From f85b7136b4d2a960956831bc06c18871dae76339 Mon Sep 17 00:00:00 2001 From: William Johnson Date: Mon, 21 Oct 2024 17:33:12 -0700 Subject: [PATCH] Move getting/setting user preferences to a dedicated class, `UserPreferences`. This separation improves the separation of concerns a bit. In addition to cleaning up the interface and fixing the callback issues that were still present, it cuts the initial initialization of user preferences down from ~60 ms, to about 1 millisecond; and for users with options already in the database it slightly increases things. I think this was the last issue that needs to be fixed for the final 1.0.13 release. --- CMakeLists.txt | 2 + InterSpec/InterSpec.h | 22 +- InterSpec/InterSpecUser.h | 396 +------------ InterSpec/RemoteRid.h | 2 +- InterSpec/SpectraFileModel.h | 4 +- InterSpec/UserPreferences.h | 275 +++++++++ src/BatchActivity.cpp | 3 +- src/BatchCommandLine.cpp | 3 +- src/ColorThemeWindow.cpp | 17 +- src/CompactFileManager.cpp | 5 +- src/D3TimeChart.cpp | 3 +- src/DbFileBrowser.cpp | 6 +- src/DbStateBrowser.cpp | 2 +- src/DecayActivityDiv.cpp | 11 +- src/DecayChainChart.cpp | 3 +- src/DecaySelectNuclideDiv.cpp | 5 +- src/DecayWindow.cpp | 3 +- src/DetectionLimitSimple.cpp | 5 +- src/DetectionLimitTool.cpp | 11 +- src/DoseCalcWidget.cpp | 5 +- src/DrfSelect.cpp | 31 +- src/EnergyCalGraphical.cpp | 8 +- src/EnergyCalTool.cpp | 5 +- src/ExportSpecFile.cpp | 3 +- src/FluxTool.cpp | 3 +- src/GammaCountDialog.cpp | 3 +- src/GammaXsGui.cpp | 3 +- src/InterSpec.cpp | 154 +++--- src/InterSpecApp.cpp | 37 +- src/InterSpecUser.cpp | 564 ++----------------- src/IsotopeSearchByEnergy.cpp | 3 +- src/LeafletRadMap.cpp | 8 +- src/LicenseAndDisclaimersWindow.cpp | 7 +- src/MakeDrf.cpp | 7 +- src/MakeDrfSrcDef.cpp | 15 +- src/MoreNuclideInfoDisplay.cpp | 5 +- src/MultimediaDisplay.cpp | 3 +- src/PeakInfoDisplay.cpp | 3 +- src/ReferencePhotopeakDisplay.cpp | 11 +- src/RelActAutoGui.cpp | 17 +- src/RelActCalcManual.cpp | 9 + src/RelActManualGui.cpp | 7 +- src/RemoteRid.cpp | 49 +- src/SearchMode3DChart.cpp | 3 +- src/ShieldingSelect.cpp | 25 +- src/ShieldingSourceDisplay.cpp | 21 +- src/ShowRiidInstrumentsAna.cpp | 3 +- src/SpecFileQueryWidget.cpp | 47 +- src/SpecMeasManager.cpp | 45 +- src/SpectraFileModel.cpp | 9 +- src/TerminalWidget.cpp | 14 +- src/UnitsConverterTool.cpp | 7 +- src/UseInfoWindow.cpp | 5 +- src/UserPreferences.cpp | 829 ++++++++++++++++++++++++++++ src/WarningWidget.cpp | 15 +- 55 files changed, 1548 insertions(+), 1213 deletions(-) create mode 100644 InterSpec/UserPreferences.h create mode 100644 src/UserPreferences.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b37893c..5b81e970 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -329,6 +329,7 @@ set(sources src/EnterAppUrlWindow.cpp src/NuclideSourceEnter.cpp src/AddNewPeakDialog.cpp + src/UserPreferences.cpp js/SpectrumChart.js js/InterSpec.js ) @@ -432,6 +433,7 @@ set(headers InterSpec/EnterAppUrlWindow.h InterSpec/NuclideSourceEnter.h InterSpec/AddNewPeakDialog.h + InterSpec/UserPreferences.h ) diff --git a/InterSpec/InterSpec.h b/InterSpec/InterSpec.h index e1b1c0d8..31da90bc 100644 --- a/InterSpec/InterSpec.h +++ b/InterSpec/InterSpec.h @@ -65,6 +65,7 @@ class DrfSelectWindow; class PeakInfoDisplay; class SpecMeasManager; class UndoRedoManager; +class UserPreferences; class GammaCountDialog; class PopupDivMenuItem; class SpectraFileHeader; @@ -902,10 +903,17 @@ class InterSpec : public Wt::WContainerWidget /** Brings up a dialog asking the user to confirm starting a new session, and if they select so, will start new session. */ void startClearSession(); - // The user itself gets to be public--no need to protect access to it. - //Note 20130116: m_user should be made protected, but really the whole - // preference thing should be re-done, see README - Wt::Dbo::ptr m_user; + /** Pointer to class to access user preferences. */ + UserPreferences *preferences(); + + /** The user information in the database. */ + const Wt::Dbo::ptr &user(); + + /** Calls the `reread()` function on `m_user`, which refreshes the information pointed to by + `m_user` to match what is currently in the database. Please note, this may (and maybe always) + cause the `m_user` pointer to point to a different location in memory. + */ + void reReadUserInfoFromDb(); //sql returns the DbSession (which holds the Wt::Dbo::Session) associated // with m_user. The reason for using an indirection via @@ -1303,6 +1311,9 @@ class InterSpec : public Wt::WContainerWidget void changeLocale( std::string languageCode ); protected: + Wt::Dbo::ptr m_user; + UserPreferences *m_preferences; + PeakModel *m_peakModel; D3SpectrumDisplayDiv *m_spectrum; D3TimeChart *m_timeSeries; @@ -1315,9 +1326,6 @@ class InterSpec : public Wt::WContainerWidget void handleUserIncrementSampleNum( SpecUtils::SpectrumType type, bool increment); - - /* Start widgets this class keeps track of */ - Wt::Signal< Wt::WString, int > m_messageLogged; WarningWidget *m_warnings; diff --git a/InterSpec/InterSpecUser.h b/InterSpec/InterSpecUser.h index d1c87fb2..166dcfee 100644 --- a/InterSpec/InterSpecUser.h +++ b/InterSpec/InterSpecUser.h @@ -25,27 +25,19 @@ #include "InterSpec_config.h" -#include #include #include #include -#include -#include -#include -#include #include -#include #include -#include #include //#include //usable with Wt 3.3.1, but not currently using #include #include #include -#include "InterSpec/DataBaseUtils.h" class SpecMeas; class InterSpec; @@ -56,6 +48,7 @@ class ColorThemeInfo; class UserFileInDbData; struct ShieldingSourceModel; +namespace DataBaseUtils{ class DbSession; } class DataStr : public std::vector{ public: }; typedef DataStr FileData_t; @@ -63,10 +56,6 @@ typedef DataStr FileData_t; class UserOption; class InterSpecUser; -namespace rapidxml -{ - template class xml_node; -}//namespace rapidxml //The database this InterSpec is using; if higher than database registry, will @@ -157,23 +146,10 @@ class UserOption public: enum DataType{ String, Decimal, Integer, Boolean }; - //Right now MySQL is only used for web deployments, and there are no string - // option values that should be very long, since none of them should - // coorespond to file paths. - // Non-web deployments have a few options (like File Query Tool path, - // detector response folder location), that include file paths, so we should - // allow longer preference values. Note, SQLite does not impose any - // length restrictions on string fields, even if they are declared with - // something like VARCHAR(255). -#if( USE_MYSQL_DB ) - static const size_t sm_max_name_str_len = 30; - static const size_t sm_max_value_str_len = 35; -#elif( USE_SQLITE3_DB ) + //Note, SQLite does not impose any length restrictions on string fields, + // even if they are declared with something like VARCHAR(255). static const size_t sm_max_name_str_len = 30; static const size_t sm_max_value_str_len = 4096; -#else - static_assert( 0, "Compile-time database type not recognized" ); -#endif Wt::Dbo::ptr m_user; @@ -195,6 +171,14 @@ class UserOption };//class UserOption +/** The user information in the database. + + This stores preferences, saved states, previously opened spectra, and use statistics. + This struct should be treated as a view into the database, not a persistent object in memory; that is, + the `InterSpec::m_user` Wt::Dbo::ptr may change where it points in memory when things are + refreshed from the database. Because of this, we cant store any information in this class, that isnt + backed by the database. + */ class InterSpecUser { //Right now access to the InterSpecUser class should only be done from the @@ -208,6 +192,12 @@ class InterSpecUser TabletDevice = 0x2 }; + /** The file name of the default preferences XML file. File name is relative + to InterSpec::dataDirectory(), and has a default value of + "default_preferences.xml". + */ + static const std::string sm_defaultPreferenceFile; + public: //InterSpecUser(): default constructor necessary for Wt::Dbo to be able to // construct InterSpecUser objects to later call persist(...) on to read @@ -221,6 +211,8 @@ class InterSpecUser const std::string &userName() const; + int deviceType() const; + /** The number of new InterSpec sessions that has been created for this user. */ int accessCount() const; @@ -244,122 +236,6 @@ class InterSpecUser /** The UTC time when this user was first created in the database. */ std::chrono::system_clock::time_point firstAccessUTC() const; - //userFromViewer: a simple helper function to return the user from a - // spectrum viewer pointer; necessary to break a "Member access into - // incomplete type 'InterSpec'" issue. - static Wt::Dbo::ptr &userFromViewer( InterSpec *viewer ); - - //sqlFromViewer: same story as userFromViewer() - static std::shared_ptr sqlFromViewer( InterSpec *viewer ); - - - //preferenceValueAny(...): Retrieves preference value. If value has not - // been previously set, will return default value as well as adding this - // value to to the users preferences. If no preference by the passed in name - // is found, nor one with a default value, a runtime_error will be thrown. - // There must be a Dbo::Session associated with usr or else and exception - // will be thrown. - // The InterSpec is necessary in order to add the default value to the - // database in the case the user doesnt already have a preference by the - // given name, but that preference does exist in the default values. - static boost::any preferenceValueAny( const std::string &name, InterSpec *viewer ); - - /** Convienince function to call for #preferenceValueAny */ - template - static T preferenceValue( const std::string &name, - InterSpec *viewer ); - - //setPreferenceValue(): Sets preference value for named preference to both - // the InterSpecUser in memory and the database. - // If the preference isnt already in memory, and its not in - // data/default_preferences.xml then will throw an exception - // Note the reason for passing InterSpecUser and viewer pointers is so - // we dont have to #include InterSpec.h in this file. - template - static void setPreferenceValue( Wt::Dbo::ptr user, - const std::string &name, const T &value, - InterSpec *viewer ); - - /** Specialization of #setPreferenceValue for boolean preference; does same as other variant - of the function, but also calls functions in m_onBoolChangeSignals. - */ - static void setPreferenceValue( Wt::Dbo::ptr user, - const std::string &name, - const bool &value, - InterSpec *viewer ); - - /** Just a convenience function to call above variant, for binding to signals. */ - static void setBoolPreferenceValue( Wt::Dbo::ptr user, - const std::string &name, - const bool &value, - InterSpec *viewer ); - -protected: - template - static void setPreferenceValueWorker( Wt::Dbo::ptr user, - const std::string &name, const T &value, - InterSpec *viewer ); - -public: - - /** Add a function to callback when the preference changes, either through loading a new state, - or toggling the preference checkbox or whatever. - - This variant of #addCallbackWhenChanged is useful to book a slot (member function) of a - Wt::WObject up to, so the callback lifetime will be limited to the lifetime of the WObject. - - This function must be called from the application thread such that InterSpec::instance() will be - non-void. - */ - template - static void addCallbackWhenChanged( Wt::Dbo::ptr &user, - InterSpec *viewer, - const std::string &name, T *target, - void (V::*method)(bool) ); - - /** Adds a function to callback when the preference changes, either through loading - a new state, or toggling the preference checkbox or whatever. - - This function must be called from the application thread such that InterSpec::instance() will be - non-void - - Note, the function passed in must be sure that it will be safe to call until the end of the users - session. If you need limit lifetime of callbacks, either make sure your caller connects to - a slot of a Wt::WObject, or is otherwise somehow safe for the entirety of the InterSpec class - lifetime. - */ - template - static void addCallbackWhenChanged( Wt::Dbo::ptr &user, - InterSpec *viewer, - const std::string &name, - const T &fcn ); - - /** Makes it so if the user changes the value via this GUI element, the value of the preference - stored in memory and in the database will be correspondingly updated. Also makes it so if - a different GUI element updates this preference value, or #setPreferenceValue is called for - this preference, this widgets state will be correspondingly updated as well. - - However, the signals you have hooked up to this widget wont be called when the value changes by - another widget or #setPreferenceValue, to handle this case, use the #addCallbackWhenChanged - function to add a callback for whenever the value of the preference changes. - */ - static void associateWidget( Wt::Dbo::ptr user, - const std::string &name, - Wt::WCheckBox *cb, - InterSpec *viewer ); - - /* - static void associateWidget( Wt::Dbo::ptr user, - const std::string &name, - Wt::WSpinBox *spinner, - InterSpec *viewer ); - static void associateWidget( Wt::Dbo::ptr user, - const std::string &name, - Wt::WDoubleSpinBox *spinner, - InterSpec *viewer ); - */ - - //startingNewSession(): increments access counts, and updates previous and // current timestamps. Should be called at the begining of a new InterSpec. @@ -380,25 +256,6 @@ class InterSpecUser //userFiles(): useful to a users previously saved files const Wt::Dbo::collection< Wt::Dbo::ptr > &userFiles() const; - //userOptionsToXml(): serializes the current user options to a node called - // under parent passed in, with each preferences being placed - // in a node under it. Uses same schema as 'default_preferences.xml'. - //Returns the node. - ::rapidxml::xml_node *userOptionsToXml( - ::rapidxml::xml_node *parent, - InterSpec *viewer ) const; - - //restoreUserPrefsFromXml(): sets the currently active user preferences from - // passed in XML node. 'preferences' must be a node named - // and should be in same format that userOptionsToXml() creates. - // If there is a current user option that is not in the passd in XML, its - // value will not be altered. Any new options in the XML not currently in - // memory will be set. - //Throws exception upon error. - static void restoreUserPrefsFromXml( Wt::Dbo::ptr user, - const ::rapidxml::xml_node *preferences, - InterSpec *viewer ); - template void persist( Action &a ) { @@ -420,6 +277,7 @@ class InterSpecUser std::chrono::system_clock::time_point currentAccessStartUTC() const; + const Wt::Dbo::collection< Wt::Dbo::ptr > &preferences() const; const Wt::Dbo::collection< Wt::Dbo::ptr > &shieldSrcModels() const; const Wt::Dbo::collection< Wt::Dbo::ptr > &userStates() const; const Wt::Dbo::collection< Wt::Dbo::ptr > &colorThemes() const; @@ -430,13 +288,7 @@ class InterSpecUser void setCurrentAccessTime( const std::chrono::system_clock::time_point &utcTime ); void setPreviousAccessTime( const std::chrono::system_clock::time_point &utcTime ); - //getDefaultUserPreference(...): will throw exception upon error, otherwise - // results will always be valid. - //Will search for user option specialized for DeviceType (represented by the - // int 'type') before returning the general option - static UserOption *getDefaultUserPreference( const std::string &name, - const int type ); - + std::string m_userName; int m_deviceType; int m_accessCount; @@ -446,23 +298,6 @@ class InterSpecUser boost::posix_time::ptime m_currentAccessStartUTC; boost::posix_time::time_duration m_totalTimeInApp; - /** Holds callbacks set from #addCallbackWhenChanged, for boolean preferences. - - I believe using a Wt::Signals::signal allows makes it so we can connect widget slots (function - calls), and then the connection will automatically get deleted when the widget is deleted, making - things safe. - - Note that these function may be "safe" for widgets getting deleted (e.g., signals automatically - disconnected), if the signal is connected to an object deriving from Wt::WObject. - - Currently only boolean preferences require callbacks in InterSpec; in the future, if other - preference types (string, int, doubles) need callbacks, we will have to re-factor things, or - add in analogous member variables. - - Mutable so Dbo::ptr.modify() doesnt need to be called - */ - mutable std::map>> m_onBoolChangeSignals; - Wt::Dbo::collection< Wt::Dbo::ptr > m_userFiles; Wt::Dbo::collection< Wt::Dbo::ptr > m_dbPreferences; @@ -475,11 +310,7 @@ class InterSpecUser // Wt::Dbo::collection< Wt::Dbo::ptr > m_spectrums; - /** The file name of the default preferences XML file. File name is relative - to InterSpec::dataDirectory(), and has a default value of - "default_preferences.xml". - */ - static const std::string sm_defaultPreferenceFile; + friend class InterSpec; };//struct UserOption @@ -1070,189 +901,6 @@ struct UseDrfPref } };//struct UseDrfPref - -//Implementation of inline and templated functions -#include -#include -#include -#include - - -template -T InterSpecUser::preferenceValue( const std::string &name, - InterSpec *viewer ) -{ - boost::any value = InterSpecUser::preferenceValueAny( name, viewer ); - return boost::any_cast( value ); -}//preferenceValue(...) - - - -template -void InterSpecUser::setPreferenceValueWorker( Wt::Dbo::ptr user, - const std::string &name, const T &value, - InterSpec *viewer ) -{ - if( name.size() > UserOption::sm_max_name_str_len ) - throw std::runtime_error( "Invalid name for preference: " + name ); - - std::shared_ptr sql = sqlFromViewer( viewer ); - - const T oldVal = preferenceValue( name, viewer ); - if( value == oldVal ) - return; - - if( sql->session() != user.session() ) - throw std::runtime_error( "InterSpecUser::setPreferenceValue() must be called" - " with same db session as user" ); - - std::string strval; - {// Begin put value into strval - std::stringstream valuestrm; - valuestrm << value; - strval = valuestrm.str(); - if( strval.size() > UserOption::sm_max_value_str_len ) - strval = strval.substr( 0, UserOption::sm_max_value_str_len ); - }// End put value into strval - - std::vector< Wt::Dbo::ptr > options; - {//begin interacting with the database - DataBaseUtils::DbTransaction transaction( *sql ); - Wt::Dbo::collection< Wt::Dbo::ptr > optioncol - = user->m_dbPreferences.find() - .where( "name=?" ).bind( name ); - std::copy( optioncol.begin(), optioncol.end(), std::back_inserter(options) ); - transaction.commit(); - }//end interacting with the database - - const size_t noptions = options.size(); - - if( noptions > 1 ) - { - //Hmmm, not sure how this happened, but I have made it here for "ColorThemeIndex". - // We just delete all options and start over. -#if( PERFORM_DEVELOPER_CHECKS ) - char buffer[1024]; - snprintf( buffer, sizeof(buffer), "Invalid number of preferences (%i) for %s for user %s; will" - " remove all of them after the first from the database.", - static_cast(noptions), name.c_str(), user->userName().c_str() ); - log_developer_error( __func__, buffer ); -#endif - - try - { - DataBaseUtils::DbTransaction transaction( *sql ); - - for( size_t i = 0; i < (noptions - 1); ++i ) - options[i].remove(); - - // Delete all but the last one in the DB - Wt::Dbo::ptr option = options.back(); - option.modify()->m_value = strval; - - transaction.commit(); - }catch(...) - { - assert( 0 ); - std::cerr << "Caught exception removing duplicate preference from database" << std::endl; - } - - DataBaseUtils::DbTransaction transaction( *sql ); - - Wt::Dbo::ptr option = options.back(); - option.modify()->m_value = strval; - - transaction.commit(); - }else if( noptions == 0 ) - { - UserOption *newoption = new UserOption(); - newoption->m_name = name; - newoption->m_user = user; - newoption->m_value = strval; - - if( typeid(value) == typeid(std::string) ) - newoption->m_type = UserOption::String; - else if( typeid(value) == typeid(double) || typeid(value) == typeid(float) ) - newoption->m_type = UserOption::Decimal; - else if( typeid(value) == typeid(int) - || typeid(value) == typeid(unsigned int) - || typeid(value) == typeid(long long) ) - newoption->m_type = UserOption::Integer; - else if( typeid(value) == typeid(bool) ) - newoption->m_type = UserOption::Boolean; - else - { - delete newoption; - throw std::runtime_error( "setPreferenceValue(...): invalid type: " - + std::string(typeid(value).name()) ); - } - - DataBaseUtils::DbTransaction transaction( *sql ); - Wt::Dbo::ptr option = sql->session()->add( newoption ); - //user.modify()->m_dbPreferences.insert( newoption ); // I think this is equivalent of `newoption->m_user = user;` - transaction.commit(); - }else //if( noptions == 1 ) - { - Wt::Dbo::ptr option = options.front(); - - DataBaseUtils::DbTransaction transaction( *sql ); - option.modify()->m_value = strval; - transaction.commit(); - }//if( noptions == 0 ) / else / else -}//setPreferenceValueWorker(...) - - - - -template -void InterSpecUser::setPreferenceValue( Wt::Dbo::ptr user, - const std::string &name, const T &value, - InterSpec *viewer ) -{ - setPreferenceValueWorker( user, name, value, viewer ); -} - - -template -void InterSpecUser::addCallbackWhenChanged( Wt::Dbo::ptr &user, - InterSpec *viewer, - const std::string &name, const T &fcn ) -{ - assert( user ); - if( !user ) //shouldnt ever happen - return; - - //Make sure a valid bool preference - user->preferenceValue( name, viewer ); - - std::shared_ptr> &signal = user->m_onBoolChangeSignals[name]; - if( !signal ) - signal = std::make_shared>(); - signal->connect( fcn ); -}//addCallbackWhenChanged(...) - - -template -void InterSpecUser::addCallbackWhenChanged( Wt::Dbo::ptr &user, - InterSpec *viewer, - const std::string &name, - T *target, void (V::*method)(bool) ) -{ - assert( user ); - if( !user ) //shouldnt ever happen - return; - - //Make sure a valid bool preference - user->preferenceValue( name, viewer ); - - // Retrieve (or create) the signal, and connect things up - std::shared_ptr> &signal = user->m_onBoolChangeSignals[name]; - if( !signal ) - signal = std::make_shared>(); - - signal->connect( boost::bind(method, target, boost::placeholders::_1) ); -}//addCallbackWhenChanged(...) - #endif //InterSpecUser_h diff --git a/InterSpec/RemoteRid.h b/InterSpec/RemoteRid.h index 23214964..7b6fc1ef 100644 --- a/InterSpec/RemoteRid.h +++ b/InterSpec/RemoteRid.h @@ -40,7 +40,7 @@ namespace RestRidImp { class ExternalRidWidget; } /** An enum that represents the value of the integer "AlwaysCallExternalRid" preference. ``` - const int always_call = InterSpecUser::preferenceValue( "AlwaysCallExternalRid", m_interspec ); + const int always_call = UserPreferences::preferenceValue( "AlwaysCallExternalRid", m_interspec ); const auto pref = static_cast( always_call ); ``` */ diff --git a/InterSpec/SpectraFileModel.h b/InterSpec/SpectraFileModel.h index 561bd72e..2cd97c15 100644 --- a/InterSpec/SpectraFileModel.h +++ b/InterSpec/SpectraFileModel.h @@ -130,9 +130,7 @@ class SpectraFileHeader // all caching mechanisms implicitly rely on SpecUtils::SpecFile not being // changed out of this class public: - SpectraFileHeader( Wt::Dbo::ptr user, - bool keepInMemmory, - InterSpec *viewer ); + SpectraFileHeader( bool keepInMemmory, InterSpec *viewer ); virtual ~SpectraFileHeader() noexcept(true); diff --git a/InterSpec/UserPreferences.h b/InterSpec/UserPreferences.h new file mode 100644 index 00000000..a8d7980c --- /dev/null +++ b/InterSpec/UserPreferences.h @@ -0,0 +1,275 @@ +#ifndef UserPrefernces_h +#define UserPrefernces_h +/* InterSpec: an application to analyze spectral gamma radiation data. + + Copyright 2018 National Technology & Engineering Solutions of Sandia, LLC + (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. + Government retains certain rights in this software. + For questions contact William Johnson via email at wcjohns@sandia.gov, or + alternative emails of interspec@sandia.gov. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "InterSpec_config.h" + +#include +#include +#include + +#include +#include +#include + +class InterSpec; +class UserOption; +class InterSpecUser; +namespace rapidxml{ template class xml_node; } + + +/** This class tracks user preferences that get stored in the database. + + It is used to access or change user preferences, or hold callbacks to perform + an action when a value is changed. + + This functionality can not be part of the `InterSpecUser` class, as the object + pointed to by `InterSpec::m_user` is not stable, and will change when it + gets refreshed from the database, so adding any extra non-database backed info + there will get lost randomly. + + This class also caches database values to speed things up a bit - its not much + (~60 ms on first load, on a M1 macbook pro, a few ms on subsequent loads), + but death by a thousand paper cuts is always an issue. + */ +class UserPreferences : public Wt::WObject +{ +public: + /** You must pass in a valid pointer. + */ + UserPreferences( InterSpec *parent ); + + + /** Returns preference value, for the named preference. + + If preference has not been previously set, will return default value as + well as adding this value to to the users preferences. + + If no preference by the passed in name is found, nor one with a default value, a + `runtime_error` will be thrown. + + First checks if the preference is in `UserPreferences::m_options`, and then if not, + will check the database (incase another session has added it since this session began), + and then finally gets the default value from the XML file, and both adds it to the database, + and returns that answer. + */ + static boost::any preferenceValueAny( const std::string &name, InterSpec *viewer ); + + /** Convenience function to call for #preferenceValueAny */ + template + static T preferenceValue( const std::string &name, + InterSpec *viewer ); + + /** Sets preference value for named preference to both the in-memory value (stored by `UserPreferences`) + and the database. + + If a preference with the specified name isnt already in memory, and the name is not in + `data/default_preferences.xml` then will throw an exception. + + This function merely calls the various `setPreferenceValueInternal(...)` functions, + but is set up this way to make sure implicit conversions of types is not performed somewhere, + leading to wonky results. + */ + template + static typename std::enable_if< + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value + >::type + setPreferenceValue( const std::string &name, const T& value, InterSpec *viewer ); + + + /** Just a convenience function to call above variant, for binding to signals. */ + static void setBoolPreferenceValue( const std::string &name, const bool &value, InterSpec *viewer ); + + + /** Add a function to callback when the preference changes, either through loading a new state, + or toggling the preference checkbox or whatever. + + This variant of #addCallbackWhenChanged is useful to book a slot (member function) of a + Wt::WObject up to, so the callback lifetime will be limited to the lifetime of the WObject. + + This function must be called from the application thread such that InterSpec::instance() will be + non-void. + */ + template + void addCallbackWhenChanged( const std::string &name, T *target, + void (V::*method)(bool) ); + + /** Adds a function to callback when the preference changes, either through loading + a new state, or toggling the preference checkbox or whatever. + + This function must be called from the application thread such that InterSpec::instance() will be + non-void + + Note, the function passed in must be sure that it will be safe to call until the end of the users + session. If you need limit lifetime of callbacks, either make sure your caller connects to + a slot of a Wt::WObject, or is otherwise somehow safe for the entirety of the InterSpec class + lifetime. + */ + template + void addCallbackWhenChanged( const std::string &name, + const T &fcn ); + + /** Makes it so if the user changes the value via this GUI element, the value of the preference + stored in memory and in the database will be correspondingly updated. Also makes it so if + a different GUI element updates this preference value, or #setPreferenceValue is called for + this preference, this widgets state will be correspondingly updated as well. + + However, the signals you have hooked up to this widget wont be called when the value changes by + another widget or #setPreferenceValue, to handle this case, use the #addCallbackWhenChanged + function to add a callback for whenever the value of the preference changes. + */ + static void associateWidget( const std::string &name, + Wt::WCheckBox *cb, + InterSpec *viewer ); + + /* + static void associateWidget( Wt::Dbo::ptr user, + const std::string &name, + Wt::WSpinBox *spinner, + InterSpec *viewer ); + static void associateWidget( Wt::Dbo::ptr user, + const std::string &name, + Wt::WDoubleSpinBox *spinner, + InterSpec *viewer ); + */ + + //userOptionsToXml(): serializes the current user options to a node called + // under parent passed in, with each preferences being placed + // in a node under it. Uses same schema as 'default_preferences.xml'. + //Returns the node. + rapidxml::xml_node *userOptionsToXml( rapidxml::xml_node *parent, + InterSpec *viewer ) const; + + //restoreUserPrefsFromXml(): sets the currently active user preferences from + // passed in XML node. 'preferences' must be a node named + // and should be in same format that userOptionsToXml() creates. + // If there is a current user option that is not in the passd in XML, its + // value will not be altered. Any new options in the XML not currently in + // memory will be set. + //Throws exception upon error. + static void restoreUserPrefsFromXml( const rapidxml::xml_node *preferences, + InterSpec *viewer ); + +protected: + + static void setPreferenceValueInternal( const std::string &name, + const std::string &value, + InterSpec *viewer ); + + /** Similar to `setPreferenceValueInternal(string,string,InterSpec)`, but for boolean + preferences, and also calls functions in `UserPreferences::m_onBoolChangeSignals`. + */ + static void setPreferenceValueInternal( const std::string &name, + const bool &value, + InterSpec *viewer ); + + /** Similar to `setPreferenceValueInternal(string,string,InterSpec)`, but for integer preferences. */ + static void setPreferenceValueInternal( const std::string &name, + const int &value, + InterSpec *viewer ); + + /** Similar to `setPreferenceValueInternal(string,string,InterSpec)`, but for double preferences. */ + static void setPreferenceValueInternal( const std::string &name, + const double &value, + InterSpec *viewer ); + + static void setPreferenceValueWorker( const std::string &name, + std::string value_as_string, + InterSpec *viewer ); + +protected: + InterSpec *m_interspec; + std::map> m_options; + + /** Holds callbacks set from #addCallbackWhenChanged, for boolean preferences. + + I believe using a Wt::Signals::signal allows makes it so we can connect widget slots (function + calls), and then the connection will automatically get deleted when the widget is deleted, making + things safe. + + Note that these function may be "safe" for widgets getting deleted (e.g., signals automatically + disconnected), if the signal is connected to an object deriving from Wt::WObject. + + Currently only boolean preferences require callbacks in InterSpec; in the future, if other + preference types (string, int, doubles) need callbacks, we will have to re-factor things, or + add in analogous member variables. + */ + std::map>> m_onBoolChangeSignals; +};//class UserPreferences + + + +template +T UserPreferences::preferenceValue( const std::string &name, + InterSpec *viewer ) +{ + boost::any value = UserPreferences::preferenceValueAny( name, viewer ); + return boost::any_cast( value ); +}//preferenceValue(...) + + +template +typename std::enable_if< + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value + >::type +UserPreferences::setPreferenceValue( const std::string &name, const T& value, InterSpec *viewer ) +{ + setPreferenceValueInternal( name, value, viewer ); +} + +template +void UserPreferences::addCallbackWhenChanged( const std::string &name, const T &fcn ) +{ + //Make sure a valid bool preference + preferenceValue( name, m_interspec ); + + std::shared_ptr> &signal = m_onBoolChangeSignals[name]; + if( !signal ) + signal = std::make_shared>(); + signal->connect( fcn ); +}//addCallbackWhenChanged(...) + + +template +void UserPreferences::addCallbackWhenChanged( const std::string &name, + T *target, void (V::*method)(bool) ) +{ + //Make sure a valid bool preference + preferenceValue( name, m_interspec ); + + // Retrieve (or create) the signal, and connect things up + std::shared_ptr> &signal = m_onBoolChangeSignals[name]; + if( !signal ) + signal = std::make_shared>(); + + signal->connect( boost::bind(method, target, boost::placeholders::_1) ); +}//addCallbackWhenChanged(...) + +#endif //UserPrefernces_h diff --git a/src/BatchActivity.cpp b/src/BatchActivity.cpp index 4af43b1b..74272301 100644 --- a/src/BatchActivity.cpp +++ b/src/BatchActivity.cpp @@ -52,6 +52,7 @@ #include "InterSpec/InterSpecApp.h" #include "InterSpec/BatchActivity.h" #include "InterSpec/PhysicalUnits.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/DecayDataBaseServer.h" #include "InterSpec/DetectorPeakResponse.h" #include "InterSpec/GammaInteractionCalc.h" @@ -408,7 +409,7 @@ void fit_activities_in_files( const std::string &exemplar_filename, assert( (fit_results.m_result_code != BatchActivity::BatchActivityFitResult::ResultCode::Success) || fit_results.m_fit_results ); - const bool useBq = InterSpecUser::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); + const bool useBq = UserPreferences::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); shared_ptr drf = fit_results.m_options.drf_override; diff --git a/src/BatchCommandLine.cpp b/src/BatchCommandLine.cpp index d5bb943e..0c31fded 100644 --- a/src/BatchCommandLine.cpp +++ b/src/BatchCommandLine.cpp @@ -34,6 +34,7 @@ #include "InterSpec/BatchPeak.h" #include "InterSpec/BatchActivity.h" #include "InterSpec/InterSpecUser.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/BatchCommandLine.h" #include "InterSpec/DetectorPeakResponse.h" @@ -261,7 +262,7 @@ int run_batch_command( int argc, char **argv ) bool use_bq = false, hard_background_sub = false; try { - use_bq = InterSpecUser::preferenceValue( "DisplayBecquerel", nullptr ); + use_bq = UserPreferences::preferenceValue( "DisplayBecquerel", nullptr ); }catch( std::exception &) { assert( 0 ); diff --git a/src/ColorThemeWindow.cpp b/src/ColorThemeWindow.cpp index 475097a3..57ba336e 100644 --- a/src/ColorThemeWindow.cpp +++ b/src/ColorThemeWindow.cpp @@ -46,7 +46,9 @@ #include "InterSpec/ColorTheme.h" #include "InterSpec/ColorSelect.h" #include "InterSpec/SimpleDialog.h" +#include "InterSpec/DataBaseUtils.h" #include "InterSpec/InterSpecUser.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/ColorThemeWidget.h" #include "InterSpec/ColorThemeWindow.h" @@ -265,7 +267,7 @@ m_apply( nullptr ) if( !phone ) foot->addWidget( autoDarkCb ); - InterSpecUser::associateWidget(m_interspec->m_user, "AutoDarkFromOs", autoDarkCb, m_interspec); + UserPreferences::associateWidget( "AutoDarkFromOs", autoDarkCb, m_interspec ); m_save = new WPushButton( "Save", foot ); m_apply = new WPushButton( "Apply", foot ); @@ -293,7 +295,7 @@ m_apply( nullptr ) //if( currentColorTheme ) // colorThemeIndex = currentColorTheme->dbIndex; //else - // colorThemeIndex = InterSpecUser::preferenceValue("ColorThemeIndex", m_interspec); + // colorThemeIndex = UserPreferences::preferenceValue("ColorThemeIndex", m_interspec); for( unique_ptr &p : defaultThemes ) { @@ -415,7 +417,7 @@ void ColorThemeWindow::removeThemeCallback() std::shared_ptr sql = m_interspec->sql(); DataBaseUtils::DbTransaction transaction( *sql ); - Dbo::ptr dbentry = m_interspec->m_user->colorThemes().find().where( "id = ?" ).bind( dbIndex ); + Dbo::ptr dbentry = m_interspec->user()->colorThemes().find().where( "id = ?" ).bind( dbIndex ); dbentry.remove(); transaction.commit(); }catch( Wt::Dbo::Exception & ) @@ -615,7 +617,7 @@ unique_ptr ColorThemeWindow::saveThemeToDb( const ColorTheme *theme unique_ptr newTheme; string errormsg; - Dbo::ptr &user = m_interspec->m_user; + const Dbo::ptr &user = m_interspec->user(); std::shared_ptr sql = m_interspec->sql(); if( !theme || !user || !sql ) @@ -769,8 +771,7 @@ void ColorThemeWindow::applyCallback() const ColorTheme *theme = item->theme(); assert( theme ); //def shouldnt ever happen - Dbo::ptr &user = m_interspec->m_user; - InterSpecUser::setPreferenceValue( user, "ColorThemeIndex", static_cast(theme->dbIndex), m_interspec ); + UserPreferences::setPreferenceValue( "ColorThemeIndex", static_cast(theme->dbIndex), m_interspec ); m_interspec->applyColorTheme( make_shared(*theme) ); @@ -796,14 +797,14 @@ std::vector> ColorThemeWindow::userDbThemes() try { - Dbo::ptr &user = m_interspec->m_user; + const Dbo::ptr &user = m_interspec->user(); std::shared_ptr sql = m_interspec->sql(); if (!user || !sql) throw runtime_error("Invalid database ish"); DataBaseUtils::DbTransaction transaction(*sql); - user.reread(); + m_interspec->reReadUserInfoFromDb(); const Wt::Dbo::collection< Wt::Dbo::ptr > &userthemes = user->colorThemes(); diff --git a/src/CompactFileManager.cpp b/src/CompactFileManager.cpp index 1febf8e4..b10bf989 100644 --- a/src/CompactFileManager.cpp +++ b/src/CompactFileManager.cpp @@ -65,6 +65,7 @@ #include "InterSpec/PhysicalUnits.h" #include "InterSpec/WarningWidget.h" #include "InterSpec/SpecMeasManager.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/SpectraFileModel.h" #include "InterSpec/NativeFloatSpinBox.h" #include "InterSpec/CompactFileManager.h" @@ -112,7 +113,7 @@ CompactFileManager::CompactFileManager( SpecMeasManager *fileManager, std::shared_ptr sql = hostViewer->sql(); #endif - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", hostViewer ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", hostViewer ); WTabWidget *tabWidget = nullptr; WGridLayout *tabbedLayout = nullptr; @@ -1093,7 +1094,7 @@ void CompactFileManager::updateSummaryTable( SpecUtils::SpectrumType type, label->setToolTip( WString::tr("cfm-tt-meas-type") ); val->setToolTip( WString::tr("cfm-tt-meas-type") ); - //const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", hostViewer ); + //const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", hostViewer ); //HelpSystem::attachToolTipOn( {label, val}, WString::tr("cfm-tt-meas-type"), // showToolTips, HelpSystem::ToolTipPosition::Right ); }//if( a single sample, and spectrum type is marked ) diff --git a/src/D3TimeChart.cpp b/src/D3TimeChart.cpp index c5d04bda..d4ba757d 100644 --- a/src/D3TimeChart.cpp +++ b/src/D3TimeChart.cpp @@ -52,6 +52,7 @@ #include "InterSpec/ColorTheme.h" #include "InterSpec/D3TimeChart.h" #include "InterSpec/UndoRedoManager.h" +#include "InterSpec/UserPreferences.h" using namespace Wt; using namespace std; @@ -297,7 +298,7 @@ class D3TimeChartFilters : public WContainerWidget m_normalizeCb->addStyleClass("DoNormCb"); m_normalizeCb->hide(); - const bool showToolTips = InterSpecUser::preferenceValue("ShowTooltips", InterSpec::instance()); + const bool showToolTips = UserPreferences::preferenceValue("ShowTooltips", InterSpec::instance()); const char* tooltip = "Allows you to select an energy to normalize the filtered energy range of interest to.
" "i.e., for each time slice, the sum of gammas in the filtered energy range becomes the numerator, and the sum of gammas in" " the normalize range becomes the denominator.
" diff --git a/src/DbFileBrowser.cpp b/src/DbFileBrowser.cpp index 86cfc6d6..fc9b65c5 100644 --- a/src/DbFileBrowser.cpp +++ b/src/DbFileBrowser.cpp @@ -289,7 +289,7 @@ size_t SnapshotBrowser::num_saved_states( InterSpec *viewer, { DataBaseUtils::DbTransaction transaction( *session ); - Dbo::collection< Dbo::ptr > query = SnapshotBrowser::get_user_states_collection( viewer->m_user, session, header ); + Dbo::collection< Dbo::ptr > query = SnapshotBrowser::get_user_states_collection( viewer->user(), session, header ); num_states = query.size(); @@ -361,7 +361,7 @@ SnapshotBrowser::SnapshotBrowser( SpecMeasManager *manager, try { - Dbo::ptr user = m_viewer->m_user; + Dbo::ptr user = m_viewer->user(); WContainerWidget* tablecontainer = new WContainerWidget(); m_snapshotTable = new WTree(); @@ -387,7 +387,7 @@ SnapshotBrowser::SnapshotBrowser( SpecMeasManager *manager, DataBaseUtils::DbTransaction transaction( *m_session ); - Dbo::collection< Dbo::ptr > query = SnapshotBrowser::get_user_states_collection( m_viewer->m_user, m_session, m_header ); + Dbo::collection< Dbo::ptr > query = SnapshotBrowser::get_user_states_collection( user, m_session, m_header ); m_nrows = static_cast( query.size() ); //save for future query diff --git a/src/DbStateBrowser.cpp b/src/DbStateBrowser.cpp index 31315ca6..64e6f88d 100644 --- a/src/DbStateBrowser.cpp +++ b/src/DbStateBrowser.cpp @@ -64,7 +64,7 @@ DbStateBrowser::DbStateBrowser( InterSpec *viewer, bool testStatesOnly ) try { WGridLayout *layout = stretcher(); - Dbo::ptr user = m_viewer->m_user; + const Dbo::ptr &user = m_viewer->user(); m_table = new RowStretchTreeView(); m_table->setRootIsDecorated( false ); //makes the tree look like a table! :) diff --git a/src/DecayActivityDiv.cpp b/src/DecayActivityDiv.cpp index 31ca3986..af2c852d 100644 --- a/src/DecayActivityDiv.cpp +++ b/src/DecayActivityDiv.cpp @@ -78,6 +78,7 @@ #include "InterSpec/WarningWidget.h" #include "InterSpec/PhysicalUnits.h" #include "InterSpec/DecayChainChart.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/DecayActivityDiv.h" #include "InterSpec/DecayDataBaseServer.h" #include "InterSpec/MassAttenuationTool.h" @@ -809,7 +810,7 @@ class DateLengthCalculator : public WContainerWidget double entered_activity = nucinfo.activity; const bool useCurie = nucinfo.useCurie; - //const bool useCurie = !InterSpecUser::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); + //const bool useCurie = !UserPreferences::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); if( timeSpan < 0.0 ) { @@ -1877,7 +1878,7 @@ void DecayActivityDiv::handleAppUrl( std::string path, std::string query_str ) }; const SandiaDecay::SandiaDecayDataBase *db = DecayDataBaseServer::database(); - bool useBq = InterSpecUser::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); + bool useBq = UserPreferences::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); clearAllNuclides(); @@ -2144,7 +2145,7 @@ Wt::WContainerWidget *DecayActivityDiv::initDisplayOptionWidgets() m_displayActivityUnitsCombo->addItem( WString::tr("dad-arbitrary") ); - const bool useBq = InterSpecUser::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); + const bool useBq = UserPreferences::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); m_displayActivityUnitsCombo->setCurrentIndex( (useBq ? 2 : 7) ); //MBq : mCi @@ -2160,7 +2161,7 @@ Wt::WContainerWidget *DecayActivityDiv::initDisplayOptionWidgets() m_displayTimeLength->enterPressed().connect( boost::bind( &DecayActivityDiv::refreshDecayDisplay, this, true ) ); - const bool showToolTips = m_viewer ? InterSpecUser::preferenceValue( "ShowTooltips", m_viewer ) : false; + const bool showToolTips = m_viewer ? UserPreferences::preferenceValue( "ShowTooltips", m_viewer ) : false; if( m_viewer ) HelpSystem::attachToolTipOn( m_displayTimeLength, WString::tr("dad-tt-age"), showToolTips ); @@ -2881,7 +2882,7 @@ WContainerWidget *DecayActivityDiv::isotopesSummary( const double time ) const return nuc.useCurie; } - return !InterSpecUser::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); + return !UserPreferences::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); }; infostrm << "
Starting from:
"; diff --git a/src/DecayChainChart.cpp b/src/DecayChainChart.cpp index f7b8ae4a..20d4a340 100644 --- a/src/DecayChainChart.cpp +++ b/src/DecayChainChart.cpp @@ -66,6 +66,7 @@ #include "InterSpec/InterSpecUser.h" #include "InterSpec/PhysicalUnits.h" #include "InterSpec/DecayChainChart.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/DecayActivityDiv.h" #include "InterSpec/DecayDataBaseServer.h" #include "InterSpec/PhysicalUnitsLocalized.h" @@ -355,7 +356,7 @@ pair InterSpec *viewer = InterSpec::instance(); assert( viewer ); - const bool useBq = InterSpecUser::preferenceValue( "DisplayBecquerel", viewer ); + const bool useBq = UserPreferences::preferenceValue( "DisplayBecquerel", viewer ); viewer->colorThemeChanged(); viewer->colorThemeChanged().connect( chart, &DecayChainChart::colorThemeChanged ); diff --git a/src/DecaySelectNuclideDiv.cpp b/src/DecaySelectNuclideDiv.cpp index 756d9173..2d52c38f 100644 --- a/src/DecaySelectNuclideDiv.cpp +++ b/src/DecaySelectNuclideDiv.cpp @@ -53,6 +53,7 @@ #include "InterSpec/InterSpec.h" #include "InterSpec/InterSpecUser.h" #include "InterSpec/PhysicalUnits.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/DecayActivityDiv.h" #include "InterSpec/DecayDataBaseServer.h" #include "InterSpec/DecaySelectNuclideDiv.h" @@ -369,7 +370,7 @@ void DecaySelectNuclide::initActivityAgeSelects() m_nuclideActivityEdit->setValidator( actvalidator ); m_nuclideActivityEdit->setTextSize( 10 ); - const bool useCi = !InterSpecUser::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); + const bool useCi = !UserPreferences::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); m_nuclideActivityEdit->setText( useCi ? "1 uCi" : "37 kBq" ); m_nuclideAgeEdit->setText( "0.0 us" ); @@ -495,7 +496,7 @@ void DecaySelectNuclide::emitAccepted() const string::size_type unitpos = activityTxt.find_first_of( "CcBb" ); if( unitpos == string::npos ) - selected.useCurie = !InterSpecUser::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); + selected.useCurie = !UserPreferences::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); else if( activityTxt[unitpos]=='C' || activityTxt[unitpos]=='c' ) selected.useCurie = true; else diff --git a/src/DecayWindow.cpp b/src/DecayWindow.cpp index 5fd8e22c..98d67b71 100644 --- a/src/DecayWindow.cpp +++ b/src/DecayWindow.cpp @@ -43,6 +43,7 @@ #include "InterSpec/InterSpec.h" #include "InterSpec/DecayWindow.h" #include "InterSpec/PhysicalUnits.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/DecayActivityDiv.h" #include "InterSpec/DecayDataBaseServer.h" @@ -74,7 +75,7 @@ DecayWindow::DecayWindow( InterSpec *viewer ) layout->setHorizontalSpacing( 0 ); layout->setRowStretch( 0, 1 ); - const bool useBq = InterSpecUser::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); + const bool useBq = UserPreferences::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); const double actUnits = useBq ? PhysicalUnits::MBq : PhysicalUnits::microCi; const string actStr = useBq ? "1 MBq" : "1 uCi"; m_activityDiv->addNuclide( 53, 135, 0, 1.0*actUnits, !useBq, 0.0, actStr); diff --git a/src/DetectionLimitSimple.cpp b/src/DetectionLimitSimple.cpp index 0b6a1adf..2dabc6a8 100644 --- a/src/DetectionLimitSimple.cpp +++ b/src/DetectionLimitSimple.cpp @@ -66,6 +66,7 @@ #include "InterSpec/ShieldingSelect.h" #include "InterSpec/SpecMeasManager.h" #include "InterSpec/UndoRedoManager.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/DetectionLimitCalc.h" #include "InterSpec/DetectionLimitTool.h" #include "InterSpec/NativeFloatSpinBox.h" @@ -94,7 +95,7 @@ namespace if( !interspec ) return true; - return !InterSpecUser::preferenceValue( "DisplayBecquerel", interspec ); + return !UserPreferences::preferenceValue( "DisplayBecquerel", interspec ); }//bool use_curie_units() }//namespace @@ -244,7 +245,7 @@ void DetectionLimitSimple::init() addStyleClass( "DetectionLimitSimple" ); - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", m_viewer ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", m_viewer ); WContainerWidget *resultsDiv = new WContainerWidget( this ); resultsDiv->addStyleClass( "ResultsArea" ); diff --git a/src/DetectionLimitTool.cpp b/src/DetectionLimitTool.cpp index 086434cc..d5137973 100644 --- a/src/DetectionLimitTool.cpp +++ b/src/DetectionLimitTool.cpp @@ -90,6 +90,7 @@ #include "InterSpec/MakeFwhmForDrf.h" #include "InterSpec/SwitchCheckbox.h" #include "InterSpec/ShieldingSelect.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/SpectraFileModel.h" #include "InterSpec/ReferenceLineInfo.h" #include "InterSpec/DetectionLimitTool.h" @@ -119,7 +120,7 @@ bool use_curie_units() if( !interspec ) return true; - return !InterSpecUser::preferenceValue( "DisplayBecquerel", interspec ); + return !UserPreferences::preferenceValue( "DisplayBecquerel", interspec ); }//bool use_curie_units() }//namespace @@ -707,7 +708,7 @@ class MdaPeakRow : public WContainerWidget m_decon_cont_norm_method->addItem( "Fixed full ROI" ); m_decon_cont_norm_method->setCurrentIndex( static_cast(m_input.decon_cont_norm_method) ); - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", InterSpec::instance() ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", InterSpec::instance() ); const char *tooltip = "How the continuum normalization should be determined:" "
  • Floating: The polynomial continuum is fit for, at each given activity -" " the activity affects the continuum.
  • " @@ -814,7 +815,7 @@ class MdaPeakRow : public WContainerWidget //Right now we are just having DetectionLimitTool completely refresh on activity units change, // but we could be a little more fine-grained about this. //InterSpec *viewer = InterSpec::instance(); - //InterSpecUser::addCallbackWhenChanged( viewer->m_user, viewer, "DisplayBecquerel", + //viewer->preferences()->addCallbackWhenChanged( "DisplayBecquerel", // boost::bind(&MdaPeakRow::setSimplePoisonTxt, this) ); }//MdaPeakRow constructor @@ -1143,7 +1144,7 @@ DetectionLimitTool::DetectionLimitTool( InterSpec *viewer, m_ageEdit->setAutoComplete( false ); label->setBuddy( m_ageEdit ); - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", InterSpec::instance() ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", InterSpec::instance() ); const char *tooltip = "
    The age of the nuclide.
    " "
    " @@ -1398,7 +1399,7 @@ DetectionLimitTool::DetectionLimitTool( InterSpec *viewer, handleUserNuclideChange(); // Update the displayed activity units, when the user changes this preference. - InterSpecUser::addCallbackWhenChanged( viewer->m_user, viewer, "DisplayBecquerel", + viewer->preferences()->addCallbackWhenChanged( "DisplayBecquerel", boost::bind(&DetectionLimitTool::handleInputChange, this) ); diff --git a/src/DoseCalcWidget.cpp b/src/DoseCalcWidget.cpp index 26e5c183..d43e5583 100644 --- a/src/DoseCalcWidget.cpp +++ b/src/DoseCalcWidget.cpp @@ -82,6 +82,7 @@ #include "InterSpec/GadrasSpecFunc.h" #include "InterSpec/ShieldingSelect.h" #include "InterSpec/UndoRedoManager.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/NativeFloatSpinBox.h" #include "InterSpec/NuclideSourceEnter.h" #include "InterSpec/MassAttenuationTool.h" @@ -359,8 +360,8 @@ void DoseCalcWidget::init() } const bool narrowLayout = ((w > 100) && (w < 450)); - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", m_viewer ); - const bool useBq = InterSpecUser::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", m_viewer ); + const bool useBq = UserPreferences::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); WContainerWidget *enterDiv = new WContainerWidget(); WContainerWidget *answerDiv = new WContainerWidget(); diff --git a/src/DrfSelect.cpp b/src/DrfSelect.cpp index 1a357d38..b1512666 100644 --- a/src/DrfSelect.cpp +++ b/src/DrfSelect.cpp @@ -97,6 +97,7 @@ #include "InterSpec/MakeFwhmForDrf.h" #include "InterSpec/SpecMeasManager.h" #include "InterSpec/UndoRedoManager.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/SpectraFileModel.h" #include "InterSpec/NativeFloatSpinBox.h" #include "InterSpec/RowStretchTreeView.h" @@ -1336,7 +1337,7 @@ void RelEffDetSelect::docreate() WContainerWidget *holder = new WContainerWidget( this ); holder->addStyleClass( "DrfSelectAreaFooter" ); //holder->setToolTip( WString::tr("reds-tt-add-drf-btn") ); - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", m_interspec ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", m_interspec ); HelpSystem::attachToolTipOn( holder, WString::tr("reds-tt-add-drf-btn"), showToolTips, HelpSystem::ToolTipPosition::Left ); @@ -1455,14 +1456,14 @@ void GadrasDetSelect::docreate() m_directories->addStyleClass( "DrfSelectAreaFiles" ); //Need to point the GUI to the appropriate directory, and implement to an `ls` to find detctors with both Detcotr.dat and Efficy.csv. - const string drfpaths = InterSpecUser::preferenceValue( "GadrasDRFPath", m_interspec ); + const string drfpaths = UserPreferences::preferenceValue( "GadrasDRFPath", m_interspec ); WContainerWidget *footer = new WContainerWidget( this ); footer->addStyleClass( "DrfSelectAreaFooter" ); #if( !BUILD_FOR_WEB_DEPLOYMENT && !defined(IOS) ) //footer->setToolTip( WString::tr("reds-tt-add-dir") ); - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", m_interspec ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", m_interspec ); HelpSystem::attachToolTipOn( footer, WString::tr("reds-tt-add-dir"), showToolTips, HelpSystem::ToolTipPosition::Left ); @@ -1477,7 +1478,7 @@ void GadrasDetSelect::docreate() try { if( m_interspec ) - pathstr = InterSpecUser::preferenceValue( "GadrasDRFPath", m_interspec ); + pathstr = UserPreferences::preferenceValue( "GadrasDRFPath", m_interspec ); }catch( std::exception & ) { passMessage( "Error retrieving 'GadrasDRFPath' preference.", WarningWidget::WarningMsgHigh ); @@ -1688,7 +1689,7 @@ void GadrasDetSelect::saveFilePathToUserPreferences() try { - InterSpecUser::setPreferenceValue( m_interspec->m_user, "GadrasDRFPath", dirs, m_interspec ); + UserPreferences::setPreferenceValue( "GadrasDRFPath", dirs, m_interspec ); }catch( std::exception &e ) { passMessage( WString::tr("reds-err-saving-gad-path").arg(e.what()), WarningWidget::WarningMsgHigh ); @@ -1735,7 +1736,7 @@ GadrasDirectory::GadrasDirectory( std::string directory, GadrasDetSelect *parent m_directoryEdit->setAttributeValue( "ondragstart", "return false" ); auto interspec = InterSpec::instance(); - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", interspec ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", interspec ); WContainerWidget *topdiv = new WContainerWidget( this ); @@ -2192,7 +2193,7 @@ DetectorDisplay::DetectorDisplay( InterSpec *specViewer, m_interspec( specViewer ), m_fileModel( fileModel ) { - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", specViewer ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", specViewer ); HelpSystem::attachToolTipOn( this, WString::tr("app-tt-det-disp"), showToolTips ); //"app-tt-det-disp" defined in InterSpec.xml localisation, so we dont need to load DrfSelect.xml for this widget @@ -2319,7 +2320,7 @@ DrfSelect::DrfSelect( std::shared_ptr currentDet, wApp->useStyleSheet( "InterSpec_resources/DrfSelect.css" ); m_interspec->useMessageResourceBundle( "DrfSelect" ); - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", specViewer ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", specViewer ); WGridLayout *mainLayout = new WGridLayout(); @@ -2767,7 +2768,7 @@ DrfSelect::DrfSelect( std::shared_ptr currentDet, WGridLayout* recentDivLayout = new WGridLayout( recentDiv ); recentDivLayout->setContentsMargins( 0, 0, 0, 0 ); - Dbo::ptr user = m_interspec->m_user; + const Dbo::ptr &user = m_interspec->user(); m_DBtable = new RowStretchTreeView(); m_DBtable->setRootIsDecorated ( false ); //makes the tree look like a table! :) @@ -3279,7 +3280,7 @@ void DrfSelect::createChooseDrfDialog( vector> return; auto sql = interspec->sql(); - auto user = interspec->m_user; + const Dbo::ptr &user = interspec->user(); DrfSelect::updateLastUsedTimeOrAddToDb( drf, user.id(), sql ); interspec->detectorChanged().emit( drf ); //This loads it to the foreground spectrum file @@ -3394,7 +3395,7 @@ void DrfSelect::handleFitFwhmRequested() void DrfSelect::handleFitFwhmFinished( std::shared_ptr drf ) { if( drf ) - updateLastUsedTimeOrAddToDb( drf, m_interspec->m_user.id(), m_sql ); + updateLastUsedTimeOrAddToDb( drf, m_interspec->user().id(), m_sql ); done().emit(); }//void handleFitFwhmFinished( std::shared_ptr drf ) @@ -4571,14 +4572,14 @@ void DrfSelect::setUserPrefferedDetector( std::shared_ptr void DrfSelect::acceptAndFinish() { - updateLastUsedTimeOrAddToDb( m_detector, m_interspec->m_user.id(), m_sql ); + updateLastUsedTimeOrAddToDb( m_detector, m_interspec->user().id(), m_sql ); emitChangedSignal(); //emitModifiedSignal(); auto meas = m_interspec->measurment(SpecUtils::SpectrumType::Foreground); auto sql = m_interspec->sql(); - auto user = m_interspec->m_user; + const Dbo::ptr &user = m_interspec->user(); auto drf = m_detector; if( meas && drf && m_defaultForSerialNumber && m_defaultForSerialNumber->isChecked() ) @@ -4635,7 +4636,7 @@ vector> DrfSelect::avaliableGadrasDetectors() const const string drfpaths = SpecUtils::append_path( datadir, "GenericGadrasDetectors" ) + ";" + SpecUtils::append_path( datadir, "OUO_GadrasDetectors" ); #else - const string drfpaths = InterSpecUser::preferenceValue( "GadrasDRFPath", m_interspec ); + const string drfpaths = UserPreferences::preferenceValue( "GadrasDRFPath", m_interspec ); #endif vector paths; @@ -5120,7 +5121,7 @@ std::shared_ptr DrfSelect::initAGadrasDetector( const std: const string drfpaths = SpecUtils::append_path( datadir, "GenericGadrasDetectors" ) + ";" + SpecUtils::append_path( datadir, "OUO_GadrasDetectors" ); #else - const string drfpaths = InterSpecUser::preferenceValue( "GadrasDRFPath", interspec ); + const string drfpaths = UserPreferences::preferenceValue( "GadrasDRFPath", interspec ); #endif diff --git a/src/EnergyCalGraphical.cpp b/src/EnergyCalGraphical.cpp index 473f0e61..11e02ee9 100644 --- a/src/EnergyCalGraphical.cpp +++ b/src/EnergyCalGraphical.cpp @@ -36,13 +36,15 @@ #include #include -#include "InterSpec/SpecMeas.h" #include "SpecUtils/SpecFile.h" +#include "SpecUtils/EnergyCalibration.h" + +#include "InterSpec/SpecMeas.h" #include "InterSpec/AuxWindow.h" #include "InterSpec/InterSpec.h" #include "InterSpec/HelpSystem.h" #include "InterSpec/EnergyCalTool.h" -#include "SpecUtils/EnergyCalibration.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/EnergyCalGraphical.h" #include "InterSpec/NativeFloatSpinBox.h" @@ -133,7 +135,7 @@ EnergyCalGraphicalConfirm::EnergyCalGraphicalConfirm( double lowe, double highe, m_preserveLastCal->setInline( false ); m_preserveLastCal->setStyleClass( "PreserveLastCalCb" ); - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", viewer ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", viewer ); HelpSystem::attachToolTipOn( m_preserveLastCal,"This is only possible if a offset or" " linear term adjustment was previously" diff --git a/src/EnergyCalTool.cpp b/src/EnergyCalTool.cpp index 3db3fcf8..846bc528 100644 --- a/src/EnergyCalTool.cpp +++ b/src/EnergyCalTool.cpp @@ -65,6 +65,7 @@ #include "InterSpec/WarningWidget.h" #include "InterSpec/SpecMeasManager.h" #include "InterSpec/UndoRedoManager.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/SpectraFileModel.h" #include "InterSpec/NativeFloatSpinBox.h" #include "InterSpec/EnergyCalGraphical.h" @@ -1494,7 +1495,7 @@ void EnergyCalTool::initWidgets( EnergyCalTool::LayoutType layoutType ) m_uploadCALp = nullptr; #endif - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", m_interspec ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", m_interspec ); m_layout->setContentsMargins( 0, 0, 0, 0 ); m_layout->setVerticalSpacing( 0 ); @@ -4381,7 +4382,7 @@ void EnergyCalTool::deleteGraphicalRecalConfirmWindow() m_graphicalRecal = nullptr; }//if( m_graphicalRecal ) - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", m_interspec ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", m_interspec ); if( showToolTips ) { m_interspec->logMessage( WString::tr("ect-del-graphical-msg"), 1 ); diff --git a/src/ExportSpecFile.cpp b/src/ExportSpecFile.cpp index 0ad0807f..78eecb68 100644 --- a/src/ExportSpecFile.cpp +++ b/src/ExportSpecFile.cpp @@ -74,6 +74,7 @@ #include "InterSpec/ExportSpecFile.h" #include "InterSpec/SpecMeasManager.h" #include "InterSpec/UndoRedoManager.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/SpectraFileModel.h" #include "InterSpec/PhysicalUnitsLocalized.h" @@ -740,7 +741,7 @@ void ExportSpecFileTool::init() m_interspec->saveRelActManualStateToForegroundSpecMeas(); #endif - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", m_interspec ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", m_interspec ); const bool isMobile = m_interspec && m_interspec->isMobile(); if( isMobile ) diff --git a/src/FluxTool.cpp b/src/FluxTool.cpp index ec558fda..cb83e55c 100644 --- a/src/FluxTool.cpp +++ b/src/FluxTool.cpp @@ -66,6 +66,7 @@ #include "InterSpec/PhysicalUnits.h" #include "InterSpec/SpecMeasManager.h" #include "InterSpec/UndoRedoManager.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/SpectraFileModel.h" #include "InterSpec/RowStretchTreeView.h" @@ -1082,7 +1083,7 @@ void FluxToolWidget::init() wApp->useStyleSheet( "InterSpec_resources/FluxTool.css" ); - const bool showToolTips = m_interspec ? InterSpecUser::preferenceValue( "ShowTooltips", m_interspec ) : false; + const bool showToolTips = m_interspec ? UserPreferences::preferenceValue( "ShowTooltips", m_interspec ) : false; addStyleClass( "FluxToolWidget" ); diff --git a/src/GammaCountDialog.cpp b/src/GammaCountDialog.cpp index 98864337..ac25d245 100644 --- a/src/GammaCountDialog.cpp +++ b/src/GammaCountDialog.cpp @@ -50,6 +50,7 @@ #include "InterSpec/InterSpecApp.h" #include "InterSpec/SpectrumChart.h" #include "InterSpec/UndoRedoManager.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/GammaCountDialog.h" #include "InterSpec/NativeFloatSpinBox.h" @@ -141,7 +142,7 @@ void GammaCountDialog::init() wApp->useStyleSheet( "InterSpec_resources/GammaCountDialog.css" ); wApp->useStyleSheet( "InterSpec_resources/GridLayoutHelpers.css" ); - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", m_specViewer ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", m_specViewer ); if( !m_specViewer ) throw runtime_error( "GammaCountDialog: you must pass in valid InterSpec pointer" ); diff --git a/src/GammaXsGui.cpp b/src/GammaXsGui.cpp index aa43a9a7..18c297f1 100644 --- a/src/GammaXsGui.cpp +++ b/src/GammaXsGui.cpp @@ -49,6 +49,7 @@ #include "InterSpec/PhysicalUnits.h" #include "InterSpec/SpecMeasManager.h" #include "InterSpec/UndoRedoManager.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/MassAttenuationTool.h" #include "InterSpec/DecayDataBaseServer.h" @@ -104,7 +105,7 @@ GammaXsGui::GammaXsGui( MaterialDB *materialDB, m_layout = new WGridLayout(); setLayout( m_layout ); - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", m_specViewer ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", m_specViewer ); m_specViewer->useMessageResourceBundle( "GammaXsGui" ); diff --git a/src/InterSpec.cpp b/src/InterSpec.cpp index ae655f86..9f2cef9d 100644 --- a/src/InterSpec.cpp +++ b/src/InterSpec.cpp @@ -123,6 +123,7 @@ #include "InterSpec/SpecMeasManager.h" #include "InterSpec/SpecFileSummary.h" #include "InterSpec/UndoRedoManager.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/AddNewPeakDialog.h" #include "InterSpec/ColorThemeWindow.h" #include "InterSpec/GammaCountDialog.h" @@ -378,6 +379,8 @@ namespace InterSpec::InterSpec( WContainerWidget *parent ) : WContainerWidget( parent ), + m_user{}, + m_preferences( nullptr ), m_peakModel( 0 ), m_spectrum( 0 ), m_timeSeries( 0 ), @@ -522,7 +525,7 @@ InterSpec::InterSpec( WContainerWidget *parent ) addStyleClass( "InterSpec" ); //Setting setLayoutSizeAware doesnt seem to add any appreciable network - // overhead, at least according to the chrome inspectrion panel when running + // overhead, at least according to the chrome inspection panel when running // locally setLayoutSizeAware( true ); @@ -600,6 +603,8 @@ InterSpec::InterSpec( WContainerWidget *parent ) m_user = m_sql->session()->add( newuser ); }//if( m_user ) / else + m_preferences = new UserPreferences( this ); + m_user.modify()->startingNewSession(); transaction.commit(); @@ -607,7 +612,7 @@ InterSpec::InterSpec( WContainerWidget *parent ) detectClientDeviceType(); - const string langPref = InterSpecUser::preferenceValue("Language", this); + const string langPref = UserPreferences::preferenceValue("Language", this); if( !langPref.empty() ) wApp->setLocale( WLocale(langPref) ); @@ -621,7 +626,7 @@ InterSpec::InterSpec( WContainerWidget *parent ) std::unique_ptr undo_blocker; if( (UndoRedoManager::maxUndoRedoSteps() >= 0) /* && !app->isPhone() - && (!app->isTablet() || InterSpecUser::preferenceValue("TabletUseDesktopMenus", this)) */ ) + && (!app->isTablet() || UserPreferences::preferenceValue("TabletUseDesktopMenus", this)) */ ) { m_undo = new UndoRedoManager( this ); undo_blocker = std::unique_ptr(); @@ -1625,7 +1630,7 @@ void InterSpec::layoutSizeChanged( int w, int h ) // makes screen large, currently wont un-compactify axis, even if // user wants this; should evaluate if its worth checking the user // preference about this. - //const bool makeCompact = InterSpecUser::preferenceValue( "CompactXAxis", this ); + //const bool makeCompact = UserPreferences::preferenceValue( "CompactXAxis", this ); if( comactX && !m_spectrum->isAxisCompacted() ) m_spectrum->setCompactAxis( comactX ); @@ -1678,7 +1683,7 @@ void InterSpec::detectClientDeviceType() bool tablet = app->isTablet(); bool mobile = app->isMobile(); if( mobile && !phone && tablet ) - tablet = mobile = !InterSpecUser::preferenceValue("TabletUseDesktopMenus", this); + tablet = mobile = !UserPreferences::preferenceValue("TabletUseDesktopMenus", this); for( ClientDeviceType type= ClientDeviceType( 0x1 ); type < NumClientDeviceType; type= ClientDeviceType( type << 1 ) ) @@ -1725,7 +1730,7 @@ void InterSpec::detectClientDeviceType() void InterSpec::changeLocale( std::string languageCode ) { - const string prevLangPref = InterSpecUser::preferenceValue("Language", this); + const string prevLangPref = UserPreferences::preferenceValue("Language", this); //if( prevLangPref == local ) // return; @@ -1737,7 +1742,7 @@ void InterSpec::changeLocale( std::string languageCode ) return; }//if( !languages.count(languageCode) ) - InterSpecUser::setPreferenceValue( m_user, "Language", languageCode, this ); + UserPreferences::setPreferenceValue( "Language", languageCode, this ); // Add the style class to the item selected if( m_languagesSubMenu ) @@ -3440,10 +3445,10 @@ void InterSpec::saveStateToDb( Wt::Dbo::ptr entry ) try { - if( m_user->preferenceValue( "ShowVerticalGridlines", this ) ) + if( UserPreferences::preferenceValue( "ShowVerticalGridlines", this ) ) entry.modify()->shownDisplayFeatures |= UserState::kVerticalGridLines; - if( m_user->preferenceValue( "ShowHorizontalGridlines", this ) ) + if( UserPreferences::preferenceValue( "ShowHorizontalGridlines", this ) ) entry.modify()->shownDisplayFeatures |= UserState::kHorizontalGridLines; }catch(...) { @@ -3658,7 +3663,7 @@ void InterSpec::saveStateToDb( Wt::Dbo::ptr entry ) try { Wt::Dbo::ptr theme; - const int colorThemIndex = m_user->preferenceValue("ColorThemeIndex", this); + const int colorThemIndex = UserPreferences::preferenceValue("ColorThemeIndex", this); if( colorThemIndex > 0 ) theme = m_user->m_colorThemes.find().where( "id = ?" ).bind( colorThemIndex ).resultValue(); if( theme ) @@ -3842,7 +3847,7 @@ void InterSpec::loadStateFromDb( Wt::Dbo::ptr entry ) //All of this is really ugly and horrible, and should be improved! // const bool autosave -// = InterSpecUser::preferenceValue( "CheckForPrevOnSpecLoad", this ); +// = UserPreferences::preferenceValue( "CheckForPrevOnSpecLoad", this ); // if( autosave ) // { // Its actually to late, HEAD will be set to the tag version even if the @@ -4391,28 +4396,28 @@ void InterSpec::loadStateFromDb( Wt::Dbo::ptr entry ) case UserOption::String: { const std::string value = obj.get("value"); - InterSpecUser::setPreferenceValue( m_user, name, value, this ); + UserPreferences::setPreferenceValue( name, value, this ); break; }//case UserOption::Boolean: case UserOption::Decimal: { const double value = obj.get("value"); - InterSpecUser::setPreferenceValue( m_user, name, value, this ); + UserPreferences::setPreferenceValue( name, value, this ); break; }//case UserOption::Boolean: case UserOption::Integer: { const int value = obj.get("value"); - InterSpecUser::setPreferenceValue( m_user, name, value, this ); + UserPreferences::setPreferenceValue( name, value, this ); break; }//case UserOption::Boolean: case UserOption::Boolean: { const bool value = obj.get("value"); - InterSpecUser::setPreferenceValue( m_user, name, value, this ); + UserPreferences::setPreferenceValue( name, value, this ); }//case UserOption::Boolean: }//switch( datatype ) }//for( size_t i = 0; i < userOptions.size(); ++i ) @@ -4534,7 +4539,7 @@ std::shared_ptr InterSpec::getColorTheme() try { - const int colorThemIndex = m_user->preferenceValue("ColorThemeIndex", this); + const int colorThemIndex = UserPreferences::preferenceValue("ColorThemeIndex", this); if( colorThemIndex < 0 ) { auto deftheme = ColorTheme::predefinedTheme( ColorTheme::PredefinedColorTheme(-colorThemIndex) ); @@ -4643,7 +4648,7 @@ void InterSpec::osThemeChange( std::string name ) try { - const bool autoDark = InterSpecUser::preferenceValue( "AutoDarkFromOs", this ); + const bool autoDark = UserPreferences::preferenceValue( "AutoDarkFromOs", this ); if( autoDark && (name == "dark") ) { @@ -4734,6 +4739,22 @@ void InterSpec::startClearSession() }//void startClearSession() +UserPreferences *InterSpec::preferences() +{ + return m_preferences; +} + + +const Wt::Dbo::ptr &InterSpec::user() +{ + return m_user; +} + +void InterSpec::reReadUserInfoFromDb() +{ + m_user.reread(); +} + void InterSpec::deleteLicenseAndDisclaimersWindow() { if( !m_licenseWindow ) @@ -4761,7 +4782,7 @@ void InterSpec::showWelcomeDialogWorker( const bool force ) if( !force ) { dontShowAgainCallback = [this](bool value){ - InterSpecUser::setPreferenceValue( m_user, "ShowSplashScreen", value, this ); + UserPreferences::setPreferenceValue( "ShowSplashScreen", value, this ); }; }//if( !force ) @@ -4792,7 +4813,7 @@ void InterSpec::showWelcomeDialog( const bool force ) try { - if( !force && !m_user->preferenceValue("ShowSplashScreen", this) ) + if( !force && !UserPreferences::preferenceValue("ShowSplashScreen", this) ) return; }catch(...) { @@ -5339,7 +5360,7 @@ void InterSpec::storeTestStateToN42( std::ostream &output, InterSpecNode->append_attribute( attr ); //Add user options into the XML - m_user->userOptionsToXml( InterSpecNode, this ); + preferences()->userOptionsToXml( InterSpecNode, this ); if( newbacksamples.size() ) { @@ -5464,7 +5485,7 @@ void InterSpec::loadTestStateFromN42( const std::string filename ) const xml_node *preferences = InterSpecNode->first_node( "preferences" ); if( preferences ) - InterSpecUser::restoreUserPrefsFromXml( m_user, preferences, this ); + UserPreferences::restoreUserPrefsFromXml( preferences, this ); else cerr << "Warning, couldnt find preferences in the XML" " when restoring state from XML" << endl; @@ -6008,7 +6029,7 @@ void InterSpec::addFileMenu( WWidget *parent, const bool isAppTitlebar ) return; const bool mobile = isMobile(); - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", this ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", this ); PopupDivMenu *parentMenu = dynamic_cast( parent ); WContainerWidget *menuDiv = dynamic_cast( parent ); @@ -6349,7 +6370,7 @@ void InterSpec::addToolsTabToMenuItems() try { - showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", this ); + showToolTips = UserPreferences::preferenceValue( "ShowTooltips", this ); }catch(...) { } @@ -6819,7 +6840,7 @@ void InterSpec::setToolTabsVisible( bool showToolTabs ) }else { // The phone is landscape, restore showing scaler to user preference - const bool showScalers = InterSpecUser::preferenceValue( "ShowYAxisScalers", this ); + const bool showScalers = UserPreferences::preferenceValue( "ShowYAxisScalers", this ); m_showYAxisScalerItems[0]->setHidden( showScalers ); m_showYAxisScalerItems[1]->setHidden( !showScalers ); m_spectrum->showYAxisScalers( showScalers ); @@ -6865,7 +6886,7 @@ void InterSpec::addViewMenu( WWidget *parent ) throw runtime_error( "InterSpec::addViewMenu(): parent passed in" " must be a PopupDivMenu or WContainerWidget" ); - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", this ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", this ); if( menuDiv ) { @@ -6902,7 +6923,7 @@ void InterSpec::addViewMenu( WWidget *parent ) "InterSpec_resources/images/spec_settings_small.png"); bool logypref = true; - try{ logypref = m_user->preferenceValue( "LogY", this ); }catch(...){} + try{ logypref = UserPreferences::preferenceValue( "LogY", this ); }catch(...){} m_logYItems[0] = chartmenu->addMenuItem( WString::tr("app-mi-view-logy") ); m_logYItems[1] = chartmenu->addMenuItem( WString::tr("app-mi-view-liny") ); @@ -6911,10 +6932,9 @@ void InterSpec::addViewMenu( WWidget *parent ) m_logYItems[0]->triggered().connect( boost::bind( &InterSpec::setLogY, this, true ) ); m_logYItems[1]->triggered().connect( boost::bind( &InterSpec::setLogY, this, false ) ); m_spectrum->setYAxisLog( logypref ); - InterSpecUser::addCallbackWhenChanged( m_user, this, "LogY", this, &InterSpec::setLogY ); - + m_preferences->addCallbackWhenChanged( "LogY", this, &InterSpec::setLogY ); - const bool verticleLines = InterSpecUser::preferenceValue( "ShowVerticalGridlines", this ); + const bool verticleLines = UserPreferences::preferenceValue( "ShowVerticalGridlines", this ); m_verticalLinesItems[0] = chartmenu->addMenuItem( WString::tr("app-mi-view-show-vert"), "InterSpec_resources/images/sc_togglegridvertical.png"); m_verticalLinesItems[1] = chartmenu->addMenuItem( WString::tr("app-mi-view-hide-vert"), @@ -6925,9 +6945,9 @@ void InterSpec::addViewMenu( WWidget *parent ) m_verticalLinesItems[1]->setHidden( !verticleLines ); m_spectrum->showVerticalLines( verticleLines ); m_timeSeries->showVerticalLines( verticleLines ); - InterSpecUser::addCallbackWhenChanged( m_user, this, "ShowVerticalGridlines", this, &InterSpec::setVerticalLines ); + m_preferences->addCallbackWhenChanged( "ShowVerticalGridlines", this, &InterSpec::setVerticalLines ); - const bool horizontalLines = InterSpecUser::preferenceValue( "ShowHorizontalGridlines", this ); + const bool horizontalLines = UserPreferences::preferenceValue( "ShowHorizontalGridlines", this ); m_horizantalLinesItems[0] = chartmenu->addMenuItem( WString::tr("app-mi-view-show-horz"), "InterSpec_resources/images/sc_togglegridhorizontal.png"); m_horizantalLinesItems[1] = chartmenu->addMenuItem( WString::tr("app-mi-view-hide-horz"), @@ -6938,7 +6958,7 @@ void InterSpec::addViewMenu( WWidget *parent ) m_horizantalLinesItems[1]->setHidden( !horizontalLines ); m_spectrum->showHorizontalLines( horizontalLines ); m_timeSeries->showHorizontalLines( horizontalLines ); - InterSpecUser::addCallbackWhenChanged( m_user, this, "ShowHorizontalGridlines", this, &InterSpec::setHorizantalLines ); + m_preferences->addCallbackWhenChanged( "ShowHorizontalGridlines", this, &InterSpec::setHorizantalLines ); if( isPhone() ) @@ -6946,7 +6966,7 @@ void InterSpec::addViewMenu( WWidget *parent ) m_compactXAxisItems[0] = m_compactXAxisItems[1] = nullptr; }else { - const bool makeCompact = InterSpecUser::preferenceValue( "CompactXAxis", this ); + const bool makeCompact = UserPreferences::preferenceValue( "CompactXAxis", this ); m_spectrum->setCompactAxis( makeCompact ); m_compactXAxisItems[0] = chartmenu->addMenuItem( WString::tr("app-mi-view-compact-x"), ""); m_compactXAxisItems[1] = chartmenu->addMenuItem( WString::tr("app-mi-view-normal-x"), ""); @@ -6954,7 +6974,7 @@ void InterSpec::addViewMenu( WWidget *parent ) m_compactXAxisItems[1]->triggered().connect( boost::bind( &InterSpec::setXAxisCompact, this, false ) ); m_compactXAxisItems[0]->setHidden( makeCompact ); m_compactXAxisItems[1]->setHidden( !makeCompact ); - InterSpecUser::addCallbackWhenChanged( m_user, this, "CompactXAxis", this, &InterSpec::setXAxisCompact ); + m_preferences->addCallbackWhenChanged( "CompactXAxis", this, &InterSpec::setXAxisCompact ); } //What we should do here is have a dialog that pops up that lets users select @@ -6984,7 +7004,7 @@ void InterSpec::addViewMenu( WWidget *parent ) addPeakLabelSubMenu( m_displayOptionsPopupDiv ); //add Peak menu - const bool showSlider = InterSpecUser::preferenceValue( "ShowXAxisSlider", this ); + const bool showSlider = UserPreferences::preferenceValue( "ShowXAxisSlider", this ); m_spectrum->showXAxisSliderChart( showSlider ); m_showXAxisSliderItems[0] = m_displayOptionsPopupDiv->addMenuItem( WString::tr("app-mi-view-show-slider"), ""); m_showXAxisSliderItems[1] = m_displayOptionsPopupDiv->addMenuItem( WString::tr("app-mi-view-hide-slider"), ""); @@ -6992,10 +7012,10 @@ void InterSpec::addViewMenu( WWidget *parent ) m_showXAxisSliderItems[1]->triggered().connect( boost::bind( &InterSpec::setXAxisSlider, this, false ) ); m_showXAxisSliderItems[0]->setHidden( showSlider ); m_showXAxisSliderItems[1]->setHidden( !showSlider ); - InterSpecUser::addCallbackWhenChanged( m_user, this, "ShowXAxisSlider", this, &InterSpec::setXAxisSlider ); + m_preferences->addCallbackWhenChanged( "ShowXAxisSlider", this, &InterSpec::setXAxisSlider ); - const bool showScalers = InterSpecUser::preferenceValue( "ShowYAxisScalers", this ); + const bool showScalers = UserPreferences::preferenceValue( "ShowYAxisScalers", this ); m_spectrum->showYAxisScalers( showScalers ); m_showYAxisScalerItems[0] = m_displayOptionsPopupDiv->addMenuItem( WString::tr("app-mi-view-show-scaler"), ""); @@ -7004,7 +7024,7 @@ void InterSpec::addViewMenu( WWidget *parent ) m_showYAxisScalerItems[1]->triggered().connect( boost::bind( &InterSpec::setShowYAxisScalers, this, false ) ); m_showYAxisScalerItems[0]->setHidden( showScalers ); m_showYAxisScalerItems[1]->setHidden( !showScalers ); - InterSpecUser::addCallbackWhenChanged( m_user, this, "ShowYAxisScalers", this, &InterSpec::setShowYAxisScalers ); + m_preferences->addCallbackWhenChanged( "ShowYAxisScalers", this, &InterSpec::setShowYAxisScalers ); m_displayOptionsPopupDiv->addSeparator(); @@ -7308,7 +7328,7 @@ void InterSpec::setLogY( bool logy ) { const bool wasLogY = m_spectrum->yAxisIsLog(); - InterSpecUser::setPreferenceValue( m_user, "LogY", logy, this ); + UserPreferences::setPreferenceValue( "LogY", logy, this ); m_logYItems[0]->setHidden( logy ); m_logYItems[1]->setHidden( !logy ); m_spectrum->setYAxisLog( logy ); @@ -7343,7 +7363,7 @@ void InterSpec::setVerticalLines( bool show ) { const bool wasShow = m_spectrum->verticalLinesShowing(); - InterSpecUser::setPreferenceValue( m_user, "ShowVerticalGridlines", show, this ); + UserPreferences::setPreferenceValue( "ShowVerticalGridlines", show, this ); m_verticalLinesItems[0]->setHidden( show ); m_verticalLinesItems[1]->setHidden( !show ); m_spectrum->showVerticalLines( show ); @@ -7362,7 +7382,7 @@ void InterSpec::setHorizantalLines( bool show ) { const bool wasShow = m_spectrum->horizontalLinesShowing(); - InterSpecUser::setPreferenceValue( m_user, "ShowHorizontalGridlines", show, this ); + UserPreferences::setPreferenceValue( "ShowHorizontalGridlines", show, this ); m_horizantalLinesItems[0]->setHidden( show ); m_horizantalLinesItems[1]->setHidden( !show ); m_spectrum->showHorizontalLines( show ); @@ -7562,7 +7582,7 @@ void InterSpec::setXAxisSlider( bool show ) { const bool wasShowing = m_spectrum->xAxisSliderChartIsVisible(); - InterSpecUser::setPreferenceValue( m_user, "ShowXAxisSlider", show, this ); + UserPreferences::setPreferenceValue( "ShowXAxisSlider", show, this ); m_showXAxisSliderItems[0]->setHidden( show ); m_showXAxisSliderItems[1]->setHidden( !show ); @@ -7578,7 +7598,7 @@ void InterSpec::setXAxisSlider( bool show ) }else { //Go back to whatever the user wants/selects - const bool makeCompact = InterSpecUser::preferenceValue( "CompactXAxis", this ); + const bool makeCompact = UserPreferences::preferenceValue( "CompactXAxis", this ); if( m_compactXAxisItems[0] ) m_compactXAxisItems[0]->setHidden( makeCompact ); @@ -7605,7 +7625,7 @@ void InterSpec::setXAxisCompact( bool compact ) { const bool wasCompact = m_spectrum->isAxisCompacted(); - InterSpecUser::setPreferenceValue( m_user, "CompactXAxis", compact, this ); + UserPreferences::setPreferenceValue( "CompactXAxis", compact, this ); if( m_compactXAxisItems[0] ) m_compactXAxisItems[0]->setHidden( compact ); @@ -7639,7 +7659,7 @@ void InterSpec::setShowYAxisScalers( bool show ) try { - InterSpecUser::setPreferenceValue( m_user, "ShowYAxisScalers", show, this ); + UserPreferences::setPreferenceValue( "ShowYAxisScalers", show, this ); }catch( std::exception &e ) { cerr << "InterSpec::setShowYAxisScalers: Got exception setting pref: " << e.what() << endl; @@ -7804,19 +7824,19 @@ void InterSpec::addAboutMenu( Wt::WWidget *parent ) PopupDivMenu *subPopup = m_helpMenuPopup->addPopupMenuItem( WString::tr("app-mi-help-opts"), "InterSpec_resources/images/cog_small.png" ); - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", this ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", this ); // Note: we will associate the WCheckBox's with a option, to set their checked state, // BEFORE adding to the menu - so this way macOS native menus will pickup the // correct values. WCheckBox *cb = new WCheckBox( WString::tr("app-mi-help-pref-auto-store") ); - InterSpecUser::associateWidget( m_user, "AutoSaveSpectraToDb", cb, this ); + UserPreferences::associateWidget( "AutoSaveSpectraToDb", cb, this ); item = subPopup->addWidget( cb ); HelpSystem::attachToolTipOn( item, WString::tr("app-mi-tt-help-pref-auto-store"), showToolTips ); cb = new WCheckBox( WString::tr("app-mi-help-pref-check-prev") ); - InterSpecUser::associateWidget( m_user, "CheckForPrevOnSpecLoad", cb, this ); + UserPreferences::associateWidget( "CheckForPrevOnSpecLoad", cb, this ); item = subPopup->addWidget( cb ); HelpSystem::attachToolTipOn( item, WString::tr("app-mi-tt-help-pref-check-prev"), showToolTips ); @@ -7824,7 +7844,7 @@ void InterSpec::addAboutMenu( Wt::WWidget *parent ) if( !isMobile() ) { WCheckBox *checkbox = new WCheckBox( WString::tr("app-mi-help-pref-show-tt") ); - InterSpecUser::associateWidget( m_user, "ShowTooltips", checkbox, this ); + UserPreferences::associateWidget( "ShowTooltips", checkbox, this ); item = subPopup->addWidget( checkbox ); HelpSystem::attachToolTipOn( item, WString::tr("app-mi-tt-help-pref-show-tt"), true, HelpSystem::ToolTipPosition::Right ); @@ -7834,7 +7854,7 @@ void InterSpec::addAboutMenu( Wt::WWidget *parent ) {//begin add "AskPropagatePeaks" to menu WCheckBox *checkbox = new WCheckBox( WString::tr("app-mi-help-pref-prop-peak") ); - InterSpecUser::associateWidget( m_user, "AskPropagatePeaks", checkbox, this ); + UserPreferences::associateWidget( "AskPropagatePeaks", checkbox, this ); item = subPopup->addWidget( checkbox ); HelpSystem::attachToolTipOn( item, WString::tr("app-mi-tt-help-pref-prop-peak"), true, HelpSystem::ToolTipPosition::Right ); @@ -7845,7 +7865,7 @@ void InterSpec::addAboutMenu( Wt::WWidget *parent ) {//begin add "DisplayBecquerel" WCheckBox *checkbox = new WCheckBox( WString::tr("app-mi-help-pref-disp-bq") ); - InterSpecUser::associateWidget( m_user, "DisplayBecquerel", checkbox, this ); + UserPreferences::associateWidget( "DisplayBecquerel", checkbox, this ); item = subPopup->addWidget( checkbox ); HelpSystem::attachToolTipOn( item, WString::tr("app-mi-tt-help-pref-disp-bq"), true, HelpSystem::ToolTipPosition::Right ); @@ -7853,7 +7873,7 @@ void InterSpec::addAboutMenu( Wt::WWidget *parent ) {//begin add "LoadDefaultDrf" WCheckBox *checkbox = new WCheckBox( WString::tr("app-mi-help-pref-def-drfs") ); - InterSpecUser::associateWidget( m_user, "LoadDefaultDrf", checkbox, this ); + UserPreferences::associateWidget( "LoadDefaultDrf", checkbox, this ); item = subPopup->addWidget( checkbox ); HelpSystem::attachToolTipOn( item, WString::tr("app-mi-tt-help-def-drfs"), true, HelpSystem::ToolTipPosition::Right ); @@ -7863,7 +7883,7 @@ void InterSpec::addAboutMenu( Wt::WWidget *parent ) if( app && app->isTablet() ) { WCheckBox *checkbox = new WCheckBox( WString::tr("app-mi-help-pref-desktop") ); - InterSpecUser::associateWidget( m_user, "TabletUseDesktopMenus", checkbox, this ); + UserPreferences::associateWidget( "TabletUseDesktopMenus", checkbox, this ); item = subPopup->addWidget( checkbox ); HelpSystem::attachToolTipOn( item, WString::tr("app-mi-tt-help-desktop"), true, HelpSystem::ToolTipPosition::Right ); @@ -7882,12 +7902,12 @@ void InterSpec::addAboutMenu( Wt::WWidget *parent ) }//if( is tablet ) WCheckBox *autoDarkCb = new WCheckBox( WString::tr("app-mi-help-pref-auto-dark") ); - InterSpecUser::associateWidget( m_user, "AutoDarkFromOs", autoDarkCb, this ); + UserPreferences::associateWidget( "AutoDarkFromOs", autoDarkCb, this ); item = subPopup->addWidget( autoDarkCb ); HelpSystem::attachToolTipOn( item, WString::tr("app-mi-tt-help-auto-dark"), true, HelpSystem::ToolTipPosition::Right ); - InterSpecUser::addCallbackWhenChanged( m_user, this, "AutoDarkFromOs", std::bind([](){ + m_preferences->addCallbackWhenChanged( "AutoDarkFromOs", std::bind([](){ InterSpec *viewer = InterSpec::instance(); if( viewer ) viewer->doJavaScript( "try{ Wt.WT.DetectOsColorThemeJs('" + viewer->id() + "'); }" @@ -7900,12 +7920,12 @@ void InterSpec::addAboutMenu( Wt::WWidget *parent ) #if( PROMPT_USER_BEFORE_LOADING_PREVIOUS_STATE ) subPopup->addSeparator(); WCheckBox *promptOnLoad = new WCheckBox( WString::tr("app-mi-help-pref-prompt-prev") ); - InterSpecUser::associateWidget( m_user, "PromptStateLoadOnStart", promptOnLoad, this ); + UserPreferences::associateWidget( "PromptStateLoadOnStart", promptOnLoad, this ); item = subPopup->addWidget( promptOnLoad ); HelpSystem::attachToolTipOn( item, WString::tr("app-mi-tt-help-pref-prompt-prev"), showToolTips ); WCheckBox *doLoad = new WCheckBox( WString::tr("app-mi-help-pref-load-prev") ); - InterSpecUser::associateWidget( m_user, "LoadPrevStateOnStart", doLoad, this ); + UserPreferences::associateWidget( "LoadPrevStateOnStart", doLoad, this ); item = subPopup->addWidget( doLoad ); HelpSystem::attachToolTipOn( item, WString::tr("app-mi-tt-help-pref-load-prev"), showToolTips ); #endif @@ -7915,7 +7935,7 @@ void InterSpec::addAboutMenu( Wt::WWidget *parent ) const set &languages = InterSpecApp::languagesAvailable(); if( languages.size() > 1 ) { - const string langPref = InterSpecUser::preferenceValue("Language", this); + const string langPref = UserPreferences::preferenceValue("Language", this); m_languagesSubMenu = m_helpMenuPopup->addPopupMenuItem( WString::tr("app-mi-help-language") ); @@ -8417,7 +8437,7 @@ DecayWindow *InterSpec::createDecayInfoWindow() // TODO: We could do a little better and check the Shielding/Source Fit widget and grab those activities (and ages) if they match - const bool useBq = InterSpecUser::preferenceValue("DisplayBecquerel", InterSpec::instance()); + const bool useBq = UserPreferences::preferenceValue("DisplayBecquerel", InterSpec::instance()); const double act = useBq ? PhysicalUnits::MBq : (1.0E-6 * PhysicalUnits::curie); const string actStr = useBq ? "1 MBq" : "1 uCi"; const double hl = nuc.m_nuclide->halfLife; @@ -9695,7 +9715,7 @@ void InterSpec::handleToolTabClosed( const int tabnum ) void InterSpec::addToolsMenu( Wt::WWidget *parent ) { - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", this ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", this ); PopupDivMenu *parentMenu = dynamic_cast( parent ); WContainerWidget *menuDiv = dynamic_cast( parent ); @@ -10516,7 +10536,7 @@ void InterSpec::handleToolTabChanged( int tab ) if( focus && (current_tab == calibtab) ) { if( !m_infoNotificationsMade.count("recal-tab") - && InterSpecUser::preferenceValue( "ShowTooltips", this ) + && UserPreferences::preferenceValue( "ShowTooltips", this ) && !isMobile() ) { m_infoNotificationsMade.insert( "recal-tab" ); @@ -11051,7 +11071,7 @@ void InterSpec::setSpectrum( std::shared_ptr meas, #if( USE_DB_TO_STORE_SPECTRA ) /* - if( previous && m_user->preferenceValue( "AutoSaveSpectraToDb" ) ) + if( previous && UserPreferences::preferenceValue( "AutoSaveSpectraToDb" ) ) { //We also need to do this in the InterSpec destructor as well. // Also maybe change size limitations to only apply to auto saving @@ -11157,7 +11177,7 @@ void InterSpec::setSpectrum( std::shared_ptr meas, if( !meas->detector() /* && (detType != SpecUtils::DetectorType::Unknown) */ ) { - const bool doLoadDefault = InterSpecUser::preferenceValue( "LoadDefaultDrf", this ); + const bool doLoadDefault = UserPreferences::preferenceValue( "LoadDefaultDrf", this ); const string &manufacturer = meas->manufacturer(); const string &model = meas->instrument_model(); @@ -11289,7 +11309,7 @@ void InterSpec::setSpectrum( std::shared_ptr meas, std::function)> propigate_peaks_fcns; const bool askToPropigatePeaks - = InterSpecUser::preferenceValue( "AskPropagatePeaks", this ); + = UserPreferences::preferenceValue( "AskPropagatePeaks", this ); if( askToPropigatePeaks && options.testFlag(InterSpec::SetSpectrumOptions::CheckToPreservePreviousEnergyCal) && !sameSpecFile && meas && m_dataMeasurement && previous && m_spectrum->data() @@ -11657,7 +11677,7 @@ void InterSpec::setSpectrum( std::shared_ptr meas, if( show_notification ) { - const bool show = InterSpecUser::preferenceValue( "AutoShowSpecMultimedia", this ); + const bool show = UserPreferences::preferenceValue( "AutoShowSpecMultimedia", this ); if( show ) { showMultimedia( SpecUtils::SpectrumType::Foreground ); @@ -11686,7 +11706,7 @@ void InterSpec::setSpectrum( std::shared_ptr meas, if( spec_type==SpecUtils::SpectrumType::Foreground && !!m_dataMeasurement && m_dataMeasurement->passthrough() ) { - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", this ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", this ); if( showToolTips ) { @@ -11792,7 +11812,7 @@ void InterSpec::userOpenFile( std::shared_ptr meas, std::string displa if( !m_fileManager ) //shouldnt ever happen. throw runtime_error( "Internal logic error, no valid m_fileManager" ); - auto header = std::make_shared( m_user, true, this ); + auto header = std::make_shared( true, this ); header->setFile( displayFileName, meas ); bool couldBeBackground = true; diff --git a/src/InterSpecApp.cpp b/src/InterSpecApp.cpp index b18c2d54..1890b1dc 100644 --- a/src/InterSpecApp.cpp +++ b/src/InterSpecApp.cpp @@ -67,6 +67,7 @@ #include "InterSpec/InterSpecUser.h" #include "InterSpec/DataBaseUtils.h" #include "InterSpec/WarningWidget.h" +#include "InterSpec/UserPreferences.h" #if( BUILD_AS_ELECTRON_APP ) #include "target/electron/ElectronUtils.h" @@ -651,15 +652,15 @@ void InterSpecApp::setupWidgets( const bool attemptStateLoad ) //Check to see if we should load the apps last saved state try { - bool saveState = InterSpecUser::preferenceValue( "AutoSaveSpectraToDb", m_viewer ); + bool saveState = UserPreferences::preferenceValue( "AutoSaveSpectraToDb", m_viewer ); saveState = (saveState && attemptStateLoad); #if( PROMPT_USER_BEFORE_LOADING_PREVIOUS_STATE ) //Make it so the app will prompt user to load the state // - Experimental! // - maybe should make it so it always does this, except on iOS and Android - const bool loadPrev = InterSpecUser::preferenceValue("LoadPrevStateOnStart", m_viewer ); - const bool promptLoad = InterSpecUser::preferenceValue("PromptStateLoadOnStart", m_viewer ); + const bool loadPrev = UserPreferences::preferenceValue("LoadPrevStateOnStart", m_viewer ); + const bool promptLoad = UserPreferences::preferenceValue("PromptStateLoadOnStart", m_viewer ); if( !loadPrev && !promptLoad ) saveState = false; #endif @@ -680,7 +681,7 @@ void InterSpecApp::setupWidgets( const bool attemptStateLoad ) DataBaseUtils::DbTransaction transaction( *sql ); const string query = "InterSpecUser_id = " - + std::to_string( m_viewer->m_user.id() ) + + std::to_string( m_viewer->user().id() ) + " AND (StateType = " + std::to_string( int(UserState::kEndOfSessionTemp) ) + " OR StateType = " @@ -723,8 +724,7 @@ void InterSpecApp::setupWidgets( const bool attemptStateLoad ) dialogDiv->addWidget( cb ); auto changePromptPref = [this](bool dont_ask ){ try { - InterSpecUser::setPreferenceValue( m_viewer->m_user, - "PromptStateLoadOnStart", dont_ask, m_viewer ); + UserPreferences::setPreferenceValue( "PromptStateLoadOnStart", dont_ask, m_viewer ); }catch( std::exception &e ) { Wt::log("error") << "Failed setting 'PromptStateLoadOnStart' user prefrence/"; } @@ -732,8 +732,7 @@ void InterSpecApp::setupWidgets( const bool attemptStateLoad ) auto changeDoLoadPref = [this](bool load ){ try { - InterSpecUser::setPreferenceValue( m_viewer->m_user, - "LoadPrevStateOnStart", load, m_viewer ); + UserPreferences::setPreferenceValue( "LoadPrevStateOnStart", load, m_viewer ); }catch( std::exception &e ) { Wt::log("error") << "Failed setting 'LoadPrevStateOnStart' user prefrence."; } @@ -913,9 +912,9 @@ void InterSpecApp::setupWidgets( const bool attemptStateLoad ) } #endif - if( m_viewer->m_user ) + if( m_viewer->user() ) Wt::log("debug") << "Have started session " << sessionId() << " for user " - << m_viewer->m_user->userName() << "."; + << m_viewer->user()->userName() << "."; #if( BUILD_AS_ELECTRON_APP || BUILD_AS_OSX_APP || ANDROID || BUILD_AS_WX_WIDGETS_APP ) // TODO 20220405: in macOS app I see the error "Wt: decodeSignal(): signal 'of7g69f.SucessfullyLoadedConf' not exposed" @@ -1266,8 +1265,8 @@ void InterSpecApp::updateUsageTimeToDb() DataBaseUtils::DbTransaction transaction( *sql ); // Other sessions may have added to the session - we'll reread from the DB - m_viewer->m_user.reread(); - m_viewer->m_user.modify()->addUsageTimeDuration( m_activeTimeSinceDbUpdate ); + m_viewer->reReadUserInfoFromDb(); + m_viewer->user().modify()->addUsageTimeDuration( m_activeTimeSinceDbUpdate ); transaction.commit(); }//End db transaction @@ -1283,7 +1282,7 @@ void InterSpecApp::updateUsageTimeToDb() void InterSpecApp::prepareForEndOfSession() { - if( !m_viewer || !m_viewer->m_user ) + if( !m_viewer || !m_viewer->user() ) return; updateUsageTimeToDb(); @@ -1294,12 +1293,12 @@ void InterSpecApp::prepareForEndOfSession() const chrono::milliseconds nmilli = chrono::duration_cast(m_activeTimeInSession); Wt::log("info") << "At session end, actively used for " << nmilli.count() << " ms for user " - << m_viewer->m_user->userName(); + << m_viewer->user()->userName(); #if( USE_DB_TO_STORE_SPECTRA ) //Check to see if we should save the apps state - const bool saveState = InterSpecUser::preferenceValue( "AutoSaveSpectraToDb", m_viewer ); + const bool saveState = UserPreferences::preferenceValue( "AutoSaveSpectraToDb", m_viewer ); //Clean up the kEndOfSessions from before bool cleanupStates = true; @@ -1307,7 +1306,7 @@ void InterSpecApp::prepareForEndOfSession() { DataBaseUtils::DbTransaction transaction( *sql ); const string query = "InterSpecUser_id = " - + std::to_string( m_viewer->m_user.id() ) + + std::to_string( m_viewer->user().id() ) + " AND (StateType = " + std::to_string( int(UserState::kEndOfSessionTemp)) + " OR StateType = " @@ -1350,7 +1349,7 @@ void InterSpecApp::prepareForEndOfSession() DataBaseUtils::DbTransaction transaction( *sql ); UserState *state = new UserState(); - state->user = m_viewer->m_user; + state->user = m_viewer->user(); state->stateType = UserState::kEndOfSessionTemp; state->creationTime = WDateTime::currentDateTime(); state->name = name; @@ -1700,9 +1699,9 @@ void InterSpecApp::finalize() { prepareForEndOfSession(); - if( m_viewer && m_viewer->m_user ) + if( m_viewer && m_viewer->user() ) Wt::log("info") << "Have finalized session " << sessionId() << " for user " - << m_viewer->m_user->userName(); + << m_viewer->user()->userName(); }//void InterSpecApp::finalize() diff --git a/src/InterSpecUser.cpp b/src/InterSpecUser.cpp index 478800ac..c4b6f7b8 100644 --- a/src/InterSpecUser.cpp +++ b/src/InterSpecUser.cpp @@ -23,11 +23,13 @@ #include "InterSpec_config.h" + #include #include #include #include #include +#include #include #include @@ -62,9 +64,6 @@ #include "3rdparty/date/include/date/date.h" -#include "rapidxml/rapidxml.hpp" -#include "rapidxml/rapidxml_utils.hpp" - #include "InterSpec/SpecMeas.h" #include "InterSpec/PopupDiv.h" #include "InterSpec/InterSpec.h" @@ -145,73 +144,6 @@ const std::string InterSpecUser::sm_defaultPreferenceFile = "default_preferences namespace { - UserOption *parseUserOption( const rapidxml::xml_node *pref ) - { - using rapidxml::internal::compare; - typedef rapidxml::xml_attribute XmlAttribute; - - UserOption *option = new UserOption; - try - { - const XmlAttribute *name_att = pref->first_attribute( "name", 4 ); - const XmlAttribute *type_att = pref->first_attribute( "type", 4 ); - if( !name_att || !name_att->value() || !type_att || !type_att->value() ) - throw runtime_error( "Ill formatted default preferences file" ); - - const char *typestr = type_att->value(); - std::size_t typestrlen = type_att->value_size(); - - option->m_name = name_att->value(); - - // Get rid of "_tablet" or "_phone" - auto pos = option->m_name.find( "_tablet" ); - if( pos == string::npos ) - pos = option->m_name.find( "_phone" ); - if( pos != string::npos ) - option->m_name = option->m_name.substr(0, pos); - - const char *valuestr = pref->value(); - - // We'll let "String" type values be empty, but no other types. - if( !valuestr ) - { - if( !compare(typestr, typestrlen, "String", 6, true) ) - throw runtime_error( "Missing value for \"" + option->m_name + "\" in default preferences file" ); - - valuestr = ""; - }//if( value is empty in XML ) - - option->m_value = valuestr; - - if( option->m_name.length() > UserOption::sm_max_name_str_len ) - throw runtime_error( "Pref \"" + option->m_name + "\" name to long" ); - if( option->m_value.length() > UserOption::sm_max_value_str_len ) - throw runtime_error( "Pref \"" + option->m_name + "\" value to long" ); - - if( compare( typestr, typestrlen, "String", 6, true) ) - option->m_type = UserOption::String; - else if( compare( typestr, typestrlen, "Decimal", 7, true) ) - option->m_type = UserOption::Decimal; - else if( compare( typestr, typestrlen, "Integer", 7, true) ) - option->m_type = UserOption::Integer; - else if( compare( typestr, typestrlen, "Boolean", 7, true) ) - { - option->m_type = UserOption::Boolean; - if( option->m_value == "true" ) - option->m_value = "1"; - else if( option->m_value == "false" ) - option->m_value = "0"; - }else - throw runtime_error( "Invalid \"type\" (\"" + string(typestr) + "\") " - " for pref " + string(typestr) ); - }catch( std::exception &e ) - { - delete option; - throw runtime_error( e.what() ); - } - return option; - }//UserOption *parseUserOption( rapidxml::xml_node *node ) - /** Compares two boost::any objects to check if their underlying type is the same, and if so, if their values are equal. @@ -379,221 +311,6 @@ UserState::UserState() { } -void InterSpecUser::setBoolPreferenceValue( Wt::Dbo::ptr user, - const std::string &name, - const bool &value, - InterSpec *viewer ) -{ - setPreferenceValue( user, name, value, viewer ); -} - - -void InterSpecUser::setPreferenceValue( Wt::Dbo::ptr user, - const std::string &name, - const bool &value, - InterSpec *viewer ) -{ - setPreferenceValueWorker( user, name, value, viewer ); - - const auto callback_pos = user->m_onBoolChangeSignals.find(name); - if( (callback_pos != end(user->m_onBoolChangeSignals)) && callback_pos->second ) - (*callback_pos->second)(value); -}//setPreferenceValue(...) - - -boost::any InterSpecUser::preferenceValueAny( const std::string &name, InterSpec *viewer ) -{ - using namespace Wt; - - bool from_default = false; - const auto start_time = chrono::time_point_cast( chrono::system_clock::now() ); - - if( !viewer ) - { - UserOption *option = getDefaultUserPreference( name, DeviceType::Desktop ); - boost::any value = option->value(); - delete option; - return value; - } - - //This next line is the only reason InterSpec.h needs to be included - // above - Dbo::ptr &user = userFromViewer(viewer); - std::shared_ptr sql = sqlFromViewer(viewer); - - if( !user || !sql ) - throw std::runtime_error( "preferenceValueAny(...): invalid usr or sql ptr" ); - - boost::any value; - DataBaseUtils::DbTransaction transaction( *sql ); - - try - { - Wt::Dbo::ptr option = user->m_dbPreferences - .find() - .where( "name = ?" ) - .bind( name ) - .limit(1) - .resultValue(); - - if( !option ) - { - from_default = true; - UserOption *option_raw = getDefaultUserPreference( name, user->m_deviceType ); - option.reset( option_raw ); - option.modify()->m_user = user; - sql->session()->add( option ); - //user.modify()->m_dbPreferences.insert( option ); - }//if( dboption ) - - value = option->value(); - - transaction.commit(); - }catch( std::exception &e ) - { - assert( 0 ); - transaction.rollback(); - throw e; - }//try / catch - - const auto end_time = chrono::time_point_cast( chrono::system_clock::now() ); - - cout << "Took " << (end_time - start_time).count() << " microseconds to get '" << name - << "' preference from " << (from_default ? "default-xml" : "data-base") << "." << endl; - - assert( !value.empty() ); - return value; -}//boost::any preferenceValue( const std::string &name, InterSpec *viewer ); - - - -void InterSpecUser::associateWidget( Wt::Dbo::ptr user, - const std::string &name, - Wt::WCheckBox *cb, - InterSpec *viewer ) -{ - const bool value = preferenceValue( name, viewer ); - cb->setChecked( value ); - - InterSpecUser::addCallbackWhenChanged( user, viewer, name, cb, &WCheckBox::setChecked ); - - cb->checked().connect( boost::bind( &InterSpecUser::setBoolPreferenceValue, user, name, true, viewer ) ); - cb->unChecked().connect( boost::bind( &InterSpecUser::setBoolPreferenceValue, user, name, false, viewer ) ); - - /* - //We need to emit the checked() and unChecked() signals so that any side-effect - // can happen from the change. For actually changing the state of the widget - // we can safely do this (incase 'cb' gets deleted at some point) using - // WApplication::bind, but to call the emit functions I couldnt think of a - // safe way to do this, other than searching the widget tree and making sure - // that we can find the widget (hence, know it hasnt been deleted) before - // de-referening the 'cb' passed into this function; this is a bit of a hack - // but it works for now. - const string cbid = cb->id(); - - std::function fcn = [=]( boost::any valueAny ){ - const bool value = boost::any_cast(valueAny); - const bool setCbChecked = reverseValue ? !value : value; - - // The below doesnt seem to find widgets in AuxWindows (and maybe pop-ups) - auto w = wApp->domRoot()->findById(cbid); - if( !w && wApp->domRoot2() ) - w = wApp->domRoot2()->findById(cbid); - if( !w && wApp->root() ) - w = wApp->root()->findById(cbid); - - if( w ) - { - cb->changed().emit(); - if( value ) - cb->checked().emit(); - else - cb->unChecked().emit(); - }else - { - cerr << "Couldnt find widget with cbid='" << cbid << "', so wont call any side-effect functions for pref '" - << name << "'" << endl; - } - };//fcn - - InterSpecUser::associateFunction( user, name, fcn, viewer ); - */ -}//void associateWidget( ) - - -/* -void InterSpecUser::associateWidget( Wt::Dbo::ptr user, - const std::string &name, - Wt::WDoubleSpinBox *sb, - InterSpec *viewer ) -{ - const double value = preferenceValue( name, viewer ); - - sb->setValue( value ); - sb->valueChanged().connect( - boost::bind( &InterSpecUser::setPreferenceValue, - user, name, boost::placeholders::_1, viewer ) ); - - const string sbid = sb->id(); - - std::function fcn = [=]( boost::any valueAny ){ - const double value = boost::any_cast(valueAny); - - auto w = wApp->domRoot()->findById(sbid); - if( !w && wApp->domRoot2() ) - w = wApp->domRoot2()->findById(sbid); - - if( w ) - { - sb->setValue( value ); - sb->valueChanged().emit( value ); - }else - { - cerr << "Couldnt find WDoubleSpinBox with id='" << sbid << "', so wont call any side-effect functions" << endl; - } - };//fcn - - InterSpecUser::associateFunction( user, name, fcn, viewer ); -}//void associateWidget(...) - - -void InterSpecUser::associateWidget( Wt::Dbo::ptr user, - const std::string &name, - Wt::WSpinBox *sb, - InterSpec *viewer ) -{ - const int value = preferenceValue( name, viewer ); - - sb->setValue( value ); - - sb->valueChanged().connect( - boost::bind( &InterSpecUser::setPreferenceValue, - user, name, boost::placeholders::_1, viewer ) ); - - const string sbid = sb->id(); - - std::function fcn = [=]( boost::any valueAny ){ - const int value = boost::any_cast(valueAny); - - auto w = wApp->domRoot()->findById(sbid); - if( !w && wApp->domRoot2() ) - w = wApp->domRoot2()->findById(sbid); - - if( w ) - { - sb->setValue( value ); - sb->valueChanged().emit( value ); - }else - { - cerr << "Couldnt find WSpinBox with id='" << sbid << "', so wont call any side-effect functions" << endl; - } - };//fcn - - InterSpecUser::associateFunction( user, name, fcn, viewer ); -}//void InterSpecUser::associateWidget(...) -*/ - - void UserFileInDbData::setFileData( const std::string &path, const SerializedFileFormat format ) { @@ -1032,12 +749,36 @@ boost::any UserOption::value() const { case String: return m_value; + case Decimal: - return std::stod( m_value ); + { + double val = 0.0; + const bool parsed = SpecUtils::parse_double( m_value.c_str(), m_value.size(), val ); + assert( parsed ); + //if( !parsed ) + // throw std::logic_error( "Invalid Decimal str: '" + m_value + "'" ); + + return val; + //return std::stod( m_value ); + }//case Decimal: + case Integer: - return std::stoi( m_value ); + { + int val = 0; + const bool parsed = SpecUtils::parse_int( m_value.c_str(), m_value.size(), val ); + assert( parsed ); + //if( !parsed ) + // throw std::logic_error( "Invalid Integer str: '" + m_value + "'" ); + + return val; + //return std::stoi( m_value ); + }//case Integer: + case Boolean: + { + assert( (m_value == "0") || (m_value == "1") ); return (m_value=="true" || m_value=="1"); + } }//switch( m_type ) throw runtime_error( "UserOption::value(): invalid m_type" ); @@ -1061,98 +802,16 @@ InterSpecUser::InterSpecUser( const std::string &username, }//InterSpecUser::InterSpecUser(...) - -UserOption *InterSpecUser::getDefaultUserPreference( const std::string &name, - const int type ) -{ - const bool isphone = ((type & InterSpecUser::PhoneDevice) != 0); - const bool istablet = ((type & InterSpecUser::TabletDevice) != 0); - - - static std::mutex sm_def_prefs_mutex; - static std::map> sm_def_prefs; - - std::lock_guard lock( sm_def_prefs_mutex ); - - if( sm_def_prefs.empty() ) - { - const string filename = SpecUtils::append_path( InterSpec::staticDataDirectory(), sm_defaultPreferenceFile ); - - std::vector data; - SpecUtils::load_file_data( filename.c_str(), data ); - - rapidxml::xml_document doc; - const int flags = rapidxml::parse_normalize_whitespace - | rapidxml::parse_trim_whitespace; - - doc.parse( &data.front() ); - rapidxml::xml_node *node = doc.first_node(); - if( !node || !node->name() - || !rapidxml::internal::compare( node->name(), node->name_size(), "preferences", 11, true) ) - throw runtime_error( "InterSpecUser: invalid first node" ); - - for( const rapidxml::xml_node *pref = node->first_node( "pref", 4 ); - pref; - pref = pref->next_sibling( "pref", 4 ) ) - { - const rapidxml::xml_attribute *name_att = pref->first_attribute( "name", 4 ); - assert( name_att && name_att->value() && name_att->value_size() ); - if( !name_att || !name_att->value() || !name_att->value_size() ) - continue; - - const char *prefname = name_att->value(); - const size_t prefnamelen = name_att->value_size(); - std::string prefnamestr(prefname, prefname + prefnamelen); - - UserOption *option = parseUserOption( pref ); - - // option->m_name will have had "_phone" and "_tablet" removed, even though `prefnamestr` - // will have these in them - assert( option ); - assert( !SpecUtils::icontains( option->m_name, "_phone") ); - assert( !SpecUtils::icontains( option->m_name, "_tablet") ); - - sm_def_prefs[std::move(prefnamestr)] = std::make_unique( *option ); - }//for( loop over preferneces ) - }//if( sm_def_prefs.empty() ) - - - if( isphone ) - { - const auto pos = sm_def_prefs.find( name + "_phone" ); - assert( (pos == end(sm_def_prefs)) || pos->second ); - if( pos != end(sm_def_prefs) ) - return new UserOption( *pos->second ); - }// - - if( istablet ) - { - const auto pos = sm_def_prefs.find( name + "_tablet" ); - assert( (pos == end(sm_def_prefs)) || pos->second ); - if( pos != end(sm_def_prefs) ) - return new UserOption( *pos->second ); - }//if( istablet ) - - - const auto pos = sm_def_prefs.find( name ); - assert( (pos == end(sm_def_prefs)) || pos->second ); - if( pos != end(sm_def_prefs) ) - return new UserOption( *pos->second ); - - //Note: the string "couldn't find preference by name" is currently used in - // restoreUserPrefsFromXml(...) to check if a preference with this name is no longer used. - // So dont change next string without also changing there - or really, a more robust - // indication should be used. - throw runtime_error( "InterSpecUser::getDefaultUserPreference(...):" - " couldn't find preference by name " + name ); -}//UserOption *getDefaultUserPreference( const std::string &name ) - - const std::string &InterSpecUser::userName() const { return m_userName; } +int InterSpecUser::deviceType() const +{ + return m_deviceType; +} + int InterSpecUser::accessCount() const { @@ -1183,21 +842,6 @@ std::chrono::system_clock::time_point InterSpecUser::firstAccessUTC() const } -Wt::Dbo::ptr &InterSpecUser::userFromViewer( InterSpec *viewer ) -{ - static Dbo::ptr dummy; - if( !viewer ) - return dummy; - return viewer->m_user; -} - -std::shared_ptr InterSpecUser::sqlFromViewer( InterSpec *viewer ) -{ - if( !viewer ) - return std::shared_ptr(); - return viewer->sql(); -} - void InterSpecUser::startingNewSession() { incrementAccessCount(); @@ -1218,148 +862,16 @@ void InterSpecUser::incrementSpectraFileOpened() }//void incrementSpectraFileOpened() -void InterSpecUser::restoreUserPrefsFromXml( - Wt::Dbo::ptr user, - const ::rapidxml::xml_node *prefs_node, - InterSpec *viewer ) -{ - using namespace rapidxml; - using rapidxml::internal::compare; - - if( !user || !prefs_node - || !compare( prefs_node->name(), prefs_node->name_size(), "preferences", 11, true) ) - throw runtime_error( "restoreUserPrefsFromXml: invalid input" ); - - - for( const xml_node *pref = prefs_node->first_node( "pref", 4 ); pref; - pref = pref->next_sibling( "pref", 4 ) ) - { - std::unique_ptr option; - try - { - option.reset( parseUserOption(pref) ); - - switch( option->m_type ) - { - case UserOption::String: - { - const string value = boost::any_cast( option->value() ); - setPreferenceValue( user, option->m_name, value, viewer ); - break; - }//case String - - case UserOption::Decimal: - { - const double value = boost::any_cast( option->value() ); - setPreferenceValue( user, option->m_name, value, viewer ); - break; - }//case Decimal - - case UserOption::Integer: - { - const int value = boost::any_cast( option->value() ); - setPreferenceValue( user, option->m_name, value, viewer ); - break; - }//case Integer - - case UserOption::Boolean: - { - const bool value = boost::any_cast( option->value() ); - setPreferenceValue( user, option->m_name, value, viewer ); - break; - }//case Boolean - }//switch( datatype ) - }catch( std::exception &e ) - { - const string errmsg = e.what(); - if( SpecUtils::icontains( errmsg, "couldn't find preference by name" ) ) - { - cerr << "Warning: couldnt find a preference named '" - << (option ? option->m_name : string("N/A")) << "' that is in the" - << " file being loaded, but apears to no longer be used." << endl; - }else - { - throw runtime_error( "Failed to deserialize user prefernces from XML: " + errmsg ); - } - }//try / catch - }//for( loop over prefs ) -}//void restoreUserPrefsFromXml(...) - - -::rapidxml::xml_node *InterSpecUser::userOptionsToXml( - ::rapidxml::xml_node *parent_node, - InterSpec *viewer ) const -{ - using namespace ::rapidxml; - - if( !parent_node ) - throw runtime_error( "userOptionsToXml: invalid input" ); - - xml_document *doc = parent_node->document(); - - xml_node *prefs_node = doc->allocate_node( node_element, "preferences" ); - parent_node->append_node( prefs_node ); - - vector< Dbo::ptr > options; - - {//begin codeblock to retrieve prefernces from database - std::shared_ptr sql = viewer->sql(); - DataBaseUtils::DbTransaction transaction( *sql ); - - std::copy( m_dbPreferences.begin(), m_dbPreferences.end(), - std::back_inserter(options) ); - transaction.commit(); - }//end codeblock to retrieve prefernces from database - - for( vector< Dbo::ptr >::const_iterator iter = options.begin(); - iter != options.end(); ++iter ) - { - Dbo::ptr option = *iter; - - const string &name = option->m_name; - const string &value = option->m_value; - const char *namestr = doc->allocate_string( name.c_str(), name.size()+1 ); - - const char *valstr = 0; - switch( option->m_type ) - { - case UserOption::String: case UserOption::Decimal: case UserOption::Integer: - valstr = doc->allocate_string( value.c_str(), value.size()+1 ); - break; - - case UserOption::Boolean: - valstr = (boost::any_cast(option->value()) ? "true" : "false"); - break; - }//switch( m_type ) - - const char *typestr = 0; - switch( option->m_type ) - { - case UserOption::String: typestr = "String"; break; - case UserOption::Decimal: typestr = "Decimal"; break; - case UserOption::Integer: typestr = "Integer"; break; - case UserOption::Boolean: typestr = "Boolean"; break; - }//switch( m_type ) - - xml_node *node = doc->allocate_node( node_element, "pref", valstr ); - xml_attribute *name_att = doc->allocate_attribute( "name", namestr ); - xml_attribute *type_att = doc->allocate_attribute( "type", typestr ); - node->append_attribute( name_att ); - node->append_attribute( type_att ); - prefs_node->append_node( node ); - }//for( loop over DB entries ) - - return prefs_node; -}//xml_node *userOptionsToXml( xml_node * ) const - - - - const Wt::Dbo::collection< Wt::Dbo::ptr > &InterSpecUser::userFiles() const { return m_userFiles; } +const Wt::Dbo::collection< Wt::Dbo::ptr > &InterSpecUser::preferences() const +{ + return m_dbPreferences; +} + const Wt::Dbo::collection< Wt::Dbo::ptr > &InterSpecUser::shieldSrcModels() const { return m_shieldSrcModels; diff --git a/src/IsotopeSearchByEnergy.cpp b/src/IsotopeSearchByEnergy.cpp index 0a8784cb..dfa41c40 100644 --- a/src/IsotopeSearchByEnergy.cpp +++ b/src/IsotopeSearchByEnergy.cpp @@ -57,6 +57,7 @@ #include "InterSpec/ReactionGamma.h" #include "InterSpec/PhysicalUnits.h" #include "InterSpec/UndoRedoManager.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/PeakSearchGuiUtils.h" #include "InterSpec/RowStretchTreeView.h" #include "InterSpec/NativeFloatSpinBox.h" @@ -361,7 +362,7 @@ IsotopeSearchByEnergy::IsotopeSearchByEnergy( InterSpec *viewer, helpBtn->addStyleClass( "Wt-icon ContentHelpBtn" ); helpBtn->clicked().connect( boost::bind( &HelpSystem::createHelpWindow, "nuclide-search-dialog" ) ); - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", m_viewer ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", m_viewer ); WContainerWidget *optionDiv = new WContainerWidget( searchOptions ); WLabel *label = new WLabel( WString::tr("isbe-min-br"), optionDiv ); diff --git a/src/LeafletRadMap.cpp b/src/LeafletRadMap.cpp index 2915169f..06878607 100644 --- a/src/LeafletRadMap.cpp +++ b/src/LeafletRadMap.cpp @@ -48,9 +48,9 @@ #include "InterSpec/InterSpec.h" #include "InterSpec/InterSpecApp.h" #include "InterSpec/SimpleDialog.h" -#include "InterSpec/InterSpecUser.h" #include "InterSpec/LeafletRadMap.h" #include "InterSpec/WarningWidget.h" +#include "InterSpec/UserPreferences.h" #if( BUILD_AS_ELECTRON_APP || IOS || ANDROID || BUILD_AS_OSX_APP || BUILD_AS_LOCAL_SERVER || BUILD_AS_WX_WIDGETS_APP ) #include @@ -93,7 +93,7 @@ SimpleDialog *LeafletRadMap::showForMeasurement( const std::shared_ptruseMessageResourceBundle( "LeafletRadMap" ); - const bool showWarning = InterSpecUser::preferenceValue( "ShowMapDataWarning", viewer ); + const bool showWarning = UserPreferences::preferenceValue( "ShowMapDataWarning", viewer ); if( forceNoWarning || !showWarning ) { @@ -129,7 +129,7 @@ SimpleDialog *LeafletRadMap::showForMeasurement( const std::shared_ptrchecked().connect( std::bind([cb](){ InterSpec *viewer = InterSpec::instance(); if( viewer ) - InterSpecUser::setPreferenceValue(viewer->m_user, "ShowMapDataWarning", !cb->isChecked(), viewer); + UserPreferences::setPreferenceValue("ShowMapDataWarning", !cb->isChecked(), viewer); }) ); WPushButton *accept = dialog->addButton( WString::tr("lrm-pre-warn-proceed-btn") ); @@ -138,7 +138,7 @@ SimpleDialog *LeafletRadMap::showForMeasurement( const std::shared_ptrclicked().connect( std::bind([](){ InterSpec *viewer = InterSpec::instance(); if( viewer ) - InterSpecUser::setPreferenceValue(viewer->m_user, "ShowMapDataWarning", true, viewer); + UserPreferences::setPreferenceValue("ShowMapDataWarning", true, viewer); }) ); accept->setFocus(); diff --git a/src/LicenseAndDisclaimersWindow.cpp b/src/LicenseAndDisclaimersWindow.cpp index a89f32f9..f33a0dd0 100644 --- a/src/LicenseAndDisclaimersWindow.cpp +++ b/src/LicenseAndDisclaimersWindow.cpp @@ -55,6 +55,7 @@ #include "InterSpec/InterSpecApp.h" #include "InterSpec/PhysicalUnits.h" #include "InterSpec/UseInfoWindow.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/PhysicalUnitsLocalized.h" #include "InterSpec/LicenseAndDisclaimersWindow.h" @@ -390,7 +391,7 @@ void LicenseAndDisclaimersWindow::dataStorageCreator( Wt::WContainerWidget *pare string totalUserTime, totalFilesOpened, totalSessions, firstAccess; InterSpec *viewer = InterSpec::instance(); - Dbo::ptr user = ((app && viewer) ? viewer->m_user : Dbo::ptr()); + Dbo::ptr user = ((app && viewer) ? viewer->user() : Dbo::ptr()); if( user ) { try @@ -478,8 +479,8 @@ void LicenseAndDisclaimersWindow::dataStorageCreator( Wt::WContainerWidget *pare #endif #if( USE_REMOTE_RID ) - const string urls = InterSpecUser::preferenceValue( "ExternalRidUrl", viewer ); - const string exes = InterSpecUser::preferenceValue( "ExternalRidExe", viewer ); + const string urls = UserPreferences::preferenceValue( "ExternalRidUrl", viewer ); + const string exes = UserPreferences::preferenceValue( "ExternalRidExe", viewer ); if( urls.empty() && exes.empty() ) content.arg( WString::tr("ladw-remote-rid-none") ); diff --git a/src/MakeDrf.cpp b/src/MakeDrf.cpp index 28156cf6..069ad284 100644 --- a/src/MakeDrf.cpp +++ b/src/MakeDrf.cpp @@ -81,6 +81,7 @@ #include "InterSpec/PhysicalUnits.h" #include "InterSpec/ShieldingSelect.h" #include "InterSpec/SpecMeasManager.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/SpectraFileModel.h" #include "InterSpec/NativeFloatSpinBox.h" #include "InterSpec/PeakSearchGuiUtils.h" @@ -1530,7 +1531,7 @@ MakeDrf::MakeDrf( InterSpec *viewer, MaterialDB *materialDB, addStyleClass( "MakeDrf" ); - const bool showToolTips = InterSpecUser::preferenceValue("ShowTooltips", m_interspec ); + const bool showToolTips = UserPreferences::preferenceValue("ShowTooltips", m_interspec ); WGridLayout *upperLayout = new WGridLayout(); upperLayout->setContentsMargins( 0, 0, 0, 0 ); @@ -2042,7 +2043,7 @@ void MakeDrf::startSaveAs() DataBaseUtils::DbTransaction transaction( *sql ); //Create a separate DetectorPeakResponse because shared_ptr and dbo::ptr don't work well together DetectorPeakResponse *tempDetector = new DetectorPeakResponse( *drf ); - tempDetector->m_user = m_interspec->m_user.id(); + tempDetector->m_user = m_interspec->user().id(); auto newDbDet = sql->session()->add( tempDetector ); transaction.commit(); @@ -2055,7 +2056,7 @@ void MakeDrf::startSaveAs() m_interspec->detectorChanged().emit( drf ); std::shared_ptr sql = m_interspec->sql(); - Wt::Dbo::ptr user = m_interspec->m_user; + const Wt::Dbo::ptr &user = m_interspec->user(); if( def_for_serial_cb && def_for_serial_cb->isChecked() && representative_meas ) { diff --git a/src/MakeDrfSrcDef.cpp b/src/MakeDrfSrcDef.cpp index 72d90b66..245052cb 100644 --- a/src/MakeDrfSrcDef.cpp +++ b/src/MakeDrfSrcDef.cpp @@ -60,6 +60,7 @@ #include "InterSpec/MakeDrfSrcDef.h" #include "InterSpec/PhysicalUnits.h" #include "InterSpec/ShieldingSelect.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/NativeFloatSpinBox.h" #include "InterSpec/DecayDataBaseServer.h" #include "InterSpec/DetectorPeakResponse.h" @@ -479,7 +480,7 @@ void MakeDrfSrcDef::setNuclide( const SandiaDecay::Nuclide *nuc ) m_useAgeInfo->setUnChecked(); m_distanceEdit->setValueText( "25 cm" ); - const bool useCi = !InterSpecUser::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); + const bool useCi = !UserPreferences::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); m_activityEdit->setValueText( useCi ? "1 uCi" : "37 kBq" ); m_useAgeInfo->hide(); } @@ -566,7 +567,7 @@ void MakeDrfSrcDef::create() WRegExpValidator *val = new WRegExpValidator( PhysicalUnits::sm_activityRegex, this ); val->setFlags( Wt::MatchCaseInsensitive ); m_activityEdit->setValidator( val ); - const bool useCi = !InterSpecUser::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); + const bool useCi = !UserPreferences::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); m_activityEdit->setText( useCi ? "100 uCi" : "3.7 MBq" ); m_activityEdit->changed().connect( this, &MakeDrfSrcDef::handleUserChangedActivity ); m_activityEdit->enterPressed().connect( this, &MakeDrfSrcDef::handleUserChangedActivity ); @@ -668,7 +669,7 @@ void MakeDrfSrcDef::create() m_lib_src_menu->setAutoHide( true, 2500 ); - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", InterSpec::instance() ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", InterSpec::instance() ); const char *tooltip = "Sources defined in Source.lib file in your users data directory.
    " "When clicked, this button will display a menu with all sources for this nuclide - and when" " on of the items is selected, its information will be populated."; @@ -826,7 +827,7 @@ void MakeDrfSrcDef::handleUserChangedAgeAtAssay() if( agestr.empty() || (agestr.find_first_not_of("+-0.")==string::npos) ) { - const bool useCi = !InterSpecUser::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); + const bool useCi = !UserPreferences::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); m_sourceAgeAtAssay->setText( useCi ? "0 uCi" : "0 bq" ); }else { @@ -1039,7 +1040,7 @@ void MakeDrfSrcDef::setDistance( const double dist ) void MakeDrfSrcDef::setActivity( const double act ) { - const bool useCi = !InterSpecUser::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); + const bool useCi = !UserPreferences::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); const int ndecimals = 4; m_activityEdit->setText( PhysicalUnits::printToBestActivityUnits(act, ndecimals, useCi) ); updateAgedText(); @@ -1062,7 +1063,7 @@ void MakeDrfSrcDef::setAssayInfo( const double activity, if( activity > 0.0 ) { const int ndecimals = 4; - const bool useCi = !InterSpecUser::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); + const bool useCi = !UserPreferences::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); m_activityEdit->setText( PhysicalUnits::printToBestActivityUnits(activity, ndecimals, useCi) ); } @@ -1217,7 +1218,7 @@ std::string MakeDrfSrcDef::toGadrasLikeSourceString() const answer += ","; const double activity = activityAtSpectrumTime(); - const bool useCi = !InterSpecUser::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); + const bool useCi = !UserPreferences::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); answer += PhysicalUnits::printToBestActivityUnits(activity,5,useCi); if( m_shieldingSelect && m_useShielding->isChecked() ) diff --git a/src/MoreNuclideInfoDisplay.cpp b/src/MoreNuclideInfoDisplay.cpp index c89cd28b..c3349c88 100644 --- a/src/MoreNuclideInfoDisplay.cpp +++ b/src/MoreNuclideInfoDisplay.cpp @@ -48,8 +48,9 @@ #include "InterSpec/InterSpec.h" #include "InterSpec/PhysicalUnits.h" #include "InterSpec/DecayChainChart.h" -#include "InterSpec/UndoRedoManager.h" #include "InterSpec/MoreNuclideInfo.h" +#include "InterSpec/UndoRedoManager.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/DecayDataBaseServer.h" #include "InterSpec/MoreNuclideInfoDisplay.h" #include "InterSpec/ReferencePhotopeakDisplay.h" @@ -368,7 +369,7 @@ void MoreNuclideInfoDisplay::setNuclide( const SandiaDecay::Nuclide *const nuc, if( !db ) throw runtime_error( "Error getting DecayDataBaseServer" ); - const bool useBq = InterSpecUser::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); + const bool useBq = UserPreferences::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); tmplt.setCondition( "display-title", m_displayTitle ); tmplt.setCondition( "if-have-prev", !history.empty() ); diff --git a/src/MultimediaDisplay.cpp b/src/MultimediaDisplay.cpp index 400812f2..40e582f4 100644 --- a/src/MultimediaDisplay.cpp +++ b/src/MultimediaDisplay.cpp @@ -41,6 +41,7 @@ #include "InterSpec/InterSpec.h" #include "InterSpec/SimpleDialog.h" #include "InterSpec/InterSpecUser.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/MultimediaDisplay.h" using namespace std; @@ -147,7 +148,7 @@ class MultimediaDisplay : public WContainerWidget InterSpec *interspec = InterSpec::instance(); assert( interspec ); if( interspec ) - InterSpecUser::associateWidget( interspec->m_user, "AutoShowSpecMultimedia", cb, interspec ); + UserPreferences::associateWidget( "AutoShowSpecMultimedia", cb, interspec ); #if( BUILD_AS_OSX_APP || IOS ) m_download = new WAnchor( WLink(m_resource), footer ); diff --git a/src/PeakInfoDisplay.cpp b/src/PeakInfoDisplay.cpp index 52588fe8..a65a4c9e 100644 --- a/src/PeakInfoDisplay.cpp +++ b/src/PeakInfoDisplay.cpp @@ -66,6 +66,7 @@ #include "InterSpec/WarningWidget.h" #include "InterSpec/PeakInfoDisplay.h" #include "InterSpec/UndoRedoManager.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/AddNewPeakDialog.h" #include "InterSpec/RowStretchTreeView.h" #include "InterSpec/PeakSearchGuiUtils.h" @@ -851,7 +852,7 @@ void PeakInfoDisplay::init() assert( !m_infoView ); assert( !m_infoLayout ); - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", m_viewer ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", m_viewer ); m_infoLayout = new WGridLayout(); setLayout( m_infoLayout ); diff --git a/src/ReferencePhotopeakDisplay.cpp b/src/ReferencePhotopeakDisplay.cpp index 89f1805f..1da17d00 100644 --- a/src/ReferencePhotopeakDisplay.cpp +++ b/src/ReferencePhotopeakDisplay.cpp @@ -69,6 +69,7 @@ #include "InterSpec/ShieldingSelect.h" #include "InterSpec/SpecMeasManager.h" #include "InterSpec/UndoRedoManager.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/ReferenceLineInfo.h" #include "InterSpec/NativeFloatSpinBox.h" #include "InterSpec/RowStretchTreeView.h" @@ -873,7 +874,7 @@ ReferencePhotopeakDisplay::ReferencePhotopeakDisplay( if( !chart ) throw runtime_error( "ReferencePhotopeakDisplay: a valid chart must be passed in" ); - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", specViewer ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", specViewer ); //The inputDiv/Layout is the left side of the widget that holds all the // nuclide input,age, color picker, DRF, etc @@ -1152,11 +1153,11 @@ ReferencePhotopeakDisplay::ReferencePhotopeakDisplay( m_showFeatureMarkers->unChecked().connect( this, &ReferencePhotopeakDisplay::featureMarkerCbToggled ); - //const bool showToolTips = InterSpecUser::preferenceValue("ShowTooltips", this); + //const bool showToolTips = UserPreferences::preferenceValue("ShowTooltips", this); //HelpSystem::attachToolTipOn(m_showPrevNucs, "Show ", showToolTips); - InterSpecUser::associateWidget( specViewer->m_user, "RefLineShowPrev", m_showPrevNucs, specViewer ); - InterSpecUser::associateWidget( specViewer->m_user, "RefLineShowRiid", m_showRiidNucs, specViewer ); - InterSpecUser::associateWidget( specViewer->m_user, "RefLineShowAssoc", m_showAssocNucs, specViewer ); + UserPreferences::associateWidget( "RefLineShowPrev", m_showPrevNucs, specViewer ); + UserPreferences::associateWidget( "RefLineShowRiid", m_showRiidNucs, specViewer ); + UserPreferences::associateWidget( "RefLineShowAssoc", m_showAssocNucs, specViewer ); //HelpSystem::attachToolTipOn(m_options, "If checked, selection will be shown.", diff --git a/src/RelActAutoGui.cpp b/src/RelActAutoGui.cpp index 56e9e8c0..826d4f8c 100644 --- a/src/RelActAutoGui.cpp +++ b/src/RelActAutoGui.cpp @@ -76,6 +76,7 @@ #include "InterSpec/RelActCalcAuto.h" #include "InterSpec/SwitchCheckbox.h" #include "InterSpec/UndoRedoManager.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/RelActTxtResults.h" #include "InterSpec/NativeFloatSpinBox.h" #include "InterSpec/DecayDataBaseServer.h" @@ -295,7 +296,7 @@ namespace wApp->useStyleSheet( "InterSpec_resources/GridLayoutHelpers.css" ); - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", InterSpec::instance() ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", InterSpec::instance() ); WLabel *label = new WLabel( "Lower Energy", this ); label->addStyleClass( "GridFirstCol GridFirstRow" ); @@ -549,7 +550,7 @@ namespace { addStyleClass( "RelActAutoNuclide" ); - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", InterSpec::instance() ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", InterSpec::instance() ); WLabel *label = new WLabel( "Nuclide:", this ); m_nuclide_edit = new WLineEdit( "", this ); @@ -952,7 +953,7 @@ namespace { addStyleClass( "RelActFreePeak" ); - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", InterSpec::instance() ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", InterSpec::instance() ); WLabel *label = new WLabel( "Energy", this ); label->addStyleClass( "GridFirstCol GridFirstRow" ); @@ -1245,7 +1246,7 @@ RelActAutoGui::RelActAutoGui( InterSpec *viewer, Wt::WContainerWidget *parent ) addStyleClass( "RelActAutoGui" ); - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", m_interspec ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", m_interspec ); WText *alpha_warning = new WText( "This tool is under active development - this is an early preview", this ); alpha_warning->addStyleClass( "RelActCalcAutoAlphaBuildWarning" ); @@ -1269,7 +1270,7 @@ RelActAutoGui::RelActAutoGui( InterSpec *viewer, Wt::WContainerWidget *parent ) m_interspec->colorThemeChanged().connect( boost::bind( &D3SpectrumDisplayDiv::applyColorTheme, m_spectrum, boost::placeholders::_1 ) ); m_spectrum->applyColorTheme( m_interspec->getColorTheme() ); - const bool logypref = InterSpecUser::preferenceValue( "LogY", m_interspec ); + const bool logypref = UserPreferences::preferenceValue( "LogY", m_interspec ); m_spectrum->setYAxisLog( logypref ); auto set_log_y = wApp->bind( boost::bind( &D3SpectrumDisplayDiv::setYAxisLog, m_spectrum, true ) ); @@ -1281,8 +1282,8 @@ RelActAutoGui::RelActAutoGui( InterSpec *viewer, Wt::WContainerWidget *parent ) set_lin_y(); }; - InterSpecUser::addCallbackWhenChanged( m_interspec->m_user, m_interspec, - "LogY", m_spectrum, &D3SpectrumDisplayDiv::setYAxisLog ); + m_interspec->preferences()->addCallbackWhenChanged( "LogY", m_spectrum, + &D3SpectrumDisplayDiv::setYAxisLog ); m_peak_model = new PeakModel( m_spectrum ); m_peak_model->setNoSpecMeasBacking(); @@ -3820,7 +3821,7 @@ void RelActAutoGui::setPeaksToForeground() refit_holder->addStyleClass( "AddOrReplaceRefitRow" ); WCheckBox *refit_peaks = new WCheckBox( "Refit Peaks", refit_holder ); - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", InterSpec::instance() ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", InterSpec::instance() ); const char *tooltip = "When checked, peaks will be refit without the constraints of the relative efficiency curve," " expected branching ratios, or FWHM constraints from other ROIs - allowing statistical" diff --git a/src/RelActCalcManual.cpp b/src/RelActCalcManual.cpp index ada70b71..c6c71847 100644 --- a/src/RelActCalcManual.cpp +++ b/src/RelActCalcManual.cpp @@ -53,6 +53,7 @@ #include "InterSpec/RelActCalc.h" #include "InterSpec/PhysicalUnits.h" #include "InterSpec/ReactionGamma.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/RelActCalcManual.h" #include "InterSpec/DecayDataBaseServer.h" #include "InterSpec/GammaInteractionCalc.h" @@ -1223,6 +1224,14 @@ double RelEffSolution::activity_ratio_uncert( const size_t iso1, const size_t is assert( iso2 < m_rel_act_covariance.size() ); assert( m_rel_act_covariance.size() == m_activity_norms.size() ); + if( (iso1 == iso2) + || (iso1 >= m_rel_act_covariance.size()) + || (iso2 >= m_rel_act_covariance.size()) ) + { + //throw runtime_error( "RelEffSolution::activity_ratio_uncert: invalid iso number" ); + return -1; + } + const double norm_1 = m_activity_norms[iso1]; const double norm_2 = m_activity_norms[iso2]; diff --git a/src/RelActManualGui.cpp b/src/RelActManualGui.cpp index cdf0d01d..769b38dc 100644 --- a/src/RelActManualGui.cpp +++ b/src/RelActManualGui.cpp @@ -74,6 +74,7 @@ #include "InterSpec/RelActCalcAuto.h" #include "InterSpec/RelActManualGui.h" #include "InterSpec/UndoRedoManager.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/RelActCalcManual.h" #include "InterSpec/RowStretchTreeView.h" #include "InterSpec/NativeFloatSpinBox.h" @@ -329,7 +330,7 @@ class ManRelEffNucDisp : public Wt::WPanel label = new WLabel( WString::tr("mrend-spec-act"), cell ); cell = m_nucContentTable->elementAt(2, 1); - const bool useCurie = !InterSpecUser::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); + const bool useCurie = !UserPreferences::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); const double specificActivity = nuc->activityPerGram() / PhysicalUnits::gram; const string sa = PhysicalUnits::printToBestSpecificActivityUnits( specificActivity, 3, useCurie ); txt = new WText( sa, cell ); @@ -391,7 +392,7 @@ class ManRelEffNucDisp : public Wt::WPanel m_decay_during_meas->checked().connect( this, &ManRelEffNucDisp::handleDecayDuringMeasurementChanged ); m_decay_during_meas->unChecked().connect( this, &ManRelEffNucDisp::handleDecayDuringMeasurementChanged ); - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", InterSpec::instance() ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", InterSpec::instance() ); HelpSystem::attachToolTipOn( m_decay_during_meas, WString::tr("mrend-tt-decay-during-meas"), showToolTips ); @@ -536,7 +537,7 @@ void RelActManualGui::init() delete m_layout; m_layout = new WGridLayout( this ); - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", m_interspec ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", m_interspec ); m_layout->setContentsMargins( 0, 0, 0, 0 ); m_layout->setVerticalSpacing( 0 ); diff --git a/src/RemoteRid.cpp b/src/RemoteRid.cpp index 731ffc4f..c8e04444 100644 --- a/src/RemoteRid.cpp +++ b/src/RemoteRid.cpp @@ -83,6 +83,7 @@ #include "InterSpec/PhysicalUnits.h" #include "InterSpec/WarningWidget.h" #include "InterSpec/UndoRedoManager.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/DecayDataBaseServer.h" #include "InterSpec/ReferencePhotopeakDisplay.h" @@ -428,7 +429,7 @@ class ExternalRidWidget : public Wt::WContainerWidget break; }//switch( m_type ) - const string prev_urls = InterSpecUser::preferenceValue( prefname, m_interspec ); + const string prev_urls = UserPreferences::preferenceValue( prefname, m_interspec ); vector prev; SpecUtils::split( prev, prev_urls, ";" ); @@ -446,7 +447,7 @@ class ExternalRidWidget : public Wt::WContainerWidget new_url += (i ? ";" : "") + prev[i]; } - InterSpecUser::setPreferenceValue( m_interspec->m_user, prefname, new_url, m_interspec ); + UserPreferences::setPreferenceValue( prefname, new_url, m_interspec ); }catch( std::exception &e ) { cerr << "Error in setMostRecentUrl( '" << url << "' ): " << e.what() << endl; @@ -496,7 +497,7 @@ class ExternalRidWidget : public Wt::WContainerWidget try { - const string prev_urls = InterSpecUser::preferenceValue( prefname, interspec ); + const string prev_urls = UserPreferences::preferenceValue( prefname, interspec ); vector prev; SpecUtils::split( prev, prev_urls, ";" ); #if( !ANDROID && !IOS && !BUILD_FOR_WEB_DEPLOYMENT ) @@ -784,7 +785,7 @@ class ExternalRidWidget : public Wt::WContainerWidget try { - InterSpecUser::setPreferenceValue( m_interspec->m_user, "AlwaysCallExternalRid", + UserPreferences::setPreferenceValue( "AlwaysCallExternalRid", static_cast(pref_value), m_interspec ); }catch( std::exception & ) { @@ -1547,7 +1548,7 @@ class ExternalRidWidget : public Wt::WContainerWidget static void generateResultHtml( WStringStream &rslttxt, const FullSpecResults &results ) { - const bool useBq = InterSpecUser::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); + const bool useBq = UserPreferences::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); rslttxt << "\n" << "\t" @@ -2233,7 +2234,7 @@ ExternalRidAuotCallPref RemoteRid::external_rid_call_pref( InterSpec *viewer ) if( !viewer ) throw runtime_error( "Invalid InterSpec instance" ); - const int pref_value = InterSpecUser::preferenceValue( "AlwaysCallExternalRid", viewer ); + const int pref_value = UserPreferences::preferenceValue( "AlwaysCallExternalRid", viewer ); const auto pref = static_cast( pref_value ); bool valid = false; @@ -2269,7 +2270,7 @@ SimpleDialog *RemoteRid::startRemoteRidDialog( InterSpec *viewer, if( !viewer ) return nullptr; - const bool showWarning = InterSpecUser::preferenceValue( "ExternalRidWarn", viewer ); + const bool showWarning = UserPreferences::preferenceValue( "ExternalRidWarn", viewer ); if( !showWarning ) { @@ -2310,7 +2311,7 @@ SimpleDialog *RemoteRid::startRemoteRidDialog( InterSpec *viewer, btn = dialog->addButton( "Continue" ); btn->clicked().connect( std::bind([viewer,callback,cb](){ if( cb->isChecked() ) - InterSpecUser::setPreferenceValue(viewer->m_user, "ExternalRidWarn", false, viewer ); + UserPreferences::setPreferenceValue("ExternalRidWarn", false, viewer ); auto res = RemoteRid::createDialog( viewer ); if( callback ) @@ -2671,7 +2672,7 @@ void RemoteRid::disableAutoRemoteRid( InterSpec *interspec ) try { - InterSpecUser::setPreferenceValue(interspec->m_user, "AlwaysCallExternalRid", 0, interspec); + UserPreferences::setPreferenceValue( "AlwaysCallExternalRid", 0, interspec); }catch(...) { assert( 0 ); @@ -2788,10 +2789,10 @@ void RemoteRid::handleAppUrl( std::string query_str ) assert( interspec ); if( !interspec ) return; - InterSpecUser::setPreferenceValue( interspec->m_user, "AlwaysCallExternalRid", static_cast(0), interspec ); - InterSpecUser::setPreferenceValue( interspec->m_user, "ExternalRidWarn", true, interspec ); - InterSpecUser::setPreferenceValue( interspec->m_user, "ExternalRidUrl", string(), interspec ); - InterSpecUser::setPreferenceValue( interspec->m_user, "ExternalRidExe", string(), interspec ); + UserPreferences::setPreferenceValue( "AlwaysCallExternalRid", static_cast(0), interspec ); + UserPreferences::setPreferenceValue( "ExternalRidWarn", true, interspec ); + UserPreferences::setPreferenceValue( "ExternalRidUrl", string(), interspec ); + UserPreferences::setPreferenceValue( "ExternalRidExe", string(), interspec ); passMessage( "External-RID preferences have been reset.", WarningWidget::WarningMsgInfo ); }) ); dialog->addButton( "Cancel" ); @@ -2853,12 +2854,12 @@ void RemoteRid::handleAppUrl( std::string query_str ) }//if( !url_path.empty() ) / else }//if( always_call ) - InterSpecUser::setPreferenceValue( interspec->m_user, "AlwaysCallExternalRid", static_cast(pref_value), interspec ); - //InterSpecUser::setPreferenceValue( interspec->m_user, "ExternalRidWarn", false, interspec ); + UserPreferences::setPreferenceValue( "AlwaysCallExternalRid", static_cast(pref_value), interspec ); + //UserPreferences::setPreferenceValue( "ExternalRidWarn", false, interspec ); if( !url_path.empty() ) - InterSpecUser::setPreferenceValue( interspec->m_user, "ExternalRidUrl", url_path, interspec ); + UserPreferences::setPreferenceValue( "ExternalRidUrl", url_path, interspec ); if( !exe_path.empty() ) - InterSpecUser::setPreferenceValue( interspec->m_user, "ExternalRidExe", exe_path, interspec ); + UserPreferences::setPreferenceValue( "ExternalRidExe", exe_path, interspec ); passMessage( "External-RID preferences have been updated.", WarningWidget::WarningMsgInfo ); };//set_to_new_prefs @@ -2870,27 +2871,27 @@ void RemoteRid::handleAppUrl( std::string query_str ) try { - prev_always_call = InterSpecUser::preferenceValue( "AlwaysCallExternalRid", interspec ); + prev_always_call = UserPreferences::preferenceValue( "AlwaysCallExternalRid", interspec ); }catch( std::exception & ){ cerr << "Exception getting AlwaysCallExternalRid." << endl; } try { - prev_url_path = InterSpecUser::preferenceValue( "ExternalRidUrl", interspec ); + prev_url_path = UserPreferences::preferenceValue( "ExternalRidUrl", interspec ); }catch( std::exception & ){ cerr << "Exception getting ExternalRidUrl" << endl; } try { - prev_exe_path = InterSpecUser::preferenceValue( "ExternalRidExe", interspec ); + prev_exe_path = UserPreferences::preferenceValue( "ExternalRidExe", interspec ); }catch( std::exception & ){ cerr << "Exception getting ExternalRidExe." << endl; } const auto set_to_old_prefs = [prev_always_call, prev_url_path, prev_exe_path](){ InterSpec *interspec = InterSpec::instance(); assert( interspec ); - InterSpecUser::setPreferenceValue( interspec->m_user, "AlwaysCallExternalRid", prev_always_call, interspec ); - //InterSpecUser::setPreferenceValue( interspec->m_user, "ExternalRidWarn", false, interspec ); - InterSpecUser::setPreferenceValue( interspec->m_user, "ExternalRidUrl", prev_url_path, interspec ); - InterSpecUser::setPreferenceValue( interspec->m_user, "ExternalRidExe", prev_exe_path, interspec ); + UserPreferences::setPreferenceValue( "AlwaysCallExternalRid", prev_always_call, interspec ); + //UserPreferences::setPreferenceValue( "ExternalRidWarn", false, interspec ); + UserPreferences::setPreferenceValue( "ExternalRidUrl", prev_url_path, interspec ); + UserPreferences::setPreferenceValue( "ExternalRidExe", prev_exe_path, interspec ); passMessage( "External-RID preferences have been reverted back to original.", WarningWidget::WarningMsgInfo ); };//set_to_old_prefs diff --git a/src/SearchMode3DChart.cpp b/src/SearchMode3DChart.cpp index 7b64d902..5b603e10 100644 --- a/src/SearchMode3DChart.cpp +++ b/src/SearchMode3DChart.cpp @@ -51,6 +51,7 @@ #include "InterSpec/InterSpec.h" #include "InterSpec/HelpSystem.h" #include "InterSpec/InterSpecUser.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/SearchMode3DChart.h" #include "InterSpec/NativeFloatSpinBox.h" #include "InterSpec/SearchMode3DDataModel.h" @@ -102,7 +103,7 @@ void SearchMode3DChart::init() if( m_viewer ) m_viewer->useMessageResourceBundle( "SearchMode3DChart" ); - const bool showToolTips = InterSpecUser::preferenceValue("ShowTooltips", InterSpec::instance()); + const bool showToolTips = UserPreferences::preferenceValue("ShowTooltips", InterSpec::instance()); m_layout = new WGridLayout(); m_layout->setContentsMargins( 9, 9, 9, 0 ); //left, top, right, bottom (default is 9 pixels on each side) diff --git a/src/ShieldingSelect.cpp b/src/ShieldingSelect.cpp index 4950b2e2..07f56e19 100644 --- a/src/ShieldingSelect.cpp +++ b/src/ShieldingSelect.cpp @@ -64,6 +64,7 @@ #include "InterSpec/PhysicalUnits.h" #include "InterSpec/ShieldingSelect.h" #include "InterSpec/UndoRedoManager.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/NativeFloatSpinBox.h" #include "InterSpec/MassAttenuationTool.h" #include "InterSpec/DecayDataBaseServer.h" @@ -148,7 +149,7 @@ class TraceSrcDisplay : public WGroupBox { wApp->useStyleSheet( "InterSpec_resources/GridLayoutHelpers.css" ); - const bool useBq = InterSpecUser::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); + const bool useBq = UserPreferences::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); assert( !parent->isGenericMaterial() ); addStyleClass( "TraceSrcDisplay" ); @@ -328,7 +329,7 @@ class TraceSrcDisplay : public WGroupBox handleUserNuclideChange(); - const bool useCi = !InterSpecUser::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); + const bool useCi = !UserPreferences::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); const string acttxt = PhysicalUnits::printToBestActivityUnits( trace.m_activity, 6, useCi ); m_activityInput->setText( WString::fromUTF8(acttxt) ); @@ -400,7 +401,7 @@ class TraceSrcDisplay : public WGroupBox if( nuc == m_currentNuclide ) { - const bool useCi = !InterSpecUser::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); + const bool useCi = !UserPreferences::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); m_isoSelect->removeItem( m_isoSelect->currentIndex() ); m_isoSelect->setCurrentIndex( 0 ); @@ -426,7 +427,7 @@ class TraceSrcDisplay : public WGroupBox void handleUserActivityChange() { - const bool useCi = !InterSpecUser::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); + const bool useCi = !UserPreferences::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); if( m_parent->isGenericMaterial() ) { @@ -622,7 +623,7 @@ class TraceSrcDisplay : public WGroupBox void updateAvailableActivityTypes() { - const bool useCi = !InterSpecUser::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); + const bool useCi = !UserPreferences::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); const int previous = m_activityType->currentIndex(); assert( previous < static_cast(TraceActivityType::NumTraceActivityType) ); @@ -699,7 +700,7 @@ class TraceSrcDisplay : public WGroupBox /** We will keep total activity the same, but update the display activity based on current value of m_activityType. */ void updateDispActivityFromTotalActivity() { - const bool useCi = !InterSpecUser::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); + const bool useCi = !UserPreferences::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); const int currentIndex = m_activityType->currentIndex(); const double shieldVolume = m_parent->shieldingVolume(); @@ -753,7 +754,7 @@ class TraceSrcDisplay : public WGroupBox void updateTotalActivityFromDisplayActivity() { - const bool useCi = !InterSpecUser::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); + const bool useCi = !UserPreferences::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); if( !m_currentNuclide ) @@ -823,7 +824,7 @@ class TraceSrcDisplay : public WGroupBox if( m_currentNuclide ) { m_currentTotalActivity = m_currentDisplayActivity = 0.0; - const bool useCi = !InterSpecUser::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); + const bool useCi = !UserPreferences::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); m_activityInput->setText( (useCi ? "0 uCi" : "0 bq") ); //const SandiaDecay::Nuclide * const oldNuc = m_currentNuclide; @@ -838,7 +839,7 @@ class TraceSrcDisplay : public WGroupBox shared_ptr mat = m_parent->material(); if( !mat ) { - const bool useBq = InterSpecUser::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); + const bool useBq = UserPreferences::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); m_currentDisplayActivity = m_currentTotalActivity = 0.0; m_activityInput->setText( (useBq ? "0 Bq" : "0 uCi") ); @@ -965,7 +966,7 @@ class TraceSrcDisplay : public WGroupBox m_currentDisplayActivity = act; - const bool useCi = !InterSpecUser::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); + const bool useCi = !UserPreferences::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); const string actstr = PhysicalUnits::printToBestActivityUnits(act,4,useCi); m_activityInput->setValueText( WString::fromUTF8(actstr) ); @@ -2030,7 +2031,7 @@ void ShieldingSelect::init() //TODO/NOTE: had to hard code this as false because there is no way //to easily get the preference via InterSpec because //is still initializing when calling at this moment. - const bool showToolTips = interspec ? InterSpecUser::preferenceValue( "ShowTooltips", interspec ) : false; + const bool showToolTips = interspec ? UserPreferences::preferenceValue( "ShowTooltips", interspec ) : false; if( interspec ) interspec->useMessageResourceBundle( "ShieldingSelect" ); @@ -4327,7 +4328,7 @@ void ShieldingSelect::handleMaterialChange() //NOTE: can't add tooltip to this, causes WT error when toggling. Can't fix. // InterSpecApp *app = dynamic_cast( wApp ); -// const bool showToolTips = true;//InterSpecUser::preferenceValue( "ShowTooltips", app->viewer() ); +// const bool showToolTips = true;//UserPreferences::preferenceValue( "ShowTooltips", app->viewer() ); // HelpSystem::attachToolTipOn( this,tooltip, showToolTips ); m_materialSummary->setText( summary ); diff --git a/src/ShieldingSourceDisplay.cpp b/src/ShieldingSourceDisplay.cpp index 7c816e30..2254e22b 100644 --- a/src/ShieldingSourceDisplay.cpp +++ b/src/ShieldingSourceDisplay.cpp @@ -103,6 +103,7 @@ #include "InterSpec/ShieldingSelect.h" #include "InterSpec/SpecMeasManager.h" #include "InterSpec/UndoRedoManager.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/RowStretchTreeView.h" #include "InterSpec/NativeFloatSpinBox.h" #include "InterSpec/MassAttenuationTool.h" @@ -375,8 +376,8 @@ SourceFitModel::SourceFitModel( PeakModel *peakModel, }else { interspec->useMessageResourceBundle( "ShieldingSourceDisplay" ); //jic - m_displayCuries = !InterSpecUser::preferenceValue( "DisplayBecquerel", interspec ); - InterSpecUser::addCallbackWhenChanged( interspec->m_user, interspec, "DisplayBecquerel", + m_displayCuries = !UserPreferences::preferenceValue( "DisplayBecquerel", interspec ); + interspec->preferences()->addCallbackWhenChanged( "DisplayBecquerel", this, &SourceFitModel::displayUnitsChanged ); }//if( !interspec ) / else @@ -2875,7 +2876,7 @@ ShieldingSourceDisplay::ShieldingSourceDisplay( PeakModel *peakModel, assert( m_specViewer ); m_specViewer->useMessageResourceBundle( "ShieldingSourceDisplay" ); - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", m_specViewer ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", m_specViewer ); setLayoutSizeAware( true ); const bool isotopesHaveSameAge = true; @@ -4339,7 +4340,7 @@ void ShieldingSourceDisplay::setFitQuantitiesToDefaultValues() if( m_sourceModel->fitActivity(i) ) { WModelIndex index = m_sourceModel->index( i, SourceFitModel::kActivity ); - const bool useCi = !InterSpecUser::preferenceValue( "DisplayBecquerel", m_specViewer ); + const bool useCi = !UserPreferences::preferenceValue( "DisplayBecquerel", m_specViewer ); if( useCi ) m_sourceModel->setData( index, "1 mCi" ); else @@ -6266,7 +6267,7 @@ void ShieldingSourceDisplay::closeBrowseDatabaseModelsWindow() void ShieldingSourceDisplay::startBrowseDatabaseModels() { - if( !m_specViewer || !m_specViewer->m_user ) + if( !m_specViewer || !m_specViewer->user() ) throw runtime_error( "startBrowseDatabaseModels(): invalid user" ); if( m_modelDbBrowseWindow ) @@ -6306,7 +6307,7 @@ void ShieldingSourceDisplay::startBrowseDatabaseModels() std::shared_ptr sql = m_specViewer->sql(); DataBaseUtils::DbTransaction transaction( *sql ); nfileprev[0] = dbmeas ? dbmeas->modelsUsedWith.size() : 0; - nfileprev[1] = m_specViewer->m_user->shieldSrcModels().size(); + nfileprev[1] = m_specViewer->user()->shieldSrcModels().size(); transaction.commit(); }//end codeblock for database interaction @@ -6322,7 +6323,7 @@ void ShieldingSourceDisplay::startBrowseDatabaseModels() if( i == 0 ) model->setQuery( dbmeas->modelsUsedWith.find() ); else - model->setQuery( m_specViewer->m_user->shieldSrcModels().find() ); + model->setQuery( m_specViewer->user()->shieldSrcModels().find() ); model->addColumn( "Name" ); selection->setModel( model ); selection->setModelColumn( 0 ); @@ -6683,7 +6684,7 @@ bool ShieldingSourceDisplay::finishSaveModelToDatabase( const Wt::WString &name, if( !m_modelInDb ) { model = new ShieldingSourceModel(); - model->user = m_specViewer->m_user; + model->user = m_specViewer->user(); model->serializeTime = WDateTime::currentDateTime(); m_modelInDb.reset( new ShieldingSourceModel() ); m_modelInDb = sql->session()->add( model ); @@ -6746,7 +6747,7 @@ void ShieldingSourceDisplay::saveCloneModelToDatabase() ShieldingSourceModel *model = new ShieldingSourceModel(); model->shallowEquals( *m_modelInDb ); - model->user = m_specViewer->m_user; + model->user = m_specViewer->user(); model->serializeTime = WDateTime::currentDateTime(); model->name = model->name + " Clone"; @@ -8683,7 +8684,7 @@ void ShieldingSourceDisplay::updateCalcLogWithFitResults( const SandiaDecay::Nuclide *nuc = chi2Fcn->nuclide( nucn ); if( nuc ) { - const bool useCi = !InterSpecUser::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); + const bool useCi = !UserPreferences::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); const double act = chi2Fcn->activity( nuc, params ); const string actStr = PhysicalUnits::printToBestActivityUnits( act, 2, useCi ); diff --git a/src/ShowRiidInstrumentsAna.cpp b/src/ShowRiidInstrumentsAna.cpp index c6e0e0b5..95145be3 100644 --- a/src/ShowRiidInstrumentsAna.cpp +++ b/src/ShowRiidInstrumentsAna.cpp @@ -40,6 +40,7 @@ #include "InterSpec/SimpleDialog.h" #include "InterSpec/PhysicalUnits.h" #include "InterSpec/InterSpecUser.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/ShowRiidInstrumentsAna.h" using namespace std; @@ -295,7 +296,7 @@ class AnaResultDisplay : public WContainerWidget + ""; if( res.activity_ > 0.0 ) { - const bool useBq = InterSpecUser::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); + const bool useBq = UserPreferences::preferenceValue( "DisplayBecquerel", InterSpec::instance() ); result += ""; } diff --git a/src/SpecFileQueryWidget.cpp b/src/SpecFileQueryWidget.cpp index bdc08ae4..e810e9d2 100644 --- a/src/SpecFileQueryWidget.cpp +++ b/src/SpecFileQueryWidget.cpp @@ -66,20 +66,21 @@ #include #include "SpecUtils/DateTime.h" +#include "SpecUtils/StringAlgo.h" +#include "SpecUtils/Filesystem.h" +#include "SpecUtils/SpecUtilsAsync.h" +#include "SpecUtils/EnergyCalibration.h" + #include "InterSpec/SpecMeas.h" #include "InterSpec/PopupDiv.h" #include "InterSpec/InterSpec.h" -#include "SpecUtils/StringAlgo.h" -#include "SpecUtils/Filesystem.h" #include "InterSpec/HelpSystem.h" #include "InterSpec/InterSpecApp.h" #include "InterSpec/WarningWidget.h" #include "InterSpec/SpecFileQuery.h" #include "InterSpec/PhysicalUnits.h" -#include "SpecUtils/SpecUtilsAsync.h" #include "InterSpec/SpecMeasManager.h" - -#include "SpecUtils/EnergyCalibration.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/RowStretchTreeView.h" #include "InterSpec/SpecFileQueryWidget.h" #include "InterSpec/DecayDataBaseServer.h" @@ -1507,12 +1508,12 @@ void SpecFileQueryWidget::init() { try { - dofilter = InterSpecUser::preferenceValue( "SpecFileQueryFilter", m_viewer ); - docache = InterSpecUser::preferenceValue( "SpecFileQueryCacheParse", m_viewer ); - dorecursive = InterSpecUser::preferenceValue( "SpecFileQueryRecursive", m_viewer ); - maxsize = InterSpecUser::preferenceValue( "SpecFileQueryMaxSize", m_viewer ); - defpath = InterSpecUser::preferenceValue( "SpecFileQueryPath", m_viewer ); - instantToolTip = InterSpecUser::preferenceValue( "ShowTooltips", m_viewer ); + dofilter = UserPreferences::preferenceValue( "SpecFileQueryFilter", m_viewer ); + docache = UserPreferences::preferenceValue( "SpecFileQueryCacheParse", m_viewer ); + dorecursive = UserPreferences::preferenceValue( "SpecFileQueryRecursive", m_viewer ); + maxsize = UserPreferences::preferenceValue( "SpecFileQueryMaxSize", m_viewer ); + defpath = UserPreferences::preferenceValue( "SpecFileQueryPath", m_viewer ); + instantToolTip = UserPreferences::preferenceValue( "ShowTooltips", m_viewer ); if( defpath == "None" ) @@ -2181,15 +2182,15 @@ void SpecFileQueryWidget::basePathChanged() bool prevrecursive = true, prevfilter = true, prevFilterUnique = true, prevcache = true; try { - prevmaxsize = InterSpecUser::preferenceValue( "SpecFileQueryMaxSize", m_viewer ); - prevcache = InterSpecUser::preferenceValue( "SpecFileQueryCacheParse", m_viewer ); - prevfilter = InterSpecUser::preferenceValue( "SpecFileQueryFilter", m_viewer ); - prevrecursive = InterSpecUser::preferenceValue( "SpecFileQueryRecursive", m_viewer ); - prevFilterUnique = InterSpecUser::preferenceValue( "SpecFileQueryUnique", m_viewer ); + prevmaxsize = UserPreferences::preferenceValue( "SpecFileQueryMaxSize", m_viewer ); + prevcache = UserPreferences::preferenceValue( "SpecFileQueryCacheParse", m_viewer ); + prevfilter = UserPreferences::preferenceValue( "SpecFileQueryFilter", m_viewer ); + prevrecursive = UserPreferences::preferenceValue( "SpecFileQueryRecursive", m_viewer ); + prevFilterUnique = UserPreferences::preferenceValue( "SpecFileQueryUnique", m_viewer ); #if( BUILD_AS_OSX_APP || BUILD_AS_ELECTRON_APP ) prefpath = ""; #else - prefpath = InterSpecUser::preferenceValue( "SpecFileQueryPath", m_viewer ); + prefpath = UserPreferences::preferenceValue( "SpecFileQueryPath", m_viewer ); if( prefpath == "None" ) prefpath = ""; #endif @@ -2203,18 +2204,18 @@ void SpecFileQueryWidget::basePathChanged() try { if( prevrecursive != recursive ) - InterSpecUser::setPreferenceValue( m_viewer->m_user, "SpecFileQueryRecursive", recursive, m_viewer ); + UserPreferences::setPreferenceValue( "SpecFileQueryRecursive", recursive, m_viewer ); if( prevcache != docache ) - InterSpecUser::setPreferenceValue( m_viewer->m_user, "SpecFileQueryCacheParse", docache, m_viewer ); + UserPreferences::setPreferenceValue( "SpecFileQueryCacheParse", docache, m_viewer ); if( prevfilter != filter ) - InterSpecUser::setPreferenceValue( m_viewer->m_user, "SpecFileQueryFilter", filter, m_viewer ); + UserPreferences::setPreferenceValue( "SpecFileQueryFilter", filter, m_viewer ); if( maxsize != prevmaxsize ) - InterSpecUser::setPreferenceValue( m_viewer->m_user, "SpecFileQueryFilter", maxsize, m_viewer ); + UserPreferences::setPreferenceValue( "SpecFileQueryFilter", maxsize, m_viewer ); if( filterUnique != prevFilterUnique ) - InterSpecUser::setPreferenceValue( m_viewer->m_user, "SpecFileQueryUnique", filterUnique, m_viewer ); + UserPreferences::setPreferenceValue( "SpecFileQueryUnique", filterUnique, m_viewer ); #if( !BUILD_AS_OSX_APP && !BUILD_AS_ELECTRON_APP ) if( prefpath != basepath ) - InterSpecUser::setPreferenceValue( m_viewer->m_user, "SpecFileQueryPath", basepath, m_viewer ); + UserPreferences::setPreferenceValue( "SpecFileQueryPath", basepath, m_viewer ); #endif }catch( ... ) { diff --git a/src/SpecMeasManager.cpp b/src/SpecMeasManager.cpp index af39615d..203b764d 100644 --- a/src/SpecMeasManager.cpp +++ b/src/SpecMeasManager.cpp @@ -129,6 +129,7 @@ #include "InterSpec/ExportSpecFile.h" #include "InterSpec/SpecMeasManager.h" #include "InterSpec/UndoRedoManager.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/SpectraFileModel.h" #include "InterSpec/LocalTimeDelegate.h" #include "InterSpec/PeakSearchGuiUtils.h" @@ -916,7 +917,7 @@ class UploadedImgDisplay : public WContainerWidget SpecMeasManager *fileManager = m_viewer->fileManager(); SpectraFileModel *fileModel = fileManager->model(); - auto header = make_shared( m_viewer->m_user, true, m_viewer ); + auto header = make_shared( true, m_viewer ); header->setFile( display_name, specmeas ); fileManager->addToTempSpectrumInfoCache( specmeas ); const int row = fileModel->addRow( header ); @@ -1409,7 +1410,7 @@ SpecMeasManager::SpecMeasManager( InterSpec *viewer ) //Moved what use to be SpecMeasManager, out to a startSpectrumManager() to correct modal issues void SpecMeasManager::startSpectrumManager() { - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", m_viewer ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", m_viewer ); if( m_spectrumManagerWindow ) { @@ -3249,8 +3250,8 @@ bool SpecMeasManager::handleEccFile( std::istream &input, SimpleDialog *dialog ) if( !new_drf || !interspec ) return; - auto sql = interspec->sql(); - auto user = interspec->m_user; + shared_ptr sql = interspec->sql(); + const Wt::Dbo::ptr &user = interspec->user(); DrfSelect::updateLastUsedTimeOrAddToDb( new_drf, user.id(), sql ); interspec->detectorChanged().emit( new_drf ); //This loads it to the foreground spectrum file @@ -4492,7 +4493,7 @@ void SpecMeasManager::displayFile( int row, #if( USE_DB_TO_STORE_SPECTRA ) const bool storeInDb - = InterSpecUser::preferenceValue( "AutoSaveSpectraToDb", m_viewer ); + = UserPreferences::preferenceValue( "AutoSaveSpectraToDb", m_viewer ); #endif @@ -5709,14 +5710,13 @@ int SpecMeasManager::setDbEntry( Wt::Dbo::ptr dbfile, if( !dbfile ) throw runtime_error( "SpecMeasManager::setDbEntry(...): invalid dbentry" ); - if( enforceUser && (dbfile->user != m_viewer->m_user) ) + if( enforceUser && (dbfile->user != m_viewer->user()) ) throw runtime_error( "SpecMeasManager::setDbEntry(...): invalid user" ); int row = -1; header.reset(); measurement.reset(); - header.reset( new SpectraFileHeader( m_viewer->m_user, - false, m_viewer ) ); + header.reset( new SpectraFileHeader( false, m_viewer ) ); measurement = header->resetFromDatabase( dbfile ); addToTempSpectrumInfoCache( measurement ); row = m_fileModel->addRow( header ); @@ -5796,7 +5796,7 @@ void SpecMeasManager::showPreviousSpecFileUsesDialog( std::shared_ptr( "AutoSaveSpectraToDb", m_viewer ); + //auto_save_states = UserPreferences::preferenceValue( "AutoSaveSpectraToDb", m_viewer ); if( userStatesWithFile.size() ) { @@ -5865,7 +5865,7 @@ void SpecMeasManager::showPreviousSpecFileUsesDialog( std::shared_ptraddWidget( cb, 1, 0 ); layout->setRowStretch( 0, 1 ); - InterSpecUser::associateWidget( m_viewer->m_user, "CheckForPrevOnSpecLoad", cb, m_viewer ); + UserPreferences::associateWidget( "CheckForPrevOnSpecLoad", cb, m_viewer ); const int width = std::min( 500, static_cast(0.95*m_viewer->renderedWidth()) ); @@ -5906,7 +5906,7 @@ void SpecMeasManager::checkIfPreviouslyOpened( const std::string sessionID, try { const bool storeInDb - = InterSpecUser::preferenceValue( "CheckForPrevOnSpecLoad", m_viewer ); + = UserPreferences::preferenceValue( "CheckForPrevOnSpecLoad", m_viewer ); if( !storeInDb ) return; @@ -5914,7 +5914,7 @@ void SpecMeasManager::checkIfPreviouslyOpened( const std::string sessionID, if( !header ) throw runtime_error( "Invalid SpectraFileHeader passed in" ); - if( !m_viewer || !m_viewer->m_user ) + if( !m_viewer || !m_viewer->user() ) throw runtime_error( "Invalid InterSpec or user pointer" ); vector> userStatesWithFile; @@ -5922,12 +5922,9 @@ void SpecMeasManager::checkIfPreviouslyOpened( const std::string sessionID, {//begin interaction with database std::shared_ptr sql = m_viewer->sql(); + const Wt::Dbo::ptr &user = m_viewer->user(); DataBaseUtils::DbTransaction transaction( *sql ); - // Update our in-memory values, since another session may have updated - // things on us. - m_viewer->m_user.reread(); - typedef Dbo::collection< Dbo::ptr > UserFileInDbColl; // UserFileInDbColl files = m_viewer->m_user->userFiles().find() @@ -5935,7 +5932,7 @@ void SpecMeasManager::checkIfPreviouslyOpened( const std::string sessionID, // "AND IsPartOfSaveState = 0" ) // .bind( header->m_uuid ) // .bind( header->m_displayName ); - UserFileInDbColl files = m_viewer->m_user->userFiles().find() + UserFileInDbColl files = user->userFiles().find() .where( "UUID = ? AND IsPartOfSaveState = 0" ) .bind( header->m_uuid ); @@ -5950,12 +5947,12 @@ void SpecMeasManager::checkIfPreviouslyOpened( const std::string sessionID, // Now get user-saved states with this spectrum file in them Dbo::collection< Dbo::ptr > states_query - = SnapshotBrowser::get_user_states_collection( m_viewer->m_user, sql, header ); + = SnapshotBrowser::get_user_states_collection( user, sql, header ); for( auto iter = states_query.begin(); iter != states_query.end(); ++iter ) userStatesWithFile.push_back( *iter ); - m_viewer->m_user.modify()->incrementSpectraFileOpened(); + user.modify()->incrementSpectraFileOpened(); transaction.commit(); }//end interaction with database @@ -6029,7 +6026,7 @@ std::shared_ptr SpecMeasManager::addFile( const std::string & throw runtime_error( "SpecMeasManager::addFile(): invalid input" ); std::shared_ptr header - = std::make_shared( m_viewer->m_user, false, m_viewer ); + = std::make_shared( false, m_viewer ); header->setMeasurmentInfo( measurement ); addToTempSpectrumInfoCache( measurement ); @@ -6053,7 +6050,7 @@ int SpecMeasManager::setFile( const std::string &displayName, measurement.reset(); std::shared_ptr new_header - = std::make_shared( m_viewer->m_user, false, m_viewer ); + = std::make_shared( false, m_viewer ); std::shared_ptr new_measurement = new_header->setFile( displayName, filename, parser_type ); @@ -6171,7 +6168,7 @@ void SpecMeasManager::clearTempSpectrumInfoCache() #if( USE_DB_TO_STORE_SPECTRA ) const bool storeInDb - = InterSpecUser::preferenceValue( "AutoSaveSpectraToDb", m_viewer ); + = UserPreferences::preferenceValue( "AutoSaveSpectraToDb", m_viewer ); #endif for( queue_type::iterator iter = m_tempSpectrumInfoCache.begin(); @@ -6243,7 +6240,7 @@ void SpecMeasManager::removeFromSpectrumInfoCache( std::shared_ptr( "AutoSaveSpectraToDb", m_viewer ); + = UserPreferences::preferenceValue( "AutoSaveSpectraToDb", m_viewer ); if( saveToDisk && storeInDb ) saveToDatabase( meas ); #endif @@ -6277,7 +6274,7 @@ void SpecMeasManager::addToTempSpectrumInfoCache( std::shared_ptr( "AutoSaveSpectraToDb", m_viewer ); + = UserPreferences::preferenceValue( "AutoSaveSpectraToDb", m_viewer ); #endif size_t curr_size = 0; diff --git a/src/SpectraFileModel.cpp b/src/SpectraFileModel.cpp index 99152efc..3a2877fe 100644 --- a/src/SpectraFileModel.cpp +++ b/src/SpectraFileModel.cpp @@ -85,6 +85,7 @@ #include "InterSpec/WarningWidget.h" #include "SpecUtils/SpecUtilsAsync.h" #include "InterSpec/SpecMeasManager.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/SpectraFileModel.h" #include "InterSpec/RowStretchTreeView.h" @@ -224,10 +225,10 @@ SpectraHeader::~SpectraHeader() } -SpectraFileHeader::SpectraFileHeader( Wt::Dbo::ptr user, - bool keepInMemmory, +SpectraFileHeader::SpectraFileHeader( bool keepInMemmory, InterSpec *viewer ) { + assert( viewer ); m_viewer = viewer; m_sql = viewer->sql(); m_fileSystemLocation = ""; @@ -236,7 +237,7 @@ SpectraFileHeader::SpectraFileHeader( Wt::Dbo::ptr user, m_numDetectors = -1; m_hasNeutronDetector = false; m_keepCache = keepInMemmory; - m_user = user; + m_user = viewer->user(); m_modifiedSinceDecode = false; m_candidateForSavingToDb = true; m_app = wApp; @@ -250,7 +251,7 @@ SpectraFileHeader::~SpectraFileHeader() noexcept(true) try { #if( USE_DB_TO_STORE_SPECTRA ) - const bool autosave = InterSpecUser::preferenceValue( "AutoSaveSpectraToDb", m_viewer ); + const bool autosave = UserPreferences::preferenceValue( "AutoSaveSpectraToDb", m_viewer ); #endif string fileSystemLocation; diff --git a/src/TerminalWidget.cpp b/src/TerminalWidget.cpp index 56416d4f..653600f6 100644 --- a/src/TerminalWidget.cpp +++ b/src/TerminalWidget.cpp @@ -38,22 +38,22 @@ #include #include #include +#include #include #include #include #include #include -#include #include #include "InterSpec/SpecMeas.h" #include "InterSpec/AuxWindow.h" -#include "InterSpec/PeakModel.h" -#include "InterSpec/InterSpecUser.h" -#include "InterSpec/TerminalWidget.h" #include "InterSpec/InterSpec.h" -#include "InterSpec/InterSpecApp.h" +#include "InterSpec/PeakModel.h" #include "InterSpec/HelpSystem.h" +#include "InterSpec/InterSpecApp.h" +#include "InterSpec/TerminalWidget.h" +#include "InterSpec/UserPreferences.h" #include "js/TerminalWidget.js" @@ -138,7 +138,7 @@ TerminalWidget::TerminalWidget( InterSpec *viewer, Wt::WContainerWidget *parent m_commandmenu->addWidget( m_commandsearch ); - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", m_viewer ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", m_viewer ); for ( const TerminalModel::CommandHelperTuple s : m_model->commandsFunctionsList() ) { // initialize the command menu with commands const std::string& name = std::get<0>(s), @@ -346,7 +346,7 @@ void TerminalWidget::commandMenuSearchInput() toolTip = std::get<2>( m_commandMenuItems.at(index) ), searchRegex = searchToRegexLiteral( search ); - const bool showToolTips = InterSpecUser::preferenceValue( "ShowTooltips", m_viewer ); + const bool showToolTips = UserPreferences::preferenceValue( "ShowTooltips", m_viewer ); if ( !toolTip.empty() ) HelpSystem::attachToolTipOn( menuItem, toolTip, showToolTips ); diff --git a/src/UnitsConverterTool.cpp b/src/UnitsConverterTool.cpp index 4fd0d0ea..7175ee7f 100644 --- a/src/UnitsConverterTool.cpp +++ b/src/UnitsConverterTool.cpp @@ -45,11 +45,12 @@ #include "InterSpec/AppUtils.h" #include "InterSpec/AuxWindow.h" -#include "InterSpec/InterSpec.h" // Only for preferenceValue("DisplayBecquerel") +#include "InterSpec/InterSpec.h" #include "InterSpec/InterSpecApp.h" -#include "InterSpec/InterSpecUser.h" // Only for preferenceValue("DisplayBecquerel") +#include "InterSpec/InterSpecUser.h" #include "InterSpec/PhysicalUnits.h" #include "InterSpec/UndoRedoManager.h" +#include "InterSpec/UserPreferences.h" // Only for preferenceValue("DisplayBecquerel") #include "InterSpec/UnitsConverterTool.h" #include "InterSpec/DecayDataBaseServer.h" @@ -668,7 +669,7 @@ std::string UnitsConverterTool::convert( std::string val ) bool useCuries = false; InterSpec *viewer = InterSpec::instance(); if( viewer) - useCuries = !InterSpecUser::preferenceValue( "DisplayBecquerel", viewer ); + useCuries = !UserPreferences::preferenceValue( "DisplayBecquerel", viewer ); return PhysicalUnits::printToBestActivityUnits( activity, num_sig_figs(val), useCuries ); }catch(...) diff --git a/src/UseInfoWindow.cpp b/src/UseInfoWindow.cpp index 1381e908..070847e4 100644 --- a/src/UseInfoWindow.cpp +++ b/src/UseInfoWindow.cpp @@ -70,6 +70,7 @@ #include "InterSpec/InterSpecUser.h" #include "InterSpec/DbFileBrowser.h" #include "InterSpec/UndoRedoManager.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/RowStretchTreeView.h" @@ -239,7 +240,7 @@ UseInfoWindow::UseInfoWindow( std::function showAgainCallback, try { #if( USE_DB_TO_STORE_SPECTRA ) - Dbo::ptr user = m_viewer->m_user; + const Dbo::ptr &user = m_viewer->user(); SpecMeasManager *manager = m_viewer->fileManager(); m_snapshotModel = new SnapshotBrowser( manager, m_viewer, nullptr ); @@ -854,7 +855,7 @@ UseInfoWindow::UseInfoWindow( std::function showAgainCallback, try { - const bool showAtStartup = viewer->m_user->preferenceValue( "ShowSplashScreen", viewer ); + const bool showAtStartup = UserPreferences::preferenceValue( "ShowSplashScreen", viewer ); showAgainCb->setChecked( showAtStartup ); }catch(...) { diff --git a/src/UserPreferences.cpp b/src/UserPreferences.cpp new file mode 100644 index 00000000..310e81eb --- /dev/null +++ b/src/UserPreferences.cpp @@ -0,0 +1,829 @@ +/* InterSpec: an application to analyze spectral gamma radiation data. + + Copyright 2018 National Technology & Engineering Solutions of Sandia, LLC + (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. + Government retains certain rights in this software. + For questions contact William Johnson via email at wcjohns@sandia.gov, or + alternative emails of interspec@sandia.gov. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "InterSpec_config.h" + +#include +#include +#include + +#include +#include +#include +#include + +//#include //temporary for debug + +#include "rapidxml/rapidxml.hpp" +#include "rapidxml/rapidxml_utils.hpp" + +#include "SpecUtils/Filesystem.h" +#include "SpecUtils/StringAlgo.h" + +#include "InterSpec/InterSpec.h" +#include "InterSpec/InterSpecApp.h" +#include "InterSpec/DataBaseUtils.h" +#include "InterSpec/InterSpecUser.h" +#include "InterSpec/UserPreferences.h" + +using namespace Wt; +using namespace std; + +namespace +{ + /** Throws exception if the value string doesnt represent the specified data type. */ + void check_string_gives_type( const string &val_str, const UserOption::DataType type, const string &name ) + { + switch( type ) + { + case UserOption::String: + break; + + case UserOption::Decimal: + { + double val; + if( !SpecUtils::parse_double(val_str.c_str(), val_str.size(), val) ) + throw std::logic_error( "The decimal preference '" + name + + "' is invalid type; given value: '" + val_str + "'." ); + break; + } + + case UserOption::Integer: + { + int val; + if( !SpecUtils::parse_int(val_str.c_str(), val_str.size(), val) ) + throw std::logic_error( "The integer preference '" + name + + "' is invalid type; given value: '" + val_str + "'." ); + break; + } + + case UserOption::Boolean: + { + if( (val_str != "0") && (val_str != "1") ) + throw std::logic_error( "The boolean preference '" + name + + "' must be '0' or '1'; given value: '" + val_str + "'." ); + break; + } + }//switch( ptr->m_type ) + };//check_string_gives_type lambda + + + Wt::Dbo::ptr parseUserOption( const rapidxml::xml_node *pref ) + { + using rapidxml::internal::compare; + typedef rapidxml::xml_attribute XmlAttribute; + + UserOption *option = new UserOption(); + Wt::Dbo::ptr option_owner( option ); + try + { + const XmlAttribute *name_att = pref->first_attribute( "name", 4 ); + const XmlAttribute *type_att = pref->first_attribute( "type", 4 ); + if( !name_att || !name_att->value() || !type_att || !type_att->value() ) + throw runtime_error( "Ill formatted default preferences file" ); + + const char *typestr = type_att->value(); + std::size_t typestrlen = type_att->value_size(); + + option->m_name = name_att->value(); + + // Get rid of "_tablet" or "_phone" + auto pos = option->m_name.find( "_tablet" ); + if( pos == string::npos ) + pos = option->m_name.find( "_phone" ); + if( pos != string::npos ) + option->m_name = option->m_name.substr(0, pos); + + const char *valuestr = pref->value(); + + // We'll let "String" type values be empty, but no other types. + if( !valuestr && !compare(typestr, typestrlen, "String", 6, true) ) + throw runtime_error( "Missing value for \"" + option->m_name + "\" in default preferences file" ); + + if( valuestr ) + option->m_value = std::string( valuestr, valuestr + pref->value_size() ); + + if( option->m_name.length() > UserOption::sm_max_name_str_len ) + throw runtime_error( "Pref \"" + option->m_name + "\" name to long" ); + if( option->m_value.length() > UserOption::sm_max_value_str_len ) + throw runtime_error( "Pref \"" + option->m_name + "\" value to long" ); + + if( compare( typestr, typestrlen, "String", 6, true) ) + option->m_type = UserOption::String; + else if( compare( typestr, typestrlen, "Decimal", 7, true) ) + option->m_type = UserOption::Decimal; + else if( compare( typestr, typestrlen, "Integer", 7, true) ) + option->m_type = UserOption::Integer; + else if( compare( typestr, typestrlen, "Boolean", 7, true) ) + { + option->m_type = UserOption::Boolean; + if( option->m_value == "true" ) + option->m_value = "1"; + else if( option->m_value == "false" ) + option->m_value = "0"; + }else + throw runtime_error( "Invalid \"type\" (\"" + string(typestr) + "\") " + " for pref " + string(typestr) ); + + check_string_gives_type( option->m_value, option->m_type, option->m_name ); + }catch( std::exception &e ) + { + throw runtime_error( e.what() ); + } + + return option_owner; + }//UserOption *parseUserOption( rapidxml::xml_node *node ) + + + /** Returns a map from preference name, to default values, for all preferences. + + The map key will have "_phone" and "_tablet" appended to the preference names, for specializations + for those devices, but the name in the `UserOption` will not have those postfixes. The `Dbo::ptr` + will NOT have been added to the database or anything. + */ + map> defaultUserPreferences() + { + map> answer; + + const string filename = SpecUtils::append_path( InterSpec::staticDataDirectory(), InterSpecUser::sm_defaultPreferenceFile ); + + std::vector data; + SpecUtils::load_file_data( filename.c_str(), data ); + + rapidxml::xml_document doc; + const int flags = (rapidxml::parse_normalize_whitespace | rapidxml::parse_trim_whitespace); + + doc.parse( &data.front() ); + rapidxml::xml_node *node = doc.first_node(); + if( !node || !node->name() + || !rapidxml::internal::compare( node->name(), node->name_size(), "preferences", 11, true) ) + throw runtime_error( "defaultUserPreferences: invalid first node" ); + + for( const rapidxml::xml_node *pref = node->first_node( "pref", 4 ); + pref; + pref = pref->next_sibling( "pref", 4 ) ) + { + const rapidxml::xml_attribute *name_att = pref->first_attribute( "name", 4 ); + assert( name_att && name_att->value() && name_att->value_size() ); + if( !name_att || !name_att->value() || !name_att->value_size() ) + continue; + + const char *prefname = name_att->value(); + const size_t prefnamelen = name_att->value_size(); + std::string prefnamestr(prefname, prefname + prefnamelen); + + Wt::Dbo::ptr option = parseUserOption( pref ); + + // option->m_name will have had "_phone" and "_tablet" removed, even though `prefnamestr` + // will have these in them + assert( option.get() ); + assert( !SpecUtils::icontains( option->m_name, "_phone") ); + assert( !SpecUtils::icontains( option->m_name, "_tablet") ); + + answer[prefnamestr] = option; + }//for( loop over preferences ) + + return answer; + }//map> defaultUserPreferences() + + + /** The returned option will NOT be in the database. + Returned value will be valid, and will throw exception if invalid preference name. + */ + Wt::Dbo::ptr getDefaultUserPreference( const std::string &name, const int type ) + { + const map> prefs = defaultUserPreferences(); + + const bool isphone = ((type & InterSpecUser::PhoneDevice) != 0); + const bool istablet = ((type & InterSpecUser::TabletDevice) != 0); + + if( isphone ) + { + const auto pos = prefs.find( name + "_phone" ); + assert( (pos == end(prefs)) || pos->second ); + if( pos != end(prefs) ) + return pos->second; + }// + + if( istablet ) + { + const auto pos = prefs.find( name + "_tablet" ); + assert( (pos == end(prefs)) || pos->second ); + if( pos != end(prefs) ) + return pos->second; + }//if( istablet ) + + + const auto pos = prefs.find( name ); + assert( (pos == end(prefs)) || pos->second ); + if( pos != end(prefs) ) + return pos->second; + + //Note: the string "couldn't find preference by name" is currently used in + // restoreUserPrefsFromXml(...) to check if a preference with this name is no longer used. + // So dont change next string without also changing there - or really, a more robust + // indication should be used. + throw runtime_error( "getDefaultUserPreference(...):" + " couldn't find preference by name " + name ); + + return Wt::Dbo::ptr{}; + }//Wt::Dbo::ptr getDefaultUserPreference( const std::string &name, const int type ) +}//namespace + + +UserPreferences::UserPreferences( InterSpec *parent ) + : Wt::WObject( parent ), + m_interspec( parent ), + m_options{} +{ + assert( m_interspec ); + if( !m_interspec ) + throw runtime_error( "UserPreferences got nullptr" ); + + const Wt::Dbo::ptr &user = m_interspec->user(); + std::shared_ptr sql = m_interspec->sql(); + assert( sql && user ); + if( !sql || !user ) + throw runtime_error( "UserPreferences invalid sql" ); + + //Takes about 200 microseconds to get all the preferences from the database, if the user + // already has them all + //Takes about 1 milli-second to get default preference values and put them into the database + // if the havent already been put in. + //const auto start_time = chrono::time_point_cast( chrono::system_clock::now() ); + //BOOST_SCOPE_EXIT(start_time){ + // const auto end_time = chrono::time_point_cast( chrono::system_clock::now() ); + // cout << "Took " << (end_time - start_time).count() << " microseconds to get initial preferences from " + // << "data-base" << "." << endl; + //} BOOST_SCOPE_EXIT_END + + // Since we only create this class in one place, where there is already + // an active transaction, we dont need to do out own, but the effects + // of the transaction are recursive, so I dont think it adds any real + // extra overhead to just be safe + DataBaseUtils::DbTransaction transaction( *sql ); + + const Dbo::collection> &dbprefs = user->preferences(); + for( Dbo::collection>::const_iterator iter = dbprefs.begin(); + iter != dbprefs.end(); + ++iter ) + { + Dbo::ptr option = *iter; + assert( option ); + + const string &name = option->m_name; + + if( m_options.count(name) ) + { + cerr << "Found a duplicate preference for '" << name << "', will delete the former one" << endl; + assert( 0 ); + m_options[name].remove(); + } + + try + { + check_string_gives_type( option->m_value, option->m_type, option->m_name ); + }catch( std::exception &e ) + { + cerr << "User preference in database gave error: " << e.what() << endl; + assert( 0 ); + + try + { + option = getDefaultUserPreference( option->m_name, user->deviceType() ); + }catch( std::exception & ) + { + cerr << "Failed to get a default preference value for '" << option->m_name + << "' - skipping." << endl; + assert( 0 ); + continue; + } + }//try / catch - to make sure database value string represents purported data type + + m_options[name] = option; + }//for( loop over preferences in the database ); + + if( m_options.empty() ) + { + auto app = dynamic_cast( wApp ); + const bool isphone = app && app->isPhone(); + const bool istablet = app && app->isTablet(); + + const map> defopts = defaultUserPreferences(); + + for( const auto &name_opt : defopts ) + { + const string &name = name_opt.first; + Dbo::ptr opt = name_opt.second; + assert( opt ); + if( !opt ) + continue; + + if( !istablet && (name.find("_tablet") != string::npos) ) + continue; + + if( !isphone && (name.find("_phone") != string::npos) ) + continue; + + if( m_options.count(opt->m_name) ) + continue; + + opt.modify()->m_user = user; + sql->session()->add( opt ); + m_options[opt->m_name] = opt; + }//for( const auto &name_opt : defopts ) + }//if( m_options.empty() ) + + transaction.commit(); +}//UserPreferences constructor + + +void UserPreferences::setBoolPreferenceValue( const std::string &name, + const bool &value, + InterSpec *viewer ) +{ + setPreferenceValueInternal( name, value, viewer ); +} + + +void UserPreferences::setPreferenceValueInternal( const std::string &name, + const bool &value, + InterSpec *viewer ) +{ + const string value_as_str = value ? "1" : "0"; + UserPreferences::setPreferenceValueWorker( name, value_as_str, viewer ); + + UserPreferences * const self = viewer->preferences(); + assert( self ); + + const auto callback_pos = self->m_onBoolChangeSignals.find(name); + if( (callback_pos != end(self->m_onBoolChangeSignals)) && callback_pos->second ) + (*callback_pos->second)(value); +}//setPreferenceValueInternal(...) + + +void UserPreferences::setPreferenceValueInternal( const std::string &name, + const int &value, + InterSpec *viewer ) +{ + const string value_as_str = std::to_string(value); + UserPreferences::setPreferenceValueWorker( name, value_as_str, viewer ); +}//void UserPreferences::setPreferenceValueInternal(int) + + +void UserPreferences::setPreferenceValueInternal( const std::string &name, + const double &value, + InterSpec *viewer ) +{ + const std::string value_as_str = SpecUtils::printCompact( value, 12 ); + UserPreferences::setPreferenceValueWorker( name, value_as_str, viewer ); +}//void UserPreferences::setPreferenceValueInternal(double) + + +void UserPreferences::setPreferenceValueInternal( const std::string &name, + const std::string &value, + InterSpec *viewer ) +{ + UserPreferences::setPreferenceValueWorker( name, value, viewer ); +}//void UserPreferences::setPreferenceValueInternal(string) + + + + +void UserPreferences::setPreferenceValueWorker( const std::string &name, + std::string value_as_string, + InterSpec *viewer ) +{ + if( name.size() > UserOption::sm_max_name_str_len ) + throw std::runtime_error( "Invalid name for preference: " + name ); + + assert( viewer ); + if( !viewer ) + throw std::runtime_error( "setPreferenceValueWorker: called with invalid viewer ptr" ); + + + UserPreferences * const self = viewer->preferences(); + assert( self ); + auto old_pos = self->m_options.find(name); + if( (old_pos != end(self->m_options)) && (old_pos->second->m_value == value_as_string) ) + return; + + std::shared_ptr sql = viewer->sql(); + const Wt::Dbo::ptr &user = viewer->user(); + + assert( sql && user ); + if( !sql || !user ) + throw runtime_error( "Invalid sql or user ptr" ); + + if( sql->session() != user.session() ) + throw std::runtime_error( "UserPreferences::setPreferenceValue() must be called" + " with same db session as user" ); + + if( value_as_string.size() > UserOption::sm_max_value_str_len ) + value_as_string = value_as_string.substr( 0, UserOption::sm_max_value_str_len ); + + if( old_pos != end(self->m_options) ) + { + try + { + check_string_gives_type( value_as_string, old_pos->second->m_type, name ); + + DataBaseUtils::DbTransaction transaction( *sql ); + + old_pos->second.reread(); // In case another session has changed the value, so we dont get a stale object exception + old_pos->second.modify()->m_value = value_as_string; + old_pos->second->value(); + + transaction.commit(); + }catch( std::exception &e ) + { + assert( 0 ); + std::cerr << "Caught exception setting preference value to database" << std::endl; + } + + return; + }//if( old_pos != end(self->m_options) ) + + + // If we are here, we dont have a value in memory so we'll check if its been added + // to the database since we initialized this session, and if not, we'll start with' + // default value + DataBaseUtils::DbTransaction transaction( *sql ); + + Dbo::collection< Wt::Dbo::ptr > options_in_db + = user->preferences().find() + .where( "name = ?" ).bind( name ); + const size_t noptions = options_in_db.size(); + + assert( noptions < 2 ); + + Wt::Dbo::ptr new_opt; + if( noptions >= 1 ) + { + vector< Dbo::ptr > options; + std::copy( options_in_db.begin(), options_in_db.end(), std::back_inserter(options) ); + assert( options.size() == 1 ); + + new_opt = options.back(); + new_opt.modify()->m_value = value_as_string; + + if( noptions > 1 ) + { + // We shouldnt actually ever make it here I think. +#if( PERFORM_DEVELOPER_CHECKS ) + char buffer[1024]; + snprintf( buffer, sizeof(buffer), "Invalid number of preferences (%i) for %s for user %s; will" + " remove all of them after the first from the database.", + static_cast(noptions), name.c_str(), user->userName().c_str() ); + log_developer_error( __func__, buffer ); +#endif + + // Remove all but the most recent option (assuming results are returned sorted by ID) + for( size_t i = 0; (i+1) < options.size(); ++i ) + options[i].remove(); + }//if( noptions > 1 ) + }else + { + new_opt = getDefaultUserPreference( name, user->deviceType() ); + new_opt.modify()->m_user = user; + new_opt.modify()->m_value = value_as_string; + sql->session()->add( new_opt ); + }//if( optioncol.size() >= 1 ) / else + + transaction.commit(); +}//setPreferenceValueWorker(...) + + +boost::any UserPreferences::preferenceValueAny( const std::string &name, InterSpec *viewer ) +{ + // These first few things are just for debug + //bool from_default = false; + //const auto start_time = chrono::time_point_cast( chrono::system_clock::now() ); + //BOOST_SCOPE_EXIT(start_time, name, from_default){ + // const auto end_time = chrono::time_point_cast( chrono::system_clock::now() ); + // cout << "Took " << (end_time - start_time).count() << " microseconds to get '" << name + // << "' preference from " << (from_default ? "default-xml" : "data-base") << "." << endl; + //} BOOST_SCOPE_EXIT_END + + if( !viewer ) + { + Dbo::ptr option = getDefaultUserPreference( name, InterSpecUser::DeviceType::Desktop ); + boost::any value = option->value(); + return value; + } + + UserPreferences * const self = viewer->preferences(); + assert( self ); + + auto known_pos = self->m_options.find(name); + assert( (known_pos == end(self->m_options)) || known_pos->second.get() ); + if( known_pos != end(self->m_options) ) + return known_pos->second->value(); + + const Dbo::ptr &user = viewer->user(); + std::shared_ptr sql = viewer->sql(); + + if( !user || !sql ) + throw std::runtime_error( "preferenceValueAny(...): invalid usr or sql ptr" ); + + //from_default = true; + + Wt::Dbo::ptr def_value = getDefaultUserPreference( name, user->deviceType() ); + + DataBaseUtils::DbTransaction transaction( *sql ); + def_value.modify()->m_user = user; + sql->session()->add( def_value ); + transaction.commit(); + + self->m_options[name] = def_value; + + return def_value->value(); +}//boost::any preferenceValue( const std::string &name, InterSpec *viewer ); + + + +void UserPreferences::associateWidget( const std::string &name, + Wt::WCheckBox *cb, + InterSpec *viewer ) +{ + const bool value = preferenceValue( name, viewer ); + cb->setChecked( value ); + + viewer->preferences()->addCallbackWhenChanged( name, cb, &WCheckBox::setChecked ); + + cb->checked().connect( boost::bind( &UserPreferences::setBoolPreferenceValue, name, true, viewer ) ); + cb->unChecked().connect( boost::bind( &UserPreferences::setBoolPreferenceValue, name, false, viewer ) ); + + /* + //We need to emit the checked() and unChecked() signals so that any side-effect + // can happen from the change. For actually changing the state of the widget + // we can safely do this (incase 'cb' gets deleted at some point) using + // WApplication::bind, but to call the emit functions I couldnt think of a + // safe way to do this, other than searching the widget tree and making sure + // that we can find the widget (hence, know it hasnt been deleted) before + // de-referening the 'cb' passed into this function; this is a bit of a hack + // but it works for now. + const string cbid = cb->id(); + + std::function fcn = [=]( boost::any valueAny ){ + const bool value = boost::any_cast(valueAny); + const bool setCbChecked = reverseValue ? !value : value; + + // The below doesnt seem to find widgets in AuxWindows (and maybe pop-ups) + auto w = wApp->domRoot()->findById(cbid); + if( !w && wApp->domRoot2() ) + w = wApp->domRoot2()->findById(cbid); + if( !w && wApp->root() ) + w = wApp->root()->findById(cbid); + + if( w ) + { + cb->changed().emit(); + if( value ) + cb->checked().emit(); + else + cb->unChecked().emit(); + }else + { + cerr << "Couldnt find widget with cbid='" << cbid << "', so wont call any side-effect functions for pref '" + << name << "'" << endl; + } + };//fcn + + UserPreferences::associateFunction( user, name, fcn, viewer ); + */ +}//void associateWidget( ) + + +/* +void UserPreferences::associateWidget( Wt::Dbo::ptr user, + const std::string &name, + Wt::WDoubleSpinBox *sb, + InterSpec *viewer ) +{ + const double value = preferenceValue( name, viewer ); + + sb->setValue( value ); + sb->valueChanged().connect( + boost::bind( &UserPreferences::setPreferenceValue, + user, name, boost::placeholders::_1, viewer ) ); + + const string sbid = sb->id(); + + std::function fcn = [=]( boost::any valueAny ){ + const double value = boost::any_cast(valueAny); + + auto w = wApp->domRoot()->findById(sbid); + if( !w && wApp->domRoot2() ) + w = wApp->domRoot2()->findById(sbid); + + if( w ) + { + sb->setValue( value ); + sb->valueChanged().emit( value ); + }else + { + cerr << "Couldnt find WDoubleSpinBox with id='" << sbid << "', so wont call any side-effect functions" << endl; + } + };//fcn + + UserPreferences::associateFunction( user, name, fcn, viewer ); +}//void associateWidget(...) + + +void UserPreferences::associateWidget( Wt::Dbo::ptr user, + const std::string &name, + Wt::WSpinBox *sb, + InterSpec *viewer ) +{ + const int value = preferenceValue( name, viewer ); + + sb->setValue( value ); + + sb->valueChanged().connect( + boost::bind( &UserPreferences::setPreferenceValue, + user, name, boost::placeholders::_1, viewer ) ); + + const string sbid = sb->id(); + + std::function fcn = [=]( boost::any valueAny ){ + const int value = boost::any_cast(valueAny); + + auto w = wApp->domRoot()->findById(sbid); + if( !w && wApp->domRoot2() ) + w = wApp->domRoot2()->findById(sbid); + + if( w ) + { + sb->setValue( value ); + sb->valueChanged().emit( value ); + }else + { + cerr << "Couldnt find WSpinBox with id='" << sbid << "', so wont call any side-effect functions" << endl; + } + };//fcn + + UserPreferences::associateFunction( user, name, fcn, viewer ); +}//void UserPreferences::associateWidget(...) +*/ + + +void UserPreferences::restoreUserPrefsFromXml( const rapidxml::xml_node *prefs_node, + InterSpec *viewer ) +{ + using namespace rapidxml; + using rapidxml::internal::compare; + + if( !viewer || !prefs_node + || !compare( prefs_node->name(), prefs_node->name_size(), "preferences", 11, true) ) + throw runtime_error( "restoreUserPrefsFromXml: invalid input" ); + + + for( const xml_node *pref = prefs_node->first_node( "pref", 4 ); pref; + pref = pref->next_sibling( "pref", 4 ) ) + { + Wt::Dbo::ptr option; + try + { + option = parseUserOption(pref); + assert( option ); + + switch( option->m_type ) + { + case UserOption::String: + { + const string value = boost::any_cast( option->value() ); + setPreferenceValueInternal( option->m_name, value, viewer ); + break; + }//case String + + case UserOption::Decimal: + { + const double value = boost::any_cast( option->value() ); + setPreferenceValueInternal( option->m_name, value, viewer ); + break; + }//case Decimal + + case UserOption::Integer: + { + const int value = boost::any_cast( option->value() ); + setPreferenceValueInternal( option->m_name, value, viewer ); + break; + }//case Integer + + case UserOption::Boolean: + { + const bool value = boost::any_cast( option->value() ); + setPreferenceValueInternal( option->m_name, value, viewer ); + break; + }//case Boolean + }//switch( datatype ) + }catch( std::exception &e ) + { + const string errmsg = e.what(); + if( SpecUtils::icontains( errmsg, "couldn't find preference by name" ) ) + { + cerr << "Warning: couldnt find a preference named '" + << (option ? option->m_name : string("N/A")) << "' that is in the" + << " file being loaded, but apears to no longer be used." << endl; + }else + { + throw runtime_error( "Failed to deserialize user prefernces from XML: " + errmsg ); + } + }//try / catch + }//for( loop over prefs ) +}//void restoreUserPrefsFromXml(...) + + +rapidxml::xml_node *UserPreferences::userOptionsToXml( + rapidxml::xml_node *parent_node, + InterSpec *viewer ) const +{ + using namespace ::rapidxml; + + if( !parent_node ) + throw runtime_error( "userOptionsToXml: invalid input" ); + + xml_document *doc = parent_node->document(); + + xml_node *prefs_node = doc->allocate_node( node_element, "preferences" ); + parent_node->append_node( prefs_node ); + + vector< Dbo::ptr > options; + + {//begin codeblock to retrieve preferences from database + std::shared_ptr sql = viewer->sql(); + const Wt::Dbo::ptr &user = viewer->user(); + + DataBaseUtils::DbTransaction transaction( *sql ); + + std::copy( user->preferences().begin(), user->preferences().end(), + std::back_inserter(options) ); + transaction.commit(); + }//end codeblock to retrieve prefernces from database + + for( vector< Dbo::ptr >::const_iterator iter = options.begin(); + iter != options.end(); ++iter ) + { + Dbo::ptr option = *iter; + + const string &name = option->m_name; + const string &value = option->m_value; + const char *namestr = doc->allocate_string( name.c_str(), name.size()+1 ); + + const char *valstr = 0; + switch( option->m_type ) + { + case UserOption::String: case UserOption::Decimal: case UserOption::Integer: + valstr = doc->allocate_string( value.c_str(), value.size()+1 ); + break; + + case UserOption::Boolean: + valstr = (boost::any_cast(option->value()) ? "true" : "false"); + break; + }//switch( m_type ) + + const char *typestr = 0; + switch( option->m_type ) + { + case UserOption::String: typestr = "String"; break; + case UserOption::Decimal: typestr = "Decimal"; break; + case UserOption::Integer: typestr = "Integer"; break; + case UserOption::Boolean: typestr = "Boolean"; break; + }//switch( m_type ) + + xml_node *node = doc->allocate_node( node_element, "pref", valstr ); + xml_attribute *name_att = doc->allocate_attribute( "name", namestr ); + xml_attribute *type_att = doc->allocate_attribute( "type", typestr ); + node->append_attribute( name_att ); + node->append_attribute( type_att ); + prefs_node->append_node( node ); + }//for( loop over DB entries ) + + return prefs_node; +}//xml_node *userOptionsToXml( xml_node * ) const + + + + + diff --git a/src/WarningWidget.cpp b/src/WarningWidget.cpp index 772a97b9..23a2432f 100644 --- a/src/WarningWidget.cpp +++ b/src/WarningWidget.cpp @@ -48,6 +48,7 @@ #include "InterSpec/InterSpecUser.h" #include "InterSpec/WarningWidget.h" #include "InterSpec/UndoRedoManager.h" +#include "InterSpec/UserPreferences.h" #include "InterSpec/RowStretchTreeView.h" using namespace Wt; @@ -187,10 +188,10 @@ WarningWidget::WarningWidget( InterSpec *hostViewer, // Find which messages should be active. for( WarningMsgLevel i = WarningMsgLevel(0); i <= WarningMsgHigh; i = WarningMsgLevel(i+1) ) - m_active[i] = !m_hostViewer->m_user->preferenceValue( tostr(i), m_hostViewer ); + m_active[i] = !UserPreferences::preferenceValue(tostr(i), m_hostViewer); for( WarningMsgLevel i = WarningMsgLevel(0); i <= WarningMsgHigh; i = WarningMsgLevel(i+1) ) - m_popupActive[i] = !InterSpecUser::preferenceValue( WarningWidget::popupToStr(i), m_hostViewer ); + m_popupActive[i] = !UserPreferences::preferenceValue(popupToStr(i), m_hostViewer); //Force WarningMsgSave to always be true m_active[WarningMsgSave] = true; @@ -263,8 +264,6 @@ void WarningWidget::createContent() m_layout->setRowStretch(0,1); m_layout->setColumnStretch(0,1); - Wt::Dbo::ptr m_user = m_hostViewer->m_user; - WImage* image = new Wt::WImage(Wt::WLink( iconUrl(WarningMsgLevel::WarningMsgInfo) )); image->setMaximumSize(WLength(16,WLength::Pixel), WLength(16,WLength::Pixel)); @@ -308,7 +307,7 @@ void WarningWidget::createContent() m_layout->addWidget( warnToggle, 3, i++, AlignCenter); const char *str = tostr( level ); - InterSpecUser::associateWidget( m_user, str, warnToggle, m_hostViewer ); + UserPreferences::associateWidget( str, warnToggle, m_hostViewer ); warnToggle->checked().connect( boost::bind( &WarningWidget::setActivity, this, level, false ) ); warnToggle->unChecked().connect( boost::bind( &WarningWidget::setActivity, this, level, true ) ); @@ -327,7 +326,7 @@ void WarningWidget::createContent() m_layout->addWidget( warnToggle, 4, i++, AlignCenter ); const char *str = popupToStr( level ); - InterSpecUser::associateWidget( m_user, str, warnToggle, m_hostViewer ); + UserPreferences::associateWidget( str, warnToggle, m_hostViewer ); warnToggle->checked().connect( boost::bind( &WarningWidget::setPopupActivity, this, level, false ) ); warnToggle->unChecked().connect( boost::bind( &WarningWidget::setPopupActivity, this, level, true ) ); @@ -598,7 +597,7 @@ void WarningWidget::setActivity( WarningWidget::WarningMsgLevel priority, bool a try { const bool value = isUndo ? active : !active; - InterSpecUser::setBoolPreferenceValue( viewer->m_user, tostr(priority), value, viewer ); + UserPreferences::setBoolPreferenceValue( tostr(priority), value, viewer ); warn->m_active[priority] = isUndo ? !active : active; }catch( std::exception &e ) { @@ -629,7 +628,7 @@ void WarningWidget::setPopupActivity( WarningWidget::WarningMsgLevel priority, b try { const bool value = isUndo ? showPopup : !showPopup; - InterSpecUser::setBoolPreferenceValue( viewer->m_user, popupToStr(priority), value, viewer ); + UserPreferences::setBoolPreferenceValue( popupToStr(priority), value, viewer ); warn->m_popupActive[priority] = isUndo ? !showPopup : showPopup; }catch( std::exception &e ) {
    " + PhysicalUnits::printToBestLengthUnits(res.distance_ * PhysicalUnits::mm,4) + "
    " + WString::tr("Activity").toUTF8() + "" + PhysicalUnits::printToBestActivityUnits( res.activity_, 2, !useBq, 1.0 ) + "