From 751dbc8ecb4d9e67c128ad86fb917682c3fe5d1f Mon Sep 17 00:00:00 2001 From: "Joachim Wuttke (o)" <j.wuttke@fz-juelich.de> Date: Tue, 27 Jul 2021 16:03:59 +0200 Subject: [PATCH] rm mvvm and gui2 --- .gitlab-ci.yml | 2 +- CMakeLists.txt | 9 - Tests/Unit/gui2/CMakeLists.txt | 4 - Tests/Unit/gui2/data/Untitled 1.csv | 3 - Tests/Unit/gui2/data/bilayer1.txt | 503 -------- Tests/Unit/gui2/data/bilayer2.txt | 503 -------- Tests/Unit/gui2/data/bilayer3a.txt | 501 -------- Tests/Unit/gui2/data/bilayer3b.txt | 501 -------- .../Unit/gui2/data/p15320_00017128_report.csv | 254 ----- .../Unit/gui2/libtestmachinery/CMakeLists.txt | 17 - .../gui2/libtestmachinery/folderbasedtest.cpp | 42 - .../gui2/libtestmachinery/folderbasedtest.h | 33 - .../Unit/gui2/libtestmachinery/google_test.h | 15 - .../Unit/gui2/libtestmachinery/test_utils.cpp | 73 -- Tests/Unit/gui2/libtestmachinery/test_utils.h | 63 - .../gui2/libtestmachinery/widgetbasedtest.cpp | 33 - .../gui2/libtestmachinery/widgetbasedtest.h | 31 - Tests/Unit/gui2/testdareflcore/CMakeLists.txt | 26 - Tests/Unit/gui2/testdareflcore/TestAll.cpp | 26 - .../testdareflcore/applicationmodels.test.cpp | 51 - .../gui2/testdareflcore/datahandler.test.cpp | 58 - .../testdareflcore/dataloader_utils.test.cpp | 280 ----- .../dataselectionmodel.test.cpp | 166 --- .../testdareflcore/defaultparser.test.cpp | 91 -- .../experimentaldatamodel.test.cpp | 209 ---- .../importdataeditoractions.test.cpp | 105 -- .../testdareflcore/importtableheader.test.cpp | 116 -- .../testdareflcore/instrumentitems.test.cpp | 59 - .../layereditoractions.test.cpp | 242 ---- .../testdareflcore/layerelements.test.cpp | 1011 ----------------- .../gui2/testdareflcore/layeritems.test.cpp | 83 -- .../layerselectionmodel.test.cpp | 127 --- .../testdareflcore/layerviewmodel.test.cpp | 160 --- .../testdareflcore/materialprofile.test.cpp | 77 -- .../gui2/testdareflcore/modelutils.test.cpp | 67 -- .../testdareflcore/quicksimutils.test.cpp | 218 ---- .../sldelementcontroller.test.cpp | 101 -- gui2/CMakeLists.txt | 34 - gui2/cmake/modules/ClangFormat.cmake | 46 - gui2/cmake/modules/CodeTools.cmake | 18 - gui2/cmake/modules/configuration.cmake | 59 - gui2/cmake/scripts/testconfig.h.in | 29 - gui2/core/CMakeLists.txt | 3 - gui2/core/app_constants.h | 33 - gui2/dataloader/CMakeLists.txt | 32 - gui2/dataloader/datahandler.cpp | 54 - gui2/dataloader/datahandler.h | 45 - gui2/dataloader/dataloader_constants.h | 29 - gui2/dataloader/dataloader_types.h | 54 - gui2/dataloader/dataloader_utils.cpp | 221 ---- gui2/dataloader/dataloader_utils.h | 85 -- gui2/dataloader/dataloaderdialog.cpp | 286 ----- gui2/dataloader/dataloaderdialog.h | 77 -- gui2/dataloader/dataloadertoolbar.cpp | 41 - gui2/dataloader/dataloadertoolbar.h | 38 - gui2/dataloader/defaultparser.cpp | 80 -- gui2/dataloader/defaultparser.h | 57 - gui2/dataloader/importfilewidget.cpp | 184 --- gui2/dataloader/importfilewidget.h | 67 -- gui2/dataloader/importtableheader.cpp | 136 --- gui2/dataloader/importtableheader.h | 57 - gui2/dataloader/importtablemodel.cpp | 141 --- gui2/dataloader/importtablemodel.h | 69 -- gui2/dataloader/importtablewidget.cpp | 51 - gui2/dataloader/importtablewidget.h | 58 - gui2/dataloader/importtextview.cpp | 131 --- gui2/dataloader/importtextview.h | 74 -- gui2/dataloader/loaderpreviewpanel.cpp | 74 -- gui2/dataloader/loaderpreviewpanel.h | 54 - gui2/dataloader/loaderselectorpanel.cpp | 98 -- gui2/dataloader/loaderselectorpanel.h | 68 -- gui2/dataloader/parserinterface.h | 49 - gui2/dataloader/parserpropertywidget.cpp | 362 ------ gui2/dataloader/parserpropertywidget.h | 75 -- gui2/importdataview/CMakeLists.txt | 19 - gui2/importdataview/dataselectionmodel.cpp | 104 -- gui2/importdataview/dataselectionmodel.h | 58 - gui2/importdataview/dataselectorwidget.cpp | 73 -- gui2/importdataview/dataselectorwidget.h | 61 - gui2/importdataview/dataviewmodel.cpp | 121 -- gui2/importdataview/dataviewmodel.h | 49 - gui2/importdataview/graphcanvaswidget.cpp | 40 - gui2/importdataview/graphcanvaswidget.h | 48 - gui2/importdataview/graphimportdata.h | 38 - gui2/importdataview/importdataeditor.cpp | 125 -- gui2/importdataview/importdataeditor.h | 60 - .../importdataeditoractions.cpp | 144 --- gui2/importdataview/importdataeditoractions.h | 67 -- .../importdataeditortoolbal.cpp | 88 -- gui2/importdataview/importdataeditortoolbal.h | 42 - gui2/importdataview/importdataview.cpp | 30 - gui2/importdataview/importdataview.h | 39 - gui2/layereditor/CMakeLists.txt | 20 - .../customlayertreeeditorfactory.cpp | 60 - .../customlayertreeeditorfactory.h | 41 - gui2/layereditor/layereditor.cpp | 72 -- gui2/layereditor/layereditor.h | 55 - gui2/layereditor/layereditoractions.cpp | 149 --- gui2/layereditor/layereditoractions.h | 55 - gui2/layereditor/layereditortoolbar.cpp | 119 -- gui2/layereditor/layereditortoolbar.h | 46 - gui2/layereditor/layereditorwidget.cpp | 58 - gui2/layereditor/layereditorwidget.h | 56 - gui2/layereditor/layerselectionmodel.cpp | 102 -- gui2/layereditor/layerselectionmodel.h | 52 - gui2/layereditor/layertreeview.cpp | 38 - gui2/layereditor/layertreeview.h | 37 - gui2/layereditor/layerviewmodel.cpp | 27 - gui2/layereditor/layerviewmodel.h | 38 - gui2/layereditor/layerviewmodelcontroller.cpp | 80 -- gui2/layereditor/layerviewmodelcontroller.h | 37 - gui2/main.cpp | 32 - gui2/mainwindow/CMakeLists.txt | 15 - gui2/mainwindow/actionmanager.cpp | 110 -- gui2/mainwindow/actionmanager.h | 69 -- gui2/mainwindow/fancytab.cpp | 84 -- gui2/mainwindow/fancytab.h | 52 - gui2/mainwindow/mainbarwidget.cpp | 59 - gui2/mainwindow/mainbarwidget.h | 52 - gui2/mainwindow/mainwindow.cpp | 122 -- gui2/mainwindow/mainwindow.h | 61 - gui2/mainwindow/simulationview.cpp | 65 -- gui2/mainwindow/simulationview.h | 49 - gui2/mainwindow/styleutils.cpp | 46 - gui2/mainwindow/styleutils.h | 46 - gui2/materialeditor/CMakeLists.txt | 16 - gui2/materialeditor/materialeditor.cpp | 61 - gui2/materialeditor/materialeditor.h | 50 - gui2/materialeditor/materialeditoractions.cpp | 180 --- gui2/materialeditor/materialeditoractions.h | 56 - gui2/materialeditor/materialeditortoolbar.cpp | 75 -- gui2/materialeditor/materialeditortoolbar.h | 37 - gui2/materialeditor/materialeditorwidget.cpp | 59 - gui2/materialeditor/materialeditorwidget.h | 59 - .../materialeditor/materialselectionmodel.cpp | 66 -- gui2/materialeditor/materialselectionmodel.h | 53 - gui2/materialeditor/materialtableview.cpp | 69 -- gui2/materialeditor/materialtableview.h | 46 - gui2/materialeditor/materialtreeview.cpp | 76 -- gui2/materialeditor/materialtreeview.h | 48 - gui2/model/CMakeLists.txt | 31 - gui2/model/applicationmodels.cpp | 138 --- gui2/model/applicationmodels.h | 61 - gui2/model/experimentaldatacontroller.cpp | 52 - gui2/model/experimentaldatacontroller.h | 45 - gui2/model/experimentaldataitems.cpp | 55 - gui2/model/experimentaldataitems.h | 64 -- gui2/model/experimentaldatamodel.cpp | 139 --- gui2/model/experimentaldatamodel.h | 61 - gui2/model/instrumentitems.cpp | 134 --- gui2/model/instrumentitems.h | 101 -- gui2/model/instrumentmodel.cpp | 45 - gui2/model/instrumentmodel.h | 32 - gui2/model/item_constants.h | 40 - gui2/model/jobitem.cpp | 227 ---- gui2/model/jobitem.h | 87 -- gui2/model/jobmodel.cpp | 97 -- gui2/model/jobmodel.h | 58 - gui2/model/materialitems.cpp | 82 -- gui2/model/materialitems.h | 69 -- gui2/model/materialmodel.cpp | 146 --- gui2/model/materialmodel.h | 54 - gui2/model/materialpropertycontroller.cpp | 50 - gui2/model/materialpropertycontroller.h | 41 - gui2/model/modelutils.cpp | 95 -- gui2/model/modelutils.h | 59 - gui2/model/sampleitems.cpp | 98 -- gui2/model/sampleitems.h | 67 -- gui2/model/samplemodel.cpp | 56 - gui2/model/samplemodel.h | 33 - gui2/quicksimeditor/CMakeLists.txt | 27 - .../custombeampropertyeditorfactory.cpp | 68 -- .../custombeampropertyeditorfactory.h | 41 - .../instrumentpropertyeditor.cpp | 65 -- .../quicksimeditor/instrumentpropertyeditor.h | 50 - gui2/quicksimeditor/jobmanager.cpp | 101 -- gui2/quicksimeditor/jobmanager.h | 57 - gui2/quicksimeditor/materialprofile.cpp | 51 - gui2/quicksimeditor/materialprofile.h | 42 - gui2/quicksimeditor/profilehelper.cpp | 93 -- gui2/quicksimeditor/profilehelper.h | 42 - gui2/quicksimeditor/quicksim_types.h | 56 - gui2/quicksimeditor/quicksimcontroller.cpp | 151 --- gui2/quicksimeditor/quicksimcontroller.h | 81 -- gui2/quicksimeditor/quicksimeditor.cpp | 107 -- gui2/quicksimeditor/quicksimeditor.h | 57 - gui2/quicksimeditor/quicksimeditortoolbar.cpp | 100 -- gui2/quicksimeditor/quicksimeditortoolbar.h | 57 - gui2/quicksimeditor/quicksimutils.cpp | 89 -- gui2/quicksimeditor/quicksimutils.h | 40 - gui2/quicksimeditor/simplotcontroller.cpp | 45 - gui2/quicksimeditor/simplotcontroller.h | 50 - gui2/quicksimeditor/simplotwidget.cpp | 68 -- gui2/quicksimeditor/simplotwidget.h | 53 - gui2/resources/CMakeLists.txt | 8 - gui2/resources/icons.qrc | 28 - gui2/resources/icons/F-letter_1000x.png | Bin 746456 -> 0 bytes .../icons/arrow-down-circle-outline.svg | 1 - .../icons/arrow-up-circle-outline.svg | 1 - gui2/resources/icons/aspect-ratio.svg | 1 - .../resources/icons/beaker-remove-outline.svg | 1 - .../resources/icons/card-bulleted-outline.svg | 54 - gui2/resources/icons/close-circle-outline.svg | 1 - gui2/resources/icons/cog-outline.svg | 1 - gui2/resources/icons/dock-left.svg | 1 - gui2/resources/icons/dock-right.svg | 1 - gui2/resources/icons/export.svg | 1 - gui2/resources/icons/import.svg | 1 - gui2/resources/icons/layers-outline.svg | 1 - .../resources/icons/layers-triple-outline.svg | 1 - gui2/resources/icons/play-circle-outline.svg | 1 - .../icons/plus-box-multiple-outline.svg | 1 - gui2/resources/icons/plus-box-outline.svg | 1 - .../icons/plus-circle-multiple-outline.svg | 1 - gui2/resources/icons/plus-circle-outline.svg | 1 - gui2/resources/icons/redo.svg | 1 - gui2/resources/icons/set-merge.svg | 1 - gui2/resources/icons/undo.svg | 1 - gui2/resources/resources.h | 25 - gui2/settingsview/CMakeLists.txt | 4 - gui2/settingsview/settingsview.cpp | 83 -- gui2/settingsview/settingsview.h | 53 - gui2/sldeditor/CMakeLists.txt | 28 - gui2/sldeditor/elementview.cpp | 265 ----- gui2/sldeditor/elementview.h | 74 -- gui2/sldeditor/graphicsscene.cpp | 87 -- gui2/sldeditor/graphicsscene.h | 56 - gui2/sldeditor/handleelementview.cpp | 98 -- gui2/sldeditor/handleelementview.h | 56 - gui2/sldeditor/layerelementcontroller.cpp | 912 --------------- gui2/sldeditor/layerelementcontroller.h | 160 --- gui2/sldeditor/layerelementitem.cpp | 57 - gui2/sldeditor/layerelementitem.h | 61 - gui2/sldeditor/roughnesselementview.cpp | 88 -- gui2/sldeditor/roughnesselementview.h | 49 - gui2/sldeditor/segmentelementview.cpp | 97 -- gui2/sldeditor/segmentelementview.h | 56 - gui2/sldeditor/sldeditor.cpp | 69 -- gui2/sldeditor/sldeditor.h | 48 - gui2/sldeditor/sldeditoractions.cpp | 42 - gui2/sldeditor/sldeditoractions.h | 45 - gui2/sldeditor/sldeditortoolbar.cpp | 35 - gui2/sldeditor/sldeditortoolbar.h | 40 - gui2/sldeditor/sldelementcontroller.cpp | 309 ----- gui2/sldeditor/sldelementcontroller.h | 75 -- gui2/sldeditor/sldelementmodel.cpp | 49 - gui2/sldeditor/sldelementmodel.h | 41 - gui2/sldeditor/sldviewwidget.cpp | 55 - gui2/sldeditor/sldviewwidget.h | 46 - gui2/welcomeview/CMakeLists.txt | 16 - gui2/welcomeview/openprojectwidget.cpp | 100 -- gui2/welcomeview/openprojectwidget.h | 54 - gui2/welcomeview/projecthandler.cpp | 125 -- gui2/welcomeview/projecthandler.h | 73 -- gui2/welcomeview/projectpanewidget.cpp | 105 -- gui2/welcomeview/projectpanewidget.h | 59 - gui2/welcomeview/recentprojectsettings.cpp | 114 -- gui2/welcomeview/recentprojectsettings.h | 51 - gui2/welcomeview/recentprojectwidget.cpp | 114 -- gui2/welcomeview/recentprojectwidget.h | 59 - gui2/welcomeview/userinteractor.cpp | 116 -- gui2/welcomeview/userinteractor.h | 53 - gui2/welcomeview/welcomeview.cpp | 112 -- gui2/welcomeview/welcomeview.h | 66 -- mvvm/CMakeLists.txt | 26 - mvvm/cmake/modules/ClangFormat.cmake | 46 - mvvm/cmake/modules/CodeTools.cmake | 28 - mvvm/cmake/modules/configuration.cmake | 67 -- mvvm/cmake/modules/installation.cmake | 45 - mvvm/cmake/scripts/MVVMConfig.cmake.in | 21 - mvvm/cmake/scripts/mvvm_version.h.in | 51 - mvvm/cmake/scripts/testconfig.h.in | 29 - mvvm/model/CMakeLists.txt | 38 - mvvm/model/mvvm/CMakeLists.txt | 10 - mvvm/model/mvvm/commands/CMakeLists.txt | 23 - .../mvvm/commands/abstractitemcommand.cpp | 129 --- .../model/mvvm/commands/abstractitemcommand.h | 67 -- mvvm/model/mvvm/commands/commandadapter.cpp | 37 - mvvm/model/mvvm/commands/commandadapter.h | 42 - mvvm/model/mvvm/commands/commandresult.h | 29 - mvvm/model/mvvm/commands/commandservice.cpp | 110 -- mvvm/model/mvvm/commands/commandservice.h | 85 -- mvvm/model/mvvm/commands/commandutils.cpp | 32 - mvvm/model/mvvm/commands/commandutils.h | 43 - mvvm/model/mvvm/commands/copyitemcommand.cpp | 81 -- mvvm/model/mvvm/commands/copyitemcommand.h | 42 - .../mvvm/commands/insertnewitemcommand.cpp | 83 -- .../mvvm/commands/insertnewitemcommand.h | 43 - mvvm/model/mvvm/commands/moveitemcommand.cpp | 128 --- mvvm/model/mvvm/commands/moveitemcommand.h | 43 - .../model/mvvm/commands/removeitemcommand.cpp | 76 -- mvvm/model/mvvm/commands/removeitemcommand.h | 42 - mvvm/model/mvvm/commands/setvaluecommand.cpp | 76 -- mvvm/model/mvvm/commands/setvaluecommand.h | 43 - mvvm/model/mvvm/commands/undostack.cpp | 102 -- mvvm/model/mvvm/commands/undostack.h | 60 - mvvm/model/mvvm/core/CMakeLists.txt | 8 - mvvm/model/mvvm/core/filesystem.h | 93 -- mvvm/model/mvvm/core/types.h | 27 - mvvm/model/mvvm/core/uniqueidgenerator.cpp | 23 - mvvm/model/mvvm/core/uniqueidgenerator.h | 40 - mvvm/model/mvvm/core/variant.h | 25 - mvvm/model/mvvm/core/version.h | 55 - mvvm/model/mvvm/factories/CMakeLists.txt | 12 - .../mvvm/factories/itemcataloguefactory.cpp | 41 - .../mvvm/factories/itemcataloguefactory.h | 27 - .../mvvm/factories/itemconverterfactory.cpp | 59 - .../mvvm/factories/itemconverterfactory.h | 45 - .../mvvm/factories/modelconverterfactory.cpp | 45 - .../mvvm/factories/modelconverterfactory.h | 37 - .../mvvm/factories/modeldocumentfactory.cpp | 25 - .../mvvm/factories/modeldocumentfactory.h | 32 - .../mvvm/factories/projectmanagerfactory.cpp | 27 - .../mvvm/factories/projectmanagerfactory.h | 33 - mvvm/model/mvvm/interfaces/CMakeLists.txt | 12 - .../interfaces/applicationmodelsinterface.h | 36 - .../mvvm/interfaces/itembackupstrategy.h | 40 - mvvm/model/mvvm/interfaces/itemcopystrategy.h | 37 - .../mvvm/interfaces/itemfactoryinterface.h | 41 - .../mvvm/interfaces/itemlistenerinterface.h | 74 -- .../mvvm/interfaces/modeldocumentinterface.h | 35 - .../mvvm/interfaces/modellistenerinterface.h | 60 - mvvm/model/mvvm/interfaces/projectinterface.h | 37 - .../mvvm/interfaces/projectmanagerinterface.h | 48 - .../mvvm/interfaces/undostackinterface.h | 56 - mvvm/model/mvvm/model/CMakeLists.txt | 49 - mvvm/model/mvvm/model/comboproperty.cpp | 257 ----- mvvm/model/mvvm/model/comboproperty.h | 78 -- mvvm/model/mvvm/model/comparators.cpp | 41 - mvvm/model/mvvm/model/comparators.h | 35 - mvvm/model/mvvm/model/compounditem.cpp | 37 - mvvm/model/mvvm/model/compounditem.h | 74 -- mvvm/model/mvvm/model/customvariants.cpp | 135 --- mvvm/model/mvvm/model/customvariants.h | 81 -- mvvm/model/mvvm/model/datarole.cpp | 25 - mvvm/model/mvvm/model/datarole.h | 35 - mvvm/model/mvvm/model/externalproperty.cpp | 65 -- mvvm/model/mvvm/model/externalproperty.h | 57 - mvvm/model/mvvm/model/function_types.h | 36 - mvvm/model/mvvm/model/groupitem.cpp | 92 -- mvvm/model/mvvm/model/groupitem.h | 61 - mvvm/model/mvvm/model/itemcatalogue.cpp | 101 -- mvvm/model/mvvm/model/itemcatalogue.h | 67 -- mvvm/model/mvvm/model/itemfactory.cpp | 37 - mvvm/model/mvvm/model/itemfactory.h | 44 - mvvm/model/mvvm/model/itemmanager.cpp | 94 -- mvvm/model/mvvm/model/itemmanager.h | 63 - mvvm/model/mvvm/model/itempool.cpp | 79 -- mvvm/model/mvvm/model/itempool.h | 53 - mvvm/model/mvvm/model/itemutils.cpp | 177 --- mvvm/model/mvvm/model/itemutils.h | 104 -- mvvm/model/mvvm/model/modelutils.cpp | 107 -- mvvm/model/mvvm/model/modelutils.h | 124 -- mvvm/model/mvvm/model/mvvm_types.h | 67 -- mvvm/model/mvvm/model/path.cpp | 85 -- mvvm/model/mvvm/model/path.h | 65 -- mvvm/model/mvvm/model/propertyitem.cpp | 33 - mvvm/model/mvvm/model/propertyitem.h | 38 - mvvm/model/mvvm/model/sessionitem.cpp | 393 ------- mvvm/model/mvvm/model/sessionitem.h | 206 ---- .../model/mvvm/model/sessionitemcontainer.cpp | 155 --- mvvm/model/mvvm/model/sessionitemcontainer.h | 74 -- mvvm/model/mvvm/model/sessionitemdata.cpp | 95 -- mvvm/model/mvvm/model/sessionitemdata.h | 49 - mvvm/model/mvvm/model/sessionitemtags.cpp | 183 --- mvvm/model/mvvm/model/sessionitemtags.h | 88 -- mvvm/model/mvvm/model/sessionmodel.cpp | 232 ---- mvvm/model/mvvm/model/sessionmodel.h | 142 --- mvvm/model/mvvm/model/taginfo.cpp | 89 -- mvvm/model/mvvm/model/taginfo.h | 66 -- mvvm/model/mvvm/model/tagrow.cpp | 57 - mvvm/model/mvvm/model/tagrow.h | 49 - mvvm/model/mvvm/model/variant_constants.h | 38 - mvvm/model/mvvm/project/CMakeLists.txt | 15 - .../project/modelhaschangedcontroller.cpp | 52 - .../mvvm/project/modelhaschangedcontroller.h | 44 - mvvm/model/mvvm/project/project.cpp | 86 -- mvvm/model/mvvm/project/project.h | 48 - mvvm/model/mvvm/project/project_types.h | 65 -- .../mvvm/project/projectchangecontroller.cpp | 82 -- .../mvvm/project/projectchangecontroller.h | 54 - mvvm/model/mvvm/project/projectmanager.cpp | 132 --- mvvm/model/mvvm/project/projectmanager.h | 61 - .../mvvm/project/projectmanagerdecorator.cpp | 192 ---- .../mvvm/project/projectmanagerdecorator.h | 62 - mvvm/model/mvvm/project/projectutils.cpp | 76 -- mvvm/model/mvvm/project/projectutils.h | 48 - mvvm/model/mvvm/serialization/CMakeLists.txt | 35 - .../mvvm/serialization/compatibilityutils.cpp | 79 -- .../mvvm/serialization/compatibilityutils.h | 54 - .../serialization/jsonconverterinterfaces.h | 24 - .../model/mvvm/serialization/jsondocument.cpp | 84 -- mvvm/model/mvvm/serialization/jsondocument.h | 44 - .../model/mvvm/serialization/jsonitem_types.h | 76 -- .../serialization/jsonitembackupstrategy.cpp | 43 - .../serialization/jsonitembackupstrategy.h | 44 - .../jsonitemcontainerconverter.cpp | 155 --- .../jsonitemcontainerconverter.h | 48 - .../mvvm/serialization/jsonitemconverter.cpp | 141 --- .../mvvm/serialization/jsonitemconverter.h | 46 - .../jsonitemconverterinterface.h | 42 - .../serialization/jsonitemcopystrategy.cpp | 38 - .../mvvm/serialization/jsonitemcopystrategy.h | 42 - .../serialization/jsonitemdataconverter.cpp | 121 -- .../serialization/jsonitemdataconverter.h | 59 - .../jsonitemdataconverterinterface.h | 42 - .../serialization/jsonitemformatassistant.cpp | 140 --- .../serialization/jsonitemformatassistant.h | 53 - .../serialization/jsonitemtagsconverter.cpp | 116 -- .../serialization/jsonitemtagsconverter.h | 48 - .../mvvm/serialization/jsonmodelconverter.cpp | 94 -- .../mvvm/serialization/jsonmodelconverter.h | 46 - .../jsonmodelconverterinterface.h | 39 - .../serialization/jsontaginfoconverter.cpp | 77 -- .../mvvm/serialization/jsontaginfoconverter.h | 41 - .../jsontaginfoconverterinterface.h | 39 - mvvm/model/mvvm/serialization/jsonutils.cpp | 76 -- mvvm/model/mvvm/serialization/jsonutils.h | 41 - .../serialization/jsonvariantconverter.cpp | 313 ----- .../mvvm/serialization/jsonvariantconverter.h | 52 - .../jsonvariantconverterinterface.h | 38 - mvvm/model/mvvm/signals/CMakeLists.txt | 14 - mvvm/model/mvvm/signals/callback_types.h | 38 - mvvm/model/mvvm/signals/callbackcontainer.h | 75 -- mvvm/model/mvvm/signals/itemlistener.h | 33 - mvvm/model/mvvm/signals/itemlistenerbase.cpp | 125 -- mvvm/model/mvvm/signals/itemlistenerbase.h | 61 - mvvm/model/mvvm/signals/itemmapper.cpp | 232 ---- mvvm/model/mvvm/signals/itemmapper.h | 59 - mvvm/model/mvvm/signals/modellistener.h | 35 - mvvm/model/mvvm/signals/modellistenerbase.cpp | 92 -- mvvm/model/mvvm/signals/modellistenerbase.h | 51 - mvvm/model/mvvm/signals/modelmapper.cpp | 158 --- mvvm/model/mvvm/signals/modelmapper.h | 67 -- mvvm/model/mvvm/standarditems/CMakeLists.txt | 27 - mvvm/model/mvvm/standarditems/axisitems.cpp | 149 --- mvvm/model/mvvm/standarditems/axisitems.h | 111 -- .../model/mvvm/standarditems/colormapitem.cpp | 50 - mvvm/model/mvvm/standarditems/colormapitem.h | 44 - .../standarditems/colormapviewportitem.cpp | 81 -- .../mvvm/standarditems/colormapviewportitem.h | 48 - .../mvvm/standarditems/containeritem.cpp | 32 - mvvm/model/mvvm/standarditems/containeritem.h | 38 - mvvm/model/mvvm/standarditems/data1ditem.cpp | 97 -- mvvm/model/mvvm/standarditems/data1ditem.h | 77 -- mvvm/model/mvvm/standarditems/data2ditem.cpp | 87 -- mvvm/model/mvvm/standarditems/data2ditem.h | 52 - mvvm/model/mvvm/standarditems/graphitem.cpp | 92 -- mvvm/model/mvvm/standarditems/graphitem.h | 58 - .../mvvm/standarditems/graphviewportitem.cpp | 105 -- .../mvvm/standarditems/graphviewportitem.h | 47 - mvvm/model/mvvm/standarditems/linkeditem.cpp | 35 - mvvm/model/mvvm/standarditems/linkeditem.h | 48 - .../mvvm/standarditems/plottableitems.cpp | 78 -- .../model/mvvm/standarditems/plottableitems.h | 54 - .../mvvm/standarditems/standarditemincludes.h | 35 - mvvm/model/mvvm/standarditems/vectoritem.cpp | 45 - mvvm/model/mvvm/standarditems/vectoritem.h | 40 - .../model/mvvm/standarditems/viewportitem.cpp | 67 -- mvvm/model/mvvm/standarditems/viewportitem.h | 53 - mvvm/model/mvvm/utils/CMakeLists.txt | 19 - mvvm/model/mvvm/utils/binutils.cpp | 92 -- mvvm/model/mvvm/utils/binutils.h | 31 - mvvm/model/mvvm/utils/containerutils.cpp | 15 - mvvm/model/mvvm/utils/containerutils.h | 108 -- mvvm/model/mvvm/utils/fileutils.cpp | 135 --- mvvm/model/mvvm/utils/fileutils.h | 55 - mvvm/model/mvvm/utils/ifactory.h | 67 -- mvvm/model/mvvm/utils/mathconstants.h | 22 - mvvm/model/mvvm/utils/numericutils.cpp | 44 - mvvm/model/mvvm/utils/numericutils.h | 33 - mvvm/model/mvvm/utils/progresshandler.cpp | 61 - mvvm/model/mvvm/utils/progresshandler.h | 57 - mvvm/model/mvvm/utils/reallimits.cpp | 154 --- mvvm/model/mvvm/utils/reallimits.h | 87 -- mvvm/model/mvvm/utils/stringutils.cpp | 130 --- mvvm/model/mvvm/utils/stringutils.h | 64 -- mvvm/model/mvvm/utils/threadsafestack.h | 135 --- mvvm/tests/CMakeLists.txt | 12 - mvvm/tests/README.md | 9 - mvvm/tests/data/c++_exec | Bin 19672 -> 0 bytes mvvm/tests/data/mandelbrot.ppm | Bin 196623 -> 0 bytes mvvm/tests/data/pdf_file | Bin 536732 -> 0 bytes mvvm/tests/data/png_file.png | Bin 119128 -> 0 bytes mvvm/tests/data/text_UTF-8-BOM.txt | 2 - mvvm/tests/data/text_UTF-8.txt | 2 - mvvm/tests/data/word_file | Bin 10220 -> 0 bytes mvvm/tests/libtestmachinery/CMakeLists.txt | 17 - .../libtestmachinery/folderbasedtest.cpp | 47 - mvvm/tests/libtestmachinery/folderbasedtest.h | 38 - mvvm/tests/libtestmachinery/google_test.h | 22 - mvvm/tests/libtestmachinery/mockinterfaces.h | 56 - mvvm/tests/libtestmachinery/mockwidgets.cpp | 144 --- mvvm/tests/libtestmachinery/mockwidgets.h | 76 -- mvvm/tests/libtestmachinery/test_utils.cpp | 130 --- mvvm/tests/libtestmachinery/test_utils.h | 109 -- mvvm/tests/libtestmachinery/toyitems.cpp | 106 -- mvvm/tests/libtestmachinery/toyitems.h | 124 -- mvvm/tests/libtestmachinery/toymodel.cpp | 44 - mvvm/tests/libtestmachinery/toymodel.h | 33 - .../libtestmachinery/widgetbasedtest.cpp | 38 - mvvm/tests/libtestmachinery/widgetbasedtest.h | 36 - mvvm/tests/testintegration/CMakeLists.txt | 20 - mvvm/tests/testintegration/TestAll.cpp | 30 - .../standarditemserialization.test.cpp | 125 -- .../testintegration/toyitemslattice.test.cpp | 45 - .../toyitemsserialization.test.cpp | 94 -- .../toyitemsshapegroup.test.cpp | 288 ----- .../testintegration/undoscenario.test.cpp | 85 -- mvvm/tests/testmodel/CMakeLists.txt | 20 - mvvm/tests/testmodel/TestAll.cpp | 27 - mvvm/tests/testmodel/axisitems.test.cpp | 135 --- mvvm/tests/testmodel/binutils.test.cpp | 69 -- .../testmodel/callbackcontainer.test.cpp | 114 -- mvvm/tests/testmodel/colormapitem.test.cpp | 77 -- .../testmodel/colormapviewportitem.test.cpp | 157 --- mvvm/tests/testmodel/comboproperty.test.cpp | 422 ------- .../testmodel/compatibilityutils.test.cpp | 74 -- mvvm/tests/testmodel/compounditem.test.cpp | 253 ----- mvvm/tests/testmodel/containeritem.test.cpp | 48 - mvvm/tests/testmodel/containerutils.test.cpp | 83 -- mvvm/tests/testmodel/copyitemcommand.test.cpp | 90 -- mvvm/tests/testmodel/customvariants.test.cpp | 248 ---- mvvm/tests/testmodel/data1ditem.test.cpp | 202 ---- mvvm/tests/testmodel/data2ditem.test.cpp | 122 -- .../tests/testmodel/externalproperty.test.cpp | 75 -- mvvm/tests/testmodel/fileutils.test.cpp | 112 -- mvvm/tests/testmodel/graphitem.test.cpp | 177 --- .../testmodel/graphviewportitem.test.cpp | 164 --- mvvm/tests/testmodel/groupitem.test.cpp | 39 - .../testmodel/insertnewitemcommand.test.cpp | 217 ---- mvvm/tests/testmodel/itemcatalogue.test.cpp | 170 --- .../testmodel/itemconverterfactory.test.cpp | 156 --- mvvm/tests/testmodel/itemlistener.test.cpp | 110 -- mvvm/tests/testmodel/itemmanager.test.cpp | 42 - mvvm/tests/testmodel/itemmapper.test.cpp | 254 ----- mvvm/tests/testmodel/itempool.test.cpp | 115 -- mvvm/tests/testmodel/itemutils.test.cpp | 328 ------ mvvm/tests/testmodel/jsondocument.test.cpp | 177 --- mvvm/tests/testmodel/jsonitem_types.test.cpp | 43 - .../testmodel/jsonitembackupstrategy.test.cpp | 113 -- .../jsonitemcontainerconverter.test.cpp | 201 ---- .../testmodel/jsonitemconverter.test.cpp | 237 ---- .../testmodel/jsonitemcopystrategy.test.cpp | 110 -- .../testmodel/jsonitemdataconverter.test.cpp | 259 ----- .../jsonitemformatassistant.test.cpp | 126 -- .../testmodel/jsonmodelconverter.test.cpp | 264 ----- .../testmodel/jsontaginfoconverter.test.cpp | 110 -- mvvm/tests/testmodel/jsonutils.test.cpp | 49 - .../testmodel/jsonvariantconverter.test.cpp | 344 ------ mvvm/tests/testmodel/linkeditem.test.cpp | 128 --- .../modelhaschangedcontroller.test.cpp | 135 --- mvvm/tests/testmodel/modellistener.test.cpp | 98 -- mvvm/tests/testmodel/modelmapper.test.cpp | 230 ---- mvvm/tests/testmodel/modelutils.test.cpp | 173 --- mvvm/tests/testmodel/moveitemcommand.test.cpp | 335 ------ mvvm/tests/testmodel/numericutils.test.cpp | 35 - mvvm/tests/testmodel/path.test.cpp | 131 --- mvvm/tests/testmodel/plottableitems.test.cpp | 57 - mvvm/tests/testmodel/progresshandler.test.cpp | 73 -- mvvm/tests/testmodel/project.test.cpp | 139 --- .../projectchangecontroller.test.cpp | 81 -- mvvm/tests/testmodel/projectmanager.test.cpp | 253 ----- .../projectmanagerdecorator.test.cpp | 208 ---- mvvm/tests/testmodel/projectutils.test.cpp | 97 -- mvvm/tests/testmodel/reallimits.test.cpp | 242 ---- .../testmodel/removeitemcommand.test.cpp | 181 --- mvvm/tests/testmodel/sessionitem.test.cpp | 722 ------------ .../testmodel/sessionitemcontainer.test.cpp | 305 ----- mvvm/tests/testmodel/sessionitemdata.test.cpp | 171 --- mvvm/tests/testmodel/sessionitemtags.test.cpp | 227 ---- mvvm/tests/testmodel/sessionmodel.test.cpp | 467 -------- mvvm/tests/testmodel/setvaluecommand.test.cpp | 80 -- mvvm/tests/testmodel/stringutils.test.cpp | 188 --- mvvm/tests/testmodel/taginfo.test.cpp | 95 -- mvvm/tests/testmodel/tagrow.test.cpp | 141 --- mvvm/tests/testmodel/test_utils.test.cpp | 41 - mvvm/tests/testmodel/threadsafestack.test.cpp | 163 --- mvvm/tests/testmodel/undostack.test.cpp | 919 --------------- mvvm/tests/testmodel/vectoritem.test.cpp | 68 -- mvvm/tests/testview/CMakeLists.txt | 20 - mvvm/tests/testview/TestAll.cpp | 37 - .../testview/axistitlecontroller.test.cpp | 100 -- .../testview/colormapplotcontroller.test.cpp | 214 ---- .../colormapviewportplotcontroller.test.cpp | 142 --- mvvm/tests/testview/customplot_test_utils.cpp | 37 - mvvm/tests/testview/customplot_test_utils.h | 59 - .../testview/customplot_test_utils.test.cpp | 60 - .../testview/customplotsceneadapter.test.cpp | 53 - .../testview/data1dplotcontroller.test.cpp | 181 --- .../testview/data2dplotcontroller.test.cpp | 207 ---- .../testview/graphplotcontroller.test.cpp | 267 ----- .../graphviewportplotcontroller.test.cpp | 388 ------- mvvm/tests/testview/pencontroller.test.cpp | 109 -- mvvm/tests/testview/propertyflatview.test.cpp | 86 -- .../viewportaxisplotcontroller.test.cpp | 435 ------- mvvm/tests/testview/widgetutils.test.cpp | 102 -- mvvm/tests/testviewmodel/CMakeLists.txt | 20 - mvvm/tests/testviewmodel/TestAll.cpp | 27 - mvvm/tests/testviewmodel/TestToyLayerItem.cpp | 155 --- .../testviewmodel/TestToyMultiLayerItem.cpp | 105 -- .../testviewmodel/TestToyParticleItem.cpp | 63 - .../defaulteditorfactory.test.cpp | 200 ---- .../testviewmodel/defaultviewmodel.test.cpp | 852 -------------- .../labeldatarowstrategy.test.cpp | 82 -- .../propertiesrowstrategy.test.cpp | 162 --- .../propertyflatviewmodel.test.cpp | 122 -- .../propertytableviewmodel.test.cpp | 135 --- .../testviewmodel/propertyviewmodel.test.cpp | 116 -- .../testviewmodel/scientificspinbox.test.cpp | 128 --- .../standardchildrenstrategies.test.cpp | 263 ----- .../testviewmodel/standardviewitems.test.cpp | 269 ----- .../testviewmodel/topitemsviewmodel.test.cpp | 144 --- mvvm/tests/testviewmodel/viewitem.test.cpp | 232 ---- .../testviewmodel/viewmodelbase.test.cpp | 377 ------ .../viewmodelcontroller.test.cpp | 479 -------- .../viewmodelcontrollerbuilder.test.cpp | 60 - .../viewmodelcontrollerfactory.test.cpp | 48 - .../testviewmodel/viewmodeldelegate.test.cpp | 94 -- .../testviewmodel/viewmodelfactory.test.cpp | 109 -- .../testviewmodel/viewmodelutils.test.cpp | 252 ---- mvvm/view/CMakeLists.txt | 34 - mvvm/view/mvvm/CMakeLists.txt | 3 - mvvm/view/mvvm/plotting/CMakeLists.txt | 45 - .../mvvm/plotting/axistitlecontroller.cpp | 57 - mvvm/view/mvvm/plotting/axistitlecontroller.h | 45 - mvvm/view/mvvm/plotting/colormapcanvas.cpp | 72 -- mvvm/view/mvvm/plotting/colormapcanvas.h | 48 - .../mvvm/plotting/colormapinfoformatter.cpp | 64 -- .../mvvm/plotting/colormapinfoformatter.h | 38 - .../mvvm/plotting/colormapplotcontroller.cpp | 131 --- .../mvvm/plotting/colormapplotcontroller.h | 50 - .../colormapviewportplotcontroller.cpp | 87 -- .../plotting/colormapviewportplotcontroller.h | 47 - .../plotting/colorscaleplotcontroller.cpp | 106 -- .../mvvm/plotting/colorscaleplotcontroller.h | 45 - .../mvvm/plotting/customplotproxywidget.cpp | 75 -- .../mvvm/plotting/customplotproxywidget.h | 49 - .../mvvm/plotting/customplotsceneadapter.cpp | 100 -- .../mvvm/plotting/customplotsceneadapter.h | 50 - mvvm/view/mvvm/plotting/customplotutils.cpp | 44 - mvvm/view/mvvm/plotting/customplotutils.h | 35 - .../mvvm/plotting/data1dplotcontroller.cpp | 114 -- .../view/mvvm/plotting/data1dplotcontroller.h | 47 - .../mvvm/plotting/data2dplotcontroller.cpp | 93 -- .../view/mvvm/plotting/data2dplotcontroller.h | 47 - mvvm/view/mvvm/plotting/graphcanvas.cpp | 143 --- mvvm/view/mvvm/plotting/graphcanvas.h | 57 - .../view/mvvm/plotting/graphinfoformatter.cpp | 74 -- mvvm/view/mvvm/plotting/graphinfoformatter.h | 38 - .../mvvm/plotting/graphplotcontroller.cpp | 106 -- mvvm/view/mvvm/plotting/graphplotcontroller.h | 49 - .../plotting/graphviewportplotcontroller.cpp | 123 -- .../plotting/graphviewportplotcontroller.h | 46 - mvvm/view/mvvm/plotting/mousemovereporter.cpp | 66 -- mvvm/view/mvvm/plotting/mousemovereporter.h | 45 - mvvm/view/mvvm/plotting/mouseposinfo.h | 35 - mvvm/view/mvvm/plotting/pencontroller.cpp | 68 -- mvvm/view/mvvm/plotting/pencontroller.h | 46 - .../mvvm/plotting/sceneadapterinterface.h | 50 - .../plotting/statusstringformatterinterface.h | 37 - .../mvvm/plotting/statusstringreporter.cpp | 88 -- .../view/mvvm/plotting/statusstringreporter.h | 46 - .../plotting/statusstringreporterfactory.cpp | 36 - .../plotting/statusstringreporterfactory.h | 40 - .../plotting/viewportaxisplotcontroller.cpp | 137 --- .../plotting/viewportaxisplotcontroller.h | 47 - mvvm/view/mvvm/widgets/CMakeLists.txt | 26 - .../view/mvvm/widgets/adjustingscrollarea.cpp | 51 - mvvm/view/mvvm/widgets/adjustingscrollarea.h | 41 - mvvm/view/mvvm/widgets/allitemstreeview.cpp | 27 - mvvm/view/mvvm/widgets/allitemstreeview.h | 36 - mvvm/view/mvvm/widgets/collapsiblebar.cpp | 76 -- mvvm/view/mvvm/widgets/collapsiblebar.h | 51 - .../mvvm/widgets/collapsiblelistwidget.cpp | 47 - .../view/mvvm/widgets/collapsiblelistwidget.h | 44 - mvvm/view/mvvm/widgets/itemstreeview.cpp | 113 -- mvvm/view/mvvm/widgets/itemstreeview.h | 73 -- .../mvvm/widgets/itemstreeviewinterface.h | 40 - mvvm/view/mvvm/widgets/layoututils.cpp | 125 -- mvvm/view/mvvm/widgets/layoututils.h | 48 - mvvm/view/mvvm/widgets/propertyflatview.cpp | 164 --- mvvm/view/mvvm/widgets/propertyflatview.h | 45 - mvvm/view/mvvm/widgets/propertytreeview.cpp | 44 - mvvm/view/mvvm/widgets/propertytreeview.h | 37 - mvvm/view/mvvm/widgets/standardtreeviews.h | 25 - mvvm/view/mvvm/widgets/statuslabel.cpp | 70 -- mvvm/view/mvvm/widgets/statuslabel.h | 51 - mvvm/view/mvvm/widgets/topitemstreeview.cpp | 27 - mvvm/view/mvvm/widgets/topitemstreeview.h | 40 - mvvm/view/mvvm/widgets/widgetutils.cpp | 179 --- mvvm/view/mvvm/widgets/widgetutils.h | 94 -- mvvm/viewmodel/CMakeLists.txt | 33 - mvvm/viewmodel/mvvm/CMakeLists.txt | 4 - mvvm/viewmodel/mvvm/editors/CMakeLists.txt | 35 - mvvm/viewmodel/mvvm/editors/booleditor.cpp | 62 - mvvm/viewmodel/mvvm/editors/booleditor.h | 44 - mvvm/viewmodel/mvvm/editors/coloreditor.cpp | 74 -- mvvm/viewmodel/mvvm/editors/coloreditor.h | 48 - .../mvvm/editors/combopropertyeditor.cpp | 111 -- .../mvvm/editors/combopropertyeditor.h | 50 - mvvm/viewmodel/mvvm/editors/customeditor.cpp | 47 - mvvm/viewmodel/mvvm/editors/customeditor.h | 53 - .../mvvm/editors/customeventfilters.cpp | 63 - .../mvvm/editors/customeventfilters.h | 53 - .../mvvm/editors/defaulteditorfactory.cpp | 131 --- .../mvvm/editors/defaulteditorfactory.h | 79 -- mvvm/viewmodel/mvvm/editors/doubleeditor.cpp | 74 -- mvvm/viewmodel/mvvm/editors/doubleeditor.h | 48 - .../viewmodel/mvvm/editors/editor_constants.h | 39 - .../viewmodel/mvvm/editors/editorbuilders.cpp | 141 --- mvvm/viewmodel/mvvm/editors/editorbuilders.h | 66 -- .../editors/externalpropertycomboeditor.cpp | 109 -- .../editors/externalpropertycomboeditor.h | 58 - .../mvvm/editors/externalpropertyeditor.cpp | 79 -- .../mvvm/editors/externalpropertyeditor.h | 51 - mvvm/viewmodel/mvvm/editors/integereditor.cpp | 67 -- mvvm/viewmodel/mvvm/editors/integereditor.h | 44 - .../mvvm/editors/scientificdoubleeditor.cpp | 70 -- .../mvvm/editors/scientificdoubleeditor.h | 46 - .../mvvm/editors/scientificspinbox.cpp | 180 --- .../mvvm/editors/scientificspinbox.h | 74 -- .../mvvm/editors/scientificspinboxeditor.cpp | 80 -- .../mvvm/editors/scientificspinboxeditor.h | 48 - .../mvvm/editors/selectablecomboboxeditor.cpp | 211 ---- .../mvvm/editors/selectablecomboboxeditor.h | 63 - mvvm/viewmodel/mvvm/editors/styleutils.cpp | 33 - mvvm/viewmodel/mvvm/editors/styleutils.h | 33 - mvvm/viewmodel/mvvm/factories/CMakeLists.txt | 7 - .../factories/viewmodelcontrollerbuilder.cpp | 72 -- .../factories/viewmodelcontrollerbuilder.h | 62 - .../factories/viewmodelcontrollerfactory.h | 47 - .../mvvm/factories/viewmodelfactory.cpp | 47 - .../mvvm/factories/viewmodelfactory.h | 74 -- mvvm/viewmodel/mvvm/interfaces/CMakeLists.txt | 6 - .../mvvm/interfaces/celldecoratorinterface.h | 38 - .../interfaces/childrenstrategyinterface.h | 41 - .../mvvm/interfaces/editorfactoryinterface.h | 41 - .../mvvm/interfaces/rowstrategyinterface.h | 42 - mvvm/viewmodel/mvvm/viewmodel/CMakeLists.txt | 36 - .../mvvm/viewmodel/defaultcelldecorator.cpp | 66 -- .../mvvm/viewmodel/defaultcelldecorator.h | 36 - .../mvvm/viewmodel/defaultviewmodel.cpp | 23 - .../mvvm/viewmodel/defaultviewmodel.h | 35 - .../mvvm/viewmodel/labeldatarowstrategy.cpp | 41 - .../mvvm/viewmodel/labeldatarowstrategy.h | 39 - .../mvvm/viewmodel/propertiesrowstrategy.cpp | 65 -- .../mvvm/viewmodel/propertiesrowstrategy.h | 45 - .../mvvm/viewmodel/propertyflatviewmodel.cpp | 23 - .../mvvm/viewmodel/propertyflatviewmodel.h | 33 - .../mvvm/viewmodel/propertytableviewmodel.cpp | 23 - .../mvvm/viewmodel/propertytableviewmodel.h | 35 - .../mvvm/viewmodel/propertyviewmodel.cpp | 23 - .../mvvm/viewmodel/propertyviewmodel.h | 33 - .../viewmodel/standardchildrenstrategies.cpp | 103 -- .../viewmodel/standardchildrenstrategies.h | 63 - .../mvvm/viewmodel/standardviewitems.cpp | 67 -- .../mvvm/viewmodel/standardviewitems.h | 61 - .../standardviewmodelcontrollers.cpp | 92 -- .../viewmodel/standardviewmodelcontrollers.h | 79 -- .../mvvm/viewmodel/topitemsviewmodel.cpp | 23 - .../mvvm/viewmodel/topitemsviewmodel.h | 34 - mvvm/viewmodel/mvvm/viewmodel/viewitem.cpp | 245 ---- mvvm/viewmodel/mvvm/viewmodel/viewitem.h | 76 -- mvvm/viewmodel/mvvm/viewmodel/viewmodel.cpp | 84 -- mvvm/viewmodel/mvvm/viewmodel/viewmodel.h | 59 - .../mvvm/viewmodel/viewmodelbase.cpp | 185 --- mvvm/viewmodel/mvvm/viewmodel/viewmodelbase.h | 75 -- .../mvvm/viewmodel/viewmodelcontroller.cpp | 308 ----- .../mvvm/viewmodel/viewmodelcontroller.h | 74 -- .../mvvm/viewmodel/viewmodeldelegate.cpp | 119 -- .../mvvm/viewmodel/viewmodeldelegate.h | 63 - .../mvvm/viewmodel/viewmodelutils.cpp | 126 -- .../viewmodel/mvvm/viewmodel/viewmodelutils.h | 73 -- 774 files changed, 1 insertion(+), 67311 deletions(-) delete mode 100644 Tests/Unit/gui2/CMakeLists.txt delete mode 100644 Tests/Unit/gui2/data/Untitled 1.csv delete mode 100644 Tests/Unit/gui2/data/bilayer1.txt delete mode 100644 Tests/Unit/gui2/data/bilayer2.txt delete mode 100644 Tests/Unit/gui2/data/bilayer3a.txt delete mode 100644 Tests/Unit/gui2/data/bilayer3b.txt delete mode 100644 Tests/Unit/gui2/data/p15320_00017128_report.csv delete mode 100644 Tests/Unit/gui2/libtestmachinery/CMakeLists.txt delete mode 100644 Tests/Unit/gui2/libtestmachinery/folderbasedtest.cpp delete mode 100644 Tests/Unit/gui2/libtestmachinery/folderbasedtest.h delete mode 100644 Tests/Unit/gui2/libtestmachinery/google_test.h delete mode 100644 Tests/Unit/gui2/libtestmachinery/test_utils.cpp delete mode 100644 Tests/Unit/gui2/libtestmachinery/test_utils.h delete mode 100644 Tests/Unit/gui2/libtestmachinery/widgetbasedtest.cpp delete mode 100644 Tests/Unit/gui2/libtestmachinery/widgetbasedtest.h delete mode 100644 Tests/Unit/gui2/testdareflcore/CMakeLists.txt delete mode 100644 Tests/Unit/gui2/testdareflcore/TestAll.cpp delete mode 100644 Tests/Unit/gui2/testdareflcore/applicationmodels.test.cpp delete mode 100644 Tests/Unit/gui2/testdareflcore/datahandler.test.cpp delete mode 100644 Tests/Unit/gui2/testdareflcore/dataloader_utils.test.cpp delete mode 100644 Tests/Unit/gui2/testdareflcore/dataselectionmodel.test.cpp delete mode 100644 Tests/Unit/gui2/testdareflcore/defaultparser.test.cpp delete mode 100644 Tests/Unit/gui2/testdareflcore/experimentaldatamodel.test.cpp delete mode 100644 Tests/Unit/gui2/testdareflcore/importdataeditoractions.test.cpp delete mode 100644 Tests/Unit/gui2/testdareflcore/importtableheader.test.cpp delete mode 100644 Tests/Unit/gui2/testdareflcore/instrumentitems.test.cpp delete mode 100644 Tests/Unit/gui2/testdareflcore/layereditoractions.test.cpp delete mode 100644 Tests/Unit/gui2/testdareflcore/layerelements.test.cpp delete mode 100644 Tests/Unit/gui2/testdareflcore/layeritems.test.cpp delete mode 100644 Tests/Unit/gui2/testdareflcore/layerselectionmodel.test.cpp delete mode 100644 Tests/Unit/gui2/testdareflcore/layerviewmodel.test.cpp delete mode 100644 Tests/Unit/gui2/testdareflcore/materialprofile.test.cpp delete mode 100644 Tests/Unit/gui2/testdareflcore/modelutils.test.cpp delete mode 100644 Tests/Unit/gui2/testdareflcore/quicksimutils.test.cpp delete mode 100644 Tests/Unit/gui2/testdareflcore/sldelementcontroller.test.cpp delete mode 100644 gui2/CMakeLists.txt delete mode 100644 gui2/cmake/modules/ClangFormat.cmake delete mode 100644 gui2/cmake/modules/CodeTools.cmake delete mode 100644 gui2/cmake/modules/configuration.cmake delete mode 100644 gui2/cmake/scripts/testconfig.h.in delete mode 100644 gui2/core/CMakeLists.txt delete mode 100644 gui2/core/app_constants.h delete mode 100644 gui2/dataloader/CMakeLists.txt delete mode 100644 gui2/dataloader/datahandler.cpp delete mode 100644 gui2/dataloader/datahandler.h delete mode 100644 gui2/dataloader/dataloader_constants.h delete mode 100644 gui2/dataloader/dataloader_types.h delete mode 100644 gui2/dataloader/dataloader_utils.cpp delete mode 100644 gui2/dataloader/dataloader_utils.h delete mode 100644 gui2/dataloader/dataloaderdialog.cpp delete mode 100644 gui2/dataloader/dataloaderdialog.h delete mode 100644 gui2/dataloader/dataloadertoolbar.cpp delete mode 100644 gui2/dataloader/dataloadertoolbar.h delete mode 100644 gui2/dataloader/defaultparser.cpp delete mode 100644 gui2/dataloader/defaultparser.h delete mode 100644 gui2/dataloader/importfilewidget.cpp delete mode 100644 gui2/dataloader/importfilewidget.h delete mode 100644 gui2/dataloader/importtableheader.cpp delete mode 100644 gui2/dataloader/importtableheader.h delete mode 100644 gui2/dataloader/importtablemodel.cpp delete mode 100644 gui2/dataloader/importtablemodel.h delete mode 100644 gui2/dataloader/importtablewidget.cpp delete mode 100644 gui2/dataloader/importtablewidget.h delete mode 100644 gui2/dataloader/importtextview.cpp delete mode 100644 gui2/dataloader/importtextview.h delete mode 100644 gui2/dataloader/loaderpreviewpanel.cpp delete mode 100644 gui2/dataloader/loaderpreviewpanel.h delete mode 100644 gui2/dataloader/loaderselectorpanel.cpp delete mode 100644 gui2/dataloader/loaderselectorpanel.h delete mode 100644 gui2/dataloader/parserinterface.h delete mode 100644 gui2/dataloader/parserpropertywidget.cpp delete mode 100644 gui2/dataloader/parserpropertywidget.h delete mode 100644 gui2/importdataview/CMakeLists.txt delete mode 100644 gui2/importdataview/dataselectionmodel.cpp delete mode 100644 gui2/importdataview/dataselectionmodel.h delete mode 100644 gui2/importdataview/dataselectorwidget.cpp delete mode 100644 gui2/importdataview/dataselectorwidget.h delete mode 100644 gui2/importdataview/dataviewmodel.cpp delete mode 100644 gui2/importdataview/dataviewmodel.h delete mode 100644 gui2/importdataview/graphcanvaswidget.cpp delete mode 100644 gui2/importdataview/graphcanvaswidget.h delete mode 100644 gui2/importdataview/graphimportdata.h delete mode 100644 gui2/importdataview/importdataeditor.cpp delete mode 100644 gui2/importdataview/importdataeditor.h delete mode 100644 gui2/importdataview/importdataeditoractions.cpp delete mode 100644 gui2/importdataview/importdataeditoractions.h delete mode 100644 gui2/importdataview/importdataeditortoolbal.cpp delete mode 100644 gui2/importdataview/importdataeditortoolbal.h delete mode 100644 gui2/importdataview/importdataview.cpp delete mode 100644 gui2/importdataview/importdataview.h delete mode 100644 gui2/layereditor/CMakeLists.txt delete mode 100644 gui2/layereditor/customlayertreeeditorfactory.cpp delete mode 100644 gui2/layereditor/customlayertreeeditorfactory.h delete mode 100644 gui2/layereditor/layereditor.cpp delete mode 100644 gui2/layereditor/layereditor.h delete mode 100644 gui2/layereditor/layereditoractions.cpp delete mode 100644 gui2/layereditor/layereditoractions.h delete mode 100644 gui2/layereditor/layereditortoolbar.cpp delete mode 100644 gui2/layereditor/layereditortoolbar.h delete mode 100644 gui2/layereditor/layereditorwidget.cpp delete mode 100644 gui2/layereditor/layereditorwidget.h delete mode 100644 gui2/layereditor/layerselectionmodel.cpp delete mode 100644 gui2/layereditor/layerselectionmodel.h delete mode 100644 gui2/layereditor/layertreeview.cpp delete mode 100644 gui2/layereditor/layertreeview.h delete mode 100644 gui2/layereditor/layerviewmodel.cpp delete mode 100644 gui2/layereditor/layerviewmodel.h delete mode 100644 gui2/layereditor/layerviewmodelcontroller.cpp delete mode 100644 gui2/layereditor/layerviewmodelcontroller.h delete mode 100644 gui2/main.cpp delete mode 100644 gui2/mainwindow/CMakeLists.txt delete mode 100644 gui2/mainwindow/actionmanager.cpp delete mode 100644 gui2/mainwindow/actionmanager.h delete mode 100644 gui2/mainwindow/fancytab.cpp delete mode 100644 gui2/mainwindow/fancytab.h delete mode 100644 gui2/mainwindow/mainbarwidget.cpp delete mode 100644 gui2/mainwindow/mainbarwidget.h delete mode 100644 gui2/mainwindow/mainwindow.cpp delete mode 100644 gui2/mainwindow/mainwindow.h delete mode 100644 gui2/mainwindow/simulationview.cpp delete mode 100644 gui2/mainwindow/simulationview.h delete mode 100644 gui2/mainwindow/styleutils.cpp delete mode 100644 gui2/mainwindow/styleutils.h delete mode 100644 gui2/materialeditor/CMakeLists.txt delete mode 100644 gui2/materialeditor/materialeditor.cpp delete mode 100644 gui2/materialeditor/materialeditor.h delete mode 100644 gui2/materialeditor/materialeditoractions.cpp delete mode 100644 gui2/materialeditor/materialeditoractions.h delete mode 100644 gui2/materialeditor/materialeditortoolbar.cpp delete mode 100644 gui2/materialeditor/materialeditortoolbar.h delete mode 100644 gui2/materialeditor/materialeditorwidget.cpp delete mode 100644 gui2/materialeditor/materialeditorwidget.h delete mode 100644 gui2/materialeditor/materialselectionmodel.cpp delete mode 100644 gui2/materialeditor/materialselectionmodel.h delete mode 100644 gui2/materialeditor/materialtableview.cpp delete mode 100644 gui2/materialeditor/materialtableview.h delete mode 100644 gui2/materialeditor/materialtreeview.cpp delete mode 100644 gui2/materialeditor/materialtreeview.h delete mode 100644 gui2/model/CMakeLists.txt delete mode 100644 gui2/model/applicationmodels.cpp delete mode 100644 gui2/model/applicationmodels.h delete mode 100644 gui2/model/experimentaldatacontroller.cpp delete mode 100644 gui2/model/experimentaldatacontroller.h delete mode 100644 gui2/model/experimentaldataitems.cpp delete mode 100644 gui2/model/experimentaldataitems.h delete mode 100644 gui2/model/experimentaldatamodel.cpp delete mode 100644 gui2/model/experimentaldatamodel.h delete mode 100644 gui2/model/instrumentitems.cpp delete mode 100644 gui2/model/instrumentitems.h delete mode 100644 gui2/model/instrumentmodel.cpp delete mode 100644 gui2/model/instrumentmodel.h delete mode 100644 gui2/model/item_constants.h delete mode 100644 gui2/model/jobitem.cpp delete mode 100644 gui2/model/jobitem.h delete mode 100644 gui2/model/jobmodel.cpp delete mode 100644 gui2/model/jobmodel.h delete mode 100644 gui2/model/materialitems.cpp delete mode 100644 gui2/model/materialitems.h delete mode 100644 gui2/model/materialmodel.cpp delete mode 100644 gui2/model/materialmodel.h delete mode 100644 gui2/model/materialpropertycontroller.cpp delete mode 100644 gui2/model/materialpropertycontroller.h delete mode 100644 gui2/model/modelutils.cpp delete mode 100644 gui2/model/modelutils.h delete mode 100644 gui2/model/sampleitems.cpp delete mode 100644 gui2/model/sampleitems.h delete mode 100644 gui2/model/samplemodel.cpp delete mode 100644 gui2/model/samplemodel.h delete mode 100644 gui2/quicksimeditor/CMakeLists.txt delete mode 100644 gui2/quicksimeditor/custombeampropertyeditorfactory.cpp delete mode 100644 gui2/quicksimeditor/custombeampropertyeditorfactory.h delete mode 100644 gui2/quicksimeditor/instrumentpropertyeditor.cpp delete mode 100644 gui2/quicksimeditor/instrumentpropertyeditor.h delete mode 100644 gui2/quicksimeditor/jobmanager.cpp delete mode 100644 gui2/quicksimeditor/jobmanager.h delete mode 100644 gui2/quicksimeditor/materialprofile.cpp delete mode 100644 gui2/quicksimeditor/materialprofile.h delete mode 100644 gui2/quicksimeditor/profilehelper.cpp delete mode 100644 gui2/quicksimeditor/profilehelper.h delete mode 100644 gui2/quicksimeditor/quicksim_types.h delete mode 100644 gui2/quicksimeditor/quicksimcontroller.cpp delete mode 100644 gui2/quicksimeditor/quicksimcontroller.h delete mode 100644 gui2/quicksimeditor/quicksimeditor.cpp delete mode 100644 gui2/quicksimeditor/quicksimeditor.h delete mode 100644 gui2/quicksimeditor/quicksimeditortoolbar.cpp delete mode 100644 gui2/quicksimeditor/quicksimeditortoolbar.h delete mode 100644 gui2/quicksimeditor/quicksimutils.cpp delete mode 100644 gui2/quicksimeditor/quicksimutils.h delete mode 100644 gui2/quicksimeditor/simplotcontroller.cpp delete mode 100644 gui2/quicksimeditor/simplotcontroller.h delete mode 100644 gui2/quicksimeditor/simplotwidget.cpp delete mode 100644 gui2/quicksimeditor/simplotwidget.h delete mode 100644 gui2/resources/CMakeLists.txt delete mode 100644 gui2/resources/icons.qrc delete mode 100644 gui2/resources/icons/F-letter_1000x.png delete mode 100644 gui2/resources/icons/arrow-down-circle-outline.svg delete mode 100644 gui2/resources/icons/arrow-up-circle-outline.svg delete mode 100644 gui2/resources/icons/aspect-ratio.svg delete mode 100644 gui2/resources/icons/beaker-remove-outline.svg delete mode 100644 gui2/resources/icons/card-bulleted-outline.svg delete mode 100644 gui2/resources/icons/close-circle-outline.svg delete mode 100644 gui2/resources/icons/cog-outline.svg delete mode 100644 gui2/resources/icons/dock-left.svg delete mode 100644 gui2/resources/icons/dock-right.svg delete mode 100644 gui2/resources/icons/export.svg delete mode 100644 gui2/resources/icons/import.svg delete mode 100644 gui2/resources/icons/layers-outline.svg delete mode 100644 gui2/resources/icons/layers-triple-outline.svg delete mode 100644 gui2/resources/icons/play-circle-outline.svg delete mode 100644 gui2/resources/icons/plus-box-multiple-outline.svg delete mode 100644 gui2/resources/icons/plus-box-outline.svg delete mode 100644 gui2/resources/icons/plus-circle-multiple-outline.svg delete mode 100644 gui2/resources/icons/plus-circle-outline.svg delete mode 100644 gui2/resources/icons/redo.svg delete mode 100644 gui2/resources/icons/set-merge.svg delete mode 100644 gui2/resources/icons/undo.svg delete mode 100644 gui2/resources/resources.h delete mode 100644 gui2/settingsview/CMakeLists.txt delete mode 100644 gui2/settingsview/settingsview.cpp delete mode 100644 gui2/settingsview/settingsview.h delete mode 100644 gui2/sldeditor/CMakeLists.txt delete mode 100644 gui2/sldeditor/elementview.cpp delete mode 100644 gui2/sldeditor/elementview.h delete mode 100644 gui2/sldeditor/graphicsscene.cpp delete mode 100644 gui2/sldeditor/graphicsscene.h delete mode 100644 gui2/sldeditor/handleelementview.cpp delete mode 100644 gui2/sldeditor/handleelementview.h delete mode 100644 gui2/sldeditor/layerelementcontroller.cpp delete mode 100644 gui2/sldeditor/layerelementcontroller.h delete mode 100644 gui2/sldeditor/layerelementitem.cpp delete mode 100644 gui2/sldeditor/layerelementitem.h delete mode 100644 gui2/sldeditor/roughnesselementview.cpp delete mode 100644 gui2/sldeditor/roughnesselementview.h delete mode 100644 gui2/sldeditor/segmentelementview.cpp delete mode 100644 gui2/sldeditor/segmentelementview.h delete mode 100644 gui2/sldeditor/sldeditor.cpp delete mode 100644 gui2/sldeditor/sldeditor.h delete mode 100644 gui2/sldeditor/sldeditoractions.cpp delete mode 100644 gui2/sldeditor/sldeditoractions.h delete mode 100644 gui2/sldeditor/sldeditortoolbar.cpp delete mode 100644 gui2/sldeditor/sldeditortoolbar.h delete mode 100644 gui2/sldeditor/sldelementcontroller.cpp delete mode 100644 gui2/sldeditor/sldelementcontroller.h delete mode 100644 gui2/sldeditor/sldelementmodel.cpp delete mode 100644 gui2/sldeditor/sldelementmodel.h delete mode 100644 gui2/sldeditor/sldviewwidget.cpp delete mode 100644 gui2/sldeditor/sldviewwidget.h delete mode 100644 gui2/welcomeview/CMakeLists.txt delete mode 100644 gui2/welcomeview/openprojectwidget.cpp delete mode 100644 gui2/welcomeview/openprojectwidget.h delete mode 100644 gui2/welcomeview/projecthandler.cpp delete mode 100644 gui2/welcomeview/projecthandler.h delete mode 100644 gui2/welcomeview/projectpanewidget.cpp delete mode 100644 gui2/welcomeview/projectpanewidget.h delete mode 100644 gui2/welcomeview/recentprojectsettings.cpp delete mode 100644 gui2/welcomeview/recentprojectsettings.h delete mode 100644 gui2/welcomeview/recentprojectwidget.cpp delete mode 100644 gui2/welcomeview/recentprojectwidget.h delete mode 100644 gui2/welcomeview/userinteractor.cpp delete mode 100644 gui2/welcomeview/userinteractor.h delete mode 100644 gui2/welcomeview/welcomeview.cpp delete mode 100644 gui2/welcomeview/welcomeview.h delete mode 100644 mvvm/CMakeLists.txt delete mode 100644 mvvm/cmake/modules/ClangFormat.cmake delete mode 100644 mvvm/cmake/modules/CodeTools.cmake delete mode 100644 mvvm/cmake/modules/configuration.cmake delete mode 100644 mvvm/cmake/modules/installation.cmake delete mode 100644 mvvm/cmake/scripts/MVVMConfig.cmake.in delete mode 100644 mvvm/cmake/scripts/mvvm_version.h.in delete mode 100644 mvvm/cmake/scripts/testconfig.h.in delete mode 100644 mvvm/model/CMakeLists.txt delete mode 100644 mvvm/model/mvvm/CMakeLists.txt delete mode 100644 mvvm/model/mvvm/commands/CMakeLists.txt delete mode 100644 mvvm/model/mvvm/commands/abstractitemcommand.cpp delete mode 100644 mvvm/model/mvvm/commands/abstractitemcommand.h delete mode 100644 mvvm/model/mvvm/commands/commandadapter.cpp delete mode 100644 mvvm/model/mvvm/commands/commandadapter.h delete mode 100644 mvvm/model/mvvm/commands/commandresult.h delete mode 100644 mvvm/model/mvvm/commands/commandservice.cpp delete mode 100644 mvvm/model/mvvm/commands/commandservice.h delete mode 100644 mvvm/model/mvvm/commands/commandutils.cpp delete mode 100644 mvvm/model/mvvm/commands/commandutils.h delete mode 100644 mvvm/model/mvvm/commands/copyitemcommand.cpp delete mode 100644 mvvm/model/mvvm/commands/copyitemcommand.h delete mode 100644 mvvm/model/mvvm/commands/insertnewitemcommand.cpp delete mode 100644 mvvm/model/mvvm/commands/insertnewitemcommand.h delete mode 100644 mvvm/model/mvvm/commands/moveitemcommand.cpp delete mode 100644 mvvm/model/mvvm/commands/moveitemcommand.h delete mode 100644 mvvm/model/mvvm/commands/removeitemcommand.cpp delete mode 100644 mvvm/model/mvvm/commands/removeitemcommand.h delete mode 100644 mvvm/model/mvvm/commands/setvaluecommand.cpp delete mode 100644 mvvm/model/mvvm/commands/setvaluecommand.h delete mode 100644 mvvm/model/mvvm/commands/undostack.cpp delete mode 100644 mvvm/model/mvvm/commands/undostack.h delete mode 100644 mvvm/model/mvvm/core/CMakeLists.txt delete mode 100644 mvvm/model/mvvm/core/filesystem.h delete mode 100644 mvvm/model/mvvm/core/types.h delete mode 100644 mvvm/model/mvvm/core/uniqueidgenerator.cpp delete mode 100644 mvvm/model/mvvm/core/uniqueidgenerator.h delete mode 100644 mvvm/model/mvvm/core/variant.h delete mode 100644 mvvm/model/mvvm/core/version.h delete mode 100644 mvvm/model/mvvm/factories/CMakeLists.txt delete mode 100644 mvvm/model/mvvm/factories/itemcataloguefactory.cpp delete mode 100644 mvvm/model/mvvm/factories/itemcataloguefactory.h delete mode 100644 mvvm/model/mvvm/factories/itemconverterfactory.cpp delete mode 100644 mvvm/model/mvvm/factories/itemconverterfactory.h delete mode 100644 mvvm/model/mvvm/factories/modelconverterfactory.cpp delete mode 100644 mvvm/model/mvvm/factories/modelconverterfactory.h delete mode 100644 mvvm/model/mvvm/factories/modeldocumentfactory.cpp delete mode 100644 mvvm/model/mvvm/factories/modeldocumentfactory.h delete mode 100644 mvvm/model/mvvm/factories/projectmanagerfactory.cpp delete mode 100644 mvvm/model/mvvm/factories/projectmanagerfactory.h delete mode 100644 mvvm/model/mvvm/interfaces/CMakeLists.txt delete mode 100644 mvvm/model/mvvm/interfaces/applicationmodelsinterface.h delete mode 100644 mvvm/model/mvvm/interfaces/itembackupstrategy.h delete mode 100644 mvvm/model/mvvm/interfaces/itemcopystrategy.h delete mode 100644 mvvm/model/mvvm/interfaces/itemfactoryinterface.h delete mode 100644 mvvm/model/mvvm/interfaces/itemlistenerinterface.h delete mode 100644 mvvm/model/mvvm/interfaces/modeldocumentinterface.h delete mode 100644 mvvm/model/mvvm/interfaces/modellistenerinterface.h delete mode 100644 mvvm/model/mvvm/interfaces/projectinterface.h delete mode 100644 mvvm/model/mvvm/interfaces/projectmanagerinterface.h delete mode 100644 mvvm/model/mvvm/interfaces/undostackinterface.h delete mode 100644 mvvm/model/mvvm/model/CMakeLists.txt delete mode 100644 mvvm/model/mvvm/model/comboproperty.cpp delete mode 100644 mvvm/model/mvvm/model/comboproperty.h delete mode 100644 mvvm/model/mvvm/model/comparators.cpp delete mode 100644 mvvm/model/mvvm/model/comparators.h delete mode 100644 mvvm/model/mvvm/model/compounditem.cpp delete mode 100644 mvvm/model/mvvm/model/compounditem.h delete mode 100644 mvvm/model/mvvm/model/customvariants.cpp delete mode 100644 mvvm/model/mvvm/model/customvariants.h delete mode 100644 mvvm/model/mvvm/model/datarole.cpp delete mode 100644 mvvm/model/mvvm/model/datarole.h delete mode 100644 mvvm/model/mvvm/model/externalproperty.cpp delete mode 100644 mvvm/model/mvvm/model/externalproperty.h delete mode 100644 mvvm/model/mvvm/model/function_types.h delete mode 100644 mvvm/model/mvvm/model/groupitem.cpp delete mode 100644 mvvm/model/mvvm/model/groupitem.h delete mode 100644 mvvm/model/mvvm/model/itemcatalogue.cpp delete mode 100644 mvvm/model/mvvm/model/itemcatalogue.h delete mode 100644 mvvm/model/mvvm/model/itemfactory.cpp delete mode 100644 mvvm/model/mvvm/model/itemfactory.h delete mode 100644 mvvm/model/mvvm/model/itemmanager.cpp delete mode 100644 mvvm/model/mvvm/model/itemmanager.h delete mode 100644 mvvm/model/mvvm/model/itempool.cpp delete mode 100644 mvvm/model/mvvm/model/itempool.h delete mode 100644 mvvm/model/mvvm/model/itemutils.cpp delete mode 100644 mvvm/model/mvvm/model/itemutils.h delete mode 100644 mvvm/model/mvvm/model/modelutils.cpp delete mode 100644 mvvm/model/mvvm/model/modelutils.h delete mode 100644 mvvm/model/mvvm/model/mvvm_types.h delete mode 100644 mvvm/model/mvvm/model/path.cpp delete mode 100644 mvvm/model/mvvm/model/path.h delete mode 100644 mvvm/model/mvvm/model/propertyitem.cpp delete mode 100644 mvvm/model/mvvm/model/propertyitem.h delete mode 100644 mvvm/model/mvvm/model/sessionitem.cpp delete mode 100644 mvvm/model/mvvm/model/sessionitem.h delete mode 100644 mvvm/model/mvvm/model/sessionitemcontainer.cpp delete mode 100644 mvvm/model/mvvm/model/sessionitemcontainer.h delete mode 100644 mvvm/model/mvvm/model/sessionitemdata.cpp delete mode 100644 mvvm/model/mvvm/model/sessionitemdata.h delete mode 100644 mvvm/model/mvvm/model/sessionitemtags.cpp delete mode 100644 mvvm/model/mvvm/model/sessionitemtags.h delete mode 100644 mvvm/model/mvvm/model/sessionmodel.cpp delete mode 100644 mvvm/model/mvvm/model/sessionmodel.h delete mode 100644 mvvm/model/mvvm/model/taginfo.cpp delete mode 100644 mvvm/model/mvvm/model/taginfo.h delete mode 100644 mvvm/model/mvvm/model/tagrow.cpp delete mode 100644 mvvm/model/mvvm/model/tagrow.h delete mode 100644 mvvm/model/mvvm/model/variant_constants.h delete mode 100644 mvvm/model/mvvm/project/CMakeLists.txt delete mode 100644 mvvm/model/mvvm/project/modelhaschangedcontroller.cpp delete mode 100644 mvvm/model/mvvm/project/modelhaschangedcontroller.h delete mode 100644 mvvm/model/mvvm/project/project.cpp delete mode 100644 mvvm/model/mvvm/project/project.h delete mode 100644 mvvm/model/mvvm/project/project_types.h delete mode 100644 mvvm/model/mvvm/project/projectchangecontroller.cpp delete mode 100644 mvvm/model/mvvm/project/projectchangecontroller.h delete mode 100644 mvvm/model/mvvm/project/projectmanager.cpp delete mode 100644 mvvm/model/mvvm/project/projectmanager.h delete mode 100644 mvvm/model/mvvm/project/projectmanagerdecorator.cpp delete mode 100644 mvvm/model/mvvm/project/projectmanagerdecorator.h delete mode 100644 mvvm/model/mvvm/project/projectutils.cpp delete mode 100644 mvvm/model/mvvm/project/projectutils.h delete mode 100644 mvvm/model/mvvm/serialization/CMakeLists.txt delete mode 100644 mvvm/model/mvvm/serialization/compatibilityutils.cpp delete mode 100644 mvvm/model/mvvm/serialization/compatibilityutils.h delete mode 100644 mvvm/model/mvvm/serialization/jsonconverterinterfaces.h delete mode 100644 mvvm/model/mvvm/serialization/jsondocument.cpp delete mode 100644 mvvm/model/mvvm/serialization/jsondocument.h delete mode 100644 mvvm/model/mvvm/serialization/jsonitem_types.h delete mode 100644 mvvm/model/mvvm/serialization/jsonitembackupstrategy.cpp delete mode 100644 mvvm/model/mvvm/serialization/jsonitembackupstrategy.h delete mode 100644 mvvm/model/mvvm/serialization/jsonitemcontainerconverter.cpp delete mode 100644 mvvm/model/mvvm/serialization/jsonitemcontainerconverter.h delete mode 100644 mvvm/model/mvvm/serialization/jsonitemconverter.cpp delete mode 100644 mvvm/model/mvvm/serialization/jsonitemconverter.h delete mode 100644 mvvm/model/mvvm/serialization/jsonitemconverterinterface.h delete mode 100644 mvvm/model/mvvm/serialization/jsonitemcopystrategy.cpp delete mode 100644 mvvm/model/mvvm/serialization/jsonitemcopystrategy.h delete mode 100644 mvvm/model/mvvm/serialization/jsonitemdataconverter.cpp delete mode 100644 mvvm/model/mvvm/serialization/jsonitemdataconverter.h delete mode 100644 mvvm/model/mvvm/serialization/jsonitemdataconverterinterface.h delete mode 100644 mvvm/model/mvvm/serialization/jsonitemformatassistant.cpp delete mode 100644 mvvm/model/mvvm/serialization/jsonitemformatassistant.h delete mode 100644 mvvm/model/mvvm/serialization/jsonitemtagsconverter.cpp delete mode 100644 mvvm/model/mvvm/serialization/jsonitemtagsconverter.h delete mode 100644 mvvm/model/mvvm/serialization/jsonmodelconverter.cpp delete mode 100644 mvvm/model/mvvm/serialization/jsonmodelconverter.h delete mode 100644 mvvm/model/mvvm/serialization/jsonmodelconverterinterface.h delete mode 100644 mvvm/model/mvvm/serialization/jsontaginfoconverter.cpp delete mode 100644 mvvm/model/mvvm/serialization/jsontaginfoconverter.h delete mode 100644 mvvm/model/mvvm/serialization/jsontaginfoconverterinterface.h delete mode 100644 mvvm/model/mvvm/serialization/jsonutils.cpp delete mode 100644 mvvm/model/mvvm/serialization/jsonutils.h delete mode 100644 mvvm/model/mvvm/serialization/jsonvariantconverter.cpp delete mode 100644 mvvm/model/mvvm/serialization/jsonvariantconverter.h delete mode 100644 mvvm/model/mvvm/serialization/jsonvariantconverterinterface.h delete mode 100644 mvvm/model/mvvm/signals/CMakeLists.txt delete mode 100644 mvvm/model/mvvm/signals/callback_types.h delete mode 100644 mvvm/model/mvvm/signals/callbackcontainer.h delete mode 100644 mvvm/model/mvvm/signals/itemlistener.h delete mode 100644 mvvm/model/mvvm/signals/itemlistenerbase.cpp delete mode 100644 mvvm/model/mvvm/signals/itemlistenerbase.h delete mode 100644 mvvm/model/mvvm/signals/itemmapper.cpp delete mode 100644 mvvm/model/mvvm/signals/itemmapper.h delete mode 100644 mvvm/model/mvvm/signals/modellistener.h delete mode 100644 mvvm/model/mvvm/signals/modellistenerbase.cpp delete mode 100644 mvvm/model/mvvm/signals/modellistenerbase.h delete mode 100644 mvvm/model/mvvm/signals/modelmapper.cpp delete mode 100644 mvvm/model/mvvm/signals/modelmapper.h delete mode 100644 mvvm/model/mvvm/standarditems/CMakeLists.txt delete mode 100644 mvvm/model/mvvm/standarditems/axisitems.cpp delete mode 100644 mvvm/model/mvvm/standarditems/axisitems.h delete mode 100644 mvvm/model/mvvm/standarditems/colormapitem.cpp delete mode 100644 mvvm/model/mvvm/standarditems/colormapitem.h delete mode 100644 mvvm/model/mvvm/standarditems/colormapviewportitem.cpp delete mode 100644 mvvm/model/mvvm/standarditems/colormapviewportitem.h delete mode 100644 mvvm/model/mvvm/standarditems/containeritem.cpp delete mode 100644 mvvm/model/mvvm/standarditems/containeritem.h delete mode 100644 mvvm/model/mvvm/standarditems/data1ditem.cpp delete mode 100644 mvvm/model/mvvm/standarditems/data1ditem.h delete mode 100644 mvvm/model/mvvm/standarditems/data2ditem.cpp delete mode 100644 mvvm/model/mvvm/standarditems/data2ditem.h delete mode 100644 mvvm/model/mvvm/standarditems/graphitem.cpp delete mode 100644 mvvm/model/mvvm/standarditems/graphitem.h delete mode 100644 mvvm/model/mvvm/standarditems/graphviewportitem.cpp delete mode 100644 mvvm/model/mvvm/standarditems/graphviewportitem.h delete mode 100644 mvvm/model/mvvm/standarditems/linkeditem.cpp delete mode 100644 mvvm/model/mvvm/standarditems/linkeditem.h delete mode 100644 mvvm/model/mvvm/standarditems/plottableitems.cpp delete mode 100644 mvvm/model/mvvm/standarditems/plottableitems.h delete mode 100644 mvvm/model/mvvm/standarditems/standarditemincludes.h delete mode 100644 mvvm/model/mvvm/standarditems/vectoritem.cpp delete mode 100644 mvvm/model/mvvm/standarditems/vectoritem.h delete mode 100644 mvvm/model/mvvm/standarditems/viewportitem.cpp delete mode 100644 mvvm/model/mvvm/standarditems/viewportitem.h delete mode 100644 mvvm/model/mvvm/utils/CMakeLists.txt delete mode 100644 mvvm/model/mvvm/utils/binutils.cpp delete mode 100644 mvvm/model/mvvm/utils/binutils.h delete mode 100644 mvvm/model/mvvm/utils/containerutils.cpp delete mode 100644 mvvm/model/mvvm/utils/containerutils.h delete mode 100644 mvvm/model/mvvm/utils/fileutils.cpp delete mode 100644 mvvm/model/mvvm/utils/fileutils.h delete mode 100644 mvvm/model/mvvm/utils/ifactory.h delete mode 100644 mvvm/model/mvvm/utils/mathconstants.h delete mode 100644 mvvm/model/mvvm/utils/numericutils.cpp delete mode 100644 mvvm/model/mvvm/utils/numericutils.h delete mode 100644 mvvm/model/mvvm/utils/progresshandler.cpp delete mode 100644 mvvm/model/mvvm/utils/progresshandler.h delete mode 100644 mvvm/model/mvvm/utils/reallimits.cpp delete mode 100644 mvvm/model/mvvm/utils/reallimits.h delete mode 100644 mvvm/model/mvvm/utils/stringutils.cpp delete mode 100644 mvvm/model/mvvm/utils/stringutils.h delete mode 100644 mvvm/model/mvvm/utils/threadsafestack.h delete mode 100644 mvvm/tests/CMakeLists.txt delete mode 100644 mvvm/tests/README.md delete mode 100755 mvvm/tests/data/c++_exec delete mode 100644 mvvm/tests/data/mandelbrot.ppm delete mode 100644 mvvm/tests/data/pdf_file delete mode 100644 mvvm/tests/data/png_file.png delete mode 100644 mvvm/tests/data/text_UTF-8-BOM.txt delete mode 100644 mvvm/tests/data/text_UTF-8.txt delete mode 100644 mvvm/tests/data/word_file delete mode 100644 mvvm/tests/libtestmachinery/CMakeLists.txt delete mode 100644 mvvm/tests/libtestmachinery/folderbasedtest.cpp delete mode 100644 mvvm/tests/libtestmachinery/folderbasedtest.h delete mode 100644 mvvm/tests/libtestmachinery/google_test.h delete mode 100644 mvvm/tests/libtestmachinery/mockinterfaces.h delete mode 100644 mvvm/tests/libtestmachinery/mockwidgets.cpp delete mode 100644 mvvm/tests/libtestmachinery/mockwidgets.h delete mode 100644 mvvm/tests/libtestmachinery/test_utils.cpp delete mode 100644 mvvm/tests/libtestmachinery/test_utils.h delete mode 100644 mvvm/tests/libtestmachinery/toyitems.cpp delete mode 100644 mvvm/tests/libtestmachinery/toyitems.h delete mode 100644 mvvm/tests/libtestmachinery/toymodel.cpp delete mode 100644 mvvm/tests/libtestmachinery/toymodel.h delete mode 100644 mvvm/tests/libtestmachinery/widgetbasedtest.cpp delete mode 100644 mvvm/tests/libtestmachinery/widgetbasedtest.h delete mode 100644 mvvm/tests/testintegration/CMakeLists.txt delete mode 100644 mvvm/tests/testintegration/TestAll.cpp delete mode 100644 mvvm/tests/testintegration/standarditemserialization.test.cpp delete mode 100644 mvvm/tests/testintegration/toyitemslattice.test.cpp delete mode 100644 mvvm/tests/testintegration/toyitemsserialization.test.cpp delete mode 100644 mvvm/tests/testintegration/toyitemsshapegroup.test.cpp delete mode 100644 mvvm/tests/testintegration/undoscenario.test.cpp delete mode 100644 mvvm/tests/testmodel/CMakeLists.txt delete mode 100644 mvvm/tests/testmodel/TestAll.cpp delete mode 100644 mvvm/tests/testmodel/axisitems.test.cpp delete mode 100644 mvvm/tests/testmodel/binutils.test.cpp delete mode 100644 mvvm/tests/testmodel/callbackcontainer.test.cpp delete mode 100644 mvvm/tests/testmodel/colormapitem.test.cpp delete mode 100644 mvvm/tests/testmodel/colormapviewportitem.test.cpp delete mode 100644 mvvm/tests/testmodel/comboproperty.test.cpp delete mode 100644 mvvm/tests/testmodel/compatibilityutils.test.cpp delete mode 100644 mvvm/tests/testmodel/compounditem.test.cpp delete mode 100644 mvvm/tests/testmodel/containeritem.test.cpp delete mode 100644 mvvm/tests/testmodel/containerutils.test.cpp delete mode 100644 mvvm/tests/testmodel/copyitemcommand.test.cpp delete mode 100644 mvvm/tests/testmodel/customvariants.test.cpp delete mode 100644 mvvm/tests/testmodel/data1ditem.test.cpp delete mode 100644 mvvm/tests/testmodel/data2ditem.test.cpp delete mode 100644 mvvm/tests/testmodel/externalproperty.test.cpp delete mode 100644 mvvm/tests/testmodel/fileutils.test.cpp delete mode 100644 mvvm/tests/testmodel/graphitem.test.cpp delete mode 100644 mvvm/tests/testmodel/graphviewportitem.test.cpp delete mode 100644 mvvm/tests/testmodel/groupitem.test.cpp delete mode 100644 mvvm/tests/testmodel/insertnewitemcommand.test.cpp delete mode 100644 mvvm/tests/testmodel/itemcatalogue.test.cpp delete mode 100644 mvvm/tests/testmodel/itemconverterfactory.test.cpp delete mode 100644 mvvm/tests/testmodel/itemlistener.test.cpp delete mode 100644 mvvm/tests/testmodel/itemmanager.test.cpp delete mode 100644 mvvm/tests/testmodel/itemmapper.test.cpp delete mode 100644 mvvm/tests/testmodel/itempool.test.cpp delete mode 100644 mvvm/tests/testmodel/itemutils.test.cpp delete mode 100644 mvvm/tests/testmodel/jsondocument.test.cpp delete mode 100644 mvvm/tests/testmodel/jsonitem_types.test.cpp delete mode 100644 mvvm/tests/testmodel/jsonitembackupstrategy.test.cpp delete mode 100644 mvvm/tests/testmodel/jsonitemcontainerconverter.test.cpp delete mode 100644 mvvm/tests/testmodel/jsonitemconverter.test.cpp delete mode 100644 mvvm/tests/testmodel/jsonitemcopystrategy.test.cpp delete mode 100644 mvvm/tests/testmodel/jsonitemdataconverter.test.cpp delete mode 100644 mvvm/tests/testmodel/jsonitemformatassistant.test.cpp delete mode 100644 mvvm/tests/testmodel/jsonmodelconverter.test.cpp delete mode 100644 mvvm/tests/testmodel/jsontaginfoconverter.test.cpp delete mode 100644 mvvm/tests/testmodel/jsonutils.test.cpp delete mode 100644 mvvm/tests/testmodel/jsonvariantconverter.test.cpp delete mode 100644 mvvm/tests/testmodel/linkeditem.test.cpp delete mode 100644 mvvm/tests/testmodel/modelhaschangedcontroller.test.cpp delete mode 100644 mvvm/tests/testmodel/modellistener.test.cpp delete mode 100644 mvvm/tests/testmodel/modelmapper.test.cpp delete mode 100644 mvvm/tests/testmodel/modelutils.test.cpp delete mode 100644 mvvm/tests/testmodel/moveitemcommand.test.cpp delete mode 100644 mvvm/tests/testmodel/numericutils.test.cpp delete mode 100644 mvvm/tests/testmodel/path.test.cpp delete mode 100644 mvvm/tests/testmodel/plottableitems.test.cpp delete mode 100644 mvvm/tests/testmodel/progresshandler.test.cpp delete mode 100644 mvvm/tests/testmodel/project.test.cpp delete mode 100644 mvvm/tests/testmodel/projectchangecontroller.test.cpp delete mode 100644 mvvm/tests/testmodel/projectmanager.test.cpp delete mode 100644 mvvm/tests/testmodel/projectmanagerdecorator.test.cpp delete mode 100644 mvvm/tests/testmodel/projectutils.test.cpp delete mode 100644 mvvm/tests/testmodel/reallimits.test.cpp delete mode 100644 mvvm/tests/testmodel/removeitemcommand.test.cpp delete mode 100644 mvvm/tests/testmodel/sessionitem.test.cpp delete mode 100644 mvvm/tests/testmodel/sessionitemcontainer.test.cpp delete mode 100644 mvvm/tests/testmodel/sessionitemdata.test.cpp delete mode 100644 mvvm/tests/testmodel/sessionitemtags.test.cpp delete mode 100644 mvvm/tests/testmodel/sessionmodel.test.cpp delete mode 100644 mvvm/tests/testmodel/setvaluecommand.test.cpp delete mode 100644 mvvm/tests/testmodel/stringutils.test.cpp delete mode 100644 mvvm/tests/testmodel/taginfo.test.cpp delete mode 100644 mvvm/tests/testmodel/tagrow.test.cpp delete mode 100644 mvvm/tests/testmodel/test_utils.test.cpp delete mode 100644 mvvm/tests/testmodel/threadsafestack.test.cpp delete mode 100644 mvvm/tests/testmodel/undostack.test.cpp delete mode 100644 mvvm/tests/testmodel/vectoritem.test.cpp delete mode 100644 mvvm/tests/testview/CMakeLists.txt delete mode 100644 mvvm/tests/testview/TestAll.cpp delete mode 100644 mvvm/tests/testview/axistitlecontroller.test.cpp delete mode 100644 mvvm/tests/testview/colormapplotcontroller.test.cpp delete mode 100644 mvvm/tests/testview/colormapviewportplotcontroller.test.cpp delete mode 100644 mvvm/tests/testview/customplot_test_utils.cpp delete mode 100644 mvvm/tests/testview/customplot_test_utils.h delete mode 100644 mvvm/tests/testview/customplot_test_utils.test.cpp delete mode 100644 mvvm/tests/testview/customplotsceneadapter.test.cpp delete mode 100644 mvvm/tests/testview/data1dplotcontroller.test.cpp delete mode 100644 mvvm/tests/testview/data2dplotcontroller.test.cpp delete mode 100644 mvvm/tests/testview/graphplotcontroller.test.cpp delete mode 100644 mvvm/tests/testview/graphviewportplotcontroller.test.cpp delete mode 100644 mvvm/tests/testview/pencontroller.test.cpp delete mode 100644 mvvm/tests/testview/propertyflatview.test.cpp delete mode 100644 mvvm/tests/testview/viewportaxisplotcontroller.test.cpp delete mode 100644 mvvm/tests/testview/widgetutils.test.cpp delete mode 100644 mvvm/tests/testviewmodel/CMakeLists.txt delete mode 100644 mvvm/tests/testviewmodel/TestAll.cpp delete mode 100644 mvvm/tests/testviewmodel/TestToyLayerItem.cpp delete mode 100644 mvvm/tests/testviewmodel/TestToyMultiLayerItem.cpp delete mode 100644 mvvm/tests/testviewmodel/TestToyParticleItem.cpp delete mode 100644 mvvm/tests/testviewmodel/defaulteditorfactory.test.cpp delete mode 100644 mvvm/tests/testviewmodel/defaultviewmodel.test.cpp delete mode 100644 mvvm/tests/testviewmodel/labeldatarowstrategy.test.cpp delete mode 100644 mvvm/tests/testviewmodel/propertiesrowstrategy.test.cpp delete mode 100644 mvvm/tests/testviewmodel/propertyflatviewmodel.test.cpp delete mode 100644 mvvm/tests/testviewmodel/propertytableviewmodel.test.cpp delete mode 100644 mvvm/tests/testviewmodel/propertyviewmodel.test.cpp delete mode 100644 mvvm/tests/testviewmodel/scientificspinbox.test.cpp delete mode 100644 mvvm/tests/testviewmodel/standardchildrenstrategies.test.cpp delete mode 100644 mvvm/tests/testviewmodel/standardviewitems.test.cpp delete mode 100644 mvvm/tests/testviewmodel/topitemsviewmodel.test.cpp delete mode 100644 mvvm/tests/testviewmodel/viewitem.test.cpp delete mode 100644 mvvm/tests/testviewmodel/viewmodelbase.test.cpp delete mode 100644 mvvm/tests/testviewmodel/viewmodelcontroller.test.cpp delete mode 100644 mvvm/tests/testviewmodel/viewmodelcontrollerbuilder.test.cpp delete mode 100644 mvvm/tests/testviewmodel/viewmodelcontrollerfactory.test.cpp delete mode 100644 mvvm/tests/testviewmodel/viewmodeldelegate.test.cpp delete mode 100644 mvvm/tests/testviewmodel/viewmodelfactory.test.cpp delete mode 100644 mvvm/tests/testviewmodel/viewmodelutils.test.cpp delete mode 100644 mvvm/view/CMakeLists.txt delete mode 100644 mvvm/view/mvvm/CMakeLists.txt delete mode 100644 mvvm/view/mvvm/plotting/CMakeLists.txt delete mode 100644 mvvm/view/mvvm/plotting/axistitlecontroller.cpp delete mode 100644 mvvm/view/mvvm/plotting/axistitlecontroller.h delete mode 100644 mvvm/view/mvvm/plotting/colormapcanvas.cpp delete mode 100644 mvvm/view/mvvm/plotting/colormapcanvas.h delete mode 100644 mvvm/view/mvvm/plotting/colormapinfoformatter.cpp delete mode 100644 mvvm/view/mvvm/plotting/colormapinfoformatter.h delete mode 100644 mvvm/view/mvvm/plotting/colormapplotcontroller.cpp delete mode 100644 mvvm/view/mvvm/plotting/colormapplotcontroller.h delete mode 100644 mvvm/view/mvvm/plotting/colormapviewportplotcontroller.cpp delete mode 100644 mvvm/view/mvvm/plotting/colormapviewportplotcontroller.h delete mode 100644 mvvm/view/mvvm/plotting/colorscaleplotcontroller.cpp delete mode 100644 mvvm/view/mvvm/plotting/colorscaleplotcontroller.h delete mode 100644 mvvm/view/mvvm/plotting/customplotproxywidget.cpp delete mode 100644 mvvm/view/mvvm/plotting/customplotproxywidget.h delete mode 100644 mvvm/view/mvvm/plotting/customplotsceneadapter.cpp delete mode 100644 mvvm/view/mvvm/plotting/customplotsceneadapter.h delete mode 100644 mvvm/view/mvvm/plotting/customplotutils.cpp delete mode 100644 mvvm/view/mvvm/plotting/customplotutils.h delete mode 100644 mvvm/view/mvvm/plotting/data1dplotcontroller.cpp delete mode 100644 mvvm/view/mvvm/plotting/data1dplotcontroller.h delete mode 100644 mvvm/view/mvvm/plotting/data2dplotcontroller.cpp delete mode 100644 mvvm/view/mvvm/plotting/data2dplotcontroller.h delete mode 100644 mvvm/view/mvvm/plotting/graphcanvas.cpp delete mode 100644 mvvm/view/mvvm/plotting/graphcanvas.h delete mode 100644 mvvm/view/mvvm/plotting/graphinfoformatter.cpp delete mode 100644 mvvm/view/mvvm/plotting/graphinfoformatter.h delete mode 100644 mvvm/view/mvvm/plotting/graphplotcontroller.cpp delete mode 100644 mvvm/view/mvvm/plotting/graphplotcontroller.h delete mode 100644 mvvm/view/mvvm/plotting/graphviewportplotcontroller.cpp delete mode 100644 mvvm/view/mvvm/plotting/graphviewportplotcontroller.h delete mode 100644 mvvm/view/mvvm/plotting/mousemovereporter.cpp delete mode 100644 mvvm/view/mvvm/plotting/mousemovereporter.h delete mode 100644 mvvm/view/mvvm/plotting/mouseposinfo.h delete mode 100644 mvvm/view/mvvm/plotting/pencontroller.cpp delete mode 100644 mvvm/view/mvvm/plotting/pencontroller.h delete mode 100644 mvvm/view/mvvm/plotting/sceneadapterinterface.h delete mode 100644 mvvm/view/mvvm/plotting/statusstringformatterinterface.h delete mode 100644 mvvm/view/mvvm/plotting/statusstringreporter.cpp delete mode 100644 mvvm/view/mvvm/plotting/statusstringreporter.h delete mode 100644 mvvm/view/mvvm/plotting/statusstringreporterfactory.cpp delete mode 100644 mvvm/view/mvvm/plotting/statusstringreporterfactory.h delete mode 100644 mvvm/view/mvvm/plotting/viewportaxisplotcontroller.cpp delete mode 100644 mvvm/view/mvvm/plotting/viewportaxisplotcontroller.h delete mode 100644 mvvm/view/mvvm/widgets/CMakeLists.txt delete mode 100644 mvvm/view/mvvm/widgets/adjustingscrollarea.cpp delete mode 100644 mvvm/view/mvvm/widgets/adjustingscrollarea.h delete mode 100644 mvvm/view/mvvm/widgets/allitemstreeview.cpp delete mode 100644 mvvm/view/mvvm/widgets/allitemstreeview.h delete mode 100644 mvvm/view/mvvm/widgets/collapsiblebar.cpp delete mode 100644 mvvm/view/mvvm/widgets/collapsiblebar.h delete mode 100644 mvvm/view/mvvm/widgets/collapsiblelistwidget.cpp delete mode 100644 mvvm/view/mvvm/widgets/collapsiblelistwidget.h delete mode 100644 mvvm/view/mvvm/widgets/itemstreeview.cpp delete mode 100644 mvvm/view/mvvm/widgets/itemstreeview.h delete mode 100644 mvvm/view/mvvm/widgets/itemstreeviewinterface.h delete mode 100644 mvvm/view/mvvm/widgets/layoututils.cpp delete mode 100644 mvvm/view/mvvm/widgets/layoututils.h delete mode 100644 mvvm/view/mvvm/widgets/propertyflatview.cpp delete mode 100644 mvvm/view/mvvm/widgets/propertyflatview.h delete mode 100644 mvvm/view/mvvm/widgets/propertytreeview.cpp delete mode 100644 mvvm/view/mvvm/widgets/propertytreeview.h delete mode 100644 mvvm/view/mvvm/widgets/standardtreeviews.h delete mode 100644 mvvm/view/mvvm/widgets/statuslabel.cpp delete mode 100644 mvvm/view/mvvm/widgets/statuslabel.h delete mode 100644 mvvm/view/mvvm/widgets/topitemstreeview.cpp delete mode 100644 mvvm/view/mvvm/widgets/topitemstreeview.h delete mode 100644 mvvm/view/mvvm/widgets/widgetutils.cpp delete mode 100644 mvvm/view/mvvm/widgets/widgetutils.h delete mode 100644 mvvm/viewmodel/CMakeLists.txt delete mode 100644 mvvm/viewmodel/mvvm/CMakeLists.txt delete mode 100644 mvvm/viewmodel/mvvm/editors/CMakeLists.txt delete mode 100644 mvvm/viewmodel/mvvm/editors/booleditor.cpp delete mode 100644 mvvm/viewmodel/mvvm/editors/booleditor.h delete mode 100644 mvvm/viewmodel/mvvm/editors/coloreditor.cpp delete mode 100644 mvvm/viewmodel/mvvm/editors/coloreditor.h delete mode 100644 mvvm/viewmodel/mvvm/editors/combopropertyeditor.cpp delete mode 100644 mvvm/viewmodel/mvvm/editors/combopropertyeditor.h delete mode 100644 mvvm/viewmodel/mvvm/editors/customeditor.cpp delete mode 100644 mvvm/viewmodel/mvvm/editors/customeditor.h delete mode 100644 mvvm/viewmodel/mvvm/editors/customeventfilters.cpp delete mode 100644 mvvm/viewmodel/mvvm/editors/customeventfilters.h delete mode 100644 mvvm/viewmodel/mvvm/editors/defaulteditorfactory.cpp delete mode 100644 mvvm/viewmodel/mvvm/editors/defaulteditorfactory.h delete mode 100644 mvvm/viewmodel/mvvm/editors/doubleeditor.cpp delete mode 100644 mvvm/viewmodel/mvvm/editors/doubleeditor.h delete mode 100644 mvvm/viewmodel/mvvm/editors/editor_constants.h delete mode 100644 mvvm/viewmodel/mvvm/editors/editorbuilders.cpp delete mode 100644 mvvm/viewmodel/mvvm/editors/editorbuilders.h delete mode 100644 mvvm/viewmodel/mvvm/editors/externalpropertycomboeditor.cpp delete mode 100644 mvvm/viewmodel/mvvm/editors/externalpropertycomboeditor.h delete mode 100644 mvvm/viewmodel/mvvm/editors/externalpropertyeditor.cpp delete mode 100644 mvvm/viewmodel/mvvm/editors/externalpropertyeditor.h delete mode 100644 mvvm/viewmodel/mvvm/editors/integereditor.cpp delete mode 100644 mvvm/viewmodel/mvvm/editors/integereditor.h delete mode 100644 mvvm/viewmodel/mvvm/editors/scientificdoubleeditor.cpp delete mode 100644 mvvm/viewmodel/mvvm/editors/scientificdoubleeditor.h delete mode 100644 mvvm/viewmodel/mvvm/editors/scientificspinbox.cpp delete mode 100644 mvvm/viewmodel/mvvm/editors/scientificspinbox.h delete mode 100644 mvvm/viewmodel/mvvm/editors/scientificspinboxeditor.cpp delete mode 100644 mvvm/viewmodel/mvvm/editors/scientificspinboxeditor.h delete mode 100644 mvvm/viewmodel/mvvm/editors/selectablecomboboxeditor.cpp delete mode 100644 mvvm/viewmodel/mvvm/editors/selectablecomboboxeditor.h delete mode 100644 mvvm/viewmodel/mvvm/editors/styleutils.cpp delete mode 100644 mvvm/viewmodel/mvvm/editors/styleutils.h delete mode 100644 mvvm/viewmodel/mvvm/factories/CMakeLists.txt delete mode 100644 mvvm/viewmodel/mvvm/factories/viewmodelcontrollerbuilder.cpp delete mode 100644 mvvm/viewmodel/mvvm/factories/viewmodelcontrollerbuilder.h delete mode 100644 mvvm/viewmodel/mvvm/factories/viewmodelcontrollerfactory.h delete mode 100644 mvvm/viewmodel/mvvm/factories/viewmodelfactory.cpp delete mode 100644 mvvm/viewmodel/mvvm/factories/viewmodelfactory.h delete mode 100644 mvvm/viewmodel/mvvm/interfaces/CMakeLists.txt delete mode 100644 mvvm/viewmodel/mvvm/interfaces/celldecoratorinterface.h delete mode 100644 mvvm/viewmodel/mvvm/interfaces/childrenstrategyinterface.h delete mode 100644 mvvm/viewmodel/mvvm/interfaces/editorfactoryinterface.h delete mode 100644 mvvm/viewmodel/mvvm/interfaces/rowstrategyinterface.h delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/CMakeLists.txt delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/defaultcelldecorator.cpp delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/defaultcelldecorator.h delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/defaultviewmodel.cpp delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/defaultviewmodel.h delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/labeldatarowstrategy.cpp delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/labeldatarowstrategy.h delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/propertiesrowstrategy.cpp delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/propertiesrowstrategy.h delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/propertyflatviewmodel.cpp delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/propertyflatviewmodel.h delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/propertytableviewmodel.cpp delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/propertytableviewmodel.h delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/propertyviewmodel.cpp delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/propertyviewmodel.h delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/standardchildrenstrategies.cpp delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/standardchildrenstrategies.h delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/standardviewitems.cpp delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/standardviewitems.h delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/standardviewmodelcontrollers.cpp delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/standardviewmodelcontrollers.h delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/topitemsviewmodel.cpp delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/topitemsviewmodel.h delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/viewitem.cpp delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/viewitem.h delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/viewmodel.cpp delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/viewmodel.h delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/viewmodelbase.cpp delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/viewmodelbase.h delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/viewmodelcontroller.cpp delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/viewmodelcontroller.h delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/viewmodeldelegate.cpp delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/viewmodeldelegate.h delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/viewmodelutils.cpp delete mode 100644 mvvm/viewmodel/mvvm/viewmodel/viewmodelutils.h diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 949fa0cebc9..e5cd347be43 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -57,7 +57,7 @@ native_Debian_clang: - mkdir build - cd build - cmake .. -DCMAKE_CXX_COMPILER_LAUNCHER=ccache #-DWERROR=ON - - xvfb-run make -j6 # TODO rm mvvm tests, then rm xvfb-run + - make -j6 - xvfb-run ctest -j6 --output-on-failure - make package_source artifacts: diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e692f49d23..cc0034f1ef0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,7 +49,6 @@ add_custom_target(fullcheck COMMAND ${CMAKE_CTEST_COMMAND}) option(BORNAGAIN_PYTHON "Build with python support" ON) option(BORNAGAIN_GUI "Build a graphical user interface" ON) option(BORNAGAIN_TIFF_SUPPORT "Tiff files read/write support" ON) -option(BORNAGAIN_NEWREFL "Compile reflectometry GUI prototype" OFF) # options that are off by default (switch on for additional functionality) option(BORNAGAIN_MPI "Build with MPI support" OFF) @@ -163,10 +162,6 @@ if(BORNAGAIN_TIDY) set(CMAKE_CXX_CLANG_TIDY "clang-tidy") # has effect only if compiler is clang; uses .clang-tidy endif() -if(BORNAGAIN_NEWREFL) - add_subdirectory(mvvm) -endif() - # core components foreach(lib ${CoreComponents}) add_subdirectory(${lib}) @@ -192,10 +187,6 @@ if(BORNAGAIN_GUI) add_subdirectory(Tests/Unit/GUI) add_subdirectory(Tests/Performance/GUI) endif() -if(BORNAGAIN_NEWREFL) - add_subdirectory(gui2) - add_subdirectory(Tests/Unit/gui2) -endif() # TODO: split Core / GUI add_subdirectory(Tests/Functional) diff --git a/Tests/Unit/gui2/CMakeLists.txt b/Tests/Unit/gui2/CMakeLists.txt deleted file mode 100644 index 158fc060fb3..00000000000 --- a/Tests/Unit/gui2/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -include(GoogleTest) - -add_subdirectory(libtestmachinery) -add_subdirectory(testdareflcore) diff --git a/Tests/Unit/gui2/data/Untitled 1.csv b/Tests/Unit/gui2/data/Untitled 1.csv deleted file mode 100644 index 088bdf9ecf3..00000000000 --- a/Tests/Unit/gui2/data/Untitled 1.csv +++ /dev/null @@ -1,3 +0,0 @@ -# hello,,,, -1.23,42,abc,hello world,1.00E-03 -,,,,1.00E-03 diff --git a/Tests/Unit/gui2/data/bilayer1.txt b/Tests/Unit/gui2/data/bilayer1.txt deleted file mode 100644 index 7ea696e626e..00000000000 --- a/Tests/Unit/gui2/data/bilayer1.txt +++ /dev/null @@ -1,503 +0,0 @@ -# BornAgain Intensity Data -# Simple array suitable for numpy, matlab etc. -# coordinates intensities -3.000000000000e-03 1.000000000000e+08 -9.000000000000e-03 1.000000000000e+08 -1.500000000000e-02 1.000000000000e+08 -2.100000000000e-02 1.000000000000e+08 -2.700000000000e-02 1.000000000000e+08 -3.300000000000e-02 1.000000000000e+08 -3.900000000000e-02 1.000000000000e+08 -4.500000000000e-02 1.000000000000e+08 -5.100000000000e-02 1.000000000000e+08 -5.700000000000e-02 1.000000000000e+08 -6.300000000000e-02 1.000000000000e+08 -6.900000000000e-02 1.000000000000e+08 -7.500000000000e-02 1.000000000000e+08 -8.100000000000e-02 1.000000000000e+08 -8.700000000000e-02 1.000000000000e+08 -9.300000000000e-02 1.000000000000e+08 -9.900000000000e-02 1.000000000000e+08 -1.050000000000e-01 1.000000000000e+08 -1.110000000000e-01 1.000000000000e+08 -1.170000000000e-01 1.000000000000e+08 -1.230000000000e-01 1.000000000000e+08 -1.290000000000e-01 1.000000000000e+08 -1.350000000000e-01 1.000000000000e+08 -1.410000000000e-01 1.000000000000e+08 -1.470000000000e-01 1.000000000000e+08 -1.530000000000e-01 1.000000000000e+08 -1.590000000000e-01 1.000000000000e+08 -1.650000000000e-01 1.000000000000e+08 -1.710000000000e-01 1.000000000000e+08 -1.770000000000e-01 1.000000000000e+08 -1.830000000000e-01 1.000000000000e+08 -1.890000000000e-01 1.000000000000e+08 -1.950000000000e-01 1.000000000000e+08 -2.010000000000e-01 1.000000000000e+08 -2.070000000000e-01 1.000000000000e+08 -2.130000000000e-01 1.000000000000e+08 -2.190000000000e-01 1.000000000000e+08 -2.250000000000e-01 1.000000000000e+08 -2.310000000000e-01 1.000000000000e+08 -2.370000000000e-01 1.000000000000e+08 -2.430000000000e-01 1.000000000000e+08 -2.490000000000e-01 1.000000000000e+08 -2.550000000000e-01 1.000000000000e+08 -2.610000000000e-01 1.000000000000e+08 -2.670000000000e-01 1.000000000000e+08 -2.730000000000e-01 1.000000000000e+08 -2.790000000000e-01 1.000000000000e+08 -2.850000000000e-01 1.000000000000e+08 -2.910000000000e-01 1.000000000000e+08 -2.970000000000e-01 1.000000000000e+08 -3.030000000000e-01 1.000000000000e+08 -3.090000000000e-01 1.000000000000e+08 -3.150000000000e-01 5.252514859094e+07 -3.210000000000e-01 3.012698683171e+07 -3.270000000000e-01 2.598017445747e+07 -3.330000000000e-01 2.207918447065e+07 -3.390000000000e-01 1.700163778446e+07 -3.450000000000e-01 1.124384831399e+07 -3.510000000000e-01 5.894216058224e+06 -3.570000000000e-01 2.045875035710e+06 -3.630000000000e-01 2.612612187593e+05 -3.690000000000e-01 3.553472115495e+05 -3.750000000000e-01 1.639935104717e+06 -3.810000000000e-01 3.348957557669e+06 -3.870000000000e-01 4.919489547851e+06 -3.930000000000e-01 6.059794537745e+06 -3.990000000000e-01 6.697353636462e+06 -4.050000000000e-01 6.896494971676e+06 -4.110000000000e-01 6.785236400962e+06 -4.170000000000e-01 6.500627600727e+06 -4.230000000000e-01 6.153148375207e+06 -4.290000000000e-01 5.809750069296e+06 -4.350000000000e-01 5.493631632724e+06 -4.410000000000e-01 5.195872583352e+06 -4.470000000000e-01 4.892004284118e+06 -4.530000000000e-01 4.557147798544e+06 -4.590000000000e-01 4.175921375539e+06 -4.650000000000e-01 3.746248183213e+06 -4.710000000000e-01 3.278240411146e+06 -4.770000000000e-01 2.790207969710e+06 -4.830000000000e-01 2.303819951618e+06 -4.890000000000e-01 1.839923758771e+06 -4.950000000000e-01 1.415807188754e+06 -5.010000000000e-01 1.044000666592e+06 -5.070000000000e-01 7.322305116300e+05 -5.130000000000e-01 4.839406040953e+05 -5.190000000000e-01 2.988835068687e+05 -5.250000000000e-01 1.735342981890e+05 -5.310000000000e-01 1.013542213516e+05 -5.370000000000e-01 7.310587248886e+04 -5.430000000000e-01 7.744355864765e+04 -5.490000000000e-01 1.018917826225e+05 -5.550000000000e-01 1.341514306365e+05 -5.610000000000e-01 1.635185514329e+05 -5.670000000000e-01 1.821233501493e+05 -5.730000000000e-01 1.857166663723e+05 -5.790000000000e-01 1.738314448531e+05 -5.850000000000e-01 1.492907247457e+05 -5.910000000000e-01 1.171801679809e+05 -5.970000000000e-01 8.351662911654e+04 -6.030000000000e-01 5.390001180840e+04 -6.090000000000e-01 3.242267091364e+04 -6.150000000000e-01 2.103412105088e+04 -6.210000000000e-01 1.944030846708e+04 -6.270000000000e-01 2.548896974943e+04 -6.330000000000e-01 3.588964907170e+04 -6.390000000000e-01 4.706291010464e+04 -6.450000000000e-01 5.591549983531e+04 -6.510000000000e-01 6.038737127484e+04 -6.570000000000e-01 5.969251386486e+04 -6.630000000000e-01 5.425572873747e+04 -6.690000000000e-01 4.541285854175e+04 -6.750000000000e-01 3.498081870649e+04 -6.810000000000e-01 2.481237243398e+04 -6.870000000000e-01 1.643234504228e+04 -6.930000000000e-01 1.081552781744e+04 -6.990000000000e-01 8.323253206492e+03 -7.050000000000e-01 8.776764935406e+03 -7.110000000000e-01 1.161947498279e+04 -7.170000000000e-01 1.611085368163e+04 -7.230000000000e-01 2.150103328993e+04 -7.290000000000e-01 2.715243717928e+04 -7.350000000000e-01 3.259627083081e+04 -7.410000000000e-01 3.753117289947e+04 -7.470000000000e-01 4.178420659253e+04 -7.530000000000e-01 4.525877439075e+04 -7.590000000000e-01 4.789064698061e+04 -7.650000000000e-01 4.962470561352e+04 -7.710000000000e-01 5.041468557126e+04 -7.770000000000e-01 5.023946698323e+04 -7.830000000000e-01 4.912454180710e+04 -7.890000000000e-01 4.715697407517e+04 -7.950000000000e-01 4.448580133922e+04 -8.010000000000e-01 4.130570270374e+04 -8.070000000000e-01 3.782778142640e+04 -8.130000000000e-01 3.424561360256e+04 -8.190000000000e-01 3.070615998786e+04 -8.250000000000e-01 2.729353988637e+04 -8.310000000000e-01 2.402973458658e+04 -8.370000000000e-01 2.089133593006e+04 -8.430000000000e-01 1.783697311592e+04 -8.490000000000e-01 1.483727551455e+04 -8.550000000000e-01 1.189883038377e+04 -8.610000000000e-01 9.075542431390e+03 -8.670000000000e-01 6.464438341853e+03 -8.730000000000e-01 4.187223684167e+03 -8.790000000000e-01 2.362640545612e+03 -8.850000000000e-01 1.076952744978e+03 -8.910000000000e-01 3.601982340400e+02 -8.970000000000e-01 1.742228205630e+02 -9.030000000000e-01 4.154585792631e+02 -9.090000000000e-01 9.317689964478e+02 -9.150000000000e-01 1.549375855374e+03 -9.210000000000e-01 2.103689179321e+03 -9.270000000000e-01 2.467234625757e+03 -9.330000000000e-01 2.568856763889e+03 -9.390000000000e-01 2.400630693471e+03 -9.450000000000e-01 2.011826706273e+03 -9.510000000000e-01 1.492127916676e+03 -9.570000000000e-01 9.484554472550e+02 -9.630000000000e-01 4.807798502925e+02 -9.690000000000e-01 1.620542932011e+02 -9.750000000000e-01 2.605684004077e+01 -9.810000000000e-01 6.487373737703e+01 -9.870000000000e-01 2.355145801321e+02 -9.930000000000e-01 4.732386443163e+02 -9.990000000000e-01 7.079811563525e+02 -1.005000000000e+00 8.799899223938e+02 -1.011000000000e+00 9.513840273463e+02 -1.017000000000e+00 9.116016899542e+02 -1.023000000000e+00 7.762665942107e+02 -1.029000000000e+00 5.804931483896e+02 -1.035000000000e+00 3.687501821765e+02 -1.041000000000e+00 1.839108034027e+02 -1.047000000000e+00 5.798768265208e+01 -1.053000000000e+00 6.388412606318e+00 -1.059000000000e+00 2.653308060155e+01 -1.065000000000e+00 1.006134440434e+02 -1.071000000000e+00 2.013840278680e+02 -1.077000000000e+00 2.993372430959e+02 -1.083000000000e+00 3.695056628377e+02 -1.089000000000e+00 3.964284303879e+02 -1.095000000000e+00 3.764023029455e+02 -1.101000000000e+00 3.168468060338e+02 -1.107000000000e+00 2.332747364964e+02 -1.113000000000e+00 1.448322463138e+02 -1.119000000000e+00 6.957433175815e+01 -1.125000000000e+00 2.055942210642e+01 -1.131000000000e+00 3.533273693131e+00 -1.137000000000e+00 1.652519628052e+01 -1.143000000000e+00 5.121453957676e+01 -1.149000000000e+00 9.554819709694e+01 -1.155000000000e+00 1.368741048667e+02 -1.161000000000e+00 1.648298823364e+02 -1.167000000000e+00 1.733725913735e+02 -1.173000000000e+00 1.616013721760e+02 -1.179000000000e+00 1.333357643762e+02 -1.185000000000e+00 9.569404525212e+01 -1.191000000000e+00 5.710882647854e+01 -1.197000000000e+00 2.528930524411e+01 -1.203000000000e+00 5.589633248060e+00 -1.209000000000e+00 9.685301973864e-02 -1.215000000000e+00 7.553960625711e+00 -1.221000000000e+00 2.403421996281e+01 -1.227000000000e+00 4.412637839279e+01 -1.233000000000e+00 6.230639865021e+01 -1.239000000000e+00 7.416948285793e+01 -1.245000000000e+00 7.726682024298e+01 -1.251000000000e+00 7.141005334040e+01 -1.257000000000e+00 5.844076524817e+01 -1.263000000000e+00 4.158068442096e+01 -1.269000000000e+00 2.455658449552e+01 -1.275000000000e+00 1.071971199477e+01 -1.281000000000e+00 2.353813983483e+00 -1.287000000000e+00 3.005661375010e-01 -1.293000000000e+00 3.945723175434e+00 -1.299000000000e+00 1.152495824347e+01 -1.305000000000e+00 2.064351684780e+01 -1.311000000000e+00 2.887025340096e+01 -1.317000000000e+00 3.426774243825e+01 -1.323000000000e+00 3.575131347599e+01 -1.329000000000e+00 3.322041130650e+01 -1.335000000000e+00 2.746211842145e+01 -1.341000000000e+00 1.987594246567e+01 -1.347000000000e+00 1.210128223223e+01 -1.353000000000e+00 5.639532579466e+00 -1.359000000000e+00 1.552136988813e+00 -1.365000000000e+00 2.891476075167e-01 -1.371000000000e+00 1.667923547168e+00 -1.377000000000e+00 4.987115324857e+00 -1.383000000000e+00 9.234405820501e+00 -1.389000000000e+00 1.333226856725e+01 -1.395000000000e+00 1.636558610667e+01 -1.401000000000e+00 1.774650634868e+01 -1.407000000000e+00 1.729124778044e+01 -1.413000000000e+00 1.520545393990e+01 -1.419000000000e+00 1.199411115295e+01 -1.425000000000e+00 8.325218972109e+00 -1.431000000000e+00 4.881478238306e+00 -1.437000000000e+00 2.231397254863e+00 -1.443000000000e+00 7.422547653667e-01 -1.449000000000e+00 5.451669528739e-01 -1.455000000000e+00 1.550194563148e+00 -1.461000000000e+00 3.499631764593e+00 -1.467000000000e+00 6.042002269970e+00 -1.473000000000e+00 8.808330890036e+00 -1.479000000000e+00 1.147534650191e+01 -1.485000000000e+00 1.380601452504e+01 -1.491000000000e+00 1.566446383137e+01 -1.497000000000e+00 1.700833886314e+01 -1.503000000000e+00 1.786569662793e+01 -1.509000000000e+00 1.830524027392e+01 -1.515000000000e+00 1.840804239515e+01 -1.521000000000e+00 1.824657495470e+01 -1.527000000000e+00 1.787371567566e+01 -1.533000000000e+00 1.732134773479e+01 -1.539000000000e+00 1.660591713644e+01 -1.545000000000e+00 1.573723665868e+01 -1.551000000000e+00 1.472694297380e+01 -1.557000000000e+00 1.359404209136e+01 -1.563000000000e+00 1.236646083375e+01 -1.569000000000e+00 1.107897282003e+01 -1.575000000000e+00 9.768900113463e+00 -1.581000000000e+00 8.471401440781e+00 -1.587000000000e+00 7.215944087707e+00 -1.593000000000e+00 6.024889492988e+00 -1.599000000000e+00 4.914271073357e+00 -1.605000000000e+00 3.896087194742e+00 -1.611000000000e+00 2.980986389883e+00 -1.617000000000e+00 2.180184049918e+00 -1.623000000000e+00 1.505789869334e+00 -1.629000000000e+00 9.693084156900e-01 -1.635000000000e+00 5.787099565727e-01 -1.641000000000e+00 3.349652317572e-01 -1.647000000000e+00 2.291592747697e-01 -1.653000000000e+00 2.411940062404e-01 -1.659000000000e+00 3.406976461652e-01 -1.665000000000e+00 4.901959680567e-01 -1.671000000000e+00 6.500200118542e-01 -1.677000000000e+00 7.839776460741e-01 -1.683000000000e+00 8.646113730324e-01 -1.689000000000e+00 8.769449155523e-01 -1.695000000000e+00 8.199569398427e-01 -1.701000000000e+00 7.055219875813e-01 -1.707000000000e+00 5.551020688246e-01 -1.713000000000e+00 3.949308077616e-01 -1.719000000000e+00 2.507066890908e-01 -1.725000000000e+00 1.428527972346e-01 -1.731000000000e+00 8.321410089400e-02 -1.737000000000e+00 7.370702466211e-02 -1.743000000000e+00 1.070003052480e-01 -1.749000000000e+00 1.688914182450e-01 -1.755000000000e+00 2.417360697923e-01 -1.761000000000e+00 3.081445009584e-01 -1.767000000000e+00 3.541918189630e-01 -1.773000000000e+00 3.715744431348e-01 -1.779000000000e+00 3.584251551441e-01 -1.785000000000e+00 3.188048211136e-01 -1.791000000000e+00 2.611519361094e-01 -1.797000000000e+00 1.961413608508e-01 -1.803000000000e+00 1.344569729125e-01 -1.809000000000e+00 8.492388413329e-02 -1.815000000000e+00 5.330271467128e-02 -1.821000000000e+00 4.186401643081e-02 -1.827000000000e+00 4.968121875287e-02 -1.833000000000e+00 7.344371983263e-02 -1.839000000000e+00 1.085210839501e-01 -1.845000000000e+00 1.500095739838e-01 -1.851000000000e+00 1.935515405895e-01 -1.857000000000e+00 2.358130022440e-01 -1.863000000000e+00 2.746071330557e-01 -1.869000000000e+00 3.087361116047e-01 -1.875000000000e+00 3.376739301577e-01 -1.881000000000e+00 3.612224387723e-01 -1.887000000000e+00 3.792470437035e-01 -1.893000000000e+00 3.915500287142e-01 -1.899000000000e+00 3.978849712878e-01 -1.905000000000e+00 3.980706849141e-01 -1.911000000000e+00 3.921382084322e-01 -1.917000000000e+00 3.804433731288e-01 -1.923000000000e+00 3.636972508307e-01 -1.929000000000e+00 3.428989416427e-01 -1.935000000000e+00 3.191889395030e-01 -1.941000000000e+00 2.936667229232e-01 -1.947000000000e+00 2.672266056918e-01 -1.953000000000e+00 2.404593840578e-01 -1.959000000000e+00 2.136469154969e-01 -1.965000000000e+00 1.868490557894e-01 -1.971000000000e+00 1.600555338416e-01 -1.977000000000e+00 1.333568028392e-01 -1.983000000000e+00 1.070824734350e-01 -1.989000000000e+00 8.186467665437e-02 -1.995000000000e+00 5.860389664006e-02 -2.001000000000e+00 3.834085436803e-02 -2.007000000000e+00 2.206302403909e-02 -2.013000000000e+00 1.049195749140e-02 -2.019000000000e+00 3.903507779074e-03 -2.025000000000e+00 2.026067164797e-03 -2.031000000000e+00 4.044061867603e-03 -2.037000000000e+00 8.709742894548e-03 -2.043000000000e+00 1.454169219730e-02 -2.049000000000e+00 2.006920819455e-02 -2.055000000000e+00 2.407213186862e-02 -2.061000000000e+00 2.576758645396e-02 -2.067000000000e+00 2.490765921304e-02 -2.073000000000e+00 2.177199726464e-02 -2.079000000000e+00 1.706185751924e-02 -2.085000000000e+00 1.172224505928e-02 -2.091000000000e+00 6.732132882175e-03 -2.097000000000e+00 2.906860585535e-03 -2.103000000000e+00 7.513376013128e-04 -2.109000000000e+00 3.894261669134e-04 -2.115000000000e+00 1.577226510581e-03 -2.121000000000e+00 3.790028154193e-03 -2.127000000000e+00 6.358289995263e-03 -2.133000000000e+00 8.619967896990e-03 -2.139000000000e+00 1.005602820967e-02 -2.145000000000e+00 1.038254481335e-02 -2.151000000000e+00 9.584376436546e-03 -2.157000000000e+00 7.889143343748e-03 -2.163000000000e+00 5.692946516061e-03 -2.169000000000e+00 3.458367515877e-03 -2.175000000000e+00 1.609142146106e-03 -2.181000000000e+00 4.441814774837e-04 -2.187000000000e+00 8.721929928972e-05 -2.193000000000e+00 4.791188360112e-04 -2.199000000000e+00 1.410038951418e-03 -2.205000000000e+00 2.580414479250e-03 -2.211000000000e+00 3.674665488006e-03 -2.217000000000e+00 4.430482330584e-03 -2.223000000000e+00 4.689267824495e-03 -2.229000000000e+00 4.418878822738e-03 -2.235000000000e+00 3.706706582359e-03 -2.241000000000e+00 2.727739705572e-03 -2.247000000000e+00 1.697165559335e-03 -2.253000000000e+00 8.193972203199e-04 -2.259000000000e+00 2.449263394940e-04 -2.265000000000e+00 4.349777691636e-05 -2.271000000000e+00 1.976630704601e-04 -2.277000000000e+00 6.159217793450e-04 -2.283000000000e+00 1.160498424648e-03 -2.289000000000e+00 1.682159338746e-03 -2.295000000000e+00 2.053761384789e-03 -2.301000000000e+00 2.195392204941e-03 -2.307000000000e+00 2.086554798175e-03 -2.313000000000e+00 1.764151202025e-03 -2.319000000000e+00 1.308236813323e-03 -2.325000000000e+00 8.199582378989e-04 -2.331000000000e+00 3.973062382952e-04 -2.337000000000e+00 1.141789175617e-04 -2.343000000000e+00 6.935255316084e-06 -2.349000000000e+00 7.053974315914e-05 -2.355000000000e+00 2.640901841824e-04 -2.361000000000e+00 5.235095555347e-04 -2.367000000000e+00 7.778732542680e-04 -2.373000000000e+00 9.654423599251e-04 -2.379000000000e+00 1.045968482009e-03 -2.385000000000e+00 1.007020021568e-03 -2.391000000000e+00 8.636200583148e-04 -2.397000000000e+00 6.520092067897e-04 -2.403000000000e+00 4.195247264000e-04 -2.409000000000e+00 2.132036072721e-04 -2.415000000000e+00 6.970199758118e-05 -2.421000000000e+00 8.550700321100e-06 -2.427000000000e+00 2.982320015770e-05 -2.433000000000e+00 1.162243685951e-04 -2.439000000000e+00 2.386591440051e-04 -2.445000000000e+00 3.637017069182e-04 -2.451000000000e+00 4.611586800717e-04 -2.457000000000e+00 5.101058145100e-04 -2.463000000000e+00 5.022885633666e-04 -2.469000000000e+00 4.424653822577e-04 -2.475000000000e+00 3.459703015307e-04 -2.481000000000e+00 2.343280663708e-04 -2.487000000000e+00 1.300689588041e-04 -2.493000000000e+00 5.192362162136e-05 -2.499000000000e+00 1.135777882358e-05 -2.505000000000e+00 1.101053682869e-05 -2.511000000000e+00 4.513368845897e-05 -2.517000000000e+00 1.017005902531e-04 -2.523000000000e+00 1.655476467796e-04 -2.529000000000e+00 2.217779332109e-04 -2.535000000000e+00 2.587008757938e-04 -2.541000000000e+00 2.697715073803e-04 -2.547000000000e+00 2.542684838268e-04 -2.553000000000e+00 2.167423256019e-04 -2.559000000000e+00 1.655107291819e-04 -2.565000000000e+00 1.106314944511e-04 -2.571000000000e+00 6.182655684059e-05 -2.577000000000e+00 2.676991417401e-05 -2.583000000000e+00 1.001595747579e-05 -2.589000000000e+00 1.267280981732e-05 -2.595000000000e+00 3.275925673414e-05 -2.601000000000e+00 6.605762409946e-05 -2.607000000000e+00 1.072086379867e-04 -2.613000000000e+00 1.507919634046e-04 -2.619000000000e+00 1.921874292699e-04 -2.625000000000e+00 2.280966571349e-04 -2.631000000000e+00 2.566988301301e-04 -2.637000000000e+00 2.774956437834e-04 -2.643000000000e+00 2.909536771099e-04 -2.649000000000e+00 2.980710678708e-04 -2.655000000000e+00 2.999820248777e-04 -2.661000000000e+00 2.976765390560e-04 -2.667000000000e+00 2.918666321535e-04 -2.673000000000e+00 2.829873260216e-04 -2.679000000000e+00 2.712898625067e-04 -2.685000000000e+00 2.569716613577e-04 -2.691000000000e+00 2.402915438887e-04 -2.697000000000e+00 2.216356335912e-04 -2.703000000000e+00 2.015219971164e-04 -2.709000000000e+00 1.805534687884e-04 -2.715000000000e+00 1.593425928727e-04 -2.721000000000e+00 1.384373744965e-04 -2.727000000000e+00 1.182718747812e-04 -2.733000000000e+00 9.915427547722e-05 -2.739000000000e+00 8.129126336467e-05 -2.745000000000e+00 6.483566110525e-05 -2.751000000000e+00 4.993754168949e-05 -2.757000000000e+00 3.677905453287e-05 -2.763000000000e+00 2.557929860609e-05 -2.769000000000e+00 1.656562939583e-05 -2.775000000000e+00 9.918628881197e-06 -2.781000000000e+00 5.706496022232e-06 -2.787000000000e+00 3.828543729780e-06 -2.793000000000e+00 3.985891072049e-06 -2.799000000000e+00 5.690916157634e-06 -2.805000000000e+00 8.317433018578e-06 -2.811000000000e+00 1.118342383429e-05 -2.817000000000e+00 1.365006210690e-05 -2.823000000000e+00 1.521664385075e-05 -2.829000000000e+00 1.559177206943e-05 -2.835000000000e+00 1.472638466250e-05 -2.841000000000e+00 1.280257958021e-05 -2.847000000000e+00 1.018162734844e-05 -2.853000000000e+00 7.322887836363e-06 -2.859000000000e+00 4.690766528013e-06 -2.865000000000e+00 2.668333036880e-06 -2.871000000000e+00 1.493702548128e-06 -2.877000000000e+00 1.229572266712e-06 -2.883000000000e+00 1.768868670416e-06 -2.889000000000e+00 2.872019234063e-06 -2.895000000000e+00 4.225492516267e-06 -2.901000000000e+00 5.508049212697e-06 -2.907000000000e+00 6.451033954906e-06 -2.913000000000e+00 6.881726894484e-06 -2.919000000000e+00 6.743407519357e-06 -2.925000000000e+00 6.091176476915e-06 -2.931000000000e+00 5.067517565017e-06 -2.937000000000e+00 3.865082057340e-06 -2.943000000000e+00 2.685684836981e-06 -2.949000000000e+00 1.703957215000e-06 -2.955000000000e+00 1.041892583538e-06 -2.961000000000e+00 7.573494174399e-07 -2.967000000000e+00 8.462531718732e-07 -2.973000000000e+00 1.255500079232e-06 -2.979000000000e+00 1.901914988380e-06 -2.985000000000e+00 2.692240646279e-06 -2.991000000000e+00 3.539915599577e-06 -2.997000000000e+00 4.375966275944e-06 diff --git a/Tests/Unit/gui2/data/bilayer2.txt b/Tests/Unit/gui2/data/bilayer2.txt deleted file mode 100644 index 3cea18c7c9c..00000000000 --- a/Tests/Unit/gui2/data/bilayer2.txt +++ /dev/null @@ -1,503 +0,0 @@ -# BornAgain Intensity Data -# Simple array suitable for numpy, matlab etc. -# coordinates intensities -3.000000000000e-03 1.000000000000e+08 -9.000000000000e-03 1.000000000000e+08 -1.500000000000e-02 1.000000000000e+08 -2.100000000000e-02 1.000000000000e+08 -2.700000000000e-02 1.000000000000e+08 -3.300000000000e-02 1.000000000000e+08 -3.900000000000e-02 1.000000000000e+08 -4.500000000000e-02 1.000000000000e+08 -5.100000000000e-02 1.000000000000e+08 -5.700000000000e-02 1.000000000000e+08 -6.300000000000e-02 1.000000000000e+08 -6.900000000000e-02 1.000000000000e+08 -7.500000000000e-02 1.000000000000e+08 -8.100000000000e-02 1.000000000000e+08 -8.700000000000e-02 1.000000000000e+08 -9.300000000000e-02 1.000000000000e+08 -9.900000000000e-02 1.000000000000e+08 -1.050000000000e-01 1.000000000000e+08 -1.110000000000e-01 1.000000000000e+08 -1.170000000000e-01 1.000000000000e+08 -1.230000000000e-01 1.000000000000e+08 -1.290000000000e-01 1.000000000000e+08 -1.350000000000e-01 1.000000000000e+08 -1.410000000000e-01 1.000000000000e+08 -1.470000000000e-01 1.000000000000e+08 -1.530000000000e-01 1.000000000000e+08 -1.590000000000e-01 1.000000000000e+08 -1.650000000000e-01 1.000000000000e+08 -1.710000000000e-01 1.000000000000e+08 -1.770000000000e-01 1.000000000000e+08 -1.830000000000e-01 1.000000000000e+08 -1.890000000000e-01 1.000000000000e+08 -1.950000000000e-01 1.000000000000e+08 -2.010000000000e-01 1.000000000000e+08 -2.070000000000e-01 1.000000000000e+08 -2.130000000000e-01 1.000000000000e+08 -2.190000000000e-01 1.000000000000e+08 -2.250000000000e-01 1.000000000000e+08 -2.310000000000e-01 1.000000000000e+08 -2.370000000000e-01 1.000000000000e+08 -2.430000000000e-01 1.000000000000e+08 -2.490000000000e-01 1.000000000000e+08 -2.550000000000e-01 1.000000000000e+08 -2.610000000000e-01 1.000000000000e+08 -2.670000000000e-01 1.000000000000e+08 -2.730000000000e-01 1.000000000000e+08 -2.790000000000e-01 1.000000000000e+08 -2.850000000000e-01 1.000000000000e+08 -2.910000000000e-01 1.000000000000e+08 -2.970000000000e-01 1.000000000000e+08 -3.030000000000e-01 1.000000000000e+08 -3.090000000000e-01 1.000000000000e+08 -3.150000000000e-01 4.958890595531e+07 -3.210000000000e-01 2.784755638488e+07 -3.270000000000e-01 2.517579061861e+07 -3.330000000000e-01 2.253765402056e+07 -3.390000000000e-01 1.835954817644e+07 -3.450000000000e-01 1.301817054916e+07 -3.510000000000e-01 7.531087265117e+06 -3.570000000000e-01 3.093202985447e+06 -3.630000000000e-01 5.620776609678e+05 -3.690000000000e-01 7.596761289755e+04 -3.750000000000e-01 1.112265320103e+06 -3.810000000000e-01 2.884476724466e+06 -3.870000000000e-01 4.714730894570e+06 -3.930000000000e-01 6.191043235474e+06 -3.990000000000e-01 7.154732942568e+06 -4.050000000000e-01 7.622556290697e+06 -4.110000000000e-01 7.705227830434e+06 -4.170000000000e-01 7.542030261838e+06 -4.230000000000e-01 7.254861890767e+06 -4.290000000000e-01 6.922529533066e+06 -4.350000000000e-01 6.575040691861e+06 -4.410000000000e-01 6.204198400524e+06 -4.470000000000e-01 5.783116414212e+06 -4.530000000000e-01 5.286318021468e+06 -4.590000000000e-01 4.704240550861e+06 -4.650000000000e-01 4.049431386988e+06 -4.710000000000e-01 3.354683709148e+06 -4.770000000000e-01 2.665204104379e+06 -4.830000000000e-01 2.027837099839e+06 -4.890000000000e-01 1.480630274508e+06 -4.950000000000e-01 1.045577831755e+06 -5.010000000000e-01 7.261925554482e+05 -5.070000000000e-01 5.099194483346e+05 -5.130000000000e-01 3.739299274257e+05 -5.190000000000e-01 2.920843125611e+05 -5.250000000000e-01 2.409834539126e+05 -5.310000000000e-01 2.037837787609e+05 -5.370000000000e-01 1.713874205511e+05 -5.430000000000e-01 1.413951843072e+05 -5.490000000000e-01 1.156699421907e+05 -5.550000000000e-01 9.749347302431e+04 -5.610000000000e-01 8.917182522366e+04 -5.670000000000e-01 9.063966365952e+04 -5.730000000000e-01 9.923352555011e+04 -5.790000000000e-01 1.104525516332e+05 -5.850000000000e-01 1.192894671663e+05 -5.910000000000e-01 1.216367404595e+05 -5.970000000000e-01 1.153425224947e+05 -6.030000000000e-01 1.006585222378e+05 -6.090000000000e-01 8.002378077552e+04 -6.150000000000e-01 5.730850706804e+04 -6.210000000000e-01 3.676325246995e+04 -6.270000000000e-01 2.196327853873e+04 -6.330000000000e-01 1.500677624787e+04 -6.390000000000e-01 1.613522522934e+04 -6.450000000000e-01 2.382321562681e+04 -6.510000000000e-01 3.526706099044e+04 -6.570000000000e-01 4.711579322237e+04 -6.630000000000e-01 5.625158504106e+04 -6.690000000000e-01 6.044059969400e+04 -6.750000000000e-01 5.872787890710e+04 -6.810000000000e-01 5.152230450258e+04 -6.870000000000e-01 4.039056862521e+04 -6.930000000000e-01 2.763713254338e+04 -6.990000000000e-01 1.578101382033e+04 -7.050000000000e-01 7.046816484403e+03 -7.110000000000e-01 2.969209888992e+03 -7.170000000000e-01 4.174050946452e+03 -7.230000000000e-01 1.035531971996e+04 -7.290000000000e-01 2.042536370361e+04 -7.350000000000e-01 3.278516999038e+04 -7.410000000000e-01 4.564639598747e+04 -7.470000000000e-01 5.733822466293e+04 -7.530000000000e-01 6.654654750581e+04 -7.590000000000e-01 7.245503774571e+04 -7.650000000000e-01 7.478137974017e+04 -7.710000000000e-01 7.372223636517e+04 -7.770000000000e-01 6.983421928668e+04 -7.830000000000e-01 6.388394378877e+04 -7.890000000000e-01 5.669874737633e+04 -7.950000000000e-01 4.904265636952e+04 -8.010000000000e-01 4.153219039444e+04 -8.070000000000e-01 3.459615295473e+04 -8.130000000000e-01 2.847477032061e+04 -8.190000000000e-01 2.324773395238e+04 -8.250000000000e-01 1.887830395501e+04 -8.310000000000e-01 1.526127910356e+04 -8.370000000000e-01 1.226542902853e+04 -8.430000000000e-01 9.764792661720e+03 -8.490000000000e-01 7.657028121727e+03 -8.550000000000e-01 5.869993524575e+03 -8.610000000000e-01 4.359564872105e+03 -8.670000000000e-01 3.102335965919e+03 -8.730000000000e-01 2.086540868269e+03 -8.790000000000e-01 1.303669926269e+03 -8.850000000000e-01 7.422004345400e+02 -8.910000000000e-01 3.839302915564e+02 -8.970000000000e-01 2.027522737366e+02 -9.030000000000e-01 1.653705147830e+02 -9.090000000000e-01 2.333753702193e+02 -9.150000000000e-01 3.661487659873e+02 -9.210000000000e-01 5.241643486942e+02 -9.270000000000e-01 6.723084634234e+02 -9.330000000000e-01 7.828627569469e+02 -9.390000000000e-01 8.377827322213e+02 -9.450000000000e-01 8.299249966261e+02 -9.510000000000e-01 7.629603889423e+02 -9.570000000000e-01 6.498766663589e+02 -9.630000000000e-01 5.102051019027e+02 -9.690000000000e-01 3.663525835953e+02 -9.750000000000e-01 2.396219649873e+02 -9.810000000000e-01 1.466005106524e+02 -9.870000000000e-01 9.655435128484e+01 -9.930000000000e-01 9.028543855367e+01 -9.990000000000e-01 1.206220271569e+02 -1.005000000000e+00 1.743869420735e+02 -1.011000000000e+00 2.353938083208e+02 -1.017000000000e+00 2.878265298207e+02 -1.023000000000e+00 3.193041905730e+02 -1.029000000000e+00 3.230309801777e+02 -1.035000000000e+00 2.986524924219e+02 -1.041000000000e+00 2.517320285265e+02 -1.047000000000e+00 1.920557560453e+02 -1.053000000000e+00 1.312086593532e+02 -1.059000000000e+00 7.998649722308e+01 -1.065000000000e+00 4.620213445470e+01 -1.071000000000e+00 3.331765520827e+01 -1.077000000000e+00 4.012274494702e+01 -1.083000000000e+00 6.143729151065e+01 -1.089000000000e+00 8.959759435274e+01 -1.095000000000e+00 1.163370697862e+02 -1.101000000000e+00 1.346208561086e+02 -1.107000000000e+00 1.400425525519e+02 -1.113000000000e+00 1.315212904312e+02 -1.119000000000e+00 1.112129290055e+02 -1.125000000000e+00 8.372786109627e+01 -1.131000000000e+00 5.489021548002e+01 -1.137000000000e+00 3.035121133693e+01 -1.143000000000e+00 1.437182554028e+01 -1.149000000000e+00 9.023237055111e+00 -1.155000000000e+00 1.393881106225e+01 -1.161000000000e+00 2.661838159232e+01 -1.167000000000e+00 4.316521163959e+01 -1.173000000000e+00 5.925344444584e+01 -1.179000000000e+00 7.109354278784e+01 -1.185000000000e+00 7.618667342628e+01 -1.191000000000e+00 7.372577879758e+01 -1.197000000000e+00 6.459224193958e+01 -1.203000000000e+00 5.099017784249e+01 -1.209000000000e+00 3.583487794922e+01 -1.215000000000e+00 2.205337210759e+01 -1.221000000000e+00 1.195775833795e+01 -1.227000000000e+00 6.819283272664e+00 -1.233000000000e+00 6.713948574170e+00 -1.239000000000e+00 1.064375721483e+01 -1.245000000000e+00 1.687719410310e+01 -1.251000000000e+00 2.341077694478e+01 -1.257000000000e+00 2.843768904314e+01 -1.263000000000e+00 3.072033525643e+01 -1.269000000000e+00 2.979593096971e+01 -1.275000000000e+00 2.598856773307e+01 -1.281000000000e+00 2.024656914900e+01 -1.287000000000e+00 1.386027651623e+01 -1.293000000000e+00 8.135672810081e+00 -1.299000000000e+00 4.100737246278e+00 -1.305000000000e+00 2.305819778018e+00 -1.311000000000e+00 2.751913587597e+00 -1.317000000000e+00 4.948794995786e+00 -1.323000000000e+00 8.076108995008e+00 -1.329000000000e+00 1.120064534602e+01 -1.335000000000e+00 1.349566848540e+01 -1.341000000000e+00 1.441351829629e+01 -1.347000000000e+00 1.377823514593e+01 -1.353000000000e+00 1.178616064457e+01 -1.359000000000e+00 8.924045541106e+00 -1.365000000000e+00 5.831364845832e+00 -1.371000000000e+00 3.142994402905e+00 -1.377000000000e+00 1.348895583756e+00 -1.383000000000e+00 6.998459880899e-01 -1.389000000000e+00 1.175093333321e+00 -1.395000000000e+00 2.512589765792e+00 -1.401000000000e+00 4.288748386655e+00 -1.407000000000e+00 6.025294940502e+00 -1.413000000000e+00 7.297338962210e+00 -1.419000000000e+00 7.819358337474e+00 -1.425000000000e+00 7.493143328432e+00 -1.431000000000e+00 6.411731286965e+00 -1.437000000000e+00 4.823494694988e+00 -1.443000000000e+00 3.068585528242e+00 -1.449000000000e+00 1.504367250740e+00 -1.455000000000e+00 4.367336631615e-01 -1.461000000000e+00 7.075769309964e-02 -1.467000000000e+00 4.881458854136e-01 -1.473000000000e+00 1.652127168882e+00 -1.479000000000e+00 3.434315408612e+00 -1.485000000000e+00 5.654030938598e+00 -1.491000000000e+00 8.119215368506e+00 -1.497000000000e+00 1.065940397251e+01 -1.503000000000e+00 1.314459866367e+01 -1.509000000000e+00 1.548828027174e+01 -1.515000000000e+00 1.763705888455e+01 -1.521000000000e+00 1.955258839831e+01 -1.527000000000e+00 2.119273783619e+01 -1.533000000000e+00 2.249846541021e+01 -1.539000000000e+00 2.339069454111e+01 -1.545000000000e+00 2.377840550018e+01 -1.551000000000e+00 2.357595747516e+01 -1.557000000000e+00 2.272512813000e+01 -1.563000000000e+00 2.121606764169e+01 -1.569000000000e+00 1.910154801334e+01 -1.575000000000e+00 1.650042453406e+01 -1.581000000000e+00 1.358869008565e+01 -1.587000000000e+00 1.057928589037e+01 -1.593000000000e+00 7.694291412396e+00 -1.599000000000e+00 5.134720118508e+00 -1.605000000000e+00 3.053586303063e+00 -1.611000000000e+00 1.537144007736e+00 -1.617000000000e+00 5.974567748544e-01 -1.623000000000e+00 1.771553923452e-01 -1.629000000000e+00 1.648890760558e-01 -1.635000000000e+00 4.180581609915e-01 -1.641000000000e+00 7.882927318396e-01 -1.647000000000e+00 1.144980471082e+00 -1.653000000000e+00 1.392906645043e+00 -1.659000000000e+00 1.481523555665e+00 -1.665000000000e+00 1.405168023092e+00 -1.671000000000e+00 1.195305998186e+00 -1.677000000000e+00 9.072591835504e-01 -1.683000000000e+00 6.046253871346e-01 -1.689000000000e+00 3.446553506005e-01 -1.695000000000e+00 1.672577795520e-01 -1.701000000000e+00 8.925776869819e-02 -1.707000000000e+00 1.042905171126e-01 -1.713000000000e+00 1.875429506827e-01 -1.719000000000e+00 3.036869169436e-01 -1.725000000000e+00 4.159193631440e-01 -1.731000000000e+00 4.940724007352e-01 -1.737000000000e+00 5.202134935792e-01 -1.743000000000e+00 4.908810247850e-01 -1.749000000000e+00 4.159125148752e-01 -1.755000000000e+00 3.145440937279e-01 -1.761000000000e+00 2.099520805494e-01 -1.767000000000e+00 1.235957163762e-01 -1.773000000000e+00 7.060092165109e-02 -1.779000000000e+00 5.705928145405e-02 -1.785000000000e+00 7.960933542981e-02 -1.791000000000e+00 1.271410070963e-01 -1.797000000000e+00 1.840308640625e-01 -1.803000000000e+00 2.340557749013e-01 -1.809000000000e+00 2.640801250317e-01 -1.815000000000e+00 2.667545434913e-01 -1.821000000000e+00 2.417503574088e-01 -1.827000000000e+00 1.954081002475e-01 -1.833000000000e+00 1.390192959347e-01 -1.839000000000e+00 8.621940662359e-02 -1.845000000000e+00 5.010221903224e-02 -1.851000000000e+00 4.065919139901e-02 -1.857000000000e+00 6.301872034333e-02 -1.863000000000e+00 1.167502789937e-01 -1.869000000000e+00 1.962592343935e-01 -1.875000000000e+00 2.920818843664e-01 -1.881000000000e+00 3.927378676425e-01 -1.887000000000e+00 4.867314815774e-01 -1.893000000000e+00 5.643168721279e-01 -1.899000000000e+00 6.187383305107e-01 -1.905000000000e+00 6.467970298948e-01 -1.911000000000e+00 6.487452910920e-01 -1.917000000000e+00 6.276372925710e-01 -1.923000000000e+00 5.883482497423e-01 -1.929000000000e+00 5.365020642846e-01 -1.935000000000e+00 4.775227431748e-01 -1.941000000000e+00 4.159605859620e-01 -1.947000000000e+00 3.551597194136e-01 -1.953000000000e+00 2.972499039936e-01 -1.959000000000e+00 2.433803088561e-01 -1.965000000000e+00 1.940772489846e-01 -1.971000000000e+00 1.496047749216e-01 -1.977000000000e+00 1.102320012183e-01 -1.983000000000e+00 7.635410907742e-02 -1.989000000000e+00 4.846244473319e-02 -1.995000000000e+00 2.700100658818e-02 -2.001000000000e+00 1.217298913640e-02 -2.007000000000e+00 3.767789515923e-03 -2.013000000000e+00 1.066900266715e-03 -2.019000000000e+00 2.861541201053e-03 -2.025000000000e+00 7.583390615651e-03 -2.031000000000e+00 1.352052059377e-02 -2.037000000000e+00 1.907036569855e-02 -2.043000000000e+00 2.297421495520e-02 -2.049000000000e+00 2.448389283579e-02 -2.055000000000e+00 2.342841857659e-02 -2.061000000000e+00 2.017169793699e-02 -2.067000000000e+00 1.547596846420e-02 -2.073000000000e+00 1.030449311105e-02 -2.079000000000e+00 5.607155649231e-03 -2.085000000000e+00 2.132674238372e-03 -2.091000000000e+00 3.019801976602e-04 -2.097000000000e+00 1.616956758697e-04 -2.103000000000e+00 1.418502114524e-03 -2.109000000000e+00 3.538603775072e-03 -2.115000000000e+00 5.884809394932e-03 -2.121000000000e+00 7.858955177213e-03 -2.127000000000e+00 9.019788418139e-03 -2.133000000000e+00 9.154780831002e-03 -2.139000000000e+00 8.296271121975e-03 -2.145000000000e+00 6.684967256126e-03 -2.151000000000e+00 4.694447763655e-03 -2.157000000000e+00 2.736884282411e-03 -2.163000000000e+00 1.171826084409e-03 -2.169000000000e+00 2.367517059893e-04 -2.175000000000e+00 1.139193930034e-05 -2.181000000000e+00 4.193704292770e-04 -2.187000000000e+00 1.262469754599e-03 -2.193000000000e+00 2.276520648688e-03 -2.199000000000e+00 3.194639249207e-03 -2.205000000000e+00 3.803644880361e-03 -2.211000000000e+00 3.982569533556e-03 -2.217000000000e+00 3.717230102832e-03 -2.223000000000e+00 3.090580630306e-03 -2.229000000000e+00 2.253702785106e-03 -2.235000000000e+00 1.385813290848e-03 -2.241000000000e+00 6.530109152494e-04 -2.247000000000e+00 1.746209440219e-04 -2.253000000000e+00 3.360201461992e-06 -2.259000000000e+00 1.219025129350e-04 -2.265000000000e+00 4.546597563975e-04 -2.271000000000e+00 8.905168869420e-04 -2.277000000000e+00 1.310440675264e-03 -2.283000000000e+00 1.613559299604e-03 -2.289000000000e+00 1.736377377024e-03 -2.295000000000e+00 1.661861202881e-03 -2.301000000000e+00 1.417647692374e-03 -2.307000000000e+00 1.065013809496e-03 -2.313000000000e+00 6.820036477390e-04 -2.319000000000e+00 3.449491152318e-04 -2.325000000000e+00 1.124680809258e-04 -2.331000000000e+00 1.502862694771e-05 -2.337000000000e+00 5.163817603197e-05 -2.343000000000e+00 1.935364030187e-04 -2.349000000000e+00 3.933122344677e-04 -2.355000000000e+00 5.969086593920e-04 -2.361000000000e+00 7.556666785597e-04 -2.367000000000e+00 8.358813484445e-04 -2.373000000000e+00 8.241564588184e-04 -2.379000000000e+00 7.279213172140e-04 -2.385000000000e+00 5.715568681989e-04 -2.391000000000e+00 3.894414309671e-04 -2.397000000000e+00 2.177146125702e-04 -2.403000000000e+00 8.661417390671e-05 -2.409000000000e+00 1.490463702359e-05 -2.415000000000e+00 7.305118674737e-06 -2.421000000000e+00 5.509736807330e-05 -2.427000000000e+00 1.394179186380e-04 -2.433000000000e+00 2.362449192157e-04 -2.439000000000e+00 3.218602609299e-04 -2.445000000000e+00 3.776163265800e-04 -2.451000000000e+00 3.931197326304e-04 -2.457000000000e+00 3.673745776948e-04 -2.463000000000e+00 3.078982849778e-04 -2.469000000000e+00 2.282326015952e-04 -2.475000000000e+00 1.445446334305e-04 -2.481000000000e+00 7.210981154150e-05 -2.487000000000e+00 2.239315194273e-05 -2.493000000000e+00 1.234784775662e-06 -2.499000000000e+00 8.360817698301e-06 -2.505000000000e+00 3.814866998514e-05 -2.511000000000e+00 8.133695070642e-05 -2.517000000000e+00 1.272247553183e-04 -2.523000000000e+00 1.658702421136e-04 -2.529000000000e+00 1.898652203823e-04 -2.535000000000e+00 1.954035135407e-04 -2.541000000000e+00 1.825372769054e-04 -2.547000000000e+00 1.546869986615e-04 -2.553000000000e+00 1.176042241351e-04 -2.559000000000e+00 7.806046552757e-05 -2.565000000000e+00 4.254562597362e-05 -2.571000000000e+00 1.621275729407e-05 -2.577000000000e+00 2.221293842023e-06 -2.583000000000e+00 1.530988680077e-06 -2.589000000000e+00 1.310583838258e-05 -2.595000000000e+00 3.441838166785e-05 -2.601000000000e+00 6.210920351821e-05 -2.607000000000e+00 9.265522251350e-05 -2.613000000000e+00 1.229272527303e-04 -2.619000000000e+00 1.505617467419e-04 -2.625000000000e+00 1.741213350763e-04 -2.631000000000e+00 1.930629125915e-04 -2.637000000000e+00 2.075630268629e-04 -2.643000000000e+00 2.182649059085e-04 -2.649000000000e+00 2.260104039522e-04 -2.655000000000e+00 2.316073924701e-04 -2.661000000000e+00 2.356639870542e-04 -2.667000000000e+00 2.385010852846e-04 -2.673000000000e+00 2.401382146212e-04 -2.679000000000e+00 2.403369651742e-04 -2.685000000000e+00 2.386818996529e-04 -2.691000000000e+00 2.346794013261e-04 -2.697000000000e+00 2.278586272243e-04 -2.703000000000e+00 2.178632824795e-04 -2.709000000000e+00 2.045268203015e-04 -2.715000000000e+00 1.879261321525e-04 -2.721000000000e+00 1.684099247637e-04 -2.727000000000e+00 1.465986207851e-04 -2.733000000000e+00 1.233535764236e-04 -2.739000000000e+00 9.971558307121e-05 -2.745000000000e+00 7.681606241377e-05 -2.751000000000e+00 5.576874730310e-05 -2.757000000000e+00 3.755398689848e-05 -2.763000000000e+00 2.291091753997e-05 -2.769000000000e+00 1.225356221786e-05 -2.775000000000e+00 5.624805801831e-06 -2.781000000000e+00 2.697190509113e-06 -2.787000000000e+00 2.822330861223e-06 -2.793000000000e+00 5.122752451708e-06 -2.799000000000e+00 8.612693312614e-06 -2.805000000000e+00 1.232935355556e-05 -2.811000000000e+00 1.545429531663e-05 -2.817000000000e+00 1.740657260306e-05 -2.823000000000e+00 1.789434993200e-05 -2.829000000000e+00 1.691921423289e-05 -2.835000000000e+00 1.473563710489e-05 -2.841000000000e+00 1.177550866037e-05 -2.847000000000e+00 8.552970414223e-06 -2.853000000000e+00 5.567031106867e-06 -2.859000000000e+00 3.218409668336e-06 -2.865000000000e+00 1.753127178635e-06 -2.871000000000e+00 1.239507355051e-06 -2.877000000000e+00 1.578699030273e-06 -2.883000000000e+00 2.542891762494e-06 -2.889000000000e+00 3.831120143862e-06 -2.895000000000e+00 5.130588382763e-06 -2.901000000000e+00 6.171927026062e-06 -2.907000000000e+00 6.769363089567e-06 -2.913000000000e+00 6.840716708380e-06 -2.919000000000e+00 6.406518398978e-06 -2.925000000000e+00 5.571473971575e-06 -2.931000000000e+00 4.494286077422e-06 -2.937000000000e+00 3.353085009636e-06 -2.943000000000e+00 2.313394233683e-06 -2.949000000000e+00 1.503945833406e-06 -2.955000000000e+00 1.003273351698e-06 -2.961000000000e+00 8.374415121770e-07 -2.967000000000e+00 9.870688966875e-07 -2.973000000000e+00 1.400353291584e-06 -2.979000000000e+00 2.008295327407e-06 -2.985000000000e+00 2.738684393483e-06 -2.991000000000e+00 3.526432769323e-06 -2.997000000000e+00 4.319182015173e-06 diff --git a/Tests/Unit/gui2/data/bilayer3a.txt b/Tests/Unit/gui2/data/bilayer3a.txt deleted file mode 100644 index ef62fc7c621..00000000000 --- a/Tests/Unit/gui2/data/bilayer3a.txt +++ /dev/null @@ -1,501 +0,0 @@ -# data from TimeOfFlightReflectometry.py -0, 0.01, 0.9999999999999998 -1, 0.011983967935871743, 1.0 -2, 0.013967935871743487, 1.0000000000000004 -3, 0.01595190380761523, 1.0000000000000002 -4, 0.017935871743486972, 0.9999999999999999 -5, 0.019919839679358717, 1.0 -6, 0.02190380761523046, 1.0000000000000004 -7, 0.023887775551102206, 1.0000000000000002 -8, 0.02587174348697395, 1.0000000000000004 -9, 0.027855711422845694, 1.0 -10, 0.02983967935871744, 1.0 -11, 0.031823647294589176, 1.0000000000000002 -12, 0.03380761523046092, 1.0000000000000004 -13, 0.035791583166332665, 0.9999999999999996 -14, 0.03777555110220441, 0.9999999999999998 -15, 0.03975951903807615, 0.9999999999999998 -16, 0.0417434869739479, 1.0 -17, 0.04372745490981964, 1.0 -18, 0.045711422845691387, 1.0 -19, 0.04769539078156313, 1.0 -20, 0.049679358717434875, 1.0 -21, 0.05166332665330661, 1.0 -22, 0.05364729458917836, 1.0000000000000002 -23, 0.0556312625250501, 1.0000000000000007 -24, 0.057615230460921846, 0.9999999999999998 -25, 0.05959919839679359, 1.0 -26, 0.061583166332665334, 0.9999999999999998 -27, 0.06356713426853708, 0.9999999999999996 -28, 0.06555110220440881, 0.9999999999999998 -29, 0.06753507014028055, 1.0 -30, 0.0695190380761523, 0.9999999999999999 -31, 0.07150300601202404, 0.9999999999999996 -32, 0.07348697394789579, 0.9999999999999997 -33, 0.07547094188376753, 0.9999999999999997 -34, 0.07745490981963928, 0.9999999999999996 -35, 0.07943887775551102, 0.9999999999999996 -36, 0.08142284569138276, 0.9999999999999998 -37, 0.08340681362725451, 1.0000000000000004 -38, 0.08539078156312625, 0.9999999999999993 -39, 0.087374749498998, 0.9999999999999996 -40, 0.08935871743486974, 1.0000000000000004 -41, 0.09134268537074147, 1.0000000000000004 -42, 0.09332665330661322, 1.0000000000000009 -43, 0.09531062124248496, 0.9999999999999998 -44, 0.0972945891783567, 0.9999999999999999 -45, 0.09927855711422845, 0.9999999999999993 -46, 0.1012625250501002, 1.0000000000000002 -47, 0.10324649298597194, 0.9999992528061048 -48, 0.10523046092184368, 0.9999985974506073 -49, 0.10721442885771543, 0.999997921309658 -50, 0.10919839679358717, 0.9999971427452203 -51, 0.11118236472945892, 0.9999962122090085 -52, 0.11316633266533066, 0.9999950799230959 -53, 0.1151503006012024, 0.9999936875180906 -54, 0.11713426853707415, 0.9999919626983287 -55, 0.1191182364729459, 0.9999898137850864 -56, 0.12110220440881762, 0.9999871230233972 -57, 0.12308617234468937, 0.9999837378321051 -58, 0.12507014028056113, 0.9999794590650891 -59, 0.12705410821643287, 0.9999740250410741 -60, 0.12903807615230461, 0.9999670895946517 -61, 0.13102204408817636, 0.999958191635177 -62, 0.1330060120240481, 0.999946712547489 -63, 0.13498997995991985, 0.9999318160224282 -64, 0.1369739478957916, 0.9999123622332293 -65, 0.13895791583166334, 0.9998867841333866 -66, 0.14094188376753508, 0.999852907147281 -67, 0.14292585170340683, 0.9998076831535236 -68, 0.14490981963927857, 0.9997467928547588 -69, 0.14689378757515031, 0.99966404291278 -70, 0.14887775551102206, 0.9995504376444865 -71, 0.1508617234468938, 0.9993927251319745 -72, 0.15284569138276555, 0.999171077251899 -73, 0.1548296593186373, 0.9988553104956281 -74, 0.15681362725450904, 0.9983985870085685 -75, 0.15879759519038078, 0.9977266436988359 -76, 0.16078156312625252, 0.9967188386688286 -77, 0.16276553106212427, 0.9951737046113363 -78, 0.164749498997996, 0.9927440254766213 -79, 0.16673346693386776, 0.9888093621532706 -80, 0.1687174348697395, 0.9822141139510879 -81, 0.17070140280561122, 0.9707022984498375 -82, 0.17268537074148296, 0.9496383601680007 -83, 0.1746693386773547, 0.909021525956259 -84, 0.17665330661322645, 0.8268006578284636 -85, 0.1786372745490982, 0.6588522111821937 -86, 0.18062124248496994, 0.3622399614191024 -87, 0.18260521042084168, 0.06420150811805793 -88, 0.18458917835671343, 0.024633662977522494 -89, 0.18657314629258517, 0.15244093776327813 -90, 0.18855711422845692, 0.27529995099868965 -91, 0.19054108216432866, 0.3526612082641258 -92, 0.1925250501002004, 0.39063545710665876 -93, 0.19450901803607215, 0.3990333790987325 -94, 0.1964929859719439, 0.3845452071189573 -95, 0.19847695390781564, 0.35144925997264576 -96, 0.20046092184368738, 0.3031439950053475 -97, 0.20244488977955913, 0.24353655832687798 -98, 0.20442885771543087, 0.17808572440435927 -99, 0.20641282565130262, 0.11410584881256816 -100, 0.20839679358717436, 0.059830552998912524 -101, 0.2103807615230461, 0.022250824859572964 -102, 0.21236472945891785, 0.004789833661897041 -103, 0.2143486973947896, 0.006356984649312908 -104, 0.21633266533066134, 0.022365632593830034 -105, 0.21831663326653308, 0.04682241052607952 -106, 0.22030060120240483, 0.07416136769212864 -107, 0.22228456913827657, 0.1001720615138792 -108, 0.22426853707414832, 0.12213465250467488 -109, 0.22625250501002006, 0.13855308036162264 -110, 0.2282364729458918, 0.1487974036569265 -111, 0.23022044088176355, 0.15280542074664977 -112, 0.23220440881763527, 0.15088251751204407 -113, 0.234188376753507, 0.14358811351822667 -114, 0.23617234468937875, 0.13168199675274497 -115, 0.2381563126252505, 0.11610304292527356 -116, 0.24014028056112224, 0.09795584761494282 -117, 0.242124248496994, 0.07848468865313928 -118, 0.24410821643286573, 0.05901971090221587 -119, 0.24609218436873748, 0.04088892407033809 -120, 0.24807615230460922, 0.02530184438722244 -121, 0.25006012024048097, 0.013223849390327617 -122, 0.2520440881763527, 0.0052693211843496445 -123, 0.25402805611222445, 0.0016411405846048022 -124, 0.2560120240480962, 0.002132606619548132 -125, 0.25799599198396794, 0.006189794194701373 -126, 0.2599799599198397, 0.013015844928207822 -127, 0.26196392785571143, 0.021690550068973954 -128, 0.2639478957915832, 0.03128056624196508 -129, 0.2659318637274549, 0.04092443009513365 -130, 0.26791583166332666, 0.049887002117678506 -131, 0.2698997995991984, 0.05758607973651505 -132, 0.27188376753507015, 0.06359816326314606 -133, 0.2738677354709419, 0.06765120572513812 -134, 0.27585170340681364, 0.06961093772604753 -135, 0.2778356713426854, 0.0694652862160818 -136, 0.27981963927855713, 0.06730932686430591 -137, 0.2818036072144289, 0.06333151898991729 -138, 0.2837875751503006, 0.05780078596127256 -139, 0.28577154308617236, 0.051053313090844835 -140, 0.2877555110220441, 0.04347770115747981 -141, 0.28973947895791585, 0.035497304971156975 -142, 0.2917234468937876, 0.027549165730337055 -143, 0.29370741482965934, 0.02005983448493074 -144, 0.2956913827655311, 0.013419426316096529 -145, 0.29767535070140283, 0.007956202507095663 -146, 0.2996593186372746, 0.0039145718050655805 -147, 0.3016432865731463, 0.0014393995905691696 -148, 0.30362725450901806, 0.0005688307236762597 -149, 0.3056112224448898, 0.0012365918074232387 -150, 0.30759519038076155, 0.0032832519146904853 -151, 0.3095791583166333, 0.006474578249753218 -152, 0.31156312625250504, 0.010524253267492914 -153, 0.3135470941883768, 0.01511797850115291 -154, 0.31553106212424853, 0.01993633203535728 -155, 0.3175150300601203, 0.024674476171810362 -156, 0.319498997995992, 0.02905767996968943 -157, 0.32148296593186376, 0.03285241227312436 -158, 0.3234669338677355, 0.03587334202207215 -159, 0.32545090180360725, 0.03798691116784891 -160, 0.327434869739479, 0.03911224589336394 -161, 0.3294188376753507, 0.039220104446289754 -162, 0.33140280561122243, 0.03833039410542655 -163, 0.3333867735470942, 0.036508587609869456 -164, 0.3353707414829659, 0.0338611788968911 -165, 0.33735470941883766, 0.03053017320088866 -166, 0.3393386773547094, 0.02668652941152426 -167, 0.34132264529058115, 0.02252247460745127 -168, 0.3433066132264529, 0.01824269289141468 -169, 0.34529058116232464, 0.01405454201275371 -170, 0.3472745490981964, 0.010157647151281007 -171, 0.34925851703406813, 0.006733424032981525 -172, 0.35124248496993987, 0.003935247174387443 -173, 0.3532264529058116, 0.0018800579891315712 -174, 0.35521042084168336, 0.0006421686942934479 -175, 0.3571943887775551, 0.0002498520126686543 -176, 0.35917835671342685, 0.0006850336571871474 -177, 0.3611623246492986, 0.0018860713465820211 -178, 0.36314629258517034, 0.0037532727931319895 -179, 0.3651302605210421, 0.006156536579232831 -180, 0.3671142284569138, 0.008944337730497173 -181, 0.36909819639278557, 0.011953240771592386 -182, 0.3710821643286573, 0.015017195992625963 -183, 0.37306613226452906, 0.017976027723366806 -184, 0.3750501002004008, 0.020682715399392357 -185, 0.37703406813627255, 0.02300925974043486 -186, 0.3790180360721443, 0.024851087812409446 -187, 0.38100200400801604, 0.026130065575115363 -188, 0.3829859719438878, 0.026796250719654913 -189, 0.3849699398797595, 0.02682853773180647 -190, 0.38695390781563127, 0.026234332576648815 -191, 0.388937875751503, 0.025048360075339858 -192, 0.39092184368737476, 0.02333066686964983 -193, 0.3929058116232465, 0.021163849255882144 -194, 0.39488977955911825, 0.018649518038920673 -195, 0.39687374749499, 0.015904018618641446 -196, 0.39885771543086174, 0.013053456576381392 -197, 0.4008416833667335, 0.01022813530149914 -198, 0.4028256513026052, 0.007556585914512282 -199, 0.40480961923847697, 0.005159449356900526 -200, 0.4067935871743487, 0.003143540947434207 -201, 0.40877755511022046, 0.001596472756954287 -202, 0.4107615230460922, 0.0005822148779162445 -203, 0.41274549098196395, 0.0001379349307479975 -204, 0.4147294589178357, 0.0002723663906171204 -205, 0.41671342685370744, 0.0009658304295724124 -206, 0.4186973947895792, 0.0021718907815956433 -207, 0.4206813627254509, 0.0038204786964381836 -208, 0.42266533066132267, 0.005822206686778807 -209, 0.4246492985971944, 0.00807351144553656 -210, 0.42663326653306616, 0.010462235517300439 -211, 0.4286172344689379, 0.012873272374476484 -212, 0.43060120240480965, 0.015193951170327341 -213, 0.4325851703406814, 0.017318911741101627 -214, 0.43456913827655314, 0.019154302457323406 -215, 0.4365531062124249, 0.02062121023427756 -216, 0.4385370741482966, 0.021658294151797174 -217, 0.44052104208416837, 0.022223636927716544 -218, 0.4425050100200401, 0.022295851343178564 -219, 0.44448897795591186, 0.021874484335386073 -220, 0.4464729458917836, 0.02097975476338624 -221, 0.44845691382765535, 0.019651647884218724 -222, 0.4504408817635271, 0.017948376696229146 -223, 0.45242484969939883, 0.015944213409551735 -224, 0.4544088176352705, 0.013726698199906193 -225, 0.45639278557114227, 0.011393250203976028 -226, 0.458376753507014, 0.00904723825169567 -227, 0.46036072144288576, 0.0067936141627217735 -228, 0.4623446893787575, 0.004734264635469744 -229, 0.46432865731462925, 0.002963291172441509 -230, 0.466312625250501, 0.001562471623510888 -231, 0.46829659318637273, 0.0005971820099725045 -232, 0.4702805611222445, 0.00011305525542118876 -233, 0.4722645290581162, 0.00013361998494645114 -234, 0.47424849699398797, 0.0006590985922606173 -235, 0.4762324649298597, 0.0016664559033823143 -236, 0.47821643286573146, 0.0031106893010797083 -237, 0.4802004008016032, 0.004927252009008278 -238, 0.48218436873747494, 0.007035416996440179 -239, 0.4841683366733467, 0.00934233025415317 -240, 0.48615230460921843, 0.011747474637648414 -241, 0.4881362725450902, 0.014147269061668665 -242, 0.4901202404809619, 0.016439557549772294 -243, 0.49210420841683367, 0.018527790107948538 -244, 0.4940881763527054, 0.020324752880853687 -245, 0.49607214428857715, 0.021755759417808256 -246, 0.4980561122244489, 0.022761260944526455 -247, 0.5000400801603206, 0.023298866813712966 -248, 0.5020240480961924, 0.023344785094500477 -249, 0.5040080160320641, 0.022894698329123202 -250, 0.5059919839679359, 0.0219640835395822 -251, 0.5079759519038076, 0.02058797266194096 -252, 0.5099599198396794, 0.01882013459000496 -253, 0.5119438877755511, 0.016731648124927643 -254, 0.5139278557114229, 0.014408831409332683 -255, 0.5159118236472946, 0.011950502241680821 -256, 0.5178957915831663, 0.009464568041977305 -257, 0.5198797595190381, 0.007063985106731697 -258, 0.5218637274549098, 0.004862182277144541 -259, 0.5238476953907816, 0.0029681091037364084 -260, 0.5258316633266533, 0.0014811346546237739 -261, 0.5278156312625251, 0.000486079503704131 -262, 0.5297995991983968, 4.869861470054776e-05 -263, 0.5317835671342686, 0.00021193684162505277 -264, 0.5337675350701403, 0.0009932457099753257 -265, 0.535751503006012, 0.0023831802222176583 -266, 0.5377354709418838, 0.004345394715813398 -267, 0.5397194388777555, 0.0068180403841822815 -268, 0.5417034068136273, 0.009716450625062706 -269, 0.543687374749499, 0.012936900724504447 -270, 0.5456713426853708, 0.01636115898480383 -271, 0.5476553106212425, 0.019861514793368784 -272, 0.5496392785571143, 0.023305975875961987 -273, 0.551623246492986, 0.026563366329670324 -274, 0.5536072144288577, 0.029508118832884456 -275, 0.5555911823647295, 0.03202462662109848 -276, 0.5575751503006012, 0.03401109178813516 -277, 0.559559118236473, 0.0353828667589039 -278, 0.5615430861723447, 0.03607532889791913 -279, 0.5635270541082165, 0.03604635071583357 -280, 0.5655110220440882, 0.03527842913439466 -281, 0.56749498997996, 0.033780517814299825 -282, 0.5694789579158317, 0.03158956906567713 -283, 0.5714629258517034, 0.02877173989735132 -284, 0.5734468937875752, 0.025423155100636436 -285, 0.5754308617234469, 0.021670055336900616 -286, 0.5774148296593187, 0.017668098632004647 -287, 0.5793987975951904, 0.01360054066770616 -288, 0.5813827655310622, 0.009675006335630927 -289, 0.5833667334669339, 0.006118596777585109 -290, 0.5853507014028057, 0.0031711655175897836 -291, 0.5873346693386774, 0.0010767514123354313 -292, 0.5893186372745491, 7.337126248933872e-05 -293, 0.5913026052104209, 0.00038163193943142594 -294, 0.5932865731462926, 0.0021928845162606674 -295, 0.5952705410821644, 0.0056578607646843365 -296, 0.5972545090180361, 0.010876850180429382 -297, 0.5992384769539079, 0.017892447510133306 -298, 0.6012224448897796, 0.02668570573220913 -299, 0.6032064128256514, 0.037176183119206514 -300, 0.6051903807615231, 0.04922592865951015 -301, 0.6071743486973948, 0.06264698790058769 -302, 0.6091583166332666, 0.07721161770418648 -303, 0.6111422845691383, 0.09266414313905552 -304, 0.6131262525050101, 0.10873330807907763 -305, 0.6151102204408818, 0.1251440579875237 -306, 0.6170941883767536, 0.14162790993709415 -307, 0.6190781563126253, 0.15793135330400035 -308, 0.621062124248497, 0.17382202514428602 -309, 0.6230460921843688, 0.18909266915069564 -310, 0.6250300601202405, 0.2035630872277019 -311, 0.6270140280561123, 0.21708041825847607 -312, 0.628997995991984, 0.22951813481130462 -313, 0.6309819639278558, 0.24077414965038677 -314, 0.6329659318637275, 0.2507683876073812 -315, 0.6349498997995993, 0.2594401213643935 -316, 0.636933867735471, 0.2667453054381796 -317, 0.6389178356713427, 0.27265408047685313 -318, 0.6409018036072145, 0.2771485653514613 -319, 0.6428857715430862, 0.28022100981180365 -320, 0.644869739478958, 0.2818723458635943 -321, 0.6468537074148296, 0.28211115035364215 -322, 0.6488376753507014, 0.28095301267295864 -323, 0.6508216432865731, 0.27842028792463336 -324, 0.6528056112224448, 0.2745422053320233 -325, 0.6547895791583166, 0.26935529229416066 -326, 0.6567735470941883, 0.26290406487717055 -327, 0.6587575150300601, 0.2552419246275385 -328, 0.6607414829659318, 0.2464321888632971 -329, 0.6627254509018036, 0.2365491670864776 -330, 0.6647094188376753, 0.22567918059298267 -331, 0.6666933867735471, 0.21392140723490657 -332, 0.6686773547094188, 0.2013884209214198 -333, 0.6706613226452905, 0.18820628886495724 -334, 0.6726452905811623, 0.17451409231440515 -335, 0.674629258517034, 0.1604627521165676 -336, 0.6766132264529058, 0.1462130717958097 -337, 0.6785971943887775, 0.13193295929218454 -338, 0.6805811623246493, 0.11779385297798245 -339, 0.682565130260521, 0.10396645389418357 -340, 0.6845490981963928, 0.09061594680987843 -341, 0.6865330661322645, 0.07789696743553526 -342, 0.6885170340681362, 0.06594863026369184 -343, 0.690501002004008, 0.054889960155995815 -344, 0.6924849699398797, 0.0448160631944758 -345, 0.6944689378757515, 0.03579532599062774 -346, 0.6964529058116232, 0.027867851529310705 -347, 0.698436873747495, 0.02104523376601873 -348, 0.7004208416833667, 0.015311657001329292 -349, 0.7024048096192385, 0.01062619545703843 -350, 0.7043887775551102, 0.006926097790945572 -351, 0.706372745490982, 0.004130780592705779 -352, 0.7083567134268537, 0.0021462285840382345 -353, 0.7103406813627254, 0.0008695059176485245 -354, 0.7123246492985972, 0.000193116620165803 -355, 0.7143086172344689, 9.004092252511728e-06 -356, 0.7162925851703407, 0.0002120401728068695 -357, 0.7182765531062124, 0.000702915069422189 -358, 0.7202605210420842, 0.0013903939835314 -359, 0.7222444889779559, 0.0021929505257092573 -360, 0.7242284569138276, 0.003039819393007217 -361, 0.7262124248496994, 0.003871531521672373 -362, 0.7281963927855711, 0.0046400055593236855 -363, 0.7301803607214429, 0.0053082722075593804 -364, 0.7321643286573146, 0.00584990512316418 -365, 0.7341482965931864, 0.006248225808298112 -366, 0.7361322645290581, 0.006495342061634846 -367, 0.7381162324649299, 0.006591071435228216 -368, 0.7401002004008016, 0.006541793617286351 -369, 0.7420841683366733, 0.006359269203542507 -370, 0.7440681362725451, 0.006059457066852846 -371, 0.7460521042084168, 0.00566135838422228 -372, 0.7480360721442886, 0.005185912077448136 -373, 0.7500200400801603, 0.0046549636367218305 -374, 0.7520040080160321, 0.004090326684460166 -375, 0.7539879759519038, 0.003512953900481543 -376, 0.7559719438877756, 0.0029422308513142605 -377, 0.7579559118236473, 0.0023954027315925422 -378, 0.759939879759519, 0.0018871400311782185 -379, 0.7619238476953908, 0.0014292447890569052 -380, 0.7639078156312625, 0.0010304945698099762 -381, 0.7658917835671343, 0.0006966168414242411 -382, 0.767875751503006, 0.00043038230631217646 -383, 0.7698597194388778, 0.00023180218940765428 -384, 0.7718436873747495, 9.84117236624081e-05 -385, 0.7738276553106213, 2.5620234778188087e-05 -386, 0.775811623246493, 7.107377406914545e-06 -387, 0.7777955911823647, 3.52452002712822e-05 -388, 0.7797795591182365, 0.00010152673394992479 -389, 0.7817635270541082, 0.00019698356418977264 -390, 0.78374749498998, 0.0003125772019421228 -391, 0.7857314629258517, 0.0004395517998419576 -392, 0.7877154308617235, 0.0005697387072413838 -393, 0.7896993987975952, 0.0006958063329571489 -394, 0.791683366733467, 0.0008114516542151142 -395, 0.7936673346693387, 0.0009115323612152717 -396, 0.7956513026052104, 0.0009921409810344773 -397, 0.7976352705410822, 0.0010506243337552023 -398, 0.7996192384769539, 0.001085553314410324 -399, 0.8016032064128257, 0.0010966492627619995 -400, 0.8035871743486974, 0.0010846740892822139 -401, 0.8055711422845692, 0.0010512918893824008 -402, 0.8075551102204409, 0.0009989100239061956 -403, 0.8095390781563127, 0.0009305076002728938 -404, 0.8115230460921844, 0.0008494589854774166 -405, 0.8135070140280561, 0.00075935945102763 -406, 0.8154909819639279, 0.0006638593242132494 -407, 0.8174749498997996, 0.0005665121353902215 -408, 0.8194589178356714, 0.0004706412451662618 -409, 0.8214428857715431, 0.00037922834870297243 -410, 0.8234268537074149, 0.0002948261286759213 -411, 0.8254108216432866, 0.00021949620614409963 -412, 0.8273947895791584, 0.00015477246102135799 -413, 0.8293787575150301, 0.0001016487995011666 -414, 0.8313627254509018, 6.058956842976623e-05 -415, 0.8333466933867736, 3.156008360717915e-05 -416, 0.8353306613226453, 1.4074169956026223e-05 -417, 0.8373146292585171, 7.255217543491329e-06 -418, 0.8392985971943888, 9.90704095152882e-06 -419, 0.8412825651302606, 2.0590784494257463e-05 -420, 0.8432665330661323, 3.770422887497309e-05 -421, 0.845250501002004, 5.956010654522103e-05 -422, 0.8472344689378758, 8.446039924674957e-05 -423, 0.8492184368737475, 0.00011076404514623134 -424, 0.8512024048096193, 0.000136945996638117 -425, 0.853186372745491, 0.00016164611570323428 -426, 0.8551703406813628, 0.00018370694574904647 -427, 0.8571543086172345, 0.00020219993387200045 -428, 0.8591382765531063, 0.00021644017559656643 -429, 0.861122244488978, 0.00022599019928002835 -430, 0.8631062124248498, 0.00023065368741721326 -431, 0.8650901803607215, 0.0002304603388671356 -432, 0.8670741482965932, 0.00022564330514447347 -433, 0.869058116232465, 0.00021661078442208924 -434, 0.8710420841683367, 0.00020391343091570734 -435, 0.8730260521042085, 0.00018820923967411692 -436, 0.8750100200400802, 0.00017022750450188212 -437, 0.876993987975952, 0.00015073332857219645 -438, 0.8789779559118237, 0.00013049400330005412 -439, 0.8809619238476954, 0.000110248372099437 -440, 0.8829458917835672, 9.068007294074733e-05 -441, 0.8849298597194389, 7.239531824426369e-05 -442, 0.8869138276553107, 5.590563314823341e-05 -443, 0.8888977955911824, 4.1615743224692816e-05 -444, 0.8908817635270542, 2.9816588699738765e-05 -445, 0.8928657314629259, 2.0683251103241476e-05 -446, 0.8948496993987977, 1.4277415306972197e-05 -447, 0.8968336673346694, 1.0553858652037456e-05 -448, 0.898817635270541, 9.37036112123466e-06 -449, 0.9008016032064128, 1.0500366435400228e-05 -450, 0.9027855711422845, 1.3647692212306002e-05 -451, 0.9047695390781563, 1.846258532505323e-05 -452, 0.906753507014028, 2.455844273946196e-05 -453, 0.9087374749498998, 3.152856409783335e-05 -454, 0.9107214428857715, 3.896236544615011e-05 -455, 0.9127054108216432, 4.646055895408592e-05 -456, 0.914689378757515, 5.3648886578259536e-05 -457, 0.9166733466933867, 6.0190082063025034e-05 -458, 0.9186573146292585, 6.579382169509857e-05 -459, 0.9206412825651302, 7.022450672594404e-05 -460, 0.922625250501002, 7.330679697464244e-05 -461, 0.9246092184368737, 7.492888419096991e-05 -462, 0.9265931863727455, 7.504355436828833e-05 -463, 0.9285771543086172, 7.366714005748984e-05 -464, 0.930561122244489, 7.087650708308553e-05 -465, 0.9325450901803607, 6.68042555393723e-05 -466, 0.9345290581162324, 6.163234345009486e-05 -467, 0.9365130260521042, 5.55843640350746e-05 -468, 0.9384969939879759, 4.891672516024524e-05 -469, 0.9404809619238477, 4.190899314945568e-05 -470, 0.9424649298597194, 3.485367338835541e-05 -471, 0.9444488977955912, 2.8045707454866356e-05 -472, 0.9464328657314629, 2.177197095804266e-05 -473, 0.9484168336673346, 1.6301057635033626e-05 -474, 0.9504008016032064, 1.1873633040468727e-05 -475, 0.9523847695390781, 8.693634636673068e-06 -476, 0.9543687374749499, 6.92058338840771e-06 -477, 0.9563527054108216, 6.663254174100191e-06 -478, 0.9583366733466934, 7.974927607402354e-06 -479, 0.9603206412825651, 1.0850413561052086e-05 -480, 0.9623046092184369, 1.522499642978052e-05 -481, 0.9642885771543086, 2.0975403970465235e-05 -482, 0.9662725450901803, 2.7922845876944936e-05 -483, 0.9682565130260521, 3.5838106039737854e-05 -484, 0.9702404809619238, 4.444860517151333e-05 -485, 0.9722244488977956, 5.344728009985515e-05 -486, 0.9742084168336673, 6.250305492762241e-05 -487, 0.9761923847695391, 7.127261017668016e-05 -488, 0.9781763527054108, 7.941309194198597e-05 -489, 0.9801603206412826, 8.659534707463544e-05 -490, 0.9821442885771543, 9.251722552731721e-05 -491, 0.984128256513026, 9.691646007784041e-05 -492, 0.9861122244488978, 9.958261918441031e-05 -493, 0.9880961923847695, 0.00010036763270500964 -494, 0.9900801603206413, 9.919441397882462e-05 -495, 0.992064128256513, 9.606314591544722e-05 -496, 0.9940480961923848, 9.10548630266493e-05 -497, 0.9960320641282565, 8.433204463476253e-05 -498, 0.9980160320641283, 7.613603476225939e-05 -499, 1.0, 6.678121852368965e-05 diff --git a/Tests/Unit/gui2/data/bilayer3b.txt b/Tests/Unit/gui2/data/bilayer3b.txt deleted file mode 100644 index 801a30c227d..00000000000 --- a/Tests/Unit/gui2/data/bilayer3b.txt +++ /dev/null @@ -1,501 +0,0 @@ -# data from TimeOfFlightReflectometry.py -0.01 0.9999999999999998 -0.011983967935871743 1.0 -0.013967935871743487 1.0000000000000004 -0.01595190380761523 1.0000000000000002 -0.017935871743486972 0.9999999999999999 -0.019919839679358717 1.0 -0.02190380761523046 1.0000000000000004 -0.023887775551102206 1.0000000000000002 -0.02587174348697395 1.0000000000000004 -0.027855711422845694 1.0 -0.02983967935871744 1.0 -0.031823647294589176 1.0000000000000002 -0.03380761523046092 1.0000000000000004 -0.035791583166332665 0.9999999999999996 -0.03777555110220441 0.9999999999999998 -0.03975951903807615 0.9999999999999998 -0.0417434869739479 1.0 -0.04372745490981964 1.0 -0.045711422845691387 1.0 -0.04769539078156313 1.0 -0.049679358717434875 1.0 -0.05166332665330661 1.0 -0.05364729458917836 1.0000000000000002 -0.0556312625250501 1.0000000000000007 -0.057615230460921846 0.9999999999999998 -0.05959919839679359 1.0 -0.061583166332665334 0.9999999999999998 -0.06356713426853708 0.9999999999999996 -0.06555110220440881 0.9999999999999998 -0.06753507014028055 1.0 -0.0695190380761523 0.9999999999999999 -0.07150300601202404 0.9999999999999996 -0.07348697394789579 0.9999999999999997 -0.07547094188376753 0.9999999999999997 -0.07745490981963928 0.9999999999999996 -0.07943887775551102 0.9999999999999996 -0.08142284569138276 0.9999999999999998 -0.08340681362725451 1.0000000000000004 -0.08539078156312625 0.9999999999999993 -0.087374749498998 0.9999999999999996 -0.08935871743486974 1.0000000000000004 -0.09134268537074147 1.0000000000000004 -0.09332665330661322 1.0000000000000009 -0.09531062124248496 0.9999999999999998 -0.0972945891783567 0.9999999999999999 -0.09927855711422845 0.9999999999999993 -0.1012625250501002 1.0000000000000002 -0.10324649298597194 0.9999992528061048 -0.10523046092184368 0.9999985974506073 -0.10721442885771543 0.999997921309658 -0.10919839679358717 0.9999971427452203 -0.11118236472945892 0.9999962122090085 -0.11316633266533066 0.9999950799230959 -0.1151503006012024 0.9999936875180906 -0.11713426853707415 0.9999919626983287 -0.1191182364729459 0.9999898137850864 -0.12110220440881762 0.9999871230233972 -0.12308617234468937 0.9999837378321051 -0.12507014028056113 0.9999794590650891 -0.12705410821643287 0.9999740250410741 -0.12903807615230461 0.9999670895946517 -0.13102204408817636 0.999958191635177 -0.1330060120240481 0.999946712547489 -0.13498997995991985 0.9999318160224282 -0.1369739478957916 0.9999123622332293 -0.13895791583166334 0.9998867841333866 -0.14094188376753508 0.999852907147281 -0.14292585170340683 0.9998076831535236 -0.14490981963927857 0.9997467928547588 -0.14689378757515031 0.99966404291278 -0.14887775551102206 0.9995504376444865 -0.1508617234468938 0.9993927251319745 -0.15284569138276555 0.999171077251899 -0.1548296593186373 0.9988553104956281 -0.15681362725450904 0.9983985870085685 -0.15879759519038078 0.9977266436988359 -0.16078156312625252 0.9967188386688286 -0.16276553106212427 0.9951737046113363 -0.164749498997996 0.9927440254766213 -0.16673346693386776 0.9888093621532706 -0.1687174348697395 0.9822141139510879 -0.17070140280561122 0.9707022984498375 -0.17268537074148296 0.9496383601680007 -0.1746693386773547 0.909021525956259 -0.17665330661322645 0.8268006578284636 -0.1786372745490982 0.6588522111821937 -0.18062124248496994 0.3622399614191024 -0.18260521042084168 0.06420150811805793 -0.18458917835671343 0.024633662977522494 -0.18657314629258517 0.15244093776327813 -0.18855711422845692 0.27529995099868965 -0.19054108216432866 0.3526612082641258 -0.1925250501002004 0.39063545710665876 -0.19450901803607215 0.3990333790987325 -0.1964929859719439 0.3845452071189573 -0.19847695390781564 0.35144925997264576 -0.20046092184368738 0.3031439950053475 -0.20244488977955913 0.24353655832687798 -0.20442885771543087 0.17808572440435927 -0.20641282565130262 0.11410584881256816 -0.20839679358717436 0.059830552998912524 -0.2103807615230461 0.022250824859572964 -0.21236472945891785 0.004789833661897041 -0.2143486973947896 0.006356984649312908 -0.21633266533066134 0.022365632593830034 -0.21831663326653308 0.04682241052607952 -0.22030060120240483 0.07416136769212864 -0.22228456913827657 0.1001720615138792 -0.22426853707414832 0.12213465250467488 -0.22625250501002006 0.13855308036162264 -0.2282364729458918 0.1487974036569265 -0.23022044088176355 0.15280542074664977 -0.23220440881763527 0.15088251751204407 -0.234188376753507 0.14358811351822667 -0.23617234468937875 0.13168199675274497 -0.2381563126252505 0.11610304292527356 -0.24014028056112224 0.09795584761494282 -0.242124248496994 0.07848468865313928 -0.24410821643286573 0.05901971090221587 -0.24609218436873748 0.04088892407033809 -0.24807615230460922 0.02530184438722244 -0.25006012024048097 0.013223849390327617 -0.2520440881763527 0.0052693211843496445 -0.25402805611222445 0.0016411405846048022 -0.2560120240480962 0.002132606619548132 -0.25799599198396794 0.006189794194701373 -0.2599799599198397 0.013015844928207822 -0.26196392785571143 0.021690550068973954 -0.2639478957915832 0.03128056624196508 -0.2659318637274549 0.04092443009513365 -0.26791583166332666 0.049887002117678506 -0.2698997995991984 0.05758607973651505 -0.27188376753507015 0.06359816326314606 -0.2738677354709419 0.06765120572513812 -0.27585170340681364 0.06961093772604753 -0.2778356713426854 0.0694652862160818 -0.27981963927855713 0.06730932686430591 -0.2818036072144289 0.06333151898991729 -0.2837875751503006 0.05780078596127256 -0.28577154308617236 0.051053313090844835 -0.2877555110220441 0.04347770115747981 -0.28973947895791585 0.035497304971156975 -0.2917234468937876 0.027549165730337055 -0.29370741482965934 0.02005983448493074 -0.2956913827655311 0.013419426316096529 -0.29767535070140283 0.007956202507095663 -0.2996593186372746 0.0039145718050655805 -0.3016432865731463 0.0014393995905691696 -0.30362725450901806 0.0005688307236762597 -0.3056112224448898 0.0012365918074232387 -0.30759519038076155 0.0032832519146904853 -0.3095791583166333 0.006474578249753218 -0.31156312625250504 0.010524253267492914 -0.3135470941883768 0.01511797850115291 -0.31553106212424853 0.01993633203535728 -0.3175150300601203 0.024674476171810362 -0.319498997995992 0.02905767996968943 -0.32148296593186376 0.03285241227312436 -0.3234669338677355 0.03587334202207215 -0.32545090180360725 0.03798691116784891 -0.327434869739479 0.03911224589336394 -0.3294188376753507 0.039220104446289754 -0.33140280561122243 0.03833039410542655 -0.3333867735470942 0.036508587609869456 -0.3353707414829659 0.0338611788968911 -0.33735470941883766 0.03053017320088866 -0.3393386773547094 0.02668652941152426 -0.34132264529058115 0.02252247460745127 -0.3433066132264529 0.01824269289141468 -0.34529058116232464 0.01405454201275371 -0.3472745490981964 0.010157647151281007 -0.34925851703406813 0.006733424032981525 -0.35124248496993987 0.003935247174387443 -0.3532264529058116 0.0018800579891315712 -0.35521042084168336 0.0006421686942934479 -0.3571943887775551 0.0002498520126686543 -0.35917835671342685 0.0006850336571871474 -0.3611623246492986 0.0018860713465820211 -0.36314629258517034 0.0037532727931319895 -0.3651302605210421 0.006156536579232831 -0.3671142284569138 0.008944337730497173 -0.36909819639278557 0.011953240771592386 -0.3710821643286573 0.015017195992625963 -0.37306613226452906 0.017976027723366806 -0.3750501002004008 0.020682715399392357 -0.37703406813627255 0.02300925974043486 -0.3790180360721443 0.024851087812409446 -0.38100200400801604 0.026130065575115363 -0.3829859719438878 0.026796250719654913 -0.3849699398797595 0.02682853773180647 -0.38695390781563127 0.026234332576648815 -0.388937875751503 0.025048360075339858 -0.39092184368737476 0.02333066686964983 -0.3929058116232465 0.021163849255882144 -0.39488977955911825 0.018649518038920673 -0.39687374749499 0.015904018618641446 -0.39885771543086174 0.013053456576381392 -0.4008416833667335 0.01022813530149914 -0.4028256513026052 0.007556585914512282 -0.40480961923847697 0.005159449356900526 -0.4067935871743487 0.003143540947434207 -0.40877755511022046 0.001596472756954287 -0.4107615230460922 0.0005822148779162445 -0.41274549098196395 0.0001379349307479975 -0.4147294589178357 0.0002723663906171204 -0.41671342685370744 0.0009658304295724124 -0.4186973947895792 0.0021718907815956433 -0.4206813627254509 0.0038204786964381836 -0.42266533066132267 0.005822206686778807 -0.4246492985971944 0.00807351144553656 -0.42663326653306616 0.010462235517300439 -0.4286172344689379 0.012873272374476484 -0.43060120240480965 0.015193951170327341 -0.4325851703406814 0.017318911741101627 -0.43456913827655314 0.019154302457323406 -0.4365531062124249 0.02062121023427756 -0.4385370741482966 0.021658294151797174 -0.44052104208416837 0.022223636927716544 -0.4425050100200401 0.022295851343178564 -0.44448897795591186 0.021874484335386073 -0.4464729458917836 0.02097975476338624 -0.44845691382765535 0.019651647884218724 -0.4504408817635271 0.017948376696229146 -0.45242484969939883 0.015944213409551735 -0.4544088176352705 0.013726698199906193 -0.45639278557114227 0.011393250203976028 -0.458376753507014 0.00904723825169567 -0.46036072144288576 0.0067936141627217735 -0.4623446893787575 0.004734264635469744 -0.46432865731462925 0.002963291172441509 -0.466312625250501 0.001562471623510888 -0.46829659318637273 0.0005971820099725045 -0.4702805611222445 0.00011305525542118876 -0.4722645290581162 0.00013361998494645114 -0.47424849699398797 0.0006590985922606173 -0.4762324649298597 0.0016664559033823143 -0.47821643286573146 0.0031106893010797083 -0.4802004008016032 0.004927252009008278 -0.48218436873747494 0.007035416996440179 -0.4841683366733467 0.00934233025415317 -0.48615230460921843 0.011747474637648414 -0.4881362725450902 0.014147269061668665 -0.4901202404809619 0.016439557549772294 -0.49210420841683367 0.018527790107948538 -0.4940881763527054 0.020324752880853687 -0.49607214428857715 0.021755759417808256 -0.4980561122244489 0.022761260944526455 -0.5000400801603206 0.023298866813712966 -0.5020240480961924 0.023344785094500477 -0.5040080160320641 0.022894698329123202 -0.5059919839679359 0.0219640835395822 -0.5079759519038076 0.02058797266194096 -0.5099599198396794 0.01882013459000496 -0.5119438877755511 0.016731648124927643 -0.5139278557114229 0.014408831409332683 -0.5159118236472946 0.011950502241680821 -0.5178957915831663 0.009464568041977305 -0.5198797595190381 0.007063985106731697 -0.5218637274549098 0.004862182277144541 -0.5238476953907816 0.0029681091037364084 -0.5258316633266533 0.0014811346546237739 -0.5278156312625251 0.000486079503704131 -0.5297995991983968 4.869861470054776e-05 -0.5317835671342686 0.00021193684162505277 -0.5337675350701403 0.0009932457099753257 -0.535751503006012 0.0023831802222176583 -0.5377354709418838 0.004345394715813398 -0.5397194388777555 0.0068180403841822815 -0.5417034068136273 0.009716450625062706 -0.543687374749499 0.012936900724504447 -0.5456713426853708 0.01636115898480383 -0.5476553106212425 0.019861514793368784 -0.5496392785571143 0.023305975875961987 -0.551623246492986 0.026563366329670324 -0.5536072144288577 0.029508118832884456 -0.5555911823647295 0.03202462662109848 -0.5575751503006012 0.03401109178813516 -0.559559118236473 0.0353828667589039 -0.5615430861723447 0.03607532889791913 -0.5635270541082165 0.03604635071583357 -0.5655110220440882 0.03527842913439466 -0.56749498997996 0.033780517814299825 -0.5694789579158317 0.03158956906567713 -0.5714629258517034 0.02877173989735132 -0.5734468937875752 0.025423155100636436 -0.5754308617234469 0.021670055336900616 -0.5774148296593187 0.017668098632004647 -0.5793987975951904 0.01360054066770616 -0.5813827655310622 0.009675006335630927 -0.5833667334669339 0.006118596777585109 -0.5853507014028057 0.0031711655175897836 -0.5873346693386774 0.0010767514123354313 -0.5893186372745491 7.337126248933872e-05 -0.5913026052104209 0.00038163193943142594 -0.5932865731462926 0.0021928845162606674 -0.5952705410821644 0.0056578607646843365 -0.5972545090180361 0.010876850180429382 -0.5992384769539079 0.017892447510133306 -0.6012224448897796 0.02668570573220913 -0.6032064128256514 0.037176183119206514 -0.6051903807615231 0.04922592865951015 -0.6071743486973948 0.06264698790058769 -0.6091583166332666 0.07721161770418648 -0.6111422845691383 0.09266414313905552 -0.6131262525050101 0.10873330807907763 -0.6151102204408818 0.1251440579875237 -0.6170941883767536 0.14162790993709415 -0.6190781563126253 0.15793135330400035 -0.621062124248497 0.17382202514428602 -0.6230460921843688 0.18909266915069564 -0.6250300601202405 0.2035630872277019 -0.6270140280561123 0.21708041825847607 -0.628997995991984 0.22951813481130462 -0.6309819639278558 0.24077414965038677 -0.6329659318637275 0.2507683876073812 -0.6349498997995993 0.2594401213643935 -0.636933867735471 0.2667453054381796 -0.6389178356713427 0.27265408047685313 -0.6409018036072145 0.2771485653514613 -0.6428857715430862 0.28022100981180365 -0.644869739478958 0.2818723458635943 -0.6468537074148296 0.28211115035364215 -0.6488376753507014 0.28095301267295864 -0.6508216432865731 0.27842028792463336 -0.6528056112224448 0.2745422053320233 -0.6547895791583166 0.26935529229416066 -0.6567735470941883 0.26290406487717055 -0.6587575150300601 0.2552419246275385 -0.6607414829659318 0.2464321888632971 -0.6627254509018036 0.2365491670864776 -0.6647094188376753 0.22567918059298267 -0.6666933867735471 0.21392140723490657 -0.6686773547094188 0.2013884209214198 -0.6706613226452905 0.18820628886495724 -0.6726452905811623 0.17451409231440515 -0.674629258517034 0.1604627521165676 -0.6766132264529058 0.1462130717958097 -0.6785971943887775 0.13193295929218454 -0.6805811623246493 0.11779385297798245 -0.682565130260521 0.10396645389418357 -0.6845490981963928 0.09061594680987843 -0.6865330661322645 0.07789696743553526 -0.6885170340681362 0.06594863026369184 -0.690501002004008 0.054889960155995815 -0.6924849699398797 0.0448160631944758 -0.6944689378757515 0.03579532599062774 -0.6964529058116232 0.027867851529310705 -0.698436873747495 0.02104523376601873 -0.7004208416833667 0.015311657001329292 -0.7024048096192385 0.01062619545703843 -0.7043887775551102 0.006926097790945572 -0.706372745490982 0.004130780592705779 -0.7083567134268537 0.0021462285840382345 -0.7103406813627254 0.0008695059176485245 -0.7123246492985972 0.000193116620165803 -0.7143086172344689 9.004092252511728e-06 -0.7162925851703407 0.0002120401728068695 -0.7182765531062124 0.000702915069422189 -0.7202605210420842 0.0013903939835314 -0.7222444889779559 0.0021929505257092573 -0.7242284569138276 0.003039819393007217 -0.7262124248496994 0.003871531521672373 -0.7281963927855711 0.0046400055593236855 -0.7301803607214429 0.0053082722075593804 -0.7321643286573146 0.00584990512316418 -0.7341482965931864 0.006248225808298112 -0.7361322645290581 0.006495342061634846 -0.7381162324649299 0.006591071435228216 -0.7401002004008016 0.006541793617286351 -0.7420841683366733 0.006359269203542507 -0.7440681362725451 0.006059457066852846 -0.7460521042084168 0.00566135838422228 -0.7480360721442886 0.005185912077448136 -0.7500200400801603 0.0046549636367218305 -0.7520040080160321 0.004090326684460166 -0.7539879759519038 0.003512953900481543 -0.7559719438877756 0.0029422308513142605 -0.7579559118236473 0.0023954027315925422 -0.759939879759519 0.0018871400311782185 -0.7619238476953908 0.0014292447890569052 -0.7639078156312625 0.0010304945698099762 -0.7658917835671343 0.0006966168414242411 -0.767875751503006 0.00043038230631217646 -0.7698597194388778 0.00023180218940765428 -0.7718436873747495 9.84117236624081e-05 -0.7738276553106213 2.5620234778188087e-05 -0.775811623246493 7.107377406914545e-06 -0.7777955911823647 3.52452002712822e-05 -0.7797795591182365 0.00010152673394992479 -0.7817635270541082 0.00019698356418977264 -0.78374749498998 0.0003125772019421228 -0.7857314629258517 0.0004395517998419576 -0.7877154308617235 0.0005697387072413838 -0.7896993987975952 0.0006958063329571489 -0.791683366733467 0.0008114516542151142 -0.7936673346693387 0.0009115323612152717 -0.7956513026052104 0.0009921409810344773 -0.7976352705410822 0.0010506243337552023 -0.7996192384769539 0.001085553314410324 -0.8016032064128257 0.0010966492627619995 -0.8035871743486974 0.0010846740892822139 -0.8055711422845692 0.0010512918893824008 -0.8075551102204409 0.0009989100239061956 -0.8095390781563127 0.0009305076002728938 -0.8115230460921844 0.0008494589854774166 -0.8135070140280561 0.00075935945102763 -0.8154909819639279 0.0006638593242132494 -0.8174749498997996 0.0005665121353902215 -0.8194589178356714 0.0004706412451662618 -0.8214428857715431 0.00037922834870297243 -0.8234268537074149 0.0002948261286759213 -0.8254108216432866 0.00021949620614409963 -0.8273947895791584 0.00015477246102135799 -0.8293787575150301 0.0001016487995011666 -0.8313627254509018 6.058956842976623e-05 -0.8333466933867736 3.156008360717915e-05 -0.8353306613226453 1.4074169956026223e-05 -0.8373146292585171 7.255217543491329e-06 -0.8392985971943888 9.90704095152882e-06 -0.8412825651302606 2.0590784494257463e-05 -0.8432665330661323 3.770422887497309e-05 -0.845250501002004 5.956010654522103e-05 -0.8472344689378758 8.446039924674957e-05 -0.8492184368737475 0.00011076404514623134 -0.8512024048096193 0.000136945996638117 -0.853186372745491 0.00016164611570323428 -0.8551703406813628 0.00018370694574904647 -0.8571543086172345 0.00020219993387200045 -0.8591382765531063 0.00021644017559656643 -0.861122244488978 0.00022599019928002835 -0.8631062124248498 0.00023065368741721326 -0.8650901803607215 0.0002304603388671356 -0.8670741482965932 0.00022564330514447347 -0.869058116232465 0.00021661078442208924 -0.8710420841683367 0.00020391343091570734 -0.8730260521042085 0.00018820923967411692 -0.8750100200400802 0.00017022750450188212 -0.876993987975952 0.00015073332857219645 -0.8789779559118237 0.00013049400330005412 -0.8809619238476954 0.000110248372099437 -0.8829458917835672 9.068007294074733e-05 -0.8849298597194389 7.239531824426369e-05 -0.8869138276553107 5.590563314823341e-05 -0.8888977955911824 4.1615743224692816e-05 -0.8908817635270542 2.9816588699738765e-05 -0.8928657314629259 2.0683251103241476e-05 -0.8948496993987977 1.4277415306972197e-05 -0.8968336673346694 1.0553858652037456e-05 -0.898817635270541 9.37036112123466e-06 -0.9008016032064128 1.0500366435400228e-05 -0.9027855711422845 1.3647692212306002e-05 -0.9047695390781563 1.846258532505323e-05 -0.906753507014028 2.455844273946196e-05 -0.9087374749498998 3.152856409783335e-05 -0.9107214428857715 3.896236544615011e-05 -0.9127054108216432 4.646055895408592e-05 -0.914689378757515 5.3648886578259536e-05 -0.9166733466933867 6.0190082063025034e-05 -0.9186573146292585 6.579382169509857e-05 -0.9206412825651302 7.022450672594404e-05 -0.922625250501002 7.330679697464244e-05 -0.9246092184368737 7.492888419096991e-05 -0.9265931863727455 7.504355436828833e-05 -0.9285771543086172 7.366714005748984e-05 -0.930561122244489 7.087650708308553e-05 -0.9325450901803607 6.68042555393723e-05 -0.9345290581162324 6.163234345009486e-05 -0.9365130260521042 5.55843640350746e-05 -0.9384969939879759 4.891672516024524e-05 -0.9404809619238477 4.190899314945568e-05 -0.9424649298597194 3.485367338835541e-05 -0.9444488977955912 2.8045707454866356e-05 -0.9464328657314629 2.177197095804266e-05 -0.9484168336673346 1.6301057635033626e-05 -0.9504008016032064 1.1873633040468727e-05 -0.9523847695390781 8.693634636673068e-06 -0.9543687374749499 6.92058338840771e-06 -0.9563527054108216 6.663254174100191e-06 -0.9583366733466934 7.974927607402354e-06 -0.9603206412825651 1.0850413561052086e-05 -0.9623046092184369 1.522499642978052e-05 -0.9642885771543086 2.0975403970465235e-05 -0.9662725450901803 2.7922845876944936e-05 -0.9682565130260521 3.5838106039737854e-05 -0.9702404809619238 4.444860517151333e-05 -0.9722244488977956 5.344728009985515e-05 -0.9742084168336673 6.250305492762241e-05 -0.9761923847695391 7.127261017668016e-05 -0.9781763527054108 7.941309194198597e-05 -0.9801603206412826 8.659534707463544e-05 -0.9821442885771543 9.251722552731721e-05 -0.984128256513026 9.691646007784041e-05 -0.9861122244488978 9.958261918441031e-05 -0.9880961923847695 0.00010036763270500964 -0.9900801603206413 9.919441397882462e-05 -0.992064128256513 9.606314591544722e-05 -0.9940480961923848 9.10548630266493e-05 -0.9960320641282565 8.433204463476253e-05 -0.9980160320641283 7.613603476225939e-05 -1.0 6.678121852368965e-05 diff --git a/Tests/Unit/gui2/data/p15320_00017128_report.csv b/Tests/Unit/gui2/data/p15320_00017128_report.csv deleted file mode 100644 index e668b1fc1c3..00000000000 --- a/Tests/Unit/gui2/data/p15320_00017128_report.csv +++ /dev/null @@ -1,254 +0,0 @@ -# ***************************************************************************** - -# Reflected beam: p15320_00017128.cfg - -# Primary beam: p15320_00017114.cfg - -# Comment: reflectivity for Sample_1_water_swollen static at 0.60 deg - -# wavelength -# alpha_corr -# q_nomin -# q_corr -# prim_integ_peak -# prim_sigma_integ_peak -# refl_integ_peak -# refl_sigma_integ_peak -# reflectivity -# sigma_reflectivity - -# ***************************************************************************** - -2e-10, --, 0.06579616009915212, --, --, 0.0, --, 0.0, --, -- -2.0201005025125624e-10, --, 0.06514147193896157, --, --, 0.0, --, 0.0, --, -- -2.0404030201257538e-10, --, 0.06449329808882265, --, --, 0.0, --, 0.0, --, -- -2.0609095831420927e-10, --, 0.06385157372972991, --, --, 0.0, --, 0.0, --, -- -2.0816222422691487e-10, --, 0.06321623468764305, --, --, 0.0, --, 0.0, --, -- -2.102543068824617e-10, --, 0.06258721742706949, --, --, 0.0, --, 0.0, --, -- -2.123674154943457e-10, --, 0.0619644590447106, --, --, 0.0, --, 0.0, --, -- -2.1450176137871093e-10, --, 0.06134789726317121, --, --, 0.0, --, 0.0, --, -- -2.1665755797548188e-10, --, 0.0607374704247317, --, --, 0.0, --, 0.0, --, -- -2.1883502086970776e-10, --, 0.06013311748518215, --, --, 0.0, --, 0.0, --, -- -2.2103436781312186e-10, --, 0.05953477800771766, --, --, 0.0, --, 0.0, --, -- -2.2325581874591706e-10, --, 0.058942392156894605, --, --, 0.0, --, 0.0, --, -- -2.254995958187403e-10, --, 0.058355900692646914, --, --, 0.0, --, 0.0, --, -- -2.277659234149085e-10, --, 0.05777524496436188, --, --, 0.0, --, 0.0, --, -- -2.300550281728472e-10, --, 0.05720036690501501, --, --, 0.0, --, 0.0, --, -- -2.323671390087552e-10, --, 0.05663120902536312, --, --, 0.0, --, 0.0, --, -- -2.347024871394964e-10, --, 0.05606771440819534, --, --, 0.0, --, 0.0, --, -- -2.370613061057225e-10, --, 0.05550982670264116, --, --, 0.0, --, 0.0, --, -- -2.394438317952272e-10, --, 0.054957490118535283, --, 398.4040263797292, 21.143187154929578, 0.0, 1.0, --, -- -2.4185030246653594e-10, --, 0.05441064942083844, --, 1534.6363068379035, 40.841746377161826, 2.0, 1.7320508075688772, 7.174614594692112e-05, 6.245216967779148e-05 -2.4428095877273223e-10, --, 0.05386924992411369, --, 1571.062200624783, 41.776479152073485, 5.0, 2.6457513110645907, 0.00018093055054731107, 9.695866471987633e-05 -2.467360437855235e-10, 0.5658307609070953, 0.05333323748705783, 0.05029607896932152, 1609.1153418951753, 42.4964992330779, 8.7428554899919, 3.6308772628693364, 0.00031875719788951106, 0.00013503187882546317 -2.492158030195488e-10, --, 0.05280255850708713, --, 1804.0966099733887, 44.64826087194522, 5.0, 3.1622776601683795, 0.00015543733529493358, 9.910737714287484e-05 -2.5172048445693114e-10, --, 0.05227715991497681, --, 1905.99856531297, 46.198585125687686, 7.721774846696749, 3.7479602227489783, 0.00021474955458839, 0.00010568298278671186 -2.5425033857207615e-10, 0.5634863884525005, 0.051756989169554166, 0.04860736967987198, 2039.8120444290178, 47.24636149029365, 12.0, 3.872983346207417, 0.00036869987271973123, 0.00012213594755335256 -2.568056183567201e-10, 0.5713083859852034, 0.05124199425244418, 0.04879171830060828, 2141.590072891357, 48.50213534412636, 14.0, 4.0, 0.00035446361790297755, 0.00010470511834254454 -2.5938657934522977e-10, --, 0.05073212366286763, --, 2236.3705889158855, 49.56563496401141, 5.850225616105519, 3.633552998549248, 0.00015025608606638726, 9.393883523707522e-05 -2.6199347964015667e-10, 0.5677963509669373, 0.05022732641249085, 0.04753157851263009, 2302.157237070461, 50.905139402940115, 10.0, 3.605551275463989, 0.00023744327450590788, 8.727146133225931e-05 -2.646265799380477e-10, 0.5722895294845328, 0.04972755202032676, 0.047431007138860386, 2508.854911489066, 52.66065978043699, 20.19378687955571, 5.6013275066596675, 0.0004614643484094782, 0.0001318102634290037 -2.672861435555154e-10, 0.5736917814912171, 0.04923275050768672, 0.04707411437361956, 2473.2956727987967, 52.487171371921704, 18.678792086081224, 4.693892580777178, 0.00043012186251625194, 0.00011177463926242429 -2.699724364555708e-10, 0.5673838849262329, 0.048742872393182375, 0.04609328960397372, 2553.0380539164644, 53.60685494976364, 10.0, 3.4641016151377544, 0.00019529503293149234, 6.894263191780379e-05 -2.7268572727421967e-10, 0.5703073662094136, 0.04825786868777759, 0.0458697775751033, 2581.8108989934053, 53.542769676755654, 15.0, 4.0, 0.000295488443890465, 8.11691197407189e-05 -2.754262873473274e-10, 0.572172846813268, 0.04777769088988926, 0.04556190446932175, 2665.3453314821245, 55.25926397241143, 21.656704847853753, 5.001804603184886, 0.00043356143273443585, 0.00010369473055768681 -2.7819439073775273e-10, 0.5717746304843873, 0.047302290980537134, 0.04507715893548082, 2794.9199583477957, 56.197629405966936, 12.626564850167767, 4.366377803139587, 0.0002554589412383227, 8.967655403117131e-05 -2.809903142627552e-10, 0.576570012954414, 0.04683162141854175, 0.0450029105982062, 2750.573446719889, 55.569241902415435, 20.0, 5.0990195135927845, 0.00041561028353391874, 0.00010876079076377993 -2.8381433752167737e-10, 0.5725709086495587, 0.04636563513577019, 0.044246095315304754, 2857.5741756334605, 56.430373190298766, 27.0, 5.291502622129181, 0.0005440962814405112, 0.00011113317638011863 -2.8666674292390517e-10, 0.5746153748632182, 0.045904285532429215, 0.04396224694909134, 2935.753939604304, 57.30902668943389, 19.716117088973736, 5.116484171206859, 0.00038541462664650443, 0.00010240776492185803 -2.8954781571711026e-10, 0.5698054126261662, 0.04544752647240504, 0.04316048835638389, 3058.1888233252344, 58.2220849564387, 29.0, 5.916079783099616, 0.0004911096972496526, 0.0001039771848510276 -2.9245784401577466e-10, 0.5733696028334697, 0.04499531227864977, 0.04299830876441435, 3337.0747657063516, 60.62404845973859, 30.587191947240537, 6.014445182259056, 0.0005013210960663508, 0.00010222661196437309 -2.953971188300034e-10, 0.5716442005021961, 0.044547597728613476, 0.04244236473496125, 3592.761818812912, 63.30881376813905, 41.0, 6.48074069840786, 0.0006126764318371097, 0.00010233658468871584 -2.9836593409462653e-10, 0.5708395221880317, 0.0441043380497218, 0.04196090483279468, 3779.889482818466, 64.80665983234653, 23.69802152030545, 5.568948721673628, 0.0003224553722452253, 7.774949758730845e-05 -3.013645866985925e-10, 0.5724348686872228, 0.043665488914898715, 0.04165948237670842, 3891.5324540090246, 66.06008698133806, 26.43653823903737, 6.274276005727119, 0.0003650870019924193, 8.868101897045752e-05 -3.043933765146588e-10, 0.5725411974241433, 0.04323100643813355, 0.04125262108544657, 3978.883720930233, 65.948711573006, 33.047529792895986, 6.498124045100933, 0.00046225822313480854, 9.38367830129222e-05 -3.0745260642937894e-10, 0.5716207902656013, 0.04280084717009243, 0.040776492306401145, 4134.832696980216, 67.54499959139945, 42.66459562651857, 7.417415270054759, 0.0005772955944842899, 0.00010426584080885295 -3.105425823733927e-10, 0.5750610953929685, 0.04237496809377312, 0.04061371970709788, 4100.026726830962, 66.97107811372003, 50.0, 7.416198487095663, 0.0006502373192951335, 0.00010139067377168397 -3.1366361335201975e-10, 0.5690533953142517, 0.04195332662020323, 0.039789544680610384, 4111.689806780054, 67.57334452259086, 37.58694897604998, 7.368657887016019, 0.0004632440650837637, 9.346574069364315e-05 -3.168160114761606e-10, 0.5701125645904992, 0.04153588058418132, 0.039466949077802224, 4325.352701608238, 68.50574277226899, 52.29940992710865, 7.879384037790949, 0.0006317448114345859, 9.965439780906357e-05 -3.2000009199350886e-10, 0.5709010070900161, 0.04112258824006012, 0.03912827941940098, 4234.6447529792895, 68.05526114813603, 54.42540784449844, 8.136205993548533, 0.0006386144021849369, 9.997887832101616e-05 -3.2321617332007674e-10, 0.5745362371604681, 0.04071340825757197, 0.03898560650077779, 4460.844706699063, 69.22707856614485, 49.0, 7.416198487095663, 0.0005963518369003051, 9.400150511290926e-05 -3.264645770720372e-10, 0.5724001235643003, 0.04030829971769564, 0.0384541894049841, 4554.0874696286, 69.55284861480425, 54.160823788036566, 8.555208947702019, 0.0006259820670105101, 0.00010263802037503378 -3.297456280978868e-10, 0.57096302720046, 0.039907222108564344, 0.03797597945935461, 4561.069536040728, 69.34183068551383, 56.0, 8.06225774829855, 0.0006836675075753987, 0.00010264651379216846 -3.330596545109308e-10, 0.5708771323986914, 0.03951013532141446, 0.03759245300232875, 4586.875112808052, 69.75350560798051, 49.71072544255467, 7.351349971773715, 0.0005634676758406563, 8.676083357828955e-05 -3.364069877220959e-10, 0.5717266439194317, 0.03911699964657452, 0.03727378090572066, 4680.320953372672, 70.00460886300118, 54.0, 7.416198487095662, 0.0005893689938457065, 8.473371457359537e-05 -3.397879624730717e-10, 0.5717390051047588, 0.03872777576949418, 0.0369036953571727, 4633.793127386326, 69.49249508122209, 69.43543908365152, 8.604503877835503, 0.0007957714256769522, 0.00010391591883596735 -3.432029168697859e-10, 0.5712708066166515, 0.038342424766812655, 0.03650657557440909, 4820.991044776119, 71.20173148993062, 93.0, 10.198039027185569, 0.001041349412329139, 0.00012179686100677445 -3.4665219241621584e-10, 0.5720740787224103, 0.037960908102466266, 0.03619414602968369, 4781.520930232558, 70.84227485743766, 82.0, 9.643650760992955, 0.0008726753161453195, 0.00010875003466703083 -3.501361340485396e-10, 0.573752343401439, 0.03758318762383477, 0.03593912616629551, 4837.281846581048, 71.34140434292186, 82.33473331019322, 9.38955973423551, 0.0009528707479953561, 0.0001149937949272083 -3.536550901696304e-10, 0.5719640497121008, 0.03720922555792597, 0.03547062473167824, 5015.888233252343, 72.11873381270401, 96.51336341548074, 10.256600214956954, 0.0010073825719030497, 0.00011422588565711758 -3.5720941268389793e-10, 0.5728005627246247, 0.03683898450759836, 0.035169042051076724, 5085.875911141965, 72.52754686187009, 71.03967372440125, 8.902514961986078, 0.0006678780108985284, 8.79904803968042e-05 -3.6079945703247974e-10, 0.5769617572666634, 0.03647242744782127, 0.0350720413347633, 5070.514092329052, 72.49624821230009, 90.61159319680667, 10.344717969313148, 0.0009101472364839245, 0.00010990705762652325 -3.64425582228786e-10, 0.5715158396112747, 0.0361095177219723, 0.03439532716968344, 5204.508932083767, 73.43756069661542, 92.0, 9.848857801796104, 0.0009429414140672893, 0.0001072752712189468 -3.6808815089440196e-10, 0.5741689039788583, 0.035750219038171584, 0.03421115948378256, 5168.169755871805, 73.26219715114135, 89.29774383894481, 9.8574180912949, 0.0009522770700153147, 0.00011102337898862297 -3.7178752929535066e-10, 0.5733690006101942, 0.03539449546565247, 0.03382356448002948, 5358.880944116628, 74.48569698863919, 118.0, 11.180339887498949, 0.001132087581918549, 0.00011555452414208149 -3.7552408737872097e-10, 0.5728423666506173, 0.03504231143116837, 0.03345625512411443, 5136.973273169038, 72.64314645464472, 111.9674881406919, 11.188470909432546, 0.0011114651741212947, 0.00011861919609936893 -3.792981988096627e-10, 0.5705584576087975, 0.034693631715435366, 0.03299129939171578, 5218.977542519958, 73.40484138887994, 133.68737706814764, 12.183657634857255, 0.001369364630723623, 0.0001345232709398948 -3.831102410087548e-10, 0.5748390612539285, 0.034348421449610145, 0.03290807328988706, 5211.23443248872, 73.60582230641594, 126.24609510586602, 11.626207095462734, 0.0011949865303339648, 0.00011872070918704905 -3.8696059518974716e-10, 0.57301323423922, 0.03400664611180309, 0.032477149319660545, 5134.689135716764, 73.21928379608234, 120.42276987157237, 11.586356864604182, 0.0012736894512957151, 0.0001308684990980158 -3.9084964639768436e-10, 0.5740816740172163, 0.03366827152362594, 0.03221394589784299, 5047.3873192178635, 72.55331855485153, 118.0, 11.135528725660043, 0.0012798303754431532, 0.00012911806427506333 -3.947777835474097e-10, 0.5829130102640814, 0.03333326384677395, 0.03238402185000811, 5047.389448108295, 72.3077565868085, 116.66545181071388, 11.55147843090244, 0.001219091748190807, 0.00012834095790293686 -3.9874539946245903e-10, 0.5734533951683128, 0.033001589579641874, 0.03154150586824322, 4861.257399051255, 71.05592001743747, 109.85428670600488, 11.003849000008806, 0.0011308745002155446, 0.00012049536700593618 -4.027528909143429e-10, 0.574423816390964, 0.0326732155539738, 0.031280502989561726, 4779.589031586254, 70.30520561684953, 139.53748698368622, 12.048798730595475, 0.00151037096742173, 0.00014104167293775395 -4.068006586622257e-10, 0.571617846501096, 0.03234810893154621, 0.030817979304927617, 5054.877820201319, 72.25041300780696, 148.46830961471713, 12.417251270785846, 0.0015564771106532183, 0.00014117278848532584 -4.1088910749300176e-10, 0.5752544730134882, 0.032026237200884065, 0.030705439009390607, 5753.4571329399505, 77.0749172368031, 174.59922480620153, 14.053759528680173, 0.001469191308377735, 0.00012929786067982627 -4.150186462617755e-10, 0.5746640055649891, 0.031707568174009604, 0.030368709442419806, 6325.7330787920855, 80.59487479000032, 220.32725905356935, 15.237561146654064, 0.0017178564798984324, 0.0001332170609006542 -4.191896879327481e-10, 0.5740514943557751, 0.03139206998322345, 0.03003448759561688, 6376.506907323846, 80.96316027529606, 234.0, 15.716233645501712, 0.0018864153075920616, 0.00014240119726563924 -4.234026496205144e-10, 0.573699709787592, 0.031079711077917734, 0.02971741528253069, 6453.634154807359, 81.63780797146187, 251.18974892976976, 16.85570795496185, 0.0020375165152231017, 0.0001532169949973361 -4.2765795263177574e-10, 0.5749996200109653, 0.030770460221421055, 0.029488382203408416, 6873.7108642832345, 84.00172102830254, 292.63141270392225, 17.527575338816735, 0.002148601543616395, 0.00014798345093327945 -4.319560225074719e-10, 0.5750440231172188, 0.03046428648787458, 0.029197219907648377, 7251.577577230129, 86.00826950223956, 235.02395001735508, 15.892885989043739, 0.0016503642763768187, 0.00012440925023239483 -4.362972890653358e-10, 0.5736513841124758, 0.03016115925913952, 0.028836696533043733, 7533.131366423697, 87.90233330986719, 264.4900497512438, 16.99077888633064, 0.001725166066098775, 0.00012474734683688303 -4.4068218644287687e-10, 0.5671194806471337, 0.02986104822173515, 0.02822469192251784, 7969.8205484206865, 90.31710497784633, 247.23060279995372, 16.351304259576356, 0.001623746573557252, 0.00011929673190829297 -4.4511115314079515e-10, 0.572220423664031, 0.029563923363807438, 0.028195181201302707, 8130.924887191946, 91.20996218813103, 294.18026148328124, 17.321449926604732, 0.0019010623114440604, 0.00012661746070131576 -4.495846320668332e-10, 0.5733729825044432, 0.02926975497212777, 0.0279708555439751, 8155.658104824712, 91.29996599315125, 328.28110609741987, 18.52674268124196, 0.001961574824274164, 0.00012663379569156544 -4.5410307058006753e-10, 0.5736514820901536, 0.028978513629121537, 0.02770598898544913, 8389.960499826448, 92.4450135355855, 379.6629063982413, 20.208063865512734, 0.002255183461558323, 0.00013829980500958938 -4.5866692053564604e-10, 0.5730601470369867, 0.0286901702099263, 0.027402032563095095, 8454.53453661923, 93.0455021125727, 400.56279069767436, 20.604741268001337, 0.0024579728292377844, 0.00014596617718058808 -4.6327663832997415e-10, 0.5746506347129868, 0.02840469587947927, 0.02720466865347032, 8659.185352308225, 93.80732726014409, 501.4527941686913, 22.790437169561475, 0.0028900596590930644, 0.00015658000338939436 -4.679326849463557e-10, 0.574236420152664, 0.02812206208963371, 0.0269145617746557, 9046.70322804582, 96.22004350422101, 597.3315052643758, 25.01945789119018, 0.00322743737317117, 0.0001645102979335105 -4.726355260010928e-10, 0.5746629200318323, 0.02784224057630403, 0.02666654574390238, 9442.388753904896, 97.94698181207579, 599.2557792433182, 24.967865498117916, 0.003329727961537192, 0.00016726031567660727 -4.773856317900485e-10, 0.5743535538785467, 0.027565203356639315, 0.02638699453346676, 9820.2484901076, 100.1217759052098, 651.1440472058314, 25.96828760478075, 0.0034708770457513214, 0.0001686320846076389 -4.82183477335677e-10, 0.5759424916237067, 0.027290922726225002, 0.02619670768222673, 9673.279416869142, 99.40684374393373, 591.9342242276986, 24.83166513137624, 0.003236193225285449, 0.0001620531211825134 -4.870295424345279e-10, 0.5763216758393273, 0.02701937125631232, 0.025953118904844714, 9908.820779821819, 100.3866699949212, 539.2454356126345, 23.649815642780847, 0.0026927470725172918, 0.00013964985374412457 -4.919243117052266e-10, 0.5747566681558366, 0.02675052179107539, 0.025625106540492215, 10039.580701145434, 101.17024907972977, 484.28229781325933, 22.968656487482807, 0.0024734587477531424, 0.00013519362256487227 -4.968682746369373e-10, 0.5754240988864506, 0.026484347444895535, 0.025399590189374533, 10093.056577577232, 101.25681914035603, 463.3333333333333, 22.262678762261334, 0.002430722444227431, 0.00013341385164323668 -5.018619256383135e-10, 0.5724898773806631, 0.0262208215996727, 0.025018632541203706, 10063.116626171466, 101.0588114605972, 499.96253615642723, 23.332177125450553, 0.002528726378738469, 0.00013558619484851776 -5.069057640869398e-10, 0.5753518877081896, 0.02595991790216352, 0.02489351623000045, 10078.170426935092, 101.35990120883558, 593.4656369316209, 25.05917328873028, 0.0029820878246752816, 0.00014841194499557456 -5.120002943792707e-10, 0.5747830734074706, 0.02570161026134598, 0.024621454593929365, 10116.581858151103, 101.75781833600111, 726.2631030892053, 27.682354282690973, 0.0036441332168153096, 0.00016858470784409028 -5.171460259810723e-10, 0.5749741338894291, 0.0254458728458102, 0.024384567570760473, 10269.115237764665, 102.52748296059616, 838.6434571329398, 29.646125304573168, 0.0040982749375797255, 0.0001800523508631565 -5.223434734783693e-10, 0.5755117693140166, 0.025192680081175287, 0.02416450846122641, 10415.67823672336, 102.99185289757943, 1083.146546338077, 33.4010425359037, 0.005448484713155046, 0.00021756771372107876 -5.275931566289057e-10, 0.5757902269350959, 0.024942006647531746, 0.023935640704693225, 10755.464074973968, 104.88599453717386, 1133.3293995140577, 34.618895029920814, 0.005393097929112829, 0.00021418150353603386 -5.328956004141207e-10, 0.5756725757651175, 0.02469382747690955, 0.023692633185342094, 10448.732384588686, 103.335703488249, 1192.0208376721046, 35.17743983743189, 0.005724924024935895, 0.00022368867578771479 -5.382513350916495e-10, 0.5757338531194964, 0.02444811775077115, 0.023459382371009235, 10337.415133634153, 102.6945457788859, 1151.7091287747312, 34.75464654458072, 0.0056418963617233765, 0.00022216404740035722 -5.436608962483494e-10, 0.5761992286416322, 0.02420485289752965, 0.023244728985389332, 10389.57931273863, 102.97776792716652, 1022.4426240888579, 32.64024555651484, 0.004838490128990716, 0.00019692794605593163 -5.491248248538603e-10, 0.5768607233388297, 0.023964008590091547, 0.023039857402736067, 10566.998727293765, 103.75772952057854, 971.970010413051, 31.863798131704165, 0.0046809673582241165, 0.00019237601889880623 -5.546436673147031e-10, 0.5772154482820445, 0.02372556074342397, 0.022824631382251884, 10187.850399166955, 102.12394039433696, 885.2778202013191, 30.6468616407558, 0.00445196690350773, 0.00018920147756995015 -5.602179755289211e-10, 0.5750979131844203, 0.023489485512146125, 0.022514623615163494, 10118.078676385516, 101.77883509779055, 729.0910563461761, 28.121624329796443, 0.003669487725549219, 0.00016770841255349603 -5.65848306941272e-10, 0.5769916426284344, 0.023255759288144676, 0.022363995334904567, 10096.096147171116, 101.74922040403493, 783.9695823209534, 29.016961131754705, 0.003969784884894351, 0.0001762330780558802 -5.715352245989731e-10, 0.5741310652763524, 0.023024358698212892, 0.022031699952056283, 10078.172162443596, 101.48580673723764, 833.8023371514521, 29.79816403169565, 0.004176936236631505, 0.00018081099842836773 -5.772792972080078e-10, 0.5744003894925092, 0.022795260601713267, 0.02182271092289715, 9965.558486636584, 101.00713595268117, 992.4444290177022, 32.29509136180509, 0.005113188185003203, 0.00020762075014822147 -5.830810991899978e-10, 0.5754097763379805, 0.022568442088263387, 0.021643535450924112, 10018.931273863243, 101.29199734498653, 1256.1749045470322, 36.30575163148736, 0.006609522876246469, 0.00024839670475572157 -5.889412107396459e-10, 0.5765830124527359, 0.02234388047544485, 0.021471866559164516, 9724.022364919589, 99.74794509761844, 1467.2318639361333, 39.17031758790732, 0.007586876050644626, 0.0002747289169914237 -5.948602178827578e-10, 0.5756727221653276, 0.022121553306534956, 0.021224655503751737, 9713.45678583825, 99.87764121244429, 1651.492676154113, 41.73184673939626, 0.008701801274324421, 0.0003055130898612273 -6.008387125348458e-10, 0.5760116605530755, 0.021901438348260978, 0.021025836566670477, 9478.162790697674, 98.34041493354768, 1839.0514057618884, 43.93641472289062, 0.00957336234159374, 0.0003279690238381971 -6.068772925603215e-10, 0.5774393445442857, 0.021683513588576793, 0.020868217933752077, 9299.503297466159, 97.47049144502064, 2004.5599676038412, 46.00431215923147, 0.0108886829769264, 0.0003660480964268284 -6.129765618322844e-10, 0.5772448425394644, 0.021467757234461603, 0.02065361499729097, 9237.611246095104, 97.45967757952462, 1912.7417563346064, 44.64792658279896, 0.01041204738474301, 0.000352000686871265 -6.191371302929103e-10, 0.5782539306466206, 0.021254147709740597, 0.020483850739173654, 9107.975355779243, 96.45749990768739, 1749.6733772995485, 43.23283186168793, 0.009448405476001292, 0.00033139208602521534 -6.253596140144469e-10, 0.5780418027300006, 0.021042663652927264, 0.02027259201094195, 9032.176674765706, 96.26434088090093, 1546.265440240657, 40.30635890963839, 0.008826981541765467, 0.00031522036266125086 -6.316446352608231e-10, 0.5785736295211894, 0.020833283915087195, 0.020089340238594593, 8753.96910794863, 94.6945710381685, 1280.3019784796945, 36.99159355518723, 0.00724739508377976, 0.00027634134545126323 -6.379928225498765e-10, 0.5784642538322843, 0.020625987557723147, 0.019885686459486955, 8629.803193335647, 94.36735032130738, 1154.63908365151, 35.25520583339059, 0.006546905923014836, 0.0002589220701989955 -6.444048107162068e-10, 0.5778823998309648, 0.02042075385068113, 0.01966801641662558, 8623.50196690964, 94.39119306014149, 1059.1502140460489, 34.08734722065474, 0.006054594616777095, 0.0002469046687081223 -6.508812409746611e-10, 0.5751914700037796, 0.02021756227007734, 0.019381644291775326, 8502.547032280456, 93.43154161052648, 1076.6750202475994, 34.29805855363275, 0.006304229245587982, 0.0002549971943677087 -6.574227609844566e-10, 0.5759369333011319, 0.020016392496245726, 0.019213660452751618, 8220.879555709822, 92.21236600527163, 1183.275286358903, 36.01841096574153, 0.007080428145357753, 0.0002801171674086077 -6.640300249139485e-10, 0.5769509144138034, 0.01981722441170597, 0.019055969154295423, 7965.077403679278, 90.52460396074218, 1478.022145088511, 39.77025308189336, 0.009408686001937712, 0.0003465437609886113 -6.707036935060482e-10, 0.577118015105115, 0.019620038099151693, 0.0188718215468921, 7953.752863589031, 90.71519793513636, 1852.3984727525167, 44.22218365534976, 0.011252043164312272, 0.00039301156759626824 -6.774444341443e-10, 0.5757523377405839, 0.01942481383945864, 0.018639830276573382, 7884.450885109338, 90.02526931306933, 2185.020467430291, 48.43392246700388, 0.013546451256573375, 0.00045809975045043894 -6.842529209196194e-10, 0.5769860076463694, 0.019231532109712787, 0.018493900321201934, 7631.8205484206865, 88.57301762714937, 2539.574973967372, 51.62236384025239, 0.016438818960202623, 0.0005349151729436704 -6.91129834697706e-10, 0.57845952239549, 0.019040173581257935, 0.018356639851102453, 7602.125303713988, 88.29503097944543, 2826.359944463728, 54.44773055620663, 0.018085978544159997, 0.0005835138832817013 -6.9807586318713e-10, 0.5787190090655621, 0.018850719117762836, 0.01818213896879982, 7490.732384588685, 87.62572462131948, 2985.9334258937865, 55.84940329729279, 0.019599283910849988, 0.0006225378583616453 -7.05091701008106e-10, 0.5788390487934849, 0.018663149773307488, 0.018004955906578628, 7313.957653592502, 86.7354734027981, 3025.781673030198, 56.33326418679823, 0.019784266537672637, 0.000637599765803666 -7.12178049761956e-10, 0.5793710445187742, 0.018477446790488513, 0.017842184783574907, 7322.607427976398, 86.72019702482001, 2778.1180145782714, 54.18269069401645, 0.018898536342721352, 0.0006149447180153205 -7.193356181012722e-10, 0.5797278412500374, 0.018293591598543355, 0.017675528738411637, 6913.762929538355, 84.29256829911903, 2537.241814184889, 51.94453427214205, 0.017962748709489298, 0.0006003036376468966 -7.265651218007823e-10, 0.580421431331919, 0.018111565811493174, 0.017520588810910905, 6825.765359250261, 83.64947483845754, 2125.5949554552817, 48.15685063522208, 0.015425011419489964, 0.0005362923983674231 -7.338672838289307e-10, 0.5809978409534856, 0.017931351226304192, 0.017363480362905397, 6555.6581048247135, 82.39139454563981, 1806.1103783408535, 44.426165009510704, 0.013501679009336716, 0.0004892789817753431 -7.412428344201763e-10, 0.5789128603672831, 0.017752929821067333, 0.017129020590151575, 6508.61413860928, 81.65421161342717, 1530.495209996529, 41.53726011408903, 0.011739298461060947, 0.00044684571763288207 -7.486925111480169e-10, 0.5807386674846756, 0.017576283753196024, 0.017012065653076256, 6418.234640749739, 81.06025758193876, 1344.1370126113618, 38.71567519399834, 0.010043263945779975, 0.00039670644864411213 -7.562170589987507e-10, 0.5800094819679792, 0.017401395357641836, 0.016821643990631966, 6331.6946662038645, 80.48253037302385, 1491.5842415827838, 40.38141650596367, 0.011715538007768481, 0.0004474738460926948 -7.638172304459743e-10, 0.5782092295766281, 0.017228247145127988, 0.016602574155535753, 6040.4321416174935, 78.99726487912417, 1695.6585907670947, 42.960997947812956, 0.013823063817386627, 0.0005150080860127682 -7.714937855258331e-10, 0.5786747790282124, 0.017056821800400353, 0.016450608636845054, 5826.154113155153, 77.30359556220425, 2084.7837556404024, 47.352045830772695, 0.017140186771994154, 0.0006136483751792636 -7.792474919130274e-10, 0.5786614627533209, 0.016887102180495873, 0.016286546212157547, 5681.8590767094765, 76.46660005054838, 2619.0901307416407, 53.27592507867457, 0.02200482935141593, 0.000761446497074403 -7.870791249975803e-10, 0.5799137256532573, 0.016719071313028255, 0.016159384334783394, 5592.981372208726, 75.82370077650285, 3288.9166955918085, 59.111285277904685, 0.0280781495427234, 0.0009393257194318495 -7.949894679623799e-10, 0.5806119026261397, 0.016552712394490663, 0.016017855009551104, 5472.651162790698, 74.928958602194, 3949.818882332523, 64.44927562645472, 0.03582318165396904, 0.0011632509174815944 -8.029793118614991e-10, 0.5809823647544003, 0.016388008788575336, 0.01586859159375547, 5280.467198889275, 73.79385341009592, 4264.988892745575, 67.55754742816565, 0.03963711057254274, 0.0012934378455997809 -8.11049455699303e-10, 0.5817488155235502, 0.016224944024509913, 0.0157314205065644, 5212.629526784682, 73.37530774167392, 4676.711361795674, 70.26867662241747, 0.04433796721564227, 0.0014298675615017928 -8.192007065103511e-10, 0.5819230535452956, 0.01606350179541032, 0.01557955359192765, 5045.472058313086, 72.00366616837586, 4746.8169038528295, 70.87197515379845, 0.04642998356596498, 0.0015120335575906356 -8.274338794401033e-10, 0.5828795405329966, 0.015903665956650017, 0.015449885061015635, 4754.7705657757715, 70.06039443543303, 4489.029387944002, 68.83838328173289, 0.04406217169171844, 0.0014709774950951919 -8.357497978264359e-10, 0.5827603410368657, 0.015745420524245542, 0.015293026889078716, 4782.566817077404, 69.96022572092372, 4165.335647344673, 66.75825306773184, 0.0407386947201747, 0.0013773931309600082 -8.441492932819778e-10, 0.5832800079720074, 0.015588749673258026, 0.015154358611338396, 4671.7613097304165, 69.40693378532178, 3510.5813953488373, 61.62659498384058, 0.03601271214811765, 0.0012461721977905676 -8.526332057772739e-10, 0.5836768350779183, 0.015433637736210684, 0.015013776107013148, 4553.124609510586, 68.50456474198937, 2980.2614832812683, 56.9855295028291, 0.030862720891363216, 0.0011042185772915402 -8.61202383724784e-10, 0.5838328573754975, 0.015280069201522025, 0.01486835855221142, 4276.980562304755, 66.27231275348133, 2463.1249103320606, 52.309843057176494, 0.028052541829640593, 0.0010348654405636416 -8.698576840637264e-10, 0.5837318443873853, 0.015128028711954645, 0.014717867892698524, 4306.891704269349, 66.6416408541285, 2067.9741987735742, 48.5335955024813, 0.02319045118889747, 0.0008900746001524405 -8.785999723457736e-10, 0.5812790446354094, 0.014977501063079478, 0.014510195473084223, 4089.4057618882334, 64.91990633166199, 2058.309961818813, 48.339321863067, 0.023454419044044238, 0.0009175175769403318 -8.874301228216105e-10, 0.5811843502813222, 0.014828471201755306, 0.014363475208766815, 4069.4831655675116, 64.90390461622852, 2348.393405067685, 51.29014457892239, 0.028693958102837962, 0.001084452277853452 -8.963490185283603e-10, 0.5823633804782643, 0.014680924224623413, 0.014249402852495374, 3858.559875043387, 63.40694066864141, 2953.940645609164, 57.1056858324181, 0.03569674469916246, 0.0013272761796816362 -9.053575513778913e-10, 0.5823723659429287, 0.014534845376617212, 0.01410783541278572, 3749.0746268656712, 62.11219987357015, 3884.3623741756332, 64.73513440029585, 0.050358498737409746, 0.0018100967840493421 -9.144566222460106e-10, 0.5829894576898422, 0.014390220049486696, 0.013982258589560764, 3636.7847159551084, 61.22477804549306, 4853.400555362721, 71.96248637058949, 0.06378234999134863, 0.0022594642372782505 -9.236471410625534e-10, 0.5825645482890798, 0.014247033780337577, 0.013833042478287792, 3505.0437348143, 59.84944665730586, 5815.6122874002085, 78.78943726921511, 0.07667931171601396, 0.0027084571811122147 -9.329300269023777e-10, 0.5840507536331833, 0.014105272250184969, 0.01373033797751234, 3440.715376605345, 59.578261528759874, 6672.941686914266, 84.38636331236069, 0.08682292798771808, 0.0030902328147362117 -9.423062080772758e-10, 0.5850233531028101, 0.013964921282521438, 0.013616354059849903, 3248.927837556404, 58.19610708248198, 7180.322457480042, 87.58811302066027, 0.09834666755351175, 0.0035520561944728055 -9.517766222288061e-10, 0.5851466939851487, 0.013825966841899335, 0.013483710031808285, 3147.901076015272, 57.070998974827205, 7505.209302325581, 89.09989168160878, 0.1084238170675303, 0.003909591873012023 -9.613422164220603e-10, 0.5864702716356646, 0.013688395032527206, 0.013379738825992641, 3020.9616336920053, 56.06178101455682, 7245.986810135369, 87.76111433809754, 0.11070516677219362, 0.004052437756206878 -9.710039472403723e-10, 0.5864067713304041, 0.013552192096880172, 0.013245172864736589, 2975.8930926761536, 55.50771934896844, 6734.225026032627, 84.86880251951739, 0.10366654490113454, 0.0038240942067708267 -9.807627808809788e-10, 0.5876016994479974, 0.013417344414324154, 0.01314010045608965, 2928.9119518685643, 55.29191724349004, 5995.988545643873, 80.75277985528503, 0.09643574549593698, 0.003598557006581921 -9.906196932516417e-10, 0.5879303439383797, 0.013283838499753765, 0.013016629038557019, 2815.860465116279, 54.535672131951586, 5044.974661575841, 73.95961964764393, 0.08337480673768596, 0.003175624918247139 -1.000575670068241e-09, 0.5888044604807128, 0.01315166100224378, 0.012906269822345908, 2737.1324771491386, 53.70993417245492, 4288.861506421381, 68.30913916584382, 0.07305521333852232, 0.0028625418847804886 -1.0106317069533489e-09, 0.5884842435596351, 0.013020798703713992, 0.012770900333382056, 2681.720930232558, 52.94600145211574, 4011.6876084692817, 66.43276594821805, 0.0700793384821887, 0.0027707986566655575 -1.0207888095357943e-09, 0.5848430306061603, 0.01289123851760739, 0.012565596461623509, 2613.076709475877, 52.18163748412006, 4205.483061437, 68.24459094308168, 0.07003003031606726, 0.002833990712376606 -1.0310479935512293e-09, 0.58529372152017, 0.012762967487581448, 0.012450152248924777, 2334.5310193219943, 49.53751046155044, 5133.4321416174935, 74.69650499442731, 0.09739968900806702, 0.003955043957482669 -1.0414102849437037e-09, 0.5868525469203367, 0.01263597278621248, 0.012359097811776971, 2352.1797986810134, 49.3337632997827, 6629.513710517182, 83.78834220512313, 0.1265251267950927, 0.0050474073842564394 -1.0518767199682633e-09, 0.5869790195400427, 0.012510241713712856, 0.012238758629061871, 2352.190211732038, 49.46523818064523, 8566.667129468933, 95.36421168620367, 0.16067607491348276, 0.0064291096243517 -1.0624483452945776e-09, 0.5883001050660626, 0.012385761696660986, 0.012144250084508958, 2185.1649311581627, 47.70454597882853, 10568.875390489413, 105.78811185341473, 0.22115897540535212, 0.008801793853401874 -1.0731262181116082e-09, 0.5882413266125379, 0.012262520286743966, 0.012022210529941235, 2033.2158972578968, 45.93238620779573, 11956.689691079484, 111.91653277742532, 0.26564569857043596, 0.0108464157863694 -1.0839114062333327e-09, 0.5887508956214, 0.012140505159512685, 0.011912896897773865, 2114.6476917736895, 46.66210784165575, 12696.782714335299, 115.40891337182194, 0.2625356395655608, 0.01082006355002557 -1.0948049882055269e-09, 0.5895402340806787, 0.012019704113149374, 0.011810172754506942, 1964.7285664699757, 45.35715416039706, 12812.410274210344, 116.34685094917548, 0.28936747143012165, 0.012119805928851335 -1.1058080534136223e-09, 0.5907083397031141, 0.011900105067247395, 0.011715825425068232, 1906.0735855605697, 44.440144176627406, 12129.090940645608, 113.59977578919067, 0.28449883742831805, 0.011930092733329607 -1.1169217021916486e-09, 0.5912213954427857, 0.011781696061603142, 0.011609324140596502, 1834.7896563693164, 43.83161351844903, 11194.920166608816, 109.14247492791789, 0.27765792018669533, 0.011778647269578474 -1.128147045932268e-09, 0.5901714243864992, 0.011664465255020028, 0.01147339693771463, 1738.7098229781327, 42.66485880700729, 11122.074279763974, 108.62746301113742, 0.2553746600640163, 0.011467187817671334 -1.1394852071979186e-09, 0.5908200442238961, 0.01154840092412431, 0.01137171755248398, 1583.6716417910447, 40.97141340517691, 12259.250607427975, 113.72664328367395, 0.32016589845006, 0.014451802102902932 -1.1509373198330733e-09, 0.5906002786123183, 0.011433491462192727, 0.01125437846633924, 1601.616105518917, 41.06560476214959, 14595.986115931968, 124.1370769917998, 0.39276548062133404, 0.01769943169071254 -1.1625045290776268e-09, 0.590677805507453, 0.011319725377991804, 0.011143857188623523, 1549.9197038065483, 40.47375639364707, 18322.123915307184, 138.88079811368027, 0.5557435356838124, 0.02470433618391924 -1.1741879916814216e-09, 0.592073123668846, 0.011207091294628706, 0.011059034555617112, 1499.1386092791854, 39.89943101883182, 21912.375216938563, 151.60351177681022, 0.6297032364662322, 0.028931376901843508 -1.1859888760199282e-09, 0.5921962603130255, 0.011095577948413498, 0.010951271451188105, 1433.3950017355087, 39.39809635666403, 24532.57480041652, 160.47562182332703, 0.7319702480096729, 0.0341528916902918 -1.1979083622110831e-09, 0.5937592992409984, 0.010985174187732767, 0.010870919657962077, 1470.0565775772304, 39.02490771831203, 26205.42589378688, 165.7726578969316, 0.7740064329256464, 0.0360236191245588 -1.2099476422333047e-09, 0.5940468606968569, 0.010875868971934434, 0.010767963586255829, 1233.638088626634, 36.29988100033755, 26797.818812912185, 167.63108657730467, 0.943870376929199, 0.04547319104447396 -1.2221079200446945e-09, 0.5951203191443921, 0.010767651370223645, 0.010680083361968143, 1225.6549809094065, 36.14975696798184, 26654.75564040264, 167.6163606561047, 0.9508975782670601, 0.046586782780240435 -1.234390411703435e-09, 0.5947627772045, 0.01066051056056968, 0.010567461469437236, 1210.764665046859, 36.05877611557471, 26333.997223186394, 166.18063297753324, 0.9500960449810729, 0.04675230326499962 -1.2467963454893992e-09, 0.5946755042927604, 0.010554435828623714, 0.010460777459826492, 1092.2347333101932, 34.425774955061044, 25453.02637972926, 163.8748718390673, 0.9658927677256465, 0.04969134027856357 -1.2593269620269806e-09, 0.5957667468935169, 0.010449416566647362, 0.010375694191262125, 1041.8833738285316, 33.55766121426623, 24517.974314474144, 161.01871222167273, 0.9934019478866505, 0.051596263546925154 -1.271983514409161e-09, 0.5970652942739055, 0.010345442272451868, 0.010294842727477682, 1052.4859423811176, 34.28577048308645, 23885.182228392918, 158.89561910766162, 0.8837546287170633, 0.04742840566589401 -1.2847672683228207e-09, 0.5976909624653584, 0.010242502548347871, 0.01020308677619031, 1002.0537429133402, 32.47298292309528, 22535.924678930925, 154.54150031971696, 0.9893136615028234, 0.05197314859242925 -1.2976795021753112e-09, 0.5972893052155368, 0.010140587100105606, 0.010094775369180365, 1006.9160013884067, 32.895045198503574, 22030.818118708787, 152.63741966271317, 0.9874399633914812, 0.05194839131489955 -1.3107215072222989e-09, 0.597767243985373, 0.010039685735925454, 0.010002326812806541, 915.0249913224575, 31.33648047687459, 21209.801457827147, 150.02066038440458, 0.9607726132835948, 0.05310768758337278 -1.323894587696895e-09, 0.5987310479728758, 0.009939788365418731, 0.00991876727376405, 864.2040958000694, 30.672396453487792, 20007.500520652553, 146.41550286546655, 0.9681556274569953, 0.05463435942125302 -1.3372000609400796e-09, 0.5995535470392663, 0.009840884998598646, 0.009833562778895676, 855.7427976397084, 30.401598680995242, 19603.360638667127, 144.34901926003124, 0.9764237066967213, 0.05524133391974118 -1.3506392575324419e-09, 0.5994621713552392, 0.009742965744881248, 0.009734232653592129, 813.0013884068032, 30.01663891232989, 18643.43769524471, 141.22167788535617, 0.9276920270599444, 0.054341011135331595 -1.3642135214272401e-09, 0.5985454988752335, 0.009646020812096362, 0.009622638083561294, 746.0937174592154, 28.738585256424585, 18121.90454703228, 139.30217318106605, 1.0998247736110323, 0.06492858091058354 -1.3779242100848e-09, 0.6007870450271008, 0.009550040505508341, 0.009562567233164471, 663.0312391530719, 27.073395812991915, 17142.771954182575, 135.2487635648379, 1.0836453055726352, 0.06669841408020052 -1.3917726946082652e-09, 0.6014839704682703, 0.009455015226846566, 0.009478399307807375, 736.2315168344325, 27.96012308924207, 16645.275945852132, 133.4530780991876, 0.9229295769947128, 0.05625680647060416 -1.4057603598807097e-09, 0.6019721514060951, 0.009360935473345608, 0.009391702979826697, 697.1777160708087, 27.27565712702833, 15435.57827143353, 129.08300325204118, 0.9394610323964148, 0.05765439053756752 -1.419888604703631e-09, 0.6027846808569759, 0.00926779183679491, 0.009310803324374162, 637.0494041420803, 26.60781281655717, 14802.535925026032, 126.52851091739745, 0.8953709521567955, 0.058384328284785604 -1.4341588419368332e-09, 0.6026291009514585, 0.009175575002597946, 0.00921577937817392, 589.5871803771838, 25.654595658001785, 14434.123221103782, 125.05949295793668, 1.0618403514367134, 0.06907238045818391 -1.4485724986397158e-09, 0.6030628024457724, 0.009084275748840753, 0.009130646277368029, 536.6980215203052, 24.625265060056275, 13883.267268309615, 122.97045471043191, 1.0548362473106565, 0.07166035179334895 -1.4631310162139839e-09, 0.6044860397075766, 0.008993884945369703, 0.009061127334624077, 562.9452620617841, 25.40224813562041, 13027.87608469282, 119.10551588951364, 0.944041672046378, 0.06438359750716491 -1.4778358505477926e-09, 0.6035116400070953, 0.008904393552878464, 0.008956506672154998, 570.9232905241234, 24.941465664148055, 12488.575841721626, 117.16835817864128, 0.8738235117721207, 0.05991892986263024 -1.4926884721613379e-09, 0.6038751035443021, 0.008815792622004054, 0.00887272736923343, 495.6689806780052, 23.759709098945315, 11895.08261020479, 114.13114119203054, 0.9526471992543925, 0.06821307375797614 -1.5076903663539138e-09, 0.6071174458855236, 0.008728073292431875, 0.008831605422371049, 512.1279185468009, 24.077351341506677, 11399.529677195418, 111.97977640094028, 0.839161907863425, 0.06098394747792023 -1.5228430333524455e-09, 0.6062002431488687, 0.008641226792009668, 0.00873051965576641, 460.04859423811183, 22.909409146048144, 10709.407150295036, 109.0760874330619, 0.8427963494476273, 0.06412932531918272 -1.5381479884615152e-09, 0.60793586492057, 0.008555244435870269, 0.00866839565746391, 415.4307763508041, 21.683317590135566, 10270.444984380423, 106.26172883790089, 0.9197476958526316, 0.07145712870007224 -1.5536067622148969e-09, 0.607697813423301, 0.008470117625563104, 0.008578782551724968, 378.700867754252, 20.89976242497364, 9841.933356473446, 104.32193749891033, 1.1323555183485408, 0.08796650338524525 -1.5692209005286139e-09, 0.610224516376923, 0.00838583784819432, 0.008528734384247562, 381.0118130278838, 20.70563594639063, 9275.457132939951, 101.78675192312626, 1.0345928584673185, 0.08085263616300195 -1.5849919648555344e-09, 0.6082069055217229, 0.008302396675575472, 0.008415954080711401, 376.0891819969917, 20.607402719865732, 8812.533148212426, 98.83049555571182, 0.8774899713852518, 0.0713945068040351 -1.6009215323415195e-09, 0.6115854365673558, 0.008219785763380692, 0.008378496136751021, 359.6353696633113, 20.122964582167, 8301.6518569941, 97.37221443002053, 0.9800136724017913, 0.07898171695712884 -1.6170111959831424e-09, 0.6142870418288007, 0.00813799685031223, 0.008331769347474796, 315.5737591114197, 18.837528547480975, 7926.045123221103, 94.7942766034896, 1.0013215214560696, 0.08533938878683045 -1.6332625647869927e-09, 0.6105061978735955, 0.0080570217572743, 0.008198097571990526, 314.4033321763277, 19.224897082264768, 7500.663658451926, 92.22438040750436, 1.0393429905793479, 0.08876000198261938 -1.6496772639305801e-09, 0.6101415824253196, 0.007976852386555155, 0.008111677175561863, 319.39592734004395, 19.60757845680979, 7341.605345366192, 91.82807116908101, 0.87215546748432, 0.07731232769092775 -1.6662569349248573e-09, 0.6124746753622068, 0.007897480721017291, 0.008061672044023405, 285.78306143700104, 18.08913869046835, 6720.543908365152, 87.52403150926521, 1.0301112171493358, 0.09092097963475786 -1.6830032357783734e-09, 0.615241977195925, 0.007818898823295728, 0.008017517079587054, 289.177368969108, 18.676793917342774, 6467.818812912182, 86.1462778481335, 0.911639845953131, 0.08338285925599645 -1.6999178411630802e-09, 0.6131865051048203, 0.00774109883500423, 0.007911222474999426, 242.15779243318295, 17.69478790257297, 5975.014925373135, 83.23451852823362, 0.9994863175522324, 0.09819693506223756 -1.7170024425818043e-09, 0.6143414833683057, 0.007664072975949463, 0.007847256329407479, 224.23160939488602, 16.98911385525887, 5710.637625824366, 81.63554602117655, 1.1552198019489681, 0.11418973798730464 -1.7342587485374001e-09, 0.6176547214015712, 0.007587813543352952, 0.007811072908447886, 229.47784334143236, 17.490137053035955, 5321.3391183616795, 79.08009156316348, 0.8807799686982775, 0.09126299899582709 -1.7516884847036046e-09, 0.613727951381251, 0.007512312911080785, 0.0076841875214758186, 220.64977438389442, 16.320239753634304, 5221.342936480389, 77.65730528108486, 1.0537992585936233, 0.1051797824413345 -1.7692933940976106e-09, 0.6172432850443558, 0.007437563528880976, 0.007651302088735933, 188.98507462686567, 15.673915968609911, 4846.055883373828, 75.98647324771805, 1.1541502220233884, 0.12395198055092563 -1.7870752372543705e-09, 0.6210230495259008, 0.007363557921628429, 0.007621555393650287, 194.35260904778434, 15.451461324532369, 4706.9173897952105, 74.94719881493097, 1.003098559435399, 0.10741138946782941 -1.805035792402655e-09, 0.6400451756114651, 0.007290288688577403, 0.007776837233485063, 180.25442554668518, 14.857509025853386, 4409.272821936828, 72.7854874137913, 1.1595293806771054, 0.12317898294506015 -1.8231768556428823e-09, 0.6182270941046347, 0.007217748502621411, 0.007437004420599897, 155.97959041999303, 14.4657730787917, 4076.835820895522, 69.82953658090878, 0.9754798913848411, 0.11783310152259556 -1.84150024112673e-09, 0.6245133436830408, 0.007145930109560502, 0.007437869842405078, 160.6629642485248, 14.154046621071842, 3989.8833738285316, 69.4774540852748, 1.1448780495290698, 0.1292414845652645 -1.860007781238556e-09, 0.6217625338465699, 0.007074826327375822, 0.007331426676323603, 159.35265532801105, 14.75287581022727, 3680.5741062131206, 67.53832907162332, 0.8972305174367942, 0.10918744350285654 -1.878701326778642e-09, 0.6384247559767222, 0.007004430045511386, 0.007452984564889887, 143.6039569593891, 13.29631851608639, 3543.6362374175633, 66.66606154995536, 0.8935742172578933, 0.11053735289550325 -1.897582747148276e-09, 0.6202051057411267, 0.006934734224163017, 0.0071682536487873965, 135.3119055883374, 12.924338601599256, 3349.9555709822976, 64.63779412246137, 0.9709994987681356, 0.12002235318410412 -1.9166539305367008e-09, 0.6265234922281538, 0.00686573189357433, 0.007169225363506401, 124.46407497396737, 12.269308253770163, 3151.5588337382856, 62.1404953815281, 0.993593715994068, 0.12788508120969153 -1.9359167841099335e-09, 0.6227710181281332, 0.006797416153339761, 0.007055379657088446, 108.54714798102513, 11.949854731972174, 2971.707046164526, 61.239635480916064, 1.0523404837781152, 0.1468696716104045 -1.95537323420149e-09, 0.6283167576526549, 0.006729780171714492, 0.0070473769838584836, 123.43570519495543, 12.96885704436362, 2773.463727872267, 59.19912391351525, 1.0545382983684768, 0.1398064692335227 -1.9750252265050225e-09, 0.636107971013765, 0.006662817184931264, 0.007063769194480512, 101.53332176327666, 12.263196039013962, 2606.9906282540783, 57.48051297392814, 1.0937468659281957, 0.1605552164379071 -1.9948747262688914e-09, 0.634749007063621, 0.006596520496523989, 0.006978542857004066, --, 11.02669444036391, --, 55.63627727335115, --, -- -2.0149237184926994e-09, 0.6031970373841334, 0.006530883476658078, 0.006565681325456984, --, 10.2190007533557, --, 49.552719989048896, --, -- -2.035174208125791e-09, 0.6157967206590592, 0.006465899561467454, 0.006636126439430265, --, 7.7534688410909, --, 36.707659602815056, --, -- diff --git a/Tests/Unit/gui2/libtestmachinery/CMakeLists.txt b/Tests/Unit/gui2/libtestmachinery/CMakeLists.txt deleted file mode 100644 index 6bc5f901205..00000000000 --- a/Tests/Unit/gui2/libtestmachinery/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -set(library_name darefltestmachinery) - -file(GLOB source_files "*.cpp") -file(GLOB include_files "*.h") - -find_package(Qt5 COMPONENTS Widgets Core Test REQUIRED) - -if(WIN32) - add_definitions(-DGTEST_LINKED_AS_SHARED_LIBRARY) -endif() - -add_library(${library_name} STATIC ${source_files} ${include_files}) -target_link_libraries(${library_name} gtest gmock Qt5::Core Qt5::Test MVVM::View) -target_include_directories(${library_name} PUBLIC - $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}> $<BUILD_INTERFACE:${DAREFL_AUTOGEN_DIR}>) - -target_compile_features(${library_name} PUBLIC cxx_std_17) diff --git a/Tests/Unit/gui2/libtestmachinery/folderbasedtest.cpp b/Tests/Unit/gui2/libtestmachinery/folderbasedtest.cpp deleted file mode 100644 index 8cd4db462b4..00000000000 --- a/Tests/Unit/gui2/libtestmachinery/folderbasedtest.cpp +++ /dev/null @@ -1,42 +0,0 @@ -// ************************************************************************** // -// -// Reflectometry simulation software prototype -// -//! @license GNU General Public License v3 or higher (see COPYING) -//! @authors see AUTHORS -// -// ************************************************************************** // - -#include "folderbasedtest.h" -#include "mvvm/utils/fileutils.h" -#include "test_utils.h" - -FolderBasedTest::FolderBasedTest(const std::string& test_dir) : m_test_dir(test_dir) -{ - TestUtils::CreateTestDirectory(m_test_dir); -} - -std::string FolderBasedTest::testDir() const -{ - return m_test_dir; -} - -//! Return full path to the test folder. Located in CMAKE_BINARY_DIR/test_output/<m_test_dir>. - -std::string FolderBasedTest::testPath() const -{ - return TestUtils::TestDirectoryPath(m_test_dir); -} - -//! Creates an empty directory in main test folder. -//! Remove recursively previous one with the same name, if exist. - -std::string FolderBasedTest::createEmptyDir(const std::string& subdir) const -{ - auto path = ModelView::Utils::join(testPath(), subdir); - ModelView::Utils::remove_all(path); - ModelView::Utils::create_directory(path); - return path; -} - -FolderBasedTest::~FolderBasedTest() = default; diff --git a/Tests/Unit/gui2/libtestmachinery/folderbasedtest.h b/Tests/Unit/gui2/libtestmachinery/folderbasedtest.h deleted file mode 100644 index c8a127cbe9f..00000000000 --- a/Tests/Unit/gui2/libtestmachinery/folderbasedtest.h +++ /dev/null @@ -1,33 +0,0 @@ -// ************************************************************************** // -// -// Reflectometry simulation software prototype -// -//! @license GNU General Public License v3 or higher (see COPYING) -//! @authors see AUTHORS -// -// ************************************************************************** // - -#ifndef BORNAGAIN_TESTS_UNITTESTS_GUI2_LIBTESTMACHINERY_FOLDERBASEDTEST_H -#define BORNAGAIN_TESTS_UNITTESTS_GUI2_LIBTESTMACHINERY_FOLDERBASEDTEST_H - -#include <gtest/gtest.h> -#include <string> - -//! Convenience class which creates a directory on disk for test content. - -class FolderBasedTest : public ::testing::Test { -public: - FolderBasedTest(const std::string& test_dir); - ~FolderBasedTest(); - - std::string testDir() const; - - std::string testPath() const; - - std::string createEmptyDir(const std::string& subdir) const; - -protected: - std::string m_test_dir; //! main directory of given test -}; - -#endif // BORNAGAIN_TESTS_UNITTESTS_GUI2_LIBTESTMACHINERY_FOLDERBASEDTEST_H diff --git a/Tests/Unit/gui2/libtestmachinery/google_test.h b/Tests/Unit/gui2/libtestmachinery/google_test.h deleted file mode 100644 index 8c07c25edc9..00000000000 --- a/Tests/Unit/gui2/libtestmachinery/google_test.h +++ /dev/null @@ -1,15 +0,0 @@ -// ************************************************************************** // -// -// Reflectometry simulation software prototype -// -//! @license GNU General Public License v3 or higher (see COPYING) -//! @authors see AUTHORS -// -// ************************************************************************** // - -#ifndef BORNAGAIN_TESTS_UNITTESTS_GUI2_LIBTESTMACHINERY_GOOGLE_TEST_H -#define BORNAGAIN_TESTS_UNITTESTS_GUI2_LIBTESTMACHINERY_GOOGLE_TEST_H - -#include <gtest/gtest.h> - -#endif // BORNAGAIN_TESTS_UNITTESTS_GUI2_LIBTESTMACHINERY_GOOGLE_TEST_H diff --git a/Tests/Unit/gui2/libtestmachinery/test_utils.cpp b/Tests/Unit/gui2/libtestmachinery/test_utils.cpp deleted file mode 100644 index cacc9445978..00000000000 --- a/Tests/Unit/gui2/libtestmachinery/test_utils.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// ************************************************************************** // -// -// Reflectometry simulation software prototype -// -//! @license GNU General Public License v3 or higher (see COPYING) -//! @authors see AUTHORS -// -// ************************************************************************** // - -#include "test_utils.h" -#include "mvvm/utils/fileutils.h" -#include "testconfig.h" // this file is auto generated by the build system in build directory -#include <QFile> -#include <QString> -#include <QTextStream> -#include <stdexcept> -#include <string> - -using namespace ModelView; - -std::string TestUtils::TestOutputDir() -{ - return TestConfig::TestOutputDir(); // defined in auto-generated testconfig.h -} - -std::string TestUtils::CreateTestDirectory(const std::string& test_sub_dir) -{ - std::string result = TestDirectoryPath(test_sub_dir); - Utils::create_directory(result); - return result; -} - -std::string TestUtils::TestDirectoryPath(const std::string& test_sub_dir) -{ - return TestOutputDir() + std::string("/") + test_sub_dir; -} - -std::string TestUtils::TestFileName(const std::string& test_sub_dir, const std::string& file_name) -{ - - return TestDirectoryPath(test_sub_dir) + std::string("/") + file_name; -} - -std::string TestUtils::CreateTestFile(const std::string& dirname, const std::string& fileName, - const std::string& content) -{ - std::string filename = dirname.empty() ? fileName : dirname + "/" + fileName; - - QFile file(QString::fromStdString(filename)); - if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) - throw std::runtime_error("TestFileUtils::createTestFile() -> Error. " - "Can't create file"); - - QTextStream out(&file); - if (content.empty()) - out << "Test file " << 42 << "\n"; - else - out << QString::fromStdString(content); - file.close(); - - return filename; -} - -std::string TestUtils::CreateEmptyFile(const std::string& dirname, const std::string& fileName) -{ - std::string filename = dirname.empty() ? fileName : dirname + "/" + fileName; - - QFile file(QString::fromStdString(filename)); - if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) - throw std::runtime_error("TestFileUtils::createTestFile() -> Error. " - "Can't create file"); - return filename; -} diff --git a/Tests/Unit/gui2/libtestmachinery/test_utils.h b/Tests/Unit/gui2/libtestmachinery/test_utils.h deleted file mode 100644 index 805ad3d2f0a..00000000000 --- a/Tests/Unit/gui2/libtestmachinery/test_utils.h +++ /dev/null @@ -1,63 +0,0 @@ -// ************************************************************************** // -// -// Reflectometry simulation software prototype -// -//! @license GNU General Public License v3 or higher (see COPYING) -//! @authors see AUTHORS -// -// ************************************************************************** // - -#ifndef BORNAGAIN_TESTS_UNITTESTS_GUI2_LIBTESTMACHINERY_TEST_UTILS_H -#define BORNAGAIN_TESTS_UNITTESTS_GUI2_LIBTESTMACHINERY_TEST_UTILS_H - -#include "mvvm/model/customvariants.h" -#include <QString> -#include <memory> - -//! @file Tests/Unit/gui2/libtestmachinery/test_utils.h -//! @brief Collection of utility functions for various unit tests. - -class QJsonObject; -class QJsonArray; - -namespace ModelView { -class SessionModel; -} - -//! Various common utils for unit tests. - -namespace TestUtils { - -//! Returns full path to the main test folder, as defined by CMake at compile time. -//! Should point to CMAKE_BINARY_DIR/test_output -std::string TestOutputDir(); - -//! Creates test directory in main test folder and returns full path. -//! If directory exists, will do nothing. -std::string CreateTestDirectory(const std::string& test_sub_dir); - -//! Returns full path to the main test folder in CMAKE_BINARY_DIR. -std::string TestDirectoryPath(const std::string& test_sub_dir); - -//! Returns full path to the file in test directory. -std::string TestFileName(const std::string& test_sub_dir, const std::string& file_name); - -//! Helper function to create test file in a given directory (directory should exist). -//! Returns full path of the file. -std::string CreateTestFile(const std::string& dirname, const std::string& fileName, - const std::string& content = {}); - -//! Helper function to create empty file in a given directory (directory should exist). -//! Returns full path of the file. -std::string CreateEmptyFile(const std::string& dirname, const std::string& fileName); - -template <typename T = std::string, typename... Args> std::vector<T> toStringVector(Args&&... args) -{ - std::vector<T> v; - (v.push_back(std::string(args)), ...); - return v; -} - -} // namespace TestUtils - -#endif // BORNAGAIN_TESTS_UNITTESTS_GUI2_LIBTESTMACHINERY_TEST_UTILS_H diff --git a/Tests/Unit/gui2/libtestmachinery/widgetbasedtest.cpp b/Tests/Unit/gui2/libtestmachinery/widgetbasedtest.cpp deleted file mode 100644 index 54c52b64ae8..00000000000 --- a/Tests/Unit/gui2/libtestmachinery/widgetbasedtest.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// ************************************************************************** // -// -// Reflectometry simulation software prototype -// -//! @license GNU General Public License v3 or higher (see COPYING) -//! @authors see AUTHORS -// -// ************************************************************************** // - -#include "widgetbasedtest.h" -#include <QApplication> - -QApplication* WidgetBasedTest::m_app = nullptr; - -namespace { -// faking argc and argv -char progname[] = "testview"; -char* argv[] = {&progname[0], nullptr}; -int argc = 1; -} // namespace - -WidgetBasedTest::WidgetBasedTest() {} - -void WidgetBasedTest::SetUpTestSuite() -{ - m_app = new QApplication(argc, argv); -} - -void WidgetBasedTest::TearDownTestSuite() -{ - delete m_app; - m_app = 0; -} diff --git a/Tests/Unit/gui2/libtestmachinery/widgetbasedtest.h b/Tests/Unit/gui2/libtestmachinery/widgetbasedtest.h deleted file mode 100644 index 2aa33ef5c72..00000000000 --- a/Tests/Unit/gui2/libtestmachinery/widgetbasedtest.h +++ /dev/null @@ -1,31 +0,0 @@ -// ************************************************************************** // -// -// Reflectometry simulation software prototype -// -//! @license GNU General Public License v3 or higher (see COPYING) -//! @authors see AUTHORS -// -// ************************************************************************** // - -#ifndef BORNAGAIN_TESTS_UNITTESTS_GUI2_LIBTESTMACHINERY_WIDGETBASEDTEST_H -#define BORNAGAIN_TESTS_UNITTESTS_GUI2_LIBTESTMACHINERY_WIDGETBASEDTEST_H - -#include <gtest/gtest.h> - -class QApplication; - -//! Convenience class to setup QApplication for tests involving QWidget creation. - -class WidgetBasedTest : public ::testing::Test { -public: - WidgetBasedTest(); - - static void SetUpTestSuite(); - - static void TearDownTestSuite(); - -protected: - static QApplication* m_app; -}; - -#endif // BORNAGAIN_TESTS_UNITTESTS_GUI2_LIBTESTMACHINERY_WIDGETBASEDTEST_H diff --git a/Tests/Unit/gui2/testdareflcore/CMakeLists.txt b/Tests/Unit/gui2/testdareflcore/CMakeLists.txt deleted file mode 100644 index 2cb782766f3..00000000000 --- a/Tests/Unit/gui2/testdareflcore/CMakeLists.txt +++ /dev/null @@ -1,26 +0,0 @@ -set(executable_name testdareflcore) - -include(GoogleTest) - -file(GLOB source_files "*.cpp") -file(GLOB include_files "*.h") - -find_package(Qt5Core REQUIRED) -find_package(Qt5Test REQUIRED) - -if(WIN32) - add_definitions(-DGTEST_LINKED_AS_SHARED_LIBRARY) -endif() - -# necessary for Qt creator and clang code model -include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) - -set(CMAKE_AUTOMOC ON) -add_executable(${executable_name} ${source_files} ${include_files}) -target_link_libraries(${executable_name} gtest gmock Qt5::Core Qt5::Test dareflcore - darefltestmachinery) - -# to make clang code model in Qt creator happy -target_compile_features(${executable_name} PUBLIC cxx_std_17) - -gtest_discover_tests(${executable_name}) diff --git a/Tests/Unit/gui2/testdareflcore/TestAll.cpp b/Tests/Unit/gui2/testdareflcore/TestAll.cpp deleted file mode 100644 index 14e42475c91..00000000000 --- a/Tests/Unit/gui2/testdareflcore/TestAll.cpp +++ /dev/null @@ -1,26 +0,0 @@ -// ************************************************************************** // -// -// Reflectometry simulation software prototype -// -//! @license GNU General Public License v3 or higher (see COPYING) -//! @authors see AUTHORS -// -// ************************************************************************** // - -#include "google_test.h" -#include "mvvm/model/comparators.h" -#include <QApplication> -#include <QStandardItem> -#include <string> - -int main(int argc, char** argv) -{ - QApplication app(argc, argv); - Q_UNUSED(app) - - ModelView::Comparators::registerComparators(); - qRegisterMetaType<QStandardItem*>("QStandardItem*"); - - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/Tests/Unit/gui2/testdareflcore/applicationmodels.test.cpp b/Tests/Unit/gui2/testdareflcore/applicationmodels.test.cpp deleted file mode 100644 index b29320d22e5..00000000000 --- a/Tests/Unit/gui2/testdareflcore/applicationmodels.test.cpp +++ /dev/null @@ -1,51 +0,0 @@ -// ************************************************************************** // -// -// Reflectometry simulation software prototype -// -//! @license GNU General Public License v3 or higher (see COPYING) -//! @authors see AUTHORS -// -// ************************************************************************** // - -#include "folderbasedtest.h" -#include "google_test.h" -#include "gui2/model/applicationmodels.h" -#include "mvvm/factories/modeldocumentfactory.h" -#include "mvvm/project/project.h" -#include "mvvm/project/project_types.h" - -using namespace ModelView; - -//! Tests of ApplicationModels - -class ApplicationModelsTest : public FolderBasedTest { -public: - ApplicationModelsTest() : FolderBasedTest("test_ApplicationModelsTest") {} - - ~ApplicationModelsTest(); -}; - -ApplicationModelsTest::~ApplicationModelsTest() = default; - -//! Testing that nothing is crashing on project save and load. - -TEST_F(ApplicationModelsTest, saveLoad) -{ - auto project_dir = createEmptyDir("Untitled1"); - - gui2::ApplicationModels models, loadedModels; - - { - ProjectContext context1; - context1.m_models_callback = [&models]() { return models.persistent_models(); }; - Project project1(context1); - project1.save(project_dir); - } - - { - ProjectContext context2; - context2.m_models_callback = [&loadedModels]() { return loadedModels.persistent_models(); }; - Project project2(context2); - project2.load(project_dir); - } -} diff --git a/Tests/Unit/gui2/testdareflcore/datahandler.test.cpp b/Tests/Unit/gui2/testdareflcore/datahandler.test.cpp deleted file mode 100644 index 98d5e20d9c6..00000000000 --- a/Tests/Unit/gui2/testdareflcore/datahandler.test.cpp +++ /dev/null @@ -1,58 +0,0 @@ -// ************************************************************************** // -// -// Reflectometry simulation software prototype -// -//! @license GNU General Public License v3 or higher (see COPYING) -//! @authors see AUTHORS -// -// ************************************************************************** // - -#include "folderbasedtest.h" -#include "gui2/dataloader/datahandler.h" -#include "mvvm/utils/fileutils.h" -#include "test_utils.h" - -using TestUtils::toStringVector; -using namespace gui2; - -class DataHandlerTest : public FolderBasedTest { -public: - DataHandlerTest() : FolderBasedTest("test_DataHandlerTest") {} - ~DataHandlerTest(); -}; - -DataHandlerTest::~DataHandlerTest() = default; - -//! Testing function TrimWhitespace - -TEST_F(DataHandlerTest, updateRawData) -{ - auto file_name1 = TestUtils::CreateTestFile(testPath(), "a.txt", {"aaa bbb\nccc ddd\n"}); - - DataHandler handler; - handler.updateRawData({file_name1}); - - // the data was loaded - EXPECT_EQ(handler.textData(file_name1), toStringVector("aaa bbb", "ccc ddd")); - - // removing file physically from disk - ModelView::Utils::remove(file_name1); - - // Updating to the same data will not change anything. Handler knows nothing about - // the data removed from disk and still keeps the buffer. - - handler.updateRawData({file_name1}); - EXPECT_EQ(handler.textData(file_name1), toStringVector("aaa bbb", "ccc ddd")); - - // adding second file - auto file_name2 = TestUtils::CreateTestFile(testPath(), "b.txt", {"111 222\n333 444\n"}); - handler.updateRawData({file_name1, file_name2}); - - EXPECT_EQ(handler.textData(file_name1), toStringVector("aaa bbb", "ccc ddd")); - EXPECT_EQ(handler.textData(file_name2), toStringVector("111 222", "333 444")); - - // remove file from list - handler.updateRawData({}); - EXPECT_TRUE(handler.textData(file_name1).empty()); - EXPECT_TRUE(handler.textData(file_name2).empty()); -} diff --git a/Tests/Unit/gui2/testdareflcore/dataloader_utils.test.cpp b/Tests/Unit/gui2/testdareflcore/dataloader_utils.test.cpp deleted file mode 100644 index d88b58343bb..00000000000 --- a/Tests/Unit/gui2/testdareflcore/dataloader_utils.test.cpp +++ /dev/null @@ -1,280 +0,0 @@ -// ************************************************************************** // -// -// Reflectometry simulation software prototype -// -//! @license GNU General Public License v3 or higher (see COPYING) -//! @authors see AUTHORS -// -// ************************************************************************** // - -#include "folderbasedtest.h" -#include "gui2/dataloader/dataloader_constants.h" -#include "gui2/dataloader/dataloader_utils.h" -#include "gui2/importdataview/graphimportdata.h" -#include "test_utils.h" -#include <initializer_list> -#include <vector> - -using namespace gui2; -using TestUtils::toStringVector; - -//! Tests of ParseUtils. - -class ParseUtilsTest : public FolderBasedTest { -public: - ParseUtilsTest() : FolderBasedTest("test_ParseUtilsTest") {} - ~ParseUtilsTest(); - - std::vector<std::pair<int, int>> - toPairVector(std::initializer_list<std::pair<int, int>> list = {}) - { - return std::vector<std::pair<int, int>>(list.begin(), list.end()); - } - - std::vector<double> toVector(std::initializer_list<double> args) - { - return std::vector<double>(args.begin(), args.end()); - }; -}; - -ParseUtilsTest::~ParseUtilsTest() = default; - -TEST_F(ParseUtilsTest, LoadASCIIFile) -{ - std::string content = {"abc abc\n 123 456\n"}; - auto file_name = TestUtils::CreateTestFile(testPath(), "a.txt", content); - - auto raw_data = Utils::LoadASCIIFile(file_name); - EXPECT_EQ(raw_data.size(), 2u); - EXPECT_EQ(raw_data[0], std::string("abc abc")); - EXPECT_EQ(raw_data[1], std::string(" 123 456")); -} - -//! Testing local utility function. - -TEST_F(ParseUtilsTest, toStringVector) -{ - std::vector<std::string> expected; - - expected = {}; - EXPECT_EQ(toStringVector(), expected); - - expected = {"a"}; - EXPECT_EQ(toStringVector("a"), expected); - - expected = {"a", "b"}; - EXPECT_EQ(toStringVector("a", "b"), expected); - - expected = {"aaa", "bbb", ""}; - EXPECT_EQ(toStringVector("aaa", "bbb", ""), expected); -} - -//! Testing local utility function. - -TEST_F(ParseUtilsTest, toPairVector) -{ - std::vector<std::pair<int, int>> expected; - - expected = {}; - EXPECT_EQ(toPairVector(), expected); - expected = {{1, 2}}; - EXPECT_EQ(toPairVector({{1, 2}}), expected); - expected = {{1, 2}, {3, 4}}; - EXPECT_EQ(toPairVector({{1, 2}, {3, 4}}), expected); -} - -//! Checking method to expand line numbers -//! "1, 2-4" -> {0, 0}, {1, 3} - -TEST_F(ParseUtilsTest, ExpandLineNumberPattern) -{ - using Utils::ExpandLineNumberPattern; - EXPECT_EQ(ExpandLineNumberPattern(""), toPairVector()); - EXPECT_EQ(ExpandLineNumberPattern(" "), toPairVector()); - EXPECT_EQ(ExpandLineNumberPattern("aaa"), toPairVector()); - EXPECT_EQ(ExpandLineNumberPattern("1"), toPairVector({{1, 1}})); - EXPECT_EQ(ExpandLineNumberPattern(" 1"), toPairVector({{1, 1}})); - EXPECT_EQ(ExpandLineNumberPattern(" 1 "), toPairVector({{1, 1}})); - EXPECT_EQ(ExpandLineNumberPattern("42"), toPairVector({{42, 42}})); - - EXPECT_EQ(ExpandLineNumberPattern("1,1"), toPairVector({{1, 1}, {1, 1}})); - EXPECT_EQ(ExpandLineNumberPattern("1,2"), toPairVector({{1, 1}, {2, 2}})); - EXPECT_EQ(ExpandLineNumberPattern(" 1 , 2 "), toPairVector({{1, 1}, {2, 2}})); - - EXPECT_EQ(ExpandLineNumberPattern("1-1"), toPairVector({{1, 1}})); - EXPECT_EQ(ExpandLineNumberPattern("1-5"), toPairVector({{1, 5}})); - - EXPECT_EQ(ExpandLineNumberPattern("1,2-3"), toPairVector({{1, 1}, {2, 3}})); - EXPECT_EQ(ExpandLineNumberPattern("1, 2-3, 42"), toPairVector({{1, 1}, {2, 3}, {42, 42}})); - EXPECT_EQ(ExpandLineNumberPattern("42, 2-3, 1"), toPairVector({{42, 42}, {2, 3}, {1, 1}})); - - // more wrong patterns - EXPECT_EQ(ExpandLineNumberPattern("1,b"), toPairVector({{1, 1}})); - EXPECT_EQ(ExpandLineNumberPattern("a,1,b"), toPairVector({{1, 1}})); - EXPECT_EQ(ExpandLineNumberPattern("a-2"), toPairVector()); - EXPECT_EQ(ExpandLineNumberPattern("6-5"), toPairVector()); // wrong order -} - -TEST_F(ParseUtilsTest, CreateLineNumberPatternValidator) -{ - using Utils::CreateLineNumberPatternValidator; - - auto is_accepted = CreateLineNumberPatternValidator("1"); - EXPECT_FALSE(is_accepted(0)); - EXPECT_TRUE(is_accepted(1)); - EXPECT_FALSE(is_accepted(2)); - - is_accepted = CreateLineNumberPatternValidator(""); - EXPECT_FALSE(is_accepted(0)); - EXPECT_FALSE(is_accepted(1)); - EXPECT_FALSE(is_accepted(2)); - - is_accepted = CreateLineNumberPatternValidator("1, 2"); - EXPECT_FALSE(is_accepted(0)); - EXPECT_TRUE(is_accepted(1)); - EXPECT_TRUE(is_accepted(2)); - EXPECT_FALSE(is_accepted(3)); - - is_accepted = CreateLineNumberPatternValidator("42, 44-46"); - EXPECT_FALSE(is_accepted(0)); - EXPECT_TRUE(is_accepted(42)); - EXPECT_FALSE(is_accepted(43)); - EXPECT_TRUE(is_accepted(44)); - EXPECT_TRUE(is_accepted(45)); - EXPECT_TRUE(is_accepted(46)); - EXPECT_FALSE(is_accepted(47)); -} - -TEST_F(ParseUtilsTest, CreateLinePrefixValidator) -{ - using Utils::CreateLinePrefixValidator; - - auto is_accepted = CreateLinePrefixValidator(""); - EXPECT_FALSE(is_accepted("")); - EXPECT_FALSE(is_accepted(" ")); - EXPECT_FALSE(is_accepted(" ")); - EXPECT_TRUE(is_accepted("abc")); - EXPECT_TRUE(is_accepted(" abc ")); - - is_accepted = CreateLinePrefixValidator("#"); - EXPECT_FALSE(is_accepted("")); - EXPECT_FALSE(is_accepted(" ")); - EXPECT_FALSE(is_accepted("# abc")); - EXPECT_TRUE(is_accepted("42 ")); -} - -TEST_F(ParseUtilsTest, CreateSeparatorSplitter) -{ - using Utils::CreateSeparatorBasedSplitter; - - auto parse = CreateSeparatorBasedSplitter(","); - EXPECT_EQ(parse("a"), toStringVector("a")); - EXPECT_EQ(parse("a,b"), toStringVector("a", "b")); - EXPECT_EQ(parse("a, b"), toStringVector("a", " b")); // shouldn't we trim white spaces? - EXPECT_EQ(parse(" a, b "), toStringVector("a", " b")); - - EXPECT_EQ(parse("1.0,2.0,3.0"), toStringVector("1.0", "2.0", "3.0")); - EXPECT_EQ(parse("1.0, 2.0, 3.0"), toStringVector("1.0", " 2.0", " 3.0")); - EXPECT_EQ(parse("1.0, 2.0, 3.0,"), toStringVector("1.0", " 2.0", " 3.0", "")); - - parse = CreateSeparatorBasedSplitter(" "); - EXPECT_EQ(parse("a"), toStringVector("a")); - EXPECT_EQ(parse("a b"), toStringVector("a", "b")); - EXPECT_EQ(parse(" a b "), toStringVector("a", "b")); - EXPECT_EQ(parse(" a b "), toStringVector("a", "b")); -} - -TEST_F(ParseUtilsTest, AddHtmlColorTag) -{ - EXPECT_EQ(Utils::AddHtmlColorTag("abc", "x"), "<font color=\"x\">abc</font>"); -} - -TEST_F(ParseUtilsTest, AddHtmlDivTag) -{ - EXPECT_EQ(Utils::AddHtmlDivTag("abc"), "<div>abc</div>"); -} - -TEST_F(ParseUtilsTest, AddHtmlColorTagToParts) -{ - auto parse = Utils::CreateSeparatorBasedSplitter(","); - std::string line("a,b"); - EXPECT_EQ(Utils::AddHtmlColorTagToParts(line, parse(line), "A", "B"), - "<div><font color=\"A\">a</font><span style=\"background-color:B\">,</span><font " - "color=\"A\">b</font></div>"); - - parse = Utils::CreateSeparatorBasedSplitter(" | "); - line = "abc | efg"; - EXPECT_EQ(Utils::AddHtmlColorTagToParts(line, parse(line), "A", "B"), - "<div><font color=\"A\">abc</font><span style=\"background-color:B\"> | </span><font " - "color=\"A\">efg</font></div>"); -} - -TEST_F(ParseUtilsTest, ExtractTwoColumns) -{ - using Utils::ExtractTwoColumns; - - auto result = ExtractTwoColumns({{}}, 0, 0); - EXPECT_EQ(result.first.size(), 0); - EXPECT_EQ(result.second.size(), 0); - - // normal parsing - result = ExtractTwoColumns({{"1.0", "2.0"}, {"3.0", "4.0"}}, 0, 1); - EXPECT_EQ(result.first, toVector({1.0, 3.0})); - EXPECT_EQ(result.second, toVector({2.0, 4.0})); - - result = ExtractTwoColumns({{" 1.0 ", " 2.0 "}, {" 3.0 ", " 4.0 "}}, 0, 1); - EXPECT_EQ(result.first, toVector({1.0, 3.0})); - EXPECT_EQ(result.second, toVector({2.0, 4.0})); - - // partial parsing (nan in input) - result = ExtractTwoColumns({{"---", "2.0"}, {"3.0", "4.0"}}, 0, 1); - EXPECT_EQ(result.first, toVector({3.0})); - EXPECT_EQ(result.second, toVector({4.0})); - - // partial parsing (different length of columns) - result = ExtractTwoColumns({{"1.0", "2.0", "3.0"}, {"4.0", "5.0", "6.0", "7.0"}}, 0, 3); - EXPECT_EQ(result.first, toVector({4.0})); - EXPECT_EQ(result.second, toVector({7.0})); -} - -//! Checks method CreateGraphInfoPairs. - -TEST_F(ParseUtilsTest, CreateGraphInfoPairs) -{ - ColumnInfo col0{0, GUI::Constants::AxisType, "", 0}; - ColumnInfo col1{1, GUI::Constants::IntensityType, "", 0}; - ColumnInfo col2{2, GUI::Constants::IgnoreType, "", 0}; - ColumnInfo col3{3, GUI::Constants::IntensityType, "", 0}; - - std::vector<ColumnInfo> infos = {col0, col1, col2, col3}; - - auto info_pairs = Utils::CreateGraphInfoPairs(infos); - - // if we have one Axis and two Intesity columns, we have to get - // two pairs of info - - ASSERT_EQ(info_pairs.size(), 2); - EXPECT_EQ(info_pairs[0].first.column, 0); - EXPECT_EQ(info_pairs[0].second.column, 1); - EXPECT_EQ(info_pairs[1].first.column, 0); - EXPECT_EQ(info_pairs[1].second.column, 3); -} - -TEST_F(ParseUtilsTest, CreateData) -{ - ColumnInfo col0{0, GUI::Constants::AxisType, "units0", 1.0}; - ColumnInfo col2{2, GUI::Constants::IntensityType, "units2", 2.0}; - - std::vector<std::vector<std::string>> text_data = {{"1.0", "2.0", "3.0"}, - {"4.0", "5.0", "6.0"}, - {"7.0", "8.0", "9.0"}, - {"10.0", "11.0", "12.0"}}; - - auto data = Utils::CreateData(text_data, col0, col2); - - EXPECT_EQ(data.graph_description, ""); - EXPECT_EQ(data.bin_centers, toVector({1.0, 4.0, 7.0, 10.0})); - EXPECT_EQ(data.axis_units, "units0"); - EXPECT_EQ(data.bin_values, toVector({6.0, 12.0, 18.0, 24.0})); - EXPECT_EQ(data.signal_units, "units2"); -} diff --git a/Tests/Unit/gui2/testdareflcore/dataselectionmodel.test.cpp b/Tests/Unit/gui2/testdareflcore/dataselectionmodel.test.cpp deleted file mode 100644 index 3c6b2a87495..00000000000 --- a/Tests/Unit/gui2/testdareflcore/dataselectionmodel.test.cpp +++ /dev/null @@ -1,166 +0,0 @@ -// ************************************************************************** // -// -// Reflectometry simulation software prototype -// -//! @license GNU General Public License v3 or higher (see COPYING) -//! @authors see AUTHORS -// -// ************************************************************************** // - -#include "google_test.h" -#include "gui2/importdataview/dataselectionmodel.h" -#include "gui2/importdataview/dataviewmodel.h" -#include "gui2/model/experimentaldataitems.h" -#include "gui2/model/experimentaldatamodel.h" -#include "mvvm/model/modelutils.h" -#include "mvvm/standarditems/graphitem.h" -#include "test_utils.h" -#include <QDebug> - -using namespace gui2; -using namespace ModelView; - -//! Tests of DataSelectionModel. - -class DataSelectionModelTest : public ::testing::Test { -public: - ~DataSelectionModelTest(); - - struct TestData { - ExperimentalDataModel data_model; - DataViewModel view_model{&data_model}; - DataSelectionModel selection_model{&view_model}; - CanvasContainerItem* container{nullptr}; - CanvasItem* canvas0{nullptr}; - GraphItem* graph_c0_a{nullptr}; - GraphItem* graph_c0_b{nullptr}; - CanvasItem* canvas1{nullptr}; - GraphItem* graph_c1_a{nullptr}; - TestData() - { - container = Utils::TopItem<CanvasContainerItem>(&data_model); - canvas0 = data_model.insertItem<CanvasItem>(container); - graph_c0_a = data_model.insertItem<GraphItem>(canvas0); - graph_c0_b = data_model.insertItem<GraphItem>(canvas0); - canvas1 = data_model.insertItem<CanvasItem>(container); - graph_c1_a = data_model.insertItem<GraphItem>(canvas1); - view_model.setRootSessionItem(container); - } - }; -}; - -DataSelectionModelTest::~DataSelectionModelTest() = default; - -//! Initial state of selection model. - -TEST_F(DataSelectionModelTest, initialState) -{ - TestData test_data; - // checking layout of container with test data - EXPECT_EQ(test_data.container->size(), 2); - - // checking that no selection exists - EXPECT_FALSE(test_data.selection_model.hasSelection()); - EXPECT_EQ(test_data.selection_model.activeCanvas(), nullptr); - EXPECT_EQ(test_data.selection_model.selectedGraph(), nullptr); - EXPECT_EQ(test_data.selection_model.selectedCanvas().size(), 0); - EXPECT_EQ(test_data.selection_model.selectedGraphs().size(), 0); -} - -//! Select single canvas and check that it was actually selected. - -TEST_F(DataSelectionModelTest, selectCanvasItem) -{ - TestData test_data; - EXPECT_FALSE(test_data.selection_model.hasSelection()); - - // make canvas0 selected - test_data.selection_model.selectItem(test_data.canvas0); - - EXPECT_TRUE(test_data.selection_model.hasSelection()); - EXPECT_EQ(test_data.selection_model.selectedIndexes().size(), 2); // CanvasItem, ViewEmptyItem - - std::vector<SessionItem*> expected = {test_data.canvas0}; - EXPECT_EQ(test_data.selection_model.selectedItems(), expected); - - // check that selection model reports same canvas as active one - EXPECT_EQ(test_data.selection_model.activeCanvas(), test_data.canvas0); - std::vector<CanvasItem*> expected_canvas{test_data.canvas0}; - EXPECT_EQ(test_data.selection_model.selectedCanvas(), expected_canvas); - - // no graphs selected - EXPECT_EQ(test_data.selection_model.selectedGraph(), nullptr); - EXPECT_EQ(test_data.selection_model.selectedGraphs().size(), 0); -} - -//! Select single graph and check reported selection. - -TEST_F(DataSelectionModelTest, selectGraph) -{ - TestData test_data; - EXPECT_FALSE(test_data.selection_model.hasSelection()); - - // make graph0 selected - test_data.selection_model.selectItem(test_data.graph_c0_a); - - EXPECT_TRUE(test_data.selection_model.hasSelection()); - EXPECT_EQ(test_data.selection_model.selectedIndexes().size(), 2); // GraphItem, LabelItem - - std::vector<SessionItem*> expected = {test_data.graph_c0_a}; - EXPECT_EQ(test_data.selection_model.selectedItems(), expected); - - // method should report parent of graph as active canvas - EXPECT_EQ(test_data.selection_model.activeCanvas(), test_data.canvas0); - - // no canvas selected - EXPECT_EQ(test_data.selection_model.selectedCanvas(), std::vector<CanvasItem*>()); - - // method should report parent of graph as selected canvas - EXPECT_EQ(test_data.selection_model.selectedGraph(), test_data.graph_c0_a); - std::vector<ModelView::GraphItem*> expected_graphs{test_data.graph_c0_a}; - EXPECT_EQ(test_data.selection_model.selectedGraphs(), expected_graphs); -} - -//! Select two graphs and check reported selection. - -TEST_F(DataSelectionModelTest, selectTwoGraphs) -{ - TestData test_data; - EXPECT_FALSE(test_data.selection_model.hasSelection()); - - // make two graphs selected - std::vector<SessionItem*> expected = {test_data.graph_c0_a, test_data.graph_c0_b}; - test_data.selection_model.selectItems(expected); - - EXPECT_TRUE(test_data.selection_model.hasSelection()); - EXPECT_EQ(test_data.selection_model.selectedIndexes().size(), 4); // GraphItem, LabelItem - - EXPECT_EQ(test_data.selection_model.selectedItems(), expected); - - // method should report parent of graph as active canvas - EXPECT_EQ(test_data.selection_model.activeCanvas(), test_data.canvas0); - EXPECT_EQ(test_data.selection_model.selectedCanvas().size(), 0); - - // method should report parent of graph as selected canvas - EXPECT_EQ(test_data.selection_model.selectedGraph(), test_data.graph_c0_a); - std::vector<ModelView::GraphItem*> expected_graphs{test_data.graph_c0_a, test_data.graph_c0_b}; - EXPECT_EQ(test_data.selection_model.selectedGraphs(), expected_graphs); -} - -//! Select two graphs and check reported selection. - -// FIXME Enable test. For that DataSelectionModel should allow to select -// parent+child programmatically. - -// TEST_F(DataSelectionModelTest, selectGraphAndCanvas) -//{ -// TestData test_data; -// EXPECT_FALSE(test_data.selection_model.hasSelection()); - -// // make two graphs selected -// std::vector<SessionItem*> expected = {test_data.graph_c0_a, test_data.canvas0}; -// test_data.selection_model.selectItems(expected); - -// EXPECT_TRUE(test_data.selection_model.hasSelection()); -// EXPECT_EQ(test_data.selection_model.selectedIndexes().size(), 4); // GraphItem, LabelItem -//} diff --git a/Tests/Unit/gui2/testdareflcore/defaultparser.test.cpp b/Tests/Unit/gui2/testdareflcore/defaultparser.test.cpp deleted file mode 100644 index 1fa0d4b379c..00000000000 --- a/Tests/Unit/gui2/testdareflcore/defaultparser.test.cpp +++ /dev/null @@ -1,91 +0,0 @@ -// ************************************************************************** // -// -// Reflectometry simulation software prototype -// -//! @license GNU General Public License v3 or higher (see COPYING) -//! @authors see AUTHORS -// -// ************************************************************************** // - -#include "google_test.h" -#include "gui2/dataloader/dataloader_types.h" -#include "gui2/dataloader/defaultparser.h" -#include "test_utils.h" - -using namespace gui2; -using TestUtils::toStringVector; - -//! Test DefaultParser. - -class DefaultParserTest : public ::testing::Test { -public: - ~DefaultParserTest(); - - int parsedDataRowCount(const DefaultParser& parser) { return parser.parsedData().size(); } -}; - -DefaultParserTest::~DefaultParserTest() = default; - -TEST_F(DefaultParserTest, initialState) -{ - DefaultParser parser({}); - - EXPECT_EQ(parser.totalLineCount(), 0); - EXPECT_EQ(parsedDataRowCount(parser), 0); -} - -//! Testing line acceptance with standard parser settings. - -TEST_F(DefaultParserTest, lineAcceptanceStandardParser) -{ - DefaultParser parser({"#", ",", ""}); // prefix, separator, line pattern - - parser.process({"1, 2, 3"}); - EXPECT_EQ(parser.totalLineCount(), 1); - EXPECT_EQ(parsedDataRowCount(parser), 1); - - parser.process({"1, 2, 3", "4, 5, 6"}); - EXPECT_EQ(parser.totalLineCount(), 2); - EXPECT_EQ(parsedDataRowCount(parser), 2); - - parser.process({"# 1, 2, 3"}); - EXPECT_EQ(parser.totalLineCount(), 1); - EXPECT_EQ(parsedDataRowCount(parser), 0); - - parser.process({"#1, 2, 3", "4, 5, 6"}); - EXPECT_EQ(parser.totalLineCount(), 2); - EXPECT_EQ(parsedDataRowCount(parser), 1); - - parser.process({"#1, 2, 3", "4, 5, 6", ""}); - EXPECT_EQ(parser.totalLineCount(), 3); - EXPECT_EQ(parsedDataRowCount(parser), 1); -} - -//! Testing line acceptance with standard parser settings. - -TEST_F(DefaultParserTest, lineAcceptanceNumberPatternParser) -{ - DefaultParser parser({"#", ",", "1-2"}); // prefix, separator, line pattern - - parser.process({"1, 2, 3", "4, 5, 6", "7, 8, 9", "#", ""}); - EXPECT_EQ(parser.totalLineCount(), 5); - EXPECT_EQ(parsedDataRowCount(parser), 1); -} - -TEST_F(DefaultParserTest, parseResults) -{ - DefaultParser parser({"#", ",", ""}); // prefix, separator, line pattern - - parser.process({"1, 2, 3"}); - EXPECT_EQ(parser.parsedData().size(), 1); - EXPECT_EQ(parser.parsedData()[0], toStringVector("1", " 2", " 3")); - - parser.process({"1, 2, 3", "4, 5, 6"}); - EXPECT_EQ(parser.parsedData().size(), 2); - EXPECT_EQ(parser.parsedData()[0], toStringVector("1", " 2", " 3")); - EXPECT_EQ(parser.parsedData()[1], toStringVector("4", " 5", " 6")); - - parser.process({"#1, 2, 3", "4, 5, 6"}); - EXPECT_EQ(parser.parsedData().size(), 1); - EXPECT_EQ(parser.parsedData()[0], toStringVector("4", " 5", " 6")); -} diff --git a/Tests/Unit/gui2/testdareflcore/experimentaldatamodel.test.cpp b/Tests/Unit/gui2/testdareflcore/experimentaldatamodel.test.cpp deleted file mode 100644 index c5b2ae23911..00000000000 --- a/Tests/Unit/gui2/testdareflcore/experimentaldatamodel.test.cpp +++ /dev/null @@ -1,209 +0,0 @@ -// ************************************************************************** // -// -// Reflectometry simulation software prototype -// -//! @license GNU General Public License v3 or higher (see COPYING) -//! @authors see AUTHORS -// -// ************************************************************************** // - -#include "google_test.h" - -#include "gui2/importdataview/graphimportdata.h" -#include "gui2/model/experimentaldataitems.h" -#include "gui2/model/experimentaldatamodel.h" -#include "gui2/model/item_constants.h" -#include "mvvm/model/modelutils.h" -#include "mvvm/standarditems/data1ditem.h" -#include "mvvm/standarditems/graphitem.h" - -using namespace gui2; -using namespace ModelView; - -//! Tests of ExperimentalDataModel. - -class ExperimentalDataModelTest : public ::testing::Test { -public: - ~ExperimentalDataModelTest(); -}; - -ExperimentalDataModelTest::~ExperimentalDataModelTest() = default; - -//! Test the initial state of the model. - -TEST_F(ExperimentalDataModelTest, initialState) -{ - ExperimentalDataModel model; - - ASSERT_TRUE(model.canvasContainer() != nullptr); - EXPECT_EQ(model.canvasContainer()->childrenCount(), 0); - EXPECT_EQ(model.canvasContainer()->modelType(), gui2::GUI::Constants::CanvasContainerItemType); - EXPECT_EQ(model.canvasContainer()->canvasItems().size(), 0); - - ASSERT_TRUE(model.dataContainer() != nullptr); - EXPECT_EQ(model.dataContainer()->childrenCount(), 0); - EXPECT_EQ(model.dataContainer()->modelType(), - gui2::GUI::Constants::ExperimentalDataContainerItemType); - EXPECT_EQ(model.dataContainer()->dataItems().size(), 0); -} - -//! Adding canvas to the model. - -TEST_F(ExperimentalDataModelTest, addCanvas) -{ - ExperimentalDataModel model; - - auto canvas = model.addCanvas(); - ASSERT_TRUE(canvas != nullptr); - EXPECT_EQ(canvas->graphItems().size(), 0); - EXPECT_EQ(canvas->modelType(), gui2::GUI::Constants::CanvasItemType); - EXPECT_EQ(model.canvasContainer()->canvasItems().size(), 1); - EXPECT_EQ(model.canvasContainer()->canvasItems(), std::vector<CanvasItem*>() = {canvas}); -} - -//! Adding graph from the data structure. - -TEST_F(ExperimentalDataModelTest, addGraph) -{ - ExperimentalDataModel model; - std::vector<double> bin_centers{1, 2, 3}; - std::vector<double> bin_values{10, 20, 30}; - - GraphImportData raw_data = {"", bin_centers, "", bin_values, ""}; - - auto canvas = model.addCanvas(); - - // adding graph, it should appear in canvas - auto graph = model.addGraph(raw_data, *canvas); - ASSERT_EQ(canvas->graphItems().size(), 1); - EXPECT_EQ(canvas->graphItems()[0], graph); - - // graph should have values as defined in the raw_data - EXPECT_EQ(graph->binCenters(), bin_centers); - EXPECT_EQ(graph->binValues(), bin_values); - - EXPECT_EQ(model.dataContainer()->childrenCount(), 1); - std::vector<Data1DItem*> expected_items = {graph->dataItem()}; - EXPECT_EQ(model.dataContainer()->dataItems(), expected_items); -} - -//! Removing just added graph. - -TEST_F(ExperimentalDataModelTest, removeGraph) -{ - ExperimentalDataModel model; - std::vector<double> bin_centers{1, 2, 3}; - std::vector<double> bin_values{10, 20, 30}; - - GraphImportData raw_data = {"", bin_centers, "", bin_values, ""}; - - auto canvas = model.addCanvas(); - - // adding graph - auto graph = model.addGraph(raw_data, *canvas); - - // removing graph - model.removeGraph(*graph); - - // should remoive bove graph, and underlying data item - EXPECT_EQ(canvas->graphItems().size(), 0); - EXPECT_EQ(model.dataContainer()->dataItems().size(), 0); -} - -//! Removing middle graph from the collection. - -TEST_F(ExperimentalDataModelTest, removeMiddleGraph) -{ - ExperimentalDataModel model; - std::vector<double> bin_centers{1, 2, 3}; - std::vector<double> bin_values{10, 20, 30}; - - GraphImportData raw_data = {"", bin_centers, "", bin_values, ""}; - - auto canvas = model.addCanvas(); - - // adding graph, it should appear in canvas - auto graph0 = model.addGraph(raw_data, *canvas); - auto graph1 = model.addGraph(raw_data, *canvas); - auto graph2 = model.addGraph(raw_data, *canvas); - - // removing graph - model.removeGraph(*graph1); - - std::vector<GraphItem*> expected_graphs = {graph0, graph2}; - ASSERT_EQ(canvas->graphItems(), expected_graphs); - std::vector<Data1DItem*> expected_items = {graph0->dataItem(), graph2->dataItem()}; - EXPECT_EQ(model.dataContainer()->dataItems(), expected_items); -} - -//! Removing just added graph. - -TEST_F(ExperimentalDataModelTest, removeCanvasWithGraph) -{ - ExperimentalDataModel model; - std::vector<double> bin_centers{1, 2, 3}; - std::vector<double> bin_values{10, 20, 30}; - - GraphImportData raw_data = {"", bin_centers, "", bin_values, ""}; - - auto canvas = model.addCanvas(); - - // adding graph - model.addGraph(raw_data, *canvas); - - // removing graph - model.removeCanvas(*canvas); - - // should remoive bove graph, and underlying data item - EXPECT_EQ(model.dataContainer()->dataItems().size(), 0); - EXPECT_EQ(model.canvasContainer()->canvasItems().size(), 0); -} - -//! Merge single canvas into itself. Nothing should happen. - -TEST_F(ExperimentalDataModelTest, mergeSingleCanvas) -{ - ExperimentalDataModel model; - - GraphImportData raw_data = {"", {1}, "", {10}, ""}; - auto canvas = model.addCanvas(); - auto graph = model.addGraph(raw_data, *canvas); - - // removing graph - model.mergeCanvases(std::vector<CanvasItem*>({canvas})); - - // should remove bove graph, and underlying data item - EXPECT_EQ(model.dataContainer()->dataItems().size(), 1); - EXPECT_EQ(model.canvasContainer()->canvasItems().size(), 1); - EXPECT_EQ(canvas->graphItems(), std::vector<GraphItem*>({graph})); -} - -//! Merge single canvas into itself. Nothing should happen. - -TEST_F(ExperimentalDataModelTest, mergeTwoCanvases) -{ - ExperimentalDataModel model; - - GraphImportData raw_data0 = {"", {1}, "", {10}, ""}; - GraphImportData raw_data1 = {"", {2}, "", {20}, ""}; - GraphImportData raw_data2 = {"", {3}, "", {30}, ""}; - - auto canvas0 = model.addCanvas(); - auto graph0 = model.addGraph(raw_data0, *canvas0); - - auto canvas1 = model.addCanvas(); - auto graph1 = model.addGraph(raw_data1, *canvas1); - auto graph2 = model.addGraph(raw_data2, *canvas1); - - // removing graph - model.mergeCanvases(std::vector<CanvasItem*>({canvas0, canvas1})); - - // should remoive bove graph, and underlying data item - EXPECT_EQ(model.dataContainer()->dataItems().size(), 3); - EXPECT_EQ(model.canvasContainer()->canvasItems(), std::vector<CanvasItem*>({canvas0})); - EXPECT_EQ(canvas0->graphItems(), std::vector<GraphItem*>({graph0, graph1, graph2})); - - EXPECT_EQ(graph0->dataItem()->binCenters(), std::vector<double>({1})); - EXPECT_EQ(graph1->dataItem()->binCenters(), std::vector<double>({2})); - EXPECT_EQ(graph2->dataItem()->binCenters(), std::vector<double>({3})); -} diff --git a/Tests/Unit/gui2/testdareflcore/importdataeditoractions.test.cpp b/Tests/Unit/gui2/testdareflcore/importdataeditoractions.test.cpp deleted file mode 100644 index f387502d57e..00000000000 --- a/Tests/Unit/gui2/testdareflcore/importdataeditoractions.test.cpp +++ /dev/null @@ -1,105 +0,0 @@ -// ************************************************************************** // -// -// Reflectometry simulation software prototype -// -//! @license GNU General Public License v3 or higher (see COPYING) -//! @authors see AUTHORS -// -// ************************************************************************** // - -#include "google_test.h" -#include "gui2/importdataview/dataselectionmodel.h" -#include "gui2/importdataview/dataviewmodel.h" -#include "gui2/importdataview/graphimportdata.h" -#include "gui2/importdataview/importdataeditoractions.h" -#include "gui2/model/experimentaldataitems.h" -#include "gui2/model/experimentaldatamodel.h" -#include "mvvm/model/comboproperty.h" -#include "mvvm/model/modelutils.h" -#include "mvvm/standarditems/graphitem.h" -#include "mvvm/standarditems/plottableitems.h" -#include "test_utils.h" - -using namespace gui2; -using namespace ModelView; - -//! Tests of ImportDataEditorActions. - -class ImportDataEditorActionsTest : public ::testing::Test { -public: - ~ImportDataEditorActionsTest(); - - struct TestData { - ExperimentalDataModel data_model; - DataViewModel view_model{&data_model}; - DataSelectionModel selection_model{&view_model}; - CanvasItem* canvas0{nullptr}; - ModelView::GraphItem* graph0{nullptr}; - ModelView::GraphItem* graph1{nullptr}; - ImportDataEditorActions actions{&data_model}; - TestData() - { - canvas0 = data_model.addCanvas(); - - GraphImportData raw_data = {"", {42}, "", {42}, ""}; - graph0 = data_model.addGraph(raw_data, *canvas0); - graph1 = data_model.addGraph(raw_data, *canvas0); - - view_model.setRootSessionItem(data_model.canvasContainer()); - actions.setSelectionModel(&selection_model); - } - }; -}; - -ImportDataEditorActionsTest::~ImportDataEditorActionsTest() = default; - -//! Checks onAddCanvas. - -TEST_F(ImportDataEditorActionsTest, onAddCanvas) -{ - ExperimentalDataModel model; - ImportDataEditorActions actions(&model); - - EXPECT_EQ(model.canvasContainer()->canvasItems().size(), 0); - actions.onAddCanvas(); - EXPECT_EQ(model.canvasContainer()->canvasItems().size(), 1); -} - -//! Checks onAddCanvas. - -TEST_F(ImportDataEditorActionsTest, onDeleteItem) -{ - TestData test_data; - EXPECT_EQ(test_data.data_model.canvasContainer()->canvasItems().size(), 1); - EXPECT_EQ(test_data.data_model.dataContainer()->dataItems().size(), 2); - - test_data.selection_model.selectItem(test_data.canvas0); - - test_data.actions.onDeleteItem(); - - EXPECT_EQ(test_data.data_model.canvasContainer()->canvasItems().size(), 0); - EXPECT_EQ(test_data.data_model.dataContainer()->dataItems().size(), 0); -} - -//! Checks line style change on selection changed. - -TEST_F(ImportDataEditorActionsTest, onSelectionChanged) -{ - TestData test_data; - - test_data.selection_model.selectItem(test_data.graph0); - - auto pen0 = test_data.graph0->penItem(); - auto pen1 = test_data.graph1->penItem(); - - auto pencombo0 = pen0->property<ModelView::ComboProperty>(ModelView::PenItem::P_STYLE); - auto pencombo1 = pen1->property<ModelView::ComboProperty>(ModelView::PenItem::P_STYLE); - EXPECT_EQ(pencombo0.currentIndex(), 2); // correspond to dashed line (i.e. selected) - EXPECT_EQ(pencombo1.currentIndex(), 1); // correspond to solid line - - test_data.selection_model.selectItem(test_data.graph1); - pencombo0 = pen0->property<ModelView::ComboProperty>(ModelView::PenItem::P_STYLE); - pencombo1 = pen1->property<ModelView::ComboProperty>(ModelView::PenItem::P_STYLE); - EXPECT_EQ(pencombo0.currentIndex(), 1); // correspond to solid line - EXPECT_EQ(pencombo1.currentIndex(), 2); // correspond to dashed line (i.e. selected) -} diff --git a/Tests/Unit/gui2/testdareflcore/importtableheader.test.cpp b/Tests/Unit/gui2/testdareflcore/importtableheader.test.cpp deleted file mode 100644 index 41786f125ad..00000000000 --- a/Tests/Unit/gui2/testdareflcore/importtableheader.test.cpp +++ /dev/null @@ -1,116 +0,0 @@ -// ************************************************************************** // -// -// Reflectometry simulation software prototype -// -//! @license GNU General Public License v3 or higher (see COPYING) -//! @authors see AUTHORS -// -// ************************************************************************** // - -#include "google_test.h" -#include "gui2/dataloader/dataloader_constants.h" -#include "gui2/dataloader/dataloader_types.h" -#include "gui2/dataloader/importtableheader.h" -#include "mvvm/model/comboproperty.h" -#include "test_utils.h" - -using namespace gui2; - -//! Test the data column item -class ImportTableHeaderTest : public ::testing::Test { -public: - ~ImportTableHeaderTest(); -}; - -ImportTableHeaderTest::~ImportTableHeaderTest() = default; - -TEST_F(ImportTableHeaderTest, initialStateTwoColumn) -{ - const int column_count{2}; - ImportTableHeader header(column_count); - - EXPECT_EQ(header.columnCount(), column_count); - EXPECT_EQ(header.rowCount(), ImportTableHeader::MAX); - EXPECT_EQ(header.columnInfo().size(), column_count); - - // first column is by default represents Axis - EXPECT_EQ(header.columnInfo()[0].column, 0); - EXPECT_EQ(header.columnInfo()[0].type_name, GUI::Constants::AxisType); - EXPECT_EQ(header.columnInfo()[0].units, std::string("a.u.")); - EXPECT_EQ(header.columnInfo()[0].multiplier, 1.0); - - // second column is by default represents Intensity - EXPECT_EQ(header.columnInfo()[1].column, 1); - EXPECT_EQ(header.columnInfo()[1].type_name, GUI::Constants::IntensityType); - EXPECT_EQ(header.columnInfo()[1].units, std::string("a.u.")); - EXPECT_EQ(header.columnInfo()[1].multiplier, 1.0); -} - -TEST_F(ImportTableHeaderTest, initialStateThreeColumn) -{ - const int column_count{3}; - ImportTableHeader header(column_count); - - EXPECT_EQ(header.columnCount(), column_count); - EXPECT_EQ(header.rowCount(), ImportTableHeader::MAX); - EXPECT_EQ(header.columnInfo().size(), column_count); - - // first column is by default represents Axis - EXPECT_EQ(header.columnInfo()[0].column, 0); - EXPECT_EQ(header.columnInfo()[0].type_name, GUI::Constants::AxisType); - EXPECT_EQ(header.columnInfo()[0].units, std::string("a.u.")); - EXPECT_EQ(header.columnInfo()[0].multiplier, 1.0); - - // second column is by default represents Intensity - EXPECT_EQ(header.columnInfo()[1].column, 1); - EXPECT_EQ(header.columnInfo()[1].type_name, GUI::Constants::IntensityType); - EXPECT_EQ(header.columnInfo()[1].units, std::string("a.u.")); - EXPECT_EQ(header.columnInfo()[1].multiplier, 1.0); - - // second column is by default represents Intensity - EXPECT_EQ(header.columnInfo()[2].column, 2); - EXPECT_EQ(header.columnInfo()[2].type_name, GUI::Constants::IgnoreType); - EXPECT_EQ(header.columnInfo()[2].units, std::string("a.u.")); - EXPECT_EQ(header.columnInfo()[2].multiplier, 1.0); -} - -TEST_F(ImportTableHeaderTest, rowName) -{ - const int column_count{2}; - ImportTableHeader header(column_count); - - ASSERT_EQ(header.rowCount(), 3); - EXPECT_EQ(header.rowName(0), "Type"); - EXPECT_EQ(header.rowName(1), "Unit"); - EXPECT_EQ(header.rowName(2), "Multiplier"); -} - -TEST_F(ImportTableHeaderTest, setData) -{ - const int column_count{2}; - ImportTableHeader header(column_count); - - EXPECT_EQ(header.columnCount(), column_count); - EXPECT_EQ(header.rowCount(), ImportTableHeader::MAX); - EXPECT_EQ(header.columnInfo().size(), column_count); - - const std::vector<std::string> typeNames = { - GUI::Constants::AxisType, GUI::Constants::IntensityType, GUI::Constants::IgnoreType}; - auto combo = ModelView::ComboProperty::createFrom(typeNames); - combo.setValue(GUI::Constants::IgnoreType); - - header.setData(ImportTableHeader::TYPE, 0, QVariant::fromValue(combo)); - header.setData(ImportTableHeader::MULTIPLIER, 0, QVariant::fromValue(2.0)); - - // first column is by default represents Axis - EXPECT_EQ(header.columnInfo()[0].column, 0); - EXPECT_EQ(header.columnInfo()[0].type_name, GUI::Constants::IgnoreType); - EXPECT_EQ(header.columnInfo()[0].units, std::string("a.u.")); - EXPECT_EQ(header.columnInfo()[0].multiplier, 2.0); - - // second column is by default represents Intensity - EXPECT_EQ(header.columnInfo()[1].column, 1); - EXPECT_EQ(header.columnInfo()[1].type_name, GUI::Constants::IntensityType); - EXPECT_EQ(header.columnInfo()[1].units, std::string("a.u.")); - EXPECT_EQ(header.columnInfo()[1].multiplier, 1.0); -} diff --git a/Tests/Unit/gui2/testdareflcore/instrumentitems.test.cpp b/Tests/Unit/gui2/testdareflcore/instrumentitems.test.cpp deleted file mode 100644 index 60d5b936c8f..00000000000 --- a/Tests/Unit/gui2/testdareflcore/instrumentitems.test.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// ************************************************************************** // -// -// Reflectometry simulation software prototype -// -//! @license GNU General Public License v3 or higher (see COPYING) -//! @authors see AUTHORS -// -// ************************************************************************** // - -#include "google_test.h" -#include "gui2/model/experimentaldataitems.h" -#include "gui2/model/instrumentitems.h" -#include "gui2/model/instrumentmodel.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/standarditems/axisitems.h" -#include "mvvm/standarditems/data1ditem.h" -#include "mvvm/standarditems/graphitem.h" - -using namespace gui2; -using namespace ModelView; - -//! Tests of ExperimentalDataModel. - -class InstrumentItemsTest : public ::testing::Test { -public: - ~InstrumentItemsTest(); -}; - -InstrumentItemsTest::~InstrumentItemsTest() = default; - -TEST_F(InstrumentItemsTest, experimentalScanItemInitialState) -{ - ExperimentalScanItem item; - EXPECT_EQ(item.graphItem(), nullptr); - EXPECT_TRUE(item.qScanValues().empty()); -} - -TEST_F(InstrumentItemsTest, experimentalScanGetValues) -{ - InstrumentModel model; - - // preparing DataItem - auto data_item = model.insertItem<Data1DItem>(); - std::vector<double> expected_content = {1.0, 2.0, 3.0}; - std::vector<double> expected_centers = {0.5, 1.5, 2.5}; - data_item->setAxis<FixedBinAxisItem>(3, 0.0, 3.0); - data_item->setValues(expected_content); - - // preparing GraphItem - auto graph_item = model.insertItem<GraphItem>(); - graph_item->setDataItem(data_item); - - // preparing ScanItem - auto scan_item = model.insertItem<ExperimentalScanItem>(); - scan_item->setGraphItem(graph_item); - - EXPECT_EQ(scan_item->graphItem(), graph_item); - EXPECT_EQ(scan_item->qScanValues(), expected_centers); -} diff --git a/Tests/Unit/gui2/testdareflcore/layereditoractions.test.cpp b/Tests/Unit/gui2/testdareflcore/layereditoractions.test.cpp deleted file mode 100644 index ac7a232a1b4..00000000000 --- a/Tests/Unit/gui2/testdareflcore/layereditoractions.test.cpp +++ /dev/null @@ -1,242 +0,0 @@ -// ************************************************************************** // -// -// Reflectometry simulation software prototype -// -//! @license GNU General Public License v3 or higher (see COPYING) -//! @authors see AUTHORS -// -// ************************************************************************** // - -#include "google_test.h" -#include "gui2/layereditor/layereditoractions.h" -#include "gui2/layereditor/layerselectionmodel.h" -#include "gui2/layereditor/layerviewmodel.h" -#include "gui2/model/item_constants.h" -#include "gui2/model/sampleitems.h" -#include "gui2/model/samplemodel.h" -#include "mvvm/viewmodel/standardviewitems.h" -#include "mvvm/viewmodel/viewmodelutils.h" -#include "test_utils.h" -#include <QDebug> - -using namespace gui2; -using namespace ModelView; - -//! Tests of LayerEditorActions. - -class LayerEditorActionsTest : public ::testing::Test { -public: - ~LayerEditorActionsTest(); - - //! Test data representing MultiLayer with two layers and all machinery around: - //! ViewModel, SelectionModel and actions. - - struct TestData { - SampleModel sample_model; - LayerViewModel view_model{&sample_model}; - LayerSelectionModel selection_model{&view_model}; - LayerEditorActions actions; - SessionItem* multilayer{nullptr}; - SessionItem* top{nullptr}; - SessionItem* bottom{nullptr}; - TestData() - { - actions.setModel(&sample_model); - actions.setSelectionModel(&selection_model); - multilayer = sample_model.insertItem<MultiLayerItem>(); - top = sample_model.insertItem<LayerItem>(multilayer); - bottom = sample_model.insertItem<LayerItem>(multilayer); - view_model.setRootSessionItem(multilayer); - } - }; -}; - -LayerEditorActionsTest::~LayerEditorActionsTest() = default; - -//! Checking initial data for testing. - -TEST_F(LayerEditorActionsTest, initialState) -{ - TestData test_data; - - // checking layout of multilayer - auto layers = test_data.multilayer->getItems(MultiLayerItem::T_LAYERS); - EXPECT_EQ(layers.size(), 2); - EXPECT_EQ(layers.at(0), test_data.top); - EXPECT_EQ(layers.at(1), test_data.bottom); - - // checking, that there are no layers selected - EXPECT_EQ(test_data.selection_model.selectedItems(), std::vector<SessionItem*>()); -} - -//! Adds new layer after selected layer. - -TEST_F(LayerEditorActionsTest, addNewLayerAfterSelection) -{ - TestData test_data; - - // selecting top layer - test_data.selection_model.selectItem(test_data.top); - std::vector<SessionItem*> expected = {test_data.top}; - EXPECT_EQ(test_data.selection_model.selectedItems(), expected); - - // layout - auto ml_index = QModelIndex(); - auto top_index = test_data.view_model.index(0, 0, ml_index); - auto bottom_index = test_data.view_model.index(1, 0, ml_index); - EXPECT_EQ(test_data.view_model.rootItem()->item(), test_data.multilayer); - EXPECT_EQ(test_data.view_model.itemFromIndex(top_index)->item(), - test_data.top->getItem(LayerItem::P_NAME)); - EXPECT_EQ(test_data.view_model.itemFromIndex(bottom_index)->item(), - test_data.bottom->getItem(LayerItem::P_NAME)); - - // adding new layer after selection - test_data.actions.onAddLayer(); - - top_index = test_data.view_model.index(0, 0, ml_index); - bottom_index = test_data.view_model.index(2, 0, ml_index); - EXPECT_EQ(test_data.view_model.itemFromIndex(top_index)->item(), - test_data.top->getItem(LayerItem::P_NAME)); - EXPECT_EQ(test_data.view_model.itemFromIndex(bottom_index)->item(), - test_data.bottom->getItem(LayerItem::P_NAME)); - - // checking layout of multilayer - auto layers = test_data.multilayer->getItems(MultiLayerItem::T_LAYERS); - EXPECT_EQ(layers.size(), 3); - EXPECT_EQ(layers.at(0), test_data.top); - EXPECT_EQ(layers.at(2), test_data.bottom); - - // checking layout of viewmodel - - // checking, that new layer is selected - expected = {layers.at(1)}; - EXPECT_EQ(test_data.selection_model.selectedItems(), expected); -} - -//! Adds new multi-layer after selected layer. - -TEST_F(LayerEditorActionsTest, addNewMultiLayerAfterSelection) -{ - TestData test_data; - - // selecting top layer - test_data.selection_model.selectItem(test_data.top); - - // adding new layer after selection - test_data.actions.onAddMultiLayer(); - - // checking layout of multilayer - auto layers = test_data.multilayer->getItems(MultiLayerItem::T_LAYERS); - EXPECT_EQ(layers.size(), 3); - EXPECT_EQ(layers.at(0), test_data.top); - EXPECT_EQ(layers.at(2), test_data.bottom); - - // checking that layer was added - EXPECT_EQ(layers.at(1)->modelType(), gui2::GUI::Constants::MultiLayerItemType); - auto sublayers = layers.at(1)->getItems(MultiLayerItem::T_LAYERS); - EXPECT_EQ(sublayers.size(), 2); - - // checking, that new layer is selected - std::vector<SessionItem*> expected = {layers.at(1)}; - EXPECT_EQ(test_data.selection_model.selectedItems(), expected); -} - -//! Adds new layer when no selection exists. - -TEST_F(LayerEditorActionsTest, addNewLayerNoSelection) -{ - TestData test_data; - - // adding new layer when no selection exist - test_data.actions.onAddLayer(); - - // checking layout of multilayer - auto layers = test_data.multilayer->getItems(MultiLayerItem::T_LAYERS); - EXPECT_EQ(layers.size(), 3); - EXPECT_EQ(layers.at(0), test_data.top); - EXPECT_EQ(layers.at(1), test_data.bottom); - - // checking, that new layer is selected - std::vector<SessionItem*> expected = {layers.at(2)}; - EXPECT_EQ(test_data.selection_model.selectedItems(), expected); -} - -//! Adds new layer after selected layer. - -TEST_F(LayerEditorActionsTest, onRemoveLayer) -{ - TestData test_data; - - // selecting top layer - test_data.selection_model.selectItem(test_data.top); - - // removing selected top layer - test_data.actions.onRemove(); - - // checking layout of multilayer - auto layers = test_data.multilayer->getItems(MultiLayerItem::T_LAYERS); - EXPECT_EQ(layers.size(), 1); - EXPECT_EQ(layers.at(0), test_data.bottom); - - // checking, that next layer beneath is selected - std::vector<SessionItem*> expected = {test_data.bottom}; - EXPECT_EQ(test_data.selection_model.selectedItems(), expected); -} - -//! Move one layer up. - -TEST_F(LayerEditorActionsTest, onMoveUp) -{ - TestData test_data; - - // selecting bottom layer - test_data.selection_model.selectItem(test_data.bottom); - - // moving selected bottom layer up - test_data.actions.onMoveUp(); - - // checking layout of multilayer - auto layers = test_data.multilayer->getItems(MultiLayerItem::T_LAYERS); - EXPECT_EQ(layers.size(), 2); - EXPECT_EQ(layers.at(0), test_data.bottom); - EXPECT_EQ(layers.at(1), test_data.top); - - // checking, that former bottom layer is stil selected - std::vector<SessionItem*> expected = {test_data.bottom}; - EXPECT_EQ(test_data.selection_model.selectedItems(), expected); - - // moving up once again, nothing should change - test_data.actions.onMoveUp(); - EXPECT_EQ(layers.at(0), test_data.bottom); - EXPECT_EQ(layers.at(1), test_data.top); - EXPECT_EQ(test_data.selection_model.selectedItems(), expected); -} - -//! Move one layer up. - -TEST_F(LayerEditorActionsTest, onMoveDown) -{ - TestData test_data; - - // selecting top layer - test_data.selection_model.selectItem(test_data.top); - - // moving selected top layer down - test_data.actions.onMoveDown(); - - // checking layout of multilayer - auto layers = test_data.multilayer->getItems(MultiLayerItem::T_LAYERS); - EXPECT_EQ(layers.size(), 2); - EXPECT_EQ(layers.at(0), test_data.bottom); - EXPECT_EQ(layers.at(1), test_data.top); - - // checking, that former top layer is stil selected - std::vector<SessionItem*> expected = {test_data.top}; - EXPECT_EQ(test_data.selection_model.selectedItems(), expected); - - // moving down once again, nothing should change - test_data.actions.onMoveDown(); - EXPECT_EQ(layers.at(0), test_data.bottom); - EXPECT_EQ(layers.at(1), test_data.top); - EXPECT_EQ(test_data.selection_model.selectedItems(), expected); -} diff --git a/Tests/Unit/gui2/testdareflcore/layerelements.test.cpp b/Tests/Unit/gui2/testdareflcore/layerelements.test.cpp deleted file mode 100644 index c4f24808599..00000000000 --- a/Tests/Unit/gui2/testdareflcore/layerelements.test.cpp +++ /dev/null @@ -1,1011 +0,0 @@ -// ************************************************************************** // -// -// Reflectometry simulation software prototype -// -//! @license GNU General Public License v3 or higher (see COPYING) -//! @authors see AUTHORS -// -// ************************************************************************** // - -#include "google_test.h" - -#include "gui2/model/materialmodel.h" -#include "gui2/model/samplemodel.h" -#include "gui2/sldeditor/graphicsscene.h" -#include "gui2/sldeditor/handleelementview.h" -#include "gui2/sldeditor/layerelementcontroller.h" -#include "gui2/sldeditor/layerelementitem.h" -#include "gui2/sldeditor/roughnesselementview.h" -#include "gui2/sldeditor/segmentelementview.h" -#include "gui2/sldeditor/sldelementmodel.h" -#include "mvvm/viewmodel/viewmodelutils.h" -#include "test_utils.h" - -#include <QColor> -#include <QGraphicsSceneMouseEvent> -#include <QSignalSpy> - -using namespace gui2; -using namespace ModelView; - -class LayerElementControllerFriend : public LayerElementController { -public: - LayerElementControllerFriend(LayerElementItem* item) : LayerElementController(item){}; - QRectF pubSideSegmentRect() const { return sideSegmentRect(); }; - QRectF pubTopSegmentRect() const { return topSegmentRect(); }; - QRectF pubFirstSegmentHandleRect() const { return firstSegmentHandleRect(); }; - QRectF pubSecondSegmentHandleRect() const { return secondSegmentHandleRect(); }; - QPainterPath pubLeftRoughnessPath() const { return leftRoughnessPath(); }; - QPainterPath pubRightRoughnessPath() const { return rightRoughnessPath(); }; - QRectF pubLeftRoughnessHandleRect() const { return leftRoughnessHandleRect(); }; - QRectF pubRightRoughnessHandleRect() const { return rightRoughnessHandleRect(); }; -}; - -class SegmentElementViewFriend : public SegmentElementView { -public: - SegmentElementViewFriend() : SegmentElementView(){}; - QRectF rectangle() const { return m_rectangle; }; - QPen pen() const { return m_pen; }; - QBrush brush() const { return m_brush; }; -}; - -class HandleElementViewFriend : public HandleElementView { -public: - HandleElementViewFriend() : HandleElementView(){}; - QRectF rectangle() const { return m_rectangle; }; - QPen pen() const { return m_pen; }; - QBrush brush() const { return m_brush; }; -}; - -class RoughnessElementViewFriend : public RoughnessElementView { -public: - RoughnessElementViewFriend() : RoughnessElementView(){}; - QPainterPath leftPath() const { return m_left_path; }; - QPainterPath rightPath() const { return m_right_path; }; - QPen pen() const { return m_pen; }; - QBrush brush() const { return m_brush; }; -}; - -//! Tests of LayerViewController. -class LayerElementTest : public ::testing::Test { -public: - ~LayerElementTest(); - struct TestData { - LayerElementItem* exposed_layer_item{nullptr}; - LayerElementControllerFriend* exposed_controller{nullptr}; - - SampleModel* sample_model{nullptr}; - MaterialModel* material_model{nullptr}; - SLDElementModel* view_model{nullptr}; - - LayerElementItem* above_layer_item{nullptr}; - LayerElementItem* middle_layer_item{nullptr}; - LayerElementItem* below_layer_item{nullptr}; - - LayerElementController* above_controller{nullptr}; - LayerElementController* middle_controller{nullptr}; - LayerElementController* below_controller{nullptr}; - - GraphicsScene* scene{nullptr}; - - TestData() - { - exposed_layer_item = new LayerElementItem(); - exposed_controller = new LayerElementControllerFriend(exposed_layer_item); - - sample_model = new SampleModel(); - view_model = new SLDElementModel(); - above_layer_item = view_model->addLayer(); - middle_layer_item = view_model->addLayer(); - below_layer_item = view_model->addLayer(); - - // FIXME multiple memory leakages - above_controller = new LayerElementController(above_layer_item); - middle_controller = new LayerElementController(middle_layer_item); - below_controller = new LayerElementController(below_layer_item); - - scene = new GraphicsScene(); - } - }; -}; - -LayerElementTest::~LayerElementTest() = default; - -//! Test the creation of the controller - -TEST_F(LayerElementTest, initialstate) -{ - TestData test_data; - - //! Check the layer item system init - EXPECT_NE(nullptr, test_data.middle_controller->layerElementItem()); - - //! Check the layer system init - EXPECT_EQ(nullptr, test_data.middle_controller->layerAbove()); - EXPECT_EQ(nullptr, test_data.middle_controller->layerBelow()); - - //! Check the segment system init - EXPECT_EQ(nullptr, test_data.middle_controller->topSegment()); - EXPECT_EQ(nullptr, test_data.middle_controller->sideSegment()); - - //! Check the scene init - EXPECT_EQ(nullptr, test_data.middle_controller->scene()); - - //! Check the handles init - EXPECT_EQ(nullptr, test_data.middle_controller->firstSegmentHandle()); - EXPECT_EQ(nullptr, test_data.middle_controller->secondSegmentHandle()); - - //! Check the roughness init - EXPECT_EQ(nullptr, test_data.middle_controller->roughness()); - - //! Check the roughness handles init - EXPECT_EQ(nullptr, test_data.middle_controller->leftRoughnessHandle()); - EXPECT_EQ(nullptr, test_data.middle_controller->rightRoughnessHandle()); -} - -TEST_F(LayerElementTest, autopopulate) -{ - TestData test_data; - test_data.middle_controller->autoPopulate(); - - //! Check the layer item system init - EXPECT_NE(nullptr, test_data.middle_controller->layerElementItem()); - - //! Check the layer system init - EXPECT_EQ(nullptr, test_data.middle_controller->layerAbove()); - EXPECT_EQ(nullptr, test_data.middle_controller->layerBelow()); - - //! Check the segment system init - EXPECT_NE(nullptr, test_data.middle_controller->topSegment()); - EXPECT_NE(nullptr, test_data.middle_controller->sideSegment()); - - //! Check the scene init - EXPECT_EQ(nullptr, test_data.middle_controller->scene()); - - //! Check the handles init - EXPECT_NE(nullptr, test_data.middle_controller->firstSegmentHandle()); - EXPECT_NE(nullptr, test_data.middle_controller->secondSegmentHandle()); - - //! Check the roughness init - EXPECT_NE(nullptr, test_data.middle_controller->roughness()); - - //! Check the roughness handles init - EXPECT_NE(nullptr, test_data.middle_controller->leftRoughnessHandle()); - EXPECT_NE(nullptr, test_data.middle_controller->rightRoughnessHandle()); -} - -TEST_F(LayerElementTest, addremoveabovebelow) -{ - TestData test_data; - - EXPECT_EQ(nullptr, test_data.above_controller->layerAbove()); - EXPECT_EQ(nullptr, test_data.above_controller->layerBelow()); - EXPECT_EQ(nullptr, test_data.middle_controller->layerAbove()); - EXPECT_EQ(nullptr, test_data.middle_controller->layerBelow()); - EXPECT_EQ(nullptr, test_data.below_controller->layerAbove()); - EXPECT_EQ(nullptr, test_data.below_controller->layerBelow()); - - test_data.middle_controller->setLayerAbove(test_data.above_controller); - EXPECT_EQ(nullptr, test_data.above_controller->layerAbove()); - EXPECT_EQ(test_data.middle_controller, test_data.above_controller->layerBelow()); - EXPECT_EQ(test_data.above_controller, test_data.middle_controller->layerAbove()); - EXPECT_EQ(nullptr, test_data.middle_controller->layerBelow()); - EXPECT_EQ(nullptr, test_data.below_controller->layerAbove()); - EXPECT_EQ(nullptr, test_data.below_controller->layerBelow()); - - test_data.middle_controller->setLayerBelow(test_data.below_controller); - EXPECT_EQ(nullptr, test_data.above_controller->layerAbove()); - EXPECT_EQ(test_data.middle_controller, test_data.above_controller->layerBelow()); - EXPECT_EQ(test_data.above_controller, test_data.middle_controller->layerAbove()); - EXPECT_EQ(test_data.below_controller, test_data.middle_controller->layerBelow()); - EXPECT_EQ(test_data.middle_controller, test_data.below_controller->layerAbove()); - EXPECT_EQ(nullptr, test_data.below_controller->layerBelow()); - - test_data.middle_controller->unsetLayerAbove(); - EXPECT_EQ(nullptr, test_data.above_controller->layerAbove()); - EXPECT_EQ(nullptr, test_data.above_controller->layerBelow()); - EXPECT_EQ(nullptr, test_data.middle_controller->layerAbove()); - EXPECT_EQ(test_data.below_controller, test_data.middle_controller->layerBelow()); - EXPECT_EQ(test_data.middle_controller, test_data.below_controller->layerAbove()); - EXPECT_EQ(nullptr, test_data.below_controller->layerBelow()); - - test_data.middle_controller->unsetLayerBelow(); - EXPECT_EQ(nullptr, test_data.above_controller->layerAbove()); - EXPECT_EQ(nullptr, test_data.above_controller->layerBelow()); - EXPECT_EQ(nullptr, test_data.middle_controller->layerAbove()); - EXPECT_EQ(nullptr, test_data.middle_controller->layerBelow()); - EXPECT_EQ(nullptr, test_data.below_controller->layerAbove()); - EXPECT_EQ(nullptr, test_data.below_controller->layerBelow()); -} - -TEST_F(LayerElementTest, testgeometriesupdate) -{ - TestData test_data; - - // Grab the item to change the properties - LayerElementItem* item = test_data.exposed_controller->layerElementItem(); - - double side_thickness = 5.; - double top_thickness = 5.; - double handle_radius = 5.; - double r_handle_radius = 5.; - double roughness = 5.; - double pos = 10.; - double width = 20.; - double height = 30.; - - item->setProperty(LayerElementItem::P_X_POS, pos); - item->setProperty(LayerElementItem::P_WIDTH, width); - item->setProperty(LayerElementItem::P_HEIGHT, height); - item->setProperty(LayerElementItem::P_ROUGHNESS, roughness); - item->setProperty(LayerElementItem::P_SIDE_THICKNESS, side_thickness); - item->setProperty(LayerElementItem::P_TOP_THICKNESS, top_thickness); - item->setProperty(LayerElementItem::P_HANDLE_RADIUS, handle_radius); - item->setProperty(LayerElementItem::P_R_HANDLE_RADIUS, r_handle_radius); - - // Init rectangle vars - QRectF side_rect; - QRectF top_rect; - QRectF first_handle_rect; - QRectF second_handle_rect; - QPainterPath left_roughness_path; - QPainterPath right_roughness_path; - QRectF left_roughness_handle_rec; - QRectF right_roughness_handle_rec; - - // #############################################################################s - // Check initial state - side_rect = test_data.exposed_controller->pubSideSegmentRect(); - top_rect = test_data.exposed_controller->pubTopSegmentRect(); - first_handle_rect = test_data.exposed_controller->pubFirstSegmentHandleRect(); - second_handle_rect = test_data.exposed_controller->pubSecondSegmentHandleRect(); - left_roughness_path = test_data.exposed_controller->pubLeftRoughnessPath(); - right_roughness_path = test_data.exposed_controller->pubRightRoughnessPath(); - left_roughness_handle_rec = test_data.exposed_controller->pubLeftRoughnessHandleRect(); - right_roughness_handle_rec = test_data.exposed_controller->pubRightRoughnessHandleRect(); - - EXPECT_DOUBLE_EQ(pos - side_thickness / 2., side_rect.x()); - EXPECT_DOUBLE_EQ(0., side_rect.y()); - EXPECT_DOUBLE_EQ(side_thickness, side_rect.width()); - EXPECT_DOUBLE_EQ(height, side_rect.height()); - - EXPECT_DOUBLE_EQ(pos, top_rect.x()); - EXPECT_DOUBLE_EQ(height - top_thickness / 2., top_rect.y()); - EXPECT_DOUBLE_EQ(width, top_rect.width()); - EXPECT_DOUBLE_EQ(top_thickness, top_rect.height()); - - EXPECT_DOUBLE_EQ(pos - handle_radius, first_handle_rect.x()); - EXPECT_DOUBLE_EQ(-handle_radius, first_handle_rect.y()); - EXPECT_DOUBLE_EQ(2 * handle_radius, first_handle_rect.width()); - EXPECT_DOUBLE_EQ(2 * handle_radius, first_handle_rect.height()); - - EXPECT_DOUBLE_EQ(pos - handle_radius, second_handle_rect.x()); - EXPECT_DOUBLE_EQ(height - handle_radius, second_handle_rect.y()); - EXPECT_DOUBLE_EQ(2 * handle_radius, second_handle_rect.width()); - EXPECT_DOUBLE_EQ(2 * handle_radius, second_handle_rect.height()); - - EXPECT_DOUBLE_EQ(pos - roughness, left_roughness_path.boundingRect().x()); - EXPECT_DOUBLE_EQ(0., left_roughness_path.boundingRect().y()); - EXPECT_DOUBLE_EQ(roughness, left_roughness_path.boundingRect().width()); - EXPECT_DOUBLE_EQ(height, left_roughness_path.boundingRect().height()); - - EXPECT_DOUBLE_EQ(pos, right_roughness_path.boundingRect().x()); - EXPECT_DOUBLE_EQ(0., right_roughness_path.boundingRect().y()); - EXPECT_DOUBLE_EQ(roughness, right_roughness_path.boundingRect().width()); - EXPECT_DOUBLE_EQ(height, right_roughness_path.boundingRect().height()); - - EXPECT_DOUBLE_EQ(pos - roughness - r_handle_radius, left_roughness_handle_rec.x()); - EXPECT_DOUBLE_EQ(height / 2 - r_handle_radius, left_roughness_handle_rec.y()); - EXPECT_DOUBLE_EQ(2 * r_handle_radius, left_roughness_handle_rec.width()); - EXPECT_DOUBLE_EQ(2 * r_handle_radius, left_roughness_handle_rec.height()); - - EXPECT_DOUBLE_EQ(pos + roughness - r_handle_radius, right_roughness_handle_rec.x()); - EXPECT_DOUBLE_EQ(height / 2 - r_handle_radius, right_roughness_handle_rec.y()); - EXPECT_DOUBLE_EQ(2 * r_handle_radius, right_roughness_handle_rec.width()); - EXPECT_DOUBLE_EQ(2 * r_handle_radius, right_roughness_handle_rec.height()); - - // #############################################################################s - // Check x move - pos = 22.; - item->setProperty(LayerElementItem::P_X_POS, pos); - side_rect = test_data.exposed_controller->pubSideSegmentRect(); - top_rect = test_data.exposed_controller->pubTopSegmentRect(); - first_handle_rect = test_data.exposed_controller->pubFirstSegmentHandleRect(); - second_handle_rect = test_data.exposed_controller->pubSecondSegmentHandleRect(); - left_roughness_path = test_data.exposed_controller->pubLeftRoughnessPath(); - right_roughness_path = test_data.exposed_controller->pubRightRoughnessPath(); - left_roughness_handle_rec = test_data.exposed_controller->pubLeftRoughnessHandleRect(); - right_roughness_handle_rec = test_data.exposed_controller->pubRightRoughnessHandleRect(); - - EXPECT_DOUBLE_EQ(pos - side_thickness / 2., side_rect.x()); - EXPECT_DOUBLE_EQ(0., side_rect.y()); - EXPECT_DOUBLE_EQ(side_thickness, side_rect.width()); - EXPECT_DOUBLE_EQ(height, side_rect.height()); - - EXPECT_DOUBLE_EQ(pos, top_rect.x()); - EXPECT_DOUBLE_EQ(height - top_thickness / 2., top_rect.y()); - EXPECT_DOUBLE_EQ(width, top_rect.width()); - EXPECT_DOUBLE_EQ(top_thickness, top_rect.height()); - - EXPECT_DOUBLE_EQ(pos - handle_radius, first_handle_rect.x()); - EXPECT_DOUBLE_EQ(-handle_radius, first_handle_rect.y()); - EXPECT_DOUBLE_EQ(2 * handle_radius, first_handle_rect.width()); - EXPECT_DOUBLE_EQ(2 * handle_radius, first_handle_rect.height()); - - EXPECT_DOUBLE_EQ(pos - handle_radius, second_handle_rect.x()); - EXPECT_DOUBLE_EQ(height - handle_radius, second_handle_rect.y()); - EXPECT_DOUBLE_EQ(2 * handle_radius, second_handle_rect.width()); - EXPECT_DOUBLE_EQ(2 * handle_radius, second_handle_rect.height()); - - EXPECT_DOUBLE_EQ(pos - roughness, left_roughness_path.boundingRect().x()); - EXPECT_DOUBLE_EQ(0., left_roughness_path.boundingRect().y()); - EXPECT_DOUBLE_EQ(roughness, left_roughness_path.boundingRect().width()); - EXPECT_DOUBLE_EQ(height, left_roughness_path.boundingRect().height()); - - EXPECT_DOUBLE_EQ(pos, right_roughness_path.boundingRect().x()); - EXPECT_DOUBLE_EQ(0., right_roughness_path.boundingRect().y()); - EXPECT_DOUBLE_EQ(roughness, right_roughness_path.boundingRect().width()); - EXPECT_DOUBLE_EQ(height, right_roughness_path.boundingRect().height()); - - EXPECT_DOUBLE_EQ(pos - roughness - r_handle_radius, left_roughness_handle_rec.x()); - EXPECT_DOUBLE_EQ(height / 2 - r_handle_radius, left_roughness_handle_rec.y()); - EXPECT_DOUBLE_EQ(2 * r_handle_radius, left_roughness_handle_rec.width()); - EXPECT_DOUBLE_EQ(2 * r_handle_radius, left_roughness_handle_rec.height()); - - EXPECT_DOUBLE_EQ(pos + roughness - r_handle_radius, right_roughness_handle_rec.x()); - EXPECT_DOUBLE_EQ(height / 2 - r_handle_radius, right_roughness_handle_rec.y()); - EXPECT_DOUBLE_EQ(2 * r_handle_radius, right_roughness_handle_rec.width()); - EXPECT_DOUBLE_EQ(2 * r_handle_radius, right_roughness_handle_rec.height()); - - // #############################################################################s - // Check width change - width = 22.; - item->setProperty(LayerElementItem::P_WIDTH, width); - side_rect = test_data.exposed_controller->pubSideSegmentRect(); - top_rect = test_data.exposed_controller->pubTopSegmentRect(); - first_handle_rect = test_data.exposed_controller->pubFirstSegmentHandleRect(); - second_handle_rect = test_data.exposed_controller->pubSecondSegmentHandleRect(); - left_roughness_path = test_data.exposed_controller->pubLeftRoughnessPath(); - right_roughness_path = test_data.exposed_controller->pubRightRoughnessPath(); - left_roughness_handle_rec = test_data.exposed_controller->pubLeftRoughnessHandleRect(); - right_roughness_handle_rec = test_data.exposed_controller->pubRightRoughnessHandleRect(); - - EXPECT_DOUBLE_EQ(pos - side_thickness / 2., side_rect.x()); - EXPECT_DOUBLE_EQ(0., side_rect.y()); - EXPECT_DOUBLE_EQ(side_thickness, side_rect.width()); - EXPECT_DOUBLE_EQ(height, side_rect.height()); - - EXPECT_DOUBLE_EQ(pos, top_rect.x()); - EXPECT_DOUBLE_EQ(height - top_thickness / 2., top_rect.y()); - EXPECT_DOUBLE_EQ(width, top_rect.width()); - EXPECT_DOUBLE_EQ(top_thickness, top_rect.height()); - - EXPECT_DOUBLE_EQ(pos - handle_radius, first_handle_rect.x()); - EXPECT_DOUBLE_EQ(-handle_radius, first_handle_rect.y()); - EXPECT_DOUBLE_EQ(2 * handle_radius, first_handle_rect.width()); - EXPECT_DOUBLE_EQ(2 * handle_radius, first_handle_rect.height()); - - EXPECT_DOUBLE_EQ(pos - handle_radius, second_handle_rect.x()); - EXPECT_DOUBLE_EQ(height - handle_radius, second_handle_rect.y()); - EXPECT_DOUBLE_EQ(2 * handle_radius, second_handle_rect.width()); - EXPECT_DOUBLE_EQ(2 * handle_radius, second_handle_rect.height()); - - EXPECT_DOUBLE_EQ(pos - roughness, left_roughness_path.boundingRect().x()); - EXPECT_DOUBLE_EQ(0., left_roughness_path.boundingRect().y()); - EXPECT_DOUBLE_EQ(roughness, left_roughness_path.boundingRect().width()); - EXPECT_DOUBLE_EQ(height, left_roughness_path.boundingRect().height()); - - EXPECT_DOUBLE_EQ(pos, right_roughness_path.boundingRect().x()); - EXPECT_DOUBLE_EQ(0., right_roughness_path.boundingRect().y()); - EXPECT_DOUBLE_EQ(roughness, right_roughness_path.boundingRect().width()); - EXPECT_DOUBLE_EQ(height, right_roughness_path.boundingRect().height()); - - EXPECT_DOUBLE_EQ(pos - roughness - r_handle_radius, left_roughness_handle_rec.x()); - EXPECT_DOUBLE_EQ(height / 2 - r_handle_radius, left_roughness_handle_rec.y()); - EXPECT_DOUBLE_EQ(2 * r_handle_radius, left_roughness_handle_rec.width()); - EXPECT_DOUBLE_EQ(2 * r_handle_radius, left_roughness_handle_rec.height()); - - EXPECT_DOUBLE_EQ(pos + roughness - r_handle_radius, right_roughness_handle_rec.x()); - EXPECT_DOUBLE_EQ(height / 2 - r_handle_radius, right_roughness_handle_rec.y()); - EXPECT_DOUBLE_EQ(2 * r_handle_radius, right_roughness_handle_rec.width()); - EXPECT_DOUBLE_EQ(2 * r_handle_radius, right_roughness_handle_rec.height()); - - // #############################################################################s - // Check heigh change - height = 35.; - item->setProperty(LayerElementItem::P_HEIGHT, height); - side_rect = test_data.exposed_controller->pubSideSegmentRect(); - top_rect = test_data.exposed_controller->pubTopSegmentRect(); - first_handle_rect = test_data.exposed_controller->pubFirstSegmentHandleRect(); - second_handle_rect = test_data.exposed_controller->pubSecondSegmentHandleRect(); - left_roughness_path = test_data.exposed_controller->pubLeftRoughnessPath(); - right_roughness_path = test_data.exposed_controller->pubRightRoughnessPath(); - left_roughness_handle_rec = test_data.exposed_controller->pubLeftRoughnessHandleRect(); - right_roughness_handle_rec = test_data.exposed_controller->pubRightRoughnessHandleRect(); - - EXPECT_DOUBLE_EQ(pos - side_thickness / 2., side_rect.x()); - EXPECT_DOUBLE_EQ(0., side_rect.y()); - EXPECT_DOUBLE_EQ(side_thickness, side_rect.width()); - EXPECT_DOUBLE_EQ(height, side_rect.height()); - - EXPECT_DOUBLE_EQ(pos, top_rect.x()); - EXPECT_DOUBLE_EQ(height - top_thickness / 2., top_rect.y()); - EXPECT_DOUBLE_EQ(width, top_rect.width()); - EXPECT_DOUBLE_EQ(top_thickness, top_rect.height()); - - EXPECT_DOUBLE_EQ(pos - handle_radius, first_handle_rect.x()); - EXPECT_DOUBLE_EQ(-handle_radius, first_handle_rect.y()); - EXPECT_DOUBLE_EQ(2 * handle_radius, first_handle_rect.width()); - EXPECT_DOUBLE_EQ(2 * handle_radius, first_handle_rect.height()); - - EXPECT_DOUBLE_EQ(pos - handle_radius, second_handle_rect.x()); - EXPECT_DOUBLE_EQ(height - handle_radius, second_handle_rect.y()); - EXPECT_DOUBLE_EQ(2 * handle_radius, second_handle_rect.width()); - EXPECT_DOUBLE_EQ(2 * handle_radius, second_handle_rect.height()); - - EXPECT_DOUBLE_EQ(pos - roughness, left_roughness_path.boundingRect().x()); - EXPECT_DOUBLE_EQ(0., left_roughness_path.boundingRect().y()); - EXPECT_DOUBLE_EQ(roughness, left_roughness_path.boundingRect().width()); - EXPECT_DOUBLE_EQ(height, left_roughness_path.boundingRect().height()); - - EXPECT_DOUBLE_EQ(pos, right_roughness_path.boundingRect().x()); - EXPECT_DOUBLE_EQ(0., right_roughness_path.boundingRect().y()); - EXPECT_DOUBLE_EQ(roughness, right_roughness_path.boundingRect().width()); - EXPECT_DOUBLE_EQ(height, right_roughness_path.boundingRect().height()); - - EXPECT_DOUBLE_EQ(pos - roughness - r_handle_radius, left_roughness_handle_rec.x()); - EXPECT_DOUBLE_EQ(height / 2 - r_handle_radius, left_roughness_handle_rec.y()); - EXPECT_DOUBLE_EQ(2 * r_handle_radius, left_roughness_handle_rec.width()); - EXPECT_DOUBLE_EQ(2 * r_handle_radius, left_roughness_handle_rec.height()); - - EXPECT_DOUBLE_EQ(pos + roughness - r_handle_radius, right_roughness_handle_rec.x()); - EXPECT_DOUBLE_EQ(height / 2 - r_handle_radius, right_roughness_handle_rec.y()); - EXPECT_DOUBLE_EQ(2 * r_handle_radius, right_roughness_handle_rec.width()); - EXPECT_DOUBLE_EQ(2 * r_handle_radius, right_roughness_handle_rec.height()); -} - -TEST_F(LayerElementTest, testviewsupdate) -{ - TestData test_data; - - // Connect the controller to the model - test_data.middle_controller->connectToModel(); - - // Set the segments - auto side_segment = new SegmentElementViewFriend(); - auto top_segment = new SegmentElementViewFriend(); - test_data.middle_controller->setSideSegment(side_segment); - test_data.middle_controller->setTopSegment(top_segment); - - // Set the handles - auto first_handle = new HandleElementViewFriend(); - auto second_handle = new HandleElementViewFriend(); - test_data.middle_controller->setSegmentHandles(first_handle, second_handle); - - // Set the roughness view item - auto roughness_view = new RoughnessElementViewFriend(); - test_data.middle_controller->setRoughness(roughness_view); - - // Set the roughness handles - auto left_handle = new HandleElementViewFriend(); - auto right_handle = new HandleElementViewFriend(); - test_data.middle_controller->setRoughnessHandles(left_handle, right_handle); - - // Grab the item to change the properties - LayerElementItem* item = test_data.middle_controller->layerElementItem(); - - double side_thickness = 5.; - double top_thickness = 5.; - double handle_radius = 5.; - double r_handle_radius = 5.; - double roughness = 5.; - double pos = 10.; - double width = 20.; - double height = 30.; - - item->setProperty(LayerElementItem::P_X_POS, pos); - item->setProperty(LayerElementItem::P_WIDTH, width); - item->setProperty(LayerElementItem::P_HEIGHT, height); - item->setProperty(LayerElementItem::P_ROUGHNESS, roughness); - item->setProperty(LayerElementItem::P_SIDE_THICKNESS, side_thickness); - item->setProperty(LayerElementItem::P_TOP_THICKNESS, top_thickness); - item->setProperty(LayerElementItem::P_HANDLE_RADIUS, handle_radius); - item->setProperty(LayerElementItem::P_R_HANDLE_RADIUS, r_handle_radius); - - // Init rectangle vars - QRectF side_rect; - QRectF top_rect; - QRectF first_handle_rect; - QRectF second_handle_rect; - QPainterPath left_roughness_path; - QPainterPath right_roughness_path; - QRectF left_roughness_handle_rec; - QRectF right_roughness_handle_rec; - - // #############################################################################s - // Check initial state - side_rect = side_segment->rectangle(); - top_rect = top_segment->rectangle(); - first_handle_rect = first_handle->rectangle(); - second_handle_rect = second_handle->rectangle(); - left_roughness_path = roughness_view->leftPath(); - right_roughness_path = roughness_view->rightPath(); - left_roughness_handle_rec = left_handle->rectangle(); - right_roughness_handle_rec = right_handle->rectangle(); - - EXPECT_DOUBLE_EQ(pos - side_thickness / 2., side_rect.x()); - EXPECT_DOUBLE_EQ(0., side_rect.y()); - EXPECT_DOUBLE_EQ(side_thickness, side_rect.width()); - EXPECT_DOUBLE_EQ(height, side_rect.height()); - - EXPECT_DOUBLE_EQ(pos, top_rect.x()); - EXPECT_DOUBLE_EQ(height - top_thickness / 2., top_rect.y()); - EXPECT_DOUBLE_EQ(width, top_rect.width()); - EXPECT_DOUBLE_EQ(top_thickness, top_rect.height()); - - EXPECT_DOUBLE_EQ(pos - handle_radius, first_handle_rect.x()); - EXPECT_DOUBLE_EQ(-handle_radius, first_handle_rect.y()); - EXPECT_DOUBLE_EQ(2 * handle_radius, first_handle_rect.width()); - EXPECT_DOUBLE_EQ(2 * handle_radius, first_handle_rect.height()); - - EXPECT_DOUBLE_EQ(pos - handle_radius, second_handle_rect.x()); - EXPECT_DOUBLE_EQ(height - handle_radius, second_handle_rect.y()); - EXPECT_DOUBLE_EQ(2 * handle_radius, second_handle_rect.width()); - EXPECT_DOUBLE_EQ(2 * handle_radius, second_handle_rect.height()); - - EXPECT_DOUBLE_EQ(pos - roughness, left_roughness_path.boundingRect().x()); - EXPECT_DOUBLE_EQ(0., left_roughness_path.boundingRect().y()); - EXPECT_DOUBLE_EQ(roughness, left_roughness_path.boundingRect().width()); - EXPECT_DOUBLE_EQ(height, left_roughness_path.boundingRect().height()); - - EXPECT_DOUBLE_EQ(pos, right_roughness_path.boundingRect().x()); - EXPECT_DOUBLE_EQ(0., right_roughness_path.boundingRect().y()); - EXPECT_DOUBLE_EQ(roughness, right_roughness_path.boundingRect().width()); - EXPECT_DOUBLE_EQ(height, right_roughness_path.boundingRect().height()); - - EXPECT_DOUBLE_EQ(pos - roughness - r_handle_radius, left_roughness_handle_rec.x()); - EXPECT_DOUBLE_EQ(height / 2 - r_handle_radius, left_roughness_handle_rec.y()); - EXPECT_DOUBLE_EQ(2 * r_handle_radius, left_roughness_handle_rec.width()); - EXPECT_DOUBLE_EQ(2 * r_handle_radius, left_roughness_handle_rec.height()); - - EXPECT_DOUBLE_EQ(pos + roughness - r_handle_radius, right_roughness_handle_rec.x()); - EXPECT_DOUBLE_EQ(height / 2 - r_handle_radius, right_roughness_handle_rec.y()); - EXPECT_DOUBLE_EQ(2 * r_handle_radius, right_roughness_handle_rec.width()); - EXPECT_DOUBLE_EQ(2 * r_handle_radius, right_roughness_handle_rec.height()); - - // #############################################################################s - // Test change of position - pos = 22.; - item->setProperty(LayerElementItem::P_X_POS, pos); - top_rect = top_segment->rectangle(); - side_rect = side_segment->rectangle(); - first_handle_rect = first_handle->rectangle(); - second_handle_rect = second_handle->rectangle(); - left_roughness_path = roughness_view->leftPath(); - right_roughness_path = roughness_view->rightPath(); - left_roughness_handle_rec = left_handle->rectangle(); - right_roughness_handle_rec = right_handle->rectangle(); - - EXPECT_DOUBLE_EQ(pos - side_thickness / 2., side_rect.x()); - EXPECT_DOUBLE_EQ(0., side_rect.y()); - EXPECT_DOUBLE_EQ(side_thickness, side_rect.width()); - EXPECT_DOUBLE_EQ(height, side_rect.height()); - - EXPECT_DOUBLE_EQ(pos, top_rect.x()); - EXPECT_DOUBLE_EQ(height - top_thickness / 2., top_rect.y()); - EXPECT_DOUBLE_EQ(width, top_rect.width()); - EXPECT_DOUBLE_EQ(top_thickness, top_rect.height()); - - EXPECT_DOUBLE_EQ(pos - handle_radius, first_handle_rect.x()); - EXPECT_DOUBLE_EQ(-handle_radius, first_handle_rect.y()); - EXPECT_DOUBLE_EQ(2 * handle_radius, first_handle_rect.width()); - EXPECT_DOUBLE_EQ(2 * handle_radius, first_handle_rect.height()); - - EXPECT_DOUBLE_EQ(pos - handle_radius, second_handle_rect.x()); - EXPECT_DOUBLE_EQ(height - handle_radius, second_handle_rect.y()); - EXPECT_DOUBLE_EQ(2 * handle_radius, second_handle_rect.width()); - EXPECT_DOUBLE_EQ(2 * handle_radius, second_handle_rect.height()); - - EXPECT_DOUBLE_EQ(pos - roughness, left_roughness_path.boundingRect().x()); - EXPECT_DOUBLE_EQ(0., left_roughness_path.boundingRect().y()); - EXPECT_DOUBLE_EQ(roughness, left_roughness_path.boundingRect().width()); - EXPECT_DOUBLE_EQ(height, left_roughness_path.boundingRect().height()); - - EXPECT_DOUBLE_EQ(pos, right_roughness_path.boundingRect().x()); - EXPECT_DOUBLE_EQ(0., right_roughness_path.boundingRect().y()); - EXPECT_DOUBLE_EQ(roughness, right_roughness_path.boundingRect().width()); - EXPECT_DOUBLE_EQ(height, right_roughness_path.boundingRect().height()); - - EXPECT_DOUBLE_EQ(pos - roughness - r_handle_radius, left_roughness_handle_rec.x()); - EXPECT_DOUBLE_EQ(height / 2 - r_handle_radius, left_roughness_handle_rec.y()); - EXPECT_DOUBLE_EQ(2 * r_handle_radius, left_roughness_handle_rec.width()); - EXPECT_DOUBLE_EQ(2 * r_handle_radius, left_roughness_handle_rec.height()); - - EXPECT_DOUBLE_EQ(pos + roughness - r_handle_radius, right_roughness_handle_rec.x()); - EXPECT_DOUBLE_EQ(height / 2 - r_handle_radius, right_roughness_handle_rec.y()); - EXPECT_DOUBLE_EQ(2 * r_handle_radius, right_roughness_handle_rec.width()); - EXPECT_DOUBLE_EQ(2 * r_handle_radius, right_roughness_handle_rec.height()); - - // #############################################################################s - // Test change of width - width = 22.; - item->setProperty(LayerElementItem::P_WIDTH, width); - top_rect = top_segment->rectangle(); - side_rect = side_segment->rectangle(); - first_handle_rect = first_handle->rectangle(); - second_handle_rect = second_handle->rectangle(); - left_roughness_path = roughness_view->leftPath(); - right_roughness_path = roughness_view->rightPath(); - left_roughness_handle_rec = left_handle->rectangle(); - right_roughness_handle_rec = right_handle->rectangle(); - - EXPECT_DOUBLE_EQ(pos - side_thickness / 2., side_rect.x()); - EXPECT_DOUBLE_EQ(0., side_rect.y()); - EXPECT_DOUBLE_EQ(side_thickness, side_rect.width()); - EXPECT_DOUBLE_EQ(height, side_rect.height()); - - EXPECT_DOUBLE_EQ(pos, top_rect.x()); - EXPECT_DOUBLE_EQ(height - top_thickness / 2., top_rect.y()); - EXPECT_DOUBLE_EQ(width, top_rect.width()); - EXPECT_DOUBLE_EQ(top_thickness, top_rect.height()); - - EXPECT_DOUBLE_EQ(pos - handle_radius, first_handle_rect.x()); - EXPECT_DOUBLE_EQ(-handle_radius, first_handle_rect.y()); - EXPECT_DOUBLE_EQ(2 * handle_radius, first_handle_rect.width()); - EXPECT_DOUBLE_EQ(2 * handle_radius, first_handle_rect.height()); - - EXPECT_DOUBLE_EQ(pos - handle_radius, second_handle_rect.x()); - EXPECT_DOUBLE_EQ(height - handle_radius, second_handle_rect.y()); - EXPECT_DOUBLE_EQ(2 * handle_radius, second_handle_rect.width()); - EXPECT_DOUBLE_EQ(2 * handle_radius, second_handle_rect.height()); - - EXPECT_DOUBLE_EQ(pos - roughness, left_roughness_path.boundingRect().x()); - EXPECT_DOUBLE_EQ(0., left_roughness_path.boundingRect().y()); - EXPECT_DOUBLE_EQ(roughness, left_roughness_path.boundingRect().width()); - EXPECT_DOUBLE_EQ(height, left_roughness_path.boundingRect().height()); - - EXPECT_DOUBLE_EQ(pos, right_roughness_path.boundingRect().x()); - EXPECT_DOUBLE_EQ(0., right_roughness_path.boundingRect().y()); - EXPECT_DOUBLE_EQ(roughness, right_roughness_path.boundingRect().width()); - EXPECT_DOUBLE_EQ(height, right_roughness_path.boundingRect().height()); - - EXPECT_DOUBLE_EQ(pos - roughness - r_handle_radius, left_roughness_handle_rec.x()); - EXPECT_DOUBLE_EQ(height / 2 - r_handle_radius, left_roughness_handle_rec.y()); - EXPECT_DOUBLE_EQ(2 * r_handle_radius, left_roughness_handle_rec.width()); - EXPECT_DOUBLE_EQ(2 * r_handle_radius, left_roughness_handle_rec.height()); - - EXPECT_DOUBLE_EQ(pos + roughness - r_handle_radius, right_roughness_handle_rec.x()); - EXPECT_DOUBLE_EQ(height / 2 - r_handle_radius, right_roughness_handle_rec.y()); - EXPECT_DOUBLE_EQ(2 * r_handle_radius, right_roughness_handle_rec.width()); - EXPECT_DOUBLE_EQ(2 * r_handle_radius, right_roughness_handle_rec.height()); - - // #############################################################################s - // Test propagation change of height - height = 35.; - item->setProperty(LayerElementItem::P_HEIGHT, height); - top_rect = top_segment->rectangle(); - side_rect = side_segment->rectangle(); - first_handle_rect = first_handle->rectangle(); - second_handle_rect = second_handle->rectangle(); - left_roughness_path = roughness_view->leftPath(); - right_roughness_path = roughness_view->rightPath(); - left_roughness_handle_rec = left_handle->rectangle(); - right_roughness_handle_rec = right_handle->rectangle(); - - EXPECT_DOUBLE_EQ(pos - side_thickness / 2., side_rect.x()); - EXPECT_DOUBLE_EQ(0., side_rect.y()); - EXPECT_DOUBLE_EQ(side_thickness, side_rect.width()); - EXPECT_DOUBLE_EQ(height, side_rect.height()); - - EXPECT_DOUBLE_EQ(pos, top_rect.x()); - EXPECT_DOUBLE_EQ(height - top_thickness / 2., top_rect.y()); - EXPECT_DOUBLE_EQ(width, top_rect.width()); - EXPECT_DOUBLE_EQ(top_thickness, top_rect.height()); - - EXPECT_DOUBLE_EQ(pos - handle_radius, first_handle_rect.x()); - EXPECT_DOUBLE_EQ(-handle_radius, first_handle_rect.y()); - EXPECT_DOUBLE_EQ(2 * handle_radius, first_handle_rect.width()); - EXPECT_DOUBLE_EQ(2 * handle_radius, first_handle_rect.height()); - - EXPECT_DOUBLE_EQ(pos - handle_radius, second_handle_rect.x()); - EXPECT_DOUBLE_EQ(height - handle_radius, second_handle_rect.y()); - EXPECT_DOUBLE_EQ(2 * handle_radius, second_handle_rect.width()); - EXPECT_DOUBLE_EQ(2 * handle_radius, second_handle_rect.height()); - - EXPECT_DOUBLE_EQ(pos - roughness, left_roughness_path.boundingRect().x()); - EXPECT_DOUBLE_EQ(0., left_roughness_path.boundingRect().y()); - EXPECT_DOUBLE_EQ(roughness, left_roughness_path.boundingRect().width()); - EXPECT_DOUBLE_EQ(height, left_roughness_path.boundingRect().height()); - - EXPECT_DOUBLE_EQ(pos, right_roughness_path.boundingRect().x()); - EXPECT_DOUBLE_EQ(0., right_roughness_path.boundingRect().y()); - EXPECT_DOUBLE_EQ(roughness, right_roughness_path.boundingRect().width()); - EXPECT_DOUBLE_EQ(height, right_roughness_path.boundingRect().height()); - - EXPECT_DOUBLE_EQ(pos - roughness - r_handle_radius, left_roughness_handle_rec.x()); - EXPECT_DOUBLE_EQ(height / 2 - r_handle_radius, left_roughness_handle_rec.y()); - EXPECT_DOUBLE_EQ(2 * r_handle_radius, left_roughness_handle_rec.width()); - EXPECT_DOUBLE_EQ(2 * r_handle_radius, left_roughness_handle_rec.height()); - - EXPECT_DOUBLE_EQ(pos + roughness - r_handle_radius, right_roughness_handle_rec.x()); - EXPECT_DOUBLE_EQ(height / 2 - r_handle_radius, right_roughness_handle_rec.y()); - EXPECT_DOUBLE_EQ(2 * r_handle_radius, right_roughness_handle_rec.width()); - EXPECT_DOUBLE_EQ(2 * r_handle_radius, right_roughness_handle_rec.height()); -} - -TEST_F(LayerElementTest, testscene) -{ - TestData test_data; - - // Set and test the scene - test_data.middle_controller->setScene(test_data.scene); - EXPECT_EQ(test_data.scene, test_data.middle_controller->scene()); - - // Connect the controller to the model - test_data.middle_controller->connectToModel(); - - // Set the segments - auto side_segment_middle = new SegmentElementViewFriend(); - auto top_segment_middle = new SegmentElementViewFriend(); - test_data.middle_controller->setSideSegment(side_segment_middle); - test_data.middle_controller->setTopSegment(top_segment_middle); - - // Set the handles - auto first_handle = new HandleElementViewFriend(); - auto second_handle = new HandleElementViewFriend(); - test_data.middle_controller->setSegmentHandles(first_handle, second_handle); - - // Test automatic scene placement - EXPECT_EQ(test_data.scene, side_segment_middle->scene()); - EXPECT_EQ(test_data.scene, top_segment_middle->scene()); - - // Test scene removal - test_data.middle_controller->unsetScene(); - EXPECT_EQ(nullptr, side_segment_middle->scene()); - EXPECT_EQ(nullptr, top_segment_middle->scene()); - - // Test scene placement - test_data.middle_controller->setScene(test_data.scene); - EXPECT_EQ(test_data.scene, side_segment_middle->scene()); - EXPECT_EQ(test_data.scene, top_segment_middle->scene()); -} - -TEST_F(LayerElementTest, testpropagation) -{ - TestData test_data; - - auto adapter = test_data.scene->sceneAdapter(); - - // Set the scenes - test_data.above_controller->setScene(test_data.scene); - test_data.middle_controller->setScene(test_data.scene); - test_data.below_controller->setScene(test_data.scene); - - // Connect controller to the model - test_data.above_controller->connectToModel(); - test_data.middle_controller->connectToModel(); - test_data.below_controller->connectToModel(); - - // Set the associated ids (will be model ids afterwards) - test_data.above_controller->setSampleItemId("above"); - test_data.middle_controller->setSampleItemId("middle"); - test_data.below_controller->setSampleItemId("below"); - - // Set the layer above up - auto side_segment_above = new SegmentElementViewFriend(); - auto top_segment_above = new SegmentElementViewFriend(); - test_data.above_controller->setSideSegment(side_segment_above); - test_data.above_controller->setTopSegment(top_segment_above); - auto first_handle_above = new HandleElementViewFriend(); - auto second_handle_above = new HandleElementViewFriend(); - test_data.above_controller->setSegmentHandles(first_handle_above, second_handle_above); - auto roughness_view_above = new RoughnessElementViewFriend(); - test_data.middle_controller->setRoughness(roughness_view_above); - auto left_handle_above = new HandleElementViewFriend(); - auto right_handle_above = new HandleElementViewFriend(); - test_data.above_controller->setRoughnessHandles(left_handle_above, right_handle_above); - - // Set the middle layer up - auto side_segment_middle = new SegmentElementViewFriend(); - auto top_segment_middle = new SegmentElementViewFriend(); - test_data.middle_controller->setSideSegment(side_segment_middle); - test_data.middle_controller->setTopSegment(top_segment_middle); - auto first_handle_middle = new HandleElementViewFriend(); - auto second_handle_middle = new HandleElementViewFriend(); - test_data.middle_controller->setSegmentHandles(first_handle_middle, second_handle_middle); - auto roughness_view_middle = new RoughnessElementViewFriend(); - test_data.middle_controller->setRoughness(roughness_view_middle); - auto left_handle_middle = new HandleElementViewFriend(); - auto right_handle_middle = new HandleElementViewFriend(); - test_data.middle_controller->setRoughnessHandles(left_handle_middle, right_handle_middle); - - // Set the layer below up - auto side_segment_below = new SegmentElementViewFriend(); - auto top_segment_below = new SegmentElementViewFriend(); - test_data.below_controller->setSideSegment(side_segment_below); - test_data.below_controller->setTopSegment(top_segment_below); - auto first_handle_below = new HandleElementViewFriend(); - auto second_handle_below = new HandleElementViewFriend(); - test_data.below_controller->setSegmentHandles(first_handle_below, second_handle_below); - auto roughness_view_below = new RoughnessElementViewFriend(); - test_data.below_controller->setRoughness(roughness_view_below); - auto left_handle_below = new HandleElementViewFriend(); - auto right_handle_below = new HandleElementViewFriend(); - test_data.below_controller->setRoughnessHandles(left_handle_below, right_handle_below); - - // Set the layer relationships - test_data.middle_controller->setLayerAbove(test_data.above_controller); - test_data.middle_controller->setLayerBelow(test_data.below_controller); - - // Get the items of each layer - LayerElementItem* item_above = test_data.above_controller->layerElementItem(); - LayerElementItem* item_middle = test_data.middle_controller->layerElementItem(); - LayerElementItem* item_below = test_data.below_controller->layerElementItem(); - - // #############################################################################s - // Test propagation for the contruction - EXPECT_DOUBLE_EQ(0., item_above->property<double>(LayerElementItem::P_X_POS)); - EXPECT_DOUBLE_EQ(10., item_above->property<double>(LayerElementItem::P_WIDTH)); - EXPECT_DOUBLE_EQ(10., item_middle->property<double>(LayerElementItem::P_X_POS)); - EXPECT_DOUBLE_EQ(10., item_middle->property<double>(LayerElementItem::P_WIDTH)); - EXPECT_DOUBLE_EQ(20., item_below->property<double>(LayerElementItem::P_X_POS)); - EXPECT_DOUBLE_EQ(10., item_below->property<double>(LayerElementItem::P_WIDTH)); - - // #############################################################################s - // Test signaling for property changes - QSignalSpy spy_ctr_above_width(test_data.above_controller, - &LayerElementController::widthChanged); - QSignalSpy spy_ctr_middle_height(test_data.middle_controller, - &LayerElementController::heightChanged); - QSignalSpy spy_ctr_middle_roughness(test_data.middle_controller, - &LayerElementController::roughnessChanged); - - auto mouse_move_event = new QGraphicsSceneMouseEvent(); - QList<QVariant> move_arguments; - - // Try standard x move - mouse_move_event->setPos(QPointF(adapter->toSceneX(-8), adapter->toSceneY(0))); - side_segment_middle->mouseMoveEvent(mouse_move_event); - move_arguments = spy_ctr_above_width.takeLast(); - - EXPECT_EQ(move_arguments.at(0).value<std::string>(), "above"); - EXPECT_DOUBLE_EQ(move_arguments.at(1).value<double>(), 8); - EXPECT_DOUBLE_EQ(8., item_above->property<double>(LayerElementItem::P_WIDTH)); - EXPECT_DOUBLE_EQ(8., item_middle->property<double>(LayerElementItem::P_X_POS)); - EXPECT_DOUBLE_EQ(3., first_handle_middle->rectangle().x()); - EXPECT_DOUBLE_EQ(5., first_handle_middle->rectangle().y()); - EXPECT_DOUBLE_EQ(3., second_handle_middle->rectangle().x()); - EXPECT_DOUBLE_EQ(5., second_handle_middle->rectangle().y()); - - move_arguments = spy_ctr_middle_roughness.takeLast(); - EXPECT_EQ(move_arguments.at(0).value<std::string>(), "middle"); - EXPECT_DOUBLE_EQ(move_arguments.at(1).value<double>(), 4); - EXPECT_DOUBLE_EQ(4, item_middle->property<double>(LayerElementItem::P_ROUGHNESS)); - - // Try limit x move - mouse_move_event->setPos(QPointF(adapter->toSceneX(1), adapter->toSceneY(0))); - side_segment_middle->mouseMoveEvent(mouse_move_event); - - move_arguments = spy_ctr_above_width.takeLast(); - EXPECT_EQ(move_arguments.at(0).value<std::string>(), "above"); - EXPECT_DOUBLE_EQ(move_arguments.at(1).value<double>(), 0); - EXPECT_DOUBLE_EQ(0, item_above->property<double>(LayerElementItem::P_WIDTH)); - EXPECT_DOUBLE_EQ(0, item_middle->property<double>(LayerElementItem::P_X_POS)); - EXPECT_DOUBLE_EQ(-5, first_handle_middle->rectangle().x()); - EXPECT_DOUBLE_EQ(5., first_handle_middle->rectangle().y()); - EXPECT_DOUBLE_EQ(-5, second_handle_middle->rectangle().x()); - EXPECT_DOUBLE_EQ(5., second_handle_middle->rectangle().y()); - - // Try standard x move - mouse_move_event->setPos(QPointF(adapter->toSceneX(-10), adapter->toSceneY(0))); - side_segment_middle->mouseMoveEvent(mouse_move_event); - move_arguments = spy_ctr_above_width.takeLast(); - - EXPECT_EQ(move_arguments.at(0).value<std::string>(), "above"); - EXPECT_DOUBLE_EQ(move_arguments.at(1).value<double>(), 10); - EXPECT_DOUBLE_EQ(10., item_above->property<double>(LayerElementItem::P_WIDTH)); - EXPECT_DOUBLE_EQ(10., item_middle->property<double>(LayerElementItem::P_X_POS)); - EXPECT_DOUBLE_EQ(5., first_handle_middle->rectangle().x()); - EXPECT_DOUBLE_EQ(5., first_handle_middle->rectangle().y()); - EXPECT_DOUBLE_EQ(5., second_handle_middle->rectangle().x()); - EXPECT_DOUBLE_EQ(5., second_handle_middle->rectangle().y()); - - // Try standard y move - mouse_move_event->setPos(QPointF(adapter->toSceneX(0), adapter->toSceneY(8))); - top_segment_middle->mouseMoveEvent(mouse_move_event); - move_arguments = spy_ctr_middle_height.takeLast(); - - EXPECT_EQ(move_arguments.at(0).value<std::string>(), "middle"); - EXPECT_DOUBLE_EQ(move_arguments.at(1).value<double>(), 8); - EXPECT_DOUBLE_EQ(8., item_middle->property<double>(LayerElementItem::P_HEIGHT)); - EXPECT_DOUBLE_EQ(5., first_handle_middle->rectangle().x()); - EXPECT_DOUBLE_EQ(5., first_handle_middle->rectangle().y()); - EXPECT_DOUBLE_EQ(5., second_handle_middle->rectangle().x()); - EXPECT_DOUBLE_EQ(3., second_handle_middle->rectangle().y()); - - // Try limit y move - mouse_move_event->setPos(QPointF(adapter->toSceneX(-0), adapter->toSceneY(-1))); - top_segment_middle->mouseMoveEvent(mouse_move_event); - move_arguments = spy_ctr_middle_height.takeLast(); - - EXPECT_EQ(move_arguments.at(0).value<std::string>(), "middle"); - EXPECT_EQ(move_arguments.at(1).value<double>(), 0); - EXPECT_DOUBLE_EQ(0, item_middle->property<double>(LayerElementItem::P_HEIGHT)); - EXPECT_DOUBLE_EQ(5., first_handle_middle->rectangle().x()); - EXPECT_DOUBLE_EQ(5., first_handle_middle->rectangle().y()); - EXPECT_DOUBLE_EQ(5., second_handle_middle->rectangle().x()); - EXPECT_DOUBLE_EQ(-5., second_handle_middle->rectangle().y()); - - // Try standard y move - mouse_move_event->setPos(QPointF(adapter->toSceneX(0), adapter->toSceneY(10))); - top_segment_middle->mouseMoveEvent(mouse_move_event); - move_arguments = spy_ctr_middle_height.takeLast(); - - EXPECT_EQ(move_arguments.at(0).value<std::string>(), "middle"); - EXPECT_DOUBLE_EQ(move_arguments.at(1).value<double>(), 10); - EXPECT_DOUBLE_EQ(10., item_middle->property<double>(LayerElementItem::P_HEIGHT)); - EXPECT_DOUBLE_EQ(5., first_handle_middle->rectangle().x()); - EXPECT_DOUBLE_EQ(5., first_handle_middle->rectangle().y()); - EXPECT_DOUBLE_EQ(5., second_handle_middle->rectangle().x()); - EXPECT_DOUBLE_EQ(5., second_handle_middle->rectangle().y()); - - // Try standard roughness move left handle - mouse_move_event->setPos(QPointF(adapter->toSceneX(-7), adapter->toSceneY(0))); - left_handle_middle->mouseMoveEvent(mouse_move_event); - - move_arguments = spy_ctr_middle_roughness.takeLast(); - EXPECT_EQ(move_arguments.at(0).value<std::string>(), "middle"); - EXPECT_DOUBLE_EQ(move_arguments.at(1).value<double>(), 3.); - EXPECT_DOUBLE_EQ(3., item_middle->property<double>(LayerElementItem::P_ROUGHNESS)); - - // Try limit roughness move left handle - mouse_move_event->setPos(QPointF(adapter->toSceneX(-2), adapter->toSceneY(0))); - left_handle_middle->mouseMoveEvent(mouse_move_event); - - move_arguments = spy_ctr_middle_roughness.takeLast(); - EXPECT_EQ(move_arguments.at(0).value<std::string>(), "middle"); - EXPECT_DOUBLE_EQ(move_arguments.at(1).value<double>(), 5.); - EXPECT_DOUBLE_EQ(5., item_middle->property<double>(LayerElementItem::P_ROUGHNESS)); - - // Try standard roughness move left handle - mouse_move_event->setPos(QPointF(adapter->toSceneX(-8), adapter->toSceneY(0))); - left_handle_middle->mouseMoveEvent(mouse_move_event); - - move_arguments = spy_ctr_middle_roughness.takeLast(); - EXPECT_EQ(move_arguments.at(0).value<std::string>(), "middle"); - EXPECT_FLOAT_EQ(move_arguments.at(1).value<double>(), 2.); // DOUBLE_EQ not enough under Windows - EXPECT_FLOAT_EQ(2., item_middle->property<double>(LayerElementItem::P_ROUGHNESS)); - - // Try standard roughness move right handle - mouse_move_event->setPos(QPointF(adapter->toSceneX(-13), adapter->toSceneY(0))); - right_handle_middle->mouseMoveEvent(mouse_move_event); - - move_arguments = spy_ctr_middle_roughness.takeLast(); - EXPECT_EQ(move_arguments.at(0).value<std::string>(), "middle"); - EXPECT_DOUBLE_EQ(move_arguments.at(1).value<double>(), 3.); - EXPECT_DOUBLE_EQ(3., item_middle->property<double>(LayerElementItem::P_ROUGHNESS)); - - // Try limit roughness move right handle - mouse_move_event->setPos(QPointF(adapter->toSceneX(-8), adapter->toSceneY(0))); - right_handle_middle->mouseMoveEvent(mouse_move_event); - - move_arguments = spy_ctr_middle_roughness.takeLast(); - EXPECT_EQ(move_arguments.at(0).value<std::string>(), "middle"); - EXPECT_DOUBLE_EQ(move_arguments.at(1).value<double>(), 0); - EXPECT_DOUBLE_EQ(0, item_middle->property<double>(LayerElementItem::P_ROUGHNESS)); - - // Try standard roughness move right handle - mouse_move_event->setPos(QPointF(adapter->toSceneX(-13), adapter->toSceneY(0))); - right_handle_middle->mouseMoveEvent(mouse_move_event); - - move_arguments = spy_ctr_middle_roughness.takeLast(); - EXPECT_EQ(move_arguments.at(0).value<std::string>(), "middle"); - EXPECT_DOUBLE_EQ(move_arguments.at(1).value<double>(), 3); - EXPECT_DOUBLE_EQ(3, item_middle->property<double>(LayerElementItem::P_ROUGHNESS)); -} -TEST_F(LayerElementTest, moveelements) -{ - TestData test_data; - test_data.middle_controller->setLayerAbove(test_data.above_controller); - test_data.middle_controller->setLayerBelow(test_data.below_controller); -} diff --git a/Tests/Unit/gui2/testdareflcore/layeritems.test.cpp b/Tests/Unit/gui2/testdareflcore/layeritems.test.cpp deleted file mode 100644 index 873fbff78a0..00000000000 --- a/Tests/Unit/gui2/testdareflcore/layeritems.test.cpp +++ /dev/null @@ -1,83 +0,0 @@ -// ************************************************************************** // -// -// Reflectometry simulation software prototype -// -//! @license GNU General Public License v3 or higher (see COPYING) -//! @authors see AUTHORS -// -// ************************************************************************** // - -#include "google_test.h" -#include "gui2/model/sampleitems.h" -#include "gui2/model/samplemodel.h" -#include "test_utils.h" - -using namespace gui2; -using namespace ModelView; - -//! Test layer items. - -class LayerItemsTest : public ::testing::Test { -public: - ~LayerItemsTest(); -}; - -LayerItemsTest::~LayerItemsTest() = default; - -//! Checks that layers in multilayer have proper appearance of "thickness" property. -//! Top and bottom layers should have "thickness" always disabled. - -// FIXME restore testing of thickness and roughness appearance for top and bottom layers -// FIXME together with layeritems.cpp - -TEST_F(LayerItemsTest, layerAppearanceTwoLayerSystem) -{ - SampleModel model; - - auto multilayer = model.insertItem<MultiLayerItem>(); - - auto top = model.insertItem<LayerItem>(multilayer); - auto bottom = model.insertItem<LayerItem>(multilayer); - - // check appearance of thickness properties - EXPECT_FALSE(top->getItem(LayerItem::P_THICKNESS)->isEnabled()); - EXPECT_FALSE(bottom->getItem(LayerItem::P_THICKNESS)->isEnabled()); - - // check that thickness of top and bottom layer is 0. - EXPECT_EQ(top->property<double>(LayerItem::P_THICKNESS), 0.0); - EXPECT_EQ(bottom->property<double>(LayerItem::P_THICKNESS), 0.0); -} - -TEST_F(LayerItemsTest, layerAppearanceThreeLayerSystem) -{ - SampleModel model; - - auto multilayer = model.insertItem<MultiLayerItem>(); - - auto top = model.insertItem<LayerItem>(multilayer); - auto middle = model.insertItem<LayerItem>(multilayer); - auto bottom = model.insertItem<LayerItem>(multilayer); - - // check appearance of thickness properties - EXPECT_FALSE(top->getItem(LayerItem::P_THICKNESS)->isEnabled()); - EXPECT_TRUE(middle->getItem(LayerItem::P_THICKNESS)->isEnabled()); - EXPECT_FALSE(bottom->getItem(LayerItem::P_THICKNESS)->isEnabled()); - - middle->setProperty(LayerItem::P_THICKNESS, 42.0); - - // moving middle layer on top - model.moveItem(middle, multilayer, {MultiLayerItem::T_LAYERS, 0}); - - auto new_top = middle; - auto new_middle = top; - auto new_bottom = bottom; - - EXPECT_FALSE(new_top->getItem(LayerItem::P_THICKNESS)->isEnabled()); - EXPECT_TRUE(new_middle->getItem(LayerItem::P_THICKNESS)->isEnabled()); - EXPECT_FALSE(new_bottom->getItem(LayerItem::P_THICKNESS)->isEnabled()); - - // check the value of thickness - EXPECT_EQ(new_top->property<double>(LayerItem::P_THICKNESS), 0.0); // was reset during move - EXPECT_EQ(new_middle->property<double>(LayerItem::P_THICKNESS), 0.0); - EXPECT_EQ(new_bottom->property<double>(LayerItem::P_THICKNESS), 0.0); -} diff --git a/Tests/Unit/gui2/testdareflcore/layerselectionmodel.test.cpp b/Tests/Unit/gui2/testdareflcore/layerselectionmodel.test.cpp deleted file mode 100644 index 323ebe29bf9..00000000000 --- a/Tests/Unit/gui2/testdareflcore/layerselectionmodel.test.cpp +++ /dev/null @@ -1,127 +0,0 @@ -// ************************************************************************** // -// -// Reflectometry simulation software prototype -// -//! @license GNU General Public License v3 or higher (see COPYING) -//! @authors see AUTHORS -// -// ************************************************************************** // - -#include "google_test.h" -#include "gui2/layereditor/layerselectionmodel.h" -#include "gui2/layereditor/layerviewmodel.h" -#include "gui2/model/sampleitems.h" -#include "gui2/model/samplemodel.h" -#include "mvvm/viewmodel/viewmodelutils.h" -#include "test_utils.h" - -using namespace gui2; -using namespace ModelView; - -//! Tests of LayerSelectionModel. - -class LayerSelectionModelTest : public ::testing::Test { -public: - ~LayerSelectionModelTest(); - - struct TestData { - SampleModel sample_model; - LayerViewModel view_model{&sample_model}; - LayerSelectionModel selection_model{&view_model}; - SessionItem* multilayer{nullptr}; - SessionItem* top{nullptr}; - SessionItem* bottom{nullptr}; - TestData() - { - multilayer = sample_model.insertItem<MultiLayerItem>(); - top = sample_model.insertItem<LayerItem>(multilayer); - bottom = sample_model.insertItem<LayerItem>(multilayer); - view_model.setRootSessionItem(multilayer); - } - }; -}; - -LayerSelectionModelTest::~LayerSelectionModelTest() = default; - -//! Layout of LayerViewModel for two layer system. - -TEST_F(LayerSelectionModelTest, initialState) -{ - TestData test_data; - EXPECT_FALSE(test_data.selection_model.hasSelection()); - - // checking layout of multilayer - auto layers = test_data.multilayer->getItems(MultiLayerItem::T_LAYERS); - EXPECT_EQ(layers.size(), 2); - EXPECT_EQ(layers.at(0), test_data.top); - EXPECT_EQ(layers.at(1), test_data.bottom); -} - -TEST_F(LayerSelectionModelTest, selectLayerItem) -{ - TestData test_data; - - // selecting top layer - test_data.selection_model.selectItem(test_data.top); - EXPECT_TRUE(test_data.selection_model.hasSelection()); - // 4 indexes should be selected: name, nr, material, thickness, sigma - EXPECT_EQ(test_data.selection_model.selectedIndexes().size(), 5); - - // checking back that top layer is selected - std::vector<SessionItem*> expected = {test_data.top}; - EXPECT_EQ(test_data.selection_model.selectedItems(), expected); - - // selecting bottom layer - test_data.selection_model.selectItem(test_data.bottom); - EXPECT_TRUE(test_data.selection_model.hasSelection()); - // 4 indexes should be selected: name, nr, material, thickness, sigma - EXPECT_EQ(test_data.selection_model.selectedIndexes().size(), 5); - - // checking back that top layer is selected - expected = {test_data.bottom}; - EXPECT_EQ(test_data.selection_model.selectedItems(), expected); -} - -TEST_F(LayerSelectionModelTest, selectLayerItems) -{ - TestData test_data; - - std::vector<ModelView::SessionItem*> to_select{test_data.top, test_data.bottom}; - test_data.selection_model.selectItems(to_select); - - // selecting top layer - EXPECT_TRUE(test_data.selection_model.hasSelection()); - - EXPECT_EQ(test_data.selection_model.selectedIndexes().size(), 10); // two rows of cells - - // checking back that top layer is selected - std::vector<SessionItem*> expected = to_select; - EXPECT_EQ(test_data.selection_model.selectedItems(), expected); -} - -//! Checking selection of MultiLayer items - -TEST_F(LayerSelectionModelTest, selectMultiLayerItem) -{ - TestData test_data; - - // adding new MultiLayer between top and bottom layer - auto new_ml = test_data.sample_model.insertItem<MultiLayerItem>(test_data.multilayer, - {MultiLayerItem::T_LAYERS, 1}); - - // checking layout - auto layers = test_data.multilayer->getItems(MultiLayerItem::T_LAYERS); - EXPECT_EQ(layers.size(), 3); - EXPECT_EQ(layers.at(0), test_data.top); - EXPECT_EQ(layers.at(1), new_ml); - EXPECT_EQ(layers.at(2), test_data.bottom); - - // selecting new multi-layer - test_data.selection_model.selectItem(new_ml); - - EXPECT_TRUE(test_data.selection_model.hasSelection()); - EXPECT_EQ(test_data.selection_model.selectedIndexes().size(), 5); - - std::vector<SessionItem*> expected = {new_ml}; - EXPECT_EQ(test_data.selection_model.selectedItems(), expected); -} diff --git a/Tests/Unit/gui2/testdareflcore/layerviewmodel.test.cpp b/Tests/Unit/gui2/testdareflcore/layerviewmodel.test.cpp deleted file mode 100644 index 85c1edcabea..00000000000 --- a/Tests/Unit/gui2/testdareflcore/layerviewmodel.test.cpp +++ /dev/null @@ -1,160 +0,0 @@ -// ************************************************************************** // -// -// Reflectometry simulation software prototype -// -//! @license GNU General Public License v3 or higher (see COPYING) -//! @authors see AUTHORS -// -// ************************************************************************** // - -#include "google_test.h" -#include "gui2/layereditor/layerviewmodel.h" -#include "gui2/model/sampleitems.h" -#include "gui2/model/samplemodel.h" -#include "mvvm/viewmodel/viewmodelutils.h" -#include "test_utils.h" -#include <QSignalSpy> - -using namespace gui2; -using namespace ModelView; - -//! Tests of LayerViewModel. - -class LayerViewModelTest : public ::testing::Test { -public: - ~LayerViewModelTest(); -}; - -LayerViewModelTest::~LayerViewModelTest() = default; - -TEST_F(LayerViewModelTest, initialState) -{ - SampleModel model; - LayerViewModel viewmodel(&model); - - EXPECT_EQ(viewmodel.rowCount(), 0); - EXPECT_EQ(viewmodel.columnCount(), 0); -} - -//! Checks signals while inserting LayerItem in empty model - -TEST_F(LayerViewModelTest, insertLayer) -{ - SampleModel model; - LayerViewModel viewmodel(&model); - - QSignalSpy spyRowsInserted(&viewmodel, &LayerViewModel::rowsInserted); - QSignalSpy spyColumnsInserted(&viewmodel, &LayerViewModel::columnsInserted); - QSignalSpy spyRowsRemoved(&viewmodel, &LayerViewModel::rowsRemoved); - QSignalSpy spyColumnsRemoved(&viewmodel, &LayerViewModel::columnsRemoved); - - auto layer = model.insertItem<LayerItem>(); - EXPECT_EQ(viewmodel.rowCount(), 1); - EXPECT_EQ(viewmodel.columnCount(), 5); - - // checking ::rowInserted - EXPECT_EQ(spyRowsInserted.count(), 1); - QList<QVariant> arguments = spyRowsInserted.takeFirst(); - EXPECT_EQ(arguments.size(), 3); // QModelIndex &parent, int first, int last - EXPECT_EQ(arguments.at(0).value<QModelIndex>(), QModelIndex()); - EXPECT_EQ(arguments.at(1).value<int>(), 0); - EXPECT_EQ(arguments.at(2).value<int>(), 0); - - // checking ::columnInserted - EXPECT_EQ(spyColumnsInserted.count(), 0); - - // checking ::spyRowsRemoved - EXPECT_EQ(spyColumnsInserted.count(), 0); - - // checking ::spyRowsRemoved - EXPECT_EQ(spyColumnsRemoved.count(), 0); - - // checking layout of ViewModel - QModelIndexList selection = {viewmodel.index(0, 0), viewmodel.index(0, 1), - viewmodel.index(0, 2), viewmodel.index(0, 3)}; - - std::vector<SessionItem*> expected = {layer->getItem(LayerItem::P_NAME), nullptr, - layer->getItem(LayerItem::P_MATERIAL), - layer->getItem(LayerItem::P_THICKNESS)}; - EXPECT_EQ(Utils::ItemsFromIndex(selection), expected); -} - -//! Checks signals while inserting MultiLayerItem in empty model. - -TEST_F(LayerViewModelTest, inserMultitLayer) -{ - SampleModel model; - LayerViewModel viewmodel(&model); - - QSignalSpy spyRowsInserted(&viewmodel, &LayerViewModel::rowsInserted); - QSignalSpy spyColumnsInserted(&viewmodel, &LayerViewModel::columnsInserted); - QSignalSpy spyRowsRemoved(&viewmodel, &LayerViewModel::rowsRemoved); - QSignalSpy spyColumnsRemoved(&viewmodel, &LayerViewModel::columnsRemoved); - - auto multilayer = model.insertItem<MultiLayerItem>(); - EXPECT_EQ(viewmodel.rowCount(), 1); - EXPECT_EQ(viewmodel.columnCount(), 5); - - // checking ::rowInserted - EXPECT_EQ(spyRowsInserted.count(), 1); - QList<QVariant> arguments = spyRowsInserted.takeFirst(); - EXPECT_EQ(arguments.size(), 3); // QModelIndex &parent, int first, int last - EXPECT_EQ(arguments.at(0).value<QModelIndex>(), QModelIndex()); - EXPECT_EQ(arguments.at(1).value<int>(), 0); - EXPECT_EQ(arguments.at(2).value<int>(), 0); - - // checking ::columnInserted - EXPECT_EQ(spyColumnsInserted.count(), 0); - - // checking ::spyRowsRemoved - EXPECT_EQ(spyColumnsInserted.count(), 0); - - // checking ::spyRowsRemoved - EXPECT_EQ(spyColumnsRemoved.count(), 0); - - // checking layout of ViewModel - QModelIndexList selection = {viewmodel.index(0, 0), viewmodel.index(0, 1), - viewmodel.index(0, 2), viewmodel.index(0, 3)}; - - std::vector<SessionItem*> expected = {multilayer->getItem(MultiLayerItem::P_NAME), - multilayer->getItem(MultiLayerItem::P_NREPETITIONS), - nullptr, nullptr}; - EXPECT_EQ(Utils::ItemsFromIndex(selection), expected); - - // adding layer to the multilayer - QModelIndex mlIndex = viewmodel.index(0, 0); - model.insertItem<LayerItem>(multilayer); - EXPECT_EQ(viewmodel.rowCount(mlIndex), 1); - EXPECT_EQ(viewmodel.columnCount(mlIndex), 5); -} - -//! Layout of LayerViewModel for two layer system. - -TEST_F(LayerViewModelTest, twoLayerSystem) -{ - SampleModel model; - auto multilayer = model.insertItem<MultiLayerItem>(); - auto top = model.insertItem<LayerItem>(multilayer); - auto bottom = model.insertItem<LayerItem>(multilayer); - - LayerViewModel viewmodel(&model); - viewmodel.setRootSessionItem(multilayer); - - EXPECT_EQ(viewmodel.rowCount(), 2); - EXPECT_EQ(viewmodel.columnCount(), 5); // name, Nr, material, thickness, sigma - - // check indexes in first row and their correspondence to top-layer related items - QModelIndexList selection = {viewmodel.index(0, 0), viewmodel.index(0, 1), - viewmodel.index(0, 2), viewmodel.index(0, 3)}; - std::vector<SessionItem*> expected = {top->getItem(LayerItem::P_NAME), nullptr, - top->getItem(LayerItem::P_MATERIAL), - top->getItem(LayerItem::P_THICKNESS)}; - EXPECT_EQ(Utils::ItemsFromIndex(selection), expected); - - // check indexes in first row and their correspondence to bottom-layer related items - selection = {viewmodel.index(1, 0), viewmodel.index(1, 1), viewmodel.index(1, 2), - viewmodel.index(1, 3)}; - expected = {bottom->getItem(LayerItem::P_NAME), nullptr, bottom->getItem(LayerItem::P_MATERIAL), - bottom->getItem(LayerItem::P_THICKNESS)}; - EXPECT_EQ(Utils::ItemsFromIndex(selection), expected); -} diff --git a/Tests/Unit/gui2/testdareflcore/materialprofile.test.cpp b/Tests/Unit/gui2/testdareflcore/materialprofile.test.cpp deleted file mode 100644 index c22972a544b..00000000000 --- a/Tests/Unit/gui2/testdareflcore/materialprofile.test.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// ************************************************************************** // -// -// Reflectometry simulation software prototype -// -//! @license GNU General Public License v3 or higher (see COPYING) -//! @authors see AUTHORS -// -// ************************************************************************** // - -#include "google_test.h" -#include "gui2/quicksimeditor/materialprofile.h" - -using namespace gui2; - -//! Tests utility functions from MaterialProfile namespace. - -class MaterialProfileTest : public ::testing::Test { -public: - ~MaterialProfileTest(); -}; - -MaterialProfileTest::~MaterialProfileTest() = default; - -TEST_F(MaterialProfileTest, GenerateZValues) -{ - auto values = MaterialProfile::GenerateZValues(0, -2.0, 4.0); - EXPECT_EQ(values.size(), 0); - - values = MaterialProfile::GenerateZValues(1, -2.0, 4.0); - EXPECT_EQ(values, std::vector<double>{-2.0}); - - values = MaterialProfile::GenerateZValues(2, -2.0, 4.0); - EXPECT_EQ(values, (std::vector<double>{-2.0, 4.0})); - - values = MaterialProfile::GenerateZValues(3, -2.0, 4.0); - EXPECT_EQ(values, (std::vector<double>{-2.0, 1.0, 4.0})); - - values = MaterialProfile::GenerateZValues(3, -15.0, 5.0); - EXPECT_EQ(values, (std::vector<double>{-15.0, -5.0, 5.0})); -} - -TEST_F(MaterialProfileTest, DefaultMaterialProfileLimits) -{ - SliceData air{{0.0, 0.0}, 0.0, 0.0}; - SliceData substrate{{2e-06, 0.0}, 0.0, 0.0}; - multislice_t multislice = {air, substrate}; - - auto [xmin, xmax] = MaterialProfile::DefaultMaterialProfileLimits(multislice); - EXPECT_EQ(xmin, -10.0); - EXPECT_EQ(xmax, 10.0); -} - -TEST_F(MaterialProfileTest, TwoLayersProfile) -{ - SliceData air{{0.0, 0.0}, 0.0, 0.0}; - SliceData substrate{{2e-06, 0.0}, 0.0, 0.0}; - multislice_t multislice = {air, substrate}; - - auto values = MaterialProfile::CalculateProfile(multislice, 2, -10.0, 10.0); - ASSERT_EQ(values.size(), 2); - EXPECT_DOUBLE_EQ(values[0].real(), substrate.material.real()); - EXPECT_DOUBLE_EQ(values[1].real(), air.material.real()); -} - -TEST_F(MaterialProfileTest, ThreeLayersProfile) -{ - SliceData air{{0.0, 0.0}, 0.0, 0.0}; - SliceData ni{{9e-06, 0.0}, 10.0, 0.0}; - SliceData substrate{{2e-06, 0.0}, 0.0, 0.0}; - multislice_t multislice = {air, ni, substrate}; - - auto values = MaterialProfile::CalculateProfile(multislice, 3, -15.0, 5.0); - ASSERT_EQ(values.size(), 3); - EXPECT_DOUBLE_EQ(values[0].real(), substrate.material.real()); - EXPECT_DOUBLE_EQ(values[1].real(), ni.material.real()); - EXPECT_DOUBLE_EQ(values[2].real(), air.material.real()); -} diff --git a/Tests/Unit/gui2/testdareflcore/modelutils.test.cpp b/Tests/Unit/gui2/testdareflcore/modelutils.test.cpp deleted file mode 100644 index 9b77f259465..00000000000 --- a/Tests/Unit/gui2/testdareflcore/modelutils.test.cpp +++ /dev/null @@ -1,67 +0,0 @@ -// ************************************************************************** // -// -// Reflectometry simulation software prototype -// -//! @license GNU General Public License v3 or higher (see COPYING) -//! @authors see AUTHORS -// -// ************************************************************************** // - -#include "google_test.h" -#include "gui2/model/modelutils.h" -#include "mvvm/standarditems/axisitems.h" -#include "mvvm/standarditems/data1ditem.h" - -using namespace ModelView; - -//! Tests of Utils namespace functions. - -class ModelUtilsTest : public ::testing::Test { -public: - ~ModelUtilsTest(); -}; - -ModelUtilsTest::~ModelUtilsTest() = default; - -//! Testing CreateDiffVector helper method. - -TEST_F(ModelUtilsTest, CreateDiffVector) -{ - std::vector<double> a{1.0, 2.0}; - std::vector<double> b{1.0, 4.0, 3.0}; - - auto result = gui2::Utils::CreateDiffVector(a, b); - EXPECT_EQ(result.size(), 2u); - EXPECT_DOUBLE_EQ(result[0], 0.0); - EXPECT_DOUBLE_EQ(result[1], 2.0 * (2.0 - 4.0) / (2.0 + 4.0)); -} - -//! Testing SetDifference helper method. - -TEST_F(ModelUtilsTest, SetDifference) -{ - Data1DItem item1; - item1.setAxis<FixedBinAxisItem>(3, 0.0, 3.0); - Data1DItem item2; - item2.setAxis<FixedBinAxisItem>(3, 0.0, 3.0); - - std::vector<double> values1{1.0, 2.0, 3.0}; - std::vector<double> values2{1.0, 4.0, -3.0}; - item1.setValues(values1); - item2.setValues(values2); - - Data1DItem diff; - diff.setAxis<FixedBinAxisItem>(3, 0.0, 3.0); - gui2::Utils::SetDifference(&item1, &item2, &diff); - - std::vector<double> expected_centers = {0.5, 1.5, 2.5}; - EXPECT_EQ(diff.binCenters(), expected_centers); - auto values = diff.binValues(); - - EXPECT_EQ(values.size(), 3u); - - auto diff_func = [](auto a, auto b) { return 2.0 * (a - b) / (a + b); }; - EXPECT_FLOAT_EQ(values[0], diff_func(values1[0], values2[0])); - EXPECT_FLOAT_EQ(values[1], diff_func(values1[1], values2[1])); - EXPECT_FLOAT_EQ(values[2], 0.0); // division by zero -} diff --git a/Tests/Unit/gui2/testdareflcore/quicksimutils.test.cpp b/Tests/Unit/gui2/testdareflcore/quicksimutils.test.cpp deleted file mode 100644 index 9fb703d14d7..00000000000 --- a/Tests/Unit/gui2/testdareflcore/quicksimutils.test.cpp +++ /dev/null @@ -1,218 +0,0 @@ -// ************************************************************************** // -// -// Reflectometry simulation software prototype -// -//! @license GNU General Public License v3 or higher (see COPYING) -//! @authors see AUTHORS -// -// ************************************************************************** // - -#include "google_test.h" -#include "gui2/model/materialitems.h" -#include "gui2/model/materialmodel.h" -#include "gui2/model/sampleitems.h" -#include "gui2/model/samplemodel.h" -#include "gui2/quicksimeditor/quicksimutils.h" -#include "mvvm/model/externalproperty.h" -#include "mvvm/model/itempool.h" -#include <QColor> -#include <tuple> - -using namespace gui2; -using namespace ModelView; - -namespace { -//! Helper function to set layer's parameters. -void setup_layer(LayerItem* layer, double thickness, double sigma, SLDMaterialItem* material) -{ - layer->setProperty(LayerItem::P_THICKNESS, thickness); - auto roughness = layer->item<RoughnessItem>(LayerItem::P_ROUGHNESS); - roughness->setProperty(RoughnessItem::P_SIGMA, sigma); - layer->setProperty(LayerItem::P_MATERIAL, material->external_property()); -} - -} // namespace - -//! Tests of QuickSimUtils namespace functions. - -class QuickSimUtilsTest : public ::testing::Test { -public: - //! Helper container with testing data. - struct TestData { - std::shared_ptr<ItemPool> item_pool; - SampleModel sample_model; - MaterialModel material_model; - gui2::MultiLayerItem* multilayer{nullptr}; - TestData() - : item_pool(std::make_shared<ItemPool>()) - , sample_model(item_pool) - , material_model(item_pool) - { - multilayer = sample_model.insertItem<gui2::MultiLayerItem>(); - } - - //! Add layer to given multilayer models. At the same time corresponding material will - //! be added to MaterialModel and the Layer will be linked to it. - void addLayer(gui2::MultiLayerItem* _multilayer, double thickness, double sigma, - complex_t sld) - { - auto material = material_model.insertItem<SLDMaterialItem>(); - material->set_properties("gold", QColor(), sld.real(), sld.imag()); - auto layer = sample_model.insertItem<LayerItem>(_multilayer); - setup_layer(layer, thickness, sigma, material); - } - - void addLayer(gui2::MultiLayerItem* _multilayer, - const std::tuple<double, double, complex_t>& info) - { - auto [thickness, sigma, sld] = info; - addLayer(_multilayer, thickness, sigma, sld); - } - }; - - ~QuickSimUtilsTest(); -}; - -QuickSimUtilsTest::~QuickSimUtilsTest() = default; - -//! Testing helper structure. - -TEST_F(QuickSimUtilsTest, testData) -{ - TestData test_data; - - // creating single layer - double thickness{42.0}; - double sigma{43.0}; - complex_t sld{1.0, 2.0}; - test_data.addLayer(test_data.multilayer, thickness, sigma, sld); - - // checking that layer got necessary parameters - auto layer = test_data.multilayer->item<LayerItem>(gui2::MultiLayerItem::T_LAYERS); - EXPECT_EQ(layer->property<double>(LayerItem::P_THICKNESS), thickness); - auto roughness = layer->item<RoughnessItem>(LayerItem::P_ROUGHNESS); - EXPECT_EQ(roughness->property<double>(RoughnessItem::P_SIGMA), sigma); - - // checking that layer is linked to material with necessary parameters - auto material_property = layer->property<ExternalProperty>(LayerItem::P_MATERIAL); - auto material = test_data.sample_model.findItem(material_property.identifier()); - EXPECT_EQ(material->property<double>(SLDMaterialItem::P_SLD_REAL), sld.real()); - EXPECT_EQ(material->property<double>(SLDMaterialItem::P_SLD_IMAG), sld.imag()); -} - -//! Multi-slice of empty MultiLayer. - -TEST_F(QuickSimUtilsTest, emptySlice) -{ - SampleModel model; - auto multilayer = model.insertItem<gui2::MultiLayerItem>(); - auto multislice = gui2::Utils::CreateMultiSlice(*multilayer); - EXPECT_EQ(multislice.size(), 0); -} - -//! Multi-slice of MultiLayer with single layer without material. - -TEST_F(QuickSimUtilsTest, layerSlice) -{ - SampleModel model; - auto multilayer = model.insertItem<gui2::MultiLayerItem>(); - model.insertItem<LayerItem>(multilayer); - auto multislice = gui2::Utils::CreateMultiSlice(*multilayer); - - ASSERT_EQ(multislice.size(), 1); - EXPECT_EQ(multislice[0].material.real(), 0.0); - EXPECT_EQ(multislice[0].material.imag(), 0.0); - EXPECT_EQ(multislice[0].thickness, 0.0); - EXPECT_EQ(multislice[0].sigma, 0.0); -} - -//! Multi-slice of MultiLayer with single layer with defined material and roughness. - -TEST_F(QuickSimUtilsTest, definedLayerSlice) -{ - TestData test_data; - - // initializing MaterialModel with single material - const double thickness{42.0}; - const double sigma{43.0}; - const complex_t sld{1.0, 2.0}; - - // adding layer - test_data.addLayer(test_data.multilayer, thickness, sigma, sld); - - // creating multi slice - auto multislice = gui2::Utils::CreateMultiSlice(*test_data.multilayer); - - ASSERT_EQ(multislice.size(), 1); - EXPECT_EQ(multislice[0].material.real(), sld.real()); - EXPECT_EQ(multislice[0].material.imag(), sld.imag()); - EXPECT_EQ(multislice[0].thickness, thickness); - EXPECT_EQ(multislice[0].sigma, sigma); -} - -//! Multi-slice of MultiLayer with three layers. - -TEST_F(QuickSimUtilsTest, threeLayerSlices) -{ - TestData test_data; - - // initializing MaterialModel with single material - using layer_info = std::tuple<double, double, complex_t>; // thickness, sigma, material - std::vector<layer_info> layer_data = { - {0.0, 0.0, {11, 12}}, {42.0, 10.0, {13, 14}}, {0.0, 0.0, {15, 16}}}; - for (auto [thickness, sigma, sld] : layer_data) - test_data.addLayer(test_data.multilayer, thickness, sigma, sld); - - auto multislice = gui2::Utils::CreateMultiSlice(*test_data.multilayer); - - ASSERT_EQ(multislice.size(), 3); - int index(0); - for (auto [thickness, sigma, sld] : layer_data) { - EXPECT_EQ(multislice[index].material.real(), sld.real()); - EXPECT_EQ(multislice[index].material.imag(), sld.imag()); - EXPECT_EQ(multislice[index].thickness, thickness); - EXPECT_EQ(multislice[index].sigma, sigma); - ++index; - } -} - -//! Slice for MultiLayer containing air, repeated bi-layer and substrate. - -TEST_F(QuickSimUtilsTest, nestedMultiLayerSlice) -{ - TestData test_data; - - // preparing layer data - using layer_info = std::tuple<double, double, complex_t>; // thickness, sigma, material - layer_info air = {0.0, 0.0, {0.0, 0.0}}; - const int repetition_count = 2; - layer_info ti_layer = {20.0, 10.0, {-1.9493e-06, 0.0}}; - layer_info ni_layer = {80.0, 10.0, {9.4245e-06, 0.0}}; - layer_info substrate = {0.0, 10.0, {2.0704e-06, 0.0}}; - - // adding air layer - test_data.addLayer(test_data.multilayer, air); - // adding nested multilayer with content repetition - auto multilayer = test_data.multilayer; - auto nested_multilayer = test_data.sample_model.insertItem<gui2::MultiLayerItem>(multilayer); - nested_multilayer->setProperty(gui2::MultiLayerItem::P_NREPETITIONS, repetition_count); - test_data.addLayer(nested_multilayer, ti_layer); - test_data.addLayer(nested_multilayer, ni_layer); - // adding substrate - test_data.addLayer(test_data.multilayer, substrate); - - auto multislice = gui2::Utils::CreateMultiSlice(*test_data.multilayer); - ASSERT_EQ(multislice.size(), 6); - - // expected slice content - const std::vector<layer_info> layer_data = {air, ti_layer, ni_layer, - ti_layer, ni_layer, substrate}; - int index(0); - for (auto [thickness, sigma, sld] : layer_data) { - EXPECT_EQ(multislice[index].material.real(), sld.real()); - EXPECT_EQ(multislice[index].material.imag(), sld.imag()); - EXPECT_EQ(multislice[index].thickness, thickness); - EXPECT_EQ(multislice[index].sigma, sigma); - ++index; - } -} diff --git a/Tests/Unit/gui2/testdareflcore/sldelementcontroller.test.cpp b/Tests/Unit/gui2/testdareflcore/sldelementcontroller.test.cpp deleted file mode 100644 index cddb11af9b7..00000000000 --- a/Tests/Unit/gui2/testdareflcore/sldelementcontroller.test.cpp +++ /dev/null @@ -1,101 +0,0 @@ -// ************************************************************************** // -// -// Reflectometry simulation software prototype -// -//! @license GNU General Public License v3 or higher (see COPYING) -//! @authors see AUTHORS -// -// ************************************************************************** // - -#include "google_test.h" - -#include "gui2/model/materialmodel.h" -#include "gui2/model/sampleitems.h" -#include "gui2/model/samplemodel.h" -#include "gui2/sldeditor/graphicsscene.h" -#include "gui2/sldeditor/layerelementcontroller.h" -#include "gui2/sldeditor/sldelementcontroller.cpp" -#include "gui2/sldeditor/sldelementmodel.h" -#include "test_utils.h" -#include <QGraphicsScene> - -using namespace gui2; -using namespace ModelView; - -class SLDElementControllerTest : public ::testing::Test { -public: - ~SLDElementControllerTest(); - - struct TestData { - MaterialModel* p_material_model{nullptr}; - SampleModel* p_sample_model{nullptr}; - SLDElementModel* p_sld_model{nullptr}; - GraphicsScene* p_scene_item{nullptr}; - SLDElementController* p_element_controller{nullptr}; - MultiLayerItem* multilayer{nullptr}; - LayerItem* top{nullptr}; - LayerItem* middle{nullptr}; - LayerItem* bottom{nullptr}; - - TestData() - { - p_material_model = new MaterialModel(); - p_sample_model = new SampleModel(); - p_sld_model = new SLDElementModel(); - p_scene_item = new GraphicsScene(); - p_element_controller = new SLDElementController(p_material_model, p_sample_model, - p_sld_model, p_scene_item); - - multilayer = p_sample_model->insertItem<MultiLayerItem>(); - - top = p_sample_model->insertItem<LayerItem>(multilayer); - middle = p_sample_model->insertItem<LayerItem>(multilayer); - bottom = p_sample_model->insertItem<LayerItem>(multilayer); - } - }; -}; - -SLDElementControllerTest::~SLDElementControllerTest() = default; - -TEST_F(SLDElementControllerTest, testInit) -{ - TestData test_data; - - EXPECT_EQ(1, test_data.p_sample_model->rootItem()->childrenCount()); - EXPECT_EQ(5, test_data.p_sample_model->rootItem()->children().at(0)->childrenCount()); - EXPECT_EQ(3, test_data.p_sld_model->rootItem()->childrenCount()); -} - -TEST_F(SLDElementControllerTest, testInsertRemoveLayer) -{ - TestData test_data; - - auto new_item = test_data.p_sample_model->insertItem<LayerItem>( - test_data.p_sample_model->rootItem()->children().at(0)); - - EXPECT_EQ(1, test_data.p_sample_model->rootItem()->childrenCount()); - EXPECT_EQ(6, test_data.p_sample_model->rootItem()->children().at(0)->childrenCount()); - EXPECT_EQ(4, test_data.p_sld_model->rootItem()->childrenCount()); - - test_data.p_sample_model->removeItem(new_item->parent(), - new_item->parent()->tagRowOfItem(new_item)); - EXPECT_EQ(1, test_data.p_sample_model->rootItem()->childrenCount()); - EXPECT_EQ(5, test_data.p_sample_model->rootItem()->children().at(0)->childrenCount()); - EXPECT_EQ(3, test_data.p_sld_model->rootItem()->childrenCount()); -} - -TEST_F(SLDElementControllerTest, testClearAllLayer) -{ - TestData test_data; - - test_data.p_sample_model->removeItem(test_data.top->parent(), - test_data.top->parent()->tagRowOfItem(test_data.top)); - test_data.p_sample_model->removeItem( - test_data.middle->parent(), test_data.middle->parent()->tagRowOfItem(test_data.middle)); - test_data.p_sample_model->removeItem( - test_data.bottom->parent(), test_data.bottom->parent()->tagRowOfItem(test_data.bottom)); - - EXPECT_EQ(1, test_data.p_sample_model->rootItem()->childrenCount()); - EXPECT_EQ(2, test_data.p_sample_model->rootItem()->children().at(0)->childrenCount()); - EXPECT_EQ(0, test_data.p_sld_model->rootItem()->childrenCount()); -} diff --git a/gui2/CMakeLists.txt b/gui2/CMakeLists.txt deleted file mode 100644 index b15a94ebabc..00000000000 --- a/gui2/CMakeLists.txt +++ /dev/null @@ -1,34 +0,0 @@ -set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake/modules) - -include(configuration) - -set(library_name dareflcore) - -add_library(${library_name} SHARED "") - -# -- Generate header for export -- - -set(export_filename ${DAREFL_AUTOGEN_DIR}/darefl_export.h) -generate_export_header(${library_name} EXPORT_FILE_NAME ${export_filename}) - -add_subdirectory(dataloader) -add_subdirectory(importdataview) -add_subdirectory(layereditor) -add_subdirectory(mainwindow) -add_subdirectory(materialeditor) -add_subdirectory(model) -add_subdirectory(quicksimeditor) -add_subdirectory(resources) -add_subdirectory(settingsview) -add_subdirectory(sldeditor) -add_subdirectory(welcomeview) - -target_link_libraries(${library_name} PUBLIC MVVM::View Qt5::Core Qt5::Gui Qt5::Widgets - ${BornAgainCore_LIBRARY}) -target_include_directories(${library_name} PUBLIC - $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/..> - $<BUILD_INTERFACE:${DAREFL_AUTOGEN_DIR}>) - -set(executable_name bornagain2) -add_executable(${executable_name} main.cpp) -target_link_libraries(${executable_name} PRIVATE dareflcore) diff --git a/gui2/cmake/modules/ClangFormat.cmake b/gui2/cmake/modules/ClangFormat.cmake deleted file mode 100644 index eb89dcd4018..00000000000 --- a/gui2/cmake/modules/ClangFormat.cmake +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright Tomas Zeman 2019. -# Distributed under the Boost Software License, Version 1.0. -# (See accompanying file LICENSE_1_0.txt or copy at -# http://www.boost.org/LICENSE_1_0.txt) - -function(clangformat_setup) - if(NOT CLANGFORMAT_EXECUTABLE) - set(CLANGFORMAT_EXECUTABLE clang-format) - endif() - - if(NOT EXISTS ${CLANGFORMAT_EXECUTABLE}) - find_program(clangformat_executable_tmp ${CLANGFORMAT_EXECUTABLE}) - if(clangformat_executable_tmp) - set(CLANGFORMAT_EXECUTABLE ${clangformat_executable_tmp}) - unset(clangformat_executable_tmp) - else() - message(FATAL_ERROR "ClangFormat: ${CLANGFORMAT_EXECUTABLE} not found! Aborting") - endif() - endif() - - foreach(clangformat_source ${ARGV}) - get_filename_component(clangformat_source ${clangformat_source} ABSOLUTE) - list(APPEND clangformat_sources ${clangformat_source}) - endforeach() - - add_custom_target(${PROJECT_NAME}_clangformat - COMMAND - ${CLANGFORMAT_EXECUTABLE} - -style=file - -i - ${clangformat_sources} - COMMENT - "Formating with ${CLANGFORMAT_EXECUTABLE} ..." - ) - - if(TARGET clangformat) - add_dependencies(clangformat ${PROJECT_NAME}_clangformat) - else() - add_custom_target(clangformat DEPENDS ${PROJECT_NAME}_clangformat) - endif() -endfunction() - -function(target_clangformat_setup target) - get_target_property(target_sources ${target} SOURCES) - clangformat_setup(${target_sources}) -endfunction() diff --git a/gui2/cmake/modules/CodeTools.cmake b/gui2/cmake/modules/CodeTools.cmake deleted file mode 100644 index ec991ff40bd..00000000000 --- a/gui2/cmake/modules/CodeTools.cmake +++ /dev/null @@ -1,18 +0,0 @@ -# Collection of functions to set-up code beautification and analysis -include(ClangFormat) - -# List of targets for project code beautification. -set(BEAUTIFICATION_TARGETS dareflcore testdareflcore) - -# Defines new target for 'clangformat' to beautify whole project. -# Use 'make clangformat' or 'cmake --build . --target clangformat' to beautify the code. -# Beautification settings are located in .clang-format in project directory. - -function(project_clangformat_setup) - set(all_sources) - foreach(target ${BEAUTIFICATION_TARGETS}) - get_target_property(target_sources ${target} SOURCES) - list(APPEND all_sources ${target_sources}) - endforeach() - clangformat_setup(${all_sources}) -endfunction() diff --git a/gui2/cmake/modules/configuration.cmake b/gui2/cmake/modules/configuration.cmake deleted file mode 100644 index c9c486a1574..00000000000 --- a/gui2/cmake/modules/configuration.cmake +++ /dev/null @@ -1,59 +0,0 @@ -# ----------------------------------------------------------------------------- -# Modules -# ----------------------------------------------------------------------------- - -include(CTest) -include(CodeTools) -include(GenerateExportHeader) -include(GNUInstallDirs) - -# ----------------------------------------------------------------------------- -# Variables -# ----------------------------------------------------------------------------- - -get_filename_component(DAREFL_PROJECT_DIR "${CMAKE_CURRENT_LIST_DIR}/../.." ABSOLUTE) - -set(DAREFL_TESTOUTPUT_DIR ${CMAKE_BINARY_DIR}/test_output_darefl) - -# ----------------------------------------------------------------------------- -# Directories -# ----------------------------------------------------------------------------- - -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) - -file(MAKE_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) -file(MAKE_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) -file(MAKE_DIRECTORY ${DAREFL_TESTOUTPUT_DIR}) - -# directory for autogenerated configs -set(DAREFL_AUTOGEN_DIR ${CMAKE_BINARY_DIR}/autogen/darefl) -file(MAKE_DIRECTORY ${DAREFL_AUTOGEN_DIR}) - -# ----------------------------------------------------------------------------- -# Dependencies -# ----------------------------------------------------------------------------- - -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTORCC ON) - -find_package(Qt5 5.12 COMPONENTS Widgets Core Gui PrintSupport REQUIRED) -find_package(Threads) - -get_target_property(Qt5Widgets_location Qt5::Widgets LOCATION_Release) -message(STATUS " Qt5 libraries : ${Qt5Widgets_LIBRARIES} ${Qt5Widgets_location}") -message(STATUS " Qt5 Includes : ${Qt5Widgets_INCLUDE_DIRS}") - -# ----------------------------------------------------------------------------- -# Generating config files -# ----------------------------------------------------------------------------- - -configure_file(${DAREFL_PROJECT_DIR}/cmake/scripts/testconfig.h.in ${DAREFL_AUTOGEN_DIR}/testconfig.h @ONLY) - -# ----------------------------------------------------------------------------- -# Compile options -# ----------------------------------------------------------------------------- - -add_compile_options($<$<CXX_COMPILER_ID:MSVC>:/MP>) diff --git a/gui2/cmake/scripts/testconfig.h.in b/gui2/cmake/scripts/testconfig.h.in deleted file mode 100644 index 659c78455ee..00000000000 --- a/gui2/cmake/scripts/testconfig.h.in +++ /dev/null @@ -1,29 +0,0 @@ -// ************************************************************************** // -// -// Model-view-view-model framework for large GUI applications -// -//! @license GNU General Public License v3 or higher (see COPYING) -//! @authors see AUTHORS -// -// ************************************************************************** // - -#ifndef SCRIPTS_TESTCONFIG_H -#define SCRIPTS_TESTCONFIG_H - -#include <string> - -//! Provides build time information for unit tests. -//! Automatically generated by CMake from testconfig.h.in - -namespace TestConfig { - -inline std::string CMakeSourceDir() { return "@CMAKE_SOURCE_DIR@"; } -inline std::string CMakeBinaryDir() { return "@CMAKE_BINARY_DIR@"; } -inline std::string TestOutputDir() { return "@DAREFL_TESTOUTPUT_DIR@"; } -inline std::string ProjectSourceDir() { return "@DAREFL_PROJECT_DIR@"; } -inline std::string TestData() { return "@DAREFL_PROJECT_DIR@/tests/data"; } - -} - -#endif - diff --git a/gui2/core/CMakeLists.txt b/gui2/core/CMakeLists.txt deleted file mode 100644 index 93a834d6bb9..00000000000 --- a/gui2/core/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -target_sources(${library_name} PRIVATE - app_constants.h -) diff --git a/gui2/core/app_constants.h b/gui2/core/app_constants.h deleted file mode 100644 index 2429462b7fb..00000000000 --- a/gui2/core/app_constants.h +++ /dev/null @@ -1,33 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/core/app_constants.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_CORE_APP_CONSTANTS_H -#define BORNAGAIN_GUI2_CORE_APP_CONSTANTS_H - -#include <QString> - -namespace gui2::Constants { - -//! Constants for QSettings. - -const QString DataLoaderGroupKey = "dataloader"; -const QString ParserPropertyGroupKey = "parserproperty"; - -//! Initial state of some widgets. - -const inline bool live_simulation_default_on = false; - -} // namespace gui2::Constants - -#endif // BORNAGAIN_GUI2_CORE_APP_CONSTANTS_H diff --git a/gui2/dataloader/CMakeLists.txt b/gui2/dataloader/CMakeLists.txt deleted file mode 100644 index 4ca2dd3d64d..00000000000 --- a/gui2/dataloader/CMakeLists.txt +++ /dev/null @@ -1,32 +0,0 @@ -target_sources(${library_name} PRIVATE - datahandler.cpp - datahandler.h - dataloader_constants.h - dataloader_types.h - dataloader_utils.cpp - dataloader_utils.h - dataloaderdialog.cpp - dataloaderdialog.h - dataloadertoolbar.cpp - dataloadertoolbar.h - defaultparser.cpp - defaultparser.h - importfilewidget.cpp - importfilewidget.h - importtableheader.cpp - importtableheader.h - importtablemodel.cpp - importtablemodel.h - importtablewidget.cpp - importtablewidget.h - importtextview.cpp - importtextview.h - loaderpreviewpanel.cpp - loaderpreviewpanel.h - loaderselectorpanel.cpp - loaderselectorpanel.h - parserinterface.h - parserpropertywidget.cpp - parserpropertywidget.h -) - diff --git a/gui2/dataloader/datahandler.cpp b/gui2/dataloader/datahandler.cpp deleted file mode 100644 index 6603f326851..00000000000 --- a/gui2/dataloader/datahandler.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/dataloader/datahandler.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/dataloader/datahandler.h" -#include "gui2/dataloader/dataloader_utils.h" -#include "mvvm/utils/containerutils.h" -#include <iostream> - -namespace gui2 { - -//! Load raw data from the list of files, if it was not loaded yet. -//! Remove data which is not present. - -void DataHandler::updateRawData(const std::vector<std::string>& file_names) -{ - for (const auto& file_name : file_names) - if (auto it = m_raw_data.find(file_name); it == m_raw_data.end()) - loadFile(file_name); - - for (auto it = m_raw_data.begin(); it != m_raw_data.end(); /* no increment */) { - if (ModelView::Utils::Contains(file_names, it->first)) - it++; - else - m_raw_data.erase(it++); - } -} - -//! Returns raw text data representing content of the file with given name. - -std::vector<std::string> DataHandler::textData(const std::string& file_name) -{ - auto it = m_raw_data.find(file_name); - return it != m_raw_data.end() ? it->second : std::vector<std::string>(); -} - -//! Load file with given name. File is assumed to be ASCII. - -void DataHandler::loadFile(const std::string& file_name) -{ - m_raw_data[file_name] = Utils::LoadASCIIFile(file_name); -} - -} // namespace gui2 diff --git a/gui2/dataloader/datahandler.h b/gui2/dataloader/datahandler.h deleted file mode 100644 index 9d2251c6d3c..00000000000 --- a/gui2/dataloader/datahandler.h +++ /dev/null @@ -1,45 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/dataloader/datahandler.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_DATALOADER_DATAHANDLER_H -#define BORNAGAIN_GUI2_DATALOADER_DATAHANDLER_H - -#include "darefl_export.h" -#include <map> -#include <string> -#include <vector> - -namespace gui2 { - -//! Handles raw data during the life time of DataHandlerDialog. -//! Loads the data from multiple ASCII files and stores in a buffer of strings. - -class DAREFLCORE_EXPORT DataHandler { -public: - DataHandler() = default; - - void updateRawData(const std::vector<std::string>& file_names); - - std::vector<std::string> textData(const std::string& file_name); - -private: - void loadFile(const std::string& file_name); - - //!< correspondence of file name to the raw data in the file (i.e. all strings) - std::map<std::string, std::vector<std::string>> m_raw_data; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_DATALOADER_DATAHANDLER_H diff --git a/gui2/dataloader/dataloader_constants.h b/gui2/dataloader/dataloader_constants.h deleted file mode 100644 index 9417c1739e6..00000000000 --- a/gui2/dataloader/dataloader_constants.h +++ /dev/null @@ -1,29 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/dataloader/dataloader_constants.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_DATALOADER_DATALOADER_CONSTANTS_H -#define BORNAGAIN_GUI2_DATALOADER_DATALOADER_CONSTANTS_H - -#include <string> -#include <vector> - -namespace gui2::Constants { - -const std::string AxisType = "Axis"; -const std::string IntensityType = "Intensity"; -const std::string IgnoreType = "Ignore"; - -} // namespace gui2::Constants - -#endif // BORNAGAIN_GUI2_DATALOADER_DATALOADER_CONSTANTS_H diff --git a/gui2/dataloader/dataloader_types.h b/gui2/dataloader/dataloader_types.h deleted file mode 100644 index b98b456a846..00000000000 --- a/gui2/dataloader/dataloader_types.h +++ /dev/null @@ -1,54 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/dataloader/dataloader_types.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_DATALOADER_DATALOADER_TYPES_H -#define BORNAGAIN_GUI2_DATALOADER_DATALOADER_TYPES_H - -#include "darefl_export.h" -#include <functional> -#include <string> -#include <vector> - -namespace gui2 { - -//! Parser options to process multi column ASCII files. - -struct DAREFLCORE_EXPORT - ParserOptions { // #import Here the options for the default parser are defined - std::string m_header_prefix = "#"; //!< prefix denoting header line - std::string m_separator = " "; //!< column separator - std::string m_skip_index_pattern; //!< pattern denoting line to skip (i.e. '1,10-12,42') -}; - -//! Info about the column as defined by the user via ImportTableWidget. - -struct DAREFLCORE_EXPORT ColumnInfo { - int column{-1}; - std::string type_name; - std::string units; - double multiplier{0.0}; -}; - -//! Function to define if given index satisfies criteria. -using accept_int_t = std::function<bool(int)>; - -//! Function to define if given string should be accepted for further consideration. -using accept_string_t = std::function<bool(const std::string& line)>; - -//! Function to define line splitter according to some criteria. -using line_splitter_t = std::function<std::vector<std::string>(const std::string& line)>; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_DATALOADER_DATALOADER_TYPES_H diff --git a/gui2/dataloader/dataloader_utils.cpp b/gui2/dataloader/dataloader_utils.cpp deleted file mode 100644 index 83d351e0983..00000000000 --- a/gui2/dataloader/dataloader_utils.cpp +++ /dev/null @@ -1,221 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/dataloader/dataloader_utils.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/dataloader/dataloader_utils.h" -#include "gui2/dataloader/dataloader_constants.h" -#include "gui2/importdataview/graphimportdata.h" -#include "mvvm/utils/stringutils.h" -#include <algorithm> -#include <fstream> - -namespace gui2 { - -namespace { - -//! Returns true if given pair of values can represent range -bool isRepresentRange(const std::optional<int>& v0, const std::optional<int>& v1) -{ - if (v0.has_value() && v1.has_value()) - return v0.value() > 0 && v1.value() > 0 && v0.value() <= v1.value(); - return false; -} - -//! Finds in vector of ColumnInfo all columns of given type and returns it as a new vector. -std::vector<ColumnInfo> columnsForType(const std::vector<ColumnInfo>& input, - const std::string& columnType) -{ - std::vector<ColumnInfo> result; - std::copy_if(input.begin(), input.end(), std::back_inserter(result), - [columnType](auto x) { return x.type_name == columnType; }); - return result; -} - -} // namespace - -std::vector<std::string> Utils::LoadASCIIFile(const std::string& file_name) -{ - std::vector<std::string> result; - - std::ifstream file(file_name); - if (!file.is_open()) - throw std::ios_base::failure("Unable to open file '" + file_name + "'"); - for (std::string line; getline(file, line);) - result.emplace_back(line); - return result; -} - -std::vector<std::pair<int, int>> Utils::ExpandLineNumberPattern(const std::string& pattern) -{ - std::vector<std::pair<int, int>> result; - - // splitting "1, 2-3" first on comma-separated tokens - for (const auto& token : ModelView::Utils::SplitString(pattern, ",")) { - auto parts = ModelView::Utils::SplitString(token, "-"); - // splitting on dash-separared tokens - if (!parts.empty()) { - // if no "-" is present, make from "1" a pair {1, 1} - // if "-" is present, make from "1-2" a pair {1,2} - auto conv0 = ModelView::Utils::StringToInteger(parts[0]); - auto conv1 = parts.size() > 1 ? ModelView::Utils::StringToInteger(parts[1]) : conv0; - if (isRepresentRange(conv0, conv1)) - result.push_back({conv0.value(), conv1.value()}); - } - } - - return result; -} - -accept_int_t Utils::CreateLineNumberPatternValidator(const std::string& pattern) -{ - std::vector<std::pair<int, int>> expanded_pattern = Utils::ExpandLineNumberPattern(pattern); - auto result = [expanded_pattern](int line_number) { - for (auto pair : expanded_pattern) { - if (line_number >= pair.first && line_number <= pair.second) - return true; - } - return false; - }; - return result; -} - -accept_string_t Utils::CreateLinePrefixValidator(const std::string& prefix_to_exclude) -{ - auto result = [prefix_to_exclude](const std::string& line) { - // line contains spaces only - if (line.empty() || line.find_first_not_of(' ') == std::string::npos) - return false; - // line starts from pattern - return line.find_first_of(prefix_to_exclude) == 0 ? false : true; - }; - return result; -} - -line_splitter_t Utils::CreateSeparatorBasedSplitter(const std::string& separator) -{ - if (separator.empty()) - throw std::runtime_error("Error, empty separator."); - - bool is_space_only_separator = separator.find_first_not_of(' ') == std::string::npos; - auto result = [separator, is_space_only_separator](const std::string& line) { - std::vector<std::string> values; - std::string trimmed = ModelView::Utils::TrimWhitespace(line); - if (is_space_only_separator) - trimmed = ModelView::Utils::RemoveRepeatedSpaces(trimmed); - return ModelView::Utils::SplitString(trimmed, separator); - }; - return result; -} - -std::string Utils::AddHtmlDivTag(const std::string& line) -{ - const std::string open_div = "<div>"; - const std::string close_div = "</div>"; - std::string result; - return open_div + line + close_div; -} - -std::string Utils::AddHtmlColorTag(const std::string& line, const std::string& color) -{ - const std::string open_tag = "<font color=\"" + color + "\">"; - const std::string close_tag = "</font>"; - std::string result; - return open_tag + line + close_tag; -} - -std::string Utils::AddHtmlBackgroundTag(const std::string& line, const std::string& color) -{ - const std::string open_tag = "<span style=\"background-color:" + color + "\">"; - const std::string close_tag = "</span>"; - std::string result; - return open_tag + line + close_tag; -} - -std::string Utils::AddHtmlColorTagToParts(const std::string& line, - const std::vector<std::string>& parts, - const std::string& color_parts, - const std::string& color_rest) -{ - std::string result; - std::string_view view(line); - - if (parts.empty()) - return AddHtmlDivTag(AddHtmlColorTag(line, color_rest)); - - for (auto part : parts) { - auto it = view.find_first_of(part); - if (it > 0) - result.append(AddHtmlBackgroundTag(std::string(view.substr(0, it)), color_rest)); - result.append(AddHtmlColorTag(part, color_parts)); - view.remove_prefix(it + part.size()); - } - return AddHtmlDivTag(result); -} - -std::pair<std::vector<double>, std::vector<double>> -Utils::ExtractTwoColumns(const std::vector<std::vector<std::string>>& text_data, size_t col1, - size_t col2) -{ - std::vector<double> vec1, vec2; - for (const auto& row : text_data) { - if (col1 < row.size() && col2 < row.size()) { - auto val1 = ModelView::Utils::StringToDouble(row[col1]); - auto val2 = ModelView::Utils::StringToDouble(row[col2]); - if (val1.has_value() && val2.has_value()) { - vec1.push_back(val1.value()); - vec2.push_back(val2.value()); - } - } - } - - return std::make_pair(std::move(vec1), std::move(vec2)); -} - -std::vector<std::pair<ColumnInfo, ColumnInfo>> -Utils::CreateGraphInfoPairs(const std::vector<ColumnInfo>& column_info) -{ - std::vector<std::pair<ColumnInfo, ColumnInfo>> result; - - auto axis_columns = columnsForType(column_info, GUI::Constants::AxisType); - auto intensity_columns = columnsForType(column_info, GUI::Constants::IntensityType); - - if (axis_columns.size() != 1) - throw std::runtime_error("There must be exactly one column with AxisType selected."); - - for (const auto& intensity_info : intensity_columns) - result.push_back(std::make_pair(axis_columns.back(), intensity_info)); - - return result; -} - -GraphImportData Utils::CreateData(const std::vector<std::vector<std::string>>& text_data, - const ColumnInfo& axis, const ColumnInfo& intensity) -{ - GraphImportData result; - - auto [axis_values, intensity_values] = - Utils::ExtractTwoColumns(text_data, axis.column, intensity.column); - - std::transform(intensity_values.begin(), intensity_values.end(), intensity_values.begin(), - [&intensity](auto x) { return x * intensity.multiplier; }); - - result.bin_centers = axis_values; - result.axis_units = axis.units; - - result.bin_values = intensity_values; - result.signal_units = intensity.units; - - return result; -} - -} // namespace gui2 diff --git a/gui2/dataloader/dataloader_utils.h b/gui2/dataloader/dataloader_utils.h deleted file mode 100644 index 34e85ed3561..00000000000 --- a/gui2/dataloader/dataloader_utils.h +++ /dev/null @@ -1,85 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/dataloader/dataloader_utils.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_DATALOADER_DATALOADER_UTILS_H -#define BORNAGAIN_GUI2_DATALOADER_DATALOADER_UTILS_H - -#include "gui2/dataloader/dataloader_types.h" - -namespace gui2 { - -struct GraphImportData; - -namespace Utils { - -//! Loads ASCII file, returns it in the form of vector of strings. -DAREFLCORE_EXPORT std::vector<std::string> LoadASCIIFile(const std::string& file_name); - -//! Expands string representing line number pattern to inclusive pairs of line numbers. -//! "1" will be expanded to { {1, 1} }, "1, 3-5" will be expanded to { {1, 1}, {3, 5} } -DAREFLCORE_EXPORT std::vector<std::pair<int, int>> -ExpandLineNumberPattern(const std::string& pattern); - -//! Creates a callback to define if given line number satisfies line number pattern. -//! "1, 4-6" will accept numbers {1, 4, 5, 6} and will refuse all others. -DAREFLCORE_EXPORT accept_int_t CreateLineNumberPatternValidator(const std::string& pattern); - -//! Creates a callback to define if given line has a valid content for further parsing. -//! Empty lines and lines starting from a given prefix will be excluded. -DAREFLCORE_EXPORT accept_string_t CreateLinePrefixValidator(const std::string& prefix_to_exclude); - -//! Creates line splitter based on separator. -DAREFLCORE_EXPORT line_splitter_t CreateSeparatorBasedSplitter(const std::string& separator); - -//! Returns string representing original 'line' wrapped in 'div' tag. -DAREFLCORE_EXPORT std::string AddHtmlDivTag(const std::string& line); - -//! Returns string representing original 'line' wrapped in html color tag. -DAREFLCORE_EXPORT std::string AddHtmlColorTag(const std::string& line, const std::string& color); - -//! Returns string representing original 'line' wrapped in 'div' tag. -DAREFLCORE_EXPORT std::string AddHtmlBackgroundTag(const std::string& line, - const std::string& color); - -//! Returns string representing original 'line', where 'parts' are surrounded with color tag. -DAREFLCORE_EXPORT std::string AddHtmlColorTagToParts(const std::string& line, - const std::vector<std::string>& parts, - const std::string& color_parts, - const std::string& color_rest); - -//! Extracts double values from two columns of a string array. -//! The row must be valid: string value must represent single double for both target columns, -//! rows should have enough columns. If a row is invalid, it will be skipped, so resulting arrays -//! have always the same length. - -DAREFLCORE_EXPORT std::pair<std::vector<double>, std::vector<double>> -ExtractTwoColumns(const std::vector<std::vector<std::string>>& text_data, size_t col1, size_t col2); - -//! Pack ColumnInfo into pairs representing {AxisType, IntensityType}. -//! For the moment we expect that only one column with AxisType exists. Number of intensity columns -//! can be arbitrary. - -DAREFLCORE_EXPORT std::vector<std::pair<ColumnInfo, ColumnInfo>> -CreateGraphInfoPairs(const std::vector<ColumnInfo>& column_info); - -//! Creates structure from text data. - -DAREFLCORE_EXPORT GraphImportData CreateData(const std::vector<std::vector<std::string>>& text_data, - const ColumnInfo& axis, const ColumnInfo& intensity); - -} // namespace Utils - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_DATALOADER_DATALOADER_UTILS_H diff --git a/gui2/dataloader/dataloaderdialog.cpp b/gui2/dataloader/dataloaderdialog.cpp deleted file mode 100644 index d8cac1f8937..00000000000 --- a/gui2/dataloader/dataloaderdialog.cpp +++ /dev/null @@ -1,286 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/dataloader/dataloaderdialog.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/dataloader/dataloaderdialog.h" -#include "gui2/core/app_constants.h" -#include "gui2/dataloader/datahandler.h" -#include "gui2/dataloader/dataloader_types.h" -#include "gui2/dataloader/dataloader_utils.h" -#include "gui2/dataloader/dataloadertoolbar.h" -#include "gui2/dataloader/loaderpreviewpanel.h" -#include "gui2/dataloader/loaderselectorpanel.h" -#include "gui2/dataloader/parserinterface.h" -#include "mvvm/utils/fileutils.h" -#include "mvvm/widgets/widgetutils.h" -#include <QApplication> -#include <QDebug> -#include <QDialogButtonBox> -#include <QKeyEvent> -#include <QMessageBox> -#include <QPushButton> -#include <QSettings> -#include <QSplitter> -#include <QVBoxLayout> -#include <sstream> - -namespace gui2 { - -namespace { - -//! Wraps user method in try/catch and invoke it. -//! Provides busy-sign while executing, and warning dialog on exception catch. -template <typename T> void invoke_and_catch(T method) -{ - QApplication::setOverrideCursor(Qt::WaitCursor); - try { - std::invoke(method); - QApplication::restoreOverrideCursor(); - } catch (const std::exception& ex) { - QApplication::restoreOverrideCursor(); - QMessageBox msgBox; - - QString message = - QString("Exception was thrown while trying to load files\n\n%1").arg(ex.what()); - msgBox.setText(message); - msgBox.setIcon(msgBox.Critical); - msgBox.exec(); - } -} - -const QString dialogsize_key = "dialogsize"; -const QString splittersize_key = "splittersize"; - -const QString dialogsize_setting_name() -{ - return GUI::Constants::DataLoaderGroupKey + "/" + dialogsize_key; -} - -const QString splittersize_setting_name() -{ - return GUI::Constants::DataLoaderGroupKey + "/" + splittersize_key; -} - -//! Returns string representing import summary: filename and columns used for import. - -std::string createImportDescription(const QString& file_name, const ColumnInfo& axis_info, - const ColumnInfo& intensity_info) -{ - std::ostringstream ostr; - ostr << "file: '" << ModelView::Utils::WithTildeHomePath(file_name).toStdString() << "', "; - ostr << "columns: (" << axis_info.column << ", " << intensity_info.column << ")"; - return ostr.str(); -} - -} // namespace - -DataLoaderDialog::DataLoaderDialog(QWidget* parent) - : QDialog(parent) - , m_toolBar(new DataLoaderToolBar) - , m_selectorPanel(new LoaderSelectorPanel) - , m_previewPanel(new LoaderPreviewPanel) - , m_splitter(new QSplitter) - , m_dataHandler(std::make_unique<DataHandler>()) -{ - m_splitter->setChildrenCollapsible(false); - m_splitter->addWidget(m_selectorPanel); - m_splitter->addWidget(m_previewPanel); - - auto button_box = new QDialogButtonBox; - auto button = button_box->addButton("Import data", QDialogButtonBox::AcceptRole); - button->setAutoDefault(false); - button->setDefault(false); - - button = button_box->addButton("Cancel", QDialogButtonBox::RejectRole); - button->setAutoDefault(false); - button->setDefault(false); - - connect(button_box, &QDialogButtonBox::accepted, this, &DataLoaderDialog::accept); - connect(button_box, &QDialogButtonBox::rejected, this, &DataLoaderDialog::reject); - - auto layout = new QVBoxLayout(this); - layout->addWidget(m_toolBar); - layout->addWidget(m_splitter); - layout->addWidget(button_box); - - initConnections(); - setWindowTitle("Data import dialog"); - - readSettings(); -} - -DataLoaderDialog::~DataLoaderDialog() -{ - writeSettings(); -} - -//! Returns the result of whole parsing. - -std::vector<GraphImportData> DataLoaderDialog::graphImportData() const -{ - return m_graphImportData; -} - -//! Set list of target canvas to define entr where to import. - -void DataLoaderDialog::setTargetCanvas(const std::vector<std::string>& canvas_names, - int current_index) -{ - m_selectorPanel->setTargetCanvas(ModelView::Utils::toStringList(canvas_names), current_index); -} - -//! Returns index of target canvas for graph import. - -int DataLoaderDialog::targetCanvasIndex() const -{ - return m_selectorPanel->targetCanvasIndex(); -} - -//! Invokes file selector dialog. - -void DataLoaderDialog::invokeFileSelectorDialog() -{ - m_selectorPanel->onAddFilesRequest(); -} - -QStringList DataLoaderDialog::fileNames() const -{ - return m_selectorPanel->fileNames(); -} - -//! Make dialog intact to enter-key to handle it by LoadSelectorPanel. - -void DataLoaderDialog::keyPressEvent(QKeyEvent* event) -{ - if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) - return; - QDialog::keyPressEvent(event); -} - -void DataLoaderDialog::accept() -{ - invoke_and_catch([this]() { onParseAllRequest(); }); - - QDialog::accept(); - close(); -} - -//! Loads ASCII data from all files in a list. - -void DataLoaderDialog::onLoadFilesRequest() -{ - auto update_raw_data = [this]() { - m_dataHandler->updateRawData( - ModelView::Utils::fromStringList(m_selectorPanel->fileNames())); - }; - invoke_and_catch(update_raw_data); -} - -//! Show content of selected file in text/table views. - -void DataLoaderDialog::onShowFilePreviewRequest() -{ - auto selected_files = m_selectorPanel->selectedFileNames(); - if (selected_files.empty()) { - m_previewPanel->clearPanel(); - return; - } - - auto data_to_parse = m_dataHandler->textData(selected_files.back().toStdString()); - - // creating parser using current settings - auto parser = m_selectorPanel->createParser(); - parser->process(data_to_parse); - - m_previewPanel->showData(parser.get()); -} - -//! Parse all string data and generate graph data. - -void DataLoaderDialog::onParseAllRequest() -{ - m_graphImportData.clear(); - - auto parser = m_selectorPanel->createParser(); - for (const auto& name : m_selectorPanel->fileNames()) { - auto data_to_parse = m_dataHandler->textData(name.toStdString()); - - parser->process(data_to_parse); - auto parsed_text = parser->parsedData(); - - auto columns = m_previewPanel->columnInfo(); - for (auto [axis_info, intensity_info] : Utils::CreateGraphInfoPairs(columns)) { - auto data = Utils::CreateData(parsed_text, axis_info, intensity_info); - data.graph_description = createImportDescription(name, axis_info, intensity_info); - m_graphImportData.emplace_back(data); - } - } -} - -//! Reads dialog settings. - -void DataLoaderDialog::readSettings() -{ - QSettings settings; - - if (settings.contains(dialogsize_setting_name())) - resize(settings.value(dialogsize_setting_name(), QSize(800, 600)).toSize()); - - if (settings.contains(splittersize_setting_name())) { - QStringList splitter_sizes = QStringList() << "400" - << "400"; - splitter_sizes = settings.value(splittersize_setting_name(), splitter_sizes).toStringList(); - QList<int> sizes; - for (auto num : splitter_sizes) - sizes.push_back(num.toInt()); - m_splitter->setSizes(sizes); - } -} - -//! Writes dialog settings. - -void DataLoaderDialog::writeSettings() -{ - QSettings settings; - settings.setValue(dialogsize_setting_name(), size()); - - QStringList splitter_sizes; - for (auto x : m_splitter->sizes()) - splitter_sizes.push_back(QString::number(x)); - settings.setValue(splittersize_setting_name(), splitter_sizes); -} - -//! Init interconnections of all widgets. - -void DataLoaderDialog::initConnections() -{ - // connect toolbar and LoaderSelectorPanel - connect(m_toolBar, &DataLoaderToolBar::addFilesRequest, m_selectorPanel, - &LoaderSelectorPanel::onAddFilesRequest); - connect(m_toolBar, &DataLoaderToolBar::removeFilesRequest, m_selectorPanel, - &LoaderSelectorPanel::onRemoveFileRequest); - - // updates raw data container when file list changed - connect(m_selectorPanel, &LoaderSelectorPanel::fileNamesChanged, this, - &DataLoaderDialog::onLoadFilesRequest); - - // update text/table view when file selection changed - connect(m_selectorPanel, &LoaderSelectorPanel::fileSelectionChanged, this, - &DataLoaderDialog::onShowFilePreviewRequest); - - // update text/table view when parser properties changed - connect(m_selectorPanel, &LoaderSelectorPanel::parserPropertyChanged, this, - &DataLoaderDialog::onShowFilePreviewRequest); -} - -} // namespace gui2 diff --git a/gui2/dataloader/dataloaderdialog.h b/gui2/dataloader/dataloaderdialog.h deleted file mode 100644 index fe14c692666..00000000000 --- a/gui2/dataloader/dataloaderdialog.h +++ /dev/null @@ -1,77 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/dataloader/dataloaderdialog.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_DATALOADER_DATALOADERDIALOG_H -#define BORNAGAIN_GUI2_DATALOADER_DATALOADERDIALOG_H - -#include "darefl_export.h" -#include "gui2/importdataview/graphimportdata.h" -#include <QDialog> -#include <memory> -#include <vector> - -class QSplitter; - -namespace gui2 { - -class DataLoaderToolBar; -class LoaderSelectorPanel; -class LoaderPreviewPanel; -class DataHandler; - -//! Main dialog for the data loader. - -class DAREFLCORE_EXPORT DataLoaderDialog : public QDialog { - Q_OBJECT - -public: - DataLoaderDialog(QWidget* parent = nullptr); - ~DataLoaderDialog(); - - std::vector<GraphImportData> graphImportData() const; - - void setTargetCanvas(const std::vector<std::string>& canvas_names, int current_index); - - int targetCanvasIndex() const; - - void invokeFileSelectorDialog(); - - QStringList fileNames() const; - -protected: - void keyPressEvent(QKeyEvent* event) override; - void accept() override; - -private slots: - void onLoadFilesRequest(); - void onShowFilePreviewRequest(); - void onParseAllRequest(); - -private: - void readSettings(); - void writeSettings(); - void initConnections(); - - DataLoaderToolBar* m_toolBar{nullptr}; - LoaderSelectorPanel* m_selectorPanel{nullptr}; - LoaderPreviewPanel* m_previewPanel{nullptr}; - QSplitter* m_splitter{nullptr}; - - std::unique_ptr<DataHandler> m_dataHandler; - std::vector<GraphImportData> m_graphImportData; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_DATALOADER_DATALOADERDIALOG_H diff --git a/gui2/dataloader/dataloadertoolbar.cpp b/gui2/dataloader/dataloadertoolbar.cpp deleted file mode 100644 index f463b0fe91f..00000000000 --- a/gui2/dataloader/dataloadertoolbar.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/dataloader/dataloadertoolbar.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/dataloader/dataloadertoolbar.h" -#include "gui2/mainwindow/styleutils.h" -#include <QAction> - -namespace gui2 { - -DataLoaderToolBar::DataLoaderToolBar(QWidget* parent) : QToolBar(parent) -{ - GUI::Utils::Style::SetToolBarStyleTextBesides(this); - - // add files - auto action = new QAction("Add files", this); - action->setIcon(QIcon(":/icons/import.svg")); - action->setToolTip("Adds more files to the list.\n " - "All of them will be parsed in the same way."); - connect(action, &QAction::triggered, [this]() { this->addFilesRequest(); }); - addAction(action); - - // remove files - action = new QAction("Remove files", this); - action->setIcon(QIcon(":/icons/beaker-remove-outline.svg")); - action->setToolTip("Remove selected files from the list."); - connect(action, &QAction::triggered, [this]() { this->removeFilesRequest(); }); - addAction(action); -} - -} // namespace gui2 diff --git a/gui2/dataloader/dataloadertoolbar.h b/gui2/dataloader/dataloadertoolbar.h deleted file mode 100644 index dc16b75c873..00000000000 --- a/gui2/dataloader/dataloadertoolbar.h +++ /dev/null @@ -1,38 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/dataloader/dataloadertoolbar.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_DATALOADER_DATALOADERTOOLBAR_H -#define BORNAGAIN_GUI2_DATALOADER_DATALOADERTOOLBAR_H - -#include "darefl_export.h" -#include <QToolBar> - -namespace gui2 { - -//! Tool bar for DataLoaderDialog. - -class DAREFLCORE_EXPORT DataLoaderToolBar : public QToolBar { - Q_OBJECT - -public: - DataLoaderToolBar(QWidget* parent = nullptr); - -signals: - void addFilesRequest(); - void removeFilesRequest(); -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_DATALOADER_DATALOADERTOOLBAR_H diff --git a/gui2/dataloader/defaultparser.cpp b/gui2/dataloader/defaultparser.cpp deleted file mode 100644 index 2ebe6a23a93..00000000000 --- a/gui2/dataloader/defaultparser.cpp +++ /dev/null @@ -1,80 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/dataloader/defaultparser.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/dataloader/defaultparser.h" -#include "gui2/dataloader/dataloader_utils.h" -#include <stdexcept> - -namespace gui2 { - -DefaultParser::DefaultParser(const ParserOptions& options) -{ - m_isSkipLineNumber = Utils::CreateLineNumberPatternValidator(options.m_skip_index_pattern); - m_isValidLineContent = Utils::CreateLinePrefixValidator(options.m_header_prefix); - m_line_splitter = Utils::CreateSeparatorBasedSplitter(options.m_separator); -} - -//! Parse data representing content of ASCII file. - -void DefaultParser::process(const std::vector<std::string>& raw_data) -{ - m_rawData = raw_data; - m_parsedData.clear(); - - int index{0}; - for (const auto& line : m_rawData) { - bool isValidLine = m_isValidLineContent(line) && !m_isSkipLineNumber(index + 1); - - if (isValidLine) - m_parsedData.emplace(index, m_line_splitter(line)); - - ++index; - } -} - -//! Returns total number of lines in raw data. - -size_t DefaultParser::totalLineCount() const -{ - return m_rawData.size(); -} - -//! Returns a pair representing raw line and flag describing parsing results. - -std::string DefaultParser::getLine(size_t index) const -{ - if (index >= m_rawData.size()) - throw std::runtime_error("Error in DefaultParser: out of bounds."); - - return m_rawData[index]; -} - -std::vector<std::string> DefaultParser::parseResults(size_t index) const -{ - auto it = m_parsedData.find(index); - return it == m_parsedData.end() ? std::vector<std::string>() : it->second; -} - -std::vector<std::vector<std::string>> DefaultParser::parsedData() const -{ - std::vector<std::vector<std::string>> result; - for (size_t index = 0; index < totalLineCount(); ++index) { - if (auto it = m_parsedData.find(index); it != m_parsedData.end()) - result.push_back(it->second); - } - - return result; -} - -} // namespace gui2 diff --git a/gui2/dataloader/defaultparser.h b/gui2/dataloader/defaultparser.h deleted file mode 100644 index 43808ecfb51..00000000000 --- a/gui2/dataloader/defaultparser.h +++ /dev/null @@ -1,57 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/dataloader/defaultparser.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_DATALOADER_DEFAULTPARSER_H -#define BORNAGAIN_GUI2_DATALOADER_DEFAULTPARSER_H - -#include "darefl_export.h" -#include "gui2/dataloader/dataloader_types.h" -#include "gui2/dataloader/parserinterface.h" -#include <map> - -namespace gui2 { - -//! Provides basic algorirthm for parsing multi-string data representing content -//! of multi-column ASCII file. -//! + Skips empty lines or lines matching the prefix. -//! + Skips lines matching given line number pattern. -//! + Parse data in columns of basing on given separator value. - -// #import Here is a parser implementation (the only present parser so far) -class DAREFLCORE_EXPORT DefaultParser : public ParserInterface { -public: - DefaultParser(const ParserOptions& options); - - void process(const std::vector<std::string>& raw_data) override; - - size_t totalLineCount() const override; - - std::string getLine(size_t index) const override; - - std::vector<std::string> parseResults(size_t index) const override; - - std::vector<std::vector<std::string>> parsedData() const override; - -private: - accept_int_t m_isSkipLineNumber; - accept_string_t m_isValidLineContent; - line_splitter_t m_line_splitter; - std::vector<std::string> m_rawData; - //!< correspondence of parsed data to original line index - std::map<size_t, std::vector<std::string>> m_parsedData; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_DATALOADER_DEFAULTPARSER_H diff --git a/gui2/dataloader/importfilewidget.cpp b/gui2/dataloader/importfilewidget.cpp deleted file mode 100644 index 4c8d90b76fb..00000000000 --- a/gui2/dataloader/importfilewidget.cpp +++ /dev/null @@ -1,184 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/dataloader/importfilewidget.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/dataloader/importfilewidget.h" -#include "gui2/core/app_constants.h" -#include "mvvm/utils/binutils.h" -#include "mvvm/utils/fileutils.h" -#include <QFileDialog> -#include <QItemSelectionModel> -#include <QListView> -#include <QMessageBox> -#include <QSettings> -#include <QStringListModel> -#include <QVBoxLayout> - -namespace gui2 { - -namespace { -const QString current_workdir_key = "currentworkdir"; - -const QString workdir_setting_name() -{ - return GUI::Constants::DataLoaderGroupKey + "/" + current_workdir_key; -} - -} // namespace - -ImportFileWidget::ImportFileWidget(QWidget* parent) - : QWidget(parent), m_listView(new QListView), m_listModel(new QStringListModel(this)) -{ - readSettings(); - - auto layout = new QVBoxLayout(this); - layout->setContentsMargins(0, 0, 0, 0); - layout->addWidget(m_listView); - - m_listView->setModel(m_listModel); - m_listView->setEditTriggers(QAbstractItemView::NoEditTriggers); - m_listView->setAlternatingRowColors(true); - m_listView->setSelectionMode(QAbstractItemView::ExtendedSelection); - - connect(m_listView->selectionModel(), &QItemSelectionModel::selectionChanged, this, - &ImportFileWidget::fileSelectionChanged); -} - -ImportFileWidget::~ImportFileWidget() -{ - writeSettings(); -} - -//! Summons dialog for file selections, update list view with file names. - -void ImportFileWidget::onAddFilesRequest() -{ - QFileDialog dialog(this, "Select one or more files to load", m_currentWorkdir); - dialog.setFileMode(QFileDialog::ExistingFiles); - dialog.setNameFilter("Text (*.txt *.csv *.dat);; Other (*.*)"); - dialog.setOption(QFileDialog::DontUseNativeDialog); - QStringList file_names = dialog.exec() ? dialog.selectedFiles() : QStringList(); - - file_names = validateForBinaryFiles(file_names); - - if (file_names.empty()) - return; - - updateCurrentWorkdir(file_names); - addFileNamesToModel(file_names); -} - -//! Removes currently selected file - -void ImportFileWidget::onRemoveFileRequest() -{ - auto selected = m_listView->selectionModel()->selectedIndexes(); - while (!selected.empty()) { - m_listModel->removeRow(selected.back().row()); - selected = m_listView->selectionModel()->selectedIndexes(); - } - - emit fileNamesChanged(); - - makeLastSelected(); -} - -//! Retuns the list of all file names imported by the user. - -QStringList ImportFileWidget::fileNames() const -{ - return m_listModel->stringList(); -} - -//! Retuns the list of currently selected file names. - -QStringList ImportFileWidget::selectedFileNames() const -{ - QStringList result; - for (auto index : m_listView->selectionModel()->selectedIndexes()) - result.append(m_listModel->data(index).toString()); - return result; -} - -//! Loads widget settings. - -void ImportFileWidget::readSettings() -{ - QSettings settings; - m_currentWorkdir = QDir::homePath(); - - if (settings.contains(workdir_setting_name())) - m_currentWorkdir = settings.value(workdir_setting_name()).toString(); -} - -//! Writes widget settings. - -void ImportFileWidget::writeSettings() -{ - QSettings settings; - settings.setValue(workdir_setting_name(), m_currentWorkdir); -} - -//! Returns list validated for binary files. - -QStringList ImportFileWidget::validateForBinaryFiles(const QStringList& file_names) -{ - QStringList result; - for (const auto& file_name : file_names) { - if (ModelView::Utils::is_binary(file_name.toStdString())) { - QMessageBox msgBox; - msgBox.setText(file_name + "\nmay be a binary file. Open it anyway?"); - msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); - int ret = msgBox.exec(); - if (ret == QMessageBox::Yes) - result.push_back(file_name); - } else { - result.push_back(file_name); - } - } - return result; -} - -//! Updates current working dir. - -void ImportFileWidget::updateCurrentWorkdir(const QStringList& file_names) -{ - auto file_name = file_names.back(); - auto parent_path = ModelView::Utils::parent_path(file_name.toStdString()); - m_currentWorkdir = QString::fromStdString(parent_path); -} - -//! Adds given list of file names to the model. - -void ImportFileWidget::addFileNamesToModel(const QStringList& file_names) -{ - auto current_names = fileNames(); - QStringList updated_names = current_names + file_names; - updated_names.removeDuplicates(); - m_listModel->setStringList(updated_names); - - emit fileNamesChanged(); - - makeLastSelected(); -} - -void ImportFileWidget::makeLastSelected() -{ - if (m_listView->selectionModel()->selectedIndexes().empty()) { - auto flags = QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows; - auto toSelect = m_listModel->index(m_listModel->rowCount() - 1); - m_listView->selectionModel()->select(toSelect, flags); - } -} - -} // namespace gui2 diff --git a/gui2/dataloader/importfilewidget.h b/gui2/dataloader/importfilewidget.h deleted file mode 100644 index c9360e5def2..00000000000 --- a/gui2/dataloader/importfilewidget.h +++ /dev/null @@ -1,67 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/dataloader/importfilewidget.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_DATALOADER_IMPORTFILEWIDGET_H -#define BORNAGAIN_GUI2_DATALOADER_IMPORTFILEWIDGET_H - -#include "darefl_export.h" -#include <QWidget> - -class QListView; -class QStringListModel; - -namespace gui2 { - -//! Provides the possibility to select file names on disk and add them to list view. -//! List represents names of ASCII files which will be later imported and parsed. -//! Part of LoaderPreviewPanel. - -class DAREFLCORE_EXPORT ImportFileWidget : public QWidget { - Q_OBJECT - -public: - ImportFileWidget(QWidget* parent = nullptr); - ~ImportFileWidget(); - -public slots: - void onAddFilesRequest(); - void onRemoveFileRequest(); - - QStringList fileNames() const; - - QStringList selectedFileNames() const; - -signals: - void fileNamesChanged(); - void fileSelectionChanged(); - -private: - void readSettings(); - void writeSettings(); - - QStringList validateForBinaryFiles(const QStringList& file_names); - - void updateCurrentWorkdir(const QStringList& file_names); - void addFileNamesToModel(const QStringList& file_names); - - void makeLastSelected(); - - QListView* m_listView{nullptr}; - QStringListModel* m_listModel{nullptr}; - QString m_currentWorkdir; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_DATALOADER_IMPORTFILEWIDGET_H diff --git a/gui2/dataloader/importtableheader.cpp b/gui2/dataloader/importtableheader.cpp deleted file mode 100644 index cdae6c799dd..00000000000 --- a/gui2/dataloader/importtableheader.cpp +++ /dev/null @@ -1,136 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/dataloader/importtableheader.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/dataloader/importtableheader.h" -#include "gui2/dataloader/dataloader_constants.h" -#include "gui2/dataloader/dataloader_types.h" -#include "mvvm/model/comboproperty.h" - -using ModelView::ComboProperty; - -namespace gui2 { - -namespace { -const std::vector<std::string> utilityRowNames = {"Type", "Unit", "Multiplier"}; - -const std::vector<std::string> typeNames = {GUI::Constants::AxisType, GUI::Constants::IntensityType, - GUI::Constants::IgnoreType}; - -const std::vector<std::string> unitNames = {"a.u.", "counts", "1/nm", "Angstrom"}; - -//! Returns column type from column index. -std::string suggestColumnTypeFromColumnIndex(int col) -{ - return col < static_cast<int>(typeNames.size()) ? typeNames[col] : GUI::Constants::IgnoreType; -} - -QVariant CreateTypeVariant(int col = 0) -{ - auto combo = ComboProperty::createFrom(typeNames); - auto selected_value = suggestColumnTypeFromColumnIndex(col); - combo.setValue(selected_value); - return QVariant::fromValue<ComboProperty>(combo); -} - -std::vector<QVariant> CreateTypeVariants(int maxColumnCount) -{ - std::vector<QVariant> result; - for (int i = 0; i < maxColumnCount; ++i) - result.push_back(CreateTypeVariant(i)); - return result; -} - -std::vector<QVariant> CreateUnitVariants(int maxColumnCount) -{ - std::vector<QVariant> result; - for (int i = 0; i < maxColumnCount; ++i) - result.push_back(QVariant::fromValue<ComboProperty>(ComboProperty::createFrom(unitNames))); - return result; -} - -std::vector<QVariant> CreateMultiplierVariants(int maxColumnCount) -{ - std::vector<QVariant> result(maxColumnCount, 1.0); - return result; -} - -} // namespace - -ImportTableHeader::ImportTableHeader(int max_column_count) : m_maxColumnCount(max_column_count) -{ - init_data(); -} - -int ImportTableHeader::rowCount() const -{ - return utilityRowNames.size(); -} - -int ImportTableHeader::columnCount() const -{ - return m_maxColumnCount; -} - -QVariant ImportTableHeader::data(int row, int column) const -{ - return isValid(row, column) ? m_data[row][column] : QVariant(); -} - -bool ImportTableHeader::setData(int row, int column, const QVariant& variant) -{ - if (isValid(row, column)) { - m_data[row][column] = variant; - return true; - } - - return false; -} - -std::string ImportTableHeader::rowName(int row) const -{ - return utilityRowNames[row]; -} - -std::vector<ColumnInfo> ImportTableHeader::columnInfo() const -{ - std::vector<ColumnInfo> result; - for (int column = 0; column < columnCount(); ++column) { - ColumnInfo info; - info.column = column; - info.type_name = data(TYPE, column).value<ComboProperty>().value(); - info.units = data(UNITS, column).value<ComboProperty>().value(); - info.multiplier = data(MULTIPLIER, column).value<double>(); - result.push_back(info); - } - - return result; -} - -void ImportTableHeader::init_data() -{ - m_data.resize(MAX); - m_data[TYPE] = CreateTypeVariants(columnCount()); - m_data[UNITS] = CreateUnitVariants(columnCount()); - m_data[MULTIPLIER] = CreateMultiplierVariants(columnCount()); -} - -//! Returns true if given pair of indices are valid for data array. - -bool ImportTableHeader::isValid(int row, int column) const -{ - return (row >= 0 && row < static_cast<int>(m_data.size())) - && (column >= 0 && column < static_cast<int>(m_data[row].size())); -} - -} // namespace gui2 diff --git a/gui2/dataloader/importtableheader.h b/gui2/dataloader/importtableheader.h deleted file mode 100644 index 5c1247b4591..00000000000 --- a/gui2/dataloader/importtableheader.h +++ /dev/null @@ -1,57 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/dataloader/importtableheader.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_DATALOADER_IMPORTTABLEHEADER_H -#define BORNAGAIN_GUI2_DATALOADER_IMPORTTABLEHEADER_H - -#include "darefl_export.h" -#include <QVariant> -#include <vector> - -namespace gui2 { - -struct ColumnInfo; - -//! Holds all data related to the content of utility rows in ImportTableModel. - -class DAREFLCORE_EXPORT ImportTableHeader { -public: - enum RowTypes { TYPE, UNITS, MULTIPLIER, MAX }; - using header_data_t = std::vector<std::vector<QVariant>>; - - ImportTableHeader(int max_column_count); - - int rowCount() const; - - int columnCount() const; - - QVariant data(int row, int column) const; - - bool setData(int row, int column, const QVariant& variant); - - std::string rowName(int row) const; - - std::vector<ColumnInfo> columnInfo() const; - -private: - void init_data(); - bool isValid(int row, int column) const; - - header_data_t m_data; - int m_maxColumnCount; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_DATALOADER_IMPORTTABLEHEADER_H diff --git a/gui2/dataloader/importtablemodel.cpp b/gui2/dataloader/importtablemodel.cpp deleted file mode 100644 index 5b61de4fab7..00000000000 --- a/gui2/dataloader/importtablemodel.cpp +++ /dev/null @@ -1,141 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/dataloader/importtablemodel.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/dataloader/importtablemodel.h" -#include "gui2/dataloader/dataloader_types.h" -#include "gui2/dataloader/importtableheader.h" -#include <QBrush> - -namespace gui2 { - -namespace { -//! Returns maximum number of columns in 2D data vector. -int maxColumnCount(const ImportTableModel::raw_data_t& data) -{ - int result{0}; - for (const auto& x : data) - result = std::max(result, static_cast<int>(x.size())); - return result; -} - -const int default_header_ncols = 2; -}; // namespace - -ImportTableModel::ImportTableModel(QObject* parent) - : QAbstractTableModel(parent) - , m_header(std::make_unique<ImportTableHeader>(default_header_ncols)) -{ -} - -ImportTableModel::~ImportTableModel() = default; - -//! Sets content of the model. - -void ImportTableModel::setRawData(const ImportTableModel::raw_data_t& raw_data) -{ - beginResetModel(); - m_maxColumnCount = maxColumnCount(raw_data); - m_header = std::make_unique<ImportTableHeader>(m_maxColumnCount); - m_rawData = raw_data; - endResetModel(); -} - -int ImportTableModel::rowCount(const QModelIndex&) const -{ - return static_cast<int>(m_rawData.size()) + utilityRowCount(); -} - -int ImportTableModel::columnCount(const QModelIndex&) const -{ - return m_maxColumnCount; -} - -QVariant ImportTableModel::data(const QModelIndex& index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - if (role == Qt::DisplayRole || role == Qt::EditRole) - return dataFromIndex(index); - - else if (role == Qt::BackgroundRole && index.row() < utilityRowCount()) - return QBrush(Qt::lightGray); - - return QVariant(); -} - -bool ImportTableModel::setData(const QModelIndex& index, const QVariant& value, int role) -{ - if (!index.isValid()) - return false; - - if (index.row() < utilityRowCount() && role == Qt::EditRole) - return m_header->setData(index.row(), index.column(), value); - - return false; -} - -QVariant ImportTableModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (orientation == Qt::Horizontal || role != Qt::DisplayRole) - return QVariant(); - - return section < utilityRowCount() ? QString::fromStdString(m_header->rowName(section)) - : QVariant(section - utilityRowCount() + 1); -} - -Qt::ItemFlags ImportTableModel::flags(const QModelIndex& index) const -{ - Qt::ItemFlags result = QAbstractItemModel::flags(index); - if (index.row() < utilityRowCount()) - result |= Qt::ItemIsEnabled | Qt::ItemIsEditable; - else - result |= Qt::ItemIsEnabled | Qt::ItemIsSelectable; - return result; -} - -std::vector<ColumnInfo> ImportTableModel::columnInfo() const -{ - return m_header->columnInfo(); -} - -int ImportTableModel::utilityRowCount() const -{ - return m_header ? m_header->rowCount() : 0; -} - -//! Returns data from index. Combines header data with parsed user data. - -QVariant ImportTableModel::dataFromIndex(const QModelIndex& index) const -{ - if (!index.isValid()) - return QVariant(); - - if (index.row() < utilityRowCount()) { - return m_header->data(index.row(), index.column()); - - } else { - int row = index.row() - utilityRowCount(); - if (row >= 0 && row < static_cast<int>(m_rawData.size())) { - int col = index.column(); - if (col >= 0 && col < static_cast<int>(m_rawData[row].size())) { - auto str = m_rawData[static_cast<int>(row)][static_cast<int>(col)]; - return QString::fromStdString(str); - } - } - } - return QVariant(); -} - -} // namespace gui2 diff --git a/gui2/dataloader/importtablemodel.h b/gui2/dataloader/importtablemodel.h deleted file mode 100644 index 6817673dc1b..00000000000 --- a/gui2/dataloader/importtablemodel.h +++ /dev/null @@ -1,69 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/dataloader/importtablemodel.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_DATALOADER_IMPORTTABLEMODEL_H -#define BORNAGAIN_GUI2_DATALOADER_IMPORTTABLEMODEL_H - -#include "darefl_export.h" -#include <QAbstractTableModel> -#include <memory> -#include <string> -#include <vector> - -namespace gui2 { - -struct ColumnInfo; - -class ImportTableHeader; - -//! Table model to hold imported ASCII data after parsing it to multi-column presentation. - -class DAREFLCORE_EXPORT ImportTableModel : public QAbstractTableModel { - Q_OBJECT - -public: - using raw_data_t = std::vector<std::vector<std::string>>; - - ImportTableModel(QObject* parent = nullptr); - ~ImportTableModel() override; - - void setRawData(const raw_data_t& raw_data); - - int rowCount(const QModelIndex& = QModelIndex()) const override; - - int columnCount(const QModelIndex& = QModelIndex()) const override; - - QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; - - bool setData(const QModelIndex& index, const QVariant& value, int role) override; - - QVariant headerData(int section, Qt::Orientation orientation, int role) const override; - - Qt::ItemFlags flags(const QModelIndex& index) const override; - - std::vector<ColumnInfo> columnInfo() const; - -private: - int utilityRowCount() const; - QVariant dataFromIndex(const QModelIndex& index) const; - - std::unique_ptr<ImportTableHeader> m_header; - raw_data_t m_rawData; //! parsed column data - - int m_maxColumnCount{0}; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_DATALOADER_IMPORTTABLEMODEL_H diff --git a/gui2/dataloader/importtablewidget.cpp b/gui2/dataloader/importtablewidget.cpp deleted file mode 100644 index f2ae8123a44..00000000000 --- a/gui2/dataloader/importtablewidget.cpp +++ /dev/null @@ -1,51 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/dataloader/importtablewidget.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/dataloader/importtablewidget.h" -#include "gui2/dataloader/dataloader_types.h" -#include "gui2/dataloader/importtablemodel.h" -#include "mvvm/viewmodel/viewmodeldelegate.h" -#include <QStandardItemModel> -#include <QTableView> -#include <QVBoxLayout> - -namespace gui2 { - -ImportTableWidget::ImportTableWidget(QWidget* parent) - : QWidget(parent) - , m_tableModel(new ImportTableModel(this)) - , m_tableView(new QTableView) - , m_delegate(new ModelView::ViewModelDelegate) -{ - auto layout = new QVBoxLayout(this); - layout->setContentsMargins(0, 0, 0, 0); - layout->addWidget(m_tableView); - - m_tableView->setItemDelegate(m_delegate.get()); - m_tableView->setModel(m_tableModel); -} - -ImportTableWidget::~ImportTableWidget() = default; - -void ImportTableWidget::setRawData(const std::vector<std::vector<std::string>>& table_data) -{ - m_tableModel->setRawData(table_data); -} - -std::vector<ColumnInfo> ImportTableWidget::columnInfo() const -{ - return m_tableModel->columnInfo(); -} - -} // namespace gui2 diff --git a/gui2/dataloader/importtablewidget.h b/gui2/dataloader/importtablewidget.h deleted file mode 100644 index 69e9ba1de99..00000000000 --- a/gui2/dataloader/importtablewidget.h +++ /dev/null @@ -1,58 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/dataloader/importtablewidget.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_DATALOADER_IMPORTTABLEWIDGET_H -#define BORNAGAIN_GUI2_DATALOADER_IMPORTTABLEWIDGET_H - -#include "darefl_export.h" -#include <QWidget> -#include <memory> -#include <string> -#include <vector> - -class QTableView; - -namespace ModelView { -class ViewModelDelegate; -} - -namespace gui2 { - -struct ColumnInfo; - -class ImportTableModel; - -//! Contains table with imported data. -//! Belongs to LoaderPreviewPanel. - -class DAREFLCORE_EXPORT ImportTableWidget : public QWidget { - Q_OBJECT - -public: - ImportTableWidget(QWidget* parent = nullptr); - ~ImportTableWidget(); - - void setRawData(const std::vector<std::vector<std::string>>& table_data); - - std::vector<ColumnInfo> columnInfo() const; - -private: - ImportTableModel* m_tableModel{nullptr}; - QTableView* m_tableView{nullptr}; - std::unique_ptr<ModelView::ViewModelDelegate> m_delegate; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_DATALOADER_IMPORTTABLEWIDGET_H diff --git a/gui2/dataloader/importtextview.cpp b/gui2/dataloader/importtextview.cpp deleted file mode 100644 index ce43335592e..00000000000 --- a/gui2/dataloader/importtextview.cpp +++ /dev/null @@ -1,131 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/dataloader/importtextview.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/dataloader/importtextview.h" -#include "mvvm/widgets/widgetutils.h" - -//! Based on Qt example "codeeditor" -//! Copyright (C) 2016 The Qt Company Ltd. - -#include <QPainter> -#include <QTextBlock> - -namespace gui2 { - -namespace { -const int line_number_gap = 4; -} - -ImportTextView::ImportTextView(QWidget* parent) : QPlainTextEdit(parent) -{ - lineNumberArea = new LineNumberArea(this); - - connect(this, &ImportTextView::blockCountChanged, this, - &ImportTextView::updateLineNumberAreaWidth); - connect(this, &ImportTextView::updateRequest, this, &ImportTextView::updateLineNumberArea); - connect(this, &ImportTextView::cursorPositionChanged, this, - &ImportTextView::highlightCurrentLine); - - updateLineNumberAreaWidth(0); - // highlightCurrentLine(); - - setReadOnly(true); - setWordWrapMode(QTextOption::NoWrap); - - setFont(QFont("Monospace", ModelView::Utils::SystemPointSize() * 0.8, QFont::Light)); -} - -int ImportTextView::lineNumberAreaWidth() -{ - int digits = 1; - int max = qMax(1, blockCount()); - while (max >= 10) { - max /= 10; - ++digits; - } - - int space = line_number_gap * 2 + fontMetrics().horizontalAdvance(QLatin1Char('9')) * digits; - - return space; -} - -void ImportTextView::updateLineNumberAreaWidth(int /* newBlockCount */) -{ - setViewportMargins(lineNumberAreaWidth(), 0, 0, 0); -} - -void ImportTextView::updateLineNumberArea(const QRect& rect, int dy) -{ - if (dy) - lineNumberArea->scroll(0, dy); - else - lineNumberArea->update(0, rect.y(), lineNumberArea->width(), rect.height()); - - if (rect.contains(viewport()->rect())) - updateLineNumberAreaWidth(0); -} - -void ImportTextView::resizeEvent(QResizeEvent* e) -{ - QPlainTextEdit::resizeEvent(e); - - QRect cr = contentsRect(); - lineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height())); -} - -void ImportTextView::highlightCurrentLine() -{ - QList<QTextEdit::ExtraSelection> extraSelections; - - if (!isReadOnly()) { - QTextEdit::ExtraSelection selection; - - QColor lineColor = QColor(Qt::yellow).lighter(160); - - selection.format.setBackground(lineColor); - selection.format.setProperty(QTextFormat::FullWidthSelection, true); - selection.cursor = textCursor(); - selection.cursor.clearSelection(); - extraSelections.append(selection); - } - - setExtraSelections(extraSelections); -} - -void ImportTextView::lineNumberAreaPaintEvent(QPaintEvent* event) -{ - QPainter painter(lineNumberArea); - painter.fillRect(event->rect(), Qt::lightGray); - - QTextBlock block = firstVisibleBlock(); - int blockNumber = block.blockNumber(); - int top = qRound(blockBoundingGeometry(block).translated(contentOffset()).top()); - int bottom = top + qRound(blockBoundingRect(block).height()); - - while (block.isValid() && top <= event->rect().bottom()) { - if (block.isVisible() && bottom >= event->rect().top()) { - QString number = QString::number(blockNumber + 1); - painter.setPen(Qt::black); - painter.drawText(0, top, lineNumberArea->width() - line_number_gap, - fontMetrics().height(), Qt::AlignRight, number); - } - - block = block.next(); - top = bottom; - bottom = top + qRound(blockBoundingRect(block).height()); - ++blockNumber; - } -} - -} // namespace gui2 diff --git a/gui2/dataloader/importtextview.h b/gui2/dataloader/importtextview.h deleted file mode 100644 index 6a75eb91f83..00000000000 --- a/gui2/dataloader/importtextview.h +++ /dev/null @@ -1,74 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/dataloader/importtextview.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_DATALOADER_IMPORTTEXTVIEW_H -#define BORNAGAIN_GUI2_DATALOADER_IMPORTTEXTVIEW_H - -#include "darefl_export.h" - -//! Based on Qt example "codeeditor" -//! Copyright (C) 2016 The Qt Company Ltd. - -#include <QPlainTextEdit> - -class QPaintEvent; -class QResizeEvent; -class QSize; -class QWidget; - -namespace gui2 { - -class LineNumberArea; - -//! Text view to show imported data. - -class DAREFLCORE_EXPORT ImportTextView : public QPlainTextEdit { - Q_OBJECT - -public: - ImportTextView(QWidget* parent = nullptr); - - void lineNumberAreaPaintEvent(QPaintEvent* event); - int lineNumberAreaWidth(); - -protected: - void resizeEvent(QResizeEvent* event) override; - -private slots: - void updateLineNumberAreaWidth(int newBlockCount); - void highlightCurrentLine(); - void updateLineNumberArea(const QRect& rect, int dy); - -private: - QWidget* lineNumberArea; -}; - -//! Area with line numbers. - -class DAREFLCORE_EXPORT LineNumberArea : public QWidget { -public: - LineNumberArea(ImportTextView* editor) : QWidget(editor), codeEditor(editor) {} - - QSize sizeHint() const override { return QSize(codeEditor->lineNumberAreaWidth(), 0); } - -protected: - void paintEvent(QPaintEvent* event) override { codeEditor->lineNumberAreaPaintEvent(event); } - -private: - ImportTextView* codeEditor; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_DATALOADER_IMPORTTEXTVIEW_H diff --git a/gui2/dataloader/loaderpreviewpanel.cpp b/gui2/dataloader/loaderpreviewpanel.cpp deleted file mode 100644 index de72210de92..00000000000 --- a/gui2/dataloader/loaderpreviewpanel.cpp +++ /dev/null @@ -1,74 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/dataloader/loaderpreviewpanel.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/dataloader/loaderpreviewpanel.h" -#include "gui2/dataloader/dataloader_utils.h" -#include "gui2/dataloader/importtablewidget.h" -#include "gui2/dataloader/importtextview.h" -#include "gui2/dataloader/parserinterface.h" -#include <QColor> -#include <QTabWidget> -#include <QVBoxLayout> - -namespace gui2 { - -namespace { -const std::string gray{"#aab7b8"}; -const std::string blue{"#1b4f72"}; - -} // namespace - -LoaderPreviewPanel::LoaderPreviewPanel(QWidget* parent) - : QWidget(parent) - , m_textView(new ImportTextView) - , m_tableWidget(new ImportTableWidget) - , m_tabWidget(new QTabWidget) -{ - auto layout = new QVBoxLayout(this); - layout->setContentsMargins(0, 0, 0, 0); - - m_tabWidget->addTab(m_textView, "Text view"); - m_tabWidget->addTab(m_tableWidget, "Table view"); - - layout->addWidget(m_tabWidget); -} - -//! Sets raw text to the TextView. - -void LoaderPreviewPanel::showData(const ParserInterface* parser) -{ - m_textView->clear(); - for (size_t index = 0; index < parser->totalLineCount(); ++index) { - auto line_data = parser->getLine(index); - auto parts = parser->parseResults(index); - auto string_to_show = Utils::AddHtmlColorTagToParts(line_data, parts, blue, gray); - m_textView->appendHtml(QString::fromStdString(string_to_show)); - } - m_textView->moveCursor(QTextCursor::Start); - - m_tableWidget->setRawData(parser->parsedData()); -} - -std::vector<ColumnInfo> LoaderPreviewPanel::columnInfo() const -{ - return m_tableWidget->columnInfo(); -} - -void LoaderPreviewPanel::clearPanel() -{ - m_textView->clear(); - m_tableWidget->setRawData({}); -} - -} // namespace gui2 diff --git a/gui2/dataloader/loaderpreviewpanel.h b/gui2/dataloader/loaderpreviewpanel.h deleted file mode 100644 index c367f8a9ced..00000000000 --- a/gui2/dataloader/loaderpreviewpanel.h +++ /dev/null @@ -1,54 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/dataloader/loaderpreviewpanel.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_DATALOADER_LOADERPREVIEWPANEL_H -#define BORNAGAIN_GUI2_DATALOADER_LOADERPREVIEWPANEL_H - -#include "darefl_export.h" -#include <QWidget> - -class QTabWidget; - -namespace gui2 { - -class ImportTextView; -class ImportTableWidget; - -class ParserInterface; -struct ColumnInfo; - -//! Panel with settings for DataLoaderDialog. -//! Located on its right side, contains text and table views. - -class DAREFLCORE_EXPORT LoaderPreviewPanel : public QWidget { - Q_OBJECT - -public: - LoaderPreviewPanel(QWidget* parent = nullptr); - - void showData(const ParserInterface* parser); - - std::vector<ColumnInfo> columnInfo() const; - - void clearPanel(); - -private: - ImportTextView* m_textView{nullptr}; - ImportTableWidget* m_tableWidget{nullptr}; - QTabWidget* m_tabWidget{nullptr}; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_DATALOADER_LOADERPREVIEWPANEL_H diff --git a/gui2/dataloader/loaderselectorpanel.cpp b/gui2/dataloader/loaderselectorpanel.cpp deleted file mode 100644 index c397d96a8ee..00000000000 --- a/gui2/dataloader/loaderselectorpanel.cpp +++ /dev/null @@ -1,98 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/dataloader/loaderselectorpanel.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/dataloader/loaderselectorpanel.h" -#include "gui2/dataloader/defaultparser.h" -#include "gui2/dataloader/importfilewidget.h" -#include "gui2/dataloader/parserpropertywidget.h" -#include <QSplitter> -#include <QVBoxLayout> - -namespace gui2 { - -LoaderSelectorPanel::LoaderSelectorPanel(QWidget* parent) - : QWidget(parent) - , m_fileSelectorWidget(new ImportFileWidget) - , m_propertyWidget(new ParserPropertyWidget) - , m_splitter(new QSplitter) -{ - auto layout = new QVBoxLayout(this); - layout->setContentsMargins(0, 0, 0, 0); - - m_splitter->setOrientation(Qt::Vertical); - m_splitter->setChildrenCollapsible(false); - - m_splitter->addWidget(m_fileSelectorWidget); - m_splitter->addWidget(m_propertyWidget); - - layout->addWidget(m_splitter); - - init_connections(); -} - -LoaderSelectorPanel::~LoaderSelectorPanel() = default; - -std::unique_ptr<ParserInterface> LoaderSelectorPanel::createParser() const -{ - return m_propertyWidget->createParser(); -} - -void LoaderSelectorPanel::setTargetCanvas(const QStringList& canvas_names, int current_index) -{ - m_propertyWidget->setTargetCanvas(canvas_names, current_index); -} - -int LoaderSelectorPanel::targetCanvasIndex() const -{ - return m_targetCanvasIndex; -} - -void LoaderSelectorPanel::onAddFilesRequest() -{ - m_fileSelectorWidget->onAddFilesRequest(); -} - -void LoaderSelectorPanel::onRemoveFileRequest() -{ - m_fileSelectorWidget->onRemoveFileRequest(); -} - -QStringList LoaderSelectorPanel::selectedFileNames() const -{ - return m_fileSelectorWidget->selectedFileNames(); -} - -QStringList LoaderSelectorPanel::fileNames() const -{ - return m_fileSelectorWidget->fileNames(); -} - -void LoaderSelectorPanel::init_connections() -{ - auto on_file_names_changed = [this]() { fileNamesChanged(m_fileSelectorWidget->fileNames()); }; - connect(m_fileSelectorWidget, &ImportFileWidget::fileNamesChanged, on_file_names_changed); - - auto on_selection_changed = [this]() { - fileSelectionChanged(m_fileSelectorWidget->selectedFileNames()); - }; - connect(m_fileSelectorWidget, &ImportFileWidget::fileSelectionChanged, on_selection_changed); - - connect(m_propertyWidget, &ParserPropertyWidget::parserPropertyChanged, this, - &LoaderSelectorPanel::parserPropertyChanged); - - auto on_target_changed = [this](auto index) { m_targetCanvasIndex = index; }; - connect(m_propertyWidget, &ParserPropertyWidget::targetCanvasChanged, on_target_changed); -} - -} // namespace gui2 diff --git a/gui2/dataloader/loaderselectorpanel.h b/gui2/dataloader/loaderselectorpanel.h deleted file mode 100644 index ec73d41a16a..00000000000 --- a/gui2/dataloader/loaderselectorpanel.h +++ /dev/null @@ -1,68 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/dataloader/loaderselectorpanel.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_DATALOADER_LOADERSELECTORPANEL_H -#define BORNAGAIN_GUI2_DATALOADER_LOADERSELECTORPANEL_H - -#include "darefl_export.h" -#include <QWidget> -#include <memory> - -class QSplitter; - -namespace gui2 { - -class ParserInterface; -class ImportFileWidget; -class ParserPropertyWidget; - -//! Panel with settings for DataLoaderDialog. -//! Located on its left side, contains file selection dialog and parser property widget. - -class DAREFLCORE_EXPORT LoaderSelectorPanel : public QWidget { - Q_OBJECT - -public: - LoaderSelectorPanel(QWidget* parent = nullptr); - ~LoaderSelectorPanel(); - - std::unique_ptr<ParserInterface> createParser() const; - - void setTargetCanvas(const QStringList& canvas_names, int current_index); - - int targetCanvasIndex() const; - -public slots: - void onAddFilesRequest(); - void onRemoveFileRequest(); - QStringList selectedFileNames() const; - QStringList fileNames() const; - -signals: - void fileNamesChanged(const QStringList& file_names); - void fileSelectionChanged(const QStringList& file_names); - void parserPropertyChanged(); - -private: - void init_connections(); - - int m_targetCanvasIndex{-1}; - ImportFileWidget* m_fileSelectorWidget{nullptr}; - ParserPropertyWidget* m_propertyWidget{nullptr}; - QSplitter* m_splitter{nullptr}; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_DATALOADER_LOADERSELECTORPANEL_H diff --git a/gui2/dataloader/parserinterface.h b/gui2/dataloader/parserinterface.h deleted file mode 100644 index 8a0ea786d53..00000000000 --- a/gui2/dataloader/parserinterface.h +++ /dev/null @@ -1,49 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/dataloader/parserinterface.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_DATALOADER_PARSERINTERFACE_H -#define BORNAGAIN_GUI2_DATALOADER_PARSERINTERFACE_H - -#include "darefl_export.h" -#include <string> -#include <vector> - -namespace gui2 { - -//! Interface for all classes capable of parsing ASCII data into multicolumn presentation. - -class DAREFLCORE_EXPORT ParserInterface { // #import Here is where the data gets parsed -public: - virtual ~ParserInterface() = default; - - //! Parse data representing content of ASCII file. - virtual void process(const std::vector<std::string>& raw_data) = 0; - - //! Returns total number of lines in raw data. - virtual size_t totalLineCount() const = 0; - - //! Returns original line. - virtual std::string getLine(size_t index) const = 0; - - //! Returns parsed text for given line index. If line was skipped during parsing, returns empty - //! vector. - virtual std::vector<std::string> parseResults(size_t index) const = 0; - - //! Returns 2D vector representing parsed text. Skipped lines are not present. - virtual std::vector<std::vector<std::string>> parsedData() const = 0; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_DATALOADER_PARSERINTERFACE_H diff --git a/gui2/dataloader/parserpropertywidget.cpp b/gui2/dataloader/parserpropertywidget.cpp deleted file mode 100644 index 9caebe5871c..00000000000 --- a/gui2/dataloader/parserpropertywidget.cpp +++ /dev/null @@ -1,362 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/dataloader/parserpropertywidget.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/dataloader/parserpropertywidget.h" -#include "gui2/core/app_constants.h" -#include "gui2/dataloader/defaultparser.h" -#include "mvvm/widgets/widgetutils.h" -#include <QButtonGroup> -#include <QComboBox> -#include <QDebug> -#include <QGridLayout> -#include <QGroupBox> -#include <QLabel> -#include <QLineEdit> -#include <QRadioButton> -#include <QRegExpValidator> -#include <QSettings> -#include <QVBoxLayout> - -namespace { - -//! Creates widget with label and little space above. Intended for grid layouts. -QWidget* createSectionWidget(const QString& text) -{ - auto widget = new QWidget; - auto layout = new QVBoxLayout(widget); - layout->addSpacing(10); - auto label = new QLabel(text); - QFont font = label->font(); - font.setPointSize(ModelView::Utils::SystemPointSize() * 1.05); - label->setFont(font); - layout->addWidget(label); - return widget; -} - -const QString separatorgroupid_setting_name() -{ - const QString key = "separatorgroup_id"; - return gui2::GUI::Constants::ParserPropertyGroupKey + "/" + key; -} - -const QString customseparatortext_setting_name() -{ - const QString key = "customseparator_text"; - return gui2::GUI::Constants::ParserPropertyGroupKey + "/" + key; -} - -} // namespace - -namespace gui2 { - -ParserPropertyWidget::ParserPropertyWidget(QWidget* parent) : QWidget(parent) -{ - auto layout = new QVBoxLayout(this); - layout->addLayout(createGridLayout()); - layout->addStretch(1); - - readSettings(); -} - -ParserPropertyWidget::~ParserPropertyWidget() -{ - writeSettings(); -} - -//! Creates parser from parser properties. - -std::unique_ptr<ParserInterface> ParserPropertyWidget::createParser() const -{ - ParserOptions options = m_options; - - // sanity check for empty separator - options.m_separator = options.m_separator.empty() ? std::string(" ") : options.m_separator; - return std::make_unique<DefaultParser>(options); -} - -//! Sets list of canvas names as possible import targets. - -void ParserPropertyWidget::setTargetCanvas(const QStringList& canvas_names, int current_index) -{ - m_targetCanvasCombo->insertItems(0, canvas_names); - m_targetCanvasCombo->setCurrentIndex(current_index); -} - -void ParserPropertyWidget::onParserPropertyChange() -{ - qDebug() << "option" - << "header:" << QString::fromStdString(m_options.m_header_prefix) - << "separator:" << QString::fromStdString(m_options.m_separator) << "pattern" - << QString::fromStdString(m_options.m_skip_index_pattern); - emit parserPropertyChanged(); -} - -//! Reads widget settings. - -void ParserPropertyWidget::readSettings() -{ - QSettings settings; - - if (settings.contains(separatorgroupid_setting_name())) { - int button_id = settings.value(separatorgroupid_setting_name()).toInt(); - if (auto button = m_separatorButtonGroup->button(button_id); button) { - button->click(); - } - } - - if (settings.contains(customseparatortext_setting_name())) - m_customSeparatorLineEdit->setText( - settings.value(customseparatortext_setting_name()).toString()); -} - -//! Writes widget settings. - -void ParserPropertyWidget::writeSettings() -{ - QSettings settings; - - settings.setValue(separatorgroupid_setting_name(), m_separatorButtonGroup->checkedId()); - settings.setValue(customseparatortext_setting_name(), m_customSeparatorLineEdit->text()); -} - -QGridLayout* ParserPropertyWidget::createGridLayout() -{ - auto grid_layout = new QGridLayout; - - addSectionLabel("Separator", grid_layout); - m_separatorButtonGroup = new QButtonGroup(this); - addStandardSeparatorRow(grid_layout, m_separatorButtonGroup); - addCustomSeparatorRow(grid_layout, m_separatorButtonGroup); - - addSectionLabel("Ignore lines", grid_layout); - addIgnoreStringPatternRow(grid_layout); - addIgnoreNumbersPatternRow(grid_layout); - - addSectionLabel("Import target", grid_layout); - addImportToBlock(grid_layout); - - // make first colum with invisible label fixed - for (int col = 0; col < grid_layout->columnCount(); ++col) - grid_layout->setColumnStretch(col, 10); - grid_layout->setColumnStretch(0, 0); - - return grid_layout; -} - -void ParserPropertyWidget::addSectionLabel(const QString& text, QGridLayout* layout) -{ - int row = layout->rowCount(); - layout->addWidget(createSectionWidget(text), row, 0, 1, 3, Qt::AlignLeft); -} - -//! Adds row to the grid: elements with standard separator settings. - -void ParserPropertyWidget::addStandardSeparatorRow(QGridLayout* layout, QButtonGroup* group) -{ - // automatic separator - int row = layout->rowCount(); - auto automaticRadio = new QRadioButton; - automaticRadio->setChecked(true); - automaticRadio->setText("Automatic"); - automaticRadio->setToolTip("Try to guess column separator"); - connect(automaticRadio, &QRadioButton::clicked, [this](auto) { - m_options.m_separator.clear(); - onParserPropertyChange(); - }); - - // space separator - auto spaceRadio = new QRadioButton; - spaceRadio->setText("Space"); - spaceRadio->setToolTip("Use empty space as column separator"); - connect(spaceRadio, &QRadioButton::clicked, [this](auto) { - m_options.m_separator = " "; - onParserPropertyChange(); - }); - - // comma separator - auto commaRadio = new QRadioButton; - commaRadio->setText("Comma"); - commaRadio->setToolTip("Use comma as column separator"); - connect(commaRadio, &QRadioButton::clicked, [this](auto) { - m_options.m_separator = ","; - onParserPropertyChange(); - }); - - // adding all to layout - layout->addWidget(new QLabel(" "), row, 0, Qt::AlignLeft); - layout->addWidget(automaticRadio, row, 1, Qt::AlignLeft); - layout->addWidget(spaceRadio, row, 2, Qt::AlignLeft); - layout->addWidget(commaRadio, row, 3, Qt::AlignLeft); - group->addButton(automaticRadio, AUTOMATIC); - group->addButton(spaceRadio, SPACE); - group->addButton(commaRadio, COMMA); -} - -//! Adds row to the grid: elements with custom separator settings. - -void ParserPropertyWidget::addCustomSeparatorRow(QGridLayout* layout, QButtonGroup* group) -{ - int row = layout->rowCount(); - m_customSeparatorLineEdit = new QLineEdit; - auto customRadio = new QRadioButton; - - // custom separator radio - customRadio->setText("Custom"); - customRadio->setToolTip("Use given symbols as column separator"); - auto on_custom_separator = [this](auto) { - m_options.m_separator = m_customSeparatorLineEdit->text().toStdString(); - onParserPropertyChange(); - }; - connect(customRadio, &QRadioButton::clicked, on_custom_separator); - - // custom separator text - m_customSeparatorLineEdit->setMaximumWidth(ModelView::Utils::WidthOfLetterM() * 4); - m_customSeparatorLineEdit->setToolTip("Use given symbols as column separator"); - auto on_custom_lineedit = [this, customRadio]() { - if (customRadio->isChecked()) - m_options.m_separator = m_customSeparatorLineEdit->text().toStdString(); - onParserPropertyChange(); - }; - connect(m_customSeparatorLineEdit, &QLineEdit::editingFinished, on_custom_lineedit); - - // adding to the layout - layout->addWidget(new QLabel(" "), row, 0, Qt::AlignLeft); - layout->addWidget(customRadio, row, 1, Qt::AlignLeft); - layout->addWidget(m_customSeparatorLineEdit, row, 2, Qt::AlignLeft); - group->addButton(customRadio, CUSTOM); -} - -//! Adds row to the grid: elements with pattern to ignore lines. - -void ParserPropertyWidget::addIgnoreStringPatternRow(QGridLayout* layout) -{ - auto startingWithRadio = new QRadioButton; - auto startingFromLineEdit = new QLineEdit; - - // radio settings - int row = layout->rowCount(); - startingWithRadio->setText("Starting with"); - startingWithRadio->setAutoExclusive(false); - startingWithRadio->setChecked(true); - startingWithRadio->setToolTip("Ignore lines starting with a given character(s)"); - auto on_startingfrom_radio = [this, startingFromLineEdit](auto checked) { - m_options.m_header_prefix = checked ? startingFromLineEdit->text().toStdString() : ""; - onParserPropertyChange(); - }; - connect(startingWithRadio, &QRadioButton::clicked, on_startingfrom_radio); - - // line edit settings - startingFromLineEdit->setText("#"); - startingFromLineEdit->setToolTip("Ignore lines starting with a given character(s)"); - auto on_startingfrom_lineedit = [this, startingWithRadio, startingFromLineEdit]() { - if (startingWithRadio->isChecked()) - m_options.m_header_prefix = startingFromLineEdit->text().toStdString(); - onParserPropertyChange(); - }; - connect(startingFromLineEdit, &QLineEdit::editingFinished, on_startingfrom_lineedit); - - // adding to layout - layout->addWidget(new QLabel(" "), row, 0, Qt::AlignLeft); - layout->addWidget(startingWithRadio, row, 1, Qt::AlignLeft); - layout->addWidget(startingFromLineEdit, row, 2, Qt::AlignLeft); -} - -//! Adds row to the grid: elements with pattern to ignore line numbers. - -void ParserPropertyWidget::addIgnoreNumbersPatternRow(QGridLayout* layout) -{ - auto lineNumbersRadio = new QRadioButton; - auto lineNumbersLineEdit = new QLineEdit; - - // radio settings - int row = layout->rowCount(); - lineNumbersRadio->setAutoExclusive(false); - lineNumbersRadio->setText("Line numbers"); - lineNumbersRadio->setToolTip("Ignore lines with line numbers matching the pattern"); - auto on_linenumbers_radio = [this, lineNumbersLineEdit](auto checked) { - m_options.m_skip_index_pattern = checked ? lineNumbersLineEdit->text().toStdString() : ""; - onParserPropertyChange(); - }; - connect(lineNumbersRadio, &QRadioButton::clicked, on_linenumbers_radio); - - // line edit settings - lineNumbersLineEdit->setPlaceholderText("Example: 1-5,42"); - lineNumbersLineEdit->setToolTip("Ignore lines with line numbers matching the pattern"); - auto on_linenumbers_lineedit = [this, lineNumbersRadio, lineNumbersLineEdit]() { - if (lineNumbersRadio->isChecked()) - m_options.m_skip_index_pattern = lineNumbersLineEdit->text().toStdString(); - onParserPropertyChange(); - }; - connect(lineNumbersLineEdit, &QLineEdit::editingFinished, on_linenumbers_lineedit); - - // adding to the layout - layout->addWidget(new QLabel(" "), row, 0, Qt::AlignLeft); - layout->addWidget(lineNumbersRadio, row, 1, Qt::AlignLeft); - layout->addWidget(lineNumbersLineEdit, row, 2, Qt::AlignLeft); - - // validator - auto validator = new QRegExpValidator(QRegExp("^[0-9,-]*$"), this); - lineNumbersLineEdit->setValidator(validator); -} - -//! Adds row to the grid: elements related to the import target. - -void ParserPropertyWidget::addImportToBlock(QGridLayout* layout) -{ - auto newCanvasRadio = new QRadioButton; - auto existingCanvasRadio = new QRadioButton; - m_targetCanvasCombo = new QComboBox; - - // radio settings - newCanvasRadio->setText("New canvas"); - newCanvasRadio->setChecked(true); - newCanvasRadio->setToolTip("Data will be imported into the new canvas"); - auto on_newcanvas_radio = [this](auto checked) { - if (checked) - targetCanvasChanged(-1); - }; - connect(newCanvasRadio, &QRadioButton::clicked, on_newcanvas_radio); - - existingCanvasRadio->setText("Existing canvas"); - existingCanvasRadio->setToolTip("Data will be imported into existing canvas"); - auto on_existingcanvas_radio = [this](auto checked) { - if (checked) - targetCanvasChanged(m_targetCanvasCombo->currentIndex()); - }; - connect(existingCanvasRadio, &QRadioButton::clicked, on_existingcanvas_radio); - - // combo settings - m_targetCanvasCombo->setToolTip("Data will be imported into existing canvas"); - auto on_canvas_combo = [this, existingCanvasRadio](int index) { - if (existingCanvasRadio->isChecked()) - targetCanvasChanged(index); - }; - connect(m_targetCanvasCombo, - static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), - on_canvas_combo); - - // adding to the layout - int row = layout->rowCount(); - layout->addWidget(new QLabel(" "), row, 0, Qt::AlignLeft); - layout->addWidget(newCanvasRadio, row, 1, Qt::AlignLeft); - row = layout->rowCount(); - layout->addWidget(new QLabel(" "), row, 0, Qt::AlignLeft); - layout->addWidget(existingCanvasRadio, row, 1, Qt::AlignLeft); - layout->addWidget(m_targetCanvasCombo, row, 2, Qt::AlignLeft); - auto buttonGroup = new QButtonGroup(this); - buttonGroup->addButton(newCanvasRadio); - buttonGroup->addButton(existingCanvasRadio); -} - -} // namespace gui2 diff --git a/gui2/dataloader/parserpropertywidget.h b/gui2/dataloader/parserpropertywidget.h deleted file mode 100644 index c9d8c533d82..00000000000 --- a/gui2/dataloader/parserpropertywidget.h +++ /dev/null @@ -1,75 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/dataloader/parserpropertywidget.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_DATALOADER_PARSERPROPERTYWIDGET_H -#define BORNAGAIN_GUI2_DATALOADER_PARSERPROPERTYWIDGET_H - -#include "darefl_export.h" -#include "gui2/dataloader/dataloader_types.h" -#include <QWidget> -#include <memory> - -class QGridLayout; -class QButtonGroup; -class QComboBox; -class QLineEdit; - -namespace gui2 { - -class ParserInterface; - -//! Panel to setup ASCII parser. Intended for concrete class DefaultParser. -//! Contains selection of separator symbols, patterns to ignore lines, and import target settings. - -class DAREFLCORE_EXPORT ParserPropertyWidget : public QWidget { - Q_OBJECT - -public: - enum SeparatorButtonId { AUTOMATIC, SPACE, COMMA, CUSTOM }; - - ParserPropertyWidget(QWidget* parent = nullptr); - ~ParserPropertyWidget(); - - std::unique_ptr<ParserInterface> createParser() const; - - void setTargetCanvas(const QStringList& canvas_names, int current_index); - -signals: - void parserPropertyChanged(); - int targetCanvasChanged(int canvas_index); - -private slots: - void onParserPropertyChange(); - -private: - void readSettings(); - void writeSettings(); - QGridLayout* createGridLayout(); - - void addSectionLabel(const QString& text, QGridLayout* layout); - void addStandardSeparatorRow(QGridLayout* layout, QButtonGroup* group); - void addCustomSeparatorRow(QGridLayout* layout, QButtonGroup* group); - void addIgnoreStringPatternRow(QGridLayout* layout); - void addIgnoreNumbersPatternRow(QGridLayout* layout); - void addImportToBlock(QGridLayout* layout); - - ParserOptions m_options; - QButtonGroup* m_separatorButtonGroup{nullptr}; - QComboBox* m_targetCanvasCombo{nullptr}; - QLineEdit* m_customSeparatorLineEdit{nullptr}; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_DATALOADER_PARSERPROPERTYWIDGET_H diff --git a/gui2/importdataview/CMakeLists.txt b/gui2/importdataview/CMakeLists.txt deleted file mode 100644 index d5cf77b5702..00000000000 --- a/gui2/importdataview/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -target_sources(${library_name} PRIVATE - dataselectionmodel.cpp - dataselectionmodel.h - dataselectorwidget.cpp - dataselectorwidget.h - dataviewmodel.cpp - dataviewmodel.h - graphcanvaswidget.cpp - graphcanvaswidget.h - graphimportdata.h - importdataeditor.cpp - importdataeditor.h - importdataeditoractions.cpp - importdataeditoractions.h - importdataeditortoolbal.cpp - importdataeditortoolbal.h - importdataview.cpp - importdataview.h -) diff --git a/gui2/importdataview/dataselectionmodel.cpp b/gui2/importdataview/dataselectionmodel.cpp deleted file mode 100644 index 2609b405d72..00000000000 --- a/gui2/importdataview/dataselectionmodel.cpp +++ /dev/null @@ -1,104 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/importdataview/dataselectionmodel.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/importdataview/dataselectionmodel.h" -#include "gui2/model/experimentaldataitems.h" -#include "gui2/model/item_constants.h" -#include "mvvm/model/itemutils.h" -#include "mvvm/model/mvvm_types.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/standarditems/graphitem.h" -#include "mvvm/viewmodel/viewmodel.h" -#include "mvvm/viewmodel/viewmodelutils.h" - -namespace gui2 { - -//! The constructor -DataSelectionModel::DataSelectionModel(ModelView::ViewModel* view_model, QObject* parent) - : QItemSelectionModel(view_model, parent) -{ - // FIXME cover with unit tests after implementing ViewItemSelectionModel - connect(view_model, &ModelView::ViewModel::modelAboutToBeReset, [this]() { clearSelection(); }); -} - -//! Set the selection on a single item -void DataSelectionModel::selectItem(ModelView::SessionItem* item) -{ - selectItems({item}); -} - -//! Set the selection on a list of items -void DataSelectionModel::selectItems(std::vector<ModelView::SessionItem*> items) -{ - QModelIndexList indexes; - for (auto item : items) - indexes << viewModel()->indexOfSessionItem(item); - - if (indexes.empty()) - return; - - clearSelection(); - - QItemSelection selection(indexes.front(), indexes.back()); - auto flags = QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows; - select(selection, flags); -} - -//! Return the selected items -std::vector<ModelView::SessionItem*> DataSelectionModel::selectedItems() const -{ - auto items = ModelView::Utils::ItemsFromIndex(selectedIndexes()); - return ModelView::Utils::UniqueItems(items); -} - -const ModelView::ViewModel* DataSelectionModel::viewModel() const -{ - return static_cast<const ModelView::ViewModel*>(model()); -} - -//! Returns active canvas. The canvas is active when it is either selected, or one of its own -//! graph is selected. If more than one canvas is selected, will return the first one. - -CanvasItem* DataSelectionModel::activeCanvas() const -{ - for (auto item : selectedItems()) { - if (item->modelType() == GUI::Constants::CanvasItemType) - return static_cast<CanvasItem*>(item); - else if (item->modelType() == ModelView::GUI::Constants::GraphItemType) - return static_cast<CanvasItem*>(item->parent()); - } - return nullptr; -} - -//! Returns currently selected graph. If more than one graph is selected, will return first one. - -ModelView::GraphItem* DataSelectionModel::selectedGraph() const -{ - auto graphs = selectedGraphs(); - return graphs.empty() ? nullptr : graphs.at(0); -} - -//! Returns vector of currently slected canvas. - -std::vector<CanvasItem*> DataSelectionModel::selectedCanvas() const -{ - return ModelView::Utils::CastedItems<CanvasItem>(selectedItems()); -} - -std::vector<ModelView::GraphItem*> DataSelectionModel::selectedGraphs() const -{ - return ModelView::Utils::CastedItems<ModelView::GraphItem>(selectedItems()); -} - -} // namespace gui2 diff --git a/gui2/importdataview/dataselectionmodel.h b/gui2/importdataview/dataselectionmodel.h deleted file mode 100644 index cd64bc03e6a..00000000000 --- a/gui2/importdataview/dataselectionmodel.h +++ /dev/null @@ -1,58 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/importdataview/dataselectionmodel.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_IMPORTDATAVIEW_DATASELECTIONMODEL_H -#define BORNAGAIN_GUI2_IMPORTDATAVIEW_DATASELECTIONMODEL_H - -#include "darefl_export.h" -#include <QItemSelectionModel> -#include <vector> - -namespace ModelView { -class ViewModel; -class SessionItem; -class GraphItem; -} // namespace ModelView - -namespace gui2 { - -class CanvasItem; - -//! Custom selection model for data view model (AbstractViewModel). - -class DAREFLCORE_EXPORT DataSelectionModel : public QItemSelectionModel { - Q_OBJECT - -public: - DataSelectionModel(ModelView::ViewModel* view_model, QObject* parent = nullptr); - ~DataSelectionModel() = default; - - void selectItem(ModelView::SessionItem* item); - void selectItems(std::vector<ModelView::SessionItem*> items); - - std::vector<ModelView::SessionItem*> selectedItems() const; - - const ModelView::ViewModel* viewModel() const; - - CanvasItem* activeCanvas() const; - ModelView::GraphItem* selectedGraph() const; - - std::vector<CanvasItem*> selectedCanvas() const; - - std::vector<ModelView::GraphItem*> selectedGraphs() const; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_IMPORTDATAVIEW_DATASELECTIONMODEL_H diff --git a/gui2/importdataview/dataselectorwidget.cpp b/gui2/importdataview/dataselectorwidget.cpp deleted file mode 100644 index 72bb7e960c3..00000000000 --- a/gui2/importdataview/dataselectorwidget.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/importdataview/dataselectorwidget.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/importdataview/dataselectorwidget.h" -#include "gui2/importdataview/dataselectionmodel.h" -#include "gui2/importdataview/dataviewmodel.h" -#include "gui2/model/experimentaldataitems.h" -#include "mvvm/standarditems/graphitem.h" -#include "mvvm/widgets/collapsiblelistwidget.h" -#include "mvvm/widgets/standardtreeviews.h" -#include "mvvm/widgets/widgetutils.h" -#include <QHBoxLayout> -#include <QLabel> -#include <QSplitter> -#include <QTreeView> -#include <QVBoxLayout> - -namespace gui2 { - -DataSelectorWidget::DataSelectorWidget(DataViewModel* view_model, QWidget* parent) - : QWidget(parent) - , m_viewModel(view_model) - , m_selectionModel(new DataSelectionModel(m_viewModel, this)) - , m_selectorTree(new QTreeView) - , m_canvasPropertyEditor(new ModelView::PropertyTreeView) - , m_graphPropertyEditor(new ModelView::PropertyTreeView) - , m_collapsibleWidget(new ModelView::CollapsibleListWidget) -{ - auto layout = new QVBoxLayout(this); - - m_collapsibleWidget->addWidget(m_selectorTree, "Canvas list"); - m_collapsibleWidget->addWidget(m_canvasPropertyEditor, "Canvas properties", - /*set_collapsed*/ true); - m_collapsibleWidget->addWidget(m_graphPropertyEditor, "Graph properties", - /*set_collapsed*/ true); - - layout->addWidget(m_collapsibleWidget); - - m_selectorTree->setModel(m_viewModel); - m_selectorTree->setSelectionModel(m_selectionModel); - m_selectorTree->setSelectionMode(QAbstractItemView::ExtendedSelection); - m_selectorTree->setDragDropMode(QAbstractItemView::InternalMove); - m_selectorTree->setDragEnabled(true); - - connect(selectionModel(), &DataSelectionModel::selectionChanged, this, - &DataSelectorWidget::onSelectionChanged); -} - -DataSelectionModel* DataSelectorWidget::selectionModel() const -{ - return m_selectionModel; -} - -void DataSelectorWidget::onSelectionChanged() -{ - m_canvasPropertyEditor->setItem(m_selectionModel->activeCanvas()); - m_graphPropertyEditor->setItem(m_selectionModel->selectedGraph()); - - selectionChanged(); // emmit further -} - -} // namespace gui2 diff --git a/gui2/importdataview/dataselectorwidget.h b/gui2/importdataview/dataselectorwidget.h deleted file mode 100644 index e9ac3eea636..00000000000 --- a/gui2/importdataview/dataselectorwidget.h +++ /dev/null @@ -1,61 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/importdataview/dataselectorwidget.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_IMPORTDATAVIEW_DATASELECTORWIDGET_H -#define BORNAGAIN_GUI2_IMPORTDATAVIEW_DATASELECTORWIDGET_H - -#include "darefl_export.h" -#include <QWidget> - -class QTreeView; - -namespace ModelView { -class PropertyTreeView; -class CollapsibleListWidget; -} // namespace ModelView - -namespace gui2 { - -class DataSelectionModel; -class DataViewModel; - -//! Widget to select graphs and look at their properties. -//! Occupies the left part of ImportDataEditor. - -class DAREFLCORE_EXPORT DataSelectorWidget : public QWidget { - Q_OBJECT - -public: - DataSelectorWidget(DataViewModel* view_model, QWidget* parent = nullptr); - - DataSelectionModel* selectionModel() const; - -signals: - void selectionChanged(); - -private slots: - void onSelectionChanged(); - -private: - DataViewModel* m_viewModel{nullptr}; - DataSelectionModel* m_selectionModel{nullptr}; - QTreeView* m_selectorTree{nullptr}; - ModelView::PropertyTreeView* m_canvasPropertyEditor{nullptr}; - ModelView::PropertyTreeView* m_graphPropertyEditor{nullptr}; - ModelView::CollapsibleListWidget* m_collapsibleWidget{nullptr}; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_IMPORTDATAVIEW_DATASELECTORWIDGET_H diff --git a/gui2/importdataview/dataviewmodel.cpp b/gui2/importdataview/dataviewmodel.cpp deleted file mode 100644 index 89a754cbf27..00000000000 --- a/gui2/importdataview/dataviewmodel.cpp +++ /dev/null @@ -1,121 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/importdataview/dataviewmodel.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/importdataview/dataviewmodel.h" -#include "gui2/model/experimentaldatamodel.h" -#include "gui2/model/item_constants.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/viewmodel/viewmodelutils.h" -#include "mvvm/widgets/widgetutils.h" -#include <QByteArray> -#include <QDataStream> -#include <QMimeData> - -using namespace ModelView; - -namespace { -const std::string ExperimentalDataMimeType = "darefl/ExperimentalDataMime"; - -} // namespace - -namespace gui2 { - -DataViewModel::DataViewModel(ExperimentalDataModel* model, QObject* parent) - : ModelView::TopItemsViewModel(model, parent) -{ -} - -//! Return the Qt flags for given index. We allow GraphItem drag, they can be dropped on CanvasItem. - -Qt::ItemFlags DataViewModel::flags(const QModelIndex& index) const -{ - Qt::ItemFlags result = ViewModel::flags(index); - - if (auto item = sessionItemFromIndex(index); item) { - if (item->modelType() == ModelView::GUI::Constants::GraphItemType) - result |= Qt::ItemIsDragEnabled; - else if (item->modelType() == GUI::Constants::CanvasItemType) - result |= Qt::ItemIsDropEnabled; - } - - return result; -} - -//! Generate the mime data for all selected items. - -QMimeData* DataViewModel::mimeData(const QModelIndexList& index_list) const -{ - auto result = new QMimeData; - - // Get the list of the identifiers - QStringList identifiers; - for (auto item : Utils::UniqueItemsFromIndex(index_list)) - identifiers.append(QString::fromStdString(item->identifier())); - - result->setData(QString::fromStdString(ExperimentalDataMimeType), - Utils::serialize(identifiers)); - - return result; -} - -//! Supported drag actions. - -Qt::DropActions DataViewModel::supportedDragActions() const -{ - return Qt::TargetMoveAction; -} - -//! Supported drop actions. - -Qt::DropActions DataViewModel::supportedDropActions() const -{ - return Qt::TargetMoveAction; -} - -//! Returns true if we can drop item here. - -bool DataViewModel::canDropMimeData(const QMimeData* data, Qt::DropAction, int, int, - const QModelIndex& parent) const -{ - if (data->hasFormat(QString::fromStdString(ExperimentalDataMimeType))) - if (auto target = sessionItemFromIndex(parent); target) - return target->modelType() == GUI::Constants::CanvasItemType; - - return false; -} - -bool DataViewModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, - const QModelIndex& parent) -{ - if (!canDropMimeData(data, action, row, column, parent)) - return false; - - auto target = sessionItemFromIndex(parent); - - int requested_row = parent.isValid() ? parent.row() : row; - - // retrieving list of item identifiers and accessing items - auto identifiers = - Utils::deserialize(data->data(QString::fromStdString(ExperimentalDataMimeType))); - for (auto id : identifiers) { - auto item = sessionModel()->findItem(id.toStdString()); - - int row = std::clamp(requested_row, 0, item->parent()->itemCount(item->tagRow().tag) - 1); - sessionModel()->moveItem(item, target, {"", row}); - } - - return true; -} - -} // namespace gui2 diff --git a/gui2/importdataview/dataviewmodel.h b/gui2/importdataview/dataviewmodel.h deleted file mode 100644 index aa3d508c9fb..00000000000 --- a/gui2/importdataview/dataviewmodel.h +++ /dev/null @@ -1,49 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/importdataview/dataviewmodel.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_IMPORTDATAVIEW_DATAVIEWMODEL_H -#define BORNAGAIN_GUI2_IMPORTDATAVIEW_DATAVIEWMODEL_H - -#include "darefl_export.h" -#include "mvvm/viewmodel/topitemsviewmodel.h" - -namespace ModelView { -class SessionModel; -} - -namespace gui2 { - -class ExperimentalDataModel; - -//! View model for ExperimentalDataModel with drag-and-drop support. - -class DAREFLCORE_EXPORT DataViewModel : public ModelView::TopItemsViewModel { - Q_OBJECT - -public: - DataViewModel(ExperimentalDataModel* model, QObject* parent = nullptr); - - Qt::ItemFlags flags(const QModelIndex& index) const override; - QMimeData* mimeData(const QModelIndexList& index_list) const override; - Qt::DropActions supportedDragActions() const override; - Qt::DropActions supportedDropActions() const override; - bool canDropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, - const QModelIndex& parent) const override; - bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, - const QModelIndex& parent) override; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_IMPORTDATAVIEW_DATAVIEWMODEL_H diff --git a/gui2/importdataview/graphcanvaswidget.cpp b/gui2/importdataview/graphcanvaswidget.cpp deleted file mode 100644 index b80ae5044c8..00000000000 --- a/gui2/importdataview/graphcanvaswidget.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/importdataview/graphcanvaswidget.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/importdataview/graphcanvaswidget.h" -#include "gui2/model/experimentaldataitems.h" -#include "mvvm/plotting/graphcanvas.h" -#include <QVBoxLayout> - -namespace gui2 { - -GraphCanvasWidget::GraphCanvasWidget(QWidget* parent) - : QWidget(parent), m_graphCanvas(new ModelView::GraphCanvas) -{ - auto layout = new QVBoxLayout(this); - layout->addWidget(m_graphCanvas); - layout->setContentsMargins(0, 5, 5, 5); -} - -void GraphCanvasWidget::setItem(CanvasItem* canvas_item) -{ - m_graphCanvas->setItem(canvas_item); -} - -void GraphCanvasWidget::updateViewport() -{ - m_graphCanvas->setViewportToContent(); -} - -} // namespace gui2 diff --git a/gui2/importdataview/graphcanvaswidget.h b/gui2/importdataview/graphcanvaswidget.h deleted file mode 100644 index a2e818624cf..00000000000 --- a/gui2/importdataview/graphcanvaswidget.h +++ /dev/null @@ -1,48 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/importdataview/graphcanvaswidget.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_IMPORTDATAVIEW_GRAPHCANVASWIDGET_H -#define BORNAGAIN_GUI2_IMPORTDATAVIEW_GRAPHCANVASWIDGET_H - -#include "darefl_export.h" -#include <QWidget> - -namespace ModelView { -class GraphCanvas; -} - -namespace gui2 { - -class CanvasItem; - -//! Widget to show canvas with graph collection. -//! Occupies the right part of ImportDataEditor. - -class DAREFLCORE_EXPORT GraphCanvasWidget : public QWidget { - Q_OBJECT - -public: - GraphCanvasWidget(QWidget* parent = nullptr); - - void setItem(CanvasItem* canvas_item); - - void updateViewport(); - -private: - ModelView::GraphCanvas* m_graphCanvas{nullptr}; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_IMPORTDATAVIEW_GRAPHCANVASWIDGET_H diff --git a/gui2/importdataview/graphimportdata.h b/gui2/importdataview/graphimportdata.h deleted file mode 100644 index 01e688d4660..00000000000 --- a/gui2/importdataview/graphimportdata.h +++ /dev/null @@ -1,38 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/importdataview/graphimportdata.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_IMPORTDATAVIEW_GRAPHIMPORTDATA_H -#define BORNAGAIN_GUI2_IMPORTDATAVIEW_GRAPHIMPORTDATA_H - -#include "darefl_export.h" -#include <string> -#include <vector> - -namespace gui2 { - -//! Raw data to construct GraphItem and Data1DItem's. - -struct DAREFLCORE_EXPORT GraphImportData { - std::string graph_description; - - std::vector<double> bin_centers; - std::string axis_units; - - std::vector<double> bin_values; - std::string signal_units; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_IMPORTDATAVIEW_GRAPHIMPORTDATA_H diff --git a/gui2/importdataview/importdataeditor.cpp b/gui2/importdataview/importdataeditor.cpp deleted file mode 100644 index 1dc977629fb..00000000000 --- a/gui2/importdataview/importdataeditor.cpp +++ /dev/null @@ -1,125 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/importdataview/importdataeditor.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/importdataview/importdataeditor.h" -#include "gui2/dataloader/dataloaderdialog.h" -#include "gui2/importdataview/dataselectionmodel.h" -#include "gui2/importdataview/dataselectorwidget.h" -#include "gui2/importdataview/dataviewmodel.h" -#include "gui2/importdataview/graphcanvaswidget.h" -#include "gui2/importdataview/importdataeditoractions.h" -#include "gui2/importdataview/importdataeditortoolbal.h" -#include "gui2/model/experimentaldataitems.h" -#include "gui2/model/experimentaldatamodel.h" -#include "mvvm/model/modelutils.h" -#include "mvvm/utils/containerutils.h" -#include <QSplitter> -#include <QVBoxLayout> - -using namespace ModelView; - -namespace gui2 { - -ImportDataEditor::ImportDataEditor(ExperimentalDataModel* model, QWidget* parent) - : QWidget(parent) - , m_dataModel(model) - , m_viewModel(new DataViewModel(model, this)) - , m_editorActions(new ImportDataEditorActions(m_dataModel, this)) - , m_editorToolBar(new ImportDataEditorToolBar(m_editorActions, this)) - , m_dataSelectorWidget(new DataSelectorWidget(m_viewModel)) - , m_graphCanvasWidget(new GraphCanvasWidget) -{ - auto layout = new QVBoxLayout(this); - layout->setContentsMargins(0, 0, 0, 0); - - auto splitter = new QSplitter; - splitter->addWidget(m_dataSelectorWidget); - splitter->addWidget(m_graphCanvasWidget); - splitter->setSizes(QList<int>() << 150 << 300); - - layout->addWidget(m_editorToolBar); - layout->addWidget(splitter); - - m_editorActions->setSelectionModel(m_dataSelectorWidget->selectionModel()); - m_viewModel->setRootSessionItem(model->canvasContainer()); - - setupConnections(); -} - -void ImportDataEditor::setupConnections() -{ - // connect toolbar with this editor - connect(m_editorToolBar, &ImportDataEditorToolBar::updateViewportRequest, - [this]() { m_graphCanvasWidget->updateViewport(); }); - - // connect selection model with this - auto on_selection_changed = [this]() { - m_graphCanvasWidget->setItem(selectionModel()->activeCanvas()); - }; - connect(m_dataSelectorWidget, &DataSelectorWidget::selectionChanged, on_selection_changed); - - // connect actions - connect(m_editorActions, &ImportDataEditorActions::invokeImportDialogRequest, this, - &ImportDataEditor::invokeImportDialog); -} - -//! Invoke the data load dialog and connect its state. - -void ImportDataEditor::invokeImportDialog() -{ - DataLoaderDialog dialog(this); - - auto [names, index] = canvasInfo(); - dialog.setTargetCanvas(names, index); - dialog.invokeFileSelectorDialog(); - if (dialog.fileNames().empty()) - return; - - if (dialog.exec() == QDialog::Accepted) { - auto canvases = Utils::FindItems<CanvasItem>(m_dataModel); - CanvasItem* target = - dialog.targetCanvasIndex() >= 0 ? canvases[dialog.targetCanvasIndex()] : nullptr; - - onImportDialogAccept(dialog.graphImportData(), target); - } -} - -//! Returns vector of canvas display name together with index of currently selected canvas. - -std::pair<std::vector<std::string>, int> ImportDataEditor::canvasInfo() const -{ - std::vector<std::string> names; - auto canvases = Utils::FindItems<CanvasItem>(m_dataModel); - auto current_canvas = selectionModel()->activeCanvas(); - std::transform(canvases.begin(), canvases.end(), std::back_inserter(names), - [](auto x) { return x->displayName(); }); - return std::make_pair(names, ModelView::Utils::IndexOfItem(canvases, current_canvas)); -} - -void ImportDataEditor::onImportDialogAccept(const std::vector<GraphImportData>& graph_data, - CanvasItem* canvas) -{ - if (!canvas) - canvas = m_dataModel->addCanvas(); - for (auto& data : graph_data) - m_dataModel->addGraph(data, *canvas); - selectionModel()->selectItem(canvas); -} - -DataSelectionModel* ImportDataEditor::selectionModel() const -{ - return m_dataSelectorWidget->selectionModel(); -} - -} // namespace gui2 diff --git a/gui2/importdataview/importdataeditor.h b/gui2/importdataview/importdataeditor.h deleted file mode 100644 index ed9e14b2887..00000000000 --- a/gui2/importdataview/importdataeditor.h +++ /dev/null @@ -1,60 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/importdataview/importdataeditor.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_IMPORTDATAVIEW_IMPORTDATAEDITOR_H -#define BORNAGAIN_GUI2_IMPORTDATAVIEW_IMPORTDATAEDITOR_H - -#include "darefl_export.h" -#include <QWidget> -#include <string> - -namespace gui2 { - -class ExperimentalDataModel; -struct GraphImportData; -class DataViewModel; -class DataSelectionModel; -class ImportDataEditorActions; -class ImportDataEditorToolBar; -class DataSelectorWidget; -class GraphCanvasWidget; -class CanvasItem; - -//! Main editor to import user data. - -class DAREFLCORE_EXPORT ImportDataEditor : public QWidget { - Q_OBJECT - -public: - ImportDataEditor(ExperimentalDataModel* model, QWidget* parent = nullptr); - -private: - void setupConnections(); - void invokeImportDialog(); - - std::pair<std::vector<std::string>, int> canvasInfo() const; - void onImportDialogAccept(const std::vector<GraphImportData>& graph_data, CanvasItem* canvas); - DataSelectionModel* selectionModel() const; - - ExperimentalDataModel* m_dataModel{nullptr}; - DataViewModel* m_viewModel{nullptr}; - ImportDataEditorActions* m_editorActions{nullptr}; - ImportDataEditorToolBar* m_editorToolBar{nullptr}; - DataSelectorWidget* m_dataSelectorWidget{nullptr}; - GraphCanvasWidget* m_graphCanvasWidget{nullptr}; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_IMPORTDATAVIEW_IMPORTDATAEDITOR_H diff --git a/gui2/importdataview/importdataeditoractions.cpp b/gui2/importdataview/importdataeditoractions.cpp deleted file mode 100644 index b5b28417302..00000000000 --- a/gui2/importdataview/importdataeditoractions.cpp +++ /dev/null @@ -1,144 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/importdataview/importdataeditoractions.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/importdataview/importdataeditoractions.h" -#include "gui2/importdataview/dataselectionmodel.h" -#include "gui2/importdataview/graphimportdata.h" -#include "gui2/model/experimentaldataitems.h" -#include "gui2/model/experimentaldatamodel.h" -#include "mvvm/interfaces/undostackinterface.h" -#include "mvvm/model/comboproperty.h" -#include "mvvm/model/itemutils.h" -#include "mvvm/standarditems/graphitem.h" -#include "mvvm/standarditems/plottableitems.h" -#include "mvvm/viewmodel/viewmodelutils.h" - -namespace { -template <typename T> std::vector<T*> itemsFromIndexList(const QModelIndexList& indices) -{ - return ModelView::Utils::CastedItems<T>(ModelView::Utils::UniqueItemsFromIndex(indices)); -} - -} // namespace - -namespace gui2 { - -ImportDataEditorActions::ImportDataEditorActions(ExperimentalDataModel* model, QObject* parent) - : QObject(parent), m_dataModel(model) -{ -} - -void ImportDataEditorActions::setSelectionModel(DataSelectionModel* selection_model) -{ - if (m_selectionModel) - disconnect(m_selectionModel, &DataSelectionModel::selectionChanged, this, - &ImportDataEditorActions::onSelectionChanged); - - m_selectionModel = selection_model; - - if (m_selectionModel) - connect(m_selectionModel, &DataSelectionModel::selectionChanged, this, - &ImportDataEditorActions::onSelectionChanged); -} - -bool ImportDataEditorActions::isUndoEnabled() const -{ - return m_dataModel->undoStack() != nullptr; -} - -//! Create new canvas and append it to the end of canvas container. - -void ImportDataEditorActions::onAddCanvas() -{ - m_dataModel->addCanvas(); -} - -//! Merge selected canvases. All graphs will appear below canvas selected first. - -void ImportDataEditorActions::onMergeCanvases() -{ - if (isUndoEnabled()) - undoStack()->beginMacro("onMergeCanvases"); - - m_dataModel->mergeCanvases(m_selectionModel->selectedCanvas()); - - if (isUndoEnabled()) - undoStack()->endMacro(); -} - -//! Delete currently selected items. - -void ImportDataEditorActions::onDeleteItem() -{ - if (isUndoEnabled()) - undoStack()->beginMacro("onDeleteItem"); - - for (auto canvas : m_selectionModel->selectedCanvas()) - m_dataModel->removeCanvas(*canvas); - - for (auto graph : m_selectionModel->selectedGraphs()) - m_dataModel->removeGraph(*graph); - - if (isUndoEnabled()) - undoStack()->endMacro(); -} - -void ImportDataEditorActions::onUndo() -{ - if (!isUndoEnabled()) - return; - - m_dataModel->undoStack()->undo(); -} - -void ImportDataEditorActions::onRedo() -{ - if (!isUndoEnabled()) - return; - - m_dataModel->undoStack()->redo(); -} - -void ImportDataEditorActions::onImportDialogRequest() -{ - if (isUndoEnabled()) - undoStack()->beginMacro("onImportDialogRequest"); - - invokeImportDialogRequest(); - - if (isUndoEnabled()) - undoStack()->endMacro(); -} - -//! Processes changed selection. Will change line style of selected graph from -//! solid to dashed line. - -void ImportDataEditorActions::onSelectionChanged(const QItemSelection& selected, - const QItemSelection& deselected) -{ - auto selected_graphs = itemsFromIndexList<ModelView::GraphItem>(selected.indexes()); - for (auto graph : selected_graphs) - graph->penItem()->setSelected(true); - - auto deselected_graphs = itemsFromIndexList<ModelView::GraphItem>(deselected.indexes()); - for (auto graph : deselected_graphs) - graph->penItem()->setSelected(false); -} - -ModelView::UndoStackInterface* ImportDataEditorActions::undoStack() const -{ - return m_dataModel->undoStack(); -} - -} // namespace gui2 diff --git a/gui2/importdataview/importdataeditoractions.h b/gui2/importdataview/importdataeditoractions.h deleted file mode 100644 index e8df2d860a3..00000000000 --- a/gui2/importdataview/importdataeditoractions.h +++ /dev/null @@ -1,67 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/importdataview/importdataeditoractions.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_IMPORTDATAVIEW_IMPORTDATAEDITORACTIONS_H -#define BORNAGAIN_GUI2_IMPORTDATAVIEW_IMPORTDATAEDITORACTIONS_H - -#include "darefl_export.h" -#include <QObject> - -class QItemSelection; - -namespace ModelView { -class UndoStackInterface; -} - -namespace gui2 { - -class ExperimentalDataModel; -class DataSelectionModel; - -//! Actions for ImportDataEditor. - -class DAREFLCORE_EXPORT ImportDataEditorActions : public QObject { - Q_OBJECT - -public: - ImportDataEditorActions(ExperimentalDataModel* model, QObject* parent = nullptr); - - void setSelectionModel(DataSelectionModel* selection_model); - - bool isUndoEnabled() const; - -signals: - void invokeImportDialogRequest(); - -public slots: - void onAddCanvas(); - void onMergeCanvases(); - void onDeleteItem(); - void onUndo(); - void onRedo(); - void onImportDialogRequest(); - -private slots: - void onSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected); - -private: - ModelView::UndoStackInterface* undoStack() const; - - ExperimentalDataModel* m_dataModel{nullptr}; - DataSelectionModel* m_selectionModel{nullptr}; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_IMPORTDATAVIEW_IMPORTDATAEDITORACTIONS_H diff --git a/gui2/importdataview/importdataeditortoolbal.cpp b/gui2/importdataview/importdataeditortoolbal.cpp deleted file mode 100644 index 2a166251480..00000000000 --- a/gui2/importdataview/importdataeditortoolbal.cpp +++ /dev/null @@ -1,88 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/importdataview/importdataeditortoolbal.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/importdataview/importdataeditortoolbal.h" -#include "gui2/importdataview/importdataeditoractions.h" -#include "gui2/mainwindow/styleutils.h" -#include <QAction> - -namespace gui2 { - -ImportDataEditorToolBar::ImportDataEditorToolBar(ImportDataEditorActions* editorActions, - QWidget* parent) - : QToolBar(parent), m_editorActions(editorActions) -{ - GUI::Utils::Style::SetToolBarStyleTextBesides(this); - - auto action = new QAction("Import", this); - action->setToolTip("Opens the data import dialog."); - action->setIcon(QIcon(":/icons/import.svg")); - connect(action, &QAction::triggered, this, - [this]() { m_editorActions->onImportDialogRequest(); }); - addAction(action); - - addSeparator(); - - auto add_canvas_action = new QAction("Add canvas", this); - add_canvas_action->setToolTip( - "Creates an empty canvas and appends it to the list.\n" - "Canvas can hold multiple graphs, graphs can be moved between canvas."); - add_canvas_action->setIcon(QIcon(":/icons/plus-box-outline.svg")); - connect(add_canvas_action, &QAction::triggered, [this]() { m_editorActions->onAddCanvas(); }); - addAction(add_canvas_action); - - auto merge_canvases_action = new QAction("Merge", this); - merge_canvases_action->setToolTip("Merge several selected canvases into one.\n" - "All graphs will appear on a single canvas."); - merge_canvases_action->setIcon(QIcon(":/icons/set-merge.svg")); - connect(merge_canvases_action, &QAction::triggered, - [this]() { m_editorActions->onMergeCanvases(); }); - addAction(merge_canvases_action); - - auto delete_action = new QAction("Remove", this); - delete_action->setToolTip("Remove the currently selected item,\n" - "single graph or canvas with whole content."); - delete_action->setIcon(QIcon(":/icons/beaker-remove-outline.svg")); - connect(delete_action, &QAction::triggered, [this]() { m_editorActions->onDeleteItem(); }); - addAction(delete_action); - - addSeparator(); - - auto undo_action = new QAction("Undo", this); - undo_action->setToolTip("Undo the action last performed."); - undo_action->setIcon(QIcon(":/icons/undo.svg")); - undo_action->setEnabled(editorActions->isUndoEnabled()); - connect(undo_action, &QAction::triggered, [this]() { m_editorActions->onUndo(); }); - addAction(undo_action); - - auto redo_action = new QAction("Redo", this); - redo_action->setToolTip("Redo the action just performed."); - redo_action->setIcon(QIcon(":/icons/redo.svg")); - redo_action->setEnabled(editorActions->isUndoEnabled()); - connect(redo_action, &QAction::triggered, [this]() { m_editorActions->onRedo(); }); - addAction(redo_action); - - auto empty = new QWidget(this); - empty->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); - addWidget(empty); - - auto reset_graph_action = new QAction("Replot", this); - reset_graph_action->setToolTip("Set plot axes to default range"); - reset_graph_action->setIcon(QIcon(":/icons/aspect-ratio.svg")); - connect(reset_graph_action, &QAction::triggered, this, - &ImportDataEditorToolBar::updateViewportRequest); - addAction(reset_graph_action); -} - -} // namespace gui2 diff --git a/gui2/importdataview/importdataeditortoolbal.h b/gui2/importdataview/importdataeditortoolbal.h deleted file mode 100644 index 2aab6d93976..00000000000 --- a/gui2/importdataview/importdataeditortoolbal.h +++ /dev/null @@ -1,42 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/importdataview/importdataeditortoolbal.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_IMPORTDATAVIEW_IMPORTDATAEDITORTOOLBAL_H -#define BORNAGAIN_GUI2_IMPORTDATAVIEW_IMPORTDATAEDITORTOOLBAL_H - -#include "darefl_export.h" -#include <QToolBar> - -namespace gui2 { - -class ImportDataEditorActions; - -//! Toolbar for ImportDataEditor. - -class DAREFLCORE_EXPORT ImportDataEditorToolBar : public QToolBar { - Q_OBJECT - -public: - ImportDataEditorToolBar(ImportDataEditorActions* editorActions, QWidget* parent = nullptr); - -signals: - void updateViewportRequest(); - -private: - ImportDataEditorActions* m_editorActions{nullptr}; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_IMPORTDATAVIEW_IMPORTDATAEDITORTOOLBAL_H diff --git a/gui2/importdataview/importdataview.cpp b/gui2/importdataview/importdataview.cpp deleted file mode 100644 index b4c330517d9..00000000000 --- a/gui2/importdataview/importdataview.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/importdataview/importdataview.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/importdataview/importdataview.h" -#include "gui2/importdataview/importdataeditor.h" -#include "gui2/model/applicationmodels.h" -#include <QVBoxLayout> - -namespace gui2 { - -ImportDataView::ImportDataView(ApplicationModels* models, QWidget* parent) - : QWidget(parent), m_models(models) -{ - auto layout = new QVBoxLayout(this); - layout->addWidget(new ImportDataEditor(models->experimentalDataModel())); - layout->setContentsMargins(0, 0, 0, 0); -} - -} // namespace gui2 diff --git a/gui2/importdataview/importdataview.h b/gui2/importdataview/importdataview.h deleted file mode 100644 index ca609a1a0eb..00000000000 --- a/gui2/importdataview/importdataview.h +++ /dev/null @@ -1,39 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/importdataview/importdataview.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_IMPORTDATAVIEW_IMPORTDATAVIEW_H -#define BORNAGAIN_GUI2_IMPORTDATAVIEW_IMPORTDATAVIEW_H - -#include "darefl_export.h" -#include <QWidget> - -namespace gui2 { - -class ApplicationModels; - -//! Main window to import user data. - -class DAREFLCORE_EXPORT ImportDataView : public QWidget { - Q_OBJECT - -public: - ImportDataView(ApplicationModels* models, QWidget* parent = nullptr); - -private: - ApplicationModels* m_models{nullptr}; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_IMPORTDATAVIEW_IMPORTDATAVIEW_H diff --git a/gui2/layereditor/CMakeLists.txt b/gui2/layereditor/CMakeLists.txt deleted file mode 100644 index af4570d25a3..00000000000 --- a/gui2/layereditor/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -target_sources(${library_name} PRIVATE - customlayertreeeditorfactory.cpp - customlayertreeeditorfactory.h - layereditor.cpp - layereditor.h - layereditoractions.cpp - layereditoractions.h - layereditortoolbar.cpp - layereditortoolbar.h - layereditorwidget.cpp - layereditorwidget.h - layerselectionmodel.cpp - layerselectionmodel.h - layertreeview.cpp - layertreeview.h - layerviewmodel.cpp - layerviewmodel.h - layerviewmodelcontroller.cpp - layerviewmodelcontroller.h -) diff --git a/gui2/layereditor/customlayertreeeditorfactory.cpp b/gui2/layereditor/customlayertreeeditorfactory.cpp deleted file mode 100644 index 73a17c450c9..00000000000 --- a/gui2/layereditor/customlayertreeeditorfactory.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/layereditor/customlayertreeeditorfactory.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/layereditor/customlayertreeeditorfactory.h" -#include "gui2/model/applicationmodels.h" -#include "gui2/model/materialmodel.h" -#include "mvvm/editors/externalpropertycomboeditor.h" -#include "mvvm/model/externalproperty.h" -#include <QModelIndex> -#include <algorithm> - -using namespace ModelView; - -namespace gui2 { - -namespace { -//! Return list of possible choices for material properties in MaterialModel. -//! Use "undefined material" as a first item in a list. -std::vector<ModelView::ExternalProperty> get_choice_of_materials(MaterialModel* model) -{ - std::vector<ModelView::ExternalProperty> result{ModelView::ExternalProperty::undefined()}; - auto other_data = model->material_data(); - std::copy(other_data.begin(), other_data.end(), std::back_inserter(result)); - return result; -} -} // namespace - -CustomLayerTreeEditorFactory::~CustomLayerTreeEditorFactory() = default; - -CustomLayerTreeEditorFactory::CustomLayerTreeEditorFactory(ApplicationModels* models) - : m_models(models) -{ -} - -std::unique_ptr<CustomEditor> -CustomLayerTreeEditorFactory::createEditor(const QModelIndex& index) const -{ - auto value = index.data(Qt::EditRole); - if (Utils::IsExtPropertyVariant(value)) { - auto material_choice_callback = [this]() { - return get_choice_of_materials(m_models->materialModel()); - }; - return std::make_unique<ExternalPropertyComboEditor>(material_choice_callback); - } else { - return DefaultEditorFactory::createEditor(index); - } -} - -} // namespace gui2 diff --git a/gui2/layereditor/customlayertreeeditorfactory.h b/gui2/layereditor/customlayertreeeditorfactory.h deleted file mode 100644 index e1e2e88118d..00000000000 --- a/gui2/layereditor/customlayertreeeditorfactory.h +++ /dev/null @@ -1,41 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/layereditor/customlayertreeeditorfactory.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_LAYEREDITOR_CUSTOMLAYERTREEEDITORFACTORY_H -#define BORNAGAIN_GUI2_LAYEREDITOR_CUSTOMLAYERTREEEDITORFACTORY_H - -#include "darefl_export.h" -#include "mvvm/editors/defaulteditorfactory.h" - -namespace gui2 { - -class ApplicationModels; - -//! Custom editor factory for LayerTreeView. Substitutes default ExternalProperty editor -//! with custom one, which will offer the choice between all defined materials. - -class DAREFLCORE_EXPORT CustomLayerTreeEditorFactory : public ModelView::DefaultEditorFactory { -public: - CustomLayerTreeEditorFactory(ApplicationModels* models); - ~CustomLayerTreeEditorFactory(); - - std::unique_ptr<ModelView::CustomEditor> createEditor(const QModelIndex& index) const; - -private: - ApplicationModels* m_models; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_LAYEREDITOR_CUSTOMLAYERTREEEDITORFACTORY_H diff --git a/gui2/layereditor/layereditor.cpp b/gui2/layereditor/layereditor.cpp deleted file mode 100644 index eb93fcb4308..00000000000 --- a/gui2/layereditor/layereditor.cpp +++ /dev/null @@ -1,72 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/layereditor/layereditor.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/layereditor/layereditor.h" -#include "gui2/layereditor/layereditoractions.h" -#include "gui2/layereditor/layereditortoolbar.h" -#include "gui2/layereditor/layereditorwidget.h" -#include "gui2/layereditor/layerselectionmodel.h" -#include "gui2/mainwindow/styleutils.h" -#include "gui2/model/applicationmodels.h" -#include <QVBoxLayout> - -namespace gui2 { - -LayerEditor::LayerEditor(QWidget* parent) - : QWidget(parent) - , m_actions(new LayerEditorActions(this)) - , m_editorWidget(new LayerEditorWidget(this)) - , m_toolBar(new LayerEditorToolBar(m_actions)) -{ - setWindowTitle("Layer editor"); - auto layout = new QVBoxLayout; - layout->addWidget(m_toolBar); - layout->addWidget(m_editorWidget); - setLayout(layout); - layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(0); -} - -//! Set the mododel for the different items -void LayerEditor::setModels(ApplicationModels* models) -{ - m_actions->setModel(models->sampleModel()); - m_editorWidget->setModels(models); - - connect(m_editorWidget->selectionModel(), &LayerSelectionModel::selectionChanged, this, - &LayerEditor::selectionChanged); - - m_actions->setSelectionModel(m_editorWidget->selectionModel()); -} - -QSize LayerEditor::sizeHint() const -{ - return GUI::Utils::Style::DockSizeHint(); -} - -QSize LayerEditor::minimumSizeHint() const -{ - return GUI::Utils::Style::DockMinimumSizeHint(); -} - -void LayerEditor::selectionChanged() -{ - dynamic_cast<LayerEditorToolBar*>(m_toolBar)->updateToolButtonStates( - m_editorWidget->selectionModel()->firstSelected(), - m_editorWidget->selectionModel()->lastSelected()); -} - -LayerEditor::~LayerEditor() = default; - -} // namespace gui2 diff --git a/gui2/layereditor/layereditor.h b/gui2/layereditor/layereditor.h deleted file mode 100644 index c6545e61f8e..00000000000 --- a/gui2/layereditor/layereditor.h +++ /dev/null @@ -1,55 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/layereditor/layereditor.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_LAYEREDITOR_LAYEREDITOR_H -#define BORNAGAIN_GUI2_LAYEREDITOR_LAYEREDITOR_H - -#include "darefl_export.h" -#include <QWidget> - -namespace ModelView { -class AbstractViewModel; -} - -namespace gui2 { - -class ApplicationModels; -class LayerEditorActions; -class LayerEditorToolBar; -class LayerEditorWidget; - -//! Layer editor. - -class DAREFLCORE_EXPORT LayerEditor : public QWidget { - Q_OBJECT - -public: - LayerEditor(QWidget* parent = nullptr); - ~LayerEditor(); - - void setModels(ApplicationModels* models); - - QSize sizeHint() const override; - QSize minimumSizeHint() const override; - void selectionChanged(); - -private: - LayerEditorActions* m_actions{nullptr}; - LayerEditorWidget* m_editorWidget{nullptr}; - LayerEditorToolBar* m_toolBar{nullptr}; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_LAYEREDITOR_LAYEREDITOR_H diff --git a/gui2/layereditor/layereditoractions.cpp b/gui2/layereditor/layereditoractions.cpp deleted file mode 100644 index 47522882d03..00000000000 --- a/gui2/layereditor/layereditoractions.cpp +++ /dev/null @@ -1,149 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/layereditor/layereditoractions.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/layereditor/layereditoractions.h" -#include "gui2/layereditor/layerselectionmodel.h" -#include "gui2/model/item_constants.h" -#include "gui2/model/sampleitems.h" -#include "gui2/model/samplemodel.h" -#include "mvvm/model/itemutils.h" -#include "mvvm/model/modelutils.h" -#include "mvvm/viewmodel/viewmodel.h" -#include <QAction> - -using namespace ModelView; - -namespace gui2 { - -struct LayerEditorActions::LayerEditorActionsImpl { - SampleModel* sample_model{nullptr}; - LayerSelectionModel* selection_model{nullptr}; - LayerEditorActionsImpl() {} - - //! Finds parent and tagrow to insert new item - - std::pair<SessionItem*, TagRow> locateInsertPlace() - { - auto all_selected = selection_model->selectedItems(); - auto selected = all_selected.empty() ? nullptr : all_selected.back(); - if (selected) - return {selected->parent(), selected->tagRow().next()}; - return {root_item(), TagRow{}}; - } - - //! Returns a multi layer playing the role of invisible root item. - - ModelView::SessionItem* root_item() - { - return selection_model->viewModel()->sessionItemFromIndex(QModelIndex()); - } -}; - -LayerEditorActions::LayerEditorActions(QObject* parent) - : QObject(parent), p_impl(std::make_unique<LayerEditorActionsImpl>()) -{ -} - -void LayerEditorActions::setModel(SampleModel* model) -{ - p_impl->sample_model = model; -} - -//! Adds layer after selected item. If more than one item is selected, adds after the last one. - -void LayerEditorActions::onAddLayer() -{ - if (!p_impl->sample_model) - return; - - auto [parent, tagrow] = p_impl->locateInsertPlace(); - auto new_item = p_impl->sample_model->insertItem<LayerItem>(parent, tagrow); - p_impl->selection_model->selectItem(new_item); -} - -void LayerEditorActions::onAddMultiLayer() -{ - if (!p_impl->sample_model) - return; - - auto [parent, tagrow] = p_impl->locateInsertPlace(); - auto multilayer = p_impl->sample_model->insertItem<MultiLayerItem>(parent, tagrow); - p_impl->sample_model->insertItem<LayerItem>(multilayer); - p_impl->sample_model->insertItem<LayerItem>(multilayer); - p_impl->selection_model->selectItem(multilayer); -} - -void LayerEditorActions::onClone() -{ - if (!p_impl->sample_model) - return; - - auto items = p_impl->selection_model->selectedItems(); - if (items.empty()) - return; - - std::vector<ModelView::SessionItem*> new_selection; - for (auto to_clone : items) - new_selection.push_back(p_impl->sample_model->copyItem(to_clone, to_clone->parent(), - to_clone->tagRow().next())); - - p_impl->selection_model->selectItems(new_selection); -} - -void LayerEditorActions::onRemove() -{ - auto items = p_impl->selection_model->selectedItems(); - if (items.empty()) - return; - - auto prev_to_select = ModelView::Utils::FindPreviousSibling(items.front()); - auto next_to_select = ModelView::Utils::FindNextSibling(items.back()); - - for (auto item : items) - ModelView::Utils::DeleteItemFromModel(item); - if (next_to_select) { - p_impl->selection_model->selectItem(next_to_select); - } else if (prev_to_select) { - p_impl->selection_model->selectItem(prev_to_select); - } -} - -void LayerEditorActions::onMoveUp() -{ - auto selected = p_impl->selection_model->selectedItems(); - - for (auto item : selected) - ModelView::Utils::MoveUp(item); - - p_impl->selection_model->selectItems(selected); -} - -void LayerEditorActions::onMoveDown() -{ - auto selected = p_impl->selection_model->selectedItems(); - - for (auto item : selected) - ModelView::Utils::MoveDown(item); - - p_impl->selection_model->selectItems(selected); -} - -void LayerEditorActions::setSelectionModel(LayerSelectionModel* selection_model) -{ - p_impl->selection_model = selection_model; -} - -LayerEditorActions::~LayerEditorActions() = default; - -} // namespace gui2 diff --git a/gui2/layereditor/layereditoractions.h b/gui2/layereditor/layereditoractions.h deleted file mode 100644 index 8814586e6d8..00000000000 --- a/gui2/layereditor/layereditoractions.h +++ /dev/null @@ -1,55 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/layereditor/layereditoractions.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_LAYEREDITOR_LAYEREDITORACTIONS_H -#define BORNAGAIN_GUI2_LAYEREDITOR_LAYEREDITORACTIONS_H - -#include "darefl_export.h" -#include <QObject> -#include <memory> - -namespace gui2 { - -class SampleModel; -class LayerSelectionModel; - -//! Handles user actions applied to layer tree. -//! Belongs to LayerEditor. - -class DAREFLCORE_EXPORT LayerEditorActions : public QObject { - Q_OBJECT - -public: - LayerEditorActions(QObject* parent = nullptr); - ~LayerEditorActions(); - - void setModel(SampleModel* model); - - void onAddLayer(); - void onAddMultiLayer(); - void onClone(); - void onRemove(); - void onMoveUp(); - void onMoveDown(); - - void setSelectionModel(LayerSelectionModel* selection_model); - -private: - struct LayerEditorActionsImpl; - std::unique_ptr<LayerEditorActionsImpl> p_impl; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_LAYEREDITOR_LAYEREDITORACTIONS_H diff --git a/gui2/layereditor/layereditortoolbar.cpp b/gui2/layereditor/layereditortoolbar.cpp deleted file mode 100644 index 32e555a92cc..00000000000 --- a/gui2/layereditor/layereditortoolbar.cpp +++ /dev/null @@ -1,119 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/layereditor/layereditortoolbar.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/layereditor/layereditortoolbar.h" -#include "gui2/layereditor/layereditoractions.h" -#include "gui2/mainwindow/styleutils.h" -#include <QAction> -#include <QMenu> -#include <QToolButton> - -namespace gui2 { - -LayerEditorToolBar::LayerEditorToolBar(LayerEditorActions* actions, QWidget* parent) - : QToolBar(parent) -{ - GUI::Utils::Style::SetToolBarStyleTextBesides(this); - - auto layer_menu = create_layer_menu(actions); - - auto add_layer_button = new QToolButton; - add_layer_button->setText("Add"); - add_layer_button->setToolTip( - "Adds a new single layer (default) or new layer-repeater after currently selected.\nIf " - "nothing is selected, appends to the end. Click and hold to see possible choices."); - add_layer_button->setPopupMode(QToolButton::MenuButtonPopup); - add_layer_button->setIcon(QIcon(":/icons/plus-circle-outline.svg")); - add_layer_button->setMenu(layer_menu); - add_layer_button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - addWidget(add_layer_button); - m_toolbarWidgets.insert(std::pair<std::string, QWidget*>("Add", add_layer_button)); - connect(add_layer_button, &QToolButton::clicked, - [layer_menu]() { layer_menu->defaultAction()->triggered(); }); - - auto action = new QAction("Clone", this); - action->setIcon(QIcon(":/icons/plus-circle-multiple-outline.svg")); - action->setToolTip("Clones selected layer"); - connect(action, &QAction::triggered, actions, &LayerEditorActions::onClone); - addAction(action); - m_toolbarWidgets.insert(std::pair<std::string, QWidget*>("Clone", widgetForAction(action))); - - action = new QAction("Remove", this); - action->setIcon(QIcon(":/icons/beaker-remove-outline.svg")); - action->setToolTip("Removes selected layer"); - connect(action, &QAction::triggered, actions, &LayerEditorActions::onRemove); - addAction(action); - m_toolbarWidgets.insert(std::pair<std::string, QWidget*>("Remove", widgetForAction(action))); - - addSeparator(); - - action = new QAction("Up", this); - action->setIcon(QIcon(":/icons/arrow-up-circle-outline.svg")); - action->setToolTip("Moves selected layer up"); - connect(action, &QAction::triggered, actions, &LayerEditorActions::onMoveUp); - addAction(action); - m_toolbarWidgets.insert(std::pair<std::string, QWidget*>("Up", widgetForAction(action))); - - action = new QAction("Down", this); - action->setIcon(QIcon(":/icons/arrow-down-circle-outline.svg")); - action->setToolTip("Moves selected layer down"); - connect(action, &QAction::triggered, actions, &LayerEditorActions::onMoveDown); - addAction(action); - m_toolbarWidgets.insert(std::pair<std::string, QWidget*>("Down", widgetForAction(action))); - - m_toolbarWidgets["Add"]->setEnabled(true); - m_toolbarWidgets["Clone"]->setEnabled(true); - m_toolbarWidgets["Remove"]->setEnabled(true); - m_toolbarWidgets["Up"]->setEnabled(false); - m_toolbarWidgets["Down"]->setEnabled(false); -} - -//! Creates menu to add layer and layer-repeater. - -QMenu* LayerEditorToolBar::create_layer_menu(LayerEditorActions* editor_actions) -{ - auto result = new QMenu("Add", this); - result->setToolTipsVisible(true); - result->menuAction()->setToolTip("Adds a single layer or layer-repeater."); - result->setIcon(QIcon(":/icons/plus-circle-outline.svg")); - - // add layer action - auto action = result->addAction("Adds a single layer"); - action->setIcon(QIcon(":/icons/layers-outline.svg")); - action->setToolTip("Adds a new layer after selected one"); - connect(action, &QAction::triggered, editor_actions, &LayerEditorActions::onAddLayer); - result->setDefaultAction(action); - - // add layer repeater action - action = result->addAction("Adds layer repeater"); - action->setIcon(QIcon(":/icons/layers-triple-outline.svg")); - action->setToolTip("Adds a new layer-repeater after selected one.\n" - "Layer repeater allows to repeat it content (i.e. bi-layer) " - "certain amount of times"); - connect(action, &QAction::triggered, editor_actions, &LayerEditorActions::onAddMultiLayer); - - return result; -} - -//! Handle the QToolButtons for their enabled state depending on what is selected -void LayerEditorToolBar::updateToolButtonStates(bool first_present, bool last_present) -{ - m_toolbarWidgets["Add"]->setEnabled(true); - m_toolbarWidgets["Clone"]->setEnabled(true); - m_toolbarWidgets["Remove"]->setEnabled(true); - m_toolbarWidgets["Up"]->setEnabled((!first_present) ? (true) : (false)); - m_toolbarWidgets["Down"]->setEnabled((!last_present) ? (true) : (false)); -} - -} // namespace gui2 diff --git a/gui2/layereditor/layereditortoolbar.h b/gui2/layereditor/layereditortoolbar.h deleted file mode 100644 index 02abfab72dc..00000000000 --- a/gui2/layereditor/layereditortoolbar.h +++ /dev/null @@ -1,46 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/layereditor/layereditortoolbar.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_LAYEREDITOR_LAYEREDITORTOOLBAR_H -#define BORNAGAIN_GUI2_LAYEREDITOR_LAYEREDITORTOOLBAR_H - -#include "darefl_export.h" -#include <QToolBar> -#include <QWidget> -#include <map> -#include <string> - -namespace gui2 { - -class LayerEditorActions; - -//! Layer editor toolbar. - -class DAREFLCORE_EXPORT LayerEditorToolBar : public QToolBar { - Q_OBJECT - -public: - LayerEditorToolBar(LayerEditorActions* actions, QWidget* parent = nullptr); - ~LayerEditorToolBar() = default; - - void updateToolButtonStates(bool first_present, bool last_present); - -private: - QMenu* create_layer_menu(LayerEditorActions* editor_actions); - std::map<std::string, QWidget*> m_toolbarWidgets; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_LAYEREDITOR_LAYEREDITORTOOLBAR_H diff --git a/gui2/layereditor/layereditorwidget.cpp b/gui2/layereditor/layereditorwidget.cpp deleted file mode 100644 index 26b7b1e0311..00000000000 --- a/gui2/layereditor/layereditorwidget.cpp +++ /dev/null @@ -1,58 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/layereditor/layereditorwidget.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/layereditor/layereditorwidget.h" -#include "gui2/layereditor/customlayertreeeditorfactory.h" -#include "gui2/layereditor/layerselectionmodel.h" -#include "gui2/layereditor/layertreeview.h" -#include "gui2/layereditor/layerviewmodel.h" -#include "gui2/model/applicationmodels.h" -#include "gui2/model/sampleitems.h" -#include "gui2/model/samplemodel.h" -#include "mvvm/viewmodel/viewmodeldelegate.h" -#include <QVBoxLayout> - -namespace gui2 { - -LayerEditorWidget::LayerEditorWidget(QWidget* parent) - : QWidget(parent) - , m_layerView(new LayerTreeView) - , m_delegate(std::make_unique<ModelView::ViewModelDelegate>()) -{ - auto layout = new QVBoxLayout; - layout->setContentsMargins(0, 0, 0, 0); - layout->addWidget(m_layerView); - setLayout(layout); - m_layerView->setItemDelegate(m_delegate.get()); -} - -LayerEditorWidget::~LayerEditorWidget() = default; - -void LayerEditorWidget::setModels(ApplicationModels* models) -{ - m_viewModel = std::make_unique<LayerViewModel>(models->sampleModel()); - m_selectionModel = new LayerSelectionModel(m_viewModel.get(), this); - - m_delegate->setEditorFactory(std::make_unique<CustomLayerTreeEditorFactory>(models)); - m_viewModel->setRootSessionItem(models->sampleModel()->topItem<MultiLayerItem>()); - m_layerView->setModel(m_viewModel.get()); - m_layerView->setSelectionModel(m_selectionModel); -} - -LayerSelectionModel* LayerEditorWidget::selectionModel() const -{ - return m_selectionModel; -} - -} // namespace gui2 diff --git a/gui2/layereditor/layereditorwidget.h b/gui2/layereditor/layereditorwidget.h deleted file mode 100644 index 74c15defd10..00000000000 --- a/gui2/layereditor/layereditorwidget.h +++ /dev/null @@ -1,56 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/layereditor/layereditorwidget.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_LAYEREDITOR_LAYEREDITORWIDGET_H -#define BORNAGAIN_GUI2_LAYEREDITOR_LAYEREDITORWIDGET_H - -#include "darefl_export.h" -#include <QWidget> -#include <memory> - -namespace ModelView { -class ViewModelDelegate; -} // namespace ModelView - -namespace gui2 { - -class ApplicationModels; -class LayerTreeView; -class LayerSelectionModel; -class LayerViewModel; - -//! Widget to hold layer tree (LayerTreeView) and all corresponding models and delegates. -//! Belongs to LayerEditor. - -class DAREFLCORE_EXPORT LayerEditorWidget : public QWidget { - Q_OBJECT - -public: - LayerEditorWidget(QWidget* parent = nullptr); - ~LayerEditorWidget(); - - void setModels(ApplicationModels* models); - - LayerSelectionModel* selectionModel() const; - -private: - std::unique_ptr<LayerViewModel> m_viewModel; - LayerSelectionModel* m_selectionModel{nullptr}; - LayerTreeView* m_layerView{nullptr}; - std::unique_ptr<ModelView::ViewModelDelegate> m_delegate; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_LAYEREDITOR_LAYEREDITORWIDGET_H diff --git a/gui2/layereditor/layerselectionmodel.cpp b/gui2/layereditor/layerselectionmodel.cpp deleted file mode 100644 index 8d9893546a8..00000000000 --- a/gui2/layereditor/layerselectionmodel.cpp +++ /dev/null @@ -1,102 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/layereditor/layerselectionmodel.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/layereditor/layerselectionmodel.h" -#include "gui2/model/sampleitems.h" -#include "mvvm/viewmodel/viewmodel.h" -#include "mvvm/viewmodel/viewmodelutils.h" -#include <QItemSelection> - -namespace gui2 { - -LayerSelectionModel::LayerSelectionModel(ModelView::ViewModel* view_model, QObject* parent) - : QItemSelectionModel(view_model, parent) -{ - // FIXME cover with unit tests after implementing ViewItemSelectionModel - connect(view_model, &ModelView::ViewModel::modelAboutToBeReset, [this]() { clearSelection(); }); -} - -//! Selects all rows corresponding to given items. - -void LayerSelectionModel::selectItems(std::vector<ModelView::SessionItem*> items) -{ - QModelIndexList indexes; - for (auto item : items) - indexes << viewModel()->indexOfSessionItem(item->getItem(LayerItem::P_NAME)); - - if (indexes.empty()) - return; - - clearSelection(); - - QItemSelection selection(indexes.front(), indexes.back()); - auto flags = QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows; - select(selection, flags); - // setCurrentIndex(id, flags); What to do? -} - -//! Selects whole row corresponding to given item. - -void LayerSelectionModel::selectItem(ModelView::SessionItem* item) -{ - selectItems({item}); -} - -//! Returns vector of selected layers or multilayers. -//! We assume, that there is a single line selection mode switched on, and that -//! the columns contains property items related to either LayerItem or MultiLayerItem. - -std::vector<ModelView::SessionItem*> LayerSelectionModel::selectedItems() const -{ - const QModelIndexList& selection = selectedRows(); - if (selection.empty()) - return {}; - - std::vector<ModelView::SessionItem*> result; - for (const auto& index : selection) - if (auto item = viewModel()->sessionItemFromIndex(index); item) - result.push_back(item->parent()); - - return result; -} - -//! Return the casted view model -const ModelView::ViewModel* LayerSelectionModel::viewModel() const -{ - return static_cast<const ModelView::ViewModel*>(model()); -} - -//! Checks if the first row is presen in the selection -bool LayerSelectionModel::firstSelected() const -{ - const QModelIndexList& selection = selectedRows(); - for (const auto& index : selection) { - if (index.row() == 0) - return true; - } - return false; -} - -//! checks if the last row is present in the selection -bool LayerSelectionModel::lastSelected() const -{ - const QModelIndexList& selection = selectedRows(); - for (const auto& index : selection) { - if (index.row() == viewModel()->rowCount() - 1) - return true; - } - return false; -} - -} // namespace gui2 diff --git a/gui2/layereditor/layerselectionmodel.h b/gui2/layereditor/layerselectionmodel.h deleted file mode 100644 index 27ce310e871..00000000000 --- a/gui2/layereditor/layerselectionmodel.h +++ /dev/null @@ -1,52 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/layereditor/layerselectionmodel.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_LAYEREDITOR_LAYERSELECTIONMODEL_H -#define BORNAGAIN_GUI2_LAYEREDITOR_LAYERSELECTIONMODEL_H - -#include "darefl_export.h" -#include <QItemSelectionModel> -#include <vector> - -namespace ModelView { -class ViewModel; -class SessionItem; -} // namespace ModelView - -namespace gui2 { - -class LayerEditorActions; - -//! Custom selection model for layer view model (AbstractViewModel). - -class DAREFLCORE_EXPORT LayerSelectionModel : public QItemSelectionModel { - Q_OBJECT - -public: - LayerSelectionModel(ModelView::ViewModel* view_model, QObject* parent = nullptr); - ~LayerSelectionModel() = default; - - void selectItems(std::vector<ModelView::SessionItem*> items); - void selectItem(ModelView::SessionItem* item); - bool firstSelected() const; - bool lastSelected() const; - - std::vector<ModelView::SessionItem*> selectedItems() const; - - const ModelView::ViewModel* viewModel() const; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_LAYEREDITOR_LAYERSELECTIONMODEL_H diff --git a/gui2/layereditor/layertreeview.cpp b/gui2/layereditor/layertreeview.cpp deleted file mode 100644 index 96f5f8c6b2a..00000000000 --- a/gui2/layereditor/layertreeview.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/layereditor/layertreeview.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/layereditor/layertreeview.h" -#include <QHeaderView> -#include <QMouseEvent> - -namespace gui2 { - -LayerTreeView::~LayerTreeView() = default; - -LayerTreeView::LayerTreeView(QWidget* parent) : QTreeView(parent) -{ - setAlternatingRowColors(true); - setSelectionBehavior(QAbstractItemView::SelectRows); - setSelectionMode(QAbstractItemView::ExtendedSelection); - // setTabKeyNavigation(true); - header()->setSectionResizeMode(QHeaderView::Stretch); -} - -void LayerTreeView::setModel(QAbstractItemModel* model) -{ - QTreeView::setModel(model); - expandAll(); -} - -} // namespace gui2 diff --git a/gui2/layereditor/layertreeview.h b/gui2/layereditor/layertreeview.h deleted file mode 100644 index 020944e05d5..00000000000 --- a/gui2/layereditor/layertreeview.h +++ /dev/null @@ -1,37 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/layereditor/layertreeview.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_LAYEREDITOR_LAYERTREEVIEW_H -#define BORNAGAIN_GUI2_LAYEREDITOR_LAYERTREEVIEW_H - -#include "darefl_export.h" -#include <QTreeView> - -namespace gui2 { - -//! Extension of QTreeView for layer editing. - -class DAREFLCORE_EXPORT LayerTreeView : public QTreeView { -public: - using QTreeView::QTreeView; - - explicit LayerTreeView(QWidget* parent = nullptr); - ~LayerTreeView() override; - - void setModel(QAbstractItemModel* model) override; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_LAYEREDITOR_LAYERTREEVIEW_H diff --git a/gui2/layereditor/layerviewmodel.cpp b/gui2/layereditor/layerviewmodel.cpp deleted file mode 100644 index 81bba9a71c8..00000000000 --- a/gui2/layereditor/layerviewmodel.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/layereditor/layerviewmodel.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/layereditor/layerviewmodel.h" -#include "gui2/layereditor/layerviewmodelcontroller.h" - -using namespace ModelView; - -namespace gui2 { - -LayerViewModel::LayerViewModel(SessionModel* model, QObject* parent) - : ViewModel(std::make_unique<LayerViewModelController>(model, this), parent) -{ -} - -} // namespace gui2 diff --git a/gui2/layereditor/layerviewmodel.h b/gui2/layereditor/layerviewmodel.h deleted file mode 100644 index f787cf4ec8c..00000000000 --- a/gui2/layereditor/layerviewmodel.h +++ /dev/null @@ -1,38 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/layereditor/layerviewmodel.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_LAYEREDITOR_LAYERVIEWMODEL_H -#define BORNAGAIN_GUI2_LAYEREDITOR_LAYERVIEWMODEL_H - -#include "darefl_export.h" -#include "mvvm/viewmodel/viewmodel.h" - -namespace ModelView { -class SessionModel; -} // namespace ModelView - -namespace gui2 { - -//! View model to display content of MultiLayerItem in table like views. - -class DAREFLCORE_EXPORT LayerViewModel : public ModelView::ViewModel { - Q_OBJECT - -public: - LayerViewModel(ModelView::SessionModel* model, QObject* parent = nullptr); -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_LAYEREDITOR_LAYERVIEWMODEL_H diff --git a/gui2/layereditor/layerviewmodelcontroller.cpp b/gui2/layereditor/layerviewmodelcontroller.cpp deleted file mode 100644 index 2e84bd4389e..00000000000 --- a/gui2/layereditor/layerviewmodelcontroller.cpp +++ /dev/null @@ -1,80 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/layereditor/layerviewmodelcontroller.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/layereditor/layerviewmodelcontroller.h" -#include "gui2/model/sampleitems.h" -#include "mvvm/interfaces/rowstrategyinterface.h" -#include "mvvm/viewmodel/standardchildrenstrategies.h" -#include "mvvm/viewmodel/standardviewitems.h" -#include "mvvm/viewmodel/viewmodel.h" - -using namespace ModelView; - -namespace gui2 { - -//! Custom strategy to form table rows for nested multilayers and layers. - -class CustomLayerRowStrategy : public RowStrategyInterface { -public: - QStringList horizontalHeaderLabels() const - { - return QStringList() << "Name" - << "Nr." - << "Material" - << "Thickness [nm]" - << "Sigma [nm]"; - } - - std::vector<std::unique_ptr<ViewItem>> constructRow(SessionItem* item) - { - std::vector<std::unique_ptr<ViewItem>> result; - - // multilayer row contains its name, repetion and placeholders (instead of material and - // thickness) - if (auto multilayer = dynamic_cast<MultiLayerItem*>(item)) { - result.emplace_back( - std::make_unique<ViewDataItem>(multilayer->getItem(LayerItem::P_NAME))); - // result.push_back(new ViewLabelItem(multilayer)); - result.emplace_back(std::make_unique<ViewDataItem>( - multilayer->getItem(MultiLayerItem::P_NREPETITIONS))); - result.emplace_back(std::make_unique<ViewEmptyItem>()); // instead of P_MATERIAL - result.emplace_back(std::make_unique<ViewEmptyItem>()); // instead of P_THICKNESS - result.emplace_back(std::make_unique<ViewEmptyItem>()); // instead of P_ROUGHNESS - } - - // layer row contains its name, placeholder for repetition, layer material and thickness - if (auto layer = dynamic_cast<LayerItem*>(item)) { - result.emplace_back(std::make_unique<ViewDataItem>(layer->getItem(LayerItem::P_NAME))); - // result.push_back(new ViewLabelItem(layer)); - result.emplace_back(std::make_unique<ViewEmptyItem>()); // instead of P_NREPETITIONS - result.emplace_back( - std::make_unique<ViewDataItem>(layer->getItem(LayerItem::P_MATERIAL))); - result.emplace_back( - std::make_unique<ViewDataItem>(layer->getItem(LayerItem::P_THICKNESS))); - result.emplace_back(std::make_unique<ViewDataItem>( - layer->getItem(LayerItem::P_ROUGHNESS)->getItem(RoughnessItem::P_SIGMA))); - } - - return result; - } -}; - -LayerViewModelController::LayerViewModelController(SessionModel* model, ViewModel* view_model) - : ViewModelController(model, view_model) -{ - setRowStrategy(std::make_unique<CustomLayerRowStrategy>()); - setChildrenStrategy(std::make_unique<TopItemsStrategy>()); -} - -} // namespace gui2 diff --git a/gui2/layereditor/layerviewmodelcontroller.h b/gui2/layereditor/layerviewmodelcontroller.h deleted file mode 100644 index ba2f8e54f39..00000000000 --- a/gui2/layereditor/layerviewmodelcontroller.h +++ /dev/null @@ -1,37 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/layereditor/layerviewmodelcontroller.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_LAYEREDITOR_LAYERVIEWMODELCONTROLLER_H -#define BORNAGAIN_GUI2_LAYEREDITOR_LAYERVIEWMODELCONTROLLER_H - -#include "darefl_export.h" -#include "mvvm/viewmodel/viewmodelcontroller.h" - -namespace ModelView { -class ViewModel; -} // namespace ModelView - -namespace gui2 { - -//! Controller for LayerViewModel to show MultiLayerItem in a tree with custom layout. -//! Will iterate through all top level items and creates rows with layer properties. - -class DAREFLCORE_EXPORT LayerViewModelController : public ModelView::ViewModelController { -public: - LayerViewModelController(ModelView::SessionModel* model, ModelView::ViewModel* view_model); -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_LAYEREDITOR_LAYERVIEWMODELCONTROLLER_H diff --git a/gui2/main.cpp b/gui2/main.cpp deleted file mode 100644 index a02e438cf6e..00000000000 --- a/gui2/main.cpp +++ /dev/null @@ -1,32 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/main.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/mainwindow/mainwindow.h" -#include <QApplication> -#include <QLocale> - -int main(int argc, char** argv) -{ - QLocale::setDefault(QLocale(QLocale::English, QLocale::UnitedStates)); - - QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); - QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); - - QApplication app(argc, argv); - - gui2::MainWindow win; - win.show(); - - return app.exec(); -} diff --git a/gui2/mainwindow/CMakeLists.txt b/gui2/mainwindow/CMakeLists.txt deleted file mode 100644 index 686de9d1caf..00000000000 --- a/gui2/mainwindow/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -target_sources(${library_name} PRIVATE - actionmanager.cpp - actionmanager.h - fancytab.cpp - fancytab.h - mainbarwidget.cpp - mainbarwidget.h - mainwindow.cpp - mainwindow.h - simulationview.cpp - simulationview.h - styleutils.cpp - styleutils.h -) - diff --git a/gui2/mainwindow/actionmanager.cpp b/gui2/mainwindow/actionmanager.cpp deleted file mode 100644 index 7f203171236..00000000000 --- a/gui2/mainwindow/actionmanager.cpp +++ /dev/null @@ -1,110 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/mainwindow/actionmanager.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/mainwindow/actionmanager.h" -#include "mvvm/widgets/widgetutils.h" -#include <QAction> -#include <QMainWindow> -#include <QMenuBar> -#include <QToolBar> - -namespace gui2 { - -ActionManager::ActionManager(QMainWindow* mainwindow) - : QObject(mainwindow), m_mainWindow(mainwindow) -{ - createActions(); - setupMenus(m_mainWindow->menuBar()); -} - -void ActionManager::aboutToShowFileMenu() -{ - m_recentProjectMenu->clear(); - m_recentProjectMenu->setEnabled(!m_recentProjects.isEmpty()); - - for (auto project_dir : m_recentProjects) { - auto trimmed_project_dir = ModelView::Utils::WithTildeHomePath(project_dir); - auto action = m_recentProjectMenu->addAction(trimmed_project_dir); - action->setData(QVariant::fromValue(project_dir)); - auto on_project_selected = [this, project_dir]() { - openExistingProjectRequest(project_dir); - }; - connect(action, &QAction::triggered, on_project_selected); - } - - if (!m_recentProjects.empty()) { - m_recentProjectMenu->addSeparator(); - auto action = m_recentProjectMenu->addAction("Clear Menu"); - connect(action, &QAction::triggered, [this]() { clearResentProjectListRequest(); }); - } -} - -void ActionManager::setRecentProjectsList(const QStringList& projects) -{ - m_recentProjects = projects; -} - -//! Creates application-wise actions to create, open, save, and save-as projects. - -void ActionManager::createActions() -{ - m_createNewProjectAction = new QAction("&New Project", this); - m_createNewProjectAction->setShortcuts(QKeySequence::New); - m_createNewProjectAction->setStatusTip("Create a new project"); - connect(m_createNewProjectAction, &QAction::triggered, this, - &ActionManager::createNewProjectRequest); - - m_openExistingProjectAction = new QAction("&Open Project", this); - m_openExistingProjectAction->setShortcuts(QKeySequence::Open); - m_openExistingProjectAction->setStatusTip("Open an existing project"); - connect(m_openExistingProjectAction, &QAction::triggered, - [this]() { openExistingProjectRequest({}); }); - - m_saveCurrentProjectAction = new QAction("&Save Project", this); - m_saveCurrentProjectAction->setShortcuts(QKeySequence::Save); - m_saveCurrentProjectAction->setStatusTip("Save project"); - m_saveCurrentProjectAction->setShortcutContext(Qt::ApplicationShortcut); - connect(m_saveCurrentProjectAction, &QAction::triggered, this, - &ActionManager::saveCurrentProjectRequest); - - m_saveProjectAsAction = new QAction("Save &As...", this); - m_saveProjectAsAction->setShortcuts(QKeySequence::SaveAs); - m_saveProjectAsAction->setStatusTip("Save project under different name"); - connect(m_saveProjectAsAction, &QAction::triggered, this, &ActionManager::saveProjectAsRequest); - - m_exitAction = new QAction("E&xit Application", this); - m_exitAction->setShortcuts(QKeySequence::Quit); - m_exitAction->setStatusTip("Exit the application"); - connect(m_exitAction, &QAction::triggered, m_mainWindow, &QMainWindow::close); -} - -//! Equips menu with actions. - -void ActionManager::setupMenus(QMenuBar* menubar) -{ - auto fileMenu = menubar->addMenu("&File"); - connect(fileMenu, &QMenu::aboutToShow, this, &ActionManager::aboutToShowFileMenu); - fileMenu->addAction(m_createNewProjectAction); - fileMenu->addAction(m_openExistingProjectAction); - m_recentProjectMenu = fileMenu->addMenu("Recent Projects"); - - fileMenu->addSeparator(); - fileMenu->addAction(m_saveCurrentProjectAction); - fileMenu->addAction(m_saveProjectAsAction); - - fileMenu->addSeparator(); - fileMenu->addAction(m_exitAction); -} - -} // namespace gui2 diff --git a/gui2/mainwindow/actionmanager.h b/gui2/mainwindow/actionmanager.h deleted file mode 100644 index bfc19406ba6..00000000000 --- a/gui2/mainwindow/actionmanager.h +++ /dev/null @@ -1,69 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/mainwindow/actionmanager.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_MAINWINDOW_ACTIONMANAGER_H -#define BORNAGAIN_GUI2_MAINWINDOW_ACTIONMANAGER_H - -#include "darefl_export.h" -#include <QObject> - -class QMainWindow; -class QAction; -class QMenuBar; -class QMenu; - -namespace gui2 { - -//! Actions for MainWindow. Equips toolbar and menubar with actions to create, open, save, -//! and save-as projects. It doesn't have logic and simply forwards requests further. - -class DAREFLCORE_EXPORT ActionManager : public QObject { - Q_OBJECT - -public: - ActionManager(QMainWindow* mainwindow = nullptr); - -signals: - void createNewProjectRequest(); - void openExistingProjectRequest(const QString& dirname); - void saveCurrentProjectRequest(); - void saveProjectAsRequest(); - void clearResentProjectListRequest(); - -public slots: - void setRecentProjectsList(const QStringList& projects); - -private slots: - void aboutToShowFileMenu(); - -private: - void createActions(); - void setupMenus(QMenuBar* menubar); - - QMainWindow* m_mainWindow{nullptr}; - - QAction* m_createNewProjectAction{nullptr}; - QAction* m_openExistingProjectAction{nullptr}; - QAction* m_saveCurrentProjectAction{nullptr}; - QAction* m_saveProjectAsAction{nullptr}; - QAction* m_exitAction{nullptr}; - - QMenu* m_recentProjectMenu{nullptr}; - - QStringList m_recentProjects; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_MAINWINDOW_ACTIONMANAGER_H diff --git a/gui2/mainwindow/fancytab.cpp b/gui2/mainwindow/fancytab.cpp deleted file mode 100644 index e8aabba5bd6..00000000000 --- a/gui2/mainwindow/fancytab.cpp +++ /dev/null @@ -1,84 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/mainwindow/fancytab.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/mainwindow/fancytab.h" -#include "mvvm/widgets/widgetutils.h" -#include <QHBoxLayout> -#include <QLabel> -#include <QMouseEvent> -#include <QPainter> - -namespace { -QColor defaultColor() -{ - static QWidget tmpWidget; - return tmpWidget.palette().color(QPalette::Window); -} -} // namespace - -namespace gui2 { - -FancyTab::FancyTab(const QString& title, QWidget* parent) - : QWidget(parent), m_label(new QLabel(title)) -{ - ModelView::Utils::ScaleLabelFont(m_label, 1.25); - setFixedHeight(ModelView::Utils::HeightOfLetterM() * 2.5); - - auto layout = new QHBoxLayout(this); - layout->setContentsMargins(0, 0, 0, 0); - - layout->addWidget(m_label, 0, Qt::AlignCenter); - setMouseTracking(true); -} - -void FancyTab::setSelected(bool value) -{ - m_isSelected = value; - update(); -} - -void FancyTab::paintEvent(QPaintEvent*) -{ - QPainter painter(this); - - if (m_widgetColor.isValid()) - painter.fillRect(0, 0, size().width(), size().height(), m_widgetColor); - - if (m_isSelected && isEnabled()) - painter.fillRect( - QRectF(QPointF(0, size().height() - 2), QPointF(size().width(), size().height())), - QColor("#0d4283")); -} - -void FancyTab::mousePressEvent(QMouseEvent* event) -{ - if (isEnabled() && event->button() == Qt::LeftButton) - clicked(); -} - -void FancyTab::enterEvent(QEvent*) -{ - if (isEnabled()) - m_widgetColor = QColor(Qt::lightGray); - update(); -} - -void FancyTab::leaveEvent(QEvent*) -{ - if (isEnabled()) - m_widgetColor = defaultColor(); - update(); -} - -} // namespace gui2 diff --git a/gui2/mainwindow/fancytab.h b/gui2/mainwindow/fancytab.h deleted file mode 100644 index e9b5f8325db..00000000000 --- a/gui2/mainwindow/fancytab.h +++ /dev/null @@ -1,52 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/mainwindow/fancytab.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_MAINWINDOW_FANCYTAB_H -#define BORNAGAIN_GUI2_MAINWINDOW_FANCYTAB_H - -#include "darefl_export.h" -#include <QColor> -#include <QWidget> - -class QLabel; -class QString; - -namespace gui2 { - -class DAREFLCORE_EXPORT FancyTab : public QWidget { - Q_OBJECT - -public: - FancyTab(const QString& title, QWidget* parent = nullptr); - - void setSelected(bool value); - -signals: - void clicked(); - -protected: - void paintEvent(QPaintEvent*) override; - void mousePressEvent(QMouseEvent* event) override; - void enterEvent(QEvent*) override; - void leaveEvent(QEvent*) override; - -private: - QLabel* m_label{nullptr}; - bool m_isSelected{false}; - QColor m_widgetColor; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_MAINWINDOW_FANCYTAB_H diff --git a/gui2/mainwindow/mainbarwidget.cpp b/gui2/mainwindow/mainbarwidget.cpp deleted file mode 100644 index e75f790a433..00000000000 --- a/gui2/mainwindow/mainbarwidget.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/mainwindow/mainbarwidget.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/mainwindow/mainbarwidget.h" -#include "gui2/mainwindow/fancytab.h" -#include <QHBoxLayout> -#include <QLabel> -#include <QPushButton> -#include <QStackedWidget> -#include <QVBoxLayout> - -namespace gui2 { - -MainBarWidget::MainBarWidget(QWidget* parent) - : QWidget(parent), m_stackedWidget(new QStackedWidget), m_labelLayout(new QHBoxLayout) -{ - m_labelLayout->setContentsMargins(0, 0, 0, 0); - - auto layout = new QVBoxLayout(this); - layout->addLayout(m_labelLayout); - layout->addWidget(m_stackedWidget); - layout->setContentsMargins(0, 0, 0, 0); -} - -MainBarWidget::~MainBarWidget() = default; - -void MainBarWidget::addWidget(QWidget* widget, const QString& title, bool is_enabled) -{ - int index = m_stackedWidget->addWidget(widget); - - auto tab = new FancyTab(title); - tab->setEnabled(is_enabled); - auto on_tab_clicked = [this, index]() { setCurrentIndex(index); }; - connect(tab, &FancyTab::clicked, on_tab_clicked); - - m_indexToTab[index] = tab; - m_labelLayout->addWidget(tab); -} - -void MainBarWidget::setCurrentIndex(int index) -{ - for (auto it : m_indexToTab) - it.second->setSelected(it.first == index); - - m_stackedWidget->setCurrentIndex(index); -} - -} // namespace gui2 diff --git a/gui2/mainwindow/mainbarwidget.h b/gui2/mainwindow/mainbarwidget.h deleted file mode 100644 index 2b6c082e08c..00000000000 --- a/gui2/mainwindow/mainbarwidget.h +++ /dev/null @@ -1,52 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/mainwindow/mainbarwidget.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_MAINWINDOW_MAINBARWIDGET_H -#define BORNAGAIN_GUI2_MAINWINDOW_MAINBARWIDGET_H - -#include "darefl_export.h" -#include <QWidget> -#include <map> - -class QStackedWidget; -class QHBoxLayout; -class QPushButton; - -namespace gui2 { - -class FancyTab; - -//! Widget container with functionality similar to QTabWidget. Has large button bar on top, -//! and stacked widget at bottom. - -class DAREFLCORE_EXPORT MainBarWidget : public QWidget { - Q_OBJECT - -public: - MainBarWidget(QWidget* parent = nullptr); - ~MainBarWidget(); - - void addWidget(QWidget* widget, const QString& title, bool is_enabled = true); - - void setCurrentIndex(int index); - -private: - QStackedWidget* m_stackedWidget{nullptr}; - QHBoxLayout* m_labelLayout{nullptr}; - std::map<int, FancyTab*> m_indexToTab; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_MAINWINDOW_MAINBARWIDGET_H diff --git a/gui2/mainwindow/mainwindow.cpp b/gui2/mainwindow/mainwindow.cpp deleted file mode 100644 index 272176f4215..00000000000 --- a/gui2/mainwindow/mainwindow.cpp +++ /dev/null @@ -1,122 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/mainwindow/mainwindow.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/mainwindow/mainwindow.h" -#include "BAVersion.h" -#include "gui2/importdataview/importdataview.h" -#include "gui2/mainwindow/actionmanager.h" -#include "gui2/mainwindow/mainbarwidget.h" -#include "gui2/mainwindow/simulationview.h" -#include "gui2/model/applicationmodels.h" -#include "gui2/settingsview/settingsview.h" -#include "gui2/welcomeview/welcomeview.h" -#include <QCloseEvent> -#include <QCoreApplication> -#include <QFileDialog> -#include <QMenuBar> -#include <QSettings> - -namespace { -const QString main_window_group = "MainWindow"; -const QString size_key = "size"; -const QString pos_key = "pos"; -} // namespace - -namespace gui2 { - -MainWindow::MainWindow() - : m_models(std::make_unique<ApplicationModels>()), m_actionManager(new ActionManager(this)) -{ - init_application(); - init_components(); - init_connections(); - setCentralWidget(m_barWidget); -} - -MainWindow::~MainWindow() = default; - -void MainWindow::closeEvent(QCloseEvent* event) -{ - if (m_welcomeView->canCloseProject()) { - write_settings(); - event->accept(); - } else { - event->ignore(); - } -} - -void MainWindow::init_application() -{ - QCoreApplication::setApplicationName("BornAgain gui2 preview"); - QCoreApplication::setApplicationVersion(QString::fromStdString(BornAgain::GetVersionNumber())); - QCoreApplication::setOrganizationName("BornAgain"); - - QSettings settings; - if (settings.childGroups().contains(main_window_group)) { - settings.beginGroup(main_window_group); - resize(settings.value(size_key, QSize(400, 400)).toSize()); - move(settings.value(pos_key, QPoint(200, 200)).toPoint()); - settings.endGroup(); - } -} - -void MainWindow::init_components() -{ - m_welcomeView = new WelcomeView(m_models.get()); - m_importDataView = new ImportDataView(m_models.get()); - m_simView = new SimulationView(m_models.get()); - m_settingsView = new SettingsView(m_models.get()); - m_barWidget = new MainBarWidget; - - m_barWidget->addWidget(m_welcomeView, "Project"); - m_barWidget->addWidget(m_importDataView, "Data"); - m_barWidget->addWidget(m_simView, "Simulation"); - m_barWidget->addWidget(new QWidget, "Fitting", false); - m_barWidget->addWidget(new QWidget, "Export", false); - m_barWidget->addWidget(m_settingsView, "Settings"); - m_barWidget->setCurrentIndex(0); -} - -//! Setup main connections. - -void MainWindow::init_connections() -{ - // connect ActionManager signals with WelcomeView slots - connect(m_actionManager, &ActionManager::createNewProjectRequest, m_welcomeView, - &WelcomeView::onCreateNewProject); - connect(m_actionManager, &ActionManager::openExistingProjectRequest, m_welcomeView, - &WelcomeView::onOpenExistingProject); - connect(m_actionManager, &ActionManager::saveCurrentProjectRequest, m_welcomeView, - &WelcomeView::onSaveCurrentProject); - connect(m_actionManager, &ActionManager::saveProjectAsRequest, m_welcomeView, - &WelcomeView::onSaveProjectAs); - connect(m_actionManager, &ActionManager::clearResentProjectListRequest, m_welcomeView, - &WelcomeView::onClearRecentProjectsList); - - connect(m_welcomeView, &WelcomeView::recentProjectsListModified, m_actionManager, - &ActionManager::setRecentProjectsList); - - m_welcomeView->updateNames(); -} - -void MainWindow::write_settings() -{ - QSettings settings; - settings.beginGroup(main_window_group); - settings.setValue(size_key, size()); - settings.setValue(pos_key, pos()); - settings.endGroup(); -} - -} // namespace gui2 diff --git a/gui2/mainwindow/mainwindow.h b/gui2/mainwindow/mainwindow.h deleted file mode 100644 index c86fd27a4f7..00000000000 --- a/gui2/mainwindow/mainwindow.h +++ /dev/null @@ -1,61 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/mainwindow/mainwindow.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_MAINWINDOW_MAINWINDOW_H -#define BORNAGAIN_GUI2_MAINWINDOW_MAINWINDOW_H - -#include "darefl_export.h" -#include <QMainWindow> -#include <memory> - -namespace gui2 { - -class WelcomeView; -class ImportDataView; -class SimulationView; -class MainBarWidget; -class ApplicationModels; -class ActionManager; -class SettingsView; - -//! Application main window. - -class DAREFLCORE_EXPORT MainWindow : public QMainWindow { - Q_OBJECT - -public: - MainWindow(); - ~MainWindow(); - -protected: - void closeEvent(QCloseEvent* event); - -private: - void init_application(); - void init_components(); - void init_connections(); - void write_settings(); - - std::unique_ptr<ApplicationModels> m_models; - ActionManager* m_actionManager{nullptr}; - WelcomeView* m_welcomeView{nullptr}; - ImportDataView* m_importDataView{nullptr}; - SimulationView* m_simView{nullptr}; - SettingsView* m_settingsView{nullptr}; - MainBarWidget* m_barWidget{nullptr}; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_MAINWINDOW_MAINWINDOW_H diff --git a/gui2/mainwindow/simulationview.cpp b/gui2/mainwindow/simulationview.cpp deleted file mode 100644 index 82f05023ff3..00000000000 --- a/gui2/mainwindow/simulationview.cpp +++ /dev/null @@ -1,65 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/mainwindow/simulationview.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/mainwindow/simulationview.h" -#include "gui2/layereditor/layereditor.h" -#include "gui2/materialeditor/materialeditor.h" -#include "gui2/quicksimeditor/instrumentpropertyeditor.h" -#include "gui2/quicksimeditor/quicksimeditor.h" -#include "gui2/sldeditor/sldeditor.h" -#include "mvvm/widgets/collapsiblelistwidget.h" -#include <QSplitter> -#include <QVBoxLayout> - -using namespace ModelView; - -namespace gui2 { - -SimulationView::SimulationView(ApplicationModels* models, QWidget* parent) - : QMainWindow(parent) - , m_editorList(new CollapsibleListWidget) - , m_simEditor(new QuickSimEditor) - , m_models(models) -{ - auto splitter = new QSplitter; - - initEditorList(); - - splitter->addWidget(m_editorList); - splitter->addWidget(m_simEditor); - - setCentralWidget(splitter); -} - -void SimulationView::initEditorList() -{ - m_editorList->layout()->setContentsMargins(4, 4, 4, 4); - auto material_editor = new MaterialEditor(this); - auto layer_editor = new LayerEditor(this); - auto sld_editor = new SLDEditor(this); - auto instrument_editor = new InstrumentPropertyEditor(this); - - m_editorList->addWidget(material_editor, "Material editor"); - m_editorList->addWidget(layer_editor, "Layer editor", /*set_collapsed*/ true); - m_editorList->addWidget(instrument_editor, "Instrument editor", /*set_collapsed*/ true); - m_editorList->addWidget(sld_editor, "SLD editor", /*set_collapsed*/ true); - - material_editor->setModels(m_models); - layer_editor->setModels(m_models); - sld_editor->setModels(m_models); - m_simEditor->setModels(m_models); - instrument_editor->setModels(m_models); -} - -} // namespace gui2 diff --git a/gui2/mainwindow/simulationview.h b/gui2/mainwindow/simulationview.h deleted file mode 100644 index 0daf2048d8d..00000000000 --- a/gui2/mainwindow/simulationview.h +++ /dev/null @@ -1,49 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/mainwindow/simulationview.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_MAINWINDOW_SIMULATIONVIEW_H -#define BORNAGAIN_GUI2_MAINWINDOW_SIMULATIONVIEW_H - -#include "darefl_export.h" -#include <QMainWindow> -#include <memory> - -namespace ModelView { -class CollapsibleListWidget; -} - -namespace gui2 { - -class ApplicationModels; -class QuickSimEditor; - -//! Main simulation window with all components for quick sample editing and simulations. - -class DAREFLCORE_EXPORT SimulationView : public QMainWindow { - Q_OBJECT - -public: - SimulationView(ApplicationModels* models, QWidget* parent = nullptr); - -private: - void initEditorList(); - - ModelView::CollapsibleListWidget* m_editorList{nullptr}; - QuickSimEditor* m_simEditor{nullptr}; - ApplicationModels* m_models{nullptr}; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_MAINWINDOW_SIMULATIONVIEW_H diff --git a/gui2/mainwindow/styleutils.cpp b/gui2/mainwindow/styleutils.cpp deleted file mode 100644 index b623439ad65..00000000000 --- a/gui2/mainwindow/styleutils.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/mainwindow/styleutils.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/mainwindow/styleutils.h" -#include "gui2/resources/resources.h" -#include <QFontMetrics> -#include <QSize> -#include <QToolBar> -#include <QWidget> - -namespace gui2 { - -QSize GUI::Utils::Style::ToolBarIconSize() -{ - return QSize(24, 24); -} - -QSize GUI::Utils::Style::DockSizeHint() -{ - return QSize(480, 360); -} - -QSize GUI::Utils::Style::DockMinimumSizeHint() -{ - return QSize(320, 240); -} - -void GUI::Utils::Style::SetToolBarStyleTextBesides(QToolBar* toolbar) -{ - InitIconResources(); - toolbar->setIconSize(GUI::Utils::Style::ToolBarIconSize()); - toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); -} - -} // namespace gui2 diff --git a/gui2/mainwindow/styleutils.h b/gui2/mainwindow/styleutils.h deleted file mode 100644 index 46cd0558141..00000000000 --- a/gui2/mainwindow/styleutils.h +++ /dev/null @@ -1,46 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/mainwindow/styleutils.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_MAINWINDOW_STYLEUTILS_H -#define BORNAGAIN_GUI2_MAINWINDOW_STYLEUTILS_H - -#include "darefl_export.h" - -class QSize; -class QFont; -class QToolBar; - -namespace gui2 { - -//! Namespace for central access to all theme styling. - -namespace GUI::Utils::Style { - -//! Size of tolbar icons for LayerEditor, MaterialEditor and similar. -DAREFLCORE_EXPORT QSize ToolBarIconSize(); - -//! Hint on size of docks on main reflectometry window. -DAREFLCORE_EXPORT QSize DockSizeHint(); - -//! Hint on minimum size of docks on main reflectometry window. -DAREFLCORE_EXPORT QSize DockMinimumSizeHint(); - -//! Set common style for a toolbar. -DAREFLCORE_EXPORT void SetToolBarStyleTextBesides(QToolBar* toolbar); - -}; // namespace GUI::Utils::Style - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_MAINWINDOW_STYLEUTILS_H diff --git a/gui2/materialeditor/CMakeLists.txt b/gui2/materialeditor/CMakeLists.txt deleted file mode 100644 index 60371de52cd..00000000000 --- a/gui2/materialeditor/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -target_sources(${library_name} PRIVATE - materialeditor.cpp - materialeditor.h - materialeditoractions.cpp - materialeditoractions.h - materialeditortoolbar.cpp - materialeditortoolbar.h - materialeditorwidget.cpp - materialeditorwidget.h - materialselectionmodel.cpp - materialselectionmodel.h - materialtableview.cpp - materialtableview.h - materialtreeview.cpp - materialtreeview.h -) diff --git a/gui2/materialeditor/materialeditor.cpp b/gui2/materialeditor/materialeditor.cpp deleted file mode 100644 index b26a7a8b4e5..00000000000 --- a/gui2/materialeditor/materialeditor.cpp +++ /dev/null @@ -1,61 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/materialeditor/materialeditor.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/materialeditor/materialeditor.h" -#include "gui2/mainwindow/styleutils.h" -#include "gui2/materialeditor/materialeditoractions.h" -#include "gui2/materialeditor/materialeditortoolbar.h" -#include "gui2/materialeditor/materialeditorwidget.h" -#include "gui2/model/applicationmodels.h" -#include "gui2/model/materialmodel.h" -#include <QVBoxLayout> - -namespace gui2 { - -MaterialEditor::MaterialEditor(QWidget* parent) - : QWidget(parent) - , m_actions(new MaterialEditorActions(this)) - , m_editorWidget(new MaterialEditorWidget) - , m_toolBar(new MaterialEditorToolBar(m_actions)) -{ - setWindowTitle("Material editor"); - auto layout = new QVBoxLayout; - layout->addWidget(m_toolBar); - layout->addWidget(m_editorWidget); - setLayout(layout); - layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(0); -} - -//! Set the mododel for the different items -void MaterialEditor::setModels(ApplicationModels* models) -{ - m_editorWidget->setModels(models); - m_actions->setModel(models->materialModel()); - m_actions->setMaterialSelectionModel(m_editorWidget->selectionModel()); -} - -QSize MaterialEditor::sizeHint() const -{ - return GUI::Utils::Style::DockSizeHint(); -} - -QSize MaterialEditor::minimumSizeHint() const -{ - return GUI::Utils::Style::DockMinimumSizeHint(); -} - -MaterialEditor::~MaterialEditor() = default; - -} // namespace gui2 diff --git a/gui2/materialeditor/materialeditor.h b/gui2/materialeditor/materialeditor.h deleted file mode 100644 index 9dda6f932f3..00000000000 --- a/gui2/materialeditor/materialeditor.h +++ /dev/null @@ -1,50 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/materialeditor/materialeditor.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_MATERIALEDITOR_MATERIALEDITOR_H -#define BORNAGAIN_GUI2_MATERIALEDITOR_MATERIALEDITOR_H - -#include "darefl_export.h" -#include <QWidget> - -namespace gui2 { - -class ApplicationModels; -class MaterialEditorActions; -class MaterialEditorToolBar; -class MaterialEditorWidget; - -//! Material editor. - -class DAREFLCORE_EXPORT MaterialEditor : public QWidget { - Q_OBJECT - -public: - MaterialEditor(QWidget* parent = nullptr); - ~MaterialEditor(); - - void setModels(ApplicationModels* models); - - QSize sizeHint() const override; - QSize minimumSizeHint() const override; - -private: - MaterialEditorActions* m_actions{nullptr}; - MaterialEditorWidget* m_editorWidget{nullptr}; - MaterialEditorToolBar* m_toolBar{nullptr}; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_MATERIALEDITOR_MATERIALEDITOR_H diff --git a/gui2/materialeditor/materialeditoractions.cpp b/gui2/materialeditor/materialeditoractions.cpp deleted file mode 100644 index 59ff450438a..00000000000 --- a/gui2/materialeditor/materialeditoractions.cpp +++ /dev/null @@ -1,180 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/materialeditor/materialeditoractions.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/materialeditor/materialeditoractions.h" -#include "gui2/materialeditor/materialselectionmodel.h" -#include "gui2/model/materialitems.h" -#include "gui2/model/materialmodel.h" -#include "mvvm/model/modelutils.h" -#include "mvvm/model/sessionitemdata.h" -#include "mvvm/viewmodel/viewmodel.h" -#include <QFile> -#include <QTextStream> - -using namespace ModelView; - -namespace gui2 { - -struct MaterialEditorActions::MaterialEditorActionsImpl { - MaterialModel* material_model{nullptr}; - MaterialSelectionModel* selection_model{nullptr}; - MaterialEditorActionsImpl() {} - - //! Finds parent and tagrow to insert new item - - std::pair<SessionItem*, TagRow> locateInsertPlace() - { - auto all_selected = selection_model->selectedMaterials(); - auto selected = all_selected.empty() ? nullptr : all_selected.back(); - if (selected) - return {selected->parent(), selected->tagRow().next()}; - return {root_item(), TagRow{}}; - } - - //! Returns a multi layer playing the role of invisible root item. - - ModelView::SessionItem* root_item() - { - return selection_model->viewModel()->sessionItemFromIndex(QModelIndex()); - } -}; - -MaterialEditorActions::MaterialEditorActions(QObject* parent) - : QObject(parent), p_impl(std::make_unique<MaterialEditorActionsImpl>()) -{ -} - -void MaterialEditorActions::setModel(MaterialModel* model) -{ - p_impl->material_model = model; -} - -void MaterialEditorActions::setMaterialSelectionModel(MaterialSelectionModel* selection_model) -{ - p_impl->selection_model = selection_model; -} - -void MaterialEditorActions::onAddMaterial() -{ - if (!p_impl->material_model) - return; - - auto [parent, tagrow] = p_impl->locateInsertPlace(); - auto material = p_impl->material_model->addDefaultMaterial(tagrow); - p_impl->selection_model->selectItem(material); -} - -//! Processes request to clone selected materials. - -void MaterialEditorActions::onCloneMaterial() -{ - if (!p_impl->material_model) - return; - - std::vector<ModelView::SessionItem*> new_selection; - for (const auto item : p_impl->selection_model->selectedMaterials()) - new_selection.push_back(p_impl->material_model->cloneMaterial(item)); - p_impl->selection_model->selectItems(new_selection); -} - -void MaterialEditorActions::onRemoveMaterial() -{ - if (!p_impl->selection_model) - return; - - for (auto item : p_impl->selection_model->selectedMaterials()) - ModelView::Utils::DeleteItemFromModel(item); -} - -void MaterialEditorActions::onMoveUp() -{ - if (!p_impl->selection_model) - return; - - for (auto item : p_impl->selection_model->selectedMaterials()) - ModelView::Utils::MoveUp(item); -} - -void MaterialEditorActions::onMoveDown() -{ - if (!p_impl->selection_model) - return; - - auto items = p_impl->selection_model->selectedMaterials(); - std::reverse(items.begin(), items.end()); // to correctly move multiple selections - for (auto item : p_impl->selection_model->selectedMaterials()) - ModelView::Utils::MoveDown(item); -} - -void MaterialEditorActions::onExport() -{ - auto item = p_impl->root_item(); - const auto containers = item->children(); - - bool title = true; // print title only once in ascii file - QString tableData; - QString titleData; - - for (auto container : containers) { - auto data = container->modelType(); - for (auto fields : dynamic_cast<MaterialBaseItem*>(container)->children()) { - if (title) { - titleData += (fields->displayName()).c_str(); - titleData += " "; - } - auto val = fields->data<QVariant>(1); // role 1 has the values. - if (strcmp(val.typeName(), "std::string") == 0) { - tableData += val.value<std::string>().c_str(); - tableData += " "; - } else if (strcmp(val.typeName(), "int") == 0) { - auto int_val = val.value<int>(); - tableData += QString::number(int_val); - tableData += " "; - } else if (strcmp(val.typeName(), "double") == 0) { - auto double_val = val.value<double>(); - tableData += QString::number(double_val); - tableData += " "; - } else if (strcmp(val.typeName(), "float") == 0) { - auto float_val = val.value<float>(); - tableData += QString::number(float_val); - tableData += " "; - } else { - auto color_str = val.toString(); - tableData += color_str; - tableData += " "; - } - } - if (title) { - titleData += "\n"; - tableData = titleData + tableData; - } - title = false; - tableData += "\n"; - } - /*for text file*/ - QFile txtFile("materialdata"); - if (txtFile.open(QIODevice::WriteOnly)) { - - QTextStream out(&txtFile); - out << tableData; - - txtFile.close(); - } -} - -void MaterialEditorActions::onImport() {} - -MaterialEditorActions::~MaterialEditorActions() = default; - -} // namespace gui2 diff --git a/gui2/materialeditor/materialeditoractions.h b/gui2/materialeditor/materialeditoractions.h deleted file mode 100644 index 151ceaeab2b..00000000000 --- a/gui2/materialeditor/materialeditoractions.h +++ /dev/null @@ -1,56 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/materialeditor/materialeditoractions.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_MATERIALEDITOR_MATERIALEDITORACTIONS_H -#define BORNAGAIN_GUI2_MATERIALEDITOR_MATERIALEDITORACTIONS_H - -#include "darefl_export.h" -#include <QObject> -#include <memory> - -namespace gui2 { - -class MaterialModel; -class MaterialSelectionModel; - -//! Handles user actions applied to material table. -//! Belongs to MaterialEditor. - -class DAREFLCORE_EXPORT MaterialEditorActions : public QObject { - Q_OBJECT - -public: - MaterialEditorActions(QObject* parent = nullptr); - ~MaterialEditorActions(); - - void setModel(MaterialModel* model); - - void onAddMaterial(); - void onCloneMaterial(); - void onRemoveMaterial(); - void onMoveUp(); - void onMoveDown(); - void onExport(); - void onImport(); - - void setMaterialSelectionModel(MaterialSelectionModel* selection_model); - -private: - struct MaterialEditorActionsImpl; - std::unique_ptr<MaterialEditorActionsImpl> p_impl; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_MATERIALEDITOR_MATERIALEDITORACTIONS_H diff --git a/gui2/materialeditor/materialeditortoolbar.cpp b/gui2/materialeditor/materialeditortoolbar.cpp deleted file mode 100644 index e0ad2ff50b8..00000000000 --- a/gui2/materialeditor/materialeditortoolbar.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/materialeditor/materialeditortoolbar.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/materialeditor/materialeditortoolbar.h" -#include "gui2/mainwindow/styleutils.h" -#include "gui2/materialeditor/materialeditoractions.h" -#include <QAction> -#include <QToolButton> - -namespace gui2 { - -MaterialEditorToolBar::MaterialEditorToolBar(MaterialEditorActions* actions, QWidget* parent) - : QToolBar(parent) -{ - GUI::Utils::Style::SetToolBarStyleTextBesides(this); - - auto action = new QAction("Add material", this); - action->setIcon(QIcon(":/icons/plus-circle-outline.svg")); - action->setToolTip("Adds new material at the bottom of the list"); - connect(action, &QAction::triggered, actions, &MaterialEditorActions::onAddMaterial); - addAction(action); - - action = new QAction("Clone", this); - action->setIcon(QIcon(":/icons/plus-circle-multiple-outline.svg")); - action->setToolTip("Clones selected material"); - connect(action, &QAction::triggered, actions, &MaterialEditorActions::onCloneMaterial); - addAction(action); - - action = new QAction("Remove", this); - action->setIcon(QIcon(":/icons/beaker-remove-outline.svg")); - action->setToolTip("Removes selected material"); - connect(action, &QAction::triggered, actions, &MaterialEditorActions::onRemoveMaterial); - addAction(action); - - addSeparator(); - - action = new QAction("Up", this); - action->setIcon(QIcon(":/icons/arrow-up-circle-outline.svg")); - action->setToolTip("Moves selected material up"); - connect(action, &QAction::triggered, actions, &MaterialEditorActions::onMoveUp); - addAction(action); - - action = new QAction("Down", this); - action->setIcon(QIcon(":/icons/arrow-down-circle-outline.svg")); - action->setToolTip("Moves selected material down"); - connect(action, &QAction::triggered, actions, &MaterialEditorActions::onMoveDown); - addAction(action); - - addSeparator(); - - action = new QAction("Import", this); - action->setIcon(QIcon(":/icons/import.svg")); - action->setToolTip("Imports materials from file"); - connect(action, &QAction::triggered, actions, &MaterialEditorActions::onImport); - addAction(action); - - action = new QAction("Export", this); - action->setIcon(QIcon(":/icons/export.svg")); - action->setToolTip("Exports materials to file"); - connect(action, &QAction::triggered, actions, &MaterialEditorActions::onExport); - addAction(action); -} - -} // namespace gui2 diff --git a/gui2/materialeditor/materialeditortoolbar.h b/gui2/materialeditor/materialeditortoolbar.h deleted file mode 100644 index 201f66441fd..00000000000 --- a/gui2/materialeditor/materialeditortoolbar.h +++ /dev/null @@ -1,37 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/materialeditor/materialeditortoolbar.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_MATERIALEDITOR_MATERIALEDITORTOOLBAR_H -#define BORNAGAIN_GUI2_MATERIALEDITOR_MATERIALEDITORTOOLBAR_H - -#include "darefl_export.h" -#include <QToolBar> - -namespace gui2 { - -class MaterialEditorActions; - -//! Material editor toolbar. - -class DAREFLCORE_EXPORT MaterialEditorToolBar : public QToolBar { - Q_OBJECT - -public: - MaterialEditorToolBar(MaterialEditorActions* actions, QWidget* parent = nullptr); - ~MaterialEditorToolBar() = default; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_MATERIALEDITOR_MATERIALEDITORTOOLBAR_H diff --git a/gui2/materialeditor/materialeditorwidget.cpp b/gui2/materialeditor/materialeditorwidget.cpp deleted file mode 100644 index 21041496081..00000000000 --- a/gui2/materialeditor/materialeditorwidget.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/materialeditor/materialeditorwidget.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/materialeditor/materialeditorwidget.h" -#include "gui2/materialeditor/materialselectionmodel.h" -#include "gui2/materialeditor/materialtableview.h" -#include "gui2/materialeditor/materialtreeview.h" -#include "gui2/model/applicationmodels.h" -#include "gui2/model/materialitems.h" -#include "gui2/model/materialmodel.h" -#include "mvvm/factories/viewmodelfactory.h" -#include "mvvm/model/modelutils.h" -#include "mvvm/viewmodel/viewmodeldelegate.h" -#include <QVBoxLayout> - -namespace gui2 { - -MaterialEditorWidget::MaterialEditorWidget(QWidget* parent) - : QWidget(parent) - , m_materialView(new MaterialTreeView) - , m_delegate(std::make_unique<ModelView::ViewModelDelegate>()) -{ - auto layout = new QVBoxLayout; - layout->setContentsMargins(0, 0, 0, 0); - layout->addWidget(m_materialView); - setLayout(layout); - m_materialView->setItemDelegate(m_delegate.get()); -} - -MaterialEditorWidget::~MaterialEditorWidget() = default; - -void MaterialEditorWidget::setModels(ApplicationModels* models) -{ - m_materialModel = models->materialModel(); - m_viewModel = ModelView::Factory::CreatePropertyTableViewModel(m_materialModel); - m_selectionModel = new MaterialSelectionModel(m_viewModel.get(), this); - m_viewModel->setRootSessionItem( - ModelView::Utils::TopItem<MaterialContainerItem>(m_materialModel)); - m_materialView->setModel(m_viewModel.get()); - m_materialView->setSelectionModel(m_selectionModel); -} - -MaterialSelectionModel* MaterialEditorWidget::selectionModel() const -{ - return m_selectionModel; -} - -} // namespace gui2 diff --git a/gui2/materialeditor/materialeditorwidget.h b/gui2/materialeditor/materialeditorwidget.h deleted file mode 100644 index 6ec8302c2c4..00000000000 --- a/gui2/materialeditor/materialeditorwidget.h +++ /dev/null @@ -1,59 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/materialeditor/materialeditorwidget.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_MATERIALEDITOR_MATERIALEDITORWIDGET_H -#define BORNAGAIN_GUI2_MATERIALEDITOR_MATERIALEDITORWIDGET_H - -#include "darefl_export.h" -#include <QWidget> -#include <memory> - -namespace ModelView { -class ViewModel; -class ViewModelDelegate; -} // namespace ModelView - -namespace gui2 { - -class ApplicationModels; -class MaterialModel; -class MaterialTableView; -class MaterialTreeView; -class MaterialSelectionModel; - -//! Widget to hold material table (MaterialTreeView) and all corresponding models and delegates. -//! Belongs to MaterialEditor. - -class DAREFLCORE_EXPORT MaterialEditorWidget : public QWidget { - Q_OBJECT - -public: - MaterialEditorWidget(QWidget* parent = nullptr); - ~MaterialEditorWidget(); - - void setModels(ApplicationModels* models); - - MaterialSelectionModel* selectionModel() const; - -private: - MaterialModel* m_materialModel{nullptr}; - std::unique_ptr<ModelView::ViewModel> m_viewModel; - MaterialSelectionModel* m_selectionModel{nullptr}; - MaterialTreeView* m_materialView{nullptr}; - std::unique_ptr<ModelView::ViewModelDelegate> m_delegate; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_MATERIALEDITOR_MATERIALEDITORWIDGET_H diff --git a/gui2/materialeditor/materialselectionmodel.cpp b/gui2/materialeditor/materialselectionmodel.cpp deleted file mode 100644 index 6095cbfe492..00000000000 --- a/gui2/materialeditor/materialselectionmodel.cpp +++ /dev/null @@ -1,66 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/materialeditor/materialselectionmodel.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/materialeditor/materialselectionmodel.h" -#include "gui2/model/materialitems.h" -#include "mvvm/viewmodel/viewmodel.h" -#include "mvvm/viewmodel/viewmodelutils.h" - -namespace gui2 { - -MaterialSelectionModel::MaterialSelectionModel(ModelView::ViewModel* view_model, QObject* parent) - : QItemSelectionModel(view_model, parent) -{ - // FIXME cover with unit tests after implementing ViewItemSelectionModel - connect(view_model, &ModelView::ViewModel::modelAboutToBeReset, [this]() { clearSelection(); }); -} - -void MaterialSelectionModel::selectItem(ModelView::SessionItem* item) -{ - selectItems({item}); -} - -void MaterialSelectionModel::selectItems(std::vector<ModelView::SessionItem*> items) -{ - QModelIndexList indexes; - for (auto item : items) - indexes << viewModel()->indexOfSessionItem(item->getItem(MaterialBaseItem::P_NAME)); - - if (indexes.empty()) - return; - - clearSelection(); - - QItemSelection selection(indexes.front(), indexes.back()); - auto flags = QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows; - select(selection, flags); -} - -//! Returns vector of selected materials. - -std::vector<MaterialBaseItem*> MaterialSelectionModel::selectedMaterials() const -{ - std::vector<MaterialBaseItem*> result; - auto selected_items = ModelView::Utils::ParentItemsFromIndex(selectedIndexes()); - std::transform(std::begin(selected_items), std::end(selected_items), std::back_inserter(result), - [](auto item) { return dynamic_cast<MaterialBaseItem*>(item); }); - return result; -} - -const ModelView::ViewModel* MaterialSelectionModel::viewModel() const -{ - return static_cast<const ModelView::ViewModel*>(model()); -} - -} // namespace gui2 diff --git a/gui2/materialeditor/materialselectionmodel.h b/gui2/materialeditor/materialselectionmodel.h deleted file mode 100644 index 297b5ed7281..00000000000 --- a/gui2/materialeditor/materialselectionmodel.h +++ /dev/null @@ -1,53 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/materialeditor/materialselectionmodel.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_MATERIALEDITOR_MATERIALSELECTIONMODEL_H -#define BORNAGAIN_GUI2_MATERIALEDITOR_MATERIALSELECTIONMODEL_H - -#include "darefl_export.h" -#include <QItemSelectionModel> -#include <vector> - -namespace ModelView { -class ViewModel; -class SessionItem; -} // namespace ModelView - -namespace gui2 { - -class MaterialEditorActions; -class MaterialBaseItem; - -//! Custom selection model for material view model (AbstractViewModel). -//! Reports clients about selected MaterialItem in material table and hides -//! QModelIndex related machinery. - -class DAREFLCORE_EXPORT MaterialSelectionModel : public QItemSelectionModel { - Q_OBJECT - -public: - MaterialSelectionModel(ModelView::ViewModel* view_model, QObject* parent = nullptr); - ~MaterialSelectionModel() = default; - - void selectItem(ModelView::SessionItem* item); - void selectItems(std::vector<ModelView::SessionItem*> items); - - std::vector<MaterialBaseItem*> selectedMaterials() const; - - const ModelView::ViewModel* viewModel() const; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_MATERIALEDITOR_MATERIALSELECTIONMODEL_H diff --git a/gui2/materialeditor/materialtableview.cpp b/gui2/materialeditor/materialtableview.cpp deleted file mode 100644 index 332c935f5c4..00000000000 --- a/gui2/materialeditor/materialtableview.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/materialeditor/materialtableview.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/materialeditor/materialtableview.h" -#include <QHeaderView> -#include <QMouseEvent> - -namespace gui2 { - -MaterialTableView::~MaterialTableView() = default; - -void MaterialTableView::setModel(QAbstractItemModel* model) -{ - QTableView::setModel(model); - setAlternatingRowColors(true); - horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); - horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents); -} - -void MaterialTableView::keyPressEvent(QKeyEvent* event) -{ - if (!event || event->key() != Qt::Key_Return || state() == QAbstractItemView::EditingState) - return QTableView::keyPressEvent(event); - - const QModelIndex index = currentIndex(); - if (isKeyboardEditable(index)) - edit(index); -} - -QModelIndex MaterialTableView::moveCursor(QAbstractItemView::CursorAction cursorAction, - Qt::KeyboardModifiers modifiers) -{ - const QModelIndex current_index = currentIndex(); - bool filtered_action = cursorAction == QAbstractItemView::MoveNext - || cursorAction == QAbstractItemView::MovePrevious; - - if (!current_index.isValid() || !isTextField(current_index) || !filtered_action) - return QTableView::moveCursor(cursorAction, modifiers); - - QModelIndex next = current_index; - do { - setCurrentIndex(next); - next = QTableView::moveCursor(cursorAction, modifiers); - } while (!isTextField(next)); - return next; -} - -bool MaterialTableView::isTextField(const QModelIndex& index) const -{ - return index.isValid() && index.column() > 1; // color and checkbox are not keyboard editable -} - -bool MaterialTableView::isKeyboardEditable(const QModelIndex& index) const -{ - return index.isValid(); -} - -} // namespace gui2 diff --git a/gui2/materialeditor/materialtableview.h b/gui2/materialeditor/materialtableview.h deleted file mode 100644 index 1a276a4b567..00000000000 --- a/gui2/materialeditor/materialtableview.h +++ /dev/null @@ -1,46 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/materialeditor/materialtableview.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_MATERIALEDITOR_MATERIALTABLEVIEW_H -#define BORNAGAIN_GUI2_MATERIALEDITOR_MATERIALTABLEVIEW_H - -#include "darefl_export.h" -#include <QTableView> - -namespace gui2 { - -//! Extension of QTableView for material editing. -//! Provide better user experinece while navigating between cells. -//! Part of MaterialTableWidget. - -class DAREFLCORE_EXPORT MaterialTableView : public QTableView { -public: - using QTableView::QTableView; - ~MaterialTableView() override; - - void setModel(QAbstractItemModel* model) override; - -protected: - void keyPressEvent(QKeyEvent* event) override; - QModelIndex moveCursor(QAbstractItemView::CursorAction cursorAction, - Qt::KeyboardModifiers modifiers) override; - -private: - bool isTextField(const QModelIndex& index) const; - bool isKeyboardEditable(const QModelIndex& index) const; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_MATERIALEDITOR_MATERIALTABLEVIEW_H diff --git a/gui2/materialeditor/materialtreeview.cpp b/gui2/materialeditor/materialtreeview.cpp deleted file mode 100644 index b21916153e3..00000000000 --- a/gui2/materialeditor/materialtreeview.cpp +++ /dev/null @@ -1,76 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/materialeditor/materialtreeview.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/materialeditor/materialtreeview.h" -#include <QHeaderView> -#include <QMouseEvent> - -namespace gui2 { - -MaterialTreeView::~MaterialTreeView() = default; - -MaterialTreeView::MaterialTreeView(QWidget* parent) : QTreeView(parent) -{ - setAlternatingRowColors(true); - setSelectionBehavior(QAbstractItemView::SelectRows); - setSelectionMode(QAbstractItemView::ExtendedSelection); - // setTabKeyNavigation(true); - header()->setSectionResizeMode(QHeaderView::Stretch); -} - -void MaterialTreeView::setModel(QAbstractItemModel* model) -{ - QTreeView::setModel(model); - expandAll(); -} - -void MaterialTreeView::keyPressEvent(QKeyEvent* event) -{ - if (!event || event->key() != Qt::Key_Return || state() == QAbstractItemView::EditingState) - return QTreeView::keyPressEvent(event); - - const QModelIndex index = currentIndex(); - if (isKeyboardEditable(index)) - edit(index); -} - -QModelIndex MaterialTreeView::moveCursor(QAbstractItemView::CursorAction cursorAction, - Qt::KeyboardModifiers modifiers) -{ - const QModelIndex current_index = currentIndex(); - bool filtered_action = cursorAction == QAbstractItemView::MoveNext - || cursorAction == QAbstractItemView::MovePrevious; - - if (!current_index.isValid() || !isTextField(current_index) || !filtered_action) - return QTreeView::moveCursor(cursorAction, modifiers); - - QModelIndex next = current_index; - do { - setCurrentIndex(next); - next = QTreeView::moveCursor(cursorAction, modifiers); - } while (!isTextField(next)); - return next; -} - -bool MaterialTreeView::isTextField(const QModelIndex& index) const -{ - return index.isValid() && index.column() > 0; // color is not keyboard editable -} - -bool MaterialTreeView::isKeyboardEditable(const QModelIndex& index) const -{ - return index.isValid(); -} - -} // namespace gui2 diff --git a/gui2/materialeditor/materialtreeview.h b/gui2/materialeditor/materialtreeview.h deleted file mode 100644 index 2a7f3492e1d..00000000000 --- a/gui2/materialeditor/materialtreeview.h +++ /dev/null @@ -1,48 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/materialeditor/materialtreeview.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_MATERIALEDITOR_MATERIALTREEVIEW_H -#define BORNAGAIN_GUI2_MATERIALEDITOR_MATERIALTREEVIEW_H - -#include "darefl_export.h" -#include <QTreeView> - -namespace gui2 { - -//! Extension of QTreeView for material editing. -//! Provide better user experinece while navigating between cells. -//! Part of MaterialTableWidget. - -class DAREFLCORE_EXPORT MaterialTreeView : public QTreeView { -public: - using QTreeView::QTreeView; - - explicit MaterialTreeView(QWidget* parent = nullptr); - ~MaterialTreeView() override; - - void setModel(QAbstractItemModel* model) override; - -protected: - void keyPressEvent(QKeyEvent* event) override; - QModelIndex moveCursor(QAbstractItemView::CursorAction cursorAction, - Qt::KeyboardModifiers modifiers) override; - -private: - bool isTextField(const QModelIndex& index) const; - bool isKeyboardEditable(const QModelIndex& index) const; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_MATERIALEDITOR_MATERIALTREEVIEW_H diff --git a/gui2/model/CMakeLists.txt b/gui2/model/CMakeLists.txt deleted file mode 100644 index 6245d7c30c6..00000000000 --- a/gui2/model/CMakeLists.txt +++ /dev/null @@ -1,31 +0,0 @@ -target_sources(${library_name} PRIVATE - applicationmodels.cpp - applicationmodels.h - experimentaldatacontroller.cpp - experimentaldatacontroller.h - experimentaldataitems.cpp - experimentaldataitems.h - experimentaldatamodel.cpp - experimentaldatamodel.h - instrumentitems.cpp - instrumentitems.h - instrumentmodel.cpp - instrumentmodel.h - item_constants.h - jobitem.cpp - jobitem.h - jobmodel.cpp - jobmodel.h - materialitems.cpp - materialitems.h - materialmodel.cpp - materialmodel.h - materialpropertycontroller.cpp - materialpropertycontroller.h - modelutils.cpp - modelutils.h - sampleitems.cpp - sampleitems.h - samplemodel.cpp - samplemodel.h -) diff --git a/gui2/model/applicationmodels.cpp b/gui2/model/applicationmodels.cpp deleted file mode 100644 index bd6e815bb31..00000000000 --- a/gui2/model/applicationmodels.cpp +++ /dev/null @@ -1,138 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/model/applicationmodels.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/model/applicationmodels.h" -#include "gui2/model/experimentaldatacontroller.h" -#include "gui2/model/experimentaldatamodel.h" -#include "gui2/model/instrumentmodel.h" -#include "gui2/model/jobmodel.h" -#include "gui2/model/materialmodel.h" -#include "gui2/model/materialpropertycontroller.h" -#include "gui2/model/sampleitems.h" -#include "gui2/model/samplemodel.h" -#include "gui2/sldeditor/sldelementmodel.h" -#include "mvvm/model/externalproperty.h" -#include "mvvm/model/itempool.h" -#include "mvvm/model/modelutils.h" -#include "mvvm/model/sessionitem.h" - -namespace gui2 { - -using namespace ModelView; - -struct ApplicationModels::ApplicationModelsImpl { - std::unique_ptr<MaterialModel> m_material_model; - std::unique_ptr<SampleModel> m_sample_model; - std::unique_ptr<SLDElementModel> m_sld_view_model; - std::unique_ptr<JobModel> m_job_model; - std::unique_ptr<ExperimentalDataModel> m_experimental_model; - std::unique_ptr<InstrumentModel> m_instrument_model; - std::unique_ptr<MaterialPropertyController> m_material_controller; - std::unique_ptr<ExperimentalDataController> m_data_controller; - std::shared_ptr<ItemPool> item_pool; - - ApplicationModelsImpl() - { - item_pool = std::make_shared<ItemPool>(); - m_material_model = std::make_unique<MaterialModel>(item_pool); - m_sample_model = std::make_unique<SampleModel>(item_pool); - m_sld_view_model = std::make_unique<SLDElementModel>(); - m_job_model = std::make_unique<JobModel>(item_pool); - m_experimental_model = std::make_unique<ExperimentalDataModel>(item_pool); - m_instrument_model = std::make_unique<InstrumentModel>(item_pool); - m_material_controller = std::make_unique<MaterialPropertyController>(m_material_model.get(), - m_sample_model.get()); - m_data_controller = std::make_unique<ExperimentalDataController>(m_experimental_model.get(), - m_instrument_model.get()); - m_sample_model->create_default_multilayer(); - update_material_properties(); - } - - //! Runs through all layers and assign materials. - //! Expecting 3 materials existing by default (air, default, Si) to assign to our 3 layers. - - void update_material_properties() - { - auto multilayer = Utils::TopItem<MultiLayerItem>(m_sample_model.get()); - auto layers = multilayer->items<LayerItem>(MultiLayerItem::T_LAYERS); - size_t index(0); - for (const auto& material_property : m_material_model->material_data()) { - if (index < layers.size()) - layers[index]->setProperty(LayerItem::P_MATERIAL, material_property); - ++index; - } - } - - //! Models intended for saving. - std::vector<SessionModel*> persistent_models() const - { - return {m_material_model.get(), m_sample_model.get(), m_instrument_model.get(), - m_experimental_model.get()}; - } - - //! All application models. - std::vector<SessionModel*> application_models() const - { - return {m_material_model.get(), m_sample_model.get(), m_instrument_model.get(), - m_sld_view_model.get(), m_job_model.get(), m_experimental_model.get()}; - } -}; - -ApplicationModels::ApplicationModels() : p_impl(std::make_unique<ApplicationModelsImpl>()) {} - -ApplicationModels::~ApplicationModels() = default; - -MaterialModel* ApplicationModels::materialModel() -{ - return p_impl->m_material_model.get(); -} - -SampleModel* ApplicationModels::sampleModel() -{ - return p_impl->m_sample_model.get(); -} - -SLDElementModel* ApplicationModels::sldViewModel() -{ - return p_impl->m_sld_view_model.get(); -} - -JobModel* ApplicationModels::jobModel() -{ - return p_impl->m_job_model.get(); -} - -ExperimentalDataModel* ApplicationModels::experimentalDataModel() -{ - return p_impl->m_experimental_model.get(); -} - -InstrumentModel* ApplicationModels::instrumentModel() -{ - return p_impl->m_instrument_model.get(); -} - -std::vector<SessionModel*> ApplicationModels::persistent_models() const -{ - return p_impl->persistent_models(); -} - -//! Return vector of all models of our application. - -std::vector<SessionModel*> ApplicationModels::application_models() const -{ - return p_impl->application_models(); -} - -} // namespace gui2 diff --git a/gui2/model/applicationmodels.h b/gui2/model/applicationmodels.h deleted file mode 100644 index 3b862b9215b..00000000000 --- a/gui2/model/applicationmodels.h +++ /dev/null @@ -1,61 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/model/applicationmodels.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_MODEL_APPLICATIONMODELS_H -#define BORNAGAIN_GUI2_MODEL_APPLICATIONMODELS_H - -#include "darefl_export.h" -#include "mvvm/interfaces/applicationmodelsinterface.h" -#include <memory> - -namespace ModelView { -class SessionModel; -} - -namespace gui2 { - -class MaterialModel; -class SampleModel; -class SLDElementModel; -class SLDElementController; -class JobModel; -class ExperimentalDataModel; -class InstrumentModel; - -//! Main class to holds all models of GUI session. - -class DAREFLCORE_EXPORT ApplicationModels : public ModelView::ApplicationModelsInterface { -public: - ApplicationModels(); - ~ApplicationModels(); - - MaterialModel* materialModel(); - SampleModel* sampleModel(); - SLDElementModel* sldViewModel(); - JobModel* jobModel(); - ExperimentalDataModel* experimentalDataModel(); - InstrumentModel* instrumentModel(); - - std::vector<ModelView::SessionModel*> persistent_models() const override; - - std::vector<ModelView::SessionModel*> application_models() const; - -private: - struct ApplicationModelsImpl; - std::unique_ptr<ApplicationModelsImpl> p_impl; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_MODEL_APPLICATIONMODELS_H diff --git a/gui2/model/experimentaldatacontroller.cpp b/gui2/model/experimentaldatacontroller.cpp deleted file mode 100644 index 384e82b14d7..00000000000 --- a/gui2/model/experimentaldatacontroller.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/model/experimentaldatacontroller.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/model/experimentaldatacontroller.h" -#include "gui2/model/experimentaldataitems.h" -#include "gui2/model/experimentaldatamodel.h" -#include "gui2/model/instrumentitems.h" -#include "gui2/model/instrumentmodel.h" -#include "gui2/model/modelutils.h" -#include "mvvm/model/externalproperty.h" -#include "mvvm/model/modelutils.h" - -namespace gui2 { - -ExperimentalDataController::ExperimentalDataController(ExperimentalDataModel* data_model, - InstrumentModel* instrument_model) - : ModelListener(data_model), m_instrument_model(instrument_model) -{ - setOnDataChange([this](auto, auto) { update_all(); }); - setOnItemInserted([this](auto, auto) { update_all(); }); - setOnItemRemoved([this](auto, auto) { update_all(); }); - setOnModelReset([this](auto) { update_all(); }); - - update_all(); -} - -//! Updates all material properties in LayerItems to get new material colors and labels. - -void ExperimentalDataController::update_all() -{ - for (auto scan : ModelView::Utils::FindItems<ExperimentalScanItem>(m_instrument_model)) { - auto property = - scan->property<ModelView::ExternalProperty>(ExperimentalScanItem::P_IMPORTED_DATA); - auto updated = - Utils::FindProperty(Utils::CreateGraphProperties(model()), property.identifier()); - if (property != updated) - scan->setProperty(ExperimentalScanItem::P_IMPORTED_DATA, updated); - } -} - -} // namespace gui2 diff --git a/gui2/model/experimentaldatacontroller.h b/gui2/model/experimentaldatacontroller.h deleted file mode 100644 index d7769cb687c..00000000000 --- a/gui2/model/experimentaldatacontroller.h +++ /dev/null @@ -1,45 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/model/experimentaldatacontroller.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_MODEL_EXPERIMENTALDATACONTROLLER_H -#define BORNAGAIN_GUI2_MODEL_EXPERIMENTALDATACONTROLLER_H - -#include "darefl_export.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/signals/modellistener.h" - -namespace gui2 { - -class InstrumentModel; -class ExperimentalDataModel; - -//! Listens for all changes in ExperimentalDataModel and updates properties in InstrumentModel. -//! Main task is to update links of ExperimentalScanItem to particular imported graph, when -//! ExperimentalDataModel is changing. - -class DAREFLCORE_EXPORT ExperimentalDataController - : public ModelView::ModelListener<ExperimentalDataModel> { -public: - ExperimentalDataController(ExperimentalDataModel* data_model, - InstrumentModel* instrument_model); - -private: - void update_all(); - - InstrumentModel* m_instrument_model{nullptr}; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_MODEL_EXPERIMENTALDATACONTROLLER_H diff --git a/gui2/model/experimentaldataitems.cpp b/gui2/model/experimentaldataitems.cpp deleted file mode 100644 index 4838a6c56d1..00000000000 --- a/gui2/model/experimentaldataitems.cpp +++ /dev/null @@ -1,55 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/model/experimentaldataitems.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/model/experimentaldataitems.h" -#include "gui2/model/item_constants.h" -#include "mvvm/standarditems/axisitems.h" -#include "mvvm/standarditems/data1ditem.h" - -using namespace ModelView; - -namespace gui2 { - -CanvasItem::CanvasItem() : GraphViewportItem(GUI::Constants::CanvasItemType) -{ - yAxis()->setProperty(ViewportAxisItem::P_IS_LOG, true); - setData(std::string("")); -} - -std::pair<double, double> CanvasItem::data_yaxis_range() const -{ - auto [ymin, ymax] = GraphViewportItem::data_yaxis_range(); - return {ymin, ymax * 2.0}; -} - -CanvasContainerItem::CanvasContainerItem() : ContainerItem(GUI::Constants::CanvasContainerItemType) -{ -} - -std::vector<CanvasItem*> CanvasContainerItem::canvasItems() const -{ - return items<CanvasItem>(T_ITEMS); -} - -ExperimentalDataContainerItem::ExperimentalDataContainerItem() - : ContainerItem(GUI::Constants::ExperimentalDataContainerItemType) -{ -} - -std::vector<Data1DItem*> ExperimentalDataContainerItem::dataItems() const -{ - return items<ModelView::Data1DItem>(T_ITEMS); -} - -} // namespace gui2 diff --git a/gui2/model/experimentaldataitems.h b/gui2/model/experimentaldataitems.h deleted file mode 100644 index 47a3549a56d..00000000000 --- a/gui2/model/experimentaldataitems.h +++ /dev/null @@ -1,64 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/model/experimentaldataitems.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_MODEL_EXPERIMENTALDATAITEMS_H -#define BORNAGAIN_GUI2_MODEL_EXPERIMENTALDATAITEMS_H - -#include "darefl_export.h" -#include "mvvm/standarditems/containeritem.h" -#include "mvvm/standarditems/graphviewportitem.h" - -namespace ModelView { -class Data1DItem; -} - -namespace gui2 { - -//! Holds a collection of GraphItem's for simultaneous plotting, as well as all information -//! related to plotting properties. Used in the context of importing of 1D data. Serves as an input -//! for GraphCanvas widget. - -class DAREFLCORE_EXPORT CanvasItem : public ModelView::GraphViewportItem { -public: - CanvasItem(); - -protected: - std::pair<double, double> data_yaxis_range() const override; -}; - -//! Holds a collection of CanvasItem. -//! Used in the context of importing of 1D data, when user groups different GraphItem's -//! on different canvas for later plotting. - -class DAREFLCORE_EXPORT CanvasContainerItem : public ModelView::ContainerItem { -public: - CanvasContainerItem(); - - std::vector<CanvasItem*> canvasItems() const; -}; - -//! Holds a collection of Data1DItem's with raw data as imported by the user. -//! The order of items in the collection chronologically corresponds to user activity. -//! All other plotting entities (GraphItems) are linked to data items in this container. - -class DAREFLCORE_EXPORT ExperimentalDataContainerItem : public ModelView::ContainerItem { -public: - ExperimentalDataContainerItem(); - - std::vector<ModelView::Data1DItem*> dataItems() const; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_MODEL_EXPERIMENTALDATAITEMS_H diff --git a/gui2/model/experimentaldatamodel.cpp b/gui2/model/experimentaldatamodel.cpp deleted file mode 100644 index e11a3bf517e..00000000000 --- a/gui2/model/experimentaldatamodel.cpp +++ /dev/null @@ -1,139 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/model/experimentaldatamodel.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/model/experimentaldatamodel.h" -#include "gui2/importdataview/graphimportdata.h" -#include "gui2/model/experimentaldataitems.h" - -#include "mvvm/model/itemcatalogue.h" -#include "mvvm/model/modelutils.h" -#include "mvvm/standarditems/axisitems.h" -#include "mvvm/standarditems/containeritem.h" -#include "mvvm/standarditems/data1ditem.h" -#include "mvvm/standarditems/graphitem.h" -#include "mvvm/standarditems/graphviewportitem.h" - -#include <algorithm> -#include <cmath> - -using namespace ModelView; - -namespace { - -std::unique_ptr<ItemCatalogue> CreateItemCatalogue() -{ - auto result = std::make_unique<ModelView::ItemCatalogue>(); - result->registerItem<gui2::CanvasItem>(); - result->registerItem<gui2::CanvasContainerItem>(); - result->registerItem<gui2::ExperimentalDataContainerItem>(); - return result; -} - -} // namespace - -namespace gui2 { - -ExperimentalDataModel::ExperimentalDataModel(std::shared_ptr<ItemPool> pool) - : SessionModel("ExperimentalDataModel", pool) - -{ - init_model(); -} - -//! Returns the canvas container of the model. - -CanvasContainerItem* ExperimentalDataModel::canvasContainer() const -{ - return topItem<CanvasContainerItem>(); -} - -//! Returns the data container of the model. - -ExperimentalDataContainerItem* ExperimentalDataModel::dataContainer() const -{ - return topItem<ExperimentalDataContainerItem>(); -} - -CanvasItem* ExperimentalDataModel::addCanvas() -{ - return insertItem<CanvasItem>(canvasContainer()); -} - -//! Adds graph to 'target_canvas' and returns the result. -//! Internally add Data1DItem object to ExperimentalDataContainerItem, -//! and set it to GraphItem. - -ModelView::GraphItem* ExperimentalDataModel::addGraph(const GraphImportData& graph_data, - CanvasItem& target_canvas) -{ - auto result = insertItem<GraphItem>(&target_canvas); - - auto data = insertItem<Data1DItem>(dataContainer()); - data->setAxis<PointwiseAxisItem>(graph_data.bin_centers); - data->setValues(graph_data.bin_values); - result->setDataItem(data); - - result->setData(graph_data.graph_description); - - return result; -} - -//! Remove graph from the model. Underlying DataItem will be removed too. - -void ExperimentalDataModel::removeGraph(GraphItem& graph) -{ - auto dataItem = graph.dataItem(); - - removeItem(graph.parent(), graph.tagRow()); - removeItem(dataItem->parent(), dataItem->tagRow()); -} - -//! Remove canvas with all its graphs. - -void ExperimentalDataModel::removeCanvas(CanvasItem& canvas) -{ - // Remove graph first. Use special method for that, since we want to remove underlying items. - for (auto graph : canvas.graphItems()) - removeGraph(*graph); - removeItem(canvas.parent(), canvas.tagRow()); -} - -//! Merge canvas from the vector. All graphs will be the children of the first canvas in the vector. -//! All other canvas will be emptied and deleted. - -void ExperimentalDataModel::mergeCanvases(const std::vector<CanvasItem*>& canvases) -{ - if (canvases.size() <= 1) - return; - - CanvasItem* target = canvases.front(); - for (auto it = std::next(canvases.begin()); it < canvases.end(); ++it) { - CanvasItem* source = (*it); - for (auto graph : source->graphItems()) - moveItem(graph, target, {"", -1}); - removeItem(source->parent(), source->tagRow()); - } -} - -void ExperimentalDataModel::init_model() -{ - setItemCatalogue(CreateItemCatalogue()); - - insertItem<ExperimentalDataContainerItem>(rootItem()); - insertItem<CanvasContainerItem>(rootItem()); - - setUndoRedoEnabled(true); -} - -} // namespace gui2 diff --git a/gui2/model/experimentaldatamodel.h b/gui2/model/experimentaldatamodel.h deleted file mode 100644 index dd0529be837..00000000000 --- a/gui2/model/experimentaldatamodel.h +++ /dev/null @@ -1,61 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/model/experimentaldatamodel.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_MODEL_EXPERIMENTALDATAMODEL_H -#define BORNAGAIN_GUI2_MODEL_EXPERIMENTALDATAMODEL_H - -#include "darefl_export.h" -#include "mvvm/model/sessionmodel.h" -#include <vector> - -namespace ModelView { -class SessionItem; -class GraphItem; -class GraphViewportItem; -} // namespace ModelView - -namespace gui2 { - -class CanvasContainerItem; -class ExperimentalDataContainerItem; -class CanvasItem; -struct GraphImportData; - -//! The model to store imported reflectometry data. - -class DAREFLCORE_EXPORT ExperimentalDataModel : public ModelView::SessionModel { -public: - ExperimentalDataModel(std::shared_ptr<ModelView::ItemPool> pool = {}); - - CanvasContainerItem* canvasContainer() const; - - ExperimentalDataContainerItem* dataContainer() const; - - CanvasItem* addCanvas(); - - ModelView::GraphItem* addGraph(const GraphImportData& graph_data, CanvasItem& target_canvas); - - void removeGraph(ModelView::GraphItem& graph); - - void removeCanvas(CanvasItem& canvas); - - void mergeCanvases(const std::vector<CanvasItem*>& canvases); - -private: - void init_model(); -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_MODEL_EXPERIMENTALDATAMODEL_H diff --git a/gui2/model/instrumentitems.cpp b/gui2/model/instrumentitems.cpp deleted file mode 100644 index a0990a06256..00000000000 --- a/gui2/model/instrumentitems.cpp +++ /dev/null @@ -1,134 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/model/instrumentitems.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/model/instrumentitems.h" -#include "gui2/model/item_constants.h" -#include "gui2/model/modelutils.h" -#include "mvvm/model/externalproperty.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/standarditems/axisitems.h" -#include "mvvm/standarditems/graphitem.h" -#include <QColor> - -using namespace ModelView; - -namespace gui2 { - -BasicSpecularScanItem::BasicSpecularScanItem(const std::string& model_type) - : CompoundItem(model_type) -{ -} - -// ---------------------------------------------------------------------------- - -QSpecScanItem::QSpecScanItem() : BasicSpecularScanItem(GUI::Constants::QSpecScanItemType) -{ - addProperty(P_NBINS, 500)->setDisplayName("Nbins"); - addProperty(P_QMIN, 0.0)->setDisplayName("Qmin"); - addProperty(P_QMAX, 1.0)->setDisplayName("Qmax"); -} - -std::vector<double> QSpecScanItem::qScanValues() const -{ - int nbins = property<int>(P_NBINS); - double qmin = property<double>(P_QMIN); - double qmax = property<double>(P_QMAX); - return FixedBinAxisItem::create(nbins, qmin, qmax)->binCenters(); -} - -// ---------------------------------------------------------------------------- - -ExperimentalScanItem::ExperimentalScanItem() - : BasicSpecularScanItem(GUI::Constants::ExperimentalScanItemType) -{ - addProperty(P_IMPORTED_DATA, ExternalProperty::undefined())->setDisplayName("Graph"); -} - -void ExperimentalScanItem::setGraphItem(GraphItem* graph) -{ - setProperty(P_IMPORTED_DATA, Utils::CreateProperty(graph)); -} - -GraphItem* ExperimentalScanItem::graphItem() const -{ - if (model()) { - auto graph_id = property<ExternalProperty>(P_IMPORTED_DATA).identifier(); - return dynamic_cast<GraphItem*>(model()->findItem(graph_id)); - } - return nullptr; -} - -std::vector<double> ExperimentalScanItem::qScanValues() const -{ - return graphItem() ? graphItem()->binCenters() : std::vector<double>(); -} - -// ---------------------------------------------------------------------------- - -SpecularScanGroupItem::SpecularScanGroupItem() - : GroupItem(GUI::Constants::SpecularScanGroupItemType) -{ - registerItem<QSpecScanItem>("Q-scan", /*make_selected*/ true); - registerItem<ExperimentalScanItem>("Based on data"); - init_group(); -} - -// ---------------------------------------------------------------------------- - -SpecularBeamItem::SpecularBeamItem() : CompoundItem(GUI::Constants::SpecularBeamItemType) -{ - addProperty(P_INTENSITY, 1.0)->setDisplayName("Intensity"); - addProperty<SpecularScanGroupItem>(P_SCAN_GROUP)->setDisplayName("Specular scan type"); -} - -std::vector<double> SpecularBeamItem::qScanValues() const -{ - auto scan_group = item<SpecularScanGroupItem>(P_SCAN_GROUP); - if (auto scanItem = dynamic_cast<const BasicSpecularScanItem*>(scan_group->currentItem()); - scanItem) - return scanItem->qScanValues(); - return {}; -} - -double SpecularBeamItem::intensity() const -{ - return property<double>(P_INTENSITY); -} - -//! Returns corresponding experimental graph. If current setup is based on simple q-scan, will -//! return nullptr. - -GraphItem* SpecularBeamItem::experimentalGraphItem() const -{ - auto scan_group = item<SpecularScanGroupItem>(P_SCAN_GROUP); - if (auto scanItem = dynamic_cast<const ExperimentalScanItem*>(scan_group->currentItem()); - scanItem) - return scanItem->graphItem(); - return nullptr; -} - -// ---------------------------------------------------------------------------- - -SpecularInstrumentItem::SpecularInstrumentItem() - : CompoundItem(GUI::Constants::SpecularInstrumentItemType) -{ - addProperty<SpecularBeamItem>(P_BEAM); -} - -SpecularBeamItem* SpecularInstrumentItem::beamItem() const -{ - return item<SpecularBeamItem>(P_BEAM); -} - -} // namespace gui2 diff --git a/gui2/model/instrumentitems.h b/gui2/model/instrumentitems.h deleted file mode 100644 index 7e39a9290cf..00000000000 --- a/gui2/model/instrumentitems.h +++ /dev/null @@ -1,101 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/model/instrumentitems.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_MODEL_INSTRUMENTITEMS_H -#define BORNAGAIN_GUI2_MODEL_INSTRUMENTITEMS_H - -//! @file gui2/model/instrumentitems.h -//! Collection of items to construct specular instrument. - -#include "darefl_export.h" -#include "mvvm/model/compounditem.h" -#include "mvvm/model/groupitem.h" - -namespace ModelView { -class GraphItem; -} - -namespace gui2 { - -//! Represents base type for beam scan parameters. - -class DAREFLCORE_EXPORT BasicSpecularScanItem : public ModelView::CompoundItem { -public: - BasicSpecularScanItem(const std::string& model_type); - virtual std::vector<double> qScanValues() const = 0; -}; - -//! Represents Q-space specular scan with fixed bin size. - -class DAREFLCORE_EXPORT QSpecScanItem : public BasicSpecularScanItem { -public: - static inline const std::string P_NBINS = "P_NBINS"; - static inline const std::string P_QMIN = "P_QMIN"; - static inline const std::string P_QMAX = "P_QMAX"; - QSpecScanItem(); - - std::vector<double> qScanValues() const override; -}; - -//! Represents scan according to imported experimental data. - -class DAREFLCORE_EXPORT ExperimentalScanItem : public BasicSpecularScanItem { -public: - static inline const std::string P_IMPORTED_DATA = "P_IMPORTED_DATA"; - ExperimentalScanItem(); - - void setGraphItem(ModelView::GraphItem* graph); - - ModelView::GraphItem* graphItem() const; - - std::vector<double> qScanValues() const override; -}; - -//! Represent selection of possible specular scans. - -class DAREFLCORE_EXPORT SpecularScanGroupItem : public ModelView::GroupItem { -public: - SpecularScanGroupItem(); -}; - -//! Represents specular beam, contains settings of scan parameters. - -class DAREFLCORE_EXPORT SpecularBeamItem : public ModelView::CompoundItem { -public: - static inline const std::string P_INTENSITY = "P_INTENSITY"; - static inline const std::string P_SCAN_GROUP = "P_SCAN_GROUP"; - - SpecularBeamItem(); - - std::vector<double> qScanValues() const; - - double intensity() const; - - ModelView::GraphItem* experimentalGraphItem() const; -}; - -//! Represents specular instrument. - -class DAREFLCORE_EXPORT SpecularInstrumentItem : public ModelView::CompoundItem { -public: - static inline const std::string P_BEAM = "P_BEAM"; - - SpecularInstrumentItem(); - - SpecularBeamItem* beamItem() const; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_MODEL_INSTRUMENTITEMS_H diff --git a/gui2/model/instrumentmodel.cpp b/gui2/model/instrumentmodel.cpp deleted file mode 100644 index 299f378e439..00000000000 --- a/gui2/model/instrumentmodel.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/model/instrumentmodel.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/model/instrumentmodel.h" -#include "gui2/model/instrumentitems.h" -#include "mvvm/model/itemcatalogue.h" - -using namespace ModelView; - -namespace { - -std::unique_ptr<ItemCatalogue> CreateItemCatalogue() -{ - auto result = std::make_unique<ModelView::ItemCatalogue>(); - result->registerItem<gui2::SpecularInstrumentItem>(); - result->registerItem<gui2::SpecularBeamItem>(); - result->registerItem<gui2::SpecularScanGroupItem>(); - result->registerItem<gui2::QSpecScanItem>(); - result->registerItem<gui2::ExperimentalScanItem>(); - return result; -} - -} // namespace - -namespace gui2 { - -InstrumentModel::InstrumentModel(std::shared_ptr<ItemPool> pool) - : ModelView::SessionModel("InstrumentModel", pool) -{ - setItemCatalogue(CreateItemCatalogue()); - insertItem<SpecularInstrumentItem>(); -} - -} // namespace gui2 diff --git a/gui2/model/instrumentmodel.h b/gui2/model/instrumentmodel.h deleted file mode 100644 index d9f545338a8..00000000000 --- a/gui2/model/instrumentmodel.h +++ /dev/null @@ -1,32 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/model/instrumentmodel.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_MODEL_INSTRUMENTMODEL_H -#define BORNAGAIN_GUI2_MODEL_INSTRUMENTMODEL_H - -#include "darefl_export.h" -#include "mvvm/model/sessionmodel.h" - -namespace gui2 { - -//! Model to store specular instruments settings. - -class DAREFLCORE_EXPORT InstrumentModel : public ModelView::SessionModel { -public: - InstrumentModel(std::shared_ptr<ModelView::ItemPool> pool = {}); -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_MODEL_INSTRUMENTMODEL_H diff --git a/gui2/model/item_constants.h b/gui2/model/item_constants.h deleted file mode 100644 index 720c6f1ace1..00000000000 --- a/gui2/model/item_constants.h +++ /dev/null @@ -1,40 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/model/item_constants.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_MODEL_ITEM_CONSTANTS_H -#define BORNAGAIN_GUI2_MODEL_ITEM_CONSTANTS_H - -#include <string> - -namespace gui2::Constants { - -const std::string CanvasContainerItemType = "CanvasContainer"; -const std::string CanvasItemType = "Canvas"; -const std::string ExperimentalDataContainerItemType = "ExperimentalDataContainer"; -const std::string ExperimentalScanItemType = "ExperimentalScan"; -const std::string JobItemType = "Job"; -const std::string LayerItemType = "Layer"; -const std::string MaterialContainerItemType = "MaterialContainer"; -const std::string MultiLayerItemType = "MultiLayer"; -const std::string QSpecScanItemType = "QSpecScan"; -const std::string RoughnessItemType = "Roughness"; -const std::string SLDCanvasItemType = "SLDCanvas"; -const std::string SLDMaterialItemType = "SLDMaterial"; -const std::string SpecularBeamItemType = "SpecularBeam"; -const std::string SpecularInstrumentItemType = "SpecularInstrument"; -const std::string SpecularScanGroupItemType = "SpecularScanGroup"; - -} // namespace gui2::Constants - -#endif // BORNAGAIN_GUI2_MODEL_ITEM_CONSTANTS_H diff --git a/gui2/model/jobitem.cpp b/gui2/model/jobitem.cpp deleted file mode 100644 index 2ca4e15f172..00000000000 --- a/gui2/model/jobitem.cpp +++ /dev/null @@ -1,227 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/model/jobitem.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/model/jobitem.h" -#include "gui2/model/experimentaldataitems.h" -#include "gui2/model/item_constants.h" -#include "gui2/model/jobmodel.h" -#include "gui2/model/modelutils.h" -#include "mvvm/model/modelutils.h" -#include "mvvm/standarditems/axisitems.h" -#include "mvvm/standarditems/data1ditem.h" -#include "mvvm/standarditems/graphitem.h" -#include "mvvm/standarditems/graphviewportitem.h" -#include <QColor> - -using namespace ModelView; - -namespace gui2 { - -namespace { -const int row_sim_graph = 0; -const int row_reference_graph = 1; - -GraphItem* create_reference_graph(JobItem* item) -{ - auto model = item->model(); - return model->insertItem<GraphItem>(item->specularViewport(), - {ViewportItem::T_ITEMS, row_reference_graph}); -} - -GraphItem* create_difference_graph(JobItem* item) -{ - auto model = item->model(); - return model->insertItem<GraphItem>(item->diffViewport(), {ViewportItem::T_ITEMS, 0}); -} - -//! Creates viewport and data properties. Creates graph, adds data to the graph, and graph -//! to viewport. -//! TODO consider to replace it with classes, when JobItem structure becomes clear. - -template <typename Data, typename Graph, typename Viewport> -void initViewport(CompoundItem* item, const std::string& data_name, - const std::string& viewport_name) -{ - auto data = item->addProperty<Data>(data_name); - auto viewport = item->addProperty<Viewport>(viewport_name); - auto graph = std::make_unique<GraphItem>(); - graph->setDataItem(data); - viewport->insertItem(graph.release(), {ViewportItem::T_ITEMS, 0}); -} - -} // namespace - -SLDCanvasItem::SLDCanvasItem() : GraphViewportItem(GUI::Constants::SLDCanvasItemType) {} - -std::pair<double, double> SLDCanvasItem::data_yaxis_range() const -{ - auto [ymin, ymax] = GraphViewportItem::data_yaxis_range(); - double range = ymax - ymin; - return {ymin - range / 10.0, ymax + range / 10.0}; -} - -// ---------------------------------------------------------------------------- - -JobItem::JobItem() : ModelView::CompoundItem(GUI::Constants::JobItemType) -{ - setup_sld_viewport(); - setup_specular_viewport(); - setup_diff_viewport(); -} - -Data1DItem* JobItem::sldData() const -{ - return item<Data1DItem>(P_SLD_DATA); -} - -SLDCanvasItem* JobItem::sldViewport() const -{ - return item<SLDCanvasItem>(P_SLD_VIEWPORT); -} - -Data1DItem* JobItem::specularData() const -{ - return item<Data1DItem>(P_SPECULAR_DATA); -} - -CanvasItem* JobItem::specularViewport() const -{ - return item<CanvasItem>(P_SPECULAR_VIEWPORT); -} - -GraphViewportItem* JobItem::diffViewport() const -{ - return item<GraphViewportItem>(P_DIFF_VIEWPORT); -} - -//! Updates reference graph in specular viewport from external graph. -//! External graph represents user imported data, it will be used to -//! is comming from another viewport (i.e. containing user imported data), -//! and it is used - -void JobItem::updateReferenceGraph(const GraphItem* graph) -{ - if (graph) { - setupReferenceGraphFrom(graph); - setupDifferenceGraphFrom(graph); - } else { - removeReferenceGraph(); - removeDifferenceGraph(); - } -} - -//! Updates values stored in Data1DItem representing the difference between specular and reference -//! graphs. - -void JobItem::updateDifferenceData() -{ - if (auto reference_graph = referenceGraph(); reference_graph) { - const auto reference_data = reference_graph->dataItem(); - const auto specular_data = specularData(); - auto diff_data = differenceData(); - Utils::SetDifference(specular_data, reference_data, diff_data); - } -} - -Data1DItem* JobItem::differenceData() const -{ - return item<Data1DItem>(P_DIFF_DATA); -} - -//! Returns specular graph. - -GraphItem* JobItem::specularGraph() const -{ - auto graphs = specularViewport()->graphItems(); - return graphs.size() > 0 ? graphs.at(row_sim_graph) : nullptr; -} - -//! Returns reference graph, if exists. It represents imported user data from ExperimentalScanItem. -//! Here it is stored in SpecularViewport. - -GraphItem* JobItem::referenceGraph() const -{ - auto graphs = specularViewport()->graphItems(); - return graphs.size() > 1 ? graphs.at(row_reference_graph) : nullptr; -} - -//! Returns graph representing a numeric difference between simulated and reference curve. - -GraphItem* JobItem::differenceGraph() const -{ - auto graphs = diffViewport()->graphItems(); - return graphs.size() > 0 ? graphs.at(0) : nullptr; -} - -void JobItem::setupReferenceGraphFrom(const GraphItem* graph) -{ - assert(graph); - auto reference_graph = referenceGraph() ? referenceGraph() : create_reference_graph(this); - reference_graph->setFromGraphItem(graph); -} - -void JobItem::setupDifferenceGraphFrom(const GraphItem* /*graph*/) -{ - // FIXME rename unused graph - if (!differenceGraph()) { - create_difference_graph(this); - differenceGraph()->setDataItem(differenceData()); - } - - updateDifferenceData(); -} - -//! Removes reference graph from specular viewport. - -void JobItem::removeReferenceGraph() -{ - if (auto graph = referenceGraph(); graph) - ModelView::Utils::DeleteItemFromModel(graph); -} - -//! Removes difference graph from specular viewport. - -void JobItem::removeDifferenceGraph() -{ - if (auto graph = differenceGraph(); graph) - ModelView::Utils::DeleteItemFromModel(graph); -} - -void JobItem::setup_sld_viewport() -{ - initViewport<Data1DItem, GraphItem, SLDCanvasItem>(this, P_SLD_DATA, P_SLD_VIEWPORT); - sldData()->setAxis<FixedBinAxisItem>(1, 0.0, 1.0); -} - -//! Setups a specular viewport together with a single graph in it and corresponding data item. -//! Intended to store simulated specular curve, and possibly reference graphs. - -void JobItem::setup_specular_viewport() -{ - initViewport<Data1DItem, GraphItem, CanvasItem>(this, P_SPECULAR_DATA, P_SPECULAR_VIEWPORT); - auto graph = specularGraph(); - graph->setNamedColor("cornflowerblue"); - specularData()->setAxis<PointwiseAxisItem>(std::vector<double>()); -} - -//! Setups viewport, difference graph, and its underlying data to show the difference between -//! simulated and reference curves. - -void JobItem::setup_diff_viewport() -{ - initViewport<Data1DItem, GraphItem, GraphViewportItem>(this, P_DIFF_DATA, P_DIFF_VIEWPORT); - differenceData()->setAxis<PointwiseAxisItem>(std::vector<double>()); -} - -} // namespace gui2 diff --git a/gui2/model/jobitem.h b/gui2/model/jobitem.h deleted file mode 100644 index a7ca161c053..00000000000 --- a/gui2/model/jobitem.h +++ /dev/null @@ -1,87 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/model/jobitem.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_MODEL_JOBITEM_H -#define BORNAGAIN_GUI2_MODEL_JOBITEM_H - -#include "darefl_export.h" -#include "mvvm/model/compounditem.h" -#include "mvvm/standarditems/graphviewportitem.h" - -namespace ModelView { -class Data1DItem; -class GraphViewportItem; -class GraphItem; -} // namespace ModelView - -namespace gui2 { - -class CanvasItem; - -//! Viewport intended for showing SLD profile. -//! Provides custom y-axis range. - -class DAREFLCORE_EXPORT SLDCanvasItem : public ModelView::GraphViewportItem { -public: - SLDCanvasItem(); - -protected: - std::pair<double, double> data_yaxis_range() const override; -}; - -//! Represents state of QuickSimEditor. -//! Holds results of realtime simulation, SLD profiles and difference plot. - -class DAREFLCORE_EXPORT JobItem : public ModelView::CompoundItem { -public: - static inline const std::string P_SLD_DATA = "P_SLD_DATA"; - static inline const std::string P_SLD_VIEWPORT = "P_SLD_VIEWPORT"; - static inline const std::string P_SPECULAR_DATA = "P_SPECULAR_DATA"; - static inline const std::string P_SPECULAR_VIEWPORT = "P_SPECULAR_VIEWPORT"; - static inline const std::string P_DIFF_DATA = "P_DIFF_DATA"; - static inline const std::string P_DIFF_VIEWPORT = "P_DIFF_VIEWPORT"; - - JobItem(); - - ModelView::Data1DItem* sldData() const; - SLDCanvasItem* sldViewport() const; - - ModelView::Data1DItem* specularData() const; - CanvasItem* specularViewport() const; - - ModelView::GraphViewportItem* diffViewport() const; - - void updateReferenceGraph(const ModelView::GraphItem* graph); - - void updateDifferenceData(); - -private: - ModelView::Data1DItem* differenceData() const; - ModelView::GraphItem* specularGraph() const; - ModelView::GraphItem* referenceGraph() const; - ModelView::GraphItem* differenceGraph() const; - - void setupReferenceGraphFrom(const ModelView::GraphItem* graph); - void setupDifferenceGraphFrom(const ModelView::GraphItem* graph); - void removeReferenceGraph(); - void removeDifferenceGraph(); - - void setup_sld_viewport(); - void setup_specular_viewport(); - void setup_diff_viewport(); -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_MODEL_JOBITEM_H diff --git a/gui2/model/jobmodel.cpp b/gui2/model/jobmodel.cpp deleted file mode 100644 index cf0f79838a0..00000000000 --- a/gui2/model/jobmodel.cpp +++ /dev/null @@ -1,97 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/model/jobmodel.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/model/jobmodel.h" -#include "gui2/model/experimentaldataitems.h" -#include "gui2/model/jobitem.h" -#include "gui2/quicksimeditor/quicksim_types.h" -#include "mvvm/model/itemcatalogue.h" -#include "mvvm/model/modelutils.h" -#include "mvvm/standarditems/axisitems.h" -#include "mvvm/standarditems/data1ditem.h" -#include "mvvm/standarditems/graphitem.h" -#include "mvvm/standarditems/graphviewportitem.h" - -using namespace ModelView; - -namespace gui2 { - -namespace { - -std::unique_ptr<ItemCatalogue> CreateItemCatalogue() -{ - auto result = std::make_unique<ModelView::ItemCatalogue>(); - result->registerItem<JobItem>(); - result->registerItem<CanvasItem>(); - result->registerItem<SLDCanvasItem>(); - return result; -} - -} // namespace - -JobModel::JobModel(std::shared_ptr<ItemPool> pool) : SessionModel("JobModel", pool) -{ - setItemCatalogue(CreateItemCatalogue()); - insertItem<JobItem>(); -} - -GraphViewportItem* JobModel::sldViewport() const -{ - return jobItem()->sldViewport(); -} - -CanvasItem* JobModel::specularViewport() const -{ - return jobItem()->specularViewport(); -} - -GraphViewportItem* JobModel::diffViewport() const -{ - return jobItem()->diffViewport(); -} - -void JobModel::updateReferenceGraph(const ModelView::GraphItem* graph) -{ - jobItem()->updateReferenceGraph(graph); -} - -//! Updates specular data in JobItem from simulation results. - -void JobModel::updateSpecularData(const SimulationResult& data) -{ - auto specularData = jobItem()->specularData(); - specularData->item<PointwiseAxisItem>(Data1DItem::T_AXIS)->setParameters(data.qvalues); - specularData->setValues(data.amplitudes); - - // updating difference graph - jobItem()->updateDifferenceData(); -} - -//! Updates SLD profile data. - -void JobModel::updateSLDProfile(const SLDProfile& data) -{ - auto sldData = jobItem()->sldData(); - // sldData->setAxis<FixedBinAxisItem>(data.sld_real_values.size(), data.zmin, data.zmax); - sldData->item<FixedBinAxisItem>(Data1DItem::T_AXIS) - ->setParameters(data.sld_real_values.size(), data.zmin, data.zmax); - sldData->setValues(data.sld_real_values); -} - -JobItem* JobModel::jobItem() const -{ - return Utils::TopItem<JobItem>(this); -} - -} // namespace gui2 diff --git a/gui2/model/jobmodel.h b/gui2/model/jobmodel.h deleted file mode 100644 index 929c385b51e..00000000000 --- a/gui2/model/jobmodel.h +++ /dev/null @@ -1,58 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/model/jobmodel.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_MODEL_JOBMODEL_H -#define BORNAGAIN_GUI2_MODEL_JOBMODEL_H - -#include "darefl_export.h" -#include "mvvm/model/sessionmodel.h" - -namespace ModelView { -class GraphViewportItem; -class GraphItem; -} // namespace ModelView - -namespace gui2 { - -class JobItem; -class CanvasItem; -struct SimulationResult; -struct SLDProfile; - -//! The model to store results of (possibly) multiple reflectometry simulation, and all -//! viewports, representing various graphs in QuickSimEditor widgets. - -class DAREFLCORE_EXPORT JobModel : public ModelView::SessionModel { -public: - JobModel(std::shared_ptr<ModelView::ItemPool> pool = {}); - - ModelView::GraphViewportItem* sldViewport() const; - - CanvasItem* specularViewport() const; - - ModelView::GraphViewportItem* diffViewport() const; - - void updateReferenceGraph(const ModelView::GraphItem* graph); - - void updateSpecularData(const SimulationResult& data); - - void updateSLDProfile(const SLDProfile& data); - -private: - JobItem* jobItem() const; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_MODEL_JOBMODEL_H diff --git a/gui2/model/materialitems.cpp b/gui2/model/materialitems.cpp deleted file mode 100644 index 36a6bd2aa51..00000000000 --- a/gui2/model/materialitems.cpp +++ /dev/null @@ -1,82 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/model/materialitems.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/model/materialitems.h" -#include "gui2/model/item_constants.h" -#include "mvvm/model/externalproperty.h" -#include "mvvm/model/itemutils.h" -#include <QColor> - -using namespace ModelView; - -namespace gui2 { - -MaterialContainerItem::MaterialContainerItem() - : ModelView::CompoundItem(GUI::Constants::MaterialContainerItemType) -{ - registerTag(TagInfo::universalTag(T_MATERIALS, {GUI::Constants::SLDMaterialItemType}), - /*set_as_default*/ true); -} - -// ---------------------------------------------------------------------------- - -MaterialBaseItem::MaterialBaseItem(const std::string& model_type) - : ModelView::CompoundItem(model_type) -{ - addProperty(P_COLOR, QColor(Qt::green))->setDisplayName("Color"); - addProperty(P_NAME, "Unnamed")->setDisplayName("Name"); -} - -//! Returns ExternalProperty representing this material. - -ModelView::ExternalProperty MaterialBaseItem::external_property() const -{ - QColor color = Utils::HasTag(*this, P_COLOR) ? property<QColor>(P_COLOR) : QColor(Qt::red); - std::string name = Utils::HasTag(*this, P_NAME) ? property<std::string>(P_NAME) : ""; - return ModelView::ExternalProperty(name, color, identifier()); -} - -/*! Creates mag. field-related properties. - * Should be called from descendants' constructors in order - * to preserve view-oriented property sequence. - * The same can be achieved with a proper - * ModelView::RowStrategyInterface::constructRow - * implementation. - */ -void MaterialBaseItem::init_magnetic_field() -{ - addProperty(P_H_X, 0.0)->setDisplayName("H, x"); - addProperty(P_H_Y, 0.0)->setDisplayName("H, y"); - addProperty(P_H_Z, 0.0)->setDisplayName("H, z"); -} - -// ---------------------------------------------------------------------------- - -SLDMaterialItem::SLDMaterialItem() : MaterialBaseItem(GUI::Constants::SLDMaterialItemType) -{ - addProperty(P_SLD_REAL, 1e-06)->setDisplayName("Re(SLD)")->setLimits(RealLimits::limitless()); - addProperty(P_SLD_IMAG, 1e-08)->setDisplayName("Im(SLD)")->setLimits(RealLimits::limitless()); - init_magnetic_field(); -} - -void SLDMaterialItem::set_properties(const std::string& name, const QColor& color, double real, - double imag) -{ - setProperty(P_NAME, name); - setProperty(P_COLOR, color); - setProperty(P_SLD_REAL, real); - setProperty(P_SLD_IMAG, imag); -} - -} // namespace gui2 diff --git a/gui2/model/materialitems.h b/gui2/model/materialitems.h deleted file mode 100644 index fb3f7673055..00000000000 --- a/gui2/model/materialitems.h +++ /dev/null @@ -1,69 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/model/materialitems.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_MODEL_MATERIALITEMS_H -#define BORNAGAIN_GUI2_MODEL_MATERIALITEMS_H - -//! materialitems.h -//! Collection of materials to populate MaterialModel. - -#include "darefl_export.h" -#include "mvvm/model/compounditem.h" - -namespace ModelView { -class ExternalProperty; -} - -namespace gui2 { - -//! Container to hold MaterialItems. - -class DAREFLCORE_EXPORT MaterialContainerItem : public ModelView::CompoundItem { -public: - static inline const std::string T_MATERIALS = "T_MATERIALS"; - MaterialContainerItem(); -}; - -//! Base class with all materials with name and color defined. - -class DAREFLCORE_EXPORT MaterialBaseItem : public ModelView::CompoundItem { -public: - static inline const std::string P_COLOR = "P_COLOR"; - static inline const std::string P_NAME = "P_NAME"; - static inline const std::string P_H_X = "P_H_X"; - static inline const std::string P_H_Y = "P_H_Y"; - static inline const std::string P_H_Z = "P_H_Z"; - - ModelView::ExternalProperty external_property() const; - -protected: - MaterialBaseItem(const std::string& model_type); - void init_magnetic_field(); -}; - -//! Represents material based on scattering length density. - -class DAREFLCORE_EXPORT SLDMaterialItem : public MaterialBaseItem { -public: - static inline const std::string P_SLD_REAL = "P_SLD_REAL"; - static inline const std::string P_SLD_IMAG = "P_SLD_IMAG"; - - SLDMaterialItem(); - - void set_properties(const std::string& name, const QColor& color, double real, double imag); -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_MODEL_MATERIALITEMS_H diff --git a/gui2/model/materialmodel.cpp b/gui2/model/materialmodel.cpp deleted file mode 100644 index a9b071c3fef..00000000000 --- a/gui2/model/materialmodel.cpp +++ /dev/null @@ -1,146 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/model/materialmodel.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/model/materialmodel.h" -#include "gui2/model/item_constants.h" -#include "gui2/model/materialitems.h" -#include "mvvm/model/externalproperty.h" -#include "mvvm/model/itemcatalogue.h" -#include "mvvm/widgets/widgetutils.h" -#include <QColor> - -using namespace ModelView; - -namespace { - -std::unique_ptr<ItemCatalogue> CreateItemCatalogue() -{ - auto result = std::make_unique<ModelView::ItemCatalogue>(); - result->registerItem<gui2::MaterialContainerItem>(); - result->registerItem<gui2::SLDMaterialItem>(); - return result; -} - -const double rho_si = 2.0704e-06; -const double mu_si = 5.96e-07; - -const double rho_default = 9.4245e-06; -const double mu_default = 0.0; - -const std::string air_material_name = "Air"; -const std::string substrate_material_name = "Si"; -const std::string default_material_name = "Default"; - -//! Returns map of good looking colors for standard material names. - -std::map<std::string, QColor> name_to_color_map() -{ - std::map<std::string, QColor> result = {{air_material_name, QColor(179, 242, 255)}, - {substrate_material_name, QColor(205, 102, 0)}, - {default_material_name, QColor(Qt::green)}}; - return result; -} - -QColor suggestMaterialColor(const std::string& name) -{ - static auto color_map = name_to_color_map(); - auto it = color_map.find(name); - return it != color_map.end() ? it->second : Utils::RandomColor(); -} - -} // namespace - -namespace gui2 { - -MaterialModel::MaterialModel(std::shared_ptr<ModelView::ItemPool> pool) - : SessionModel("MaterialModel", pool) -{ - init_model(); -} - -//! Returns vector of properties representing possible choice of materials for the given container. -//! Here we assume that all materials seats in top level material containers. -//! If no container_id was given, the very first container is examined. - -// TODO Simplify and cover with unit tests. - -std::vector<ExternalProperty> MaterialModel::material_data(std::string container_id) const -{ - std::vector<ExternalProperty> result; - const auto containers = rootItem()->children(); - if (!containers.empty() && container_id.empty()) - container_id = topItem<MaterialContainerItem>()->identifier(); - - for (auto container : containers) { - if (container->identifier() != container_id) - continue; - for (auto item : container->children()) { - if (auto material = dynamic_cast<MaterialBaseItem*>(item)) - result.push_back(material->external_property()); - } - } - return result; -} - -//! Returns property from given material id. - -ExternalProperty MaterialModel::material_property(const std::string& id) -{ - for (const auto& prop : material_data()) - if (prop.identifier() == id) - return prop; - - return ExternalProperty::undefined(); -} - -//! Clones material and adds it at the bottom of MaterialContainerItem. - -MaterialBaseItem* MaterialModel::cloneMaterial(const MaterialBaseItem* item) -{ - auto tagrow = item->tagRow().next(); - return static_cast<MaterialBaseItem*>(SessionModel::copyItem(item, item->parent(), tagrow)); -} - -//! Adds default material. - -SLDMaterialItem* MaterialModel::addDefaultMaterial(const ModelView::TagRow& tagrow) -{ - auto material = insertItem<SLDMaterialItem>(materialContainer(), tagrow); - material->set_properties("Default", QColor(Qt::green), rho_default, mu_default); - return material; -} - -//! Populates the model with some default content. - -void MaterialModel::init_model() -{ - setItemCatalogue(CreateItemCatalogue()); - - auto container = insertItem<MaterialContainerItem>(); - auto material = insertItem<SLDMaterialItem>(container); - material->set_properties(air_material_name, suggestMaterialColor(air_material_name), 0.0, 0.0); - material = insertItem<SLDMaterialItem>(container); - material->set_properties(default_material_name, suggestMaterialColor(default_material_name), - rho_default, mu_default); - material = insertItem<SLDMaterialItem>(container); - material->set_properties(substrate_material_name, suggestMaterialColor(substrate_material_name), - rho_si, mu_si); -} - -MaterialContainerItem* MaterialModel::materialContainer() -{ - return topItem<MaterialContainerItem>(); -} - -} // namespace gui2 diff --git a/gui2/model/materialmodel.h b/gui2/model/materialmodel.h deleted file mode 100644 index e3baab611d7..00000000000 --- a/gui2/model/materialmodel.h +++ /dev/null @@ -1,54 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/model/materialmodel.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_MODEL_MATERIALMODEL_H -#define BORNAGAIN_GUI2_MODEL_MATERIALMODEL_H - -#include "darefl_export.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/model/tagrow.h" -#include <vector> - -namespace ModelView { -class ExternalProperty; -} - -namespace gui2 { - -class MaterialBaseItem; -class MaterialContainerItem; -class SLDMaterialItem; - -//! Model to hold MaterialItems. - -class DAREFLCORE_EXPORT MaterialModel : public ModelView::SessionModel { -public: - MaterialModel(std::shared_ptr<ModelView::ItemPool> pool = {}); - - std::vector<ModelView::ExternalProperty> material_data(std::string container_id = "") const; - - ModelView::ExternalProperty material_property(const std::string& id); - - MaterialBaseItem* cloneMaterial(const MaterialBaseItem* item); - - SLDMaterialItem* addDefaultMaterial(const ModelView::TagRow& tagrow = {}); - -private: - void init_model(); - MaterialContainerItem* materialContainer(); -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_MODEL_MATERIALMODEL_H diff --git a/gui2/model/materialpropertycontroller.cpp b/gui2/model/materialpropertycontroller.cpp deleted file mode 100644 index 0088c25043e..00000000000 --- a/gui2/model/materialpropertycontroller.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/model/materialpropertycontroller.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/model/materialpropertycontroller.h" -#include "gui2/model/materialmodel.h" -#include "gui2/model/sampleitems.h" -#include "gui2/model/samplemodel.h" -#include "mvvm/model/externalproperty.h" -#include "mvvm/model/modelutils.h" - -using namespace ModelView; - -namespace gui2 { - -MaterialPropertyController::MaterialPropertyController(MaterialModel* material_model, - SampleModel* sample_model) - : ModelListener(material_model), m_sample_model(sample_model) -{ - setOnDataChange([this](auto, auto) { update_all(); }); - setOnItemInserted([this](auto, auto) { update_all(); }); - setOnItemRemoved([this](auto, auto) { update_all(); }); - setOnModelReset([this](auto) { update_all(); }); - - update_all(); -} - -//! Updates all material properties in LayerItems to get new material colors and labels. - -void MaterialPropertyController::update_all() -{ - for (auto layer : Utils::FindItems<LayerItem>(m_sample_model)) { - auto property = layer->property<ExternalProperty>(LayerItem::P_MATERIAL); - auto updated = model()->material_property(property.identifier()); - if (property != updated) - layer->setProperty(LayerItem::P_MATERIAL, updated); - } -} - -} // namespace gui2 diff --git a/gui2/model/materialpropertycontroller.h b/gui2/model/materialpropertycontroller.h deleted file mode 100644 index aaf8215105a..00000000000 --- a/gui2/model/materialpropertycontroller.h +++ /dev/null @@ -1,41 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/model/materialpropertycontroller.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_MODEL_MATERIALPROPERTYCONTROLLER_H -#define BORNAGAIN_GUI2_MODEL_MATERIALPROPERTYCONTROLLER_H - -#include "darefl_export.h" -#include "mvvm/signals/modellistener.h" - -namespace gui2 { - -class SampleModel; -class MaterialModel; - -//! Listens for all changes in material model and updates properties in SampleModel. - -class DAREFLCORE_EXPORT MaterialPropertyController - : public ModelView::ModelListener<MaterialModel> { -public: - MaterialPropertyController(MaterialModel* material_model, SampleModel* sample_model); - -private: - void update_all(); - - SampleModel* m_sample_model{nullptr}; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_MODEL_MATERIALPROPERTYCONTROLLER_H diff --git a/gui2/model/modelutils.cpp b/gui2/model/modelutils.cpp deleted file mode 100644 index ae871453cf3..00000000000 --- a/gui2/model/modelutils.cpp +++ /dev/null @@ -1,95 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/model/modelutils.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/model/modelutils.h" -#include "gui2/model/experimentaldatamodel.h" -#include "mvvm/model/externalproperty.h" -#include "mvvm/model/modelutils.h" -#include "mvvm/standarditems/axisitems.h" -#include "mvvm/standarditems/data1ditem.h" -#include "mvvm/standarditems/graphitem.h" - -namespace { -bool areCompatibleAxes(const ModelView::Data1DItem& item1, const ModelView::Data1DItem& item2) -{ - // TODO consider moving the logic on board of Data1DItem, consider implement getAxis() getter. - auto axis1 = item1.getItem(ModelView::Data1DItem::T_AXIS); - auto axis2 = item2.getItem(ModelView::Data1DItem::T_AXIS); - if (!axis1 || !axis2) - return false; - - return axis1->modelType() == axis2->modelType(); -} - -} // namespace - -namespace gui2 { - -ModelView::ExternalProperty Utils::CreateProperty(const ModelView::GraphItem* graph) -{ - std::string name = graph->parent()->displayName() + "/" + graph->displayName(); - auto colorName = QString::fromStdString(graph->colorName()); - return ModelView::ExternalProperty(name, QColor(colorName), graph->identifier()); -} - -std::vector<ModelView::ExternalProperty> Utils::CreateGraphProperties(ExperimentalDataModel* model) -{ - std::vector<ModelView::ExternalProperty> result; - for (auto graph : ModelView::Utils::FindItems<ModelView::GraphItem>(model)) - result.push_back(Utils::CreateProperty(graph)); - return result; -} - -// FIXME unit tests -ModelView::ExternalProperty -Utils::FindProperty(const std::vector<ModelView::ExternalProperty>& properties, - const std::string& id) -{ - for (const auto& prop : properties) - if (prop.identifier() == id) - return prop; - - return ModelView::ExternalProperty::undefined(); -} - -std::vector<double> Utils::CreateDiffVector(const std::vector<double>& a, - const std::vector<double>& b) -{ - size_t length = std::min(a.size(), b.size()); - std::vector<double> result(length, 0.0); - for (size_t i = 0; i < length; ++i) { - double denom = a[i] + b[i]; - result[i] = denom != 0.0 ? 2 * (a[i] - b[i]) / (a[i] + b[i]) : 0.0; - } - return result; -} - -void Utils::SetDifference(const ModelView::Data1DItem* data1, const ModelView::Data1DItem* data2, - ModelView::Data1DItem* target) -{ - if (!areCompatibleAxes(*data1, *data2) || !areCompatibleAxes(*data1, *target)) - return; - - // We expect same number of points to caclulate the difference graph. - if (data1->binCenters().size() != data2->binCenters().size()) - return; - - if (data1->binCenters() != target->binCenters()) { - target->item<ModelView::PointwiseAxisItem>(ModelView::Data1DItem::T_AXIS) - ->setParameters(data1->binCenters()); - } - target->setValues(CreateDiffVector(data1->binValues(), data2->binValues())); -} - -} // namespace gui2 diff --git a/gui2/model/modelutils.h b/gui2/model/modelutils.h deleted file mode 100644 index 66815d8b6ce..00000000000 --- a/gui2/model/modelutils.h +++ /dev/null @@ -1,59 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/model/modelutils.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_MODEL_MODELUTILS_H -#define BORNAGAIN_GUI2_MODEL_MODELUTILS_H - -#include "darefl_export.h" -#include <string> -#include <vector> - -namespace ModelView { -class GraphItem; -class Data1DItem; -class ExternalProperty; -} // namespace ModelView - -namespace gui2 { - -class ExperimentalDataModel; - -namespace Utils { - -//! Returns property representing given graph. -//! Used to link with the graph from various editors. -DAREFLCORE_EXPORT ModelView::ExternalProperty CreateProperty(const ModelView::GraphItem* graph); - -//! Returns vector of properties representing GraphItem content of the model. -DAREFLCORE_EXPORT std::vector<ModelView::ExternalProperty> -CreateGraphProperties(ExperimentalDataModel* model); - -//! Finds the property with the same `id` in given vector and returns it. -DAREFLCORE_EXPORT ModelView::ExternalProperty -FindProperty(const std::vector<ModelView::ExternalProperty>& properties, const std::string& id); - -//! Returns vector representing elementwise 2*(a-b)/(a+b) difference over two vectors. -//! Resulting vector will have size equal to min(a.size(), b.size()) -DAREFLCORE_EXPORT std::vector<double> CreateDiffVector(const std::vector<double>& a, - const std::vector<double>& b); - -//! Make target item represent difference of two Data1DItems. Target will get an axis as in data1. -DAREFLCORE_EXPORT void SetDifference(const ModelView::Data1DItem* data1, - const ModelView::Data1DItem* data2, - ModelView::Data1DItem* target); -} // namespace Utils - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_MODEL_MODELUTILS_H diff --git a/gui2/model/sampleitems.cpp b/gui2/model/sampleitems.cpp deleted file mode 100644 index dfd84641890..00000000000 --- a/gui2/model/sampleitems.cpp +++ /dev/null @@ -1,98 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/model/sampleitems.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/model/sampleitems.h" -#include "gui2/model/item_constants.h" -#include "gui2/model/materialitems.h" -#include "gui2/model/materialmodel.h" -#include "mvvm/model/externalproperty.h" -#include "mvvm/signals/itemmapper.h" -#include <QVariant> - -namespace gui2 { - -RoughnessItem::RoughnessItem() : ModelView::CompoundItem(GUI::Constants::RoughnessItemType) -{ - addProperty(P_SIGMA, 0.0)->setDisplayName("Sigma"); - addProperty(P_HURST, 0.5)->setDisplayName("Hurst"); - addProperty(P_LATERAL_CORR_LENGTH, 0.0)->setDisplayName("Correlation length"); -} - -//! --------------------------------------------------------------------------- - -LayerItem::LayerItem() : ModelView::CompoundItem(GUI::Constants::LayerItemType) -{ - addProperty(P_NAME, "Unnamed")->setDisplayName("Name"); - addProperty(P_MATERIAL, ModelView::ExternalProperty::undefined())->setDisplayName("Material"); - addProperty(P_THICKNESS, 0.0) - ->setDisplayName("Thickness") - ->setToolTip("Layer thickness in [nm]"); - addProperty<RoughnessItem>(P_ROUGHNESS); -} - -//! --------------------------------------------------------------------------- - -MultiLayerItem::MultiLayerItem() : ModelView::CompoundItem(GUI::Constants::MultiLayerItemType) -{ - addProperty(P_NAME, "Unnamed")->setDisplayName("Name"); - addProperty(P_NREPETITIONS, 1)->setDisplayName("Nr."); - std::vector<std::string> allowed_child = {GUI::Constants::MultiLayerItemType, - GUI::Constants::LayerItemType}; - registerTag(ModelView::TagInfo::universalTag(T_LAYERS, allowed_child), /*set_default*/ true); - - void update_layer_appearance(); -} - -void MultiLayerItem::activate() -{ - auto on_item_inserted = [this](ModelView::SessionItem*, ModelView::TagRow) { - update_layer_appearance(); - }; - mapper()->setOnItemInserted(on_item_inserted, this); - - auto on_item_removed = [this](ModelView::SessionItem*, ModelView::TagRow) { - update_layer_appearance(); - }; - mapper()->setOnItemRemoved(on_item_removed, this); -} - -//! Sets thickness property of top and bottom layers to disabled state. -//! Reset thickness of top and bottom layer to 0. - -void MultiLayerItem::update_layer_appearance() -{ - // FIXME restore correct enabling/disabling of thickness and roughness of top and bottom layers - // FIXME together with tests in layeritems.test.cpp - if (parent() != model()->rootItem()) - return; - - auto layers = items<LayerItem>(T_LAYERS); - for (auto it = layers.begin(); it != layers.end(); ++it) { - if (it == layers.begin()) { - (*it)->getItem(LayerItem::P_THICKNESS)->setEnabled(false); - (*it) - ->getItem(LayerItem::P_ROUGHNESS) - ->getItem(RoughnessItem::P_SIGMA) - ->setEnabled(false); - (*it)->setProperty(LayerItem::P_THICKNESS, 0.0); - } else if (std::next(it) == layers.end()) { - (*it)->getItem(LayerItem::P_THICKNESS)->setEnabled(false); - (*it)->setProperty(LayerItem::P_THICKNESS, 0.0); - } else { - (*it)->getItem(LayerItem::P_THICKNESS)->setEnabled(true); - } - } -} - -} // namespace gui2 diff --git a/gui2/model/sampleitems.h b/gui2/model/sampleitems.h deleted file mode 100644 index 5e086d98011..00000000000 --- a/gui2/model/sampleitems.h +++ /dev/null @@ -1,67 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/model/sampleitems.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_MODEL_SAMPLEITEMS_H -#define BORNAGAIN_GUI2_MODEL_SAMPLEITEMS_H - -//! @file gui2/model/sampleitems.h -//! Collection of layer and multi-layer items to populate SampleModel. - -#include "darefl_export.h" -#include "mvvm/model/compounditem.h" - -namespace gui2 { - -//! Item to represent the roughness of the layer. - -class DAREFLCORE_EXPORT RoughnessItem : public ModelView::CompoundItem { -public: - static inline const std::string P_SIGMA = "P_SIGMA"; - static inline const std::string P_HURST = "P_HURST"; - static inline const std::string P_LATERAL_CORR_LENGTH = "P_LATERAL_CORR_LENGTH"; - - RoughnessItem(); -}; - -//! Layer with name, thickness and reference to material. - -class DAREFLCORE_EXPORT LayerItem : public ModelView::CompoundItem { -public: - static inline const std::string P_NAME = "P_NAME"; - static inline const std::string P_MATERIAL = "P_MATERIAL"; - static inline const std::string P_THICKNESS = "P_THICKNESS"; - static inline const std::string P_ROUGHNESS = "P_ROUGHNESS"; - - LayerItem(); -}; - -//! Multi layer capable of holding layers and other multi-layers. - -class DAREFLCORE_EXPORT MultiLayerItem : public ModelView::CompoundItem { -public: - static inline const std::string P_NAME = "P_NAME"; - static inline const std::string T_LAYERS = "T_LAYERS"; - static inline const std::string P_NREPETITIONS = "P_NREPETITIONS"; - - MultiLayerItem(); - - void activate() override; - -private: - void update_layer_appearance(); -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_MODEL_SAMPLEITEMS_H diff --git a/gui2/model/samplemodel.cpp b/gui2/model/samplemodel.cpp deleted file mode 100644 index fde1cb7a1ef..00000000000 --- a/gui2/model/samplemodel.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/model/samplemodel.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/model/samplemodel.h" -#include "gui2/model/sampleitems.h" -#include "mvvm/model/itemcatalogue.h" - -using namespace ModelView; - -namespace gui2 { - -namespace { -std::unique_ptr<ItemCatalogue> CreateItemCatalogue() -{ - auto result = std::make_unique<ItemCatalogue>(); - result->registerItem<MultiLayerItem>(); - result->registerItem<LayerItem>(); - result->registerItem<RoughnessItem>(); - return result; -} -} // namespace - -SampleModel::SampleModel(std::shared_ptr<ModelView::ItemPool> pool) - : SessionModel("SampleModel", pool) -{ - setItemCatalogue(CreateItemCatalogue()); -} - -//! Populate the model with default MultiLayer with 3 layers. - -void SampleModel::create_default_multilayer() -{ - auto multilayer = insertItem<MultiLayerItem>(); - - auto top = insertItem<LayerItem>(multilayer); - top->setProperty(LayerItem::P_NAME, std::string("Ambient")); - auto middle = insertItem<LayerItem>(multilayer); - middle->setProperty(LayerItem::P_NAME, std::string("Middle")); - auto substrate = insertItem<LayerItem>(multilayer); - substrate->setProperty(LayerItem::P_NAME, std::string("Substrate")); - - middle->setProperty(LayerItem::P_THICKNESS, 42.0); -} - -} // namespace gui2 diff --git a/gui2/model/samplemodel.h b/gui2/model/samplemodel.h deleted file mode 100644 index 508bab2125a..00000000000 --- a/gui2/model/samplemodel.h +++ /dev/null @@ -1,33 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/model/samplemodel.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_MODEL_SAMPLEMODEL_H -#define BORNAGAIN_GUI2_MODEL_SAMPLEMODEL_H - -#include "darefl_export.h" -#include "mvvm/model/sessionmodel.h" - -namespace gui2 { - -//! Model to hold layers and multi-layers. -class DAREFLCORE_EXPORT SampleModel : public ModelView::SessionModel { -public: - SampleModel(std::shared_ptr<ModelView::ItemPool> pool = {}); - - void create_default_multilayer(); -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_MODEL_SAMPLEMODEL_H diff --git a/gui2/quicksimeditor/CMakeLists.txt b/gui2/quicksimeditor/CMakeLists.txt deleted file mode 100644 index b65f4ddec90..00000000000 --- a/gui2/quicksimeditor/CMakeLists.txt +++ /dev/null @@ -1,27 +0,0 @@ -target_sources(${library_name} PRIVATE - custombeampropertyeditorfactory.cpp - custombeampropertyeditorfactory.h - instrumentpropertyeditor.cpp - instrumentpropertyeditor.h - jobmanager.cpp - jobmanager.h - materialprofile.cpp - materialprofile.h - profilehelper.cpp - profilehelper.h - quicksim_types.h - quicksimcontroller.cpp - quicksimcontroller.h - quicksimeditor.cpp - quicksimeditor.h - quicksimeditortoolbar.cpp - quicksimeditortoolbar.h - quicksimutils.cpp - quicksimutils.h - simplotcontroller.cpp - simplotcontroller.h - simplotwidget.cpp - simplotwidget.h - speculartoysimulation.cpp - speculartoysimulation.h -) diff --git a/gui2/quicksimeditor/custombeampropertyeditorfactory.cpp b/gui2/quicksimeditor/custombeampropertyeditorfactory.cpp deleted file mode 100644 index 9a88cd35478..00000000000 --- a/gui2/quicksimeditor/custombeampropertyeditorfactory.cpp +++ /dev/null @@ -1,68 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/quicksimeditor/custombeampropertyeditorfactory.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/quicksimeditor/custombeampropertyeditorfactory.h" -#include "gui2/model/applicationmodels.h" -#include "gui2/model/experimentaldataitems.h" -#include "gui2/model/experimentaldatamodel.h" -#include "gui2/model/materialmodel.h" -#include "gui2/model/modelutils.h" -#include "mvvm/editors/externalpropertycomboeditor.h" -#include "mvvm/model/externalproperty.h" -#include "mvvm/model/modelutils.h" -#include "mvvm/standarditems/graphitem.h" -#include <QModelIndex> -#include <algorithm> - -using namespace ModelView; - -namespace gui2 { - -namespace { - -//! Returns vector of ExternalProperty representing imported graphs. -//! Use "Undefined graph" as a first item in a list. - -std::vector<ModelView::ExternalProperty> available_graph_properties(ExperimentalDataModel* model) -{ - std::vector<ModelView::ExternalProperty> result{ExternalProperty::undefined()}; - auto properties = Utils::CreateGraphProperties(model); - std::copy(properties.begin(), properties.end(), std::back_inserter(result)); - return result; -} -} // namespace - -CustomBeamGUI::View::PropertyEditorFactory::~CustomBeamPropertyEditorFactory() = default; - -CustomBeamGUI::View::PropertyEditorFactory::CustomBeamPropertyEditorFactory( - ApplicationModels* models) - : m_models(models) -{ -} - -std::unique_ptr<CustomEditor> -CustomBeamGUI::View::PropertyEditorFactory::createEditor(const QModelIndex& index) const -{ - auto value = index.data(Qt::EditRole); - if (ModelView::Utils::IsExtPropertyVariant(value)) { - auto choice_callback = [this]() { - return available_graph_properties(m_models->experimentalDataModel()); - }; - return std::make_unique<ExternalPropertyComboEditor>(choice_callback); - } else { - return DefaultEditorFactory::createEditor(index); - } -} - -} // namespace gui2 diff --git a/gui2/quicksimeditor/custombeampropertyeditorfactory.h b/gui2/quicksimeditor/custombeampropertyeditorfactory.h deleted file mode 100644 index 8ec16b4a53a..00000000000 --- a/gui2/quicksimeditor/custombeampropertyeditorfactory.h +++ /dev/null @@ -1,41 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/quicksimeditor/custombeampropertyeditorfactory.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_QUICKSIMEDITOR_CUSTOMBEAMPROPERTYEDITORFACTORY_H -#define BORNAGAIN_GUI2_QUICKSIMEDITOR_CUSTOMBEAMPROPERTYEDITORFACTORY_H - -#include "darefl_export.h" -#include "mvvm/editors/defaulteditorfactory.h" - -namespace gui2 { - -class ApplicationModels; - -//! Custom editor factory for LayerTreeView. Substitutes default ExternalProperty editor -//! with custom one, which will offer the choice between all defined materials. - -class DAREFLCORE_EXPORT CustomBeamPropertyEditorFactory : public ModelView::DefaultEditorFactory { -public: - CustomBeamPropertyEditorFactory(ApplicationModels* models); - ~CustomBeamPropertyEditorFactory(); - - std::unique_ptr<ModelView::CustomEditor> createEditor(const QModelIndex& index) const; - -private: - ApplicationModels* m_models{nullptr}; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_QUICKSIMEDITOR_CUSTOMBEAMPROPERTYEDITORFACTORY_H diff --git a/gui2/quicksimeditor/instrumentpropertyeditor.cpp b/gui2/quicksimeditor/instrumentpropertyeditor.cpp deleted file mode 100644 index 22b4a78702e..00000000000 --- a/gui2/quicksimeditor/instrumentpropertyeditor.cpp +++ /dev/null @@ -1,65 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/quicksimeditor/instrumentpropertyeditor.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/quicksimeditor/instrumentpropertyeditor.h" -#include "gui2/mainwindow/styleutils.h" -#include "gui2/model/applicationmodels.h" -#include "gui2/model/instrumentitems.h" -#include "gui2/model/instrumentmodel.h" -#include "gui2/quicksimeditor/custombeampropertyeditorfactory.h" -#include "mvvm/viewmodel/viewmodeldelegate.h" -#include "mvvm/widgets/propertytreeview.h" -#include <QTreeView> -#include <QVBoxLayout> - -using namespace ModelView; - -namespace gui2 { - -InstrumentPropertyEditor::InstrumentPropertyEditor(QWidget* parent) - : QWidget(parent), m_beamPropertyEditor(new ModelView::PropertyTreeView) - -{ - auto layout = new QVBoxLayout(this); - layout->addWidget(m_beamPropertyEditor); -} - -InstrumentPropertyEditor::~InstrumentPropertyEditor() = default; - -void InstrumentPropertyEditor::setModels(ApplicationModels* models) -{ - auto instrument = models->instrumentModel()->topItem<SpecularInstrumentItem>(); - - auto delegate = std::make_unique<ViewModelDelegate>(); - delegate->setEditorFactory(std::make_unique<CustomBeamPropertyEditorFactory>(models)); - m_beamPropertyEditor->setViewModelDelegate(std::move(delegate)); - - m_beamPropertyEditor->setItem( - instrument->item<SpecularBeamItem>(SpecularInstrumentItem::P_BEAM)); - - m_beamPropertyEditor->treeView()->setRootIsDecorated(true); - m_beamPropertyEditor->treeView()->expandAll(); -} - -QSize InstrumentPropertyEditor::sizeHint() const -{ - return GUI::Utils::Style::DockSizeHint(); -} - -QSize InstrumentPropertyEditor::minimumSizeHint() const -{ - return GUI::Utils::Style::DockMinimumSizeHint(); -} - -} // namespace gui2 diff --git a/gui2/quicksimeditor/instrumentpropertyeditor.h b/gui2/quicksimeditor/instrumentpropertyeditor.h deleted file mode 100644 index 78c1bb72cda..00000000000 --- a/gui2/quicksimeditor/instrumentpropertyeditor.h +++ /dev/null @@ -1,50 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/quicksimeditor/instrumentpropertyeditor.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_QUICKSIMEDITOR_INSTRUMENTPROPERTYEDITOR_H -#define BORNAGAIN_GUI2_QUICKSIMEDITOR_INSTRUMENTPROPERTYEDITOR_H - -#include "darefl_export.h" -#include <QWidget> - -namespace ModelView { -class PropertyTreeView; -} - -namespace gui2 { - -class ApplicationModels; - -//! Widget with InstrumentItem properties. -//! Used to modify q-scan parameters, located under QuickSimEditor. - -class DAREFLCORE_EXPORT InstrumentPropertyEditor : public QWidget { - Q_OBJECT - -public: - InstrumentPropertyEditor(QWidget* parent = nullptr); - ~InstrumentPropertyEditor(); - - void setModels(ApplicationModels* models); - - QSize sizeHint() const override; - QSize minimumSizeHint() const override; - -private: - ModelView::PropertyTreeView* m_beamPropertyEditor{nullptr}; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_QUICKSIMEDITOR_INSTRUMENTPROPERTYEDITOR_H diff --git a/gui2/quicksimeditor/jobmanager.cpp b/gui2/quicksimeditor/jobmanager.cpp deleted file mode 100644 index c9e10661f6a..00000000000 --- a/gui2/quicksimeditor/jobmanager.cpp +++ /dev/null @@ -1,101 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/quicksimeditor/jobmanager.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/quicksimeditor/jobmanager.h" -#include "gui2/quicksimeditor/speculartoysimulation.h" - -namespace gui2 { - -JobManager::JobManager(QObject* parent) : QObject(parent), m_isRunning(true) -{ - // starting thread to run consequent simulations - m_simThread = std::thread{&JobManager::wait_and_run, this}; -} - -JobManager::~JobManager() -{ - m_isRunning = false; - // making stack throw to stops waiting in JobManager::wait_and_run - m_requestedInputValues.stop(); - m_simThread.join(); -} - -//! Returns vector representing results of a simulation. - -SimulationResult JobManager::simulationResult() -{ - auto result = m_simulationResult.try_pop(); - return result ? *result.get() : SimulationResult(); -} - -//! Performs simulation request. Given multislice will be stored in a stack of values to trigger -//! a waiting thread. - -void JobManager::requestSimulation(const multislice_t& multislice, - const std::vector<double>& qvalues, double intensity) -{ - // At this point, non-empty stack means that currently simulation thread is busy. - // Replacing top value in a stack, meaning that we are droping previous request. - SimulationInput input_data; - input_data.slice_data = multislice; - input_data.qvalues = qvalues; - input_data.intensity = intensity; - m_requestedInputValues.update_top(input_data); -} - -//! Processes interrupt request by setting corresponding flag. - -void JobManager::onInterruptRequest() -{ - m_interruptRequest = true; -} - -//! Performs concequent simulations for given simulation parameter. Waits for simulation input -//! parameter to appear in a stack, starts new simulation as soon as input data is ready. -//! Method is intended for execution in a thread. - -void JobManager::wait_and_run() -{ - while (m_isRunning) { - try { - // Waiting here for the value which we will use as simulation input parameter. - auto value = m_requestedInputValues.wait_and_pop(); - - // preparing simulation - SpecularToySimulation simulation(*value.get()); - auto on_progress = [this](int value) { - progressChanged(value); - return m_interruptRequest; - }; - simulation.setProgressCallback(on_progress); - - // running simulation - simulation.runSimulation(); - - // Saving simulation result, overwrite previous if exists. If at this point stack - // with results is not empty it means that plotting is disabled or running too slow. - m_simulationResult.update_top(simulation.simulationResult()); - simulationCompleted(); - - } catch (std::exception& ex) { - // Exception is thrown - // a) If waiting on stack was stopped my calling threadsafe_stack::stop. - // b) If simulation was interrupted via interrupt_request - m_interruptRequest = false; - progressChanged(0); - } - } -} - -} // namespace gui2 diff --git a/gui2/quicksimeditor/jobmanager.h b/gui2/quicksimeditor/jobmanager.h deleted file mode 100644 index 7341c117842..00000000000 --- a/gui2/quicksimeditor/jobmanager.h +++ /dev/null @@ -1,57 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/quicksimeditor/jobmanager.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_QUICKSIMEDITOR_JOBMANAGER_H -#define BORNAGAIN_GUI2_QUICKSIMEDITOR_JOBMANAGER_H - -#include "darefl_export.h" -#include "gui2/quicksimeditor/quicksim_types.h" -#include "mvvm/utils/threadsafestack.h" -#include <QObject> - -namespace gui2 { - -//! Handles all thread activity for running job simulation in the background. - -class DAREFLCORE_EXPORT JobManager : public QObject { - Q_OBJECT - -public: - JobManager(QObject* parent = nullptr); - ~JobManager() override; - - SimulationResult simulationResult(); - -signals: - void progressChanged(int value); - void simulationCompleted(); - -public slots: - void requestSimulation(const multislice_t& multislice, const std::vector<double>& qvalues, - double intensity); - void onInterruptRequest(); - -private: - void wait_and_run(); - - std::thread m_simThread; - ModelView::threadsafe_stack<SimulationInput> m_requestedInputValues; - ModelView::threadsafe_stack<SimulationResult> m_simulationResult; - std::atomic<bool> m_isRunning; - bool m_interruptRequest{false}; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_QUICKSIMEDITOR_JOBMANAGER_H diff --git a/gui2/quicksimeditor/materialprofile.cpp b/gui2/quicksimeditor/materialprofile.cpp deleted file mode 100644 index 0b1e11836a5..00000000000 --- a/gui2/quicksimeditor/materialprofile.cpp +++ /dev/null @@ -1,51 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/quicksimeditor/materialprofile.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/quicksimeditor/materialprofile.h" -#include "gui2/quicksimeditor/profilehelper.h" -#include "gui2/quicksimeditor/quicksimutils.h" -#include <Resample/Slice/Slice.h> - -namespace gui2 { - -std::vector<complex_t> MaterialProfile::CalculateProfile(const multislice_t& multilayer, - int n_points, double z_min, double z_max) -{ - auto baSlices = Utils::createBornAgainSlices(multilayer); - ProfileHelper helper(baSlices); - std::vector<double> z_values = GenerateZValues(n_points, z_min, z_max); - return helper.calculateProfile(z_values); -} - -std::pair<double, double> -MaterialProfile::DefaultMaterialProfileLimits(const multislice_t& multilayer) -{ - auto baSlices = Utils::createBornAgainSlices(multilayer); - ProfileHelper helper(baSlices); - return helper.defaultLimits(); -} - -std::vector<double> MaterialProfile::GenerateZValues(int n_points, double z_min, double z_max) -{ - std::vector<double> result; - if (n_points < 1) - return result; - double step = n_points > 1 ? (z_max - z_min) / (n_points - 1) : 0.0; - for (int i = 0; i < n_points; ++i) { - result.push_back(z_min + i * step); - } - return result; -} - -} // namespace gui2 diff --git a/gui2/quicksimeditor/materialprofile.h b/gui2/quicksimeditor/materialprofile.h deleted file mode 100644 index 846eb8e94b9..00000000000 --- a/gui2/quicksimeditor/materialprofile.h +++ /dev/null @@ -1,42 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/quicksimeditor/materialprofile.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_QUICKSIMEDITOR_MATERIALPROFILE_H -#define BORNAGAIN_GUI2_QUICKSIMEDITOR_MATERIALPROFILE_H - -#include "darefl_export.h" -#include "gui2/quicksimeditor/quicksim_types.h" - -namespace gui2 { - -//! Collection of methods borrowed from BornAgain for material profile calculations. - -namespace MaterialProfile { - -//! Calculate average material profile for given multilayer -DAREFLCORE_EXPORT std::vector<complex_t> CalculateProfile(const multislice_t& multilayer, - int n_points, double z_min, double z_max); - -//! Get default z limits for generating a material profile -DAREFLCORE_EXPORT std::pair<double, double> -DefaultMaterialProfileLimits(const multislice_t& multilayer); - -//! Generate z values (equidistant) for use in MaterialProfile -DAREFLCORE_EXPORT std::vector<double> GenerateZValues(int n_points, double z_min, double z_max); - -} // namespace MaterialProfile - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_QUICKSIMEDITOR_MATERIALPROFILE_H diff --git a/gui2/quicksimeditor/profilehelper.cpp b/gui2/quicksimeditor/profilehelper.cpp deleted file mode 100644 index 211e1649d41..00000000000 --- a/gui2/quicksimeditor/profilehelper.cpp +++ /dev/null @@ -1,93 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/quicksimeditor/profilehelper.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/quicksimeditor/profilehelper.h" -#include <Resample/Slice/Slice.h> -#include <Sample/Interface/LayerRoughness.h> - -namespace { -const double prefactor = std::sqrt(2.0 / M_PI); -double Transition(double x, double sigma); -double TransitionTanh(double x); -} // namespace - -gui2::ProfileHelper::ProfileHelper(const SliceStack& sample) -{ - auto N = sample.size(); - m_materialdata.reserve(N); - if (N > 1) { - m_zlimits.reserve(N - 1); - m_sigmas.reserve(N - 1); - } - double bottom_z{0}; - for (size_t i = 0; i < N; ++i) { - m_materialdata.push_back(sample[i].material().materialData()); - bottom_z -= sample[i].thickness(); - if (i + 1 < N) { - m_zlimits.push_back(bottom_z); - auto sigma = 0.; - if (auto roughness = sample[i + 1].topRoughness()) - sigma = roughness->getSigma(); - - m_sigmas.push_back(sigma); - } - } -} - -// Note: for refractive index materials, the material interpolation actually happens at the level -// of n^2. To first order in delta and beta, this implies the same smooth interpolation of delta -// and beta, as is done here. -std::vector<complex_t> -gui2::ProfileHelper::calculateProfile(const std::vector<double>& z_values) const -{ - complex_t top_value = m_materialdata.size() ? m_materialdata[0] : 0.0; - std::vector<complex_t> result(z_values.size(), top_value); - for (size_t i = 0; i < m_zlimits.size(); ++i) { - auto sld_diff = m_materialdata[i + 1] - m_materialdata[i]; - for (size_t j = 0; j < z_values.size(); ++j) { - auto arg = (z_values[j] - m_zlimits[i]); - auto t = Transition(arg, m_sigmas[i]); - result[j] += sld_diff * t; - } - } - return result; -} - -std::pair<double, double> gui2::ProfileHelper::defaultLimits() const -{ - if (m_zlimits.size() < 1) - return {0.0, 0.0}; - double interface_span = m_zlimits.front() - m_zlimits.back(); - double default_margin = interface_span > 0.0 ? interface_span / 20.0 : 10.0; - double top_margin = m_sigmas.front() > 0.0 ? 5.0 * m_sigmas.front() : default_margin; - double bottom_margin = m_sigmas.back() > 0.0 ? 5.0 * m_sigmas.back() : default_margin; - double z_min = m_zlimits.back() - bottom_margin; - double z_max = m_zlimits.front() + top_margin; - return {z_min, z_max}; -} - -gui2::ProfileHelper::~ProfileHelper() = default; - -namespace { -double Transition(double x, double sigma) -{ - if (sigma <= 0.0) - return x < 0.0 ? 1.0 : 0.0; - return TransitionTanh(x / sigma); -} -double TransitionTanh(double x) -{ - return (1.0 - std::tanh(prefactor * x)) / 2.0; -} -} // namespace diff --git a/gui2/quicksimeditor/profilehelper.h b/gui2/quicksimeditor/profilehelper.h deleted file mode 100644 index 34b3bf93e56..00000000000 --- a/gui2/quicksimeditor/profilehelper.h +++ /dev/null @@ -1,42 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/quicksimeditor/profilehelper.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_QUICKSIMEDITOR_PROFILEHELPER_H -#define BORNAGAIN_GUI2_QUICKSIMEDITOR_PROFILEHELPER_H - -#include "darefl_export.h" -#include <Resample/Slice/Slice.h> -#include <utility> -#include <vector> - -//! Object that can generate the material profile of a sample as a function of depth. -namespace gui2 { - -class DAREFLCORE_EXPORT ProfileHelper { -public: - ProfileHelper(const SliceStack& sample); - ~ProfileHelper(); - - std::vector<complex_t> calculateProfile(const std::vector<double>& z_values) const; - std::pair<double, double> defaultLimits() const; - -private: - std::vector<complex_t> m_materialdata; - std::vector<double> m_zlimits; - std::vector<double> m_sigmas; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_QUICKSIMEDITOR_PROFILEHELPER_H diff --git a/gui2/quicksimeditor/quicksim_types.h b/gui2/quicksimeditor/quicksim_types.h deleted file mode 100644 index 0a0b75a4b95..00000000000 --- a/gui2/quicksimeditor/quicksim_types.h +++ /dev/null @@ -1,56 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/quicksimeditor/quicksim_types.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_QUICKSIMEDITOR_QUICKSIM_TYPES_H -#define BORNAGAIN_GUI2_QUICKSIMEDITOR_QUICKSIM_TYPES_H - -#include "darefl_export.h" -#include <complex> -#include <vector> - -namespace gui2 { - -using complex_t = std::complex<double>; - -//! Data structure for simple multilayer representation. -struct DAREFLCORE_EXPORT SliceData { - complex_t material; - double thickness{0.0}; - double sigma{0.0}; // top interface sigma -}; -using multislice_t = std::vector<SliceData>; - -//! Represents data to run specular simulations. -struct DAREFLCORE_EXPORT SimulationInput { - std::vector<double> qvalues; - multislice_t slice_data; - double intensity; -}; - -//! Represents results of the simulation. -struct DAREFLCORE_EXPORT SimulationResult { - std::vector<double> qvalues; - std::vector<double> amplitudes; -}; - -//! Represents results of SLD profile calculations. -struct DAREFLCORE_EXPORT SLDProfile { - double zmin{0.0}; - double zmax{0.0}; - std::vector<double> sld_real_values; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_QUICKSIMEDITOR_QUICKSIM_TYPES_H diff --git a/gui2/quicksimeditor/quicksimcontroller.cpp b/gui2/quicksimeditor/quicksimcontroller.cpp deleted file mode 100644 index f9ad53d89da..00000000000 --- a/gui2/quicksimeditor/quicksimcontroller.cpp +++ /dev/null @@ -1,151 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/quicksimeditor/quicksimcontroller.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/quicksimeditor/quicksimcontroller.h" -#include "gui2/core/app_constants.h" -#include "gui2/model/applicationmodels.h" -#include "gui2/model/instrumentitems.h" -#include "gui2/model/instrumentmodel.h" -#include "gui2/model/jobitem.h" -#include "gui2/model/jobmodel.h" -#include "gui2/model/materialmodel.h" -#include "gui2/model/sampleitems.h" -#include "gui2/model/samplemodel.h" -#include "gui2/quicksimeditor/jobmanager.h" -#include "gui2/quicksimeditor/quicksimutils.h" -#include "gui2/quicksimeditor/speculartoysimulation.h" -#include "mvvm/project/modelhaschangedcontroller.h" -#include "mvvm/standarditems/axisitems.h" -#include "mvvm/standarditems/data1ditem.h" - -namespace { -const int profile_points_count = 1000; -} - -namespace gui2 { - -QuickSimController::QuickSimController(QObject* parent) - : QObject(parent) - , m_jobManager(new JobManager(this)) - , m_isRealTimeMode(GUI::Constants::live_simulation_default_on) -{ -} - -QuickSimController::~QuickSimController() = default; - -void QuickSimController::setModels(ApplicationModels* models) -{ - m_models = models; - - auto on_model_change = [this]() { onMultiLayerChange(); }; - m_materialChangedController = std::make_unique<ModelView::ModelHasChangedController>( - m_models->materialModel(), on_model_change); - m_sampleChangedController = std::make_unique<ModelView::ModelHasChangedController>( - m_models->sampleModel(), on_model_change); - m_instrumentChangedController = std::make_unique<ModelView::ModelHasChangedController>( - m_models->instrumentModel(), on_model_change); - - setup_jobmanager_connections(); - - onMultiLayerChange(); - jobModel()->sldViewport()->setViewportToContent(); -} - -//! Requests interruption of running simulaitons. - -void QuickSimController::onInterruptRequest() -{ - m_jobManager->onInterruptRequest(); -} - -void QuickSimController::onRealTimeRequest(bool status) -{ - m_isRealTimeMode = status; -} - -//! Processes multilayer on request. Doesn't work in real time mode. - -void QuickSimController::onRunSimulationRequest() -{ - process_multilayer(/*submit_simulation*/ true); -} - -//! Processes multilayer on any model change. Works only in realtime mode. - -void QuickSimController::onMultiLayerChange() -{ - process_multilayer(/*submit_simulation*/ m_isRealTimeMode); -} - -//! Takes simulation results from JobManager and write into the model. - -void QuickSimController::onSimulationCompleted() -{ - jobModel()->updateSpecularData(m_jobManager->simulationResult()); -} - -//! Constructs multislice, calculates profile and submits specular simulation. - -void QuickSimController::process_multilayer(bool submit_simulation) -{ - auto multilayer = m_models->sampleModel()->topItem<MultiLayerItem>(); - auto slices = Utils::CreateMultiSlice(*multilayer); - update_sld_profile(slices); - if (submit_simulation) - submit_specular_simulation(slices); -} - -//! Calculates sld profile from slice and immediately update data items. - -void QuickSimController::update_sld_profile(const multislice_t& multislice) -{ - auto data = SpecularToySimulation::sld_profile(multislice, profile_points_count); - jobModel()->updateSLDProfile(data); -} - -//! Submit data to JobManager for consequent specular simulation in a separate thread. - -void QuickSimController::submit_specular_simulation(const multislice_t& multislice) -{ - auto instrument = instrumentModel()->topItem<SpecularInstrumentItem>(); - auto beam = instrument->beamItem(); - m_jobManager->requestSimulation(multislice, beam->qScanValues(), beam->intensity()); -} - -//! Connect signals going from JobManager. Connections are made queued since signals are emitted -//! from non-GUI thread and we want to deal with widgets. - -void QuickSimController::setup_jobmanager_connections() -{ - - // Simulation progress is propagated from JobManager to this controller for further forwarding. - connect(m_jobManager, &JobManager::progressChanged, this, &QuickSimController::progressChanged, - Qt::QueuedConnection); - - // Notification about completed simulation from jobManager to this controller. - connect(m_jobManager, &JobManager::simulationCompleted, this, - &QuickSimController::onSimulationCompleted, Qt::QueuedConnection); -} - -JobModel* QuickSimController::jobModel() const -{ - return m_models->jobModel(); -} - -InstrumentModel* QuickSimController::instrumentModel() const -{ - return m_models->instrumentModel(); -} - -} // namespace gui2 diff --git a/gui2/quicksimeditor/quicksimcontroller.h b/gui2/quicksimeditor/quicksimcontroller.h deleted file mode 100644 index 8330ca9e3a3..00000000000 --- a/gui2/quicksimeditor/quicksimcontroller.h +++ /dev/null @@ -1,81 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/quicksimeditor/quicksimcontroller.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_QUICKSIMEDITOR_QUICKSIMCONTROLLER_H -#define BORNAGAIN_GUI2_QUICKSIMEDITOR_QUICKSIMCONTROLLER_H - -#include "darefl_export.h" -#include "gui2/quicksimeditor/quicksim_types.h" -#include <QObject> -#include <memory> - -namespace ModelView { -class ModelHasChangedController; -} - -namespace gui2 { - -class ApplicationModels; -class JobManager; -class JobModel; -class InstrumentModel; - -//! Provides quick reflectometry simulations on any change of SampleModel and MaterialModel. -//! Listens for any change in SampleModel and MaterialModel, extracts the data needed for -//! the simulation, and then submit simulation request to JobManager. As soon as JobManager reports -//! about completed simulations, extract results from there and put them into JobModel. - -class DAREFLCORE_EXPORT QuickSimController : public QObject { - Q_OBJECT - -public: - QuickSimController(QObject* parent = nullptr); - ~QuickSimController(); - - void setModels(ApplicationModels* models); - -signals: - void progressChanged(int value); - -public slots: - void onInterruptRequest(); - void onRealTimeRequest(bool status); - void onRunSimulationRequest(); - -private slots: - void onMultiLayerChange(); - void onSimulationCompleted(); - -private: - void process_multilayer(bool submit_simulation = false); - void update_sld_profile(const multislice_t& multilayer); - void submit_specular_simulation(const multislice_t& multislice); - void setup_jobmanager_connections(); - - JobModel* jobModel() const; - InstrumentModel* instrumentModel() const; - - ApplicationModels* m_models{nullptr}; - JobManager* m_jobManager{nullptr}; - - bool m_isRealTimeMode; //! Run simulation on every parameter change. - - std::unique_ptr<ModelView::ModelHasChangedController> m_materialChangedController; - std::unique_ptr<ModelView::ModelHasChangedController> m_sampleChangedController; - std::unique_ptr<ModelView::ModelHasChangedController> m_instrumentChangedController; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_QUICKSIMEDITOR_QUICKSIMCONTROLLER_H diff --git a/gui2/quicksimeditor/quicksimeditor.cpp b/gui2/quicksimeditor/quicksimeditor.cpp deleted file mode 100644 index 2621d9ab29b..00000000000 --- a/gui2/quicksimeditor/quicksimeditor.cpp +++ /dev/null @@ -1,107 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/quicksimeditor/quicksimeditor.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/quicksimeditor/quicksimeditor.h" -#include "gui2/mainwindow/styleutils.h" -#include "gui2/model/applicationmodels.h" -#include "gui2/model/experimentaldataitems.h" -#include "gui2/model/jobmodel.h" -#include "gui2/quicksimeditor/quicksimcontroller.h" -#include "gui2/quicksimeditor/quicksimeditortoolbar.h" -#include "gui2/quicksimeditor/simplotcontroller.h" -#include "gui2/quicksimeditor/simplotwidget.h" -#include "mvvm/model/modelutils.h" -#include "mvvm/plotting/graphcanvas.h" -#include "mvvm/standarditems/graphviewportitem.h" -#include <QTabWidget> -#include <QVBoxLayout> - -using namespace ModelView; - -namespace gui2 { - -QuickSimEditor::QuickSimEditor(QWidget* parent) - : QWidget(parent) - , m_simController(new QuickSimController(this)) - , m_plotController(new SimPlotController(this)) - , m_plotWidget(new SimPlotWidget) - , m_toolBar(new QuickSimEditorToolBar) -{ - setWindowTitle(QString("Reflectivity plot")); - auto layout = new QVBoxLayout(this); - layout->addWidget(m_toolBar); - layout->addWidget(m_plotWidget); - layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(0); - - setup_toolbar_connections(); - setup_controller_connections(); -} - -QuickSimEditor::~QuickSimEditor() = default; - -//! Set the mododel for the different items -void QuickSimEditor::setModels(ApplicationModels* models) -{ - m_appModels = models; - m_simController->setModels(models); - m_plotController->setModels(models); - m_plotWidget->setModels(models); -} - -QSize QuickSimEditor::sizeHint() const -{ - return GUI::Utils::Style::DockSizeHint(); -} - -QSize QuickSimEditor::minimumSizeHint() const -{ - return GUI::Utils::Style::DockMinimumSizeHint(); -} - -//! Connects signals from toolbar. - -void QuickSimEditor::setup_toolbar_connections() -{ - // Request to reset plot is propagated from toolbar to viewports. - auto on_reset_view = [this]() { m_plotWidget->updateViewport(); }; - connect(dynamic_cast<QuickSimEditorToolBar*>(m_toolBar), - &QuickSimEditorToolBar::resetViewRequest, on_reset_view); - - // Simulation interrupt request is propagated from toolbar to controller. - connect(dynamic_cast<QuickSimEditorToolBar*>(m_toolBar), &QuickSimEditorToolBar::cancelPressed, - m_simController, &QuickSimController::onInterruptRequest); - - // Request for real time mode is propagated from toobar to controller. - connect(dynamic_cast<QuickSimEditorToolBar*>(m_toolBar), - &QuickSimEditorToolBar::realTimeRequest, m_simController, - &QuickSimController::onRealTimeRequest); - - // Run simulation is propagated from toobar to controller. - connect(dynamic_cast<QuickSimEditorToolBar*>(m_toolBar), - &QuickSimEditorToolBar::runSimulationRequest, m_simController, - &QuickSimController::onRunSimulationRequest); -} - -//! Connects signals from controller. - -void QuickSimEditor::setup_controller_connections() -{ - // Progress values propagated from controller to toolbar. - connect(m_simController, &QuickSimController::progressChanged, - dynamic_cast<QuickSimEditorToolBar*>(m_toolBar), - &QuickSimEditorToolBar::onProgressChanged); -} - -} // namespace gui2 diff --git a/gui2/quicksimeditor/quicksimeditor.h b/gui2/quicksimeditor/quicksimeditor.h deleted file mode 100644 index ca1267d0a98..00000000000 --- a/gui2/quicksimeditor/quicksimeditor.h +++ /dev/null @@ -1,57 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/quicksimeditor/quicksimeditor.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_QUICKSIMEDITOR_QUICKSIMEDITOR_H -#define BORNAGAIN_GUI2_QUICKSIMEDITOR_QUICKSIMEDITOR_H - -#include "darefl_export.h" -#include <QWidget> - -namespace gui2 { - -class JobModel; -class ApplicationModels; -class QuickSimController; -class QuickSimEditorToolBar; -class SimPlotController; -class SimPlotWidget; - -//! Quick reflectivity simulations. - -class DAREFLCORE_EXPORT QuickSimEditor : public QWidget { - Q_OBJECT - -public: - QuickSimEditor(QWidget* parent = nullptr); - ~QuickSimEditor(); - - void setModels(ApplicationModels* models); - - QSize sizeHint() const override; - QSize minimumSizeHint() const override; - -private: - void setup_toolbar_connections(); - void setup_controller_connections(); - - ApplicationModels* m_appModels{nullptr}; - QuickSimController* m_simController{nullptr}; - SimPlotController* m_plotController{nullptr}; - SimPlotWidget* m_plotWidget{nullptr}; - QuickSimEditorToolBar* m_toolBar{nullptr}; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_QUICKSIMEDITOR_QUICKSIMEDITOR_H diff --git a/gui2/quicksimeditor/quicksimeditortoolbar.cpp b/gui2/quicksimeditor/quicksimeditortoolbar.cpp deleted file mode 100644 index 6b2c4e67960..00000000000 --- a/gui2/quicksimeditor/quicksimeditortoolbar.cpp +++ /dev/null @@ -1,100 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/quicksimeditor/quicksimeditortoolbar.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/quicksimeditor/quicksimeditortoolbar.h" -#include "gui2/core/app_constants.h" -#include <QAction> -#include <QCheckBox> -#include <QLabel> -#include <QProgressBar> -#include <QVBoxLayout> - -namespace gui2 { - -QuickSimEditorToolBar::QuickSimEditorToolBar(QWidget* parent) - : QToolBar(parent), m_liveCheckbox(new QCheckBox), m_progressBar(new QProgressBar) -{ - const int toolbar_icon_size = 24; - setIconSize(QSize(toolbar_icon_size, toolbar_icon_size)); - setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - - setup_simulation_elements(); - add_wide_separator(); - - setup_plot_elements(); -} - -//! Set progress bar to given value. - -void QuickSimEditorToolBar::onProgressChanged(int value) -{ - m_progressBar->setValue(value); -} - -void QuickSimEditorToolBar::add_wide_separator() -{ - addWidget(new QLabel(" ")); - addSeparator(); - addWidget(new QLabel(" ")); -} - -//! Setups elements to run simulation. - -void QuickSimEditorToolBar::setup_simulation_elements() -{ - // live check box and label - const QString live_tooltip = "Automatically run simulation and update plot\n" - "on any multilayer change."; - m_liveCheckbox->setCheckState(GUI::Constants::live_simulation_default_on ? Qt::Checked - : Qt::Unchecked); - m_liveCheckbox->setToolTip(live_tooltip); - auto on_check_state = [this](int state) { realTimeRequest(state == Qt::Checked); }; - connect(m_liveCheckbox, &QCheckBox::stateChanged, on_check_state); - addWidget(m_liveCheckbox); - auto label = new QLabel("Live"); - label->setToolTip(live_tooltip); - addWidget(label); - - // run simulation - auto run_action = new QAction("Run", this); - run_action->setIcon(QIcon(":/icons/play-circle-outline.svg")); - run_action->setToolTip("Run simulation for current multilayer state"); - connect(run_action, &QAction::triggered, this, &QuickSimEditorToolBar::runSimulationRequest); - addAction(run_action); - - // progress bar - m_progressBar->setFixedWidth(150); - m_progressBar->setTextVisible(false); - addWidget(m_progressBar); - - // cancel simulation - auto cancel_action = new QAction("Cancel", this); - cancel_action->setIcon(QIcon(":/icons/close-circle-outline.svg")); - cancel_action->setToolTip("Cancel running simulation"); - connect(cancel_action, &QAction::triggered, this, &QuickSimEditorToolBar::cancelPressed); - addAction(cancel_action); -} - -//! Setups actions to reset plot and access its settings. - -void QuickSimEditorToolBar::setup_plot_elements() -{ - auto reset_view = new QAction("Replot", this); - reset_view->setToolTip("Set plot axes to default range"); - reset_view->setIcon(QIcon(":/icons/aspect-ratio.svg")); - connect(reset_view, &QAction::triggered, this, &QuickSimEditorToolBar::resetViewRequest); - addAction(reset_view); -} - -} // namespace gui2 diff --git a/gui2/quicksimeditor/quicksimeditortoolbar.h b/gui2/quicksimeditor/quicksimeditortoolbar.h deleted file mode 100644 index 21888f544ad..00000000000 --- a/gui2/quicksimeditor/quicksimeditortoolbar.h +++ /dev/null @@ -1,57 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/quicksimeditor/quicksimeditortoolbar.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_QUICKSIMEDITOR_QUICKSIMEDITORTOOLBAR_H -#define BORNAGAIN_GUI2_QUICKSIMEDITOR_QUICKSIMEDITORTOOLBAR_H - -#include "darefl_export.h" -#include <QToolBar> - -class QProgressBar; -class QCheckBox; - -namespace gui2 { - -//! Toolbar for QuickSimEditor. -//! Contains live simulation button, cancel button, simulation progress bar and settings buttons. - -class DAREFLCORE_EXPORT QuickSimEditorToolBar : public QToolBar { - Q_OBJECT - -public: - explicit QuickSimEditorToolBar(QWidget* parent = nullptr); - -signals: - void realTimeRequest(bool); - void runSimulationRequest(); - void cancelPressed(); - void instrumentSettingsRequest(); - void resetViewRequest(); - void plotSettingsRequest(); - -public slots: - void onProgressChanged(int value); - -private: - void add_wide_separator(); - void setup_simulation_elements(); - void setup_plot_elements(); - - QCheckBox* m_liveCheckbox{nullptr}; - QProgressBar* m_progressBar{nullptr}; //! Simulation progressbar. -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_QUICKSIMEDITOR_QUICKSIMEDITORTOOLBAR_H diff --git a/gui2/quicksimeditor/quicksimutils.cpp b/gui2/quicksimeditor/quicksimutils.cpp deleted file mode 100644 index faeef5449eb..00000000000 --- a/gui2/quicksimeditor/quicksimutils.cpp +++ /dev/null @@ -1,89 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/quicksimeditor/quicksimutils.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/quicksimeditor/quicksimutils.h" -#include "gui2/model/item_constants.h" -#include "gui2/model/materialitems.h" -#include "gui2/model/sampleitems.h" -#include "mvvm/model/externalproperty.h" -#include "mvvm/model/sessionmodel.h" -#include <Resample/Slice/Slice.h> -#include <Sample/Interface/LayerRoughness.h> -#include <Sample/Material/MaterialFactoryFuncs.h> -#include <stdexcept> - -namespace gui2 { - -namespace { - -//! Creates slice from layer content. -SliceData create_slice(const ModelView::SessionItem& layer) -{ - if (layer.modelType() != GUI::Constants::LayerItemType) - throw std::runtime_error("Error in create_slice(): not a layer."); - - double thickness = layer.property<double>(LayerItem::P_THICKNESS); - auto roughness = layer.item<RoughnessItem>(LayerItem::P_ROUGHNESS); - double sigma = roughness->property<double>(RoughnessItem::P_SIGMA); - - auto material_property = layer.property<ModelView::ExternalProperty>(LayerItem::P_MATERIAL); - auto material = layer.model()->findItem(material_property.identifier()); - // layer which is not linked with material will get (0,0) as SLD material. - double sld_real = material ? material->property<double>(SLDMaterialItem::P_SLD_REAL) : 0.0; - double sld_imag = material ? material->property<double>(SLDMaterialItem::P_SLD_IMAG) : 0.0; - return {complex_t{sld_real, sld_imag}, thickness, sigma}; -} - -//! Adds slices to existing vector of slices using content of a multilayer. -//! Will be called recursively for multilayers inside multilayers. -void AddToMultiSlice(multislice_t& result, const ModelView::SessionItem& multilayer) -{ - for (const auto item : multilayer.getItems(MultiLayerItem::T_LAYERS)) { - if (item->modelType() == GUI::Constants::LayerItemType) { - result.push_back(create_slice(*item)); - } else if (item->modelType() == GUI::Constants::MultiLayerItemType) { - const int rep_count = item->property<int>(MultiLayerItem::P_NREPETITIONS); - for (int i_rep = 0; i_rep < rep_count; ++i_rep) - AddToMultiSlice(result, *item); - } else { - throw std::runtime_error("Error in AddToMultiSlice: unsupported item type."); - } - } -} - -} // namespace - -multislice_t Utils::CreateMultiSlice(const MultiLayerItem& multilayer) -{ - multislice_t result; - AddToMultiSlice(result, multilayer); - return result; -} - -SliceStack Utils::createBornAgainSlices(const multislice_t& multislice) -{ - SliceStack result; - result.reserve(multislice.size()); - - for (auto& slice : multislice) { - auto material = MaterialBySLD("", slice.material.real(), slice.material.imag()); - auto roughness = LayerRoughness(slice.sigma, 0., 0.); - - result.emplace_back(slice.thickness, material, roughness); - } - - return result; -} - -} // namespace gui2 diff --git a/gui2/quicksimeditor/quicksimutils.h b/gui2/quicksimeditor/quicksimutils.h deleted file mode 100644 index fe90a749a53..00000000000 --- a/gui2/quicksimeditor/quicksimutils.h +++ /dev/null @@ -1,40 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/quicksimeditor/quicksimutils.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_QUICKSIMEDITOR_QUICKSIMUTILS_H -#define BORNAGAIN_GUI2_QUICKSIMEDITOR_QUICKSIMUTILS_H - -#include "darefl_export.h" -#include "gui2/quicksimeditor/quicksim_types.h" - -class Slice; -class SliceStack; - -namespace gui2 { - -class MultiLayerItem; - -//! Collection of utility functions for running quick simulations. -namespace Utils { - -//! Creates multi-slice presentation of internal multilayer structure. -DAREFLCORE_EXPORT multislice_t CreateMultiSlice(const MultiLayerItem& multilayer); - -DAREFLCORE_EXPORT SliceStack createBornAgainSlices(const multislice_t& multislice); - -} // namespace Utils - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_QUICKSIMEDITOR_QUICKSIMUTILS_H diff --git a/gui2/quicksimeditor/simplotcontroller.cpp b/gui2/quicksimeditor/simplotcontroller.cpp deleted file mode 100644 index e9e87dad47f..00000000000 --- a/gui2/quicksimeditor/simplotcontroller.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/quicksimeditor/simplotcontroller.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/quicksimeditor/simplotcontroller.h" -#include "gui2/model/applicationmodels.h" -#include "gui2/model/instrumentitems.h" -#include "gui2/model/instrumentmodel.h" -#include "gui2/model/jobitem.h" -#include "gui2/model/jobmodel.h" -#include "mvvm/project/modelhaschangedcontroller.h" - -namespace gui2 { - -SimPlotController::SimPlotController(QObject* parent) : QObject(parent) {} - -void SimPlotController::setModels(ApplicationModels* models) -{ - m_models = models; - - auto on_model_change = [this]() { onInstrumentChange(); }; - m_instrumentChangedController = std::make_unique<ModelView::ModelHasChangedController>( - m_models->instrumentModel(), on_model_change); -} - -void SimPlotController::onInstrumentChange() -{ - auto instrument = m_models->instrumentModel()->topItem<SpecularInstrumentItem>(); - auto graph = instrument->beamItem()->experimentalGraphItem(); - m_models->jobModel()->updateReferenceGraph(graph); -} - -SimPlotController::~SimPlotController() = default; - -} // namespace gui2 diff --git a/gui2/quicksimeditor/simplotcontroller.h b/gui2/quicksimeditor/simplotcontroller.h deleted file mode 100644 index 43496fc98ba..00000000000 --- a/gui2/quicksimeditor/simplotcontroller.h +++ /dev/null @@ -1,50 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/quicksimeditor/simplotcontroller.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_QUICKSIMEDITOR_SIMPLOTCONTROLLER_H -#define BORNAGAIN_GUI2_QUICKSIMEDITOR_SIMPLOTCONTROLLER_H - -#include "darefl_export.h" -#include <QObject> -#include <memory> - -namespace ModelView { -class ModelHasChangedController; -} - -namespace gui2 { - -class ApplicationModels; - -//! Updates reference curve in JobItem when BeamItem is changed. - -class DAREFLCORE_EXPORT SimPlotController : public QObject { - Q_OBJECT - -public: - SimPlotController(QObject* parent = nullptr); - ~SimPlotController(); - - void setModels(ApplicationModels* models); - -private: - void onInstrumentChange(); - - ApplicationModels* m_models{nullptr}; - std::unique_ptr<ModelView::ModelHasChangedController> m_instrumentChangedController; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_QUICKSIMEDITOR_SIMPLOTCONTROLLER_H diff --git a/gui2/quicksimeditor/simplotwidget.cpp b/gui2/quicksimeditor/simplotwidget.cpp deleted file mode 100644 index bc387bf435f..00000000000 --- a/gui2/quicksimeditor/simplotwidget.cpp +++ /dev/null @@ -1,68 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/quicksimeditor/simplotwidget.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/quicksimeditor/simplotwidget.h" -#include "gui2/model/applicationmodels.h" -#include "gui2/model/experimentaldataitems.h" -#include "gui2/model/jobmodel.h" -#include "mvvm/plotting/graphcanvas.h" -#include "mvvm/standarditems/graphviewportitem.h" -#include <QList> -#include <QSplitter> -#include <QVBoxLayout> - -namespace gui2 { - -SimPlotWidget::SimPlotWidget(QWidget* parent) - : QWidget(parent) - , m_specularCanvas(new ModelView::GraphCanvas) - , m_diffCanvas(new ModelView::GraphCanvas) -{ - auto layout = new QVBoxLayout(this); - layout->setContentsMargins(0, 5, 5, 5); - - auto splitter = new QSplitter; - splitter->setOrientation(Qt::Vertical); - - splitter->addWidget(m_specularCanvas); - splitter->addWidget(m_diffCanvas); - - // splitter->setStyleSheet("background-color:white;"); - splitter->setSizes(QList<int>() << 300 << 100); - - layout->addWidget(splitter); - - auto on_axis_margins = [this](int left, int, int right, int) { - // syncronizes left and right margins, leave top and bottom automatic - m_diffCanvas->setAxisMargins(left, -1, right, -1); - }; - connect(m_specularCanvas, &ModelView::GraphCanvas::axisMarginsChanged, on_axis_margins); -} - -SimPlotWidget::~SimPlotWidget() = default; - -void SimPlotWidget::setModels(ApplicationModels* models) -{ - m_models = models; - m_specularCanvas->setItem(m_models->jobModel()->specularViewport()); - m_diffCanvas->setItem(m_models->jobModel()->diffViewport()); -} - -void SimPlotWidget::updateViewport() -{ - m_specularCanvas->setViewportToContent(); - m_diffCanvas->setViewportToContent(); -} - -} // namespace gui2 diff --git a/gui2/quicksimeditor/simplotwidget.h b/gui2/quicksimeditor/simplotwidget.h deleted file mode 100644 index 5fd1c9ef51c..00000000000 --- a/gui2/quicksimeditor/simplotwidget.h +++ /dev/null @@ -1,53 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/quicksimeditor/simplotwidget.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_QUICKSIMEDITOR_SIMPLOTWIDGET_H -#define BORNAGAIN_GUI2_QUICKSIMEDITOR_SIMPLOTWIDGET_H - -#include "darefl_export.h" -#include <QWidget> - -namespace ModelView { -class GraphCanvas; -} // namespace ModelView - -namespace gui2 { - -class ApplicationModels; - -//! Presents simulation results together with reference experimental data on two canvas. -//! The top canvas contains graphs itself, bottom canvas their relative difference. - -class DAREFLCORE_EXPORT SimPlotWidget : public QWidget { - Q_OBJECT - -public: - SimPlotWidget(QWidget* parent = nullptr); - ~SimPlotWidget(); - - void setModels(ApplicationModels* models); - - void updateViewport(); - - void updateDiffPlot(); - -private: - ApplicationModels* m_models{nullptr}; - ModelView::GraphCanvas* m_specularCanvas{nullptr}; - ModelView::GraphCanvas* m_diffCanvas{nullptr}; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_QUICKSIMEDITOR_SIMPLOTWIDGET_H diff --git a/gui2/resources/CMakeLists.txt b/gui2/resources/CMakeLists.txt deleted file mode 100644 index c0cefab2c0c..00000000000 --- a/gui2/resources/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -set(CMAKE_AUTORCC ON) - -target_sources(${library_name} PRIVATE - icons.qrc - resources.h -) - -target_include_directories(${library_name} PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>) diff --git a/gui2/resources/icons.qrc b/gui2/resources/icons.qrc deleted file mode 100644 index b428b8994e9..00000000000 --- a/gui2/resources/icons.qrc +++ /dev/null @@ -1,28 +0,0 @@ -<RCC> - <qresource prefix="/"> - <file>icons</file> - <file>icons/arrow-down-circle-outline.svg</file> - <file>icons/arrow-up-circle-outline.svg</file> - <file>icons/aspect-ratio.svg</file> - <file>icons/beaker-remove-outline.svg</file> - <file>icons/beaker-remove-outline.svg</file> - <file>icons/close-circle-outline.svg</file> - <file>icons/cog-outline.svg</file> - <file>icons/export.svg</file> - <file>icons/import.svg</file> - <file>icons/layers-outline.svg</file> - <file>icons/layers-triple-outline.svg</file> - <file>icons/play-circle-outline.svg</file> - <file>icons/plus-box-multiple-outline.svg</file> - <file>icons/plus-box-outline.svg</file> - <file>icons/plus-circle-outline.svg</file> - <file>icons/redo.svg</file> - <file>icons/undo.svg</file> - <file>icons/set-merge.svg</file> - <file>icons/F-letter_1000x.png</file> - <file>icons/card-bulleted-outline.svg</file> - <file>icons/dock-left.svg</file> - <file>icons/dock-right.svg</file> - <file>icons/plus-circle-multiple-outline.svg</file> - </qresource> -</RCC> diff --git a/gui2/resources/icons/F-letter_1000x.png b/gui2/resources/icons/F-letter_1000x.png deleted file mode 100644 index df05f0de24c9525f53fcbeafd52ce8066c4eb522..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 746456 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⁣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�rCR^R1s$ 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 Y(GLE3&-}HYgMgQ+lBQy{yhZ5$0i&Kr$N&HU diff --git a/gui2/resources/icons/arrow-down-circle-outline.svg b/gui2/resources/icons/arrow-down-circle-outline.svg deleted file mode 100644 index a1054904f58..00000000000 --- a/gui2/resources/icons/arrow-down-circle-outline.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M11,6H13V14L16.5,10.5L17.92,11.92L12,17.84L6.08,11.92L7.5,10.5L11,14V6M12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22M12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20Z" /></svg> \ No newline at end of file diff --git a/gui2/resources/icons/arrow-up-circle-outline.svg b/gui2/resources/icons/arrow-up-circle-outline.svg deleted file mode 100644 index ea6c393ffff..00000000000 --- a/gui2/resources/icons/arrow-up-circle-outline.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M13,18H11V10L7.5,13.5L6.08,12.08L12,6.16L17.92,12.08L16.5,13.5L13,10V18M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z" /></svg> \ No newline at end of file diff --git a/gui2/resources/icons/aspect-ratio.svg b/gui2/resources/icons/aspect-ratio.svg deleted file mode 100644 index 5fce7b199eb..00000000000 --- a/gui2/resources/icons/aspect-ratio.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M19,12H17V15H14V17H19V12M7,9H10V7H5V12H7V9M21,3H3A2,2 0 0,0 1,5V19A2,2 0 0,0 3,21H21A2,2 0 0,0 23,19V5A2,2 0 0,0 21,3M21,19H3V5H21V19Z" /></svg> \ No newline at end of file diff --git a/gui2/resources/icons/beaker-remove-outline.svg b/gui2/resources/icons/beaker-remove-outline.svg deleted file mode 100644 index 931e4ad9fb7..00000000000 --- a/gui2/resources/icons/beaker-remove-outline.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M15.46 15.88L16.88 14.46L19 16.59L21.12 14.47L22.53 15.88L20.41 18L22.54 20.12L21.12 21.54L19 19.41L16.88 21.53L15.47 20.12L17.59 18L15.46 15.88M3 3H21V5C19.9 5 19 5.9 19 7V12C18.3 12 17.63 12.12 17 12.34V5H7V7H12V8H7V9H10V10H7V11H10V12H7V13H12V14H7V15H10V16H7V19H13.08C13.2 19.72 13.45 20.39 13.8 21H7C5.9 21 5 20.11 5 19V7C5 5.9 4.11 5 3 5V3Z" /></svg> \ No newline at end of file diff --git a/gui2/resources/icons/card-bulleted-outline.svg b/gui2/resources/icons/card-bulleted-outline.svg deleted file mode 100644 index d09c415c8c7..00000000000 --- a/gui2/resources/icons/card-bulleted-outline.svg +++ /dev/null @@ -1,54 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - inkscape:version="1.0 (4035a4fb49, 2020-05-01)" - sodipodi:docname="card-bulleted-outline.svg" - id="svg4" - viewBox="0 0 24 24" - height="24" - width="24" - version="1.1"> - <metadata - id="metadata10"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - </cc:Work> - </rdf:RDF> - </metadata> - <defs - id="defs8" /> - <sodipodi:namedview - inkscape:current-layer="svg4" - inkscape:window-maximized="0" - inkscape:window-y="0" - inkscape:window-x="0" - inkscape:cy="12" - inkscape:cx="12" - inkscape:zoom="29.791667" - showgrid="false" - id="namedview6" - inkscape:window-height="1312" - inkscape:window-width="1922" - inkscape:pageshadow="2" - inkscape:pageopacity="0" - guidetolerance="10" - gridtolerance="10" - objecttolerance="10" - borderopacity="1" - bordercolor="#666666" - pagecolor="#ffffff" /> - <path - style="fill:#424446;fill-opacity:1" - id="path2" - d="M12,15H10V13H12V15M18,15H14V13H18V15M8,11H6V9H8V11M18,11H10V9H18V11M20,20H4A2,2 0 0,1 2,18V6A2,2 0 0,1 4,4H20A2,2 0 0,1 22,6V18A2,2 0 0,1 20,20M4,6V18H20V6H4Z" /> -</svg> diff --git a/gui2/resources/icons/close-circle-outline.svg b/gui2/resources/icons/close-circle-outline.svg deleted file mode 100644 index dad58cf89bf..00000000000 --- a/gui2/resources/icons/close-circle-outline.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,2C6.47,2 2,6.47 2,12C2,17.53 6.47,22 12,22C17.53,22 22,17.53 22,12C22,6.47 17.53,2 12,2M14.59,8L12,10.59L9.41,8L8,9.41L10.59,12L8,14.59L9.41,16L12,13.41L14.59,16L16,14.59L13.41,12L16,9.41L14.59,8Z" /></svg> \ No newline at end of file diff --git a/gui2/resources/icons/cog-outline.svg b/gui2/resources/icons/cog-outline.svg deleted file mode 100644 index d7ed72b2d8c..00000000000 --- a/gui2/resources/icons/cog-outline.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M12,8A4,4 0 0,1 16,12A4,4 0 0,1 12,16A4,4 0 0,1 8,12A4,4 0 0,1 12,8M12,10A2,2 0 0,0 10,12A2,2 0 0,0 12,14A2,2 0 0,0 14,12A2,2 0 0,0 12,10M10,22C9.75,22 9.54,21.82 9.5,21.58L9.13,18.93C8.5,18.68 7.96,18.34 7.44,17.94L4.95,18.95C4.73,19.03 4.46,18.95 4.34,18.73L2.34,15.27C2.21,15.05 2.27,14.78 2.46,14.63L4.57,12.97L4.5,12L4.57,11L2.46,9.37C2.27,9.22 2.21,8.95 2.34,8.73L4.34,5.27C4.46,5.05 4.73,4.96 4.95,5.05L7.44,6.05C7.96,5.66 8.5,5.32 9.13,5.07L9.5,2.42C9.54,2.18 9.75,2 10,2H14C14.25,2 14.46,2.18 14.5,2.42L14.87,5.07C15.5,5.32 16.04,5.66 16.56,6.05L19.05,5.05C19.27,4.96 19.54,5.05 19.66,5.27L21.66,8.73C21.79,8.95 21.73,9.22 21.54,9.37L19.43,11L19.5,12L19.43,13L21.54,14.63C21.73,14.78 21.79,15.05 21.66,15.27L19.66,18.73C19.54,18.95 19.27,19.04 19.05,18.95L16.56,17.95C16.04,18.34 15.5,18.68 14.87,18.93L14.5,21.58C14.46,21.82 14.25,22 14,22H10M11.25,4L10.88,6.61C9.68,6.86 8.62,7.5 7.85,8.39L5.44,7.35L4.69,8.65L6.8,10.2C6.4,11.37 6.4,12.64 6.8,13.8L4.68,15.36L5.43,16.66L7.86,15.62C8.63,16.5 9.68,17.14 10.87,17.38L11.24,20H12.76L13.13,17.39C14.32,17.14 15.37,16.5 16.14,15.62L18.57,16.66L19.32,15.36L17.2,13.81C17.6,12.64 17.6,11.37 17.2,10.2L19.31,8.65L18.56,7.35L16.15,8.39C15.38,7.5 14.32,6.86 13.12,6.62L12.75,4H11.25Z" /></svg> \ No newline at end of file diff --git a/gui2/resources/icons/dock-left.svg b/gui2/resources/icons/dock-left.svg deleted file mode 100644 index 3b5ffd7a11d..00000000000 --- a/gui2/resources/icons/dock-left.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M20 4H4A2 2 0 0 0 2 6V18A2 2 0 0 0 4 20H20A2 2 0 0 0 22 18V6A2 2 0 0 0 20 4M20 18H9V6H20Z" /></svg> \ No newline at end of file diff --git a/gui2/resources/icons/dock-right.svg b/gui2/resources/icons/dock-right.svg deleted file mode 100644 index 8344eae8a95..00000000000 --- a/gui2/resources/icons/dock-right.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M20 4H4A2 2 0 0 0 2 6V18A2 2 0 0 0 4 20H20A2 2 0 0 0 22 18V6A2 2 0 0 0 20 4M15 18H4V6H15Z" /></svg> \ No newline at end of file diff --git a/gui2/resources/icons/export.svg b/gui2/resources/icons/export.svg deleted file mode 100644 index cf6f1595fd0..00000000000 --- a/gui2/resources/icons/export.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M23,12L19,8V11H10V13H19V16M1,18V6C1,4.89 1.9,4 3,4H15A2,2 0 0,1 17,6V9H15V6H3V18H15V15H17V18A2,2 0 0,1 15,20H3A2,2 0 0,1 1,18Z" /></svg> \ No newline at end of file diff --git a/gui2/resources/icons/import.svg b/gui2/resources/icons/import.svg deleted file mode 100644 index 2cc83b8399c..00000000000 --- a/gui2/resources/icons/import.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M14,12L10,8V11H2V13H10V16M20,18V6C20,4.89 19.1,4 18,4H6A2,2 0 0,0 4,6V9H6V6H18V18H6V15H4V18A2,2 0 0,0 6,20H18A2,2 0 0,0 20,18Z" /></svg> \ No newline at end of file diff --git a/gui2/resources/icons/layers-outline.svg b/gui2/resources/icons/layers-outline.svg deleted file mode 100644 index 3b376e42461..00000000000 --- a/gui2/resources/icons/layers-outline.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M12,18.54L19.37,12.8L21,14.07L12,21.07L3,14.07L4.62,12.81L12,18.54M12,16L3,9L12,2L21,9L12,16M12,4.53L6.26,9L12,13.47L17.74,9L12,4.53Z" /></svg> \ No newline at end of file diff --git a/gui2/resources/icons/layers-triple-outline.svg b/gui2/resources/icons/layers-triple-outline.svg deleted file mode 100644 index d074e884c2b..00000000000 --- a/gui2/resources/icons/layers-triple-outline.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M12 16.54L19.37 10.8L21 12.07L12 19.07L3 12.07L4.62 10.81L12 16.54M12 14L3 7L12 0L21 7L12 14M12 2.53L6.26 7L12 11.47L17.74 7L12 2.53M12 21.47L19.37 15.73L21 17L12 24L3 17L4.62 15.74L12 21.47" /></svg> \ No newline at end of file diff --git a/gui2/resources/icons/play-circle-outline.svg b/gui2/resources/icons/play-circle-outline.svg deleted file mode 100644 index 792051fe486..00000000000 --- a/gui2/resources/icons/play-circle-outline.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M10,16.5L16,12L10,7.5V16.5Z" /></svg> \ No newline at end of file diff --git a/gui2/resources/icons/plus-box-multiple-outline.svg b/gui2/resources/icons/plus-box-multiple-outline.svg deleted file mode 100644 index 7857512bf32..00000000000 --- a/gui2/resources/icons/plus-box-multiple-outline.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M18 11H15V14H13V11H10V9H13V6H15V9H18M20 4V16H8V4H20M20 2H8C6.9 2 6 2.9 6 4V16C6 17.11 6.9 18 8 18H20C21.11 18 22 17.11 22 16V4C22 2.9 21.11 2 20 2M4 6H2V20C2 21.11 2.9 22 4 22H18V20H4V6Z" /></svg> \ No newline at end of file diff --git a/gui2/resources/icons/plus-box-outline.svg b/gui2/resources/icons/plus-box-outline.svg deleted file mode 100644 index b00356cd36d..00000000000 --- a/gui2/resources/icons/plus-box-outline.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M19,19V5H5V19H19M19,3A2,2 0 0,1 21,5V19A2,2 0 0,1 19,21H5A2,2 0 0,1 3,19V5C3,3.89 3.9,3 5,3H19M11,7H13V11H17V13H13V17H11V13H7V11H11V7Z" /></svg> \ No newline at end of file diff --git a/gui2/resources/icons/plus-circle-multiple-outline.svg b/gui2/resources/icons/plus-circle-multiple-outline.svg deleted file mode 100644 index 001d9824254..00000000000 --- a/gui2/resources/icons/plus-circle-multiple-outline.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M16,8H14V11H11V13H14V16H16V13H19V11H16M2,12C2,9.21 3.64,6.8 6,5.68V3.5C2.5,4.76 0,8.09 0,12C0,15.91 2.5,19.24 6,20.5V18.32C3.64,17.2 2,14.79 2,12M15,3C10.04,3 6,7.04 6,12C6,16.96 10.04,21 15,21C19.96,21 24,16.96 24,12C24,7.04 19.96,3 15,3M15,19C11.14,19 8,15.86 8,12C8,8.14 11.14,5 15,5C18.86,5 22,8.14 22,12C22,15.86 18.86,19 15,19Z" /></svg> \ No newline at end of file diff --git a/gui2/resources/icons/plus-circle-outline.svg b/gui2/resources/icons/plus-circle-outline.svg deleted file mode 100644 index 3fe6bec7ebb..00000000000 --- a/gui2/resources/icons/plus-circle-outline.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M13,7H11V11H7V13H11V17H13V13H17V11H13V7Z" /></svg> \ No newline at end of file diff --git a/gui2/resources/icons/redo.svg b/gui2/resources/icons/redo.svg deleted file mode 100644 index 54af62edc72..00000000000 --- a/gui2/resources/icons/redo.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M18.4,10.6C16.55,9 14.15,8 11.5,8C6.85,8 2.92,11.03 1.54,15.22L3.9,16C4.95,12.81 7.95,10.5 11.5,10.5C13.45,10.5 15.23,11.22 16.62,12.38L13,16H22V7L18.4,10.6Z" /></svg> \ No newline at end of file diff --git a/gui2/resources/icons/set-merge.svg b/gui2/resources/icons/set-merge.svg deleted file mode 100644 index 4869b87086c..00000000000 --- a/gui2/resources/icons/set-merge.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M2 7V9H7V7H2M12 9V11H9V13H12V15L15 12L12 9M17 9V15H22V9H17M2 11V13H7V11H2M2 15V17H7V15H2Z" /></svg> \ No newline at end of file diff --git a/gui2/resources/icons/undo.svg b/gui2/resources/icons/undo.svg deleted file mode 100644 index 797d4d36804..00000000000 --- a/gui2/resources/icons/undo.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M12.5,8C9.85,8 7.45,9 5.6,10.6L2,7V16H11L7.38,12.38C8.77,11.22 10.54,10.5 12.5,10.5C16.04,10.5 19.05,12.81 20.1,16L22.47,15.22C21.08,11.03 17.15,8 12.5,8Z" /></svg> \ No newline at end of file diff --git a/gui2/resources/resources.h b/gui2/resources/resources.h deleted file mode 100644 index 464f01d213e..00000000000 --- a/gui2/resources/resources.h +++ /dev/null @@ -1,25 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/resources/resources.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_RESOURCES_RESOURCES_H -#define BORNAGAIN_GUI2_RESOURCES_RESOURCES_H - -#include <QtGlobal> - -inline void InitIconResources() -{ - Q_INIT_RESOURCE(icons); -} - -#endif // BORNAGAIN_GUI2_RESOURCES_RESOURCES_H diff --git a/gui2/settingsview/CMakeLists.txt b/gui2/settingsview/CMakeLists.txt deleted file mode 100644 index 332b321d7a2..00000000000 --- a/gui2/settingsview/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -target_sources(${library_name} PRIVATE - settingsview.cpp - settingsview.h -) diff --git a/gui2/settingsview/settingsview.cpp b/gui2/settingsview/settingsview.cpp deleted file mode 100644 index af103356cb9..00000000000 --- a/gui2/settingsview/settingsview.cpp +++ /dev/null @@ -1,83 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/settingsview/settingsview.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/settingsview/settingsview.h" -#include "gui2/model/applicationmodels.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/widgets/allitemstreeview.h" -#include "mvvm/widgets/widgetutils.h" -#include <QHBoxLayout> -#include <QListView> -#include <QListWidget> -#include <QStackedWidget> -#include <QTabWidget> -#include <QTreeView> - -namespace gui2 { - -SettingsView::SettingsView(ApplicationModels* models, QWidget* parent) - : QWidget(parent) - , m_listWidget(new QListWidget) - , m_stackedWidget(new QStackedWidget) - , m_tabWidget(new QTabWidget) - , m_models(models) -{ - init_list_selector(); - init_model_settings(); - init_other_settings(); - - auto layout = new QHBoxLayout(this); - layout->addWidget(m_listWidget); - layout->addSpacing(ModelView::Utils::WidthOfLetterM() / 2); - layout->addWidget(m_stackedWidget); - - m_stackedWidget->setCurrentIndex(0); -} - -//! Initialize tabs with model content. -//! Each model will be represented by a single tree (with all items shown) in a tab. - -void SettingsView::init_model_settings() -{ - for (auto model : m_models->application_models()) { - auto view = new ModelView::AllItemsTreeView(model); - view->treeView()->setAlternatingRowColors(true); - m_tabWidget->addTab(view, QString::fromStdString(model->modelType())); - } - m_stackedWidget->addWidget(m_tabWidget); -} - -void SettingsView::init_list_selector() -{ - const int width = ModelView::Utils::WidthOfLetterM() * 10; - m_listWidget->setFixedWidth(width); - m_listWidget->setIconSize( - QSize(ModelView::Utils::WidthOfLetterM() * 1.2, ModelView::Utils::WidthOfLetterM() * 1.2)); - - auto item = new QListWidgetItem(QIcon(":/icons/card-bulleted-outline.svg"), "All models"); - m_listWidget->addItem(item); - - item = new QListWidgetItem(QIcon(":/icons/cog-outline.svg"), "Miscellaneous"); - m_listWidget->addItem(item); - - connect(m_listWidget, &QListWidget::currentRowChanged, - [this](int row) { m_stackedWidget->setCurrentIndex(row); }); -} - -void SettingsView::init_other_settings() -{ - m_stackedWidget->addWidget(new QWidget); -} - -} // namespace gui2 diff --git a/gui2/settingsview/settingsview.h b/gui2/settingsview/settingsview.h deleted file mode 100644 index c54c58c069d..00000000000 --- a/gui2/settingsview/settingsview.h +++ /dev/null @@ -1,53 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/settingsview/settingsview.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_SETTINGSVIEW_SETTINGSVIEW_H -#define BORNAGAIN_GUI2_SETTINGSVIEW_SETTINGSVIEW_H - -#include "darefl_export.h" -#include <QWidget> - -class QTabWidget; -class QStackedWidget; -class QListWidget; - -namespace gui2 { - -class ApplicationModels; - -//! Main settings view, belongs directly to MainWindow. -//! For the moment contains QTabWidget with trees representating all application models. -//! In the future, might be extended to have any type of settings. - -class DAREFLCORE_EXPORT SettingsView : public QWidget { - Q_OBJECT - -public: - SettingsView(ApplicationModels* models, QWidget* parent = nullptr); - -private: - void init_list_selector(); - void init_model_settings(); - void init_other_settings(); - - QListWidget* m_listWidget; //!< selector for specific settings window on the left - QStackedWidget* m_stackedWidget{nullptr}; //!< stack with settings widgets - QTabWidget* m_tabWidget{nullptr}; //!< application model settings - - ApplicationModels* m_models{nullptr}; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_SETTINGSVIEW_SETTINGSVIEW_H diff --git a/gui2/sldeditor/CMakeLists.txt b/gui2/sldeditor/CMakeLists.txt deleted file mode 100644 index dc72eb3b3ff..00000000000 --- a/gui2/sldeditor/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -target_sources(${library_name} PRIVATE - sldelementmodel.h - sldelementmodel.cpp - layerelementcontroller.h - layerelementcontroller.cpp - layerelementitem.h - layerelementitem.cpp - sldelementcontroller.cpp - sldelementcontroller.h - elementview.h - elementview.cpp - segmentelementview.h - segmentelementview.cpp - handleelementview.h - handleelementview.cpp - roughnesselementview.h - roughnesselementview.cpp - graphicsscene.cpp - graphicsscene.h - sldviewwidget.h - sldviewwidget.cpp - sldeditor.h - sldeditor.cpp - sldeditoractions.h - sldeditoractions.cpp - sldeditortoolbar.h - sldeditortoolbar.cpp -) diff --git a/gui2/sldeditor/elementview.cpp b/gui2/sldeditor/elementview.cpp deleted file mode 100644 index ab0555026e4..00000000000 --- a/gui2/sldeditor/elementview.cpp +++ /dev/null @@ -1,265 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/sldeditor/elementview.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/sldeditor/elementview.h" -#include "gui2/sldeditor/graphicsscene.h" -#include "mvvm/plotting/sceneadapterinterface.h" - -#include <QCursor> -#include <QPainter> -#include <QPainterPath> - -namespace gui2 { - -//! The constructor -ElementView::ElementView() : QGraphicsObject() -{ - setAcceptHoverEvents(true); -} - -ElementView::~ElementView() = default; - -//! Get the conversion axes -ModelView::SceneAdapterInterface* ElementView::sceneAdapter() const -{ - GraphicsScene* scene_item = static_cast<GraphicsScene*>(scene()); - if (!scene_item) - return nullptr; - - return scene_item->sceneAdapter(); -} - -//! Advance method used by the scene adapter -void ElementView::advance(int phase) -{ - if (!phase) - return; - prepareGeometryChange(); - update(); -} - -//! paint override -void ElementView::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, - QWidget* /*widget*/) -{ - painter->setClipRect(sceneAdapter()->viewportRectangle()); -} - -//! modify the rectangle for display according to the scene adapter -QRectF ElementView::displayRect(const QRectF& real_rect) const -{ - auto adapter = sceneAdapter(); - if (!adapter) - return real_rect; - - auto output = QRectF(real_rect); - - if (m_center_based) { - output = displayRectCenterBased(real_rect); - } else { - output = displayRectEdgeBased(real_rect); - } - - if (m_stretch_left) { - output = stretchRectLeft(output); - } else if (m_stretch_right) { - output = stretchRectRight(output); - } - - return output; -} - -//! Helper function for displayRect based on the center of real_rect -QRectF ElementView::displayRectCenterBased(const QRectF& real_rect) const -{ - auto adapter = sceneAdapter(); - double x = real_rect.x(); - double y = real_rect.y(); - double w = real_rect.width(); - double h = real_rect.height(); - - double center_x = x + w / 2.; - double center_y = y + h / 2.; - - if (m_adapt_x) { - center_x = adapter->toSceneX(-center_x); - } - if (m_adapt_y) { - center_y = adapter->toSceneY(center_y); - } - if (m_adapt_width) { - w = adapter->toSceneX(w) - adapter->toSceneX(0); - } - if (m_adapt_height) { - h = adapter->toSceneY(h) - adapter->toSceneY(0); - } - - x = center_x - w / 2; - y = center_y - h / 2; - - return QRectF(x, y, w, h); -} - -//! Helper function for displayRect based on the edge of real_rect -QRectF ElementView::displayRectEdgeBased(const QRectF& real_rect) const -{ - auto adapter = sceneAdapter(); - double x = real_rect.x(); - double y = real_rect.y(); - double w = real_rect.width(); - double h = real_rect.height(); - - if (m_adapt_x) { - x = adapter->toSceneX(-x); - } - if (m_adapt_y) { - y = adapter->toSceneY(y); - } - if (m_adapt_width) { - w = adapter->toSceneX(w) - adapter->toSceneX(0); - } - if (m_adapt_height) { - h = adapter->toSceneY(h) - adapter->toSceneY(0); - } - - return QRectF(x, y, w, h); -} - -//! Stretch the rectangle to the left limit of the viewport -QRectF ElementView::stretchRectLeft(const QRectF& real_rect) const -{ - double x_i = real_rect.x(); - double y_i = real_rect.y(); - double x_f = real_rect.x() + real_rect.width(); - double y_f = real_rect.y() + real_rect.height(); - - auto viewport_rect = sceneAdapter()->viewportRectangle(); - x_i = viewport_rect.x(); - - return QRectF(x_i, y_i, x_f - x_i, y_f - y_i); -} - -//! Stretch the rectangle to the right limit of the viewport -QRectF ElementView::stretchRectRight(const QRectF& real_rect) const -{ - double x_i = real_rect.x(); - double y_i = real_rect.y(); - double x_f = real_rect.x() + real_rect.width(); - double y_f = real_rect.y() + real_rect.height(); - - auto viewport_rect = sceneAdapter()->viewportRectangle(); - x_f = viewport_rect.x() + viewport_rect.width(); - - return QRectF(x_i, y_i, x_f - x_i, y_f - y_i); -} - -//! modify the path for display according to the scene adapter -QPainterPath ElementView::displayPath(QPainterPath real_path) const -{ - auto adapter = sceneAdapter(); - if (!adapter) - return real_path; - - auto display_path = QPainterPath(real_path); - for (int i = 0; i < display_path.elementCount(); i++) { - QPointF pt = display_path.elementAt(i); - display_path.setElementPositionAt(i, adapter->toSceneX(-pt.x()), adapter->toSceneY(pt.y())); - } - return display_path; -} - -//! modify the rectangle for display according to the scene adapter -QPointF ElementView::scenePos(QPointF pixel_pos) const -{ - auto adapter = sceneAdapter(); - if (!adapter) - return pixel_pos; - - return QPointF(-adapter->fromSceneX(pixel_pos.x()), adapter->fromSceneY(pixel_pos.y())); -} - -//! Adapt the dimensions according to the center -void ElementView::setCenterBased(bool choice) -{ - m_center_based = choice; -} - -//! Adapt the x position -void ElementView::adaptX(bool choice) -{ - m_adapt_x = choice; -} - -//! Adapt the y position -void ElementView::adaptY(bool choice) -{ - m_adapt_y = choice; -} - -//! Adapt the width -void ElementView::adaptW(bool choice) -{ - m_adapt_width = choice; -} - -//! Adapt the height -void ElementView::adaptH(bool choice) -{ - m_adapt_height = choice; -} - -//! Stretch the rectangle to the left limit of the viewport -void ElementView::stretchLeft(bool choice) -{ - m_stretch_left = choice; -} - -//! Stretch the rectangle to the right limit of the viewport -void ElementView::stretchRight(bool choice) -{ - m_stretch_right = choice; -} - -//! The hoover enter event -void ElementView::hoverEnterEvent(QGraphicsSceneHoverEvent* event) -{ - if (flags() & QGraphicsItem::ItemIsMovable) - setCursor(QCursor(Qt::OpenHandCursor)); - QGraphicsItem::hoverEnterEvent(event); -} - -//! The hoover exit event -void ElementView::hoverLeaveEvent(QGraphicsSceneHoverEvent* event) -{ - unsetCursor(); - QGraphicsItem::hoverLeaveEvent(event); -} - -//! The mouse press event -void ElementView::mousePressEvent(QGraphicsSceneMouseEvent* event) -{ - if (flags() & QGraphicsItem::ItemIsMovable) - setCursor(QCursor(Qt::ClosedHandCursor)); - QGraphicsItem::mousePressEvent(event); -} - -//! The mouse release event -void ElementView::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) -{ - if (flags() & QGraphicsItem::ItemIsMovable) - setCursor(QCursor(Qt::OpenHandCursor)); - QGraphicsItem::mouseReleaseEvent(event); -} - -} // namespace gui2 diff --git a/gui2/sldeditor/elementview.h b/gui2/sldeditor/elementview.h deleted file mode 100644 index 4296b597764..00000000000 --- a/gui2/sldeditor/elementview.h +++ /dev/null @@ -1,74 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/sldeditor/elementview.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_SLDEDITOR_ELEMENTVIEW_H -#define BORNAGAIN_GUI2_SLDEDITOR_ELEMENTVIEW_H - -#include "darefl_export.h" -#include "gui2/sldeditor/graphicsscene.h" - -#include <QGraphicsObject> - -namespace gui2 { - -//! The interface of any QGraphicsViewItem on GraphicsScene to the Sceneadapter -class DAREFLCORE_EXPORT ElementView : public QGraphicsObject { - Q_OBJECT - -public: - ElementView(); - ~ElementView(); - - void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override; - void advance(int phase) override; - - ModelView::SceneAdapterInterface* sceneAdapter() const; - - void setCenterBased(bool choice); - void adaptX(bool choice); - void adaptY(bool choice); - void adaptW(bool choice); - void adaptH(bool choice); - void stretchLeft(bool choice); - void stretchRight(bool choice); - -protected: - QRectF displayRect(const QRectF& real_rect) const; - QPainterPath displayPath(QPainterPath real_path) const; - QPointF scenePos(QPointF pixel_pos) const; - - void hoverEnterEvent(QGraphicsSceneHoverEvent* event) override; - void hoverLeaveEvent(QGraphicsSceneHoverEvent* event) override; - void mousePressEvent(QGraphicsSceneMouseEvent* event) override; - void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) override; - -private: - QRectF displayRectCenterBased(const QRectF& real_rect) const; - QRectF displayRectEdgeBased(const QRectF& real_rect) const; - QRectF stretchRectLeft(const QRectF& real_rect) const; - QRectF stretchRectRight(const QRectF& real_rect) const; - -private: - bool m_center_based = true; - bool m_adapt_x = true; - bool m_adapt_y = true; - bool m_adapt_width = true; - bool m_adapt_height = true; - bool m_stretch_left = false; - bool m_stretch_right = false; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_SLDEDITOR_ELEMENTVIEW_H diff --git a/gui2/sldeditor/graphicsscene.cpp b/gui2/sldeditor/graphicsscene.cpp deleted file mode 100644 index 18f3f8bfc16..00000000000 --- a/gui2/sldeditor/graphicsscene.cpp +++ /dev/null @@ -1,87 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/sldeditor/graphicsscene.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/sldeditor/graphicsscene.h" - -#include "mvvm/model/modelutils.h" -#include "mvvm/plotting/customplotproxywidget.h" -#include "mvvm/plotting/graphcanvas.h" -#include "mvvm/plotting/sceneadapterinterface.h" -#include "mvvm/standarditems/graphviewportitem.h" - -namespace { -const double scene_origin_x{0.0}; -const double scene_origin_y{0.0}; -const QRectF default_scene_rect{QPointF{scene_origin_x, scene_origin_y}, QSizeF{800, 600}}; -} // namespace - -namespace gui2 { - -//! The contructor -GraphicsScene::GraphicsScene(QObject* parent) : QGraphicsScene(parent) -{ - setItemIndexMethod(QGraphicsScene::NoIndex); - setSceneRect(default_scene_rect); - setContext(); -} - -//! The destructor -GraphicsScene::~GraphicsScene() = default; - -//! Initialise the GraphicsScene with its elements -void GraphicsScene::setContext() -{ - graph_canvas = new ModelView::GraphCanvas; - createPlotProxy(graph_canvas); -} - -//! Set te graph canvas item -void GraphicsScene::setItem(ModelView::GraphViewportItem* viewport_item) -{ - graph_canvas->setItem(viewport_item); -} - -//! Set te graph canvas item -ModelView::GraphCanvas* GraphicsScene::graphCanvas() const -{ - return graph_canvas; -} - -//! Adjust size of scene and color map proxy. -void GraphicsScene::update_size(const QSize& newSize) -{ - if (plot_proxy) { - plot_proxy->resize(newSize); - setSceneRect(scene_origin_x, scene_origin_y, newSize.width(), newSize.height()); - plot_proxy->setPos(0.0, 0.0); - advance(); // notifies all QGraphicsItem that it is time to replot themself using new status - // of scene adapter - } -} - -//! Create the Proxy item -void GraphicsScene::createPlotProxy(ModelView::GraphCanvas* plot_canvas) -{ - scene_adapter = plot_canvas->createSceneAdapter(); - plot_proxy = new ModelView::CustomPlotProxyWidget(plot_canvas); - addItem(plot_proxy); -} - -//! Return the pointer of the scene adapter on request -ModelView::SceneAdapterInterface* GraphicsScene::sceneAdapter() const -{ - return scene_adapter.get(); -} - -} // namespace gui2 diff --git a/gui2/sldeditor/graphicsscene.h b/gui2/sldeditor/graphicsscene.h deleted file mode 100644 index e4c32a6a5c1..00000000000 --- a/gui2/sldeditor/graphicsscene.h +++ /dev/null @@ -1,56 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/sldeditor/graphicsscene.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_SLDEDITOR_GRAPHICSSCENE_H -#define BORNAGAIN_GUI2_SLDEDITOR_GRAPHICSSCENE_H - -#include "darefl_export.h" -#include <QGraphicsScene> -#include <memory> - -namespace ModelView { -class GraphCanvas; -class SceneAdapterInterface; -class GraphViewportItem; -class CustomPlotProxyWidget; -} // namespace ModelView - -namespace gui2 { - -class RegionOfInterestItem; - -//! Custom graphics scene to show QCustomPlot with additional elements on top. -class DAREFLCORE_EXPORT GraphicsScene : public QGraphicsScene { - Q_OBJECT - -public: - GraphicsScene(QObject* parent = nullptr); - ~GraphicsScene() override; - - void setContext(); - void setItem(ModelView::GraphViewportItem* viewport_item); - ModelView::GraphCanvas* graphCanvas() const; - ModelView::SceneAdapterInterface* sceneAdapter() const; - void update_size(const QSize& newSize); - -private: - void createPlotProxy(ModelView::GraphCanvas* plot_canvas); - ModelView::CustomPlotProxyWidget* plot_proxy{nullptr}; - std::unique_ptr<ModelView::SceneAdapterInterface> scene_adapter; - ModelView::GraphCanvas* graph_canvas{nullptr}; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_SLDEDITOR_GRAPHICSSCENE_H diff --git a/gui2/sldeditor/handleelementview.cpp b/gui2/sldeditor/handleelementview.cpp deleted file mode 100644 index 4593ef4e7ac..00000000000 --- a/gui2/sldeditor/handleelementview.cpp +++ /dev/null @@ -1,98 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/sldeditor/handleelementview.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/sldeditor/handleelementview.h" -#include "gui2/sldeditor/layerelementcontroller.h" - -#include <QGraphicsSceneMouseEvent> -#include <QPainter> -#include <QStyleOption> - -namespace gui2 { - -//! The constructor -HandleElementView::HandleElementView() - : ElementView() - , m_pos(QPointF(0, 0)) - , m_rectangle(QRectF(0, 0, 0, 0)) - , m_brush(QBrush()) - , m_pen(QPen()) -{ - adaptW(false); - adaptH(false); - setZValue(2); -} - -//! The overriden paint method -void HandleElementView::paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*) -{ - painter->setClipRect(sceneAdapter()->viewportRectangle()); - painter->setPen(m_pen); - painter->setBrush(m_brush); - painter->drawEllipse(displayRect(m_rectangle)); -} - -//! The shape -QPainterPath HandleElementView::shape() const -{ - QPainterPath path; - path.addRect(displayRect(m_rectangle)); - return path; -} - -//! The bounding rectangle of the handle -QRectF HandleElementView::boundingRect() const -{ - return displayRect(m_rectangle); -} - -//! On move update the model -void HandleElementView::mouseMoveEvent(QGraphicsSceneMouseEvent* event) -{ - m_pos = scenePos(event->pos()); - p_controller->handleViewMoved(this); -} - -//! Set the controller to report back the move -void HandleElementView::setLayerElementController(LayerElementController* controller) -{ - p_controller = controller; -} - -//! Set the draw rectangle -void HandleElementView::setRectangle(QRectF rectangle) -{ - prepareGeometryChange(); - m_rectangle = rectangle; -} - -//! Set the brush -void HandleElementView::setBrush(QBrush brush) -{ - m_brush = brush; -} - -//! Set the pen -void HandleElementView::setPen(QPen pen) -{ - m_pen = pen; -} - -//! Get the last position of the item -QPointF HandleElementView::getLastPos() const -{ - return m_pos; -} - -} // namespace gui2 diff --git a/gui2/sldeditor/handleelementview.h b/gui2/sldeditor/handleelementview.h deleted file mode 100644 index 5d7a03a301e..00000000000 --- a/gui2/sldeditor/handleelementview.h +++ /dev/null @@ -1,56 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/sldeditor/handleelementview.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_SLDEDITOR_HANDLEELEMENTVIEW_H -#define BORNAGAIN_GUI2_SLDEDITOR_HANDLEELEMENTVIEW_H - -#include "darefl_export.h" -#include "gui2/sldeditor/elementview.h" - -#include <QBrush> -#include <QPen> -#include <QRectF> - -namespace gui2 { - -class LayerElementController; - -//! The handle QGraphicsViewItem on GraphicsScene -class DAREFLCORE_EXPORT HandleElementView : public ElementView { -public: - HandleElementView(); - QRectF boundingRect() const override; - QPainterPath shape() const override; - void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override; - - void setLayerElementController(LayerElementController* controller); - void setRectangle(QRectF rectangle); - void setBrush(QBrush brush); - void setPen(QPen pen); - QPointF getLastPos() const; - -public: - void mouseMoveEvent(QGraphicsSceneMouseEvent* event) override; - -protected: - LayerElementController* p_controller; - QPointF m_pos; - QRectF m_rectangle; - QBrush m_brush; - QPen m_pen; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_SLDEDITOR_HANDLEELEMENTVIEW_H diff --git a/gui2/sldeditor/layerelementcontroller.cpp b/gui2/sldeditor/layerelementcontroller.cpp deleted file mode 100644 index 3f8b8670ca2..00000000000 --- a/gui2/sldeditor/layerelementcontroller.cpp +++ /dev/null @@ -1,912 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/sldeditor/layerelementcontroller.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/sldeditor/layerelementcontroller.h" - -#include "gui2/sldeditor/graphicsscene.h" -#include "gui2/sldeditor/handleelementview.h" -#include "gui2/sldeditor/layerelementitem.h" -#include "gui2/sldeditor/roughnesselementview.h" -#include "gui2/sldeditor/segmentelementview.h" - -#include "mvvm/signals/itemmapper.h" -#include <stdexcept> -#include <string> - -using namespace ModelView; - -namespace gui2 { - -//! Constructor -LayerElementController::LayerElementController(LayerElementItem* layer_view_item) - : QObject(), p_model_item(layer_view_item), m_sample_item_id(" ") -{ -} - -//! Returns the pointer to the LayerElementItem in the model -LayerElementItem* LayerElementController::layerElementItem() const -{ - return p_model_item; -} - -//! Allow the population of the own elements -void LayerElementController::autoPopulate() -{ - setSideSegment(new SegmentElementView()); - setTopSegment(new SegmentElementView()); - setSegmentHandles(new HandleElementView(), new HandleElementView()); - setRoughness(new RoughnessElementView()); - setRoughnessHandles(new HandleElementView(), new HandleElementView()); -} - -//! If loacally created the view elements nees to be locally destroyed -void LayerElementController::deleteViewItems() -{ - if (m_segment_views[0]) { - auto temp_ptr = m_segment_views[0]; - unsetSideSegment(); - delete temp_ptr; - } - if (m_segment_views[1]) { - auto temp_ptr = m_segment_views[1]; - unsetTopSegment(); - delete temp_ptr; - } - if (m_handle_views[0]) { - auto temp_ptr_0 = m_handle_views[0]; - auto temp_ptr_1 = m_handle_views[1]; - unsetSegmentHandles(); - delete temp_ptr_0; - delete temp_ptr_1; - } - if (p_roughness_view) { - auto temp_ptr = p_roughness_view; - unsetRoughness(); - delete temp_ptr; - } - if (m_rough_handles_views[0]) { - auto temp_ptr_0 = m_rough_handles_views[0]; - auto temp_ptr_1 = m_rough_handles_views[1]; - unsetRoughnessHandles(); - delete temp_ptr_0; - delete temp_ptr_1; - } -} - -//! Connect to the set item -void LayerElementController::connectToModel() const -{ - auto on_property_change = [this](ModelView::SessionItem* /*item*/, std::string property_name) { - if (property_name == LayerElementItem::P_X_POS) { - updateSideSegment(); - updateTopSegment(); - updateSegmentHandles(); - updateRoughness(); - if (layerBelow()) - layerBelow()->layerElementItem()->setProperty( - LayerElementItem::P_X_POS, - layerElementItem()->property<double>(LayerElementItem::P_X_POS) - + layerElementItem()->property<double>(LayerElementItem::P_WIDTH)); - } - if (property_name == LayerElementItem::P_HEIGHT) { - emit heightChanged(m_sample_item_id, - layerElementItem()->property<double>(LayerElementItem::P_HEIGHT)); - updateSideSegment(); - updateTopSegment(); - updateSegmentHandles(); - updateRoughness(); - if (layerBelow()) { - layerBelow()->updateSideSegment(); - layerBelow()->updateSegmentHandles(); - layerBelow()->updateRoughness(); - } - } - if (property_name == LayerElementItem::P_WIDTH) { - emit widthChanged(m_sample_item_id, - layerElementItem()->property<double>(LayerElementItem::P_WIDTH)); - updateSideSegment(); - updateTopSegment(); - updateSegmentHandles(); - updateRoughness(); - if (layerBelow()) - layerBelow()->layerElementItem()->setProperty( - LayerElementItem::P_X_POS, - layerElementItem()->property<double>(LayerElementItem::P_X_POS) - + layerElementItem()->property<double>(LayerElementItem::P_WIDTH)); - } - if (property_name == LayerElementItem::P_ROUGHNESS) { - emit roughnessChanged(m_sample_item_id, layerElementItem()->property<double>( - LayerElementItem::P_ROUGHNESS)); - updateRoughness(); - } - - // Side segment update - if (property_name == LayerElementItem::P_SIDE_THICKNESS) { - updateSideSegment(); - } - if (property_name == LayerElementItem::P_SIDE_PEN_WIDTH) { - updateSideSegment(); - } - if (property_name == LayerElementItem::P_SIDE_PEN_COLOR) { - updateSideSegment(); - } - if (property_name == LayerElementItem::P_SIDE_BRUSH_COLOR) { - updateSideSegment(); - } - - // Top segment update - if (property_name == LayerElementItem::P_TOP_THICKNESS) { - updateTopSegment(); - } - if (property_name == LayerElementItem::P_TOP_PEN_WIDTH) { - updateTopSegment(); - } - if (property_name == LayerElementItem::P_TOP_PEN_COLOR) { - updateTopSegment(); - } - if (property_name == LayerElementItem::P_TOP_BRUSH_COLOR) { - updateTopSegment(); - } - - // Segment handle update - if (property_name == LayerElementItem::P_HANDLE_RADIUS) { - updateSegmentHandles(); - } - if (property_name == LayerElementItem::P_HANDLE_BRUSH_COLOR) { - updateSegmentHandles(); - } - if (property_name == LayerElementItem::P_HANDLE_PEN_WIDTH) { - updateSegmentHandles(); - } - if (property_name == LayerElementItem::P_HANDLE_PEN_COLOR) { - updateSegmentHandles(); - } - - // Roughness handles update - if (property_name == LayerElementItem::P_R_HANDLE_RADIUS) { - updateRoughness(); - } - if (property_name == LayerElementItem::P_R_HANDLE_BRUSH_COLOR) { - updateRoughness(); - } - if (property_name == LayerElementItem::P_R_HANDLE_PEN_WIDTH) { - updateRoughness(); - } - if (property_name == LayerElementItem::P_R_HANDLE_PEN_COLOR) { - updateRoughness(); - } - - // Roughness update - if (property_name == LayerElementItem::P_ROUGHNESS_BRUSH_COLOR) { - updateRoughness(); - } - if (property_name == LayerElementItem::P_ROUGHNESS_PEN_WIDTH) { - updateRoughness(); - } - if (property_name == LayerElementItem::P_ROUGHNESS_PEN_COLOR) { - updateRoughness(); - } - }; - - layerElementItem()->mapper()->setOnPropertyChange(on_property_change, this); -} - -//! Disconnect from the set item -void LayerElementController::disconnectFormModel() const -{ - layerElementItem()->mapper()->unsubscribe(this); -} - -//! Set the scene -void LayerElementController::setScene(GraphicsScene* scene) -{ - if (!scene) - return; - - p_scene = scene; - - putSegementsOnScene(); - putSegmentHandlesOnScene(); - putRoughnessOnScene(); - putRoughnessHandlesOnScene(); -} - -//! Return the current set scene -GraphicsScene* LayerElementController::scene() const -{ - return p_scene; -} - -//! Set the scene -void LayerElementController::unsetScene() -{ - if (!scene()) - return; - - removeSegmentsFromScene(); - removeSegmentHandlesFromScene(); - removeRoughnessFromScene(); - removeRoughnessHandlesFromScene(); - p_scene = nullptr; -} - -//! Set the idenfier of the sample item to report -void LayerElementController::setSampleItemId(std::string identifier) -{ - m_sample_item_id = identifier; -} - -//! Return the set sample item identifier -std::string LayerElementController::sampleItemId() const -{ - return m_sample_item_id; -} - -//! Unset the sample item identifier -void LayerElementController::unsetSampleItemId() -{ - m_sample_item_id = " "; -} - -//! Get the scene adapter to convert to axes -SceneAdapterInterface* LayerElementController::sceneAdapter() const -{ - if (!p_scene) - return nullptr; - - return p_scene->sceneAdapter(); -} - -//! Set the layer above the current one in relation -void LayerElementController::setLayerAbove(LayerElementController* layer_view_controller) -{ - p_controller_above = layer_view_controller; - - if (layer_view_controller->layerBelow() != this) - layer_view_controller->setLayerBelow(this); - - double pos = - p_controller_above->layerElementItem()->property<double>(LayerElementItem::P_X_POS) - + p_controller_above->layerElementItem()->property<double>(LayerElementItem::P_WIDTH); - layerElementItem()->setProperty(LayerElementItem::P_X_POS, pos); -} - -//! Set the layer below the current one in relation -void LayerElementController::setLayerBelow(LayerElementController* layer_view_controller) -{ - p_controller_below = layer_view_controller; - - if (layer_view_controller->layerAbove() != this) - layer_view_controller->setLayerAbove(this); -} - -//! Return the layer above the current one in relation -LayerElementController* LayerElementController::layerAbove() const -{ - return p_controller_above; -} - -//! Return the layer below the current one in relation -LayerElementController* LayerElementController::layerBelow() const -{ - return p_controller_below; -} - -//! Unset the layer above the current one in relation -void LayerElementController::unsetLayerAbove(bool silent) -{ - if (!p_controller_above) - return; - - if (silent) - p_controller_above->unsetLayerBelow(false); - - p_controller_above = nullptr; -} - -//! Unset the layer below the current one in relation -void LayerElementController::unsetLayerBelow(bool silent) -{ - if (!p_controller_below) - return; - - if (silent) - p_controller_below->unsetLayerAbove(false); - - p_controller_below = nullptr; -} - -//! Set the side segment elements -void LayerElementController::setSideSegment(SegmentElementView* segment_view) -{ - m_segment_views[0] = segment_view; - m_segment_views[0]->adaptW(false); - m_segment_views[0]->setLayerElementController(this); - updateSideSegment(); - - if (scene()) - scene()->addItem(m_segment_views[0]); -} - -//! Set the top segment elements -void LayerElementController::setTopSegment(SegmentElementView* segment_view) -{ - m_segment_views[1] = segment_view; - m_segment_views[1]->adaptH(false); - m_segment_views[1]->setLayerElementController(this); - updateTopSegment(); - - if (scene()) - scene()->addItem(m_segment_views[1]); -} - -//! Return the side Segment view -SegmentElementView* LayerElementController::sideSegment() const -{ - return m_segment_views[0]; -} - -//! Return the top Segment view -SegmentElementView* LayerElementController::topSegment() const -{ - return m_segment_views[1]; -} - -//! Unset the side segment elements -void LayerElementController::unsetSideSegment() -{ - if (m_segment_views[0] && scene() && m_segment_views[0]->scene() == scene()) - scene()->removeItem(m_segment_views[0]); - - m_segment_views[0] = nullptr; -} - -//! Unset the top segment elements -void LayerElementController::unsetTopSegment() -{ - if (m_segment_views[1] && scene() && m_segment_views[1]->scene() == scene()) - scene()->removeItem(m_segment_views[1]); - - m_segment_views[1] = nullptr; -} - -//! The move logic for the segments -void LayerElementController::segmentViewMoved(SegmentElementView* segment_view) -{ - if (segment_view == sideSegment()) { - sideSegmentMoved(); - } else if (segment_view == topSegment()) { - topSegmentMoved(); - } -} - -//! Update the view of the side segment -void LayerElementController::updateSideSegment() const -{ - if (!m_segment_views[0]) - return; - - auto pen = QPen(); - pen.setColor(layerElementItem()->property<QColor>(LayerElementItem::P_SIDE_PEN_COLOR)); - pen.setWidthF(layerElementItem()->property<double>(LayerElementItem::P_SIDE_PEN_WIDTH)); - m_segment_views.at(0)->setPen(pen); - - auto brush = QBrush(Qt::SolidPattern); - brush.setColor(layerElementItem()->property<QColor>(LayerElementItem::P_SIDE_BRUSH_COLOR)); - m_segment_views.at(0)->setBrush(brush); - - m_segment_views.at(0)->setRectangle(sideSegmentRect()); -} - -//! Update the view of the top segment -void LayerElementController::updateTopSegment() const -{ - if (!m_segment_views[1]) - return; - - auto pen = QPen(); - pen.setColor(layerElementItem()->property<QColor>(LayerElementItem::P_TOP_PEN_COLOR)); - pen.setWidthF(layerElementItem()->property<double>(LayerElementItem::P_TOP_PEN_WIDTH)); - m_segment_views.at(1)->setPen(pen); - - auto brush = QBrush(Qt::SolidPattern); - brush.setColor(layerElementItem()->property<QColor>(LayerElementItem::P_TOP_BRUSH_COLOR)); - m_segment_views.at(1)->setBrush(brush); - - m_segment_views.at(1)->setRectangle(topSegmentRect()); -} - -//! Return the side segment rectangle -QRectF LayerElementController::sideSegmentRect() const -{ - double this_pos = layerElementItem()->property<double>(LayerElementItem::P_X_POS); - double this_height = layerElementItem()->property<double>(LayerElementItem::P_HEIGHT); - double this_thickness = - layerElementItem()->property<double>(LayerElementItem::P_SIDE_THICKNESS); - - double above_height = 0; - if (layerAbove()) { - above_height = - layerAbove()->layerElementItem()->property<double>(LayerElementItem::P_HEIGHT); - } - - if (above_height > this_height) { - return QRectF(this_pos - this_thickness / 2., this_height, this_thickness, - above_height - this_height); - } else { - return QRectF(this_pos - this_thickness / 2., above_height, this_thickness, - this_height - above_height); - } -} - -//! Return the top segment rectangle -QRectF LayerElementController::topSegmentRect() const -{ - double pos = layerElementItem()->property<double>(LayerElementItem::P_X_POS); - double height = layerElementItem()->property<double>(LayerElementItem::P_HEIGHT); - double width = layerElementItem()->property<double>(LayerElementItem::P_WIDTH); - double thickness = layerElementItem()->property<double>(LayerElementItem::P_TOP_THICKNESS); - return QRectF(pos, height - thickness / 2., width, thickness); -} - -//! Put segments on scene -void LayerElementController::putSegementsOnScene() const -{ - if (!scene()) - return; - for (auto segment_view : m_segment_views) { - if (segment_view) - scene()->addItem(segment_view); - } -} - -//! Remove the segments from the scene -void LayerElementController::removeSegmentsFromScene() const -{ - if (!scene()) - return; - for (auto segment_view : m_segment_views) { - if (segment_view && segment_view->scene() == scene()) - scene()->removeItem(segment_view); - } -} - -//! Handle the position variation of the side segment -void LayerElementController::sideSegmentMoved() const -{ - double x = sideSegment()->getLastPos().x(); - if (layerAbove()) { - double w = 0; - auto item = layerAbove()->layerElementItem(); - if (x < item->property<double>(LayerElementItem::P_X_POS)) { - x = item->property<double>(LayerElementItem::P_X_POS) + w; - } else { - w = x - item->property<double>(LayerElementItem::P_X_POS); - } - item->setProperty(LayerElementItem::P_WIDTH, w); - } - layerElementItem()->setProperty(LayerElementItem::P_X_POS, x); -} - -//! Handle the position variation of the top segment -void LayerElementController::topSegmentMoved() const -{ - double y = topSegment()->getLastPos().y(); - if (y < 0) - y = 0; - layerElementItem()->setProperty(LayerElementItem::P_HEIGHT, y); -} - -//! Set the side segment elements -void LayerElementController::setSegmentHandles(HandleElementView* first_handle, - HandleElementView* second_handle) -{ - m_handle_views[0] = first_handle; - m_handle_views[1] = second_handle; - m_handle_views[0]->setLayerElementController(this); - m_handle_views[1]->setLayerElementController(this); - updateSegmentHandles(); - - if (scene()) { - scene()->addItem(m_handle_views[0]); - scene()->addItem(m_handle_views[1]); - } -} - -//! Return the side Segment view -HandleElementView* LayerElementController::firstSegmentHandle() const -{ - return m_handle_views[0]; -} - -//! Return the top Segment view -HandleElementView* LayerElementController::secondSegmentHandle() const -{ - return m_handle_views[1]; -} - -//! Unset the side segment elements -void LayerElementController::unsetSegmentHandles() -{ - if (m_handle_views[0] && scene() && m_handle_views[0]->scene() == scene()) - scene()->removeItem(m_handle_views[0]); - if (m_handle_views[1] && scene() && m_handle_views[1]->scene() == scene()) - scene()->removeItem(m_handle_views[1]); - - m_handle_views[0] = nullptr; - m_handle_views[1] = nullptr; -} - -//! The move logic for the handles associated to the segments -void LayerElementController::handleViewMoved(HandleElementView* handle_view) -{ - if (handle_view == leftRoughnessHandle()) { - leftHandleMoved(); - } else if (handle_view == rightRoughnessHandle()) { - rightHandleMoved(); - } -} - -//! Update the handles of the segment -void LayerElementController::updateSegmentHandles() const -{ - auto pen = QPen(); - pen.setColor(layerElementItem()->property<QColor>(LayerElementItem::P_HANDLE_PEN_COLOR)); - pen.setWidthF(layerElementItem()->property<double>(LayerElementItem::P_HANDLE_PEN_WIDTH)); - - auto brush = QBrush(Qt::SolidPattern); - brush.setColor(layerElementItem()->property<QColor>(LayerElementItem::P_HANDLE_BRUSH_COLOR)); - - if (m_handle_views[0]) { - m_handle_views.at(0)->setPen(pen); - m_handle_views.at(0)->setBrush(brush); - m_handle_views.at(0)->setRectangle(firstSegmentHandleRect()); - } - if (m_handle_views[1]) { - m_handle_views.at(1)->setPen(pen); - m_handle_views.at(1)->setBrush(brush); - m_handle_views.at(1)->setRectangle(secondSegmentHandleRect()); - } -} - -//! Get the first segment handle rectangle -QRectF LayerElementController::firstSegmentHandleRect() const -{ - double pos = layerElementItem()->property<double>(LayerElementItem::P_X_POS); - double radius = layerElementItem()->property<double>(LayerElementItem::P_HANDLE_RADIUS); - - double above_height = 0; - if (layerAbove()) { - above_height = - layerAbove()->layerElementItem()->property<double>(LayerElementItem::P_HEIGHT); - } - - return QRectF(pos - radius, above_height - radius, 2 * radius, 2 * radius); -} - -//! Get the second segment handle rectangle -QRectF LayerElementController::secondSegmentHandleRect() const -{ - double pos = layerElementItem()->property<double>(LayerElementItem::P_X_POS); - double height = layerElementItem()->property<double>(LayerElementItem::P_HEIGHT); - double radius = layerElementItem()->property<double>(LayerElementItem::P_HANDLE_RADIUS); - return QRectF(pos - radius, height - radius, 2 * radius, 2 * radius); -} - -//! Put the segment handles on the secene -void LayerElementController::putSegmentHandlesOnScene() const -{ - if (!scene()) - return; - for (auto handle_view : m_handle_views) { - if (handle_view) - scene()->addItem(handle_view); - } -} - -//! Remove the segment handles on the scene -void LayerElementController::removeSegmentHandlesFromScene() const -{ - if (!scene()) - return; - for (auto handle_view : m_handle_views) { - if (handle_view && handle_view->scene() == scene()) - scene()->removeItem(handle_view); - } -} - -//! Set the roughness element view -void LayerElementController::setRoughness(RoughnessElementView* roughness_view) -{ - p_roughness_view = roughness_view; - updateRoughness(); - - if (scene()) { - scene()->addItem(p_roughness_view); - } -} - -//! Set the roughness handle element views -void LayerElementController::setRoughnessHandles(HandleElementView* first_handle_view, - HandleElementView* second_handle_view) -{ - m_rough_handles_views[0] = first_handle_view; - m_rough_handles_views[1] = second_handle_view; - m_rough_handles_views[0]->setFlag(QGraphicsItem::ItemIsMovable); - m_rough_handles_views[1]->setFlag(QGraphicsItem::ItemIsMovable); - m_rough_handles_views[0]->setLayerElementController(this); - m_rough_handles_views[1]->setLayerElementController(this); - updateRoughness(); - - if (scene()) { - scene()->addItem(m_rough_handles_views[0]); - scene()->addItem(m_rough_handles_views[1]); - } -} - -//! Return the roughness element view -RoughnessElementView* LayerElementController::roughness() const -{ - return p_roughness_view; -} - -//! Return the left roughness handle element view -HandleElementView* LayerElementController::leftRoughnessHandle() const -{ - return m_rough_handles_views[0]; -} - -//! Return the right roughness handle element view -HandleElementView* LayerElementController::rightRoughnessHandle() const -{ - return m_rough_handles_views[1]; -} - -//! Remove the roughness view element pointer -void LayerElementController::unsetRoughness() -{ - if (p_roughness_view && scene() && p_roughness_view->scene() == scene()) - scene()->removeItem(p_roughness_view); - p_roughness_view = nullptr; -} - -//! Remove the handle pointers -void LayerElementController::unsetRoughnessHandles() -{ - if (m_rough_handles_views[0] && scene() && m_rough_handles_views[0]->scene() == scene()) - scene()->removeItem(m_rough_handles_views[0]); - if (m_rough_handles_views[1] && scene() && m_rough_handles_views[1]->scene() == scene()) - scene()->removeItem(m_rough_handles_views[1]); - - m_rough_handles_views[0] = nullptr; - m_rough_handles_views[1] = nullptr; -} - -//! Update the whole roughness drawing -void LayerElementController::updateRoughness() const -{ - // Test the roughness - double roughness = layerElementItem()->property<double>(LayerElementItem::P_ROUGHNESS); - // double width = layerElementItem()->property<double>(LayerElementItem::P_WIDTH); - setRoughnessInLimits(roughness, false); - - // Perform the painting - auto pen = QPen(); - auto brush = QBrush(); - - // Take care of the rounghnessview - pen.setStyle(Qt::PenStyle::DashLine); - pen.setColor(layerElementItem()->property<QColor>(LayerElementItem::P_ROUGHNESS_PEN_COLOR)); - pen.setWidthF(layerElementItem()->property<double>(LayerElementItem::P_ROUGHNESS_PEN_WIDTH)); - brush.setColor(layerElementItem()->property<QColor>(LayerElementItem::P_ROUGHNESS_BRUSH_COLOR)); - if (p_roughness_view) { - p_roughness_view->setPen(pen); - p_roughness_view->setBrush(brush); - p_roughness_view->setLeftPath(leftRoughnessPath()); - p_roughness_view->setRightPath(rightRoughnessPath()); - } - - // Take care of the handles - pen.setStyle(Qt::PenStyle::SolidLine); - pen.setColor(layerElementItem()->property<QColor>(LayerElementItem::P_R_HANDLE_PEN_COLOR)); - pen.setWidthF(layerElementItem()->property<double>(LayerElementItem::P_R_HANDLE_PEN_WIDTH)); - brush.setColor(layerElementItem()->property<QColor>(LayerElementItem::P_R_HANDLE_BRUSH_COLOR)); - brush.setStyle(Qt::SolidPattern); - - if (m_rough_handles_views[0]) { - m_rough_handles_views.at(0)->setPen(pen); - m_rough_handles_views.at(0)->setBrush(brush); - m_rough_handles_views.at(0)->setRectangle(leftRoughnessHandleRect()); - } - if (m_rough_handles_views[1]) { - m_rough_handles_views.at(1)->setPen(pen); - m_rough_handles_views.at(1)->setBrush(brush); - m_rough_handles_views.at(1)->setRectangle(rightRoughnessHandleRect()); - } -} - -//! get the left painter path for the roughness view -QPainterPath LayerElementController::leftRoughnessPath() const -{ - double pos = layerElementItem()->property<double>(LayerElementItem::P_X_POS); - double height = layerElementItem()->property<double>(LayerElementItem::P_HEIGHT); - double roughness = layerElementItem()->property<double>(LayerElementItem::P_ROUGHNESS); - - auto path = QPainterPath(); - - auto layer_above = layerAbove(); - if (!layer_above) { - path.moveTo(pos, 0); - path.lineTo(pos - roughness, 0); - } else { - path.moveTo(pos - roughness, - layer_above->layerElementItem()->property<double>(LayerElementItem::P_HEIGHT)); - } - path.lineTo(pos - roughness, height); - path.lineTo(pos, height); - - return path; -} - -//! get the right painter path for the roughness view -QPainterPath LayerElementController::rightRoughnessPath() const -{ - double pos = layerElementItem()->property<double>(LayerElementItem::P_X_POS); - double height = layerElementItem()->property<double>(LayerElementItem::P_HEIGHT); - double roughness = layerElementItem()->property<double>(LayerElementItem::P_ROUGHNESS); - - auto path = QPainterPath(); - - auto layer_above = layerAbove(); - if (!layer_above) { - path.moveTo(pos, 0); - path.lineTo(pos + roughness, 0); - } else { - path.moveTo(pos, - layer_above->layerElementItem()->property<double>(LayerElementItem::P_HEIGHT)); - path.lineTo(pos + roughness, - layer_above->layerElementItem()->property<double>(LayerElementItem::P_HEIGHT)); - } - path.lineTo(pos + roughness, height); - - return path; -} - -//! get the rectangle for the left roughness handles -QRectF LayerElementController::leftRoughnessHandleRect() const -{ - double pos_x = layerElementItem()->property<double>(LayerElementItem::P_X_POS); - double height = layerElementItem()->property<double>(LayerElementItem::P_HEIGHT); - double radius = layerElementItem()->property<double>(LayerElementItem::P_R_HANDLE_RADIUS); - double roughness = layerElementItem()->property<double>(LayerElementItem::P_ROUGHNESS); - - auto layer_above = layerAbove(); - double lower_height = 0; - if (layer_above) { - lower_height = - layer_above->layerElementItem()->property<double>(LayerElementItem::P_HEIGHT); - } - double pos_y = (lower_height - height) / 2 + height; - - return QRectF(pos_x - roughness - radius, pos_y - radius, 2 * radius, 2 * radius); -} - -//! get the rectangle for the right roughness handles -QRectF LayerElementController::rightRoughnessHandleRect() const -{ - double pos_x = layerElementItem()->property<double>(LayerElementItem::P_X_POS); - double height = layerElementItem()->property<double>(LayerElementItem::P_HEIGHT); - double radius = layerElementItem()->property<double>(LayerElementItem::P_R_HANDLE_RADIUS); - double roughness = layerElementItem()->property<double>(LayerElementItem::P_ROUGHNESS); - - auto layer_above = layerAbove(); - double lower_height = 0; - if (layer_above) { - lower_height = - layer_above->layerElementItem()->property<double>(LayerElementItem::P_HEIGHT); - } - double pos_y = (lower_height - height) / 2 + height; - - return QRectF(pos_x + roughness - radius, pos_y - radius, 2 * radius, 2 * radius); -} - -//! Put the roughnes view on the scene -void LayerElementController::putRoughnessOnScene() const -{ - if (!scene()) - return; - if (p_roughness_view) - scene()->addItem(p_roughness_view); -} - -//! Put the roughness handles on the scene -void LayerElementController::putRoughnessHandlesOnScene() const -{ - if (!scene()) - return; - for (auto handle_roughness_view : m_rough_handles_views) { - if (handle_roughness_view) - scene()->addItem(handle_roughness_view); - } -} - -//! Remove the roughness view item from the scene -void LayerElementController::removeRoughnessFromScene() const -{ - if (!scene()) - return; - if (p_roughness_view && p_roughness_view->scene() == scene()) - scene()->removeItem(p_roughness_view); -} - -//! Remove the roughness handles from the sene -void LayerElementController::removeRoughnessHandlesFromScene() const -{ - if (!scene()) - return; - for (auto handle_roughness_view : m_rough_handles_views) { - if (handle_roughness_view && handle_roughness_view->scene() == scene()) - scene()->removeItem(handle_roughness_view); - } -} - -//! Handle the position variation of the left handle -void LayerElementController::leftHandleMoved() const -{ - double pos = layerElementItem()->property<double>(LayerElementItem::P_X_POS); - double roughness = pos - leftRoughnessHandle()->getLastPos().x(); - setRoughnessInLimits(roughness); -} - -//! Handle the position variation of the right handle -void LayerElementController::rightHandleMoved() const -{ - double pos = layerElementItem()->property<double>(LayerElementItem::P_X_POS); - double roughness = rightRoughnessHandle()->getLastPos().x() - pos; - setRoughnessInLimits(roughness); -} - -//! Handle the position variation of the right handle -void LayerElementController::setRoughnessInLimits(double roughness, bool active) const -{ - if (roughness < 0) { - layerElementItem()->setProperty(LayerElementItem::P_ROUGHNESS, 0.); - return; - } - - double width = layerElementItem()->property<double>(LayerElementItem::P_WIDTH); - if (width == 0) - width = 1e6; - - auto layer_above = layerAbove(); - if (layer_above) { - double second_width = - layer_above->layerElementItem()->property<double>(LayerElementItem::P_WIDTH); - if (second_width == 0) - second_width = 1e6; - - if (second_width < width) - width = second_width; - } - - if (roughness > width / 2.) { - layerElementItem()->setProperty(LayerElementItem::P_ROUGHNESS, width / 2.); - return; - } - - if (active) - layerElementItem()->setProperty(LayerElementItem::P_ROUGHNESS, roughness); -} - -} // namespace gui2 diff --git a/gui2/sldeditor/layerelementcontroller.h b/gui2/sldeditor/layerelementcontroller.h deleted file mode 100644 index 37febdf5ed8..00000000000 --- a/gui2/sldeditor/layerelementcontroller.h +++ /dev/null @@ -1,160 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/sldeditor/layerelementcontroller.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_SLDEDITOR_LAYERELEMENTCONTROLLER_H -#define BORNAGAIN_GUI2_SLDEDITOR_LAYERELEMENTCONTROLLER_H - -#include "darefl_export.h" -#include "mvvm/plotting/sceneadapterinterface.h" -#include <QObject> -#include <QPainterPath> -#include <vector> - -namespace gui2 { - -// The mvvm item associated to this layer -class LayerElementItem; -// The graphics scene to put the QGraphicsViewItem on -class GraphicsScene; -// The handle QGraphicsViewItem -class HandleElementView; -// The segment QGraphicsViewItem -class SegmentElementView; -// The roughness QGraphicsViewItem -class RoughnessElementView; - -//! Manages the whole appearance of a layer on the graphicsscene -class DAREFLCORE_EXPORT LayerElementController : public QObject { - Q_OBJECT - -public: - LayerElementController(LayerElementItem* layer_view_item); - LayerElementItem* layerElementItem() const; - void autoPopulate(); - void deleteViewItems(); - void connectToModel() const; - void disconnectFormModel() const; - - // ################################################################################## - // Scene management related - void setScene(GraphicsScene* scene); - GraphicsScene* scene() const; - void unsetScene(); - - // ################################################################################## - // Sample id management related - void setSampleItemId(std::string indentifier); - std::string sampleItemId() const; - void unsetSampleItemId(); - - // ################################################################################## - // Inter layer logic related - void setLayerAbove(LayerElementController* layer_view_controller); - void setLayerBelow(LayerElementController* layer_view_controller); - LayerElementController* layerAbove() const; - LayerElementController* layerBelow() const; - void unsetLayerAbove(bool silent = true); - void unsetLayerBelow(bool silent = true); - - // ################################################################################## - // Segment related public methods - void setSideSegment(SegmentElementView* segment_view); - void setTopSegment(SegmentElementView* segment_view); - SegmentElementView* sideSegment() const; - SegmentElementView* topSegment() const; - void unsetSideSegment(); - void unsetTopSegment(); - void segmentViewMoved(SegmentElementView* segment_view); - - // ################################################################################## - // Handle related public methods - void setSegmentHandles(HandleElementView* first_handle, HandleElementView* secondHandle); - HandleElementView* firstSegmentHandle() const; - HandleElementView* secondSegmentHandle() const; - void unsetSegmentHandles(); - void handleViewMoved(HandleElementView* handle_view); - - // ################################################################################## - // Roughness related - void setRoughness(RoughnessElementView* roughness_view); - void setRoughnessHandles(HandleElementView* first_handle_view, - HandleElementView* second_handle_view); - RoughnessElementView* roughness() const; - HandleElementView* leftRoughnessHandle() const; - HandleElementView* rightRoughnessHandle() const; - void unsetRoughness(); - void unsetRoughnessHandles(); - void updateRoughness() const; - -signals: - void heightChanged(std::string id, double value) const; - void widthChanged(std::string id, double value) const; - void roughnessChanged(std::string id, double value) const; - -private: - ModelView::SceneAdapterInterface* sceneAdapter() const; - -protected: - // ################################################################################## - // Segment related protected methods - void updateSideSegment() const; - void updateTopSegment() const; - QRectF sideSegmentRect() const; - QRectF topSegmentRect() const; - void putSegementsOnScene() const; - void removeSegmentsFromScene() const; - - void sideSegmentMoved() const; - void topSegmentMoved() const; - - // ################################################################################## - // Handle related protected methods - void updateSegmentHandles() const; - QRectF firstSegmentHandleRect() const; - QRectF secondSegmentHandleRect() const; - void putSegmentHandlesOnScene() const; - void removeSegmentHandlesFromScene() const; - - // ################################################################################## - // Roughness related protected methods - QPainterPath leftRoughnessPath() const; - QPainterPath rightRoughnessPath() const; - QRectF leftRoughnessHandleRect() const; - QRectF rightRoughnessHandleRect() const; - void putRoughnessOnScene() const; - void putRoughnessHandlesOnScene() const; - void removeRoughnessFromScene() const; - void removeRoughnessHandlesFromScene() const; - void setRoughnessInLimits(double roughness, bool active = true) const; - - void leftHandleMoved() const; - void rightHandleMoved() const; - -private: - LayerElementItem* p_model_item; - GraphicsScene* p_scene = nullptr; - std::string m_sample_item_id; - - std::vector<SegmentElementView*> m_segment_views = {nullptr, nullptr}; - std::vector<HandleElementView*> m_handle_views = {nullptr, nullptr}; - std::vector<HandleElementView*> m_rough_handles_views = {nullptr, nullptr}; - RoughnessElementView* p_roughness_view = nullptr; - - LayerElementController* p_controller_above = nullptr; - LayerElementController* p_controller_below = nullptr; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_SLDEDITOR_LAYERELEMENTCONTROLLER_H diff --git a/gui2/sldeditor/layerelementitem.cpp b/gui2/sldeditor/layerelementitem.cpp deleted file mode 100644 index 98ff179d33c..00000000000 --- a/gui2/sldeditor/layerelementitem.cpp +++ /dev/null @@ -1,57 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/sldeditor/layerelementitem.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/sldeditor/layerelementitem.h" -#include "mvvm/model/externalproperty.h" -#include "mvvm/model/itemcatalogue.h" -#include "mvvm/utils/numericutils.h" -#include "mvvm/utils/reallimits.h" - -namespace gui2 { - -LayerElementItem::LayerElementItem() : ModelView::CompoundItem("LayerElement") -{ - addProperty(P_X_POS, 0.)->setDisplayName("Position"); - addProperty(P_WIDTH, 10.)->setDisplayName("Width"); - addProperty(P_HEIGHT, 10.)->setDisplayName("Height"); - addProperty(P_ROUGHNESS, 5.)->setDisplayName("Roughness"); - - addProperty(P_SIDE_THICKNESS, 5.)->setDisplayName("Side segment thickness"); - addProperty(P_SIDE_BRUSH_COLOR, QColor("black"))->setDisplayName("Side segment color"); - addProperty(P_SIDE_PEN_WIDTH, 2.)->setDisplayName("Side segment pen width"); - addProperty(P_SIDE_PEN_COLOR, QColor("black"))->setDisplayName("Side segment pen color"); - - addProperty(P_TOP_THICKNESS, 5.)->setDisplayName("Top segment thickness"); - addProperty(P_TOP_BRUSH_COLOR, QColor("black"))->setDisplayName("Top segment color"); - addProperty(P_TOP_PEN_WIDTH, 2.)->setDisplayName("Top segment pen width"); - addProperty(P_TOP_PEN_COLOR, QColor("black"))->setDisplayName("Top segment pen color"); - - addProperty(P_HANDLE_RADIUS, 5.)->setDisplayName("Handle radius"); - addProperty(P_HANDLE_BRUSH_COLOR, QColor("black"))->setDisplayName("Handle color"); - addProperty(P_HANDLE_PEN_WIDTH, 2.)->setDisplayName("Handle pen width"); - addProperty(P_HANDLE_PEN_COLOR, QColor("black"))->setDisplayName("Handle pen color"); - - addProperty(P_R_HANDLE_RADIUS, 4.)->setDisplayName("Roughness handle radius"); - addProperty(P_R_HANDLE_BRUSH_COLOR, QColor("black"))->setDisplayName("Roughness handle color"); - addProperty(P_R_HANDLE_PEN_WIDTH, 2.)->setDisplayName("Roughness handle pen width"); - addProperty(P_R_HANDLE_PEN_COLOR, QColor("black")) - ->setDisplayName("Roughness handle pen color"); - - addProperty(P_ROUGHNESS_BRUSH_COLOR, QColor("black"))->setDisplayName("Roughness handle color"); - addProperty(P_ROUGHNESS_PEN_WIDTH, 2.)->setDisplayName("Roughness handle pen width"); - addProperty(P_ROUGHNESS_PEN_COLOR, QColor("black")) - ->setDisplayName("Roughness handle pen color"); -} - -} // namespace gui2 diff --git a/gui2/sldeditor/layerelementitem.h b/gui2/sldeditor/layerelementitem.h deleted file mode 100644 index 60acf311b9e..00000000000 --- a/gui2/sldeditor/layerelementitem.h +++ /dev/null @@ -1,61 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/sldeditor/layerelementitem.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_SLDEDITOR_LAYERELEMENTITEM_H -#define BORNAGAIN_GUI2_SLDEDITOR_LAYERELEMENTITEM_H - -#include "darefl_export.h" -#include "mvvm/model/compounditem.h" -#include "mvvm/model/sessionmodel.h" - -namespace gui2 { - -//! The mvvm session item associated to a layer -class DAREFLCORE_EXPORT LayerElementItem : public ModelView::CompoundItem { -public: - static inline const std::string P_X_POS = "P_X_POS"; - static inline const std::string P_WIDTH = "P_WIDTH"; - static inline const std::string P_HEIGHT = "P_HEIGHT"; - static inline const std::string P_ROUGHNESS = "P_ROUGHNESS"; - - static inline const std::string P_SIDE_THICKNESS = "P_SIDE_THICKNESS"; - static inline const std::string P_SIDE_BRUSH_COLOR = "P_SIDE_BRUSH_COLOR"; - static inline const std::string P_SIDE_PEN_WIDTH = "P_SIDE_PEN_WIDTH"; - static inline const std::string P_SIDE_PEN_COLOR = "P_SIDE_PEN_COLOR"; - - static inline const std::string P_TOP_THICKNESS = "P_TOP_THICKNESS"; - static inline const std::string P_TOP_BRUSH_COLOR = "P_TOP_BRUSH_COLOR"; - static inline const std::string P_TOP_PEN_WIDTH = "P_TOP_PEN_WIDTH"; - static inline const std::string P_TOP_PEN_COLOR = "P_TOP_PEN_COLOR"; - - static inline const std::string P_HANDLE_RADIUS = "P_HANDLE_RADIUS"; - static inline const std::string P_HANDLE_BRUSH_COLOR = "P_HANDLE_BRUSH_COLOR"; - static inline const std::string P_HANDLE_PEN_WIDTH = "P_HANDLE_PEN_WIDTH"; - static inline const std::string P_HANDLE_PEN_COLOR = "P_HANDLE_PEN_COLOR"; - - static inline const std::string P_ROUGHNESS_BRUSH_COLOR = "P_ROUGHNESS_BRUSH_COLOR"; - static inline const std::string P_ROUGHNESS_PEN_WIDTH = "P_ROUGHNESS_PEN_WIDTH"; - static inline const std::string P_ROUGHNESS_PEN_COLOR = "P_ROUGHNESS_PEN_COLOR"; - - static inline const std::string P_R_HANDLE_RADIUS = "P_R_HANDLE_RADIUS"; - static inline const std::string P_R_HANDLE_BRUSH_COLOR = "P_R_HANDLE_BRUSH_COLOR"; - static inline const std::string P_R_HANDLE_PEN_WIDTH = "P_R_HANDLE_PEN_WIDTH"; - static inline const std::string P_R_HANDLE_PEN_COLOR = "P_R_HANDLE_PEN_COLOR"; - - LayerElementItem(); -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_SLDEDITOR_LAYERELEMENTITEM_H diff --git a/gui2/sldeditor/roughnesselementview.cpp b/gui2/sldeditor/roughnesselementview.cpp deleted file mode 100644 index 83fbc3f322e..00000000000 --- a/gui2/sldeditor/roughnesselementview.cpp +++ /dev/null @@ -1,88 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/sldeditor/roughnesselementview.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/sldeditor/roughnesselementview.h" -#include "gui2/sldeditor/layerelementcontroller.h" - -#include <QPainter> -#include <QStyleOption> - -namespace gui2 { - -//! The constructor -RoughnessElementView::RoughnessElementView() - : ElementView() - , m_left_path(QPainterPath()) - , m_right_path(QPainterPath()) - , m_brush(QBrush()) - , m_pen(QPen()) -{ - setZValue(0); -} - -//! The overriden paint method -void RoughnessElementView::paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*) -{ - painter->setClipRect(sceneAdapter()->viewportRectangle()); - painter->setPen(m_pen); - painter->setBrush(m_brush); - painter->drawPath(displayPath(m_left_path)); - painter->drawPath(displayPath(m_right_path)); -} - -//! The shape -QPainterPath RoughnessElementView::shape() const -{ - QPainterPath path; - path.addPath(displayPath(m_left_path)); - path.addPath(displayPath(m_right_path)); - return path; -} - -//! The bounding rectangle of the handle -QRectF RoughnessElementView::boundingRect() const -{ - QPainterPath path; - path.addPath(displayPath(m_left_path)); - path.addPath(displayPath(m_right_path)); - return path.boundingRect(); -} - -//! Set the draw path for the left side -void RoughnessElementView::setLeftPath(QPainterPath left_path) -{ - prepareGeometryChange(); - m_left_path = left_path; -} - -//! Set the draw path for the right side -void RoughnessElementView::setRightPath(QPainterPath right_path) -{ - prepareGeometryChange(); - m_right_path = right_path; -} - -//! Set the brush -void RoughnessElementView::setBrush(QBrush brush) -{ - m_brush = brush; -} - -//! Set the pen -void RoughnessElementView::setPen(QPen pen) -{ - m_pen = pen; -} - -} // namespace gui2 diff --git a/gui2/sldeditor/roughnesselementview.h b/gui2/sldeditor/roughnesselementview.h deleted file mode 100644 index 5773450a94f..00000000000 --- a/gui2/sldeditor/roughnesselementview.h +++ /dev/null @@ -1,49 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/sldeditor/roughnesselementview.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_SLDEDITOR_ROUGHNESSELEMENTVIEW_H -#define BORNAGAIN_GUI2_SLDEDITOR_ROUGHNESSELEMENTVIEW_H - -#include "darefl_export.h" -#include "gui2/sldeditor/elementview.h" - -#include <QBrush> -#include <QPainterPath> -#include <QPen> - -namespace gui2 { - -//! The roughness QGraphicsViewItem -class DAREFLCORE_EXPORT RoughnessElementView : public ElementView { -public: - RoughnessElementView(); - QRectF boundingRect() const override; - QPainterPath shape() const override; - void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override; - - void setLeftPath(QPainterPath left_path); - void setRightPath(QPainterPath right_path); - void setBrush(QBrush brush); - void setPen(QPen pen); - -protected: - QPainterPath m_left_path; - QPainterPath m_right_path; - QBrush m_brush; - QPen m_pen; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_SLDEDITOR_ROUGHNESSELEMENTVIEW_H diff --git a/gui2/sldeditor/segmentelementview.cpp b/gui2/sldeditor/segmentelementview.cpp deleted file mode 100644 index c9e38de9752..00000000000 --- a/gui2/sldeditor/segmentelementview.cpp +++ /dev/null @@ -1,97 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/sldeditor/segmentelementview.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/sldeditor/segmentelementview.h" -#include "gui2/sldeditor/layerelementcontroller.h" - -#include <QGraphicsSceneMouseEvent> -#include <QPainter> -#include <QStyleOption> - -namespace gui2 { - -//! The constructor -SegmentElementView::SegmentElementView() - : ElementView() - , m_pos(QPointF(0, 0)) - , m_rectangle(QRectF(0, 0, 0, 0)) - , m_brush(QBrush()) - , m_pen(QPen()) -{ - setFlag(QGraphicsItem::ItemIsMovable); - setZValue(1); -} - -//! The overriden paint method -void SegmentElementView::paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*) -{ - painter->setClipRect(sceneAdapter()->viewportRectangle()); - painter->setPen(m_pen); - painter->setBrush(m_brush); - painter->drawRect(displayRect(m_rectangle)); -} - -//! The shape -QPainterPath SegmentElementView::shape() const -{ - QPainterPath path; - path.addRect(displayRect(m_rectangle)); - return path; -} - -//! The bounding rectangle of the handle -QRectF SegmentElementView::boundingRect() const -{ - return displayRect(m_rectangle); -} - -//! On move save the new position and notify the controller -void SegmentElementView::mouseMoveEvent(QGraphicsSceneMouseEvent* event) -{ - m_pos = scenePos(event->pos()); - p_controller->segmentViewMoved(this); -} - -//! Set the controller to report back the move -void SegmentElementView::setLayerElementController(LayerElementController* controller) -{ - p_controller = controller; -} - -//! Set the draw rectangle -void SegmentElementView::setRectangle(QRectF rectangle) -{ - prepareGeometryChange(); - m_rectangle = rectangle; -} - -//! Set the brush -void SegmentElementView::setBrush(QBrush brush) -{ - m_brush = brush; -} - -//! Set the pen -void SegmentElementView::setPen(QPen pen) -{ - m_pen = pen; -} - -//! Get the last position of the item -QPointF SegmentElementView::getLastPos() const -{ - return m_pos; -} - -} // namespace gui2 diff --git a/gui2/sldeditor/segmentelementview.h b/gui2/sldeditor/segmentelementview.h deleted file mode 100644 index 68011748a36..00000000000 --- a/gui2/sldeditor/segmentelementview.h +++ /dev/null @@ -1,56 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/sldeditor/segmentelementview.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_SLDEDITOR_SEGMENTELEMENTVIEW_H -#define BORNAGAIN_GUI2_SLDEDITOR_SEGMENTELEMENTVIEW_H - -#include "darefl_export.h" -#include "gui2/sldeditor/elementview.h" - -#include <QBrush> -#include <QPen> -#include <QRectF> - -namespace gui2 { - -class LayerElementController; - -//! The segment QGraphicsViewItem on the Graphicsscene -class DAREFLCORE_EXPORT SegmentElementView : public ElementView { -public: - SegmentElementView(); - QRectF boundingRect() const override; - QPainterPath shape() const override; - void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override; - - void setLayerElementController(LayerElementController* controller); - void setRectangle(QRectF rectangle); - void setBrush(QBrush brush); - void setPen(QPen pen); - QPointF getLastPos() const; - -public: - void mouseMoveEvent(QGraphicsSceneMouseEvent* event) override; - -protected: - LayerElementController* p_controller; - QPointF m_pos; - QRectF m_rectangle; - QBrush m_brush; - QPen m_pen; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_SLDEDITOR_SEGMENTELEMENTVIEW_H diff --git a/gui2/sldeditor/sldeditor.cpp b/gui2/sldeditor/sldeditor.cpp deleted file mode 100644 index d3a01c2dea5..00000000000 --- a/gui2/sldeditor/sldeditor.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/sldeditor/sldeditor.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/sldeditor/sldeditor.h" -#include "gui2/mainwindow/styleutils.h" -#include "gui2/model/applicationmodels.h" -#include "gui2/sldeditor/graphicsscene.h" -#include "gui2/sldeditor/sldeditoractions.h" -#include "gui2/sldeditor/sldeditortoolbar.h" -#include "gui2/sldeditor/sldviewwidget.h" -#include "mvvm/plotting/graphcanvas.h" -#include <QVBoxLayout> - -namespace gui2 { - -//! The constructor -SLDEditor::SLDEditor(QWidget* parent) - : QWidget(parent) - , m_editorActions(new SLDEditorActions(this)) - , m_viewWidget(new SLDViewWidget(this)) - , m_toolBar(new SLDEditorToolBar(m_editorActions)) -{ - setWindowTitle("SLD editor"); - auto layout = new QVBoxLayout; - layout->addWidget(m_toolBar); - layout->addWidget(m_viewWidget); - setLayout(layout); - layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(0); - - connect(dynamic_cast<SLDEditorToolBar*>(m_toolBar), &SLDEditorToolBar::resetViewport, [this]() { - GraphicsScene* scene_item = dynamic_cast<GraphicsScene*>(m_viewWidget->scene()); - if (!scene_item) - return; - scene_item->graphCanvas()->setViewportToContent(); - }); -} - -//! The destructor -SLDEditor::~SLDEditor() = default; - -void SLDEditor::setModels(ApplicationModels* models) -{ - m_viewWidget->setModels(models); - m_editorActions->setModel(models->sldViewModel()); -} - -QSize SLDEditor::sizeHint() const -{ - return GUI::Utils::Style::DockSizeHint(); -} - -QSize SLDEditor::minimumSizeHint() const -{ - return GUI::Utils::Style::DockMinimumSizeHint(); -} - -} // namespace gui2 diff --git a/gui2/sldeditor/sldeditor.h b/gui2/sldeditor/sldeditor.h deleted file mode 100644 index b320a8dc526..00000000000 --- a/gui2/sldeditor/sldeditor.h +++ /dev/null @@ -1,48 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/sldeditor/sldeditor.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_SLDEDITOR_SLDEDITOR_H -#define BORNAGAIN_GUI2_SLDEDITOR_SLDEDITOR_H - -#include "darefl_export.h" -#include <QWidget> - -namespace gui2 { -class SLDEditorActions; -class SLDEditorToolBar; -class SLDViewWidget; -class ApplicationModels; - -//! The SLD editor QWidget -class DAREFLCORE_EXPORT SLDEditor : public QWidget { - Q_OBJECT - -public: - SLDEditor(QWidget* parent = nullptr); - ~SLDEditor(); - - void setModels(ApplicationModels* models); - - QSize sizeHint() const override; - QSize minimumSizeHint() const override; - -private: - SLDEditorActions* m_editorActions{nullptr}; - SLDViewWidget* m_viewWidget{nullptr}; - SLDEditorToolBar* m_toolBar{nullptr}; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_SLDEDITOR_SLDEDITOR_H diff --git a/gui2/sldeditor/sldeditoractions.cpp b/gui2/sldeditor/sldeditoractions.cpp deleted file mode 100644 index 12d43c11911..00000000000 --- a/gui2/sldeditor/sldeditoractions.cpp +++ /dev/null @@ -1,42 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/sldeditor/sldeditoractions.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/sldeditor/sldeditoractions.h" -#include "gui2/sldeditor/layerelementitem.h" -#include "gui2/sldeditor/sldelementmodel.h" -#include "mvvm/model/modelutils.h" -#include "mvvm/viewmodel/viewmodel.h" - -using namespace ModelView; - -namespace gui2 { - -struct SLDEditorActions::SLDEditorActionsImpl { - SLDElementModel* sld_element_model{nullptr}; - SLDEditorActionsImpl() {} -}; - -SLDEditorActions::SLDEditorActions(QObject* parent) - : QObject(parent), p_impl(std::make_unique<SLDEditorActionsImpl>()) -{ -} - -void SLDEditorActions::setModel(SLDElementModel* model) -{ - p_impl->sld_element_model = model; -} - -SLDEditorActions::~SLDEditorActions() = default; - -} // namespace gui2 diff --git a/gui2/sldeditor/sldeditoractions.h b/gui2/sldeditor/sldeditoractions.h deleted file mode 100644 index 3d24f9c24fd..00000000000 --- a/gui2/sldeditor/sldeditoractions.h +++ /dev/null @@ -1,45 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/sldeditor/sldeditoractions.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_SLDEDITOR_SLDEDITORACTIONS_H -#define BORNAGAIN_GUI2_SLDEDITOR_SLDEDITORACTIONS_H - -#include "darefl_export.h" -#include <QObject> -#include <memory> - -namespace gui2 { - -class SLDElementModel; - -//! Handles user actions applied to SLDEditor. -//! Belongs to SLDEditor. - -class DAREFLCORE_EXPORT SLDEditorActions : public QObject { - Q_OBJECT - -public: - SLDEditorActions(QObject* parent = nullptr); - ~SLDEditorActions(); - - void setModel(SLDElementModel* model); - -private: - struct SLDEditorActionsImpl; - std::unique_ptr<SLDEditorActionsImpl> p_impl; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_SLDEDITOR_SLDEDITORACTIONS_H diff --git a/gui2/sldeditor/sldeditortoolbar.cpp b/gui2/sldeditor/sldeditortoolbar.cpp deleted file mode 100644 index c32d0d46ab8..00000000000 --- a/gui2/sldeditor/sldeditortoolbar.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/sldeditor/sldeditortoolbar.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/sldeditor/sldeditortoolbar.h" -#include "gui2/mainwindow/styleutils.h" -#include "gui2/resources/resources.h" -#include "gui2/sldeditor/sldeditoractions.h" -#include <QAction> -#include <QToolButton> - -namespace gui2 { - -SLDEditorToolBar::SLDEditorToolBar(SLDEditorActions*, QWidget* parent) : QToolBar(parent) -{ - GUI::Utils::Style::SetToolBarStyleTextBesides(this); - - auto reset_view = new QToolButton; - reset_view->setToolTip("Set axes to default range."); - reset_view->setIcon(QIcon(":/icons/aspect-ratio.svg")); - addWidget(reset_view); - connect(reset_view, &QToolButton::clicked, [this]() { resetViewport(); }); -} - -} // namespace gui2 diff --git a/gui2/sldeditor/sldeditortoolbar.h b/gui2/sldeditor/sldeditortoolbar.h deleted file mode 100644 index a740a9ac428..00000000000 --- a/gui2/sldeditor/sldeditortoolbar.h +++ /dev/null @@ -1,40 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/sldeditor/sldeditortoolbar.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_SLDEDITOR_SLDEDITORTOOLBAR_H -#define BORNAGAIN_GUI2_SLDEDITOR_SLDEDITORTOOLBAR_H - -#include "darefl_export.h" -#include <QToolBar> - -namespace gui2 { - -class SLDEditorActions; - -//! Material editor toolbar. - -class DAREFLCORE_EXPORT SLDEditorToolBar : public QToolBar { - Q_OBJECT - -public: - SLDEditorToolBar(SLDEditorActions* actions, QWidget* parent = nullptr); - ~SLDEditorToolBar() = default; - -signals: - void resetViewport(); -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_SLDEDITOR_SLDEDITORTOOLBAR_H diff --git a/gui2/sldeditor/sldelementcontroller.cpp b/gui2/sldeditor/sldelementcontroller.cpp deleted file mode 100644 index 8e5b70be11d..00000000000 --- a/gui2/sldeditor/sldelementcontroller.cpp +++ /dev/null @@ -1,309 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/sldeditor/sldelementcontroller.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/sldeditor/sldelementcontroller.h" -#include "gui2/model/materialitems.h" -#include "gui2/model/materialmodel.h" -#include "gui2/model/sampleitems.h" -#include "gui2/model/samplemodel.h" -#include "gui2/sldeditor/graphicsscene.h" -#include "gui2/sldeditor/layerelementcontroller.h" -#include "gui2/sldeditor/layerelementitem.h" -#include "gui2/sldeditor/segmentelementview.h" -#include "gui2/sldeditor/sldelementmodel.h" -#include "mvvm/model/compounditem.h" -#include "mvvm/model/externalproperty.h" -#include "mvvm/model/modelutils.h" -#include "mvvm/signals/modelmapper.h" -#include <iostream> - -using namespace ModelView; - -namespace gui2 { - -//! Contructor -SLDElementController::SLDElementController(MaterialModel* material_model, SampleModel* sample_model, - SLDElementModel* sld_model, GraphicsScene* scene_item) - : p_material_model(material_model) - , p_sample_model(sample_model) - , p_sld_model(sld_model) - , p_scene_item(scene_item) -{ - connectSLDElementModel(); - connectLayerModel(); - connectMaterialModel(); - buildSLD(); -} - -SLDElementController::~SLDElementController() -{ - clearScene(); -} - -//! Connect with signals of MaterialModel -void SLDElementController::connectMaterialModel() -{ - auto on_mat_data_change = [this](SessionItem* item, int) { updateToView(item); }; - p_material_model->mapper()->setOnDataChange(on_mat_data_change, this); - - auto on_mat_model_destroyed = [this](SessionModel*) { p_material_model = nullptr; }; - p_material_model->mapper()->setOnModelDestroyed(on_mat_model_destroyed, this); -} - -// FIXME Consider switching to ModelHasChangedController, or ModelListener. -// See quicksimcontroller.h or materialpropertycontroller.h as an example. - -//! Connect with signals of SampleModel -void SLDElementController::connectLayerModel() -{ - auto on_sam_data_change = [this](SessionItem* item, int) { updateToView(item); }; - p_sample_model->mapper()->setOnDataChange(on_sam_data_change, this); - - auto on_sam_item_inserted = [this](SessionItem*, TagRow) { buildSLD(); }; - p_sample_model->mapper()->setOnItemInserted(on_sam_item_inserted, this); - - auto on_sam_item_removed = [this](SessionItem*, TagRow) { buildSLD(); }; - p_sample_model->mapper()->setOnItemRemoved(on_sam_item_removed, this); - - auto on_sam_model_destroyed = [this](SessionModel*) { p_sample_model = nullptr; }; - p_sample_model->mapper()->setOnModelDestroyed(on_sam_model_destroyed, this); -} - -//! Connect with signals of SLDViewModel -void SLDElementController::connectSLDElementModel() -{ - auto on_sld_model_destroyed = [this](SessionModel*) { p_sld_model = nullptr; }; - p_sld_model->mapper()->setOnModelDestroyed(on_sld_model_destroyed, this); -} - -//! Disconnect with signals of MaterialModel -void SLDElementController::disconnectMaterialModel() const -{ - p_material_model->mapper()->unsubscribe(this); -} - -//! Disconnect with signals of SampleModel -void SLDElementController::disconnectLayerModel() const -{ - p_sample_model->mapper()->unsubscribe(this); -} - -//! Disconnect with signals of SLDViewModel -void SLDElementController::disconnectSLDElementModel() const -{ - p_sld_model->mapper()->unsubscribe(this); -} - -//! Set the scene of the current controller to be passed to the LayerElementControllers -void SLDElementController::setScene(GraphicsScene* scene) -{ - p_scene_item = scene; - buildSLD(); -} - -//! Updates all material properties in LayerItems to get new material colors and labels -void SLDElementController::buildSLD() -{ - if (!p_sld_model) - return; - if (!p_sample_model) - return; - if (!p_material_model) - return; - if (!p_scene_item) - return; - - disconnectSLDElementModel(); - clearScene(); - - if (p_sample_model->rootItem()->childrenCount() == 0) - return; - string_vec identifiers = getIdentifierVector(p_sample_model->rootItem()->children().at(0)); - if (identifiers.size() == 0) - return; - - buildLayerControllers(identifiers); - updateToView(); - connectSLDElementModel(); - connectLayerControllers(); -} - -//! Remove all LayerElementControllers and their items from scene and memory -void SLDElementController::clearScene() -{ - if (!p_scene_item) - return; - if (!p_sld_model) - return; - - for (size_t i = 0; i < m_layer_controllers.size(); ++i) { - m_layer_controllers.at(i)->disconnectFormModel(); - m_layer_controllers.at(i)->unsetScene(); - m_layer_controllers.at(i)->deleteViewItems(); - } - m_layer_controllers.clear(); - p_sld_model->clear(); -} - -//! Get the identifiers of all layeritems in the sample model in order of appearance -SLDElementController::string_vec SLDElementController::getIdentifierVector(SessionItem* item) -{ - string_vec output; - - auto children = item->children(); - for (int i = 0; i < item->childrenCount(); ++i) { - if (dynamic_cast<MultiLayerItem*>(children.at(i))) { - auto child = dynamic_cast<MultiLayerItem*>(children.at(i)); - for (int j = 0; j < child->property<int>(MultiLayerItem::P_NREPETITIONS); ++j) { - auto child_output = getIdentifierVector(child); - output.insert(output.end(), child_output.begin(), child_output.end()); - } - } else if (dynamic_cast<LayerItem*>(children.at(i))) { - auto child = dynamic_cast<LayerItem*>(children.at(i)); - output.push_back(child->identifier()); - } - } - return output; -} - -//! Build and set up the layer controllers -void SLDElementController::buildLayerControllers(string_vec& identifiers) -{ - if (!p_scene_item) - return; - if (!p_sld_model) - return; - - for (auto& identifier : identifiers) { - auto layer_element_item = p_sld_model->addLayer(); - auto layer_element_controller = - std::make_unique<LayerElementController>(layer_element_item); - layer_element_controller->autoPopulate(); - layer_element_controller->setScene(p_scene_item); - layer_element_controller->connectToModel(); - layer_element_controller->setSampleItemId(identifier); - m_layer_controllers.push_back(std::move(layer_element_controller)); - } - - for (size_t i = 0; i < m_layer_controllers.size() - 1; ++i) { - m_layer_controllers.at(i)->setLayerBelow(m_layer_controllers.at(i + 1).get()); - } - - if (m_layer_controllers.size() > 0) { - m_layer_controllers.at(0)->topSegment()->stretchRight(true); - m_layer_controllers.at(0)->topSegment()->setFlag(QGraphicsItem::ItemIsMovable, false); - } - if (m_layer_controllers.size() > 1) { - m_layer_controllers.at(1)->sideSegment()->setFlag(QGraphicsItem::ItemIsMovable, false); - m_layer_controllers.at(m_layer_controllers.size() - 1)->topSegment()->stretchLeft(true); - } -} - -//! Connect the layer controllers -void SLDElementController::connectLayerControllers() -{ - for (const auto& layer_controller : m_layer_controllers) { - QObject::connect(layer_controller.get(), &LayerElementController::heightChanged, this, - &SLDElementController::updateSLDFromView); - QObject::connect(layer_controller.get(), &LayerElementController::widthChanged, this, - &SLDElementController::updateThicknessFromView); - QObject::connect(layer_controller.get(), &LayerElementController::roughnessChanged, this, - &SLDElementController::updateRoughnessFromView); - } -} - -//! Disconnect the layer controllers -void SLDElementController::disconnectLayerControllers() -{ - for (const auto& layer_controller : m_layer_controllers) { - QObject::disconnect(layer_controller.get(), &LayerElementController::heightChanged, this, - &SLDElementController::updateSLDFromView); - QObject::disconnect(layer_controller.get(), &LayerElementController::widthChanged, this, - &SLDElementController::updateThicknessFromView); - QObject::disconnect(layer_controller.get(), &LayerElementController::roughnessChanged, this, - &SLDElementController::updateRoughnessFromView); - } -} - -//! Update the view items with the changes in the material or layer models -void SLDElementController::updateToView(SessionItem* item) -{ - if (item && dynamic_cast<MultiLayerItem*>(item->parent())) { - buildSLD(); - return; - } - - for (const auto& layer_controller : m_layer_controllers) { - auto layer_item = - dynamic_cast<LayerItem*>(p_sample_model->findItem(layer_controller->sampleItemId())); - if (!layer_item) { - buildSLD(); - return; - } - auto roughness_item = layer_item->item<RoughnessItem>(LayerItem::P_ROUGHNESS); - auto material_item = dynamic_cast<SLDMaterialItem*>(p_material_model->findItem( - layer_item->property<ExternalProperty>(LayerItem::P_MATERIAL).identifier())); - - layer_controller->layerElementItem()->setProperty( - LayerElementItem::P_ROUGHNESS, - roughness_item->property<double>(RoughnessItem::P_SIGMA)); - layer_controller->layerElementItem()->setProperty( - LayerElementItem::P_WIDTH, layer_item->property<double>(LayerItem::P_THICKNESS)); - - if (material_item) { - layer_controller->layerElementItem()->setProperty( - LayerElementItem::P_HEIGHT, - material_item->property<double>(SLDMaterialItem::P_SLD_REAL)); - layer_controller->layerElementItem()->setProperty( - LayerElementItem::P_TOP_BRUSH_COLOR, - material_item->property<QColor>(SLDMaterialItem::P_COLOR)); - layer_controller->layerElementItem()->setProperty( - LayerElementItem::P_SIDE_BRUSH_COLOR, - material_item->property<QColor>(SLDMaterialItem::P_COLOR)); - } else { - layer_controller->layerElementItem()->setProperty(LayerElementItem::P_HEIGHT, 1e-6); - layer_controller->layerElementItem()->setProperty(LayerElementItem::P_TOP_BRUSH_COLOR, - QColor("red")); - layer_controller->layerElementItem()->setProperty(LayerElementItem::P_SIDE_BRUSH_COLOR, - QColor("red")); - } - } -} - -//! Update the material and layer models from the view items -void SLDElementController::updateThicknessFromView(std::string identifier, double value) -{ - auto layer_item = dynamic_cast<LayerItem*>(p_sample_model->findItem(identifier)); - layer_item->setProperty(LayerItem::P_THICKNESS, value); -} - -//! Update the material and layer models from the view items -void SLDElementController::updateSLDFromView(std::string identifier, double value) -{ - auto layer_item = dynamic_cast<LayerItem*>(p_sample_model->findItem(identifier)); - auto material_item = dynamic_cast<SLDMaterialItem*>(p_material_model->findItem( - layer_item->property<ExternalProperty>(LayerItem::P_MATERIAL).identifier())); - if (material_item) - material_item->setProperty(SLDMaterialItem::P_SLD_REAL, value); -} - -//! Update the material and layer models from the view items -void SLDElementController::updateRoughnessFromView(std::string identifier, double value) -{ - auto layer_item = dynamic_cast<LayerItem*>(p_sample_model->findItem(identifier)); - auto roughness_item = layer_item->item<RoughnessItem>(LayerItem::P_ROUGHNESS); - roughness_item->setProperty(RoughnessItem::P_SIGMA, value); -} - -} // namespace gui2 diff --git a/gui2/sldeditor/sldelementcontroller.h b/gui2/sldeditor/sldelementcontroller.h deleted file mode 100644 index 38959bb95bd..00000000000 --- a/gui2/sldeditor/sldelementcontroller.h +++ /dev/null @@ -1,75 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/sldeditor/sldelementcontroller.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_SLDEDITOR_SLDELEMENTCONTROLLER_H -#define BORNAGAIN_GUI2_SLDEDITOR_SLDELEMENTCONTROLLER_H - -#include "darefl_export.h" -#include "gui2/sldeditor/layerelementcontroller.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionmodel.h" -#include <QObject> -#include <vector> - -namespace gui2 { - -class MaterialModel; -class SampleModel; -class SLDElementModel; -class GraphicsScene; - -//! The controller of the sld layer visual representation -class DAREFLCORE_EXPORT SLDElementController : public QObject { - Q_OBJECT - -public: - using string_vec = std::vector<std::string>; - using layer_ctrl_vec = std::vector<std::unique_ptr<LayerElementController>>; - - SLDElementController(MaterialModel* material_model, SampleModel* sample_model, - SLDElementModel* sld_model, GraphicsScene* scene_item); - ~SLDElementController(); - void setScene(GraphicsScene* scene); - -private: - void connectMaterialModel(); - void connectLayerModel(); - void connectSLDElementModel(); - void disconnectMaterialModel() const; - void disconnectLayerModel() const; - void disconnectSLDElementModel() const; - - void buildSLD(); - void clearScene(); - string_vec getIdentifierVector(ModelView::SessionItem* item); - void buildLayerControllers(string_vec& identifiers); - void connectLayerControllers(); - void disconnectLayerControllers(); - - void updateToView(ModelView::SessionItem* item = nullptr); - void updateThicknessFromView(std::string identifier, double value); - void updateSLDFromView(std::string identifier, double value); - void updateRoughnessFromView(std::string identifier, double value); - -private: - MaterialModel* p_material_model; - SampleModel* p_sample_model; - SLDElementModel* p_sld_model; - GraphicsScene* p_scene_item; - layer_ctrl_vec m_layer_controllers; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_SLDEDITOR_SLDELEMENTCONTROLLER_H diff --git a/gui2/sldeditor/sldelementmodel.cpp b/gui2/sldeditor/sldelementmodel.cpp deleted file mode 100644 index f6f4ec6c031..00000000000 --- a/gui2/sldeditor/sldelementmodel.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/sldeditor/sldelementmodel.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/sldeditor/sldelementmodel.h" -#include "gui2/sldeditor/layerelementitem.h" - -#include "mvvm/model/externalproperty.h" -#include "mvvm/model/itemcatalogue.h" -#include "mvvm/utils/reallimits.h" - -using namespace ModelView; - -namespace gui2 { - -namespace { -std::unique_ptr<ItemCatalogue> CreateItemCatalogue() -{ - auto result = std::make_unique<ModelView::ItemCatalogue>(); - result->registerItem<LayerElementItem>(); - return result; -} - -} // namespace - -//! Contructor -SLDElementModel::SLDElementModel() : SessionModel("ViewItemsModel") -{ - setItemCatalogue(CreateItemCatalogue()); -} - -//! Add a layer item to the model and return its pointer -LayerElementItem* SLDElementModel::addLayer() -{ - auto layer_element_item = insertItem<LayerElementItem>(); - return layer_element_item; -} - -} // namespace gui2 diff --git a/gui2/sldeditor/sldelementmodel.h b/gui2/sldeditor/sldelementmodel.h deleted file mode 100644 index 69b8e647006..00000000000 --- a/gui2/sldeditor/sldelementmodel.h +++ /dev/null @@ -1,41 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/sldeditor/sldelementmodel.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_SLDEDITOR_SLDELEMENTMODEL_H -#define BORNAGAIN_GUI2_SLDEDITOR_SLDELEMENTMODEL_H - -#include "darefl_export.h" -#include "mvvm/model/sessionmodel.h" -#include <vector> - -namespace ModelView { -class ExternalProperty; -} - -namespace gui2 { - -class LayerElementItem; - -//! The model of the sld layer visual representation -class DAREFLCORE_EXPORT SLDElementModel : public ModelView::SessionModel { -public: - SLDElementModel(); - - //! Add a layer item - LayerElementItem* addLayer(); -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_SLDEDITOR_SLDELEMENTMODEL_H diff --git a/gui2/sldeditor/sldviewwidget.cpp b/gui2/sldeditor/sldviewwidget.cpp deleted file mode 100644 index 85776e0d869..00000000000 --- a/gui2/sldeditor/sldviewwidget.cpp +++ /dev/null @@ -1,55 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/sldeditor/sldviewwidget.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/sldeditor/sldviewwidget.h" -#include "gui2/model/applicationmodels.h" -#include "gui2/model/jobmodel.h" -#include "gui2/sldeditor/graphicsscene.h" -#include "gui2/sldeditor/sldelementcontroller.h" -#include <QResizeEvent> - -namespace gui2 { - -//! The constructor -SLDViewWidget::SLDViewWidget(QWidget* parent) : QGraphicsView(parent) -{ - GraphicsScene* scene_item = new GraphicsScene(parent = this); - setScene(scene_item); - setRenderHints(QPainter::Antialiasing); - setDragMode(QGraphicsView::ScrollHandDrag); - setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - setContentsMargins(0, 0, 0, 0); -} - -//! The destructor -SLDViewWidget::~SLDViewWidget() = default; - -void SLDViewWidget::setModels(ApplicationModels* models) -{ - m_sld_controller = std::make_unique<SLDElementController>( - models->materialModel(), models->sampleModel(), models->sldViewModel(), nullptr); - m_sld_controller->setScene(dynamic_cast<GraphicsScene*>(scene())); - dynamic_cast<GraphicsScene*>(scene())->setItem(models->jobModel()->sldViewport()); -} - -//! Resize event management -void SLDViewWidget::resizeEvent(QResizeEvent* event) -{ - QWidget::resizeEvent(event); - GraphicsScene* scene_item = static_cast<GraphicsScene*>(scene()); - scene_item->update_size(event->size()); -} - -} // namespace gui2 diff --git a/gui2/sldeditor/sldviewwidget.h b/gui2/sldeditor/sldviewwidget.h deleted file mode 100644 index 9204299e7dc..00000000000 --- a/gui2/sldeditor/sldviewwidget.h +++ /dev/null @@ -1,46 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/sldeditor/sldviewwidget.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_SLDEDITOR_SLDVIEWWIDGET_H -#define BORNAGAIN_GUI2_SLDEDITOR_SLDVIEWWIDGET_H - -#include "darefl_export.h" -#include <QGraphicsView> -#include <memory> - -namespace gui2 { - -class ApplicationModels; -class SLDElementController; - -//! The segment QGraphicsViewItem on the Graphicsscene -class DAREFLCORE_EXPORT SLDViewWidget : public QGraphicsView { - Q_OBJECT - -public: - SLDViewWidget(QWidget* parent = nullptr); - ~SLDViewWidget(); - - void setModels(ApplicationModels* models); - -protected: - void resizeEvent(QResizeEvent* event); - -private: - std::unique_ptr<SLDElementController> m_sld_controller; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_SLDEDITOR_SLDVIEWWIDGET_H diff --git a/gui2/welcomeview/CMakeLists.txt b/gui2/welcomeview/CMakeLists.txt deleted file mode 100644 index 0ba8b05994b..00000000000 --- a/gui2/welcomeview/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -target_sources(${library_name} PRIVATE - openprojectwidget.cpp - openprojectwidget.h - projecthandler.cpp - projecthandler.h - projectpanewidget.cpp - projectpanewidget.h - recentprojectsettings.cpp - recentprojectsettings.h - recentprojectwidget.cpp - recentprojectwidget.h - userinteractor.cpp - userinteractor.h - welcomeview.cpp - welcomeview.h -) diff --git a/gui2/welcomeview/openprojectwidget.cpp b/gui2/welcomeview/openprojectwidget.cpp deleted file mode 100644 index 587c9603b9f..00000000000 --- a/gui2/welcomeview/openprojectwidget.cpp +++ /dev/null @@ -1,100 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/welcomeview/openprojectwidget.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/welcomeview/openprojectwidget.h" -#include "BAVersion.h" -#include "gui2/mainwindow/styleutils.h" -#include "mvvm/widgets/widgetutils.h" -#include <QHBoxLayout> -#include <QLabel> -#include <QPushButton> -#include <QVBoxLayout> - -namespace { -int logo_width() -{ - return ModelView::Utils::SizeOfLetterM().height() * 40; -} - -const QString str_open = "Open"; -const QString str_new = "New"; - -} // namespace - -namespace gui2 { - -OpenProjectWidget::OpenProjectWidget(QWidget* parent) : QWidget(parent) -{ - auto layout = new QVBoxLayout(this); - - QPixmap logo(":/icons/F-letter_1000x.png"); - auto label = new QLabel; - label->setPixmap(logo.scaled(logo_width(), logo_width(), Qt::KeepAspectRatio)); - - layout->addSpacing(ModelView::Utils::SizeOfLetterM().height() * 1.5); - layout->addWidget(label, 0, Qt::AlignHCenter); - layout->addSpacing(ModelView::Utils::SizeOfLetterM().height()); - layout->addLayout(createProjectTitleLayout()); - layout->addSpacing(ModelView::Utils::SizeOfLetterM().height()); - layout->addLayout(createLinkedLabelLayout()); - layout->addStretch(); -} - -QSize OpenProjectWidget::sizeHint() const -{ - return GUI::Utils::Style::DockSizeHint(); -} - -QSize OpenProjectWidget::minimumSizeHint() const -{ - return GUI::Utils::Style::DockMinimumSizeHint(); -} - -QBoxLayout* OpenProjectWidget::createProjectTitleLayout() -{ - auto result = new QHBoxLayout; - QString title = - "BornAgain " + QString::fromStdString(BornAgain::GetVersionNumber()) + ", gui2 preview"; - auto label = new QLabel(title); - ModelView::Utils::ScaleLabelFont(label, 1.25); - - result->addWidget(label, 0, Qt::AlignHCenter); - return result; -} - -QBoxLayout* OpenProjectWidget::createLinkedLabelLayout() -{ - auto result = new QHBoxLayout; - - m_newProjectLabel = new QLabel(ModelView::Utils::ClickableText(str_new)); - m_newProjectLabel->setToolTip("Create new project"); - connect(m_newProjectLabel, &QLabel::linkActivated, [this](auto) { createNewProjectRequest(); }); - ModelView::Utils::ScaleLabelFont(m_newProjectLabel, 1.25); - - m_openProjectLabel = new QLabel(ModelView::Utils::ClickableText(str_open)); - m_openProjectLabel->setToolTip("Open existing project"); - connect(m_openProjectLabel, &QLabel::linkActivated, - [this](auto) { openExistingProjectRequest(); }); - ModelView::Utils::ScaleLabelFont(m_openProjectLabel, 1.15); - - result->addStretch(1); - result->addWidget(m_newProjectLabel); - result->addSpacing(ModelView::Utils::WidthOfLetterM()); - result->addWidget(m_openProjectLabel); - result->addStretch(1); - - return result; -} - -} // namespace gui2 diff --git a/gui2/welcomeview/openprojectwidget.h b/gui2/welcomeview/openprojectwidget.h deleted file mode 100644 index 3afbca8701c..00000000000 --- a/gui2/welcomeview/openprojectwidget.h +++ /dev/null @@ -1,54 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/welcomeview/openprojectwidget.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_WELCOMEVIEW_OPENPROJECTWIDGET_H -#define BORNAGAIN_GUI2_WELCOMEVIEW_OPENPROJECTWIDGET_H - -#include "darefl_export.h" -#include <QWidget> -#include <memory> - -class QBoxLayout; -class QPushButton; -class QLabel; - -namespace gui2 { - -//! Widget with logo, name of the program and buttons to create new project or open existing one. -//! Occupies right part of WelcomeView. - -class DAREFLCORE_EXPORT OpenProjectWidget : public QWidget { - Q_OBJECT - -public: - explicit OpenProjectWidget(QWidget* parent = nullptr); - - QSize sizeHint() const override; - QSize minimumSizeHint() const override; - -signals: - void createNewProjectRequest(); - void openExistingProjectRequest(); - -private: - QBoxLayout* createProjectTitleLayout(); - QBoxLayout* createLinkedLabelLayout(); - - QLabel* m_newProjectLabel{nullptr}; - QLabel* m_openProjectLabel{nullptr}; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_WELCOMEVIEW_OPENPROJECTWIDGET_H diff --git a/gui2/welcomeview/projecthandler.cpp b/gui2/welcomeview/projecthandler.cpp deleted file mode 100644 index 45bea510d14..00000000000 --- a/gui2/welcomeview/projecthandler.cpp +++ /dev/null @@ -1,125 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/welcomeview/projecthandler.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/welcomeview/projecthandler.h" -#include "gui2/model/applicationmodels.h" -#include "gui2/welcomeview/recentprojectsettings.h" -#include "gui2/welcomeview/recentprojectwidget.h" -#include "gui2/welcomeview/userinteractor.h" -#include "mvvm/factories/projectmanagerfactory.h" -#include "mvvm/project/project_types.h" -#include "mvvm/widgets/widgetutils.h" -#include <QMainWindow> - -using namespace ModelView; - -namespace gui2 { - -ProjectHandler::ProjectHandler(ApplicationModels* models, QWidget* parent) - : QObject(parent) - , m_recentProjectSettings(std::make_unique<RecentProjectSettings>()) - , m_userInteractor(std::make_unique<UserInteractor>(m_recentProjectSettings.get(), parent)) - , m_models(models) -{ - initProjectManager(); - updateRecentProjectNames(); -} - -ProjectHandler::~ProjectHandler() = default; - -//! Update names (name of the current project, recent project name list, notifies the world). - -void ProjectHandler::updateNames() -{ - updateCurrentProjectName(); - updateRecentProjectNames(); -} - -//! Returns 'true' if current project can be closed. -//! Internally will perform check for unsaved data, and proceed via save/discard/cancel dialog. - -bool ProjectHandler::canCloseProject() const -{ - return m_projectManager->closeCurrentProject(); -} - -void ProjectHandler::onCreateNewProject() -{ - if (m_projectManager->createNewProject()) - updateNames(); -} - -void ProjectHandler::onOpenExistingProject(const QString& dirname) -{ - if (m_projectManager->openExistingProject(dirname.toStdString())) - updateNames(); -} - -void ProjectHandler::onSaveCurrentProject() -{ - if (m_projectManager->saveCurrentProject()) - updateNames(); -} - -void ProjectHandler::onSaveProjectAs() -{ - if (m_projectManager->saveProjectAs()) - updateNames(); -} - -void ProjectHandler::clearRecentProjectsList() -{ - m_recentProjectSettings->clearRecentProjectsList(); - updateNames(); -} - -void ProjectHandler::initProjectManager() -{ - auto modified_callback = [this]() { updateCurrentProjectName(); }; - auto models_callback = [this]() { return m_models->persistent_models(); }; - ProjectContext project_context{modified_callback, models_callback}; - - auto select_dir_callback = [this]() { return m_userInteractor->onSelectDirRequest(); }; - auto create_dir_callback = [this]() { return m_userInteractor->onCreateDirRequest(); }; - auto answer_callback = [this]() { return m_userInteractor->onSaveChangesRequest(); }; - UserInteractionContext user_context{select_dir_callback, create_dir_callback, answer_callback}; - - m_projectManager = CreateProjectManager(project_context, user_context); -} - -//! Updates the name of the current project on main window, notifies the world. - -void ProjectHandler::updateCurrentProjectName() -{ - const auto current_project_dir = QString::fromStdString(m_projectManager->currentProjectDir()); - const auto is_modified = m_projectManager->isModified(); - - // set main window title - auto title = ModelView::Utils::ProjectWindowTitle(current_project_dir, is_modified); - if (auto main_window = ModelView::Utils::FindMainWindow(); main_window) - main_window->setWindowTitle(title); - - currentProjectModified(current_project_dir, is_modified); -} - -//! Update recent project list in settings, notifies the world. - -void ProjectHandler::updateRecentProjectNames() -{ - m_recentProjectSettings->addToRecentProjects( - QString::fromStdString(m_projectManager->currentProjectDir())); - recentProjectsListModified(m_recentProjectSettings->recentProjects()); -} - -} // namespace gui2 diff --git a/gui2/welcomeview/projecthandler.h b/gui2/welcomeview/projecthandler.h deleted file mode 100644 index 9c95e24b006..00000000000 --- a/gui2/welcomeview/projecthandler.h +++ /dev/null @@ -1,73 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/welcomeview/projecthandler.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_WELCOMEVIEW_PROJECTHANDLER_H -#define BORNAGAIN_GUI2_WELCOMEVIEW_PROJECTHANDLER_H - -#include "darefl_export.h" -#include <QObject> -#include <memory> -#include <vector> - -class QWidget; - -namespace ModelView { -class ProjectManagerInterface; -} - -namespace gui2 { - -class RecentProjectSettings; -class UserInteractor; -class ApplicationModels; -class RecentProjectWidget; - -//! Main class to coordinate all activity on user's request to create new project, -//! open existing one, or choose one of recent projects on disk. - -class DAREFLCORE_EXPORT ProjectHandler : public QObject { - Q_OBJECT - -public: - explicit ProjectHandler(ApplicationModels* models, QWidget* parent); - ~ProjectHandler() override; - -signals: - void currentProjectModified(const QString& project_dir, bool is_modified); - void recentProjectsListModified(const QStringList& projects); - -public slots: - void updateNames(); - bool canCloseProject() const; - void onCreateNewProject(); - void onOpenExistingProject(const QString& dirname = {}); - void onSaveCurrentProject(); - void onSaveProjectAs(); - - void clearRecentProjectsList(); - -private: - void initProjectManager(); - void updateCurrentProjectName(); - void updateRecentProjectNames(); - - std::unique_ptr<RecentProjectSettings> m_recentProjectSettings; - std::unique_ptr<UserInteractor> m_userInteractor; - std::unique_ptr<ModelView::ProjectManagerInterface> m_projectManager; - ApplicationModels* m_models{nullptr}; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_WELCOMEVIEW_PROJECTHANDLER_H diff --git a/gui2/welcomeview/projectpanewidget.cpp b/gui2/welcomeview/projectpanewidget.cpp deleted file mode 100644 index eab382a7858..00000000000 --- a/gui2/welcomeview/projectpanewidget.cpp +++ /dev/null @@ -1,105 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/welcomeview/projectpanewidget.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/welcomeview/projectpanewidget.h" -#include "mvvm/widgets/widgetutils.h" -#include <QLabel> -#include <QMouseEvent> -#include <QPainter> -#include <QVBoxLayout> - -namespace { -int widget_height() -{ - return ModelView::Utils::SizeOfLetterM().height() * 3; -} -} // namespace - -namespace gui2 { - -ProjectPaneWidget::ProjectPaneWidget(QWidget* parent) - : QWidget(parent) - , m_currentProjectTitle(new QLabel(" ")) - , m_currentProjectDir(new QLabel(" ")) - , m_widgetColor(QColor(Qt::white)) -{ - setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); - setFixedHeight(widget_height()); - auto layout = new QVBoxLayout(this); - layout->addWidget(m_currentProjectTitle); - layout->addWidget(m_currentProjectDir); -} - -//! Sets current project dir to 'project_dir', adjust title according to 'is_modified'. - -void ProjectPaneWidget::setCurrentProject(const QString& project_dir, bool is_modified) -{ - m_active = true; - m_projectDir = project_dir; - - auto trimmed_project_dir = ModelView::Utils::WithTildeHomePath(project_dir); - auto project_title = ModelView::Utils::ProjectWindowTitle(project_dir, is_modified); - - m_currentProjectDir->setText(trimmed_project_dir); - m_currentProjectDir->setToolTip(m_projectDir); - m_currentProjectTitle->setText(project_title); -} - -//! Clear content of widget and make it inactive. Inactive widget doesnt' send signals when -//! user click on it. - -void ProjectPaneWidget::clear() -{ - setActive(false); - m_projectDir.clear(); - m_currentProjectDir->setText({}); - m_currentProjectDir->setToolTip({}); - m_currentProjectTitle->setText({}); -} - -//! Set 'active' flag to the given value. 'False' means that the widget only shows the project -//! title, but doesn't react on mouse clicks and doesn't change the background on mouse -//! hover events. -void ProjectPaneWidget::setActive(bool value) -{ - m_active = value; - update(); -} - -void ProjectPaneWidget::paintEvent(QPaintEvent*) -{ - QPainter painter(this); - painter.fillRect(0, 0, size().width(), size().height(), m_widgetColor); -} - -void ProjectPaneWidget::enterEvent(QEvent*) -{ - if (m_active) - m_widgetColor = QColor(Qt::lightGray); - update(); -} - -void ProjectPaneWidget::leaveEvent(QEvent*) -{ - m_widgetColor = QColor(Qt::white); - update(); -} - -void ProjectPaneWidget::mousePressEvent(QMouseEvent* event) -{ - if (m_active && event->button() == Qt::LeftButton) - projectSelected(m_projectDir); -} - -} // namespace gui2 diff --git a/gui2/welcomeview/projectpanewidget.h b/gui2/welcomeview/projectpanewidget.h deleted file mode 100644 index cd15c366b30..00000000000 --- a/gui2/welcomeview/projectpanewidget.h +++ /dev/null @@ -1,59 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/welcomeview/projectpanewidget.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_WELCOMEVIEW_PROJECTPANEWIDGET_H -#define BORNAGAIN_GUI2_WELCOMEVIEW_PROJECTPANEWIDGET_H - -#include "darefl_export.h" -#include <QWidget> - -class QLabel; - -namespace gui2 { - -//! Panel with labels to hold project name and project dir. When user clicks on it, -//! sends the request to open corresponding project. Part of RecentProjectsWidget. - -class DAREFLCORE_EXPORT ProjectPaneWidget : public QWidget { - Q_OBJECT - -public: - explicit ProjectPaneWidget(QWidget* parent = nullptr); - - void setCurrentProject(const QString& project_dir, bool is_modified = false); - - void clear(); - - void setActive(bool value); - -signals: - void projectSelected(const QString& project_dir); - -protected: - void paintEvent(QPaintEvent*) override; - void enterEvent(QEvent*) override; - void leaveEvent(QEvent*) override; - void mousePressEvent(QMouseEvent* event) override; - -private: - QLabel* m_currentProjectTitle{nullptr}; - QLabel* m_currentProjectDir{nullptr}; - QColor m_widgetColor; - bool m_active{false}; - QString m_projectDir; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_WELCOMEVIEW_PROJECTPANEWIDGET_H diff --git a/gui2/welcomeview/recentprojectsettings.cpp b/gui2/welcomeview/recentprojectsettings.cpp deleted file mode 100644 index ec3d4f1caa7..00000000000 --- a/gui2/welcomeview/recentprojectsettings.cpp +++ /dev/null @@ -1,114 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/welcomeview/recentprojectsettings.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/welcomeview/recentprojectsettings.h" -#include "mvvm/utils/fileutils.h" -#include <QDir> -#include <QSettings> - -namespace { -const int max_recent_projects = 10; - -const QString group_key = "welcomeview"; -const QString current_workdir_key = "currentworkdir"; -const QString recent_projects_key = "recentprojects"; - -const QString workdir_setting_name() -{ - return group_key + "/" + current_workdir_key; -} - -const QString recent_projects_setting_name() -{ - return group_key + "/" + recent_projects_key; -} - -} // namespace - -namespace gui2 { - -RecentProjectSettings::RecentProjectSettings() -{ - readSettings(); -} - -RecentProjectSettings::~RecentProjectSettings() -{ - writeSettings(); -} - -//! Returns current workdir. -QString RecentProjectSettings::currentWorkdir() const -{ - return m_currentWorkdir; -} - -//! Updates current workdir value from user selection. -//! Workdir will be set as parent director of selected `dirname`. -void RecentProjectSettings::updateWorkdirFromSelection(const QString& dirname) -{ - if (!dirname.isEmpty()) { - auto parent_path = ModelView::Utils::parent_path(dirname.toStdString()); - m_currentWorkdir = QString::fromStdString(parent_path); - } -} - -//! Returns list of recent projects, validates if projects still exists on disk. -QStringList RecentProjectSettings::recentProjects() -{ - QStringList updatedList; - for (const auto& fileName : m_recentProjects) { - if (ModelView::Utils::exists(fileName.toStdString())) - updatedList.append(fileName); - } - m_recentProjects = updatedList; - return m_recentProjects; -} - -//! Adds directory to the list of recent projects. -void RecentProjectSettings::addToRecentProjects(const QString& dirname) -{ - m_recentProjects.removeAll(dirname); - m_recentProjects.prepend(dirname); - while (m_recentProjects.size() > max_recent_projects) - m_recentProjects.removeLast(); -} - -void RecentProjectSettings::clearRecentProjectsList() -{ - m_recentProjects.clear(); -} - -//! Write all settings to file. -void RecentProjectSettings::writeSettings() -{ - QSettings settings; - settings.setValue(workdir_setting_name(), m_currentWorkdir); - settings.setValue(recent_projects_setting_name(), m_recentProjects); -} - -//! Reads all settings from file. -void RecentProjectSettings::readSettings() -{ - QSettings settings; - m_currentWorkdir = QDir::homePath(); - - if (settings.contains(workdir_setting_name())) - m_currentWorkdir = settings.value(workdir_setting_name()).toString(); - - if (settings.contains(recent_projects_setting_name())) - m_recentProjects = settings.value(recent_projects_setting_name()).toStringList(); -} - -} // namespace gui2 diff --git a/gui2/welcomeview/recentprojectsettings.h b/gui2/welcomeview/recentprojectsettings.h deleted file mode 100644 index 4db3f5b14db..00000000000 --- a/gui2/welcomeview/recentprojectsettings.h +++ /dev/null @@ -1,51 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/welcomeview/recentprojectsettings.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_WELCOMEVIEW_RECENTPROJECTSETTINGS_H -#define BORNAGAIN_GUI2_WELCOMEVIEW_RECENTPROJECTSETTINGS_H - -#include "darefl_export.h" -#include <QStringList> - -namespace gui2 { - -//! Collection of settings for RecentProjectWidget. Used to save last directory selected -//! by the user, and list of recent projects. Relies on QSettings machinery. - -class DAREFLCORE_EXPORT RecentProjectSettings { -public: - RecentProjectSettings(); - ~RecentProjectSettings(); - - QString currentWorkdir() const; - - void updateWorkdirFromSelection(const QString& dirname); - - QStringList recentProjects(); - - void addToRecentProjects(const QString& dirname); - - void clearRecentProjectsList(); - -private: - void writeSettings(); - void readSettings(); - - QString m_currentWorkdir; - QStringList m_recentProjects; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_WELCOMEVIEW_RECENTPROJECTSETTINGS_H diff --git a/gui2/welcomeview/recentprojectwidget.cpp b/gui2/welcomeview/recentprojectwidget.cpp deleted file mode 100644 index 4ddfee9b534..00000000000 --- a/gui2/welcomeview/recentprojectwidget.cpp +++ /dev/null @@ -1,114 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/welcomeview/recentprojectwidget.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/welcomeview/recentprojectwidget.h" -#include "gui2/mainwindow/styleutils.h" -#include "gui2/welcomeview/projectpanewidget.h" -#include "mvvm/widgets/adjustingscrollarea.h" -#include "mvvm/widgets/widgetutils.h" -#include <QLabel> -#include <QScrollArea> -#include <QVBoxLayout> - -namespace { -const int max_recent_project_count = 10; -const double section_label_scale = 1.25; -} // namespace - -namespace gui2 { - -RecentProjectWidget::RecentProjectWidget(QWidget* parent) - : QWidget(parent), m_currentProjectPane(new ProjectPaneWidget) -{ - auto layout = new QVBoxLayout(this); - layout->setContentsMargins(20, 0, 10, 0); - - layout->addWidget(createRecentProjectScrollArea()); - layout->addStretch(1); -} - -QSize RecentProjectWidget::sizeHint() const -{ - return GUI::Utils::Style::DockSizeHint(); -} - -QSize RecentProjectWidget::minimumSizeHint() const -{ - return GUI::Utils::Style::DockMinimumSizeHint(); -} - -//! Set current project title and label on appropriate widget. -void RecentProjectWidget::setCurrentProject(const QString& project_dir, bool is_modified) -{ - m_currentProjectPane->setCurrentProject(project_dir, is_modified); - m_currentProjectPane->setActive(false); -} - -//! Set name of all recent projects to appropriate widgets. -void RecentProjectWidget::setRecentProjectsList(const QStringList& projects) -{ - int widget_index{0}; - for (auto widget : m_recentProjectPanes) { - if (widget_index < projects.size()) - widget->setCurrentProject(projects.at(widget_index), false); - else - widget->clear(); - - ++widget_index; - } -} - -QBoxLayout* RecentProjectWidget::createCurrentProjectLayout() const -{ - auto result = new QVBoxLayout; - auto label = new QLabel("Current Project"); - ModelView::Utils::ScaleLabelFont(label, section_label_scale); - result->addWidget(label); - result->addWidget(m_currentProjectPane); - return result; -} - -QBoxLayout* RecentProjectWidget::createRecentProjectLayout() -{ - auto result = new QVBoxLayout; - auto label = new QLabel("Recent Projects"); - ModelView::Utils::ScaleLabelFont(label, section_label_scale); - result->addWidget(label); - - for (int i = 0; i < max_recent_project_count; ++i) { - auto widget = new ProjectPaneWidget; - connect(widget, &ProjectPaneWidget::projectSelected, this, - &RecentProjectWidget::projectSelected); - m_recentProjectPanes.push_back(widget); - result->addWidget(widget); - } - return result; -} - -QWidget* RecentProjectWidget::createRecentProjectScrollArea() -{ - auto result = new ModelView::AdjustingScrollArea; - - auto content = new QWidget; - auto layout = new QVBoxLayout; - layout->addLayout(createCurrentProjectLayout()); - layout->addSpacing(ModelView::Utils::SizeOfLetterM().height()); - layout->addLayout(createRecentProjectLayout()); - content->setLayout(layout); - - result->setWidget(content); - return result; -} - -} // namespace gui2 diff --git a/gui2/welcomeview/recentprojectwidget.h b/gui2/welcomeview/recentprojectwidget.h deleted file mode 100644 index d78a6687257..00000000000 --- a/gui2/welcomeview/recentprojectwidget.h +++ /dev/null @@ -1,59 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/welcomeview/recentprojectwidget.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_WELCOMEVIEW_RECENTPROJECTWIDGET_H -#define BORNAGAIN_GUI2_WELCOMEVIEW_RECENTPROJECTWIDGET_H - -#include "darefl_export.h" -#include <QWidget> -#include <memory> -#include <vector> - -class QBoxLayout; - -namespace gui2 { - -class ProjectPaneWidget; - -//! Widget with the name of current project and collection of recent projects. -//! Occupies left part of WelcomeView. - -class DAREFLCORE_EXPORT RecentProjectWidget : public QWidget { - Q_OBJECT - -public: - explicit RecentProjectWidget(QWidget* parent = nullptr); - - QSize sizeHint() const override; - QSize minimumSizeHint() const override; - - void setCurrentProject(const QString& project_dir, bool is_modified); - - void setRecentProjectsList(const QStringList& projects); - -signals: - void projectSelected(const QString& project_dir); - -private: - QBoxLayout* createCurrentProjectLayout() const; - QBoxLayout* createRecentProjectLayout(); - QWidget* createRecentProjectScrollArea(); - - ProjectPaneWidget* m_currentProjectPane; - std::vector<ProjectPaneWidget*> m_recentProjectPanes; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_WELCOMEVIEW_RECENTPROJECTWIDGET_H diff --git a/gui2/welcomeview/userinteractor.cpp b/gui2/welcomeview/userinteractor.cpp deleted file mode 100644 index 868ea56b35d..00000000000 --- a/gui2/welcomeview/userinteractor.cpp +++ /dev/null @@ -1,116 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/welcomeview/userinteractor.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/welcomeview/userinteractor.h" -#include "gui2/welcomeview/recentprojectsettings.h" -#include "mvvm/project/project_types.h" -#include "mvvm/project/projectutils.h" -#include "mvvm/utils/fileutils.h" -#include <QFileDialog> -#include <QMessageBox> -#include <map> - -using namespace ModelView; - -namespace { -//! Map of standard Qt answers to what ProjectManager expects. -std::map<QMessageBox::StandardButton, SaveChangesAnswer> answer_map() -{ - std::map<QMessageBox::StandardButton, SaveChangesAnswer> result = { - {QMessageBox::Save, SaveChangesAnswer::SAVE}, - {QMessageBox::Discard, SaveChangesAnswer::DISCARD}, - {QMessageBox::Cancel, SaveChangesAnswer::CANCEL}}; - return result; -} -} // namespace - -namespace gui2 { - -UserInteractor::UserInteractor(RecentProjectSettings* settings, QWidget* parent) - : m_settings(settings), m_parent(parent) -{ -} - -//! Returns directory on disk selected by the user via QFileDialog. -//! Checks if selected directory can be the project directory. - -std::string UserInteractor::onSelectDirRequest() -{ - auto dirname = selectDir(); - - if (dirname.empty()) // no valid selection - return {}; - - if (!ModelView::GUI::Project::Utils::IsPossibleProjectDir(dirname)) { - QMessageBox msgBox; - msgBox.setText( - "Selected directory doesn't look like a project directory, choose another one"); - msgBox.exec(); - return {}; - } - - return dirname; -} - -//! Returns new directory on disk created by the user via QFileDialog. - -std::string UserInteractor::onCreateDirRequest() - -{ - auto dirname = selectDir(); - - if (dirname.empty()) // no valid selection - return {}; - - if (!ModelView::Utils::is_empty(dirname)) { - QMessageBox msgBox; - msgBox.setText("The selected directory is not empty, choose another one."); - msgBox.exec(); - return {}; - } - - return dirname; -} - -//! Returns save/cancel/discard changes choice provided by the user. - -SaveChangesAnswer UserInteractor::onSaveChangesRequest() -{ - static auto translate = answer_map(); - - QMessageBox msgBox; - msgBox.setText("The project has been modified."); - msgBox.setInformativeText("Do you want to save your changes?"); - msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); - msgBox.setDefaultButton(QMessageBox::Save); - auto ret = static_cast<QMessageBox::StandardButton>(msgBox.exec()); - return translate[ret]; -} - -//! Summon dialog to select directory on disk. If selection is not empty, -//! save parent directory for later re-use. - -std::string UserInteractor::selectDir() const -{ - QString dirname = QFileDialog::getExistingDirectory( - m_parent, "Select directory", m_settings->currentWorkdir(), - QFileDialog::DontResolveSymlinks | QFileDialog::ShowDirsOnly); - - if (!dirname.isEmpty()) - m_settings->updateWorkdirFromSelection(dirname); - - return dirname.toStdString(); -} - -} // namespace gui2 diff --git a/gui2/welcomeview/userinteractor.h b/gui2/welcomeview/userinteractor.h deleted file mode 100644 index 1f92440bb5c..00000000000 --- a/gui2/welcomeview/userinteractor.h +++ /dev/null @@ -1,53 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/welcomeview/userinteractor.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_WELCOMEVIEW_USERINTERACTOR_H -#define BORNAGAIN_GUI2_WELCOMEVIEW_USERINTERACTOR_H - -#include "darefl_export.h" -#include <string> - -class QWidget; - -namespace ModelView { -enum class SaveChangesAnswer; -} - -namespace gui2 { - -class RecentProjectSettings; - -//! Provide save/discard/cancel and similar dialogs on user request. -//! Intended to work in pair with ProjectManagerDecorator. - -class DAREFLCORE_EXPORT UserInteractor { -public: - UserInteractor(RecentProjectSettings* settings, QWidget* parent); - - std::string onSelectDirRequest(); - - std::string onCreateDirRequest(); - - ModelView::SaveChangesAnswer onSaveChangesRequest(); - -private: - std::string selectDir() const; - - RecentProjectSettings* m_settings{nullptr}; - QWidget* m_parent{nullptr}; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_WELCOMEVIEW_USERINTERACTOR_H diff --git a/gui2/welcomeview/welcomeview.cpp b/gui2/welcomeview/welcomeview.cpp deleted file mode 100644 index 2d55b9a2cac..00000000000 --- a/gui2/welcomeview/welcomeview.cpp +++ /dev/null @@ -1,112 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/welcomeview/welcomeview.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "gui2/welcomeview/welcomeview.h" -#include "gui2/model/applicationmodels.h" -#include "gui2/welcomeview/openprojectwidget.h" -#include "gui2/welcomeview/projecthandler.h" -#include "gui2/welcomeview/recentprojectsettings.h" -#include "gui2/welcomeview/recentprojectwidget.h" -#include "mvvm/project/projectutils.h" -#include "mvvm/widgets/widgetutils.h" -#include <QApplication> -#include <QHBoxLayout> -#include <QMainWindow> - -namespace gui2 { - -WelcomeView::WelcomeView(ApplicationModels* models, QWidget* parent) - : QWidget(parent) - , m_models(models) - , m_projectHandler(new ProjectHandler(models, this)) - , m_recentProjectWidget(new RecentProjectWidget) - , m_openProjectWidget(new OpenProjectWidget) -{ - QPalette palette; - palette.setColor(QPalette::Window, Qt::white); - setAutoFillBackground(true); - setPalette(palette); - - auto layout = new QHBoxLayout(this); - layout->addSpacing(50); - layout->addWidget(m_recentProjectWidget, 38); - layout->addWidget(m_openProjectWidget, 62); - layout->addSpacing(50); - - setup_connections(); -} - -WelcomeView::~WelcomeView() = default; - -//! Returns 'true' if current project can be closed. -//! Internally will perform check for unsaved data, and proceed via save/discard/cancel dialog. - -bool WelcomeView::canCloseProject() const -{ - return m_projectHandler->canCloseProject(); -} - -void WelcomeView::updateNames() -{ - m_projectHandler->updateNames(); -} - -void WelcomeView::onCreateNewProject() -{ - m_projectHandler->onCreateNewProject(); -} - -void WelcomeView::onOpenExistingProject(const QString& dirname) -{ - return m_projectHandler->onOpenExistingProject(dirname); -} - -void WelcomeView::onSaveCurrentProject() -{ - return m_projectHandler->onSaveCurrentProject(); -} - -void WelcomeView::onSaveProjectAs() -{ - return m_projectHandler->onSaveProjectAs(); -} - -void WelcomeView::onClearRecentProjectsList() -{ - m_projectHandler->clearRecentProjectsList(); -} - -void WelcomeView::setup_connections() -{ - // connect buttons of OpenProjectWidget with this slots. - auto open_existing_project = [this]() { onOpenExistingProject(); }; - connect(m_openProjectWidget, &OpenProjectWidget::openExistingProjectRequest, - open_existing_project); - connect(m_openProjectWidget, &OpenProjectWidget::createNewProjectRequest, this, - &WelcomeView::onCreateNewProject); - - // connect RecentProjectWidget panels with this slots. - connect(m_recentProjectWidget, &RecentProjectWidget::projectSelected, this, - &WelcomeView::onOpenExistingProject); - - // connect ProjectHandler with RecentProjectWidget - connect(m_projectHandler, &ProjectHandler::currentProjectModified, m_recentProjectWidget, - &RecentProjectWidget::setCurrentProject); - connect(m_projectHandler, &ProjectHandler::recentProjectsListModified, m_recentProjectWidget, - &RecentProjectWidget::setRecentProjectsList); - connect(m_projectHandler, &ProjectHandler::recentProjectsListModified, this, - &WelcomeView::recentProjectsListModified); -} - -} // namespace gui2 diff --git a/gui2/welcomeview/welcomeview.h b/gui2/welcomeview/welcomeview.h deleted file mode 100644 index 091b715faca..00000000000 --- a/gui2/welcomeview/welcomeview.h +++ /dev/null @@ -1,66 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file gui2/welcomeview/welcomeview.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_GUI2_WELCOMEVIEW_WELCOMEVIEW_H -#define BORNAGAIN_GUI2_WELCOMEVIEW_WELCOMEVIEW_H - -#include "darefl_export.h" -#include <QWidget> -#include <memory> - -namespace gui2 { - -class ApplicationModels; -class ProjectHandler; -class RecentProjectWidget; -class OpenProjectWidget; -class RecentProjectSettings; - -//! Welcome view. Main widget on first tab of MainWindow. - -class DAREFLCORE_EXPORT WelcomeView : public QWidget { - Q_OBJECT - -public: - WelcomeView(ApplicationModels* models, QWidget* parent = nullptr); - ~WelcomeView(); - - bool canCloseProject() const; - - void updateNames(); - -signals: - void recentProjectsListModified(const QStringList& projects); - -public slots: - void onCreateNewProject(); - void onOpenExistingProject(const QString& dirname = {}); - void onSaveCurrentProject(); - void onSaveProjectAs(); - void onClearRecentProjectsList(); - -private: - void setup_connections(); - void update_current_project_name(); - void update_recent_project_names(); - - ApplicationModels* m_models{nullptr}; - ProjectHandler* m_projectHandler{nullptr}; - RecentProjectWidget* m_recentProjectWidget{nullptr}; - OpenProjectWidget* m_openProjectWidget{nullptr}; -}; - -} // namespace gui2 - -#endif // BORNAGAIN_GUI2_WELCOMEVIEW_WELCOMEVIEW_H diff --git a/mvvm/CMakeLists.txt b/mvvm/CMakeLists.txt deleted file mode 100644 index f963a972f93..00000000000 --- a/mvvm/CMakeLists.txt +++ /dev/null @@ -1,26 +0,0 @@ -cmake_minimum_required(VERSION 3.14) -project(qt-mvvm VERSION 0.2.0 LANGUAGES CXX) - -set(CMAKE_CXX_STANDARD 17) - -option(MVVM_BUMP_VERSION "Propagate version number" OFF) -option(MVVM_DISCOVER_TESTS "Auto discover tests and add to ctest, otherwise will run at compile time" ON) -option(MVVM_ENABLE_FILESYSTEM "Enable <filesystem> (requires modern compiler), otherwise rely on Qt" ON) -option(MVVM_BUILD_EXAMPLES "Build user examples" ON) - -set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake/modules) -include(configuration) - -set(CMAKE_CXX_VISIBILITY_PRESET hidden) -set(CMAKE_VISIBILITY_INLINES_HIDDEN 1) - -add_subdirectory(model) -add_subdirectory(viewmodel) -add_subdirectory(view) - -add_subdirectory(tests) - -include(installation) - -# Defines new 'clangformat' target. See CodeTools.cmake for details. -# project_clangformat_setup() diff --git a/mvvm/cmake/modules/ClangFormat.cmake b/mvvm/cmake/modules/ClangFormat.cmake deleted file mode 100644 index eb89dcd4018..00000000000 --- a/mvvm/cmake/modules/ClangFormat.cmake +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright Tomas Zeman 2019. -# Distributed under the Boost Software License, Version 1.0. -# (See accompanying file LICENSE_1_0.txt or copy at -# http://www.boost.org/LICENSE_1_0.txt) - -function(clangformat_setup) - if(NOT CLANGFORMAT_EXECUTABLE) - set(CLANGFORMAT_EXECUTABLE clang-format) - endif() - - if(NOT EXISTS ${CLANGFORMAT_EXECUTABLE}) - find_program(clangformat_executable_tmp ${CLANGFORMAT_EXECUTABLE}) - if(clangformat_executable_tmp) - set(CLANGFORMAT_EXECUTABLE ${clangformat_executable_tmp}) - unset(clangformat_executable_tmp) - else() - message(FATAL_ERROR "ClangFormat: ${CLANGFORMAT_EXECUTABLE} not found! Aborting") - endif() - endif() - - foreach(clangformat_source ${ARGV}) - get_filename_component(clangformat_source ${clangformat_source} ABSOLUTE) - list(APPEND clangformat_sources ${clangformat_source}) - endforeach() - - add_custom_target(${PROJECT_NAME}_clangformat - COMMAND - ${CLANGFORMAT_EXECUTABLE} - -style=file - -i - ${clangformat_sources} - COMMENT - "Formating with ${CLANGFORMAT_EXECUTABLE} ..." - ) - - if(TARGET clangformat) - add_dependencies(clangformat ${PROJECT_NAME}_clangformat) - else() - add_custom_target(clangformat DEPENDS ${PROJECT_NAME}_clangformat) - endif() -endfunction() - -function(target_clangformat_setup target) - get_target_property(target_sources ${target} SOURCES) - clangformat_setup(${target_sources}) -endfunction() diff --git a/mvvm/cmake/modules/CodeTools.cmake b/mvvm/cmake/modules/CodeTools.cmake deleted file mode 100644 index 03234b7b670..00000000000 --- a/mvvm/cmake/modules/CodeTools.cmake +++ /dev/null @@ -1,28 +0,0 @@ -# Collection of functions to set-up code beautification and analysis -include(ClangFormat) - -# List of targets for project code beautification. -set(BEAUTIFICATION_TARGETS mvvm_model mvvm_viewmodel mvvm_view testmodel testviewmodel testview) -set(BEAUTIFICATION_EXAMPLES celleditorscore concurrentplotcore dragandmovecore flateditorcore - graphicsproxycore layereditorcore plotcolormapcore plotgraphscore saveloadprojectcore treeviewscore) - -# Defines new target for 'clangformat' to beautify whole project. -# Use 'make clangformat' or 'cmake --build . --target clangformat' to beautify the code. -# Beautification settings are located in .clang-format in project directory. - -function(project_clangformat_setup) - set(all_sources) - foreach(target ${BEAUTIFICATION_TARGETS}) - get_target_property(target_sources ${target} SOURCES) - list(APPEND all_sources ${target_sources}) - endforeach() - # examples needs to add manualy target source dir to all names - foreach(target ${BEAUTIFICATION_EXAMPLES}) - get_target_property(target_sources ${target} SOURCES) - get_target_property(target_source_dir ${target} SOURCE_DIR) - foreach(target ${target_sources}) - list(APPEND all_sources ${target_source_dir}/${target}) - endforeach() - endforeach() - clangformat_setup(${all_sources}) -endfunction() diff --git a/mvvm/cmake/modules/configuration.cmake b/mvvm/cmake/modules/configuration.cmake deleted file mode 100644 index 46e653e10fb..00000000000 --- a/mvvm/cmake/modules/configuration.cmake +++ /dev/null @@ -1,67 +0,0 @@ -message(STATUS "CMake version ${CMAKE_VERSION}") - -# ----------------------------------------------------------------------------- -# Modules -# ----------------------------------------------------------------------------- - -include(CTest) -include(CodeTools) -include(GenerateExportHeader) -include(GNUInstallDirs) - -# ----------------------------------------------------------------------------- -# Variables -# ----------------------------------------------------------------------------- - -get_filename_component(MVVM_PROJECT_DIR "${CMAKE_CURRENT_LIST_DIR}/../.." ABSOLUTE) - -set(MVVM_SOVERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}) -set(MVVM_BUILDVERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}) -set(MVVM_TESTOUTPUT_DIR ${CMAKE_BINARY_DIR}/test_output_mvvm) - -# ----------------------------------------------------------------------------- -# Directories -# ----------------------------------------------------------------------------- - -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) - -file(MAKE_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) -file(MAKE_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) -file(MAKE_DIRECTORY ${MVVM_TESTOUTPUT_DIR}) - -# directory for autogenerated configs -set(MVVM_AUTOGEN_DIR ${CMAKE_BINARY_DIR}/autogen/mvvm) -file(MAKE_DIRECTORY ${MVVM_AUTOGEN_DIR}) - -# ----------------------------------------------------------------------------- -# Dependencies -# ----------------------------------------------------------------------------- - -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTORCC ON) - -find_package(Qt5 5.12 COMPONENTS Widgets Core Gui PrintSupport REQUIRED) -find_package(Threads) - -get_target_property(Qt5Widgets_location Qt5::Widgets LOCATION_Release) -message(STATUS " Qt5 libraries : ${Qt5Widgets_LIBRARIES} ${Qt5Widgets_location}") -message(STATUS " Qt5 Includes : ${Qt5Widgets_INCLUDE_DIRS}") - -# ----------------------------------------------------------------------------- -# Generating config files -# ----------------------------------------------------------------------------- - -configure_file(${MVVM_PROJECT_DIR}/cmake/scripts/testconfig.h.in ${MVVM_AUTOGEN_DIR}/testconfig.h @ONLY) - -if (MVVM_BUMP_VERSION) - configure_file(${MVVM_PROJECT_DIR}/cmake/scripts/mvvm_version.h.in ${MVVM_PROJECT_DIR}/source/libmvvm_model/mvvm/core/version.h @ONLY) -endif() - -# ----------------------------------------------------------------------------- -# Compile options -# ----------------------------------------------------------------------------- - -add_compile_options($<$<CXX_COMPILER_ID:MSVC>:/MP>) diff --git a/mvvm/cmake/modules/installation.cmake b/mvvm/cmake/modules/installation.cmake deleted file mode 100644 index 4ff10cd6fd4..00000000000 --- a/mvvm/cmake/modules/installation.cmake +++ /dev/null @@ -1,45 +0,0 @@ -# ----------------------------------------------------------------------------- -# Installation -# Credits to https://pabloariasal.github.io/2018/02/19/its-time-to-do-cmake-right, -# https://gitlab.kitware.com/cmake/community/-/wikis/doc/tutorials/Exporting-and-Importing-Targets -# ----------------------------------------------------------------------------- - -set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/mvvm) - -# exporting targets to a script and installing it -install(EXPORT mvvm-targets FILE MVVMTargets.cmake NAMESPACE MVVM:: DESTINATION ${INSTALL_CONFIGDIR}) - -# ----------------------------------------------------------------------------- -# Exporting -# ----------------------------------------------------------------------------- - -# Add all targets to the build-tree export set -export(TARGETS mvvm_model mvvm_viewmodel mvvm_view NAMESPACE MVVM:: FILE "${PROJECT_BINARY_DIR}/MVVMTargets.cmake") - -# Export the package for use from the build-tree (goes to $HOME/.cmake) -set(CMAKE_EXPORT_PACKAGE_REGISTRY ON) -export(PACKAGE MVVM) - -# ----------------------------------------------------------------------------- -# Creating and installing MVVMConfig.cmake -# ----------------------------------------------------------------------------- - -include(CMakePackageConfigHelpers) - -# to use in the build tree -configure_package_config_file(${MVVM_PROJECT_DIR}/cmake/scripts/MVVMConfig.cmake.in - ${CMAKE_CURRENT_BINARY_DIR}/MVVMConfig.cmake - INSTALL_DESTINATION ${INSTALL_CONFIGDIR} -) - -# to use in install tree -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/MVVMConfig.cmake DESTINATION ${INSTALL_CONFIGDIR}) - -# ----------------------------------------------------------------------------- -# Create and install ConfigVersion.cmake file -# ----------------------------------------------------------------------------- - -write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/MVVMConfigVersion.cmake VERSION - ${PROJECT_VERSION} COMPATIBILITY AnyNewerVersion) - -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/MVVMConfigVersion.cmake DESTINATION ${INSTALL_CONFIGDIR}) diff --git a/mvvm/cmake/scripts/MVVMConfig.cmake.in b/mvvm/cmake/scripts/MVVMConfig.cmake.in deleted file mode 100644 index f6d2dfc8e56..00000000000 --- a/mvvm/cmake/scripts/MVVMConfig.cmake.in +++ /dev/null @@ -1,21 +0,0 @@ -# MVVM_INCLUDE_DIRS - include directories for qt-mvvm -# MVVM_LIBRARIES - libraries to link against - -get_filename_component(MVVM_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) - -include(CMakeFindDependencyMacro) -find_dependency(Qt5 5.12 COMPONENTS Widgets REQUIRED) -find_dependency(Threads) - -if(TARGET MVVM::Model OR TARGET MVVM::ViewModel OR TARGET MVVM::View) -else() - message(STATUS "MVVMConfig.cmake including ${MVVM_CMAKE_DIR}/MVVMTargets.cmake") - include("${MVVM_CMAKE_DIR}/MVVMTargets.cmake") -endif() - -set(MVVM_LIBRARIES MVVM::Model MVVM::ViewModel MVVM::View) - -message(STATUS "MVVMConfig.cmake in ${CMAKE_CURRENT_LIST_FILE}") -message(STATUS " MVVM_LIBRARIES ${MVVM_LIBRARIES}") - - diff --git a/mvvm/cmake/scripts/mvvm_version.h.in b/mvvm/cmake/scripts/mvvm_version.h.in deleted file mode 100644 index f8c49492674..00000000000 --- a/mvvm/cmake/scripts/mvvm_version.h.in +++ /dev/null @@ -1,51 +0,0 @@ -// ************************************************************************** // -// -// Model-view-view-model framework for large GUI applications -// -//! @license GNU General Public License v3 or higher (see COPYING) -//! @authors see AUTHORS -// -// ************************************************************************** // - -#ifndef MVVM_CORE_VERSION_H -#define MVVM_CORE_VERSION_H - -//! @file version.h -//! Automatically generated from mvvm_version.h.in - -#include <string> - -namespace ModelView -{ - -//! Returns major project version. -inline int ProjectVersionMajor() -{ - const int project_version_major = @PROJECT_VERSION_MAJOR@; - return project_version_major; -} - -//! Returns minor project version. -inline int ProjectVersionMinor() -{ - const int project_version_minor = @PROJECT_VERSION_MINOR@; - return project_version_minor; -} - -//! Returns patch project version. -inline int ProjectVersionPatch() -{ - const int project_version_path = @PROJECT_VERSION_PATCH@; - return project_version_path; -} - -//! Returns project version string -inline std::string ProjectVersion() -{ - const std::string project_version = "@PROJECT_VERSION@"; - return project_version; -} - -} // namespace ModelView - -#endif // MVVM_CORE_VERSION_H diff --git a/mvvm/cmake/scripts/testconfig.h.in b/mvvm/cmake/scripts/testconfig.h.in deleted file mode 100644 index ca6899b3f29..00000000000 --- a/mvvm/cmake/scripts/testconfig.h.in +++ /dev/null @@ -1,29 +0,0 @@ -// ************************************************************************** // -// -// Model-view-view-model framework for large GUI applications -// -//! @license GNU General Public License v3 or higher (see COPYING) -//! @authors see AUTHORS -// -// ************************************************************************** // - -#ifndef SCRIPTS_TESTCONFIG_H -#define SCRIPTS_TESTCONFIG_H - -#include <string> - -//! Provides build time information for unit tests. -//! Automatically generated by CMake from testconfig.h.in - -namespace TestConfig { - -inline std::string CMakeSourceDir() { return "@CMAKE_SOURCE_DIR@"; } -inline std::string CMakeBinaryDir() { return "@CMAKE_BINARY_DIR@"; } -inline std::string TestOutputDir() { return "@MVVM_TESTOUTPUT_DIR@"; } -inline std::string ProjectSourceDir() { return "@MVVM_PROJECT_DIR@"; } -inline std::string TestData() { return "@MVVM_PROJECT_DIR@/tests/data"; } - -} - -#endif - diff --git a/mvvm/model/CMakeLists.txt b/mvvm/model/CMakeLists.txt deleted file mode 100644 index e738bbcdb53..00000000000 --- a/mvvm/model/CMakeLists.txt +++ /dev/null @@ -1,38 +0,0 @@ -# ----------------------------------------------------------------------------- -# Library: mvvm_model -# ----------------------------------------------------------------------------- -set(library_name mvvm_model) - -add_library(${library_name} SHARED "") -add_subdirectory(mvvm) -add_library(MVVM::Model ALIAS ${library_name}) # alias for build-tree usage - -# -- Generate header for export -- - -set(export_filename ${MVVM_AUTOGEN_DIR}/mvvm/model_export.h) -generate_export_header(${library_name} EXPORT_FILE_NAME ${export_filename}) - -# -- Dependencies -- - -target_link_libraries(${library_name} PUBLIC Qt5::Widgets Threads::Threads) -target_include_directories(${library_name} - PUBLIC - $<INSTALL_INTERFACE:include> - $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}> $<BUILD_INTERFACE:${MVVM_AUTOGEN_DIR}> - ) - -# -- Definitions -- - -target_compile_features(${library_name} PUBLIC cxx_std_17) # clang code model in Qt creator - -if (MVVM_ENABLE_FILESYSTEM) - add_definitions(-DENABLE_FILESYSTEM) -endif() - -# -- Installation -- - -install(TARGETS ${library_name} EXPORT mvvm-targets LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) -set_target_properties(${library_name} PROPERTIES EXPORT_NAME Model SOVERSION ${MVVM_SOVERSION} VERSION ${MVVM_BUILDVERSION}) -install(DIRECTORY mvvm/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/mvvm FILES_MATCHING PATTERN "*.h") -install(FILES ${export_filename} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/mvvm) - diff --git a/mvvm/model/mvvm/CMakeLists.txt b/mvvm/model/mvvm/CMakeLists.txt deleted file mode 100644 index 09107428c2d..00000000000 --- a/mvvm/model/mvvm/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -add_subdirectory(commands) -add_subdirectory(core) -add_subdirectory(factories) -add_subdirectory(interfaces) -add_subdirectory(model) -add_subdirectory(project) -add_subdirectory(serialization) -add_subdirectory(signals) -add_subdirectory(standarditems) -add_subdirectory(utils) diff --git a/mvvm/model/mvvm/commands/CMakeLists.txt b/mvvm/model/mvvm/commands/CMakeLists.txt deleted file mode 100644 index 70e73403f5f..00000000000 --- a/mvvm/model/mvvm/commands/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -target_sources(${library_name} PRIVATE - abstractitemcommand.cpp - abstractitemcommand.h - commandadapter.cpp - commandadapter.h - commandresult.h - commandservice.cpp - commandservice.h - commandutils.cpp - commandutils.h - copyitemcommand.cpp - copyitemcommand.h - insertnewitemcommand.cpp - insertnewitemcommand.h - moveitemcommand.cpp - moveitemcommand.h - removeitemcommand.cpp - removeitemcommand.h - setvaluecommand.cpp - setvaluecommand.h - undostack.cpp - undostack.h -) diff --git a/mvvm/model/mvvm/commands/abstractitemcommand.cpp b/mvvm/model/mvvm/commands/abstractitemcommand.cpp deleted file mode 100644 index bd3b88ed550..00000000000 --- a/mvvm/model/mvvm/commands/abstractitemcommand.cpp +++ /dev/null @@ -1,129 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/commands/abstractitemcommand.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/commands/abstractitemcommand.h" -#include "mvvm/model/modelutils.h" -#include "mvvm/model/path.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionmodel.h" -#include <stdexcept> - -using namespace ModelView; - -struct AbstractItemCommand::AbstractItemCommandImpl { - enum class Status { initial, after_execute, after_undo }; - bool m_isObsolete{false}; - std::string m_text; - Status m_status{Status::initial}; - SessionModel* m_model{nullptr}; - AbstractItemCommand* m_self{nullptr}; - CommandResult m_result; - AbstractItemCommandImpl(AbstractItemCommand* parent) : m_self(parent) {} - - void set_after_execute() { m_status = Status::after_execute; } - void set_after_undo() { m_status = Status::after_undo; } - bool can_execute() const { return m_status != Status::after_execute; } - bool can_undo() const { return m_status == Status::after_execute && !m_self->isObsolete(); } -}; - -AbstractItemCommand::AbstractItemCommand(SessionItem* receiver) - : p_impl(std::make_unique<AbstractItemCommand::AbstractItemCommandImpl>(this)) -{ - if (!receiver) - throw std::runtime_error("Invalid item."); - - if (!receiver->model()) - throw std::runtime_error("Item doesn't have a model"); - - p_impl->m_model = receiver->model(); -} - -AbstractItemCommand::~AbstractItemCommand() = default; - -//! Execute command. - -void AbstractItemCommand::execute() -{ - if (!p_impl->can_execute()) - throw std::runtime_error("Can't execute the command. Wrong order."); - - execute_command(); - - p_impl->set_after_execute(); -} - -//! Undo command as it was before execution. - -void AbstractItemCommand::undo() -{ - if (!p_impl->can_undo()) - throw std::runtime_error("Can't undo the command. Wrong order."); - - undo_command(); - - p_impl->set_after_undo(); -} - -//! Returns whether the command is obsolete (which means that it shouldn't be kept in the stack). - -bool AbstractItemCommand::isObsolete() const -{ - return p_impl->m_isObsolete; -} - -//! Returns command description. - -std::string AbstractItemCommand::description() const -{ - return p_impl->m_text; -} - -CommandResult AbstractItemCommand::result() const -{ - return p_impl->m_result; -} - -//! Sets command obsolete flag. - -void AbstractItemCommand::setObsolete(bool flag) -{ - p_impl->m_isObsolete = flag; -} - -//! Sets command description. - -void AbstractItemCommand::setDescription(const std::string& text) -{ - p_impl->m_text = text; -} - -Path AbstractItemCommand::pathFromItem(SessionItem* item) const -{ - return Utils::PathFromItem(item); -} - -SessionItem* AbstractItemCommand::itemFromPath(const Path& path) const -{ - return Utils::ItemFromPath(*p_impl->m_model, path); -} - -SessionModel* AbstractItemCommand::model() const -{ - return p_impl->m_model; -} - -void AbstractItemCommand::setResult(const CommandResult& command_result) -{ - p_impl->m_result = command_result; -} diff --git a/mvvm/model/mvvm/commands/abstractitemcommand.h b/mvvm/model/mvvm/commands/abstractitemcommand.h deleted file mode 100644 index 42602e687ad..00000000000 --- a/mvvm/model/mvvm/commands/abstractitemcommand.h +++ /dev/null @@ -1,67 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/commands/abstractitemcommand.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_COMMANDS_ABSTRACTITEMCOMMAND_H -#define BORNAGAIN_MVVM_MODEL_MVVM_COMMANDS_ABSTRACTITEMCOMMAND_H - -#include "mvvm/commands/commandresult.h" -#include "mvvm/model_export.h" -#include <memory> -#include <string> - -namespace ModelView { - -class SessionItem; -class SessionModel; -class Path; - -//! Abstract command interface to manipulate SessionItem in model context. - -class MVVM_MODEL_EXPORT AbstractItemCommand { -public: - explicit AbstractItemCommand(SessionItem* receiver); - virtual ~AbstractItemCommand(); - - AbstractItemCommand(const AbstractItemCommand& other) = delete; - AbstractItemCommand& operator=(const AbstractItemCommand& other) = delete; - - void execute(); - - void undo(); - - bool isObsolete() const; - - std::string description() const; - - CommandResult result() const; - -protected: - void setObsolete(bool flag); - void setDescription(const std::string& text); - Path pathFromItem(SessionItem* item) const; - SessionItem* itemFromPath(const Path& path) const; - SessionModel* model() const; - void setResult(const CommandResult& command_result); - -private: - virtual void execute_command() = 0; - virtual void undo_command() = 0; - - struct AbstractItemCommandImpl; - std::unique_ptr<AbstractItemCommandImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_COMMANDS_ABSTRACTITEMCOMMAND_H diff --git a/mvvm/model/mvvm/commands/commandadapter.cpp b/mvvm/model/mvvm/commands/commandadapter.cpp deleted file mode 100644 index 8dea93805d8..00000000000 --- a/mvvm/model/mvvm/commands/commandadapter.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/commands/commandadapter.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/commands/commandadapter.h" -#include "mvvm/commands/abstractitemcommand.h" - -using namespace ModelView; - -CommandAdapter::CommandAdapter(std::shared_ptr<AbstractItemCommand> command) - : m_command(std::move(command)) -{ -} - -CommandAdapter::~CommandAdapter() = default; - -void CommandAdapter::undo() -{ - m_command->undo(); -} - -void CommandAdapter::redo() -{ - m_command->execute(); - setObsolete(m_command->isObsolete()); - setText(QString::fromStdString(m_command->description())); -} diff --git a/mvvm/model/mvvm/commands/commandadapter.h b/mvvm/model/mvvm/commands/commandadapter.h deleted file mode 100644 index 972449df66b..00000000000 --- a/mvvm/model/mvvm/commands/commandadapter.h +++ /dev/null @@ -1,42 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/commands/commandadapter.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_COMMANDS_COMMANDADAPTER_H -#define BORNAGAIN_MVVM_MODEL_MVVM_COMMANDS_COMMANDADAPTER_H - -#include "mvvm/model_export.h" -#include <QUndoCommand> -#include <memory> - -namespace ModelView { - -class AbstractItemCommand; - -//! Adapter to execute our commands within Qt undo/redo framework. - -class MVVM_MODEL_EXPORT CommandAdapter : public QUndoCommand { -public: - CommandAdapter(std::shared_ptr<AbstractItemCommand> command); - ~CommandAdapter() override; - - void undo() override; - void redo() override; - -private: - std::shared_ptr<AbstractItemCommand> m_command; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_COMMANDS_COMMANDADAPTER_H diff --git a/mvvm/model/mvvm/commands/commandresult.h b/mvvm/model/mvvm/commands/commandresult.h deleted file mode 100644 index 13dde1c1511..00000000000 --- a/mvvm/model/mvvm/commands/commandresult.h +++ /dev/null @@ -1,29 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/commands/commandresult.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_COMMANDS_COMMANDRESULT_H -#define BORNAGAIN_MVVM_MODEL_MVVM_COMMANDS_COMMANDRESULT_H - -#include <variant> - -namespace ModelView { - -class SessionItem; - -//! Results of command execution. -using CommandResult = std::variant<bool, ModelView::SessionItem*>; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_COMMANDS_COMMANDRESULT_H diff --git a/mvvm/model/mvvm/commands/commandservice.cpp b/mvvm/model/mvvm/commands/commandservice.cpp deleted file mode 100644 index 4e5e43b9f5c..00000000000 --- a/mvvm/model/mvvm/commands/commandservice.cpp +++ /dev/null @@ -1,110 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/commands/commandservice.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/commands/commandservice.h" -#include "mvvm/commands/copyitemcommand.h" -#include "mvvm/commands/insertnewitemcommand.h" -#include "mvvm/commands/moveitemcommand.h" -#include "mvvm/commands/removeitemcommand.h" -#include "mvvm/commands/setvaluecommand.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionmodel.h" -#include <stdexcept> - -using namespace ModelView; - -CommandService::CommandService(SessionModel* model) : m_model(model), m_pause_record(false) {} - -void CommandService::setUndoRedoEnabled(bool value) -{ - if (value) - m_commands = std::make_unique<UndoStack>(); - else - m_commands.reset(); -} - -SessionItem* CommandService::insertNewItem(const item_factory_func_t& func, SessionItem* parent, - const TagRow& tagrow) -{ - if (!parent) - parent = m_model->rootItem(); - - int actual_row = tagrow.row < 0 ? parent->itemCount(tagrow.tag) : tagrow.row; - - return std::get<SessionItem*>( - process_command<InsertNewItemCommand>(func, parent, TagRow{tagrow.tag, actual_row})); -} - -SessionItem* CommandService::copyItem(const SessionItem* item, SessionItem* parent, - const TagRow& tagrow) -{ - if (!item) - return nullptr; - - if (parent->model() != m_model) - throw std::runtime_error( - "CommandService::copyItem() -> Item doesn't belong to given model"); - - int actual_row = tagrow.row < 0 ? parent->itemCount(tagrow.tag) : tagrow.row; - - return std::get<SessionItem*>( - process_command<CopyItemCommand>(item, parent, TagRow{tagrow.tag, actual_row})); -} - -bool CommandService::setData(SessionItem* item, const Variant& value, int role) -{ - if (!item) - return false; - - return std::get<bool>(process_command<SetValueCommand>(item, value, role)); -} - -void CommandService::removeItem(SessionItem* parent, const TagRow& tagrow) -{ - if (parent->model() != m_model) - throw std::runtime_error( - "CommandService::removeRow() -> Item doesn't belong to given model"); - - process_command<RemoveItemCommand>(parent, tagrow); -} - -void CommandService::moveItem(SessionItem* item, SessionItem* new_parent, const TagRow& tagrow) -{ - if (item->model() != m_model) - throw std::runtime_error( - "CommandService::removeRow() -> Item doesn't belong to given model"); - - if (new_parent->model() != m_model) - throw std::runtime_error( - "CommandService::removeRow() -> Parent doesn't belong to given model"); - - int actual_row = tagrow.row < 0 ? new_parent->itemCount(tagrow.tag) : tagrow.row; - - process_command<MoveItemCommand>(item, new_parent, TagRow{tagrow.tag, actual_row}); -} - -UndoStackInterface* CommandService::undoStack() const -{ - return m_commands.get(); -} - -void CommandService::setCommandRecordPause(bool value) -{ - m_pause_record = value; -} - -bool CommandService::provideUndo() const -{ - return m_commands && !m_pause_record; -} diff --git a/mvvm/model/mvvm/commands/commandservice.h b/mvvm/model/mvvm/commands/commandservice.h deleted file mode 100644 index 4cf98c727f7..00000000000 --- a/mvvm/model/mvvm/commands/commandservice.h +++ /dev/null @@ -1,85 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/commands/commandservice.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_COMMANDS_COMMANDSERVICE_H -#define BORNAGAIN_MVVM_MODEL_MVVM_COMMANDS_COMMANDSERVICE_H - -#include "mvvm/commands/commandresult.h" -#include "mvvm/commands/undostack.h" -#include "mvvm/core/variant.h" -#include "mvvm/model/function_types.h" -#include "mvvm/model_export.h" -#include <memory> - -class QUndoCommand; - -namespace ModelView { - -class SessionModel; -class SessionItem; -class TagRow; - -//! Provides undo/redo for all commands of SessionModel. - -class MVVM_MODEL_EXPORT CommandService { -public: - CommandService(SessionModel* model); - - void setUndoRedoEnabled(bool value); - - SessionItem* insertNewItem(const item_factory_func_t& func, SessionItem* parent, - const TagRow& tagrow); - - SessionItem* copyItem(const SessionItem* item, SessionItem* parent, const TagRow& tagrow); - - bool setData(SessionItem* item, const Variant& value, int role); - - void removeItem(SessionItem* parent, const TagRow& tagrow); - - void moveItem(SessionItem* item, SessionItem* new_parent, const TagRow& tagrow); - - UndoStackInterface* undoStack() const; - - void setCommandRecordPause(bool value); - -private: - template <typename C, typename... Args> CommandResult process_command(Args&&... args); - - bool provideUndo() const; - - SessionModel* m_model; - std::unique_ptr<UndoStackInterface> m_commands; - bool m_pause_record; -}; - -//! Creates and processes command of given type using given argument list. - -template <typename C, typename... Args> -CommandResult CommandService::process_command(Args&&... args) -{ - if (provideUndo()) { - // making shared because underlying QUndoStack requires ownership - auto command = std::make_shared<C>(std::forward<Args>(args)...); - m_commands->execute(command); - return command->result(); - } else { - C command(std::forward<Args>(args)...); - command.execute(); - return command.result(); - } -} - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_COMMANDS_COMMANDSERVICE_H diff --git a/mvvm/model/mvvm/commands/commandutils.cpp b/mvvm/model/mvvm/commands/commandutils.cpp deleted file mode 100644 index 36a2568682e..00000000000 --- a/mvvm/model/mvvm/commands/commandutils.cpp +++ /dev/null @@ -1,32 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/commands/commandutils.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/commands/commandutils.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/serialization/jsonitembackupstrategy.h" -#include "mvvm/serialization/jsonitemcopystrategy.h" - -std::unique_ptr<ModelView::ItemBackupStrategy> -ModelView::CreateItemBackupStrategy(const ModelView::SessionModel* model) -{ - assert(model); - return std::make_unique<JsonItemBackupStrategy>(model->factory()); -} - -std::unique_ptr<ModelView::ItemCopyStrategy> -ModelView::CreateItemCopyStrategy(const ModelView::SessionModel* model) -{ - assert(model); - return std::make_unique<JsonItemCopyStrategy>(model->factory()); -} diff --git a/mvvm/model/mvvm/commands/commandutils.h b/mvvm/model/mvvm/commands/commandutils.h deleted file mode 100644 index 7adb35dc46a..00000000000 --- a/mvvm/model/mvvm/commands/commandutils.h +++ /dev/null @@ -1,43 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/commands/commandutils.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_COMMANDS_COMMANDUTILS_H -#define BORNAGAIN_MVVM_MODEL_MVVM_COMMANDS_COMMANDUTILS_H - -//! @file mvvm/model/mvvm/commands/commandutils.h -//! Collection of various utility functions for command service. - -#include "mvvm/interfaces/itembackupstrategy.h" -#include "mvvm/interfaces/itemcopystrategy.h" -#include <memory> - -namespace ModelView { - -class SessionModel; - -//! Creates strategy suitable for item saving/restoring. Restored item will have same identifiers -//! as original. - -MVVM_MODEL_EXPORT std::unique_ptr<ItemBackupStrategy> -CreateItemBackupStrategy(const SessionModel* model); - -//! Returns strategy for item copying. Identifiers of the copy will be different from identifiers -//! of the original. - -MVVM_MODEL_EXPORT std::unique_ptr<ItemCopyStrategy> -CreateItemCopyStrategy(const SessionModel* model); - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_COMMANDS_COMMANDUTILS_H diff --git a/mvvm/model/mvvm/commands/copyitemcommand.cpp b/mvvm/model/mvvm/commands/copyitemcommand.cpp deleted file mode 100644 index 56eef0f9e60..00000000000 --- a/mvvm/model/mvvm/commands/copyitemcommand.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/commands/copyitemcommand.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/commands/copyitemcommand.h" -#include "mvvm/commands/commandutils.h" -#include "mvvm/interfaces/itembackupstrategy.h" -#include "mvvm/interfaces/itemcopystrategy.h" -#include "mvvm/model/path.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionmodel.h" -#include <sstream> - -using namespace ModelView; - -namespace { -std::string generate_description(const std::string& modelType, const TagRow& tagrow); -} // namespace - -struct CopyItemCommand::CopyItemCommandImpl { - TagRow tagrow; - std::unique_ptr<ItemBackupStrategy> backup_strategy; - Path item_path; - CopyItemCommandImpl(TagRow tagrow) : tagrow(std::move(tagrow)) {} -}; - -CopyItemCommand::CopyItemCommand(const SessionItem* item, SessionItem* parent, TagRow tagrow) - : AbstractItemCommand(parent), p_impl(std::make_unique<CopyItemCommandImpl>(std::move(tagrow))) -{ - setResult(nullptr); - - setDescription(generate_description(item->modelType(), p_impl->tagrow)); - p_impl->backup_strategy = CreateItemBackupStrategy(parent->model()); - p_impl->item_path = pathFromItem(parent); - - auto copy_strategy = CreateItemCopyStrategy(parent->model()); // to modify id's - auto item_copy = copy_strategy->createCopy(item); - - p_impl->backup_strategy->saveItem(item_copy.get()); -} - -CopyItemCommand::~CopyItemCommand() = default; - -void CopyItemCommand::undo_command() -{ - auto parent = itemFromPath(p_impl->item_path); - delete parent->takeItem(p_impl->tagrow); - setResult(nullptr); -} - -void CopyItemCommand::execute_command() -{ - auto parent = itemFromPath(p_impl->item_path); - auto item = p_impl->backup_strategy->restoreItem(); - if (parent->insertItem(item.get(), p_impl->tagrow)) { - auto result = item.release(); - setResult(result); - } else { - setResult(nullptr); - setObsolete(true); - } -} - -namespace { -std::string generate_description(const std::string& modelType, const TagRow& tagrow) -{ - std::ostringstream ostr; - ostr << "Copy item'" << modelType << "' tag:'" << tagrow.tag << "', row:" << tagrow.row; - return ostr.str(); -} -} // namespace diff --git a/mvvm/model/mvvm/commands/copyitemcommand.h b/mvvm/model/mvvm/commands/copyitemcommand.h deleted file mode 100644 index 31bb7ef8f37..00000000000 --- a/mvvm/model/mvvm/commands/copyitemcommand.h +++ /dev/null @@ -1,42 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/commands/copyitemcommand.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_COMMANDS_COPYITEMCOMMAND_H -#define BORNAGAIN_MVVM_MODEL_MVVM_COMMANDS_COPYITEMCOMMAND_H - -#include "mvvm/commands/abstractitemcommand.h" - -namespace ModelView { - -class SessionItem; -class TagRow; - -//! Command to copy an item. - -class MVVM_MODEL_EXPORT CopyItemCommand : public AbstractItemCommand { -public: - CopyItemCommand(const SessionItem* item, SessionItem* parent, TagRow tagrow); - ~CopyItemCommand() override; - -private: - void undo_command() override; - void execute_command() override; - - struct CopyItemCommandImpl; - std::unique_ptr<CopyItemCommandImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_COMMANDS_COPYITEMCOMMAND_H diff --git a/mvvm/model/mvvm/commands/insertnewitemcommand.cpp b/mvvm/model/mvvm/commands/insertnewitemcommand.cpp deleted file mode 100644 index 741aca6d995..00000000000 --- a/mvvm/model/mvvm/commands/insertnewitemcommand.cpp +++ /dev/null @@ -1,83 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/commands/insertnewitemcommand.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/commands/insertnewitemcommand.h" -#include "mvvm/model/path.h" -#include "mvvm/model/sessionitem.h" -#include <sstream> - -using namespace ModelView; - -namespace { -std::string generate_description(const std::string& modelType, const TagRow& tagrow); -} // namespace - -struct InsertNewItemCommand::InsertNewItemCommandImpl { - item_factory_func_t factory_func; - TagRow tagrow; - Path item_path; - std::string initial_identifier; - InsertNewItemCommandImpl(item_factory_func_t func, TagRow tagrow) - : factory_func(std::move(func)), tagrow(std::move(tagrow)) - { - } -}; - -InsertNewItemCommand::InsertNewItemCommand(item_factory_func_t func, SessionItem* parent, - const TagRow& tagrow) - : AbstractItemCommand(parent), p_impl(std::make_unique<InsertNewItemCommandImpl>(func, tagrow)) -{ - setResult(nullptr); - p_impl->item_path = pathFromItem(parent); -} - -InsertNewItemCommand::~InsertNewItemCommand() = default; - -void InsertNewItemCommand::undo_command() -{ - auto parent = itemFromPath(p_impl->item_path); - auto item = parent->takeItem(p_impl->tagrow); - // saving identifier for later redo - if (p_impl->initial_identifier.empty()) - p_impl->initial_identifier = item->identifier(); - delete item; - setResult(nullptr); -} - -void InsertNewItemCommand::execute_command() -{ - auto parent = itemFromPath(p_impl->item_path); - auto child = p_impl->factory_func().release(); - // here we restore original identifier to get exactly same item on consequitive undo/redo - if (!p_impl->initial_identifier.empty()) - child->setData(QVariant::fromValue(p_impl->initial_identifier), ItemDataRole::IDENTIFIER, - /*direct*/ true); - - setDescription(generate_description(child->modelType(), p_impl->tagrow)); - if (parent->insertItem(child, p_impl->tagrow)) { - setResult(child); - } else { - delete child; - setObsolete(true); - } -} - -namespace { -std::string generate_description(const std::string& modelType, const TagRow& tagrow) -{ - std::ostringstream ostr; - ostr << "New item type '" << modelType << "' tag:'" << tagrow.tag << "', row:" << tagrow.row; - return ostr.str(); -} -} // namespace diff --git a/mvvm/model/mvvm/commands/insertnewitemcommand.h b/mvvm/model/mvvm/commands/insertnewitemcommand.h deleted file mode 100644 index 733979d830e..00000000000 --- a/mvvm/model/mvvm/commands/insertnewitemcommand.h +++ /dev/null @@ -1,43 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/commands/insertnewitemcommand.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_COMMANDS_INSERTNEWITEMCOMMAND_H -#define BORNAGAIN_MVVM_MODEL_MVVM_COMMANDS_INSERTNEWITEMCOMMAND_H - -#include "mvvm/commands/abstractitemcommand.h" -#include "mvvm/model/function_types.h" - -namespace ModelView { - -class SessionItem; -class TagRow; - -//! Command for unddo/redo to insert new item. - -class MVVM_MODEL_EXPORT InsertNewItemCommand : public AbstractItemCommand { -public: - InsertNewItemCommand(item_factory_func_t func, SessionItem* parent, const TagRow& tagrow); - ~InsertNewItemCommand() override; - -private: - void undo_command() override; - void execute_command() override; - - struct InsertNewItemCommandImpl; - std::unique_ptr<InsertNewItemCommandImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_COMMANDS_INSERTNEWITEMCOMMAND_H diff --git a/mvvm/model/mvvm/commands/moveitemcommand.cpp b/mvvm/model/mvvm/commands/moveitemcommand.cpp deleted file mode 100644 index cb82b6fda6d..00000000000 --- a/mvvm/model/mvvm/commands/moveitemcommand.cpp +++ /dev/null @@ -1,128 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/commands/moveitemcommand.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/commands/moveitemcommand.h" -#include "mvvm/model/itemutils.h" -#include "mvvm/model/path.h" -#include "mvvm/model/sessionitem.h" -#include <sstream> -#include <stdexcept> - -using namespace ModelView; - -namespace { -void check_input_data(const SessionItem* item, const SessionItem* parent); -std::string generate_description(const TagRow& tagrow); -} // namespace - -struct MoveItemCommand::MoveItemCommandImpl { - TagRow target_tagrow; - Path target_parent_path; - Path original_parent_path; - TagRow original_tagrow; - MoveItemCommandImpl(TagRow tagrow) : target_tagrow(std::move(tagrow)) - { - if (target_tagrow.row < 0) - throw std::runtime_error("MoveItemCommand() -> Error. Uninitialized target row"); - } -}; - -MoveItemCommand::MoveItemCommand(SessionItem* item, SessionItem* new_parent, TagRow tagrow) - : AbstractItemCommand(new_parent), p_impl(std::make_unique<MoveItemCommandImpl>(tagrow)) -{ - setResult(true); - - check_input_data(item, new_parent); - setDescription(generate_description(p_impl->target_tagrow)); - - p_impl->target_parent_path = pathFromItem(new_parent); - p_impl->original_parent_path = pathFromItem(item->parent()); - p_impl->original_tagrow = item->tagRow(); - - if (Utils::IsSinglePropertyTag(*item->parent(), p_impl->original_tagrow.tag)) - throw std::runtime_error("MoveItemCommand::MoveItemCommand() -> Single property tag."); - - if (Utils::IsSinglePropertyTag(*new_parent, p_impl->target_tagrow.tag)) - throw std::runtime_error("MoveItemCommand::MoveItemCommand() -> Single property tag."); - - if (item->parent() == new_parent) { - if (p_impl->target_tagrow.row >= new_parent->itemCount(p_impl->target_tagrow.tag)) - throw std::runtime_error( - "MoveCommand::MoveCommand() -> move index exceeds number of items in a tag"); - } -} - -MoveItemCommand::~MoveItemCommand() = default; - -void MoveItemCommand::undo_command() -{ - // first find items - auto current_parent = itemFromPath(p_impl->target_parent_path); - auto target_parent = itemFromPath(p_impl->original_parent_path); - - // then make manipulations - auto taken = current_parent->takeItem(p_impl->target_tagrow); - target_parent->insertItem(taken, p_impl->original_tagrow); - - // adjusting new addresses - p_impl->target_parent_path = pathFromItem(current_parent); - p_impl->original_parent_path = pathFromItem(target_parent); -} - -void MoveItemCommand::execute_command() -{ - // first find items - auto original_parent = itemFromPath(p_impl->original_parent_path); - auto target_parent = itemFromPath(p_impl->target_parent_path); - - // then make manipulations - auto taken = original_parent->takeItem(p_impl->original_tagrow); - - if (!taken) - throw std::runtime_error("MoveItemCommand::execute() -> Can't take an item."); - - bool succeeded = target_parent->insertItem(taken, p_impl->target_tagrow); - if (!succeeded) - throw std::runtime_error("MoveItemCommand::execute() -> Can't insert item."); - - // adjusting new addresses - p_impl->target_parent_path = pathFromItem(target_parent); - p_impl->original_parent_path = pathFromItem(original_parent); -} - -namespace { -void check_input_data(const SessionItem* item, const SessionItem* parent) -{ - if (!item || !item->model()) - throw std::runtime_error("MoveItemCommand::MoveItemCommand() -> Invalid input item"); - - if (!parent || !parent->model()) - throw std::runtime_error("MoveItemCommand::MoveItemCommand() -> Invalid parent item"); - - if (item->model() != parent->model()) - throw std::runtime_error( - "MoveItemCommand::MoveItemCommand() -> Items belong to different models"); - - if (!item->parent()) - throw std::runtime_error( - "MoveItemCommand::MoveItemCommand() -> Item doesn't have a parent"); -} - -std::string generate_description(const TagRow& tagrow) -{ - std::ostringstream ostr; - ostr << "Move item to tag '" << tagrow.tag << "', row:" << tagrow.row; - return ostr.str(); -} -} // namespace diff --git a/mvvm/model/mvvm/commands/moveitemcommand.h b/mvvm/model/mvvm/commands/moveitemcommand.h deleted file mode 100644 index 4831f32b6f0..00000000000 --- a/mvvm/model/mvvm/commands/moveitemcommand.h +++ /dev/null @@ -1,43 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/commands/moveitemcommand.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_COMMANDS_MOVEITEMCOMMAND_H -#define BORNAGAIN_MVVM_MODEL_MVVM_COMMANDS_MOVEITEMCOMMAND_H - -#include "mvvm/commands/abstractitemcommand.h" -#include <memory> - -namespace ModelView { - -class SessionItem; -class TagRow; - -//! Command for unddo/redo framework to move item from one parent to another. - -class MVVM_MODEL_EXPORT MoveItemCommand : public AbstractItemCommand { -public: - MoveItemCommand(SessionItem* item, SessionItem* new_parent, TagRow tagrow); - ~MoveItemCommand() override; - -private: - void undo_command() override; - void execute_command() override; - - struct MoveItemCommandImpl; - std::unique_ptr<MoveItemCommandImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_COMMANDS_MOVEITEMCOMMAND_H diff --git a/mvvm/model/mvvm/commands/removeitemcommand.cpp b/mvvm/model/mvvm/commands/removeitemcommand.cpp deleted file mode 100644 index 04c0534600f..00000000000 --- a/mvvm/model/mvvm/commands/removeitemcommand.cpp +++ /dev/null @@ -1,76 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/commands/removeitemcommand.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/commands/removeitemcommand.h" -#include "mvvm/commands/commandutils.h" -#include "mvvm/interfaces/itembackupstrategy.h" -#include "mvvm/model/path.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionmodel.h" -#include <sstream> - -using namespace ModelView; - -namespace { -std::string generate_description(const TagRow& tagrow); -} // namespace - -struct RemoveItemCommand::RemoveItemCommandImpl { - TagRow tagrow; - std::unique_ptr<ItemBackupStrategy> backup_strategy; - Path item_path; - RemoveItemCommandImpl(TagRow tagrow) : tagrow(std::move(tagrow)) {} -}; - -RemoveItemCommand::RemoveItemCommand(SessionItem* parent, TagRow tagrow) - : AbstractItemCommand(parent) - , p_impl(std::make_unique<RemoveItemCommandImpl>(std::move(tagrow))) -{ - setResult(false); - - setDescription(generate_description(p_impl->tagrow)); - p_impl->backup_strategy = CreateItemBackupStrategy(parent->model()); - p_impl->item_path = pathFromItem(parent); -} - -RemoveItemCommand::~RemoveItemCommand() = default; - -void RemoveItemCommand::undo_command() -{ - auto parent = itemFromPath(p_impl->item_path); - auto reco_item = p_impl->backup_strategy->restoreItem(); - parent->insertItem(reco_item.release(), p_impl->tagrow); -} - -void RemoveItemCommand::execute_command() -{ - auto parent = itemFromPath(p_impl->item_path); - if (auto child = parent->takeItem(p_impl->tagrow); child) { - p_impl->backup_strategy->saveItem(child); - delete child; - setResult(true); - } else { - setResult(false); - setObsolete(true); - } -} - -namespace { -std::string generate_description(const TagRow& tagrow) -{ - std::ostringstream ostr; - ostr << "Remove item from tag '" << tagrow.tag << "', row " << tagrow.row; - return ostr.str(); -} -} // namespace diff --git a/mvvm/model/mvvm/commands/removeitemcommand.h b/mvvm/model/mvvm/commands/removeitemcommand.h deleted file mode 100644 index f171e8d55d4..00000000000 --- a/mvvm/model/mvvm/commands/removeitemcommand.h +++ /dev/null @@ -1,42 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/commands/removeitemcommand.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_COMMANDS_REMOVEITEMCOMMAND_H -#define BORNAGAIN_MVVM_MODEL_MVVM_COMMANDS_REMOVEITEMCOMMAND_H - -#include "mvvm/commands/abstractitemcommand.h" - -namespace ModelView { - -class SessionItem; -class TagRow; - -//! Command for unddo/redo framework to remove item from a model using child's tag and row. - -class MVVM_MODEL_EXPORT RemoveItemCommand : public AbstractItemCommand { -public: - RemoveItemCommand(SessionItem* parent, TagRow tagrow); - ~RemoveItemCommand() override; - -private: - void undo_command() override; - void execute_command() override; - - struct RemoveItemCommandImpl; - std::unique_ptr<RemoveItemCommandImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_COMMANDS_REMOVEITEMCOMMAND_H diff --git a/mvvm/model/mvvm/commands/setvaluecommand.cpp b/mvvm/model/mvvm/commands/setvaluecommand.cpp deleted file mode 100644 index 21b9658d211..00000000000 --- a/mvvm/model/mvvm/commands/setvaluecommand.cpp +++ /dev/null @@ -1,76 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/commands/setvaluecommand.cpp -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/commands/setvaluecommand.h" -#include "mvvm/core/variant.h" -#include "mvvm/model/modelutils.h" -#include "mvvm/model/path.h" -#include "mvvm/model/sessionitem.h" -#include <sstream> - -namespace { -std::string generate_description(const std::string& str, int role); -} // namespace - -using namespace ModelView; - -struct SetValueCommand::SetValueCommandImpl { - Variant m_value; //! Value to set as a result of command execution. - int m_role; - Path m_item_path; - SetValueCommandImpl(Variant value, int role) : m_value(std::move(value)), m_role(role) {} -}; - -// ---------------------------------------------------------------------------- - -SetValueCommand::SetValueCommand(SessionItem* item, Variant value, int role) - : AbstractItemCommand(item) - , p_impl(std::make_unique<SetValueCommandImpl>(std::move(value), role)) -{ - setResult(false); - - setDescription(generate_description(p_impl->m_value.toString().toStdString(), role)); - p_impl->m_item_path = pathFromItem(item); -} - -SetValueCommand::~SetValueCommand() = default; - -void SetValueCommand::undo_command() -{ - swap_values(); -} - -void SetValueCommand::execute_command() -{ - swap_values(); -} - -void SetValueCommand::swap_values() -{ - auto item = itemFromPath(p_impl->m_item_path); - auto old = item->data<Variant>(p_impl->m_role); - auto result = item->setData(p_impl->m_value, p_impl->m_role, /*direct*/ true); - setResult(result); - setObsolete(!result); - p_impl->m_value = old; -} - -namespace { -std::string generate_description(const std::string& str, int role) -{ - std::ostringstream ostr; - ostr << "Set value: " << str << ", role:" << role; - return ostr.str(); -} -} // namespace diff --git a/mvvm/model/mvvm/commands/setvaluecommand.h b/mvvm/model/mvvm/commands/setvaluecommand.h deleted file mode 100644 index 86594493f73..00000000000 --- a/mvvm/model/mvvm/commands/setvaluecommand.h +++ /dev/null @@ -1,43 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/commands/setvaluecommand.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_COMMANDS_SETVALUECOMMAND_H -#define BORNAGAIN_MVVM_MODEL_MVVM_COMMANDS_SETVALUECOMMAND_H - -#include "mvvm/commands/abstractitemcommand.h" -#include "mvvm/core/variant.h" - -namespace ModelView { - -class SessionItem; - -//! Command for unddo/redo framework to set the data of SessionItem. - -class MVVM_MODEL_EXPORT SetValueCommand : public AbstractItemCommand { -public: - SetValueCommand(SessionItem* item, Variant value, int role); - ~SetValueCommand() override; - -private: - void undo_command() override; - void execute_command() override; - void swap_values(); - - struct SetValueCommandImpl; - std::unique_ptr<SetValueCommandImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_COMMANDS_SETVALUECOMMAND_H diff --git a/mvvm/model/mvvm/commands/undostack.cpp b/mvvm/model/mvvm/commands/undostack.cpp deleted file mode 100644 index c6b6d274b57..00000000000 --- a/mvvm/model/mvvm/commands/undostack.cpp +++ /dev/null @@ -1,102 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/commands/undostack.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/commands/undostack.h" -#include "mvvm/commands/commandadapter.h" -#include <QUndoStack> - -using namespace ModelView; - -struct UndoStack::UndoStackImpl { - std::unique_ptr<QUndoStack> m_undoStack; - UndoStackImpl() : m_undoStack(std::make_unique<QUndoStack>()) {} - QUndoStack* undoStack() { return m_undoStack.get(); } -}; - -UndoStack::UndoStack() : p_impl(std::make_unique<UndoStackImpl>()) {} - -void UndoStack::execute(std::shared_ptr<AbstractItemCommand> command) -{ - // Wrapping command for Qt. It will be executed by Qt after push. - auto adapter = new CommandAdapter(std::move(command)); - p_impl->undoStack()->push(adapter); -} - -UndoStack::~UndoStack() = default; - -bool UndoStack::isActive() const -{ - return p_impl->undoStack()->isActive(); -} - -bool UndoStack::canUndo() const -{ - return p_impl->undoStack()->canUndo(); -} - -bool UndoStack::canRedo() const -{ - return p_impl->undoStack()->canRedo(); -} - -int UndoStack::index() const -{ - return p_impl->undoStack()->index(); -} - -int UndoStack::count() const -{ - return p_impl->undoStack()->count(); -} - -void UndoStack::undo() -{ - return p_impl->undoStack()->undo(); -} - -void UndoStack::redo() -{ - return p_impl->undoStack()->redo(); -} - -void UndoStack::clear() -{ - return p_impl->undoStack()->clear(); -} - -void UndoStack::setUndoLimit(int limit) -{ - return p_impl->undoStack()->setUndoLimit(limit); -} - -//! Returns underlying QUndoStack if given object can be casted to UndoStack instance. -//! This method is used to "convert" current instance to Qt implementation, and use it with other -//! Qt widgets, if necessary. - -QUndoStack* UndoStack::qtUndoStack(UndoStackInterface* stack_interface) -{ - if (auto stack = dynamic_cast<UndoStack*>(stack_interface); stack) - return stack->p_impl->undoStack(); - return nullptr; -} - -void UndoStack::beginMacro(const std::string& name) -{ - p_impl->undoStack()->beginMacro(QString::fromStdString(name)); -} - -void UndoStack::endMacro() -{ - p_impl->undoStack()->endMacro(); -} diff --git a/mvvm/model/mvvm/commands/undostack.h b/mvvm/model/mvvm/commands/undostack.h deleted file mode 100644 index c3726d715f5..00000000000 --- a/mvvm/model/mvvm/commands/undostack.h +++ /dev/null @@ -1,60 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/commands/undostack.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_COMMANDS_UNDOSTACK_H -#define BORNAGAIN_MVVM_MODEL_MVVM_COMMANDS_UNDOSTACK_H - -#include "mvvm/interfaces/undostackinterface.h" -#include "mvvm/model_export.h" -#include <memory> - -class QUndoStack; - -namespace ModelView { - -//! Default undo stack implementation. Internally relies on QUndoStack. -//! It serves two goals: a) hides Qt usage b) simplifies future refactoring toward Qt-independent -//! libmvvm_model library. - -class MVVM_MODEL_EXPORT UndoStack : public UndoStackInterface { -public: - UndoStack(); - ~UndoStack() override; - - //! Executes the command, then pushes it in the stack for possible undo. - void execute(std::shared_ptr<AbstractItemCommand> command) override; - - bool isActive() const override; - bool canUndo() const override; - bool canRedo() const override; - int index() const override; - int count() const override; - void undo() override; - void redo() override; - void clear() override; - void setUndoLimit(int limit) override; - - static QUndoStack* qtUndoStack(UndoStackInterface* stack_interface); - - void beginMacro(const std::string& name) override; - void endMacro() override; - -private: - struct UndoStackImpl; - std::unique_ptr<UndoStackImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_COMMANDS_UNDOSTACK_H diff --git a/mvvm/model/mvvm/core/CMakeLists.txt b/mvvm/model/mvvm/core/CMakeLists.txt deleted file mode 100644 index 2a745fe708f..00000000000 --- a/mvvm/model/mvvm/core/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -target_sources(${library_name} PRIVATE - filesystem.h - types.h - uniqueidgenerator.cpp - uniqueidgenerator.h - variant.h - version.h -) diff --git a/mvvm/model/mvvm/core/filesystem.h b/mvvm/model/mvvm/core/filesystem.h deleted file mode 100644 index 6a690cc75c8..00000000000 --- a/mvvm/model/mvvm/core/filesystem.h +++ /dev/null @@ -1,93 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/core/filesystem.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_CORE_FILESYSTEM_H -#define BORNAGAIN_MVVM_MODEL_MVVM_CORE_FILESYSTEM_H - -//! @file mvvm/model/mvvm/core/filesystem.h of <filesystem> library for older compilers (<gcc8.0) -//! See -//! https://stackoverflow.com/questions/53365538/how-to-determine-whether-to-use-filesystem-or-experimental-filesystem - -// We haven't checked which filesystem to include yet -#ifndef INCLUDE_STD_FILESYSTEM_EXPERIMENTAL - -// Check for feature test macro for <filesystem> -#if defined(__cpp_lib_filesystem) -#define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 0 - -// Check for feature test macro for <experimental/filesystem> -#elif defined(__cpp_lib_experimental_filesystem) -#define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 1 - -// We can't check if headers exist... -// Let's assume experimental to be safe -#elif !defined(__has_include) -#define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 1 - -// Check if the header "<filesystem>" exists -#elif __has_include(<filesystem>) - -// If we're compiling on Visual Studio and are not compiling with C++17, we need to use experimental -#ifdef _MSC_VER - -// Check and include header that defines "_HAS_CXX17" -#if __has_include(<yvals_core.h>) -#include <yvals_core.h> - -// Check for enabled C++17 support -#if defined(_HAS_CXX17) && _HAS_CXX17 -// We're using C++17, so let's use the normal version -#define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 0 -#endif -#endif - -// If the marco isn't defined yet, that means any of the other VS specific checks failed, so we need -// to use experimental -#ifndef INCLUDE_STD_FILESYSTEM_EXPERIMENTAL -#define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 1 -#endif - -// Not on Visual Studio. Let's use the normal version -#else // #ifdef _MSC_VER -#define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 0 -#endif - -// Check if the header "<filesystem>" exists -#elif __has_include(<experimental/filesystem>) -#define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 1 - -// Fail if neither header is available with a nice error message -#else -#error Could not find system header "<filesystem>" or "<experimental/filesystem>" -#endif - -// We priously determined that we need the exprimental version -#if INCLUDE_STD_FILESYSTEM_EXPERIMENTAL -// Include it -#include <experimental/filesystem> - -// We need the alias from std::experimental::filesystem to std::filesystem -namespace std { -namespace filesystem = experimental::filesystem; -} - -// We have a decent compiler and can use the normal version -#else -// Include it -#include <filesystem> -#endif - -#endif // #ifndef INCLUDE_STD_FILESYSTEM_EXPERIMENTAL - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_CORE_FILESYSTEM_H diff --git a/mvvm/model/mvvm/core/types.h b/mvvm/model/mvvm/core/types.h deleted file mode 100644 index 4fcdf5c89dd..00000000000 --- a/mvvm/model/mvvm/core/types.h +++ /dev/null @@ -1,27 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/core/types.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_CORE_TYPES_H -#define BORNAGAIN_MVVM_MODEL_MVVM_CORE_TYPES_H - -#include <string> - -namespace ModelView { - -using identifier_type = std::string; -using model_type = std::string; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_CORE_TYPES_H diff --git a/mvvm/model/mvvm/core/uniqueidgenerator.cpp b/mvvm/model/mvvm/core/uniqueidgenerator.cpp deleted file mode 100644 index 54ad825c5f6..00000000000 --- a/mvvm/model/mvvm/core/uniqueidgenerator.cpp +++ /dev/null @@ -1,23 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/core/uniqueidgenerator.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/core/uniqueidgenerator.h" -#include <QUuid> - -using namespace ModelView; - -identifier_type UniqueIdGenerator::generate() -{ - return QUuid::createUuid().toString().toStdString(); -} diff --git a/mvvm/model/mvvm/core/uniqueidgenerator.h b/mvvm/model/mvvm/core/uniqueidgenerator.h deleted file mode 100644 index 38a3256415a..00000000000 --- a/mvvm/model/mvvm/core/uniqueidgenerator.h +++ /dev/null @@ -1,40 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/core/uniqueidgenerator.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_CORE_UNIQUEIDGENERATOR_H -#define BORNAGAIN_MVVM_MODEL_MVVM_CORE_UNIQUEIDGENERATOR_H - -#include "mvvm/core/types.h" -#include "mvvm/model_export.h" - -namespace ModelView { - -/*! -@class UniqueIdGenerator -@brief Provides generation of unique SessionItem itentifier. - -In the future might be turned to singleton to keep track of all generated identifier -and make sure, that SessionItem identifiers loaded from disk, are different from those -generated during dynamic session. For the moment though, we rely on zero-probability of -such event. -*/ - -class MVVM_MODEL_EXPORT UniqueIdGenerator { -public: - static identifier_type generate(); -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_CORE_UNIQUEIDGENERATOR_H diff --git a/mvvm/model/mvvm/core/variant.h b/mvvm/model/mvvm/core/variant.h deleted file mode 100644 index ac0c2e35cd5..00000000000 --- a/mvvm/model/mvvm/core/variant.h +++ /dev/null @@ -1,25 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/core/variant.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_CORE_VARIANT_H -#define BORNAGAIN_MVVM_MODEL_MVVM_CORE_VARIANT_H - -//! @file mvvm/model/mvvm/core/variant.h -//! Defines variant type (intermediate step toward migration from QVariant to std::variant). - -#include <QVariant> - -using Variant = QVariant; - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_CORE_VARIANT_H diff --git a/mvvm/model/mvvm/core/version.h b/mvvm/model/mvvm/core/version.h deleted file mode 100644 index 758d96a7066..00000000000 --- a/mvvm/model/mvvm/core/version.h +++ /dev/null @@ -1,55 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/core/version.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_CORE_VERSION_H -#define BORNAGAIN_MVVM_MODEL_MVVM_CORE_VERSION_H - -//! @file mvvm/model/mvvm/core/version.h -//! Automatically generated from mvvm_version.h.in - -#include <string> - -namespace ModelView { - -//! Returns major project version. -inline int ProjectVersionMajor() -{ - const int project_version_major = 0; - return project_version_major; -} - -//! Returns minor project version. -inline int ProjectVersionMinor() -{ - const int project_version_minor = 2; - return project_version_minor; -} - -//! Returns patch project version. -inline int ProjectVersionPatch() -{ - const int project_version_path = 0; - return project_version_path; -} - -//! Returns project version string -inline std::string ProjectVersion() -{ - const std::string project_version = "0.2.0"; - return project_version; -} - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_CORE_VERSION_H diff --git a/mvvm/model/mvvm/factories/CMakeLists.txt b/mvvm/model/mvvm/factories/CMakeLists.txt deleted file mode 100644 index 1730bd52b10..00000000000 --- a/mvvm/model/mvvm/factories/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -target_sources(${library_name} PRIVATE - itemcataloguefactory.cpp - itemcataloguefactory.h - itemconverterfactory.cpp - itemconverterfactory.h - modelconverterfactory.cpp - modelconverterfactory.h - modeldocumentfactory.cpp - modeldocumentfactory.h - projectmanagerfactory.cpp - projectmanagerfactory.h -) diff --git a/mvvm/model/mvvm/factories/itemcataloguefactory.cpp b/mvvm/model/mvvm/factories/itemcataloguefactory.cpp deleted file mode 100644 index 84f6bc95c0f..00000000000 --- a/mvvm/model/mvvm/factories/itemcataloguefactory.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/factories/itemcataloguefactory.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/factories/itemcataloguefactory.h" -#include "mvvm/standarditems/standarditemincludes.h" - -using namespace ModelView; - -std::unique_ptr<ItemCatalogue> ModelView::CreateStandardItemCatalogue() -{ - auto result = std::make_unique<ItemCatalogue>(); - result->registerItem<ColorMapItem>(); - result->registerItem<ColorMapViewportItem>(); - result->registerItem<CompoundItem>(); - result->registerItem<ContainerItem>(); - result->registerItem<Data1DItem>(); - result->registerItem<Data2DItem>(); - result->registerItem<FixedBinAxisItem>(); - result->registerItem<GraphItem>(); - result->registerItem<GraphViewportItem>(); - result->registerItem<LinkedItem>(); - result->registerItem<PenItem>(); - result->registerItem<PointwiseAxisItem>(); - result->registerItem<PropertyItem>(); - result->registerItem<SessionItem>(); - result->registerItem<TextItem>(); - result->registerItem<VectorItem>(); - result->registerItem<ViewportAxisItem>(); - return result; -} diff --git a/mvvm/model/mvvm/factories/itemcataloguefactory.h b/mvvm/model/mvvm/factories/itemcataloguefactory.h deleted file mode 100644 index cfa0aeed1a3..00000000000 --- a/mvvm/model/mvvm/factories/itemcataloguefactory.h +++ /dev/null @@ -1,27 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/factories/itemcataloguefactory.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_FACTORIES_ITEMCATALOGUEFACTORY_H -#define BORNAGAIN_MVVM_MODEL_MVVM_FACTORIES_ITEMCATALOGUEFACTORY_H - -#include "mvvm/model/itemcatalogue.h" - -namespace ModelView { - -//! Creates a catalog of items supported by SessionModel out-of-the-box. -MVVM_MODEL_EXPORT std::unique_ptr<ItemCatalogue> CreateStandardItemCatalogue(); - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_FACTORIES_ITEMCATALOGUEFACTORY_H diff --git a/mvvm/model/mvvm/factories/itemconverterfactory.cpp b/mvvm/model/mvvm/factories/itemconverterfactory.cpp deleted file mode 100644 index 75145240197..00000000000 --- a/mvvm/model/mvvm/factories/itemconverterfactory.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/factories/itemconverterfactory.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/factories/itemconverterfactory.h" -#include "mvvm/serialization/jsonitem_types.h" -#include "mvvm/serialization/jsonitemconverter.h" - -namespace ModelView { - -//! Creates JSON item converter intended for item cloning. -//! Saves full deep copy of item to JSON. When restoring from JSON, reconstruct everything, -//! including item's unique ID. Used for backup. - -std::unique_ptr<JsonItemConverterInterface> -CreateItemCloneConverter(const ItemFactoryInterface* item_factory) -{ - ConverterContext context{item_factory, ConverterMode::clone}; - return std::make_unique<JsonItemConverter>(context); -} - -//! Creates JSON item converter intended for item copying. -//! Saves full deep copy of item to JSON. When restoring from JSON, will regenerate item's ID -//! to make it unique. Used for copying of item together with its children. - -std::unique_ptr<JsonItemConverterInterface> -CreateItemCopyConverter(const ItemFactoryInterface* item_factory) -{ - ConverterContext context{item_factory, ConverterMode::copy}; - return std::make_unique<JsonItemConverter>(context); -} - -//! Creates JSON item converter intended for saving on disk. -//! When saving on disk: -//! + Only selected data roles of items are saved (i.e. DATA, IDENTIFIER) -//! + All tags with its content are saved as usual -//! When loading from disk: -//! + Only selected roles are taken from JSON (i.e. DATA, IDENTIFIER), other roles (e.g. TOOLTIPS) -//! are taken from memory. -//! + Property tags are updated, universal tags reconstructed. - -std::unique_ptr<JsonItemConverterInterface> -CreateItemProjectConverter(const ItemFactoryInterface* item_factory) -{ - ConverterContext context{item_factory, ConverterMode::project}; - return std::make_unique<JsonItemConverter>(context); -} - -} // namespace ModelView diff --git a/mvvm/model/mvvm/factories/itemconverterfactory.h b/mvvm/model/mvvm/factories/itemconverterfactory.h deleted file mode 100644 index 57ee5c899da..00000000000 --- a/mvvm/model/mvvm/factories/itemconverterfactory.h +++ /dev/null @@ -1,45 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/factories/itemconverterfactory.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_FACTORIES_ITEMCONVERTERFACTORY_H -#define BORNAGAIN_MVVM_MODEL_MVVM_FACTORIES_ITEMCONVERTERFACTORY_H - -//! @file mvvm/model/mvvm/factories/itemconverterfactory.h -//! Collection of factory functions to create SessionItem converters to/from serialized content. - -#include "mvvm/serialization/jsonitemconverterinterface.h" -#include <memory> - -namespace ModelView { - -class ItemFactoryInterface; - -//! Creates JSON item converter intended for item cloning. - -MVVM_MODEL_EXPORT std::unique_ptr<JsonItemConverterInterface> -CreateItemCloneConverter(const ItemFactoryInterface* item_factory); - -//! Creates JSON item converter intended for item copying. - -MVVM_MODEL_EXPORT std::unique_ptr<JsonItemConverterInterface> -CreateItemCopyConverter(const ItemFactoryInterface* item_factory); - -//! Creates JSON item converter intended for saving on disk. - -MVVM_MODEL_EXPORT std::unique_ptr<JsonItemConverterInterface> -CreateItemProjectConverter(const ItemFactoryInterface* item_factory); - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_FACTORIES_ITEMCONVERTERFACTORY_H diff --git a/mvvm/model/mvvm/factories/modelconverterfactory.cpp b/mvvm/model/mvvm/factories/modelconverterfactory.cpp deleted file mode 100644 index d8558fa0901..00000000000 --- a/mvvm/model/mvvm/factories/modelconverterfactory.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/factories/modelconverterfactory.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/factories/modelconverterfactory.h" -#include "mvvm/factories/itemconverterfactory.h" -#include "mvvm/serialization/jsonitem_types.h" -#include "mvvm/serialization/jsonmodelconverter.h" - -//! Creates a JSON model converter intended for model cloning. -//! Saves a full deep copy of model in JSON. When restoring, reconstruct full copy. -//! This will lead to item ID's which are identical to original. - -std::unique_ptr<ModelView::JsonModelConverterInterface> ModelView::CreateModelCloneConverter() -{ - return std::make_unique<JsonModelConverter>(ConverterMode::clone); -} - -//! Creates a JSON model converter intended for model copying. -//! Saves a full deep copy of model in JSON. When restoring, reconstruct full copy and regenerate -//! item's ID to make them unique. - -std::unique_ptr<ModelView::JsonModelConverterInterface> ModelView::CreateModelCopyConverter() -{ - return std::make_unique<JsonModelConverter>(ConverterMode::copy); -} - -//! Creates a JSON model converter intended for save/load of the project on disk. -//! When saving to disk, only certain data is saved. When loading from disk, items -//! in memory is gently updated from JSON content. - -std::unique_ptr<ModelView::JsonModelConverterInterface> ModelView::CreateModelProjectConverter() -{ - return std::make_unique<JsonModelConverter>(ConverterMode::project); -} diff --git a/mvvm/model/mvvm/factories/modelconverterfactory.h b/mvvm/model/mvvm/factories/modelconverterfactory.h deleted file mode 100644 index 8f92735937d..00000000000 --- a/mvvm/model/mvvm/factories/modelconverterfactory.h +++ /dev/null @@ -1,37 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/factories/modelconverterfactory.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_FACTORIES_MODELCONVERTERFACTORY_H -#define BORNAGAIN_MVVM_MODEL_MVVM_FACTORIES_MODELCONVERTERFACTORY_H - -//! @file mvvm/model/mvvm/factories/modelconverterfactory.h -//! Collection of factory functions to create SessionModel converters to/from serialized content. - -#include "mvvm/serialization/jsonmodelconverterinterface.h" -#include <memory> - -namespace ModelView { - -//! Creates a JSON model converter intended for model cloning. -MVVM_MODEL_EXPORT std::unique_ptr<JsonModelConverterInterface> CreateModelCloneConverter(); - -//! Creates a JSON model converter intended for model copying. -MVVM_MODEL_EXPORT std::unique_ptr<JsonModelConverterInterface> CreateModelCopyConverter(); - -//! Creates a JSON model converter intended for save/load of the project on disk. -MVVM_MODEL_EXPORT std::unique_ptr<JsonModelConverterInterface> CreateModelProjectConverter(); - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_FACTORIES_MODELCONVERTERFACTORY_H diff --git a/mvvm/model/mvvm/factories/modeldocumentfactory.cpp b/mvvm/model/mvvm/factories/modeldocumentfactory.cpp deleted file mode 100644 index 9d6a2a1bff0..00000000000 --- a/mvvm/model/mvvm/factories/modeldocumentfactory.cpp +++ /dev/null @@ -1,25 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/factories/modeldocumentfactory.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/factories/modeldocumentfactory.h" -#include "mvvm/serialization/jsondocument.h" - -namespace ModelView { - -std::unique_ptr<ModelDocumentInterface> CreateJsonDocument(const std::vector<SessionModel*>& models) -{ - return std::make_unique<JsonDocument>(models); -} - -} // namespace ModelView diff --git a/mvvm/model/mvvm/factories/modeldocumentfactory.h b/mvvm/model/mvvm/factories/modeldocumentfactory.h deleted file mode 100644 index 5a4a261d3fd..00000000000 --- a/mvvm/model/mvvm/factories/modeldocumentfactory.h +++ /dev/null @@ -1,32 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/factories/modeldocumentfactory.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_FACTORIES_MODELDOCUMENTFACTORY_H -#define BORNAGAIN_MVVM_MODEL_MVVM_FACTORIES_MODELDOCUMENTFACTORY_H - -#include "mvvm/interfaces/modeldocumentinterface.h" -#include <memory> -#include <vector> - -namespace ModelView { - -class SessionModel; - -//! Creates JsonDocument to save and load models. -MVVM_MODEL_EXPORT std::unique_ptr<ModelDocumentInterface> -CreateJsonDocument(const std::vector<SessionModel*>& models); - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_FACTORIES_MODELDOCUMENTFACTORY_H diff --git a/mvvm/model/mvvm/factories/projectmanagerfactory.cpp b/mvvm/model/mvvm/factories/projectmanagerfactory.cpp deleted file mode 100644 index dd579eb4c1d..00000000000 --- a/mvvm/model/mvvm/factories/projectmanagerfactory.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/factories/projectmanagerfactory.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/factories/projectmanagerfactory.h" -#include "mvvm/project/project_types.h" -#include "mvvm/project/projectmanagerdecorator.h" - -namespace ModelView { -std::unique_ptr<ProjectManagerInterface> -CreateProjectManager(const ProjectContext& project_context, - const UserInteractionContext& user_context) -{ - return std::make_unique<ProjectManagerDecorator>(project_context, user_context); -} - -} // namespace ModelView diff --git a/mvvm/model/mvvm/factories/projectmanagerfactory.h b/mvvm/model/mvvm/factories/projectmanagerfactory.h deleted file mode 100644 index 177dbe6b5ea..00000000000 --- a/mvvm/model/mvvm/factories/projectmanagerfactory.h +++ /dev/null @@ -1,33 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/factories/projectmanagerfactory.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_FACTORIES_PROJECTMANAGERFACTORY_H -#define BORNAGAIN_MVVM_MODEL_MVVM_FACTORIES_PROJECTMANAGERFACTORY_H - -#include "mvvm/interfaces/projectmanagerinterface.h" -#include <memory> - -namespace ModelView { - -struct ProjectContext; -struct UserInteractionContext; - -//! Creates default ProjectManager to save and load models. -MVVM_MODEL_EXPORT std::unique_ptr<ProjectManagerInterface> -CreateProjectManager(const ProjectContext& project_context, - const UserInteractionContext& user_context); - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_FACTORIES_PROJECTMANAGERFACTORY_H diff --git a/mvvm/model/mvvm/interfaces/CMakeLists.txt b/mvvm/model/mvvm/interfaces/CMakeLists.txt deleted file mode 100644 index 569e88432a0..00000000000 --- a/mvvm/model/mvvm/interfaces/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -target_sources(${library_name} PRIVATE - applicationmodelsinterface.h - itembackupstrategy.h - itemcopystrategy.h - itemfactoryinterface.h - itemlistenerinterface.h - modeldocumentinterface.h - modellistenerinterface.h - projectinterface.h - projectmanagerinterface.h - undostackinterface.h -) diff --git a/mvvm/model/mvvm/interfaces/applicationmodelsinterface.h b/mvvm/model/mvvm/interfaces/applicationmodelsinterface.h deleted file mode 100644 index eceaf9efe28..00000000000 --- a/mvvm/model/mvvm/interfaces/applicationmodelsinterface.h +++ /dev/null @@ -1,36 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/interfaces/applicationmodelsinterface.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_INTERFACES_APPLICATIONMODELSINTERFACE_H -#define BORNAGAIN_MVVM_MODEL_MVVM_INTERFACES_APPLICATIONMODELSINTERFACE_H - -#include "mvvm/model_export.h" -#include <vector> - -namespace ModelView { - -class SessionModel; - -//! Interface to access application's model list for further manipulation. -//! Used in the context of save/load projects. - -class MVVM_MODEL_EXPORT ApplicationModelsInterface { -public: - //! Returns vector of models intended for saving on disk. - virtual std::vector<SessionModel*> persistent_models() const = 0; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_INTERFACES_APPLICATIONMODELSINTERFACE_H diff --git a/mvvm/model/mvvm/interfaces/itembackupstrategy.h b/mvvm/model/mvvm/interfaces/itembackupstrategy.h deleted file mode 100644 index 770c96b82f1..00000000000 --- a/mvvm/model/mvvm/interfaces/itembackupstrategy.h +++ /dev/null @@ -1,40 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/interfaces/itembackupstrategy.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_INTERFACES_ITEMBACKUPSTRATEGY_H -#define BORNAGAIN_MVVM_MODEL_MVVM_INTERFACES_ITEMBACKUPSTRATEGY_H - -#include "mvvm/model_export.h" -#include <memory> - -namespace ModelView { - -class SessionItem; - -//! Interface to backup items for later restore. - -class MVVM_MODEL_EXPORT ItemBackupStrategy { -public: - virtual ~ItemBackupStrategy() = default; - - //! Restore item from saved content. - virtual std::unique_ptr<SessionItem> restoreItem() const = 0; - - //! Save item's content. - virtual void saveItem(const SessionItem*) = 0; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_INTERFACES_ITEMBACKUPSTRATEGY_H diff --git a/mvvm/model/mvvm/interfaces/itemcopystrategy.h b/mvvm/model/mvvm/interfaces/itemcopystrategy.h deleted file mode 100644 index 46831af35f0..00000000000 --- a/mvvm/model/mvvm/interfaces/itemcopystrategy.h +++ /dev/null @@ -1,37 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/interfaces/itemcopystrategy.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_INTERFACES_ITEMCOPYSTRATEGY_H -#define BORNAGAIN_MVVM_MODEL_MVVM_INTERFACES_ITEMCOPYSTRATEGY_H - -#include "mvvm/model_export.h" -#include <memory> - -namespace ModelView { - -class SessionItem; - -//! Interface for deep item copying. - -class MVVM_MODEL_EXPORT ItemCopyStrategy { -public: - virtual ~ItemCopyStrategy() = default; - - //! Creates item copy by deep copying all children. SessionItem identifiers will be regenerated. - virtual std::unique_ptr<SessionItem> createCopy(const SessionItem* item) const = 0; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_INTERFACES_ITEMCOPYSTRATEGY_H diff --git a/mvvm/model/mvvm/interfaces/itemfactoryinterface.h b/mvvm/model/mvvm/interfaces/itemfactoryinterface.h deleted file mode 100644 index 9c7612bd528..00000000000 --- a/mvvm/model/mvvm/interfaces/itemfactoryinterface.h +++ /dev/null @@ -1,41 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/interfaces/itemfactoryinterface.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_INTERFACES_ITEMFACTORYINTERFACE_H -#define BORNAGAIN_MVVM_MODEL_MVVM_INTERFACES_ITEMFACTORYINTERFACE_H - -#include "mvvm/core/types.h" -#include "mvvm/model/function_types.h" -#include "mvvm/model_export.h" -#include <memory> - -namespace ModelView { - -class SessionItem; - -//! Interface class for all factories capable of producing SessionItem's. - -class MVVM_MODEL_EXPORT ItemFactoryInterface { -public: - virtual ~ItemFactoryInterface() = default; - - virtual void registerItem(const std::string& modelType, item_factory_func_t func, - const std::string& label) = 0; - - virtual std::unique_ptr<SessionItem> createItem(const model_type& modelType) const = 0; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_INTERFACES_ITEMFACTORYINTERFACE_H diff --git a/mvvm/model/mvvm/interfaces/itemlistenerinterface.h b/mvvm/model/mvvm/interfaces/itemlistenerinterface.h deleted file mode 100644 index d50bbc4b6d2..00000000000 --- a/mvvm/model/mvvm/interfaces/itemlistenerinterface.h +++ /dev/null @@ -1,74 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/interfaces/itemlistenerinterface.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_INTERFACES_ITEMLISTENERINTERFACE_H -#define BORNAGAIN_MVVM_MODEL_MVVM_INTERFACES_ITEMLISTENERINTERFACE_H - -#include "mvvm/signals/callback_types.h" - -namespace ModelView { - -//! Interface to subscribe to various events happening with specific SessionItem. - -class MVVM_MODEL_EXPORT ItemListenerInterface { -public: - virtual ~ItemListenerInterface() = default; - - virtual void setOnItemDestroy(Callbacks::item_t f, Callbacks::slot_t owner) = 0; - - //! Sets callback to be notified on item's data change. - //! Callback will be called with (SessionItem*, data_role). - - virtual void setOnDataChange(Callbacks::item_int_t f, Callbacks::slot_t owner) = 0; - - //! Sets callback to be notified on item's property change. - //! Callback will be called with (compound_item, property_name). - - virtual void setOnPropertyChange(Callbacks::item_str_t f, Callbacks::slot_t owner) = 0; - - //! Sets callback to be notified on item's children property change. - //! Callback will be called with (compound_item, property_name). For MultiLayer containing the - //! layer with "thickness" property, the signal will be triggered on thickness change using - //! (layeritem*, "thickness") as callback parameters. - - virtual void setOnChildPropertyChange(Callbacks::item_str_t f, Callbacks::slot_t owner) = 0; - - //! Sets callback to be notified on child insertion. - //! Callback will be called with (compound_item, tag, row). For MultiLayer containing the - //! T_LAYERS tag, the signal will be triggered on layer insertion with - //! (multilayer*, {T_LAYER, row}) as callback parameters. - - virtual void setOnItemInserted(Callbacks::item_tagrow_t f, Callbacks::slot_t owner) = 0; - - //! Sets callback to be notified on child removal. - //! Callback will be called with (compound_item, tag, row). For MultiLayer containing the - //! T_LAYERS tag, the signal will be triggered on layer removal with - //! (multilayer*, {T_LAYER, oldrow}) as callback parameters. - - virtual void setOnItemRemoved(Callbacks::item_tagrow_t f, Callbacks::slot_t owner) = 0; - - //! Sets callback to be notified when row is about to be removed. - //! Callback will be called with (compound_item, tagrow). For MultiLayer containing the - //! T_LAYERS tag, the signal will be triggered on layer deletion with - //! (multilayer*, {T_LAYER, row}) as callback parameters. - - virtual void setOnAboutToRemoveItem(Callbacks::item_tagrow_t f, Callbacks::slot_t owner) = 0; - - //! Removes given client from all subscriptions. - virtual void unsubscribe(Callbacks::slot_t client) = 0; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_INTERFACES_ITEMLISTENERINTERFACE_H diff --git a/mvvm/model/mvvm/interfaces/modeldocumentinterface.h b/mvvm/model/mvvm/interfaces/modeldocumentinterface.h deleted file mode 100644 index 86b93caf130..00000000000 --- a/mvvm/model/mvvm/interfaces/modeldocumentinterface.h +++ /dev/null @@ -1,35 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/interfaces/modeldocumentinterface.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_INTERFACES_MODELDOCUMENTINTERFACE_H -#define BORNAGAIN_MVVM_MODEL_MVVM_INTERFACES_MODELDOCUMENTINTERFACE_H - -#include "mvvm/model_export.h" -#include <string> - -namespace ModelView { - -//! Pure virtual interface to save and restore session models to/from disk. - -class MVVM_MODEL_EXPORT ModelDocumentInterface { -public: - virtual ~ModelDocumentInterface() = default; - - virtual void save(const std::string& file_name) const = 0; - virtual void load(const std::string& file_name) = 0; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_INTERFACES_MODELDOCUMENTINTERFACE_H diff --git a/mvvm/model/mvvm/interfaces/modellistenerinterface.h b/mvvm/model/mvvm/interfaces/modellistenerinterface.h deleted file mode 100644 index 580a6f95771..00000000000 --- a/mvvm/model/mvvm/interfaces/modellistenerinterface.h +++ /dev/null @@ -1,60 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/interfaces/modellistenerinterface.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_INTERFACES_MODELLISTENERINTERFACE_H -#define BORNAGAIN_MVVM_MODEL_MVVM_INTERFACES_MODELLISTENERINTERFACE_H - -#include "mvvm/signals/callback_types.h" - -namespace ModelView { - -//! Interface to subscribe to various signals generated by SessionModel. - -class MVVM_MODEL_EXPORT ModelListenerInterface { -public: - virtual ~ModelListenerInterface() = default; - - //! Sets callback to be notified on item's data change. The callback will be called - //! with (SessionItem*, data_role). - virtual void setOnDataChange(Callbacks::item_int_t f, Callbacks::slot_t client) = 0; - - //! Sets callback to be notified on item insert. The callback will be called with - //! (SessionItem* parent, tagrow), where 'tagrow' denotes inserted child position. - virtual void setOnItemInserted(Callbacks::item_tagrow_t f, Callbacks::slot_t client) = 0; - - //! Sets callback to be notified on item remove. The callback will be called with - //! (SessionItem* parent, tagrow), where 'tagrow' denotes child position before the removal. - virtual void setOnItemRemoved(Callbacks::item_tagrow_t f, Callbacks::slot_t client) = 0; - - //! Sets callback to be notified when the item is about to be removed. The callback will be - //! called with (SessionItem* parent, tagrow), where 'tagrow' denotes child position being - //! removed. - virtual void setOnAboutToRemoveItem(Callbacks::item_tagrow_t f, Callbacks::slot_t client) = 0; - - //! Sets the callback for notifications on model destruction. - virtual void setOnModelDestroyed(Callbacks::model_t f, Callbacks::slot_t client) = 0; - - //! Sets the callback to be notified just before the reset of the root item. - virtual void setOnModelAboutToBeReset(Callbacks::model_t f, Callbacks::slot_t client) = 0; - - //! Sets the callback to be notified right after the root item recreation. - virtual void setOnModelReset(Callbacks::model_t f, Callbacks::slot_t client) = 0; - - //! Removes given client from all subscriptions. - virtual void unsubscribe(Callbacks::slot_t client) = 0; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_INTERFACES_MODELLISTENERINTERFACE_H diff --git a/mvvm/model/mvvm/interfaces/projectinterface.h b/mvvm/model/mvvm/interfaces/projectinterface.h deleted file mode 100644 index dd06efbd402..00000000000 --- a/mvvm/model/mvvm/interfaces/projectinterface.h +++ /dev/null @@ -1,37 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/interfaces/projectinterface.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_INTERFACES_PROJECTINTERFACE_H -#define BORNAGAIN_MVVM_MODEL_MVVM_INTERFACES_PROJECTINTERFACE_H - -#include "mvvm/model_export.h" -#include <string> - -namespace ModelView { - -//! Interface to manipulate projects on disk. -//! Project represents content of all application models in a folder on disk. - -class MVVM_MODEL_EXPORT ProjectInterface { -public: - virtual ~ProjectInterface() = default; - virtual std::string projectDir() const = 0; - virtual bool save(const std::string& dirname) const = 0; - virtual bool load(const std::string& dirname) = 0; - virtual bool isModified() const = 0; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_INTERFACES_PROJECTINTERFACE_H diff --git a/mvvm/model/mvvm/interfaces/projectmanagerinterface.h b/mvvm/model/mvvm/interfaces/projectmanagerinterface.h deleted file mode 100644 index 187458ac3d1..00000000000 --- a/mvvm/model/mvvm/interfaces/projectmanagerinterface.h +++ /dev/null @@ -1,48 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/interfaces/projectmanagerinterface.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_INTERFACES_PROJECTMANAGERINTERFACE_H -#define BORNAGAIN_MVVM_MODEL_MVVM_INTERFACES_PROJECTMANAGERINTERFACE_H - -#include "mvvm/model_export.h" -#include <string> - -namespace ModelView { - -//! Interface class for ProjectManager family. - -//! Responsible for handling new/save/save-as/close Project logic, where the Project represents -//! a collection of serialized application models in the project directory. - -class MVVM_MODEL_EXPORT ProjectManagerInterface { -public: - virtual ~ProjectManagerInterface() = default; - virtual bool createNewProject(const std::string& dirname = {}) = 0; - - virtual bool saveCurrentProject() = 0; - - virtual bool saveProjectAs(const std::string& dirname = {}) = 0; - - virtual bool openExistingProject(const std::string& dirname = {}) = 0; - - virtual std::string currentProjectDir() const = 0; - - virtual bool isModified() const = 0; - - virtual bool closeCurrentProject() const = 0; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_INTERFACES_PROJECTMANAGERINTERFACE_H diff --git a/mvvm/model/mvvm/interfaces/undostackinterface.h b/mvvm/model/mvvm/interfaces/undostackinterface.h deleted file mode 100644 index 802d01d4afc..00000000000 --- a/mvvm/model/mvvm/interfaces/undostackinterface.h +++ /dev/null @@ -1,56 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/interfaces/undostackinterface.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_INTERFACES_UNDOSTACKINTERFACE_H -#define BORNAGAIN_MVVM_MODEL_MVVM_INTERFACES_UNDOSTACKINTERFACE_H - -#include <memory> -#include <string> - -class QUndoCommand; - -namespace ModelView { - -class AbstractItemCommand; - -//! Interface class for undo/redo stack. - -class UndoStackInterface { -public: - virtual ~UndoStackInterface() = default; - - //! Executes the command, then pushes it in the stack for possible undo. - //! Current design relies on shared pointer. This is done - //! a) to retrieve result of the command from another place - //! b) to adapt the command for QUndoStack - //! c) to bypass QUndoStack behavior which wants to have an ownership - virtual void execute(std::shared_ptr<AbstractItemCommand> command) = 0; - - virtual bool isActive() const = 0; - virtual bool canUndo() const = 0; - virtual bool canRedo() const = 0; - virtual int index() const = 0; - virtual int count() const = 0; - virtual void undo() = 0; - virtual void redo() = 0; - virtual void clear() = 0; - virtual void setUndoLimit(int limit) = 0; - - virtual void beginMacro(const std::string& name) = 0; - virtual void endMacro() = 0; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_INTERFACES_UNDOSTACKINTERFACE_H diff --git a/mvvm/model/mvvm/model/CMakeLists.txt b/mvvm/model/mvvm/model/CMakeLists.txt deleted file mode 100644 index 0356baaaec1..00000000000 --- a/mvvm/model/mvvm/model/CMakeLists.txt +++ /dev/null @@ -1,49 +0,0 @@ -target_sources(${library_name} PRIVATE - comboproperty.cpp - comboproperty.h - comparators.cpp - comparators.h - compounditem.cpp - compounditem.h - customvariants.cpp - customvariants.h - datarole.cpp - datarole.h - externalproperty.cpp - externalproperty.h - function_types.h - groupitem.cpp - groupitem.h - itemcatalogue.cpp - itemcatalogue.h - itemfactory.cpp - itemfactory.h - itemmanager.cpp - itemmanager.h - itempool.cpp - itempool.h - itemutils.cpp - itemutils.h - modelutils.cpp - modelutils.h - mvvm_types.h - path.cpp - path.h - propertyitem.cpp - propertyitem.h - sessionitem.cpp - sessionitem.h - sessionitemcontainer.cpp - sessionitemcontainer.h - sessionitemdata.cpp - sessionitemdata.h - sessionitemtags.cpp - sessionitemtags.h - sessionmodel.cpp - sessionmodel.h - taginfo.cpp - taginfo.h - tagrow.cpp - tagrow.h - variant_constants.h -) diff --git a/mvvm/model/mvvm/model/comboproperty.cpp b/mvvm/model/mvvm/model/comboproperty.cpp deleted file mode 100644 index 33ee40a0e82..00000000000 --- a/mvvm/model/mvvm/model/comboproperty.cpp +++ /dev/null @@ -1,257 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/comboproperty.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/model/comboproperty.h" -#include "mvvm/utils/containerutils.h" -#include <sstream> -#include <stdexcept> - -namespace { -const std::string value_separator = ";"; -const std::string selection_separator = ","; -const std::string multiple_label = "Multiple"; -const std::string none_label = "None"; - -template <typename C, typename T> std::string toString(const C& container, const T& delim) -{ - std::stringstream result; - for (auto it = container.begin(); it != container.end(); ++it) { - result << *it; - if (std::distance(it, container.end()) != 1) - result << delim; - } - return result.str(); -} - -std::vector<std::string> tokenize(const std::string& str, const std::string& delimeter) -{ - std::vector<std::string> result; - size_t start = str.find_first_not_of(delimeter); - size_t end = start; - - while (start != std::string::npos) { - // Find next occurence of delimiter - end = str.find(delimeter, start); - // Push back the token found into vector - result.push_back(str.substr(start, end - start)); - // Skip all occurences of the delimiter to find new start - start = str.find_first_not_of(delimeter, end); - } - return result; -} -} // namespace - -using namespace ModelView; - -ComboProperty::ComboProperty() = default; - -ComboProperty::ComboProperty(std::vector<std::string> values) : m_values(std::move(values)) {} - -ComboProperty ComboProperty::createFrom(const std::vector<std::string>& values, - const std::string& current_value) -{ - ComboProperty result(values); - - if (!current_value.empty()) - result.setValue(current_value); - else - result.setCurrentIndex(0); - - return result; -} - -std::string ComboProperty::value() const -{ - return currentIndex() < 0 ? "" : m_values.at(static_cast<size_t>(currentIndex())); -} - -void ComboProperty::setValue(const std::string& name) -{ - if (!Utils::Contains(m_values, name)) - throw std::runtime_error("ComboProperty::setValue() -> Error. Combo doesn't contain " - "value " - + name); - setCurrentIndex(Utils::IndexOfItem(m_values, name)); -} - -std::vector<std::string> ComboProperty::values() const -{ - return m_values; -} - -//! Sets new list of values. Current value will be preserved, if exists in a new list. - -void ComboProperty::setValues(const std::vector<std::string>& values) -{ - if (values.empty()) - return; - - auto current = value(); - m_values = values; - setCurrentIndex(Utils::Contains(m_values, current) ? Utils::IndexOfItem(m_values, current) : 0); -} - -//! returns list of tool tips for all values -std::vector<std::string> ComboProperty::toolTips() const -{ - return m_tooltips; -} - -void ComboProperty::setToolTips(const std::vector<std::string>& tooltips) -{ - m_tooltips = tooltips; -} - -int ComboProperty::currentIndex() const -{ - return m_selected_indices.empty() ? -1 : m_selected_indices.at(0); -} - -void ComboProperty::setCurrentIndex(int index) -{ - if (index < 0 || index >= static_cast<int>(m_values.size())) - throw std::runtime_error("ComboProperty::setCurrentIndex(int index) -> Error. " - "Invalid index"); - m_selected_indices.clear(); - m_selected_indices.push_back(index); -} - -ComboProperty& ComboProperty::operator<<(const std::string& str) -{ - m_values.push_back(str); - if (currentIndex() == -1) - setCurrentIndex(0); - return *this; -} - -ComboProperty& ComboProperty::operator<<(const std::vector<std::string>& str) -{ - m_values.insert(m_values.end(), str.begin(), str.end()); - if (currentIndex() == -1) - setCurrentIndex(0); - return *this; -} - -bool ComboProperty::operator==(const ComboProperty& other) const -{ - if (m_selected_indices != other.m_selected_indices) - return false; - if (m_values != other.m_values) - return false; - return true; -} - -bool ComboProperty::operator!=(const ComboProperty& other) const -{ - return !(*this == other); -} - -bool ComboProperty::operator<(const ComboProperty& other) const -{ - return m_selected_indices < other.m_selected_indices && m_values < other.m_values; -} - -//! Returns a single string containing values delimited with ';'. - -std::string ComboProperty::stringOfValues() const -{ - return toString(m_values, value_separator); -} - -//! Sets values from the string containing delimeter ';'. - -void ComboProperty::setStringOfValues(const std::string& values) -{ - auto current = value(); - m_values = tokenize(values, value_separator); - setCurrentIndex(Utils::Contains(m_values, current) ? Utils::IndexOfItem(m_values, current) : 0); -} - -//! Returns vector of selected indices. - -std::vector<int> ComboProperty::selectedIndices() const -{ - return m_selected_indices; -} - -//! Returns list of string with selected values; - -std::vector<std::string> ComboProperty::selectedValues() const -{ - std::vector<std::string> result; - for (auto index : m_selected_indices) - result.push_back(m_values.at(static_cast<size_t>(index))); - return result; -} - -//! Sets given index selection flag. -//! If false, index will be excluded from selection. - -void ComboProperty::setSelected(int index, bool value) -{ - if (index < 0 || index >= static_cast<int>(m_values.size())) - return; - - auto pos = find(m_selected_indices.begin(), m_selected_indices.end(), index); - if (value) { - if (pos == m_selected_indices.end()) - m_selected_indices.push_back(index); - } else { - if (pos != m_selected_indices.end()) - m_selected_indices.erase(pos); - } - std::sort(m_selected_indices.begin(), m_selected_indices.end()); -} - -void ComboProperty::setSelected(const std::string& name, bool value) -{ - setSelected(Utils::IndexOfItem(m_values, name), value); -} - -//! Return string with coma separated list of selected indices. - -std::string ComboProperty::stringOfSelections() const -{ - std::vector<std::string> text; - for (auto index : m_selected_indices) - text.push_back(std::to_string(index)); - return toString(text, selection_separator); -} - -//! Sets selected indices from string. - -void ComboProperty::setStringOfSelections(const std::string& values) -{ - m_selected_indices.clear(); - if (values.empty()) - return; - - for (const auto& str : tokenize(values, selection_separator)) { - int num = std::stoi(str); - setSelected(num, true); - } -} - -//! Returns the label to show. - -std::string ComboProperty::label() const -{ - if (m_selected_indices.size() > 1) { - return multiple_label; - } else if (m_selected_indices.size() == 1) { - return value(); - } else { - return none_label; - } -} diff --git a/mvvm/model/mvvm/model/comboproperty.h b/mvvm/model/mvvm/model/comboproperty.h deleted file mode 100644 index 39d309ce848..00000000000 --- a/mvvm/model/mvvm/model/comboproperty.h +++ /dev/null @@ -1,78 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/comboproperty.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_MODEL_COMBOPROPERTY_H -#define BORNAGAIN_MVVM_MODEL_MVVM_MODEL_COMBOPROPERTY_H - -#include "mvvm/core/variant.h" -#include "mvvm/model_export.h" -#include <string> -#include <vector> - -namespace ModelView { - -//! Custom property to define list of string values with multiple selections. - -class MVVM_MODEL_EXPORT ComboProperty { -public: - ComboProperty(); - - static ComboProperty createFrom(const std::vector<std::string>& values, - const std::string& current_value = {}); - - std::string value() const; - void setValue(const std::string& name); - - std::vector<std::string> values() const; - void setValues(const std::vector<std::string>& values); - - std::vector<std::string> toolTips() const; - void setToolTips(const std::vector<std::string>& tooltips); - - int currentIndex() const; - void setCurrentIndex(int index); - - ComboProperty& operator<<(const std::string& str); - ComboProperty& operator<<(const std::vector<std::string>& str); - bool operator==(const ComboProperty& other) const; - bool operator!=(const ComboProperty& other) const; - bool operator<(const ComboProperty& other) const; - - std::string stringOfValues() const; - void setStringOfValues(const std::string& values); - - std::vector<int> selectedIndices() const; - std::vector<std::string> selectedValues() const; - - void setSelected(int index, bool value = true); - void setSelected(const std::string& name, bool value = true); - - std::string stringOfSelections() const; - void setStringOfSelections(const std::string& values); - - std::string label() const; - -private: - ComboProperty(std::vector<std::string> values); - - std::vector<std::string> m_values; - std::vector<std::string> m_tooltips; - std::vector<int> m_selected_indices; -}; - -} // namespace ModelView - -Q_DECLARE_METATYPE(ModelView::ComboProperty) - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_MODEL_COMBOPROPERTY_H diff --git a/mvvm/model/mvvm/model/comparators.cpp b/mvvm/model/mvvm/model/comparators.cpp deleted file mode 100644 index acbeefad7dc..00000000000 --- a/mvvm/model/mvvm/model/comparators.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/comparators.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/model/comparators.h" -#include "mvvm/model/comboproperty.h" -#include "mvvm/model/customvariants.h" -#include "mvvm/model/externalproperty.h" -#include "mvvm/utils/reallimits.h" -#include <QMetaType> - -using namespace ModelView; - -bool Comparators::m_is_registered = false; - -void Comparators::registerComparators() -{ - if (!m_is_registered) { - QMetaType::registerComparators<std::string>(); - QMetaType::registerComparators<std::vector<double>>(); - QMetaType::registerComparators<ComboProperty>(); - QMetaType::registerComparators<ExternalProperty>(); - QMetaType::registerComparators<RealLimits>(); - m_is_registered = true; - } -} - -bool Comparators::registered() -{ - return m_is_registered; -} diff --git a/mvvm/model/mvvm/model/comparators.h b/mvvm/model/mvvm/model/comparators.h deleted file mode 100644 index e67a89b055c..00000000000 --- a/mvvm/model/mvvm/model/comparators.h +++ /dev/null @@ -1,35 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/comparators.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_MODEL_COMPARATORS_H -#define BORNAGAIN_MVVM_MODEL_MVVM_MODEL_COMPARATORS_H - -#include "mvvm/model_export.h" - -namespace ModelView { - -//! Helper class to register variant comparators. - -class MVVM_MODEL_EXPORT Comparators { -public: - static void registerComparators(); - static bool registered(); - -private: - static bool m_is_registered; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_MODEL_COMPARATORS_H diff --git a/mvvm/model/mvvm/model/compounditem.cpp b/mvvm/model/mvvm/model/compounditem.cpp deleted file mode 100644 index c7b51616885..00000000000 --- a/mvvm/model/mvvm/model/compounditem.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/compounditem.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/model/compounditem.h" -#include "mvvm/model/itemutils.h" - -using namespace ModelView; - -namespace { -bool has_custom_display_name(const SessionItem* item) -{ - return item->SessionItem::displayName() != item->modelType(); -} -} // namespace - -CompoundItem::CompoundItem(const std::string& modelType) : SessionItem(modelType) {} - -std::string CompoundItem::displayName() const -{ - if (has_custom_display_name(this)) - return SessionItem::displayName(); - - int copy_number = Utils::CopyNumber(this); - return copy_number != -1 ? SessionItem::displayName() + std::to_string(copy_number) - : SessionItem::displayName(); -} diff --git a/mvvm/model/mvvm/model/compounditem.h b/mvvm/model/mvvm/model/compounditem.h deleted file mode 100644 index 25a09ff28f2..00000000000 --- a/mvvm/model/mvvm/model/compounditem.h +++ /dev/null @@ -1,74 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/compounditem.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_MODEL_COMPOUNDITEM_H -#define BORNAGAIN_MVVM_MODEL_MVVM_MODEL_COMPOUNDITEM_H - -#include "mvvm/model/customvariants.h" -#include "mvvm/model/propertyitem.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/taginfo.h" -#include "mvvm/utils/reallimits.h" - -namespace ModelView { - -//! Complex item holding mixed SessionItem types (single properties and other CompountItems). - -class MVVM_MODEL_EXPORT CompoundItem : public SessionItem { -public: - CompoundItem(const std::string& modelType = GUI::Constants::CompoundItemType); - - //! Adds property item of given type. - template <typename T = PropertyItem> T* addProperty(const std::string& name); - - template <typename V> PropertyItem* addProperty(const std::string& name, const V& value); - - //! Register char property. Special case to turn it into std::string. - PropertyItem* addProperty(const std::string& name, const char* value); - - std::string displayName() const override; -}; - -template <typename T> T* CompoundItem::addProperty(const std::string& name) -{ - T* property = new T; - registerTag(TagInfo::propertyTag(name, property->modelType())); - property->setDisplayName(name); - insertItem(property, {name, 0}); - return property; -} - -inline PropertyItem* CompoundItem::addProperty(const std::string& name, const char* value) -{ - return addProperty(name, std::string(value)); -} - -template <typename V> -PropertyItem* CompoundItem::addProperty(const std::string& name, const V& value) -{ - auto property = new PropertyItem; - registerTag(TagInfo::propertyTag(name, property->modelType())); - property->setDisplayName(name); - property->setData(value); - - if constexpr (std::is_floating_point_v<V>) - property->setData(RealLimits::limitless(), ItemDataRole::LIMITS); - - insertItem(property, {name, 0}); - return property; -} - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_MODEL_COMPOUNDITEM_H diff --git a/mvvm/model/mvvm/model/customvariants.cpp b/mvvm/model/mvvm/model/customvariants.cpp deleted file mode 100644 index e218be97430..00000000000 --- a/mvvm/model/mvvm/model/customvariants.cpp +++ /dev/null @@ -1,135 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/customvariants.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/model/customvariants.h" -#include "mvvm/model/comboproperty.h" -#include "mvvm/model/externalproperty.h" -#include "mvvm/model/variant_constants.h" - -namespace { -const QString qstring_name = "QString"; -} - -using namespace ModelView; - -std::string Utils::VariantName(const Variant& variant) -{ - return variant.isValid() ? variant.typeName() : GUI::Constants::invalid_type_name; -} - -int Utils::VariantType(const Variant& variant) -{ - auto result = static_cast<int>(variant.type()); - if (result == Variant::UserType) - result = variant.userType(); - return result; -} - -bool Utils::CompatibleVariantTypes(const Variant& oldValue, const Variant& newValue) -{ - // Invalid variant can be rewritten by any variant. - // Valid Variant can be replaced by invalid variant. - // In other cases types of variants should coincide to be compatible. - - if (!oldValue.isValid() || !newValue.isValid()) - return true; - - return Utils::VariantType(oldValue) == Utils::VariantType(newValue); -} - -bool Utils::IsTheSame(const Variant& var1, const Variant& var2) -{ - // variants of different type are always reported as not the same - if (VariantType(var1) != VariantType(var2)) - return false; - - // variants of same type are compared by value - return var1 == var2; -} - -Variant Utils::toQtVariant(const Variant& custom) -{ - if (!custom.isValid()) - return custom; - - // converts variant based on std::string to variant based on QString - if (custom.typeName() == GUI::Constants::string_type_name) { - return Variant(QString::fromStdString(custom.value<std::string>())); - } else if (IsDoubleVectorVariant(custom)) { - QString str = - QString("vector of %1 elements").arg(custom.value<std::vector<double>>().size()); - return Variant(str); - } - - // in other cases returns unchanged variant - return custom; -} - -Variant Utils::toCustomVariant(const Variant& standard) -{ - if (!standard.isValid()) - return standard; - - // converts variant based on std::string to variant based on QString - if (standard.typeName() == qstring_name) - return Variant::fromValue(standard.toString().toStdString()); - - // in other cases returns unchanged variant - return standard; -} - -bool Utils::IsBoolVariant(const Variant& variant) -{ - return variant.type() == Variant::Bool; -} - -bool Utils::IsIntVariant(const Variant& variant) -{ - return variant.type() == Variant::Int; -} - -bool Utils::IsDoubleVariant(const Variant& variant) -{ - return variant.type() == Variant::Double; -} - -bool Utils::IsComboVariant(const Variant& variant) -{ - return variant.canConvert<ComboProperty>(); -} - -bool Utils::IsStdStringVariant(const Variant& variant) -{ - return variant.canConvert<std::string>(); -} - -bool Utils::IsDoubleVectorVariant(const Variant& variant) -{ - return variant.typeName() == GUI::Constants::vector_double_type_name; -} - -bool Utils::IsColorVariant(const Variant& variant) -{ - return variant.type() == Variant::Color; -} - -bool Utils::IsExtPropertyVariant(const Variant& variant) -{ - return variant.canConvert<ExternalProperty>(); -} - -bool Utils::IsRealLimitsVariant(const Variant& variant) -{ - return variant.canConvert<RealLimits>(); -} diff --git a/mvvm/model/mvvm/model/customvariants.h b/mvvm/model/mvvm/model/customvariants.h deleted file mode 100644 index 5b53cc908b7..00000000000 --- a/mvvm/model/mvvm/model/customvariants.h +++ /dev/null @@ -1,81 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/customvariants.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_MODEL_CUSTOMVARIANTS_H -#define BORNAGAIN_MVVM_MODEL_MVVM_MODEL_CUSTOMVARIANTS_H - -//! @file mvvm/model/mvvm/model/customvariants.h -//! Registrations and translations for custom variants. - -#include "mvvm/core/variant.h" -#include "mvvm/model_export.h" -#include "mvvm/utils/reallimits.h" -#include <QMetaType> -#include <string> -#include <vector> - -namespace ModelView::Utils { - -//! Returns name of variant. -MVVM_MODEL_EXPORT std::string VariantName(const Variant& variant); - -//! Returns type of variant (additionally checks for user type). -MVVM_MODEL_EXPORT int VariantType(const Variant& variant); - -//! Returns true if variants has compatible types. -MVVM_MODEL_EXPORT bool CompatibleVariantTypes(const Variant& oldValue, const Variant& newValue); - -//! Returns true if given variants have same type and value. -MVVM_MODEL_EXPORT bool IsTheSame(const Variant& var1, const Variant& var2); - -//! Converts custom variant to standard variant which Qt views will understand. -MVVM_MODEL_EXPORT Variant toQtVariant(const Variant& custom); - -//! Converts Qt variant to custom variant on board of SessionItem. -MVVM_MODEL_EXPORT Variant toCustomVariant(const Variant& standard); - -//! Returns true in the case of double value based variant. -MVVM_MODEL_EXPORT bool IsBoolVariant(const Variant& variant); - -//! Returns true in the case of double value based variant. -MVVM_MODEL_EXPORT bool IsIntVariant(const Variant& variant); - -//! Returns true in the case of double value based variant. -MVVM_MODEL_EXPORT bool IsDoubleVariant(const Variant& variant); - -//! Returns true in the case of double value based variant. -MVVM_MODEL_EXPORT bool IsComboVariant(const Variant& variant); - -//! Returns true in the case of double value based variant. -MVVM_MODEL_EXPORT bool IsStdStringVariant(const Variant& variant); - -//! Returns true in the case of variant based on std::vector<double>. -MVVM_MODEL_EXPORT bool IsDoubleVectorVariant(const Variant& variant); - -//! Returns true in the case of QColor based variant. -MVVM_MODEL_EXPORT bool IsColorVariant(const Variant& variant); - -//! Returns true in the case of ExternalProperty based variant. -MVVM_MODEL_EXPORT bool IsExtPropertyVariant(const Variant& variant); - -//! Returns true in the case of RealLimits based variant. -MVVM_MODEL_EXPORT bool IsRealLimitsVariant(const Variant& variant); - -} // namespace ModelView::Utils - -Q_DECLARE_METATYPE(std::string) -Q_DECLARE_METATYPE(std::vector<double>) -Q_DECLARE_METATYPE(ModelView::RealLimits) - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_MODEL_CUSTOMVARIANTS_H diff --git a/mvvm/model/mvvm/model/datarole.cpp b/mvvm/model/mvvm/model/datarole.cpp deleted file mode 100644 index 6e3b7785b82..00000000000 --- a/mvvm/model/mvvm/model/datarole.cpp +++ /dev/null @@ -1,25 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/datarole.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/model/datarole.h" -#include "mvvm/model/customvariants.h" - -using namespace ModelView; - -DataRole::DataRole(Variant data, int role) : m_data(std::move(data)), m_role(role) {} - -bool DataRole::operator==(const DataRole& other) const -{ - return m_role == other.m_role && Utils::IsTheSame(m_data, other.m_data); -} diff --git a/mvvm/model/mvvm/model/datarole.h b/mvvm/model/mvvm/model/datarole.h deleted file mode 100644 index c0c01f61667..00000000000 --- a/mvvm/model/mvvm/model/datarole.h +++ /dev/null @@ -1,35 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/datarole.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_MODEL_DATAROLE_H -#define BORNAGAIN_MVVM_MODEL_MVVM_MODEL_DATAROLE_H - -#include "mvvm/core/variant.h" -#include "mvvm/model_export.h" - -namespace ModelView { - -//! Represents pair of data,role for SessionItemData. - -class MVVM_MODEL_EXPORT DataRole { -public: - DataRole(Variant data = Variant(), int role = -1); - Variant m_data; - int m_role; - bool operator==(const DataRole& other) const; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_MODEL_DATAROLE_H diff --git a/mvvm/model/mvvm/model/externalproperty.cpp b/mvvm/model/mvvm/model/externalproperty.cpp deleted file mode 100644 index b8e6d0d5956..00000000000 --- a/mvvm/model/mvvm/model/externalproperty.cpp +++ /dev/null @@ -1,65 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/externalproperty.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/model/externalproperty.h" - -using namespace ModelView; - -ExternalProperty::ExternalProperty() = default; - -ExternalProperty::ExternalProperty(std::string text, QColor color, std::string id) - : m_text(std::move(text)), m_color(std::move(color)), m_identifier(std::move(id)) -{ -} - -ExternalProperty ExternalProperty::undefined() -{ - return ExternalProperty("Undefined", QColor(Qt::red)); -} - -std::string ExternalProperty::text() const -{ - return m_text; -} - -QColor ExternalProperty::color() const -{ - return m_color; -} - -std::string ExternalProperty::identifier() const -{ - return m_identifier; -} - -bool ExternalProperty::isValid() const -{ - return !(m_identifier.empty() && m_text.empty() && !m_color.isValid()); -} - -bool ExternalProperty::operator==(const ExternalProperty& other) const -{ - return m_identifier == other.m_identifier && m_text == other.m_text && m_color == other.m_color; -} - -bool ExternalProperty::operator!=(const ExternalProperty& other) const -{ - return !(*this == other); -} - -bool ExternalProperty::operator<(const ExternalProperty& other) const -{ - return m_identifier < other.m_identifier && m_text < other.m_text - && m_color.name() < other.m_color.name(); -} diff --git a/mvvm/model/mvvm/model/externalproperty.h b/mvvm/model/mvvm/model/externalproperty.h deleted file mode 100644 index 79643d0562b..00000000000 --- a/mvvm/model/mvvm/model/externalproperty.h +++ /dev/null @@ -1,57 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/externalproperty.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_MODEL_EXTERNALPROPERTY_H -#define BORNAGAIN_MVVM_MODEL_MVVM_MODEL_EXTERNALPROPERTY_H - -#include "mvvm/core/variant.h" -#include "mvvm/model_export.h" -#include <QColor> -#include <string> - -namespace ModelView { - -//! Property to carry text, color and identifier. -//! Can be used to link items with each other. - -class MVVM_MODEL_EXPORT ExternalProperty { -public: - ExternalProperty(); - ExternalProperty(std::string text, QColor color, std::string id = {}); - - static ExternalProperty undefined(); - - std::string text() const; - - QColor color() const; - - std::string identifier() const; - - bool isValid() const; - - bool operator==(const ExternalProperty& other) const; - bool operator!=(const ExternalProperty& other) const; - bool operator<(const ExternalProperty& other) const; - -private: - std::string m_text; - QColor m_color; - std::string m_identifier; -}; - -} // namespace ModelView - -Q_DECLARE_METATYPE(ModelView::ExternalProperty) - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_MODEL_EXTERNALPROPERTY_H diff --git a/mvvm/model/mvvm/model/function_types.h b/mvvm/model/mvvm/model/function_types.h deleted file mode 100644 index c515a5e9a50..00000000000 --- a/mvvm/model/mvvm/model/function_types.h +++ /dev/null @@ -1,36 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/function_types.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_MODEL_FUNCTION_TYPES_H -#define BORNAGAIN_MVVM_MODEL_MVVM_MODEL_FUNCTION_TYPES_H - -#include <functional> -#include <memory> - -namespace ModelView { - -class SessionItem; - -//! Definition for item factory funciton. -using item_factory_func_t = std::function<std::unique_ptr<SessionItem>()>; - -//! Creates factory function for item of specific type. -template <typename T> item_factory_func_t ItemFactoryFunction() -{ - return []() { return std::make_unique<T>(); }; -} - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_MODEL_FUNCTION_TYPES_H diff --git a/mvvm/model/mvvm/model/groupitem.cpp b/mvvm/model/mvvm/model/groupitem.cpp deleted file mode 100644 index dd1d0ceec0d..00000000000 --- a/mvvm/model/mvvm/model/groupitem.cpp +++ /dev/null @@ -1,92 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/groupitem.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/model/groupitem.h" -#include "mvvm/model/comboproperty.h" -#include "mvvm/model/taginfo.h" -#include "mvvm/utils/containerutils.h" -#include <stdexcept> - -using namespace ModelView; - -GroupItem::~GroupItem() = default; - -GroupItem::GroupItem(model_type modelType) - : SessionItem(std::move(modelType)) - , m_catalogue(std::make_unique<ItemCatalogue>()) - , m_default_selected_index(0) -{ - registerTag(TagInfo::universalTag(T_GROUP_ITEMS), /*set_as_default*/ true); - setData(ComboProperty()); -} - -int GroupItem::currentIndex() const -{ - return data<ComboProperty>().currentIndex(); -} - -//! Returns currently selected item. - -const SessionItem* GroupItem::currentItem() const -{ - return is_valid_index() ? getItem("", currentIndex()) : nullptr; -} - -SessionItem* GroupItem::currentItem() -{ - return const_cast<SessionItem*>(static_cast<const GroupItem*>(this)->currentItem()); -} - -std::string GroupItem::currentType() const -{ - return is_valid_index() ? m_catalogue->modelTypes()[static_cast<size_t>(currentIndex())] : ""; -} - -//! Sets item corresponding to given model type. - -void GroupItem::setCurrentType(const std::string& model_type) -{ - auto model_types = m_catalogue->modelTypes(); - int index = Utils::IndexOfItem(model_types, model_type); - if (index == -1) - throw std::runtime_error("GroupItem::setCurrentType() -> Model type '" + model_type - + "' doesn't belong to the group"); - - setCurrentIndex(index); -} - -void GroupItem::setCurrentIndex(int index) -{ - auto combo = data<ComboProperty>(); - combo.setCurrentIndex(index); - setData(combo, ItemDataRole::DATA); -} - -bool GroupItem::is_valid_index() const -{ - return currentIndex() != -1; -} - -//! Inits group item by creating all registered items and constructing combo property -//! for switching between items. - -void GroupItem::init_group() -{ - ComboProperty combo; - combo.setValues(m_catalogue->labels()); - combo.setCurrentIndex(m_default_selected_index); - setData(combo, ItemDataRole::DATA); - for (const auto& x : m_catalogue->modelTypes()) - insertItem(m_catalogue->create(x).release(), TagRow::append(T_GROUP_ITEMS)); -} diff --git a/mvvm/model/mvvm/model/groupitem.h b/mvvm/model/mvvm/model/groupitem.h deleted file mode 100644 index 06209964cfb..00000000000 --- a/mvvm/model/mvvm/model/groupitem.h +++ /dev/null @@ -1,61 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/groupitem.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_MODEL_GROUPITEM_H -#define BORNAGAIN_MVVM_MODEL_MVVM_MODEL_GROUPITEM_H - -#include "mvvm/model/itemcatalogue.h" -#include "mvvm/model/sessionitem.h" -#include <memory> - -namespace ModelView { - -//! Group item holds collection of predefined items. - -class MVVM_MODEL_EXPORT GroupItem : public SessionItem { -public: - static inline const std::string T_GROUP_ITEMS = "T_GROUP_ITEMS"; - - GroupItem(model_type modelType = GUI::Constants::GroupItemType); - ~GroupItem() override; - - int currentIndex() const; - - const SessionItem* currentItem() const; - SessionItem* currentItem(); - - std::string currentType() const; - void setCurrentType(const std::string& model_type); - -protected: - void setCurrentIndex(int index); - bool is_valid_index() const; - template <typename T> void registerItem(const std::string& text, bool make_selected = false); - // FIXME how to make sure that init_group() was called in constructor? - // Shell we delegate this call to CompoundItem::addProperty ? - void init_group(); - std::unique_ptr<ItemCatalogue> m_catalogue; - int m_default_selected_index; -}; - -template <typename T> void GroupItem::registerItem(const std::string& text, bool make_selected) -{ - m_catalogue->registerItem<T>(text); - if (make_selected) - m_default_selected_index = m_catalogue->itemCount() - 1; -} - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_MODEL_GROUPITEM_H diff --git a/mvvm/model/mvvm/model/itemcatalogue.cpp b/mvvm/model/mvvm/model/itemcatalogue.cpp deleted file mode 100644 index d1844fa24a1..00000000000 --- a/mvvm/model/mvvm/model/itemcatalogue.cpp +++ /dev/null @@ -1,101 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/itemcatalogue.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/model/itemcatalogue.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/utils/ifactory.h" -#include <stdexcept> - -using namespace ModelView; - -struct ItemCatalogue::ItemCatalogueImpl { - IFactory<std::string, SessionItem> factory; - struct TypeAndLabel { - std::string item_type; - std::string item_label; - }; - - std::vector<TypeAndLabel> m_info; -}; - -ItemCatalogue::ItemCatalogue() : p_impl(std::make_unique<ItemCatalogueImpl>()) {} - -ItemCatalogue::ItemCatalogue(const ItemCatalogue& other) -{ - p_impl = std::make_unique<ItemCatalogueImpl>(*other.p_impl); -} - -ItemCatalogue& ItemCatalogue::operator=(const ItemCatalogue& other) -{ - if (this != &other) { - ItemCatalogue tmp(other); - std::swap(this->p_impl, tmp.p_impl); - } - return *this; -} - -void ItemCatalogue::registerItem(const std::string& modelType, item_factory_func_t func, - const std::string& label) -{ - p_impl->factory.add(modelType, func); - p_impl->m_info.push_back({modelType, label}); -} - -ItemCatalogue::~ItemCatalogue() = default; - -bool ItemCatalogue::contains(const std::string& modelType) const -{ - return p_impl->factory.contains(modelType); -} - -std::unique_ptr<SessionItem> ItemCatalogue::create(const std::string& modelType) const -{ - return p_impl->factory.create(modelType); -} - -std::vector<std::string> ItemCatalogue::modelTypes() const -{ - std::vector<std::string> result; - for (const auto& x : p_impl->m_info) - result.push_back(x.item_type); - return result; -} - -std::vector<std::string> ItemCatalogue::labels() const -{ - std::vector<std::string> result; - for (const auto& x : p_impl->m_info) - result.push_back(x.item_label); - return result; -} - -int ItemCatalogue::itemCount() const -{ - return static_cast<int>(p_impl->factory.size()); -} - -//! Adds content of other catalogue to this. - -void ItemCatalogue::merge(const ItemCatalogue& other) -{ - size_t index(0); - for (auto it : other.p_impl->factory) { - if (contains(it.first)) - throw std::runtime_error( - "ItemCatalogue::add() -> Catalogue contains duplicated records"); - - registerItem(it.first, it.second, other.p_impl->m_info[index].item_label); - ++index; - } -} diff --git a/mvvm/model/mvvm/model/itemcatalogue.h b/mvvm/model/mvvm/model/itemcatalogue.h deleted file mode 100644 index 04586ae64f6..00000000000 --- a/mvvm/model/mvvm/model/itemcatalogue.h +++ /dev/null @@ -1,67 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/itemcatalogue.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_MODEL_ITEMCATALOGUE_H -#define BORNAGAIN_MVVM_MODEL_MVVM_MODEL_ITEMCATALOGUE_H - -#include "mvvm/model/function_types.h" -#include "mvvm/model_export.h" -#include <string> -#include <vector> - -namespace ModelView { - -class SessionItem; - -//! Catalogue for item constructions. Contains collection of factory functions associated with -//! item's modelType and optional label. - -class MVVM_MODEL_EXPORT ItemCatalogue { -public: - ItemCatalogue(); - ~ItemCatalogue(); - - ItemCatalogue(const ItemCatalogue& other); - ItemCatalogue& operator=(const ItemCatalogue& other); - - template <typename T> void registerItem(const std::string& label = {}); - - void registerItem(const std::string& modelType, item_factory_func_t func, - const std::string& label); - - bool contains(const std::string& modelType) const; - - std::unique_ptr<SessionItem> create(const std::string& modelType) const; - - std::vector<std::string> modelTypes() const; - - std::vector<std::string> labels() const; - - int itemCount() const; - - void merge(const ItemCatalogue& other); - -private: - struct ItemCatalogueImpl; - std::unique_ptr<ItemCatalogueImpl> p_impl; -}; - -template <typename T> void ItemCatalogue::registerItem(const std::string& label) -{ - registerItem(T().modelType(), ItemFactoryFunction<T>(), label); -} - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_MODEL_ITEMCATALOGUE_H diff --git a/mvvm/model/mvvm/model/itemfactory.cpp b/mvvm/model/mvvm/model/itemfactory.cpp deleted file mode 100644 index aaea3fc62e5..00000000000 --- a/mvvm/model/mvvm/model/itemfactory.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/itemfactory.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/model/itemfactory.h" -#include "mvvm/model/itemcatalogue.h" -#include "mvvm/model/sessionitem.h" - -using namespace ModelView; - -GUI::Model::ItemFactory::ItemFactory(std::unique_ptr<ItemCatalogue> catalogue) - : m_catalogue(std::move(catalogue)) -{ -} - -void GUI::Model::ItemFactory::registerItem(const std::string& modelType, item_factory_func_t func, - const std::string& label) -{ - m_catalogue->registerItem(modelType, func, label); -} - -GUI::Model::ItemFactory::~ItemFactory() = default; - -std::unique_ptr<SessionItem> GUI::Model::ItemFactory::createItem(const model_type& modelType) const -{ - return m_catalogue->create(modelType); -} diff --git a/mvvm/model/mvvm/model/itemfactory.h b/mvvm/model/mvvm/model/itemfactory.h deleted file mode 100644 index 47192e5f26b..00000000000 --- a/mvvm/model/mvvm/model/itemfactory.h +++ /dev/null @@ -1,44 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/itemfactory.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_MODEL_ITEMFACTORY_H -#define BORNAGAIN_MVVM_MODEL_MVVM_MODEL_ITEMFACTORY_H - -#include "mvvm/interfaces/itemfactoryinterface.h" -#include "mvvm/model_export.h" -#include <memory> - -namespace ModelView { - -class ItemCatalogue; - -//! Default SessionItem factory. - -class MVVM_MODEL_EXPORT ItemFactory : public ItemFactoryInterface { -public: - ItemFactory(std::unique_ptr<ItemCatalogue> catalogue); - ~ItemFactory() override; - - void registerItem(const std::string& modelType, item_factory_func_t func, - const std::string& label) override; - - std::unique_ptr<SessionItem> createItem(const model_type& modelType) const override; - -protected: - std::unique_ptr<ItemCatalogue> m_catalogue; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_MODEL_ITEMFACTORY_H diff --git a/mvvm/model/mvvm/model/itemmanager.cpp b/mvvm/model/mvvm/model/itemmanager.cpp deleted file mode 100644 index b4752256cdb..00000000000 --- a/mvvm/model/mvvm/model/itemmanager.cpp +++ /dev/null @@ -1,94 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/itemmanager.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/model/itemmanager.h" -#include "mvvm/factories/itemcataloguefactory.h" -#include "mvvm/model/itemfactory.h" -#include "mvvm/model/itempool.h" -#include "mvvm/model/sessionitem.h" - -namespace { -std::unique_ptr<ModelView::ItemFactory> DefaultItemFactory() -{ - return std::make_unique<ModelView::ItemFactory>(ModelView::CreateStandardItemCatalogue()); -} -} // namespace - -using namespace ModelView; - -ItemManager::ItemManager() : m_item_factory(DefaultItemFactory()) {} - -void ItemManager::setItemFactory(std::unique_ptr<ItemFactoryInterface> factory) -{ - m_item_factory = std::move(factory); -} - -void ItemManager::setItemPool(std::shared_ptr<ItemPool> pool) -{ - m_item_pool = std::move(pool); -} - -ItemManager::~ItemManager() = default; - -std::unique_ptr<SessionItem> ItemManager::createItem(const model_type& modelType) const -{ - return m_item_factory->createItem(modelType); -} - -std::unique_ptr<SessionItem> ItemManager::createRootItem() const -{ - return std::make_unique<SessionItem>(); -} - -SessionItem* ItemManager::findItem(const identifier_type& id) const -{ - return m_item_pool ? m_item_pool->item_for_key(id) : nullptr; -} - -identifier_type ItemManager::findIdentifier(const SessionItem* item) const -{ - return m_item_pool ? m_item_pool->key_for_item(item) : identifier_type(); -} - -const ItemPool* ItemManager::itemPool() const -{ - return m_item_pool.get(); -} - -ItemPool* ItemManager::itemPool() -{ - return m_item_pool.get(); -} - -void ItemManager::registerInPool(SessionItem* item) -{ - if (m_item_pool) - m_item_pool->register_item(item, item->identifier()); -} - -void ItemManager::unregisterFromPool(SessionItem* item) -{ - if (m_item_pool) - m_item_pool->unregister_item(item); -} - -const ItemFactoryInterface* ItemManager::factory() const -{ - return m_item_factory.get(); -} - -ItemFactoryInterface* ItemManager::factory() -{ - return const_cast<ItemFactoryInterface*>(static_cast<const ItemManager*>(this)->factory()); -} diff --git a/mvvm/model/mvvm/model/itemmanager.h b/mvvm/model/mvvm/model/itemmanager.h deleted file mode 100644 index 617c983b9f2..00000000000 --- a/mvvm/model/mvvm/model/itemmanager.h +++ /dev/null @@ -1,63 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/itemmanager.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_MODEL_ITEMMANAGER_H -#define BORNAGAIN_MVVM_MODEL_MVVM_MODEL_ITEMMANAGER_H - -#include "mvvm/model/mvvm_types.h" -#include "mvvm/model_export.h" -#include <memory> - -namespace ModelView { - -class SessionItem; -class ItemPool; -class ItemFactoryInterface; - -//! Manages item creation/registration for SessionModel. - -class MVVM_MODEL_EXPORT ItemManager { -public: - ItemManager(); - ~ItemManager(); - - void setItemFactory(std::unique_ptr<ItemFactoryInterface> factory); - void setItemPool(std::shared_ptr<ItemPool> pool); - - std::unique_ptr<SessionItem> createItem(const model_type& modelType = {}) const; - - std::unique_ptr<SessionItem> createRootItem() const; - - SessionItem* findItem(const identifier_type& id) const; - - identifier_type findIdentifier(const SessionItem* item) const; - - const ItemPool* itemPool() const; - ItemPool* itemPool(); - - void registerInPool(SessionItem* item); - void unregisterFromPool(SessionItem* item); - - const ItemFactoryInterface* factory() const; - - ItemFactoryInterface* factory(); - -private: - std::shared_ptr<ItemPool> m_item_pool; - std::unique_ptr<ItemFactoryInterface> m_item_factory; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_MODEL_ITEMMANAGER_H diff --git a/mvvm/model/mvvm/model/itempool.cpp b/mvvm/model/mvvm/model/itempool.cpp deleted file mode 100644 index cc0b33eb1ba..00000000000 --- a/mvvm/model/mvvm/model/itempool.cpp +++ /dev/null @@ -1,79 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/itempool.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/model/itempool.h" -#include "mvvm/core/uniqueidgenerator.h" -#include <stdexcept> - -using namespace ModelView; - -size_t ItemPool::size() const -{ - if (m_key_to_item.size() != m_item_to_key.size()) - throw std::runtime_error("Error in ItemPool: array size mismatch"); - return m_key_to_item.size(); -} - -identifier_type ItemPool::register_item(SessionItem* item, identifier_type key) - -{ - if (m_item_to_key.find(item) != m_item_to_key.end()) - throw std::runtime_error("ItemPool::register_item() -> Attempt to register already " - "registered item."); - - if (key.empty()) { - key = UniqueIdGenerator::generate(); - while (m_key_to_item.find(key) != m_key_to_item.end()) - key = UniqueIdGenerator::generate(); // preventing improbable duplicates - } else { - if (m_key_to_item.find(key) != m_key_to_item.end()) - throw std::runtime_error(" ItemPool::register_item() -> Attempt to reuse existing key"); - } - - m_key_to_item.insert(std::make_pair(key, item)); - m_item_to_key.insert(std::make_pair(item, key)); - - return key; -} - -void ItemPool::unregister_item(SessionItem* item) -{ - auto it = m_item_to_key.find(item); - if (it == m_item_to_key.end()) - throw std::runtime_error("ItemPool::deregister_item() -> Attempt to deregister " - "non existing item."); - auto key = it->second; - m_item_to_key.erase(it); - - auto it2 = m_key_to_item.find(key); - m_key_to_item.erase(it2); -} - -identifier_type ItemPool::key_for_item(const SessionItem* item) const -{ - const auto it = m_item_to_key.find(item); - if (it != m_item_to_key.end()) - return it->second; - - return {}; -} - -SessionItem* ItemPool::item_for_key(const identifier_type& key) const -{ - auto it = m_key_to_item.find(key); - if (it != m_key_to_item.end()) - return it->second; - - return nullptr; -} diff --git a/mvvm/model/mvvm/model/itempool.h b/mvvm/model/mvvm/model/itempool.h deleted file mode 100644 index 68be36663a2..00000000000 --- a/mvvm/model/mvvm/model/itempool.h +++ /dev/null @@ -1,53 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/itempool.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_MODEL_ITEMPOOL_H -#define BORNAGAIN_MVVM_MODEL_MVVM_MODEL_ITEMPOOL_H - -#include "mvvm/model/mvvm_types.h" -#include "mvvm/model_export.h" -#include <map> - -namespace ModelView { - -class SessionItem; - -//! Provides registration of SessionItem pointers and their unique identifiers -//! in global memory pool. - -class MVVM_MODEL_EXPORT ItemPool { -public: - ItemPool() = default; - ItemPool(const ItemPool&) = delete; - ItemPool(ItemPool&&) = delete; - ItemPool& operator=(const ItemPool&) = delete; - ItemPool& operator=(ItemPool&&) = delete; - - size_t size() const; - - identifier_type register_item(SessionItem* item, identifier_type key = {}); - void unregister_item(SessionItem* item); - - identifier_type key_for_item(const SessionItem* item) const; - - SessionItem* item_for_key(const identifier_type& key) const; - -private: - std::map<identifier_type, SessionItem*> m_key_to_item; - std::map<const SessionItem*, identifier_type> m_item_to_key; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_MODEL_ITEMPOOL_H diff --git a/mvvm/model/mvvm/model/itemutils.cpp b/mvvm/model/mvvm/model/itemutils.cpp deleted file mode 100644 index a3374b327a4..00000000000 --- a/mvvm/model/mvvm/model/itemutils.cpp +++ /dev/null @@ -1,177 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/itemutils.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/model/itemutils.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionitemcontainer.h" -#include "mvvm/model/sessionitemtags.h" -#include "mvvm/utils/containerutils.h" -#include <iterator> - -using namespace ModelView; - -void Utils::iterate(SessionItem* item, const std::function<void(SessionItem*)>& fun) -{ - if (item) - fun(item); - else - return; - - for (auto child : item->children()) - iterate(child, fun); -} - -void Utils::iterate_if(const SessionItem* item, const std::function<bool(const SessionItem*)>& fun) -{ - bool proceed_with_children(true); - - if (item) - proceed_with_children = fun(item); - - if (!item || !proceed_with_children) - return; - - for (auto child : item->children()) - iterate_if(child, fun); -} - -int Utils::CopyNumber(const SessionItem* item) -{ - int result(-1); - - if (!item) - return result; - - int count(0); - auto model_type = item->modelType(); - if (auto parent = item->parent()) { - for (auto child : parent->children()) { - if (child == item) - result = count; - if (child->modelType() == model_type) - ++count; - } - } - - return count > 1 ? result : -1; -} - -SessionItem* Utils::ChildAt(const SessionItem* parent, int index) -{ - if (!parent) - return nullptr; - - auto container = parent->children(); - return index >= 0 && static_cast<size_t>(index) < container.size() - ? container[static_cast<size_t>(index)] - : nullptr; -} - -int Utils::IndexOfChild(const SessionItem* parent, const SessionItem* child) -{ - return Utils::IndexOfItem(parent->children(), child); -} - -bool Utils::HasTag(const SessionItem& item, const std::string& tag) -{ - return item.itemTags()->isTag(tag); -} - -bool Utils::IsSinglePropertyTag(const SessionItem& item, const std::string& tag) -{ - return item.itemTags()->isSinglePropertyTag(tag); -} - -std::vector<std::string> Utils::RegisteredTags(const SessionItem& item) -{ - std::vector<std::string> result; - for (const auto container : *item.itemTags()) - result.push_back(container->name()); - return result; -} - -std::vector<std::string> Utils::RegisteredUniversalTags(const SessionItem& item) -{ - std::vector<std::string> result; - for (const auto& tag : RegisteredTags(item)) - if (!IsSinglePropertyTag(item, tag)) - result.push_back(tag); - return result; -} - -std::vector<SessionItem*> Utils::TopLevelItems(const SessionItem& item) -{ - std::vector<SessionItem*> result; - for (auto child : item.children()) - if (!IsSinglePropertyTag(item, item.tagRowOfItem(child).tag)) - result.push_back(child); - return result; -} - -std::vector<SessionItem*> Utils::SinglePropertyItems(const SessionItem& item) -{ - std::vector<SessionItem*> result; - for (auto child : item.children()) - if (IsSinglePropertyTag(item, item.tagRowOfItem(child).tag)) - result.push_back(child); - return result; -} - -SessionItem* Utils::FindNextSibling(SessionItem* item) -{ - auto parent = item ? item->parent() : nullptr; - if (!parent) - return nullptr; - auto tagrow = item->tagRow(); - return parent->getItem(tagrow.tag, tagrow.row + 1); -} - -SessionItem* Utils::FindPreviousSibling(SessionItem* item) -{ - auto parent = item ? item->parent() : nullptr; - if (!parent) - return nullptr; - auto tagrow = parent->tagRowOfItem(item); - return parent->getItem(tagrow.tag, tagrow.row - 1); -} - -SessionItem* Utils::FindNextItemToSelect(SessionItem* item) -{ - auto next = FindNextSibling(item); - auto closest = next ? next : FindPreviousSibling(item); - return closest ? closest : item->parent(); -} - -bool Utils::IsItemAncestor(const SessionItem* item, const SessionItem* candidate) -{ - if (!item || !candidate) - return false; - const SessionItem* parent = item->parent(); - while (parent) { - if (parent == candidate) - return true; - else - parent = parent->parent(); - } - return false; -} - -std::vector<SessionItem*> Utils::UniqueItems(const std::vector<SessionItem*>& items) -{ - auto filtered = Utils::UniqueWithOrder(items); - std::vector<SessionItem*> result; - std::copy_if(filtered.begin(), filtered.end(), std::back_inserter(result), - [](auto x) { return x != nullptr; }); - return result; -} diff --git a/mvvm/model/mvvm/model/itemutils.h b/mvvm/model/mvvm/model/itemutils.h deleted file mode 100644 index 49174449d8b..00000000000 --- a/mvvm/model/mvvm/model/itemutils.h +++ /dev/null @@ -1,104 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/itemutils.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_MODEL_ITEMUTILS_H -#define BORNAGAIN_MVVM_MODEL_MVVM_MODEL_ITEMUTILS_H - -#include "mvvm/model_export.h" -#include <functional> -#include <string> -#include <vector> - -namespace ModelView { - -class SessionItem; - -namespace Utils { - -//! Iterates through item and all its children. -MVVM_MODEL_EXPORT void iterate(SessionItem* item, const std::function<void(SessionItem*)>& fun); - -//! Iterates through all model indices and calls user function. -//! If function returns false for given index, iteration will not go down to children. -MVVM_MODEL_EXPORT void iterate_if(const SessionItem* item, - const std::function<bool(const SessionItem*)>& fun); - -//! Returns copy number of given item in it's parent hierarchy. Takes into account only items with -//! same modelType. -MVVM_MODEL_EXPORT int CopyNumber(const SessionItem* item); - -//! Returns child at given index of parent. No tags are involved, index is considered -//! as global index in the combined array of all children. -MVVM_MODEL_EXPORT SessionItem* ChildAt(const SessionItem* parent, int index); - -//! Returns index in children array corresponding to given child. No tags are involved, -//! index is considered as global index in the combined array of all children. -MVVM_MODEL_EXPORT int IndexOfChild(const SessionItem* parent, const SessionItem* child); - -//! Returns true if given item has registered tag. -MVVM_MODEL_EXPORT bool HasTag(const SessionItem& item, const std::string& tag); - -//! Returns true if given item has registered `tag`, and it belongs to single property. -MVVM_MODEL_EXPORT bool IsSinglePropertyTag(const SessionItem& item, const std::string& tag); - -//! Returns vector of strings containing all registered tags of the given item. -MVVM_MODEL_EXPORT std::vector<std::string> RegisteredTags(const SessionItem& item); - -//! Returns vector of strings containing all registered universal tags of the given item. -//! A universal tag is a tag that is usually empty after item construction and serves for later -//! children's insertion. -MVVM_MODEL_EXPORT std::vector<std::string> RegisteredUniversalTags(const SessionItem& item); - -//! Returns vector of children representing top level items. -MVVM_MODEL_EXPORT std::vector<SessionItem*> TopLevelItems(const SessionItem& item); - -//! Returns vector of children representing property items. - -MVVM_MODEL_EXPORT std::vector<SessionItem*> SinglePropertyItems(const SessionItem& item); - -//! Returns next sibling with same tag. - -MVVM_MODEL_EXPORT SessionItem* FindNextSibling(SessionItem* item); - -//! Returns previous sibling with same tag. - -MVVM_MODEL_EXPORT SessionItem* FindPreviousSibling(SessionItem* item); - -//! Finds next item to select -//! Method is used in the context of next item selection after given item was deleted. - -MVVM_MODEL_EXPORT SessionItem* FindNextItemToSelect(SessionItem* item); - -//! Returns true if 'candidate' is one of ancestor of given item. -MVVM_MODEL_EXPORT bool IsItemAncestor(const SessionItem* item, const SessionItem* candidate); - -//! Returns vector with duplicates and 'nullptr' filtered out. -MVVM_MODEL_EXPORT std::vector<SessionItem*> UniqueItems(const std::vector<SessionItem*>& items); - -//! Returns vector of items casted to given type. -template <typename T> std::vector<T*> CastedItems(const std::vector<SessionItem*>& items) -{ - std::vector<T*> result; - for (auto item : items) - if (auto casted_item = dynamic_cast<T*>(item); casted_item) - result.push_back(casted_item); - - return result; -} - -} // namespace Utils - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_MODEL_ITEMUTILS_H diff --git a/mvvm/model/mvvm/model/modelutils.cpp b/mvvm/model/mvvm/model/modelutils.cpp deleted file mode 100644 index 0ad046e3e44..00000000000 --- a/mvvm/model/mvvm/model/modelutils.cpp +++ /dev/null @@ -1,107 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/modelutils.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/model/modelutils.h" -#include "mvvm/interfaces/undostackinterface.h" -#include "mvvm/model/path.h" -#include <QJsonObject> - -using namespace ModelView; - -Path Utils::PathFromItem(const SessionItem* item) -{ - if (!item || !item->model()) - return {}; - - Path result; - const SessionItem* current(item); - while (current && current->parent()) { - result.prepend(Utils::IndexOfChild(current->parent(), current)); - current = current->parent(); - } - return result; -} - -SessionItem* Utils::ItemFromPath(const SessionModel& model, const Path& path) -{ - SessionItem* result(model.rootItem()); - for (const auto& x : path) { - result = Utils::ChildAt(result, x); - if (!result) - break; - } - return result; -} - -void Utils::PopulateEmptyModel(const JsonModelConverterInterface* converter, - const SessionModel& source, SessionModel& target) -{ - QJsonObject object = converter->to_json(source); - converter->from_json(object, target); -} - -void Utils::DeleteItemFromModel(SessionItem* item) -{ - auto model = item->model(); - if (!model) - return; - - model->removeItem(item->parent(), item->tagRow()); -} - -void Utils::MoveUp(SessionItem* item) -{ - auto tagrow = item->tagRow(); - if (tagrow.row == 0) - return; // item already at the top - item->model()->moveItem(item, item->parent(), tagrow.prev()); -} - -void Utils::MoveDown(SessionItem* item) -{ - auto tagrow = item->tagRow(); - if (tagrow.row == item->parent()->itemCount(tagrow.tag) - 1) - return; // item already at the buttom - item->model()->moveItem(item, item->parent(), tagrow.next()); -} - -void Utils::Undo(SessionModel& model) -{ - if (auto stack = model.undoStack(); stack) - stack->undo(); -} - -void Utils::Redo(SessionModel& model) -{ - if (auto stack = model.undoStack(); stack) - stack->redo(); -} - -void Utils::BeginMacros(const SessionItem* item, const std::string& macro_name) -{ - if (!item->model()) - return; - - if (auto stack = item->model()->undoStack(); stack) - stack->beginMacro(macro_name); -} - -void Utils::EndMacros(const SessionItem* item) -{ - if (!item->model()) - return; - - if (auto stack = item->model()->undoStack(); stack) - stack->endMacro(); -} diff --git a/mvvm/model/mvvm/model/modelutils.h b/mvvm/model/mvvm/model/modelutils.h deleted file mode 100644 index e36b20f57e7..00000000000 --- a/mvvm/model/mvvm/model/modelutils.h +++ /dev/null @@ -1,124 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/modelutils.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_MODEL_MODELUTILS_H -#define BORNAGAIN_MVVM_MODEL_MVVM_MODEL_MODELUTILS_H - -#include "mvvm/factories/modelconverterfactory.h" -#include "mvvm/model/itemutils.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/model_export.h" -#include <memory> -#include <vector> - -namespace ModelView { - -class Path; - -namespace Utils { - -//! Returns all top level items of given type. - -template <typename T = SessionItem> std::vector<T*> TopItems(const SessionModel* model) -{ - std::vector<T*> result; - for (auto child : model->rootItem()->children()) { - if (auto item = dynamic_cast<T*>(child); item) - result.push_back(item); - } - - return result; -} - -//! Returns top level item of given type. - -template <typename T = SessionItem> T* TopItem(const SessionModel* model) -{ - auto items = TopItems<T>(model); - return items.empty() ? nullptr : items.front(); -} - -//! Returns all items in a tree of given type. - -template <typename T = SessionItem> std::vector<T*> FindItems(const SessionModel* model) -{ - std::vector<T*> result; - - auto func = [&result](SessionItem* item) { - if (auto concrete = dynamic_cast<T*>(item); concrete) - result.push_back(concrete); - }; - - iterate(model->rootItem(), func); - - return result; -} - -//! Constructs path to find given item. Item must belong to a model. -MVVM_MODEL_EXPORT Path PathFromItem(const SessionItem* item); - -//! Returns item found in the model following given Path. -MVVM_MODEL_EXPORT SessionItem* ItemFromPath(const SessionModel& moodel, const Path& path); - -//! Populate empty model with content of target model using provided converter. -//! Serves as auxiliary function for model copying and cloning. -MVVM_MODEL_EXPORT void PopulateEmptyModel(const JsonModelConverterInterface* converter, - const SessionModel& source, SessionModel& target); - -//! Creates full deep copy of given model. All item's ID will be generated. -template <typename T = SessionModel> std::unique_ptr<T> CreateCopy(const T& model) -{ - auto result = std::make_unique<T>(); - auto converter = CreateModelCopyConverter(); - PopulateEmptyModel(converter.get(), model, *result.get()); - return result; -} - -//! Creates exact clone of given model. All item's ID will be preserved. -template <typename T = SessionModel> std::unique_ptr<T> CreateClone(const T& model) -{ - auto result = std::make_unique<T>(); - auto converter = CreateModelCloneConverter(); - PopulateEmptyModel(converter.get(), model, *result.get()); - return result; -} - -//! Removes and deletes item from its model. -MVVM_MODEL_EXPORT void DeleteItemFromModel(SessionItem* item); - -//! Moves item up (decrements row of the item). Works on children belonging to single tag. -MVVM_MODEL_EXPORT void MoveUp(SessionItem* item); - -//! Moves item down (increments row of the item). Works on children belonging to single tag. -MVVM_MODEL_EXPORT void MoveDown(SessionItem* item); - -//! Undo last model operation. If not undo/redo enabled, will do nothing. -MVVM_MODEL_EXPORT void Undo(SessionModel& model); - -//! Redo model operation which was undone just before. If not undo/redo enabled, will do nothing. -MVVM_MODEL_EXPORT void Redo(SessionModel& model); - -//! Begin undo/redo macros with given name. Works only if item belongs to the model, and model has -//! undo/redo enabled. Otherwise, do nothing. -MVVM_MODEL_EXPORT void BeginMacros(const SessionItem* item, const std::string& macro_name); - -//! Finishes undo/redo macros. Works only if item belongs to the model, and model has undo/redo -//! enabled. Otherwise, do nothing. -MVVM_MODEL_EXPORT void EndMacros(const SessionItem* item); - -} // namespace Utils -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_MODEL_MODELUTILS_H diff --git a/mvvm/model/mvvm/model/mvvm_types.h b/mvvm/model/mvvm/model/mvvm_types.h deleted file mode 100644 index c78656a7f3d..00000000000 --- a/mvvm/model/mvvm/model/mvvm_types.h +++ /dev/null @@ -1,67 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/mvvm_types.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_MODEL_MVVM_TYPES_H -#define BORNAGAIN_MVVM_MODEL_MVVM_MODEL_MVVM_TYPES_H - -#include "mvvm/core/types.h" -#include <string> - -namespace ModelView { - -class SessionItem; -class SessionModel; - -//! Defines constants to be used as SessionItem data role. - -namespace ItemDataRole { -const int IDENTIFIER = 0; //!< unique identifier -const int DATA = 1; //!< main data role -const int DISPLAY = 2; //!< display name -const int APPEARANCE = 3; //!< appearance flag -const int LIMITS = 4; //!< possibly limits on item's data -const int TOOLTIP = 5; //!< tooltip for item's data -const int EDITORTYPE = 6; //!< type of custom editor for the data role -} // namespace ItemDataRole - -enum Appearance { - NOFLAGS = 0x000, - ENABLED = 0x001, // enabled in Qt widgets; when disabled, will be shown in gray - EDITABLE = 0x002 // editable in Qt widgets; readonly otherwise -}; - -namespace GUI::Constants { -const model_type BaseType = "SessionItem"; -const model_type ColorMapItemType = "ColorMap"; -const model_type ColorMapViewportItemType = "ColorMapViewport"; -const model_type CompoundItemType = "Compound"; -const model_type ContainerItemType = "Container"; -const model_type Data1DItemType = "Data1D"; -const model_type Data2DItemType = "Data2D"; -const model_type FixedBinAxisItemType = "FixedBinAxis"; -const model_type GraphItemType = "Graph"; -const model_type GraphViewportItemType = "GraphViewport"; -const model_type GroupItemType = "Group"; -const model_type LinkedItemType = "Linked"; -const model_type PenItemType = "Pen"; -const model_type PointwiseAxisItemType = "PointwiseAxis"; -const model_type PropertyType = "Property"; -const model_type TextItemType = "Text"; -const model_type VectorItemType = "Vector"; -const model_type ViewportAxisItemType = "ViewportAxis"; -} // namespace GUI::Constants - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_MODEL_MVVM_TYPES_H diff --git a/mvvm/model/mvvm/model/path.cpp b/mvvm/model/mvvm/model/path.cpp deleted file mode 100644 index 71877a77d89..00000000000 --- a/mvvm/model/mvvm/model/path.cpp +++ /dev/null @@ -1,85 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/path.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/model/path.h" -#include <algorithm> -#include <iterator> -#include <numeric> -#include <sstream> - -using namespace ModelView; - -//! Constructs Path object from string containing sequence of integers ("0,0,1,3"). - -Path Path::fromString(const std::string& str) -{ - Path result; - - std::string str_spaces(str); - std::replace(str_spaces.begin(), str_spaces.end(), ',', ' '); - - std::istringstream iss(str_spaces); - std::for_each(std::istream_iterator<std::string>(iss), std::istream_iterator<std::string>(), - [&result](auto x) { result.append(std::stoi(x)); }); - return result; -} - -//! Constructs Path object from vector of integers.. - -Path Path::fromVector(const std::vector<int>& data) -{ - Path result; - std::for_each(data.begin(), data.end(), [&result](auto x) { result.append(x); }); - return result; -} - -//! Returns string representing path ("0,0,1,3"). - -std::string Path::str() const -{ - auto comma_fold = [](std::string a, int b) { return std::move(a) + ',' + std::to_string(b); }; - return m_data.empty() ? "" - : std::accumulate(std::next(m_data.begin()), m_data.end(), - std::to_string(m_data[0]), comma_fold); -} - -void Path::append(Path::PathElement element) -{ - m_data.push_back(element); -} - -void Path::prepend(Path::PathElement element) -{ - m_data.insert(m_data.begin(), element); -} - -Path::iterator Path::begin() -{ - return m_data.begin(); -} - -Path::const_iterator Path::begin() const -{ - return m_data.begin(); -} - -Path::iterator Path::end() -{ - return m_data.end(); -} - -Path::const_iterator Path::end() const -{ - return m_data.end(); -} diff --git a/mvvm/model/mvvm/model/path.h b/mvvm/model/mvvm/model/path.h deleted file mode 100644 index 9c0a8a3256d..00000000000 --- a/mvvm/model/mvvm/model/path.h +++ /dev/null @@ -1,65 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/path.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_MODEL_PATH_H -#define BORNAGAIN_MVVM_MODEL_MVVM_MODEL_PATH_H - -#include "mvvm/model_export.h" -#include <string> -#include <vector> - -namespace ModelView { - -//! Supports navigation through SessionModel. Contains a chain of indexes that have to -//! be used to reach the desired SessionItem starting from the root item. Path class plays -//! a role of simplified QModelIndex for SessionModel. Used for undo/redo only. - -//! Example of tree: -//! - root path:"" -//! - child path:"0" -//! - grandchild path:"0,0" -//! - grandchild path:"0,1" -//! - child path:"1" - -class MVVM_MODEL_EXPORT Path { -public: - using PathElement = int; - using container_t = std::vector<PathElement>; - using iterator = container_t::iterator; - using const_iterator = container_t::const_iterator; - - Path() = default; - - static Path fromString(const std::string& str); - - static Path fromVector(const std::vector<int>& data); - - std::string str() const; - - void append(PathElement element); - void prepend(PathElement element); - - iterator begin(); - const_iterator begin() const; - - iterator end(); - const_iterator end() const; - -private: - container_t m_data; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_MODEL_PATH_H diff --git a/mvvm/model/mvvm/model/propertyitem.cpp b/mvvm/model/mvvm/model/propertyitem.cpp deleted file mode 100644 index b3b3af04ba3..00000000000 --- a/mvvm/model/mvvm/model/propertyitem.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/propertyitem.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/model/propertyitem.h" -#include "mvvm/model/customvariants.h" -#include "mvvm/model/mvvm_types.h" - -using namespace ModelView; - -PropertyItem::PropertyItem() : SessionItem(GUI::Constants::PropertyType) {} - -PropertyItem* PropertyItem::setDisplayName(const std::string& name) -{ - SessionItem::setDisplayName(name); - return this; -} - -PropertyItem* PropertyItem::setLimits(const RealLimits& value) -{ - this->setData(value, ItemDataRole::LIMITS); - return this; -} diff --git a/mvvm/model/mvvm/model/propertyitem.h b/mvvm/model/mvvm/model/propertyitem.h deleted file mode 100644 index bfc8feb240e..00000000000 --- a/mvvm/model/mvvm/model/propertyitem.h +++ /dev/null @@ -1,38 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/propertyitem.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_MODEL_PROPERTYITEM_H -#define BORNAGAIN_MVVM_MODEL_MVVM_MODEL_PROPERTYITEM_H - -#include "mvvm/model/sessionitem.h" - -namespace ModelView { - -class RealLimits; - -//! Item to carry concrete editable entity (e.g. 'double' value with limits). -//! Intended for use as a child or CompountItem, not expected to have own children. - -class MVVM_MODEL_EXPORT PropertyItem : public SessionItem { -public: - PropertyItem(); - - PropertyItem* setDisplayName(const std::string& name) override; - - PropertyItem* setLimits(const RealLimits& value); -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_MODEL_PROPERTYITEM_H diff --git a/mvvm/model/mvvm/model/sessionitem.cpp b/mvvm/model/mvvm/model/sessionitem.cpp deleted file mode 100644 index 46c8827eced..00000000000 --- a/mvvm/model/mvvm/model/sessionitem.cpp +++ /dev/null @@ -1,393 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/sessionitem.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/model/sessionitem.h" -#include "mvvm/core/uniqueidgenerator.h" -#include "mvvm/model/customvariants.h" -#include "mvvm/model/sessionitemdata.h" -#include "mvvm/model/sessionitemtags.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/model/taginfo.h" -#include "mvvm/signals/itemmapper.h" -#include "mvvm/signals/modelmapper.h" -#include <stdexcept> - -using namespace ModelView; - -namespace { -int appearance(const ModelView::SessionItem& item) -{ - const int default_appearance = Appearance::EDITABLE | Appearance::ENABLED; - return item.hasData(ItemDataRole::APPEARANCE) ? item.data<int>(ItemDataRole::APPEARANCE) - : default_appearance; -} -} // namespace - -struct SessionItem::SessionItemImpl { - SessionItem* m_self{nullptr}; - SessionItem* m_parent{nullptr}; - SessionModel* m_model{nullptr}; - std::unique_ptr<ItemMapper> m_mapper; - std::unique_ptr<SessionItemData> m_data; - std::unique_ptr<SessionItemTags> m_tags; - model_type m_modelType; - - SessionItemImpl(SessionItem* this_item) - : m_self(this_item) - , m_data(std::make_unique<SessionItemData>()) - , m_tags(std::make_unique<SessionItemTags>()) - { - } - - bool do_setData(const Variant& variant, int role) - { - bool result = m_data->setData(variant, role); - if (result && m_model) - m_model->mapper()->callOnDataChange(m_self, role); - return result; - } -}; - -SessionItem::SessionItem(model_type modelType) : p_impl(std::make_unique<SessionItemImpl>(this)) -{ - p_impl->m_modelType = std::move(modelType); - setData(UniqueIdGenerator::generate(), ItemDataRole::IDENTIFIER); - setData(p_impl->m_modelType, ItemDataRole::DISPLAY); -} - -SessionItem::~SessionItem() -{ - if (p_impl->m_mapper) - p_impl->m_mapper->callOnItemDestroy(); - - if (p_impl->m_model) - p_impl->m_model->unregisterFromPool(this); -} - -//! Returns item's model type. - -model_type SessionItem::modelType() const -{ - return p_impl->m_modelType; -} - -//! Returns unique identifier. - -std::string SessionItem::identifier() const -{ - return data<std::string>(ItemDataRole::IDENTIFIER); -} - -//! Returns display name. - -std::string SessionItem::displayName() const -{ - return data<std::string>(ItemDataRole::DISPLAY); -} - -//! Sets display name (fluent interface). - -SessionItem* SessionItem::setDisplayName(const std::string& name) -{ - setData(name, ItemDataRole::DISPLAY); - return this; -} - -//! Returns the model to which given item belongs to. Will return nullptr if item doesn't have a -//! model. - -SessionModel* SessionItem::model() const -{ - return p_impl->m_model; -} - -//! Returns parent item. Will return nullptr if item doesn't have a parent. - -SessionItem* SessionItem::parent() const -{ - return p_impl->m_parent; -} - -//! Returns TagRow of this item under which it is accessible through its parent. - -TagRow SessionItem::tagRow() const -{ - return parent() ? parent()->tagRowOfItem(this) : TagRow(); -} - -//! Returns true if item has data on board with given role. - -bool SessionItem::hasData(int role) const -{ - return p_impl->m_data->hasData(role); -} - -//! Returns pointer to item's data container (const version). - -const SessionItemData* SessionItem::itemData() const -{ - return p_impl->m_data.get(); -} - -//! Returns pointer to item's data container (non-const version). - -SessionItemData* SessionItem::itemData() -{ - return const_cast<SessionItemData*>(static_cast<const SessionItem*>(this)->itemData()); -} - -//! Returns total number of children in all tags. - -int SessionItem::childrenCount() const -{ - return static_cast<int>(children().size()); -} - -//! Returns vector of children formed from all chidlren from all tags. - -std::vector<SessionItem*> SessionItem::children() const -{ - return p_impl->m_tags->allitems(); -} - -//! Returns number of items in given tag. - -int SessionItem::itemCount(const std::string& tag) const -{ - return p_impl->m_tags->itemCount(tag); -} - -//! Returns item at given row of given tag. - -SessionItem* SessionItem::getItem(const std::string& tag, int row) const -{ - return p_impl->m_tags->getItem({tag, row}); -} - -//! Returns all children stored at given tag. - -std::vector<SessionItem*> SessionItem::getItems(const std::string& tag) const -{ - return p_impl->m_tags->getItems(tag); -} - -//! Returns pair of tag and row corresponding to given item. -//! Returns {"", -1} if given item doesn't belong to children. - -TagRow SessionItem::tagRowOfItem(const SessionItem* item) const -{ - return p_impl->m_tags->tagRowOfItem(item); -} - -//! Returns pointer to internal collection of tag-registered items (const version). - -const SessionItemTags* SessionItem::itemTags() const -{ - return p_impl->m_tags.get(); -} - -//! Registers tag to hold items under given name. - -void SessionItem::registerTag(const TagInfo& tagInfo, bool set_as_default) -{ - p_impl->m_tags->registerTag(tagInfo, set_as_default); -} - -//! Returns pointer to internal collection of tag-registered items (non-const version). - -SessionItemTags* SessionItem::itemTags() -{ - return const_cast<SessionItemTags*>(static_cast<const SessionItem*>(this)->itemTags()); -} - -//! Insert item into given tag under the given row. - -bool SessionItem::insertItem(SessionItem* item, const TagRow& tagrow) -{ - // think of passing unique_ptr directly - - if (!item) - throw std::runtime_error("SessionItem::insertItem() -> Invalid item."); - - if (item->parent()) - throw std::runtime_error("SessionItem::insertItem() -> Existing parent."); - - if (item->model()) - throw std::runtime_error("SessionItem::insertItem() -> Existing model."); - - auto result = p_impl->m_tags->insertItem(item, tagrow); - if (result) { - item->setParent(this); - item->setModel(model()); - - if (p_impl->m_model) { - // FIXME think of actual_tagrow removal if input tag,row will be always valid - auto actual_tagrow = tagRowOfItem(item); - p_impl->m_model->mapper()->callOnItemInserted(this, actual_tagrow); - } - } - - return result; -} - -//! Removes item from given row from given tag, returns it to the caller. - -SessionItem* SessionItem::takeItem(const TagRow& tagrow) -{ - if (!p_impl->m_tags->canTakeItem(tagrow)) - return nullptr; - - if (p_impl->m_model) - p_impl->m_model->mapper()->callOnItemAboutToBeRemoved(this, tagrow); - - auto result = p_impl->m_tags->takeItem(tagrow); - result->setParent(nullptr); - result->setModel(nullptr); - // FIXME remaining problem is that ItemMapper still looking to the model - if (p_impl->m_model) - p_impl->m_model->mapper()->callOnItemRemoved(this, tagrow); - - return result; -} - -//! Returns true if this item has `editable` flag set. -//! The data value of an editable item normally can be changed when it appears in trees and tables. - -bool SessionItem::isEditable() const -{ - return appearance(*this) & Appearance::EDITABLE; -} - -//! Sets `editable` flag to given value (fluent interface). - -SessionItem* SessionItem::setEditable(bool value) -{ - setAppearanceFlag(Appearance::EDITABLE, value); - return this; -} - -//! Returns true if this item has `enabled` flag set. -//! Enabled items appear in normal color, disabled items are grayed out. - -bool SessionItem::isEnabled() const -{ - return appearance(*this) & Appearance::ENABLED; -} - -//! Sets `enabled` flag to given value (fluent interface). - -SessionItem* SessionItem::setEnabled(bool value) -{ - setAppearanceFlag(Appearance::ENABLED, value); - return this; -} - -//! Returns item tooltip, if exists. - -std::string SessionItem::toolTip() const -{ - return hasData(ItemDataRole::TOOLTIP) ? data<std::string>(ItemDataRole::TOOLTIP) : ""; -} - -//! Sets item tooltip (fluent interface). - -SessionItem* SessionItem::setToolTip(const std::string& tooltip) -{ - setData(tooltip, ItemDataRole::TOOLTIP); - return this; -} - -//! Returns editor type. - -std::string SessionItem::editorType() const -{ - return hasData(ItemDataRole::EDITORTYPE) ? data<std::string>(ItemDataRole::EDITORTYPE) : ""; -} - -//! Sets editor type (fluent interface). -//! Allows creating custom editors in the cells of Qt trees and tables. - -SessionItem* SessionItem::setEditorType(const std::string& editor_type) -{ - setData(editor_type, ItemDataRole::EDITORTYPE); - return this; -} - -//! Sets the data for given role. Method invented to hide implementaiton details. - -bool SessionItem::set_data_internal(const Variant& value, int role, bool direct) -{ - // If model is present, and undo stack is enabled, will forward request to the model - // (unless user explicitly asks for direct processing via direct=true flag). - const bool act_through_model = !direct && model() && model()->undoStack(); - return act_through_model ? model()->setData(this, value, role) - : p_impl->do_setData(value, role); -} - -//! Returns data for given role. Method invented to hide implementaiton details and avoid -//! placing sessionitemdata.h into 'sessionitem.h' header. - -Variant SessionItem::data_internal(int role) const -{ - return p_impl->m_data->data(role); -} - -void SessionItem::setParent(SessionItem* parent) -{ - p_impl->m_parent = parent; -} - -void SessionItem::setModel(SessionModel* model) -{ - if (p_impl->m_model) - p_impl->m_model->unregisterFromPool(this); - - p_impl->m_model = model; - - if (p_impl->m_model) - p_impl->m_model->registerInPool(this); - - for (auto child : children()) - child->setModel(model); -} - -void SessionItem::setAppearanceFlag(int flag, bool value) -{ - int flags = appearance(*this); - if (value) - flags |= flag; - else - flags &= ~flag; - - // By setting data with internal method we are bypassing the model, and so undo/redo. - // So current convention is to not invoke undo when changing appearance properties. - // Shall we change it? - p_impl->do_setData(flags, ItemDataRole::APPEARANCE); -} - -//! Returns item mapper. Allows subscribing to various events happening to the item. - -ItemMapper* SessionItem::mapper() -{ - if (!p_impl->m_mapper) - p_impl->m_mapper = std::make_unique<ItemMapper>(this); - return p_impl->m_mapper.get(); -} - -void SessionItem::setDataAndTags(std::unique_ptr<SessionItemData> data, - std::unique_ptr<SessionItemTags> tags) -{ - p_impl->m_data = std::move(data); - p_impl->m_tags = std::move(tags); -} diff --git a/mvvm/model/mvvm/model/sessionitem.h b/mvvm/model/mvvm/model/sessionitem.h deleted file mode 100644 index b6476306d69..00000000000 --- a/mvvm/model/mvvm/model/sessionitem.h +++ /dev/null @@ -1,206 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/sessionitem.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_MODEL_SESSIONITEM_H -#define BORNAGAIN_MVVM_MODEL_MVVM_MODEL_SESSIONITEM_H - -#include "mvvm/core/variant.h" -#include "mvvm/model/customvariants.h" -#include "mvvm/model/mvvm_types.h" -#include "mvvm/model/tagrow.h" -#include "mvvm/model_export.h" -#include <memory> -#include <stdexcept> -#include <vector> - -namespace ModelView { - -class SessionModel; -class TagInfo; -class ItemMapper; -class SessionItemData; -class SessionItemTags; - -//! The main object representing an editable/displayable/serializable entity. Serves as a -//! construction element (node) of SessionModel to represent all the data of GUI application. - -class MVVM_MODEL_EXPORT SessionItem { -public: - explicit SessionItem(model_type modelType = GUI::Constants::BaseType); - virtual ~SessionItem(); - SessionItem(const SessionItem&) = delete; - SessionItem& operator=(const SessionItem&) = delete; - - // basic item properties - - model_type modelType() const; - - std::string identifier() const; - - virtual SessionItem* setDisplayName(const std::string& name); - virtual std::string displayName() const; - - SessionModel* model() const; - - SessionItem* parent() const; - - TagRow tagRow() const; - - // methods to deal with item data - - bool hasData(int role = ItemDataRole::DATA) const; - - template <typename T> T data(int role = ItemDataRole::DATA) const; - - template <typename T> - bool setData(const T& value, int role = ItemDataRole::DATA, bool direct = false); - - SessionItemData* itemData(); - const SessionItemData* itemData() const; - - // children access - - int childrenCount() const; - - std::vector<SessionItem*> children() const; - - int itemCount(const std::string& tag) const; - - SessionItem* getItem(const std::string& tag, int row = 0) const; - - std::vector<SessionItem*> getItems(const std::string& tag) const; - - template <typename T> T* item(const std::string& tag) const; - template <typename T = SessionItem> std::vector<T*> items(const std::string& tag) const; - - TagRow tagRowOfItem(const SessionItem* item) const; - - void registerTag(const TagInfo& tagInfo, bool set_as_default = false); - - SessionItemTags* itemTags(); - const SessionItemTags* itemTags() const; - - // item manipulation - - bool insertItem(SessionItem* item, const TagRow& tagrow); - - SessionItem* takeItem(const TagRow& tagrow); - - // more convenience methods - - bool isEditable() const; - SessionItem* setEditable(bool value); - - bool isEnabled() const; - SessionItem* setEnabled(bool value); - - std::string toolTip() const; - SessionItem* setToolTip(const std::string& tooltip); - - std::string editorType() const; - SessionItem* setEditorType(const std::string& editor_type); - - template <typename T> T property(const std::string& tag) const; - template <typename T> void setProperty(const std::string& tag, const T& value); - void setProperty(const std::string& tag, const char* value); - - ItemMapper* mapper(); - -private: - friend class SessionModel; - friend class JsonItemConverter; - virtual void activate() {} - bool set_data_internal(const Variant& value, int role, bool direct); - Variant data_internal(int role) const; - void setParent(SessionItem* parent); - void setModel(SessionModel* model); - void setAppearanceFlag(int flag, bool value); - - void setDataAndTags(std::unique_ptr<SessionItemData> data, - std::unique_ptr<SessionItemTags> tags); - - struct SessionItemImpl; - std::unique_ptr<SessionItemImpl> p_impl; -}; - -//! Sets data for a given role. When extra parameter `direct` is false (default case), will act -//! through the model to register command in undo/redo framework (if enabled) and so allow later -//! undo. - -template <typename T> inline bool SessionItem::setData(const T& value, int role, bool direct) -{ - return set_data_internal(Variant::fromValue(value), role, direct); -} - -//! Returns data of given type T for given role. - -template <typename T> inline T SessionItem::data(int role) const -{ - return data_internal(role).value<T>(); -} - -//! Returns first item under given tag casted to a specified type. -//! Returns nullptr, if item doesn't exist. If item exists but can't be casted will throw. - -template <typename T> inline T* SessionItem::item(const std::string& tag) const -{ - if (auto item = getItem(tag); item) { - T* tag_item = dynamic_cast<T*>(item); - if (!tag_item) - throw std::runtime_error("Can't cast an item to given type"); - return tag_item; - } - return nullptr; -} - -//! Returns all items under given tag casted to specific type. - -template <typename T> std::vector<T*> SessionItem::items(const std::string& tag) const -{ - std::vector<T*> result; - for (auto item : getItems(tag)) - if (auto casted = dynamic_cast<T*>(item); casted) - result.push_back(casted); - return result; -} - -//! Returns data stored in property item. -//! Property is single item registered under certain tag via CompoundItem::addProperty method. - -template <typename T> inline T SessionItem::property(const std::string& tag) const -{ - return getItem(tag)->data<T>(); -} - -//! Sets value to property item. -//! Property is single item registered under certain tag via CompoundItem::addProperty method, the -//! value will be assigned to it's data role. - -template <typename T> inline void SessionItem::setProperty(const std::string& tag, const T& value) -{ - getItem(tag)->setData(value); -} - -//! Sets value to property item (specialized for special "const char *" case). -//! Property is single item registered under certain tag via CompoundItem::addProperty method, the -//! value will be assigned to it's data role. - -inline void SessionItem::setProperty(const std::string& tag, const char* value) -{ - setProperty(tag, std::string(value)); -} - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_MODEL_SESSIONITEM_H diff --git a/mvvm/model/mvvm/model/sessionitemcontainer.cpp b/mvvm/model/mvvm/model/sessionitemcontainer.cpp deleted file mode 100644 index fa9c4eacbaa..00000000000 --- a/mvvm/model/mvvm/model/sessionitemcontainer.cpp +++ /dev/null @@ -1,155 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/sessionitemcontainer.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/model/sessionitemcontainer.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/utils/containerutils.h" - -using namespace ModelView; - -SessionItemContainer::SessionItemContainer(ModelView::TagInfo tag_info) - : m_tag_info(std::move(tag_info)) -{ -} - -SessionItemContainer::~SessionItemContainer() -{ - for (auto item : m_items) - delete item; -} - -bool SessionItemContainer::empty() const -{ - return m_items.empty(); -} - -//! Returns number of items in given tag. - -int SessionItemContainer::itemCount() const -{ - return static_cast<int>(m_items.size()); -} - -//! Returns vector of items in this container. - -std::vector<SessionItem*> SessionItemContainer::items() const -{ - return m_items; -} - -/*! -@brief Inserts item in a vector of children at given index, returns true in the case of success. -@param item Item to be inserted, ownership will be taken. -@param index Item insert index in a range [0, itemCount] - -Insert index is an index which item will have after insertion. If item can't be inserted -(wrong model type, wrong index or maximum number of items reached), will return false. -*/ - -bool SessionItemContainer::insertItem(SessionItem* item, int index) -{ - if (!canInsertItem(item, index)) - return false; - - m_items.insert(std::next(m_items.begin(), index), item); - return true; -} - -//! Removes item at given index and returns it to the user. - -SessionItem* SessionItemContainer::takeItem(int index) -{ - if (minimum_reached()) - return nullptr; - - SessionItem* result = itemAt(index); - if (result) - m_items.erase(std::next(m_items.begin(), index)); - - return result; -} - -//! Returns true if item can be taken. - -bool SessionItemContainer::canTakeItem(int index) const -{ - return itemAt(index) && !minimum_reached(); -} - -//! Returns true if given item can be inserted under given index. - -bool SessionItemContainer::canInsertItem(const SessionItem* item, int index) const -{ - const bool valid_index = (index >= 0 && index <= itemCount()); - const bool enough_place = !maximum_reached(); - return valid_index && enough_place && is_valid_item(item); -} - -//! Returns index of item in vector of items. -//! Returns -1 if item doesn't belong to us. - -int SessionItemContainer::indexOfItem(const SessionItem* item) const -{ - return Utils::IndexOfItem(m_items, item); -} - -//! Returns item at given index. Returns nullptr if index is invalid. - -SessionItem* SessionItemContainer::itemAt(int index) const -{ - return index >= 0 && index < itemCount() ? m_items[static_cast<size_t>(index)] : nullptr; -} - -//! Returns the name of SessionItemTag. - -std::string SessionItemContainer::name() const -{ - return m_tag_info.name(); -} - -TagInfo SessionItemContainer::tagInfo() const -{ - return m_tag_info; -} - -SessionItemContainer::const_iterator SessionItemContainer::begin() const -{ - return m_items.begin(); -} - -SessionItemContainer::const_iterator SessionItemContainer::end() const -{ - return m_items.end(); -} - -//! Returns true if no more items are allowed. - -bool SessionItemContainer::maximum_reached() const -{ - return m_tag_info.max() != -1 && m_tag_info.max() == itemCount(); -} - -//! Returns true if less items than now is not allowed. - -bool SessionItemContainer::minimum_reached() const -{ - return m_tag_info.min() != -1 && m_tag_info.min() == itemCount(); -} - -//! Returns true if item's modelType is intended for this tag. - -bool SessionItemContainer::is_valid_item(const SessionItem* item) const -{ - return item && m_tag_info.isValidChild(item->modelType()); -} diff --git a/mvvm/model/mvvm/model/sessionitemcontainer.h b/mvvm/model/mvvm/model/sessionitemcontainer.h deleted file mode 100644 index d51978e76f6..00000000000 --- a/mvvm/model/mvvm/model/sessionitemcontainer.h +++ /dev/null @@ -1,74 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/sessionitemcontainer.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_MODEL_SESSIONITEMCONTAINER_H -#define BORNAGAIN_MVVM_MODEL_MVVM_MODEL_SESSIONITEMCONTAINER_H - -#include "mvvm/model/taginfo.h" -#include "mvvm/model_export.h" -#include <vector> - -namespace ModelView { - -class SessionItem; - -//! Holds collection of SessionItem objects related to the same tag. - -class MVVM_MODEL_EXPORT SessionItemContainer { -public: - using container_t = std::vector<SessionItem*>; - using const_iterator = container_t::const_iterator; - - SessionItemContainer(TagInfo tag_info); - SessionItemContainer(const SessionItemContainer&) = delete; - SessionItemContainer& operator=(const SessionItemContainer&) = delete; - ~SessionItemContainer(); - - bool empty() const; - - int itemCount() const; - - std::vector<SessionItem*> items() const; - - bool insertItem(SessionItem* item, int index); - - SessionItem* takeItem(int index); - - bool canTakeItem(int index) const; - - bool canInsertItem(const SessionItem* item, int index) const; - - int indexOfItem(const SessionItem* item) const; - - SessionItem* itemAt(int index) const; - - std::string name() const; - - TagInfo tagInfo() const; - - const_iterator begin() const; - - const_iterator end() const; - -private: - bool maximum_reached() const; - bool minimum_reached() const; - bool is_valid_item(const SessionItem* item) const; - TagInfo m_tag_info; - container_t m_items; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_MODEL_SESSIONITEMCONTAINER_H diff --git a/mvvm/model/mvvm/model/sessionitemdata.cpp b/mvvm/model/mvvm/model/sessionitemdata.cpp deleted file mode 100644 index c1359a09e97..00000000000 --- a/mvvm/model/mvvm/model/sessionitemdata.cpp +++ /dev/null @@ -1,95 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/sessionitemdata.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/model/sessionitemdata.h" -#include "mvvm/model/customvariants.h" -#include <algorithm> -#include <sstream> -#include <stdexcept> - -using namespace ModelView; - -std::vector<int> SessionItemData::roles() const -{ - std::vector<int> result; - for (const auto& value : m_values) - result.push_back(value.m_role); - return result; -} - -Variant SessionItemData::data(int role) const -{ - for (const auto& value : m_values) { - if (value.m_role == role) - return value.m_data; - } - return Variant(); -} - -//! Sets the data for given role. Returns true if data was changed. -//! If variant is invalid, corresponding role will be removed. - -bool SessionItemData::setData(const Variant& value, int role) -{ - assure_validity(value, role); - - for (auto it = m_values.begin(); it != m_values.end(); ++it) { - if (it->m_role == role) { - if (value.isValid()) { - if (Utils::IsTheSame(it->m_data, value)) - return false; - it->m_data = value; - } else { - m_values.erase(it); - } - return true; - } - } - m_values.push_back(DataRole(value, role)); - return true; -} - -SessionItemData::const_iterator SessionItemData::begin() const -{ - return m_values.begin(); -} - -SessionItemData::const_iterator SessionItemData::end() const -{ - return m_values.end(); -} - -//! Returns true if item has data with given role. - -bool SessionItemData::hasData(int role) const -{ - auto has_role = [role](const auto& x) { return x.m_role == role; }; - return std::find_if(m_values.begin(), m_values.end(), has_role) != m_values.end(); -} - -//! Check if variant is compatible - -void SessionItemData::assure_validity(const Variant& variant, int role) -{ - if (variant.typeName() == QStringLiteral("QString")) - throw std::runtime_error("Attempt to set QString based variant"); - - if (!Utils::CompatibleVariantTypes(data(role), variant)) { - std::ostringstream ostr; - ostr << "SessionItemData::assure_validity() -> Error. Variant types mismatch. " - << "Old variant type '" << data(role).typeName() << "' " - << "new variant type '" << variant.typeName() << "\n"; - throw std::runtime_error(ostr.str()); - } -} diff --git a/mvvm/model/mvvm/model/sessionitemdata.h b/mvvm/model/mvvm/model/sessionitemdata.h deleted file mode 100644 index 287509f524a..00000000000 --- a/mvvm/model/mvvm/model/sessionitemdata.h +++ /dev/null @@ -1,49 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/sessionitemdata.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_MODEL_SESSIONITEMDATA_H -#define BORNAGAIN_MVVM_MODEL_MVVM_MODEL_SESSIONITEMDATA_H - -#include "mvvm/model/datarole.h" -#include "mvvm/model_export.h" -#include <vector> - -namespace ModelView { - -//! Handles data roles for SessionItem. - -class MVVM_MODEL_EXPORT SessionItemData { -public: - using container_type = std::vector<DataRole>; - using const_iterator = container_type::const_iterator; - - std::vector<int> roles() const; - - Variant data(int role) const; - - bool setData(const Variant& value, int role); - - const_iterator begin() const; - const_iterator end() const; - - bool hasData(int role) const; - -private: - void assure_validity(const Variant& variant, int role); - container_type m_values; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_MODEL_SESSIONITEMDATA_H diff --git a/mvvm/model/mvvm/model/sessionitemtags.cpp b/mvvm/model/mvvm/model/sessionitemtags.cpp deleted file mode 100644 index 4170372ae10..00000000000 --- a/mvvm/model/mvvm/model/sessionitemtags.cpp +++ /dev/null @@ -1,183 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/sessionitemtags.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/model/sessionitemtags.h" -#include "mvvm/model/sessionitemcontainer.h" -#include <stdexcept> - -using namespace ModelView; - -SessionItemTags::SessionItemTags() = default; - -SessionItemTags::~SessionItemTags() -{ - for (auto tag : m_containers) - delete tag; -} - -void SessionItemTags::registerTag(const TagInfo& tagInfo, bool set_as_default) -{ - if (isTag(tagInfo.name())) - throw std::runtime_error("SessionItemTags::registerTag() -> Error. Existing name '" - + tagInfo.name() + "'"); - - m_containers.push_back(new SessionItemContainer(tagInfo)); - if (set_as_default) - m_default_tag = tagInfo.name(); -} - -//! Returns true if container with such name exists. - -bool SessionItemTags::isTag(const std::string& name) const -{ - for (auto tag : m_containers) - if (tag->name() == name) - return true; - return false; -} - -//! Returns the name of the default tag. - -std::string SessionItemTags::defaultTag() const -{ - return m_default_tag; -} - -void SessionItemTags::setDefaultTag(const std::string& name) -{ - m_default_tag = name; -} - -int SessionItemTags::itemCount(const std::string& tag_name) const -{ - return container(tag_name)->itemCount(); -} - -//! Inserts item in container with given tag name and at given row. -//! Returns true in the case of success. If tag name is empty, default tag will be used. - -bool SessionItemTags::insertItem(SessionItem* item, const TagRow& tagrow) -{ - auto tag_container = container(tagrow.tag); - auto row = tagrow.row < 0 ? tag_container->itemCount() : tagrow.row; - return container(tagrow.tag)->insertItem(item, row); -} - -//! Removes item at given row and for given tag, returns it to the user. - -SessionItem* SessionItemTags::takeItem(const TagRow& tagrow) -{ - return container(tagrow.tag)->takeItem(tagrow.row); -} - -//! Returns true if item can be taken. - -bool SessionItemTags::canTakeItem(const TagRow& tagrow) const -{ - return container(tagrow.tag)->canTakeItem(tagrow.row); -} - -//! Returns item at given row of given tag. - -SessionItem* SessionItemTags::getItem(const TagRow& tagrow) const -{ - return container(tagrow.tag)->itemAt(tagrow.row); -} - -//! Returns vector of items in the container with given name. -//! If tag name is empty, default tag will be used. - -std::vector<SessionItem*> SessionItemTags::getItems(const std::string& tag) const -{ - return container(tag)->items(); -} - -std::vector<SessionItem*> SessionItemTags::allitems() const -{ - std::vector<SessionItem*> result; - for (auto cont : m_containers) { - auto container_items = cont->items(); - result.insert(result.end(), container_items.begin(), container_items.end()); - } - - return result; -} - -//! Returns tag name and row of item in container. - -TagRow SessionItemTags::tagRowOfItem(const SessionItem* item) const -{ - for (auto cont : m_containers) { - int row = cont->indexOfItem(item); - if (row != -1) - return {cont->name(), row}; - } - - return {}; -} - -SessionItemTags::const_iterator SessionItemTags::begin() const -{ - return m_containers.begin(); -} - -SessionItemTags::const_iterator SessionItemTags::end() const -{ - return m_containers.end(); -} - -//! Returns true if given tag corresponds to registered single property tag. - -bool SessionItemTags::isSinglePropertyTag(const std::string& tag) const -{ - auto cont = find_container(tag); - return cont ? cont->tagInfo().isSinglePropertyTag() : false; -} - -int SessionItemTags::tagsCount() const -{ - return static_cast<int>(m_containers.size()); -} - -SessionItemContainer& SessionItemTags::at(int index) -{ - if (index < 0 || index >= tagsCount()) - throw std::runtime_error("Error it SessionItemTags: wrong container index"); - return *m_containers.at(index); -} - -//! Returns container corresponding to given tag name. If name is empty, -//! default tag will be used. Exception is thrown if no such tag exists. - -SessionItemContainer* SessionItemTags::container(const std::string& tag_name) const -{ - std::string tagName = tag_name.empty() ? defaultTag() : tag_name; - auto container = find_container(tagName); - if (!container) - throw std::runtime_error("SessionItemTags::container() -> Error. No such container '" - + tagName + "'"); - - return container; -} - -//! Returns container corresponding to given tag name. - -SessionItemContainer* SessionItemTags::find_container(const std::string& tag_name) const -{ - for (auto cont : m_containers) - if (cont->name() == tag_name) - return cont; - - return nullptr; -} diff --git a/mvvm/model/mvvm/model/sessionitemtags.h b/mvvm/model/mvvm/model/sessionitemtags.h deleted file mode 100644 index 9483ee736b5..00000000000 --- a/mvvm/model/mvvm/model/sessionitemtags.h +++ /dev/null @@ -1,88 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/sessionitemtags.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_MODEL_SESSIONITEMTAGS_H -#define BORNAGAIN_MVVM_MODEL_MVVM_MODEL_SESSIONITEMTAGS_H - -#include "mvvm/model/tagrow.h" -#include "mvvm/model_export.h" -#include <string> -#include <vector> - -namespace ModelView { - -class SessionItemContainer; -class TagInfo; -class SessionItem; - -//! Collection of SessionItem's containers according to their tags. - -class MVVM_MODEL_EXPORT SessionItemTags { -public: - using container_t = std::vector<SessionItemContainer*>; - using const_iterator = container_t::const_iterator; - - SessionItemTags(); - ~SessionItemTags(); - SessionItemTags(const SessionItemTags&) = delete; - SessionItemTags& operator=(const SessionItemTags&) = delete; - - // tag - - void registerTag(const TagInfo& tagInfo, bool set_as_default = false); - - bool isTag(const std::string& name) const; - - std::string defaultTag() const; - - void setDefaultTag(const std::string& name); - - int itemCount(const std::string& tag_name) const; - - // adding and removal - - bool insertItem(SessionItem* item, const TagRow& tagrow); - - SessionItem* takeItem(const TagRow& tagrow); - - bool canTakeItem(const TagRow& tagrow) const; - - // item access - SessionItem* getItem(const TagRow& tagrow) const; - - std::vector<SessionItem*> getItems(const std::string& tag = {}) const; - - std::vector<SessionItem*> allitems() const; - - TagRow tagRowOfItem(const SessionItem* item) const; - - const_iterator begin() const; - const_iterator end() const; - - bool isSinglePropertyTag(const std::string& tag) const; - - int tagsCount() const; - - SessionItemContainer& at(int index); - -private: - SessionItemContainer* container(const std::string& tag_name) const; - SessionItemContainer* find_container(const std::string& tag_name) const; - std::vector<SessionItemContainer*> m_containers; - std::string m_default_tag; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_MODEL_SESSIONITEMTAGS_H diff --git a/mvvm/model/mvvm/model/sessionmodel.cpp b/mvvm/model/mvvm/model/sessionmodel.cpp deleted file mode 100644 index a822c930ec8..00000000000 --- a/mvvm/model/mvvm/model/sessionmodel.cpp +++ /dev/null @@ -1,232 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/sessionmodel.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/model/sessionmodel.h" -#include "mvvm/commands/commandservice.h" -#include "mvvm/factories/itemcataloguefactory.h" -#include "mvvm/model/customvariants.h" -#include "mvvm/model/itemcatalogue.h" -#include "mvvm/model/itemfactory.h" -#include "mvvm/model/itemmanager.h" -#include "mvvm/model/itempool.h" -#include "mvvm/model/itemutils.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/taginfo.h" -#include "mvvm/model/tagrow.h" -#include "mvvm/signals/modelmapper.h" - -using namespace ModelView; - -//! Pimpl class for SessionModel. - -struct SessionModel::SessionModelImpl { - SessionModel* m_self{nullptr}; - std::string m_modelType; - std::unique_ptr<ItemManager> m_itemManager; - std::unique_ptr<CommandService> m_commands; - std::unique_ptr<ModelMapper> m_mapper; - std::unique_ptr<SessionItem> m_root_item; - SessionModelImpl(SessionModel* self, std::string modelType, std::shared_ptr<ItemPool> pool) - : m_self(self) - , m_modelType(std::move(modelType)) - , m_itemManager(std::make_unique<ItemManager>()) - , m_commands(std::make_unique<CommandService>(self)) - , m_mapper(std::make_unique<ModelMapper>(self)) - { - setItemPool(pool); - } - - void setItemPool(std::shared_ptr<ItemPool> pool) - { - m_itemManager->setItemPool(pool ? std::move(pool) : std::make_shared<ItemPool>()); - } - - //! Creates root item. - void createRootItem() - { - m_root_item = m_itemManager->createRootItem(); - m_root_item->setModel(m_self); - m_root_item->registerTag(TagInfo::universalTag("rootTag"), /*set_as_default*/ true); - } -}; - -//! Main c-tor. - -SessionModel::SessionModel(std::string model_type, std::shared_ptr<ItemPool> pool) - : p_impl(std::make_unique<SessionModelImpl>(this, std::move(model_type), std::move(pool))) - -{ - p_impl->createRootItem(); -} - -SessionModel::~SessionModel() -{ - // Explicitly call root item's destructor. It uses p_impl pointer during own descruction - // and we have to keep pimpl pointer intact. Without line below will crash on MacOS because - // of pecularities of MacOS libc++. See explanations here: - // http://ibob.github.io/blog/2019/11/07/dont-use-unique_ptr-for-pimpl/ - p_impl->m_root_item.reset(); - - p_impl->m_mapper->callOnModelDestroyed(); -} - -//! Insert new item using item's modelType. - -SessionItem* SessionModel::insertNewItem(const model_type& modelType, SessionItem* parent, - const TagRow& tagrow) -{ - // intentionally passing by value inside lambda - auto create_func = [this, modelType]() { return factory()->createItem(modelType); }; - return intern_insert(create_func, parent, tagrow); -} - -//! Removes given row from parent. - -void SessionModel::removeItem(SessionItem* parent, const TagRow& tagrow) -{ - p_impl->m_commands->removeItem(parent, tagrow); -} - -//! Move item from it's current parent to a new parent under given tag and row. -//! Old and new parents should belong to this model. - -void SessionModel::moveItem(SessionItem* item, SessionItem* new_parent, const TagRow& tagrow) -{ - p_impl->m_commands->moveItem(item, new_parent, tagrow); -} - -//! Copy item and insert it in parent's tag and row. Item could belong to any model/parent. - -SessionItem* SessionModel::copyItem(const SessionItem* item, SessionItem* parent, - const TagRow& tagrow) -{ - return p_impl->m_commands->copyItem(item, parent, tagrow); -} - -//! Returns the data for given item and role. - -Variant SessionModel::data(SessionItem* item, int role) const -{ - return item->data<Variant>(role); -} - -//! Sets the data for given item. - -bool SessionModel::setData(SessionItem* item, const Variant& value, int role) -{ - return p_impl->m_commands->setData(item, value, role); -} - -//! Returns model type. - -std::string SessionModel::modelType() const -{ - return p_impl->m_modelType; -} - -//! Returns root item of the model. - -SessionItem* SessionModel::rootItem() const -{ - return p_impl->m_root_item.get(); -} - -//! Returns model mapper. Can be used to subscribe to various model's signal. - -ModelMapper* SessionModel::mapper() -{ - return p_impl->m_mapper.get(); -} - -//! Returns command stack to perform undo/redo. - -UndoStackInterface* SessionModel::undoStack() const -{ - return p_impl->m_commands->undoStack(); -} - -//! Returns item factory which can generate all items supported by this model. - -const ItemFactoryInterface* SessionModel::factory() const -{ - return p_impl->m_itemManager->factory(); -} - -//! Returns SessionItem for given identifier. - -SessionItem* SessionModel::findItem(const identifier_type& id) -{ - return p_impl->m_itemManager->findItem(id); -} - -//! Sets brand new catalog of user-defined items. They become available for undo/redo and -//! serialization. Internally user catalog will be merged with the catalog of standard items. - -void SessionModel::setItemCatalogue(std::unique_ptr<ItemCatalogue> catalogue) -{ - // adding standard items to the user catalogue - std::unique_ptr<ItemCatalogue> full_catalogue = std::move(catalogue); - full_catalogue->merge(*CreateStandardItemCatalogue()); - p_impl->m_itemManager->setItemFactory(std::make_unique<ItemFactory>(std::move(full_catalogue))); -} - -//! Sets undo/redo either enabled or disabled. By default undo/redo is disabled. - -void SessionModel::setUndoRedoEnabled(bool value) -{ - p_impl->m_commands->setUndoRedoEnabled(value); -} - -//! Removes all items from the model. If callback is provided, use it to rebuild content of root -//! item (used while restoring the model from serialized content). - -void SessionModel::clear(std::function<void(SessionItem*)> callback) -{ - if (undoStack()) - undoStack()->clear(); - mapper()->callOnModelAboutToBeReset(); - p_impl->createRootItem(); - if (callback) - callback(rootItem()); - mapper()->callOnModelReset(); -} - -//! Registers item in pool. This will allow to find item pointer using its unique identifier. - -void SessionModel::registerInPool(SessionItem* item) -{ - p_impl->m_itemManager->registerInPool(item); - item->activate(); // activates buisiness logic -} - -//! Unregister item from pool. - -void SessionModel::unregisterFromPool(SessionItem* item) -{ - p_impl->m_itemManager->unregisterFromPool(item); -} - -//! Insert new item into given parent using factory function provided. - -SessionItem* SessionModel::intern_insert(const item_factory_func_t& func, SessionItem* parent, - const TagRow& tagrow) -{ - return p_impl->m_commands->insertNewItem(func, parent, tagrow); -} - -void SessionModel::intern_register(const model_type& modelType, const item_factory_func_t& func, - const std::string& label) -{ - p_impl->m_itemManager->factory()->registerItem(modelType, func, label); -} diff --git a/mvvm/model/mvvm/model/sessionmodel.h b/mvvm/model/mvvm/model/sessionmodel.h deleted file mode 100644 index 80e0d95fa61..00000000000 --- a/mvvm/model/mvvm/model/sessionmodel.h +++ /dev/null @@ -1,142 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/sessionmodel.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_MODEL_SESSIONMODEL_H -#define BORNAGAIN_MVVM_MODEL_MVVM_MODEL_SESSIONMODEL_H - -#include "mvvm/core/types.h" -#include "mvvm/core/variant.h" -#include "mvvm/model/function_types.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/tagrow.h" -#include "mvvm/model_export.h" -#include <memory> - -namespace ModelView { - -class SessionItem; -class ItemCatalogue; -class ItemPool; -class ModelMapper; -class ItemFactoryInterface; -class UndoStackInterface; - -//! Main class to hold hierarchy of SessionItem objects. - -class MVVM_MODEL_EXPORT SessionModel { -public: - explicit SessionModel(std::string model_type = {}, std::shared_ptr<ItemPool> pool = {}); - virtual ~SessionModel(); - SessionModel(const SessionModel& other) = delete; - SessionModel& operator=(const SessionModel& other) = delete; - - // Methods to manipulate data and items. - - SessionItem* insertNewItem(const model_type& modelType, SessionItem* parent = nullptr, - const TagRow& tagrow = {}); - - template <typename T> T* insertItem(SessionItem* parent = nullptr, const TagRow& tagrow = {}); - - void removeItem(SessionItem* parent, const TagRow& tagrow); - - void moveItem(SessionItem* item, SessionItem* new_parent, const TagRow& tagrow); - - SessionItem* copyItem(const SessionItem* item, SessionItem* parent, const TagRow& tagrow = {}); - - Variant data(SessionItem* item, int role) const; - - bool setData(SessionItem* item, const Variant& value, int role); - - // Various getters. - - std::string modelType() const; - - SessionItem* rootItem() const; - - ModelMapper* mapper(); - - UndoStackInterface* undoStack() const; - - const ItemFactoryInterface* factory() const; - - SessionItem* findItem(const identifier_type& id); - - template <typename T = SessionItem> std::vector<T*> topItems() const; - - template <typename T = SessionItem> T* topItem() const; - - // Methods to steer global behaviour. - - void setItemCatalogue(std::unique_ptr<ItemCatalogue> catalogue); - - void setUndoRedoEnabled(bool value); - - void clear(std::function<void(SessionItem*)> callback = {}); - - template <typename T> void registerItem(const std::string& label = {}); - -private: - friend class SessionItem; - void registerInPool(SessionItem* item); - void unregisterFromPool(SessionItem* item); - SessionItem* intern_insert(const item_factory_func_t& func, SessionItem* parent, - const TagRow& tagrow); - void intern_register(const model_type& modelType, const item_factory_func_t& func, - const std::string& label); - - struct SessionModelImpl; - std::unique_ptr<SessionModelImpl> p_impl; -}; - -//! Inserts item into given parent under given tagrow. - -template <typename T> T* SessionModel::insertItem(SessionItem* parent, const TagRow& tagrow) -{ - return static_cast<T*>(intern_insert(ItemFactoryFunction<T>(), parent, tagrow)); -} - -//! Returns top items of the given type. -//! The top item is an item that is a child of an invisible root item. - -template <typename T> std::vector<T*> SessionModel::topItems() const -{ - std::vector<T*> result; - for (auto child : rootItem()->children()) { - if (auto item = dynamic_cast<T*>(child)) - result.push_back(item); - } - - return result; -} - -//! Returns top item of the given type. If more than one item exists, return the first one. -//! The top item is an item that is a child of an invisible root item. - -template <typename T> T* SessionModel::topItem() const -{ - auto items = topItems<T>(); - return items.empty() ? nullptr : items.front(); -} - -//! Register used defined item to use with the model. It will become possible to undo/redo -//! operations with this item, as well as serialize it to/from JSON. - -template <typename T> void SessionModel::registerItem(const std::string& label) -{ - intern_register(T().modelType(), ItemFactoryFunction<T>(), label); -} - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_MODEL_SESSIONMODEL_H diff --git a/mvvm/model/mvvm/model/taginfo.cpp b/mvvm/model/mvvm/model/taginfo.cpp deleted file mode 100644 index ed38fde009d..00000000000 --- a/mvvm/model/mvvm/model/taginfo.cpp +++ /dev/null @@ -1,89 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/taginfo.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/model/taginfo.h" -#include "mvvm/utils/containerutils.h" -#include <algorithm> -#include <sstream> -#include <stdexcept> - -ModelView::TagInfo::TagInfo() : m_min(0), m_max(-1) {} - -ModelView::TagInfo::TagInfo(std::string name, int min, int max, std::vector<std::string> modelTypes) - : m_name(std::move(name)), m_min(min), m_max(max), m_modelTypes(std::move(modelTypes)) -{ - if (m_min < 0 || (m_min > m_max && m_max >= 0) || m_name.empty()) { - std::ostringstream ostr; - ostr << "Invalid constructor parameters" - << " " << m_name << " " << m_min << " " << m_max; - throw std::runtime_error(ostr.str()); - } -} - -ModelView::TagInfo ModelView::TagInfo::universalTag(std::string name, - std::vector<std::string> modelTypes) -{ - return TagInfo(std::move(name), 0, -1, std::move(modelTypes)); -} - -ModelView::TagInfo ModelView::TagInfo::propertyTag(std::string name, std::string model_type) -{ - return TagInfo(std::move(name), 1, 1, {std::move(model_type)}); -} - -std::string ModelView::TagInfo::name() const -{ - return m_name; -} - -int ModelView::TagInfo::min() const -{ - return m_min; -} - -int ModelView::TagInfo::max() const -{ - return m_max; -} - -std::vector<std::string> ModelView::TagInfo::modelTypes() const -{ - return m_modelTypes; -} - -//! Returns true if given modelType matches the list of possible model types. - -bool ModelView::TagInfo::isValidChild(const std::string& modelType) const -{ - return m_modelTypes.empty() ? true : Utils::Contains(m_modelTypes, modelType); -} - -//! Returns true if this tag is used to store single properties. -//! Properties are children that are created in SessionItem constructor using ::addProperty method. - -bool ModelView::TagInfo::isSinglePropertyTag() const -{ - return m_min == 1 && m_max == 1; -} - -bool ModelView::TagInfo::operator==(const ModelView::TagInfo& other) const -{ - return m_name == other.m_name && m_min == other.m_min && m_max == other.m_max - && m_modelTypes == other.m_modelTypes; -} - -bool ModelView::TagInfo::operator!=(const ModelView::TagInfo& other) const -{ - return !(*this == other); -} diff --git a/mvvm/model/mvvm/model/taginfo.h b/mvvm/model/mvvm/model/taginfo.h deleted file mode 100644 index f352b1cf6ee..00000000000 --- a/mvvm/model/mvvm/model/taginfo.h +++ /dev/null @@ -1,66 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/taginfo.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_MODEL_TAGINFO_H -#define BORNAGAIN_MVVM_MODEL_MVVM_MODEL_TAGINFO_H - -#include "mvvm/model_export.h" -#include <string> -#include <vector> - -namespace ModelView { - -//! Holds info about single tag for SessionItem. -//! The tag specifies information about children that can be added to a SessionItem. A tag has a -//! name, min, max allowed number of children, and vector of all modelTypes that children can have. - -class MVVM_MODEL_EXPORT TagInfo { -public: - TagInfo(); - - TagInfo(std::string name, int min, int max, std::vector<std::string> modelTypes); - - //! Constructs universal tag intended for unlimited amount of various items. - static TagInfo universalTag(std::string name, std::vector<std::string> modelTypes = {}); - - //! Constructs tag intended for single property. - static TagInfo propertyTag(std::string name, std::string model_type); - - std::string name() const; - - int min() const; - - int max() const; - - std::vector<std::string> modelTypes() const; - - bool maximumReached() const; - - bool isValidChild(const std::string& modelType) const; - - bool isSinglePropertyTag() const; - - bool operator==(const TagInfo& other) const; - bool operator!=(const TagInfo& other) const; - -private: - std::string m_name; - int m_min; - int m_max; - std::vector<std::string> m_modelTypes; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_MODEL_TAGINFO_H diff --git a/mvvm/model/mvvm/model/tagrow.cpp b/mvvm/model/mvvm/model/tagrow.cpp deleted file mode 100644 index e0309b26186..00000000000 --- a/mvvm/model/mvvm/model/tagrow.cpp +++ /dev/null @@ -1,57 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/tagrow.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/model/tagrow.h" - -//! Constructs new tagrow representing next row in given tag. -//! No validity check. - -ModelView::TagRow ModelView::TagRow::next() const -{ - return {tag, row + 1}; -} - -//! Constructs new tagrow representing previous row in given tag. -//! No validity check. - -ModelView::TagRow ModelView::TagRow::prev() const -{ - return {tag, row - 1}; -} - -//! Returns TagRow corresponding to the append to tag_name. -//! If tag_name =="" the default name will be used in SessionItemTags context. - -ModelView::TagRow ModelView::TagRow::append(const std::string& tag_name) -{ - return {tag_name, -1}; -} - -//! Returns TagRow corresponding to prepending to tag_name. -//! If tag_name =="" the default name will be used in SessionItemTags context. - -ModelView::TagRow ModelView::TagRow::prepend(const std::string& tag_name) -{ - return {tag_name, 0}; -} - -bool ModelView::TagRow::operator==(const ModelView::TagRow& other) const -{ - return row == other.row && tag == other.tag; -} - -bool ModelView::TagRow::operator!=(const ModelView::TagRow& other) const -{ - return !(*this == other); -} diff --git a/mvvm/model/mvvm/model/tagrow.h b/mvvm/model/mvvm/model/tagrow.h deleted file mode 100644 index bf66cd94613..00000000000 --- a/mvvm/model/mvvm/model/tagrow.h +++ /dev/null @@ -1,49 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/tagrow.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_MODEL_TAGROW_H -#define BORNAGAIN_MVVM_MODEL_MVVM_MODEL_TAGROW_H - -#include "mvvm/model_export.h" -#include <string> - -namespace ModelView { - -//! Aggregate to hold (tag, row) information for SessionModel. - -class MVVM_MODEL_EXPORT TagRow { -public: - std::string tag = {}; - int row = -1; - - TagRow() {} - - TagRow(const std::string& name, int row = -1) : tag(name), row(row) {} - TagRow(const char* name, int row = -1) : tag(name), row(row) {} - - TagRow next() const; - - TagRow prev() const; - - static TagRow append(const std::string& tag_name = {}); - - static TagRow prepend(const std::string& tag_name = {}); - - bool operator==(const TagRow& other) const; - bool operator!=(const TagRow& other) const; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_MODEL_TAGROW_H diff --git a/mvvm/model/mvvm/model/variant_constants.h b/mvvm/model/mvvm/model/variant_constants.h deleted file mode 100644 index bb7ca58dca9..00000000000 --- a/mvvm/model/mvvm/model/variant_constants.h +++ /dev/null @@ -1,38 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/model/variant_constants.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_MODEL_VARIANT_CONSTANTS_H -#define BORNAGAIN_MVVM_MODEL_MVVM_MODEL_VARIANT_CONSTANTS_H - -#include <string> - -//! @file mvvm/model/mvvm/model/variant_constants.h -//! Collection of constants with supported variant names. - -namespace ModelView::Constants { - -const std::string invalid_type_name = "invalid"; -const std::string bool_type_name = "bool"; -const std::string int_type_name = "int"; -const std::string string_type_name = "std::string"; -const std::string double_type_name = "double"; -const std::string vector_double_type_name = "std::vector<double>"; -const std::string comboproperty_type_name = "ModelView::ComboProperty"; -const std::string qcolor_type_name = "QColor"; -const std::string extproperty_type_name = "ModelView::ExternalProperty"; -const std::string reallimits_type_name = "ModelView::RealLimits"; - -} // namespace ModelView::Constants - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_MODEL_VARIANT_CONSTANTS_H diff --git a/mvvm/model/mvvm/project/CMakeLists.txt b/mvvm/model/mvvm/project/CMakeLists.txt deleted file mode 100644 index 2de7a9057c4..00000000000 --- a/mvvm/model/mvvm/project/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -target_sources(${library_name} PRIVATE - modelhaschangedcontroller.cpp - modelhaschangedcontroller.h - project.cpp - project.h - project_types.h - projectchangecontroller.cpp - projectchangecontroller.h - projectmanager.cpp - projectmanager.h - projectmanagerdecorator.cpp - projectmanagerdecorator.h - projectutils.cpp - projectutils.h -) diff --git a/mvvm/model/mvvm/project/modelhaschangedcontroller.cpp b/mvvm/model/mvvm/project/modelhaschangedcontroller.cpp deleted file mode 100644 index 12642de6f14..00000000000 --- a/mvvm/model/mvvm/project/modelhaschangedcontroller.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/project/modelhaschangedcontroller.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/project/modelhaschangedcontroller.h" - -using namespace ModelView; - -//! Constructor of ModelHasChangedController. -//! Acccept 'model' to listen, and a 'callback' to report about changes in a model. - -ModelHasChangedController::ModelHasChangedController(SessionModel* model, callback_t callback) - : ModelListener(model), m_callback(callback) -{ - setOnDataChange([this](auto, auto) { process_change(); }); - setOnItemInserted([this](auto, auto) { process_change(); }); - setOnItemRemoved([this](auto, auto) { process_change(); }); - setOnModelReset([this](auto) { process_change(); }); -} - -//! Returns true if the model was changed since last call of resetChanged. - -bool ModelHasChangedController::hasChanged() const -{ - return m_has_changed; -} - -//! Reset has_changed flag. - -void ModelHasChangedController::resetChanged() -{ - m_has_changed = false; -} - -//! Sets 'has_changed' flag and reports back to client. - -void ModelHasChangedController::process_change() -{ - m_has_changed = true; - if (m_callback) - m_callback(); -} diff --git a/mvvm/model/mvvm/project/modelhaschangedcontroller.h b/mvvm/model/mvvm/project/modelhaschangedcontroller.h deleted file mode 100644 index f2793beed9e..00000000000 --- a/mvvm/model/mvvm/project/modelhaschangedcontroller.h +++ /dev/null @@ -1,44 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/project/modelhaschangedcontroller.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_PROJECT_MODELHASCHANGEDCONTROLLER_H -#define BORNAGAIN_MVVM_MODEL_MVVM_PROJECT_MODELHASCHANGEDCONTROLLER_H - -#include "mvvm/signals/modellistener.h" -#include <functional> - -namespace ModelView { - -//! Tracks changes in the model. -//! Allows to check if model has been changed (e.g. modified, inserted or removed items) since last -//! call of ::resetChanged(). - -class MVVM_MODEL_EXPORT ModelHasChangedController : public ModelListener<SessionModel> { -public: - using callback_t = std::function<void()>; - ModelHasChangedController(SessionModel* model, callback_t callback = {}); - - bool hasChanged() const; - - void resetChanged(); - -private: - void process_change(); - bool m_has_changed{false}; - callback_t m_callback; //! informs the user about change in the model -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_PROJECT_MODELHASCHANGEDCONTROLLER_H diff --git a/mvvm/model/mvvm/project/project.cpp b/mvvm/model/mvvm/project/project.cpp deleted file mode 100644 index 2dad145672d..00000000000 --- a/mvvm/model/mvvm/project/project.cpp +++ /dev/null @@ -1,86 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/project/project.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/project/project.h" -#include "mvvm/factories/modeldocumentfactory.h" -#include "mvvm/project/project_types.h" -#include "mvvm/project/projectchangecontroller.h" -#include "mvvm/project/projectutils.h" -#include "mvvm/utils/fileutils.h" -#include <functional> - -using namespace ModelView; - -struct Project::ProjectImpl { - std::string m_project_dir; - ProjectContext m_context; - ProjectChangedController m_change_controller; - - ProjectImpl(const ProjectContext& context) - : m_context(context) - , m_change_controller(context.m_models_callback(), context.m_modified_callback) - { - } - - //! Returns list of models which are subject to save/load. - std::vector<SessionModel*> models() const { return m_context.m_models_callback(); } - - //! Processes all models one by one and either save or load them to/from given directory. - //! Template parameter `method` specifies ModelDocumentInterface's method to use. - template <typename T> bool process(const std::string& dirname, T method) - { - if (!Utils::exists(dirname)) - return false; - - for (auto model : models()) { - auto document = CreateJsonDocument({model}); - auto filename = Utils::join(dirname, GUI::Project::Utils::SuggestFileName(*model)); - std::invoke(method, document, filename); - } - m_project_dir = dirname; - m_change_controller.resetChanged(); - return true; - } -}; - -Project::Project(const ProjectContext& context) : p_impl(std::make_unique<ProjectImpl>(context)) {} - -Project::~Project() = default; - -//! Returns the full path to a project directory. It is a name where the project has been last time -//! saved, or loaded from. - -std::string Project::projectDir() const -{ - return p_impl->m_project_dir; -} - -//! Saves all models to a given directory. Directory should exist. -//! Provided name will become 'projectDir'. - -bool Project::save(const std::string& dirname) const -{ - return p_impl->process(dirname, &ModelDocumentInterface::save); -} - -//! Loads all models from the given directory. -bool Project::load(const std::string& dirname) -{ - return p_impl->process(dirname, &ModelDocumentInterface::load); -} - -bool Project::isModified() const -{ - return p_impl->m_change_controller.hasChanged(); -} diff --git a/mvvm/model/mvvm/project/project.h b/mvvm/model/mvvm/project/project.h deleted file mode 100644 index fff9a3e34e5..00000000000 --- a/mvvm/model/mvvm/project/project.h +++ /dev/null @@ -1,48 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/project/project.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_PROJECT_PROJECT_H -#define BORNAGAIN_MVVM_MODEL_MVVM_PROJECT_PROJECT_H - -#include "mvvm/interfaces/projectinterface.h" -#include <memory> - -namespace ModelView { - -struct ProjectContext; - -//! Project represents content of all application models in a folder on disk. -//! Responsible for saving/loading application models to/from disk. - -class MVVM_MODEL_EXPORT Project : public ModelView::ProjectInterface { -public: - Project(const ProjectContext& context); - ~Project(); - - std::string projectDir() const override; - - bool save(const std::string& dirname) const override; - - bool load(const std::string& dirname) override; - - bool isModified() const override; - -private: - struct ProjectImpl; - std::unique_ptr<ProjectImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_PROJECT_PROJECT_H diff --git a/mvvm/model/mvvm/project/project_types.h b/mvvm/model/mvvm/project/project_types.h deleted file mode 100644 index 4f7f3bf0c53..00000000000 --- a/mvvm/model/mvvm/project/project_types.h +++ /dev/null @@ -1,65 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/project/project_types.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_PROJECT_PROJECT_TYPES_H -#define BORNAGAIN_MVVM_MODEL_MVVM_PROJECT_PROJECT_TYPES_H - -#include "mvvm/model_export.h" -#include <functional> -#include <string> -#include <vector> - -namespace ModelView { - -class SessionModel; - -//! Possible user answers on question "Project was modified". -enum class SaveChangesAnswer { SAVE = 0, DISCARD = 1, CANCEL = 2 }; - -//! Provides necessary information for Project construction. - -struct MVVM_MODEL_EXPORT ProjectContext { - //!< To notify about the change of the project with respect to what was written on disk. - using modified_callback_t = std::function<void()>; - - //! To ask for a vector of models to save/load to/from disk. - //! This is intentionally obtained via callback since save request might come after - //! the Project construction. - using models_callback_t = std::function<std::vector<SessionModel*>()>; - - modified_callback_t m_modified_callback; - models_callback_t m_models_callback; -}; - -//! Defines the context to interact with the user regarding save/save-as/create-new project -//! scenarious. - -struct MVVM_MODEL_EXPORT UserInteractionContext { - //!< To ask the user to select existing directory, returns full path to the directory. - using select_dir_callback_t = std::function<"">; - - //!< To ask the user to create a new directory, returns full path to the directory. - using create_dir_callback_t = std::function<"">; - - //!< To ask the user what to do with modified project. - using answer_callback_t = std::function<SaveChangesAnswer()>; - - select_dir_callback_t m_select_dir_callback; - create_dir_callback_t m_create_dir_callback; - answer_callback_t m_answer_callback; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_PROJECT_PROJECT_TYPES_H diff --git a/mvvm/model/mvvm/project/projectchangecontroller.cpp b/mvvm/model/mvvm/project/projectchangecontroller.cpp deleted file mode 100644 index 24fd2317d4d..00000000000 --- a/mvvm/model/mvvm/project/projectchangecontroller.cpp +++ /dev/null @@ -1,82 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/project/projectchangecontroller.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/project/projectchangecontroller.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/project/modelhaschangedcontroller.h" - -using namespace ModelView; - -struct ProjectChangedController::ProjectChangedControllerImpl { - std::vector<SessionModel*> m_models; - std::vector<std::unique_ptr<ModelHasChangedController>> change_controllers; - callback_t m_project_changed_callback; - bool m_project_has_changed{false}; - - ProjectChangedControllerImpl(const std::vector<SessionModel*>& models, callback_t callback) - : m_models(models), m_project_changed_callback(callback) - { - create_controllers(); - } - - void create_controllers() - { - auto on_model_changed = [this]() { onProjectHasChanged(); }; - change_controllers.clear(); - for (auto model : m_models) - change_controllers.emplace_back( - std::make_unique<ModelHasChangedController>(model, on_model_changed)); - } - - bool hasChanged() const { return m_project_has_changed; } - - void resetChanged() - { - for (auto& controller : change_controllers) - controller->resetChanged(); - m_project_has_changed = false; - } - - void onProjectHasChanged() - { - if (!m_project_has_changed) { - m_project_has_changed = true; - if (m_project_changed_callback) - m_project_changed_callback(); - } - } -}; - -ProjectChangedController::ProjectChangedController(const std::vector<SessionModel*>& models, - callback_t project_changed_callback) - : p_impl(std::make_unique<ProjectChangedControllerImpl>(models, project_changed_callback)) -{ -} - -ProjectChangedController::~ProjectChangedController() = default; - -//! Returns true if the change in the models has been registered since the last call of -//! resetChanged. - -bool ProjectChangedController::hasChanged() const -{ - return p_impl->hasChanged(); -} - -//! Reset controller to initial state, pretending that no changes has been registered. - -void ProjectChangedController::resetChanged() -{ - return p_impl->resetChanged(); -} diff --git a/mvvm/model/mvvm/project/projectchangecontroller.h b/mvvm/model/mvvm/project/projectchangecontroller.h deleted file mode 100644 index 1b380bfa034..00000000000 --- a/mvvm/model/mvvm/project/projectchangecontroller.h +++ /dev/null @@ -1,54 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/project/projectchangecontroller.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_PROJECT_PROJECTCHANGECONTROLLER_H -#define BORNAGAIN_MVVM_MODEL_MVVM_PROJECT_PROJECTCHANGECONTROLLER_H - -#include "mvvm/model_export.h" -#include <functional> -#include <memory> -#include <vector> - -namespace ModelView { - -class SessionModel; -class ModelHasChangedController; - -//! Tracks changes in all models. -//! Allows to check if one or more models have been changed since last call of ::resetChanged(). -//! This is intended to work together with the Project class. It will take care of calling -//! resetChanged after own saving. - -//! To avoid extra signaling while being in already "changed" mode, the controller reports only -//! once. - -class MVVM_MODEL_EXPORT ProjectChangedController { -public: - using callback_t = std::function<void()>; - ProjectChangedController(const std::vector<SessionModel*>& models, - callback_t project_changed_callback = {}); - ~ProjectChangedController(); - - bool hasChanged() const; - - void resetChanged(); - -private: - struct ProjectChangedControllerImpl; - std::unique_ptr<ProjectChangedControllerImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_PROJECT_PROJECTCHANGECONTROLLER_H diff --git a/mvvm/model/mvvm/project/projectmanager.cpp b/mvvm/model/mvvm/project/projectmanager.cpp deleted file mode 100644 index 5d564e23faa..00000000000 --- a/mvvm/model/mvvm/project/projectmanager.cpp +++ /dev/null @@ -1,132 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/project/projectmanager.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/project/projectmanager.h" -#include "mvvm/interfaces/projectinterface.h" -#include "mvvm/project/project_types.h" -#include "mvvm/project/projectutils.h" - -using namespace ModelView; - -namespace { -const bool succeeded = true; -const bool failed = false; -} // namespace - -struct ProjectManager::ProjectManagerImpl { - std::unique_ptr<ProjectInterface> m_current_project; - ProjectContext m_project_context; - - ProjectManagerImpl(ProjectContext context) : m_project_context(std::move(context)) - { - createNewProject(); - } - - //! Closes current project. Used in assumption that project was already saved. - void createNewProject() - { - m_current_project = GUI::Project::Utils::CreateUntitledProject(m_project_context); - } - - //! Returns true if the project has directory already defined. - bool projectHasDir() const { return !m_current_project->projectDir().empty(); } - - //! Saves project in project directory. If directory is not defined - bool saveCurrentProject() { return saveCurrentProjectAs(m_current_project->projectDir()); } - - //! Saves the project into a given directory. - bool saveCurrentProjectAs(const std::string& dirname) - { - return m_current_project->save(dirname); - } - - //! Loads the project from a given directory. - bool loadFrom(const std::string& dirname) { return m_current_project->load(dirname); } - - //! Returns true if project has been modified after the last save. - bool isModified() const { return m_current_project->isModified(); } -}; - -//! Constructor for ProjectManager. - -ProjectManager::ProjectManager(const ProjectContext& context) - : p_impl(std::make_unique<ProjectManagerImpl>(context)) -{ -} - -ProjectManager::~ProjectManager() = default; - -//! Creates a new project, returns 'true' in the case of success. -//! Current project has to be in a saved state, otherwise will return false. - -bool ProjectManager::createNewProject(const std::string& dirname) -{ - if (p_impl->isModified()) - return failed; - p_impl->createNewProject(); - return p_impl->saveCurrentProjectAs(dirname); -} - -//! Saves current project, returns 'true' in the case of success. -//! The project should have a project directory defined to succeed. - -bool ProjectManager::saveCurrentProject() -{ - if (!p_impl->projectHasDir()) - return failed; - return p_impl->saveCurrentProject(); -} - -//! Saves the project under a given directory, returns true in the case of success. -//! The directory should exist already. - -bool ProjectManager::saveProjectAs(const std::string& dirname) -{ - return p_impl->saveCurrentProjectAs(dirname); -} - -//! Opens existing project, returns 'true' in the case of success. -//! Current project should be in a saved state, new project should exist. - -bool ProjectManager::openExistingProject(const std::string& dirname) -{ - if (p_impl->isModified()) - return failed; - p_impl->createNewProject(); - return p_impl->loadFrom(dirname); -} - -//! Returns current project directory. - -std::string ProjectManager::currentProjectDir() const -{ - return p_impl->m_current_project ? p_impl->m_current_project->projectDir() : ""; -} - -//! Returns true if project was modified since last save. - -bool ProjectManager::isModified() const -{ - return p_impl->isModified(); -} - -//! Closes current project (without saving). -//! No checks whether it is modified or not being performed. - -bool ProjectManager::closeCurrentProject() const -{ - // no special operation is required to close the project - p_impl->createNewProject(); // ready for further actions - return succeeded; -} diff --git a/mvvm/model/mvvm/project/projectmanager.h b/mvvm/model/mvvm/project/projectmanager.h deleted file mode 100644 index 5e728132c5f..00000000000 --- a/mvvm/model/mvvm/project/projectmanager.h +++ /dev/null @@ -1,61 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/project/projectmanager.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_PROJECT_PROJECTMANAGER_H -#define BORNAGAIN_MVVM_MODEL_MVVM_PROJECT_PROJECTMANAGER_H - -#include "mvvm/interfaces/projectmanagerinterface.h" -#include <memory> - -namespace ModelView { - -struct ProjectContext; - -//! Responsible for handling new/save/save-as/close Project logic, where the Project represents -//! a collection of serialized application models in the project directory. - -//! This ProjectManager requires certain prerequisites to function properly: for example, -//! the creation of a new project will be possible only if the old project is in a saved state. See -//! description to the class methods. - -class MVVM_MODEL_EXPORT ProjectManager : public ModelView::ProjectManagerInterface { -public: - ProjectManager(const ProjectContext& context); - ~ProjectManager() override; - - ProjectManager(const ProjectManager& other) = delete; - ProjectManager& operator=(const ProjectManager& other) = delete; - - bool createNewProject(const std::string& dirname) override; - - bool saveCurrentProject() override; - - bool saveProjectAs(const std::string& dirname) override; - - bool openExistingProject(const std::string& dirname) override; - - std::string currentProjectDir() const override; - - bool isModified() const override; - - bool closeCurrentProject() const override; - -private: - struct ProjectManagerImpl; - std::unique_ptr<ProjectManagerImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_PROJECT_PROJECTMANAGER_H diff --git a/mvvm/model/mvvm/project/projectmanagerdecorator.cpp b/mvvm/model/mvvm/project/projectmanagerdecorator.cpp deleted file mode 100644 index b40d2986abb..00000000000 --- a/mvvm/model/mvvm/project/projectmanagerdecorator.cpp +++ /dev/null @@ -1,192 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/project/projectmanagerdecorator.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/project/projectmanagerdecorator.h" -#include "mvvm/interfaces/projectinterface.h" -#include "mvvm/project/project_types.h" -#include "mvvm/project/projectmanager.h" -#include <stdexcept> - -using namespace ModelView; - -namespace { -const bool succeeded = true; -const bool failed = false; -} // namespace - -struct ProjectManagerDecorator::ProjectManagerImpl { - ProjectContext m_project_context; - UserInteractionContext m_user_context; - std::unique_ptr<ProjectManager> project_manager; - - ProjectManagerImpl(ProjectContext project_context, UserInteractionContext user_context) - : m_project_context(std::move(project_context)), m_user_context(std::move(user_context)) - { - project_manager = std::make_unique<ProjectManager>(m_project_context); - } - - //! Returns true if the project has directory already defined. - bool projectHasDir() const { return !project_manager->currentProjectDir().empty(); } - - //! Saves project in project directory. If directory is not defined, will acquire - //! directory susing callback provided. - bool saveCurrentProject() - { - // Feature FIXME?: already saved project (i.e. isModified=false) will be saved again. - // Files will be same, but creation date will be changed. - - auto save_dir = - projectHasDir() ? project_manager->currentProjectDir() : acquireNewProjectDir(); - return saveCurrentProjectAs(save_dir); - } - - //! Saves current project under directory selected. - bool saveCurrentProjectAs(const std::string& dirname) - { - // empty dirname varible means 'cancel' during directory selection - return dirname.empty() ? failed : project_manager->saveProjectAs(dirname); - } - - std::string currentProjectDir() const { return project_manager->currentProjectDir(); } - - bool isModified() const { return project_manager->isModified(); } - - //! Performs saving of previous project before creating a new one. - bool saveBeforeClosing() - { - if (isModified()) { - switch (acquireSaveChangesAnswer()) { - case SaveChangesAnswer::SAVE: - return saveCurrentProject(); - case SaveChangesAnswer::CANCEL: - return failed; // saving was interrupted by the 'cancel' button - case SaveChangesAnswer::DISCARD: - project_manager->closeCurrentProject(); - return succeeded; - default: - throw std::runtime_error("Error in ProjectManager: unexpected answer."); - } - } - return succeeded; - } - - //! Asks the user whether to save/cancel/discard the project using callback provided. - SaveChangesAnswer acquireSaveChangesAnswer() const - { - if (!m_user_context.m_answer_callback) - throw std::runtime_error("Error in ProjectManager: absent save_callback"); - return m_user_context.m_answer_callback(); - } - - //! Acquire the name of the new project directory using callback provided. - std::string acquireNewProjectDir() - { - if (!m_user_context.m_create_dir_callback) - throw std::runtime_error("Error in ProjectManager: absent creat_dir callback."); - return m_user_context.m_create_dir_callback(); - } - - //! Acquire the name of the existing project directory using callback provided. - std::string acquireExistingProjectDir() - { - if (!m_user_context.m_select_dir_callback) - throw std::runtime_error("Error in ProjectManager: absent open_dir callback."); - return m_user_context.m_select_dir_callback(); - } -}; - -//! Constructor for ProjectManagerDecorator. - -ProjectManagerDecorator::ProjectManagerDecorator(const ProjectContext& project_context, - const UserInteractionContext& user_context) - : p_impl(std::make_unique<ProjectManagerImpl>(project_context, user_context)) -{ -} - -ProjectManagerDecorator::~ProjectManagerDecorator() = default; - -//! Creates a new project in the directory 'dirname', returns 'true' in the case of success. -//! The directory should exist. -//! If provided name is empty, will call directory selector dialog using callback provided. -//! If current project is in unsaved state, will perform 'save-before-closing' procedure before -//! proceeding further. - -bool ProjectManagerDecorator::createNewProject(const std::string& dirname) -{ - if (!p_impl->saveBeforeClosing()) - return failed; - - auto project_dir = dirname.empty() ? p_impl->acquireNewProjectDir() : dirname; - // empty project_dir string denotes 'cancel' during directory creation dialog - return project_dir.empty() ? failed : p_impl->project_manager->createNewProject(project_dir); -} - -//! Saves current project, returns 'true' in the case of success. -//! The project should have a project directory defined, if it is not the case, it will -//! launch the procedure of directory selection using callback provided. - -bool ProjectManagerDecorator::saveCurrentProject() -{ - return p_impl->saveCurrentProject(); -} - -//! Saves the project under a given directory, returns true in the case of success. -//! The directory should exist already. If provided 'dirname' variable is empty, -//! it will acquire a new project directory using dialog provided. - -bool ProjectManagerDecorator::saveProjectAs(const std::string& dirname) -{ - auto project_dir = dirname.empty() ? p_impl->acquireNewProjectDir() : dirname; - // empty project_dir variable denotes 'cancel' during directory creation dialog - return project_dir.empty() ? failed : p_impl->saveCurrentProjectAs(project_dir); -} - -//! Opens existing project, returns 'true' in the case of success. -//! If provided name is empty, will call directory selector dialog using callback provided. -//! If current project is in unsaved state, it will perform 'save-before-closing' procedure before -//! proceeding further. - -bool ProjectManagerDecorator::openExistingProject(const std::string& dirname) -{ - if (!p_impl->saveBeforeClosing()) - return failed; - auto project_dir = dirname.empty() ? p_impl->acquireExistingProjectDir() : dirname; - // empty project_dir variable denotes 'cancel' during directory selection dialog - return project_dir.empty() ? failed : p_impl->project_manager->openExistingProject(project_dir); -} - -//! Returns current project directory. - -std::string ProjectManagerDecorator::currentProjectDir() const -{ - return p_impl->currentProjectDir(); -} - -//! Returns true if project was modified since last save. - -bool ProjectManagerDecorator::isModified() const -{ - return p_impl->isModified(); -} - -//! Closes current project, returns 'true' if succeeded. -//! Will show the dialog, via callback provided, asking the user whether to save/discard/cancel. -//! Returns 'false' only if user has selected 'cancel' button. - -bool ProjectManagerDecorator::closeCurrentProject() const -{ - if (!p_impl->saveBeforeClosing()) - return failed; - return succeeded; -} diff --git a/mvvm/model/mvvm/project/projectmanagerdecorator.h b/mvvm/model/mvvm/project/projectmanagerdecorator.h deleted file mode 100644 index f759aff59ea..00000000000 --- a/mvvm/model/mvvm/project/projectmanagerdecorator.h +++ /dev/null @@ -1,62 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/project/projectmanagerdecorator.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_PROJECT_PROJECTMANAGERDECORATOR_H -#define BORNAGAIN_MVVM_MODEL_MVVM_PROJECT_PROJECTMANAGERDECORATOR_H - -#include "mvvm/interfaces/projectmanagerinterface.h" -#include <memory> - -namespace ModelView { - -struct ProjectContext; -struct UserInteractionContext; - -//! Decorator for ProjectManager to provide interaction with the user on open/save-as requests. -//! It relies on the same interface and adds additional logic related to "unsaved" data. - -//! For example, on createNewProject it will check if previous project is saved, and will -//! call external dialog save/discard/cancel via provided callback. - -class MVVM_MODEL_EXPORT ProjectManagerDecorator : public ProjectManagerInterface { -public: - ProjectManagerDecorator(const ProjectContext& project_context, - const UserInteractionContext& user_context); - - ~ProjectManagerDecorator() override; - ProjectManagerDecorator(const ProjectManagerDecorator& other) = delete; - ProjectManagerDecorator& operator=(const ProjectManagerDecorator& other) = delete; - - bool createNewProject(const std::string& dirname = {}) override; - - bool saveCurrentProject() override; - - bool saveProjectAs(const std::string& dirname = {}) override; - - bool openExistingProject(const std::string& dirname = {}) override; - - std::string currentProjectDir() const override; - - bool isModified() const override; - - bool closeCurrentProject() const override; - -private: - struct ProjectManagerImpl; - std::unique_ptr<ProjectManagerImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_PROJECT_PROJECTMANAGERDECORATOR_H diff --git a/mvvm/model/mvvm/project/projectutils.cpp b/mvvm/model/mvvm/project/projectutils.cpp deleted file mode 100644 index 72a0cac24cf..00000000000 --- a/mvvm/model/mvvm/project/projectutils.cpp +++ /dev/null @@ -1,76 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/project/projectutils.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/project/projectutils.h" -#include "mvvm/interfaces/applicationmodelsinterface.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/project/project.h" -#include "mvvm/project/project_types.h" -#include "mvvm/utils/fileutils.h" -#include <cctype> - -namespace { -const std::string json_extention = ".json"; -const std::string untitled_name = "Untitled"; -} // namespace - -namespace ModelView { - -//! Suggests file name which can be used to store json content of given model. -//! Uses the model type to construct a filename: MaterialModel -> materialmodel.json - -std::string GUI::Project::Utils::SuggestFileName(const SessionModel& model) -{ - std::string result = model.modelType(); - std::transform(result.begin(), result.end(), result.begin(), ::tolower); - return result + json_extention; -} - -//! Returns 'true' if given directory might be a project directory. -//! This simplified check counts number of files with json extention. - -bool GUI::Project::Utils::IsPossibleProjectDir(const std::string& project_dir) -{ - return !Utils::FindFiles(project_dir, json_extention).empty(); -} - -//! Creates new untitled project. - -std::unique_ptr<ProjectInterface> -GUI::Project::Utils::CreateUntitledProject(const ProjectContext& context) -{ - return std::make_unique<Project>(context); -} - -//! Returns a MainWindow title for given project. - -std::string GUI::Project::Utils::ProjectWindowTitle(const ProjectInterface& project) -{ - return ProjectWindowTitle(project.projectDir(), project.isModified()); -} - -//! Returns a title composed from last part of project path, and `is_modified` flag. -//! Project without projectDir will be "Untitled", modified project will be "*Untitled". -//! Project with projectDir in "/home/user/project1" will get title "project1". - -std::string GUI::Project::Utils::ProjectWindowTitle(const std::string& project_dir, - bool is_modified) -{ - auto pos = project_dir.find_last_of('/'); - auto project_name = (pos == std::string::npos ? untitled_name : project_dir.substr(pos + 1)); - auto unsaved_status = is_modified ? "*" : ""; - return unsaved_status + project_name; -} - -} // namespace ModelView diff --git a/mvvm/model/mvvm/project/projectutils.h b/mvvm/model/mvvm/project/projectutils.h deleted file mode 100644 index cfb6edafd6b..00000000000 --- a/mvvm/model/mvvm/project/projectutils.h +++ /dev/null @@ -1,48 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/project/projectutils.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_PROJECT_PROJECTUTILS_H -#define BORNAGAIN_MVVM_MODEL_MVVM_PROJECT_PROJECTUTILS_H - -#include "mvvm/model_export.h" -#include <memory> -#include <string> -#include <vector> - -namespace ModelView { - -class SessionModel; -class ProjectInterface; -struct ProjectContext; - -//! Collection of utility functions to handle project saving and loading. - -namespace GUI::Project::Utils { - -MVVM_MODEL_EXPORT std::string SuggestFileName(const SessionModel& model); - -MVVM_MODEL_EXPORT bool IsPossibleProjectDir(const std::string& project_dir); - -MVVM_MODEL_EXPORT std::unique_ptr<ProjectInterface> -CreateUntitledProject(const ProjectContext& context); - -MVVM_MODEL_EXPORT std::string ProjectWindowTitle(const ProjectInterface& project); - -MVVM_MODEL_EXPORT std::string ProjectWindowTitle(const std::string& project_dir, bool is_modified); - -} // namespace GUI::Project::Utils - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_PROJECT_PROJECTUTILS_H diff --git a/mvvm/model/mvvm/serialization/CMakeLists.txt b/mvvm/model/mvvm/serialization/CMakeLists.txt deleted file mode 100644 index 64a9d95b90b..00000000000 --- a/mvvm/model/mvvm/serialization/CMakeLists.txt +++ /dev/null @@ -1,35 +0,0 @@ -target_sources(${library_name} PRIVATE - compatibilityutils.cpp - compatibilityutils.h - jsonconverterinterfaces.h - jsondocument.cpp - jsondocument.h - jsonitem_types.h - jsonitembackupstrategy.cpp - jsonitembackupstrategy.h - jsonitemcontainerconverter.cpp - jsonitemcontainerconverter.h - jsonitemconverter.cpp - jsonitemconverter.h - jsonitemconverterinterface.h - jsonitemcopystrategy.cpp - jsonitemcopystrategy.h - jsonitemdataconverter.cpp - jsonitemdataconverter.h - jsonitemdataconverterinterface.h - jsonitemformatassistant.cpp - jsonitemformatassistant.h - jsonitemtagsconverter.cpp - jsonitemtagsconverter.h - jsonmodelconverter.cpp - jsonmodelconverter.h - jsonmodelconverterinterface.h - jsontaginfoconverter.cpp - jsontaginfoconverter.h - jsontaginfoconverterinterface.h - jsonutils.cpp - jsonutils.h - jsonvariantconverter.cpp - jsonvariantconverter.h - jsonvariantconverterinterface.h -) diff --git a/mvvm/model/mvvm/serialization/compatibilityutils.cpp b/mvvm/model/mvvm/serialization/compatibilityutils.cpp deleted file mode 100644 index eb276955fe8..00000000000 --- a/mvvm/model/mvvm/serialization/compatibilityutils.cpp +++ /dev/null @@ -1,79 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/serialization/compatibilityutils.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/serialization/compatibilityutils.h" -#include "mvvm/model/groupitem.h" -#include "mvvm/model/sessionitemcontainer.h" -#include "mvvm/model/sessionitemdata.h" -#include <set> - -namespace ModelView ::Compatibility { - -/* -Returns `true` if given TagInfo is compatible with given container. -Here, `container` is what exists at runtime, `taginfo` has been obtained from the serialization. -Container is considered to be compatible (i.e. can be updated from serialized content), if it has -exactly same tag, and it is empty. -*/ - -bool IsCompatibleUniversalTag(const SessionItemContainer& container, const TagInfo& taginfo) -{ - auto container_taginfo = container.tagInfo(); - - bool is_empty = container.empty(); - bool both_are_universal = - !container_taginfo.isSinglePropertyTag() && !taginfo.isSinglePropertyTag(); - bool same_tags = container_taginfo == taginfo; - - return both_are_universal && same_tags && is_empty; -} - -/* -Returns `true` if given TagInfo is a single property tag which is compatible with given container. -Here, `container` is what exists at runtime, `taginfo` has been obtained from the serialization. -Container is considered to be compatible (i.e. can be updated from serialized content), if it has -exactly same tag, and property item ready for update. -*/ - -bool IsCompatibleSinglePropertyTag(const SessionItemContainer& container, const TagInfo& taginfo) -{ - auto container_taginfo = container.tagInfo(); - - bool has_item = !container.empty(); - bool both_are_properties = - container_taginfo.isSinglePropertyTag() && taginfo.isSinglePropertyTag(); - bool same_tags = container_taginfo == taginfo; - - return both_are_properties && same_tags && has_item; -} - -/* -Returns `true` if given TagInfo is compatible with given container. -Here, `container` is what exists at runtime, `taginfo` has been obtained from the serialization. -Container is considered to be compatible (i.e. can be updated from serialized content), -if it has exactly same tag, and it's name corresponds to GroupItem. -*/ - -bool IsCompatibleGroupTag(const SessionItemContainer& container, const TagInfo& taginfo) -{ - auto container_taginfo = container.tagInfo(); - bool has_item = !container.empty(); - bool same_tags = container_taginfo == taginfo; - bool both_are_universal = - !container_taginfo.isSinglePropertyTag() && !taginfo.isSinglePropertyTag(); - bool valid_tag_name = taginfo.name() == GroupItem::T_GROUP_ITEMS; - return both_are_universal && same_tags && has_item && valid_tag_name; -} - -} // namespace ModelView::Compatibility diff --git a/mvvm/model/mvvm/serialization/compatibilityutils.h b/mvvm/model/mvvm/serialization/compatibilityutils.h deleted file mode 100644 index 0810cb196a1..00000000000 --- a/mvvm/model/mvvm/serialization/compatibilityutils.h +++ /dev/null @@ -1,54 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/serialization/compatibilityutils.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_COMPATIBILITYUTILS_H -#define BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_COMPATIBILITYUTILS_H - -//! @file mvvm/model/mvvm/serialization/compatibilityutils.h -//! @brief Place for utils to fix back compatibility of serialized projects. - -#include "mvvm/model_export.h" -#include <memory> - -namespace ModelView { - -class SessionItemData; -class SessionItemContainer; -class TagInfo; - -namespace Compatibility { - -//! Returns `true` if given TagInfo is compatible with given container. -//! See explanations in the code. - -MVVM_MODEL_EXPORT -bool IsCompatibleUniversalTag(const SessionItemContainer& container, const TagInfo& taginfo); - -//! Returns `true` if given TagInfo is a single property tag which is compatible with given -//! container. See more explanations in the code. - -MVVM_MODEL_EXPORT -bool IsCompatibleSinglePropertyTag(const SessionItemContainer& container, const TagInfo& taginfo); - -//! Returns `true` if given TagInfo is a tag from GroupItem which is compatible with given -//! container. See more explanations in the code. - -MVVM_MODEL_EXPORT bool IsCompatibleGroupTag(const SessionItemContainer& container, - const TagInfo& taginfo); - -} // namespace Compatibility - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_COMPATIBILITYUTILS_H diff --git a/mvvm/model/mvvm/serialization/jsonconverterinterfaces.h b/mvvm/model/mvvm/serialization/jsonconverterinterfaces.h deleted file mode 100644 index 2c6f4dd2a74..00000000000 --- a/mvvm/model/mvvm/serialization/jsonconverterinterfaces.h +++ /dev/null @@ -1,24 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/serialization/jsonconverterinterfaces.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONCONVERTERINTERFACES_H -#define BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONCONVERTERINTERFACES_H - -#include "mvvm/serialization/jsonitemconverterinterface.h" -#include "mvvm/serialization/jsonitemdataconverterinterface.h" -#include "mvvm/serialization/jsonmodelconverterinterface.h" -#include "mvvm/serialization/jsontaginfoconverterinterface.h" -#include "mvvm/serialization/jsonvariantconverterinterface.h" - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONCONVERTERINTERFACES_H diff --git a/mvvm/model/mvvm/serialization/jsondocument.cpp b/mvvm/model/mvvm/serialization/jsondocument.cpp deleted file mode 100644 index 4974dc84548..00000000000 --- a/mvvm/model/mvvm/serialization/jsondocument.cpp +++ /dev/null @@ -1,84 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/serialization/jsondocument.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/serialization/jsondocument.h" -#include "mvvm/factories/modelconverterfactory.h" -#include "mvvm/model/sessionmodel.h" -#include <QFile> -#include <QJsonArray> -#include <QJsonDocument> -#include <QJsonObject> -#include <sstream> -#include <stdexcept> - -using namespace ModelView; - -struct JsonDocument::JsonDocumentImpl { - std::vector<SessionModel*> models; - JsonDocumentImpl(const std::vector<SessionModel*>& models) : models(models) {} -}; - -JsonDocument::JsonDocument(const std::vector<SessionModel*>& models) - : p_impl(std::make_unique<JsonDocumentImpl>(models)) -{ -} - -//! Saves models on disk. -void JsonDocument::save(const std::string& file_name) const -{ - auto converter = ModelView::CreateModelProjectConverter(); - QJsonArray array; - - for (auto model : p_impl->models) - array.push_back(converter->to_json(*model)); - - QJsonDocument document(array); - QFile file(QString::fromStdString(file_name)); - - if (!file.open(QIODevice::WriteOnly)) - throw std::runtime_error("Error in JsonDocument: can't save the file '" + file_name + "'"); - - file.write(document.toJson()); - - file.close(); -} - -//! Loads models from disk. If models have some data already, it will be rewritten. - -void JsonDocument::load(const std::string& file_name) -{ - QFile file(QString::fromStdString(file_name)); - if (!file.open(QIODevice::ReadOnly)) - throw std::runtime_error("Error in JsonDocument: can't read the file '" + file_name + "'"); - - auto document = QJsonDocument::fromJson(file.readAll()); - auto array = document.array(); - if (array.size() != static_cast<int>(p_impl->models.size())) { - std::ostringstream ostr; - ostr << "Error in JsonDocument: number of application models " << p_impl->models.size() - << " and number of json models " << array.size() << " doesn't match"; - throw std::runtime_error(ostr.str()); - } - - auto converter = ModelView::CreateModelProjectConverter(); - int index(0); - for (auto model : p_impl->models) { - converter->from_json(array.at(index).toObject(), *model); - ++index; - } - - file.close(); -} - -JsonDocument::~JsonDocument() = default; diff --git a/mvvm/model/mvvm/serialization/jsondocument.h b/mvvm/model/mvvm/serialization/jsondocument.h deleted file mode 100644 index bbee9147ce4..00000000000 --- a/mvvm/model/mvvm/serialization/jsondocument.h +++ /dev/null @@ -1,44 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/serialization/jsondocument.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONDOCUMENT_H -#define BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONDOCUMENT_H - -#include "mvvm/interfaces/modeldocumentinterface.h" -#include <memory> -#include <vector> - -namespace ModelView { - -class SessionModel; - -//! Saves and restores list of SessionModel's to/from disk using json format. -//! Single JsonDocument corresponds to a single file on disk. - -class MVVM_MODEL_EXPORT JsonDocument : public ModelDocumentInterface { -public: - JsonDocument(const std::vector<SessionModel*>& models); - ~JsonDocument() override; - - void save(const std::string& file_name) const override; - void load(const std::string& file_name) override; - -private: - struct JsonDocumentImpl; - std::unique_ptr<JsonDocumentImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONDOCUMENT_H diff --git a/mvvm/model/mvvm/serialization/jsonitem_types.h b/mvvm/model/mvvm/serialization/jsonitem_types.h deleted file mode 100644 index fdb35ccb1d9..00000000000 --- a/mvvm/model/mvvm/serialization/jsonitem_types.h +++ /dev/null @@ -1,76 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/serialization/jsonitem_types.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONITEM_TYPES_H -#define BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONITEM_TYPES_H - -//! @file mvvm/model/mvvm/serialization/jsonitem_types.h -//! Collection of custom types involved into SessionItem and JSON mutual convertion. - -#include "mvvm/model_export.h" -#include <functional> -#include <memory> -#include <vector> - -class QJsonObject; - -namespace ModelView { - -class SessionItem; -class ItemFactoryInterface; - -//! Provides necessary callbacks to convert SessionItem to JSON and back. - -struct MVVM_MODEL_EXPORT ConverterCallbacks { - using create_json_t = std::function<QJsonObject(const SessionItem&)>; - using create_item_t = std::function<std::unique_ptr<SessionItem>(const QJsonObject&)>; - using update_item_t = std::function<void(const QJsonObject&, SessionItem*)>; - - create_json_t m_create_json; //! creates JSON object from session item - create_item_t m_create_item; //! creates new SessionItem from JSON object - update_item_t m_update_item; //! updates existing SessionItem from JSON object -}; - -//! Flags to define converter behavior on the way from SessionItem to JSON and back. - -enum class ConverterMode { - none, //!< undefined converter mode - clone, //!< full deep copying with item identifiers preserved - copy, //!< full deep copying with item identifiers regenerated - project //!< selective copying for saving/loading the project (tags and data created by item, - //!< updated from JSON) -}; - -//! Returns true if given mode requires ID regeneration instead of using the one stored in JSON. -inline bool isRegenerateIdWhenBackFromJson(ConverterMode mode) -{ - return mode == ConverterMode::copy; -} - -//! Returns true if item content should be reconstructed from JSON -inline bool isRebuildItemDataAndTagFromJson(ConverterMode mode) -{ - return mode != ConverterMode::project; -} - -//! Collection of input paramters for SessionItemConverter - -struct MVVM_MODEL_EXPORT ConverterContext { - const ItemFactoryInterface* m_factory{nullptr}; - ConverterMode m_mode = ConverterMode::none; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONITEM_TYPES_H diff --git a/mvvm/model/mvvm/serialization/jsonitembackupstrategy.cpp b/mvvm/model/mvvm/serialization/jsonitembackupstrategy.cpp deleted file mode 100644 index 035455fe54e..00000000000 --- a/mvvm/model/mvvm/serialization/jsonitembackupstrategy.cpp +++ /dev/null @@ -1,43 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/serialization/jsonitembackupstrategy.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/serialization/jsonitembackupstrategy.h" -#include "mvvm/factories/itemconverterfactory.h" -#include "mvvm/model/sessionitem.h" -#include <QJsonObject> - -using namespace ModelView; - -struct JsonItemBackupStrategy::JsonItemBackupStrategyImpl { - std::unique_ptr<JsonItemConverterInterface> m_converter; - QJsonObject m_json; -}; - -JsonItemBackupStrategy::JsonItemBackupStrategy(const ItemFactoryInterface* item_factory) - : p_impl(std::make_unique<JsonItemBackupStrategyImpl>()) -{ - p_impl->m_converter = CreateItemCloneConverter(item_factory); -} - -JsonItemBackupStrategy::~JsonItemBackupStrategy() = default; - -std::unique_ptr<SessionItem> JsonItemBackupStrategy::restoreItem() const -{ - return p_impl->m_converter->from_json(p_impl->m_json); -} - -void JsonItemBackupStrategy::saveItem(const SessionItem* item) -{ - p_impl->m_json = p_impl->m_converter->to_json(item); -} diff --git a/mvvm/model/mvvm/serialization/jsonitembackupstrategy.h b/mvvm/model/mvvm/serialization/jsonitembackupstrategy.h deleted file mode 100644 index d407da0fdce..00000000000 --- a/mvvm/model/mvvm/serialization/jsonitembackupstrategy.h +++ /dev/null @@ -1,44 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/serialization/jsonitembackupstrategy.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONITEMBACKUPSTRATEGY_H -#define BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONITEMBACKUPSTRATEGY_H - -#include "mvvm/interfaces/itembackupstrategy.h" -#include <memory> - -namespace ModelView { - -class SessionItem; -class ItemFactoryInterface; - -//! Provide backup of SessionItem using json strategy. - -class MVVM_MODEL_EXPORT JsonItemBackupStrategy : public ItemBackupStrategy { -public: - JsonItemBackupStrategy(const ItemFactoryInterface* item_factory); - ~JsonItemBackupStrategy() override; - - std::unique_ptr<SessionItem> restoreItem() const override; - - void saveItem(const SessionItem* item) override; - -private: - struct JsonItemBackupStrategyImpl; - std::unique_ptr<JsonItemBackupStrategyImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONITEMBACKUPSTRATEGY_H diff --git a/mvvm/model/mvvm/serialization/jsonitemcontainerconverter.cpp b/mvvm/model/mvvm/serialization/jsonitemcontainerconverter.cpp deleted file mode 100644 index bc364dff83b..00000000000 --- a/mvvm/model/mvvm/serialization/jsonitemcontainerconverter.cpp +++ /dev/null @@ -1,155 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/serialization/jsonitemcontainerconverter.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/serialization/jsonitemcontainerconverter.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionitemcontainer.h" -#include "mvvm/model/sessionitemtags.h" -#include "mvvm/model/tagrow.h" -#include "mvvm/serialization/compatibilityutils.h" -#include "mvvm/serialization/jsonitem_types.h" -#include "mvvm/serialization/jsonitemformatassistant.h" -#include "mvvm/serialization/jsontaginfoconverter.h" -#include <QJsonArray> -#include <QJsonObject> - -using namespace ModelView; - -struct JsonItemContainerConverter::JsonItemContainerConverterImpl { - std::unique_ptr<JsonTagInfoConverterInterface> m_taginfo_converter; - ConverterCallbacks m_converter_callbacks; - - JsonItemContainerConverterImpl(ConverterCallbacks callbacks = {}) - : m_converter_callbacks(std::move(callbacks)) - { - m_taginfo_converter = std::make_unique<JsonTagInfoConverter>(); - } - - QJsonObject create_json(const SessionItem& item) - { - return m_converter_callbacks.m_create_json ? m_converter_callbacks.m_create_json(item) - : QJsonObject(); - } - - std::unique_ptr<SessionItem> create_item(const QJsonObject& json) - { - return m_converter_callbacks.m_create_item ? m_converter_callbacks.m_create_item(json) - : std::unique_ptr<SessionItem>(); - } - - void update_item(const QJsonObject& json, SessionItem* item) - { - if (m_converter_callbacks.m_update_item) - m_converter_callbacks.m_update_item(json, item); - } - - //! Update container from json content. Number of existing container items should match size - //! of json array. - void update_items(const QJsonObject& json, SessionItemContainer& container) - { - auto array = json[JsonItemFormatAssistant::itemsKey].toArray(); - if (array.size() != container.itemCount()) - throw std::runtime_error("Error in JsonItemContainerConverter: size is different"); - int index{0}; - for (const auto obj : array) - update_item(obj.toObject(), container.itemAt(index++)); - } - - void create_items(const QJsonObject& json, SessionItemContainer& container) - { - for (const auto obj : json[JsonItemFormatAssistant::itemsKey].toArray()) { - if (auto item = create_item(obj.toObject()); item) - container.insertItem(item.release(), container.itemCount()); - } - } - - //! Populates container with content reconstructed from JSON object. Container must be empty. - - void populate_container(const QJsonObject& json, SessionItemContainer& container) - { - if (!container.empty()) - throw std::runtime_error( - "Error in JsonItemContainerConverter: container is not empty."); - - create_items(json, container); - } - - //! Update container with content reconstructed from JSON object. - //! It is assumed, that container has some items already created. - - void update_container(const QJsonObject& json, SessionItemContainer& container) - { - TagInfo tagInfo = - m_taginfo_converter->from_json(json[JsonItemFormatAssistant::tagInfoKey].toObject()); - - if (Compatibility::IsCompatibleSinglePropertyTag(container, tagInfo)) - update_items(json, container); - - else if (Compatibility::IsCompatibleGroupTag(container, tagInfo)) - update_items(json, container); - - else if (Compatibility::IsCompatibleUniversalTag(container, tagInfo)) - create_items(json, container); - - else - throw std::runtime_error("Error in JsonItemContainerConverter: can't convert json"); - } -}; - -JsonItemContainerConverter::JsonItemContainerConverter(ConverterCallbacks callbacks) - : p_impl(std::make_unique<JsonItemContainerConverterImpl>(std::move(callbacks))) -{ -} - -JsonItemContainerConverter::~JsonItemContainerConverter() = default; - -QJsonObject JsonItemContainerConverter::to_json(const SessionItemContainer& container) -{ - QJsonObject result; - result[JsonItemFormatAssistant::tagInfoKey] = - p_impl->m_taginfo_converter->to_json(container.tagInfo()); - - QJsonArray itemArray; - for (auto item : container) - itemArray.append(p_impl->create_json(*item)); - result[JsonItemFormatAssistant::itemsKey] = itemArray; - - return result; -} - -//! Reconstructs SessionItemContainer from the content of JSON object. Can work in two modes: -//! + If SessionItemContainer is empty, the content will be reconstructed from JSON -//! + If SessionItemContainer contains some items already, they will be populated from JSON. -//! Second mode is used when loading project from disk to allow back compatibility. - -void JsonItemContainerConverter::from_json(const QJsonObject& json, SessionItemContainer& container) -{ - static JsonItemFormatAssistant assistant; - - if (!assistant.isSessionItemContainer(json)) - throw std::runtime_error("Error in JsonItemContainerConverter: given JSON can't represent " - "SessionItemContainer."); - - TagInfo tagInfo = p_impl->m_taginfo_converter->from_json( - json[JsonItemFormatAssistant::tagInfoKey].toObject()); - - if (tagInfo.name() != container.tagInfo().name()) - throw std::runtime_error("Error in JsonItemContainerConverter: attempt to update " - "container from JSON representing another container."); - - if (container.empty()) - p_impl->populate_container(json, container); - else - p_impl->update_container(json, container); -} diff --git a/mvvm/model/mvvm/serialization/jsonitemcontainerconverter.h b/mvvm/model/mvvm/serialization/jsonitemcontainerconverter.h deleted file mode 100644 index 01448965671..00000000000 --- a/mvvm/model/mvvm/serialization/jsonitemcontainerconverter.h +++ /dev/null @@ -1,48 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/serialization/jsonitemcontainerconverter.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONITEMCONTAINERCONVERTER_H -#define BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONITEMCONTAINERCONVERTER_H - -#include "mvvm/model_export.h" -#include <functional> -#include <memory> - -class QJsonObject; - -namespace ModelView { - -class SessionItem; -class SessionItemContainer; -struct ConverterCallbacks; - -//! Converter between SessionItemContainer and JSON object. - -class MVVM_MODEL_EXPORT JsonItemContainerConverter { -public: - JsonItemContainerConverter(ConverterCallbacks callbacks); - ~JsonItemContainerConverter(); - - QJsonObject to_json(const SessionItemContainer& container); - - void from_json(const QJsonObject& json, SessionItemContainer& container); - -private: - struct JsonItemContainerConverterImpl; - std::unique_ptr<JsonItemContainerConverterImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONITEMCONTAINERCONVERTER_H diff --git a/mvvm/model/mvvm/serialization/jsonitemconverter.cpp b/mvvm/model/mvvm/serialization/jsonitemconverter.cpp deleted file mode 100644 index 3e8e9164d5d..00000000000 --- a/mvvm/model/mvvm/serialization/jsonitemconverter.cpp +++ /dev/null @@ -1,141 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/serialization/jsonitemconverter.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/serialization/jsonitemconverter.h" -#include "mvvm/core/uniqueidgenerator.h" -#include "mvvm/interfaces/itemfactoryinterface.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionitemdata.h" -#include "mvvm/model/sessionitemtags.h" -#include "mvvm/serialization/jsonitem_types.h" -#include "mvvm/serialization/jsonitemdataconverter.h" -#include "mvvm/serialization/jsonitemformatassistant.h" -#include "mvvm/serialization/jsonitemtagsconverter.h" -#include <QJsonArray> -#include <QJsonObject> - -using namespace ModelView; - -namespace { - -//! Creates converter for SessionItemData/JSON. - -std::unique_ptr<JsonItemDataConverterInterface> createDataConverter(const ConverterMode& mode) -{ - return mode == ConverterMode::project ? JsonItemDataConverter::createProjectConverter() - : JsonItemDataConverter::createCopyConverter(); -} - -} // namespace - -struct JsonItemConverter::JsonItemConverterImpl { - JsonItemConverter* m_self{nullptr}; - std::unique_ptr<JsonItemDataConverterInterface> m_itemdata_converter; - std::unique_ptr<JsonItemTagsConverter> m_itemtags_converter; - ConverterContext m_context; - - JsonItemConverterImpl(JsonItemConverter* parent, const ConverterContext& context) - : m_self(parent), m_context(context) - { - //! Callback to convert SessionItem to JSON object. - auto create_json = [this](const SessionItem& item) { return m_self->to_json(&item); }; - - //! Callback to create SessionItem from JSON object. - auto create_item = [this](const QJsonObject& json) { return m_self->from_json(json); }; - - //! Callback to update SessionItem from JSON object. - auto update_item = [this](const QJsonObject& json, SessionItem* item) { - populate_item(json, *item); - }; - - ConverterCallbacks callbacks{create_json, create_item, update_item}; - - m_itemdata_converter = createDataConverter(m_context.m_mode); - m_itemtags_converter = std::make_unique<JsonItemTagsConverter>(callbacks); - } - - const ItemFactoryInterface* factory() { return m_context.m_factory; } - - void populate_item_data(const QJsonArray& json, SessionItemData& item_data) - { - m_itemdata_converter->from_json(json, item_data); - } - - void populate_item_tags(const QJsonObject& json, SessionItemTags& item_tags) - { - m_itemtags_converter->from_json(json, item_tags); - } - - void populate_item(const QJsonObject& json, SessionItem& item) - { - auto modelType = json[JsonItemFormatAssistant::modelKey].toString().toStdString(); - - if (modelType != item.modelType()) - throw std::runtime_error("Item model mismatch"); - - if (isRebuildItemDataAndTagFromJson(m_context.m_mode)) { - item.setDataAndTags(std::make_unique<SessionItemData>(), - std::make_unique<SessionItemTags>()); - } - - populate_item_data(json[JsonItemFormatAssistant::itemDataKey].toArray(), *item.itemData()); - populate_item_tags(json[JsonItemFormatAssistant::itemTagsKey].toObject(), *item.itemTags()); - - for (auto child : item.children()) - child->setParent(&item); - - if (isRegenerateIdWhenBackFromJson(m_context.m_mode)) - item.setData(UniqueIdGenerator::generate(), ItemDataRole::IDENTIFIER); - } - - QJsonObject item_to_json(const SessionItem& item) const - { - QJsonObject result; - result[JsonItemFormatAssistant::modelKey] = QString::fromStdString(item.modelType()); - result[JsonItemFormatAssistant::itemDataKey] = - m_itemdata_converter->to_json(*item.itemData()); - result[JsonItemFormatAssistant::itemTagsKey] = - m_itemtags_converter->to_json(*item.itemTags()); - - return result; - } -}; - -JsonItemConverter::JsonItemConverter(const ConverterContext& context) - : p_impl(std::make_unique<JsonItemConverterImpl>(this, context)) -{ -} - -JsonItemConverter::~JsonItemConverter() = default; - -QJsonObject JsonItemConverter::to_json(const SessionItem* item) const -{ - return item ? p_impl->item_to_json(*item) : QJsonObject(); -} - -std::unique_ptr<SessionItem> JsonItemConverter::from_json(const QJsonObject& json) const -{ - static JsonItemFormatAssistant assistant; - - if (!assistant.isSessionItem(json)) - throw std::runtime_error("JsonItemConverterV2::from_json() -> Error. Given json object " - "can't represent a SessionItem."); - - auto modelType = json[JsonItemFormatAssistant::modelKey].toString().toStdString(); - auto result = p_impl->factory()->createItem(modelType); - - p_impl->populate_item(json, *result); - - return result; -} diff --git a/mvvm/model/mvvm/serialization/jsonitemconverter.h b/mvvm/model/mvvm/serialization/jsonitemconverter.h deleted file mode 100644 index 4232437cf0c..00000000000 --- a/mvvm/model/mvvm/serialization/jsonitemconverter.h +++ /dev/null @@ -1,46 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/serialization/jsonitemconverter.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONITEMCONVERTER_H -#define BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONITEMCONVERTER_H - -#include "mvvm/serialization/jsonitemconverterinterface.h" - -namespace ModelView { - -class ItemFactoryInterface; -struct ConverterContext; - -//! Converter between SessionItem and JSON object. - -class MVVM_MODEL_EXPORT JsonItemConverter : public JsonItemConverterInterface { -public: - JsonItemConverter(const ConverterContext& context); - JsonItemConverter(const JsonItemConverter&) = delete; - JsonItemConverter& operator=(const JsonItemConverter&) = delete; - - ~JsonItemConverter() override; - - QJsonObject to_json(const SessionItem* item) const override; - - std::unique_ptr<SessionItem> from_json(const QJsonObject& json) const override; - -private: - struct JsonItemConverterImpl; - std::unique_ptr<JsonItemConverterImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONITEMCONVERTER_H diff --git a/mvvm/model/mvvm/serialization/jsonitemconverterinterface.h b/mvvm/model/mvvm/serialization/jsonitemconverterinterface.h deleted file mode 100644 index 562c7aeb63c..00000000000 --- a/mvvm/model/mvvm/serialization/jsonitemconverterinterface.h +++ /dev/null @@ -1,42 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/serialization/jsonitemconverterinterface.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONITEMCONVERTERINTERFACE_H -#define BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONITEMCONVERTERINTERFACE_H - -#include "mvvm/model_export.h" -#include <memory> - -class QJsonObject; - -namespace ModelView { - -class SessionItem; - -//! Base class for all converters of SessionItem to/from JSON object. - -class MVVM_MODEL_EXPORT JsonItemConverterInterface { -public: - virtual ~JsonItemConverterInterface() = default; - - //! Converts item to JSON. - virtual QJsonObject to_json(const SessionItem* item) const = 0; - - //! Creates item from JSON. - virtual std::unique_ptr<SessionItem> from_json(const QJsonObject&) const = 0; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONITEMCONVERTERINTERFACE_H diff --git a/mvvm/model/mvvm/serialization/jsonitemcopystrategy.cpp b/mvvm/model/mvvm/serialization/jsonitemcopystrategy.cpp deleted file mode 100644 index acae455e3da..00000000000 --- a/mvvm/model/mvvm/serialization/jsonitemcopystrategy.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/serialization/jsonitemcopystrategy.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/serialization/jsonitemcopystrategy.h" -#include "mvvm/factories/itemconverterfactory.h" -#include "mvvm/model/sessionitem.h" -#include <QJsonObject> - -using namespace ModelView; - -struct JsonItemCopyStrategy::JsonItemCopyStrategyImpl { - std::unique_ptr<JsonItemConverterInterface> m_converter; -}; - -JsonItemCopyStrategy::JsonItemCopyStrategy(const ItemFactoryInterface* item_factory) - : p_impl(std::make_unique<JsonItemCopyStrategyImpl>()) -{ - p_impl->m_converter = CreateItemCopyConverter(item_factory); -} - -JsonItemCopyStrategy::~JsonItemCopyStrategy() = default; - -std::unique_ptr<SessionItem> JsonItemCopyStrategy::createCopy(const SessionItem* item) const -{ - auto json = p_impl->m_converter->to_json(item); - return p_impl->m_converter->from_json(json); -} diff --git a/mvvm/model/mvvm/serialization/jsonitemcopystrategy.h b/mvvm/model/mvvm/serialization/jsonitemcopystrategy.h deleted file mode 100644 index 4daa97ee116..00000000000 --- a/mvvm/model/mvvm/serialization/jsonitemcopystrategy.h +++ /dev/null @@ -1,42 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/serialization/jsonitemcopystrategy.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONITEMCOPYSTRATEGY_H -#define BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONITEMCOPYSTRATEGY_H - -#include "mvvm/interfaces/itemcopystrategy.h" -#include <memory> - -namespace ModelView { - -class SessionItem; -class ItemFactoryInterface; - -//! Provide SessionItem copying using json based strategy. - -class MVVM_MODEL_EXPORT JsonItemCopyStrategy : public ItemCopyStrategy { -public: - JsonItemCopyStrategy(const ItemFactoryInterface* item_factory); - ~JsonItemCopyStrategy(); - - std::unique_ptr<SessionItem> createCopy(const SessionItem* item) const; - -private: - struct JsonItemCopyStrategyImpl; - std::unique_ptr<JsonItemCopyStrategyImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONITEMCOPYSTRATEGY_H diff --git a/mvvm/model/mvvm/serialization/jsonitemdataconverter.cpp b/mvvm/model/mvvm/serialization/jsonitemdataconverter.cpp deleted file mode 100644 index 4fa5fde8fd6..00000000000 --- a/mvvm/model/mvvm/serialization/jsonitemdataconverter.cpp +++ /dev/null @@ -1,121 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/serialization/jsonitemdataconverter.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/serialization/jsonitemdataconverter.h" -#include "mvvm/model/mvvm_types.h" -#include "mvvm/model/sessionitemdata.h" -#include "mvvm/serialization/jsonitemformatassistant.h" -#include "mvvm/serialization/jsonvariantconverter.h" -#include <QJsonArray> -#include <QJsonObject> -#include <set> -#include <stdexcept> - -using namespace ModelView; - -namespace { -QJsonValue keyValue(const QJsonValue& parent_value, const QString& key) -{ - const QJsonObject& parent_object = parent_value.toObject(); - return parent_object.value(key); -} -} // namespace - -JsonItemDataConverter::JsonItemDataConverter(accept_strategy_t to_json_accept, - accept_strategy_t from_json_accept) - : m_to_json_accept(to_json_accept) - , m_from_json_accept(from_json_accept) - , m_variant_converter(std::make_unique<JsonVariantConverter>()) -{ -} - -JsonItemDataConverter::~JsonItemDataConverter() = default; - -QJsonArray JsonItemDataConverter::to_json(const SessionItemData& data) -{ - QJsonArray result; - - for (const auto& x : data) { - QJsonObject object; - if (isRoleToJson(x.m_role)) { - object[JsonItemFormatAssistant::roleKey] = x.m_role; - object[JsonItemFormatAssistant::variantKey] = m_variant_converter->get_json(x.m_data); - result.append(object); - } - } - - return result; -} - -//! Updates existing data with JSON content. - -void JsonItemDataConverter::from_json(const QJsonArray& object, SessionItemData& data) -{ - static JsonItemFormatAssistant assistant; - auto persistent_data = std::make_unique<SessionItemData>(); - - for (const auto& x : object) { - if (!assistant.isSessionItemData(x.toObject())) - throw std::runtime_error("JsonItemData::get_data() -> Invalid json object."); - auto role = keyValue(x, JsonItemFormatAssistant::roleKey).toInt(); - auto variant = m_variant_converter->get_variant( - keyValue(x, JsonItemFormatAssistant::variantKey).toObject()); - if (isRoleFromJson(role)) - persistent_data->setData(variant, role); - } - - auto runtime_roles = data.roles(); - auto persistent_roles = persistent_data->roles(); - - std::set<int> roles(runtime_roles.begin(), runtime_roles.end()); - roles.insert(persistent_roles.begin(), persistent_roles.end()); - - for (auto role : roles) { - // all roles existing in `persistent` will be taken from there - if (persistent_data->hasData(role)) - data.setData(persistent_data->data(role), role); - } -} - -//! Creates JSON data converter intended for simple data copying. Nothing is filtered out. - -std::unique_ptr<JsonItemDataConverterInterface> JsonItemDataConverter::createCopyConverter() -{ - return std::make_unique<JsonItemDataConverter>(); -} - -//! Creates JSON data converter intended for project saving. Only IDENTIFIER and DATA gous to/from -//! JSON. - -std::unique_ptr<JsonItemDataConverterInterface> JsonItemDataConverter::createProjectConverter() -{ - auto accept_roles = [](auto role) { - return role == ItemDataRole::IDENTIFIER || role == ItemDataRole::DATA; - }; - return std::make_unique<JsonItemDataConverter>(accept_roles, accept_roles); -} - -//! Returns true if given role should be saved in json object. - -bool JsonItemDataConverter::isRoleToJson(int role) const -{ - return m_to_json_accept ? m_to_json_accept(role) : true; -} - -//! Returns true if given role should be parsed from json object. - -bool JsonItemDataConverter::isRoleFromJson(int role) const -{ - return m_from_json_accept ? m_from_json_accept(role) : true; -} diff --git a/mvvm/model/mvvm/serialization/jsonitemdataconverter.h b/mvvm/model/mvvm/serialization/jsonitemdataconverter.h deleted file mode 100644 index 767f4ed862b..00000000000 --- a/mvvm/model/mvvm/serialization/jsonitemdataconverter.h +++ /dev/null @@ -1,59 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/serialization/jsonitemdataconverter.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONITEMDATACONVERTER_H -#define BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONITEMDATACONVERTER_H - -#include "mvvm/serialization/jsonitemdataconverterinterface.h" -#include <QString> -#include <functional> -#include <memory> - -class QJsonObject; - -namespace ModelView { - -class JsonVariantConverterInterface; - -//! Default converter of SessionItemData to/from json object. - -class MVVM_MODEL_EXPORT JsonItemDataConverter : public JsonItemDataConverterInterface { -public: - using accept_strategy_t = std::function<bool(int)>; - - JsonItemDataConverter(accept_strategy_t to_json_accept = {}, - accept_strategy_t from_json_accept = {}); - - ~JsonItemDataConverter() override; - - QJsonArray to_json(const SessionItemData& data) override; - - void from_json(const QJsonArray& object, SessionItemData& data) override; - - static std::unique_ptr<JsonItemDataConverterInterface> createCopyConverter(); - - static std::unique_ptr<JsonItemDataConverterInterface> createProjectConverter(); - -private: - bool isRoleToJson(int role) const; - bool isRoleFromJson(int role) const; - - accept_strategy_t m_to_json_accept; //!< callback to find whether to write role to json - accept_strategy_t m_from_json_accept; //!< callback to find whether to read role from json - std::unique_ptr<JsonVariantConverterInterface> m_variant_converter; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONITEMDATACONVERTER_H diff --git a/mvvm/model/mvvm/serialization/jsonitemdataconverterinterface.h b/mvvm/model/mvvm/serialization/jsonitemdataconverterinterface.h deleted file mode 100644 index edd4a36a269..00000000000 --- a/mvvm/model/mvvm/serialization/jsonitemdataconverterinterface.h +++ /dev/null @@ -1,42 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/serialization/jsonitemdataconverterinterface.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONITEMDATACONVERTERINTERFACE_H -#define BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONITEMDATACONVERTERINTERFACE_H - -#include "mvvm/model_export.h" -#include <memory> - -class QJsonArray; - -namespace ModelView { - -class SessionItemData; - -//! Base class for all converters of SessionItemData to/from JSON object. - -class MVVM_MODEL_EXPORT JsonItemDataConverterInterface { -public: - virtual ~JsonItemDataConverterInterface() = default; - - //! Converts SessionItemData to JSON; - virtual QJsonArray to_json(const SessionItemData&) = 0; - - //! Converts SessionItemData from JSON; - virtual void from_json(const QJsonArray& object, SessionItemData& data) = 0; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONITEMDATACONVERTERINTERFACE_H diff --git a/mvvm/model/mvvm/serialization/jsonitemformatassistant.cpp b/mvvm/model/mvvm/serialization/jsonitemformatassistant.cpp deleted file mode 100644 index 94f37911024..00000000000 --- a/mvvm/model/mvvm/serialization/jsonitemformatassistant.cpp +++ /dev/null @@ -1,140 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/serialization/jsonitemformatassistant.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/serialization/jsonitemformatassistant.h" -#include <QJsonObject> -#include <QStringList> - -using namespace ModelView; - -namespace { -//! Returns list of keys which should be in QJsonObject to represent SessionItem. -QStringList expected_item_keys() -{ - QStringList result{JsonItemFormatAssistant::modelKey, JsonItemFormatAssistant::itemDataKey, - JsonItemFormatAssistant::itemTagsKey}; - std::sort(result.begin(), result.end()); - return result; -} - -//! Returns list of keys which should be in QJsonObject to represent SessionItemData. - -QStringList expected_itemdata_keys() -{ - QStringList result{JsonItemFormatAssistant::roleKey, JsonItemFormatAssistant::variantKey}; - std::sort(result.begin(), result.end()); - return result; -} - -//! Returns list of keys which should be in QJsonObject to represent SessionItemTags. - -QStringList expected_tags_keys() -{ - QStringList result{JsonItemFormatAssistant::defaultTagKey, - JsonItemFormatAssistant::containerKey}; - std::sort(result.begin(), result.end()); - return result; -} - -//! Returns list of keys which should be in QJsonObject to represent SessionItemContainer. - -QStringList expected_itemcontainer_keys() -{ - QStringList result = {JsonItemFormatAssistant::tagInfoKey, JsonItemFormatAssistant::itemsKey}; - std::sort(result.begin(), result.end()); - return result; -} - -//! Returns list of keys which should be in QJsonObject to represent SessionModel. - -QStringList expected_sessionmodel_keys() -{ - QStringList result{JsonItemFormatAssistant::sessionModelKey, JsonItemFormatAssistant::itemsKey}; - std::sort(result.begin(), result.end()); - return result; -} - -} // namespace - -//! Returns true if given json object represents SessionItem. - -bool JsonItemFormatAssistant::isSessionItem(const QJsonObject& json) const -{ - static const QStringList expected = expected_item_keys(); - - if (json.keys() != expected) - return false; - - if (!json[itemDataKey].isArray()) - return false; - - if (!json[itemTagsKey].isObject()) - return false; - - return true; -} - -bool JsonItemFormatAssistant::isSessionItemData(const QJsonObject& json) const -{ - static const QStringList expected = expected_itemdata_keys(); - return json.keys() == expected; -} - -//! Returns true if given json object represents SessionItemTags. - -bool JsonItemFormatAssistant::isSessionItemTags(const QJsonObject& json) const -{ - static const QStringList expected = expected_tags_keys(); - - if (json.keys() != expected) - return false; - - if (!json[containerKey].isArray()) - return false; - - return true; -} - -//! Returns true if given json object represents SessionItemContainer. - -bool JsonItemFormatAssistant::isSessionItemContainer(const QJsonObject& json) const -{ - static const QStringList expected = expected_itemcontainer_keys(); - - if (json.keys() != expected) - return false; - - if (!json[tagInfoKey].isObject()) - return false; - - if (!json[itemsKey].isArray()) - return false; - - return true; -} - -//! Returns true if given json object represents SessionModel. - -bool JsonItemFormatAssistant::isSessionModel(const QJsonObject& object) const -{ - static const QStringList expected = expected_sessionmodel_keys(); - - if (object.keys() != expected) - return false; - - if (!object[itemsKey].isArray()) - return false; - - return true; -} diff --git a/mvvm/model/mvvm/serialization/jsonitemformatassistant.h b/mvvm/model/mvvm/serialization/jsonitemformatassistant.h deleted file mode 100644 index 197e6246ee4..00000000000 --- a/mvvm/model/mvvm/serialization/jsonitemformatassistant.h +++ /dev/null @@ -1,53 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/serialization/jsonitemformatassistant.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONITEMFORMATASSISTANT_H -#define BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONITEMFORMATASSISTANT_H - -#include "mvvm/serialization/jsonitemconverterinterface.h" -#include <QString> -#include <memory> - -namespace ModelView { - -class ItemFactoryInterface; - -//! Utility class to determine, whether given JSON object can represent various parts of -//! SessionModel. It is made a class (and not a set of free functions) to allow different formats in -//! the future. - -class MVVM_MODEL_EXPORT JsonItemFormatAssistant { -public: - static inline const QString modelKey = "model"; - static inline const QString itemDataKey = "itemData"; - static inline const QString itemTagsKey = "itemTags"; - static inline const QString defaultTagKey = "defaultTag"; - static inline const QString containerKey = "containers"; - static inline const QString tagInfoKey = "tagInfo"; - static inline const QString itemsKey = "items"; - static inline const QString sessionModelKey = "sessionmodel"; - static inline const QString versionKey = "version"; - static inline const QString roleKey = "role"; - static inline const QString variantKey = "variant"; - - bool isSessionItem(const QJsonObject& json) const; - bool isSessionItemData(const QJsonObject& json) const; - bool isSessionItemTags(const QJsonObject& json) const; - bool isSessionItemContainer(const QJsonObject& json) const; - bool isSessionModel(const QJsonObject& object) const; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONITEMFORMATASSISTANT_H diff --git a/mvvm/model/mvvm/serialization/jsonitemtagsconverter.cpp b/mvvm/model/mvvm/serialization/jsonitemtagsconverter.cpp deleted file mode 100644 index f95576a70a1..00000000000 --- a/mvvm/model/mvvm/serialization/jsonitemtagsconverter.cpp +++ /dev/null @@ -1,116 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/serialization/jsonitemtagsconverter.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/serialization/jsonitemtagsconverter.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionitemtags.h" -#include "mvvm/model/taginfo.h" -#include "mvvm/serialization/jsonitem_types.h" -#include "mvvm/serialization/jsonitemcontainerconverter.h" -#include "mvvm/serialization/jsonitemformatassistant.h" -#include "mvvm/serialization/jsontaginfoconverter.h" -#include <QJsonArray> -#include <QJsonObject> - -using namespace ModelView; - -struct JsonItemTagsConverter::JsonItemTagsConverterImpl { - std::unique_ptr<JsonItemContainerConverter> m_container_converter; - std::unique_ptr<JsonTagInfoConverterInterface> m_taginfo_converter; - - JsonItemTagsConverterImpl(ConverterCallbacks callbacks = {}) - { - m_container_converter = std::make_unique<JsonItemContainerConverter>(std::move(callbacks)); - m_taginfo_converter = std::make_unique<JsonTagInfoConverter>(); - } - - //! Create containers from JSON. SessionItemTags should be empty. - - void create_containers(const QJsonObject& json, SessionItemTags& item_tags) - { - if (item_tags.tagsCount()) - throw std::runtime_error("Error in JsonItemTagsConverter: no containers expected."); - - auto default_tag = json[JsonItemFormatAssistant::defaultTagKey].toString().toStdString(); - item_tags.setDefaultTag(default_tag); - - auto container_array = json[JsonItemFormatAssistant::containerKey].toArray(); - - for (const auto ref : container_array) { - QJsonObject json_container = ref.toObject(); - QJsonObject json_taginfo = - json_container[JsonItemFormatAssistant::tagInfoKey].toObject(); - TagInfo tagInfo = m_taginfo_converter->from_json(json_taginfo); - item_tags.registerTag(tagInfo); - } - } - - //! Populate containers from JSON. Container must be already created. - - void populate_containers(const QJsonObject& json, SessionItemTags& item_tags) - { - auto container_array = json[JsonItemFormatAssistant::containerKey].toArray(); - - if (container_array.size() != item_tags.tagsCount()) - throw std::runtime_error("Error in JsonItemTagsConverter: mismatch in number of tags"); - - auto default_tag = json[JsonItemFormatAssistant::defaultTagKey].toString().toStdString(); - if (default_tag != item_tags.defaultTag()) - throw std::runtime_error("Error in JsonItemTagsConverter: default tag mismatch."); - - int index(0); - for (const auto container_ref : container_array) - m_container_converter->from_json(container_ref.toObject(), item_tags.at(index++)); - } -}; - -JsonItemTagsConverter::JsonItemTagsConverter(ConverterCallbacks callbacks) - : p_impl(std::make_unique<JsonItemTagsConverterImpl>(callbacks)) -{ -} - -JsonItemTagsConverter::~JsonItemTagsConverter() = default; - -QJsonObject JsonItemTagsConverter::to_json(const SessionItemTags& item_tags) -{ - QJsonObject result; - result[JsonItemFormatAssistant::defaultTagKey] = QString::fromStdString(item_tags.defaultTag()); - - QJsonArray containerArray; - for (auto container : item_tags) - containerArray.append(p_impl->m_container_converter->to_json(*container)); - result[JsonItemFormatAssistant::containerKey] = containerArray; - - return result; -} - -//! Reconstructs SessionItemTags from the content of JSON object. Can work in two modes: -//! + If SessionItemTags is empty, all tags will be reconstructed as in JSON, and then populated -//! with the content. -//! + If SessionItemTags contains some tags already, they will be populated from JSON. in this -//! case it will be assumed, that existing item's tags are matching JSON. - -void JsonItemTagsConverter::from_json(const QJsonObject& json, SessionItemTags& item_tags) -{ - static JsonItemFormatAssistant assistant; - - if (!assistant.isSessionItemTags(json)) - throw std::runtime_error( - "Error in JsonItemTagsConverter: given json object can't represent a SessionItemTags."); - - if (!item_tags.tagsCount()) - p_impl->create_containers(json, item_tags); - - p_impl->populate_containers(json, item_tags); -} diff --git a/mvvm/model/mvvm/serialization/jsonitemtagsconverter.h b/mvvm/model/mvvm/serialization/jsonitemtagsconverter.h deleted file mode 100644 index 36c17e80f69..00000000000 --- a/mvvm/model/mvvm/serialization/jsonitemtagsconverter.h +++ /dev/null @@ -1,48 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/serialization/jsonitemtagsconverter.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONITEMTAGSCONVERTER_H -#define BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONITEMTAGSCONVERTER_H - -#include "mvvm/model_export.h" -#include <functional> -#include <memory> - -class QJsonObject; - -namespace ModelView { - -class SessionItem; -class SessionItemTags; -struct ConverterCallbacks; - -//! Converter between SessionItemTags and JSON object. - -class MVVM_MODEL_EXPORT JsonItemTagsConverter { -public: - JsonItemTagsConverter(ConverterCallbacks callbacks); - ~JsonItemTagsConverter(); - - QJsonObject to_json(const SessionItemTags& item_tags); - - void from_json(const QJsonObject& json, SessionItemTags& item_tags); - -private: - struct JsonItemTagsConverterImpl; - std::unique_ptr<JsonItemTagsConverterImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONITEMTAGSCONVERTER_H diff --git a/mvvm/model/mvvm/serialization/jsonmodelconverter.cpp b/mvvm/model/mvvm/serialization/jsonmodelconverter.cpp deleted file mode 100644 index ae79b411dfc..00000000000 --- a/mvvm/model/mvvm/serialization/jsonmodelconverter.cpp +++ /dev/null @@ -1,94 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/serialization/jsonmodelconverter.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/serialization/jsonmodelconverter.h" -#include "mvvm/factories/itemconverterfactory.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/serialization/jsonitem_types.h" -#include "mvvm/serialization/jsonitemconverter.h" -#include "mvvm/serialization/jsonitemformatassistant.h" -#include <QJsonArray> -#include <QJsonObject> -#include <stdexcept> - -using namespace ModelView; - -namespace { -std::unique_ptr<JsonItemConverterInterface> CreateConverter(const ItemFactoryInterface* factory, - ConverterMode mode) -{ - if (mode == ConverterMode::clone) - return CreateItemCloneConverter(factory); - else if (mode == ConverterMode::copy) - return CreateItemCopyConverter(factory); - else if (mode == ConverterMode::project) - return CreateItemProjectConverter(factory); - else - throw std::runtime_error("Error in JsonModelConverter: unknown converter mode"); -} - -} // namespace - -JsonModelConverter::JsonModelConverter(ConverterMode mode) : m_mode(mode) {} - -JsonModelConverter::~JsonModelConverter() = default; - -QJsonObject JsonModelConverter::to_json(const SessionModel& model) const -{ - QJsonObject result; - - if (!model.rootItem()) - throw std::runtime_error("JsonModel::to_json() -> Error. Model is not initialized."); - - result[JsonItemFormatAssistant::sessionModelKey] = QString::fromStdString(model.modelType()); - - QJsonArray itemArray; - - auto itemConverter = CreateConverter(model.factory(), m_mode); - - for (auto item : model.rootItem()->children()) - itemArray.append(itemConverter->to_json(item)); - - result[JsonItemFormatAssistant::itemsKey] = itemArray; - - return result; -} - -void JsonModelConverter::from_json(const QJsonObject& json, SessionModel& model) const -{ - if (!model.rootItem()) - throw std::runtime_error("JsonModel::json_to_model() -> Error. Model is not initialized."); - - JsonItemFormatAssistant assistant; - if (!assistant.isSessionModel(json)) - throw std::runtime_error("JsonModel::json_to_model() -> Error. Invalid json object."); - - if (json[JsonItemFormatAssistant::sessionModelKey].toString() - != QString::fromStdString(model.modelType())) - throw std::runtime_error( - "JsonModel::json_to_model() -> Unexpected model type '" + model.modelType() - + "', json key '" - + json[JsonItemFormatAssistant::sessionModelKey].toString().toStdString() + "'"); - - auto itemConverter = CreateConverter(model.factory(), m_mode); - - auto rebuild_root = [&json, &itemConverter](auto parent) { - for (const auto ref : json[JsonItemFormatAssistant::itemsKey].toArray()) { - auto item = itemConverter->from_json(ref.toObject()); - parent->insertItem(item.release(), TagRow::append()); - } - }; - model.clear(rebuild_root); -} diff --git a/mvvm/model/mvvm/serialization/jsonmodelconverter.h b/mvvm/model/mvvm/serialization/jsonmodelconverter.h deleted file mode 100644 index e31e68d612f..00000000000 --- a/mvvm/model/mvvm/serialization/jsonmodelconverter.h +++ /dev/null @@ -1,46 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/serialization/jsonmodelconverter.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONMODELCONVERTER_H -#define BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONMODELCONVERTER_H - -#include "mvvm/serialization/jsonmodelconverterinterface.h" - -class QJsonObject; - -namespace ModelView { - -class SessionModel; -enum class ConverterMode; - -//! Converter of SessionModel to/from json object with posibility to select one of convertion modes. - -class MVVM_MODEL_EXPORT JsonModelConverter : public JsonModelConverterInterface { -public: - JsonModelConverter(ConverterMode mode); - ~JsonModelConverter() override; - - //! Writes content of model into json. - QJsonObject to_json(const SessionModel& model) const override; - - //! Reads json object and build the model. - void from_json(const QJsonObject& json, SessionModel& model) const override; - -private: - ConverterMode m_mode; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONMODELCONVERTER_H diff --git a/mvvm/model/mvvm/serialization/jsonmodelconverterinterface.h b/mvvm/model/mvvm/serialization/jsonmodelconverterinterface.h deleted file mode 100644 index f02f5328993..00000000000 --- a/mvvm/model/mvvm/serialization/jsonmodelconverterinterface.h +++ /dev/null @@ -1,39 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/serialization/jsonmodelconverterinterface.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONMODELCONVERTERINTERFACE_H -#define BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONMODELCONVERTERINTERFACE_H - -#include "mvvm/model_export.h" - -class QJsonObject; - -namespace ModelView { - -class SessionModel; - -//! Base class for all converters of SessionModel to/from json object. - -class MVVM_MODEL_EXPORT JsonModelConverterInterface { -public: - virtual ~JsonModelConverterInterface() = default; - - virtual QJsonObject to_json(const SessionModel&) const = 0; - - virtual void from_json(const QJsonObject&, SessionModel&) const = 0; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONMODELCONVERTERINTERFACE_H diff --git a/mvvm/model/mvvm/serialization/jsontaginfoconverter.cpp b/mvvm/model/mvvm/serialization/jsontaginfoconverter.cpp deleted file mode 100644 index 4839664f7f3..00000000000 --- a/mvvm/model/mvvm/serialization/jsontaginfoconverter.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/serialization/jsontaginfoconverter.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/serialization/jsontaginfoconverter.h" -#include "mvvm/model/taginfo.h" -#include <QJsonArray> -#include <QJsonObject> -#include <QStringList> -#include <stdexcept> - -using namespace ModelView; - -namespace { -QStringList expected_taginfo_keys() -{ - QStringList result = QStringList() - << JsonTagInfoConverter::nameKey << JsonTagInfoConverter::minKey - << JsonTagInfoConverter::maxKey << JsonTagInfoConverter::modelsKey; - std::sort(result.begin(), result.end()); - return result; -} -} // namespace - -QJsonObject JsonTagInfoConverter::to_json(const ModelView::TagInfo& tag) -{ - QJsonObject result; - result[nameKey] = QString::fromStdString(tag.name()); - result[minKey] = tag.min(); - result[maxKey] = tag.max(); - QJsonArray str_array; - for (const auto& str : tag.modelTypes()) - str_array.append(QString::fromStdString(str)); - result[modelsKey] = str_array; - - return result; -} - -TagInfo JsonTagInfoConverter::from_json(const QJsonObject& object) -{ - if (!isTagInfo(object)) - throw std::runtime_error("JsonTagInfo::get_tags() -> Invalid json object."); - - auto name = object[nameKey].toString().toStdString(); - auto min = object[minKey].toInt(); - auto max = object[maxKey].toInt(); - std::vector<std::string> models; - for (const auto ref : object[modelsKey].toArray()) - models.push_back(ref.toString().toStdString()); - - return TagInfo(name, min, max, models); -} - -//! Returns true if given json object represents TagInfo object. - -bool JsonTagInfoConverter::isTagInfo(const QJsonObject& object) -{ - static const QStringList expected = expected_taginfo_keys(); - - if (object.keys() != expected) - return false; - - if (!object[modelsKey].isArray()) - return false; - - return true; -} diff --git a/mvvm/model/mvvm/serialization/jsontaginfoconverter.h b/mvvm/model/mvvm/serialization/jsontaginfoconverter.h deleted file mode 100644 index f9178f92d1a..00000000000 --- a/mvvm/model/mvvm/serialization/jsontaginfoconverter.h +++ /dev/null @@ -1,41 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/serialization/jsontaginfoconverter.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONTAGINFOCONVERTER_H -#define BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONTAGINFOCONVERTER_H - -#include "mvvm/serialization/jsontaginfoconverterinterface.h" -#include <QString> - -namespace ModelView { - -//! Default converter between TagInfo and json object. - -class MVVM_MODEL_EXPORT JsonTagInfoConverter : public JsonTagInfoConverterInterface { -public: - static inline const QString nameKey = "name"; - static inline const QString minKey = "min"; - static inline const QString maxKey = "max"; - static inline const QString modelsKey = "models"; - - QJsonObject to_json(const TagInfo& tag) override; - - TagInfo from_json(const QJsonObject& object) override; - - bool isTagInfo(const QJsonObject& object); -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONTAGINFOCONVERTER_H diff --git a/mvvm/model/mvvm/serialization/jsontaginfoconverterinterface.h b/mvvm/model/mvvm/serialization/jsontaginfoconverterinterface.h deleted file mode 100644 index affd33cfc15..00000000000 --- a/mvvm/model/mvvm/serialization/jsontaginfoconverterinterface.h +++ /dev/null @@ -1,39 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/serialization/jsontaginfoconverterinterface.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONTAGINFOCONVERTERINTERFACE_H -#define BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONTAGINFOCONVERTERINTERFACE_H - -#include "mvvm/model_export.h" - -class QJsonObject; - -namespace ModelView { - -class TagInfo; - -//! Base class for all converters of TagInfo to/from json object - -class MVVM_MODEL_EXPORT JsonTagInfoConverterInterface { -public: - virtual ~JsonTagInfoConverterInterface() = default; - - virtual QJsonObject to_json(const TagInfo&) = 0; - - virtual TagInfo from_json(const QJsonObject&) = 0; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONTAGINFOCONVERTERINTERFACE_H diff --git a/mvvm/model/mvvm/serialization/jsonutils.cpp b/mvvm/model/mvvm/serialization/jsonutils.cpp deleted file mode 100644 index 76e491bd62a..00000000000 --- a/mvvm/model/mvvm/serialization/jsonutils.cpp +++ /dev/null @@ -1,76 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/serialization/jsonutils.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/serialization/jsonutils.h" -#include "mvvm/factories/modelconverterfactory.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/utils/reallimits.h" -#include <QJsonDocument> -#include <QJsonObject> -#include <stdexcept> - -namespace { -const std::string text_limitless = "limitless"; -const std::string text_positive = "positive"; -const std::string text_nonnegative = "nonnegative"; -const std::string text_lowerlimited = "lowerlimited"; -const std::string text_upperlimited = "upperlimited"; -const std::string text_limited = "limited"; -const std::string separator = " "; -} // namespace -using namespace ModelView; - -std::string JsonUtils::ModelToJsonString(const ModelView::SessionModel& model) -{ - auto converter = CreateModelCopyConverter(); - QJsonObject json_source = converter->to_json(model); - QJsonDocument document(json_source); - return QString(document.toJson(QJsonDocument::Indented)).toStdString(); -} - -std::string JsonUtils::ToString(const ModelView::RealLimits& limits) -{ - if (limits.isLimitless()) - return text_limitless; - else if (limits.isPositive()) - return text_positive; - else if (limits.isNonnegative()) - return text_nonnegative; - else if (limits.isLowerLimited()) - return text_lowerlimited; - else if (limits.isUpperLimited()) - return text_upperlimited; - else if (limits.isLimited()) - return text_limited; - else - throw std::runtime_error("JsonUtils::ToString() -> Unknown type"); -} - -RealLimits JsonUtils::CreateLimits(const std::string& text, double min, double max) -{ - if (text == text_limitless) - return RealLimits(); - else if (text == text_positive) - return RealLimits::positive(); - else if (text == text_nonnegative) - return RealLimits::nonnegative(); - else if (text == text_lowerlimited) - return RealLimits::lowerLimited(min); - else if (text == text_upperlimited) - return RealLimits::upperLimited(max); - else if (text == text_limited) - return RealLimits::limited(min, max); - else - throw std::runtime_error("JsonUtils::CreateLimits -> Unknown type"); -} diff --git a/mvvm/model/mvvm/serialization/jsonutils.h b/mvvm/model/mvvm/serialization/jsonutils.h deleted file mode 100644 index 1dc53908945..00000000000 --- a/mvvm/model/mvvm/serialization/jsonutils.h +++ /dev/null @@ -1,41 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/serialization/jsonutils.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONUTILS_H -#define BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONUTILS_H - -#include "mvvm/model_export.h" -#include <string> - -namespace ModelView { - -class SessionModel; -class RealLimits; - -namespace JsonUtils { - -//! Returns multiline string representing model content as json. -MVVM_MODEL_EXPORT std::string ModelToJsonString(const SessionModel& model); - -//! Returns string representation of RealLimits. -MVVM_MODEL_EXPORT std::string ToString(const RealLimits& limits); - -MVVM_MODEL_EXPORT RealLimits CreateLimits(const std::string& text, double min = 0.0, - double max = 0.0); - -} // namespace JsonUtils - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONUTILS_H diff --git a/mvvm/model/mvvm/serialization/jsonvariantconverter.cpp b/mvvm/model/mvvm/serialization/jsonvariantconverter.cpp deleted file mode 100644 index e4fe1b3f2f4..00000000000 --- a/mvvm/model/mvvm/serialization/jsonvariantconverter.cpp +++ /dev/null @@ -1,313 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/serialization/jsonvariantconverter.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/serialization/jsonvariantconverter.h" -#include "mvvm/model/comboproperty.h" -#include "mvvm/model/customvariants.h" -#include "mvvm/model/externalproperty.h" -#include "mvvm/model/variant_constants.h" -#include "mvvm/serialization/jsonutils.h" -#include "mvvm/utils/reallimits.h" -#include <QJsonArray> -#include <QJsonObject> -#include <stdexcept> - -using namespace ModelView; - -namespace { - -const QString variantTypeKey = "type"; -const QString variantValueKey = "value"; -const QString comboValuesKey = "values"; -const QString comboSelectionKey = "selections"; -const QString extPropertyTextKey = "text"; -const QString extPropertyColorKey = "color"; -const QString extPropertyIdKey = "identifier"; -const QString realLimitsTextKey = "text"; -const QString realLimitsMinKey = "min"; -const QString realLimitsMaxKey = "max"; - -QStringList expected_variant_keys(); - -QJsonObject from_invalid(const Variant& variant); -Variant to_invalid(const QJsonObject& object); - -QJsonObject from_bool(const Variant& variant); -Variant to_bool(const QJsonObject& object); - -QJsonObject from_int(const Variant& variant); -Variant to_int(const QJsonObject& object); - -QJsonObject from_string(const Variant& variant); -Variant to_string(const QJsonObject& object); - -QJsonObject from_double(const Variant& variant); -Variant to_double(const QJsonObject& object); - -QJsonObject from_vector_double(const Variant& variant); -Variant to_vector_double(const QJsonObject& object); - -QJsonObject from_comboproperty(const Variant& variant); -Variant to_comboproperty(const QJsonObject& object); - -QJsonObject from_qcolor(const Variant& variant); -Variant to_qcolor(const QJsonObject& object); - -QJsonObject from_extproperty(const Variant& variant); -Variant to_extproperty(const QJsonObject& object); - -QJsonObject from_reallimits(const Variant& variant); -Variant to_reallimits(const QJsonObject& object); - -} // namespace - -JsonVariantConverter::JsonVariantConverter() -{ - m_converters[GUI::Constants::invalid_type_name] = {from_invalid, to_invalid}; - m_converters[GUI::Constants::bool_type_name] = {from_bool, to_bool}; - m_converters[GUI::Constants::int_type_name] = {from_int, to_int}; - m_converters[GUI::Constants::string_type_name] = {from_string, to_string}; - m_converters[GUI::Constants::double_type_name] = {from_double, to_double}; - m_converters[GUI::Constants::vector_double_type_name] = {from_vector_double, to_vector_double}; - m_converters[GUI::Constants::comboproperty_type_name] = {from_comboproperty, to_comboproperty}; - m_converters[GUI::Constants::qcolor_type_name] = {from_qcolor, to_qcolor}; - m_converters[GUI::Constants::extproperty_type_name] = {from_extproperty, to_extproperty}; - m_converters[GUI::Constants::reallimits_type_name] = {from_reallimits, to_reallimits}; -} - -QJsonObject JsonVariantConverter::get_json(const Variant& variant) -{ - const std::string type_name = Utils::VariantName(variant); - - if (m_converters.find(type_name) == m_converters.end()) - throw std::runtime_error("json::get_json() -> Error. Unknown variant type '" + type_name - + "'."); - - return m_converters[type_name].variant_to_json(variant); -} - -Variant JsonVariantConverter::get_variant(const QJsonObject& object) -{ - if (!isVariant(object)) - throw std::runtime_error("json::get_variant() -> Error. Invalid json object"); - - const auto type_name = object[variantTypeKey].toString().toStdString(); - if (m_converters.find(type_name) == m_converters.end()) - throw std::runtime_error("json::get_variant() -> Error. Unknown variant type '" + type_name - + "' in json object."); - - return m_converters[type_name].json_to_variant(object); -} - -//! Returns true if given json object represents variant. - -bool JsonVariantConverter::isVariant(const QJsonObject& object) const -{ - static const QStringList expected = expected_variant_keys(); - return object.keys() == expected; -} - -namespace { - -QStringList expected_variant_keys() -{ - QStringList result = QStringList() << variantTypeKey << variantValueKey; - std::sort(result.begin(), result.end()); - return result; -} - -QJsonObject from_invalid(const Variant& variant) -{ - (void)variant; - QJsonObject result; - result[variantTypeKey] = QString::fromStdString(GUI::Constants::invalid_type_name); - result[variantValueKey] = QJsonValue(); - return result; -} - -Variant to_invalid(const QJsonObject& object) -{ - (void)object; - return Variant(); -} - -QJsonObject from_bool(const Variant& variant) -{ - QJsonObject result; - result[variantTypeKey] = QString::fromStdString(GUI::Constants::bool_type_name); - result[variantValueKey] = variant.value<bool>(); - return result; -} - -Variant to_bool(const QJsonObject& object) -{ - return object[variantValueKey].toVariant(); -} - -QJsonObject from_int(const Variant& variant) -{ - QJsonObject result; - result[variantTypeKey] = QString::fromStdString(GUI::Constants::int_type_name); - result[variantValueKey] = variant.value<int>(); - return result; -} - -Variant to_int(const QJsonObject& object) -{ - // deliberately recreating variant, otherwise it is changing type to Qariant::Double - return Variant::fromValue(object[variantValueKey].toVariant().value<int>()); -} - -QJsonObject from_string(const Variant& variant) -{ - QJsonObject result; - result[variantTypeKey] = QString::fromStdString(GUI::Constants::string_type_name); - result[variantValueKey] = QString::fromStdString(variant.value<std::string>()); - return result; -} - -Variant to_string(const QJsonObject& object) -{ - std::string value = object[variantValueKey].toString().toStdString(); - return Variant::fromValue(value); -} - -QJsonObject from_double(const Variant& variant) -{ - QJsonObject result; - result[variantTypeKey] = QString::fromStdString(GUI::Constants::double_type_name); - result[variantValueKey] = variant.value<double>(); - return result; -} - -Variant to_double(const QJsonObject& object) -{ - // deliberately recreating variant, otherwise it is changing type to qlonglong for - // numbers like 43.0 - return object[variantValueKey].toVariant().value<double>(); -} - -// --- std::vector<double> ------ - -QJsonObject from_vector_double(const Variant& variant) -{ - QJsonObject result; - result[variantTypeKey] = QString::fromStdString(GUI::Constants::vector_double_type_name); - QJsonArray array; - auto data = variant.value<std::vector<double>>(); - std::copy(data.begin(), data.end(), std::back_inserter(array)); - result[variantValueKey] = array; - return result; -} - -Variant to_vector_double(const QJsonObject& object) -{ - std::vector<double> vec; - for (auto x : object[variantValueKey].toArray()) - vec.push_back(x.toDouble()); - return Variant::fromValue(vec); -} - -// --- ComboProperty ------ - -QJsonObject from_comboproperty(const Variant& variant) -{ - QJsonObject result; - result[variantTypeKey] = QString::fromStdString(GUI::Constants::comboproperty_type_name); - auto combo = variant.value<ComboProperty>(); - QJsonObject json_data; - json_data[comboValuesKey] = QString::fromStdString(combo.stringOfValues()); - json_data[comboSelectionKey] = QString::fromStdString(combo.stringOfSelections()); - result[variantValueKey] = json_data; - return result; -} - -Variant to_comboproperty(const QJsonObject& object) -{ - ComboProperty combo; - QJsonObject json_data = object[variantValueKey].toObject(); - combo.setStringOfValues(json_data[comboValuesKey].toString().toStdString()); - combo.setStringOfSelections(json_data[comboSelectionKey].toString().toStdString()); - return Variant::fromValue(combo); -} - -// --- QColor ------ - -QJsonObject from_qcolor(const Variant& variant) -{ - QJsonObject result; - result[variantTypeKey] = QString::fromStdString(GUI::Constants::qcolor_type_name); - auto color = variant.value<QColor>(); - result[variantValueKey] = color.name(QColor::HexArgb); - return result; -} - -Variant to_qcolor(const QJsonObject& object) -{ - return Variant::fromValue(QColor(object[variantValueKey].toString())); -} - -// --- ExternalProperty ------ - -QJsonObject from_extproperty(const Variant& variant) -{ - QJsonObject result; - result[variantTypeKey] = QString::fromStdString(GUI::Constants::extproperty_type_name); - auto extprop = variant.value<ExternalProperty>(); - QJsonObject json_data; - json_data[extPropertyTextKey] = QString::fromStdString(extprop.text()); - json_data[extPropertyColorKey] = extprop.color().name(QColor::HexArgb); - json_data[extPropertyIdKey] = QString::fromStdString(extprop.identifier()); - result[variantValueKey] = json_data; - return result; -} - -Variant to_extproperty(const QJsonObject& object) -{ - QJsonObject json_data = object[variantValueKey].toObject(); - const std::string text = json_data[extPropertyTextKey].toString().toStdString(); - const std::string color = json_data[extPropertyColorKey].toString().toStdString(); - const std::string id = json_data[extPropertyIdKey].toString().toStdString(); - - return Variant::fromValue(ExternalProperty(text, QColor(QString::fromStdString(color)), id)); -} - -// --- RealLimits ------ - -QJsonObject from_reallimits(const Variant& variant) -{ - QJsonObject result; - result[variantTypeKey] = QString::fromStdString(GUI::Constants::reallimits_type_name); - auto limits = variant.value<RealLimits>(); - QJsonObject json_data; - - json_data[realLimitsTextKey] = QString::fromStdString(JsonUtils::ToString(limits)); - json_data[realLimitsMinKey] = limits.lowerLimit(); - json_data[realLimitsMaxKey] = limits.upperLimit(); - - result[variantValueKey] = json_data; - return result; -} - -Variant to_reallimits(const QJsonObject& object) -{ - QJsonObject json_data = object[variantValueKey].toObject(); - const std::string text = json_data[realLimitsTextKey].toString().toStdString(); - const double min = json_data[realLimitsMinKey].toDouble(); - const double max = json_data[realLimitsMaxKey].toDouble(); - - return Variant::fromValue(JsonUtils::CreateLimits(text, min, max)); -} - -} // namespace diff --git a/mvvm/model/mvvm/serialization/jsonvariantconverter.h b/mvvm/model/mvvm/serialization/jsonvariantconverter.h deleted file mode 100644 index 154fc1cf1c8..00000000000 --- a/mvvm/model/mvvm/serialization/jsonvariantconverter.h +++ /dev/null @@ -1,52 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/serialization/jsonvariantconverter.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONVARIANTCONVERTER_H -#define BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONVARIANTCONVERTER_H - -#include "mvvm/core/variant.h" -#include "mvvm/serialization/jsonvariantconverterinterface.h" -#include <functional> -#include <map> -#include <string> - -class QJsonObject; -class QJsonVariant; - -namespace ModelView { - -//! Default converter between supported variants and json objects. - -class MVVM_MODEL_EXPORT JsonVariantConverter : public JsonVariantConverterInterface { -public: - JsonVariantConverter(); - - QJsonObject get_json(const Variant& variant) override; - - Variant get_variant(const QJsonObject& object) override; - - bool isVariant(const QJsonObject& object) const; - -private: - struct Converters { - std::function<QJsonObject(const Variant& variant)> variant_to_json; - std::function<Variant(const QJsonObject& json)> json_to_variant; - }; - - std::map<std::string, Converters> m_converters; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONVARIANTCONVERTER_H diff --git a/mvvm/model/mvvm/serialization/jsonvariantconverterinterface.h b/mvvm/model/mvvm/serialization/jsonvariantconverterinterface.h deleted file mode 100644 index 60f09f99c21..00000000000 --- a/mvvm/model/mvvm/serialization/jsonvariantconverterinterface.h +++ /dev/null @@ -1,38 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/serialization/jsonvariantconverterinterface.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONVARIANTCONVERTERINTERFACE_H -#define BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONVARIANTCONVERTERINTERFACE_H - -#include "mvvm/core/variant.h" -#include "mvvm/model_export.h" - -class QJsonObject; - -namespace ModelView { - -//! Base class for all supported converters of Variant to/from json object - -class MVVM_MODEL_EXPORT JsonVariantConverterInterface { -public: - virtual ~JsonVariantConverterInterface() = default; - - virtual QJsonObject get_json(const Variant&) = 0; - - virtual Variant get_variant(const QJsonObject&) = 0; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_SERIALIZATION_JSONVARIANTCONVERTERINTERFACE_H diff --git a/mvvm/model/mvvm/signals/CMakeLists.txt b/mvvm/model/mvvm/signals/CMakeLists.txt deleted file mode 100644 index c25a84b2bb8..00000000000 --- a/mvvm/model/mvvm/signals/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -target_sources(${library_name} PRIVATE - callback_types.h - callbackcontainer.h - itemlistener.h - itemlistenerbase.cpp - itemlistenerbase.h - itemmapper.cpp - itemmapper.h - modellistener.h - modellistenerbase.cpp - modellistenerbase.h - modelmapper.cpp - modelmapper.h -) diff --git a/mvvm/model/mvvm/signals/callback_types.h b/mvvm/model/mvvm/signals/callback_types.h deleted file mode 100644 index ecb76cfc975..00000000000 --- a/mvvm/model/mvvm/signals/callback_types.h +++ /dev/null @@ -1,38 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/signals/callback_types.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_SIGNALS_CALLBACK_TYPES_H -#define BORNAGAIN_MVVM_MODEL_MVVM_SIGNALS_CALLBACK_TYPES_H - -#include "mvvm/model/tagrow.h" -#include <functional> -#include <string> - -namespace ModelView { - -class SessionItem; -class SessionModel; - -namespace Callbacks { -using slot_t = const void*; -using item_t = std::function<void(SessionItem*)>; -using item_int_t = std::function<void(SessionItem*, int)>; -using item_str_t = std::function<void(SessionItem*, std::string)>; -using item_tagrow_t = std::function<void(SessionItem*, TagRow)>; -using model_t = std::function<void(SessionModel*)>; -} // namespace Callbacks - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_SIGNALS_CALLBACK_TYPES_H diff --git a/mvvm/model/mvvm/signals/callbackcontainer.h b/mvvm/model/mvvm/signals/callbackcontainer.h deleted file mode 100644 index 34cb3073d09..00000000000 --- a/mvvm/model/mvvm/signals/callbackcontainer.h +++ /dev/null @@ -1,75 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/signals/callbackcontainer.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_SIGNALS_CALLBACKCONTAINER_H -#define BORNAGAIN_MVVM_MODEL_MVVM_SIGNALS_CALLBACKCONTAINER_H - -#include "mvvm/model_export.h" -#include "mvvm/signals/callback_types.h" -#include <algorithm> -#include <functional> -#include <list> - -namespace ModelView { - -class SessionItem; -class SessionModel; - -//! Container to hold callbacks in the context of ModelMapper. - -template <typename T, typename U> class SignalBase { -public: - SignalBase() = default; - - void connect(T callback, U client); - - template <typename... Args> void operator()(Args... args); - - void remove_client(U client); - -private: - std::list<std::pair<T, U>> m_callbacks; -}; - -template <typename T, typename U> void SignalBase<T, U>::connect(T callback, U client) -{ - m_callbacks.push_back(std::make_pair(callback, client)); -} - -//! Notify clients using given list of arguments. -template <typename T, typename U> -template <typename... Args> -void SignalBase<T, U>::operator()(Args... args) -{ - for (const auto& f : m_callbacks) { - f.first(args...); - } -} - -//! Remove client from the list to call back. - -template <typename T, typename U> void SignalBase<T, U>::remove_client(U client) -{ - m_callbacks.remove_if( - [client](const std::pair<T, U>& x) -> bool { return (x.second == client ? true : false); }); -} - -//! Callback container for specific client type. - -template <typename T> class Signal : public SignalBase<T, Callbacks::slot_t> { -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_SIGNALS_CALLBACKCONTAINER_H diff --git a/mvvm/model/mvvm/signals/itemlistener.h b/mvvm/model/mvvm/signals/itemlistener.h deleted file mode 100644 index 076b52b05ec..00000000000 --- a/mvvm/model/mvvm/signals/itemlistener.h +++ /dev/null @@ -1,33 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/signals/itemlistener.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_SIGNALS_ITEMLISTENER_H -#define BORNAGAIN_MVVM_MODEL_MVVM_SIGNALS_ITEMLISTENER_H - -#include "mvvm/signals/itemlistenerbase.h" - -namespace ModelView { - -class SessionItem; - -//! Base class to subscribe to signals generated by SessionItem of certin type. - -template <typename T> class ItemListener : public ItemListenerBase { -public: - T* currentItem() const { return static_cast<T*>(item()); } -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_SIGNALS_ITEMLISTENER_H diff --git a/mvvm/model/mvvm/signals/itemlistenerbase.cpp b/mvvm/model/mvvm/signals/itemlistenerbase.cpp deleted file mode 100644 index 830f4703809..00000000000 --- a/mvvm/model/mvvm/signals/itemlistenerbase.cpp +++ /dev/null @@ -1,125 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/signals/itemlistenerbase.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "itemlistenerbase.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/signals/itemmapper.h" - -ModelView::ItemListenerBase::ItemListenerBase(ModelView::SessionItem* item) -{ - setItem(item); -} - -ModelView::ItemListenerBase::~ItemListenerBase() -{ - if (m_item) - m_item->mapper()->unsubscribe(this); -} - -void ModelView::ItemListenerBase::setItem(ModelView::SessionItem* item) -{ - if (m_item == item) - return; - - unsubscribe_from_current(); - - m_item = item; - - if (!m_item) - return; - - auto on_item_destroy = [this](auto) { - m_item = nullptr; - unsubscribe(); - }; - m_item->mapper()->setOnItemDestroy(on_item_destroy, this); - - subscribe(); -} - -void ModelView::ItemListenerBase::setOnItemDestroy(ModelView::Callbacks::item_t f) -{ - item()->mapper()->setOnItemDestroy(f, this); -} - -//! Sets callback to be notified on item's data change. -//! Callback will be called with (SessionItem*, data_role). - -void ModelView::ItemListenerBase::setOnDataChange(ModelView::Callbacks::item_int_t f) -{ - item()->mapper()->setOnDataChange(f, this); -} - -//! Sets callback to be notified on item's property change. -//! Callback will be called with (compound_item, property_name). - -void ModelView::ItemListenerBase::setOnPropertyChange(ModelView::Callbacks::item_str_t f) -{ - item()->mapper()->setOnPropertyChange(f, this); -} - -//! Sets callback to be notified on item's children property change. -//! Callback will be called with (compound_item, property_name). For MultiLayer containing the -//! layer with "thickness" property, the signal will be triggered on thickness change using -//! (layeritem*, "thickness") as callback parameters. - -void ModelView::ItemListenerBase::setOnChildPropertyChange(ModelView::Callbacks::item_str_t f) -{ - item()->mapper()->setOnChildPropertyChange(f, this); -} - -//! Sets callback to be notified on child insertion. -//! Callback will be called with (compound_item, tag, row). For MultiLayer containing the T_LAYERS -//! tag, the signal will be triggered on layer insertion with -//! (multilayer*, {T_LAYER, row}) as callback parameters. - -void ModelView::ItemListenerBase::setOnItemInserted(ModelView::Callbacks::item_tagrow_t f) -{ - item()->mapper()->setOnItemInserted(f, this); -} - -//! Sets callback to be notified on child removal. -//! Callback will be called with (compound_item, tag, row). For MultiLayer containing the T_LAYERS -//! tag, the signal will be triggered on layer removal with -//! (multilayer*, {T_LAYER, oldrow}) as callback parameters. - -void ModelView::ItemListenerBase::setOnItemRemoved(ModelView::Callbacks::item_tagrow_t f) -{ - item()->mapper()->setOnItemRemoved(f, this); -} - -void ModelView::ItemListenerBase::setOnAboutToRemoveItem(ModelView::Callbacks::item_tagrow_t f) -{ - item()->mapper()->setOnAboutToRemoveItem(f, this); -} - -//! Sets callback to be notified when row is about to be removed. -//! Callback will be called with (compound_item, tagrow). For MultiLayer containing the T_LAYERS -//! tag, the signal will be triggered on layer deletion with -//! (multilayer*, {T_LAYER, row}) as callback parameters. - -ModelView::SessionItem* ModelView::ItemListenerBase::item() const -{ - return m_item; -} - -void ModelView::ItemListenerBase::unsubscribe_from_current() -{ - if (!m_item) - return; - - unsubscribe(); - - m_item->mapper()->unsubscribe(this); -} diff --git a/mvvm/model/mvvm/signals/itemlistenerbase.h b/mvvm/model/mvvm/signals/itemlistenerbase.h deleted file mode 100644 index cc92626522e..00000000000 --- a/mvvm/model/mvvm/signals/itemlistenerbase.h +++ /dev/null @@ -1,61 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/signals/itemlistenerbase.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_SIGNALS_ITEMLISTENERBASE_H -#define BORNAGAIN_MVVM_MODEL_MVVM_SIGNALS_ITEMLISTENERBASE_H - -#include "mvvm/model_export.h" -#include "mvvm/signals/callback_types.h" - -namespace ModelView { - -class SessionItem; - -//! Provides sets of methods to subscribe to various signals generated by SessionItem. -//! Used to implement user actions on item change. - -//! Automatically tracks the time of life of SessionItem. Unsubscribes from the item on -//! own destruction. Can be switched from tracking one item to another of the same type. - -class MVVM_MODEL_EXPORT ItemListenerBase { -public: - explicit ItemListenerBase(SessionItem* item = nullptr); - virtual ~ItemListenerBase(); - - ItemListenerBase& operator=(const ItemListenerBase& other) = delete; - ItemListenerBase(const ItemListenerBase& other) = delete; - - void setItem(SessionItem* item); - - void setOnItemDestroy(Callbacks::item_t f); - void setOnDataChange(Callbacks::item_int_t f); - void setOnPropertyChange(Callbacks::item_str_t f); - void setOnChildPropertyChange(Callbacks::item_str_t f); - void setOnItemInserted(Callbacks::item_tagrow_t f); - void setOnItemRemoved(Callbacks::item_tagrow_t f); - void setOnAboutToRemoveItem(Callbacks::item_tagrow_t f); - -protected: - virtual void subscribe() {} //! For necessary manipulations on new item. - virtual void unsubscribe() {} //! For necessary manipulations on unsubscription. - SessionItem* item() const; - -private: - void unsubscribe_from_current(); - SessionItem* m_item{nullptr}; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_SIGNALS_ITEMLISTENERBASE_H diff --git a/mvvm/model/mvvm/signals/itemmapper.cpp b/mvvm/model/mvvm/signals/itemmapper.cpp deleted file mode 100644 index 920a484062f..00000000000 --- a/mvvm/model/mvvm/signals/itemmapper.cpp +++ /dev/null @@ -1,232 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/signals/itemmapper.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/signals/itemmapper.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/signals/callbackcontainer.h" -#include "mvvm/signals/modelmapper.h" -#include <stdexcept> - -using namespace ModelView; - -struct ItemMapper::ItemMapperImpl { - ItemMapper* m_itemMapper{nullptr}; - Signal<Callbacks::item_t> m_on_item_destroy; - Signal<Callbacks::item_int_t> m_on_data_change; - Signal<Callbacks::item_str_t> m_on_property_change; - Signal<Callbacks::item_str_t> m_on_child_property_change; - Signal<Callbacks::item_tagrow_t> m_on_item_inserted; - Signal<Callbacks::item_tagrow_t> m_on_item_removed; - Signal<Callbacks::item_tagrow_t> m_on_about_to_remove_item; - - bool m_active{true}; - SessionItem* m_item{nullptr}; - - ItemMapperImpl(ItemMapper* item_mapper) : m_itemMapper(item_mapper) {} - - void unsubscribe(Callbacks::slot_t client) - { - m_on_item_destroy.remove_client(client); - m_on_data_change.remove_client(client); - m_on_property_change.remove_client(client); - m_on_child_property_change.remove_client(client); - m_on_item_inserted.remove_client(client); - m_on_item_removed.remove_client(client); - m_on_about_to_remove_item.remove_client(client); - } - - int nestlingDepth(SessionItem* item, int level = 0) - { - if (item == nullptr || item == m_itemMapper->model()->rootItem()) - return -1; - if (item == m_item) - return level; - return nestlingDepth(item->parent(), level + 1); - } - - //! Processes signals from the model when item data changed. - - void processDataChange(SessionItem* item, int role) - { - int nestling = nestlingDepth(item); - - // own item data changed - if (nestling == 0) - callOnDataChange(item, role); - - // data of item's property changed - if (nestling == 1) - callOnPropertyChange(m_item, m_item->tagRowOfItem(item).tag); - - // child property changed - if (nestling == 2) { - if (auto parent = item->parent()) - callOnChildPropertyChange(parent, parent->tagRowOfItem(item).tag); - } - } - - void processItemInserted(SessionItem* parent, const TagRow& tagrow) - { - if (parent == m_item) - callOnItemInserted(m_item, tagrow); - } - - void processItemRemoved(SessionItem* parent, const TagRow& tagrow) - { - if (parent == m_item) - callOnItemRemoved(m_item, tagrow); - } - - void processAboutToRemoveItem(SessionItem* parent, const TagRow& tagrow) - { - if (parent == m_item) - callOnAboutToRemoveItem(m_item, tagrow); - } - - //! Notifies all callbacks subscribed to "item data is changed" event. - - void callOnDataChange(SessionItem* item, int role) - { - if (m_active) - m_on_data_change(item, role); - } - - //! Notifies all callbacks subscribed to "item property is changed" event. - - void callOnPropertyChange(SessionItem* item, const std::string& property_name) - { - if (m_active) - m_on_property_change(item, property_name); - } - - //! Notifies all callbacks subscribed to "child property changed" event. - - void callOnChildPropertyChange(SessionItem* item, const std::string& property_name) - { - if (m_active) - m_on_child_property_change(item, property_name); - } - - //! Notifies all callbacks subscribed to "on row inserted" event. - - void callOnItemInserted(SessionItem* parent, const TagRow& tagrow) - { - if (m_active) - m_on_item_inserted(parent, tagrow); - } - - //! Notifies all callbacks subscribed to "on row removed" event. - - void callOnItemRemoved(SessionItem* parent, const TagRow& tagrow) - { - if (m_active) - m_on_item_removed(parent, tagrow); - } - - //! Notifies all callbacks subscribed to "on row about to be removed". - - void callOnAboutToRemoveItem(SessionItem* parent, const TagRow& tagrow) - { - if (m_active) - m_on_about_to_remove_item(parent, tagrow); - } -}; - -ItemMapper::ItemMapper(SessionItem* item) - : ModelListener(item->model()), p_impl(std::make_unique<ItemMapperImpl>(this)) -{ - if (!item) - throw std::runtime_error("ItemMapper::ItemMapper() -> Not initialized item"); - - if (!item->model()) - throw std::runtime_error("ItemMapper::ItemMapper() -> Item doesn't have model"); - - p_impl->m_item = item; - - auto on_data_change = [this](auto item, auto role) { p_impl->processDataChange(item, role); }; - ModelListener::setOnDataChange(on_data_change); - - auto on_item_inserted = [this](auto item, auto tagrow) { - p_impl->processItemInserted(item, tagrow); - }; - ModelListener::setOnItemInserted(on_item_inserted, this); - - auto on_item_removed = [this](auto item, auto tagrow) { - p_impl->processItemRemoved(item, tagrow); - }; - ModelListener::setOnItemRemoved(on_item_removed, this); - - auto on_about_to_remove_item = [this](auto item, auto tagrow) { - p_impl->processAboutToRemoveItem(item, tagrow); - }; - ModelListener::setOnAboutToRemoveItem(on_about_to_remove_item, this); -} - -ItemMapper::~ItemMapper() = default; - -void ItemMapper::setOnItemDestroy(Callbacks::item_t f, Callbacks::slot_t owner) -{ - p_impl->m_on_item_destroy.connect(std::move(f), owner); -} - -void ItemMapper::setOnDataChange(Callbacks::item_int_t f, Callbacks::slot_t owner) -{ - p_impl->m_on_data_change.connect(std::move(f), owner); -} - -void ItemMapper::setOnPropertyChange(Callbacks::item_str_t f, Callbacks::slot_t owner) -{ - p_impl->m_on_property_change.connect(std::move(f), owner); -} - -void ItemMapper::setOnChildPropertyChange(Callbacks::item_str_t f, Callbacks::slot_t owner) -{ - p_impl->m_on_child_property_change.connect(std::move(f), owner); -} - -void ItemMapper::setOnItemInserted(Callbacks::item_tagrow_t f, Callbacks::slot_t owner) -{ - p_impl->m_on_item_inserted.connect(std::move(f), owner); -} - -void ItemMapper::setOnItemRemoved(Callbacks::item_tagrow_t f, Callbacks::slot_t owner) -{ - p_impl->m_on_item_removed.connect(std::move(f), owner); -} - -void ItemMapper::setOnAboutToRemoveItem(Callbacks::item_tagrow_t f, Callbacks::slot_t owner) -{ - p_impl->m_on_about_to_remove_item.connect(std::move(f), owner); -} - -void ItemMapper::unsubscribe(Callbacks::slot_t client) -{ - p_impl->unsubscribe(client); -} - -//! Sets activity flag to given value. Will disable all callbacks if false. - -void ItemMapper::setActive(bool value) -{ - p_impl->m_active = value; -} - -//! Calls all callbacks subscribed to "item is destroyed" event. - -void ItemMapper::callOnItemDestroy() -{ - if (p_impl->m_active) - p_impl->m_on_item_destroy(p_impl->m_item); -} diff --git a/mvvm/model/mvvm/signals/itemmapper.h b/mvvm/model/mvvm/signals/itemmapper.h deleted file mode 100644 index 1d48b5dd4dd..00000000000 --- a/mvvm/model/mvvm/signals/itemmapper.h +++ /dev/null @@ -1,59 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/signals/itemmapper.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_SIGNALS_ITEMMAPPER_H -#define BORNAGAIN_MVVM_MODEL_MVVM_SIGNALS_ITEMMAPPER_H - -#include "mvvm/interfaces/itemlistenerinterface.h" -#include "mvvm/signals/modellistener.h" -#include <memory> - -namespace ModelView { - -class SessionItem; - -//! Provides notifications on various changes for a specific item. -//! ItemMapper listens signals coming from the model (i.e. via ModelMapper) and processes only whose -//! signals which are related to the given item. Notifies all interested subscribers about things -//! going with the item and its relatives. - -class MVVM_MODEL_EXPORT ItemMapper : public ItemListenerInterface, - private ModelListener<SessionModel> { -public: - ItemMapper(SessionItem* item); - ~ItemMapper(); - - void setOnItemDestroy(Callbacks::item_t f, Callbacks::slot_t owner) override; - void setOnDataChange(Callbacks::item_int_t f, Callbacks::slot_t owner) override; - void setOnPropertyChange(Callbacks::item_str_t f, Callbacks::slot_t owner) override; - void setOnChildPropertyChange(Callbacks::item_str_t f, Callbacks::slot_t owner) override; - void setOnItemInserted(Callbacks::item_tagrow_t f, Callbacks::slot_t owner) override; - void setOnItemRemoved(Callbacks::item_tagrow_t f, Callbacks::slot_t owner) override; - void setOnAboutToRemoveItem(Callbacks::item_tagrow_t f, Callbacks::slot_t owner) override; - - void unsubscribe(Callbacks::slot_t client) override; - - void setActive(bool value); - -private: - friend class SessionItem; - void callOnItemDestroy(); - - struct ItemMapperImpl; - std::unique_ptr<ItemMapperImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_SIGNALS_ITEMMAPPER_H diff --git a/mvvm/model/mvvm/signals/modellistener.h b/mvvm/model/mvvm/signals/modellistener.h deleted file mode 100644 index 594102480ea..00000000000 --- a/mvvm/model/mvvm/signals/modellistener.h +++ /dev/null @@ -1,35 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/signals/modellistener.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_SIGNALS_MODELLISTENER_H -#define BORNAGAIN_MVVM_MODEL_MVVM_SIGNALS_MODELLISTENER_H - -#include "mvvm/signals/modellistenerbase.h" - -namespace ModelView { - -class SessionItem; - -//! Templated class for all objects willing to listen for changes in concrete SessionModel. - -template <typename T> class ModelListener : public ModelListenerBase { -public: - ModelListener(T* session_model) : ModelListenerBase(session_model) {} - - T* model() const { return static_cast<T*>(m_model); } -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_SIGNALS_MODELLISTENER_H diff --git a/mvvm/model/mvvm/signals/modellistenerbase.cpp b/mvvm/model/mvvm/signals/modellistenerbase.cpp deleted file mode 100644 index 4a8afb3663e..00000000000 --- a/mvvm/model/mvvm/signals/modellistenerbase.cpp +++ /dev/null @@ -1,92 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/signals/modellistenerbase.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/signals/modellistenerbase.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/signals/modelmapper.h" -#include <stdexcept> - -using namespace ModelView; - -ModelListenerBase::ModelListenerBase(SessionModel* model) : m_model(model) -{ - if (!m_model) - throw std::runtime_error("Error in ModelListenerBase: no model defined"); - setOnModelDestroyed([this](SessionModel*) { m_model = nullptr; }); -} - -ModelListenerBase::~ModelListenerBase() -{ - unsubscribe(); -} - -//! Sets callback to be notified on item's data change. The callback will be called -//! with (SessionItem*, data_role). - -void ModelListenerBase::setOnDataChange(ModelView::Callbacks::item_int_t f, Callbacks::slot_t) -{ - m_model->mapper()->setOnDataChange(f, this); -} - -//! Sets callback to be notified on item insert. The callback will be called with -//! (SessionItem* parent, tagrow), where 'tagrow' denotes inserted child position. - -void ModelListenerBase::setOnItemInserted(ModelView::Callbacks::item_tagrow_t f, Callbacks::slot_t) -{ - m_model->mapper()->setOnItemInserted(f, this); -} - -//! Sets callback to be notified on item remove. The callback will be called with -//! (SessionItem* parent, tagrow), where 'tagrow' denotes child position before the removal. - -void ModelListenerBase::setOnItemRemoved(ModelView::Callbacks::item_tagrow_t f, Callbacks::slot_t) -{ - m_model->mapper()->setOnItemRemoved(f, this); -} - -//! Sets callback to be notified when the item is about to be removed. The callback will be called -//! with (SessionItem* parent, tagrow), where 'tagrow' denotes child position being removed. - -void ModelListenerBase::setOnAboutToRemoveItem(ModelView::Callbacks::item_tagrow_t f, - Callbacks::slot_t) -{ - m_model->mapper()->setOnAboutToRemoveItem(f, this); -} - -//! Sets the callback for notifications on model destruction. - -void ModelListenerBase::setOnModelDestroyed(Callbacks::model_t f, Callbacks::slot_t) -{ - m_model->mapper()->setOnModelDestroyed(f, this); -} - -//! Sets the callback to be notified before model's full reset (root item recreated). - -void ModelListenerBase::setOnModelAboutToBeReset(Callbacks::model_t f, Callbacks::slot_t) -{ - m_model->mapper()->setOnModelAboutToBeReset(f, this); -} - -//! Sets the callback to be notified after model was fully reset (root item recreated). - -void ModelListenerBase::setOnModelReset(ModelView::Callbacks::model_t f, Callbacks::slot_t) -{ - m_model->mapper()->setOnModelReset(f, this); -} - -void ModelListenerBase::unsubscribe(Callbacks::slot_t) -{ - if (m_model) - m_model->mapper()->unsubscribe(this); -} diff --git a/mvvm/model/mvvm/signals/modellistenerbase.h b/mvvm/model/mvvm/signals/modellistenerbase.h deleted file mode 100644 index cdd2f940e9b..00000000000 --- a/mvvm/model/mvvm/signals/modellistenerbase.h +++ /dev/null @@ -1,51 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/signals/modellistenerbase.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_SIGNALS_MODELLISTENERBASE_H -#define BORNAGAIN_MVVM_MODEL_MVVM_SIGNALS_MODELLISTENERBASE_H - -#include "mvvm/interfaces/modellistenerinterface.h" - -namespace ModelView { - -class SessionModel; - -//! Provides sets of methods to subscribe to various signals generated by SessionModel. -//! Automatically tracks the time of life of SessionModel. Unsubscribes from the model on -//! own destruction. - -class MVVM_MODEL_EXPORT ModelListenerBase : public ModelListenerInterface { -public: - ModelListenerBase(SessionModel* model); - ~ModelListenerBase() override; - - // 'client' is not used here, since 'this' is used - - void setOnDataChange(Callbacks::item_int_t f, Callbacks::slot_t client = {}) override; - void setOnItemInserted(Callbacks::item_tagrow_t f, Callbacks::slot_t client = {}) override; - void setOnItemRemoved(Callbacks::item_tagrow_t f, Callbacks::slot_t client = {}) override; - void setOnAboutToRemoveItem(Callbacks::item_tagrow_t f, Callbacks::slot_t client = {}) override; - void setOnModelDestroyed(Callbacks::model_t f, Callbacks::slot_t client = {}) override; - void setOnModelAboutToBeReset(Callbacks::model_t f, Callbacks::slot_t client = {}) override; - void setOnModelReset(Callbacks::model_t f, Callbacks::slot_t client = {}) override; - - void unsubscribe(Callbacks::slot_t client = {}) override; - -protected: - SessionModel* m_model{nullptr}; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_SIGNALS_MODELLISTENERBASE_H diff --git a/mvvm/model/mvvm/signals/modelmapper.cpp b/mvvm/model/mvvm/signals/modelmapper.cpp deleted file mode 100644 index 2fef63ad9dc..00000000000 --- a/mvvm/model/mvvm/signals/modelmapper.cpp +++ /dev/null @@ -1,158 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/signals/modelmapper.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/signals/modelmapper.h" -#include "mvvm/signals/callbackcontainer.h" - -using namespace ModelView; - -struct ModelMapper::ModelMapperImpl { - Signal<Callbacks::item_int_t> m_on_data_change; - Signal<Callbacks::item_tagrow_t> m_on_item_inserted; - Signal<Callbacks::item_tagrow_t> m_on_item_removed; - Signal<Callbacks::item_tagrow_t> m_on_item_about_removed; - Signal<Callbacks::model_t> m_on_model_destroyed; - Signal<Callbacks::model_t> m_on_model_about_reset; - Signal<Callbacks::model_t> m_on_model_reset; - - bool m_active{true}; - SessionModel* m_model{nullptr}; - - ModelMapperImpl(SessionModel* model) : m_model(model){}; - - void unsubscribe(Callbacks::slot_t client) - { - m_on_data_change.remove_client(client); - m_on_item_inserted.remove_client(client); - m_on_item_removed.remove_client(client); - m_on_item_about_removed.remove_client(client); - m_on_model_destroyed.remove_client(client); - m_on_model_about_reset.remove_client(client); - m_on_model_reset.remove_client(client); - } -}; - -ModelMapper::ModelMapper(SessionModel* model) : p_impl(std::make_unique<ModelMapperImpl>(model)) {} - -ModelMapper::~ModelMapper() = default; - -//! Sets callback to be notified on item's data change. The callback will be called -//! with (SessionItem*, data_role). - -void ModelMapper::setOnDataChange(Callbacks::item_int_t f, Callbacks::slot_t client) -{ - p_impl->m_on_data_change.connect(std::move(f), client); -} - -//! Sets callback to be notified on item insert. The callback will be called with -//! (SessionItem* parent, tagrow), where 'tagrow' denotes inserted child position. - -void ModelMapper::setOnItemInserted(Callbacks::item_tagrow_t f, Callbacks::slot_t client) -{ - p_impl->m_on_item_inserted.connect(std::move(f), client); -} - -//! Sets callback to be notified on item remove. The callback will be called with -//! (SessionItem* parent, tagrow), where 'tagrow' denotes child position before the removal. - -void ModelMapper::setOnItemRemoved(Callbacks::item_tagrow_t f, Callbacks::slot_t client) -{ - p_impl->m_on_item_removed.connect(std::move(f), client); -} - -//! Sets callback to be notified when the item is about to be removed. The callback will be called -//! with (SessionItem* parent, tagrow), where 'tagrow' denotes child position being removed. - -void ModelMapper::setOnAboutToRemoveItem(Callbacks::item_tagrow_t f, Callbacks::slot_t client) -{ - p_impl->m_on_item_about_removed.connect(std::move(f), client); -} - -//! Sets the callback for notifications on model destruction. - -void ModelMapper::setOnModelDestroyed(Callbacks::model_t f, Callbacks::slot_t client) -{ - p_impl->m_on_model_destroyed.connect(std::move(f), client); -} - -//! Sets the callback to be notified just before the reset of the root item. - -void ModelMapper::setOnModelAboutToBeReset(Callbacks::model_t f, Callbacks::slot_t client) -{ - p_impl->m_on_model_about_reset.connect(std::move(f), client); -} - -//! Sets the callback to be notified right after the root item recreation. - -void ModelMapper::setOnModelReset(Callbacks::model_t f, Callbacks::slot_t client) -{ - p_impl->m_on_model_reset.connect(std::move(f), client); -} - -//! Sets activity flag to given value. Will disable all callbacks if false. - -void ModelMapper::setActive(bool value) -{ - p_impl->m_active = value; -} - -//! Removes given client from all subscriptions. - -void ModelMapper::unsubscribe(Callbacks::slot_t client) -{ - p_impl->unsubscribe(client); -} - -//! Notifies all callbacks subscribed to "item data is changed" event. - -void ModelMapper::callOnDataChange(SessionItem* item, int role) -{ - if (p_impl->m_active) - p_impl->m_on_data_change(item, role); -} - -//! Notifies all callbacks subscribed to "item data is changed" event. - -void ModelMapper::callOnItemInserted(SessionItem* parent, const TagRow& tagrow) -{ - if (p_impl->m_active) - p_impl->m_on_item_inserted(parent, tagrow); -} - -void ModelMapper::callOnItemRemoved(SessionItem* parent, const TagRow& tagrow) -{ - if (p_impl->m_active) - p_impl->m_on_item_removed(parent, tagrow); -} - -void ModelMapper::callOnItemAboutToBeRemoved(SessionItem* parent, const TagRow& tagrow) -{ - if (p_impl->m_active) - p_impl->m_on_item_about_removed(parent, tagrow); -} - -void ModelMapper::callOnModelDestroyed() -{ - p_impl->m_on_model_destroyed(p_impl->m_model); -} - -void ModelMapper::callOnModelAboutToBeReset() -{ - p_impl->m_on_model_about_reset(p_impl->m_model); -} - -void ModelMapper::callOnModelReset() -{ - p_impl->m_on_model_reset(p_impl->m_model); -} diff --git a/mvvm/model/mvvm/signals/modelmapper.h b/mvvm/model/mvvm/signals/modelmapper.h deleted file mode 100644 index 7e84ad4169e..00000000000 --- a/mvvm/model/mvvm/signals/modelmapper.h +++ /dev/null @@ -1,67 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/signals/modelmapper.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_SIGNALS_MODELMAPPER_H -#define BORNAGAIN_MVVM_MODEL_MVVM_SIGNALS_MODELMAPPER_H - -#include "mvvm/interfaces/modellistenerinterface.h" -#include <memory> - -namespace ModelView { - -class SessionItem; -class SessionModel; - -//! Provides notifications on various SessionModel changes. -//! Allows to subscribe to SessionModel's changes, and triggers notifications. - -class MVVM_MODEL_EXPORT ModelMapper : public ModelListenerInterface { -public: - ModelMapper(SessionModel* model); - ~ModelMapper(); - - ModelMapper(const ModelMapper& other) = delete; - ModelMapper& operator=(const ModelMapper& other) = delete; - - void setOnDataChange(Callbacks::item_int_t f, Callbacks::slot_t client) override; - void setOnItemInserted(Callbacks::item_tagrow_t f, Callbacks::slot_t client) override; - void setOnItemRemoved(Callbacks::item_tagrow_t f, Callbacks::slot_t client) override; - void setOnAboutToRemoveItem(Callbacks::item_tagrow_t f, Callbacks::slot_t client) override; - void setOnModelDestroyed(Callbacks::model_t f, Callbacks::slot_t client) override; - void setOnModelAboutToBeReset(Callbacks::model_t f, Callbacks::slot_t client) override; - void setOnModelReset(Callbacks::model_t f, Callbacks::slot_t client) override; - - void setActive(bool value); - - void unsubscribe(Callbacks::slot_t client) override; - -private: - friend class SessionModel; - friend class SessionItem; - - void callOnDataChange(SessionItem* item, int role); - void callOnItemInserted(SessionItem* parent, const TagRow& tagrow); - void callOnItemRemoved(SessionItem* parent, const TagRow& tagrow); - void callOnItemAboutToBeRemoved(SessionItem* parent, const TagRow& tagrow); - void callOnModelDestroyed(); - void callOnModelAboutToBeReset(); - void callOnModelReset(); - - struct ModelMapperImpl; - std::unique_ptr<ModelMapperImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_SIGNALS_MODELMAPPER_H diff --git a/mvvm/model/mvvm/standarditems/CMakeLists.txt b/mvvm/model/mvvm/standarditems/CMakeLists.txt deleted file mode 100644 index 69120e60574..00000000000 --- a/mvvm/model/mvvm/standarditems/CMakeLists.txt +++ /dev/null @@ -1,27 +0,0 @@ -target_sources(${library_name} PRIVATE - axisitems.cpp - axisitems.h - colormapitem.cpp - colormapitem.h - colormapviewportitem.cpp - colormapviewportitem.h - containeritem.cpp - containeritem.h - data1ditem.cpp - data1ditem.h - data2ditem.cpp - data2ditem.h - graphitem.cpp - graphitem.h - graphviewportitem.cpp - graphviewportitem.h - linkeditem.cpp - linkeditem.h - plottableitems.cpp - plottableitems.h - standarditemincludes.h - vectoritem.cpp - vectoritem.h - viewportitem.cpp - viewportitem.h -) diff --git a/mvvm/model/mvvm/standarditems/axisitems.cpp b/mvvm/model/mvvm/standarditems/axisitems.cpp deleted file mode 100644 index b06e9b6dbed..00000000000 --- a/mvvm/model/mvvm/standarditems/axisitems.cpp +++ /dev/null @@ -1,149 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/standarditems/axisitems.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/standarditems/axisitems.h" -#include "mvvm/standarditems/plottableitems.h" - -namespace { -const double default_axis_min = 0.0; -const double default_axis_max = 1.0; -} // namespace - -using namespace ModelView; - -BasicAxisItem::BasicAxisItem(const std::string& model_type) : CompoundItem(model_type) {} - -void BasicAxisItem::register_min_max() -{ - addProperty(P_MIN, default_axis_min)->setDisplayName("Min"); - addProperty(P_MAX, default_axis_max)->setDisplayName("Max"); -} - -// --- ViewportAxisItem ------------------------------------------------------ - -ViewportAxisItem::ViewportAxisItem(const std::string& model_type) : BasicAxisItem(model_type) -{ - addProperty<TextItem>(P_TITLE)->setDisplayName("Title"); - register_min_max(); - addProperty(P_IS_LOG, false)->setDisplayName("log10"); -} - -//! Returns pair of lower, upper axis range. - -std::pair<double, double> ViewportAxisItem::range() const -{ - return std::make_pair(property<double>(P_MIN), property<double>(P_MAX)); -} - -//! Sets lower, upper range of axis to given values. - -void ViewportAxisItem::set_range(double lower, double upper) -{ - setProperty(P_MIN, lower); - setProperty(P_MAX, upper); -} - -bool ViewportAxisItem::is_in_log() const -{ - return property<bool>(P_IS_LOG); -} - -// --- BinnedAxisItem ------------------------------------------------------ - -BinnedAxisItem::BinnedAxisItem(const std::string& model_type) : BasicAxisItem(model_type) {} - -// --- FixedBinAxisItem ------------------------------------------------------ - -FixedBinAxisItem::FixedBinAxisItem(const std::string& model_type) : BinnedAxisItem(model_type) -{ - addProperty(P_NBINS, 1)->setDisplayName("Nbins"); - register_min_max(); -} - -void FixedBinAxisItem::setParameters(int nbins, double xmin, double xmax) -{ - setProperty(P_NBINS, nbins); - setProperty(P_MIN, xmin); - setProperty(P_MAX, xmax); -} - -std::unique_ptr<FixedBinAxisItem> FixedBinAxisItem::create(int nbins, double xmin, double xmax) -{ - auto result = std::make_unique<FixedBinAxisItem>(); - result->setParameters(nbins, xmin, xmax); - return result; -} - -std::pair<double, double> FixedBinAxisItem::range() const -{ - return std::make_pair(property<double>(P_MIN), property<double>(P_MAX)); -} - -int FixedBinAxisItem::size() const -{ - return property<int>(P_NBINS); -} - -std::vector<double> FixedBinAxisItem::binCenters() const -{ - std::vector<double> result; - int nbins = property<int>(P_NBINS); - double start = property<double>(P_MIN); - double end = property<double>(P_MAX); - double step = (end - start) / nbins; - - result.resize(static_cast<size_t>(nbins), 0.0); - for (size_t i = 0; i < static_cast<size_t>(nbins); ++i) - result[i] = start + step * (i + 0.5); - - return result; -} - -// --- PointwiseAxisItem ------------------------------------------------------ - -PointwiseAxisItem::PointwiseAxisItem(const std::string& model_type) : BinnedAxisItem(model_type) -{ - // vector of points matching default xmin, xmax - setData(std::vector<double>{default_axis_min, default_axis_max}); - setEditable(false); // prevent editing in widgets, since there is no corresponding editor -} - -void PointwiseAxisItem::setParameters(const std::vector<double>& data) -{ - setData(data); -} - -std::unique_ptr<PointwiseAxisItem> PointwiseAxisItem::create(const std::vector<double>& data) -{ - auto result = std::make_unique<PointwiseAxisItem>(); - result->setParameters(data); - return result; -} - -std::pair<double, double> PointwiseAxisItem::range() const -{ - auto data = binCenters(); - return binCenters().empty() ? std::make_pair(default_axis_min, default_axis_max) - : std::make_pair(data.front(), data.back()); -} - -int PointwiseAxisItem::size() const -{ - return binCenters().size(); -} - -std::vector<double> PointwiseAxisItem::binCenters() const -{ - return data<std::vector<double>>(); -} diff --git a/mvvm/model/mvvm/standarditems/axisitems.h b/mvvm/model/mvvm/standarditems/axisitems.h deleted file mode 100644 index 3b65ca68a72..00000000000 --- a/mvvm/model/mvvm/standarditems/axisitems.h +++ /dev/null @@ -1,111 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/standarditems/axisitems.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_AXISITEMS_H -#define BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_AXISITEMS_H - -//! @file mvvm/model/mvvm/standarditems/axisitems.h -//! Collection of axis items for 1D and 2D data/plotting support. - -#include "mvvm/model/compounditem.h" -#include <memory> -#include <vector> - -namespace ModelView { - -//! Base class for all axes items. Has min, max defined, but nothing else. - -class MVVM_MODEL_EXPORT BasicAxisItem : public CompoundItem { -public: - static inline const std::string P_MIN = "P_MIN"; - static inline const std::string P_MAX = "P_MAX"; - - explicit BasicAxisItem(const std::string& model_type); - -protected: - void register_min_max(); -}; - -//! Item to represent viewport axis. -//! Serves as a counterpart of QCPAxis from QCustomPlot. Intended to cary title, fonts etc. - -class MVVM_MODEL_EXPORT ViewportAxisItem : public BasicAxisItem { -public: - static inline const std::string P_TITLE = "P_TITLE"; - static inline const std::string P_IS_LOG = "P_IS_LOG"; - explicit ViewportAxisItem(const std::string& model_type = GUI::Constants::ViewportAxisItemType); - - std::pair<double, double> range() const; - - void set_range(double lower, double upper); - - bool is_in_log() const; -}; - -//! Item to represent an axis with arbitrary binning. -//! Base class to define an axis with specific binning (fixed, variable). Used in Data1DItem and -//! Data2Ditem to store 1d and 2d data. Doesn't carry any appearance info (e.g. axis title, label -//! size, etc) and thus not intended for direct plotting. - -class MVVM_MODEL_EXPORT BinnedAxisItem : public BasicAxisItem { -public: - explicit BinnedAxisItem(const std::string& model_type); - - virtual std::pair<double, double> range() const = 0; - - virtual int size() const = 0; - - virtual std::vector<double> binCenters() const = 0; -}; - -//! Item to represent fixed bin axis. -//! Defines an axis with equidistant binning. - -class MVVM_MODEL_EXPORT FixedBinAxisItem : public BinnedAxisItem { -public: - static inline const std::string P_NBINS = "P_NBINS"; - FixedBinAxisItem(const std::string& model_type = GUI::Constants::FixedBinAxisItemType); - - void setParameters(int nbins, double xmin, double xmax); - - static std::unique_ptr<FixedBinAxisItem> create(int nbins, double xmin, double xmax); - - std::pair<double, double> range() const override; - - int size() const override; - - std::vector<double> binCenters() const override; -}; - -//! Item to represent pointwise axis. -//! Defines an axis via array of points representing point coordinates. - -class MVVM_MODEL_EXPORT PointwiseAxisItem : public BinnedAxisItem { -public: - PointwiseAxisItem(const std::string& model_type = GUI::Constants::PointwiseAxisItemType); - - void setParameters(const std::vector<double>& data); - - static std::unique_ptr<PointwiseAxisItem> create(const std::vector<double>& data); - - std::pair<double, double> range() const override; - - int size() const override; - - std::vector<double> binCenters() const override; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_AXISITEMS_H diff --git a/mvvm/model/mvvm/standarditems/colormapitem.cpp b/mvvm/model/mvvm/standarditems/colormapitem.cpp deleted file mode 100644 index 583fd6df127..00000000000 --- a/mvvm/model/mvvm/standarditems/colormapitem.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/standarditems/colormapitem.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/standarditems/colormapitem.h" -#include "mvvm/model/comboproperty.h" -#include "mvvm/standarditems/data2ditem.h" -#include "mvvm/standarditems/linkeditem.h" -#include "mvvm/standarditems/plottableitems.h" - -using namespace ModelView; - -namespace { -const ComboProperty gradientCombo = - ComboProperty::createFrom({"Grayscale", "Hot", "Cold", "Night", "Candy", "Geography", "Ion", - "Thermal", "Polar", "Spectrum", "Jet", "Hues"}, - "Polar"); -} - -ColorMapItem::ColorMapItem() : CompoundItem(GUI::Constants::ColorMapItemType) -{ - addProperty<LinkedItem>(P_LINK)->setDisplayName("Link"); - addProperty<TextItem>(P_TITLE)->setDisplayName("Title"); - addProperty(P_GRADIENT, gradientCombo)->setDisplayName("Gradient"); - addProperty(P_INTERPOLATION, true)->setDisplayName("Interpolation"); -} - -//! Sets link to the data item. - -void ColorMapItem::setDataItem(const Data2DItem* data_item) -{ - item<LinkedItem>(P_LINK)->setLink(data_item); -} - -//! Returns data item linked to the given ColorMapItem. - -Data2DItem* ColorMapItem::dataItem() const -{ - return item<LinkedItem>(P_LINK)->get<Data2DItem>(); -} diff --git a/mvvm/model/mvvm/standarditems/colormapitem.h b/mvvm/model/mvvm/standarditems/colormapitem.h deleted file mode 100644 index 2fd8c8f87a0..00000000000 --- a/mvvm/model/mvvm/standarditems/colormapitem.h +++ /dev/null @@ -1,44 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/standarditems/colormapitem.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_COLORMAPITEM_H -#define BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_COLORMAPITEM_H - -#include "mvvm/model/compounditem.h" - -namespace ModelView { - -class Data2DItem; - -//! Two-dimensional color map representation of Data2DItem. -//! Contains plot properties (i.e. color, gradient etc) and link to Data2DItem, which will provide -//! actual data to plot. ColorMapItem is intended for plotting only via ColorMapViewportItem. - -class MVVM_MODEL_EXPORT ColorMapItem : public CompoundItem { -public: - static inline const std::string P_LINK = "P_LINK"; - static inline const std::string P_TITLE = "P_TITLE"; - static inline const std::string P_GRADIENT = "P_GRADIENT"; - static inline const std::string P_INTERPOLATION = "P_INTERPOLATION"; - - ColorMapItem(); - - void setDataItem(const Data2DItem* item); - - Data2DItem* dataItem() const; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_COLORMAPITEM_H diff --git a/mvvm/model/mvvm/standarditems/colormapviewportitem.cpp b/mvvm/model/mvvm/standarditems/colormapviewportitem.cpp deleted file mode 100644 index 05b0abc0759..00000000000 --- a/mvvm/model/mvvm/standarditems/colormapviewportitem.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/standarditems/colormapviewportitem.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/standarditems/colormapviewportitem.h" -#include "mvvm/standarditems/axisitems.h" -#include "mvvm/standarditems/colormapitem.h" -#include "mvvm/standarditems/data2ditem.h" -#include <algorithm> -#include <vector> - -namespace { -const std::pair<double, double> default_axis_range{0.0, 1.0}; -} - -using namespace ModelView; - -ColorMapViewportItem::ColorMapViewportItem() - : ViewportItem(GUI::Constants::ColorMapViewportItemType) -{ - register_xy_axes(); - addProperty<ViewportAxisItem>(P_ZAXIS)->setDisplayName("color-axis"); - // for the moment allow only one ColorMapItem - registerTag(TagInfo(T_ITEMS, 0, 1, {GUI::Constants::ColorMapItemType}), /*set_default*/ true); -} - -ViewportAxisItem* ColorMapViewportItem::zAxis() const -{ - return item<ViewportAxisItem>(P_ZAXIS); -} - -void ColorMapViewportItem::setViewportToContent() -{ - ViewportItem::setViewportToContent(); - update_data_range(); -} - -//! Returns range of x-axis as defined in underlying Data2DItem. - -std::pair<double, double> ColorMapViewportItem::data_xaxis_range() const -{ - auto dataItem = data_item(); - return dataItem && dataItem->xAxis() ? dataItem->xAxis()->range() : default_axis_range; -} - -//! Returns range of y-axis as defined in underlying Data2DItem. - -std::pair<double, double> ColorMapViewportItem::data_yaxis_range() const -{ - auto dataItem = data_item(); - return dataItem && dataItem->yAxis() ? dataItem->yAxis()->range() : default_axis_range; -} - -//! Returns Data2DItem if exists. - -Data2DItem* ColorMapViewportItem::data_item() const -{ - auto colormap_item = item<ColorMapItem>(T_ITEMS); - return colormap_item ? colormap_item->dataItem() : nullptr; -} - -//! Updates zAxis to lower, upper values over all data points. - -void ColorMapViewportItem::update_data_range() -{ - if (auto dataItem = data_item(); dataItem) { - auto values = dataItem->content(); - auto [lower, upper] = std::minmax_element(std::begin(values), std::end(values)); - zAxis()->set_range(*lower, *upper); - } -} diff --git a/mvvm/model/mvvm/standarditems/colormapviewportitem.h b/mvvm/model/mvvm/standarditems/colormapviewportitem.h deleted file mode 100644 index c99af201f81..00000000000 --- a/mvvm/model/mvvm/standarditems/colormapviewportitem.h +++ /dev/null @@ -1,48 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/standarditems/colormapviewportitem.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_COLORMAPVIEWPORTITEM_H -#define BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_COLORMAPVIEWPORTITEM_H - -#include "mvvm/standarditems/viewportitem.h" - -namespace ModelView { - -class Data2DItem; - -//! Container with viewport and collection of ColorMapItem's to plot. - -class MVVM_MODEL_EXPORT ColorMapViewportItem : public ViewportItem { -public: - static inline const std::string P_ZAXIS = "P_ZAXIS"; - - ColorMapViewportItem(); - - ViewportAxisItem* zAxis() const; - - using ViewportItem::setViewportToContent; - void setViewportToContent() override; - -protected: - virtual std::pair<double, double> data_xaxis_range() const override; - virtual std::pair<double, double> data_yaxis_range() const override; - -private: - Data2DItem* data_item() const; - void update_data_range(); -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_COLORMAPVIEWPORTITEM_H diff --git a/mvvm/model/mvvm/standarditems/containeritem.cpp b/mvvm/model/mvvm/standarditems/containeritem.cpp deleted file mode 100644 index d8656f735c3..00000000000 --- a/mvvm/model/mvvm/standarditems/containeritem.cpp +++ /dev/null @@ -1,32 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/standarditems/containeritem.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/standarditems/containeritem.h" - -using namespace ModelView; - -ContainerItem::ContainerItem(const std::string& modelType) : CompoundItem(modelType) -{ - registerTag(ModelView::TagInfo::universalTag(T_ITEMS), /*set_as_default*/ true); -} - -bool ContainerItem::empty() const -{ - return childrenCount() == 0; -} - -size_t ContainerItem::size() const -{ - return static_cast<size_t>(childrenCount()); -} diff --git a/mvvm/model/mvvm/standarditems/containeritem.h b/mvvm/model/mvvm/standarditems/containeritem.h deleted file mode 100644 index 7efae8f79e7..00000000000 --- a/mvvm/model/mvvm/standarditems/containeritem.h +++ /dev/null @@ -1,38 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/standarditems/containeritem.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_CONTAINERITEM_H -#define BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_CONTAINERITEM_H - -#include "mvvm/model/compounditem.h" - -namespace ModelView { - -//! Simple container to store any type of children. -//! Used as convenience item to create branch with uniform children beneath. - -class MVVM_MODEL_EXPORT ContainerItem : public CompoundItem { -public: - static inline const std::string T_ITEMS = "T_ITEMS"; - - ContainerItem(const std::string& modelType = GUI::Constants::ContainerItemType); - - bool empty() const; - - size_t size() const; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_CONTAINERITEM_H diff --git a/mvvm/model/mvvm/standarditems/data1ditem.cpp b/mvvm/model/mvvm/standarditems/data1ditem.cpp deleted file mode 100644 index c89372dff25..00000000000 --- a/mvvm/model/mvvm/standarditems/data1ditem.cpp +++ /dev/null @@ -1,97 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/standarditems/data1ditem.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/standarditems/data1ditem.h" -#include "mvvm/standarditems/axisitems.h" -#include <stdexcept> - -using namespace ModelView; - -namespace { -size_t total_bin_count(Data1DItem* item) -{ - auto axis = item->item<BinnedAxisItem>(Data1DItem::T_AXIS); - return axis ? static_cast<size_t>(axis->size()) : 0; -} -} // namespace - -Data1DItem::Data1DItem() : CompoundItem(GUI::Constants::Data1DItemType) -{ - // prevent editing in widgets, since there is no corresponding editor - addProperty(P_VALUES, std::vector<double>())->setDisplayName("Values")->setEditable(false); - - addProperty(P_ERRORS, std::vector<double>())->setDisplayName("Errors")->setEditable(false); - - registerTag( - TagInfo(T_AXIS, 0, 1, - {GUI::Constants::FixedBinAxisItemType, GUI::Constants::PointwiseAxisItemType}), - true); - setValues(std::vector<double>()); -} - -//! Sets axis. Bin content will be set to zero. - -// void Data1DItem::setAxis(std::unique_ptr<BinnedAxisItem> axis) -//{ -// // we disable possibility to re-create axis to facilitate undo/redo - -// if (getItem(T_AXIS, 0)) -// throw std::runtime_error("Axis was already set. Currently we do not support axis change"); - -// insertItem(axis.release(), {T_AXIS, 0}); -// setValues(std::vector<double>(total_bin_count(this), 0.0)); -//} - -//! Returns coordinates of bin centers. - -std::vector<double> Data1DItem::binCenters() const -{ - auto axis = item<BinnedAxisItem>(T_AXIS); - return axis ? axis->binCenters() : std::vector<double>{}; -} - -//! Sets internal data buffer to given data. If size of axis doesn't match the size of the data, -//! exception will be thrown. - -void Data1DItem::setValues(const std::vector<double>& data) -{ - if (total_bin_count(this) != data.size()) - throw std::runtime_error("Data1DItem::setValues() -> Data doesn't match size of axis"); - - setProperty(P_VALUES, data); -} - -//! Returns values stored in bins. - -std::vector<double> Data1DItem::binValues() const -{ - return property<std::vector<double>>(P_VALUES); -} - -//! Sets errors on values in bins. - -void Data1DItem::setErrors(const std::vector<double>& errors) -{ - if (total_bin_count(this) != errors.size()) - throw std::runtime_error("Data1DItem::setErrors() -> Data doesn't match size of axis"); - - setProperty(P_ERRORS, errors); -} - -//! Returns value errors stored in bins. - -std::vector<double> Data1DItem::binErrors() const -{ - return property<std::vector<double>>(P_ERRORS); -} diff --git a/mvvm/model/mvvm/standarditems/data1ditem.h b/mvvm/model/mvvm/standarditems/data1ditem.h deleted file mode 100644 index f18821a7dce..00000000000 --- a/mvvm/model/mvvm/standarditems/data1ditem.h +++ /dev/null @@ -1,77 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/standarditems/data1ditem.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_DATA1DITEM_H -#define BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_DATA1DITEM_H - -#include "mvvm/model/compounditem.h" -#include "mvvm/model/sessionmodel.h" -#include <vector> - -namespace ModelView { - -class BinnedAxisItem; - -//! Represents one-dimensional data (axis and values). -//! Values are stored in Data1DItem itself, axis is attached as a child. Corresponding plot -//! properties will be served by GraphItem. - -class MVVM_MODEL_EXPORT Data1DItem : public CompoundItem { -public: - static inline const std::string P_VALUES = "P_VALUES"; - static inline const std::string P_ERRORS = "P_ERRORS"; - static inline const std::string T_AXIS = "T_AXIS"; - - Data1DItem(); - - // void setAxis(std::unique_ptr<BinnedAxisItem> axis); - - std::vector<double> binCenters() const; - - void setValues(const std::vector<double>& data); - std::vector<double> binValues() const; - - void setErrors(const std::vector<double>& errors); - std::vector<double> binErrors() const; - - //! Inserts axis of given type. - template <typename T, typename... Args> T* setAxis(Args&&... args); -}; - -// FIXME Consider redesign of the method below. Should the axis exist from the beginning -// or added later? It is not clear how to create axis a) via Data1DItem::setAxis -// b) via model directly c) in constructor? - -template <typename T, typename... Args> T* Data1DItem::setAxis(Args&&... args) -{ - // we disable possibility to re-create axis to facilitate undo/redo - if (getItem(T_AXIS, 0)) - throw std::runtime_error("Axis was already set. Currently we do not support axis change"); - - T* result{nullptr}; - if (model()) { - // acting through the model to enable undo/redo - result = model()->insertItem<T>(this); - } else { - result = new T; - insertItem(result, {T_AXIS, 0}); - } - result->setParameters(std::forward<Args>(args)...); - setValues(std::vector<double>(result->size(), 0.0)); - return result; -} - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_DATA1DITEM_H diff --git a/mvvm/model/mvvm/standarditems/data2ditem.cpp b/mvvm/model/mvvm/standarditems/data2ditem.cpp deleted file mode 100644 index e9b5e2e9db0..00000000000 --- a/mvvm/model/mvvm/standarditems/data2ditem.cpp +++ /dev/null @@ -1,87 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/standarditems/data2ditem.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/standarditems/data2ditem.h" -#include "mvvm/standarditems/axisitems.h" -#include <stdexcept> - -using namespace ModelView; - -namespace { -size_t total_bin_count(Data2DItem* item) -{ - if (auto xaxis = item->xAxis(); xaxis) - if (auto yaxis = item->yAxis(); yaxis) - return static_cast<size_t>(xaxis->size() * yaxis->size()); - return 0; -} -} // namespace - -Data2DItem::Data2DItem() : CompoundItem(GUI::Constants::Data2DItemType) -{ - registerTag(TagInfo(T_XAXIS, 0, 1, {GUI::Constants::FixedBinAxisItemType})); - registerTag(TagInfo(T_YAXIS, 0, 1, {GUI::Constants::FixedBinAxisItemType})); - setContent(std::vector<double>()); // prevent editing in widgets, since there is no - // corresponding editor -} - -//! Sets axes and put data points to zero. - -void Data2DItem::setAxes(std::unique_ptr<BinnedAxisItem> x_axis, - std::unique_ptr<BinnedAxisItem> y_axis) -{ - insert_axis(std::move(x_axis), T_XAXIS); - insert_axis(std::move(y_axis), T_YAXIS); - setContent(std::vector<double>(total_bin_count(this), 0.0)); -} - -//! Returns x-axis (nullptr if it doesn't exist). - -BinnedAxisItem* Data2DItem::xAxis() const -{ - return item<BinnedAxisItem>(T_XAXIS); -} - -//! Returns y-axis (nullptr if it doesn't exist). - -BinnedAxisItem* Data2DItem::yAxis() const -{ - return item<BinnedAxisItem>(T_YAXIS); -} - -void Data2DItem::setContent(const std::vector<double>& data) -{ - if (total_bin_count(this) != data.size()) - throw std::runtime_error("Data1DItem::setContent() -> Data doesn't match size of axis"); - - setData(data); -} - -//! Returns 2d vector representing 2d data. - -std::vector<double> Data2DItem::content() const -{ - return data<std::vector<double>>(); -} - -//! Insert axis under given tag. Previous axis will be deleted and data points invalidated. - -void Data2DItem::insert_axis(std::unique_ptr<BinnedAxisItem> axis, const std::string& tag) -{ - // removing current axis - if (getItem(tag, 0)) - delete takeItem({tag, 0}); - - insertItem(axis.release(), {tag, 0}); -} diff --git a/mvvm/model/mvvm/standarditems/data2ditem.h b/mvvm/model/mvvm/standarditems/data2ditem.h deleted file mode 100644 index 749c67ea2e6..00000000000 --- a/mvvm/model/mvvm/standarditems/data2ditem.h +++ /dev/null @@ -1,52 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/standarditems/data2ditem.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_DATA2DITEM_H -#define BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_DATA2DITEM_H - -#include "mvvm/model/compounditem.h" -#include <vector> - -namespace ModelView { - -class BinnedAxisItem; - -//! Represents two-dimensional data (axes definition and 2d array of values). -//! Values are stored in Data2DItem itself, axes are attached as children. Corresponding plot -//! properties will be served by ColorMapItem. - -class MVVM_MODEL_EXPORT Data2DItem : public CompoundItem { -public: - static inline const std::string T_XAXIS = "T_XAXIS"; - static inline const std::string T_YAXIS = "T_YAXIS"; - - Data2DItem(); - - void setAxes(std::unique_ptr<BinnedAxisItem> x_axis, std::unique_ptr<BinnedAxisItem> y_axis); - - BinnedAxisItem* xAxis() const; - - BinnedAxisItem* yAxis() const; - - void setContent(const std::vector<double>& data); - - std::vector<double> content() const; - -private: - void insert_axis(std::unique_ptr<BinnedAxisItem> axis, const std::string& tag); -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_DATA2DITEM_H diff --git a/mvvm/model/mvvm/standarditems/graphitem.cpp b/mvvm/model/mvvm/standarditems/graphitem.cpp deleted file mode 100644 index bfaeea57a22..00000000000 --- a/mvvm/model/mvvm/standarditems/graphitem.cpp +++ /dev/null @@ -1,92 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/standarditems/graphitem.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/standarditems/graphitem.h" -#include "mvvm/model/comboproperty.h" -#include "mvvm/standarditems/data1ditem.h" -#include "mvvm/standarditems/linkeditem.h" -#include "mvvm/standarditems/plottableitems.h" -#include <QColor> - -using namespace ModelView; - -GraphItem::GraphItem(const std::string& model_type) : CompoundItem(model_type) -{ - addProperty<LinkedItem>(P_LINK)->setDisplayName("Link"); - addProperty<TextItem>(P_GRAPH_TITLE)->setDisplayName("Graph title"); - addProperty<PenItem>(P_PEN)->setDisplayName("Pen"); - addProperty(P_DISPLAYED, true)->setDisplayName("Displayed"); -} - -//! Sets link to the data item. - -void GraphItem::setDataItem(const Data1DItem* data_item) -{ - item<LinkedItem>(P_LINK)->setLink(data_item); -} - -//! Update item from the content of given graph. Link to the data will be set -//! as in given item, other properties copied. - -void GraphItem::setFromGraphItem(const GraphItem* graph_item) -{ - setDataItem(graph_item->dataItem()); - auto pen = item<PenItem>(P_PEN); - auto source_pen = graph_item->item<PenItem>(P_PEN); - pen->setProperty(PenItem::P_COLOR, source_pen->property<QColor>(PenItem::P_COLOR)); - pen->setProperty(PenItem::P_STYLE, source_pen->property<ComboProperty>(PenItem::P_STYLE)); - pen->setProperty(PenItem::P_WIDTH, source_pen->property<int>(PenItem::P_WIDTH)); -} - -//! Returns data item linked to the given GraphItem. - -Data1DItem* GraphItem::dataItem() const -{ - return item<LinkedItem>(P_LINK)->get<Data1DItem>(); -} - -std::vector<double> GraphItem::binCenters() const -{ - return dataItem() ? dataItem()->binCenters() : std::vector<double>(); -} - -std::vector<double> GraphItem::binValues() const -{ - return dataItem() ? dataItem()->binValues() : std::vector<double>(); -} - -std::vector<double> GraphItem::binErrors() const -{ - return dataItem() ? dataItem()->binErrors() : std::vector<double>(); -} - -//! Returns color name in #RRGGBB format. - -std::string GraphItem::colorName() const -{ - return penItem()->colorName(); -} - -//! Sets named color following schema from https://www.w3.org/TR/css-color-3/#svg-color. -//! e.g. "mediumaquamarine" - -void GraphItem::setNamedColor(const std::string& named_color) -{ - penItem()->setNamedColor(named_color); -} - -PenItem* GraphItem::penItem() const -{ - return item<PenItem>(P_PEN); -} diff --git a/mvvm/model/mvvm/standarditems/graphitem.h b/mvvm/model/mvvm/standarditems/graphitem.h deleted file mode 100644 index bf53941782e..00000000000 --- a/mvvm/model/mvvm/standarditems/graphitem.h +++ /dev/null @@ -1,58 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/standarditems/graphitem.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_GRAPHITEM_H -#define BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_GRAPHITEM_H - -#include "mvvm/model/compounditem.h" - -namespace ModelView { - -class Data1DItem; -class PenItem; - -//! One-dimensional graph representation of Data1DItem. -//! Contains plot properties (i.e. color, line type etc) and link to Data1DItem, which will provide -//! actual data to plot. GraphItem is intended for plotting only via GraphViewportItem. - -class MVVM_MODEL_EXPORT GraphItem : public CompoundItem { -public: - static inline const std::string P_LINK = "P_LINK"; - static inline const std::string P_GRAPH_TITLE = "P_GRAPH_TITLE"; - static inline const std::string P_PEN = "P_PEN"; - static inline const std::string P_DISPLAYED = "P_DISPLAYED"; - - GraphItem(const std::string& model_type = GUI::Constants::GraphItemType); - - void setDataItem(const Data1DItem* item); - - void setFromGraphItem(const GraphItem* graph_item); - - Data1DItem* dataItem() const; - - std::vector<double> binCenters() const; - - std::vector<double> binValues() const; - - std::vector<double> binErrors() const; - - std::string colorName() const; - void setNamedColor(const std::string& named_color); - - PenItem* penItem() const; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_GRAPHITEM_H diff --git a/mvvm/model/mvvm/standarditems/graphviewportitem.cpp b/mvvm/model/mvvm/standarditems/graphviewportitem.cpp deleted file mode 100644 index 515d4d51d65..00000000000 --- a/mvvm/model/mvvm/standarditems/graphviewportitem.cpp +++ /dev/null @@ -1,105 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/standarditems/graphviewportitem.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/standarditems/graphviewportitem.h" -#include "mvvm/standarditems/graphitem.h" -#include <algorithm> -#include <vector> - -using namespace ModelView; - -namespace { - -const double failback_min = 0.0; -const double failback_max = 1.0; - -//! Find min and max values along all data points in all graphs. -//! Function 'func' is used to run either through binCenters or binValues. - -template <typename T> auto get_min_max(const std::vector<GraphItem*>& graphs, T func) -{ - std::vector<double> values; - for (auto graph : graphs) { - const auto array = func(graph); - std::copy(std::begin(array), std::end(array), std::back_inserter(values)); - } - - auto [xmin, xmax] = std::minmax_element(std::begin(values), std::end(values)); - return xmin != xmax ? std::make_pair(*xmin, *xmax) : std::make_pair(failback_min, failback_max); -} - -} // namespace - -GraphViewportItem::GraphViewportItem(const std::string& model_type) : ViewportItem(model_type) -{ - register_xy_axes(); - registerTag(TagInfo::universalTag(T_ITEMS, {GUI::Constants::GraphItemType}), - /*set_default*/ true); -} - -//! Returns the selected graph items. - -std::vector<GraphItem*> GraphViewportItem::graphItems() const -{ - return items<GraphItem>(T_ITEMS); -} - -//! Returns the selected graph items. - -std::vector<GraphItem*> GraphViewportItem::visibleGraphItems() const -{ - std::vector<GraphItem*> all_items = items<GraphItem>(T_ITEMS); - std::vector<GraphItem*> visible_items; - std::copy_if(all_items.begin(), all_items.end(), std::back_inserter(visible_items), - [](const GraphItem* graph_item) { - return graph_item->property<bool>(GraphItem::P_DISPLAYED); - }); - return visible_items; -} - -//! Set the graph selection. - -void GraphViewportItem::setVisible(const std::vector<GraphItem*>& visible_graph_items) -{ - std::vector<GraphItem*> output; - for (auto graph_item : items<GraphItem>(T_ITEMS)) { - if (std::find(visible_graph_items.begin(), visible_graph_items.end(), graph_item) - != visible_graph_items.end()) - graph_item->setProperty(GraphItem::P_DISPLAYED, true); - else - graph_item->setProperty(GraphItem::P_DISPLAYED, false); - } -} - -//! Reset the graph selection. - -void GraphViewportItem::setAllVisible() -{ - for (auto graph_item : items<GraphItem>(T_ITEMS)) - graph_item->setProperty(GraphItem::P_DISPLAYED, true); -} - -//! Returns lower, upper range on x-axis occupied by all data points of all graphs. - -std::pair<double, double> GraphViewportItem::data_xaxis_range() const -{ - return get_min_max(visibleGraphItems(), [](GraphItem* graph) { return graph->binCenters(); }); -} - -//! Returns lower, upper range on y-axis occupied by all data points of all graphs. - -std::pair<double, double> GraphViewportItem::data_yaxis_range() const -{ - return get_min_max(visibleGraphItems(), [](GraphItem* graph) { return graph->binValues(); }); -} diff --git a/mvvm/model/mvvm/standarditems/graphviewportitem.h b/mvvm/model/mvvm/standarditems/graphviewportitem.h deleted file mode 100644 index a331f74b53b..00000000000 --- a/mvvm/model/mvvm/standarditems/graphviewportitem.h +++ /dev/null @@ -1,47 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/standarditems/graphviewportitem.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_GRAPHVIEWPORTITEM_H -#define BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_GRAPHVIEWPORTITEM_H - -#include "mvvm/standarditems/viewportitem.h" - -namespace ModelView { - -class GraphItem; - -//! 2D viewport specialized for showing multiple GraphItem's. -//! Provides calculation of viewport's x-axis and y-axis range basing on GraphItem data. -//! Provides functionality to hide selected graphs. - -class MVVM_MODEL_EXPORT GraphViewportItem : public ViewportItem { -public: - GraphViewportItem(const std::string& model_type = GUI::Constants::GraphViewportItemType); - - std::vector<GraphItem*> graphItems() const; - - std::vector<GraphItem*> visibleGraphItems() const; - - void setVisible(const std::vector<GraphItem*>& visible_graph_items); - - void setAllVisible(); - -protected: - std::pair<double, double> data_xaxis_range() const override; - std::pair<double, double> data_yaxis_range() const override; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_GRAPHVIEWPORTITEM_H diff --git a/mvvm/model/mvvm/standarditems/linkeditem.cpp b/mvvm/model/mvvm/standarditems/linkeditem.cpp deleted file mode 100644 index 2d9e2b4379e..00000000000 --- a/mvvm/model/mvvm/standarditems/linkeditem.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/standarditems/linkeditem.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/standarditems/linkeditem.h" -#include "mvvm/model/customvariants.h" - -using namespace ModelView; - -namespace { -const Variant empty_link = Variant::fromValue(""); -} - -LinkedItem::LinkedItem() : SessionItem(GUI::Constants::LinkedItemType) -{ - setData(empty_link); - setEditable(false); // prevent editing in widgets, link is set programmatically. -} - -//! Set link to given item. - -void LinkedItem::setLink(const SessionItem* item) -{ - setData(item ? Variant::fromValue(item->identifier()) : empty_link); -} diff --git a/mvvm/model/mvvm/standarditems/linkeditem.h b/mvvm/model/mvvm/standarditems/linkeditem.h deleted file mode 100644 index 47000f4c2c8..00000000000 --- a/mvvm/model/mvvm/standarditems/linkeditem.h +++ /dev/null @@ -1,48 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/standarditems/linkeditem.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_LINKEDITEM_H -#define BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_LINKEDITEM_H - -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionmodel.h" - -namespace ModelView { - -//! Item to store a persistent link to other arbitrary items. - -//! The identifier of the item intended for linking is stored as DataRole on board of LinkedItem -//! and can be used to find the corresponding item via SessionModel::findItem machinery. -//! Provided mechanism is persistent and outlive serialization. Can be used to find items in -//! different models. For that being the case, models should use same ItemPool. - -class MVVM_MODEL_EXPORT LinkedItem : public SessionItem { -public: - LinkedItem(); - - void setLink(const SessionItem* item); - - template <typename T = SessionItem> T* get() const; -}; - -//! Returns item linked to given item. Works only in model context. - -template <typename T> T* LinkedItem::get() const -{ - return model() ? dynamic_cast<T*>(model()->findItem(data<std::string>())) : nullptr; -} - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_LINKEDITEM_H diff --git a/mvvm/model/mvvm/standarditems/plottableitems.cpp b/mvvm/model/mvvm/standarditems/plottableitems.cpp deleted file mode 100644 index 6a7315d8e80..00000000000 --- a/mvvm/model/mvvm/standarditems/plottableitems.cpp +++ /dev/null @@ -1,78 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/standarditems/plottableitems.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/standarditems/plottableitems.h" -#include "mvvm/model/comboproperty.h" -#include <QColor> - -using namespace ModelView; - -namespace { -//! Following Qt styles. -const ComboProperty penStyleCombo = ComboProperty::createFrom( - {"NoPen", "SolidLine", "DashLine", "DotLine", "DashDotLine", "DashDotDotLine"}, "SolidLine"); -const int pen_default_width = 1; -const int pen_min_width = 0; -const int pen_max_width = 7; -const int penstyle_index_solid = 1; -const int penstyle_index_dashline = 2; - -// We do not want to depend from widgetutils.h to get App default font size. Let's stick to -// hardcoded value for the moment, even if on different systems it can be not-optimal. -const int default_title_size = 10; -const std::string default_title_family = "Noto Sans"; -} // namespace - -TextItem::TextItem() : CompoundItem(GUI::Constants::TextItemType) -{ - addProperty(P_TEXT, QString())->setDisplayName("Text"); - addProperty(P_FONT, default_title_family)->setDisplayName("Font"); - addProperty(P_SIZE, default_title_size)->setDisplayName("Size"); -} - -PenItem::PenItem() : CompoundItem(GUI::Constants::PenItemType) -{ - addProperty(P_COLOR, QColor(Qt::black))->setDisplayName("Color")->setToolTip("Pen color"); - addProperty(P_STYLE, penStyleCombo)->setDisplayName("Style")->setToolTip("Pen style"); - addProperty(P_WIDTH, pen_default_width) - ->setDisplayName("Width") - ->setLimits(RealLimits::limited(pen_min_width, pen_max_width)) - ->setToolTip("Pen width"); -} - -//! Sets style of the pen to represent selected object (dash line). - -void PenItem::setSelected(bool is_selected) -{ - auto combo = penStyleCombo; - combo.setCurrentIndex(is_selected ? penstyle_index_dashline : penstyle_index_solid); - setProperty(P_STYLE, combo); -} - -//! Returns color name in #RRGGBB format. -//! We do not want to expose QColor itself since it will be eventually removed. - -std::string PenItem::colorName() const -{ - return property<QColor>(P_COLOR).name().toStdString(); -} - -//! Sets named color following schema from https://www.w3.org/TR/css-color-3/#svg-color. -//! e.g. "mediumaquamarine" -//! We do not want to expose QColor itself since it will be eventually removed. - -void PenItem::setNamedColor(const std::string& named_color) -{ - setProperty(P_COLOR, QColor(QString::fromStdString(named_color))); -} diff --git a/mvvm/model/mvvm/standarditems/plottableitems.h b/mvvm/model/mvvm/standarditems/plottableitems.h deleted file mode 100644 index 5673cddd904..00000000000 --- a/mvvm/model/mvvm/standarditems/plottableitems.h +++ /dev/null @@ -1,54 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/standarditems/plottableitems.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_PLOTTABLEITEMS_H -#define BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_PLOTTABLEITEMS_H - -//! @file mvvm/model/mvvm/standarditems/plottableitems.h -//! Collection of items to plot in QCustomPlot context. - -#include "mvvm/model/compounditem.h" - -namespace ModelView { - -//! Represent text item on plot. - -class MVVM_MODEL_EXPORT TextItem : public CompoundItem { -public: - static inline const std::string P_TEXT = "P_TEXT"; - static inline const std::string P_FONT = "P_FONT"; - static inline const std::string P_SIZE = "P_SIZE"; - - TextItem(); -}; - -//! Represents basics settings of QPen. - -class MVVM_MODEL_EXPORT PenItem : public CompoundItem { -public: - static inline const std::string P_COLOR = "P_COLOR"; - static inline const std::string P_STYLE = "P_STYLE"; - static inline const std::string P_WIDTH = "P_WIDTH"; - - PenItem(); - - void setSelected(bool is_selected); - - std::string colorName() const; - void setNamedColor(const std::string& named_color); -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_PLOTTABLEITEMS_H diff --git a/mvvm/model/mvvm/standarditems/standarditemincludes.h b/mvvm/model/mvvm/standarditems/standarditemincludes.h deleted file mode 100644 index cc7c39424a1..00000000000 --- a/mvvm/model/mvvm/standarditems/standarditemincludes.h +++ /dev/null @@ -1,35 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/standarditems/standarditemincludes.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_STANDARDITEMINCLUDES_H -#define BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_STANDARDITEMINCLUDES_H - -//! @file mvvm/model/mvvm/standarditems/standarditemincludes.h -//! Collection of all includes with items supported by the model out-of-the-box. - -#include "mvvm/model/compounditem.h" -#include "mvvm/model/propertyitem.h" -#include "mvvm/standarditems/axisitems.h" -#include "mvvm/standarditems/colormapitem.h" -#include "mvvm/standarditems/colormapviewportitem.h" -#include "mvvm/standarditems/containeritem.h" -#include "mvvm/standarditems/data1ditem.h" -#include "mvvm/standarditems/data2ditem.h" -#include "mvvm/standarditems/graphitem.h" -#include "mvvm/standarditems/graphviewportitem.h" -#include "mvvm/standarditems/linkeditem.h" -#include "mvvm/standarditems/plottableitems.h" -#include "mvvm/standarditems/vectoritem.h" - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_STANDARDITEMINCLUDES_H diff --git a/mvvm/model/mvvm/standarditems/vectoritem.cpp b/mvvm/model/mvvm/standarditems/vectoritem.cpp deleted file mode 100644 index 6127e5732da..00000000000 --- a/mvvm/model/mvvm/standarditems/vectoritem.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/standarditems/vectoritem.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/standarditems/vectoritem.h" -#include "mvvm/model/customvariants.h" -#include "mvvm/signals/itemmapper.h" -#include <sstream> - -using namespace ModelView; - -VectorItem::VectorItem() : CompoundItem(GUI::Constants::VectorItemType) -{ - addProperty(P_X, 0.0)->setDisplayName("X"); - addProperty(P_Y, 0.0)->setDisplayName("Y"); - addProperty(P_Z, 0.0)->setDisplayName("Z"); - - setEditable(false); - - update_label(); -} - -void VectorItem::activate() -{ - auto on_property_change = [this](SessionItem*, const std::string&) { update_label(); }; - mapper()->setOnPropertyChange(on_property_change, this); -} - -void VectorItem::update_label() -{ - std::ostringstream ostr; - ostr << "(" << property<double>(P_X) << ", " << property<double>(P_Y) << ", " - << property<double>(P_Z) << ")"; - setData(Variant::fromValue(ostr.str()), ItemDataRole::DATA, /*direct*/ true); -} diff --git a/mvvm/model/mvvm/standarditems/vectoritem.h b/mvvm/model/mvvm/standarditems/vectoritem.h deleted file mode 100644 index dd7aa8923b6..00000000000 --- a/mvvm/model/mvvm/standarditems/vectoritem.h +++ /dev/null @@ -1,40 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/standarditems/vectoritem.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_VECTORITEM_H -#define BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_VECTORITEM_H - -#include "mvvm/model/compounditem.h" - -namespace ModelView { - -//! Vector item with three x,y,z property items. - -class MVVM_MODEL_EXPORT VectorItem : public CompoundItem { -public: - static inline const std::string P_X = "P_X"; - static inline const std::string P_Y = "P_Y"; - static inline const std::string P_Z = "P_Z"; - - VectorItem(); - - void activate() override; - -private: - void update_label(); -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_VECTORITEM_H diff --git a/mvvm/model/mvvm/standarditems/viewportitem.cpp b/mvvm/model/mvvm/standarditems/viewportitem.cpp deleted file mode 100644 index 29d5e7cdddb..00000000000 --- a/mvvm/model/mvvm/standarditems/viewportitem.cpp +++ /dev/null @@ -1,67 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/standarditems/viewportitem.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/standarditems/viewportitem.h" -#include "mvvm/model/modelutils.h" -#include "mvvm/standarditems/axisitems.h" - -using namespace ModelView; - -ViewportItem::ViewportItem(const ModelView::model_type& model) : CompoundItem(model) {} - -ViewportAxisItem* ViewportItem::xAxis() const -{ - return item<ViewportAxisItem>(P_XAXIS); -} - -ViewportAxisItem* ViewportItem::yAxis() const -{ - return item<ViewportAxisItem>(P_YAXIS); -} - -//! Sets range of x,y window to show all data. -//! Allows adding an additional margin to automatically calculated axis range. Margins are -//! given in relative units wrt calculated axis range. -//! Example: setViewportToContent(0.0, 0.1, 0.0, 0.1) will set axes to show all graphs with 10% gap -//! above and below graph's max and min. - -void ViewportItem::setViewportToContent(double left, double top, double right, double bottom) -{ - Utils::BeginMacros(this, "setViewportToContent"); - auto [xmin, xmax] = data_xaxis_range(); - xAxis()->set_range(xmin - (xmax - xmin) * left, xmax + (xmax - xmin) * right); - - auto [ymin, ymax] = data_yaxis_range(); - yAxis()->set_range(ymin - (ymax - ymin) * bottom, ymax + (ymax - ymin) * top); - Utils::EndMacros(this); -} - -//! Sets range of x,y window to show all data. - -void ViewportItem::setViewportToContent() -{ - Utils::BeginMacros(this, "setViewportToContent"); - auto [xmin, xmax] = data_xaxis_range(); - xAxis()->set_range(xmin, xmax); - - auto [ymin, ymax] = data_yaxis_range(); - yAxis()->set_range(ymin, ymax); - Utils::EndMacros(this); -} - -void ViewportItem::register_xy_axes() -{ - addProperty<ViewportAxisItem>(P_XAXIS)->setDisplayName("X axis"); - addProperty<ViewportAxisItem>(P_YAXIS)->setDisplayName("Y axis"); -} diff --git a/mvvm/model/mvvm/standarditems/viewportitem.h b/mvvm/model/mvvm/standarditems/viewportitem.h deleted file mode 100644 index d5c8e7b9b06..00000000000 --- a/mvvm/model/mvvm/standarditems/viewportitem.h +++ /dev/null @@ -1,53 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/standarditems/viewportitem.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_VIEWPORTITEM_H -#define BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_VIEWPORTITEM_H - -#include "mvvm/model/compounditem.h" - -namespace ModelView { - -class ViewportAxisItem; - -//! Base class to represent 2D viewport. -//! Contains x,y axis, indended to display graphs or 2d colormaps. - -class MVVM_MODEL_EXPORT ViewportItem : public CompoundItem { -public: - static inline const std::string P_XAXIS = "P_XAXIS"; - static inline const std::string P_YAXIS = "P_YAXIS"; - static inline const std::string T_ITEMS = "T_ITEMS"; - - ViewportItem(const model_type& model); - - ViewportAxisItem* xAxis() const; - - ViewportAxisItem* yAxis() const; - - virtual void setViewportToContent(double left, double top, double right, double bottom); - - virtual void setViewportToContent(); - -protected: - void register_xy_axes(); - -protected: - virtual std::pair<double, double> data_xaxis_range() const = 0; - virtual std::pair<double, double> data_yaxis_range() const = 0; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_STANDARDITEMS_VIEWPORTITEM_H diff --git a/mvvm/model/mvvm/utils/CMakeLists.txt b/mvvm/model/mvvm/utils/CMakeLists.txt deleted file mode 100644 index 1fa0a17ffdd..00000000000 --- a/mvvm/model/mvvm/utils/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -target_sources(${library_name} PRIVATE - binutils.cpp - binutils.h - containerutils.cpp - containerutils.h - fileutils.cpp - fileutils.h - ifactory.h - mathconstants.h - numericutils.cpp - numericutils.h - progresshandler.cpp - progresshandler.h - reallimits.cpp - reallimits.h - stringutils.cpp - stringutils.h - threadsafestack.h -) diff --git a/mvvm/model/mvvm/utils/binutils.cpp b/mvvm/model/mvvm/utils/binutils.cpp deleted file mode 100644 index ab9e8c7bcc0..00000000000 --- a/mvvm/model/mvvm/utils/binutils.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#include "mvvm/utils/binutils.h" -#include <fstream> -#include <iostream> - -namespace { -//! Returns buffer size -int get_buffer_size(const std::string& filename); - -//! Returns part of file content in a buffer -void get_buffer_data(const std::string& filename, int* buffer, int buffer_size); - -//! Returns true if the interger is control character as defined above -bool is_control_char(int ch); - -//! Returns true if there is two null bytes in the buffer -bool null_check(int* buffer, int buffer_size); -} // namespace - -namespace ModelView ::Utils { - -// length of buffer -#define BYTE_LENGTH 2048 - -// null character -#define NULL_CHR (0x00) - -// control characters -#define NUL 0 -#define BS 8 -#define CR 13 -#define SUB 26 - -bool is_binary(const std::string& filename) -{ - int buffer[BYTE_LENGTH]; - - // get buffer size - int buffer_size = get_buffer_size(filename); - if (buffer_size == 0) - return false; - - // buffer allocation - get_buffer_data(filename, buffer, buffer_size); - - // control character check - for (int count = 0; count < buffer_size; ++count) - if (is_control_char(buffer[count])) - return true; - - return null_check(buffer, buffer_size); -} - -bool is_text(const std::string& filename) -{ - - return (!is_binary(filename)); -} - -} // namespace ModelView::Utils - -namespace { - -int get_buffer_size(const std::string& filename) -{ - std::ifstream mySource; - mySource.open(filename, std::ios_base::binary); - mySource.seekg(0, std::ios_base::end); - int size = mySource.tellg(); - mySource.close(); - return (size > BYTE_LENGTH) ? BYTE_LENGTH : size; -} - -void get_buffer_data(const std::string& filename, int* buffer, int byte_length) -{ - std::ifstream fstr(filename, std::ios::in | std::ios::binary); - for (int i = 0; i < byte_length; i++) - buffer[i] = fstr.get(); -} - -bool is_control_char(int ch) -{ - return ((ch > NUL && ch < BS) || (ch > CR && ch < SUB)); -} - -bool null_check(int* buffer, int buffer_size) -{ - for (int i = 1; i < buffer_size; ++i) - if (buffer[i] == NULL_CHR && buffer[i - 1] == NULL_CHR) - return true; - return false; -} -} // namespace diff --git a/mvvm/model/mvvm/utils/binutils.h b/mvvm/model/mvvm/utils/binutils.h deleted file mode 100644 index 99c4e322a18..00000000000 --- a/mvvm/model/mvvm/utils/binutils.h +++ /dev/null @@ -1,31 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/utils/binutils.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_UTILS_BINUTILS_H -#define BORNAGAIN_MVVM_MODEL_MVVM_UTILS_BINUTILS_H - -#include "mvvm/model_export.h" -#include <string> - -namespace ModelView ::Utils { - -//! Returns true if file is binary -MVVM_MODEL_EXPORT bool is_binary(const std::string& filename); - -//! Returns true if file is text/ascii -MVVM_MODEL_EXPORT bool is_text(const std::string& filename); - -} // namespace ModelView::Utils - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_UTILS_BINUTILS_H diff --git a/mvvm/model/mvvm/utils/containerutils.cpp b/mvvm/model/mvvm/utils/containerutils.cpp deleted file mode 100644 index fbe52dc6b52..00000000000 --- a/mvvm/model/mvvm/utils/containerutils.cpp +++ /dev/null @@ -1,15 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/utils/containerutils.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "containerutils.h" diff --git a/mvvm/model/mvvm/utils/containerutils.h b/mvvm/model/mvvm/utils/containerutils.h deleted file mode 100644 index ca456d61261..00000000000 --- a/mvvm/model/mvvm/utils/containerutils.h +++ /dev/null @@ -1,108 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/utils/containerutils.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_UTILS_CONTAINERUTILS_H -#define BORNAGAIN_MVVM_MODEL_MVVM_UTILS_CONTAINERUTILS_H - -#include "mvvm/model_export.h" -#include <algorithm> -#include <complex> -#include <iterator> -#include <memory> -#include <string> -#include <type_traits> -#include <unordered_set> -#include <vector> - -namespace ModelView::Utils { - -template <class T> struct is_unique_ptr : std::false_type { -}; - -template <class T, class D> struct is_unique_ptr<std::unique_ptr<T, D>> : std::true_type { -}; - -//! Returns index corresponding to the first occurance of the item in the container. -//! If item doesn't exist, will return -1. Works with containers containing unique_ptr. - -template <typename It, typename T> int IndexOfItem(It begin, It end, const T& item) -{ - It pos; - if constexpr (is_unique_ptr<typename std::iterator_traits<It>::value_type>::value) - pos = find_if(begin, end, [&item](const auto& x) { return x.get() == item; }); - else - pos = find_if(begin, end, [&item](const auto& x) { return x == item; }); - - return pos == end ? -1 : static_cast<int>(std::distance(begin, pos)); -} - -//! Returns index corresponding to the first occurance of the item in the container. -//! If item doesn't exist, will return -1. Works with containers containing unique_ptr. - -template <typename C, typename T> int IndexOfItem(const C& container, const T& item) -{ - return IndexOfItem(container.begin(), container.end(), item); -} - -//! Returns vector containing results of elemntwise unary function application. - -template <typename It, typename UnaryFunction> -std::vector<double> Apply(It begin, It end, UnaryFunction func) -{ - std::vector<double> result; - std::transform(begin, end, std::back_inserter(result), func); - return result; -} - -//! Returns vector with real part of complex numbers. - -template <typename C> std::vector<double> Real(const C& container) -{ - return Apply(std::begin(container), std::end(container), - [](const auto& x) { return std::real(x); }); -} - -//! Returns vector with imag part of complex numbers. - -template <typename C> std::vector<double> Imag(const C& container) -{ - return Apply(std::begin(container), std::end(container), - [](const auto& x) { return std::imag(x); }); -} - -//! Returns copy of container with all duplicated elements filtered our. The order is preserved. - -template <typename C> C UniqueWithOrder(const C& container) -{ - C result; - - using valueType = typename C::value_type; - std::unordered_set<valueType> unique; - - std::copy_if(container.begin(), container.end(), std::back_inserter(result), - [&unique](auto x) { return unique.insert(x).second; }); - - return result; -} - -//! Returns true if container contains a given element. - -template <typename A, typename B> bool Contains(const A& container, const B& element) -{ - return std::find(container.begin(), container.end(), element) != container.end(); -} - -} // namespace ModelView::Utils - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_UTILS_CONTAINERUTILS_H diff --git a/mvvm/model/mvvm/utils/fileutils.cpp b/mvvm/model/mvvm/utils/fileutils.cpp deleted file mode 100644 index 1a2c15e4faf..00000000000 --- a/mvvm/model/mvvm/utils/fileutils.cpp +++ /dev/null @@ -1,135 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/utils/fileutils.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/utils/fileutils.h" -#include <QDir> -#include <QFile> -#include <QFileInfo> -#include <stdexcept> - -#ifdef ENABLE_FILESYSTEM -#include "mvvm/core/filesystem.h" -#endif - -using namespace ModelView; - -bool Utils::exists(const std::string& fileName) -{ -#ifdef ENABLE_FILESYSTEM - return std::filesystem::exists(fileName); -#else - QFileInfo info(QString::fromStdString(fileName)); - return info.exists(); -#endif -} - -std::string Utils::join(const std::string& part1, const std::string& part2) -{ -#ifdef ENABLE_FILESYSTEM - auto path = std::filesystem::path(part1) / std::filesystem::path(part2); - return path.string(); -#else - return part1 + std::string("/") + part2; -#endif -} - -bool Utils::create_directory(const std::string& path) -{ -#ifdef ENABLE_FILESYSTEM - return std::filesystem::create_directory(path); -#else - QDir dir(QString::fromStdString(path)); - return dir.mkpath("."); -#endif -} - -bool Utils::remove(const std::string& path) -{ -#ifdef ENABLE_FILESYSTEM - return std::filesystem::remove(path); -#else - QFile file(QString::fromStdString(path)); - return file.remove(); -#endif -} - -void Utils::remove_all(const std::string& path) -{ -#ifdef ENABLE_FILESYSTEM - std::filesystem::remove_all(path); -#else - QDir dir(QString::fromStdString(path)); - if (dir.exists()) - dir.removeRecursively(); -#endif -} - -std::string Utils::base_name(const std::string& path) -{ -#ifdef ENABLE_FILESYSTEM - return std::filesystem::path(path).stem().string(); -#else - return QFileInfo(QString::fromStdString(path)).completeBaseName().toStdString(); -#endif -} - -std::vector<std::string> Utils::FindFiles(const std::string& dirname, const std::string& ext) -{ -#ifdef ENABLE_FILESYSTEM - std::vector<std::string> result; - for (const auto& entry : std::filesystem::directory_iterator(dirname)) { - const auto filenameStr = entry.path().filename().string(); - if (entry.is_regular_file() && entry.path().extension() == ext) - result.push_back(entry.path().string()); - } - return result; -#else - std::vector<std::string> result; - QDir dir(QString::fromStdString(dirname)); - if (dir.exists()) { - QStringList filters = {QString::fromStdString("*" + ext)}; - for (auto entry : dir.entryList(filters)) { - auto name = dir.filePath(entry); - result.push_back(name.toStdString()); - } - } - return result; -#endif -} - -std::string Utils::parent_path(const std::string& path) -{ -#ifdef ENABLE_FILESYSTEM - return std::filesystem::path(path).parent_path().string(); -#else - QFileInfo info(QString::fromStdString(path)); - return info.dir().path().toStdString(); -#endif -} - -bool Utils::is_empty(const std::string& path) -{ -#ifdef ENABLE_FILESYSTEM - return std::filesystem::is_empty(path); -#else - QFileInfo info(QString::fromStdString(path)); - if (info.isDir()) { - QDir dir(QString::fromStdString(path)); - return dir.isEmpty(); - } else { - return info.size() == 0; - } - return false; -#endif -} diff --git a/mvvm/model/mvvm/utils/fileutils.h b/mvvm/model/mvvm/utils/fileutils.h deleted file mode 100644 index 267d305b730..00000000000 --- a/mvvm/model/mvvm/utils/fileutils.h +++ /dev/null @@ -1,55 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/utils/fileutils.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_UTILS_FILEUTILS_H -#define BORNAGAIN_MVVM_MODEL_MVVM_UTILS_FILEUTILS_H - -#include "mvvm/model_export.h" -#include <string> -#include <vector> - -namespace ModelView::Utils { - -//! Returns true if file exists. -MVVM_MODEL_EXPORT bool exists(const std::string& fileName); - -//! Joins two path elements into the path. -MVVM_MODEL_EXPORT std::string join(const std::string& part1, const std::string& part2); - -//! Create directory, parent directory must exist. If path resolves to existing directory, -//! no error reported. -MVVM_MODEL_EXPORT bool create_directory(const std::string& path); - -//! Removes file or empty directory. -MVVM_MODEL_EXPORT bool remove(const std::string& path); - -//! Removes directory with all its content. -MVVM_MODEL_EXPORT void remove_all(const std::string& path); - -//! Provide the filename of a file path. -MVVM_MODEL_EXPORT std::string base_name(const std::string& path); - -//! Returns list of files with given extention found in given directory. -MVVM_MODEL_EXPORT std::vector<std::string> FindFiles(const std::string& dirname, - const std::string& ext); - -//! Returns the path to the parent directory. -MVVM_MODEL_EXPORT std::string parent_path(const std::string& path); - -//! Returns true if the file indicated by 'path' refers to empty file or directory. -MVVM_MODEL_EXPORT bool is_empty(const std::string& path); - -} // namespace ModelView::Utils - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_UTILS_FILEUTILS_H diff --git a/mvvm/model/mvvm/utils/ifactory.h b/mvvm/model/mvvm/utils/ifactory.h deleted file mode 100644 index 8b29f88cf51..00000000000 --- a/mvvm/model/mvvm/utils/ifactory.h +++ /dev/null @@ -1,67 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/utils/ifactory.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_UTILS_IFACTORY_H -#define BORNAGAIN_MVVM_MODEL_MVVM_UTILS_IFACTORY_H - -#include <functional> -#include <map> -#include <memory> -#include <sstream> -#include <stdexcept> - -namespace ModelView { - -//! Base for factories. - -template <class Key, class Value> class IFactory { -public: - using function_t = std::function<std::unique_ptr<Value>()>; - using map_t = std::map<Key, function_t>; - - bool contains(const Key& item_key) const { return m_data.find(item_key) != m_data.end(); } - - std::unique_ptr<Value> create(const Key& item_key) const - { - auto it = m_data.find(item_key); - if (it == m_data.end()) { - std::ostringstream message; - message << "IFactory::createItem() -> Error. Unknown item key '" << item_key << "'"; - throw std::runtime_error(message.str()); - } - return it->second(); - } - - bool add(const Key& key, function_t func) - { - if (m_data.find(key) != m_data.end()) { - std::ostringstream message; - message << "IFactory::createItem() -> Already registered item key '" << key << "'"; - throw std::runtime_error(message.str()); - } - return m_data.insert(make_pair(key, func)).second; - } - - size_t size() const { return m_data.size(); } - - typename map_t::iterator begin() { return m_data.begin(); } - typename map_t::iterator end() { return m_data.end(); } - -private: - map_t m_data; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_UTILS_IFACTORY_H diff --git a/mvvm/model/mvvm/utils/mathconstants.h b/mvvm/model/mvvm/utils/mathconstants.h deleted file mode 100644 index c5fdb44751b..00000000000 --- a/mvvm/model/mvvm/utils/mathconstants.h +++ /dev/null @@ -1,22 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/utils/mathconstants.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_UTILS_MATHCONSTANTS_H -#define BORNAGAIN_MVVM_MODEL_MVVM_UTILS_MATHCONSTANTS_H - -#ifndef M_PI -#define M_PI 3.14159265358979323846264338327950288 /* pi */ -#endif - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_UTILS_MATHCONSTANTS_H diff --git a/mvvm/model/mvvm/utils/numericutils.cpp b/mvvm/model/mvvm/utils/numericutils.cpp deleted file mode 100644 index 277691ad9f3..00000000000 --- a/mvvm/model/mvvm/utils/numericutils.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/utils/numericutils.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/utils/numericutils.h" -#include <algorithm> -#include <cmath> -#include <limits> -#include <random> - -using namespace ModelView; - -bool Utils::AreAlmostEqual(double a, double b, double tolerance) -{ - constexpr double eps = std::numeric_limits<double>::epsilon(); - return std::abs(a - b) - <= eps * std::max(tolerance * eps, std::max(1., tolerance) * std::abs(b)); -} - -int Utils::RandInt(int low, int high) -{ - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_int_distribution<int> uniform_int(low, high); - return uniform_int(gen); -} - -double Utils::RandDouble(double low, double high) -{ - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_real_distribution<> uniform_real(low, high); - return uniform_real(gen); -} diff --git a/mvvm/model/mvvm/utils/numericutils.h b/mvvm/model/mvvm/utils/numericutils.h deleted file mode 100644 index 3e37ea44236..00000000000 --- a/mvvm/model/mvvm/utils/numericutils.h +++ /dev/null @@ -1,33 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/utils/numericutils.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_UTILS_NUMERICUTILS_H -#define BORNAGAIN_MVVM_MODEL_MVVM_UTILS_NUMERICUTILS_H - -#include "mvvm/model_export.h" - -namespace ModelView::Utils { - -//! Returns true if two doubles agree within epsilon*tolerance. -MVVM_MODEL_EXPORT bool AreAlmostEqual(double a, double b, double tolerance_factor = 1.0); - -//! Produces random integer values uniformly distributed on the closed interval [low, high]. -MVVM_MODEL_EXPORT int RandInt(int low, int high); - -//! Produces random FLOAT values uniformly distributed on the interval [low, high). -MVVM_MODEL_EXPORT double RandDouble(double low, double high); - -} // namespace ModelView::Utils - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_UTILS_NUMERICUTILS_H diff --git a/mvvm/model/mvvm/utils/progresshandler.cpp b/mvvm/model/mvvm/utils/progresshandler.cpp deleted file mode 100644 index a213c01ee4d..00000000000 --- a/mvvm/model/mvvm/utils/progresshandler.cpp +++ /dev/null @@ -1,61 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/utils/progresshandler.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/utils/progresshandler.h" - -using namespace ModelView; - -ProgressHandler::ProgressHandler(ProgressHandler::callback_t callback, size_t max_ticks_count) - : runner_callback(std::move(callback)), max_ticks_count(max_ticks_count) -{ -} - -void ProgressHandler::subscribe(ProgressHandler::callback_t callback) -{ - runner_callback = std::move(callback); -} - -//! Sets expected ticks count, representing progress of a computation. - -void ProgressHandler::setMaxTicksCount(size_t value) -{ - reset(); - max_ticks_count = value; -} - -bool ProgressHandler::has_interrupt_request() const -{ - return interrupt_request; -} - -//! Increment number of completed computation steps. Performs callback to inform -//! subscriber about current progress (in percents) and retrieves interrupt request flag. - -void ProgressHandler::setCompletedTicks(size_t value) -{ - std::unique_lock<std::mutex> lock(mutex); - completed_ticks += value; - if (completed_ticks > max_ticks_count) - max_ticks_count = completed_ticks + 1; - int percentage_done = static_cast<int>(100.0 * completed_ticks / max_ticks_count); - interrupt_request = runner_callback ? runner_callback(percentage_done) : interrupt_request; -} - -//! Resets progress. - -void ProgressHandler::reset() -{ - interrupt_request = false; - completed_ticks = 0; -} diff --git a/mvvm/model/mvvm/utils/progresshandler.h b/mvvm/model/mvvm/utils/progresshandler.h deleted file mode 100644 index 087b3a7bb66..00000000000 --- a/mvvm/model/mvvm/utils/progresshandler.h +++ /dev/null @@ -1,57 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/utils/progresshandler.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_UTILS_PROGRESSHANDLER_H -#define BORNAGAIN_MVVM_MODEL_MVVM_UTILS_PROGRESSHANDLER_H - -#include "mvvm/model_export.h" -#include <functional> -#include <mutex> - -namespace ModelView { - -//! Maintain information about progress of a computation. -//! Initialized with callback function to report progress and retrieve interruption request status. - -class MVVM_MODEL_EXPORT ProgressHandler { -public: - using callback_t = std::function<bool(size_t)>; - - ProgressHandler() = default; - ProgressHandler(callback_t callback, size_t max_ticks_count); - - ProgressHandler(const ProgressHandler& other) = delete; - ProgressHandler& operator=(const ProgressHandler& other) = delete; - - void subscribe(callback_t callback); - - void setMaxTicksCount(size_t value); - - bool has_interrupt_request() const; - - void setCompletedTicks(size_t value); - - void reset(); - -private: - std::mutex mutex; - callback_t runner_callback; - size_t max_ticks_count{0}; - size_t completed_ticks{0}; - bool interrupt_request{false}; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_UTILS_PROGRESSHANDLER_H diff --git a/mvvm/model/mvvm/utils/reallimits.cpp b/mvvm/model/mvvm/utils/reallimits.cpp deleted file mode 100644 index cd37cec50c6..00000000000 --- a/mvvm/model/mvvm/utils/reallimits.cpp +++ /dev/null @@ -1,154 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/utils/reallimits.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/utils/reallimits.h" -#include "mvvm/utils/numericutils.h" -#include <limits> - -namespace { -const double lmin = std::numeric_limits<double>::lowest(); -const double lmax = std::numeric_limits<double>::max(); -const double poszero = std::numeric_limits<double>::min(); -} // namespace - -using namespace ModelView; - -RealLimits::RealLimits() - : m_has_lower_limit(false), m_has_upper_limit(false), m_lower_limit(lmin), m_upper_limit(lmax) -{ -} - -RealLimits::RealLimits(bool has_lower_limit, bool has_upper_limit, double lower_limit, - double upper_limit) - : m_has_lower_limit(has_lower_limit) - , m_has_upper_limit(has_upper_limit) - , m_lower_limit(lower_limit) - , m_upper_limit(upper_limit) -{ -} - -RealLimits RealLimits::lowerLimited(double bound_value) -{ - return RealLimits(true, false, bound_value, lmax); -} - -RealLimits RealLimits::positive() -{ - return lowerLimited(poszero); -} - -RealLimits RealLimits::nonnegative() -{ - return lowerLimited(0.); -} - -RealLimits RealLimits::upperLimited(double bound_value) -{ - return RealLimits(false, true, lmin, bound_value); -} - -RealLimits RealLimits::limited(double left_bound_value, double right_bound_value) -{ - return RealLimits(true, true, left_bound_value, right_bound_value); -} - -RealLimits RealLimits::limitless() -{ - return RealLimits(); -} - -bool RealLimits::hasLowerLimit() const -{ - return m_has_lower_limit; -} - -double RealLimits::lowerLimit() const -{ - return m_lower_limit; -} - -bool RealLimits::hasUpperLimit() const -{ - return m_has_upper_limit; -} - -double RealLimits::upperLimit() const -{ - return m_upper_limit; -} - -bool RealLimits::hasLowerAndUpperLimits() const -{ - return (m_has_lower_limit && m_has_upper_limit); -} - -bool RealLimits::isInRange(double value) const -{ - if (hasLowerLimit() && value < m_lower_limit) - return false; - if (hasUpperLimit() && value >= m_upper_limit) - return false; - return true; -} - -bool RealLimits::operator==(const RealLimits& other) const -{ - // Intenional 'unsafe' double comparison to have RealLimits::positive and - // RealLimits::nonnegative different. - // FIXME Is there better solution? Can we drop either positive or non-negative? - return (m_has_lower_limit == other.m_has_lower_limit) - && (m_has_upper_limit == other.m_has_upper_limit) && m_lower_limit == other.m_lower_limit - && m_upper_limit == other.m_upper_limit; -} - -bool RealLimits::operator!=(const RealLimits& other) const -{ - return !(*this == other); -} - -bool RealLimits::operator<(const RealLimits& other) const -{ - return m_lower_limit < other.m_lower_limit && m_upper_limit < other.m_upper_limit; -} - -bool RealLimits::isLimitless() const -{ - return !hasLowerLimit() && !hasUpperLimit(); -} - -bool RealLimits::isPositive() const -{ - // intenional 'unsafe' double comparison - return hasLowerLimit() && !hasUpperLimit() && lowerLimit() == poszero; -} - -bool RealLimits::isNonnegative() const -{ - return hasLowerLimit() && !hasUpperLimit() && lowerLimit() == 0.0; -} - -bool RealLimits::isLowerLimited() const -{ - return !isPositive() && !isNonnegative() && hasLowerLimit() && !hasUpperLimit(); -} - -bool RealLimits::isUpperLimited() const -{ - return !hasLowerLimit() && hasUpperLimit(); -} - -bool RealLimits::isLimited() const -{ - return hasLowerLimit() && hasUpperLimit(); -} diff --git a/mvvm/model/mvvm/utils/reallimits.h b/mvvm/model/mvvm/utils/reallimits.h deleted file mode 100644 index b082dad6a1d..00000000000 --- a/mvvm/model/mvvm/utils/reallimits.h +++ /dev/null @@ -1,87 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/utils/reallimits.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_UTILS_REALLIMITS_H -#define BORNAGAIN_MVVM_MODEL_MVVM_UTILS_REALLIMITS_H - -#include "mvvm/model_export.h" - -namespace ModelView { - -//! Limits for double. -//! @ingroup fitting - -class MVVM_MODEL_EXPORT RealLimits { -public: - RealLimits(); - - //! Creates an object bounded from the left - static RealLimits lowerLimited(double bound_value); - - //! Creates an object which can have only positive values (>0., zero is not included) - static RealLimits positive(); - - //! Creates an object which can have only positive values with 0. included - static RealLimits nonnegative(); - - //! Creates an object bounded from the right - static RealLimits upperLimited(double bound_value); - - //! Creates an object bounded from the left and right - static RealLimits limited(double left_bound_value, double right_bound_value); - - //! Creates an object withoud bounds (default) - static RealLimits limitless(); - - //! if has lower limit - bool hasLowerLimit() const; - - //! Returns lower limit - double lowerLimit() const; - - //! if has upper limit - bool hasUpperLimit() const; - - //! Returns upper limit - double upperLimit() const; - - //! if has lower and upper limit - bool hasLowerAndUpperLimits() const; - - //! returns true if proposed value is in limits range - bool isInRange(double value) const; - - bool operator==(const RealLimits& other) const; - bool operator!=(const RealLimits& other) const; - bool operator<(const RealLimits& other) const; - - bool isLimitless() const; - bool isPositive() const; - bool isNonnegative() const; - bool isLowerLimited() const; - bool isUpperLimited() const; - bool isLimited() const; - -protected: - RealLimits(bool has_lower_limit, bool has_upper_limit, double lower_limit, double upper_limit); - - bool m_has_lower_limit; //! parameter has lower bound - bool m_has_upper_limit; //! parameter has upper bound - double m_lower_limit; //! minimum allowed value - double m_upper_limit; //! maximum allowed value -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_UTILS_REALLIMITS_H diff --git a/mvvm/model/mvvm/utils/stringutils.cpp b/mvvm/model/mvvm/utils/stringutils.cpp deleted file mode 100644 index 957ba350c3c..00000000000 --- a/mvvm/model/mvvm/utils/stringutils.cpp +++ /dev/null @@ -1,130 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/utils/stringutils.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/utils/stringutils.h" -#include <algorithm> -#include <cmath> -#include <iomanip> -#include <iterator> -#include <sstream> -#include <string_view> - -using namespace ModelView; - -std::string Utils::DoubleToString(double input, int precision) -{ - std::ostringstream inter; - inter << std::setprecision(precision); - if (std::abs(input) < std::numeric_limits<double>::epsilon()) { - inter << "0.0"; - return inter.str(); - } - inter << input; - if (inter.str().find('e') == std::string::npos && inter.str().find('.') == std::string::npos) - inter << ".0"; - return inter.str(); -} - -std::string Utils::ScientificDoubleToString(double input, int precision) -{ - std::ostringstream inter; - inter << std::scientific; - inter << std::setprecision(precision); - inter << input; - - std::string::size_type pos = inter.str().find('e'); - if (pos == std::string::npos) - return inter.str(); - - std::string part1 = inter.str().substr(0, pos); - std::string part2 = inter.str().substr(pos, std::string::npos); - - part1.erase(part1.find_last_not_of('0') + 1, std::string::npos); - if (part1.back() == '.') - part1 += "0"; - - return part1 + part2; -} - -std::string Utils::TrimWhitespace(const std::string& str) -{ - const char whitespace[]{" \t\n"}; - const size_t first = str.find_first_not_of(whitespace); - if (std::string::npos == first) - return {}; - const size_t last = str.find_last_not_of(whitespace); - return str.substr(first, (last - first + 1)); -} - -std::string Utils::RemoveRepeatedSpaces(std::string str) -{ - if (str.empty()) - return {}; - auto it = std::unique(str.begin(), str.end(), - [](auto x, auto y) { return x == y && std::isspace(x); }); - str.erase(it, str.end()); - return str; -} - -std::optional<double> Utils::StringToDouble(const std::string& str) -{ - std::istringstream iss(Utils::TrimWhitespace(str)); - iss.imbue(std::locale::classic()); - double value; - iss >> value; - return (!iss.fail() && iss.eof()) ? std::optional<double>(value) : std::optional<double>{}; -} - -std::optional<int> Utils::StringToInteger(const std::string& str) -{ - std::istringstream iss(Utils::TrimWhitespace(str)); - int value; - iss >> value; - return (!iss.fail() && iss.eof()) ? std::optional<int>(value) : std::optional<int>{}; -} - -std::vector<std::string> Utils::SplitString(const std::string& str, const std::string& delimeter) -{ - // splitting string following Python's str.split() - if (delimeter.empty()) - throw std::runtime_error("Empty delimeter"); - if (str.empty()) - return {}; - - std::vector<std::string> result; - std::string_view view(str); - size_t pos{0}; - - while ((pos = view.find(delimeter)) != std::string::npos) { - result.emplace_back(std::string(view.substr(0, pos))); - view.remove_prefix(pos + delimeter.length()); - } - result.emplace_back(std::string(view)); - return result; -} - -std::vector<double> Utils::ParseSpaceSeparatedDoubles(const std::string& str) -{ - std::vector<double> result; - ParseSpaceSeparatedDoubles(str, result); - return result; -} - -void Utils::ParseSpaceSeparatedDoubles(const std::string& str, std::vector<double>& result) -{ - std::istringstream iss(str); - iss.imbue(std::locale::classic()); - std::copy(std::istream_iterator<double>(iss), std::istream_iterator<double>(), - back_inserter(result)); -} diff --git a/mvvm/model/mvvm/utils/stringutils.h b/mvvm/model/mvvm/utils/stringutils.h deleted file mode 100644 index 80e059912e5..00000000000 --- a/mvvm/model/mvvm/utils/stringutils.h +++ /dev/null @@ -1,64 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/utils/stringutils.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_UTILS_STRINGUTILS_H -#define BORNAGAIN_MVVM_MODEL_MVVM_UTILS_STRINGUTILS_H - -#include "mvvm/model_export.h" -#include <optional> -#include <string> -#include <vector> - -namespace ModelView ::Utils { - -//! Returns string representation of double with given precision. -//! Provides additional formatting on top of iomanip, so "double x{0}" becomes "0.0". -MVVM_MODEL_EXPORT std::string DoubleToString(double input, int precision = 12); - -//! Returns string representation of scientific double. -//! Provides additional formatting on top of iomanip, so "double x{1}" becomes "1.0e+00". -MVVM_MODEL_EXPORT std::string ScientificDoubleToString(double input, int precision = 6); - -//! Returns string after trimming whitespace surrounding, including tabs and carriage returns. -MVVM_MODEL_EXPORT std::string TrimWhitespace(const std::string& str); - -//! Removes repeating spaces for a string. -MVVM_MODEL_EXPORT std::string RemoveRepeatedSpaces(std::string str); - -//! Converts string to double value using classc locale and returns it in the form of optional. -//! Requires that string represents exactly one double and contains no other literals. Empty -//! spaces at the beginning and end of the string are still allowed. -MVVM_MODEL_EXPORT std::optional<double> StringToDouble(const std::string& str); - -//! Converts string to integer. Requires that string represents exactly one integer and -//! no extra symbols are defined. Empty spaces at the beginning and end of the string are still -//! allowed. -MVVM_MODEL_EXPORT std::optional<int> StringToInteger(const std::string& str); - -//! Split string on substring using given delimeter. Reproduces Python's str.split() behavior. -MVVM_MODEL_EXPORT std::vector<std::string> SplitString(const std::string& str, - const std::string& delimeter); - -//! Parses string for double values and returns result as a vector. -//! All non-numeric symbols are ingored. -MVVM_MODEL_EXPORT std::vector<double> ParseSpaceSeparatedDoubles(const std::string& str); - -//! Parses string for double values and stores result in a vector. -//! All non-numeric symbols are ingored. -MVVM_MODEL_EXPORT void ParseSpaceSeparatedDoubles(const std::string& str, - std::vector<double>& result); - -} // namespace ModelView::Utils - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_UTILS_STRINGUTILS_H diff --git a/mvvm/model/mvvm/utils/threadsafestack.h b/mvvm/model/mvvm/utils/threadsafestack.h deleted file mode 100644 index 1f1dec6ad45..00000000000 --- a/mvvm/model/mvvm/utils/threadsafestack.h +++ /dev/null @@ -1,135 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/model/mvvm/utils/threadsafestack.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_MODEL_MVVM_UTILS_THREADSAFESTACK_H -#define BORNAGAIN_MVVM_MODEL_MVVM_UTILS_THREADSAFESTACK_H - -#include "mvvm/model_export.h" -#include <atomic> -#include <condition_variable> -#include <memory> -#include <mutex> -#include <stack> -#include <stdexcept> -#include <thread> - -//! @file mvvm/model/mvvm/utils/threadsafestack.h -//! @brief Thread-safe stack borrowed from Anthony Williams, C++ Concurrency in Action, Second -//! edition. - -namespace ModelView { - -struct empty_stack : public std::exception { - const char* what() const noexcept { return "Empty stack"; } -}; - -//! @class threadsafe_stack -//! @brief Thread-safe stack borrowed from Anthony Williams, C++ Concurrency in Action, Second -//! edition. - -template <typename T> class threadsafe_stack { -private: - std::stack<T> data; - mutable std::mutex m; - std::condition_variable data_condition; - std::atomic<bool> in_waiting_state{true}; - -public: - threadsafe_stack() {} - ~threadsafe_stack() { stop(); } - threadsafe_stack(const threadsafe_stack& other) - { - std::lock_guard<std::mutex> lock(m); - data = other.data; - } - threadsafe_stack& operator=(const threadsafe_stack& other) = delete; - - void push(T new_value) - { - std::lock_guard<std::mutex> lock(m); - data.push(std::move(new_value)); - data_condition.notify_one(); - } - - //! Updates top value in a stack. - - void update_top(T new_value) - { - std::lock_guard<std::mutex> lock(m); - if (!data.empty()) - data.pop(); - data.push(std::move(new_value)); - data_condition.notify_one(); - } - - void wait_and_pop(T& value) - { - std::unique_lock<std::mutex> lock(m); - data_condition.wait(lock, [this] { return !data.empty() || !in_waiting_state; }); - if (data.empty()) - throw empty_stack(); - value = std::move(data.top()); - data.pop(); - } - - std::shared_ptr<T> wait_and_pop() - { - std::unique_lock<std::mutex> lock(m); - data_condition.wait(lock, [this] { return !data.empty() || !in_waiting_state; }); - if (data.empty()) - throw empty_stack(); - std::shared_ptr<T> const res(std::make_shared<T>(std::move(data.top()))); - data.pop(); - return res; - } - - bool try_pop(T& value) - { - std::lock_guard<std::mutex> lock(m); - if (data.empty()) - return false; - value = std::move(data.top()); - data.pop(); - return true; - } - - std::shared_ptr<T> try_pop() - { - std::lock_guard<std::mutex> lock(m); - if (data.empty()) - return std::shared_ptr<T>(); - std::shared_ptr<T> res(std::make_shared<T>(std::move(data.top()))); - data.pop(); - return res; - } - - bool empty() const - { - std::lock_guard<std::mutex> lock(m); - return data.empty(); - } - - //! Terminates waiting in wait_and_pop methods. - - void stop() - { - std::lock_guard<std::mutex> lock(m); - in_waiting_state = false; - data_condition.notify_all(); - } -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_MODEL_MVVM_UTILS_THREADSAFESTACK_H diff --git a/mvvm/tests/CMakeLists.txt b/mvvm/tests/CMakeLists.txt deleted file mode 100644 index cbe849feb94..00000000000 --- a/mvvm/tests/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -include(GoogleTest) - -if(WIN32) - add_definitions(-DGTEST_LINKED_AS_SHARED_LIBRARY) -endif() - -add_subdirectory(libtestmachinery) -add_subdirectory(testintegration) -add_subdirectory(testmodel) -add_subdirectory(testview) -add_subdirectory(testviewmodel) - diff --git a/mvvm/tests/README.md b/mvvm/tests/README.md deleted file mode 100644 index 09e3c346866..00000000000 --- a/mvvm/tests/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Unit tests for qt-mvvm. - -- `data` collection of files used for testing -- `libtestmachinery` static library with supporting test utils and mock items -- `testmodel` collection of unit tests for `libmvvm_model` -- `testviewmodel` collection of unit tests for `libmvvm_viewmodel` -- `testview` collection of unit tests for `libmvvm_view` -- `testintegration` collection of tests with various integration scenarios - diff --git a/mvvm/tests/data/c++_exec b/mvvm/tests/data/c++_exec deleted file mode 100755 index e40e4d16327eb69e2b9359d213c72c38521259d4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19672 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 CH2`@4 diff --git a/mvvm/tests/data/mandelbrot.ppm b/mvvm/tests/data/mandelbrot.ppm deleted file mode 100644 index 1fef16f813f392f8f59af4f07bd3bea99c62099d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 196623 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 SoPuKsEGw|;qF8pQTKhlq&%)OL diff --git a/mvvm/tests/data/pdf_file b/mvvm/tests/data/pdf_file deleted file mode 100644 index d287cfd1b87afc9041d0799e28d37725079dc8bf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 536732 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�%fL8wu%$GQyQO*d zL`$Toq{KfJ6+_5&39zV6^!GZc)I_Ur4h_c$_0kGRCle(z_Z!VLH)tSbhk)(IctoT) z4251ms_TE71!8z+26=8In3?_(5Ap`jOb%yhF1AAKwy7Lh6^FRtCo1-h$n*7VGx@~a z9mI?@Lx3~0wsQi=3_H7ByKFFPYK1X4HS9KlxWGS1>EANq4zxeMb7PP{?s4#$W%t7^ zaQq}jI6EX_$KksTq?C)Lj{zIszH0C#Dv-s|n?}x1p+9nrITH&x6GPJntr!L_H7%$z zMr{rE1S+<+a-$BWG?2k%FkLdFv@578@y<fWJg+@=-QEuIv{*~31zBhKE9(3&L(n*0 zZeaEreF9zoV%D2bhGn&>3}c8Wh=#cmDQBc?S^qdrW7>8U&LY;7tw`wCRPPk@_76$; zMCbLuag40Gi2K02eutcUsIl>+a9K}}OD7T`KN%(O#RV1o5h`5VC$EEKrn_MtLfzi) z$}Mos3G{g+oJ|HCik?BpexXPk*C(B2xW)Rx%O1o+C^5sOb_-hd9>C31N85VY`myLe z)EMF?cwVTZ)Prm=0E#>-=$;fDo?8`f6@W7rv(|tjBDYmx{E$~hEXWRHFG^o!PcoCy z6M%%N&s|r_T0%67uLh2}ciC3xW$ydtjIc|~aYGd`Dj!_Qnqr}3669Bfd=V#@8@;p# z_4Q&nKEEHHCuq;qS&<WILLE_i98%7yE2@E5xeyIG6Ga=qpuyu{MK&R=Y?Dd4H0-I` zszxaZVs+{BnGked(raYL3fL;v(Nz*w#Zee!Tj@;MKXsCy3P}1=e9TSx6HzBazqdm= zpZHS)4f1;C6sR1I2v*-TiRDV{IF;o5pJ!QyGI55VT7fg}li2DDrNrFL_V@aF2K_Qr zir#h+j!XLk!RL)$n=h0CFRFjniKr}vHsC-S!+=IbN)*_+&Q!bA88+_J0GuoI!;G%P zh?2rPT^p9lG6$nlR0<kP-$qK<Ou-VJjciuKX7}=dr<(ACctZ$x6<0!t`4;OoP&ajd zTdnF}SNtDcCvdB!U~vn6rRtP0FU7(x=&|6VA=dS1Fe%56>JQMBc>>0jK0%Ks=m~vr z$@-zGSG~_S_X)cy-L<X@7nnfxEJQNLp$yai6>PyA`mhU~F(Mi=?;P|@0TdyX`q`FQ zAlL&*ekzkf$+mv=w}L3``}Y0dN;!CXIIszw>n7hKA`T`jIKDvKgog)w!dt(%GhSM; zwI^=c{7~I{?a8l?N^R|!1+5wYl$Q5><>?gE8pAdPISdL77v-B1rkz#V><VoE$};)w z|K()AwlJ!Dj(M~%mmKMx9o55Mkgc~y8uoeu(+hO9<%HBR7`be00&o}uf&hs@ngP0f z-BwJ=iNhfBnTHQ%(T_Dk6+;lhI$D(qiYs;4@kSZdz;KG`dZtlg%P*J`GdI(JyA0`> zku7=P%Fp96;L0hv@Hab`Kr$nREiZHre*durZeKy@{};A^>HnE6U}E58V*M|4!Iavy zt=8y2ZOop*Z{$9(ipuTv*mM_PPFJNOnXCdSXkbx;F(#YFJc-l-!^>;06I};4)exC{ zfn*MOnn>|SkCUmZ#NqSD<ze6E?W|kd5b{X7Q1M*4)y(V6<MDUjUajt)9r{{4ct7<l zzU}LoTLQYB+7&z3$!K5M!GD8}KA+ORt-bGcOS5<$r#&7#SvmcCCpxk3&)z<~fqXSP zSG#_I&78ei0`|Vp!Xos3ANQbq!qV{ZgS<>_oOs{Ux6BW6qr|_?Z=aWdzKpHlvRru6 z9!@^*kHbG-YA&uc9+5t8KhdL!9zMR!U%sthv(JuB4)9nz|K^gUJ}<W~3puY-)K)LQ z?~UQwz-J8`c{gic(Djh2Q^uD;K24f?;8(fs0#Bb@-QWsp^{my@CJQ~LdtGSq1FwHM z^?#H7V}svZ-=W)FyTRsTLqC!&fAPSLn>lsAmQUCT=Fx$z>h1Vx+)h8QZ`{FP-}gMS zY--)mCrdBkZ)b1kVLIg4&)Bnq%r+<CH!GKa_yX^KA@rMf=wsQxp57m>@2(FrOZnYj zFJd#<&3@k&Ul;8zViSIjmLsoEY8mQ7lM@r)M|Wv=tJz)xha=$=qyNq2yE?slcIxtc z*!uXid$!{IG;HgS#4QcL%H2Od6#6!5YvaFPnt68*rs8is?M`BU9nQX;?7q#9_IK`= z?&tnG4q`XIw+6qme4O2*7Y`kTa!B3PZ2g3@Xw!M?f5{sTXWe0F8@hdpgXJ!!1u{eY z>ya{S6Lsf*LmEktVnV<Sv~Ly~Yy|z+GBCCxi|ONV4-f&&iP??d5_i>&L*PNc0e@5+ zYWx5$p{Qn0AM6dEa217Hz<7di+a|RnL^e?fUE_utSX=Y2a`&BfI2TBYU_l7>_iSHK zDeY^P<T+ww&M+cuuVmC7W^n9MG8yfEib%jFHPsTpGb%DC<Uu31Ch&kMC(@Qz<BS3Q z>C8b^Vsu@LEiSSqFqjI4;){<@Y<2jE2m14A;*Mb7XqJqfT?^OrlOTVi-$7w?pxCJ{ z&n}ogmPzoS?p;29G_G=A_-1_(tL<2p4QH>yfR!#?If!9cX)WQeFQ3&)ixp_+61h>Z zClAb4MWh^X#Nc270e1zFKOFEvk~%E`mW23ZK|A#Cs^e-F-Oj_#Dpmob9nuc91XIOm zlCB}#OZDL)0D*{`8%|*YBQh(5y9nKqplf=#5E-Hwfkz<yNQsRRM)I>)^6y&<U9i^2 z3Tq$XYQOK6HNYxrbN0VG3ZZN%5Md86K>G`ViOB{+0*N$-pvi`iD7{{w+KcwvVvT29 z29ZrJ*K7?jJsO|Q;z-^^eQX&-#t&kd=8Z%)Wnxz)a0Zp>osdT%n1O13hrvM}Nzubd zy4^r4$%4Vc`4KT*cOWP*C@vbdL}nEA=dZI<3q+hi6+Ts*30q@DJ&BeURRtlNQ504C z8qqwY(I3(=gi8CBjHy7Zp^tHlrIgs*(`=)<<ye|!Ie>tB?hx=Z0_PEB)n;-*(&$`> zp8V2i@Cg0cX}KZ=MVG?&KKQq#F#L%$$HS{Chq6ref7?x3{D(_y1AGarC%?kFJp}Ts z3493-$<7U{Nz;cq;%<x%|A_<@hZH=Z2^ewfQ)EuWSrDXj!5bpt3GFNb93EKHP(;Mm z8ze~&)jS@KO>w<o9tm-=BdkvpG1OxTiG(amkdu^)IkOQx5qW4Oq0r#FcoHbjtL`XW z&RmfjjHYK}_tDmC4@|}IWnu<h7~ijp1)!=-g#+XStz>Uf4%-SgW0S?uxrz2;a64g$ zVk9eD%`Rg6*^8;u&Bz>wcBhM>DRRdiXBmEh0R-C6-#SAv<35E5(z&Q$li@-$M7OrC zmzEyDqek1)Xn+OL9;9mk-A}L-G^Lc_UG)#=(7_Tzd`jOtUHUv9;_;<hj_3#MLp$`@ zgmh>K@Gi7r<Uq_!#w5&E_m6U!I^F}fYA_&}(#F&x^I0mK2NJ4_QW0@IDFej@{m|FQ z!$2T+O|$i@CWm<`SBGiI)A&LzP;c*x2^&VSReUsW5u5wm$b@xcEWUEs)`9_I=Jp~2 z`f`PU?Em<y*yxcB07zPvmA$2pW_UIcz}$c(iCm4#{Ax5+LU946rd1!d0EU2@wFn4k zv`bJKr!=5La#r7j>LyTknI<Ij11H@c30UcaZ^M5fzrIjSADuisTsc)LH8$~Oj?Pa; zBhkG@MqQQ(za-N@yjn#OP`v|u{~om8bgpUvRwA?3gzVjLJ)V2c0EOw)Vhgp|soxVU zB&}1&@oaf-vVpbk#D1Z8G&5ZsJ6pq>1(SSIO&H(#hh@}I&XB;7<+@*Ye<ng~f%m8` zyUe9+$93RDcOV9M{mGeRd)t(xprL(@h2d$2CMyrR^n88`HOnKd@`k94a0VP3E-Qfx zq=)%9W27~06F+RoS^PC`LTJVi1bbIgkgujLI%SjD7>Tcoz>pW>M5^cpX@#4r(b+AD zq|xXZ<qs#vCJL0I(et7pIZC@H<;T_`T8L-=4K$^OZ&F$%VmDOjrwmOhCcFwVcvqhc zaG#8=T%a&h_QKwnOFgAb4i(WlP6}jpUo&$p5!3NVa<tlBHZ}I+aoZrn%qUu+Y}Fe1 z-!h_Y0}NS!dWeIO-1n}5xs#u8<Z+inupW1O8X^(K<}|Q{^lKQ9Hlq;D@4QCRL>Y3D zN*8(KxWOfy<p`YY`$H9|2ueu0O?4O{e3uS0!`C_jOKwA7o&dv=R*D`g6~NLvfM5g+ zLg=$h=8Q_;IuN65@a0r)JMr2nXWZ5HFwPUy`0L`({6TCYR_w;ipz;a~MItUQXCigk zniyOY6$qzmtDd&{Xh;eWxYv=z&5C-3f5PtCUVG-tb#80T8Sd4ZnbOa|m?Oap2=2Q@ z+BS1TKs?nA9unbHSA^>6D=^L_YotJc=3)&~r?tx>$~lT&nQ!ku!EKJq{`(-fQIzeQ z{F1vd2*w2`XCRs);cy56CyWKO`S%}1XJE6a1;vFhgd~l=a(xR?UI+d{LwowAmgE#N ze>qfz&mcEOjH`{aw=GQ+ak`GA?0F?}<3>>55VP&U``IACA(X@GQm&_K5Josx3;t3Q z@iKd=DW1h~V*u?)_8gY8P{U#OlT(PkM<8+p(1F<5CPE%M(lR*)i0nsG<APKuTOotT z3ZuiTYZ>r5Xq&6%(Aq}<9_g%7;ygBcd-O3o!f<M;-RTHf+uR}Gwivnr>7<}VQBdAp zEq|}aL%$ryaadl}x8DUZ)&#Jv&VjMx7OnC0$r8jZgBcn;2iZ2TyM-vyuH27~Gw<z; z3XX}H^%+_=Mn4!hD?Qm54VWfXR4Uy&caUO5kDk1!h%_-!sIb#A1T<E~_~jrd!uYA| zP5Y$T_fm&MmS^pFmWL#mQM+@b9Y<f|RS<<xVk=K)b-G72DUnk3?$Z7AyqzqcFK++5 zUl{go*v`1I4<dh^A#V8V$&&Km$<Ds~nsjTc9`Phle1SZ}{hH>1;$^)?c3WD!fWZeg zV~cDOgAhZCSnapw(l=E6aA-6(=UKSCmUoGWWlgrq>G9#o+3oW6c6tAPU9PUehxj;s z0X&%X(CtHve(us=yL&oy{a({+I`61Lk3Qn3-u_&p_weabb*W@(^BZvUaJL*j7OO0- zGy<#>&4k&We$4OtA7Q1by5h_<elsAWj&@t5!EVtj`#e;s%sYZ6?tSjPhY=mTpK+T@ zE7I^rpQ3sp%H))^SsT?cih5R!r?=B2X3|tGAv=lq_A7_`)?bgADR2!M_Y*@8pv=OC zT?ed_40r&OU-Bj^MuFaP_#_~0;eUh()P}mtArL2>fE(nZAs91qjW6Z3r;2GoR_io{ zUy{&7YMTx5)|iA8yt$|@rx2nCW=t!1f*mvsMb@CXnp8U(uag^GOHF2u%Q&Dvqv&7F z4-pJ>7yn6MN`UvVR%JzzQ4CoN$-rvnYPpgqDu%95>op<vk<6nUxD%WL4(#(;j2Sze z3Kk#q4{}OXVGB&U(eZKalD9J4LNknbJZYD@Q2Yv>YjDAGzZz_0&@i+vZBIb|byc5+ zm$s3KC5KcVLVQ74zh0=eHVNBt(`<?~&>W8Kis@u>a`e=+0-iWC!wp+@cD%L9H6$Uf zMES!nL>W%<7xJ+KHly;57+69p=;+q<`|-8~(kK4U3aP7_4R3caB7!%6Yy_U{0B%Pu zM4Rx(m}zJ@bw`9w3A;|Ph2l3DAZ;$WBs5|jbc95X`^uq6g1#iNl|iV|Oojq=Wf^-| z2d15{H5wF1$yj26&0tws#lwJiA2qhw0o#C&JE``yMsowg+IxXblK3`LC8I2D`@h^} zUbGCunAoK^>s8U5Xv<@xy!Ik*j1H#)H5nw<bz!g^MEX4ld#dD=u*^6``oWs+C+GW0 zE*Qt*Z^YoPr5k~~lD1O<;OHpqqMkTH_5@2nD9{nv{E;@DiEzq!i4f76PK-#R1Nuk~ zDi?N1kzm|zJH2z!x}Qr7N=r^Cz6Hd#|F?qXDhGbRCgsse2Y*>t-mPCkiJq{+KtqZW zOazV#+I=12Si=VU>zI$W+9ox8rN{>VnzKl9RU^@!iq15|eaE!wQs{&U(q2!N2(d-3 zX=NtjTy%W~!8`zlbWmRTBszUQt|IK**xf|>&<(QOcm->i5&y-Lc#`L@e*E?GfUUQ6 z&jb%OzTIY!`a;P0#@w$?8V2bb!*z4Hk@iJparVo}bC;tA#FU=u(8+?X>c(LYHp!^W z{F#MB^|_#_)F6Ck2VQSkSXSp6)@G?4@<jCK&hwuK5nurUxJ>o?Gc3w!nRxy$mzq_~ z@>f{qt@otn2@K+E2sb{pp!EyzpwZ-J_~}As%d```cS}@)n*3}b6z<(NI*=#aCPL0n zk0dYQnLH|n=*Qt}jYGSh#CiQJZz=R_7)A>KA(s}4R!%YyknDwcrTM{!5~ZjX8P9c# z$u<I;hfEUN6KvYxD(}BM(`1A7666y&*bc1XOD_0b0SM;IJ_%y&527>!$MR<qhX(HE z*^T(q47Qk~SZJM>iEznEqUR)sj*WhbY(vnk*v_ami9-x*i4CqPHZ&H7wK5qWEg_J3 z9uACAWIW{(r<o#b5#8A&9kV?hg&7O3s$@@Hay?%77cJvzs}1RMw_=%ibSjI)AWTvl zS3*|4hnGHk4K%tjr|FE~ytEC7NB`ty3}v_tAEUOlOUQaN$EgyG@8wMn4_r17BiTsc zJe-nK(1QsWEkSpyuT5PJog1J?GM0!mp5NQ43{9t9UPkI~Zk~T_&<a+H$@`zq8Ch#> zEp^uYNj4Wo{GwtrD7B1&XiGI|1PTHTHSN)OX*9}YAKcb%wGXl692W&!jnD8rYyu>$ zkQl0;d?u|+WHf{>JP*pW`%s9Rg*S^_mP;!4rIcvxS;??FW?8zC21Rd(pRYcThzbnO z>)W%jT78%j>rCc&NQP)cP{`O)akgPbgV)CUyltjq+*?U(W5|*g?piYI{3*<{h#@GJ zM>*&ntym%Q)4baa^<FKwkyi~UT^iAg`e{_k-{hf6ZS=7iy4I;|_!~qTsM{I2e)xjN z;)+xXFj>20O%(%}p#TzB_HR?e8QN`c_!KX7Gt}7DBVF5Nth>a}V$(mw@}tcE!ez=+ zK#0AdM2m73U*%6RFh=T+^1LEsT+uF_iE#VHHozz3d{;$Qyk|?q>Xb@J__rota!Qt` z1372UV}!nomj8PjU-W=&rr9W&P7Z6EyTA+t;`j9GmWiLB4`y~`q@LY9;LgKC>Eq9l z>A$dRQ10)Kz_86;iBe;P?%Q&#(?q+=>Y0nz?=iCTnyUn=q=jH+k{5ytxjfGB-JwCA z#^eP?cBi1j3aok0bw&zKluIWW8*CY>Uiq*BMNdZ>rr{K&3Jlp0Q<%BtvjI0Lpvvo{ z{@Z2{K;CP|q$*>XYHl(*Hw8_QG~X(-F-VD5`<Xl0OXv6b8aunI`+I+W{JgkIkKgm{ z@ikPN^Ai)ru+H4e>HBqh`Tu=^ViY9F;tMSvMQ~K`etPF?Ddh??q}7%Y%ijw;I`f3& z9a^C?Lw2Y5F60sFhwp1!POnevn+=OZiKD?_VTF<YWa01^#K!bZH+d{@!RBDBUVPR~ zd&^H5OS!VaS`FRJAyaDR@!AEZ3l_6QqLZ)w87M{&uPUoK?Owyj)iu;l911pvqtE%y z7EG(BZ-BM!42L#A-zfkIw>*k$f|aBx(JanA4PzsD=yOI=Am?}%c?^R`TBZdg8>PW6 zVP#fI6XBLF*iL=!i87rfG8~G{@<uwOl?@`24K6I&-ccyZ8e-N!tGHntV=d9t5i-#R zvwzcAC)eV@P*le&wfU51HCS9G<)q#&=~!LXtbdM1M`|ft+QVbeLY*DZe6c7IaKs$} zf+lU2lNN<_RL1jO**<pCK}tj*MBcna0F~N4s6s(|Hs^%d9z{yQAUaFar|O#JTZ=k> zsE>kDqSF9e6(U2l)9KLAjITGg#TIf6DxM(JvHOJCsC=`iOfPdp%l(w*{zW-7DV=31 z7<bf64jR^q!Xg>3hhLax_OwY|x3-KTj1+59Ho{53rXI>+`D=md#EXA&xlIaAVX8b1 z)Z`%#JAMS4)u`s0k!Dsu(=t-Fyt%Pp{isXT>XUXtcPP9HhTm}MQ&N_`j&1HfmE#!< zBA~2ZK8ZDtHk?hk@>aPFuI#s<8(<;b`^Zqcb?yv_VvNSk=O+NM)VT<$C82&D1qQ0a zm+o`tW?*e@FT?URdW8(jElg{@7||UN2VjgniY0Ys>vR;qU;1R=x~0+zpLt{z6eSN~ zA@Ne{dX`iag=YQ5YMpC{lUM}O&qRVshlZ3v&mV2sR0_kG@wQ;mf!=a8rmsTF04k>? zD;3s|?k1Dk)#ou(e*<};To$v4t8mqW8u_Q`eE1^n4Nt(Prh_7oQuH+i`?6jW8BIG3 zuhmDD1a(};Q;Ai=!Fz}<J9K!<2=1r{2GxgRQ$C;R^-^7{V1TPi2By~rxDwf+%MxRR zhi%9hZ-j4ZI`myC>FqC7kTeiQ(+ck53YCi7z_=;KYeAHU_Uf3fy6?=i(XbCbO)!7# zyqE;`wosI0n4=>;%#ZJ36}&{_GE1wyq01jyiOCk)*JhP(o{N>G0s-jwDP*YOehA`8 ztQL4Dl}Js*EE6mt{jRkpAxfYZhyZ=FtNR%Ld~*1>`Mzaacpv?=8U8_(lefAM>kL{L zg*{jVXX!Wm%i{(ja7U4O`ZB+=Xh9$~xgPu%Ddrt)>nJm`kd}X?CZJH`-R`G|s>;ap z!wRg>_VWCDSX7$>UEyDaxJijopANWx-`0Kes7j|L3SP&yHR9g*h)-9##dH7^=Sh*6 z6EmAwY1<MSEYl76%S{?0Tn+qdtF-BW0F>q(VBg5zamk=8TZ~IZE_oe#YK@IGZs3O` zFGU9<aW?lT>=L}$pIvvDDw(mG|8(QU<|e=aisT^>M(a01nJj8Iof8{zg%!O~G&b5~ z!@tdsZq|%1#5Ud{D;xoa@t>^6Yh~h`*Zk8zX?&6aoyx30rr4bE$ut%EOTX?2dPa=w z;RLM>rDnBUtIX@6MWGgkA{YM{c8>p@fLp8}G2q!8Ey=Efc!>K8wheA@U2kqG&Mn~} zTE>8Rp+>my$!7y0Gk~iO5ibzA*Ba_RTu!iXEm_GAuP$M7UHDRp)r8Vynus{dH=!74 zgiSy>RD@WCy@4p0a@ihhkjEj{0baFUoS>F!x^cNyiYh|`%Gq2M{)XS&0tbCc73aS> zvNS~mi5rhnW>0T;ZIVoqCI(X0re_9=Z|JDNI7UBZiLzoui`6f(LPbC{<xut4`S#zT zkyIWP7*Skq(i$)UirK<+v&1$CmE(U!7;;N<mtP1&2KbFily>E)NlsN+i((5NTR;^t zV+T_%RvM}x<eM!Ng!kb3ZqcCuHycBWJW(QS5(oFutwpD4{1Oid<0#VIV_sdd0iW=B z(-ap69lej_h#3ek8pHZXZNrYrC<ZxN35_p+9h;PgBQ!vs)maB-Tx@}v@F@TOiqPGB z!m!+r@#33rJXoBrAb&Q|Rua6o-(Hg7*m37a#^qm#PoLPI6e-qgg0$E~dm?E11qrv- z@pwfSH#l3=e4i~WxP^tsuzIdCYV6E8(vxD#lI!GI5AnOD$L=RN<X|n@bI*$2fGm`r zBqOdaA7p$C9}-l>cjG)7ao`%;OrSP42-~p@!q2A<^2%r(UM^$Eyhp_5Gs0;|m09C= zK;6`z7;!8Q##NOw76YR9y5*mIF72%X!7r<u*D9_oVdyLcI83=H6WElMI{Wtz$kPG~ z2bKV@4{0uEfggo8;*m5B%3*CpZ#;8Ng=V*auRo{5&j~W<S>psUo3AL2PoyOgFSHi- z(ZEF?oVrJqRAd9T6sy%)S!%(4H}LNQGkV>l6}L+zT6XQ8Bvl)>u5VUF6(zyP73b-R zInW3aaSg2KafB^YJoepiNX0cixR=KO?~EE8s6r|?tJbODfA+}L0M~YmM4cy|&$u{@ zT`_eU?q=!;l8tVoEjw%GC~x@Yh+j^v$u1587OyavSFt>iG$s=Qn%~fjDm1Ph;C6;~ zi=~TiByg1)r%gj=b*X?}63pyO=GSHY3^&0o9uJBH?LCcM!c>Z(;*^*#Ml^>3uZOHR zda#`_2$Qi+ubT(>thwqHk6+q%drB=Zv?3+G*heI&ZGv5277Q47kS#+^V$l!@`G2eu zTP<lOj+-#8CSzG+HxWgWq&I=jN@_QR_&t)BD3K+dwzs|*nYR!ZwCzg9Qeu@y5u27( zEjvW4EJ*nrDBElW*R1vvhNl~3V$BL#J!58-Sv(mwZN;8?F`5nIHRX~Khx#nBx`d7E zL=my-$Qh}e1;sT3j{9?ibTXPv5^*xxvwYaO@?m(W2%1<%yzR5xcA1z~vPI;{P8Rm+ zMO{w=c)iGjRdt+!I404>qSRT?xzSxUCiyR5R)9cqPRT^0(rN?=&=!F1t1m_{L~3(r z@V*E;P-svG9MUYKCaC~)3kXUEHHQ#mBEk$|u@GhCfH*QC29t8~h%XopOzC6dY}8PI zVzal)=~v07(h;e*P=3`mPICO-7&4Ol%A>@Q;YJ2=z***zE#c>-6^9LJqMNxOxZ)n> z`96VpQhFz(&{yT4SdAxC-T*J7Dp(F~u#i5#@Sq?=9WZ8z3cTbmO&l+om20f~%I7Pj zMaL|?YHbUb*9D=|b_@M9!(hlTROF`kLRV9RKBodGl{?CUShC5Lw-_#RmN~9>ku~BW z#zzwNb^Y7i%ZJS%hfN6dOJ;LqEG5fi9}h3uUrunVYmJ$ypqUyr;R10VCLpG5{3+}t z0PU9@B<Rn7<kCPYx)TdWA_@gpu7ED&f>?3<)8ftIoqTMRzoS#Djt6?#Tmg31C`}6m zjEZtX?q5z;bN!Dz6&<0L`0UAtNPw1=ZV6fq#~2kXST^}rl|gzAR41*c$Xe@$K%7op zln{+$5#);N8+s;C;!3702arXM_eRd)4I49%8$>`<r_jV20<kry#dVJZMl*t;2*g-U zXf|`Lk)?%2YLj-*P<9Xy+t><04ORqRhSv(SJ0beWijm$xMXiR`H`_Z4!Sk3EfdH|s z?wMrBL=Jng>`>jlV{fpkh4tcqs9?*qo>~czR=027r9q{s%yWBH+_<%)IYs!GMfn>P zG}1RVdEmh-(U7sTTVGx|%p!7wxuazH%3NdUYcbIRr6x7yN>5&OSF-V!jCmc98$-Ct z%!bL+46%&vriO9(XjY4A&b1N)UDYJ0hA7Mw!2hE^>h39=BRLl{V5~JCvGyHCo~Rn2 zOOrm{J>OZhetW*IA3FWKAI~RO`F(tUKi*gMeBT~&FY({b2BR}Xd`*AczfWrQdcA*+ zM=wL|dcU83el}<Q-k&ew(LV1#yoi3sdjGVAT)zHxHC=|ruHOFs_-Q)xsY0*H{dRnv z{C=nV@$1?;%ix6{6s9SP)G^TWiCuR6NK30{eMgnALZx!854(1Y2KLJ16G}sVt%n_u z*_FW>|7e5okAN4Gc|6|Iv47+&V9sVYoJTJ0cI{N0U-Qv1gmz3B@omF&6xgxI@Sa5j zKQylU>+nF!G6HG-6*J(xf(W!B3vcypg(kS}6l)w8KcblpYiY`E*)H5=DZ`Ys!Zi!5 zqBUasyQ^p(3R*a<cuz$(z_3x<rO6$`yDHr5qlrJQ#`p#X+FjV@kSkn-Z94)9O7HT( zCbps0vIACdkdHuib5Uncz-bq23K(^SGyJ|sr#Q11+tISEx4gg>jjrihOcm1SyVB4l z{JJRS^)h1=G>TrzYBm?r7Otho{P50tkf91`0I9Gd3G=S&igIhcXyyueekqHj2;@j( z>}|A;;^`Bjeq*kIA=heX0JG=~#`t%-&%p+sa-_$X)|8k4--`-qU}=DjPZ+HoWO1)X z(CNM&KYfLuR0xKBO3ez-&^+jk(TsG6?2J!Z{bwO$EY0!AlGwmm1N;F)J|L*hK9-x? zC!I~enYbVJ!^rh^RFeiSH!!RN=7Z1iHP1Ls?irWR;f))kN0CFgY<C9xu4Fc@^HX@% zaXxeLJLH*r%-wqlDdy-Y$<6i5xQ~vScH44<>BVe+s~;a#a6SfOsrx34MLiXY0}twG z&5bEopW+!YHg4b%*RA0Gz*&TVVxuaNN!AEZkY7lhXTsnFdzK{FL?7u<cFL2YjVg&` zJ#f*CM#VMbrRN~r764`!4qijdA<FSZ2=}wgty5#_kEF~R0C+SnXB|5EQnfiZOXa01 ziX!5rv+$MKUEHVY$gED_0QS!x%OxR4IhiD&UeL`?F4qi=C)6~Lbz(fN=2R`rv{|zR z;^yRjy$-d@km_!B8}bNPkp!>8?VUZ7>FBr}%cTe5v5l=s=fZ(L-p&o6%0gbWDylgW zlE?H6Y9XdXZly|yy(o=?8YGve{JAsKC?p`#nWyO4SCpeViwEmA#OOIm&sqoLnX8JS zq{lMV17frAGU2lL5nW)*53Hj4&fBO<KHzDLg4&`M?gcF<^Ko_3&o{o_8EKnmmvTdy zc3rfJ3V0U)by2~y(W{s#3C#HNxO+bh`c#$nSmG%D{;okk<T!%Uw%b%=QtC+o0>U9~ zBUvQkFdK#kBD>t(Ri~0l&lN<7NoH1^ZI^eto@wa`yfQD4zMB=qfwCAXHKHhLu4%)e z35(keDH5a{U&sC7c$n#@6D(es{=UI8q9ivAs9|D>qyj#I47~jMZ%!r{w%>a`nEJMM z(Xb<ObCV(j+jn*KmF70-#A=KAZpp6f#zJF4m0G+hd27f?eNbP$!lXe!f}{2FiGj37 zg#irH0UMfQDArSA663<_WT+i6;5TY?NTAT?G#=j*${4l$jR^R7A1JeLhP;?RlBA7V z-c*jO^|A5!6HC;dtVHB-jt_)W9{if5=%%GKLt4IUUPm-?L*Hfr1i1>AjCzt(ci*$} z2sD7D9?f7n1%&v5>KsCK-RBVHw7156Rt7DF;9k!m1$;tH+3jcd?OCswFYal<2x50I zkmYtgMSQ{d!OprEGxneFu1fNl7I>5F4>g8e_=kYXh=_tm_@D?!XY{}usWOlbq*7C| z*W%JPuF`tV<L8th@a<-$)p7EgoODVD_Qy(80Rv-Zb6bHlHzi6>bU1`zAjl-tK%V>x z<tD8;c2;BpE2Vk1j<(vr8iKs59#4PWW*@mQV(o)q?gqQHnKK4Cu<$lHz{MBwc(p~= zXAPt0>KfSyKQ1B2yL*%oBC~l7Mb33%e>4x~AKHh4tLu>hK%f0_>i|HT^;|}Qz}nvc zhj0~f+@ky`J-g!{5@mG&+?=(^nlvZz)i*ND5IxpSjporl2J^q$U`G02%iAFO9q915 zHgBk@64-4{Cr~if@QLeQQ~Ph>gd<c{$NHZNR+V8t*yBg{Tq>TXPgXw}b%pz%)~KMz zBK;AIQ^Gu|^#_Cj7>A`mz};H2`a~wQEI{j>596ovRFBzc$sB_VJpMjoUHuJt!F0n4 zh2m*v>SMJ9O&V!{0w%pAs<YJEEpih*z0V3Wg*yAelgNULK1^x{og+2zum;#C)JSO= zqSU#)>8`itbX$#1Xrq`cY?>M)4r$kMHBK?Y*L$DR9@37${zu;@PCDP5->Avv%lD@} zR9*d<jmZu%j=KNcUeJf<(F!>xn&mBs932BBuph&l>by+{XPsPN5Q>d!QhYv7s$r4L zY>BZcK4=VdVusm-<vQ$U8$2Wuqb+r8BcF4@6N8muzaaD@869wzJrJEz|EgqRDpP#D z3plmB9e5ZT09wGbzT4?+IrZ4l5VT{&1xlujn$H&k^_u`g7hKZ6??B&1jW#fJ-<QiR z1$Hm*BE1orSlUDeTq$_9(*{86cuFSs71F#H5v&laIKefkHtf8bvEZoySv}t<s&O?D z9E_lb=*A`VXcA^E3GkG|JA$<Arw&URwW=({Zw)t^Md8i>p=ZWD9t3M4PCPo>mpD_- z9cPq<It+?U1$s_7Xo52QaM7pHfp`XP!6yvhj*Cv^p7Y*^G*F*wL&%QOd7fm!B*Cun zChU&;@})=+>oMtMDBp8Vy5o3lIr)C_6-`SHSvC6o(}Z9fSP*yiEP!0?O+=?y-wIH# z!^xlp_Jn$ov2DfcC|v1d{dJ8_o$OSJMz9VkZ3{vMzr;2%V!j>odD^pnpy+a$5@`ul z*m?`7pbO&xt0lhZTb>s8z<2OiJ>GB4u=oHFvA69zv#gvrZ51J4AwEhr^$Ot%qCPPF zxTP^9As~h?>y;<V_1lCXU#YI(0$~(k=OGey`B$g)^^2c5-5V|1mAi(kxTfRZ+AH|Y zl_6CF<;k_#YeR~*Vqjq$JVazqIG81xqsMDeOZ}<WJdH6CV1>AqzT#W2EyfBTfL7Oa z5<P{ud7y|opIQGt*#_h<^H_`)z?iVXpixB!B&=BI*Gh{D{9tTn`2fb@7GVlHvBF!p zmFFT7LcwENRG(WmYHETbAN2AfC=ec?hlssET!k=!iJJqqbtp*fkCxnQ2&sNC!qVcu z)Q<Bve<XIBvw3UdqI?<-2(f62ESr9PK0mMBcKsgDS8M$~9$ua&>G^-}F8ylx@8-ct zz{(WOe%yT?TWfoL-`__s2U~Z2J-t4!OM8DFp23HGyL{!2@ZY_l)!KSKPpftC`E+-` z4}YD{y!mOhbAMbur+*&Ne}3GNJ(pRumGtUVs?$f#R^?j%C3C!3B4rnnVV&Ff;;#A3 zPH9F!(PoXy)H~UPoY<Dr$nJ|y+=MI`aBI%SX|Z{niSH}QMNrI>vywo<$Q895fUTDX zctCMbi}q*UX{*q7Jw{_|&B&a`9>xMihMv?cRVA7^)-_7lL1P$lcN?N6-U^$34i0v9 z6p?E)DmEdRQ6?oY4u#-#x9o%QRUVl+1jgjHP0_qfDUVHO0$7rK6I!t4)CJX=4w|dY z7eW-cd2}&HbVTe;84;<z($#fpvNwgcJ+q4*&hiTnnz(sMM4;CzTPRC`A=S58cFLAO z+9UYHdeN?;rV-ViNLM}9R@aE7SWcYd{kb(<i9|CR#uLLi3Q}9^pk#s7183l`1onE! zX6zkALIZJo5}?Ugfy;!KPIJkz^o^W^wc46jIJa0D0}bsAU3f7>%aV>;&SIIp^Yc@P z5Q7=%JSWx|R>?nD*IEux<T`DW@!K$6drB7uMSQ!aPe9~P!-CZry0ojt-$qWvpF-g2 z7qkuwtoQKi^dpg`;f^>9aArta4zKe}MYM>R0@jLQJMoaq=l<dI6cIxg?FcW<o<1@q z&2XVI1&vKy>Qug9b8SxQ;NHPbu8hn--CV?)>Qt}d&xOu^ZnBAZioYFGN?^gJD^JNt z6SJEEfb@zfv@4JJOdFLXSh2rxX4s$T(wlvNJ;^1ErAyU9P5C@t35U@`;Nv4VueE(4 zL4<<Zu;JflY_N?x8vq*HChvl@Wr9no_rdPfA7Ro}GfZg9^bTO<1bsB+7hXb7N`MYI zw%PJzuu7vAVxeOdUZPVAi?7N(dxJVbmbE7pG6`(V#T|s!j~55mb)P&rrT#@kkppVn zEYOx)6V&ukm286Q?Wk)&HU_>M_Kt@qbJ8n}Q>kV;N83A}zdY~t28{~L@Y2!FHdXb8 zQDHto5%U)viG^**cB*os3+TAEd>p9<&d?<40>_Ar@ao=`wo9;x@tT3;`Ek1C*gUGA ztYW;41hQ*c$O&B_DeR1UvPuu1kA$G?=*d(j(|C;skAbCd=DNv*9JVe=hfV1cMN3q> z1L23v_Vvm~NlZLfiWFJ1s2(4q@YN&4{iFah_a@d?2=c<iV$tgz3kOJa#*2?Sl>p@W zzVMm*)2*qLsm--J`-ow0XLUBwr+JTecIvC)swB}#i=A}g0kRhb(MeWpxnNBP6f^|@ z_u3k$3?3#vyxKcQC@kD43iIo*?bP>qww`pAC8$TNa)uSoBH-Gh6ZNp9+?_aY|7Cag z6^f<d16k{y;Y!XGo0RCNm7Cg4I5d_+j900=FdfO_{S}7h_YCU@@E<G*hZ+0|Dt0xK zl4`X<z>t94e10CX@TscIE+~S;T<$nne`o!wVyW7#kwJWfpm~SWC>2FHMrdgsKk0MN zURW;ID@naOcER}--n&K*fVfGJf4#@NCCsCQoHhw$z!;;F_|iSxpzSySHG5j1PY?Zc zF~*#QGELCw2}_^bXAc7CmX5Ip$>d>F%L!w2sIwqP_@nY!sERyoGWQ+lsa_IMJ#0LQ zKey2u=}1wEI@ocJgG<WV=rx6$eFpt|iFmPb9J~#~dBc}xWJzQxli>SDM?_DkRJU&x z5X=#O{vJ~LGSvs1ZO`jKDRY$A@`1WM4Z<mgtu2JI5DMw)305C}Rk&GAKfB@wl$N^Y zWXx2)q+ehrFj8OUmq)q!it--D2UtqW#dVDSuF+?1x5W@c@dNJe+XBf8gk1gRnu|_g z1A-AzFkEE|DiMb>j01`ditg~~Js)4!;DMuc!5{!OKvvkgxh$7+ohB)2fAW<&r}$75 zmu+f{xzv~-rLC+MD#@7rQna-C{jLQBRNCH%RY{e$hG@C1Ok&LF7e+5cK(?q}5mJ9h zos%>PLGM`4i3)c@rYtY_m?%Q$|C)%suFoXqSw01tNNFoxHV3PB;~B5C89<NI4!Kv; zrE6#%l4xkkG%R)?tm}m!O~PWPiFV_G=x^+dkwJ;2M#~(PAoO#_O`B1%lJA=7^+own zsF)4zq<d&o>8<1xLZs%7JFPo>dEFvJ^1wT+E29<aHshgXAAabwc}@&0Wq7!^LXfHO z=a*hMf9H`~&8E+tl+6N?HJg(K%?PhJ(q*o6!n)YVkr)Vpa?ilFSSANo#n)${0C?5} z0=Wv2tmPHcsm;zI%`1*`Se?^$^jiptHlb)KV~ec1?_R29nlrLaeT;QfWVGwD@<I*6 z&{o<`G#1K1jhHtd2;*QziUO@@J_7PPtx!(5l+Y0xWSyg-WD*#Feo%r4vsj?{om`0c zmpFq#xwo0mkW!%1R*9-&SM4|_b2AGxG+EsgBLdz=CInX+3ofOeA4lkIFbWn`r(c*k zDko9f0+m<F38FFaVSnk0LxEPj0iHUxfU4upT+SDzKL9*i3nk1SKwuY(>{GqR;rl+M z`L7J^tDsAgY2lNhsIiNA`yhi9+ai@`&fth!{7lIdT7>9Z93bU+btkL=$=9o%p2^{e zXS+(q=!mo6t_<CPWDylHj;yj@GX2q6K)z?(ks*wTt$=J0GE%@ui4VY8r%!a8Dym}m zo6hJWMP@0K{9!;*{lbsbU%r^hgS~wQJ1#@&miFfzXL%yya~y+sZGKd&^$dh&w5Z&| z7xUdQ`3%)vzoI@J2Lb4`Vy~5q`>z@f(*)bod2zA=n;G`@0tK`fs(ny2xNzxkz-0m} zSpoX{I{pn>878FKDe9j5+@ea1cL3Z&L#+$n*A|CjFiUEwbau1y63Q|tc;t#guWPbF z@gb!OLMI(rHdDnSYAv4aBj#@S9Hc@e9Q2b@_F5oY9b790(w0V-&<)9uiaF{$M#Z7p zl5>=)wmKkb+qKM+H~HP<_qt@<49GH*GA*p>FNUe*cjChU$0$!%kd7nvE$u+DXxi$V zZ-^ery7B@VQ71f@;d&I17P(58mV`#ZSR|Zme*wBr<|i-o68Zv$P0haUQ)ds^1PmOh z9872Z8HRnR|3xt^bm8FJnHK|2M*~Qm|20}4Z8U+kiYRph@HGc<TBW(Qa_!$j9l5|) z&zUk{$|^d6@QjOOyNLuO3fTRsD3x9?s$&FZAWaeD$w~;>=63WUNsvuBQlL7cM(agN zw&)Xojd>%FJ_Xt5Z#2i^i4|ArK*(~5`|p0)7x<@wjZq2$xWrrfm&keF#UiTW&~;A{ zQ#g(kKM*Pk1}*{u3Wp*h^lJfYL)2cdcMA0fgjIKzLV@fnD+nYphDojmDv>I!2VN2> z%qZCIuLZ*NKVTM-2c9i*ilZQkruBq)ErDw*%5Kj7hSx{g1B%~C*-`(Ev2$wAMFEy{ zY$q$WZQEY4?G@X$ZQHhO+qP|e$?UnDdCtZ86<u9j_4d>{^ln>Mym@Qrk9$OBT43<K zo$q!F>dNsA?xE~;S#$hhs9b33Z3hj=CsJ9oL05y9DaAkw*Rxr$t5QZ|DrRe97OM1r z@W9Y_?}w$4J>Q;Qu3TUDb^$)z+`HIcy`O=)OGm)ZN3+h4@6(T~-<zZ7`}gB}-O|JT z*Uwg+fPgo*55S(++t+>DZ(HCd&v%R9EssBY7f&x&-Y&K8sYA2Y4l!)3?-{Ujan&_8 z)RuPS%Xfsry6~(4$U4ciBgpNeO>1l8cza`zCqOLYk#dV6{XN>rZ2MfGUn6*Pi?iaF ztS|R_c>NaP5HlwHp<S0BxR-lxI%gD<cpfGw-1F^I;+T8WM;x=eirRa88>BV+I9U?Y zqTi$K`~Low`?+&%>}{Q=<@kz9d+76fe47HNL5-?Xx7)|Cs8`g?rFX?7TVUL}46&;- z0X*loN@}Ec*acGu7E&@N)C4Fr_~3mCORD-s$1{SgQ8k7e4rKQmYbzv+T`9ZuQ0{=o zC@NnBCt)ahsL8;{i6L3KP7k7$uymYDp`Xu#7UDBy?0iNP3w8>222m1LTy~wJeq4!# z3_CzPpU1=L22N4|w`f0&gL4_^x)q=dP_Vx#ZyzeY*LpF9cSc5Bw!9E02)7G|S9po; zQr@6oIIx%Skjgz|XL=&apir?-W#LI<KCPFSl1OB$v5Q<p-b3J7v;*CS;wY<8qZ2VX zDH8MDvx32O!zH=hor`8t6x&kCohT=?^zNAmA=Mw9KV0YJWQ;Rlb!oRuBfrA1xrC-v zF{<&ul1uXcrPKUFs@HJ5dm}B0-ms=Ch-`!Je+{%d>(zK6J|zjD9#RRtJEETBnHevm z{UejuLcUw@x8k4QpUI{8SeH$Uf(M)29{7H@0m>m9)Q|OnlYHC+rNl?c37BtBD94$J z(3r_k@{6;0$_6g>1ST=a04@C-ZpdPi!y*_eR`+(CMvIq8YSJlK2rw~q4djiHOsaBj zp8g@OabB;}e?@M5I2o!`jfn?5^(Ku^ORAK<^){u1r_&Y5piUuhIl`FtbfVs1L~K`^ zNP*G22%4~%3Qc5o<`knGNY-Pt0<H0b!<A0y2fUv2TOoY=rPjymi?Enl<I%XnFgO;@ z(v1l_W;rk@>#teIiv9+e3v7end}=6#rB3in_xtS^Kf-~PJo05jc06UX@E9lhgUN6$ zj>^Go)J8p&r7CCi!G$W#!JJlyXo*gjb8=IDRMDLIz&LBQg|HiQy88xZf?66=-)L4% z5adJyVOVs$twURFKR{}A*VSNToJNAu_k#$a{w$tO0+OrYK)hd!A^DJ=vKB#yh4|WL z&{hTH+GdHm<Cl4X>t0L!ioYnQ8){t*F?3*Bteyq_m%>=kQ>w)}G8wCq!{5<mQ_O|M zl~VVAAA*W>v@LthR>Ff%s*TxNt2AP<X4QoT!};(!BL8H`Eb64)CwXRpmRIcuW+sRC zvKU)yi^PP_RV$(AuP6$(sq{lnqpf)~D_)+PEU8duTK=;qA_T&{Qt1~Cyq}4u<Js8J z*r0=|ZBn;5m6cfd@F>py7?sueXZo_v57oE~#6fm*)}K*cY4Dr~PEm^OBR8<6r793! z%*;Zq-%YQ3aRCFQ{U$z^MNQRV99Yuv-v*>F*4@pFU>_k)txg2V@yNzFqtGx9sxG#1 z8q`ncn520pM5(e0HKMeL?7x^oM=X)xOiS@%d3mTTr5!HGZI6E>St=y~W{JxbE>oQD zi987rvG&L13+$k{m;{-bT0{rtdA%)%W((pFZ7uGB_(bG7YUztSBndAOhfb;#FEcsf z?HKJdN$mYTYf-i1TG(tNS_Ec9oC?vqND{iB&o@|d3P|M*N$*;S?Z4-X&3rXsIz#Bm znG2GoG;S1u(!)A#tO(BaQJ%r>$U>UUzD}&*&i%zh5u_K$LPQ;OlKebXbxBTOB0E?S zL)5e#v7zXPAH`YqgyghqhT<^A!tthxZ~3AM<F10_k+bx)%QuzOa+Lcp;%!+wQ59!b zSvyX6-5op6KJxEJBWF>0YQLVJ2M0gT_U^qrwfyXKYj=6_dv28kZ}MMdd#_`FGQ={s zu(Y$KOU)TaA1_YcpOU*fIsH98{N5hiw=1kPBPoT<l|IedkJp2bDMt@4FDJj>$L9z8 z&LrnG$wz_R8jmW18%KbH-&_0XkQTij;PB?kuP<wV#&7*UZ(1sj1WG`tZ>W!H+TTm% zEvf~3!Yd$V;W`>~qOm{GfLH7N1|arwF<E_-G9Uw+8RS_=bC;0N=l%Uvm-q9*_)M0k z-9Gqo@TJ|g+ug(O{q+m>vj&~bkO8~)MPS>vk!Sb*?XEpldsBgbbO_tLQkw1x=oT!P zgTDlcAbW+X=Po?AmVX)f6@l*heG(=#N%8`7jj~A+(P9M)aN^_5s{l#KENn`4UYI@Q z6>pqb%%LI#-+A^f(czs++)yF2>?tD>;(a}aRr#d+!OIbKflL8|UJ>u3#fvw><*}7* zGmJ6-Pl>;W`V)n_)D%3!v<Ndf68;LO?eg1&+bdxN)1kdCI4f)z!M5y`j?Rh9S_}D$ zKy-{9>lV1al`Z6~W)+e92d&C7OG`*`M#~1L8uv}Z&9)r2t0Jw3M2c~bbPH39F#Nek zjES^I`%pYNO_k;z)wy6FNwM|~s#CSDJM!7|`cx{Jzj9^*kr#TS<m3uxFrO%FIu-$E z%Qg`~cVw>h8CI!He$5>_u>GyYRVhu*ehMsN&~P;=3BxqMQ;AXEo<P4yDw&xJ=ZeLM z`$b?F1!{Hs^*^3IF&$M;lt<!Lrw`E=WxVCCnMY!yz7O|__zgV;AV=i`mB<v14Xo-> zLCM)Cora~-BNA2uV45kLC|1*CC#eZtgr~VA)K9s2Q!vfGRaf2&#IS7GhVnpMm&t-k zR+E$?U9O}udb{qTEQ#=HR;Qv{Adbg7D9VHev40xAF#?NY@89tie+AK0+n;{GPPnqf z_)YFC;S_F5Hh?P>Ic6I>91?%b3I>BZ^D0_>P*Byh^p4?1aMYrlwMIkIsb2mewf)Ez ziVF`+v)Y(dQ`?+yt+P$Ts?AQ9rKaXSET?cw=6WL6J4s^5d2@<mu7uAuRb;(Isvjg3 zE`c*#3_8~{xS(1}0h*JyBgXsb2N#zKJsOS=8o+sg2MQ^TKa0w&T^7d~U&bFLaeQn= z%X4!L%9!0zOSbY}&@l?R@VEd;bVbRzYrw;unS$52vvi+sn;n-U1#AOWK;|*%%4$bp zvn}2#ML}_XYrOLJRsQi9A~dQW`7_w;X+6C5GhW6G<1Z9JE}J^pbxcZfD-+iC5wem( z)QD366g$o~CSmsK{+~myKa53*WjxvSA}c}vBr{#f^SoaW97o(-GFD}DfmTLMV>)ba z;KD>A$u6hCTL0tu6DiTzjs*BC`}D_K!qysUJ}u~SGY<(*@l(K2t)(Ur5w>gLsai!N zlCO3%B|{Ro9B=*{H731Yk0TtX`iRi~1B8Mu$n4cDhwOV#Lk5aCdMTMmtm!&!wf2!O z6p%QG%HZsdF1t0Ygh)1NUJ7xtIPqQ|H*Jm6wQx=@O_`NQco@{?0IJ!;OWxVziDy=> zt8I@YddHE625hYV;y&O5B!jrYiDRiG#tN5*wmbab<av)5<7unpB$)%wn++x)7tZYY zU;`sPMFZMo=`F_o>Z03Vc&O$)UKi)X!LY^L7$hUvSnWgWC#xawTX-KVk)Kxj+AyxO zTrVJnh`TtvgfD*1YH6ZfZrvn^*D(>O;CTr!n(SUF)PUJYx3QW%PM54KDcRy9)9$9J zO7BXu2Nba16=8|X)R*k*C0w?tuach}<>n9GW^1UC8g~ZGTP-lS&|Sy&-qY~KIF$?` z)O3{8k=IF}O6m>Y4kJB+2yZk!#WP)VP#BL}(fWcD3wAc_p>V4_<<FCq%U05Ns3>u* z@?&qjT&w<x>!ba{r0KLNKbYSsr?k5sNW^te8dY+A&_POAQEvkVc5MAan+P{!x*k}C z#+#!;2T+`{$4<OgdaRV~6m&G2(jA|B-HNKLou~nu@M4`oCGw%P!GAO1mCMw$V&l1& zaK_6b?ao@_LLAW0=})(CYKQUjDKhBU)SFm51+=qPz7LVipEehDNxMAV$BBchh8=9! z=*TS_btwfO_&YQWy@Z6jo<*We6`wmV{qe%p)oI!i(+$^JGF@6xDJEj2C{^5YxQ!tN zs~VEjGfnzor`Ae4uLhtkfuGp%JGw9t8FubK#vJbkrqIdEy0lG@c|JkJ??(rQTe$OB z^6<ceaE%*LxJ_=8;IW*ZNB?(oSIbPCP~PoHQm~1`ki1kE&YnpsyyL_r|NH<~ts||B zc9q;M;)D~>LsAUimz0?`XA3K<U;?L-m@<~K2BE;adtTS!*+r#dpc2^A#2+&>r4}Sq zpgf1hip%E}EuC*AyPKa%xhPr8vF@Uf`a#b{BGP(+Ihh^uc%LpuMlLNQ3)XrT0zPD{ zoJC<ss<uvb*6R^h2z)}O^|G)(qhZ#<4UI+crGsL=qNKkJK5^8LHd;L;+!WZMg3DN( zYvn0|cD}R|ipQJq7z)uge%`%*Ob0NA<ZhJ~qk>P8xLV=%=6ByZA{S40!>0U6G4Gck z`yFLZQQJ4}6nkMD;mx@#*29&cq}D-P)t+>C=`;u5{iKnUmjL;7{hGRQ^8*|L%I5sO zFOJUgd;uq2{{<|5`qk}jBM~Cf_Vo0>Kc33+d_NvMpH|fG1bn}5&IACTA3TP8dVP=| z|Nlf4@O19&Mz4Jn*!FD_*t`FDaAk<gG5=fFfXAxm{!ynd5F||ftWom~D@MYnhz4#M z44io4Wrzgwx|mZle^r{gfDwu^j3KFWGcFEe)5exgIw^gNC)xV)gcqr;?Q8U|n#x}@ znhpz!YUhWJ*BC;r^zI#@Mj*dj)MT}PYtOK7)zLyxTCGWoP3xqElsSciIuxv^A27+n zT=Hj!-Rd!d{KZuKAN9pO&ckqiLQ)5<HuhLz&r)(xFy6Y(UT|-g_E4FfnF|%l3QO4r z`D<*ii2fV}c}<Oy<{Q7aje7fTM@;%D|E6h)aiQ&;(scn(+KHsamgSW?)JTp6bf1?5 z!JXs0VSY2Li2W+3mZB?<CvRbuG<PPlW@hUY;#0OD&&q{R>>bG3xcI`VIimCWtv9#~ zhq%mw*2wn`)#P$`@riZv%CPfd-RnFGuCpRzPl0t`QI%Wlu!~MGd39qo#<PD)^owrJ zzImgr#Ln*gBuZd)T6SxRJGjH;X^S2`I(b{axC#=>a?goXg_6L;EHE}hZd6gXc{$hg zB6z?iJR9lqgctKg7ZMj2JBCz!o@}KQk>^ZgV5X#Q-G3_J1LdA}uqv8&M%zV-HZ_gX zCryHqzY9r*TS`QU`J;U0hV>EuwWKo2-Ns7eB3)@Klvb5z2f6san;xl`*|M){Nz{z! zW8tBpcRX1Z$>u^ww85E%GbjwHL>Iz^IDKtup_ggKq3jdZu^J)wSl8nH{aP=>?cd3I z#4}m%93xv4`USkU%<jmlp2t2nQU@A-%4fUnwDN2$lueJmrP|G1@Eiezz`k?LC>-VU zMXXR3wqg9C27{oq%9jp874l)jQmvExEB+B>A7)qe@;U9YT)IwAxsYDnSoNcw+(a3K z@KVE_8L`$_<wyP&qQwQ&oVrbXdpVi_%G0KV3cI!GnPW#5my6k%qL<4};yNPmN*-CF zUg#0X|D^FqMNsT>=-QlHyI<?n)1^*V1#YRcaj7m<i98`MD~d)wCa&?X2w`|@HbV@1 z=2I9RPn<(~=RaXaW8JEKOgX{{v&h*GcO2|0W8R3UJ{(qh!taS%C;$FOIUa+`(ixpZ zspRgrQ<V1yh;Y>nZ<UDecj#N0dlQZU!O81|3>5u1cUN@@G`v!0$`{w?wT<zr<GLey z3z@2R?l6`McM{DWf)(0dvmRNsYwkqGZ?HGK*--_Ms^lkwI#Q2417j^7lF3%edQwVh z*ghY>gfq3%JoPO-s3WH$W@h7eNJ75r{~F~V#N;_t5JPW=Ty=y7QwlxgAYb2F_m&ao z18(3DW1BF{@kXL2n86AT4rJ8b8#CbxK+57)y3+|M1&m21AQX-|O=}4Ar8mR#Fc58{ zE?5dQVepYcHP`ASbgHGEJ?s|)>7yRe0{#lKD^Q=K?UO}trXD4s3i4(Du>{<gu(J#z z1~kH1DB&gN#HgV3%15BdH6<$)rH#KGi;(G3wp`cTE9+eK>av5-AX~3Yk*C}L%BV7- zt%3=i43ensG?-{?Q!+2wf@;uw6`Fp+r+s9n@PG)Hgrq5dOJ%N$h-Df0g!5SJ(y9RE zJU~?WmZGaf=d_m7rL+0U-MB9D)kwoML+4(0F&ZWw7BfHp?Vbq^rtzTqNq{a$b4rkH zPt5#h!3dmABO1OYmyx2uPKN&sF44S`27E4ax2d4uj6bgqSntX%<)*!XY865?i2x!~ zvQ*|z1$|S7(x}4^WRd^ibY|lwJP!^uBr8k9)JL$h+?GF)5FD2=N*#jGA`Da_2I;zD zi3vcq+AobAaqh>gnOz%pu#{&Z&dhw9zmN$|nW^6Prl&KukjXRRgiAuw)(D|fV}0Vh zLK?Qaw&70PyoSxdaNcBP`HuuxZo4$d*mmM~c;OdGGbIVXWi-w&UrAns*UkZ7QK5q5 zN>y%c^vHk8d_&1_7FGDKrmSU%<<z5$l~+@bpBcLp1bWJ*g}oZD(HTJvTN%%3eVW|h zC}d>1S?l4;JB#3IkQHX2`*kGygqJdw;k>(=={SyU8asx8U)7MOF~ppcQJy=^d(ET{ z^cjwNruPAyOd%`0aczOuXwD0c<+X9}$bt!HW|}4hSp@)x4e4_180lzZwBAFA0m4un ztyBn<y<(vx&I}vr(@UEtWZIf%@*7R5W132%SOPKGBg#YyO)C*}xwZdxUZoeY@yKGv z3xB3m+=58jl+eu=?)wbLkL_9rqO{R9;>pG!{=#iVR!)L(oPt2_PtXD?sv!3^X5xp$ zLy5W=Brg)4-<x4-iPG@)`}lhNItdfl?e_8fIe9qvc(_W7lmEpRc(}N2iD{bS>*e14 ze*J!aKUs2kyWQCe3$x4f1AKlj80q5)pnS0W(%;&Tg$Xd6w0pUDzu)~{%=_}q;Mx6l zdTalFApY`W(FH*#?Lv}2cSz=cK5P`r*Qi1b009AI2$j<o@tuiE*57n6AXrHREMvNo z2`g)A9y13bl)qHMZd{`|i{1NNgvx8a^e%gG#>-GHhMb4pa0OZ_(noC8m@z5QDB6!! z6Dg3-g<^f|Y2xW53vHH?7_4Jg2E%dbw4z8v2T7yD0v(6~k%ERcn{Nm^B2=C#1z-l= zc8`GFso|?2M60(c+a`;*`e=XR!}fQ!%%6yji({m@GjCUy2D5J{=ku7mwxH;0BZDY) z8xaeokl7ZE)wqLWLPr&bZq1MvsNNgG8T2&=Rwk^nBby>61F5ti>izL$@@ShF9i8%- zs`4hE`Ety^FsB4RtAgW_H*W|p9`zMWv3#!tB&fXuz&VzOmQjyzbN5wy-eA;TNijyr zh^05B#mWi>T2hbDni4W$f*j+9cY{zcv_L1xQJFE8;%KsJbP?ZTuij6zp@6xvP-;tr z{X*$~5-$nqM_xSE6y8hh*hUGVQ#{xHQcbq1fW9?v@WWCYri2l)`o%q|dSrvf|EJvC zsVww5Vvtr9@MpILU7B)!GAl=kLouAC#^(XJ%v7Nug?|B=eA!*&ApuO1)z%r6wkZTx z{$n(ra9X6S0TQW|USuwJ<2?eA&``d5W|VgSvTuu3$^6f&I8!4TTCR%rd*VaFFETS5 zo|2Q_)Lhu->|%%P{fRVQuE<5S*8m}1K7Ub+7*(g#_(i3+m`AkVB+HCeVw>Fkw?a9a zXT3D{5t>KXMHbTqENfYWWT`kLkqfy+h%t*vBi2La&G20Z5TL(8pDx$I*2PON*SH+6 z&8i(PE*%aMTb(8uJsh+A(JW!U{GlB+J-H^&kP{xueF<{(VQRD7mi<p<pZm7crgt(? zC~5;%=(crC;lQ;1#xebtbQ}Cv>XT~2p-JnCzwD<i^I!g(BuD3PtK4BHJUi@;iXp7j zni{|@3FWS`Z4%3!x|K^vv^>BN!g@b$_iQaR61qLbwR=uBRpkFZtM0);^j%m1RIb~~ zxO<(gH)al$*AhQ0oV_N21>MYF6DPp1)B>$GkNz}e%m}`<rw(Rd_4sDZ?<y|>m#|aW z=L4iz{F%yn$liv|*1szOZv?la10ry5J?5|<j6cF13iL?%iK?LFgC%ix7b}jwihE+t zO;zO->Ri0RM2)`b%IcJ5yk~32&0DD<7FkOFZCKPT-^F3P-2EK*Q&JsdNW@nB^yi#< z?eK>raRBkThX3-$ft*M;pu_0!HLqUS!_22$L&3@r)~MCUa%+W<t}TpkO#T5Fe+)pu zSZ_<!i=!4O?BYR##LaQ7-V(YO9Gn%;>)==l;{T;rj^v5o6U}tm^OtOmkSc#6{%F@0 z(_8mhI#cW=6};p6rIP$rXNQ6sO}`CkbMLJngyl;W1PI5F;5D_)2dI&q&xi)KR`QSZ z)7L1p?dO$aSZ>P|xxIHX-1i^R{1HY|b>S`H$~_TxH3lzgPu*>FlSn`2Uue|{`7&2< z76`K_d!EL?h^ZJj>4qyp>XK3enr*t&`CXe;VPWQboMCSSC>qsMuwq0-vbPA0u8BY< z*BQ>t^(4IvvRRi>?u#gkq^*4%%Ume0KNhW4o(Qbr$%!iV7cq!d<jL7cJfbEBBzmB7 zqx{f&gA;HW+EciYZB2oPD6+b2zky1lH|L=Al|%=<E1G?VR_*q-SwuwRX(1bBTj9PB zzY5|#4A(x$@~~RnC6i=sf5BB==<x*pz>gQAH<NU(5dO!^)HhebXc@(l%)94ZrXodM zS&gqu$5qV<4G!^9Iblcr-m)R)yHie23)pI)wna4x@0HJw_?y#jgm2kqH6ci0OVkq? zig(rdfb%ByIz2T^gFRC1A9gu9R9<O_XIXA3SstwHVn;-GfqB#@JwL;<7MVIFY;dk* zTj(V3g~b}IXgOtby!16(JcxTfCsWnqp11Q-(B+hi#J7qX6&Rd|WX5_~jc8=cW<xac zw)2&)oh583YXMxDRut8$_I=3)22;_QD+tOk&g|bENmfU53;;)-xwL^bq+$NLh(lM2 z!%|Z?Z2f@WI`5>$6|XuBJYw>@X1%>JGY!9YM5v4Inwb=DC$73lsokB<lRtEs7<N=E zBadVf7m&s&7j3N3nxiirzET=WF0hm4VDTBLP$V2)(KBIjQbIr6V7IjJkl_hE{2)vn z=FQ*ROonmnouH0ZDd-~pD@l9TOt+>YuJc$t9&<+2G`DV9WT#V_%)KfGPFO~FmF z)q88qle)+YDT`=^S9Y-P+gjDFY#9_-1Yk)iAmG{C2`z*o5kBT`<_No)#D{T~vQ=)k z&*5Ye#xz8iR9@%_{YWHiCE?g8htuntKN+2O&<w5lBq1T-2t1YFJoK>3X-rRT$w6n` zshzrc9}6_v-h`H$K4?-yJ}^@5WZ|v=@r6^0TN2HVNDtRcH+Wnvy<u0>ufdHw%x+fR z=(%~UmTjGy*H%pHH`PB+x3>Q2{r3Ghh~u!!%k}f{cK7}MJULqG8<Y3$^s=!X6Va%P z<>=!R;Pd@(-tO^wdp$VVI@9mt_iJ8PABz+HneL0?*?u^flldUHIf*qMK>=I%GmXCq zW~`uY>eDD292E?WBd9Uw{dFd)-^sicmLOi#5t)_p8_vt`&Fky^D<i%p(BO)n=hq7$ z@aui}y1Ln^bdDghz~=E@7}BD1sTL4${dNo3?%B?F47b1<#wp$?KExI0IQCy+@rPh@ z2hnvS>5L|XxCuQ+Sw^2X*v+R#LbKb5wMEZ^79N<ZS7v~M`SzU*3*-)KsV^)W2E9R; z1Q=YdW?xxAuD;=^<W>7O5cv|pJUQWX5Kf!IlXynVNsHDV)UbFJT@#s#KY|xBq5y2K zecdSwZ&dtRKHx%1n72{FD}~!HLL#5dKv0A4+W%2dFAhn#9&#tR(qd=7MH=Te&55G_ zyAWIDluBT7TlL2O8)iOZafC^miUSiK_aCg}4^>|8l9{J;CO`J6uKFQN90OFC$t`$| z$<{0-^?HQ_avjVA-Rjh9J9~)->(xO#6p@3y;-9dMS%j#tUR*DWH}x)Sbv^jr;a_c< z1Na=N0<G?$l(x$Zb{^DyV;lMSmYB&Aa1fBhf*#x8<Go_5*Gz9F+$@)~Hhi>;Y@e|p zwSx5>GhPyyV=Y)iVxiFIDYihVujUwwv1j>;JITp4j!nyyo;ZCFxYn6o+T2-_Q?Wxo z$m1|7nW7MQb*0Hywc-gRg}P{q`1$jLC|BLpbAD2pImW^I9Q?^n{9A7%HZ;?~0{e&w zh1#h{AVTHp(_;Ivf+jD|Frq@5p_P(~OFHZBT@Q1ho%WX3vbLjudFIKh3DG<JvKith z8i-Grvt{4Enn|a1J)fOs1E&R_8<K;HZCGpAV0+ZWy#y(<Z|7)T*3~FQm2yJXue8%4 z4r+<ja78=eZQ6;%AaDep5C{F%OYy^r#}!EISm;P#6<uXU%`b$%SAkrpiVJU3BJi_o zT$?g;P&`Y~)K)!Pw`*73fz+R}knj>nO7?EF2Tb<HXQFDKBLz%+!r62cp0%WE>u@-0 zJS}bLL=0p$U4aaQ@kUf{%_P4zqI@s_u2P9%-<FBcY4uI>`D3~w;IU@+u*YO<nSXfn zgLycL%i!nrC^m?~ZU;wro|w`R(93Sypu$axSJ=R?2XTDUl7nFuKMF;QgB2B;l>Y>7 z3`=SVyDCBg(R3^pH%_PA%)m8l)=73(a=U3X|3h<H6|hFNy1{b?Z<6Q@Mj?-e6Umju z977&l^dz$`NyPnws+;9Kn|?X6*S?nF4}WAEyl~;ih92`=e}5cJP|J7<R-C!JdDHd> z2GTBqX!;*WkSzae5+oA`JInt|g1lCrvc;Q#2Lyg0_COneDt1N28L4(Rrpy3AFxw+d zUqi&w#)UM{38tcdeBruYp>IeZM!Xpd#*^j9G>#96I7atE)ybNrNUnI+OvSHsraZjY z(GgpvsLh6(HmjCAVD(R(NG%I2;S-tCwj{UoUotjqZlDi@jgM|Xfeu^|wi;S5W^x+s zqhc9WLAMwN=Y@4Ow8ZYtApx6I|D7RX{l*ZiSCJF9)LjY9EpiDa{EpsNl}U+M|86ck z!$?Dr)$SOY#d<Ouy3c<;zU+NCKCaLt+?LkwAVjBeFC~2Q8Ob||W+WVyCT1H8oMXM& znAB)ddIY5rZ{lwz>TEO-r6N{5lBSfmc*v#ki))B|#y6;pP2C$3v5Xw9VKpg;S@pb% z?YFwdq@mspVC*iu^{q_3hD;|f;U2}dW2;{L_v#`N-Z-`xVfv<wc=ynK<Lg-@*Of=k zTV&()YUMG_-=MufpZOGuaT#jxX9-?0!_lWdsDbES%;pRR^~D<8yD}tJry_^452{)e z!O<2l0DUkm{$xFE)(v%I_?LOFn@<bTj3l*n5fv`8`lV(96VR5gSQ4{Apq;0-^NChb zHFE-VOU2QZ!e3!>8?tISny@iILnSX{4qK$h$Kr@aTJd-Ca?nOB*VEbXu8R6^V@G*? zHE}X&QBkoLrzX+eK!q*>Wr!yT+I$=<)P`<XwM2Ou#pPA}wn2YBRgo8zn#Z#H-?cx{ zWsJg|D4(DHP!m{1ojB*!E)kpUS+%Gu7?oC_6M-u`I6|c;SAo7(o=`18$47)CyMI-= zIHR`JW7#1hAaS>3VHko|g%hjYIWm$>K&#{PGp~Ky!S4ppOiE^^5jdEUq?iM32W6p1 z-0e?@755j`X8DkzEvokQtsahnZ42}gw1~q+#z&5>g~G&u2jp3u6Uhk2BRQ|O6#Nhj z$5SLDtuas31|tVA%Oz-bwXuL(`7;95q*I;Zp<_`q*x@|U8z{%&vG)Tg#Zl<}5QiPK zfU9uOnAr^UXIR-FG9f)cD+=S7Ud3}S&+&+)fTHiQQ3G)E=eIziL%{}1fG}|~mh+Wt zlB~R<gax^X2*Ha&Es5qy^mCSaW?rvuORM8u|K<ef7FZF(B%W5LHVX^Q<#Ga9PUtwr zHz7!ips|$nuy1m;U<5Mo<^@3ndkYFEp^`MBv+S``?IY1?O_#akuwTwvBz9c%M-pjV zO2O=AGghNrDGpmvhK*m>l>}O2iY2s#Dq1NgD+o>#2|p*aK=4tKOdOp|=<5W&bT)Uo zBGKc6;V)bZcxx}eHSas?)AM?U7N>K@ji84(u9Y|NbPE1RZIYC~wNi?hQ8E4JTt;A) z{DZspFH)uW1BE2ZdE@*lhA=Rxh*+!R!Tt2yVm`t!It|Z&5f5gaLL#JK*=&$}0b<>s zSDMlQK268*1a2;2$%6aenB?{V_7qW@y$jVnE>)mk0@nhg))@-t#bU4z)+}e;yQvUb zY$3<$zE6n6@?|g5qdeTM!?5lp({vvcEWsr3c``9aqs=xPT`M*2?`@6EjP#EnRxE23 zsb`m!;{@~LR5AihNzMwjjl{N&0}=W!0GOStwuFD{FRTcxiCrU9*|W#X)^FbpO=vCD zgenLnzNb}e7UaAK{?E1>VjC8(hO}i17Cd2C$O$4N=$=>#nZ;L8+hmTUH9gxW$iP5T z8m`mvICqP$V9fF!l@P8e183(NZMx^UvKA>9i|Hm_oNM=TYCC!qU-wUuMHpVN`Q*Z% z%S9zT1sNy@IVSdeY#R#%3s&SORU@6nDn<q^sRwJ<_!v+d>6?@DN?{76u6MCz<$*dm z?L9VWGDxV349!|?D{5bU#vMkYS>!nQ!1rs+7*^j)m@1`Ou2kkI{5@5F>=uL0x@m41 zG9DvIOMAiM6BoC);g*L2gt3v&nTw$df-(WX<KfB<K;N$R*3P!CyX$N15#!sEmcjSk zv5)ubW(BdXZjWz!&)fa|`R%J~Yp?ddTEo)J*H@gt?v8K!hcn>y^XcWxPj64R@Au*7 z@%3N|@N_cw?yjlV-P;3r*3I*C`+ay^x>*bBy4o3Sf=xy!6nOu51j+09c)WVNez{Ut zS4Yg-+uiMZvUejr?DJ|H%lXkx!?3H{^VvKC{$c}sKVF=jL%G8Eg3ds)3K!cIocM<p z#@ZFU_=|7!VY%_-XRZ5*KR&)y-Cs_vjBRz*d9mvsUa6!uet-Wgr~aEu%k%pTBJ-<1 z?rSjNT`Z!ltCdH5xUqZxd<lk!cPR0}*DgcIP+CiyE3@o<&=_>S*5YP@ZPlmkbBcQ1 zL1)fYBYv!GX$ql^`hc6~s<qK?YS^&QDqu>q<WCxZ`$|Ay--=D(0$!JwpsnE}(zzw8 zNxTo5vbgV-LpK&+OLR=SlgYf*x&C4cMMmPZXM|sLG3q@_ZT>ifGD2(>Uz}znq9jA- zfs1WOK=KjGR90HItMX<AD(ETcaQ8sT(=T=a2M$VMkHH#1S9}CS;FLXMdNf1b3RjUS z1%AF9y;1}Q`FmCF+l!Yayq}UrK4`z&%d$<h6&PHIMKqB_pLM{5gF;{t82wp6u|<Me zHA0aV!<THChS>xGTP&uss~4Vz4E`9%u)#b@x%p2Xd&&!3t{GP(q|QKD7nA&v@jn5_ zgAx!RArZ{CJ6#;>!qL&93%uC{WY?m|OXuCRKrX^qXaIwp5rRYeBQ=P!zYE>|eZM!c zr=bu-zTtJwc^6cnHw34xcOb-p(HM;$cC(~dT3WV**RqqXFqdhX2CZd71+Sn#FlSM! zE)!Yv645%emM|ZXP)D<4JjM0^4LzPWSl@oxhXB87C{ne1StV<85HWLF&8Nhx@=1XL z_n^gngc`9}=SqUdP4RJ@AsZVhJd*^dUC<r+V?2^|6koolFcA%jxi-&Ma4M)oz9y9v zYH2Vl6ZY0f6yHK<?PikA1f&O9SAa4hG_yi)8sj?CN5o=MNB0f$+8ailtiKd@8cj}X zzI3n{u!F;bF_j3;+xC>gbY+RG`kP}b-B!Cz?=Khe?`fDlPSlvgBHV{kddR<FZD_~P zyNb3Ak@p!MPBa>O3W!FLsKr8s?;MQuu|zTazA@eZ-f8SL@c5Q3*wtZA?mJ^GSTlQv zWJ33s7IX@ipwa*pFeA@nuvI&2CgT};AXLBx6nl2!WJ&Cg@(YZFt`A5znM<LC<(88n z4bdia8`|yjnxM@r)8*LS8%ye!OXxQr^0^qblAVpqkHBjixhd_*OV4pBYT~#PF!b^Y zDdhj4+zAnHm7^}{CdFSW=)4Hw06FL`WtLGthEEc>V(S}T-ya{4JnsEpO{dF&@*VgQ z)|DM&+}QOoSdCHxVuEavWV)|tYe3QnKK}SC&lX3o=>szM%G_^GB%`Lwz7bbx>B)>l zlG00{^CVTdnSI#X0lZ8hd(9$Slag&fS0A^<Ju`I}EQ)$@)K<8U*4<`qVVK&vJP$s; zDY+egYn7D14Vu7~S1y-ssDL*Ul>&g{P`?I7l91bhh;~C55lWSAhh|01y$QW%x3Hgg zPB=iMCz4IB^fef*H0a>{zGAq6kY#yQ7ZjZK?3UgL=F&!2*DMt;+o_Wta=LR+k`Bp@ z{!?%>DsULh>#7Z^IA(i=1*yebJ}yZ4AD9--aq)<#KK~%ztV2=hJ6`vQe+_OJdJEn> zy%XMnd7O9TnM9`fz&(bB0X~*8O7ZhvE`i&zePX_KGAS-rHb$47(dKN}4wfL-2|+4h z@^*43tRCKeM>a$p$)Pfe_k(j9G2FdnAPQMJ0tqu!Px8N7n?Ci)u7nWXu*Gi4)*TNH zW|uXPV?NnGO(KkWoud$Rka?wsWMQ=Kb%>cYwExmWaHDp6s@Viy#^4eFgU|}RfySN< z&lx8D#MRX^q=C{FKnjI}vsuZZ@n3Nytien%<^PBoBJgnm_oT9Pe3&#ttDwzVfGCq< z)SPxOqZK#@QBTU4vvQLicvv_!K}%o^<LZbmEW8Adppe40QmWyG!hq=js)30wuSN3% zG-%kMJ_(O0aAJ`oDRCi&mPF{jH_rDj>V_*992{oEga!%~+<>myC^QMQYPW@a*4<&V z^hLSZf$;T5-V%mihvA0Nzjxsc-D3RS3i=oo66t+!(gJ{_oc#U^2-NtV1Q>^3F9QX9 z%@rUs%ZEvSo%w_GOTPG6M&(N!WQv*vEJeE6EU0ruvf+zPi)uqIC!)y+WD>ik0M1?s zOKzfQNh}9%oEr*RqYUKg<{M{e8gVsYTCXN(5@{u3(_tq29rBEbf%gS;%Ku*JSY`w8 z-$lILl|+*}D-1`0`ApukV-l7?C<rUnRtq_i2bQ6rP*)k62R!7_-jpp5sF~e0=7G#h z|K2`vMZib-|Fj@;xJQ__Eqce|9A{F+))7ZUBU=-AZAAbLP>v1cMqzD0HG3FVK5-tY z;E$I_EKuPcuTt-TMXouUf&)1R-1<BRN(uccg&>bIjq2WD-j;+#SR7FAW17leKuL-i z1@Bq^0RP@DN<U75!=7U{yZwVJ&Q1lFI9>o9Jd||CkhF9sG+?U`;YHZ2j+;!!wXP$f zZmZmDjeeOYIcr&-Ma1h+l8)oNS1q95lE8nFsEakx*`oH2eD_`ELO{p5aq#!)pyFa7 z|KsZ3q`r``9kI`s__p!TSZd}N(>BCCIF$d1K4#YbebfALPL`|hLexdp*9T{>3O!^o z4b0y^3xckZZ|50ez@6kT1esS@bT7$Czy>{y+0yS(=K(9uhDrB8B?)8B%UB=c3fhN` zL`}ion)Sq~;YsV`Ch0tT&%8%w5`Z`W8UCt6SFHx|JSkHmA(d-o2<Mu}W}rakV7DWJ zIZQajvcj-72t~_o;;sV#gWwHO1CeMtH9%`FvDcQ;-ck@?HE%(A+`LSU`oJGEPYXAU z`e9_cZxE`<s&8==y`{=FN>PRd%Ebfn825~gYY^$zEw~H85SB>R5mq-2({ZCqWB5wz z@@wRq(k)u7prL*`@iOrC!{mvn=dhR}+eE~T?j`U_XT&ISv?^dWcccavuadzH<~3l6 zyzRm~CvDcphd(k7_McfdoJXU|`EE@rvSx^RmCeU3N*p34)9|DA5WdQ|?FzKjLVQkS zq>S*K%img;b`$r8LusAFZAG&ON4#N`BB6!E%AA6FZeBp*EPTE}<b|UwJGCE|x+na5 zKN*A^|Hqf{7@l?QgUdo@oJjQ+69oe@TLp)uO~!`A;kEg~xBhQ%iW1kXhY9dSCP;B$ z=|!Vpvn3G)%n$}LCoy4t@`}&O*GKk(k(tESWK|NJ4Eey57%QYasOYOp-0XQLy(1?y zslEhEP9WOD;h{+`X>Is|)iixPE|R8vanuJknRAwxFh6)k%Q|Id;F{#Ex>PTq)~9Qi zs%K)P0%NqFyH0O_=<rM7<l&NA!<4ZITDKP&^J_MBv?9NNWt5pMv}E{lGBW_05HjbR z^tc;>B3;6@aMuuRB|$C~!?0hwA`O6TL*Fwj-Jz}$)Y<<>fNdkq1p++lnqJ|F%!%V= zhjb%f#9!qAgqzsBiQDeMG>&Dw4k+Lf`DA;JQ_CX9o*9FkHnx1bQE*>xIX3n@2+bD; zr0NZz9zH6cw<g-t{qr#Nbh78?`+J1A#*vr12Y9#urha`tdpWY}>-_?J+|0?x`E`4> z|2X>i`TXu(Exo+u<=x>6__*`4(&_5`-oL-!|IPDz`hLAVcsMF6yK)#^qoKV0eLijH z0Hib2`E~SqaNY)q8P4DRdVKtheEIwy{M;NZjoGFC{+``AeSCS99d+g{zuI>PUP6~1 zy_VE-%h{D!!VpUS>j}v2CDe*gi}IK?_Y4{@)*6?GN8-HsQz<tpj?zLMke`Oh$`483 zJD<%Z+MuvQXd~@*sRjiiVa0W$d|O%`h=m2VWPlT*Zpd}6%7w-WBZIyJ!6R;UaPSUC z=|V9ta&TPC=6|2z3Mxbd#vwb{CRN)LVF&uVPO~!#D5-Tzqw-jf1e-93FZPMv)X6r; zg96E0jjEA6@pn#)P^Sm(gxP&2Lz`5<0xRhI!#a1J?-{|~qrQ&Q-+hJqUlFm)(Kf$i ze|4#<vyV8?QLiNN@(NW@$O&k0wUNHY1p>Bfn?|MX+O>_hZd)En89SU89xHc}9X&sq zU~TEC8hvyU6U3^oQD-Vx5Lvk$e%OPrXIkNYC*v5lZZPgjwPnWX{&p}P;qAXaN)~ks z!LY!}O^wyBkJRNFW=NJ}nz@K`+qXYnxKOP57O*uWjibDKovi41CCVu)7!suUupG?M zXXMFgXiZa)#j(A+J8|@4j+rHl8a`oz{-5qf1&13`L;)_b9WMAZ`pVK;#{6vv^Wf|) z*ykXwCL@U#gF!s5P-+<Dc>j2>{R7e%qiK?RXgLRjc!o-LL7@kowC;eF>ZNyJn6NfF zWBmn>cm6DG1<DMm9QdvWE%weXLmnke>`JzJF1JaX!;KRQ7S6g;4oGK^GzCpt2jPMU z5X=SCnfat6ozlyfzpijO6*(%%;KIxm*9|qbt&)I!joz?x>Gulr-SOXj^zJfx2O)os ziuCOxgj#7906*<Cm#S(wxG_f7nN1t*{uA1USX9DxB5KWQtt!Q6hmC&A4`rX8U2j^7 zm;U=M@Mp5kL;C{8loN6X%WQra%j#+Zyzju|NZnLHMXPNBe=;UKMBTJ}%JwVPsh_q) zBY54Gb({Q)d&1_fz*+gyJB5>|L3<#D<+Ucq6ue1rnM{z@j%dUN>?$-Ax5%9<4Rca1 z@djG<Wt2`st3M=ig@UlZz&SclF$o?X$$s;62MZc4U&fvs0^b<Vcs&$X8Ds>MRVx~t zZPl=V&M_^eA2=KpO}Xi#HX{NCz|EhoDGYhZ9;`<r*!cogk!VXeH}gIlJVS*<zBQ=E z7)9L)TZ|-_fQnm~K{m0i7P<=phGQOeL@3P{snCvCykbNYoia#-IS&<}SEtqTahs}5 zTV_fAw=eIbYDSK*>S|U3uQJybl%@_@DKuLS+Ux^L6}!qmXp}y+|7xc2Dm+GKioarR zHNp#ou%yK$_HTGJn}@`YpeKiDMh<8hs}gQ_d#gJ|SI{^eoc<TVmuW4|S&HZZ#XgTS zXGD1yL;ARxNq@kB$zVWeDbY|ew7JLc$0hs6p7O6oBN{d)c<{W>r>&|BI)<P1vo{1K zxsc7)65C+YbZ^|yvKyWw4?@t~v6ncO7X=OF1X25L#CxG`xWh?FYG_b$&ytH-^qx*L z@bDGehUG}hWd-YLL9n&;)pUT(3YyWdTr<_io+a1=HC6@Q&<y?@MkPA0y&c3H2CpoZ zJn!gmY-_@fL5eSJP<hcmCd+eU|F3nQpsb84#?4DWvg@5~EuqAfyXj@O{CB(H0wTOG zIfjK&P;lX2&sG+zgxlL?-B4ydt+5CxsWD2X3NG+Pon@dyDMzlr&9b0<s<`>L?j`dw z)AX1_H*6%pga&TT3&=;~+q;(+jW^qYyuRaZDEpg?QB6ADVZO5jyuH@e1Onn*hGmj% zO?tdCQaOci4Lf(owJNQG0U%6EQ?SYAACfv_hVdo$yy<oY#xU=K5SzI2X+tua?E`9I zQwp^87iVhg;e`z~O9#bRA%Qw&4JR0h*0-EVRk2>ZFiS@yhK>-c`EiXbxCk+w9g5bn zvszS!DMn<D?1J*f^p2z&72*b^Q%Pm!-r)p#@pQ1-$=2xc+cmBI<a$G`1KfNzj;Q~1 zdUqw__P)?rBK%?y`EIC`#b8@2DrV`r;i($`Rd{rSbmd?T)>(*;QP8~jhKNzpPcW0? z&~N(~lU2=h4s&mBm61axJK~|HJRF$a6=8u~M8R6kdkU*u(U<u0Eok5q0m9%#@!XA) z6!aRBVU?sF&mV3oXZ27itp(<E5{I<FfG_)$!i6RtxXJa<@On0Gyog^{y3AIrNDZ{B z=TwzgYH5}(PRHJc@53-lNxq8(lihkXj_z|GZr8>oQW>~~2EnKAIe*l^(+HbZ|I-mN zUQws`?GT&j=KAavI#k03be9a@QWOX#@HSM0IV>>^bP&E=_YVm7)|N$FsHs{Vlp@8j zOMLpO_Ku+EfA+hB=cnryy42B4b|Y7|J#9I%JnqnSF-5G11#aC67}M7cOMp!(trE1Q zb080#py?&%CGa_y^a80lXIX;NADZFYvA(8M3G%L(gNdF*sW*{cP!sG_-X2s=GN66` zbYoSLqd#fLMn!}|GIrBDV+BjjNY{}z=F~u=<|5yOx|2Y1WE3(oPIC^|3X1cI4Nv8& z=!Ru_@;nXnPo%ZL#+`6Af{!+JTdJipxCgB~Avfs>%@r1#YC-GZ6;M!%I50*a`J65( z-w?I@pB+F2P?{&^>E<dNR}g`>rXXQ4Hb_Iwgo<sM_Tox4>`=?}Rr}_OPX83qDJg4Y ztxAHJfv~_TQaN&USnM=ut`3{nbxSg#(G;6%>_rOBxo=jxGxfvP^W)_R#*Sic)J9J# z^PJLic=!lgh()**5PfMf1uP`fSDPJJ0_hU9HoJwV3}7C4k>0F|*t@zL0?e$hs9!-o z{~qK0>p~AbKSvMU3ev_5iIa}8dSk7Aj&W%|<MNojY`<B9vvGT~<0qq&&G{$m@^B9m zxfHZHYvL17zNzxsP&v|&;<&@Pb|F1Six=eJlf3QeMz=hPw$STpMOOQ%mVJ4*7O_NI zTi~;X1E#GBFqns7i2vwb!Q?JyH|QX!^UfKVAmAv~Yg<!eqXPR<Pe#ri>sdWDC+Sbq zQgHG&ZG+n$(unpkI?eW7wFtxhx0BMjNS1s8wkDY>-0i>bR@9rF;!2_C<i!<qU$~%Y znXMzRKt^-q*v<Cuj9%jQU^(D4jj0#J@M)fH&A{*<H~NK)&9p=(?SQ$POANVBdMLnq z+*qL5Yyns}O=Oj)0J=jeY*Vf@bQTsewEO(nq2Csz5{b4@sV%|<b4dtdyN3thOXYd7 zd4bCXw}zTpc?dlibfo}jVQByAzMDu}6Zn33c#PZY`#Sgv<H+OxdA%x=$evufLnQbC z)b06xyuX&c$@l%dzTZC$b%nY8dbt1IPstP9<?;g_9xk;9PhQ^S<>U6Azc>i?cKP^y z-k*OCI!D*KNN0b4W?r@*_uM@IPeWN_R~`yOOvgcf{J(%HP~W#Pz*gBCLGQ*9;1^in z%U}PsX(-vT+nK%RCsH!)U}mT7&NiBK$s0?&6QD{he#B##uC>e;)$GT-F|k!8yzb6` zbv_JPBjeiM74YVT$553|P$$_H|H&kJ5;&afu}%<VVhdyz#~PSs#^6{2Cp0>ea^42x zdB~$=>(UDOBnX#iIESQm=NOLJ{<GYlnXp`5C1^O{D3AI$aqqLSsV*h#04~KAf)#0_ z>3$0%+?)uyqKRlLZeE1vX##0s!}AA|`Vr;@466p0p{*GViIoN}Z;UHd^ys@2i(h+R zhWKJ#$hH=r^a;&SBBTZRPH#I0sc$|Be?6FZB0#1;!ft^=PV{LRo5J6K7SRY_{JtTf zNZMW-UjND5q8ife=Ag8nkgLM#@PqvJtXq|`hf^W_qg+K(Lfj+DDvCP@n}#00B0C5P zO|Mle7X^;MBrId%11;D6*n?Y^)>5c2<B}N<tSSA~@K#+uzY3rA)4>2$%c^f<)+T!y zQ=^PU3?XsSmFmUAj4<YyfZ`t);t|s4gbE1E-;z{vt`-DPttgqofokVi1QewQdd^~H z!XSFa0cM0$s6|5+bRt8CFl-8^eKOpr;So)C<1QKla&%jX^+NAD<{DfVUW(Ep++mY= z6D}j3Tnk#6BHU+<00k$M+XZenj&~zpCKs%At9H{!n;R$sNR=v+UBg~_8DynqSrUNT zzeA-j{0y4%P)cKP(ZoG&#(16BGZ_fzp$D#bI^5P3zziYm;dt;m@QjFGdpptOth&R0 zzJUX)SjR1EGX#042;K)^jq)@u&&^Z+4yAVh;C)o6?+Ug<>qx8gHHmagB=npUfO%3$ z7zImho8wlgOQRhDA@(2fmv>uPEuE}lb9)Eqav>uylFB|J8c6gcEO~X>Z4V>^K9Q8R zIK@sng8>N#j*;vpYb_<QyBaj%+9-?&rGynxvO#8}SP}`dt?BG3=WyLIN$nZO6cQCd zHvJ$ALy}xjtJ`8Q&D(z(!b06^o*$7I*Lr1I=;i_ob{X#uq<~g)t>Dmjk4IOH(>cjH z?$2|`hGo6|t>>|cHuN0uM1^YwCv+M^Z0R`$ezWQrUv(gVr56xtRS_fO^u>bj&En>7 zH-X4v)p@m$J!+Q{VP^2kWHZMSfy5o2Z86@7jcm5p$`D#@eN@M7GC&siLTdeD_m)+B z1p%BT&=b<DrxVtfPqyMOESs$i!o&_VFi)r5&IDtu6TC_*Q1TTs*`?eD-01`R2TaAn z9a>xr=hTzjk^4cTASsUK>4G3_5+7wO*Nz@Z>&2FxiGwJs`8D0wZq@nQZ8#gO8hQ`s zw2*$6IKfB*`Vq4ddfm+W0KYD*siQBhTZbRXIDaU?o-cW1cFtz~hp5!6g|$k<a4v=L zTW(JlpR%##PUy!bCQkk>jP0;msC{D3*|H@z-{=wL8qY?wScH8&t%sR%xq~o`*d&<h z*23{(X@?Lr(4?JHTE6auLnpa7m-w|)j|9JPF<-_6f`>(0TS}F@8w5#{Ycd6&-9DYd z$-mr-BJLwHo>Hm>yO07-2m$^7!`L})SE6-WG&XkZ)Q)Z2wo$QdRBYQgv2EM7or-Py zs|UD0?mMiu+H7--**lSGZ1==h!TUb>xS*vCcOg^sPLW!LZez61e(|`J)E50w$H(_E zOP-TzyUw@H<^lT6{YiWinx*&p`6S_<{Z#;9XigMhnI^8m_hXF#Et*BebD#~iFLggM z1ixMpO%!bR#c}~Zz+X0{fcaybYDrQd=T0h#aW&{4bUn(S^?-W9g%!NX>Mj`NqvJ4$ zP60Pk6=>Gh`QMMSKBSb>73D2Nk4SbO5vcIOhQ!&`&&#fSfd*Y-%V2Co`tTZ!L@#ZC zB3Q;;y*0X8pl^$>zVCCI!ERIel5YA5vvc<;yjcNm5H{NbbF)McJnh(oj3FE+x=!aA z4C+Ya>VVnnW)8E}jDK+a9$AP$v<@6h(_i06cg<Zd+OH`nK(jb9?3m^mc`4*jv8<Ks zp8S7Wxr&-_*`8pu_Zd5eT@c7e?!yx70CVTV9X>!h8@<D5&<Ax7weZ=_qavT|cnKHY z$>nhK8PgoQEu^4BWj6A%F}0oczb7yd6gy2_VQdUXrw1g@tjTJ~8YKm6a#TJL{&z!; z2QDT8co-V3b~Np9%_4rukSD*YO8mX+f7-(U2&hwVM;h<R#S;Rh^|bRBM;f<vMDivg zL$l`c*&m0r*$zSet$h0wB1l;q-6VNZpH?ELbCU4RP6G%S`3Y`=dP+?@@)wBSGV3Aa zk3JKQL)<3QVB+}gh_r<FU{%1rL}O9H*Np|@J*ayvu?G$TR=bdlZBV|0GB%inLx!-r zYENy853@7i@nPhWRK%a3m#^lrOtX=wia7y&k@u1X1L-*bWvBiK9Lc#HaMUcpI}Jya zon^ASo0D}=`*Y0`JfEE2UqcJmKV0ld!G#GX-u@dEd-12!y=(C!cZ9`|sBhhZFvulM zH~Md~gwbI!(be(ml<C!*aKjbc-f<gIrp)RI4z{|0k(X`nQTBnsH5J~_2Q)T{a`36~ zT3uISuR811kqiI=OzINNf6IT4RTI@Pw8k+Bu@bbwuDu)rS?IRw9$g0}+rfRNoq1(R zEH4o|bL~Hl*b(*-xDGtVeGrz~vGaZC#m{3S#|b-2gVpu3kCA?~Z^yHr*X$zLI4kGb zpYo!&TBNV;BBr$!DpqUW(6t^@bPHUA=p0r3_#3Y`)7V~r2lH_}nEI&n0L?|qOV9vn zJu@+qweTZS3~-DDY+#eVb{C4z?&_Q%LW&C?w8L5Z7i{BKBnGROfs0Oj7Xr6v8)bkb zDCVl8iGV}{m?>Jj$E5>WpMk;L9~guY02zF+Ws5Da4$kgwRR|po5N>cyF)4IEX8LiJ z;;-3DD$xsRpbO{D17k^(#J7r5SY*}kp2pZrNl3$QZLQ?xlIB3nNGNakFW%0|Di82S zoKXFRnvRk}JhdfUy5^o7UWEogVta`=4d|=UDy~_OB9Op0NbZ&f{KAW{`USI$T8iAB z>@%Rl^rTZdI6x-HFOQAIAlk2J(g10MScB_HxBPuEOZUDB`}kv5VSr1pA4&*T;I~fa zZwwYvzttrkVA*2XBWvT3t#fjEpQXeH@Trdh1ygX%Wm$q`q?VfVW&G3ajR5RhmC6Rw zZ@k{n;sF#&J_%Y}@4IJlVXDx$1nR3-aYFXyNnwQ>E<vd<!b{L_w9Ms--uH!9IhotA zRBQ@BtCWI=R<U&ZGyWvfwUEb90rLdfG62v}$7m>UAFJt(yIQ#_Jwx?Omb{<1jxwFA zXwX<q%6_yqxX1w(qy&UDtmorxcT?xRTpoj2Y^bhyT0a=rXr;QWgJ|130!71ZZKKgA z3F{dk0n@B9*p%YotQ#PT3&(|7ft$=v0oXw!UW$I3^yTkH9be&?K@eM_oCcx{P;{6< zns`dPB1%r=OyM4A9&J%M5?PFP3zM!UW(|bVHYgdZ5RA7XE=0hkCJ3ty=^8(5YQLwc zEzMcdL47m`%;V`^&<qheEl>nEj2LJjJ5Hc0AWCdARPOvzjYPikKWOa5d28yZC1{Wu zkq0*LdRZ!zrhuK4Uodfo{LCo90pDp-wjc;oCnYL>iGr6TK)g{#LHk4t=J|p=yIuNY z=ldQZ#J3lbhp3!O6ZpnAOpaOyS?ZS|17kU<DHyvgxrPnz_?d9^WCxR<12g!br_jbd zAR&zKBF+URFLaVkO_~Ns#HeXiz>`{TAB35u|9Rkah#~{C%-ipscK-XNq+KSKuR=~p zdC~xld=3LAn%?!&O9%r~r|z^(Q~7ht)WlGaTmG0)Ve5yZ{K`KhsU+cyk&ovx<Uif| z=7?K2t)-be!4LPdNJP@~EhmCxL}?k5%YAYXj8lY&9IP>g17W6*RIi2hc2)LT2~D&S zM2F5E(C=cpN<(gFkjiE}h2DKItQX)vj2(^rCyR7PC|yoGy?NLa0fAcAX@11_26(J2 z?!vi)J4z&vJ)h_Oz_;rto;i?4Gd4PN1e_pNmwhhLKd8G<tb)%s^Bxt_!Um@i3B6H; zG|oV2fx^iP8e?+->^G+oMr%ZyZeyD5_5V9I2+U8)oLtbAKbs(baUjF$!&AaTN<uws z(qo))qXSSkzc*}R1;?mYGHYgkays5l%h>YVWC*r(q;ec`xq+AnAgg)W$$5kYAZSMi z5Rz~c1iLT{UGt_WRQNnC;0q`*yIoK^0$o1d>f5BxW=Gh`0f$?JLsimO{Y`32<-8yN z5s{h`PGufd2uAarQ(ZM4TlaqR%6*?3TH?Kw=G@rkMF3Aq_QjyYk1QN$tGd7oz#1W- zqabS;fVb&Qt6+w8plN9V=~YW$peI(2p#|njqW^v<Eik%&qhFOv7MOB0t~r!Z1+C&c z(9N?6y52^2{%)u~r__H6u@irTR^D>*GpnJ+fqdBp0_=*3#>l{6nbRj8pEG{SBq$g3 ztW_jW1`M2rl~b|tm`zI|^+tpDZxh3K$MD|)3Q`A_Kc&DsG(d+c?8_oLG4#BRqo!o= zUd%Ogv>oc(N&5=+T@fOOKv)%z`3`c0@lcp1O!*qpnUUNKqk}m%lv$~h9~v6ViNV%P zHxmp^v7#(P${RuR&v{RXyV`$6bFKj^EAw@)syG1(h*5l{Isc@4t0~<sz7nm8khdYK zQnVn7%U^N!{<3#D3=mUUR62S+^N-d?@SuNXJiXT*=#lGae9Jx2q7%4#QGX0HF(HlQ zGPwgJe?>y`!`wMV^grJo1Z!n~+8H=Rd5&FH;-g0G5u*AhwBnSeoBsCo9*pU3)y)|q zpOjN|j_ZFB^RR8<g@jqxK;dJEonK{E>w=}G{jW%X@wX-_fs^DBV&=0MkAAPw%IP^7 z5@R-^+7?9=DmpkARt=0f9xe<ODFpH0CFO6y7PKG`Dm@B-wj{tdKH~G9y;R}*{5mZ* z<eV?$uM_@E)#pn;C2X47YIzTs68c8uaLtl&fIl~?xB;B#)4t058;$A~5Dh1M`L{X5 zijrLh2;dC4Z*L&o66|d`uNRwzS5E41t#_~lQQY4$YT(fU_#bFGDTj(zSiS8LJ2%Jt zQ*IDWO|^ma1;l`(i_j&%H1+j9n+7}FuU*1u-+s#J9cV*+z&hr=cr*hH;jKbe`?%3C zI1&k{kNlbbtKmWHnk#@bWe5MiM{Whn&K*nym~&5}gT07zN|WcRji5Ys0hlvhSwjB6 zc5S8$Ow*p6k~_Wq%Nl(Y{SB*AzQWvwFF2BvWdZO7m-kwfVy~b3JyK2G%x;sr9Imxd z9Qk{`@866(u(;^r(*KQM0hpl?!vy1vIbqmH5}|3(BU_Ce6e=CP4+-IhEZUi$fX5Xt z0TWjdfA(z%i~C+@EV0)&UdUPb{L`&tr`Q3ysZ2)hz3u$}j)aythBL6oJk0q@|03W2 z`ZpKz)#3*a(4_PI0e>a|MODsrbK7%k_;Euz^+g@akkT%7^414WeB}}?$l1AlO;D01 z<+RK>q6mK(MnLrab}Ugktuzn~1aZ#`Zh)M#utLIQNZm4sgO|Q_4k6CkVwKHtri-G) z68aB2ij!tmWJs#@Gnj29gP%>SpTt^Dqf$(TMx>(YUfP-?Ai`0$i<!CCp!Q_te8Re? zraA|_b|HLw``1a986Qw+RY}fLQ30!gHE(O1jz}sn$A$Q<o~=GOp!L(BwGA$#CHGvt zDY=qh>>*&Z?Ok;2Wp-Icxo3Ckoc&Q?`}pPS)T?!Khd*=9BLJ3<)V7yIt1|?F-gRkg zslhAms0G#(abhTZE55Utl_0@jQSE~&9#K<J8YMzjq#|btQcznNs9mTD4y+#`H}r(d z=1j%1QNUtxc5Wcx5EK3-%2$EcN<0piR%91VSrNf0vw1E*Gd4Db(tfWAyFDI(LW2UK zK$pSk*|+VYA_r?3l7u2#jG2oqeymh?8C?G1LPkrp^eacUOfOb8q2)^xS8#tXbss(! zwX_C>`L{F~|GTTJkTDbmO*(>O$8T-pCb9y=4wxK13g6v`o3FvBLLJ#n`?1N$rB3f2 zz>kK(Zl@pRN(1Id%y>NJ$g6^cII@&oP~C{IKXYTKGI9P%Bw?VXy(W}5PK=;7{ANka z1ifQVAT}ALIZ56x;btms0uDI{^);;d?Kr8f+dNEo_Sn<W2{|iL@he9)Mb-vXH%1lY zQ<FDdgQDm$MtcrIIv8qn*z#htg3OKRU{S-jw^buyDcnWr-m1Tx8R1ly5J@KcV<XqT zo_hBEe^-nY)X>iO!~#{}JZJP6*|14)Z!|ZPpf|K5i&3N=gJf}7BzPpo$VqP7PrQ=y z_|krP&$I?IWmG2<igxxI$a|~KMz;s^T2QQ{*sB||WM?q`F=7vBV-A@Ce}~VE$^2C1 zt330})n`hnwfG&|l5>)Q+}DmnTK)L;5qkI1Y8S@JsP;%Y<3$%w8I98O{q`=6HmRfO zgOHei07>NPA0N8JRAe;tPu?zClC|NR`|AK?*cue!+)^CsXh9#ror9&0NUUh4=Ze)c zz7D-7E{;Dk$04nhh=irthNWpHIgvop`YAmFo=93fGv)Z9wlkBdER^;GjtjLl^vFtr z1uL<31h6r`lt!~8<rLE1c%yKI16A^y07a<8J~&J8q4dvSv{bYGpY7c-p5xB#A%2y5 zH?UxtIg1=L+XWgUin(HOVnsimkH@3(W*RYgD2XwCV513-^oiod+SQC<HAR@N02eDg zj|EO`xW?EiwkMI({mm7!)w!2U+4wKh^KoRGbnGZE7|l`tc=3&FVhkH;ot7-IFb*!l zD0p#J86&g<5mX{RdeAY%q=T`grc0-F^;<o0gFl2|frRURX8t#j&7%Hs4O%4A=_q>W z2v2PYxc%D9`KVziFae>`zLXPn3NJyj1w*$eMkbYn*fv2_JF)z?MxkEP)ri3bREL_k zMs%n`3bb72wOi?n%QK`{<mMTs$fC(P0MT092qsP&wn^kBGlg|gvTFnJI)@n!uDzZw zc9LHA?)vd5oDOA%Ro;&`L1r}8dXsCa2Q!$``n3z_ZxxXXFr#idA`9D2nGH2ui0Umg zQtTkZU=Sr*7}$UsKeog3A?$qo=sD+-{lpa8tG}0)Q$+pI#F&xpK{ZOpcB5{Wpic!X zc$WW_UBgUey=?`F;{ye$Z}5NLwfMOES^(IFl?6IRC|*CUg`PrL5yU7ay24q0dk}@c z2v6iN)<8P2S-voa*9DE7sDEad6e%J<tO9IcxoX8XhdfFt-w+2Zw%uv%N>^%d`iXea zN}6c8Q${14k91aypz_~_B<pKav{{smiYmbhPMt{)7w?CjqB81hZB03H4k9(%Y+3vi z8+JR$mK|ID12BZNCHr#W&yC5;Qp$o5=H*Mv1hcdmS;VJEQ3CQ3mi{EU3hJ>&L^c~F zZR>)_;K5|+qAA@SNFj;9bdwRqWA5#xQ5a6-)V<nRv<tJp1UqyG3elU8w52YY5)`DH zAT8&K{b&KsuLI?GwMR2;oAB@O^Skf^O2%Mfs$XHRijbNlT|yrL6^?@s7QhZP;%$AD zn9!0U2GaW@46Wj$BBN@qg-k1arF`3)3v-2ZdMX&;pI7hp6`Fln2TFQ`obEy$Dn0R2 zOFu=(-HwTNR+tV>LaTt9f7kWf&-KOiR==27^k>y}KW~`2Nx<?;<W@_q%Z({xbmLbd z38a(y^BBDdvAUb0%UUu}Vy5#k-|9ZwblAer5i+vwdvDalzghDqztC7^nQc#SkC^!u z1RYPC7->wxYzQP34^r*XHcE9Qdt<r(+ff&t@&9nqV6p5vvTCt0pX*9+FpZU6fhU@& zVT3Byy)zJKgy>7R^=y}{H45)D==t-w@#)}Ce)i<BR=MN*RW6KDt>EMEZ`ab%BdgrW z??$et^uk=*WGf`}no&8H^4NHR;VLIL5o!KBGA?f!Q}e!m2xs4S&`@BJSlbkZcc#+y zXri1c@LdieWfQ*Q{Ss_KLzfMfGQWAWFXOW_EW#z%^|(>Rac>1usLNNfyt1edZ{2R3 z*TTQNbIo$<E&K3!@BT2g+Nw{It<6t(ecEG^agV;?*EHMP`=D87!@^;Uh*mi-e@pg_ zKy4Y7(UaE$l(lNP%Q0yh4=F_MBvIQ(0|_YE$~f-`n0C6{_V0)#h-)LDN-pe=|9e?+ z_m6v{cI0L{ARzQmCcu+|{>u?ZX?>x9YoL4`kGXIzfTo_0hEUhAjd0wghC1`CtDxLm z?JPEW;tlLZY>4PGc@;8;iCxYLmsOIPlpEP(j5agQPC)1GbV_|=-d@Ivd+5zq`z%X0 zhF;@(%6SqwajA^RxK&f<uTx4$c`=pem+PdZvQ~%tM$JmORr>-uoq)BxpW;ldTG_Cx zq|I`dF#Rd+Jcd(g+@H}wCvy%m51qaFhw`~v_NK@i86UlxHD$7E?oi3CU3Ia;2#YyR z*BT2qz5IXnt#}MJp304ylE5@Bw=VtU>Zeo+gmV0uwxJlIudJ;pkbw2*vJo)0V0hiT z=eZ32Ty{?TQ_-nOq?iju*I8_V7X}pA@`pDHf)_6ZbjqTBd~&wJ<5O1A`Us<nY-3c= z1*Lsqvjv72W1#aAr;@C&w9SJor5ax(m{f3o_9TvMHrt`gS3@sI{sb1YS>?LQ5o2m9 z%_D3(aca7#esT+uxr!%Iw<%4Kf;~G|1F$E*a7BHrarzU9q+8^x!HCldw}ii@Df)u% zq=wNi%uKhL6r#Z)4XV331c>w-I9Ft7@pD^sD)(?D$Por=4)c%<Pi5HU0aj9TJ`a)d zejueh9!4CYvYD`9E<Tc4=}XzD{GM`7rckUeQol?WY<C!q@9_~-@qU-x8M={Lmj>Ro z-)WS7p@-}!BY-&Ycbz)`UwCoHQo%lSj$VlLmd7^;Wnq?Y4=YO=r?;+CK<asrmucv0 za>_xHazat$(FDMi2$@JEValhTEJd#YmT=!N=wD|9g;U{MrlrEFscITZWgdDW&-}_R zDy8gOc9rE7c0UQq%85@9u6qh^37<hsmC5xy=2IYqw`05xRUsl#xCs9AiG=2go4W;A z@EflUBxtw8e}()G<G;3~jX)(MDjh1_3e<USyOk%eOQ?s&j{EV8^4vDM8?H|(tapal zzqV2$jvC&lTEZ{YSQd4PP)voVV^Wn1f~xR|Z$&S$j!pV+-!NVvO=WR|;i^+*M=_y` zPN79Vttb0U`o9=JF7q%DN1mE6u_)<DCqa2-db$f6>8=#TNDM(9J3NORk*4#HT@ZY* z9x5M;jx#q0zNOn}P91i4>7!?=mQB7go@kd!&B+qtcv|oL#%`-i{_{8%zN*Q<ihJ!C z;~AL!LrZ6>l)rqn#?8HfE|?Mfb~}A2=dZv$4xgQ>0d|DWIS-J4&(6A2UfIVnbwNd9 z+!5WoV5M>kOlhBfEzbh?<>?nh)lFZI7H4YDOrr6_!Vtw~H8N0E$u;krI7MR_KR&^> z(4;XSvvP1W5s7f*4Vgsj9(s#3JzoIT`(g@_Oslf#q&}Is8s<Qv9Y&^-#Vw+A2g@d% zZ5?C!(NZqRTSE)ANk>;q#<ros@P9)5LY<fFcQ9&!A$p#PVHe|1B}uFNK`!NY?aBlA z9}85B-VciUD(#E2DFj!6Hu=3z&|b^E^TNB$qLTd}xG`NOY;d+_^zMy<{Kv?1OY$++ z01LYI-AH~pS`aAELN>t-2RB${Sa`7Qzphw$`LZfOlK*)h*+r=yDLx^g5`ikqKaU8q zglT{-{b8mbi^r}o;M~KrL5x_%?-Z3|Wx3HO$a4deqCw&YdIqAes<#Wj<DeEB1~7IA z?=-#AyYQMtt5`*y^Pfmox1g^3^V#f{=siH4Re+jID)Ca3=V%TKo0>4z0swWX{m!(C z3Fty1;{pVq*(Bl<4YN2J;W@x*)9R{EpL%4t_N89`OUbudz?ST8v*uy+BLzoOTJ(!B z;mH$&2n_;w^{?ph?h8bcG*@X9;XWCKw!atpTHzZV%wPwNA2{~v_)h*%|AZAnBiu!+ z8*lO465T}a!m@W~^?-60fx!Vv?v*s)Ak0uWLuJbyx_DlYTUH0)wPJ`xG3i3TbL5N~ zWpUQ=n2KsmJCfp;wi!1k4yeA1G#(=zd9V38+0sF>H2V&qF)rM{wKAW#EpqqJcUY=$ z2(`sWtLwzE8)p=vBF~UVLRg)<JecSo)^%<}CGWq9nJc#DY6oq`#rid7wJD$ZTV@CZ zds-q+z(Akc)&17Kpo(Xo#o`}n+-lpI4fe4oE^ICpo4St0mD-`O@<{CY>DHh(ZO#{R z8CQ)u0=iT3_sH9+G;91loDRmup-t)7<p9DKm8@F05%=mav_<8S=L1D_Mxe8dFQaU( z9q8OBrK9}0RA#@ifw<-yZ<OSt&FQ+R*ZG13A{cXFnfhQdGtF>-!Ttq+0=5w!#*`<^ z5L+}%-z22}y%D~HQ9Hv24gaPaTGVzY0bc3&nI*NwQ={%#i2-zBAy16-Kf2`Xb+a%7 zj&zrBU@hI7DFwQulVaLV7d&mGWpayyI77%k-<P%(Z=u=3FYxH?x>b^rNd1GIfx~e; zV#d?II0Hj;g8-rjebFZIMVn!wFRuGc^Ia*ti<Z+>#s5+%DaOv1mcmK$Zm?O^HEf}F zya!MBQ3{lmr##6KWs@#RNb$gu++-TC3zjwU5q5Hn4jxw|c}iRtEcEa{JE?cUy;DV_ zP8a?qM1ROwkJ}fl`(}tcY1DW7PTf6YY}xsPJ6!8#=5j-Cqtml+OQs*FO#iiwAV)Gd z<NkA@=;s7@$?D!{<7EVYXy^|~;@!0<ORZk@yHbcT?4~x~Xg53Kxm$oq3x>8tXZu^x ztIv-JTZ_q`;;&zssb(^|E{5#}BdzF0LW#DT7OM(3C2$nf5N$#?DT0e{!I{sQn2-u^ z#FFp|JqV^@abTb!UL4X+>L4WvU%8Cn>&=G3A$zMz{IM^$)Q70eFuT__gt#V12Bmf{ zP}k#0cCIBw2tvQ2NR%7_$HS@^6vy5qhr3z!c&MNaBAGy!cjl>PV!(EO4UQSD`$}p7 zjCEz0wko}fUChr5L0t=?_uAHr=SsI?Y!$HyBixoGWYaSzcr{!FW@G+rjkj_xSMf55 zg=#TaaXyxI&+V!Z{g}3dV`n=l3k4U+w_-rYRBeLkWM0VzF<66mcOzT=(US)aP++Vi zV3Plx5@P8XG#4$No}mD0zSl;eBzGCHoSK+0-c)%d!B>0GUm=A@&dv00Gx=t4vB8oV zm@S{+%i;9^|7cYS{RUsj-%%3jPWX;fF69mPr_{v1GZSykuj3U(R`O<L`Xk?TSm=`b ztfd6j4%q>Qo$=1n(!abK+TVrwlf=+=-*mE|Dumex%3dfAUWF6TRO%+@or1519Ilm2 zuAATjt-ywjLNN42Gk7TP4Go3qWR8?Ux0t25w>k3%4H#N74&(5qZfpVaFBh6Vxf~MW zH80Gt=I95@#8whlr0wEt)&?tB*3CKK1JayerdQcT(#7^Is<;P2AM@J7oT6yXp`Agr z1>0J?zbXy74X{r3|J@w$q`J!M4s`@Zz0o<W*sZ+ix(?UJQU`rd)TE~)Ix2}FlUMy3 z0%vu|p>hOYT?u?)#!+Oo0!02ZX2{qMMS$@QG%&&Vqp5aF<a;V+5|~2vQ!3C#V^fYF z?f;nsCNZdYH?)^X%)r$T{WCq>`(7Dsc+nfEkM9IGU{}Uq|4x-7e}Td?qlpw~tzrXK zh>gJi&iXx00G?c#UM^w$aVcWEm9Zwba5xMd9?MVhVC0DUN9lcB7dcak{VFNEcWq^O zf`5V+yu{p8lw6~pcaZVdaF1|wea1dQj5dg<t4Nf}y<kqqZy-otmLOZBk|Q21rRPp^ zPdeIQ7ZpNZrbxTKZ%vT{glalEy5SsjrbQj7@scxrMkBs7?|4aj2NyDf*|*Ew7ASEC z-jL;7T+v?39H=sxNR#3L!VZEq6_$~-gZ121b_<j#4`UXFOW7;=>IzkNqe`fGVt({% z>hAXhH_^Y6kDqt@=SAEvzwfiXtCO^sUO(R<u-nJc!P1}4k1u<M8($CK_ph&m!CK^B zg#4c`dvANK`g%LOKHslSRp4j$@1>=sqkfMctqeCiy!<}zpPvp{0l)cr`Rqh~{`=SP z_WASiN3{NI_}8by*A2(u_v_vF_TodSUl-tOxA^FShJdxG)zMfJ7YoJ>7V31c5*8zY zFAgDSkIIg_ZZ17iaTG*RyTVtJ{g`}X#Xr94BfJ=7jg@(7O)d(785L3d(#pLh;+81s zTXq)+i~SXQsu`etyH*$P=r}Yn7d5AyFg;niFC4@a#=k8LPR0DQYjJC+Jfk{3l`$ua zci_7Tr;_z2-Q<tECh7(Zkac(yPZ>f9VF7NuZ29X#Tggxaw<P~JXh2^5`V6CJTX^qi zQA=k^D%qal^e}^GT`;D(;32oxEQAvB!3ZG4E^v?8d;-7Jm=#qiG-kHtrsLeW-r9MU z$&i^fcLetIv~()c^Nt+y#s58?AU8E&<BJ><`eoK-GotEH9*UjDF3pn5KMoY7XhskI z_=(z}A5H>fi8z2tw~lq$xJGEXz8_^ovujB|>UPhH2EtV>h{1~26amz@9!0#t1a=A~ zNWlRdpoo=bet|o%TI447h}#pUl`y2>zleQpMF+DoQtsqwrqOIJjqy)$lPpcb(hr^| z@X54dI;JCdDB79nYBnli9O@l))!-Z1u6>lOiP8xWwo$?X?bmMF?>idfG%SKWuQ<+2 zjWu|vn2Na*xSu>yZ@_~pK#@ga2-u7-`|)f^8a~VoQaE!}K}T5Q0SAAnz=BLj%dIeq zs%k@_K_?W|qQeK*1Yum=G5k>Ha@KZh!O{;f=<FW!Ah8&U(LABW`B&l{I21=vKc7ey zvds?yrtfiH04LzgBb=(q(g}wxYt?JCq&erDkicB$j9ShOW=?{h@nzv<NrooENT0iy z{(0KiXtFK&YaNJ-C*PS9inYQEH_7zv1;t%w`Fwrspa3Lk+%?;I0H~6{04}ZC4<JbW z40LZ=Q;ieRZR7^x2>ns(k&S2%Q+L>vKdEw*cpTj!N_xp{I5*5PDXx<ba+F5`sCSB( zVSS4Wmksn3a`p>n^Hd1<yP144!<3Mp)Z=VXI2Tt^fi`Wq{vF%&Z%6v=z(HubH{fwA zxgl`@YM*B8t2(>ZlOo2M6d{`#Jh4+7>sb~94-y(|wYcfQ#M2Fx{jk)Xq!<n(O&WhD z{(5n}jV7ny9W#T{gOb%00=aRkn7d3K=oEM~#`W*F&jq?QST3SY07BPtl3>bUB;+!u zNUSRq0+y=fe2C>+fdZQC$vI4wf+3zn8#a^7Y009X2EL8v`PC3rhq0NBfIQMq6EU&$ ze}FT>p_sBgZsJ&W_K6wtf8kt=YBv;CiD(Vg;I<y=^viDnb%mG3<Z~})<;`>tn3mH7 zW}U-DRN@$m#bAp;cq3^Oa(Mesb$p0beVl<NZoc(=javlF{W{dsFX+3c8@;bzGyU)8 zTYMFdejZ5QHxu`*&bJ>(-qh?42A|@l5GR-V$ksd2TwXuqX@5z?&xV!I(0!eSVq!gS zmD}2o&0q?T<gbDPT8}Kf>E!y!`CCpNre=RJJ4y7dr;loagg?iOGoDql^Wce4zD^EJ z5mpqAxUIx0a#C{^`2h=X>C{~dq`(}5UnTPU7%_Qvkk9zi_apg!fQ<0i8yIV+{hrR> zAugtT`c$v5f}9AHUcrKz!UI`!ez;w+LEXfL8Ti5-1fxqQoPc@OA$Gdpa|<Y!KUPy5 z(<vi+)e;lX0%3|U?%elOlG_}Lw7+ipg!d-<tBx^d^m~)#2L21SiC>DY3C-^tU_xiw z8&)hrK^@7};v6Hyk3;H3HtIy<)|2LDeK*K<Vg18iwy47v2Ip?PROy%GqviQ@y?sog zW_Y-`m}1Z9#k9XOC0tq*tggTv`lzoW!k|F0Y&V!;-Yb?|Q$*<O3ly!uFCYsuA7ePe z1T)-KikvI$;4H~ZD}faT&K861K@7WRy<ba84RguOfo9x)$-x`75xFdA^h4lyt7t;9 zW3XJ%0pszP>WK#!HQ6_xq^J3Fh(Ul=v)MAGeRmi(Oh2{!RCofvRgJPx!Y(>?rSy6T zB9H0e4`73@&hj_!HE5o|->MT{W&ZI}hOEoc{|{aY>;IdV!py|P%mv5K59jRSWNK&& zCm9i~(b+)Ij`$h)fQ+`$pG_PNJw8{gs)lFl;rx~`mjC02m&F2d%voo*ydtf^2LZ#1 zJ8kN$d{bfE;_59W@R7B$Bk*wg)ns=S`mYm?(~EKlb49E$l#UrTc6R_7AAXOB3v#!v z3BL5gWm8B#Xs&+|8=M4+v-JpzQp_2`hweKp%$A&0{aFPLI9R^ma*EAaM+l29@7|F{ zsd$oo{Vz=WS8GEO)GS_+gv2G&^=}m0F{>#uync=*tFnO4<1sMe<g6-OpLCGwq>Z)Q zhxC^dn7k{|!KSTY@U?7mCg7J1J4x7E&m<#t1a%>i^t#2TE^&xYbk^j^6z_bVShc#` z)gieO+mqzqH49bL?=CxvI<oghl4<AIdblE@jCyIls%*LAssxKb1{V4q&~plgj+@|( z_-q5K>9s@ENb3XqMCZsS&q$*GFxLOP!MI#gv*o(FkxM<J=I7XAj^#Pi$0~m1xy6pK zErBpOLpZ<*C&Ud;y@NJ3#V)*H8QO4JoUGzriTaY6-#;sJjR@ZMh)b=Vgf`9Lv8f-O zl6$cS>K(m;7kO^Ha!TL{;Bb3_)aZ!kK4!IyEAirgOy4xILTyHoAZFPXW`Xbf6BG~G z{F2R`<r<)p<rFLMmdAfgOH0-|g-@avs__k1@O^apyWx|QT-v4`;7u|XX5{0VRf)ZB zU;B(t_QS!qe7@wH;O-jmOq_lE2mlU78#>W(B^31#87zc)yNxLU*JDsU3Mt`QV{>DL zsgA_wyA=OU#h}KuHjPR>%9qbdcG@G5vn`bPuz3@lAcQ}r{ZvThnG|-I$e&Zl9eorn z*CZYGyBPF2M>=0pn<yyV?|rUbylC)j+>Hw8xqTkhj{fo+)&F^(UG=-`-M!uI<@<FW zRVeNz2vrm`dh?6_`{Df_k<7h)9$>(|bTtwUj>_t+b)E$s3RirB7sv+9cc_We#yKQ9 zGKcVZb+mmJz{GEp)ks4IwL4Y7{yOZ0*o>F|munz6(SHzt_nVJd2-_Ph2TJIJX^jfc zeaERu)7Swz6n66Y`-RV5B=uM_mQSyk*vm$uu_2D)c~RRD=l*O|Gi_P`XZsXo6u-X8 zuI_BHz!9e#eT_j!J|I|g#AA}!TJJ60;yy1EE??KtI37^`H!ceX9dQ##jSR7pQLV0$ z@hVr5A?yNNFD!|w1`k7bWnqX^HghuIFS1T32Zc0>gzEh23*jsNk9?BpuQ`6;citmi zYv1j6RVr)0WI#i*@y$6z^vq?x1eg`l+?yFcD+DE_5*5ND+~kT~v@J8QeGKS8P4xz& z4l9obOcQqdNE1mMXtQO=<F0N|I#<Kd8Bo@z8vr4N6(XJ?5-KI=Es`&Q>Fk|y)EEXr zW#=5EIHillS>1&+rMC!(iTZvmljQ*}6iW<vhg7gYFtN&_gjF?MRz^MJk&cGW>257^ zh`#?3o{N`UqhjH2G7k&f4v!)7DsiNsqxu-8GIh6;#5BKC6O6?*F5yFm@e{dBe3i#X zEZ$aRwjwhPFRaEn+@v}{p4CIiflatYT9$}hq6d2NKL+tQXOLR3NW2DdRa(L}<BZJ) z&xgV^TkfIzVgk_RwZtgrmL!NedVUAgOqTU$MyXt*?w}1fO0cds!m5id)+P)HzI6d1 zylpI|2sa~8rs?Y$>gp&b=#lwmB`S%SD|v&)P-c}JX;^7<A?@;T+1G4vpoQlol~oKU z;;mP}2gLBT!>9sqjRZ;JCKse{hNneTOHX~qhN#asp|M=UxJ;>8Zwcm&By`>x92}dy z`V7ppuW1ZImrQ!S=%ZNCcX)MQBt7;*R*GK+;YR&o4!Pl@3Je+4O`&X8lBBTXYT~Xo zw+O^_PhT~o4rO`-HbgYd`IOawf+MM2y+wFXbXlpnMs~En&EVxlTozVr6#|VbD=M() zhlX3~QQ(B_H}@y{8&lRnVDy?IlY0?a9|1)WMNix_z7v#5^|mW@HmLghfq1GZFXsO( zpM!!4C?Zz>=9b!@meDa=uD^P(sQ7-xuzvko#V^rp=Q<oos|a!3L0Tku!JV&a)~Dza z_h=O=f#Nk^*VEs6IcUUobwTfJ+^{w<$g+dcRbJ7!`%8&)AP19~YEWac{<1eqHp`U5 zMHtNw|21rZJY4Bdk4xV&rESqWO-N~wr_r9aUWjN@=-pzsr^>nAg-a_ii-wuU%jm|7 z-5qbR2IyntuhTqwnv=&mdiMnz;gDh<X2NWN>KuOVL~H5oa&YI>G7Wp!s-rsat-k!I zcOn>OAoA<i$~ipX=F(k4f)+b}d-c{w9^h+mCq$z4SMW$aH!MGfG?K5FB8Yc(S58Aj zU5l}~i_@J=Fi)^WFEZzk1w$rwXLShrJ7TwUg`0Pz<wW~PfaFWe$fG?d9yfi`g;rWA z_TpxqIqcb`jQ~(`IfkQPS!e!Gn_+f8h@@aSgkW7oJUCn$#w6`q?{RHjbE<M#^^Y9J z@K_wC1i$_^oC(UIANbEh8QQ0DDpA=@sqJY22ilQx#K_&8A88{JU+2-L+~r*V-C{g^ z>w``EQGf+*wFr4mhe!Y(?@t`hf5DWO%|Temp>)(8uHbk*D`*=F&3q-Z`oEQ-aB#_z zjW{geaY|?y-MR^ISLEhQD@$BrJIC4GSxk+MfgHN~R+}XJY*xm&vP&fCukQ%$jUM?1 zIH*s~_AwbND;e|K98-d}_ED8b*2e=?107c*a?IMj1@mR@n><!kkC&o&Wa;@5O?UGs z(fldx58h_+PO$ns6U-|eMnX~Nl_$bJL#VG+=Cr>m7vg_<k$b-ew6hsk=Kif=Vnnt& z9MU0&)pa@BE-112%r8tQ88qoq7>zH(_kKTr8ILlDSDrl}Nb0z{SYLals@y8!4bKnO z<<|5VrUp0|&TuXXF4-ob<yRz^K+?Cg!=;l!DG*m;TL3F~I8ovu#vB}DoAPq9GRn+T zL+u2ZxCg7Bnw}%K2%#c71PVwSmmQ6m#PLIDZk`fAWniAIxD7XJ@D~i|8rcH<)dXT* zz`bo}IiU&%ym!}jp#gln^b*7^tzM7NF9Kh>8cd9C^QQbT%G-M|sD^c<bn>7e0|jrH zTnNQjH1^uTcVe`<G$Ro?#je5q)EW@!`Z-~wiUZ??#6bAsI>J$XUTyh|9=7tF{gO3x zF&G^svA(szJAM|A9uR`UD&*)X-r;Ae(}6;2#g;O-@O@)0EAl}M8tEHiajfN!1AfiC z;#UwDYsk|iTSD>87zYsY-5jO(%Gg92$%K$6B8*j29E^E&ezp&_X67?O^C~S#pW*(3 z6^arpsr7L6c8H-W9VZCYuX)2UDV%5;q-o~BSdXq$-yU{kM#Z=Z=^c+n+b41i!GAIk zCzQnFr1&@GO#A(h&4>9zCNJ2a@mB*>lQe7WDJ*8MskG_zXzL!vBJLt2s@TJ!OCgT> z(Y(0PyiOg1H-SUqL7lF!(>zj0aIPVq${b<<jt9)DCMp;#NV^tg#4DP?*Z80HDoI~_ zp-<RHBp)Wpzc|Y;VXldX2RSz0>_$jNLNxsi+^<(plke^tVl9}pP^qT>bWe>9%p}BQ zYR2oaao44k4wN;bnJa(N=`jiQtPAdqgLezN<cDcX36TOh6F72&sfIr#9nXi_rl`(M z5Z6;u6-#$ot+4&m)*=qc<T-m4vpAVDm76WwXXr+k0fpD2?NM||#76L7&|iY-oEau3 z@vO47a-lBe->Pyq<g;h^4P7GZrrj47ZkbFSQbY7oTIqitFW70X$-VGV(OIo`if=;u z1kGTB${y*T52gqDo_9vVs}$K1hYR`T`>O-{(z5s78_?cqe5niWmh?RPJ|(w`N9Dz= zdntJlJL{sXNYtj3qLolY5a5!G#q%MB@|gIt8x|y|CSZoU9h~*f2-0n2#Flen_avbr z`wU*leJdIci&zRs;a87H_e0Rm4@f)BnxLJ%D6lFMN#^WAi(Cnms^V6P9`IRM#SEf? zKWJtBm~|+FHG*R?U1LPHM~Kjq-5mOXWT5jGM0-d0P%LncRG~4{XH)<U+;yVkYchJ? z=nHFAkn@#U6wFX+$X`%k8?`|wU;!XVVU#m(0jE;O-b57$al=&T#T^BTmgB<!hYH|r zlG4UBh@!(Bi8Sv^cZIsg<8e(88JY*)vNfqcrtT)5k-GjX&M;+nH6z!Ag=USMidKZ| zmG~Gjr~ZuV>xV<Q@%8wjtr}ulrcdFakF<WNAXiPw@vY;fYk^<3=hSE+{P*KX^F_38 zje=EWWSt#N&*sG0sEhr1hW7KeTR3NJiM(OQ$1A~1=%`k&n2FEsA6qf@z^d}=6bW=k zZR4!cN;q?NF+nJ<c9y>lJ_i_30mh^$4BCn*tjhO>f1)*ABd3dw=nW|k>dQ6Ofs)Dz zoGspnS{~YgMniV}dBq&-=rQF=#G?b3rT4zU_4F$#=9aeWYQJq}FeI=<Y=m*Pg$FUH zk}jLO951aZ+Kp@vbj+n@nQkW60><eOhgsFZsJ4V5+@;;3hA<>LKp^;lY97BGC5Nrg zs`e)km~_b3*KEyy+v_hK^b((U)$UfIhR_*jiSqj!t_?bhaM)PFb~u*ECOuQaY?7<R zqs4MnUYCxSa1J?rN#f7Yed%x&YRFC74E(3j2#+TuaqeL7WV=7(+O`v}hDwAWBdkF` zvouXtJx05mh|_d&WXufeU=vZge(5H)IX_@R=s-B|*Nu&Ea>}*}v~($W^U1pf17tZ> z_3lVkh5Pqa<brLfm(Hu1SL%Q82#8x0|4P3}i84W%?Qe~&=dle|BPL;0^5J4KHk)lY z#3!d+3*kajHALsha*5y5qhAbSj0kYR(s*0@1+bew(O&KTUnIF|cLDuTGPDoJh1Tp@ zfpO{(2kg8BWX-1H87fO>qZp%CV#}$clr2+$BzUTVDcWdz81+F36+DpM`oZ+uTGfJh zAu)>ybi77-oj{SlM#10emj7{&-HB4#SAN=}r`foxb^-D`;ZJZ(!7Am_+~6=Gw4lD= z7E3#z8TT$dG-^-HOkfV*l$x1t!c*~$@cC+98ib_pe!7{&#)pY+DS48dhW9*lS`qDM zG?XSBGPmQp|9b4uAq-q~^h$m(AS7`{#@9a~DU)<7N3$;zFuFc?WCl4V$UY#<E}7U9 zR|Z;|a=A0F{7of_$~5a?N(P-4pI}LQD*Bi}2CHH=9GMm(NSdeY%@qL(GQf=tvHnLg zFDsGr(*D|2aS{@(B2Y@JRL0v`=$so4sm@^%sj=Z;Nb910B{j9kl<8xM&#r{J_vVx& z#h%>+j8l^MTT%my8B&l!?<p(u$bwrEpvX&5p}pA|E=+uRIxsYD#m<|H2i1GRq(y$4 z7IG%4A}g!?;VON5Y#2<`vyliHk1@Y-wrZyWM=qu%)BkrdRA3lKPbB`MeLl^0%6xjx zujhT!ulMWwpwzF&@8j{iRp0OZwRcnC_4Fdmrc25E`}p~)SHQ>n=VG?C*6(%iqqTKc z;PawYK>+9Zzs+4C-_hRM<HqLqOTV;T&j2eu+Z5dH#_17{AnJ*DwwoZsk~_m4?C2d{ z8cV9ywDecPt_~uzlnD<!B+m%-7Ea{j;VslA&!6%U$UK-4TVyz%<owr<I06&?qG<;p zqDY%UBvC+rt=or#F(WxiuJH!kO6J>zZ~mqA7$!uQ%6^jJiZGmL=xd|muk;a$M#la6 zEMw}~U-#n~nsubW*Kd5$TL<NWXeyw-OVuEdM$RrS?$30wMx0@;w~ilVR1j+OvEc0< zz1wl#&^coc<24Y-$b_&5<jZwlfHOPh;w1<k-+n0-Wk*39{mW=Mr7PZv6ReiU7$)T6 z9&#RO)>F+F*{ujE!*V`;2C3~&W8@Mi9%O-49P5{<RFJQVed9BA9?u%tUQ^K>sw;<Q zI$vVxl(W%Ly_BukoR&S!9zYE~kl6xBD}i>MhL4S+GbcW;(S9XmT{KPE_fTyQ<MO*j z#NW`Laj|ThD+H___1bu)(3F457N(3vRHCFJJIMdhNtjH>{nY#xaUgbSA>%ArAI&Q; zkq0=bBo4z)JJSv(gcg(&G}8oJu@ExwG9>!+T;OvH$vpk$M3j-a^YZ<Nr@nrK^2`=N z(`ZDk(Go|v=tHoRY!{|`al5Bd?`sf=@dB0E=rZ^%z2V+*9@aDC251?Zmg{|jGvVbL z9a&mkh%z{Zz0V%VSR2uKQMvyscupfn1NgL5ob1_Pcz|Q}KQ5W9*P>Bvf}9nSh8qlN zIGPF@c;|_l;Jw`|QxmFpk@JX&o<M3H@VrzEW&B|-B?cb*Oo)Y`X(*mp!fx(q62HEu zPl=`U0&R}4nF_1t>sR*#r%+qTF|_c71mZl8AzC*E&u%t_O){T=27?O=&ZJr6XF9}& zY(C3S$lC*Z{L#cBBuK8hkr-^}5b?iLO(-QPE%6~X_@0@adly;b7H4^kg+8=MOV36{ zVYI|tfY1mRIq}-YR2a%$8*h38oSk%tUQm6v)Pn>;6#lMH>5B@L-YYKH{!PgJOsyF+ zM96M(M#><LLRVb>f!LfrHfv=8C_$XvzXz<`-10xlm`&-dm!}X`y#?V1l#foKwf~v0 zrmeD+I?38q)F~6=o`M^m%wq3cGISaQIo(Aw-mJ5t;E)32v;+{Fh3je55lyw(-TZ3x z@=%N&N&G=~PIMM!4I{<D=Hz=SF#w;G9lADb(zA4Gnu16W1b8BC9zs=m+L@WjNE(!} z*G7_&?HfwDGZDq;K4I#m)WZb(P&~zj=*D5xOuU|psbh|s6+0}U8#7EOJ)E*GVSl?C zo~L<L?!!gDLhli#hp9bN1aOrm_2-^-lYPaot39El#q>_EI%Ei_>&h%^_s;M;vxA0o z*D7_fORBm+ET>6g?%+%rHg)(Lmvvh&(zBZS2c1Z+5#it1HCp<89Vgu4M17Y7783Tx z-`fSz9u9UXFTIY!2%qxecaSBvP=vPrdy>hT4YmO8QC)PZyU)B(2^w!dHa1?jdb%(O zUzPlw1(!G|KF>n(?qG1W=fy0j0oPE*Na(i6k%T*Y0H}Kh4-}ekZ2GYN18n%?(gfdm z8KDrji+GmZ!?@d{EK_9?ZYFwpx+rPi#4a4?cZV(Fq+5gxB~9{%O5q2S@m9pCa+-DA z&w~_<iodksO1xNvA2tx9bf?fITGtiw9&?8d3z9qYHZ}<fQ!%?`W5V`$rq*Suqt_ib z{r5*V!dF(RD4wZm)MS@04+me(rb@^qL3Slc8Z0lqOYN-ZWsNxOuG)SUvoH!hN%<r) zm@^ayT~&Fdxeq}>l<ZNtC3m9OW;Uof%dsH!1LYbsMn*2f;kT89#>&9ZUQuEX*z>3o zli4luN@a0#O(E=e;K#2Sa-dKRUtEqxLt@eqbU-lRCQu2LB-|EoPyEBbY(A4w)AOE+ zq7z)S@F*)#QTdkvUqJY!dQvv?^z=(J^-yTjg(RY!s{b@&43};zXxA}3nfk&3LEMbb zZ)|3xpc_^gr*j7$q|UIgpBDRQLf9zLH(VWJ`Qa%=J4_j~0^M6k$}prt#afg!ov5%4 zX?X^>IK3J-({UHA^O0&u0%R{g>hS|zu~?;$M)Xq2HCEM*J<T%>`&^Fnvw`fyb?#Wc zLTrkcdhpE4RGQ7l)B#2Bu02!v%9q|ePHKy5t|($Az#d~0e=!9~@X(CLA(TyZfR(hC zQks=oy+9IWTGUIedkm%(a0<eaQH}zQpCC@oub>ETD%Dei4+mqCCMFFKR?e;%=`<9x z!A`VOYh-mgu7yHX922#o#RBPHTkcrWsoMCxE+%XW1&j5WrA)?E&jBevl8q@@X%Isa z<~zDJ-#K459wi(-mv`hej6MQ3k)`i6g|4nhhS`1Hlb2nlbNm^ob;Yd#6BOB$lg=6e zPiHCBbH0F2qpjOSnlbVj32RrFQ#Q^$-g?FcMOo@-6ew<eszQY05rcvg<iy`&)`U5q znQ<L#Qh7HBiV=ut7yVt-14QKc3r7%CQkKGq%`0P&o>iklIzXuk&y%Py5X~BHI^Z&9 zFxg-<_tTtm%m{FM^B>Zp-!@q26p29qcT~rqID>&mY}T+<MR<x(LS4cU?Bqe9Mnf!| zXM|EVcBjOiRP&&Nr1x(}BwW;x3Tj|ZjB=AAR9TE<$6_jDeBMkly7Wd3$DuCH1up?K zg|an&Lj|j5wxL@xUoOC4(i90fMFbzZoQb!xR!J(4YuM~4wpjtNa)wpsQ0%8PEoOEs z?ekxXYO{O7BF{exTv0ldqA~8%<M~fVu*sAUn3t+geH`5Rp>56X_u80)QLfk@?U}ym z(F!ut6;O731$4eUK0V)#Us{o883g$GdU$=_KKI^ouXsMsuPX@PWYTV0TYI~B1$@5S zw*Q$S{(;e+7H9SK_$i-#KQPB`?%IRtY;JzqmaeXSy?!?RTuwRV;Wr0!(`uiuD7^Xg zYQ$XXX6!CM;NTM_CA>RyXe1@p><)iV2O<qPm~cZ8SBgDL$Z=dIT6JHgA|+b`nj|W1 zINq-j;4{A=@+ZASt0&h5yKhhh8auk&Uei-MLX@>MjT%${a5O|B>p5}M)C|tE5jFVW zE8<h~7oHvw&wY6#HgZr$1a$=M0yRvA38F^%2vT!U+t7ez`+0J<^2Fxhs{AFqI>f%- z*Dz~JUbZ0{9yUXzw2o`=o*)Jg$iz-i2n7poymi+mPDv()-{iJs5GCE9fS!8(I7-hX z)K@jF%L!>EzOtyot-mSlwAqR;ij44uUb9e7ZJrVtNN<ya-ug=N3Q+#+;+-B^oSx(? zEsON&j7h{wu7q-GG^2wC&tu8?)}6OWs2e5sMiolcmldYGIC|V~xfRUI;bPr^=X>-J zO7<6_)aBf8<bP?J`sxk|65?H^N~p^6p4exyM-PQBk(&LX97n{6=6cPGpc2$#s6Qf; zZAsXKQo7)mKZN16fHs3n|6qg=6AOCVstD;>mFCaVH7lBO4}e1W5RV#@5kdTBoS&O~ zKzE|v8FfXu^|NH)_Y^yvuA(eQC+s`R^Eog+p=A0+jjxU#2Q)z3eVqid*0bZhVJH&k z#+hC^4yPpTAuGrtxaU{mg(hF4yJ)aSFfI0{oyDMQto$`)&O3<E<Q$iOIMy<t(&@ri zHQ-S^=YNcyLzE_3m~7LwZCiiZwr$(CZQHi3O51i;+BU228@{!A(4#Xwv-k!(c8Jy> z#Q}e!QA@qE0~b4qV!~XRF5KUq*!z1>K@C+q9vOm@V@hXuwSL%pdd?i2vg=KN8KPMO zs*|v#RUN?ysz3-O<%*!u@a%?RrR~`Fed4ch^N2z!ec58zpz(dG%Sqsj)Bh_#l8V-- zg{g;_I@4G8D?o^7SMIG4d%gepJC~%pxpFbGk-han(Pi@5M4Q(RySEGb)gMA-ZI|!x zY3YkVrk~2&smj;&O>NrR&Jf~Vqv!IILA3vO{hHfR2H$;+8xlz}pKiLcSY$O#`Rjrh z0Kt((-3EaBh)sp;gZ{1OBySS|*|SHwPR_*>5}yT20`!^+PJcdE4adx&DT1s*XeK~g zMv2dqw^uyIYe-g=aspj6MsZvW*JGhsowxh91&3P+2)b@-(a(!f42pR7-tcRpR-Mod z$2XOEF?l<T6bGidFQ36!p+V=Dx?3NNPlLHd!C9|H($3-@<+1loDRq72MM<b_&GC?B zQ+Vb2#h2;hQA|o3<e3S+p?hTQsDC1ea-Vo?6_7P2YrG2bgs*@J7QOl6QY8hcNn*TN zhmBmL@Y){7UsF>h;wmPVT>Uv4_9>T_*pJ;fKIPY}MNlq=Uv(<L@brS?Guy4m8%DBr zDx_OOhg1^W>hZENE<%v3qh<oTazOWu<7CnF@)zo*TcT7;J=ZjJZ7D}BW>{{L0=#jP z8tp<PKXkK;9KX~gVnw~AZ6;GU^OBayKjOk=jZ7TN7TmuFTTFVynhb0+6$5Ei97f4e z(xARwG*9meP*)aXrStA8Tax)FGZYfHUa3}WJhyjE81ljt1k3JhgcY%Hnn4t$v#E5e zB}EQIo<gd0xFj2r57eiiTZjeEBjG^s;++yXk)4kwUvF0T`$Vf)8UE%jy$dMAFQ+LV zKC)E;BLHEbrLQFN5jzrIPgOR84n#gL*^^kExn~2x8BFbT^(bE*3l_Z-b(0!EIJ4dp zZ`1|V`$Sd{Og;dI5a@ByMtDm1{%vL-b4<L9yX{XgNQ2xXjs_hZO1#+SA(nDQ+F)T+ z%+V;wynwoFg}W<KO%A_=__GQ1yx9Y2AsrNj>ygVrMJn9G4laI=3foN-rh|twH{bR2 z-X+j}aEP7-r#Y{|;2M4hRXS`+Hpv)xkw4Ucm=IEQu&-OP*Mx#CQLUJ6%3ia(q-I;N zVS}S#tC@x;0(r7T+L$)2KY==#gqv=C3QJIAkv`~3IgTq5#-TTZv#V^d55caMMO0iF zmgf>K!V+bj@Ft9Ehy`nxZP8i{adwd_LyiQc`K;6&Jkp|t;)hkcZ|U>BtjO{VsT2kj zXq)RumN{eWP&C`~YWJlXza{m~Lz+oQQA5$6#0rJt6)K)vGa9#CMhcrPC{9Jy4x^L< zAorj)AiT|B)4;B#c%zz=3#q@T0m&rZhGB(CV7p|kLqOJZ#`=pI6FgCus;SPrqFi~o z!qpCiR<?werJ+-mtz{fx`MWtNpX_cXxhrqFizy@+8<yguRy&+jaiMK^OO3*2FN9fk zG*HTKdr^`)4AM2U9G<A6^5Nn9#AeetpXQZJ^@`@OXdQ5i3$!W--U1no(pel)8=yh> zddOs+)FttATNjC?5W>Kv%168s6cdY7M3ynEs&_C!!TO1yO;mp3G;<bww@C|uv-ry; zI)vWXofJMDm04_RDyJnI6Es8cr?IL=7#G|hrw9LfzTG}v9%t_y2=fj6eZ76XJ|91Z zp9Ho&-dA_gr4@+~o_=0mGcwL9<_SA`Jnjex2><wheoRk8j_x_^gM9GuXEcW0{19ez z@9sL>1$-qS;E!7w`Mf_puJ-?g&Py~dyEe%}l4d!k-z=X*FAlCyOuMgfF;9lUN0$f3 z$E*nl9!p<6L{o=vm3sprlircu5S=I#-`18Z&2;I(mKsnpfD<KBjmgn?bHuG0$(@;# zSwSooNjiwLzmA*TkDz2aHo5D$lvcGt7ez~_`(N{vp{)x<f{FiTTsv!ZU2zuQqn_i> zkr|4RdA#(cB*}$ilY_Cqic<_GZWyKt?eQDD@LRl9lLmqPYuNIu(r;t%!^diub5aKd zkatwO?U7A<ou*Lr4yiT3G*}2rEMvRNH-)b<3iDd|fayitJ!cWLZ+sx->d-I@lI7JE zwu}xC^nR)TGEmH=*YzwP>Vetxut}$rpw*esw+~*~3#8W`5+6am$e2UPNQgj*wcv|A zp&t2JC;b(Ij#LkJ@!3(o=|+V_>)z##tFOeYpu+jQ#;1VHg+W3>OtCUJb8GYKN|=V? z<jWDJ;fk58^#iMj*Qc)XMPV^`5=%elh$*GOIpKM^*5lC-(pTpbpF)%l#<elU_no6y z`a(v9K>}BV^Ku{$N5w-%r4EO?F>KUVVfvYnRMes%_g#vIcO(ULswK~<$MUw~5<)@h z+?=nJeBRf%o94<$UFBl=7)`D?nVxPXE*_O!+Yo|}`r>>m|6t`yfg?8tXm_m7-+m`I zW_e_8umhbkN38C%mrMGO$*n)-j;P+3h~)4cyAq4-yi8Ccd43sYY4?cY$*XiLLg}Q1 zKD7`mNl^z`^oAY?yy-^~GmMknz+448*h*VDMAaZwXs5|MKm^uZ$k6*Pr&?^pPb@8< zWSmhyS>h&G!Cx<UetLGabXhAsy`_?VnHW2mlG!=;LcyqO9Ujs10P+Y7?p^%TPC0X4 zSH$v~E0CL7^nusdfDYRnM;@4B*x!#=-TTlMC)>ms!KqmnYz!;RZ{$G<j9Z=&{o5cE z5Z@qnAJ&^IDD9XTxJ+^(4P2)B&{2nSVB{+DcAu4FwTPSZDJ%T0{&pIWAGd#YbGX`^ zR9=L+f)!Ch6Sx#&Cpl({At5Q-$w_qMU%WG)>VzA46Qp`={*XRUcV=Sc_BTS`4FF`g z7!lU%ve1k8qx3-HHC9~qzK6heZ;K}6-+U>}kx%Qqv1GWyQ05>_UzDv(i>@dsSNRWP zkJ8XaZ_~XOUjdmfbRy6<m(!`B%~4}Fap(FCk1+N&;n6)A_fIPwy0f1;&^iSW!x_3% zTTG0XvmpBmE@fnlMXVP1n&{9IMW&5vTyGHm_rp1!oMk246_Hl*O@0XUL13I6A(1tH zK+Y#3Mfdhxt*@nI6QNDh+O6LA$WWUs0B9tNkg9XsIVGEs<gI-yE87Un7%clkivp`T z86OJ_+%co?les8rU_ZC;C+}FFv{;=}9o((l%><UrhT&<pMa(;c=YnmeRO*TY5W9_S zb^oXp2bRIi6}Pu?-4d+`s;g>)ZQR_OMBSF1IS;4qZ3<w4bMYp%DWUFy4Vh}9U36wb z#Fd6*vl6PM?N+LOe}ZhM{2e2`X?TI2c;56(YosrKT5Z-_$a(#wM`%2ax5O<BO5T_9 z$+7*V)J-6MZ1Tc+XjZj4ER$aN1`(U{w0n+07~8;8-QbhTkJ2G;V7|K46KOX%qw$Id zT)Dld)3&0<P4bpYAJ8$5C7jz5QS`u(WB~;pndKCgrT9AX+K}hur&Ouzyb4>5f3}fy zibIV%3R?Ab%{j$YV!G19nF8ioT#4!m#Ju*?LQsn+v1#vjm<tjejy3J7$6xnUDy_xw zNb9`JrOZ0#22&1&1N#FetTTD<ht5)WY)U-Rj<G1T3~pnS=Qj0wVRo<T{jPM@kUF@! z$3(t@i^<UuuoaVwmnKxxvJQ)hB$|a(u%4=_K+4JHu%d%?D-rI=N;w@|&%yq4U+>pn zWOFuZm{liWGkprq?g$dd{C_$V8mwV8E<YR%{OrK3bJ{67Py+XM<Ks%nNq0H_;J|H{ z;z)CA-PJaC6kBR1h;1F}u{hNgjl@6s*oWmo)eN|x?BwgzZMlq$SQ&bKe}Tw1<EaP# z9GZt=oC4vcQx}yA|F;sK`sL6=%RpUXu89rf_C35i<OLqAT%sr!LnjT5m+HnYCGGJq ze;H`i9U3D)e+@gULb1z9qq_0s)(dlwQBlX=Ls?#$KFrKK!Qo_ueWb7Opz5{oK#>dv zRb2}#VMCy@-NJ{}4kMPgHvgUG-r$NfRaU`FO((_beOn#MRX%8U!T2RY^>1}{JbE=D z{epA;4R>x<_@9^gkkJ^DmYB_B(oryUF>I8OY4N6ERGdb`B^NbQz8@oc6<1Wo`}Cb~ z6Pa@d?mNP%uAd?%TynFuue}ZdHXFSdUY~HCtcqM7vhFL-=>SXY{Ekbnh`g*RMoO`v zI`5~XJ*WlL87OnwS)Wpdu!o(E(onT^;*QT`xg7e12WfIgZgz%BNCanpGsGUTZRS(t z@Xu~ESjb^RQpk1jNm*&M9~;Inx)+8c7XCI<fc+@z-(!&(sj|JR<Et|BX!-su@Rq62 z;RIsDT35-!UP0u(D2suT1qDW!8wGl+t9Y{~D{&e0;xa@Llg${aZlnxK;^+#>BI7Fl zC}U}&mt;F@iP70KjdPkS@k$L@&OKUb>Kj1cjJ+;|?QZ-_6R4qGGD=@)IITTYGN@sg zCWd26h^Y%xScbJl8azX0P+W8HP^4ASNEQjo_Ij@T;-i<El&qXBCwZc>GRNOpu2_|N z6o!1)N@=JlAKC<oM+Ds<IEwI>E_qm~%14L;^#wQ7@?B=i^^Q7A&6LF)hG$mGVPBE3 z6c@+KFYK91EpVc??!9*h?`HI0(DjW8=eZVU?eqyGm2i_<ZyfX7rmG?{u)K=9ywAd( z3DG%z5a^kK&Y&41@n#?Ff*uF~jUq_~KzW~DH94E2OGZzti}8LH0*9uG0qQ91Tm4VX zPTG?9G(4JF4e-^nv2C3$4gRiaHMB^4*jR5`>C@?_7u8{R%ye9)aXy-Yvcti>TVVxV zBP%I${gjpp`@Kh2zxiQvBOC)VJU*#;>o3Vu)5xgKT)mBc>X!bPTE+w^`Y3!d!I+e( zI{kwV0eX+(*?HQ^815JB1J|i(kibU)J?5pwj;vWwt=4N+FcLgN-a3?!tltGQ3ZIpT ztclcbmSe;NtbdjXG{ox|Dcx#3Zm(+<nJ2U{;<vGOYX<gH8vA4ZhpTzN5BOm^+FGI< zKMdy`&t@Vp2sD?m9ajU4_Ks35vW%m&9A0LEnaqtHZLvmD>{h!h1Zd9(7h&tW>1noW z%>}b@9>V%dx;Q(=`9pIL7o0gE=@RQJIIVN~;I4_7KKYTdpdb}F@6=`Sw+={8#me&W zWk#!q$DPsx19fburA7~)sElzQ47X$I6?M?<0M@WdD5j+f-T`ZM-SXjKtM+`ZEjw6K z1xX9lHI_%UJST{Nd3!1vTBB_X<<flAmtNCd-r%g~i8yDqvXb;ploZ`Jsmu7Nh2bh# zbB*f;uS96xjGd{{8Md3vpVZl#B~hC^v|BR>olW&3S8-IPz4@Cv(?${qk<B#!49h9v zo(<z$w6oiihkgJxDf%s%_!LW?)4F>G&;o<*5sy$OYtD-6JQRnEx6M0T2<tG9mXtRZ z8VCup_OMed9mPh6up!d*6AY7M@IQSq%WdcFeEs!&TtKS=-rM*IgvV|~lZ3diHjn6x zJnCyliqEIGC6}q3LuPujy$zsZ7|(w_*`9a_jSz!CUU~yY%7J$G#wJ1d-`0ZdWUX;} zM5^FN`3ywbfRg*Z4=+rx`X}!E(kpAGzbTjn`LY`kH8a+zLP|LInBEZEAL-@{Pdrch zW`cq@b-ac!l1v6pKDar~;T{=B?Ki8}GOTWq5i}(Ac#1FnRJNe5jAPLL&-bT@0RQ*x zi2a|Vp8t;W$BF`hfd5<`2L9hS0SN;4H4gmg3gXUsC)?v50Y8u1^MAgt9%c~*eE)qs z1^j!;F!=L*-nP&0uW&;6@kPwHbaW^1Q}E(!)pW&q_)gime_#uOVmkKY&q>R*iHj24 z#wzX<I?ya68RPr)T|LjGw(4;cWRxkhTYq_3{qJZop2H#GKhQ=hV(zNdvG?ocVfOcX z;%*!=WzvkrS*(&VmUJ<(zN6;n{Tm6Lguc`GGpJVZ{nBQIH2M|nzxBmTa3*cMc#*@c zKv#*0e_UCh=;wz#B^l=kNpn4Q#C9<U3?=m^qMn?+;i-i)JRq84CT5~*_)TOCDZ3HI z%GD9EQkw&rGm&XVCb{MBz<-!y!6nU+CjT)d%0FyDv{z>i<Iq-;<n1yqaC^id&}_&} zdBBny;w5iWGXR>Ug3@4;xQ#a}Yu|<Zk%P99BkG9elpW|jhjGDIVL}(eQI^6-BWp}4 zFwlTv;)2tOKsUveehFY-eAqjQK|WIIHb_>2Vt0$MxYAR!p;*+sIQo)$QBt{5SVIp0 zvY^T2Jz|f`2B6QFxq4Oyzg8!vSWYtXM}MG>Mr)m7W(Gd4O_|xEa9XjdptM3^9nT^) z1QB+aVG~S3wsJzy*h-MXF~~zH;t)npufWNJ$Ex~Cm}PAvN70%jFkGTXnBE<`yOW|~ zt=BOU4Il^r7u{$`f2y>1&vS{p8~3UY%E~pwH5&rDQLAlN`6gw`_Dyz#9-du$vwQsP zj4z)*f11sT=I9}uuYFj;`B}wu(3ET6Q!>AnzH5|_d}<h-sWmj&I<Y=AoqJ7|=!2XZ z&0>af+HXp!Ne}c(i&ab7nROW)o-gNx&=KX_FYS&6E=M!afU8o!OW;2tJZ%#o-dE|d zNtbeaQ3k3O#cz6Xg2`8JB}(bDe7cL79L6VVFq8Jy4v8UT&3CBf2m4};S}i(z3Dk#A z1aSm`C)IMaEFos(6v8FE7o1CcK?H@r?7c39{jO%F!jUc(!_)Y#p-6jPVUjrZ-@JJB z>WpxWcO8X1uP>~9n<U<lYH5sBAxoyD0%=uGoS+sx6O4Fii^ZyH7$VN5Yu^k<SkZw} zDti1(<%`?BXP)@5M0>C_5Ge*7?H+3@XD`etgQBOvTTWf*I{n<cakX%hl~t0l(W<Tn z!<ugL-IvfBlFAV+Rpy~8tnv07O5Gx@=3Qr-tTdyb0NE=#kEv&}UY*7=Oe~#^iD}fU zMb(mTmMmg_tyG%;bJa@OR=179jh@5~@UP-;{i>&hXdM+eDhI_AtdX!#{tqmfTXd&z zQ2;mm=rKSIf>o6VLnM<W5`md%-mCgtu?z!(649=3ie2jZuEc(M43DZ<9%{Cyl2w|H zU?H_cfFi<@rUqhl`DQeXH*>h|ZkAT$fm-cMzcx||l`y7Qg!9Wt>LPc`NH02@mLMdK zF-CO*_R~18Rr4sN>@=!oG*GbOj!-0!Z5d=7!XI$R|60ruJKa?B7g6$$7maxEg{s0D z+VSr&!R7bP`vhd|Me_z4s=$FF&l2PeDEMUcSSvDH#(#ILDC@A$0yf1<-HxVIR_ZHt zXv<iks9F%dDTGL7hxzS*hxVkCyQ+Sj3;kdqJVVI}4Ug$V<J`FahLD-0vfGkN4-8%E zGt0*mfUnxNZqH*Ok=XJ@S4zZmHIE(w@?CjO`prU?hGVyT$UP4)K1;l+$Z(luA=+7S z<#3DypqolPWX`1Q%KfB;*^}=yWNQ^Rh22x04}7=REVggMQMWu*%IoB}?Su`JZ_LbW zB^bV5e3qia?FIKI+7@napQp8fku4?#q)ULa5jNY3muh4K(c5;0+tY11oZIZS_3YB- zGJm7*ww;U%I>Jzw_KxL2f{Yu{C&r<5mn}f|j6I6~1`oieC9>ds)8!_z;5woLOlp3~ z?%g#oM-<~1N2-p`>wHpMB}wf!mqgRl?l3xarr2dK$ov9_#~U3cn|FH&{SwZ}ZZFE) zWlj))#RY&@HY%!%Js-JsKVr-MG`bw!pT#5RwygKXp&;O74-gzkwWLA^be5l(`N_zi zIDcZTX0JT3x?U|`YIQ+?CV?{+*2Ga`zH~NA@lb2FE@sqn3nVR(*R;cwdOHat)bhT~ zF>=OoY2Y_e#oMd}nKH#u^&X+&p0pKYqsz13d`^&iw^9J>F6qTaM$IYrl4VPPRqN(h z@qKyZHLtaN?7`do)<~u$n>2v&H0q~nVgyZFx_nkl<`WBM;S7z!y7wRz-xjN&t_!25 zorY^g@86cK*3qmdw7W>iWvge-Y$N(7cNl7K4CC!?WLbWCiRk=FG0GXrl0ZJ%RRw3y z*>HE_g3146^^JB2*J<sxwuY8YIw$#Ow^QA|t-~vDr+4+XlD(OI9!FU=yOzkPsm6PJ ze=FTkiG|=qTyjA7!DJ88<p$v1j{WE*SQ!{}vafv}0yW;1=~l;?>aVMGhH#E?XuNE1 zbMBPQG$LBeZ2|_qTEpNBy=BDrMGv)Qg;PD{I}M|usdZLTj`jA6Kci|Vaw?fIQ%}x| zViO<W2JlC9O02r1Z;#Vxi1oy$GeFzWP{W#^^BUk0U(pAo%(P?4Y*hZP&6?aO_$t5! zu3I+;XZ9uaAXXVVZ+?=t{FP8oP6JN%A*ca`Mge3M+7R#rN^WLK>1Z1X^ka;z5m!Jd z@g&1FBRbtGtl5673)$k>&&p{7Q^#GI33@^Wa=l|Y`H1&lEgEBL9ExSzO0q*#H(xZ` zox5$1>6cVEk>O$)L}#%rD!$^yum$z4t8?t`JS1)F9?dhU@M9+<85#J(#l~*Bpu*O1 zh}1gi(@%T@81Xj2$LrWoRuU>VdC2gGF6Ef}P@6kv;Eeb~N7Ilk^b*7|Ssl$f4c7Wn zISyW^t8eu$m=Va}BFl++Tkv*Vi>`v6@JOkBYB3(?viMqqN6+Vs(4RO}e8cU<xs+4v zt;*XTNt$kX;{1u!K+7MavCdXRvY}`)qUFI#X1hflYG4L+qg8Q#7#>uWrq=j{Gx5|Y zYW$FFlm?*}@QXQ1_I!7xYeUp@j}L+C+mH(VNjgv`;wH0}_~Tb9P;wNpxAkum_l}V( zM|W{=O`1;8QJ#=9j8~))u=((={KgUy4HY!T_X!U%y`$A(1}3yTp!lF{4_8FCgp7u* zn%oz@d9jLW5EwMf5w|qFh=i-}`_xEabP<hcmq9LEB~+slw|A|UR!5GP8m&EwOrmYb zz{(CpXj;nVF;C#$Wm_gh8nZtKv_L$YZNn(Ew*ZCG)4yNuZ%2#*0dKd{>jA!>_b<zY zfBrq51dPA=K8v>^+Oj=uAN2(I{XAa0C5-0~`g^_|40p```#!p2MEQDsr%ehtdP9zP z?EN=ig72_La2N2If{?dvb>R7Sb(Hh-k+h$HNN0NclwWPe3WToW%0*Z}ct~7`&XG4i zg!2z68-dbcnTRo`R|z4jx@eha3ujH!wdpJSUOT4zKn737$$h2?gZey>1N$$pP7{>5 zohv@yI|iG`z0C;8Un2K)MJ1NSh-<LkXQT4sTCFvfq#b;+WB}H>5UQXww$_y#Win}i zgd<inTD5xvJs=xvmxpZXbv(UoSSIGwGBt}Fgt#;!Hq7hL)kc*pxlkMaF2+(C#?{%2 z9A;6%5sK$JFlA-=dp?-=y?N$ffq=^Tl!EH4ERr1tA+U^Nw~&H9C<}|LhdqI55Uh_q zD-ls|B$zoNGq~I%S|%(?(J`yqv*lW(JGO-Hw3w4^EZxffwt~(U4+29tq#1Xz5A~X% zd__-3RVQ+nAR0MbCkLH0bQ<ez>l!cUq7aB$+zskCClX^ghY9`@297G&4-JrTPxORU zDetD2rh7;LY_*IGRg>+z0E<uzU84s%DX83fd89&OBrYANziH^z13b!U(KnTHixUM< zwWG|%DfILmF>fvJB28K>PJ(jJR%`Uv-mD-&jaVyIQI7Q(Yk9Y=3{%hT{1EsuYthlL z2Roo#;K`K(!i^+r{vd2Po-|j-=7chL2Ng>nADX}*l8Nc{>p$&^u+;}eHN_@^fScCv zn>6=ub6Yi=_(aO0Y&vPNuB);oeM~^_(1$>QX0n&n1Dov-qnY7`up2ubh<+9MMOVWo zJC1jGS5^Wwds>{fluEM%7@M-0YGXvJtBxwnMn{LR<a9-I4SI4dw^XOg#{yK>1eZrp zY|%y?q^_k@B{2|#0IdFHJ;-R?^X-Ca4;yp5dY96a4N8^JD&iOURa4Hm0ywos35nN8 zugQQRpPh8~lC*l~Mz;~zY7l^1sRdj*d=M%&8>obv8+4uJmXca*9$y?Hvn7Oe*wonO zPp{9SX0Dy>d5Z(Al$hecX>RP{D9lXwxt*j>=h89Q3mb1Wy{X#ZP3(NB^+Zug+vhjJ zxUEs1D{6e`D>n+ckgT*UsAr?N463k^&ed7#+EL~5vh#n^z0BqGWJ2hdX%hIzS+Kzb zGZ5uhZV0DWqFs*VMjBB;9y|wYd}JTGugwIN^+eKgAZ9}|3=W#GRvQ+D5aM^kzXk;g zq=QQO#@2IJhO}GRl3MmIgJIa<mNnUu9V06^)qV75HT6l?U!!@8k&TUs{Prlgttf|* zIt61}Cc=+Pn}4ItaD@Nrv&Pq|FwQn=6WT7^(Jwx>p$$=)ZYw;JXRoIa{oqlC`88r^ zwf@D4*T#hZM_FN>RM(n6Hm4lx$Vu`aZ!ogv@I+=@oLs;a0nvq&Q2P@v*?%sRXl8BH zUS=a!OTUUWw$TyR3WL(ju7y>coASq4CmWxg2*=Er=XUrMS(ifmWE#jW!K(6%#AR8~ zwksRI*aPrtttXjNJZj95-dc+?wjx=@9+A}MUYAttLi;hz^1U<m&r`B>B!8Zogi};q zOFr1<3U8vAnVZd+BBz2xM8m64X;x=47~}Be<C-kAXEJiU-tvl>**t`lASVZxQ+nL) zdr2Gsv?#vk6`)f$d0f{#HQxG<MqTDCUxDb*MDxcVdh|ix7EPNa9>kjux{PAfnrZyg z8g<!6*4`bdZ`vKcz<#-s&IS#~;q<vvB5kW2X|WJ59EL|3#?DJuiF&sNmef1O^ar0l zGh^pw0y@<`4o07xV<@!xw6}cInH6y@b4gg!#5i_vZ>i*1aOQ&A_N<jaw0n_Y#95Nz zRv`&%>651=O<rLzZ4kpIz?s0;n?{rRqjtVXXD~h}-Bc_(ltpY7ntJW%R#8OR#|Jj` z<eGg=x*S~=c%Y2+8Zy9LW86?L0t$g2A^E2e?5%zntKu<<kB~+yy$ON11KUN|%qoW4 zRA8}<wE8-)uv#5In1jQr?l$$rPe09aOdgN9r}BtepDwCUKJ+TSQoi;2%r*v0=#kt- znes?YSh&@HYBl8%xMMM%9L1boa34XF3KjIJ#2}?FmJii4I*J;ukoB_C1{utx#nBAc zjCz`#!I`3P)0(^td08HYdrNO-aQ~s?RxxALo({+`)m(!g`kS6x$84`#WL5cLvAM~G z2GsULkw5iHR-~i1m8?d`6+%*Lx}eTabrfW)&Nzx&W6V6S74dg*fc{Ffeu{8WHDg<q zu#HPf&kA_b5HgUFEA_<Vzy7};M1D2GsQVIs_m!PPlK-!5gUU^MaUAegRan-`VuyRZ zjUVfCJrE~qEm^9mVDrr*Rv$qIR-&7^wV=ie29Ri&I>uzVb*1(%Y4{w%=X1|<!(1~w zN;aPjm=7Vx&q6%`0E(o1rw2PG{jRBtfpJ~~d719V$Qo~kC52zB^4J#bb>f4w`0#dJ z(Xu>I>ZVwedC#z2Y~^qr>ssy863svwHnFgK@wc@AEn07?D`i$&0;9MC1IAF}c*#2l z#j!ONta<1-lHV4oQ5qUGB!I6)3k?NN>teon)I0X9AbU($4t5*pL}|RYqd=;KJ7-%( z3kAfQj$UR$-iD81+ky6+)xiyNy{D?<({3TW)K-fpxc@?9nm@txwgLn4`~~lax+aWm zb<Iz4$Jjc{?V&v$ZB0IM4hQLF(Y<|qCU14LGqe`X3nPq=t2k90EV^=%U(VxLvPWS5 zs(IdtI_bFi6HZO1s<mtj$O<hvo+#g`VohiXuwnQ&61s>T?%!ctG0x!P5C6u-LJf*E z_G-C^Sn=*Af7N|E4DrQd4!~GA(W{cFrii4){P&sL8@O$<*}(FAz05{15?^h01;UU_ z<+pO}S+#T%d~&dhT!o|$eiN1*UL4GbJSI%J?z{zS0Vaih4T$JjII}%L!S|y-J`OqP zKlY4spUs+OcO*Mq{mrS_C$Bu|ZzbSKkt(YI{W1)Uz1Q7T4z&2Q9=M%O@2WLDKDIhN zy(q7Zp^Fv`<8^|{Ru*pFvO*xQ8S4r}XuhYjCq8q_`Hb?^g-DJaDYlj~{-GCy`tY&R zm|jQiM>Qc)f4G(t(Q6G!93JIbu6rK%Og0=Q{rrblVlWE9`H5m4)R%g?1f@hVMQWUM z4{1e0@mv~MhuP^IotMR0B*Nq<%jEC{>AbDoHPjV;E}zZ`M3!A)R!p4R4+1C0jbG=1 zyao31+tn!!zm5YqMf35oJg|)AF=xy5!DWOh{V$c(bPMh!52s7oVF5>`@1|ztId*YL zn1&51s<%qsrX}V%tVL!OvFP|El+uc$J+?2jz)-v=OcwDc6@Hdd&PJ>6Iaf|oH#jzG zN=kcCsOR7C!}Io~6!6EYDX6WPM2oRcDp^Crh`iuTU!ntFoLxFx*0n?A17+3wG9-k1 z=IriNUB0=*&X;bwC(#!2uq2rsB820!5xDgsppVY*=D;3cN7>4BA|Zd)L*>Do2xC;e zB(OyvuHe=d$5UOcoohFf)ueiR?@$RGI^~phFxg2@C4te33yp7g4<RIZ+y1IEl^xea zwfP_oarUVewt;SRl0;1>u8|0A<kV#Wz3-z#g+KcHynnu54sK5lF0O8#{<QzOSK6)* z`2K9}`}jE>e|zg_U_>m?C;a#O`2GC#^Iyxr|NZ^(qvEVW0Wsl?@b=G6z{lCo)6aN; zzQ3pU&*!827psBC_RH6QN}PX}|0!__0z7>`Uq5~S6g-?Nle4rzJ9OCef8D=y+{}Kx zad>z%81VD<?O1pFkbMa3-+JW`{<!}f{JuKPpG^sUo`!gw8Kjcqcz>1rZ3JrpJ|Yww zC+BA7+!y=vHJ{e~;r;e;@v;4T=<(g>n6vHw;hEII;m~2={~JL0&x2=Ep5AqP_Gk3! z`^@3{=SG;^KQhFV1rF013zqEbW*Qt$Z`;sRX*BW7esrRcMHopy-YO@XghN=gP5RZS zIU|{VA1{~Sv%L7%ZK_JQFPUB12Fc!ls1g)gxXMb>r9T_VJ%H(J-9stqsIxXFx!B#} zx|*ol%sBrnv>&6Upu6v`I-6`W@iuaY1ZzajYDp~&%4abLJhn2krrJVwIOU!CA17uW zn=#Qwp|*?mpI@qDjeO#2gQYZuq8S&OG1fRgRb>t7I?bD!FOR7|s@?X(OgKto()Dhk zol6F3YE^2aKvr_{%b(C0W>YG4)2dl~>IKV;#1dAko*JrEL1oxe9PKIfW~^E31|%7l zbGlL);8j=Q7$i2i<7%$U(N(+_Els)Qp=%JqJIKprb|=JjrYR`g(>0jfYdRi{@<sN> zoVXpP*Fr8d7qRNSa74sy+^k!(ku?8pQGvEF&sN%NVALq5P?Jfg$xl$Vj}<;~PI3w; zmPy6es=-x5G)Vj_qjfxMp)a-Q$ykW361nQga_dO!YzZ7<Wp2eu*32)*K#9dti7={@ zgwA2#c>CPJ2DNkxpRwzd-ZQJUR5!y|7xoVIs8-Qkvd1Bcsd<WB{IkiJvCEl23L^l# ze_${LtL*n@gjQ-3j44r25Xk{Oy9r#9Oi<<jUDcNX<<;3*P<uK+<VK$5SN@l(LQq_A z8mcX#?1PSHBm@94K~Ht!8e%TOqJNpYB9es*auQMwi*3st0-3f*7m}`PdV)gsnc=b9 z+$<Oq)D?|Ir&OP+{QlfZ&WoIAJ<PB4oH&$kohFU!^9HkQNA_f!KxNBmG@Dtt)uc{( zN3i8-S3*e`p<RE{+47<0>ndtmx`cWK>}D62!mFD)G!WZ*Ma*6CT#T)y`qiD4KDl*c z%e389r}oNN#fV?wQDaaqDa*$bITHSZ0=-&mp&#DUA;`ZhWk?NbT@vWsa?YKMA1mz? zOf?#gv{;FMy4P(@<6;;7`(0-rhgRMee@tZQjR>`mjEs#nY5hl(YiI*M&+x-N&YFM5 z8$_h@@=XX-2{B)L(5dv&P+1C2b}fE_k>(ezjgW8-?&JjjgFS4}_-T3CYU-n_%OA^x zSRR`?Y3wwUIjgiMOVf4oYw0ca&y2qZ$FCnGhikEd8NZ&pQgnA{J=@I90%Y4_af=*m z&Ufq}jWeY(60bg<#X9V3gs2}WKjC^=fKOW+Ks3e*AKNiosA9#cfj*2!mv+pA6V{z^ zi&<O@eo=FSrl?ZZtdZ%JNElb8g3aAumF0!UEie<dG<g)d+$1}a@hxskxi$)VXaTva z60<sHJ2{Pz5sEhYb@}LNZ6s<9zw}H<9ZPr{H5L}RF@la}{yNfa-Kts=tK@c5aZ$?V zy6VO{@&xv~Q)H*-QfGNI43m)=QJLe=*dFV)-ctj0TblAn#3f0A>RvKp%N3mL3u~o9 zDm^|hjMbfo{I&il<J+PO+Kj!5M*X1Z+H!%0LW-^i5}EorvWIY_;yxGJXWQP4^oa^U zwHIkK&W}#E_7n<twV23VvL^z1ooJo28(EJ%QV$w&nq!~tR=4g>LEH63m-Z9-auArx z@HHKoE1dVQs6Dj1a=i8w31MsI^LJEO0}~RCb5bCfh#RB%X_S<P#U{~s2Cf$;U{sb# z%r&#Teo%q0vIzWqQyxQzsSZcZ#@O6)jmc+TNwZw2+fz8=3SAvG0ga1pCN`Jv6>9YT z*XZNmRDk2(v9RCQ?Z-(&!JPrW0H442@7)6?zxDX`AHIIye_KEA_b&%me+csl{XfR1 zBPtvc7|(Wb<2&vO^83Gw?gG3&ZiX`|{?mR4<T~^wO)@mieGfmR-}(E0zj!|V{`7o( zIT#4=_xYlDD3JXM1aSYj|BD;nPCzW^&;2FZlZx`YKRUU)<9@$=`0c1T+gA_~cyvLH zcl`dImgCv`{_kxNKz)xu<z&o)9q)<Aad@5I|M++bhoz5OYVgxRY&S$gF8+Rbp(rWW zu0%-598!h{EsnK(B8EzmW3Tv+N#x@MlMuIyHslUWMQI>yMapzkN6BI6_a@EIfKo!_ zz7CF@WKL<^4n!JD^DstP*S?vO`!O7?P|k_bAX7)2i(imegFx#s!MUPiop@A^Zlc0k zBnuZJZ#^9zY$3>DvEwB<a4q`-PBlg4Qm2-#dgn)j2@3%z+5woRg<iOTfUE=bBda5d z#Wne9acJWl0o=CYdbj{7W0m=6AsR>YQTr<^X-a@<BcDsmUpk?67GQHtRIw{iVQ>`J z{PqJ@tE}A(PrFkl>-L}cWC<xM(c{%ZVf|Dmj+w1;A=8qjGF#eZ+%U7C^qyc*%0gus z=fnTVt^YF2@uJD98h{@ry<VDFCMoG$C%>ttUG4U)npFR$9k)-!wYuE>OfTG<=?pTo zd&L$$Q4?G!HQa|v4m#Ku^7jg0;R7k*)Bapg0jp;too~5~S5Gd6N|{An0S$ZEJZD+B z4NWLYDN5Effvhu4@)}~<?A({#m?{nkb8HtriG`l+VeJ40VzH_==LjKV&oW1ok!0DZ z)}vSB28^2DGGLDj&#M=fEHN|&uQ}p#k-)$yG3&PQfrgepb!1VQ+@d_0X&v5A5U|~a z#@81z6#qBV(B8;ZaXu)Q19CyW875*UHjOJP3q71_#6c-Rq&+k8*hGY9t?r0p=!jvb zlysm@f4>*@-{5-6f)6}~T1I8Gl!sRYd(>(%PNT;&kMRi>B)({RQn_b-x+n!gjIKPJ z$(<TCjBc?{n8uf74zbeIB46+rz=3gzUoh3k6;W8OhDW)INWoy60zU+-WRe3+W37=# zg(xeCW1@Ma8kgKh(Mk^%p}^^d;ubGKvbBWvBo%3@F$>rE`G(D(XBvMQFP4k^-ZF_* z>Ax<ra-5`i*@I7uhdt#q@0FieN~{>aj|>nDHRoD!rXGWu%p6kjN}f=rvP1Tk7wN^6 zI%sdwk(KwZW6vc^whN>ka&dXWF;EpA${flpM{vO&v0WY;;0TLE>(M`CQ9AF!?QXz$ zbBelrgSbR2!x&}o^F?S<ZmFiMe7@~TKj)BVR~9(ff2H6BuEsyJYz`?NT?XAMY1d<X zH_|*PM}leSXN@tKy4!P1cRut$2L_X5y~GBb=-$SeU(lhiyEd=fxZVjij8yJwXPqb4 zs(wz;6!l|7xYNz@zc8_V7WEG>uRH$Rjlclk4l&=_hT<+MGdbC_4M4B!#u?+0msszy z*iQTFPL5?>j3tf)mY7SP>e^g~q7i`l>Y6C3Rle+C95q$Jt9zV_GmCM+C2fM$r5-fX zJvu8GU&zXY;nJS}R+#yl-f<gtFtnfyy(rj8-D!<tUabU**=}v)W6#D~q6cS{6}(j1 zca8t<4!4gUgO!D^;Nu6uxPUR3Oo>P=9wv&8LUje7+93>;R)N>yVST>c4421zgDb~6 zZtrH$A$~g;Qmi6zu|j8O3VGoS+jY{1k*fKah%zpJ+I+rn!N5b(cvg<Ok<Oa!&b!vj z{&L#vs>Obbeysem^xM3zWW311lTl9zScImo@ZAlAV`{K<BGpdxYj;yZp(3sTVkkBf z?SK_ao@<2WcIByJwh&!U&EkOiCC?CeRyv4mBk0S7@xNh57V*)DM0?Uxil7^ot4qq3 zBE=0oO{l^#Jy94@;K;bDjuD6#4PNS?iTp2|QsshJmKMtp!vw^g@w>Mi>UqY=T4b}5 zj+18`?OcZg{R!>vphvtsTBsA5vU|`>{l$(E1Kyy7<}YQ1%vZMa_{7e=-W#bM2+27m zF(Eph&}7gO`rUbH#)ten6X$nq14N^~(s(miqbFOz5s&|wN4vyg4ZX_mb&z!ITn{SI z@5T1tAj%7y2Dnxck2+m{$Q-N6vlrm88L%YcRoYoHOQfKYw#fcW<iVzCDmb1xmzq9_ zd4uHdo2n<FvfA?!W14v=ld#^cPOs>6dy|Qjl1>9ZOS7KIF{OH$D!JmIFf%*94?$_X zf>!^X;5_>B1t|3b2{%X-%g$uX<KKqwptHq?_B4impq$VMy67U^5N*@LXJB;=W0l{; zLBW5n2qB@d>-C{W@<;@h3U~MLFM%`1TuAp;9GT?5hB^jpu@l#yOpH1Qkx4<TlxWdT zGZ_D~HirQ@IpEYzUI`xSS>mRzv0zgi4h>~9-by9u3O|zJU8J`ml71PdCTxUk2A_f4 zwL*@P=>CKDLJn(QaSz8Bi?vHz`FW05L8md{ihdfhzM8aGOxdAfj9srHXbPpDf=Glb z6otJ8GJLC?u&WwEd^;*T$<@7L++psa@CZDNy-T}6FNE6DOV}2HzZiXFf6a8wjEEn% z>@$D&&!kUU{YX0aFXppA?{pxTzw?cjS%Il9$Bg0iwjrAd={<-r|N9-jQUecb&Ah!O zm|V4ULTu)N4p|&V;BwQuRNn*+EyZZY&C|utMMHqWo&et;KVMJ(?}wk|<%Sz{|L(z0 zVc5g^eS5}#UjE+Szh*sNPp7{J2WK6B0(b+8Z*E>e(i<N5!su-Y|M9floVtI#A0YB^ z&ozYI{Qg^3_<BOvXQV9Dkz=V9<)IEUlt@t6>Ca=ybX>C=QbiIAGR4Hj0LKEQdYUi1 zL9X=w#S207H+<q~@~o8NePwT>tAQtMwLnQGB;Gi@4NR4#C>|8GE?TUs@+qAflH(2V z1Q}dRJ~s5*eVCrWVag4`AIZjxMtbu~W1wgTW_PI@1ji$NtvAa+yRSJp1QGWX+J^#p zfFN2Wj*Sh2>#*_*x>)E?^uVz@e5vfRSrb+kb<(mU|63gxQ7Xe!1blicxXG3oE^hQ! zCFT~NCAL^={t=k#{+_E$uo@HN{koL%uUn_WCz4H&U5kq@j)GdNj!wBp>KN8Y;oB)A zyF3vC+eSYMR3JjPR=UN&NsU#1Xd8GEDtIcM)(dwjcRfg%(c8_;OQmmjN|>YG&kZFH z)g+m95V&cbRf&5!mN?B|9aMF6l2^VPUXzmQJhEmG4GgcJGQmzx82nh6AR8JdW-GB) zn3l6(9l&>uF@%Q8S>ez<LfmFvYhdWe9HhFj)bLiXBWM_GL*EidN`4sVJXgLSl7&OL z^|=dx9BZIq4A}=OH6j28(1BAthdaac9eGE6^9{7uwT)i{(mb&2S)mA+=)UyCWe=v( zv!>#AtsE}L9|<sP=IN4AkP9hq@f<bXECx%q_*fQiCUt9+;&r!cyWl)!AbLg~U2m78 zS3_-D@I2SAl$+JcLNwZyqkM=6&H3|5GkX=*7tBzUXM-&zt;)h=4Fy&uk~et(*E=%i z<Y0*-8iEMLyq=#Mha`uFK>4@?(^8;|9|qs_{iI50$S7ZUou{lJcdwurCTOvo?K4gv z!@d^5NEOv8YjSoP#Xq8p-A1i*yf_Y0j};LPh525n32SU|mZ*kxT}hI_14`R_#G2yB z_f3kd;#+4{T&QWybkWo`3~C|VTn-W^hx-mbGr%K)onpKQ2laRJI#h{Kc{Htci%e(C z1aGmpMlM86>UQZyIBzpJ=0EH%rmscj&vcO$ubnxBx21>er|`1zT>pkuvo!51!vG{6 z0f%*C)hr^EtZ5|OLt(A<IaoC3I8FMgpHXb-N7$%{rRy6z5lC5UjprJAu$<k4GdpM$ zW1TQ77iNVTWI?ZHn-*zg<>65IBh05_9tE||bbMeXYx;_1n(<2t1Z|?F&BQut4+K8) zI!K^^7nAvun4NhOD|*yWEuKm0wi>a_Y<y2s8*ezuT9MwxHr_Qx^tKiPS+}c|9WOiB zwH^LeqKbAefK5JFLK{vhrCMbP5p9~l0urF0-VF+?%SOFwLD&G1(~qR2#{wEHSmG8F zX&H)pm!O@|vkw9XWb(RR{=Zl)0sm*KB{L^8GvNQTTGndJR+n}l`vrd?^g+)E*3(t_ zr`3+lu^>r8JuqZS8TU5AoA*v^pQZNymMZ(?z=k-zT~sUo<Z_G6_VNE4JJM@aHFIV? z-MbG`aawW``8>+$@F*%?{egZIToo2~E<p?Tz94Zoxqh(M?so2B29u?|zG-L0%GAeU zWKFB+(1v5aNnDYsJKa>If4AmxF+_4t^Ea_Iu6%G_yPu@ms>xTV0Y{dGffjPNq6Q8^ z#wF#8^Kl~lu;QH}(h@q-7!ey)7sc5NJT%HRzSP!KmwXRwQ$%FM1S`+MJjp9{TubzO zI<6)D>f4HYa1_VcEH{;c`3=Axn&)O=p*m~AcxfU~NSfH2QoO1ms2jkFJKLWj5w^`6 zn<JTu*4WAP1k2zSbqEC;dVv@qCu(z@V;rZoqLbjy8HQEsn?8|F&=!xo)D0L4)~AYv z*s5QE3U%iD2M0JcR`%k{IodsJ-qX97GOW^DA1*;(95DG${|L#ZPOkeP#VDP&9_iNU zP`dJomk>Tw#DU~mv5Es?QCGRxsnjx=RHPdHD#%IVC&Z~=UDq(!_+U5#9-U8A8QYHT zl)x^EoA`jLiaw7snyFs%In2b}jRbnM%Y?43aW^r{3_O`KOVGNNQA35vU)|S^3m%K6 zw3p!h7u5VERB%A<t9?m4YG*$(Gb?j*aC?Ho;arK=KqJ{=o#fukf|q+KB3@@cAL%T6 zqmlGP_of{MbP2Yo`D@eBEOPuCj4R>UF&4dTjRkDZYEy`MQF4_^fCkBgobxlGQgYX9 zfl|`VhvXP_*i*z8!o)<Smv=&tP~Ra^7HU}v11z37plMl5BOxBLNUD0e9t{cWLbMC+ zFnt-S(qtkp3qqohVMN%XXlFDTyL4I8`Snnum*~?FM!Gmdd+Qb&Rs$=1;szMUYWE#9 zagny5zO>=FEedIIS<;T-KQtIGXpq-LkaBzsM3kA23xFQu!5R++CMQSzN3fJd$#W<j zcST;IZS%4ZxvJ^?IDfaIqW2jX`PLa?*_GJPqdVE26cdK1Wp_Bh|7(7Gf8YOYzJdSe z@kBwu_xEvlUcm2dI)E@>K!R|<F%DPz>FB8DkKe~hL_vRm@B8PKLV>`)&m)6?-$y+| z0e*kPW5S2KI{wR}?SQ_Eqv7%Kj(_h0*!T)IFAo>{4u5X{eS`n&4~PLUme?$@8Imw< z<%OIk5C#oJG{L`?1ISwHFq#w53y0Pun%SZw8>q!D**1O_Xbr_Le$LlD%zVYQ%)#~6 z<T8p}(D0d>tAUM*?=r~qy`_}8(AdP)UUK6-Lc&2VDAiE4!S43-?(ON>Vkz82-_>xr zNF+O}B~}#qi<3!U90vb`EZL~X)qUQqsKpt*K+!jf6T+(IuuH&UH9bT#I>bxNZ!}6I zYwVArw`H4o4^e1up4r81HW7-~vaxS=p`!4*QqYbPTIm;hoMtraNk@$z$WQ~3du?eY zS3rLhaVgsoEs$3IOu?&A0Mg$d_82nkZ;{%SJ1@<Z*HlgCOIM2k*Ixb=j7QDw4P}~c zh`Yb0lVzF6WqaalOQ(T8h?_oW@La?7GCL3!A@^+B;9V@nT6DdLprtaeVZJNr7Vy9( zi!p@mt#M)4DotxtH+d8MLZZpAARnr|E;8W@8%x&%g7qp2<1qT-&5Sr*x!828`HI8= z9#-UZM)7&{MoiX+A7e1Fq+3U9OZ|ZL<s8hbTomk5B|zLnER<1skAldTZ4s7ty)c#V zGdUd2(q%QB-d?Au7z#|*ZVw|?^=pY0C729N6tCf7@d_T&R?yYZj6a?}*l|t|#uWlC zF}anctL6M|!VesDe!hU<RmkINh0=Ia+4FOFRa(e5v&8w!YEoldd0iqWHt4HEorQx7 z0gH%sE__ZxLr0jpc7@zXocd+IsY0ZvW^lFu6N<9mbtmXiyQiu5@N-N>WWCYws;evt zRuKVc;2We#MTZw(LQa+)oaCunf?s0AYqgFbN`mKkweIdF`T5O?WPInQOM3}<m^F@+ zA}lBG<nCGLYTq`gv|_Mn8e4>@_U&XP%JhX@;I$VSoOfQkc&6L%OfMWqH*7aGJUNqQ zX-WAlM2gqZ3h)4tlk@f<q)B0tMtJ@*1QLf#c<}Agnso-Ttm!<~!VN*7JGiMFoe9&; z`Jb??JsdJZLaLFv-Ig-#&+~ldN-0u#EZ-$H6w)v=vajdxWTKu;onbIt)zE0^^20U3 zxx)xl-oniX7bu**4}x!Y-KP#sitdeGy->u1<P=yZwKAnKqO`ovD5u)^C+ZE570wdz z+KYVpFtW#7PAc2`(w+U)>^~v;dN<BKQBll%O5=k7X0_)=u!|!43?%x{Q!GZTLAC(@ zoc%+~!*vHU-CBI?fGD8<aO$~j8P}!pxzkpH%HCQKaHAm$-&M(Z*-gd%eiLG^kNOt< zwI)l>Co$g`E9}irH}%}MLnjtsga&_8WHpW=yR$ABEuK6+2(yib+gg89n0-JdCl8j5 z(i{SXjklJDE5Vx!L?g?Maf`;4r`k&f@s4r50juPO%-P7%giT$+a%~^Mw{p|pJEku{ z-g$5cnqnGRZo#q?i#^Kl>-<aXH^Q_UaJiHK2=JoR>T=BH#0>1z^Xnr?C<r@uC1w%5 zclnjr5NR2r4ZMm=H8GpmY)FFHz!yrd6$D;+DnWc(u*?90j3U*L)qPVw6m&PUa~Uk9 z*{iw?ITu1gRSnU2d|Z~M+pR?_H6dnfgc+YIKzg|5v<SR$*h%?wOxHRxDdiZ>uF%eH zgFJkpX%kx%e9i6BsWik>=(}58@<i+`NOXy)t?Fq$5(E`eR++LWEVkJYLVWQ*({06( zZ&xlfqZdZ8GfYET5p}g8l=5Gs`wcbUF;J<=O<B-9N@BeJL`|M~j@q&IcdAf9Nmj0S zjR|=ceZ;3tACWl|=4j1~j?K7p<K((3D`}%4@%ePT|6%W&f-{M_c4Om>oxI`1wr$(C zZQHhOOl;e>CZ5>IL^CJf|KFUdb9L^&?whVvy`SE-YFG7MYp?Y@Y?oTuLdqp`XRRRW ziL!ivJ5AVrzN()ADz`KgZPQc_KaD5nRX~X9+wULC!Gee?K%Q5{bdC?u@u972Hv{%$ z(p@bI<~4TLNepJHGFy%SLvig^=I?M{$}XZXC^G*TAy!=~u~g$e4qs^!Na?3+#|=c! zmbayzxZNsyG?((-3P;6ti!ToWl<!4v{-YCj4%Md&#p8#ZC8K`xyumT3i-y9br!MEo zmTWAT=5=qVsq0c0J>g>C3$-|a3-X)oC0`pMKwvIj6Kc1~A9Adv7oQ7eGT~lAiu3Xn zdt!0h&SFm}`u%oV^eirk9DTkJMS&;R3ueY?eXW6v%Zku0LGMYJl#?i<h%wIVcsBt{ z<mYn<F{${Lj<`7~+hVJ^=@>Raj(&LgcLwnx2F8FB9_`RiVUcXY5+*kl2QvDXNra|P z?;Yc(=9*U(XvmI*I+khrZ(3a3EV~%sr+o00a~n9R<o#U9FQ^(@o-5clqHD7z?eiqj z|5R6f;)TG~D$j-VCQspo6v^S3+`_d=t6{EzJgg2m>>C_6<e+$Ag@@mODdEn|M#l0w z9g4a?zB97IiUf1t@nJT1Y5_c+sp_FId~3&|DaBcCiD9mO_G*k|3Mr7&HhPM`Bv@Bt zOKm~()Om^Z{N#{@&+KEiD`&-RI+Qu5&@MEP*F`2t@8Sl(=k((=7ZF!d<0w#rA^A}L z-4-1-?c?XI<jB2m(Y%97N7~%zF5<_e$~T{~<vC)Fs~!t8)Ufn}MkL*>c?(n6e08oP zRh-pVVnI%x8-k9pXO4#`m|GSu>%Nv7-y1J8KR54V<95NE)4(hY_#?wbUy(JIxiq22 z31d|iHqA!-=g@#@OTgd)qW3?OmYtBgz(UnV-)(>H59gQ5O^yQk27M%foqfWaf&zQ} zzk7X08fRD^)XPWR*AE%P(_hQ$*Prk27cVc*_eYNh;y-WO9EAMm*R!^FUOvv6oAUa% z{r_DDxLW?}&pmCc>2B8ex;60i<BMU;EZLA?nJEqmttWmt>TlzNgOcPkZZh0Jp`f$} zZqRSz4OPgc!b`Y|9tQ;)jI&e&Gq0{A6BjWfY~aRKiVYVUokt+JAU$|ltb!?7CsBWl zaI@S84@7wP&PR$^XrTRo`N%OYj`VKdgwnEDMrkaPN1@IZ1=G_vuO@j<f|ZaUViIF) z5ob3ly<EaAccp_1Gy61NSXZ^s!tp_^h_;)Qh~2@yZ^)+kNQGZb4Lz&z(x*Ux5H3cZ zQ9M9mQBk5Cyki|#2{!ayQi(p6&VlTevv;I%MT732tV^}6B%E_Xwc?c*8fa<CRX!FA zF)sdNu-@{5Q#GNDaj0mSVNs7aq@@|2J|0xqnq3^|G)RHRSlfw5VU~2w9+TV#ykVtq zA^43meT-|ANmJ}Xp^@IMZ?XD$YF2JGTuRGcI4^rRCTXOKi{uCenQe6J=IdD?a#3y# zSli|>r-fSPvLnOm@iM8#Heb}OS4JJ8rR@mRAwD-gIq|~wDDS5)G+BBkJ6#Nx?Itaa zhsDmtMW>t;H7~9$%H&YWBP<^Jtb2)~=N;?rE)<6@NOP)qgro;!PEl6(A;Gh^EQgIk z|Gi?Vr*_TT3YTDE92qyk<(c;!$6j1~stqVUwM9@lYSqg<V^kad`9qAw{?PH#Hi#)- zL;z=Ajy8ZU`-zdCj~tw8sKg6t3_Kc(C}dJxS3}AhuE;WNf^yYr)5*BQ3XeMSItmJP z53edk2XwNF0;QU?uu2xO+M#Fz=(hjc6S6_?Tw<(MBcyN;6DQfGxd=O9C&#n6GO^e9 zlg*kvG2wh!4Ru_m(~vewu-aD#EuX{LP7<`=Z59{i%+>=QDuKnI2m!FI%$$_$*-~CE z2IRuD05UflFN498sb)xqbD!m*sWRmBTI@{Lz#&vW;+Tl@xc#<DpGS^Frn_jRA`-w| zliVNVp9y;*m><AptjP#R6F&>qk8USZAw32&I%Rc{PQ5ogc0neOwtg_mf}iAs52~dN zFVsws60qX2&QCe`3E*>ciC&qXMl$w+o0OW=Ol^cvCHGFrC*do>5TVj#gI&(@1AFnJ zJ$7EC_X%8kBV}GtskNaDl{P>tU%y!jo~UByGj*s!t=PVG{_%oJTGAw5Hd+Ku1=taW ziMzOzQQ}XvSNBY17$U!^tBaBiH8xMI^{eF)kMVNE&sQsjfXr<@>EbYDdVD+;}$T zz&7Ic7uQb7o6Tqp4=+}x1w4f(JVmFTGIFd!3QuF5?8At4_OKYo-VCDBYd3;n_s9Wj z!_+BzuCT0apva&Igkzr?e|0*dMn@T=C*KDz975IjhLMnp3(k7@pjw=PM)Ud;4oWMl zk(e>i+Xx<G90f9tmhgq?o4S=)X~XVZTwl!I=zx)MGRMm^Kkv^q5|9GKMYQVk^Vr`` zt;De0eogX2l^gq{O@KXQ9SAA_H72Bt6P~MT0kk%(N!c9(*0*tZl9LQY;#5nMWsY7) z%CyZNTzq2~%us}*QGSXR9Y#F-rT!H~o)KL?Rrdy;+dU-h0;)By>W<k|o@KtG;obdV zXtJ4ZKUB!C>g2ejH`Qp}FBYuTay;)^s@bnqZ&(VLq6~e;)M2*GY*OlzKrM+6PlE-2 z=KFB}R?MW3uq2)xKazZ8YB<iMRmfk*ba!iEWFeML!ik|O7lU)At}!pc(o`NE$mi^k zpQRUZ6K=%uXl$J(lSYbS59win#Z=oriQ9O=_Bd>0&<xb2^On67UbOzMlD19LLwSfY z_icbM#xPQ@H=+Cu5j8B^Y6zb8N~OI`(F}T9!7mjK5MXEpCoK_lU=3GfnT?Sv)`+za zlH9FsjHOzqtr>}=ut;z8+Y#Ds5<N_sEycP;7-1;-i98#A$bqm;u9|pj`y9+`-zJij zTB27GKB_e<m^8^o?4=3!4$M@^hEdiOUYuhdn*DWU%T%!q4WB~AG-K_?G$w*xtZ~Bx zdeD#RnK1IG{g?~d>h;JPf9|!H1JP)j*I0iR2^&3E|D&Tn_xgtTIfRoAH-a<{r<sFV z=nL1i`TE*u$+S@rXn7eU?U_l0e;t<^{`sOxXTx#~ihBtS)aORB*?B1Tmxq&lj6N0> zkPOOtu_?W2#o7C_ZqlT(92JBAlI2HwZ4(kXlpWinN7N4xGJc&v;PI=X&%ZVeiK<0e zlnwWn#t(v#nPG`+gja7$1j)Ewimt{%Yw?j3WU3tA%WKqF^@cxD{gKSr@>q0_wH}-m zQY<>#w!M9tvGO8S5L&oB)F(Km8yn^z_0s5-!3rlMkUi9N-*&j(M9j+1!{UNBT0s?G z$o|rJvocX$r~J44{W2ol5KK<2x@BLq2L)e5w`8$p%2r$Xua#?1%~q;cO)<_^8pI>B z%Qcc}X_DJ{+KmFUxNMS4xRBqTbxYgNR-9M8Qr_KqQ@yqhD^%;v=v3A1IQ}BoL;N(d zRx_iXG}cb8lAaodCmr!y69hPC<33`G30Vg?#ddtA6!l895(26%V*EkJ*)4Em{G8ZP zZ(fMUbT0NwD5;nx=$6y?2!zn(_%!wka%>Wb(0$Y;ZRbT^{@f^uQWzGULG;{SXQ*e# zT{+Qo&c1U>M7+}A$8pYjhb)hJHd-ggB))kX=t04^Ec=~e+W0w=;0{y=Koaaxh!A7> zJ9h%<a_x66d$N@1o1*@3;yzE?mtUzwPIRPP9dZKNdjTzSXu-H@bqY>Jmq~5-cSm;3 zWXPiOHZGJiml(R2K+Z(ua`GC4zKWKG7I3l+@Z@ILm$3JT=LUZMF?gab%g;$QPV2a} z3Gyt*)+JKwB3&kTiB1&o<gLu_T(JC%i2$#xeUod6Shqbq05LlXKZbR^k_2CqRabY% z9R{g{)2ZD~`=p|xq(vv%qnGeLBN)sv1s+H@t}`%+2mY&goY!5-NgYBWRrCd(;?&EC z@hBBDJ-hJm)K8qG(<UH@QfMCF@#o}fz1e|ZPrn!6zoVaTM?he==d;&uyy4yb?P%GE z=ilSS_+@v_`tVoAdV~QI!+gNr*YkP%$9#l<zrWXC=YNi`_c!17z6XOxK>+{v(TN{- z&vxI*k$rcMzrX*V2L1q-KS#sEj|L449v?VoSWi}mQ1`p;lNPrHefDST>l*Ft4GBaA z$Q;Qx@nrupa`+q`>-Omd_I7!%-WZpg-`+l!=ff~)SKpvrbr(vp|H6I|z0{PBPm#j@ z1^YY9<Bf#aYPv=upP5pYH1;ss7nfPi2jpm38TeN)@xE6R>r2>-Pq(Nn!mya8)+s73 zDF}}_c(#DMP`Eq4*A3~K^7kx<_n$L{eow#O<K3TwiBlI}eyIxA_v_~r_xA&$ihcjD zJ@WnT<9}|GyzLU)y&XOelm9-j`2WHCt^VS1?Epr|%BW2~_4pWT%cI$-28zr1Jg=QJ zXqy;p7P2STyfn!kP2{x%KpU~IYrGvc1UIvqY3Y7WFe?CO&YMsYnRZ$8XSh}iFL_$@ z2<gOwyIA@U*O|}`tT#vyQxtu1Y`;4crjC8^$QP-us~`-#ItXd4rJW-<3C;fa+5fC) zZ2U7-j^^V?Gu(3O-%jj^)+8TEHknJ2R+OWS6BUo*nY_jf`x!-^>JffVIhb6x#84p{ zVtGT>N}CG&RHK9-`2a5gN@}i)ri0h8ua68Cntiw{NiarMOljCt^yG*@l`H6m$GZ_l za+y`YC$^Hg0fEY9AAw<&`p#M~mdA%?Af@r*c`n@~arw#p_=UP|1{MS?BVS2;RVJKZ zAU6M~hg(x7`3@zW`_<LUyonQqn5%K3K}wnnN@={BuZ#)D6jiO5=~Q*51TdS;*{dnZ z;cy(5OT+q-D#|rQT7}4k3>!|ZO`PrFdSdoBwkR67NCTqS3FB7oFI;^USRmz<ITE}P z5W@e0SwS^M^$T8P%OMczW6VjhA?+2TmAP<#$`rk@;Qpp#>vC*PsyJmC8<afLwtQHL z-Ggk#mS9rd<Z01hRd$FCyt0ksfxh6CG>r)@7%YXrw96t`mji7O)>hFO2sdAEm@sHG zqlue}A~G#o0is6+U=kVt0)iw;X-xbEW>J@o{Ui^(1~IK&EXDxrM5xgCz-xZhF2O3K z{&*qdeeBX$Y}heF0Ut48%!!=cdPz^zGGyS>m48RAOEl}PG0+^btVObJnQ_A$(f%<~ zbz62%+vcl0uh%_yIj?07FzG+1L3=;H5!s+&v0upDf4{&GiC(ARvtI*MrFz$|zGV&g zj^M3N0|I9b7gMR#L$Cb&bHRxu)nCOMRg;cEggjIN3ejfIXG{5ctfeVpHE0wLD5b7I ziuWSbH^McFmWWqs|5lTtZ}^m6<Q8R`)~zh=)>f$?10Y3z_aKN43O}<X;@q+J?ZJ!$ z{Sgwl74s8jzh`T%vLZV0Z?lyTV10mJs-eOl5-?REAH@pd9VDLpCZl^DLWdR&=^>LI z%xQ$>LOtzrkRB^Zn#r!wl}PSGvo)cWEN3?QHHKJKJjs-twS)3<)AXDiM>zqk8dEgh z8u!8j*TjXV5Gl=<DGa&ccH*nvB1)<UF*SZ+NF>)$lC|m)9cb5j9V2o-ise8c$Ao|= z5$v@vW%ye;ip@xgM?#a@di6_DU0yx6=1segyP^wQ0H~Nj*JCZ~kKqa67+L5v)J{0O z++fP&G=m^}($|1jbgAJ_Jr$fPwBE1(A(XL5%>kgxZkk&hGX;l%(>T&|+^kU<+fi6@ zsYF6WQX4?p<YSV5Rj5_n;1ALE%U&fZvwY5H3%k^Hl55}F5C6>xk*CJDv{)1ln}$1D zHmXSnr#F@{m@mJ?g)VBCCYms0F9H2$nhyQg8wtl&!OEw;d?1qJm6<ZVxQ84a-(=0= zHTWnkEN_PC0L?hgn<nSd)Wll5&WDVxZx9Vngz=F}B_g=(XLqsZr8!nso2>fTAQtB5 zYAF;%MB90y$xu?aOgJ)y{i;@B?s)y2EEiN!&ch4-KJZctjtR0&IB29xF#ejk`p#}< z@~+#iJC){^Tzo9=F+wjQNu!?`m{?$&ZEG>tB!VLhToG2NH5=4MEnLz<tb^3&;(~Ib zGEE2uSXwbS_G}BY?Pg+B33DgEY6Ir>zJ`AJ=P@yg%GwGZXPcDnQA<@r!cM9*lW7nl z6(+-L>{wExz+;PQI~?F=8L?!7B+E6o9m#<>S;s5ty>4i!U|a5cs=*}+uEP)M-_f+J z?d!;3KH@q%ft;j5Qdv!=e9bYYn7#3uiT#+O*AC|h9-W%ofe$`LI|BpjOd2J{A@E#$ zVze%IQ~iUj>Q7`Mj<~1n$pIED*E?Q=2cbt%e2_Y}V_#$)@^9i`%<rLk5M~=V&LPI6 z3b6&&j+g~&^385q2o7C_9F_QUHYO0++72OUvKc&5ZYLLEQ1a%JuAUYcr_8Vuf^YJ( zf~B1R9PCp1Dm4b>$UkKj^&?7krbQ*1EV&eOGTl0P3V4oZ+Erti<G~&lC~Mbkw*r)u zBX-xWGWs$qvRyAFya*g1vj-iF$GA~M*9iGV+lC*39v(a8Z5hT~HSjDdfXRWZUJV+@ z!JG!Sy;x;&f}L274`)O6NQDVksJ|PY8;ye{Fb>DWti6JmEp&00#3f=)#U*Gc^2y$= zfLUpV2%MSl_vV{xTtm{R7e2#JtTlVZ#4K1`cr$^%01ZAcoVJ3w*1Z#$<OuoYkw$EG zaT@3wMW#j;%v|>8@3%+^s&v?|?j(hpy#>UfXYqrffv2EF{FD!<xYiiQMA&!asT#+a zIdvP(arEUfF|4n_orh|zVGx?pVa8~MKb;~<VT-8|)?}0Tlj6<koc$dEfi2`5*kDfn zqdGCv7lzf5#yh{EE;)eZ%qp~7V34fwrF{l-38<wCB$m2vkdiAa$UiD9ky2opF;y}l z7F8Q4b|ia8T)M`@)K*mu0%y?Qw%BD4`n0;^{+Ndim0ytNAAvXPhxuVi4oK_g#eaC& zC10~jqG17_H*!WPxq0=-<T)us?JoW~2hh?jNWjH`KX)rw(Vz=bI$7q$FA$@5$*>8U z*L|Egho^cTBx~mG5*9IA^7T|Vo9O~(WH;&nMIY>E8m-kv?e5m~PO#5fI8XEaty9zB zDj8K6G<y-FK3)Y?J-oP0GB*1V%FZw1MowUBN27e{D%TXfk{Y{#hzrbQ@YGjkII7Go zfGeyQuvP$6dt7I?{x!a4mlU<a9S9>=yCPV32n+!jGcGNBtqvMC71k|sW1YZo#}Bl` zSqm*Fb<3?=_7T=~WgMo6<`1T8uFc6o(N%PEnBPj}WZ{CB5I1dA=v$yrlWDk4VZr&M zhzw?$P%a5`PO=fo*tqsP4{St&aSHLQJ7xJ|(R6S0e<>fo<CbWK69**wP>+P95L0MT zVrCI*)|7ysun;hz*?*>-KImZ5t&Jpa>M!SuoWJuui;4Z)M{0a2{NR;m9Y8sSmK88l zPGVX(5&1IrJ~`*}X!r(20#R8!RD}rCjZG#*+c6O<JJ4iTsPoniZwSe?1L1G|gf<rj z(B*Ca6>2VJgn0}S4A?U1wboMmd&)=x<5xD->uM=FuEjbsPGsV|h)$WM;cljWZg{<o z9>Gm4{><W|v9eqFalEg-Wlu1XhAgBY^Qbje1zEix9GZ2F^*e=3I^#<g4>@!bU_Fp- z#3zthiMj&>pPF(|OH>r!`9%g~dcx*880TxVsZ~ZJ3()11c~2f=HO_iRS%POcu&>q( zTuA|>+iMgm8aYlu&h+&+?sA(zR*NsOb%QMihJW6EYs<|AdjY&(cjGYy{yjd==SSQ7 z0=p&$e4kFQ2mAefeqZ;q^9jiP{k{DkCpWvh-T!v@_Im%m9Zg3l+zJq0GUVldR}?$` zlMDEFag>wsicG}Mw?|;%`_(l1MPxwmTF&rzzRBRxaFg&cKp_AA_w8+;VefYIW19mx zfv8W5%6ER>;N%v*c!HC<>RoU@uvRAP{6R?$GmAG6HaReu+74@_m|0Pvz2E33{EL)x zVgte!1V?$>;27cc%%O@m5+!6pfzUsberxx~E~no$rkimDN~x4^&DOotiDHX#86mNk zBtm)Ej=8S|BIo*1J%h&<T88Z5UvIu7Vo4Ea3p-dF*C@%V8hds(|IGL7OsI=Bc42pC z)9}a!;x>^vE78m@VCIUON^ph+*J`^@tdydg62ci)TZ(5P(~`g}=ZYq3ZFrR0*qh## z6v<T7x)XVsHZpSX9Kwfn$A2G03jPw`K@u}8O=FjP6%(!p{dwgnVZ+SCm`;xiA+~q6 zplT}U<a`K8NulqMlVEs}eu{`6u~6;CUvJ=pk}wE<jY)O3TG=(~Wdc^5n&zJW<KQZA z@UNyE4@9h(2#qa*XWq7#-$Z5AbYW)qJf1cL&xy9AtvFI<R}2|Dsa8bZU8eR!mrj;9 zEF~rUJtb$z^WUs9sfF&*eZ;o!NOEh$(7Aiu6>gQkIEmB4kg{hns@i&b!yZ~L>9Zzt zL+hDd0#du5RZxIN&;ojdiRoj6=61}2=AaKhTltbYXsv{*D8$d@tc7CW-4rR1JITic zE<$5XZC;<+NS4uYLFlql2=sTu@i@d$UL<Cv0$kvRuJSMt?x>wPd+@GE5nh+%oSC9< zl28+Dk+{fDSaDCPdAu-%gY0CPyy%A?YY%M3M2e=aE~TM%*$s=ZSm@`n_MfI~p2?Mw zKR<0mPxdwnOlw3dArL&Y2?|v>fAEL7<d28uxprv7G~tJ!<jD!Mr%bju$E-{6Hz{Oi zGPVyy`uyc04_A)H`}woh!sfAbjZJ*|%JOja^=i9eV&rxmTc9w{|8Btn7LF59e3Zbe zRR%e;ka1erA5NO>qh)Iy3cn9pdN+k!-BxFH<l}H*doI5lj@9!b$F)ucn@!^x(H@i9 zj=0brHM2O;!a-+7--Xc}BsCRm<YmQcMpKrzd{O~l(fvjoGz5{MP6DCHZ5HCNN33AX zWr{-AkQO(y_(xJ;@>~swI~yaCY%UanadSet(N^>rs?+v?t2p>znW8;<Qxwq;_Ar$m zW-4}!An*-LmMa6#HinJRxL-c;mB9+`2|Zbq-wJ4TkXtuoHSKkJ_H2fwrwbqhf|;W( z9glnIEI)Ka$xlbR(h4`4j(J_x;VE4|E`pStbi~$EzRznt$F|A57|;b-OCije0Z5ll zvQvkHkHFe}!ZAc5+LsHKBJJKAFsNb;W7fj&-t|=7Ej+P9Wh%pHG(ulqsmJ85%&Wb) zZ41`I3yhOg3mzg3(lLKzL}=NU>S0D%d&IqYb!B@Lv23d|R>sVsJF?y|HbcOKJ{$x5 zeUsYW6&ZFqNu$KwyUx)3%^03yj2>7o>JdhYUSs#YxP`|uzQakOT#<0(4ihQ)jIWhb z54;yDb|^T*5_ou%XCq@JydN5D#OuerrFbx#gm(2pcwb7a>|!!XN;}3<NIH*LF@#iY ztmO?zrc$~mhp*eT?l!2Sn<OvhQf$FAB~FfyLo}h3AKE|%-bG0FD*jpBwLi=5qFiH) z2jQu6N%574(0g~5<|~UAhemy>V1>0jZ6&_W^vRR3mmu?B_jz+bv~Nvg!i@zjqnqw+ z-MzBjIF~C$9(1v!6VKa(IA!ioBJhJPUK2sVL&z@5C!dT78_(<tDl0lvnU`Ymft+S- z-E2B1;G0^U{Wi*&=44lj)fA_w+voLLNP^x7Y=&O)DF%B0pMuJyt;!h*`<iE~1^!g5 ztHF!BwyDK>Zu!(uoYN2&L3O~j7Rf=rn$Mh%rTDyC*0>5vF#|I=2*l{{Q>ep+v|swC zuVEoC=o%R0f-?eC3fe@kx(*#VM2O?$0Z@l-whq>iyT|BWYWVn@TZs-^B-G4-2VzNW zrD~ERoLTGmxipYXTXW}*+S9)Q400P00h<xy>Ow+8FtAEbmp){Aqy|q!DC~XFOwoB3 zm9a3;O<91E%)VW!ccbhB2eB;$T)ON8AXgZ?SwLo(u}X#pj<)ob&5H0mfevC;og)=A z*~NCJG_L-It;OHjP`(PDq8-J7X2B^=t6J*pn~2Mi%tsr&F+A+<BF3M2U$+)06~~|x z!{nd#-iM=SjTjj<duI-tox7g1y4iwF0xL(;dqo`&Yjx`B`-(@fIA!VDAI10Y560~b z8zZ7PCQa~gY0%Yr={eV@qacb{j-{=Q(1z(f$FMGz5kpCwWpR6rc}~wZw<Y516euKN z13mVi#`CxOw_nA*R?wx{ME8v|EjPXwv0(&CF%m++DsCfq*`@?uioq|*?H_U1fGX^f zg3@sgmPGZ=K;B0Id30DWuIgX@&v1jlL>N#o9gHR@igq9?K#!d#l?f;)R3ttdoYr}h zekNr&jLKN$aSQ10CAj^&xLqJvm&l8L+ID}z>G}#S60|MFn`XHALf5WDmE2e+U5NT| zH1gAE0gKMyMRNX94Qf~Lycp398ML@U^54qO#FTYh@itUe<{cEMy@WusjqJxZ(#*_* z$CbUt@uv3{JWn`ba}$6**&lV~EA<DHO+^;PC;Cz`m2LOE?O&F%t{pkT|1=ob(d|o~ z!mQ9jVmgdzuz#v@K#*=dZ>lm_+Zd|G<m}pu*K>{Xm5>X_0!9(vf2gBU9!W(Efe(b{ zQ|A2`oa%u34M{VAb~5lys0Atlfp;{rhJuJUcShFkiMrlVvb9yy9pC`d5br3lR9P1i zh#p!(PC&Vz1ZBNy_GJka%E2t)U}cus_ab6A<N^f!l+oBHq%za>fMkJDh65X@4_8?s z5*61!+5%McN+B#&%?6m=KNH<w8gCpq%G%f8Q>;=!fB!!ZxzOv$_itHHxMFyw)6Hl| z@&?0GPaOg2-8M_(UIs8+(F#$Mup&m-^YoUmvV|E);peR@3%Z)Y_BlkiasC+_<vZlL zp%%f)FxT`t>;-kXIX@urav4!2T7=8SQD*c*<zye=MC_Qz+n=GsH#iEjCF>V;wIInd zcJ<d#ud+<~?5^YWVnJ3}elTvJk#`!3m%hQfkK&$QWufINYNW(wfhespav(k=2mfa3 z8;^daMWlcy@e>V68%`Mbqx{ikXaYWJsEJS6tMLJYi1-^Zlge>si+jxYz=yI{W3KR} zEyt&p2Q7uF+J+97X^oAoel5HG_y7k{TIp*K<YJ00+^V7{pPFw`{Tv#C7gMcESmuhp zt6&g46<ra~6LTXTH}z~iKz1*E&C@cfF@o0b{dGB>p&{_~_1MtQ+w=AL@V8+;A(2NR zK*0C?mFV`Luh;AL<Fdgm5s`nupS!yQ0pxk)fWI%!uTS4wM0A1k)k}l?e8Pa=-(6#S zyL|k9@1LKH^MCxS!afW3=X!P<o-UqmksXLE`0s+NZ$bhH1p2dn%@f`3^YI0|8x{Te z2ljU!%wGh(CQ8To2|6Rv?N$_*mB}(Sa{jC0RQ3;M?6DSJ^gySKX+TPmoon+8@gyUv z1*4)clubs97~E;U$<}oC1j-D$Bqz*eg$UbqEf?`{G9N_lXcrW!?*v5MIY%jEh;5M> zJe|$hnsUPuF<z6MB*%8_-OM+`;3R`H?~rDVCST4sM<;4@C<}OXdIyPeNSmTWGD01B zK3UNDhCd0YikHJ;rwwd(nHo`@kX_Paj{`-uM+0ggb#h1sacyZaF5?!aRD7Il`G|8Y zlyJ|f>KzQ*UVU@}8Y89jOMS*{J31mZdYRW=TkWtti~A3o;NwudORP26Op?hu9dU)@ zx`6i?N$*OJh?KjrbN{{B<1g_v2r-~gdyzEUAQt07l<T12q}^(;A=?`?&LGe%Sz|rj zT}~(k%!HoUu#&9-1w;zf8gV<j4Vcw9v+JD{$m~2nhlYrtD>9<X2FBPaZHs~-&?d3{ z;%lX|;*U7cffFk7#qg|_OuHTjuJDWJH<&sAyrQ+;FQz~3w*MO9Pc%qAK^y*JOq;ra znig3kaZ*+pBBS4{&W0P~&Yx8h#kM4CqDi~uXKQxEevfd7BSfWAGsmzdGLKZT{MkrB zjG4QPiyIQWAMVUa`Xk?BDIWc*+<B>qb_&Rq3MRT=GL{86x-<1v7X3J`aJzp(U4Xxg zFS45u(#ue?+qTWjjh>fE#HLSq$+w2d+>X464Z+0g$M9i|?XR<Sks}rbyl<U%Eh^b= zYF>rXh(oxv>oTRCRbeb|k%lWxmB*EJ*v30%TMUu5G4~}`?oODZ)+Y2C&AY&$j(A)| z!#9cCOH+dSH=6>AkOK6zKEX$ykdeReFhE&?+v`?-<ke+MHUGPzT;Qzea-~&{HGRr8 zVsBvG=oM*IaFMQjjCTmU{uRdOFuH>U8g@cng-dY7Ws<+D0H#v57U4565`a`3vf+Cf zmd`va)P5Dg6S&1<Th!&1eBm`S^E~tV)5@}{R+W~H6rZv_!pY`QEM{3wkr26%y_~Z3 zaAarxmj+ispv=QjQR@3dmt{6R-0iW#@TQF0af+gGy6jJLhR*9JUz)|(;GZUzP}BIb z5yjMEEFm(3g9@O>^ei%yLr6PW96xY9uW4ffmCnh5w%Zt}qWROTZ7t-em2$i!r9|<` zof_GnuqVPvH|J$L_*abhEE-q5ZV3M5Toi_){AYiH3BW-^9XSh`Jgn`8UFl>%u}>9| zE)5HViZ}0?qD=jOU!AtDr`hD?nn=4P6xe&YcIcupzo5+l7Kg7@QG9;+Q0SL)MtIe0 zh}fBdTV&mhJ@%Kk+wLhOs<oE1StNI?sYU2^M!hU2bK!^USMYP6qYL@|qM<Co{48Kz z>6WIrXQSy{*Z;If*ssKbw*}jrMpwB*1NXXdoDUGtaf_b>_|HpnL-X3~>xoFTaJ_g< zc6?r#C&u$Pr}2%4@8Zzem*yD=4?Ii~+R2y^UPq}cOO$p8Th5rPKhRuvEaclP;^-T- z;s-8L9>AV!>zZmBi7*q^mkXJ_9jM4?GwX`#*NJB_LU-o#x3ti3p}H)~&bG%w4!-$E zs_Ss1E}%#GtTu_DTN@Ogdm~@>ME&(8e1AL)x>D(yi0)A?_ZvM?Cz(W-OC``K^JNIe zX>(HBw-rRXuj4Kjm>Mc?of;%CYbKj8ul&M&EvSON2}qcPJKCo!nmO9VfQk8o_!REe zUl5=oCzN%KlT+eu%bZL!vvo>%B6gcOSa#%sRFZybZblAk2-jv1$$0>P!RjNM3#!y7 zAp#)sSYly~4Ybe|I4;mjs6P^F#hQRg)bNK0Ei?OEptA5D2!B&Osv{K5fP#%@g~oL8 z41$>Mt|@&hwoT4~3Kq$6F(FX~4hg%uAS7Ce+@x-Rjcp-iXF`W`wYVb5Qm)L!z3;Rs z2G*eF?F&)xS>XZiSfr_fMGv~S7BJ&MJ5M&pp!v1}JQocg6~k#!Up&UrJ1CuKBExcB zLdl7Z#xNf!0a4d;ZKDWvlZ}!S4rtp_-%Ss|8VABx?o-!{YNd~QnscHE$y*#_GQX%1 z4W65=qH!cB(*Of@KFho<G_gdbCNdd|g#GLAD{Sm|9Mj7-WJ*`=3R66|06)b;$RH6< zBXpDdp_RhHr%@tbuK+`52|~bCfj^7sHWVmZbRBs-j$ZRPYi#|_>VV)#DZv}B-Oio> z+wkM98GqB7O?TN|#1f8@CRgU5kwm%?u2}u^@&}l#G>0|;>WRACSE+i~^}Bq@F4^Lp zRF1N9C^GA8;x6PY^>yFAYHiiU@5J*@L#nAEsJOn{=?RKDljS7w#hGO}`GXBJYwtuj z9J>71Ci~Thg=;<?B}s}nitOVn%k{Iph?i~`_YD-*7#Qv7t{@d~F>&!<HoiF!%+U!P z0Uerg!a|>Sol+#5x!3-0TX7VaFxsq9>?$Up#Vjn`rsfmc&bPS71j{$Bw7dx5q3?Q; z0tmLFjmuSdmTbMiTga6=Op%E-oKald3xGVP3W!qeP|MuHD+sv_Wt+Z`-Zi+kE%CBs zaJn6ML(Imqg;;BZDnVcA>u&Bac>*Flqz658S_K|NS?4BkM5%F=2;ekMoWNP*3rZ&7 zE7tPQyg?L+iy~akn*fH`zuyb}C}P$7RULgna}}wSXks94clNGqg5Wv7+^^aVT0=~3 z^moDsh&i&ZV0K>tpHPWn-pKd<kT7YbzB7lVb&d!yP}2O7Q}!37C_jbPWa;bxvT%?i z_Fx`PZ9X7H^;oQ*xpiLac%dD<c!?CvqQiHwD0dkKt?sem+>$1&`H95r);J5f?eqB> zZgm>>z32LB&EQz=!P=krGWP^bJyXCXwmBxr4BAQrRI0rrDvTM+2S+67TE}c-i(yNE z;~#=Xhc#{Z{cv?=7z8~*|Hr?0CgLv7*Hb_Nxb0%qW#F$3T{+ix+tzGRB}adIlFeRM zNU6jjuJDRf_VUR~H?-oaG;3VZvovPK2DvG^sW+}4BZzsME2%jJ*!uzP7CGXqO(Doo zrTqKZawZj4s>{w45iGONp>~Y3+Gc8C6NZbNXX{dc7p*oNQ^Yva625ZSg*9cf11;}Z zst+#3%4BXBK;7a7^Zw-Oh8l~VM}PF91)Y_9;9Y;({K4&7HaYG|(hmS&w5RNEAbz*S zh}zGX&ayZXZF7haGU7ofP#rUBh|=~XUl<^6l^3ezy(6kNNYr`wcEjIlIl3?e7;2>k zRHU}2z*Z^Q^K}O36uJTvRtK_(J3et_vhXihC>5<e#G;JK?>c3h1Mmk4Dy73$qCM_Y zp((RFEOJ6&mR@E~*7ian??zytEG!>M0sX3GasFWEF$-IH5GYDt@N19FeMg8}SE?b| zlv;<xp2lEpOY#XHiNJ9)bb(CS_M%Q!uiaF{3hdZ!JB@q>7}@3-E*7g*gG;7&XY?$f z6d$jsMZNxT6U{H+3*z##?DV7m$Lg*=iBU1hh{w=~rkf6uF;hwqcQ!BLxcGfhRmnVv z7OO)9XR%AUIgqvv%LsFnR*F(0*O<N8AXbY&5#DKu6bfgMf0#Hh-qJG0>^QuwW`w69 zw7GPbAKVBRq8HnQJMQ3U6Iy8m1z<9NhiH!nVvIOyoGG0VUbrcZBDkv+fLm&&bOewl z_q4YrIgvBj(LOu?5}870TVo{7)e;~wYZD>}k`cH%*V@G=<K?Sr9J7C9qVqCR%A7I+ zagpigi`~0V&uNcpmV6U8+Z7+Ja*31YL0@YVCC>0Mtg6%l9j}sa05if{iBMJyT9HNr zUXvi8E$T{z+qRN8)Pm;vVEi7WkKGcFTOUwE&NOGXPdf5IpxwHqp)~oN6O7*Uy>6>U zj7r~}8mqpReeJD_x*b02q@U-%7c|?4+w?+FTb>*HGzXFX+pLV`Tj<bdDM;1Ta3R^i zZ%JqTJO}Poym$L3C4U}{z&SX{D#t<*Zw8?a!_qabL?vKh5uuZRJ*phg>%L2ZS7JI+ zD2(>Avr9bOa{ltYQz{cK(nBd9Dk#U>9R@F0yaC2G@ues?B$4;XKZ!}&;Eg01NWeXA zN_3FedGqTW{5)!nPItED>3JI5`h<ExFFS$=xw17o4};k>fBL5$JjD2K8p4v_2w8l{ zcaKj!lfBuO$O=SX)-(}U7%}F4K%(WLJ&pidrI-$OEXmw`4!d*&P=WpW#2=H?b*x=& zr3?8rr*or26NSC{8gyE2dmWmHxA;fxysfCy()=2B6G`0vTrc0w2X<v9e8Nz2D&msp z|1vzArI2Y<iyUBhB6osn1cpMy`XD=iv$eho_(=>vMqN1jWXJy&TK`gpdGbK7%WY+x zUwLa#f7}~teoCP5hn|vBoZX<J#%h~Ae+^WGJu8YG%;~uTe{D8D|5Gdn(LWZL%AJ@z zL8UCx^?Qy1zEY?sraX(_H@5luexRgV+zL$5{VU;`#z&!93$_#>L%%uJK|ZP%Ve;*& zF69@}^Mq8&?qcNf;5PRgYf<AC(!~5fP$RdUg$B@~sY5M)EL@AU9;_ZS3fRQxymkx% zm{%UdI{bjO(av-l`qsi&!|_AD>p}&>I7VR&`7%TZAI<0aACy+iG&8MB+|fXc-s#}e z1-6aB>IIW`#cK0%mlGV(Mbt>!DLg4j7hP;&eJ^D3(s<EPG(;ND3;>nL+(f{aSXX~L zv$8GBNojLWhtT-XoSuH@&^y~mMB&++i4yD9EkhnXY|)Bl#Udq}5Zb$s7#u3Tyc7Vd zpk6eMykP4JB5^^h$>gRSwiit43L_fzlxBPr6+=A{-)~xcGev<fBf&)Ok5jKLt270E zaH01OCUyXS&G=7aG3Ug2FZ4X2ad7IZgrT~-cs>&Rkx*ha`%sP{cXVL2k3d!;`_$1F z&2@!dM%C;IcVz79<;VgdS!db^nCfB~Gvr4)-kw4)E-hH9YA-5|mWHu#QNoE6Sl`Li z!CB>m?+&))L)`@61T)W&r2Pzc>9LLEs1d=MN-){IDJz*NG)*A}J2K(cAa%i~9mdtB zCkVfHoiU77$(=Vqq0)(-a4P$R7c3E>_lClGSochyK2$-1Ffv`GgH#A%Y37(10!S19 zrL5QN)oScFO4VuwiI*%PEqQ!K+0YGou=jDndo5Xp58&c6CLpJRcIbGg`{hCyCbUP2 z?misFX~mT|W&r3)yfJ>pUp>4OG+jTMZ-6y&yv?)YRoGn5mMB6_X(y4US0|<Fa=a5S z+V?Y?nUSi1L#$qPs1^?un=Z~8SuZ4(^myh9noBDHm!@Vih&*>r-H@P^#?y~{!bjZ! zYa_BF5X&ZAx%i^U4AEfA;|tNR`DIL3^lNTAfL}~%pCyLUW7D7buj<C_UdM#=XR2x= z0WsI+gOAXr84@rj<%b0H5+fFpaw9mIfIa2#y`!p_R35%g)*NQiODa0{<RqdYc&N2o z;Ax>`ETh)V{2)eq`!yGI9Ia1fIWC^vJ+<%FxkN*2R0n|#P}y%Ywj^3^<`*)mHOH2f z_yijs;N*9<pK{D!XRP&i{s3Ca9z^#bKPUegN--9z4`rg#73}h6xHLcII;QkzSJ$lT z#>`My{%&f2q~XyJfv6aLdJE;3$@S&}gv&T$A`#ppsC)fL&%+Cp>k5&nsoZpte3^zf z&t_%`a+Sa(DR_OZY@0hMjBdU1DB$cUDaC92@FivC0%mkG9CXH{sM9^n=c*%m5uxrd zn<&Tq3PIrrD)EENs4WH`1X$lFsG;O0_~IwITk_#!PJFY$eBM(<`BVR^eHXPN@TT#b z-ud<?eJhOQXe*H*-5Uaq<Tx!Kj33!LC+#2Z{Ccm@KQj$le1W35mSC<joZFh3F%mPK zzh-Tv2H^_M1r(|&k|-Iu%v<Ud`t*<zd@325l}q}yeDME<P+=Wyj|p>%jUY#dnR=mm z1I{nPj^yP+aHzJ8aC-rOznA>kY}AMVDy7Fz(`OT5iSgn@_7j9;0-1WM<F#EtAek;H z&;n}4XF39%2tuVRIX*4GJe&ktI6PVRJVMrC<l{^5$@5YPjw(HPKZ-xV7uqgj`Z6b( z+fJ-s#CNr~Gqpw&t}7;MR-O0<N^Om5q`zCpk~wR^hK?I3vGYgLtaq(BKVM+#)k;2b z*^0?YffstLW1zQ82Ej~oAk64jrFKFJF5n<<-o;xDFSZO41_Q@F?9gbPCdl%}Xt!?R zM=m9&F9aB&BR*uM1|nem84{)uf~U4gj~;%K92i=dTk@Zk(GF8qc!nT_I}}B}`kWs& z^1TU~haEoi9hf6LOfWb0`>J^a6)|8ZNOjXFl7+aS<6E_7mYhSoOu^`&@M2M3xsGIU z3Uv`Iq#lxLD@ca2=(F?{Y~%5)%qL{uZ}1gF(l~RPcSgfzBUa9Y=E-~nniv-r552bn z`44w8Z>~OTFiMrM8^c+VCOxW8P&C1p&_4z;Y}VK4$s!K^6zm?)c9XNU(5`FvpPD|j zl|DDbyTeAOsNjkZLTV?fb>lqn{F?u(RK;h$^xMU7!FPoPVepxe`4I#C&|Uf3BBTw= zkCMjxf~eE{1?It@v{hc*2Mz(XgI_kE3%>eLKj<)>NIIh|yb&k!Wrwe51xv-*5x2=r zz$6RJSFyeny0w7m`K;wIc^jM7;N$%oue{8!-5rVap&^f_Yxr^N$su9I$!`~Vk~?cj zPX!{xtF3`yo=2O<Z_1lYQedS^lZ4|Wb`W{%-g`v<k2EVpmu^IBWe1d7^0kx%-7|Yj zff5!e)%cD?e}DcW^~m{_(BK`?b(89odA!8udycfvqx#aO_fQnU+Ui>TpVUPu1YvaE zP+!;_<V1+c+oYY6yCM6<(K-~!i{FyfY_A}##XeS`Dm1rqv&#s|sl7O2F&*;3{F8)6 z{Lz8=DR30(fxl(>2}0)GdEJJg0DjLA*`g3Qhi6H|D?_tyvbANI!1El-JLqHwo78nO z?N|)f{XX^lA7$d?qS9a0-S+bSykOOo9{_pb<+GGmNO;0yvkr8UJ#rtK1(^@^M=}}w z>Le<m4%}j@V@z+F{RtwMr&*iC#uv;Vw*l1IvepVIlO<4~2fN3??xeW8(QFs_gQ>X+ z?YOcG^i3gy<!Cu<CMpwR;A$>Sv?D)I<DeLkUsfnngdlVbvpp@=iLsOSl*fK9qVCj5 zM%=OEOff$n(Pu){eSTNm1M1GK%&Bol*l>mC1ki{D+@;}R;2Z@I<tW0mEM(8UKi@tZ zD<zm^6BbC~SG2+_N2fWsz{>bymqxEL@{@5G$k@C@euGvyNNda}%P%T=;IKb{?K%1u zl*oUFP{WlYm+<OSmXg+Sl2t7Z;weJTR))Xs{Lla5kClm1qg}hETfrh`iIf{f?D$^} z=U$tc`U3PX8EN%2^aNk@>~${0C@lK$-(8scJ({QF?rBG#Goi{19+QUG>Ky$Lp!e%y zi%BCF>tcn3x=u=jYz`T2*y0AGr-O==*;$M&z6)DmRh2H)vG0ha&Lwqyg{VEFZ2HF_ zbw|9LoO(I%z2^q_Rlp!we0j6{pU9Ge5PI$7@`bTd3RdPzDxuSID>G9W5)D#<3%ll~ zyPqC;>+eob30<L$nO6KHI4HWWPrL$Y+4xm3H_ck;BQV6kQ&_o8AID!=MIjK6<0;=` zz0(8^-MdT*(jnkY4SyPSw3;xtmS>uX9olN9ic;s%7FkJsCE{2y^p*V+v+D7<CQSfl z#q49j5?7WSk4sRR^iv<$5QiH14%>G2nK`)(W22<S;B;E-qYFR$9V7osxT$#a=rKh* zM*U^XI%&RUMTZfWO3_Da<gd3^z4X4OMXylnT<+FuJ<@mRG^AH}B|?F-tj5WlO0_eJ z-nr(96-#P`71pm?ff3y>o%nC$GUt8IaNm2$FC+Nhy}|UqDh8KhUT18$f&q_Aw93dZ zc;kw`Fy;BLWiQ~ST%hwRr&{$tw1KhrJ0TIm8qD$Pf8n#13FFKU4w=%Q&(~~9&n19I z3`m5InO~F_Jjmj7|I(|?EqLG$rPx~YPUN58>$cR|!P_TBM>PCzvJahos9L&B6jvBo zKSuvNB$tkx&h7B(!!3)gde|j4#I4Hhh~Uw{sc+c}Uu63WV|u+ZQaH|OO^wA<d}=WQ zv3FJ<Dt==)zKN8)r}j;?t^6I$+EERP-ZmI&HLx2Cp=X%!<#1|faHIB!sX0dzW$GN$ zGpX9_)9gMi>(%-68X}Uoc-6-UU54aCQBbN~AuMZ}7HvenaC!(NY4Zf-9>%rV!hyIo z+A@Afb#dUFj?XKY#&ZprOcfuzvr!tBWe#f*4S+ijfto_dels#<T3RChMSRXs@+fz) zE<{@*;DeV|ujwo&EOFkaH=7}}M3^!pI=e3(r}H45aSS!Q0d>B8N|0?(dcd4Ejz^NU z4^eKAo>-QTeRbsB3-iFgZS|ZsW60mmQP(N&OKhNoOJcz-H?qf!gLlxuntRShc{`4y zM>WZDa!YWt<T8kZ$b-B;8Ao-VIO9s6TaoJMY0iVmP^KphsC+wFYbCJ8;3&??azT`J zIMlMv!qJ2~+jeI;&*PkVYlm>xVK(2yn7$X6!V=MTA+y1I7}YVF4iVnA+C8`PwbQ-H zwwEW8u{!htpQy=l&ylUxBaL{XE!f9HrGA~M8ecKKSOtaT7#hAauU!}o%{hO>snB(> z^ks5A!Cb}4esCCj;MnH@fRwZ@KnN4j3O&4S1v#Qb9pM4&0~d}`tk~jHCRY{?bmGS% zLx>j_!1d+o!Iu7HD=ek*2*~mlBqeE>*T1_LX<sJ69j08&oc!gS4m(`=EhH^ps`8iF zreQ1BWhiTjbXe2z`m2cz@&`uRPO1FuMH6dn+zcQrej8zXR5BB7TW2k_Fq4e-ZSt56 zCOnooL`7u#WcGdWNkAj&55k-fZYos#rNM!&jbG~LTct!)%n;hNg)0NbFZ8NoR9Kfz zJDHuwm=0-W&^gshjjGm#y#j@V)EsV_8!wL<gZJNlDE}U@dS7}iq$2VoTED!&%a)1b zjxDPXh9S^LCUXq4RT(g4>SG$7+Nr#lMdqZ_CJl#s$OCPMn6qgtSybUDR|a&txv40k zy+BEw$xfO=jgc}ENWWNfWg*Cowj-3$1OI+HPnp#ITPI~p7ju|hSpE4M>5#<{&N?+f zz~YaiH@_NRdb_=pv`=KKzyaR0z?er2gYl<E%CtQ7#yn9Bw_kaVv>jhoaR!Hf!IvkV zMckA*Ys>RPIsZp8xl&B@3{Eb`rZQ}Ft%CU2c7w?mq=kyQsr&&f(+^JakI0B*7P^d< z4~6g7c-+?lgb?qq<vJzXL;-V~$=27MS8-HRL;<Ac;;|^%wc~azJ!(}a#j5$#Ui>cz zCVCT5i%6z4w^^JqBRuc5hkeU00e=53eURDjdvP9(PePUt-(SZs8ZlpH?@CI_AVee( z4-+x5x!W&?HM1!MBQ@nG`1<>{y7d`QUl8P4m4~qh6{eIHpTY$k73iVR=Z=gKaRp~7 zyML-QG-Ll*7G~jQsWs?piC|{=Eo?QodFCvB&+aHmw~|F_Y>dOzkW~b}RN|T~xf<d- zxA5!_A)beRCW3$fMsLI#2#UwS{{Z1sv>u@~8G%iSYo+TGYV9-ryd}_H7Ob4SAkYjU z0|$`b=}SKi_~yfL<oe?LfBA5j+5ay-991tzGa!Seg0Z!ki7Om~ikq?P|5&8#j4aIH z7&NR*T`hs^ESzu*l4e#Gmaaff*6(LwD_0jqGiMP8J4Xk5Gy88V9D|61t%I|Qqmjus zo~W6-m5G_Mgzz__oRNzSknKO;REA@aHM6()24v#kV&!52eqV45;#Rh<X3jtcaa$u- zGf^`W2UD~Ei-E^;>uML)RmE&i;4Z=sT1z!iP74Ddq>cft=(;cniGhLQ1W0OK@N-z^ zquYH|hAR`KDnpm1vdb>tT&CraN@0~-W|f0wTbVG`-|Or7dc56V{wHUIE%q%l66h}w zF???2<6xhk!{7hmvz7<|`S0~#5B%2y|MkHCrw0N*!j)wGG*@s{>LrxE$9^J-`(y|t zsrCEzFWI^mvVNNgE9>j)+nJ9|EmrsAS-G|viUt2;bv=IO(NSn?+~-%^UVR3Kg8%{f zB}7V3i>k;&m8u|NlnMm`G7vK7<tKwUBEn4{8ZiM60&>SAx)<2|JW>+wsrJ9%tVw&H z;?<=Nfrp)*Ow!>00dbiup@>iyKq*SiVJP{Z8MHuZ6{CRWI-yL3x4Hi>Fnglb7ERzk zY!Q7~Ek)e#2_fT#$j!!JB=y$1d1>;Yegg%eDV?fK9`4zvmEq$3FSLou`w48Ze?ETv zV*JwgAkT$N+pm~Ku%`B4h-Rh#fzgf_ezA(ry)sI2i|qLiuBb*kZ}0UxR{ak*Ki=xt z_;?>KVrh(T{0zp;v7goHVfSn~4=aPUW9uqqB7R|=)IJ&-XAYd(=_G!t&ta18G8$)W zsdV42ZETz+SbtpW=C;-e3iphPv|-zv#zCjNIhv&Crpb@3a$)#C0DwS$ztR1MYnT=% zCK09)!PINX(&wW2@8;EqmKu-+0|3AuGkdKze^_A5(>U(xsIJbAFHRchXiHox$)4_i z^cNodKEZsE>8|G%@l5vR2MzP~)esjR@s<n;l+R>J_Y`UZOAM6Jh7b$@0DmN#a->hk z)@~~5(IOah5Im}~`Esqp0;-=3l@OQi$m<Ng<;1OuxbcXWc-}rqe}CSWN0d9t^?IAF z=LvPO&UQ%Ay5YFad0LlB;wE}J$+S4bIG(`|KX0O=^VU+m+2KGS1^|FZn%!>SSZMY+ z{VS2TK-Kb6cI~Hmb)!ORre>($%B_7PT|P2^Pp7>@eL3@zq+~B^cC=_^rjEGriNA6% zLrFYXe@PL$7wdz{j1xn-Q({Fi<$6YmhGTBmTkXFP**jm))({sq@!*XK77p^{yp$ka za<F*>Q#&lMrk@Y@%?yQSv3_Av&G1leuGH4`!w<NF008i4>Fny<bMC^-aL#Ri%8*(a zWu{jXB@@G?&tzN}=35oTHpg%*bcm#L&&8=4^3D0>sj@iXrO^Rw`a3+MJ%it}DnmiM zDxV18%t&e&5mrn5rG4U6LsC?;vW(MHEiWFp@R)BE*?x{}KTmwQZ=}h;JSdN;Xy7%p zt<P1F-Iaa3#q(3-9o@Mtea?L2mHz(aQ7q$UhcvxB%ZW?*m|(%oO!JbXZ3~iX$=7H1 zo^3AH*{z*jNQVIc;NjQN(Xr`R9dYL^$ddjj_v|WDwY;!TFegFT-&geFe$$KlB;i$7 ztNq`Ny_c_cyqHJ_%Bt<hYxkbkKgHmzDx_{6P6`W+ZC7Ni{~J`S9UmhkL0sa;p@Syb z!+Ie`@>Q{A)9H(!7Ph>Qt{)n$9u}n@7O5T{LiO>eqI2f~-ooXnQsYna;_I!}>k12P zj*j+@&iD$$kWk(P2LFrG-#(pCHzc6uZ@Kj!?N_YG(O<oa0nz{f@bI$Q9ABPnCflpu z&Q{Wy?Am;4W3GHMQ$906KPFr?Hb@Z1Y4+w?f1Xa7?d`)EeBx8n$BRWa7OhMXv-Le1 zTh;Y1ogM9rQq4#vcU&xQ#UUzF*gSQg(m85tPsIQq!NU03zvVYl=GI?5)#^Mdxw`3@ zeKm8FjVlVipA{z`?aw|a|1M41BGQ|2iva-Op+ZNlx3ks6h5JmX=*uJOv7!6{e)6%A z>Wu~Uj55QWj~y4m?-d}Uv()~cY?s2?du;5b51$z$B5u`m<b5)TJHU_k>hW*OG}fLU z-hQn!PkLc-n)I(nnuju|Arb2N`vjgPMw=ZQ2Lk}W!x46E=7~8*vb&T7NS(>13-5^l z-m+NXjv{?e!szM@Rd0W3U)PFh`@|6()4g}IMzf5)d_?2I<jjh?1zDmU+}|>#h(3gC z>)2JSfA?_RBnEF)p?!Y3ad<#=R9VBuQZxF30RZ4WX0KPSUTbgfX?~Z(Gk<<WGb&u_ zoEEgVlDKg;6d3bFchNk%Fj?L^Kt48rMLb#2yrz2(YL)u7UfzPqF|~&T=KED9p5>+~ zQKByj8pklCllJK*2UfqBt#MqviVzF{0QVAKyt&r*(`#PIZkm;(2|TUs$x&NNbTbcq zOSW}~T~{{o6@F5nx~muM8z3ZZB9D``drunz&o$h8YjZHiOgyS4Fl(dB>hE_Eb*{;x z*5@iGgbU~7v`&pytUaJi=9(ipEeOE?0B~0{Eq1T-O*8f>h%2AC@#e+L-p-QuFw(h} zS^kzL>*vdJ244@JUrBw>!;r(_=ocm?p*1BMo6EU|NUr(bBk#L;D&oeT7+o7zUjKlK zi++{Od8#*4<>O-2gZv~fW;U))myvJI&50M4X}+zy=;-Y1L@d6g#4Pr!4x0@{0RTW( ziEHyT&qWD`Mbr^D;ryi9`SGHpQmW@?pd1|@r!@-_WMnssxbtFJjdvAvNTirVDROHr z_@8gsT%^1AfVCirH!_GnBv|okQq6-dy5+^zShn%=<Mm|k`Bx4!J$ImWN@ByD6vNCY z=?iJ~A0AS97i;htqm2vazOcV(%Sj!I0s#KZ^tKN7lbZLkm2)yoy<It@1BGuN)Z|NB zdLDP$+B<fXn4eFqCGLDW+jVyJdJDz{^X?*e5%S7WE!kh2A+boB9Fj)+y+__ssrmI3 zIq{VZ2<H5r4=`7+cC63Wzn3ka5>?~e10qO90+izes#j#l56jxPhBo42yg3=_PZxOu zImAm!0;~U=Zyx~u<k%YH+eZy=q)BJQDnBi_FgUP!cS%#Wz~WMF?ManL|4yO2#i!Kx za$?QcFwxW;?MvzPPcXQ|i$y%>{{>`g`CVPlNIK_@l>1aPyqYPCVj1tfTDIBjALXct zcg@UL!S7$m)33;swy({S##GcVNu=f_$Ogp8pNef99V{X)<%{+SQv}xiHLWuuc}X1o zpC=6U^(`m<GUCb#!8XbO@TWmncN+`!#JyrsY|Z@rjqe;(zHvaMX}R3<YEaYJm0NT1 znNY#fbo~=i+R0HhuP4>KndMw3$~jOfl+!DOA|CuVGw!0Ln$CH9#Yx5%*83NzcAc#w zTTAZ4JMHY;TiiglRD5<+^PjEcol}!cZM_4zU*xIR=G4xJ6pRRw4r9okVAc(a(9F*= zepCMalnCLncwu~{v7zm%&eq=5y_>@yFE%Ym;tpd-CNovm%a|4o0Dpp*+uj~2xVXDO z^TvMF@&xH$GgT*~7Mr6-JNh|SQ8u)WW{Os)NuG$5j*B-e%(1?grOM_t<>`O2<4^xE zZm(bk@f57ix{Jt5oRPO?Sw%BjVDu=hA9z37hv};dIBz0fmj1oOa_w5jL-N=|OXKOw zbk;Jxt-A4_@8s5!T@~b;bFXG--z~CF&1jyHX72A^`AWPf@O+bBX~VcsuBz?YA0`BI z_12Z?>gSWRKOo@+fIn`AOY<+&zhBt+ex7PmzHUv1+_TKw!}jEjrFyx=k*=^$h!HPJ z(M?Ke8WJqra7_RC5oLk#%56{bW|nwVgp%y7b}78OQBuT@H#e)UTx9wnuXcW#<lbYA z^9)t3<g2oGkJRfdZ4dpO>f{<7gBhHcGN|CPhV=&(V*@#lhbf=SZ2F|+^0Fi;aXGg< zsV26<m?O15y6G_eHordEI3-S7d*L$vJOSX3C!1$>D{I(wu4&bN`RAt^Gel;J`t_Z2 zo$0OR%0Pei_7mF4@#?{z)lbA}XXMt6_2-6HUgQ|t?p)_@%U6tNs>qJ=1krcq@7g;$ zdYWt!AKuV?s`Yu&_l_ucpR2zwk@w4d{oo))=-GOQqvH_@RX0*&k=bOwN?rJ`x6`F# zLwKK^b1coh@Z>(jAaAxyQGK$YWp2D=SApiyio*1zx}3Z4RFwJ*)q=lJ0QkcsxfEqC z(bx{kE^RE(_?|bseN@A{<L(33tM09ZdhNx3?k%Yw&lE4sHuZ~>4P(gr2MYdvOtU;i z;$Cj)VfF4+q@EO`rX%lBxz&87OKR#-vPoz$0+Lp{>!>PT)I!|L?@MWrU1fv9bsrs3 zJ?ed?*RMaYTu)reBhDKZC(Fr>a|3<3ZyvF%K0vL=pdJh0^bM?zVmD?>tz>KI)<Wvx ze9O)mSeewgeFm>0OtS)jKZ0D5#r3ptZLY?rq%N+?n4|fXm5S+~a*Xzn^Nn8|)%NpX zjg71w8m63k#PB>*G%7|G%Qm@`>hHGp-dmuY6s;yZS*e1Slj_!&;zd0?@R>+{?*Q5A zEa{>Q>9#ZV_a$m9O{^h-nnA(Uf25&G^m}o1b&U%yE!Eg7>)IG)#(n{8;#1knwQ^O4 zY~3;Agi!9JNZvQcYZ>K@@8#7#9V=L$uj;yX?P|vl4?{W3+Q%QB(9}1e3V*Nw@aW3Z z+mhMF`AO3GO#Z6}G&|3lq(=Ll4=I$I%M9Nw&Q!mcpd>rFeS=iv8IpO)@_ki}G2GUB zsh`L1;|~l~Iw$t7$d_0h;^u!)JsZU3lCSRLCp)YA?xPdB(5lA!(%Ssl5iRkQ&)g@v z+p{?w?Q3%6grPVowLDrunWwj}$&#+fruJ1gt<P0I5zZaL;4RJ2d~>E{eaSy2hx4`_ ztIZLa{JCZZ+uYUlA83T%dR+NbYBTX+<1ZHg9yuLXuQFMt#W8|c_G{lrqvpj+Hs@14 zsYpvH%d8mbkU%N%lMfD*zL%~1OPW$+vEP^W$<a~#ehdZi;73+E$8j}X`tL%vz9$Fn zDyZ%4BMCcKAI!E)VQ}tC<n0^GC2r!citc*<h1BZC#mCxqR$eZqtdHo&<3=X}+e}um zHP-f18vDcu?f{R<uL|l{A8qOFQ$0Ic^w}Ym|JjDQF@hzryt1E;QU47uHzz}~jotRE zCt;q`Ov|`HTq^J<3;>T#joMMDY1>g~e8154?qT!n1m)Mo#?l__JWE%JIpUT#Gd1Ib z`A_aMOpDX4O0A*8-2KgZUQQ7Y52J`1H(Pe`=Iiz>*qH_dDaJ6W-##Q=lPkY37voZ- znG{>MG)WyRqTi;Y=Le>7Ex{r>h;;lBKR;bD+?L?V=5)SgQM{P!C>!C&S$5R+<_Y_} zIPr)8*0e~T=UHQs(z+l^n#495tvwn_y(mpGBwYSlew*CzD{C^BP?nkLP5oRe@wW^B z4?m05F@q_2B~CaZs&+-D?ztFgnWjfA!(7VxYEJ#y9M$j$<;N#&^O7ZN56CO)E<K2r z#&O}?fuYXGO9!vJI{zuvw|#si1AW*ZpU^H!5@|Hn62rajzIVCp%B%z>*}?7~BnxC& z91aIQML*7nX(Xwft+yAcZL?zdbmU#LU$wE&^hR#e$SA6}4|j2b?A`rJx3i}0C+i1# zv)ARw4+&Z&-Q(MDxizn*2}Xx1pNdqj&z1h_OZRfG7~qba7XTh!E?iq8yYcC8;mi9Q zUpv+|JxS$v-k9HmB$sWp`=2!|Pp_RDEqUqCg*UR)TMATq#{=9whd%Su(L&-QAs&41 zvb)<A*Pm@19mE^pQ9UL|vZqL$$uliTX!tb0j%~fCaLFjEpTFNSo2gn?V7zwi8a_@v z3CC$%V_$#vYbo;3a}92%>gPubh%c*`cjeO|)f-RSHWgb}q|{80;1SpIUf!(!K9xI* zv~ESU!#p{}h5cob{=DwnGVQl#b>HPHY}vfqd<=+t#Xxr!3LF3)zG|>DPj&gl7{Sa0 z&EP=U>cfq*V#RXHuXn~dYfg@FTrmITIPusx-8-4`<@?pY&tiS_OYbc)k!|I~op)Gv z@vcI=RAYW8Q$coB(*M3Vtn@l>BzxGzO|b50?Y;FscASu2(6rd^s<jzzYetfhvf9t+ zzW*jq@q9G@gB-O-<)tlWF04FY=;J3MyUNL4)<7TrupsHMK*=CK;m}~kL`L=AGkS~d zC!gD1yUuSeGGX0j0C-4}j`iiXn2N@~W>Aj@^Hvvp*Uy7BCQ`h+NW;5h>gU`jwpgE1 z-Sk?fvab*8vyy*I2<E+UNS7h~UH5A5a5y#>7|4za;>nMwFx~BYcP-MBot4Cm`}q-# zOR0WVoSbYwKP_IDE;L`y#q3r)_PBZYa^%i5u72++^`aDwb0y5Zm1Gag`TvbCs-9x1 zm!{P{9?W?^OS&mnkz8#W<<I-zh(2Cq_U74eHv!;3Scx6ie77t~GBQ#%Bv3IaQNJWX z@=1>L*E8Jq_D@e5-b|CvNzx7ul&(8woEIxOs=E9isnFO`^BWl|vWu>7Iew>gyNl}H zN|Uk8JzW~w{j|ZE^SZEzJEyOoWT2mzj^M<ls*hX6$_%M@sp<V}<>nI&pXaHE`Bf3O z^3A7=Mk^K}d{{ofpBy!i?G?^FB5(P(mk!6}{Q~7fLTd+yst1Rti5rLhPG6s@nemF( z(&ei&q>2ky5P|{V_fe>~E=rSomefs)6i-Psj*B%cNs>jD8+NeolnXdVDeK!^3pE># z>c{w1uPU%V#o%r)(0P^`tpCwLM6=RFqr*hbR^q*^(ZReCVbo-%GQ3>#D*=~l3p`V| zGMyR{ASS-j!QR}FK2^`fiT|Fb92vqH?!$3b)E)w7BQ_nE>4bWB74ef2f7uwOc6OYS z`0@t?Rc|<~%9XUNIwYaY4&=qZbIi?<ns=1wNBC8fJ(a|*invyhz3dxd*HiLDh;V+2 zV85s-fos`$O#M=VaJUb9h$m-mqGm8dNn%yQnVQ)t4KpIe#Etz*qA*i<VRM0w_;ZPS zsXLNl0JygbN^ibVjnMyVS{xr9uX`m`LA-f=y+s28YhFu{db7=U4!D%rJ~L7Adb(oO zk(R+h(urZhC;d1{tcRt`Y+R&(eml-KMpqWuQ9gzt-;g6?Q@42-Xz%De*YIB>7~I#> z<+C%J2Kq^6CDp!_A$uy6x4-7we94XVE7~3HA0Ic9ZFCaNB3^9b(cOl5Ba^C{5F!}p z&*|eQ9vZ418df_bL^UWxwk$=t{ba+EOe(Ufsny=z@kg8)^ibJcY?vNTIoD31@4FkD z>?$AOUo}3sx>t9D^D_*-cS(cJ{FA9h9)-1U=hRo#UVJ4*LIT<IV#O~dD~Km|NT|Ax zueh&|u$LE)cysATxM^9YK(+ye3;=f(Z8JsIRNe)N$^P{j!;oM;%Y1ozk$$j$^)um; zK@5uQq$3o2<^H-N%`YxNK!1OAq5g|p)#PZ|#OT^3xz;{jtj$FYr|ax%azqc+o6VJ8 z=<Ug$$P~~uaBo-s&f=!74#$sw!O_vVHdpmdmh`az{?ugi%SXO_%#X9?P+erX(VKho zvE&E4qvPfwn@;MVi<S{@3E6!;pK=bMCA+GKk8n<!WNw<srAT#D))G-}{Pciqc8u_) zOyk5D`LJBoglPGUc>Pe{st@z(HXNeXm0Iucd?M9YgUTAi%k;>w?$&}>LGMVTvr)pO zg8u)d1j&jl(VPUqz#!QGAO3~{jm>5=n}0IGDpSzB@nnn5_V1O}xaRg?mLZ+r+}F+7 zRY6?Ybox9iw)TS~4X#BL3K;<I9DQik&Q7J}^3qJ{CkIu-{lx=)Yy8d`$3%z*gb0Jq zHxAk-?H^n-G*~e-KsX{$G%`rM>4^I06~5Wt&S`948ZZ1yvgFzHra}JV#TmM_ht)X} z%flHR=&NFs=zDt!()i|gb5&oTys`GO!<pdQcbAx#BuU;oZW+zg3<;1fOH*#h(-cXq zJ$WxBI_sCGO}mO3<}-OCgR0gZkb9qN_BmI7+iPfVzm~%@FG&&2h!(w<t?ci|eJo5h zn4umQU$-Px!DJb4dBy9u%=2vHAaB{H#~O`hJ3hvD<&|*mz;OBURLL}^ob2UoI_m6P zm=ePqz@VOql5WhA-Yn;vheWeX+s|I?Xur*P`wwyyeSL+_mJPRRkFq8_2m!#IZ7!rd z&gj27Qad)BKRZF&FHq*}lyu>|oFq9TZyM|?9PC}qYrMQHQ~r9UjAQ&>)a;P7{8ygN zQOKr#90=UYj5|+gpNkSrO>A0nwC%~jYS%N3*&<7x`C;GcZfZEUkFVr}!WmCB7Tc-} zse6%jdZg&d5c%Ycy2Uw~$Gmy2g@$~&Rb;#C_d@@E7Mog-C8G1_9`@wYzq2NvI<LF5 zIbSo<hdnq<H^Nu+PPXb0_3t6y&NZEQEsuxMH;esY4%LO$>ASfjT)CyaoxYVFzGeCs z6V=4KhU{|gGxbW6Xkak)e5^dMSoQM*4~j0leb|&IxJ@&1M@NS<?E2IY5AM1wSyxvf z${7HDUimO7M-p5Aa)P**8=H8Di93(DbII<C8$FU<eB!}+Ax=2JM>IQ1kfb;N(s^3i z9G{$OT%9U^Ccf@5hH`SG__ZWau;39GVRAhVGR=-faU$Z*e<51*d0y?(RMptnx)BW7 zEAf)Or|K4@NhK!xoqMrkIp$UAHFwz3_xd(HzwshFI?_e9rSWp&Q8O)CzC1(rT%2%5 zytqHZInK6w?lAAGg1R5a^m3i`iAZWxaP{VVWjx2kHrgE+yY=I2H=BB8zhTXx`c>Jw zK7QS;&92oO4mtN^92?E;6{L6}P9A!@$a_lDwjiZ$Ti)&02u+h)W~bJZU2NBr+Me(4 z3;=(UnCs25xD==+hKtCq^P{44%MUb8k5dtEp|eNw-%~1B#FOXjo!rA3>`}QeNwMXa zI!EQWIn>J0(Y~$R{B^!|OaO04r2e@C{cHPcb7jrmY}+GI4x$$O-cpnMN$uEBo^y07 z{Z4mPke!ud0;|6)Y~UE}B6E)(9@Z67^OLC&VYMzr4SMTOntY8mdnB)AXMuK72(Q1t zcv*_@H{UWk-QIYlZhWMgxQa-ik_1r1SMKasboL#ymL*ERJgodUR~u5%NdJDWplMV> zOK(5fsBmgLQ#H_?^-`K5xvFtNl7wo#iV!?LRz&=2$PSKkw7FZgb9mL>YPXUGXXK6G z_6w#aM%Ao4B>DM)74_dwh}A5L7u^0fb#|=E){|}QEKv&z833-=-ZIPgFC<8w4iZky z)(j02jSu8`oHj(1o8%^&Yl-gdT-Evm3bMEAM&xxSznt!W-OCHOaft_iQAYh=l4QiS z;`x2T6)CcQ!P0&n+-I1w&ks|Dsz+lQh27qMOx&_4Ua~yBmbeLe1t`aaQe^M>$NbpN zNhO}V<ynfmI-zTsiFk48kp0)3Izx;7W@+y@rC%H)9ulS=!cg}2V9!rc?c=xH;~fm= zShgP3ZZFU#@S67&YQM@;c^B)(1aira^6ncX=;tq<7%AWy?R!d_1_y|VyU_U~cP??| zlHDBQC0v}Ye>G9sFF=wlvM|`@1<b-faE!RQs^rhVpCPs^OjHmbHu0)-&Lm>U$+lAZ zM}J9?j0jcs2@sBty-j#!vP_d=sToY(FTLDLiBhtg8^>uz<OTSXv$c14bFFTt8^?#s z`-G@p$}&Blt_rU-=81kXOo;ya>%w~a;h(rwl3nF9<7yYiN~VYN`q5$K=8PD+8sE!z z=3E{^vWrdU$N?25uC4PCs%O^a%AZMYCY}<qqjGVAd|{?SZfeWdS^G1%-B~~DnK&(9 z-`evNvK#&}z*k7zcuO)=UEOVrbZNv@H_c6|>+K_(86nznT$@kX?pJ4b*U!ZBn<g_U zXOpS3nWE<DF#h%LznLYU87-fds3U&jQQ@+=3F<Y6jcf9Zs}43!%P^0MP&?<*go?aM z46nvY{3U;o`KRrjovn@zhr`j{aqa*9-~abZ9qZTI|H<kkCRgvATNI-h9n7J>A9Pwb zIi_|{ka$+?ZNlqZCijygx-au?KWMp(vMfr|kv+U`it7=10sgc}uC=2pi)Vf_Q|X-B z;wG9DAzO1muCv;IJW5AybzJzz|M57jU&s^>3R4gE6_A}3%aY`IlGd)S&iD4q1_snP zCp<b=Run!KrW{VE3ERuMqcdIA^O-EeqgBa#p06ODe2>zmv>I!c#A0c?vNS`vG_J;( z815`5F04sRO4a($9;Q+B7pK(U4)ha|ZRg)R++ej`HrlVcoHg~PGf<Z*|B9xr&i3Eo zM*l5aMfN%$kS0gS_Y`S=e0jUrmS^KducV1$%k_5qm9DO9SFW^gE}%9YtMxcz91<)f zdn(2;>lPomG&zL#OgPV{u(nWjiEVN`%(wMlH=#pdc8HoCxiZTcmG!L3S|qd9UHb2q z%k3Q<T{cI1dq?Nb4=gpdOU=$d0js?usmk<pm|#qhnC#|!c2L>XWzOarpN-e{@fQ&W z`{%FwcsOr#oNQM7?dQZzi{%XoRZfjjesV~P`Ul`oC{1c}J7-vxRXZwxu2rg*B`Q|t zYRdGNqN`d;<t<j5V{VS_vqSQo#SN1~`4eO628Gnlj#tl)6V7Fd$#%AX_p}ISm3~l1 z$K1XioOg0`Gs5{_78o-)rkFD06DicH+&b6N7MAW2>(c7_;o7rF(g>E>udI2HZ}m`r zao-R%@fQCjQ@iDauD1tod~h}8_{DdW#xrj}WAQFEeR4!SnaSH(sEOfNokP@iRZI^T z9hO<9M+o|PiARSj-Z?=1ZY47^xVm?MoOtj(PicGjqSx>50%l>3)Own_usB{qc9gvs zE7`{S=iDswK!3rfdD=}!G;?A_5@TD3vsL4V-!Q2A$L=xC8e1;UO%stFrNp(|<&=JB zf&Q(Onq`^lofVfqI;8wKPg$yMQ&}#H^bVuZ_)DiMmRqLB*Uk*%c^2#DG6kc9WyDiJ zKW_E+<oa~?^Tw6wXT)iVJMWYH+pJ*bUo4%SN)eax+g{{?gc{<)-%!wW<;oRQK>&Zk zY<C~aNW5fZH=B4=Zz<4Jn!c~U)IKXwMrWy0!}wD|`Mv3I6Q~{)sT&h0c<+$bztm8m zbB>H6?$snn`(l!;tD_@>XPT2-M|M>X@TsEg|E^3Non5Q)bR5$ovtlis9I;K8%aj*i zOpwlDN}oz>9LZ3S-K^Qs;uKzUs<4H)l$L6&nPLkGkkWPXZF>g2*|ubXnRxNYZf8DA ze1wA|wa-QiPAM$(lSy~ac=_`Q$^l;0WM{?LXw8?uFaq`-@2tesw)>2EWGHVWgR>xx z=U=2Ll3#o&xyHFnv^VedRPjB=IQ!ME`LVpfbBz(~3-6`ZObq9KoTuNz``>r+Tl#sk zMnwotDlW!y%}H$PSK}#LfAPyx=9S6v)miHCVS=?sHF;*oZOfKsHrqsI+xlXPQLZUB z*p(L?xsvA9`zhkio){unclz?*PF$Lv+5AkpWfVg)z>mG-L|s&QV=&8fS7XK=59jt} zD4l~D_Eb5Ch0|eokMpVNrOf(5iL)18YPz&I(?DFR4@z&dg>*hozamRbwwB$t=-i48 zWJ|?{0)1CkCu$*pKVjtE&$qmlsTvYgLp*p(lB9jT*+cw9&c$h+i@<ZnF>Bw+kPi$N z#7NpihJSAtPXGVcOeJySlfCSv@sb%~)x-P+bTu{9pUo`StvN&yZ`OvRlt5#(*xGL% z^3axp_41WjX_}QuvQKm56T@r9L}=z8Xnr$YIgKefDR+*6aa_6L#s79gzP|I?wXV*N z!4aaNiK-P$!A}dbb9ox|#UW}&l=!(s?Y48aY4KF|pkfwXX%F_PTA5PQ$L(K>{a)Yl z#*IBEfqFMjb>H8S$ZY@osBW0Qte2mJ1S<LltA>ZyJ|3kV;42v$S?gbFWKg#I+nY?b zveS4L?{h7E+$)x+%0D`F>6v81-~iqWOu<(L^`9M5Mmm@HxP3aD%5LZ%D0lASw39_V zg~XM$I9awOSLwp~$(kxDl9p|!o8C&TS(zjz-qn5lghK;Gqr$0?5t<Q9&1*+28%|vu z;8EGzo4qkdUZnkAWVA2HqJCQ*JvfBZCrIwRN4qMf?UTNcRx_I^BHJt0<eBW<`%$(! z+FwXe5?7wr?H<5f3aMF%3gUj7oHt)&o)ulw*I)ir6c?Sm0Ds>2+u5<L$lz9}TN)=C z;ZH|f4e=HcPkwJ#*7`i%FAY2SI9E+vx);H7<2pCo+0(s;%ihZ8<7<b8Du}CaFhl-a zT<wdg^)n(xqx`FTxv|LBQsP?nN`fdu&~i+AVO198d?5y#Fh$;_zw4@o-3vt9NHF`b zxOrtx-Hd3-;N!-}_bK|iasGOM@+oa}DZjB26xZ50FNQaFpQKNqjJOG&kEdQvl=XKP ztUaW^UZt<f(<gDw<3dGqm^F)Xza8nv`+KfxTcNHy`8B){&KvG0aE@qlt#Z!mxgK`+ zu=;wiU5e{=6xDAyBn{`9iFfJN!uweQALh#Y`|{|B-`Blr@jk&fd5Q%weEM-{cC;{* z)B2!`x_j>SwI?Ha{^v{{B@Iu9aEAGDm*m;r%rQ=j5gk`v<Tu)jl^5s-0Q#3HbKBnz z)%A1lZVlK|(Z@$PmRZ|BSV_EOgTqvlgL%%6Rv>?QlK6=LI{oB1lTi2S+0oMe{vtXo zKA#{DIIUZoM{(|JcgCbBZr=c@bKJr%&Nt5Fv@5fwm3+-wUwC8Tae<-jX(mNHB>OnG zSyViSt6!F?3}-k0{LEX9sE7xDSP1o6N)0L>0OY?kw%!)QZ|(2JA^{5GU2%tB{vzMG z6u?jSN~@$ZyCHs6Ycdskiw)Zg>%KguogF14yQ-cDm2Nz4+<M0HL7w5gL-o%ksD=dB z^m6C)@~rL^z#{>?>CrWx=c$JJvfs>>s?7GQ*M4{q(ZM-R%;Nauuqxo3>6JtY@v0sk zq8t{bTalrf5+@C+YBbva-THS_Wqtpc?%8-vzW^oi5YgGq^a$rtA<iuVJ@|us*aHJO z?_?-O1`CJyOBZG}E>D$3vdxb6pR^w~U$noRq9Wg%zonq)Dt&-QRc|^4cByjix4!%Q zQz5*aC-g&uxg#TaGhzj?<$Agtu5|aC{)VDBMYZ<z5t6;^Vg7>g{*^DqOU$kRnW0eE zub-uBU6EV2FiuK*<aCNUI#N3{Q1(;+XZ>N-SNR&^#_L|Bg3~8JJ}Q*!U8G(RFMJ}L z_w|XokMf&d&uJXNR1aXN`uR(p&v@?qA^w70g}OZjYWaiNw#jI7yqTpYyQ}(mRKI&r zm(=|j7GBXrwzKEO$O=^7I-lGumv^6OTAr#%;@xKH7^~U-@zFYO)@^<?gi%fQ@`s12 zKR<}!RRHjBWNR_;m%o^xXt7`UrRvnL`g^jq%$f7v3@E!RR;EbYi#0V(_LlGe=ZM7O zUs5+MoKJRFjtmqf^O_88|8rJ-;Uv}SQEFI~A$c=Hl`XrN!n1sRL^U>;OLm@rI$HJi zsqeRx|8wnG$D(u{aXJ5Vcy)5Mxuo{u+8oNcNsxOfVTj!Gc|EN2lcbk92K#yarGPVa z3t~loNmVY)XdphaK@9bzFxi|)K}2PX&$&7}Ufw*)oo&tHH$5L$Ga+0$BEdA!vtl-r z_r+m~xR$$~(X7n+*Q^{5E{PORj@OS3<p-T_<o<N^DygkAxKy_+yZ-ro)V!3sr$dD| z_ENu*^Hz)tm(5Pn%}&<OO*1@~sDCz5KP65#Bvk!)Leqpa)09&UuN<y>^ThYd^IGS{ zQv<!(WLp{8bAEZUB)XzeX#U6QEa~bT%^f06;Awq7Us3n&?at?f0fEw&l4}>G)Z|H8 zm@H%Ye`x7u%HE#9ZT2ZMPYLHf5ya~is3Ja!KAz55#)AT-PX%(foVDoeSAQC{JGqp3 zP7FJts(ByVJU3QAJXj<Bs;0(iUOHg@TVdPE+@>cP+(rBNE`_y6WfwObr9UU-Q<6mG z8uM?C$a!4js~L4<d-<Y7nZV$jIM&h8@y#){Gu__J*?i(gLr_P%{m1)d{FuY$`1F`Q zsNDGTYj7`XoS)USFh$EaqeoQ(05_{Um(x1pA)FPb;AmQY>37!VYUzBfmuD6665I^F zecjmuJ*xV+RXXS0Y%jk)`^Gu3h>knXq^63vbDZCF=lnHY_VY@qt-V8Ra)g|%AI6}o zxU#o1scq+Mb5eD~!l}MKf<C_8JM?*0ElpMw$?vq*gh<o6wZ!yhhWv4cbbz09Sg3kP zXm<nV?&_&g(skL?&JyRUAh*1u&E=M*Owo*Z^{_x{c53~{2bAyS8}!{livDRD)jA<U z)W=77BY^I$T9ZSa)O^>`+419(l$oxq$yF~)q5AtuMn%`Xouxc3v*e1K`*=%w)A?F= z5AVEqc_zmwY`Da$r~h3%CA)C4#&TF_`sxVv<srqV2dUQ&$d@LIM+Q~Buuu5@0p<7* z-sa=_-6hSt&$X_|F$@cl)6r@YlUlo9u_}ksSZ=ck)Xrnd-rm9+`StbxcUJWF5e*HL z_VeZ~Pp1~`6ORk#vQ1ZRS-miyr++p}=TfNa>g>FB?S}{VF)P;FMn{PMmZ@BsRr`9X z!nL%?)Oxvx$F_HNYA-sXD;p<-amlXptI`yIKYejtigsu?MSn>8_zQ=JOO|KWZaS(N z=*c3x$_55<V#@Ra%het()wm>0FeXe!T-akn1n%Yf?nr8TFGEUpR8ET1?kLjy>KW+B zyZ&fhG{=0~abns_|6^)vM^ys=Kdu?#t4+Opq=Wp~A-_B_icY~uvUX~UY$Sv09Qk>3 zB}qES?qp4mtzDXF=;PdhwBmZt<^V6=-V?Q3^OZ}|)I-Ac&xG-9zg7)iZ%AC5=bW7p z%Q1VNt{)o68{n@Pnyy`cQk%y%Z!6G7RyO`?`=qXQM|I!iYlepkW=7Xc+@~e3-2Q&T zX^|4*&R&u%O{i>~$lx-5mWFK2*SHjECPmeZPrEQVx^`5km{HN-Qfj_s@sMosg^^*k z&X(aD`=RruMhgBNwF~uuCH3<Ym9tVB`-Ig^i=-ALQ@cuTOkox@e&==8xcO*pFZv;5 z+xg8!&384UC*(&1nPRzm;!?WhsAgA@fw)(^akzPVh2zO!?uUnzOqMx`eIt!plPwt< zz$beu2hfizK4NDVvlso?!*bpZ4~}z~u8&}7O6{C1>kMYi0RO7B*^1+`i<|OwlS4Q| z(zTPK>F95m9W5FX#PKX^iewv!d->cX-hFS{rOzg^w)2eEd!0<w*|j5IO8nU?QY4}0 z^zUWKiFYO0Q8pn=v^cMI?!k+H$!lGD%sxF%PP|y-Blx~W+K4g(v)qs(X=4`NIw5y( zB)4yH4cX0J7$;$tYOd$JTZ<Y-hShDz*LU?Gd>f6nwFk8cTr<W{0YFb%n_bF=GV698 zztfmo;$GD|)<{B~<1^>PONnds^^nxtvvNt2^sM&Vi{D@QIG5`0!KJ_1&sQ=!Qo-#` zGv$`cFJ|a61eV`$VJ>;18DYHPv8o)A@#UnNArbmX(XwTU{0|P+eq7M>RE+e41L~nc z)$8(<13fvt-MF8f&^y}Of1$emVM~ee*--v-aq5A7(ihU|UdvIl3~j$OqRiRmnJ9NY zV_eT7J-CaLMCFuaMiR9uMK&>_roX?E_{-)+ix<W#B*xo}9L|wi`*>HAZRKAVH{Y*u z#jTgG=E*Nj46b@Dxn^{v`jvF;i_tZ2XUQv7)|cbO#EtiuPvx2e%4fnw{e8uK=n(AA zCm!tHKGiR$%SMLtUP_RCnx}gvR@pbOW_XBlP@tUnh@3lO>|{B+5?m^Kd5ik{N%}h5 z`Pm}^x$BSCe|}UqJd~R)uvqL@9*M0kENxe^`Q|s%YKTWUo&P=+z<qOnZGRu}0DtiS zU-9D%xmR)BEpMbyWL%b^C0okh%dMk~w(I9%a!soa8w^%QkFxUi_N_&R=<dft)F%M& z@9ITwPvkbO$x^x$8LnKp`je=-t9(?n{*?0K9S$E9!Rwu&pAhF<+kbAni1^lYM_$f^ zaQ;5tjUk=OQY3x+YWlfXep8^2<C^Ycb($n~EQ}WQ4O9*H7rhuOd~d&ESAlMMj@03B zR*d?V%P*!GdWGnUsEfB8XmoUJ&ey!0rkowETy&)E#T4hBw*DphSiSvMD@pfa)u=F; zbIO(Tow6rJ@W%yn2ZYqpS>#jkvc*|?M!A8jG5^xxYw}dYvx@9uy}rMWr83{|5YrJU zux>AGnwcaYu}?ZDzHTrbF1>0V4{<&kr3oxCEaPjbvT<R&zP@y96{hn{|4OAhHx^2X zducq^lq76@BUv&!u=>S);)Usk5upm=CncUDXCQX1a&8r|y@I$@_X?0tkJm0quN&ye zp1Duta$LQu#K_>;9A@*w|FNgv@9GSNGwZ+JKJ!hvU&XEaL3ehv?<~FeY^ah^@$38N zIu}zd(v!W_OZV5saLqP*`}J>nmz$&5H&*KCXm7uBL$|5z%9>-Q35;rYt`Wf+;E#&_ z>keZz_71A0E9XAGvM=*BKVC>Mw5oYnXze-eo%Rx%yid?4RP#oLqB~NTk^q^rOV^## z+gmi)o&EAbovGE)$Ad-uWW=4dy4-kQE<1~D4m+p)ra-&$plO_c)f>suOrGVoU1^C` z#&?d?Yiw6;ekVs{d3nF;gA*5qG87{tRV!0!*5>N&s+OaFc}lpjd%`Ild0AvHyN{o2 zRG4%_zRtDu*H5)POFAxuM+dJDk2Gq`mmkobEi>3Y&DV~OlJ|1sJP{`II%Bw{*?jo{ z>iI-<WJSX-U2u0e{()n-yd;KBb<39~*6b`YEK3kgjpPju5DyQM4h@tz!?%Z!_$i!Q zEx7S}dGqEZs%J$C9*^LLRhruz5AQxlLi6P@QP+D7ss;r}h#PNGsNjnunh8<DQ&dZ5 zXXo`2I4ZIvmFau9MRF9oX)r@aeB{Je@Xmha+@zWep=n#8?!zqkf<$3oAGX(dYq8vX zhciT1nux!WxCqvtFqm!V0tWcQMqbZi-3#&5v=HHNuj(-oDwh)d%^zimFZOb+3})Xc zTzZ~RKOSRvDOu)>yqk)MzifyPf6acym{9)oFyRMh9I;iV1@U4!PCXt{J*AL+uUE^q z`kdCko~e94TfOd>K8a&ml_mYvZb$m3t8+A~(`Ehq1xpWEr^hHpFodZ*Q!wv0&S`Zm z);mX(IUAhWWKYG;Q^quDPnV?78*T4pDadZ#>-+Eja*^~2SCreyNbl=kui@Gl#fESZ zeQmc%wHRD;zQ*43Yj7@NEHZ8`lpa&Ga2vlXuDzIE-8em3FvP2BSqe2LMNb0c#9c)8 z@JRqQX<yxxFyX+!D*Ar86zQbahihSHw%)!tUNRz5zcfMOb4oKalKy<k$X6`lPtl3| zh(P{JsZ>yjeo5?IbZR>nlVqDGMF^fttS25dBtSJfQa#YKs-H*I&=3XjlFW=%i*(jI z9RBtp`K&nE%UKPt?5EglGb$*6KX&|e&dfcgBRje8=hfaE%oE9KBrauSUEUpz<!Nm* zQ%#E#oI{j1<!Q(c&f23ldbJ!^u3q_}>q`5z*)d|`AzB<KU{mJbc6lsa>xW16tJ7*e zDQMhNpvj@W@9gO8$$?ahV|0jMeO}%3DTc{0iZ73-KFl*vrq+8bF~Z>X3J^HMt|xDL zB=5E_T}ZXYmfvQ=h;!6UCPn9oBg3li`>p(<sQ$@R^VcVT?*&P^e)u6<Y9-$6CGo;- z`Rc!>*7WnP?C&QU6{#J>Q1@d{%d-t1<fw=@XZuO5qrKB;G(LzKxOZi38gEfSORmIx z{Vg1*xyU$g{^GE5pbw|NuW)3fws)X%I76`}*Z5kd;(+?HvGwwdq@-WE>?F4Ntpn<f zc_L>gh``p@leHpMzU{bXYPf*xU=c58_xfOe(Oc=%%jxopI-A~RyX~orR)@6>%Qyr4 zS%UA{Tn+h#?Q#CX^;Kl)?*yG|m=(!qu<tbA&~)j_v&jvcj;Nij(-lqR8`j)J)vlB3 zErps_4;ufHN++a0EX`08R~{XCySm6-T~&du)w@K$G(+`PuI{-gv0rIjq4f5{MCq9R z3F`{uN87QDix+)$toG$x>!K9RTgkE$vX&gme!r@E_hM>Rtc<u+5)a<`?AuI?$`v(F zNvK;8Bl!7`vbg#sagsiPiWg#q_x(PSJ?F??aa>i?e=w-g`L)JqotVTsU;iD~{9m8t zD2R9U(;>WtSxsY;n;v882e?&E4i{yJZ60SDo@5HHc62-p>F~dXjkmv&B+3z3itGL{ zJd{5^kUK3_O?+jKGwVkBaeMi4eM=h-h%KA)H3bsWk3;9?BeEysO@kTyO$E9ipYz#B z;pAvpFK_mACVy%;e`!|Z(Ab8_v4%b#?2nEca%I+CER1{x_>*dLT-}i0K)yb|dcT^d zw_dL_wj5P&%2Rc9-Dw!7$=?2CsAO4+;)6Wp^e_Q&VUPA<k7sb4qknxB#G{J-olzmx z#78ndLYgb>(MaY(%E4qAKP_x{H^2Gi1j*=NL5Av1S*oMmv8&iHHQThiP%CZvZoEHx zuqSJHxNK>vl2P8Qv)uo_)GV&%<s^#ic1B*8g4&<goCif_`Yn$MqvEP=9Z?p`)-Q|| z_YG1kOuEmJm%fbGv$bR!JMip7x8JLCj{|$>hdI)Z4^lf%Xy(Psh$nlXuW({g{pV*c z&5jb0o#%F+(i+<SPbp>H$~I@2&HwSF`1)lRP%S&oHqZ|VpC3{#OqBQY=Z*|jPl&5q zncMW)sn!p&RZj+UU(b|I46b$#><^%pCW?Q2hLHi)f6t|Eo~`Zb4-=V!tw%H~(^W$Q zC69$`o=$0aBUv`WpMOmLZ60Mq6bAV7B)rFzt<J@hcd)mgG27dneS%vH&4b)3+<UN# z9C0~My!p;i%`Vl%jXOJ%zvql@YNCOJX=a8A>BDD43dx?TA)%^O`{j4OjEyBNZ>33= zW!AovF8knEeHzz9*?xWcMyb@eD5IY2Vh!^bJ)dn^cdBhmLH&uE3#H}<)wk$gs+kif zcaA0V;6<Llb;n`nCcLVP1N}t3yd`5pZ(R>xs<*zKAs@`3wj5F1hkMuFe)Ig-u3h`| zq?K$fe=A)gGTR>J&l{tZni9(`(zTsYwoYd7pA8o*%lmd}re#KI^FWWvL4NF~f~$kd zn>|kH`!hIO3jf1<I^Uc|?Ql@;Tw{f{Emv~!$xwcO_ljrs>Bhu142;x~JzTPz^Xh(G znf@mwKQ2=6Rlf4)pAgFIU>V!86n6SN3zKUH$C<{*H4vAI=b|NyG6R!qK?nx;!$)40 z(Z1=p{&9bP==mGHv^(-OWJj6%&n7wEg04XNyRnI{@U<+(-wr5<D`!cHy8eRQy;##b zQp6~74wil_ghxDtL;MBV0`t#bx=h!)<5bgQ{#BpkDnHGy+jCO;X12y)yV8@(&R`jz z&#ZmixAMtQDV>2%VhCn21vbY6pJB?Ttbff^JC|;D=Vyv;Gc7lqZSL>Gqto6eB5rMP zUY8>p$KXB1RHw2H_jnoGiuLo8Rhy3Acsjb;(XsYe6WPHU=2NvMNAzfY0@=E@$+3c! zDe{1G2G`<-jk&51vgG4~s|N=PUpsnnb-KKlb0qnB@^uMuEq^^l6mUwDBWU6oFE?KL zZ*R7FRUu{m*Xqp=;_fH0^^CK|_;UR%L&TFf=2z218;<KDs;qm9^kte`8)wFa^R}N* z-{F$zA6;&5e==6`_I_$_k?!LI%7H$Wt8yCN&rv%UJlRz~He7I0ZKo`k>9_eXv;zRZ z{jL4BmYQ$w5}qTq_j0N5I{hnSb<CIkIX|_Ixbm#u|Idni6$z~Ay|+4q^^>IoKB6OX zVD02c{>|^S+U(<#q#F(>K0d6Qm#$mP6u*(DE!DSe%%XZSak7MJ`TU6b^^DrFVUmwd zT^iwEIozv)&Nx0kto?nqI;G6EcMfQr^IP3{X}6ouL>cXCb5vvxe|}=kEk9*)^)Kub zkBw4i@fz;&GS<^iN1o!*;Z$7r)ah1x`<lE)vb}srpd^dm{OEoT|D|JVmMDp9EmU6E zbwb<Ar*eQ><?17rXEK`mGiry0s-KG1J(EyBA(HClRxu`!^KycCdwy-md1C_4;!<oN z!K_!)1Vz%8_V)YGkNjb#bZlJXXolkXXc6O_eonk(`{{<0+Do@w_H9~(eA#bh*4))% zW#bs4Lb=teteLo0yp*h%m)kl%vGIv0_4ttL9VhBS${HRI<_r&Hl^d`8sH_l<0saV1 zDq4mF$+i_}ZrOI{mWsmbykRU;Mdw-X<f>Tuwh3V(vW*p9@sr4FGPUMVZ7q(Dqg2Zg z<v$k1N>*moO;2f95GRYQXk1rh>e+lr2glTQRAryX<iByaWnygY#$4sbqk3z1bF?!X z@|3PWthL!3zeC{|&9?Us-iW;MY}0M05(k_!l0Cu+q5NAaM*7*{o8wx#B0Z<P*J&eL ziuA;TOP^{(p0gQWpl@B7T~9~eL4nfs2YcF-{b*I%SKF^{DKU7RHTa!1jts6Qd&@@! ziHC$~oKu<hauy~l79=SK__E2a^Fu>K&!^S@^_ca&LR<F$WBL<P`9c(T`$>b?{C$$t zHYJPN$nNPNaJSQjp&`Q2p>h&H5jP2Obx!CW79gA!CZJQ)=*mW^#oj%H@5bz{@N&)b zX{t1?xpydMWaw|7Oh<oOd^y(1q1?Y8ulqbtLp)f-rF`5z{iwKxmk(c@6Db@Q#QUt! z^4{UPW3m?1S^$r_T#;!^I5m<XJt+Le0HL?HzmcOQ+sY?|h-bx%PD)#*N6CmQe{+7_ zk1O^MOIrepbvue12L$rRC7PC|YUac%PbsYI9`wV~zd#3+X-v@@X|fSf%FjywxhPh& z{D5Ba(_MkSJf<2Jrdpk;lp3wSL*bNatnVIBU*9M&hI9KxJXWU3i8nPnM*Q<WzOV8% z6QVWRZ}0UXA(UkzZgk{TeR$Xzc~iK?dCA&-dn$VS*G!3}x;lS7{bv;G!mBQPa7;-# z9;Ntu{b6Pun<Cz9j^4KVQ0<E3nyGQxf!_Sr4(Q(5FP)MkS2TX7Z2j+58B((4EZI>p zhbh@`=GzZWe)~dJ)05HKUS5^Nt8#dV;PX87#Bjl)TwTYHddzQs)Ajarfq77vU}l)` z-9z;w!{o$ENW7%PpBfcjJH(qaz`N?B!_?$R5eeq}B~dac%JAMH#qdb(;3yeSW4<qM zc_fpa#IqL2T2^E$`+8NpouyorV{krHxNuizN_j@Rsnw3U3*ga3HD7)zf+C*PTMIO| ztp~XF&eh2h=k~8V*_)3$2h+a7<V=mNC42rqdv5~O)VZ|*zl`DlPIaEgR$J|$Ry$Zb zXh%ETw$|EeZLQU+$P6JQAz=;(NEn2O$RHr1qKFtWkvTa@PC^Dk2oS-DsiofB>&NHK z@5k@rKPMoQVJz#vtt)%ET$-Gm;XUW<=Y96O_p___7HczvEmu{oubvdIj^QuNH_Rcc z-Gi71E*XEitSg3$!#bG?L%YiKFU$?&q)^s}g}3IDL@!cghcD?$4ffq*U2EH#liH~S z!5bO>f2?mJ=kVp(8W6xk^)!y!XEeaO5~1PW73n)W9}f8K(bC$53F><Dzy9QDd|#*q z!CXL)gD?j2-;b$cuL$X;WYMe1(pNGKLjCY%%gYi3AWSkTMw2Ht{jDEYWK?m?R(wVM z0y5-^5lxJcKNrK_Ko;*ftI;*L0Sx(ZP_|=x26r`CNTxM@o2POMqEEq8FGy{gmeQ~+ zg1bDN`#x1k*O>vP0>N}ZW)>LDcBfTsZS7k#W$TlLkyVDBxf-uf*1|{$2w)++8(_r% z4}pMcTTd=zZpQEo6MvPXrt(eW9$v6KwBFF(Srh|-V>FLY&RbdfuZx><h0PB4<uHf+ zO-*_0kA-nHuHizki!zXERqBJT8@HTNI0bNkcNOsGe03JBtU1YTSQrhV8NdDvcN}kK zjM_a?y)jF_E=zt<Yk`by!zxYPvpm?GC=M>G+mI}sNs`Y_ta~q~;g|B8u*qT>^0zbt z%{hbzaAI(%Y}|*!WNO3GZ*NLdmkN!KPqpLc3w7Wa1B4>TOS6x>7-SrTzg%f~0(m=* zmTKpdYwGU&`%j+OqC`Hx@<5n$au^%=?}0qoylDCJiQ*R$1g~c5pKO3gsMIY_6do-% zTH3nl_Vzr*Pgk11JKvyf?Gf1Qcxe5*>K3Ak-0kgAjOKZ9$h=4h`~~hLrDwPpnV&3* z7W`780Y@uHmHNK%|JJtlOu_ACae~jzHMx@%6G$r0a0v*hM&r7n%q^#sBv#|FRMc>{ z{nsn?C<XSV_x5D;r-J}C2<D-kF#|X=Na7FEp_vI{exrF@t~8iwLIuG6IZnuNgSa4w zhc>w&YNtiA9q!9v4*Q#0b)9)(yn0HcXf3&->yuKQ`Fc(3-t)R;Bmt_{>&se5;x3Ei z?#!w2E>g^n=C4gspiah@J|_a2Mid5K(rwOEmJQPPr^lE&BaUVWuc)0v<ZQ{PeeU$_ zxzR8Pt^OoSwlha5Yi_kmItF~w{s8ho4ZrBoVzL|ri;C30jk`0CtgQDWz>_1@ZzK!Z zjrV{L4PD@--2#VcAe6U(BD8yfUIeCdu=w-*dPDyJpxJEx{Iq;&Le2mF-=9qeTTS75 zMT$V6;0>F4=Tb6fa-`hWpRsCFx>n!P@3{L4bnlgDCdZ1GCF!1x;=O)GOBFVEcA_x^ z-Qz|)(X5-VoRIu@<#s0jZ!BGi%EDAM@5@skC|0|KAsEVD5+j)%tMQ0Xxsmm=$?Dfm z>0U_^dS9+9g-zg41@kc<o<$ZODp0OZR|9VZH@WDaFy*UM-M#kV8v-6K)6R^Px`x9b zM2L)kPp@iQbId9)yl?^*d1crxB>D1KF>*><PuB*PssWM7G><v~Y&n^;GF~)0y2cAH znMvXwI<JanGzMMKdyzQr=IV%ShwP-o9QJoLS+iwo1Ox(EVv|MPa_{|Y*=s41sfYtX zc%?7eY1hez9$KMKW*eslGd&4XC&cIYvC!FmAosg-3ZZcrO@N0FA8yWsmqoB&PZ1&b zH5n(KNQAefK>M#W!p3{RFlf5wW{U8qQUiw8Z*{e;y<>Tb1Vl**6^8K|0hCtIjY)LF z!4f?<LhGV4UNNerWK_%dP^lgqWq1(<$z1zwWeWL*=T2%iq^gSf{YVq0OuZ^zygW%G zGTix-XNW0<UpOHJM_E6Wpv7OnzrrP4ip+@I!<E}JWdqKNX|(JrQlhcYqX;fkyW^pW zIN{sr@Sz;2>tf^CElbH7;LQYBnOnruz?;xoa)V(xnW}AR!(KIfku6;w$KRc&e*Kij z884e0shUEP0ZjFaDYCu!HQ=k#cPab=zR`5gdads6H|KP(pA<UB2(ab)5q3=2mkZBK zl&>KR-%FSEVZICyP`xQhxFZd63gAq~vOmvJZ_28h7+EtRObkNUZ)K_8O_fe3GD&6X zQmFa9wd4MS2SXogf1bjLs0PPb@8%-+-Rj6~c9_GS8bjV?rywTcqGuC1?@=|LQ8Kiz zI^s+a*hQxF$lu55FC+-|7pRtnLo3OmlMKCcC>;cFw`4cu3XOy9^H7<NLf5}VmA{xE znUny{r)ZwR@fJo3T!N|*N3^q0Gwg}YCRKxQ&61P)97#)GhqEnH<`M;;k=z=W%cpW0 zKgzFLo}^wJ2hYM)qrK!%^1^b(i&vtWkd|F}HQ*Tg&2)vV(LSkfZ-4Msj$u=(nl9_N z?gmIS#NRGGp~)Bi*@lTNckl1L*bIKUe)uw)O#W^fe^I;=In})-wXo4nrVZFm-kJ%o zNYaAiRp_nsV>$&1Rz_B5@tV^4Ezb}nXzw1Ms%6Qwm@BnU@q;ZiSw7B|rB&bjh${6Y zu%=*`t10^DGH<Utt$&uPd^4~0g?RC^vAma03M0$3MdDi(wRgG3f4xnGGq^1ml)tBO zj2Y5qKpiw=S>JkZWr6}7<Syhz8+g$;hSteM`7}bczU_VnNBuDsIw!sTO)d<A8Oswj zujJoZouYe*qJBTWX={qo=US7+GAPl`gG{p{`4e%{83evhsn+4F9Okg6z%N<aAM7en z??{t-hE}_TvY&|%E+KIyg|VGN_%0#*`B9>83N#b2Z1*tnjx;e}XXZZ^?Rg_p>_XJO zm8E$yo0rIJ-jX8y?yO=aQ7}1N?iMPVLJ+Tu<r7)AtR0<~WG!BJ0XRnAbgH&XyK)F4 zRO%MQK)|Q^!%PU_aXp9lr9k0EP!z+h<1z?e7u1445$ZgB=;$A`i!1ZRG;I>MOU<C; zr(zvAMBkPUD{tCgzqYhn-_JMBh=BIwLj8y;*SI-dF)38?L6-DSQWf+(&uM@k@7+^1 zkj|V-H*C#qc_C3av~EL=$oMQ7LMBcii0y>ofk56wyx1+2-;ESL%ymgh>j>hj0}P>F zRB_8|6fwt^cK+9AVLg?w9UYwp^Sv_p?VpR3Ae7}5%KoOHb_Z3pC=Qw!342B=r$#Bf zB4M{Mz6V}1IUJf0$^}@-v8#hhbk>_KFQx0c$J3ADM36cY8OX*P5`~k9va635M7oY{ zM0GXWFDZWi>a5%qD*`?oaGd*1ah<_5RF-lnN4+5hb`BQr&QmaTW{2Z)n8W^##xa@! zk^4)rYIcO^z0<lc3Y(V1OFz$q7RB->gs>+PWy_-_n^R@yq^1w4!h84bewr<J4wJ5l zL*uUc2i<PAP}o9Z-Ju!o+B)?ZDeMOSYsSf8%#BHsC5g2lRK7g9ZgC{fGmME~nP(*Y zac+0bDF7JYM?)Cp3#oOH<;r0lre_!(U?C970GQIA55ul#-3W3je;g7!(SB`tY7d;a zJ7DO?aPwn*c1A%r-0NxNG@HZ){=$9d4fosaccE>wSl1`W015UYa|Y}@?3y0@T(zsH z{?F@vfI|f!gtPT@T?K4HkoSYa)}Qj^!(Cm+{`2*~PXGewL08qQ5~QA(>aM&v^iPPu zDS$o(=p-`L@0~MFi%@LJs$UVsn;OP?hR9ClG*kG^!nTgSeV>0UQMnVLBgNXkEIK1Y zWHhzyD}Vux^9Dt>zfkQ{X2_G8_7^D8MF+p?RSD`?#-N=JbmsOM1TLChWqWwMBIH}s zP>t>cc9UB;9C$%(_g9)G^ILiKucbmCQ{e+Nb>E$r=<j-0wG=-_STuITG}+YA#dG4J z4HTxsgE`D$e<ONp>lAOCif6r+Ds{(;CL+WW&!0rB-dj}jd?Mt56?))An-j$w(v_uf zKPsBQYDV6APYmPT6yb|GxAxg8j}U*=W_@(1L`|$}euDxz2Xmc6MNXkYaD+Au&-o-% zTB-jh+CVk~0wFx$!}^e_glx_P!_=)gApk-A*|Cy6RA|^UKU7pRAzbbhNT<TRoc9F1 z0YTnGX2ZA)&JX$U>{w-Ys;UotNkYv9Y5$;c01X6+{jM2@=qx`G%LUPDXW~G|IETam zzJep=x5fo}5Slh8QMEQvm{6|Vo_DiQc6;dU&E~e<=X4+#0)ceozu0p9f&?`<PU}Wq zi~s~yjRASv+S>OQ*MeVZws!N?Xr3)j?jEiH0o8A1sy@%v5GqjF3h!d2Cs6_dg`0D3 z=82pCZm*2>`*X_4M1C>UGQ{{8EuHQGRcKZ-`tVdOh*QQ?pfO=At<i-5zmzN*;(dJb z^Nx=8ji;0oNVWS*(bfC{ar1#f)iaS02;e_^qUJ1g+u&H_b(q8csvvumW89eoEsEwG zEYTK9Tm4EkucpX1rq#Zjb9+;E3-GS=xup6cw+1bR2o-rmD(2$3Yf@|Q74@*Lr<A5s z*JkyI=A<wh4_z{V<8+Tu?gX3=`14oC^YTQumd8joovK44W<S$FkZ4+zVpg;Q`3DHD z&XBeCu*E}{v>=2Fe3?`5qW7uY={@*z9l$`YSlUP~r(Ai<g8)qn<BA`zW+Hpon=5xE z$kVw^eXqJp$~OYdpf-P#U-MpWJ=}b6B$%>1PB4Ke>*l;Z^yMd1W&P(Kz=sAf^8H0a zb(C<zaY2*{JaJJV5J7Fx!OJ(t1$k#j@utTskvp41*S(qq?JFH7$1IJb-;$;Pp=i(m z`LA~g{Cb8O#mcU=Yy^2jt0G3PZH0D9%8|R{CAJl0#>7zeoQUeFIQAPEnx#ov1nB3G zpd}>E>S(TWKsCBei176k(XfSPXmL06SJ5j*&VgoZ^R665`AXJonH|nU>v_@K06K!a z<ZEaGLkz3Qg`n7;As+b=Ef!1Ejk=jcDL8UtWukOrs&sXt3J~Dgc<y{W%ZKLZFz7Ic z{S^T>ieYf~W9-ROtxS^5B=JE=wNnVk1tW093fv;3FO!9{a2&O<jniTQp&WMtyf#6; zEm^!Xw>J{lX|;Y{teX<b-kqoXpr8RDP9RhQ0ywDi#4u37fn!yhlA#r3Dez^3qc>cG zS#ME9Z>2-qGK9Obp%2rgxEoD<gN+ADQ2hs&V3u<jcY8M6_2Egj5d?`{gBT<6`FTpS za|nA$g1TJMi-slgRyVxtaFL?tK)&?GAL!-;o0R2ex$xX1y)#CdTs<PO%J>_)Mag<_ z%*F(dS3Q@kQQf-NcVKtrszHGCi#*i>tCeD~ze9lIfJik6tQ_#Udn5-L!|zL)9zL}G z`MVrcRy!kB=|N=TO4Xaw)%3<;x*}TIE#DXGL9nC?c@e9SHSfq$0Y5Z@&J`>CfGUj` zld5E3iEQObt&?w+lRteLNw%KMu>t0z9GU5hTzFZs!W9eIjzUM*K#wpZm_so~DiaR7 z*6_<E<F<79@v^2UMss7!-$S6OL}Xl<r0x#j`!Obm306gOR>tw)Nr#-npd|Vz^tzd_ zaT`Shd|4oXVXGITdtmv<yPU{2IQ*N#9QKz*-hG8i5MBp<W!ch%n82TjyuaTTYin=+ zv%-KD{us@cg^}FliK<C>*((_}oW{G|!NYcIvHZ@i9HmPjeOh9}?BvD`Sq9fY<n^b` zA#s<IcqsA)(?PJUdcvEIwu$$nPbWdY6e%kDXyw^bSo+B%E~N+ohBt@6efA`jE^F=D zhE!P(FjBW*#z@!=Y_@!vqnJd51B+EXA5w%S7aX)dSJrdfcXHI=7n<Ky6nQ_Uis!^B zfggK5iPJWM^dXM6byKEx7S=YwI8J+xQY$pH^}VRqQl$V3Z#}KlG`EqN_H(&E%#u%t z*0=``<h*Msd>6d*^Zfc2^EmVlzN)<K8ZMg`EhJu2Z%)&&heO_2rgkF*;k?o=<*P1B zsMM{Bhrw~Wdzk20v2tu@B5Q12mr^q^gf#_M4KN~bw4(2b6&S5QpR4f*sz#NEqvSiY zwFTmqA!79;TH^$~6#QBRjx)ipG}lnUVnX#>XKNE7v&mxpE5CQ;@|&xYRA>&{<9c4; z#{oDw@a8_Bs?HFOqTUway!-MLAdu5VpFt2e;Ch3D=Q_+`e{n--*H<M?d(LT`F%q-` zFABc&*HdLb6{_DqU32h)es#PQ9AhmctKw<(U2k+b-12j2-Iw`>8BvPo5}*U;RLdfG z?`J|ixu6w>KVD7|fg@F@Yz|tk$Z!i~b#cQ5WL2Sf80B_Mxo&xaY(*rWz%*S_b*Cb) zjuiru3gOEmL0)8oz{|A=QZGv2^<F84<{S?FdajrA?k?1UBdnD%T&vmYU#gssAg_1T z{8%}&c0_6$ot;)>e9-FG0QL-mQ2cmK(q)s8OUB9H%26^@w@YFBzBKk)tp`aN{zOP{ zDVghrlYWw`*Z=7*y2zf-D}Q$m6R#!<%OK;2IeMmH804kHEju!6&=fUdb<c;%73z(t zDr9`zu)?p;Kw~~17i2PqJZ3)hMP&GB=|BW8;p^9)RIiMeB@1ss`eClUKyE^%nUJ|+ z5IU)|O&wXNx&=<WEe)DPWIadbA~$_ra!n?u@h|n#y_1fnK%-gB{<c59^cAuEgGIHw zsPY7M<7jacm7v6mka2Jg=KOr2E|=fvaBB{8*x!Qr=E+QRqa_nV7*nxql$;*3mB~c% zJu%g9r^$UUXuisk#WCs+UAqPRZoGQt=H_JR!_#WI{*PUya1Y>mR~nM|t<S|vy~2eP zQsC{UwN3~H_O4o+q@d}{>rRSYg7~w-1%|uBXb&DJlr4^hW`_$d!*?3Y-K(xMNo<g+ zagT*Ys9Lau$N>?x$h@Klsb5GFI}s!=C-NUJC_mbNxdj})fy@VfE~xqZEbJVJ@_ko= zY;G(!Vg%7tG+tfah-zpAu&3d;rv6MB4YLOdex=<pv<|v(oW(Ot2$!D{4ixM~g(ZW< zUl%r*?^^!+&Bf6g0ambyB7+U)Pjht@%3+XqYbNA^69Syf727M9u_qT=8ZSp^+JrC> zxoYg~3sICrkk^+nIfUa~R@c$dZ_tpV%p}RbENbfPd@$^;607P#Fn?y0{OhygR}#f8 zI0*PuJxdm^K<K0o4}?M<L>a(IC!T0nmn!~Cy>WA+xvm5VdDuL$tR*DQbdof*Og-kM zYA+lcgd)hxat^Hytk5|en!_CSx3gS{X&D(t;BrbR6JMpLsatWC`tQ!E7iMcciHf=L z@*^dh_X~Qb<)*g#hp!ml&5-WM)dP%RB0)MMTKwG^`BobRPU1EKALg25Ee&qT<2HIm zD9{!#7<lM&2(HwwJ|SNiuOgIb`*3q)CZKT*;|%SfdcH!zi==uXLDIAR!BVXgUa~N{ z8%?a%2VWI6p}2{Vag;i-(aDA%-8E9P)K=y*tQ%}^Z$DUS<GlW?IV67TeQV#-Sa(7M zaPSBDs{j1wKW3}_;Ad>5Zem1Dz}0%Y$h*6!vE4Eb3dtK1g#gRno+)c=Y5TgkX+S2} zleVuIUKR@h3^W^G-E(4Oy5XfXHS%D1;6xd$vC9vl1T92_f^PuVDGb_Mr0RR^==>@~ zytfp2z#a{~A4_NF`UE}*;!O_aeUqc)XiYQl{3(&@z2_9zO5KqX^~NL#8qP)TX}D?< zq53cNmVHK*O^bq&Dc{;e!MkY^H@tLfs%Xr6Tbm$6$J3t$!m1ID?{H!cbJ*X}QaR18 z7(Vc0u80;bj^Ke~RSOB61@Y2+p-I@*-X$&IK^6v-s@I>CFNomHid6xB4gz5lu!5j6 z&CY_}=Jh#}7T{aGHbqkon~I>ODJ1yW817(;p1rX3@Zo_=H>XF!#Ijz+DnMd_SdA-= zI|TApK*qCDb0KW*?6e%nlg)^b?<wk4_VYmY5Dqz$P$!(jHf&3mPQ-8?7v$PpbO7>i z=87am<;au=IyyQ6Z`=mI-T;Bso}nzAxxFv)23)BJ0m5B{%14hL4R*pw4BZ5x;w1Z~ z9pnuZ?JqSxdi3zm-_Uu9TsNX<XSUjU|Nf!tce%~~9*!nkUr&*N<NSSv+MZ5=ot+PV zy4(y7-<W_CMU>Z#`D8qz`5;mX{BA6c<t>Pm&L&EI`ox^qCi9(%vQJUX!$-po{#1nP z1#p0Gl}8BwhjSVVqyDR0b*27p*Z6;T4xSMqLmuNraSH6Ezm|^E&TQ#a1bKbw_zL~I z6tOD~dM=qi=9oqVqX{k1@@Gv6=PaUd9UjbK4*Tm{Ppr7neBX5c|8^Fs)|2^@B9zY% zB{p3qRGR74GzqB!*0!{LdA<So-gq@h<QXMLc_Q+@`!Ty~TD|EXo~~(Y?}t8QdIRv| zzI0NKwiT$Tok)aVPZGeeW!O{tJV)&wq1xJ~yuu@z=@M7xiX94hH=Go@N7XJ)RPHMf z(%|M*Cu+VrZ+!Hy8@vD-f0n+Gh!(1BN)Uh$DSGAsHm%D5=KE)4^NCy#!e21rPA;e( zW2OQG@)4&u8_zXa9`^kzw8DTO?<T5jsHuX+JOK|8Z#39JUO(Z{Ypufq&`6BJF>Fay z239n--*5jsSA(M&hP^JI@<u0r-nta1PtN(H1DA}Ck$3DN%}oU=*RvY1rx@Au^GR}m zk@xN8v^J6N5-$5RuWms9CcF0iImI(1G;od92;-F><n~)NCX}mJB&h*TygrTdS9{}L zNECu#v~N*`y7eDanR^(tks=uL4ui`LAP9X3J@IUCw9Mht9OkgUzENeZBxcL*Z25~x zvK7e<Q&SbsCP}uW$*(Ez6mlD1q1ZAoeX2kRV+Nk>6{!GN398TRYXfB;Tbnb)^?B1) zC#iBp{W4K)O?>nS=Y2l825P*U#%h>Ef;T1bna1H`m8+w8AhH(VSUsP3MblkVYF$aZ zCzK~UdJ2Kpr$|A7U`C`6?4%fscV9}Y`=r2NeJn4J(19s2^0^Uc1ogu-@u~!+P57~D zeFC&CTgB40qWRdNg5`;$U?0Gz677EL%UnPd+<VZgH)SxjeSTK$83FG(D;?tJT;qLL zj3DgV6Uggd2}0%nKufne{`@UwLWcLVRHvopjt=Xg>%X0snTCaiEh&;2C!1zRar%Dv zRvN-pITLZBd{N7oPsS-y@JxpGIa@sc(_H!5bB%9Pdh@lM2}}?mI#7P+QOEFMb|!4x zbW-XRz<K{{!=BRSkF&c=zjv3Ls6r!0-LfV{yEs;QpiuT#L*8%lHNczY9!Sp>HXk@w z<BXGT9V_x8dplO92f=(a(=52!r_51I<S>W*4GfUj(@z@a#MF31NIuDuJx76Qwub2i zI@7`!{xrOxJ02UvK^Ky`GDw33E;wjzta5Q2?1^E`kC9C!$rop}_*K*o@Cj+vO~9{u zNuoBQLd$QwJ2_l3fyiA>We)31cIT-;80<>wrJv3?y3<Kb7xMN%?H5$(JE;mBy`k&F zU&>7gDRT<trtzCQA3fTTqS$gu$A7HzJdeWljFlZZU(@y9-9^eN;Sz)_e05$O%rV}+ z_dgrcGyuc*7)|$5KxBb%88SIp84nwN>y6_2l&POffTl(&z8eyG`9_NyR*1Qx9WY~g zHjWR7nyC@|ak<3?vw8QqdJ6Xzg5!Sdd!@?Zm8$`!YHE&QV@AKlM`t`8gh~+PErD;1 z`DETGP*2O(F0d^}>e||aA*;GiY~Pj0bV^pP&ro(+hfghQYis-Jta?tgO!-*+#AvbX zNE0tk)~|`>Z_gBZ;`q-e%1PzgA)-Bl!a5UvFI@~mtLH{@@D)uAU2Cb?`s5yjjdw4~ zM<+Gb`S9VvE6w0Q<(IiN&8?O<lf_P0>0II%dp{wErt+JC5BhFS3=?0F-g5XfhdJzT zfV>n#xw@5Edxs?KSKCqqH@lK*Y=Rg}TggA$R<*%cK;qB9FsI-d$xH*(*v2)sRVbVH zUewK@s7SPaE{<yK+?FN*f&67;8M0-mwAm{H?W+qg!z1rv5*HBEYZH1szXy@+60LK> z_Er^rn8Ejqg255ytEV)F%5>iq*0|$%UCn1-%W3-8|NO@zxcbF(eHy*{tTtpo3r;A` zKCWqc^ynd)VTF+ZTRE-HWO*~Y0WE{Vjlu`6Na9b7lml=2ig<Zr+yC}*_GGS07~ho$ zPm3LD-A&kRnGhxlxTG0?y#818z)!e7Su!q#naEDCw3{cG$o!<khxt3V>5pN5f-6%m z&bo=<Z{H6WMRVN<QV#@q<+sOtGEvv{9+5ROarU195X=Pebs%&^w*WGjw`VGrC)KjF z-R%^SXV@LX0Df!~iGsMu!!VgBUJ`BR791?Ajpv&Zp+V_CmEHJEvK$bQ=K-D?shk}l z-kc`+=oFO6?+xq}3k}=O)X$ET9xScfJ>nYYG_LNIG}W9K1%`&UkNk`ZgAfP=RE_cG zT06aqRbBak9$}(lNsGg$Im}^yU#rkrzA1n~0CNFJu<At3LK0_rJRfmo^CI|Xq$8T- z12Pj$WDS)}4nkXtpMzQ^;vuIneoq&Jp?-^HfRx(R@x9`KUL@wkgxZzK-7Ak~OZz*g zYtguOmw-SBAN9AWSUr6*9$FG7y4P-9NrqM@Ya+|LQ>k~ADkn!t6WHC(R@Z#@qqBMt zB$`BGA_Tel_xqdDv;ZfVlsJkUmDw@X?j$tYJeR<`+upl0xvM~lMhWoJNfFgUjMc7U z7@5rM&X#pr`{hON$dUuRa$byZyuzCy*9^!}V1xOmYk$zRcZdCn_SU^kdA|Xleu=Ul zrHdyMWfQ{0MSsG*$5qx(ic(D(WcIZ=kvl!f;DYCj<N<4MZ~rDwJu9Xro!#)5-QS-| zK<0|9#bm{l2swgBbEBbc83U`2k%wnRA`JXRREf!Kw#*}=&6|X6?Ycg!mac1?5g~K( zV@(NRdWCUNX!B)(P|-_iwJ*hs_U9|H7d5Cfx1Vjb#fP&xSw0f^4mA0J-2%MX@8mR? zY!!2dN;Jrs%nav@ZI)FEo2Fx#z^1q9iWTl1t{sNM9OkgUxS_q&F-(KGUE6Z6)pCFT zh5D}k@19}M4;Si&brb+GKN|4oZa4{BtsQKwc>-QCKU%P>bSTb?DTd~dWvKF1xZ>56 zUef19WV^*`-5)zB>%&L;E;PBKfZNp=m(h*9f!r73B^5Br-?wC{XT~Z%d)$NX9|iD~ z2q>D-t=nwvu<pHd3j_)P4(+^i``*9bJZ*5o3TBc=fxPQZ@TZ`hw`x_QO3>J=5q$<~ zMfLDQL@T2NLkv`|tko%y9e8<QQy%AFIv}doBthfFc{e0VUWwxxO!xL)x>;UxXV{<I zZ@0df*|?hAub<#YREZ}^?uOwP$;KYiWa-<yqE+Bfh23xci)G7G8>U3^M^?4n@nf-W zQG&W!*R6c=(*@NeB0K}fx(J)zKP>@AZfwiamOjzu*kZAO5L9!+w@m+iiFN{xi@RR` zMwaTmOmWX~X?pWS9DfQ<cwTlZkKed48R~8=g!Z0hPa#UDkz}ZZHTw7=^Svr$zV}_B zY9uw_KIM9sAl{qV24!;_GUNKGPzC%1%cA*XyVn$*Wn+q{``m(fo?$G9fR)1>_Vl1| zhxNx|J@DZI|7x#r={MQ3VP9}#+k?3zJ~&E${j{OY@&E;5j9^wQw4FLA=e1g`-xt+Q z2$Q3PF@X2}>D~e*4;*_!taes(Z>4Q1WCUScG)MF}ed|fl3A$l*G#_}gKFpGLcB1(? zlOy>~F`Ax!1c!>@^%Pb1<Ln?bGdW8MLZEdCD79>}v~SBcfIuDs#3LV;uk+=o=H#&| zv}1zeR?nW#3R|3zSwX0zZ%OQvcBohfe5$CDI|H_H4MdPvJ2!?mUS4!k5aWwe=rrb+ zt2a;ahR<+AwsiNQ9lX#_`9F=OSrAtPg4ks4*jos38rmjDD54qlb}#KvF0`ts2@pqA zPu<zkao~b}QG$xA??&G5&dJuM>AyYG9YX-eXzSAIaAkJZd3Wc@T|$K~CkbE5uA309 zK$ROqWN)2qs#JF`g0ma%BFO6!zzQ#~E0wmak3;M1kSW$n@q$C=l}^Eo-nNO}Rko5e z{^ryX&6H$D^Y+|^1(E2i#%wfyPDN&JoVU{CW4qVLuP~Q2=(N*TU$8M%-P(5F;jbL# zu&0Lq63TUZi|Qt0n2VCMr|84?+S9lG&kC{}9IbjWtyZYJ3xE%ZmOPUpUrQVmc^|g7 ze_7IkR>-0gc>X&Xy{+N*<v`0R^#I50n;5e*Q#>n10eo5O<G6a$-OtWyz%P}boUP@k z(WIR<Nut&1hFv+`rAdiQ^&1&#s-$;m4}>rQPI2f$ZPz~M5qJPE9ToDr6RVvFGMg5n z@)Xn1Ggcv`8Z_P1oAv#<p?VcfCozF9EtNkoCmc;MBdR<|yz#nsFD8iG0&k!?V>ir) z^Ascb6*yk`U2$FC<mxn<etw+NHI!djH}=K^C2F%9PK>X#uPvO-Hf+wnHJLP`bzTH{ zf4NY%FkW4)?`<VdmE8KV>}FTZIPhbwPN_XqXh-W3x@mF8L0@J|nTES7V>vt0B~vlX zBG}v`*}ay+oDp6P{Mk++yh#KI6&^oE1HMe>K(>1z1Le84GH~Yr=Jw2*`H_<8aWx0Z zMpSI|=)r?;&fi=}MC;>duwh5K6#R1Iz-41+r*&*M_mrR+jfC6OSyv@%PgLn0{>otv zd&=;4IYqdFdNbnM@Nc)#(%}`#1U{@6DB22T3yQpP(#fgtJWPRYSC$-M%a7OYWN^{Q za7TN44p)aQ*S=~K2ex&ilPrqpt!jBUL%1s4aJWR%7kOLTEFb4L0B@#SFulE_<LwLu zIL19(ir|G6*=Bkqe+fnZ{bS_CmBFv1D1FL%V~=QsF9yP1>sH%vK@J+B03*&aZEx@R z{#-38*Y8L7ijeQ5b{`0{t8_<^3Y>%y<o&g*ehB0R#0ubFb%NP60C}SrH(kS|KNl#+ z>&5|p8o;7K^|vx)d(KElhP*d^zIv-~jxqkSdUlM$1;b-B{&VbS0(Jo`cP7Dpjcp0P zacjZt=~2}qd>Vf|uU(X=yQb>R_)6p&!>Vp>C!;eoH-E;~)A~}V@yQ<+tM9sm@;^!+ zR8^A7gtsIK5afM^Bwm%QS(jS7F|%$-G=EV9cU`K^Ey&hTDS+t|z{uts^CUO1mHH8q zR+_B&ogAZc2-^Fj-D3GRR|yW@*mt4np|yQ%ujCm?EAXrCBH+#h=|nus;i(+vu&2yC zi0tKAEiu=IpWavKEH0>&J$rSsx&$(UU8NvO@cbzmhKfda4aU2>i<>+GL@y`Hyf0|J z&X&EJC_po^{21K|9$4u!eMJ92Fnz{}x-3q=a>a)at^X*#34W!&lZxhIy?aUyj&b%C zYCEiG+ud0af<=ki19{z)r+}l|cB*D)PA{B&kR^4+N&PNqyXb~XFbjmk2uO_-d9Bu8 zueNmSPh!QMk=<ttwjTmfB9M%V6dkW<9-<}}U@CxLRV;lV@&;bjPbMj%%EnE3353uA z9$j9a7|wn!VMN7B3nQz)J>O_H_tM)k_~!CNwR3QFF5EiiGbzyCb&ryPgXMO=)i$<m zFZm4wjwsO=8K{5cs#d1f%WvKr@Ok%DAFBBJLQCu2dr$t5#596n&i=E?!Qc`>-i`5s z2^bEA)o@jD3u^fNw&g$V4<EI)KWMW2Kc7=&;5Y*Wu%{q%%i(hM2iW#R{^IP~WvOU9 z2Z7%2^J);z`*oq=?!B?}r9v~|%j<1Q8n)1Pm=pvs9B#^C4tvTB9IbfnY-^Qf_$(co zp>rn{@dF&^<BV_5!qIF!!1G^Cm68g&)>R+;d=)Lf?QYQE%|HMY977Y8Cx>w+B5X7q zn$w5#0w0<uSr=K^k82yvmK`~D;3(tsOmq=*X|w?N3BEX^(%JeOE{_!~Nvi!Zx0}~6 zA;U-68f<0n8s*^v`Gj!!zTED0VV7VwAi#ZF*A46RK0K`e0cbx&Cmb~8F;hcVv;#R) zBcW%bIETxchM+>yTmPOACQj$~%N0k1s3jWY#10pYTb?S4yg?}Eokn0gg^sAx;*WXo zjx%+o+}=fDj@mQ<%Xh`{Bk5z8Yk*Cb&FM8SC)y{Q_bm56IDcmvjy=M@7_TIWp3P_y zHQcrP?G%5;4_8c`9S@)U%BNBff~$|7s~PM-7K`P}EU70!bho|Z$)6w0SE2!4U)J_C zaTzqa(Eta_K!$35maO|;7puV`+SXH=65&{S459rf)5JF2W?MT>ycC2wIIqJT_EZ`2 zntyUexh&PduDd&ujT6{LFM_~5h_}C>hFk%I1o`AdXx<?-+T7mWz9mft0=QlMNnJ3U z0~hMP%2k11X<L#ZGPiMZdaZLvFZY>6V9!a?llmiX2jY7Z#lVN-b3u*#cV%?72N7Ny zFUFLisaYEncw4fKv5&jA94&>`Ccz|nFY;!v3{xW&Kjy=oHpL{TAUeRwcjc)^J{oVP zN<okSy@XKlG(vYWmC4-ZNf68-OLwG;kD$mqDCf<QnVf>yq)J0y{}1U*ADxm;BEbjp z#}9dtOADgelGW+%VIxA`UkYp9&(W9m=@o0f-!X&0cL^0%)&DW(GqIS>U*{;J==Rrn zAKZWNVUg*DKGkGHkH1Gi)r)6Lnp+mT-@crt-Bnm`7kSCtW`y$wU(ycV|JV9czQQ?x z(Io`k*L*5;tK#@*ozZc|@e=iDGouI`!wb{~5X|nn3n5qQz@Zx(Qk2P++OfToS;7|J z%kC<#b0^8?#tR&t%3%(B%8)nYhH3E$ZB5IFB4cgs);-1g<;hxQbK5=~6C_AMj2P^r zcI|2go`Gsn`qABdtG_vi`athuEeM4t2QkZKEsN7y`qq<9!O`cOtj`tS8gM={@N5t) z{*sEu?taLTPK#2kjzf_b`PLoBc<t=XJZ?9YSWV$hjg~AT_VVb<aLepC)ko>#HWM0C z1sIg`u1^?g6)kdghcDK5N1Kn)zdhIO#&5R$&&mYF@+jW+lfs`$4FekC^#jCR1x|rg zei!>?VR0bS+Z4&{*qR(}{dnEDX;B<k3>t*|DjRw}fjbi9O<**>cSfJnhx6XI{=d(s zXh8rgmNWL;=C-!BZ*rB<H2cfnR;zVQJhZc*VI;cEwg?RE`D_zg_nX~kb)qlhr>nQ0 zocSPM?I~4*5bmxV*f@xmDJnlwu5<Qd6hH&pXJ9IHYvOIl>&@^+k+);CFVX%B4Zydu z>n=nwni1q(9WOtcD;wJ@`6I%bgLz#UUy~x?btxi;r*fFXo+j(CT7N33n~W16NIEjN z(6PUye)UOhYukM)$1p!e3h;bDD(&(Z>yyPGL<j=dbECzHbd=0`A=pRK03Sy5_1eWr zIuO9wozq>u1cK?aQ|dYTfyV{tLL0O%Ux93vTv@k}tXdK!+*b^B5xv*XH1)8ywG{5O zXi48-vf|dgWr-RuEMvK?SP3PD#8IsCGIho+sdAgl7XuuwJb3k17haewmT5SiJD&eh zmhAAQh7RkX$UB|FbipzQ@Ft<!v^^D?5v@$TF>bw9x2EwIkWj$IT~hD6U>FHaE0CIA zPSf}l_a+Aa>wo`mO|lAAAtP3d`Am>Q`<3Z^F4(7vC@c4&?YVll^U;Hm><#!cS7x^4 z3!Cik>QLqWE3Hp*UNnS4=7KQ6o_rN^aO4fEs6&vq7&bromG1>Ls^W5-d7wbq)6Qw+ zBXES)=Hy$|g}h&%RRLe_vN+kk>@hYG2ESGStOy~zE91B`;*}ne9EYcJn8ThXYcscf zbFOZ}h_T7iZv7@-vxcH;Hs7bvXz!)Vj$N)ZS{^6B1XcmO%qfKbl`X7_a)EOyfLDTm zDk{5f=?N9Ua3ksZ$H+^YLor;}{x-mgx#2h<P#n)dqn8zmTdR__i=u>k3Q)Xo!7#R; z)>y6Gj*(cQemM<x?UN59G+HL(gop>mF;U!i3FnV0!wRvbkvvp?G6ZqLv_0n=AKE~c zqchEkmb-*<KFgLLyE0U?c{*9>f@K6;)DDO?zn?Ch8Ka~y#;rVLPqysM({=5goq^|J zf5z`4LEaMh_MCVn=p*x3({lIOWDUS@0CDV1V3E-{R8ku-c$gO)zwt%s9gAgT$Qw|( zCfjsPdE4Hz*FGa$_~Yf9b~*1_Dg>|`%uvzhPX9|<aO?&En1RIHV*Mc>tz|<vum9kC z7y@~Bo>i}o@9rGEI$i`YA~%v`dh8ex6JTf{ObmRgK0Pg6iA;@rD;=K7VGetmtU_pb z|FmWYMKnThbN~MRuL=z>o~r--Uk{8%;|Ce?ggzBC^T=FOdNGLSj%9XvBnsOILL>l7 zFW@w+$ZYPLH41{6Ua>VdY6l|k%O^!n7|Ed`l=F%ket&^d>w)JjrwDCynEuh3UT#&v zHEchl@*wmU4Q;2wPQi4~2$<J|mdQ+w;yDpu_ux^~^G?IF0Tu#548-qk&D54bXc!hl zE_KH8o$=zA$ovBr8mxnJ-gy+J8wsJt$_lubdA&`Ao*{AP#i>%p4|y-inm;?MHCa28 z>9xMhKdjc_W4Wd99oKMqT%W+Qu<q9CWG%q*UH=^N9x1Ioc7D**CWQ08Q*eu;G>v3$ z&P4Xotd>Z7nf7h>AIy)G>@BcEuF<L@rP(=@AADIe1oHZoYMfBcJ21>*Hk&^^1pz-6 zIL<g$q8<b1-JV^$IHEh|A4P8hhiUFaXcCD&wjuS1X1jz-fOpmIe0Vt-elbDpkg;-@ z!=5IK6}C(yLcSMNBYd?HH;YW~my#8CEf2^--CJq0r0QPeO|Gh2c0%P8%zHga)E(cu zqC+Vpw))$1%IPN?0j9S;0uT_4<4k{|g5%kE9>B^D7NW>|^Y?$hkg8h~FCo#;EGghk z+goPpX>q$J2VQ<c*0&pMqvhf3Xo&|NDuhsBtM^hwt^~zv6lfH5Bb1KN_O2FA^U3n} zZ2`m1($WpdQkPKSt4X47&(*cG4jP%?MP)8MA)g%!?WFX^Zx5BJm&FQZ$0*4a;}&h! zn(q0q?~9xMNi0(zWmwuQ!<UZci<{lU;ZxPUfm~^m`B?;c1KH~n$1W(1JQ7DQ>V7F4 zw4CRqlj2nw4PRxC=q`XT=EC%w;a6(y0^DT38;379+C5O7tQBB|Awwc>=vAFdAe(C( zn7(B+n|Ei+QO@hb+?NY?cC?Lh1UlbHmoFw&cOkF;RXsS)a>0vdk;ck-v21`7Ay51M z^QwhJ-ocCdacM5;Fo!wpFJc#!txh;u8n0!<$lJO5d?PrF!U~ybSQ9TyVD@@Kj_%G2 zDO#scZr9ptXQ%bMvvL=l>|-hfLRs#Kh9xKX{g4;WeNylD`y81oUUs+$_2l(;EYGLv zR>X*KWj5pusM>H!)6>9rXO3`bf)pI;Mc$nSI;RlE1cH43Sv1`JQlh{m9G*oSg#+B0 zDV`Pug8({$M-!uDa}v5`55GSnMU!5<t3J!s>RQZ${d_ICVtNeZ87*1f2YEMBp=sfq zd2u!4xX#;Xw)k=$T$Gz)D|N>h|FpCZAItsZl-L;~dh4_rGIl5T%9~oBJBc<@Tpd03 zl|<xr_+GB-S-yYLf-b1moN9bKb41_{u#Cm0niFYyyU*SVvf}Fko!tX%&VXi+1lX(k zA&@t`q7IEV@2hz7tBA^aR6)g;^+CFXt2T{x5P<i!H_kRaur+j^NfLrE2?*klZWzYK zRbNAu+@#=W73N0$>S*B)CG}lZat?Et!=4Vy;Wc_i!<*Aa$_mj|Eafc|L(njq)neJ5 zQ<GSIv&X$y5mpjX4MN3-O3-==tJ(ZDRpw5R`xYw~#Bkm@|C_ks_nuuXisnor@e!6W z;1B^9)DwyDho>MKjizq-eO;<<GJ(H=f`;-uNNlGNT2CG?nWo;HrdS%;2h9=ltwQD! z6K%Rx0M9||dA&!GKJ-JrY+jra_*DUarW0NQel6>|YAUbEDTIeO!%uUxYU7~DyPZ-o zKS2(NV$erXvp{Irk|dr-Ruv1H$Lq#5wp#X<-9F80!ju}{&rwv!hTk)xT<3%lE+xzJ z_}!6kaf5kPlG-VVQ=}Msmq282qM62zGQ^#omcfvB#i_=Z(?*26OUc~1X~rEH61xYQ z6Cqd=XTM{`A_{jhNs!9DIRx@XRW*R)l}`#~pqEf(0NzaGY5Y7(3K>R^yn$8kU%2z= z5gLs_g(z{zJdM9AS2;H14ZNgWlcWI07#K$5@+c89*|4@-9L~yN4tr|sg#`YGd4>we zIKu7Q+pRxbXxf@A=<-)#*Bi0r{hAzs;3}suX|eQn*9ONgs1VNk-5GgMiDGZXKXt8^ zK0cN|C0h8T8_ec5O^s4)NfJ^h6pQuYi>cZvc;1FY6nUqFF=ybbdytnVG<=w?+Fj5~ zB-y&w=@B9jDgj~WZe39M4j2TEV!3TW40i%a-mT*6&yAuPyADSYm;i#j$LVjSD=rBK zRaz)z!rRb_V3r4o*_ZRqAaW7Tn|x#3)_D;pzo)dtyHJ@YH2$Nw`4Tid^3ru}?gZK1 z>|Uu!n)=qVgc|2i;aSnmKZCpxRZXj+*i0C<yFsDw=ByM0z>erH6mS~XoizcReeDQv z{Km8}mfdqWT4`_!<6+8lLxA0T=`s+)03<t{H<5c2)qe3|?l~hjnMSQ*?Sf})NL6$l z+Lg$33P%pa|14KMwjrIFz;z=@obZrGEC(E;y_uye<`^8#%3%(BI!tG>I0do%=)aA8 zW^S=qzbtMHxTxv!R{>Y*BX9I8<eV49aSoG~Du3(RfONgyjUXeH*S?=7`lkGkKki%m z&;%yHR6mKl1;U%tqm|FbaD6D0j*gD!66CWYME~fjDhjNcP3o;(Jz5N}O_KLjPbyQJ z=Em?quy8>n%8Z=C*eLQI9R=qF!8E6EIa*bOR#UP*IW1$_mIdF)5l)X3A<p?#3QQSX z@reoR-lK?s4{aVv*cW-HhI1Cgsj`^kR?lm;+}~AdI+hPts7;?2_trQMVHcIZx#6HA zMZKv|*&M?{vJ!*{zNSJEBj_bG81hEao92_)WZ3@vEl1GwOk(|DZ3G8ciP-pRe(RhV z``Qu6PWGLzx0_+LJqHFM)$xM}dUTny4;|pE?ILeHy|D{<Kc`B^fV|(Fk$!viZtFd? zsnF&$;j|bS__0U6h%><EBy%Q2LI`VKm&l)i6L`kJt0|7cB8NHbDY5$2d#~i)-gt5p znUlzt>*9q6&ce2TTlW^IPq2)AfBt5Q03o@jxJ_Lf$1)o|!sU4a<Gvhd69w+ij+tmr zz$cKGuDLTewgz2k1>oMl+t*TRpC^kGZMjJ<VT_)v=?FBFS-5}<_3g=y)-mP6z^D4n zbd-RA<JSQmUK=~o^r7~S_FaV<Xa8=odf>;Lj2AF%l1Cia`0;5KI6_~WAPX#k2ipUz zGk<al0s&Q^59i%k0L_iy&WWozF@Ed3$klm<GnXWwt%qlm*xRYYuNS0MH@Xqz`*M1_ z*U>b$RwSwr5C2Ls|IZ+A_>HDX82SdP{b4DEnrEHR&m)XTK$5R--H>aVf~rm0NgxLY z%YQAm4`**vx$~oh>AdD4qRqSWYZfCjx>ESbuVT<>Gjh{eZzhY1xuYhSq^sJ)jQg$k z(AC&~P=#~jVBpINDbtQ^NGHW|T!<2IwCZq)W_^;}EmAs<>_A?JIqWGhS<^qit@xLB zF+3VK+u>hz*YCWl3weLNSbzLNzid)Mxy}_Y+nFiua%jI6*GweBr`Qd|GSv!FzlJr7 zB000kqP|htZrWIPdv2@>1hK$&@}ozc%gOS^v62HeX=slKmJ^=dgAvgc>P@N2Z5gt@ zF7%A>7Vu_mO4Ddt+Cbn95GE(wXdIyfY_nJnU%P`!Pj@3P`-gm`(Tuv&wl>S2bG6_| z)xucl<H2E$Q~|Gx<9Xuw>^=%1uF)JP3~P3*GU}RY9B&^8rMueT4EcM?2*efwFR9Vg zwLVG6LW5~lk{T7~5226wG|+0D%X;8j3CQ*zB$~!FB|?$NX&D*vnqJJ%Ehh0Z7-+7P z+0<t2lZre$i0)lsvYY5@Fq%I<ql%;r#d#Nz`I{&bs?hl4C45(c&Okr_Yh!{Sku&Nj zF+=y;kJtaWWLs^|7B;__szn~cgN0*U=LIA-z>7fu=QFB&Q7q&XF5Q&DclaxZIqd1M zu*>Q_<@dymqlm2`p5&u!)vtxh?th<A`j+;quf$%`xZq`TquE^>{E!E`5@g=zlxvd( zURYM&EmtP+*Q7uj$O8%KCD|<mq(A@@fL&;pgc#wBNb&X@o00~e<<X~~Zw^!UR;IGA zBBc#^O~8k}ETM)_u64q40YUB+OdI*tZKEn&y2R>j(Pq-M+OA_++b#Qwv<UJ(6CvI} z;SRP3DpR*9K`=9>rm)Y-DZ+n25c74aE@k{UFJhip#=>Yc_C7C`1A>QduXBoPbPtz@ zT<#@e{D$99<n?AOiIrcHna0d{QRF>-eXtEK%5P4K(Hy%pqD0@@_x}A}UK8-H{vboL zIUD-8(0HS!H=k`?4DZ+Lt#*+YG8jLmDzO#yLvY^1m+GdI*gFgCM8$!BB?yO*`QEZ< z?w(?3w1dEJY}<47_hcG+e;-(_ALTcKW6TfIBs!gW3`6>Mns|1!3>>;1PHTFdB10hf z<xH`|UpdTSPlrt;u~#O_Iyy$-Z&A6PGn((u$h!XhOKELbdB2>NT*)njSOF}fYlH7| z<p>aemn}h~$Nv3V#eqQD>{!uu%0Qw7H~({1l*|JsE`w1ln1*34ij(cJ5%cv4+}RPm z>%0dF;8heD^p7^jF`IxtXT^z{=nAbX302aVg&hT<yC}Q)(wP<nyV16WzHEF&T@Uj9 zP^3eUcV3j_-HbuQ*FxC%UW#aXw6aLt(tD*u4uZS~E;a>~jVI^boFZ64Mn~hDvvTn3 z@G?g~79g`AxzG9DnNpFcc|p7y9HY4t<gcETj2U^qEl^Jg;hOGQ?C#-$<mQwJCFW|~ zNcR8mQRhn(74YTYD)jHBh}=lZ@|xZ*TCO4V?@R0>_<>@0Nt`&P+BgLAZl;Lm#PBvy z?A$(pWda-od|4Yt$$7h;#u7>6?kj&}+w!0)WX)SL>k;JLmj`!R$1<d8H0?%84LC}B zCsV#CoVzq$zHc;vDTg`CVSi-<Av6#$I`JZl<$*8LlFII0#`V8aA5zvY+8kV=bH<AS zhHKlv8d|P%#!8NzQ_dyvfM0*)4Q4J#5bf;0IEXIPo>x!6iKpWEGLuE!()MhkasrMw z&nERSmB8w|&Wmu*c_-zr{o~c2<tjl4e-cT)I+6Q)Jl~lBzmYZy3e(=+zKN<^63Lkn zB?CUJUy787OQ$dqe{TKxLLE3<wLA{mpEsx*ED##sI4PPHQ^Tt3gBQ^#C;hItA@D-Y zINrV)B-Ykb=%Vf3T={B}a9CWML_okr)+NjN_-xIuq|0ZJq`;r|W}1A=$oo@Ctvgn< z{aoX&0_Bq%6bf%ni;%C36&vqaN3ug_|0)nHJY1w&nJ#yV6s#ro(LM9Mez5Yl4tp9? zDDuXN^5l1hxQa<c_B1lTZ}MA@eVi$r7YieId=8PbZ8V%0Iow&H@dT^kRcig_L^LLB zd0>4fyAB*@ze5oT$HaN}6hH{t0$&E&aPvsjzM|S8q{1EMFo*s9kQXz$1-_2Xjvp^H zhCc4_z9S39T&eBr>&`+g04w$g?XLZauBrz?f{@GVEy)sa{6@c{W0)^eAacb(<c+53 zyhw5{9A9g`Um$2iNZC6XFvlitfSj;bIJ<}QQYzG2b2NR0h*6eZs0Km2xiJca;=OhP zRniz0@^;x7ar1%*gaOf=0~z2r-S@Kg{(}c!Wy`@~+U8W4${sWW$Aetqgy+qRttpfD zR>)?GO-@18J9F#Du|F?jhYOXzUc24d`RLdMb<mYMyD3S1?O=hzB|?)W?3aUNX@Brq z8bX;_dy4e1X^cgquTjJZR)P?b2hL8=JK&;vayaBglt9h@90`y)`&EGu0sgvf3XZoq zSwt1~J|T0$Sznj7wA{7RSBpY3Y)qD&k+cqhyugR%5nesd9_K9)8aJP+1IHNC!`bUI zMu)t|OVsnDp$SAOu&ryLN%a9$04v>+#4pv2Wqo0FGT$poiYD0mF<kKSRWV$LdZfb~ z_SBejFwH%jJz9cu>`GG{t*-0ezg*A~D(d=fkO{STl0?g6#1A?i*`$FQ08YN?gb;yU z5Y(UZV(8nm70f3}QJ8h+nUOM&P+pm`m9Dw9fUMe`uj#Zk=5xX`=STHMo403hU#F_p z_gN`M^H&SCAec8PLcWB|MSuVh6n*6-hqXYaF0`!nd5Ri%S9P!UdROl|SM$)?{#h1+ zP*pD_%Sq*fy3^@op=&7fnP_ElpUNXtQaFUW{Y>4l;_*b@pYmh}QRMB6s4{++ThnPB z1~=K-)&zl>YrH?ejW0{!&&;adlCI1clei_aw--<FL9l3U9Q>}m<w(+1oflC$fgr|} z>qfG-MNvEu&ikRT#x0l=dZpi5#?N{3uS;5>##VdL2E*%VFo`i#VKNA|UsYr~ZpWU& zTHwd?z_Df}az}Of(KG`%#sWAQI8@&C;Y1wM2`8I?WzHir$1tScmz1lM6u^fL0(ncK zgdb)I9nQ*O4tshyZ!pa%Y;>NX!)o=Zx_d@!?D9FjW%Wrsle$lb!RQLzOD80&PS#4B z&|;jmv3x*Op`8Q+X#*530D*Jxvaa)qFxt^Fos%DZdL&G(Zpalj&WTfgd|Ih9p;5a@ zc=p6T8CKvx*$Zh3OtHE@^6Eh#cU59dvAhNN(*RcXOVLPEOv%@E&m}=_7(T*tK>(v` zsrR{4O+<PlSFL+14F=wfMbVPa2IoZJPyq;G%!pDDuJoQ&uB_Ds!*Pp)S0#>ThH6x0 z{jTDh9S^KzcJqtz0;+I~>04ag-LJ0w_V!o>m(UEWD+u(k-=^x=_Szx@m8vHb<qIRY zIYQG&_U0KW04EhMB#Dsw7{}?iZX0w-yD?MmQ*5X2EvZ7i?t~;ue0!*R-bl`*82iy? zbjqhQzkNpYK?bBAjfNF+xYIo2oCqoK=l>(GyH<K0soDi62f++L8VmBmdecW|(4>Ne zG1B--ox@W(%wbOz@`lhr=;)%&hSq!EU%Yt+LP-Y`Hh-EaT^a@NLEFc4185FM_iD0a zQG)uC6xDWE8p(Ag0Qf)$zp0)faY0~}7pb56&>S-7!?Q{Xb0Fsh`$~ad)vTDBYsy<! z6)lSrR9jQ!Gzg`TQ^Qzu`(#+XmIkd)S7Q1npZJ&QKmd15oap#vtrNmn1GpiVYX=iH z?CU?DO_45$Qvg3U%5xFS^`^Up@Vr79xw5-x8{AUpO$2$5(O1Pw4-LM~8^|;`2Qg<x zDHHq1Rb`7?okKVu<kxS_l8oc+JH=^y<8&S0(0W$diuPyRMg6ZlMabY_Dh%K-^Xp>c zg9moxqr=LyUMVe~W<kT=g|59#!0-T8^g)iwHH80hrnE1TrdAtYqt*wO+Cg4(d;9l= zwdulJLm;nfBxhNoa2I9pY^932k2B>4)9AAOk?qgdm?vX65o~k!obY%R2$P{%TVvw9 z*fQnw$x09)c%IC2xG9G@>?yOg$-GGf#;ATmQyM?bua9S;a9OD}uZre_pDVvEZtCeD zWNmLha9%etT)saKb&oG32tl~YCWB0y(0~5#8qN8+tj=N?=#9}DoIv_B(KY9#E!S&I z3*uEfvujLd)EOegd6`YNx68lf%kFGRuZixnQY>q^`%#____NVwLji0MzyblBkSoIr z8a#UN;OFbVgF|%mhrV<#9B*?9ysuEpe4HVZNK<b-DMx6?`=`}xUF%@`;n%l$g^OoK z%Fl{=MWq<^ZSJ9huM6w9oDz&9-f<1T&rVct%@hV+s&xzH=ZIVXTgdy%g<5cgy{D+Y z=P~Gyi4UJWchh5JO&O5AeUYR1N3kJ}u6OgNyM}PLpXwE0DcAh|YF7Qh0z1gt(b4hq z1-(!CP$C!rOLxLo17gKshYl&L`!2txqhl=X3FZ^n$IH7_qusC!C!B1|ocFn8{?tfl z5(!=!%XPRZhdJyivt#+P7f*;r6=Uq|?C_)icFv|4K;{}factnj-f>#pQ(}qiE$FHN zV4z7hPrE#U>lB8tMfvW0jYnudlwU*^hO_K2lrH3*9;ryE)W=-cO^<><$*wWjniPN_ z+6;WJ6jEu;Z*OO5sGQz#+(miIj1%?XSQV;A^eYXeuKw)r^VQnc`$JzBG65)tTi?v8 z1HNo<jIrai3Tn1+)V+M%r%b&rNeT|rb`|K%=0Uj|U4M5{7=KnYTqNmTaIC8P-91#e zBd7MAQ{%~bMJ@LylH_xUoYj%s>2bB9oB#T6JxB{seQfm<B5zz0fgqg`He1o&97Q$2 zk25Juc<54Hg>4Fyz-gHn0Rb%AF7p0ZQb!z8JrWSvfKUy5>?1GJXn8F~{86TyJNk|Z z=*ml}_Ve6Yoe`ZPu1Mgy5)kBN1L9bgXy+vIJxCC8a~C;~*I^EO>TFFSZ#fwnmACA$ zT5+5^84_DV1gq8Wq81!w%qNOYR2sTsiOAk|WWXRoGbs*T)BKPMd6FdWo>F+1-`a7q zpBLJZuk@?5qkG_iAa4MDDuTS%bYD=>a^P?C)Yi5(WJ@m`dmfR~^An$n#@DE7GOZVR zWzB7eOZ9IgiFRg5eJ`r#5IEpC^Xq~-uKw;wuC7qrvLH${Ek=FhqISSn+j5j3ln0K{ zKRb(f*Fj4~Xqr~9aLL>l1=r9Uu`H3BJi^4Q($sU~#?z6K+09OY>{PBXsN66)s^+@d z{9k#BklW#NSwH1O<D2Q@Ij`8B0d31v0e==6{|}Swv(+o@%z;6ez;4?|^fazE4#>2B z0(m*MKlYLL5YxCYit}E&lr#D+frxFU$hM@yR3`c~?no6+AjoZ9o-4+%EAJBJvxy22 z3V~p{!%aENVNaQDJSlwVTyw{0kXPT>y0@f$x2*{v;^LC3o5A5K@N-p2g|Ta?7v;R= z25^Y}Z855N;9UT%N`RL|^4`u;A3dk+>nCy7>j^YF$a|nv4g6`YVWK_x@cL9S@aKM! zqcWON<aNWcx{$Z)SJQEvT}8US8CDUGl?@Ps8h%@stUxGv8mDRGAmVrZ)=aD&?zR&x z6Xqgp`uie%yJb+$Tc&LBB0{qxr6OZ*1<hH>E%#9Vnv|M(<4R$`&Qjo0l`C%9ou{4@ zp*$yT`EMa_rS@(TtEqxx9JhUTbiABa1CDc%3-rPZ7DsV*m7;ff|0Ny3@Xm-#cAF-U zIOc_T7MVHdGVaUC!s$dV*i|~%g&ZkTtv?|lF~`yv2zb+;Pm*_uLjO{zoJ52X<efLB zXfs+>6e$G+nKQ{D*X%HdJ#8j1{Du+z=bs&qM*fpTrr`~$HqDl#YLm)0yq+wMVKrY? zbvHah9U$`-I6(WZ7_A4%q3gD0Y7wX8946e8E$dH|bo&a_PrA@~WagxBJ_zDX#4*r# zoeyJ4wD5o}jmj;Iu`*WJgS=C4%<Va9lC2h|>qCpRBbwd#!b$13xvKeST#*ifxN}IX zUCa?Vz(QH`s*@V*Rn3!M#j=}0Fc%!H`r&+ir*+VEz|%a#lyK<`g22$$n`=~}ZF9wN zUpy&)J5xT6g_*kw;Q5i<Bj?qtqdA^YHTmM^u5s{XwHzpf|JD_t$jOJ&8`qw!nT!!{ zI@4IK@7BlImIKX=7M_*dwu`(!m+F9b<q%0t2MS~>;{@~e+23G0iMcFI4q}Ah|6}h> zz?wMMzVU}mQQWuIwyw2qt#(n{+SY1Yt8KNL)>>Pw)+%o7TL@tZtL(BvL>3Vd5m7`4 z*(b?lpClw9gb*SzLZlJXa`JNc{P=tEp9#28RP?m(caA6bmCGxJnP;AvJh^|*?_PJ5 zJq_~uQf3?#zC*$`S9^+)F%c369}0o|G?B@ZlUXiAfg@4kYPUAq#V*gB2u;>ck4vkb zge$$6$QmCbN@f`bbe-Qt4Z&7D@~$RC->l0L124{xr<?ow`eeHHO@$h8h&-CWBV1^_ zUr%~pYzFH~{(K2_Z|~Oy$ns?V?$ZjJv61G2XBP{xD?T5^`t0PrB80%5yev~1Q6luG zsgH5nwiL*w;_0&!<sgWMbvgH@0bd&MgC>O2*H?@Pd4EC`CQ%$UToXWdlx+pD!J+#7 zmAYZ;1`kLkO*0`%jxseO?ViPwqrd4I0xwJ8depT1g@71Qqki=$cVZ}GY6NRyOhXZj zh4+Niw#-aveleP7w)Xu;FHZDcDC*8Asv#L_N7~E-&uu=DHY*0sd`Lqg;Du$bJpy?l zb^DL!u9gUg4@FxVQ8Ouq=MpK(emc(UPX+|;KrK_^rKWKal2HM4n)YdmllWH37AH#q zUgQ`?v#Tk)*yY)isiCwrS-g=SpH5b+$y3I#2I-YYm4<lvfqJ~?`?rsYfG>9)sZpyn z`PL|AC&_?k{n#+x{iTet%0HEj`^pr<s?aK-e*byxdKAHJWqbgheL`r|TP)+l=pP@K z-39vh<DuobGXE;YtQZJ{DS;Ogc+fGPiv>0_K_J}?&sv-;^ExZ<*FIjCqGP(^7+Z=| z#^C~}P_n-wO#=4Sc~rMP0zJ_k!lYQq=eZ(xM1PlwXF;u_LwKXS>pd_2mB^cpm{uO+ zO%7#7Q?Iz-I296OKvm<EP$39_e>vS?vOI(J22=S)Cj#HUa(HJh2D?6W0DSha(^YNe z{q>e4O4G1^B_aAHG0><mWFtxZSvvVi=Z4`vjx%7&W;UBAL<pac77MT3c#7N1rKnb= z%K=XKq$TF;Vi&voN6-ql5Zdh6C-5cZGxVRFQ0LsSHiuE#VyRb#An&2dCUBU(DosjJ zwXY#zomNovbPMI5mznNAEUZ?u_0%xc8&E3ub}|P9^TFXeRiylA&mWb2lq-5AhW-6% z&D~Qb6KE*sjjvHpj$kZLlWi<%{qT5`EuNVUf;o<O8d+mzX}d;vYiH!w-YhnKnmv5u z0QAEZ$pVzi|6H!=eYmw5JuH*crZo-r4(RLcjit7_1=Fz1;zM<x<SQ;Uw%>0e_!qyv zBWL83!#Kg0w7`@6Vl00&j^i50Jy_ZN2v+_dy<E4Rlsm;WyO+v`N8UinXgnm;KJ?p> z>AQYB-*#Hk_OS6oE@0jLmZT{tiYIoOzn=`f5GxL>#`3r?DLckT2%n2ZE+9|ieD^3r z79}EpAOaC~&TALDJc9y`)P9gH8aWWe)v8uy%jeq)Hew|dF6#m-)kBb%Sl<fv)E=p7 zF?L%=2io##eJEo?`9<8e`wt`3wYUz~MX1+eTALvNKJ-D@^~EYDgD~my39vJ+evvJo zcX0AJ+RAJhu?CC1(HPo|);>hvdGKr_2p0ZuQuf4~5FBGxoRo$bA9ldD;s$g_@pYj} zYcP#`Sg)E^aIhA$dt+?}>t9I}{d!LSNe)8Q|3lkpCDHUxjl$KM&IJivM?c040u!?g zc+~?R#@ks^_0yJL{`Z#(p<zS0{$WY7AdvhrNpI+UXa<>3Z~nfb^^EwT$m@-68vWs= zNs{)??kAmFe2roaNnL33w$~}IPY4%Gi4tefnxEn}S7dS~$B00v2-vO7cCpK|DANM! z{c5j_T=^HUwCj#5{c8tJsqY=*2VZ;ud9_B<4=6(YT6eG#Bd0$VO0cXDjPvrit@jY2 zzqjANrrGtuExj!~RlhowKRJwxL5V*T__9C{@8x5S+l!<iUhjsd?5w(15`SC_Yhsdk zR{R4|D7#8!qrwD5!Y4}%sSxVdoz)NNV>o1PZ=?u;ANP$^gjh8qSSKS|4@UcrVg$2- z2C!_%i{c{<9Hs(q*1ROe)NtnFcs8L%yRBH3SfilGj2yLTB;SR~&^0%nKZD2u0T8w= zL0{P5w5j1Ru1fV^yO`IJ_-~zTkFI~HXQ7O*-+tk@JkCR7-N9ip@PpqcHJL3>yn9Kf zp+AkLc-z(Mn|~V{!kH2!Ia;TDiW`}g$Ql<d1R)}OR*_xo@=VI6EdIfoYu3Jz`UE!= ziC;{U+--^L>Pri`_<++*tu=jnLJkg5=EibgPU20$(=oc|O&J#^fOR+SQ&ajiB#A5W zfszQDOW;?NV65v6icp^Q;|b8d(~3D^439gsdT|{40Oj}o+qds>-qA6FarYHRVrk95 zkNS0il%{`z<W_X>cuKQdN`q%j(<6>qlO=Kpgq=gU_jd#yRuF^bV0G)u(a@}9#q@C7 zE@F_sW2wsHLK)9R2|*wiivjaz0Usv7abG_s`!KKZ{S4{SqwovSOusYo3-aF-?N$&` z^L_~otJPjLFs;q9vsf}ZnDIGD>J}*m-gKuxXjZ(;Ie>0U9i?r}m-bn$|Fw(xV5xXs zcB^w3qrZQ6@YS_<{aRxtUVP~0<o#55L9*)oY?;OS#9nf%)w=(J!K)l=!Ca&6bPa(f z5G9^xWKVG;j(8df;bEND?&r0OU7k5X(Il7MI{%nreZffEqJ5Dso}D5F1lGXmftQ-& z>K}+2GMS7!PB(!gG>qK&K){QRwHfuHj1FQaK=;xx=M&)xah!2c4<N5UP5Vw>gN?y4 zXGgI&7Yt2}0ReSe&tGr9(<Bb~(?B>3aQBe6oNsvUSj+5K))Sd<5ybL-y3i>^SpV>D zs#r7Pa}6&g$Wa>Z{h)#Uei62HuFH|ENLNgblwy#2q#mQ~Hr8)*ShGCODOq|Gqq%b) z%Af<;81nitv41`3;1C&jP=G(jIaD|$Mlv&AHZhovBK4$j#>zy_j0lDsjy@+|y6dd= z?PMPCsCNrue|JJ8Xu19^NrW|W4G~U{VEs3@lbaO}fk58YQf>e3o?#EL^z<H~bcJ1b zDDrBx{naWHRb_mtUsIoF?X{P}uqBPvVGG3Suz8(+nvTq#RZZC1Ac%mk9eM3ymuC=p zPfLtrqQ&5F)d)`^R;pHI$aa?xrqJSQbYaz+`?bB%V%dF0<>o_m3ZTx8;x38jZp;<A z;TXV^{L)G7;X8feP};aEL$JG8IAlJb9hW!+3l=0c9Qjj&q1(55-px_Yh?BZk4Dtxz zUIXy(=KOn=6ILZKRux_id_s#WS!S3JiA;%rT#1jWIMinDUXi5&-pu1{{SenSS!;=} z*UpLItVrfN5(HR>K`;7*Na>4FycNmZpz21KKpI9%eHcF!O1x^+UL{BbtY4ESLLdIS z8T>D@`4jN;=i_A9iCBafiodk?Qn^c#c)*|O7{Hnt1<#F!o%|T%ar6y2;;}e%KSO^l zNl?JigKZUGqsZ&a1Q8N|BmXxq=EEpp9iaYrR)71}@Wj8Tr`L<|yZ2f7-!TEL$^1iw z0eIA6!FVNzD}g>HO!84C|4H{k=<(_7t5{rOh;S^<?&r0OU7kTfbv3|q0;|*`)DNpN zIkS?GDB56F5$;m+k#fZl7((UdKYIAO8x(C<aE*5VY59~euC03u_nVW0cnY?WUQOeB zJ=kUl^Q9*890{TwCBxY0Zuj+VKX(mPtv=@mXPe43x<&}*$3S;ae=WUXDxUGAx^7#s zWOTUHy{zHSEenTC4+PLbu;}wUB|)$NDoWeuIU?5(cw%&eYpenbC-bUz#<3SgLF<nz zR0gbuWR+GwIT8Xswjiq@D!|j6yzAf2Z4ztR!Dr-wCuXwr<_NBThz=FZ!Dw<IdrU9~ z9R~$#M?dP$QhC5d&6E%ZW)L_`wxvzcoPDw84i14-M?Z=!_X-Ah>h2P8@9qBX|2~^D z`V6PXuz){r>nUw__weyiw{PA0@tonq6z<<Kfxf=}U(e~lVQlkOg6Q8&;DBJ^SR(5w zkT;I3#Ucy?xSR5%|IJ1WyV&KKMc#;-QIY(x%dHP{jSWTKY@uT~?0m=CeB_)ayzX8F z$atE5dJJc7oN)Cq;RjjrSrIIhnqdJ&2kU?b<=qUS@am0$B9la#c10#acra9n2x(^~ zAe(Y{57S6%bEV)Aop`6(a}=X(Y?N>uad0x4cFQ;CZ!C+VKhYB~TP!QG8=UdNZ%QBE zp!Rc;$T?ID4qc9;Y4@DfObdn<7ph*06TO}+!d+;bKx8=wGe98haZx*frzjyU6&jc3 zC>#cyyaPd2eoU0nyqhD@8@jM#+-vq#YQ8R%%m`)vT-vxIi62|n9B@IC4jJ-z#@!{- zCGp&Ok({wXbQ>MM<1a>ESP&Wm8_k#MjB;x4`f1UE4MojARJ47RD|f)rmL+kbE-5cH zbX2R&09TU=8EUm%h`#rkUCjPu5Ww4cR)-?&!<NvhB>0tB=<k3tbj<m2(AZ!MY7w=1 zQ?>x$MIeOn6yt<kXv91ee(di{8tlkx7rQ)rQUa+z%u~*cV5g^#sMZAGvJT)lcv~+v zt<n5lfjp6Zud;+^nQTT<6YzzAClwq<i5Lq6bC#q^ekxVJ7%ljQbT56WR@3$A2~F~Y zwe=F~wF@(w0xJ~{yZU=+0^q^)I5Wty!QKiG$_0BdmQiEs-Cm50i<FfJo-BiM1BpL1 zO7>}yR^9&iaMlX3acn3bWwoR6+%ZJ?o0*N@mnwyN^WFP$je!7;yDXmTQ`Nk$OzDiL zx#1xff&gQwcax-S_&YZm@~Tk%P%M2R2HH`CJWqtaD^aN0x`t?4_ewQ^tUJooVVPE* zwghKCDnM~IfQFf-QSkMqIt9|kg`;%1?#B`tO>u2Ks`K6JKrr7WR4|b!nieG)8;qiJ z{V0E`TNu9#x%R9s=3S+-X-Ul+Pc-yBB;gt>u$C)d68m>~!$Cm(vchXL1s0&5MAZUc z?xZm0Q*hpVu3?~O)}eA$f1lN^rtD&uXH2lbq00tv;KB$GURQv)M2J#oSYC$NZ2qpW zVdu$v-EZp~uf3J4K>@<WkMVArAc@*CD;~nts{49+zCWX17R$anR2<8=;e@gbejvm5 z)oAX{D&xa675e$4P?S;fcNa_hdoh$;n+?BkRJ6AY^Xf*B)lLDl7n8)b+9!x=wpc8? zN@T!aFejG#u<IT!gV&N2WkTc14AFvUwqpPj9Ijgs&nsfL-X0*f#G1Wjim+PE_!zDO z7D+|16|ThEBw@y9U)lq(i&f>a(N!-B*2?e*b$X1T1Tm`Gt(#8k-;9T55utRJL1gGo zVd!2tinR<tk3spYt#L4QdIY?qSmKDMyps!OK)MMr@T*7J+ww)Tqac(Kj}KzZCqmDK zLxWeuu0^piWtuHT3+wyyTB`84pd*u|fA+|f$z%z++(P2EK4J-NJEi_Ko%c6Q;JHN3 zxMRBMK@_Y|OpO{Gpng@T9JXiiBY$PR2TnI&;dx%P)#-w4NMTn~cCpK|hrAoIk!eH` zU)MR3gVz@b9m2)4;sgW#^}npYzw--be?F^3L1#_^!cyN$yw&Q>-=1k3aDfd-F09{l zN?XFeN3?w_RjW!&`_42xLTxUH;{qR!&$&U)`wmI?LZY-nb{&0xnt#PDTJcFH{6v7l z(wY|>h24nKUy8)T9zdv+El3og$Tb$v!KkYjie>bV()ow0Rr3=$!PU(_o>YtrWjOg# z#)k47Lihj&+aQmE@l!u0mT&8IAMBzq8o+W0W;@}yj<%Nrj*Hn9kC4aUpwXd1=U|S5 zFU2K*HYq|lK0@e>4v1m5&5i4gVW^`YZ4AnGee2&%;}L3_riL?JaI~>F8hQ$5(Dt)c z&h)K!7=YdTS$2SpETex7WKD<=jm9y4Jk_`<ADJA*_=*Hqh}&-8?)z^rY>vTG7bdd* zron$IL}nao1wL4u+j9{t;K%*G{7PTnttUm^UrVG|$eb_TC5+`9Z*Ny*7rQ)z0`^uf zF6_A6aBT!Sp?@375D6kwtE2zz?d{!PsRg?#??2|?X*s|Px14yOE>_>Ip6zF^%#FQQ zm@$Xly!%ooe5VCD`uS`E<HeN5p59xJxccgJA@F1!xr5Hjv*Agxl1f=8W{A!d&&g_C zpUZ!ud#9+{T|(d~k)qm%l&<nCN9HHUW)heV{`5OUlCm(C^HKr={JG#D1+%N8T+$Eg z?0~&LL`F9r<D<k&($zD<nd8Hl)1yU$R$?#8f;fH})E2{NtB{*AnL6*Y4O?;rt26ma zjtMbZik)7MdB*+d12}sRJ-w;pBSe#<5v;m`4}`Jc!!+Pd#>h2#T|e5ULh1f0rDGJ$ zIgIj4nS4E|0Yl;Yk=Ngbyxx@2A+%X>(2P(P<_-wpx<rU)#7W=CQhi#|JSPtNs!$wW zqdLmaRcn4*SAuv|s7<y?n9l|%_x1ICU#9!+IR9@Ne`7X(YKm@55Ef|wa1;>4`?5r5 z?s_tFG5W;6&J)^#0q7uzK9Ip@7rWTy858#k#ggM!-%jE@R=Z+}8`WA6%%2*9#SdFd zmMujJu>AqfyCOsWa*7;RGc;)Vhf|Hy!o;DMTP(KfKw%e^M<`w0J%iCcu-3Grv}r@8 z;L%tABufGU_;aHMgPDm{>Q$MFGD%0eP(M9R_EDbVNdaPAI?pvsg3?B?{-HfvQOTe- z&x}`KLB#_xPFUHf`uEcW@22oDN*l;=35FemSZ}2W4^_5oKc#p(om(#LGMTNI5uidj zIb4jf-Gg=eFFep>r~g)udzJpv9O3Q@S6y(>8_9xzi_Oc^B@O{>haj#C9>z?>-Y9C* z7sYaZDUq#9htbPzCP@yQ*L|HYeKnB}2oMNnqD*;QIDc$7+cla8GNg{alu@Y0#?x#a z1nye7v2xbVcz9F@&y~O%g@+x3xmW<Lhi&uVei`hmaSMkR9+S95@>b=@*X7IC<q4xN ztILIL@lc!JW%Y!3){0b4fzYHj{ijx^G<8|GpYL$-p&YDGSpMq01ZEK;iCp6vCq-gd zi9LLO_^VP~SGV;^#|gir9>@WA31*jy^>!U)7rQ)rqBVBCncFfimNydQT|p9q0BCV? zLlH-hV(;f9so%wW`Mk`g>$6fdTM86ID)ysXV_$hA*k3>MsC0KR2FjbW`MaurYrCVv z-OCW?a7iBjQ56h&ZuNatst2B&rOAVwrADaVn6Em{X?ryha*A&J^tkwmkQYV9MezcZ zLN1Dj&<P8*rp*QLunkqMR_n%G+43aO^ceOX7D<^K&7K(s0YCQh;ZPoQ#l2FU%u@fE z9+GHC67$NkWD$ySQ$jfh&o=C;Ru}TGzIjaaGLgN8q?t@$>^$9AD()CKy<B6O7s-4s zoE>{vz3+6xD1SEas9%<bq_J9h?+k9+_Ly;12#9CRjE75+>nFvg;Ht)7N)%hm6;r}k zwuVg)TATgoZoxGCg~oi?kWkxn;G}eAGG|T<Yh?;=aU6GfiWr^Asw~a8co-zeUQANF zlqjDOEuI*`9uvu)8VPMFmIu(=y)R$!tiML6Z~mdYfp}T{ZaR#wX)2anjbZ6yY7|w7 z@!y;sn>sCDm1x01`iCd45T2?P;G=&X?_`PqQ8`fZ5#zi8@YkoF26>NjuVAJRAF5Lj z=ZBMWL~pUHDZAL^858uEKH9f_|K-k+=*;E0@I)eSMyz~C0S0p)WrzyJ_x!o@BbifD z^zR%SYR&8E?%8|Z01lE}@a)$TIp`<fWb<}bv~_n6+LlA=HPbWX%ZZPw8({3}-f%+k zT3TZ~tL@I7!L+5Y;j>)Pf>@Nqv1?>c7S+5q8=iJljsjvpwc78ZdP9!%#Ypa=RB4Uu zUjJ6T*(z-LZBJ$6#$3Vf^P2AprM8k>HY__#eko40;e=#8?J=wb)*9c-5Mq{Ll=%ii zj(BJkp6eJ4qrB#WWBj$*;&e#=um(wy)T?ed2n%2ig5Jw%*n6rWm1VHr9zJMiR~fRq ztZ_~x*Otar4~~%Ul;)s<K-L%>WnH$|&}q2?-+hG3`u!zxBCS>1Y3;K1SS;49MF=3! z9HUq$WqvD}?{&V(y<GKaw%8TV7#&D|Cj<GZtl6`+9S_|oY4|<%vTA86ck`)+eBo6{ zZOUb8^VnCi+4=>^+#N+oeWTfA?n@_Y|9zIaO4s>WVKeYxZ6-;3d;j|Es`-gL5ZUM$ zfGuMXK?8XHhk0s-;)yF_Z7!Ap92fi6j*ypSHL2VGu&XJ%*yY(1;8*{7se!L~e2~=! zl5j#eJRwTDCKJ}{EsKuw5-7R>O>Q)q*X1dmkCT@1hawT8H+_Vag`__h%U@rDao#nl zoP9Od`)}W}K|t?}2=1<`R_)`$lGkTLM4|OANWp637bK}Igb8{p^@+<PfWtL_fVBX| z%3S!{?5@7_7vlKDIt^9XL2c^7RckVs+G^x#GPUKjq<wF-dQTa$JP`&Sn0NK_9HF-B z*255UZ;c+{Xi?ObkXr4_@z9J&!JWPas5+%!X5In#K2i_-*>e)*b0gWnpYjb!6na4w zcCj)1Qd5!SI-ypBt7;q{&L2ci^k8qsf>hP=WYN56D7n7*>wL-BaF}pG39I#!<5*(| zY~anD5zhKBLxeKf6=~AdIgR5ZMIe9!+-t!R+L#DrNis4jfa(y$oD~O+4M8!Q?2Kc- z6b_-Y`o0*2U6x+|Kr=^F(7{nSo)rTfrL^on)94yP1&1!JP7`e_X&xO!eJ71qj<m;; z^_%mBBuI}KEamc!UrXfy)r}^zm8xo|H2%g^cTiOwXOZ^x$xwyR^#6(%a}@2L9B%@F z0<f>@FCQG+h~|nwMALIb%p2ky#{gl%RT-)x))VhN_Lhnlr^>Je6Tn_@6t?vC+I5s& z?D7mEFOj`8QxjMwe(aGSli(TAg3*!E`H398UjJ%}80;F1SUy~?n3<#*ji(NhwFhNf z2pt6QH|NV)YRo1%nLz)($~4gXz^kTdVybRo4ChG|a!if-trQWepd3SZUKd7MhHC&# z$`$Rd;asdn-(j*XN(zgBniRo6w+*r@T~)|c^bM!2hxK43XY|A~663;T1Vz9x!L;3{ zm50hy8%s1bh|$v9*I@i3<U-?U0`-Sd#lj@<lqmL|O5KiA4eRsyy9*>Uh$57_jtOFH zJuaFSLLZHCUOXS*c_5ev0$`_5(WF?#gc!wy=!P+&f`RwrqGee47Ut@ufgso=gg-V^ zfW85NJcn=@2o|E683b}Bg~6ZZ$;L%sBpUcbuVu*ajH?2jS#;$lO=DV}4S#e@kVJ3& zp-46@l;Pk<!)gNi*-A#E9NdqN#bXD+z>kZ47|31}!=4nyatWdPoo&$lX0`MTzq>G6 ztmTTXxpC0eV(F0z<ts5Pw@~`d66M}<)wn?FWFoUbU}!V<=*-<WZuN`x=Cgvf8b$jt zW@{q3iPv(Y3NZ?<{I2ipMxR@k^%kt|1pxfrh%D2zZ#dBeaC{K<x70&ET&4)5cDPq! zEqXy1%6SEE98+Wt=f8Ng-0JQ9rP2Tn){iFgvRJKl1!WhzJd^Tsv1GzgCD`+LBH&+% zEKL%lD&@@_Nd&BSKuOl#LFDzjtN{^X_wr%V4=on!j?)_8PF|NSEr784PNPtMSJ7d$ z_Mq>-J*k))uL`TtJn6L6defGI2C%>WlYAwtH9r<`-z2FHpFt9sZJ%TcX2gm>sBA_I z^7+Y@X+*360SMy1mnO1UdLQeoBx$Ya2#b!2zyUIPQ)7sT8-Xz=o)^IBU7safn8J&% z*G$LJW)W%k4;4|T!wNzMLWj?(2vw@xrHD%u2ZYi=AQgRiU1&nC{*l3KO{Qtnp|(Q! z3h`1?@OjmoNS1>y1B*=dsed;W4y#hWnF?cP1yC2ov1djL2kU8|gx$wh*vgLzJnKdW z(GFfTZp;(UKMH+C68K+G)imD>z1*_>bVC%~7+0tLFdgIIE`h9d*#dy0g2T0MCGi&@ zQ(&Ncm;yqCfFOE4PWD!I)0-JelwWTpiF_(lIozugVpt%825BurN`~TU)WC}tLD8o% zbW4-qQGWGX3S@`sZ%!o8-a97#^`s2!s`9AOba(eytkzzWNoO=S7|j~f?}nQ_RAmRF z(Ui~D!TR6T9bF1Tmr!eN@A>Op#(RpyFT^4%vKlM?wy>qU-#GB7MLDl)Acsh8eTrfl zUl%FReboL-dQ*3gT|wE!F3+UoFf^`2DUtH{M!hR?;VIF4lp?K66VddRsiDxCY>X+l znYtEcD__Z#6Uv0cYLe|Gjli9{zoKcNCJ%}Z+p9YI`g$u^>b3bTlR`&S&~FGP($uC^ z$He3D!o4Tu53~B}ySn(se=dn-y_X@HaY8jQT7lBWG2!g(MY1n)kj?pGtJV5No@jie z^sNk03BT<T*~IBuEu=BksLWsGir!A;Z#vyFDI9jivA_}X+s7nd92Y=3%b!mt=`Bjb zjc>~urxUQeU2vE>IZB?v7^ybioF#A#M=)?Clm9#=a?|y<QzaPqV&yxSFUE^D7r+C5 zQnXoW747&+*{XDSW+HUttTLQtT%86lN`gMl<o$TMae9noVw7xE3jA4t_>%gj*7!S$ zn;TC^-bk0a1ybDzbk`s{Hu6F88!3|S^O5O92n5haNARYnHGOxk{Recv6i(lo%gg8J z4BfZPy?uY4Uahtp_*1+pn+{cKmnLxC!WetXwJTHkz?1xAnKqrQzWbe`V(hYVw4JOb zQ%n2JhATJa*KeJdcTiLve__-X7VnKkJ+*fK&2WZJ_&|jc_^}-D@TVt`r<gy8WP!q_ zuS(?h>XCM_%d;xM=M^rY;;>6ikNugiE)Y+R6@Wng3z1Atn|WL)H<^k>2kog)JK`lj z7Rm<7GXGf-tvbd7N7%d0G@HyAIduu3eo}I!-E96i5gHRE|2R()Ih>99{t==tH+@=q zB}aJm?uW4yEvnp>q^l@$!vpBt*FQOo5_Czo_pJK4aONvn`u9sr%M*B$<2ad6TeZ4# zW;%SoJ|`<oh0;z-zh$`V_7NzCpbLs4mCZ9E81Lrm{MbFq;@RUvX!}ozCGFj7b0z3~ zMN?YuMjdyVyB0>Vc9+NkE@)qlg+LJPl??6toVK@*>%S<`tvM!Kc1&I_G>lNA`ka-! z5fKM}3a(}-P2=dymsFh}WJ|zdvLlYMFhv-{F+O_JQbJQ>4eZvJoRM8WtLXZvK=?uw zb4oOG^>IlORqI)y_#m6V<#hAM8G_HV1wWL^0gi@J<L6`fM=6>Ly&eD@2{O`Jy1gqD zz_-r9i~LRs?_h1`!TOs^l7y4P=$~cr@wKhq=bJDNjwd5}>s?^g>-DZgm{{BVdA`&s znD$+X*01JDM0KP8B~5U>7VM}1d&#i&ULDQ~n#I+tqRV<~2=W?D7V8}Y_}#%r3TOUg z;Lrc=Tu1M%zt?k${US~)fG5o%TnzR;jZfJnj^mKo1P)f)wUb@!@~n#Y8TqJi$#*2t zW0hDXtY4cY10Kw8PRJYEe|N_7z{2eAuI>$mTBi`ntVHNxifBWg)D3I!*EFCi#}k<A zPG}LG>6K_6!12;qy2q-rpqf?`<JRRX?rPl?X(;MZmmE`EmK$(1y|B%EQfzuNN3ylt zFg;H(E==g;Pu+7`{ZY0k6E==qKX21ps%7mTpKO{Q!T9b3vf`L*d?@>iJn>{a?frCE z)o#6iC=ftFmHFYbs*94Vuf?;L9u<6ZwsCg4WD-$^(!I~}<y-Q_C^DkNlc5+$y1A_} znnymF6sl%Iq!jqk-zOnM{#4Ar;(AnrIjMapj)BZK;yA7$^q@Ker0rZ!vpgwRGIn;C z2#rgVIj<$doAN|1fgH@g959dzOq+nC&5mb#RVtFmO=l3j&e8+lsj#7I@21I&tCAs< zce~<f-=9F%9OJv-Xl^0&FAG(>OO=zt881XLH)rz7MOUs_{?XHiIrmLn-PWGI5`NqF zr{zSNZe6ATgwj7Q5Px%~;mtJI|GW$(?E0?W`9wIL*8it1#O=FlptJk`h+BS>wEiyE zccGv5-0DO07W6eNA`@ygD|4E+6)XR4BKdJTZ&Da+GyX%K7n`2qa<8Qc$7D4FyuClK zUF`DA%Fo4E3!a~grH>^MLaphGd?om`F7RR#Z0rVy>LO}$dWKWBQZ2v@Co7;fhC z4L?BLxv+lVzt2Z<Ur$9Yidx-*nX_Zz^%cTL&ycLvf089|3FLj6D;b#HNx_wQsg1z1 z-r0|~I2M`}15Lm~FQls9J=6R`6n9HiYXQ{eYV)*?kfd<*mKBMdwW-|k;qa@;vd>Q_ zKFHvOU1;1@f-EHs4d1F%wC_BtdozJEDu@QWsV)J`8F3A+3EEdPHRFlUuXXxbr3q!1 zmIoi^zDn7GRQarM!Jbmt2<uT4aP;Geh-)B?r5$>Fxym#l8XAjdU>%DB7yvH-!IG&* zRnJAh6JiBF7faquW@S<|)<4n4$4XCk_ofp{XJ5vhq+qNO(#~R0>}Ay_Il^6)s$jk) zmZq&#cC~kPTP#?+r5&X*5=GPBH+ZedBm{uZfLBfIrN+;4g>K>W&3VXS>dhThruR-X zJA^Q1L^2LlHU?i(f1EEahxO%rJ>S?}t2Lifb_7=`-_7D3s8FOZbgPdEmSl;2E~zKP zFaVCSqW~$OX{#0YqNB0YW^2!l?q19P^#1eKE#r;<VK;X+xBq_o_U-=RgoyNmg`fT1 zKdASzMF20vTmT-Fuo~^KhM-5wk<*eH8EqiU-k;Ylc6nyyheEM)m}Fmxba$ET+cT}@ zqK6wTdwYLAqXl~|2UTmX-1r9yd~p=rvLvB%AatarapYGVgJX<~626(n2R_VSN*`Z) zDjU|i1ks<1lSNcD@U^Cc)veCHbq;~Hs>ogxM}LM>0OjjK<!3p(B5~))Q3kg3mkQdx zAxV}dbALF~HvcGZNeZ8EL9?Fpn2v$D4DFiZnkf+oib_tt)M-S{zB5YTK`s%tJ>o=? z(6BPU1stYL4reSSjYz5HGWBD_gfob|D>sIBP0ozx0KDkiBH72qvYo}!B}sggsLqI% zjEh!INKn0;fP9p%`KnMgH-^0?jh9f{6n9yf&uNLQZ}Y8CJ>t3x6SyuR0$a}oszVTG z=PCL9yA%{(dv4!qxAa=9w~S_M3bWPyjQou>?ymFtNXZ{ZE;XUJDbjbH;^}}*24U}Z ze<E44I-3LBtH8dhttGN(hT%Y&?88j>gA4(IYx1mZelMH1CYyKov<xNS%8qXTYRzk@ zFv^9G^RMhFlYgGee>0i$eOc@7(~3>Ug;AHA&mu-JGyeX#=plya0qO4jzgKVe_VxAO zzBR~~QFh$jbL(#^{ZYo(uSyZx3X0M^&MKbb5-~NZ7qj%}X2Y(X>|&Q^Qc$(<av~S_ zLSfbFFLHz+P_#0Q8~G5gtF3@!8`yox^IT(VXSb6-)ir<)j!=Jol18F^y}fUwO0b&n zN60f1ReQ=5kDFm%BEMr)Aj{F0>g-RQ62TuA-GCLg^{js>MmRQrx#M(`&|n@py=7_5 z&&9J)z<o7S`ffqnmQ(t7Gx)@M-P%+>o~#{Vte4i@6L3j|YS-mys$7<CBsv^D;OFy3 zaDaB8q7ku-i1TLBTP8#zPJUG4aA~1pZTr-4HVBo^kAVipC#f;6UE9#Hue@mz5&E)F zy`@yQFjWpB8{ASf(~_DdL^n){Q%s7I%{z*GU#u`$hM{A7D^;UHd4q)48%16=OLybZ z_Z3f5gKZ?5-qP@!^}O`z8UB^yklyQj<Bl^8%hI@nON|s&$AL>~e6=dy)+XtG89?U} z#CTDABa5XwR3>{Pm9w=-_RCq#mIBc)WwID%>;CGd`KkQn>2M~$ouV?4xb1sOk@@kQ zJ>|-+#nPAKSwEF3{4O@5G&8JPVeGOFiJ`iCt-ij&ugTEW-Fm(I?&nr(zp<-FVYECf zi|WaKQ|Jculmd7$P~`QjcpBtQqc+dV)B`WFT|wE!F3&3R&W_>$U)C%lgp$WGq5Qzw zrbit7ZIJ@Sj$ca~qRAQuZwiW@v*VCdddtYwOJ`U2*JW*3oh5(vycj|M!wUtVW2%kT zpH4PRh!6s=dglP<lyG=n6zjt@zH5kRVzhksDap7a^#?9JrZ@vuQ#V~rtkJp=SZ^lr ze!O^NR)XlK;)ahi;pLga{$5L^qWy&!7#yIwUurj-M_TR=5Lh6Hw=xa0(r?HReOcTT zRr{#isJ_0w*RfD!=8^J-5n3S6ORi3ek^-N4_lluup}(9Ie^k`!8pxTIAm3Fie%Otp zQpDF|>7_*0Iueq_y%uy)v*;)v1c??O@5s9^T@*c%qV7C+PBS+O0=^6sb{+hvV}s}@ z@~+PojAS7dbK16^YTRDjAZ)X^BZJOXuQKtPbk5;2@xe>#i!$Rmaa-dxOH;?qYU#Cn zR?7vkQQdyC{iaplX-zMI`?cD86*4W>d`JiORDo?}KbOlk<x4)v;qR{21zv7ikj7n< z$bBn~Q`OLUS!J3P!}6(WO5<GHSuFXrTprBq@Vcm-6$LpHDBkCkCxvahO3;l#1^UeY zyiNv@x3}NW)nhVSd;d@0z$Yl9H~ewy{wfye{pd}-Biv#gDp!sPW~1QiSw0kbM-mv8 ze7WhRbUpB>wJRvQ*yUM8UM!_F2*&Cv_|hka(1{PPx`Q6PwNQ#8ulpILcU7Z<7X^4S zR%ePwo)a{{Jhq&<0uIzoijqVPXQS@z?Q662REv#e0t1d>z`7%OU@d{BMDvUJhIm8| ztMy+MHNBXG)tUz0l#S=Elnys%Tb`+EY7ED}QZ*x#`F2Lb=XuDiShjzarbgCzSHo{8 zSHDDLqDpc~FqFYD^p1E7@eWBiK1wn#nw`wPG7iTCe!L5c>%&f?R^9$}QNzYOTk8Zb z`oS{_t93;9p3l)wM3L8@GBbXtb^72bWcD%TtOVJ{{3j?_Jy=LYY?u%&SdlD%b@%e1 z_mn7RL`x<_O3)b&RLyb>qP-jqIry=rgg?H+J5AB{W%;#nVY1QTk_iOv>=@1`S?K-N zk~Nn5O~Heq&K8ULAH9w37M1aLk<Lt4b>z^RtHf90$jybkwi>00p=$5$y;UhT7)<87 zXRRk86&jPI{r6Os&g-n~jYQ74P{!A#GXIN>`CL7v`NqEUs!auwV2VD0eKiWYzA}xs z`8e`RW!tzA8VI6nE09LlwLBNWS{TP-X)TY)Aj5FK7hQ%}>%_aCTl@NMTL0Pp`#&|; z|BxCiT9fq=7>dR7ljOjkv*ak3btl=pzpvMq-1cFbFoUk^e`IB<?IrRlDLQb_j=XlU z%QGvl<x57T$!%^a`uD{$wdJ8YL2EQ`EmncOwdj`qB&<g{@02J>K$UVN3g4fi+fsh@ ztqgffN7upxKJbNHgBS&T<GqYdi<PbEtU}tkn$Ff6J#xd1RGMy94DH=a!H3yW$3Ql~ z591pJp`6JXs_7BryBC@tkD!`)ZJu~(9QWIbYg6M9;LUUiU}VBqhbT#XZ|_<X@<Ngl z%YyOe{8;>Csh50R`+GSJ;E=7|57zIV_C^Xnm2JF>=~dd!ZKqVzf*Gsx)BwlB%HZ3o zQGHh^8{s8l>zc+zA~U0*Z56}ZyL+X0PZ{zTSV#N&$;ytY1gsl7dM|Sl8^(unZ96er zUtk}~c%ta*lPFIgn%RW%(j6@8(sV3|xJGR{T%nps<h>XP&q-E|ijaaJF$fmCnXOrq zDFK1>#W}KgvKF}0+-uas0v6`Ws8D@H;*&USH@pAO|NQg+k~poo%;pTX9#7VbbvLl2 zt#0d(bJ6!Sb*HmGd3ih(L(_&_*36D$IR{fV79hJT8$Tn-KR>HKa#<gAsU`4Y)32u+ zJ}*+gm8WpOaP_Am+4vyJvQ$AfT^Gk{i=gS}r^CILM|DL+KNlOiYZXSB!J@YzueG<& z&}F@SyI<G!&(`1m(Q>2L(sz4DnK1maX8uvEHYxC<5oj2ZPolQGo-CdfuK*#4|2f4Y zalh9MPXiIEMMv3o?PM3bJgdTLGfz(z0bk}TnHnNP_Xtg{H(It9DZ#$FJtfjla|Pf~ z-Ml0Pq`CfRt)43GT%N!l9mWOwYaMXBxl!!#LAHWR9(B)!vjb|HVkpf}v-xV5mD_St z-SH2F-i%ltpd>|nBi1#K$OpKg?fj-k!hj(8C<A^c8~%_a*<OUWMRUG6-Lxi0^mB<) z)@EU7u8$4j&WMpHu37FQeQlG8jGszn7$ADmfggMAi6&uN*OLO)!Rn@|$x2%-3#@?= z@S;wNlKotP5qY-ux5eoVV+leK#Krpddf#c7ixtDKM|Tn1io}(Cy{_HT)6?^?XFfZI z<$xE3RjdEjM3{J*c20@{+dp{G(d|k;N4JqAaq(l`=`ctI?&O{4uJrd0?Vh~5Shnc2 z-YtTDZ!>^c6z!G^iW_glL2o9&Yx5Li!(b4Ien(O#qTH2$?zpHgALZ;h)euyxe>+34 zIGwY*NQ|pgv~_mhg}g3{wUlp2tWobRK^CMUB&dz4`(0z|M)!o8j^8cTTj(>xT<z^v ztu48!x3AA)bL<X`K-OXg5VGFPYwg-ohThkuFY~404AVz>(sd`~`zkbh%A0;JQ*SA0 zdM#HyHBs?ecH^hVr7r%IQFzLixng`x^HGNWwPU>HsT@S#_1Gtmpo`Ula^%}Ry?v4^ zT`J?vm05_3KQy>1JnLu0a^k4kc?lBW$ygZ0jb*eC)RP(d<I-EgE~#;s+jbSpp2O;{ zP}Zag()rhibpEo7UF`B~2n|us#7gxFzbjEb)?xZnc@ywpObelp2}IG8F%}P5E!MkA zZ+<$oG=`QA>G4&{MJarMr>{uhzkEy${Gk^kIP;^~&H<=;w<W3!XmwBOf@EY%A>x67 z%R{+#wW@to2oHpD1}>zw_5%Vf7dEUfkd6*Q$6?Hg;lCKm`k|~bs2XLL6xx0DmQB{% zqXHSDaO@-H4Xd+-&xb*43tASWshy)6P@owbz<50!D=CS>&C{{PcwzhZB}#A@tKv8_ zf{Eq82C$EuksHmwzfEew>Q;GCW<|4Kh-BHC<l0*02Ek6@tXD|NH*&NemzmZUwYlNw zz^Be3jJ_lhdOw37Q{QqNzCzb@&a|1qahI9`$-4Drh}LZV%OTjyOB+(FnVuwCmfrHy zSq)ibvYE1F(_^Irg!lPy<_q!iPYZ_eO^=I$976fu6s!CC`i6Zt6m|PM*}@4Cj5JzH zv7|k&Uc0kUvLKcPe97QQEx=JHL~%YY&}^-``9+0k=}~y;QLa+}^PQZ=#VNA+@sjCL zVh|!7Hb@Em#L(4a?7|*MqwyxK^&gs!KknL3D2i+68ruD<)ctojEaq{pfWc>Fb7J63 z_?pi-)r&DqR|0KJDCN1M&_}tVWtj~N(i=xbOR*}}`)fe}*NG^4?|Ad8M`4$6#>!N# zcSS?B+LFrBPfus1Lx#jUO--Ze&$9};pq>~E+ZuV??b1lc+$FXyS97DeZ>904N728? z;+IG|s^q4H$#4x~90>h#FO!W8WMjt<{6IV@^OBWIlLh<B<z&6RZ-QOy@=VFLQ<5=J z3P(R`0rdF3Dvt4-=VOIf1P-c}{TaZA>=?#yB``cHo4z?AbHX!0D9?o`aSInXg<w@O zKp^wgIQCm9-0un{TS*dUKdi0%J*5=Lb_s?@hX}tp^{5a~+4Y`r;X;6(>rZnpQ+`>Z zL3zZFXPUihjZ+8^%GTdX;0&=q-s-zuqc&|VQS2&H1D|?ZcL1hKtZaIM^t)ok_a~JA z1v@YLjx#O&4{5qLa_~J|rNxlfm+It)F<%ES>cT`}NKMn*DLhOAd(^+3%3qnvyGMIH z>KDXvD&^PqS2Y3mOK+v{4_(lGSE^Z=E?bf;85biO8_t^$&Yv71L}3WUJ_moQQwYa7 zl=Eew()*(B)dU`jTyxVEMJ&y~{_O(*3Sh&|vrRJ7t^WSOM{!=PpO@G$(C61Fn6)BP zFs%E#n4^6;RsB(ZGfUMuk_UA!p1GpJ_UZ?qLfqC>*>H1a1Y=$}6NCuo9#hPY;sW<N z-~-K$<_FicWj>^f;sbY)-ha1%29`e#>5kNBip3pVT^F^bTX+4B5{_<fg}z2Md`%U^ z+^1+WmxzsPa(MHjSY8*kJ4=-xWg~9k91z5F43~}~%H1LxCdMcL4sr^nP77s@!}C{U zDA#5QOA+&3T8a+td7%XyBtOs``TiHW4t*GoA^gz<fm^u93C95*wIP=@;;UA&s$JjN z&CvHAxTpbv^g-<Q#Eu_@gWpO+JkBZX#U$-wmuEoM(-1<fdNiI5{MnPjncFImk?7A~ z%cM?0oPlId;74;IqQpcvnZTVJ#hprE0UUE2kux=lgFzeCAC@{NQ8qhKHkv3N6E1!; zQ~d&}(rv))=u4Rw1Azd_;$zBc#q}Xt#bmLp&lVo%w0&QwcsY*a6u<z7>l^~eTZ)un z7aEr*LqdhV;cC~iOeOH7`&6pFErF6bZ71Qj*HQ%&3DEobx>wTFfT)-lC3W<pIR!#n z^O3Jg6`z#|2b44ZvIZQaep%eCHJJVd*zL{ZU0s}TrwQQQ7fNsrk!^bntN<hMWCEPv z9@F=xOvE!k$r7(j5umErx26R#m{VEWbc!aGtoo_|o)AX+qCoO`f{i+PQx_eDP;iW| z*9KLopC>Y%ed_^1G>RbEeYT~mtNX873|JI2M@QEEeqP#9B5a!$#vWAuUeq^p8?3j6 zVYT}9&JXgnt|9O&TifM-QEFA~hilt*l?i`3qga;JvOZ5%fwVt$$}MJv?(I_;&AA){ zq&5k4=3HJ!E<;<$*85c|j<Z|KM26}{b78~v6)5Wz8t=c*KpY^9=+}{in+uR4fdPL> zyX+{(DTKB-K|C{21;P-l=aD-ZCCJM&HA@n?S&&|!zv)|}nH9ytzJ1@vz0fs)qc4mX zy+zV)E)bRo^;%PRS9cHp${*jIl#CCiIff&^lLq{`^N;dhj9>u|+L#ddxo}oAx!Ky+ zZ--#J*yTT02sGWOC@F@6J`5i$<^M5Brm*gYN0n|est4}UH(S={+*oMSanbk3B@1HM z0LO6(=8VR}&Y|4Z>BvvT@+l#h4R&cNZ_9D9s?B`lf@&5Kat@cd1XBUAW{9?|)tFr) zn4e}!c9f{6#_+bD(s)&08IO+O&D>Oo42hC`H(Tg{?ExsxLG&5%;!)wkNwLaNVUn># z;m0{L^l~r8a1Nf4j|!lUBT9lQ6!%}~D76*rC4YCaaac>~NMv)FVoi>8dW`fz(;{_{ zs`#Vi2zCx)zW~ZWHNTgm`0}^}_|#$UUB7$ai!~_pr8{Cx`XJy*2Hy3H;@GPa;dkP> zlS3KK!8B+8frA;-qj`Z<_a^_vaWQ(z<WP}+bxU90Utol+LG-u_nuF(B=0wBeLxtnR z1+Kv?+s+DWCF~T;d;f&e&^6RG+1uNjNY%}a5`kbY2xI((%TjN$(p7&~8(%Dz&uk?t zuK!dl`LV3wz(sX%l@fnh8%Q;T({u-_ltJejP_XW_!P}ot@9gT?Q?3C7+Dox)k4p9I zcp>m%0)lvSgwzRr4(RvmRsVR{wXKkqsbb{HjgH@YY(ZjO_Z#!K7l`NOD1R!JEs2A6 zm&t-I>z*TWuoV@Cj3LUuEK+aEL%tyKk+yC-1lz?f|8Y_R>sKWU#s)&*2t9$Od-Sh3 zfzh)1gccmCzX!W__?=S#9pK^FNs1*&65vN)nIS9Yx4BoV(D8@$-j*Ro*AxwiU|_`` zVEy)TRdlU(VLbcwWd8T1O^*JQO($e0Mc3Emi(xHFMsB~BECAjRzP2Tk-*!paNn*Et zo+H{&&@vP2IA4#xUWnuaJp1)D;c2lc6Ee(?Vd+hSwKcFZhJKXsk<wpIN$yU-YV8ZG zZU*}(+fP2F1uq>jy^+QP0fGT%=mX|NKWIUM6y0yQpKU%X?W|G^=EW?I8_bg(aN&KB zA;wiHUyb8nj}#@yKD5y|=JOG3EQ>0D2YjGGtAY>JA%Hc`pR((eJmi9MLIiD;U%gud z;#;M$SRP>O#0umv()Sd%bGSzFa!w1_Pr;(EJ;?LpINS0iE&)^rfBKYA{+FjT!+wdm zLc{zt1!iW)!($^@|4$b16Qxrz8cij__5$&B_i{ynxE;i^+-Yj8v{r9Vx=`;`tBqz} zsn>L-(p$bMl74-rVPA#fmonL|lM>ve#v+mN&0OKyWT*f#-oO2GO@~t%djAXBF$Bh2 znT^ijBFxl0@B_J9dvjRi#kxAuD-@l?x|XSU`WqRVr741HmFdrShvBl`>RY8i7dGyq zdQ33mg=jGdle>l^@8mX53unEW$o?>m_h}YCj%LVZT_FkY)u7v3h0G^u?dWV5yZpNe zu1c{mRq;b<Qw+!O=p$FBa@`^%nC;Z&{=-_Ud(*&?`nd@LkF(14xuO!Wsm(kh<EOFH zQUDv@$>zVCA$lhRnR`s+TWeUB!h1hg{^w4+J-xR+I?;lKD*4nq5!j<ccv!R*3Soz_ zPtf_in<1mBJ07N9Vj!bK0CjS-^k}W}epB|YQwp%JZbzBUYJD2oh+eJ?X<L~o$4r8E zIj<LGK^%Wefh6-!A>!Lg5G-8Ek1-fz<wJGyqfH2fU4j`Or15_#k&O$aJ|DtJp*F8O zAzqgw-gp9W3WJ?d8ijQLq@Y)E#`BzmVSpEni<G^WE~=qD;2P+&TKASW&P~(2l`I?; z13l5bpzM`!S+nMtz$KLDjOU{~*T%sqPQk2wC6aWSuDkozpU<+GEZfm%c(8taq-1p} z=YM(KT5jF`qEzis-KsWONBDgOUDP<>rNAG3q%07?0znMdQ0C54%DrVu^ov6Y<`Bzv zPj8>X(0#B>w(mld=f&n9Ps_q;G`r8Jd@eS9cmi3IEr`6RYHq)Yo?`yP+H$>%qqDr7 zC-SaP&5VFX`%zs2=%f6oqwsV`f7%@se&FTcOBqjKj0%CVw#ew(j*u-%<c@H|yt7RC z^>JH+$N<PO7<NKmc;5Kv2B#3oYYF@hNg}AJ!~L{uUNmQNk}|wbV^?%`vCF@Ypi0B{ zyvEs^eW*h7*r?_>igrW3^tpJ&OL0;_5KfMTUP%(I%#df>dfq*DU#&1%#>EITX)W7I zl?&pyuOuqg6>1KiQ99#jYe|nvO64?mtjIS2FBIsR7$|tt?m1QcvG}D=NeZvZhkB@G z?bb;|t^-~$mB_mPb^<S|!6E7^DKd{T<f$KEI%1v^2~7;=-bLPJslt<7l<D`a&1Qka zRi5XR_{(iS6(SR{DCPPA*qs>02mTDlV2E(3`KNLf(q<02*f<I$M_wq$q=RkS24?tP zfyDcK!_Jcp<3ri6BnUD5^<z5Wn7hv?akQax?(6IQwW?))Bn*fO5G4Bg<P*1cB`frq zoVK+mn-@iM-p*1^ixOjf?)(_jV&&6A7;|ITUu6ro<_Y7ex&nb=MLg8s*LP87SVvNS zQ=rf)jQ`8)cJm3TQ+NaLU~D|8%cYG7S=>ux>+`XAun$P;*D@OCBs72!0dS`RcNz#1 zPKlAfo`OscrEkg+`JYv$vh>*;9q_6_zr<U%w66h~89|#)WJNP|zUP&}74i(KmRPUZ zU8(Y}H*7s6J8(vp$<g~%HtZ~vZY0U~mdZDulup7kYz%jJZ!fxlUy6XfJb}U}WmAE= z&pOf)YGiHu{8Zb<&la=yb_ze0*8EZ=G&Nk{Oc1=0)i^a88*?l{_|*xio%Y(rF8@BT zTY_j;WlDiNd1H=9as7X~disVO&~E8DCHd`#BBYpiC69OQ@I}?OVzF<TVqPMzN^c>n zO(o*CM~qMIC)db~@8?Lt;meyyqKUDB@sYggu>ydy0FU~)G5pOW{$uAURGV>B<2NUe zEd_{6C^SEswJuA7LGXZ$*N=uGGCFK3MLq24>#GzFAv8B4vb{`p|J)9iD}e`nUW#l_ zh3sE^jK5ycV(i0b5PY42SR3+W(__UTMC3}8O^j(67a?@SaWFNDz5-aU#X+wni${eE za`~8@;ipqFfEQzCb3dAM5LBf$-#@tbIo12={5Wb0I7C^VsV+wzWtymx8NWK!=yyh$ z&c6E8%C^7%wyC>!Z<)-aT<LSM<r|WCV~+UaZ0X`ep%Vdf;*TRrXT;05pVj|#rg_70 z<r~TH^F+pWlCTIiH2wC^yTzUUK9e%~$?GYwbC_&2K{z*FNJa*GV2z}%V^42ycTZ0V z+~!-3d{!XwtI`BsP=Ayo2L4pc;g0Tzg4pAT$mBTX<d}wuak7c=^3_=lKb>j$I$t&# z-5njibhu2$X=*oj_dXbPa9h*yyVqIy`V)d(<&C3*>5jhjj{Xz}KgvU0y_o5P=0f0O zg<P=WW|TR}^4*n+kw2EBbxqGl^06YW{_KTE2fI$MEs_H)o0#T^=T8h5j0&QK(F`an zztz{*cgK94uGLml*s<6ycKKg|TCDzn-u!+x^liR4m7#w(1-3=rQk?zhxq`u%mVT>s zM}_(*SwpC8$zbZXlq&X@N<GRHLcMk5RU!)@-L{jB07v~e7uj?E>Zhlgcb!)JTqKzq z%N>PdI|i_@>xN1ert%_3;H|sMIL46>_cA1mp`96Pv(fodF?XaN#VJ_0K5zJqJ&vq* z3ZRaSkbHSubpL?svcw=*{CbM$U%?3X65){w^|VO-{SMqwq5Rb+^mC$M5Da~LLR!_( z5mKX>NZ`I4$D4nY2ZCT%Jmr@{$*54#<z}pQ$B#wGiZt1jIF!LrodTdG@jQ=mc>%Ze zqM|dKYgiluWwP`of~&v_dM=VZ(yyTRiB0H6vz5d*ew`;A6HK2G$?~n$?<~F7II`EI zc8w@OIpBOEE4EJaN)itoxePq&mM6gLP8mKezrH-Lb>6YY*E7|x#PKJFGB%PB;wAO; zIM$p*&f-KCu>w(RO;6g(9+gCf_Ls7zFY?6|iU(q@N))Eo(uF>kS{TiPzES@Qh3BQK z85~0eVt&#e1!A(`YPHoWGr-Y+7wsh9Sf{yJCB0UyXpg<zv?c|9B^Cl+<mW@7)hDz& zYyWVsx2!9>{&KPmUE$9~GFK+BKRL$Tk}KF&B$^k;UKj^sIPOEU1vx*=&r2N@!2G@t z`5=@3UaDwjEP`#`yr`4n_#u~@$Xe3~Cm#p5PYGc-62z0jv3e#ILj9arJ_zEu1<}4F zN!Z%12uM#>==CPct=s*5cdK`rty<F`3~lE-sgTgnYwood>#&Pm{xY0R@7FF&*LGTZ zVkuXqM9_aMZrFGJ%Evhkt`R&Cia3SKKp<yKAnk=HbcalhzTA|@Zke3~Zz*e-n=Ao- ztOc1$R-1W**8*Nzvbud`fo6S%$gjq@FhTfj0dnN5YHOLoqq1>JzSKFG?G`F@4Q7uH zqB-HHixNd2WC@-c_2Oz(YqMm)mo+DfT_G_{3ub*@(rO&e%goVtj}4+bhl-Xau<!qT z9F74(r0=B*{uShX_ZZJHqTybKvL|Ils&MZ)rKRtI6qRrDQIZaC$di1MD+U4l6^Zcv z5|qD-E+{$%kLm6Gwp{yjiF|A@!!<<U8rc9s#8@tpCj%TIuges^lPYit5ge*e{C%s% ztFqCNfPU`~)&~lGVH+FT=lKoao{*2mQO?V*UH#26q@L0sDEIXVb>{6w!Yu{jF?bF* zLcz#m5C?k^IOsxh#B*MW6Mm7e*mthwm+}_R^G#~gNRy%VmZ<<i2D}*W=PG_KRlX9- z`KCZpr|i(ScMS=}CsfLoWVQf5UQl(DR@?tDvb-TvFfj}o7tSbv4SHLbWPsAY5jVO* zxvi?*T7wwo#IdH~nQx~FRv+UZINP|ZtZ9A%0(`kPFL^!6XPx{gZee_^2==hi&OVee zc=jfe#Gj(;>FLSlU0o0>vPDoZfEQ~`w*0dk2}6AyF+QdV*Vf_&CmhEuLL6p`eqWm{ z27v;AXRR(0KjJKN&n=X*pO#!LlXhI~{{OoF+1uIO(`{>^IMCS`(ObZ0_D)H5vCD|m z0;=yzS(>HEj09@S!Sco_M7lGcIzL`ADOLd@RUk|<n!sP4u3VRmwZC~Gf)QG+PG;ys zX<DaX`XmBlc7hb3x&qzyqpHK3`9iAtjL7h?g78?oJ;ZphM0mJLwZ2FNe8@AxnG@n< z%TmQh&M03`;Vnqwz8q^SCGl(Blt|(H80bJnQw85RpH%TqHtXXo@$>OWNki9D178$P z3w)U~BRJ<phB=YY#-e8PaL8K#x48r{9fNt}addpmJ$E(;qyU0+UFN^Ud40<juck;b z*Rzeqpy2pcmT>5^vRe0+Dn<qJ7DPc`WD8MB<z1otBwOSV2;W8CyHE9<Y+*J{`%RH- zQWSS<S?kAzT89uA9I68cv2sRCmFaI<Iloq@9X+WpMhT`7Vc>1EoO{!OA02Bcb%fy< zCS7n$y*x`ZDV*bkXKl$71y<{apzu5C!WSao2_dYpp)giK#S80W^<u2(>(bV@vm0Mc zQ-Tm7IK%+^>Rlt{k>sWkKK3$LyZ5YS?TJ?4%f`xU1j1t@WiKC7FG*851~Xqx6@*?? zFHhto(=_M!t*@rbfG2HwB-2*jM4iRZ)0-baF5;!e*(qA!#|I(E+$0qW!K+d@)5Gbb zgP^F##r8jzILFnfHx)KwNk-o6FUqb`RPCYUmX*gu<HO+x(gA$#ggv8(?7>=B`5VBM zpr<cN<fA*u*+gjV@g@)~0KwwLX-#8rjP)ehR?`2^-kX3mb#86L50e7oJk{A+tJZ3* zwbr)Q+QF*FdfIxdwbt5N+ge4LhY$iufB<0>WEPP@1dNCxV#qwtJ2N4K5J1EfIW#>S zUS2+belNbgfjS^Vkn+~^ep#277i{)^_Vny^ueF}F?j;ruRX~)(H?BSd8-DaR6Gfe! zonM@j0Wa37GpY{?#MWPVXLomBhlrl;J|eB(I=de{c%Z!9evaSFtZ9uW>vLH~V_R2e zch3a1Gq#CM{<Gr7M4Cy_)YjPg@2JY^buql{DU$t{OzRTl&Jhs6%BM!C=S8cX0vSu9 z1gLVuYblUh6eFCh<u_XH-@h-_-`#OR^+~#D<5|(VL_P?nAq{ik@7)iH13Lt;H%4;Z zOASMM4Sr?n<0YD(uQz{`saZ+vE9dlMg8<l9qT2^jC-(#QNwS>}ZE_H2XO;q8UVEf) zpw~Q+te+VrI$o@p90)5Y#`0Lr1yR$pea`y|#4kq)oP24YotMlDX6!FE-G3~^3bJS< zY18}JnoUH$Q$Sw<vIpJ!y0Q0<>Rm?y3xugRCyK_YOd=@8$v7DTzFw5MQHt+}YS_0s zjlDizYzMosUQ`&5ekxY)%29$q-r?bsyZT?TvAxqihzk3mA7yJ2$rDu`(%sqlU9kap zvfoJJZ%pFb`Lmsa>D$hV9K0wCvFt5p1i*_7Jjt*pI}Imv4OhYHeR;I}(+r6>qp`UY zd7fZq76@b`YCl%GKL_CqnI9!z6{C!+s_pIRs!%i*@D0<#Stm<0`!DMD=4)t%u?2*s zZ9NAI6zk9GCWngNO%^T=gI{QsD~2~eM(Y}`1YydT<CVKIm9ueuEEZl>@25kn@-;|f z`WsD;mpm_t<J!kUhw@b4<SXFa>|w9mzklj-6PXR;f4r?d50Zs<-cJ;}S2R7W3EH2p zMA+VZ`|_zgsIZG_=Ys&TV<3Aemg!TbH(R=cDr)DTU}u?@!Zhle+n3-Od$VL9kTWYx ziahW@!G~Ej+b(FLXoeNEN0jESp$rGC+%1?N&o*VV8y6APuKt{$>)Jo1iH5qVwG3*` z<JPNNI(c>NmxK*@qNZe)k)mp58{4=IzpW+GY(Q+2|3`!dnNE}$<EeGNS9LE&Fz1Ew zS0(9Y5>y~m0njp6y!73(vXw;fbOJZHyp~(rmdrO1v$Fctc$V0FhTgCtK>|)zEQk=h zgvpU+0X}qBtY}N7?)6x~`ULL#@krgu-t$U>sd*3zunn#IauwecY7j;Ue-8MivVUc2 zz<~Eg){7Cn=Sgu5q`6<MKF6;6;i3X{!|-;p0JvAW1X4G|ap#2z971Jt2r`F$nh1Bw zo^ue(^enDiPj}}}murw>SRW)t_hCBJf1MxXD)wH0KnM@kS^%p+F10@w>tt0F{SnFo z91TR*%m`(UQ+vRN`&ZPu!tS9Lb!LbV;R+sVz<oLFxe*)(v=H`m+|YD;U)o}#cx%?^ z<v##T1EEX|-!yTPs=K@U$VCkZ5QEc|fu&k!JoLd?q0ZbnH<Z2Ue1ovo3{VvSMRP`x zxCZk#muuEU36U_kH|>KW9ljLFPb=5AI|ni0xikqS`0gT-R?X1g<2QB=*_xJS>VAf2 z4j+;(Zu#M6lfu;MNrK?lSE)4%4Xqwms{xArR-$B92&b<}0g}T8yx7Q8^r-qQTWM)) zdpJTYz1}XE?_H+3-`TnUoMbLe*vArvFlld16aOhV^7Ji#eL;mp@4XrGqU7%-iF`{n z@12!`6II{lE1|w{_a#~L3}Q9#WWav?&m_&G6&yTG-emdevTjR~1S#kXl1&TYyM=Pk z2>PhKdb(Fc^WiIZ3}6LR=&)o12oSxMBAEy~KZ~z}pShs1UD#lus_rB+>U^q<-d8mt zH?-&Z4FRRvMMM_5RNZRnvSG1J{#VMUIg-WMtpFtjejM0lb_wFFj1`~0q6;e5ffE(q zmuLqiu{3vfM^I{=LfC&xm#sOYhP@JimO6(i=0z&pg6OZsi%*xR=i^y<&_Mc1XItCe z(ncE6qNST<XrUQ9HpFsgpy}V0-TCrr>#Q*G>oMHD>Eda4X&>{|A0!gqRObNt+8FMX zK*ZsNM?P{<wJ}zJC_t3ZDMShz?TAn9%j)Z<M+Lr*D6l34^RLKSo=qVfC+X}w`onjO z)sb9}V)?sg1oNUGCyZ!87|*++DdJXjKvmt$U<Oh@8_55vK=JU&EQw_Sto(216n~0? zusKm+AHW62Z~kz-c{n0y90}T!t(=1Cr_<+rlP|X>4{CRgcp;Gs0BS-*DCkNP@Z|%4 z>fv0eLx}WPKH`iY%!B?$YAcuA9<+Z2)U-BE3fvig%Y;m3#A*_nEN(%4)ylt+kC)Y3 ztS*gtwrJ1ezrya}-*c2S&7Jp>#o$zxOE70e6zj_zXm^&}Er{_^s*Fg}(X`FqmYKfE zQ}4-E+xbx8?Ypy-^Y9!buI@>l5-j>8zb=h$_|q>nPjl~`vr@-Eu44dmPn!6nY^bZF zgQ0ESl3oo?Qce{ORDOw@EFWYkUyTzS%u^jJQISlhr-#~|qktiM9c(uH^5;cJH>Zd` zNEI&#N7zSU%V9&J`~|#V5{gZN{)fuD;7h7dw`@)3AI%l#a_hWq7=F2=@L~M=bE$4! zJbzb)IO3LmcYzw9NEVaD2FEt}PZv*;`n?M^Ab@rxUjcuH?<VlxjAQ*&s?KH`&=qQc zrtHg+hLBDBGR4lpG~h{|7R;I*r2#>(o@9a`{)SkA6N<7eO{BQnt!QeqJf+9nOAYZ= zwZY|eA7;p1g4v4`4Re#~J~(fHoz^c4G*VMLNvG@S?mkssgEZywV-7gH9#t<z2@d5# zll=NZYF>13lD;ff`A(8#LkxRQrg%*(Z+@I+Dwa2Z)PWBfpC~UA#hkkKXLuBro}M*l zga{EqAJ3sPij5R7!uG(as%2*s2QNY&<U$cu`b<IN>PXldqP!TVJd*RMhzUhf%!!a4 zA~il3U&n-g*`CaI2ol)&ao$W8_YO}-scLFl9;*T#R5z?>--tOd52dB8)4xm$!0`#y zqNhr0V3!JZ7|&GcT=CGs^HO+Y3SIwU;obcgWKSP*>Uz!eDD46~Kc0cq(sGS$(=Z(P zz7~W-gj+S;-Q9oIg&`q#bLXZE;Uqi_S7pRfZWAgE@1zM=#<Jaul^YXfj)6QkoXic) z?&l`~zBGGZ%5*e+8kUKadn5CW?1E-biGa;ccp5MUYP;OYefAH1c_in=wMMbA?axIA zU2Es}1*+u<IuI&d9W9(4#__8d$d3CaPi_~0Bw~GdMnHoGK$d1Q<16}uHr*q%?me8R zSVI&c-IRPdNdKdU6fQ_&2Hh1!MUf5v^Gd1zHSHkMD)p_GAoHn9ihtxtwx>yUWr{Z@ z3qDR3p1Q30qCmMbM_kA@(zGpHU5gEiZSo%~(N#4rxB(Iymr&l4NYOGPgfIhO6bR0S z$`{5~FO5;b%4b;w+qX=g$ZFV@B`$(mt_bQ^B0Y3y`-*fs3UuHA>4xgg!Am+2Kz%O- znueDEPlmle^Q9P3z;*rKvn0#o`Aw}u@HsnUsE7vec+`KV|7Uw|+PkR=FOoKt{3vBb zZ8Gm98K*`v07ir;Lq9swwHD#TpniP;>b8tI+H5kJKFZTLU><7#1fTr`8OaEOH4^-P zARl2XI!Oa3`ZTRKbs7O$_ZZbu3QhlFtn#Dm8eL=C_+Q83C^oE4=S0eXEE=ggm|bgL z8l$ie<b9GNur{dP(-X|RLu8pIRCE4ZQVq~DXB_LQ`1Xtl#oO^5c>9`I_S*Az;TJU2 zAwBL_Yn_AmuMoI1LK$0<g|Ox0isBHc4dcmEf+ysPWC);Wm6|&TN&4kP(dXyY+t2Ai zun^$n4p=$xLO8}9FhXPt(u|MMSI|uz;8pXbP6?zr2C(Kw%I8Kv3!@av<5Vvt>F0$B zmqzjq<f;8`8dk)J`pm_C@TDy$3K?~6f8xVT=9|9CmhQ^01HQbqvCyFcMJB&#K0&Z5 z4Ov+zkT$)Qu0UEL`!MF<8C$dXV;!GRq5hdvI~U7cOjIEG6&_?{1sMq+F%RU)X*$z$ zqM(pv1$EQj0_dweCF;6vU$%5xhUBAc-L`b?-wI3@CCxS+Zj=8+;hS0yUN-cp9a>)@ z0|IBSL~*C07%<w-4riouo3+jDka@s_Zf)yyuQ0BOlg$bdI)zF=kjMqYvqO08s%Bw@ z&II-Pth)XAH7|s+z%NzaWjd2(l<M8!Yc;#_jT;h?F0M}gv{i(@<<wKueYskxacEWc zPL_09xC9A1dy-cWxl5z@up6@Xf`&Zy5Lr)euM3)v<dOCp{JxA=6QD0I)_zMeeRf{{ zT~W=uNs?U|&`%`8o(nqU!eH<Xl>!vy;RqzDX-$&$(|p6Tu}2OPyAT@iN0ebQc_coi z?+cVuLd8>Y;;XXTR*no`o)`Tt!_Yc$bE&7N^ZOz_2oTw0r~<>?nFPfb=MbNFbG&GE zQcatsYe>EN;C|P)g&J_83ZO-OOiFauP^M?avk-z6K#iL+pwF{pNOwGc7+b4mhC`FX zQ~(2S;k}Y5KUQ}8?NsR-aRLM}Jt(t7IG^R#e37q1Xs%)Cfe%5rOsRI>l-(JUT(0S= z?9SP$npAonOMeeCcj?+X`&qx>%U&MA242jWA*>TuYi9?;Q;`)%{d4@pZj0pz$*>`s z>xd9pFg`1)RktBiE3jPmS`y?*Lh_B*#`6%H6JdZszEg07^lYGJZ0pVw+}@obT^J@9 z@O%TgCyJlbHi84Oi?*(>t~Y~YH$edZM3K^_!)@{(B#0$x=`B&1u{ZT=WBBul>LqyY zKwCN#V`U<DN(lRC!9cDNI8K{M<TLbbM2c~J3=h$e?nt5$QuN{9N67ET1OZ$}G~=~I zA%UWM@ZkPe7i++ew@#6?*05Xf_06bK#jJ3iV<3Au()pb>9VhWD)eLuRTYJYlX~<gh z!YG6h;`=h4?-gC8_U^N(3+%00R~!WVdCTIeJ+Ek^$lCiI9lcM9+0yaT6&-96%#D!l z%y{Htz<}~fjCfChvE$EJlw1_XfS=?Nq9T>9AMSC*ll4m?VUVKEz>BS4+D3>jdF_=o zcPDOAB`}TCFkFBZ1Mge9miFmkveP7W4!>b3QN1vf@$}n)Oy(ah8@47ZkecWd<k^vm z7jS$SN*qztF-67UkrTO%+s<iMM2SI!_Jv5fgD=HCm^Kf`{61fi!n^x*z7$|6xnk46 zyR?|LofSC+2|vvewY6EgJ39_vs<!ju%)rupu2wt2UNF+tm$p3(`tY2jpm~Uxf0oJe zLy>A>xO8hWQcbpmh(szDVVRB?`i?Y#7fCIs`>kAUK2iXAR_GqCDO*!bgDO-1<C1m; z0RpFJ^9Yc~Rh`f>Ft7IKs;7rbN;Qb>bf`e?iWMWKpfAJTk9w$JTm{-K=Jt>CO$eoV zUkio}XN1QqOwY6K3a_kP9;*QX!hlk>O_|%|KLT(`kQrbY2QO;3XR4hsym#}Pb|i~V zUp2IK^?aHSO%Ip$$5dfdb;StZO%fg}P)x@&?E<MTfpoVpIq>Dq2;qE`Ci^B&xj2UR zYO)Biy1kJw-2#F%gI5oqrK6+cz@<9yGx@6va)a602r#aq7Ks6#hJ7Ew>m#OCZad4L z6{!S1EOcc(03d8c_Nu=8*wzI^w!I%^TZ(i|6n7DxH8-3cOV&Nqhhdb4SE4x=B&{PW zMxSfdz+bc|jQ4m(6MTqE0A=UJCS6n8_}`8*?11}m7DdZrZfS;9JLEbCA2#r;nvN6a z2^y>)9LM_=eiRcoyAV$402p~C!2Q;pfBwEOT0SF~;ert?AV4vdx}kjJ-Y{z8XBTP@ zT&n%@oa~1(EnWZX)DXd>Aa<U0;R~etm1X*|STmO(bP5)2OVjMhtG=cC70)+=EZT9J zX-Kexw_q#uc7dW9kprs+*&HK0x7}ewvFHBwRPiyA@j!tNcr)z$xo&v=fjl*iWqLZ< zSlZN~YHr(>BAy>iw-00^4m(CR39oRAP|c6l&cO?3g)$a|F+Hy60<P7pOObw6s5yCA zy8^adPgerG0eMydWPoEF&68?d20}wbmf_9f8sJBDKr!D=e<E$qvrG?AyiEjtPiN=5 zX^;bK^!70}&&G?D_r_)Qb#``bKBpciC--B3KnlRqo^w4Ga#R0JacvUK*2KXk&*S{; z?(9aDL3oBS=vwtmf?!54b6sY`i)kje5cYQ$mB-8UezZpgGK(Ualkw1_3Vq}UIzpb~ zMMsumgM^a;>C17fQbkKw_XBR-U2J9j(nt=xk5d5M4THEXUl-Q&cLV>HWCX`<d0(xz z(hn7y+FwgR8W_QT+&iPOQULc#5Fm1kRLl-%J$=gsI!lG}?(R$}2%y+ugj<rNuO{n| z65{{~z$lS^eBOKzB3m6VcSF+;<jab5*5*u}D1z3c8C@})LFr@;!K{thwTb2G@xQNW z!A#)GUJxb6m8pi?=NFO|`0!!mor)Eosj9Jl{2Zp9ThlUelMS4zM5wF0Xj>A5UtUsA z_GiFg^j3;C`eyaW@xwmu7UYqZ8QPpf1al*mmCB~kPS=gAG;GPyt&CHHUudw+Ha~<0 zwOFiV#q=qw0bab#XJ!5S^9|-#h@ucmV_#}gU&3H_=Ud6*7vgJ?HjAg3E0UC_%ZD%? z@&%3aVylr4#G~qO8Itq-2Gn)kJBj=y1P<`HH7|s{KD};PLiN08^^2+X3lsGVVl->d zHLc5OUVHBL%Jjy?sit`;^-I%C9~9qP7bkQHq5myglFn&Dm1*6t8~m<3F$VyOVjm); zsay9HLNkb(f#9$=WjaoBp4Tv*cegJ~I0sq2=|f&m`s^s)rm-&^|5d?fo1VAHUp@~X z{$ObB+LR&oD6QU`1+9t^I)(7u@QRs4)z?>=-b|7v(dwH!hgg0+QC_?7Y^|Li$F83l zz-yol${Z|zJ%R0A)qF$Va=ZvST%!5qiUD|$opB0eUCgs;WfW&;su&!*dFom{JZyV= z`&X9?;6&9+c=ngY#$GF0#$&IlT*8###I3b)a;kn5-_xC}U!7jNGgHBSvb9ZjPmlX$ z-HY+6SrKYyyaM1Ly9oK_vx+bCweDB+M+()eqId`L6hB@uz7hxP88*N`4ruXmqU4kF zifk(iT`iWbFD|z@_)!L39tdS^FKx)9>Bjj6>&?Hvomm4;RXK-BUQ6H&_m+bwHEUz| z&OscP5TQr$7~np#Qd=f9P2BYRGEcsUC`1<DBL%aNbwjEnnnR!%tsewaVX*U|%qGZ- z#0?{b&DqVLrc0ed#r7fcxuKkI@)VO%%t<Kr_7t|2mrA5JfbYrQUW0AOf$+r@>1~#w zZIDgncdPNkWYMc<)WDmwDptbR4fezj7b+&B5qeAzz{W8eA77pT$>fgvJ&hebVpCfl zr@oNaaE?`1By5y7{sx)a8P#`+d5sa})!*hqAPBZ^E4QYIKPzsYgp;|TdDBAplTh6G z5l<MFZbY^-K|-ni_0?!D(r+t(iLCN?QD)&KH>>ZB=iQ;o)C-7Gq*BkLYFV^`NUpUZ znoVpnsv6;&d$NViOyh6KRKrWLq8Gz?Te1uQBZZOW#TW(fXZ~<mcb40{F_Am6m5690 zachypoWiMJOB75Ap!N~wP^SiS7KAX@M)KFiLvA<;@DqVx`Qk7haK8zE_;%j3zh_I< zM6!V&cTGIf=!C0l*?C@vEE}D!ni*#}QZl;do5MEj%8((P6~`-gWorufBdKc`%5$}r zH1-n-Eo9xV+ftNs!d3G_8Oglc_{zFXDSSs9nWk>;?d=@2)`qH3AIg@^3==NKvK?`9 z;LUwEQx|%D6zJ@*bbVUbxRk)_eA1QxL5$b3>q5uZs|XgAsclQ99*MV~rhbt(QkKgN zY10b?89)i9g)yxk-@Qn2o>e<>Q?aM3yF_uv#h><do~lIJXzxYel)xoIW5D_YIea@5 zdv2uShU)f^JJ8H+`?Dkq67+VVig{S>p&VHZqrODd_7VZ<s`EjnMrUfZ=9BsPitd#t z+0pX4?$Jp8`x+>>ZB3H6;57Tr%Lcs^vbuTSMFTia-kzb#Ve7{EA?wWDu<h|qhSJ`f z_HuG9ie^%`c3!RNsHnMzwD~}>CWmlgOJ*KHIv>xOisyg;9)i4Kl3Tjgah@7ou5k{P zE)Qq9lbdX?YZIG{`pHDIOk1;SVc*jQFPlnGIbmUi%v&8RJ(w-uc~03^U`T$pk-xm8 zLHdsMcmG8AE7*=ftV2b$U*)P7B`C<+=JuZ6xj2D?AANHy&(5oAK2ha;O$#5pCZ0V# zTryCn<6iYahJvg%TXV*rNP1_q2%My>jhCGuX@@WjxJ_@w@&1+}|KhwV?Xd!x9CqDX zX}X0G8c&keWFA9pk=WR}r%3;53~caomlGAArjN9~-20&Ci=u{iGHV*mPZSQ%jO4s~ zxgoJ~yh~18oq1E54nf{uDvw`>4Tk%}?b&4MUJ%9yC?N=@T0cIo_MWQow~3pLo%ef; zzy5n^IPX9~b$drA@Vxa)p<+zkj==FOfacB)m#CY6d;0AcK)1K1%0Yl=W1RT!#dWMc zxCv*QR%aL-{kiZ=Ta=)4x2_q)K)tKg5VI{0atao#JF6+sHVqo*AGs=Uin$?0ox(N# zSx*vGz%PXQidD%5w;;(I+4Xo<BRE2`$8gssh+zNvgB0O(OkX8}H^n7H^tE+0-6IG| zdfP2bz9(I5gI$~0#2N*1;zrGyIQi;GJ_w|rx^6<1X#t8H&u<<#nc@9U^N>{Dz8YJ~ zcZHfG1&UQf*5h7tTX)Z_P!5t|<%wii&5w{B&6A_bp!s;lK*<C0kLDP}al^2zIcYKt z`Yc=VMw)TUIpfhklnIhW!!}{briJ00rSa9k2kBzth*xe*LFg=CZx&C#Juh75iWjDe zp7E~vjU8{mD&1e;hLf#Nk_=BlaDUeY-MZxJiYH^XQzAHTTrd*Kwc~u7oQKGJlDxmr zU~V6l7)0ONg^(PgM8Lbs`tg7LS6B1zT@yDOqgbY2N_F#t*<a*UH@8}r6PTWt$5x*L zj%2|zXEH`mARP>FA(e&|u^Rh8(JRRk_G7hCNc(1e`<_hc+GxT1=MC?~vnS~8@D(}` zESVa?_ar@0@dS>RIfckS&DD|(mOuUL-k%KtKQ8cQdzRN8A*qqx6M=Lj3CgQ#I#x72 zgbzH(4rt**!g%jGPtf3sgAQGQOcu-kCJkxn={d`6$mTabj8g#sLMU|vX=_VsN0?1) z@~q!xU&FBnZ5@9;&tAYSgDZxM<XN4jxnCQC*}!17tcaE%u=}V(H+3yh@B)_oSW^`m z@L_$HB_1XHSZ8MRHPC^7Zt6nnjNl*cboPFDM(}9{G=(5{!K)mC#4m-j--zMDrv91) zrK0gS$YODWQ68_Jf#dyDHm+D79x_b|g>@QlX|&S+%CLCh7|NcNXxNs<eSG6fDdGj^ zYc|EQ#`(ZacP(49YLO_tH+4E*yeXbL+@8Y5c7$ySBLco-K;C=*>T3P>1Tx|liA{mG z8YlVC_vdMvT07<hRz=e4$8@?ZzG+er8=#oZ_=+b2J5`1kBULX(tA4pOEK6r=N7qDr z1rMdUt@E3rYP(=)9-bZlhhc!Qc4W#HpQ*jTsh^0iJB(tq_e0$INOD~UzY&}!_a{KQ zp|~eXR0l7}odY@cL4sN1MqWzIJy*P(IAPs1{u=cNdecRXt`Wi)Gw)`y2O<)X&N3y8 z>l7|LR8XD8GKJa1CeMxZbhbsY8pEm@ocs|^n@N7OfU3Gd-Q17}mD=QtVIyfCew2O+ zN2IXAlQxi=HPHTmx-?Or&M}R^Wf#pbu8orII;TBcAO-Ffu@wF3GUNI<(W@zj$pqcv zSj}Rb;LWqDR9bCEN5_U_G4Nr!1TbER;7kdj+u;;%q(BGCpz+@|NG89YDgyz6<?-rb z)zEdXs}qG&;%X;hD35PkkSLs;U9)D~$ZN4!oCD}cJ6h!FF?Xd3O{SqW0{}$eO$I@n zH_t-WvsQAAtqHW6iBqk;Jw3nwyZf~y@s9J=Mbbv#UU|AyKc>@Z%$*CPl=eXsXCnXM z^av%_&LJol5#)j8HGd&4^d&A@_FO<nx)w!A_nd=heN{`Rt~V@=RSWdLPQ)40SjKr_ zLU5AubD`=i%hcDg7fHQXMi83YJA6orS$KrZ+##4Y&P3mf;wERTEP-DCzYBRmK&i?l zT(+1X*bzVQd0KjVXJF|wB5Oi8zv-IV!)#)c=R*!>i@z+WaSagyUzU>}<CSRPYr{KV zu+P=k`W%7XR}v&UGo&u4zSQWxF5V6Stn~zrXSrcyhc}$sh;TD{)9r#dz@ITGMB;?z zFC&VM=BwUGmX_*}E)1=0mN>zkUFYRU&-Bw({pAl%IOWnPF8R;nWoox{y&fwB0YXPC ze=czdB*k4<FH5hVPkh3~`2Lb+M_xl(<#=<O5oV+)A+n^77%hw=`SRh`tY>D?oP+5g zn7buIb^rc-E62YiYxuUbdIFKRPtc{>A4}_nP3_?LjiBqgF`Z7`+BF+11VL0lr1cMY zmea75pj;3x^D0(74_`-fPtX1m^{a_;hal0Ma7Lccv^_<(CaGH0G*JuaKbB}*u~OI! zOQ0AK<F>CU(sVRqSGvpz#c_f^88i>JkjII<XPI>_5y~HnHUIn8d7r+c3dpp(KwqXa zhuOp?&y9E%LDRxz2$SvUs!b_M47Ij*7(Qg!Nkn=U^w%Hx@*)@}sI4=DQ@=AyJrj*= zbPVFgQENv1F4n~I0b1D4pylMpIg+n9UIbbC8f}{_-GSx0$zj45aJ&U)jBb&dtx4iF zaRM0GXJVy?a#gzKKY3Z|Xr6cmmJb3%%cFQw<KP>=A!}HYVgjctAK#e4Y1nn~cI=;l zywi~wLLV)d2Q8H|EO+rtBIJOg%nIjykX@~9wASz_(=?wXH%ut<-fsK%(PI6cb1JCO zygrhRx;8eTXBgUMU<C-1!-_1#&Z*GNj8uRC{>Qn`7fB!<YFkU>+6M_Ds`MWvi&n&| z8JdYIl>Y7C|KHMR`K&O;$;&D`AL=4J%MM{WB?BMM%dygLb7in2ya2}@C-UOU_0!|D zz_Ze(0BvHEze+yKmA;oML-+x_DUQK{lV!%<p&YB-WP=-?4ZFr`h=NmB^?S1hLcYCS z9s3Inz`e=^!`quPdK?)1R1QKU@VV^0sOVb_B4gXhVuecpb?-UFyE#U;P|1tY!mqNS zRe09k457gA>l+u;%LqLDEmLoA_e9+OQIc+2n841LcdFcE>3AX~iB;3G<cwi^lJs#c zM<my-ORw9JA{_szfoFv)UN$L^1>^70;$gWr0g49DluvRL-;x?3t2;FmKutu^uM>&9 z-OAhT-xgONDui9#2bi0+$1W**yT@P}m8+ZG@RF$^f-*(3p|#_qbhRCd_j1DX$m)Qq zGB^Z_X21qvD05k?YU0S0q)i=eIDtKe^N$PiNq)4jTRNvex?3>+gEU!1pV#(QJa=6z zf1Jqcdqq1vzS@<*uxUS=*yJx1wW;kmsoFk}3*0HIQ)`o1L$JXgr_o-D6@UQlzCMQM z$2IWQvoher`y^FtXdVR@Ad6*XI18bG?pqpOjAt*8mQIaO&4{gA8xQ?_SyQevA1CSh zLAKjsnG-IaON7qLnkLkD!&VvQN675G*_c~~$MKz4XI`FCKOg@@AUct5*ql{=I7d9r z^R{>Nyqcu=tU$4j$Z^6+fzPlG$bl^A#YpDS677LY^+m!)E63kUq(zczo7%f3?zy%8 z>%VU3E#DR+LFQv6y2E+$F;x{Qn}3^$7rTXuWX9H=X;OPX(f(W{+l@vu9j$E7Vi}(K z!Upx5{!?UOG#BA^^Q1UnrBfo96ZBNe#0IBO!HjVJhFG4x4=tN#S|80_7N?dP22v5e z$P|2Y&3xYqzM2svyu8K@_IdF(<h6-S{$hdE)Tif^;52ngIJAPu9dd{7b7cU{JN9G| zCA|IhGZKJRjes2w#k~T08HR6fT7R_JIauI~;eMVi+m;4Fjpjka{#;T6F!J3OR1>`# z1&{xJrph5exI9{ne~g?;U}$-PsQ&p$Z#0qK@KTy#P5gK>tg2gn+fsP<?epR#c-H3Z z`n=(3wP+?w@O90n=b+uW2A0;^I49VV^;ViVi!p(%S3A1yU(x*f{S~CK<LR>6pNqy; zRYa0D&5cw{@}XdFRxb`001w)H42xm-XUO%MRT;Nq$Til#z5<hlZ~W(3n(^Z-;m29f zsq)6J@>PG!g7)S^(4BvY8(U57oh_E`EXe%Ka|uV*&5u#T$ZHoWwGU%V&^+9qD|ZbR z|8P<HP7;Fh*&O3X$pTlbbZ6c`G-*ew=y3VH2jlx9u;sdGaa#LumQC;3#3p~a<Oy$2 zM{{3}S49m)N(`%oZ?dHT!&{ZiAGH1R3n~yOakiq2*U;H>@`@Ir?C_*+OO>Q^Oli>2 z?ZrcG>ZanAUlrBrTRZ;;?p@o~H8)xc{I~_uI}bM!X(r%BM&GRKe5^AOmSTJ<->`Dr zTC20`{<<XPyJy8OhH*YGs2PDt1L;`AF}|K6`Fnv;Y_vAo?2Mv)SEz`mOl;Hha`mqV zE*U%9Em1TRx_oRIR_8cI=McV~56u~cbnDxCRs>FvKPxuG@LFNeVOepf@abZOZ9iVp z*ay;=$Ep^^Xr1s1;Kc(!Re@7X;K6YtsMegVog2!Xjpe<7;{xxhkIt#xZx}kq?lZ#Y zdM8~9yqR`b8NgB};$a@n6VHl}0uS~NC7Lg@CBT<?fmi=&h6MNvf#*QsRgtjnB>ms| zmT@IK_>iin$7$Vc(Po?2<T;Rox#Fcn*^Ef}-l0ONu;w|OBL%^{51#t#@@S5Icr`$c z{vpGI;3@TXzAV_m39lTs2&F{av^iP1GEPBOH;*usrMFjR{O!kUhB*<m!=;8omWc>i zp4qASu2AU^BHMQMkzJ8xGOds09>`U-wLL<C&<g$Qne_)V#=Fkj)7$%IiV~cnIG}}Z zp5c$25bk+hvo2Be;klYLtC18%(q*#hO014fKclrKZP|HVt8X-;Z`9z%$N)?<S@vC! zIr=k!JN1n?KEJ8`!xYhsAkKzZ(JVCEJX#0jCGqU{vQ!IVG?TH4rSa9y0W>!Z<GWmW zR8?&hwf>h&%3m%imV~o_AHy|>IW>egJ(Ra3PHTsjfFR+AsgjcwbtcQ$G6zhpmhI_E z;K^J>;O$NR^GQ6C`Gz+#wJ)XC9Vmd-L~?*1CtuLud$k&Pi(G;S8ZVYe8cx!FQ;e@w zRt#A`HC78kZOCgAoBZ|SSE_g=Q2~M_Ae1(w{y&f@wnOudmJLoXc|VEoO4P56;##A4 z<0<;}Ng8mHG92>48$ZrbJBBJFhKJ@fwjotu{vZRIhZW9<(l}rhQwgd`-jtW31yQty zG=}LJ@Hb!90?@+gSf2S0oQ1J{e}?3z%eu#_n=@FZ)oF%}!`EI}BRCTDUWR;9umE^e z&52TdTdW+}T*=@!0Uz3$L^ZL(TJ9{q&I~)DpJhoWbj|=z^6M$Gv`XFP6n+@l&^;DT zB>29LlAu{(q6I|t_kBwk0i{SUOK_SoGgK5-IZ}1-!4fU(=Yjyq{6Oj_nNq0fH-3|4 z(;q#iABral0-hvplSSCru5IZMH2m`)IkJ}$)Zi4uElj>CPH={M$NH&)aDbDW!0}s? zLL>p#|NM$AWR2To$rUuQv@NTmIQBuJYf$syLL~?hZ8#(7KOHIyDin{Gv+f+nG$&Cv z$+pgG6Pr8-VrjR0RcHW#B7hkJd1*A-wzCpDG~cswu-o-zj>G{g_q%QtkwRx@*FTCI zz|UmDP%aXp+-Qa+srAbu1qC$IklHDVYFZk`1%7-7jC5X@$SH_AIg|lA1q)-eAV>mE zF;~Y(H=L2YmNvG4Fpj3)d={FHR~#-?KWw;g;*#pKY=x=$k?T#8-PxE`zhjI(@1Q&V zsYLNoiXNP%0$;`=qR4FN9Oc-G8sJagdcIa{u(m9b&o}SNlDOg~R=WZe?FUkg7;1*C zwfE0KcoH<GXEL9_hF{s*IO)R{SKpUuXAy+Jo3$cA89q!3@x~PX%y6L-TC(??B72O9 zSO^?5n!Ybk&I=b!#zIp=#GBJpy4Ef$k3Dv|791x7Klbhn@q|9uDIqlY@@^=arx92n zM)k&-0bV!&0KT%OaePZK`Mk#IvDE-Yu_-;9*yJylMswR&#U|j(cSaBG^X^F%fneT_ zg2CFAS9fnV0XklywX$F8wloO1GeRqdSQfPO_HK;hP7jk3hH~zDd%MvU#^teE;KSRJ zAo;GaTHS0}kp-<FK_%*z0@<%0rb~C_=#jp$XxW@dUe;K8`Smq&t)Aq|+kdg9ySuZ0 zOJr5u<^)mNW8=WcyDF`2Wh~d~VadcQeH^b9Rr!cxzqzx=|5p7}e_CI2dFrSahPkdo zy7T1J8m+Eq*6GYY6e?B`xf42P-0-Zgi>gU%)2eXR=Ck6pkMTfS^T30yRXYaD-h8Z* z2tmdYXiG8#`~+K*q)|_Grs(MES`#CLC%~b?n#b4Q$BMs-mX71sjjM?g_(UIF(1%x! z?!&@6>`IlcPBVf)wrdEhXTlQyNIJ8<Z<Rxc^i;88ZY&!FL;L^8GX$%WgKyR7ZjXz~ z<EFZG25hGK*^t*JHu>uX#<iCdga9Qum_J04Dvf5^lqj+b;vFj)+#t`=-RtB*@w{%d zG6*(3oC#23-_pTIjWlB|RXT>sJT4EB&up<+P86%>gv%!f@(y2AiA}9Ty3E>^PXEeU z;751z=YM;#`e_=>5k%e8w&85`>@dmkzH5VpKx>#A&if$e5j5P?{Q6?5VJ^YS=CI^w znsEU^4o)yWJg0noy%_QM!Z-jWwD+Z=XrnO7?#xyIFZPEybtbd5$lKlB{f~V4MEf-W zN8X&Nh_6sP2GHh3iSXnxc%UnYqz!Rg`yg(CuxW7op$vLGRyZTE>7z{P<MlzvlE+0g zKuhODh{DP0@n1i)?%w;EYVZqnUbrxxJNlY9OV^wMweHB2%#Bj!iY96ey{+s1d@LKD z8@rQ)D-yZ(fr6tXWAA-rRT?Cd)9AGo!{<L(BW&aE(;`*igpEnVCN_B<khgbUC<FNL zPyJC*`?!z|hBxDRb{O97q9G!R;HMjS#$79sH}tyN1rHs|A6)(^G`7B(tXLi+FNYeR z9*S6{ofV}4zRVBLNk%>q{MUnp>iJPBC$xB7Jbz420Kk4|pa}g35#qKKDL}#a_$UZa zrns{lM&2m4HKUy>sa_hT1Rhl2$9_9q+SBvsvfI1vZ%$DGFV^G$8s9Vuo-RWIu$-NF zb(XGf>+j__l15IR&^hCVV|`bmOQz}A`j*`V`fT<Xz=+>$nS^F9i)5pQ3Qe$}=EFBz zletgKgj}&{d7NSaRvbmq|5?kdP;2)GnM&ZrS$9U(+-w!1y`ClmVb!nxvG)3>OP0xE z?@zVINH)ZBwr231Fyg}{b-fRekhsCn8$+(~D;<~BQ6|4LHA4LpNpI^CXcL<}56C+k z%L3l~Q&)7oPnBJmOs0>|ia`k9kuZ4uw5{X*Y`g@0v(Cy7nOV~^hale^&nw`YA3wWy zsb(rc>wZc8^dYTX_diOPyM?J&CkU*(moG1<fIs(z7-a^346Rg`#`5e#Wivu)006|I zaIdrv<{y8oHB$KXnz?86Zln3UX0!S2H0k_ECGcS*O%<aR9#;m}K~KXnK#&-CSAkW@ zBadc}7Xn|-u6$$1SdbT8WjJwFH=)`Uc#?PIX$T~BZ|{R|Z`5x`(X2nVTy35bt~^yT zhAQA~$wGkFypbjy(@h~hb5G|8hj0z%t;vcR!P27_mBX>8J}#(o^_(dA(ir)^0%$^> zDj)%GvK>Y`6+?q<kA+dPFAHn$cMYt7q_T}am5d8{bHvS#C~h3B-iEw3vB~owAh-&k zCA-t5G_~nz<lUMi23P^W4PMK;+tIxwvD(*aB@Z=qPDQhUf7Qb(MOZ4;ElY{eoCsmF zWpKKS#bWv5yvzk7^Sfzn(sB;Vup~+d{8+$`yD1gw?X<QEBaTt$gyy*hv77r!PLRe- zXpST95uo6&SI<AApN(gatm=cyb<;wnz?-@uS-2*KH#Y*hz^WVkwEf8dBeM^pf#gv< z(?y9~*Z|mZ!PwquEhiPy<+eiae`8`h!2uL`bB1DH22vFj%)V1O7GC-l;fw`I#@*T0 zrYad*J2u7(W<|<N<xOLP-2_Jcu>!>o;z(A3UzvJQ7~cUc`LRS#8!eU#kNHW41mM&s zODCkE62g&=VmM+ICkl~1iIaV3`%9WSJKFmL@8bHlMDn<3!waM>_JM4_QmqYnZDNz> zL1qz{03&@rNmRrhG<{=ZWZ~9qJh5#}Cbn(c&cwED+Y?W0+qTo;#J26*=e+0sf&Q>} zKhLgORjca5Qis^WJIY49$iw#IH-g#KX-4!uM3TMED+!P;Qa~!}G3oY{f96xK=aS^1 zlit(g6-dcZEwZx6z6po41=**T_r6#Cb_{V~jyjJrT7R804~?o*b4_rCWT<0e<VxtE z6i@wS5#XN_QYbe78;8!veGMu9S+%OY!?N&JUO(J$t=vtc{U%K=Y!_d<I2#4XqsQ&y z|1vA+10`#QD+!)Ylfc@sbOYVUX&0Z=OyCI9|Gw2CVNKN2?z?S>#fb_4gCJJSRtGLv zU?MIWd2#`bv}%=XTHc;p<ZL#WewkFbX!-||S^9F7xdg%jg1Ey(+nECf*p-<vX+Fud zggHMR-QK65$A6XYU0n$fbtNH$G2Ev-Vc*URlYfEBZv-W_?js#^m)OJ=;*El5RxS(? zC&bm3)sxf$$IQHA4cIMc>g+_5Te-X*o+*+nbkRq>X_O*~VVYceLB<~XjmzD8=Tk&E z7;QTT;jmC(lT;rg?ekFz&eokBVS;J4FdLThOW{t{)WCLY9W1ek0EQnP^DE-@0&-aY zUY!Y)9^{rGp~kK5fx~oi+NSGlh%Z+=^z+c6GrUX*1RsH!R5@U-Yc82*d6v<doyS@M zrBoJ<(vEG~mFTHG&PBgr2;DeXI5N&nY0TOE3`O0AVi!UC2>T><0?;^r6fNu6>Peco z(r7a)X6qE%(b_ti_dd%U+Z=11KI*i&7jykfrxUi9D)%yAw>!IPFuq<!wfJhwBzuZK z0gN|U!q-R%p6sSDh<JQ!O34YPqqDP@MiIoHJ&>1V6K$ulH;eR*SZ_7Fi$h?7Y2h!0 zCBR;idLRy8K-*a1xlx6KVH|akzZpr&Rzx3&dOQ<SZ#Sek$1n!7@dP+CVHL>(%<LuA z|4s}kSVunZdv0S`g&B_2>1u2ubQyZ_X{i(2T(qt;9eV9d=HZQ8elP7;AihLzbMb_9 z@F)?x^-pY#FKAa8@Ap#K>*p2xC>;X~ns~E>#M<V>VfT|a_1!lNNXKG{SwjRCq`#kq zZAl&U`m}_RdxvyQCJ!Nw<L+p14L}p7r+|>HW6yfp7(WAAF&)JCs8pdkXUX<Cofb^! zp;2sR2}RUUe7I{-m#$P3$5{i%Iw5AceWapKD+8W-O1ddowcKqt1rBJAL@RNY=+w6+ zv#5*$06x4U82Se!8YhBv4(ersRs^?46{1&Nk{0?Qv>#F(j4xfqiU8Bt!-_MzRYA-= zBd3(7Se62qfm)2)E_aoLsxXFy(8`%IqcW%@Fe#&<gNR9MI&MHCK$A{NJmJ}b(Bywl z)Sf<~G<*^()bZ)?ChyK}tZ#prBeY|t)PiW|+TA>fIAVkIAl#?7az9<iHjXlbK;E|` zUJn-Y+Zt-8|ATS#x0Rg;#tlaaWoeiQ741HN%s;m<6IaE%U_<+=WY)>Fkqm?Jt|wDh z?)<#)?bNe9hvE5W0r_auoqkcnsWyV3)TKzm&OxH?>G^5%F<~V~2JUR@WD0n%VLdY( zb9F3Q$M2sj=!Jc-7YW>9l7RxQs(J2yq(NOeERV32o>}iJndZhBI*orLvlyLktAa!S zt@i9$o4H1xJqc8E!44|-W)f!xW3{+$wwn1n>l_O>v9Z8RxY&LE>AnNkVi{OvDQMo( zP*vw|?LJPaBWUB&`Y=(b*|Mn_=aT0S?PGsDEa1poZ#7@Bkp@u7R-@h8Xoxyvijl3w zC~;Vu4O6-w3l(sO9AuNT-=6t1pk?KHnh2V#L$U~P(KP*`WiK`d(#yj=e=&9e>0N9= z12Ge%z^<#-II>h|5#s9_a+0&<Ip5Vz;F;*I%D|mkODrBxD|>(bCYFKkT02i>CC_IH zwFMIj;#+Gdo%Fu70pMjc1DK|+dPZu0Fu%~KSjS;iiYsP$JE`_1gMR!0FkQ6pb7WLo zd$LGfVuJblZBdi?vxh+rxIS5M7u-+V8W^;96YnW;x9TzCiB^Oay`EuZmUzlHZS==` zRoS5b{eC4qKqu8B7^c7z*E-fqpK>3GR;RFHRBx0RaL4siLFyX&&0q<Zh$Z$!SxY7o zVY&d^fqdQm9?dKq3-kYH93sI$T@_=jS~gX~d0Jud)%a=w=>wRVeEy)HtlscfrzCiZ z=B+`J|72@vBI-2cR<B~mhN7HF8@|nn^m-3~q1wFbpf&Ih`|+JH!dN3;gIGPc{dw%) z|Ldp75$~;@!a`#y2M^jBBKA=M^s*zWiHaBSGjBk@ZICieK`990JFIA0w{tL^Y#LVD zPKBKJ9MktM{yEn49sj+QQ>2$%<^+RbKfOalThjuU8Uo+!hb6)W+VoOrBKoaLow+h1 z1*+YXz*q%EpTKT6xfrOT=0<sC^dX3*mRa*UNc)phN<eHu8z6}Jk^qY6HW6~8<w9|P zEEbE{zZmYm8@9hzs>-t;F19sSSz(D1rO-%@(V>JNWzR5%FJfx{>rNaJ-Kh8~mV6au z-=34E3Dp-_x@O^Q+(N&&#dD7W-X`8x`7hncxtN8o%!Y+iweug+n~r82ew}175IU(M z0psoB+^egcS65&ONY%#nbCi{Sk&{ft*(qUvLVj`eW#XY{gXk`5{ui|b6N;hE2HWE# z48F$!b<P?L%|9K+I@)!!Q*x?n+h6?NlVSVhw=zmy8pfPj3IF}t_v&xS9&j1wo1%z_ zW{r0oNo~3G!tN(bDgBt0`lK11EB*WI_uM)xzRD%GU6o5P@U@qO;$^i7OK9tyG)J#k zM-jEX4nPxBmV;55g3cc-<It@P74d{c9s}J{eHWzvD!JNyA~)NVt+H^$9__YqQRwH- zh72110f(YX`TM-Zpv+lN>1}ousPnvMP#;U4@>mY+-q_g|Kzr0Bw-o~Z5pgqwCc2NV z&BNfhj4byaF9qa{Yc|Y#9mJO4=G@-d<XY}gOri3=?-ZX6_0W=?C%Q|M6OotqDVQ!_ zeEcmn7t8kHR=-ghX7Td8QLC0p=|(oa460f+$wi$46Dk-CEj%D*s?<3Wr|#(~V{2~y zJe_3^*6*Z(%)mqU?KJ)ay`$y7A+L}zqYS~<DYUe)(RGc_3MsOkDAxEtHf{>h!Y6*` z7gZI&xB9QP{r-JV^Rk@gA4%e~V46#t(YjwKeA0L&Z?aTVAibZrQ<3wY^+mV+r({wm zs_Hu6Py~^O?+<8K1_0*n6AMEqJ=IS0v<n*4^A9r)d2_iYq>ol#I_!76UsWUcw29+G zA+QBnS*x(oYIPr*Sha@_L+U8MOpXUmb|*cs!&tZ2?GdM1ug;>)OaCR|ntlaTVL4Hz z@LlMSQ;tg6g<zV=A|FC*b>85Zch^}dsa%$;G+}-Z`3iWG3w;`YAb%~9r&`bha4p=W z)V`jz$xYKWg~L@n+srsjjrX@wtCg8JB@&hu4^L;_B`FVghrV0@y9w?B;l3))YesKs zUp-vfh-$1b;vmO{9(JJ<LJ?R>Gl{R0y}kGGXUEfbfktZt9uK#77@I}`O{D=}aMA1G zO%}mm2yylkvS)YMQQuvr`Lid@BEu*&KGQ=+i$x<4Md6QKAma*3$)~73^I|C5SzP*1 zt<_NpF_Me=xr5$eN3~|uTFV!$)QW8MN$*ItJm>o){t{3vyP=x5o3`atF4L9WnUb^P z)npvP)uq+yrNsr)M-NC0P~mWH55qBRu+(%cb~{-}rNy;X!ACNjy(#DdHiFQg3Vf9U zBZA3kT`1jM)W8*4k%`}0B)83(UHsjf9p#~cUG|*v6<5}y#Q5}sQI)npdfCZe+#$-{ zQL0tbu^*HAn#fv6TKjLc;a!;Rrow5^2}!e*ZkNfQz8ngh>!vnS@IWkarBrD2Etk6Y zihj{TEezu(pH^z_Y8+MD8EZRr5zK%0?ZsU8Ahr}|OBz`o4h1X*I)qeh)u?%`dn$#@ znoTo|Zfg9PqhrSe&$z7#Wxu`-r7dX8UC<GBv3hv}F3esZU)mtzuu5U6R(7sRt|mtT z*+X|lC#+x}v8P~bLBmzdgsn3K=1Ag}D6a87XfF#c$=uIcLUJ#?uuWyINlxqkgc2{V zQ6_q6<{lIth%VgqFKeKsF)sfMf0r&$qmr)%=f`1CnEIj}IV^}ayrM7`t2>1{$T{WC zs2dE%1)sq*^M<uutBn+(Bh-P&fXADXsN~KJ^#J<rtwCTc<@Bma0pX}U6k7UF<QV?f z{j_8#VibbB5^v4fk7|kVt+`<0da_KW3NQn-SvS$>-d^36_?B(m3axa?>u=8$Psp2n zIx^uOaA9Wp+M>U?AYh)4fd4P?=P&7y*cr_eBK4Opq^s>;jMr164FLiy5;kgoPz>X% z<_7s0Zc62e@Afohl8e@rkymJQtkJQnD}ICVH&G>4tf{*&9^hsQ@N0Z3C)GfBjXXgt z{(F+@C*2Xt$*#)Y92ILh=Vc`;fr5za>E)!ontS|q8J03s`4Kbb6KXPGu+*`4xR~i( zs4oQuXkyLBg@7&zfxY~o0@Mj33pPvKmKx_9i!JdfuypILRK*@Xti^V4%tRQ-__;L8 z8^&lPzs`Ov$6?$W#2s~`jyF`_{{uQFf+b_0mrXWv(HTReZEUVHQK&c4NC{CrdwG97 zdeRP7tk}dAaL~msf!J@sS-jmEiNQARj)EQ1(xM3|Gf=;@J8@^$Q-xW_V|Jx8H(j$J zh_nWG>k(1E6ff*&=708n;dyLiqR<P-)Z#gPv%nAf1i?eh3D#LCkw4!;arniQ#ER12 znAk`zFDw>1Ak|2j1pmI>MO{reIgKX7!xR&N6dtO%>Cr}rOBU$0SilK?as<hQ61o2> z2>ZeJEFm^xJ#jRipX83_88+{fb-mr2N7BhblWk8l7F|)%jOvMmK`9im0m&0c=Vh4Z zu3lwf3<P@m8qLF~l{b#ugnk3cx~7+96hAn{8zf-Ze+qcRqSfv`mursWqdyX1Anf(% z2C2FMZKYN_gSn9e%kcY4-~5nBB(EQ2%2tEnzgX}njPprvDU+$=ZFTWyz_&Jf%_r?o zr^sz9FP?fCX$M<>QuCmXQi(pvjPJyJTHhT#FN75FCtsiZWUIJKG<}!qa8QC@e+zvS zYN(Y&0K;aGaEkEBkvsiA>FW*;^A}EkJ&6UWN0`Bi3CHY6eb}PF_HpG#En*QLnQXf( z0KW>|gBi(SfvweTui}gyd3a}t&LIh&>^g>cq=uF0<}V0Q7+F;606PNLk-(ixUvM1p zwdFq!-^iSMkyDkygODtdvS<^%=L*dp@F*nS3QP$aUYX|j-_KT$6XXP(dXRblFpz8` z_G7AQ5s#xv!=;`NaB!!g4Yp73ppYqZ38IQzLz83sg1c$uP3m2c_Q4l^|M<S=UETL% zq-NGv%DkNSQ9V7GH&bzb4@^2cRI=eg_ov7;JzW2wmAH&hu~Rsb+Xz`pA2z^bxVnDO zCb3V`mbi9!OBw(rL+CDVjahc44qJm3@G(b!%t+*jw#|+S{*Y$->?I7Km16yjt_R-C zz>Ne%3`D(RwHoUjd|DUizo-({J8eu#7q~S8X&Ui$NH%@GYKoWmw4aAjf#0fW1OL!> z$+Mx`lH(I|C7(w002ef@ZUTZkY%k(;K0lGZy(%>8*$&ruequz}b%sUg37`Jo_ktH% zP{@t&AbuhIH!P{@8GCkbH&q&6QP=~eQXXaXrNkCo(#Y$)^bh~s6}b?Fg=4d7K5b}u z<(1Mhf$3UEKUpmQvw1KJQ*1mBMS^+Sq<r5org-%8ek$rcGVeLWgGz6&EBm<J_C4Z{ zSdxTi!F(6T7<dn5LE>5Zh_9PBnW2)?UD6$?ANs7CYhdnC-lvq)^G?`dOE6~~GZoG_ zlH6;7!UWJk5)|e_Y11%Z8reP~3w43~$bwKpd-2Sv%S~Fddy5LNsgS9$TgiI1R-eog zE!2=;UsFJrW@@|m3@Cay<I*1G@{rsgW79dOg0fyLL&8zfrLFp-GUj1DiLSRp>?^YK zh@}pO;O_^rsG17!{-pHC?7DfUg0SS;22(eSTh~-Bjg5y%jLQwlL;_x^uJXPtsICm_ z0?1zzu?dEZT}M*at3Mpl#H0RodWri*dN-icGv4VFVS0MYm0V_^B3JnEh%NJ}l93<u zP>!r|T2Nz>@}>b?165i%BsTMcLff^XJ!8yX-+hLourXh?64MKMIS3da{nS8ACvE=| zB;jRw+9iO+fQU~m!_}kw?6|vGYWe-HHX8prk*tAI+|SWs!%OB4k4`Wq(zQ72^t<l2 z0zz;c7S(?H$Z}j<b-B^bumhao)CjZTUzZ1SU$r8(X1?i}+rI9C;Vbktk_4#1!<AP? zXnsy0P=-(a;$6JkC=val1%p63L8s-^uDaOPq+%uP(A+;5g(#)Wnw9L9qBi$`8_l4; zi}(-#vR3rce>pnq8)1JSo<Px&)w|Ou9Y!=j42~us5L8%bd!{HD1rwm3ma79{m@hO9 ziw!XfTcx_U*B$|EpJKzC?n$CDdrjlZYAs+yQGEJrbUt8-D=4#2B)U7n#0+A-KdkB| zjxpF<e%)V-s;iqj#tuNqP>(hRm3i`rm0Q^Pe<&f#l|0ttBh&gNj(Hx9Rm?8omJ)yI zL@v1BWgdx2=74APMR!0?da(owko^J2bDa?)jkkX6=2O6C;b^X|;mmL*PPMVX!|~*+ zS(YE60;@rc?h483z(J{4GxWJJ;pY`bS22_}&WbP#(PDi`gMqlg3SoZrA3|`UBiK&C z`P*Y2RLV}+0pj{S&-UI>bee1<Y3?b0b|fq{*u;plkbBqmKkl#-@9XyG;d+hwUS&M( z5cghxq1NxG2$}s&>_4JtLx+pk4}6pOB-&_9jQK?|qx;SbwwW)VRRixb_O*`XRzPLq zYApj~B)RvzhG^87!lS9kLPo=4?a;U)M)S^bRyeFRaB8HGYH=xCWRRQ8-+do0Wwe7N z0y2#;NCzOz9e(y)pfew;GXR$dxapStJfE(%?X{Pj@3-3zEJ#di-J@xiDLD8Mb@om+ zLL3405xS})zIN#kCh*jRT+?By85P4CW++!+WgHb&Wr4eNOc$hm5d@h_{?Z<cx-eym z$B~1PPN^-gKOdYN1B?dj?k?=?w##QlF3H55$!&wV*Jk!NYl3F?7(jMIH?1$~p8(%o z51%@sqndb~|3^h4ylNe0TSq6Qe7$g%D^M~4ih3E!2z6t|5Ti8GFq&-`T7{m1lP2b{ z%f-m>hwE3!sc*HJYc_Uv*P~sD{Q6?rxb4OtB^C^tdwQanE(~gxhM98_(BIEv<{8n3 zoZ;nuT}AkJwVuKN(jW(?GV;fI!bF>#*U>D5-ITCYh`4TrrD&;4A0-w0+QVVZ@kIy9 zRPMPTp|uYNfiHi!^Y2JS2ZL%)wj{YXW33k1<r|q)v4y!W8~e+WH;q<BrkgHEw*(vM zS)eL58CqqR6=i70uaVP_8w0sVF`2${wJW_OQo%WkdU;B-6g9G(AtbAziGo1}!bcN= z=;Lu+QaKbGuPS;SoXZd`w*!F_`QoWg7pG<#jT?^dY#=mE!(_xe4D#MZR65S{k4|XC zTBz<|-=zO<4i@^20jW~}1Nc*kduim-w5<dz2HXH-U$`X^xn?>gxezp>o9lCx+`<4J z5@y`cc!C(97rXGoJ21Zx?RF@A)KMsaxUHb3=9B-Tm#jJz1p7*Jm6uc3Ym{D)QCKEa z0;zfdB&f~ccueB)=qSALVjkbvdWGYax-+>+;ofTP-+UtU4mPncf)7PquDcquHMbCS zTsql_gNQ;>EGUt;Hv;59N$;Wh>`3HqaQL=uFwg=v(ZJ%y<6W|U*poSUB>KtT?(-Q; zRqF1~54t-`vjdD86nF$5S}MY?5^vmUbnykjg8VmsyR!dMJT<o=6rUK-uvWMRBlODb zt=BN##T{V`X#AmQ+eZfbOkp6ptIa#4#8=!S3$3`~zhmv0d63KX;fezXGjtB+AX8@h zsSC4#Gg)XV_V4H~GlI>2cM@ZQ12kKUH`5p7(l^vM&;~ONZCdk5HlJD2{fYMY%>0^T z?rrTN@}7b`j}G6eylL~lNs6xT`L@%N<%W1-eyXU~0AWwL)$4^cO%yBZ9bq?!*qogN zlWbqk(<Vk?apQZb@UY2hy3qY|o;Rs$d*S_8&m`RrI)RqOy(!^XI?`zGu9)=?flW=! z;mi=s>S(OS*y`0RS7ONzFMRj8nSW38$H<7xe+8_W0zc&o`uHPOehfV{!#KIBtc$4i za!OlZ(f-S1Ru&6W$aiu31OMcCy)AK<tT*_FSkR+ERFBdI#IFhoAlkI6^EsgZ{p4)5 zGrjF?ht3=N-9Z)0$0F_2)duzCF7C%L!#VPR9ozBx>{ND|N}glP=q|K);wap!JbZ@E zcSE7D5#l&l4;ls+my)s0?ccK|%o39^0po{g?m03Yxkq>UHW-Q<=jb5jP+oB8_WW(% zP{UU+Z1Y7d^Xyo#)4fr`XSr8<hrs{=;O|u3RuB*_%HWqy4-f=}PVHsfyK`rsJqQZM z54PlyEF@+KGy_^WGfzmwWfIwKW#_A+Q~-<9;22h+O8|!n)A>818tiRuvX^$jFT=2t zO<=Ffac3wqzZ9B0oJnnD;A)un%?xX@rA|egY7}-XbYZ!`V}7)Dx$Rq^O*?7(r;R;b z=(YvXP=-W8gZ0(Fl-&R757J;PUBcE4noyt;J!4Ics~7x>+_k^f>Nr!{X>^*&gE$~* zsEvSf(aiJ0k_uR8FYmA?<TtM4WJI-<mw=i{d?MIvzVBde?v;HldvGC!Ib}yWT2o(o z4}0RfEkDGZ(J}JdpqfCDYxMHRzH4%{0GfwNg8!FnIPurF>GkwrX4{j%&LBluTdixk zwQZ$a{qn!*%9fTTb=&3@V8WG-bv0=NBs57#NFAi88w3OdiXs_l1hz10!*y~3NeH7L z2w6$W!p3?0`Q-WJ{vZ!{3pErfiuU)|^X;wDA1)T>qp4@-p7rBSVsUV=pV1a~ml;Tc zn9XX(*$G@>(vHJ}j|35P;aH;OJ(R2)J+YSWIFxsL#oIY**JEjg?5<#=u(k8Rq+5xg z4@O{+{U+asDiM8Bw~uhoch$d&R-ByTa#q_&T$LT?0Z}~WbJ)M^m9-0thF`3$yQ!kF z!!@IYq>W>|5=qDxh1-`MpNEhK4qAY*5fqXU-Orp-Ljkjh1PF?TgOHEIrsd|yipod5 zPcg%1{g~QK-zTBf&AGKtKinqL_8Lu&4T?ZTWgdF;VU^r|8&j1g4vmHr2;O8;kL=lv zi~&%H-Q`LDCb!?g9%?5gVk_N*WHd?Ggorw=?im7{kb#9&;9{<LDkeKvM^VWI&v*4# zgfxY~e8l=@Sjj0aN8IY$uc!`U`!$WHcgrhibG)5XM2M#@$+E*|yUvkDVEmz@A<5CD zxs?p9rE4we^zwZPdRi#|j-<i1#JCZ1A7rlDy1cPv_xDT^_ugTp@gcC_lG1p$lJ+&_ zMVwJIFWo8B5MbGz^{cSE@%=yRWczmGfhD2ph3vK9SNze;CMV>#cJgq^AlqNehTGH^ z@mS_UMT~KgJ}JeUkJ!c-5y+spO*DE;Q{)bdz+|=){_&n(a|hV{zhmCSy+@BGkbRLy z2~{bhxm8tJIK^71GtCny-M=JSIJM9G$Gy}Nc@o*!v}H6lE;#(DeQNP+L8;K)pU~O5 zvz8V;y{phKXmMBQWSi#F3t6_dw8}=+SYzLQdeof)lH!878g}(FT0ZZMHk%wyFBGrp zCy~a|ier0g7%o7zVuY+~{SCM_AmmHygz7f#yB_f$pW<c+%<<GImK=$kwSs$khFSaF zbuWy%O5!-sQj;kaHB&<`+87#5R7CQTR7`q`UH3mYK}JFJ4^Olh79fTe{0@qpMpTA> z&V6nMb-lKNW9Y%CA&_M$3mSrcPe3v`M0RY}JOG>g{B@;GMvxlS#7N;NR`0O;t+i~6 zNV=v2hnqo0onh0eBSsb!({mE!NvU{ekXF)dMS{lYv|IT%nWYA5c!zayS@UA%AwjOK zi52mkx*esFt^B0%!Ki=GOViprJYpF(3-g1IYs>Bo>u3H$pXyTB5wMWW>1WI(A88}% z4%<HyNcb@gt>K^RY;Z15rU5Q6-|AVhvTsVP2<<Z&o+m=Tz(l9YN&g4DtE#Bh1ASaQ zH^kv5g8Jvey7S`IZsx<+!0{=0i+kv;BitnE-4)OFgS1o5zu&lpko&phxL6dK3I{-J zIU9=-OqWr}!~b@1;+-eA<d=Y$A%~e(ytV3iB0bn3Is5{ByHyz>F+--m&7m?B&7#dw z6qSda1x7Q^r(ZH=A>yie%^`+BFSOv!LYpu()QHT!P){TGAWseN(*_U;9oB}s7KX?e zQ9-ANP^zdig^Co&B~Ew<cQ=SQ2u=B&I@U9MgD0gJcfo&`YsBHB@g-q;Sxc6s=EU`3 z#Om+WMqN^{^}m<#G^cr85((hMY&nbzsc@g!Z1|HDh=O}u8R-6aZu!XD%@mdIOfFff z6siqyfR7?Fk;JEB+s*-KtJ-DgZoa{Hi}>eLLYsj2&)1WBm^rZBV9CP|NYX`_R7YD% z(@Nm7xN5}(3O_RrFT+Fo`$`g9U%kwpk#7!2-rm?s{ZeVCocmVw>1~=|`U|Tzz#_uA z|EV=AG5&N&Rn>79Ag~@vF4BB;Baw*^W{bl{C|0L-z}Ii(1grG&Xz$%pBJJO?V8*T! zUsmvoJ|3kfZ+&5zNgZMCc`pCBP>e(aFB0EGufqt=G^J9tPEUd!KCV9dCaC$eIqc~N zeF~i7dlK5+Bhx@6QwM=3uvQK;26!dtX4zL*e_tnSt$6_&EV!UiXCskUox<S?A^Rjk zXvCnpC4$SHZ5w>#+nhkvAmSS9QiOv%%25BgN+}}~Q|JNgCxfzKlCEbA4Rc^ZDh}s0 z+d?3luNW+QYkI#I;<3t}(%j1J<8GRt9$knyD>EenjcS(HQgeajGx2aBQkAG7O|Wo= zb1<S$qgZ}REn12%@DN%AWFVjg!>Uz{eXxs9FbO^yf1w7!CM=~U2bwT<47p}1#s9fy zv%r5m9ioAuPxSIv=0-d+BlAH1juAJ=P6PM<0=Wn>UUA1*9+u_~3$G)?T12Cqo2Lou z+1iB{=b%2)|GM%42&&D?qN*1}<1CJ16Xk`q*QNfUMPTm59VEi4c%zVDs(NBU`4h&0 zim`=NMRpxVo~YAyr0JjFYaDc9(O%!W(gBXccYDZr3Y+Od+cAnbR>is1CV0f<=^ga< z(EtVvV1c&+73?WefqJgIJDe>Uww#CF38~SYL){Jb-9f6)&edgS8FMFDU*jFI_DAS9 zU1SpccLz~klP%W-4EhqyEwxkVVK<=0XKmT1xu?<mw^8qGC`Cf50Q&MH|G&@Ir_1%x z3GIORzj-y)0_doexnlLQ`RXNBd7|pHu}A)X;(=Ez$03H{hLLJpn#1a-BAwA+)`53^ z%=fMBQ4G?4>Flv}Y=#E*&;35B&JjiuB~$-*KnOiG81)rU&}dT;L3_{b4>zk6HK9St zpzowA&>?67j@eX>FQ1tcVIbzktz)iwzn*6rOg@+KC))t?uJ4u6)jO17UB7D1M<Ao) z4Hjl#(%XQCEU!O%RWpMw4k=Cp4BuUy@&~id_IGD;r6A+OG_v|r@UKVHAmEDF?Q>3e z4GFDaiXqfw{X~xWnM)XAWd%?9Kada6`+_3g98)Y%5%Ucf=~rGk#G4P@Q-sQ~bDd?z zMD1k#*rkxG$xz-2`-cZ@fBlCcca^`xs#~ih3~`_g1$Q$O>(1x|_mnn1uax&7?rxi3 zUq&qA+}y}=H)8HWq%QHt;we2oErFWc_@5f??$52EUc#1_$=YjKEGavbqvuwW`{RLn zn|z*oCC6mLh#psO0B&i4a5pE@&nAbZc(H#%W6@DX<@@*d5hB0YTKpLAl!q%}(t4wf z^uqB#H>H@D&7&|y$MogPCR3Y0^I&(J2|lYOdM<)}_6;uP(8iX|XPWW2zjzw7hLsy= zP>_isQ>;1{fZy|6$b-$)%Bel=)ia*Zx-IT?KcV04PnhmX!6Xdzu+20;xt{0lK|DFt zj1S5=KH455(GVNfvzO8$TdrzE6SF<SxsE(6GDNL;3UL>hc5^Hi6=3j~l-&FS_V>>0 zBhAu3<oNTooLXm+N{*k}<?xW8vVzrFvb%^eTuczw{Q;IUg)#OF#O`+6^%cv1v;laI zQBovxSNM~OSIODp$fV$-l^}8H1Ge>1T}*1RaiXueEYc6XYkSMVKdZ%)UC&YZZa+;< z!KnQ&5;%fDd-3}fj&Q-GUErv)EenUlUmUL9=8gC`|II!}1Tnm|nlDY2{bOK3N7;DV z&5I^sxtNlespQFhSnw-`zDcH4z`mg2)Xo1cD%uZ4V^sIO7zE2rMI*BcKtek=kP40C zo#^<u33z8<{|$MeFMEvq>MRmK@Havb8P7w11+TMbVg)P-9a$@&PaiW_soe{ca%;Bx z)V;W8+Xxt^EZE7ZJ@0#b6;aAj)vyL^`l%PR)bIjE0Sm-;1=LeUb#5z<O+}9BJCFP^ zrGkj@8#6Gc1{I3Cn%$(rM<(XiT`=lKKv)ImtqEmZr#-LS>fF||(!;0g^)3DNrrixE zm7yDJ$XX8JCs1w1jque{m!?=VjnPc2P_dq6y=j>?Fkj18tV2kbPON^%HJaFFSC2rW za05kX`v^mFgJs11Em<r9W2B?(xb1z)9FSZUk(h32r#<Hl4{>gGG70w81#X_dJIea} z=o_D^GEN}Os2goWOn5l5MVMu?bsdE;<FUL-VvmBPg+=y3E_3Rs#F9%)87@Jhkp@Q+ zPLIQGHh|Kl{xGwWbEaT(U*j|c_bc(|I?KWP+q$F{fjMp>8=3BxUCCV}p#TxH-OK_v zDh7ZI>YCKzaPI5lpVqt;7QQ*J(g7i94tmX7&EFEq4c1uLPZuToL{0ear91}qwl{S; z#C=eOC$kv9`oMIuR>z7$RG>Lzi^l@7pb9v?Giv@sFW9@F849*aBc32cizRE;v^N|c zMYzz`a5LHWf+PIO$(tee*rgG$VkC$Cx)g`mDkGGWp#tqo%_0asjcS|YS41F^y=~0F zi75f6=Ivyv`uSMaIM`N<2-f4~W;f=jsN7=ltt<ZO%RN^rxX^_`#d=P$%bi(IimuFO zqy6Nh;YICCD|YNMxIUgTc>qM9V44~@;M!?(h|$$U>$1=PSPTyTtI0DyWwq#DiE6&t z|4%rsb(Naqodrb_o#k}e_J))6moMRic(#LO8+NcB!{X6lQmyu`?UqRRQHb+sICbyr zFztz~Ql-T3bgo@ig1c`6>WgX&!QDi{%q-xrFP$%SjF5<0AfeW80<`rwnxDJa;V1Rn z=Q=Gj){DB4oYUv~c-npna97rVL~qIx$<+*TQzjz7&fcLbBe5cYIp>lE#0>+~U}ho2 z3%77<FC|H=gjLA8rY%(eod^v4e4~{~><qo?fkuS31_4&&S-Hx_v*<)I5EsAvj}#_D zk@Qmzfv$83%b&W8k3(($h9*o=5j&IRD|k<kdrZum&wL1z6}i~{J4)G-8HQOaf}jIu zvRB}{@x?(*e5^4Zr?tZBTpbo)%M7uRN3)-9_$}N4(ZG(Cp}kJGc<jZLz@<umQ={*$ ze5Rp<glO<y0?m|FT?A5HEP@t!@tmA0Mmp(9n=D+hS#Y);iMDgXX4enIC*CZTp3WWk zZz`snJ-+6i1#a|_w$b{-dNs}z<%kHA`6yva&{^&|<rxHOgCW<~OoEi_iB-U)s<B7o z;^~%ihobwLG@Df}!5B~QuKHI{h)|I-*pLb(O&l!RwFF8>B$Oh!sv*yGGvWP+i*i-h z$UCUFZ5As)kIXcq(Pqbx8^bZ)acB$)4L>Z}D8oQsa-@(+0>q0N|Fi|1CGQF&3^cNl zsgWjx!AX!jR$Q>1H$K=a);Dbd=wEw{jspOTYgPlEJX}^dE?p0-BRo3L=k1<;ZtG)x zVu;h=hD@9(k9i)`a>Ot#g5@7^Gz}3sT!~*ai763Tx>K1Re*d1O!V-ve{1{Whc|=xx zYnO7zWw>-Dq(%t8dFFB0py}FEhGC%ne1B+`-0m&LiS%+KFuw5g@Nj|*SO(l=Q$9vo zlg-LPc~C@DwLVh=%OrClwfY6kB%FS!&?SidU4Lwy*{G@m=6$dsj~bH5v<6i8dPYGS z1{E_+1h=bK*uPE9xuL5Fi~bh)Waxgby8=T;B&Iefgwc=0<n*dvCbu3ckua*#Pv7yM z|9;`tF??*7-RuSiM$eFIVcd0M#JX^9Gl`xc)5h$dLD^aQp<@lAZ&Rz1^x~6wk-pbI zKIs{7f5<l&S?dE@8s%p5#qlGr1$XlS&HVaKZ4hqcPT*Cy{GhmSJJh9Nl|MYZsJdD% zUms=hIIFHUyU(Y;X2YqBPn1N>2YZVUJ)V9TetJw6n3A>By64q8!XFvhGSq$fX1CWj zJ4a^gaYf~5A#;v^@LvG$16@AMrr7Ga?wl=<xLb2E%ab#_G<U@aOWOkY7d4(eu`GF! zmTfh1k!xfyy1#X{d4b@^=f6VA-OQwE3FiQtzC}$4A5H|;hFl4|7h13474+%P(03(L z|L|{?!e_13i~e>(F1Kuq^mx0MV<7M}SQpFn(nYETlEtJ-xq`a5+S^}W@oqZLv*BEX zq4aVJ8U;$`1RJF4ovs1<QrZH@P55J5dF5}~ynskpJK6HLR>kpuS`T5sUdq&7M1-cp zOs0nLDvn<Ujm0&Ls=XDW85=``T|Sf-KGbqYB8S!z>DJw(VI8Kv5FzoA`=I<b`t~Fi z%HB5TGc9+WL`H2ZXNMPX&Be=rUIYL!4V#b#D@8i;X^;gKrz~FE1dmi1)h}$ptrer6 zWP+FvDeLZD*Bk9TC%Uk3o^(PP)J;bBL9%0@25hMn+zg|0EsCbRPM>DxrLOk(o1N}R zx|i5QL-;BOKT|n^j6eT4rQD8d1?O+_HaG&eSry_I<j5vRP}(XBJ>Qu|`PEy3X{;TF zV4bOt#a2Z128BT^bvJ{sUn?V<=U2j9$jC1B;0A!^>qCd^R{+M)NUPiXR<|n^_0B9( zS3&db%+Iiyc&7)3(LvcZ+cF7nr2E(V>%kiP)IuclLn$#uDp@Q(Nd{SCGPE)_l9dN* zWzoLUx515)Tpm74uzhb!2UL;gL#o$o#$g>xSN!Vw)=qbWz%4q}*QJcaV+q^w#3^|} zd>`sd=~;R=PfQ#AV@5=e7-AGMZ{AX0I<+4<6~|(?xX7BCu9x%Y-m_&}G09*8U!)ad z6%Cs@AmB+>*IF?vd)aps>MfpCupwh3i3jv1k6oJ8G;xPfwcKt}EQqs6lr_HSp5C9> ztw*~wwx>eh*R<_ADOW!;U8C8~GX3zSq-ebbM|ZI`A%hP8%sgOKDI~-jO*PfFzNbec z!06I2IX13+Xmyy7LQY^9>!h&yB_lO<&xUzzsT;2ZF6i{4myg=@Kd?lS^hF2TcJtD3 z<StJpprk!0Q)l`xoHmA5J2I<K6un#fq-{Nj!@|d@^`IN*RCH~JI>_)B_3&*JsVz3q zi(HBdY$?22b=0hnbhUkAS?7R}6Xh&7xU_U+N@2WZD4j`>UoobydZ{x#tI~`(MlFe} z`R5jka-`L~oMAX3VIH)&l%q)<YQ-qx$n`}(t0#5Twlc)6KPekc*Wh1(b15y!Ru6Fw zzI%E1P}%&!Q)4mOGP5yXSiT*9)#<zM_}|GOC-I#XQv?6_O#@7lG<P@Lw;tCd5|kO6 z1fk^3XTMe&Q3%aMuyoo{yVTJ&SKGv((l(H;0`I~0A}kWQm!#d96vuc;Z*Mk)H_?c* zq0c~YSjx*y;@bB#Fr^~AI^7owHwT~_<SMQGRr2OATbzkq^3n~cBIJ#p0PUX$e)y3q zh&%*A@;N4xg47g1MAz@=T8Wl2{=r^3*2yArCaxZ)+kbNsvsm|R)F^1*O_V2Uzu{l} z*)+#kVr_=2{z2N`K}TfXm8CSh80WX&PCC{`tKIPe;iGSK-UxxdG8J783L3<+$Bw7a z|1pxDIr<velfUwh5DT$G87U$PW1g3Wj3pM$hgt~4T)1GOQ1lr;l!FAPBtrX}I@gK# zPAJ03oF0AB!T~y|n{ZIVKeyJxJt_g5Sv(ThDOkzuf*@+*M<xCr2ALBq&RdLcyWV>j z+qa+j-)B1uc1It3SO|6TH*dRpy|MGJ<0*_>V4pBMx;oLpEQkltF!JeBPcQOYaC*>P z9(MYu$8hdIPzvp`!%wfvNXkY|sCS+2rUUflrq6Mv|Ni-ut!(8?=<i(rk7kbc`vK2z zEhN?H67ul}?CB9%nzZKf*5_2&RGl&-Iiu;zb@h*hGN&v&`dpM^Z8QcCXwUZy){lz1 zUdO`IoO{Z|Zv0={f6VBJw^mxOV-`~3L(>e$yrmZp2764e>h13BW|OMM1y|p;M_i_V zFWc2EawVMZjEWi*YoM^6rT>mdh^3m4Qhn^bM`zDvCQCY(OyYrp-CJwH4ck5v*@s@h zLbKX&n}lMBtyxbl)yoStaU%M20GuOqhEOuUOb^C%{fczI0lPCwfyS@>C_~;omHmz6 zYk35&ai356XhZ!V)RG&Hts!>5QGN#Cy=d{B%)*x}aLrg%adv4fi+KY*c%%Ya&lhk| zK2Q`av&zp0ojLTm%CS4U+<|C`o}G-LfpY}-^JXhrCXP086r;kmq?uy}A;v)gRt_OT zloG%9iFP;ve5!9)hco*Ni~rhNFWenMV7jPBlT&MljO<$R+xDw9cU6-P8Uay7e*VU} zMUStNr-F~uGl09^h>8;^mcIH{lM47{=${bNdW}ZbIWP-yQp=Y23b&b<xn=KHT|LiN z!K(CFJ{TiKf&W}6@1SBR?ix%w=t>tmoeY5_+Zm>k@sk@a`n>oivkT|amz|h_m`RT; zWut^%$EFp<`gc`5qJG0!5wuv^PbS7;?eG#5Ng$1`U%=cU%SIk)p11#)_9Wm`KMcXb zTw5M$(Y5yBQDdQ3{dAp4=m*#{^f6=$k4T)cf32NMn=wwuB}_-AEVyF&2+;;-6c4Ij z9GOJwRH448GnB&6ZCxH~B^37hNThYt)~@^vIK&>3LHRv)F24&_;GjUrUW_wPVzAok z<z2V>tnlFV3$^K|2=^Wp+n*^GsgGHw^Go1*eCNkL7&bJqI^UIwEwh_XO0`Z-*`ek{ z9kYvTZA>j_F8mGL;B&)_$NP*7icc%Z?N<Rwd9hcAH~&;fza#td^?IR`e|`;MgXM4N z-zOe3=h-~{HvQ`biWuIxTJ8$n?{w>{Y4pR3B8u8c+)4#iN^4g#>CK3}=n6dbrN_)< zR6$k$gh%M-AY+gp5R-M>SP%9e7Npltq$i%48;#iYUdF0!=WU8(@%m_C^Wvx7*zCie zppl2z)ElX01hC#$Tb<6W?jhDtVp|}$HO`r}_f{ZkSF*ctZw*6|*-@_Yqm|FufUHjM zszGUq<DTg06aG0E2Fg%dtCL1rLhr{}C6AbV=bM=BZi6H1ew1bA%qG#;!we!y)pn&_ z+foxVnS<hR*uQ`QJU%|YVXOVCAg>*jPu()gh6Y#UU2!fOU6j%cT>3&8?RM9acBM0_ ze%gRhZ6mrEG%h7kZ>M_6&mNYBm1H1CYg2BEs~<}#t@D)I+=L_hnWZ4)cF_DEnF0ty z@o!`U@bHh>uz6ZcPPb$$C1_1Gn_ahZFesd$YoKzcz(X}VPJ8fy*&Q8Us(dOu<ikYE z@9|}VwTJk9xr7uX+7(U1ZN#XiAiqRyx0)kKwR>ALG%IwlHF_C^ZQ#)SpjvNJ0V7Ds z!!FiCGn5nGE<JW$qH;EB`()2JeIr$R+rUvcsMUOe==)t&={)zImK{pw=cmx6aXM?S zZgY9n%MvFHbQP5Tp?dtq#g!Nb519|FOMghk_H4~7gqS}1pE35-h#>M1(~js;k>{(; z#zQU9>KqzWtJ1Ksr$fmqq$?%nL~^LDKhxeQ4TSC#=_2=}=*~vxh*)Le+aNxFN?gdP z^<&ZxCF`Jq_MvUkB|q;dCxy1EEek|%>2g4IJbsc6{!4Z;5Axwv`tmCYTld*UnO_0i z8wyp+RCyT?wzRnCCbfqMN-*>teZ`*alAa86ih+i3x%G^Pu@`Osm>{~txCdwpLVFKc z&zi6^6iRI=)O%+EtDODy=Vl#L3SM@MTPo%kA5U>U7tkCKXM+OW3h(`A^Av5zL(RhP zwxz-r+8E*3dy-<b`<HS>$SY4=I>hFccNiQ78~LKoLE_v4>@r}l!<jb=An0z6JPZ~P zH+EBq*`brJWn0IWD474yQ`#?JEORwSVc1R@t0j+p2+4{PCqybK7xF+f7&%dTqnqO; zv=fUqajbO6oyi-<#)tXS55>vuuzf0SnM4op`jz_$GrC(YtTICwv92V#Ud;!sB?>2~ zTElUHw{Xw|m?`G7Dfzq{`D<}!X$pO%l0ybAAe2k1n6KPOc<GL)qu=x=a6D8zQXr~~ zFV&DgrgK?Yi_VRWs9IPG^C`p&?dI#E*Q(Y28jK>zv@mISSdYTJ|NGuM3|O)^U$T== zY!9tiDLQjAr#hNzizaI)7Hr_wpGh@rc~YguN;v1|@%@%}((HInQZqkX{lKSF^-ZhB zeR;hlUAlW%Zr{x=eO=g=RrSj*Ca@SY1(M^#Jh81yss+a79asRn%1T@p)UPBTy{^!> z;$p?g1=WM}%11b1NP38ILvE<VHsxxaKUT0x*qX}E9<%9xX&zZbO#i6uO~zE)X=kbK zBln$MUA1_~z>$U!3qB}CVKZg;cLi~Js>#EnyaSpG-!Hy2tU!hQ0`xgf&hkt|G_~qE zoUJ$?lsZh%Yftd|U7=A3dwvt(DvCH+b=d_BT3zFW7Us!OEng2!mW04*9Elirn3*ZI z?H2$CdBD2jHsXd|$M8tV;7Y*5z9+#;``!M9m!tDaT%$Cm(I)fbUlEYeOew&K>M~Mq znA9Nxw(%bZxR+ZjmCEpbl?RRD4ufCeBKuN%=l^un-5j|AzmBza!7Jr*G&2@*SrUvR z-ol;PL}IrsuWmd3{Tc8kAFc#~yl<}QAj)Di94H~?(YefN&+2ynBO7qkQKm}5mo598 zNYd+fs<Rg>THnb${YJKTg{<dnCrhvMuCYX(r|H&Cj$`)q#1$ZT2QEtuk~=C6|297Y zl9b-gIrxaXP$f+MCt17w3IJUrC4P)@F!3+l!3a3b1VcAYq8C_wsrh3nJ6@j;)1}zs z{c}byK>_v~Gts&o0?)aj6YhAU$z`G&;{cG|HsY@q9}llT5FQOciN?@`Ri{6*)B6Zu zqB3yM<Ki(F(v?UFo`@P?&K*j%NhZM(aY{JqS`vYTd0I`#6*raX*<c6anOEqf$O1^y zX#a(jD~?e!O{9ZJQ%mEe5q0E|h^y#9Tmkw5yx=&~>^5OYI43?>CCsmw?Rmg$XEk)1 z_oxyK^?<Xa=*_{Q@NhrMs_uENM;Rb9>GS4MA)2(oHZyUNdA-FUxY_e{gkpYp(DYrE zJ-qgQ_g+389Km2BT|rig^lIZ1!EHTl#OHdhzkp6#ZsPl~m8a9lBLRnJkF-Hz!b-<i zuAQWEto8Q=&Xc340`RI?LR#Ksr}yR?Rd?-lFk8%<+Nx#92o!6ZxYbRb*?}$5Kv?OQ zOL0$c?YANQnO{bzrnrr+ZBhE}TVv$w2A7Y`uUY1XsKouD<%OnJ<I69N80T1`C|YL7 z<6z`&zdgFvjJS0XSn@RUaz>gFSz~LEVz%lqA*_S*@M|{z&lN+C9EzE=wg4AoAXzFN z>d;&nyP<%KJ`0oZyHEuf6YdW^-+ii|H)5^Gsc3^KwZEVx07tR=TuH+(%;%;I#8Dh{ z`8`zvm7}1u*_MM)Al>(46C*hJ*_XyKU9}Q$<|Ai-j5n%z;nEN<!KN!)ecVykUk!m+ z)><*t74T1#fL^bX#DTip{cZ*grbKca2~}XJj96XR1I7bMga#LL-T>9IJEHv!VQ*2+ z4udXQ>A`6XyO|skzPd7iJ&ydHQA{{IY?27X*Eh>a6*T=k4EJm|n!XgKM9G?e%66%F zYqkWre}sUm`POFYNbWaVm;0MvnA-0|tMPJmiHWhqR(vbHWc6NpeJcrl;LV~XxMlDB zHz2BCNold~<3g72B?HG}2@GYsx3S1R+m0lC(C2_dBFM!Hy0`P#uj~P$^EJ52D*$b_ zxc_`l)Y{k+O=J!GHs~27oX#0vso^>Zion@A?rHiV%tJ;Gy0CVMsBJOE+^Bt8;CHn= zP0p>4k>zF?pR2;G43lyz^k#1(oodQd{9b#=gx+mcIm+C`>b&Sys3qw$^tcgTQs7Fl zq`s{6Zw@0^PbHJ)XU|xK+J@tIUJLe&Mn315{jMLAm=tWOmFs@gRT&w<tEk72qV$*q zPe$FDWhCg<JUl$^M`WF|XSPxq&ZT!CN(X|f^rUK~lrak6_(Rr@cIZnlzw<|teObe( zTD%y9pd+XiI+m@@X=Y`bYssxAt8Q(K=?G?pL4LjRD!gmlu~eOZkhaDt2i~$6C^f(8 zcl2^eRh8+a&BK03%yeYWEL6+R_hQpEk<|iqN?PEKAN<;FL;vPk@3aPOT)zjDK96NT z9~)Pdmyq;*4?bi)z8fhtu(hfPq4f(_(d=Zyx5f@v2;^fZxtw~30bimI7isg_^-712 zLpY>jgJnSdR}yKNizGn=&b+LNFD^VUHgGJFBbq@6NzpR43wLuNIUT0L)=GzPBE83{ zggzs>JY})e!n#H|^E5%8C{t!(n&N6QgjLRQ#Hy!k9JyDy4^ck|#r>UGUwF(`S@6o5 zW+%G>&#Bu(+x+VPZC|Sq=;hW0>Y=X5XX;CFfIJ39IJF5Wmt!`y((zMbH`iysl!m?e zp2tCpCz%wAbG~9Rj40X)AuKV$#q>@)uP}n(rXl#4mZu}b;rx9KVSQH9;jZZB0j@yp z-gq}yY&nCE*!cPg|8uhbZr6I2WT=e_-0kPM^G_s6uBoHk>sbz;C4*5t_j{zHyvum4 zo9Wh>%}hxg&oGQPv01)y9o(OF|Nl8($-WA4FRZ^qLsiS8$U!G}d!WYAweFxo<gREG zwJJ+DC5$(b04v?q2z*#8e-L0ARBh&}1%q8pa301(vRNma5<7tSaLHIS^lT9ZOSY0~ zH@$@jvx|Q7Oi_-hfC}*UIpcq5GK{Po-QwkaC3<^zZbx8nC;oNm>E9F6$VN8KS#wMU zkp8_qEsX5Cas-Zd9akUCbZAp-kG1zJaDl~&{*<=83)gNfR%8C&;_d9}30%uS3G&v5 zRDA5_)$%nZIf9PKg1U8@D*?gJ@S=BvJz-ZR-!6-&P}>`JCfp`S$tdG#*n;UMU6xqn zesI_k{~+>;OJniP&x_y&gDzfWXOhmleWe6Cd(gV|07q1E6xbXqRkA=L_~dD4TMINY zmc5{_OmoiP_y#^1z5#@cp;Aa^0vC|v$Hhu=!~L(^s}Bu+F)GN&-Csi{y*Djk9ZVDD zaQJ$99mwK>%(_I(^CY+b7l8wX#m64icQgW=gJ&JwVS;chy9se*nNQE0&kXy+CMJ>? z(w)(01Y*s4@=SZn8-4Srh%q*JT?=uD*$hh1FP~@RnE)O8Yn<1V)e%0ew0Sx-=Bg;n zw!PPtGoF^?GTop<0GWrvcUNw@3$bDgzdp0z;q43Pw{#lkekgI-1Rt0enGkPO<EK^T zR3hnCrXAn&pC5R=Jub=WOO$i&A5$mFty-nUXG|IV@@ZYdXmb;z*q*nt=3t{XSJA-i z00pMipsrK|{}?1p5QSwdPo$_Bc^Ny|m=K^DpdJP8Bx#wB>z@>;?hb<=o!GNjS^+-0 zw(lkiFTflp3NjJaq3i7uz^x8Q$U*U=1lEmd_&Fk;c6~y3l|*YF&s$-Nk42|&*c7{8 zovtN;wukl`reZQv?unHU!8Xl649iBb{y&<|fx8Z9+rnXE+l}4Wwr$(CZQE+>q_Iw% z#<p$SN#D75-1isu*jRh5Z_b%sh_{G&XeqT&VmMj-xe;)!df`v-Y6rR5`m|qPkdQhE zOPivQ+#$Yf$4}lu%lF^=SzoyFy4p8y?sfF|bK{|<FXO|%O#gS=E|UvPjdtp$D<e_$ z_)ZQKxWL%E>IKGo3YD!yGQOvJ-8duJJtL=jn_Eq+21BfJst&Vn&rA|H{tm)OhsVwJ zOPIExVl3PN@6khYyBsa*1*T{PNu~$|HbeALXBf4Tpc5JGa73KB{lhRmbfQ?_gm)Bj zt?-MKzl_$^6wyPf%pm6=vavwTRqwR=G~B^8&OsFk2N>L=2G`!N<w<#~)-WkjB5M!g z=ws0;WymQc2aIin^cgwJJ361>+F*;q_opFTbYG!BL%(28Un+|un4o{9*{L*sNi{7k z5C0~=a3&meB!c^1ue6rnn2`IThQmfVOa&*G<Bi14g~QWeZsUCq_l(<^-8QlzTp{)p zAkQfqG2IBZ@K|<n?ijzb^WhL|`Ol8d)m_W)jP*m{l)lA-=ktbrK7AfLmVW@4p!{t# zX1tzigJP=I{WqNVR<KN7NuM9^d!ZFd1f%G>|G&RA%$$?{cl?EK$S1)~pjSJA<mO^* zoi`s+7C)gr8+YXoKP2swp4fo`v|7E4@<>CX5yRq#HvpXg%~Qi(p#0?V;qx~ZEcKFn zY|Tbqt?wr5E1d}9t+2&T)rtw^LWtJ{>bZiNKw4WzQ=12W8dPwa4_tUtQ+^C;Y%vYN zD88T)2$%)%w*yHnh$>I28BvA*tFh@%T46UEefEjupUR!OG1K*|Uyz=dzZ-^K`yo=7 zLQJq($<&Ceo7UBbwLL@wud+Z4_y`TQ*1jTb$m7B$3zq^7nX>L6b|>bto(mj&^UHpW zwEt8*#;?jI0%zdU>LStadQSBh{)zY6kRcw8l~)5kQl%%HN{g08sB!KSUQ2v_amD9e z=$^~rBOU6D8}_m?_@2hr{(2XL^-$WLJ?N#dOsL^-`F<>&LR)IioCFuF_4BtkliU>{ zD1%f8)e4^JpCdyFstIU+YPgFN9F<LgL4fK{t8G<J8<{H45{KH85#Og+Xg9UIx&NXi z7NA~MlVu7`HeLzb*XhGBfxU)0J$ugtaPP358ge{qkJk8Qzb%h$FBwswQ+tR6OxH__ zx_xH2wn_{^Z>zG=hPTwnyd`<E{-u@u=uci9aX)w~%g^gTzC8i~I!YVga8<MAbQK&M z`PD6UCKuwUI)&MDel<}gpGYflB5NX=hMAlKn=H|+bE(i{*4a<+)f6TtGb^%_>p4GD zA#FwUlct-gg$~8{+vAw-#htY+p1MSo@E(@%50vo|SF#4cgqD8(My-*=eWdlE8DSFr zcKV@9X5~|~aa%Gd5p9ixZn-i9O)&QrZjcrd&=MlB!Y9i|4dxDvbPv1tA60Rf3|$IG z)ZzW%<4hd!6Owb7%XzOyCRe!**!P4Qp$Xzx;6m4SyN{BSuV``6sWZ0<HfM%(E~e7w zBmSl#N=NAaoW1Z*tiisSVb7d8ElA(F=D?LVUG5EdK5k8Vk47;$!NR#eLpe9rs{a2S zUv>{aVT|!Xc=gmuEce6LYvVpqU&f7SVayk~Oz+*ob|s_khv!}LYiILFXXW$bGa#i@ z3%`1(6O_;lWTq+)#~H%w@DI@Sm8JN6o0FQJ#T8*Qc~2pRGkhU*`v>Q=Ke?71T|iYn zXN9#ME_s$@5E6zK&PdWEgeezvRkmQ5`f!4I?n<#_Gqe1+e)3aIt&hjeYa`E$48pYO z^DA;rB6`bN&y*53WMkWjph>5pZ~ZC%=?(bO`^LZSR67XHi|GgatT`^tcCzFM3!wz0 zQxBYce6U&t#iQHo{6}*|sRI(7nBScCIt{HMg*THao01sYziI8~+rx!wVZ$>}Tkg6i zfo6YCT*`F$IiwvO+=Svr9y;y_eGL?0(!E^O*lQ(wo)kTWv_$N4Zp$|F{Yd0+`+q*I zB`vC~E^r06$FrB)rn5H1vo|)kw)}Q$*2zU1Xm!m0ONBK3^tm?_L`A3?*A+eB3av7K zDi`MShqcxW0$H08Yw_d57R~PT5tvd1%f)_GeNVeURzaFD9BzW|06Xj=;e#FT)A&Q9 z?%pnV6?||x7T!s8Wd?#H>p(UdRPcH$HZ^ks6g`5UL|^9!)DOcRj||Wn7b>fLEF78| zZw1htn=7-yRTeoXrJ1hlan0i-Aw$<Vmr+ql+)FrBS;4=?LHvbcH`Uzga2e^UY7(4> zcbsHv6?^%U_?5=JJ5lxr-lR?Y-Mj1M2uNlP54`7Xc@)G{Hvq4B`PpOSp`El}S51q< zU<9y0aWoi;H?=sh@BfTMu~U>`e$K53h;Lv1!(51oZ|<91xcIgFHD|TNDWL9fv*ToP zao}04{6wuA8#%oC)*G}o+u}yAun2Z@LF!*O8!ZmRnuziLiz(+rzHt(O^$(;uCA15k zjNI#1ur)=cQ#)qNd7gg1<(ZmAUG^j=RLs_LRf2#fXC<BA1<}>`)H@ud{=|&#h%six zQ?Q9fx8vZRbSYNjVNRu3Fnj{Wk^ljed|`B1l8HbLqQ6ew|J=?JwU*t>H&-80*V{EW z_<3NnMUxIrt*fDkxXbfHNN(<=k*gfxiR9$m5`pmriMD_Gk1_hMK2AAkn#-_AAq?`; zH_G&UdS*mk1Z44YpyD(mh#2a2)@H0LShBImmqGCcmYeoH5k>1x-iUD$=IC_|wFWmi zyHx@m1Cr!_-@HQgMoOAb7y~POn*KYWt+XcOCc}x%$=^Qig@AwI)e@<FUC}%)p_(+> z9tt-Gam*a1mhL49ylueM`4QAOCfd_oHTs{)qp1wVp=9c?GhtNY)l|k*jB+<BZNqC` znL6prh-IL|()uLS&*Z;iJza_dOro3-WqG)TPNj>o2~~d7u`<W42=K<A2LW&<*6!xQ zm_9kzQyNAkFWYKA6&a(H`J&%JNm==kc*2)$8xRJzrO`VhsDrQ;s8zX`d|6#`y2ss8 z2FNDt4CfVpUUf~gtM;`4r13~;&h2n?$>zHYe!4=N`lOQnXG^_%C+N-lrG>2l68kSD zD}H!h7@a;@0kTwuhMvOrDhXa#Ht@f)0zv#zVMdrA72f70+|^yp2H}|xQAym&q5GVU zjcF8qJN)~lz{ex5LuL1Y*E90f5j_Q2W~(H#Dw3-WY_xAsJL8%h%83MOKtiWpPdohY z>j2%yvRS-%l<<tkkb=D}U9E#b+WML|%yNPCTU-dlCb1lauU*op#`d#!m)6Z;7>0%X zig_^PUaD^T5LL08OYJX^3kxR`x`-aq-qngCsh2TaqT)A%z{<ib_2(lDcFk+W?|m<E zB!Lvsw`g?cjuW7nO`uhti8`X6Lg!Kmjj3c$WZda`dB6~{nvw&X$egrkJ))sdnW>X~ z^Gp`C|Lkqhq0ige+BsUm@A51-9g7>yR%ppLY7sDKWfVqNw%5CI7(=g>Q~bTW_OMs< zl>TmtATaYG^BCCobzMWDWxK!r-1#rLC9CG3G_Jyqhbgd^3vWHO5QHCKCF&m5!*?3( zFI>TF91B+pbtqVOdBq~$Yb>`~Y@1%!e<?OcT5ka*3fdR-a=5bg-)<P@5PdWom%5AI z1N1#1jWV75>~xu?yoUM3dPd%CESxD1T0tKfM8&JBU#`+%FmjCbLx^h{nttpP%e@&l zq!WyWo4L)_Rc-Xz>YH}aW>yVsuf9X^v=-rCmtrc#3m|~w7>5t(;DQ=lBbVBttVRh* ze=rE)V!LbC<8iZ{)R^vU#}l-c-Pbr4K81e9P8OLL;t3i33|yngxpsOz6laX$#UFwE z<VD_Y05>$CEh{L{qe#4%zCLg`uy-m#FY^Ab|8przA#pJj@%u&?JCSj_>5TP?Jgt_3 zW@{16SM5R^6)e})JkwVMABo^R=2Lrj3cM#x2^GAj*&h{>`>*vYaBIP#4r2W!_|AZ8 zo7GNIhWn(Ihz9`qN5vkyn@mHjE?!c0hNvUd%4szIqB3R8^_BVa6J<JyF!<yAD9?D6 z#K1|fU{k`xF|hATyDVH3TO8?qt<hjA_<P1%a$yI`F|C?%SS@1!1i7~c>}P+0pS)et z*4h5q9<ud=d<l(#0&du*c}=eZ-43<lA6kP8VN9-r8Ub|U%g;{5`i?h)nQA$y)-^y@ zW}DU}avw#!Q#gjTlJ|lQRN_k5P&U4iA8EhCn+d*@ckq%W3skDt4Hx#SjNa(Ihf8y7 z{?ZBuaWs@LMqk_e6SfkUX}7!D^5UHp#pAlvSqbvmqT^I6{?<Dy{KbDvi>Mbt6RN0% zYUr}|1c@(&l>+jLKk<4bQaV0jlBY`#ttST!uh}0d#ay?0nz~9%eBMVjNN>#oZ@HlY z;t9Nf;{*#Kb1|p#p+#iEE8Sc9GD~sYJh*tPJDxDPt!to4C(LUW&GlNxCzGF8|GPiT zTe`=VYU(W9A{c~!Src>wu4g-QY^mE>_`DxmY8SQZp=vn_;xA9s_;UQO4F%wX$?&^U z=`atu%-4(68^h?09;^9hA`$?nuK;htzP0M>+I8oxwE3v<{5aj!Qb5n)KrQ%}4Z#-| zSEALWx{QOzORvjfi48dW4Pgf3vnd`GpC68QgrL1ExpCL}NK>K*TIqsF=|SafxC(M< zBmanDcLPegA!d<a`FlB7%U^2g`7Byj0$vdd=JLBZ(>L5~WMp)gVrwk1G4Q&@HjmAj z%YDlY{_(jWlSwPG9*qurxf)Ale6YyN-aQ{y_s#Fadm?aA3q6l(cefp}e@+PO*Y&Ro z_#zzp811AIpu#qR3=?&CK$(bzP&9-bQLfOY2+<r3A=YoK<i}q<YM5%|^K*OxxM1z) z4seUD2BWs@y#+rv(=>Qw6MO<km}?jhpN#bsn^kCW%Vy8jIKCfBkkW=1zf@y6Q3~ZM z&nGEOS7!D(=wcQE4`iW|H_2VmQ|Y-Q%G-aydnxJOp5#@u#sfTVHt(ox;xU@*WRYh` zm~8>$43L@DdO7UjPXOdtTR9EIthleyoI~{C2J$wX%Nd9uTU>B2k`Oq)%pptdjGq$y zP9##_zOnlU@R4=c@o*%~x^1k8KYBLN_u~2$r-cwPmU?g*h=>%~_#-*!fqW1cXt1qI z^keED$ZP+@VUR4YcsjA9mxUVKr2b|OnO4>%MnBa`(FoD)Uk~ea5U(bXqW9@LaHg3o zRiaz(>Sg{XDvvjrLi_Ea=a>oN+rfIk<txu{t^3Y%&4b%Za^0s(_XmpY>5`A8DJsSx zL2Wsbi!{F=h{b9clsiAG0o+Yiqo?%HuV`M~SuulR48x<85!B3{3cLJML2&~5iZigs zfrdx(&_=d@(aJ+;vgN8#4R^K9kgGsK=Y+QB*AYhqzt1%bP5$ieLSC@jVnVH@(rx9| zuKUR64YpXF2%=?eM`D@m`f~<cs>6kW))&Fd5um&)x_a;Az<^2ML9_c!JjFxk)2uLp zX;8umBkEggM%O_A{c%I<a}(>MF)VJgnZ@i*Ns%ZBx;Gw_^jU7J<WcxfuSGd>!0XR3 zMS+|vk!O=$FoX-W@UGgESAP6t&)nM9*Xsu?UF~3|e1bYE?H@kzteU5gBAthVPNRsp z{lG3nu!h(><|lgPu`g5hT@BFBEK>a-sF{o5h(QUsRQEUw;FSm0NUp(5t9$P(Y$rAq z7qOVP7XrY9N`Rdh^~wAZ>D$IA*6_TH=Jy>jW5rG;B349PIK-CeM&$KX^`{juf{pkM zFrz$-9P5N?xv;-k!AiuqoApdS_sTx@v_cc%OJ+`d{`Yy)a`;Vm@$)P;QK`Z~Sc0nZ zYYLL#wN#Sjmn5>%nyK3)Nsy69<KZZz9!)uQgw4Wm$nbn^(cKnwF!L5R=3voiS26|n z7!!QSmVY|MH`Smwe7ESz9xBOr`X=_B>A-Lu5az2d@9T>=K?aj6TLvkpVxw*`C{W@_ z(S&-ycndE3^>AgwX2&o@rRcepOtO<mG1um1Kb1<4c%lGZ4+(L1$yiv-;6tINTxbWS zlM+t%>-yrMcQXM1IzW%({MZW{ayF5?!!MowiLgV8=>lVl5NI1!B)6qt5;m^OY~hz| zIb;&fh+7`If*uY3zhy0&#TA%doQ#<(X(TGKcX!>|c6r0(be`I$j=0OJc40-d!u`W` z62%`-4|f}_S*bCWcs&cL#2eV_@bD`^Wx8Zs=`&9{HL-3<U|YG+z`KJRJgQPaRkw{> z96nf$p_HVcYz!&w_vtcEBg|ZDjzN4xzY_)L^m~~i2hFI=9(L|6udh`%k63D-T?6;= zGyd9y)4j?*o@#i$jm{>f;;bB;4>!jC;y-rx>M%GwB@^B%Se(o+gHyH}b8QEe7G|kt zR`K_PxA$@~k{Gp|XH)7PhQ-c(Em}0g3`RC+bs*>_)cpRG+L`c>t<OKF5VRjxS8zV2 zx>R-<PHlvIn^6-RK_yG}%Ds<D2R2ft)xEs#3A0?xrT8fiE3RK?ckx;G`BG3o)_TvH zz{UT^y6>3H<K^>xhp+~ur`~}%OTeb2JNkV`bubsLlSZ#g?*NEN&IHejjdqRAMvHDO zMN~1)Dm;}(@AMTiPna|3e@hD?j4F0|G3B2=2+*?z8}egw<2^&-&KUI_KOK>&xBb;1 zo{+Byq<lGvL6FO9<0y;DRNM#xYatDCyA6IWhwJRN_>Cat+=hH7Dm?`O`zRmHtg-c- zsyQZzETJ1vgr(oeDa=VT8Z%IYCe_R#aV5N4VA`V1fm*n%(4<y?Vf~D;H0sK}M(uO2 z^-u(nLhWoNv$9&7&iV^LA&<XqkQFn2QeJ9<R*O{y!aG5(>iB5<Rj~=~&4nI?yl|zV zT+$cQK%sspoCLaKz56#`6cv#7J5vfnjtq`6C2W_|M+yueV34=ya3SW1(3ikQsyZoB zllnftbVX#o6eJjhhx1T6!s7O?=Y9k7XsQ>=%RnYb?_*6Dj?C_E6Hot5(-@=km{M_# zl#u1pyV$29b>%hZe`{(xsoIVjT8(DZ-tw@g;O61~RU()!R+<fyg_O4f!s~z;_9R*m zKvIqq3LP%A{i0He2+m$FAkZl=<eq2-&#;uI(d?gGn@?Ek#Zv3<u8Q#6QYg_GOs$0m zBa2Y^>m0_&(P}X1p3-ELNF4u1?zVnz{k@GuDXfWGHZ(*`4}Y;fI<Zs(@ut_!^p=T6 zPA^YAm$vT4f{BXKgOBT+E35^qSU-Wx6^%$NQ8{(6%mq|phARP=WZO07ExiPO$SY+> zt%=gSF=JN#MChOANYCwIStWife}=fW#9<wvr)&HDAONbtP`AV03EM-gvF9m$4H$jt z-T?|B4GPo5=TRENo^LaQ)doD-2UpiTI<OAi*wlhI#8tvr`fG<`BZ(_fud1VX`b%KE zH1~z_|NF{vY1TlAh7}te6&IX$gO|4He$kUM3^7Nj;s~bNT0y)-@&7~juNJA8<+2}b zVh;Ur@{{;KR6^Oi5N37R6$hQg6DQv5pNye#gP4@5vfF1YVY`6G?5-{e4`C4Nq92Wd zfgqpGTQBcZN}zDMQPW93*AlB-5m7?bG|QcZwYzxh*$L=vIGv>|4&1@;&E*Om0|n47 z3lsLNf!{bW*xs3tABiuDNfe1j1PW`CD?@*>q17J*bY!D{hF_H*0>c0dFs*No)_V%Q zv}Zp)dnyniXpbN3Z!;=#+wSevR}$nw2&}Kj)LHFhGoYAs;1kFhn>{waPmM<ee@E!T z_j+hU0{&pZM#>7}Z~@%~N(wN`EU(TRY<U0E^G^JGhbpKJMl}5Mkk9ieJ;b0?OwB5M z5>KUcL4H(YKEGx<Sa%Kr+ILrte-U)-a`<EPLL;xw>vzS3TX@=BQR&=$ni3dNx{)jf z=kFIIoPmldpXT`k=>-*iI2-aA+b<H0qf-OF3dI9ibchV#0mK@CNiU&}nl#^3^D{`0 zTUR!}^i86G@Sn`%ik{0Wpg%*ER>Rv2-HXQ}@F&(jT$&+_^2%W;^|?2y&M;a0cPy?x z9Df(nL}R7v>6Z)b*$ArDPNl|VcS`2?$d9CJsfn*4qhV5WaDiHS-S6w7aI>%j-qd1a zq4Z#kdx=@)n6T_U3-wZC%a+A~**pVpUih1CzBM4*0B(Em8f`-bvQZoWEikP}A6tLZ z`S5Rz?LR6=y3uS`bT5p)0+v%1J(MY;DpfR<4y*f<A~T))0=hNkKk!cQsNR?t5xD7f ztlWD*^9&&K(D0BQu|}Gr`A((2u)|24Id!p<0Pz2mv%*xYDZY5_(t3YO+9%6T^Csdo z-S(;*<`M;Sw8g@weXN>h%T#FTKXxN^$pz-5=PkVIwYB_Umzm||kVc5ts4J36&|Tyw z`s<c(n17gKI;sL>`?Ok=P}>+iMQGC3HdaiwlL|K%c9=lZ9Go%oHE?URk7~kqD8&|i z6YH?Qg-zlnfLa@Y{A$quD8s;gX`IMM89AEBMOscve&t6wg?~CrLGg}*f+%=_pDGII z&VvcSa1J}eqIN+_J)^gct`kO0H0`!>C|?|>K=7~}Ii)?_lvON1V!tQA8G>z&u90rt z)<8x<xAhaA06DUJnEtd2rgfst-EAlbvg$wq6KF`|TdFEI1T=*hpGLV?W11BvO<7~? z@Oc@+qeu~Ctk*`6_^w~_AVU8HQ6ReO9Vl9GAJ{+c`8mx%y_&p6s}{sHm_NU?M)y^Q zoZ=pV`E~LkR_e{Lo@$^(%bfVMbyfXweMnTTggp5Q#~mo1JGvF54LY_%#rRGWlehlY zQwOp&g9JjsJ?iK@n2B?5rT}MUNt!U1&~TGSZO5AZhfQq~G%ZP!jy+m_$qaH>hoJ`C zN{rEY&AnvH?P1TojPfIWv25EUVu?+5TJ&mvNW$0|=Cs=3=>T%6?=&i0kZ}!5FaW~# zv2vUhiP{wtE6&P{m^Bv7;{oK8iVwD|Se1^|W-Dvm^nNv${0f)?#jTmbfQx7N9$*UU z7aKnj+(`^0&sGxzs6;)e_VEI#LtTZPpY#;%-au`ovbl&v)g?#i9ttZRDqD9}8Mp*a zUyk3W^nnGJ+Zu^2!X&KN&VkA;yN^0=fpNv+X7m3a@`kVN<35S#$D6ILi?Q#UY|Vbf z(XN}C;}%o4EJ%U5mUM9#MR3M^4%009pG~>KZ&dQ20d+Fa7PtuSACwRx+luZ2_s#-R z8hr7P)$*{Ll|aw^Y+Ran@0u(<vL-9PvhN7|So_Fo7C9K95hL&&8jIel`Ull%i0#{{ z`yWFnBbm6=amOv!RDpI7#q?RTE_=MYGcGW~LCG+_Rq2XeDj}<<gyz!i=E@M4fyPYn z312YioV$=IB4KEQM~G3<<v+I}tf!hQ*3Xn$R7hCkK>;xR!X8DdC=8<iKnxI%gZmn% zv=D=W?aJ9^RKzN{*aHC$!_dABL+rHntN3nRG8L{W>xt|)1Dc>e2Pce)+f7C*_)Hls z?zA&<6~BGO9GG7;huDO1m^pN>kg2?Ah3-AO5Nc|kaKU(I$Bg9Q_Nq6!B7#PNsE0!d z>D96QVv&Grr4>w;982sp|7?+4!ndhky0I7_+r8T#Q!Mn_Wf%=2d04)H7NHm2IA)gZ zH|(n((H%y&G|J=GyqVqqYEI?|ioQgSU6jMCv$M5aL^~-=geT$vf9(bi1si6*EK0Op zoM}^-Fs70PPrGDG3boY)0B`IJ>_4R0$?L)e3+35rxFE`l4`I0H$`P@rflS<*xZhYG zx8z^H!S!V{g0b1C4e}_h;OMJ8?k*ze3#x)h%mNP}^;@?q?ZnU>aMhB3_B1f7*&o>5 zD4_CTsmD$0YpN~q!V<My<|iQg=fj=JZaL;~>8xUo30mS0c0w?@<DGszdX%ktV#*tm z=AZ1*X6xXKWQ<#eYk#XiZ@r%-yZipM0qOrRdYKUb4onoDC@YOw)Ylf;xP%VfAREr@ zdP=M%?6x&b%tF+al56aI;<=S(pVzF&-{<v0M0+&CYu@4xP6eo$!36GV+QSz};D}9h z8{Dl!t9t*k1n36#eJF1ou<aV9O<~`Avz#cF#_9c=VN`r_;(oNpOxJT@&wM$w3g9j$ zRI%_haJWgxK(OzR?$}dy%w@_W`|cFb?`2YB$_W+=4n!K9vj}BpXieg*&PS={`73mZ z`(FXcVTWd&PxE_iX#dZP-5E`O(IfuWupZ41vwTb@J}s<XC$i~K$*Gogg9z?Gb;Rxz zsuABDs{41OC;({;Oi{V3yNAbC2ghdz)qQ8tC<fI)RrK^SufHR1*Ip@TRb26k$1%s* z1`~X?cm!e-e5&(SC^E6Nxqo7ve>@FjKK`Uj{CX^Zz<xc^$^ig_1@p^J)-8rd3!U=A zZG$T=P9b}2zP1d;JcWz5+exDivzy~_{vd?&ffO>Z58}IqAS*Edr;%Uie%Cyn$vj+& zom`TCM%mrt<uvx#OZEFWWj@2iY#O39MZkvfa(hQu)j<-~=n&D*kBB0QH>Q>{)+vXZ zQpx7|kbmHC%;D2dO_~5x0qM}B>2LyKg!V*{yr5(pQv#;2bqC>?Fui~FB@rTxW2&%9 zpC44MZ&-*WeZcx%Bm2jzBjs*2zkEjA!-?IP-ExaNA~)TmH%n)s@SGQg8iftc^ag-K zfh8wY&4n^;@TR;%c>H_>%u8tyP;L>3T6SU?1-iA$`5Q0aq^7o<&96IC>N*yq-RP<+ z$bj54`Tk9anKYV*r{nBOH0gge@#TbCjqE(C%*Bt5^86K$9&4)ZN2MYNgSq>X&e6D` zEd1JzDjZa=EbRpzDQWn{aB=;|BCbXqCN+9?im&Ga<LBPr-Y==%Zq0^Xx!k_1m?w`t z{27Yu;m{Ck5W!(})|B-ty^hKuYau;qzc`;|Ni43UP);pAbI>Q<5DyC@e?afkuXQ3+ zd|eFY5)6NQ=i(01F&qa|S+&+I(Eyc2c)3!%!cS9r7HsT3IbsWum)#brS6f>^k%u0b zXy#MIwb8AyU}}&dPG=HG1TCYwEWBWreNb*(R%hG1wa{-*FdS{nYJJfVeBqzF3q(s# zRrs+TU+i6^@dxs|etCX7z$kGs-_PjnbAtSoEyskwEh4$qiR6}g**W9yxQIJ~?mCVU zHq|%d+#h+XOLKrhv2(h+IQWJ$c-QLsn`^7b8mjFB7Wdm=LQnPL1YS~EZ7TaLDHN`K z(THECyAk&pe?)_2-1@q3U!BUr9I0#em}`y|-};)szC8Gmw(6+t&KTLiyeOrbi#Jop zhVD;$1QOaIfy!8Nr~W(k(y^NbwunEuFqxHb0Gv8dQi<)5j%4ymxd4SO^8bh{)Q?I% z`!6{&eOs=TrWU%o|9FYjnB}a%2^}YLii&TfoEMEX6j4*|hJ4ZaV$M8Kt?>MKV1yeb zvXMq?)f}}dWPFT416sszC{EbgWs9TWq>oSs5)rX+2?d^pr4$)m<5$z%-#BdU4sNAe z5l?>%YW;PYv!1lLh{%ega_&%ETqxSrN@Bb2=Y?`OrpY!n$gjGwS2(EumnWZYFP-dZ zU^SGu9@D|9j5%ZbCmOJ5gU~eiwtXb`5n~(oV0@X>KF+5l4b6P$h_Z8ke5N{9<2cGC z8Xi>tLJ9)wxK!s!fyD0ET-!BGlOY~it}=^&WTIcQ`60cfP>OI6@)i*80VS(P$Ii{A z+LJtNfJy&Nq8CnH^zuX>e~3a`YXNkHIHxp5F+Ki0a(K#<K99aCet{A>0_b1ed9}91 z+4Wr2d8o`6M!lI@sTH?-hE^906AHNKWmb7vm&N2ykuMvhe64jE2wgS#@%R!7E9!xm zOTBlVqV^t9kFB{Ls229&q+fW~F=*}=0#c>|()ti;G#JlI|5usTI=^tDx5P*+c9bu$ zg;6+>q08v}tmUhiF>}(vVi6RWk%_*XeJGf;eCB!7+hoQPxoNl7vi0S3E@iZY=bY2k z<<qx!56osG{hrKHy!fzi2t2^}{{|MD%)PMlmev{R)XMwy*3-%7o|C-U9lC4Cw+qT; zz*SU|2Ur|Ol6punQdUM<IioS3*&k#w3*P*xC9U6^WBY8iQghs|_egp*P3QXJe^-t2 z#SV<J0XugW!PWfQ_NN5v`ooD>kl7$X;6j~L>tH^I@91Nxju_#H`jcKvv*BcCc$8ix zuqDzP#4N7#QR>%R9y=^rRCYONmHyPxH(r&?kYX;2+mChpx~eRnfCl(*j{`A>p$W;@ z<N601TQPlHI0)umfokR)Iq(_%x*J}4p~AS6aEqy*FifBKKxKZ@aj1InC=a>p>e|K& zWw5IYUT3?v3a;rlJCwGca##|Rw*ZLf;-cSE=&5HCO)Ho`pLlyQkr!wlB7jyHXF0CF zX}(n|iLsA~FhJ<qlW;ikauDCvptuWn(=Y~a;`IsZ-@nUlFI5$bk6)jR(401T)@ke7 z)wOncJnQNk>&uAsORd_qE!x$pC`nv`fuUacBZ2T>B;rHF!r^&d`B1_@3ov41(IG(n z(aD}C=dYe_a2gTl#ir$#cZutzrLZyi`^&t%5AQmkcKgIp1MC?f{^yJsTqbs}ZYG)A zNdB<TSy#v!%0K9UF$4yBJC<xA9_il?BksRx;F)PQn47_mC*#({6=;YXy>E!4!)STU z<68NaK&xKDU?96oCvEK<haQ3qDkx(Rn^rG_;&p%*K$9kaq(eH+!%u4qMoIn9;M-GZ zp~4;D4ZYLb+D|aJsj@b|67k3FpUn6d+wk=^)NVp`#5YlB{gd8SK|1!fT*jw|PSuq; zM;F3c6wO_kt{kyXDs%p-A-Cqjb%ev8_hi|;>nqdgF`0j!*?r0AbvC)iT92W`^~UmD zq|sNWGEOkRXW+~#<dBAmQYL2YM%6>mO7zGZL*Iw#D|yQqX`n-N6=$0nt4yF_z6UYH zykG`bRf>3Z7oO{xujpzFu_XXhGRC2;766hjz&9CkUv_xfbW9U7_B5o3#1H6!KX{Zg zDDk+e<<>X)DRb@h$~>W$X842io=2C8qx4b7xZ+>@3Wu2E{JOuI{I=K+N@-0pY4=wx z&UFT;-UIr)x}%iHE<(8<od?%dVQxtVB3seLw-*jMRLn$FRw4vgQ$Ey^jAhDTJ?HR4 zR#_AiWq!JGB3_E*#~)`nBF{siog>Xd1gO)5C#@8O))nA;(Ta&fcy*r-19ff0F#GMl zEpPQNr~&yT#G=@5u9m(-8(g96q_NL>`JN_Kjt-UP0j1_%J?Id#;`SKf-!AvX4&$k2 z3;FZgrA)pM<lTi>Xr7<XKCQw;{X=V(G|7Qic$~gUcoI#*R~n+sfkZz1;aStRejGBK zP;z<43T`pBnyBTUj0S4t?1+fj5%NuuOBAy&vGuRoC^KL|_#?>QWe~?;o&lLOaDnUm zQ)<3$>RVfJk{O<AV=ws<#&ZkuJJ7$k-vV%+o5>}fysWCXO|cT2bGE)eTFWE<iKlx{ z!s~W7ya?8wRlDZ9O;;~;6HY;seVX`{r)5pPNztsnDY*6(m?>x4P9v4t$pYONNN)d- zgtn7}Gm*~Ex1Wn-#b9*x|HP&dRL=S%;dd|b2ZTE5z9<mwX|!2r$Ef~WKA8$Mn`|{^ z<lsbfpd{)Fn>e4)POQYn4%SCJ75<gQAOUY8(hB!r9DQjnd$~|N2QNAY&IBW3!nAi6 z#c1!gTJHMpbN3=;ksZyAk7e$$7Uz?BEl)7my=pXOFL<E@J9&EewhKSoUkDp7>lm%i zQ7oyH)Z4|_@?FjA2Xfz5?glUvyX5BVqXz)#xr{`76U*q|jcdj8cUgKz>d;GzwdOV? zgh5;pyuMAgb3oVoB3ANmzf+U{E*AH|&~m|*lYUSv6t`G#?|Z?tl{==*a@lBE7>pn^ zQs{lszmEkvr5Iw#(h$LATUHBiXcKDrRw}VWRLKBsLH>IIhWUHft38^xlms~9(?nQW zhkIZa2BcGYoDCa8^d5G2MS1p=dBJ<DX*$LspYpE)%t;qmLwhN#=(ywXUk_sbBMKPV zX-TnfFmf!4vGw<&1{hKb`pbayIRKkRejfqe&W~@=!}uqXZcGMVf2pJBm8;&_TXi}M z8F>>nqBkjV0}Zdl?=6>q{k-UJt7;|&|5(NaNC^5Z-!XMBW;fQS?A<?Z)*B&?;i4L2 zqLtJXyi?u`YtJ@pw?*fU8JavL7}&p64uM|E1%&i2&*k!u104IAeHkqz(N70lY@8|% z7+%${?=Q}HMixh>5?NT{BJ=(JhvjK)`liUXICsxvR@kGVNRGTB+JP8mDwG+axI+g4 z^hy1AfjCLYatF_Zn&I2T=vFyEW9$0!1}ROY7%R8V_Z6Be<okhK4|a82wVg|a<$()P z1FKxZ;deqx+~zeL?P)fyw?qM1tRZEcxsreZuNmaI<wRV6vbeI3pCZllBAld=+~_h9 z9PB3XLMR&qB9@%Pj$C(jjTE9ugU)JtbDO8$rjcYy)~}o@=%#01pdgd5VTkILlzPOI zujZoE@oF|B47R_r&ynd?)qZkAJ9PSL5|zL~5%DdydPJ;@o|srcGZv2C1~iG#5iF<M zKF!gn;kVES&we_^gB(hOh=Y8dF?zT^M&yJ10yqV~H5Ln)sDw*aF!8@}y5=F~3Qt>V zW528@Hy|AP@no@(2D3kUG^~bdWDOJP(s!MqP6lfnArEb5P1U2g<+c}*TFb#Co(slZ zuRK(dCvA3Uf});8Py>j#%Jc{4w&)I8!VT0h9~|7XK6au^I*K4ifuj{Y3-W3wprJYl z?>Sbf<@SuY=pbT-)bk$CJJgS!u?*Y4R;ZCPt9ytH{>16uW@cC#Hbm%_99>^82s*U? zu**9by}`2k7kv)buh7sAy@EKLaB-c5{xu1S5!){GG%+I&xxkgk9^TEau(=4YF;)-E zulGrTfn*&dFg<VnJJyBc)DmEcd^gK~rM}c=G<KnJa6)&{3)-zUmFbQn8pM#>$ehW; z>eo#wNo?E68dpY(l=~UjLZXzU%E6wdOUG&->a>%>DUge&#=-s)L2jjfh^;S(q#-&u z%EO?3rc*6ta)BkzEL?A+8=~bf+{EnG`kN-j#`T%FPX72W(>kC|A?=|PC}N6??X)Jx z$Rxki&02&1T)j{sx7qKI{Bi^V|Hfo3^T)#i6;}(~N7PfxP~1MV4&`z#4JY8*-<b#p zKW2>NDW+Vx9-inlH#|x*rS;o<i%rkcTmuL?x3nLG=q^FM#StuPFXhf6Lt}9LXw>-2 z7{QPG-P>>2mntJLjm`tra2zqGJC}ajj?~OmZ)Eaghdz-NiWa850^3b_k2TcfUC~?& zI-o5IV8iknbATffKvb=+HWqt;gh+gm7i<}+9E~(Z4IRGX4TZjfFuV^vH%~P@0Ru3Y zP$M#frK`q;or~0uPWcs|)E6)rgB*}b?9CH(vOA)UG2?n)49FO8Q&8mgUSWtfRgkd+ z!6xGj_{=@rUE(?Z_#!I5^{<#^k!Vx-BGtj3@o%d_P$_QY;p<wv>a~YiQ7yo%;I&yS zE}q(wsLT4Kpb8V-Lzp&7Y%+_5OOhON0Xiq8Ync$@Uvk*6h6^=ug;&CK7CFO|_a+Sm z(93|!i(s-#=UXx<r(HMcz7;W+?Zh2(S%xohRf#WrIgD%5F_}VBYY^I}6b7=CD8!`7 zari_4j#k!qcSQ=qlWL?727bMVQ4vnIz=V(CB_?qVT(a-ZXs47_q2LY+<jC4<WB>A{ z)yfrAbwZ#(e_W*W(V*3zwhAe<J1r>8Sb|e=jo(I&uF%e+d91c`g)<p^wd`VI(Xe)X zjxZN*I0*CTsZNyZ7>I@gleC&_Fmw3&`r1il1reCD=r9iMG}+;TRNlnvYmNi_;p#KQ z8XywiMz-%ECkm=d>q#5^>Ok(N%Nx7QpWnSy$al5lP;DzWDWO&!d=)M4bM(Lz=cSAd zYo1N7<~U(H<F4^N0}%r?A~`|UNA2)BJP2y3;$Xo`dVNFggk<iwGR_sVICyamvk8R= z(}?Bumq#C@7KWcrJTVjvkpI(wuf1BPyN8!x^P!+`Plu&dN%=^5N&tSg@JZ}h&&HPu z$6)A<yu=rMf<CF2MHWD+*Uc=(Mw*MnU?mALNsf+E4ptsXbQ0-~rPX;d{ZmfOCAboC zyF{QW)xn|)A6OzmTs>khZBRp>@M%+gg*cw<^^xw1y>?WoS@;gq(F0!3tb(n`5nN^y zx6an4?Eksx<P5thpn=~g^-9#OKcamf=L=teNrSW+q6!jgG49BmBQc8-?ISZ7F=qPO z<n&T2ernTw(a7A&BzX#9yP0Q#{3ai*SYSyvr5(q-M|^xCs+^}wINa6rUWL0`zm69u zD}VX+04Cx|Ig2W7k016XJ!+<oMKFW^T@OH<!yQ0wc^KsCi}{pK?^NGJTsG(attI)2 zdzqX28v(H=Ei#h5enR;nyOuQ{+N}(sW1SqEqD=kUEy&hBm$|*i?3B8;;bjmO?UqR1 z^kxL+grYUD*sVo)u+w#}7gqFFzir70`2yyBaJQt*Ej%lw%q)hCRn~c`ARQJ&GE$t% zwqOxLOE&pjkr>maW3oSYbaW^fn5Ie5)WH;vj0<$jEt^4=4b_1qePeGh*UB{Sop;QT z^i06}4$V14ehNp`;&|64V={V`|L6K?Cm}?RuMq{$ANHs&eggwiTcQM9JeMIv(A*40 z*2h!*%Ox0Dj@VnP@Rb6tnfc(jNeWKNh&}%E_X--J%xG<zn$d>_lFOy=D9lZh>yO$p zJM+=oxOb64)&w&#HXDEVPy5U6Fyq4hN2OMH{us~j{>_zyzcO(zMA(yGa>Lu$RrX@= zNIiNxGw2X{3$~+)!6_v;LW>G4Z*^3b?G+m-&CJ;{N*=ZH8lVco6IZ<!Vl+i#wdqqi z^C7Enq&Fkfm*7|%`3PQn<arsJx`fjZ#KM6j8+rL#m(K<qi)>5byrm9XRBA!2Mb8VU zgBrl+LrZS!CLMcOI~%$C7YC@q@JX1o%RfOnt$)JJVD5B%5eNgNks!q!hv!HH-4#<t zZKOQ^41Wa!f>?Eg&fcHB3gL^YE<TtE<SZb)j9fn&MWJ}9HyS-d{Emra(j=*EK|)yz zJynjwA7c4l42GDI7jH?r)8-VXmIp`Sg0t)k>4L4DJ7lnd?0<#P^hG{Z0-%gApl|En zXLs!oBTFEv1Y0}goX{WSS0p6n9%2@Isw;0W>yL>N7nYX3>i9}#Ko03zK6z%iF>y$u z`?_#<8fT>pww*sGp3KFk*Ep`a4BF2O!TUMs3q48Mlrw`g1RZH@LvK!j)K-#k0hUzz z^QO`^0Cy4_f*lHgTkd;BD{3(^6mC{@Nk<A$e~&^S>Ai53))B7J@HlqzV0|+Q+&H?; zB!9EU><m!hNEwu`ud&7b*hU<Sl)1m_H1kG5h4yb?$w-$n>791PMWf!O(*Lnq`GpsC z)$bJTZL;zSXA2Re>3+kE=%xRZfZ!-1?|6hsV|^#&eXYKpt{QoA+u3hF)uxSs(i|lY zb{SCSQXjI*BcT2&R^~S^f2^o?@CYOHOcbdji2_e9gF7R;Cqa5}ooKX(XdI|x0}`Qz zXuc1xOIed&@j;Wf7o+2I8cT#jK1hU*^>Id5)$EqQ@s1a4ha%Q)0So~IbRSIc|LzaC zn&i5PH2S~s<I4oq=)DFdNbR<K>osi8-MNMCq9{pw42<{r!R96q5j3UczxsIG$E!r| zriI6<sdF_BGN*$sJ(NpI(_7=oHM3pXv4ysOMk8Q36_r`ZQ^~k2_(Jcbi&_%~$RhN9 zbpym-L%!)UT0EY7<5WmcczcPLMe+!QzYcOu^`X(CgndwUCKjaKD6qZqL~s^DDq}0f za%e@xcSm#!mZ;b?om{v4ke0~?=<*3?&U}ee_mGD+Qh9X`eWg~v^}SXAk$bg&XE36* zAhmae0%5k=#G=_6=p@ih*t5+~8^lar_DdNIPhDnf4wev@7AvQlx1tZG5sR;AvlzMQ z*<E{+$iOUq-@t}$I3KYDXNfn}^4_3|3a5lHJ_<e;jx4!S!*2mo+CM5KDe062FsTuG zn7r&<;;J?vr#8|kvCkWEAoaIB1I@Ugeu{bgOzkWzVIgW{o`;4=5lS5Wn6I$@WjZcc zhP<}>mP`PKeR~X>OWd7Da&_((`13|>3O=^k@{w#Lq;voYj<qYgq={*O#JxIh;iYsd z2aQ)2NyK_F3!UB++b6rp7+W3%e<x=2+Ooxi!q;TxFXPS(!BSMZ!h-#@a*$xpgozNl z`ADw@`FJ4GlT!h@n+Hmf=F{!opvQenUoRmg8>qqQDi;|xp`%pG%RNEsa5HQ0-GW!Q z>Px}{z|YSw|1h3mI+}kh<A!qu*K_LEmhFEEQ2HwMCo?N>Y(2^cf82k6P36M+-c8Y3 zdwjIAo%wMa6XPjF<8N4xOw(+AxbeygY?LuFx$R)O+*Gn>#}axNwIBW2i@KYBh4JwT z$8x#|G$JIlF{%9_6E}T-`N!?sLHb*o|2tDR+uo$^hxm5yEe-Y@Vu1$fQkBK1B<dTy z8E*8Ch3z$?Pb?<7-5Fiv{$Q@r8i+C(vJqlzFdmljV8BUeUSCEBakbL2Jzsl-=_$!8 z(a~KlSk}Zr==STL7D(c7b7hI2In@)>AZi`$DUbrATg+@d8Z!@F0Eq^jRUJM40g{Z{ zTJlFPII{qwlH(A%JQ9Gs(##)qrDak*&sk9h3ef<^nP*@cbMm`_4~cNS9olC`+B^&n zS#8pBE`>N*wD4|$Zm!VS^jwjx*G^6U;#s%9gjRHyE%lmk;t`wUnd!$o0AofQpEM2E zyDUt3oA!*ZOiO5cXC^9FM9k>0VQjgb`)XY^b5L1e3>CjNxjx#wc4|HnUUJ7~_DFmM zTFQ5;!sDSX)q!FDnx8f&1VasW-8T1o1d<t<lNS$~fmIj1Rp@|b9yA+Tc<qBu1Kd9f zFB?=*HIN_-ATb_VKfsB{O*5IJwDPH6h7K`Lue?2(@;Y1y{l?ns?N~-AlPDjvAK;`O zKN14(ywLEzZT}F$VVo(TeqNd#FzL0F$JbTAshvr|uk_0Z`~6ZH*S%@u!O*kpdcx(L z>A}k*t;&3;;@)aG0v1CCA;tu!8a2`K;Vhd1?l4JU6t!N?Sm(ahbyv<^%!L3vYg9m_ zqmN`2&Tv-*4rSV=iNb;+x{<l5qr2R}5A5a1Q~i)LZ*2SHYXf#s#>6aw1|<PXMKTZ- zfGz*8L$rr6_zJyG3zHtEcRf*R0UVu3v&7zl_>Hp+&DV4wbzEk2s=F`a&OJPP+0rkc z8oC6OmB<sV&uafVxO~^MxBg$ZG595K#P=I>dgoQ{UG|@D?0LJXv;nGC*6cQ%?Sj?G z2gGcs02)*<0d8pHPj<5oRJsx``fNfmt2U*2JcUKjfOjvU4-Kv&FU$9@hy+`}Ub&X< zierC~<Z(0fT=K0ErUUEq<*#JF{5WWlFLesP-c`?vNP)BU9tNx)?5VZH(cqE;(lKz# z>NExlR&4L@Z>lf^gF4$r45e9S_lp6tsqH2clQSfu=YQc2Avj0ytT_JXhGqvcFyl1D zB-bc%w(f{%#SC%M$Np&(1}SyEr1^XT%F4`4Ct|`o{fAhj5i{9k*CaId@h5-{rvWjg z%O~nf(xdrq^82$wfSJ-{-cfN$gL(;L(zA=_%l#|vfQ^jtsRg78K`>`LRY_6b!oZP{ zP6|oRu?({mf`NvOP!V)s-kRbA%zSs<HoxQNaQ!QL@fi{6P2cXWGadM=bIO^WyN3lw z7ayf~iu<RN)(Ly({@sHyusv6!Bu}<(`|cy7^+5+e;Pq9<PHM6e&TS!LiO4_OXnE{g zgPbJOxadOCu>~%vbA9hwCKEcUykQIHp&LwH?Uc7NM9_CJoQz$j29ZTDzUtljgo_oG zGX-A;8nE4pFLm?v;Uv0cpusapcQ-@gj|GhZ4)^6cEC{^@9|q#@d>Cfnqh&8#G_@8d z=kX0BkiW?S_wD6Tz#NiyBBfr;UH;8u@3HReIdb1t|0~%qYb_o@tJlJv31OHkW4*(I z1Os_q*9`2p-OifufHwt%7D~u*tphM;!Wi^OTBd&7i*H#5gXZmczet*eq#(gGYHfTP zQLMnwd(tLd;g0X<uy1zo&aOPjRPM*nL3jml)9U4|JWCxP9yr{>Pqoq|;Q`7Aw8r+t z7ca`+77PP#JAS_&571Z7nHR0UTHAN=WV82(Pw**$kSy(%TbERw3oigHagJ#k*KhGU ztxy_Suu@4ABvz_gF|1q5fzh(i71=<MS_Xc9QRW^xyPRFsAug|@3D?cC?y)hQi9eN# zQt&#la07)Jn*8izLi`@7-}0VtN%NVLkx|EHGouy)FHTbkb-xCy%b)$y)4k`?v?x>y z2h`7z1agF*&>e4Z6`co9-9J)rKGl8hho__@h;=$%b>l>!0WoN~70Gc0TENEw_**sJ z!GbZ6%dnTn33K9!IchRaMVoqLdPb;`)Zo^p5ip21CJ9!OEQE#$b;cL&fUl%5Snc7J zW@Md$f?)izPU_K6jl2A-NiCbWBb~H;ljBImbz7^Gwc;PObB-*kpElZhj-jVGD`BBy zGZabr$((Q#jae&)>bW0AnlNVO);G#Cqf8dE_*H~`%><9m+7XCU^d!pBCwq7$%T~O` z{nrs8ND!GL*wj&M&R3k0XwNTf;$RMhr6D#C%K=!OcpijB(yUnB$2vYgGFIR-uN?x8 z^}N7#XB=-yAkngc4+9n8u|+do!EQmcWi}l}vbL{)+Oq2;Ko3cN8w6z4Q)I{bZ!Fc^ zi5K8ZXp%*vMFE)jR%vWT=d6~HKEY3W6HP43-v*Y9+cTLG$f7PfhI~lg0zQLxyJK(& z=b?9QZe1hE=lHb65~4-?r0y!#^9#9CjKeo|NL(CciFImNIy^c(h|<YEncb50B<(_u zXTsHG;PhdZu9nSip0e7TE&eJL*PgfBN;V1NNjjn7fhQL&Q=V9LMDTe+b9kN1yrJ30 z(2$OKI`GXrvu`fpN`ohtc0?*p;!ZB&SF7jp86A!O>^Rl)K`ky!ZYQXK=eaT|XWXI^ z*^VWze!z^WKL}#~=*AQLC|vLJP#U{qHm;k94HDbNl>JOpp|xsGlNw}j4(XQs=u+7k zwiV2Vx2HA#_$@Ogw_<E4P7nu5XjCB5+Yx7>46+G-x_zPl%g)GIVzNohvO#MpMswoQ zy>k&*ZcRjiu@wdSJm~k(3fwwQ^sik#sNm70<ucsvlz}bP-$<3>(5lLOsD`>|Ga@U} z68XZ!<=~-_n~dp|`-Bk4qC5H1dE@y+dlR|Og>@uG7ZAu=^?8^BQyH6iXwyP^1Q^Ia zz7IPJ2MITb1KTg0Jpuy$DYH6R)tI4%ayYqF341Nr`he<VsYZ(LN4+QFbyiB`<c0h$ zR;%iVnMUz108gVq9<4%m#qV8(kYB2&Sf^=yBavw4mq~+RqmM*o_}dsRk^v5G@#`Xx z2(?AB#^(LW8cB=WYf7O2I5`moE%-ph_xP;(tYhbS|0#;}do)Kn?yoH^7a|e>^m}&3 zxmK2>os5+(GU-e4XRGQu$4peZguaGOt6(hT*!tp}m^Wv(y<aP?{#XnX>PD`IN;)(I z(u-NjlAdMokHCh8?CNYnELJ24L2OBuxsOlXItb8BHW=HaJ^5a-4=2ynTaaYuqYEE% zvD=`PTORP6_oqAFlIh#v@i8psXC(DT?Rb<aVG$%w|E&m9@79Jz;uhQyWFhV?L!bl9 z+7%a5!*crzpib4<=1_s{GTfb8Jzc_~;T^TE@FveSD1v+ncGeX6mpF|%+#AcBEhQaX zAc0=@3&)8yU9lJPbXPe@|B$xSNVSwC6mo1YKNbZ#(`xS6mSZbRT!kLGd|rqdyjLaD z2>PE5{V98`D51=Z###$K^@T~s<#pZbQI%1XR<6RoI~gP~LxSEB$j`q_2c}bt>|kQu zcSSamtF73!n!NjfFZ3hh{{aL+`@X^P3u>1j%E{5QhukA*f8vLUD){UAo?4{Z2YA%Z zjzp9efIoFXEKk@m3GxPCQ6tEEk`!=tsyqcJG{Aroa?Ms=82A%F2z&Qg@t$HO?BqeF zE#!SImhokQk|61DMX_Nn`9wJ?qD}|XG5Bxq3(6nx-PPRoN!JGBHGHk$SK(EC<4<-= zGT%L|>HdR$c>S;E8T_lQ2qXd&1cE65WhGA$cBNrXlx%ATe_(K6cZp(ltm@b~$x!fN zezG>cZWJbdi{meelY!HuB{34bq|?S%uq8_fzr)a38+-ebS%yR9(sB2R!4WN3k-!}s zoG^jn7y`{o(*9UtUG<UMZk`<_d@{ue{!;#`MUvFsml|h;@ueMRNNRW|O?pYvxd=@^ zdf6~IFz^h&@lP@ZAXJpWv?H(G?Do7EQ`7N7d6R#gex%#M5?fpm6@n(KFJZUe|Exq6 zN3bx9Kq3g@f0WnYjaPvabz9T<8V%z7XIO@-y2mmd0vSIPBA!szdi#xZH4Mj20j%$e zB>fX;zZWz0yYYA8>NE#RM0+c&<47h$zdW;Lk%iC7+j}?UXdQwXvql8W;a6oc+8oj1 z;}&m;<99fPa!y^C(!@PWSJ6%2$3Qr5i*?R>NoI5l<AH-UAQ1YsOlBHU_wrF9>ucOF zHCqr&-dzhborg;?#N}~(%RYoYGn~QI^-L1(=5!*$BPU6|wVEjg7=Z@_BSkdP7Ds+N zjqMuF-I&Vtx!MeKUf^vDd0&oUe2tUybltPC(hoC4PmmW8qX<VNRt}sKeR<w`T6WwS z4V=2Dc11DSIx932oFKfG*ZSzuBPVa-#<Lw(E3Ci{$RUX07-dDN@+_-mSz^PSP{`QV zw<AxoG_5(gzHtb7XD2qDxj3pbE{dRfGhGEv5f{fuN;$T=idLm?7GsCg%KiO=XKGbM z-T0QM+8I&&AByCIX6eMc9rD6dT@umyPYHmXAcXRy=5SUJ`QQcQ8;mBm&W}-u+K}-^ zFDFU9I4gAXBA>2%mS{6<LA8z3!X>dpogI1YX1C|WAbI=li!FIl(@3|2s%v(^%6%>< ztiFVzx&3Ly&7HYOatef#0b2HLp}hP41GfO?pU4P2b4RcFm3T4mh4vSVodOvdbYvTE z=y|Xr)`C8sq&?@FCQ{?MChqv7?mt(>H&0!V?<tjA^%dyrjdPQmhAOoNU?-U%S{%vS zjvK{!i$%8_LPdv45tn#^pamhUn7ZaEB5#ed8~8EbOm85_+N~ZCSf@v(=XlnE5c;p> zO2Y``{W_Dkw{p0}b$K*x&y~BT?)$Y&&1bnX;6--wrM#8d(0ZrO_5uoE<n^I}Q>4Qc zDzkY?vPDaAT$fPjw=$`Xh&qgt?`6p52h-k46*~k{Y;)e#G0Y#U^x9j!i(ry??AnvZ zT|yw>gA{MSp1|5#b`!^#3i1wmT~scJk*bYmYuHq`GOxAw@BZm%+S-CPZ%Zs?9P;94 zMbh5Qwo=VS)^yE?;LQzZ?1iN8H?pbRBsU=H+-`A<xoO&;aietDQtGsCr8mIHyD&;z z$g)-2ybw!!CqwdNzmZ96;Tpz%5o<G<^P+hBio`bK48)gmTCd2g7s+D?n$-!cB^bug zJG>dkIB^x}ocbzLxHwhI)gu9u1yTI1*&<gziszNqXM@hmvaZ>&4L_Bt?8s|3yFDk? z-rjTMT4$-KbEKclV0Aj8B|CDgH;f$0<i3-{J!lE!E=gd!rs#nm(PZv-@}c-$Mhblm z=KdEkY!JviP$G2;<^EEP@D5Y|z+4oBG|QYOe~~9A2(9M_wOu_wRCoMdDGRzJ^CtIL zM_z))DU9VDjua{n>U0j?b&KOA+i;^RtT-Lh!o(RCMn>27x`d0Oh*OQ+UQ~1gU&fj= zRW;wbbACTpI19~KmBa`Bq#p}}{UeZfFHRInA6_;%UH^GS$H2pf<xE768k``$m(1N= zuH&}evyD^lrty*XD=*5C^D5)i1Ot&);$-&0QfdFhgX9JV9(vy}g5!1Y*|}n=t96q^ zoA;DAQ5tS8h?C6<fu5{w2TtM|%!cuO7KZwMsj-wXxiSM1Kfl&AJA!NMv(iC~y{7)D z_KvL6ya-GEwESj`&`O?nM}lEPHgBsn<Tdp5FNA40nhx+%cyIG+Y6DE4;m2#G9s6o; z<&IX;cq5HDJ6ZxxlNLqt#hw4QvE~<JXloL=PmuTUCADzewqaHZa~g)5VDr9$k#})& zBUN+T>azrmW>YHr3}Kj!u85?axq%=r_@#PPp5d|#`Bgfih@WJMUI>JKu6)K4DX!t( zoEYW1nS8suvzy(X6N4T5AJ;mom7~I{-nCi~CYu%tO%Ng*+zPwFe8deNE0-?I)Xxk@ z`Y&L{aq8mmzWVb_t`mwKP^+F5&U+({JrovpkF(6xJw-ZDuI}lXun_2#Xy&{~S`ka< zNB?(!zZG8%ya`Sy%AXfBj{%PON{W2%=-TEOlFl_kQYk{jK<KSzmoQ;2t8I#otG=lR z_%q!T<d`egSwB~0`Z`w>Nz$)Q5v@-ZCg7hU!e8fQ{^Z+3AM{!bV@)hF3jmTDzAjL} zWdDUw`nnWR*Zqf+gpU2|Kqw;iJM{`H6vSN-P5-e_Y@WE~6Mn1r@+80?-W0Jw2-S9l zm3tih%><sHx%-2&t$Rl_?Y@z~+*D!|>2H6WDg5x<&8R8VHY4Ntu50In^E#}lW<he} zpEvH(ntP5FOHNYmKX}k@^&L=`RjcEybeS2={qLpm++#RWlW7i?^+cy7g{b-Vd?$`Q z>KxalFy}@~;X80)w4_?tX^Zp5GhEQn6XgB1N=4N5jyc93FMTsfl*{U{iM)_Aih-S= z1PgeV&%n|)<_}-q^@+>?5;B0}{Uq9ZXFJj<NTJ25I0mL(|4t%rN1o)_ke8-2yM_rp zYqWNEXE(b&CkB(36LmK$RihGc7CBt*``Oli>8rho&Jhs6Ah@jV=>4etri*3pA2?N? zOdCeGt!aFxV0H@AFguL*Qz_CHtnRx1a)Jaw-V;RM>#a80PB)}U7NQ|__k&Z!TV}JB z){o6N>6}m~gFQUi9C+2QOIKoQM~+MyJb9^cW~BUzWk*pg>~co)Kgr}y(Q$cQ)56%~ zo~T6EDkoaxU)Acc>n;1wsaMAHu_M}qvuLe{YdQ^G2vL10hW>Ii3*q}F)4p;weBx_j z+1m@%*Oa$xFJK9ljf{goMfRv}7#x^VRTwg>D3FAu==yCAPyh3xVtHyKGBMU~l4!GQ z7zAI~Oj6T<E8R5p(^~YvYU#HZy6Tl(mxS#r3)+G2<P#J6`udJnH8}fIrPd|@hSaFu zNnmZpOV3=8{CK{_YE4}NWY|+8{?Hodg%3W1Z*U2qRB23~7b)k*N{u~8Fu#&(*mwEX zMad`~wvB1**^v@wAM%PsSq0aYvc#@J!LCatm1S_!!-tQ&uW7!=j8#0|n#*01AYA!) zaNz{VONPN0-YZUkyo3d@On6m8|2%q5a<T@=i6qo%cAoD#Wl8rOJ15&wsB}-1FO6V4 z8}eevI>%u4finAiK)c!Pd9i_kK5yd9Ec&QM*NvHc;KTga`SuAYClR12&QS>5+>m!{ zDmJdRI3I^I#G{W@@lRDLokKa8I?bF2KJY-C^A4QB!QV2c{v;{jy50uz&IqQ?38oTN z#yyp-wPGs@mfi%-Ytf8I@-WK7etUkLWK3;yGNsiGBdeAmF>R!)D1;3>u1)2FAmzIR zEldnIWHpK>sIWpv9I@k@Jkg2--l;0p$QIfnZpT4<yT&5;3<B%tTH*%g9`pY4MsU3T zi!8yxi>;KV+qT!REDlCqF7PCK;?+~E$KH$AEWpqrh`K?W$U8XDzaowgP(0vEvRz>X z<2VQ)C)8^^>U-~xh!WRHJAS%s@~Tm0P?{GcH|;EvP6`J-Jw3maDiMXx)mGFyy{qKg z3N_}3kNhskcUKvD`>ZG&Cg2TgQrM2+kk#+PvswxKsBYc_M+|LaX~(}Ut6iPKeiPS9 z)r{KYY)oRzh?4F-ulnu0_ISD2#+rXUE8cziZk-&t{T@7g<a51gJn}kYA*V>*)-<lo z@CDH+n0~Z)0?vyexM1n?qlV?JUc%D;s6l=jOm)XkwRb;A=OB+>Zvy@dq<0TRdbZzq zt*YB4OmO79+V1Y`X1C{u^ZrFJ7H~$D5^cpvfiG+O*=BWzwPMsv9SDOQEoylBF7umN znx)}1(|;b!jh4RhSQ8+hrJsi8EQ?|~_)&(&(JGDi)}$$r5iOo1|7$uM$U8e2`XEan z>ogy(?X)_r6-K(F<+1~1!#U0U#d61J5r~0CzDWVU!!1J0(j&RNy~QdJ#seW!olJT~ zbsP9Fw&XQ+bpLyzMIJaOe=}LIEk_(RIv|YWv>mNAc3H^X5-e?T7;=>c`}>YuXadLU z-cRQ2sc0cihP-pI5Q4m?D85&;Q!HnN0r$H+;f`#+Ml*?8$ctEtLzo0X-ibLcqGXsM z1wKTAqU{v*KkeO5OTzB=J@~E8yf}<PkQ-l!QH+^lVBPaGRXcLTuSU}oq*iKxGs&9Y zuHNbXpMh#gn>X`+w8mbm>q&!}w`N-Byq*_S)6hI|SKr%d-1*_d!c(7Qv)AJcm&KhU zkKCRun2i;*-1++$UVnN5iMAmt<FxO+ayQo^po%ouUTH*+lVO>UWr9fF{3xc4_jx;s z`(jF?w)=l3;=BM&eIhLRK|1$ToeAmnHTP{T)(^!KY8&nVe<p&wVbo{)jZ<{@W=AWQ zVVQP!XE(b&CnndM_FrrZzS=nQ!B0w6PQl#I&o*~h3lAd58wBI&u*`*%ANk!hk;-^? ze!S|*wANag(ItdED+F@zAq}-F5;a|O!dSqM2D~5-eA7hky6-{%dqq0%7lEcV{d(C* zQ*~IUoORkCOT=bN-tdP)sbhq2dy#nL0T&eA4ngc9ArdCd=XNcQm#$7@Pw|>$bB(~4 zzAm#tcYDGj9B}N~3kkAMvm|K3sJa)J^Zt0=FtqIX5!6>>SdmEUmf^c16*zw5=VIw! zS2~Cdlc?{0n9rXdEd(b>LD!q7JTXDDC7HdyL~1bE;>cCp_Lmc-Ab<^Q);7Z*`YEpA zrC?$yyX6G=o~id~%>U7&!6Ss*-{$lB`UhsCxhJY&<n6P)wr$127b4la^7vL&KFBKL zkt?^1_XoT$$d40yA3S_$bv+b~3BJ@;p9;CAfjOPDyYG*3@zRK4csfxo-d@&5)7~C& z<Ul}O`|J>=W$YmPXoW50RU3P^l;3i;=zg2-Kk%(p&)<O`$9b6m!(A3<>%Ll>#B~eh z5;eV6pLN4>XGb$;V1|)5_?pHS$@3z6E#OtRCJ|B9K-D0*52R~8^cj)2N@{Y&2p3>z zc6VntyFDi+)*AO!wFZtB*(1u^Kg?6Vo!Lm1Td6?y*6CIxvNvW3hf;EmzNFtS8Z=$K z4yTAi;<WIC)rxMXAjTU>To6Pa5<t**_qoSdx{6LgUY90BUbA`7HITM0i6=GnuZ-g! zEE8JgcSkDZhsq!K91)tF!i8INM^#wict#M!Jc>thTZ@hoKPymHa;G{Utx#|i__J5W z3-#UiZA1~5q{(;WNh3+4BIn^`!^e55Od|5RFNM=S$w7j#J!bQP3Jo|`w=-9Cu)4Ko zGTkSyD)E|RDL6s4An%kouLq=AlgQqY#fNNiUZT9~^;8*xyaAJN-XE$v9DVCjiJHTg zJ7BaO`P6(2Yo=wa<icR+-5gm8G|7d%*3j*U7WiFmwECsvXu7Y8)ta7un1mj=cB{XC z!0LKZ8Tv(7hV}SEv3Pof=w9FdnU}zDLQ#fPF;RH=R~4O5^PQ)gI!n;oH4vFd_(KtY zccIY6nk8*_-o$mhV&RER#=8eDs>X|Lx+gGZ#R*(6wqhwDkPHGLFb=hcpWTfArdXUp z9NtlQ*EFD@o5R%$CkcCN5z#8(js<8A2$O(d%Cr5(SEn#QAU)uUeLkSw?DiZX@9s+d zx+KoX2mAZ_em>ta8zVVTB(&=5ek>HOO<_MSExz&FWg}nLjYt9e4<m0rzr!Jb<`x3Y zj$#57;xHK+(~?Lw!g*2rRS6thMJc{@-=0<UKOC49##|6Zvx>Z4mm0px;+ZVz)aV;9 z@(Om9$wvn#^$ww|a7)jN^HJi)bZK~vd`e;4!9l#_7Vu%bo3Cr?oRB{PFQR*je1Dk| zGdkM5G={k_LHLP<Uw@IwJ#y9b@Zm#5Bk_7G`0K{Dblwl=bUBQcNk|G;|4E(-cv8J; zraG$#AxBr#dkRHn(<A~})50NSmV}S3Jnw`4fn&tpZ*ut9tIFeb-2;zCO*I03L~sV# zO&u<kfDjhIFl?`lpt?03BQFzM7eA~Ow|`gC0O`zFg681m_K7%exv+C*F5l`?!L=IK za7iK0Fe?_m*(i9trN%FXu|7{n8zEXER2jZ0kU97u)qwH!4ewei?k2jtvW~mU5)>X6 zHI^<pWNpH6`X~NXs<oNYC)$q_B5wc$yse}2p<2x+#qv_x@E7r~eEzZ8;oAxLk#=9{ z8612Fc2<F4HVBu1h-W0N0Ly@XgQ3@3?C#EPc6&}Nji~;xplN9|Z4C16uhN46{+Q{3 zPkh~97nSc83=0T21XF&!)WO!7W(1H|#SBkfxuoa<fy`|w+%NNmPh8W=SUOT+6~tQ| zXA5~}1(H3kbr`zMP61>`EY0cyuyxJLBdH#EByX5WYjMU1KPeie+Z=YS$uWc-$3&vO z)yg}c6={>|G*h`C@M}wW9Uy?SCQ0<@(IXq2_vKW@Z|78T<WZb=Wenq&>ZXtyq?+Yt zobXuP-M+qln75v})&ir|PiLjaYTN5nlSG>zJb3VHbqnwy`(4vc9eEFz$&OaYCz_b> z<goQA97pt6<gF4KG{&bnefY%xB=&xk#tN%dewrs5vjz}IdNYYXGy~Tml$*q!L_ji$ zteYOG@W5L?7fhXYLpnb}YCcpf-jZUy{v44;;ddS=lUlzS2wD&<0zo7YP6Lm}Rp7b$ z9XRE-5s?tC)UdNaJ|uk^OKjX~bF%P*fq@w)>aj~Lg9Cj70|Q={RW$wBh_NS(ylG;0 ztgVg5P;Ijt$~y9vtGc)1lu?A?+NV=hisO%kI+w?BHWjzy*t((mKu3&ZdMxYNe&cSL zOowpRu?rfzyR)0!o)g<q#9x-$FhpUV#^w6@ey`~S-mG_0t#~K+kzdYA{BI1)z=9y! zHeBO5p5dDkHOyazjyYJPbqJw<m?B(?p#x8(D0By8aDyFV0F1ocEg3u;PVdcFruPk_ zrrQiqL@+@)>qluk5Jm?UhgQUHcZw7_V@6e2p|5J-ef~DfSUN9);WM1Nm^P(o8!~jb zS_wjgGlC&o#2gFFPmrxo;z!kw;=Jw{#{TN2BFlL9K)mX=YNSoZ+|#qaTn#*K9I8|u zsn&2i?oW=q7xds5(K5MpV5-Z$r1~VC14$>Tu=+5a=NKkNDy()ApS(7OX887OL#<|* z)^&9E{&C~3SG6LkPW@Tl(*eYTW^*~$08r$8HAvfOGF9&qF0NJHoMdqX_4Fv^8x!P1 z-IJKpE#I<dh4jbr#)*(Oo!_~&L~8YJJXj(HXBe+Xv%fpn0KDpljyY5%eLY3EE<yU_ zuzX?r3(<n1hTj-M<G~BIYQx~0+rgW-zd|$6W9;uAI9RO!sId{_6oTesoP1e~t;!`3 z0@;W**QmQcFH~ngjyC^VBHK|U8(LK=S-Uv11^Ef2FaVl8FG{vOhxcs1arXq~bPRh> zvDEJF>}I#;#P*g8mnF&;N3chFp@D(HJ?9$1aY{6$)vB-ixkwsNH{4FUE|vRbVMB%B z=FuxgWAAXWjWZSkD5__r`mbu$SC)QWZz6GX3J0muj1YrRTN$fe1%f|o+ZpZMh@|ZV zWC5m}@HT#JCgM!a2|FDl1OPn>d5>2pKoEVX={TII|K*}yrJK@JaYSw+ne70x!pH2% zsx6CSxJ65VKQ)InY8XALR^@%Q`DhtZ?&Vvp{EK*R@X@0lQ_oMO4d7(m_c-w%ms({v zCn-rXb({B9wSwa$kBg1AIqb+~2VBwYDioO}8=n44k<ck@6z4^dm)Q=&<>$r27}|Zm z?*#cC_PPdLtJsn;%tOt6eIYkmKgp0e_>ev?YP$d6K@_Rg6(c<_o`mz3a9d|Zt5!{b zytBe-ZehsRgkv@zxzhgd;UlYOSHNpqm0}&^4pmE+$BUf(>249CCu0si%;&z6Dyxz| zCGs?lX=P^P5b{>=TaVWnZO0Slp_yB96d0mq^M+TDwPWl2zt0t}%8)pR+qzHSt)o*2 z17^oopG|>uJ4%$*{Nc^gTM5ix&TEIpA`oO9%d%VH`y8N&0L5~Pl)jSujGT9E3S&+L z3w=#*SC6!t-JTO$jpMtZIUf}0bhqw}babyqF@ZP7&zfStrKUyV^Z1ACU8VdWTaip^ zhG|;6X*eS1QLBYf>1es?c(p2^Zn!mLPk{*dF&4zizA3V`d-umTcwFg#v?kyK`yA_T zZ9xpfDV)8h9C2t@8tyoS@qej$8cZ{o&F`mi9fBC^EWyT_Mw2(=&cplnrwC!+<qIc8 z-ovHBmlNf{haO%(s{EM1)%<=@v(l2D&ZIW`((VrqKEe?-pXbS){KyB(<X!|LQ+L<) z8U_aX_m!)F2PNR2M4L~ZSALkz#ZSh0KhJ=#4DaPwe*XxJ+nz6Uij^I!9##(F>$(pU z?&dIC_TYr>vCN@-Xis0?iUc7DlmIX4JE^i7w(i{`wJS!JIC>J_cst8A%!*Sz84W+i z*2i-|5Cy)?hz%WHq&tI;1_rHJDoy`(8b9*Ml)c9`^vJQAR&as{0$9MGJUmoa-u}Zm zO#<_2F`Le0ekF+q0>}X1tAx5}qUOm)(aQBC6898{9pL+_QVQQr-;_!@&0~>wYc6-k zS>?C+eA~#oAX%m9vOWuVEf(5dD9R=eb6#f@`L~O$g9FH3>xR73J*O3T*STXDE+L$S zvGSiwM9=mcUlhXt{*Z00ExXyxZvTIpSDkiwtl-^zZD-HlBi;Dg6h1i3^1?S*MP9G! zhW!<dJv~Tp(xXDQBu@CpMa`$#Ps5VFb$Z|jp|9zVTu?Y#T8$8Bs8Rx7hI^vir&?xX zNmzo``-V}d?*^xDOw@$CI*soT&0aagc}1NLA-ugKBzipXkb4Z%F_?uRBQR7T?D*|+ zyUH*n&I<+yUBYQd4U$ddU75n387&0?oNd{o1Q{4YUEqzDAyuLpg%QR58!6cm8oocP zniD{N2}9pn*u>D@nG{A}iDdzAw%=9V)T7ODMBUtQ+Ftx5LB#;P3g)~n0i$B+F}1>t z=bL9l4L8h|Gj%^*x_ypo03nq580gW!qaiBlT_xX9q}o{|S)Q)mj$<ETn4BVI`*8x> zpLncN=>{XOwHbfjc-5ylGDm;t_X_0~*@8u(w4;?u-&&0)Uj0{fgBM;6|9$4N26a{Y zai-|4WZ_3yA{0S)>axbWrs;<w1-w6*k7e(ykbIUeL*_xFSWo8u)bI^EaEcwcQC9;T zyXK6BVB{r8jfEUr0|?-!_7@2pL&aNSn7#KO?5<R<92ae#k7ay+t|7!`<sOW@OEQ}D z-T%w#v(>V$Uu!za>f!AH@VxQ+#o^?Iz;O4)q(*R*u&tas2g{p@mA;$KdA8s9x?Cm* zq}pc{+0Aaxj~y-Nu1t}?ouM?}AGQB?!%!VVM1NE$1_rFmR86I}u1yh_Tc#cqv)f#w zg+Em`{9N*Mv=F*hvnrY$UZ=-GNQubMtD@?(F!H_>FZ;7xViS2a>$BCrl!+but><}> zSYkH!l}sgMDL1W@ncTv72dmU0SGe!2$SH(VZK-9#K>A}hI`8*Bm?DJjg2@hA>=<gR zTVzojb6TVXoTkQ+Mmg}o!G6!n&G5bj{#(*<&zox2b=@Br7&ubhv^tgnle^W~4J_^L zN!RdFJbbXgyJo7<X0zG+TZQVy2>RB1E~-`<L)6p?b@0}ximO|l!gbGNI%9|>qMNC- zj@M&Z4!$(tLk7OYm!j!=3&jPjwsV4x57H%T(i*xf(u*N@>DQGv>y%xN81j4RER*>` z_hYSTcx~$T-o<Ge;7{C~%bc5}{485+du=bJia?SA1X@v0{5VU1v^e=dFu_Mi>lrfe zB!ZJ<aE1;}kr6OGO$Q!CaDoK9Ao%71;c^fp0^u^`4{QSnSAZZP2<9Tea~}!_hLLwz z=!vN@eP5#9QLGwyRo_YA4T*>nxQ23GTYFERt=r(N1lfQczy9#i;ISKB1ohaRn|lIx zW3FsRwk^)PGJ&=_yGeV;`n?x%RdbT^@1Fa^&w``ZLpXmA?X_}+Jx)FppUb-K<wD>~ zTN0=E_1rTiYS-p+;cajhv)zuocC*{_g1mUOTciwjTw@;py-YkEBl#|0tkM|AJEas# zt20(iY(lsjxAoraD53W??H?DOjv>R+bW5--{~Ap+Q$NR&frX#SW*UGGeMOQYsK%Cr zY$8#&B1yidOzeuLP84nStdf3F(Q#dcXanZ4joS;=IOfx=C2Vgdv1ei=R}_enMhdmr zv(9w?{uGgy_<63(0WAVHE3CdR<}Znt0WWF_brkZJ^9`GG<lFO9*Tn6-wmZje+~sPz zgc{RM91J~_B@wi3MNN{n-bt5ns!Hw>Aq^u=X)v6Bt@PWo4K64qjHffhn6JceR>yO< zqzb+%kgZGPIR-Ol#z<VEM6;vivm&KPOEnC;K9mJ9!Y^?RZ)eC|F#_ih9ykpxi4ew- zk&>`al9(SAx3~5@csYu3x>`w)8)a?g5xX)1@TWR@lh<ZK^OKaHWJzo<Z%2V>ZJBOQ zsg(}3T7&u1JQ47xc;Fk>CG&w7$x;kVg1NR+1a)yVeQPd%cd_`-OUmzxWiNzKmqpQM zU>UC@G3LZE02ZlaK2R!~7tR6R@J51S;k=}wD^|?Vf08TPnK`P<JCxY^jitH|fYpg4 zt+2h@2J+_dZ-NjZI8~=J_Iz0c?=#1Wx~xuMEYFaPEHD^v9+lEB$~%|_>)-gv^U4#H z`#qLTFES7?l=(%0a=38O0|&fr<nV6Rs=MJm$);S5q~*@D{l?*)*lW?O3|gxldF^Jm z=fRL>4Z%&MBg20zz~w;A3u5J8<%xvWkT-(R<Qygms73TQXu96%Sbh}8bbN%6pjVyN zA)Mh~s|~Je_Pp9WgoYQSCYZ~-mZFTkZc9QopWeDSN%3|r_qBM|j0r-%QMIbCD>|zz z+JOEw>UCL4Z%dT5uh0B>j`%oUgGe6-aA#Qxv<msgeHRUUjd3d11V1?F7Rf}&4%!y- zmNIo4v(>;8iY2x@E&UxF+?XZ?zRVY};+L?DWM<nR_%=vx?Ca~<Tc&z1h5HRox)Uem z8Yh=x&Z9KXi<0?Wol>5+R^0yWIgPUq6?l_eQLH!8)o*6N*F^JnR^$7HEnCt>AdtE) zU7W@+7V+A5q;Y{S{q+?24ULJRGI|nJi(;5Qmm8O-Gy)&~n@I>T&B4-^#0YMi|2Mp0 zNTD=`Txn#Es4oJC@G#819mn5OD_xkR{3v~r@pEq_^Va3Icvf5Q4fm;OT$jd!N%!19 zD#Cf;jlpRO@P&XMby^^8X*g?j40l~3|Fsy-^Z>~Bdb_G;)DG(FA_;N>!vq<oy&=pe z$jjDr?K~?3OK~GV3r+6Wm4lS)!?>1Muc?!E*jV=ancV3i?A5Wt+vdLav-uw7W4m$y zmN7d;=ygpqIM_eX-*5Gsin*rx=0aB?+xm2dP4TQf_?v^47H0L$d+(NYfkQRJTf1`Z zyf9i6xvfUh3I3{oFTbhfw)xq9=CxeShiSYd$d0^rv)l86yl1&>AXMal9xF^oP;@L! zRDG2rvdVeWX|0ZE@%I*GgE~#mv`}U^)!=#Usq8}vQT1_-@EDS<QwCTPOcvz51-xnN zv(-0bwme=2S?3lbb&P@*gj2U?i>$hfGPYs&#jYaDxS4dC{?md+oJBMv32J^JRt5Z7 zGs5}vVjDioL{gCFBt{>`oxz7w;=C~Ou83zjgo+)*sWv>-=Q#eNcsV#t4ZGGf5_#W9 zmpl72EeJw=C5ru4n(}D5xR$B;I7_m(Sn6@9=|F`F(ofELg>841C8|y!wart?^YZj} z7Gro0e#|8?%yPD_Mrhz_jY3_Q(cCvUII3FsbcGzgJ{xj0|L6Zc8u^`uQ`?STZrx!K zBmsfMS&@8c>)nIZhO_+6N^WcO?Wbh`AVCU~&G0%y3g56KQTA=F*!Dd(EttL}xnWzb zHPz*Tz5`{7!)5Xsx$&I@0XR{=A(8X@Ir*pQywB75Z^yG1h0_2Ek^V*^&z__gGYx-S z8kJUvXLg>c)%{o~aq@#e5CixR*EVNE+V^rr;1FTt!Dp`Of48(I0pN{e>bSaY8!Kl@ zb)V(QUyc{I-1^VIDz$9GSm4`+6E28ke448`Qq{D-qUmtCYGn-j!yLY%<6i&3z|b<< zJ9_>g+%4m^jSR><Mc!og%BW$XX_fKr#*&-MFwDngWBorAi1w8#zr;zvN#Z+MjRecM z-e)i@LHABF|Hyfz9eM3$x90?TOL!d)p`xWRW7PoalwC^`l;7uzbw<<pS6xuthF>+$ zB6Cs9H^B@jg4TKR@>9+Wadn^N3VjLMD6&40KD^n^<r*!M2sFHATg6UsbxkuOB>+Ws z2_OX$thYkc$&Ei>Y)^)es@eih$I(mhVf|I&_6^x8;6+{-&WWnm<uO||mc?hY+dL_z z-YM5M@5&Xx`-(-8G#j33Z>jjjI2nw*fwjZqnL=-9BFU|}+;-T@fzy`YrzdfFqI^%O zxTkO6mx@MLZ{n{N>RlzWa_;1{UIugj^Z@G7YRwd<1n$L~9Q-IRMDzS<dfP{SktYTy z-l8z3vG?Klb05Z5RNahFwz94FP<3mS>{jR%HBtR^=#Lvt3W6wE%yy4D9SG(9gp=65 zp3brCm(OdqThq;k_Z5HPoBlY5R0tfdRD51)tl+l54-%vXrqqaqG~X8s7iBWnB`|jv zNR~ti-pvw@8OAh(Wpy&+t4T5hc^^kA4i)oPCU8e0??B(c(F;wM*;0cEc+IXd#Wit< z?Uj36(zylFc)IRmms{kW{~2?@(JJYy(JXL=1AI8ZmjgWMz=!9Ak*&+t&JUySDN~p5 zJC9Y$w-)O5o|OU=bf{eQE57AS&C{M`=QH)66%OZUuFE^$J9l$+3hzn5_L)l!tKxVr zArQRl*qE&rwmxgLnV?#ez{67PoY!u4dtQ*YfZYyb=&OliJ0&6Yt(Ov&-{wokBQFAW zyiS-Y!(;@ZpW-ejU+C~9{jMs-lb`k?6wy72kFHZ6xuo^Fq&`rkdLu`c%4`Q-l=lm? zQbUi8Rn^G4W@E*`myD2|W7j5H`8UN9AB!w=g~a%C1rpF+5&}7eOF%HkJ%KgS$rK2> zemvj8P#LFqP4*Xxkgg(|6;?~JOb{XfJ~ZD;2=YQ|lY5c^__7ycm`*_u@Fu}e!T)?O zx3QY7gMI$~ipG@~#%pohw-VS=Q{SY^Fq#LZ`I3LFRQLBxsU%z5-S<KScUlNzZRR8+ znSd`5{_dRpNswm3+AVN`08Z6q6SdzI$O%GI8b!w#F<)sZg1qE(n&CvP3WRd^7RzjZ zqI;3lwW`U@T0V3~xpYN5H@Y5)SA3Ph-BCQ@Y^Gx2E$=H6R_B&;T2`m45abOVM&4hF zxUVE}fyZcl6;j*bQiSuCGjy-UbM_VsZQltWrt@7QB+2xaJ>?2{*Vu1ubqeE^B>wC1 z><y{BZMj0!bzLU2{kJkXI9(6ZbKt`S{%jD$K}x@TA*A{%hy_B$vqGN6Qh>+xU#{O9 z`e$-|)29_(FwcGR>BzK7KPosw0;i$XF`Q?{O_<%ko5IWG-LxaG-R$<9Aa5bJ1Lk*M z<O#-e-sMTE_fmM}42>10EVu=G?>QFETchfk7D!v0E_)}7|Kz8=vr@h#U#2sdc9+RE zr-^nI$zF(;W^+5?-TS6=>A=7v8><Swte6(c4<u+8Ml-+26ip;*^ks=^uSHfmw!V3K z80`PW>tb0*mJoJUA=FS(d&qU&W}I55>xrpvK2?AF;e&@$ye5ZBBna}NY(bb~I1Pl0 zV0iuItYpw^78-hvR;bowHi2LP2%yglXKzg9x&}}-XDQPN8kNTMHBPp_R5}mCnHfwe zmrf3*D98Z3>VGU#>kNjeESuhZcVVPxemH$A&ephCoyj~uiUWdpu7PB!sehs?usvG} zfBNAv>1$C;0Kh%--}nFL>A~NZOXs4YIAY7OY842c9C>GF^4DB$`96&~{)2Gbc7S4M zSVTVe6$sp;X;!tA5f~!tLdX+W=Mox__UZuU%*UM9yGr_Ix?rfT{&8e`z~`#|7$UGa z2%prFXxh&FNix*HljwkL*j^&vSFVuW9LsrA2%3GBjXR5x3;unT^iZuKkgPjeEzgCt z@J`I<y7rAU0SKg8>e(O=zyLmU5GMY${AmjSIC*{V)w_chk<uRuMXU1LH)QaioN@-O z6~^2-A@l`U&hjX(xv&4(e&#Bj@v9tBD!t8)ymqtObAr72>~^Ox(JyDkV>}+c`L=tK zYHON+AhbdiF(f^JvB}cG0Z@1c6lF=W^!4<onvfy14);`HrON1gRRaPz;52n%1S^}< z3B0MV$FOaCfI}rBr(mvUP16gJtf;!?iIDdcbZ>nk+rn;yz>f(NOyI)+p7qEGU|$;Y zh(Bvia?|If;`2h|i8@nv?^L7BJF^8a^17mJ4P^uVbpRs)XJ|*tWJ6<u;TK|P?am>P zOAvWuhOoV-7fJiZaXc%f+i}cgiTq7ToUaNL8(<;^c22qskV%B8)7OP6hHy%vm)VeR zQH)|n2z6zGtqp_eKmYSVz6M^la{w7KOoY7sfmgLoXGljcshvZ}0Ibeq86eG5&TBB4 zKFj2KT~K|66FP?SQ%RGo5X5ocxvu*q&5FM9b!FGADCM`sNOF01o?uBd!z%Kc&4XX% z@qVh5Pju&;sFEVV{{ZIPh~XSTHc|6_fn*Hw{#o4=L1?xhZ-Pq%b!oyRgCh~yjd(5a zCa+KAT$FT<d6p~GeP5vld#d0!+*;HC`~=`65#IQ1%@7|rr#ybKadQd}_){EwDNX@o z2OrXFsggt0N|5lhIS6>w?Y`W7zYnQESf9avHLvY!+*8hbpj-hH=g+c42QMh!$kAxJ z?mgSjJOR>eNaXt0sO`vWH@iJ2$eYJ%hdJ-w!m&j%(RG?RQ7}!D5A<0HjV93yFgDE! zL88q7OF+bFgE^bC5G4&9Uiy83ct$AiwFJQq9D|`TZp>={L4vKB{9UDDm?VCfuQA-Q z<wNqAdbcpapOp%?a8^Y9M9BN-cS4U#2vYf+47I!*BX~cZzdc*@by>@6Npkl@nL~td zR-|}&UfaI&vUEzzpI5qI=$+y<fuSGi=S5L$9P@RF9H4mc=^U?=jq2y^H*HSlxtt-- zisJ0WiC>GN|5D7y)T%aQicVcpy2tb9VCYW-lWkb$pl%_C{!xws$C#4PeGH^Qx{5-X z07I~GSpUOEYm!As3#~s10PAAH@Hc)EuYozQdkp-B5CGl*HR?+vQlePM@J0;N^MWF# zzQr+^A8l=H?kN1ivOcV7#o(<dPE?9ITqC5pJOtFf$q~F9&$P~Y&F0UtxbNqPCc=&@ z(ymph3J}D6(hK%&K7Uy}XB6^UDy)1FahSm!3D7Jo&9+zyvT`&8ax}o7_D%vjk1;m* zkPB%(EKveq0{n)X3#99_6nm=LKP%L(fZ2Bt4@v%dQDM?LGfeOUPOuis#cIrB*6$EN z`MUa+N{<wJp)V^o7T<g?jr(M^drIXXR5~9GEs9}mDCxAFqp^v+s`W`+Upw;J&2G;R z=Vig1SL{(WHeh&J+_5-L@q2}0aKH-kVo9w|;o{vmB-ras5Uz@40Ds=A(JUnFRxErq ztpRw^KFE+HSjN=utyXyw47oz%$`poEIRB%(#$KCovxGXBYH?nQ;(#;IP<yEr1nKMd zAol!x7OCw?XSANV(Kh6<4c!mCs^y2u#V#?7RVmCpc)iJFPJ&vzNVf+bPBq$`O4cDW z1yJOPJk|fl-kX3majxy-FN=!%(yF!ArD|PUyI5<jwc2W}ZSA71wR&o`?kma~LRi8c z0a;}Uh#(*WB7z7Z8=36eWFr|u2r(EjVoGz$;rQkA&*jB`2CNEA0#(mBe!s(eaUq6b z=AC!mdG6<aR-M_p_d-3E$>LSFDNXozsbpL!y!ey=W!$eP3s2FS)}@Ndc=}Cb(c*ZK zS6Tfq%-Kgn`Rrh(u`mjHD@jJEQVnL>bfj%eAQuF(0nQoniuLAYX$a<n#-?GPtD+SC zhirH(j<LTKnGwO7Ki|`I`}gLyN4C~YRVW-VVj0b}1`F`xMpicrfL|wTKg>233+$A4 zj;+-W3xPAZ81lXx4Y?8P!X69sdVDo@708?bB@4N|HXE4}CwBeA&pRiUH7Sw<Jo_Lo zk=pWAPgc<ZvSD5<+fjwIhvS!G#keTd`V8?;#f=5RmOp6}^kFf=7h<7@=fB_GE!Ve2 zRv{iJtou-L709Pq@^O*!?b-Ird;uKovyxlt77LCJhh9S#6<-H<>Z4n}s0IWW5Fk_m zn3B@`<9Yd$z2+*NX=A!H;Hq{Ad56Z(cutV_EboRJL9)NFe|elb<km|u%7Yi}SC71? zY;z3~|8!Q;V+pkWcvcDy)3#nv_tf4u0N*-rl0H6^d!W1zUt1W<93CoMaYohE<t$T% zK+(DTF;UnBo{Si({n+MKOUF-@w>P9<1R+~-%a?hxU&!m@<D%mBzH6;U(_lI8W%(_X z^SbyuL|(Jiy0=){gS^b|v&6Q(m!1|2O^so1P3CFLzwfwghTs;kwR}>nFr1<rf#U$b z!6C1X@==Nuga|x{^ubJ<sy4qE34;Lc7(%&|!wwe9UM9(bPaW{Bb%eZ#!LqMH>l(tE z5z5+37F%KXUf2D9-2LB&2kfjeObTMe(3{I-w*Zb?RX>2+8sMNea~i$!?Ia(CR%(U? z!&y92&tcT*vG#i+l+x)-6S$KdFGVt4^KmxvR)S<x#)F_Kzf#pFWa;CLaj~kWSM;lT z`brxNhGhwy0Xc82%=BuU^1E}g{pHtF=>3=LNRet$qI4x$;$$y%-oO7@Ve{l*`<{z; z<9X}yTX@>mD2jIRY0=nV-g}9Uc>OpWW%6mwvQ+r(MCgsPHv%i_pA>m5pQlT4<;W27 z4vnGloFH!z-0B`AjidHA7>W#*X(Z*LV*AKT=3sGI-(`w={%bo{tU_UES{SsWNV_^i zg?U=Bv>L`l0@H<9H<~E^Iak(gwT4%z7bnPGKP~HY8thaoH;=$!*bTg(;2Qg>5N+)p zJFb`)M`5nRLiz2VOPh2ChuJqdg6q4ln6$>WL9WTw`ZnOtpBc{W>gslM%$BoC;LSmi z_nS<it#7CjATnR+K@gU{{cmMAb%r*uxpZ2bFrLwjA+IlWFvv@xBri=8gFx>349;Mt zJ%>aGK8z7Tk}|ooo_)i^>s-U77&Z2Gh`h;ZtR3v@FBGT`mNb5qF6!2GComdwAFFb_ zsmd@uh#pein8R&$!SO1Tw+2AoaNfK!-9~#^MLC@7BSWR1rHkmWVNxV}>1nnD?559( zW3Ntg7(5nIUcVtnyEap?{*P9AixW6wBRKt#_hf}`T~F(!^#|yq33Nvl(jJb6R_lBD zH)n;_t<IK4)bywJp+%~<(&dw#I*ZY3tw|NFPqoi^)7gfvi(AVjhF!TZ@Z*Aml!Fz< z&OiJ+xI)3otfu{i^>4;PE6?97;2NInHTSF%&5h#5)HV$v@6Z?;&k6G82yYAzmX<5} z3s0`T4MpC=rS*1Af2SFGloc+E$K+%(Y<NFK08TJ4mU@^0j@QjPC7DHpu$n^td=LbW zj?nmD)1#b}hnPOeP%TcZR~emotQIHpfHxI*GXY*WhQPFYOh<=p@3lMMXJd4uSaoM- zX%kAl9i8wD+i-+-`|jYXM}}e1+HJs>Gyjyd)8;S+yQfW|1bEjih=FkQJ|b~GiSt4P zrV>b7{{B^oIlNM}m<XZB>s^722;jKk8IB;Y5ByP*=baY02J*wHgNm@KLd>6&Ma#0A zYc<Xo=rDYpD^W5$lnVkJTAQ~fvlgDKpOvk^QCp9fU_>dfOyOVhD7wibA93?zgi@Mg z>3SC&w?;ES8EW85dplG6u>bqxgQ0C39V|qb@tZv9`{}YbPIJTTS)13B=x-!&X2&|j zIPVh0=ednv=BPhE^Pp|V?mWrdcz!=WFM(qCB$3-w!-|d)UgzQKnC{eQeka%D?o~T4 zf%DV3{%OlQ^F@nOq|Q%r|Ni}7s?6`7w%_caTzPBL#am=5;*O&OJO>=3Z6GV?f5g;< zmrFj#ZeE)%+m<C-b^d0=Rn3#V=2a@wx-*i`vm|t@bx7z9jiK>8An(aa?Fb^AFX->X zs!_I1j#eCXfV|malN&+uLl5wUR>6x%9B_g;2FLKLXe2QWyNlsWsL90_ni#}bn=B$+ z(_4GgctdO3#_akf$r{CVXUI!YnLf)915XAZ2!WS<dMMDfZM$qfT8MSEOJN%hRvLPH ztJxjD6i5$LoA2HoEb@j^4ZxSXI6>KBaVV*L%2adXRp=x?Oytq~CjR&o%fHLUB2-1j z_Gxj7iQ&wpBxqt3?~J&0B#OL2bU+yxOaX;5G8h^jB)KGS9ps}-7U=z|w9fnQEGF@0 zlBA#Kr~sa}QD=V|OVfA&&-<}JO{i)l^f(7A6~;}uk8lS75;#WLnP0y*A08eorXT|t z6NVEZ_ekZaP&-Ogw#4LuV|awf!fD#qVmP5y_QT0gpqv}W-Ja_({g!Y=y*5*|Iu-W6 z`~csVCv#p)5=<oa;k>@phV_ZO-s+K<@nIlq^HqiEjqH|@Cu-hH;suoTH};nEbnDNl zTtl59Z>!n5yUMaT%YK8uYwBA+UN(ym69L_F{%mlPJ}#Jxr#yIwo_Uh@F6pP7kwjju zU!8YzPp<TdUvvD`MmIcleJMO7^oGXJcpgR+(>Odtn#Jy4SGHW*@?w<Yc$vnowYf}l z$2~|AQh`Z1l*}G4Q3GGW2>;rLV!u*(b6BYOlbk-uN-R~&hiUSqsYpYsGnXqJHhg+U z2t4T^R1AnLyT`P(ckZdVbGQVHK1vha@U6S)q{<@hiu&ivH?*d<L9Pj*S_izLw^Qod zI~{_x-qgM>R|k&Je#wLTf!zkOXw4Zu=<e2;IwwR4M@6&NCi6VPxjFEyQGr|#R0l`{ zyKa+p-3ub15y28_WBapu)>tcqbn&7%cz=l&tEb@rdBd4{fM*{pQU_Kw9lwm##w?Q< z-%AnD|FGxNQKA~c^T1R0=D{P0Ql4ReY!`s1k2|Gam~1C`mW~*3*P1_0760QOwxict z@`No87`=6MhCH^~VG#AQB+f_avcDD7`&@cJ4Clu}lVW+GFY=zK)UP;&RarqFfk!Z7 zK;$jrUw=K@1ol<^oGba_Pt6cTD$|x?WFiT2at5o-_Ik2-i383H>DpK2-`H2Inn8rH zz-+8qH7ko_=-p8pNUXV6wBWRmSf+aG?2Y$Q_)q-Y1Fto@VEzTlki;7rL*scEYsjME z5wgTTnKa{Tv}1^{zrD`v=qKl;w}u5v<LONgjCfbI;3Q|!=|_d{3dPr_#>*%7ElSzi z*8bHw^{er!t4`}S=fH;5DME0JJ~c-ANw)o32lwyY+jaHMln^ZOC!S^4S81?zIxGye zuRy;2k_j;m7J0wO6#-ue<-BsIy=)F#P`TjvA=mmZ(Y#pB_$W*o>u>-5TITgpL}oV6 z<c6=?enI9&5WGYZZ6%cs%z2Y_Ab>U^SX!;NJgaBj-EG@{*)Ttn=T+GZJZt7Vpx#tn zn~PuFfkNe`OsQ8-SkAXu{5KPXT@OoxHnc6zZr+wF_%2K08pMm|7@c2t`DyNixccSs zJiE)--f@>M(skdr;rJaFr1qWERVvHZ1!{`pDr6{uU6xh9_o62JkDJ<^Ct63AOnj`O zS7&GEu8R#nWIqUDawE`yAJh5FnMmu~=S;)BYPaTy-%09k>(-gf-=43JWDZcF;>B?2 z%MAN$y%fIrc8+OvGDL=S?~@U5lmh%9uRl7EM3>3mJ=cW7aoA<$tc>d*g!#nC8^bV+ z2^O&o?L!i8Xbg?#VSr$&OBnq5nf?G5SEC&p276uB*hOA4uVqAtv;@A<``=MGDhQH& zM3z28-ZJ^E5s}j6Ieo%DZ5<sy6gEtYQILD;Ejt=}O5wRNY?SkkjgZVsfE);u`+KW@ zozm0PAQ93ZuDtHV+U$Qty{W)ZCNd6EOqI%8fe&lmX;p6*21nz)6fPi0;~4z~?^F`x z8iK{LO0T!i%`$pKvhY>9aYWX#WR44-za#-B4Z`S`+^u^#5*i*Xsn!1a%#gRMb9-UK z(pX_I%><5KbL8h0v~;)!uzfG9zamRFq+)f+=8&M5Lh2sg<|#_!mSU61WQnZSf<Vsv zG@kS8eqX3|Pf~(Il}?xaK2B&YgzaN%vBc!V?{H-NM<@153};E2_&^DAqWD1)*0Na6 z#%$QL>d{hnb#)P_rjL89%^aP1+$kCG834<uD$~-emI=PJfXm7cQ~Td0ou}K7tJ++w zc5dN+KS{WiY(K&Z(znM+|JT&ki5|A~g4)#wLRG$TVRgNZfNi;=S&3S3v<B4{W}PuC za6p^~v!z2(&U=!xtzfVXIR2&MKOX31wRT$WcIWa8=r;I2D!02I4OAoU>a=$14Ayd< z!JSA41p2OGWP2&9Gk1m!jpubNPT-7;kgZJY|5EQbCmkLv{iRUZ{U<JoKJ1h_!-x<` znXI+<zdshJfDiBUbMlACn*p~0qL3`?BS^b#cTwbBkf@e6Iot3SOXq<L3UG`DLd3wc z+R?^alcfNMuVG38Q|DcK!-+`QTcG$fSD(w$4suPRSw`SpH#=5Is&N+60zz0ILN)78 zi7+6PIlhPN-)R4z8A+I3cTTcsK|BwC6#;?#*ApeB@W9Bc>(;#*0SyZhRcM}_wYjTv zM^WRvD4yrlCYJ!}MhBc%b^9NqgZMjgq%&f8hkAN0A1ji4f9a;F{ef=tI*Ikk*~SZe z!;uS0;LqGuIzX+Dc)EUMME&MGXNk$}ownn&JK22u#*nl+{V&(rVZGT=L9>~W?2ppK zUuDa_&3wQvXOKh_qBzTv`&gUt)y*C_tnk{c-~VTBdXu})0M=Kt)%HRDO>pq)8Aj9E z)c)G33NdZDfUL+KpjFd~ROwflBD*v1?CKVoAG8;Yq#$$RmB5<?d}+N!PNv4QTtg*` zV)&IZ)AR(*3t>;rc|XmPxSWKxmfpN~@9w`&nDion$z(2tbj#wQwYhMi`gTZ7<NQ<5 zFal#t2*V?g0ghJ#Z`!^~nnFoyzN8g4-*fa#dr+mT_unem{7mo~i&24Z&WjbUId6I| z$2jklY*Gk2gKxCjtKahU*W$PQp2E4gA(j7H6a)gGbHXPtoo%t%GB~C~7bK&IOb|f( zI!6)8y6t(@u(z~n@1>?63y{ru$hu6`$S~fpAl`^z!7H)wwruIoSB>4>wy>e`{EY;P zdL_BR<xdVTLb-Aj5%#WVu<PdytJZpiNXvQ{3i{tMcqYI})@Q389$chqS(GTpm-nH} z9UbkP&m*(rRAu7pPNvk=*}3z)3~SQr$KFvObM#`qcSiC`B(I0_8je$1oFMPEY{}B} zhJD3@4KImDk=KXya*X_Bk;2JqH8qSq{j_Ftc7G~_>YVE{F^*%f+?x<4LY0SAWYLre zXns5d0(sw@Rdx>|#B{z+=Z2@D%FlUT%d-HMKxw~2Ufa&%rdgrf!<V(do8pM`N^kvR zY%qVpDbDZ^=y)-Py#B?CT{VAK-TbZh#>e6rz>hl$&v`Y1>xyHB(+B8|R3^VUCPMjj zuCqcAD60N&Q5Q<Lf3ue)Ez7fXhfCzU3z1-|q2HFkbiX2X$km1)a#cQM4<fAG!&z&| zk}5j#_+itCtlmr_t=T#|6&XVu0L$q0=C{wcqJl1o-nipZ|9%ZB#Ju%_2InB<{Lwxk zvOGc<1BzVi$Osnj=MVRx^32!{8AD>Zg@_kK@wBF2=OiP*tM-W>{jy}v$Z&XVR)grN zSspr1bI-j<3>JB7X9`36Wd{ERVx31QV^f|anANteK)v>ibXlr+OJRKg+kD^>GM2!c z9K~3Yz<xOfLbt(z3e$=V#3Mv7GDx)IjB4|FWjIAG(Y6W@^S)9Q@Tzk4t36cOfTx;v z7wb-3y?&rfzdoz}M5S)^88NDs%p>u_D^*phwpL5WUjj1p;0w~53*$92BBh_7m2JzD z9lEUBezEzDEX~%E>#Gw4TXW?sr1j|`xKw1^eo?zMPc!4Ra#6BsWrpTRRSORKm9G1B zkSC{zOrcc>*i#jGt#NB{!=zxwXX%n3N{w$N@ivs2$A`1r2n=wndW1jyn>68Tk<ce8 zg1zTuDUh*Tbvqq0#Md@`lMPP{V7?H8oUG7VpERbu`(L1tJSTRK|M=jjjbG$70neKL z)=paExG=d_d4pYRGl|+Xj3BH~KWJM9d?>(+xAXFihpk{yA%C#E<%_gF<^F88yFV47 zr-e@z%bY(H@5>sD^ZG!?%QTLVcX||OT|rCt{jPL@@ffAWNp%ifS^er9U1YUtkZTf3 z(F1?()M)vhJco3~uT1rFxOmIO7PF;)Ip|5Dbz^(zi3vqsqR7LKx+YCD5*5GU)DaQl zZF%Z$>%cky1--#=*6?6SuE6-Lo^>i+{cehCaxnDOIR!XT13c{`Z<*9IE>r}(s2(BE z*J(l&Z;Ig?JFD7@r5G%hON{7AFumwlE%2hb1TZ%iiJV`Tr~hp<5#F2wJ9*tC`nJtQ z4F}5YPf6KgnGvfP7sv%(!u_TFw?uc>y~CBp3VEwfh0eeH0rI*M=$~dwVc78ap4nHT z0Vk?KX?JVK-Iq_Rf#(3odsS*$me=YY!pIXDwwLt3E}}DkaaNfkFgZuw+0oGKSZFX0 z=tQLkgeY8m>97Uc%+upJt_1!&2~wu^&MT*6PlLP(EabI#*_>DfzFhTJ#A6qPm@x?O zr0gt3&I(&TNfl0wX06Q>y_3NEvDmQXykdEV@ReBhMzSP~(iqFo7szj?if*sTR;?oQ z5S{7IqPzDKLX~Pmj(lgna?UB)h-l=Mq{er1O<$C@tuDOvPX6tM$!c)y8mc4h%9E{6 z6?_oO-B&1!r0TvQi(LI^i%-k;*8aYu%DN;;1&-FPOA}p`-io9fJqYwYMWP~>&XE$X zZR_|fyB>IPb`>cf+G=f`omJ|#&&ksHDXNX7mUpvJ$p!)c+V66t_)48mx$ZBakI7{5 zt~HFoGu}CSbKxmIu0pf32qsZfe_U(;1XLN@lQ=DN;-C##@@?l;AEiq7U)FCtFaO{S zKapmDTP)T;rahR+KsxhhWChBJb{A=Nt(GUl$DW~k;*DoBn0rsre%ID<@BaP!kF4bX zSwsDQ9C^)V%l4A%;AnrN?!f{n%4+u&*{`ILC%)x|=V$XCB)k9|1^Dv6DQL3%;fd?E z*?zi+^venAvfbTzvC%zP@+H|>lqQOyhl-S#Y(K$>bsW9|{3x5s@1Va;;NIL((qu50 z?Jlw}-7q_l`CUO1q93fbUo3A0-mJOts!XOsfAqpw9w5lqkX3)4-fJYztVpa1qPg?l zi;?h-^Xf9S<%KZT`dkt4ulqVf(mjawLntA237`!Nl6aM>p4GFCE|)JmqX9>2Tya<` zD@eAl($r|oV+bh5(Od&q(<7nz^EX;LY&)uc^Xw@xN>`aaDQtE1gw|!s-b)ey9AjC| z0E$Gcz~mmp3AyU5h7wh7+EUb1?jW43we#+)QGDRb9h<CL64O82z5CwX164*yZ#j8I z`^6s-3#i=wI7^z}m-Bv~hvFBeWa`boFUT~w4^&B`LfWz{@8+lw+C_nJ*Olgeu>=N- zb<ZUou4;g)jBWw+QAEaI9uTn#-d?Ew{(`d9ik)x<W^EQNj^Wjt?#xSW0EerdM%qjc zfk1dY`l=3iq`JlWR*HB|9PjWI<9GSmx6-9AL~_3`&;+t?AHJkspD8F7G$+!VGFbX5 z<Y^Ox>GGD3&nefOkxnGCmL>3x70Y=BtI6W5xk;r&uWGwXs6uvKRQgvN4wW^2m|p*J zreabQZ*iP>Jz06|YRk$L;h0dyu6*f<3vx0`-+AvoidmINy9-|UV}aZ*n=rR`=8IZh zB5}49t8k24o6a{3BXZx#Rv);m-g{ZIC{eIHg`Y;(NsYf%%5GY1UAFu8&9<(GIAXAN zn%X;mE~}rD)x7&+eKOaqGyjFfN#GhIYjt?u@B1%nx8z9HXUlgN$#dvBbjv*rOh8EM zhIDvDfcS?TMU~1KomW|HxJ#PlDbmHUoUp46u~fZpscLzOU{rA3Tj`>=QU%^un#kN1 zLbZ-qs{^>I`H_?=k-_c>_mm@VCks;R4C~17#3adZ|GJ5R%moScZ>H<sNJU-_VUG=D z0w3CnGqQcfNXRv9p}4hJ-nKRaUY;QjrWyc&0(>xUH7295?-Y#Lc~Tzy;6uq0-P({X zC((`hk{czeI~SzpbncBD!41F5^@K_!wyx!z=w=MFB}dqj58oyWZlpj4-*WYr>B6Nk z?8!vN*dX>xF@l)7rvEJRqI}^f<u*7{+iwjEgvLpcaz{0+@`NTAJTL8!Ea2UFl9!|4 zMTyGj$7*4X$5W?9OAT#zA6KFGls0(;OTIZPb~2@%rSP0+2zW72_#;#|IpVx?V>ut3 zyKb>|X2a&cl{T48{r7o}$n;ir!{lfsI8gijdBkKMY;1F$<Q97A*|Eyl8v7NA5liQ) z6w#;<$&q5YJ(Ym^UJ_QWAs;cjc~VKV=0f<^YY}zvkik6w`Z*6C%&TPX>>7t>xZp** z&Pkuuv))eT%}r2Ytz&zU*B<A^0?ZJz2cCÍ=N;b~wQuElI!k!zgVqfT_+zyH<6 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�J^3av~8tkPQJRDuI1; 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%0Do zab57B!?L7PH+d*om+TvaN8YrqV3IjD3+`waz~E~81}u8U?k?o1?%f9CM3n!ll=GbX z*)K=gR?!@8J|_O^rXX`iKzOv|KhmM&Gx=ponp-68^jKKHbAX7jUSfV{ywmAz5!_8; z0D7JG%Qy7ohZWrb#C=IEgjX(9^ffX62>z}dj|U`T2I7NPTGqxeNeL}}g|((`;(_4@ zPcFdYB+V=Rlx#L>Je%l#42Lhykz19m@Yltg*H5b<A<Ymadzx!M-En2KY4J-LzkUr5 zna}2dNJJcNKPmVgiIke-b*YGAJCP_fmwqlkMBE)L5u^SOF<oZ7nl6PXg05W=d%hIo zKUQ)dizF$3mb%*gkZg2&Bm{)@XxTOIqNIyhx#=FG&jtLibD3;(!%31dJbYSqZ=Q{S zyix==g-$y4&GVio$d1KVSg>B%&`Agh)7q=Dbxm7svOi&DBEIr@Z^g*q3!v>5rB|yu z$T<D?bI*oJB05c$#ha(|Tv60WUP=F_4Pp3WXmM%2M`HLXgZjPKRFH7z?svkLpt0|# zoxY8?LWu=CgyMqYlFCY{7#|ZA=`HQ%9XW+Lv0`Py=^1~}Ja;385eVhAMBp6mg>Z*V z!7kcN9=~tIC6RQ@k{G@I-wZ?~ku7c7?o8w3r>O4^t|da)PVKv?)KrGFc!&x?u6Y|> zU+iz+2=#>*mC3_YlxpblhldJF8FqfTxH5lMVIs=e>?2Yr{yL$yvVIW^^77}3<GAYS zDwm2vH%@<Q`1iV}d@yf9M7Ztw_tr%(o1?q8g!8Q`QPArdhpQkMn`r(uh!+xcl%|YD zV!K9$wc2Yu3ttrW3BxQSGO0?h;B1``pCNfCU7(gJAlHt>99A>!mI-zg+D{=`UB;Ny zhd5lK5cPQbH=JNx%jYWS2+k1;W_+0SuN^04oOQJ-6sQQ+n)Yv7Gf?q$(>m7M-kI0m zjdpc2Om<R3&Mq^pU=o)dxbGLw1SHM;vF5M6V{ZA&#{GBw-&;Q#m7to)GZrGi2udLb zJcc}RG*m9>lQ_?5FV1GAz%)w?YjW+E8a6C0XEA0OMyIPGa2xiaND8=quEk(32^h*Y zeSjfv=H=($)p9ExJlRs1lFea5z_`+ME&1_friVggf#i<)y#CqvW~LO^@`fk4MUm}{ zulaBB$IvDN3j*Af>SerzA^xWEs`_dTPRw1R9A?`tA0#2qkmGpe_88)+1DWRe>2zI- zpz2{wGhs$)cvaQ+U1!j4-Wqn-P2XxWIvYp8g938L=WTY!YwgUZhsrPNRSr*xQ{}$= zEoE*tnKf&kwIg`7zLFibFW7i3ef<uG;oS4bkqeU^_AR62rH+S@$s)VjEkocXa^0E= z?u#Z~lfn&qI}^NZjWiz~0!lsO&we&3nI#07#droXY>S3Drk$+XFDen~8!q)EfqFl8 z>j#rBKB-A)J?hH;`y;;`&-n32-z+6cpCnzc^ZcKPIYo4uxp^Vl)FMlnlG34~GKxuR zo22QQonHmmV5w;RQA6@6bxcJ~SWYo{qQh-z%7oqi<g{0ET6>XbX@CETBcRaIuMl^? z%GIHb@9V;XR^_(-YIsW%`AV@Hfk21GL6(p{Fe=5MKI-{*l5fRQTA4fBvXdW(gwKiO z$~5-48lxT6?Q&|1gdA2F)RN>|Ff3R#oYp*by*uk^E-vqHs5)NW!611f5z7Uvsfs;L z;a|ju#mDxUMb(tFZ8|F(ECR=R?>wpnLs_Kv11tWvr{UW0-P*t}`u6~tJyTby3J?9* za~E`)<Kjc?AvIt(-PiKoTxTNs!kgtzHMm4ChF_}GmUgw$_#Kw;r_4sZ8c$AM;>arL zRU+N#VKd6i{7t<Db!4s&(%BrHi~SXj&%~oTAb7Rom3E`IqCC(j#7!~LZw}GZqsjs- z-l0h?-~ocbW|l9ASV*h5JWlf3TMmV_j#c%{IO{3yj-slkcYU?>Q+_o(*zR5Y#KkXi z<r2Q_U}QS(H%UxTunly!3E7&P_b>h~p8}=dP3%!oQ|y84@lKq+y|NIpjLfR$ccvrO zwq8f(Vtv~e?@UV^E7x<gFXry7?kfBPQ&yEV9^2W@8ja7fDL9p!2Y1|&DEYT(&KOk* ze!|>`krAP-PFFW=B%ZDx?VMT-Q=VU~FP%VFO<ol7=}$>In!+Db^gf<&-WMtuv$s$< zlQS=R?UWnhv1KC@dyR_A`WCp&7!SQs_MWX?2Uk=|iF%m5k!H1{5-$x3RP0DFulxMX zWPb2vK(uclVsKxCbQ`7e5nzfMM%nG3Bp;`TmU0&A=>P3HH0=)oU7<+Rf^nXgfC#37 z!3p<lS7*CeSNyU)n!n+uoMvs2Oi<4LDN4*kjTZdm$W;Xf%+%OavUQ(YU8EYw-ieh6 z;c1=1PwN<a9><?M7>wO^-R-oe*O*{iJ$x*z7c)Lq()OiyYM^IY60H(Y1GjazIg~*< ze%D=0C_qW-uc3?kfU@Qux`r-h=|<w6O$FcigW{nJgIp*igWcwu`0h??p>;K+FJ$@N zPhbzm3VbrKJ4~vos=;d<FXseg!xE@l6q)iaTbn1_nq%Yh!5P9b%<<x{)at5+7f<E6 z5Tc3x{2f50T~1qHXZ@tv0lH5}?%ja6IN`@jnYUbjqc*;Rdaz<!LjuI>atUY@R2uJ4 zQB6QWHzl96qrD}c8VSw%)KsFK%>@Z#$!j)P2N39FELZ*rESkL;Vx9s<Umk`R;8GNa z)=uJ#+B-)ZE&4+bieDndP8Z?VSHuFb{CymevpW<mGgNR-?2F73{er0l-yorqgRoh7 zHEg1BZpP0^^_a*51m$h$bRv>6T{`AJ%ZkCgxdj^vaxl-a&qB}w*Xy(ZF|u&qk=Sq5 zG?Z2{sHBKYnDyS04`ku5R?B65<#I!Pir(CwlDc5>wdCDFte%L{zQ;Iz)zHqNo$ZZ| zhIkver{igwzN=<*zRKK>zr{1P(X(dZ{%0AuUsH%nQl+1%kV<yMk}+LMD7nBIr@U-= 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%0DrV1Aw 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 diff --git a/mvvm/tests/data/png_file.png b/mvvm/tests/data/png_file.png deleted file mode 100644 index 997edcbec596fb84fcec83b3d2ccda23ecc9ac29..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 119128 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 diff --git a/mvvm/tests/data/text_UTF-8-BOM.txt b/mvvm/tests/data/text_UTF-8-BOM.txt deleted file mode 100644 index 64a7a02dc46..00000000000 --- a/mvvm/tests/data/text_UTF-8-BOM.txt +++ /dev/null @@ -1,2 +0,0 @@ -simple text and -some characters like 🌂, 💖, ä, ð„ž, € and ∰ diff --git a/mvvm/tests/data/text_UTF-8.txt b/mvvm/tests/data/text_UTF-8.txt deleted file mode 100644 index 26168a37cbd..00000000000 --- a/mvvm/tests/data/text_UTF-8.txt +++ /dev/null @@ -1,2 +0,0 @@ -simple text and -some characters like 🌂, 💖, ä, ð„ž, € and ∰ diff --git a/mvvm/tests/data/word_file b/mvvm/tests/data/word_file deleted file mode 100644 index 6174820fddb1ead4ab4a5399319bc6bb465178e8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10220 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 diff --git a/mvvm/tests/libtestmachinery/CMakeLists.txt b/mvvm/tests/libtestmachinery/CMakeLists.txt deleted file mode 100644 index bbb90e51a78..00000000000 --- a/mvvm/tests/libtestmachinery/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -set(library_name testmachinery) - -file(GLOB source_files "*.cpp") -file(GLOB include_files "*.h") - -find_package(Qt5 COMPONENTS Widgets Core Test REQUIRED) - -if(WIN32) - add_definitions(-DGTEST_LINKED_AS_SHARED_LIBRARY) -endif() - -add_library(${library_name} STATIC ${source_files} ${include_files}) -target_link_libraries(${library_name} gtest gmock Qt5::Core Qt5::Test mvvm_model) -target_include_directories(${library_name} PUBLIC - $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}> $<BUILD_INTERFACE:${MVVM_AUTOGEN_DIR}>) - -target_compile_features(${library_name} PUBLIC cxx_std_17) diff --git a/mvvm/tests/libtestmachinery/folderbasedtest.cpp b/mvvm/tests/libtestmachinery/folderbasedtest.cpp deleted file mode 100644 index d3919650217..00000000000 --- a/mvvm/tests/libtestmachinery/folderbasedtest.cpp +++ /dev/null @@ -1,47 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/libtestmachinery/folderbasedtest.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "folderbasedtest.h" -#include "mvvm/utils/fileutils.h" -#include "test_utils.h" - -FolderBasedTest::FolderBasedTest(const std::string& test_dir) : m_test_dir(test_dir) -{ - TestUtils::CreateTestDirectory(m_test_dir); -} - -std::string FolderBasedTest::testDir() const -{ - return m_test_dir; -} - -//! Return full path to the test folder. Located in CMAKE_BINARY_DIR/test_output/<m_test_dir>. - -std::string FolderBasedTest::testPath() const -{ - return TestUtils::TestDirectoryPath(m_test_dir); -} - -//! Creates an empty directory in main test folder. -//! Remove recursively previous one with the same name, if exist. - -std::string FolderBasedTest::createEmptyDir(const std::string& subdir) const -{ - auto path = ModelView::Utils::join(testPath(), subdir); - ModelView::Utils::remove_all(path); - ModelView::Utils::create_directory(path); - return path; -} - -FolderBasedTest::~FolderBasedTest() = default; diff --git a/mvvm/tests/libtestmachinery/folderbasedtest.h b/mvvm/tests/libtestmachinery/folderbasedtest.h deleted file mode 100644 index 25e5b957c3b..00000000000 --- a/mvvm/tests/libtestmachinery/folderbasedtest.h +++ /dev/null @@ -1,38 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/libtestmachinery/folderbasedtest.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_TESTS_LIBTESTMACHINERY_FOLDERBASEDTEST_H -#define BORNAGAIN_MVVM_TESTS_LIBTESTMACHINERY_FOLDERBASEDTEST_H - -#include <gtest/gtest.h> -#include <string> - -//! Convenience class which creates a directory on disk for test content. - -class FolderBasedTest : public ::testing::Test { -public: - FolderBasedTest(const std::string& test_dir); - ~FolderBasedTest(); - - std::string testDir() const; - - std::string testPath() const; - - std::string createEmptyDir(const std::string& subdir) const; - -protected: - std::string m_test_dir; //! main directory of given test -}; - -#endif // BORNAGAIN_MVVM_TESTS_LIBTESTMACHINERY_FOLDERBASEDTEST_H diff --git a/mvvm/tests/libtestmachinery/google_test.h b/mvvm/tests/libtestmachinery/google_test.h deleted file mode 100644 index ca8abc21b69..00000000000 --- a/mvvm/tests/libtestmachinery/google_test.h +++ /dev/null @@ -1,22 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/libtestmachinery/google_test.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_TESTS_LIBTESTMACHINERY_GOOGLE_TEST_H -#define BORNAGAIN_MVVM_TESTS_LIBTESTMACHINERY_GOOGLE_TEST_H - -#include "mvvm/model/comparators.h" -#include "mvvm/model/customvariants.h" -#include <gtest/gtest.h> - -#endif // BORNAGAIN_MVVM_TESTS_LIBTESTMACHINERY_GOOGLE_TEST_H diff --git a/mvvm/tests/libtestmachinery/mockinterfaces.h b/mvvm/tests/libtestmachinery/mockinterfaces.h deleted file mode 100644 index 35bd8e7150a..00000000000 --- a/mvvm/tests/libtestmachinery/mockinterfaces.h +++ /dev/null @@ -1,56 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/libtestmachinery/mockinterfaces.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_TESTS_LIBTESTMACHINERY_MOCKINTERFACES_H -#define BORNAGAIN_MVVM_TESTS_LIBTESTMACHINERY_MOCKINTERFACES_H - -#include "mvvm/model/tagrow.h" -#include <string> - -//! Various common utils for unit tests. - -namespace ModelView { -class SessionItem; -class SessionModel; -} // namespace ModelView - -//! Interface for testing callbacks comming from SessionItem within gmock framework. - -class ItemTestWidgetInterface { -public: - virtual ~ItemTestWidgetInterface() = default; - - virtual void onItemDestroy(ModelView::SessionItem* item) = 0; - virtual void onDataChange(ModelView::SessionItem* item, int role) = 0; - virtual void onPropertyChange(ModelView::SessionItem* item, std::string name) = 0; - virtual void onItemInserted(ModelView::SessionItem* item, ModelView::TagRow) = 0; - virtual void onAboutToRemoveItem(ModelView::SessionItem* item, ModelView::TagRow) = 0; -}; - -//! Interface for testing callbacks comming from SessionModel within gmock framework. - -class ModelTestWidgetInterface { -public: - virtual ~ModelTestWidgetInterface() = default; - - virtual void onModelDestroyed(ModelView::SessionModel*) = 0; - virtual void onModelAboutToBeReset(ModelView::SessionModel*) = 0; - virtual void onModelReset(ModelView::SessionModel*) = 0; - virtual void onDataChange(ModelView::SessionItem*, int) = 0; - virtual void onItemInserted(ModelView::SessionItem*, ModelView::TagRow) = 0; - virtual void onItemRemoved(ModelView::SessionItem*, ModelView::TagRow) = 0; - virtual void onAboutToRemoveItem(ModelView::SessionItem*, ModelView::TagRow) = 0; -}; - -#endif // BORNAGAIN_MVVM_TESTS_LIBTESTMACHINERY_MOCKINTERFACES_H diff --git a/mvvm/tests/libtestmachinery/mockwidgets.cpp b/mvvm/tests/libtestmachinery/mockwidgets.cpp deleted file mode 100644 index c6969b16dd3..00000000000 --- a/mvvm/tests/libtestmachinery/mockwidgets.cpp +++ /dev/null @@ -1,144 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/libtestmachinery/mockwidgets.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mockwidgets.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/model/tagrow.h" -#include "mvvm/signals/itemmapper.h" -#include "mvvm/signals/modelmapper.h" - -// ---------------------------------------------------------------------------- - -MockWidgetForItem::MockWidgetForItem(ModelView::SessionItem* item) : m_item(nullptr) -{ - setItem(item); -} - -MockWidgetForItem::~MockWidgetForItem() -{ - if (m_item) - m_item->mapper()->unsubscribe(this); -} - -void MockWidgetForItem::setItem(ModelView::SessionItem* item) -{ - if (m_item == item) - return; - - if (m_item) - m_item->mapper()->unsubscribe(this); - - m_item = item; - - if (!m_item) - return; - - auto on_item_destroy = [this](ModelView::SessionItem* item) { - m_item = nullptr; - onItemDestroy(item); - }; - m_item->mapper()->setOnItemDestroy(on_item_destroy, this); - - auto on_data_change = [this](ModelView::SessionItem* item, int role) { - onDataChange(item, role); - }; - m_item->mapper()->setOnDataChange(on_data_change, this); - - auto on_property_change = [this](ModelView::SessionItem* item, std::string name) { - onPropertyChange(item, name); - }; - m_item->mapper()->setOnPropertyChange(on_property_change, this); - - auto on_child_property_change = [this](ModelView::SessionItem* item, std::string name) { - onChildPropertyChange(item, name); - }; - m_item->mapper()->setOnChildPropertyChange(on_child_property_change, this); - - auto on_item_inserted = [this](ModelView::SessionItem* item, ModelView::TagRow tagrow) { - onItemInserted(item, tagrow); - }; - m_item->mapper()->setOnItemInserted(on_item_inserted, this); - - auto on_item_removed = [this](ModelView::SessionItem* item, ModelView::TagRow tagrow) { - onItemRemoved(item, tagrow); - }; - m_item->mapper()->setOnItemRemoved(on_item_removed, this); - - auto on_about_to_remove_item = [this](ModelView::SessionItem* item, ModelView::TagRow tagrow) { - onAboutToRemoveItem(item, tagrow); - }; - m_item->mapper()->setOnAboutToRemoveItem(on_about_to_remove_item, this); -} - -// ---------------------------------------------------------------------------- - -MockWidgetForModel::MockWidgetForModel(ModelView::SessionModel* model) : m_model(nullptr) -{ - setModel(model); -} - -MockWidgetForModel::~MockWidgetForModel() -{ - if (m_model) - m_model->mapper()->unsubscribe(this); -} - -void MockWidgetForModel::setModel(ModelView::SessionModel* model) -{ - if (m_model == model) - return; - - if (m_model) - m_model->mapper()->unsubscribe(this); - - m_model = model; - - if (!m_model) - return; - - auto on_data_change = [this](ModelView::SessionItem* item, int role) { - onDataChange(item, role); - }; - m_model->mapper()->setOnDataChange(on_data_change, this); - - auto on_item_inserted = [this](ModelView::SessionItem* item, ModelView::TagRow tagrow) { - onItemInserted(item, tagrow); - }; - m_model->mapper()->setOnItemInserted(on_item_inserted, this); - - auto on_item_removed = [this](ModelView::SessionItem* item, ModelView::TagRow tagrow) { - onItemRemoved(item, tagrow); - }; - m_model->mapper()->setOnItemRemoved(on_item_removed, this); - - auto on_about_to_remove_item = [this](ModelView::SessionItem* item, ModelView::TagRow tagrow) { - onAboutToRemoveItem(item, tagrow); - }; - m_model->mapper()->setOnAboutToRemoveItem(on_about_to_remove_item, this); - - auto on_model_destroyed = [this](ModelView::SessionModel* model) { - m_model = nullptr; - onModelDestroyed(model); - }; - m_model->mapper()->setOnModelDestroyed(on_model_destroyed, this); - - auto on_model_about_reset = [this](ModelView::SessionModel* model) { - onModelAboutToBeReset(model); - }; - m_model->mapper()->setOnModelAboutToBeReset(on_model_about_reset, this); - - auto on_model_reset = [this](ModelView::SessionModel* model) { onModelReset(model); }; - m_model->mapper()->setOnModelReset(on_model_reset, this); -} diff --git a/mvvm/tests/libtestmachinery/mockwidgets.h b/mvvm/tests/libtestmachinery/mockwidgets.h deleted file mode 100644 index 49a680be00e..00000000000 --- a/mvvm/tests/libtestmachinery/mockwidgets.h +++ /dev/null @@ -1,76 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/libtestmachinery/mockwidgets.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_TESTS_LIBTESTMACHINERY_MOCKWIDGETS_H -#define BORNAGAIN_MVVM_TESTS_LIBTESTMACHINERY_MOCKWIDGETS_H - -#include "mockinterfaces.h" -#include <gmock/gmock.h> - -namespace ModelView { -class SessionItem; -class SessionModel; -} // namespace ModelView - -//! Mock class for CallbackContainer. - -class CallbackMockWidget { -public: - MOCK_METHOD1(onItemDestroy, void(ModelView::SessionItem* item)); - MOCK_METHOD2(onDataChange, void(ModelView::SessionItem* item, int role)); -}; - -//! Mock widget to test ItemMapper functionality. - -class MockWidgetForItem : public ItemTestWidgetInterface { -public: - MockWidgetForItem(ModelView::SessionItem* item); - ~MockWidgetForItem(); - - void setItem(ModelView::SessionItem* item); - - MOCK_METHOD1(onItemDestroy, void(ModelView::SessionItem* item)); - MOCK_METHOD2(onDataChange, void(ModelView::SessionItem* item, int role)); - MOCK_METHOD2(onPropertyChange, void(ModelView::SessionItem* item, std::string name)); - MOCK_METHOD2(onChildPropertyChange, void(ModelView::SessionItem* item, std::string name)); - MOCK_METHOD2(onItemInserted, void(ModelView::SessionItem* item, ModelView::TagRow tagrow)); - MOCK_METHOD2(onItemRemoved, void(ModelView::SessionItem* item, ModelView::TagRow tagrow)); - MOCK_METHOD2(onAboutToRemoveItem, void(ModelView::SessionItem* item, ModelView::TagRow tagrow)); - -private: - ModelView::SessionItem* m_item; -}; - -//! Mock class to test ModelMapper functionality. - -class MockWidgetForModel : public ModelTestWidgetInterface { -public: - MockWidgetForModel(ModelView::SessionModel* model); - ~MockWidgetForModel(); - - void setModel(ModelView::SessionModel* model); - - MOCK_METHOD1(onModelDestroyed, void(ModelView::SessionModel* model)); - MOCK_METHOD1(onModelAboutToBeReset, void(ModelView::SessionModel* model)); - MOCK_METHOD1(onModelReset, void(ModelView::SessionModel* model)); - MOCK_METHOD2(onDataChange, void(ModelView::SessionItem* item, int role)); - MOCK_METHOD2(onItemInserted, void(ModelView::SessionItem* item, ModelView::TagRow tagrow)); - MOCK_METHOD2(onItemRemoved, void(ModelView::SessionItem* item, ModelView::TagRow tagrow)); - MOCK_METHOD2(onAboutToRemoveItem, void(ModelView::SessionItem* item, ModelView::TagRow tagrow)); - -private: - ModelView::SessionModel* m_model; -}; - -#endif // BORNAGAIN_MVVM_TESTS_LIBTESTMACHINERY_MOCKWIDGETS_H diff --git a/mvvm/tests/libtestmachinery/test_utils.cpp b/mvvm/tests/libtestmachinery/test_utils.cpp deleted file mode 100644 index c007883d22f..00000000000 --- a/mvvm/tests/libtestmachinery/test_utils.cpp +++ /dev/null @@ -1,130 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/libtestmachinery/test_utils.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "test_utils.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/serialization/jsonconverterinterfaces.h" -#include "mvvm/serialization/jsonutils.h" -#include "mvvm/utils/fileutils.h" -#include "testconfig.h" // this file is auto generated by the build system in build directory -#include <QFile> -#include <QJsonDocument> -#include <QJsonObject> -#include <QString> -#include <QTextStream> -#include <stdexcept> -#include <string> - -using namespace ModelView; - -namespace { -void SaveDocument(const QJsonDocument& document, const std::string& fileName); -} - -std::string TestUtils::TestOutputDir() -{ - return TestConfig::TestOutputDir(); // defined in auto-generated testconfig.h -} - -std::string TestUtils::CreateTestDirectory(const std::string& test_sub_dir) -{ - std::string result = TestDirectoryPath(test_sub_dir); - Utils::create_directory(result); - return result; -} - -std::string TestUtils::TestDirectoryPath(const std::string& test_sub_dir) -{ - return TestOutputDir() + std::string("/") + test_sub_dir; -} - -std::string TestUtils::TestFileName(const std::string& test_sub_dir, const std::string& file_name) -{ - - return TestDirectoryPath(test_sub_dir) + std::string("/") + file_name; -} - -void TestUtils::SaveJson(const QJsonObject& object, const std::string& fileName) -{ - QJsonDocument document(object); - SaveDocument(document, fileName); -} - -void TestUtils::SaveJson(const QJsonArray& object, const std::string& fileName) -{ - QJsonDocument document(object); - SaveDocument(document, fileName); -} - -QString TestUtils::JsonToString(const QJsonObject& object) -{ - QJsonDocument document(object); - return QString(document.toJson(QJsonDocument::Compact)); -} - -QString TestUtils::ModelToJsonString(SessionModel& model) -{ - return QString::fromStdString(JsonUtils::ModelToJsonString(model)); -} - -QJsonDocument TestUtils::LoadJson(const std::string& fileName) -{ - QFile jsonFile(QString::fromStdString(fileName)); - - if (!jsonFile.open(QIODevice::ReadOnly)) - throw std::runtime_error("TestUtils::LoadJson() -> Can't read file"); - - return QJsonDocument().fromJson(jsonFile.readAll()); -} - -std::string TestUtils::CreateTestFile(const std::string& dirname, const std::string& fileName) -{ - std::string filename = dirname.empty() ? fileName : dirname + "/" + fileName; - - QFile file(QString::fromStdString(filename)); - if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) - throw std::runtime_error("TestFileUtils::createTestFile() -> Error. " - "Can't create file"); - - QTextStream out(&file); - out << "Test file " << 42 << "\n"; - file.close(); - - return filename; -} - -std::string TestUtils::CreateEmptyFile(const std::string& dirname, const std::string& fileName) -{ - std::string filename = dirname.empty() ? fileName : dirname + "/" + fileName; - - QFile file(QString::fromStdString(filename)); - if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) - throw std::runtime_error("TestFileUtils::createTestFile() -> Error. " - "Can't create file"); - return filename; -} - -namespace { - -void SaveDocument(const QJsonDocument& document, const std::string& fileName) -{ - QFile saveFile(QString::fromStdString(fileName)); - - if (!saveFile.open(QIODevice::WriteOnly)) - throw std::runtime_error("TestUtils::SaveDocument() -> Can't save file"); - - saveFile.write(document.toJson()); -} - -} // namespace diff --git a/mvvm/tests/libtestmachinery/test_utils.h b/mvvm/tests/libtestmachinery/test_utils.h deleted file mode 100644 index b743ca07c20..00000000000 --- a/mvvm/tests/libtestmachinery/test_utils.h +++ /dev/null @@ -1,109 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/libtestmachinery/test_utils.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_TESTS_LIBTESTMACHINERY_TEST_UTILS_H -#define BORNAGAIN_MVVM_TESTS_LIBTESTMACHINERY_TEST_UTILS_H - -#include "mvvm/model/customvariants.h" -#include <QString> -#include <memory> - -//! @file mvvm/tests/libtestmachinery/test_utils.h -//! @brief Collection of utility functions for various unit tests. - -class QJsonObject; -class QJsonArray; - -namespace ModelView { -class SessionModel; -} - -//! Various common utils for unit tests. - -namespace TestUtils { - -//! Returns full path to the main test folder, as defined by CMake at compile time. -//! Shoud point to CMAKE_BINARY_DIR/test_output -std::string TestOutputDir(); - -//! Creates test directory in main test folder and returns full path. -//! If directory exists, will do nothing. -std::string CreateTestDirectory(const std::string& test_sub_dir); - -//! Returns full path to the main test folder in CMAKE_BINARY_DIR. -std::string TestDirectoryPath(const std::string& test_sub_dir); - -//! Returns full path to the file in test directory. -std::string TestFileName(const std::string& test_sub_dir, const std::string& file_name); - -void SaveJson(const QJsonObject& object, const std::string& fileName); - -void SaveJson(const QJsonArray& object, const std::string& fileName); - -QString JsonToString(const QJsonObject& object); - -//! Returns string representing serialized json content of the model. -QString ModelToJsonString(ModelView::SessionModel& model); - -QJsonDocument LoadJson(const std::string& fileName); - -//! Helper function to create test file in a given directory (directory should exist). -//! Returns full path of the file. -std::string CreateTestFile(const std::string& dirname, const std::string& fileName); - -//! Helper function to create empty file in a given directory (directory should exist). -//! Returns full path of the file. -std::string CreateEmptyFile(const std::string& dirname, const std::string& fileName); - -//! Deletes items in the container and cleans container afterwards. - -template <typename T> void clean_items(T& items) -{ - for (auto item : items) - delete item; - items.clear(); -} - -//! Creates vector of unique_ptr of given type. - -template <typename B, typename D> auto create_row(int ncolumns) -{ - std::vector<std::unique_ptr<B>> result; - for (int i = 0; i < ncolumns; ++i) - result.emplace_back(std::make_unique<D>()); - return result; -} - -//! Creates vector of pointers from vector of unique_ptr. - -template <typename T> auto create_pointers(const std::vector<std::unique_ptr<T>>& vec) -{ - std::vector<T*> result; - std::transform(vec.begin(), vec.end(), std::back_inserter(result), - [](auto& x) { return x.get(); }); - return result; -} - -//! Creates vector of T from argument list. Used in EXPECT_EQ macros for convenience. - -template <typename T, typename... Args> std::vector<T> toVector(Args&&... args) -{ - std::vector<T> v; - (v.push_back(T(args)), ...); - return v; -} - -} // namespace TestUtils - -#endif // BORNAGAIN_MVVM_TESTS_LIBTESTMACHINERY_TEST_UTILS_H diff --git a/mvvm/tests/libtestmachinery/toyitems.cpp b/mvvm/tests/libtestmachinery/toyitems.cpp deleted file mode 100644 index 2ef891ccbe4..00000000000 --- a/mvvm/tests/libtestmachinery/toyitems.cpp +++ /dev/null @@ -1,106 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/libtestmachinery/toyitems.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "toyitems.h" -#include "mvvm/model/comboproperty.h" -#include "mvvm/model/taginfo.h" -#include "mvvm/signals/itemmapper.h" -#include "mvvm/standarditems/vectoritem.h" -#include <QColor> -#include <stdexcept> - -using namespace ToyItems; - -MultiLayerItem::MultiLayerItem() : CompoundItem(GUI::Constants::MultiLayerItemType) -{ - registerTag(ModelView::TagInfo::universalTag(T_LAYERS, {GUI::Constants::LayerItemType}), - /*set_as_default*/ true); -} - -LayerItem::LayerItem() : CompoundItem(GUI::Constants::LayerItemType) -{ - addProperty(P_THICKNESS, 42.0); - addProperty(P_COLOR, QColor(Qt::green)); - registerTag(ModelView::TagInfo::universalTag(T_PARTICLES, {GUI::Constants::ParticleItemType}), - /*set_as_default*/ true); -} - -// ---------------------------------------------------------------------------- - -ParticleItem::ParticleItem() : CompoundItem(GUI::Constants::ParticleItemType) -{ - addProperty<ModelView::VectorItem>(P_POSITION); - addProperty<ShapeGroupItem>(P_SHAPES); -} - -// ---------------------------------------------------------------------------- - -LatticeItem::LatticeItem() : CompoundItem(GUI::Constants::LatticeItemType) -{ - addProperty(P_ROTATION_ANLE, 90.0); - addProperty(P_INTEGRATION, true); - - auto combo = ModelView::ComboProperty::createFrom({"Default", "Square", "Hexagonal"}); - addProperty(P_LATTICE_TYPE, combo); - - update_appearance(); -} - -void LatticeItem::activate() -{ - auto onIntegrationFlagChange = [this](SessionItem*, std::string property) { - if (property == P_INTEGRATION) - update_appearance(); - }; - mapper()->setOnPropertyChange(onIntegrationFlagChange, this); -} - -void LatticeItem::update_appearance() -{ - auto angle_item = getItem(P_ROTATION_ANLE); - angle_item->setEnabled(!property<bool>(P_INTEGRATION)); -} - -// ---------------------------------------------------------------------------- - -CylinderItem::CylinderItem() : CompoundItem(GUI::Constants::CylinderItemType) -{ - addProperty(P_RADIUS, 8.0); - addProperty(P_HEIGHT, 10.0); -} - -// ---------------------------------------------------------------------------- - -SphereItem::SphereItem() : CompoundItem(GUI::Constants::SphereItemType) -{ - addProperty(P_RADIUS, 8.0); -} - -// ---------------------------------------------------------------------------- - -AnysoPyramidItem::AnysoPyramidItem() : CompoundItem(GUI::Constants::AnysoPyramidItemType) -{ - addProperty(P_LENGTH, 8.0); - addProperty(P_WIDTH, 8.0); - addProperty(P_HEIGHT, 8.0); - addProperty(P_ALPHA, 8.0); -} - -ShapeGroupItem::ShapeGroupItem() : GroupItem(GUI::Constants::ShapeGroupItemType) -{ - registerItem<CylinderItem>("Cylinder"); - registerItem<SphereItem>("Full sphere", /*make_selected*/ true); - registerItem<AnysoPyramidItem>("Anysotropical pyramid"); - init_group(); -} diff --git a/mvvm/tests/libtestmachinery/toyitems.h b/mvvm/tests/libtestmachinery/toyitems.h deleted file mode 100644 index a9dec15cf9f..00000000000 --- a/mvvm/tests/libtestmachinery/toyitems.h +++ /dev/null @@ -1,124 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/libtestmachinery/toyitems.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_TESTS_LIBTESTMACHINERY_TOYITEMS_H -#define BORNAGAIN_MVVM_TESTS_LIBTESTMACHINERY_TOYITEMS_H - -#include "mvvm/model/compounditem.h" -#include "mvvm/model/groupitem.h" -#include "mvvm/model/itempool.h" -#include "mvvm/model/sessionmodel.h" -#include <string> - -//! Collection of toy items and models for testing purposes. - -namespace ToyItems::Constants { - -const ModelView::model_type MultiLayerItemType = "MultiLayer"; -const ModelView::model_type LayerItemType = "Layer"; -const ModelView::model_type ParticleItemType = "Particle"; -const ModelView::model_type LatticeItemType = "Lattice"; - -const ModelView::model_type CylinderItemType = "Cylinder"; -const ModelView::model_type SphereItemType = "Sphere"; -const ModelView::model_type AnysoPyramidItemType = "AnysoPyramid"; - -const ModelView::model_type ShapeGroupItemType = "ShapeGroup"; -} // namespace Constants - -//! Represents multilayer with collection of layers. - -class MultiLayerItem : public ModelView::CompoundItem { -public: - static inline const std::string T_LAYERS = "T_LAYERS"; - MultiLayerItem(); -}; - -//! Represents a layer, with thickness and color, and possibly populated with particles. - -class LayerItem : public ModelView::CompoundItem { -public: - static inline const std::string P_THICKNESS = "Thickness"; - static inline const std::string P_COLOR = "Color"; - static inline const std::string T_PARTICLES = "Particles"; - LayerItem(); -}; - -//! Represents a particle, with a position, and a selection of possible shapes. - -class ParticleItem : public ModelView::CompoundItem { -public: - static inline const std::string P_POSITION = "Position"; - static inline const std::string P_SHAPES = "Shapes"; - - ParticleItem(); -}; - -//! Represents a lattice. - -class LatticeItem : public ModelView::CompoundItem { -public: - static inline const std::string P_ROTATION_ANLE = "Rotation"; - static inline const std::string P_INTEGRATION = "Integration"; - static inline const std::string P_LATTICE_TYPE = "Lattice type"; - - LatticeItem(); - - void activate() override; - -private: - void update_appearance(); -}; - -//! Represents a cylindrical shape. - -class CylinderItem : public ModelView::CompoundItem { -public: - static inline const std::string P_RADIUS = "Radius"; - static inline const std::string P_HEIGHT = "Height"; - - CylinderItem(); -}; - -//! Represents a shpere. - -class SphereItem : public ModelView::CompoundItem { -public: - static inline const std::string P_RADIUS = "Radius"; - - SphereItem(); -}; - -//! Represents an anysotropical pyramid. - -class AnysoPyramidItem : public ModelView::CompoundItem { -public: - static inline const std::string P_LENGTH = "Length"; - static inline const std::string P_WIDTH = "Width"; - static inline const std::string P_HEIGHT = "Height"; - static inline const std::string P_ALPHA = "Alpha"; - - AnysoPyramidItem(); -}; - -//! Represents a group item holding a collection of shapes. - -class ShapeGroupItem : public ModelView::GroupItem { -public: - ShapeGroupItem(); -}; - -} // namespace ToyItems - -#endif // BORNAGAIN_MVVM_TESTS_LIBTESTMACHINERY_TOYITEMS_H diff --git a/mvvm/tests/libtestmachinery/toymodel.cpp b/mvvm/tests/libtestmachinery/toymodel.cpp deleted file mode 100644 index 5d028d3a241..00000000000 --- a/mvvm/tests/libtestmachinery/toymodel.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/libtestmachinery/toymodel.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "toymodel.h" -#include "mvvm/model/itemcatalogue.h" -#include "toyitems.h" - -namespace { -std::unique_ptr<ModelView::ItemCatalogue> CreateItemCatalogue() -{ - auto result = std::make_unique<ModelView::ItemCatalogue>(); - result->registerItem<ToyItems::MultiLayerItem>(); - result->registerItem<ToyItems::LayerItem>(); - result->registerItem<ToyItems::ParticleItem>(); - result->registerItem<ToyItems::LatticeItem>(); - result->registerItem<ToyItems::SphereItem>(); - result->registerItem<ToyItems::CylinderItem>(); - result->registerItem<ToyItems::AnysoPyramidItem>(); - result->registerItem<ToyItems::ShapeGroupItem>(); - return result; -} -} // namespace - -ToyItems::SampleModel::SampleModel() : SessionModel("ToyModel") -{ - setItemCatalogue(CreateItemCatalogue()); -} - -ToyItems::SampleModel::SampleModel(std::shared_ptr<ModelView::ItemPool> pool) - : SessionModel("ToyModel", pool) -{ - setItemCatalogue(CreateItemCatalogue()); -} diff --git a/mvvm/tests/libtestmachinery/toymodel.h b/mvvm/tests/libtestmachinery/toymodel.h deleted file mode 100644 index c41b6e8ad0f..00000000000 --- a/mvvm/tests/libtestmachinery/toymodel.h +++ /dev/null @@ -1,33 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/libtestmachinery/toymodel.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_TESTS_LIBTESTMACHINERY_TOYMODEL_H -#define BORNAGAIN_MVVM_TESTS_LIBTESTMACHINERY_TOYMODEL_H - -#include "mvvm/model/itempool.h" -#include "mvvm/model/sessionmodel.h" - -//! Collection of toy items and models for testing purposes. - -namespace ToyItems { - -class SampleModel : public ModelView::SessionModel { -public: - SampleModel(); - SampleModel(std::shared_ptr<ModelView::ItemPool> pool); -}; - -} // namespace ToyItems - -#endif // BORNAGAIN_MVVM_TESTS_LIBTESTMACHINERY_TOYMODEL_H diff --git a/mvvm/tests/libtestmachinery/widgetbasedtest.cpp b/mvvm/tests/libtestmachinery/widgetbasedtest.cpp deleted file mode 100644 index 39f62d5eac8..00000000000 --- a/mvvm/tests/libtestmachinery/widgetbasedtest.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/libtestmachinery/widgetbasedtest.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "widgetbasedtest.h" -#include <QApplication> - -QApplication* WidgetBasedTest::m_app = nullptr; - -namespace { -// faking argc and argv -char progname[] = "testview"; -char* argv[] = {&progname[0], nullptr}; -int argc = 1; -} // namespace - -WidgetBasedTest::WidgetBasedTest() {} - -void WidgetBasedTest::SetUpTestSuite() -{ - m_app = new QApplication(argc, argv); -} - -void WidgetBasedTest::TearDownTestSuite() -{ - delete m_app; - m_app = 0; -} diff --git a/mvvm/tests/libtestmachinery/widgetbasedtest.h b/mvvm/tests/libtestmachinery/widgetbasedtest.h deleted file mode 100644 index cb92e31183c..00000000000 --- a/mvvm/tests/libtestmachinery/widgetbasedtest.h +++ /dev/null @@ -1,36 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/libtestmachinery/widgetbasedtest.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_TESTS_LIBTESTMACHINERY_WIDGETBASEDTEST_H -#define BORNAGAIN_MVVM_TESTS_LIBTESTMACHINERY_WIDGETBASEDTEST_H - -#include <gtest/gtest.h> - -class QApplication; - -//! Convenience class to setup QApplication for tests involving QWidget creation. - -class WidgetBasedTest : public ::testing::Test { -public: - WidgetBasedTest(); - - static void SetUpTestSuite(); - - static void TearDownTestSuite(); - -protected: - static QApplication* m_app; -}; - -#endif // BORNAGAIN_MVVM_TESTS_LIBTESTMACHINERY_WIDGETBASEDTEST_H diff --git a/mvvm/tests/testintegration/CMakeLists.txt b/mvvm/tests/testintegration/CMakeLists.txt deleted file mode 100644 index 8471dad8f4b..00000000000 --- a/mvvm/tests/testintegration/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -set(test testintegration) - -file(GLOB source_files "*.cpp") -file(GLOB include_files "*.h") - -find_package(Qt5Core REQUIRED) -find_package(Qt5Test REQUIRED) - -# necessary for Qt creator and clang code model -include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) - -set(CMAKE_AUTOMOC ON) -add_executable(${test} ${source_files} ${include_files}) -target_link_libraries(${test} gtest gmock Qt5::Core Qt5::Test mvvm_view testmachinery qcustomplot) - -if (MVVM_DISCOVER_TESTS) - gtest_discover_tests(${test}) -else() - add_custom_target(${test}_run ALL DEPENDS ${test} COMMAND ${test}) -endif() diff --git a/mvvm/tests/testintegration/TestAll.cpp b/mvvm/tests/testintegration/TestAll.cpp deleted file mode 100644 index e1ca7ebad8e..00000000000 --- a/mvvm/tests/testintegration/TestAll.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testintegration/TestAll.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "qcustomplot.h" -#include <QApplication> -#include <QStandardItem> - -int main(int argc, char** argv) -{ - ::testing::InitGoogleTest(&argc, argv); - - ModelView::Comparators::registerComparators(); - - QApplication app(argc, argv); - Q_UNUSED(app) - - return RUN_ALL_TESTS(); -} diff --git a/mvvm/tests/testintegration/standarditemserialization.test.cpp b/mvvm/tests/testintegration/standarditemserialization.test.cpp deleted file mode 100644 index bed05bcb370..00000000000 --- a/mvvm/tests/testintegration/standarditemserialization.test.cpp +++ /dev/null @@ -1,125 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testintegration/standarditemserialization.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/modelutils.h" -#include "mvvm/serialization/jsonmodelconverter.h" -#include "mvvm/standarditems/standarditemincludes.h" -#include <QDebug> -#include <QJsonObject> - -using namespace ModelView; - -//! Testing serialization of ToyItems using json converters. - -class StandardItemsSerializationTest : public ::testing::Test { -public: - ~StandardItemsSerializationTest(); -}; - -StandardItemsSerializationTest::~StandardItemsSerializationTest() = default; - -//! Checking that serialization works (not crashing) for all defined standard items. - -TEST_F(StandardItemsSerializationTest, allItems) -{ - SessionModel model; - model.insertItem<ColorMapItem>(); - model.insertItem<ColorMapViewportItem>(); - model.insertItem<CompoundItem>(); - model.insertItem<ContainerItem>(); - model.insertItem<Data1DItem>(); - model.insertItem<Data2DItem>(); - model.insertItem<FixedBinAxisItem>(); - model.insertItem<GraphItem>(); - model.insertItem<GraphViewportItem>(); - model.insertItem<LinkedItem>(); - model.insertItem<PenItem>(); - model.insertItem<PointwiseAxisItem>(); - model.insertItem<PropertyItem>(); - model.insertItem<SessionItem>(); - model.insertItem<TextItem>(); - model.insertItem<VectorItem>(); - model.insertItem<ViewportAxisItem>(); - - auto modelCopy = Utils::CreateCopy(model); - EXPECT_EQ(model.rootItem()->childrenCount(), modelCopy->rootItem()->childrenCount()); -} - -//! Creating graph with data. It has to be identical after serialization. - -TEST_F(StandardItemsSerializationTest, GraphItemAndDataSerialization) -{ - // preparing model, data item and graph pointing to it - SessionModel model; - auto graph_item = model.insertItem<GraphItem>(); - auto data_item = model.insertItem<Data1DItem>(); - const std::vector<double> expected_values = {1.0, 2.0, 3.0}; - const std::vector<double> expected_centers = {0.5, 1.5, 2.5}; - data_item->setAxis<FixedBinAxisItem>(3, 0.0, 3.0); - data_item->setValues(expected_values); - graph_item->setDataItem(data_item); - - // accessing copied items - auto modelClone = Utils::CreateClone(model); - EXPECT_EQ(model.rootItem()->childrenCount(), modelClone->rootItem()->childrenCount()); - - auto graphClone = modelClone->topItem<GraphItem>(); - auto dataClone = modelClone->topItem<Data1DItem>(); - - // analyzing copies - EXPECT_EQ(graphClone->dataItem(), dataClone); - EXPECT_EQ(dataClone->binCenters(), expected_centers); - EXPECT_EQ(dataClone->binValues(), expected_values); -} - -//! Creating viewport with one graph. Serializing and restoring the model. - -TEST_F(StandardItemsSerializationTest, graphViewPortItemSerialization) -{ - SessionModel model; - auto viewport_item = model.insertItem<GraphViewportItem>(); - auto graph_item = model.insertItem<GraphItem>(viewport_item); - auto data_item = model.insertItem<Data1DItem>(); - - const std::vector<double> expected_values = {1.0, 2.0, 3.0}; - const std::vector<double> expected_centers = {0.5, 1.5, 2.5}; - data_item->setAxis<FixedBinAxisItem>(3, 0.0, 3.0); - data_item->setValues(expected_values); - - graph_item->setDataItem(data_item); - EXPECT_EQ(viewport_item->graphItems().size(), 1); - - // updating viewport to graph - viewport_item->setViewportToContent(); - - // accessing cloned items - auto modelClone = Utils::CreateClone(model); - EXPECT_EQ(model.rootItem()->childrenCount(), modelClone->rootItem()->childrenCount()); - auto viewportCopy = modelClone->topItem<GraphViewportItem>(); - ASSERT_EQ(viewportCopy->graphItems().size(), 1); - auto graphClone = viewportCopy->graphItems().at(0); - auto dataClone = modelClone->topItem<Data1DItem>(); - - // analyzing clones - EXPECT_EQ(graphClone->dataItem(), dataClone); - EXPECT_EQ(graphClone->dataItem(), dataClone); - EXPECT_EQ(dataClone->binCenters(), expected_centers); - EXPECT_EQ(dataClone->binValues(), expected_values); - - EXPECT_DOUBLE_EQ(viewportCopy->xAxis()->property<double>(ViewportAxisItem::P_MIN), - expected_centers[0]); - EXPECT_DOUBLE_EQ(viewportCopy->xAxis()->property<double>(ViewportAxisItem::P_MAX), - expected_centers[2]); -} diff --git a/mvvm/tests/testintegration/toyitemslattice.test.cpp b/mvvm/tests/testintegration/toyitemslattice.test.cpp deleted file mode 100644 index 90c7c67d3fe..00000000000 --- a/mvvm/tests/testintegration/toyitemslattice.test.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testintegration/toyitemslattice.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "toyitems.h" -#include "toymodel.h" - -using namespace ModelView; -using namespace ToyItems; - -//! Test toy LatticeItem. - -class ToyItemsLatticeTest : public ::testing::Test { -public: - ~ToyItemsLatticeTest(); -}; - -ToyItemsLatticeTest::~ToyItemsLatticeTest() = default; - -//! Business logice (enabled/disabled). - -TEST_F(ToyItemsLatticeTest, ToyItemsLatticeTest) -{ - ToyItems::SampleModel model; - auto lattice = model.insertItem<LatticeItem>(); - - // by default integration flag is ON, rotation angle is disabled - EXPECT_TRUE(lattice->property<bool>(LatticeItem::P_INTEGRATION)); - EXPECT_FALSE(lattice->getItem(LatticeItem::P_ROTATION_ANLE)->isEnabled()); - - // switching integration OFF, checking that rotation is enabled - lattice->setProperty(LatticeItem::P_INTEGRATION, false); - EXPECT_TRUE(lattice->getItem(LatticeItem::P_ROTATION_ANLE)->isEnabled()); -} diff --git a/mvvm/tests/testintegration/toyitemsserialization.test.cpp b/mvvm/tests/testintegration/toyitemsserialization.test.cpp deleted file mode 100644 index 4ba387e0223..00000000000 --- a/mvvm/tests/testintegration/toyitemsserialization.test.cpp +++ /dev/null @@ -1,94 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testintegration/toyitemsserialization.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/modelutils.h" -#include "mvvm/serialization/jsonmodelconverter.h" -#include "toyitems.h" -#include "toymodel.h" -#include <QJsonObject> - -using namespace ModelView; -using namespace ToyItems; - -//! Testing serialization of ToyItems using json converters. - -class ToyItemsSerializationTest : public ::testing::Test { -public: - ~ToyItemsSerializationTest(); -}; - -ToyItemsSerializationTest::~ToyItemsSerializationTest() = default; - -//! Checking ShapeGroupItem in a model. -//! Serialization/deserelization should give an item identical to original. - -TEST_F(ToyItemsSerializationTest, defaultShapeGroupItemInModel) -{ - // model with single group item - SampleModel model; - auto group = model.insertItem<ShapeGroupItem>(); - - // copy of model - auto modelCopy = Utils::CreateCopy(model); - auto groupCopy = modelCopy->topItem<ShapeGroupItem>(); - - // basic properties in copied item should be the same - EXPECT_EQ(group->currentIndex(), groupCopy->currentIndex()); - EXPECT_EQ(group->currentItem()->modelType(), groupCopy->currentItem()->modelType()); -} - -//! Checking ShapeGroupItem in a model. -//! Serialization/deserelization should give an item identical to original. - -TEST_F(ToyItemsSerializationTest, modifiedShapeGroupItemInModel) -{ - SampleModel model; - auto group = model.insertItem<ShapeGroupItem>(); - - // modifying group item - group->setCurrentType(ToyItems::GUI::Constants::AnysoPyramidItemType); - group->children().at(0)->setProperty(CylinderItem::P_RADIUS, 42.0); - group->children().at(1)->setProperty(SphereItem::P_RADIUS, 43.0); - group->children().at(2)->setProperty(AnysoPyramidItem::P_LENGTH, 44.0); - - // creating copy - auto modelCopy = Utils::CreateCopy(model); - auto groupCopy = modelCopy->topItem<ShapeGroupItem>(); - - // checking properties of - EXPECT_EQ(groupCopy->currentIndex(), group->currentIndex()); - EXPECT_EQ(groupCopy->currentItem()->modelType(), group->currentItem()->modelType()); - EXPECT_EQ(groupCopy->children().at(0)->property<double>(CylinderItem::P_RADIUS), 42.0); - EXPECT_EQ(groupCopy->children().at(1)->property<double>(SphereItem::P_RADIUS), 43.0); - EXPECT_EQ(groupCopy->children().at(2)->property<double>(AnysoPyramidItem::P_LENGTH), 44.0); -} - -//! Insert all supported items in a model and check that after serialization - -TEST_F(ToyItemsSerializationTest, allItemsInAModel) -{ - SampleModel model; - model.insertItem<ToyItems::MultiLayerItem>(); - model.insertItem<ToyItems::LayerItem>(); - model.insertItem<ToyItems::ParticleItem>(); - model.insertItem<ToyItems::LatticeItem>(); - model.insertItem<ToyItems::SphereItem>(); - model.insertItem<ToyItems::CylinderItem>(); - model.insertItem<ToyItems::AnysoPyramidItem>(); - model.insertItem<ToyItems::ShapeGroupItem>(); - - auto modelCopy = Utils::CreateCopy(model); - EXPECT_EQ(model.rootItem()->childrenCount(), modelCopy->rootItem()->childrenCount()); -} diff --git a/mvvm/tests/testintegration/toyitemsshapegroup.test.cpp b/mvvm/tests/testintegration/toyitemsshapegroup.test.cpp deleted file mode 100644 index af4a51275aa..00000000000 --- a/mvvm/tests/testintegration/toyitemsshapegroup.test.cpp +++ /dev/null @@ -1,288 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testintegration/toyitemsshapegroup.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/comboproperty.h" -#include "mvvm/viewmodel/defaultviewmodel.h" -#include "mvvm/viewmodel/propertyviewmodel.h" -#include "mvvm/viewmodel/standardviewitems.h" -#include "toyitems.h" -#include "toymodel.h" -#include <QSignalSpy> - -using namespace ModelView; - -//! Tests of toy ShapeGroup in the context of model and viewmodel. - -class ToyItemsShapeGroupTest : public ::testing::Test { -public: - ~ToyItemsShapeGroupTest(); -}; - -ToyItemsShapeGroupTest::~ToyItemsShapeGroupTest() = default; - -//! Toy multilayer as produced bo toy SampleModel. - -TEST_F(ToyItemsShapeGroupTest, initialState) -{ - ToyItems::ShapeGroupItem item; - - EXPECT_EQ(item.currentIndex(), 1); - ASSERT_TRUE(item.currentItem() != nullptr); - EXPECT_TRUE(item.data<QVariant>().isValid()); - EXPECT_EQ(item.currentType(), item.currentItem()->modelType()); - EXPECT_EQ(item.currentType(), ToyItems::GUI::Constants::SphereItemType); - ASSERT_EQ(item.children().size(), 3); - - // parent child relationship - EXPECT_EQ(item.children().at(0)->parent(), &item); - EXPECT_EQ(item.children().at(1)->parent(), &item); - EXPECT_EQ(item.children().at(2)->parent(), &item); - - // expected value in combo - ComboProperty combo = item.data<ComboProperty>(); - EXPECT_EQ(combo.currentIndex(), 1); - EXPECT_EQ(combo.values(), - std::vector<std::string>({"Cylinder", "Full sphere", "Anysotropical pyramid"})); -} - -TEST_F(ToyItemsShapeGroupTest, setCurrentType) -{ - ToyItems::ShapeGroupItem item; - item.setCurrentType(ToyItems::GUI::Constants::CylinderItemType); - - EXPECT_EQ(item.currentIndex(), 0); - ASSERT_TRUE(item.currentItem() != nullptr); - EXPECT_EQ(item.currentType(), item.currentItem()->modelType()); - EXPECT_EQ(item.currentItem()->modelType(), ToyItems::GUI::Constants::CylinderItemType); - EXPECT_TRUE(item.data<QVariant>().isValid()); - EXPECT_EQ(item.children().size(), 3); - - // expected value in combo - ComboProperty combo = item.data<ComboProperty>(); - EXPECT_EQ(combo.currentIndex(), 0); - EXPECT_EQ(combo.values(), - std::vector<std::string>({"Cylinder", "Full sphere", "Anysotropical pyramid"})); -} - -TEST_F(ToyItemsShapeGroupTest, currentItemNoConst) -{ - ToyItems::ShapeGroupItem item; - item.currentItem()->setProperty(ToyItems::SphereItem::P_RADIUS, 42.0); - EXPECT_EQ(item.currentItem()->property<double>(ToyItems::SphereItem::P_RADIUS), 42.0); -} - -TEST_F(ToyItemsShapeGroupTest, inModelContext) -{ - ToyItems::SampleModel model; - auto item = model.insertItem<ToyItems::ShapeGroupItem>(); - ASSERT_TRUE(item != nullptr); - - EXPECT_EQ(item->currentIndex(), 1); - ASSERT_TRUE(item->currentItem() != nullptr); - EXPECT_TRUE(item->data<QVariant>().isValid()); - EXPECT_EQ(item->currentType(), item->currentItem()->modelType()); - EXPECT_EQ(item->children().size(), 3); - - // parent child relationship - EXPECT_EQ(item->children().at(0)->parent(), item); - EXPECT_EQ(item->children().at(1)->parent(), item); - EXPECT_EQ(item->children().at(2)->parent(), item); - - // model relationship - EXPECT_EQ(item->children().at(0)->model(), &model); - EXPECT_EQ(item->children().at(1)->model(), &model); - EXPECT_EQ(item->children().at(2)->model(), &model); -} - -TEST_F(ToyItemsShapeGroupTest, setDataInModelContext) -{ - ToyItems::SampleModel model; - auto item = model.insertItem<ToyItems::ShapeGroupItem>(); - ASSERT_TRUE(item != nullptr); - - // initial status - EXPECT_EQ(item->currentIndex(), 1); - EXPECT_EQ(item->currentType(), ToyItems::GUI::Constants::SphereItemType); - ComboProperty combo = model.data(item, ItemDataRole::DATA).value<ComboProperty>(); - EXPECT_EQ(combo.currentIndex(), 1); - - // setting through combo - combo.setCurrentIndex(0); - model.setData(item, QVariant::fromValue(combo), ItemDataRole::DATA); - - EXPECT_EQ(item->currentIndex(), 0); - EXPECT_EQ(item->currentType(), ToyItems::GUI::Constants::CylinderItemType); -} - -//! ViewLabelItem and ViewDataItem from ShapeItem. - -TEST_F(ToyItemsShapeGroupTest, viewItemsFromShapeGroup) -{ - ToyItems::SampleModel model; - - auto groupItem = model.insertItem<ToyItems::ShapeGroupItem>(); - - ViewLabelItem labelItem(groupItem); - EXPECT_EQ(labelItem.data(Qt::DisplayRole).toString().toStdString(), - ToyItems::GUI::Constants::ShapeGroupItemType); - - ViewDataItem dataItem(groupItem); - EXPECT_EQ(dataItem.data(Qt::DisplayRole).value<ComboProperty>().currentIndex(), 1); -} - -//! ShapeGroup item in DefaultViewModel. - -TEST_F(ToyItemsShapeGroupTest, inDefaultViewModelContext) -{ - ToyItems::SampleModel model; - auto groupItem = model.insertItem<ToyItems::ShapeGroupItem>(); - - // constructing viewModel from sample model - DefaultViewModel viewModel(&model); - - // root item should have one child, item looking at our groupItem - EXPECT_EQ(viewModel.rowCount(), 1); - EXPECT_EQ(viewModel.columnCount(), 2); - - // accessing to viewItem representing groupItem - QModelIndex groupIndex = viewModel.index(0, 0); - EXPECT_EQ(viewModel.sessionItemFromIndex(groupIndex), groupItem); - EXPECT_EQ(viewModel.columnCount(groupIndex), 2); - EXPECT_EQ(viewModel.rowCount(groupIndex), 3); // Cylinder, Sphere, Anysopyramid - - // setting up signal spy - QSignalSpy spyRemove(&viewModel, &DefaultViewModel::rowsRemoved); - QSignalSpy spyInsert(&viewModel, &DefaultViewModel::rowsInserted); - QSignalSpy spyData(&viewModel, &DefaultViewModel::dataChanged); - - // changing the data - ComboProperty combo = model.data(groupItem, ItemDataRole::DATA).value<ComboProperty>(); - combo.setCurrentIndex(0); - model.setData(groupItem, QVariant::fromValue(combo), ItemDataRole::DATA); - - // expectances - EXPECT_EQ(spyRemove.count(), 0); - EXPECT_EQ(spyInsert.count(), 0); - EXPECT_EQ(spyData.count(), 1); - - // what signal reports - QList<QVariant> arguments = spyData.takeFirst(); - QModelIndex dataIndex = viewModel.index(0, 1); - EXPECT_EQ(arguments.size(), 3); // QModelIndex left, QModelIndex right, QVector<int> roles - EXPECT_EQ(arguments.at(0).value<QModelIndex>(), dataIndex); - EXPECT_EQ(arguments.at(1).value<QModelIndex>(), dataIndex); - QVector<int> expectedRoles = {Qt::DisplayRole, Qt::EditRole}; - EXPECT_EQ(arguments.at(2).value<QVector<int>>(), expectedRoles); - - // same layout - groupIndex = viewModel.index(0, 0); - EXPECT_EQ(viewModel.sessionItemFromIndex(groupIndex), groupItem); - EXPECT_EQ(viewModel.columnCount(groupIndex), 2); - EXPECT_EQ(viewModel.rowCount(groupIndex), 3); // Cylinder, Sphere, Anysopyramid -} - -//! ShapeGroup item in PropertyViewModel. - -TEST_F(ToyItemsShapeGroupTest, inPropertyViewModelContext) -{ - ToyItems::SampleModel model; - auto parent = model.insertItem<SessionItem>(); - parent->registerTag( - TagInfo::propertyTag("property_tag", ToyItems::GUI::Constants::ShapeGroupItemType)); - - auto groupItem = model.insertItem<ToyItems::ShapeGroupItem>(parent, "property_tag"); - ASSERT_TRUE(groupItem != nullptr); - - // constructing viewModel from sample model - PropertyViewModel viewModel(&model); - viewModel.setRootSessionItem(parent); - - // root item should have one child, item looking at our groupItem - EXPECT_EQ(viewModel.rowCount(), 1); - EXPECT_EQ(viewModel.columnCount(), 2); - - // accessing to viewItem representing groupItem - QModelIndex groupIndex = viewModel.index(0, 0); - EXPECT_EQ(viewModel.sessionItemFromIndex(groupIndex), groupItem); - - // There is only one row under groupItem, representing radius of Sphere - EXPECT_EQ(viewModel.columnCount(groupIndex), 2); - EXPECT_EQ(viewModel.rowCount(groupIndex), 1); // Radius of sphere - - // ViewLabelItem and ViewDataItem should look at propertyItem corresponding to sphere - { - QModelIndex radiusLabelIndex = viewModel.index(0, 0, groupIndex); - QModelIndex radiusValueIndex = viewModel.index(0, 1, groupIndex); - auto radiusLabelItem = - dynamic_cast<ViewLabelItem*>(viewModel.itemFromIndex(radiusLabelIndex)); - ASSERT_TRUE(radiusLabelItem != nullptr); - auto radiusPropertyItem = groupItem->currentItem()->getItem(ToyItems::SphereItem::P_RADIUS); - EXPECT_EQ(radiusLabelItem->item(), radiusPropertyItem); - auto radiusValueItem = - dynamic_cast<ViewDataItem*>(viewModel.itemFromIndex(radiusValueIndex)); - ASSERT_TRUE(radiusValueItem != nullptr); - EXPECT_EQ(radiusValueItem->item(), radiusPropertyItem); - } - - // setting up signal spy - QSignalSpy spyRemove(&viewModel, &DefaultViewModel::rowsRemoved); - QSignalSpy spyInsert(&viewModel, &DefaultViewModel::rowsInserted); - QSignalSpy spyData(&viewModel, &DefaultViewModel::dataChanged); - - // changing the data (now GroupItem's current item changed from Sphere to Cylinder - ComboProperty combo = model.data(groupItem, ItemDataRole::DATA).value<ComboProperty>(); - combo.setCurrentIndex(0); - model.setData(groupItem, QVariant::fromValue(combo), ItemDataRole::DATA); - - EXPECT_EQ(viewModel.columnCount(groupIndex), 2); - EXPECT_EQ(viewModel.rowCount(groupIndex), 2); // Radius and Height of cylinder - - // Checking ViewLabelItem and ViewDataItem corresponding to Cylinder's radius - { - QModelIndex radiusLabelIndex = viewModel.index(0, 0, groupIndex); - QModelIndex radiusValueIndex = viewModel.index(0, 1, groupIndex); - auto radiusLabelItem = - dynamic_cast<ViewLabelItem*>(viewModel.itemFromIndex(radiusLabelIndex)); - ASSERT_TRUE(radiusLabelItem != nullptr); - auto radiusPropertyItem = - groupItem->currentItem()->getItem(ToyItems::CylinderItem::P_RADIUS); - EXPECT_EQ(radiusLabelItem->item(), radiusPropertyItem); - auto radiusValueItem = - dynamic_cast<ViewDataItem*>(viewModel.itemFromIndex(radiusValueIndex)); - ASSERT_TRUE(radiusValueItem != nullptr); - EXPECT_EQ(radiusValueItem->item(), radiusPropertyItem); - } - - // Checking ViewLabelItem and ViewDataItem corresponding to Cylinder's height - { - QModelIndex heightLabelIndex = viewModel.index(1, 0, groupIndex); - QModelIndex heightValueIndex = viewModel.index(1, 1, groupIndex); - auto heightLabelItem = - dynamic_cast<ViewLabelItem*>(viewModel.itemFromIndex(heightLabelIndex)); - ASSERT_TRUE(heightLabelItem != nullptr); - auto heightPropertyItem = - groupItem->currentItem()->getItem(ToyItems::CylinderItem::P_HEIGHT); - EXPECT_EQ(heightLabelItem->item(), heightPropertyItem); - auto heightValueItem = - dynamic_cast<ViewDataItem*>(viewModel.itemFromIndex(heightValueIndex)); - ASSERT_TRUE(heightPropertyItem != nullptr); - EXPECT_EQ(heightValueItem->item(), heightPropertyItem); - } - - // expectances - EXPECT_EQ(spyRemove.count(), 1); - EXPECT_EQ(spyInsert.count(), 2); - EXPECT_EQ(spyData.count(), 1); -} diff --git a/mvvm/tests/testintegration/undoscenario.test.cpp b/mvvm/tests/testintegration/undoscenario.test.cpp deleted file mode 100644 index c32bec991c9..00000000000 --- a/mvvm/tests/testintegration/undoscenario.test.cpp +++ /dev/null @@ -1,85 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testintegration/undoscenario.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/interfaces/undostackinterface.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/plotting/viewportaxisplotcontroller.h" -#include "mvvm/standarditems/axisitems.h" -#include "qcustomplot.h" - -using namespace ModelView; - -//! Testing various undo/redo scenario. - -class UndoScenarioTest : public ::testing::Test { -public: - ~UndoScenarioTest(); -}; - -UndoScenarioTest::~UndoScenarioTest() = default; - -//! Check undo/redo of ViewportAxisItem range, when it is listened by the controller. -//! Real-life bug. - -TEST_F(UndoScenarioTest, undoViewportSetRange) -{ - // initialzing model, custom plot and controller - SessionModel model; - auto axisItem = model.insertItem<ViewportAxisItem>(); - axisItem->setProperty(ViewportAxisItem::P_MIN, 1.0); - axisItem->setProperty(ViewportAxisItem::P_MAX, 2.0); - QCustomPlot custom_plot; - ViewportAxisPlotController controller(custom_plot.xAxis); - controller.setItem(axisItem); - - // initial axis state - EXPECT_EQ(custom_plot.xAxis->range().lower, 1.0); - EXPECT_EQ(custom_plot.xAxis->range().upper, 2.0); - - // enabling undo/redo, and its initial state - model.setUndoRedoEnabled(true); - auto stack = model.undoStack(); - EXPECT_FALSE(stack->canRedo()); - EXPECT_FALSE(stack->canUndo()); - EXPECT_EQ(stack->index(), 0); - EXPECT_EQ(stack->count(), 0); - - // changing axis - axisItem->setProperty(ViewportAxisItem::P_MAX, 20.0); - EXPECT_FALSE(stack->canRedo()); - EXPECT_TRUE(stack->canUndo()); - EXPECT_EQ(stack->index(), 1); - EXPECT_EQ(stack->count(), 1); - EXPECT_EQ(custom_plot.xAxis->range().lower, 1.0); - EXPECT_EQ(custom_plot.xAxis->range().upper, 20.0); - - // undoing - stack->undo(); - EXPECT_TRUE(stack->canRedo()); - EXPECT_FALSE(stack->canUndo()); - EXPECT_EQ(stack->index(), 0); - EXPECT_EQ(stack->count(), 1); - EXPECT_EQ(custom_plot.xAxis->range().lower, 1.0); - EXPECT_EQ(custom_plot.xAxis->range().upper, 2.0); - - // redoing - stack->redo(); - EXPECT_FALSE(stack->canRedo()); - EXPECT_TRUE(stack->canUndo()); - EXPECT_EQ(stack->index(), 1); - EXPECT_EQ(stack->count(), 1); - EXPECT_EQ(custom_plot.xAxis->range().lower, 1.0); - EXPECT_EQ(custom_plot.xAxis->range().upper, 20.0); -} diff --git a/mvvm/tests/testmodel/CMakeLists.txt b/mvvm/tests/testmodel/CMakeLists.txt deleted file mode 100644 index dbf904932c8..00000000000 --- a/mvvm/tests/testmodel/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -set(test testmodel) - -file(GLOB source_files "*.cpp") -file(GLOB include_files "*.h") - -find_package(Qt5Core REQUIRED) -find_package(Qt5Test REQUIRED) - -# necessary for Qt creator and clang code model -include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) - -set(CMAKE_AUTOMOC ON) -add_executable(${test} ${source_files} ${include_files}) -target_link_libraries(${test} gtest gmock Qt5::Core Qt5::Test mvvm_model testmachinery) - -if (MVVM_DISCOVER_TESTS) - gtest_discover_tests(${test}) -else() - add_custom_target(${test}_run ALL DEPENDS ${test} COMMAND ${test}) -endif() diff --git a/mvvm/tests/testmodel/TestAll.cpp b/mvvm/tests/testmodel/TestAll.cpp deleted file mode 100644 index a18379681c4..00000000000 --- a/mvvm/tests/testmodel/TestAll.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/TestAll.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include <gmock/gmock.h> - -int main(int argc, char** argv) -{ - ::testing::InitGoogleTest(&argc, argv); - ::testing::InitGoogleMock(&argc, argv); - - ModelView::Comparators::registerComparators(); - - // run all google tests - return RUN_ALL_TESTS(); -} diff --git a/mvvm/tests/testmodel/axisitems.test.cpp b/mvvm/tests/testmodel/axisitems.test.cpp deleted file mode 100644 index b170adef4dc..00000000000 --- a/mvvm/tests/testmodel/axisitems.test.cpp +++ /dev/null @@ -1,135 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/axisitems.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/standarditems/axisitems.h" - -using namespace ModelView; - -//! Testing AxisItems. - -class AxisItemsTest : public ::testing::Test { -public: - ~AxisItemsTest(); -}; - -AxisItemsTest::~AxisItemsTest() = default; - -//! Initial state - -TEST_F(AxisItemsTest, viewportAxisInitialState) -{ - ViewportAxisItem axis; - EXPECT_EQ(axis.property<double>(ViewportAxisItem::P_MIN), 0.0); - EXPECT_EQ(axis.property<double>(ViewportAxisItem::P_MAX), 1.0); - EXPECT_FALSE(axis.property<bool>(ViewportAxisItem::P_IS_LOG)); -} - -//! ViewportAxisItem::setRange - -TEST_F(AxisItemsTest, viewportAxisSetRange) -{ - ViewportAxisItem axis; - - // default range - auto [lower, upper] = axis.range(); - EXPECT_EQ(lower, 0.0); - EXPECT_EQ(upper, 1.0); - - axis.set_range(1.0, 2.0); - EXPECT_EQ(axis.property<double>(ViewportAxisItem::P_MIN), 1.0); - EXPECT_EQ(axis.property<double>(ViewportAxisItem::P_MAX), 2.0); -} - -//! Factory method for FixedBinAxisItem. - -TEST_F(AxisItemsTest, fixedBinAxisInitialState) -{ - FixedBinAxisItem axis; - EXPECT_EQ(axis.property<double>(FixedBinAxisItem::P_MIN), 0.0); - EXPECT_EQ(axis.property<double>(FixedBinAxisItem::P_MAX), 1.0); - EXPECT_EQ(axis.property<int>(FixedBinAxisItem::P_NBINS), 1); - EXPECT_EQ(axis.binCenters(), std::vector<double>{0.5}); - EXPECT_EQ(axis.size(), 1); - auto [lower, upper] = axis.range(); - EXPECT_EQ(lower, 0.0); - EXPECT_EQ(upper, 1.0); -} - -//! Factory method for FixedBinAxisItem. - -TEST_F(AxisItemsTest, fixedBinAxisSetParameters) -{ - FixedBinAxisItem axis; - axis.setParameters(3, 1.0, 4.0); - - EXPECT_EQ(axis.property<int>(FixedBinAxisItem::P_NBINS), 3); - EXPECT_EQ(axis.property<double>(FixedBinAxisItem::P_MIN), 1.0); - EXPECT_EQ(axis.property<double>(FixedBinAxisItem::P_MAX), 4.0); - - std::vector<double> expected{1.5, 2.5, 3.5}; - EXPECT_EQ(axis.binCenters(), expected); - EXPECT_EQ(axis.size(), 3); -} - -//! Factory method for FixedBinAxisItem. - -TEST_F(AxisItemsTest, fixedBinAxisFactory) -{ - auto axis = FixedBinAxisItem::create(3, 1.0, 4.0); - - EXPECT_EQ(axis->property<int>(FixedBinAxisItem::P_NBINS), 3); - EXPECT_EQ(axis->property<double>(FixedBinAxisItem::P_MIN), 1.0); - EXPECT_EQ(axis->property<double>(FixedBinAxisItem::P_MAX), 4.0); - - std::vector<double> expected{1.5, 2.5, 3.5}; - EXPECT_EQ(axis->binCenters(), expected); - EXPECT_EQ(axis->size(), 3); -} - -//! Range method. - -TEST_F(AxisItemsTest, fixedBinAxisRange) -{ - auto axis = FixedBinAxisItem::create(3, 1.0, 4.0); - - auto [lower, upper] = axis->range(); - EXPECT_EQ(lower, 1.0); - EXPECT_EQ(upper, 4.0); -} - -TEST_F(AxisItemsTest, PointwiseAxisInitialState) -{ - PointwiseAxisItem axis; - std::vector<double> expected_centers = {0.0, 1.0}; - EXPECT_EQ(axis.binCenters(), expected_centers); - EXPECT_EQ(axis.size(), 2); -} - -TEST_F(AxisItemsTest, PointwiseAxisSetParameters) -{ - std::vector<double> expected_centers{1.0, 2.0, 3.0}; - PointwiseAxisItem axis; - axis.setParameters(expected_centers); - EXPECT_EQ(axis.binCenters(), expected_centers); - EXPECT_EQ(axis.size(), 3); -} - -TEST_F(AxisItemsTest, PointwiseAxisCreate) -{ - std::vector<double> expected_centers{1.0, 2.0, 3.0}; - auto axis = PointwiseAxisItem::create(expected_centers); - EXPECT_EQ(axis->binCenters(), expected_centers); - EXPECT_EQ(axis->size(), 3); -} diff --git a/mvvm/tests/testmodel/binutils.test.cpp b/mvvm/tests/testmodel/binutils.test.cpp deleted file mode 100644 index 7d78d6eb41f..00000000000 --- a/mvvm/tests/testmodel/binutils.test.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/binutils.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/utils/binutils.h" -#include <testconfig.h> - -using namespace ModelView; - -//! Testing BinUtils - -class BinUtilsTest : public ::testing::Test { -public: - ~BinUtilsTest(); -}; - -BinUtilsTest::~BinUtilsTest() = default; - -//! Testing Binary Files - -TEST_F(BinUtilsTest, testBinaryFiles) -{ - std::string root_path = TestConfig::TestData(); - - std::string binary_file1 = root_path + std::string("/") + "pdf_file"; - std::string binary_file2 = root_path + std::string("/") + "png_file.png"; - std::string binary_file3 = root_path + std::string("/") + "word_file"; - std::string binary_file4 = root_path + std::string("/") + "mandelbrot.ppm"; - std::string binary_file5 = root_path + std::string("/") + "c++_exec"; - - EXPECT_TRUE(Utils::is_binary(binary_file1)); - EXPECT_TRUE(Utils::is_binary(binary_file2)); - EXPECT_TRUE(Utils::is_binary(binary_file3)); - EXPECT_TRUE(Utils::is_binary(binary_file4)); - EXPECT_TRUE(Utils::is_binary(binary_file5)); - - EXPECT_FALSE(Utils::is_text(binary_file1)); - EXPECT_FALSE(Utils::is_text(binary_file2)); - EXPECT_FALSE(Utils::is_text(binary_file3)); - EXPECT_FALSE(Utils::is_text(binary_file4)); - EXPECT_FALSE(Utils::is_text(binary_file5)); -} - -//! Testing text files - -TEST_F(BinUtilsTest, testTextFiles) -{ - std::string root_path = TestConfig::TestData(); - - std::string text_file1 = root_path + std::string("/") + "text_UTF-8.txt"; - std::string text_file2 = root_path + std::string("/") + "text_UTF-8-BOM.txt"; - - EXPECT_TRUE(Utils::is_text(text_file1)); - EXPECT_TRUE(Utils::is_text(text_file2)); - - EXPECT_FALSE(Utils::is_binary(text_file1)); - EXPECT_FALSE(Utils::is_binary(text_file2)); -} \ No newline at end of file diff --git a/mvvm/tests/testmodel/callbackcontainer.test.cpp b/mvvm/tests/testmodel/callbackcontainer.test.cpp deleted file mode 100644 index 1b29372e8ef..00000000000 --- a/mvvm/tests/testmodel/callbackcontainer.test.cpp +++ /dev/null @@ -1,114 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/callbackcontainer.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mockwidgets.h" -#include "mvvm/model/mvvm_types.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/signals/callbackcontainer.h" -#include <memory> - -using namespace ModelView; -using ::testing::_; - -//! Testing CallbackContainer class. - -class CallbackContainerTest : public ::testing::Test { -public: - ~CallbackContainerTest(); -}; - -CallbackContainerTest::~CallbackContainerTest() = default; - -//! Callback container notifies single widget. Check if removal of widget disables notifications. - -TEST_F(CallbackContainerTest, singleWidget) -{ - CallbackMockWidget widget; - Signal<Callbacks::item_t> signal; - - signal.connect(std::bind(&CallbackMockWidget::onItemDestroy, &widget, std::placeholders::_1), - &widget); - - std::unique_ptr<SessionItem> item(new SessionItem); - EXPECT_CALL(widget, onItemDestroy(item.get())).Times(1); - - // perform action - signal(item.get()); - - // removing client - signal.remove_client(&widget); - EXPECT_CALL(widget, onItemDestroy(_)).Times(0); - - // perform action - signal(item.get()); -} - -//! Callback container notifies two widgets. Check if one widget is removed, -//! the second is still notified. - -TEST_F(CallbackContainerTest, twoWidgets) -{ - CallbackMockWidget widget1, widget2; - Signal<Callbacks::item_t> signal; - - signal.connect([&](SessionItem* item) { widget1.onItemDestroy(item); }, &widget1); - - signal.connect([&](SessionItem* item) { widget2.onItemDestroy(item); }, &widget2); - - std::unique_ptr<SessionItem> item(new SessionItem); - EXPECT_CALL(widget1, onItemDestroy(item.get())).Times(1); - EXPECT_CALL(widget2, onItemDestroy(item.get())).Times(1); - - // perform action - signal(item.get()); - - // removing one of client - signal.remove_client(&widget1); - EXPECT_CALL(widget1, onItemDestroy(_)).Times(0); - EXPECT_CALL(widget2, onItemDestroy(item.get())).Times(1); - - // perform action - signal(item.get()); -} - -//! Callback function with two parameters. - -TEST_F(CallbackContainerTest, twoParameters) -{ - CallbackMockWidget widget1, widget2; - Signal<Callbacks::item_int_t> signal; - - signal.connect([&](SessionItem* item, int role) { widget1.onDataChange(item, role); }, - &widget1); - - signal.connect([&](SessionItem* item, int role) { widget2.onDataChange(item, role); }, - &widget2); - - int expected_role = 42; - std::unique_ptr<SessionItem> item(new SessionItem); - EXPECT_CALL(widget1, onDataChange(item.get(), expected_role)).Times(1); - EXPECT_CALL(widget2, onDataChange(item.get(), expected_role)).Times(1); - - // perform action - signal(item.get(), expected_role); - - // removing one of client - signal.remove_client(&widget1); - EXPECT_CALL(widget1, onDataChange(_, _)).Times(0); - EXPECT_CALL(widget2, onDataChange(item.get(), expected_role)).Times(1); - - // perform action - signal(item.get(), expected_role); -} diff --git a/mvvm/tests/testmodel/colormapitem.test.cpp b/mvvm/tests/testmodel/colormapitem.test.cpp deleted file mode 100644 index c56ae413464..00000000000 --- a/mvvm/tests/testmodel/colormapitem.test.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/colormapitem.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mockwidgets.h" -#include "mvvm/model/comboproperty.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/standarditems/axisitems.h" -#include "mvvm/standarditems/colormapitem.h" -#include "mvvm/standarditems/data2ditem.h" -#include "mvvm/standarditems/linkeditem.h" - -using namespace ModelView; -using ::testing::_; - -//! Testing ColorMapItem. - -class ColorMapItemTest : public ::testing::Test { -public: - ~ColorMapItemTest(); -}; - -ColorMapItemTest::~ColorMapItemTest() = default; - -//! Initial state. - -TEST_F(ColorMapItemTest, initialState) -{ - ColorMapItem item; - EXPECT_TRUE(item.dataItem() == nullptr); - EXPECT_TRUE(item.property<bool>(ColorMapItem::P_INTERPOLATION)); - EXPECT_EQ(item.property<ComboProperty>(ColorMapItem::P_GRADIENT).value(), "Polar"); -} - -//! Setting dataItem in model context. - -TEST_F(ColorMapItemTest, setDataItem) -{ - SessionModel model; - auto data_item = model.insertItem<Data2DItem>(); - auto colormap_item = model.insertItem<ColorMapItem>(); - - colormap_item->setDataItem(data_item); - - EXPECT_EQ(colormap_item->dataItem(), data_item); -} - -//! Check signaling on set data item. - -TEST_F(ColorMapItemTest, onSetDataItem) -{ - SessionModel model; - auto data_item = model.insertItem<Data2DItem>(); - auto colormap_item = model.insertItem<ColorMapItem>(); - - MockWidgetForItem widget(colormap_item); - - EXPECT_CALL(widget, onDataChange(_, _)).Times(0); - EXPECT_CALL(widget, onPropertyChange(colormap_item, ColorMapItem::P_LINK)).Times(1); - EXPECT_CALL(widget, onChildPropertyChange(_, _)).Times(0); - EXPECT_CALL(widget, onItemInserted(_, _)).Times(0); - EXPECT_CALL(widget, onAboutToRemoveItem(_, _)).Times(0); - - // performing action - colormap_item->setDataItem(data_item); -} diff --git a/mvvm/tests/testmodel/colormapviewportitem.test.cpp b/mvvm/tests/testmodel/colormapviewportitem.test.cpp deleted file mode 100644 index ae0a6c6c13d..00000000000 --- a/mvvm/tests/testmodel/colormapviewportitem.test.cpp +++ /dev/null @@ -1,157 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/colormapviewportitem.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mockwidgets.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/standarditems/axisitems.h" -#include "mvvm/standarditems/colormapitem.h" -#include "mvvm/standarditems/colormapviewportitem.h" -#include "mvvm/standarditems/data2ditem.h" - -using namespace ModelView; -using ::testing::_; - -//! Testing ColorMapViewportItem. - -class ColorMapViewportItemTest : public ::testing::Test { -public: - ~ColorMapViewportItemTest(); -}; - -ColorMapViewportItemTest::~ColorMapViewportItemTest() = default; - -//! Initial state. - -TEST_F(ColorMapViewportItemTest, initialState) -{ - ColorMapViewportItem viewport; - EXPECT_EQ(viewport.xAxis()->modelType(), GUI::Constants::ViewportAxisItemType); - EXPECT_EQ(viewport.yAxis()->modelType(), GUI::Constants::ViewportAxisItemType); - EXPECT_EQ(viewport.zAxis()->modelType(), GUI::Constants::ViewportAxisItemType); - EXPECT_EQ(viewport.items<ColorMapItem>(ColorMapViewportItem::T_ITEMS).size(), 0); -} - -//! Update on unitialized viewport. - -TEST_F(ColorMapViewportItemTest, uninitializedViewportUpdate) -{ - SessionModel model; - - auto viewport_item = model.insertItem<ColorMapViewportItem>(); - - viewport_item->setViewportToContent(); - - // x-axis of viewport should be set Data2DItem - auto [xmin, xmax] = viewport_item->xAxis()->range(); - EXPECT_DOUBLE_EQ(xmin, 0.0); - EXPECT_DOUBLE_EQ(xmax, 1.0); - - // y-axis of viewport should be set Data2DItem - auto [ymin, ymax] = viewport_item->yAxis()->range(); - EXPECT_DOUBLE_EQ(ymin, 0.0); - EXPECT_DOUBLE_EQ(ymax, 1.0); - - // z-axis of viewport should be set to min/max content - auto [zmin, zmax] = viewport_item->zAxis()->range(); - EXPECT_DOUBLE_EQ(zmin, 0.0); - EXPECT_DOUBLE_EQ(zmax, 1.0); -} - -//! Add graph to viewport. - -TEST_F(ColorMapViewportItemTest, addItem) -{ - SessionModel model; - - auto viewport_item = model.insertItem<ColorMapViewportItem>(); - auto colormap_item = model.insertItem<ColorMapItem>(viewport_item); - auto data_item = model.insertItem<Data2DItem>(); - - data_item->setAxes(FixedBinAxisItem::create(2, 0.0, 2.0), - FixedBinAxisItem::create(3, 0.0, 3.0)); - const std::vector<double> expected_content = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0}; - data_item->setContent(expected_content); - - colormap_item->setDataItem(data_item); - EXPECT_EQ(viewport_item->items<ColorMapItem>(ColorMapViewportItem::T_ITEMS).size(), 1); - - // updating viewport to graph - viewport_item->setViewportToContent(); - - // x-axis of viewport should be set Data2DItem - auto [xmin, xmax] = viewport_item->xAxis()->range(); - EXPECT_DOUBLE_EQ(xmin, 0.0); - EXPECT_DOUBLE_EQ(xmax, 2.0); - - // y-axis of viewport should be set Data2DItem - auto [ymin, ymax] = viewport_item->yAxis()->range(); - EXPECT_DOUBLE_EQ(ymin, 0.0); - EXPECT_DOUBLE_EQ(ymax, 3.0); - - // z-axis of viewport should be set to min/max content - auto [zmin, zmax] = viewport_item->zAxis()->range(); - EXPECT_DOUBLE_EQ(zmin, 1.0); - EXPECT_DOUBLE_EQ(zmax, 6.0); -} - -//! Check signaling on set data item. - -TEST_F(ColorMapViewportItemTest, onAddItem) -{ - SessionModel model; - auto viewport_item = model.insertItem<ColorMapViewportItem>(); - - MockWidgetForItem widget(viewport_item); - - const TagRow expected_tagrow{ViewportItem::T_ITEMS, 0}; - EXPECT_CALL(widget, onDataChange(_, _)).Times(0); - EXPECT_CALL(widget, onPropertyChange(_, _)).Times(0); - EXPECT_CALL(widget, onChildPropertyChange(_, _)).Times(0); - EXPECT_CALL(widget, onItemInserted(viewport_item, expected_tagrow)).Times(1); - EXPECT_CALL(widget, onAboutToRemoveItem(_, _)).Times(0); - - // triggering action - model.insertItem<ColorMapItem>(viewport_item); -} - -//! Check signaling on set data item. - -TEST_F(ColorMapViewportItemTest, onSetDataItem) -{ - SessionModel model; - auto viewport_item = model.insertItem<ColorMapViewportItem>(); - - // setting upda tata item - auto data_item = model.insertItem<Data2DItem>(); - - data_item->setAxes(FixedBinAxisItem::create(2, 0.0, 2.0), - FixedBinAxisItem::create(3, 0.0, 3.0)); - const std::vector<double> expected_content = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0}; - data_item->setContent(expected_content); - - // inserting graph item - auto colormap_item = model.insertItem<ColorMapItem>(viewport_item); - - MockWidgetForItem widget(viewport_item); - - EXPECT_CALL(widget, onDataChange(_, _)).Times(0); - EXPECT_CALL(widget, onPropertyChange(_, _)).Times(0); - EXPECT_CALL(widget, onChildPropertyChange(colormap_item, ColorMapItem::P_LINK)).Times(1); - EXPECT_CALL(widget, onItemInserted(_, _)).Times(0); - EXPECT_CALL(widget, onAboutToRemoveItem(_, _)).Times(0); - - // triggering action - colormap_item->setDataItem(data_item); -} diff --git a/mvvm/tests/testmodel/comboproperty.test.cpp b/mvvm/tests/testmodel/comboproperty.test.cpp deleted file mode 100644 index 5f8bffdf582..00000000000 --- a/mvvm/tests/testmodel/comboproperty.test.cpp +++ /dev/null @@ -1,422 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/comboproperty.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/comboproperty.h" -#include "mvvm/model/comparators.h" -#include <stdexcept> - -using namespace ModelView; - -class ComboPropertyTest : public ::testing::Test { -public: - ~ComboPropertyTest(); -}; - -ComboPropertyTest::~ComboPropertyTest() = default; - -TEST_F(ComboPropertyTest, initialState) -{ - ComboProperty combo; - EXPECT_EQ(combo.value(), ""); - EXPECT_EQ(combo.values(), std::vector<std::string>()); - EXPECT_EQ(combo.toolTips(), std::vector<std::string>()); - EXPECT_EQ(combo.currentIndex(), -1); - EXPECT_EQ(combo.stringOfValues(), ""); - EXPECT_EQ(combo.selectedIndices(), std::vector<int>()); - EXPECT_EQ(combo.label(), "None"); -} - -TEST_F(ComboPropertyTest, createFrom) -{ - // from vector of values, first item should be selected - std::vector<std::string> expected{"a1", "a2"}; - ComboProperty combo = ComboProperty::createFrom(expected); - EXPECT_EQ(combo.values(), expected); - EXPECT_EQ(combo.currentIndex(), 0); - EXPECT_EQ(combo.value(), "a1"); - EXPECT_EQ(combo.selectedIndices(), std::vector<int>({0})); - - // from vector of values, selection provided - expected = {"b1", "b2", "b3"}; - combo = ComboProperty::createFrom(expected, "b2"); - EXPECT_EQ(combo.values(), expected); - EXPECT_EQ(combo.currentIndex(), 1); - EXPECT_EQ(combo.value(), "b2"); - EXPECT_EQ(combo.selectedIndices(), std::vector<int>({1})); -} - -TEST_F(ComboPropertyTest, setValue) -{ - std::vector<std::string> expected{"a1", "a2"}; - ComboProperty combo = ComboProperty::createFrom(expected); - - // setting second value - combo.setValue("a2"); - EXPECT_EQ(combo.values(), expected); - EXPECT_EQ(combo.currentIndex(), 1); - EXPECT_EQ(combo.value(), "a2"); - EXPECT_EQ(combo.selectedIndices(), std::vector<int>({1})); - - // setting non-existing value - EXPECT_THROW(combo.setValue("c0"), std::runtime_error); - EXPECT_EQ(combo.values(), expected); - EXPECT_EQ(combo.currentIndex(), 1); - EXPECT_EQ(combo.value(), "a2"); - EXPECT_EQ(combo.selectedIndices(), std::vector<int>({1})); -} - -TEST_F(ComboPropertyTest, setCurrentIndex) -{ - std::vector<std::string> expected{"c1", "c2", "c3"}; - ComboProperty combo = ComboProperty::createFrom(expected); - - EXPECT_EQ(combo.currentIndex(), 0); - EXPECT_EQ(combo.selectedIndices(), std::vector<int>({0})); - EXPECT_EQ(combo.values(), expected); - - combo.setCurrentIndex(1); - EXPECT_EQ(combo.value(), std::string("c2")); - EXPECT_EQ(combo.selectedIndices(), std::vector<int>({1})); - - // setting unexpected index - EXPECT_THROW(combo.setCurrentIndex(3), std::runtime_error); - EXPECT_EQ(combo.value(), std::string("c2")); - EXPECT_EQ(combo.selectedIndices(), std::vector<int>({1})); -} - -TEST_F(ComboPropertyTest, setValues) -{ - // seting values through stream - std::vector<std::string> expectedValues{"a1", "a2"}; - ComboProperty combo = ComboProperty::createFrom(expectedValues); - - EXPECT_EQ(combo.values(), expectedValues); - EXPECT_EQ(combo.value(), std::string("a1")); - EXPECT_EQ(combo.currentIndex(), 0); - EXPECT_EQ(combo.selectedIndices(), std::vector<int>({0})); - - // setting values from setter, old values have to be overriden - std::vector<std::string> newValues{"b1", "b2", "b3"}; - combo.setValues(newValues); - EXPECT_EQ(combo.value(), std::string("b1")); - EXPECT_EQ(combo.values(), newValues); - EXPECT_EQ(combo.currentIndex(), 0); - EXPECT_EQ(combo.selectedIndices(), std::vector<int>({0})); - - // setting new/old values through setter, old value should be preserved - newValues = {"c1", "b1", "c2"}; - combo.setValues(newValues); - EXPECT_EQ(combo.value(), std::string("b1")); - EXPECT_EQ(combo.values(), newValues); - EXPECT_EQ(combo.currentIndex(), 1); - EXPECT_EQ(combo.selectedIndices(), std::vector<int>({1})); - - // setting empty list shouldn't change anything - std::vector<std::string> empty; - combo.setValues(empty); - EXPECT_EQ(combo.value(), std::string("b1")); - EXPECT_EQ(combo.values(), newValues); - EXPECT_EQ(combo.currentIndex(), 1); - EXPECT_EQ(combo.selectedIndices(), std::vector<int>({1})); -} - -TEST_F(ComboPropertyTest, setSelected) -{ - std::vector<std::string> expectedValues = {"a1", "a2", "a3"}; - ComboProperty combo = ComboProperty::createFrom(expectedValues); - - EXPECT_EQ(combo.currentIndex(), 0); - EXPECT_EQ(combo.value(), "a1"); - EXPECT_EQ(combo.values(), expectedValues); - EXPECT_EQ(combo.selectedIndices(), std::vector<int>({0})); - EXPECT_EQ(combo.selectedValues(), std::vector<std::string>({"a1"})); - - // selecting already selected element, nothing should change - combo.setSelected(0); - EXPECT_EQ(combo.currentIndex(), 0); - EXPECT_EQ(combo.value(), "a1"); - EXPECT_EQ(combo.values(), expectedValues); - EXPECT_EQ(combo.selectedIndices(), std::vector<int>({0})); - EXPECT_EQ(combo.selectedValues(), std::vector<std::string>({"a1"})); - - // deselecting index - combo.setSelected(0, false); - EXPECT_EQ(combo.currentIndex(), -1); - EXPECT_EQ(combo.value(), ""); - EXPECT_EQ(combo.values(), expectedValues); - EXPECT_EQ(combo.selectedIndices(), std::vector<int>()); - EXPECT_EQ(combo.selectedValues(), std::vector<std::string>()); - - // selecting two indeces - combo.setSelected(1, true); - combo.setSelected(2, true); - EXPECT_EQ(combo.currentIndex(), 1); - EXPECT_EQ(combo.value(), "a2"); - EXPECT_EQ(combo.values(), expectedValues); - EXPECT_EQ(combo.selectedIndices(), std::vector<int>({1, 2})); - EXPECT_EQ(combo.selectedValues(), std::vector<std::string>({"a2", "a3"})); - - // selecting by name - combo.setSelected("a2", false); - combo.setSelected("a1", true); - EXPECT_EQ(combo.currentIndex(), 0); - EXPECT_EQ(combo.value(), "a1"); - EXPECT_EQ(combo.values(), expectedValues); - EXPECT_EQ(combo.selectedIndices(), std::vector<int>({0, 2})); - EXPECT_EQ(combo.selectedValues(), std::vector<std::string>({"a1", "a3"})); - - // setting current index invalidates selection - combo.setCurrentIndex(1); - EXPECT_EQ(combo.currentIndex(), 1); - EXPECT_EQ(combo.value(), "a2"); - EXPECT_EQ(combo.values(), expectedValues); - EXPECT_EQ(combo.selectedIndices(), std::vector<int>({1})); - EXPECT_EQ(combo.selectedValues(), std::vector<std::string>({"a2"})); -} - -TEST_F(ComboPropertyTest, fromStream) -{ - ComboProperty combo = ComboProperty() << "a1" - << "a2"; - std::vector<std::string> expected{"a1", "a2"}; - EXPECT_EQ(combo.values(), expected); - EXPECT_EQ(combo.currentIndex(), 0); - EXPECT_EQ(combo.value(), "a1"); - EXPECT_EQ(combo.selectedIndices(), std::vector<int>({0})); - - // adding more - combo << "c0"; - expected = {"a1", "a2", "c0"}; - EXPECT_EQ(combo.values(), expected); - EXPECT_EQ(combo.currentIndex(), 0); - EXPECT_EQ(combo.value(), "a1"); - EXPECT_EQ(combo.selectedIndices(), std::vector<int>({0})); - - // setting another index, adding more, state should be preserved - combo.setCurrentIndex(1); - combo.setSelected(2, true); - EXPECT_EQ(combo.values(), expected); - EXPECT_EQ(combo.currentIndex(), 1); - EXPECT_EQ(combo.value(), "a2"); - EXPECT_EQ(combo.selectedIndices(), std::vector<int>({1, 2})); - combo << "c1"; - expected = {"a1", "a2", "c0", "c1"}; - EXPECT_EQ(combo.values(), expected); - EXPECT_EQ(combo.currentIndex(), 1); -} - -TEST_F(ComboPropertyTest, fromVectorStream) -{ - std::vector<std::string> expected{"a1", "a2"}; - ComboProperty combo = ComboProperty::createFrom(expected); - combo.setSelected(0, true); - combo.setSelected(1, true); - - EXPECT_EQ(combo.values(), expected); - EXPECT_EQ(combo.currentIndex(), 0); - EXPECT_EQ(combo.value(), "a1"); - EXPECT_EQ(combo.selectedIndices(), std::vector<int>({0, 1})); - - // adding from vector stream, old selection state should be preserved - std::vector<std::string> more{"c1", "c2"}; - combo << more; - expected = {"a1", "a2", "c1", "c2"}; - EXPECT_EQ(combo.values(), expected); - EXPECT_EQ(combo.currentIndex(), 0); - EXPECT_EQ(combo.value(), "a1"); - EXPECT_EQ(combo.selectedIndices(), std::vector<int>({0, 1})); -} - -TEST_F(ComboPropertyTest, setSringOfValues) -{ - std::vector<std::string> expectedValues = {"a1", "a2"}; - ComboProperty combo = ComboProperty::createFrom(expectedValues); - - EXPECT_EQ(combo.stringOfValues(), std::string("a1;a2")); - EXPECT_EQ(combo.value(), std::string("a1")); - EXPECT_EQ(combo.currentIndex(), 0); - EXPECT_EQ(combo.selectedIndices(), std::vector<int>({0})); - - // setting string of values, current value should change - std::string stringOfValues("b1;b2;b3"); - combo.setStringOfValues(stringOfValues); - EXPECT_EQ(combo.stringOfValues(), stringOfValues); - EXPECT_EQ(combo.value(), std::string("b1")); - EXPECT_EQ(combo.values(), std::vector<std::string>({"b1", "b2", "b3"})); - EXPECT_EQ(combo.currentIndex(), 0); - EXPECT_EQ(combo.selectedIndices(), std::vector<int>({0})); - - // setting new string of values, containing current value. Current values should remain. - stringOfValues = std::string("c1;b1;c3"); - combo.setStringOfValues(stringOfValues); - EXPECT_EQ(combo.stringOfValues(), stringOfValues); - EXPECT_EQ(combo.value(), std::string("b1")); - EXPECT_EQ(combo.currentIndex(), 1); - EXPECT_EQ(combo.selectedIndices(), std::vector<int>({1})); -} - -TEST_F(ComboPropertyTest, setStringOfSelections) -{ - ComboProperty combo; - EXPECT_EQ(combo.stringOfSelections(), ""); - - // checking the content of stringOfSelections - combo.setValues(std::vector<std::string>({"a1", "a2", "a3"})); - EXPECT_EQ(combo.selectedIndices(), std::vector<int>({0})); - EXPECT_EQ(combo.currentIndex(), 0); - EXPECT_EQ(combo.stringOfSelections(), "0"); - - combo.setSelected(2, true); - EXPECT_EQ(combo.selectedIndices(), std::vector<int>({0, 2})); - EXPECT_EQ(combo.currentIndex(), 0); - EXPECT_EQ(combo.stringOfSelections(), "0,2"); - - // setting string of selections - combo.setStringOfSelections(""); - EXPECT_EQ(combo.selectedIndices(), std::vector<int>({})); - EXPECT_EQ(combo.currentIndex(), -1); - EXPECT_EQ(combo.stringOfSelections(), ""); - - combo.setStringOfSelections("1,2"); - EXPECT_EQ(combo.selectedIndices(), std::vector<int>({1, 2})); - EXPECT_EQ(combo.currentIndex(), 1); - EXPECT_EQ(combo.stringOfSelections(), "1,2"); - - combo.setStringOfSelections("0,42"); - EXPECT_EQ(combo.selectedIndices(), std::vector<int>({0})); - EXPECT_EQ(combo.currentIndex(), 0); - EXPECT_EQ(combo.stringOfSelections(), "0"); -} - -TEST_F(ComboPropertyTest, comboEqualityDiffIndex) -{ - ComboProperty c1 = ComboProperty::createFrom({"a1", "a2"}); - ComboProperty c2 = ComboProperty::createFrom({"a1", "a2"}); - - c1.setValue("a1"); - c2.setValue("a2"); - EXPECT_TRUE(c1 != c2); - - c2.setValue("a1"); - EXPECT_TRUE(c1 == c2); -} - -TEST_F(ComboPropertyTest, comboEqualityDiffList) -{ - ComboProperty c1; - ComboProperty c2; - EXPECT_TRUE(c1 == c2); - - c1 << "a1" - << "a2"; - c2 << "a1" - << "a2"; - EXPECT_TRUE(c1 == c2); - EXPECT_FALSE(c1 != c2); - - c2 << "a3"; - EXPECT_TRUE(c1 != c2); - EXPECT_FALSE(c1 == c2); - c2.setValue("a2"); - EXPECT_TRUE(c1 != c2); - EXPECT_FALSE(c1 == c2); - - c1 << "a3"; - c1.setValue("a2"); - EXPECT_TRUE(c1 == c2); - EXPECT_FALSE(c1 != c2); - - // with selection indices - c1 = ComboProperty() << "a1" - << "a2" - << "a3"; - c2 = ComboProperty() << "a1" - << "a2" - << "a3"; - EXPECT_TRUE(c1 == c2); - - c2.setSelected(0, false); - c2.setSelected(2, true); - EXPECT_TRUE(c1 != c2); - - c1.setStringOfSelections("2"); - c2.setStringOfSelections("2"); - EXPECT_TRUE(c1 == c2); -} - -//! Check equality of ComboProperty's variants. -//! If comparators are not registered, the behavior is undefined. - -TEST_F(ComboPropertyTest, variantEqualityDiffLists) -{ - if (ModelView::Comparators::registered()) { - ComboProperty c1 = ComboProperty() << "a1" - << "a2"; - ComboProperty c2 = ComboProperty() << "a1" - << "a2"; - - EXPECT_TRUE(QVariant::fromValue(c1) == QVariant::fromValue(c2)); - - c2 << "a3"; - c2.setValue("a2"); - - EXPECT_TRUE(QVariant::fromValue(c1) != QVariant::fromValue(c2)); - EXPECT_FALSE(QVariant::fromValue(c1) == QVariant::fromValue(c2)); - - c1 << "a3"; - c1.setValue("a2"); - - EXPECT_TRUE(QVariant::fromValue(c1) == QVariant::fromValue(c2)); - EXPECT_FALSE(QVariant::fromValue(c1) != QVariant::fromValue(c2)); - - c1.setStringOfSelections("0"); - c2.setStringOfSelections("1"); - EXPECT_TRUE(QVariant::fromValue(c1) != QVariant::fromValue(c2)); - EXPECT_FALSE(QVariant::fromValue(c1) == QVariant::fromValue(c2)); - } -} - -//! Check equality of ComboProperty's variants when only selected item differs. - -TEST_F(ComboPropertyTest, variantEqualityDiffIndex) -{ - if (ModelView::Comparators::registered()) { - ComboProperty c1 = ComboProperty::createFrom({"a1", "a2"}); - ComboProperty c2 = ComboProperty::createFrom({"a1", "a2"}); - - c1.setValue("a1"); - c2.setValue("a2"); - - EXPECT_FALSE(QVariant::fromValue(c1) == QVariant::fromValue(c2)); - EXPECT_TRUE(QVariant::fromValue(c1) != QVariant::fromValue(c2)); - - c2.setValue("a1"); - EXPECT_TRUE(QVariant::fromValue(c1) == QVariant::fromValue(c2)); - EXPECT_FALSE(QVariant::fromValue(c1) != QVariant::fromValue(c2)); - } - - if (ModelView::Comparators::registered()) { - ComboProperty c1 = ComboProperty::createFrom({"a1", "a2"}); - ComboProperty c2 = ComboProperty::createFrom({"a1", "a2"}); - - c1.setValue("a1"); - c2.setValue("a2"); - - std::vector<QVariant> variants = {QVariant::fromValue(c1), QVariant::fromValue(c2)}; - - EXPECT_FALSE(variants[0] == variants[1]); - EXPECT_TRUE(variants[0] != variants[1]); - } -} diff --git a/mvvm/tests/testmodel/compatibilityutils.test.cpp b/mvvm/tests/testmodel/compatibilityutils.test.cpp deleted file mode 100644 index d52f0bef80d..00000000000 --- a/mvvm/tests/testmodel/compatibilityutils.test.cpp +++ /dev/null @@ -1,74 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/compatibilityutils.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/groupitem.h" -#include "mvvm/model/mvvm_types.h" -#include "mvvm/model/propertyitem.h" -#include "mvvm/model/sessionitemcontainer.h" -#include "mvvm/model/sessionitemdata.h" -#include "mvvm/serialization/compatibilityutils.h" - -using namespace ModelView; - -//! Test of CompatibilityUtilsTest. - -class CompatibilityUtilsTest : public ::testing::Test { -public: - ~CompatibilityUtilsTest(); -}; - -CompatibilityUtilsTest::~CompatibilityUtilsTest() = default; - -//! Testing IsCompatibleSingleProperty. - -TEST_F(CompatibilityUtilsTest, IsCompatibleSinglePropertyTag) -{ - TagInfo tag = TagInfo::propertyTag("thickness", GUI::Constants::PropertyType); - SessionItemContainer container(tag); - - // to be compatible, container should have PropertyItem in it already - EXPECT_FALSE(Compatibility::IsCompatibleSinglePropertyTag(container, tag)); - - EXPECT_TRUE(container.insertItem(new PropertyItem, 0)); - EXPECT_TRUE(Compatibility::IsCompatibleSinglePropertyTag(container, tag)); -} - -//! Testing IsCompatibleSingleProperty. - -TEST_F(CompatibilityUtilsTest, IsCompatibleUniversalTag) -{ - TagInfo tag = TagInfo::universalTag("layers"); - SessionItemContainer container(tag); - - // to be compatible, container should be empty - EXPECT_TRUE(Compatibility::IsCompatibleUniversalTag(container, tag)); - - EXPECT_TRUE(container.insertItem(new PropertyItem, 0)); - EXPECT_FALSE(Compatibility::IsCompatibleUniversalTag(container, tag)); -} - -//! Testing IsCompatibleSingleProperty. - -TEST_F(CompatibilityUtilsTest, IsCompatibleGroupTag) -{ - TagInfo tag = TagInfo::universalTag(GroupItem::T_GROUP_ITEMS); - SessionItemContainer container(tag); - - // to be compatible, container should be non-empty - EXPECT_FALSE(Compatibility::IsCompatibleGroupTag(container, tag)); - - EXPECT_TRUE(container.insertItem(new PropertyItem, 0)); - EXPECT_TRUE(Compatibility::IsCompatibleGroupTag(container, tag)); -} diff --git a/mvvm/tests/testmodel/compounditem.test.cpp b/mvvm/tests/testmodel/compounditem.test.cpp deleted file mode 100644 index 45a95ffc233..00000000000 --- a/mvvm/tests/testmodel/compounditem.test.cpp +++ /dev/null @@ -1,253 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/compounditem.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/compounditem.h" -#include "mvvm/model/customvariants.h" -#include "mvvm/model/itemutils.h" -#include "mvvm/model/sessionmodel.h" -#include "test_utils.h" -#include <memory> -#include <stdexcept> - -using namespace ModelView; - -namespace { -const std::string property_name("name"); -} - -//! Test of CompountItem machinery (property children etc). - -class CompoundItemTest : public ::testing::Test { -}; - -TEST_F(CompoundItemTest, initialState) -{ - CompoundItem item; - EXPECT_EQ(item.childrenCount(), 0); -} - -TEST_F(CompoundItemTest, addIntProperty) -{ - CompoundItem item; - - const int expected = 42; - auto propertyItem = item.addProperty(property_name, expected); - EXPECT_TRUE(Utils::HasTag(item, "name")); - - EXPECT_EQ(propertyItem->modelType(), GUI::Constants::PropertyType); - EXPECT_TRUE(Utils::IsIntVariant(propertyItem->data<QVariant>())); - EXPECT_EQ(propertyItem->displayName(), property_name); - EXPECT_EQ(propertyItem->data<int>(), expected); - - EXPECT_FALSE(propertyItem->data<QVariant>(ItemDataRole::LIMITS).isValid()); -} - -TEST_F(CompoundItemTest, setIntProperty) -{ - CompoundItem item; - auto propertyItem = item.addProperty(property_name, 41); - - const int expected = 42; - item.setProperty(property_name, expected); - - EXPECT_EQ(item.property<int>(property_name), expected); - EXPECT_EQ(propertyItem->data<int>(), expected); -} - -TEST_F(CompoundItemTest, addDoubleProperty) -{ - CompoundItem item; - - const double expected = 42.1; - auto propertyItem = item.addProperty(property_name, expected); - EXPECT_TRUE(Utils::HasTag(item, property_name)); - - EXPECT_EQ(propertyItem->modelType(), GUI::Constants::PropertyType); - EXPECT_TRUE(Utils::IsDoubleVariant(propertyItem->data<QVariant>())); - EXPECT_EQ(propertyItem->displayName(), property_name); - EXPECT_EQ(propertyItem->data<double>(), expected); - - EXPECT_TRUE(propertyItem->data<QVariant>(ItemDataRole::LIMITS).isValid()); - - // limits should be "negative 'unlimited' by default - auto limits = propertyItem->data<RealLimits>(ItemDataRole::LIMITS); - EXPECT_FALSE(limits.hasLowerLimit()); - EXPECT_FALSE(limits.hasUpperLimit()); -} - -TEST_F(CompoundItemTest, setDoubleProperty) -{ - CompoundItem item; - auto propertyItem = item.addProperty(property_name, 41.11); - - const double expected = 42.0; - item.setProperty(property_name, expected); - - EXPECT_EQ(item.property<double>(property_name), expected); - EXPECT_EQ(propertyItem->data<double>(), expected); -} - -TEST_F(CompoundItemTest, addCharProperty) -{ - CompoundItem item; - - auto propertyItem = item.addProperty(property_name, "abc"); - EXPECT_TRUE(Utils::HasTag(item, property_name)); - - EXPECT_EQ(propertyItem->modelType(), GUI::Constants::PropertyType); - EXPECT_TRUE(Utils::IsStdStringVariant(propertyItem->data<QVariant>())); - EXPECT_EQ(propertyItem->data<std::string>(), std::string("abc")); - - EXPECT_FALSE(propertyItem->data<QVariant>(ItemDataRole::LIMITS).isValid()); -} - -TEST_F(CompoundItemTest, setCharProperty) -{ - CompoundItem item; - auto propertyItem = item.addProperty(property_name, "aaa"); - - const char* expected{"bbb"}; - item.setProperty(property_name, expected); - - EXPECT_EQ(item.property<std::string>(property_name), std::string(expected)); - EXPECT_EQ(propertyItem->data<std::string>(), std::string(expected)); -} - -TEST_F(CompoundItemTest, addStringProperty) -{ - CompoundItem item; - - auto propertyItem = item.addProperty(property_name, std::string("abc")); - EXPECT_TRUE(Utils::HasTag(item, property_name)); - - EXPECT_EQ(propertyItem->modelType(), GUI::Constants::PropertyType); - EXPECT_TRUE(Utils::IsStdStringVariant(propertyItem->data<QVariant>())); - EXPECT_EQ(propertyItem->data<std::string>(), std::string("abc")); - - EXPECT_FALSE(propertyItem->data<QVariant>(ItemDataRole::LIMITS).isValid()); -} - -TEST_F(CompoundItemTest, setStringProperty) -{ - CompoundItem item; - auto propertyItem = item.addProperty(property_name, std::string("aaa")); - - const std::string expected{"bbb"}; - item.setProperty(property_name, expected); - - EXPECT_EQ(item.property<std::string>(property_name), expected); - EXPECT_EQ(propertyItem->data<std::string>(), expected); -} - -TEST_F(CompoundItemTest, addBoolProperty) -{ - CompoundItem item; - - const bool expected = true; - auto propertyItem = item.addProperty(property_name, expected); - EXPECT_TRUE(Utils::HasTag(item, property_name)); - - EXPECT_EQ(propertyItem->modelType(), GUI::Constants::PropertyType); - EXPECT_TRUE(Utils::IsBoolVariant(propertyItem->data<QVariant>())); - EXPECT_EQ(propertyItem->data<bool>(), expected); - - EXPECT_FALSE(propertyItem->data<QVariant>(ItemDataRole::LIMITS).isValid()); -} - -TEST_F(CompoundItemTest, setBoolProperty) -{ - CompoundItem item; - auto propertyItem = item.addProperty(property_name, false); - - const bool expected = true; - item.setProperty(property_name, expected); - - EXPECT_EQ(item.property<bool>(property_name), expected); - EXPECT_EQ(propertyItem->data<bool>(), expected); -} - -TEST_F(CompoundItemTest, itemAccess) -{ - const std::string tag = "tag"; - - // creating parent with one tag - SessionItem parent; - parent.registerTag(TagInfo::universalTag(tag)); - - // inserting two children - auto property = new PropertyItem; - parent.insertItem(property, {tag, 0}); - - EXPECT_TRUE(parent.item<PropertyItem>(tag) == property); - EXPECT_THROW(parent.item<CompoundItem>(tag), std::runtime_error); -} - -TEST_F(CompoundItemTest, itemVectorAccess) -{ - const std::string tag = "tag"; - - // creating parent with one tag - SessionItem parent; - parent.registerTag(TagInfo::universalTag(tag)); - - // inserting two children - auto property1 = new PropertyItem; - auto property2 = new PropertyItem; - parent.insertItem(property1, TagRow::append(tag)); - parent.insertItem(property2, TagRow::append(tag)); - - auto items = parent.items<PropertyItem>(tag); - std::vector<PropertyItem*> expected = {property1, property2}; - EXPECT_EQ(items, expected); - EXPECT_EQ(parent.items<CompoundItem>(tag).size(), 0); -} - -//! Tests automatic index addition to default display name. - -TEST_F(CompoundItemTest, displayNameIndexAddition) -{ - const std::string tag = "tag"; - - // creating parent with one tag - SessionItem parent; - parent.registerTag(TagInfo::universalTag(tag)); - - // inserting two children - auto child0 = new CompoundItem; - parent.insertItem(child0, TagRow::append(tag)); - auto child1 = new CompoundItem; - parent.insertItem(child1, TagRow::append(tag)); - - // Default display names of items of the same type should have indices - EXPECT_EQ(child0->displayName(), GUI::Constants::CompoundItemType + "0"); - EXPECT_EQ(child1->displayName(), GUI::Constants::CompoundItemType + "1"); - - // however, if children have custom display name, they should remain intact - child0->setDisplayName("Jekyll"); - child1->setDisplayName("Hyde"); - EXPECT_EQ(child0->displayName(), "Jekyll"); - EXPECT_EQ(child1->displayName(), "Hyde"); -} - -//! Test all children method. -//! Property items are also children. - -TEST_F(CompoundItemTest, children) -{ - CompoundItem item; - EXPECT_TRUE(item.children().empty()); - auto propertyItem = item.addProperty(property_name, false); - EXPECT_EQ(item.children(), std::vector<SessionItem*>({propertyItem})); -} diff --git a/mvvm/tests/testmodel/containeritem.test.cpp b/mvvm/tests/testmodel/containeritem.test.cpp deleted file mode 100644 index f76fad8b4bf..00000000000 --- a/mvvm/tests/testmodel/containeritem.test.cpp +++ /dev/null @@ -1,48 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/containeritem.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/standarditems/containeritem.h" -#include "test_utils.h" -#include <memory> - -using namespace ModelView; - -//! Test of ContainerItem. - -class ContainerItemTest : public ::testing::Test { -public: - ~ContainerItemTest(); -}; - -ContainerItemTest::~ContainerItemTest() = default; - -TEST_F(ContainerItemTest, initialState) -{ - ContainerItem item; - EXPECT_EQ(item.size(), 0); - EXPECT_TRUE(item.empty()); -} - -TEST_F(ContainerItemTest, isEmpty) -{ - ContainerItem item; - - // inserting two children - auto property = new PropertyItem; - item.insertItem(property, {"", 0}); - - EXPECT_EQ(item.size(), 1); - EXPECT_FALSE(item.empty()); -} diff --git a/mvvm/tests/testmodel/containerutils.test.cpp b/mvvm/tests/testmodel/containerutils.test.cpp deleted file mode 100644 index 4d35cdf7230..00000000000 --- a/mvvm/tests/testmodel/containerutils.test.cpp +++ /dev/null @@ -1,83 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/containerutils.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/utils/containerutils.h" -#include <complex> - -using namespace ModelView; - -//! Tests of container utils. - -class ContainerUtilsTest : public ::testing::Test { -public: - ~ContainerUtilsTest(); -}; - -ContainerUtilsTest::~ContainerUtilsTest() = default; - -TEST_F(ContainerUtilsTest, isUniquePtr) -{ - EXPECT_FALSE(Utils::is_unique_ptr<int>::value); - EXPECT_TRUE(Utils::is_unique_ptr<std::unique_ptr<int>>::value); -} - -TEST_F(ContainerUtilsTest, IndexOfItem) -{ - // searching in vector of integers - std::vector<int> vv{1, 7, 5}; - EXPECT_EQ(Utils::IndexOfItem(vv, 1), 0); - EXPECT_EQ(Utils::IndexOfItem(vv, 10), -1); - EXPECT_EQ(Utils::IndexOfItem(vv, 5), 2); - EXPECT_EQ(Utils::IndexOfItem(vv.begin(), vv.end(), 7), 1); - - // searching in vector of SessionItem's - std::vector<SessionItem*> items{new SessionItem, new SessionItem, new SessionItem}; - SessionItem other; - EXPECT_EQ(Utils::IndexOfItem(items, items[0]), 0); - EXPECT_EQ(Utils::IndexOfItem(items, items[1]), 1); - EXPECT_EQ(Utils::IndexOfItem(items, items[2]), 2); - EXPECT_EQ(Utils::IndexOfItem(items, &other), -1); - for (auto x : items) - delete x; - - // searching in vector of unique_ptr - std::vector<std::unique_ptr<SessionItem>> unique_items; - unique_items.emplace_back(std::make_unique<SessionItem>()); - unique_items.emplace_back(std::make_unique<SessionItem>()); - EXPECT_EQ(Utils::IndexOfItem(unique_items, unique_items[0].get()), 0); - EXPECT_EQ(Utils::IndexOfItem(unique_items, unique_items[1].get()), 1); - EXPECT_EQ(Utils::IndexOfItem(unique_items, &other), -1); -} - -TEST_F(ContainerUtilsTest, Real) -{ - std::vector<std::complex<double>> data = {{1.0, 10.0}, {2.0, 20.0}}; - EXPECT_EQ(Utils::Real(data), (std::vector<double>{1.0, 2.0})); - EXPECT_EQ(Utils::Imag(data), (std::vector<double>{10.0, 20.0})); -} - -TEST_F(ContainerUtilsTest, UniqueWithOrder) -{ - std::vector<int> data = {1, 42, 1, 6, 43, 6}; - EXPECT_EQ(Utils::UniqueWithOrder(data), (std::vector<int>{1, 42, 6, 43})); -} - -TEST_F(ContainerUtilsTest, Contains) -{ - std::vector<int> data = {1, 42, 1, 6, 43, 6}; - EXPECT_TRUE(Utils::Contains(data, 42)); - EXPECT_FALSE(Utils::Contains(data, 99)); -} diff --git a/mvvm/tests/testmodel/copyitemcommand.test.cpp b/mvvm/tests/testmodel/copyitemcommand.test.cpp deleted file mode 100644 index 0512ed6efd1..00000000000 --- a/mvvm/tests/testmodel/copyitemcommand.test.cpp +++ /dev/null @@ -1,90 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/copyitemcommand.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/commands/copyitemcommand.h" -#include "mvvm/model/compounditem.h" -#include "mvvm/model/itemutils.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/model/taginfo.h" -#include <stdexcept> - -using namespace ModelView; - -class CopyItemCommandTest : public ::testing::Test { -public: - ~CopyItemCommandTest(); -}; - -CopyItemCommandTest::~CopyItemCommandTest() = default; - -TEST_F(CopyItemCommandTest, copyChild) -{ - SessionModel model; - - // parent with children and data - auto parent = model.insertItem<SessionItem>(model.rootItem(), {"", 0}); - parent->registerTag(TagInfo::universalTag("tag1"), /*set_as_default*/ true); - - auto child0 = model.insertItem<SessionItem>(parent, "tag1"); - child0->setData(42.0); - auto child1 = model.insertItem<SessionItem>(parent, "tag1"); - child1->setData(43.0); - - // making copy of child - auto command = std::make_unique<CopyItemCommand>(child1, parent, TagRow{"tag1", 1}); - command->execute(); - - // checking that parent has now three children - auto copy = std::get<SessionItem*>(command->result()); - EXPECT_FALSE(command->isObsolete()); - EXPECT_TRUE(copy != nullptr); - EXPECT_EQ(parent->childrenCount(), 3); - std::vector<SessionItem*> expected = {child0, copy, child1}; - EXPECT_EQ(parent->getItems("tag1"), expected); - EXPECT_EQ(copy->data<double>(), 43.0); - - // undoing command - command->undo(); - expected = {child0, child1}; - EXPECT_EQ(parent->getItems("tag1"), expected); - EXPECT_FALSE(command->isObsolete()); -} - -//! Attempt to copy item to invalid tag - -TEST_F(CopyItemCommandTest, invalidCopyAttempt) -{ - SessionModel model; - - // parent with children and data - auto parent = model.insertItem<CompoundItem>(model.rootItem()); - parent->addProperty("thickness", 42.0); - parent->registerTag(TagInfo::universalTag("tag1"), /*set_as_default*/ true); - - auto child0 = model.insertItem<SessionItem>(parent); - child0->setData(42.0); - - // making copy of child - auto command = std::make_unique<CopyItemCommand>(child0, parent, TagRow{"thickness", 0}); - command->execute(); - - // checking that parent has now three children - EXPECT_TRUE(command->isObsolete()); - EXPECT_EQ(std::get<SessionItem*>(command->result()), nullptr); - - // undoing of obsolete command is not possible - EXPECT_THROW(command->undo(), std::runtime_error); -} diff --git a/mvvm/tests/testmodel/customvariants.test.cpp b/mvvm/tests/testmodel/customvariants.test.cpp deleted file mode 100644 index 57a5df4d6f9..00000000000 --- a/mvvm/tests/testmodel/customvariants.test.cpp +++ /dev/null @@ -1,248 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/customvariants.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/comboproperty.h" -#include "mvvm/model/customvariants.h" -#include "mvvm/model/externalproperty.h" -#include "mvvm/model/itemutils.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/model/taginfo.h" -#include "mvvm/model/variant_constants.h" -#include <QColor> -#include <functional> -#include <memory> - -using namespace ModelView; - -class CustomVariantsTest : public ::testing::Test { -public: - ~CustomVariantsTest(); - - template <typename T> QVariant variantFromArgument(const T& value) - { - return QVariant::fromValue(value); - } -}; - -CustomVariantsTest::~CustomVariantsTest() = default; - -//! To keep under control implicit type conversion. - -TEST_F(CustomVariantsTest, VariantFromTemplateArgument) -{ - EXPECT_EQ(variantFromArgument(true).typeName(), GUI::Constants::bool_type_name); - EXPECT_EQ(variantFromArgument(1).typeName(), GUI::Constants::int_type_name); - EXPECT_EQ(variantFromArgument(42.0).typeName(), GUI::Constants::double_type_name); - EXPECT_EQ(variantFromArgument(std::string("abc")).typeName(), GUI::Constants::string_type_name); -} - -//! Variant compatibility. - -TEST_F(CustomVariantsTest, VariantName) -{ - const std::vector<double> vec{1, 2}; - const ComboProperty combo = ComboProperty::createFrom({"a1", "a2", "s3"}); - EXPECT_EQ(Utils::VariantName(QVariant()), GUI::Constants::invalid_type_name); - EXPECT_EQ(Utils::VariantName(QVariant::fromValue(true)), GUI::Constants::bool_type_name); - EXPECT_EQ(Utils::VariantName(QVariant::fromValue(1)), GUI::Constants::int_type_name); - EXPECT_EQ(Utils::VariantName(QVariant::fromValue(42.0)), GUI::Constants::double_type_name); - EXPECT_EQ(Utils::VariantName(QVariant::fromValue(std::string("string"))), - GUI::Constants::string_type_name); - EXPECT_EQ(Utils::VariantName(QVariant::fromValue(vec)), - GUI::Constants::vector_double_type_name); - EXPECT_EQ(Utils::VariantName(QVariant::fromValue(combo)), - GUI::Constants::comboproperty_type_name); - EXPECT_EQ(Utils::VariantName(QVariant::fromValue(QColor(Qt::red))), - GUI::Constants::qcolor_type_name); - EXPECT_EQ(Utils::VariantName(QVariant::fromValue(ExternalProperty())), - GUI::Constants::extproperty_type_name); - EXPECT_EQ(Utils::VariantName(QVariant::fromValue(RealLimits())), - GUI::Constants::reallimits_type_name); -} - -//! Variant compatibility. - -TEST_F(CustomVariantsTest, CompatibleVariantTypes) -{ - QVariant undefined; - QVariant bool_variant = QVariant::fromValue(true); - QVariant int_variant = QVariant::fromValue(1); - QVariant double_variant = QVariant::fromValue(42.0); - QVariant string_variant = QVariant::fromValue(std::string("string")); - std::vector<double> vec{1, 2}; - QVariant vector_variant = QVariant::fromValue(vec); - ComboProperty combo = ComboProperty::createFrom({"a1", "a2", "s3"}); - QVariant combo_variant = QVariant::fromValue(combo); - QVariant color_variant = QVariant::fromValue(QColor(Qt::red)); - QVariant extprop_variant = QVariant::fromValue(ExternalProperty()); - QVariant limits_variant = QVariant::fromValue(RealLimits()); - - std::vector<QVariant> variants = {bool_variant, int_variant, double_variant, - string_variant, vector_variant, combo_variant, - color_variant, extprop_variant, limits_variant}; - for (size_t i = 0; i < variants.size(); ++i) { - EXPECT_TRUE(Utils::CompatibleVariantTypes(undefined, variants[i])); - EXPECT_FALSE(Utils::VariantType(undefined) == Utils::VariantType(variants[i])); - for (size_t j = 0; j < variants.size(); ++j) { - if (i == j) { - EXPECT_TRUE(Utils::VariantType(variants[i]) == Utils::VariantType(variants[j])); - EXPECT_TRUE(Utils::CompatibleVariantTypes(variants[i], variants[j])); - } else { - EXPECT_FALSE(Utils::CompatibleVariantTypes(variants[i], variants[j])); - EXPECT_FALSE(Utils::VariantType(variants[i]) == Utils::VariantType(variants[j])); - } - } - } -} - -//! Test variant equality reported by GUI::Session::ItemUtils::isTheSame - -TEST_F(CustomVariantsTest, IsTheSameVariant) -{ - const std::vector<double> vec1{1, 2}; - const std::vector<double> vec2{1, 2, 3}; - const ComboProperty combo1 = ComboProperty::createFrom({"a1", "a2"}); - const ComboProperty combo2 = ComboProperty::createFrom({"b1"}); - const ExternalProperty extprop1; - const ExternalProperty extprop2("abc", QColor(Qt::red), "123"); - const RealLimits lim1; - const RealLimits lim2 = RealLimits::limited(1.0, 2.0); - - ComboProperty combo3 = ComboProperty::createFrom({"e1", "e2"}); - ComboProperty combo4 = ComboProperty::createFrom({"e1", "e2"}); - combo3.setValue("e1"); - combo4.setValue("e2"); - - std::vector<QVariant> variants = {QVariant(), - QVariant::fromValue(true), - QVariant::fromValue(false), - QVariant::fromValue(1), - QVariant::fromValue(2), - QVariant::fromValue(42.0), - QVariant::fromValue(43.0), - QVariant::fromValue(std::string("string1")), - QVariant::fromValue(std::string("string2")), - QVariant::fromValue(vec1), - QVariant::fromValue(vec2), - QVariant::fromValue(combo1), - QVariant::fromValue(combo2), - QVariant::fromValue(QColor(Qt::red)), - QVariant::fromValue(QColor(Qt::green)), - QVariant::fromValue(extprop1), - QVariant::fromValue(extprop2), - QVariant::fromValue(lim1), - QVariant::fromValue(lim2), - QVariant::fromValue(combo3), - QVariant::fromValue(combo4)}; - - for (size_t i = 0; i < variants.size(); ++i) { - for (size_t j = 0; j < variants.size(); ++j) { - if (i == j) - EXPECT_TRUE(Utils::IsTheSame(variants[i], variants[j])); - else - EXPECT_FALSE(Utils::IsTheSame(variants[i], variants[j])); - } - } -} - -//! Checks if ComboProperty based variant is the same. - -TEST_F(CustomVariantsTest, IsTheSameComboProperty) -{ - ComboProperty combo1 = ComboProperty::createFrom({"a1", "a2"}); - ComboProperty combo2 = ComboProperty::createFrom({"a1", "a2"}); - - EXPECT_TRUE(Utils::IsTheSame(QVariant::fromValue(combo1), QVariant::fromValue(combo1))); - - combo1.setValue("a1"); - combo2.setValue("a2"); - EXPECT_FALSE(Utils::IsTheSame(QVariant::fromValue(combo1), QVariant::fromValue(combo2))); - - QVariant v1 = QVariant::fromValue(combo1); - QVariant v2 = QVariant::fromValue(combo2); - EXPECT_FALSE(Utils::IsTheSame(v1, v2)); -} - -//! Test toQtVAriant function. - -TEST_F(CustomVariantsTest, toQtVariant) -{ - // from Variant based on std::string to variant based on QString - QVariant stdstring_variant = QVariant::fromValue(std::string("abc")); - QVariant qstring_variant = QVariant::fromValue(QString("abc")); - QVariant converted = Utils::toQtVariant(stdstring_variant); - - EXPECT_FALSE(qstring_variant == stdstring_variant); - EXPECT_TRUE(qstring_variant == converted); - - // Double variant should be unchanged - QVariant value(42.0); - EXPECT_TRUE(Utils::toQtVariant(value) == QVariant::fromValue(42.0)); - - QVariant invalid; - EXPECT_FALSE(Utils::toQtVariant(invalid).isValid()); -} - -//! Test translation of variants - -TEST_F(CustomVariantsTest, toCustomVariant) -{ - // from Variant based on QString to variant based on std::string - QVariant stdstring_variant = QVariant::fromValue(std::string("abc")); - QVariant qstring_variant = QVariant::fromValue(QString("abc")); - QVariant converted = Utils::toCustomVariant(qstring_variant); - - EXPECT_FALSE(qstring_variant == stdstring_variant); - EXPECT_TRUE(stdstring_variant == converted); - - // Double variant should be unchanged - QVariant value(42.0); - EXPECT_TRUE(Utils::toCustomVariant(value) == QVariant::fromValue(42.0)); - - QVariant invalid; - EXPECT_FALSE(Utils::toCustomVariant(invalid).isValid()); -} - -//! Checks all functions related to variant types. - -// FIXME replace tests in loop with parameterized tests - -TEST_F(CustomVariantsTest, isVariantType) -{ - using is_variant_t = std::function<bool(const QVariant&)>; - - std::vector<std::pair<QVariant, is_variant_t>> data = { - {QVariant::fromValue(true), Utils::IsBoolVariant}, - {QVariant::fromValue(1), Utils::IsIntVariant}, - {QVariant::fromValue(42.0), Utils::IsDoubleVariant}, - {QVariant::fromValue(ComboProperty()), Utils::IsComboVariant}, - {QVariant::fromValue(std::string("string1")), Utils::IsStdStringVariant}, - {QVariant::fromValue(std::vector<double>({1, 2})), Utils::IsDoubleVectorVariant}, - {QVariant::fromValue(QColor(Qt::red)), Utils::IsColorVariant}, - {QVariant::fromValue(ExternalProperty()), Utils::IsExtPropertyVariant}, - {QVariant::fromValue(RealLimits()), Utils::IsRealLimitsVariant}}; - - for (size_t i = 0; i < data.size(); ++i) { - auto is_variant_func = data[i].second; - for (size_t j = 0; j < data.size(); ++j) { - auto variant = data[j].first; - if (i == j) - EXPECT_TRUE(is_variant_func(variant)); - else - EXPECT_FALSE(is_variant_func(variant)); - } - } -} diff --git a/mvvm/tests/testmodel/data1ditem.test.cpp b/mvvm/tests/testmodel/data1ditem.test.cpp deleted file mode 100644 index 4e8e5a1d077..00000000000 --- a/mvvm/tests/testmodel/data1ditem.test.cpp +++ /dev/null @@ -1,202 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/data1ditem.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mockwidgets.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/standarditems/axisitems.h" -#include "mvvm/standarditems/data1ditem.h" -#include <stdexcept> - -using namespace ModelView; -using ::testing::_; - -//! Testing Data1DItem. - -class Data1DItemTest : public ::testing::Test { -public: - ~Data1DItemTest(); -}; - -Data1DItemTest::~Data1DItemTest() = default; - -//! Initial state. - -TEST_F(Data1DItemTest, initialState) -{ - Data1DItem item; - - EXPECT_EQ(item.getItem(Data1DItem::T_AXIS), nullptr); - EXPECT_EQ(item.binCenters(), std::vector<double>()); - EXPECT_EQ(item.binValues(), std::vector<double>()); - EXPECT_EQ(item.binErrors(), std::vector<double>()); - EXPECT_FALSE(item.hasData()); -} - -//! Checking the method ::setFixedBinAxis. - -TEST_F(Data1DItemTest, setFixedBinAxis) -{ - Data1DItem item; - - item.setAxis<FixedBinAxisItem>(5, 0.0, 5.0); - - // check type of the axis - EXPECT_TRUE(item.item<FixedBinAxisItem>(Data1DItem::T_AXIS) != nullptr); - - // check bin centers and values - std::vector<double> expected_centers = {0.5, 1.5, 2.5, 3.5, 4.5}; - EXPECT_EQ(item.binCenters(), expected_centers); - std::vector<double> expected_values = std::vector<double>(expected_centers.size(), 0.0); - EXPECT_EQ(item.binValues(), expected_values); - - // setting another axis - // for the moment we have disabled possibility to re-create axes to faciltate undo/redo - // item.setAxis(FixedBinAxisItem::create(1, 1.0, 2.0)); - // expected_centers = {1.5}; - // EXPECT_EQ(item.binCenters(), expected_centers); - // expected_values = {0.0}; - // EXPECT_EQ(item.binValues(), expected_values); -} - -//! Sets fixed bin axis via templated method. - -TEST_F(Data1DItemTest, setTemplatedFixedBinAxis) -{ - Data1DItem item; - - auto axis = item.setAxis<FixedBinAxisItem>(5, 0.0, 5.0); - - // check type of the axis - EXPECT_EQ(item.item<FixedBinAxisItem>(Data1DItem::T_AXIS), axis); - - // check bin centers and values - std::vector<double> expected_centers = {0.5, 1.5, 2.5, 3.5, 4.5}; - EXPECT_EQ(item.binCenters(), expected_centers); - std::vector<double> expected_values = std::vector<double>(expected_centers.size(), 0.0); - EXPECT_EQ(item.binValues(), expected_values); -} - -//! Sets fixed bin axis via templated method. - -TEST_F(Data1DItemTest, setTemplatedFixedBinAxisInModelContext) -{ - SessionModel model; - auto dataItem = model.insertItem<Data1DItem>(); - - auto axis = dataItem->setAxis<FixedBinAxisItem>(5, 0.0, 5.0); - - // check type of the axis - EXPECT_EQ(dataItem->item<FixedBinAxisItem>(Data1DItem::T_AXIS), axis); - - // check bin centers and values - std::vector<double> expected_centers = {0.5, 1.5, 2.5, 3.5, 4.5}; - EXPECT_EQ(dataItem->binCenters(), expected_centers); - std::vector<double> expected_values = std::vector<double>(expected_centers.size(), 0.0); - EXPECT_EQ(dataItem->binValues(), expected_values); -} - -//! Sets fixed bin axis via model context. -// FIXME Not clear if this method should be used - -TEST_F(Data1DItemTest, setFixedBinAxisInModel) -{ - SessionModel model; - - auto dataItem = model.insertItem<Data1DItem>(); - model.insertItem<FixedBinAxisItem>(dataItem)->setParameters(5, 0.0, 5.0); - - // check type of the axis - EXPECT_TRUE(dataItem->item<FixedBinAxisItem>(Data1DItem::T_AXIS) != nullptr); - - // check bin centers and values - std::vector<double> expected_centers = {0.5, 1.5, 2.5, 3.5, 4.5}; - EXPECT_EQ(dataItem->binCenters(), expected_centers); - std::vector<double> expected_values = std::vector<double>(expected_centers.size(), 0.0); - EXPECT_TRUE(dataItem->binValues().empty()); -} - -//! Checking the method ::setValues. - -TEST_F(Data1DItemTest, setValues) -{ - Data1DItem item; - - // check that it is not possible to set content to uninitialized axis - std::vector<double> expected_content = {1.0, 2.0, 3.0}; - EXPECT_THROW(item.setValues(expected_content), std::runtime_error); - - item.setAxis<FixedBinAxisItem>(3, 0.0, 3.0); - item.setValues(expected_content); - EXPECT_EQ(item.binValues(), expected_content); -} - -//! Checking the method ::setErrors. - -TEST_F(Data1DItemTest, setErrors) -{ - Data1DItem item; - - // check that it is not possible to errors to uninitialized axis - std::vector<double> expected_errors = {10.0, 20.0, 30.0}; - - EXPECT_THROW(item.setErrors(expected_errors), std::runtime_error); - - item.setAxis<FixedBinAxisItem>(3, 0.0, 3.0); - item.setErrors(expected_errors); - - EXPECT_EQ(item.binErrors(), expected_errors); -} - -//! Checking the signals when axes changed. - -TEST_F(Data1DItemTest, checkSignalsOnAxisChange) -{ - SessionModel model; - auto item = model.insertItem<Data1DItem>(); - - MockWidgetForItem widget(item); - - const std::string expected_value_tag{Data1DItem::P_VALUES}; - const TagRow expected_axis_tagrow{Data1DItem::T_AXIS, 0}; - - EXPECT_CALL(widget, onDataChange(_, _)).Times(0); - EXPECT_CALL(widget, onPropertyChange(item, expected_value_tag)).Times(1); - EXPECT_CALL(widget, onChildPropertyChange(_, _)).Times(2); - EXPECT_CALL(widget, onItemInserted(item, expected_axis_tagrow)).Times(1); - EXPECT_CALL(widget, onAboutToRemoveItem(_, _)).Times(0); - - // trigger change - item->setAxis<FixedBinAxisItem>(3, 0.0, 3.0); -} - -//! Checking the signals when bin values changed. - -TEST_F(Data1DItemTest, checkSignalsOnBinValuesChange) -{ - SessionModel model; - auto item = model.insertItem<Data1DItem>(); - item->setAxis<FixedBinAxisItem>(3, 0.0, 3.0); - - MockWidgetForItem widget(item); - - EXPECT_CALL(widget, onDataChange(_, _)).Times(0); - EXPECT_CALL(widget, onPropertyChange(item, Data1DItem::P_VALUES)).Times(1); - EXPECT_CALL(widget, onChildPropertyChange(_, _)).Times(0); - EXPECT_CALL(widget, onItemInserted(_, _)).Times(0); - EXPECT_CALL(widget, onAboutToRemoveItem(_, _)).Times(0); - - // trigger change - item->setValues(std::vector<double>{1.0, 2.0, 3.0}); -} diff --git a/mvvm/tests/testmodel/data2ditem.test.cpp b/mvvm/tests/testmodel/data2ditem.test.cpp deleted file mode 100644 index 9b4985bdbcb..00000000000 --- a/mvvm/tests/testmodel/data2ditem.test.cpp +++ /dev/null @@ -1,122 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/data2ditem.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mockwidgets.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/standarditems/axisitems.h" -#include "mvvm/standarditems/data2ditem.h" -#include <stdexcept> - -using namespace ModelView; -using ::testing::_; - -//! Testing Data1DItem. - -class Data2DItemTest : public ::testing::Test { -public: - ~Data2DItemTest(); -}; - -Data2DItemTest::~Data2DItemTest() = default; - -//! Initial state. - -TEST_F(Data2DItemTest, initialState) -{ - Data2DItem item; - - EXPECT_EQ(item.xAxis(), nullptr); - EXPECT_EQ(item.yAxis(), nullptr); - EXPECT_EQ(item.content(), std::vector<double>()); - EXPECT_TRUE(item.hasData()); - EXPECT_TRUE(item.data<std::vector<double>>().empty()); -} - -//! Checking the method ::setAxis. - -TEST_F(Data2DItemTest, setAxes) -{ - Data2DItem item; - - const int nx = 5, ny = 3; - item.setAxes(FixedBinAxisItem::create(nx, 0.0, 5.0), FixedBinAxisItem::create(ny, 0.0, 3.0)); - - // checking type of the axis - EXPECT_TRUE(item.item<FixedBinAxisItem>(Data2DItem::T_XAXIS) != nullptr); - EXPECT_TRUE(item.item<FixedBinAxisItem>(Data2DItem::T_YAXIS) != nullptr); - - // checking bin values - auto values = item.content(); - EXPECT_EQ(values.size(), nx * ny); - EXPECT_EQ(std::accumulate(values.begin(), values.end(), 0), 0.0); -} - -//! Checking the method ::setContent. - -TEST_F(Data2DItemTest, setContent) -{ - Data2DItem item; - - // check that it is not possible to set content to uninitialized axis - std::vector<double> expected_content = {1.0, 2.0}; - EXPECT_THROW(item.setContent(expected_content), std::runtime_error); - - const int nx = 1, ny = 2; - item.setAxes(FixedBinAxisItem::create(nx, 0.0, 5.0), FixedBinAxisItem::create(ny, 0.0, 3.0)); - - item.setContent(expected_content); - EXPECT_EQ(item.content(), expected_content); -} - -//! Checking the signals when axes changed. - -TEST_F(Data2DItemTest, checkSignalsOnAxisChange) -{ - SessionModel model; - auto item = model.insertItem<Data2DItem>(); - - FixedBinAxisItem::create(3, 0.0, 3.0); - - MockWidgetForItem widget(item); - - EXPECT_CALL(widget, onDataChange(item, ItemDataRole::DATA)).Times(1); // values should change - EXPECT_CALL(widget, onPropertyChange(_, _)).Times(0); - EXPECT_CALL(widget, onChildPropertyChange(_, _)).Times(0); - EXPECT_CALL(widget, onItemInserted(item, _)).Times(2); - EXPECT_CALL(widget, onAboutToRemoveItem(_, _)).Times(0); - - // trigger change - item->setAxes(FixedBinAxisItem::create(1, 0.0, 5.0), FixedBinAxisItem::create(3, 0.0, 3.0)); -} - -//! Checking the signals when content changed. - -TEST_F(Data2DItemTest, checkSignalsOnContentChange) -{ - SessionModel model; - auto item = model.insertItem<Data2DItem>(); - item->setAxes(FixedBinAxisItem::create(1, 0.0, 5.0), FixedBinAxisItem::create(3, 0.0, 3.0)); - - MockWidgetForItem widget(item); - - EXPECT_CALL(widget, onDataChange(item, ItemDataRole::DATA)).Times(1); // values should change - EXPECT_CALL(widget, onPropertyChange(_, _)).Times(0); - EXPECT_CALL(widget, onChildPropertyChange(_, _)).Times(0); - EXPECT_CALL(widget, onItemInserted(_, _)).Times(0); - EXPECT_CALL(widget, onAboutToRemoveItem(_, _)).Times(0); - - // trigger change - item->setContent(std::vector<double>{1.0, 2.0, 3.0}); -} diff --git a/mvvm/tests/testmodel/externalproperty.test.cpp b/mvvm/tests/testmodel/externalproperty.test.cpp deleted file mode 100644 index d2df92331c7..00000000000 --- a/mvvm/tests/testmodel/externalproperty.test.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/externalproperty.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/comparators.h" -#include "mvvm/model/externalproperty.h" - -using namespace ModelView; - -class ExternalPropertyTest : public ::testing::Test { -public: - ~ExternalPropertyTest(); -}; - -ExternalPropertyTest::~ExternalPropertyTest() = default; - -TEST_F(ExternalPropertyTest, initialState) -{ - ExternalProperty property; - EXPECT_FALSE(property.isValid()); - EXPECT_EQ(property.text(), ""); - EXPECT_EQ(property.identifier(), ""); - EXPECT_FALSE(property.color().isValid()); -} - -TEST_F(ExternalPropertyTest, constructor) -{ - ExternalProperty property("text", QColor(Qt::red), "123"); - EXPECT_TRUE(property.isValid()); - EXPECT_EQ(property.text(), "text"); - EXPECT_EQ(property.color(), QColor("red")); - EXPECT_EQ(property.identifier(), "123"); -} - -TEST_F(ExternalPropertyTest, equalityOperators) -{ - ExternalProperty prop1a; - ExternalProperty prop1b; - - EXPECT_TRUE(prop1a == prop1b); - EXPECT_FALSE(prop1a < prop1b); - - ExternalProperty prop2a("text", QColor(Qt::red)); - ExternalProperty prop2b("text", QColor(Qt::red)); - EXPECT_TRUE(prop2a == prop2b); - EXPECT_FALSE(prop2a < prop2b); - - EXPECT_FALSE(prop1a == prop2a); -} - -TEST_F(ExternalPropertyTest, variantEquality) -{ - if (Comparators::registered()) { - ExternalProperty prop1a; - ExternalProperty prop1b; - EXPECT_TRUE(QVariant::fromValue(prop1a) == QVariant::fromValue(prop1b)); - - ExternalProperty prop2a("text", QColor(Qt::red)); - ExternalProperty prop2b("text", QColor(Qt::red)); - EXPECT_TRUE(QVariant::fromValue(prop2a) == QVariant::fromValue(prop2b)); - - EXPECT_FALSE(QVariant::fromValue(prop1a) == QVariant::fromValue(prop2a)); - } -} diff --git a/mvvm/tests/testmodel/fileutils.test.cpp b/mvvm/tests/testmodel/fileutils.test.cpp deleted file mode 100644 index 09e4fac3968..00000000000 --- a/mvvm/tests/testmodel/fileutils.test.cpp +++ /dev/null @@ -1,112 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/fileutils.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "folderbasedtest.h" -#include "google_test.h" -#include "mvvm/utils/fileutils.h" -#include "test_utils.h" -#include <QDir> -#include <stdexcept> -#include <string> - -using namespace ModelView; - -class FileUtilsTest : public FolderBasedTest { -public: - FileUtilsTest() : FolderBasedTest("test_FileUtils") {} - ~FileUtilsTest(); -}; - -FileUtilsTest::~FileUtilsTest() = default; - -TEST_F(FileUtilsTest, exists) -{ - EXPECT_TRUE(Utils::exists(testPath())); - EXPECT_FALSE(Utils::exists("")); - EXPECT_FALSE(Utils::exists(std::string("abc"))); -} - -TEST_F(FileUtilsTest, create_directory) -{ - std::string dirname = testPath() + std::string("/") + "subdir"; - Utils::remove(dirname); - - EXPECT_TRUE(Utils::create_directory(dirname)); - EXPECT_TRUE(Utils::exists(dirname)); -} - -TEST_F(FileUtilsTest, remove_all) -{ - std::string dirname = testPath() + std::string("/") + "subdir2"; - Utils::create_directory(dirname); - - EXPECT_TRUE(Utils::exists(dirname)); - Utils::remove_all((dirname)); - EXPECT_FALSE(Utils::exists(dirname)); -} - -TEST_F(FileUtilsTest, base_name) -{ - std::string filename = testPath() + std::string("/testmodel/fileutils.test.cpp"); - std::string base_name = Utils::base_name(filename); - - EXPECT_EQ("fileutils.test", base_name); -} - -TEST_F(FileUtilsTest, FindFiles) -{ - TestUtils::CreateTestFile(testPath(), "a.txt"); - TestUtils::CreateTestFile(testPath(), "name0.json"); - TestUtils::CreateTestFile(testPath(), "name1.json"); - - auto found_files = Utils::FindFiles(testPath(), ".json"); - - ASSERT_EQ(found_files.size(), 2); - EXPECT_NE(found_files.end(), std::find(found_files.begin(), found_files.end(), - Utils::join(testPath(), "name0.json"))); - EXPECT_NE(found_files.end(), std::find(found_files.begin(), found_files.end(), - Utils::join(testPath(), "name1.json"))); -} - -TEST_F(FileUtilsTest, parent_path) -{ - // parent path of testPath() is the main test folder - // "<build>/test_output/test_FileUtils" -> "<build>/test_output/" - EXPECT_EQ(Utils::parent_path(testPath()), TestUtils::TestOutputDir()); - - // "<build>/test_output/test_FileUtils/a.txt" -> "<build>/test_output/test_FileUtils/" - auto filename = TestUtils::CreateTestFile(testPath(), "a.txt"); - EXPECT_EQ(Utils::parent_path(filename), testPath()); -} - -TEST_F(FileUtilsTest, is_empty) -{ - // creating new empty directory - std::string dirname = testPath() + std::string("/") + "subdir_is_empty"; - Utils::remove_all(dirname); - Utils::create_directory(dirname); - - // it should be empty - EXPECT_TRUE(Utils::is_empty(dirname)); - - // creating file in it, directory should be not empty - auto filename = TestUtils::CreateTestFile(dirname, "a.txt"); - EXPECT_FALSE(Utils::is_empty(dirname)); - // file itself should be not empty - EXPECT_FALSE(Utils::is_empty(dirname)); - - // creating empty file - auto empty_filename = TestUtils::CreateEmptyFile(dirname, "a2.txt"); - EXPECT_TRUE(Utils::is_empty(empty_filename)); -} diff --git a/mvvm/tests/testmodel/graphitem.test.cpp b/mvvm/tests/testmodel/graphitem.test.cpp deleted file mode 100644 index ee96b473a65..00000000000 --- a/mvvm/tests/testmodel/graphitem.test.cpp +++ /dev/null @@ -1,177 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/graphitem.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mockwidgets.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/standarditems/axisitems.h" -#include "mvvm/standarditems/data1ditem.h" -#include "mvvm/standarditems/graphitem.h" -#include "mvvm/standarditems/linkeditem.h" -#include "mvvm/standarditems/plottableitems.h" -#include <QColor> - -using namespace ModelView; -using ::testing::_; - -//! Testing GraphItem. - -class GraphItemTest : public ::testing::Test { -public: - ~GraphItemTest(); -}; - -GraphItemTest::~GraphItemTest() = default; - -//! Initial state. - -TEST_F(GraphItemTest, initialState) -{ - GraphItem item; - EXPECT_TRUE(item.dataItem() == nullptr); - EXPECT_EQ(item.binCenters(), std::vector<double>{}); - EXPECT_EQ(item.binValues(), std::vector<double>{}); - EXPECT_EQ(item.binErrors(), std::vector<double>{}); - EXPECT_EQ(item.colorName(), std::string("#000000")); -} - -//! Setting dataItem in model context. - -TEST_F(GraphItemTest, setDataItem) -{ - SessionModel model; - auto data_item = model.insertItem<Data1DItem>(); - auto graph_item = model.insertItem<GraphItem>(); - - graph_item->setDataItem(data_item); - - EXPECT_EQ(graph_item->dataItem(), data_item); -} - -//! Setting dataItem in model context. - -TEST_F(GraphItemTest, binValues) -{ - SessionModel model; - auto data_item = model.insertItem<Data1DItem>(); - auto graph_item = model.insertItem<GraphItem>(); - - std::vector<double> expected_values = {1.0, 2.0, 3.0}; - std::vector<double> expected_centers = {0.5, 1.5, 2.5}; - data_item->setAxis<FixedBinAxisItem>(3, 0.0, 3.0); - data_item->setValues(expected_values); - - graph_item->setDataItem(data_item); - - EXPECT_EQ(graph_item->binValues(), expected_values); - EXPECT_EQ(graph_item->binCenters(), expected_centers); -} - -//! Setting dataItem with errors - -TEST_F(GraphItemTest, binErrors) -{ - SessionModel model; - auto data_item = model.insertItem<Data1DItem>(); - auto graph_item = model.insertItem<GraphItem>(); - - std::vector<double> expected_values = {1.0, 2.0, 3.0}; - std::vector<double> expected_centers = {0.5, 1.5, 2.5}; - std::vector<double> expected_errors = {0.1, 0.2, 0.3}; - data_item->setAxis<FixedBinAxisItem>(3, 0.0, 3.0); - data_item->setValues(expected_values); - data_item->setErrors(expected_errors); - - graph_item->setDataItem(data_item); - - EXPECT_EQ(graph_item->binValues(), expected_values); - EXPECT_EQ(graph_item->binCenters(), expected_centers); - EXPECT_EQ(graph_item->binErrors(), expected_errors); -} - -//! Check unlinking when nullptr is set as Data1DItem. - -TEST_F(GraphItemTest, setNullData) -{ - SessionModel model; - auto data_item = model.insertItem<Data1DItem>(); - auto graph_item = model.insertItem<GraphItem>(); - - // preparing data item - std::vector<double> expected_values = {1.0, 2.0, 3.0}; - std::vector<double> expected_centers = {0.5, 1.5, 2.5}; - data_item->setAxis<FixedBinAxisItem>(3, 0.0, 3.0); - data_item->setValues(expected_values); - - graph_item->setDataItem(data_item); - EXPECT_EQ(graph_item->dataItem(), data_item); - - // setting null as data item - graph_item->setDataItem(nullptr); - EXPECT_TRUE(graph_item->dataItem() == nullptr); - EXPECT_EQ(graph_item->binCenters(), std::vector<double>{}); - EXPECT_EQ(graph_item->binValues(), std::vector<double>{}); - EXPECT_EQ(graph_item->binErrors(), std::vector<double>{}); -} - -//! Check signaling on set data item. - -TEST_F(GraphItemTest, onSetDataItem) -{ - SessionModel model; - auto data_item = model.insertItem<Data1DItem>(); - auto graph_item = model.insertItem<GraphItem>(); - - MockWidgetForItem widget(graph_item); - - EXPECT_CALL(widget, onDataChange(_, _)).Times(0); - EXPECT_CALL(widget, onPropertyChange(graph_item, GraphItem::P_LINK)).Times(1); - EXPECT_CALL(widget, onChildPropertyChange(_, _)).Times(0); - EXPECT_CALL(widget, onItemInserted(_, _)).Times(0); - EXPECT_CALL(widget, onAboutToRemoveItem(_, _)).Times(0); - - // performing action - graph_item->setDataItem(data_item); -} - -//! Sets GraphItem from another GraphItem - -TEST_F(GraphItemTest, setFromGraphItem) -{ - SessionModel model; - auto data_item = model.insertItem<Data1DItem>(); - auto graph_item = model.insertItem<GraphItem>(); - auto graph_item2 = model.insertItem<GraphItem>(); - - std::vector<double> expected_values = {1.0, 2.0, 3.0}; - std::vector<double> expected_centers = {0.5, 1.5, 2.5}; - data_item->setAxis<FixedBinAxisItem>(3, 0.0, 3.0); - data_item->setValues(expected_values); - - graph_item->setDataItem(data_item); - graph_item->penItem()->setProperty(PenItem::P_COLOR, QColor(Qt::red)); - - graph_item2->setFromGraphItem(graph_item); - - EXPECT_EQ(graph_item2->binValues(), expected_values); - EXPECT_EQ(graph_item2->binCenters(), expected_centers); - EXPECT_EQ(graph_item2->penItem()->property<QColor>(PenItem::P_COLOR), QColor(Qt::red)); -} - -TEST_F(GraphItemTest, penItem_setNamedColor) -{ - GraphItem item; - item.setNamedColor("mediumaquamarine"); - EXPECT_EQ(item.colorName(), std::string("#66cdaa")); -} diff --git a/mvvm/tests/testmodel/graphviewportitem.test.cpp b/mvvm/tests/testmodel/graphviewportitem.test.cpp deleted file mode 100644 index eb56ef72348..00000000000 --- a/mvvm/tests/testmodel/graphviewportitem.test.cpp +++ /dev/null @@ -1,164 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/graphviewportitem.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mockwidgets.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/standarditems/axisitems.h" -#include "mvvm/standarditems/data1ditem.h" -#include "mvvm/standarditems/graphitem.h" -#include "mvvm/standarditems/graphviewportitem.h" - -using namespace ModelView; -using ::testing::_; - -//! Testing AxesItems. - -class GraphViewportItemTest : public ::testing::Test { -public: - ~GraphViewportItemTest(); -}; - -GraphViewportItemTest::~GraphViewportItemTest() = default; - -//! Initial state. - -TEST_F(GraphViewportItemTest, initialState) -{ - GraphViewportItem item; - EXPECT_EQ(item.xAxis()->modelType(), GUI::Constants::ViewportAxisItemType); - EXPECT_EQ(item.yAxis()->modelType(), GUI::Constants::ViewportAxisItemType); - EXPECT_EQ(item.graphItems().size(), 0); -} - -//! Add graph to viewport. - -TEST_F(GraphViewportItemTest, addItem) -{ - SessionModel model; - - auto viewport_item = model.insertItem<GraphViewportItem>(); - auto graph_item = model.insertItem<GraphItem>(viewport_item); - auto data_item = model.insertItem<Data1DItem>(); - - const std::vector<double> expected_values = {1.0, 2.0, 3.0}; - const std::vector<double> expected_centers = {0.5, 1.5, 2.5}; - data_item->setAxis<FixedBinAxisItem>(3, 0.0, 3.0); - data_item->setValues(expected_values); - - graph_item->setDataItem(data_item); - EXPECT_EQ(viewport_item->graphItems().size(), 1); - - // updating viewport to graph - viewport_item->setViewportToContent(); - - // x-axis of viewport should be set to FixedBinAxis of DataItem - auto xaxis = viewport_item->xAxis(); - EXPECT_DOUBLE_EQ(xaxis->property<double>(ViewportAxisItem::P_MIN), expected_centers[0]); - EXPECT_DOUBLE_EQ(xaxis->property<double>(ViewportAxisItem::P_MAX), expected_centers[2]); - - // y-axis of viewport should be set to min/max of expected_content - auto yaxis = viewport_item->yAxis(); - auto [expected_amin, expected_amax] = - std::minmax_element(std::begin(expected_values), std::end(expected_values)); - EXPECT_DOUBLE_EQ(yaxis->property<double>(ViewportAxisItem::P_MIN), *expected_amin); - EXPECT_DOUBLE_EQ(yaxis->property<double>(ViewportAxisItem::P_MAX), *expected_amax); -} - -//! Check signaling on set data item. - -TEST_F(GraphViewportItemTest, onAddItem) -{ - SessionModel model; - auto viewport_item = model.insertItem<GraphViewportItem>(); - - MockWidgetForItem widget(viewport_item); - - const TagRow expected_tagrow{ViewportItem::T_ITEMS, 0}; - EXPECT_CALL(widget, onDataChange(_, _)).Times(0); - EXPECT_CALL(widget, onPropertyChange(_, _)).Times(0); - EXPECT_CALL(widget, onChildPropertyChange(_, _)).Times(0); - EXPECT_CALL(widget, onItemInserted(viewport_item, expected_tagrow)).Times(1); - EXPECT_CALL(widget, onAboutToRemoveItem(_, _)).Times(0); - - // triggering action - model.insertItem<GraphItem>(viewport_item); -} - -//! Check signaling on set data item. - -TEST_F(GraphViewportItemTest, onSetDataItem) -{ - SessionModel model; - auto viewport_item = model.insertItem<GraphViewportItem>(); - - // setting upda tata item - auto data_item = model.insertItem<Data1DItem>(); - const std::vector<double> expected_values = {1.0, 2.0, 3.0}; - const std::vector<double> expected_centers = {0.5, 1.5, 2.5}; - data_item->setAxis<FixedBinAxisItem>(3, 0.0, 3.0); - data_item->setValues(expected_values); - - // inserting graph item - auto graph_item = model.insertItem<GraphItem>(viewport_item); - - MockWidgetForItem widget(viewport_item); - - EXPECT_CALL(widget, onDataChange(_, _)).Times(0); - EXPECT_CALL(widget, onPropertyChange(_, _)).Times(0); - EXPECT_CALL(widget, onChildPropertyChange(graph_item, GraphItem::P_LINK)).Times(1); - EXPECT_CALL(widget, onItemInserted(_, _)).Times(0); - EXPECT_CALL(widget, onAboutToRemoveItem(_, _)).Times(0); - - // triggering action - graph_item->setDataItem(data_item); -} - -//! Add graph to viewport. - -TEST_F(GraphViewportItemTest, setViewportToContentWithMargins) -{ - SessionModel model; - - auto viewport_item = model.insertItem<GraphViewportItem>(); - auto graph_item = model.insertItem<GraphItem>(viewport_item); - auto data_item = model.insertItem<Data1DItem>(); - - const std::vector<double> expected_values = {1.0, 2.0, 3.0}; - const std::vector<double> expected_centers = {0.5, 1.5, 2.5}; - data_item->setAxis<FixedBinAxisItem>(3, 0.0, 3.0); - data_item->setValues(expected_values); - - graph_item->setDataItem(data_item); - EXPECT_EQ(viewport_item->graphItems().size(), 1); - - // updating viewport to graph - const double bottom{0.1}, top{0.1}; - viewport_item->setViewportToContent(0.0, top, 0.0, bottom); - - // x-axis of viewport should be set to FixedBinAxis of DataItem - auto xaxis = viewport_item->xAxis(); - EXPECT_DOUBLE_EQ(xaxis->property<double>(ViewportAxisItem::P_MIN), expected_centers[0]); - EXPECT_DOUBLE_EQ(xaxis->property<double>(ViewportAxisItem::P_MAX), expected_centers[2]); - - // y-axis of viewport should be set to min/max of expected_content - auto yaxis = viewport_item->yAxis(); - auto [expected_amin, expected_amax] = - std::minmax_element(std::begin(expected_values), std::end(expected_values)); - - double expected_ymin = *expected_amin - (*expected_amax - *expected_amin) * bottom; - double expected_ymax = *expected_amax + (*expected_amax - *expected_amin) * top; - EXPECT_DOUBLE_EQ(yaxis->property<double>(ViewportAxisItem::P_MIN), expected_ymin); - EXPECT_DOUBLE_EQ(yaxis->property<double>(ViewportAxisItem::P_MAX), expected_ymax); -} diff --git a/mvvm/tests/testmodel/groupitem.test.cpp b/mvvm/tests/testmodel/groupitem.test.cpp deleted file mode 100644 index 6d2033f0a34..00000000000 --- a/mvvm/tests/testmodel/groupitem.test.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/groupitem.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/groupitem.h" -#include <stdexcept> - -using namespace ModelView; - -//! Testing GroupItem class. - -class GroupItemTest : public ::testing::Test { -public: - ~GroupItemTest(); -}; - -GroupItemTest::~GroupItemTest() = default; - -TEST_F(GroupItemTest, initialState) -{ - GroupItem item; - EXPECT_EQ(item.currentIndex(), -1); - EXPECT_EQ(item.currentItem(), nullptr); - EXPECT_EQ(item.currentType(), ""); - EXPECT_TRUE(item.hasData()); - EXPECT_TRUE(item.children().empty()); - EXPECT_THROW(item.setCurrentType("abc"), std::runtime_error); -} diff --git a/mvvm/tests/testmodel/insertnewitemcommand.test.cpp b/mvvm/tests/testmodel/insertnewitemcommand.test.cpp deleted file mode 100644 index af4b304f7c0..00000000000 --- a/mvvm/tests/testmodel/insertnewitemcommand.test.cpp +++ /dev/null @@ -1,217 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/insertnewitemcommand.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/commands/insertnewitemcommand.h" -#include "mvvm/interfaces/itemfactoryinterface.h" -#include "mvvm/model/compounditem.h" -#include "mvvm/model/itempool.h" -#include "mvvm/model/itemutils.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/model/taginfo.h" -#include <stdexcept> - -using namespace ModelView; - -class InsertNewItemCommandTest : public ::testing::Test { -public: - ~InsertNewItemCommandTest(); - std::unique_ptr<InsertNewItemCommand> create_command(SessionItem* parent, std::string tag, - int row) - { - auto factory_func = [parent]() { - return parent->model()->factory()->createItem(GUI::Constants::BaseType); - }; - return std::make_unique<InsertNewItemCommand>(factory_func, parent, TagRow{tag, row}); - } -}; - -InsertNewItemCommandTest::~InsertNewItemCommandTest() = default; - -//! Insert new item through InsertNewItemCommand command. - -TEST_F(InsertNewItemCommandTest, insertNewItemCommand) -{ - SessionModel model; - - // command to insert item in a model - auto command = create_command(model.rootItem(), "", 0); - - // executing command - command->execute(); - EXPECT_EQ(model.rootItem()->childrenCount(), 1); - EXPECT_EQ(std::get<SessionItem*>(command->result()), model.rootItem()->getItem("", 0)); - EXPECT_EQ(command->isObsolete(), false); - - // undoing command - command->undo(); - EXPECT_EQ(model.rootItem()->childrenCount(), 0); - EXPECT_EQ(std::get<SessionItem*>(command->result()), nullptr); - EXPECT_EQ(command->isObsolete(), false); - - // executing again - command->execute(); - EXPECT_EQ(model.rootItem()->childrenCount(), 1); - EXPECT_EQ(std::get<SessionItem*>(command->result()), model.rootItem()->getItem("", 0)); - EXPECT_EQ(command->isObsolete(), false); -} - -//! Insert new item through InsertNewItemCommand command. - -TEST_F(InsertNewItemCommandTest, insertNewItemWithTagCommand) -{ - SessionModel model; - - // command to insert parent in the model - auto command1 = create_command(model.rootItem(), "", 0); - command1->execute(); // insertion - EXPECT_EQ(command1->isObsolete(), false); - - auto parent = std::get<SessionItem*>(command1->result()); - parent->registerTag(TagInfo::universalTag("tag1"), /*set_as_default*/ true); - EXPECT_EQ(parent->childrenCount(), 0); - - // command to insert child - auto command2 = create_command(parent, "tag1", 0); - command2->execute(); // insertion - EXPECT_EQ(command2->isObsolete(), false); - - EXPECT_EQ(parent->childrenCount(), 1); - EXPECT_EQ(Utils::ChildAt(parent, 0), std::get<SessionItem*>(command2->result())); - - // undoing command - command2->undo(); - EXPECT_EQ(parent->childrenCount(), 0); - EXPECT_EQ(nullptr, std::get<SessionItem*>(command2->result())); - EXPECT_EQ(command2->isObsolete(), false); -} - -//! Attempt to execute command twice. - -TEST_F(InsertNewItemCommandTest, attemptToExecuteTwice) -{ - SessionModel model; - // command to set same value - auto command = create_command(model.rootItem(), "", 0); - - // executing command - command->execute(); - EXPECT_THROW(command->execute(), std::runtime_error); -} - -//! Attempt to undo command twice. - -TEST_F(InsertNewItemCommandTest, attemptToUndoTwice) -{ - SessionModel model; - - // command to set same value - auto command = create_command(model.rootItem(), "", 0); - - // executing command - command->execute(); - command->undo(); - EXPECT_THROW(command->undo(), std::runtime_error); -} - -//! Attempt to insert second property to the compount item. - -TEST_F(InsertNewItemCommandTest, attemptInsertSecondProperty) -{ - SessionModel model; - auto parent = model.insertItem<CompoundItem>(); - parent->registerTag(TagInfo::propertyTag("radius", GUI::Constants::PropertyType)); - - // command to insert second property - auto factory_func = [parent]() { - return parent->model()->factory()->createItem(GUI::Constants::PropertyType); - }; - - // adding property to another tag is valid - InsertNewItemCommand command1(factory_func, parent, TagRow{"radius", -1}); - EXPECT_NO_THROW(command1.execute()); - EXPECT_FALSE(command1.isObsolete()); - EXPECT_EQ(std::get<SessionItem*>(command1.result()), parent->getItem("radius")); - - // adding second property to the same tag is not possible. Command should be in obsolete state - InsertNewItemCommand command2(factory_func, parent, TagRow{"radius", -1}); - EXPECT_NO_THROW(command2.execute()); - EXPECT_TRUE(command2.isObsolete()); - EXPECT_EQ(std::get<SessionItem*>(command2.result()), nullptr); - - // undoing failed command shouldn't be possible - EXPECT_THROW(command2.undo(), std::runtime_error); -} - -//! Insert new item through InsertNewItemCommand command. -//! We validate that undoing, and then redoing, would restore very first unique identifier. - -TEST_F(InsertNewItemCommandTest, insertNewPropertyItemPreservedId) -{ - SessionModel model; - // command to insert second property - auto factory_func = [&model]() { - return model.factory()->createItem(GUI::Constants::PropertyType); - }; - - EXPECT_EQ(model.rootItem()->childrenCount(), 0); - - InsertNewItemCommand command1(factory_func, model.rootItem(), TagRow{"", 0}); - command1.execute(); - - EXPECT_EQ(model.rootItem()->childrenCount(), 1); - auto orig_identifier = model.rootItem()->children()[0]->identifier(); - - command1.undo(); - EXPECT_EQ(model.rootItem()->childrenCount(), 0); - - command1.execute(); - EXPECT_EQ(model.rootItem()->childrenCount(), 1); - EXPECT_EQ(model.rootItem()->children()[0]->identifier(), orig_identifier); -} - -//! Insert new item through InsertNewItemCommand command. -//! We validate that undoing, and then redoing, would restore very first unique identifier. -//! Same as above, but we additionally controling item pool. - -TEST_F(InsertNewItemCommandTest, insertNewPropertyItemIdInPool) -{ - auto pool = std::make_shared<ItemPool>(); - SessionModel model("Model", pool); - // command to insert second property - auto factory_func = [&model]() { - return model.factory()->createItem(GUI::Constants::PropertyType); - }; - - EXPECT_EQ(model.rootItem()->childrenCount(), 0); - EXPECT_EQ(pool->size(), 1); // rootItem - - InsertNewItemCommand command1(factory_func, model.rootItem(), TagRow{"", 0}); - command1.execute(); - - EXPECT_EQ(model.rootItem()->childrenCount(), 1); - auto orig_identifier = model.rootItem()->children()[0]->identifier(); - EXPECT_EQ(pool->size(), 2); - - command1.undo(); - EXPECT_EQ(model.rootItem()->childrenCount(), 0); - - command1.execute(); - EXPECT_EQ(model.rootItem()->childrenCount(), 1); - auto restored_item = model.rootItem()->children()[0]; - EXPECT_EQ(restored_item->identifier(), orig_identifier); - EXPECT_EQ(model.findItem(orig_identifier), restored_item); - EXPECT_EQ(pool->item_for_key(orig_identifier), restored_item); -} diff --git a/mvvm/tests/testmodel/itemcatalogue.test.cpp b/mvvm/tests/testmodel/itemcatalogue.test.cpp deleted file mode 100644 index 75e11268b2f..00000000000 --- a/mvvm/tests/testmodel/itemcatalogue.test.cpp +++ /dev/null @@ -1,170 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/itemcatalogue.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/factories/itemcataloguefactory.h" -#include "mvvm/model/itemcatalogue.h" -#include "mvvm/model/propertyitem.h" -#include "mvvm/standarditems/vectoritem.h" -#include <stdexcept> - -using namespace ModelView; - -//! Testing ItemCatalogue construction - -class ItemCatalogueTest : public ::testing::Test { -public: - ~ItemCatalogueTest(); -}; - -ItemCatalogueTest::~ItemCatalogueTest() = default; - -TEST_F(ItemCatalogueTest, initialState) -{ - ItemCatalogue catalogue; - EXPECT_EQ(catalogue.itemCount(), 0); - EXPECT_EQ(catalogue.modelTypes(), std::vector<std::string>({})); - EXPECT_EQ(catalogue.labels(), std::vector<std::string>({})); -} - -TEST_F(ItemCatalogueTest, addItem) -{ - ItemCatalogue catalogue; - - catalogue.registerItem<PropertyItem>(); - - EXPECT_EQ(catalogue.itemCount(), 1); - - auto item = catalogue.create(GUI::Constants::PropertyType); - EXPECT_TRUE(dynamic_cast<PropertyItem*>(item.get()) != nullptr); - - // registration of second item is not allowed - EXPECT_THROW(catalogue.registerItem<PropertyItem>(), std::runtime_error); - - // item was not registered, creation not allowed - EXPECT_THROW(catalogue.create("non-registered"), std::runtime_error); - - // checking model types and labels - EXPECT_EQ(catalogue.modelTypes(), std::vector<std::string>({"Property"})); - EXPECT_EQ(catalogue.labels(), std::vector<std::string>({""})); -} - -TEST_F(ItemCatalogueTest, copyConstructor) -{ - ItemCatalogue catalogue; - catalogue.registerItem<PropertyItem>(); - - ItemCatalogue copy(catalogue); - - // creation of item using first catalogue - auto item = catalogue.create(GUI::Constants::PropertyType); - EXPECT_TRUE(dynamic_cast<PropertyItem*>(item.get()) != nullptr); - - // creation of item using catalogue copy - item = copy.create(GUI::Constants::PropertyType); - EXPECT_TRUE(dynamic_cast<PropertyItem*>(item.get()) != nullptr); - - // checking model types and labels in new catalogue - EXPECT_EQ(copy.modelTypes(), std::vector<std::string>({"Property"})); - EXPECT_EQ(copy.labels(), std::vector<std::string>({""})); - - // adding item to first catalogue but not the second - catalogue.registerItem<VectorItem>(); - item = catalogue.create(GUI::Constants::VectorItemType); - EXPECT_TRUE(dynamic_cast<VectorItem*>(item.get()) != nullptr); - - // copy of catalogue knows nothing about new VectorType - EXPECT_THROW(copy.create(GUI::Constants::VectorItemType), std::runtime_error); -} - -TEST_F(ItemCatalogueTest, assignmentOperator) -{ - ItemCatalogue catalogue; - catalogue.registerItem<PropertyItem>(); - - ItemCatalogue copy; - copy = catalogue; - - // creation of item using first catalogue - auto item = catalogue.create(GUI::Constants::PropertyType); - EXPECT_TRUE(dynamic_cast<PropertyItem*>(item.get()) != nullptr); - - // creation of item using catalogue copy - item = copy.create(GUI::Constants::PropertyType); - EXPECT_TRUE(dynamic_cast<PropertyItem*>(item.get()) != nullptr); -} - -TEST_F(ItemCatalogueTest, contains) -{ - ItemCatalogue catalogue; - catalogue.registerItem<PropertyItem>(); - - EXPECT_TRUE(catalogue.contains(GUI::Constants::PropertyType)); - EXPECT_FALSE(catalogue.contains(GUI::Constants::VectorItemType)); -} - -TEST_F(ItemCatalogueTest, defaultItemCatalogue) -{ - auto catalogue = CreateStandardItemCatalogue(); - - auto item = catalogue->create(GUI::Constants::BaseType); - EXPECT_TRUE(dynamic_cast<SessionItem*>(item.get()) != nullptr); - - item = catalogue->create(GUI::Constants::PropertyType); - EXPECT_TRUE(dynamic_cast<PropertyItem*>(item.get()) != nullptr); - - item = catalogue->create(GUI::Constants::VectorItemType); - EXPECT_TRUE(dynamic_cast<VectorItem*>(item.get()) != nullptr); - - item = catalogue->create(GUI::Constants::CompoundItemType); - EXPECT_TRUE(dynamic_cast<CompoundItem*>(item.get()) != nullptr); -} - -TEST_F(ItemCatalogueTest, addLabeledItem) -{ - ItemCatalogue catalogue; - catalogue.registerItem<PropertyItem>("property"); - catalogue.registerItem<VectorItem>("vector item"); - - // checking model types and labels - EXPECT_EQ(catalogue.modelTypes(), std::vector<std::string>({"Property", "Vector"})); - EXPECT_EQ(catalogue.labels(), std::vector<std::string>({"property", "vector item"})); -} - -TEST_F(ItemCatalogueTest, merge) -{ - ItemCatalogue catalogue1; - catalogue1.registerItem<PropertyItem>("property"); - catalogue1.registerItem<VectorItem>("vector"); - - ItemCatalogue catalogue2; - catalogue2.registerItem<CompoundItem>("compound"); - - // adding two catalogue together - catalogue1.merge(catalogue2); - - std::vector<std::string> expected_models = {GUI::Constants::PropertyType, - GUI::Constants::VectorItemType, - GUI::Constants::CompoundItemType}; - std::vector<std::string> expected_labels = {"property", "vector", "compound"}; - - EXPECT_EQ(catalogue1.modelTypes(), expected_models); - EXPECT_EQ(catalogue1.labels(), expected_labels); - - auto item = catalogue1.create(GUI::Constants::VectorItemType); - EXPECT_TRUE(dynamic_cast<VectorItem*>(item.get()) != nullptr); - - // duplications is not allowed - EXPECT_THROW(catalogue1.merge(catalogue2), std::runtime_error); -} diff --git a/mvvm/tests/testmodel/itemconverterfactory.test.cpp b/mvvm/tests/testmodel/itemconverterfactory.test.cpp deleted file mode 100644 index 51cfb4e6593..00000000000 --- a/mvvm/tests/testmodel/itemconverterfactory.test.cpp +++ /dev/null @@ -1,156 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/itemconverterfactory.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/factories/itemconverterfactory.h" -#include "mvvm/model/compounditem.h" -#include "mvvm/model/itemcatalogue.h" -#include "mvvm/model/propertyitem.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionitemdata.h" -#include "mvvm/model/sessionmodel.h" -#include "test_utils.h" -#include <QJsonObject> - -using namespace ModelView; - -//! Checks converters generated by ItemFactoryConverter. - -class ItemConverterFactoryTest : public ::testing::Test { -public: - class TestItem : public CompoundItem { - public: - TestItem() : CompoundItem("TestItem") - { - setToolTip("compound"); - addProperty("Thickness", 42)->setToolTip("thickness")->setEditorType("abc"); - } - }; - - class TestModel : public SessionModel { - public: - TestModel() : SessionModel("TestModel") - { - auto catalogue = std::make_unique<ModelView::ItemCatalogue>(); - catalogue->registerItem<TestItem>(); - setItemCatalogue(std::move(catalogue)); - } - }; - - ItemConverterFactoryTest() : m_model(std::make_unique<TestModel>()) {} - - const ItemFactoryInterface* factory() const { return m_model->factory(); } - - std::unique_ptr<SessionModel> m_model; -}; - -//! Clone converter for simple property item. - -TEST_F(ItemConverterFactoryTest, propertyItemCloneConverter) -{ - auto converter = CreateItemCloneConverter(factory()); - - PropertyItem item; - item.setToolTip("abc"); - - auto object = converter->to_json(&item); - auto reco = converter->from_json(object); - - EXPECT_EQ(reco->modelType(), item.modelType()); - EXPECT_EQ(reco->displayName(), item.displayName()); - EXPECT_EQ(reco->toolTip(), std::string("abc")); - EXPECT_EQ(reco->itemData()->roles(), item.itemData()->roles()); - EXPECT_TRUE(reco->identifier() == item.identifier()); // identifier preserved -} - -//! Copy converter for simple property item. - -TEST_F(ItemConverterFactoryTest, propertyItemCopyConverter) -{ - auto converter = CreateItemCopyConverter(factory()); - - PropertyItem item; - item.setToolTip("abc"); - - auto object = converter->to_json(&item); - auto reco = converter->from_json(object); - - EXPECT_EQ(reco->modelType(), item.modelType()); - EXPECT_EQ(reco->displayName(), item.displayName()); - EXPECT_EQ(reco->toolTip(), std::string("abc")); - EXPECT_EQ(reco->itemData()->roles(), item.itemData()->roles()); - EXPECT_FALSE(reco->identifier() == item.identifier()); // identifier has changed -} - -//! Project converter for simple property item. -//! It preserves only identifier and data roles. - -TEST_F(ItemConverterFactoryTest, propertyItemProjectConverter) -{ - auto converter = CreateItemProjectConverter(factory()); - - PropertyItem item; - item.setToolTip("abc"); - - auto object = converter->to_json(&item); - auto reco = converter->from_json(object); - - EXPECT_EQ(reco->modelType(), item.modelType()); - EXPECT_EQ(reco->displayName(), item.displayName()); - EXPECT_EQ(reco->toolTip(), ""); // tooltips are not preserved - EXPECT_TRUE(reco->identifier() == item.identifier()); // identifier preserved -} - -//! Clone converter for simple property item. - -TEST_F(ItemConverterFactoryTest, testItemCloneConverter) -{ - auto converter = CreateItemCloneConverter(factory()); - - TestItem item; - item.setToolTip("abc"); - - auto object = converter->to_json(&item); - auto reco = converter->from_json(object); - - EXPECT_EQ(reco->modelType(), item.modelType()); - EXPECT_EQ(reco->displayName(), item.displayName()); - EXPECT_EQ(reco->toolTip(), std::string("abc")); // updated tooltip is preserved - EXPECT_EQ(reco->itemData()->roles(), item.itemData()->roles()); - EXPECT_TRUE(reco->identifier() == item.identifier()); // identifier preserved - EXPECT_EQ(reco->getItem("Thickness")->toolTip(), "thickness"); - EXPECT_EQ(reco->getItem("Thickness")->identifier(), item.getItem("Thickness")->identifier()); -} - -//! Clone converter for simple property item. -//! At this time - -TEST_F(ItemConverterFactoryTest, testItemProjectConverter) -{ - auto converter = CreateItemProjectConverter(factory()); - - TestItem item; - item.setToolTip("abc"); - - auto object = converter->to_json(&item); - auto reco = converter->from_json(object); - - EXPECT_EQ(reco->modelType(), item.modelType()); - EXPECT_EQ(reco->displayName(), item.displayName()); - EXPECT_EQ(reco->toolTip(), std::string("compound")); // initial tooltip exist - EXPECT_EQ(reco->itemData()->roles(), item.itemData()->roles()); - EXPECT_TRUE(reco->identifier() == item.identifier()); // identifier preserved - EXPECT_EQ(reco->getItem("Thickness")->toolTip(), "thickness"); - EXPECT_EQ(reco->getItem("Thickness")->identifier(), item.getItem("Thickness")->identifier()); -} diff --git a/mvvm/tests/testmodel/itemlistener.test.cpp b/mvvm/tests/testmodel/itemlistener.test.cpp deleted file mode 100644 index f8ea0bd6b89..00000000000 --- a/mvvm/tests/testmodel/itemlistener.test.cpp +++ /dev/null @@ -1,110 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/itemlistener.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/propertyitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/signals/itemlistener.h" -#include <memory> - -using namespace ModelView; - -//! Testing ItemListener. - -class ItemListenerTest : public ::testing::Test { -public: - class TestController : public ItemListener<PropertyItem> { - public: - ~TestController(); - size_t ondata_change_call_count{0}; - size_t on_unsubscribe_call_count{0}; - void subscribe() - { - auto on_data_change = [this](SessionItem*, int) { ondata_change_call_count++; }; - setOnDataChange(on_data_change); - } - - void unsubscribe() { on_unsubscribe_call_count++; } - }; - - ~ItemListenerTest(); -}; - -ItemListenerTest::~ItemListenerTest() = default; -ItemListenerTest::TestController::~TestController() = default; - -//! Initial state. - -TEST_F(ItemListenerTest, initialState) -{ - TestController controller; - EXPECT_EQ(controller.currentItem(), nullptr); -} - -//! Check that controller aware of item deletion. - -TEST_F(ItemListenerTest, itemDeletedBeforeController) -{ - SessionModel model; - auto item = model.insertItem<PropertyItem>(); - - auto controller = std::make_unique<TestController>(); - controller->setItem(item); - item->setData(42.0); - - EXPECT_EQ(controller->currentItem(), item); - EXPECT_EQ(controller->ondata_change_call_count, 1); - - model.removeItem(model.rootItem(), {"", 0}); - EXPECT_EQ(controller->currentItem(), nullptr); -} - -//! Checks unsubscribe scenario. - -TEST_F(ItemListenerTest, unsubscribeScenario) -{ - SessionModel model; - auto item = model.insertItem<PropertyItem>(); - - auto controller = std::make_unique<TestController>(); - controller->setItem(item); - item->setData(42.0); - - EXPECT_EQ(controller->currentItem(), item); - EXPECT_EQ(controller->ondata_change_call_count, 1); - EXPECT_EQ(controller->on_unsubscribe_call_count, 0); - - // setting item to nullptr - controller->setItem(nullptr); - EXPECT_EQ(controller->currentItem(), nullptr); - EXPECT_EQ(controller->on_unsubscribe_call_count, 1); - // change in data shouldn't lead to update - item->setData(45.0); - EXPECT_EQ(controller->ondata_change_call_count, 1); -} - -//! Checks that controller can be deleted before item. - -TEST_F(ItemListenerTest, controllerDeletedBeforeItem) -{ - SessionModel model; - auto item = model.insertItem<PropertyItem>(); - - auto controller = std::make_unique<TestController>(); - controller->setItem(item); - EXPECT_EQ(controller->currentItem(), item); - - controller.reset(); - item->setData(42.0); -} diff --git a/mvvm/tests/testmodel/itemmanager.test.cpp b/mvvm/tests/testmodel/itemmanager.test.cpp deleted file mode 100644 index 1b2efa4b757..00000000000 --- a/mvvm/tests/testmodel/itemmanager.test.cpp +++ /dev/null @@ -1,42 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/itemmanager.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/itemmanager.h" -#include "mvvm/model/itempool.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionmodel.h" -#include <memory> - -using namespace ModelView; - -//! Testing ItemFactory in the context of SessionModel and unique identifiers of SessionItem. - -class ItemManagerTest : public ::testing::Test { -public: - ~ItemManagerTest(); -}; - -ItemManagerTest::~ItemManagerTest() = default; - -TEST_F(ItemManagerTest, initialState) -{ - ItemManager manager; - EXPECT_EQ(manager.itemPool(), nullptr); - - std::shared_ptr<ItemPool> pool(new ItemPool); - manager.setItemPool(pool); - EXPECT_EQ(manager.itemPool(), pool.get()); - EXPECT_EQ(manager.itemPool()->size(), 0); -} diff --git a/mvvm/tests/testmodel/itemmapper.test.cpp b/mvvm/tests/testmodel/itemmapper.test.cpp deleted file mode 100644 index 331a680026e..00000000000 --- a/mvvm/tests/testmodel/itemmapper.test.cpp +++ /dev/null @@ -1,254 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/itemmapper.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mockwidgets.h" -#include "mvvm/model/compounditem.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/signals/itemmapper.h" -#include <stdexcept> - -using namespace ModelView; -using ::testing::_; - -class ItemMapperTest : public ::testing::Test { -public: - ~ItemMapperTest(); -}; - -ItemMapperTest::~ItemMapperTest() = default; - -//! Check that mapper works only in model context. - -TEST(ItemMapperTest, initialState) -{ - // item outside model context can't have a mapper - auto item = std::make_unique<SessionItem>(); - EXPECT_THROW(item->mapper(), std::runtime_error); - - // item in model context does have a mapper - SessionModel model; - auto item2 = model.insertItem<SessionItem>(model.rootItem()); - EXPECT_NO_THROW(item2->mapper()); -} - -//! Destroying item, expecting single call of onItemDestroy in MockWidget. - -TEST(ItemMapperTest, onItemDestroy) -{ - SessionModel model; - auto item = model.insertItem<SessionItem>(model.rootItem()); - - MockWidgetForItem widget(item); - - auto expected_item = item; - EXPECT_CALL(widget, onItemDestroy(expected_item)).Times(1); - EXPECT_CALL(widget, onDataChange(_, _)).Times(0); - EXPECT_CALL(widget, onPropertyChange(_, _)).Times(0); - EXPECT_CALL(widget, onChildPropertyChange(_, _)).Times(0); - EXPECT_CALL(widget, onItemInserted(_, _)).Times(0); - EXPECT_CALL(widget, onItemRemoved(_, _)).Times(0); - EXPECT_CALL(widget, onAboutToRemoveItem(_, _)).Times(0); - - // performing action - model.removeItem(model.rootItem(), {"", 0}); -} - -//! Setting data to item, expecting onDataChange callback. - -TEST(ItemMapperTest, onDataChange) -{ - SessionModel model; - auto item = model.insertItem<SessionItem>(model.rootItem()); - - MockWidgetForItem widget(item); - - auto expected_role = ItemDataRole::DATA; - auto expected_item = item; - EXPECT_CALL(widget, onDataChange(expected_item, expected_role)).Times(1); - EXPECT_CALL(widget, onPropertyChange(_, _)).Times(0); - EXPECT_CALL(widget, onChildPropertyChange(_, _)).Times(0); - EXPECT_CALL(widget, onItemInserted(_, _)).Times(0); - EXPECT_CALL(widget, onItemRemoved(_, _)).Times(0); - EXPECT_CALL(widget, onAboutToRemoveItem(_, _)).Times(0); - - // perform action - item->setData(42.0); -} - -//! Setting same data to item, expecting no callbacks on onDataChange. - -TEST(ItemMapperTest, onDataChangeDuplicate) -{ - SessionModel model; - auto item = model.insertItem<SessionItem>(model.rootItem()); - - MockWidgetForItem widget(item); - - EXPECT_CALL(widget, onItemDestroy(_)).Times(0); - EXPECT_CALL(widget, onDataChange(_, _)).Times(1); - EXPECT_CALL(widget, onPropertyChange(_, _)).Times(0); - EXPECT_CALL(widget, onChildPropertyChange(_, _)).Times(0); - EXPECT_CALL(widget, onItemInserted(_, _)).Times(0); - EXPECT_CALL(widget, onItemRemoved(_, _)).Times(0); - EXPECT_CALL(widget, onAboutToRemoveItem(_, _)).Times(0); - - // perform actions, only one call should be triggered - item->setData(42.0); - item->setData(42.0); // same data -} - -//! Setting mapper activity to false, change the data, expect no callbacks. - -TEST(ItemMapperTest, setActivity) -{ - SessionModel model; - auto item = model.insertItem<SessionItem>(model.rootItem()); - - MockWidgetForItem widget(item); - - item->mapper()->setActive(false); - - EXPECT_CALL(widget, onItemDestroy(_)).Times(0); - EXPECT_CALL(widget, onDataChange(_, _)).Times(0); - EXPECT_CALL(widget, onPropertyChange(_, _)).Times(0); - EXPECT_CALL(widget, onChildPropertyChange(_, _)).Times(0); - EXPECT_CALL(widget, onItemInserted(_, _)).Times(0); - EXPECT_CALL(widget, onItemRemoved(_, _)).Times(0); - EXPECT_CALL(widget, onAboutToRemoveItem(_, _)).Times(0); - - // perform actions, no calls should be triggered - item->setData(42.0); -} - -//! Unsubscribing from item, expecting no callbacks. - -TEST(ItemMapperTest, unsubscribe) -{ - SessionModel model; - auto item = model.insertItem<SessionItem>(model.rootItem()); - - MockWidgetForItem widget1(item); - MockWidgetForItem widget2(item); - - item->mapper()->unsubscribe(&widget1); - - EXPECT_CALL(widget1, onDataChange(_, _)).Times(0); - EXPECT_CALL(widget2, onDataChange(_, _)).Times(1); - - // perform action, only one widget should be triggered - item->setData(42.0); -} - -//! Changing item property. - -TEST(ItemMapperTest, onPropertyChange) -{ - SessionModel model; - auto item = model.insertItem<CompoundItem>(); - EXPECT_TRUE(item != nullptr); - - auto property = item->addProperty("height", 42.0); - - MockWidgetForItem widget(item); - - EXPECT_CALL(widget, onItemDestroy(_)).Times(0); - EXPECT_CALL(widget, onDataChange(_, _)).Times(0); - EXPECT_CALL(widget, onPropertyChange(item, "height")).Times(1); - EXPECT_CALL(widget, onChildPropertyChange(_, _)).Times(0); - EXPECT_CALL(widget, onItemInserted(_, _)).Times(0); - EXPECT_CALL(widget, onItemRemoved(_, _)).Times(0); - EXPECT_CALL(widget, onAboutToRemoveItem(_, _)).Times(0); - - // perform action - item->setProperty("height", 43.0); - EXPECT_EQ(item->property<double>("height"), 43.0); - EXPECT_EQ(property->data<double>(), 43.0); -} - -//! Changing item property. - -TEST(ItemMapperTest, onChildPropertyChange) -{ - SessionModel model; - auto compound1 = model.insertItem<CompoundItem>(); - compound1->registerTag(TagInfo::universalTag("tag1"), /*set_as_default*/ true); - auto compound2 = model.insertItem<CompoundItem>(compound1); - - auto property = compound2->addProperty("height", 42.0); - - MockWidgetForItem widget(compound1); - - EXPECT_CALL(widget, onItemDestroy(_)).Times(0); - EXPECT_CALL(widget, onDataChange(_, _)).Times(0); - EXPECT_CALL(widget, onPropertyChange(_, _)).Times(0); - EXPECT_CALL(widget, onChildPropertyChange(compound2, "height")).Times(1); - EXPECT_CALL(widget, onItemInserted(_, _)).Times(0); - EXPECT_CALL(widget, onItemRemoved(_, _)).Times(0); - EXPECT_CALL(widget, onAboutToRemoveItem(_, _)).Times(0); - - // perform action - compound2->setProperty("height", 43.0); - EXPECT_EQ(compound2->property<double>("height"), 43.0); - EXPECT_EQ(property->data<double>(), 43.0); -} - -//! Inserting item to item. - -TEST(ItemMapperTest, onItemInsert) -{ - SessionModel model; - auto compound1 = model.insertItem<CompoundItem>(); - compound1->registerTag(TagInfo::universalTag("tag1"), /*set_as_default*/ true); - - MockWidgetForItem widget(compound1); - - const TagRow expected_tagrow{"tag1", 0}; - EXPECT_CALL(widget, onItemDestroy(_)).Times(0); - EXPECT_CALL(widget, onDataChange(_, _)).Times(0); - EXPECT_CALL(widget, onPropertyChange(_, _)).Times(0); - EXPECT_CALL(widget, onChildPropertyChange(_, _)).Times(0); - EXPECT_CALL(widget, onItemInserted(compound1, expected_tagrow)).Times(1); - EXPECT_CALL(widget, onItemRemoved(_, _)).Times(0); - EXPECT_CALL(widget, onAboutToRemoveItem(_, _)).Times(0); - - // perform action - model.insertItem<CompoundItem>(compound1, expected_tagrow); -} - -//! Inserting item to item. - -TEST(ItemMapperTest, onAboutToRemoveItem) -{ - const TagRow expected_tagrow = {"tag1", 0}; - - SessionModel model; - auto compound1 = model.insertItem<CompoundItem>(); - compound1->registerTag(TagInfo::universalTag("tag1"), /*set_as_default*/ true); - model.insertItem<CompoundItem>(compound1, expected_tagrow); - - MockWidgetForItem widget(compound1); - - EXPECT_CALL(widget, onItemDestroy(_)).Times(0); - EXPECT_CALL(widget, onDataChange(_, _)).Times(0); - EXPECT_CALL(widget, onPropertyChange(_, _)).Times(0); - EXPECT_CALL(widget, onChildPropertyChange(_, _)).Times(0); - EXPECT_CALL(widget, onItemInserted(_, _)).Times(0); - EXPECT_CALL(widget, onItemRemoved(compound1, expected_tagrow)).Times(1); - EXPECT_CALL(widget, onAboutToRemoveItem(compound1, expected_tagrow)).Times(1); - - // perform action - model.removeItem(compound1, expected_tagrow); -} diff --git a/mvvm/tests/testmodel/itempool.test.cpp b/mvvm/tests/testmodel/itempool.test.cpp deleted file mode 100644 index 3f2cb5fbe6d..00000000000 --- a/mvvm/tests/testmodel/itempool.test.cpp +++ /dev/null @@ -1,115 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/itempool.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/itempool.h" -#include "mvvm/model/sessionitem.h" -#include <memory> -#include <stdexcept> - -using namespace ModelView; - -//! Tests of ItemPool and its abilities to register/deregister SessionItem. - -class ItemPoolTest : public ::testing::Test { -public: - ~ItemPoolTest(); -}; - -ItemPoolTest::~ItemPoolTest() = default; - -TEST_F(ItemPoolTest, initialState) -{ - std::unique_ptr<ItemPool> pool(new ItemPool); - EXPECT_EQ(pool->size(), 0u); -} - -//! Explicit item registrations. - -TEST_F(ItemPoolTest, registerItem) -{ - std::unique_ptr<ItemPool> pool(new ItemPool); - std::unique_ptr<SessionItem> item(new SessionItem); - - // registering item - auto key = pool->register_item(item.get()); - EXPECT_EQ(pool->size(), 1u); - EXPECT_FALSE(key.empty()); - - // checking registered key and item - EXPECT_EQ(key, pool->key_for_item(item.get())); - EXPECT_EQ(item.get(), pool->item_for_key(key)); - - // checking unexisting registration - std::unique_ptr<SessionItem> item2(new SessionItem); - EXPECT_EQ(identifier_type(), pool->key_for_item(item2.get())); - EXPECT_EQ(nullptr, pool->item_for_key("ABC")); - - // registering second item - auto key2 = pool->register_item(item2.get()); - EXPECT_EQ(pool->size(), 2u); - EXPECT_EQ(key2, pool->key_for_item(item2.get())); - EXPECT_FALSE(key == key2); - - // attempt to register item twice - EXPECT_THROW(pool->register_item(item2.get()), std::runtime_error); -} - -//! Explicit item de-registrations. - -TEST_F(ItemPoolTest, deregisterItem) -{ - std::unique_ptr<ItemPool> pool(new ItemPool); - std::unique_ptr<SessionItem> item1(new SessionItem); - std::unique_ptr<SessionItem> item2(new SessionItem); - - auto key1 = pool->register_item(item1.get()); - auto key2 = pool->register_item(item2.get()); - - EXPECT_EQ(pool->size(), 2u); - EXPECT_EQ(item1.get(), pool->item_for_key(key1)); - EXPECT_EQ(item2.get(), pool->item_for_key(key2)); - - // deregistering item - pool->unregister_item(item1.get()); - EXPECT_EQ(pool->size(), 1u); - EXPECT_EQ(nullptr, pool->item_for_key(key1)); - EXPECT_EQ(item2.get(), pool->item_for_key(key2)); - - // attempt to deregister twice - EXPECT_THROW(pool->unregister_item(item1.get()), std::runtime_error); - - // deregistering last remaining item - pool->unregister_item(item2.get()); - EXPECT_EQ(pool->size(), 0u); -} - -//! Providing custom key. - -TEST_F(ItemPoolTest, customKey) -{ - std::shared_ptr<ItemPool> pool(new ItemPool); - EXPECT_EQ(pool.use_count(), 1l); - - // explicit item registration - const identifier_type id("abc-cde-fgh"); - auto item = new SessionItem; - pool->register_item(item, id); - - // attempt to reuse key again - std::unique_ptr<SessionItem> item2(new SessionItem); - EXPECT_THROW(pool->register_item(item2.get(), id), std::runtime_error); - - delete item; -} diff --git a/mvvm/tests/testmodel/itemutils.test.cpp b/mvvm/tests/testmodel/itemutils.test.cpp deleted file mode 100644 index aa87db6f5ca..00000000000 --- a/mvvm/tests/testmodel/itemutils.test.cpp +++ /dev/null @@ -1,328 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/itemutils.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/customvariants.h" -#include "mvvm/model/itemutils.h" -#include "mvvm/model/propertyitem.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/model/taginfo.h" -#include "mvvm/standarditems/vectoritem.h" -#include <memory> - -using namespace ModelView; - -class ItemUtilsTest : public ::testing::Test { -}; - -//! Simple iteration over item and its children - -TEST_F(ItemUtilsTest, iterateItem) -{ - std::vector<const SessionItem*> visited_items; - - auto fun = [&](const SessionItem* item) { visited_items.push_back(item); }; - - // iteration over nullptr - Utils::iterate(nullptr, fun); - EXPECT_TRUE(visited_items.empty()); - - // iteration over lonely parent - std::unique_ptr<SessionItem> parent(new SessionItem); - parent->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - - std::vector<const SessionItem*> expected = {parent.get()}; - Utils::iterate(parent.get(), fun); - EXPECT_EQ(visited_items, expected); - - // adding children - auto child1 = new SessionItem; - auto child2 = new SessionItem; - parent->insertItem(child1, TagRow::append()); - parent->insertItem(child2, TagRow::append()); - - visited_items.clear(); - Utils::iterate(parent.get(), fun); - - expected = {parent.get(), child1, child2}; - EXPECT_EQ(visited_items, expected); -} - -//! Conditional iteration over item and its children. - -TEST_F(ItemUtilsTest, iterateIfItem) -{ - std::vector<const SessionItem*> visited_items; - - // function which will not let iterate over children - std::function<bool(const SessionItem*)> fun = [&](const SessionItem* item) { - visited_items.push_back(item); - return false; - }; - - // iteration over lonely parent - std::unique_ptr<SessionItem> parent(new SessionItem); - parent->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - - auto child1 = new SessionItem; - auto child2 = new SessionItem; - parent->insertItem(child1, TagRow::append()); - parent->insertItem(child2, TagRow::append()); - - std::vector<const SessionItem*> expected = {parent.get()}; - Utils::iterate_if(parent.get(), fun); - EXPECT_EQ(visited_items, expected); -} - -//! Iteration over root item of the model. - -TEST_F(ItemUtilsTest, iterateModel) -{ - SessionModel model; - - // building model - auto parent1 = model.insertItem<SessionItem>(); - auto parent2 = model.insertItem<SessionItem>(); - parent1->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - parent2->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - auto child1 = model.insertItem<SessionItem>(parent1); - auto child2 = model.insertItem<SessionItem>(parent1); - - std::vector<const SessionItem*> visited_items; - auto fun = [&](const SessionItem* item) { visited_items.push_back(item); }; - - // iteration - Utils::iterate(model.rootItem(), fun); - - std::vector<const SessionItem*> expected = {model.rootItem(), parent1, child1, child2, parent2}; - EXPECT_EQ(visited_items, expected); -} - -//! Copy number of child in parents tree. - -TEST_F(ItemUtilsTest, itemCopyNumber) -{ - SessionModel model; - - auto parent = model.insertItem<SessionItem>(); - parent->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - - auto child1 = model.insertItem<SessionItem>(parent); - auto child2 = model.insertItem<SessionItem>(parent); - auto child3 = model.insertItem<PropertyItem>(parent); - - EXPECT_EQ(Utils::CopyNumber(child1), 0); - EXPECT_EQ(Utils::CopyNumber(child2), 1); - EXPECT_EQ(Utils::CopyNumber(child3), -1); -} - -//! Checks method ::HasTag. - -TEST_F(ItemUtilsTest, HasTag) -{ - SessionItem item; - item.registerTag(TagInfo::universalTag("default_tag"), /*set_as_default*/ true); - - EXPECT_TRUE(Utils::HasTag(item, "default_tag")); - EXPECT_FALSE(Utils::HasTag(item, "nonexisting_tag")); -} - -//! Checks method ::IsSinglePropertyTag. - -TEST_F(ItemUtilsTest, IsSinglePropertyTag) -{ - SessionItem item; - item.registerTag(TagInfo::universalTag("default_tag"), /*set_as_default*/ true); - item.registerTag(TagInfo::propertyTag("property_tag", GUI::Constants::PropertyType)); - - EXPECT_FALSE(Utils::IsSinglePropertyTag(item, "default_tag")); - EXPECT_TRUE(Utils::IsSinglePropertyTag(item, "property_tag")); -} - -//! Checks method ::RegisteredTags. - -TEST_F(ItemUtilsTest, RegisteredTags) -{ - SessionItem item; - EXPECT_TRUE(Utils::RegisteredTags(item).empty()); - - item.registerTag(TagInfo::universalTag("default_tag"), /*set_as_default*/ true); - item.registerTag(TagInfo::propertyTag("property_tag", GUI::Constants::PropertyType)); - - EXPECT_EQ(Utils::RegisteredTags(item), - std::vector<std::string>({"default_tag", "property_tag"})); -} - -//! Checks method ::RegisteredUniversalTags. - -TEST_F(ItemUtilsTest, RegisteredUniversalTags) -{ - SessionItem item; - EXPECT_TRUE(Utils::RegisteredUniversalTags(item).empty()); - - item.registerTag(TagInfo::universalTag("default_tag"), /*set_as_default*/ true); - item.registerTag(TagInfo::propertyTag("property_tag", GUI::Constants::PropertyType)); - - EXPECT_EQ(Utils::RegisteredUniversalTags(item), std::vector<std::string>({"default_tag"})); -} - -//! Check access to top level and property items. - -TEST_F(ItemUtilsTest, TopLevelItems) -{ - SessionModel model; - - auto parent = model.insertItem<SessionItem>(); - parent->registerTag(TagInfo::universalTag("default_tag"), /*set_as_default*/ true); - parent->registerTag(TagInfo::propertyTag("property_tag", GUI::Constants::PropertyType)); - - auto child1 = model.insertItem<SessionItem>(parent, "default_tag"); - model.insertItem<PropertyItem>(parent, "property_tag"); - auto child3 = model.insertItem<SessionItem>(parent, "default_tag"); - - EXPECT_EQ(Utils::TopLevelItems(*model.rootItem()), std::vector<SessionItem*>({parent})); - EXPECT_EQ(Utils::TopLevelItems(*child1), std::vector<SessionItem*>({})); - EXPECT_EQ(Utils::TopLevelItems(*parent), std::vector<SessionItem*>({child1, child3})); -} - -//! Check access to top level and property items. - -TEST_F(ItemUtilsTest, SinglePropertyItems) -{ - SessionModel model; - - auto parent = model.insertItem<SessionItem>(); - parent->registerTag(TagInfo::universalTag("default_tag"), /*set_as_default*/ true); - parent->registerTag(TagInfo::propertyTag("property_tag", GUI::Constants::PropertyType)); - - auto child1 = model.insertItem<SessionItem>(parent, "default_tag"); - auto child2 = model.insertItem<PropertyItem>(parent, "property_tag"); - model.insertItem<SessionItem>(parent, "default_tag"); - - EXPECT_EQ(Utils::SinglePropertyItems(*model.rootItem()), std::vector<SessionItem*>({})); - EXPECT_EQ(Utils::SinglePropertyItems(*child1), std::vector<SessionItem*>({})); - EXPECT_EQ(Utils::SinglePropertyItems(*parent), std::vector<SessionItem*>({child2})); -} - -//! Looking for next item. - -TEST_F(ItemUtilsTest, FindNextSibling) -{ - SessionModel model; - - auto parent = model.insertItem<SessionItem>(); - parent->registerTag(TagInfo::universalTag("default_tag"), /*set_as_default*/ true); - parent->registerTag(TagInfo::propertyTag("property_tag", GUI::Constants::PropertyType)); - - auto property = model.insertItem<PropertyItem>(parent, "property_tag"); - auto child0 = model.insertItem<SessionItem>(parent, "default_tag"); - auto child1 = model.insertItem<SessionItem>(parent, "default_tag"); - auto child2 = model.insertItem<SessionItem>(parent, "default_tag"); - - EXPECT_EQ(Utils::FindNextSibling(child0), child1); - EXPECT_EQ(Utils::FindNextSibling(child1), child2); - EXPECT_EQ(Utils::FindNextSibling(child2), nullptr); - EXPECT_EQ(Utils::FindNextSibling(property), nullptr); - EXPECT_EQ(Utils::FindNextSibling(parent), nullptr); -} - -//! Looking for previous item. - -TEST_F(ItemUtilsTest, FindPreviousSibling) -{ - SessionModel model; - - auto parent = model.insertItem<SessionItem>(); - parent->registerTag(TagInfo::universalTag("default_tag"), /*set_as_default*/ true); - parent->registerTag(TagInfo::propertyTag("property_tag", GUI::Constants::PropertyType)); - - auto property = model.insertItem<PropertyItem>(parent, "property_tag"); - auto child0 = model.insertItem<SessionItem>(parent, "default_tag"); - auto child1 = model.insertItem<SessionItem>(parent, "default_tag"); - auto child2 = model.insertItem<SessionItem>(parent, "default_tag"); - - EXPECT_EQ(Utils::FindPreviousSibling(child0), nullptr); - EXPECT_EQ(Utils::FindPreviousSibling(child1), child0); - EXPECT_EQ(Utils::FindPreviousSibling(child2), child1); - EXPECT_EQ(Utils::FindPreviousSibling(property), nullptr); - EXPECT_EQ(Utils::FindPreviousSibling(parent), nullptr); -} - -//! Looking for previous item. - -TEST_F(ItemUtilsTest, FindNextItemToSelect) -{ - SessionModel model; - - auto parent = model.insertItem<SessionItem>(); - parent->registerTag(TagInfo::universalTag("default_tag"), /*set_as_default*/ true); - parent->registerTag(TagInfo::propertyTag("property_tag", GUI::Constants::PropertyType)); - - auto property = model.insertItem<PropertyItem>(parent, "property_tag"); - auto child0 = model.insertItem<SessionItem>(parent, "default_tag"); - auto child1 = model.insertItem<SessionItem>(parent, "default_tag"); - auto child2 = model.insertItem<SessionItem>(parent, "default_tag"); - - EXPECT_EQ(Utils::FindNextItemToSelect(child0), child1); - EXPECT_EQ(Utils::FindNextItemToSelect(child1), child2); - EXPECT_EQ(Utils::FindNextItemToSelect(child2), child1); - EXPECT_EQ(Utils::FindNextItemToSelect(property), parent); - EXPECT_EQ(Utils::FindNextItemToSelect(parent), model.rootItem()); -} - -//! Looking for previous item. - -TEST_F(ItemUtilsTest, IsItemAncestor) -{ - SessionModel model; - EXPECT_FALSE(Utils::IsItemAncestor(model.rootItem(), model.rootItem())); - - // rootItem in ancestor of vectorItem, but not vice versa - auto vector_item = model.insertItem<VectorItem>(); - EXPECT_TRUE(Utils::IsItemAncestor(vector_item, model.rootItem())); - EXPECT_FALSE(Utils::IsItemAncestor(model.rootItem(), vector_item)); - - auto x_item = vector_item->getItem(VectorItem::P_X); - - EXPECT_TRUE(Utils::IsItemAncestor(x_item, model.rootItem())); - EXPECT_TRUE(Utils::IsItemAncestor(x_item, vector_item)); - EXPECT_FALSE(Utils::IsItemAncestor(model.rootItem(), x_item)); - EXPECT_FALSE(Utils::IsItemAncestor(vector_item, x_item)); - - auto y_item = vector_item->getItem(VectorItem::P_Y); - EXPECT_FALSE(Utils::IsItemAncestor(x_item, y_item)); -} - -TEST_F(ItemUtilsTest, UniqueItems) -{ - SessionModel model; - auto item0 = model.insertItem<SessionItem>(model.rootItem()); - auto item1 = model.insertItem<SessionItem>(model.rootItem()); - auto item2 = model.insertItem<SessionItem>(model.rootItem()); - std::vector<SessionItem*> data = {nullptr, item0, item1, item2, item0, item2, nullptr}; - std::vector<SessionItem*> expected = {item0, item1, item2}; - EXPECT_EQ(Utils::UniqueItems(data), expected); -} - -TEST_F(ItemUtilsTest, CastedItems) -{ - SessionModel model; - auto item0 = model.insertItem<SessionItem>(model.rootItem()); - auto item1 = model.insertItem<PropertyItem>(model.rootItem()); - auto item2 = model.insertItem<VectorItem>(model.rootItem()); - std::vector<SessionItem*> data = {nullptr, item0, item1, item2, item0, item1, item2, nullptr}; - - EXPECT_EQ(Utils::CastedItems<PropertyItem>(data), std::vector<PropertyItem*>({item1, item1})); -} diff --git a/mvvm/tests/testmodel/jsondocument.test.cpp b/mvvm/tests/testmodel/jsondocument.test.cpp deleted file mode 100644 index 3a462e2e277..00000000000 --- a/mvvm/tests/testmodel/jsondocument.test.cpp +++ /dev/null @@ -1,177 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/jsondocument.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "folderbasedtest.h" -#include "google_test.h" -#include "mvvm/model/propertyitem.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionitemtags.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/model/taginfo.h" -#include "mvvm/serialization/jsondocument.h" -#include "test_utils.h" -#include <stdexcept> - -using namespace ModelView; - -//! Tests JsonDocument class - -class JsonDocumentTest : public FolderBasedTest { -public: - JsonDocumentTest() : FolderBasedTest("test_JsonDocument") {} - ~JsonDocumentTest(); - - class TestModel1 : public SessionModel { - public: - TestModel1() : SessionModel("TestModel1") {} - ~TestModel1(); - }; - - class TestModel2 : public SessionModel { - public: - TestModel2() : SessionModel("TestModel2") {} - ~TestModel2(); - }; -}; - -JsonDocumentTest::~JsonDocumentTest() = default; -JsonDocumentTest::TestModel1::~TestModel1() = default; -JsonDocumentTest::TestModel2::~TestModel2() = default; - -//! Saving the model with content into document and restoring it after. - -TEST_F(JsonDocumentTest, saveLoadSingleModel) -{ - auto fileName = TestUtils::TestFileName(testDir(), "saveLoadSingleModel.json"); - SessionModel model("TestModel"); - JsonDocument document({&model}); - - // filling model with parent and child - auto parent = model.insertItem<SessionItem>(); - parent->setDisplayName("parent_name"); - parent->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - const auto parent_identifier = parent->identifier(); - - parent->setData(QVariant::fromValue(42)); - auto child = model.insertItem<PropertyItem>(parent); - child->setDisplayName("child_name"); - const auto child_identifier = child->identifier(); - - // saving model in file - document.save(fileName); - - // modifying model further - model.removeItem(model.rootItem(), {"", 0}); - - // loading model from file - document.load(fileName); - - // checking that it is as it was right after the save - - // accessing reconstructed parent and child - auto reco_parent = model.rootItem()->getItem("", 0); - auto reco_child = reco_parent->getItem("", 0); - - // checking parent reconstruction - EXPECT_EQ(reco_parent->model(), &model); - EXPECT_EQ(reco_parent->modelType(), GUI::Constants::BaseType); - EXPECT_EQ(reco_parent->parent(), model.rootItem()); - EXPECT_EQ(reco_parent->displayName(), "SessionItem"); // name changed becase of ProjectConverter - EXPECT_EQ(reco_parent->childrenCount(), 1); - EXPECT_EQ(reco_parent->identifier(), parent_identifier); - EXPECT_EQ(reco_parent->itemTags()->defaultTag(), "defaultTag"); - EXPECT_EQ(reco_parent->data<int>(), 42); - - // checking child reconstruction - EXPECT_EQ(reco_child->model(), &model); - EXPECT_EQ(reco_child->modelType(), GUI::Constants::PropertyType); - EXPECT_EQ(reco_child->parent(), reco_parent); - EXPECT_EQ(reco_child->displayName(), "Property"); - EXPECT_EQ(reco_child->childrenCount(), 0); - EXPECT_EQ(reco_child->identifier(), child_identifier); - EXPECT_EQ(reco_child->itemTags()->defaultTag(), ""); -} - -//! Saving two models with content into document and restoring it after. - -TEST_F(JsonDocumentTest, saveLoadTwoModels) -{ - auto fileName = TestUtils::TestFileName(testDir(), "saveLoadTwoModels.json"); - TestModel1 model1; - TestModel2 model2; - JsonDocument document({&model1, &model2}); - - // filling models - auto parent1 = model1.insertItem<SessionItem>(); - const auto parent_identifier1 = parent1->identifier(); - - auto parent2 = model2.insertItem<SessionItem>(); - const auto parent_identifier2 = parent2->identifier(); - - // saving models in file - document.save(fileName); - - // modifying model further - model1.removeItem(model1.rootItem(), {"", 0}); - model2.removeItem(model2.rootItem(), {"", 0}); - - // loading model from file - document.load(fileName); - - // checking that it is as it was right after the save - - // accessing reconstructed parent and child - auto reco_parent1 = model1.rootItem()->getItem("", 0); - auto reco_parent2 = model2.rootItem()->getItem("", 0); - - // checking parent reconstruction - EXPECT_EQ(reco_parent1->model(), &model1); - EXPECT_EQ(reco_parent1->parent(), model1.rootItem()); - EXPECT_EQ(reco_parent1->identifier(), parent_identifier1); - - EXPECT_EQ(reco_parent2->model(), &model2); - EXPECT_EQ(reco_parent2->parent(), model2.rootItem()); - EXPECT_EQ(reco_parent2->identifier(), parent_identifier2); -} - -//! Attempt to restore models in wrong order. - -TEST_F(JsonDocumentTest, loadModelsInWrongOrder) -{ - auto fileName = TestUtils::TestFileName(testDir(), "loadModelsInWrongOrder.json"); - TestModel1 model1; - TestModel2 model2; - - // filling models - auto parent1 = model1.insertItem<SessionItem>(); - const auto parent_identifier1 = parent1->identifier(); - - auto parent2 = model2.insertItem<SessionItem>(); - const auto parent_identifier2 = parent2->identifier(); - - // saving models in file - { - JsonDocument document({&model1, &model2}); - document.save(fileName); - } - - // modifying model further - model1.removeItem(model1.rootItem(), {"", 0}); - model2.removeItem(model2.rootItem(), {"", 0}); - - JsonDocument document({&model2, &model1}); // intentional wrong order - - // loading model from file - EXPECT_THROW(document.load(fileName), std::runtime_error); -} diff --git a/mvvm/tests/testmodel/jsonitem_types.test.cpp b/mvvm/tests/testmodel/jsonitem_types.test.cpp deleted file mode 100644 index 642003362c1..00000000000 --- a/mvvm/tests/testmodel/jsonitem_types.test.cpp +++ /dev/null @@ -1,43 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/jsonitem_types.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/serialization/jsonitem_types.h" - -using namespace ModelView; - -//! Testing json related flags. - -class JsonItemTypesTest : public ::testing::Test { -public: - ~JsonItemTypesTest(); -}; - -JsonItemTypesTest::~JsonItemTypesTest() = default; - -TEST_F(JsonItemTypesTest, isRegenerateIdWhenBackFromJson) -{ - EXPECT_FALSE(isRegenerateIdWhenBackFromJson(ConverterMode::none)); - EXPECT_FALSE(isRegenerateIdWhenBackFromJson(ConverterMode::clone)); - EXPECT_TRUE(isRegenerateIdWhenBackFromJson(ConverterMode::copy)); - EXPECT_FALSE(isRegenerateIdWhenBackFromJson(ConverterMode::project)); -} - -TEST_F(JsonItemTypesTest, isRebuildItemDataAndTagFromJson) -{ - EXPECT_TRUE(isRebuildItemDataAndTagFromJson(ConverterMode::none)); - EXPECT_TRUE(isRebuildItemDataAndTagFromJson(ConverterMode::clone)); - EXPECT_TRUE(isRebuildItemDataAndTagFromJson(ConverterMode::copy)); - EXPECT_FALSE(isRebuildItemDataAndTagFromJson(ConverterMode::project)); -} diff --git a/mvvm/tests/testmodel/jsonitembackupstrategy.test.cpp b/mvvm/tests/testmodel/jsonitembackupstrategy.test.cpp deleted file mode 100644 index e5cf57b37fb..00000000000 --- a/mvvm/tests/testmodel/jsonitembackupstrategy.test.cpp +++ /dev/null @@ -1,113 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/jsonitembackupstrategy.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/factories/itemcataloguefactory.h" -#include "mvvm/model/compounditem.h" -#include "mvvm/model/itemfactory.h" -#include "mvvm/model/propertyitem.h" -#include "mvvm/model/sessionitemtags.h" -#include "mvvm/serialization/jsonitembackupstrategy.h" - -using namespace ModelView; - -class JsonItemBackupStrategyTest : public ::testing::Test { -public: - JsonItemBackupStrategyTest() - : m_factory(std::make_unique<ItemFactory>(CreateStandardItemCatalogue())) - { - } - ~JsonItemBackupStrategyTest(); - - std::unique_ptr<JsonItemBackupStrategy> createBackupStrategy() - { - return std::make_unique<JsonItemBackupStrategy>(m_factory.get()); - } - - std::unique_ptr<ItemFactory> m_factory; -}; - -JsonItemBackupStrategyTest::~JsonItemBackupStrategyTest() = default; - -//! Saving/restoring PropertyItem. - -TEST_F(JsonItemBackupStrategyTest, propertyItem) -{ - auto strategy = createBackupStrategy(); - - PropertyItem item; - item.setData(42.0); - - strategy->saveItem(&item); - auto restored = strategy->restoreItem(); - - EXPECT_EQ(item.modelType(), restored->modelType()); - EXPECT_EQ(item.identifier(), restored->identifier()); - EXPECT_EQ(item.data<QVariant>(), restored->data<QVariant>()); -} - -//! Saving/restoring CompoundItem. - -TEST_F(JsonItemBackupStrategyTest, compoundItem) -{ - auto strategy = createBackupStrategy(); - - CompoundItem item; - auto property = item.addProperty("thickness", 42.0); - - strategy->saveItem(&item); - auto restored = strategy->restoreItem(); - - EXPECT_EQ(item.modelType(), restored->modelType()); - EXPECT_EQ(item.identifier(), restored->identifier()); - EXPECT_EQ(restored->getItem("thickness")->data<double>(), property->data<double>()); - EXPECT_EQ(restored->getItem("thickness")->identifier(), property->identifier()); -} - -//! Saving/restoring CustomItem. - -TEST_F(JsonItemBackupStrategyTest, customItem) -{ - auto strategy = createBackupStrategy(); - - const std::string model_type(GUI::Constants::BaseType); - - // creating parent with one child - auto parent = std::make_unique<SessionItem>(model_type); - parent->setDisplayName("parent_name"); - parent->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - auto child = new SessionItem(model_type); - child->setDisplayName("child_name"); - parent->insertItem(child, TagRow::append()); - - // creating copy - strategy->saveItem(parent.get()); - auto reco_parent = strategy->restoreItem(); - - EXPECT_EQ(reco_parent->childrenCount(), 1); - EXPECT_EQ(reco_parent->modelType(), model_type); - EXPECT_EQ(reco_parent->displayName(), "parent_name"); - EXPECT_EQ(reco_parent->identifier(), parent->identifier()); - EXPECT_EQ(reco_parent->itemTags()->defaultTag(), "defaultTag"); - EXPECT_EQ(reco_parent->model(), nullptr); - - // checking child reconstruction - auto reco_child = reco_parent->getItem("defaultTag"); - EXPECT_EQ(reco_child->parent(), reco_parent.get()); - EXPECT_EQ(reco_child->childrenCount(), 0); - EXPECT_EQ(reco_child->modelType(), model_type); - EXPECT_EQ(reco_child->displayName(), "child_name"); - EXPECT_EQ(reco_child->identifier(), child->identifier()); - EXPECT_EQ(reco_child->itemTags()->defaultTag(), ""); -} diff --git a/mvvm/tests/testmodel/jsonitemcontainerconverter.test.cpp b/mvvm/tests/testmodel/jsonitemcontainerconverter.test.cpp deleted file mode 100644 index fa3278f6ba0..00000000000 --- a/mvvm/tests/testmodel/jsonitemcontainerconverter.test.cpp +++ /dev/null @@ -1,201 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/jsonitemcontainerconverter.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "folderbasedtest.h" -#include "google_test.h" -#include "mvvm/model/mvvm_types.h" -#include "mvvm/model/propertyitem.h" -#include "mvvm/model/sessionitemcontainer.h" -#include "mvvm/model/sessionitemdata.h" -#include "mvvm/serialization/jsonitem_types.h" -#include "mvvm/serialization/jsonitemcontainerconverter.h" -#include "mvvm/serialization/jsonitemdataconverter.h" -#include "mvvm/serialization/jsonitemformatassistant.h" -#include "test_utils.h" -#include <QJsonArray> -#include <QJsonDocument> -#include <QJsonObject> - -using namespace ModelView; - -//! Checks JsonItemContainerConverter class and its ability to convert SessionItemContainer -//! to json and back. Full testing is not possible since JsonItemContainerConverter -//! requires machinery provided by JsonItemConverter. Simplifed item/json creation is used. - -class JsonItemContainerConverterTest : public FolderBasedTest { -public: - JsonItemContainerConverterTest() - : FolderBasedTest("test_JsonItemContainerConverterTest") - , m_itemdata_converter(std::make_unique<JsonItemDataConverter>()) - { - } - - std::unique_ptr<JsonItemContainerConverter> createConverter() const - { - //! Simplified method to convert SessionItem to JSON object. - auto to_json = [this](const SessionItem& item) { - QJsonObject result; - result[JsonItemFormatAssistant::modelKey] = QString::fromStdString(item.modelType()); - result[JsonItemFormatAssistant::itemDataKey] = - m_itemdata_converter->to_json(*item.itemData()); - result[JsonItemFormatAssistant::itemTagsKey] = QJsonObject(); - return result; - }; - - //! Simplified method to create SessionItem from JSON object - auto create_item = [this](const QJsonObject& json) { - auto result = std::make_unique<SessionItem>(); - m_itemdata_converter->from_json(json[JsonItemFormatAssistant::itemDataKey].toArray(), - *result->itemData()); - return result; - }; - - //! Simplified method to update SessionItem from JSON object - auto update_item = [this](const QJsonObject& json, SessionItem* item) { - m_itemdata_converter->from_json(json[JsonItemFormatAssistant::itemDataKey].toArray(), - *item->itemData()); - }; - - ConverterCallbacks callbacks{to_json, create_item, update_item}; - return std::make_unique<JsonItemContainerConverter>(callbacks); - } - - ~JsonItemContainerConverterTest(); - - std::unique_ptr<JsonItemDataConverter> m_itemdata_converter; -}; - -JsonItemContainerConverterTest::~JsonItemContainerConverterTest() = default; - -//! SessionItemContainer (with single property item) to json object. - -TEST_F(JsonItemContainerConverterTest, propertyContainerToJson) -{ - // creating container - TagInfo tag = TagInfo::propertyTag("thickness", GUI::Constants::PropertyType); - SessionItemContainer container(tag); - - // inserting single property item - auto item = new PropertyItem; - item->setData(42); - EXPECT_TRUE(container.insertItem(item, 0)); - - // converting top JSON and checking that it is valid JSON object - auto converter = createConverter(); - auto json = converter->to_json(container); - - JsonItemFormatAssistant assistant; - EXPECT_TRUE(assistant.isSessionItemContainer(json)); -} - -//! SessionItemContainer (with single property item) to json object and back. - -TEST_F(JsonItemContainerConverterTest, propertyContainerToJsonAndBack) -{ - // creating container - TagInfo tag = TagInfo::propertyTag("thickness", GUI::Constants::PropertyType); - SessionItemContainer container(tag); - - // inserting single property item - auto item = new PropertyItem; - item->setData(42); - EXPECT_TRUE(container.insertItem(item, 0)); - - // converting top JSON - auto converter = createConverter(); - auto json = converter->to_json(container); - - // creating second container with same layout, and updating it from JSON - SessionItemContainer container2(tag); - auto item2 = new PropertyItem; - item2->setData(43); - EXPECT_TRUE(container2.insertItem(item2, 0)); - converter->from_json(json, container2); - - // Checking that item in container2 has been reused, and get same properties as item. - EXPECT_EQ(container2.itemAt(0), item2); - EXPECT_EQ(item->displayName(), item2->displayName()); - EXPECT_EQ(item->identifier(), item2->identifier()); - EXPECT_EQ(42, item2->data<int>()); -} - -//! SessionItemContainer (with single property item) to json file and back. - -TEST_F(JsonItemContainerConverterTest, propertyContainerToFileAndBack) -{ - // creating container - TagInfo tag = TagInfo::propertyTag("thickness", GUI::Constants::PropertyType); - SessionItemContainer container(tag); - - // inserting single property item - auto item = new PropertyItem; - item->setData(42); - EXPECT_TRUE(container.insertItem(item, 0)); - - // converting top JSON and checking that it is valid JSON object - auto converter = createConverter(); - auto json = converter->to_json(container); - - // saving object to file - auto fileName = TestUtils::TestFileName(testDir(), "propertyContainerToFileAndBack.json"); - TestUtils::SaveJson(json, fileName); - - // loading from file - auto document = TestUtils::LoadJson(fileName); - - // creating second container with same layout, and updating it from JSON - SessionItemContainer container2(tag); - auto item2 = new PropertyItem; - item2->setData(43); - EXPECT_TRUE(container2.insertItem(item2, 0)); - converter->from_json(document.object(), container2); - - // Checking that item in container2 has been reused, and get same properties as item. - EXPECT_EQ(container2.itemAt(0), item2); - EXPECT_EQ(item->displayName(), item2->displayName()); - EXPECT_EQ(item->identifier(), item2->identifier()); - EXPECT_EQ(42, item2->data<int>()); -} - -//! SessionItemContainer (with universal tag and several items) to json object and back. - -TEST_F(JsonItemContainerConverterTest, universalContainerToJsonAndBack) -{ - // creating container - TagInfo tag = TagInfo::universalTag("items"); - SessionItemContainer container(tag); - - // inserting single property item - const int n_max_items = 3; - for (int i = 0; i < n_max_items; ++i) { - auto item = new PropertyItem; - item->setData(i + 42); - EXPECT_TRUE(container.insertItem(item, 0)); - } - - // converting top JSON - auto converter = createConverter(); - auto json = converter->to_json(container); - - // creating second container with same layout, but without items - SessionItemContainer container2(tag); - converter->from_json(json, container2); - - // Checking that container2 got same content as container - EXPECT_EQ(container2.itemCount(), n_max_items); - for (int i = 0; i < n_max_items; ++i) { - EXPECT_EQ(container.itemAt(i)->identifier(), container2.itemAt(i)->identifier()); - EXPECT_EQ(container.itemAt(i)->data<int>(), container2.itemAt(i)->data<int>()); - } -} diff --git a/mvvm/tests/testmodel/jsonitemconverter.test.cpp b/mvvm/tests/testmodel/jsonitemconverter.test.cpp deleted file mode 100644 index 81297b96c84..00000000000 --- a/mvvm/tests/testmodel/jsonitemconverter.test.cpp +++ /dev/null @@ -1,237 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/jsonitemconverter.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "folderbasedtest.h" -#include "google_test.h" -#include "mvvm/model/compounditem.h" -#include "mvvm/model/itemcatalogue.h" -#include "mvvm/model/propertyitem.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionitemtags.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/serialization/jsonitem_types.h" -#include "mvvm/serialization/jsonitemconverter.h" -#include "mvvm/serialization/jsonitemformatassistant.h" -#include "test_utils.h" -#include <QJsonArray> -#include <QJsonDocument> -#include <QJsonObject> - -using namespace ModelView; - -//! Checks JsonItem class and its ability to convert SessionItems to json and back. - -class JsonItemConverterTest : public FolderBasedTest { -public: - class TestItem : public CompoundItem { - public: - TestItem() : CompoundItem("TestItem") - { - setToolTip("compound"); - addProperty("Thickness", 42)->setToolTip("thickness"); - } - }; - - class TestModel : public SessionModel { - public: - TestModel() : SessionModel("TestModel") - { - auto catalogue = std::make_unique<ModelView::ItemCatalogue>(); - catalogue->registerItem<TestItem>(); - setItemCatalogue(std::move(catalogue)); - } - }; - - JsonItemConverterTest() - : FolderBasedTest("test_JsonItemConverter"), m_model(std::make_unique<TestModel>()) - { - } - ~JsonItemConverterTest(); - - std::unique_ptr<JsonItemConverter> createConverter() - { - ConverterContext context{m_model->factory(), ConverterMode::clone}; - return std::make_unique<JsonItemConverter>(context); - } - -private: - std::unique_ptr<SessionModel> m_model; -}; - -JsonItemConverterTest::~JsonItemConverterTest() = default; - -//! PropertyItem to json object. - -TEST_F(JsonItemConverterTest, propertyItemToJson) -{ - auto converter = createConverter(); - - PropertyItem item; - auto object = converter->to_json(&item); - - // this object represents SessionItem - JsonItemFormatAssistant assistant; - EXPECT_TRUE(assistant.isSessionItem(object)); -} - -//! PropertyItem to json object and back. - -TEST_F(JsonItemConverterTest, propertyItemToJsonAndBack) -{ - auto converter = createConverter(); - - PropertyItem item; - item.setToolTip("abc"); - auto object = converter->to_json(&item); - - auto reco = converter->from_json(object); - - EXPECT_EQ(reco->modelType(), item.modelType()); - EXPECT_EQ(reco->displayName(), item.displayName()); - EXPECT_EQ(reco->identifier(), item.identifier()); - EXPECT_EQ(reco->toolTip(), std::string("abc")); -} - -//! PropertyItem to json file and back. - -TEST_F(JsonItemConverterTest, propertyItemToFileAndBack) -{ - auto converter = createConverter(); - - PropertyItem item; - auto object = converter->to_json(&item); - - // saving object to file - auto fileName = TestUtils::TestFileName(testDir(), "propertyItemToFileAndBack.json"); - TestUtils::SaveJson(object, fileName); - - auto document = TestUtils::LoadJson(fileName); - auto reco = converter->from_json(document.object()); - - EXPECT_EQ(reco->parent(), nullptr); - EXPECT_EQ(reco->modelType(), item.modelType()); - EXPECT_EQ(reco->displayName(), item.displayName()); - EXPECT_EQ(reco->identifier(), item.identifier()); -} - -//! Parent and child to json object. - -TEST_F(JsonItemConverterTest, parentAndChildToJsonAndBack) -{ - auto converter = createConverter(); - const std::string model_type(GUI::Constants::BaseType); - - auto parent = std::make_unique<SessionItem>(model_type); - parent->setDisplayName("parent_name"); - parent->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - auto child = new SessionItem(model_type); - child->setDisplayName("child_name"); - parent->insertItem(child, TagRow::append()); - - // converting to json - auto object = converter->to_json(parent.get()); - JsonItemFormatAssistant assistant; - EXPECT_TRUE(assistant.isSessionItem(object)); - - // converting json back to item - auto reco_parent = converter->from_json(object); - - // checking parent reconstruction - EXPECT_EQ(reco_parent->childrenCount(), 1); - EXPECT_EQ(reco_parent->modelType(), model_type); - EXPECT_EQ(reco_parent->displayName(), "parent_name"); - EXPECT_EQ(reco_parent->identifier(), parent->identifier()); - EXPECT_EQ(reco_parent->itemTags()->defaultTag(), "defaultTag"); - EXPECT_EQ(reco_parent->model(), nullptr); - - // checking child reconstruction - auto reco_child = reco_parent->getItem("defaultTag"); - EXPECT_EQ(reco_child->parent(), reco_parent.get()); - EXPECT_EQ(reco_child->childrenCount(), 0); - EXPECT_EQ(reco_child->modelType(), model_type); - EXPECT_EQ(reco_child->displayName(), "child_name"); - EXPECT_EQ(reco_child->identifier(), child->identifier()); - EXPECT_EQ(reco_child->itemTags()->defaultTag(), ""); -} - -//! Parent and child to json file and back. - -TEST_F(JsonItemConverterTest, parentAndChildToFileAndBack) -{ - auto converter = createConverter(); - const std::string model_type(GUI::Constants::BaseType); - - auto parent = std::make_unique<SessionItem>(model_type); - parent->setDisplayName("parent_name"); - parent->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - auto child = new SessionItem(model_type); - child->setDisplayName("child_name"); - parent->insertItem(child, TagRow::append()); - - // converting to json - auto object = converter->to_json(parent.get()); - JsonItemFormatAssistant assistant; - EXPECT_TRUE(assistant.isSessionItem(object)); - - // saving object to file - auto fileName = TestUtils::TestFileName(testDir(), "parentAndChildToFileAndBack.json"); - TestUtils::SaveJson(object, fileName); - - // converting document back to item - auto document = TestUtils::LoadJson(fileName); - auto reco_parent = converter->from_json(document.object()); - - // checking parent reconstruction - EXPECT_EQ(reco_parent->childrenCount(), 1); - EXPECT_EQ(reco_parent->modelType(), model_type); - EXPECT_EQ(reco_parent->displayName(), "parent_name"); - EXPECT_EQ(reco_parent->identifier(), parent->identifier()); - EXPECT_EQ(reco_parent->itemTags()->defaultTag(), "defaultTag"); - EXPECT_EQ(reco_parent->model(), nullptr); - - // checking child reconstruction - auto reco_child = reco_parent->getItem("defaultTag"); - EXPECT_EQ(reco_child->parent(), reco_parent.get()); - EXPECT_EQ(reco_child->childrenCount(), 0); - EXPECT_EQ(reco_child->modelType(), model_type); - EXPECT_EQ(reco_child->displayName(), "child_name"); - EXPECT_EQ(reco_child->identifier(), child->identifier()); - EXPECT_EQ(reco_child->itemTags()->defaultTag(), ""); -} - -//! TestItem to json file and back. - -TEST_F(JsonItemConverterTest, testItemToFileAndBack) -{ - auto converter = createConverter(); - - TestItem item; - auto object = converter->to_json(&item); - - // saving object to file - auto fileName = TestUtils::TestFileName(testDir(), "testItemToFileAndBack.json"); - TestUtils::SaveJson(object, fileName); - - auto document = TestUtils::LoadJson(fileName); - auto reco = converter->from_json(document.object()); - - EXPECT_EQ(reco->parent(), nullptr); - EXPECT_EQ(reco->modelType(), item.modelType()); - EXPECT_EQ(reco->displayName(), item.displayName()); - EXPECT_EQ(reco->identifier(), item.identifier()); - - EXPECT_EQ(reco->toolTip(), "compound"); - // tooltip was preserved after the serialization - EXPECT_EQ(reco->getItem("Thickness")->toolTip(), "thickness"); -} diff --git a/mvvm/tests/testmodel/jsonitemcopystrategy.test.cpp b/mvvm/tests/testmodel/jsonitemcopystrategy.test.cpp deleted file mode 100644 index 8d3c074553f..00000000000 --- a/mvvm/tests/testmodel/jsonitemcopystrategy.test.cpp +++ /dev/null @@ -1,110 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/jsonitemcopystrategy.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/factories/itemcataloguefactory.h" -#include "mvvm/model/compounditem.h" -#include "mvvm/model/itemfactory.h" -#include "mvvm/model/propertyitem.h" -#include "mvvm/model/sessionitemtags.h" -#include "mvvm/serialization/jsonitemcopystrategy.h" - -using namespace ModelView; - -class JsonItemCopyStrategyTest : public ::testing::Test { -public: - JsonItemCopyStrategyTest() - : m_factory(std::make_unique<ItemFactory>(CreateStandardItemCatalogue())) - { - } - ~JsonItemCopyStrategyTest(); - - std::unique_ptr<JsonItemCopyStrategy> createCopyStrategy() - { - return std::make_unique<JsonItemCopyStrategy>(m_factory.get()); - } - - std::unique_ptr<ItemFactory> m_factory; -}; - -JsonItemCopyStrategyTest::~JsonItemCopyStrategyTest() = default; - -//! Saving/restoring PropertyItem. - -TEST_F(JsonItemCopyStrategyTest, propertyItem) -{ - auto strategy = createCopyStrategy(); - - PropertyItem item; - item.setData(42.0); - - auto copy = strategy->createCopy(&item); - - EXPECT_EQ(item.modelType(), copy->modelType()); - EXPECT_EQ(item.data<QVariant>(), copy->data<QVariant>()); - EXPECT_FALSE(item.identifier() == copy->identifier()); -} - -//! Saving/restoring CompoundItem. - -TEST_F(JsonItemCopyStrategyTest, compoundItem) -{ - auto strategy = createCopyStrategy(); - - CompoundItem item; - auto property = item.addProperty("thickness", 42.0); - - auto copy = strategy->createCopy(&item); - - EXPECT_EQ(item.modelType(), copy->modelType()); - EXPECT_EQ(copy->getItem("thickness")->data<double>(), property->data<double>()); - EXPECT_FALSE(copy->getItem("thickness")->identifier() == property->identifier()); - EXPECT_FALSE(item.identifier() == copy->identifier()); -} - -//! Saving/restoring CustomItem. - -TEST_F(JsonItemCopyStrategyTest, customItem) -{ - auto strategy = createCopyStrategy(); - - const std::string model_type(GUI::Constants::BaseType); - - // creating parent with one child - auto parent = std::make_unique<SessionItem>(model_type); - parent->setDisplayName("parent_name"); - parent->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - auto child = new SessionItem(model_type); - child->setDisplayName("child_name"); - parent->insertItem(child, TagRow::append()); - - // creating copy - auto parent_copy = strategy->createCopy(parent.get()); - - EXPECT_EQ(parent_copy->childrenCount(), 1); - EXPECT_EQ(parent_copy->modelType(), model_type); - EXPECT_EQ(parent_copy->displayName(), "parent_name"); - EXPECT_EQ(parent_copy->itemTags()->defaultTag(), "defaultTag"); - EXPECT_EQ(parent_copy->model(), nullptr); - EXPECT_FALSE(parent_copy->identifier() == parent->identifier()); - - // checking child reconstruction - auto child_copy = parent_copy->getItem("defaultTag"); - EXPECT_EQ(child_copy->parent(), parent_copy.get()); - EXPECT_EQ(child_copy->childrenCount(), 0); - EXPECT_EQ(child_copy->modelType(), model_type); - EXPECT_EQ(child_copy->displayName(), "child_name"); - EXPECT_EQ(child_copy->itemTags()->defaultTag(), ""); - EXPECT_FALSE(child_copy->identifier() == child->identifier()); -} diff --git a/mvvm/tests/testmodel/jsonitemdataconverter.test.cpp b/mvvm/tests/testmodel/jsonitemdataconverter.test.cpp deleted file mode 100644 index 20bf53dd72e..00000000000 --- a/mvvm/tests/testmodel/jsonitemdataconverter.test.cpp +++ /dev/null @@ -1,259 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/jsonitemdataconverter.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "folderbasedtest.h" -#include "google_test.h" -#include "mvvm/model/customvariants.h" -#include "mvvm/model/mvvm_types.h" -#include "mvvm/model/sessionitemdata.h" -#include "mvvm/serialization/jsonitemdataconverter.h" -#include "mvvm/serialization/jsonitemformatassistant.h" -#include "mvvm/serialization/jsonvariantconverter.h" -#include "test_utils.h" -#include <QJsonArray> -#include <QJsonObject> -#include <string> - -using namespace ModelView; - -//! Test convertion of SessionItemData from/to QJsonObject. - -class JsonItemDataConverterTest : public FolderBasedTest { -public: - JsonItemDataConverterTest() : FolderBasedTest("test_JsonItemData") {} - ~JsonItemDataConverterTest(); -}; - -JsonItemDataConverterTest::~JsonItemDataConverterTest() = default; - -//! Creating QJsonArray from SessionItemData. - -TEST_F(JsonItemDataConverterTest, getJson) -{ - JsonItemFormatAssistant assistant; - JsonItemDataConverter converter; - - // construction SessionItem data - SessionItemData data; - const int role = Qt::UserRole + 1; - data.setData(QVariant::fromValue(42), role); - data.setData(QVariant::fromValue(std::string("abc")), role + 2); - - // creating json object out of it - QJsonArray array = converter.to_json(data); - - // it should contain two json objects - EXPECT_EQ(array.size(), 2); - EXPECT_TRUE(array[0].isObject()); - EXPECT_TRUE(array[1].isObject()); - - // and these objects repesent DataRole - EXPECT_TRUE(assistant.isSessionItemData(array[0].toObject())); - EXPECT_TRUE(assistant.isSessionItemData(array[1].toObject())); -} - -//! From SessionItemData to json and back. - -TEST_F(JsonItemDataConverterTest, fromItemToJsonAndBack) -{ - JsonItemDataConverter converter; - - // initial data - const std::vector<int> roles = {1, 2, 3}; - const std::vector<QVariant> expected = {QVariant::fromValue(42), QVariant::fromValue(1.23), - QVariant::fromValue(std::string("abc"))}; - - // constructing SessionItemData - SessionItemData data; - for (size_t i = 0; i < roles.size(); ++i) - data.setData(expected[i], roles[i]); - - // constructing json array from data - QJsonArray array = converter.to_json(data); - auto fileName = TestUtils::TestFileName(testDir(), "itemdata.json"); - TestUtils::SaveJson(array, fileName); - - // constructing data from json array - SessionItemData data2; - converter.from_json(array, data2); - EXPECT_EQ(data2.roles().size(), 3u); - - EXPECT_EQ(data2.roles(), roles); - for (auto role : roles) { - EXPECT_EQ(data2.data(role), expected[role - 1]); - } -} - -//! By default tooltip role is enabled for read and write. - -TEST_F(JsonItemDataConverterTest, tooltipRole) -{ - // initial data - SessionItemData data; - data.setData(QVariant::fromValue(std::string("tooltip")), ItemDataRole::TOOLTIP); - - // constructing json array from data - JsonItemDataConverter converter; - QJsonArray array = converter.to_json(data); - - // constructing data from json array - SessionItemData data2; - converter.from_json(array, data2); - EXPECT_EQ(data2.roles().size(), 1u); - EXPECT_EQ(data2.data(ItemDataRole::TOOLTIP).value<std::string>(), "tooltip"); -} - -//! Write filtered roles, read all into empty SessionItemData. - -TEST_F(JsonItemDataConverterTest, writeFilteredReadAll) -{ - // initial data - SessionItemData data; - data.setData(QVariant::fromValue(std::string("abc")), ItemDataRole::IDENTIFIER); - data.setData(42, ItemDataRole::DATA); - data.setData(QVariant::fromValue(std::string("abc")), ItemDataRole::DISPLAY); - data.setData(QVariant::fromValue(std::string("tooltip")), ItemDataRole::TOOLTIP); - - // constructing json array from data - auto to_json_accept = [](auto role) { return role != ItemDataRole::TOOLTIP; }; - - JsonItemDataConverter converter(to_json_accept, {}); - QJsonArray array = converter.to_json(data); - - // constructing data from json array - SessionItemData data2; - converter.from_json(array, data2); - EXPECT_EQ(data2.roles().size(), 3u); - EXPECT_FALSE(data2.hasData(ItemDataRole::TOOLTIP)); -} - -//! Write all roles, read filtered into empty SessionItemData. - -TEST_F(JsonItemDataConverterTest, writeAllReadFiltered) -{ - // initial data - SessionItemData data; - data.setData(QVariant::fromValue(std::string("abc")), ItemDataRole::IDENTIFIER); - data.setData(42, ItemDataRole::DATA); - data.setData(QVariant::fromValue(std::string("abc")), ItemDataRole::DISPLAY); - data.setData(QVariant::fromValue(std::string("tooltip")), ItemDataRole::TOOLTIP); - - // constructing json array from data - auto from_json_accept = [](auto role) { return role == ItemDataRole::TOOLTIP; }; - - JsonItemDataConverter converter({}, from_json_accept); - QJsonArray array = converter.to_json(data); - - // constructing data from json array - SessionItemData data2; - converter.from_json(array, data2); - - // while array itself has 4 elements, restored data has only TOOLTIPS as defined in filter - EXPECT_EQ(array.size(), 4u); - EXPECT_EQ(data2.roles().size(), 1u); - EXPECT_TRUE(data2.hasData(ItemDataRole::TOOLTIP)); -} - -//! Update SessionItemData from json obtained from another JsonItemData. - -TEST_F(JsonItemDataConverterTest, updateFromJson) -{ - const std::vector<int> roles = {ItemDataRole::IDENTIFIER, ItemDataRole::DATA, - ItemDataRole::TOOLTIP}; - const std::vector<QVariant> variants = {QVariant::fromValue(std::string("identifier1")), - QVariant::fromValue(42), - QVariant::fromValue(std::string("tooltip1"))}; - - // initial data - SessionItemData data1; - for (size_t i = 0; i < roles.size(); ++i) - data1.setData(variants[i], roles[i]); - - // data intended for serialization - SessionItemData data2; - data1.setData(QVariant::fromValue(43), ItemDataRole::DATA); - data1.setData(QVariant::fromValue(std::string("identifier2")), ItemDataRole::IDENTIFIER); - - // constructing json array from data - JsonItemDataConverter converter; - QJsonArray array = converter.to_json(data2); - - // updating data1 from json array - converter.from_json(array, data1); - - // roles as in initial object, id+data as in data2, tooltip as in data1 - EXPECT_EQ(data1.roles(), roles); - EXPECT_EQ(data1.data(ItemDataRole::IDENTIFIER).value<std::string>(), - std::string("identifier2")); - EXPECT_EQ(data1.data(ItemDataRole::DATA).value<int>(), 43); - EXPECT_EQ(data1.data(ItemDataRole::TOOLTIP).value<std::string>(), std::string("tooltip1")); -} - -//! Checking factory method to create copying converter. -//! It is used normally in the situation, when the item data is fully serialized to JSON, and then -//! desirialized into empty item data. - -TEST_F(JsonItemDataConverterTest, createCopyConverter) -{ - auto converter = JsonItemDataConverter::createCopyConverter(); - - SessionItemData data; - data.setData(QVariant::fromValue(std::string("identifier")), ItemDataRole::IDENTIFIER); - data.setData(42, ItemDataRole::DATA); - data.setData(QVariant::fromValue(std::string("display")), ItemDataRole::DISPLAY); - data.setData(QVariant::fromValue(std::string("tooltip")), ItemDataRole::TOOLTIP); - - QJsonArray array = converter->to_json(data); - - SessionItemData data2; - converter->from_json(array, data2); - - //! Empty data should become the same. - EXPECT_EQ(data.roles(), data2.roles()); - EXPECT_EQ(data2.data(ItemDataRole::IDENTIFIER).value<std::string>(), "identifier"); - EXPECT_EQ(data2.data(ItemDataRole::DATA).value<int>(), 42); - EXPECT_EQ(data2.data(ItemDataRole::DISPLAY).value<std::string>(), "display"); - EXPECT_EQ(data2.data(ItemDataRole::TOOLTIP).value<std::string>(), "tooltip"); -} - -//! Checking factory method for typical scenario for copying whole data while writing, and updating -//! on reading. This is a repetition of previous test. - -TEST_F(JsonItemDataConverterTest, createProjectConverter) -{ - auto converter = JsonItemDataConverter::createProjectConverter(); - - SessionItemData data; - data.setData(QVariant::fromValue(std::string("identifier")), ItemDataRole::IDENTIFIER); - data.setData(42, ItemDataRole::DATA); - data.setData(QVariant::fromValue(std::string("display")), ItemDataRole::DISPLAY); - data.setData(QVariant::fromValue(std::string("tooltip")), ItemDataRole::TOOLTIP); - auto expected_roles = data.roles(); - - QJsonArray array = converter->to_json(data); - - // changing data - data.setData(QVariant::fromValue(std::string("identifier_new")), ItemDataRole::IDENTIFIER); - data.setData(43, ItemDataRole::DATA); - - // deserializing back - converter->from_json(array, data); - - //! DATA and IDENTIFIER roles should be as before the serialization - EXPECT_EQ(data.roles(), expected_roles); - EXPECT_EQ(data.data(ItemDataRole::IDENTIFIER).value<std::string>(), "identifier"); - EXPECT_EQ(data.data(ItemDataRole::DATA).value<int>(), 42); - EXPECT_EQ(data.data(ItemDataRole::DISPLAY).value<std::string>(), "display"); - EXPECT_EQ(data.data(ItemDataRole::TOOLTIP).value<std::string>(), "tooltip"); -} diff --git a/mvvm/tests/testmodel/jsonitemformatassistant.test.cpp b/mvvm/tests/testmodel/jsonitemformatassistant.test.cpp deleted file mode 100644 index 00d7b5602b4..00000000000 --- a/mvvm/tests/testmodel/jsonitemformatassistant.test.cpp +++ /dev/null @@ -1,126 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/jsonitemformatassistant.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/serialization/jsonitemformatassistant.h" -#include "mvvm/serialization/jsonvariantconverter.h" -#include <QJsonArray> -#include <QJsonObject> - -using namespace ModelView; - -//! Checks JsonItem class and its ability to convert SessionItems to json and back. - -class JsonItemFormatAssistantTest : public ::testing::Test { -public: - ~JsonItemFormatAssistantTest(); -}; - -JsonItemFormatAssistantTest::~JsonItemFormatAssistantTest() = default; - -//! Checks the validity of json object representing SessionItem. - -TEST_F(JsonItemFormatAssistantTest, isSessionItem) -{ - JsonItemFormatAssistant assistant; - - // empty json object is not valid - QJsonObject object; - EXPECT_FALSE(assistant.isSessionItem(object)); - - // it also should contain array - object[JsonItemFormatAssistant::modelKey] = "abc"; - object[JsonItemFormatAssistant::itemDataKey] = QJsonArray(); - object[JsonItemFormatAssistant::itemTagsKey] = 42; // intentionally incorrect - EXPECT_FALSE(assistant.isSessionItem(object)); - - // correctly constructed - object[JsonItemFormatAssistant::itemTagsKey] = QJsonObject(); - EXPECT_TRUE(assistant.isSessionItem(object)); -} - -//! Checks if json object is correctly identified as representing DataRole. - -TEST_F(JsonItemFormatAssistantTest, isValidDataRole) -{ - JsonItemFormatAssistant assistant; - JsonVariantConverter variant_converter; - - // valid json object representing DataRole - QJsonObject object; - object[JsonItemFormatAssistant::roleKey] = 42; - object[JsonItemFormatAssistant::variantKey] = variant_converter.get_json(QVariant(1.23)); - EXPECT_TRUE(assistant.isSessionItemData(object)); - - // invalid json object which can't represent DataRole - QJsonObject object2; - object2[JsonItemFormatAssistant::roleKey] = 42; - EXPECT_FALSE(assistant.isSessionItemData(object2)); - - // another invalid json object - QJsonObject object3; - object3[JsonItemFormatAssistant::roleKey] = 42; - object3[JsonItemFormatAssistant::variantKey] = variant_converter.get_json(QVariant(1.23)); - object3["abc"] = variant_converter.get_json(QVariant::fromValue(std::string("xxx"))); - EXPECT_FALSE(assistant.isSessionItemData(object3)); -} - -//! Checks the validity of json object representing SessionItemTags. - -TEST_F(JsonItemFormatAssistantTest, isSessionItemTags) -{ - JsonItemFormatAssistant assistant; - - // empty json object is not valid - QJsonObject object; - EXPECT_FALSE(assistant.isSessionItemTags(object)); - - // it also should contain array - object[JsonItemFormatAssistant::defaultTagKey] = "abc"; - object[JsonItemFormatAssistant::containerKey] = QJsonArray(); - EXPECT_TRUE(assistant.isSessionItemTags(object)); -} - -//! Checks the validity of json object representing SessionItemContainer. - -TEST_F(JsonItemFormatAssistantTest, isSessionItemContainer) -{ - JsonItemFormatAssistant assistant; - - // empty json object is not valid - QJsonObject object; - EXPECT_FALSE(assistant.isSessionItemContainer(object)); - - // it also should contain array - object[JsonItemFormatAssistant::tagInfoKey] = QJsonObject(); - object[JsonItemFormatAssistant::itemsKey] = QJsonArray(); - EXPECT_TRUE(assistant.isSessionItemContainer(object)); -} - -//! Validity of json object representing SessionModel. - -TEST_F(JsonItemFormatAssistantTest, isValidSessionModel) -{ - JsonItemFormatAssistant assistant; - - // empty json object is not valid - QJsonObject object; - EXPECT_FALSE(assistant.isSessionModel(object)); - - // json object representing valid SessionModel - QJsonObject object2; - object2[JsonItemFormatAssistant::sessionModelKey] = "abc"; - object2[JsonItemFormatAssistant::itemsKey] = QJsonArray(); - EXPECT_TRUE(assistant.isSessionModel(object2)); -} diff --git a/mvvm/tests/testmodel/jsonmodelconverter.test.cpp b/mvvm/tests/testmodel/jsonmodelconverter.test.cpp deleted file mode 100644 index 49476678028..00000000000 --- a/mvvm/tests/testmodel/jsonmodelconverter.test.cpp +++ /dev/null @@ -1,264 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/jsonmodelconverter.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "folderbasedtest.h" -#include "google_test.h" -#include "mvvm/model/itempool.h" -#include "mvvm/model/propertyitem.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionitemtags.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/model/taginfo.h" -#include "mvvm/serialization/jsonitem_types.h" -#include "mvvm/serialization/jsonitemformatassistant.h" -#include "mvvm/serialization/jsonmodelconverter.h" -#include "test_utils.h" -#include <QJsonArray> -#include <QJsonDocument> -#include <QJsonObject> -#include <stdexcept> - -using namespace ModelView; - -//! Checks JsonModel class and its ability to convert SessionModel to json and back. - -class JsonModelConverterTest : public FolderBasedTest { -public: - JsonModelConverterTest() : FolderBasedTest("test_JsonModelConverter") {} - ~JsonModelConverterTest(); -}; - -JsonModelConverterTest::~JsonModelConverterTest() = default; - -//! Creation of json object: empty model. - -TEST_F(JsonModelConverterTest, emptyModel) -{ - JsonModelConverter converter(ConverterMode::project); - SessionModel model("TestModel"); - - QJsonObject object = converter.to_json(model); - - EXPECT_EQ(object[JsonItemFormatAssistant::sessionModelKey], "TestModel"); - EXPECT_EQ(object[JsonItemFormatAssistant::itemsKey].toArray().size(), 0); - - JsonItemFormatAssistant assistant; - EXPECT_TRUE(assistant.isSessionModel(object)); -} - -//! Empty model to json and back. - -TEST_F(JsonModelConverterTest, emptyModelToJsonAndBack) -{ - JsonModelConverter converter(ConverterMode::project); - SessionModel model("TestModel"); - - QJsonObject object = converter.to_json(model); - - // attempt to reconstruct model of different type. - SessionModel target1("NewModel"); - EXPECT_THROW(converter.from_json(object, target1), std::runtime_error); - - // attempt to reconstruct non-empty model - SessionModel target2("TestModel"); - target2.insertItem<SessionItem>(); - EXPECT_NO_THROW(converter.from_json(object, target2)); - - // succesfull reconstruction - SessionModel target3("TestModel"); - EXPECT_NO_THROW(converter.from_json(object, target3)); - EXPECT_EQ(target3.rootItem()->childrenCount(), 0u); -} - -//! Creation of json object: single item in a model. - -TEST_F(JsonModelConverterTest, singleItemToJsonAndBack) -{ - JsonModelConverter converter(ConverterMode::project); - SessionModel model("TestModel"); - - auto item = model.insertItem<SessionItem>(); - - QJsonObject object = converter.to_json(model); - - // filling new model - SessionModel target("TestModel"); - converter.from_json(object, target); - EXPECT_EQ(target.rootItem()->childrenCount(), 1u); - auto reco_item = target.rootItem()->getItem("", 0); - EXPECT_EQ(reco_item->parent(), target.rootItem()); - EXPECT_EQ(reco_item->modelType(), item->modelType()); -} - -//! Filling model from json: parent and child in a model to json and back. - -TEST_F(JsonModelConverterTest, parentAndChildToJsonAndBack) -{ - JsonModelConverter converter(ConverterMode::project); - SessionModel model("TestModel"); - - // filling original model with content - auto parent = model.insertItem<SessionItem>(); - parent->setDisplayName("parent_name"); - parent->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - - parent->setData(QVariant::fromValue(42)); - auto child = model.insertItem<PropertyItem>(parent); - child->setDisplayName("child_name"); - - // writing model to json - QJsonObject object = converter.to_json(model); - - // reading model from json - SessionModel target("TestModel"); - converter.from_json(object, target); - - // accessing reconstructed parent and child - auto reco_parent = target.rootItem()->getItem("", 0); - auto reco_child = reco_parent->getItem("", 0); - - // checking parent reconstruction - EXPECT_EQ(reco_parent->model(), &target); - EXPECT_EQ(reco_parent->modelType(), GUI::Constants::BaseType); - EXPECT_EQ(reco_parent->parent(), target.rootItem()); - EXPECT_EQ(reco_parent->displayName(), - "SessionItem"); // Name changed because of ProjectConverter - EXPECT_EQ(reco_parent->childrenCount(), 1); - EXPECT_EQ(reco_parent->identifier(), parent->identifier()); - EXPECT_EQ(reco_parent->itemTags()->defaultTag(), "defaultTag"); - EXPECT_EQ(reco_parent->data<int>(), 42); - - // checking child reconstruction - EXPECT_EQ(reco_child->model(), &target); - EXPECT_EQ(reco_child->modelType(), GUI::Constants::PropertyType); - EXPECT_EQ(reco_child->parent(), reco_parent); - EXPECT_EQ(reco_child->displayName(), "Property"); // // Name changed because of ProjectConverter - EXPECT_EQ(reco_child->childrenCount(), 0); - EXPECT_EQ(reco_child->identifier(), child->identifier()); - EXPECT_EQ(reco_child->itemTags()->defaultTag(), ""); -} - -//! Item in a model to json and back: how persistent are identifiers. - -TEST_F(JsonModelConverterTest, identifiers) -{ - JsonModelConverter converter(ConverterMode::project); - auto pool1 = std::make_shared<ItemPool>(); - - // creating model and converting it to json - SessionModel source("SourceModel", pool1); - auto parent1 = source.insertItem<SessionItem>(); - QJsonObject json_source = converter.to_json(source); - - // creating source and filling it from json - auto pool2 = std::make_shared<ItemPool>(); - SessionModel target("SourceModel", pool2); - converter.from_json(json_source, target); - auto reco_parent = target.rootItem()->getItem("", 0); - - // comparing identifiers of two items from different models - auto id1 = parent1->identifier(); - auto id2 = reco_parent->identifier(); - EXPECT_EQ(id1, id2); - - // saving target in its own json - QJsonObject json_target = converter.to_json(target); - - // comparing text representations of two json - EXPECT_EQ(TestUtils::JsonToString(json_source), TestUtils::JsonToString(json_target)); - - // checking item registrations - EXPECT_EQ(pool1->item_for_key(id1), parent1); - EXPECT_EQ(pool2->item_for_key(id2), reco_parent); -} - -//! Filling model from json: parent and child in a model to json and back. - -TEST_F(JsonModelConverterTest, parentAndChildToFileAndBack) -{ - JsonModelConverter converter(ConverterMode::project); - SessionModel model("TestModel"); - - // filling original model with content - auto parent = model.insertItem<SessionItem>(); - parent->setDisplayName("parent_name"); - parent->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - - parent->setData(QVariant::fromValue(42)); - auto child = model.insertItem<PropertyItem>(parent); - child->setDisplayName("child_name"); - - // writing model to json - auto object = converter.to_json(model); - - // saving object to file - auto fileName = TestUtils::TestFileName(testDir(), "model.json"); - TestUtils::SaveJson(object, fileName); - - // converting document back to item - auto document = TestUtils::LoadJson(fileName); - SessionModel target("TestModel"); - converter.from_json(document.object(), target); - - // accessing reconstructed parent and child - auto reco_parent = target.rootItem()->getItem("", 0); - auto reco_child = reco_parent->getItem("", 0); - - // checking parent reconstruction - EXPECT_EQ(reco_parent->model(), &target); - EXPECT_EQ(reco_parent->modelType(), GUI::Constants::BaseType); - EXPECT_EQ(reco_parent->parent(), target.rootItem()); - EXPECT_EQ(reco_parent->displayName(), "SessionItem"); - EXPECT_EQ(reco_parent->childrenCount(), 1); - EXPECT_EQ(reco_parent->identifier(), parent->identifier()); - EXPECT_EQ(reco_parent->itemTags()->defaultTag(), "defaultTag"); - EXPECT_EQ(reco_parent->data<int>(), 42); - - // checking child reconstruction - EXPECT_EQ(reco_child->model(), &target); - EXPECT_EQ(reco_child->modelType(), GUI::Constants::PropertyType); - EXPECT_EQ(reco_child->parent(), reco_parent); - EXPECT_EQ(reco_child->displayName(), "Property"); - EXPECT_EQ(reco_child->childrenCount(), 0); - EXPECT_EQ(reco_child->identifier(), child->identifier()); - EXPECT_EQ(reco_child->itemTags()->defaultTag(), ""); -} - -//! Creation of json object (single item in a model), then writing same json object back -//! to model without emptying it. Real bug case: check if unsubscribtion mechanism works. - -TEST_F(JsonModelConverterTest, singleItemToJsonAndBackToSameModel) -{ - auto pool = std::make_shared<ItemPool>(); - - JsonModelConverter converter(ConverterMode::project); - SessionModel model("TestModel", pool); - auto item = model.insertItem<SessionItem>(); - - auto root_item = model.rootItem(); - auto root_id = root_item->identifier(); - auto item_id = item->identifier(); - - QJsonObject object = converter.to_json(model); - - // filling new model - converter.from_json(object, model); - - EXPECT_EQ(pool->size(), 2); - EXPECT_FALSE(pool->item_for_key(root_id) == model.rootItem()); // old root identifier has gone - EXPECT_TRUE(model.rootItem() != root_item); // old root item gone - - auto new_item = model.rootItem()->children().at(0); - EXPECT_EQ(pool->item_for_key(item_id), new_item); -} diff --git a/mvvm/tests/testmodel/jsontaginfoconverter.test.cpp b/mvvm/tests/testmodel/jsontaginfoconverter.test.cpp deleted file mode 100644 index d207a12f3bd..00000000000 --- a/mvvm/tests/testmodel/jsontaginfoconverter.test.cpp +++ /dev/null @@ -1,110 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/jsontaginfoconverter.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "folderbasedtest.h" -#include "google_test.h" -#include "mvvm/model/taginfo.h" -#include "mvvm/serialization/jsontaginfoconverter.h" -#include "test_utils.h" -#include <QJsonArray> -#include <QJsonDocument> -#include <QJsonObject> -#include <string> - -using namespace ModelView; - -//! Test convertion of TagInfo from/to QJsonObject. - -class JsonTagInfoConverterTest : public FolderBasedTest { -public: - JsonTagInfoConverterTest() : FolderBasedTest("test_JsonTagInfoConverter") {} - ~JsonTagInfoConverterTest(); -}; - -JsonTagInfoConverterTest::~JsonTagInfoConverterTest() = default; - -//! Checks if json object is correctly identified as representing TagInfo. - -TEST_F(JsonTagInfoConverterTest, isItemTag) -{ - JsonTagInfoConverter converter; - - // valid json object representing DataRole - QJsonObject object; - object[JsonTagInfoConverter::nameKey] = QString::fromStdString("tag1"); - object[JsonTagInfoConverter::minKey] = 0; - object[JsonTagInfoConverter::maxKey] = 1; - object[JsonTagInfoConverter::modelsKey] = QJsonArray(); - - EXPECT_TRUE(converter.isTagInfo(object)); - - // invalid (not fully constructed) json object which can't represent TagInfo - QJsonObject object2; - object2[JsonTagInfoConverter::minKey] = 42; - EXPECT_FALSE(converter.isTagInfo(object2)); -} - -//! Creating QJsonArray from TagInfo. - -TEST_F(JsonTagInfoConverterTest, toJson) -{ - JsonTagInfoConverter converter; - - TagInfo tag("tag1", 0, -1, std::vector<std::string>() = {}); - auto object = converter.to_json(tag); - - // this object represents TagInfo - EXPECT_TRUE(converter.isTagInfo(object)); -} - -//! From TagInfo to json and back. - -TEST_F(JsonTagInfoConverterTest, tagInfoToJsonAndBack) -{ - JsonTagInfoConverter converter; - - TagInfo tag("tag", 0, 42, std::vector<std::string>() = {"aaa", "bbb"}); - auto object = converter.to_json(tag); - - TagInfo reco_tag = converter.from_json(object); - - EXPECT_EQ(reco_tag.name(), tag.name()); - EXPECT_EQ(reco_tag.min(), tag.min()); - EXPECT_EQ(reco_tag.max(), tag.max()); - EXPECT_EQ(reco_tag.modelTypes(), tag.modelTypes()); -} - -//! To file and back. - -TEST_F(JsonTagInfoConverterTest, tagInfoToFileAndBack) -{ - const std::string tag_name("tag"); - const std::string model_type("model"); - JsonTagInfoConverter converter; - - TagInfo tag = TagInfo::propertyTag(tag_name, model_type); - auto object = converter.to_json(tag); - - // saving object to file - auto fileName = TestUtils::TestFileName(testDir(), "taginfo.json"); - TestUtils::SaveJson(object, fileName); - - auto document = TestUtils::LoadJson(fileName); - TagInfo reco_tag = converter.from_json(document.object()); - - EXPECT_EQ(reco_tag.name(), tag_name); - EXPECT_EQ(reco_tag.min(), 1); - EXPECT_EQ(reco_tag.max(), 1); - EXPECT_EQ(reco_tag.modelTypes(), std::vector<std::string>() = {model_type}); -} diff --git a/mvvm/tests/testmodel/jsonutils.test.cpp b/mvvm/tests/testmodel/jsonutils.test.cpp deleted file mode 100644 index bd4694be67f..00000000000 --- a/mvvm/tests/testmodel/jsonutils.test.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/jsonutils.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/serialization/jsonutils.h" -#include "mvvm/utils/reallimits.h" -#include <limits> - -using namespace ModelView; - -class JsonUtilsTest : public ::testing::Test { -protected: - ~JsonUtilsTest(); -}; - -JsonUtilsTest::~JsonUtilsTest() = default; - -TEST_F(JsonUtilsTest, toString) -{ - EXPECT_EQ(JsonUtils::ToString(RealLimits::limitless()), "limitless"); - EXPECT_EQ(JsonUtils::ToString(RealLimits::positive()), "positive"); - EXPECT_EQ(JsonUtils::ToString(RealLimits::nonnegative()), "nonnegative"); - EXPECT_EQ(JsonUtils::ToString(RealLimits::lowerLimited(1.0)), "lowerlimited"); - EXPECT_EQ(JsonUtils::ToString(RealLimits::lowerLimited(1.042)), "lowerlimited"); - EXPECT_EQ(JsonUtils::ToString(RealLimits::lowerLimited(-0.99)), "lowerlimited"); - EXPECT_EQ(JsonUtils::ToString(RealLimits::upperLimited(1.0)), "upperlimited"); - EXPECT_EQ(JsonUtils::ToString(RealLimits::limited(-1.0, 2.0)), "limited"); -} - -TEST_F(JsonUtilsTest, CreateLimits) -{ - EXPECT_EQ(JsonUtils::CreateLimits("limitless"), RealLimits::limitless()); - EXPECT_EQ(JsonUtils::CreateLimits("positive"), RealLimits::positive()); - EXPECT_EQ(JsonUtils::CreateLimits("nonnegative"), RealLimits::nonnegative()); - EXPECT_EQ(JsonUtils::CreateLimits("lowerlimited", 1.0, 0.0), RealLimits::lowerLimited(1.0)); - EXPECT_EQ(JsonUtils::CreateLimits("upperlimited", 0.0, 42.0), RealLimits::upperLimited(42.0)); - EXPECT_EQ(JsonUtils::CreateLimits("limited", -1.0, 2.0), RealLimits::limited(-1.0, 2.0)); -} diff --git a/mvvm/tests/testmodel/jsonvariantconverter.test.cpp b/mvvm/tests/testmodel/jsonvariantconverter.test.cpp deleted file mode 100644 index fcbdc5e07eb..00000000000 --- a/mvvm/tests/testmodel/jsonvariantconverter.test.cpp +++ /dev/null @@ -1,344 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/jsonvariantconverter.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "folderbasedtest.h" -#include "google_test.h" -#include "mvvm/model/comboproperty.h" -#include "mvvm/model/customvariants.h" -#include "mvvm/model/externalproperty.h" -#include "mvvm/model/variant_constants.h" -#include "mvvm/serialization/jsonvariantconverter.h" -#include "mvvm/utils/reallimits.h" -#include "test_utils.h" -#include <QColor> -#include <QJsonArray> -#include <QJsonDocument> -#include <QJsonObject> -#include <vector> - -using namespace ModelView; - -//! Test convertion of QVariant from/to QJsonObject. - -class JsonVariantConverterTest : public FolderBasedTest { -public: - JsonVariantConverterTest() : FolderBasedTest("test_JsonVariant") {} - ~JsonVariantConverterTest(); - - static QVariant ToJsonAndBack(const QVariant& variant) - { - JsonVariantConverter converter; - auto json = converter.get_json(variant); - return converter.get_variant(json); - } -}; - -JsonVariantConverterTest::~JsonVariantConverterTest() = default; - -//! Invalid QVariant conversion. - -TEST_F(JsonVariantConverterTest, invalidVariant) -{ - JsonVariantConverter converter; - - QVariant variant; - - // from variant to json object - auto object = converter.get_json(variant); - EXPECT_TRUE(converter.isVariant(object)); - - // from json object to variant - QVariant reco_variant = converter.get_variant(object); - EXPECT_FALSE(reco_variant.isValid()); - EXPECT_EQ(variant, reco_variant); -} - -//! Bool QVariant conversion. - -TEST_F(JsonVariantConverterTest, boolVariant) -{ - JsonVariantConverter converter; - - const bool value(true); - QVariant variant(value); - - // from variant to json object - auto object = converter.get_json(variant); - EXPECT_TRUE(converter.isVariant(object)); - - // from json object to variant - QVariant reco_variant = converter.get_variant(object); - EXPECT_TRUE(Utils::IsBoolVariant(reco_variant)); - EXPECT_EQ(reco_variant.value<bool>(), value); - EXPECT_EQ(reco_variant.typeName(), GUI::Constants::bool_type_name); - EXPECT_EQ(variant, reco_variant); - - EXPECT_EQ(ToJsonAndBack(true).value<bool>(), true); - EXPECT_EQ(ToJsonAndBack(false).value<bool>(), false); -} - -//! Int QVariant conversion. - -TEST_F(JsonVariantConverterTest, intVariant) -{ - JsonVariantConverter converter; - - const int value(42); - QVariant variant(value); - - // from variant to json object - auto object = converter.get_json(variant); - EXPECT_TRUE(converter.isVariant(object)); - - // from json object to variant - QVariant reco_variant = converter.get_variant(object); - EXPECT_TRUE(Utils::IsIntVariant(reco_variant)); - EXPECT_EQ(reco_variant.value<int>(), value); - EXPECT_EQ(reco_variant.typeName(), GUI::Constants::int_type_name); - EXPECT_EQ(variant, reco_variant); -} - -//! QVariant(std::string) conversion. - -TEST_F(JsonVariantConverterTest, stringVariant) -{ - JsonVariantConverter converter; - - const std::string value("abc"); - QVariant variant = QVariant::fromValue(value); - - // from variant to json object - auto object = converter.get_json(variant); - EXPECT_TRUE(converter.isVariant(object)); - - // from json object to variant - QVariant reco_variant = converter.get_variant(object); - EXPECT_TRUE(Utils::IsStdStringVariant(reco_variant)); - EXPECT_EQ(reco_variant.typeName(), GUI::Constants::string_type_name); - EXPECT_EQ(reco_variant.value<std::string>(), value); - - EXPECT_EQ(variant, reco_variant); -} - -//! QVariant(double) conversion. - -TEST_F(JsonVariantConverterTest, doubleVariant) -{ - JsonVariantConverter converter; - - double value(43.2); - QVariant variant = QVariant::fromValue(value); - EXPECT_EQ(variant.typeName(), GUI::Constants::double_type_name); - - // from variant to json object - auto object = converter.get_json(variant); - EXPECT_TRUE(converter.isVariant(object)); - - // from json object to variant - QVariant reco_variant = converter.get_variant(object); - EXPECT_TRUE(Utils::IsDoubleVariant(reco_variant)); - EXPECT_EQ(reco_variant.value<double>(), value); - EXPECT_EQ(variant, reco_variant); - - // more numbers - value = 1e-03; - EXPECT_DOUBLE_EQ(ToJsonAndBack(value).value<double>(), value); - value = 0.99e-7; - EXPECT_DOUBLE_EQ(ToJsonAndBack(value).value<double>(), value); - value = 3.14159265359; - EXPECT_DOUBLE_EQ(ToJsonAndBack(value).value<double>(), value); -} - -//! QVariant(double) conversion. Special value 43.0 which Qt likes to cast to int based variant. - -TEST_F(JsonVariantConverterTest, doubleVariantWhichLooksAsInt) -{ - JsonVariantConverter converter; - - double value(43.0); // special value which Qt like to cast to int-based variant - QVariant variant = QVariant::fromValue(value); - EXPECT_EQ(variant.typeName(), GUI::Constants::double_type_name); - EXPECT_TRUE(Utils::IsDoubleVariant(variant)); - - // from variant to json object - auto object = converter.get_json(variant); - EXPECT_TRUE(converter.isVariant(object)); - - // from json object to variant - QVariant reco_variant = converter.get_variant(object); - EXPECT_TRUE(Utils::IsDoubleVariant(reco_variant)); - EXPECT_EQ(reco_variant.value<double>(), value); - EXPECT_EQ(variant, reco_variant); -} - -//! QVariant(std::vector<double>) conversion. - -TEST_F(JsonVariantConverterTest, vectorOfDoubleVariant) -{ - JsonVariantConverter converter; - - const std::vector<double> value = {42.0, 43.0, 44.0}; - QVariant variant = QVariant::fromValue(value); - - // from variant to json object - auto object = converter.get_json(variant); - EXPECT_TRUE(converter.isVariant(object)); - - // from json object to variant - QVariant reco_variant = converter.get_variant(object); - EXPECT_TRUE(Utils::IsDoubleVectorVariant(reco_variant)); - EXPECT_EQ(reco_variant.value<std::vector<double>>(), value); - EXPECT_EQ(variant, reco_variant); -} - -//! QVariant(ComboProperty) conversion. - -TEST_F(JsonVariantConverterTest, comboPropertyVariant) -{ - JsonVariantConverter converter; - - ComboProperty value = ComboProperty::createFrom(std::vector<std::string>({"a1", "a2", "a3"})); - value.setSelected("a1", false); - value.setSelected("a2", true); - value.setSelected("a3", true); - - QVariant variant = QVariant::fromValue(value); - - // from variant to json object - auto object = converter.get_json(variant); - EXPECT_TRUE(converter.isVariant(object)); - - // from json object to variant - QVariant reco_variant = converter.get_variant(object); - EXPECT_TRUE(Utils::IsComboVariant(reco_variant)); - EXPECT_EQ(reco_variant.value<ComboProperty>(), value); - EXPECT_EQ(variant, reco_variant); -} - -//! QVariant(QColor) conversion. - -TEST_F(JsonVariantConverterTest, colorVariant) -{ - JsonVariantConverter converter; - - const QColor value(Qt::red); - QVariant variant = QVariant::fromValue(value); - - // from variant to json object - auto object = converter.get_json(variant); - EXPECT_TRUE(converter.isVariant(object)); - - // from json object to variant - QVariant reco_variant = converter.get_variant(object); - EXPECT_TRUE(Utils::IsColorVariant(reco_variant)); - EXPECT_EQ(reco_variant.value<QColor>(), value); - EXPECT_EQ(variant, reco_variant); -} - -//! QVariant(ExternalProperty) conversion. - -TEST_F(JsonVariantConverterTest, extPropVariant) -{ - JsonVariantConverter converter; - - const ExternalProperty value("abc", QColor(Qt::green), "123"); - QVariant variant = QVariant::fromValue(value); - - // from variant to json object - auto object = converter.get_json(variant); - EXPECT_TRUE(converter.isVariant(object)); - - // from json object to variant - QVariant reco_variant = converter.get_variant(object); - EXPECT_TRUE(Utils::IsExtPropertyVariant(reco_variant)); - EXPECT_EQ(reco_variant.value<ExternalProperty>(), value); - EXPECT_EQ(variant, reco_variant); -} - -//! QVariant(RealLimits) convertion. - -TEST_F(JsonVariantConverterTest, realLimitsVariant) -{ - JsonVariantConverter converter; - - RealLimits value = RealLimits::limited(1.0, 2.0); - QVariant variant = QVariant::fromValue(value); - - // from variant to json object - auto object = converter.get_json(variant); - EXPECT_TRUE(converter.isVariant(object)); - - // from json object to variant - QVariant reco_variant = converter.get_variant(object); - EXPECT_TRUE(Utils::IsRealLimitsVariant(reco_variant)); - EXPECT_EQ(reco_variant.value<RealLimits>(), value); - EXPECT_EQ(variant, reco_variant); - - // more values - value = RealLimits::positive(); - EXPECT_EQ(ToJsonAndBack(QVariant::fromValue(value)).value<RealLimits>(), value); - value = RealLimits::nonnegative(); - EXPECT_EQ(ToJsonAndBack(QVariant::fromValue(value)).value<RealLimits>(), value); - value = RealLimits::limited(0.123, 0.124); - EXPECT_EQ(ToJsonAndBack(QVariant::fromValue(value)).value<RealLimits>(), value); -} - -//! Writing variants to file and reading them back. - -TEST_F(JsonVariantConverterTest, toFileAndBack) -{ - const std::string string_value("abc"); - const std::vector<double> vector_value = {42.1, 42.2, 42.3}; - ComboProperty combo = ComboProperty::createFrom({"a 1", "a 2", "a/3"}); - combo.setSelected("a 1", false); - combo.setSelected("a 2", true); - combo.setSelected("a/3", true); - QColor color(Qt::red); - ExternalProperty extprop("abc", QColor(Qt::green), "1-2-3"); - - std::vector<QVariant> variants = {QVariant(), - QVariant(true), - QVariant(42), - QVariant(42.3), - QVariant(43.0), - QVariant(43.1), - QVariant(0.99e-7), - QVariant(3.14159265359), - QVariant::fromValue(string_value), - QVariant::fromValue(vector_value), - QVariant::fromValue(combo), - QVariant::fromValue(color), - QVariant::fromValue(extprop), - QVariant::fromValue(RealLimits::positive()), - QVariant::fromValue(RealLimits::limited(1.12, 2.32))}; - - // preparing array of json objects - JsonVariantConverter converter; - QJsonArray json_array; - for (auto var : variants) - json_array.append(converter.get_json(var)); - - // writing to file - auto fileName = TestUtils::TestFileName(testDir(), "variants.json"); - TestUtils::SaveJson(json_array, fileName); - - // reading variants from file - auto document = TestUtils::LoadJson(fileName); - std::vector<QVariant> reco_variants; - for (const auto x : document.array()) - reco_variants.push_back(converter.get_variant(x.toObject())); - - // comparing initial and reconstructed variants - EXPECT_EQ(variants, reco_variants); -} diff --git a/mvvm/tests/testmodel/linkeditem.test.cpp b/mvvm/tests/testmodel/linkeditem.test.cpp deleted file mode 100644 index 3cfbaa8a34b..00000000000 --- a/mvvm/tests/testmodel/linkeditem.test.cpp +++ /dev/null @@ -1,128 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/linkeditem.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mockwidgets.h" -#include "mvvm/model/itempool.h" -#include "mvvm/model/propertyitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/standarditems/linkeditem.h" - -using namespace ModelView; -using ::testing::_; - -//! LinkedItem tests. - -class LinkedItemTest : public ::testing::Test { -public: - ~LinkedItemTest(); -}; - -LinkedItemTest::~LinkedItemTest() = default; - -//! Initial state of item when it is created outside of model context. - -TEST_F(LinkedItemTest, initialState) -{ - LinkedItem item; - EXPECT_EQ(item.get(), nullptr); - EXPECT_EQ(item.data<std::string>(), ""); -} - -//! Link in single model context. - -TEST_F(LinkedItemTest, sameModelContext) -{ - SessionModel model; - auto item = model.insertItem<PropertyItem>(); - auto link = model.insertItem<LinkedItem>(); - - // no link by default - EXPECT_EQ(link->get(), nullptr); - - // setting the link - link->setLink(item); - - // now linked - EXPECT_EQ(link->get(), item); - EXPECT_EQ(link->data<std::string>(), item->identifier()); -} - -//! Link in different model context. - -TEST_F(LinkedItemTest, differentModelContext) -{ - auto pool = std::make_shared<ItemPool>(); - - SessionModel model1("TestModel1", pool); - SessionModel model2("TestModel2", pool); - - auto item = model1.insertItem<PropertyItem>(); - auto link = model2.insertItem<LinkedItem>(); - - // no link by default - EXPECT_EQ(link->get(), nullptr); - - // setting the link - link->setLink(item); - - // now linked - EXPECT_EQ(link->get(), item); -} - -//! Signals when links is set. - -TEST_F(LinkedItemTest, onSetLink) -{ - SessionModel model; - auto item = model.insertItem<PropertyItem>(); - auto link = model.insertItem<LinkedItem>(); - - // no link by default - EXPECT_EQ(link->get(), nullptr); - - MockWidgetForItem widget(link); - - auto expected_role = ItemDataRole::DATA; - auto expected_item = link; - EXPECT_CALL(widget, onDataChange(expected_item, expected_role)).Times(1); - EXPECT_CALL(widget, onPropertyChange(_, _)).Times(0); - - // making action - link->setLink(item); -} - -//! Link in different model context. - -TEST_F(LinkedItemTest, setNullAsLink) -{ - auto pool = std::make_shared<ItemPool>(); - - SessionModel model("TestModel", pool); - auto link = model.insertItem<LinkedItem>(); - auto item = model.insertItem<PropertyItem>(); - - // no link by default - EXPECT_EQ(link->get(), nullptr); - - // setting link - link->setLink(item); - EXPECT_EQ(link->get(), item); - - // still null link - link->setLink(nullptr); - EXPECT_EQ(link->get(), nullptr); - EXPECT_TRUE(link->data<QVariant>().isValid()); - EXPECT_EQ(link->data<std::string>(), ""); -} diff --git a/mvvm/tests/testmodel/modelhaschangedcontroller.test.cpp b/mvvm/tests/testmodel/modelhaschangedcontroller.test.cpp deleted file mode 100644 index 6685a9d83e9..00000000000 --- a/mvvm/tests/testmodel/modelhaschangedcontroller.test.cpp +++ /dev/null @@ -1,135 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/modelhaschangedcontroller.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/propertyitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/project/modelhaschangedcontroller.h" - -using namespace ModelView; - -//! Tests for ModelHasChangedController class. - -class ModelHasChangedControllerTest : public ::testing::Test { -public: - ~ModelHasChangedControllerTest(); -}; - -ModelHasChangedControllerTest::~ModelHasChangedControllerTest() = default; - -//! Tests initial state of the controller. - -TEST_F(ModelHasChangedControllerTest, initialState) -{ - SessionModel model; - ModelHasChangedController controller(&model); - EXPECT_FALSE(controller.hasChanged()); -} - -//! Tests if controller 'sees' item insertion. - -TEST_F(ModelHasChangedControllerTest, insertItem) -{ - SessionModel model; - ModelHasChangedController controller(&model); - - model.insertItem<PropertyItem>(); - EXPECT_TRUE(controller.hasChanged()); - - controller.resetChanged(); - EXPECT_FALSE(controller.hasChanged()); -} - -//! Tests if controller sees item insertion. - -TEST_F(ModelHasChangedControllerTest, removeItem) -{ - SessionModel model; - model.insertItem<PropertyItem>(); - - ModelHasChangedController controller(&model); - EXPECT_FALSE(controller.hasChanged()); - - model.removeItem(model.rootItem(), {"", 0}); - - EXPECT_TRUE(controller.hasChanged()); -} - -//! Tests if controller sees item data change. - -TEST_F(ModelHasChangedControllerTest, dataChanged) -{ - SessionModel model; - auto item = model.insertItem<PropertyItem>(); - - ModelHasChangedController controller(&model); - EXPECT_FALSE(controller.hasChanged()); - - item->setData(42.0); - EXPECT_TRUE(controller.hasChanged()); -} - -//! Tests if controller sees model reset. - -TEST_F(ModelHasChangedControllerTest, modelReset) -{ - SessionModel model; - model.insertItem<PropertyItem>(); - - ModelHasChangedController controller(&model); - EXPECT_FALSE(controller.hasChanged()); - - model.clear(); - EXPECT_TRUE(controller.hasChanged()); -} - -//! Tests callback functioning. - -TEST_F(ModelHasChangedControllerTest, callback) -{ - int change_count{0}; - auto on_change = [&change_count]() { change_count++; }; - - SessionModel model; - ModelHasChangedController controller(&model, on_change); - - model.insertItem<PropertyItem>(); - EXPECT_TRUE(controller.hasChanged()); - EXPECT_EQ(change_count, 1); - - controller.resetChanged(); - EXPECT_FALSE(controller.hasChanged()); - EXPECT_EQ(change_count, 1); -} - -//! Tests callback functioning. - -TEST_F(ModelHasChangedControllerTest, timeOfLife) -{ - int change_count{0}; - auto on_change = [&change_count]() { change_count++; }; - - SessionModel model; - auto controller = std::make_unique<ModelHasChangedController>(&model, on_change); - - // change the model, check controller - model.insertItem<PropertyItem>(); - EXPECT_TRUE(controller->hasChanged()); - EXPECT_EQ(change_count, 1); - - // remove controller, change the model - controller.reset(); - model.insertItem<PropertyItem>(); - EXPECT_EQ(change_count, 1); -} diff --git a/mvvm/tests/testmodel/modellistener.test.cpp b/mvvm/tests/testmodel/modellistener.test.cpp deleted file mode 100644 index a841df460f1..00000000000 --- a/mvvm/tests/testmodel/modellistener.test.cpp +++ /dev/null @@ -1,98 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/modellistener.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/propertyitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/signals/modellistener.h" -#include <memory> - -using namespace ModelView; - -//! Tests of ModelListener class. - -class ModelListenerTest : public ::testing::Test { -public: - class TestListener : public ModelListener<SessionModel> { - public: - TestListener(SessionModel* model) : ModelListener(model) {} - ~TestListener(); - }; - - ~ModelListenerTest(); -}; - -ModelListenerTest::~ModelListenerTest() = default; -ModelListenerTest::TestListener::~TestListener() = default; - -//! Initial state. - -TEST_F(ModelListenerTest, initialState) -{ - SessionModel model; - TestListener listener(&model); - EXPECT_EQ(listener.model(), &model); -} - -TEST_F(ModelListenerTest, onDataChange) -{ - auto model = std::make_unique<SessionModel>(); - auto listener = std::make_unique<TestListener>(model.get()); - - int counter{0}; - auto on_data_change = [&counter](SessionItem*, int) { counter++; }; - listener->setOnDataChange(on_data_change); - - auto item = model->insertItem<PropertyItem>(); - item->setData(42.0); - - EXPECT_EQ(counter, 1); -} - -//! Check that controller aware of item deletion. - -TEST_F(ModelListenerTest, modelDeletedBeforeListener) -{ - auto model = std::make_unique<SessionModel>(); - auto listener = std::make_unique<TestListener>(model.get()); - - EXPECT_EQ(listener->model(), model.get()); - - model.reset(); - EXPECT_EQ(listener->model(), nullptr); -} - -//! Checks that the listenerr can be deleted before the model. - -TEST_F(ModelListenerTest, listenerDeletedBeforeTheModel) -{ - // create model and its listener - auto model = std::make_unique<SessionModel>(); - auto listener = std::make_unique<TestListener>(model.get()); - - // assign to data-changed event - int counter{0}; - auto on_data_change = [&counter](SessionItem*, int) { counter++; }; - listener->setOnDataChange(on_data_change); - - // changing the data and checking the listener - auto item = model->insertItem<PropertyItem>(); - item->setData(42.0); - EXPECT_EQ(counter, 1); - - // deleting the listener and trying to change the data again - listener.reset(); - item->setData(43.0); - EXPECT_EQ(counter, 1); -} diff --git a/mvvm/tests/testmodel/modelmapper.test.cpp b/mvvm/tests/testmodel/modelmapper.test.cpp deleted file mode 100644 index db3da8e5bf0..00000000000 --- a/mvvm/tests/testmodel/modelmapper.test.cpp +++ /dev/null @@ -1,230 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/modelmapper.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mockwidgets.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionitemtags.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/model/tagrow.h" -#include "mvvm/signals/modelmapper.h" - -using namespace ModelView; -using ::testing::_; - -//! Testing ModelMapper callbacks on basic model manipulations. - -class ModelMapperTest : public ::testing::Test { -public: - ~ModelMapperTest(); -}; - -ModelMapperTest::~ModelMapperTest() = default; - -//! Setting item data and checking corresponding signal. - -TEST(ModelMapperTest, onDataChange) -{ - SessionModel model; - MockWidgetForModel widget(&model); - - EXPECT_CALL(widget, onItemInserted(_, _)); - auto item = model.insertItem<SessionItem>(model.rootItem()); - - // expecting signal to be called once - const int role = ItemDataRole::DATA; - EXPECT_CALL(widget, onDataChange(item, role)).Times(1); - EXPECT_CALL(widget, onItemInserted(_, _)).Times(0); - EXPECT_CALL(widget, onAboutToRemoveItem(_, _)).Times(0); - EXPECT_CALL(widget, onItemRemoved(_, _)).Times(0); - EXPECT_CALL(widget, onModelDestroyed(_)).Times(0); - EXPECT_CALL(widget, onModelAboutToBeReset(_)).Times(0); - EXPECT_CALL(widget, onModelReset(_)).Times(0); - model.setData(item, 42.0, ItemDataRole::DATA); // perform action - - // setting same data shouldn't trigger the signal - EXPECT_CALL(widget, onDataChange(_, _)).Times(0); - EXPECT_CALL(widget, onItemInserted(_, _)).Times(0); - EXPECT_CALL(widget, onItemRemoved(_, _)).Times(0); - EXPECT_CALL(widget, onAboutToRemoveItem(_, _)).Times(0); - EXPECT_CALL(widget, onModelDestroyed(_)).Times(0); - EXPECT_CALL(widget, onModelAboutToBeReset(_)).Times(0); - EXPECT_CALL(widget, onModelReset(_)).Times(0); - model.setData(item, 42.0, ItemDataRole::DATA); // perform action - - // setting new data through item - EXPECT_CALL(widget, onDataChange(item, role)).Times(1); - EXPECT_CALL(widget, onItemInserted(_, _)).Times(0); - EXPECT_CALL(widget, onItemRemoved(_, _)).Times(0); - EXPECT_CALL(widget, onAboutToRemoveItem(_, _)).Times(0); - EXPECT_CALL(widget, onModelDestroyed(_)).Times(0); - EXPECT_CALL(widget, onModelAboutToBeReset(_)).Times(0); - EXPECT_CALL(widget, onModelReset(_)).Times(0); - item->setData(43.0); // perform action -} - -//! Testing signaling after unsubscribe. - -TEST(ModelMapperTest, onDataChangeUnsubscribe) -{ - SessionModel model; - MockWidgetForModel widget(&model); - - EXPECT_CALL(widget, onItemInserted(_, _)); - auto item = model.insertItem<SessionItem>(model.rootItem()); - - // unsubscribing - widget.setModel(nullptr); - - // no calls should be done - EXPECT_CALL(widget, onDataChange(_, _)).Times(0); - EXPECT_CALL(widget, onItemInserted(_, _)).Times(0); - EXPECT_CALL(widget, onAboutToRemoveItem(_, _)).Times(0); - EXPECT_CALL(widget, onItemRemoved(_, _)).Times(0); - EXPECT_CALL(widget, onModelDestroyed(_)).Times(0); - EXPECT_CALL(widget, onModelAboutToBeReset(_)).Times(0); - EXPECT_CALL(widget, onModelReset(_)).Times(0); - - item->setData(43.0); // perform action -} - -//! Testing signaling after subscribe/unsubscribe twice. - -TEST(ModelMapperTest, onDataChangeMultipleUnsubscribe) -{ - SessionModel model; - MockWidgetForModel widget(&model); - - EXPECT_CALL(widget, onItemInserted(_, _)); - auto item = model.insertItem<SessionItem>(model.rootItem()); - - // unsubscribing - widget.setModel(nullptr); - // subscribing again - widget.setModel(&model); - // unsubscribing - widget.setModel(nullptr); - // subscribing again - widget.setModel(&model); - - const int role = ItemDataRole::DATA; - EXPECT_CALL(widget, onDataChange(item, role)).Times(1); - EXPECT_CALL(widget, onItemInserted(_, _)).Times(0); - EXPECT_CALL(widget, onAboutToRemoveItem(_, _)).Times(0); - EXPECT_CALL(widget, onItemRemoved(_, _)).Times(0); - EXPECT_CALL(widget, onModelDestroyed(_)).Times(0); - EXPECT_CALL(widget, onModelAboutToBeReset(_)).Times(0); - EXPECT_CALL(widget, onModelReset(_)).Times(0); - model.setData(item, 42.0, ItemDataRole::DATA); // perform action -} - -//! Inserting item and checking corresponding signals. - -TEST(ModelMapperTest, onItemInserted) -{ - SessionModel model; - MockWidgetForModel widget(&model); - - EXPECT_CALL(widget, onDataChange(_, _)).Times(0); - const TagRow expected_tagrow{model.rootItem()->itemTags()->defaultTag(), 0}; - EXPECT_CALL(widget, onItemInserted(model.rootItem(), expected_tagrow)).Times(1); - EXPECT_CALL(widget, onItemRemoved(_, _)).Times(0); - EXPECT_CALL(widget, onAboutToRemoveItem(_, _)).Times(0); - EXPECT_CALL(widget, onModelDestroyed(_)).Times(0); - EXPECT_CALL(widget, onModelAboutToBeReset(_)).Times(0); - EXPECT_CALL(widget, onModelReset(_)).Times(0); - - // perform action - model.insertItem<SessionItem>(model.rootItem(), expected_tagrow); -} - -//! Inserting item and checking corresponding signals. - -TEST(ModelMapperTest, onItemRemoved) -{ - SessionModel model; - MockWidgetForModel widget(&model); - - const TagRow expected_tagrow{model.rootItem()->itemTags()->defaultTag(), 0}; - EXPECT_CALL(widget, onItemInserted(model.rootItem(), expected_tagrow)).Times(1); - model.insertItem<SessionItem>(model.rootItem(), expected_tagrow); - - EXPECT_CALL(widget, onDataChange(_, _)).Times(0); - EXPECT_CALL(widget, onItemInserted(_, _)).Times(0); - EXPECT_CALL(widget, onItemRemoved(model.rootItem(), expected_tagrow)).Times(1); - EXPECT_CALL(widget, onAboutToRemoveItem(model.rootItem(), expected_tagrow)).Times(1); - EXPECT_CALL(widget, onModelDestroyed(_)).Times(0); - EXPECT_CALL(widget, onModelAboutToBeReset(_)).Times(0); - EXPECT_CALL(widget, onModelReset(_)).Times(0); - // perform action - model.removeItem(model.rootItem(), expected_tagrow); -} - -//! Testing signals on model destruction. - -TEST(ModelMapperTest, onModelDestroyed) -{ - auto model = std::make_unique<SessionModel>(); - auto widget = std::make_unique<MockWidgetForModel>(model.get()); - - EXPECT_CALL(*widget, onDataChange(_, _)).Times(0); - EXPECT_CALL(*widget, onItemInserted(_, _)).Times(0); - EXPECT_CALL(*widget, onItemRemoved(_, _)).Times(0); - EXPECT_CALL(*widget, onAboutToRemoveItem(_, _)).Times(0); - EXPECT_CALL(*widget, onModelAboutToBeReset(_)).Times(0); - EXPECT_CALL(*widget, onModelReset(_)).Times(0); - EXPECT_CALL(*widget, onModelDestroyed(model.get())).Times(1); - - // perform action - model.reset(); -} - -//! Testing signals on model destruction. - -TEST(ModelMapperTest, onModelReset) -{ - auto model = std::make_unique<SessionModel>(); - auto widget = std::make_unique<MockWidgetForModel>(model.get()); - - EXPECT_CALL(*widget, onDataChange(_, _)).Times(0); - EXPECT_CALL(*widget, onItemInserted(_, _)).Times(0); - EXPECT_CALL(*widget, onItemRemoved(_, _)).Times(0); - EXPECT_CALL(*widget, onAboutToRemoveItem(_, _)).Times(0); - EXPECT_CALL(*widget, onModelDestroyed(_)).Times(0); - EXPECT_CALL(*widget, onModelAboutToBeReset(_)).Times(1); - EXPECT_CALL(*widget, onModelReset(model.get())).Times(1); - - // perform action - model->clear(); -} - -//! Testing signals on cleaning the model using rebuild function. - -TEST(ModelMapperTest, onClearRebuild) -{ - auto model = std::make_unique<SessionModel>(); - - auto widget = std::make_unique<MockWidgetForModel>(model.get()); - - EXPECT_CALL(*widget, onDataChange(_, _)).Times(0); - EXPECT_CALL(*widget, onItemInserted(_, _)).Times(1); - EXPECT_CALL(*widget, onItemRemoved(_, _)).Times(0); - EXPECT_CALL(*widget, onAboutToRemoveItem(_, _)).Times(0); - EXPECT_CALL(*widget, onModelDestroyed(_)).Times(0); - EXPECT_CALL(*widget, onModelAboutToBeReset(_)).Times(1); - EXPECT_CALL(*widget, onModelReset(model.get())).Times(1); - - auto rebuild = [](auto item) { item->insertItem(new SessionItem, TagRow::append()); }; - model->clear(rebuild); -} diff --git a/mvvm/tests/testmodel/modelutils.test.cpp b/mvvm/tests/testmodel/modelutils.test.cpp deleted file mode 100644 index c2ddb8d1a46..00000000000 --- a/mvvm/tests/testmodel/modelutils.test.cpp +++ /dev/null @@ -1,173 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/modelutils.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/modelutils.h" -#include "toyitems.h" -#include "toymodel.h" - -using namespace ModelView; - -class ModelUtilsTest : public ::testing::Test { -public: - ~ModelUtilsTest(); -}; - -ModelUtilsTest::~ModelUtilsTest() = default; - -TEST_F(ModelUtilsTest, topItem) -{ - ToyItems::SampleModel model; - EXPECT_EQ(Utils::TopItem<>(&model), nullptr); - EXPECT_EQ(Utils::TopItem<SessionItem>(&model), nullptr); - EXPECT_EQ(Utils::TopItem<ToyItems::MultiLayerItem>(&model), nullptr); - - auto multilayer1 = model.insertItem<ToyItems::MultiLayerItem>(); - model.insertItem<ToyItems::MultiLayerItem>(); - - EXPECT_EQ(Utils::TopItem<>(&model), multilayer1); - EXPECT_EQ(Utils::TopItem<SessionItem>(&model), multilayer1); - EXPECT_EQ(Utils::TopItem<ToyItems::MultiLayerItem>(&model), multilayer1); -} - -TEST_F(ModelUtilsTest, topItems) -{ - ToyItems::SampleModel model; - EXPECT_EQ(Utils::TopItems<>(&model).size(), 0); - EXPECT_EQ(Utils::TopItems<SessionItem>(&model).size(), 0); - EXPECT_EQ(Utils::TopItems<ToyItems::MultiLayerItem>(&model).size(), 0); - - auto multilayer1 = model.insertItem<ToyItems::MultiLayerItem>(); - auto particle = model.insertItem<ToyItems::ParticleItem>(); - auto multilayer2 = model.insertItem<ToyItems::MultiLayerItem>(); - - std::vector<SessionItem*> expected1 = {multilayer1, particle, multilayer2}; - EXPECT_EQ(Utils::TopItems<SessionItem>(&model), expected1); - std::vector<ToyItems::MultiLayerItem*> expected2 = {multilayer1, multilayer2}; - EXPECT_EQ(Utils::TopItems<ToyItems::MultiLayerItem>(&model), expected2); -} - -TEST_F(ModelUtilsTest, findItems) -{ - ToyItems::SampleModel model; - EXPECT_EQ(Utils::FindItems<>(&model).size(), 1); // because of rootItem - EXPECT_EQ(Utils::FindItems<SessionItem>(&model).size(), 1); // because of rootItem - EXPECT_EQ(Utils::FindItems<ToyItems::MultiLayerItem>(&model).size(), 0); - - auto multilayer1 = model.insertItem<ToyItems::MultiLayerItem>(); - model.insertItem<ToyItems::ParticleItem>(); - auto multilayer2 = model.insertItem<ToyItems::MultiLayerItem>(); - - std::vector<ToyItems::MultiLayerItem*> expected2 = {multilayer1, multilayer2}; - EXPECT_EQ(Utils::FindItems<ToyItems::MultiLayerItem>(&model), expected2); - - // adding layers to multilayer - auto layer1 = model.insertItem<ToyItems::LayerItem>(multilayer1); - auto layer2 = model.insertItem<ToyItems::LayerItem>(multilayer2); - - std::vector<ToyItems::LayerItem*> expected3 = {layer1, layer2}; - EXPECT_EQ(Utils::FindItems<ToyItems::LayerItem>(&model), expected3); -} - -TEST_F(ModelUtilsTest, CreateCopy) -{ - ToyItems::SampleModel model; - auto layer = model.insertItem<ToyItems::LayerItem>(); - layer->setProperty(ToyItems::LayerItem::P_THICKNESS, 42.0); - - auto modelCopy = Utils::CreateCopy<ToyItems::SampleModel>(model); - auto layerCopy = modelCopy->topItem<ToyItems::LayerItem>(); - EXPECT_EQ(layerCopy->property<double>(ToyItems::LayerItem::P_THICKNESS), 42.0); - - // Copied model has unique identifiers - EXPECT_FALSE(model.rootItem()->identifier() == modelCopy->rootItem()->identifier()); - EXPECT_FALSE(layerCopy->identifier() == layer->identifier()); -} - -TEST_F(ModelUtilsTest, CreateClone) -{ - ToyItems::SampleModel model; - auto layer = model.insertItem<ToyItems::LayerItem>(); - layer->setProperty(ToyItems::LayerItem::P_THICKNESS, 42.0); - - auto modelCopy = Utils::CreateClone<ToyItems::SampleModel>(model); - auto layerCopy = modelCopy->topItem<ToyItems::LayerItem>(); - EXPECT_EQ(layerCopy->property<double>(ToyItems::LayerItem::P_THICKNESS), 42.0); - - // Copied model has unique identifiers - EXPECT_TRUE(layerCopy->identifier() == layer->identifier()); - - // root item by current conveniton still has unique identifier event for cloned model - // probably for the uniformity this has to be changed, and test below changed to EXPECT_TRUE - // This will require change in JsonModelConverter - EXPECT_FALSE(model.rootItem()->identifier() == modelCopy->rootItem()->identifier()); -} - -TEST_F(ModelUtilsTest, DeleteItemFromModel) -{ - ToyItems::SampleModel model; - - auto item = model.insertItem<SessionItem>(); - EXPECT_EQ(model.rootItem()->childrenCount(), 1); - Utils::DeleteItemFromModel(item); - EXPECT_EQ(model.rootItem()->childrenCount(), 0); -} - -TEST_F(ModelUtilsTest, MoveItemUp) -{ - ToyItems::SampleModel model; - - auto multilayer = model.insertItem<ToyItems::MultiLayerItem>(); - auto layer0 = model.insertItem<ToyItems::LayerItem>(multilayer); - auto layer1 = model.insertItem<ToyItems::LayerItem>(multilayer); - auto layer2 = model.insertItem<ToyItems::LayerItem>(multilayer); - - std::vector<SessionItem*> expected = {layer0, layer1, layer2}; - - // original layout - EXPECT_EQ(multilayer->getItems(ToyItems::MultiLayerItem::T_LAYERS), expected); - - // moving top layer up doesn't change the order - Utils::MoveUp(layer0); - EXPECT_EQ(multilayer->getItems(ToyItems::MultiLayerItem::T_LAYERS), expected); - - // moving bottom layer up does change the order - Utils::MoveUp(layer2); - expected = {layer0, layer2, layer1}; - EXPECT_EQ(multilayer->getItems(ToyItems::MultiLayerItem::T_LAYERS), expected); -} - -TEST_F(ModelUtilsTest, MoveItemDown) -{ - ToyItems::SampleModel model; - - auto multilayer = model.insertItem<ToyItems::MultiLayerItem>(); - auto layer0 = model.insertItem<ToyItems::LayerItem>(multilayer); - auto layer1 = model.insertItem<ToyItems::LayerItem>(multilayer); - auto layer2 = model.insertItem<ToyItems::LayerItem>(multilayer); - - std::vector<SessionItem*> expected = {layer0, layer1, layer2}; - - // original layout - EXPECT_EQ(multilayer->getItems(ToyItems::MultiLayerItem::T_LAYERS), expected); - - // moving bottom layer down doesn't change the order - Utils::MoveDown(layer2); - EXPECT_EQ(multilayer->getItems(ToyItems::MultiLayerItem::T_LAYERS), expected); - - // moving top layer down doesn't change the order - Utils::MoveDown(layer0); - expected = {layer1, layer0, layer2}; - EXPECT_EQ(multilayer->getItems(ToyItems::MultiLayerItem::T_LAYERS), expected); -} diff --git a/mvvm/tests/testmodel/moveitemcommand.test.cpp b/mvvm/tests/testmodel/moveitemcommand.test.cpp deleted file mode 100644 index fa317199564..00000000000 --- a/mvvm/tests/testmodel/moveitemcommand.test.cpp +++ /dev/null @@ -1,335 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/moveitemcommand.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/commands/moveitemcommand.h" -#include "mvvm/model/itemutils.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/model/taginfo.h" - -using namespace ModelView; - -class MoveItemCommandTest : public ::testing::Test { -public: - ~MoveItemCommandTest(); -}; - -MoveItemCommandTest::~MoveItemCommandTest() = default; - -TEST_F(MoveItemCommandTest, rootContextNext) -{ - SessionModel model; - auto item0 = model.insertItem<SessionItem>(model.rootItem()); // 0 - auto item1 = model.insertItem<SessionItem>(model.rootItem()); // 1 - auto item2 = model.insertItem<SessionItem>(model.rootItem()); // 2 - auto item3 = model.insertItem<SessionItem>(model.rootItem()); // 3 - - // expecting 4 items in the order of insertion - std::vector<SessionItem*> expected = {item0, item1, item2, item3}; - EXPECT_EQ(model.rootItem()->children(), expected); - - // moving item1 to the next position - MoveItemCommand command(item1, model.rootItem(), {"", 2}); - command.execute(); - EXPECT_EQ(std::get<bool>(command.result()), true); - - // expecting new order of items - expected = {item0, item2, item1, item3}; - EXPECT_EQ(model.rootItem()->children(), expected); - - // undoing command - command.undo(); - EXPECT_EQ(std::get<bool>(command.result()), true); - expected = {item0, item1, item2, item3}; - EXPECT_EQ(model.rootItem()->children(), expected); - - // redoing - command.execute(); - EXPECT_EQ(std::get<bool>(command.result()), true); - EXPECT_EQ(command.isObsolete(), false); - - expected = {item0, item2, item1, item3}; - EXPECT_EQ(model.rootItem()->children(), expected); -} - -TEST_F(MoveItemCommandTest, rootContextSamePos) -{ - SessionModel model; - auto item0 = model.insertItem<SessionItem>(model.rootItem()); - auto item1 = model.insertItem<SessionItem>(model.rootItem()); - auto item2 = model.insertItem<SessionItem>(model.rootItem()); - auto item3 = model.insertItem<SessionItem>(model.rootItem()); - - // expecting 4 items in the order of insertion - std::vector<SessionItem*> expected = {item0, item1, item2, item3}; - EXPECT_EQ(model.rootItem()->children(), expected); - - // moving item1 to the same position - MoveItemCommand command(item1, model.rootItem(), {"", 1}); - command.execute(); - EXPECT_EQ(std::get<bool>(command.result()), true); - EXPECT_EQ(command.isObsolete(), false); - - // expecting new order of items - expected = {item0, item1, item2, item3}; - EXPECT_EQ(model.rootItem()->children(), expected); - - // undoing command - command.undo(); - EXPECT_EQ(std::get<bool>(command.result()), true); - EXPECT_EQ(command.isObsolete(), false); - - expected = {item0, item1, item2, item3}; - EXPECT_EQ(model.rootItem()->children(), expected); -} - -TEST_F(MoveItemCommandTest, rootContextPrev) -{ - SessionModel model; - auto item0 = model.insertItem<SessionItem>(model.rootItem()); - auto item1 = model.insertItem<SessionItem>(model.rootItem()); - auto item2 = model.insertItem<SessionItem>(model.rootItem()); - auto item3 = model.insertItem<SessionItem>(model.rootItem()); - - // expecting 4 items in the order of insertion - std::vector<SessionItem*> expected = {item0, item1, item2, item3}; - EXPECT_EQ(model.rootItem()->children(), expected); - - // moving item2 to item1's place - MoveItemCommand command(item2, model.rootItem(), {"", 1}); - command.execute(); - EXPECT_EQ(std::get<bool>(command.result()), true); - EXPECT_EQ(command.isObsolete(), false); - - // expecting new order of items - expected = {item0, item2, item1, item3}; - EXPECT_EQ(model.rootItem()->children(), expected); - - // undoing command - command.undo(); - EXPECT_EQ(std::get<bool>(command.result()), true); - EXPECT_EQ(command.isObsolete(), false); - - expected = {item0, item1, item2, item3}; - EXPECT_EQ(model.rootItem()->children(), expected); -} - -TEST_F(MoveItemCommandTest, rootContextLast) -{ - SessionModel model; - auto item0 = model.insertItem<SessionItem>(model.rootItem()); - auto item1 = model.insertItem<SessionItem>(model.rootItem()); - auto item2 = model.insertItem<SessionItem>(model.rootItem()); - auto item3 = model.insertItem<SessionItem>(model.rootItem()); - - // expecting 4 items in the order of insertion - std::vector<SessionItem*> expected = {item0, item1, item2, item3}; - EXPECT_EQ(model.rootItem()->children(), expected); - - // moving item0 in the back of the list - MoveItemCommand command(item0, model.rootItem(), {"", model.rootItem()->childrenCount() - 1}); - command.execute(); - EXPECT_EQ(std::get<bool>(command.result()), true); - EXPECT_EQ(command.isObsolete(), false); - - // expecting new order of items - expected = {item1, item2, item3, item0}; - EXPECT_EQ(model.rootItem()->children(), expected); - - // undoing command - command.undo(); - EXPECT_EQ(std::get<bool>(command.result()), true); - EXPECT_EQ(command.isObsolete(), false); - - expected = {item0, item1, item2, item3}; - EXPECT_EQ(model.rootItem()->children(), expected); -} - -TEST_F(MoveItemCommandTest, rootContextLast2) -{ - SessionModel model; - auto item0 = model.insertItem<SessionItem>(model.rootItem()); - auto item1 = model.insertItem<SessionItem>(model.rootItem()); - auto item2 = model.insertItem<SessionItem>(model.rootItem()); - auto item3 = model.insertItem<SessionItem>(model.rootItem()); - - // expecting 4 items in the order of insertion - std::vector<SessionItem*> expected = {item0, item1, item2, item3}; - EXPECT_EQ(model.rootItem()->children(), expected); - - // moving item0 in the back of the list - MoveItemCommand command(item0, model.rootItem(), {"", 3}); - command.execute(); - EXPECT_EQ(std::get<bool>(command.result()), true); - EXPECT_EQ(command.isObsolete(), false); - - // expecting new order of items - expected = {item1, item2, item3, item0}; - EXPECT_EQ(model.rootItem()->children(), expected); - - // undoing command - command.undo(); - EXPECT_EQ(std::get<bool>(command.result()), true); - EXPECT_EQ(command.isObsolete(), false); - - expected = {item0, item1, item2, item3}; - EXPECT_EQ(model.rootItem()->children(), expected); -} - -TEST_F(MoveItemCommandTest, fromRootToParent) -{ - SessionModel model; - auto item0 = model.insertItem<SessionItem>(model.rootItem()); - auto parent = model.insertItem<SessionItem>(model.rootItem()); - parent->registerTag(TagInfo::universalTag("tag1"), /*set_as_default*/ true); - - auto child0 = model.insertItem<SessionItem>(parent); - auto child1 = model.insertItem<SessionItem>(parent); - - // expected items for root item - std::vector<SessionItem*> expected = {item0, parent}; - EXPECT_EQ(model.rootItem()->children(), expected); - - // expected items for parent - expected = {child0, child1}; - EXPECT_EQ(parent->children(), expected); - - // moving item0 from root to parent - MoveItemCommand command(item0, parent, {"", 1}); - command.execute(); - EXPECT_EQ(std::get<bool>(command.result()), true); - EXPECT_EQ(command.isObsolete(), false); - - // expected items for root item - expected = {parent}; - EXPECT_EQ(model.rootItem()->children(), expected); - - // expected items for parent - expected = {child0, item0, child1}; - EXPECT_EQ(parent->children(), expected); - - // undoing command - command.undo(); - EXPECT_EQ(std::get<bool>(command.result()), true); - EXPECT_EQ(command.isObsolete(), false); - - // expected items for root item - expected = {item0, parent}; - EXPECT_EQ(model.rootItem()->children(), expected); - - // expected items for parent - expected = {child0, child1}; - EXPECT_EQ(parent->children(), expected); -} - -TEST_F(MoveItemCommandTest, fromParentToRoot) -{ - SessionModel model; - auto item0 = model.insertItem<SessionItem>(model.rootItem()); - auto parent = model.insertItem<SessionItem>(model.rootItem()); - parent->registerTag(TagInfo::universalTag("tag1"), /*set_as_default*/ true); - - auto child0 = model.insertItem<SessionItem>(parent); - auto child1 = model.insertItem<SessionItem>(parent); - - // expected items for root item - std::vector<SessionItem*> expected = {item0, parent}; - EXPECT_EQ(model.rootItem()->children(), expected); - - // expected items for parent - expected = {child0, child1}; - EXPECT_EQ(parent->children(), expected); - - // moving child0 from parent to root - MoveItemCommand command(child0, model.rootItem(), {"", 0}); - command.execute(); - EXPECT_EQ(std::get<bool>(command.result()), true); - EXPECT_EQ(command.isObsolete(), false); - - // expected items for root item - expected = {child0, item0, parent}; - EXPECT_EQ(model.rootItem()->children(), expected); - - // expected items for parent - expected = {child1}; - EXPECT_EQ(parent->children(), expected); - - // undoing command - command.undo(); - EXPECT_EQ(std::get<bool>(command.result()), true); - EXPECT_EQ(command.isObsolete(), false); - - // expected items for root item - expected = {item0, parent}; - EXPECT_EQ(model.rootItem()->children(), expected); - - // expected items for parent - expected = {child0, child1}; - EXPECT_EQ(parent->children(), expected); -} - -TEST_F(MoveItemCommandTest, betweenParentTags) -{ - SessionModel model; - auto parent = model.insertItem<SessionItem>(model.rootItem()); - parent->registerTag(TagInfo::universalTag("tag1")); - parent->registerTag(TagInfo::universalTag("tag2")); - - auto child0 = model.insertItem<SessionItem>(parent, "tag1"); - auto child1 = model.insertItem<SessionItem>(parent, "tag1"); - auto child2 = model.insertItem<SessionItem>(parent, "tag2"); - auto child3 = model.insertItem<SessionItem>(parent, "tag2"); - - // expected items for root item - std::vector<SessionItem*> expected = {parent}; - EXPECT_EQ(model.rootItem()->children(), expected); - - // expected items for parent - expected = {child0, child1, child2, child3}; - EXPECT_EQ(parent->children(), expected); - - // moving child2 to another tag - MoveItemCommand command(child2, parent, {"tag1", 0}); - command.execute(); - EXPECT_EQ(std::get<bool>(command.result()), true); - EXPECT_EQ(command.isObsolete(), false); - - // expected items for root item - expected = {parent}; - EXPECT_EQ(model.rootItem()->children(), expected); - - // expected items for parents tag - expected = {child2, child0, child1, child3}; - EXPECT_EQ(parent->children(), expected); - expected = {child2, child0, child1}; - EXPECT_EQ(parent->getItems("tag1"), expected); - expected = {child3}; - EXPECT_EQ(parent->getItems("tag2"), expected); - - // undoing command - command.undo(); - EXPECT_EQ(std::get<bool>(command.result()), true); - EXPECT_EQ(command.isObsolete(), false); - - // expected items for root item - expected = {parent}; - EXPECT_EQ(model.rootItem()->children(), expected); - - // expected items for parent - expected = {child0, child1}; - EXPECT_EQ(parent->getItems("tag1"), expected); - expected = {child2, child3}; - EXPECT_EQ(parent->getItems("tag2"), expected); -} diff --git a/mvvm/tests/testmodel/numericutils.test.cpp b/mvvm/tests/testmodel/numericutils.test.cpp deleted file mode 100644 index 12a4894a5c8..00000000000 --- a/mvvm/tests/testmodel/numericutils.test.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/numericutils.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/utils/numericutils.h" -#include <cmath> - -using namespace ModelView; - -class NumericUtilsTest : public ::testing::Test { -public: - ~NumericUtilsTest(); -}; - -NumericUtilsTest::~NumericUtilsTest() = default; - -TEST_F(NumericUtilsTest, areAlmostEqual) -{ - EXPECT_TRUE(Utils::AreAlmostEqual(0.0, 0.0)); - EXPECT_TRUE(Utils::AreAlmostEqual(1.0, 1.0)); - EXPECT_TRUE(Utils::AreAlmostEqual(10.0 / 100.0, 100.0 / 1000.0)); - EXPECT_TRUE(Utils::AreAlmostEqual(std::sin(0.0), 0.0)); - EXPECT_FALSE(Utils::AreAlmostEqual(std::cos(0.0), 0.0)); -} diff --git a/mvvm/tests/testmodel/path.test.cpp b/mvvm/tests/testmodel/path.test.cpp deleted file mode 100644 index 97f0b8d4a2b..00000000000 --- a/mvvm/tests/testmodel/path.test.cpp +++ /dev/null @@ -1,131 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/path.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/modelutils.h" -#include "mvvm/model/path.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/model/taginfo.h" -#include <memory> - -using namespace ModelView; - -class PathTest : public ::testing::Test { -public: - ~PathTest(); -}; - -PathTest::~PathTest() = default; - -TEST_F(PathTest, initialState) -{ - Path path; - EXPECT_TRUE(path.str().empty()); -} - -TEST_F(PathTest, append) -{ - Path path; - path.append(1); - EXPECT_EQ(path.str(), "1"); - - path.append(2); - EXPECT_EQ(path.str(), "1,2"); - - path.prepend(3); - EXPECT_EQ(path.str(), "3,1,2"); -} - -TEST_F(PathTest, fromVector) -{ - Path path = Path::fromVector({1, 2, 3}); - EXPECT_EQ(path.str(), "1,2,3"); -} - -TEST_F(PathTest, fromString) -{ - Path path = Path::fromString("3,2,3"); - EXPECT_EQ(path.str(), "3,2,3"); -} - -TEST_F(PathTest, PathFromItem) -{ - SessionModel model; - - // unexisting path - EXPECT_TRUE(Utils::PathFromItem(nullptr).str().empty()); - // yet another unexisting path - auto alienItem = std::make_unique<SessionItem>(); - EXPECT_TRUE(Utils::PathFromItem(alienItem.get()).str().empty()); - - // three children beneeth root item - auto item0 = model.insertItem<SessionItem>(); - item0->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - auto item1 = model.insertItem<SessionItem>(); - item1->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - auto item2 = model.insertItem<SessionItem>(); - item2->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - - EXPECT_EQ(Utils::PathFromItem(item0).str(), "0"); - EXPECT_EQ(Utils::PathFromItem(item1).str(), "1"); - EXPECT_EQ(Utils::PathFromItem(item2).str(), "2"); - - // adding granchildren to item0 - auto child00 = model.insertItem<SessionItem>(item0); - auto child01 = model.insertItem<SessionItem>(item0); - - EXPECT_EQ(Utils::PathFromItem(child00).str(), "0,0"); - EXPECT_EQ(Utils::PathFromItem(child01).str(), "0,1"); - - // adding grandchildren to item2 - auto child20 = model.insertItem<SessionItem>(item2); - child20->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - - auto child200 = model.insertItem<SessionItem>(child20); - auto child201 = model.insertItem<SessionItem>(child20); - - EXPECT_EQ(Utils::PathFromItem(child200).str(), "2,0,0"); - EXPECT_EQ(Utils::PathFromItem(child201).str(), "2,0,1"); -} - -TEST_F(PathTest, itemFromPath) -{ - SessionModel model; - - // access to non-existing item - Path non_existing; - non_existing.append(8); - EXPECT_EQ(Utils::ItemFromPath(model, non_existing), nullptr); - - auto item0 = model.insertItem<SessionItem>(); - item0->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - auto item1 = model.insertItem<SessionItem>(); - item1->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - auto item2 = model.insertItem<SessionItem>(); - item2->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - - EXPECT_EQ(Utils::ItemFromPath(model, Path::fromVector({0})), item0); - EXPECT_EQ(Utils::ItemFromPath(model, Path::fromVector({1})), item1); - EXPECT_EQ(Utils::ItemFromPath(model, Path::fromVector({2})), item2); - - auto child20 = model.insertItem<SessionItem>(item2); - child20->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - auto child200 = model.insertItem<SessionItem>(child20); - auto child201 = model.insertItem<SessionItem>(child20); - - EXPECT_EQ(Utils::ItemFromPath(model, Path::fromVector({2, 0})), child20); - EXPECT_EQ(Utils::ItemFromPath(model, Path::fromVector({2, 0, 0})), child200); - EXPECT_EQ(Utils::ItemFromPath(model, Path::fromVector({2, 0, 1})), child201); -} diff --git a/mvvm/tests/testmodel/plottableitems.test.cpp b/mvvm/tests/testmodel/plottableitems.test.cpp deleted file mode 100644 index 5ef46c4bd03..00000000000 --- a/mvvm/tests/testmodel/plottableitems.test.cpp +++ /dev/null @@ -1,57 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/plottableitems.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/comboproperty.h" -#include "mvvm/standarditems/plottableitems.h" -#include <QColor> - -using namespace ModelView; - -//! Testing PlottableItemsTest. - -class PlottableItemsTest : public ::testing::Test { -public: - ~PlottableItemsTest(); -}; - -PlottableItemsTest::~PlottableItemsTest() = default; - -//! Initial state. - -TEST_F(PlottableItemsTest, penItem_initialState) -{ - PenItem item; - EXPECT_EQ(item.property<QColor>(PenItem::P_COLOR), QColor(Qt::black)); - EXPECT_EQ(item.property<int>(PenItem::P_WIDTH), 1); - EXPECT_EQ(item.property<ComboProperty>(PenItem::P_STYLE).currentIndex(), Qt::SolidLine); -} - -TEST_F(PlottableItemsTest, penItem_setSelected) -{ - PenItem item; - - item.setSelected(true); - EXPECT_EQ(item.property<ComboProperty>(PenItem::P_STYLE).currentIndex(), Qt::DashLine); - - item.setSelected(false); - EXPECT_EQ(item.property<ComboProperty>(PenItem::P_STYLE).currentIndex(), Qt::SolidLine); -} - -TEST_F(PlottableItemsTest, penItem_setNamedColor) -{ - PenItem item; - item.setNamedColor("mediumaquamarine"); - EXPECT_EQ(item.colorName(), std::string("#66cdaa")); -} diff --git a/mvvm/tests/testmodel/progresshandler.test.cpp b/mvvm/tests/testmodel/progresshandler.test.cpp deleted file mode 100644 index fac76bf911e..00000000000 --- a/mvvm/tests/testmodel/progresshandler.test.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/progresshandler.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/utils/progresshandler.h" - -using namespace ModelView; - -class ProgressHandlerTest : public ::testing::Test { -public: - ~ProgressHandlerTest(); -}; - -ProgressHandlerTest::~ProgressHandlerTest() = default; - -TEST_F(ProgressHandlerTest, initialState) -{ - ProgressHandler handler; - EXPECT_FALSE(handler.has_interrupt_request()); -} - -TEST_F(ProgressHandlerTest, fullConstructor) -{ - size_t max_ticks = 1000; - int progress{0}; - auto on_progress_change = [&progress](int value) { - progress = value; - return false; - }; - - ProgressHandler handler(on_progress_change, max_ticks); - - handler.setCompletedTicks(100); - EXPECT_FALSE(handler.has_interrupt_request()); - EXPECT_EQ(progress, 10); - - handler.setCompletedTicks(900); - EXPECT_FALSE(handler.has_interrupt_request()); - EXPECT_EQ(progress, 100); // reports value in percents -} - -TEST_F(ProgressHandlerTest, interruptRequest) -{ - size_t max_ticks = 1000; - int progress{0}; - auto on_progress_change = [&progress](int value) { - progress = value; - return true; - }; - - ProgressHandler handler(on_progress_change, max_ticks); - - handler.setCompletedTicks(1000); - EXPECT_TRUE(handler.has_interrupt_request()); - EXPECT_EQ(progress, 100); // reports value in percents - - // checking reset - handler.reset(); - EXPECT_FALSE(handler.has_interrupt_request()); - handler.setCompletedTicks(100); - EXPECT_EQ(progress, 10); // reports value in percents -} diff --git a/mvvm/tests/testmodel/project.test.cpp b/mvvm/tests/testmodel/project.test.cpp deleted file mode 100644 index a0789451eb3..00000000000 --- a/mvvm/tests/testmodel/project.test.cpp +++ /dev/null @@ -1,139 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/project.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "folderbasedtest.h" -#include "google_test.h" -#include "mvvm/model/propertyitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/project/project.h" -#include "mvvm/project/project_types.h" -#include "mvvm/utils/fileutils.h" -#include "test_utils.h" -#include <cctype> - -using namespace ModelView; - -namespace { -const std::string samplemodel_name = "SampleModel"; -const std::string materialmodel_name = "MaterialModel"; - -//! Constructs json file name from SessionModel typeName (as it is done internaly by Project). -std::string get_json_filename(const std::string& model_name) -{ - std::string result(model_name); - std::transform(result.begin(), result.end(), result.begin(), ::tolower); - return result + ".json"; -} - -} // namespace - -//! Tests for Project class. - -class ProjectTest : public FolderBasedTest { -public: - ProjectTest() - : FolderBasedTest("test_ProjectTest") - , sample_model(std::make_unique<SessionModel>(samplemodel_name)) - , material_model(std::make_unique<SessionModel>(materialmodel_name)) - { - } - ~ProjectTest(); - - std::vector<SessionModel*> models() const - { - return {sample_model.get(), material_model.get()}; - }; - - ProjectContext createContext() - { - ProjectContext result; - result.m_models_callback = [this]() { return models(); }; - return result; - } - - std::unique_ptr<SessionModel> sample_model; - std::unique_ptr<SessionModel> material_model; -}; - -ProjectTest::~ProjectTest() = default; - -TEST_F(ProjectTest, initialState) -{ - Project project(createContext()); - EXPECT_TRUE(project.projectDir().empty()); - EXPECT_FALSE(project.isModified()); -} - -//! Testing saveModel. - -TEST_F(ProjectTest, saveModel) -{ - Project project(createContext()); - - // create project directory and save file - auto project_dir = createEmptyDir("Untitled1"); - project.save(project_dir); - - EXPECT_EQ(project.projectDir(), project_dir); - EXPECT_FALSE(project.isModified()); - - auto sample_json = Utils::join(project_dir, get_json_filename(samplemodel_name)); - EXPECT_TRUE(Utils::exists(sample_json)); - - auto material_json = Utils::join(project_dir, get_json_filename(materialmodel_name)); - EXPECT_TRUE(Utils::exists(material_json)); -} - -//! Testing loadModel. - -TEST_F(ProjectTest, loadModel) -{ - Project project(createContext()); - - auto item0 = sample_model->insertItem<PropertyItem>(); - item0->setData(std::string("sample_model_item")); - auto item0_identifier = item0->identifier(); - - auto item1 = material_model->insertItem<PropertyItem>(); - item1->setData(std::string("material_model_item")); - auto item1_identifier = item1->identifier(); - - // create project directory and save file - auto project_dir = createEmptyDir("Untitled2"); - - EXPECT_TRUE(project.isModified()); - project.save(project_dir); - EXPECT_FALSE(project.isModified()); - - EXPECT_EQ(project.projectDir(), project_dir); - - // cleaning models - sample_model->clear(); - material_model->clear(); - EXPECT_EQ(sample_model->rootItem()->childrenCount(), 0); - EXPECT_EQ(material_model->rootItem()->childrenCount(), 0); - EXPECT_TRUE(project.isModified()); - - // loading - project.load(project_dir); - EXPECT_EQ(sample_model->rootItem()->childrenCount(), 1); - EXPECT_EQ(material_model->rootItem()->childrenCount(), 1); - - // checking identifiers - EXPECT_EQ(sample_model->rootItem()->children()[0]->identifier(), item0_identifier); - EXPECT_EQ(material_model->rootItem()->children()[0]->identifier(), item1_identifier); - - EXPECT_EQ(project.projectDir(), project_dir); - EXPECT_FALSE(project.isModified()); -} diff --git a/mvvm/tests/testmodel/projectchangecontroller.test.cpp b/mvvm/tests/testmodel/projectchangecontroller.test.cpp deleted file mode 100644 index 81eb26f00c6..00000000000 --- a/mvvm/tests/testmodel/projectchangecontroller.test.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/projectchangecontroller.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/propertyitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/project/projectchangecontroller.h" - -using namespace ModelView; - -//! Tests for ProjectChangeController class. - -class ProjectChangeControllerTest : public ::testing::Test { -public: - ~ProjectChangeControllerTest(); -}; - -ProjectChangeControllerTest::~ProjectChangeControllerTest() = default; - -TEST_F(ProjectChangeControllerTest, initialState) -{ - SessionModel sample_model("SampleModel"); - SessionModel material_model("MaterialModel"); - std::vector<SessionModel*> models = {&sample_model, &material_model}; - - ProjectChangedController controller(models); - EXPECT_FALSE(controller.hasChanged()); -} - -TEST_F(ProjectChangeControllerTest, twoModelsChange) -{ - SessionModel sample_model("SampleModel"); - SessionModel material_model("MaterialModel"); - std::vector<SessionModel*> models = {&sample_model, &material_model}; - - ProjectChangedController controller(models); - - sample_model.insertItem<PropertyItem>(); - material_model.insertItem<PropertyItem>(); - - EXPECT_TRUE(controller.hasChanged()); - - controller.resetChanged(); - EXPECT_FALSE(controller.hasChanged()); -} - -TEST_F(ProjectChangeControllerTest, callback) -{ - int model_changed_count{0}; - - SessionModel sample_model("SampleModel"); - SessionModel material_model("MaterialModel"); - std::vector<SessionModel*> models = {&sample_model, &material_model}; - - auto on_model_changed = [&model_changed_count]() { ++model_changed_count; }; - ProjectChangedController controller(models, on_model_changed); - - // changing first model - sample_model.insertItem<PropertyItem>(); - EXPECT_TRUE(controller.hasChanged()); - EXPECT_EQ(model_changed_count, 1); - - // changing second model - material_model.insertItem<PropertyItem>(); - EXPECT_TRUE(controller.hasChanged()); - EXPECT_EQ(model_changed_count, 1); // controller reports only once - - controller.resetChanged(); - EXPECT_FALSE(controller.hasChanged()); -} diff --git a/mvvm/tests/testmodel/projectmanager.test.cpp b/mvvm/tests/testmodel/projectmanager.test.cpp deleted file mode 100644 index fa9cf5edde9..00000000000 --- a/mvvm/tests/testmodel/projectmanager.test.cpp +++ /dev/null @@ -1,253 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/projectmanager.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "folderbasedtest.h" -#include "google_test.h" -#include "mvvm/model/propertyitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/project/project_types.h" -#include "mvvm/project/projectmanager.h" -#include "mvvm/utils/fileutils.h" -#include "test_utils.h" -#include <cctype> - -using namespace ModelView; - -namespace { -const std::string samplemodel_name = "samplemodel"; - -} // namespace - -//! Tests for ProjectManager class. - -class ProjectManagerTest : public FolderBasedTest { -public: - ProjectManagerTest() - : FolderBasedTest("test_ProjectManager") - , sample_model(std::make_unique<ModelView::SessionModel>(samplemodel_name)) - { - } - ~ProjectManagerTest(); - - std::vector<SessionModel*> models() const { return {sample_model.get()}; }; - - ProjectContext createContext() - { - ProjectContext result; - result.m_models_callback = [this]() { return models(); }; - return result; - } - - std::unique_ptr<SessionModel> sample_model; -}; - -ProjectManagerTest::~ProjectManagerTest() = default; - -//! Initial state of ProjectManager. Project created, and not-saved. - -TEST_F(ProjectManagerTest, initialState) -{ - ProjectManager manager(createContext()); - EXPECT_TRUE(manager.currentProjectDir().empty()); - EXPECT_FALSE(manager.isModified()); -} - -// ---------------------------------------------------------------------------- -// Untitled, empty project -// ---------------------------------------------------------------------------- - -//! Creating new project. Use untitled+empty project as a starting point. -//! Should succeed, since old empty project doesn't need to be saved. - -TEST_F(ProjectManagerTest, untitledEmptyNew) -{ - ProjectManager manager(createContext()); - - const auto project_dir = createEmptyDir("Project_untitledEmptyNew"); - EXPECT_TRUE(manager.createNewProject(project_dir)); - - EXPECT_EQ(manager.currentProjectDir(), project_dir); - EXPECT_FALSE(manager.isModified()); - - // project directory should contain a json file with the model - auto model_json = Utils::join(project_dir, samplemodel_name + ".json"); - EXPECT_TRUE(Utils::exists(model_json)); -} - -//! Saving of new project. Use untitled+empty project as a starting point. -//! Should fail since project directory is not defined. - -TEST_F(ProjectManagerTest, untitledEmptySave) -{ - ProjectManager manager(createContext()); - EXPECT_FALSE(manager.saveCurrentProject()); - EXPECT_FALSE(manager.isModified()); -} - -//! Saving of new project. Use untitled+empty project as a starting point. -//! Should be saved, file sould appear on disk. - -TEST_F(ProjectManagerTest, untitledEmptySaveAs) -{ - ProjectManager manager(createContext()); - - const auto project_dir = createEmptyDir("Project_untitledEmptySaveAs"); - EXPECT_TRUE(manager.saveProjectAs(project_dir)); - EXPECT_FALSE(manager.isModified()); - - // project directory should contain a json file with the model - auto model_json = Utils::join(project_dir, samplemodel_name + ".json"); - EXPECT_TRUE(Utils::exists(model_json)); -} - -// ---------------------------------------------------------------------------- -// Untitled, modified -// ---------------------------------------------------------------------------- - -//! Creating new project. Use untitled+modified project as a starting point. -//! Should fail, since modified old project will prevent creation of the new one. - -TEST_F(ProjectManagerTest, untitledModifiedNew) -{ - ProjectManager manager(createContext()); - - // modifying the model - sample_model->insertItem<PropertyItem>(); - - EXPECT_TRUE(manager.isModified()); - - const auto project_dir = createEmptyDir("Project_untitledModifiedNew"); - EXPECT_FALSE(manager.createNewProject(project_dir)); - - EXPECT_TRUE(manager.currentProjectDir().empty()); - EXPECT_TRUE(manager.isModified()); - - // project directory should be empty - auto model_json = Utils::join(project_dir, samplemodel_name + ".json"); - EXPECT_FALSE(Utils::exists(model_json)); -} - -//! Saving of new project. Use untitled+modified project as a starting point. -//! Should fail since project directory is not defined. - -TEST_F(ProjectManagerTest, untitledModifiedSave) -{ - ProjectManager manager(createContext()); - // modifying the model - sample_model->insertItem<PropertyItem>(); - - EXPECT_FALSE(manager.saveCurrentProject()); - EXPECT_TRUE(manager.isModified()); -} - -//! Saving of new project. Use untitled+empty project as a starting point. -//! Should be saved, file sould appear on disk. - -TEST_F(ProjectManagerTest, untitledModifiedSaveAs) -{ - ProjectManager manager(createContext()); - sample_model->insertItem<PropertyItem>(); // modifying the model - - const auto project_dir = createEmptyDir("Project_untitledModifiedSaveAs"); - EXPECT_TRUE(manager.saveProjectAs(project_dir)); - EXPECT_FALSE(manager.isModified()); - - // project directory should contain a json file with the model - auto model_json = Utils::join(project_dir, samplemodel_name + ".json"); - EXPECT_TRUE(Utils::exists(model_json)); -} - -// ---------------------------------------------------------------------------- -// Titled, unmodified -// ---------------------------------------------------------------------------- - -//! Creating new project. Use titled+unmodified project as a starting point. -//! Should succeed, since old empty project doesn't need to be saved. - -TEST_F(ProjectManagerTest, titledUnmodifiedNew) -{ - ProjectManager manager(createContext()); - - const auto project_dir = createEmptyDir("Project_titledUnmodifiedNew"); - EXPECT_TRUE(manager.saveProjectAs(project_dir)); - EXPECT_EQ(manager.currentProjectDir(), project_dir); - - const auto project_dir2 = createEmptyDir("Project_titledUnmodifiedNew2"); - EXPECT_TRUE(manager.createNewProject(project_dir2)); - - EXPECT_EQ(manager.currentProjectDir(), project_dir2); - EXPECT_FALSE(manager.isModified()); - - // project directory should contain a json file with the model - auto model_json = Utils::join(project_dir2, samplemodel_name + ".json"); - EXPECT_TRUE(Utils::exists(model_json)); -} - -// ---------------------------------------------------------------------------- -// Titled, modified -// ---------------------------------------------------------------------------- - -//! Saving of new project. Use titled+modified project as a starting point. -//! Should succeed. - -TEST_F(ProjectManagerTest, titledModifiedSave) -{ - ProjectManager manager(createContext()); - - const auto project_dir = createEmptyDir("Project_titledModifiedSave"); - EXPECT_TRUE(manager.saveProjectAs(project_dir)); - EXPECT_EQ(manager.currentProjectDir(), project_dir); - - // modifying the model - sample_model->insertItem<PropertyItem>(); - - EXPECT_TRUE(manager.saveCurrentProject()); - EXPECT_FALSE(manager.isModified()); -} - -// ---------------------------------------------------------------------------- -// Callbacks -// ---------------------------------------------------------------------------- - -TEST_F(ProjectManagerTest, callback) -{ - int project_modified_count{0}; - - auto context = createContext(); - context.m_modified_callback = [&project_modified_count]() { ++project_modified_count; }; - - ProjectManager manager(context); - - EXPECT_EQ(project_modified_count, 0); - - // saving the project - const auto project_dir = createEmptyDir("Project_callback"); - EXPECT_TRUE(manager.saveProjectAs(project_dir)); - EXPECT_EQ(manager.currentProjectDir(), project_dir); - EXPECT_EQ(project_modified_count, 0); - - // modifying the model - sample_model->insertItem<PropertyItem>(); - EXPECT_EQ(project_modified_count, 1); - EXPECT_TRUE(manager.isModified()); - - // modifying the model second time - sample_model->insertItem<PropertyItem>(); - EXPECT_EQ(project_modified_count, 1); // do not sum up - EXPECT_TRUE(manager.isModified()); - - EXPECT_TRUE(manager.saveCurrentProject()); - EXPECT_FALSE(manager.isModified()); - EXPECT_EQ(project_modified_count, 1); -} diff --git a/mvvm/tests/testmodel/projectmanagerdecorator.test.cpp b/mvvm/tests/testmodel/projectmanagerdecorator.test.cpp deleted file mode 100644 index 4e8fc3ed970..00000000000 --- a/mvvm/tests/testmodel/projectmanagerdecorator.test.cpp +++ /dev/null @@ -1,208 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/projectmanagerdecorator.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "folderbasedtest.h" -#include "google_test.h" -#include "mvvm/model/propertyitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/project/project_types.h" -#include "mvvm/project/projectmanagerdecorator.h" -#include "mvvm/utils/fileutils.h" -#include <cctype> - -using namespace ModelView; - -namespace { -const std::string samplemodel_name = "samplemodel"; - -} // namespace - -//! Tests for ProjectManager class. - -class ProjectManagerDecoratorTest : public FolderBasedTest { -public: - ProjectManagerDecoratorTest() - : FolderBasedTest("test_ProjectManagerDecorator") - , sample_model(std::make_unique<ModelView::SessionModel>(samplemodel_name)) - { - } - ~ProjectManagerDecoratorTest(); - - std::vector<SessionModel*> models() const { return {sample_model.get()}; }; - - ProjectContext projectContext() - { - ProjectContext result; - result.m_models_callback = [this]() { return models(); }; - return result; - } - - UserInteractionContext userContext(const std::string& create_dir = {}, - const std::string& select_dir = {}) - { - UserInteractionContext result; - result.m_create_dir_callback = [create_dir]() -> std::string { return create_dir; }; - result.m_select_dir_callback = [select_dir]() -> std::string { return select_dir; }; - return result; - } - - std::unique_ptr<SessionModel> sample_model; -}; - -ProjectManagerDecoratorTest::~ProjectManagerDecoratorTest() = default; - -//! Initial state of ProjectManager. Project created, and not-saved. - -TEST_F(ProjectManagerDecoratorTest, initialState) -{ - ProjectManagerDecorator manager(projectContext(), userContext()); - EXPECT_TRUE(manager.currentProjectDir().empty()); -} - -//! Starting from new document (without project dir defined). -//! Create new project in given directory. - -TEST_F(ProjectManagerDecoratorTest, untitledEmptyCreateNew) -{ - const auto project_dir = createEmptyDir("Project_untitledEmptyCreateNew"); - - ProjectManagerDecorator manager(projectContext(), userContext(project_dir, {})); - EXPECT_TRUE(manager.currentProjectDir().empty()); - - // saving new project to 'project_dir' directory. - EXPECT_TRUE(manager.createNewProject()); - - // checking that current projectDir has pointing to the right place - EXPECT_EQ(manager.currentProjectDir(), project_dir); - - // project directory should contain a json file with the model - auto model_json = Utils::join(project_dir, samplemodel_name + ".json"); - EXPECT_TRUE(Utils::exists(model_json)); -} - -//! Starting from new document (without project dir defined). -//! Saving project. Same behavior as SaveAs. - -TEST_F(ProjectManagerDecoratorTest, untitledEmptySaveCurrentProject) -{ - const auto project_dir = createEmptyDir("Project_untitledEmptySaveCurrentProject"); - - ProjectManagerDecorator manager(projectContext(), userContext(project_dir, {})); - EXPECT_TRUE(manager.currentProjectDir().empty()); - - // saving new project to 'project_dir' directory. - EXPECT_TRUE(manager.saveCurrentProject()); - - // checking thaxt current projectDir has pointing to the right place - EXPECT_EQ(manager.currentProjectDir(), project_dir); - - // project directory should contain a json file with the model - auto model_json = Utils::join(project_dir, samplemodel_name + ".json"); - EXPECT_TRUE(Utils::exists(model_json)); -} - -//! Starting from new document (without project dir defined). -//! Save under given name. - -TEST_F(ProjectManagerDecoratorTest, untitledEmptySaveAs) -{ - const auto project_dir = createEmptyDir("Project_untitledEmptySaveAs"); - - ProjectManagerDecorator manager(projectContext(), userContext(project_dir, {})); - EXPECT_TRUE(manager.currentProjectDir().empty()); - - // saving new project to "project_dir" directory. - EXPECT_TRUE(manager.saveProjectAs()); - - // checking that current projectDir has pointing to the right place - EXPECT_EQ(manager.currentProjectDir(), project_dir); - - // project directory should contain a json file with the model - auto model_json = Utils::join(project_dir, samplemodel_name + ".json"); - EXPECT_TRUE(Utils::exists(model_json)); -} - -//! Starting from new document (without project dir defined). -//! Attempt to save under empty name, immitating the user canceled directory selection dialog. - -TEST_F(ProjectManagerDecoratorTest, untitledEmptySaveAsCancel) -{ - ProjectManagerDecorator manager(projectContext(), userContext({}, {})); // immitates canceling - EXPECT_TRUE(manager.currentProjectDir().empty()); - - // saving new project to "project_dir" directory. - EXPECT_FALSE(manager.saveProjectAs()); - EXPECT_TRUE(manager.currentProjectDir().empty()); -} - -//! Starting from new document (without project dir defined). -//! Attempt to save in the non-existing directory. - -TEST_F(ProjectManagerDecoratorTest, untitledEmptySaveAsWrongDir) -{ - ProjectManagerDecorator manager(projectContext(), userContext("non-existing", {})); - - // saving new project to "project_dir" directory. - EXPECT_FALSE(manager.saveProjectAs()); - EXPECT_TRUE(manager.currentProjectDir().empty()); -} - -//! Untitled, modified document. Attempt to open existing project will lead to -//! the dialog save/discard/cancel. As a result of whole exersize, existing project -//! should be opened, previous project saved. - -TEST_F(ProjectManagerDecoratorTest, untitledModifiedOpenExisting) -{ - const auto existing_project_dir = createEmptyDir("Project_untitledModifiedOpenExisting1"); - const auto unsaved_project_dir = createEmptyDir("Project_untitledModifiedOpenExisting2"); - - // create "existing project" - { - ProjectManagerDecorator manager(projectContext(), userContext(existing_project_dir, {})); - manager.saveProjectAs(); - } - - // preparing manager with untitled, unmodified project - auto open_dir = [&existing_project_dir]() -> std::string { return existing_project_dir; }; - auto create_dir = [&unsaved_project_dir]() -> std::string { return unsaved_project_dir; }; - auto result = SaveChangesAnswer::DISCARD; - auto ask_create = [&result]() { - result = SaveChangesAnswer::SAVE; - return SaveChangesAnswer::SAVE; - }; - auto user_context = userContext({}, {}); - user_context.m_create_dir_callback = create_dir; - user_context.m_select_dir_callback = open_dir; - user_context.m_answer_callback = ask_create; - ProjectManagerDecorator manager(projectContext(), user_context); - - // modifying untitled project - sample_model->insertItem<PropertyItem>(); - EXPECT_TRUE(manager.isModified()); - EXPECT_TRUE(manager.currentProjectDir().empty()); - - // attempt to open existing project - manager.openExistingProject(); - - // check if user was asked and his answer coincide with expectation - EXPECT_EQ(result, SaveChangesAnswer::SAVE); - - // check that previous project was saved - auto model_json = Utils::join(unsaved_project_dir, samplemodel_name + ".json"); - EXPECT_TRUE(Utils::exists(model_json)); - - // currently manager is pointing to existing project - EXPECT_FALSE(manager.isModified()); - EXPECT_EQ(manager.currentProjectDir(), existing_project_dir); -} diff --git a/mvvm/tests/testmodel/projectutils.test.cpp b/mvvm/tests/testmodel/projectutils.test.cpp deleted file mode 100644 index 217d23bb738..00000000000 --- a/mvvm/tests/testmodel/projectutils.test.cpp +++ /dev/null @@ -1,97 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/projectutils.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "folderbasedtest.h" -#include "google_test.h" -#include "mvvm/interfaces/applicationmodelsinterface.h" -#include "mvvm/interfaces/projectinterface.h" -#include "mvvm/model/propertyitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/project/project_types.h" -#include "mvvm/project/projectutils.h" -#include "mvvm/utils/fileutils.h" -#include "test_utils.h" - -using namespace ModelView; - -//! Tests of ProjectUtils namespace functions. - -class ProjectUtilsTest : public FolderBasedTest { -public: - ProjectUtilsTest() - : FolderBasedTest("test_ProjectUtils") - , sample_model(std::make_unique<SessionModel>("SampleModel")) - { - } - - ~ProjectUtilsTest(); - - std::vector<SessionModel*> models() const { return {sample_model.get()}; }; - - ProjectContext createContext() - { - ProjectContext result; - result.m_models_callback = [this]() { return models(); }; - return result; - } - - std::unique_ptr<SessionModel> sample_model; -}; - -ProjectUtilsTest::~ProjectUtilsTest() = default; - -//! Testing helper structure. - -TEST_F(ProjectUtilsTest, SuggestFileName) -{ - SessionModel model("TestModel"); - EXPECT_EQ(std::string("testmodel.json"), GUI::Project::Utils::SuggestFileName(model)); -} - -TEST_F(ProjectUtilsTest, CreateUntitledProject) -{ - auto project = GUI::Project::Utils::CreateUntitledProject(createContext()); - EXPECT_TRUE(project->projectDir().empty()); -} - -TEST_F(ProjectUtilsTest, ProjectWindowTitle) -{ - auto project = GUI::Project::Utils::CreateUntitledProject(createContext()); - - // unmodified project without projectDir - EXPECT_EQ(GUI::Project::Utils::ProjectWindowTitle(*project), "Untitled"); - - sample_model->insertItem<PropertyItem>(); - EXPECT_EQ(GUI::Project::Utils::ProjectWindowTitle(*project), "*Untitled"); - - // saving in a project directory - project->save(testPath()); - EXPECT_EQ(GUI::Project::Utils::ProjectWindowTitle(*project), testDir()); - - // modifying - sample_model->insertItem<PropertyItem>(); - EXPECT_EQ(GUI::Project::Utils::ProjectWindowTitle(*project), "*" + testDir()); -} - -TEST_F(ProjectUtilsTest, IsPossibleProjectDir) -{ - auto project = GUI::Project::Utils::CreateUntitledProject(createContext()); - - // empty directory can't be a project directory - auto dirname = createEmptyDir("test_IsPossibleProjectDir"); - EXPECT_FALSE(GUI::Project::Utils::IsPossibleProjectDir(dirname)); - - project->save(dirname); - EXPECT_TRUE(GUI::Project::Utils::IsPossibleProjectDir(dirname)); -} diff --git a/mvvm/tests/testmodel/reallimits.test.cpp b/mvvm/tests/testmodel/reallimits.test.cpp deleted file mode 100644 index a3ca302b792..00000000000 --- a/mvvm/tests/testmodel/reallimits.test.cpp +++ /dev/null @@ -1,242 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/reallimits.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/utils/reallimits.h" -#include <limits> - -using namespace ModelView; - -class RealLimitsTest : public ::testing::Test { -protected: - ~RealLimitsTest(); -}; - -RealLimitsTest::~RealLimitsTest() = default; - -TEST_F(RealLimitsTest, initialState) -{ - RealLimits limits; - - EXPECT_FALSE(limits.hasLowerLimit()); - EXPECT_FALSE(limits.hasUpperLimit()); - EXPECT_FALSE(limits.hasLowerAndUpperLimits()); - EXPECT_TRUE(limits.isInRange(-std::numeric_limits<double>::infinity())); - EXPECT_TRUE(limits.isInRange(0.0)); - EXPECT_TRUE(limits.isInRange(std::numeric_limits<double>::infinity())); - - EXPECT_FALSE(limits.isPositive()); - EXPECT_FALSE(limits.isNonnegative()); - EXPECT_FALSE(limits.isLowerLimited()); - EXPECT_FALSE(limits.isUpperLimited()); - EXPECT_FALSE(limits.isLimited()); -} - -TEST_F(RealLimitsTest, lowerLimited) -{ - // [5.0, inf[ - RealLimits limits = RealLimits::lowerLimited(5.0); - EXPECT_TRUE(limits.hasLowerLimit()); - EXPECT_FALSE(limits.hasUpperLimit()); - EXPECT_FALSE(limits.hasLowerAndUpperLimits()); - - EXPECT_EQ(limits.lowerLimit(), 5.0); - EXPECT_DOUBLE_EQ(limits.upperLimit(), std::numeric_limits<double>::max()); - - EXPECT_FALSE(limits.isPositive()); - EXPECT_FALSE(limits.isNonnegative()); - EXPECT_TRUE(limits.isLowerLimited()); - EXPECT_FALSE(limits.isUpperLimited()); - EXPECT_FALSE(limits.isLimited()); -} - -TEST_F(RealLimitsTest, upperLimited) -{ - // [-inf, 5.0[ - RealLimits limits = RealLimits::upperLimited(5.0); - EXPECT_FALSE(limits.hasLowerLimit()); - EXPECT_TRUE(limits.hasUpperLimit()); - EXPECT_FALSE(limits.hasLowerAndUpperLimits()); - - EXPECT_DOUBLE_EQ(limits.lowerLimit(), std::numeric_limits<double>::lowest()); - EXPECT_EQ(5.0, limits.upperLimit()); - - EXPECT_TRUE(limits.isInRange(-std::numeric_limits<double>::infinity())); - EXPECT_TRUE(limits.isInRange(-2.0)); - EXPECT_FALSE(limits.isInRange(5.0)); - EXPECT_FALSE(limits.isInRange(std::numeric_limits<double>::infinity())); - - EXPECT_FALSE(limits.isPositive()); - EXPECT_FALSE(limits.isNonnegative()); - EXPECT_FALSE(limits.isLowerLimited()); - EXPECT_TRUE(limits.isUpperLimited()); - EXPECT_FALSE(limits.isLimited()); -} - -TEST_F(RealLimitsTest, limited) -{ - // [-10.0, 2.0[ - RealLimits limits = RealLimits::limited(-10.0, 2.0); - EXPECT_TRUE(limits.hasLowerLimit()); - EXPECT_TRUE(limits.hasUpperLimit()); - EXPECT_TRUE(limits.hasLowerAndUpperLimits()); - - EXPECT_EQ(limits.lowerLimit(), -10.0); - EXPECT_EQ(limits.upperLimit(), 2.0); - - EXPECT_FALSE(limits.isInRange(-11.0)); - EXPECT_TRUE(limits.isInRange(-9.0)); - EXPECT_TRUE(limits.isInRange(0.0)); - EXPECT_TRUE(limits.isInRange(1.0)); - EXPECT_FALSE(limits.isInRange(2.0)); - EXPECT_FALSE(limits.isInRange(3.0)); - - EXPECT_FALSE(limits.isPositive()); - EXPECT_FALSE(limits.isNonnegative()); - EXPECT_FALSE(limits.isLowerLimited()); - EXPECT_FALSE(limits.isUpperLimited()); - EXPECT_TRUE(limits.isLimited()); -} - -TEST_F(RealLimitsTest, positive) -{ - // ]0.0, 2.0[ - RealLimits limits = RealLimits::positive(); - EXPECT_TRUE(limits.hasLowerLimit()); - EXPECT_FALSE(limits.hasUpperLimit()); - EXPECT_FALSE(limits.hasLowerAndUpperLimits()); - - EXPECT_EQ(limits.lowerLimit(), std::numeric_limits<double>::min()); - EXPECT_EQ(limits.upperLimit(), std::numeric_limits<double>::max()); - - EXPECT_FALSE(limits.isInRange(-11.0)); - EXPECT_FALSE(limits.isInRange(0.0)); - EXPECT_TRUE(limits.isInRange(std::numeric_limits<double>::min())); - EXPECT_TRUE(limits.isInRange(1.0)); - - EXPECT_TRUE(limits.isPositive()); - EXPECT_FALSE(limits.isNonnegative()); - EXPECT_FALSE(limits.isLowerLimited()); - EXPECT_FALSE(limits.isUpperLimited()); - EXPECT_FALSE(limits.isLimited()); -} - -TEST_F(RealLimitsTest, nonnegative) -{ - // [0.0, 2.0[ - RealLimits limits = RealLimits::nonnegative(); - EXPECT_TRUE(limits.hasLowerLimit()); - EXPECT_FALSE(limits.hasUpperLimit()); - EXPECT_FALSE(limits.hasLowerAndUpperLimits()); - - EXPECT_EQ(limits.lowerLimit(), 0.0); - EXPECT_EQ(limits.upperLimit(), std::numeric_limits<double>::max()); - - EXPECT_FALSE(limits.isInRange(-11.0)); - EXPECT_TRUE(limits.isInRange(0.0)); - EXPECT_TRUE(limits.isInRange(std::numeric_limits<double>::min())); - EXPECT_TRUE(limits.isInRange(1.0)); - - EXPECT_FALSE(limits.isPositive()); - EXPECT_TRUE(limits.isNonnegative()); - EXPECT_FALSE(limits.isLowerLimited()); - EXPECT_FALSE(limits.isUpperLimited()); - EXPECT_FALSE(limits.isLimited()); -} - -TEST_F(RealLimitsTest, limitless) -{ - RealLimits limits = RealLimits::limitless(); - - EXPECT_FALSE(limits.hasLowerLimit()); - EXPECT_FALSE(limits.hasUpperLimit()); - EXPECT_FALSE(limits.hasLowerAndUpperLimits()); - - EXPECT_EQ(limits.lowerLimit(), std::numeric_limits<double>::lowest()); - EXPECT_EQ(limits.upperLimit(), std::numeric_limits<double>::max()); - - EXPECT_TRUE(limits.isInRange(-std::numeric_limits<double>::infinity())); - EXPECT_TRUE(limits.isInRange(0.0)); - EXPECT_TRUE(limits.isInRange(std::numeric_limits<double>::infinity())); - - EXPECT_FALSE(limits.isPositive()); - EXPECT_FALSE(limits.isNonnegative()); - EXPECT_FALSE(limits.isLowerLimited()); - EXPECT_FALSE(limits.isUpperLimited()); - EXPECT_FALSE(limits.isLimited()); -} - -TEST_F(RealLimitsTest, comparisonOperators) -{ - RealLimits lim1 = RealLimits::limited(1.0, 2.0); - RealLimits lim2 = RealLimits::limited(1.0, 2.0); - EXPECT_TRUE(lim1 == lim2); - EXPECT_FALSE(lim1 != lim2); - - RealLimits lim3 = RealLimits::limitless(); - RealLimits lim4 = RealLimits::limitless(); - EXPECT_TRUE(lim3 == lim4); - EXPECT_FALSE(lim3 != lim4); - - RealLimits lim5 = RealLimits::lowerLimited(1.0); - RealLimits lim6 = RealLimits::lowerLimited(1.0); - EXPECT_TRUE(lim5 == lim6); - EXPECT_FALSE(lim5 != lim6); - - RealLimits lim7 = RealLimits::upperLimited(1.0); - RealLimits lim8 = RealLimits::upperLimited(1.0); - EXPECT_TRUE(lim7 == lim8); - EXPECT_FALSE(lim7 != lim8); - - EXPECT_TRUE(RealLimits::positive() == RealLimits::positive()); - EXPECT_TRUE(RealLimits::nonnegative() == RealLimits::nonnegative()); - - EXPECT_FALSE(RealLimits::positive() == RealLimits::nonnegative()); -} - -TEST_F(RealLimitsTest, copyConstructor) -{ - RealLimits lim1 = RealLimits::limited(1.0, 2.0); - RealLimits lim2 = lim1; - EXPECT_TRUE(lim1 == lim2); - EXPECT_FALSE(lim1 != lim2); - - RealLimits lim3(lim1); - EXPECT_TRUE(lim1 == lim3); - EXPECT_FALSE(lim1 != lim3); -} - -TEST_F(RealLimitsTest, toVariant) -{ - RealLimits limit = RealLimits::limited(1.0, 2.0); - QVariant variant = QVariant::fromValue(limit); - - EXPECT_EQ(variant.value<RealLimits>().isLimited(), limit.isLimited()); - EXPECT_EQ(variant.value<RealLimits>().lowerLimit(), limit.lowerLimit()); - EXPECT_EQ(variant.value<RealLimits>().upperLimit(), limit.upperLimit()); -} - -TEST_F(RealLimitsTest, variantEquality) -{ - if (ModelView::Comparators::registered()) { - QVariant var1a = QVariant::fromValue(RealLimits::limited(1.0, 2.0)); - QVariant var1b = QVariant::fromValue(RealLimits::limited(1.0, 2.0)); - QVariant var2 = QVariant::fromValue(RealLimits::lowerLimited(1.0)); - - EXPECT_TRUE(var1a == var1b); - EXPECT_FALSE(var1a == var2); - EXPECT_FALSE(var1a != var1b); - EXPECT_TRUE(var1a != var2); - } -} diff --git a/mvvm/tests/testmodel/removeitemcommand.test.cpp b/mvvm/tests/testmodel/removeitemcommand.test.cpp deleted file mode 100644 index 40ba412d700..00000000000 --- a/mvvm/tests/testmodel/removeitemcommand.test.cpp +++ /dev/null @@ -1,181 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/removeitemcommand.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/commands/removeitemcommand.h" -#include "mvvm/model/compounditem.h" -#include "mvvm/model/itemutils.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/model/taginfo.h" - -using namespace ModelView; - -class RemoveItemCommandTest : public ::testing::Test { -public: - ~RemoveItemCommandTest(); -}; - -RemoveItemCommandTest::~RemoveItemCommandTest() = default; - -TEST_F(RemoveItemCommandTest, removeAtCommand) -{ - SessionModel model; - auto item = model.insertItem<SessionItem>(model.rootItem()); - - auto item_identifier = item->identifier(); - - // command to insert parent in the model - auto command = std::make_unique<RemoveItemCommand>(model.rootItem(), TagRow{"", 0}); - command->execute(); // removal - - EXPECT_EQ(std::get<bool>(command->result()), true); - EXPECT_EQ(model.rootItem()->childrenCount(), 0); - EXPECT_FALSE(command->isObsolete()); - - // undo command - command->undo(); - EXPECT_FALSE(command->isObsolete()); - EXPECT_EQ(model.rootItem()->childrenCount(), 1); - auto restored = Utils::ChildAt(model.rootItem(), 0); - EXPECT_EQ(restored->identifier(), item_identifier); -} - -TEST_F(RemoveItemCommandTest, removeAtCommandChild) -{ - SessionModel model; - auto parent = model.insertItem<SessionItem>(model.rootItem()); - parent->registerTag(TagInfo::universalTag("tag1"), /*set_as_default*/ true); - - auto child1 = model.insertItem<SessionItem>(parent); - child1->setData(42.0); - model.insertItem<SessionItem>(parent); - - auto child1_identifier = child1->identifier(); - - // command to remove one child - auto command = std::make_unique<RemoveItemCommand>(parent, TagRow{"", 0}); - command->execute(); // removal - - // check that one child was removed - EXPECT_FALSE(command->isObsolete()); - EXPECT_EQ(std::get<bool>(command->result()), true); - EXPECT_EQ(parent->childrenCount(), 1); - - // undo command - command->undo(); - EXPECT_FALSE(command->isObsolete()); - EXPECT_EQ(parent->childrenCount(), 2); - auto restored = Utils::ChildAt(parent, 0); - EXPECT_EQ(restored->identifier(), child1_identifier); - - // checking the data of restored item - EXPECT_EQ(restored->data<double>(), 42.0); -} - -TEST_F(RemoveItemCommandTest, removeAtCommandParentWithChild) -{ - SessionModel model; - auto parent = model.insertItem<SessionItem>(model.rootItem()); - parent->registerTag(TagInfo::universalTag("tag1"), /*set_as_default*/ true); - - auto child1 = model.insertItem<SessionItem>(parent); - child1->setData(42.0); - - auto parent_identifier = parent->identifier(); - auto child1_identifier = child1->identifier(); - - // command to remove parent - auto command = std::make_unique<RemoveItemCommand>(model.rootItem(), TagRow{"", 0}); - command->execute(); // removal - EXPECT_FALSE(command->isObsolete()); - - // check that one child was removed - EXPECT_EQ(std::get<bool>(command->result()), true); - EXPECT_EQ(model.rootItem()->childrenCount(), 0); - - // undo command - command->undo(); - EXPECT_FALSE(command->isObsolete()); - EXPECT_EQ(model.rootItem()->childrenCount(), 1); - auto restored_parent = Utils::ChildAt(model.rootItem(), 0); - auto restored_child = Utils::ChildAt(restored_parent, 0); - - EXPECT_EQ(restored_parent->identifier(), parent_identifier); - EXPECT_EQ(restored_child->identifier(), child1_identifier); - - // checking the data of restored item - EXPECT_EQ(restored_child->data<double>(), 42.0); -} - -//! RemoveAtCommand in multitag context - -TEST_F(RemoveItemCommandTest, removeAtCommandMultitag) -{ - SessionModel model; - auto parent = model.insertItem<SessionItem>(model.rootItem()); - parent->registerTag(TagInfo::universalTag("tag1")); - parent->registerTag(TagInfo::universalTag("tag2")); - - auto child1 = model.insertItem<SessionItem>(parent, "tag1"); - child1->setData(41.0); - - auto child2 = model.insertItem<SessionItem>(parent, "tag1"); - child2->setData(42.0); - - auto child3 = model.insertItem<SessionItem>(parent, "tag2"); - child3->setData(43.0); - - auto parent_identifier = parent->identifier(); - auto child1_identifier = child1->identifier(); - auto child2_identifier = child2->identifier(); - auto child3_identifier = child3->identifier(); - - // command to remove parent - auto command = std::make_unique<RemoveItemCommand>(parent, TagRow{"tag1", 1}); - command->execute(); // removal - - // check that one child was removed - EXPECT_FALSE(command->isObsolete()); - EXPECT_EQ(std::get<bool>(command->result()), true); - EXPECT_EQ(parent->childrenCount(), 2); - - // undo command - command->undo(); - EXPECT_FALSE(command->isObsolete()); - EXPECT_EQ(parent->childrenCount(), 3); - auto restored_parent = Utils::ChildAt(model.rootItem(), 0); - auto restored_child2 = Utils::ChildAt(restored_parent, 1); - - EXPECT_EQ(restored_parent->identifier(), parent_identifier); - EXPECT_EQ(restored_child2->identifier(), child2_identifier); - - // checking the data of restored item - EXPECT_EQ(restored_child2->data<double>(), 42.0); -} - -//! Attempt to remove property item. - -TEST_F(RemoveItemCommandTest, attemptToRemoveItem) -{ - SessionModel model; - auto parent = model.insertItem<CompoundItem>(model.rootItem()); - parent->addProperty("thickness", 42.0); - - auto command = std::make_unique<RemoveItemCommand>(parent, TagRow{"thickness", 0}); - command->execute(); - - EXPECT_TRUE(command->isObsolete()); - EXPECT_EQ(std::get<bool>(command->result()), false); -} diff --git a/mvvm/tests/testmodel/sessionitem.test.cpp b/mvvm/tests/testmodel/sessionitem.test.cpp deleted file mode 100644 index 43b0100ef82..00000000000 --- a/mvvm/tests/testmodel/sessionitem.test.cpp +++ /dev/null @@ -1,722 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/sessionitem.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/itempool.h" -#include "mvvm/model/itemutils.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionitemdata.h" -#include "mvvm/model/sessionitemtags.h" -#include "mvvm/model/taginfo.h" -#include "mvvm/model/variant_constants.h" -#include "test_utils.h" -#include <memory> -#include <stdexcept> - -using namespace ModelView; - -class SessionItemTest : public ::testing::Test { -public: - ~SessionItemTest(); -}; - -SessionItemTest::~SessionItemTest() = default; - -TEST_F(SessionItemTest, initialState) -{ - SessionItem item; - const int role = ItemDataRole::DATA; - - EXPECT_EQ(item.model(), nullptr); - EXPECT_EQ(item.parent(), nullptr); - EXPECT_EQ(item.childrenCount(), 0); - EXPECT_FALSE(item.data<QVariant>(role).isValid()); - EXPECT_TRUE(item.children().empty()); - EXPECT_EQ(item.modelType(), GUI::Constants::BaseType); - EXPECT_EQ(item.displayName(), GUI::Constants::BaseType); - - // Initially item has already an identifier defined. - std::vector<int> expected_roles = {ItemDataRole::IDENTIFIER, ItemDataRole::DISPLAY}; - EXPECT_EQ(item.itemData()->roles(), expected_roles); - - // Identifier is not zero - EXPECT_FALSE(item.identifier().empty()); -} - -TEST_F(SessionItemTest, modelType) -{ - SessionItem item2("Layer"); - EXPECT_EQ(item2.modelType(), "Layer"); -} - -//! Validating ::setData and appearance of roles. - -TEST_F(SessionItemTest, setData) -{ - SessionItem item; - const int role = ItemDataRole::DATA; - - EXPECT_FALSE(item.data<QVariant>(role).isValid()); - - QVariant expected(42.0); - EXPECT_TRUE(item.setData(expected, role)); - - std::vector<int> expected_roles = {ItemDataRole::IDENTIFIER, ItemDataRole::DISPLAY, - ItemDataRole::DATA}; - EXPECT_EQ(item.itemData()->roles(), expected_roles); - EXPECT_EQ(item.data<QVariant>(role), expected); - - // setting another value - EXPECT_TRUE(item.setData(43.0, role)); - EXPECT_EQ(item.itemData()->roles(), expected_roles); - EXPECT_EQ(item.data<QVariant>(role), QVariant::fromValue(43.0)); - - // setting same value - EXPECT_FALSE(item.setData(43.0, role)); - EXPECT_EQ(item.itemData()->roles(), expected_roles); - EXPECT_EQ(item.data<QVariant>(role), QVariant::fromValue(43.0)); -} - -//! Validating ::setData in the context of implicit conversion. - -TEST_F(SessionItemTest, setDataAndImplicitConversion) -{ - { - SessionItem item; - const int role = ItemDataRole::DATA; - EXPECT_TRUE(item.setData(43.0, ItemDataRole::DATA)); - EXPECT_EQ(item.data<QVariant>(role).typeName(), GUI::Constants::double_type_name); - } - - { - SessionItem item; - const int role = ItemDataRole::DATA; - EXPECT_TRUE(item.setData(43, ItemDataRole::DATA)); - EXPECT_EQ(item.data<QVariant>(role).typeName(), GUI::Constants::int_type_name); - } -} - -TEST_F(SessionItemTest, hasData) -{ - SessionItem item; - - EXPECT_FALSE(item.hasData()); - EXPECT_TRUE(item.hasData(ItemDataRole::IDENTIFIER)); - EXPECT_FALSE(item.hasData(ItemDataRole::DATA)); - EXPECT_TRUE(item.hasData(ItemDataRole::DISPLAY)); - EXPECT_FALSE(item.hasData(ItemDataRole::APPEARANCE)); - EXPECT_FALSE(item.hasData(ItemDataRole::LIMITS)); - EXPECT_FALSE(item.hasData(ItemDataRole::TOOLTIP)); - EXPECT_FALSE(item.hasData(ItemDataRole::EDITORTYPE)); - - item.setData(42.0); - EXPECT_TRUE(item.hasData()); -} - -TEST_F(SessionItemTest, setDoubleData) -{ - SessionItem item; - const double expected = 42.0; - EXPECT_TRUE(item.setData(expected)); - EXPECT_EQ(item.data<double>(), expected); -} - -TEST_F(SessionItemTest, setIntData) -{ - SessionItem item; - const int expected = 42; - EXPECT_TRUE(item.setData(expected)); - EXPECT_EQ(item.data<int>(), expected); -} - -TEST_F(SessionItemTest, setBoolData) -{ - SessionItem item; - const bool expected_true = true; - EXPECT_TRUE(item.setData(expected_true)); - EXPECT_EQ(item.data<bool>(), expected_true); - const bool expected_false = false; - EXPECT_TRUE(item.setData(expected_false)); - EXPECT_EQ(item.data<bool>(), expected_false); -} - -TEST_F(SessionItemTest, setStringData) -{ - SessionItem item; - const std::string expected{"abc"}; - EXPECT_TRUE(item.setData(expected)); - EXPECT_EQ(item.data<std::string>(), expected); -} - -//! Display role. - -TEST_F(SessionItemTest, displayName) -{ - SessionItem item("Property"); - QVariant data(42.0); - EXPECT_TRUE(item.setData(data)); - - // default display name coincide with model type - EXPECT_EQ(item.displayName(), "Property"); - - // checking setter - item.setDisplayName("width"); - EXPECT_EQ(item.displayName(), "width"); - EXPECT_EQ(item.data<double>(), 42.0); -} - -//! Attempt to set the different Variant to already existing role. - -TEST_F(SessionItemTest, variantMismatch) -{ - SessionItem item; - const int role = ItemDataRole::DATA; - QVariant expected(42.0); - - // setting data for the first time - EXPECT_TRUE(item.setData(expected, role)); - - std::vector<int> expected_roles = {ItemDataRole::IDENTIFIER, ItemDataRole::DISPLAY, - ItemDataRole::DATA}; - EXPECT_EQ(item.itemData()->roles(), expected_roles); - EXPECT_EQ(item.data<QVariant>(role), expected); - - // attempt to rewrite variant with another type - EXPECT_THROW(item.setData(std::string("abc"), role), std::runtime_error); - - // removing value by passing invalid variant - EXPECT_NO_THROW(item.setData(QVariant(), role)); - EXPECT_EQ(item.itemData()->roles().size(), 2); -} - -//! Item registration in a pool. - -TEST_F(SessionItemTest, registerItem) -{ - auto item = std::make_unique<SessionItem>(); - auto item_id = item->identifier(); - EXPECT_EQ(item->itemData()->roles().size(), 2u); - - std::shared_ptr<ItemPool> pool; - - // creating pool - pool.reset(new ItemPool); - pool->register_item(item.get(), item_id); - // registration shouldn't change item identifier - EXPECT_EQ(item->identifier(), item_id); - - // registration key should coincide with item identifier - auto key = pool->key_for_item(item.get()); - std::vector<int> expected_roles = {ItemDataRole::IDENTIFIER, ItemDataRole::DISPLAY}; - EXPECT_EQ(item->itemData()->roles(), expected_roles); - EXPECT_EQ(item_id, key); -} - -//! Item registration in a pool. - -TEST_F(SessionItemTest, defaultTag) -{ - SessionItem item; - EXPECT_EQ(item.itemTags()->defaultTag(), ""); - EXPECT_FALSE(Utils::HasTag(item, "defaultTag")); -} - -//! Registering tags - -TEST_F(SessionItemTest, registerTag) -{ - SessionItem item; - item.registerTag(TagInfo::universalTag("tagname")); - EXPECT_TRUE(Utils::HasTag(item, "tagname")); - - // registering of tag with same name forbidden - EXPECT_THROW(item.registerTag(TagInfo::universalTag("tagname")), std::runtime_error); - - // registering empty tag is forbidden - EXPECT_THROW(item.registerTag(TagInfo::universalTag("")), std::runtime_error); -} - -//! Registering tag and setting it as default - -TEST_F(SessionItemTest, registerDefaultTag) -{ - SessionItem item; - item.registerTag(TagInfo::universalTag("tagname"), /*set_as_default*/ true); - EXPECT_EQ(item.itemTags()->defaultTag(), "tagname"); -} - -//! Simple child insert. - -TEST_F(SessionItemTest, insertItem) -{ - auto parent = std::make_unique<SessionItem>(); - parent->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - - std::unique_ptr<SessionItem> child(new SessionItem); - - // empty parent - EXPECT_EQ(parent->childrenCount(), 0); - EXPECT_EQ(Utils::IndexOfChild(parent.get(), nullptr), -1); - EXPECT_EQ(Utils::IndexOfChild(parent.get(), child.get()), -1); - EXPECT_EQ(parent->getItem("", 0), nullptr); - EXPECT_EQ(parent->getItem("", -1), nullptr); - EXPECT_EQ(parent->getItem("", 10), nullptr); - - // inserting child - auto p_child = child.release(); - parent->insertItem(p_child, {"", 0}); - EXPECT_EQ(parent->childrenCount(), 1); - EXPECT_EQ(Utils::IndexOfChild(parent.get(), p_child), 0); - EXPECT_EQ(parent->children()[0], p_child); - EXPECT_EQ(parent->getItem("", 0), p_child); - EXPECT_EQ(p_child->parent(), parent.get()); -} - -//! Simple children insert. - -TEST_F(SessionItemTest, insertChildren) -{ - auto parent = std::make_unique<SessionItem>(); - parent->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - - auto child1 = new SessionItem; - auto child2 = new SessionItem; - auto child3 = new SessionItem; - auto child4 = new SessionItem; - - // inserting two items - parent->insertItem(child1, TagRow::append()); - parent->insertItem(child2, TagRow::append()); - EXPECT_EQ(Utils::IndexOfChild(parent.get(), child1), 0); - EXPECT_EQ(Utils::IndexOfChild(parent.get(), child2), 1); - EXPECT_EQ(parent->getItem("", 0), child1); - EXPECT_EQ(parent->getItem("", 1), child2); - std::vector<SessionItem*> expected = {child1, child2}; - EXPECT_EQ(parent->children(), expected); - - // inserting third item between two others - parent->insertItem(child3, {"", 1}); - expected = {child1, child3, child2}; - EXPECT_EQ(parent->children(), expected); - EXPECT_EQ(Utils::IndexOfChild(parent.get(), child1), 0); - EXPECT_EQ(Utils::IndexOfChild(parent.get(), child2), 2); - EXPECT_EQ(Utils::IndexOfChild(parent.get(), child3), 1); - EXPECT_EQ(parent->getItem("", 0), child1); - EXPECT_EQ(parent->getItem("", 1), child3); - EXPECT_EQ(parent->getItem("", 2), child2); - EXPECT_EQ(parent->getItem("", 3), nullptr); - - // inserting forth item using index equal to number of items - parent->insertItem(child4, {"", parent->childrenCount()}); - - // checking parents - EXPECT_EQ(child1->parent(), parent.get()); - EXPECT_EQ(child2->parent(), parent.get()); - EXPECT_EQ(child3->parent(), parent.get()); - EXPECT_EQ(child4->parent(), parent.get()); - - // attempt to insert same item twice - EXPECT_THROW(parent->insertItem(child2, TagRow::append()), std::runtime_error); - - // attempt to insert item using out of scope index - auto child5 = std::make_unique<SessionItem>(); - EXPECT_FALSE(parent->insertItem(child5.get(), {"", parent->childrenCount() + 1})); -} - -//! Removing (taking) item from parent. - -TEST_F(SessionItemTest, takeItem) -{ - auto parent = std::make_unique<SessionItem>(); - parent->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - - auto child1 = new SessionItem; - auto child2 = new SessionItem; - auto child3 = new SessionItem; - - // inserting items - parent->insertItem(child1, TagRow::append()); - parent->insertItem(child2, TagRow::append()); - parent->insertItem(child3, TagRow::append()); - - EXPECT_EQ(parent->childrenCount(), 3); - - // taking non-existing rows - EXPECT_EQ(parent->takeItem({"", -1}), nullptr); - EXPECT_EQ(parent->takeItem({"", parent->childrenCount()}), nullptr); - - // taking first row - auto taken = parent->takeItem({"", 0}); - EXPECT_EQ(taken->parent(), nullptr); - std::vector<SessionItem*> expected = {child2, child3}; - EXPECT_EQ(parent->children(), expected); - - delete taken; -} - -//! Insert and take tagged items. - -TEST_F(SessionItemTest, singleTagAndItems) -{ - const std::string tag1 = "tag1"; - - // creating parent with one tag - auto parent = std::make_unique<SessionItem>(); - parent->registerTag(TagInfo::universalTag(tag1)); - EXPECT_TRUE(Utils::HasTag(*parent, tag1)); - - // inserting two children - auto child1 = new SessionItem; - auto child2 = new SessionItem; - parent->insertItem(child1, {tag1, -1}); - parent->insertItem(child2, {tag1, -1}); - - // testing result of insertion via non-tag interface - std::vector<SessionItem*> expected = {child1, child2}; - EXPECT_EQ(parent->children(), expected); - EXPECT_EQ(Utils::IndexOfChild(parent.get(), child1), 0); - EXPECT_EQ(Utils::IndexOfChild(parent.get(), child2), 1); - - // testing single item access via tag interface - EXPECT_EQ(parent->getItem(tag1), child1); - EXPECT_EQ(parent->getItem(tag1, 0), child1); - EXPECT_EQ(parent->getItem(tag1, 1), child2); - EXPECT_EQ(parent->getItem(tag1, 2), nullptr); // wrong row - - // access to multiple items via tags interface - EXPECT_EQ(parent->getItems(tag1), expected); - - // removing first item - delete parent->takeItem({tag1, 0}); - EXPECT_EQ(parent->getItems(tag1), std::vector<SessionItem*>() = {child2}); - // removing second item - delete parent->takeItem({tag1, 0}); - EXPECT_EQ(parent->getItems(tag1), std::vector<SessionItem*>() = {}); - - // removing from already empty container - EXPECT_EQ(parent->takeItem({tag1, 0}), nullptr); -} - -//! Insert and take tagged items when two tags are present. - -TEST_F(SessionItemTest, twoTagsAndItems) -{ - const std::string tag1 = "tag1"; - const std::string tag2 = "tag2"; - - // creating parent with one tag - auto parent = std::make_unique<SessionItem>(); - parent->registerTag(TagInfo::universalTag(tag1)); - parent->registerTag(TagInfo::universalTag(tag2)); - EXPECT_TRUE(Utils::HasTag(*parent, tag1)); - EXPECT_TRUE(Utils::HasTag(*parent, tag2)); - - // inserting two children - auto child_t1_a = new SessionItem; - auto child_t1_b = new SessionItem; - auto child_t2_a = new SessionItem; - auto child_t2_b = new SessionItem; - auto child_t2_c = new SessionItem; - parent->insertItem(child_t2_a, {tag2, -1}); - parent->insertItem(child_t2_c, {tag2, -1}); - - parent->insertItem(child_t1_a, {tag1, -1}); - parent->insertItem(child_t1_b, {tag1, -1}); - - parent->insertItem(child_t2_b, {tag2, 1}); // between child_t2_a and child_t2_c - - // testing item access via non-tag interface - std::vector<SessionItem*> expected = {child_t1_a, child_t1_b, child_t2_a, child_t2_b, - child_t2_c}; - EXPECT_EQ(parent->children(), expected); - EXPECT_EQ(Utils::IndexOfChild(parent.get(), child_t1_a), 0); - EXPECT_EQ(Utils::IndexOfChild(parent.get(), child_t2_c), 4); - - // testing single item access via tag interface - EXPECT_EQ(parent->getItem(tag1), child_t1_a); - EXPECT_EQ(parent->getItem(tag1, 0), child_t1_a); - EXPECT_EQ(parent->getItem(tag1, 1), child_t1_b); - EXPECT_EQ(parent->getItem(tag2, 0), child_t2_a); - EXPECT_EQ(parent->getItem(tag2, 1), child_t2_b); - EXPECT_EQ(parent->getItem(tag2, 2), child_t2_c); - EXPECT_EQ(parent->getItem(tag2, 3), nullptr); // no items with such row - - // access to multiple items via tags interface - expected = {child_t1_a, child_t1_b}; - EXPECT_EQ(parent->getItems(tag1), expected); - expected = {child_t2_a, child_t2_b, child_t2_c}; - EXPECT_EQ(parent->getItems(tag2), expected); - - // removing item from the middle of tag2 - delete parent->takeItem({tag2, 1}); - expected = {child_t1_a, child_t1_b}; - EXPECT_EQ(parent->getItems(tag1), expected); - expected = {child_t2_a, child_t2_c}; - EXPECT_EQ(parent->getItems(tag2), expected); -} - -//! Inserting and removing items when tag has limits. - -TEST_F(SessionItemTest, tagWithLimits) -{ - const std::string tag1 = "tag1"; - const int maxItems = 3; - auto parent = std::make_unique<SessionItem>(); - parent->registerTag(TagInfo(tag1, 0, maxItems, std::vector<std::string>() = {})); - - // placing maximu allowed number of items - std::vector<SessionItem*> expected; - for (int i = 0; i < maxItems; ++i) { - auto child = new SessionItem; - expected.push_back(child); - EXPECT_TRUE(parent->insertItem(child, {tag1, -1})); - } - EXPECT_EQ(parent->getItems(tag1), expected); - - // no room for extra item - auto extra = new SessionItem; - EXPECT_FALSE(parent->insertItem(extra, {tag1, -1})); - - // removing first element - delete parent->takeItem({tag1, 0}); - expected.erase(expected.begin()); - EXPECT_EQ(parent->getItems(tag1), expected); - - // adding extra item - parent->insertItem(extra, {tag1, -1}); - expected.push_back(extra); - EXPECT_EQ(parent->getItems(tag1), expected); -} - -//! Inserting and removing items when tag has limits. - -TEST_F(SessionItemTest, tagModelTypes) -{ - const std::string tag1 = "tag1"; - const std::string tag2 = "tag2"; - const std::string modelType1 = "ModelType1"; - const std::string modelType2 = "ModelType2"; - const std::string modelType3 = "ModelType3"; - const std::string modelType4 = "ModelType4"; - - auto parent = std::make_unique<SessionItem>(); - parent->registerTag( - TagInfo(tag1, 0, -1, std::vector<std::string>() = {modelType1, modelType2})); - parent->registerTag(TagInfo(tag2, 0, -1, std::vector<std::string>() = {modelType3})); - - auto item1 = new SessionItem(modelType1); - auto item2 = new SessionItem(modelType2); - auto item3 = new SessionItem(modelType3); - - // attempt to add item not intended for tag - EXPECT_FALSE(parent->insertItem(item1, {tag2, -1})); - EXPECT_FALSE(parent->insertItem(item3, {tag1, -1})); - - // normal insert to appropriate tag - parent->insertItem(item3, {tag2, -1}); - parent->insertItem(item1, {tag1, -1}); - parent->insertItem(item2, {tag1, -1}); - - std::vector<SessionItem*> expected = {item1, item2}; - EXPECT_EQ(parent->getItems(tag1), expected); - expected = {item3}; - EXPECT_EQ(parent->getItems(tag2), expected); -} - -//! Testing method ::tag. - -TEST_F(SessionItemTest, tag) -{ - const std::string tag1 = "tag1"; - const std::string tag2 = "tag2"; - - // creating parent with one tag - auto parent = std::make_unique<SessionItem>(); - parent->registerTag(TagInfo::universalTag(tag1)); - parent->registerTag(TagInfo::universalTag(tag2)); - - // inserting two children - auto child_t1_a = new SessionItem; - auto child_t1_b = new SessionItem; - auto child_t2_a = new SessionItem; - auto child_t2_b = new SessionItem; - auto child_t2_c = new SessionItem; - parent->insertItem(child_t2_a, {tag2, -1}); - parent->insertItem(child_t2_c, {tag2, -1}); - parent->insertItem(child_t1_a, {tag1, -1}); - parent->insertItem(child_t1_b, {tag1, -1}); - parent->insertItem(child_t2_b, {tag2, 1}); // between child_t2_a and child_t2_c - - EXPECT_EQ(child_t1_a->tagRow().tag, "tag1"); - EXPECT_EQ(child_t1_b->tagRow().tag, "tag1"); - EXPECT_EQ(child_t2_a->tagRow().tag, "tag2"); - EXPECT_EQ(child_t2_b->tagRow().tag, "tag2"); - EXPECT_EQ(child_t2_c->tagRow().tag, "tag2"); - - SessionItem parentless_item; - EXPECT_EQ(parentless_item.tagRow().tag, ""); -} - -//! Checks row of item in its tag - -TEST_F(SessionItemTest, tagRow) -{ - const std::string tag1 = "tag1"; - const std::string tag2 = "tag2"; - - // creating parent with one tag - auto parent = std::make_unique<SessionItem>(); - parent->registerTag(TagInfo::universalTag(tag1)); - parent->registerTag(TagInfo::universalTag(tag2)); - - // inserting two children - auto child_t1_a = new SessionItem; - auto child_t1_b = new SessionItem; - auto child_t2_a = new SessionItem; - auto child_t2_b = new SessionItem; - auto child_t2_c = new SessionItem; - parent->insertItem(child_t2_a, {tag2, -1}); // 0 - parent->insertItem(child_t2_c, {tag2, -1}); // 2 - parent->insertItem(child_t1_a, {tag1, -1}); // 0 - parent->insertItem(child_t1_b, {tag1, -1}); // 1 - parent->insertItem(child_t2_b, {tag2, 1}); // 1 between child_t2_a and child_t2_c - - EXPECT_EQ(child_t1_a->tagRow().row, 0); - EXPECT_EQ(child_t1_b->tagRow().row, 1); - EXPECT_EQ(child_t2_a->tagRow().row, 0); - EXPECT_EQ(child_t2_b->tagRow().row, 1); - EXPECT_EQ(child_t2_c->tagRow().row, 2); - - EXPECT_EQ(child_t1_a->tagRow().tag, "tag1"); - EXPECT_EQ(child_t1_b->tagRow().tag, "tag1"); - EXPECT_EQ(child_t2_a->tagRow().tag, "tag2"); - EXPECT_EQ(child_t2_b->tagRow().tag, "tag2"); - EXPECT_EQ(child_t2_c->tagRow().tag, "tag2"); -} - -//! Checks row of item in its tag - -TEST_F(SessionItemTest, tagRowOfItem) -{ - const std::string tag1 = "tag1"; - const std::string tag2 = "tag2"; - - // creating parent with one tag - auto parent = std::make_unique<SessionItem>(); - parent->registerTag(TagInfo::universalTag(tag1)); - parent->registerTag(TagInfo::universalTag(tag2)); - - // inserting two children - auto child_t1_a = new SessionItem; - auto child_t1_b = new SessionItem; - auto child_t2_a = new SessionItem; - auto child_t2_b = new SessionItem; - auto child_t2_c = new SessionItem; - parent->insertItem(child_t2_a, {tag2, -1}); // 0 - parent->insertItem(child_t2_c, {tag2, -1}); // 2 - parent->insertItem(child_t1_a, {tag1, -1}); // 0 - parent->insertItem(child_t1_b, {tag1, -1}); // 1 - parent->insertItem(child_t2_b, {tag2, 1}); // 1 between child_t2_a and child_t2_c - - EXPECT_EQ(parent->tagRowOfItem(child_t1_a).row, 0); - EXPECT_EQ(parent->tagRowOfItem(child_t1_b).row, 1); - EXPECT_EQ(parent->tagRowOfItem(child_t2_a).row, 0); - EXPECT_EQ(parent->tagRowOfItem(child_t2_b).row, 1); - EXPECT_EQ(parent->tagRowOfItem(child_t2_c).row, 2); - - EXPECT_EQ(parent->tagRowOfItem(child_t1_a).tag, "tag1"); - EXPECT_EQ(parent->tagRowOfItem(child_t1_b).tag, "tag1"); - EXPECT_EQ(parent->tagRowOfItem(child_t2_a).tag, "tag2"); - EXPECT_EQ(parent->tagRowOfItem(child_t2_b).tag, "tag2"); - EXPECT_EQ(parent->tagRowOfItem(child_t2_c).tag, "tag2"); -} - -//! Checks item appearance (enabled/disabled and editable/readonly). - -TEST_F(SessionItemTest, appearance) -{ - SessionItem item("Model"); - - // there shouldn't be any data - auto variant = item.data<QVariant>(ItemDataRole::APPEARANCE); - EXPECT_FALSE(variant.isValid()); - - // default status - EXPECT_TRUE(item.isEnabled()); - EXPECT_TRUE(item.isEditable()); - - // disabling item - item.setEnabled(false); - EXPECT_FALSE(item.isEnabled()); - EXPECT_TRUE(item.isEditable()); - - // data should be there now - variant = item.data<QVariant>(ItemDataRole::APPEARANCE); - EXPECT_TRUE(variant.isValid()); - - // making it readonly - item.setEditable(false); - EXPECT_FALSE(item.isEnabled()); - EXPECT_FALSE(item.isEditable()); -} - -//! Checks item tooltip. - -TEST_F(SessionItemTest, tooltip) -{ - SessionItem item("Model"); - - EXPECT_EQ(item.toolTip(), ""); - EXPECT_FALSE(item.hasData(ItemDataRole::TOOLTIP)); - - EXPECT_EQ(item.setToolTip("abc"), &item); - EXPECT_TRUE(item.hasData(ItemDataRole::TOOLTIP)); - EXPECT_EQ(item.toolTip(), "abc"); -} - -//! Checks item's editor type. - -TEST_F(SessionItemTest, editorType) -{ - SessionItem item("Model"); - - EXPECT_EQ(item.editorType(), ""); - EXPECT_FALSE(item.hasData(ItemDataRole::EDITORTYPE)); - - EXPECT_EQ(item.setEditorType("abc"), &item); - EXPECT_TRUE(item.hasData(ItemDataRole::EDITORTYPE)); - EXPECT_EQ(item.editorType(), "abc"); -} - -TEST_F(SessionItemTest, itemsInTag) -{ - const std::string tag1 = "tag1"; - const std::string tag2 = "tag2"; - - // creating parent with one tag - auto parent = std::make_unique<SessionItem>(); - parent->registerTag(TagInfo::universalTag(tag1)); - parent->registerTag(TagInfo::universalTag(tag2)); - - // inserting two children - auto child_t1_a = new SessionItem; - auto child_t2_a = new SessionItem; - auto child_t2_b = new SessionItem; - parent->insertItem(child_t1_a, {tag1, -1}); - parent->insertItem(child_t2_a, {tag2, -1}); - parent->insertItem(child_t2_b, {tag2, -1}); - - EXPECT_EQ(parent->itemCount(tag1), 1); - EXPECT_EQ(parent->itemCount(tag2), 2); -} diff --git a/mvvm/tests/testmodel/sessionitemcontainer.test.cpp b/mvvm/tests/testmodel/sessionitemcontainer.test.cpp deleted file mode 100644 index 6cc147b30ad..00000000000 --- a/mvvm/tests/testmodel/sessionitemcontainer.test.cpp +++ /dev/null @@ -1,305 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/sessionitemcontainer.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionitemcontainer.h" -#include "test_utils.h" - -using namespace ModelView; - -//! Tests for TestSessionItemContainer class. - -class SessionItemContainerTest : public ::testing::Test { -public: - ~SessionItemContainerTest(); -}; - -SessionItemContainerTest::~SessionItemContainerTest() = default; - -//! Initial state of emty SessionItemTag. - -TEST_F(SessionItemContainerTest, initialState) -{ - const std::string name("tag"); - SessionItemContainer tag(TagInfo::universalTag(name)); - - EXPECT_EQ(tag.itemCount(), 0); - EXPECT_TRUE(tag.empty()); - EXPECT_EQ(tag.name(), name); - EXPECT_EQ(tag.items(), std::vector<SessionItem*>()); -} - -//! Checking ::insertItem. - -TEST_F(SessionItemContainerTest, insertItem) -{ - const std::string tag_name("tag"); - SessionItemContainer tag(TagInfo::universalTag(tag_name)); - - // inserting non-existing item is not allowed - EXPECT_FALSE(tag.insertItem(nullptr, tag.itemCount())); - EXPECT_EQ(tag.itemCount(), 0); - - // insertion at the end - SessionItem* child1 = new SessionItem; - SessionItem* child2 = new SessionItem; - EXPECT_TRUE(tag.insertItem(child1, tag.itemCount())); - EXPECT_TRUE(tag.insertItem(child2, tag.itemCount())); - EXPECT_EQ(tag.itemCount(), 2); - EXPECT_FALSE(tag.empty()); - std::vector<SessionItem*> expected = {child1, child2}; - EXPECT_EQ(tag.items(), expected); - - // insertion at the beginning - SessionItem* child3 = new SessionItem; - EXPECT_TRUE(tag.insertItem(child3, 0)); - expected = {child3, child1, child2}; - EXPECT_EQ(tag.items(), expected); - - // insertion in between - SessionItem* child4 = new SessionItem; - EXPECT_TRUE(tag.insertItem(child4, 1)); - expected = {child3, child4, child1, child2}; - EXPECT_EQ(tag.items(), expected); - - // using index equal to number of items - SessionItem* child5 = new SessionItem; - EXPECT_TRUE(tag.insertItem(child5, tag.itemCount())); - expected = {child3, child4, child1, child2, child5}; - EXPECT_EQ(tag.items(), expected); - - // insertion with wrong index - SessionItem* child6 = new SessionItem; - EXPECT_FALSE(tag.insertItem(child6, 42)); - EXPECT_EQ(tag.items(), expected); - delete child6; -} - -//! Checking ::insertItem when item has specific model type. - -TEST_F(SessionItemContainerTest, insertItemModelType) -{ - const std::string tag_name("tag"); - const std::vector<std::string> model_types = {"model_a"}; - - SessionItemContainer tag(TagInfo::universalTag(tag_name, model_types)); - - // insertion of wrong model type is not allowed - SessionItem* child1 = new SessionItem("model_a"); - SessionItem* child2 = new SessionItem("model_b"); - EXPECT_TRUE(tag.insertItem(child1, tag.itemCount())); - EXPECT_FALSE(tag.insertItem(child2, tag.itemCount())); - delete child2; - - std::vector<SessionItem*> expected = {child1}; - EXPECT_EQ(tag.items(), expected); -} - -//! Checking ::insertItem when tag is related to property tag. - -TEST_F(SessionItemContainerTest, insertItemPropertyType) -{ - const std::string name("tag"); - const std::string property_type("Property"); - - SessionItemContainer tag(TagInfo::propertyTag(name, property_type)); - - // insertion of second property item is not allowed (because of reached maximum) - SessionItem* child1 = new SessionItem(property_type); - SessionItem* child2 = new SessionItem(property_type); - EXPECT_TRUE(tag.insertItem(child1, tag.itemCount())); - EXPECT_FALSE(tag.insertItem(child2, tag.itemCount())); - delete child2; - - // insertion of wrong model type is not allowed - SessionItem* child3 = new SessionItem("another_model"); - EXPECT_FALSE(tag.insertItem(child3, tag.itemCount())); - delete child3; - std::vector<SessionItem*> expected = {child1}; - EXPECT_EQ(tag.items(), expected); -} - -//! Checking ::indexOfItem. - -TEST_F(SessionItemContainerTest, indexOfItem) -{ - const std::string tag_name("tag"); - const std::string model_type("model_a"); - - SessionItemContainer tag(TagInfo::universalTag(tag_name)); - - // index of two items - SessionItem* child1 = new SessionItem(model_type); - SessionItem* child2 = new SessionItem(model_type); - EXPECT_TRUE(tag.insertItem(child1, tag.itemCount())); - EXPECT_EQ(tag.indexOfItem(child1), 0); - EXPECT_TRUE(tag.insertItem(child2, tag.itemCount())); - EXPECT_EQ(tag.indexOfItem(child1), 0); - EXPECT_EQ(tag.indexOfItem(child2), 1); - - // not existing items - EXPECT_EQ(tag.indexOfItem(nullptr), -1); - auto child3 = std::make_unique<SessionItem>(model_type); - EXPECT_EQ(tag.indexOfItem(child3.get()), -1); -} - -//! Checking ::itemAt. - -TEST_F(SessionItemContainerTest, itemAt) -{ - const std::string tag_name("tag"); - const std::string model_type("model_a"); - - SessionItemContainer tag(TagInfo::universalTag(tag_name)); - - // items at given indices - SessionItem* child1 = new SessionItem(model_type); - SessionItem* child2 = new SessionItem(model_type); - EXPECT_TRUE(tag.insertItem(child1, tag.itemCount())); - EXPECT_TRUE(tag.insertItem(child2, tag.itemCount())); - EXPECT_EQ(tag.itemAt(0), child1); - EXPECT_EQ(tag.itemAt(1), child2); - - // non-existing indices - EXPECT_EQ(tag.itemAt(2), nullptr); - EXPECT_EQ(tag.itemAt(3), nullptr); - EXPECT_EQ(tag.itemAt(-1), nullptr); -} - -//! Checking ::takeItem. - -TEST_F(SessionItemContainerTest, takeItem) -{ - const std::string tag_name("tag"); - const std::string model_type("model_a"); - - SessionItemContainer tag(TagInfo::universalTag(tag_name)); - - // taking non existing items - EXPECT_EQ(tag.takeItem(0), nullptr); - - // inserting items - SessionItem* child1 = new SessionItem(model_type); - SessionItem* child2 = new SessionItem(model_type); - SessionItem* child3 = new SessionItem(model_type); - EXPECT_TRUE(tag.insertItem(child1, tag.itemCount())); - EXPECT_TRUE(tag.insertItem(child2, tag.itemCount())); - EXPECT_TRUE(tag.insertItem(child3, tag.itemCount())); - - // taking item in between - auto taken2 = tag.takeItem(1); - EXPECT_EQ(child2, taken2); - delete taken2; - - // order of remaining children - std::vector<SessionItem*> expected = {child1, child3}; - EXPECT_EQ(tag.items(), expected); - - // taking non existing items - EXPECT_EQ(tag.takeItem(-1), nullptr); - EXPECT_EQ(tag.takeItem(tag.itemCount()), nullptr); -} - -//! Checking ::canTakeItem. - -TEST_F(SessionItemContainerTest, canTakeItem) -{ - const std::string tag_name("tag"); - const std::string model_type("model_a"); - - SessionItemContainer tag(TagInfo::universalTag(tag_name)); - - // taking non existing items - EXPECT_FALSE(tag.canTakeItem(0)); - - // inserting items - SessionItem* child1 = new SessionItem(model_type); - EXPECT_TRUE(tag.insertItem(child1, tag.itemCount())); - EXPECT_TRUE(tag.canTakeItem(0)); - - // taking non existing items - EXPECT_FALSE(tag.canTakeItem(-1)); - EXPECT_FALSE(tag.canTakeItem(tag.itemCount())); -} - -//! Checking ::canInsertItem. - -TEST_F(SessionItemContainerTest, canInsertItem) -{ - const std::string tag_name("tag"); - const std::string model_type("model_a"); - - SessionItemContainer tag(TagInfo::universalTag(tag_name)); - - // inserting non-existing item - EXPECT_FALSE(tag.canInsertItem(nullptr, 0)); - - // we should be allowed to insert valid child - auto child1 = std::make_unique<SessionItem>(model_type); - EXPECT_TRUE(tag.canInsertItem(child1.get(), 0)); - - // wrong index is not allowed for insertion - EXPECT_FALSE(tag.canInsertItem(child1.get(), 1)); - EXPECT_FALSE(tag.canInsertItem(child1.get(), -1)); - - // inserting child - EXPECT_TRUE(tag.insertItem(child1.release(), tag.itemCount())); - - // can we insert second child? - auto child2 = std::make_unique<SessionItem>(model_type); - EXPECT_TRUE(tag.canInsertItem(child2.get(), 0)); - EXPECT_TRUE(tag.canInsertItem(child2.get(), 1)); - EXPECT_FALSE(tag.canInsertItem(child2.get(), 2)); - EXPECT_FALSE(tag.canInsertItem(child2.get(), -1)); -} - -//! Checking ::canInsertItem. - -TEST_F(SessionItemContainerTest, canInsertItemForPropertyTag) -{ - const std::string name("tag"); - const std::string property_type("Property"); - - SessionItemContainer tag(TagInfo::propertyTag(name, property_type)); - - // we should be allowed to insert valid child - auto child1 = std::make_unique<SessionItem>(property_type); - EXPECT_TRUE(tag.canInsertItem(child1.get(), 0)); - - // inserting child - EXPECT_TRUE(tag.insertItem(child1.release(), tag.itemCount())); - - // second property shouldn't be posible to insert because of exceeded maximum - auto child2 = std::make_unique<SessionItem>(property_type); - EXPECT_FALSE(tag.canInsertItem(child2.get(), 0)); -} - -//! Checking ::takeItem when tag is related to property tag. - -TEST_F(SessionItemContainerTest, takeItemPropertyType) -{ - const std::string name("tag"); - const std::string property_type("Property"); - - SessionItemContainer tag(TagInfo::propertyTag(name, property_type)); - - // insertion of second property item is not allowed (because of reached maximum) - SessionItem* child1 = new SessionItem(property_type); - EXPECT_TRUE(tag.insertItem(child1, tag.itemCount())); - - // attempt to take property item - EXPECT_FALSE(tag.canTakeItem(0)); - EXPECT_EQ(tag.takeItem(0), nullptr); -} diff --git a/mvvm/tests/testmodel/sessionitemdata.test.cpp b/mvvm/tests/testmodel/sessionitemdata.test.cpp deleted file mode 100644 index cfaad9b95bc..00000000000 --- a/mvvm/tests/testmodel/sessionitemdata.test.cpp +++ /dev/null @@ -1,171 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/sessionitemdata.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/comboproperty.h" -#include "mvvm/model/mvvm_types.h" -#include "mvvm/model/sessionitemdata.h" -#include <stdexcept> - -using namespace ModelView; - -//! Test of SessionItemData. - -class SessionItemDataTest : public ::testing::Test { -public: - ~SessionItemDataTest(); -}; - -SessionItemDataTest::~SessionItemDataTest() = default; - -//! Initial state of SessionItemData object. - -TEST_F(SessionItemDataTest, initialState) -{ - SessionItemData data; - EXPECT_TRUE(data.roles().empty()); - EXPECT_FALSE(data.data(Qt::DisplayRole).isValid()); -} - -//! Basic setData, data operations. - -TEST_F(SessionItemDataTest, setDataDouble) -{ - SessionItemData data; - - const int role(ItemDataRole::DATA); - const QVariant variant(42.0); - - // setting variant for role - EXPECT_TRUE(data.setData(variant, role)); - std::vector<int> expected{role}; - EXPECT_EQ(data.roles(), expected); - EXPECT_TRUE(data.data(role) == variant); - - // setting same data twice - EXPECT_FALSE(data.setData(variant, role)); - EXPECT_EQ(data.roles(), expected); - EXPECT_TRUE(data.data(role) == variant); - - // changing the data - EXPECT_TRUE(data.setData(QVariant(43.0), role)); - EXPECT_EQ(data.roles(), expected); - EXPECT_TRUE(data.data(role) == QVariant(43.0)); - - // setting invalid variant for the role will remove data - EXPECT_TRUE(data.setData(QVariant(), role)); - EXPECT_TRUE(data.roles().empty()); - EXPECT_FALSE(data.data(role).isValid()); -} - -//! Basic setData, data operations. - -TEST_F(SessionItemDataTest, setDataComboProperty) -{ - SessionItemData data; - ComboProperty c1 = ComboProperty::createFrom({"a1", "a2"}); - ComboProperty c2 = ComboProperty::createFrom({"a1", "a2"}); - c1.setValue("a1"); - c2.setValue("a2"); - - const int role(ItemDataRole::DATA); - - // setting variant for role - EXPECT_TRUE(data.setData(QVariant::fromValue(c1), role)); - EXPECT_EQ(data.data(role).value<ComboProperty>(), c1); - - // setting same data twice - EXPECT_FALSE(data.setData(QVariant::fromValue(c1), role)); - - // setting another data - EXPECT_TRUE(data.setData(QVariant::fromValue(c2), role)); - EXPECT_EQ(data.data(role).value<ComboProperty>(), c2); -} - -//! Using different roles. - -TEST_F(SessionItemDataTest, differentRoles) -{ - SessionItemData data; - - const int role1(1); - const int role2 = role1 + 1; - - EXPECT_TRUE(data.setData(QVariant::fromValue(42.0), role1)); - EXPECT_TRUE(data.setData(QVariant::fromValue(std::string("str")), role2)); - - std::vector<int> expected{role1, role2}; - EXPECT_EQ(data.roles(), expected); - - EXPECT_TRUE(data.data(role1) == QVariant(42.0)); - EXPECT_TRUE(data.data(role2) == QVariant::fromValue(std::string("str"))); - EXPECT_FALSE(data.data(role2) == QVariant(42.0)); - EXPECT_FALSE(data.data(role1) == QVariant::fromValue(std::string("str"))); -} - -//! Changing type of variant for role should not be allowed. - -TEST_F(SessionItemDataTest, changingRole) -{ - SessionItemData data; - - const int role(1); - const QVariant variant(42.0); - - // setting variant for role - EXPECT_TRUE(data.setData(variant, role)); - std::vector<int> expected{role}; - EXPECT_EQ(data.roles(), expected); - EXPECT_TRUE(data.data(role) == variant); - - QVariant s = QVariant::fromValue(std::string("str")); - EXPECT_THROW(data.setData(s, role), std::runtime_error); -} - -TEST_F(SessionItemDataTest, rangeLoop) -{ - SessionItemData data; - const std::vector<double> expected_values = {1.2, 1.3}; - const std::vector<int> expected_roles = {1, 2}; - - for (size_t i = 0; i < expected_values.size(); ++i) { - data.setData(QVariant::fromValue(expected_values[i]), expected_roles[i]); - } - - std::vector<double> values; - std::vector<int> roles; - - for (const auto& x : data) { - values.push_back(x.m_data.value<double>()); - roles.push_back(x.m_role); - } - - EXPECT_EQ(values, expected_values); - EXPECT_EQ(roles, expected_roles); -} - -TEST_F(SessionItemDataTest, hasRole) -{ - SessionItemData data; - EXPECT_FALSE(data.hasData(0)); - EXPECT_FALSE(data.hasData(1)); - - const int role = 99; - data.setData(QVariant::fromValue(42), role); - EXPECT_TRUE(data.hasData(role)); - EXPECT_FALSE(data.hasData(1)); - - data.setData(QVariant(), role); - EXPECT_FALSE(data.hasData(role)); -} diff --git a/mvvm/tests/testmodel/sessionitemtags.test.cpp b/mvvm/tests/testmodel/sessionitemtags.test.cpp deleted file mode 100644 index 1a794b31319..00000000000 --- a/mvvm/tests/testmodel/sessionitemtags.test.cpp +++ /dev/null @@ -1,227 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/sessionitemtags.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionitemtags.h" -#include "mvvm/model/taginfo.h" -#include "test_utils.h" -#include <stdexcept> - -using namespace ModelView; - -//! Tests for SessionItemTags class. - -class SessionItemTagsTest : public ::testing::Test { -public: - ~SessionItemTagsTest(); -}; - -SessionItemTagsTest::~SessionItemTagsTest() = default; - -//! Initial state of emty SessionItemTags. - -TEST_F(SessionItemTagsTest, initialState) -{ - const std::string name("tag"); - SessionItemTags tag; - EXPECT_EQ(tag.defaultTag(), ""); - - EXPECT_FALSE(tag.isTag("abc")); - - EXPECT_EQ(tag.tagsCount(), 0); -} - -//! Registering tags. - -TEST_F(SessionItemTagsTest, registerTag) -{ - const std::string name("tag"); - SessionItemTags tag; - - tag.registerTag(TagInfo::universalTag("abc")); - EXPECT_TRUE(tag.isTag("abc")); - EXPECT_EQ(tag.defaultTag(), ""); - EXPECT_EQ(tag.tagsCount(), 1); - - // registering default tag - tag.registerTag(TagInfo::universalTag("abc2"), /*set_as_default*/ true); - EXPECT_TRUE(tag.isTag("abc2")); - EXPECT_EQ(tag.defaultTag(), "abc2"); - - // registering tag with same name is not allowed - EXPECT_THROW(tag.registerTag(TagInfo::universalTag("abc")), std::runtime_error); -} - -//! Insert item. - -TEST_F(SessionItemTagsTest, insertItem) -{ - const std::string tag1 = "tag1"; - const std::string tag2 = "tag2"; - - SessionItemTags tag; - - // inserting items without tags defined - auto item = std::make_unique<SessionItem>(); - EXPECT_THROW(tag.insertItem(item.get(), TagRow::append()), std::runtime_error); - - // registering tags - tag.registerTag(TagInfo::universalTag(tag1)); - tag.registerTag(TagInfo::universalTag(tag2)); - - EXPECT_EQ(tag.tagsCount(), 2); - - // inserting items - auto child_t1_a = new SessionItem; - auto child_t1_b = new SessionItem; - auto child_t2_a = new SessionItem; - auto child_t2_b = new SessionItem; - auto child_t2_c = new SessionItem; - EXPECT_TRUE(tag.insertItem(child_t2_a, TagRow::append(tag2))); - EXPECT_TRUE(tag.insertItem(child_t2_c, TagRow::append(tag2))); - EXPECT_TRUE(tag.insertItem(child_t1_a, TagRow::append(tag1))); - EXPECT_TRUE(tag.insertItem(child_t1_b, TagRow::append(tag1))); - EXPECT_TRUE(tag.insertItem(child_t2_b, {tag2, 1})); // between child_t2_a and child_t2_c - - // checking item order in containers - std::vector<SessionItem*> expected = {child_t1_a, child_t1_b}; - EXPECT_EQ(tag.getItems(tag1), expected); - expected = {child_t2_a, child_t2_b, child_t2_c}; - EXPECT_EQ(tag.getItems(tag2), expected); - - // checking allitems order - expected = {child_t1_a, child_t1_b, child_t2_a, child_t2_b, child_t2_c}; - EXPECT_EQ(tag.allitems(), expected); -} - -//! Testing method tagRowOfItem. - -TEST_F(SessionItemTagsTest, tagRowOfItem) -{ - const std::string tag1 = "tag1"; - const std::string tag2 = "tag2"; - - // creating parent with one tag - SessionItemTags tag; - tag.registerTag(TagInfo::universalTag(tag1), /*set_as_default*/ true); - tag.registerTag(TagInfo::universalTag(tag2)); - - // inserting children - auto child_t1_a = new SessionItem; - auto child_t1_b = new SessionItem; - auto child_t2_a = new SessionItem; - tag.insertItem(child_t1_a, TagRow::append()); // 0 - tag.insertItem(child_t1_b, TagRow::append()); // 1 - tag.insertItem(child_t2_a, {tag2, 0}); // 0 - - // checking children tag and row - EXPECT_EQ(tag.tagRowOfItem(child_t1_a).tag, tag1); - EXPECT_EQ(tag.tagRowOfItem(child_t1_b).tag, tag1); - EXPECT_EQ(tag.tagRowOfItem(child_t2_a).tag, tag2); - EXPECT_EQ(tag.tagRowOfItem(child_t1_a).row, 0); - EXPECT_EQ(tag.tagRowOfItem(child_t1_b).row, 1); - EXPECT_EQ(tag.tagRowOfItem(child_t2_a).row, 0); - - // alien item has no tag and -1 row - auto alien = std::make_unique<SessionItem>(); - EXPECT_EQ(tag.tagRowOfItem(alien.get()).tag, ""); - EXPECT_EQ(tag.tagRowOfItem(alien.get()).row, -1); - - // the same for nullptr - EXPECT_EQ(tag.tagRowOfItem(nullptr).tag, ""); - EXPECT_EQ(tag.tagRowOfItem(nullptr).row, -1); -} - -//! Testing method getItem. - -TEST_F(SessionItemTagsTest, getItem) -{ - const std::string tag1 = "tag1"; - const std::string tag2 = "tag2"; - - // creating parent with one tag - SessionItemTags tag; - tag.registerTag(TagInfo::universalTag(tag1), /*set_as_default*/ true); - tag.registerTag(TagInfo::universalTag(tag2)); - - // inserting children - auto child_t1_a = new SessionItem; - auto child_t1_b = new SessionItem; - auto child_t2_a = new SessionItem; - tag.insertItem(child_t1_a, TagRow::append()); // 0 - tag.insertItem(child_t1_b, TagRow::append()); // 1 - tag.insertItem(child_t2_a, {tag2, 0}); // 0 - - EXPECT_EQ(tag.getItem({tag1, 0}), child_t1_a); - EXPECT_EQ(tag.getItem({tag1, 1}), child_t1_b); - EXPECT_EQ(tag.getItem({tag2, 0}), child_t2_a); - EXPECT_EQ(tag.getItem({tag2, 2}), nullptr); -} - -//! Testing method getItem. - -TEST_F(SessionItemTagsTest, takeItem) -{ - const std::string tag1 = "tag1"; - const std::string tag2 = "tag2"; - const std::string model_type("model"); - - SessionItemTags tag; - tag.registerTag(TagInfo::universalTag(tag1), /*set_as_default*/ true); - tag.registerTag(TagInfo::universalTag(tag2)); - - // taking non existing items - EXPECT_EQ(tag.takeItem({"", 0}), nullptr); - - // inserting items - auto child1 = new SessionItem(model_type); - auto child2 = new SessionItem(model_type); - auto child3 = new SessionItem(model_type); - auto child4 = new SessionItem(model_type); - EXPECT_TRUE(tag.insertItem(child1, TagRow::append())); - EXPECT_TRUE(tag.insertItem(child2, TagRow::append())); - EXPECT_TRUE(tag.insertItem(child3, TagRow::append())); - EXPECT_TRUE(tag.insertItem(child4, TagRow::append(tag2))); - - // taking item in between - EXPECT_TRUE(tag.canTakeItem({"", 1})); - auto taken2 = tag.takeItem({"", 1}); - EXPECT_EQ(child2, taken2); - delete taken2; - - // order of remaining children - std::vector<SessionItem*> expected = {child1, child3}; - EXPECT_EQ(tag.getItems(tag1), expected); - expected = {child4}; - EXPECT_EQ(tag.getItems(tag2), expected); - - // taking non existing items - EXPECT_FALSE(tag.canTakeItem({"", -1})); - EXPECT_EQ(tag.takeItem({"", -1}), nullptr); -} - -//! Testing isSinglePropertyTag. - -TEST_F(SessionItemTagsTest, isSinglePropertyTag) -{ - SessionItemTags tag; - tag.registerTag(TagInfo::universalTag("universal"), /*set_as_default*/ true); - EXPECT_FALSE(tag.isSinglePropertyTag("universal")); - - tag.registerTag(TagInfo::propertyTag("property_tag", "Vector")); - EXPECT_TRUE(tag.isSinglePropertyTag("property_tag")); - - EXPECT_FALSE(tag.isSinglePropertyTag("unexisting tag")); -} diff --git a/mvvm/tests/testmodel/sessionmodel.test.cpp b/mvvm/tests/testmodel/sessionmodel.test.cpp deleted file mode 100644 index 70038bd1a45..00000000000 --- a/mvvm/tests/testmodel/sessionmodel.test.cpp +++ /dev/null @@ -1,467 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/sessionmodel.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/compounditem.h" -#include "mvvm/model/itempool.h" -#include "mvvm/model/itemutils.h" -#include "mvvm/model/propertyitem.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/model/taginfo.h" -#include <memory> -#include <stdexcept> - -using namespace ModelView; - -class SessionModelTest : public ::testing::Test { -public: - ~SessionModelTest(); - - class TestItem : public SessionItem { - public: - TestItem() : SessionItem("TestItemType"){}; - }; -}; - -SessionModelTest::~SessionModelTest() = default; - -TEST_F(SessionModelTest, initialState) -{ - SessionModel model; - EXPECT_EQ(model.rootItem()->model(), &model); - EXPECT_EQ(model.rootItem()->parent(), nullptr); -} - -TEST_F(SessionModelTest, insertItem) -{ - auto pool = std::make_shared<ItemPool>(); - SessionModel model("Test", pool); - - const model_type modelType = GUI::Constants::BaseType; - - // inserting single item - auto item = model.insertItem<SessionItem>(); - EXPECT_TRUE(item != nullptr); - EXPECT_EQ(item->parent(), model.rootItem()); - EXPECT_EQ(item->model(), &model); - EXPECT_EQ(item->modelType(), modelType); - - // checking registration - auto item_key = item->identifier(); - EXPECT_EQ(pool->item_for_key(item_key), item); - - // registering tag - item->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - - // adding child to it - auto child = model.insertItem<SessionItem>(item); - auto child_key = child->identifier(); - EXPECT_EQ(pool->item_for_key(child_key), child); - - EXPECT_TRUE(child != nullptr); - EXPECT_EQ(child->parent(), item); - EXPECT_EQ(child->model(), &model); - EXPECT_EQ(child->modelType(), modelType); - - // taking child back - auto taken = item->takeItem({"", 0}); - EXPECT_EQ(taken, child); - EXPECT_EQ(child->model(), nullptr); - - // childitem not registered anymore - EXPECT_EQ(pool->item_for_key(child_key), nullptr); - - delete taken; -} - -TEST_F(SessionModelTest, insertNewItem) -{ - auto pool = std::make_shared<ItemPool>(); - SessionModel model("Test", pool); - - const model_type modelType = GUI::Constants::BaseType; - - // inserting single item - auto item = model.insertNewItem(modelType); - EXPECT_TRUE(item != nullptr); - EXPECT_EQ(item->parent(), model.rootItem()); - EXPECT_EQ(item->model(), &model); - EXPECT_EQ(item->modelType(), modelType); - - // checking registration - auto item_key = item->identifier(); - EXPECT_EQ(pool->item_for_key(item_key), item); - - // registering tag - item->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - - // adding child to it - auto child = model.insertNewItem(modelType, item); - auto child_key = child->identifier(); - EXPECT_EQ(pool->item_for_key(child_key), child); - - EXPECT_TRUE(child != nullptr); - EXPECT_EQ(child->parent(), item); - EXPECT_EQ(child->model(), &model); - EXPECT_EQ(child->modelType(), modelType); - - // taking child back - auto taken = item->takeItem({"", 0}); - EXPECT_EQ(taken, child); - EXPECT_EQ(child->model(), nullptr); - - // childitem not registered anymore - EXPECT_EQ(pool->item_for_key(child_key), nullptr); - - delete taken; -} - -TEST_F(SessionModelTest, insertNewItemWithTag) -{ - const std::string tag1("tag1"); - SessionModel model; - auto parent = model.insertItem<SessionItem>(); - parent->registerTag(TagInfo::universalTag(tag1)); - auto child1 = model.insertItem<PropertyItem>(parent, {tag1, -1}); - - EXPECT_EQ(parent->tagRowOfItem(child1).tag, tag1); - EXPECT_EQ(Utils::IndexOfChild(parent, child1), 0); - - // adding second child - auto child2 = model.insertItem<PropertyItem>(parent, {tag1, 0}); - - EXPECT_EQ(parent->tagRowOfItem(child2).tag, tag1); - EXPECT_EQ(Utils::IndexOfChild(parent, child1), 1); - EXPECT_EQ(Utils::IndexOfChild(parent, child2), 0); -} - -TEST_F(SessionModelTest, setData) -{ - SessionModel model; - - // inserting single item - auto item = model.insertItem<SessionItem>(); - EXPECT_TRUE(model.data(item, ItemDataRole::DISPLAY).isValid()); - - // setting wrong type of data - QVariant value(42.0); - EXPECT_THROW(model.setData(item, value, ItemDataRole::DISPLAY), std::runtime_error); - - // setting new data - EXPECT_TRUE(model.setData(item, value, ItemDataRole::DATA)); - EXPECT_EQ(model.data(item, ItemDataRole::DATA), value); - - // setting same data twice should return false - EXPECT_FALSE(model.setData(item, value, ItemDataRole::DATA)); -} - -TEST_F(SessionModelTest, removeItem) -{ - auto pool = std::make_shared<ItemPool>(); - SessionModel model("Test", pool); - - auto parent = model.insertItem<SessionItem>(); - parent->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - - auto child1 = model.insertItem<SessionItem>(parent); - auto child2 = model.insertItem<SessionItem>(parent, {"", 0}); // before child1 - Q_UNUSED(child2) - - // removing child2 - model.removeItem(parent, {"", 0}); // removing child2 - EXPECT_EQ(parent->childrenCount(), 1); - EXPECT_EQ(Utils::ChildAt(parent, 0), child1); - - // child2 shouldn't be registered anymore - EXPECT_EQ(pool->key_for_item(child2), ""); -} - -TEST_F(SessionModelTest, removeNonExistingItem) -{ - auto pool = std::make_shared<ItemPool>(); - SessionModel model("Test", pool); - - auto parent = model.insertItem<SessionItem>(); - parent->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - - // removing non existing child - EXPECT_NO_THROW(model.removeItem(parent, {"", 0})); -} - -TEST_F(SessionModelTest, takeRowFromRootItem) -{ - auto pool = std::make_shared<ItemPool>(); - SessionModel model("Test", pool); - - auto parent = model.insertItem<SessionItem>(); - parent->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - auto parent_key = parent->identifier(); - - auto child = model.insertItem<SessionItem>(parent); - auto child_key = child->identifier(); - - EXPECT_EQ(pool->item_for_key(parent_key), parent); - EXPECT_EQ(pool->item_for_key(child_key), child); - - // taking parent - auto taken = model.rootItem()->takeItem({"", 0}); - EXPECT_EQ(pool->item_for_key(parent_key), nullptr); - EXPECT_EQ(pool->item_for_key(child_key), nullptr); - delete taken; -} - -TEST_F(SessionModelTest, moveItem) -{ - SessionModel model; - - // parent with child - auto parent0 = model.insertItem<SessionItem>(); - parent0->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - auto child0 = model.insertItem<PropertyItem>(parent0); - - // another parent with child - auto parent1 = model.insertItem<SessionItem>(); - parent1->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - auto child1 = model.insertItem<PropertyItem>(parent1); - - // moving child0 from parent0 to parent 1 - model.moveItem(child0, parent1, {"", 0}); - - std::vector<SessionItem*> expected = {child0, child1}; - EXPECT_EQ(parent1->children(), expected); - EXPECT_EQ(parent0->children().size(), 0); -} - -TEST_F(SessionModelTest, clearModel) -{ - auto pool = std::make_shared<ItemPool>(); - SessionModel model("test", pool); - - EXPECT_EQ(pool->size(), 1); - - auto first_root = model.rootItem(); - - EXPECT_EQ(model.rootItem()->childrenCount(), 0); - model.insertItem<SessionItem>(); - model.insertItem<SessionItem>(); - EXPECT_EQ(model.rootItem()->childrenCount(), 2); - - model.clear(); - EXPECT_EQ(model.rootItem()->childrenCount(), 0); - EXPECT_FALSE(model.rootItem() == first_root); - EXPECT_EQ(pool->key_for_item(first_root), ""); - EXPECT_EQ(pool->size(), 1); -} - -TEST_F(SessionModelTest, clearRebuildModel) -{ - auto pool = std::make_shared<ItemPool>(); - SessionModel model("test", pool); - - EXPECT_EQ(pool->size(), 1); - - auto first_root = model.rootItem(); - - EXPECT_EQ(model.rootItem()->childrenCount(), 0); - model.insertItem<SessionItem>(); - model.insertItem<SessionItem>(); - EXPECT_EQ(model.rootItem()->childrenCount(), 2); - - auto new_item = new SessionItem; - auto rebuild = [new_item](auto parent) { parent->insertItem(new_item, TagRow::append()); }; - - model.clear(rebuild); - EXPECT_EQ(model.rootItem()->childrenCount(), 1); - EXPECT_FALSE(model.rootItem() == first_root); - EXPECT_EQ(pool->key_for_item(first_root), ""); - EXPECT_EQ(pool->size(), 2); - EXPECT_EQ(pool->key_for_item(new_item), new_item->identifier()); -} - -//! Tests item copy when from root item to root item. - -TEST_F(SessionModelTest, copyModelItemRootContext) -{ - SessionModel model; - - // create single item with value - auto item = model.insertItem<SessionItem>(); - item->setData(42.0); - - // copying to root item - auto copy = model.copyItem(item, model.rootItem()); - - // checking copy - ASSERT_TRUE(copy != nullptr); - ASSERT_TRUE(copy != item); - EXPECT_FALSE(copy->identifier().empty()); - EXPECT_TRUE(copy->identifier() != item->identifier()); - EXPECT_EQ(copy->data<double>(), 42.0); - EXPECT_EQ(model.rootItem()->children().size(), 2); - EXPECT_TRUE(item != copy); - std::vector<SessionItem*> expected = {item, copy}; - EXPECT_EQ(model.rootItem()->children(), expected); -} - -//! Tests item copy from parent to root item. - -TEST_F(SessionModelTest, copyParentWithProperty) -{ - SessionModel model; - - // parent with single child and data on ite - auto parent0 = model.insertItem<SessionItem>(); - parent0->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - auto child0 = model.insertItem<SessionItem>(parent0); - child0->setData(42.0); - - // copying whole parent to root - auto copy = model.copyItem(parent0, model.rootItem()); - auto copy_child = copy->getItem("defaultTag"); - - ASSERT_TRUE(copy != nullptr); - ASSERT_TRUE(copy_child != nullptr); - EXPECT_FALSE(copy->identifier().empty()); - EXPECT_TRUE(copy->identifier() != parent0->identifier()); - EXPECT_EQ(copy_child->data<double>(), 42.0); -} - -//! Tests item copy for property item. - -TEST_F(SessionModelTest, copyFreeItem) -{ - SessionModel model; - - // single parent in a model - auto parent0 = model.insertItem<SessionItem>(); - parent0->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - - // free item - auto item = std::make_unique<PropertyItem>(); - item->setData(42.0); - - // copying to parent - auto copy = model.copyItem(item.get(), parent0); - EXPECT_EQ(copy->data<double>(), 42.0); -} - -//! Attempt to copy property item into the same tag. - -TEST_F(SessionModelTest, forbiddenCopy) -{ - SessionModel model; - - // single parent in a model - auto parent0 = model.insertItem<SessionItem>(); - parent0->registerTag(TagInfo::propertyTag("property", "Property")); - auto property = model.insertItem<PropertyItem>(parent0, "property"); - - // copying property to same property tag is not allowed - auto copy = model.copyItem(property, parent0, {"property", -1}); - EXPECT_EQ(parent0->childrenCount(), 1); - EXPECT_EQ(copy, nullptr); -} - -//! Test item find using identifier. - -TEST_F(SessionModelTest, findItem) -{ - SessionModel model; - auto parent = model.insertItem<SessionItem>(); - - // check that we can find item using its own identofoer - const identifier_type id = parent->identifier(); - EXPECT_EQ(model.findItem(id), parent); - - // check that we can't find deleted item. - model.removeItem(model.rootItem(), {"", 0}); - EXPECT_EQ(model.findItem(id), nullptr); -} - -//! Test items in different models. - -TEST_F(SessionModelTest, findItemInAlienModel) -{ - // two models with common pool - auto pool = std::make_shared<ItemPool>(); - SessionModel model1("Test1", pool); - SessionModel model2("Test2", pool); - - // inserting items in both models - auto parent1 = model1.insertItem<SessionItem>(); - auto parent2 = model2.insertItem<SessionItem>(); - const identifier_type id1 = parent1->identifier(); - const identifier_type id2 = parent2->identifier(); - - // checking that we can access items from both models - EXPECT_EQ(model1.findItem(id1), parent1); - EXPECT_EQ(model2.findItem(id1), parent1); - EXPECT_EQ(model1.findItem(id2), parent2); - EXPECT_EQ(model2.findItem(id2), parent2); - - // check that we can't find deleted item. - model1.removeItem(model1.rootItem(), {"", 0}); - EXPECT_EQ(model1.findItem(id1), nullptr); - EXPECT_EQ(model2.findItem(id1), nullptr); - EXPECT_EQ(model1.findItem(id2), parent2); - EXPECT_EQ(model2.findItem(id2), parent2); -} - -TEST_F(SessionModelTest, topItem) -{ - SessionModel model; - EXPECT_EQ(model.topItem<>(), nullptr); - EXPECT_EQ(model.topItem(), nullptr); - - auto property = model.insertItem<PropertyItem>(); - auto compound = model.insertItem<CompoundItem>(); - EXPECT_EQ(model.topItem<>(), property); - EXPECT_EQ(model.topItem(), property); - EXPECT_EQ(model.topItem<CompoundItem>(), compound); -} - -TEST_F(SessionModelTest, topItems) -{ - std::vector<SessionItem*> expected; - - SessionModel model; - EXPECT_EQ(model.topItems<>(), expected); - EXPECT_EQ(model.topItems(), expected); - - auto property1 = model.insertItem<PropertyItem>(); - auto compound1 = model.insertItem<CompoundItem>(); - auto property2 = model.insertItem<PropertyItem>(); - auto compound2 = model.insertItem<CompoundItem>(); - - expected = {property1, compound1, property2, compound2}; - EXPECT_EQ(model.topItems<>(), expected); - EXPECT_EQ(model.topItems(), expected); - - std::vector<CompoundItem*> expected2 = {compound1, compound2}; - EXPECT_EQ(model.topItems<CompoundItem>(), expected2); -} - -TEST_F(SessionModelTest, registerItem) -{ - const std::string expectedModelType("TestItemType"); - - SessionModel model; - model.registerItem<TestItem>(); - - auto item = model.insertNewItem(expectedModelType); - ASSERT_TRUE(item != nullptr); - ASSERT_TRUE(dynamic_cast<TestItem*>(item) != nullptr); - EXPECT_EQ(item->modelType(), expectedModelType); -} diff --git a/mvvm/tests/testmodel/setvaluecommand.test.cpp b/mvvm/tests/testmodel/setvaluecommand.test.cpp deleted file mode 100644 index c78ff2de826..00000000000 --- a/mvvm/tests/testmodel/setvaluecommand.test.cpp +++ /dev/null @@ -1,80 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/setvaluecommand.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/commands/setvaluecommand.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionmodel.h" -#include <stdexcept> - -using namespace ModelView; - -class SetValueCommandTest : public ::testing::Test { -public: - ~SetValueCommandTest(); -}; - -SetValueCommandTest::~SetValueCommandTest() = default; - -//! Set item value through SetValueCommand command. - -TEST_F(SetValueCommandTest, setValueCommand) -{ - SessionModel model; - const int role = ItemDataRole::DATA; - - // inserting single item - auto item = model.insertItem<SessionItem>(); - EXPECT_FALSE(model.data(item, role).isValid()); - - QVariant expected(42.0); - auto command = std::make_unique<SetValueCommand>(item, expected, role); - - // executing command - command->execute(); - EXPECT_TRUE(std::get<bool>(command->result())); // value was changed - EXPECT_EQ(command->isObsolete(), false); - EXPECT_EQ(model.data(item, role), expected); - - // undoing command - command->undo(); - EXPECT_TRUE(std::get<bool>(command->result())); // value was changed - EXPECT_FALSE(model.data(item, role).isValid()); - EXPECT_EQ(command->isObsolete(), false); -} - -//! Set same item value through SetValueCommand command. - -TEST_F(SetValueCommandTest, setSameValueCommand) -{ - SessionModel model; - const int role = ItemDataRole::DATA; - - // inserting single item - auto item = model.insertItem<SessionItem>(); - QVariant expected(42.0); - item->setData(expected, role); - - // command to set same value - auto command = std::make_unique<SetValueCommand>(item, expected, role); - - // executing command - command->execute(); - EXPECT_FALSE(std::get<bool>(command->result())); // value wasn't changed - EXPECT_EQ(model.data(item, role), expected); - EXPECT_EQ(command->isObsolete(), true); - - // undoing command which is in isObsolete state is not possible - EXPECT_THROW(command->undo(), std::runtime_error); -} diff --git a/mvvm/tests/testmodel/stringutils.test.cpp b/mvvm/tests/testmodel/stringutils.test.cpp deleted file mode 100644 index e45ab06702e..00000000000 --- a/mvvm/tests/testmodel/stringutils.test.cpp +++ /dev/null @@ -1,188 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/stringutils.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/utils/stringutils.h" -#include "test_utils.h" - -using namespace ModelView; -using namespace TestUtils; - -class StringUtilsTest : public ::testing::Test { -public: - ~StringUtilsTest(); -}; - -StringUtilsTest::~StringUtilsTest() = default; - -TEST_F(StringUtilsTest, ScientificDoubleToString) -{ - using Utils::ScientificDoubleToString; - const int precision = 6; - EXPECT_EQ(ScientificDoubleToString(0.0, precision), "0.0e+00"); - EXPECT_EQ(ScientificDoubleToString(1.0, precision), "1.0e+00"); - EXPECT_EQ(ScientificDoubleToString(3.0 / 4.0, precision), "7.5e-01"); - EXPECT_EQ(ScientificDoubleToString(4.0 / 3.0, precision), "1.333333e+00"); - EXPECT_EQ(ScientificDoubleToString(1000000., precision), "1.0e+06"); -} - -TEST_F(StringUtilsTest, DoubleToString) -{ - using Utils::DoubleToString; - const int precision = 4; - EXPECT_EQ(DoubleToString(0.0, precision), "0.0"); - EXPECT_EQ(DoubleToString(1.001, precision), "1.001"); - EXPECT_EQ(DoubleToString(1.0001, precision), "1.0"); -} - -//! Testing function TrimWhitespace - -TEST_F(StringUtilsTest, TrimWhiteSpace) -{ - using Utils::TrimWhitespace; - EXPECT_EQ(TrimWhitespace(""), ""); - EXPECT_EQ(TrimWhitespace(" "), ""); - EXPECT_EQ(TrimWhitespace("abc"), std::string("abc")); - EXPECT_EQ(TrimWhitespace(" \t\n abc cde\n"), std::string("abc cde")); -} - -TEST_F(StringUtilsTest, RemoveRepeatedSpaces) -{ - using Utils::RemoveRepeatedSpaces; - EXPECT_EQ(RemoveRepeatedSpaces(std::string{}), std::string{}); - EXPECT_EQ(RemoveRepeatedSpaces(" "), std::string{" "}); - EXPECT_EQ(RemoveRepeatedSpaces("a"), std::string{"a"}); - EXPECT_EQ(RemoveRepeatedSpaces(" a "), std::string{" a "}); - EXPECT_EQ(RemoveRepeatedSpaces(" a "), std::string{" a "}); - EXPECT_EQ(RemoveRepeatedSpaces("a bb ccc "), std::string{"a bb ccc "}); -} - -//! Testing function StringToDouble. - -TEST_F(StringUtilsTest, StringToDouble) -{ - using Utils::StringToDouble; - - // not a double - EXPECT_FALSE(StringToDouble("").has_value()); - EXPECT_FALSE(StringToDouble(" ").has_value()); - EXPECT_FALSE(StringToDouble("a").has_value()); - EXPECT_FALSE(StringToDouble("a b").has_value()); - - // not a double: some mixture present - EXPECT_FALSE(StringToDouble("42a").has_value()); - EXPECT_FALSE(StringToDouble("42.5.5").has_value()); - - // not a double: more than one double - EXPECT_FALSE(StringToDouble("42.5 52").has_value()); - - // valid double - EXPECT_TRUE(StringToDouble("42").has_value()); - EXPECT_TRUE(StringToDouble(" 42").has_value()); - EXPECT_TRUE(StringToDouble(" 42 ").has_value()); - EXPECT_DOUBLE_EQ(StringToDouble("42").value(), 42.0); - EXPECT_TRUE(StringToDouble("42.5").has_value()); - EXPECT_DOUBLE_EQ(StringToDouble("42.5").value(), 42.5); - EXPECT_TRUE(StringToDouble("-1.12e-06").has_value()); - EXPECT_DOUBLE_EQ(StringToDouble("-1.12e-06").value(), -1.12e-06); -} - -//! Testing function StringToDouble. - -TEST_F(StringUtilsTest, StringToInteger) -{ - using Utils::StringToInteger; - - // not an int - EXPECT_FALSE(StringToInteger("").has_value()); - EXPECT_FALSE(StringToInteger(" ").has_value()); - EXPECT_FALSE(StringToInteger("a").has_value()); - EXPECT_FALSE(StringToInteger("a b").has_value()); - - // not an int: some mixture present - EXPECT_FALSE(StringToInteger("42a").has_value()); - EXPECT_FALSE(StringToInteger("42.5").has_value()); - - // not an int: more than one number - EXPECT_FALSE(StringToInteger("42.5 52").has_value()); - - // valid int - EXPECT_TRUE(StringToInteger("42").has_value()); - EXPECT_TRUE(StringToInteger(" 42").has_value()); - EXPECT_TRUE(StringToInteger(" 42 ").has_value()); - EXPECT_EQ(StringToInteger("42").value(), 42); -} - -//! Testing SplitString method. -//! Carefully checking that it is reproduces Python behavior, as promised in comments to the method. - -TEST_F(StringUtilsTest, SplitString) -{ - using Utils::SplitString; - - EXPECT_THROW(SplitString("", ""), std::runtime_error); - EXPECT_EQ(SplitString("", " "), toVector<std::string>()); - EXPECT_EQ(SplitString("", ","), toVector<std::string>()); - EXPECT_EQ(SplitString(" ", " "), toVector<std::string>("", "")); - EXPECT_EQ(SplitString("a", " "), toVector<std::string>("a")); - EXPECT_EQ(SplitString("a ", " "), toVector<std::string>("a", "")); - - EXPECT_EQ(SplitString("a b", " "), toVector<std::string>("a", "b")); - EXPECT_EQ(SplitString("a b", " "), toVector<std::string>("a", "", "b")); - - EXPECT_EQ(SplitString("a", "-"), toVector<std::string>("a")); - - EXPECT_EQ(SplitString("aa", "a"), toVector<std::string>("", "", "")); - - EXPECT_EQ(SplitString("a,b", ","), toVector<std::string>("a", "b")); - EXPECT_EQ(SplitString("a, b", ","), toVector<std::string>("a", " b")); - - EXPECT_EQ(SplitString("a,b,", ","), toVector<std::string>("a", "b", "")); - EXPECT_EQ(SplitString(",a,b,", ","), toVector<std::string>("", "a", "b", "")); - EXPECT_EQ(SplitString("aabbcc", "bb"), toVector<std::string>("aa", "cc")); - EXPECT_EQ(SplitString("aabbcc", "bb"), toVector<std::string>("aa", "cc")); -} - -//! Testing ParseSpaceSeparatedDoubles. - -TEST_F(StringUtilsTest, ParseSpaceSeparatedDoubles) -{ - using Utils::ParseSpaceSeparatedDoubles; - std::vector<double> data; - - EXPECT_TRUE(ParseSpaceSeparatedDoubles("").empty()); - EXPECT_TRUE(ParseSpaceSeparatedDoubles(" ").empty()); - EXPECT_TRUE(ParseSpaceSeparatedDoubles("a").empty()); - EXPECT_TRUE(ParseSpaceSeparatedDoubles("a b").empty()); - - ASSERT_EQ(ParseSpaceSeparatedDoubles("4.02").size(), 1u); - EXPECT_DOUBLE_EQ(ParseSpaceSeparatedDoubles("42")[0], 42.0); - - // this tests failing under MacOS - // ASSERT_EQ(ParseSpaceSeparatedDoubles("42aaa").size(), 1u); - // EXPECT_DOUBLE_EQ(ParseSpaceSeparatedDoubles("42aaa")[0], 42.0); - - EXPECT_EQ(ParseSpaceSeparatedDoubles("42,").size(), 1u); - EXPECT_DOUBLE_EQ(ParseSpaceSeparatedDoubles("42,")[0], 42.0); - - EXPECT_EQ(ParseSpaceSeparatedDoubles("42,43").size(), 1u); - EXPECT_DOUBLE_EQ(ParseSpaceSeparatedDoubles("42,43")[0], 42.0); - - EXPECT_EQ(ParseSpaceSeparatedDoubles("42 ,43").size(), 1u); - EXPECT_DOUBLE_EQ(ParseSpaceSeparatedDoubles("42 ,43")[0], 42.0); - - EXPECT_EQ(ParseSpaceSeparatedDoubles("42 43").size(), 2u); - EXPECT_DOUBLE_EQ(ParseSpaceSeparatedDoubles("42 43")[0], 42.0); - EXPECT_DOUBLE_EQ(ParseSpaceSeparatedDoubles("42 43")[1], 43.0); -} diff --git a/mvvm/tests/testmodel/taginfo.test.cpp b/mvvm/tests/testmodel/taginfo.test.cpp deleted file mode 100644 index 5a530244f6a..00000000000 --- a/mvvm/tests/testmodel/taginfo.test.cpp +++ /dev/null @@ -1,95 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/taginfo.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/taginfo.h" - -using namespace ModelView; - -//! Tests of TagInfo class. - -class TagInfoTest : public ::testing::Test { -public: - ~TagInfoTest(); -}; - -TagInfoTest::~TagInfoTest() = default; - -TEST_F(TagInfoTest, initialState) -{ - TagInfo tag; - EXPECT_EQ(tag.name(), ""); - EXPECT_EQ(tag.min(), 0); - EXPECT_EQ(tag.max(), -1); - EXPECT_FALSE(tag.isSinglePropertyTag()); - EXPECT_TRUE(tag.isValidChild("")); - EXPECT_TRUE(tag.isValidChild("abc")); -} - -//! Testing default tag intended for storing unlimited amount of items of any type. - -TEST_F(TagInfoTest, defaultTag) -{ - // initial state - TagInfo tag = TagInfo::universalTag("name"); - EXPECT_EQ(tag.name(), std::string("name")); - EXPECT_EQ(tag.min(), 0); - EXPECT_EQ(tag.max(), -1); - EXPECT_FALSE(tag.isSinglePropertyTag()); - EXPECT_TRUE(tag.isValidChild("")); - EXPECT_TRUE(tag.isValidChild("abc")); -} - -//! Testing property tag intended for storing single PropertyItem. - -TEST_F(TagInfoTest, propertyTag) -{ - // initial state - TagInfo tag = TagInfo::propertyTag("name", "model_type"); - - EXPECT_EQ(tag.name(), std::string("name")); - EXPECT_EQ(tag.min(), 1); - EXPECT_EQ(tag.max(), 1); - EXPECT_TRUE(tag.isSinglePropertyTag()); - EXPECT_TRUE(tag.isValidChild("model_type")); - EXPECT_FALSE(tag.isValidChild("abc")); -} - -//! Testing equality operators. - -TEST_F(TagInfoTest, equalityOperator) -{ - // default constructor - TagInfo tag1, tag2; - EXPECT_TRUE(tag1 == tag2); - EXPECT_FALSE(tag1 != tag2); - - // same property tag - TagInfo tag3 = TagInfo::propertyTag("name", "model_type"); - TagInfo tag4 = TagInfo::propertyTag("name", "model_type"); - EXPECT_TRUE(tag3 == tag4); - EXPECT_FALSE(tag3 != tag4); - - // same universal tag - TagInfo tag5 = TagInfo::universalTag("name"); - TagInfo tag6 = TagInfo::universalTag("name"); - EXPECT_TRUE(tag5 == tag6); - EXPECT_FALSE(tag5 != tag6); - - // different tag - TagInfo tag7("tag7", 0, 1, std::vector<std::string>()); - TagInfo tag8("tag8", 0, 1, std::vector<std::string>()); - EXPECT_FALSE(tag7 == tag8); - EXPECT_TRUE(tag7 != tag8); -} diff --git a/mvvm/tests/testmodel/tagrow.test.cpp b/mvvm/tests/testmodel/tagrow.test.cpp deleted file mode 100644 index 3e9c9d33ed0..00000000000 --- a/mvvm/tests/testmodel/tagrow.test.cpp +++ /dev/null @@ -1,141 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/tagrow.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/tagrow.h" - -using namespace ModelView; - -//! Testing AxisItems. - -class TagRowTest : public ::testing::Test { -public: - ~TagRowTest(); - - TagRow test_method(const TagRow& input) { return input; } -}; - -TagRowTest::~TagRowTest() = default; - -//! Initial state. - -TEST_F(TagRowTest, initialState) -{ - TagRow tagrow; - EXPECT_EQ(tagrow.tag, ""); - EXPECT_EQ(tagrow.row, -1); -} - -//! Brace initializer. - -TEST_F(TagRowTest, braceInitializer) -{ - TagRow tagrow{"abc", 42}; - EXPECT_EQ(tagrow.tag, "abc"); - EXPECT_EQ(tagrow.row, 42); - - tagrow = {}; - EXPECT_EQ(tagrow.tag, ""); - EXPECT_EQ(tagrow.row, -1); - - tagrow = {"cde", 43}; - EXPECT_EQ(tagrow.tag, "cde"); - EXPECT_EQ(tagrow.row, 43); - - TagRow tagrow2 = {"cde"}; - EXPECT_EQ(tagrow2.tag, "cde"); - EXPECT_EQ(tagrow2.row, -1); -} - -//! Equality operators. - -TEST_F(TagRowTest, equalityOperators) -{ - TagRow tag1; - TagRow tag2; - EXPECT_TRUE(tag1 == tag2); - EXPECT_FALSE(tag1 != tag2); - - TagRow tag3 = {"abc", 42}; - TagRow tag4 = {"abc", 42}; - EXPECT_TRUE(tag3 == tag4); - EXPECT_FALSE(tag3 != tag4); - - TagRow tag5 = {"abc", 42}; - TagRow tag6 = {"abc", 43}; - EXPECT_FALSE(tag5 == tag6); - EXPECT_TRUE(tag5 != tag6); - - TagRow tag7 = {"a", 42}; - TagRow tag8 = {"b", 42}; - EXPECT_FALSE(tag7 == tag8); - EXPECT_TRUE(tag7 != tag8); -} - -//! Assignment operators. - -TEST_F(TagRowTest, assignmentOperator) -{ - TagRow tag1; - TagRow tag2{"abc", 42}; - - tag1 = tag2; - EXPECT_EQ(tag1.row, 42); - EXPECT_EQ(tag1.tag, "abc"); -} - -//! Factory methods. - -TEST_F(TagRowTest, factoryMethods) -{ - auto tagrow = TagRow::append(); - EXPECT_EQ(tagrow.tag, ""); - EXPECT_EQ(tagrow.row, -1); - - const std::string expected_name("tag"); - tagrow = TagRow::append(expected_name); - EXPECT_EQ(tagrow.tag, expected_name); - EXPECT_EQ(tagrow.row, -1); - - tagrow = TagRow::prepend(expected_name); - EXPECT_EQ(tagrow.tag, expected_name); - EXPECT_EQ(tagrow.row, 0); -} - -//! Implicit type convertion - -TEST_F(TagRowTest, implicitConvertion) -{ - auto tagrow = test_method("abc"); - EXPECT_EQ(tagrow.tag, "abc"); - EXPECT_EQ(tagrow.row, -1); -} - -//! Find next tagrow. - -TEST_F(TagRowTest, next) -{ - TagRow tagrow{"tag", 0}; - EXPECT_EQ(tagrow.next().tag, "tag"); - EXPECT_EQ(tagrow.next().row, 1); -} - -//! Find previous tagrow. - -TEST_F(TagRowTest, prev) -{ - TagRow tagrow{"tag", 1}; - EXPECT_EQ(tagrow.prev().tag, "tag"); - EXPECT_EQ(tagrow.prev().row, 0); -} diff --git a/mvvm/tests/testmodel/test_utils.test.cpp b/mvvm/tests/testmodel/test_utils.test.cpp deleted file mode 100644 index e87874c32ac..00000000000 --- a/mvvm/tests/testmodel/test_utils.test.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/test_utils.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "test_utils.h" - -using TestUtils::toVector; - -//! Testing functions in test_utils. - -class TestUtilsTest : public ::testing::Test { -public: - ~TestUtilsTest(); -}; - -TestUtilsTest::~TestUtilsTest() = default; - -//! Testing toVector function. - -TEST_F(TestUtilsTest, toVector) -{ - std::vector<int> expected_int = {1, 2, 3}; - EXPECT_EQ(toVector<int>(1, 2, 3), expected_int); - - std::vector<double> expected_double = {1.1, 2.2, 3.3}; - EXPECT_EQ(toVector<double>(1.1, 2.2, 3.3), expected_double); - - std::vector<std::string> expected_string = {"a", "bb", "ccc"}; - EXPECT_EQ(toVector<std::string>("a", "bb", "ccc"), expected_string); -} diff --git a/mvvm/tests/testmodel/threadsafestack.test.cpp b/mvvm/tests/testmodel/threadsafestack.test.cpp deleted file mode 100644 index 79c014d771f..00000000000 --- a/mvvm/tests/testmodel/threadsafestack.test.cpp +++ /dev/null @@ -1,163 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/threadsafestack.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/utils/threadsafestack.h" -#include <future> - -using namespace ModelView; - -//! Testing AxisItems. - -class ThreadSafeStackTest : public ::testing::Test { -public: - ~ThreadSafeStackTest(); -}; - -ThreadSafeStackTest::~ThreadSafeStackTest() = default; - -//! Checking stack initial state (single thread mode). - -TEST_F(ThreadSafeStackTest, initialState) -{ - threadsafe_stack<int> stack; - EXPECT_TRUE(stack.empty()); - int value; - EXPECT_FALSE(stack.try_pop(value)); - - auto sh_value = stack.try_pop(); - EXPECT_FALSE(sh_value); -} - -//! Push and then pop (single thread mode). - -TEST_F(ThreadSafeStackTest, pushAndPop) -{ - threadsafe_stack<int> stack; - - stack.push(42); - EXPECT_FALSE(stack.empty()); - int value(0); - EXPECT_TRUE(stack.try_pop(value)); - EXPECT_EQ(value, 42); - - stack.push(43); - auto result = stack.wait_and_pop(); - EXPECT_EQ(*result.get(), 43); -} - -//! Update top value (single thread mode). - -TEST_F(ThreadSafeStackTest, updateTop) -{ - threadsafe_stack<int> stack; - - // update of empty stack means simple appearance of value - stack.update_top(42); - EXPECT_FALSE(stack.empty()); - int value(0); - EXPECT_TRUE(stack.try_pop(value)); - EXPECT_EQ(value, 42); - - // updating value - stack.push(43); - stack.update_top(44); - auto result = stack.wait_and_pop(); - EXPECT_EQ(*result.get(), 44); - - // shouldn't be more values - auto sh_value = stack.try_pop(); - EXPECT_FALSE(sh_value); -} - -//! Push and pop in concurrent mode. -//! Test is borrowed from Anthony Williams, C++ Concurrency in Action, Second edition. - -TEST_F(ThreadSafeStackTest, concurentPushAndPop) -{ - threadsafe_stack<int> stack; - std::promise<void> go, push_ready_for_test, pop_ready_for_test; - std::shared_future<void> ready(go.get_future()); - std::future<void> push_done; - std::future<std::shared_ptr<int>> pop_done; - - try { - // starting pushing thread - push_done = std::async(std::launch::async, [&stack, ready, &push_ready_for_test]() { - push_ready_for_test.set_value(); - ready.wait(); - stack.push(42); - }); - - // starting pop thread - pop_done = std::async(std::launch::async, [&stack, ready, &pop_ready_for_test]() { - pop_ready_for_test.set_value(); - ready.wait(); - return stack.wait_and_pop(); - }); - - // waiting for threads being prepared for racing - push_ready_for_test.get_future().wait(); - pop_ready_for_test.get_future().wait(); - - // starting concurrent push and pop - go.set_value(); - - // checking result - push_done.get(); // making sure pushing thread has finished - - EXPECT_EQ(*pop_done.get(), 42); - EXPECT_TRUE(stack.empty()); - - } catch (...) { - go.set_value(); - throw; - } -} - -//! Explicitly terminate waiting (concurrent mode). - -TEST_F(ThreadSafeStackTest, concurentStopWaiting) -{ - threadsafe_stack<int> stack; - std::promise<void> go, pop_ready_for_test; - std::shared_future<void> ready(go.get_future()); - std::future<std::shared_ptr<int>> pop_done; - - try { - // starting pop thread - pop_done = std::async(std::launch::async, [&stack, ready, &pop_ready_for_test]() { - pop_ready_for_test.set_value(); - ready.wait(); - return stack.wait_and_pop(); - }); - - // waiting for threads being prepared for racing - pop_ready_for_test.get_future().wait(); - - // starting waiting on empty stack - go.set_value(); - - // stopping waiting - stack.stop(); - - // stopping stack will raise exception - EXPECT_THROW(*pop_done.get(), empty_stack); - EXPECT_TRUE(stack.empty()); - - } catch (...) { - go.set_value(); - throw; - } -} diff --git a/mvvm/tests/testmodel/undostack.test.cpp b/mvvm/tests/testmodel/undostack.test.cpp deleted file mode 100644 index 6cee0b90856..00000000000 --- a/mvvm/tests/testmodel/undostack.test.cpp +++ /dev/null @@ -1,919 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/undostack.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/commands/commandadapter.h" -#include "mvvm/commands/setvaluecommand.h" -#include "mvvm/commands/undostack.h" -#include "mvvm/interfaces/undostackinterface.h" -#include "mvvm/model/itemutils.h" -#include "mvvm/model/propertyitem.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/model/taginfo.h" -#include "mvvm/standarditems/axisitems.h" -#include "mvvm/standarditems/data1ditem.h" -#include "mvvm/standarditems/graphitem.h" -#include "toyitems.h" -#include "toymodel.h" - -using namespace ModelView; - -class UndoStackTest : public ::testing::Test { -public: - ~UndoStackTest(); -}; - -UndoStackTest::~UndoStackTest() = default; - -//! Checking time of life of the command during undo/redo. -//! This is connected with the fact, that Qt takes ownership of the command and we have to use -//! our own wrapper. - -TEST_F(UndoStackTest, commandTimeOfLife) -{ - SessionModel model; - auto item = model.insertItem<PropertyItem>(); - item->setData(42); - - std::weak_ptr<SetValueCommand> pw_command; // to trace command time of life - - UndoStack stack; - - { - // creating command - auto command = - std::make_shared<SetValueCommand>(item, QVariant::fromValue(43), ItemDataRole::DATA); - pw_command = command; - EXPECT_EQ(pw_command.use_count(), 1); - - // checking state of item, stack and command - EXPECT_EQ(item->data<int>(), 42); - EXPECT_FALSE(stack.canRedo()); - EXPECT_FALSE(stack.canUndo()); - EXPECT_EQ(stack.index(), 0); - EXPECT_EQ(stack.count(), 0); - EXPECT_EQ(command->isObsolete(), false); - EXPECT_FALSE(std::get<bool>(command->result())); - - // adding command to the stack, it will lead to command execution - stack.execute(command); - EXPECT_EQ(pw_command.use_count(), 2); - - // checking state of item, stack and command - EXPECT_EQ(item->data<int>(), 43); - EXPECT_FALSE(stack.canRedo()); - EXPECT_TRUE(stack.canUndo()); - EXPECT_EQ(stack.index(), 1); - EXPECT_EQ(stack.count(), 1); - EXPECT_EQ(command->isObsolete(), false); - EXPECT_TRUE(std::get<bool>(command->result())); - - // undoing - stack.undo(); - EXPECT_EQ(item->data<int>(), 42); - EXPECT_TRUE(stack.canRedo()); - EXPECT_FALSE(stack.canUndo()); - EXPECT_EQ(stack.index(), 0); - EXPECT_EQ(stack.count(), 1); - EXPECT_EQ(command->isObsolete(), false); - EXPECT_TRUE(std::get<bool>(command->result())); - - // redoing - stack.redo(); - EXPECT_EQ(item->data<int>(), 43); - EXPECT_FALSE(stack.canRedo()); - EXPECT_TRUE(stack.canUndo()); - EXPECT_EQ(stack.index(), 1); - EXPECT_EQ(stack.count(), 1); - EXPECT_EQ(command->isObsolete(), false); - EXPECT_TRUE(std::get<bool>(command->result())); - } - - EXPECT_EQ(pw_command.use_count(), 1); - if (auto command = pw_command.lock()) { - EXPECT_EQ(command->isObsolete(), false); - EXPECT_TRUE(std::get<bool>(command->result())); - } -} - -//! Checking time of life of the command during undo/redo. -//! Same as above, but command is trying to set same value. It makes it "expired" and it should -//! be removed from the stack. - -TEST_F(UndoStackTest, expiredCommandTimeOfLife) -{ - SessionModel model; - auto item = model.insertItem<PropertyItem>(); - item->setData(42); - - std::weak_ptr<SetValueCommand> pw_command; // to trace command time of life - - UndoStack stack; - - { - // creating command which sets the same value - auto command = - std::make_shared<SetValueCommand>(item, QVariant::fromValue(42), ItemDataRole::DATA); - pw_command = command; - EXPECT_EQ(pw_command.use_count(), 1); - - // checking state of item, stack and command - EXPECT_EQ(item->data<int>(), 42); - EXPECT_FALSE(stack.canRedo()); - EXPECT_FALSE(stack.canUndo()); - EXPECT_EQ(stack.index(), 0); - EXPECT_EQ(stack.count(), 0); - EXPECT_EQ(command->isObsolete(), false); - EXPECT_FALSE(std::get<bool>(command->result())); - - // adding command to the stack, it will lead to command execution - stack.execute(command); - EXPECT_EQ(pw_command.use_count(), 1); // was already deleted from the stack - - // checking state of item, stack and command - EXPECT_EQ(item->data<int>(), 42); - EXPECT_FALSE(stack.canRedo()); - EXPECT_FALSE(stack.canUndo()); - EXPECT_EQ(stack.index(), 0); - EXPECT_EQ(stack.count(), 0); - EXPECT_EQ(command->isObsolete(), true); - EXPECT_FALSE(std::get<bool>(command->result())); - } - - EXPECT_EQ(pw_command.use_count(), 0); - EXPECT_EQ(pw_command.lock(), nullptr); -} - -TEST_F(UndoStackTest, initialState) -{ - SessionModel model; - - // no undo/redo stack by default - EXPECT_TRUE(model.undoStack() == nullptr); - - // switching undo/redo on - model.setUndoRedoEnabled(true); - auto stack = model.undoStack(); - EXPECT_TRUE(stack != nullptr); - - // initial state of undo redo stack - EXPECT_TRUE(stack->isActive()); - EXPECT_FALSE(stack->canRedo()); - EXPECT_FALSE(stack->canUndo()); - EXPECT_EQ(stack->index(), 0); -} - -TEST_F(UndoStackTest, insertNewItem) -{ - const model_type modelType(GUI::Constants::BaseType); - SessionModel model; - model.setUndoRedoEnabled(true); - auto stack = model.undoStack(); - - // inserting single item - auto item = model.insertItem<SessionItem>(); - EXPECT_TRUE(item != nullptr); - EXPECT_EQ(item->modelType(), GUI::Constants::BaseType); - EXPECT_EQ(model.rootItem()->childrenCount(), 1); - EXPECT_EQ(stack->index(), 1); - EXPECT_EQ(stack->count(), 1); - EXPECT_FALSE(stack->canRedo()); - EXPECT_TRUE(stack->canUndo()); - - // undoing item insertion - stack->undo(); - EXPECT_EQ(model.rootItem()->childrenCount(), 0); - EXPECT_EQ(stack->index(), 0); - EXPECT_EQ(stack->count(), 1); - EXPECT_TRUE(stack->canRedo()); - EXPECT_FALSE(stack->canUndo()); - - // redoing item insertion - stack->redo(); - EXPECT_EQ(model.rootItem()->childrenCount(), 1); - EXPECT_EQ(Utils::ChildAt(model.rootItem(), 0)->modelType(), modelType); - EXPECT_EQ(stack->index(), 1); - EXPECT_FALSE(stack->canRedo()); - EXPECT_TRUE(stack->canUndo()); - - // clearing stack - stack->clear(); - EXPECT_EQ(stack->index(), 0); - EXPECT_FALSE(stack->canRedo()); - EXPECT_FALSE(stack->canUndo()); - EXPECT_EQ(model.rootItem()->childrenCount(), 1); - EXPECT_EQ(Utils::ChildAt(model.rootItem(), 0)->modelType(), modelType); -} - -//! Insert property item, unto, redo, and checking that identifier is preserved. - -TEST_F(UndoStackTest, insertPropertyItemID) -{ - SessionModel model; - model.setUndoRedoEnabled(true); - auto stack = model.undoStack(); - - auto item = model.insertItem<PropertyItem>(); - auto original_id = item->identifier(); - - EXPECT_EQ(stack->index(), 1); - EXPECT_EQ(stack->count(), 1); - - model.undoStack()->undo(); - EXPECT_EQ(model.rootItem()->childrenCount(), 0); - - model.undoStack()->redo(); - EXPECT_EQ(model.rootItem()->childrenCount(), 1); - - auto restored_property_item = Utils::ChildAt(model.rootItem(), 0); - EXPECT_EQ(restored_property_item->modelType(), GUI::Constants::PropertyType); - EXPECT_EQ(restored_property_item->identifier(), original_id); -} - -//! Undo/redo scenario when few items inserted. - -TEST_F(UndoStackTest, insertParentAndChild) -{ - SessionModel model; - model.setUndoRedoEnabled(true); - auto stack = model.undoStack(); - - auto parent = model.insertItem<SessionItem>(); - parent->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - - model.insertItem<PropertyItem>(parent); - model.insertItem<PropertyItem>(parent); - - // state of the stack after insertion of 3 items - EXPECT_EQ(stack->count(), 3); - EXPECT_EQ(stack->index(), 3); - EXPECT_EQ(model.rootItem()->childrenCount(), 1); - EXPECT_EQ(parent->childrenCount(), 2); - - // undoing two last insertions - stack->undo(); - stack->undo(); - EXPECT_EQ(stack->count(), 3); - EXPECT_EQ(stack->index(), 1); - EXPECT_EQ(model.rootItem()->childrenCount(), 1); - EXPECT_EQ(parent->childrenCount(), 0); - - // redoing once - stack->redo(); - EXPECT_EQ(stack->count(), 3); - EXPECT_EQ(stack->index(), 2); - EXPECT_EQ(model.rootItem()->childrenCount(), 1); - EXPECT_EQ(parent->childrenCount(), 1); - EXPECT_EQ(Utils::ChildAt(parent, 0)->modelType(), GUI::Constants::PropertyType); -} - -//! Undo/redo scenario when item inserted and data set few times. - -TEST_F(UndoStackTest, setData) -{ - const int role = ItemDataRole::DATA; - SessionModel model; - model.setUndoRedoEnabled(true); - auto stack = model.undoStack(); - - // creating item - auto item = model.insertItem<SessionItem>(); - EXPECT_FALSE(model.data(item, role).isValid()); - - // setting new data - QVariant value(42.0); - model.setData(item, value, role); - EXPECT_EQ(model.data(item, role), value); - - EXPECT_EQ(stack->index(), 2); // insert and setData commands - EXPECT_FALSE(model.undoStack()->canRedo()); - EXPECT_TRUE(model.undoStack()->canUndo()); - - // undoing and checking - stack->undo(); - EXPECT_EQ(stack->index(), 1); - EXPECT_FALSE(model.data(item, role).isValid()); - - // setting data three times - model.setData(item, QVariant::fromValue(42.0), role); - model.setData(item, QVariant::fromValue(43.0), role); - model.setData(item, QVariant::fromValue(44.0), role); - EXPECT_EQ(stack->index(), 4); - EXPECT_EQ(model.data(item, role).value<double>(), 44.0); - stack->undo(); - stack->undo(); - EXPECT_EQ(model.data(item, role).value<double>(), 42.0); - stack->redo(); - stack->redo(); - EXPECT_EQ(model.data(item, role).value<double>(), 44.0); -} - -//! Undo/redo scenario when item data changed through item and not the model. - -TEST_F(UndoStackTest, setDataThroughItem) -{ - const int role = ItemDataRole::DATA; - const QVariant value(42.0); - - SessionModel model; - model.setUndoRedoEnabled(true); - auto stack = model.undoStack(); - - // creating item - auto item = model.insertItem<SessionItem>(); - EXPECT_FALSE(model.data(item, role).isValid()); - - // setting new data through item (and not through model) - item->setData(value, role); - EXPECT_EQ(item->data<QVariant>(role), value); - - EXPECT_EQ(stack->index(), 2); // insert and setData commands - EXPECT_FALSE(model.undoStack()->canRedo()); - EXPECT_TRUE(model.undoStack()->canUndo()); - - // undoing and checking - stack->undo(); - EXPECT_EQ(stack->index(), 1); - EXPECT_FALSE(model.data(item, role).isValid()); -} - -//! Undo/redo scenario when we set same data. Undo stack should be empty. - -TEST_F(UndoStackTest, setSameData) -{ - SessionModel model; - auto item = model.insertItem<PropertyItem>(); - item->setData(42.0); - - model.setUndoRedoEnabled(true); - auto stack = model.undoStack(); - EXPECT_EQ(stack->index(), 0); - EXPECT_FALSE(model.undoStack()->canRedo()); - EXPECT_FALSE(model.undoStack()->canUndo()); - - // setting same data should not lead to appearance of command in a stack - item->setData(42.0); - EXPECT_EQ(stack->index(), 0); - EXPECT_FALSE(model.undoStack()->canRedo()); - EXPECT_FALSE(model.undoStack()->canUndo()); -} - -//! Checks if we insert item, set data and undo everything we can get back to the data. - -TEST_F(UndoStackTest, insertAndSetData) -{ - const int role = ItemDataRole::DATA; - SessionModel model; - model.setUndoRedoEnabled(true); - auto stack = model.undoStack(); - - // creating item - auto item = model.insertItem<SessionItem>(); - EXPECT_FALSE(model.data(item, role).isValid()); - - // setting new data - QVariant value(42.0); - model.setData(item, value, role); - EXPECT_EQ(model.data(item, role), value); - - EXPECT_EQ(stack->index(), 2); // insert and setData commands - EXPECT_FALSE(model.undoStack()->canRedo()); - EXPECT_TRUE(model.undoStack()->canUndo()); - - // undoing twice and checking - stack->undo(); - stack->undo(); - EXPECT_EQ(stack->index(), 0); - EXPECT_EQ(model.rootItem()->childrenCount(), 0); - - // returning all back - stack->redo(); - stack->redo(); - EXPECT_EQ(stack->index(), 2); - EXPECT_EQ(model.rootItem()->childrenCount(), 1); - item = Utils::ChildAt(model.rootItem(), 0); - EXPECT_EQ(model.data(item, role).value<double>(), 42.0); -} - -//! Inserting item, setting the data, removing row, undoing, checking item and data. - -TEST_F(UndoStackTest, removeRow) -{ - const int role = ItemDataRole::DATA; - const QVariant data(42); - - SessionModel model; - model.setUndoRedoEnabled(true); - auto stack = model.undoStack(); - - auto item = model.insertItem<SessionItem>(); - item->setData(data, role); - - // initial state before removing the row - EXPECT_EQ(stack->count(), 2); // insert and setData commands - EXPECT_EQ(stack->index(), 2); // insert and setData commands - EXPECT_FALSE(model.undoStack()->canRedo()); - EXPECT_TRUE(model.undoStack()->canUndo()); - EXPECT_EQ(item->data<QVariant>(role), data); - EXPECT_EQ(model.rootItem()->childrenCount(), 1); - - // removing the row - model.removeItem(model.rootItem(), {"", 0}); - EXPECT_EQ(stack->count(), 3); - EXPECT_EQ(stack->index(), 3); - EXPECT_EQ(model.rootItem()->childrenCount(), 0); - - // undoing and checking the data - stack->undo(); - EXPECT_EQ(stack->count(), 3); - EXPECT_EQ(stack->index(), 2); - EXPECT_EQ(model.rootItem()->childrenCount(), 1); - item = Utils::ChildAt(model.rootItem(), 0); - EXPECT_EQ(model.data(item, role).value<double>(), 42.0); - EXPECT_EQ(item->modelType(), GUI::Constants::BaseType); -} - -//! Inserting parent and child, setting data to them, removing parent, undoing and checking. - -TEST_F(UndoStackTest, removeParentAndChild) -{ - const int role1(ItemDataRole::DATA), role2(ItemDataRole::DISPLAY); - const QVariant data1(42); - const QVariant data2 = QVariant::fromValue(std::string("abc")); - - SessionModel model; - model.setUndoRedoEnabled(true); - auto stack = model.undoStack(); - - auto parent = model.insertItem<SessionItem>(); - parent->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - - parent->setData(data1, role1); - auto child = model.insertItem<PropertyItem>(parent); - child->setData(data2, role2); - - EXPECT_EQ(stack->count(), 4); - EXPECT_EQ(stack->index(), 4); - - // removing parent - model.removeItem(model.rootItem(), {"", 0}); - EXPECT_EQ(stack->count(), 5); - EXPECT_EQ(stack->index(), 5); - EXPECT_EQ(model.rootItem()->childrenCount(), 0); - - // undoing - stack->undo(); - EXPECT_EQ(stack->count(), 5); - EXPECT_EQ(stack->index(), 4); - EXPECT_EQ(model.rootItem()->childrenCount(), 1); - auto parent_at = Utils::ChildAt(model.rootItem(), 0); - auto child_at = Utils::ChildAt(parent_at, 0); - - EXPECT_EQ(parent_at->modelType(), GUI::Constants::BaseType); - EXPECT_EQ(child_at->modelType(), GUI::Constants::PropertyType); - - EXPECT_EQ(parent_at->data<QVariant>(role1), data1); - EXPECT_EQ(child_at->data<QVariant>(role2), data2); -} - -//! Insert item, remove row, undo and check item id. - -TEST_F(UndoStackTest, itemIdentifierOnRemove) -{ - SessionModel model; - model.setUndoRedoEnabled(true); - auto stack = model.undoStack(); - - auto parent = model.insertItem<SessionItem>(); - parent->registerTag(TagInfo::universalTag("defaultTag"), /*set_as_default*/ true); - - identifier_type parent_id = parent->identifier(); - auto child = model.insertItem<PropertyItem>(parent); - identifier_type child_id = child->identifier(); - - // removing parent - model.removeItem(model.rootItem(), {"", 0}); - EXPECT_EQ(stack->count(), 3); - EXPECT_EQ(stack->index(), 3); - EXPECT_EQ(model.rootItem()->childrenCount(), 0); - - stack->undo(); - auto parent_at = Utils::ChildAt(model.rootItem(), 0); - auto child_at = Utils::ChildAt(parent_at, 0); - identifier_type parent_id2 = parent_at->identifier(); - identifier_type child_id2 = child_at->identifier(); - - EXPECT_EQ(parent_id, parent_id2); - EXPECT_EQ(child_id, child_id2); -} - -//! Create multilayer, add two layers, remove everything and undo. -//! Toy models are used here. - -TEST_F(UndoStackTest, multiLayer) -{ - auto pool = std::make_shared<ItemPool>(); - - ToyItems::SampleModel model(pool); - model.setUndoRedoEnabled(true); - auto stack = model.undoStack(); - - // creating multi layer - auto parent = model.insertItem<ToyItems::MultiLayerItem>(); - EXPECT_TRUE(dynamic_cast<ToyItems::MultiLayerItem*>(parent) != nullptr); - EXPECT_EQ(parent->modelType(), ToyItems::GUI::Constants::MultiLayerItemType); - - // inserting two layers - auto layer0 = model.insertItem<ToyItems::LayerItem>(parent); - auto layer1 = model.insertItem<ToyItems::LayerItem>(parent); - - // saving identifiers for further reference - identifier_type id_parent = parent->identifier(); - identifier_type id_layer0 = layer0->identifier(); - identifier_type id_layer1 = layer1->identifier(); - - // checking status of unddo stack - EXPECT_EQ(stack->count(), 3); - EXPECT_EQ(stack->index(), 3); - - // removing multi layer completely - model.removeItem(model.rootItem(), {"", 0}); - EXPECT_EQ(stack->count(), 4); - EXPECT_EQ(stack->index(), 4); - EXPECT_EQ(model.rootItem()->childrenCount(), 0); - - // multilayer and its two layers should gone from registration - EXPECT_TRUE(pool->item_for_key(id_parent) == nullptr); - EXPECT_TRUE(pool->item_for_key(id_layer0) == nullptr); - EXPECT_TRUE(pool->item_for_key(id_layer1) == nullptr); - - // undoing multilayer removal - stack->undo(); - EXPECT_EQ(stack->count(), 4); - EXPECT_EQ(stack->index(), 3); - - // restoring pointers back - auto parent_at = Utils::ChildAt(model.rootItem(), 0); - auto layer0_at = Utils::ChildAt(parent_at, 0); - auto layer1_at = Utils::ChildAt(parent_at, 1); - - // checking that restored item has corrrect identifiers - EXPECT_EQ(parent_at->identifier(), id_parent); - EXPECT_EQ(layer0_at->identifier(), id_layer0); - EXPECT_EQ(layer1_at->identifier(), id_layer1); - - // checking tag - EXPECT_EQ(layer0_at->tagRow().tag, ToyItems::MultiLayerItem::T_LAYERS); - EXPECT_EQ(layer1_at->tagRow().tag, ToyItems::MultiLayerItem::T_LAYERS); - std::vector<SessionItem*> expected = {layer0_at, layer1_at}; - EXPECT_EQ(parent_at->getItems(ToyItems::MultiLayerItem::T_LAYERS), expected); -} - -//! Move single layer from multilayer to another empty multilayer. - -TEST_F(UndoStackTest, moveLayerFromMultiLayer) -{ - auto pool = std::make_shared<ItemPool>(); - - ToyItems::SampleModel model(pool); - model.setUndoRedoEnabled(true); - auto stack = model.undoStack(); - - // creating multi layer with 3 layers - auto multilayer0 = model.insertItem<ToyItems::MultiLayerItem>(); - auto layer0 = model.insertItem<ToyItems::LayerItem>(multilayer0); - auto multilayer1 = model.insertItem<ToyItems::MultiLayerItem>(); - - // saving identifiers for further reference - identifier_type id_multilayer0 = multilayer0->identifier(); - identifier_type id_layer0 = layer0->identifier(); - identifier_type id_multilayer1 = multilayer1->identifier(); - - // moving layer from multilayer - model.moveItem(layer0, multilayer1, {"", 0}); - - // checking results - std::vector<SessionItem*> expected = {layer0}; - EXPECT_EQ(multilayer0->children().size(), 0); - EXPECT_EQ(multilayer1->children(), expected); - EXPECT_EQ(pool->item_for_key(id_layer0), layer0); - - // undoing - stack->undo(); - EXPECT_EQ(multilayer0->children(), expected); - EXPECT_EQ(multilayer1->children().size(), 0); - EXPECT_EQ(pool->item_for_key(id_layer0), layer0); -} - -//! Move single layer from multilayer to another empty multilayer. -//! Delete second multilayer and undo. - -TEST_F(UndoStackTest, moveLayerFromMLDeleteSecond) -{ - auto pool = std::make_shared<ItemPool>(); - - ToyItems::SampleModel model(pool); - model.setUndoRedoEnabled(true); - auto stack = model.undoStack(); - - // creating multi layer with 3 layers - auto multilayer0 = model.insertItem<ToyItems::MultiLayerItem>(); - auto layer0 = model.insertItem<ToyItems::LayerItem>(multilayer0); - auto multilayer1 = model.insertItem<ToyItems::MultiLayerItem>(); - - // saving identifiers for further reference - identifier_type id_multilayer0 = multilayer0->identifier(); - identifier_type id_layer0 = layer0->identifier(); - identifier_type id_multilayer1 = multilayer1->identifier(); - - // moving layer from multilayer - model.moveItem(layer0, multilayer1, {"", 0}); - - // checking results - std::vector<SessionItem*> expected = {layer0}; - EXPECT_EQ(multilayer0->children().size(), 0); - EXPECT_EQ(multilayer1->children(), expected); - EXPECT_EQ(pool->item_for_key(id_layer0), layer0); - - // deleting second multilayer - model.removeItem(model.rootItem(), {"", 1}); - - // undoing deletion - stack->undo(); - - // restoring ponters - auto layer0_at = pool->item_for_key(id_layer0); - auto multilayer1_at = pool->item_for_key(id_multilayer1); - - expected = {layer0_at}; - EXPECT_EQ(multilayer0->children().size(), 0); - EXPECT_EQ(multilayer1_at->children(), expected); - - // unoing move - stack->undo(); - - EXPECT_EQ(multilayer0->children(), expected); - EXPECT_EQ(multilayer1_at->children().size(), 0); -} - -//! Create 2 multilayers, 3 layers each. Move layer from one multilayer to another. -//! Deleting everything and undoing. - -TEST_F(UndoStackTest, moveLayerFromMLDeleteAll) -{ - auto pool = std::make_shared<ItemPool>(); - - ToyItems::SampleModel model(pool); - model.setUndoRedoEnabled(true); - auto stack = model.undoStack(); - - // creating multi layer with 3 layers - auto multilayer0 = model.insertItem<ToyItems::MultiLayerItem>(); - auto layer0 = model.insertItem<ToyItems::LayerItem>(multilayer0); - auto layer1 = model.insertItem<ToyItems::LayerItem>(multilayer0); - auto layer2 = model.insertItem<ToyItems::LayerItem>(multilayer0); - - // saving identifiers for further reference - identifier_type id_multilayer0 = multilayer0->identifier(); - identifier_type id_layer0 = layer0->identifier(); - identifier_type id_layer1 = layer1->identifier(); - identifier_type id_layer2 = layer2->identifier(); - - // creating another multi layer with 3 layers - auto multilayer1 = model.insertItem<ToyItems::MultiLayerItem>(); - auto layer3 = model.insertItem<ToyItems::LayerItem>(multilayer1); - auto layer4 = model.insertItem<ToyItems::LayerItem>(multilayer1); - auto layer5 = model.insertItem<ToyItems::LayerItem>(multilayer1); - - // saving identifiers for further reference - identifier_type id_multilayer1 = multilayer1->identifier(); - identifier_type id_layer3 = layer3->identifier(); - identifier_type id_layer4 = layer4->identifier(); - identifier_type id_layer5 = layer5->identifier(); - - // checking status of unddo stack - EXPECT_EQ(stack->count(), 8); - EXPECT_EQ(stack->index(), 8); - - // moving layer1 to second multilayer - model.moveItem(layer1, multilayer1, {ToyItems::MultiLayerItem::T_LAYERS, 0}); - - // removing multilayers - model.removeItem(model.rootItem(), {"", 1}); - model.removeItem(model.rootItem(), {"", 0}); - - // checking status of unddo stack - EXPECT_EQ(stack->count(), 11); - EXPECT_EQ(stack->index(), 11); - - // undoing thrice - stack->undo(); - stack->undo(); - stack->undo(); - - // restoring pointers - auto multilayer0_r = pool->item_for_key(id_multilayer0); - auto layer0_r = pool->item_for_key(id_layer0); - auto layer1_r = pool->item_for_key(id_layer1); - auto layer2_r = pool->item_for_key(id_layer2); - auto multilayer1_r = pool->item_for_key(id_multilayer1); - auto layer3_r = pool->item_for_key(id_layer3); - auto layer4_r = pool->item_for_key(id_layer4); - auto layer5_r = pool->item_for_key(id_layer5); - - // checking layers - std::vector<SessionItem*> expected = {layer0_r, layer1_r, layer2_r}; - EXPECT_EQ(multilayer0_r->children(), expected); - - expected = {layer3_r, layer4_r, layer5_r}; - EXPECT_EQ(multilayer1_r->children(), expected); -} - -//! Creating two multilayers. Copying layer from one multilayer to another. - -TEST_F(UndoStackTest, copyLayerFromMultilayer) -{ - auto pool = std::make_shared<ItemPool>(); - ToyItems::SampleModel model(pool); - model.setUndoRedoEnabled(true); - auto stack = model.undoStack(); - - const double expected_thickness = 55.0; - - // creating multi layer with 3 layers - auto multilayer0 = model.insertItem<ToyItems::MultiLayerItem>(); - auto layer0 = model.insertItem<ToyItems::LayerItem>(multilayer0); - layer0->setProperty(ToyItems::LayerItem::P_THICKNESS, expected_thickness); - auto multilayer1 = model.insertItem<ToyItems::MultiLayerItem>(); - - // copying layer - auto layer_copy = dynamic_cast<ToyItems::LayerItem*>(model.copyItem(layer0, multilayer1)); - EXPECT_EQ(multilayer1->itemCount(ToyItems::MultiLayerItem::T_LAYERS), 1); - EXPECT_EQ(layer_copy->property<double>(ToyItems::LayerItem::P_THICKNESS), expected_thickness); - EXPECT_TRUE(layer0->identifier() != layer_copy->identifier()); - - auto id = layer_copy->identifier(); - EXPECT_EQ(pool->item_for_key(layer_copy->identifier()), layer_copy); - - // undoing - stack->undo(); - EXPECT_EQ(multilayer1->itemCount(ToyItems::MultiLayerItem::T_LAYERS), 0); - - // redoing - stack->redo(); - EXPECT_EQ(multilayer1->itemCount(ToyItems::MultiLayerItem::T_LAYERS), 1); - EXPECT_EQ(multilayer1->getItems(ToyItems::MultiLayerItem::T_LAYERS)[0]->identifier(), id); -} - -//! Add item and changing its data from macros. - -TEST_F(UndoStackTest, beginMacrosEndMacros) -{ - const int role = ItemDataRole::DATA; - const QVariant data(42); - - SessionModel model; - model.setUndoRedoEnabled(true); - auto stack = model.undoStack(); - - stack->beginMacro("macro1"); - auto item = model.insertItem<SessionItem>(); - item->setData(data, role); - stack->endMacro(); - - // initial state before removing the row - EXPECT_EQ(stack->count(), 1); // insert and setData commands - EXPECT_EQ(stack->index(), 1); // insert and setData commands - EXPECT_FALSE(model.undoStack()->canRedo()); - EXPECT_TRUE(model.undoStack()->canUndo()); - EXPECT_EQ(item->data<QVariant>(role), data); - EXPECT_EQ(model.rootItem()->childrenCount(), 1); - - // undoing and checking the data - stack->undo(); - EXPECT_EQ(stack->count(), 1); - EXPECT_EQ(stack->index(), 0); - EXPECT_EQ(model.rootItem()->childrenCount(), 0); - - // redoing - stack->redo(); - EXPECT_EQ(stack->count(), 1); - EXPECT_EQ(stack->index(), 1); - EXPECT_EQ(model.rootItem()->childrenCount(), 1); - item = Utils::ChildAt(model.rootItem(), 0); - EXPECT_EQ(model.data(item, role).value<double>(), 42.0); -} - -//! Add GraphItem and Data1DItem, addisgn data to graph, undo, then redo. -//! GraphItem should be pointing again to Data1DItem. -//! This is real bug case. - -TEST_F(UndoStackTest, insertDataAndGraph) -{ - // constructing model with pool, enabling undo/redo - auto pool = std::make_shared<ItemPool>(); - SessionModel model("Model", pool); - model.setUndoRedoEnabled(true); - EXPECT_EQ(model.undoStack()->index(), 0); - EXPECT_EQ(model.undoStack()->count(), 0); - - auto dataItem = model.insertItem<Data1DItem>(); - auto graphItem = model.insertItem<GraphItem>(); - graphItem->setDataItem(dataItem); - - auto data_item_identifier = dataItem->identifier(); - auto graph_item_identifier = graphItem->identifier(); - - // model has two elements, graph is pointing to the data - EXPECT_EQ(model.undoStack()->index(), 3); - EXPECT_EQ(model.undoStack()->count(), 3); - EXPECT_EQ(model.rootItem()->childrenCount(), 2); - EXPECT_EQ(graphItem->dataItem(), dataItem); - - // checking pool - EXPECT_EQ(pool->item_for_key(data_item_identifier), dataItem); - EXPECT_EQ(pool->item_for_key(graph_item_identifier), graphItem); - - // undoing once (setDataItem operation) - model.undoStack()->undo(); - EXPECT_EQ(model.undoStack()->index(), 2); - EXPECT_EQ(model.undoStack()->count(), 3); - EXPECT_EQ(model.rootItem()->childrenCount(), 2); - EXPECT_EQ(graphItem->dataItem(), nullptr); - - // undoing two more times item - model.undoStack()->undo(); - model.undoStack()->undo(); - EXPECT_EQ(model.undoStack()->index(), 0); - EXPECT_EQ(model.undoStack()->count(), 3); - EXPECT_EQ(model.rootItem()->childrenCount(), 0); - - // redoing (dataItem is back) - model.undoStack()->redo(); - EXPECT_EQ(model.undoStack()->index(), 1); - EXPECT_EQ(model.rootItem()->childrenCount(), 1); - auto restoredDataItem = model.topItem<Data1DItem>(); - EXPECT_EQ(restoredDataItem->identifier(), data_item_identifier); - EXPECT_EQ(pool->item_for_key(data_item_identifier), restoredDataItem); - - // redoing (GraphItem) is back - model.undoStack()->redo(); - EXPECT_EQ(model.undoStack()->index(), 2); - EXPECT_EQ(model.rootItem()->childrenCount(), 2); - auto restoredGraphItem = model.topItem<GraphItem>(); - EXPECT_EQ(restoredGraphItem->identifier(), graph_item_identifier); - EXPECT_EQ(restoredGraphItem->dataItem(), nullptr); - - // redoing (graph is linked with data gaian) - model.undoStack()->redo(); - EXPECT_EQ(model.undoStack()->index(), 3); - EXPECT_EQ(model.rootItem()->childrenCount(), 2); - EXPECT_EQ(restoredGraphItem->dataItem(), restoredDataItem); -} - -//! Setup Data1DItem via macro. Undo, then redo. -//! Add GraphItem and Data1DItem, addisgn data to graph, undo, then redo. -//! GraphItem should be pointing again to Data1DItem. -//! This is real bug case. - -TEST_F(UndoStackTest, insertDataItemViaMacro) -{ - SessionModel model; - model.setUndoRedoEnabled(true); - EXPECT_EQ(model.undoStack()->index(), 0); - EXPECT_EQ(model.undoStack()->count(), 0); - - // setting up single data item via macro - model.undoStack()->beginMacro("AddDataItem"); - auto dataItem = model.insertItem<Data1DItem>(); - const std::vector<double> expected_values = {1.0, 2.0, 3.0}; - const std::vector<double> expected_centers = {0.5, 1.5, 2.5}; - dataItem->setAxis<FixedBinAxisItem>(3, 0.0, 3.0); - dataItem->setValues(expected_values); - model.undoStack()->endMacro(); - - EXPECT_EQ(model.undoStack()->index(), 1); - EXPECT_EQ(model.undoStack()->count(), 1); - - // undoing and returning back - model.undoStack()->undo(); - model.undoStack()->redo(); - EXPECT_EQ(model.undoStack()->index(), 1); - EXPECT_EQ(model.undoStack()->count(), 1); - - auto restoredDataItem = model.topItem<Data1DItem>(); - EXPECT_EQ(restoredDataItem->binCenters(), expected_centers); - EXPECT_EQ(restoredDataItem->binValues(), expected_values); -} diff --git a/mvvm/tests/testmodel/vectoritem.test.cpp b/mvvm/tests/testmodel/vectoritem.test.cpp deleted file mode 100644 index 55eb57155a0..00000000000 --- a/mvvm/tests/testmodel/vectoritem.test.cpp +++ /dev/null @@ -1,68 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testmodel/vectoritem.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/itemutils.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/standarditems/vectoritem.h" - -using namespace ModelView; - -//! VectorItem tests. - -class VectorItemTest : public ::testing::Test { -public: - ~VectorItemTest(); -}; - -VectorItemTest::~VectorItemTest() = default; - -//! Initial state of item when it is created outside of model context. - -TEST_F(VectorItemTest, initialState) -{ - VectorItem item; - - EXPECT_TRUE(Utils::IsSinglePropertyTag(item, VectorItem::P_X)); - EXPECT_TRUE(Utils::IsSinglePropertyTag(item, VectorItem::P_Y)); - EXPECT_TRUE(Utils::IsSinglePropertyTag(item, VectorItem::P_Z)); - - EXPECT_FALSE(item.isEditable()); - - EXPECT_EQ(item.property<double>(VectorItem::P_X), 0.0); - EXPECT_EQ(item.property<double>(VectorItem::P_Y), 0.0); - EXPECT_EQ(item.property<double>(VectorItem::P_Z), 0.0); - - // default label - EXPECT_EQ(item.data<std::string>(), "(0, 0, 0)"); -} - -//! Initial state of item in model context - -TEST_F(VectorItemTest, initialStateFromModel) -{ - SessionModel model; - auto item = model.insertItem<VectorItem>(); - - EXPECT_EQ(item->property<double>(VectorItem::P_X), 0.0); - EXPECT_EQ(item->property<double>(VectorItem::P_Y), 0.0); - EXPECT_EQ(item->property<double>(VectorItem::P_Z), 0.0); - - // default label - EXPECT_EQ(item->data<std::string>(), "(0, 0, 0)"); - - // changing vector component - item->setProperty(VectorItem::P_X, 1.0); - EXPECT_EQ(item->data<std::string>(), "(1, 0, 0)"); -} diff --git a/mvvm/tests/testview/CMakeLists.txt b/mvvm/tests/testview/CMakeLists.txt deleted file mode 100644 index fcd20d0b2a3..00000000000 --- a/mvvm/tests/testview/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -set(test testview) - -file(GLOB source_files "*.cpp") -file(GLOB include_files "*.h") - -find_package(Qt5Core REQUIRED) -find_package(Qt5Test REQUIRED) - -# necessary for Qt creator and clang code model -include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) - -set(CMAKE_AUTOMOC ON) -add_executable(${test} ${source_files} ${include_files}) -target_link_libraries(${test} gtest gmock Qt5::Core Qt5::Test mvvm_view testmachinery qcustomplot) - -if (MVVM_DISCOVER_TESTS) - gtest_discover_tests(${test}) -else() - add_custom_target(${test}_run ALL DEPENDS ${test} COMMAND ${test}) -endif() diff --git a/mvvm/tests/testview/TestAll.cpp b/mvvm/tests/testview/TestAll.cpp deleted file mode 100644 index ac7bb375367..00000000000 --- a/mvvm/tests/testview/TestAll.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testview/TestAll.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "customplot_test_utils.h" -#include "google_test.h" -#include "qcustomplot.h" -#include <QApplication> -#include <QStandardItem> -#include <gmock/gmock.h> - -int main(int argc, char** argv) -{ - ::testing::InitGoogleTest(&argc, argv); - ::testing::InitGoogleMock(&argc, argv); - - ModelView::Comparators::registerComparators(); - qRegisterMetaType<QStandardItem*>("QStandardItem*"); - qRegisterMetaType<QCPRange>("QCPRange"); - - // FIXME find the way not to run app for all tests which doesn't use QWidget. - // The problem here is because of ctest autodiscovery which runs given main at every test. - QApplication app(argc, argv); - Q_UNUSED(app) - - return RUN_ALL_TESTS(); -} diff --git a/mvvm/tests/testview/axistitlecontroller.test.cpp b/mvvm/tests/testview/axistitlecontroller.test.cpp deleted file mode 100644 index 8ff34faf0d2..00000000000 --- a/mvvm/tests/testview/axistitlecontroller.test.cpp +++ /dev/null @@ -1,100 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testview/axistitlecontroller.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/plotting/axistitlecontroller.h" -#include "mvvm/standarditems/plottableitems.h" -#include "qcustomplot.h" -#include <QFont> - -using namespace ModelView; - -//! Testing AxisTitleControllers. - -class AxisTitleControllerTest : public ::testing::Test { -public: - ~AxisTitleControllerTest(); -}; - -AxisTitleControllerTest::~AxisTitleControllerTest() = default; - -//! Initial state. - -TEST_F(AxisTitleControllerTest, initialState) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - - auto axis = custom_plot->xAxis; - - // controller shouldn''t change axis range - AxisTitleController controller(axis); - EXPECT_EQ(controller.currentItem(), nullptr); - - EXPECT_EQ(axis->label(), QString()); -} - -TEST_F(AxisTitleControllerTest, setTextItem) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - - SessionModel model; - auto textItem = model.insertItem<TextItem>(); - - auto axis = custom_plot->xAxis; - // auto expected_pointSize = axis->labelFont().pointSize(); - // auto expected_family = axis->labelFont().family(); - - // this a values hardcoded in plottableitems.cpp. Shell we provide some customized way to create - // TextItem with font/size suitable for QCPAxis ? - const int expected_pointSize = 10; - const std::string expected_family = "Noto Sans"; - - // controller shouldn''t change axis range - AxisTitleController controller(axis); - controller.setItem(textItem); - EXPECT_EQ(controller.currentItem(), textItem); - - EXPECT_EQ(axis->label(), QString()); - EXPECT_EQ(axis->labelFont().family().toStdString(), expected_family); - EXPECT_EQ(axis->labelFont().pointSize(), expected_pointSize); -} - -TEST_F(AxisTitleControllerTest, setFont) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - - SessionModel model; - auto textItem = model.insertItem<TextItem>(); - - auto axis = custom_plot->xAxis; - - // controller shouldn''t change axis range - AxisTitleController controller(axis); - controller.setItem(textItem); - EXPECT_EQ(controller.currentItem(), textItem); - - // setting new label - const QString expected_text("abc"); - const QString expected_family("Helvetica"); - const int expected_size = 42; - textItem->setProperty(TextItem::P_TEXT, expected_text.toStdString()); - textItem->setProperty(TextItem::P_SIZE, expected_size); - textItem->setProperty(TextItem::P_FONT, expected_family.toStdString()); - - // checking that label has updated - EXPECT_EQ(axis->label(), expected_text); - EXPECT_EQ(axis->labelFont().family(), expected_family); - EXPECT_EQ(axis->labelFont().pointSize(), expected_size); -} diff --git a/mvvm/tests/testview/colormapplotcontroller.test.cpp b/mvvm/tests/testview/colormapplotcontroller.test.cpp deleted file mode 100644 index c60d15babce..00000000000 --- a/mvvm/tests/testview/colormapplotcontroller.test.cpp +++ /dev/null @@ -1,214 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testview/colormapplotcontroller.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "customplot_test_utils.h" -#include "google_test.h" -#include "mvvm/model/comboproperty.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/plotting/colormapplotcontroller.h" -#include "mvvm/standarditems/axisitems.h" -#include "mvvm/standarditems/colormapitem.h" -#include "mvvm/standarditems/data2ditem.h" -#include "qcustomplot.h" -#include <QSignalSpy> - -using namespace ModelView; - -//! Testing ColorMapPlotController. - -class ColorMapPlotControllerTest : public ::testing::Test { -public: - ~ColorMapPlotControllerTest(); -}; - -ColorMapPlotControllerTest::~ColorMapPlotControllerTest() = default; - -//! Initial state. - -TEST_F(ColorMapPlotControllerTest, initialState) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - ColorMapPlotController controller(custom_plot.get()); - EXPECT_EQ(controller.currentItem(), nullptr); - - // creation of controller leads to the creation of QCPColorMap - auto color_map = TestUtils::GetPlottable<QCPColorMap>(custom_plot.get()); - ASSERT_TRUE(color_map != nullptr); -} - -//! Setting ColorMapItem with data and checking that QCPColorMap appeared among plottables. - -TEST_F(ColorMapPlotControllerTest, setItem) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - ColorMapPlotController controller(custom_plot.get()); - - // creating data item - SessionModel model; - auto data_item = model.insertItem<Data2DItem>(); - const int nx = 3, ny = 2; - data_item->setAxes(FixedBinAxisItem::create(nx, 0.0, 3.0), - FixedBinAxisItem::create(ny, 0.0, 2.0)); - std::vector<double> expected = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0}; - data_item->setContent(expected); - - // creating colormap item - auto colormap_item = model.insertItem<ColorMapItem>(); - colormap_item->setDataItem(data_item); - - // initializing controller - controller.setItem(colormap_item); - - // checking that QCPColorMap has been created - EXPECT_EQ(custom_plot->plottableCount(), 1); - auto color_map = TestUtils::GetPlottable<QCPColorMap>(custom_plot.get()); - ASSERT_TRUE(color_map != nullptr); - EXPECT_EQ(color_map->data()->keySize(), nx); - EXPECT_EQ(color_map->data()->valueSize(), ny); - EXPECT_EQ(color_map->data()->cell(0, 0), 1.0); - EXPECT_EQ(color_map->data()->cell(nx - 1, ny - 1), 6.0); - - // checking interpolation flag - EXPECT_TRUE(color_map->interpolate()); -} - -//! Setting data to graph after. - -TEST_F(ColorMapPlotControllerTest, setDataAfter) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - ColorMapPlotController controller(custom_plot.get()); - - SessionModel model; - auto colormap_item = model.insertItem<ColorMapItem>(); - - controller.setItem(colormap_item); - - // without data QCustomPlot has QCPColorMap without default settings - EXPECT_EQ(custom_plot->plottableCount(), 1); - auto color_map = TestUtils::GetPlottable<QCPColorMap>(custom_plot.get()); - ASSERT_TRUE(color_map != nullptr); - const int qcpmap_internal_default(10); - EXPECT_EQ(color_map->data()->keySize(), qcpmap_internal_default); - EXPECT_EQ(color_map->data()->valueSize(), qcpmap_internal_default); - - // setup Data2DItem and assign to ColorMapItem - auto data_item = model.insertItem<Data2DItem>(); - const int nx = 3, ny = 2; - data_item->setAxes(FixedBinAxisItem::create(nx, 0.0, 3.0), - FixedBinAxisItem::create(ny, 0.0, 2.0)); - std::vector<double> expected = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0}; - data_item->setContent(expected); - - colormap_item->setDataItem(data_item); - - // colormap should get the shape of Data2DItem - EXPECT_EQ(color_map->data()->keySize(), nx); - EXPECT_EQ(color_map->data()->valueSize(), ny); - EXPECT_EQ(color_map->data()->cell(0, 0), 1.0); - EXPECT_EQ(color_map->data()->cell(nx - 1, ny - 1), 6.0); -} - -//! Unlinking from Data2DItem or ColorMapItem. - -TEST_F(ColorMapPlotControllerTest, unlinkFromItem) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - ColorMapPlotController controller(custom_plot.get()); - - // setup model and single data item in it - SessionModel model; - auto data_item = model.insertItem<Data2DItem>(); - const int nx = 3, ny = 2; - data_item->setAxes(FixedBinAxisItem::create(nx, 0.0, 3.0), - FixedBinAxisItem::create(ny, 0.0, 2.0)); - std::vector<double> expected = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0}; - data_item->setContent(expected); - - // creating colormap item - auto colormap_item = model.insertItem<ColorMapItem>(); - colormap_item->setDataItem(data_item); - - controller.setItem(colormap_item); - - auto color_map = TestUtils::GetPlottable<QCPColorMap>(custom_plot.get()); - EXPECT_EQ(color_map->data()->keySize(), nx); - EXPECT_EQ(color_map->data()->valueSize(), ny); - - // unlinking from data item - colormap_item->setDataItem(nullptr); - - EXPECT_EQ(custom_plot->plottableCount(), 1); - - // QCPColorMap should be there, but its shapre should be (0,0) - EXPECT_EQ(color_map->data()->keySize(), 0); - EXPECT_EQ(color_map->data()->valueSize(), 0); - - // unlinking from ColorMapItem leave QCPColorMap intact - controller.setItem(nullptr); - EXPECT_EQ(custom_plot->plottableCount(), 1); - EXPECT_EQ(color_map->data()->keySize(), 0); - EXPECT_EQ(color_map->data()->valueSize(), 0); -} - -//! Deletion of controller should lead to graph removal. - -TEST_F(ColorMapPlotControllerTest, controllerDelete) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - auto controller = std::make_unique<ColorMapPlotController>(custom_plot.get()); - - // setup model and single data item in it - SessionModel model; - auto data_item = model.insertItem<Data2DItem>(); - - // setup graph item - auto colormap_item = model.insertItem<ColorMapItem>(); - colormap_item->setDataItem(data_item); - - // initializing controller - controller->setItem(colormap_item); - EXPECT_EQ(custom_plot->plottableCount(), 1); - - // deleting controller should lead to QCPColorMap removal - controller.reset(); - EXPECT_EQ(custom_plot->plottableCount(), 0); -} - -//! Deletion of controller should lead to graph removal. - -TEST_F(ColorMapPlotControllerTest, setGradient) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - auto controller = std::make_unique<ColorMapPlotController>(custom_plot.get()); - - // setup model and single data item in it - SessionModel model; - auto data_item = model.insertItem<Data2DItem>(); - - // creating colormap item - auto colormap_item = model.insertItem<ColorMapItem>(); - colormap_item->setDataItem(data_item); - - controller->setItem(colormap_item); - - auto color_map = TestUtils::GetPlottable<QCPColorMap>(custom_plot.get()); - - EXPECT_EQ(color_map->gradient(), QCPColorGradient::gpPolar); - - auto combo = colormap_item->property<ComboProperty>(ColorMapItem::P_GRADIENT); - combo.setValue("Hot"); - colormap_item->setProperty(ColorMapItem::P_GRADIENT, combo); - EXPECT_EQ(color_map->gradient(), QCPColorGradient::gpHot); -} diff --git a/mvvm/tests/testview/colormapviewportplotcontroller.test.cpp b/mvvm/tests/testview/colormapviewportplotcontroller.test.cpp deleted file mode 100644 index 7bb0891f8f0..00000000000 --- a/mvvm/tests/testview/colormapviewportplotcontroller.test.cpp +++ /dev/null @@ -1,142 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testview/colormapviewportplotcontroller.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "customplot_test_utils.h" -#include "google_test.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/plotting/colormapviewportplotcontroller.h" -#include "mvvm/standarditems/axisitems.h" -#include "mvvm/standarditems/colormapitem.h" -#include "mvvm/standarditems/colormapviewportitem.h" -#include "mvvm/standarditems/data1ditem.h" -#include "mvvm/standarditems/data2ditem.h" -#include "qcustomplot.h" -#include <qcustomplot.h> - -using namespace ModelView; - -//! Testing ColorMapViewportPlotController. - -class ColorMapViewportPlotControllerTest : public ::testing::Test { -public: - ~ColorMapViewportPlotControllerTest(); -}; - -ColorMapViewportPlotControllerTest::~ColorMapViewportPlotControllerTest() = default; - -//! Initial state. - -TEST_F(ColorMapViewportPlotControllerTest, initialState) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - ColorMapViewportPlotController controller(custom_plot.get()); - EXPECT_EQ(controller.currentItem(), nullptr); - EXPECT_TRUE(TestUtils::GetPlottable<QCPColorMap>(custom_plot.get()) != nullptr); - - const double customplot_default_lower(0.0), customplot_default_upper(5.0); - EXPECT_DOUBLE_EQ(custom_plot->xAxis->range().lower, customplot_default_lower); - EXPECT_DOUBLE_EQ(custom_plot->xAxis->range().upper, customplot_default_upper); - EXPECT_DOUBLE_EQ(custom_plot->yAxis->range().lower, customplot_default_lower); - EXPECT_DOUBLE_EQ(custom_plot->yAxis->range().upper, customplot_default_upper); -} - -//! Check ::setItem() method when no colormaps exist. - -TEST_F(ColorMapViewportPlotControllerTest, setEmptyViewport) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - ColorMapViewportPlotController controller(custom_plot.get()); - - SessionModel model; - auto viewport_item = model.insertItem<ColorMapViewportItem>(); - - controller.setItem(viewport_item); - - auto color_map = TestUtils::GetPlottable<QCPColorMap>(custom_plot.get()); - EXPECT_TRUE(color_map != nullptr); - - const double default_lower(0.0), default_upper(1.0); - EXPECT_DOUBLE_EQ(custom_plot->xAxis->range().lower, default_lower); - EXPECT_DOUBLE_EQ(custom_plot->xAxis->range().upper, default_upper); - EXPECT_DOUBLE_EQ(custom_plot->yAxis->range().lower, default_lower); - EXPECT_DOUBLE_EQ(custom_plot->yAxis->range().upper, default_upper); - - const int qcpmap_internal_default(10); - EXPECT_EQ(color_map->data()->keySize(), qcpmap_internal_default); - EXPECT_EQ(color_map->data()->valueSize(), qcpmap_internal_default); -} - -//! Check ::setItem() method when data 2d is fully set up. - -TEST_F(ColorMapViewportPlotControllerTest, setItem) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - ColorMapViewportPlotController controller(custom_plot.get()); - - SessionModel model; - auto data_item = model.insertItem<Data2DItem>(); - const int nx = 3, ny = 2; - data_item->setAxes(FixedBinAxisItem::create(nx, 0.0, 3.0), - FixedBinAxisItem::create(ny, 0.0, 2.0)); - std::vector<double> expected = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0}; - data_item->setContent(expected); - - // creating colormap item - auto viewport_item = model.insertItem<ColorMapViewportItem>(); - auto colormap_item = model.insertItem<ColorMapItem>(viewport_item); - colormap_item->setDataItem(data_item); - - // setting up the controller - controller.setItem(viewport_item); - - auto color_map = TestUtils::GetPlottable<QCPColorMap>(custom_plot.get()); - EXPECT_EQ(color_map->data()->keySize(), nx); - EXPECT_EQ(color_map->data()->valueSize(), ny); - EXPECT_EQ(color_map->data()->cell(0, 0), 1.0); - EXPECT_EQ(color_map->data()->cell(nx - 1, ny - 1), 6.0); -} - -//! Consequitive setup. - -TEST_F(ColorMapViewportPlotControllerTest, setupConsequitive) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - ColorMapViewportPlotController controller(custom_plot.get()); - - SessionModel model; - auto viewport_item = model.insertItem<ColorMapViewportItem>(); - - controller.setItem(viewport_item); - - auto color_map = TestUtils::GetPlottable<QCPColorMap>(custom_plot.get()); - EXPECT_TRUE(color_map != nullptr); - - // setting up data - auto data_item = model.insertItem<Data2DItem>(); - const int nx = 3, ny = 2; - data_item->setAxes(FixedBinAxisItem::create(nx, 0.0, 3.0), - FixedBinAxisItem::create(ny, 0.0, 2.0)); - std::vector<double> expected = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0}; - data_item->setContent(expected); - - // creating colormap item - auto colormap_item = model.insertItem<ColorMapItem>(viewport_item); - colormap_item->setDataItem(data_item); - - // checking that QCPColorMap has good shape - EXPECT_EQ(color_map->data()->keySize(), nx); - EXPECT_EQ(color_map->data()->valueSize(), ny); - EXPECT_EQ(color_map->data()->cell(0, 0), 1.0); - EXPECT_EQ(color_map->data()->cell(nx - 1, ny - 1), 6.0); -} diff --git a/mvvm/tests/testview/customplot_test_utils.cpp b/mvvm/tests/testview/customplot_test_utils.cpp deleted file mode 100644 index b4afc717bcc..00000000000 --- a/mvvm/tests/testview/customplot_test_utils.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testview/customplot_test_utils.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "customplot_test_utils.h" -#include <qcustomplot.h> - -std::vector<double> TestUtils::binCenters(const QCPGraph* graph) -{ - return get_values(graph, [](auto x) { return x.key; }); -} - -std::vector<double> TestUtils::binValues(const QCPGraph* graph) -{ - return get_values(graph, [](auto x) { return x.value; }); -} - -std::vector<double> TestUtils::binErrors(const QCPGraph* graph) -{ - std::vector<double> result; - if (auto errorBars = GetPlottable<QCPErrorBars>(graph->parentPlot()); errorBars) { - auto container = errorBars->data(); - std::transform(container->begin(), container->end(), std::back_inserter(result), - [](auto x) { return x.errorPlus; }); - }; - return result; -} diff --git a/mvvm/tests/testview/customplot_test_utils.h b/mvvm/tests/testview/customplot_test_utils.h deleted file mode 100644 index e2e9dbaa068..00000000000 --- a/mvvm/tests/testview/customplot_test_utils.h +++ /dev/null @@ -1,59 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testview/customplot_test_utils.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_TESTS_TESTVIEW_CUSTOMPLOT_TEST_UTILS_H -#define BORNAGAIN_MVVM_TESTS_TESTVIEW_CUSTOMPLOT_TEST_UTILS_H - -#include <algorithm> -#include <qcustomplot.h> -#include <vector> - -//! Various common utils for unit tests. - -namespace TestUtils { - -//! Returns vector representing bin centers/values on QCPGraph. - -template <typename G, typename T> std::vector<double> get_values(const G* graph, T operand) -{ - std::vector<double> result; - auto graph_data = *graph->data(); - std::transform(std::begin(graph_data), std::end(graph_data), std::back_inserter(result), - [operand](const auto& point) { return operand(point); }); - return result; -} - -//! Returns vector representing bin centers on QCPgraph. -std::vector<double> binCenters(const QCPGraph* graph); - -//! Returns vector representing y-values on QCPgraph. -std::vector<double> binValues(const QCPGraph* graph); - -//! Returns vector representing bin errors of QCPGraph. -std::vector<double> binErrors(const QCPGraph* graph); - -//! Finds and returns specific plottable in QCustomPlot canvas. -template <typename T> T* GetPlottable(QCustomPlot* custom_plot) -{ - for (int i = 0; i < custom_plot->plottableCount(); ++i) { - if (auto plottable = dynamic_cast<T*>(custom_plot->plottable()); plottable) - return plottable; - } - return nullptr; -} -} // namespace TestUtils - -Q_DECLARE_METATYPE(QCPRange) - -#endif // BORNAGAIN_MVVM_TESTS_TESTVIEW_CUSTOMPLOT_TEST_UTILS_H diff --git a/mvvm/tests/testview/customplot_test_utils.test.cpp b/mvvm/tests/testview/customplot_test_utils.test.cpp deleted file mode 100644 index 29846682f05..00000000000 --- a/mvvm/tests/testview/customplot_test_utils.test.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testview/customplot_test_utils.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "customplot_test_utils.h" -#include "google_test.h" -#include "qcustomplot.h" - -using namespace ModelView; - -//! Testing "utilr for testing" defined in TestUtils namespace. - -class CustomplotTestUtilsTest : public ::testing::Test { -public: - ~CustomplotTestUtilsTest(); -}; - -CustomplotTestUtilsTest::~CustomplotTestUtilsTest() = default; - -//! Check methods to access graph bin centers and values. - -TEST_F(CustomplotTestUtilsTest, binCentersbinValues) -{ - QCustomPlot custom_plot; - - auto graph = custom_plot.addGraph(); - graph->setData(QVector<double>({1, 2, 3}), QVector<double>({10, 20, 30})); - - EXPECT_EQ(TestUtils::binCenters(graph), std::vector<double>({1, 2, 3})); - EXPECT_EQ(TestUtils::binValues(graph), std::vector<double>({10, 20, 30})); -} - -//! Check methods to access graph errors. - -TEST_F(CustomplotTestUtilsTest, binErrors) -{ - QCustomPlot custom_plot; - - auto graph = custom_plot.addGraph(); - graph->setData(QVector<double>({1, 2, 3}), QVector<double>({10, 20, 30})); - - EXPECT_EQ(TestUtils::binErrors(graph), std::vector<double>()); - - QCPErrorBars* errorBars = new QCPErrorBars(custom_plot.xAxis, custom_plot.yAxis); - errorBars->removeFromLegend(); - errorBars->setDataPlottable(graph); - errorBars->setData(QVector<double>({0.1, 0.2, 0.3})); - - EXPECT_EQ(TestUtils::binErrors(graph), std::vector<double>({0.1, 0.2, 0.3})); -} diff --git a/mvvm/tests/testview/customplotsceneadapter.test.cpp b/mvvm/tests/testview/customplotsceneadapter.test.cpp deleted file mode 100644 index 94ff73b6a38..00000000000 --- a/mvvm/tests/testview/customplotsceneadapter.test.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testview/customplotsceneadapter.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/plotting/customplotsceneadapter.h" -#include "qcustomplot.h" - -using namespace ModelView; - -//! Testing CustomPlotSceneAdapter. - -class CustomPlotSceneAdapterTest : public ::testing::Test { -public: - ~CustomPlotSceneAdapterTest(); -}; - -CustomPlotSceneAdapterTest::~CustomPlotSceneAdapterTest() = default; - -//! Scenario when QCustomPlot destroyed before adapter. - -TEST_F(CustomPlotSceneAdapterTest, customPlotBeforeAdapter) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - auto adapter = std::make_unique<CustomPlotSceneAdapter>(custom_plot.get()); - - // destroying QCustomPlot - custom_plot.reset(); - - EXPECT_EQ(42, adapter->toSceneX(42)); - EXPECT_EQ(42, adapter->toSceneY(42)); - EXPECT_EQ(42, adapter->fromSceneX(42)); - EXPECT_EQ(42, adapter->fromSceneY(42)); -} - -TEST_F(CustomPlotSceneAdapterTest, adapterBeforeCustomPlot) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - auto adapter = std::make_unique<CustomPlotSceneAdapter>(custom_plot.get()); - - adapter.reset(); - custom_plot.reset(); // would cause SEGFAULT if connection to adapter still exists -} diff --git a/mvvm/tests/testview/data1dplotcontroller.test.cpp b/mvvm/tests/testview/data1dplotcontroller.test.cpp deleted file mode 100644 index 1995c70f3ac..00000000000 --- a/mvvm/tests/testview/data1dplotcontroller.test.cpp +++ /dev/null @@ -1,181 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testview/data1dplotcontroller.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "customplot_test_utils.h" -#include "google_test.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/plotting/data1dplotcontroller.h" -#include "mvvm/standarditems/axisitems.h" -#include "mvvm/standarditems/data1ditem.h" -#include "qcustomplot.h" -#include <algorithm> -#include <stdexcept> - -using namespace ModelView; - -//! Testing Data1DPlotController. - -class Data1DPlotControllerTest : public ::testing::Test { -public: - ~Data1DPlotControllerTest(); -}; - -Data1DPlotControllerTest::~Data1DPlotControllerTest() = default; - -//! Initial state. - -TEST_F(Data1DPlotControllerTest, initialState) -{ - // Constructor accept valid QCPGraph - EXPECT_THROW(Data1DPlotController(nullptr), std::runtime_error); - - auto custom_plot = std::make_unique<QCustomPlot>(); - auto graph = custom_plot->addGraph(); - - Data1DPlotController controller(graph); - EXPECT_EQ(controller.currentItem(), nullptr); - - // no points have been added to graph - EXPECT_EQ(std::vector<double>(), TestUtils::binCenters(graph)); - EXPECT_EQ(std::vector<double>(), TestUtils::binValues(graph)); -} - -//! Testing controller when Data1DItem is not initialized properly. - -TEST_F(Data1DPlotControllerTest, dataItemInInitialState) -{ - // creating custom plot and empty graph on it - auto custom_plot = std::make_unique<QCustomPlot>(); - auto graph = custom_plot->addGraph(); - - // creating data item with single point - SessionModel model; - auto data_item = model.insertItem<Data1DItem>(); - - // creating controller and point it to Data1DItem - Data1DPlotController controller(graph); - controller.setItem(data_item); - - EXPECT_EQ(std::vector<double>(), TestUtils::binCenters(graph)); - EXPECT_EQ(std::vector<double>(), TestUtils::binValues(graph)); - EXPECT_EQ(std::vector<double>(), TestUtils::binErrors(graph)); -} - -//! Testing controller when Data1DItem get it's axis after controller setup. - -TEST_F(Data1DPlotControllerTest, axisAfter) -{ - // creating custom plot and empty graph on it - auto custom_plot = std::make_unique<QCustomPlot>(); - auto graph = custom_plot->addGraph(); - - // creating data item with single point - SessionModel model; - auto data_item = model.insertItem<Data1DItem>(); - - // creating controller and point it to Data1DItem - Data1DPlotController controller(graph); - controller.setItem(data_item); - - // setting correct axis - data_item->setAxis<FixedBinAxisItem>(1, 1.0, 2.0); - EXPECT_EQ(data_item->binCenters(), TestUtils::binCenters(graph)); - EXPECT_EQ(data_item->binValues(), TestUtils::binValues(graph)); - EXPECT_EQ(std::vector<double>(), TestUtils::binErrors(graph)); -} - -//! Testing graph points update. - -TEST_F(Data1DPlotControllerTest, dataPoints) -{ - // creating custom plot and empty graph on it - auto custom_plot = std::make_unique<QCustomPlot>(); - auto graph = custom_plot->addGraph(); - - // creating data item with single point - SessionModel model; - auto data_item = model.insertItem<Data1DItem>(); - data_item->setAxis<FixedBinAxisItem>(1, 1.0, 2.0); - - // creating controller and point it to Data1DItem - Data1DPlotController controller(graph); - controller.setItem(data_item); - - // checking that QCPGraph now has data points as in Data1DItem - EXPECT_EQ(data_item->binCenters(), TestUtils::binCenters(graph)); - EXPECT_EQ(data_item->binValues(), TestUtils::binValues(graph)); - EXPECT_EQ(data_item->binErrors(), TestUtils::binErrors(graph)); - - // Setting item to nullptr. Current convention is that graph stays intact, but points disappear. - controller.setItem(nullptr); - EXPECT_EQ(std::vector<double>(), TestUtils::binCenters(graph)); - EXPECT_EQ(std::vector<double>(), TestUtils::binValues(graph)); - EXPECT_EQ(std::vector<double>(), TestUtils::binErrors(graph)); -} - -//! Testing graph errors update. - -TEST_F(Data1DPlotControllerTest, errorBars) -{ - // creating custom plot and empty graph on it - auto custom_plot = std::make_unique<QCustomPlot>(); - auto graph = custom_plot->addGraph(); - - // creating data item with single point - SessionModel model; - auto data_item = model.insertItem<Data1DItem>(); - data_item->setAxis<FixedBinAxisItem>(2, 1.0, 2.0); - - // creating controller and point it to Data1DItem - Data1DPlotController controller(graph); - controller.setItem(data_item); - - std::vector<double> expected_errors = {0.1, 0.2}; - data_item->setErrors(expected_errors); - EXPECT_EQ(TestUtils::binErrors(graph), expected_errors); - - // setting new errors - expected_errors = {0.3, 0.4}; - data_item->setErrors(expected_errors); - EXPECT_EQ(TestUtils::binErrors(graph), expected_errors); -} - -//! Testing two graph scenario. - -TEST_F(Data1DPlotControllerTest, twoDataItems) -{ - // creating custom plot and empty graph on it - auto custom_plot = std::make_unique<QCustomPlot>(); - auto graph = custom_plot->addGraph(); - - // creating two data items - SessionModel model; - auto data_item1 = model.insertItem<Data1DItem>(); - data_item1->setAxis<FixedBinAxisItem>(1, 1.0, 2.0); - auto data_item2 = model.insertItem<Data1DItem>(); - data_item2->setAxis<FixedBinAxisItem>(2, 0.0, 2.0); - - // creating controller and point it to first item - Data1DPlotController controller(graph); - controller.setItem(data_item1); - - // checking that QCPGraph now has data points as in first data item - EXPECT_EQ(data_item1->binCenters(), TestUtils::binCenters(graph)); - EXPECT_EQ(data_item1->binValues(), TestUtils::binValues(graph)); - - // pointing controller to the second item - controller.setItem(data_item2); - EXPECT_EQ(data_item2->binCenters(), TestUtils::binCenters(graph)); - EXPECT_EQ(data_item2->binValues(), TestUtils::binValues(graph)); -} diff --git a/mvvm/tests/testview/data2dplotcontroller.test.cpp b/mvvm/tests/testview/data2dplotcontroller.test.cpp deleted file mode 100644 index 26648489ca3..00000000000 --- a/mvvm/tests/testview/data2dplotcontroller.test.cpp +++ /dev/null @@ -1,207 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testview/data2dplotcontroller.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "customplot_test_utils.h" -#include "google_test.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/plotting/data2dplotcontroller.h" -#include "mvvm/standarditems/axisitems.h" -#include "mvvm/standarditems/data2ditem.h" -#include "qcustomplot.h" -#include <QSignalSpy> -#include <stdexcept> - -using namespace ModelView; - -//! Testing Data1DPlotController. - -class Data2DPlotControllerTest : public ::testing::Test { -public: - ~Data2DPlotControllerTest(); -}; - -Data2DPlotControllerTest::~Data2DPlotControllerTest() = default; - -//! Initial state. - -TEST_F(Data2DPlotControllerTest, initialState) -{ - // Constructor accept valid QCPColorMap - EXPECT_THROW(Data2DPlotController(nullptr), std::runtime_error); - - auto custom_plot = std::make_unique<QCustomPlot>(); - auto color_map = new QCPColorMap(custom_plot->xAxis, custom_plot->yAxis); - color_map->data()->clear(); // to remove default values defined in QCPColorMap - - Data2DPlotController controller(color_map); - EXPECT_EQ(controller.currentItem(), nullptr); - - EXPECT_EQ(color_map->data()->keySize(), 0); - EXPECT_EQ(color_map->data()->valueSize(), 0); -} - -//! Testing controller when Data2DItem is not initialized properly. - -TEST_F(Data2DPlotControllerTest, dataItemInInitialState) -{ - // creating custom plot and empty graph on it - auto custom_plot = std::make_unique<QCustomPlot>(); - auto color_map = new QCPColorMap(custom_plot->xAxis, custom_plot->yAxis); - - // creating data item with single point - SessionModel model; - auto data_item = model.insertItem<Data2DItem>(); - - // creating controller and point it to Data2DItem - Data2DPlotController controller(color_map); - EXPECT_NO_THROW(controller.setItem(data_item)); - - // Since data item doesn't contain axes defined, should be no points on colormap. - EXPECT_EQ(color_map->data()->keySize(), 0); - EXPECT_EQ(color_map->data()->valueSize(), 0); -} - -//! Testing controller when Data2DItem got it's axes after controller was set. - -TEST_F(Data2DPlotControllerTest, setAxesAfter) -{ - // creating custom plot and empty graph on it - auto custom_plot = std::make_unique<QCustomPlot>(); - auto color_map = new QCPColorMap(custom_plot->xAxis, custom_plot->yAxis); - - // creating data item with single point - SessionModel model; - auto data_item = model.insertItem<Data2DItem>(); - - // creating controller and point it to Data2DItem - Data2DPlotController controller(color_map); - EXPECT_NO_THROW(controller.setItem(data_item)); - - // setting axes after - const int nx = 3, ny = 2; - data_item->setAxes(FixedBinAxisItem::create(nx, 0.0, 3.0), - FixedBinAxisItem::create(ny, 0.0, 2.0)); - - // color map should get shape of axes - EXPECT_EQ(color_map->data()->keySize(), nx); - EXPECT_EQ(color_map->data()->valueSize(), ny); - EXPECT_EQ(color_map->data()->cell(0, 0), 0.0); - EXPECT_EQ(color_map->data()->cell(nx - 1, ny - 1), 0.0); -} - -//! Testing data points. - -TEST_F(Data2DPlotControllerTest, dataPoints) -{ - // creating custom plot and empty graph on it - auto custom_plot = std::make_unique<QCustomPlot>(); - auto color_map = new QCPColorMap(custom_plot->xAxis, custom_plot->yAxis); - - // creating data item with single point - SessionModel model; - auto data_item = model.insertItem<Data2DItem>(); - const int nx = 3, ny = 2; - data_item->setAxes(FixedBinAxisItem::create(nx, 0.0, 3.0), - FixedBinAxisItem::create(ny, 0.0, 2.0)); - - // creating controller and point it to Data2DItem - Data2DPlotController controller(color_map); - controller.setItem(data_item); - - EXPECT_EQ(color_map->data()->keySize(), nx); - EXPECT_EQ(color_map->data()->valueSize(), ny); - EXPECT_EQ(color_map->data()->cell(0, 0), 0.0); - EXPECT_EQ(color_map->data()->cell(nx - 1, ny - 1), 0.0); - - // setting data points - std::vector<double> expected = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0}; - data_item->setContent(expected); - EXPECT_EQ(color_map->data()->cell(0, 0), 1.0); - EXPECT_EQ(color_map->data()->cell(nx - 1, ny - 1), 6.0); - - // Setting item to nullptr. Current convention is that QCPColorMap loses its data. - controller.setItem(nullptr); - EXPECT_EQ(color_map->data()->keySize(), 0); - EXPECT_EQ(color_map->data()->valueSize(), 0); -} - -//! Testing two colormap scenario. - -TEST_F(Data2DPlotControllerTest, twoDataItems) -{ - // creating custom plot and empty graph on it - auto custom_plot = std::make_unique<QCustomPlot>(); - auto color_map = new QCPColorMap(custom_plot->xAxis, custom_plot->yAxis); - - // creating data item with single point - SessionModel model; - auto data_item1 = model.insertItem<Data2DItem>(); - const int nx1 = 3, ny1 = 2; - std::vector<double> expected1 = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0}; - data_item1->setAxes(FixedBinAxisItem::create(nx1, 0.0, 3.0), - FixedBinAxisItem::create(ny1, 0.0, 2.0)); - data_item1->setContent(expected1); - auto data_item2 = model.insertItem<Data2DItem>(); - const int nx2 = 2, ny2 = 1; - std::vector<double> expected2 = {10.0, 20.0}; - data_item2->setAxes(FixedBinAxisItem::create(nx2, 0.0, 3.0), - FixedBinAxisItem::create(ny2, 0.0, 2.0)); - data_item2->setContent(expected2); - - // creating controller and point it to first Data2DItem - Data2DPlotController controller(color_map); - controller.setItem(data_item1); - - EXPECT_EQ(color_map->data()->keySize(), nx1); - EXPECT_EQ(color_map->data()->valueSize(), ny1); - EXPECT_EQ(color_map->data()->cell(0, 0), 1.0); - EXPECT_EQ(color_map->data()->cell(nx1 - 1, ny1 - 1), 6.0); - - // pointing controller to the second Data2DItem - controller.setItem(data_item2); - - EXPECT_EQ(color_map->data()->keySize(), nx2); - EXPECT_EQ(color_map->data()->valueSize(), ny2); - EXPECT_EQ(color_map->data()->cell(0, 0), 10.0); - EXPECT_EQ(color_map->data()->cell(nx2 - 1, ny2 - 1), 20.0); -} - -//! Testing data range. - -TEST_F(Data2DPlotControllerTest, dataRange) -{ - // creating custom plot and empty graph on it - auto custom_plot = std::make_unique<QCustomPlot>(); - auto color_map = new QCPColorMap(custom_plot->xAxis, custom_plot->yAxis); - - // creating data item with single point - SessionModel model; - auto data_item = model.insertItem<Data2DItem>(); - const int nx = 3, ny = 2; - data_item->setAxes(FixedBinAxisItem::create(nx, 0.0, 3.0), - FixedBinAxisItem::create(ny, 0.0, 2.0)); - std::vector<double> expected = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0}; - data_item->setContent(expected); - - // creating controller and point it to Data2DItem - Data2DPlotController controller(color_map); - controller.setItem(data_item); - - QSignalSpy spy(color_map, &QCPColorMap::dataRangeChanged); - - auto range = color_map->dataRange(); - EXPECT_EQ(spy.count(), 0); - EXPECT_EQ(range.lower, 1.0); - EXPECT_EQ(range.upper, 6.0); -} diff --git a/mvvm/tests/testview/graphplotcontroller.test.cpp b/mvvm/tests/testview/graphplotcontroller.test.cpp deleted file mode 100644 index cf3d9b023fc..00000000000 --- a/mvvm/tests/testview/graphplotcontroller.test.cpp +++ /dev/null @@ -1,267 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testview/graphplotcontroller.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "customplot_test_utils.h" -#include "google_test.h" -#include "mvvm/model/comboproperty.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/plotting/graphplotcontroller.h" -#include "mvvm/standarditems/axisitems.h" -#include "mvvm/standarditems/data1ditem.h" -#include "mvvm/standarditems/graphitem.h" -#include "mvvm/standarditems/plottableitems.h" -#include "qcustomplot.h" -#include <QSignalSpy> - -using namespace ModelView; - -//! Testing GraphPlotController. - -class GraphPlotControllerTest : public ::testing::Test { -public: - ~GraphPlotControllerTest(); -}; - -GraphPlotControllerTest::~GraphPlotControllerTest() = default; - -//! Initial state. - -TEST_F(GraphPlotControllerTest, initialState) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - GraphPlotController controller(custom_plot.get()); - EXPECT_EQ(controller.currentItem(), nullptr); - EXPECT_EQ(custom_plot->graphCount(), 0); -} - -//! Setting GraphItem with data and checking that plottable contains correct data. - -TEST_F(GraphPlotControllerTest, setItem) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - GraphPlotController controller(custom_plot.get()); - - // setup model and single data item in it - SessionModel model; - auto data_item = model.insertItem<Data1DItem>(); - data_item->setAxis<FixedBinAxisItem>(2, 0.0, 2.0); - std::vector<double> expected_centers = {0.5, 1.5}; - std::vector<double> expected_values = {42.0, 43.0}; - data_item->setValues(expected_values); - - // setup graph item - auto graph_item = model.insertItem<GraphItem>(); - graph_item->setDataItem(data_item); - - // initializing controller - controller.setItem(graph_item); - - // Checking resulting plottables - EXPECT_EQ(custom_plot->graphCount(), 1); - auto graph = custom_plot->graph(); - EXPECT_EQ(TestUtils::binCenters(graph), expected_centers); - EXPECT_EQ(TestUtils::binValues(graph), expected_values); - EXPECT_EQ(graph->pen().color(), QColor(Qt::black)); - EXPECT_EQ(graph->pen().style(), Qt::SolidLine); - EXPECT_EQ(graph->pen().width(), 1); -} - -TEST_F(GraphPlotControllerTest, changeGraphAppearance) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - GraphPlotController controller(custom_plot.get()); - - // setup model and single data item in it - SessionModel model; - auto data_item = model.insertItem<Data1DItem>(); - data_item->setAxis<FixedBinAxisItem>(2, 0.0, 2.0); - std::vector<double> expected_centers = {0.5, 1.5}; - std::vector<double> expected_values = {42.0, 43.0}; - data_item->setValues(expected_values); - - // setup graph item - auto graph_item = model.insertItem<GraphItem>(); - graph_item->setDataItem(data_item); - - // initializing controller - controller.setItem(graph_item); - - // changing appearance properties - auto pen_item = graph_item->penItem(); - pen_item->setProperty(PenItem::P_COLOR, QColor(Qt::red)); - - auto styleCombo = pen_item->property<ComboProperty>(PenItem::P_STYLE); - styleCombo.setCurrentIndex(2); - pen_item->setProperty(PenItem::P_STYLE, styleCombo); - pen_item->setProperty(PenItem::P_WIDTH, 2); - - auto graph = custom_plot->graph(); - EXPECT_EQ(graph->pen().color(), QColor(Qt::red)); - EXPECT_EQ(graph->pen().style(), Qt::DashLine); - EXPECT_EQ(graph->pen().width(), 2); -} - -//! Setting GraphItem with data and checking that plottable contains correct data. -//! Same as aboe, except that the data is based on PointWiseAxis. - -TEST_F(GraphPlotControllerTest, setPointwiseItem) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - GraphPlotController controller(custom_plot.get()); - - // setup model and single data item in it - const std::vector<double> expected_centers = {1.0, 2.0, 3.0}; - const std::vector<double> expected_values = {42.0, 43.0, 44.0}; - - SessionModel model; - auto data_item = model.insertItem<Data1DItem>(); - data_item->setAxis<PointwiseAxisItem>(expected_centers); - data_item->setValues(expected_values); - - // setup graph item - auto graph_item = model.insertItem<GraphItem>(); - auto pen_item = graph_item->penItem(); - pen_item->setProperty(PenItem::P_COLOR, QColor(Qt::red)); - graph_item->setDataItem(data_item); - - // initializing controller - controller.setItem(graph_item); - - // Checking resulting plottables - EXPECT_EQ(custom_plot->graphCount(), 1); - auto graph = custom_plot->graph(); - EXPECT_EQ(TestUtils::binCenters(graph), expected_centers); - EXPECT_EQ(TestUtils::binValues(graph), expected_values); - EXPECT_EQ(graph->pen().color(), QColor(Qt::red)); -} - -//! Setting data to graph after. - -TEST_F(GraphPlotControllerTest, setDataAfter) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - GraphPlotController controller(custom_plot.get()); - - SessionModel model; - auto graph_item = model.insertItem<GraphItem>(); - - controller.setItem(graph_item); - - // without data QCustomPlot has a graph without points - EXPECT_EQ(custom_plot->graphCount(), 1); - auto graph = custom_plot->graph(); - EXPECT_EQ(TestUtils::binCenters(graph), std::vector<double>()); - EXPECT_EQ(TestUtils::binValues(graph), std::vector<double>()); - - // setup Data1DItem and assign to GraphItem - auto data_item = model.insertItem<Data1DItem>(); - data_item->setAxis<FixedBinAxisItem>(2, 0.0, 2.0); - std::vector<double> expected_centers = {0.5, 1.5}; - std::vector<double> expected_values = {42.0, 43.0}; - data_item->setValues(expected_values); - - graph_item->setDataItem(data_item); - - // Checking resulting plottables - EXPECT_EQ(custom_plot->graphCount(), 1); - EXPECT_EQ(TestUtils::binCenters(graph), expected_centers); - EXPECT_EQ(TestUtils::binValues(graph), expected_values); -} - -//! Unlinking from Data1DItem or GraphItem. - -TEST_F(GraphPlotControllerTest, unlinkFromItem) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - GraphPlotController controller(custom_plot.get()); - - // setup model and single data item in it - SessionModel model; - auto data_item = model.insertItem<Data1DItem>(); - data_item->setAxis<FixedBinAxisItem>(2, 0.0, 2.0); - std::vector<double> expected_centers = {0.5, 1.5}; - std::vector<double> expected_values = {42.0, 43.0}; - data_item->setValues(expected_values); - - // setup graph item - auto graph_item = model.insertItem<GraphItem>(); - auto pen_item = graph_item->penItem(); - pen_item->setProperty(PenItem::P_COLOR, QColor(Qt::red)); - graph_item->setDataItem(data_item); - - // initializing controller - controller.setItem(graph_item); - - // unlinking from data item - graph_item->setDataItem(nullptr); - - // Checking resulting plottables - // Current convention is that graph stays intact, but points disappear. - EXPECT_EQ(custom_plot->graphCount(), 1); - auto graph = custom_plot->graph(); - EXPECT_EQ(TestUtils::binCenters(graph), std::vector<double>()); - EXPECT_EQ(TestUtils::binValues(graph), std::vector<double>()); - EXPECT_EQ(graph->pen().color(), QColor(Qt::red)); - - // unlinking from graph item should remove Graph from CustomPlot - controller.setItem(nullptr); - EXPECT_EQ(custom_plot->graphCount(), 0); -} - -//! Deletion of controller should lead to graph removal. - -TEST_F(GraphPlotControllerTest, controllerDelete) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - auto controller = std::make_unique<GraphPlotController>(custom_plot.get()); - - // setup model and single data item in it - SessionModel model; - auto data_item = model.insertItem<Data1DItem>(); - - // setup graph item - auto graph_item = model.insertItem<GraphItem>(); - graph_item->setDataItem(data_item); - - // initializing controller - controller->setItem(graph_item); - EXPECT_EQ(custom_plot->graphCount(), 1); - - // deleting controller should lead to graph removal - controller.reset(); - EXPECT_EQ(custom_plot->graphCount(), 0); -} - -//! Deletion of graphItem should lead to the dissapearance of graph. - -TEST_F(GraphPlotControllerTest, graphDelete) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - auto controller = std::make_unique<GraphPlotController>(custom_plot.get()); - - // setup model and single data item in it - SessionModel model; - auto data_item = model.insertItem<Data1DItem>(); - - // setup graph item - auto graph_item = model.insertItem<GraphItem>(); - graph_item->setDataItem(data_item); - - // initializing controller - controller->setItem(graph_item); - EXPECT_EQ(custom_plot->graphCount(), 1); - - model.removeItem(graph_item->parent(), graph_item->tagRow()); - EXPECT_EQ(custom_plot->graphCount(), 0); -} diff --git a/mvvm/tests/testview/graphviewportplotcontroller.test.cpp b/mvvm/tests/testview/graphviewportplotcontroller.test.cpp deleted file mode 100644 index 382348f3998..00000000000 --- a/mvvm/tests/testview/graphviewportplotcontroller.test.cpp +++ /dev/null @@ -1,388 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testview/graphviewportplotcontroller.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "customplot_test_utils.h" -#include "google_test.h" -#include "mvvm/interfaces/undostackinterface.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/plotting/graphviewportplotcontroller.h" -#include "mvvm/standarditems/axisitems.h" -#include "mvvm/standarditems/data1ditem.h" -#include "mvvm/standarditems/graphitem.h" -#include "mvvm/standarditems/graphviewportitem.h" -#include "qcustomplot.h" - -using namespace ModelView; - -//! Testing GraphViewportPlotController. - -class GraphViewportPlotControllerTest : public ::testing::Test { -public: - ~GraphViewportPlotControllerTest(); -}; - -GraphViewportPlotControllerTest::~GraphViewportPlotControllerTest() = default; - -//! Initial state. - -TEST_F(GraphViewportPlotControllerTest, initialState) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - GraphViewportPlotController controller(custom_plot.get()); - EXPECT_EQ(custom_plot->graphCount(), 0); -} - -//! Check ::setItem() method when no graphs exist. - -TEST_F(GraphViewportPlotControllerTest, setItem) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - GraphViewportPlotController controller(custom_plot.get()); - - // setting up controller with viewport item - SessionModel model; - auto item = model.insertItem<GraphViewportItem>(); - controller.setItem(item); - - // no graphs in empty GraphViewportItem - EXPECT_EQ(custom_plot->graphCount(), 0); - - // axis should be [0, 1] as in defaule ViewportAxisItem - EXPECT_DOUBLE_EQ(custom_plot->xAxis->range().lower, 0.0); - EXPECT_DOUBLE_EQ(custom_plot->xAxis->range().upper, 1.0); - EXPECT_DOUBLE_EQ(custom_plot->yAxis->range().lower, 0.0); - EXPECT_DOUBLE_EQ(custom_plot->yAxis->range().upper, 1.0); -} - -//! Check ::setItem() method when ViewPortItem contains graphs. - -TEST_F(GraphViewportPlotControllerTest, addGraphAndSetItem) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - GraphViewportPlotController controller(custom_plot.get()); - - // setting up controller with viewport item - SessionModel model; - auto viewport_item = model.insertItem<GraphViewportItem>(); - - auto data_item = model.insertItem<Data1DItem>(); - const std::vector<double> expected_values = {1.0, 2.0, 3.0}; - const std::vector<double> expected_centers = {0.5, 1.5, 2.5}; - data_item->setAxis<FixedBinAxisItem>(3, 0.0, 3.0); - data_item->setValues(expected_values); - - auto graph_item = model.insertItem<GraphItem>(viewport_item); - graph_item->setDataItem(data_item); - controller.setItem(viewport_item); - - // single graph on custom plot. - EXPECT_EQ(custom_plot->graphCount(), 1); - - // QCustomPlot axis should correspond to - EXPECT_DOUBLE_EQ(custom_plot->xAxis->range().lower, expected_centers[0]); - EXPECT_DOUBLE_EQ(custom_plot->xAxis->range().upper, expected_centers[2]); - EXPECT_DOUBLE_EQ(custom_plot->yAxis->range().lower, expected_values[0]); - EXPECT_DOUBLE_EQ(custom_plot->yAxis->range().upper, expected_values[2]); -} - -//! Checks consequitive graph adding/removal - -TEST_F(GraphViewportPlotControllerTest, addAndRemoveGraphs) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - GraphViewportPlotController controller(custom_plot.get()); - - // setting up controller with viewport item - SessionModel model; - auto viewport_item = model.insertItem<GraphViewportItem>(); - controller.setItem(viewport_item); - - // No graphs yet. - EXPECT_EQ(custom_plot->graphCount(), 0); - - // Populating with data items - auto data1 = model.insertItem<Data1DItem>(); - const std::vector<double> expected_values1 = {1.0, 2.0, 3.0}; - const std::vector<double> expected_centers = {0.5, 1.5, 2.5}; - data1->setAxis<FixedBinAxisItem>(3, 0.0, 3.0); - data1->setValues(expected_values1); - - auto data2 = model.insertItem<Data1DItem>(); - const std::vector<double> expected_values2 = {4.0, 5.0, 6.0}; - data2->setAxis<FixedBinAxisItem>(3, 0.0, 3.0); - data2->setValues(expected_values2); - - // adding graph item to viewport - auto graph_item1 = model.insertItem<GraphItem>(viewport_item, {"", 0}); - - // check that QCustomPlot knows about graph - EXPECT_EQ(custom_plot->graphCount(), 1); - - graph_item1->setDataItem(data1); - - // check that QCustomPlot knows about graph - EXPECT_EQ(custom_plot->graphCount(), 1); - - // adding secong graph - auto graph_item2 = model.insertItem<GraphItem>(viewport_item, {"", 1}); - graph_item2->setDataItem(data2); - - // check that QCustomPlot knows about two graph - EXPECT_EQ(custom_plot->graphCount(), 2); - - // Checking that viewport min, max adjusted to both graphs when manually call update_viewport() - viewport_item->setViewportToContent(); - EXPECT_DOUBLE_EQ(custom_plot->xAxis->range().lower, expected_centers[0]); - EXPECT_DOUBLE_EQ(custom_plot->xAxis->range().upper, expected_centers[2]); - EXPECT_DOUBLE_EQ(custom_plot->yAxis->range().lower, expected_values1[0]); - EXPECT_DOUBLE_EQ(custom_plot->yAxis->range().upper, expected_values2[2]); - - // removing one GraphItem - model.removeItem(viewport_item, {ViewportItem::T_ITEMS, 1}); - - // only single graph should remain on QCustomPlot3 - EXPECT_EQ(custom_plot->graphCount(), 1); -} - -//! Checks consequitive graph adding/removal - -TEST_F(GraphViewportPlotControllerTest, addMoreGraphs) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - GraphViewportPlotController controller(custom_plot.get()); - - // setting up controller with viewport item - SessionModel model; - auto viewport_item = model.insertItem<GraphViewportItem>(); - controller.setItem(viewport_item); - - // No graphs yet. - EXPECT_EQ(custom_plot->graphCount(), 0); - - // adding graph item to viewport - model.insertItem<GraphItem>(viewport_item); - EXPECT_EQ(custom_plot->graphCount(), 1); - - model.insertItem<GraphItem>(viewport_item); - EXPECT_EQ(custom_plot->graphCount(), 2); - - model.insertItem<GraphItem>(viewport_item); - EXPECT_EQ(custom_plot->graphCount(), 3); -} - -//! Checks The fucntionality of selection in the viewport - -TEST_F(GraphViewportPlotControllerTest, checkVisible) -{ - // Convenience - struct FindVisible { - static std::vector<QCPAbstractPlottable*> findVisible(const QCustomPlot* custom_plot) - { - std::vector<QCPAbstractPlottable*> output; - for (int i = 0; i < custom_plot->graphCount(); ++i) { - if (custom_plot->graph(i)->visible()) - output.push_back(custom_plot->graph(i)); - } - return output; - } - }; - - // custom plot setup - auto custom_plot = std::make_unique<QCustomPlot>(); - GraphViewportPlotController controller(custom_plot.get()); - - // setting up controller with viewport item - SessionModel model; - auto viewport_item = model.insertItem<GraphViewportItem>(); - controller.setItem(viewport_item); - - // adding graph item to viewport - auto first_plot = model.insertItem<GraphItem>(viewport_item); - auto second_plot = model.insertItem<GraphItem>(viewport_item); - auto third_plot = model.insertItem<GraphItem>(viewport_item); - EXPECT_EQ(custom_plot->graphCount(), 3); - - viewport_item->setVisible(std::vector<GraphItem*>{first_plot}); - EXPECT_EQ(FindVisible::findVisible(custom_plot.get()).size(), 1); - - viewport_item->setVisible(std::vector<GraphItem*>{second_plot, third_plot}); - EXPECT_EQ(FindVisible::findVisible(custom_plot.get()).size(), 2); - - viewport_item->setAllVisible(); - EXPECT_EQ(FindVisible::findVisible(custom_plot.get()).size(), 3); -} - -//! Two GraphViewportItem's and switch between them. - -TEST_F(GraphViewportPlotControllerTest, switchBetweenTwoViewports) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - GraphViewportPlotController controller(custom_plot.get()); - - // setting up controller with viewport item - SessionModel model; - auto viewport_item0 = model.insertItem<GraphViewportItem>(); - auto viewport_item1 = model.insertItem<GraphViewportItem>(); - - auto data_item = model.insertItem<Data1DItem>(); - const std::vector<double> expected_values = {1.0, 2.0, 3.0}; - const std::vector<double> expected_centers = {0.5, 1.5, 2.5}; - data_item->setAxis<FixedBinAxisItem>(3, 0.0, 3.0); - data_item->setValues(expected_values); - - auto graph_item = model.insertItem<GraphItem>(viewport_item0); - graph_item->setDataItem(data_item); - controller.setItem(viewport_item0); - - // single graph on custom plot. - EXPECT_EQ(custom_plot->graphCount(), 1); - - // switch to second (empty) viewport, QCustomPlot should have no graphs - controller.setItem(viewport_item1); - EXPECT_EQ(custom_plot->graphCount(), 0); -} - -//! Adding graph, then undo, then redo again. - -TEST_F(GraphViewportPlotControllerTest, addGraphUndoRedo) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - GraphViewportPlotController controller(custom_plot.get()); - - // setting up controller with viewport item - SessionModel model; - - auto viewport_item = model.insertItem<GraphViewportItem>(); - controller.setItem(viewport_item); - - // No graphs yet. - EXPECT_EQ(custom_plot->graphCount(), 0); - - // Populating with data items - auto data1 = model.insertItem<Data1DItem>(); - const std::vector<double> expected_values = {1.0, 2.0, 3.0}; - const std::vector<double> expected_centers = {0.5, 1.5, 2.5}; - data1->setAxis<FixedBinAxisItem>(3, 0.0, 3.0); - data1->setValues(expected_values); - - // beginning of our undo/redo story - model.setUndoRedoEnabled(true); - EXPECT_EQ(model.undoStack()->index(), 0); - EXPECT_EQ(model.undoStack()->count(), 0); - - // adding graph item to viewport - auto graph_item = model.insertItem<GraphItem>(viewport_item, {"", 0}); - EXPECT_EQ(model.undoStack()->index(), 1); - EXPECT_EQ(model.undoStack()->count(), 1); - - // assigning data to graph - graph_item->setDataItem(data1); - EXPECT_EQ(model.undoStack()->index(), 2); - EXPECT_EQ(model.undoStack()->count(), 2); - - // validating graph in custom plot - EXPECT_EQ(custom_plot->graphCount(), 1); - EXPECT_EQ(TestUtils::binCenters(custom_plot->graph()), expected_centers); - EXPECT_EQ(TestUtils::binValues(custom_plot->graph()), expected_values); - - // undoing data assignment - model.undoStack()->undo(); - EXPECT_EQ(model.undoStack()->index(), 1); - EXPECT_EQ(model.undoStack()->count(), 2); - - // graph is still there, but empty - EXPECT_EQ(custom_plot->graphCount(), 1); - EXPECT_EQ(TestUtils::binCenters(custom_plot->graph()), std::vector<double>()); - EXPECT_EQ(TestUtils::binValues(custom_plot->graph()), std::vector<double>()); - EXPECT_EQ(graph_item->dataItem(), nullptr); - - // redoing data assignment - model.undoStack()->redo(); - EXPECT_EQ(model.undoStack()->index(), 2); - EXPECT_EQ(model.undoStack()->count(), 2); - - // graph is complete again - EXPECT_EQ(graph_item->dataItem(), data1); - EXPECT_EQ(custom_plot->graphCount(), 1); - EXPECT_EQ(TestUtils::binCenters(custom_plot->graph()), expected_centers); - EXPECT_EQ(TestUtils::binValues(custom_plot->graph()), expected_values); -} - -//! Adding graph together with data in macro, then undo, then redo again. - -TEST_F(GraphViewportPlotControllerTest, addGraphUndoRedoMacro) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - GraphViewportPlotController controller(custom_plot.get()); - - // setting up controller with viewport item - SessionModel model; - - auto viewport_item = model.insertItem<GraphViewportItem>(); - controller.setItem(viewport_item); - - // No graphs yet. - EXPECT_EQ(custom_plot->graphCount(), 0); - - // beginning of our undo/redo story - model.setUndoRedoEnabled(true); - EXPECT_EQ(model.undoStack()->index(), 0); - EXPECT_EQ(model.undoStack()->count(), 0); - - // adding data and graph as macro - model.undoStack()->beginMacro("addGraph"); - // Populating with data items - auto data1 = model.insertItem<Data1DItem>(); - const std::vector<double> expected_values = {1.0, 2.0, 3.0}; - const std::vector<double> expected_centers = {0.5, 1.5, 2.5}; - data1->setAxis<FixedBinAxisItem>(3, 0.0, 3.0); - data1->setValues(expected_values); - auto data_identifier = data1->identifier(); - // adding graph item to viewport - auto graph_item = model.insertItem<GraphItem>(viewport_item, {"", 0}); - // assigning data to graph - graph_item->setDataItem(data1); - model.undoStack()->endMacro(); - - EXPECT_EQ(viewport_item->graphItems()[0]->dataItem(), model.topItem<Data1DItem>()); - - EXPECT_EQ(model.undoStack()->index(), 1); - EXPECT_EQ(model.undoStack()->count(), 1); - - // validating graph in custom plot - EXPECT_EQ(custom_plot->graphCount(), 1); - EXPECT_EQ(TestUtils::binCenters(custom_plot->graph()), expected_centers); - EXPECT_EQ(TestUtils::binValues(custom_plot->graph()), expected_values); - - // undoing macro - model.undoStack()->undo(); - EXPECT_EQ(model.undoStack()->index(), 0); - EXPECT_EQ(model.undoStack()->count(), 1); - - // no graph and no items - EXPECT_EQ(viewport_item->graphItems().size(), 0); - EXPECT_EQ(model.topItem<Data1DItem>(), nullptr); - EXPECT_EQ(custom_plot->graphCount(), 0); - - // redoing macro - model.undoStack()->redo(); - EXPECT_EQ(custom_plot->graphCount(), 1); - EXPECT_EQ(viewport_item->graphItems().size(), 1); - - EXPECT_EQ(model.topItem<Data1DItem>()->identifier(), data_identifier); - EXPECT_EQ(viewport_item->graphItems()[0]->dataItem(), model.topItem<Data1DItem>()); - - EXPECT_EQ(TestUtils::binCenters(custom_plot->graph()), expected_centers); - EXPECT_EQ(TestUtils::binValues(custom_plot->graph()), expected_values); -} diff --git a/mvvm/tests/testview/pencontroller.test.cpp b/mvvm/tests/testview/pencontroller.test.cpp deleted file mode 100644 index b3203981880..00000000000 --- a/mvvm/tests/testview/pencontroller.test.cpp +++ /dev/null @@ -1,109 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testview/pencontroller.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "customplot_test_utils.h" -#include "google_test.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/plotting/pencontroller.h" -#include "mvvm/standarditems/plottableitems.h" -#include "qcustomplot.h" - -#include <stdexcept> - -using namespace ModelView; - -//! Testing PenController. - -class PenControllerTest : public ::testing::Test { -public: - ~PenControllerTest(); -}; - -PenControllerTest::~PenControllerTest() = default; - -//! Initial state. - -TEST_F(PenControllerTest, initialState) -{ - // Constructor accept valid QCPGraph - EXPECT_THROW(PenController(nullptr), std::runtime_error); - - auto custom_plot = std::make_unique<QCustomPlot>(); - auto graph = custom_plot->addGraph(); - - PenController controller(graph); - EXPECT_EQ(controller.currentItem(), nullptr); -} - -TEST_F(PenControllerTest, graphItemInInitialState) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - auto graph = custom_plot->addGraph(); - - PenController controller(graph); - - SessionModel model; - auto pen_item = model.insertItem<PenItem>(); - controller.setItem(pen_item); - - EXPECT_EQ(controller.currentItem(), pen_item); - - // parameters of graph in QCustomPlot - EXPECT_EQ(graph->pen().color(), QColor(Qt::black)); - EXPECT_EQ(graph->pen().style(), Qt::SolidLine); - EXPECT_EQ(graph->pen().width(), 1); -} - -TEST_F(PenControllerTest, setPenSelected) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - auto graph = custom_plot->addGraph(); - - PenController controller(graph); - - SessionModel model; - auto pen_item = model.insertItem<PenItem>(); - controller.setItem(pen_item); - - pen_item->setSelected(true); - - // parameters of graph in QCustomPlot - EXPECT_EQ(graph->pen().color(), QColor(Qt::black)); - EXPECT_EQ(graph->pen().style(), Qt::DashLine); - EXPECT_EQ(graph->pen().width(), 1); -} - -TEST_F(PenControllerTest, setColorAndWidth) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - auto graph = custom_plot->addGraph(); - - PenController controller(graph); - - SessionModel model; - auto pen_item = model.insertItem<PenItem>(); - controller.setItem(pen_item); - - pen_item->setProperty(PenItem::P_WIDTH, 2); - pen_item->setProperty(PenItem::P_COLOR, QColor(Qt::red)); - - // parameters of graph in QCustomPlot - EXPECT_EQ(graph->pen().color(), QColor(Qt::red)); - EXPECT_EQ(graph->pen().style(), Qt::SolidLine); - EXPECT_EQ(graph->pen().width(), 2); - - // set color via named color machinery - pen_item->setNamedColor("azure"); - EXPECT_EQ(graph->pen().color().name(), QString("#f0ffff")); -} diff --git a/mvvm/tests/testview/propertyflatview.test.cpp b/mvvm/tests/testview/propertyflatview.test.cpp deleted file mode 100644 index a134258cdcc..00000000000 --- a/mvvm/tests/testview/propertyflatview.test.cpp +++ /dev/null @@ -1,86 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testview/propertyflatview.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/standarditems/vectoritem.h" -#include "mvvm/widgets/propertyflatview.h" -#include "toyitems.h" -#include "toymodel.h" -#include <QGridLayout> - -using namespace ModelView; - -//! Tests of PropertyFlatView class. - -class PropertyFlatViewTest : public ::testing::Test { -public: - ~PropertyFlatViewTest(); - - //! Returns vector representing enable status of widgets in layout. - - std::vector<int> enable_status(PropertyFlatView& flat_view) - { - std::vector<int> result; - auto layout = flat_view.findChild<QGridLayout*>(); - for (int row = 0; row < layout->rowCount(); ++row) - for (int col = 0; col < layout->columnCount(); ++col) - result.push_back( - static_cast<int>(layout->itemAtPosition(row, col)->widget()->isEnabled())); - return result; - } -}; - -PropertyFlatViewTest::~PropertyFlatViewTest() = default; - -TEST_F(PropertyFlatViewTest, layoutForVector) -{ - SessionModel model; - auto vector_item = model.insertItem<VectorItem>(); - - PropertyFlatView flat_view; - flat_view.setItem(vector_item); - - auto layout = flat_view.findChild<QGridLayout*>(); - ASSERT_TRUE(layout != nullptr); - - EXPECT_EQ(layout->rowCount(), 3); - EXPECT_EQ(layout->columnCount(), 2); - std::vector<int> expected_enabled = {1, 1, 1, 1, 1, 1}; -} - -TEST_F(PropertyFlatViewTest, appearanceForItem) -{ - SessionModel model; - auto vector_item = model.insertItem<VectorItem>(); - auto x_item = vector_item->getItem(VectorItem::P_X); - - x_item->setEnabled(false); - - PropertyFlatView flat_view; - flat_view.setItem(vector_item); - - auto layout = flat_view.findChild<QGridLayout*>(); - ASSERT_TRUE(layout != nullptr); - - EXPECT_EQ(layout->rowCount(), 3); - EXPECT_EQ(layout->columnCount(), 2); - std::vector<int> expected_enabled = {0, 0, 1, 1, 1, 1}; - EXPECT_EQ(enable_status(flat_view), expected_enabled); - - // enabling x item - x_item->setEnabled(true); - expected_enabled = {1, 1, 1, 1, 1, 1}; - EXPECT_EQ(enable_status(flat_view), expected_enabled); -} diff --git a/mvvm/tests/testview/viewportaxisplotcontroller.test.cpp b/mvvm/tests/testview/viewportaxisplotcontroller.test.cpp deleted file mode 100644 index 3b89d72980f..00000000000 --- a/mvvm/tests/testview/viewportaxisplotcontroller.test.cpp +++ /dev/null @@ -1,435 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testview/viewportaxisplotcontroller.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "customplot_test_utils.h" -#include "google_test.h" -#include "mockwidgets.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/plotting/viewportaxisplotcontroller.h" -#include "mvvm/standarditems/axisitems.h" -#include "mvvm/standarditems/plottableitems.h" -#include "qcustomplot.h" -#include <QSignalSpy> - -using namespace ModelView; -using ::testing::_; - -//! Testing AxisPlotControllers. - -class ViewportAxisPlotControllerTest : public ::testing::Test { -public: - ~ViewportAxisPlotControllerTest(); - - std::unique_ptr<QSignalSpy> createSpy(QCPAxis* axis) - { - return std::make_unique<QSignalSpy>( - axis, static_cast<void (QCPAxis::*)(const QCPRange&)>(&QCPAxis::rangeChanged)); - } - - std::unique_ptr<QSignalSpy> createSpy2(QCPAxis* axis) - { - return std::make_unique<QSignalSpy>( - axis, static_cast<void (QCPAxis::*)(const QCPRange&, const QCPRange&)>( - &QCPAxis::rangeChanged)); - } -}; - -ViewportAxisPlotControllerTest::~ViewportAxisPlotControllerTest() = default; - -//! Initial state. - -TEST_F(ViewportAxisPlotControllerTest, initialState) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - - auto axis = custom_plot->xAxis; - - // checking initial defaults - const double customplot_default_lower(0.0), customplot_default_upper(5.0); - EXPECT_EQ(axis->range().lower, customplot_default_lower); - EXPECT_EQ(axis->range().upper, customplot_default_upper); - - // controller shouldn''t change axis range - ViewportAxisPlotController controller(axis); - EXPECT_EQ(axis->range().lower, customplot_default_lower); - EXPECT_EQ(axis->range().upper, customplot_default_upper); - - // checking axis signaling - auto xChanged = createSpy(custom_plot->xAxis); - auto yChanged = createSpy(custom_plot->yAxis); - - // changing range of axis - custom_plot->xAxis->setRangeLower(1.0); - - // checking that QCPaxis properly emiting signals - EXPECT_EQ(xChanged->count(), 1); - EXPECT_EQ(yChanged->count(), 0); -} - -//! Controller subscribed to ViewportAxisItem. -//! Checking that QCPAxis get same parameters as in AxisItem. - -TEST_F(ViewportAxisPlotControllerTest, setViewportAxisItem) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - const double expected_min = 1.0; - const double expected_max = 2.0; - - // creating the model with single ViewportAxisItem - SessionModel model; - auto axisItem = model.insertItem<ViewportAxisItem>(); - axisItem->setProperty(ViewportAxisItem::P_MIN, expected_min); - axisItem->setProperty(ViewportAxisItem::P_MAX, expected_max); - - // setting up QCustomPlot and item controller. - ASSERT_TRUE(custom_plot->xAxis != nullptr); - ViewportAxisPlotController controller(custom_plot->xAxis); - - auto prev_y_range = custom_plot->yAxis->range(); - auto xChanged = createSpy(custom_plot->xAxis); - auto yChanged = createSpy(custom_plot->yAxis); - - // Subscribtion to ViewportAxisItem should change QCPAxis range for X. - controller.setItem(axisItem); - - EXPECT_EQ(custom_plot->xAxis->range().lower, expected_min); - EXPECT_EQ(custom_plot->xAxis->range().upper, expected_max); - EXPECT_EQ(xChanged->count(), 1); - EXPECT_EQ(yChanged->count(), 0); - - // Range for QCPAxis y-axis should stay the same. - EXPECT_EQ(custom_plot->yAxis->range(), prev_y_range); -} - -//! Controller subscribed to ViewportAxisItem. -//! Change QCPAxis and check that ViewportAxisItem got new values. - -TEST_F(ViewportAxisPlotControllerTest, changeQCPAxis) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - - // creating the model with single ViewportAxisItem - SessionModel model; - auto axisItem = model.insertItem<ViewportAxisItem>(); - axisItem->setProperty(ViewportAxisItem::P_MIN, 42.0); - axisItem->setProperty(ViewportAxisItem::P_MAX, 42.1); - - // setting up QCustomPlot and item controller. - const double expected_min = 1.0; - const double expected_max = 2.0; - auto xChanged = createSpy(custom_plot->xAxis); - auto yChanged = createSpy(custom_plot->yAxis); - - // Setting up controller. - ViewportAxisPlotController controller(custom_plot->xAxis); - controller.setItem(axisItem); - - EXPECT_EQ(xChanged->count(), 1); - EXPECT_EQ(yChanged->count(), 0); - - // Changing QCPAxis - custom_plot->xAxis->setRange(expected_min, expected_max); - EXPECT_EQ(xChanged->count(), 2); - EXPECT_EQ(yChanged->count(), 0); - - // Check changed properties in ViewportAxisItem - EXPECT_EQ(axisItem->property<double>(ViewportAxisItem::P_MIN), expected_min); - EXPECT_EQ(axisItem->property<double>(ViewportAxisItem::P_MAX), expected_max); -} - -//! Controller subscribed to ViewportAxisItem. -//! Change ViewportAxisItem and check that QCPAxis got new values. - -TEST_F(ViewportAxisPlotControllerTest, changeViewportAxisItem) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - - // creating the model with single ViewportAxisItem - SessionModel model; - auto axisItem = model.insertItem<ViewportAxisItem>(); - axisItem->setProperty(ViewportAxisItem::P_MIN, 42.0); - axisItem->setProperty(ViewportAxisItem::P_MAX, 42.1); - - // setting up QCustomPlot and item controller. - ViewportAxisPlotController controller(custom_plot->xAxis); - controller.setItem(axisItem); - auto xChanged = createSpy(custom_plot->xAxis); - auto yChanged = createSpy(custom_plot->yAxis); - - // changing values - const double expected_min = 1.0; - const double expected_max = 2.0; - axisItem->setProperty(ViewportAxisItem::P_MIN, expected_min); - axisItem->setProperty(ViewportAxisItem::P_MAX, expected_max); - - // Checking QCPAxis - EXPECT_EQ(xChanged->count(), 2); - EXPECT_EQ(yChanged->count(), 0); - EXPECT_EQ(custom_plot->xAxis->range().lower, expected_min); - EXPECT_EQ(custom_plot->xAxis->range().upper, expected_max); -} - -//! Controller subscribed to ViewportAxisItem. -//! Change ViewportAxisItem and check that QCPAxis got new values. -//! Check correctness of signals issued by QCPAxisItem. - -TEST_F(ViewportAxisPlotControllerTest, changeViewportAxisItemSignaling) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - - // creating the model with single ViewportAxisItem - SessionModel model; - auto axisItem = model.insertItem<ViewportAxisItem>(); - axisItem->setProperty(ViewportAxisItem::P_MIN, 1.0); - axisItem->setProperty(ViewportAxisItem::P_MAX, 2.0); - - // setting up QCustomPlot and item controller. - ViewportAxisPlotController controller(custom_plot->xAxis); - controller.setItem(axisItem); - - // initial condition - EXPECT_EQ(custom_plot->xAxis->range().lower, 1.0); - EXPECT_EQ(custom_plot->xAxis->range().upper, 2.0); - - auto rangeChanged = createSpy(custom_plot->xAxis); - auto rangeChanged2 = createSpy2(custom_plot->xAxis); - - // making a change - const double expected_max = 20.0; - axisItem->setProperty(ViewportAxisItem::P_MAX, expected_max); - - // Checking QCPAxis - EXPECT_EQ(rangeChanged->count(), 1); - EXPECT_EQ(rangeChanged2->count(), 1); - EXPECT_EQ(custom_plot->xAxis->range().lower, 1.0); - EXPECT_EQ(custom_plot->xAxis->range().upper, expected_max); - - QList<QVariant> arguments = rangeChanged->takeFirst(); - EXPECT_EQ(arguments.size(), 1); - auto reportedRange = arguments.at(0).value<QCPRange>(); - EXPECT_EQ(reportedRange.lower, 1.0); - EXPECT_EQ(reportedRange.upper, 20.0); - - arguments = rangeChanged2->takeFirst(); - EXPECT_EQ(arguments.size(), 2); - auto newRange = arguments.at(0).value<QCPRange>(); - auto oldRange = arguments.at(1).value<QCPRange>(); - EXPECT_EQ(newRange.lower, 1.0); - EXPECT_EQ(newRange.upper, 20.0); - EXPECT_EQ(oldRange.lower, 1.0); - EXPECT_EQ(oldRange.upper, 2.0); -} - -//! Controller subscribed to ViewportAxisItem. -//! Change ViewportAxisItem and check that QCPAxis got new values. -//! Check correctness that there is not extra looping and item doesn't start changing many times - -TEST_F(ViewportAxisPlotControllerTest, changeViewportAxisItemMapping) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - - // creating the model with single ViewportAxisItem - SessionModel model; - auto axisItem = model.insertItem<ViewportAxisItem>(); - axisItem->setProperty(ViewportAxisItem::P_MIN, 1.0); - axisItem->setProperty(ViewportAxisItem::P_MAX, 2.0); - - // setting up QCustomPlot and item controller. - ViewportAxisPlotController controller(custom_plot->xAxis); - controller.setItem(axisItem); - - MockWidgetForItem widget(axisItem); - EXPECT_CALL(widget, onDataChange(_, _)).Times(0); - EXPECT_CALL(widget, onPropertyChange(axisItem, ViewportAxisItem::P_MAX)).Times(1); - EXPECT_CALL(widget, onChildPropertyChange(_, _)).Times(0); - EXPECT_CALL(widget, onItemInserted(_, _)).Times(0); - EXPECT_CALL(widget, onAboutToRemoveItem(_, _)).Times(0); - - // making a change - const double expected_max = 20.0; - axisItem->setProperty(ViewportAxisItem::P_MAX, expected_max); - - EXPECT_EQ(custom_plot->xAxis->range().lower, 1.0); - EXPECT_EQ(custom_plot->xAxis->range().upper, expected_max); -} - -//! Set ViewportAxisItem logz, subscribe controller and check that QCPAxis has it. - -TEST_F(ViewportAxisPlotControllerTest, viewportLogz) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - - // creating the model with single ViewportAxisItem - SessionModel model; - auto axisItem = model.insertItem<ViewportAxisItem>(); - axisItem->setProperty(ViewportAxisItem::P_IS_LOG, true); - - // setting up QCustomPlot and item controller. - auto qcp_axis = custom_plot->xAxis; - ViewportAxisPlotController controller(qcp_axis); - controller.setItem(axisItem); - - // QCPAxis should switch to logarithmic - EXPECT_EQ(qcp_axis->scaleType(), QCPAxis::stLogarithmic); -} - -//! Controller subscribed to ViewportAxisItem. -//! Change ViewportAxisItem logz and check that QCPAxis got new values. - -TEST_F(ViewportAxisPlotControllerTest, changeViewportLogz) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - - // creating the model with single ViewportAxisItem - SessionModel model; - auto axisItem = model.insertItem<ViewportAxisItem>(); - - // setting up QCustomPlot and item controller. - auto qcp_axis = custom_plot->xAxis; - ViewportAxisPlotController controller(qcp_axis); - controller.setItem(axisItem); - - // initial linear scale of axis - EXPECT_EQ(qcp_axis->scaleType(), QCPAxis::stLinear); - - // changing scale - axisItem->setProperty(ViewportAxisItem::P_IS_LOG, true); - - // QCPAxis should switch to logarithmic - EXPECT_EQ(qcp_axis->scaleType(), QCPAxis::stLogarithmic); -} - -//! Controller subscribed to ViewportAxisItem. -//! Change ViewportAxisItem and check that QCPAxis got new values. -//! Same test as before, only QCPAxis y-axis checked. - -TEST_F(ViewportAxisPlotControllerTest, changeViewportAxisItemYCase) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - - // creating the model with single ViewportAxisItem - SessionModel model; - auto axisItem = model.insertItem<ViewportAxisItem>(); - axisItem->setProperty(ViewportAxisItem::P_MIN, 42.0); - axisItem->setProperty(ViewportAxisItem::P_MAX, 42.1); - - // setting up QCustomPlot and item controller. - ViewportAxisPlotController controller(custom_plot->yAxis); - controller.setItem(axisItem); - auto xChanged = createSpy(custom_plot->xAxis); - auto yChanged = createSpy(custom_plot->yAxis); - - // changing values - const double expected_min = 1.0; - const double expected_max = 2.0; - axisItem->setProperty(ViewportAxisItem::P_MIN, expected_min); - axisItem->setProperty(ViewportAxisItem::P_MAX, expected_max); - - // Checking QCPAxis - EXPECT_EQ(xChanged->count(), 0); - EXPECT_EQ(yChanged->count(), 2); - EXPECT_EQ(custom_plot->yAxis->range().lower, expected_min); - EXPECT_EQ(custom_plot->yAxis->range().upper, expected_max); -} - -//! Model with two AxisItem's. Controller first is subscribed to one item, then to another. - -TEST_F(ViewportAxisPlotControllerTest, oneControllerTwoAxisItems) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - - // creating the model with single ViewportAxisItem - SessionModel model; - auto axis_item0 = model.insertItem<ViewportAxisItem>(); - axis_item0->setProperty(ViewportAxisItem::P_MIN, 1.0); - axis_item0->setProperty(ViewportAxisItem::P_MAX, 2.0); - - auto axis_item1 = model.insertItem<ViewportAxisItem>(); - axis_item1->setProperty(ViewportAxisItem::P_MIN, 10.0); - axis_item1->setProperty(ViewportAxisItem::P_MAX, 20.0); - - // setting up QCustomPlot and item controller. - auto controller = std::make_unique<ViewportAxisPlotController>(custom_plot->xAxis); - controller->setItem(axis_item0); - auto xChanged = createSpy(custom_plot->xAxis); - auto yChanged = createSpy(custom_plot->yAxis); - - // initial axis status - EXPECT_EQ(axis_item0->property<double>(ViewportAxisItem::P_MIN), - custom_plot->xAxis->range().lower); - EXPECT_EQ(axis_item0->property<double>(ViewportAxisItem::P_MAX), - custom_plot->xAxis->range().upper); - - // switching to second axis - controller->setItem(axis_item1); - - EXPECT_EQ(xChanged->count(), 1); - EXPECT_EQ(yChanged->count(), 0); - - EXPECT_EQ(axis_item1->property<double>(ViewportAxisItem::P_MIN), - custom_plot->xAxis->range().lower); - EXPECT_EQ(axis_item1->property<double>(ViewportAxisItem::P_MAX), - custom_plot->xAxis->range().upper); - - // changing QCPAxis - const double expected_min = 100.0; - const double expected_max = 200.0; - custom_plot->xAxis->setRange(expected_min, expected_max); - - // previous axis should still have original values - EXPECT_EQ(axis_item0->property<double>(ViewportAxisItem::P_MIN), 1.0); - EXPECT_EQ(axis_item0->property<double>(ViewportAxisItem::P_MAX), 2.0); - - // second axis should get values from QCPAxis - EXPECT_EQ(axis_item1->property<double>(ViewportAxisItem::P_MIN), expected_min); - EXPECT_EQ(axis_item1->property<double>(ViewportAxisItem::P_MAX), expected_max); - - // removing axes from the model - model.removeItem(model.rootItem(), {"", 0}); - model.removeItem(model.rootItem(), {"", 0}); - EXPECT_EQ(model.rootItem()->childrenCount(), 0); - - // no UB should follow (valgrind will tell us) - custom_plot->xAxis->setRange(1.0, 2.0); - - // destroying controller, no UB - controller.reset(); - custom_plot->xAxis->setRange(2.0, 3.0); -} - -//! Controller subscribed to ViewportAxisItem. -//! Change ViewportAxisItem title and check that QCPAxis got new values. - -TEST_F(ViewportAxisPlotControllerTest, changeAxisTitle) -{ - auto custom_plot = std::make_unique<QCustomPlot>(); - - // creating the model with single ViewportAxisItem - SessionModel model; - auto axisItem = model.insertItem<ViewportAxisItem>(); - - // setting up QCustomPlot and item controller. - auto qcp_axis = custom_plot->xAxis; - ViewportAxisPlotController controller(qcp_axis); - controller.setItem(axisItem); - - // changing title - auto textItem = axisItem->item<TextItem>(ViewportAxisItem::P_TITLE); - textItem->setProperty(TextItem::P_TEXT, "abc"); - - // no need to change title size and font (checked in axistitlecontroller.test) - - // QCPAxis should switch to logarithmic - EXPECT_EQ(qcp_axis->label(), QString("abc")); -} diff --git a/mvvm/tests/testview/widgetutils.test.cpp b/mvvm/tests/testview/widgetutils.test.cpp deleted file mode 100644 index bb975f491fb..00000000000 --- a/mvvm/tests/testview/widgetutils.test.cpp +++ /dev/null @@ -1,102 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testview/widgetutils.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/widgets/widgetutils.h" -#include "test_utils.h" -#include <QDebug> -#include <QDir> - -using namespace ModelView; - -//! Testing functions from utils. - -class WidgetUtilsTest : public ::testing::Test { -public: - ~WidgetUtilsTest(); -}; - -WidgetUtilsTest::~WidgetUtilsTest() = default; - -//! Test of WithTildeHomePath function. - -TEST_F(WidgetUtilsTest, WithTildeHomePath) -{ - if (ModelView::Utils::IsWindowsHost()) { - auto test_dir = QString::fromStdString(TestUtils::TestOutputDir()); - EXPECT_EQ(Utils::WithTildeHomePath(test_dir), test_dir); - } else { - auto home_path = QDir::homePath(); - auto test_dir = QString::fromStdString(TestUtils::TestOutputDir()); - auto expected = test_dir.startsWith(home_path) - ? QString("~") + test_dir.mid(home_path.size()) - : test_dir; - - // "/home/user/build-debug/test_output" -> ~/build-debug/test_output" - EXPECT_EQ(Utils::WithTildeHomePath(test_dir).toStdString(), expected.toStdString()); - - EXPECT_EQ(Utils::WithTildeHomePath("/opt/sw/build").toStdString(), - std::string("/opt/sw/build")); - } -} - -TEST_F(WidgetUtilsTest, ProjectWindowTitle) -{ - // untitled and unmodified project - EXPECT_EQ(Utils::ProjectWindowTitle(QString(""), false), "Untitled"); - - // untitled and modified project - EXPECT_EQ(Utils::ProjectWindowTitle(QString(""), true), "*Untitled"); - - // unmodified project without projectDir - EXPECT_EQ(Utils::ProjectWindowTitle(QString("Untitled"), false), "Untitled"); - - // modified project without projectDir - EXPECT_EQ(Utils::ProjectWindowTitle(QString("Untitled"), true), "*Untitled"); - - // unmodified project with projectDir - EXPECT_EQ(Utils::ProjectWindowTitle(QString("/home/user/project1"), false), "project1"); - - // modified project with projectDir - EXPECT_EQ(Utils::ProjectWindowTitle(QString("/home/user/project1"), true), "*project1"); -} - -TEST_F(WidgetUtilsTest, ClickableText) -{ - EXPECT_EQ(Utils::ClickableText("abc", "site.com"), QString("<a href=\"site.com\">abc</a>")); -} - -TEST_F(WidgetUtilsTest, toStringList) -{ - using vec_t = std::vector<std::string>; - EXPECT_EQ(Utils::toStringList(vec_t()), QStringList()); - EXPECT_EQ(Utils::toStringList(vec_t({"abc", "cde"})), QStringList({"abc", "cde"})); -} - -TEST_F(WidgetUtilsTest, fromStringList) -{ - using vec_t = std::vector<std::string>; - EXPECT_EQ(Utils::fromStringList(QStringList()), vec_t()); - EXPECT_EQ(Utils::fromStringList(QStringList({"abc", "cde"})), vec_t({"abc", "cde"})); -} - -TEST_F(WidgetUtilsTest, toFromByteArray) -{ - QStringList expected = QStringList() << "aaa" - << "bbb" - << "ccc"; - - auto array = Utils::serialize(expected); - EXPECT_EQ(Utils::deserialize(array), expected); -} diff --git a/mvvm/tests/testviewmodel/CMakeLists.txt b/mvvm/tests/testviewmodel/CMakeLists.txt deleted file mode 100644 index c5ad35aa81c..00000000000 --- a/mvvm/tests/testviewmodel/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -set(test testviewmodel) - -file(GLOB source_files "*.cpp") -file(GLOB include_files "*.h") - -find_package(Qt5Core REQUIRED) -find_package(Qt5Test REQUIRED) - -# necessary for Qt creator and clang code model -include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) - -set(CMAKE_AUTOMOC ON) -add_executable(${test} ${source_files} ${include_files}) -target_link_libraries(${test} gtest gmock Qt5::Core Qt5::Test mvvm_viewmodel testmachinery qcustomplot) - -if (MVVM_DISCOVER_TESTS) - gtest_discover_tests(${test}) -else() - add_custom_target(${test}_run ALL DEPENDS ${test} COMMAND ${test}) -endif() diff --git a/mvvm/tests/testviewmodel/TestAll.cpp b/mvvm/tests/testviewmodel/TestAll.cpp deleted file mode 100644 index 1705b94d0a0..00000000000 --- a/mvvm/tests/testviewmodel/TestAll.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testviewmodel/TestAll.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "qcustomplot.h" -#include <QApplication> -#include <QStandardItem> - -int main(int argc, char** argv) -{ - ::testing::InitGoogleTest(&argc, argv); - - ModelView::Comparators::registerComparators(); - - return RUN_ALL_TESTS(); -} diff --git a/mvvm/tests/testviewmodel/TestToyLayerItem.cpp b/mvvm/tests/testviewmodel/TestToyLayerItem.cpp deleted file mode 100644 index b10c233b2d5..00000000000 --- a/mvvm/tests/testviewmodel/TestToyLayerItem.cpp +++ /dev/null @@ -1,155 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testviewmodel/TestToyLayerItem.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/itemutils.h" -#include "mvvm/standarditems/vectoritem.h" -#include "mvvm/viewmodel/defaultviewmodel.h" -#include "mvvm/viewmodel/standardviewitems.h" -#include "mvvm/viewmodel/topitemsviewmodel.h" -#include "toyitems.h" -#include "toymodel.h" -#include <QSignalSpy> - -using namespace ModelView; - -//! Tests for toy Layer in the context of model and view model. - -class ToyLayerItemTest : public ::testing::Test { -public: - ~ToyLayerItemTest(); -}; - -ToyLayerItemTest::~ToyLayerItemTest() = default; - -//! Initial state. - -TEST_F(ToyLayerItemTest, initialState) -{ - ToyItems::LayerItem item; - EXPECT_TRUE(Utils::IsSinglePropertyTag(item, ToyItems::LayerItem::P_THICKNESS)); - EXPECT_TRUE(Utils::IsSinglePropertyTag(item, ToyItems::LayerItem::P_COLOR)); - EXPECT_FALSE(Utils::IsSinglePropertyTag(item, ToyItems::LayerItem::T_PARTICLES)); -} - -//! Toy layer as prodused by toy SampleModel. - -TEST_F(ToyLayerItemTest, inModel) -{ - ToyItems::SampleModel model; - auto layer = model.insertItem<ToyItems::LayerItem>(); - - EXPECT_FALSE(layer->data<QVariant>().isValid()); - EXPECT_EQ(layer->displayName(), ToyItems::GUI::Constants::LayerItemType); -} - -TEST_F(ToyLayerItemTest, inViewModel) -{ - ToyItems::SampleModel model; - auto layerItem = model.insertItem<ToyItems::LayerItem>(); - - // constructing viewModel from sample model - DefaultViewModel viewModel(&model); - - // root item should have one child, item looking at our layerItem - EXPECT_EQ(viewModel.rowCount(), 1); - EXPECT_EQ(viewModel.columnCount(), 2); - - // accessing to viewItem representing layerItem - QModelIndex i_layer = viewModel.index(0, 0); - auto viewItem = dynamic_cast<ViewLabelItem*>(viewModel.itemFromIndex(i_layer)); - EXPECT_TRUE(viewItem != nullptr); - EXPECT_EQ(viewItem->item(), layerItem); - - // it has two rows and two columns, corresponding to our "thickness" and "color" properties - EXPECT_EQ(viewModel.rowCount(i_layer), 2); - EXPECT_EQ(viewModel.columnCount(i_layer), 2); - - // accessing to views representing label and value of thickness property - QModelIndex thicknessLabelIndex = viewModel.index(0, 0, i_layer); - auto thicknessLabelView = - dynamic_cast<ViewLabelItem*>(viewModel.itemFromIndex(thicknessLabelIndex)); - EXPECT_TRUE(thicknessLabelView != nullptr); - - QModelIndex thicknessValueIndex = viewModel.index(0, 1, i_layer); - auto thicknessValueView = - dynamic_cast<ViewDataItem*>(viewModel.itemFromIndex(thicknessValueIndex)); - EXPECT_TRUE(thicknessValueView != nullptr); - - // internally, views for label and data should point to single SessionItem corresponding to - // thickness property - EXPECT_EQ(thicknessLabelView->item(), layerItem->getItem(ToyItems::LayerItem::P_THICKNESS)); - EXPECT_EQ(thicknessValueView->item(), layerItem->getItem(ToyItems::LayerItem::P_THICKNESS)); -} - -//! Constructing ViewModel from a Layer with one "thickness" property. -//! Change thickness property in SessionItem, control dataChanged signals from ViewModel. - -TEST_F(ToyLayerItemTest, layerItemDataChanged) -{ - ToyItems::SampleModel model; - auto layerItem = model.insertItem<ToyItems::LayerItem>(); - - // constructing viewModel from sample model - DefaultViewModel viewModel(&model); - - QModelIndex i_layer = viewModel.index(0, 0); - QModelIndex thicknessIndex = viewModel.index(0, 1, i_layer); - - QSignalSpy spyDataChanged(&viewModel, &DefaultViewModel::dataChanged); - - layerItem->setProperty(ToyItems::LayerItem::P_THICKNESS, 50.0); - EXPECT_EQ(spyDataChanged.count(), 1); - - // dataChanged should report thicknessIndex and two roles - QList<QVariant> arguments = spyDataChanged.takeFirst(); - EXPECT_EQ(arguments.size(), 3); // QModelIndex left, QModelIndex right, QVector<int> roles - EXPECT_EQ(arguments.at(0).value<QModelIndex>(), thicknessIndex); - EXPECT_EQ(arguments.at(1).value<QModelIndex>(), thicknessIndex); - QVector<int> expectedRoles = {Qt::DisplayRole, Qt::EditRole}; - EXPECT_EQ(arguments.at(2).value<QVector<int>>(), expectedRoles); -} - -//! Validates display name - -TEST_F(ToyLayerItemTest, displayNameInMultiLayer) -{ - ToyItems::SampleModel model; - auto multiLayer = model.insertItem<ToyItems::MultiLayerItem>(); - - auto layer0 = model.insertItem<ToyItems::LayerItem>(multiLayer); - EXPECT_EQ(layer0->displayName(), "Layer"); - - auto layer1 = model.insertItem<ToyItems::LayerItem>(multiLayer); - EXPECT_EQ(layer0->displayName(), "Layer0"); - EXPECT_EQ(layer1->displayName(), "Layer1"); -} - -//! LayerItem as rootItem. - -TEST_F(ToyLayerItemTest, setRootItemContext) -{ - ToyItems::SampleModel model; - auto layer = model.insertItem<ToyItems::LayerItem>(); - DefaultViewModel viewModel(&model); - viewModel.setRootSessionItem(layer); - - EXPECT_EQ(viewModel.rowCount(QModelIndex()), 2); - EXPECT_EQ(viewModel.columnCount(QModelIndex()), 2); - - // index of item representing thickness - QModelIndex thicknessIndex = viewModel.index(0, 0, QModelIndex()); - EXPECT_EQ(viewModel.sessionItemFromIndex(thicknessIndex), - layer->getItem(ToyItems::LayerItem::P_THICKNESS)); -} diff --git a/mvvm/tests/testviewmodel/TestToyMultiLayerItem.cpp b/mvvm/tests/testviewmodel/TestToyMultiLayerItem.cpp deleted file mode 100644 index 4c5165b91d9..00000000000 --- a/mvvm/tests/testviewmodel/TestToyMultiLayerItem.cpp +++ /dev/null @@ -1,105 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testviewmodel/TestToyMultiLayerItem.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/itemutils.h" -#include "mvvm/viewmodel/defaultviewmodel.h" -#include "mvvm/viewmodel/standardviewitems.h" -#include "mvvm/viewmodel/viewmodelutils.h" -#include "toyitems.h" -#include "toymodel.h" -#include <QSignalSpy> - -using namespace ModelView; - -//! Tests of toy MultiLayer in the context of model and viewmodel. - -class ToyMultilayerItemTest : public ::testing::Test { -public: - ~ToyMultilayerItemTest(); -}; - -ToyMultilayerItemTest::~ToyMultilayerItemTest() = default; - -//! Initial state. - -TEST_F(ToyMultilayerItemTest, initialState) -{ - ToyItems::MultiLayerItem item; - EXPECT_FALSE(Utils::IsSinglePropertyTag(item, ToyItems::MultiLayerItem::T_LAYERS)); -} - -//! Toy multilayer in a SampleModel. - -TEST_F(ToyMultilayerItemTest, multiLayer) -{ - ToyItems::SampleModel model; - auto multiLayer = model.insertItem<ToyItems::MultiLayerItem>(); - - EXPECT_FALSE(multiLayer->data<QVariant>().isValid()); - EXPECT_EQ(multiLayer->displayName(), ToyItems::GUI::Constants::MultiLayerItemType); -} - -//! Constructing ViewModel from a MultiLayer. -//! Checking that view items point co correct SessionItem. - -TEST_F(ToyMultilayerItemTest, multiLayerView) -{ - ToyItems::SampleModel model; - auto multiLayerItem = model.insertItem<ToyItems::MultiLayerItem>(); - - DefaultViewModel viewModel(&model); - EXPECT_EQ(viewModel.rowCount(), 1); - EXPECT_EQ(viewModel.columnCount(), 2); - - // accessing first child under the root item - QModelIndex mlIndex = viewModel.index(0, 0); - - // it should be ViewLabelItem looking at our MultiLayer item - auto viewItem = dynamic_cast<ViewLabelItem*>(viewModel.itemFromIndex(mlIndex)); - EXPECT_TRUE(viewItem != nullptr); - EXPECT_EQ(viewItem->item(), multiLayerItem); - - // adding layer - model.insertItem<ToyItems::LayerItem>(multiLayerItem); - EXPECT_EQ(viewModel.rowCount(mlIndex), 1); - EXPECT_EQ(viewModel.columnCount(mlIndex), 2); -} - -//! Find ViewItem corresponding to given MultiLayer item. - -TEST_F(ToyMultilayerItemTest, findMultiLayerView) -{ - ToyItems::SampleModel model; - auto multiLayerItem = model.insertItem<ToyItems::MultiLayerItem>(); - - DefaultViewModel viewModel(&model); - - auto views = viewModel.findViews(multiLayerItem); - EXPECT_EQ(views.size(), 2); - EXPECT_EQ(views.at(0)->item(), multiLayerItem); -} - -//! How ViewLabelItem sees MultiLayer - -TEST_F(ToyMultilayerItemTest, viewItemsForMultiLayer) -{ - ToyItems::SampleModel model; - - auto multiLayer = model.insertItem<ToyItems::MultiLayerItem>(); - - ViewLabelItem labelItem(multiLayer); - EXPECT_EQ(labelItem.data(Qt::DisplayRole).toString().toStdString(), - ToyItems::GUI::Constants::MultiLayerItemType); -} diff --git a/mvvm/tests/testviewmodel/TestToyParticleItem.cpp b/mvvm/tests/testviewmodel/TestToyParticleItem.cpp deleted file mode 100644 index 90ae72b435f..00000000000 --- a/mvvm/tests/testviewmodel/TestToyParticleItem.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testviewmodel/TestToyParticleItem.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/itemutils.h" -#include "mvvm/viewmodel/defaultviewmodel.h" -#include "mvvm/viewmodel/viewmodelutils.h" -#include "toyitems.h" -#include "toymodel.h" -#include <QSignalSpy> - -using namespace ModelView; - -//! Tests of toy MultiLayer in the context of model and viewmodel. - -class ToyParticleItemTest : public ::testing::Test { -public: - ~ToyParticleItemTest(); -}; - -ToyParticleItemTest::~ToyParticleItemTest() = default; - -//! Initial state. - -TEST_F(ToyParticleItemTest, initialState) -{ - ToyItems::ParticleItem item; - EXPECT_TRUE(Utils::IsSinglePropertyTag(item, ToyItems::ParticleItem::P_POSITION)); - EXPECT_TRUE(Utils::IsSinglePropertyTag(item, ToyItems::ParticleItem::P_SHAPES)); -} - -TEST_F(ToyParticleItemTest, TopLevelItems) -{ - ToyItems::SampleModel model; - auto particle = model.insertItem<ToyItems::ParticleItem>(); - - EXPECT_EQ(Utils::TopLevelItems(*model.rootItem()), std::vector<SessionItem*>({particle})); - EXPECT_EQ(Utils::TopLevelItems(*particle), std::vector<SessionItem*>{}); -} - -TEST_F(ToyParticleItemTest, SinglePropertyItems) -{ - ToyItems::SampleModel model; - auto particle = model.insertItem<ToyItems::ParticleItem>(); - - EXPECT_EQ(Utils::TopLevelItems(*model.rootItem()), std::vector<SessionItem*>({particle})); - - EXPECT_EQ(Utils::SinglePropertyItems(*model.rootItem()), std::vector<SessionItem*>{}); - std::vector<SessionItem*> expected = {particle->getItem(ToyItems::ParticleItem::P_POSITION), - particle->getItem(ToyItems::ParticleItem::P_SHAPES)}; - EXPECT_EQ(Utils::SinglePropertyItems(*particle), expected); -} diff --git a/mvvm/tests/testviewmodel/defaulteditorfactory.test.cpp b/mvvm/tests/testviewmodel/defaulteditorfactory.test.cpp deleted file mode 100644 index 511836a70de..00000000000 --- a/mvvm/tests/testviewmodel/defaulteditorfactory.test.cpp +++ /dev/null @@ -1,200 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testviewmodel/defaulteditorfactory.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/editors/booleditor.h" -#include "mvvm/editors/coloreditor.h" -#include "mvvm/editors/combopropertyeditor.h" -#include "mvvm/editors/defaulteditorfactory.h" -#include "mvvm/editors/editor_constants.h" -#include "mvvm/editors/externalpropertyeditor.h" -#include "mvvm/editors/integereditor.h" -#include "mvvm/editors/scientificdoubleeditor.h" -#include "mvvm/editors/scientificspinbox.h" -#include "mvvm/editors/scientificspinboxeditor.h" -#include "mvvm/editors/selectablecomboboxeditor.h" -#include "mvvm/model/comboproperty.h" -#include "mvvm/model/externalproperty.h" -#include "mvvm/model/propertyitem.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/utils/reallimits.h" -#include "mvvm/viewmodel/defaultviewmodel.h" -#include "mvvm/viewmodel/viewmodeldelegate.h" -#include "widgetbasedtest.h" -#include <QSpinBox> -#include <QStandardItemModel> -#include <limits> - -using namespace ModelView; - -class DefaultEditorFactoryTest : public WidgetBasedTest { -public: - DefaultEditorFactoryTest() : m_factory(std::make_unique<DefaultEditorFactory>()) {} - ~DefaultEditorFactoryTest(); - - //! Helper function to build temporary model and create editor for cell. - std::unique_ptr<CustomEditor> createEditor(const QVariant& variant, - RealLimits limits = RealLimits::limitless(), - const std::string& editor_type = {}) - { - // populating model with data - SessionModel model; - auto propertyItem = model.insertItem<PropertyItem>(); - propertyItem->setData(variant); - if (limits.hasLowerLimit() || limits.hasUpperLimit()) - propertyItem->setLimits(limits); - if (!editor_type.empty()) - propertyItem->setEditorType(editor_type); - - // create view model and use index of data cell to create an editor - DefaultViewModel viewModel(&model); - return m_factory->createEditor(viewModel.index(0, 1)); - } - -protected: - std::unique_ptr<DefaultEditorFactory> m_factory; -}; - -DefaultEditorFactoryTest::~DefaultEditorFactoryTest() = default; - -//! Tests editor creation on bool property. - -TEST_F(DefaultEditorFactoryTest, boolProperty) -{ - auto editor = createEditor(QVariant::fromValue(true)); - EXPECT_TRUE(dynamic_cast<BoolEditor*>(editor.get())); -} - -//! Tests editor creation on integer property. - -TEST_F(DefaultEditorFactoryTest, integerProperty) -{ - auto editor = createEditor(QVariant::fromValue(42)); - EXPECT_TRUE(dynamic_cast<IntegerEditor*>(editor.get())); - - auto spin_box = editor->findChild<QSpinBox*>(); - - ASSERT_TRUE(spin_box != nullptr); - EXPECT_EQ(spin_box->minimum(), -65536); - EXPECT_EQ(spin_box->maximum(), 65536); -} - -//! Tests editor creation on integer property with limits. - -TEST_F(DefaultEditorFactoryTest, integerPropertyWithLimits) -{ - auto editor = createEditor(QVariant::fromValue(42), RealLimits::limited(-1, 1)); - EXPECT_TRUE(dynamic_cast<IntegerEditor*>(editor.get())); - - auto spin_box = editor->findChild<QSpinBox*>(); - ASSERT_TRUE(spin_box != nullptr); - EXPECT_EQ(spin_box->minimum(), -1); - EXPECT_EQ(spin_box->maximum(), 1); -} - -//! Tests editor creation on double property. - -TEST_F(DefaultEditorFactoryTest, doubleProperty) -{ - auto editor = createEditor(QVariant::fromValue(42.42)); - EXPECT_TRUE(dynamic_cast<ScientificSpinBoxEditor*>(editor.get())); - - auto spin_box = editor->findChild<ScientificSpinBox*>(); - ASSERT_TRUE(spin_box != nullptr); - EXPECT_FLOAT_EQ(spin_box->minimum(), -std::numeric_limits<double>::max()); - EXPECT_FLOAT_EQ(spin_box->maximum(), std::numeric_limits<double>::max()); -} - -//! Tests editor creation on double property with limits. - -TEST_F(DefaultEditorFactoryTest, doublePropertyWithLimits) -{ - auto editor = createEditor(QVariant::fromValue(42.42), RealLimits::limited(41, 43)); - EXPECT_TRUE(dynamic_cast<ScientificSpinBoxEditor*>(editor.get())); - - auto spin_box = editor->findChild<ScientificSpinBox*>(); - ASSERT_TRUE(spin_box != nullptr); - EXPECT_FLOAT_EQ(spin_box->minimum(), 41); - EXPECT_FLOAT_EQ(spin_box->maximum(), 43); -} - -//! Tests editor creation on color property. - -TEST_F(DefaultEditorFactoryTest, colorProperty) -{ - auto editor = createEditor(QVariant::fromValue(QColor(Qt::green))); - EXPECT_TRUE(dynamic_cast<ColorEditor*>(editor.get())); -} - -//! Tests editor creation on combo property. - -TEST_F(DefaultEditorFactoryTest, comboProperty) -{ - auto editor = createEditor(QVariant::fromValue(ComboProperty())); - EXPECT_TRUE(dynamic_cast<ComboPropertyEditor*>(editor.get())); -} - -//! Tests editor creation on combo property. - -TEST_F(DefaultEditorFactoryTest, externalProperty) -{ - auto editor = createEditor(QVariant::fromValue(ExternalProperty())); - EXPECT_TRUE(dynamic_cast<ExternalPropertyEditor*>(editor.get())); -} - -//! Tests editor creation on some unsupported property. - -TEST_F(DefaultEditorFactoryTest, unsupportedProperty) -{ - // no dedicated editor for std::string yet - auto editor = createEditor(QVariant::fromValue(std::string("text"))); - EXPECT_EQ(editor.get(), nullptr); - - // no editor for RealLimits - editor = createEditor(QVariant::fromValue(RealLimits::limited(1.0, 2.0))); - EXPECT_EQ(editor.get(), nullptr); - - // no editor for invalid variant - editor = createEditor(QVariant()); - EXPECT_EQ(editor.get(), nullptr); - - // special case of invalid index - EXPECT_EQ(m_factory->createEditor(QModelIndex()), nullptr); -} - -//! Create test editor using EDITOR role. - -TEST_F(DefaultEditorFactoryTest, editorType) -{ - auto editor = createEditor(QVariant::fromValue(ComboProperty()), RealLimits(), - GUI::Constants::SelectableComboPropertyEditorType); - EXPECT_TRUE(dynamic_cast<SelectableComboBoxEditor*>(editor.get())); -} - -//! Tests editor creation on combo property in QStandardItemModel with our variant. - -TEST_F(DefaultEditorFactoryTest, comboPropertyInStandardModel) -{ - QStandardItemModel model; - auto parent = model.invisibleRootItem(); - QList<QStandardItem*> children{new QStandardItem}; - parent->appendRow(children); - - auto item = model.item(0, 0); - item->setData(QVariant::fromValue(ComboProperty()), Qt::EditRole); - - auto editor = m_factory->createEditor(model.index(0, 0)); - EXPECT_TRUE(dynamic_cast<ComboPropertyEditor*>(editor.get())); -} diff --git a/mvvm/tests/testviewmodel/defaultviewmodel.test.cpp b/mvvm/tests/testviewmodel/defaultviewmodel.test.cpp deleted file mode 100644 index be412135898..00000000000 --- a/mvvm/tests/testviewmodel/defaultviewmodel.test.cpp +++ /dev/null @@ -1,852 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testviewmodel/defaultviewmodel.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "folderbasedtest.h" -#include "google_test.h" -#include "mvvm/model/compounditem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/model/taginfo.h" -#include "mvvm/serialization/jsondocument.h" -#include "mvvm/serialization/jsonitem_types.h" -#include "mvvm/serialization/jsonmodelconverter.h" -#include "mvvm/standarditems/axisitems.h" -#include "mvvm/standarditems/containeritem.h" -#include "mvvm/standarditems/data1ditem.h" -#include "mvvm/standarditems/graphitem.h" -#include "mvvm/standarditems/graphviewportitem.h" -#include "mvvm/standarditems/vectoritem.h" -#include "mvvm/viewmodel/defaultviewmodel.h" -#include "mvvm/viewmodel/standardviewitems.h" -#include "mvvm/viewmodel/viewmodelutils.h" -#include "test_utils.h" -#include <QDebug> -#include <QJsonObject> -#include <QSignalSpy> - -using namespace ModelView; - -class DefaultViewModelTest : public FolderBasedTest { -public: - DefaultViewModelTest() : FolderBasedTest("test_DefaultViewModel") {} - ~DefaultViewModelTest(); -}; - -DefaultViewModelTest::~DefaultViewModelTest() = default; - -TEST_F(DefaultViewModelTest, initialState) -{ - SessionModel model; - DefaultViewModel viewModel(&model); - EXPECT_EQ(viewModel.rowCount(), 0); - EXPECT_EQ(viewModel.columnCount(), 0); - EXPECT_EQ(viewModel.sessionItemFromIndex(QModelIndex()), model.rootItem()); -} - -//! Single property item in a model. - -TEST_F(DefaultViewModelTest, fromPropertyItem) -{ - SessionModel model; - auto propertyItem = model.insertItem<PropertyItem>(); - propertyItem->setData(42.0); - - DefaultViewModel viewModel(&model); - EXPECT_EQ(viewModel.rowCount(), 1); - EXPECT_EQ(viewModel.columnCount(), 2); - - // accessing first child under the root item - QModelIndex labelIndex = viewModel.index(0, 0); - QModelIndex dataIndex = viewModel.index(0, 1); - - // it should be ViewLabelItem looking at our PropertyItem item - auto labelItem = dynamic_cast<ViewLabelItem*>(viewModel.itemFromIndex(labelIndex)); - ASSERT_TRUE(labelItem != nullptr); - EXPECT_EQ(labelItem->item(), propertyItem); - - auto dataItem = dynamic_cast<ViewDataItem*>(viewModel.itemFromIndex(dataIndex)); - ASSERT_TRUE(dataItem != nullptr); - EXPECT_EQ(dataItem->item(), propertyItem); -} - -//! Single property item in a model, inserted after DefaultViewModel was setup. - -TEST_F(DefaultViewModelTest, initThenInsert) -{ - SessionModel model; - DefaultViewModel viewModel(&model); - - auto propertyItem = model.insertItem<PropertyItem>(); - propertyItem->setData(42.0); - - EXPECT_EQ(viewModel.rowCount(), 1); - EXPECT_EQ(viewModel.columnCount(), 2); - - // accessing first child under the root item - QModelIndex labelIndex = viewModel.index(0, 0); - QModelIndex dataIndex = viewModel.index(0, 1); - - // it should be ViewLabelItem looking at our PropertyItem item - auto labelItem = dynamic_cast<ViewLabelItem*>(viewModel.itemFromIndex(labelIndex)); - ASSERT_TRUE(labelItem != nullptr); - EXPECT_EQ(labelItem->item(), propertyItem); - - // Feature: since our PropertyItem got it's value after ViewModel was initialized, the model - // still holds ViewEmptyItem and not ViewDataItem. - auto dataItem = dynamic_cast<ViewDataItem*>(viewModel.itemFromIndex(dataIndex)); - ASSERT_TRUE(dataItem != nullptr); -} - -//! Single property item in a model. - -TEST_F(DefaultViewModelTest, sessionItemFromIndex) -{ - SessionModel model; - auto propertyItem = model.insertItem<PropertyItem>(); - propertyItem->setData(42.0); - - DefaultViewModel viewModel(&model); - EXPECT_EQ(viewModel.rowCount(), 1); - EXPECT_EQ(viewModel.columnCount(), 2); - - // accessing first child under the root item - QModelIndex labelIndex = viewModel.index(0, 0); - QModelIndex dataIndex = viewModel.index(0, 1); - - EXPECT_EQ(viewModel.sessionItemFromIndex(QModelIndex()), model.rootItem()); - EXPECT_EQ(viewModel.sessionItemFromIndex(labelIndex), propertyItem); - EXPECT_EQ(viewModel.sessionItemFromIndex(dataIndex), propertyItem); -} - -//! Index from single property item. - -TEST_F(DefaultViewModelTest, indexFromSessionItem) -{ - SessionModel model; - auto propertyItem = model.insertItem<PropertyItem>(); - propertyItem->setData(42.0); - - DefaultViewModel viewModel(&model); - EXPECT_EQ(viewModel.rowCount(), 1); - EXPECT_EQ(viewModel.columnCount(), 2); - - // accessing first child under the root item - QModelIndex labelIndex = viewModel.index(0, 0); - QModelIndex dataIndex = viewModel.index(0, 1); - - QModelIndexList expected{labelIndex, dataIndex}; - EXPECT_EQ(viewModel.indexOfSessionItem(propertyItem), expected); -} - -//! Find ViewItem's corresponding to given PropertyItem. - -TEST_F(DefaultViewModelTest, findPropertyItemView) -{ - SessionModel model; - auto propertyItem = model.insertItem<PropertyItem>(); - propertyItem->setData(42.0); - - DefaultViewModel viewModel(&model); - auto views = viewModel.findViews(propertyItem); -} - -//! Constructing ViewModel from single PropertyItem. -//! Change thickness property in SessionItem, control dataChanged signals from ViewModel. - -TEST_F(DefaultViewModelTest, propertyItemDataChanged) -{ - SessionModel model; - auto propertyItem = model.insertItem<PropertyItem>(); - propertyItem->setData(42.0); - - // constructing viewModel from sample model - DefaultViewModel viewModel(&model); - - QModelIndex dataIndex = viewModel.index(0, 1); - - QSignalSpy spyDataChanged(&viewModel, &DefaultViewModel::dataChanged); - - propertyItem->setData(50.0); - EXPECT_EQ(spyDataChanged.count(), 1); - - // dataChanged should report thicknessIndex and two roles - QList<QVariant> arguments = spyDataChanged.takeFirst(); - EXPECT_EQ(arguments.size(), 3); // QModelIndex left, QModelIndex right, QVector<int> roles - EXPECT_EQ(arguments.at(0).value<QModelIndex>(), dataIndex); - EXPECT_EQ(arguments.at(1).value<QModelIndex>(), dataIndex); - QVector<int> expectedRoles = {Qt::DisplayRole, Qt::EditRole}; - EXPECT_EQ(arguments.at(2).value<QVector<int>>(), expectedRoles); -} - -//! Inserting single top level item. - -TEST_F(DefaultViewModelTest, insertSingleTopItem) -{ - SessionModel model; - DefaultViewModel viewModel(&model); - - QSignalSpy spyInsert(&viewModel, &DefaultViewModel::rowsInserted); - - // inserting single item - model.insertItem<SessionItem>(); - - // root item should have one child - EXPECT_EQ(viewModel.rowCount(), 1); - EXPECT_EQ(viewModel.columnCount(), 2); - - // removing child - model.removeItem(model.rootItem(), {"", 0}); - EXPECT_EQ(spyInsert.count(), 1); - EXPECT_EQ(viewModel.rowCount(), 0); - EXPECT_EQ(viewModel.columnCount(), 0); - - QList<QVariant> arguments = spyInsert.takeFirst(); - EXPECT_EQ(arguments.size(), 3); // QModelIndex &parent, int first, int last - EXPECT_EQ(arguments.at(0).value<QModelIndex>(), QModelIndex()); - EXPECT_EQ(arguments.at(1).value<int>(), 0); - EXPECT_EQ(arguments.at(2).value<int>(), 0); -} - -//! Removing single top level item. - -TEST_F(DefaultViewModelTest, removeSingleTopItem) -{ - SessionModel model; - - // inserting single item - model.insertItem<SessionItem>(); - - // constructing viewModel from sample model - DefaultViewModel viewModel(&model); - - // root item should have one child - EXPECT_EQ(viewModel.rowCount(), 1); - EXPECT_EQ(viewModel.columnCount(), 2); - - QSignalSpy spyRemove(&viewModel, &DefaultViewModel::rowsRemoved); - - // removing child - model.removeItem(model.rootItem(), {"", 0}); - ASSERT_EQ(spyRemove.count(), 1); - EXPECT_EQ(viewModel.rowCount(), 0); - EXPECT_EQ(viewModel.columnCount(), 0); - - QList<QVariant> arguments = spyRemove.takeFirst(); - ASSERT_EQ(arguments.size(), 3); // QModelIndex &parent, int first, int last - EXPECT_EQ(arguments.at(0).value<QModelIndex>(), QModelIndex()); - EXPECT_EQ(arguments.at(1).value<int>(), 0); - EXPECT_EQ(arguments.at(2).value<int>(), 0); -} - -//! Remove one of two top level items. - -TEST_F(DefaultViewModelTest, removeOneOfTopItems) -{ - SessionModel model; - - // inserting single item - model.insertItem<SessionItem>(); - model.insertItem<SessionItem>(); - - // constructing viewModel from sample model - DefaultViewModel viewModel(&model); - - // root item should have one child - EXPECT_EQ(viewModel.rowCount(), 2); - EXPECT_EQ(viewModel.columnCount(), 2); - - QSignalSpy spyRemove(&viewModel, &DefaultViewModel::rowsRemoved); - QSignalSpy spyInsert(&viewModel, &DefaultViewModel::rowsInserted); - - // removing child - model.removeItem(model.rootItem(), {"", 0}); - - // removal was called once - EXPECT_EQ(spyRemove.count(), 1); - EXPECT_EQ(viewModel.rowCount(), 1); - EXPECT_EQ(viewModel.columnCount(), 2); - - // no call to insert - EXPECT_EQ(spyInsert.count(), 0); - - QList<QVariant> arguments = spyRemove.takeFirst(); - EXPECT_EQ(arguments.size(), 3); // QModelIndex &parent, int first, int last - EXPECT_EQ(arguments.at(0).value<QModelIndex>(), QModelIndex()); - EXPECT_EQ(arguments.at(1).value<int>(), 0); - EXPECT_EQ(arguments.at(2).value<int>(), 0); -} - -//! Single property item in ViewModel with various appearance flags. - -TEST_F(DefaultViewModelTest, propertyItemAppearance) -{ - SessionModel model; - - // default item - auto item1 = model.insertItem<PropertyItem>(); - item1->setData(42.0); - - // disabled item - auto item2 = model.insertItem<PropertyItem>(); - item2->setData(42.0); - item2->setEnabled(false); // our convention: gray color, read only - - // read only item - auto item3 = model.insertItem<PropertyItem>(); - item3->setData(42.0); - item3->setEditable(false); // our convention: normal color, read only - - // making view model - DefaultViewModel viewModel(&model); - - // In tests below is important that SessionItem::isEnabled==false means that item is - // shown in gray color. While QStandardItem::isEnabled means "no interaction". - // In our case QStandardItem::isEnabled should be always true. - - // ViewLabel and ViewDataItem of item1 - EXPECT_FALSE(viewModel.flags(viewModel.index(0, 0)) & Qt::ItemIsEditable); - EXPECT_TRUE(viewModel.flags(viewModel.index(0, 1)) & Qt::ItemIsEditable); - - // ViewLabel and ViewDataItem of item2 - EXPECT_FALSE(viewModel.flags(viewModel.index(1, 0)) & Qt::ItemIsEditable); - EXPECT_FALSE(viewModel.flags(viewModel.index(1, 1)) & Qt::ItemIsEditable); - - // ViewLabel and ViewDataItem of item2 - EXPECT_FALSE(viewModel.flags(viewModel.index(2, 0)) & Qt::ItemIsEditable); - EXPECT_FALSE(viewModel.flags(viewModel.index(2, 1)) & Qt::ItemIsEditable); -} - -//! Signals in ViewModel when property item changes its appearance. - -TEST_F(DefaultViewModelTest, propertyItemAppearanceChanged) -{ - SessionModel model; - - // default item - auto item = model.insertItem<PropertyItem>(); - item->setData(42.0); - - // setting up ViewModel and spying it's dataChanged signals - DefaultViewModel viewModel(&model); - auto labelView = viewModel.itemFromIndex(viewModel.index(0, 0)); - auto dataView = viewModel.itemFromIndex(viewModel.index(0, 1)); - QSignalSpy spyDataChanged(&viewModel, &DefaultViewModel::dataChanged); - - // Changing item appearance - item->setEnabled(false); - EXPECT_EQ(spyDataChanged.count(), 2); // change in LabelView and DataView - - // first pack of arguments is related to ViewLabelItem - QList<QVariant> arguments = spyDataChanged.takeFirst(); - EXPECT_EQ(arguments.size(), 3); // QModelIndex &parent, int first, int last - auto index1 = arguments.at(0).value<QModelIndex>(); - auto index2 = arguments.at(1).value<QModelIndex>(); - auto roles = arguments.at(2).value<QVector<int>>(); - EXPECT_EQ(index1, viewModel.indexFromItem(labelView)); - EXPECT_EQ(index2, viewModel.indexFromItem(labelView)); -#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) - QVector<int> expected_roles = {Qt::ForegroundRole}; -#else - QVector<int> expected_roles = {Qt::TextColorRole}; -#endif - EXPECT_EQ(roles, expected_roles); - - // second pack of arguments is related to ViewDataItem - arguments = spyDataChanged.takeFirst(); - EXPECT_EQ(arguments.size(), 3); // QModelIndex &parent, int first, int last - index1 = arguments.at(0).value<QModelIndex>(); - index2 = arguments.at(1).value<QModelIndex>(); - roles = arguments.at(2).value<QVector<int>>(); - EXPECT_EQ(index1, viewModel.indexFromItem(dataView)); - EXPECT_EQ(index2, viewModel.indexFromItem(dataView)); - -#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) - expected_roles = {Qt::ForegroundRole}; -#else - expected_roles = {Qt::TextColorRole}; -#endif - - EXPECT_EQ(roles, expected_roles); -} - -//! Signals in ViewModel when property item changes its tooltips. - -TEST_F(DefaultViewModelTest, tooltipChanged) -{ - SessionModel model; - - // default item - auto item = model.insertItem<PropertyItem>(); - item->setData(42.0); - item->setToolTip("abc"); - - // setting up ViewModel and spying it's dataChanged signals - DefaultViewModel viewModel(&model); - auto labelView = viewModel.itemFromIndex(viewModel.index(0, 0)); - auto dataView = viewModel.itemFromIndex(viewModel.index(0, 1)); - - EXPECT_EQ(viewModel.data(viewModel.index(0, 0), Qt::ToolTipRole).toString(), QString("abc")); - EXPECT_EQ(viewModel.data(viewModel.index(0, 1), Qt::ToolTipRole).toString(), QString("abc")); - - QSignalSpy spyDataChanged(&viewModel, &DefaultViewModel::dataChanged); - - // Changing tooltip - item->setToolTip("abc2"); - EXPECT_EQ(spyDataChanged.count(), 2); // change in LabelView and DataView - - // first pack of arguments is related to ViewLabelItem - QList<QVariant> arguments = spyDataChanged.takeFirst(); - EXPECT_EQ(arguments.size(), 3); // QModelIndex &parent, int first, int last - auto index1 = arguments.at(0).value<QModelIndex>(); - auto index2 = arguments.at(1).value<QModelIndex>(); - auto roles = arguments.at(2).value<QVector<int>>(); - EXPECT_EQ(index1, viewModel.indexFromItem(labelView)); - EXPECT_EQ(index2, viewModel.indexFromItem(labelView)); - QVector<int> expected_roles = {Qt::ToolTipRole}; - EXPECT_EQ(roles, expected_roles); - - // second pack of arguments is related to ViewDataItem - arguments = spyDataChanged.takeFirst(); - EXPECT_EQ(arguments.size(), 3); // QModelIndex &parent, int first, int last - index1 = arguments.at(0).value<QModelIndex>(); - index2 = arguments.at(1).value<QModelIndex>(); - roles = arguments.at(2).value<QVector<int>>(); - EXPECT_EQ(index1, viewModel.indexFromItem(dataView)); - EXPECT_EQ(index2, viewModel.indexFromItem(dataView)); - - expected_roles = {Qt::ToolTipRole}; - EXPECT_EQ(roles, expected_roles); -} - -//! Setting property item as ROOT item. - -TEST_F(DefaultViewModelTest, setPropertyItemAsRoot) -{ - SessionModel model; - DefaultViewModel viewModel(&model); - - QSignalSpy spyAboutReset(&viewModel, &DefaultViewModel::modelAboutToBeReset); - QSignalSpy spyReset(&viewModel, &DefaultViewModel::modelReset); - - auto item = model.insertItem<PropertyItem>(); - viewModel.setRootSessionItem(item); - - // new root item doesn't have children - EXPECT_EQ(viewModel.rowCount(), 0); - EXPECT_EQ(viewModel.columnCount(), 0); - - EXPECT_EQ(spyAboutReset.count(), 1); - EXPECT_EQ(spyReset.count(), 1); - - // attempt to use nullptr as root item - EXPECT_THROW(viewModel.setRootSessionItem(nullptr), std::runtime_error); - - // attempt to use alien model - SessionModel model2; - EXPECT_THROW(viewModel.setRootSessionItem(model2.rootItem()), std::runtime_error); -} - -//! Setting property item as ROOT item. -//! Same as above, only view model was initialized after. - -TEST_F(DefaultViewModelTest, setPropertyItemAsRootAfter) -{ - SessionModel model; - auto item = model.insertItem<PropertyItem>(); - - DefaultViewModel viewModel(&model); - EXPECT_EQ(viewModel.rowCount(), 1); - EXPECT_EQ(viewModel.columnCount(), 2); - - QSignalSpy spyAboutReset(&viewModel, &DefaultViewModel::modelAboutToBeReset); - QSignalSpy spyReset(&viewModel, &DefaultViewModel::modelReset); - viewModel.setRootSessionItem(item); - - // new root item doesn't have children - EXPECT_EQ(viewModel.rowCount(), 0); - EXPECT_EQ(viewModel.columnCount(), 0); - - EXPECT_EQ(spyAboutReset.count(), 1); - EXPECT_EQ(spyReset.count(), 1); - - // attempt to use nullptr as root item - EXPECT_THROW(viewModel.setRootSessionItem(nullptr), std::runtime_error); - - // attempt to use alien model - SessionModel model2; - EXPECT_THROW(viewModel.setRootSessionItem(model2.rootItem()), std::runtime_error); -} - -//! Setting top level item as ROOT item (case parent and children). - -TEST_F(DefaultViewModelTest, setCompoundAsRootItem) -{ - SessionModel model; - DefaultViewModel viewModel(&model); - - auto item = model.insertItem<CompoundItem>(); - item->addProperty("thickness", 42.0); - item->addProperty<VectorItem>("position"); - item->addProperty("radius", 43.0); - - viewModel.setRootSessionItem(item); - - EXPECT_EQ(viewModel.rowCount(), 3); - EXPECT_EQ(viewModel.columnCount(), 2); - - // checking vector item - auto index_of_vector_item = viewModel.index(1, 0); - EXPECT_EQ(viewModel.rowCount(index_of_vector_item), 3); - EXPECT_EQ(viewModel.columnCount(index_of_vector_item), 2); -} - -//! Setting vector item as ROOT item. - -TEST_F(DefaultViewModelTest, setVectorItemAsRoot) -{ - SessionModel model; - auto vectorItem = model.insertItem<VectorItem>(); - - DefaultViewModel viewModel(&model); - viewModel.setRootSessionItem(vectorItem); - - EXPECT_EQ(viewModel.rowCount(), 3); - EXPECT_EQ(viewModel.columnCount(), 2); -} - -//! On model destroyed. - -TEST_F(DefaultViewModelTest, onModelReset) -{ - auto model = std::make_unique<SessionModel>(); - model->insertItem<SessionItem>(); - model->insertItem<SessionItem>(); - model->insertItem<SessionItem>(); - - DefaultViewModel viewModel(model.get()); - - QSignalSpy spyAboutReset(&viewModel, &DefaultViewModel::modelAboutToBeReset); - QSignalSpy spyReset(&viewModel, &DefaultViewModel::modelReset); - - model->clear(); - EXPECT_EQ(viewModel.rowCount(), 0); - EXPECT_EQ(viewModel.columnCount(), 0); - - EXPECT_EQ(spyAboutReset.count(), 1); - EXPECT_EQ(spyReset.count(), 1); -} - -//! On model destroyed. - -TEST_F(DefaultViewModelTest, onModelDestroyed) -{ - auto model = std::make_unique<SessionModel>(); - model->insertItem<SessionItem>(); - - DefaultViewModel viewModel(model.get()); - EXPECT_EQ(viewModel.rowCount(), 1); - EXPECT_EQ(viewModel.columnCount(), 2); - - model.reset(); - EXPECT_EQ(viewModel.rowCount(), 0); - EXPECT_EQ(viewModel.columnCount(), 0); -} - -TEST_F(DefaultViewModelTest, fromVector) -{ - SessionModel model; - auto vectorItem = model.insertItem<VectorItem>(); - - // constructing viewModel from sample model - DefaultViewModel viewModel(&model); - - // root item should have one child, item looking at our vectorItem - EXPECT_EQ(viewModel.rowCount(), 1); - EXPECT_EQ(viewModel.columnCount(), 2); - - // accessing to viewItem representing layerItem - QModelIndex vectorIndex = viewModel.index(0, 0); - - // it has three rows and two columns, corresponding to our P_X, P_Y, P_Z - EXPECT_EQ(viewModel.rowCount(vectorIndex), 3); - EXPECT_EQ(viewModel.columnCount(vectorIndex), 2); - - // ViewLabelItem and ViewDataItem correspondint to P_X - auto pxLabel = - dynamic_cast<ViewLabelItem*>(viewModel.itemFromIndex(viewModel.index(0, 0, vectorIndex))); - auto pxData = - dynamic_cast<ViewDataItem*>(viewModel.itemFromIndex(viewModel.index(0, 1, vectorIndex))); - EXPECT_EQ(pxLabel->item(), vectorItem->getItem(VectorItem::P_X)); - EXPECT_EQ(pxData->item(), vectorItem->getItem(VectorItem::P_X)); - - // ViewLabelItem and ViewDataItem correspondint to P_Y - pxLabel = - dynamic_cast<ViewLabelItem*>(viewModel.itemFromIndex(viewModel.index(1, 0, vectorIndex))); - pxData = - dynamic_cast<ViewDataItem*>(viewModel.itemFromIndex(viewModel.index(1, 1, vectorIndex))); - EXPECT_EQ(pxLabel->item(), vectorItem->getItem(VectorItem::P_Y)); - EXPECT_EQ(pxData->item(), vectorItem->getItem(VectorItem::P_Y)); - - // ViewLabelItem and ViewDataItem correspondint to P_Z - pxLabel = - dynamic_cast<ViewLabelItem*>(viewModel.itemFromIndex(viewModel.index(2, 0, vectorIndex))); - pxData = - dynamic_cast<ViewDataItem*>(viewModel.itemFromIndex(viewModel.index(2, 1, vectorIndex))); - EXPECT_EQ(pxLabel->item(), vectorItem->getItem(VectorItem::P_Z)); - EXPECT_EQ(pxData->item(), vectorItem->getItem(VectorItem::P_Z)); -} - -TEST_F(DefaultViewModelTest, horizontalLabels) -{ - SessionModel model; - model.insertItem<VectorItem>(); - - // constructing viewModel from sample model - DefaultViewModel viewModel(&model); - - EXPECT_EQ(viewModel.headerData(0, Qt::Horizontal, Qt::DisplayRole).toString(), QString("Name")); - EXPECT_EQ(viewModel.headerData(1, Qt::Horizontal, Qt::DisplayRole).toString(), - QString("Value")); -} - -//! Testing ViewModel signals while loading data with the help of json loader. - -TEST_F(DefaultViewModelTest, jsonConverterLoadModel) -{ - JsonModelConverter converter(ConverterMode::project); - QJsonObject object; - - // preparing jsob object - { - SessionModel model("TestModel"); - model.insertItem<PropertyItem>(); - JsonModelConverter converter(ConverterMode::project); - // writing model to json - object = converter.to_json(model); - } - - // loading model - SessionModel model("TestModel"); - DefaultViewModel viewmodel(&model); - EXPECT_EQ(viewmodel.rowCount(), 0); - EXPECT_EQ(viewmodel.columnCount(), 0); - - QSignalSpy spyInsert(&viewmodel, &DefaultViewModel::rowsInserted); - QSignalSpy spyRemove(&viewmodel, &DefaultViewModel::rowsRemoved); - QSignalSpy spyAboutReset(&viewmodel, &DefaultViewModel::modelAboutToBeReset); - QSignalSpy spyReset(&viewmodel, &DefaultViewModel::modelReset); - - converter.from_json(object, model); - - EXPECT_EQ(spyInsert.count(), 1); // FIXME shouldn't it be '0'? - EXPECT_EQ(spyRemove.count(), 0); - EXPECT_EQ(spyAboutReset.count(), 1); - EXPECT_EQ(spyReset.count(), 1); - - EXPECT_EQ(viewmodel.rowCount(), 1); - EXPECT_EQ(viewmodel.columnCount(), 2); -} - -//! Testing ViewModel signals while loading data with the help of json document. -//! Model is empty. - -TEST_F(DefaultViewModelTest, jsonDocumentLoadEmptyModel) -{ - auto fileName = TestUtils::TestFileName(testDir(), "jsonDocumentLoadEmptyModel.json"); - - // preparing jsob object - { - SessionModel model("TestModel"); - JsonDocument document({&model}); - document.save(fileName); - } - - // loading model - SessionModel model("TestModel"); - DefaultViewModel viewmodel(&model); - EXPECT_EQ(viewmodel.rowCount(), 0); - EXPECT_EQ(viewmodel.columnCount(), 0); - - JsonDocument document({&model}); - - QSignalSpy spyInsert(&viewmodel, &DefaultViewModel::rowsInserted); - QSignalSpy spyRemove(&viewmodel, &DefaultViewModel::rowsRemoved); - QSignalSpy spyAboutReset(&viewmodel, &DefaultViewModel::modelAboutToBeReset); - QSignalSpy spyReset(&viewmodel, &DefaultViewModel::modelReset); - - document.load(fileName); - - EXPECT_EQ(spyInsert.count(), 0); - EXPECT_EQ(spyRemove.count(), 0); - EXPECT_EQ(spyAboutReset.count(), 1); - EXPECT_EQ(spyReset.count(), 1); - - EXPECT_EQ(viewmodel.rowCount(), 0); - EXPECT_EQ(viewmodel.columnCount(), 0); -} - -//! Testing ViewModel signals while loading data with the help of json document. -//! Model is empty. - -TEST_F(DefaultViewModelTest, jsonDocumentLoadModel) -{ - auto fileName = TestUtils::TestFileName(testDir(), "jsonDocumentLoadModel.json"); - - // preparing jsob object - { - SessionModel model("TestModel"); - JsonDocument document({&model}); - model.insertItem<PropertyItem>(); - document.save(fileName); - } - - // loading model - SessionModel model("TestModel"); - DefaultViewModel viewmodel(&model); - EXPECT_EQ(viewmodel.rowCount(), 0); - EXPECT_EQ(viewmodel.columnCount(), 0); - - JsonDocument document({&model}); - - QSignalSpy spyInsert(&viewmodel, &DefaultViewModel::rowsInserted); - QSignalSpy spyRemove(&viewmodel, &DefaultViewModel::rowsRemoved); - QSignalSpy spyAboutReset(&viewmodel, &DefaultViewModel::modelAboutToBeReset); - QSignalSpy spyReset(&viewmodel, &DefaultViewModel::modelReset); - - document.load(fileName); - - EXPECT_EQ(spyInsert.count(), 1); - EXPECT_EQ(spyRemove.count(), 0); - EXPECT_EQ(spyAboutReset.count(), 1); - EXPECT_EQ(spyReset.count(), 1); - - EXPECT_EQ(viewmodel.rowCount(), 1); - EXPECT_EQ(viewmodel.columnCount(), 2); -} - -//! Testing view model after restoring from json document. - -TEST_F(DefaultViewModelTest, vectorItemInJsonDocument) -{ - auto fileName = TestUtils::TestFileName(testDir(), "vectorItemInJsonDocument.json"); - - SessionModel model; - model.insertItem<VectorItem>(); - - // constructing viewModel from sample model - DefaultViewModel viewmodel(&model); - - // root item should have one child, item looking at our vectorItem - EXPECT_EQ(viewmodel.rowCount(), 1); - EXPECT_EQ(viewmodel.columnCount(), 2); - - JsonDocument document({&model}); - document.save(fileName); - - // cleaning original model - model.clear(); - - QSignalSpy spyInsert(&viewmodel, &DefaultViewModel::rowsInserted); - QSignalSpy spyRemove(&viewmodel, &DefaultViewModel::rowsRemoved); - QSignalSpy spyAboutReset(&viewmodel, &DefaultViewModel::modelAboutToBeReset); - QSignalSpy spyReset(&viewmodel, &DefaultViewModel::modelReset); - - document.load(fileName); - - EXPECT_EQ(spyInsert.count(), 4); - EXPECT_EQ(spyRemove.count(), 0); - EXPECT_EQ(spyAboutReset.count(), 1); - EXPECT_EQ(spyReset.count(), 1); - - EXPECT_EQ(viewmodel.rowCount(), 1); - EXPECT_EQ(viewmodel.columnCount(), 2); -} - -//! Testing view model after restoring from json document. -//! VectorItem is made root item. Test demonstrates that controller is capable -//! to restore old rootSessionItem on onModelReset signal - -TEST_F(DefaultViewModelTest, vectorItemAsRootInJsonDocument) -{ - auto fileName = TestUtils::TestFileName(testDir(), "vectorItemAsRootInJsonDocument.json"); - - SessionModel model; - auto vectorItem = model.insertItem<VectorItem>(); - - // constructing viewModel from sample model - DefaultViewModel viewmodel(&model); - viewmodel.setRootSessionItem(vectorItem); - - // root item should have one child, item looking at our vectorItem - EXPECT_EQ(viewmodel.rowCount(), 3); - EXPECT_EQ(viewmodel.columnCount(), 2); - EXPECT_EQ(viewmodel.rootSessionItem(), vectorItem); - - JsonDocument document({&model}); - document.save(fileName); - - // model.clear(); // if we uncomment this, information about rootSessionItem will be lost - - QSignalSpy spyInsert(&viewmodel, &DefaultViewModel::rowsInserted); - QSignalSpy spyRemove(&viewmodel, &DefaultViewModel::rowsRemoved); - QSignalSpy spyAboutReset(&viewmodel, &DefaultViewModel::modelAboutToBeReset); - QSignalSpy spyReset(&viewmodel, &DefaultViewModel::modelReset); - - document.load(fileName); - - EXPECT_EQ(spyInsert.count(), 3); - EXPECT_EQ(spyRemove.count(), 0); - EXPECT_EQ(spyAboutReset.count(), 1); - EXPECT_EQ(spyReset.count(), 1); - - EXPECT_EQ(viewmodel.rootSessionItem(), model.rootItem()->children().at(0)); // vectorItem - - EXPECT_EQ(viewmodel.rowCount(), 3); - EXPECT_EQ(viewmodel.columnCount(), 2); -} - -//! Real life bug. One container with Data1DItem's, one ViewportItem with single graph. -//! DefaultViewModel is looking on ViewPortItem. Graph is deleted first. - -TEST_F(DefaultViewModelTest, deleteGraphVromViewport) -{ - SessionModel model; - - // creating data container and single Data1DItem in it - auto data_container = model.insertItem<ContainerItem>(); - auto data_item = model.insertItem<Data1DItem>(data_container); - data_item->setAxis<FixedBinAxisItem>(3, 0.0, 3.0); - data_item->setValues(std::vector<double>({1.0, 2.0, 3.0})); - - // creating Viewport with single graph - auto viewport_item = model.insertItem<GraphViewportItem>(); - auto graph_item = model.insertItem<GraphItem>(viewport_item); - graph_item->setDataItem(data_item); - - DefaultViewModel viewmodel(&model); - viewmodel.setRootSessionItem(viewport_item); - - // validating that we see graph at propeer index - EXPECT_EQ(viewmodel.rowCount(), 3); // X, Y and Graph - QModelIndex graph_index = viewmodel.index(2, 0); - auto graphLabel = dynamic_cast<ViewLabelItem*>(viewmodel.itemFromIndex(graph_index)); - ASSERT_TRUE(graphLabel != nullptr); - EXPECT_EQ(graphLabel->item(), graph_item); - - // removing graph item - model.removeItem(graph_item->parent(), graph_item->tagRow()); - EXPECT_EQ(viewmodel.rowCount(), 2); // X, Y - - graph_item = model.insertItem<GraphItem>(viewport_item); - EXPECT_EQ(viewmodel.rowCount(), 3); // X, Y -} diff --git a/mvvm/tests/testviewmodel/labeldatarowstrategy.test.cpp b/mvvm/tests/testviewmodel/labeldatarowstrategy.test.cpp deleted file mode 100644 index 5331740488a..00000000000 --- a/mvvm/tests/testviewmodel/labeldatarowstrategy.test.cpp +++ /dev/null @@ -1,82 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testviewmodel/labeldatarowstrategy.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/viewmodel/labeldatarowstrategy.h" -#include "mvvm/viewmodel/standardviewitems.h" -#include "test_utils.h" - -namespace { -const int expected_column_count = 2; -const QStringList expected_labels = QStringList() << "Name" - << "Value"; -} // namespace - -using namespace ModelView; - -class LabelDataRowStrategyTest : public ::testing::Test { -public: - ~LabelDataRowStrategyTest(); -}; - -LabelDataRowStrategyTest::~LabelDataRowStrategyTest() = default; - -TEST_F(LabelDataRowStrategyTest, initialState) -{ - LabelDataRowStrategy constructor; - EXPECT_EQ(constructor.constructRow(nullptr).size(), 0); - EXPECT_EQ(constructor.horizontalHeaderLabels(), expected_labels); -} - -//! Checks row construction for standard top level item, like Level, MultiLayer etc. - -TEST_F(LabelDataRowStrategyTest, topLevelItem) -{ - SessionItem item("model_type"); - - LabelDataRowStrategy constructor; - auto items = constructor.constructRow(&item); - EXPECT_EQ(items.size(), expected_column_count); // label and empty items - EXPECT_EQ(constructor.horizontalHeaderLabels(), expected_labels); - - // checking that it is label and data - auto labelItem = dynamic_cast<ViewLabelItem*>(items.at(0).get()); - auto dataItem = dynamic_cast<ViewDataItem*>(items.at(1).get()); - ASSERT_TRUE(labelItem != nullptr); - EXPECT_EQ(labelItem->item(), &item); - ASSERT_TRUE(dataItem != nullptr); - EXPECT_EQ(dataItem->item(), &item); -} - -//! Checks row construction for property item. - -TEST_F(LabelDataRowStrategyTest, propertyItem) -{ - SessionItem item("model_type"); - item.setData(42.0); - - LabelDataRowStrategy constructor; - auto items = constructor.constructRow(&item); - EXPECT_EQ(items.size(), expected_column_count); - EXPECT_EQ(constructor.horizontalHeaderLabels(), expected_labels); - - // checking that it is label and data - auto labelItem = dynamic_cast<ViewLabelItem*>(items.at(0).get()); - auto dataItem = dynamic_cast<ViewDataItem*>(items.at(1).get()); - ASSERT_TRUE(labelItem != nullptr); - EXPECT_EQ(labelItem->item(), &item); - ASSERT_TRUE(dataItem != nullptr); - EXPECT_EQ(dataItem->item(), &item); -} diff --git a/mvvm/tests/testviewmodel/propertiesrowstrategy.test.cpp b/mvvm/tests/testviewmodel/propertiesrowstrategy.test.cpp deleted file mode 100644 index 7f6faa3402f..00000000000 --- a/mvvm/tests/testviewmodel/propertiesrowstrategy.test.cpp +++ /dev/null @@ -1,162 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testviewmodel/propertiesrowstrategy.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/standarditems/vectoritem.h" -#include "mvvm/viewmodel/propertiesrowstrategy.h" -#include "mvvm/viewmodel/standardviewitems.h" -#include "test_utils.h" - -using namespace ModelView; - -class PropertiesRowStrategyTest : public ::testing::Test { -public: - ~PropertiesRowStrategyTest(); -}; - -PropertiesRowStrategyTest::~PropertiesRowStrategyTest() = default; - -TEST_F(PropertiesRowStrategyTest, initialState) -{ - PropertiesRowStrategy strategy; - EXPECT_EQ(strategy.constructRow(nullptr).size(), 0); - EXPECT_EQ(strategy.horizontalHeaderLabels(), QStringList()); -} - -//! Checks row construction for standard top level item. It shouldn't generate any rows. - -TEST_F(PropertiesRowStrategyTest, topLevelItem) -{ - SessionItem item("model_type"); - - PropertiesRowStrategy strategy; - auto items = strategy.constructRow(&item); - EXPECT_EQ(items.size(), 0); - EXPECT_EQ(strategy.horizontalHeaderLabels(), QStringList()); -} - -//! Checks row construction for property item. It shouldn't generate any rows. - -TEST_F(PropertiesRowStrategyTest, propertyItem) -{ - SessionItem item("model_type"); - item.setData(42.0); - - PropertiesRowStrategy strategy; - auto items = strategy.constructRow(&item); - EXPECT_EQ(items.size(), 0); - EXPECT_EQ(strategy.horizontalHeaderLabels(), QStringList()); -} - -//! Checks row construction for vector item. -//! There should be 3 view items looking to x, y, z properties. - -TEST_F(PropertiesRowStrategyTest, vectorItemCustomLabels) -{ - VectorItem item; - - EXPECT_EQ(item.property<double>(VectorItem::P_X), 0.0); - EXPECT_EQ(item.property<double>(VectorItem::P_Y), 0.0); - EXPECT_EQ(item.property<double>(VectorItem::P_Z), 0.0); - - PropertiesRowStrategy strategy({"a", "b", "c"}); - auto items = strategy.constructRow(&item); - - EXPECT_EQ(items.size(), 3); - EXPECT_EQ(strategy.horizontalHeaderLabels(), QStringList() << "a" - << "b" - << "c"); - - // views should look at 3 property items - auto view_x = items.at(0).get(); - EXPECT_EQ(view_x->item(), item.getItem(VectorItem::P_X)); - - auto view_y = items.at(1).get(); - EXPECT_EQ(view_y->item(), item.getItem(VectorItem::P_Y)); - - auto view_z = items.at(2).get(); - EXPECT_EQ(view_z->item(), item.getItem(VectorItem::P_Z)); -} - -//! Checks row label construction for vector item. - -TEST_F(PropertiesRowStrategyTest, vectorItemAutoLabels) -{ - VectorItem item; - - EXPECT_EQ(item.property<double>(VectorItem::P_X), 0.0); - EXPECT_EQ(item.property<double>(VectorItem::P_Y), 0.0); - EXPECT_EQ(item.property<double>(VectorItem::P_Z), 0.0); - - QStringList expected = QStringList() << "X" - << "Y" - << "Z"; - - PropertiesRowStrategy strategy; - auto items = strategy.constructRow(&item); - EXPECT_EQ(strategy.horizontalHeaderLabels(), expected); -} - -//! Row construction for rootItem with single item inserted. Shouldn't generate any row. - -TEST_F(PropertiesRowStrategyTest, baseItemInModelContext) -{ - SessionModel model; - - PropertiesRowStrategy strategy; - auto items = strategy.constructRow(model.rootItem()); - EXPECT_EQ(items.size(), 0); - - model.insertItem<SessionItem>(); - items = strategy.constructRow(model.rootItem()); - EXPECT_EQ(items.size(), 0); -} - -//! Row construction for rootItem with single item inserted. Shouldn't generate any row. - -TEST_F(PropertiesRowStrategyTest, propertyItemTree) -{ - SessionModel model; - auto parent = model.insertItem<SessionItem>(); - - parent->registerTag(TagInfo::universalTag("universal_tag")); - parent->registerTag(TagInfo::propertyTag("property_tag", GUI::Constants::PropertyType)); - - model.insertItem<SessionItem>(parent, "universal_tag"); - model.insertItem<PropertyItem>(parent, "property_tag"); - - PropertiesRowStrategy strategy; - auto items = strategy.constructRow(model.rootItem()); - - // root item doesn't have properties - EXPECT_EQ(items.size(), 0); - - // parent has one registered property. - items = strategy.constructRow(parent); - EXPECT_EQ(items.size(), 1); -} - -//! Row construction for rootItem when vectorItem is present. Shouldn't generate any row. - -TEST_F(PropertiesRowStrategyTest, vectorItemInModelContext) -{ - SessionModel model; - model.insertItem<VectorItem>(); - - PropertiesRowStrategy strategy; - auto items = strategy.constructRow(model.rootItem()); - EXPECT_EQ(items.size(), 0); -} diff --git a/mvvm/tests/testviewmodel/propertyflatviewmodel.test.cpp b/mvvm/tests/testviewmodel/propertyflatviewmodel.test.cpp deleted file mode 100644 index 06fcefdb7ad..00000000000 --- a/mvvm/tests/testviewmodel/propertyflatviewmodel.test.cpp +++ /dev/null @@ -1,122 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testviewmodel/propertyflatviewmodel.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/propertyitem.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/model/taginfo.h" -#include "mvvm/standarditems/vectoritem.h" -#include "mvvm/viewmodel/propertyflatviewmodel.h" -#include "toyitems.h" -#include "toymodel.h" - -using namespace ModelView; - -//! Tests for PropertyFlatViewModel class. - -class PropertyFlatViewModelTest : public ::testing::Test { -public: - ~PropertyFlatViewModelTest(); -}; - -PropertyFlatViewModelTest::~PropertyFlatViewModelTest() = default; - -TEST_F(PropertyFlatViewModelTest, initialState) -{ - SessionModel model; - PropertyFlatViewModel viewModel(&model); - EXPECT_EQ(viewModel.rowCount(), 0); - EXPECT_EQ(viewModel.columnCount(), 0); - EXPECT_EQ(viewModel.sessionItemFromIndex(QModelIndex()), model.rootItem()); -} - -TEST_F(PropertyFlatViewModelTest, baseItem) -{ - SessionModel model; - model.insertItem<SessionItem>(); - - PropertyFlatViewModel viewModel(&model); - - // Root item has default tag and all items considered as top items. - // PropertyViewModel shouldn't see any items. - EXPECT_EQ(viewModel.rowCount(), 0); - EXPECT_EQ(viewModel.columnCount(), 0); -} - -TEST_F(PropertyFlatViewModelTest, propertyItem) -{ - SessionModel model; - auto parent = model.insertItem<SessionItem>(); - - parent->registerTag(TagInfo::universalTag("universal_tag")); - parent->registerTag(TagInfo::propertyTag("property_tag", GUI::Constants::PropertyType)); - - model.insertItem<SessionItem>(parent, "universal_tag"); - model.insertItem<PropertyItem>(parent, "property_tag"); - model.insertItem<SessionItem>(parent, "universal_tag"); - - PropertyFlatViewModel viewModel(&model); - viewModel.setRootSessionItem(parent); - - // View model should see only property item belonging to parent. - EXPECT_EQ(viewModel.rowCount(), 1); - EXPECT_EQ(viewModel.columnCount(), 2); -} - -//! VectorItem in a model. - -TEST_F(PropertyFlatViewModelTest, vectorItem) -{ - SessionModel model; - auto parent = model.insertItem<VectorItem>(); - - PropertyFlatViewModel viewModel(&model); - - EXPECT_EQ(viewModel.rowCount(), 0); // root item doesn't have properties - EXPECT_EQ(viewModel.columnCount(), 0); - - // switching to vectorItem and checking that it has 3 properties - viewModel.setRootSessionItem(parent); - EXPECT_EQ(viewModel.rowCount(), 3); - EXPECT_EQ(viewModel.columnCount(), 2); -} - -//! ParticleItem in a model - -TEST_F(PropertyFlatViewModelTest, particleItem) -{ - ToyItems::SampleModel model; - auto particle = model.insertItem<ToyItems::ParticleItem>(); - auto group = dynamic_cast<GroupItem*>(particle->getItem(ToyItems::ParticleItem::P_SHAPES)); - group->setCurrentType(ToyItems::GUI::Constants::SphereItemType); - - PropertyFlatViewModel viewModel(&model); - viewModel.setRootSessionItem(particle); - - // We should see 3 rows: VectorItem, GroupItem itself, and Radius of sphere - EXPECT_EQ(viewModel.rowCount(), 3); - EXPECT_EQ(viewModel.columnCount(), 2); - - // switching group - group->setCurrentType(ToyItems::GUI::Constants::CylinderItemType); - // We should see 3 rows: VectorItem, GroupItem itself, Cylinderr length and radius - EXPECT_EQ(viewModel.rowCount(), 4); - EXPECT_EQ(viewModel.columnCount(), 2); - - // switching back - group->setCurrentType(ToyItems::GUI::Constants::SphereItemType); - EXPECT_EQ(viewModel.rowCount(), 3); - EXPECT_EQ(viewModel.columnCount(), 2); -} diff --git a/mvvm/tests/testviewmodel/propertytableviewmodel.test.cpp b/mvvm/tests/testviewmodel/propertytableviewmodel.test.cpp deleted file mode 100644 index 1f416312168..00000000000 --- a/mvvm/tests/testviewmodel/propertytableviewmodel.test.cpp +++ /dev/null @@ -1,135 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testviewmodel/propertytableviewmodel.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/propertyitem.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/model/taginfo.h" -#include "mvvm/standarditems/vectoritem.h" -#include "mvvm/viewmodel/propertytableviewmodel.h" -#include "toyitems.h" - -using namespace ModelView; - -class PropertyTableViewModelTest : public ::testing::Test { -public: - ~PropertyTableViewModelTest(); -}; - -PropertyTableViewModelTest::~PropertyTableViewModelTest() = default; - -TEST_F(PropertyTableViewModelTest, initialState) -{ - SessionModel model; - PropertyTableViewModel viewModel(&model); - EXPECT_EQ(viewModel.rowCount(), 0); - EXPECT_EQ(viewModel.columnCount(), 0); - EXPECT_EQ(viewModel.sessionItemFromIndex(QModelIndex()), model.rootItem()); -} - -TEST_F(PropertyTableViewModelTest, baseItem) -{ - SessionModel model; - model.insertItem<SessionItem>(); - - PropertyTableViewModel viewModel(&model); - - EXPECT_EQ(viewModel.rowCount(), 0); - EXPECT_EQ(viewModel.columnCount(), 0); -} - -TEST_F(PropertyTableViewModelTest, propertyItem) -{ - SessionModel model; - auto parent = model.insertItem<SessionItem>(); - - parent->registerTag(TagInfo::universalTag("universal_tag")); - parent->registerTag(TagInfo::propertyTag("property_tag", GUI::Constants::PropertyType)); - - model.insertItem<SessionItem>(parent, "universal_tag"); - model.insertItem<PropertyItem>(parent, "property_tag"); - model.insertItem<SessionItem>(parent, "universal_tag"); - - PropertyTableViewModel viewModel(&model); - - // one cell corresponding to single item at property_tag of our parent - EXPECT_EQ(viewModel.rowCount(), 1); - EXPECT_EQ(viewModel.columnCount(), 1); - - viewModel.setRootSessionItem(parent); - EXPECT_EQ(viewModel.rowCount(), 0); - EXPECT_EQ(viewModel.columnCount(), 0); -} - -//! VectorItem in a model. - -TEST_F(PropertyTableViewModelTest, vectorItem) -{ - SessionModel model; - auto parent = model.insertItem<VectorItem>(); - - PropertyTableViewModel viewModel(&model); - - EXPECT_EQ(viewModel.rowCount(), 1); - EXPECT_EQ(viewModel.columnCount(), 3); - - // switching to vectorItem and checking that it has 3 properties - viewModel.setRootSessionItem(parent); - EXPECT_EQ(viewModel.rowCount(), 0); - EXPECT_EQ(viewModel.columnCount(), 0); -} - -//! MultiLayer with layers, view model still looks to the RootItem. -//! No MultiLayer should be visible in table. - -TEST_F(PropertyTableViewModelTest, multiLayerAndRootItem) -{ - SessionModel model; - auto multilayer = model.insertItem<ToyItems::MultiLayerItem>(); - model.insertItem<ToyItems::LayerItem>(multilayer); - model.insertItem<ToyItems::LayerItem>(multilayer); - - PropertyTableViewModel viewModel(&model); - - // ViewModel should be empty, since we are looking to RootItem. - EXPECT_EQ(viewModel.rowCount(), 0); - EXPECT_EQ(viewModel.columnCount(), 0); -} - -//! MultiLayer with layers, multilayer is given as root index. - -TEST_F(PropertyTableViewModelTest, multiLayer) -{ - SessionModel model; - auto multilayer = model.insertItem<ToyItems::MultiLayerItem>(); - model.insertItem<ToyItems::LayerItem>(multilayer); - model.insertItem<ToyItems::LayerItem>(multilayer); - - PropertyTableViewModel viewModel(&model); - viewModel.setRootSessionItem(multilayer); - - EXPECT_EQ(viewModel.rowCount(), 2); // two layers - EXPECT_EQ(viewModel.columnCount(), 2); // layer thickness and color - - // add another layer - model.insertItem<ToyItems::LayerItem>(multilayer); - EXPECT_EQ(viewModel.rowCount(), 3); // two layers - EXPECT_EQ(viewModel.columnCount(), 2); // layer thickness and color - - // switching view model back to model's root, table should be empty - viewModel.setRootSessionItem(model.rootItem()); - EXPECT_EQ(viewModel.rowCount(), 0); // two layers - EXPECT_EQ(viewModel.columnCount(), 0); // layer thickness and color -} diff --git a/mvvm/tests/testviewmodel/propertyviewmodel.test.cpp b/mvvm/tests/testviewmodel/propertyviewmodel.test.cpp deleted file mode 100644 index 101051411c1..00000000000 --- a/mvvm/tests/testviewmodel/propertyviewmodel.test.cpp +++ /dev/null @@ -1,116 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testviewmodel/propertyviewmodel.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/propertyitem.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/model/taginfo.h" -#include "mvvm/standarditems/vectoritem.h" -#include "mvvm/viewmodel/propertyviewmodel.h" -#include "toyitems.h" -#include "toymodel.h" - -using namespace ModelView; - -//! Tests for PropertyViewModel class. - -class PropertyViewModelTest : public ::testing::Test { -public: - ~PropertyViewModelTest(); -}; - -PropertyViewModelTest::~PropertyViewModelTest() = default; - -TEST_F(PropertyViewModelTest, initialState) -{ - SessionModel model; - PropertyViewModel viewModel(&model); - EXPECT_EQ(viewModel.rowCount(), 0); - EXPECT_EQ(viewModel.columnCount(), 0); - EXPECT_EQ(viewModel.sessionItemFromIndex(QModelIndex()), model.rootItem()); -} - -TEST_F(PropertyViewModelTest, baseItem) -{ - SessionModel model; - model.insertItem<SessionItem>(); - - PropertyViewModel viewModel(&model); - - // Root item has default tag and all items considered as top items. - // PropertyViewModel shouldn't see any items. - EXPECT_EQ(viewModel.rowCount(), 0); - EXPECT_EQ(viewModel.columnCount(), 0); -} - -TEST_F(PropertyViewModelTest, propertyItem) -{ - SessionModel model; - auto parent = model.insertItem<SessionItem>(); - - parent->registerTag(TagInfo::universalTag("universal_tag")); - parent->registerTag(TagInfo::propertyTag("property_tag", GUI::Constants::PropertyType)); - - model.insertItem<SessionItem>(parent, "universal_tag"); - model.insertItem<PropertyItem>(parent, "property_tag"); - model.insertItem<SessionItem>(parent, "universal_tag"); - - PropertyViewModel viewModel(&model); - viewModel.setRootSessionItem(parent); - - // View model should see only property item belonging to parent. - EXPECT_EQ(viewModel.rowCount(), 1); - EXPECT_EQ(viewModel.columnCount(), 2); -} - -//! VectorItem in a model. - -TEST_F(PropertyViewModelTest, vectorItem) -{ - SessionModel model; - auto parent = model.insertItem<VectorItem>(); - - PropertyViewModel viewModel(&model); - - EXPECT_EQ(viewModel.rowCount(), 0); // root item doesn't have properties - EXPECT_EQ(viewModel.columnCount(), 0); - - // switching to vectorItem and checking that it has 3 properties - viewModel.setRootSessionItem(parent); - EXPECT_EQ(viewModel.rowCount(), 3); - EXPECT_EQ(viewModel.columnCount(), 2); -} - -//! LayerItem in a MultiLayer. - -TEST_F(PropertyViewModelTest, layerInMultiLayerAsRootItem) -{ - SessionModel model; - auto multilayer = model.insertItem<ToyItems::MultiLayerItem>(); - auto layer = model.insertItem<ToyItems::LayerItem>(multilayer); - - PropertyViewModel viewmodel(&model); - viewmodel.setRootSessionItem(layer); - - // check layer thickness and color - EXPECT_EQ(viewmodel.rowCount(), 2); - EXPECT_EQ(viewmodel.columnCount(), 2); - - // remove multilayer - model.removeItem(model.rootItem(), {"", 0}); - - EXPECT_EQ(viewmodel.rowCount(), 0); - EXPECT_EQ(viewmodel.columnCount(), 0); -} diff --git a/mvvm/tests/testviewmodel/scientificspinbox.test.cpp b/mvvm/tests/testviewmodel/scientificspinbox.test.cpp deleted file mode 100644 index 80140ca03aa..00000000000 --- a/mvvm/tests/testviewmodel/scientificspinbox.test.cpp +++ /dev/null @@ -1,128 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testviewmodel/scientificspinbox.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/editors/scientificspinbox.h" -#include <limits> - -using namespace ModelView; - -class ScientificSpinBoxTest : public ::testing::Test { -public: - ~ScientificSpinBoxTest() override; -}; - -ScientificSpinBoxTest::~ScientificSpinBoxTest() = default; - -TEST_F(ScientificSpinBoxTest, testValueFromText) -{ - QLocale locale(QLocale::C); - locale.setNumberOptions(QLocale::RejectGroupSeparator); - - QDoubleValidator validator; - validator.setLocale(locale); - validator.setNotation(QDoubleValidator::ScientificNotation); - - auto to_value = [&validator](QString text) { - return ScientificSpinBox::toDouble(text, validator, std::numeric_limits<double>::lowest(), - std::numeric_limits<double>::max(), 0.1); - }; - - // translation fails - EXPECT_EQ(0.1, to_value(QString("abcd"))); - EXPECT_EQ(0.1, to_value(QString("1,2"))); - EXPECT_EQ(0.1, to_value(QString("100,000,000.2"))); - EXPECT_EQ(0.1, to_value(QString("100.000.000.2"))); - EXPECT_EQ(0.1, to_value(QString("1e+2345"))); - EXPECT_EQ(0.1, to_value(QString("-1e+2345"))); - EXPECT_EQ(0.1, to_value(QString("1e-2345"))); - EXPECT_EQ(0.1, to_value(QString("-1e-2345"))); - EXPECT_EQ(0.1, to_value(QString("--0.1"))); - EXPECT_EQ(0.1, to_value(QString("-.e-12"))); - EXPECT_EQ(0.1, to_value(QString())); - - auto to_value_2 = [&validator](QString text) { - return ScientificSpinBox::toDouble(text, validator, -0.1, 1e+7, 0.1); - }; - - // translation fails due to out-of-bounds condition - EXPECT_EQ(0.1, to_value_2(QString("-0.2"))); - EXPECT_EQ(0.1, to_value_2(QString("-0.1e+1"))); - EXPECT_EQ(0.1, to_value_2(QString("1e+8"))); - - // legitimate values - EXPECT_EQ(-0.0999, to_value_2(QString("-0.0999"))); - EXPECT_EQ(-1e-13, to_value_2(QString("-.1e-12"))); - EXPECT_EQ(0.0, to_value_2(QString("0"))); - EXPECT_EQ(0.123, to_value_2(QString("0.123"))); - EXPECT_EQ(1e+6, to_value_2(QString("1e+6"))); - EXPECT_EQ(1.1e+6, to_value_2(QString("1.1e+6"))); - EXPECT_EQ(0.012, to_value_2(QString("0.012"))); -} - -TEST_F(ScientificSpinBoxTest, toString) -{ - int decimals = 3; - auto to_string = [&decimals](double val) { return ScientificSpinBox::toString(val, decimals); }; - - EXPECT_EQ(std::string("-123.45"), to_string(-123.45).toStdString()); - EXPECT_EQ(std::string("-100"), to_string(-99.9999).toStdString()); - EXPECT_EQ(std::string("-99.999"), to_string(-99.9994).toStdString()); - EXPECT_EQ(std::string("-10.123"), to_string(-10.12345).toStdString()); - EXPECT_EQ(std::string("-1"), to_string(-1.).toStdString()); - EXPECT_EQ(std::string("-0.1"), to_string(-0.1).toStdString()); - EXPECT_EQ(std::string("-0.1"), to_string(-0.1).toStdString()); - EXPECT_EQ(std::string("-9.99e-2"), to_string(-9.99e-2).toStdString()); - EXPECT_EQ(std::string("-1.266e-12"), to_string(-1.26555e-12).toStdString()); - EXPECT_EQ(std::string("0"), to_string(-0.0).toStdString()); - EXPECT_EQ(std::string("0"), to_string(0.0).toStdString()); - EXPECT_EQ(std::string("1e-12"), to_string(1.e-12).toStdString()); - EXPECT_EQ(std::string("1.23e-12"), to_string(1.23e-12).toStdString()); - EXPECT_EQ(std::string("1e-2"), to_string(1.e-2).toStdString()); - EXPECT_EQ(std::string("1.5e-2"), to_string(1.5e-2).toStdString()); - EXPECT_EQ(std::string("1.523e-2"), to_string(1.5234e-2).toStdString()); - EXPECT_EQ(std::string("9.99e-2"), to_string(9.99e-2).toStdString()); - EXPECT_EQ(std::string("1e-1"), to_string(9.9999e-2).toStdString()); - EXPECT_EQ(std::string("0.1"), to_string(0.1).toStdString()); - EXPECT_EQ(std::string("1"), to_string(1.).toStdString()); - EXPECT_EQ(std::string("1.1"), to_string(1.1).toStdString()); - EXPECT_EQ(std::string("1.123"), to_string(1.12345).toStdString()); - EXPECT_EQ(std::string("10.123"), to_string(10.12345).toStdString()); - EXPECT_EQ(std::string("99.9"), to_string(99.9).toStdString()); - EXPECT_EQ(std::string("99.999"), to_string(99.9994).toStdString()); - EXPECT_EQ(std::string("100"), to_string(99.9999).toStdString()); - EXPECT_EQ(std::string("123.45"), to_string(123.45).toStdString()); - EXPECT_EQ(std::string("1e+4"), to_string(1.e+4).toStdString()); - EXPECT_EQ(std::string("1.265e+12"), to_string(1.265e+12).toStdString()); - EXPECT_EQ(std::string("1.266e+12"), to_string(1.26555e+12).toStdString()); - - decimals = 5; - EXPECT_EQ(std::string("1.23e-12"), to_string(1.23e-12).toStdString()); - EXPECT_EQ(std::string("1.52346e-2"), to_string(1.523456e-2).toStdString()); - EXPECT_EQ(std::string("1e-1"), to_string(9.999999e-2).toStdString()); - EXPECT_EQ(std::string("1.12346"), to_string(1.123455).toStdString()); - EXPECT_EQ(std::string("10.12346"), to_string(10.123456).toStdString()); - EXPECT_EQ(std::string("99.9"), to_string(99.9).toStdString()); - EXPECT_EQ(std::string("100"), to_string(99.999999).toStdString()); - EXPECT_EQ(std::string("123.45"), to_string(123.45).toStdString()); - EXPECT_EQ(std::string("1.26556e+12"), to_string(1.265556e+12).toStdString()); -} - -TEST_F(ScientificSpinBoxTest, round) -{ - auto round_3 = [](double val) { return ScientificSpinBox::round(val, 3); }; - EXPECT_DOUBLE_EQ(1.232e-12, round_3(1.2323e-12)); - EXPECT_DOUBLE_EQ(0.123, round_3(0.1232)); - EXPECT_DOUBLE_EQ(1.002e+2, round_3(100.2)); -} diff --git a/mvvm/tests/testviewmodel/standardchildrenstrategies.test.cpp b/mvvm/tests/testviewmodel/standardchildrenstrategies.test.cpp deleted file mode 100644 index 5463c8d230a..00000000000 --- a/mvvm/tests/testviewmodel/standardchildrenstrategies.test.cpp +++ /dev/null @@ -1,263 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testviewmodel/standardchildrenstrategies.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/compounditem.h" -#include "mvvm/model/propertyitem.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/taginfo.h" -#include "mvvm/standarditems/vectoritem.h" -#include "mvvm/viewmodel/standardchildrenstrategies.h" -#include "toyitems.h" -#include "toymodel.h" - -using namespace ModelView; - -class StandardChildrenStrategiesTest : public ::testing::Test { -public: - ~StandardChildrenStrategiesTest(); - - //! Helper class with two properties and one top level item on board. - class TestItem : public CompoundItem { - public: - TestItem() : CompoundItem("test") - { - addProperty("length", 8.0); - registerTag(TagInfo::universalTag("children"), /*set_as_default*/ true); - insertItem(new SessionItem, TagRow::append()); - addProperty("height", 12.0); - } - ~TestItem(); - }; - - struct ChildrenData { - std::string model_type; - std::string tag; - bool operator==(const ChildrenData& other) const - { - return model_type == other.model_type && tag == other.tag; - } - }; - - std::vector<ChildrenData> children_data(std::vector<SessionItem*> children) - { - std::vector<ChildrenData> result; - for (auto child : children) - result.push_back({child->modelType(), child->tagRow().tag}); - return result; - } -}; - -StandardChildrenStrategiesTest::~StandardChildrenStrategiesTest() = default; -StandardChildrenStrategiesTest::TestItem::~TestItem() = default; - -//! Testing AllChildrenStrategy. - -TEST_F(StandardChildrenStrategiesTest, AllChildrenStrategy) -{ - AllChildrenStrategy strategy; - - // nullptr - auto children = strategy.children(nullptr); - EXPECT_EQ(children.size(), 0); - - // empty item - SessionItem item1("model_type"); - children = strategy.children(&item1); - EXPECT_EQ(children.size(), 0); - - // VectorItem - VectorItem item2; - children = strategy.children(&item2); - EXPECT_EQ(children.size(), 3); - - // CompoundItem - CompoundItem item3; - item3.addProperty("height", 42.0); - children = strategy.children(&item3); - EXPECT_EQ(children.size(), 1); - - // TestItem - TestItem item4; - children = strategy.children(&item4); - EXPECT_EQ(children.size(), 3); - - // GroupItem - ToyItems::ShapeGroupItem item5; - item5.setCurrentType(ToyItems::GUI::Constants::CylinderItemType); - children = strategy.children(&item5); - EXPECT_EQ(children.size(), 3); // number of registered children -} - -//! Testing TopItemsStrategy. - -TEST_F(StandardChildrenStrategiesTest, TopItemsStrategy) -{ - TopItemsStrategy strategy; - - // nullptr - auto children = strategy.children(nullptr); - EXPECT_EQ(children.size(), 0); - - // empty item - SessionItem item1("model_type"); - children = strategy.children(&item1); - EXPECT_EQ(children.size(), 0); - - // VectorItem - VectorItem item2; - children = strategy.children(&item2); - EXPECT_EQ(children.size(), 0); - - // CompoundItem - CompoundItem item3; - item3.addProperty("height", 42.0); - children = strategy.children(&item3); - EXPECT_EQ(children.size(), 0); - - // TestItem - TestItem item4; - children = strategy.children(&item4); - EXPECT_EQ(children.size(), 1); - - // GroupItem - ToyItems::ShapeGroupItem item5; - item5.setCurrentType(ToyItems::GUI::Constants::CylinderItemType); - children = strategy.children(&item5); - EXPECT_EQ(children.size(), 3); // number of registered children -} - -//! Testing PropertyItemsStrategy. - -TEST_F(StandardChildrenStrategiesTest, PropertyItemsStrategy) -{ - PropertyItemsStrategy strategy; - - // nullptr - { - auto children = strategy.children(nullptr); - EXPECT_EQ(children.size(), 0); - } - - // empty item - { - SessionItem item("model_type"); - auto children = strategy.children(&item); - EXPECT_EQ(children.size(), 0); - } - - // VectorItem - { - VectorItem item; - auto children = strategy.children(&item); - EXPECT_EQ(children.size(), 3); - } - - // CompoundItem - { - CompoundItem item; - item.addProperty("height", 42.0); - auto children = strategy.children(&item); - EXPECT_EQ(children.size(), 1); - } - - // TestItem - { - TestItem item; - auto children = strategy.children(&item); - EXPECT_EQ(children.size(), 2); - } - - // GroupItem - { - ToyItems::ShapeGroupItem item; - item.setCurrentType(ToyItems::GUI::Constants::CylinderItemType); - auto children = strategy.children(&item); - EXPECT_EQ(children.size(), 2); - - std::vector<ChildrenData> expected_children_data{ - {GUI::Constants::PropertyType, ToyItems::CylinderItem::P_RADIUS}, - {GUI::Constants::PropertyType, ToyItems::CylinderItem::P_HEIGHT}}; - EXPECT_EQ(children_data(children), expected_children_data); - } -} - -//! Testing PropertyItemsFlatStrategy. - -TEST_F(StandardChildrenStrategiesTest, PropertyItemsFlatStrategy) -{ - PropertyItemsFlatStrategy strategy; - - // nullptr - { - auto children = strategy.children(nullptr); - EXPECT_EQ(children.size(), 0); - } - - // empty item - { - SessionItem item("model_type"); - auto children = strategy.children(&item); - EXPECT_EQ(children.size(), 0); - } - - // VectorItem - { - VectorItem item; - auto children = strategy.children(&item); - EXPECT_EQ(children.size(), 3); - } - - // CompoundItem - { - CompoundItem item; - item.addProperty("height", 42.0); - auto children = strategy.children(&item); - EXPECT_EQ(children.size(), 1); - } - - // TestItem - { - TestItem item; - auto children = strategy.children(&item); - EXPECT_EQ(children.size(), 2); - } - - // GroupItem - { - ToyItems::ShapeGroupItem item; - item.setCurrentType(ToyItems::GUI::Constants::CylinderItemType); - auto children = strategy.children(&item); - EXPECT_EQ(children.size(), 2); - - std::vector<ChildrenData> expected_children_data{ - {GUI::Constants::PropertyType, ToyItems::CylinderItem::P_RADIUS}, - {GUI::Constants::PropertyType, ToyItems::CylinderItem::P_HEIGHT}}; - EXPECT_EQ(children_data(children), expected_children_data); - } - - // ParticleItem - { - ToyItems::ParticleItem item; - auto children = strategy.children(&item); - EXPECT_EQ(children.size(), 3); - - std::vector<ChildrenData> expected_children_data{ - {GUI::Constants::VectorItemType, ToyItems::ParticleItem::P_POSITION}, - {ToyItems::GUI::Constants::ShapeGroupItemType, ToyItems::ParticleItem::P_SHAPES}, - {GUI::Constants::PropertyType, ToyItems::SphereItem::P_RADIUS}}; - - EXPECT_EQ(children_data(children), expected_children_data); - } -} diff --git a/mvvm/tests/testviewmodel/standardviewitems.test.cpp b/mvvm/tests/testviewmodel/standardviewitems.test.cpp deleted file mode 100644 index 0cdce6d947d..00000000000 --- a/mvvm/tests/testviewmodel/standardviewitems.test.cpp +++ /dev/null @@ -1,269 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testviewmodel/standardviewitems.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/viewmodel/standardviewitems.h" -#include <QColor> -#include <memory> -#include <stdexcept> - -using namespace ModelView; - -class StandardViewItemsTest : public ::testing::Test { -public: - ~StandardViewItemsTest(); -}; - -StandardViewItemsTest::~StandardViewItemsTest() = default; - -// ---------------------------------------------------------------------------- -// Tests for ViewLabelItem -// ---------------------------------------------------------------------------- - -TEST_F(StandardViewItemsTest, ViewLabelItem_initialState) -{ - SessionItem item; - ViewLabelItem viewItem(&item); - EXPECT_EQ(viewItem.item(), &item); - EXPECT_EQ(viewItem.item_role(), ItemDataRole::DISPLAY); -} - -//! ViewLabelItem::data method -//! Checks that the data method is correctly forwarded to underlying SessionItem. - -TEST_F(StandardViewItemsTest, ViewLabelItem_data) -{ - // create SessionItem with data on board - SessionItem item; - const std::string expected("Layer"); - EXPECT_TRUE(item.setData(expected, ItemDataRole::DISPLAY)); - - // initialize viewItem with sessionItem and check the data - ViewLabelItem viewItem(&item); - EXPECT_EQ(Utils::toCustomVariant(viewItem.data(Qt::EditRole)), QVariant::fromValue(expected)); - EXPECT_EQ(Utils::toCustomVariant(viewItem.data(Qt::DisplayRole)), - QVariant::fromValue(expected)); -} - -//! ViewLabelItem::setData -//! Checks that the setData method is correctly forwarded to underlying SessionItem. - -TEST_F(StandardViewItemsTest, ViewLabelItem_setData) -{ - // create SessionItem with data on board - SessionItem item; - const std::string expected("Layer"); - EXPECT_TRUE(item.setData(expected, ItemDataRole::DISPLAY)); - - // initialize viewItem with sessionItem and set the data - ViewLabelItem viewItem(&item); - QVariant new_data("MultiLayer"); - EXPECT_TRUE(viewItem.setData(new_data, Qt::EditRole)); - EXPECT_EQ(viewItem.data(Qt::DisplayRole), new_data); // new data - EXPECT_EQ(viewItem.data(Qt::EditRole), new_data); // new data - - // SessionItem itself should have new data - EXPECT_EQ(item.data<QVariant>(ItemDataRole::DISPLAY), - Utils::toCustomVariant(new_data)); // new data - - // it is not allowed to set another type of data to ViewLabelItem - QVariant not_allowed_value(42); - EXPECT_THROW(viewItem.setData(not_allowed_value, Qt::EditRole), std::runtime_error); -} - -//! Testing ViewLabelItem::flags. - -TEST_F(StandardViewItemsTest, ViewLabelItem_flags) -{ - SessionItem item; - const std::string expected("Layer"); - EXPECT_TRUE(item.setData(expected, ItemDataRole::DISPLAY)); - - ViewLabelItem viewItem(&item); - EXPECT_FALSE(viewItem.flags() & Qt::ItemIsEditable); -} - -//! Testing tooltip tole. - -TEST_F(StandardViewItemsTest, ViewLabelItem_toolTipRole) -{ - SessionItem item; - - ViewLabelItem viewItem(&item); - EXPECT_FALSE(viewItem.data(Qt::ToolTipRole).isValid()); - - item.setToolTip("abc"); - EXPECT_EQ(viewItem.data(Qt::ToolTipRole).toString(), QString("abc")); -} - -// ---------------------------------------------------------------------------- -// Tests for ViewDataItem -// ---------------------------------------------------------------------------- - -TEST_F(StandardViewItemsTest, ViewDataItem_initialState) -{ - SessionItem item; - ViewDataItem viewItem(&item); - EXPECT_EQ(viewItem.item(), &item); - EXPECT_EQ(viewItem.item_role(), ItemDataRole::DATA); -} - -//! ViewDataItem::data method for double values. -//! Checks that the data method is correctly forwarded to underlying SessionItem. - -TEST_F(StandardViewItemsTest, ViewDataItem_dataForDouble) -{ - // create SessionItem with data on board - SessionItem item; - QVariant expected(42.0); - EXPECT_TRUE(item.setData(expected)); - - // initialize viewItem with sessionItem and check the data - ViewDataItem viewItem(&item); - EXPECT_EQ(viewItem.data(Qt::EditRole), expected); - EXPECT_EQ(viewItem.data(Qt::DisplayRole), expected); -} - -//! ViewDataItem::setData for double values. -//! Checks that the setData method is correctly forwarded to underlying SessionItem. - -TEST_F(StandardViewItemsTest, ViewDataItem_setDataForDouble) -{ - // create SessionItem with data on board - SessionItem item; - QVariant expected(42.0); - EXPECT_TRUE(item.setData(expected)); - - // initialize viewItem with sessionItem and set the data - ViewDataItem viewItem(&item); - QVariant new_data(43.0); - EXPECT_TRUE(viewItem.setData(new_data, Qt::EditRole)); - EXPECT_EQ(viewItem.data(Qt::DisplayRole), new_data); // new data - EXPECT_EQ(viewItem.data(Qt::EditRole), new_data); // new data - - // SessionItem itself should have new data - EXPECT_EQ(item.data<QVariant>(), new_data); // new data - - // it is not allowed to set another type of data to ViewDataItem - QVariant not_allowed_value("Layer"); - EXPECT_THROW(viewItem.setData(not_allowed_value, Qt::EditRole), std::runtime_error); -} - -//! ViewDataItem::setData for double values. -//! Checks that setting of same data returns false. - -TEST_F(StandardViewItemsTest, ViewDataItem_setSameData) -{ - // create SessionItem with data on board - SessionItem item; - QVariant expected(42.0); - EXPECT_TRUE(item.setData(expected)); - - // initialize viewItem with sessionItem and set the data - ViewDataItem viewItem(&item); - QVariant new_data(42.0); - EXPECT_FALSE(viewItem.setData(new_data, Qt::EditRole)); - EXPECT_EQ(viewItem.data(Qt::EditRole), new_data); // new data -} - -//! ViewDataItem::data method for QColor. -//! Checks that the data method is correctly forwarded to underlying SessionItem. - -TEST_F(StandardViewItemsTest, ViewDataItem_dataForColor) -{ - // create SessionItem with data on board - SessionItem item; - QVariant expected = QVariant::fromValue(QColor(Qt::green)); - EXPECT_TRUE(item.setData(expected)); - - ViewDataItem viewItem(&item); - EXPECT_EQ(viewItem.data(Qt::EditRole), expected); - EXPECT_EQ(viewItem.data(Qt::DisplayRole), expected); - EXPECT_EQ(viewItem.data(Qt::DecorationRole), expected); -} - -//! ViewDataItem::setData for QColor. -//! Checks that the setData method is correctly forwarded to underlying SessionItem. - -TEST_F(StandardViewItemsTest, ViewDataItem_setDataForColor) -{ - // create SessionItem with data on board - SessionItem item; - QVariant expected = QVariant::fromValue(QColor(Qt::green)); - EXPECT_TRUE(item.setData(expected)); - - // initialize viewItem with sessionItem and set the data - ViewDataItem viewItem(&item); - QVariant new_data = QVariant::fromValue(QColor(Qt::red)); - EXPECT_TRUE(viewItem.setData(new_data, Qt::EditRole)); - EXPECT_EQ(viewItem.data(Qt::DisplayRole), new_data); // new data - EXPECT_EQ(viewItem.data(Qt::EditRole), new_data); // new data - EXPECT_EQ(viewItem.data(Qt::DecorationRole), new_data); // new data - - // SessionItem itself should have new data - EXPECT_EQ(item.data<QVariant>(), new_data); // new data - - // it is not allowed to set another type of data to ViewDataItem - QVariant not_allowed_value("Layer"); - EXPECT_THROW(viewItem.setData(not_allowed_value, Qt::EditRole), std::runtime_error); -} - -//! Testing ViewLabelItem::flags. - -TEST_F(StandardViewItemsTest, ViewDataItem_flags) -{ - SessionItem item; - QVariant expected = QVariant::fromValue(std::string("Layer")); - EXPECT_TRUE(item.setData(expected, ItemDataRole::DATA)); - - ViewDataItem viewItem(&item); - EXPECT_TRUE(viewItem.flags() & Qt::ItemIsEditable); -} - -//! Testing tooltip tole. - -TEST_F(StandardViewItemsTest, ViewDataItem_toolTipRole) -{ - SessionItem item; - - ViewDataItem viewItem(&item); - EXPECT_FALSE(viewItem.data(Qt::ToolTipRole).isValid()); - - item.setToolTip("abc"); - EXPECT_EQ(viewItem.data(Qt::ToolTipRole).toString(), QString("abc")); -} - -//! Behavior of ViewDataItem with SessionItem which initially contains invalid data. -//! It should be possible to set data after. - -TEST_F(StandardViewItemsTest, ViewDataItem_invalidTheValidData) -{ - SessionItem item; - ViewDataItem viewItem(&item); - - // initially data is invalid, and non-editable - EXPECT_FALSE(viewItem.flags() & Qt::ItemIsEditable); - EXPECT_FALSE(viewItem.data(Qt::EditRole).isValid()); - - // current behavior that setting data is still possible - EXPECT_TRUE(viewItem.setData(QVariant::fromValue(42.0), Qt::EditRole)); - - // setting data to original item - item.setData(43.0); - - // data became editable via ViewDataItem - EXPECT_TRUE(viewItem.flags() & Qt::ItemIsEditable); - EXPECT_EQ(viewItem.data(Qt::EditRole), QVariant::fromValue(43.0)); -} diff --git a/mvvm/tests/testviewmodel/topitemsviewmodel.test.cpp b/mvvm/tests/testviewmodel/topitemsviewmodel.test.cpp deleted file mode 100644 index fe93c132340..00000000000 --- a/mvvm/tests/testviewmodel/topitemsviewmodel.test.cpp +++ /dev/null @@ -1,144 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testviewmodel/topitemsviewmodel.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/propertyitem.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/model/taginfo.h" -#include "mvvm/standarditems/vectoritem.h" -#include "mvvm/viewmodel/topitemsviewmodel.h" -#include "toyitems.h" -#include "toymodel.h" -#include <QSignalSpy> - -using namespace ModelView; - -//! Tests for TopItemsViewModel class. - -class TopItemsViewModelTest : public ::testing::Test { -public: - ~TopItemsViewModelTest(); -}; - -TopItemsViewModelTest::~TopItemsViewModelTest() = default; - -TEST_F(TopItemsViewModelTest, initialState) -{ - SessionModel model; - TopItemsViewModel viewModel(&model); - EXPECT_EQ(viewModel.rowCount(), 0); - EXPECT_EQ(viewModel.columnCount(), 0); - EXPECT_EQ(viewModel.sessionItemFromIndex(QModelIndex()), model.rootItem()); -} - -//! Insert LayerItem in empty model. - -TEST_F(TopItemsViewModelTest, insertLayerThenRemove) -{ - ToyItems::SampleModel model; - TopItemsViewModel viewmodel(&model); - - QSignalSpy spyInsert(&viewmodel, &ViewModelBase::rowsInserted); - QSignalSpy spyRemove(&viewmodel, &ViewModelBase::rowsRemoved); - - model.insertItem<ToyItems::LayerItem>(); - - EXPECT_EQ(viewmodel.rowCount(QModelIndex()), 1); - EXPECT_EQ(viewmodel.columnCount(QModelIndex()), 2); - - EXPECT_EQ(spyRemove.count(), 0); - EXPECT_EQ(spyInsert.count(), 1); - - model.removeItem(model.rootItem(), {"", 0}); - EXPECT_EQ(spyRemove.count(), 1); - EXPECT_EQ(spyInsert.count(), 1); - - EXPECT_EQ(viewmodel.rowCount(QModelIndex()), 0); - EXPECT_EQ(viewmodel.columnCount(QModelIndex()), 0); -} - -//! Insert LayerItem in MultiLayer. - -TEST_F(TopItemsViewModelTest, insertLayerInMultiLayerThenRemove) -{ - ToyItems::SampleModel model; - TopItemsViewModel viewmodel(&model); - - QSignalSpy spyInsert(&viewmodel, &ViewModelBase::rowsInserted); - QSignalSpy spyRemove(&viewmodel, &ViewModelBase::rowsRemoved); - - // inserting multilayer - auto multilayer = model.insertItem<ToyItems::MultiLayerItem>(); - - EXPECT_EQ(viewmodel.rowCount(QModelIndex()), 1); - EXPECT_EQ(viewmodel.columnCount(QModelIndex()), 2); - EXPECT_EQ(spyRemove.count(), 0); - EXPECT_EQ(spyInsert.count(), 1); - - // insert layer - auto layer = model.insertItem<ToyItems::LayerItem>(multilayer); - EXPECT_EQ(viewmodel.rowCount(QModelIndex()), 1); - EXPECT_EQ(viewmodel.columnCount(QModelIndex()), 2); - EXPECT_EQ(spyRemove.count(), 0); - EXPECT_EQ(spyInsert.count(), 2); - - // checking their indices - auto multiiLayer = viewmodel.index(0, 0, QModelIndex()); - auto i_layer = viewmodel.index(0, 0, multiiLayer); - EXPECT_EQ(viewmodel.sessionItemFromIndex(multiiLayer), multilayer); - EXPECT_EQ(viewmodel.sessionItemFromIndex(i_layer), layer); - - // checking row and columns - EXPECT_EQ(viewmodel.rowCount(multiiLayer), 1); - EXPECT_EQ(viewmodel.columnCount(multiiLayer), 2); - EXPECT_EQ(viewmodel.rowCount(i_layer), 0); - EXPECT_EQ(viewmodel.columnCount(i_layer), 0); - - // removing layer - model.removeItem(multilayer, {"", 0}); - EXPECT_EQ(spyRemove.count(), 1); - EXPECT_EQ(spyInsert.count(), 2); - EXPECT_EQ(viewmodel.rowCount(multiiLayer), 0); - EXPECT_EQ(viewmodel.columnCount(multiiLayer), 0); -} - -//! Insert LayerItem in MultiLayer while multilayer is root item. Then deleting multilayer. - -TEST_F(TopItemsViewModelTest, multuLayerAsRooItem) -{ - ToyItems::SampleModel model; - TopItemsViewModel viewmodel(&model); - - // setting up single multilayer playing the role - auto multilayer = model.insertItem<ToyItems::MultiLayerItem>(); - viewmodel.setRootSessionItem(multilayer); - QSignalSpy spyInsert(&viewmodel, &ViewModelBase::rowsInserted); - QSignalSpy spyRemove(&viewmodel, &ViewModelBase::rowsRemoved); - - // initial conditions - EXPECT_EQ(viewmodel.rowCount(QModelIndex()), 0); - EXPECT_EQ(viewmodel.columnCount(QModelIndex()), 0); - EXPECT_EQ(viewmodel.sessionItemFromIndex(QModelIndex()), multilayer); // our new root - - // adding layer - model.insertItem<ToyItems::LayerItem>(multilayer); - EXPECT_EQ(viewmodel.rowCount(QModelIndex()), 1); - EXPECT_EQ(viewmodel.columnCount(QModelIndex()), 2); - EXPECT_EQ(spyRemove.count(), 0); - EXPECT_EQ(spyInsert.count(), 1); - - // removing multilayer - model.removeItem(model.rootItem(), {"", 0}); -} diff --git a/mvvm/tests/testviewmodel/viewitem.test.cpp b/mvvm/tests/testviewmodel/viewitem.test.cpp deleted file mode 100644 index 532b911fc1a..00000000000 --- a/mvvm/tests/testviewmodel/viewitem.test.cpp +++ /dev/null @@ -1,232 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testviewmodel/viewitem.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/viewmodel/viewitem.h" -#include "test_utils.h" -#include <stdexcept> - -using namespace ModelView; - -//! Tests for ViewItem class. - -class ViewItemTest : public ::testing::Test { -public: - ~ViewItemTest(); - class TestItem : public ViewItem { - public: - TestItem() : ViewItem(nullptr, 0) {} - ~TestItem() override; - }; - - using children_t = std::vector<std::unique_ptr<ViewItem>>; - using expected_t = std::vector<ViewItem*>; - - //! Helper function to get two vectors, each ncolumns length, in the form of a pair. - //! First vector contains unique_ptr objects, second vector bare pointers to same objects. - //! First vector is intended to be moved inside a model, second vector is to validate - //! the content of a model after the move. - - std::pair<children_t, expected_t> test_data(int ncolumns) - { - auto vector_of_unique = TestUtils::create_row<ViewItem, TestItem>(ncolumns); - auto vector_of_pointers = TestUtils::create_pointers(vector_of_unique); - return std::make_pair(std::move(vector_of_unique), std::move(vector_of_pointers)); - } -}; - -ViewItemTest::~ViewItemTest() = default; -ViewItemTest::TestItem::~TestItem() = default; - -//! Initial state of RefViewItem. - -TEST_F(ViewItemTest, initialState) -{ - TestItem view_item; - - EXPECT_EQ(view_item.rowCount(), 0); - EXPECT_EQ(view_item.columnCount(), 0); - EXPECT_EQ(view_item.row(), -1); - EXPECT_EQ(view_item.column(), -1); - EXPECT_EQ(view_item.parent(), nullptr); - EXPECT_THROW(view_item.child(0, 0), std::runtime_error); - EXPECT_EQ(view_item.item(), nullptr); - EXPECT_EQ(view_item.item_role(), 0); -} - -//! Append single item as row. - -TEST_F(ViewItemTest, appendRow) -{ - auto [children, expected] = test_data(/*ncolumns*/ 1); - - // appending row with single item - TestItem view_item; - view_item.appendRow(std::move(children)); - - // checking parent - EXPECT_EQ(view_item.rowCount(), 1); - EXPECT_EQ(view_item.columnCount(), 1); - EXPECT_EQ(view_item.child(0, 0), expected[0]); - EXPECT_THROW(view_item.child(0, 1), std::runtime_error); - - // checking appended child - EXPECT_EQ(expected[0]->parent(), &view_item); - EXPECT_EQ(expected[0]->row(), 0); - EXPECT_EQ(expected[0]->column(), 0); -} - -//! Remove row. - -TEST_F(ViewItemTest, removeRow) -{ - auto [children, expected] = test_data(/*ncolumns*/ 1); - - // appending row with single item - TestItem view_item; - view_item.appendRow(std::move(children)); - view_item.removeRow(0); - - // checking parent - EXPECT_EQ(view_item.rowCount(), 0); - EXPECT_EQ(view_item.columnCount(), 0); -} - -//! Append two rows with two items each. - -TEST_F(ViewItemTest, appendTwoRows) -{ - // preparing two rows of children, two columns each - auto [children_row0, expected_row0] = test_data(/*ncolumns*/ 2); - auto [children_row1, expected_row1] = test_data(/*ncolumns*/ 2); - - // appending rows - TestItem view_item; - view_item.appendRow(std::move(children_row0)); - view_item.appendRow(std::move(children_row1)); - - EXPECT_EQ(view_item.rowCount(), 2); - EXPECT_EQ(view_item.columnCount(), 2); - EXPECT_EQ(view_item.child(0, 0), expected_row0[0]); - EXPECT_EQ(view_item.child(0, 1), expected_row0[1]); - EXPECT_EQ(view_item.child(1, 0), expected_row1[0]); - EXPECT_EQ(view_item.child(1, 1), expected_row1[1]); - EXPECT_THROW(view_item.child(2, 2), std::runtime_error); - - // checking parents - EXPECT_EQ(expected_row0[0]->parent(), &view_item); - EXPECT_EQ(expected_row0[1]->parent(), &view_item); - EXPECT_EQ(expected_row1[0]->parent(), &view_item); - EXPECT_EQ(expected_row1[1]->parent(), &view_item); - - // checking row and column of children - EXPECT_EQ(expected_row0[0]->row(), 0); - EXPECT_EQ(expected_row0[1]->row(), 0); - EXPECT_EQ(expected_row1[0]->row(), 1); - EXPECT_EQ(expected_row1[1]->row(), 1); - EXPECT_EQ(expected_row0[0]->column(), 0); - EXPECT_EQ(expected_row0[1]->column(), 1); - EXPECT_EQ(expected_row1[0]->column(), 0); - EXPECT_EQ(expected_row1[1]->column(), 1); - - // attempt to add row with different amount of children should fail - auto [children_row2, expected_row2] = test_data(/*ncolumns*/ 1); - EXPECT_THROW(view_item.appendRow(std::move(children_row2)), std::runtime_error); - EXPECT_EQ(view_item.rowCount(), 2); - EXPECT_EQ(view_item.columnCount(), 2); -} - -//! Append two rows with two items each. - -TEST_F(ViewItemTest, insertRowsThenRemove) -{ - // preparing two rows of children, two columns each - auto [children_row0, expected_row0] = test_data(/*ncolumns*/ 2); - auto [children_row1, expected_row1] = test_data(/*ncolumns*/ 2); - auto [children_row2, expected_row2] = test_data(/*ncolumns*/ 2); - - // appending rows - TestItem view_item; - view_item.appendRow(std::move(children_row0)); - view_item.appendRow(std::move(children_row1)); - view_item.insertRow(1, std::move(children_row2)); // inserting in-between - - EXPECT_EQ(view_item.rowCount(), 3); - EXPECT_EQ(view_item.columnCount(), 2); - EXPECT_EQ(view_item.child(0, 0), expected_row0[0]); - EXPECT_EQ(view_item.child(0, 1), expected_row0[1]); - EXPECT_EQ(view_item.child(1, 0), expected_row2[0]); - EXPECT_EQ(view_item.child(1, 1), expected_row2[1]); - EXPECT_EQ(view_item.child(2, 0), expected_row1[0]); - EXPECT_EQ(view_item.child(2, 1), expected_row1[1]); - - // checking parents - EXPECT_EQ(expected_row0[0]->parent(), &view_item); - EXPECT_EQ(expected_row0[1]->parent(), &view_item); - EXPECT_EQ(expected_row1[0]->parent(), &view_item); - EXPECT_EQ(expected_row1[1]->parent(), &view_item); - EXPECT_EQ(expected_row2[0]->parent(), &view_item); - EXPECT_EQ(expected_row2[1]->parent(), &view_item); - - // checking row and column of children - EXPECT_EQ(expected_row0[0]->row(), 0); - EXPECT_EQ(expected_row0[1]->row(), 0); - EXPECT_EQ(expected_row1[0]->row(), 2); - EXPECT_EQ(expected_row1[1]->row(), 2); - EXPECT_EQ(expected_row2[0]->row(), 1); - EXPECT_EQ(expected_row2[1]->row(), 1); - EXPECT_EQ(expected_row0[0]->column(), 0); - EXPECT_EQ(expected_row0[1]->column(), 1); - EXPECT_EQ(expected_row1[0]->column(), 0); - EXPECT_EQ(expected_row1[1]->column(), 1); - EXPECT_EQ(expected_row2[0]->column(), 0); - EXPECT_EQ(expected_row2[1]->column(), 1); - - // removing middle row - view_item.removeRow(1); - EXPECT_EQ(view_item.child(0, 0), expected_row0[0]); - EXPECT_EQ(view_item.child(0, 1), expected_row0[1]); - EXPECT_EQ(view_item.child(1, 0), expected_row1[0]); - EXPECT_EQ(view_item.child(1, 1), expected_row1[1]); -} - -//! Clean item's children. - -TEST_F(ViewItemTest, clear) -{ - auto [children, expected] = test_data(/*ncolumns*/ 1); - - TestItem view_item; - view_item.appendRow(std::move(children)); - view_item.clear(); - - EXPECT_EQ(view_item.rowCount(), 0); - EXPECT_EQ(view_item.columnCount(), 0); -} - -TEST_F(ViewItemTest, children) -{ - auto [children_row0, expected_row0] = test_data(/*ncolumns*/ 2); - auto [children_row1, expected_row1] = test_data(/*ncolumns*/ 2); - - TestItem view_item; - view_item.appendRow(std::move(children_row0)); - view_item.appendRow(std::move(children_row1)); - - std::vector<ViewItem*> expected; - std::copy(expected_row0.begin(), expected_row0.end(), std::back_inserter(expected)); - std::copy(expected_row1.begin(), expected_row1.end(), std::back_inserter(expected)); - - EXPECT_EQ(view_item.children(), expected); -} diff --git a/mvvm/tests/testviewmodel/viewmodelbase.test.cpp b/mvvm/tests/testviewmodel/viewmodelbase.test.cpp deleted file mode 100644 index 8c5253b7484..00000000000 --- a/mvvm/tests/testviewmodel/viewmodelbase.test.cpp +++ /dev/null @@ -1,377 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testviewmodel/viewmodelbase.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/viewmodel/standardviewitems.h" -#include "mvvm/viewmodel/viewmodelbase.h" -#include "test_utils.h" -#include <QSignalSpy> -#include <QStandardItemModel> - -using namespace ModelView; - -//! Tests for ViewModelBase class. - -class ViewModelBaseTest : public ::testing::Test { -public: - ~ViewModelBaseTest(); - - class TestItem : public ViewItem { - public: - TestItem() : ViewItem(nullptr, 0) {} - ~TestItem() override; - }; - - using children_t = std::vector<std::unique_ptr<ViewItem>>; - using expected_t = std::vector<ViewItem*>; - - //! Helper function to get two vectors, each ncolumns length, in the form of a pair. - //! First vector contains unique_ptr objects, second vector bare pointers to same objects. - //! First vector is intended to be moved inside a model, second vector is to validate - //! the content of a model after the move. - - std::pair<children_t, expected_t> test_data(int ncolumns) - { - auto vector_of_unique = TestUtils::create_row<ViewItem, TestItem>(ncolumns); - auto vector_of_pointers = TestUtils::create_pointers(vector_of_unique); - return std::make_pair(std::move(vector_of_unique), std::move(vector_of_pointers)); - } -}; - -ViewModelBaseTest::~ViewModelBaseTest() = default; -ViewModelBaseTest::TestItem::~TestItem() = default; - -//! Checking behaviour of QStandardItemModel for reference. - -TEST_F(ViewModelBaseTest, standardItemModel) -{ - QStandardItemModel model; - auto parent = model.invisibleRootItem(); - - EXPECT_EQ(model.rowCount(), 0); - EXPECT_EQ(model.columnCount(), 0); - - QList<QStandardItem*> children{new QStandardItem, new QStandardItem}; - parent->appendRow(children); - auto index = model.index(0, 1, QModelIndex()); - EXPECT_EQ(model.itemFromIndex(index), children.at(1)); - - // construction of index for non-existing column leads to invalid index - auto non_existing_index = model.index(0, 2, QModelIndex()); - EXPECT_FALSE(non_existing_index.isValid()); - EXPECT_EQ(non_existing_index, QModelIndex()); - - // attempt to retrieve item using this non-existing index leads to nullptr. - EXPECT_EQ(model.itemFromIndex(non_existing_index), nullptr); - - // default constructed index gives same nullptr - EXPECT_EQ(model.itemFromIndex(QModelIndex()), nullptr); - - // to summarize, default-constructed index, invalid index and index leading to non-existing - // item are the same -} - -//! Initial state of empty ViewModelBase. - -TEST_F(ViewModelBaseTest, initialState) -{ - ViewModelBase viewmodel; - EXPECT_EQ(viewmodel.rowCount(), 0); - EXPECT_EQ(viewmodel.columnCount(), 0); - EXPECT_TRUE(viewmodel.rootItem() != nullptr); - EXPECT_EQ(viewmodel.itemFromIndex(QModelIndex()), nullptr); - auto non_existing_index = viewmodel.index(0, 0, QModelIndex()); - EXPECT_FALSE(non_existing_index.isValid()); - EXPECT_EQ(viewmodel.itemFromIndex(non_existing_index), nullptr); - EXPECT_EQ(viewmodel.parent(QModelIndex()), QModelIndex()); - EXPECT_EQ(viewmodel.indexFromItem(viewmodel.rootItem()), QModelIndex()); -} - -TEST_F(ViewModelBaseTest, appendRow) -{ - ViewModelBase viewmodel; - - // item to append - auto [children, expected] = test_data(/*ncolumns*/ 1); - - // appending one row - viewmodel.appendRow(viewmodel.rootItem(), std::move(children)); - EXPECT_EQ(viewmodel.rowCount(), 1); - EXPECT_EQ(viewmodel.columnCount(), 1); - - // constructing index for child - auto child_index = viewmodel.index(0, 0, QModelIndex()); - EXPECT_EQ(child_index.row(), 0); - EXPECT_EQ(child_index.column(), 0); - EXPECT_EQ(child_index.model(), &viewmodel); - - // indexFromItem - EXPECT_EQ(viewmodel.indexFromItem(expected[0]), child_index); - - // getting child from index - EXPECT_EQ(viewmodel.itemFromIndex(child_index), expected[0]); - - // no grand-children - EXPECT_EQ(viewmodel.rowCount(child_index), 0); - EXPECT_EQ(viewmodel.columnCount(child_index), 0); - - // parent index - EXPECT_EQ(viewmodel.parent(child_index), QModelIndex()); -} - -//! Insert one row befor another. - -TEST_F(ViewModelBaseTest, insertRow) -{ - ViewModelBase viewmodel; - - // item to append - auto [children_row0, expected_row0] = test_data(/*ncolumns*/ 1); - auto [children_front, expected_front] = test_data(/*ncolumns*/ 1); - - // appending one row - viewmodel.appendRow(viewmodel.rootItem(), std::move(children_row0)); - viewmodel.insertRow(viewmodel.rootItem(), 0, std::move(children_front)); - EXPECT_EQ(viewmodel.rowCount(), 2); - EXPECT_EQ(viewmodel.columnCount(), 1); - - // constructing index for child - auto child_index0 = viewmodel.index(0, 0, QModelIndex()); - auto child_index1 = viewmodel.index(1, 0, QModelIndex()); - - // indexFromItem - EXPECT_EQ(viewmodel.indexFromItem(expected_row0[0]), child_index1); - EXPECT_EQ(viewmodel.indexFromItem(expected_front[0]), child_index0); - - // getting child from index - EXPECT_EQ(viewmodel.itemFromIndex(child_index0), expected_front[0]); - EXPECT_EQ(viewmodel.itemFromIndex(child_index1), expected_row0[0]); -} - -TEST_F(ViewModelBaseTest, removeRow) -{ - ViewModelBase viewmodel; - - // item to append - auto [children, expected] = test_data(/*ncolumns*/ 1); - - // appending one row - viewmodel.appendRow(viewmodel.rootItem(), std::move(children)); - EXPECT_EQ(viewmodel.rowCount(), 1); - EXPECT_EQ(viewmodel.columnCount(), 1); - - // removing row - viewmodel.removeRow(viewmodel.rootItem(), 0); - EXPECT_EQ(viewmodel.rowCount(), 0); - EXPECT_EQ(viewmodel.columnCount(), 0); -} - -TEST_F(ViewModelBaseTest, appendRowToRow) -{ - ViewModelBase viewmodel; - - // preparing two rows of children, two columns each - auto [children_row0, expected_row0] = test_data(/*ncolumns*/ 2); - auto [children_row1, expected_row1] = test_data(/*ncolumns*/ 2); - - // appending rows to root - viewmodel.appendRow(viewmodel.rootItem(), std::move(children_row0)); - // appending rows to row - auto child0_index = viewmodel.index(0, 0, QModelIndex()); - auto child1_index = viewmodel.index(0, 1, QModelIndex()); - viewmodel.appendRow(expected_row0[0], std::move(children_row1)); - - // checking results - EXPECT_EQ(viewmodel.rowCount(QModelIndex()), 1); - EXPECT_EQ(viewmodel.columnCount(QModelIndex()), 2); - EXPECT_EQ(viewmodel.rowCount(child0_index), 1); - EXPECT_EQ(viewmodel.columnCount(child0_index), 2); - - // checking parent index of children in second row - auto grandchild0_index = viewmodel.index(0, 0, child0_index); - auto grandchild1_index = viewmodel.index(0, 1, child0_index); - EXPECT_EQ(viewmodel.parent(grandchild0_index), child0_index); - EXPECT_EQ(viewmodel.parent(grandchild1_index), child0_index); - - // index of item - EXPECT_EQ(viewmodel.indexFromItem(expected_row0[0]), child0_index); - EXPECT_EQ(viewmodel.indexFromItem(expected_row0[1]), child1_index); - EXPECT_EQ(viewmodel.indexFromItem(expected_row1[0]), grandchild0_index); - EXPECT_EQ(viewmodel.indexFromItem(expected_row1[1]), grandchild1_index); -} - -TEST_F(ViewModelBaseTest, onRowsAppended) -{ - ViewModelBase viewmodel; - - // two items to append as a single row with two columns - auto [children, expected] = test_data(/*ncolumns*/ 2); - - QSignalSpy spyInsert(&viewmodel, &ViewModelBase::rowsInserted); - QSignalSpy spyRemove(&viewmodel, &ViewModelBase::rowsRemoved); - - // appending one row - viewmodel.appendRow(viewmodel.rootItem(), std::move(children)); - EXPECT_EQ(viewmodel.rowCount(), 1); - EXPECT_EQ(viewmodel.columnCount(), 2); - - // checking that signaling is about the parent - EXPECT_EQ(spyRemove.count(), 0); - EXPECT_EQ(spyInsert.count(), 1); - QList<QVariant> arguments = spyInsert.takeFirst(); - EXPECT_EQ(arguments.size(), 3); // QModelIndex &parent, int first, int last - EXPECT_EQ(arguments.at(0).value<QModelIndex>(), QModelIndex()); - EXPECT_EQ(arguments.at(1).value<int>(), 0); - EXPECT_EQ(arguments.at(2).value<int>(), 0); - - // getting child from index - auto index0 = viewmodel.index(0, 0, QModelIndex()); - auto index1 = viewmodel.index(0, 1, QModelIndex()); - EXPECT_EQ(viewmodel.itemFromIndex(index0), expected[0]); - EXPECT_EQ(viewmodel.itemFromIndex(index1), expected[1]); -} - -TEST_F(ViewModelBaseTest, rowsRemoved) -{ - ViewModelBase viewmodel; - - // three rows of items - auto [children_row0, expected_row0] = test_data(/*ncolumns*/ 2); - auto [children_row1, expected_row1] = test_data(/*ncolumns*/ 2); - auto [children_row2, expected_row2] = test_data(/*ncolumns*/ 2); - - QSignalSpy spyInsert(&viewmodel, &ViewModelBase::rowsInserted); - QSignalSpy spyRemove(&viewmodel, &ViewModelBase::rowsRemoved); - - // appending one row - viewmodel.appendRow(viewmodel.rootItem(), std::move(children_row0)); - viewmodel.appendRow(viewmodel.rootItem(), std::move(children_row1)); - viewmodel.appendRow(viewmodel.rootItem(), std::move(children_row2)); - - // removing middle row - viewmodel.removeRow(viewmodel.rootItem(), 1); - - // checking that signaling is about the parent - EXPECT_EQ(spyRemove.count(), 1); - EXPECT_EQ(spyInsert.count(), 3); - QList<QVariant> arguments = spyRemove.takeFirst(); - EXPECT_EQ(arguments.size(), 3); // QModelIndex &parent, int first, int last - EXPECT_EQ(arguments.at(0).value<QModelIndex>(), QModelIndex()); - EXPECT_EQ(arguments.at(1).value<int>(), 1); - EXPECT_EQ(arguments.at(2).value<int>(), 1); -} - -TEST_F(ViewModelBaseTest, data) -{ - SessionItem item; - QVariant expected(42.0); - item.setData(expected); - - children_t children; - children.emplace_back(std::make_unique<ViewDataItem>(&item)); - - ViewModelBase viewmodel; - viewmodel.appendRow(viewmodel.rootItem(), std::move(children)); - - QModelIndex children_index = viewmodel.index(0, 0, QModelIndex()); - - EXPECT_EQ(viewmodel.data(children_index, Qt::EditRole), expected); -} - -TEST_F(ViewModelBaseTest, setData) -{ - // creating single item - SessionItem item; - QVariant expected(42.0); - item.setData(expected); - - // creating view model displaying given SessionItem - children_t children; - children.emplace_back(std::make_unique<ViewDataItem>(&item)); - ViewModelBase viewmodel; - viewmodel.appendRow(viewmodel.rootItem(), std::move(children)); - - QSignalSpy spyData(&viewmodel, &ViewModelBase::dataChanged); - - // changing the data - QModelIndex children_index = viewmodel.index(0, 0, QModelIndex()); - QVariant new_value(43.0); - EXPECT_TRUE(viewmodel.setData(children_index, new_value, Qt::EditRole)); - - // checking signaling - EXPECT_EQ(spyData.count(), 1); - QList<QVariant> arguments = spyData.takeFirst(); - EXPECT_EQ(arguments.size(), 3); // QModelIndex &parent, int first, int last - EXPECT_EQ(arguments.at(0).value<QModelIndex>(), children_index); - EXPECT_EQ(arguments.at(1).value<QModelIndex>(), children_index); - QVector<int> expected_roles{Qt::EditRole}; - EXPECT_EQ(arguments.at(2).value<QVector<int>>(), expected_roles); -} - -TEST_F(ViewModelBaseTest, flags) -{ - SessionItem item; - QVariant expected(42.0); - item.setData(expected); - item.setDisplayName("Name"); - - children_t children; - children.emplace_back(std::make_unique<ViewLabelItem>(&item)); - children.emplace_back(std::make_unique<ViewDataItem>(&item)); - - ViewModelBase viewmodel; - viewmodel.appendRow(viewmodel.rootItem(), std::move(children)); - - QModelIndex label_index = viewmodel.index(0, 0, QModelIndex()); - QModelIndex data_index = viewmodel.index(0, 1, QModelIndex()); - - EXPECT_TRUE(viewmodel.flags(label_index) & Qt::ItemIsSelectable); - EXPECT_TRUE(viewmodel.flags(label_index) & Qt::ItemIsEnabled); - EXPECT_FALSE(viewmodel.flags(label_index) & Qt::ItemIsEditable); - - EXPECT_TRUE(viewmodel.flags(data_index) & Qt::ItemIsSelectable); - EXPECT_TRUE(viewmodel.flags(data_index) & Qt::ItemIsEnabled); - EXPECT_TRUE(viewmodel.flags(data_index) & Qt::ItemIsEditable); -} - -TEST_F(ViewModelBaseTest, clearRowsFromRoot) -{ - ViewModelBase viewmodel; - - // three rows of items - auto [children_row0, expected_row0] = test_data(/*ncolumns*/ 2); - auto [children_row1, expected_row1] = test_data(/*ncolumns*/ 2); - - QSignalSpy spyInsert(&viewmodel, &ViewModelBase::rowsInserted); - QSignalSpy spyRemove(&viewmodel, &ViewModelBase::rowsRemoved); - - // appending one row - viewmodel.appendRow(viewmodel.rootItem(), std::move(children_row0)); - viewmodel.appendRow(viewmodel.rootItem(), std::move(children_row1)); - - viewmodel.clearRows(viewmodel.rootItem()); - - EXPECT_EQ(viewmodel.rowCount(), 0); - EXPECT_EQ(viewmodel.columnCount(), 0); - - // checking that signaling is about the parent - EXPECT_EQ(spyRemove.count(), 1); - EXPECT_EQ(spyInsert.count(), 2); - QList<QVariant> arguments = spyRemove.takeFirst(); - EXPECT_EQ(arguments.size(), 3); // QModelIndex &parent, int first, int last - EXPECT_EQ(arguments.at(0).value<QModelIndex>(), QModelIndex()); - EXPECT_EQ(arguments.at(1).value<int>(), 0); - EXPECT_EQ(arguments.at(2).value<int>(), 1); -} diff --git a/mvvm/tests/testviewmodel/viewmodelcontroller.test.cpp b/mvvm/tests/testviewmodel/viewmodelcontroller.test.cpp deleted file mode 100644 index 7cbb98bfe61..00000000000 --- a/mvvm/tests/testviewmodel/viewmodelcontroller.test.cpp +++ /dev/null @@ -1,479 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testviewmodel/viewmodelcontroller.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/propertyitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/standarditems/vectoritem.h" -#include "mvvm/viewmodel/labeldatarowstrategy.h" -#include "mvvm/viewmodel/standardchildrenstrategies.h" -#include "mvvm/viewmodel/standardviewitems.h" -#include "mvvm/viewmodel/viewmodelbase.h" -#include "mvvm/viewmodel/viewmodelcontroller.h" -#include "test_utils.h" -#include <QSignalSpy> - -using namespace ModelView; - -//! Tests of ViewModelController class. - -class ViewModelControllerTest : public ::testing::Test { -public: - ~ViewModelControllerTest(); - - auto create_controller(SessionModel* session_model, ViewModelBase* view_model) - { - auto result = std::make_unique<ViewModelController>(session_model, view_model); - result->setRowStrategy(std::make_unique<LabelDataRowStrategy>()); - result->setChildrenStrategy(std::make_unique<AllChildrenStrategy>()); - result->setRootSessionItem(session_model->rootItem()); - return result; - } -}; - -ViewModelControllerTest::~ViewModelControllerTest() = default; - -//! Initial state of the controller. It is in working state only after setRootItem. - -TEST_F(ViewModelControllerTest, initialState) -{ - SessionModel session_model; - ViewModelBase view_model; - auto controller = std::make_unique<ViewModelController>(&session_model, &view_model); - EXPECT_EQ(controller->sessionModel(), &session_model); - EXPECT_EQ(controller->rootSessionItem(), nullptr); -} - -//! Initial state of the controller. Empty SessionModel, empty ViewModel. - -TEST_F(ViewModelControllerTest, create_controller) -{ - SessionModel session_model; - ViewModelBase view_model; - auto controller = create_controller(&session_model, &view_model); - - EXPECT_EQ(controller->sessionModel(), &session_model); - EXPECT_EQ(controller->rootSessionItem(), session_model.rootItem()); - EXPECT_EQ(view_model.columnCount(), 0); - EXPECT_EQ(view_model.rowCount(), 0); -} - -//! Single property item in a model. - -TEST_F(ViewModelControllerTest, fromPropertyItem) -{ - SessionModel session_model; - auto propertyItem = session_model.insertItem<PropertyItem>(); - propertyItem->setData(42.0); - - ViewModelBase view_model; - auto controller = create_controller(&session_model, &view_model); - - EXPECT_EQ(view_model.rowCount(), 1); - EXPECT_EQ(view_model.columnCount(), 2); - - // accessing first child under the root item - QModelIndex labelIndex = view_model.index(0, 0); - QModelIndex dataIndex = view_model.index(0, 1); - - // it should be ViewLabelItem and ViewDataItem looking at our PropertyItem item - EXPECT_EQ(view_model.itemFromIndex(labelIndex)->item_role(), ItemDataRole::DISPLAY); - EXPECT_EQ(view_model.itemFromIndex(labelIndex)->item(), propertyItem); - EXPECT_EQ(view_model.itemFromIndex(dataIndex)->item_role(), ItemDataRole::DATA); - EXPECT_EQ(view_model.itemFromIndex(dataIndex)->item(), propertyItem); -} - -//! VectorItem in a model. - -TEST_F(ViewModelControllerTest, fromVectorItem) -{ - SessionModel session_model; - auto vectorItem = session_model.insertItem<VectorItem>(); - - ViewModelBase view_model; - auto controller = create_controller(&session_model, &view_model); - - EXPECT_EQ(view_model.rowCount(), 1); - EXPECT_EQ(view_model.columnCount(), 2); - - // accessing first child under the root item - QModelIndex vectorLabelIndex = view_model.index(0, 0); - QModelIndex vectorDataIndex = view_model.index(0, 1); - - // it should be ViewLabelItem and ViewDataItem looking at our VectorItem item - EXPECT_EQ(view_model.itemFromIndex(vectorLabelIndex)->item_role(), ItemDataRole::DISPLAY); - EXPECT_EQ(view_model.itemFromIndex(vectorLabelIndex)->item(), vectorItem); - EXPECT_EQ(view_model.itemFromIndex(vectorDataIndex)->item_role(), ItemDataRole::DATA); - EXPECT_EQ(view_model.itemFromIndex(vectorDataIndex)->item(), vectorItem); - - // checking X, Y, Z - std::vector<SessionItem*> children = vectorItem->children(); - for (int row = 0; row < 3; ++row) { // x, y, z - QModelIndex x_labelIndex = view_model.index(row, 0, vectorLabelIndex); - QModelIndex x_dataIndex = view_model.index(row, 1, vectorLabelIndex); - EXPECT_EQ(view_model.itemFromIndex(x_labelIndex)->item_role(), ItemDataRole::DISPLAY); - EXPECT_EQ(view_model.itemFromIndex(x_labelIndex)->item(), - children[static_cast<size_t>(row)]); - EXPECT_EQ(view_model.itemFromIndex(x_dataIndex)->item_role(), ItemDataRole::DATA); - EXPECT_EQ(view_model.itemFromIndex(x_dataIndex)->item(), - children[static_cast<size_t>(row)]); - } -} - -//! Single property item in a model, inserted after controller was setup. - -TEST_F(ViewModelControllerTest, initThenInsertProperty) -{ - SessionModel session_model; - - ViewModelBase view_model; - QSignalSpy spyInsert(&view_model, &ViewModelBase::rowsInserted); - QSignalSpy spyRemove(&view_model, &ViewModelBase::rowsRemoved); - - auto controller = create_controller(&session_model, &view_model); - auto propertyItem = session_model.insertItem<PropertyItem>(); - propertyItem->setData(42.0); - - // checking signaling - EXPECT_EQ(spyInsert.count(), 1); - EXPECT_EQ(spyRemove.count(), 0); - QList<QVariant> arguments = spyInsert.takeFirst(); - EXPECT_EQ(arguments.size(), 3); // QModelIndex &parent, int first, int last - EXPECT_EQ(arguments.at(0).value<QModelIndex>(), QModelIndex()); - EXPECT_EQ(arguments.at(1).value<int>(), 0); - EXPECT_EQ(arguments.at(2).value<int>(), 0); - - // checking model layout - EXPECT_EQ(view_model.rowCount(), 1); - EXPECT_EQ(view_model.columnCount(), 2); - - // accessing first child under the root item - QModelIndex labelIndex = view_model.index(0, 0); - QModelIndex dataIndex = view_model.index(0, 1); - - // it should be ViewLabelItem and ViewDataItem looking at our PropertyItem item - EXPECT_EQ(view_model.itemFromIndex(labelIndex)->item_role(), ItemDataRole::DISPLAY); - EXPECT_EQ(view_model.itemFromIndex(labelIndex)->item(), propertyItem); - - // Our PropertyItem got it's value after ViewModel was initialized, however, - // underlying ViewDataItem should see updated values - EXPECT_EQ(view_model.itemFromIndex(dataIndex)->item_role(), ItemDataRole::DATA); - EXPECT_EQ(view_model.itemFromIndex(dataIndex)->item(), propertyItem); -} - -//! Insert three property items in a model, inserted after controller was setup. - -TEST_F(ViewModelControllerTest, initThenInsertProperties) -{ - SessionModel session_model; - - ViewModelBase view_model; - QSignalSpy spyInsert(&view_model, &ViewModelBase::rowsInserted); - QSignalSpy spyRemove(&view_model, &ViewModelBase::rowsRemoved); - - auto controller = create_controller(&session_model, &view_model); - auto item0 = session_model.insertItem<PropertyItem>(); - auto item1 = session_model.insertItem<PropertyItem>(); - auto item2 = session_model.insertItem<PropertyItem>(); - - // checking signaling - EXPECT_EQ(spyInsert.count(), 3); - - // checking model layout - EXPECT_EQ(view_model.rowCount(), 3); - EXPECT_EQ(view_model.columnCount(), 2); - - EXPECT_EQ(view_model.itemFromIndex(view_model.index(0, 0))->item(), item0); - EXPECT_EQ(view_model.itemFromIndex(view_model.index(1, 0))->item(), item1); - EXPECT_EQ(view_model.itemFromIndex(view_model.index(2, 0))->item(), item2); -} - -//! Inserting property items in reversed order. - -TEST_F(ViewModelControllerTest, insertInBetween) -{ - SessionModel session_model; - - ViewModelBase view_model; - QSignalSpy spyInsert(&view_model, &ViewModelBase::rowsInserted); - QSignalSpy spyRemove(&view_model, &ViewModelBase::rowsRemoved); - - auto controller = create_controller(&session_model, &view_model); - auto item0 = session_model.insertItem<PropertyItem>(); - // inserting in front - auto item1 = session_model.insertItem<PropertyItem>(session_model.rootItem(), {"", 0}); - - // checking signaling - EXPECT_EQ(spyInsert.count(), 2); - - // checking model layout - EXPECT_EQ(view_model.rowCount(), 2); - EXPECT_EQ(view_model.columnCount(), 2); - - EXPECT_EQ(view_model.itemFromIndex(view_model.index(0, 0))->item(), item1); - EXPECT_EQ(view_model.itemFromIndex(view_model.index(1, 0))->item(), item0); -} - -//! Insert two property items in a model, inserted after controller was setup. - -TEST_F(ViewModelControllerTest, initThenInsertVector) -{ - SessionModel session_model; - - ViewModelBase view_model; - QSignalSpy spyInsert(&view_model, &ViewModelBase::rowsInserted); - QSignalSpy spyRemove(&view_model, &ViewModelBase::rowsRemoved); - - auto controller = create_controller(&session_model, &view_model); - session_model.insertItem<VectorItem>(); - session_model.insertItem<VectorItem>(); - - // checking signaling - EXPECT_EQ(spyInsert.count(), 8); // two vector items and 2*(x,y,z) - - // checking model layout - EXPECT_EQ(view_model.rowCount(), 2); - EXPECT_EQ(view_model.columnCount(), 2); -} - -//! Insert child to parent - -TEST_F(ViewModelControllerTest, insertChildToParent) -{ - SessionModel session_model; - - ViewModelBase view_model; - QSignalSpy spyInsert(&view_model, &ViewModelBase::rowsInserted); - QSignalSpy spyRemove(&view_model, &ViewModelBase::rowsRemoved); - - auto controller = create_controller(&session_model, &view_model); - - auto parent = session_model.insertItem<CompoundItem>(); - parent->registerTag(TagInfo::universalTag("children"), /*set_as_default*/ true); - session_model.insertItem<SessionItem>(parent); - session_model.insertItem<SessionItem>(parent); - - // checking signaling - EXPECT_EQ(spyInsert.count(), 3); - - // checking model layout: parent and two children - EXPECT_EQ(view_model.rowCount(), 1); - EXPECT_EQ(view_model.columnCount(), 2); - EXPECT_EQ(view_model.rowCount(view_model.index(0, 0)), 2); - EXPECT_EQ(view_model.columnCount(view_model.index(0, 0)), 2); -} - -//! Removing single top level item. - -TEST_F(ViewModelControllerTest, removeSingleTopItem) -{ - // constructing the model with single item - SessionModel session_model; - session_model.insertItem<SessionItem>(); - - // constructing viewmodel and its controller - ViewModelBase view_model; - auto controller = create_controller(&session_model, &view_model); - - // root item should have one child - EXPECT_EQ(view_model.rowCount(), 1); - EXPECT_EQ(view_model.columnCount(), 2); - - QSignalSpy spyInsert(&view_model, &ViewModelBase::rowsInserted); - QSignalSpy spyRemove(&view_model, &ViewModelBase::rowsRemoved); - - // removing child - session_model.removeItem(session_model.rootItem(), {"", 0}); - ASSERT_EQ(spyInsert.count(), 0); - ASSERT_EQ(spyRemove.count(), 1); - EXPECT_EQ(view_model.rowCount(), 0); - EXPECT_EQ(view_model.columnCount(), 0); - - QList<QVariant> arguments = spyRemove.takeFirst(); - ASSERT_EQ(arguments.size(), 3); // QModelIndex &parent, int first, int last - EXPECT_EQ(arguments.at(0).value<QModelIndex>(), QModelIndex()); - EXPECT_EQ(arguments.at(1).value<int>(), 0); - EXPECT_EQ(arguments.at(2).value<int>(), 0); -} - -//! Remove one of two top level items. - -TEST_F(ViewModelControllerTest, removeOneOfTopItems) -{ - // constructing model with two items - SessionModel session_model; - session_model.insertItem<SessionItem>(); - session_model.insertItem<SessionItem>(); - - // constructing viewmodel and its controller - ViewModelBase view_model; - auto controller = create_controller(&session_model, &view_model); - - // root item should have one child - EXPECT_EQ(view_model.rowCount(), 2); - EXPECT_EQ(view_model.columnCount(), 2); - - QSignalSpy spyRemove(&view_model, &ViewModelBase::rowsRemoved); - QSignalSpy spyInsert(&view_model, &ViewModelBase::rowsInserted); - - // removing child - session_model.removeItem(session_model.rootItem(), {"", 0}); - - // no insert was called - EXPECT_EQ(spyInsert.count(), 0); - - // removal was called once - EXPECT_EQ(spyRemove.count(), 1); - EXPECT_EQ(view_model.rowCount(), 1); - EXPECT_EQ(view_model.columnCount(), 2); - - QList<QVariant> arguments = spyRemove.takeFirst(); - EXPECT_EQ(arguments.size(), 3); // QModelIndex &parent, int first, int last - EXPECT_EQ(arguments.at(0).value<QModelIndex>(), QModelIndex()); - EXPECT_EQ(arguments.at(1).value<int>(), 0); - EXPECT_EQ(arguments.at(2).value<int>(), 0); -} - -//! Setting top level item as ROOT item - -TEST_F(ViewModelControllerTest, setRootItem) -{ - SessionModel session_model; - - // constructing viewmodel and its controller - ViewModelBase view_model; - auto controller = create_controller(&session_model, &view_model); - - auto item = session_model.insertItem<PropertyItem>(); - - controller->setRootSessionItem(item); - - // new root item doesn't have children - EXPECT_EQ(view_model.rowCount(), 0); - EXPECT_EQ(view_model.columnCount(), 0); -} - -//! Setting top level item as ROOT item (case parent and children). - -TEST_F(ViewModelControllerTest, setCompoundAsRootItem) -{ - SessionModel session_model; - - // constructing viewmodel and its controller - ViewModelBase view_model; - auto controller = create_controller(&session_model, &view_model); - - auto item = session_model.insertItem<CompoundItem>(); - item->addProperty("thickness", 42.0); - item->addProperty<VectorItem>("position"); - item->addProperty("radius", 43.0); - - controller->setRootSessionItem(item); - - EXPECT_EQ(view_model.rowCount(), 3); - EXPECT_EQ(view_model.columnCount(), 2); - - // checking vector item - auto index_of_vector_item = view_model.index(1, 0); - EXPECT_EQ(view_model.rowCount(index_of_vector_item), 3); - EXPECT_EQ(view_model.columnCount(index_of_vector_item), 2); -} - -//! On model reset. - -TEST_F(ViewModelControllerTest, onModelReset) -{ - SessionModel session_model; - session_model.insertItem<SessionItem>(); - session_model.insertItem<SessionItem>(); - session_model.insertItem<SessionItem>(); - - // constructing viewmodel and its controller - ViewModelBase view_model; - auto controller = create_controller(&session_model, &view_model); - EXPECT_EQ(controller->rootSessionItem(), session_model.rootItem()); - - QSignalSpy spyReset(&view_model, &ViewModelBase::modelReset); - - session_model.clear(); - - EXPECT_EQ(spyReset.count(), 1); - EXPECT_EQ(view_model.rowCount(), 0); - EXPECT_EQ(view_model.columnCount(), 0); - EXPECT_EQ(controller->rootSessionItem(), session_model.rootItem()); -} - -//! Real life scenario: initially empty SessionModel, apply ::clean, and then start to insert item. - -TEST_F(ViewModelControllerTest, onEmptyModelResetAndContinue) -{ - SessionModel session_model; - - // constructing viewmodel and its controller - ViewModelBase view_model; - auto controller = create_controller(&session_model, &view_model); - - QSignalSpy spyReset(&view_model, &ViewModelBase::modelReset); - session_model.clear(); - - EXPECT_EQ(spyReset.count(), 1); - - // inserting new item - QSignalSpy spyInsert(&view_model, &ViewModelBase::rowsInserted); - session_model.insertItem<SessionItem>(); - - EXPECT_EQ(spyInsert.count(), 1); -} - -//! On model destroyed. - -TEST_F(ViewModelControllerTest, onModelDestroyed) -{ - auto session_model = std::make_unique<SessionModel>(); - session_model->insertItem<SessionItem>(); - - // constructing viewmodel and its controller - ViewModelBase view_model; - auto controller = create_controller(session_model.get(), &view_model); - EXPECT_EQ(view_model.rowCount(), 1); - EXPECT_EQ(view_model.columnCount(), 2); - - session_model.reset(); - EXPECT_EQ(view_model.rowCount(), 0); - EXPECT_EQ(view_model.columnCount(), 0); - EXPECT_EQ(view_model.rootItem()->item(), nullptr); -} - -TEST_F(ViewModelControllerTest, findViews) -{ - SessionModel session_model; - ViewModelBase view_model; - auto controller = create_controller(&session_model, &view_model); - - // view of root item - auto views = controller->findViews(session_model.rootItem()); - ASSERT_EQ(views.size(), 1); - EXPECT_EQ(views.at(0), view_model.rootItem()); - - // views of VectorItem - auto item = session_model.insertItem<VectorItem>(); - views = controller->findViews(item); - ASSERT_EQ(views.size(), 2); - - // setting as root item - controller->setRootSessionItem(item); - views = controller->findViews(item); - ASSERT_EQ(views.size(), 1); - EXPECT_EQ(views.at(0), view_model.rootItem()); -} diff --git a/mvvm/tests/testviewmodel/viewmodelcontrollerbuilder.test.cpp b/mvvm/tests/testviewmodel/viewmodelcontrollerbuilder.test.cpp deleted file mode 100644 index a0f3706dbcc..00000000000 --- a/mvvm/tests/testviewmodel/viewmodelcontrollerbuilder.test.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testviewmodel/viewmodelcontrollerbuilder.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/factories/viewmodelcontrollerbuilder.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/viewmodel/labeldatarowstrategy.h" -#include "mvvm/viewmodel/standardchildrenstrategies.h" -#include "mvvm/viewmodel/viewmodelbase.h" -#include "mvvm/viewmodel/viewmodelcontroller.h" - -using namespace ModelView; - -//! Tests of ViewModelController class. - -class ViewModelControllerBuilderTest : public ::testing::Test { -public: - ~ViewModelControllerBuilderTest(); -}; - -ViewModelControllerBuilderTest::~ViewModelControllerBuilderTest() = default; - -//! Initial state of the builder. -//! It can't build anything without configuration. - -TEST_F(ViewModelControllerBuilderTest, initialState) -{ - EXPECT_THROW(std::unique_ptr<ViewModelController> controller = ViewModelControllerBuilder(), - std::runtime_error); -} - -TEST_F(ViewModelControllerBuilderTest, allItemsControllerBuild) -{ - SessionModel session_model; - ViewModelBase view_model; - - std::unique_ptr<ViewModelController> controller = - ViewModelControllerBuilder() - .model(&session_model) - .viewModel(&view_model) - .childrenStrategy(std::make_unique<AllChildrenStrategy>()) - .rowStrategy(std::make_unique<LabelDataRowStrategy>()); - controller->setRootSessionItem(session_model.rootItem()); - - EXPECT_EQ(controller->sessionModel(), &session_model); - EXPECT_EQ(controller->rootSessionItem(), session_model.rootItem()); - EXPECT_EQ(view_model.columnCount(), 0); - EXPECT_EQ(view_model.rowCount(), 0); -} diff --git a/mvvm/tests/testviewmodel/viewmodelcontrollerfactory.test.cpp b/mvvm/tests/testviewmodel/viewmodelcontrollerfactory.test.cpp deleted file mode 100644 index 3073ea74828..00000000000 --- a/mvvm/tests/testviewmodel/viewmodelcontrollerfactory.test.cpp +++ /dev/null @@ -1,48 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testviewmodel/viewmodelcontrollerfactory.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/factories/viewmodelcontrollerfactory.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/viewmodel/labeldatarowstrategy.h" -#include "mvvm/viewmodel/standardchildrenstrategies.h" -#include "mvvm/viewmodel/viewmodelbase.h" -#include "mvvm/viewmodel/viewmodelcontroller.h" - -using namespace ModelView; - -//! Tests of ViewModelControllerFactory method. - -class ViewModelControllerFactoryTest : public ::testing::Test { -public: - ~ViewModelControllerFactoryTest(); -}; - -ViewModelControllerFactoryTest::~ViewModelControllerFactoryTest() = default; - -TEST_F(ViewModelControllerFactoryTest, allItemsControllerBuild) -{ - SessionModel session_model; - ViewModelBase view_model; - - auto controller = Factory::CreateController<AllChildrenStrategy, LabelDataRowStrategy>( - &session_model, &view_model); - - controller->setRootSessionItem(session_model.rootItem()); - - EXPECT_EQ(controller->sessionModel(), &session_model); - EXPECT_EQ(controller->rootSessionItem(), session_model.rootItem()); - EXPECT_EQ(view_model.columnCount(), 0); - EXPECT_EQ(view_model.rowCount(), 0); -} diff --git a/mvvm/tests/testviewmodel/viewmodeldelegate.test.cpp b/mvvm/tests/testviewmodel/viewmodeldelegate.test.cpp deleted file mode 100644 index 3e2ac025a8b..00000000000 --- a/mvvm/tests/testviewmodel/viewmodeldelegate.test.cpp +++ /dev/null @@ -1,94 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testviewmodel/viewmodeldelegate.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/editors/customeditor.h" -#include "mvvm/model/propertyitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/standarditems/vectoritem.h" -#include "mvvm/viewmodel/defaultviewmodel.h" -#include "mvvm/viewmodel/viewmodeldelegate.h" -#include "widgetbasedtest.h" -#include <QDataWidgetMapper> -#include <QStyleOptionViewItem> - -using namespace ModelView; - -//! Tests of ViewModelDelegate class. - -class ViewModelDelegateTest : public WidgetBasedTest { -public: - ~ViewModelDelegateTest(); - - struct TestData { - SessionModel model{}; - DefaultViewModel view_model; - ViewModelDelegate delegate; - QDataWidgetMapper mapper; - - TestData() : view_model(&model) - { - mapper.setModel(&view_model); - mapper.setItemDelegate(&delegate); - } - - std::unique_ptr<CustomEditor> create_editor(const QModelIndex& index) - { - return std::unique_ptr<CustomEditor>(dynamic_cast<CustomEditor*>( - delegate.createEditor(nullptr, QStyleOptionViewItem(), index))); - } - - void map_to_index(QWidget* widget, const QModelIndex& index) - { - mapper.setRootIndex(index.parent()); - mapper.setCurrentModelIndex(index.sibling(index.row(), 0)); - mapper.addMapping(widget, 1); - } - }; - - std::unique_ptr<TestData> test_data() { return std::make_unique<TestData>(); } -}; - -ViewModelDelegateTest::~ViewModelDelegateTest() = default; - -TEST_F(ViewModelDelegateTest, createEditor) -{ - TestData test_data; - test_data.model.insertItem<VectorItem>(); - - auto parent_index = test_data.view_model.index(0, 0); - auto x_value_index = test_data.view_model.index(0, 1, parent_index); - - EXPECT_TRUE(test_data.create_editor(x_value_index).get() != nullptr); -} - -//! Check that ViewModelDelegate can work with widget mapper. - -TEST_F(ViewModelDelegateTest, widgetMapper) -{ - TestData test_data; - auto vector_item = test_data.model.insertItem<VectorItem>(); - auto x_item = vector_item->getItem(VectorItem::P_X); - - // accessing to index list (index of label field and index of data field) - // of PropertyItem corresponding to x-coordinate. - auto x_value_index = test_data.view_model.indexOfSessionItem(x_item).at(1); - auto editor = test_data.create_editor(x_value_index); - - test_data.map_to_index(editor.get(), x_value_index); - - editor->setData(43.0); - editor->dataChanged(editor->data()); - EXPECT_EQ(x_item->data<double>(), 43.0); -} diff --git a/mvvm/tests/testviewmodel/viewmodelfactory.test.cpp b/mvvm/tests/testviewmodel/viewmodelfactory.test.cpp deleted file mode 100644 index 12712d654c6..00000000000 --- a/mvvm/tests/testviewmodel/viewmodelfactory.test.cpp +++ /dev/null @@ -1,109 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testviewmodel/viewmodelfactory.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/factories/viewmodelfactory.h" -#include "mvvm/model/propertyitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/viewmodel/labeldatarowstrategy.h" -#include "mvvm/viewmodel/standardchildrenstrategies.h" -#include "mvvm/viewmodel/standardviewitems.h" -#include "test_utils.h" - -using namespace ModelView; - -namespace { -std::unique_ptr<ViewModelController> createController(SessionModel* model, ViewModelBase* viewModel) -{ - return Factory::CreateController<TopItemsStrategy, LabelDataRowStrategy>(model, viewModel); -} -} // namespace - -class ViewModelFactoryTest : public ::testing::Test { -public: - ~ViewModelFactoryTest(); - - class CustomModel : public ViewModel { - public: - CustomModel(SessionModel* model) : ViewModel(createController(model, this), nullptr) {} - }; -}; - -ViewModelFactoryTest::~ViewModelFactoryTest() = default; - -//! Creating DefaultViewModel using strategies. - -TEST_F(ViewModelFactoryTest, createDefaultViewModelInitial) -{ - SessionModel model; - - auto viewModel = Factory::CreateViewModel<AllChildrenStrategy, LabelDataRowStrategy>(&model); - EXPECT_EQ(viewModel->rowCount(), 0); - EXPECT_EQ(viewModel->columnCount(), 0); - EXPECT_EQ(viewModel->sessionItemFromIndex(QModelIndex()), model.rootItem()); -} - -//! Creating DefaultViewModel using strategies, validating behaviour on single item in SessionModel. - -TEST_F(ViewModelFactoryTest, createDefaultViewModelUseProperty) -{ - SessionModel model; - auto propertyItem = model.insertItem<PropertyItem>(); - propertyItem->setData(42.0); - - auto viewModel = Factory::CreateViewModel<AllChildrenStrategy, LabelDataRowStrategy>(&model); - - EXPECT_EQ(viewModel->rowCount(), 1); - EXPECT_EQ(viewModel->columnCount(), 2); - - // accessing first child under the root item - QModelIndex labelIndex = viewModel->index(0, 0); - QModelIndex dataIndex = viewModel->index(0, 1); - - // it should be ViewLabelItem looking at our PropertyItem item - auto labelItem = dynamic_cast<ViewLabelItem*>(viewModel->itemFromIndex(labelIndex)); - ASSERT_TRUE(labelItem != nullptr); - EXPECT_EQ(labelItem->item(), propertyItem); - - auto dataItem = dynamic_cast<ViewDataItem*>(viewModel->itemFromIndex(dataIndex)); - ASSERT_TRUE(dataItem != nullptr); - EXPECT_EQ(dataItem->item(), propertyItem); -} - -//! Creating DefaultViewModel using strategies, validating behaviour on single item in SessionModel. - -TEST_F(ViewModelFactoryTest, createCustomViewModel) -{ - SessionModel model; - auto propertyItem = model.insertItem<PropertyItem>(); - propertyItem->setData(42.0); - - CustomModel viewModel(&model); - - EXPECT_EQ(viewModel.rowCount(), 1); - EXPECT_EQ(viewModel.columnCount(), 2); - - // accessing first child under the root item - QModelIndex labelIndex = viewModel.index(0, 0); - QModelIndex dataIndex = viewModel.index(0, 1); - - // it should be ViewLabelItem looking at our PropertyItem item - auto labelItem = dynamic_cast<ViewLabelItem*>(viewModel.itemFromIndex(labelIndex)); - ASSERT_TRUE(labelItem != nullptr); - EXPECT_EQ(labelItem->item(), propertyItem); - - auto dataItem = dynamic_cast<ViewDataItem*>(viewModel.itemFromIndex(dataIndex)); - ASSERT_TRUE(dataItem != nullptr); - EXPECT_EQ(dataItem->item(), propertyItem); -} diff --git a/mvvm/tests/testviewmodel/viewmodelutils.test.cpp b/mvvm/tests/testviewmodel/viewmodelutils.test.cpp deleted file mode 100644 index 78bc9f0fb35..00000000000 --- a/mvvm/tests/testviewmodel/viewmodelutils.test.cpp +++ /dev/null @@ -1,252 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/tests/testviewmodel/viewmodelutils.test.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "google_test.h" -#include "mvvm/model/mvvm_types.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/standarditems/vectoritem.h" -#include "mvvm/viewmodel/propertytableviewmodel.h" -#include "mvvm/viewmodel/standardviewitems.h" -#include "mvvm/viewmodel/viewmodelutils.h" -#include <QColor> -#include <QModelIndexList> -#include <QStandardItemModel> - -namespace { -QList<QStandardItem*> get_items(std::vector<int> data) -{ - QList<QStandardItem*> result; - - for (auto x : data) - result.append(new QStandardItem(QString::number(x))); - - return result; -} -} // namespace - -using namespace ModelView; - -class ViewModelUtilsTest : public ::testing::Test { -public: - ~ViewModelUtilsTest(); -}; - -ViewModelUtilsTest::~ViewModelUtilsTest() = default; - -TEST_F(ViewModelUtilsTest, iterate) -{ - QStandardItemModel model; - - model.setColumnCount(2); - QStandardItem* parentItem = model.invisibleRootItem(); - - auto row1 = get_items({1, 2}); - parentItem->appendRow(row1); - row1.at(0)->appendRow(get_items({3, 4})); - - auto row2 = get_items({10, 20}); - parentItem->appendRow(row2); - - std::vector<int> expected = {1, 2, 3, 4, 10, 20}; - std::vector<int> result; - - Utils::iterate_model(&model, QModelIndex(), [&](const QModelIndex& index) { - auto item = model.itemFromIndex(index); - result.push_back(item->data(Qt::EditRole).value<int>()); - }); - - EXPECT_EQ(result, expected); -} - -//! Translation of item role to Qt roles. - -TEST_F(ViewModelUtilsTest, ItemRoleToQtRole) -{ - // DATA role of SessionItem should be translated to two Qt roles (edit and display) - auto roles = Utils::ItemRoleToQtRole(ItemDataRole::DATA); - QVector<int> expected = {Qt::DisplayRole, Qt::EditRole}; - EXPECT_EQ(roles, expected); - - // APPEARANCE roles of SessionItem on Qt site means color - roles = Utils::ItemRoleToQtRole(ItemDataRole::APPEARANCE); -#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) - expected = {Qt::ForegroundRole}; -#else - expected = {Qt::TextColorRole}; -#endif - EXPECT_EQ(roles, expected); - - // tooltip role - roles = Utils::ItemRoleToQtRole(ItemDataRole::TOOLTIP); - expected = {Qt::ToolTipRole}; - EXPECT_EQ(roles, expected); -} - -//! Testing color role of item. - -TEST_F(ViewModelUtilsTest, itemTextColorRole) -{ - SessionItem item("Something"); - - // no color defined for item by default - auto variant = Utils::TextColorRole(item); - EXPECT_FALSE(variant.isValid()); - - item.setEnabled(false); - variant = Utils::TextColorRole(item); - EXPECT_EQ(variant.value<QColor>(), QColor(Qt::gray)); -} - -//! Testing check state role of item. - -TEST_F(ViewModelUtilsTest, itemCheckStateRole) -{ - SessionItem item("Something"); - - // no color defined for item by default - auto variant = Utils::CheckStateRole(item); - EXPECT_FALSE(variant.isValid()); - - item.setData(QVariant::fromValue(true)); - EXPECT_EQ(Utils::CheckStateRole(item).value<int>(), Qt::Checked); - - item.setData(QVariant::fromValue(false)); - EXPECT_EQ(Utils::CheckStateRole(item).value<int>(), Qt::Unchecked); -} - -//! Testing decoration role of the item. - -TEST_F(ViewModelUtilsTest, itemDecorationRole) -{ - SessionItem item("Something"); - - // no color defined for item by default - auto variant = Utils::DecorationRole(item); - EXPECT_FALSE(variant.isValid()); - - QColor expected(Qt::green); - item.setData(expected); - EXPECT_EQ(Utils::DecorationRole(item).value<QColor>(), expected); -} - -//! Testing tooltip role of the item. - -TEST_F(ViewModelUtilsTest, itemToolTipRole) -{ - SessionItem item("Something"); - - auto variant = Utils::ToolTipRole(item); - EXPECT_FALSE(variant.isValid()); - - item.setToolTip("abc"); - EXPECT_EQ(Utils::ToolTipRole(item).toString(), QString("abc")); -} - -//! Check ItemsFromIndex in PropertyTableViewModel context. -//! ViewItem with its three property x, y, z forms one row. All corresponding -//! indices of (x,y,z) should give us pointers to VectorItem's properties. - -TEST_F(ViewModelUtilsTest, itemsFromIndex) -{ - // creating VectorItem and viewModel to see it as a table - SessionModel model; - auto parent = model.insertItem<VectorItem>(); - PropertyTableViewModel viewModel(&model); - - // it's a table with one row and x,y,z columns - EXPECT_EQ(viewModel.rowCount(), 1); - EXPECT_EQ(viewModel.columnCount(), 3); - - // empty index list doesn't lead to SessionItem's - QModelIndexList index_list; - EXPECT_EQ(Utils::ItemsFromIndex(index_list).size(), 0); - - // index list populated with column of properties - index_list.push_back(viewModel.index(0, 0)); - index_list.push_back(viewModel.index(0, 1)); - index_list.push_back(viewModel.index(0, 2)); - - std::vector<SessionItem*> expected = {parent->getItem(VectorItem::P_X), - parent->getItem(VectorItem::P_Y), - parent->getItem(VectorItem::P_Z)}; - EXPECT_EQ(Utils::ItemsFromIndex(index_list), expected); - EXPECT_EQ(Utils::UniqueItemsFromIndex(index_list), expected); -} - -//! Check UniqueItemsFromIndex for artificially constructed viewmodel. - -TEST_F(ViewModelUtilsTest, UniqueItemsFromIndex) -{ - SessionItem item1; - item1.setData(42, ItemDataRole::DATA); - SessionItem item2; - item2.setData(42, ItemDataRole::DATA); - - ViewModelBase viewmodel; - std::vector<std::unique_ptr<ViewItem>> items; - items.emplace_back(std::make_unique<ViewLabelItem>(&item1)); - items.emplace_back(std::make_unique<ViewLabelItem>(&item2)); - items.emplace_back(std::make_unique<ViewDataItem>(&item1)); - items.emplace_back(std::make_unique<ViewDataItem>(&item2)); - viewmodel.insertRow(viewmodel.rootItem(), 0, std::move(items)); - - QModelIndexList index_list = {viewmodel.index(0, 0), viewmodel.index(0, 1), - viewmodel.index(0, 2), viewmodel.index(0, 3)}; - - EXPECT_EQ(Utils::ItemsFromIndex(index_list), - std::vector<SessionItem*>({&item1, &item2, &item1, &item2})); - EXPECT_EQ(Utils::UniqueItemsFromIndex(index_list), std::vector<SessionItem*>({&item1, &item2})); -} - -//! Check ParentItemsFromIndex in PropertyTableViewModel context. -//! ViewItem with its three property x, y, z forms one row. All corresponding -//! indices of (x,y,z) should give us pointer to VectorItem. - -TEST_F(ViewModelUtilsTest, parentItemsFromIndex) -{ - // creating VectorItem and viewModel to see it as a table - SessionModel model; - auto parent = model.insertItem<VectorItem>(); - PropertyTableViewModel viewModel(&model); - - // it's a table with one row and x,y,z columns - EXPECT_EQ(viewModel.rowCount(), 1); - EXPECT_EQ(viewModel.columnCount(), 3); - - // empty index list doesn't lead to SessionItem's - QModelIndexList index_list; - EXPECT_EQ(Utils::ParentItemsFromIndex(index_list).size(), 0); - - std::vector<SessionItem*> expected = {parent}; - - // one cell in a list should give us pointer to original VectorItem - index_list.push_back(viewModel.index(0, 1)); - EXPECT_EQ(Utils::ParentItemsFromIndex(index_list), expected); - - index_list.clear(); - index_list.push_back(viewModel.index(0, 1)); - EXPECT_EQ(Utils::ParentItemsFromIndex(index_list), expected); - - index_list.clear(); - index_list.push_back(viewModel.index(0, 2)); - EXPECT_EQ(Utils::ParentItemsFromIndex(index_list), expected); - - // tthree cells (x, y, z) in a list should give us pointer to original VectorItem - index_list.clear(); - index_list.push_back(viewModel.index(0, 0)); - index_list.push_back(viewModel.index(0, 1)); - index_list.push_back(viewModel.index(0, 2)); - EXPECT_EQ(Utils::ParentItemsFromIndex(index_list), expected); -} diff --git a/mvvm/view/CMakeLists.txt b/mvvm/view/CMakeLists.txt deleted file mode 100644 index 91553fd96b7..00000000000 --- a/mvvm/view/CMakeLists.txt +++ /dev/null @@ -1,34 +0,0 @@ -# ----------------------------------------------------------------------------- -# Library: mvvm_view -# ----------------------------------------------------------------------------- - -set(library_name mvvm_view) - -add_library(${library_name} SHARED "") -add_subdirectory(mvvm) -add_library(MVVM::View ALIAS ${library_name}) # alias for build-tree usage - -# -- Generate header for export -- - -set(export_filename ${MVVM_AUTOGEN_DIR}/mvvm/view_export.h) -generate_export_header(${library_name} EXPORT_FILE_NAME ${export_filename}) - -# -- Dependencies -- - -target_link_libraries(${library_name} PUBLIC mvvm_viewmodel Qt5::Widgets PRIVATE qcustomplot) -target_include_directories(${library_name} - PUBLIC - $<INSTALL_INTERFACE:include> - $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}> $<BUILD_INTERFACE:${MVVM_AUTOGEN_DIR}> - ) - -# -- Definitions -- - -target_compile_features(${library_name} PUBLIC cxx_std_17) # clang code model in Qt creator - -# -- Installation -- - -install(TARGETS ${library_name} EXPORT mvvm-targets LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) -set_target_properties(${library_name} PROPERTIES EXPORT_NAME View SOVERSION ${MVVM_SOVERSION} VERSION ${MVVM_BUILDVERSION}) -install(DIRECTORY mvvm/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/mvvm FILES_MATCHING PATTERN "*.h") -install(FILES ${export_filename} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/mvvm) diff --git a/mvvm/view/mvvm/CMakeLists.txt b/mvvm/view/mvvm/CMakeLists.txt deleted file mode 100644 index a1c86ba4584..00000000000 --- a/mvvm/view/mvvm/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -add_subdirectory(widgets) -add_subdirectory(plotting) - diff --git a/mvvm/view/mvvm/plotting/CMakeLists.txt b/mvvm/view/mvvm/plotting/CMakeLists.txt deleted file mode 100644 index 414f3b778ce..00000000000 --- a/mvvm/view/mvvm/plotting/CMakeLists.txt +++ /dev/null @@ -1,45 +0,0 @@ -target_sources(${library_name} PRIVATE - axistitlecontroller.cpp - axistitlecontroller.h - colormapcanvas.cpp - colormapcanvas.h - colormapinfoformatter.cpp - colormapinfoformatter.h - colormapplotcontroller.cpp - colormapplotcontroller.h - colormapviewportplotcontroller.cpp - colormapviewportplotcontroller.h - colorscaleplotcontroller.cpp - colorscaleplotcontroller.h - customplotproxywidget.cpp - customplotproxywidget.h - customplotsceneadapter.cpp - customplotsceneadapter.h - customplotutils.cpp - customplotutils.h - data1dplotcontroller.cpp - data1dplotcontroller.h - data2dplotcontroller.cpp - data2dplotcontroller.h - graphcanvas.cpp - graphcanvas.h - graphinfoformatter.cpp - graphinfoformatter.h - graphplotcontroller.cpp - graphplotcontroller.h - graphviewportplotcontroller.cpp - graphviewportplotcontroller.h - mousemovereporter.cpp - mousemovereporter.h - mouseposinfo.h - pencontroller.cpp - pencontroller.h - sceneadapterinterface.h - statusstringformatterinterface.h - statusstringreporter.cpp - statusstringreporter.h - statusstringreporterfactory.cpp - statusstringreporterfactory.h - viewportaxisplotcontroller.cpp - viewportaxisplotcontroller.h -) diff --git a/mvvm/view/mvvm/plotting/axistitlecontroller.cpp b/mvvm/view/mvvm/plotting/axistitlecontroller.cpp deleted file mode 100644 index 79623e7f3db..00000000000 --- a/mvvm/view/mvvm/plotting/axistitlecontroller.cpp +++ /dev/null @@ -1,57 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/axistitlecontroller.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/plotting/axistitlecontroller.h" -#include "mvvm/standarditems/plottableitems.h" -#include "qcustomplot.h" -#include <stdexcept> - -using namespace ModelView; - -struct AxisTitleController::AxisTitleControllerImpl { - QCPAxis* m_axis{nullptr}; - - AxisTitleControllerImpl(QCPAxis* axis) : m_axis(axis) - { - if (!axis) - throw std::runtime_error("AxisTitleController: axis is not initialized."); - } - - void updateAxisFromItem(TextItem* item) - { - auto font = m_axis->labelFont(); - font.setPointSize(item->property<int>(TextItem::P_SIZE)); - font.setFamily(QString::fromStdString(item->property<std::string>(TextItem::P_FONT))); - m_axis->setLabel(QString::fromStdString(item->property<std::string>(TextItem::P_TEXT))); - m_axis->setLabelFont(font); - - m_axis->parentPlot()->replot(); - } -}; - -AxisTitleController::AxisTitleController(QCPAxis* axis) - : p_impl(std::make_unique<AxisTitleControllerImpl>(axis)) - -{ -} - -AxisTitleController::~AxisTitleController() = default; - -void AxisTitleController::subscribe() -{ - auto on_property_change = [this](auto, auto) { p_impl->updateAxisFromItem(currentItem()); }; - setOnPropertyChange(on_property_change); - - p_impl->updateAxisFromItem(currentItem()); -} diff --git a/mvvm/view/mvvm/plotting/axistitlecontroller.h b/mvvm/view/mvvm/plotting/axistitlecontroller.h deleted file mode 100644 index 45d33d11ee5..00000000000 --- a/mvvm/view/mvvm/plotting/axistitlecontroller.h +++ /dev/null @@ -1,45 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/axistitlecontroller.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_AXISTITLECONTROLLER_H -#define BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_AXISTITLECONTROLLER_H - -#include "mvvm/signals/itemlistener.h" -#include "mvvm/view_export.h" -#include <memory> - -class QCPAxis; - -namespace ModelView { - -class TextItem; - -//! Propagates title settings from TextItem to QCPAxis. - -class MVVM_VIEW_EXPORT AxisTitleController : public ItemListener<TextItem> { -public: - explicit AxisTitleController(QCPAxis* axis); - ~AxisTitleController() override; - -protected: - void subscribe() override; - -public: - struct AxisTitleControllerImpl; - std::unique_ptr<AxisTitleControllerImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_AXISTITLECONTROLLER_H diff --git a/mvvm/view/mvvm/plotting/colormapcanvas.cpp b/mvvm/view/mvvm/plotting/colormapcanvas.cpp deleted file mode 100644 index 0dcd7bbd4cc..00000000000 --- a/mvvm/view/mvvm/plotting/colormapcanvas.cpp +++ /dev/null @@ -1,72 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/colormapcanvas.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/plotting/colormapcanvas.h" -#include "mvvm/plotting/colormapviewportplotcontroller.h" -#include "mvvm/plotting/customplotsceneadapter.h" -#include "mvvm/plotting/statusstringreporter.h" -#include "mvvm/plotting/statusstringreporterfactory.h" -#include "mvvm/standarditems/colormapviewportitem.h" -#include "mvvm/widgets/statuslabel.h" -#include "qcustomplot.h" - -using namespace ModelView; - -struct ColorMapCanvas::ColorMapCanvasImpl { - QCustomPlot* custom_plot{nullptr}; - std::unique_ptr<ColorMapViewportPlotController> viewport_controller; - std::unique_ptr<StatusStringReporter> reporter; - StatusLabel* status_label{nullptr}; - - ColorMapCanvasImpl() : custom_plot(new QCustomPlot), status_label(new StatusLabel) - { - viewport_controller = std::make_unique<ColorMapViewportPlotController>(custom_plot); - - auto on_mouse_move = [this](const std::string& str) { - status_label->setText(QString::fromStdString(str)); - }; - reporter = CreateColorMapReporter(custom_plot, on_mouse_move); - } - - QCustomPlot* customPlot() { return custom_plot; } -}; - -ColorMapCanvas::ColorMapCanvas(QWidget* parent) - : QWidget(parent), p_impl(std::make_unique<ColorMapCanvasImpl>()) -{ - auto layout = new QVBoxLayout(this); - layout->setMargin(0); - layout->setSpacing(0); - layout->addWidget(p_impl->custom_plot); - layout->addWidget(p_impl->status_label); - setLayout(layout); - - p_impl->customPlot()->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom); - p_impl->customPlot()->axisRect()->setupFullAxesBox(true); -} - -ColorMapCanvas::~ColorMapCanvas() = default; - -void ColorMapCanvas::setItem(ColorMapViewportItem* viewport_item) -{ - p_impl->viewport_controller->setItem(viewport_item); -} - -//! Creates adapter to convert widget coordinates, to QCustomPlot internal coordinate system -//! (defined by its axes). - -std::unique_ptr<SceneAdapterInterface> ColorMapCanvas::createSceneAdapter() const -{ - return std::make_unique<CustomPlotSceneAdapter>(p_impl->customPlot()); -} diff --git a/mvvm/view/mvvm/plotting/colormapcanvas.h b/mvvm/view/mvvm/plotting/colormapcanvas.h deleted file mode 100644 index 00058b7562e..00000000000 --- a/mvvm/view/mvvm/plotting/colormapcanvas.h +++ /dev/null @@ -1,48 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/colormapcanvas.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_COLORMAPCANVAS_H -#define BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_COLORMAPCANVAS_H - -#include "mvvm/view_export.h" -#include <QWidget> -#include <memory> - -namespace ModelView { - -class ColorMapViewportItem; -class SceneAdapterInterface; - -//! Widget to show 2D data as color map. -//! Contains embedded QCustomPlot widget, shows content of ColorMapViewportItem. - -class MVVM_VIEW_EXPORT ColorMapCanvas : public QWidget { - Q_OBJECT - -public: - explicit ColorMapCanvas(QWidget* parent = nullptr); - ~ColorMapCanvas() override; - - void setItem(ColorMapViewportItem* viewport_item); - - std::unique_ptr<SceneAdapterInterface> createSceneAdapter() const; - -private: - struct ColorMapCanvasImpl; - std::unique_ptr<ColorMapCanvasImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_COLORMAPCANVAS_H diff --git a/mvvm/view/mvvm/plotting/colormapinfoformatter.cpp b/mvvm/view/mvvm/plotting/colormapinfoformatter.cpp deleted file mode 100644 index 7e6bc5838ac..00000000000 --- a/mvvm/view/mvvm/plotting/colormapinfoformatter.cpp +++ /dev/null @@ -1,64 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/colormapinfoformatter.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/plotting/colormapinfoformatter.h" -#include "mvvm/utils/stringutils.h" -#include "qcustomplot.h" -#include <sstream> - -using namespace ModelView; - -namespace { -QCPColorMap* find_colormap(QCustomPlot* custom_plot) -{ - for (int i = 0; i < custom_plot->plottableCount(); ++i) { - if (auto plottable = dynamic_cast<QCPColorMap*>(custom_plot->plottable()); plottable) - return plottable; - } - - return nullptr; -} - -struct Context { - double xpos{0.0}; - double ypos{0.0}; - int nx{0}; - int ny{0}; - double value{0.0}; -}; - -std::string compose_string(const Context& context) -{ - std::ostringstream ostr; - ostr << "[x: " << Utils::DoubleToString(context.xpos, 3) << ", "; - ostr << "y: " << Utils::DoubleToString(context.ypos, 3) << "] "; - ostr << "[binx: " << context.nx << ", "; - ostr << "biny: " << context.ny << "] "; - ostr << "[value: " << Utils::ScientificDoubleToString(context.value) << "]"; - return ostr.str(); -} - -} // namespace - -std::string ColorMapInfoFormatter::status_string(QCustomPlot* custom_plot, double x, double y) const -{ - // shall we provide caching here? - auto color_map = find_colormap(custom_plot); - Context context{x, y}; - - color_map->data()->coordToCell(x, y, &context.nx, &context.ny); - context.value = color_map->data()->cell(context.nx, context.ny); - - return compose_string(context); -} diff --git a/mvvm/view/mvvm/plotting/colormapinfoformatter.h b/mvvm/view/mvvm/plotting/colormapinfoformatter.h deleted file mode 100644 index 8ca372d8373..00000000000 --- a/mvvm/view/mvvm/plotting/colormapinfoformatter.h +++ /dev/null @@ -1,38 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/colormapinfoformatter.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_COLORMAPINFOFORMATTER_H -#define BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_COLORMAPINFOFORMATTER_H - -#include "mvvm/plotting/statusstringformatterinterface.h" - -class QCustomPlot; - -namespace ModelView { - -//! Formats status string for current mouse position in QCPColorMap. -//! Includes coordinates of mouse pointer in viewport axes coordinates, add bins info for -//! QCPColorMap beneath. - -class MVVM_VIEW_EXPORT ColorMapInfoFormatter : public StatusStringFormatterInterface { -public: - //! Returns status string representing data in color map. - //! @params x: mouse x-position given in axis viewport coordinates - //! @params y: mouse y-position given in axis viewport coordinates - std::string status_string(QCustomPlot* custom_plot, double x, double y) const override; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_COLORMAPINFOFORMATTER_H diff --git a/mvvm/view/mvvm/plotting/colormapplotcontroller.cpp b/mvvm/view/mvvm/plotting/colormapplotcontroller.cpp deleted file mode 100644 index 3eda5fadf32..00000000000 --- a/mvvm/view/mvvm/plotting/colormapplotcontroller.cpp +++ /dev/null @@ -1,131 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/colormapplotcontroller.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/plotting/colormapplotcontroller.h" -#include "mvvm/model/comboproperty.h" -#include "mvvm/plotting/data2dplotcontroller.h" -#include "mvvm/standarditems/colormapitem.h" -#include "mvvm/standarditems/data2ditem.h" -#include "qcustomplot.h" -#include <map> - -namespace { -using gradient_map_t = std::map<std::string, QCPColorGradient::GradientPreset>; -gradient_map_t createGradientMap() -{ - gradient_map_t result; - - result["Grayscale"] = QCPColorGradient::gpGrayscale; - result["Hot"] = QCPColorGradient::gpHot; - result["Cold"] = QCPColorGradient::gpCold; - result["Night"] = QCPColorGradient::gpNight; - result["Candy"] = QCPColorGradient::gpCandy; - result["Geography"] = QCPColorGradient::gpGeography; - result["Ion"] = QCPColorGradient::gpIon; - result["Thermal"] = QCPColorGradient::gpThermal; - result["Polar"] = QCPColorGradient::gpPolar; - result["Spectrum"] = QCPColorGradient::gpSpectrum; - result["Jet"] = QCPColorGradient::gpJet; - result["Hues"] = QCPColorGradient::gpHues; - - return result; -} - -QCPColorGradient getGradient(const std::string& gradientName) -{ - static gradient_map_t gradient_map = createGradientMap(); - auto it = gradient_map.find(gradientName); - return it != gradient_map.end() ? QCPColorGradient(it->second) : QCPColorGradient::gpSpectrum; -} - -} // namespace - -using namespace ModelView; - -struct ColorMapPlotController::ColorMapPlotControllerImpl { - ColorMapPlotController* master{nullptr}; - QCustomPlot* custom_plot{nullptr}; - QCPColorMap* color_map{nullptr}; - std::unique_ptr<Data2DPlotController> data_controller; - - ColorMapPlotControllerImpl(ColorMapPlotController* master, QCustomPlot* plot, - QCPColorScale* color_scale) - : master(master), custom_plot(plot) - { - color_map = new QCPColorMap(custom_plot->xAxis, custom_plot->yAxis); - data_controller = std::make_unique<Data2DPlotController>(color_map); - - if (color_scale) - color_map->setColorScale(color_scale); - } - - ~ColorMapPlotControllerImpl() { custom_plot->removePlottable(color_map); } - - ColorMapItem* colormap_item() { return master->currentItem(); } - - void update_colormap() - { - update_data_controller(); - update_interpolation(); - update_gradient(); - custom_plot->replot(); - } - - void update_data_controller() { data_controller->setItem(colormap_item()->dataItem()); } - - //! Updates QCPColorMap's interpolation when corresponding property of ColorMapItem changed. - - void update_interpolation() - { - auto is_interpolated = colormap_item()->property<bool>(ColorMapItem::P_INTERPOLATION); - color_map->setInterpolate(is_interpolated); - } - - void update_gradient() - { - auto combo = colormap_item()->property<ComboProperty>(ColorMapItem::P_GRADIENT); - color_map->setGradient(getGradient(combo.value())); - } -}; - -ColorMapPlotController::ColorMapPlotController(QCustomPlot* custom_plot, QCPColorScale* color_scale) - : p_impl(std::make_unique<ColorMapPlotControllerImpl>(this, custom_plot, color_scale)) -{ -} - -void ColorMapPlotController::subscribe() -{ - auto on_property_change = [this](SessionItem*, std::string property_name) { - if (property_name == ColorMapItem::P_INTERPOLATION) - p_impl->update_interpolation(); - - if (property_name == ColorMapItem::P_GRADIENT) - p_impl->update_gradient(); - - if (property_name == ColorMapItem::P_LINK) - p_impl->update_data_controller(); - - p_impl->custom_plot->replot(); - }; - setOnPropertyChange(on_property_change); - - p_impl->update_colormap(); -} - -void ColorMapPlotController::unsubscribe() -{ - p_impl->data_controller->setItem(nullptr); -} - -ColorMapPlotController::~ColorMapPlotController() = default; diff --git a/mvvm/view/mvvm/plotting/colormapplotcontroller.h b/mvvm/view/mvvm/plotting/colormapplotcontroller.h deleted file mode 100644 index a169eae7941..00000000000 --- a/mvvm/view/mvvm/plotting/colormapplotcontroller.h +++ /dev/null @@ -1,50 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/colormapplotcontroller.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_COLORMAPPLOTCONTROLLER_H -#define BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_COLORMAPPLOTCONTROLLER_H - -#include "mvvm/signals/itemlistener.h" -#include "mvvm/view_export.h" -#include <memory> - -class QCustomPlot; -class QCPColorScale; - -namespace ModelView { - -class ColorMapItem; - -//! Establish communication between QCPColorMap and ColorMapItem. -//! Provide update on QCPColorMap when ColorMapItem is changed. QCPColorMap is added to -//! QCustomPlot plottables, when controller is created, and removed from plottables, when controller -//! is destroyed. - -class MVVM_VIEW_EXPORT ColorMapPlotController : public ItemListener<ColorMapItem> { -public: - explicit ColorMapPlotController(QCustomPlot* plot, QCPColorScale* color_scale = nullptr); - ~ColorMapPlotController() override; - -protected: - void subscribe() override; - void unsubscribe() override; - -private: - struct ColorMapPlotControllerImpl; - std::unique_ptr<ColorMapPlotControllerImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_COLORMAPPLOTCONTROLLER_H diff --git a/mvvm/view/mvvm/plotting/colormapviewportplotcontroller.cpp b/mvvm/view/mvvm/plotting/colormapviewportplotcontroller.cpp deleted file mode 100644 index 5b3b2aa654e..00000000000 --- a/mvvm/view/mvvm/plotting/colormapviewportplotcontroller.cpp +++ /dev/null @@ -1,87 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/colormapviewportplotcontroller.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/plotting/colormapviewportplotcontroller.h" -#include "mvvm/plotting/colormapplotcontroller.h" -#include "mvvm/plotting/colorscaleplotcontroller.h" -#include "mvvm/plotting/viewportaxisplotcontroller.h" -#include "mvvm/standarditems/axisitems.h" -#include "mvvm/standarditems/colormapitem.h" -#include "mvvm/standarditems/colormapviewportitem.h" -#include <list> -#include <qcustomplot.h> - -using namespace ModelView; - -struct ColorMapViewportPlotController::ColorMapViewportPlotControllerImpl { - ColorMapViewportPlotController* m_self{nullptr}; - QCustomPlot* m_customPlot{nullptr}; - QCPColorScale* m_colorScale{nullptr}; - std::unique_ptr<ViewportAxisPlotController> m_xAxisController; - std::unique_ptr<ViewportAxisPlotController> m_yAxisController; - std::unique_ptr<ColorScalePlotController> m_colorScaleController; - std::unique_ptr<ColorMapPlotController> m_colorMapController; - - ColorMapViewportPlotControllerImpl(ColorMapViewportPlotController* master, QCustomPlot* plot) - : m_self(master), m_customPlot(plot), m_colorScale(new QCPColorScale(m_customPlot)) - { - m_xAxisController = std::make_unique<ViewportAxisPlotController>(m_customPlot->xAxis); - m_yAxisController = std::make_unique<ViewportAxisPlotController>(m_customPlot->yAxis); - m_colorScaleController = std::make_unique<ColorScalePlotController>(m_colorScale); - m_colorMapController = std::make_unique<ColorMapPlotController>(m_customPlot, m_colorScale); - } - - ColorMapViewportItem* viewportItem() { return m_self->currentItem(); } - - //! Setup controller components. - - void setup_components() - { - auto viewport = viewportItem(); - m_xAxisController->setItem(viewport->xAxis()); - m_yAxisController->setItem(viewport->yAxis()); - m_colorScaleController->setItem(viewport->zAxis()); - auto colormap_item = viewportItem()->item<ColorMapItem>(ColorMapViewportItem::T_ITEMS); - m_colorMapController->setItem(colormap_item); - viewportItem()->setViewportToContent(); - } - - void unsubscribe_components() - { - m_xAxisController->setItem(nullptr); - m_yAxisController->setItem(nullptr); - m_colorScaleController->setItem(nullptr); - m_colorMapController->setItem(nullptr); - } -}; - -ColorMapViewportPlotController::ColorMapViewportPlotController(QCustomPlot* custom_plot) - : p_impl(std::make_unique<ColorMapViewportPlotControllerImpl>(this, custom_plot)) -{ -} - -void ColorMapViewportPlotController::subscribe() -{ - auto on_item_inserted = [this](SessionItem*, TagRow) { p_impl->setup_components(); }; - setOnItemInserted(on_item_inserted); - - p_impl->setup_components(); -} - -void ColorMapViewportPlotController::unsubscribe() -{ - p_impl->unsubscribe_components(); -} - -ColorMapViewportPlotController::~ColorMapViewportPlotController() = default; diff --git a/mvvm/view/mvvm/plotting/colormapviewportplotcontroller.h b/mvvm/view/mvvm/plotting/colormapviewportplotcontroller.h deleted file mode 100644 index 91fe056782f..00000000000 --- a/mvvm/view/mvvm/plotting/colormapviewportplotcontroller.h +++ /dev/null @@ -1,47 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/colormapviewportplotcontroller.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_COLORMAPVIEWPORTPLOTCONTROLLER_H -#define BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_COLORMAPVIEWPORTPLOTCONTROLLER_H - -#include "mvvm/signals/itemlistener.h" -#include "mvvm/view_export.h" -#include <memory> - -class QCustomPlot; - -namespace ModelView { - -class ColorMapViewportItem; - -//! Establishes communications and mutual updates for ColorMapViewportItem and QCutomPlot. -//! Populates custom plot with color map and tracks updates in items. - -class MVVM_VIEW_EXPORT ColorMapViewportPlotController : public ItemListener<ColorMapViewportItem> { -public: - explicit ColorMapViewportPlotController(QCustomPlot* plot); - ~ColorMapViewportPlotController() override; - -protected: - void subscribe() override; - void unsubscribe() override; - -private: - struct ColorMapViewportPlotControllerImpl; - std::unique_ptr<ColorMapViewportPlotControllerImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_COLORMAPVIEWPORTPLOTCONTROLLER_H diff --git a/mvvm/view/mvvm/plotting/colorscaleplotcontroller.cpp b/mvvm/view/mvvm/plotting/colorscaleplotcontroller.cpp deleted file mode 100644 index cedd354f2e7..00000000000 --- a/mvvm/view/mvvm/plotting/colorscaleplotcontroller.cpp +++ /dev/null @@ -1,106 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/colorscaleplotcontroller.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/plotting/colorscaleplotcontroller.h" -#include "mvvm/plotting/viewportaxisplotcontroller.h" -#include "mvvm/standarditems/axisitems.h" -#include "qcustomplot.h" -#include <stdexcept> - -using namespace ModelView; - -struct ColorScalePlotController::ColorScalePlotControllerImpl { - - ColorScalePlotController* controller{nullptr}; - QCPColorScale* color_scale{nullptr}; - QCPLayoutGrid* layout_grid{new QCPLayoutGrid}; - std::unique_ptr<ViewportAxisPlotController> axisController; - QCPMarginGroup* margin_group{nullptr}; - - ColorScalePlotControllerImpl(ColorScalePlotController* controller, QCPColorScale* color_scale) - : controller(controller), color_scale(color_scale) - { - if (!color_scale) - throw std::runtime_error("ColorScalePlotController: axis is not initialized."); - - axisController = std::make_unique<ViewportAxisPlotController>(color_scale->axis()); - } - - void setup_components() - { - axisController->setItem(controller->currentItem()); - update_log_scale(); - show_colorscale(); - setup_margins(); - } - - //! Updates color scale for log10. - - void update_log_scale() - { - const bool is_log = controller->currentItem()->is_in_log(); - color_scale->setDataScaleType(is_log ? QCPAxis::stLogarithmic : QCPAxis::stLinear); - } - - void show_colorscale() - { - if (!layout_grid->hasElement(0, 0)) - layout_grid->addElement(0, 0, color_scale); - - layout_grid->setVisible(true); - customPlot()->plotLayout()->addElement(0, 1, layout_grid); - } - - void hide_colorscale() - { - layout_grid->setVisible(false); - customPlot()->plotLayout()->take(layout_grid); - customPlot()->plotLayout()->simplify(); - } - - //! Setup margins of color scale to match top/bottom margins of axis rectangle. - - void setup_margins() - { - if (margin_group) - return; - - if (!customPlot()->axisRect()) - return; - margin_group = new QCPMarginGroup(customPlot()); - customPlot()->axisRect()->setMarginGroup(QCP::msBottom | QCP::msTop, margin_group); - color_scale->setMarginGroup(QCP::msBottom | QCP::msTop, margin_group); - } - - QCustomPlot* customPlot() { return color_scale->parentPlot(); } -}; - -ColorScalePlotController::ColorScalePlotController(QCPColorScale* color_scale) - : p_impl(std::make_unique<ColorScalePlotControllerImpl>(this, color_scale)) - -{ -} - -ColorScalePlotController::~ColorScalePlotController() = default; - -void ColorScalePlotController::subscribe() -{ - auto on_property_change = [this](SessionItem*, std::string property_name) { - if (property_name == ViewportAxisItem::P_IS_LOG) - p_impl->update_log_scale(); - }; - setOnPropertyChange(on_property_change); - - p_impl->setup_components(); -} diff --git a/mvvm/view/mvvm/plotting/colorscaleplotcontroller.h b/mvvm/view/mvvm/plotting/colorscaleplotcontroller.h deleted file mode 100644 index 5dc13381fef..00000000000 --- a/mvvm/view/mvvm/plotting/colorscaleplotcontroller.h +++ /dev/null @@ -1,45 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/colorscaleplotcontroller.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_COLORSCALEPLOTCONTROLLER_H -#define BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_COLORSCALEPLOTCONTROLLER_H - -#include "mvvm/signals/itemlistener.h" -#include "mvvm/view_export.h" -#include <memory> - -class QCPColorScale; - -namespace ModelView { - -class ViewportAxisItem; - -//! Establishes communication between QCPColorScale and ViewportAxisItem. - -class MVVM_VIEW_EXPORT ColorScalePlotController : public ItemListener<ViewportAxisItem> { -public: - explicit ColorScalePlotController(QCPColorScale* color_scale); - ~ColorScalePlotController() override; - -protected: - void subscribe() override; - -public: - struct ColorScalePlotControllerImpl; - std::unique_ptr<ColorScalePlotControllerImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_COLORSCALEPLOTCONTROLLER_H diff --git a/mvvm/view/mvvm/plotting/customplotproxywidget.cpp b/mvvm/view/mvvm/plotting/customplotproxywidget.cpp deleted file mode 100644 index e077d633552..00000000000 --- a/mvvm/view/mvvm/plotting/customplotproxywidget.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/customplotproxywidget.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/plotting/customplotproxywidget.h" -#include <QEvent> -#include <QGraphicsScene> -#include <QGraphicsSceneMouseEvent> -#include <QGraphicsSceneWheelEvent> -#include <QWidget> - -using namespace ModelView; - -CustomPlotProxyWidget::CustomPlotProxyWidget(QWidget* colormap) -{ - setWidget(colormap); - colormap->installEventFilter(this); -} - -//! Notifies all graphics items about axes viewport change in QCustomPlot. -//! Used in RegionOfInterestView to recalculate bounding box and scene positions depending on -//! current state of CustomPlotSceneAdapter. - -bool CustomPlotProxyWidget::eventFilter(QObject* /*object*/, QEvent* event) -{ - // catching zoom/resize events in QCustomPlot - if (event->type() == QEvent::Resize || event->type() == QEvent::UpdateRequest) { - scene()->advance(); // notifying all graphics items - return false; - } - return true; -} - -void CustomPlotProxyWidget::setBlockSignalsToProxy(bool value) -{ - block_signals_to_proxy = value; -} - -void CustomPlotProxyWidget::mousePressEvent(QGraphicsSceneMouseEvent* event) -{ - if (block_signals_to_proxy) - return; - QGraphicsProxyWidget::mousePressEvent(event); -} - -void CustomPlotProxyWidget::mouseMoveEvent(QGraphicsSceneMouseEvent* event) -{ - if (block_signals_to_proxy) - return; - QGraphicsProxyWidget::mouseMoveEvent(event); -} - -void CustomPlotProxyWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) -{ - if (block_signals_to_proxy) - return; - QGraphicsProxyWidget::mouseReleaseEvent(event); -} - -void CustomPlotProxyWidget::wheelEvent(QGraphicsSceneWheelEvent* event) -{ - if (block_signals_to_proxy) - return; - QGraphicsProxyWidget::wheelEvent(event); -} diff --git a/mvvm/view/mvvm/plotting/customplotproxywidget.h b/mvvm/view/mvvm/plotting/customplotproxywidget.h deleted file mode 100644 index 09d999a063d..00000000000 --- a/mvvm/view/mvvm/plotting/customplotproxywidget.h +++ /dev/null @@ -1,49 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/customplotproxywidget.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_CUSTOMPLOTPROXYWIDGET_H -#define BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_CUSTOMPLOTPROXYWIDGET_H - -#include "mvvm/view_export.h" -#include <QGraphicsProxyWidget> - -class QWidget; - -namespace ModelView { - -//! Custom proxy widget to embed color map in graphics scene. - -class MVVM_VIEW_EXPORT CustomPlotProxyWidget : public QGraphicsProxyWidget { - Q_OBJECT - -public: - CustomPlotProxyWidget(QWidget* colormap); - - bool eventFilter(QObject* object, QEvent* event); - - void setBlockSignalsToProxy(bool value); - -protected: - void mousePressEvent(QGraphicsSceneMouseEvent* event); - void mouseMoveEvent(QGraphicsSceneMouseEvent* event); - void mouseReleaseEvent(QGraphicsSceneMouseEvent* event); - void wheelEvent(QGraphicsSceneWheelEvent* event); - -private: - bool block_signals_to_proxy{false}; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_CUSTOMPLOTPROXYWIDGET_H diff --git a/mvvm/view/mvvm/plotting/customplotsceneadapter.cpp b/mvvm/view/mvvm/plotting/customplotsceneadapter.cpp deleted file mode 100644 index 5811f1a6d89..00000000000 --- a/mvvm/view/mvvm/plotting/customplotsceneadapter.cpp +++ /dev/null @@ -1,100 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/customplotsceneadapter.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "customplotsceneadapter.h" -#include "qcustomplot.h" -#include <QObject> - -using namespace ModelView; - -struct CustomPlotSceneAdapter::CustomPlotSceneAdapterImpl { - QCustomPlot* custom_plot{nullptr}; - std::unique_ptr<QMetaObject::Connection> conn_to_customplot; - CustomPlotSceneAdapterImpl(QCustomPlot* custom_plot) : custom_plot(custom_plot) - { - conn_to_customplot = std::make_unique<QMetaObject::Connection>(); - } - - double toSceneX(double customplot_x) const - { - return custom_plot ? custom_plot->xAxis->coordToPixel(customplot_x) : customplot_x; - } - - double toSceneY(double customplot_y) const - { - return custom_plot ? custom_plot->yAxis->coordToPixel(customplot_y) : customplot_y; - } - - double fromSceneX(double scene_x) const - { - return custom_plot ? custom_plot->xAxis->pixelToCoord(scene_x) : scene_x; - } - - double fromSceneY(double scene_y) const - { - return custom_plot ? custom_plot->yAxis->pixelToCoord(scene_y) : scene_y; - } - - QRectF viewportRectangle() const - { - if (!custom_plot) - return {}; - - auto xrange = custom_plot->xAxis->range(); - auto yrange = custom_plot->yAxis->range(); - - return QRectF(toSceneX(xrange.lower), toSceneY(yrange.upper), - toSceneX(xrange.upper) - toSceneX(xrange.lower), - toSceneY(yrange.lower) - toSceneY(yrange.upper)); - } -}; - -CustomPlotSceneAdapter::CustomPlotSceneAdapter(QCustomPlot* custom_plot) - : p_impl(std::make_unique<CustomPlotSceneAdapterImpl>(custom_plot)) -{ - auto on_customplot_destroy = [this]() { p_impl->custom_plot = nullptr; }; - *p_impl->conn_to_customplot = - QObject::connect(custom_plot, &QCustomPlot::destroyed, on_customplot_destroy); -} - -CustomPlotSceneAdapter::~CustomPlotSceneAdapter() -{ - if (p_impl->custom_plot) - QObject::disconnect(*p_impl->conn_to_customplot); -} - -double CustomPlotSceneAdapter::toSceneX(double customplot_x) const -{ - return p_impl->toSceneX(customplot_x); -} - -double CustomPlotSceneAdapter::toSceneY(double customplot_y) const -{ - return p_impl->toSceneY(customplot_y); -} - -double CustomPlotSceneAdapter::fromSceneX(double scene_x) const -{ - return p_impl->fromSceneX(scene_x); -} - -double CustomPlotSceneAdapter::fromSceneY(double scene_y) const -{ - return p_impl->fromSceneY(scene_y); -} - -QRectF CustomPlotSceneAdapter::viewportRectangle() const -{ - return p_impl->viewportRectangle(); -} diff --git a/mvvm/view/mvvm/plotting/customplotsceneadapter.h b/mvvm/view/mvvm/plotting/customplotsceneadapter.h deleted file mode 100644 index 56028a8db79..00000000000 --- a/mvvm/view/mvvm/plotting/customplotsceneadapter.h +++ /dev/null @@ -1,50 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/customplotsceneadapter.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_CUSTOMPLOTSCENEADAPTER_H -#define BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_CUSTOMPLOTSCENEADAPTER_H - -#include "mvvm/plotting/sceneadapterinterface.h" -#include <memory> - -class QCustomPlot; - -namespace ModelView { - -//! Converts QGraphicsScene coordinates in the coordinates of local system of QCustomPlot -//! and vice versa. - -class MVVM_VIEW_EXPORT CustomPlotSceneAdapter : public SceneAdapterInterface { -public: - explicit CustomPlotSceneAdapter(QCustomPlot* custom_plot); - ~CustomPlotSceneAdapter() override; - - double toSceneX(double customplot_x) const override; - - double toSceneY(double customplot_y) const override; - - double fromSceneX(double scene_x) const override; - - double fromSceneY(double scene_y) const override; - - QRectF viewportRectangle() const override; - -private: - struct CustomPlotSceneAdapterImpl; - std::unique_ptr<CustomPlotSceneAdapterImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_CUSTOMPLOTSCENEADAPTER_H diff --git a/mvvm/view/mvvm/plotting/customplotutils.cpp b/mvvm/view/mvvm/plotting/customplotutils.cpp deleted file mode 100644 index f12d1cc7dc7..00000000000 --- a/mvvm/view/mvvm/plotting/customplotutils.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/customplotutils.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/plotting/customplotutils.h" -#include <qcustomplot.h> - -void ModelView::Utils::SetLogarithmicScale(QCPColorScale* axis, bool is_log_scale) -{ - if (is_log_scale && axis->dataScaleType() != QCPAxis::stLogarithmic) - axis->setDataScaleType(QCPAxis::stLogarithmic); - - else if (!is_log_scale && axis->dataScaleType() != QCPAxis::stLinear) - axis->setDataScaleType(QCPAxis::stLinear); - - SetLogarithmicScale(axis->axis(), is_log_scale); -} - -void ModelView::Utils::SetLogarithmicScale(QCPAxis* axis, bool is_log_scale) -{ - if (is_log_scale) { - axis->setNumberFormat("eb"); - axis->setNumberPrecision(0); - axis->setScaleType(QCPAxis::stLogarithmic); - QSharedPointer<QCPAxisTicker> ticker(new QCPAxisTickerLog); - axis->setTicker(ticker); - } else { - axis->setNumberFormat("g"); - axis->setNumberPrecision(6); - axis->setScaleType(QCPAxis::stLinear); - QSharedPointer<QCPAxisTicker> ticker(new QCPAxisTicker); - axis->setTicker(ticker); - } -} diff --git a/mvvm/view/mvvm/plotting/customplotutils.h b/mvvm/view/mvvm/plotting/customplotutils.h deleted file mode 100644 index 8dbfa648e58..00000000000 --- a/mvvm/view/mvvm/plotting/customplotutils.h +++ /dev/null @@ -1,35 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/customplotutils.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_CUSTOMPLOTUTILS_H -#define BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_CUSTOMPLOTUTILS_H - -#include "mvvm/view_export.h" - -class QCPColorScale; -class QCPAxis; - -namespace ModelView::Utils { - -//! Switch axis to logarithmic scale mode. - -MVVM_VIEW_EXPORT void SetLogarithmicScale(QCPColorScale* axis, bool is_log_scale); - -//! Switch axis to logarithmic scale mode. - -MVVM_VIEW_EXPORT void SetLogarithmicScale(QCPAxis* axis, bool is_log_scale); - -} // namespace ModelView::Utils - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_CUSTOMPLOTUTILS_H diff --git a/mvvm/view/mvvm/plotting/data1dplotcontroller.cpp b/mvvm/view/mvvm/plotting/data1dplotcontroller.cpp deleted file mode 100644 index c9320507315..00000000000 --- a/mvvm/view/mvvm/plotting/data1dplotcontroller.cpp +++ /dev/null @@ -1,114 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/data1dplotcontroller.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/plotting/data1dplotcontroller.h" -#include "mvvm/standarditems/data1ditem.h" -#include "qcustomplot.h" -#include <stdexcept> - -namespace { -template <typename T> QVector<T> fromStdVector(const std::vector<T>& vec) -{ -#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) - return QVector<T>(vec.begin(), vec.end()); -#else - return QVector<T>::fromStdVector(vec); -#endif -} -} // namespace - -using namespace ModelView; - -struct Data1DPlotController::Data1DPlotControllerImpl { - QCPGraph* m_graph{nullptr}; - QCPErrorBars* m_errorBars{nullptr}; - - Data1DPlotControllerImpl(QCPGraph* graph) : m_graph(graph) - { - if (!m_graph) - throw std::runtime_error("Uninitialized graph in Data1DPlotController"); - } - - void initGraphFromItem(Data1DItem* item) - { - assert(item); - updateGraphPointsFromItem(item); - updateErrorBarsFromItem(item); - } - - void updateGraphPointsFromItem(Data1DItem* item) - { - m_graph->setData(fromStdVector<double>(item->binCenters()), - fromStdVector<double>(item->binValues())); - customPlot()->replot(); - } - - void updateErrorBarsFromItem(Data1DItem* item) - { - auto errors = item->binErrors(); - if (errors.empty()) { - resetErrorBars(); - return; - } - - if (!m_errorBars) - m_errorBars = new QCPErrorBars(customPlot()->xAxis, customPlot()->yAxis); - - m_errorBars->setData(fromStdVector<double>(errors)); - m_errorBars->setDataPlottable(m_graph); - } - - void resetGraph() - { - m_graph->setData(QVector<double>{}, QVector<double>{}); - customPlot()->replot(); - } - - void resetErrorBars() - { - delete m_errorBars; - m_errorBars = nullptr; - } - - QCustomPlot* customPlot() - { - assert(m_graph); - return m_graph->parentPlot(); - } -}; - -Data1DPlotController::Data1DPlotController(QCPGraph* graph) - : p_impl(std::make_unique<Data1DPlotControllerImpl>(graph)) -{ -} - -Data1DPlotController::~Data1DPlotController() = default; - -void Data1DPlotController::subscribe() -{ - auto on_property_change = [this](SessionItem*, std::string property_name) { - if (property_name == Data1DItem::P_VALUES) - p_impl->updateGraphPointsFromItem(currentItem()); - if (property_name == Data1DItem::P_ERRORS) - p_impl->updateErrorBarsFromItem(currentItem()); - }; - setOnPropertyChange(on_property_change); - - p_impl->initGraphFromItem(currentItem()); -} - -void Data1DPlotController::unsubscribe() -{ - p_impl->resetGraph(); -} diff --git a/mvvm/view/mvvm/plotting/data1dplotcontroller.h b/mvvm/view/mvvm/plotting/data1dplotcontroller.h deleted file mode 100644 index e1d9a7f5616..00000000000 --- a/mvvm/view/mvvm/plotting/data1dplotcontroller.h +++ /dev/null @@ -1,47 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/data1dplotcontroller.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_DATA1DPLOTCONTROLLER_H -#define BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_DATA1DPLOTCONTROLLER_H - -#include "mvvm/signals/itemlistener.h" -#include "mvvm/view_export.h" -#include <memory> - -class QCPGraph; - -namespace ModelView { - -class Data1DItem; - -//! Establishes communication between QCPGraph and Data1DItem. -//! Provides update of data points on QCPGraph when Graph1DItem is changed. - -class MVVM_VIEW_EXPORT Data1DPlotController : public ItemListener<Data1DItem> { -public: - explicit Data1DPlotController(QCPGraph* graph); - ~Data1DPlotController() override; - -protected: - void subscribe() override; - void unsubscribe() override; - -private: - struct Data1DPlotControllerImpl; - std::unique_ptr<Data1DPlotControllerImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_DATA1DPLOTCONTROLLER_H diff --git a/mvvm/view/mvvm/plotting/data2dplotcontroller.cpp b/mvvm/view/mvvm/plotting/data2dplotcontroller.cpp deleted file mode 100644 index 3f96016822e..00000000000 --- a/mvvm/view/mvvm/plotting/data2dplotcontroller.cpp +++ /dev/null @@ -1,93 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/data2dplotcontroller.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/plotting/data2dplotcontroller.h" -#include "mvvm/standarditems/axisitems.h" -#include "mvvm/standarditems/data2ditem.h" -#include "qcustomplot.h" -#include <algorithm> -#include <stdexcept> - -using namespace ModelView; - -namespace { -//! Returns QCPRange of axis. -QCPRange qcpRange(const BinnedAxisItem* axis) -{ - auto centers = axis->binCenters(); // QCPColorMapData expects centers of bin - return centers.empty() ? QCPRange() : QCPRange(centers.front(), centers.back()); -} -} // namespace - -struct Data2DPlotController::Data2DPlotControllerImpl { - Data2DPlotController* master{nullptr}; - QCPColorMap* color_map{nullptr}; - Data2DPlotControllerImpl(Data2DPlotController* master, QCPColorMap* color_map) - : master(master), color_map(color_map) - { - if (!color_map) - throw std::runtime_error("Uninitialized colormap in Data2DPlotController"); - } - - Data2DItem* dataItem() { return master->currentItem(); } - - void update_data_points() - { - reset_colormap(); - - if (auto data_item = dataItem(); data_item) { - auto xAxis = data_item->xAxis(); - auto yAxis = data_item->yAxis(); - if (xAxis && yAxis) { - const int nbinsx = xAxis->size(); - const int nbinsy = yAxis->size(); - - color_map->data()->setSize(nbinsx, nbinsy); - color_map->data()->setRange(qcpRange(xAxis), qcpRange(yAxis)); - - auto values = data_item->content(); - for (int ix = 0; ix < nbinsx; ++ix) - for (int iy = 0; iy < nbinsy; ++iy) - color_map->data()->setCell(ix, iy, - values[static_cast<size_t>(ix + iy * nbinsx)]); - - auto [min, max] = std::minmax_element(std::begin(values), std::end(values)); - color_map->setDataRange(QCPRange(*min, *max)); - } - } - color_map->parentPlot()->replot(); - } - - void reset_colormap() { color_map->data()->clear(); } -}; - -Data2DPlotController::Data2DPlotController(QCPColorMap* color_map) - : p_impl(std::make_unique<Data2DPlotControllerImpl>(this, color_map)) -{ -} - -Data2DPlotController::~Data2DPlotController() = default; - -void Data2DPlotController::subscribe() -{ - auto on_data_change = [this](SessionItem*, int) { p_impl->update_data_points(); }; - setOnDataChange(on_data_change); - - p_impl->update_data_points(); -} - -void Data2DPlotController::unsubscribe() -{ - p_impl->reset_colormap(); -} diff --git a/mvvm/view/mvvm/plotting/data2dplotcontroller.h b/mvvm/view/mvvm/plotting/data2dplotcontroller.h deleted file mode 100644 index 64a7abd3222..00000000000 --- a/mvvm/view/mvvm/plotting/data2dplotcontroller.h +++ /dev/null @@ -1,47 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/data2dplotcontroller.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_DATA2DPLOTCONTROLLER_H -#define BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_DATA2DPLOTCONTROLLER_H - -#include "mvvm/signals/itemlistener.h" -#include "mvvm/view_export.h" -#include <memory> - -class QCPColorMap; - -namespace ModelView { - -class Data2DItem; - -//! Establish communication between QCPColorMap and Data2DItem. -//! Provide update of data points on QCPColorMap when Graph2DItem is changed. - -class MVVM_VIEW_EXPORT Data2DPlotController : public ItemListener<Data2DItem> { -public: - explicit Data2DPlotController(QCPColorMap* color_map); - ~Data2DPlotController() override; - -protected: - void subscribe() override; - void unsubscribe() override; - -private: - struct Data2DPlotControllerImpl; - std::unique_ptr<Data2DPlotControllerImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_DATA2DPLOTCONTROLLER_H diff --git a/mvvm/view/mvvm/plotting/graphcanvas.cpp b/mvvm/view/mvvm/plotting/graphcanvas.cpp deleted file mode 100644 index 7abd62e4c1b..00000000000 --- a/mvvm/view/mvvm/plotting/graphcanvas.cpp +++ /dev/null @@ -1,143 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/graphcanvas.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/plotting/graphcanvas.h" -#include "mvvm/plotting/customplotsceneadapter.h" -#include "mvvm/plotting/graphviewportplotcontroller.h" -#include "mvvm/plotting/statusstringreporter.h" -#include "mvvm/plotting/statusstringreporterfactory.h" -#include "mvvm/standarditems/graphviewportitem.h" -#include "mvvm/widgets/statuslabel.h" -#include "qcustomplot.h" -#include <QBoxLayout> - -namespace { - -//! Returns policy to which side of the axes box margins can be applied. -//! If number is negative, this side will be callulated automatically. - -// FIXME move to utils, provide unit tests -QCP::MarginSides autoMarginPolicy(int left, int top, int right, int bottom) -{ - QCP::MarginSides result{QCP::msAll}; - if (left >= 0) - result &= ~QCP::msLeft; - if (top >= 0) - result &= ~QCP::msTop; - if (right >= 0) - result &= ~QCP::msRight; - if (bottom >= 0) - result &= ~QCP::msBottom; - return result; -} -} // namespace - -using namespace ModelView; - -struct GraphCanvas::GraphCanvasImpl { - QCustomPlot* custom_plot{nullptr}; - std::unique_ptr<GraphViewportPlotController> viewport_controller; - std::unique_ptr<StatusStringReporter> reporter; - StatusLabel* status_label{nullptr}; - - GraphCanvasImpl() : custom_plot(new QCustomPlot), status_label(new StatusLabel) - { - viewport_controller = std::make_unique<GraphViewportPlotController>(custom_plot); - - auto on_mouse_move = [this](const std::string& str) { - status_label->setText(QString::fromStdString(str)); - }; - reporter = CreateGraphReporter(custom_plot, on_mouse_move); - } - - //! Updates viewport. - void setViewportToContent() - { - if (!viewport_controller->currentItem()) - return; - viewport_controller->currentItem()->setViewportToContent(); - } - - //! Updates viewport. - void setViewportToContent(double left, double top, double right, double bottom) - { - if (!viewport_controller->currentItem()) - return; - viewport_controller->currentItem()->setViewportToContent(left, top, right, bottom); - } - - QCustomPlot* customPlot() { return custom_plot; } -}; - -GraphCanvas::GraphCanvas(QWidget* parent) - : QWidget(parent), p_impl(std::make_unique<GraphCanvasImpl>()) -{ - auto layout = new QVBoxLayout(this); - layout->setMargin(0); - layout->setSpacing(0); - layout->addWidget(p_impl->custom_plot); - layout->addWidget(p_impl->status_label); - setLayout(layout); - - setMouseTracking(true); - p_impl->customPlot()->setMouseTracking(true); - p_impl->customPlot()->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom); - p_impl->customPlot()->axisRect()->setupFullAxesBox(true); - - auto on_replot = [this]() { - QMargins margins = p_impl->customPlot()->axisRect()->margins(); - axisMarginsChanged(margins.left(), margins.top(), margins.right(), margins.bottom()); - }; - connect(p_impl->customPlot(), &QCustomPlot::afterReplot, this, on_replot); -} - -GraphCanvas::~GraphCanvas() = default; - -void GraphCanvas::setItem(GraphViewportItem* viewport_item) -{ - p_impl->viewport_controller->setItem(viewport_item); -} - -std::unique_ptr<SceneAdapterInterface> GraphCanvas::createSceneAdapter() const -{ - return std::make_unique<CustomPlotSceneAdapter>(p_impl->customPlot()); -} - -void GraphCanvas::setViewportToContent(double left, double top, double right, double bottom) -{ - p_impl->setViewportToContent(left, top, right, bottom); -} - -void GraphCanvas::setViewportToContent() -{ - p_impl->setViewportToContent(); -} - -//! Set margins between axes rectangle and widget borders. -//! If the value is negative, leave old margin intact and allow automatic margin adjustment. - -void GraphCanvas::setAxisMargins(int left, int top, int right, int bottom) -{ - auto customPlot = p_impl->customPlot(); - customPlot->axisRect()->setAutoMargins(autoMarginPolicy(left, top, right, bottom)); - - QMargins orig = customPlot->axisRect()->margins(); - int new_left = left >= 0 ? left : orig.left(); - int new_top = top >= 0 ? top : orig.top(); - int new_right = right >= 0 ? right : orig.right(); - int new_bottom = bottom >= 0 ? bottom : orig.bottom(); - customPlot->axisRect()->setMargins(QMargins(new_left, new_top, new_right, new_bottom)); - - customPlot->replot(); -} diff --git a/mvvm/view/mvvm/plotting/graphcanvas.h b/mvvm/view/mvvm/plotting/graphcanvas.h deleted file mode 100644 index d0d42c4013b..00000000000 --- a/mvvm/view/mvvm/plotting/graphcanvas.h +++ /dev/null @@ -1,57 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/graphcanvas.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_GRAPHCANVAS_H -#define BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_GRAPHCANVAS_H - -#include "mvvm/view_export.h" -#include <QWidget> -#include <memory> - -namespace ModelView { - -class GraphViewportItem; -class SceneAdapterInterface; - -//! Widget to show scientific figure with multiple 1D graphs. -//! Contains embedded QCustomPlot widget, shows content of GraphViewportItem. - -class MVVM_VIEW_EXPORT GraphCanvas : public QWidget { - Q_OBJECT - -public: - explicit GraphCanvas(QWidget* parent = nullptr); - ~GraphCanvas() override; - - void setItem(GraphViewportItem* viewport_item); - - std::unique_ptr<SceneAdapterInterface> createSceneAdapter() const; - - void setViewportToContent(double left, double top, double right, double bottom); - - void setViewportToContent(); - - void setAxisMargins(int left, int top, int right, int bottom); - -signals: - void axisMarginsChanged(int left, int top, int right, int bottom); - -private: - struct GraphCanvasImpl; - std::unique_ptr<GraphCanvasImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_GRAPHCANVAS_H diff --git a/mvvm/view/mvvm/plotting/graphinfoformatter.cpp b/mvvm/view/mvvm/plotting/graphinfoformatter.cpp deleted file mode 100644 index c26869cf233..00000000000 --- a/mvvm/view/mvvm/plotting/graphinfoformatter.cpp +++ /dev/null @@ -1,74 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/graphinfoformatter.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/plotting/graphinfoformatter.h" -#include "mvvm/utils/stringutils.h" -#include <qcustomplot.h> -#include <sstream> - -using namespace ModelView; - -namespace { - -QCPGraph* find_graph_nearby(QCustomPlot* custom_plot, double x, double y) -{ - double widget_px = custom_plot->xAxis->coordToPixel(x); - double widget_py = custom_plot->yAxis->coordToPixel(y); - return dynamic_cast<QCPGraph*>(custom_plot->plottableAt(QPointF(widget_px, widget_py))); -} - -int getBin(const QCPGraph* graph, double x) -{ - const int key_start = graph->findBegin(x); - const int key_end = graph->findBegin(x, false); // false = do not expand range - if (key_end == key_start || key_end == graph->dataCount()) - return key_start; - return (x - graph->dataSortKey(key_start)) <= (graph->dataSortKey(key_end) - x) ? key_start - : key_end; -} - -struct Context { - double xpos{0.0}; - double ypos{0.0}; - bool close_to_graph{false}; - int nx{0}; - double value{0.0}; -}; - -std::string compose_string(const Context& context) -{ - std::ostringstream ostr; - ostr << "[x: " << Utils::DoubleToString(context.xpos, 3) << ", "; - ostr << "y: " << Utils::DoubleToString(context.ypos, 3) << "] "; - if (context.close_to_graph) { - ostr << "[binx: " << context.nx << "] "; - ostr << "[value: " << Utils::ScientificDoubleToString(context.value) << "]"; - } - return ostr.str(); -} - -} // namespace - -std::string GraphInfoFormatter::status_string(QCustomPlot* custom_plot, double x, double y) const -{ - Context context{x, y}; - - if (auto qcp_graph = find_graph_nearby(custom_plot, x, y); qcp_graph) { - context.close_to_graph = true; - context.nx = getBin(qcp_graph, x); - context.value = qcp_graph->dataMainValue(context.nx); - } - - return compose_string(context); -} diff --git a/mvvm/view/mvvm/plotting/graphinfoformatter.h b/mvvm/view/mvvm/plotting/graphinfoformatter.h deleted file mode 100644 index 5f9f34eb9a0..00000000000 --- a/mvvm/view/mvvm/plotting/graphinfoformatter.h +++ /dev/null @@ -1,38 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/graphinfoformatter.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_GRAPHINFOFORMATTER_H -#define BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_GRAPHINFOFORMATTER_H - -#include "mvvm/plotting/statusstringformatterinterface.h" - -class QCustomPlot; - -namespace ModelView { - -//! Formats status string for current mouse position in QCustomPlot with QCPGraph's. -//! Includes coordinates of mouse pointer in viewport axes coordinates, add graph info if there is -//! one nearby. - -class MVVM_VIEW_EXPORT GraphInfoFormatter : public StatusStringFormatterInterface { -public: - //! Returns status string representing graph nearby. - //! @params x: mouse x-position given in axis viewport coordinates - //! @params y: mouse y-position given in axis viewport coordinates - std::string status_string(QCustomPlot* custom_plot, double x, double y) const override; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_GRAPHINFOFORMATTER_H diff --git a/mvvm/view/mvvm/plotting/graphplotcontroller.cpp b/mvvm/view/mvvm/plotting/graphplotcontroller.cpp deleted file mode 100644 index 77f09297a4a..00000000000 --- a/mvvm/view/mvvm/plotting/graphplotcontroller.cpp +++ /dev/null @@ -1,106 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/graphplotcontroller.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/plotting/graphplotcontroller.h" -#include "mvvm/model/comboproperty.h" -#include "mvvm/plotting/data1dplotcontroller.h" -#include "mvvm/plotting/pencontroller.h" -#include "mvvm/standarditems/data1ditem.h" -#include "mvvm/standarditems/graphitem.h" -#include "mvvm/standarditems/plottableitems.h" -#include "qcustomplot.h" - -using namespace ModelView; - -struct GraphPlotController::GraphItemControllerImpl { - GraphPlotController* m_self{nullptr}; - QCustomPlot* m_customPlot{nullptr}; - QCPGraph* m_graph{nullptr}; - std::unique_ptr<Data1DPlotController> m_dataController; - std::unique_ptr<PenController> m_penController; - - GraphItemControllerImpl(GraphPlotController* master, QCustomPlot* plot) - : m_self(master), m_customPlot(plot) - { - } - - //! Setups controllers and updates graph properties. - - void init_graph() - { - m_graph = m_customPlot->addGraph(); - m_dataController = std::make_unique<Data1DPlotController>(m_graph); - m_penController = std::make_unique<PenController>(m_graph); - - update_data_controller(); - update_graph_pen(); - update_visible(); - } - - ~GraphItemControllerImpl() - { - if (m_graph) - m_customPlot->removePlottable(m_graph); - } - - GraphItem* graph_item() { return m_self->currentItem(); } - - void update_data_controller() { m_dataController->setItem(graph_item()->dataItem()); } - - //! Updates graph pen from GraphItem. - - void update_graph_pen() { m_penController->setItem(graph_item()->penItem()); } - - //! Update visible - void update_visible() - { - m_graph->setVisible(graph_item()->property<bool>(GraphItem::P_DISPLAYED)); - m_customPlot->replot(); - } - - void reset_graph() - { - m_dataController->setItem(nullptr); - m_penController->setItem(nullptr); - m_customPlot->removePlottable(m_graph); - m_graph = nullptr; - m_customPlot->replot(); - } -}; - -GraphPlotController::GraphPlotController(QCustomPlot* custom_plot) - : p_impl(std::make_unique<GraphItemControllerImpl>(this, custom_plot)) -{ -} - -void GraphPlotController::subscribe() -{ - auto on_property_change = [this](SessionItem*, const std::string& property_name) { - if (property_name == GraphItem::P_LINK) - p_impl->update_data_controller(); - - if (property_name == GraphItem::P_DISPLAYED) - p_impl->update_visible(); - }; - setOnPropertyChange(on_property_change); - - p_impl->init_graph(); -} - -void GraphPlotController::unsubscribe() -{ - p_impl->reset_graph(); -} - -GraphPlotController::~GraphPlotController() = default; diff --git a/mvvm/view/mvvm/plotting/graphplotcontroller.h b/mvvm/view/mvvm/plotting/graphplotcontroller.h deleted file mode 100644 index 6c76c1466d1..00000000000 --- a/mvvm/view/mvvm/plotting/graphplotcontroller.h +++ /dev/null @@ -1,49 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/graphplotcontroller.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_GRAPHPLOTCONTROLLER_H -#define BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_GRAPHPLOTCONTROLLER_H - -#include "mvvm/signals/itemlistener.h" -#include "mvvm/view_export.h" -#include <memory> - -class QCustomPlot; - -namespace ModelView { - -class GraphItem; - -//! Establish communication between QCPGraph and GraphItem. -//! Provides update on QCPGraph (data points, line style, color, etc) when GraphItem is changed. -//! QCPGraph is added to QCustomPlot plottables, when controller is created, and removed from -//! plottables when controller is destroyed. - -class MVVM_VIEW_EXPORT GraphPlotController : public ItemListener<GraphItem> { -public: - explicit GraphPlotController(QCustomPlot* plot); - ~GraphPlotController() override; - -protected: - void subscribe() override; - void unsubscribe() override; - -private: - struct GraphItemControllerImpl; - std::unique_ptr<GraphItemControllerImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_GRAPHPLOTCONTROLLER_H diff --git a/mvvm/view/mvvm/plotting/graphviewportplotcontroller.cpp b/mvvm/view/mvvm/plotting/graphviewportplotcontroller.cpp deleted file mode 100644 index df80cdb7db0..00000000000 --- a/mvvm/view/mvvm/plotting/graphviewportplotcontroller.cpp +++ /dev/null @@ -1,123 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/graphviewportplotcontroller.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/plotting/graphviewportplotcontroller.h" -#include "mvvm/plotting/graphplotcontroller.h" -#include "mvvm/plotting/viewportaxisplotcontroller.h" -#include "mvvm/standarditems/axisitems.h" -#include "mvvm/standarditems/graphitem.h" -#include "mvvm/standarditems/graphviewportitem.h" -#include <list> -#include <qcustomplot.h> -#include <stdexcept> - -using namespace ModelView; - -struct GraphViewportPlotController::GraphViewportPlotControllerImpl { - GraphViewportPlotController* master{nullptr}; - QCustomPlot* custom_plot{nullptr}; - std::list<std::unique_ptr<GraphPlotController>> graph_controllers; - std::unique_ptr<ViewportAxisPlotController> xAxisController; - std::unique_ptr<ViewportAxisPlotController> yAxisController; - - GraphViewportPlotControllerImpl(GraphViewportPlotController* master, QCustomPlot* plot) - : master(master), custom_plot(plot) - { - } - - GraphViewportItem* viewport_item() { return master->currentItem(); } - - //! Setup controller components. - void setup_components() - { - create_axis_controllers(); - create_graph_controllers(); - } - - //! Creates axes controllers. - - void create_axis_controllers() - { - auto viewport = viewport_item(); - - xAxisController = std::make_unique<ViewportAxisPlotController>(custom_plot->xAxis); - xAxisController->setItem(viewport->xAxis()); - - yAxisController = std::make_unique<ViewportAxisPlotController>(custom_plot->yAxis); - yAxisController->setItem(viewport->yAxis()); - } - - //! Run through all GraphItem's and create graph controllers for QCustomPlot. - - void create_graph_controllers() - { - graph_controllers.clear(); - auto viewport = viewport_item(); - for (auto graph_item : viewport->graphItems()) { - auto controller = std::make_unique<GraphPlotController>(custom_plot); - controller->setItem(graph_item); - graph_controllers.push_back(std::move(controller)); - } - viewport->setViewportToContent(); - } - - //! Adds controller for item. - void add_controller_for_item(SessionItem* parent, const TagRow& tagrow) - { - auto added_child = dynamic_cast<GraphItem*>(parent->getItem(tagrow.tag, tagrow.row)); - - for (auto& controller : graph_controllers) - if (controller->currentItem() == added_child) - throw std::runtime_error("Attempt to create second controller"); - - auto controller = std::make_unique<GraphPlotController>(custom_plot); - controller->setItem(added_child); - graph_controllers.push_back(std::move(controller)); - custom_plot->replot(); - } - - //! Remove GraphPlotController corresponding to GraphItem. - - void remove_controller_for_item(SessionItem* parent, const TagRow& tagrow) - { - auto child_about_to_be_removed = parent->getItem(tagrow.tag, tagrow.row); - auto if_func = [&](const std::unique_ptr<GraphPlotController>& cntrl) -> bool { - return cntrl->currentItem() == child_about_to_be_removed; - }; - graph_controllers.remove_if(if_func); - custom_plot->replot(); - } -}; - -GraphViewportPlotController::GraphViewportPlotController(QCustomPlot* custom_plot) - : p_impl(std::make_unique<GraphViewportPlotControllerImpl>(this, custom_plot)) -{ -} - -void GraphViewportPlotController::subscribe() -{ - auto on_item_inserted = [this](SessionItem* parent, TagRow tagrow) { - p_impl->add_controller_for_item(parent, tagrow); - }; - setOnItemInserted(on_item_inserted); - - auto on_about_to_remove_item = [this](SessionItem* parent, TagRow tagrow) { - p_impl->remove_controller_for_item(parent, tagrow); - }; - setOnAboutToRemoveItem(on_about_to_remove_item); - - p_impl->setup_components(); -} - -GraphViewportPlotController::~GraphViewportPlotController() = default; diff --git a/mvvm/view/mvvm/plotting/graphviewportplotcontroller.h b/mvvm/view/mvvm/plotting/graphviewportplotcontroller.h deleted file mode 100644 index 681154843b2..00000000000 --- a/mvvm/view/mvvm/plotting/graphviewportplotcontroller.h +++ /dev/null @@ -1,46 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/graphviewportplotcontroller.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_GRAPHVIEWPORTPLOTCONTROLLER_H -#define BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_GRAPHVIEWPORTPLOTCONTROLLER_H - -#include "mvvm/signals/itemlistener.h" -#include "mvvm/view_export.h" -#include <memory> - -class QCustomPlot; - -namespace ModelView { - -class GraphViewportItem; - -//! Establishes communications and mutual updates for GraphViewportItem and QCutomPlot. -//! Populates custom plot with all graphs found in GraphViewportItem. - -class MVVM_VIEW_EXPORT GraphViewportPlotController : public ItemListener<GraphViewportItem> { -public: - explicit GraphViewportPlotController(QCustomPlot* plot); - ~GraphViewportPlotController() override; - -protected: - void subscribe() override; - -private: - struct GraphViewportPlotControllerImpl; - std::unique_ptr<GraphViewportPlotControllerImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_GRAPHVIEWPORTPLOTCONTROLLER_H diff --git a/mvvm/view/mvvm/plotting/mousemovereporter.cpp b/mvvm/view/mvvm/plotting/mousemovereporter.cpp deleted file mode 100644 index 6e7f136db3d..00000000000 --- a/mvvm/view/mvvm/plotting/mousemovereporter.cpp +++ /dev/null @@ -1,66 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/mousemovereporter.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/plotting/mousemovereporter.h" -#include "mvvm/plotting/mouseposinfo.h" -#include <QMouseEvent> -#include <qcustomplot.h> -#include <stdexcept> - -using namespace ModelView; - -struct MouseMoveReporter::MouseMoveReporterImpl { - MouseMoveReporter* reporter{nullptr}; - QCustomPlot* custom_plot{nullptr}; - callback_t callback; - MouseMoveReporterImpl(MouseMoveReporter* reporter, QCustomPlot* custom_plot, - callback_t callback) - : reporter(reporter), custom_plot(custom_plot), callback(std::move(callback)) - { - if (!custom_plot) - throw std::runtime_error("MouseMoveReporter: not initialized custom plot."); - - custom_plot->setMouseTracking(true); - set_connected(); - } - - void set_connected() - { - auto on_mouse_move = [this](QMouseEvent* event) { - double x = pixelToXaxisCoord(event->pos().x()); - double y = pixelToYaxisCoord(event->pos().y()); - if (callback) - callback({x, y, axesRangeContains(x, y)}); - }; - - QObject::connect(custom_plot, &QCustomPlot::mouseMove, on_mouse_move); - } - - double pixelToXaxisCoord(double pixel) const { return custom_plot->xAxis->pixelToCoord(pixel); } - - double pixelToYaxisCoord(double pixel) const { return custom_plot->yAxis->pixelToCoord(pixel); } - - bool axesRangeContains(double xpos, double ypos) const - { - return custom_plot->xAxis->range().contains(xpos) - && custom_plot->yAxis->range().contains(ypos); - } -}; - -MouseMoveReporter::MouseMoveReporter(QCustomPlot* custom_plot, callback_t callback) - : p_impl(std::make_unique<MouseMoveReporterImpl>(this, custom_plot, callback)) -{ -} - -MouseMoveReporter::~MouseMoveReporter() = default; diff --git a/mvvm/view/mvvm/plotting/mousemovereporter.h b/mvvm/view/mvvm/plotting/mousemovereporter.h deleted file mode 100644 index 9bb180820c4..00000000000 --- a/mvvm/view/mvvm/plotting/mousemovereporter.h +++ /dev/null @@ -1,45 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/mousemovereporter.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_MOUSEMOVEREPORTER_H -#define BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_MOUSEMOVEREPORTER_H - -#include "mvvm/view_export.h" -#include <functional> -#include <memory> - -class QCustomPlot; - -namespace ModelView { - -struct MousePosInfo; - -//! Tracks mouse moves in QCustomPlot canvas. -//! Notifies client about mouse moves and corresponding pointer coordinates expressed in axes units -//! at current zoom level. - -class MVVM_VIEW_EXPORT MouseMoveReporter { -public: - using callback_t = std::function<void(const MousePosInfo& pos_info)>; - MouseMoveReporter(QCustomPlot* custom_plot, callback_t callback); - ~MouseMoveReporter(); - -private: - struct MouseMoveReporterImpl; - std::unique_ptr<MouseMoveReporterImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_MOUSEMOVEREPORTER_H diff --git a/mvvm/view/mvvm/plotting/mouseposinfo.h b/mvvm/view/mvvm/plotting/mouseposinfo.h deleted file mode 100644 index 20f8ba700b6..00000000000 --- a/mvvm/view/mvvm/plotting/mouseposinfo.h +++ /dev/null @@ -1,35 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/mouseposinfo.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_MOUSEPOSINFO_H -#define BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_MOUSEPOSINFO_H - -#include "mvvm/view_export.h" - -class QCustomPlot; - -namespace ModelView { - -//! Aggregate to hold mouse position info in QCustomPlot context. -//! Position is given in axis coordinates corresponding to the current zoom level. - -struct MVVM_VIEW_EXPORT MousePosInfo { - double xpos{0.0}; - double ypos{0.0}; - bool in_axes_range{false}; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_MOUSEPOSINFO_H diff --git a/mvvm/view/mvvm/plotting/pencontroller.cpp b/mvvm/view/mvvm/plotting/pencontroller.cpp deleted file mode 100644 index 739632538e3..00000000000 --- a/mvvm/view/mvvm/plotting/pencontroller.cpp +++ /dev/null @@ -1,68 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/pencontroller.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/plotting/pencontroller.h" -#include "mvvm/model/comboproperty.h" -#include "mvvm/standarditems/plottableitems.h" -#include "qcustomplot.h" -#include <stdexcept> - -using namespace ModelView; - -namespace { -//! Returns Qt pen style from current ComboProperty index. -Qt::PenStyle getQtPenFromComboIndex(const ComboProperty& combo) -{ - // our ComboProperty for pens coincide with Qt definition - return static_cast<Qt::PenStyle>(combo.currentIndex()); -} -} // namespace - -struct PenController::PenControllerImpl { - QCPGraph* m_graph{nullptr}; - PenControllerImpl(QCPGraph* graph) : m_graph(graph) - { - if (!m_graph) - throw std::runtime_error("Error in PenController: uninitialized graph."); - } - - void update_graph_from_item(PenItem* item) - { - QColor color(QString::fromStdString(item->colorName())); - auto pencombo = item->property<ComboProperty>(PenItem::P_STYLE); - auto penwidth = item->property<int>(PenItem::P_WIDTH); - - QPen pen; - pen.setColor(color); - pen.setStyle(getQtPenFromComboIndex(pencombo)); - pen.setWidth(penwidth); - m_graph->setPen(pen); - - m_graph->parentPlot()->replot(); - } -}; - -PenController::PenController(QCPGraph* graph) : p_impl(std::make_unique<PenControllerImpl>(graph)) -{ -} - -PenController::~PenController() = default; - -void PenController::subscribe() -{ - auto on_property_change = [this](auto, auto) { p_impl->update_graph_from_item(currentItem()); }; - setOnPropertyChange(on_property_change); - - p_impl->update_graph_from_item(currentItem()); -} diff --git a/mvvm/view/mvvm/plotting/pencontroller.h b/mvvm/view/mvvm/plotting/pencontroller.h deleted file mode 100644 index 6cfbc95c928..00000000000 --- a/mvvm/view/mvvm/plotting/pencontroller.h +++ /dev/null @@ -1,46 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/pencontroller.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_PENCONTROLLER_H -#define BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_PENCONTROLLER_H - -#include "mvvm/signals/itemlistener.h" -#include "mvvm/view_export.h" -#include <memory> - -class QCPGraph; - -namespace ModelView { - -class PenItem; - -//! Establishes communication between QCPGraph and PenItem. -//! Provides update of QCPGraph's color, line style and width when PenItem is changed. - -class MVVM_VIEW_EXPORT PenController : public ItemListener<PenItem> { -public: - explicit PenController(QCPGraph* graph); - ~PenController() override; - -protected: - void subscribe() override; - -private: - struct PenControllerImpl; - std::unique_ptr<PenControllerImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_PENCONTROLLER_H diff --git a/mvvm/view/mvvm/plotting/sceneadapterinterface.h b/mvvm/view/mvvm/plotting/sceneadapterinterface.h deleted file mode 100644 index 1f51f21079c..00000000000 --- a/mvvm/view/mvvm/plotting/sceneadapterinterface.h +++ /dev/null @@ -1,50 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/sceneadapterinterface.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_SCENEADAPTERINTERFACE_H -#define BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_SCENEADAPTERINTERFACE_H - -#include "mvvm/view_export.h" - -class QRectF; - -namespace ModelView { - -//! Interface to convert coordinates of "scene" to coordinates of "widget". -//! Used in the context of QCustomPlot being embedded into QGraphicsScene. Converts QGraphicsScene -//! coordinates in the coordinates of local system of QCustomPlot and vice versa. - -class MVVM_VIEW_EXPORT SceneAdapterInterface { -public: - virtual ~SceneAdapterInterface() = default; - - //! convert local x-coordinate to scene coordinate - virtual double toSceneX(double) const = 0; - - //! convert local y-coordinate to scene coordinate - virtual double toSceneY(double) const = 0; - - //! convert scene x-coordinate to local axis coordinate - virtual double fromSceneX(double) const = 0; - - //! convert scene y-coordinate to local axis coordinate - virtual double fromSceneY(double) const = 0; - - //! returns viewport rectangle in scene coordinates - virtual QRectF viewportRectangle() const = 0; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_SCENEADAPTERINTERFACE_H diff --git a/mvvm/view/mvvm/plotting/statusstringformatterinterface.h b/mvvm/view/mvvm/plotting/statusstringformatterinterface.h deleted file mode 100644 index b9214cb9601..00000000000 --- a/mvvm/view/mvvm/plotting/statusstringformatterinterface.h +++ /dev/null @@ -1,37 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/statusstringformatterinterface.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_STATUSSTRINGFORMATTERINTERFACE_H -#define BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_STATUSSTRINGFORMATTERINTERFACE_H - -#include "mvvm/view_export.h" -#include <string> - -class QCustomPlot; - -namespace ModelView { - -//! Pure virtual interface to format string with status info corresponding to the current mouse -//! position on QCustomPlot. - -class MVVM_VIEW_EXPORT StatusStringFormatterInterface { -public: - virtual ~StatusStringFormatterInterface() = default; - - virtual std::string status_string(QCustomPlot* custom_plot, double x, double y) const = 0; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_STATUSSTRINGFORMATTERINTERFACE_H diff --git a/mvvm/view/mvvm/plotting/statusstringreporter.cpp b/mvvm/view/mvvm/plotting/statusstringreporter.cpp deleted file mode 100644 index 5d00ded66a4..00000000000 --- a/mvvm/view/mvvm/plotting/statusstringreporter.cpp +++ /dev/null @@ -1,88 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/statusstringreporter.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/plotting/statusstringreporter.h" -#include "mvvm/plotting/mousemovereporter.h" -#include "mvvm/plotting/mouseposinfo.h" -#include "mvvm/plotting/statusstringformatterinterface.h" -#include <stdexcept> - -using namespace ModelView; - -struct StatusStringReporter::StatusStringReporterImpl { - StatusStringReporter* parent{nullptr}; - QCustomPlot* custom_plot{nullptr}; - callback_t callback; - std::unique_ptr<StatusStringFormatterInterface> fmt; - std::unique_ptr<MouseMoveReporter> mouse_reporter; - MousePosInfo prevPos; - - StatusStringReporterImpl(StatusStringReporter* parent, QCustomPlot* custom_plot, - callback_t callback, - std::unique_ptr<StatusStringFormatterInterface> formatter) - : parent(parent) - , custom_plot(custom_plot) - , callback(std::move(callback)) - , fmt(std::move(formatter)) - { - if (!custom_plot) - throw std::runtime_error("StatusStringReporter: not initialized custom plot."); - - auto on_mouse_move = [this](const MousePosInfo& pos) { - if (pos.in_axes_range) { - notify_client(pos); - if (!prevPos.in_axes_range) - entering_the_area(); - } else { - if (prevPos.in_axes_range) - leaving_the_area(); - } - - prevPos = pos; - }; - mouse_reporter = std::make_unique<MouseMoveReporter>(custom_plot, on_mouse_move); - } - - //! Notify client about mouse move with formatted status string. - - void notify_client(const MousePosInfo& pos) - { - callback(fmt->status_string(this->custom_plot, pos.xpos, pos.ypos)); - } - - //! Notify client on leaving axes area. - - void leaving_the_area() - { - // notifying client with empty string as a sign that we have left the area - callback({}); - } - - //! Notify client on entering axes area. - - void entering_the_area() - { - // for future improvements - } -}; - -StatusStringReporter::StatusStringReporter( - QCustomPlot* custom_plot, callback_t callback, - std::unique_ptr<StatusStringFormatterInterface> formatter) - : p_impl(std::make_unique<StatusStringReporterImpl>(this, custom_plot, callback, - std::move(formatter))) -{ -} - -StatusStringReporter::~StatusStringReporter() = default; diff --git a/mvvm/view/mvvm/plotting/statusstringreporter.h b/mvvm/view/mvvm/plotting/statusstringreporter.h deleted file mode 100644 index 82a204e8477..00000000000 --- a/mvvm/view/mvvm/plotting/statusstringreporter.h +++ /dev/null @@ -1,46 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/statusstringreporter.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_STATUSSTRINGREPORTER_H -#define BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_STATUSSTRINGREPORTER_H - -#include "mvvm/view_export.h" -#include <functional> -#include <memory> -#include <string> - -class QCustomPlot; - -namespace ModelView { - -class StatusStringFormatterInterface; - -//! Reports back status string composed for current mouse position in QCustomPlot. -//! Doesn't report if cursor is outside of the axes range. - -class MVVM_VIEW_EXPORT StatusStringReporter { -public: - using callback_t = std::function<void(const std::string&)>; - StatusStringReporter(QCustomPlot* custom_plot, callback_t callback, - std::unique_ptr<StatusStringFormatterInterface> formatter); - ~StatusStringReporter(); - -private: - struct StatusStringReporterImpl; - std::unique_ptr<StatusStringReporterImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_STATUSSTRINGREPORTER_H diff --git a/mvvm/view/mvvm/plotting/statusstringreporterfactory.cpp b/mvvm/view/mvvm/plotting/statusstringreporterfactory.cpp deleted file mode 100644 index 6cb0e50d598..00000000000 --- a/mvvm/view/mvvm/plotting/statusstringreporterfactory.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/statusstringreporterfactory.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/plotting/statusstringreporterfactory.h" -#include "mvvm/plotting/colormapinfoformatter.h" -#include "mvvm/plotting/graphinfoformatter.h" -#include "mvvm/plotting/statusstringreporter.h" - -namespace ModelView { - -std::unique_ptr<StatusStringReporter> -CreateGraphReporter(QCustomPlot* custom_plot, std::function<void(const std::string&)> callback) -{ - return std::make_unique<StatusStringReporter>(custom_plot, callback, - std::make_unique<GraphInfoFormatter>()); -} - -std::unique_ptr<StatusStringReporter> -CreateColorMapReporter(QCustomPlot* custom_plot, std::function<void(const std::string&)> callback) -{ - return std::make_unique<StatusStringReporter>(custom_plot, callback, - std::make_unique<ColorMapInfoFormatter>()); -} - -} // namespace ModelView diff --git a/mvvm/view/mvvm/plotting/statusstringreporterfactory.h b/mvvm/view/mvvm/plotting/statusstringreporterfactory.h deleted file mode 100644 index a63be85ccd1..00000000000 --- a/mvvm/view/mvvm/plotting/statusstringreporterfactory.h +++ /dev/null @@ -1,40 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/statusstringreporterfactory.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_STATUSSTRINGREPORTERFACTORY_H -#define BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_STATUSSTRINGREPORTERFACTORY_H - -//! Contains factory methods to create StatusStringReporter - -#include "mvvm/view_export.h" -#include <functional> -#include <memory> - -class QCustomPlot; - -namespace ModelView { - -class StatusStringReporter; - -//! Creates reporter for status string in QCustomPlot containing graphs. -MVVM_VIEW_EXPORT std::unique_ptr<StatusStringReporter> -CreateGraphReporter(QCustomPlot* custom_plot, std::function<void(const std::string&)> callback); - -//! Creates reporter for status string in QCustomPlot containing QCPColorMap. -MVVM_VIEW_EXPORT std::unique_ptr<StatusStringReporter> -CreateColorMapReporter(QCustomPlot* custom_plot, std::function<void(const std::string&)> callback); - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_STATUSSTRINGREPORTERFACTORY_H diff --git a/mvvm/view/mvvm/plotting/viewportaxisplotcontroller.cpp b/mvvm/view/mvvm/plotting/viewportaxisplotcontroller.cpp deleted file mode 100644 index 576c4f7810b..00000000000 --- a/mvvm/view/mvvm/plotting/viewportaxisplotcontroller.cpp +++ /dev/null @@ -1,137 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/viewportaxisplotcontroller.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/plotting/viewportaxisplotcontroller.h" -#include "mvvm/plotting/axistitlecontroller.h" -#include "mvvm/plotting/customplotutils.h" -#include "mvvm/standarditems/axisitems.h" -#include "mvvm/standarditems/plottableitems.h" -#include "qcustomplot.h" -#include <QObject> -#include <stdexcept> - -using namespace ModelView; - -struct ViewportAxisPlotController::AxesPlotControllerImpl { - - ViewportAxisPlotController* m_self{nullptr}; - QCPAxis* m_axis{nullptr}; - bool m_blockUpdate{false}; - std::unique_ptr<QMetaObject::Connection> m_axisConn; - std::unique_ptr<AxisTitleController> m_titleController; - - AxesPlotControllerImpl(ViewportAxisPlotController* controller, QCPAxis* axis) - : m_self(controller), m_axis(axis) - { - if (!axis) - throw std::runtime_error("AxisPlotController: axis is not initialized."); - m_axisConn = std::make_unique<QMetaObject::Connection>(); - } - - //! Connects QCustomPlot signals with controller methods. - void setConnected() - { - auto on_axis_range = [this](const QCPRange& newRange) { - m_blockUpdate = true; - auto item = m_self->currentItem(); - item->set_range(newRange.lower, newRange.upper); - m_blockUpdate = false; - }; - - *m_axisConn = QObject::connect( - m_axis, static_cast<void (QCPAxis::*)(const QCPRange&)>(&QCPAxis::rangeChanged), - on_axis_range); - } - - //! Disonnects QCustomPlot signals. - - void setDisconnected() { QObject::disconnect(*m_axisConn); } - - //! Sets axesRange from SessionItem. - void setAxisRangeFromItem() - { - auto [lower, upper] = m_self->currentItem()->range(); - m_axis->setRange(QCPRange(lower, upper)); - } - - //! Sets log scale from item. - - void setAxisLogScaleFromItem() - { - Utils::SetLogarithmicScale(m_axis, m_self->currentItem()->is_in_log()); - } - - //! Init axis from item and setup connections. - - void init_axis() - { - m_titleController = std::make_unique<AxisTitleController>(m_axis); - auto text_item = m_self->currentItem()->item<TextItem>(ViewportAxisItem::P_TITLE); - m_titleController->setItem(text_item); - setAxisRangeFromItem(); - setAxisLogScaleFromItem(); - setConnected(); - } - - void updateLowerRange(const ViewportAxisItem* item) - { - setDisconnected(); - m_axis->setRangeLower(item->property<double>(ViewportAxisItem::P_MIN)); - setConnected(); - } - - void updateUpperRange(const ViewportAxisItem* item) - { - setDisconnected(); - m_axis->setRangeUpper(item->property<double>(ViewportAxisItem::P_MAX)); - setConnected(); - } - - ~AxesPlotControllerImpl() { setDisconnected(); } -}; - -ViewportAxisPlotController::ViewportAxisPlotController(QCPAxis* axis) - : p_impl(std::make_unique<AxesPlotControllerImpl>(this, axis)) - -{ -} - -ViewportAxisPlotController::~ViewportAxisPlotController() = default; - -void ViewportAxisPlotController::subscribe() -{ - auto on_property_change = [this](SessionItem*, std::string name) { - if (p_impl->m_blockUpdate) - return; - - if (name == ViewportAxisItem::P_MIN) - p_impl->updateLowerRange(currentItem()); - - if (name == ViewportAxisItem::P_MAX) - p_impl->updateUpperRange(currentItem()); - - if (name == ViewportAxisItem::P_IS_LOG) - p_impl->setAxisLogScaleFromItem(); - - p_impl->m_axis->parentPlot()->replot(); - }; - setOnPropertyChange(on_property_change); - - p_impl->init_axis(); -} - -void ViewportAxisPlotController::unsubscribe() -{ - p_impl->setDisconnected(); -} diff --git a/mvvm/view/mvvm/plotting/viewportaxisplotcontroller.h b/mvvm/view/mvvm/plotting/viewportaxisplotcontroller.h deleted file mode 100644 index 622bc076d53..00000000000 --- a/mvvm/view/mvvm/plotting/viewportaxisplotcontroller.h +++ /dev/null @@ -1,47 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/plotting/viewportaxisplotcontroller.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_VIEWPORTAXISPLOTCONTROLLER_H -#define BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_VIEWPORTAXISPLOTCONTROLLER_H - -#include "mvvm/signals/itemlistener.h" -#include "mvvm/view_export.h" -#include <memory> - -class QCPAxis; - -namespace ModelView { - -class ViewportAxisItem; - -//! Establishes communication between QCPAxis and ViewportAxisItem. -//! Provide mutual update of axis parameters (min, max, title) for two axes representations. - -class MVVM_VIEW_EXPORT ViewportAxisPlotController : public ItemListener<ViewportAxisItem> { -public: - explicit ViewportAxisPlotController(QCPAxis* axis); - ~ViewportAxisPlotController() override; - -protected: - void subscribe() override; - void unsubscribe() override; - -public: - struct AxesPlotControllerImpl; - std::unique_ptr<AxesPlotControllerImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_PLOTTING_VIEWPORTAXISPLOTCONTROLLER_H diff --git a/mvvm/view/mvvm/widgets/CMakeLists.txt b/mvvm/view/mvvm/widgets/CMakeLists.txt deleted file mode 100644 index 89c16a7c7b9..00000000000 --- a/mvvm/view/mvvm/widgets/CMakeLists.txt +++ /dev/null @@ -1,26 +0,0 @@ -target_sources(${library_name} PRIVATE - adjustingscrollarea.cpp - adjustingscrollarea.h - allitemstreeview.cpp - allitemstreeview.h - collapsiblebar.cpp - collapsiblebar.h - collapsiblelistwidget.cpp - collapsiblelistwidget.h - itemstreeview.cpp - itemstreeview.h - itemstreeviewinterface.h - layoututils.cpp - layoututils.h - propertyflatview.cpp - propertyflatview.h - propertytreeview.cpp - propertytreeview.h - standardtreeviews.h - statuslabel.cpp - statuslabel.h - topitemstreeview.cpp - topitemstreeview.h - widgetutils.cpp - widgetutils.h -) diff --git a/mvvm/view/mvvm/widgets/adjustingscrollarea.cpp b/mvvm/view/mvvm/widgets/adjustingscrollarea.cpp deleted file mode 100644 index f0b67df1ad3..00000000000 --- a/mvvm/view/mvvm/widgets/adjustingscrollarea.cpp +++ /dev/null @@ -1,51 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/widgets/adjustingscrollarea.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/widgets/adjustingscrollarea.h" -#include <QEvent> -#include <QScrollBar> - -using namespace ModelView; - -AdjustingScrollArea::AdjustingScrollArea(QWidget* parent) : QScrollArea(parent) -{ - setObjectName("AdjustingScrollArea"); - setContentsMargins(0, 0, 0, 0); - setWidgetResizable(true); - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - setStyleSheet("QScrollArea#AdjustingScrollArea {border: 0px; background-color:transparent;}"); -} - -void AdjustingScrollArea::setWidget(QWidget* w) -{ - QScrollArea::setWidget(w); - w->installEventFilter(this); -} - -QSize AdjustingScrollArea::sizeHint() const -{ - auto horizontal = horizontalScrollBar(); - QSize result(viewport()->width(), widget()->height() + horizontal->height() * 2); - return result; -} - -bool AdjustingScrollArea::eventFilter(QObject* obj, QEvent* ev) -{ - if (obj == widget() && ev->type() != QEvent::Resize) { - widget()->setMaximumWidth(viewport()->width()); - setMaximumHeight(height() - viewport()->height() + widget()->height()); - } - - return QScrollArea::eventFilter(obj, ev); -} diff --git a/mvvm/view/mvvm/widgets/adjustingscrollarea.h b/mvvm/view/mvvm/widgets/adjustingscrollarea.h deleted file mode 100644 index 6d6f11c3b00..00000000000 --- a/mvvm/view/mvvm/widgets/adjustingscrollarea.h +++ /dev/null @@ -1,41 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/widgets/adjustingscrollarea.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_ADJUSTINGSCROLLAREA_H -#define BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_ADJUSTINGSCROLLAREA_H - -#include "mvvm/view_export.h" -#include <QScrollArea> - -namespace ModelView { - -//! Modification of standard scroll area, which makes widget with dynamic layout occupy the whole -//! available space. - -class MVVM_VIEW_EXPORT AdjustingScrollArea : public QScrollArea { - Q_OBJECT - -public: - AdjustingScrollArea(QWidget* parent = 0); - void setWidget(QWidget* w); - - QSize sizeHint() const; - -private: - bool eventFilter(QObject* obj, QEvent* ev); -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_ADJUSTINGSCROLLAREA_H diff --git a/mvvm/view/mvvm/widgets/allitemstreeview.cpp b/mvvm/view/mvvm/widgets/allitemstreeview.cpp deleted file mode 100644 index b8b09ff3842..00000000000 --- a/mvvm/view/mvvm/widgets/allitemstreeview.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/widgets/allitemstreeview.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/widgets/allitemstreeview.h" -#include "mvvm/factories/viewmodelfactory.h" -#include "mvvm/viewmodel/viewmodel.h" - -namespace ModelView { -AllItemsTreeView::AllItemsTreeView(SessionModel* model, QWidget* parent) : ItemsTreeView(parent) -{ - setViewModel(Factory::CreateDefaultViewModel(model)); -} - -AllItemsTreeView::~AllItemsTreeView() = default; - -} // namespace ModelView diff --git a/mvvm/view/mvvm/widgets/allitemstreeview.h b/mvvm/view/mvvm/widgets/allitemstreeview.h deleted file mode 100644 index 80520f44fdd..00000000000 --- a/mvvm/view/mvvm/widgets/allitemstreeview.h +++ /dev/null @@ -1,36 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/widgets/allitemstreeview.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_ALLITEMSTREEVIEW_H -#define BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_ALLITEMSTREEVIEW_H - -#include "mvvm/widgets/itemstreeview.h" - -namespace ModelView { - -class SessionModel; - -//! Widget holding standard QTreeView and intended for displaying all items of SessionModel. - -class MVVM_VIEW_EXPORT AllItemsTreeView : public ItemsTreeView { - Q_OBJECT - -public: - AllItemsTreeView(SessionModel* model, QWidget* parent = nullptr); - ~AllItemsTreeView() override; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_ALLITEMSTREEVIEW_H diff --git a/mvvm/view/mvvm/widgets/collapsiblebar.cpp b/mvvm/view/mvvm/widgets/collapsiblebar.cpp deleted file mode 100644 index a90ecf979ed..00000000000 --- a/mvvm/view/mvvm/widgets/collapsiblebar.cpp +++ /dev/null @@ -1,76 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/widgets/collapsiblebar.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/widgets/collapsiblebar.h" -#include "mvvm/widgets/widgetutils.h" -#include <QEvent> -#include <QHBoxLayout> -#include <QIcon> -#include <QLabel> -#include <QMouseEvent> - -using namespace ModelView; - -CollapsibleBar::CollapsibleBar(QWidget* parent) - : QFrame(parent), m_pixmapLabel(new QLabel), m_titleLabel(new QLabel) -{ - m_pixmapLabel->setPixmap(QPixmap(":/icons/chevron-down.svg")); - - auto layout = new QHBoxLayout(this); - layout->setContentsMargins(0, 4, 0, 0); - - layout->addWidget(m_pixmapLabel, Qt::AlignLeft); - layout->addWidget(m_titleLabel, Qt::AlignCenter); - - setFixedHeight(ModelView::Utils::HeightOfLetterM() * 2); - setFrameStyle(QFrame::StyledPanel | QFrame::Raised); -} - -void CollapsibleBar::setWidget(QWidget* widget, const QString& title) -{ - m_controlledWidget = widget; - m_titleLabel->setText(title); - widget->installEventFilter(this); - updatePixmap(); -} - -void CollapsibleBar::mousePressEvent(QMouseEvent* event) -{ - if (event->button() == Qt::LeftButton) - m_controlledWidget->setHidden(m_controlledWidget->isVisible()); - updatePixmap(); -} - -//! Listens for widget signals and update collapse/expand icon on visibility change. - -bool CollapsibleBar::eventFilter(QObject* obj, QEvent* event) -{ - bool is_event_of_interest = (event->type() == QEvent::Show || event->type() == QEvent::Hide); - if (obj == m_controlledWidget && is_event_of_interest) - updatePixmap(); - return QObject::eventFilter(obj, event); -} - -//! Set pixmap depending from the visibility of the widget. - -void CollapsibleBar::updatePixmap() -{ - if (m_controlledWidget->isVisible()) { - m_pixmapLabel->setPixmap(QPixmap(":/icons/chevron-down.svg")); - setFrameStyle(QFrame::StyledPanel); - } else { - m_pixmapLabel->setPixmap(QPixmap(":/icons/chevron-right.svg")); - setFrameStyle(QFrame::StyledPanel | QFrame::Raised); - } -} diff --git a/mvvm/view/mvvm/widgets/collapsiblebar.h b/mvvm/view/mvvm/widgets/collapsiblebar.h deleted file mode 100644 index 0c04f97bba3..00000000000 --- a/mvvm/view/mvvm/widgets/collapsiblebar.h +++ /dev/null @@ -1,51 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/widgets/collapsiblebar.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_COLLAPSIBLEBAR_H -#define BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_COLLAPSIBLEBAR_H - -#include "mvvm/view_export.h" -#include <QFrame> - -class QLabel; -class QString; - -namespace ModelView { - -//! Horizontal collapsible bar, part of CollapsibleListWidget. -//! Intended for placement into the QSplitter, makes client widget visible/invisible on clicks. - -class MVVM_VIEW_EXPORT CollapsibleBar : public QFrame { - Q_OBJECT - -public: - CollapsibleBar(QWidget* parent = nullptr); - - void setWidget(QWidget* widget, const QString& title); - -protected: - void mousePressEvent(QMouseEvent* event) override; - -private: - bool eventFilter(QObject* obj, QEvent* event) override; - void updatePixmap(); - - QWidget* m_controlledWidget{nullptr}; - QLabel* m_pixmapLabel{nullptr}; - QLabel* m_titleLabel{nullptr}; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_COLLAPSIBLEBAR_H diff --git a/mvvm/view/mvvm/widgets/collapsiblelistwidget.cpp b/mvvm/view/mvvm/widgets/collapsiblelistwidget.cpp deleted file mode 100644 index 06d615beb31..00000000000 --- a/mvvm/view/mvvm/widgets/collapsiblelistwidget.cpp +++ /dev/null @@ -1,47 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/widgets/collapsiblelistwidget.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/widgets/collapsiblelistwidget.h" -#include "mvvm/widgets/collapsiblebar.h" -#include <QSplitter> -#include <QVBoxLayout> - -using namespace ModelView; - -CollapsibleListWidget::CollapsibleListWidget(QWidget* parent) - : QWidget(parent), m_splitter(new QSplitter) -{ - m_splitter->setOrientation(Qt::Vertical); - - auto layout = new QVBoxLayout(this); - layout->setContentsMargins(0, 0, 0, 0); - layout->addWidget(m_splitter); -} - -void CollapsibleListWidget::addWidget(QWidget* widget, const QString& title, bool collapsed) -{ - // add bar which will be uncollapsible and will control the appearance of our widget - auto bar = new CollapsibleBar(m_splitter); - m_splitter->addWidget(bar); - - // add widget itself - m_splitter->addWidget(widget); - - // setup bar for widget - bar->setWidget(widget, title); - m_splitter->setCollapsible(m_splitter->indexOf(bar), false); - - if (collapsed) - widget->setVisible(false); -} diff --git a/mvvm/view/mvvm/widgets/collapsiblelistwidget.h b/mvvm/view/mvvm/widgets/collapsiblelistwidget.h deleted file mode 100644 index 34482f26f87..00000000000 --- a/mvvm/view/mvvm/widgets/collapsiblelistwidget.h +++ /dev/null @@ -1,44 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/widgets/collapsiblelistwidget.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_COLLAPSIBLELISTWIDGET_H -#define BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_COLLAPSIBLELISTWIDGET_H - -#include "mvvm/view_export.h" -#include <QWidget> - -class QSplitter; -class QString; - -namespace ModelView { - -//! Vertical widget with column of panels displayed one under another. -//! Each panel contains user widget and can be collapsed/expanded. When expanded, -//! the place occupied by the panel can be changed by draging a splitter. - -class MVVM_VIEW_EXPORT CollapsibleListWidget : public QWidget { - Q_OBJECT - -public: - CollapsibleListWidget(QWidget* parent = nullptr); - - void addWidget(QWidget* widget, const QString& title, bool collapsed = false); - -private: - QSplitter* m_splitter{nullptr}; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_COLLAPSIBLELISTWIDGET_H diff --git a/mvvm/view/mvvm/widgets/itemstreeview.cpp b/mvvm/view/mvvm/widgets/itemstreeview.cpp deleted file mode 100644 index 43be9b2f826..00000000000 --- a/mvvm/view/mvvm/widgets/itemstreeview.cpp +++ /dev/null @@ -1,113 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/widgets/itemstreeview.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/widgets/itemstreeview.h" -#include "mvvm/viewmodel/viewmodel.h" -#include "mvvm/viewmodel/viewmodeldelegate.h" -#include <QTreeView> -#include <QVBoxLayout> - -using namespace ModelView; - -ItemsTreeView::ItemsTreeView(QWidget* parent) - : QWidget(parent) - , m_treeView(new QTreeView) - , m_delegate(std::make_unique<ViewModelDelegate>()) - , m_block_selection(false) -{ - auto layout = new QVBoxLayout; - layout->setMargin(0); - layout->setSpacing(0); - layout->addWidget(m_treeView); - setLayout(layout); -} - -ItemsTreeView::~ItemsTreeView() = default; - -void ItemsTreeView::setViewModel(std::unique_ptr<ViewModel> viewModel) -{ - m_viewModel = std::move(viewModel); - m_treeView->setItemDelegate(m_delegate.get()); - m_treeView->setModel(m_viewModel.get()); - m_treeView->expandAll(); - m_treeView->resizeColumnToContents(0); - set_connected(true); -} - -void ItemsTreeView::setViewModelDelegate(std::unique_ptr<ViewModelDelegate> delegate) -{ - m_delegate = std::move(delegate); -} - -//! Make given item selected in QTreeView. - -void ItemsTreeView::setSelected(SessionItem* item) -{ - if (!m_viewModel) - return; - - auto indexes = m_viewModel->indexOfSessionItem(item); - if (!indexes.empty()) - selectionModel()->select(indexes.at(0), QItemSelectionModel::SelectCurrent); -} - -void ItemsTreeView::setRootSessionItem(SessionItem* item) -{ - m_viewModel->setRootSessionItem(item); - m_treeView->expandAll(); -} - -ViewModel* ItemsTreeView::viewModel() const -{ - return m_viewModel.get(); -} - -//! Processes selections in QTreeView. Finds SessionItem corresponding to selected indexes -//! and emit itemSelected signal. - -void ItemsTreeView::onSelectionChanged(const QItemSelection&, const QItemSelection&) -{ - if (m_block_selection) - return; - - auto indexes = m_treeView->selectionModel()->selectedIndexes(); - if (!indexes.empty()) { - auto item = m_viewModel->sessionItemFromIndex(indexes.at(0)); - m_block_selection = true; - itemSelected(item); - m_block_selection = false; - } -} - -void ItemsTreeView::set_connected(bool flag) -{ - Q_ASSERT(selectionModel()); - - if (flag) - connect(selectionModel(), &QItemSelectionModel::selectionChanged, this, - &ItemsTreeView::onSelectionChanged); - else - disconnect(selectionModel(), &QItemSelectionModel::selectionChanged, this, - &ItemsTreeView::onSelectionChanged); -} - -QTreeView* ItemsTreeView::treeView() -{ - return m_treeView; -} - -QItemSelectionModel* ItemsTreeView::selectionModel() -{ - return m_treeView->selectionModel(); -} diff --git a/mvvm/view/mvvm/widgets/itemstreeview.h b/mvvm/view/mvvm/widgets/itemstreeview.h deleted file mode 100644 index 4608fd9e089..00000000000 --- a/mvvm/view/mvvm/widgets/itemstreeview.h +++ /dev/null @@ -1,73 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/widgets/itemstreeview.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_ITEMSTREEVIEW_H -#define BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_ITEMSTREEVIEW_H - -#include "mvvm/view_export.h" -#include <QWidget> -#include <memory> - -class QTreeView; -class QItemSelection; -class QItemSelectionModel; - -namespace ModelView { - -class SessionItem; -class ViewModel; -class ViewModelDelegate; - -//! Tree view to show items of SessionModel via ViewModel mechanism. -//! Provides notification mechanism for SessionItem selections, use custom delegate. - -class MVVM_VIEW_EXPORT ItemsTreeView : public QWidget { - Q_OBJECT - -public: - explicit ItemsTreeView(QWidget* parent = nullptr); - ~ItemsTreeView() override; - - QTreeView* treeView(); - - void setViewModel(std::unique_ptr<ViewModel> viewModel); - - void setViewModelDelegate(std::unique_ptr<ViewModelDelegate> delegate); - - void setSelected(SessionItem* item); - - void setRootSessionItem(SessionItem* item); - - ViewModel* viewModel() const; - -signals: - void itemSelected(ModelView::SessionItem*); - -private slots: - void onSelectionChanged(const QItemSelection&, const QItemSelection&); - -private: - QItemSelectionModel* selectionModel(); - - void set_connected(bool flag); - - QTreeView* m_treeView{nullptr}; - std::unique_ptr<ViewModel> m_viewModel; - std::unique_ptr<ViewModelDelegate> m_delegate; - bool m_block_selection; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_ITEMSTREEVIEW_H diff --git a/mvvm/view/mvvm/widgets/itemstreeviewinterface.h b/mvvm/view/mvvm/widgets/itemstreeviewinterface.h deleted file mode 100644 index ce455c9ac8c..00000000000 --- a/mvvm/view/mvvm/widgets/itemstreeviewinterface.h +++ /dev/null @@ -1,40 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/widgets/itemstreeviewinterface.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_ITEMSTREEVIEWINTERFACE_H -#define BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_ITEMSTREEVIEWINTERFACE_H - -#include "mvvm/view_export.h" -#include <QWidget> - -class QTreeView; - -namespace ModelView { - -class SessionModel; - -//! Saves and restores list of SessionModel's to/from disk using json format. - -class MVVM_VIEW_EXPORT ItemsTreeViewInterface : public QWidget { - Q_OBJECT - -public: - virtual void setSessionModel(SessionModel* model) = 0; - - virtual QTreeView* treeView() const = 0; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_ITEMSTREEVIEWINTERFACE_H diff --git a/mvvm/view/mvvm/widgets/layoututils.cpp b/mvvm/view/mvvm/widgets/layoututils.cpp deleted file mode 100644 index ce56020ec35..00000000000 --- a/mvvm/view/mvvm/widgets/layoututils.cpp +++ /dev/null @@ -1,125 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/widgets/layoututils.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/widgets/layoututils.h" -#include <QBoxLayout> -#include <QGridLayout> -#include <QLayoutItem> -#include <QWidget> - -namespace { -void remove(QGridLayout* layout, int row, int column, bool deleteWidgets); -void deleteChildWidgets(QLayoutItem* item); -} // namespace - -void GUI::Utils::Layout::clearLayout(QLayout* layout, bool deleteWidgets) -{ - if (!layout) - return; - - while (QLayoutItem* item = layout->takeAt(0)) { - if (deleteWidgets) - delete item->widget(); - if (QLayout* childLayout = item->layout()) - GUI::Utils::Layout::clearLayout(childLayout, deleteWidgets); - delete item; - } -} - -/** - * Removes all layout items on the given row from the given grid - * layout. If deleteWidgets is true, all concerned child widgets - * become not only removed from the layout, but also deleted. Note that - * this function doesn't actually remove the row itself from the grid - * layout, as this isn't possible (i.e. the rowCount() and row indices - * will stay the same after this function has been called). - */ - -void GUI::Utils::Layout::removeRow(QGridLayout* layout, int row, bool deleteWidgets) -{ - remove(layout, row, -1, deleteWidgets); - layout->setRowMinimumHeight(row, 0); - layout->setRowStretch(row, 0); -} - -/** - * Removes all layout items on the given column from the given grid - * layout. If deleteWidgets is true, all concerned child widgets - * become not only removed from the layout, but also deleted. Note that - * this function doesn't actually remove the column itself from the grid - * layout, as this isn't possible (i.e. the columnCount() and column - * indices will stay the same after this function has been called). - */ - -void GUI::Utils::Layout::removeColumn(QGridLayout* layout, int column, bool deleteWidgets) -{ - remove(layout, -1, column, deleteWidgets); - layout->setColumnMinimumWidth(column, 0); - layout->setColumnStretch(column, 0); -} - -void GUI::Utils::Layout::clearGridLayout(QGridLayout* layout, bool deleteWidgets) -{ - for (int i_row = 0; i_row < layout->rowCount(); ++i_row) { - GUI::Utils::Layout::removeRow(layout, i_row, deleteWidgets); - } -} - -namespace { - -/** - * Helper function. Removes all layout items within the given layout - * which either span the given row or column. If deleteWidgets - * is true, all concerned child widgets become not only removed from the - * layout, but also deleted. - */ - -void remove(QGridLayout* layout, int row, int column, bool deleteWidgets) -{ - // We avoid usage of QGridLayout::itemAtPosition() here to improve performance. - for (int i = layout->count() - 1; i >= 0; i--) { - int r, c, rs, cs; - layout->getItemPosition(i, &r, &c, &rs, &cs); - if ((r <= row && r + rs - 1 >= row) || (c <= column && c + cs - 1 >= column)) { - // This layout item is subject to deletion. - QLayoutItem* item = layout->takeAt(i); - if (deleteWidgets) - deleteChildWidgets(item); - delete item; - } - } -} - -/** - * Helper function. Deletes all child widgets of the given layout item. - */ - -void deleteChildWidgets(QLayoutItem* item) -{ - if (item->layout()) { - // Process all child items recursively. - for (int i = 0; i < item->layout()->count(); i++) - deleteChildWidgets(item->layout()->itemAt(i)); - } - item->widget()->deleteLater(); -} - -} // namespace - -QWidget* GUI::Utils::Layout::placeHolder() -{ - auto result = new QWidget; - result->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - return result; -} diff --git a/mvvm/view/mvvm/widgets/layoututils.h b/mvvm/view/mvvm/widgets/layoututils.h deleted file mode 100644 index 07de55c3781..00000000000 --- a/mvvm/view/mvvm/widgets/layoututils.h +++ /dev/null @@ -1,48 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/widgets/layoututils.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_LAYOUTUTILS_H -#define BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_LAYOUTUTILS_H - -#include "mvvm/view_export.h" - -class QLayout; -class QGridLayout; -class QWidget; - -//! @namespace LayoutUtils -//! @brief Utility functions to add/remove widgets to the layout on the fly. -//! Taken from https://stackoverflow.com/questions/5395266/removing-widgets-from-qgridlayout -//! Caveat: according to explanations given, grid layouts can only grow and never shrink. - -namespace GUI::Utils::Layout { - -//! Removes content from box layout. -MVVM_VIEW_EXPORT void clearLayout(QLayout* layout, bool deleteWidgets = true); - -//! Removes row from grid layout (important: doesn't change row count). -MVVM_VIEW_EXPORT void removeRow(QGridLayout* layout, int row, bool deleteWidgets = true); - -//! Removes column from grid layout. -MVVM_VIEW_EXPORT void removeColumn(QGridLayout* layout, int column, bool deleteWidgets = true); - -//! Clear layout completely. -MVVM_VIEW_EXPORT void clearGridLayout(QGridLayout* layout, bool deleteWidgets = true); - -//! Returns empty widget to occupy place in layout. -MVVM_VIEW_EXPORT QWidget* placeHolder(); - -} // namespace GUI::Utils::Layout - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_LAYOUTUTILS_H diff --git a/mvvm/view/mvvm/widgets/propertyflatview.cpp b/mvvm/view/mvvm/widgets/propertyflatview.cpp deleted file mode 100644 index ea5b427db6a..00000000000 --- a/mvvm/view/mvvm/widgets/propertyflatview.cpp +++ /dev/null @@ -1,164 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/widgets/propertyflatview.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/widgets/propertyflatview.h" -#include "mvvm/editors/customeditor.h" -#include "mvvm/editors/defaulteditorfactory.h" -#include "mvvm/factories/viewmodelfactory.h" -#include "mvvm/model/groupitem.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/viewmodel/standardviewitems.h" -#include "mvvm/viewmodel/viewmodel.h" -#include "mvvm/viewmodel/viewmodeldelegate.h" -#include "mvvm/widgets/layoututils.h" -#include <QDataWidgetMapper> -#include <QDebug> -#include <QGridLayout> -#include <QLabel> - -using namespace ModelView; - -struct PropertyFlatView::PropertyFlatViewImpl { - std::unique_ptr<ViewModel> view_model; - std::unique_ptr<ViewModelDelegate> m_delegate; - std::unique_ptr<DefaultEditorFactory> editor_factory; - std::vector<std::unique_ptr<QDataWidgetMapper>> widget_mappers; - std::map<ViewItem*, QWidget*> item_to_widget; - - QGridLayout* grid_layout{nullptr}; - PropertyFlatViewImpl() - : m_delegate(std::make_unique<ViewModelDelegate>()) - , editor_factory(std::make_unique<DefaultEditorFactory>()) - , grid_layout(new QGridLayout) - { - } - - //! Creates label for given index. - - std::unique_ptr<QLabel> create_label(ViewItem* view_item) - { - auto result = std::make_unique<QLabel>(view_item->data(Qt::DisplayRole).toString()); - result->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); - result->setEnabled(view_item->item()->isEnabled()); - return result; - } - - //! Creates custom editor for given index. - - std::unique_ptr<CustomEditor> create_editor(const QModelIndex& index) - { - auto editor = editor_factory->createEditor(index); - m_delegate->setEditorData(editor.get(), index); - connect(editor.get(), &CustomEditor::dataChanged, m_delegate.get(), - &ViewModelDelegate::onCustomEditorDataChanged); - editor->setEnabled(view_model->sessionItemFromIndex(index)->isEnabled()); - return editor; - } - - //! Connect model. - - void connect_model() - { - auto on_data_change = [this](const QModelIndex& topLeft, const QModelIndex&, - const QVector<int>& roles) { -#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) - QVector<int> expected_roles = {Qt::ForegroundRole}; -#else - QVector<int> expected_roles = {Qt::TextColorRole}; -#endif - if (roles == expected_roles) { - auto view_item = view_model->viewItemFromIndex(topLeft); - auto it = item_to_widget.find(view_item); - if (it != item_to_widget.end()) - it->second->setEnabled(view_item->item()->isEnabled()); - } - }; - connect(view_model.get(), &ViewModel::dataChanged, on_data_change); - - auto on_row_inserted = [this](const QModelIndex&, int, int) { update_grid_layout(); }; - connect(view_model.get(), &ViewModel::rowsInserted, on_row_inserted); - - auto on_row_removed = [this](const QModelIndex&, int, int) { update_grid_layout(); }; - connect(view_model.get(), &ViewModel::rowsRemoved, on_row_removed); - } - - //! Creates widget for given index to appear in grid layout. - - std::unique_ptr<QWidget> create_widget(const QModelIndex& index) - { - auto view_item = view_model->viewItemFromIndex(index); - if (auto label_item = dynamic_cast<ViewLabelItem*>(view_item); label_item) - return create_label(label_item); - - return create_editor(index); - } - - //! Creates row of widget mappers. Each widget mapper will serve all editors in a column. - - void update_mappers() - { - widget_mappers.clear(); - for (int row = 0; row < view_model->rowCount(); ++row) { - auto mapper = std::make_unique<QDataWidgetMapper>(); - mapper->setModel(view_model.get()); - mapper->setItemDelegate(m_delegate.get()); - mapper->setRootIndex(QModelIndex()); - mapper->setCurrentModelIndex(view_model->index(row, 0)); - widget_mappers.emplace_back(std::move(mapper)); - } - } - - //! Updates grid layout with all editors corresponding to the model. - - void update_grid_layout() - { - GUI::Utils::Layout::clearGridLayout(grid_layout, true); - - update_mappers(); - item_to_widget.clear(); - for (int row = 0; row < view_model->rowCount(); ++row) { - for (int col = 0; col < view_model->columnCount(); ++col) { - auto index = view_model->index(row, col); - auto widget = create_widget(index); - item_to_widget[view_model->viewItemFromIndex(index)] = widget.get(); - widget_mappers[static_cast<size_t>(row)]->addMapping(widget.get(), col); - grid_layout->addWidget(widget.release(), row, col); - } - } - } -}; - -PropertyFlatView::PropertyFlatView(QWidget* parent) - : QWidget(parent), p_impl(std::make_unique<PropertyFlatViewImpl>()) -{ - auto main_layout = new QVBoxLayout; - main_layout->setMargin(0); - main_layout->setSpacing(0); - - p_impl->grid_layout->setSpacing(6); - main_layout->addLayout(p_impl->grid_layout); - main_layout->addStretch(1); - - setLayout(main_layout); -} - -PropertyFlatView::~PropertyFlatView() = default; - -void PropertyFlatView::setItem(SessionItem* item) -{ - p_impl->view_model = Factory::CreatePropertyFlatViewModel(item->model()); - p_impl->view_model->setRootSessionItem(item); - p_impl->connect_model(); - p_impl->update_grid_layout(); -} diff --git a/mvvm/view/mvvm/widgets/propertyflatview.h b/mvvm/view/mvvm/widgets/propertyflatview.h deleted file mode 100644 index 543f1e12f9c..00000000000 --- a/mvvm/view/mvvm/widgets/propertyflatview.h +++ /dev/null @@ -1,45 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/widgets/propertyflatview.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_PROPERTYFLATVIEW_H -#define BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_PROPERTYFLATVIEW_H - -#include "mvvm/view_export.h" -#include <QWidget> -#include <memory> - -namespace ModelView { - -class SessionItem; - -//! Widget holding grid layout with editors and intended for displaying all properties of given -//! SessionItem. - -class MVVM_VIEW_EXPORT PropertyFlatView : public QWidget { - Q_OBJECT - -public: - PropertyFlatView(QWidget* parent = nullptr); - ~PropertyFlatView(); - - void setItem(SessionItem* item); - -private: - struct PropertyFlatViewImpl; - std::unique_ptr<PropertyFlatViewImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_PROPERTYFLATVIEW_H diff --git a/mvvm/view/mvvm/widgets/propertytreeview.cpp b/mvvm/view/mvvm/widgets/propertytreeview.cpp deleted file mode 100644 index c29f4320e89..00000000000 --- a/mvvm/view/mvvm/widgets/propertytreeview.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/widgets/propertytreeview.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/widgets/propertytreeview.h" -#include "mvvm/factories/viewmodelfactory.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/viewmodel/viewmodel.h" -#include <QTreeView> - -using namespace ModelView; - -PropertyTreeView::PropertyTreeView(QWidget* parent) : ItemsTreeView(parent) -{ - treeView()->setHeaderHidden(false); - // provide one click editing - treeView()->setEditTriggers(QAbstractItemView::AllEditTriggers); - treeView()->setAlternatingRowColors(true); -} - -void PropertyTreeView::setItem(SessionItem* item) -{ - if (!item) { - treeView()->setModel(nullptr); - return; - } - - setViewModel(Factory::CreatePropertyViewModel(item->model())); - viewModel()->setRootSessionItem(item); - treeView()->setRootIsDecorated(false); - treeView()->expandAll(); -} - -PropertyTreeView::~PropertyTreeView() = default; diff --git a/mvvm/view/mvvm/widgets/propertytreeview.h b/mvvm/view/mvvm/widgets/propertytreeview.h deleted file mode 100644 index c2576d83ffc..00000000000 --- a/mvvm/view/mvvm/widgets/propertytreeview.h +++ /dev/null @@ -1,37 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/widgets/propertytreeview.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_PROPERTYTREEVIEW_H -#define BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_PROPERTYTREEVIEW_H - -#include "mvvm/widgets/itemstreeview.h" - -namespace ModelView { - -//! Widget holding standard QTreeView and intended for displaying all properties of given -//! SessionItem. - -class MVVM_VIEW_EXPORT PropertyTreeView : public ItemsTreeView { - Q_OBJECT - -public: - PropertyTreeView(QWidget* parent = nullptr); - ~PropertyTreeView(); - - void setItem(SessionItem* item); -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_PROPERTYTREEVIEW_H diff --git a/mvvm/view/mvvm/widgets/standardtreeviews.h b/mvvm/view/mvvm/widgets/standardtreeviews.h deleted file mode 100644 index 56f8736b48d..00000000000 --- a/mvvm/view/mvvm/widgets/standardtreeviews.h +++ /dev/null @@ -1,25 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/widgets/standardtreeviews.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_STANDARDTREEVIEWS_H -#define BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_STANDARDTREEVIEWS_H - -//! @file mvvm/view/mvvm/widgets/standardtreeviews.h -//! @brief Collection of includes to get all standard tree views. - -#include "mvvm/widgets/allitemstreeview.h" -#include "mvvm/widgets/propertytreeview.h" -#include "mvvm/widgets/topitemstreeview.h" - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_STANDARDTREEVIEWS_H diff --git a/mvvm/view/mvvm/widgets/statuslabel.cpp b/mvvm/view/mvvm/widgets/statuslabel.cpp deleted file mode 100644 index 2950429018f..00000000000 --- a/mvvm/view/mvvm/widgets/statuslabel.cpp +++ /dev/null @@ -1,70 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/widgets/statuslabel.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/widgets/statuslabel.h" -#include "mvvm/editors/styleutils.h" -#include "mvvm/widgets/widgetutils.h" -#include <QColor> -#include <QFont> -#include <QPainter> - -using namespace ModelView; - -StatusLabel::StatusLabel(QWidget* parent) - : QFrame(parent), m_font("Monospace", Style::DefaultInfoBarTextSize(), QFont::Normal, false) -{ - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - setFixedHeight(Style::DefaultInfoBarHeight()); -} - -void StatusLabel::setText(const QString& text) -{ - m_text = text; - update(); -} - -void StatusLabel::setFont(const QFont& font) -{ - m_font = font; - update(); -} - -void StatusLabel::setPointSize(int pointSize) -{ - m_font.setPointSize(pointSize); - update(); -} - -void StatusLabel::setAlignment(Qt::Alignment alignment) -{ - m_alignment = alignment; - update(); -} - -void StatusLabel::paintEvent(QPaintEvent* event) -{ - QFrame::paintEvent(event); - - QPainter painter(this); - painter.setBrush(QColor(Qt::black)); - painter.setPen(QColor(Qt::black)); - painter.setFont(m_font); - - QRect bbox(0, 0, geometry().width(), geometry().height()); - const int gap(Utils::WidthOfLetterM() / 2); // make it smaller - auto textRect = bbox.adjusted(gap, 0, gap, 0); - - painter.fillRect(bbox, QColor(Qt::white)); - painter.drawText(textRect, static_cast<int>(m_alignment), m_text); -} diff --git a/mvvm/view/mvvm/widgets/statuslabel.h b/mvvm/view/mvvm/widgets/statuslabel.h deleted file mode 100644 index e36d75a35b7..00000000000 --- a/mvvm/view/mvvm/widgets/statuslabel.h +++ /dev/null @@ -1,51 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/widgets/statuslabel.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_STATUSLABEL_H -#define BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_STATUSLABEL_H - -#include "mvvm/view_export.h" -#include <QFrame> - -class QPaintEvent; - -namespace ModelView { - -//! Shows a single line of text on a white background. -//! Opposite to QLabel, doesn't trigger layout resize, being happy with place it has. If text string -//! is too long for current size, it will be clipped. - -class MVVM_VIEW_EXPORT StatusLabel : public QFrame { - Q_OBJECT - -public: - explicit StatusLabel(QWidget* parent = nullptr); - - void setText(const QString& text); - void setFont(const QFont& font); - void setPointSize(int pointSize); - void setAlignment(Qt::Alignment); - -protected: - void paintEvent(QPaintEvent* event); - -private: - QString m_text; - Qt::Alignment m_alignment; - QFont m_font; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_STATUSLABEL_H diff --git a/mvvm/view/mvvm/widgets/topitemstreeview.cpp b/mvvm/view/mvvm/widgets/topitemstreeview.cpp deleted file mode 100644 index c3a33187504..00000000000 --- a/mvvm/view/mvvm/widgets/topitemstreeview.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/widgets/topitemstreeview.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/widgets/topitemstreeview.h" -#include "mvvm/factories/viewmodelfactory.h" -#include "mvvm/viewmodel/viewmodel.h" - -namespace ModelView { -TopItemsTreeView::TopItemsTreeView(SessionModel* model, QWidget* parent) : ItemsTreeView(parent) -{ - setViewModel(Factory::CreateTopItemsViewModel(model)); -} - -TopItemsTreeView::~TopItemsTreeView() = default; - -} // namespace ModelView diff --git a/mvvm/view/mvvm/widgets/topitemstreeview.h b/mvvm/view/mvvm/widgets/topitemstreeview.h deleted file mode 100644 index 391aff252ed..00000000000 --- a/mvvm/view/mvvm/widgets/topitemstreeview.h +++ /dev/null @@ -1,40 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/widgets/topitemstreeview.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_TOPITEMSTREEVIEW_H -#define BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_TOPITEMSTREEVIEW_H - -#include "mvvm/widgets/itemstreeview.h" - -namespace ModelView { - -class SessionModel; - -//! Widget holding standard QTreeView and intended for displaying all top level -//! items of SessionModel. - -//! All property items (i.e. "thickness", "color" etc) will be filtered out, top level items -//! (i.e. Layer, MultiLayer, ...) will be presented as simple parent/child tree. - -class MVVM_VIEW_EXPORT TopItemsTreeView : public ItemsTreeView { - Q_OBJECT - -public: - TopItemsTreeView(SessionModel* model, QWidget* parent = nullptr); - ~TopItemsTreeView(); -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_TOPITEMSTREEVIEW_H diff --git a/mvvm/view/mvvm/widgets/widgetutils.cpp b/mvvm/view/mvvm/widgets/widgetutils.cpp deleted file mode 100644 index d9855e1c792..00000000000 --- a/mvvm/view/mvvm/widgets/widgetutils.cpp +++ /dev/null @@ -1,179 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/widgets/widgetutils.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/widgets/widgetutils.h" -#include "mvvm/utils/numericutils.h" -#include <QApplication> -#include <QColor> -#include <QDir> -#include <QFontMetrics> -#include <QLabel> -#include <QMainWindow> -#include <QSize> - -namespace { - -//! Calculates size of letter `M` for current system font settings. - -QSize FindSizeOfLetterM() -{ - QFontMetrics fontMetric(QApplication::font()); - auto em = fontMetric.horizontalAdvance('M'); - auto fontAscent = fontMetric.ascent(); - return QSize(em, fontAscent); -} - -const QString untitled_name = "Untitled"; - -} // namespace - -QColor ModelView::Utils::RandomColor() -{ - auto rndm = []() -> int { return ModelView::Utils::RandInt(0, 255); }; - return QColor(rndm(), rndm(), rndm()); -} - -std::string ModelView::Utils::RandomNamedColor() -{ - return RandomColor().name().toStdString(); -} - -bool ModelView::Utils::IsWindowsHost() -{ -#if defined(Q_OS_WIN) - return true; -#else - return false; -#endif -} - -bool ModelView::Utils::IsMacHost() -{ -#if defined(Q_OS_MAC) - return true; -#else - return false; -#endif -} - -bool ModelView::Utils::IsLinuxHost() -{ -#if defined(Q_OS_LINUX) - return true; -#else - return false; -#endif -} - -QString ModelView::Utils::WithTildeHomePath(const QString& path) -{ - if (ModelView::Utils::IsWindowsHost()) - return path; - - static const QString homePath = QDir::homePath(); - - QFileInfo fi(QDir::cleanPath(path)); - QString outPath = fi.absoluteFilePath(); - if (outPath.startsWith(homePath)) - outPath = QLatin1Char('~') + outPath.mid(homePath.size()); - else - outPath = path; - return outPath; -} - -//! Project without projectDir will be "Untitled", modified project will be "*Untitled". -//! Project with projectDir in "/home/user/project1" will get title "project1". - -QString ModelView::Utils::ProjectWindowTitle(const QString& project_dir, bool is_modified) -{ - auto pos = project_dir.lastIndexOf('/'); - auto project_name = (pos == -1) ? untitled_name : project_dir.mid(pos + 1); - auto unsaved_status = is_modified ? QString("*") : QString(); - return unsaved_status + project_name; -} - -int ModelView::Utils::WidthOfLetterM() -{ - return ModelView::Utils::SizeOfLetterM().width(); -} - -int ModelView::Utils::HeightOfLetterM() -{ - return ModelView::Utils::SizeOfLetterM().height(); -} - -QSize ModelView::Utils::SizeOfLetterM() -{ - static QSize result = FindSizeOfLetterM(); - return result; -} - -int ModelView::Utils::SystemPointSize() -{ - return QApplication::font().pointSize(); -} - -QMainWindow* ModelView::Utils::FindMainWindow() -{ - for (auto widget : qApp->topLevelWidgets()) { - if (auto result = dynamic_cast<QMainWindow*>(widget); result) - return result; - } - return nullptr; -} - -QString ModelView::Utils::ClickableText(const QString& text, const QString& tag) -{ - return QString("<a href=\"%1\">%2</a>").arg(tag.isEmpty() ? text : tag, text); -} - -void ModelView::Utils::ScaleLabelFont(QLabel* label, double scale) -{ - QFont font = label->font(); - font.setPointSize(ModelView::Utils::SystemPointSize() * scale); - label->setFont(font); -} - -QStringList ModelView::Utils::toStringList(const std::vector<std::string>& vec) -{ - QStringList result; - for (const auto& x : vec) - result.push_back(QString::fromStdString(x)); - return result; -} - -std::vector<std::string> ModelView::Utils::fromStringList(const QStringList& string_list) -{ - std::vector<std::string> result; - for (const auto& x : string_list) - result.push_back(x.toStdString()); - return result; -} - -QByteArray ModelView::Utils::serialize(const QStringList& data) -{ - QByteArray byteArray; - QDataStream out(&byteArray, QIODevice::WriteOnly); - out << data; - return byteArray; -} - -QStringList ModelView::Utils::deserialize(const QByteArray& byteArray) -{ - QByteArray array = byteArray; - QStringList result; - QDataStream in(&array, QIODevice::ReadOnly); - in >> result; - return result; -} diff --git a/mvvm/view/mvvm/widgets/widgetutils.h b/mvvm/view/mvvm/widgets/widgetutils.h deleted file mode 100644 index 2570897e1ad..00000000000 --- a/mvvm/view/mvvm/widgets/widgetutils.h +++ /dev/null @@ -1,94 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/view/mvvm/widgets/widgetutils.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_WIDGETUTILS_H -#define BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_WIDGETUTILS_H - -#include "mvvm/view_export.h" -#include <QString> -#include <QStringList> -#include <string> -#include <vector> - -class QColor; -class QSize; -class QMainWindow; -class QLabel; - -namespace ModelView { - -//! Collection of various widget-related utils. - -namespace Utils { - -//! Returns random color. -MVVM_VIEW_EXPORT QColor RandomColor(); - -//! Returns the name of random color. -MVVM_VIEW_EXPORT std::string RandomNamedColor(); - -//! Returns true if it is Windows. -MVVM_VIEW_EXPORT bool IsWindowsHost(); - -//! Returns true if it is Mac. -MVVM_VIEW_EXPORT bool IsMacHost(); - -//! Returns true if it is Linux. -MVVM_VIEW_EXPORT bool IsLinuxHost(); - -//! Returns a string where Linux path to the file is striped using '~/'. -MVVM_VIEW_EXPORT QString WithTildeHomePath(const QString& path); - -//! Returns a title composed from last part of project path, and `is_modified` flag. -MVVM_VIEW_EXPORT QString ProjectWindowTitle(const QString& project_dir, bool is_modified); - -//! Returns width of the letter 'M' deduced from current font metrics. -MVVM_VIEW_EXPORT int WidthOfLetterM(); - -//! Returns height of the letter 'M' deduced from current font metrics. -MVVM_VIEW_EXPORT int HeightOfLetterM(); - -//! Returns size corresponding to actual size of letter `M` basing on current font metrics. -MVVM_VIEW_EXPORT QSize SizeOfLetterM(); - -//! Returns size in points of default system font. -MVVM_VIEW_EXPORT int SystemPointSize(); - -//! Finds main window. -MVVM_VIEW_EXPORT QMainWindow* FindMainWindow(); - -//! Returns text wrapped into 'href' tag to provide clickable links in QLabel. -//! Example: <a href="tag">text</a>, if 'tag' is empty, 'text' will be used instead. -MVVM_VIEW_EXPORT QString ClickableText(const QString& text, const QString& tag = {}); - -//! Set label's font size to system font size scaled by given factor. -MVVM_VIEW_EXPORT void ScaleLabelFont(QLabel* label, double scale); - -//! Converts vector of strings to QStringList. -MVVM_VIEW_EXPORT QStringList toStringList(const std::vector<std::string>& vec); - -//! Converts vector of strings to QStringList. -MVVM_VIEW_EXPORT std::vector<std::string> fromStringList(const QStringList& string_list); - -//! Converts vector of strings to byte array. -MVVM_VIEW_EXPORT QByteArray serialize(const QStringList& data); - -//! Converts byte array to vector of strings. -MVVM_VIEW_EXPORT QStringList deserialize(const QByteArray& byteArray); - -} // namespace Utils - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEW_MVVM_WIDGETS_WIDGETUTILS_H diff --git a/mvvm/viewmodel/CMakeLists.txt b/mvvm/viewmodel/CMakeLists.txt deleted file mode 100644 index f251797068f..00000000000 --- a/mvvm/viewmodel/CMakeLists.txt +++ /dev/null @@ -1,33 +0,0 @@ -# ----------------------------------------------------------------------------- -# Library: mvvm_viewmodel -# ----------------------------------------------------------------------------- -set(library_name mvvm_viewmodel) - -add_library(${library_name} SHARED "") -add_subdirectory(mvvm) -add_library(MVVM::ViewModel ALIAS ${library_name}) # alias for build-tree usage - -# -- Generate header for export -- - -set(export_filename ${MVVM_AUTOGEN_DIR}/mvvm/viewmodel_export.h) -generate_export_header(${library_name} EXPORT_FILE_NAME ${export_filename}) - -# -- Dependencies -- - -target_link_libraries(${library_name} PUBLIC mvvm_model Qt5::Widgets) -target_include_directories(${library_name} - PUBLIC - $<INSTALL_INTERFACE:include> - $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}> $<BUILD_INTERFACE:${MVVM_AUTOGEN_DIR}> - ) - -# -- Definitions -- - -target_compile_features(${library_name} PUBLIC cxx_std_17) - -# -- Installation -- - -install(TARGETS ${library_name} EXPORT mvvm-targets LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) -set_target_properties(${library_name} PROPERTIES EXPORT_NAME ViewModel SOVERSION ${MVVM_SOVERSION} VERSION ${MVVM_BUILDVERSION}) -install(DIRECTORY mvvm/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/mvvm FILES_MATCHING PATTERN "*.h") -install(FILES ${export_filename} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/mvvm) diff --git a/mvvm/viewmodel/mvvm/CMakeLists.txt b/mvvm/viewmodel/mvvm/CMakeLists.txt deleted file mode 100644 index 4de42575264..00000000000 --- a/mvvm/viewmodel/mvvm/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -add_subdirectory(editors) -add_subdirectory(factories) -add_subdirectory(interfaces) -add_subdirectory(viewmodel) diff --git a/mvvm/viewmodel/mvvm/editors/CMakeLists.txt b/mvvm/viewmodel/mvvm/editors/CMakeLists.txt deleted file mode 100644 index 33efd3b655e..00000000000 --- a/mvvm/viewmodel/mvvm/editors/CMakeLists.txt +++ /dev/null @@ -1,35 +0,0 @@ -target_sources(${library_name} PRIVATE - booleditor.cpp - booleditor.h - coloreditor.cpp - coloreditor.h - combopropertyeditor.cpp - combopropertyeditor.h - customeditor.cpp - customeditor.h - customeventfilters.cpp - customeventfilters.h - defaulteditorfactory.cpp - defaulteditorfactory.h - doubleeditor.cpp - doubleeditor.h - editor_constants.h - editorbuilders.cpp - editorbuilders.h - externalpropertycomboeditor.cpp - externalpropertycomboeditor.h - externalpropertyeditor.cpp - externalpropertyeditor.h - integereditor.cpp - integereditor.h - scientificdoubleeditor.cpp - scientificdoubleeditor.h - scientificspinbox.cpp - scientificspinbox.h - scientificspinboxeditor.cpp - scientificspinboxeditor.h - selectablecomboboxeditor.cpp - selectablecomboboxeditor.h - styleutils.cpp - styleutils.h -) diff --git a/mvvm/viewmodel/mvvm/editors/booleditor.cpp b/mvvm/viewmodel/mvvm/editors/booleditor.cpp deleted file mode 100644 index 40618f6efd1..00000000000 --- a/mvvm/viewmodel/mvvm/editors/booleditor.cpp +++ /dev/null @@ -1,62 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/editors/booleditor.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/editors/booleditor.h" -#include <QCheckBox> -#include <QHBoxLayout> -#include <stdexcept> - -namespace { -const QString true_text = "True"; -const QString false_text = "False"; -} // namespace - -using namespace ModelView; - -BoolEditor::BoolEditor(QWidget* parent) : CustomEditor(parent), m_checkBox(new QCheckBox) - -{ - setAutoFillBackground(true); - auto layout = new QHBoxLayout; - layout->setContentsMargins(4, 0, 0, 0); - layout->addWidget(m_checkBox); - setLayout(layout); - - connect(m_checkBox, &QCheckBox::toggled, this, &BoolEditor::onCheckBoxChange); - setFocusProxy(m_checkBox); - m_checkBox->setText(true_text); -} - -bool BoolEditor::is_persistent() const -{ - return true; -} - -void BoolEditor::onCheckBoxChange(bool value) -{ - if (value != m_data.value<bool>()) - setDataIntern(QVariant(value)); -} - -void BoolEditor::update_components() -{ - if (m_data.type() != QVariant::Bool) - throw std::runtime_error("BoolEditor::update_components() -> Error. Wrong variant type"); - - bool value = m_data.value<bool>(); - m_checkBox->blockSignals(true); - m_checkBox->setChecked(value); - m_checkBox->setText(value ? true_text : false_text); - m_checkBox->blockSignals(false); -} diff --git a/mvvm/viewmodel/mvvm/editors/booleditor.h b/mvvm/viewmodel/mvvm/editors/booleditor.h deleted file mode 100644 index 6adbd8bc5d0..00000000000 --- a/mvvm/viewmodel/mvvm/editors/booleditor.h +++ /dev/null @@ -1,44 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/editors/booleditor.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_BOOLEDITOR_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_BOOLEDITOR_H - -#include "mvvm/editors/customeditor.h" - -class QCheckBox; - -namespace ModelView { - -//! Custom editor for QVariant based on bool values. - -class MVVM_VIEWMODEL_EXPORT BoolEditor : public CustomEditor { - Q_OBJECT - -public: - explicit BoolEditor(QWidget* parent = nullptr); - - bool is_persistent() const override; - -private slots: - void onCheckBoxChange(bool value); - -private: - void update_components() override; - QCheckBox* m_checkBox; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_BOOLEDITOR_H diff --git a/mvvm/viewmodel/mvvm/editors/coloreditor.cpp b/mvvm/viewmodel/mvvm/editors/coloreditor.cpp deleted file mode 100644 index 00055b736e5..00000000000 --- a/mvvm/viewmodel/mvvm/editors/coloreditor.cpp +++ /dev/null @@ -1,74 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/editors/coloreditor.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/editors/coloreditor.h" -#include "mvvm/editors/customeventfilters.h" -#include "mvvm/editors/styleutils.h" -#include "mvvm/model/customvariants.h" -#include <QColorDialog> -#include <QHBoxLayout> -#include <QLabel> -#include <stdexcept> - -using namespace ModelView; - -ColorEditor::ColorEditor(QWidget* parent) - : CustomEditor(parent), m_pixmapLabel(new QLabel), m_focusFilter(new LostFocusFilter(this)) - -{ - setMouseTracking(true); - setAutoFillBackground(true); - - auto layout = new QHBoxLayout; - layout->setContentsMargins(4, 0, 0, 0); - - layout->addWidget(m_pixmapLabel); - // layout->addWidget(m_textLabel); // no color name, only color rectangle - layout->addStretch(1); - setFocusPolicy(Qt::StrongFocus); - setAttribute(Qt::WA_InputMethodEnabled); - - setLayout(layout); -} - -void ColorEditor::mousePressEvent(QMouseEvent*) -{ - // temporarily installing filter to prevent loss of focus caused by too insistent dialog - installEventFilter(m_focusFilter); - - auto new_color = QColorDialog::getColor(currentColor()); - - removeEventFilter(m_focusFilter); - - if (new_color.isValid()) { - setDataIntern(new_color); - update_components(); - } -} - -QColor ColorEditor::currentColor() const -{ - return m_data.value<QColor>(); -} - -void ColorEditor::update_components() -{ - if (!Utils::IsColorVariant(m_data)) - throw std::runtime_error("ColorEditor::update_components() -> Error. Wrong variant type"); - - QPixmap pixmap(Style::DefaultPixmapSize(), Style::DefaultPixmapSize()); - pixmap.fill(currentColor()); - // m_textLabel->setText(currentColor().name()); - m_pixmapLabel->setPixmap(pixmap); -} diff --git a/mvvm/viewmodel/mvvm/editors/coloreditor.h b/mvvm/viewmodel/mvvm/editors/coloreditor.h deleted file mode 100644 index b7b1419a229..00000000000 --- a/mvvm/viewmodel/mvvm/editors/coloreditor.h +++ /dev/null @@ -1,48 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/editors/coloreditor.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_COLOREDITOR_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_COLOREDITOR_H - -#include "mvvm/editors/customeditor.h" - -class QLabel; - -namespace ModelView { - -class LostFocusFilter; - -//! Custom editor for QVariant based on QColor. - -class MVVM_VIEWMODEL_EXPORT ColorEditor : public CustomEditor { - Q_OBJECT - -public: - explicit ColorEditor(QWidget* parent = nullptr); - -protected: - void mousePressEvent(QMouseEvent* event) override; - -private: - QColor currentColor() const; - - void update_components() override; - QLabel* m_textLabel{nullptr}; - QLabel* m_pixmapLabel{nullptr}; - LostFocusFilter* m_focusFilter; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_COLOREDITOR_H diff --git a/mvvm/viewmodel/mvvm/editors/combopropertyeditor.cpp b/mvvm/viewmodel/mvvm/editors/combopropertyeditor.cpp deleted file mode 100644 index 0e1b52e1828..00000000000 --- a/mvvm/viewmodel/mvvm/editors/combopropertyeditor.cpp +++ /dev/null @@ -1,111 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/editors/combopropertyeditor.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/editors/combopropertyeditor.h" -#include "mvvm/model/comboproperty.h" -#include <QComboBox> -#include <QVBoxLayout> - -namespace { -QStringList toList(const std::vector<std::string>& container) -{ - QStringList result; - for (const auto& str : container) - result.push_back(QString::fromStdString(str)); - return result; -} -} // namespace - -using namespace ModelView; - -ComboPropertyEditor::ComboPropertyEditor(QWidget* parent) - : CustomEditor(parent), m_box(new QComboBox) -{ - setAutoFillBackground(true); - setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - - auto layout = new QVBoxLayout; - layout->setMargin(0); - layout->setSpacing(0); - layout->addWidget(m_box); - setLayout(layout); - - setConnected(true); -} - -QSize ComboPropertyEditor::sizeHint() const -{ - return m_box->sizeHint(); -} - -QSize ComboPropertyEditor::minimumSizeHint() const -{ - return m_box->minimumSizeHint(); -} - -bool ComboPropertyEditor::is_persistent() const -{ - return true; -} - -void ComboPropertyEditor::onIndexChanged(int index) -{ - auto comboProperty = m_data.value<ComboProperty>(); - - if (comboProperty.currentIndex() != index) { - comboProperty.setCurrentIndex(index); - setDataIntern(QVariant::fromValue<ComboProperty>(comboProperty)); - } -} - -void ComboPropertyEditor::update_components() -{ - setConnected(false); - - m_box->clear(); - m_box->insertItems(0, toList(internLabels())); - m_box->setCurrentIndex(internIndex()); - - setConnected(true); -} - -//! Returns list of labels for QComboBox - -std::vector<std::string> ComboPropertyEditor::internLabels() -{ - if (!m_data.canConvert<ComboProperty>()) - return {}; - auto comboProperty = m_data.value<ComboProperty>(); - return comboProperty.values(); -} - -//! Returns index for QComboBox. - -int ComboPropertyEditor::internIndex() -{ - if (!m_data.canConvert<ComboProperty>()) - return 0; - auto comboProperty = m_data.value<ComboProperty>(); - return comboProperty.currentIndex(); -} - -void ComboPropertyEditor::setConnected(bool isConnected) -{ - if (isConnected) - connect(m_box, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, - &ComboPropertyEditor::onIndexChanged, Qt::UniqueConnection); - else - disconnect(m_box, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), - this, &ComboPropertyEditor::onIndexChanged); -} diff --git a/mvvm/viewmodel/mvvm/editors/combopropertyeditor.h b/mvvm/viewmodel/mvvm/editors/combopropertyeditor.h deleted file mode 100644 index fd668306921..00000000000 --- a/mvvm/viewmodel/mvvm/editors/combopropertyeditor.h +++ /dev/null @@ -1,50 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/editors/combopropertyeditor.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_COMBOPROPERTYEDITOR_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_COMBOPROPERTYEDITOR_H - -#include "mvvm/editors/customeditor.h" - -class QComboBox; - -namespace ModelView { - -//! Custom editor for QVariant based on ComboProperty. - -class MVVM_VIEWMODEL_EXPORT ComboPropertyEditor : public CustomEditor { - Q_OBJECT - -public: - explicit ComboPropertyEditor(QWidget* parent = nullptr); - - QSize sizeHint() const override; - QSize minimumSizeHint() const override; - - bool is_persistent() const override; - -protected slots: - virtual void onIndexChanged(int index); - -private: - std::vector<std::string> internLabels(); - int internIndex(); - void setConnected(bool isConnected); - void update_components() override; - QComboBox* m_box; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_COMBOPROPERTYEDITOR_H diff --git a/mvvm/viewmodel/mvvm/editors/customeditor.cpp b/mvvm/viewmodel/mvvm/editors/customeditor.cpp deleted file mode 100644 index 191c196de35..00000000000 --- a/mvvm/viewmodel/mvvm/editors/customeditor.cpp +++ /dev/null @@ -1,47 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/editors/customeditor.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/editors/customeditor.h" - -using namespace ModelView; - -CustomEditor::CustomEditor(QWidget* parent) : QWidget(parent) {} - -QVariant CustomEditor::data() const -{ - return m_data; -} - -//! Returns true if editor should remains alive after editing finished. - -bool CustomEditor::is_persistent() const -{ - return false; -} - -//! Sets the data from model to editor. - -void CustomEditor::setData(const QVariant& data) -{ - m_data = data; - update_components(); -} - -//! Saves the data as given by editor's internal components and notifies the model. - -void CustomEditor::setDataIntern(const QVariant& data) -{ - m_data = data; - dataChanged(m_data); -} diff --git a/mvvm/viewmodel/mvvm/editors/customeditor.h b/mvvm/viewmodel/mvvm/editors/customeditor.h deleted file mode 100644 index 6059ceea182..00000000000 --- a/mvvm/viewmodel/mvvm/editors/customeditor.h +++ /dev/null @@ -1,53 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/editors/customeditor.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_CUSTOMEDITOR_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_CUSTOMEDITOR_H - -#include "mvvm/core/variant.h" -#include "mvvm/viewmodel_export.h" -#include <QWidget> - -namespace ModelView { - -//! Base class for all custom variant editors. - -class MVVM_VIEWMODEL_EXPORT CustomEditor : public QWidget { - Q_OBJECT - Q_PROPERTY(QVariant value MEMBER m_data READ data WRITE setData NOTIFY dataChanged USER true) - -public: - explicit CustomEditor(QWidget* parent = nullptr); - - QVariant data() const; - - virtual bool is_persistent() const; - -public slots: - void setData(const QVariant& data); - -signals: - //! Emmits signal when data was changed in an editor. - void dataChanged(QVariant value); - -protected: - void setDataIntern(const QVariant& data); - //! Should update widget components from m_data, if necessary. - virtual void update_components() = 0; - QVariant m_data; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_CUSTOMEDITOR_H diff --git a/mvvm/viewmodel/mvvm/editors/customeventfilters.cpp b/mvvm/viewmodel/mvvm/editors/customeventfilters.cpp deleted file mode 100644 index 883e3c857f4..00000000000 --- a/mvvm/viewmodel/mvvm/editors/customeventfilters.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/editors/customeventfilters.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/editors/customeventfilters.h" -#include <QAbstractSpinBox> -#include <QComboBox> -#include <QEvent> - -using namespace ModelView; - -LostFocusFilter::LostFocusFilter(QObject* parent) : QObject(parent) {} - -bool LostFocusFilter::eventFilter(QObject* obj, QEvent* event) -{ - if (event->type() == QEvent::FocusOut) - return true; - - return QObject::eventFilter(obj, event); -} - -// ---------------------------------------------------------------------------- - -WheelEventFilter::WheelEventFilter(QObject* parent) : QObject(parent) {} - -bool WheelEventFilter::eventFilter(QObject* obj, QEvent* event) -{ - if (auto spinBox = qobject_cast<QAbstractSpinBox*>(obj); spinBox) { - if (event->type() == QEvent::Wheel) { - if (spinBox->focusPolicy() == Qt::WheelFocus) { - event->accept(); - return false; - } else { - event->ignore(); - return true; - } - } else if (event->type() == QEvent::FocusIn) { - spinBox->setFocusPolicy(Qt::WheelFocus); - } else if (event->type() == QEvent::FocusOut) { - spinBox->setFocusPolicy(Qt::StrongFocus); - } - - } else if (auto comboBox = qobject_cast<QComboBox*>(obj); comboBox) { - if (event->type() == QEvent::Wheel) { - event->ignore(); - return true; - } else { - event->accept(); - return false; - } - } - return QObject::eventFilter(obj, event); -} diff --git a/mvvm/viewmodel/mvvm/editors/customeventfilters.h b/mvvm/viewmodel/mvvm/editors/customeventfilters.h deleted file mode 100644 index a7be1b10f08..00000000000 --- a/mvvm/viewmodel/mvvm/editors/customeventfilters.h +++ /dev/null @@ -1,53 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/editors/customeventfilters.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_CUSTOMEVENTFILTERS_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_CUSTOMEVENTFILTERS_H - -#include "mvvm/viewmodel_export.h" -#include <QObject> - -namespace ModelView { - -//! Event filter to prevent loss of the focus. -//! Can be used in the context of QTreeView and similar widgets to call external editor. Such an -//! editor is created by clicking on a cell of a tree and it appears as modal window on top of a -//! tree. - -class MVVM_VIEWMODEL_EXPORT LostFocusFilter : public QObject { - Q_OBJECT - -public: - LostFocusFilter(QObject* parent = nullptr); - -protected: - bool eventFilter(QObject* obj, QEvent* event) override; -}; - -//! Event filter to install on combo boxes and spin boxes to ignore wheel events during scrolling. -//! Helpful than the spin box is a child of some larger scroll area. - -class MVVM_VIEWMODEL_EXPORT WheelEventFilter : public QObject { - Q_OBJECT - -public: - WheelEventFilter(QObject* parent = nullptr); - -protected: - bool eventFilter(QObject* obj, QEvent* event); -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_CUSTOMEVENTFILTERS_H diff --git a/mvvm/viewmodel/mvvm/editors/defaulteditorfactory.cpp b/mvvm/viewmodel/mvvm/editors/defaulteditorfactory.cpp deleted file mode 100644 index 6dcd045a9ce..00000000000 --- a/mvvm/viewmodel/mvvm/editors/defaulteditorfactory.cpp +++ /dev/null @@ -1,131 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/editors/defaulteditorfactory.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/editors/defaulteditorfactory.h" -#include "mvvm/editors/customeditor.h" -#include "mvvm/editors/editor_constants.h" -#include "mvvm/model/customvariants.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/variant_constants.h" -#include "mvvm/viewmodel/viewmodel.h" - -using namespace ModelView; - -namespace { - -const SessionItem* itemFromIndex(const QModelIndex& index) -{ - auto model = dynamic_cast<const ViewModel*>(index.model()); - return model ? model->sessionItemFromIndex(index) : nullptr; -} - -} // namespace - -// ---------------------------------------------------------------------------- - -void AbstractEditorFactory::registerBuilder(const std::string& name, - EditorBuilders::builder_t builder) -{ - m_nameToBuilderMap[name] = std::move(builder); -} - -EditorBuilders::builder_t AbstractEditorFactory::findBuilder(const std::string& name) const -{ - auto it = m_nameToBuilderMap.find(name); - return it != m_nameToBuilderMap.end() ? it->second : EditorBuilders::builder_t(); -} - -// ---------------------------------------------------------------------------- - -RoleDependentEditorFactory::RoleDependentEditorFactory() -{ - // registering set of builders for given editor types - registerBuilder(GUI::Constants::BoolEditorType, EditorBuilders::BoolEditorBuilder()); - registerBuilder(GUI::Constants::ColorEditorType, EditorBuilders::ColorEditorBuilder()); - registerBuilder(GUI::Constants::ComboPropertyEditorType, - EditorBuilders::ComboPropertyEditorBuilder()); - registerBuilder(GUI::Constants::DoubleEditorType, EditorBuilders::DoubleEditorBuilder()); - registerBuilder(GUI::Constants::ExternalPropertyEditorType, - EditorBuilders::ExternalPropertyEditorBuilder()); - registerBuilder(GUI::Constants::IntegerEditorType, EditorBuilders::IntegerEditorBuilder()); - registerBuilder(GUI::Constants::ScientficDoubleEditorType, - EditorBuilders::ScientificDoubleEditorBuilder()); - registerBuilder(GUI::Constants::ScientficSpinBoxEditorType, - EditorBuilders::ScientificSpinBoxEditorBuilder()); - registerBuilder(GUI::Constants::SelectableComboPropertyEditorType, - EditorBuilders::SelectableComboPropertyEditorBuilder()); -} - -//! Creates cell editor basing on item role. It is expected that the index belongs to a ViewModel. - -std::unique_ptr<CustomEditor> -RoleDependentEditorFactory::createEditor(const QModelIndex& index) const -{ - auto item = itemFromIndex(index); - return item ? createItemEditor(item) : std::unique_ptr<CustomEditor>(); -} - -//! Creates cell editor basing on editor type. - -std::unique_ptr<CustomEditor> -RoleDependentEditorFactory::createItemEditor(const SessionItem* item) const -{ - auto builder = findBuilder(item->editorType()); - return builder ? builder(item) : std::unique_ptr<CustomEditor>(); -} - -// ---------------------------------------------------------------------------- - -VariantDependentEditorFactory::VariantDependentEditorFactory() -{ - // registering set of builders for given variant names - registerBuilder(GUI::Constants::bool_type_name, EditorBuilders::BoolEditorBuilder()); - registerBuilder(GUI::Constants::int_type_name, EditorBuilders::IntegerEditorBuilder()); - registerBuilder(GUI::Constants::double_type_name, - EditorBuilders::ScientificSpinBoxEditorBuilder()); - registerBuilder(GUI::Constants::qcolor_type_name, EditorBuilders::ColorEditorBuilder()); - registerBuilder(GUI::Constants::comboproperty_type_name, - EditorBuilders::ComboPropertyEditorBuilder()); - registerBuilder(GUI::Constants::extproperty_type_name, - EditorBuilders::ExternalPropertyEditorBuilder()); -} - -//! Creates cell editor basing on variant name. - -std::unique_ptr<CustomEditor> -VariantDependentEditorFactory::createEditor(const QModelIndex& index) const -{ - auto item = itemFromIndex(index); - auto value = item ? item->data<QVariant>() : index.data(Qt::EditRole); - auto builder = findBuilder(Utils::VariantName(value)); - return builder ? builder(item) : std::unique_ptr<CustomEditor>(); -} - -// ---------------------------------------------------------------------------- - -DefaultEditorFactory::DefaultEditorFactory() - : m_roleDependentFactory(std::make_unique<RoleDependentEditorFactory>()) - , m_variantDependentFactory(std::make_unique<VariantDependentEditorFactory>()) -{ -} - -//! Creates editor for given model index basing either on editorType() or specific variant name. - -std::unique_ptr<CustomEditor> DefaultEditorFactory::createEditor(const QModelIndex& index) const -{ - // trying to created an editor basing on possibly defined EDITOR role - auto editor = m_roleDependentFactory->createEditor(index); - // if we do not succeed, then creating editor from variant type - return editor ? std::move(editor) : m_variantDependentFactory->createEditor(index); -} diff --git a/mvvm/viewmodel/mvvm/editors/defaulteditorfactory.h b/mvvm/viewmodel/mvvm/editors/defaulteditorfactory.h deleted file mode 100644 index 8e1cc94f5ea..00000000000 --- a/mvvm/viewmodel/mvvm/editors/defaulteditorfactory.h +++ /dev/null @@ -1,79 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/editors/defaulteditorfactory.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_DEFAULTEDITORFACTORY_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_DEFAULTEDITORFACTORY_H - -//! @file mvvm/viewmodel/mvvm/editors/defaulteditorfactory.h -//! Defines DefaultEditorFactory and auxiliary classes for custom view model delegates. - -#include "mvvm/editors/editorbuilders.h" -#include "mvvm/interfaces/editorfactoryinterface.h" -#include <map> -#include <memory> - -namespace ModelView { - -//! Abstract editor factory for ViewModelDelegate. -//! Creates cell editors for Qt trees and tables from model index. Cell editor is -//! Qt widget intended for editing DATA role of some SessionItem. - -class MVVM_VIEWMODEL_EXPORT AbstractEditorFactory : public EditorFactoryInterface { -protected: - void registerBuilder(const std::string& name, EditorBuilders::builder_t builder); - EditorBuilders::builder_t findBuilder(const std::string& name) const; - - std::map<std::string, EditorBuilders::builder_t> m_nameToBuilderMap; -}; - -//! Editor factory for cell editors in Qt trees and tables, relies on EDITORTYPE role stored -//! on board of SessionItem. - -class MVVM_VIEWMODEL_EXPORT RoleDependentEditorFactory : public AbstractEditorFactory { -public: - RoleDependentEditorFactory(); - - std::unique_ptr<CustomEditor> createEditor(const QModelIndex& index) const override; - -protected: - std::unique_ptr<CustomEditor> createItemEditor(const SessionItem* item) const; -}; - -//! Editor factory for cell editors in Qt trees and tables, relies on variant type stored as -//! DATA role on board of SessionItem. - -class MVVM_VIEWMODEL_EXPORT VariantDependentEditorFactory : public AbstractEditorFactory { -public: - VariantDependentEditorFactory(); - - std::unique_ptr<CustomEditor> createEditor(const QModelIndex& index) const override; -}; - -//! Default editor factory for cell editors in Qt trees and tables. -//! Internaly it uses two factories - -class MVVM_VIEWMODEL_EXPORT DefaultEditorFactory : public EditorFactoryInterface { -public: - DefaultEditorFactory(); - - std::unique_ptr<CustomEditor> createEditor(const QModelIndex& index) const override; - -private: - std::unique_ptr<RoleDependentEditorFactory> m_roleDependentFactory; - std::unique_ptr<VariantDependentEditorFactory> m_variantDependentFactory; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_DEFAULTEDITORFACTORY_H diff --git a/mvvm/viewmodel/mvvm/editors/doubleeditor.cpp b/mvvm/viewmodel/mvvm/editors/doubleeditor.cpp deleted file mode 100644 index 35289f98969..00000000000 --- a/mvvm/viewmodel/mvvm/editors/doubleeditor.cpp +++ /dev/null @@ -1,74 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/editors/doubleeditor.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/editors/doubleeditor.h" -#include "mvvm/utils/numericutils.h" -#include <QDoubleSpinBox> -#include <QVBoxLayout> -#include <stdexcept> - -using namespace ModelView; - -DoubleEditor::DoubleEditor(QWidget* parent) - : CustomEditor(parent), m_doubleEditor(new QDoubleSpinBox) -{ - setFocusPolicy(Qt::StrongFocus); - m_doubleEditor->setFocusPolicy(Qt::StrongFocus); - m_doubleEditor->setKeyboardTracking(false); - - auto layout = new QVBoxLayout; - layout->setMargin(0); - layout->setSpacing(0); - - layout->addWidget(m_doubleEditor); - - connect(m_doubleEditor, - static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), - [=] { this->onEditingFinished(); }); - - setLayout(layout); - - setFocusProxy(m_doubleEditor); -} - -void DoubleEditor::setRange(double minimum, double maximum) -{ - m_doubleEditor->setRange(minimum, maximum); -} - -void DoubleEditor::setDecimals(int decimals) -{ - m_doubleEditor->setDecimals(decimals); -} - -void DoubleEditor::setSingleStep(double value) -{ - m_doubleEditor->setSingleStep(value); -} - -void DoubleEditor::onEditingFinished() -{ - double new_value = m_doubleEditor->value(); - - if (!Utils::AreAlmostEqual(new_value, m_data.value<double>())) - setDataIntern(QVariant::fromValue(new_value)); -} - -void DoubleEditor::update_components() -{ - if (m_data.type() != QVariant::Double) - throw std::runtime_error("DoubleEditor::update_components() -> Error. Wrong variant type"); - - m_doubleEditor->setValue(m_data.value<double>()); -} diff --git a/mvvm/viewmodel/mvvm/editors/doubleeditor.h b/mvvm/viewmodel/mvvm/editors/doubleeditor.h deleted file mode 100644 index 9b6ec57dc77..00000000000 --- a/mvvm/viewmodel/mvvm/editors/doubleeditor.h +++ /dev/null @@ -1,48 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/editors/doubleeditor.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_DOUBLEEDITOR_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_DOUBLEEDITOR_H - -#include "mvvm/editors/customeditor.h" - -class QDoubleSpinBox; - -namespace ModelView { - -//! Custom editor for QVariant based on double with possibility to set limits. - -class MVVM_VIEWMODEL_EXPORT DoubleEditor : public CustomEditor { - Q_OBJECT - -public: - explicit DoubleEditor(QWidget* parent = nullptr); - - void setRange(double minimum, double maximum); - - void setDecimals(int decimals); - - void setSingleStep(double value); - -private slots: - void onEditingFinished(); - -private: - void update_components() override; - QDoubleSpinBox* m_doubleEditor; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_DOUBLEEDITOR_H diff --git a/mvvm/viewmodel/mvvm/editors/editor_constants.h b/mvvm/viewmodel/mvvm/editors/editor_constants.h deleted file mode 100644 index 9465f4fa3b5..00000000000 --- a/mvvm/viewmodel/mvvm/editors/editor_constants.h +++ /dev/null @@ -1,39 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/editors/editor_constants.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_EDITOR_CONSTANTS_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_EDITOR_CONSTANTS_H - -//! @file mvvm/viewmodel/mvvm/editors/editor_constants.h -//! Collection of constants specific for cell editing. - -#include <string> - -namespace ModelView::Constants { - -const std::string BoolEditorType = "BoolEditor"; -const std::string ColorEditorType = "ColorEditor"; -const std::string ComboPropertyEditorType = "ComboPropertyEditor"; -const std::string DoubleEditorType = "DoubleEditor"; -const std::string ExternalPropertyEditorType = "ExternalPropertyEditor"; -const std::string IntegerEditorType = "IntegerEditor"; -const std::string ScientficDoubleEditorType = "ScientficDoubleEditor"; -const std::string ScientficSpinBoxEditorType = "ScientficSpinBoxEditor"; -const std::string SelectableComboPropertyEditorType = "SelectableComboPropertyEditor"; - -const int default_double_decimals = 4; //! number of digits after decimal points - -} // namespace ModelView::Constants - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_EDITOR_CONSTANTS_H diff --git a/mvvm/viewmodel/mvvm/editors/editorbuilders.cpp b/mvvm/viewmodel/mvvm/editors/editorbuilders.cpp deleted file mode 100644 index bea9b9640ce..00000000000 --- a/mvvm/viewmodel/mvvm/editors/editorbuilders.cpp +++ /dev/null @@ -1,141 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/editors/editorbuilders.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/editors/editorbuilders.h" -#include "mvvm/editors/booleditor.h" -#include "mvvm/editors/coloreditor.h" -#include "mvvm/editors/combopropertyeditor.h" -#include "mvvm/editors/doubleeditor.h" -#include "mvvm/editors/editor_constants.h" -#include "mvvm/editors/externalpropertyeditor.h" -#include "mvvm/editors/integereditor.h" -#include "mvvm/editors/scientificdoubleeditor.h" -#include "mvvm/editors/scientificspinboxeditor.h" -#include "mvvm/editors/selectablecomboboxeditor.h" -#include "mvvm/model/customvariants.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/utils/reallimits.h" -#include <cmath> - -namespace { -double singleStep(int decimals) -{ - // For item with decimals=3 (i.e. 0.001) single step will be 0.1 - return 1. / std::pow(10., decimals - 1); -} - -double getStep(double val) -{ - return val == 0.0 ? 1.0 : val / 100.; -} - -} // namespace - -namespace ModelView ::EditorBuilders { - -builder_t BoolEditorBuilder() -{ - auto builder = [](const SessionItem*) -> editor_t { return std::make_unique<BoolEditor>(); }; - return builder; -} - -builder_t IntegerEditorBuilder() -{ - auto builder = [](const SessionItem* item) -> editor_t { - auto editor = std::make_unique<IntegerEditor>(); - if (item && item->hasData(ItemDataRole::LIMITS)) { - auto limits = item->data<RealLimits>(ItemDataRole::LIMITS); - editor->setRange(limits.lowerLimit(), limits.upperLimit()); - } - return editor; - }; - return builder; -} - -builder_t DoubleEditorBuilder() -{ - auto builder = [](const SessionItem* item) -> editor_t { - auto editor = std::make_unique<DoubleEditor>(); - if (item && item->hasData(ItemDataRole::LIMITS)) { - auto limits = item->data<RealLimits>(ItemDataRole::LIMITS); - editor->setRange(limits.lowerLimit(), limits.upperLimit()); - editor->setSingleStep(singleStep(GUI::Constants::default_double_decimals)); - editor->setDecimals(GUI::Constants::default_double_decimals); - } - return editor; - }; - return builder; -} - -builder_t ScientificDoubleEditorBuilder() -{ - auto builder = [](const SessionItem* item) -> editor_t { - auto editor = std::make_unique<ScientificDoubleEditor>(); - if (item && item->hasData(ItemDataRole::LIMITS)) { - auto limits = item->data<RealLimits>(ItemDataRole::LIMITS); - editor->setRange(limits.lowerLimit(), limits.upperLimit()); - } - return editor; - }; - return builder; -} - -builder_t ScientificSpinBoxEditorBuilder() -{ - auto builder = [](const SessionItem* item) -> editor_t { - auto editor = std::make_unique<ScientificSpinBoxEditor>(); - if (item) { - if (item->hasData(ItemDataRole::LIMITS)) { - auto limits = item->data<RealLimits>(ItemDataRole::LIMITS); - editor->setRange(limits.lowerLimit(), limits.upperLimit()); - } - editor->setSingleStep(getStep(item->data<double>())); - } - editor->setDecimals(GUI::Constants::default_double_decimals); - return editor; - }; - return builder; -} - -builder_t ColorEditorBuilder() -{ - auto builder = [](const SessionItem*) -> editor_t { return std::make_unique<ColorEditor>(); }; - return builder; -} - -builder_t ComboPropertyEditorBuilder() -{ - auto builder = [](const SessionItem*) -> editor_t { - return std::make_unique<ComboPropertyEditor>(); - }; - return builder; -} - -builder_t ExternalPropertyEditorBuilder() -{ - auto builder = [](const SessionItem*) -> editor_t { - return std::make_unique<ExternalPropertyEditor>(); - }; - return builder; -} - -builder_t SelectableComboPropertyEditorBuilder() -{ - auto builder = [](const SessionItem*) -> editor_t { - return std::make_unique<SelectableComboBoxEditor>(); - }; - return builder; -} - -} // namespace ModelView::EditorBuilders diff --git a/mvvm/viewmodel/mvvm/editors/editorbuilders.h b/mvvm/viewmodel/mvvm/editors/editorbuilders.h deleted file mode 100644 index a6a41dcf350..00000000000 --- a/mvvm/viewmodel/mvvm/editors/editorbuilders.h +++ /dev/null @@ -1,66 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/editors/editorbuilders.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_EDITORBUILDERS_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_EDITORBUILDERS_H - -#include "mvvm/viewmodel_export.h" -#include <functional> -#include <memory> - -namespace ModelView { - -class CustomEditor; -class SessionItem; - -//! Collection of methods to build custom editors for trees/tables cells. -//! Used to edit SessionItem data in the context of DefaultEditorFactory. - -namespace EditorBuilders { - -using editor_t = std::unique_ptr<CustomEditor>; -using builder_t = std::function<editor_t(const SessionItem*)>; - -//! Builder for boolean property editor. -MVVM_VIEWMODEL_EXPORT builder_t BoolEditorBuilder(); - -//! Builder for integer property editor. -MVVM_VIEWMODEL_EXPORT builder_t IntegerEditorBuilder(); - -//! Builder for double editor with limits support. -MVVM_VIEWMODEL_EXPORT builder_t DoubleEditorBuilder(); - -//! Builder for double editor with scientific notation based on simple text field. -MVVM_VIEWMODEL_EXPORT builder_t ScientificDoubleEditorBuilder(); - -//! Builder for double editor with scientific notation and spinbox functionality. -MVVM_VIEWMODEL_EXPORT builder_t ScientificSpinBoxEditorBuilder(); - -//! Builder for color property editor. -MVVM_VIEWMODEL_EXPORT builder_t ColorEditorBuilder(); - -//! Builder for ComboProperty editor. -MVVM_VIEWMODEL_EXPORT builder_t ComboPropertyEditorBuilder(); - -//! Builder for external property editor. -MVVM_VIEWMODEL_EXPORT builder_t ExternalPropertyEditorBuilder(); - -//! Builder for ComboProperty editor with multi-selection functionality. -MVVM_VIEWMODEL_EXPORT builder_t SelectableComboPropertyEditorBuilder(); - -} // namespace EditorBuilders - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_EDITORBUILDERS_H diff --git a/mvvm/viewmodel/mvvm/editors/externalpropertycomboeditor.cpp b/mvvm/viewmodel/mvvm/editors/externalpropertycomboeditor.cpp deleted file mode 100644 index c845bba7800..00000000000 --- a/mvvm/viewmodel/mvvm/editors/externalpropertycomboeditor.cpp +++ /dev/null @@ -1,109 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/editors/externalpropertycomboeditor.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/editors/externalpropertycomboeditor.h" -#include "mvvm/model/externalproperty.h" -#include <QColor> -#include <QComboBox> -#include <QStandardItemModel> -#include <QVBoxLayout> - -using namespace ModelView; - -ExternalPropertyComboEditor::ExternalPropertyComboEditor(callback_t callback, QWidget* parent) - : CustomEditor(parent) - , m_getPropertiesCallback(std::move(callback)) - , m_box(new QComboBox) - , m_comboModel(new QStandardItemModel(this)) -{ - setAutoFillBackground(true); - setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - - auto layout = new QVBoxLayout; - layout->setMargin(0); - layout->setSpacing(0); - layout->addWidget(m_box); - setLayout(layout); - - m_box->setModel(m_comboModel); - - setConnected(true); -} - -QSize ExternalPropertyComboEditor::sizeHint() const -{ - return m_box->sizeHint(); -} - -QSize ExternalPropertyComboEditor::minimumSizeHint() const -{ - return m_box->minimumSizeHint(); -} - -void ExternalPropertyComboEditor::onIndexChanged(int index) -{ - auto property = m_data.value<ModelView::ExternalProperty>(); - auto mdata = m_getPropertiesCallback(); - - if (index >= 0 && index < static_cast<int>(mdata.size())) { - if (property != mdata[static_cast<size_t>(index)]) - setDataIntern(QVariant::fromValue(mdata[static_cast<size_t>(index)])); - } -} - -void ExternalPropertyComboEditor::update_components() -{ - setConnected(false); - - m_comboModel->clear(); - - QStandardItem* parentItem = m_comboModel->invisibleRootItem(); - for (auto prop : m_getPropertiesCallback()) { - auto item = new QStandardItem(QString::fromStdString(prop.text())); - parentItem->appendRow(item); - item->setData(prop.color(), Qt::DecorationRole); - } - - m_box->setCurrentIndex(internIndex()); - - setConnected(true); -} - -//! Returns index for QComboBox. - -int ExternalPropertyComboEditor::internIndex() -{ - if (!m_data.canConvert<ModelView::ExternalProperty>()) - return 0; - - auto property = m_data.value<ModelView::ExternalProperty>(); - int result(-1); - for (auto prop : m_getPropertiesCallback()) { - ++result; - if (property.identifier() == prop.identifier()) - return result; - } - - return result; -} - -void ExternalPropertyComboEditor::setConnected(bool isConnected) -{ - if (isConnected) - connect(m_box, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, - &ExternalPropertyComboEditor::onIndexChanged, Qt::UniqueConnection); - else - disconnect(m_box, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), - this, &ExternalPropertyComboEditor::onIndexChanged); -} diff --git a/mvvm/viewmodel/mvvm/editors/externalpropertycomboeditor.h b/mvvm/viewmodel/mvvm/editors/externalpropertycomboeditor.h deleted file mode 100644 index a5f573ace7e..00000000000 --- a/mvvm/viewmodel/mvvm/editors/externalpropertycomboeditor.h +++ /dev/null @@ -1,58 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/editors/externalpropertycomboeditor.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_EXTERNALPROPERTYCOMBOEDITOR_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_EXTERNALPROPERTYCOMBOEDITOR_H - -#include "mvvm/editors/customeditor.h" -#include <functional> -#include <vector> - -class QComboBox; -class QStandardItemModel; - -namespace ModelView { - -class ExternalProperty; - -//! Custom editor for table/tree cells to select ExternalProperty from the list of -//! external properties. Uses callbacks to retrieve vector of possible properties. - -class MVVM_VIEWMODEL_EXPORT ExternalPropertyComboEditor : public CustomEditor { - Q_OBJECT - -public: - using callback_t = std::function<std::vector<ModelView::ExternalProperty>()>; - - ExternalPropertyComboEditor(callback_t callback, QWidget* parent = nullptr); - - QSize sizeHint() const override; - QSize minimumSizeHint() const override; - -protected slots: - virtual void onIndexChanged(int index); - -private: - int internIndex(); - void setConnected(bool isConnected); - void update_components() override; - - callback_t m_getPropertiesCallback; - QComboBox* m_box{nullptr}; - QStandardItemModel* m_comboModel{nullptr}; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_EXTERNALPROPERTYCOMBOEDITOR_H diff --git a/mvvm/viewmodel/mvvm/editors/externalpropertyeditor.cpp b/mvvm/viewmodel/mvvm/editors/externalpropertyeditor.cpp deleted file mode 100644 index 3a6ca4ac4a0..00000000000 --- a/mvvm/viewmodel/mvvm/editors/externalpropertyeditor.cpp +++ /dev/null @@ -1,79 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/editors/externalpropertyeditor.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/editors/externalpropertyeditor.h" -#include "mvvm/editors/customeventfilters.h" -#include "mvvm/editors/styleutils.h" -#include "mvvm/model/customvariants.h" -#include "mvvm/model/externalproperty.h" -#include <QHBoxLayout> -#include <QLabel> -#include <QMessageBox> -#include <QToolButton> -#include <stdexcept> - -using namespace ModelView; - -ExternalPropertyEditor::ExternalPropertyEditor(QWidget* parent) - : CustomEditor(parent) - , m_textLabel(new QLabel) - , m_pixmapLabel(new QLabel) - , m_focusFilter(new LostFocusFilter(this)) - -{ - setMouseTracking(true); - setAutoFillBackground(true); - - auto layout = new QHBoxLayout; - layout->setContentsMargins(4, 0, 0, 0); - - auto button = new QToolButton; - button->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred)); - button->setText(QLatin1String(" . . . ")); - button->setToolTip("Open selector"); - layout->addWidget(m_pixmapLabel); - layout->addWidget(m_textLabel); - layout->addStretch(1); - layout->addWidget(button); - setFocusPolicy(Qt::StrongFocus); - setAttribute(Qt::WA_InputMethodEnabled); - connect(button, &QToolButton::clicked, this, &ExternalPropertyEditor::buttonClicked); - - setLayout(layout); -} - -void ExternalPropertyEditor::setCallback(std::function<void(const QVariant&)> callback) -{ - m_callback = std::move(callback); -} - -void ExternalPropertyEditor::buttonClicked() -{ - if (m_callback) - m_callback(m_data); - else - QMessageBox::warning(nullptr, "Not configured", "No external dialog configured."); -} - -void ExternalPropertyEditor::update_components() -{ - if (!Utils::IsExtPropertyVariant(m_data)) - throw std::runtime_error("Error. Wrong variant type (ExternalProperty is required)."); - - auto prop = m_data.value<ExternalProperty>(); - QPixmap pixmap(Style::DefaultPixmapSize(), Style::DefaultPixmapSize()); - pixmap.fill(prop.color()); - m_textLabel->setText(QString::fromStdString(prop.text())); - m_pixmapLabel->setPixmap(pixmap); -} diff --git a/mvvm/viewmodel/mvvm/editors/externalpropertyeditor.h b/mvvm/viewmodel/mvvm/editors/externalpropertyeditor.h deleted file mode 100644 index a42d21ea61a..00000000000 --- a/mvvm/viewmodel/mvvm/editors/externalpropertyeditor.h +++ /dev/null @@ -1,51 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/editors/externalpropertyeditor.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_EXTERNALPROPERTYEDITOR_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_EXTERNALPROPERTYEDITOR_H - -#include "mvvm/editors/customeditor.h" -#include <functional> - -class QLabel; - -namespace ModelView { - -class LostFocusFilter; - -//! Custom editor for QVariant based on ExternalProperty. -//! Contains icon, label and button to call external dialog via callback mechanism. - -class MVVM_VIEWMODEL_EXPORT ExternalPropertyEditor : public CustomEditor { - Q_OBJECT - -public: - explicit ExternalPropertyEditor(QWidget* parent = nullptr); - - void setCallback(std::function<void(const QVariant&)> callback); - -private slots: - void buttonClicked(); - -private: - void update_components() override; - QLabel* m_textLabel; - QLabel* m_pixmapLabel; - LostFocusFilter* m_focusFilter; - std::function<void(const QVariant&)> m_callback; //! actions to take on clicked button -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_EXTERNALPROPERTYEDITOR_H diff --git a/mvvm/viewmodel/mvvm/editors/integereditor.cpp b/mvvm/viewmodel/mvvm/editors/integereditor.cpp deleted file mode 100644 index 3fa68a4de56..00000000000 --- a/mvvm/viewmodel/mvvm/editors/integereditor.cpp +++ /dev/null @@ -1,67 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/editors/integereditor.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/editors/integereditor.h" -#include <QSpinBox> -#include <QVBoxLayout> -#include <cmath> -#include <stdexcept> - -namespace { -const int max_val = 65536; -const int min_val = -max_val; -} // namespace - -using namespace ModelView; - -IntegerEditor::IntegerEditor(QWidget* parent) : CustomEditor(parent), m_intEditor(new QSpinBox) -{ - setAutoFillBackground(true); - m_intEditor->setFocusPolicy(Qt::StrongFocus); - m_intEditor->setKeyboardTracking(false); - m_intEditor->setRange(min_val, max_val); - - auto layout = new QVBoxLayout; - layout->setMargin(0); - layout->setSpacing(0); - - layout->addWidget(m_intEditor); - - connect(m_intEditor, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), - [=] { this->onEditingFinished(); }); - - setLayout(layout); - - setFocusProxy(m_intEditor); -} - -void IntegerEditor::setRange(int minimum, int maximum) -{ - m_intEditor->setRange(minimum, maximum); -} - -void IntegerEditor::onEditingFinished() -{ - int new_value = m_intEditor->value(); - if (new_value != m_data.value<int>()) - setDataIntern(QVariant::fromValue(new_value)); -} - -void IntegerEditor::update_components() -{ - if (m_data.type() != QVariant::Int) - throw std::runtime_error("IntegerEditor::update_components() -> Error. Wrong variant type"); - - m_intEditor->setValue(m_data.value<int>()); -} diff --git a/mvvm/viewmodel/mvvm/editors/integereditor.h b/mvvm/viewmodel/mvvm/editors/integereditor.h deleted file mode 100644 index 71f51dc2221..00000000000 --- a/mvvm/viewmodel/mvvm/editors/integereditor.h +++ /dev/null @@ -1,44 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/editors/integereditor.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_INTEGEREDITOR_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_INTEGEREDITOR_H - -#include "mvvm/editors/customeditor.h" - -class QSpinBox; - -namespace ModelView { - -//! Custom editor for QVariant based on integer with possibility to set limits. - -class MVVM_VIEWMODEL_EXPORT IntegerEditor : public CustomEditor { - Q_OBJECT - -public: - explicit IntegerEditor(QWidget* parent = nullptr); - - void setRange(int minimum, int maximum); - -private slots: - void onEditingFinished(); - -private: - void update_components() override; - QSpinBox* m_intEditor; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_INTEGEREDITOR_H diff --git a/mvvm/viewmodel/mvvm/editors/scientificdoubleeditor.cpp b/mvvm/viewmodel/mvvm/editors/scientificdoubleeditor.cpp deleted file mode 100644 index 3543e372547..00000000000 --- a/mvvm/viewmodel/mvvm/editors/scientificdoubleeditor.cpp +++ /dev/null @@ -1,70 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/editors/scientificdoubleeditor.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/editors/scientificdoubleeditor.h" -#include "mvvm/utils/numericutils.h" -#include <QDoubleValidator> -#include <QLineEdit> -#include <QVBoxLayout> -#include <stdexcept> - -namespace { -const int max_digits = 1000; -} - -using namespace ModelView; - -ScientificDoubleEditor::ScientificDoubleEditor(QWidget* parent) - : CustomEditor(parent), m_lineEdit(new QLineEdit) - -{ - setAutoFillBackground(true); - - auto layout = new QVBoxLayout; - layout->setMargin(0); - layout->setSpacing(0); - - layout->addWidget(m_lineEdit); - - m_validator = new QDoubleValidator(0.0, std::numeric_limits<double>::max(), max_digits, this); - m_validator->setNotation(QDoubleValidator::ScientificNotation); - m_lineEdit->setValidator(m_validator); - - connect(m_lineEdit, &QLineEdit::editingFinished, this, - &ScientificDoubleEditor::onEditingFinished); - - setLayout(layout); -} - -void ScientificDoubleEditor::setRange(double minimum, double maximum) -{ - m_validator->setRange(minimum, maximum, max_digits); -} - -void ScientificDoubleEditor::onEditingFinished() -{ - double new_value = m_lineEdit->text().toDouble(); - - if (!Utils::AreAlmostEqual(new_value, m_data.value<double>())) - setDataIntern(QVariant::fromValue(new_value)); -} - -void ScientificDoubleEditor::update_components() -{ - if (m_data.type() != QVariant::Double) - throw std::runtime_error( - "ScientificDoubleEditor::update_components() -> Error. Wrong variant type"); - - m_lineEdit->setText(QString::number(m_data.value<double>(), 'g')); -} diff --git a/mvvm/viewmodel/mvvm/editors/scientificdoubleeditor.h b/mvvm/viewmodel/mvvm/editors/scientificdoubleeditor.h deleted file mode 100644 index 0330a009ef6..00000000000 --- a/mvvm/viewmodel/mvvm/editors/scientificdoubleeditor.h +++ /dev/null @@ -1,46 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/editors/scientificdoubleeditor.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_SCIENTIFICDOUBLEEDITOR_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_SCIENTIFICDOUBLEEDITOR_H - -#include "mvvm/editors/customeditor.h" - -class QLineEdit; -class QDoubleValidator; - -namespace ModelView { - -//! Custom editor for QVariant based on double with scientific notation support. - -class MVVM_VIEWMODEL_EXPORT ScientificDoubleEditor : public CustomEditor { - Q_OBJECT - -public: - explicit ScientificDoubleEditor(QWidget* parent = nullptr); - - void setRange(double minimum, double maximum); - -private slots: - void onEditingFinished(); - -private: - void update_components() override; - QLineEdit* m_lineEdit; - QDoubleValidator* m_validator{nullptr}; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_SCIENTIFICDOUBLEEDITOR_H diff --git a/mvvm/viewmodel/mvvm/editors/scientificspinbox.cpp b/mvvm/viewmodel/mvvm/editors/scientificspinbox.cpp deleted file mode 100644 index f5ba485c23f..00000000000 --- a/mvvm/viewmodel/mvvm/editors/scientificspinbox.cpp +++ /dev/null @@ -1,180 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/editors/scientificspinbox.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/editors/scientificspinbox.h" -#include "mvvm/editors/editor_constants.h" -#include <QLineEdit> -#include <cmath> -#include <limits> - -namespace { -const double upper_switch = 1000; -const double lower_switch = 0.1; -const double min_val = std::numeric_limits<double>::min(); -const double max_val = std::numeric_limits<double>::max(); - -bool useExponentialNotation(double val); -} // namespace - -using namespace ModelView; - -ScientificSpinBox::ScientificSpinBox(QWidget* parent) - : QAbstractSpinBox(parent) - , m_value(0.0) - , m_min(-max_val) - , m_max(max_val) - , m_step(1.0) - , m_decimals(GUI::Constants::default_double_decimals) -{ - QLocale locale; - locale.setNumberOptions(QLocale::RejectGroupSeparator); - m_validator.setLocale(locale); - m_validator.setNotation(QDoubleValidator::ScientificNotation); - - connect(this, &QAbstractSpinBox::editingFinished, this, &ScientificSpinBox::updateValue); -} - -ScientificSpinBox::~ScientificSpinBox() = default; - -double ScientificSpinBox::value() const -{ - // return last acceptable input (required for the proper focus-out behaviour) - double val = toDouble(text(), m_validator, m_min, m_max, m_value); - return round(val, m_decimals); -} - -void ScientificSpinBox::setValue(double val) -{ - double old_val = m_value; - m_value = round(val, m_decimals); - updateText(); - if (std::abs(old_val - m_value) > min_val) - emit valueChanged(m_value); -} - -void ScientificSpinBox::updateValue() -{ - double new_val = toDouble(text(), m_validator, m_min, m_max, m_value); - setValue(new_val); -} - -double ScientificSpinBox::singleStep() const -{ - return m_step; -} - -void ScientificSpinBox::setSingleStep(double step) -{ - m_step = step; -} - -double ScientificSpinBox::minimum() const -{ - return m_min; -} - -void ScientificSpinBox::setMinimum(double min) -{ - m_min = min; - if (m_value < m_min) - setValue(m_min); -} - -double ScientificSpinBox::maximum() const -{ - return m_max; -} - -void ScientificSpinBox::setMaximum(double max) -{ - m_max = max; - if (m_value > m_max) - setValue(m_max); -} - -void ScientificSpinBox::setDecimals(int val) -{ - if (val <= 0) - return; - m_decimals = val; - setValue(m_value); -} - -int ScientificSpinBox::decimals() const -{ - return m_decimals; -} - -void ScientificSpinBox::stepBy(int steps) -{ - double new_val = round(m_value + m_step * steps, m_decimals); - if (inRange(new_val)) - setValue(new_val); -} - -QString ScientificSpinBox::toString(double val, int decimal_points) -{ - QString result = useExponentialNotation(val) ? QString::number(val, 'e', decimal_points) - : QString::number(val, 'f', decimal_points); - - return result.replace(QRegExp("(\\.?0+)?((e{1}[\\+|-]{1})(0+)?([1-9]{1}.*))?$"), "\\3\\5"); -} - -double ScientificSpinBox::toDouble(QString text, const QDoubleValidator& validator, double min, - double max, double default_value) -{ - int pos = 0; - if (validator.validate(text, pos) == QValidator::Acceptable) { - double new_val = validator.locale().toDouble(text); - if (std::abs(new_val) < min_val) - new_val = 0.0; - return new_val >= min && new_val <= max ? new_val : default_value; - } - return default_value; -} - -double ScientificSpinBox::round(double val, int decimals) -{ - char notation = useExponentialNotation(val) ? 'e' : 'f'; - return QString::number(val, notation, decimals).toDouble(); -} - -QAbstractSpinBox::StepEnabled ScientificSpinBox::stepEnabled() const -{ - return isReadOnly() ? StepNone : StepUpEnabled | StepDownEnabled; -} - -void ScientificSpinBox::updateText() -{ - QString new_text = toString(m_value, m_decimals); - if (new_text != text()) - lineEdit()->setText(new_text); -} - -bool ScientificSpinBox::inRange(double val) const -{ - return val >= m_min && val <= m_max; -} - -namespace { -bool useExponentialNotation(double val) -{ - const double abs_val = std::abs(val); - - if (abs_val <= min_val) - return false; - - return abs_val >= upper_switch || abs_val < lower_switch; -} -} // namespace diff --git a/mvvm/viewmodel/mvvm/editors/scientificspinbox.h b/mvvm/viewmodel/mvvm/editors/scientificspinbox.h deleted file mode 100644 index 6f99184b188..00000000000 --- a/mvvm/viewmodel/mvvm/editors/scientificspinbox.h +++ /dev/null @@ -1,74 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/editors/scientificspinbox.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_SCIENTIFICSPINBOX_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_SCIENTIFICSPINBOX_H - -#include "mvvm/viewmodel_export.h" -#include <QAbstractSpinBox> - -namespace ModelView { - -class MVVM_VIEWMODEL_EXPORT ScientificSpinBox : public QAbstractSpinBox { - Q_OBJECT - Q_PROPERTY(double value MEMBER m_value READ value WRITE setValue NOTIFY valueChanged USER true) - -public: - ScientificSpinBox(QWidget* parent = nullptr); - ~ScientificSpinBox() override; - - double value() const; - void setValue(double val); - - double singleStep() const; - void setSingleStep(double step); - - double minimum() const; - void setMinimum(double min); - - double maximum() const; - void setMaximum(double max); - - void setDecimals(int); - int decimals() const; - - void stepBy(int steps) override; - QValidator::State validate(QString&, int&) const override { return QValidator::Acceptable; } - void fixup(QString&) const override {} - - static QString toString(double val, int decimal_points); - static double toDouble(QString text, const QDoubleValidator& validator, double min, double max, - double default_value); - static double round(double val, int decimals); - -signals: - void valueChanged(double value); - -protected: - QAbstractSpinBox::StepEnabled stepEnabled() const override; - -private: - void updateValue(); - void updateText(); - bool inRange(double val) const; - - double m_value, m_min, m_max; - double m_step; - int m_decimals; - QDoubleValidator m_validator; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_SCIENTIFICSPINBOX_H diff --git a/mvvm/viewmodel/mvvm/editors/scientificspinboxeditor.cpp b/mvvm/viewmodel/mvvm/editors/scientificspinboxeditor.cpp deleted file mode 100644 index 27b25cedf9e..00000000000 --- a/mvvm/viewmodel/mvvm/editors/scientificspinboxeditor.cpp +++ /dev/null @@ -1,80 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/editors/scientificspinboxeditor.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/editors/scientificspinboxeditor.h" -#include "mvvm/editors/scientificspinbox.h" -#include "mvvm/utils/numericutils.h" -#include <QVBoxLayout> -#include <stdexcept> - -using namespace ModelView; - -ScientificSpinBoxEditor::ScientificSpinBoxEditor(QWidget* parent) - : CustomEditor(parent), m_doubleEditor(new ScientificSpinBox) -{ - setAutoFillBackground(true); - setFocusPolicy(Qt::StrongFocus); - m_doubleEditor->setFocusPolicy(Qt::StrongFocus); - m_doubleEditor->setKeyboardTracking(false); - - auto layout = new QVBoxLayout; - layout->setMargin(0); - layout->setSpacing(0); - - layout->addWidget(m_doubleEditor); - - connect(m_doubleEditor, &ScientificSpinBox::valueChanged, [=] { this->onEditingFinished(); }); - - setLayout(layout); - - setFocusProxy(m_doubleEditor); -} - -void ScientificSpinBoxEditor::setRange(double minimum, double maximum) -{ - m_doubleEditor->setMinimum(minimum); - m_doubleEditor->setMaximum(maximum); -} - -void ScientificSpinBoxEditor::setDecimals(int decimals) -{ - m_doubleEditor->setDecimals(decimals); -} - -void ScientificSpinBoxEditor::setSingleStep(double step) -{ - m_doubleEditor->setSingleStep(step); -} - -bool ScientificSpinBoxEditor::is_persistent() const -{ - return true; -} - -void ScientificSpinBoxEditor::onEditingFinished() -{ - double new_value = m_doubleEditor->value(); - - if (!Utils::AreAlmostEqual(new_value, m_data.value<double>())) - setDataIntern(QVariant::fromValue(new_value)); -} - -void ScientificSpinBoxEditor::update_components() -{ - if (m_data.type() != QVariant::Double) - throw std::runtime_error( - "ScientificSpinBoxEditor::update_components() -> Error. Wrong variant type"); - - m_doubleEditor->setValue(m_data.value<double>()); -} diff --git a/mvvm/viewmodel/mvvm/editors/scientificspinboxeditor.h b/mvvm/viewmodel/mvvm/editors/scientificspinboxeditor.h deleted file mode 100644 index cacf9b4d37a..00000000000 --- a/mvvm/viewmodel/mvvm/editors/scientificspinboxeditor.h +++ /dev/null @@ -1,48 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/editors/scientificspinboxeditor.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_SCIENTIFICSPINBOXEDITOR_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_SCIENTIFICSPINBOXEDITOR_H - -#include "mvvm/editors/customeditor.h" - -namespace ModelView { - -class ScientificSpinBox; - -//! Custom editor for QVariant based on double with scientific notation support. - -class MVVM_VIEWMODEL_EXPORT ScientificSpinBoxEditor : public CustomEditor { - Q_OBJECT - -public: - explicit ScientificSpinBoxEditor(QWidget* parent = nullptr); - - void setRange(double minimum, double maximum); - void setDecimals(int decimals); - void setSingleStep(double step); - - bool is_persistent() const override; - -private slots: - void onEditingFinished(); - -private: - void update_components() override; - ScientificSpinBox* m_doubleEditor; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_SCIENTIFICSPINBOXEDITOR_H diff --git a/mvvm/viewmodel/mvvm/editors/selectablecomboboxeditor.cpp b/mvvm/viewmodel/mvvm/editors/selectablecomboboxeditor.cpp deleted file mode 100644 index 70b9139ade8..00000000000 --- a/mvvm/viewmodel/mvvm/editors/selectablecomboboxeditor.cpp +++ /dev/null @@ -1,211 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/editors/selectablecomboboxeditor.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -// ---------------------------------------------------------------------------- -// https://stackoverflow.com/questions/8422760/combobox-of-checkboxes -// https://stackoverflow.com/questions/21186779/catch-mouse-button-pressed-signal-from-qcombobox-popup-menu -// https://gist.github.com/mistic100/c3b7f3eabc65309687153fe3e0a9a720 -// ---------------------------------------------------------------------------- - -#include "mvvm/editors/selectablecomboboxeditor.h" -#include "mvvm/editors/customeventfilters.h" -#include "mvvm/model/comboproperty.h" -#include "mvvm/utils/containerutils.h" -#include <QAbstractItemView> -#include <QComboBox> -#include <QLineEdit> -#include <QMouseEvent> -#include <QStandardItem> -#include <QStandardItemModel> -#include <QStyledItemDelegate> -#include <QVBoxLayout> - -using namespace ModelView; - -//! Provides custom style delegate for QComboBox to allow checkboxes. - -class QCheckListStyledItemDelegate : public QStyledItemDelegate { -public: - QCheckListStyledItemDelegate(QObject* parent = nullptr) : QStyledItemDelegate(parent) {} - - void paint(QPainter* painter, const QStyleOptionViewItem& option, - const QModelIndex& index) const override - { - auto styleOption = const_cast<QStyleOptionViewItem&>(option); - styleOption.showDecorationSelected = false; - QStyledItemDelegate::paint(painter, styleOption, index); - } -}; - -// ---------------------------------------------------------------------------- - -SelectableComboBoxEditor::SelectableComboBoxEditor(QWidget* parent) - : CustomEditor(parent) - , m_box(new QComboBox) - , m_wheelEventFilter(new WheelEventFilter(this)) - , m_model(new QStandardItemModel(this)) -{ - setAutoFillBackground(true); - setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - - m_box->installEventFilter(m_wheelEventFilter); - m_box->view()->viewport()->installEventFilter(this); - - // Editable mode will be used to have None/Multiple labels on top - m_box->setEditable(true); - m_box->lineEdit()->setReadOnly(true); - m_box->lineEdit()->installEventFilter(this); - connect(m_box->lineEdit(), &QLineEdit::selectionChanged, m_box->lineEdit(), - &QLineEdit::deselect); - - // transforms ordinary combo box into check list - m_box->setItemDelegate(new QCheckListStyledItemDelegate(this)); - m_box->setModel(m_model); - - auto layout = new QVBoxLayout; - layout->setMargin(0); - layout->setSpacing(0); - layout->addWidget(m_box); - setLayout(layout); - setConnected(true); -} - -QSize SelectableComboBoxEditor::sizeHint() const -{ - return m_box->sizeHint(); -} - -QSize SelectableComboBoxEditor::minimumSizeHint() const -{ - return m_box->minimumSizeHint(); -} - -bool SelectableComboBoxEditor::is_persistent() const -{ - return true; -} - -//! Propagate check state from the model to ComboProperty. - -void SelectableComboBoxEditor::onModelDataChanged(const QModelIndex& topLeft, const QModelIndex&, - const QVector<int>& roles) -{ -#if QT_VERSION > QT_VERSION_CHECK(5, 9, 0) - // for older versions this role is always empty - if (!roles.contains(Qt::CheckStateRole)) - return; -#endif - - auto item = m_model->itemFromIndex(topLeft); - if (!item) - return; - - ComboProperty comboProperty = m_data.value<ComboProperty>(); - auto state = item->checkState() == Qt::Checked ? true : false; - comboProperty.setSelected(topLeft.row(), state); - - updateBoxLabel(); - setDataIntern(QVariant::fromValue<ComboProperty>(comboProperty)); -} - -//! Processes press event in QComboBox's underlying list view. - -void SelectableComboBoxEditor::onClickedList(const QModelIndex& index) -{ - if (auto item = m_model->itemFromIndex(index)) { - auto state = item->checkState() == Qt::Checked ? Qt::Unchecked : Qt::Checked; - item->setCheckState(state); - } -} - -//! Handles mouse clicks on QComboBox elements. - -bool SelectableComboBoxEditor::eventFilter(QObject* obj, QEvent* event) -{ - if (isClickToSelect(obj, event)) { - // Handles mouse clicks on QListView when it is expanded from QComboBox - // 1) Prevents list from closing while selecting items. - // 2) Correctly calculates underlying model index when mouse is over check box style - // element. - const auto mouseEvent = static_cast<const QMouseEvent*>(event); - auto index = m_box->view()->indexAt(mouseEvent->pos()); - onClickedList(index); - return true; - - } else if (isClickToExpand(obj, event)) { - // Expands box when clicking on None/Multiple label - m_box->showPopup(); - return true; - - } else { - // Propagate to the parent class. - return QObject::eventFilter(obj, event); - } -} - -void SelectableComboBoxEditor::update_components() -{ - if (!m_data.canConvert<ComboProperty>()) - return; - - ComboProperty property = m_data.value<ComboProperty>(); - - setConnected(false); - m_model->clear(); - - auto labels = property.values(); - auto selectedIndices = property.selectedIndices(); - - for (size_t i = 0; i < labels.size(); ++i) { - auto item = new QStandardItem(QString::fromStdString(labels[i])); - m_model->invisibleRootItem()->appendRow(item); - item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); - item->setCheckable(true); - - auto state = Utils::Contains(selectedIndices, i) ? Qt::Checked : Qt::Unchecked; - item->setData(state, Qt::CheckStateRole); - } - - setConnected(true); - updateBoxLabel(); -} - -void SelectableComboBoxEditor::setConnected(bool isConnected) -{ - if (isConnected) { - connect(m_model, &QStandardItemModel::dataChanged, this, - &SelectableComboBoxEditor::onModelDataChanged); - } else { - disconnect(m_model, &QStandardItemModel::dataChanged, this, - &SelectableComboBoxEditor::onModelDataChanged); - } -} - -//! Update text on QComboBox with the label provided by combo property. - -void SelectableComboBoxEditor::updateBoxLabel() -{ - ComboProperty combo = m_data.value<ComboProperty>(); - m_box->setCurrentText(QString::fromStdString(combo.label())); -} - -bool SelectableComboBoxEditor::isClickToSelect(QObject* obj, QEvent* event) const -{ - return obj == m_box->view()->viewport() && event->type() == QEvent::MouseButtonRelease; -} - -bool SelectableComboBoxEditor::isClickToExpand(QObject* obj, QEvent* event) const -{ - return obj == m_box->lineEdit() && event->type() == QEvent::MouseButtonRelease; -} diff --git a/mvvm/viewmodel/mvvm/editors/selectablecomboboxeditor.h b/mvvm/viewmodel/mvvm/editors/selectablecomboboxeditor.h deleted file mode 100644 index 2c3bc636a00..00000000000 --- a/mvvm/viewmodel/mvvm/editors/selectablecomboboxeditor.h +++ /dev/null @@ -1,63 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/editors/selectablecomboboxeditor.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_SELECTABLECOMBOBOXEDITOR_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_SELECTABLECOMBOBOXEDITOR_H - -#include "mvvm/editors/customeditor.h" - -class QComboBox; -class QStandardItemModel; - -namespace ModelView { - -class WheelEventFilter; - -//! Adds multi-selection capabilities to QComboBox. - -class MVVM_VIEWMODEL_EXPORT SelectableComboBoxEditor : public CustomEditor { - Q_OBJECT - -public: - explicit SelectableComboBoxEditor(QWidget* parent = nullptr); - - QSize sizeHint() const override; - QSize minimumSizeHint() const override; - - bool is_persistent() const override; - -protected slots: - void onModelDataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&); - - void onClickedList(const QModelIndex& index); - -protected: - void update_components() override; - -private: - bool eventFilter(QObject* obj, QEvent* event) override; - void setConnected(bool isConnected); - void updateBoxLabel(); - - bool isClickToSelect(QObject* obj, QEvent* event) const; - bool isClickToExpand(QObject* obj, QEvent* event) const; - - QComboBox* m_box{nullptr}; - WheelEventFilter* m_wheelEventFilter{nullptr}; - QStandardItemModel* m_model{nullptr}; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_SELECTABLECOMBOBOXEDITOR_H diff --git a/mvvm/viewmodel/mvvm/editors/styleutils.cpp b/mvvm/viewmodel/mvvm/editors/styleutils.cpp deleted file mode 100644 index 4aff4cba073..00000000000 --- a/mvvm/viewmodel/mvvm/editors/styleutils.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/editors/styleutils.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/editors/styleutils.h" - -int ModelView::Style::DefaultPixmapSize() -{ - const int default_pixmap_size = 16; - return default_pixmap_size; -} - -int ModelView::Style::DefaultInfoBarHeight() -{ - const int default_info_bar_height = 24; - return default_info_bar_height; -} - -int ModelView::Style::DefaultInfoBarTextSize() -{ - const int default_info_bar_text_saize = 8; - return default_info_bar_text_saize; -} diff --git a/mvvm/viewmodel/mvvm/editors/styleutils.h b/mvvm/viewmodel/mvvm/editors/styleutils.h deleted file mode 100644 index 3c536d46748..00000000000 --- a/mvvm/viewmodel/mvvm/editors/styleutils.h +++ /dev/null @@ -1,33 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/editors/styleutils.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_STYLEUTILS_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_STYLEUTILS_H - -#include "mvvm/viewmodel_export.h" - -namespace ModelView::Style { - -//! Returns int value corresponding to pixmap in standard Qt table/tree decorations. -MVVM_VIEWMODEL_EXPORT int DefaultPixmapSize(); - -//! Returns default height of info bar -MVVM_VIEWMODEL_EXPORT int DefaultInfoBarHeight(); - -//! Returns default size of text on info bar. -MVVM_VIEWMODEL_EXPORT int DefaultInfoBarTextSize(); - -} // namespace ModelView::Style - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_EDITORS_STYLEUTILS_H diff --git a/mvvm/viewmodel/mvvm/factories/CMakeLists.txt b/mvvm/viewmodel/mvvm/factories/CMakeLists.txt deleted file mode 100644 index 71dbc879c93..00000000000 --- a/mvvm/viewmodel/mvvm/factories/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -target_sources(${library_name} PRIVATE - viewmodelcontrollerbuilder.cpp - viewmodelcontrollerbuilder.h - viewmodelcontrollerfactory.h - viewmodelfactory.cpp - viewmodelfactory.h -) diff --git a/mvvm/viewmodel/mvvm/factories/viewmodelcontrollerbuilder.cpp b/mvvm/viewmodel/mvvm/factories/viewmodelcontrollerbuilder.cpp deleted file mode 100644 index 93fd87afcfc..00000000000 --- a/mvvm/viewmodel/mvvm/factories/viewmodelcontrollerbuilder.cpp +++ /dev/null @@ -1,72 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/factories/viewmodelcontrollerbuilder.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/factories/viewmodelcontrollerbuilder.h" -#include "mvvm/interfaces/childrenstrategyinterface.h" -#include "mvvm/interfaces/rowstrategyinterface.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/viewmodel/viewmodelcontroller.h" -#include <stdexcept> - -namespace ModelView { - -ViewModelControllerBuilder::ViewModelControllerBuilder() = default; - -ViewModelControllerBuilder::~ViewModelControllerBuilder() = default; - -ViewModelControllerBuilder::operator std::unique_ptr<ViewModelController>() -{ - if (!context.model) - throw std::runtime_error("Error in ViewModelController: undefined model"); - - if (!context.children_strategy) - throw std::runtime_error("Error in ViewModelController: no children strategy defined."); - - if (!context.row_strategy) - throw std::runtime_error("Error in ViewModelController: no row strategy defined."); - - auto result = std::make_unique<ViewModelController>(context.model, context.view_model); - result->setChildrenStrategy(std::move(context.children_strategy)); - result->setRowStrategy(std::move(context.row_strategy)); - - return result; -} - -ViewModelControllerBuilder::self& ViewModelControllerBuilder::model(SessionModel* model) -{ - context.model = model; - return *this; -} - -ViewModelControllerBuilder::self& ViewModelControllerBuilder::viewModel(ViewModelBase* view_model) -{ - context.view_model = view_model; - return *this; -} - -ViewModelControllerBuilder::self& ViewModelControllerBuilder::childrenStrategy( - std::unique_ptr<ChildrenStrategyInterface> children_strategy) -{ - context.children_strategy = std::move(children_strategy); - return *this; -} - -ViewModelControllerBuilder::self& -ViewModelControllerBuilder::rowStrategy(std::unique_ptr<RowStrategyInterface> row_strategy) -{ - context.row_strategy = std::move(row_strategy); - return *this; -} - -} // namespace ModelView diff --git a/mvvm/viewmodel/mvvm/factories/viewmodelcontrollerbuilder.h b/mvvm/viewmodel/mvvm/factories/viewmodelcontrollerbuilder.h deleted file mode 100644 index 7189a513996..00000000000 --- a/mvvm/viewmodel/mvvm/factories/viewmodelcontrollerbuilder.h +++ /dev/null @@ -1,62 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/factories/viewmodelcontrollerbuilder.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_FACTORIES_VIEWMODELCONTROLLERBUILDER_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_FACTORIES_VIEWMODELCONTROLLERBUILDER_H - -#include "mvvm/viewmodel/viewmodelcontroller.h" -#include "mvvm/viewmodel_export.h" -#include <memory> - -namespace ModelView { - -class SessionModel; -class ViewModelBase; -class ChildrenStrategyInterface; -class RowStrategyInterface; - -//! Builder class for ViewModelController. - -class MVVM_VIEWMODEL_EXPORT ViewModelControllerBuilder { -public: - using self = ViewModelControllerBuilder; - - ViewModelControllerBuilder(); - ~ViewModelControllerBuilder(); - - ViewModelControllerBuilder(const ViewModelControllerBuilder& other) = delete; - ViewModelControllerBuilder& operator=(const ViewModelControllerBuilder& other) = delete; - - self& model(SessionModel* model); - self& viewModel(ViewModelBase* view_model); - self& childrenStrategy(std::unique_ptr<ChildrenStrategyInterface> children_strategy); - self& rowStrategy(std::unique_ptr<RowStrategyInterface> row_strategy); - - operator std::unique_ptr<ViewModelController>(); - -private: - //! Components necessary to build ViewModelController - struct Context { - SessionModel* model{nullptr}; - ViewModelBase* view_model{nullptr}; - std::unique_ptr<ChildrenStrategyInterface> children_strategy; - std::unique_ptr<RowStrategyInterface> row_strategy; - }; - - Context context; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_FACTORIES_VIEWMODELCONTROLLERBUILDER_H diff --git a/mvvm/viewmodel/mvvm/factories/viewmodelcontrollerfactory.h b/mvvm/viewmodel/mvvm/factories/viewmodelcontrollerfactory.h deleted file mode 100644 index debcaf31a8b..00000000000 --- a/mvvm/viewmodel/mvvm/factories/viewmodelcontrollerfactory.h +++ /dev/null @@ -1,47 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/factories/viewmodelcontrollerfactory.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_FACTORIES_VIEWMODELCONTROLLERFACTORY_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_FACTORIES_VIEWMODELCONTROLLERFACTORY_H - -#include "mvvm/factories/viewmodelcontrollerbuilder.h" -#include "mvvm/viewmodel_export.h" -#include <memory> - -namespace ModelView { - -class SessionModel; -class ViewModelBase; -class ViewModelController; - -namespace Factory { - -//! Create universal controller. - -template <typename ChildrenStrategy, typename RowStrategy> -std::unique_ptr<ViewModelController> CreateController(SessionModel* session_model, - ViewModelBase* view_model) -{ - return ViewModelControllerBuilder() - .model(session_model) - .viewModel(view_model) - .childrenStrategy(std::make_unique<ChildrenStrategy>()) - .rowStrategy(std::make_unique<RowStrategy>()); -} - -} // namespace Factory - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_FACTORIES_VIEWMODELCONTROLLERFACTORY_H diff --git a/mvvm/viewmodel/mvvm/factories/viewmodelfactory.cpp b/mvvm/viewmodel/mvvm/factories/viewmodelfactory.cpp deleted file mode 100644 index 8ec0a4a9287..00000000000 --- a/mvvm/viewmodel/mvvm/factories/viewmodelfactory.cpp +++ /dev/null @@ -1,47 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/factories/viewmodelfactory.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/factories/viewmodelfactory.h" -#include "mvvm/viewmodel/defaultviewmodel.h" -#include "mvvm/viewmodel/propertyflatviewmodel.h" -#include "mvvm/viewmodel/propertytableviewmodel.h" -#include "mvvm/viewmodel/propertyviewmodel.h" -#include "mvvm/viewmodel/topitemsviewmodel.h" - -using namespace ModelView; - -std::unique_ptr<ViewModel> Factory::CreateDefaultViewModel(ModelView::SessionModel* model) -{ - return std::make_unique<DefaultViewModel>(model); -} - -std::unique_ptr<ViewModel> Factory::CreatePropertyViewModel(SessionModel* model) -{ - return std::make_unique<PropertyViewModel>(model); -} - -std::unique_ptr<ViewModel> Factory::CreatePropertyTableViewModel(SessionModel* model) -{ - return std::make_unique<PropertyTableViewModel>(model); -} - -std::unique_ptr<ViewModel> Factory::CreateTopItemsViewModel(SessionModel* model) -{ - return std::make_unique<TopItemsViewModel>(model); -} - -std::unique_ptr<ViewModel> Factory::CreatePropertyFlatViewModel(SessionModel* model) -{ - return std::make_unique<PropertyFlatViewModel>(model); -} diff --git a/mvvm/viewmodel/mvvm/factories/viewmodelfactory.h b/mvvm/viewmodel/mvvm/factories/viewmodelfactory.h deleted file mode 100644 index 2b481d63d4a..00000000000 --- a/mvvm/viewmodel/mvvm/factories/viewmodelfactory.h +++ /dev/null @@ -1,74 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/factories/viewmodelfactory.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_FACTORIES_VIEWMODELFACTORY_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_FACTORIES_VIEWMODELFACTORY_H - -#include "mvvm/factories/viewmodelcontrollerfactory.h" -#include "mvvm/viewmodel/viewmodel.h" -#include "mvvm/viewmodel_export.h" -#include <memory> - -namespace ModelView { - -class SessionModel; -class ViewModel; - -namespace Factory { - -//! Creates view model to represent SessionModel for Qt views. -//! The model has two columns, all items are shown. -MVVM_VIEWMODEL_EXPORT std::unique_ptr<ViewModel> CreateDefaultViewModel(SessionModel* model); - -//! Creates view model to represent SessionModel for Qt views. -//! The model has two columns, shows only property items and simplified group items. -MVVM_VIEWMODEL_EXPORT std::unique_ptr<ViewModel> CreatePropertyViewModel(SessionModel* model); - -//! Creates view model to represent SessionModel for Qt views. -//! Shows all properties of CompoundItem in columns of the table, rows of the table represent -//! different CompoundItems. Items of same type and table like structure of the model are expected. -MVVM_VIEWMODEL_EXPORT std::unique_ptr<ViewModel> CreatePropertyTableViewModel(SessionModel* model); - -//! Creates view model to represent SessionModel for Qt views. -//! Shows only top items. -MVVM_VIEWMODEL_EXPORT std::unique_ptr<ViewModel> CreateTopItemsViewModel(SessionModel* model); - -//! Creates view model to represent SessionModel for Qt views. -//! The model has two columns, shows only property items and simplified group items. -//! Subproperties of group item moved one level up. -MVVM_VIEWMODEL_EXPORT std::unique_ptr<ViewModel> CreatePropertyFlatViewModel(SessionModel* model); - -//! Creates view model to represent SessionModel for Qt views. -//! Use user provided types for ChildrenStrategy and RowStrategy. -template <typename ChildrenStrategy, typename RowStrategy> -std::unique_ptr<ViewModel> CreateViewModel(SessionModel* session_model) -{ - auto controller = CreateController<ChildrenStrategy, RowStrategy>(session_model, nullptr); - return std::make_unique<ViewModel>(std::move(controller)); -} - -//! Creates view model to represent SessionModel for Qt views. -//! Use user provided controller type. -template <typename ViewModelController> -std::unique_ptr<ViewModel> CreateViewModel(SessionModel* session_model) -{ - auto controller = std::make_unique<ViewModelController>(session_model); - return std::make_unique<ViewModel>(std::move(controller)); -} - -} // namespace Factory - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_FACTORIES_VIEWMODELFACTORY_H diff --git a/mvvm/viewmodel/mvvm/interfaces/CMakeLists.txt b/mvvm/viewmodel/mvvm/interfaces/CMakeLists.txt deleted file mode 100644 index 6080cc1b5e8..00000000000 --- a/mvvm/viewmodel/mvvm/interfaces/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -target_sources(${library_name} PRIVATE - celldecoratorinterface.h - childrenstrategyinterface.h - editorfactoryinterface.h - rowstrategyinterface.h -) diff --git a/mvvm/viewmodel/mvvm/interfaces/celldecoratorinterface.h b/mvvm/viewmodel/mvvm/interfaces/celldecoratorinterface.h deleted file mode 100644 index 938b18683b9..00000000000 --- a/mvvm/viewmodel/mvvm/interfaces/celldecoratorinterface.h +++ /dev/null @@ -1,38 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/interfaces/celldecoratorinterface.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_INTERFACES_CELLDECORATORINTERFACE_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_INTERFACES_CELLDECORATORINTERFACE_H - -#include "mvvm/viewmodel_export.h" -#include <string> - -class QModelIndex; -class QStyleOptionViewItem; - -namespace ModelView { - -//! Interface class to generate cell decorations (i.e. text) in Qt trees and tables. - -class MVVM_VIEWMODEL_EXPORT CellDecoratorInterface { -public: - virtual ~CellDecoratorInterface() = default; - - virtual bool hasCustomDecoration(const QModelIndex& index) const = 0; - virtual void initStyleOption(QStyleOptionViewItem* option, const QModelIndex& index) = 0; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_INTERFACES_CELLDECORATORINTERFACE_H diff --git a/mvvm/viewmodel/mvvm/interfaces/childrenstrategyinterface.h b/mvvm/viewmodel/mvvm/interfaces/childrenstrategyinterface.h deleted file mode 100644 index 4174b76b972..00000000000 --- a/mvvm/viewmodel/mvvm/interfaces/childrenstrategyinterface.h +++ /dev/null @@ -1,41 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/interfaces/childrenstrategyinterface.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_INTERFACES_CHILDRENSTRATEGYINTERFACE_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_INTERFACES_CHILDRENSTRATEGYINTERFACE_H - -#include "mvvm/viewmodel_export.h" -#include <vector> - -namespace ModelView { - -class SessionItem; - -//! Base class for strategies to find children, actual of fictional, of given item. - -//! Reported vector of children might be different from actual children of given item. -//! The strategy is used in context of AbstractViewModel while exposing SessionModel to Qt. -//! Thanks to this strategy ViewModel decides which items to visit. - -class MVVM_VIEWMODEL_EXPORT ChildrenStrategyInterface { -public: - virtual ~ChildrenStrategyInterface() = default; - - //! Returns vector of children of given item. - virtual std::vector<SessionItem*> children(const SessionItem* item) const = 0; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_INTERFACES_CHILDRENSTRATEGYINTERFACE_H diff --git a/mvvm/viewmodel/mvvm/interfaces/editorfactoryinterface.h b/mvvm/viewmodel/mvvm/interfaces/editorfactoryinterface.h deleted file mode 100644 index 6e474dd5054..00000000000 --- a/mvvm/viewmodel/mvvm/interfaces/editorfactoryinterface.h +++ /dev/null @@ -1,41 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/interfaces/editorfactoryinterface.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_INTERFACES_EDITORFACTORYINTERFACE_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_INTERFACES_EDITORFACTORYINTERFACE_H - -#include "mvvm/viewmodel_export.h" -#include <memory> -#include <string> - -class QModelIndex; -class QWidget; - -namespace ModelView { - -class CustomEditor; - -//! Interface for custom editor factory. -//! Intended for editor construction in cells of tables and trees in the context of delegate. - -class MVVM_VIEWMODEL_EXPORT EditorFactoryInterface { -public: - virtual ~EditorFactoryInterface() = default; - - virtual std::unique_ptr<CustomEditor> createEditor(const QModelIndex& index) const = 0; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_INTERFACES_EDITORFACTORYINTERFACE_H diff --git a/mvvm/viewmodel/mvvm/interfaces/rowstrategyinterface.h b/mvvm/viewmodel/mvvm/interfaces/rowstrategyinterface.h deleted file mode 100644 index f919423c4f1..00000000000 --- a/mvvm/viewmodel/mvvm/interfaces/rowstrategyinterface.h +++ /dev/null @@ -1,42 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/interfaces/rowstrategyinterface.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_INTERFACES_ROWSTRATEGYINTERFACE_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_INTERFACES_ROWSTRATEGYINTERFACE_H - -#include "mvvm/viewmodel_export.h" -#include <QStringList> -#include <memory> -#include <vector> - -namespace ModelView { - -class SessionItem; -class ViewItem; - -//! Base class to construct row of ViewItem's from given SessionItem. -//! Used in context of AbstractViewModel while exposing SessionModel to Qt. - -class MVVM_VIEWMODEL_EXPORT RowStrategyInterface { -public: - virtual ~RowStrategyInterface() = default; - - virtual QStringList horizontalHeaderLabels() const = 0; - - virtual std::vector<std::unique_ptr<ViewItem>> constructRow(SessionItem*) = 0; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_INTERFACES_ROWSTRATEGYINTERFACE_H diff --git a/mvvm/viewmodel/mvvm/viewmodel/CMakeLists.txt b/mvvm/viewmodel/mvvm/viewmodel/CMakeLists.txt deleted file mode 100644 index 0d93f441bc6..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/CMakeLists.txt +++ /dev/null @@ -1,36 +0,0 @@ -target_sources(${library_name} PRIVATE - defaultcelldecorator.cpp - defaultcelldecorator.h - defaultviewmodel.cpp - defaultviewmodel.h - labeldatarowstrategy.cpp - labeldatarowstrategy.h - propertiesrowstrategy.cpp - propertiesrowstrategy.h - propertyflatviewmodel.cpp - propertyflatviewmodel.h - propertytableviewmodel.cpp - propertytableviewmodel.h - propertyviewmodel.cpp - propertyviewmodel.h - standardchildrenstrategies.cpp - standardchildrenstrategies.h - standardviewitems.cpp - standardviewitems.h - standardviewmodelcontrollers.cpp - standardviewmodelcontrollers.h - topitemsviewmodel.cpp - topitemsviewmodel.h - viewitem.cpp - viewitem.h - viewmodel.cpp - viewmodel.h - viewmodelbase.cpp - viewmodelbase.h - viewmodelcontroller.cpp - viewmodelcontroller.h - viewmodeldelegate.cpp - viewmodeldelegate.h - viewmodelutils.cpp - viewmodelutils.h -) diff --git a/mvvm/viewmodel/mvvm/viewmodel/defaultcelldecorator.cpp b/mvvm/viewmodel/mvvm/viewmodel/defaultcelldecorator.cpp deleted file mode 100644 index e6f5d327357..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/defaultcelldecorator.cpp +++ /dev/null @@ -1,66 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/viewmodel/defaultcelldecorator.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/viewmodel/defaultcelldecorator.h" -#include "mvvm/editors/editor_constants.h" -#include "mvvm/editors/scientificspinbox.h" -#include "mvvm/model/comboproperty.h" -#include "mvvm/model/customvariants.h" -#include "mvvm/model/externalproperty.h" -#include <QModelIndex> -#include <QStyleOptionViewItem> - -using namespace ModelView; - -bool DefaultCellDecorator::hasCustomDecoration(const QModelIndex& index) const -{ - return cellText(index).has_value(); -} - -std::optional<std::string> DefaultCellDecorator::cellText(const QModelIndex& index) const -{ - auto variant = index.data(); - - if (Utils::IsComboVariant(variant)) - return std::optional<std::string>{variant.value<ComboProperty>().label()}; - - else if (Utils::IsBoolVariant(variant)) - return variant.value<bool>() ? std::optional<std::string>{"True"} - : std::optional<std::string>{"False"}; - - else if (Utils::IsExtPropertyVariant(variant)) - return std::optional<std::string>{variant.value<ExternalProperty>().text()}; - - else if (Utils::IsColorVariant(variant)) - return std::optional<std::string>{""}; - - else if (Utils::IsDoubleVariant(variant)) - return std::optional<std::string>{ - ScientificSpinBox::toString(index.data(Qt::EditRole).value<double>(), - GUI::Constants::default_double_decimals) - .toStdString()}; - - return {}; -} - -void DefaultCellDecorator::initStyleOption(QStyleOptionViewItem* option, const QModelIndex& index) -{ - if (!hasCustomDecoration(index)) - return; - - auto value = cellText(index).value(); - option->text = QString::fromStdString(value); - if (value.empty()) - option->features &= ~QStyleOptionViewItem::HasDisplay; -} diff --git a/mvvm/viewmodel/mvvm/viewmodel/defaultcelldecorator.h b/mvvm/viewmodel/mvvm/viewmodel/defaultcelldecorator.h deleted file mode 100644 index d6ba584cdce..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/defaultcelldecorator.h +++ /dev/null @@ -1,36 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/viewmodel/defaultcelldecorator.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_DEFAULTCELLDECORATOR_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_DEFAULTCELLDECORATOR_H - -#include "mvvm/interfaces/celldecoratorinterface.h" -#include <optional> - -namespace ModelView { - -//! Generates default cell decorations for Qt trees and tables. - -class MVVM_VIEWMODEL_EXPORT DefaultCellDecorator : public CellDecoratorInterface { -public: - bool hasCustomDecoration(const QModelIndex& index) const override; - void initStyleOption(QStyleOptionViewItem* option, const QModelIndex& index) override; - -protected: - virtual std::optional<std::string> cellText(const QModelIndex& index) const; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_DEFAULTCELLDECORATOR_H diff --git a/mvvm/viewmodel/mvvm/viewmodel/defaultviewmodel.cpp b/mvvm/viewmodel/mvvm/viewmodel/defaultviewmodel.cpp deleted file mode 100644 index b0e082d80b6..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/defaultviewmodel.cpp +++ /dev/null @@ -1,23 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/viewmodel/defaultviewmodel.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/viewmodel/defaultviewmodel.h" -#include "mvvm/viewmodel/standardviewmodelcontrollers.h" - -using namespace ModelView; - -DefaultViewModel::DefaultViewModel(SessionModel* model, QObject* parent) - : ViewModel(std::make_unique<DefaultViewModelController>(model, this), parent) -{ -} diff --git a/mvvm/viewmodel/mvvm/viewmodel/defaultviewmodel.h b/mvvm/viewmodel/mvvm/viewmodel/defaultviewmodel.h deleted file mode 100644 index d96b977dc70..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/defaultviewmodel.h +++ /dev/null @@ -1,35 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/viewmodel/defaultviewmodel.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_DEFAULTVIEWMODEL_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_DEFAULTVIEWMODEL_H - -#include "mvvm/viewmodel/viewmodel.h" - -namespace ModelView { - -//! View model to show content of SessionModel in Qt widgets: two column tree with label/data. - -//! Provides two column tree with label/data, with one-to-one child/parent -//! correspondence as in the original SessionModel. - -class MVVM_VIEWMODEL_EXPORT DefaultViewModel : public ViewModel { - Q_OBJECT -public: - DefaultViewModel(SessionModel* model, QObject* parent = nullptr); -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_DEFAULTVIEWMODEL_H diff --git a/mvvm/viewmodel/mvvm/viewmodel/labeldatarowstrategy.cpp b/mvvm/viewmodel/mvvm/viewmodel/labeldatarowstrategy.cpp deleted file mode 100644 index b7db7e4521c..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/labeldatarowstrategy.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/viewmodel/labeldatarowstrategy.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/viewmodel/labeldatarowstrategy.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/viewmodel/standardviewitems.h" - -using namespace ModelView; - -//! Example: -//! For LayerItem two items will be generated: ViewLabelItem and ViewEmptyItem, both uneditable. -//! For LayerItem's thickness property, two items will be generated: ViewLabelItem and ViewDataItem. - -QStringList LabelDataRowStrategy::horizontalHeaderLabels() const -{ - return QStringList() << "Name" - << "Value"; -} - -std::vector<std::unique_ptr<ViewItem>> LabelDataRowStrategy::constructRow(SessionItem* item) -{ - std::vector<std::unique_ptr<ViewItem>> result; - - if (!item) - return result; - - result.emplace_back(std::make_unique<ViewLabelItem>(item)); - result.emplace_back(std::make_unique<ViewDataItem>(item)); - return result; -} diff --git a/mvvm/viewmodel/mvvm/viewmodel/labeldatarowstrategy.h b/mvvm/viewmodel/mvvm/viewmodel/labeldatarowstrategy.h deleted file mode 100644 index a04f2493d28..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/labeldatarowstrategy.h +++ /dev/null @@ -1,39 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/viewmodel/labeldatarowstrategy.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_LABELDATAROWSTRATEGY_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_LABELDATAROWSTRATEGY_H - -#include "mvvm/interfaces/rowstrategyinterface.h" - -class QStandardItem; - -namespace ModelView { - -class SessionItem; - -//! Constructs row of QStandardItem's for given SessionItem. -//! Row consists of two columns, ViewLabelItem for SessionItem's display role and -//! ViewDataItem for Session's item data role. - -class MVVM_VIEWMODEL_EXPORT LabelDataRowStrategy : public RowStrategyInterface { -public: - QStringList horizontalHeaderLabels() const override; - - std::vector<std::unique_ptr<ViewItem>> constructRow(SessionItem*) override; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_LABELDATAROWSTRATEGY_H diff --git a/mvvm/viewmodel/mvvm/viewmodel/propertiesrowstrategy.cpp b/mvvm/viewmodel/mvvm/viewmodel/propertiesrowstrategy.cpp deleted file mode 100644 index 42908cffd51..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/propertiesrowstrategy.cpp +++ /dev/null @@ -1,65 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/viewmodel/propertiesrowstrategy.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/viewmodel/propertiesrowstrategy.h" -#include "mvvm/model/itemutils.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/viewmodel/standardviewitems.h" - -using namespace ModelView; - -PropertiesRowStrategy::PropertiesRowStrategy(std::vector<std::string> labels) - : user_defined_column_labels(std::move(labels)) -{ -} - -QStringList PropertiesRowStrategy::horizontalHeaderLabels() const -{ - QStringList result; - auto labels = - user_defined_column_labels.empty() ? current_column_labels : user_defined_column_labels; - std::transform(labels.begin(), labels.end(), std::back_inserter(result), - [](const std::string& str) { return QString::fromStdString(str); }); - return result; -} - -std::vector<std::unique_ptr<ViewItem>> PropertiesRowStrategy::constructRow(SessionItem* item) -{ - std::vector<std::unique_ptr<ViewItem>> result; - - if (!item) - return result; - - auto items_in_row = Utils::SinglePropertyItems(*item); - if (user_defined_column_labels.empty()) - update_column_labels(items_in_row); - - for (auto child : items_in_row) { - if (child->hasData()) - result.emplace_back(std::make_unique<ViewDataItem>(child)); - else - result.emplace_back(std::make_unique<ViewLabelItem>(child)); - } - - return result; -} - -//! Updates current column labels. - -void PropertiesRowStrategy::update_column_labels(std::vector<SessionItem*> items) -{ - current_column_labels.clear(); - std::transform(items.begin(), items.end(), std::back_inserter(current_column_labels), - [](const SessionItem* item) { return item->displayName(); }); -} diff --git a/mvvm/viewmodel/mvvm/viewmodel/propertiesrowstrategy.h b/mvvm/viewmodel/mvvm/viewmodel/propertiesrowstrategy.h deleted file mode 100644 index 047ca906e36..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/propertiesrowstrategy.h +++ /dev/null @@ -1,45 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/viewmodel/propertiesrowstrategy.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_PROPERTIESROWSTRATEGY_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_PROPERTIESROWSTRATEGY_H - -#include "mvvm/interfaces/rowstrategyinterface.h" - -class QStandardItem; - -namespace ModelView { - -class SessionItem; - -//! Constructs row of QStandardItem's for given SessionItem. -//! Row consists of columns with all PropertyItem's of given SessionItem. - -class MVVM_VIEWMODEL_EXPORT PropertiesRowStrategy : public RowStrategyInterface { -public: - PropertiesRowStrategy(std::vector<std::string> labels = {}); - - QStringList horizontalHeaderLabels() const override; - - std::vector<std::unique_ptr<ViewItem>> constructRow(SessionItem* item) override; - -private: - void update_column_labels(std::vector<ModelView::SessionItem*> items); - std::vector<std::string> current_column_labels; - std::vector<std::string> user_defined_column_labels; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_PROPERTIESROWSTRATEGY_H diff --git a/mvvm/viewmodel/mvvm/viewmodel/propertyflatviewmodel.cpp b/mvvm/viewmodel/mvvm/viewmodel/propertyflatviewmodel.cpp deleted file mode 100644 index 4054f47ab31..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/propertyflatviewmodel.cpp +++ /dev/null @@ -1,23 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/viewmodel/propertyflatviewmodel.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/viewmodel/propertyflatviewmodel.h" -#include "mvvm/viewmodel/standardviewmodelcontrollers.h" - -using namespace ModelView; - -PropertyFlatViewModel::PropertyFlatViewModel(SessionModel* model, QObject* parent) - : ViewModel(std::make_unique<PropertyFlatViewModelController>(model, this), parent) -{ -} diff --git a/mvvm/viewmodel/mvvm/viewmodel/propertyflatviewmodel.h b/mvvm/viewmodel/mvvm/viewmodel/propertyflatviewmodel.h deleted file mode 100644 index 1bcd27827d6..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/propertyflatviewmodel.h +++ /dev/null @@ -1,33 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/viewmodel/propertyflatviewmodel.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_PROPERTYFLATVIEWMODEL_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_PROPERTYFLATVIEWMODEL_H - -#include "mvvm/viewmodel/viewmodel.h" - -namespace ModelView { - -//! View model to show content of SessionModel in Qt widgets. -//! Only property items are shown, also hides inactive items of GroupProperty. - -class MVVM_VIEWMODEL_EXPORT PropertyFlatViewModel : public ViewModel { - Q_OBJECT -public: - PropertyFlatViewModel(SessionModel* model, QObject* parent = nullptr); -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_PROPERTYFLATVIEWMODEL_H diff --git a/mvvm/viewmodel/mvvm/viewmodel/propertytableviewmodel.cpp b/mvvm/viewmodel/mvvm/viewmodel/propertytableviewmodel.cpp deleted file mode 100644 index dedbb8a0ee5..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/propertytableviewmodel.cpp +++ /dev/null @@ -1,23 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/viewmodel/propertytableviewmodel.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/viewmodel/propertytableviewmodel.h" -#include "mvvm/viewmodel/standardviewmodelcontrollers.h" - -using namespace ModelView; - -PropertyTableViewModel::PropertyTableViewModel(SessionModel* model, QObject* parent) - : ViewModel(std::make_unique<PropertyTableViewModelController>(model, this), parent) -{ -} diff --git a/mvvm/viewmodel/mvvm/viewmodel/propertytableviewmodel.h b/mvvm/viewmodel/mvvm/viewmodel/propertytableviewmodel.h deleted file mode 100644 index 8bcd5af6a18..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/propertytableviewmodel.h +++ /dev/null @@ -1,35 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/viewmodel/propertytableviewmodel.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_PROPERTYTABLEVIEWMODEL_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_PROPERTYTABLEVIEWMODEL_H - -#include "mvvm/viewmodel/viewmodel.h" - -namespace ModelView { - -//! View model to show content of SessionModel in Qt widgets: all item properties as a table row. - -//! Intended to show registered properties of items in table-like view. -//! Registered properties will form columns of the table, top level items will form table rows. - -class MVVM_VIEWMODEL_EXPORT PropertyTableViewModel : public ViewModel { - Q_OBJECT -public: - PropertyTableViewModel(SessionModel* model, QObject* parent = nullptr); -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_PROPERTYTABLEVIEWMODEL_H diff --git a/mvvm/viewmodel/mvvm/viewmodel/propertyviewmodel.cpp b/mvvm/viewmodel/mvvm/viewmodel/propertyviewmodel.cpp deleted file mode 100644 index 5125ac678b5..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/propertyviewmodel.cpp +++ /dev/null @@ -1,23 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/viewmodel/propertyviewmodel.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/viewmodel/propertyviewmodel.h" -#include "mvvm/viewmodel/standardviewmodelcontrollers.h" - -using namespace ModelView; - -PropertyViewModel::PropertyViewModel(SessionModel* model, QObject* parent) - : ViewModel(std::make_unique<PropertyViewModelController>(model, this), parent) -{ -} diff --git a/mvvm/viewmodel/mvvm/viewmodel/propertyviewmodel.h b/mvvm/viewmodel/mvvm/viewmodel/propertyviewmodel.h deleted file mode 100644 index 8282f3886c5..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/propertyviewmodel.h +++ /dev/null @@ -1,33 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/viewmodel/propertyviewmodel.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_PROPERTYVIEWMODEL_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_PROPERTYVIEWMODEL_H - -#include "mvvm/viewmodel/viewmodel.h" - -namespace ModelView { - -//! View model to show content of SessionModel in Qt widgets. -//! Only property items are shown, also hides inactive items of GroupProperty. - -class MVVM_VIEWMODEL_EXPORT PropertyViewModel : public ViewModel { - Q_OBJECT -public: - PropertyViewModel(SessionModel* model, QObject* parent = nullptr); -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_PROPERTYVIEWMODEL_H diff --git a/mvvm/viewmodel/mvvm/viewmodel/standardchildrenstrategies.cpp b/mvvm/viewmodel/mvvm/viewmodel/standardchildrenstrategies.cpp deleted file mode 100644 index eadbbf17ded..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/standardchildrenstrategies.cpp +++ /dev/null @@ -1,103 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/viewmodel/standardchildrenstrategies.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/viewmodel/standardchildrenstrategies.h" -#include "mvvm/model/groupitem.h" -#include "mvvm/model/itemutils.h" - -using namespace ModelView; - -// ---------------------------------------------------------------------------- - -std::vector<SessionItem*> AllChildrenStrategy::children(const SessionItem* item) const -{ - return item ? item->children() : std::vector<SessionItem*>(); -} - -std::vector<SessionItem*> TopItemsStrategy::children(const SessionItem* item) const -{ - return item ? Utils::TopLevelItems(*item) : std::vector<SessionItem*>(); -} - -// ---------------------------------------------------------------------------- - -/* -PropertyItemsStrategy example: if group property has Cylinder active: - -Particle - ShapeGroup - Sphere - Radius - Cylinder - Height - Radius - -will become: -Particle - ShapeGroup -> Cylinder - Height - Radius -*/ - -std::vector<SessionItem*> PropertyItemsStrategy::children(const SessionItem* item) const -{ - if (!item) - return std::vector<SessionItem*>(); - - auto group = dynamic_cast<const GroupItem*>(item); - auto next_item = group ? group->currentItem() : item; - return Utils::SinglePropertyItems(*next_item); -} - -// ---------------------------------------------------------------------------- - -/* -PropertyItemsFlatStrategy example: if group property has Cylinder active: - -Particle - ShapeGroup - Sphere - Radius - Cylinder - Height - Radius - -will become: -Particle - ShapeGroup -> Cylinder - Height - Radius -*/ - -std::vector<SessionItem*> PropertyItemsFlatStrategy::children(const SessionItem* item) const -{ - if (!item) - return std::vector<SessionItem*>(); - - if (auto group = dynamic_cast<const GroupItem*>(item); group) - return Utils::SinglePropertyItems(*group->currentItem()); - - std::vector<SessionItem*> result; - for (auto child : Utils::SinglePropertyItems(*item)) { - if (auto group_item = dynamic_cast<GroupItem*>(child); group_item) { - result.push_back(group_item); - for (auto sub_property : Utils::SinglePropertyItems(*group_item->currentItem())) - result.push_back(sub_property); - } else { - result.push_back(child); - } - } - - return result; -} diff --git a/mvvm/viewmodel/mvvm/viewmodel/standardchildrenstrategies.h b/mvvm/viewmodel/mvvm/viewmodel/standardchildrenstrategies.h deleted file mode 100644 index 74cdeb7e4d0..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/standardchildrenstrategies.h +++ /dev/null @@ -1,63 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/viewmodel/standardchildrenstrategies.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_STANDARDCHILDRENSTRATEGIES_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_STANDARDCHILDRENSTRATEGIES_H - -//! @file mvvm/viewmodel/mvvm/viewmodel/standardchildrenstrategies.h -//! @brief Collection of strategies to find children, actual of fictional, of given SessionItem. -//! Used for ViewModel generation when underlying SessionModel changes its layout. - -#include "mvvm/interfaces/childrenstrategyinterface.h" - -namespace ModelView { - -class SessionItem; - -//! Strategy to find children of given item: gives all actual children back. - -class MVVM_VIEWMODEL_EXPORT AllChildrenStrategy : public ChildrenStrategyInterface { -public: - std::vector<SessionItem*> children(const SessionItem* item) const override; -}; - -//! Strategy to find children of given item: only top level items will be given, all -//! property items will be filtered out. - -class MVVM_VIEWMODEL_EXPORT TopItemsStrategy : public ChildrenStrategyInterface { -public: - std::vector<SessionItem*> children(const SessionItem* item) const override; -}; - -//! Strategy to find children of given item: only property item will be given, all top level items -//! will be filtered out, all inactive children of GroupItem will be filtered out. See example -//! in code. - -class MVVM_VIEWMODEL_EXPORT PropertyItemsStrategy : public ChildrenStrategyInterface { -public: - std::vector<SessionItem*> children(const SessionItem* item) const override; -}; - -//! Strategy to find children of given item: flat alignment. -//! Acts as PropertyItemStrategy, with the difference that active subproperties of -//! GroupItem are moved to the same parent, as GroupItem itself. See example in code. - -class MVVM_VIEWMODEL_EXPORT PropertyItemsFlatStrategy : public ChildrenStrategyInterface { -public: - std::vector<SessionItem*> children(const SessionItem* item) const override; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_STANDARDCHILDRENSTRATEGIES_H diff --git a/mvvm/viewmodel/mvvm/viewmodel/standardviewitems.cpp b/mvvm/viewmodel/mvvm/viewmodel/standardviewitems.cpp deleted file mode 100644 index 9d022a7f622..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/standardviewitems.cpp +++ /dev/null @@ -1,67 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/viewmodel/standardviewitems.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/viewmodel/standardviewitems.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/viewmodel/viewmodelutils.h" - -using namespace ModelView; - -RootViewItem::RootViewItem(SessionItem* item) : ViewItem(item, ItemDataRole::DATA) {} - -//! --------------------------------------------------------------------------- - -ViewLabelItem::ViewLabelItem(SessionItem* item) : ViewItem(item, ItemDataRole::DISPLAY) {} - -QVariant ViewLabelItem::data(int role) const -{ - if (!item()) - return QVariant(); - - // use item's display role - if (role == Qt::DisplayRole || role == Qt::EditRole) - return QString::fromStdString(item()->displayName()); - - return ViewItem::data(role); -} - -//! --------------------------------------------------------------------------- - -ViewDataItem::ViewDataItem(SessionItem* item) : ViewItem(item, ItemDataRole::DATA) {} - -Qt::ItemFlags ViewDataItem::flags() const -{ - Qt::ItemFlags result = ViewItem::flags(); - if (item() && item()->isEditable() && item()->isEnabled() && item()->data<QVariant>().isValid()) - result |= Qt::ItemIsEditable; - - return result; -} - -QVariant ViewDataItem::data(int role) const -{ - if (role == Qt::DecorationRole) - return Utils::DecorationRole(*item()); - else if (role == Qt::CheckStateRole) - return Utils::CheckStateRole(*item()); - - return ViewItem::data(role); -} - -ViewEmptyItem::ViewEmptyItem() : ViewItem(nullptr, 0) {} - -QVariant ViewEmptyItem::data(int) const -{ - return QVariant(); -} diff --git a/mvvm/viewmodel/mvvm/viewmodel/standardviewitems.h b/mvvm/viewmodel/mvvm/viewmodel/standardviewitems.h deleted file mode 100644 index 79a4688ca32..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/standardviewitems.h +++ /dev/null @@ -1,61 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/viewmodel/standardviewitems.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_STANDARDVIEWITEMS_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_STANDARDVIEWITEMS_H - -#include "mvvm/viewmodel/viewitem.h" - -namespace ModelView { - -class SessionItem; - -//! Represents root item. - -class MVVM_VIEWMODEL_EXPORT RootViewItem : public ViewItem { -public: - explicit RootViewItem(SessionItem* item); -}; - -//! Represents empty cell of tree or table. - -class MVVM_VIEWMODEL_EXPORT ViewEmptyItem : public ViewItem { -public: - ViewEmptyItem(); - QVariant data(int role) const override; -}; - -//! Represents display name of SessionItem in any cell of Qt's trees and tables. - -class MVVM_VIEWMODEL_EXPORT ViewLabelItem : public ViewItem { -public: - explicit ViewLabelItem(SessionItem* item); - - QVariant data(int role) const override; -}; - -//! Represents data role of SessionItem in any cell of Qt's trees and tables. - -class MVVM_VIEWMODEL_EXPORT ViewDataItem : public ViewItem { -public: - explicit ViewDataItem(SessionItem* item); - - Qt::ItemFlags flags() const override; - - QVariant data(int role) const override; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_STANDARDVIEWITEMS_H diff --git a/mvvm/viewmodel/mvvm/viewmodel/standardviewmodelcontrollers.cpp b/mvvm/viewmodel/mvvm/viewmodel/standardviewmodelcontrollers.cpp deleted file mode 100644 index a6a8d57cb7e..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/standardviewmodelcontrollers.cpp +++ /dev/null @@ -1,92 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/viewmodel/standardviewmodelcontrollers.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/viewmodel/standardviewmodelcontrollers.h" -#include "mvvm/model/groupitem.h" -#include "mvvm/viewmodel/labeldatarowstrategy.h" -#include "mvvm/viewmodel/propertiesrowstrategy.h" -#include "mvvm/viewmodel/standardchildrenstrategies.h" -#include "mvvm/viewmodel/standardviewitems.h" - -using namespace ModelView; - -// ---------------------------------------------------------------------------- - -DefaultViewModelController::DefaultViewModelController(SessionModel* session_model, - ViewModelBase* view_model) - : ViewModelController(session_model, view_model) -{ - setChildrenStrategy(std::make_unique<AllChildrenStrategy>()); - setRowStrategy(std::make_unique<LabelDataRowStrategy>()); -} - -// ---------------------------------------------------------------------------- - -TopItemsViewModelController::TopItemsViewModelController(SessionModel* session_model, - ViewModelBase* view_model) - : ViewModelController(session_model, view_model) -{ - setChildrenStrategy(std::make_unique<TopItemsStrategy>()); - setRowStrategy(std::make_unique<LabelDataRowStrategy>()); -} - -// ---------------------------------------------------------------------------- - -PropertyViewModelController::PropertyViewModelController(SessionModel* session_model, - ViewModelBase* view_model) - : ViewModelController(session_model, view_model) -{ - setChildrenStrategy(std::make_unique<PropertyItemsStrategy>()); - setRowStrategy(std::make_unique<LabelDataRowStrategy>()); -} - -void PropertyViewModelController::onDataChange(SessionItem* item, int role) -{ - ViewModelController::onDataChange(item, role); - // If data change occured with GroupItem, performs cleanup and regeneration of - // ViewItems, corresponding to groupItem's current index. - if (auto group = dynamic_cast<GroupItem*>(item)) - update_branch(group); -} - -// ---------------------------------------------------------------------------- - -// FIXME What to do with group property? - -PropertyTableViewModelController::PropertyTableViewModelController( - SessionModel* session_model, ViewModelBase* view_model, const std::vector<std::string>& labels) - : ViewModelController(session_model, view_model) -{ - setChildrenStrategy(std::make_unique<TopItemsStrategy>()); - setRowStrategy(std::make_unique<PropertiesRowStrategy>(labels)); -} - -// ---------------------------------------------------------------------------- - -PropertyFlatViewModelController::PropertyFlatViewModelController(SessionModel* session_model, - ViewModelBase* view_model) - : ViewModelController(session_model, view_model) -{ - setChildrenStrategy(std::make_unique<PropertyItemsFlatStrategy>()); - setRowStrategy(std::make_unique<LabelDataRowStrategy>()); -} - -void PropertyFlatViewModelController::onDataChange(SessionItem* item, int role) -{ - ViewModelController::onDataChange(item, role); - // If data change occured with GroupItem, performs cleanup and regeneration of - // ViewItems, corresponding to groupItem's current index. - if (auto group = dynamic_cast<GroupItem*>(item)) - update_branch(group->parent()); -} diff --git a/mvvm/viewmodel/mvvm/viewmodel/standardviewmodelcontrollers.h b/mvvm/viewmodel/mvvm/viewmodel/standardviewmodelcontrollers.h deleted file mode 100644 index eb3ed4b5dea..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/standardviewmodelcontrollers.h +++ /dev/null @@ -1,79 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/viewmodel/standardviewmodelcontrollers.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_STANDARDVIEWMODELCONTROLLERS_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_STANDARDVIEWMODELCONTROLLERS_H - -#include "mvvm/viewmodel/viewmodel.h" -#include "mvvm/viewmodel/viewmodelcontroller.h" -#include <string> -#include <vector> - -//! @file mvvm/viewmodel/mvvm/viewmodel/standardviewmodelcontrollers.h -//! Collection of standard controllers for AbstractViewModel. - -namespace ModelView { - -//! Controller for AbstractViewModel to show all items of SessionModel. -//! The layout corresponds to original SessionModel, generates standard label/value tree. - -class MVVM_VIEWMODEL_EXPORT DefaultViewModelController : public ViewModelController { -public: - explicit DefaultViewModelController(SessionModel* session_model, ViewModelBase* view_model); -}; - -//! Controller for AbstractViewModel to show top level items. -//! Shows only top level items, property items, group items are hidden. - -class MVVM_VIEWMODEL_EXPORT TopItemsViewModelController : public ViewModelController { -public: - explicit TopItemsViewModelController(SessionModel* session_model, ViewModelBase* view_model); -}; - -//! Controller for AbstractViewModel to show item properties. -//! Shows property items, hides top level items, hides inactive items of GroupProperty. - -class MVVM_VIEWMODEL_EXPORT PropertyViewModelController : public ViewModelController { -public: - explicit PropertyViewModelController(SessionModel* session_model, ViewModelBase* view_model); - -protected: - void onDataChange(SessionItem* item, int role) override; -}; - -//! Controller for AbstractViewModel to show item properties in table layout. -//! Shows all property items and place them in table columns. - -class MVVM_VIEWMODEL_EXPORT PropertyTableViewModelController : public ViewModelController { -public: - PropertyTableViewModelController(SessionModel* session_model, ViewModelBase* view_model, - const std::vector<std::string>& labels = {}); -}; - -//! Controller for AbstractViewModel to show item properties. -//! Shows property items, hides top level items, hides inactive items of GroupProperty, -//! moves subproperties of group item under parent of group item. - -class MVVM_VIEWMODEL_EXPORT PropertyFlatViewModelController : public ViewModelController { -public: - explicit PropertyFlatViewModelController(SessionModel* session_model, - ViewModelBase* view_model); - -protected: - void onDataChange(SessionItem* item, int role) override; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_STANDARDVIEWMODELCONTROLLERS_H diff --git a/mvvm/viewmodel/mvvm/viewmodel/topitemsviewmodel.cpp b/mvvm/viewmodel/mvvm/viewmodel/topitemsviewmodel.cpp deleted file mode 100644 index ff664764adb..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/topitemsviewmodel.cpp +++ /dev/null @@ -1,23 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/viewmodel/topitemsviewmodel.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/viewmodel/topitemsviewmodel.h" -#include "mvvm/viewmodel/standardviewmodelcontrollers.h" - -using namespace ModelView; - -TopItemsViewModel::TopItemsViewModel(SessionModel* model, QObject* parent) - : ViewModel(std::make_unique<TopItemsViewModelController>(model, this), parent) -{ -} diff --git a/mvvm/viewmodel/mvvm/viewmodel/topitemsviewmodel.h b/mvvm/viewmodel/mvvm/viewmodel/topitemsviewmodel.h deleted file mode 100644 index c86035b6064..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/topitemsviewmodel.h +++ /dev/null @@ -1,34 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/viewmodel/topitemsviewmodel.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_TOPITEMSVIEWMODEL_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_TOPITEMSVIEWMODEL_H - -#include "mvvm/viewmodel/viewmodel.h" - -namespace ModelView { - -//! View model to show top level items of SessionModel in Qt trees and tables. -//! All property items (i.e. "thickness", "color" etc) will be filtered out, top level items -//! (i.e. Layer, MultiLayer, ...) will be presented as simple parent/child tree. - -class MVVM_VIEWMODEL_EXPORT TopItemsViewModel : public ViewModel { - Q_OBJECT -public: - TopItemsViewModel(SessionModel* model, QObject* parent = nullptr); -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_TOPITEMSVIEWMODEL_H diff --git a/mvvm/viewmodel/mvvm/viewmodel/viewitem.cpp b/mvvm/viewmodel/mvvm/viewmodel/viewitem.cpp deleted file mode 100644 index 16f30edac2d..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/viewitem.cpp +++ /dev/null @@ -1,245 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/viewmodel/viewitem.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/viewmodel/viewitem.h" -#include "mvvm/model/customvariants.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/utils/containerutils.h" -#include "mvvm/viewmodel/viewmodelutils.h" -#include <algorithm> -#include <stdexcept> -#include <vector> - -using namespace ModelView; - -struct ViewItem::ViewItemImpl { - std::vector<std::unique_ptr<ViewItem>> children; //! buffer to hold rows x columns - int rows{0}; - int columns{0}; - SessionItem* item{nullptr}; - int role{0}; - ViewItem* parent_view_item{nullptr}; - ViewItemImpl(SessionItem* item, int role) : item(item), role(role) {} - - void appendRow(std::vector<std::unique_ptr<ViewItem>> items) - { - insertRow(rows, std::move(items)); - } - - void insertRow(int row, std::vector<std::unique_ptr<ViewItem>> items) - { - if (items.empty()) - throw std::runtime_error("Error in ViewItemImpl: attempt to insert empty row"); - - if (columns > 0 && items.size() != static_cast<size_t>(columns)) - throw std::runtime_error("Error in ViewItemImpl: wrong number of columns."); - - if (row < 0 || row > rows) - throw std::runtime_error("Error in ViewItemImpl: invalid row index."); - - children.insert(std::next(children.begin(), row * columns), - std::make_move_iterator(items.begin()), - std::make_move_iterator(items.end())); - - columns = static_cast<int>(items.size()); - ++rows; - } - - void removeRow(int row) - { - if (row < 0 || row >= rows) - throw std::runtime_error("Error in RefViewItem: invalid row index."); - - auto begin = std::next(children.begin(), row * columns); - auto end = std::next(begin, columns); - children.erase(begin, end); - --rows; - if (rows == 0) - columns = 0; - } - - ViewItem* child(int row, int column) const - { - if (row < 0 || row >= rows) - throw std::runtime_error("Error in RefViewItem: wrong row)"); - - if (column < 0 || column >= columns) - throw std::runtime_error("Error in RefViewItem: wrong column)"); - - return children.at(static_cast<size_t>(column + row * columns)).get(); - } - - ViewItem* parent() { return parent_view_item; } - - int index_of_child(const ViewItem* child) - { - return Utils::IndexOfItem(children.begin(), children.end(), child); - } - - //! Returns item data associated with this RefViewItem. - - QVariant data() const { return item ? item->data<QVariant>(role) : QVariant(); } - - //! Returns vector of children. - - std::vector<ViewItem*> get_children() const - { - std::vector<ViewItem*> result; - std::transform(children.begin(), children.end(), std::back_inserter(result), - [](const auto& x) { return x.get(); }); - return result; - } -}; - -ViewItem::ViewItem(SessionItem* item, int role) : p_impl(std::make_unique<ViewItemImpl>(item, role)) -{ -} - -ViewItem::~ViewItem() = default; - -//! Returns the number of child item rows that the item has. - -int ViewItem::rowCount() const -{ - return p_impl->rows; -} - -//! Returns the number of child item columns that the item has. - -int ViewItem::columnCount() const -{ - return p_impl->columns; -} - -//! Appends a row containing items. Number of items should be the same as columnCount() -//! (if there are already some rows). If it is a first row, then items can be of any size. - -void ViewItem::appendRow(std::vector<std::unique_ptr<ViewItem>> items) -{ - for (auto& x : items) - x->setParent(this); - p_impl->appendRow(std::move(items)); -} - -//! Insert a row of items at index 'row'. - -void ViewItem::insertRow(int row, std::vector<std::unique_ptr<ViewItem>> items) -{ - for (auto& x : items) - x->setParent(this); - p_impl->insertRow(row, std::move(items)); -} - -//! Removes row of items at given 'row'. Items will be deleted. - -void ViewItem::removeRow(int row) -{ - p_impl->removeRow(row); -} - -void ViewItem::clear() -{ - p_impl->children.clear(); - p_impl->rows = 0; - p_impl->columns = 0; -} - -ViewItem* ViewItem::parent() const -{ - return p_impl->parent(); -} - -ViewItem* ViewItem::child(int row, int column) const -{ - return p_impl->child(row, column); -} - -SessionItem* ViewItem::item() const -{ - return p_impl->item; -} - -int ViewItem::item_role() const -{ - return p_impl->role; -} - -//! Returns the row where the item is located in its parent's child table, or -1 if the item has no -//! parent. - -int ViewItem::row() const -{ - auto index = parent() ? parent()->p_impl->index_of_child(this) : -1; - return index >= 0 ? index / parent()->p_impl->columns : -1; -} - -//! Returns the column where the item is located in its parent's child table, or -1 if the item has -//! no parent. - -int ViewItem::column() const -{ - auto index = parent() ? parent()->p_impl->index_of_child(this) : -1; - return index >= 0 ? index % parent()->p_impl->columns : -1; -} - -//! Returns the data for given role according to Qt::ItemDataRole namespace definitions. -//! Converts data and roles from underlying SessionItem to what Qt expects. - -QVariant ViewItem::data(int qt_role) const -{ - if (!p_impl->item) - return QVariant(); - - if (qt_role == Qt::DisplayRole || qt_role == Qt::EditRole) - return Utils::toQtVariant(p_impl->data()); -#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) - else if (qt_role == Qt::ForegroundRole) -#else - else if (qt_role == Qt::TextColorRole) -#endif - return Utils::TextColorRole(*p_impl->item); - else if (qt_role == Qt::ToolTipRole) - return Utils::ToolTipRole(*p_impl->item); - else - return QVariant(); -} - -//! Sets the data to underlying SessionItem. -//! Converts data and roles from Qt definitions to what SessionItem expects. - -bool ViewItem::setData(const QVariant& value, int qt_role) -{ - if (p_impl->item && qt_role == Qt::EditRole) - return p_impl->item->setData(Utils::toCustomVariant(value), p_impl->role); - return false; -} - -//! Returns Qt's item flags. -//! Converts internal SessionItem's status enable/disabled/readonly to what Qt expects. - -Qt::ItemFlags ViewItem::flags() const -{ - Qt::ItemFlags result = Qt::ItemIsSelectable | Qt::ItemIsEnabled; - return result; -} - -std::vector<ViewItem*> ViewItem::children() const -{ - return p_impl->get_children(); -} - -void ViewItem::setParent(ViewItem* parent) -{ - p_impl->parent_view_item = parent; -} diff --git a/mvvm/viewmodel/mvvm/viewmodel/viewitem.h b/mvvm/viewmodel/mvvm/viewmodel/viewitem.h deleted file mode 100644 index ddfbf935a9d..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/viewitem.h +++ /dev/null @@ -1,76 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/viewmodel/viewitem.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_VIEWITEM_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_VIEWITEM_H - -#include "mvvm/core/variant.h" -#include "mvvm/viewmodel_export.h" -#include <memory> -#include <vector> - -namespace ModelView { - -class SessionItem; - -//! Represents the view of SessionItem's data in a single cell of ViewModel. - -class MVVM_VIEWMODEL_EXPORT ViewItem { -public: - virtual ~ViewItem(); - - int rowCount() const; - - int columnCount() const; - - void appendRow(std::vector<std::unique_ptr<ViewItem>> items); - - void insertRow(int row, std::vector<std::unique_ptr<ViewItem>> items); - - void removeRow(int row); - - void clear(); - - ViewItem* parent() const; - - ViewItem* child(int row, int column) const; - - SessionItem* item() const; - - int item_role() const; - - int row() const; - - int column() const; - - virtual QVariant data(int qt_role) const; - - virtual bool setData(const QVariant& value, int qt_role); - - virtual Qt::ItemFlags flags() const; - - std::vector<ViewItem*> children() const; - -protected: - ViewItem(SessionItem* item, int role); - void setParent(ViewItem* parent); - -private: - struct ViewItemImpl; - std::unique_ptr<ViewItemImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_VIEWITEM_H diff --git a/mvvm/viewmodel/mvvm/viewmodel/viewmodel.cpp b/mvvm/viewmodel/mvvm/viewmodel/viewmodel.cpp deleted file mode 100644 index 9a0d907ec40..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/viewmodel.cpp +++ /dev/null @@ -1,84 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/viewmodel/viewmodel.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/viewmodel/viewmodel.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/viewmodel/standardviewitems.h" -#include "mvvm/viewmodel/viewmodelcontroller.h" - -using namespace ModelView; - -ViewModel::ViewModel(std::unique_ptr<ViewModelController> controller, QObject* parent) - : ViewModelBase(parent), m_controller(std::move(controller)) -{ - m_controller->setViewModel(this); - m_controller->setRootSessionItem(sessionModel()->rootItem()); -} - -QVariant ViewModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { - auto data = m_controller->horizontalHeaderLabels(); - if (section < data.size()) - return data.at(section); - } - return QVariant(); -} - -SessionModel* ViewModel::sessionModel() const -{ - return m_controller->sessionModel(); -} - -SessionItem* ViewModel::rootSessionItem() -{ - return m_controller->rootSessionItem(); -} - -ViewModel::~ViewModel() = default; - -void ViewModel::setRootSessionItem(SessionItem* item) -{ - if (!item) - throw std::runtime_error("Error in ViewModel: atttemp to set nulptr as root item"); - m_controller->setRootSessionItem(item); -} - -SessionItem* ViewModel::sessionItemFromIndex(const QModelIndex& index) const -{ - return index.isValid() ? itemFromIndex(index)->item() : m_controller->rootSessionItem(); -} - -ViewItem* ViewModel::viewItemFromIndex(const QModelIndex& index) const -{ - return itemFromIndex(index); -} - -//! Returns list of model indices representing given SessionItem. - -QModelIndexList ViewModel::indexOfSessionItem(const SessionItem* item) const -{ - QModelIndexList result; - for (auto view : m_controller->findViews(item)) - result.push_back(indexFromItem(view)); - return result; -} - -//! Returns vector of all ViewItem's representing given SessionItem. - -std::vector<ViewItem*> ViewModel::findViews(const SessionItem* item) const -{ - return m_controller->findViews(item); -} diff --git a/mvvm/viewmodel/mvvm/viewmodel/viewmodel.h b/mvvm/viewmodel/mvvm/viewmodel/viewmodel.h deleted file mode 100644 index d9e7b3bac1d..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/viewmodel.h +++ /dev/null @@ -1,59 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/viewmodel/viewmodel.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_VIEWMODEL_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_VIEWMODEL_H - -#include "mvvm/viewmodel/viewmodelbase.h" - -namespace ModelView { - -class SessionModel; -class SessionItem; -class ViewItem; -class ViewModelController; - -//! Main class to represent content of SessionModel in Qt's trees and tables. - -class MVVM_VIEWMODEL_EXPORT ViewModel : public ViewModelBase { - Q_OBJECT - -public: - ViewModel(std::unique_ptr<ViewModelController> controller, QObject* parent = nullptr); - ~ViewModel() override; - - QVariant headerData(int section, Qt::Orientation orientation, - int role = Qt::DisplayRole) const override; - - SessionModel* sessionModel() const; - - SessionItem* rootSessionItem(); - - void setRootSessionItem(SessionItem* item); - - SessionItem* sessionItemFromIndex(const QModelIndex& index) const; - - ViewItem* viewItemFromIndex(const QModelIndex& index) const; - - QModelIndexList indexOfSessionItem(const SessionItem* item) const; - - std::vector<ViewItem*> findViews(const ModelView::SessionItem* item) const; - -private: - std::unique_ptr<ViewModelController> m_controller; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_VIEWMODEL_H diff --git a/mvvm/viewmodel/mvvm/viewmodel/viewmodelbase.cpp b/mvvm/viewmodel/mvvm/viewmodel/viewmodelbase.cpp deleted file mode 100644 index 2a0e3c4840c..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/viewmodelbase.cpp +++ /dev/null @@ -1,185 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/viewmodel/viewmodelbase.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/viewmodel/viewmodelbase.h" -#include "mvvm/viewmodel/standardviewitems.h" -#include <stdexcept> - -using namespace ModelView; - -struct ViewModelBase::ViewModelBaseImpl { - ViewModelBase* model{nullptr}; - std::unique_ptr<ViewItem> root; - ViewModelBaseImpl(ViewModelBase* model) : model(model) {} - - bool item_belongs_to_model(ViewItem* item) - { - return model->indexFromItem(item).isValid() || item == model->rootItem(); - } -}; - -ViewModelBase::ViewModelBase(QObject* parent) - : QAbstractItemModel(parent), p_impl(std::make_unique<ViewModelBaseImpl>(this)) -{ - beginResetModel(); - setRootViewItem(std::make_unique<RootViewItem>(nullptr)); - endResetModel(); -} - -ViewModelBase::~ViewModelBase() = default; - -QModelIndex ViewModelBase::index(int row, int column, const QModelIndex& parent) const -{ - auto parent_item = itemFromIndex(parent) ? itemFromIndex(parent) : rootItem(); - const bool is_valid_row = row >= 0 && row < rowCount(parent); - const bool is_valid_column = column >= 0 && column < columnCount(parent); - return is_valid_row && is_valid_column - ? createIndex(row, column, parent_item->child(row, column)) - : QModelIndex(); -} - -QModelIndex ViewModelBase::parent(const QModelIndex& child) const -{ - if (auto child_item = itemFromIndex(child); child_item) { - auto parent_item = child_item->parent(); - return parent_item == rootItem() - ? QModelIndex() - : createIndex(parent_item->row(), parent_item->column(), parent_item); - } - - return QModelIndex(); -} - -int ViewModelBase::rowCount(const QModelIndex& parent) const -{ - auto parent_item = itemFromIndex(parent); - return parent_item ? parent_item->rowCount() : rootItem()->rowCount(); -} - -int ViewModelBase::columnCount(const QModelIndex& parent) const -{ - auto parent_item = itemFromIndex(parent); - return parent_item ? parent_item->columnCount() : rootItem()->columnCount(); -} - -QVariant ViewModelBase::data(const QModelIndex& index, int role) const -{ - if (!rootItem()) - return QVariant(); - - auto item = itemFromIndex(index); - return item ? item->data(role) : QVariant(); -} - -bool ViewModelBase::setData(const QModelIndex& index, const QVariant& value, int role) -{ - if (!index.isValid()) - return false; - - if (auto item = itemFromIndex(index); item) { - bool result = item->setData(value, role); - if (result) - dataChanged(index, index, QVector<int>() << role); - return result; - } - - return false; -} - -//! Returns a pointer to invisible root item. - -ViewItem* ViewModelBase::rootItem() const -{ - return p_impl->root.get(); -} - -//! Returns a pointer to the RefViewItem associated with the given index. -//! If index is invalid, returns nullptr. - -ViewItem* ViewModelBase::itemFromIndex(const QModelIndex& index) const -{ - return index.isValid() ? static_cast<ViewItem*>(index.internalPointer()) : nullptr; -} - -//! Returns the QModelIndex associated with the given item. - -QModelIndex ViewModelBase::indexFromItem(const ViewItem* item) const -{ - return item && item->parent() - ? createIndex(item->row(), item->column(), const_cast<ViewItem*>(item)) - : QModelIndex(); -} - -void ViewModelBase::removeRow(ViewItem* parent, int row) -{ - if (!p_impl->item_belongs_to_model(parent)) - throw std::runtime_error( - "Error in ViewModelBase: attempt to use parent from another model"); - - beginRemoveRows(indexFromItem(parent), row, row); - parent->removeRow(row); - endRemoveRows(); -} - -void ViewModelBase::clearRows(ViewItem* parent) -{ - if (!p_impl->item_belongs_to_model(parent)) - throw std::runtime_error( - "Error in ViewModelBase: attempt to use parent from another model"); - - if (!parent->rowCount()) - return; - - beginRemoveRows(indexFromItem(parent), 0, parent->rowCount() - 1); - parent->clear(); - endRemoveRows(); -} - -//! Insert a row of items at index 'row' to given parent. - -void ViewModelBase::insertRow(ViewItem* parent, int row, - std::vector<std::unique_ptr<ViewItem>> items) -{ - if (!p_impl->item_belongs_to_model(parent)) - throw std::runtime_error( - "Error in ViewModelBase: attempt to use parent from another model"); - - beginInsertRows(indexFromItem(parent), row, row); - parent->insertRow(row, std::move(items)); - endInsertRows(); -} - -//! Appends row of items to given parent. - -void ViewModelBase::appendRow(ViewItem* parent, std::vector<std::unique_ptr<ViewItem>> items) -{ - insertRow(parent, parent->rowCount(), std::move(items)); -} - -//! Returns the item flags for the given index. - -Qt::ItemFlags ViewModelBase::flags(const QModelIndex& index) const -{ - Qt::ItemFlags result = QAbstractItemModel::flags(index); - if (auto item = itemFromIndex(index); item) - result |= item->flags(); - return result; -} - -//! Sets new root item. Previous item will be deleted, model will be reset. - -void ViewModelBase::setRootViewItem(std::unique_ptr<ViewItem> root_item) -{ - p_impl->root = std::move(root_item); -} diff --git a/mvvm/viewmodel/mvvm/viewmodel/viewmodelbase.h b/mvvm/viewmodel/mvvm/viewmodel/viewmodelbase.h deleted file mode 100644 index ef1ba1b60bf..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/viewmodelbase.h +++ /dev/null @@ -1,75 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/viewmodel/viewmodelbase.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_VIEWMODELBASE_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_VIEWMODELBASE_H - -#include "mvvm/viewmodel_export.h" -#include <QAbstractItemModel> -#include <memory> - -namespace ModelView { - -class ViewItem; - -//! Base class for all view models to show content of SessionModel in Qt views. -//! ViewModelBase is made of ViewItems, where each ViewItem represents some concrete data role -//! of SessionItem. ViewModelBase doesn't have own logic and needs ViewModelController to listen -//! for SessionModel changes. - -class MVVM_VIEWMODEL_EXPORT ViewModelBase : public QAbstractItemModel { - Q_OBJECT -public: - explicit ViewModelBase(QObject* parent = nullptr); - ~ViewModelBase() override; - - QModelIndex index(int row, int column, - const QModelIndex& parent = QModelIndex()) const override; - - QModelIndex parent(const QModelIndex& child) const override; - - int rowCount(const QModelIndex& parent = QModelIndex()) const override; - - int columnCount(const QModelIndex& parent = QModelIndex()) const override; - - QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; - - bool setData(const QModelIndex& index, const QVariant& value, int role) override; - - ViewItem* rootItem() const; - - ViewItem* itemFromIndex(const QModelIndex& index) const; - - QModelIndex indexFromItem(const ViewItem* item) const; - - void removeRow(ViewItem* parent, int row); - - void clearRows(ViewItem* parent); - - void insertRow(ViewItem* parent, int row, std::vector<std::unique_ptr<ViewItem>> items); - - void appendRow(ViewItem* parent, std::vector<std::unique_ptr<ViewItem>> items); - - Qt::ItemFlags flags(const QModelIndex& index) const override; - -private: - void setRootViewItem(std::unique_ptr<ViewItem> root_item); - friend class ViewModelController; - struct ViewModelBaseImpl; - std::unique_ptr<ViewModelBaseImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_VIEWMODELBASE_H diff --git a/mvvm/viewmodel/mvvm/viewmodel/viewmodelcontroller.cpp b/mvvm/viewmodel/mvvm/viewmodel/viewmodelcontroller.cpp deleted file mode 100644 index 34406baa284..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/viewmodelcontroller.cpp +++ /dev/null @@ -1,308 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/viewmodel/viewmodelcontroller.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/viewmodel/viewmodelcontroller.h" -#include "mvvm/interfaces/childrenstrategyinterface.h" -#include "mvvm/interfaces/rowstrategyinterface.h" -#include "mvvm/model/itemutils.h" -#include "mvvm/model/modelutils.h" -#include "mvvm/model/path.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/model/sessionmodel.h" -#include "mvvm/utils/containerutils.h" -#include "mvvm/viewmodel/standardviewitems.h" -#include "mvvm/viewmodel/viewmodelbase.h" -#include "mvvm/viewmodel/viewmodelutils.h" -#include <map> -#include <stdexcept> - -using namespace ModelView; - -namespace { - -//! Returns true if given SessionItem role is valid for view -bool isValidItemRole(const ViewItem* view, int item_role) -{ - if (view->item_role() == item_role) - return true; - - if (item_role == ItemDataRole::APPEARANCE || item_role == ItemDataRole::TOOLTIP) - return true; - return false; -} - -} // namespace - -struct ViewModelController::ViewModelControllerImpl { - ViewModelController* m_self; - ViewModelBase* m_viewModel{nullptr}; - std::unique_ptr<ChildrenStrategyInterface> m_childrenStrategy; - std::unique_ptr<RowStrategyInterface> m_rowStrategy; - std::map<SessionItem*, ViewItem*> m_itemToVview; //! correspondence of item and its view - Path m_rootItemPath; - - ViewModelControllerImpl(ViewModelController* controller, ViewModelBase* view_model) - : m_self(controller), m_viewModel(view_model) - { - } - - void check_initialization() - { - const std::string msg("Error in ViewModelController: "); - if (!m_viewModel) - throw std::runtime_error(msg + "ViewModel is not defined"); - - if (!m_rowStrategy) - throw std::runtime_error(msg + "RowStrategy is not defined"); - - if (!m_childrenStrategy) - throw std::runtime_error(msg + "Children is not defined"); - } - - void init_view_model() - { - check_initialization(); - m_itemToVview.clear(); - m_itemToVview[m_self->rootSessionItem()] = m_viewModel->rootItem(); - iterate(m_self->rootSessionItem(), m_viewModel->rootItem()); - } - - void iterate(const SessionItem* item, ViewItem* parent) - { - ViewItem* origParent(parent); - for (auto child : m_childrenStrategy->children(item)) { - auto row = m_rowStrategy->constructRow(child); - if (!row.empty()) { - auto next_parent = row.at(0).get(); - m_viewModel->appendRow(parent, std::move(row)); - m_itemToVview[child] = next_parent; - parent = next_parent; // labelItem - iterate(child, parent); - } - parent = origParent; - } - } - - //! Remove row of ViewItem's corresponding to given item. - - void remove_row_of_views(SessionItem* item) - { - auto pos = m_itemToVview.find(item); - if (pos != m_itemToVview.end()) { - auto view = pos->second; - m_viewModel->removeRow(view->parent(), view->row()); - m_itemToVview.erase(pos); - } - } - - void remove_children_of_view(ViewItem* view) - { - for (auto child : view->children()) { - auto pos = std::find_if(m_itemToVview.begin(), m_itemToVview.end(), - [child](const auto& it) { return it.second == child; }); - if (pos != m_itemToVview.end()) - m_itemToVview.erase(pos); - } - - m_viewModel->clearRows(view); - } - - void insert_view(SessionItem* parent, const TagRow& tagrow) - { - auto child = parent->getItem(tagrow.tag, tagrow.row); - auto children = m_childrenStrategy->children(parent); - auto index = Utils::IndexOfItem(children, child); - if (index == -1) - return; - - auto pos = m_itemToVview.find(parent); - if (pos == m_itemToVview.end()) - return; - - auto parent_view = pos->second; - - auto row = m_rowStrategy->constructRow(child); - if (!row.empty()) { - auto next_parent = row.at(0).get(); - m_viewModel->insertRow(parent_view, index, std::move(row)); - m_itemToVview[child] = next_parent; - parent_view = next_parent; // labelItem - iterate(child, parent_view); - } - } - - std::vector<ViewItem*> findViews(const SessionItem* item) const - { - if (item == m_viewModel->rootItem()->item()) - return {m_viewModel->rootItem()}; - - std::vector<ViewItem*> result; - auto on_index = [&](const QModelIndex& index) { - auto view_item = m_viewModel->itemFromIndex(index); - if (view_item->item() == item) - result.push_back(view_item); - }; - Utils::iterate_model(m_viewModel, QModelIndex(), on_index); - return result; - } - - void setRootSessionItemIntern(SessionItem* item) - { - m_rootItemPath = Utils::PathFromItem(item); - m_viewModel->setRootViewItem(std::make_unique<RootViewItem>(item)); - init_view_model(); - } -}; - -ViewModelController::ViewModelController(SessionModel* session_model, ViewModelBase* view_model) - : ModelListener(session_model) - , p_impl(std::make_unique<ViewModelControllerImpl>(this, view_model)) -{ - auto on_data_change = [this](SessionItem* item, int role) { onDataChange(item, role); }; - setOnDataChange(on_data_change); - - auto on_item_inserted = [this](SessionItem* item, TagRow tagrow) { - onItemInserted(item, std::move(tagrow)); - }; - setOnItemInserted(on_item_inserted); - - auto on_item_removed = [this](SessionItem* item, TagRow tagrow) { - onItemRemoved(item, std::move(tagrow)); - }; - setOnItemRemoved(on_item_removed); - - auto on_about_to_remove = [this](SessionItem* item, TagRow tagrow) { - onAboutToRemoveItem(item, std::move(tagrow)); - }; - setOnAboutToRemoveItem(on_about_to_remove); - - auto on_model_destroyed = [this](auto) { - p_impl->m_viewModel->setRootViewItem(std::make_unique<RootViewItem>(nullptr)); - }; - setOnModelDestroyed(on_model_destroyed); - - auto on_model_reset = [this](auto) { - auto root_item = Utils::ItemFromPath(*model(), p_impl->m_rootItemPath); - p_impl->setRootSessionItemIntern(root_item ? root_item : model()->rootItem()); - p_impl->m_viewModel->endResetModel(); - }; - setOnModelReset(on_model_reset); - - auto on_model_about_to_be_reset = [this](auto) { p_impl->m_viewModel->beginResetModel(); }; - setOnModelAboutToBeReset(on_model_about_to_be_reset); -} - -void ViewModelController::setViewModel(ViewModelBase* view_model) -{ - p_impl->m_viewModel = view_model; -} - -ViewModelController::~ViewModelController() = default; - -void ViewModelController::setChildrenStrategy( - std::unique_ptr<ChildrenStrategyInterface> children_strategy) -{ - p_impl->m_childrenStrategy = std::move(children_strategy); -} - -void ViewModelController::setRowStrategy(std::unique_ptr<RowStrategyInterface> row_strategy) -{ - p_impl->m_rowStrategy = std::move(row_strategy); -} - -//! Returns SessionModel handled by this controller. - -SessionModel* ViewModelController::sessionModel() const -{ - return model(); -} - -void ViewModelController::setRootSessionItem(SessionItem* item) -{ - if (!item) - throw std::runtime_error( - "Error in ViewModelController: atttemp to set nulptr as root item"); - - if (item->model() != model()) - throw std::runtime_error( - "Error in ViewModelController: atttemp to use item from alien model as new root."); - - p_impl->m_viewModel->beginResetModel(); - p_impl->setRootSessionItemIntern(item); - p_impl->m_viewModel->endResetModel(); -} - -SessionItem* ViewModelController::rootSessionItem() const -{ - return p_impl->m_viewModel->rootItem()->item(); -} - -//! Returns all ViewItem's displaying given SessionItem. - -std::vector<ViewItem*> ViewModelController::findViews(const SessionItem* item) const -{ - return p_impl->findViews(item); -} - -QStringList ViewModelController::horizontalHeaderLabels() const -{ - return p_impl->m_rowStrategy->horizontalHeaderLabels(); -} - -void ViewModelController::onDataChange(SessionItem* item, int role) -{ - for (auto view : findViews(item)) { - // inform corresponding LabelView and DataView - if (isValidItemRole(view, role)) { - auto index = p_impl->m_viewModel->indexFromItem(view); - p_impl->m_viewModel->dataChanged(index, index, Utils::ItemRoleToQtRole(role)); - } - } -} - -void ViewModelController::onItemInserted(SessionItem* parent, TagRow tagrow) -{ - p_impl->insert_view(parent, tagrow); -} - -void ViewModelController::onItemRemoved(SessionItem*, TagRow) {} - -void ViewModelController::onAboutToRemoveItem(SessionItem* parent, TagRow tagrow) -{ - auto item_to_remove = parent->getItem(tagrow.tag, tagrow.row); - if (item_to_remove == rootSessionItem() - || Utils::IsItemAncestor(rootSessionItem(), item_to_remove)) { - // special case when user removes SessionItem which is one of ancestors of our root item - // or root item iteslf - p_impl->m_viewModel->beginResetModel(); - p_impl->m_viewModel->setRootViewItem(std::make_unique<RootViewItem>(nullptr)); - p_impl->m_itemToVview.clear(); - p_impl->m_rootItemPath = {}; - p_impl->m_viewModel->endResetModel(); - } else { - p_impl->remove_row_of_views(item_to_remove); - } -} - -void ViewModelController::update_branch(const SessionItem* item) -{ - auto views = findViews(item); - if (views.empty()) - return; - - for (auto view : views) - p_impl->remove_children_of_view(view); - - p_impl->iterate(item, views.at(0)); -} diff --git a/mvvm/viewmodel/mvvm/viewmodel/viewmodelcontroller.h b/mvvm/viewmodel/mvvm/viewmodel/viewmodelcontroller.h deleted file mode 100644 index 723870aa0c7..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/viewmodelcontroller.h +++ /dev/null @@ -1,74 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/viewmodel/viewmodelcontroller.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_VIEWMODELCONTROLLER_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_VIEWMODELCONTROLLER_H - -#include "mvvm/model/tagrow.h" -#include "mvvm/signals/modellistener.h" -#include "mvvm/viewmodel_export.h" -#include <QStringList> -#include <memory> -#include <vector> - -class QStandardItem; - -namespace ModelView { - -class SessionModel; -class SessionItem; -class ViewModelBase; -class ViewItem; -class ChildrenStrategyInterface; -class RowStrategyInterface; - -//! Propagates changes from SessionModel to its ViewModelBase. - -class MVVM_VIEWMODEL_EXPORT ViewModelController : public ModelListener<SessionModel> { -public: - ViewModelController(SessionModel* session_model, ViewModelBase* view_model = nullptr); - ~ViewModelController(); - - void setViewModel(ViewModelBase* view_model); - - void setChildrenStrategy(std::unique_ptr<ChildrenStrategyInterface> children_strategy); - - void setRowStrategy(std::unique_ptr<RowStrategyInterface> row_strategy); - - SessionModel* sessionModel() const; - - void setRootSessionItem(SessionItem* item); - - SessionItem* rootSessionItem() const; - - std::vector<ViewItem*> findViews(const ModelView::SessionItem* item) const; - - QStringList horizontalHeaderLabels() const; - -protected: - virtual void onDataChange(SessionItem* item, int role); - virtual void onItemInserted(SessionItem* parent, TagRow tagrow); - virtual void onItemRemoved(SessionItem* parent, TagRow tagrow); - virtual void onAboutToRemoveItem(SessionItem* parent, TagRow tagrow); - - void update_branch(const SessionItem* item); - -private: - struct ViewModelControllerImpl; - std::unique_ptr<ViewModelControllerImpl> p_impl; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_VIEWMODELCONTROLLER_H diff --git a/mvvm/viewmodel/mvvm/viewmodel/viewmodeldelegate.cpp b/mvvm/viewmodel/mvvm/viewmodel/viewmodeldelegate.cpp deleted file mode 100644 index 11da8b9a352..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/viewmodeldelegate.cpp +++ /dev/null @@ -1,119 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/viewmodel/viewmodeldelegate.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/viewmodel/viewmodeldelegate.h" -#include "mvvm/editors/customeditor.h" -#include "mvvm/editors/defaulteditorfactory.h" -#include "mvvm/model/comboproperty.h" -#include "mvvm/viewmodel/defaultcelldecorator.h" -#include <QApplication> - -namespace { -const double scale_default_height_factor{1.2}; -} - -using namespace ModelView; - -ViewModelDelegate::ViewModelDelegate(QObject* parent) - : QStyledItemDelegate(parent) - , m_editor_factory(std::make_unique<DefaultEditorFactory>()) - , m_cell_decoration(std::make_unique<DefaultCellDecorator>()) -{ -} - -ViewModelDelegate::~ViewModelDelegate() = default; - -void ViewModelDelegate::setEditorFactory(std::unique_ptr<EditorFactoryInterface> editor_factory) -{ - m_editor_factory = std::move(editor_factory); -} - -void ViewModelDelegate::setCellDecoration(std::unique_ptr<CellDecoratorInterface> cell_decoration) -{ - m_cell_decoration = std::move(cell_decoration); -} - -QWidget* ViewModelDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, - const QModelIndex& index) const -{ - if (auto editor = m_editor_factory->createEditor(index)) { - editor->setParent(parent); - connect(editor.get(), &CustomEditor::dataChanged, this, - &ViewModelDelegate::onCustomEditorDataChanged); - return editor.release(); - } - return QStyledItemDelegate::createEditor(parent, option, index); -} - -void ViewModelDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const -{ - if (!index.isValid()) - return; - - if (auto customEditor = dynamic_cast<CustomEditor*>(editor)) - customEditor->setData(index.data()); - else - QStyledItemDelegate::setEditorData(editor, index); -} - -void ViewModelDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, - const QModelIndex& index) const -{ - if (!index.isValid()) - return; - - if (auto customEditor = dynamic_cast<CustomEditor*>(editor)) { - model->setData(index, customEditor->data()); - } else { - QStyledItemDelegate::setModelData(editor, model, index); - } -} - -//! Increases height of the row by 20% wrt the default. - -QSize ViewModelDelegate::sizeHint(const QStyleOptionViewItem& option, - const QModelIndex& index) const -{ - QSize result = QStyledItemDelegate::sizeHint(option, index); - result.setHeight(static_cast<int>(result.height() * scale_default_height_factor)); - return result; -} - -//! Makes an editor occupying whole available space in a cell. If cell contains an icon -//! as a decoration (i.e. icon of material property), it will be hidden as soon as editor -//! up and running. - -void ViewModelDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, - const QModelIndex& index) const -{ - QStyledItemDelegate::updateEditorGeometry(editor, option, index); - editor->setGeometry(option.rect); -} - -void ViewModelDelegate::onCustomEditorDataChanged() -{ - auto editor = qobject_cast<CustomEditor*>(sender()); - emit commitData(editor); - if (!editor->is_persistent()) - emit closeEditor(editor); -} - -void ViewModelDelegate::initStyleOption(QStyleOptionViewItem* option, - const QModelIndex& index) const -{ - QStyledItemDelegate::initStyleOption(option, index); - - if (m_cell_decoration && m_cell_decoration->hasCustomDecoration(index)) - m_cell_decoration->initStyleOption(option, index); -} diff --git a/mvvm/viewmodel/mvvm/viewmodel/viewmodeldelegate.h b/mvvm/viewmodel/mvvm/viewmodel/viewmodeldelegate.h deleted file mode 100644 index 5c6863cfc8d..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/viewmodeldelegate.h +++ /dev/null @@ -1,63 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/viewmodel/viewmodeldelegate.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_VIEWMODELDELEGATE_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_VIEWMODELDELEGATE_H - -#include "mvvm/viewmodel_export.h" -#include <QStyledItemDelegate> -#include <memory> - -namespace ModelView { - -class EditorFactoryInterface; -class CellDecoratorInterface; - -//! Model delegate to provide editing/painting for custom variants. - -class MVVM_VIEWMODEL_EXPORT ViewModelDelegate : public QStyledItemDelegate { - Q_OBJECT - -public: - explicit ViewModelDelegate(QObject* parent = nullptr); - ~ViewModelDelegate() override; - - void setEditorFactory(std::unique_ptr<EditorFactoryInterface> editor_factory); - void setCellDecoration(std::unique_ptr<CellDecoratorInterface> cell_decoration); - - QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, - const QModelIndex& index) const override; - - void setEditorData(QWidget* editor, const QModelIndex& index) const override; - void setModelData(QWidget* editor, QAbstractItemModel* model, - const QModelIndex& index) const override; - - QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; - - void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, - const QModelIndex& index) const override; - -public slots: - void onCustomEditorDataChanged(); - -protected: - void initStyleOption(QStyleOptionViewItem* option, const QModelIndex& index) const override; - - std::unique_ptr<EditorFactoryInterface> m_editor_factory; - std::unique_ptr<CellDecoratorInterface> m_cell_decoration; -}; - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_VIEWMODELDELEGATE_H diff --git a/mvvm/viewmodel/mvvm/viewmodel/viewmodelutils.cpp b/mvvm/viewmodel/mvvm/viewmodel/viewmodelutils.cpp deleted file mode 100644 index 4a6665aac4f..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/viewmodelutils.cpp +++ /dev/null @@ -1,126 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/viewmodel/viewmodelutils.cpp -//! @brief Implements class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#include "mvvm/viewmodel/viewmodelutils.h" -#include "mvvm/model/customvariants.h" -#include "mvvm/model/externalproperty.h" -#include "mvvm/model/itemutils.h" -#include "mvvm/model/mvvm_types.h" -#include "mvvm/model/sessionitem.h" -#include "mvvm/viewmodel/viewitem.h" -#include "mvvm/viewmodel/viewmodel.h" -#include <QStandardItemModel> -#include <iterator> -#include <set> - -using namespace ModelView; - -void Utils::iterate_model(const QAbstractItemModel* model, const QModelIndex& parent, - const std::function<void(const QModelIndex& child)>& fun) -{ - if (!model) - return; - - for (int row = 0; row < model->rowCount(parent); ++row) { - for (int col = 0; col < model->columnCount(parent); ++col) { - auto index = model->index(row, col, parent); - if (index.isValid()) - fun(index); - } - for (int col = 0; col < model->columnCount(parent); ++col) { - auto index = model->index(row, col, parent); - iterate_model(model, index, fun); - } - } -} - -//! Translates SessionItem's data role to vector of Qt roles. - -QVector<int> Utils::ItemRoleToQtRole(int role) -{ - QVector<int> result; - // In Qt when we are editing the data in a view two roles are emmited. - if (role == ItemDataRole::DISPLAY || role == ItemDataRole::DATA) - result = {Qt::DisplayRole, Qt::EditRole}; - else if (role == ItemDataRole::APPEARANCE) -#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) - result = {Qt::ForegroundRole}; -#else - result = {Qt::TextColorRole}; -#endif - else if (role == ItemDataRole::TOOLTIP) - result = {Qt::ToolTipRole}; - - return result; -} - -QVariant Utils::TextColorRole(const SessionItem& item) -{ - return item.isEnabled() ? QVariant() : QColor(Qt::gray); -} - -QVariant Utils::CheckStateRole(const SessionItem& item) -{ - auto value = item.data<QVariant>(); - if (Utils::IsBoolVariant(value)) - return value.value<bool>() ? Qt::Checked : Qt::Unchecked; - return QVariant(); -} - -QVariant Utils::DecorationRole(const SessionItem& item) -{ - auto value = item.data<QVariant>(); - if (Utils::IsColorVariant(value)) - return value; - else if (Utils::IsExtPropertyVariant(value)) - return value.value<ExternalProperty>().color(); - return QVariant(); -} - -QVariant Utils::ToolTipRole(const SessionItem& item) -{ - return item.hasData(ItemDataRole::TOOLTIP) ? Variant(QString::fromStdString(item.toolTip())) - : QVariant(); -} - -std::vector<SessionItem*> Utils::ItemsFromIndex(const QModelIndexList& index_list) -{ - if (index_list.empty()) - return {}; - - std::vector<SessionItem*> result; - - if (auto model = dynamic_cast<const ViewModelBase*>(index_list.front().model())) - std::transform(index_list.begin(), index_list.end(), std::back_inserter(result), - [model](auto index) { return model->itemFromIndex(index)->item(); }); - - return result; -} - -std::vector<SessionItem*> Utils::UniqueItemsFromIndex(const QModelIndexList& index_list) -{ - return Utils::UniqueItems(Utils::ItemsFromIndex(index_list)); -} - -std::vector<SessionItem*> Utils::ParentItemsFromIndex(const QModelIndexList& index_list) -{ - std::set<SessionItem*> unique_parents; - for (auto item : ItemsFromIndex(index_list)) - if (item) - unique_parents.insert(item->parent()); - - std::vector<SessionItem*> result; - std::copy(unique_parents.begin(), unique_parents.end(), std::back_inserter(result)); - return result; -} diff --git a/mvvm/viewmodel/mvvm/viewmodel/viewmodelutils.h b/mvvm/viewmodel/mvvm/viewmodel/viewmodelutils.h deleted file mode 100644 index 217853c1fea..00000000000 --- a/mvvm/viewmodel/mvvm/viewmodel/viewmodelutils.h +++ /dev/null @@ -1,73 +0,0 @@ -// ************************************************************************************************ -// -// qt-mvvm: Model-view-view-model framework for large GUI applications -// -//! @file mvvm/viewmodel/mvvm/viewmodel/viewmodelutils.h -//! @brief Defines class CLASS? -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2020 -//! @authors Gennady Pospelov et al, Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_VIEWMODELUTILS_H -#define BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_VIEWMODELUTILS_H - -#include "mvvm/viewmodel_export.h" -#include <QModelIndex> -#include <QModelIndexList> -#include <QVector> -#include <functional> -#include <vector> - -class QStandardItemModel; -class QStandardItem; -class QVariant; - -namespace ModelView { - -class ViewItem; -class SessionItem; - -namespace Utils { - -//! Iterates through QAbstractItem model -MVVM_VIEWMODEL_EXPORT void iterate_model(const QAbstractItemModel* model, const QModelIndex& parent, - const std::function<void(const QModelIndex& child)>& fun); - -//! Returns vector of Qt roles corresponding to given ItemDataRole. -MVVM_VIEWMODEL_EXPORT QVector<int> ItemRoleToQtRole(int role); - -//! Returns text color for given item. -MVVM_VIEWMODEL_EXPORT QVariant TextColorRole(const SessionItem& item); - -//! Returns check state role of given item. -MVVM_VIEWMODEL_EXPORT QVariant CheckStateRole(const SessionItem& item); - -//! Returns decoration role for given item. -MVVM_VIEWMODEL_EXPORT QVariant DecorationRole(const SessionItem& item); - -//! Returns tooltip role for given item. -MVVM_VIEWMODEL_EXPORT QVariant ToolTipRole(const SessionItem& item); - -//! Returns vector of underlying SessionItem's for given index list. -MVVM_VIEWMODEL_EXPORT std::vector<SessionItem*> ItemsFromIndex(const QModelIndexList& index_list); - -//! Returns vector of underlying SessionItem's for given index list. Removes repetitions -MVVM_VIEWMODEL_EXPORT std::vector<SessionItem*> -UniqueItemsFromIndex(const QModelIndexList& index_list); - -//! Returns vector of parent items from given index list. -//! Finds all SessionItems corresponding to given index list and collect their parents. -//! Function is usefull in the context of table-like views when we want to find compound items -//! (i.e. Layers) from table cells containing LayerItem's properties (i.e. thickness). -MVVM_VIEWMODEL_EXPORT std::vector<SessionItem*> -ParentItemsFromIndex(const QModelIndexList& index_list); - -} // namespace Utils - -} // namespace ModelView - -#endif // BORNAGAIN_MVVM_VIEWMODEL_MVVM_VIEWMODEL_VIEWMODELUTILS_H -- GitLab