mirror of
				https://github.com/saitohirga/WSJT-X.git
				synced 2025-10-30 20:40:28 -04:00 
			
		
		
		
	The Status(1) message also acquires the current configuration name as a new field. See NetworkMessage.hpp for details. The UDP reference example program message_aggregator acquires the ability to display and change the configuration of a WSJT-X client to exercise these new features.
		
			
				
	
	
		
			824 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			824 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "MultiSettings.hpp"
 | |
| 
 | |
| #include <stdexcept>
 | |
| 
 | |
| #include <QObject>
 | |
| #include <QSettings>
 | |
| #include <QString>
 | |
| #include <QStringList>
 | |
| #include <QDir>
 | |
| #include <QFont>
 | |
| #include <QApplication>
 | |
| #include <QStandardPaths>
 | |
| #include <QMainWindow>
 | |
| #include <QMenu>
 | |
| #include <QAction>
 | |
| #include <QActionGroup>
 | |
| #include <QDialog>
 | |
| #include <QLineEdit>
 | |
| #include <QRegularExpression>
 | |
| #include <QRegularExpressionValidator>
 | |
| #include <QFormLayout>
 | |
| #include <QVBoxLayout>
 | |
| #include <QDialogButtonBox>
 | |
| #include <QPushButton>
 | |
| #include <QComboBox>
 | |
| #include <QLabel>
 | |
| #include <QList>
 | |
| #include <QMetaObject>
 | |
| 
 | |
| #include "SettingsGroup.hpp"
 | |
| #include "qt_helpers.hpp"
 | |
| #include "SettingsGroup.hpp"
 | |
| #include "widgets/MessageBox.hpp"
 | |
| 
 | |
| #include "pimpl_impl.hpp"
 | |
| 
 | |
| namespace
 | |
| {
 | |
|   char const * default_string = QT_TRANSLATE_NOOP ("MultiSettings", "Default");
 | |
|   char const * multi_settings_root_group = "MultiSettings";
 | |
|   char const * multi_settings_current_group_key = "CurrentMultiSettingsConfiguration";
 | |
|   char const * multi_settings_current_name_key = "CurrentName";
 | |
|   char const * multi_settings_place_holder_key = "MultiSettingsPlaceHolder";
 | |
| 
 | |
|   QString unescape_ampersands (QString s)
 | |
|   {
 | |
|     return s.replace ("&&", "&");
 | |
|   }
 | |
| 
 | |
|   // calculate a useable and unique settings file path
 | |
|   QString settings_path ()
 | |
|   {
 | |
|     auto const& config_directory = QStandardPaths::writableLocation (QStandardPaths::ConfigLocation);
 | |
|     QDir config_path {config_directory}; // will be "." if config_directory is empty
 | |
|     if (!config_path.mkpath ("."))
 | |
|       {
 | |
|         throw std::runtime_error {"Cannot find a usable configuration path \"" + config_path.path ().toStdString () + '"'};
 | |
|       }
 | |
|     return config_path.absoluteFilePath (QApplication::applicationName () + ".ini");
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Dialog to get a valid new configuration name
 | |
|   //
 | |
|   class NameDialog final
 | |
|     : public QDialog
 | |
|   {
 | |
|     Q_OBJECT
 | |
| 
 | |
|   public:
 | |
|     explicit NameDialog (QString const& current_name,
 | |
|                          QStringList const& current_names,
 | |
|                          QWidget * parent = nullptr)
 | |
|       : QDialog {parent}
 | |
|     {
 | |
|       setWindowTitle (tr ("New Configuration Name"));
 | |
| 
 | |
|       auto form_layout = new QFormLayout ();
 | |
|       form_layout->addRow (tr ("Old name:"), &old_name_label_);
 | |
|       old_name_label_.setText (current_name);
 | |
|       form_layout->addRow (tr ("&New name:"), &name_line_edit_);
 | |
| 
 | |
|       auto main_layout = new QVBoxLayout (this);
 | |
|       main_layout->addLayout (form_layout);
 | |
| 
 | |
|       auto button_box = new QDialogButtonBox {QDialogButtonBox::Ok | QDialogButtonBox::Cancel};
 | |
|       button_box->button (QDialogButtonBox::Ok)->setEnabled (false);
 | |
|       main_layout->addWidget (button_box);
 | |
| 
 | |
|       auto name_validator = new QRegularExpressionValidator {QRegularExpression {R"([^/\\]+)"}, this};
 | |
|       name_line_edit_.setValidator (name_validator);
 | |
| 
 | |
|       connect (&name_line_edit_, &QLineEdit::textChanged, [current_names, button_box] (QString const& name) {
 | |
|           bool valid {!current_names.contains (name.trimmed ())};
 | |
|           button_box->button (QDialogButtonBox::Ok)->setEnabled (valid);
 | |
|         });
 | |
|       connect (button_box, &QDialogButtonBox::accepted, this, &QDialog::accept);
 | |
|       connect (button_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
 | |
|     }
 | |
| 
 | |
|     QString new_name () const
 | |
|     {
 | |
|       return name_line_edit_.text ().trimmed ();
 | |
|     }
 | |
| 
 | |
|   private:
 | |
|     QLabel old_name_label_;
 | |
|     QLineEdit name_line_edit_;
 | |
|   };
 | |
| 
 | |
|   //
 | |
|   // Dialog to get a valid new existing name
 | |
|   //
 | |
|   class ExistingNameDialog final
 | |
|     : public QDialog
 | |
|   {
 | |
|     Q_OBJECT
 | |
| 
 | |
|   public:
 | |
|     explicit ExistingNameDialog (QStringList const& current_names, QWidget * parent = nullptr)
 | |
|       : QDialog {parent}
 | |
|     {
 | |
|       setWindowTitle (tr ("Configuration to Clone From"));
 | |
| 
 | |
|       name_combo_box_.addItems (current_names);
 | |
| 
 | |
|       auto form_layout = new QFormLayout ();
 | |
|       form_layout->addRow (tr ("&Source Configuration Name:"), &name_combo_box_);
 | |
| 
 | |
|       auto main_layout = new QVBoxLayout (this);
 | |
|       main_layout->addLayout (form_layout);
 | |
| 
 | |
|       auto button_box = new QDialogButtonBox {QDialogButtonBox::Ok | QDialogButtonBox::Cancel};
 | |
|       main_layout->addWidget (button_box);
 | |
| 
 | |
|       connect (button_box, &QDialogButtonBox::accepted, this, &QDialog::accept);
 | |
|       connect (button_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
 | |
|     }
 | |
| 
 | |
|     QString name () const
 | |
|     {
 | |
|       return name_combo_box_.currentText ();
 | |
|     }
 | |
| 
 | |
|   private:
 | |
|     QComboBox name_combo_box_;
 | |
|   };
 | |
| }
 | |
| 
 | |
| class MultiSettings::impl final
 | |
|   : public QObject
 | |
| {
 | |
|   Q_OBJECT
 | |
| 
 | |
| public:
 | |
|   explicit impl (MultiSettings const * parent, QString const& config_name);
 | |
|   bool reposition ();
 | |
|   void create_menu_actions (QMainWindow * main_window, QMenu * menu);
 | |
|   bool exit ();
 | |
| 
 | |
|   QSettings settings_;
 | |
|   QString current_;
 | |
| 
 | |
|   // switch to this configuration
 | |
|   void select_configuration (QString const& target_name);
 | |
| 
 | |
| private:
 | |
|   using Dictionary = QMap<QString, QVariant>;
 | |
| 
 | |
|   // create a configuration maintenance sub menu
 | |
|   QMenu * create_sub_menu (QMenu * parent,
 | |
|                            QString const& menu_title,
 | |
|                            QActionGroup * = nullptr);
 | |
| 
 | |
|   // extract all settings from the current QSettings group
 | |
|   Dictionary get_settings () const;
 | |
| 
 | |
|   // write the settings values from the dictionary to the current group
 | |
|   void load_from (Dictionary const&, bool add_placeholder = true);
 | |
| 
 | |
|   // clone this configuration
 | |
|   void clone_configuration (QMenu *, QMenu const *);
 | |
| 
 | |
|   // update this configuration from another
 | |
|   void clone_into_configuration (QMenu const *);
 | |
| 
 | |
|   // reset configuration to default values
 | |
|   void reset_configuration (QMenu const *);
 | |
| 
 | |
|   // change configuration name
 | |
|   void rename_configuration (QMenu *);
 | |
| 
 | |
|   // remove a configuration
 | |
|   void delete_configuration (QMenu *);
 | |
| 
 | |
|   // action to take on restart
 | |
|   enum class RepositionType {unchanged, replace, save_and_replace};
 | |
|   void restart (RepositionType);
 | |
| 
 | |
|   MultiSettings const * parent_;  // required for emitting signals
 | |
|   QMainWindow * main_window_;
 | |
|   bool name_change_emit_pending_; // delayed until menu built
 | |
| 
 | |
|   QFont original_font_;
 | |
| 
 | |
|   RepositionType reposition_type_;
 | |
|   Dictionary new_settings_;
 | |
|   bool exit_flag_;              // false means loop around with new
 | |
|                                 // configuration
 | |
|   QActionGroup * configurations_group_;
 | |
| };
 | |
| 
 | |
| #include "MultiSettings.moc"
 | |
| #include "moc_MultiSettings.cpp"
 | |
| 
 | |
| MultiSettings::MultiSettings (QString const& config_name)
 | |
|   : m_ {this, config_name}
 | |
| {
 | |
| }
 | |
| 
 | |
| MultiSettings::~MultiSettings ()
 | |
| {
 | |
| }
 | |
| 
 | |
| QSettings * MultiSettings::settings ()
 | |
| {
 | |
|   return &m_->settings_;
 | |
| }
 | |
| 
 | |
| QVariant MultiSettings::common_value (QString const& key, QVariant const& default_value) const
 | |
| {
 | |
|   QVariant value;
 | |
|   QSettings * mutable_settings {const_cast<QSettings *> (&m_->settings_)};
 | |
|   auto const& current_group = mutable_settings->group ();
 | |
|   if (current_group.size ()) mutable_settings->endGroup ();
 | |
|   {
 | |
|     SettingsGroup alternatives {mutable_settings, multi_settings_root_group};
 | |
|     value = mutable_settings->value (key, default_value);
 | |
|   }
 | |
|   if (current_group.size ()) mutable_settings->beginGroup (current_group);
 | |
|   return value;
 | |
| }
 | |
| 
 | |
| void MultiSettings::set_common_value (QString const& key, QVariant const& value)
 | |
| {
 | |
|   auto const& current_group = m_->settings_.group ();
 | |
|   if (current_group.size ()) m_->settings_.endGroup ();
 | |
|   {
 | |
|     SettingsGroup alternatives {&m_->settings_, multi_settings_root_group};
 | |
|     m_->settings_.setValue (key, value);
 | |
|   }
 | |
|   if (current_group.size ()) m_->settings_.beginGroup (current_group);
 | |
| }
 | |
| 
 | |
| void MultiSettings::remove_common_value (QString const& key)
 | |
| {
 | |
|   if (!key.size ()) return;     // we don't allow global delete as it
 | |
|                                 // would break this classes data model
 | |
|   auto const& current_group = m_->settings_.group ();
 | |
|   if (current_group.size ()) m_->settings_.endGroup ();
 | |
|   {
 | |
|     SettingsGroup alternatives {&m_->settings_, multi_settings_root_group};
 | |
|     m_->settings_.remove (key);
 | |
|   }
 | |
|   if (current_group.size ()) m_->settings_.beginGroup (current_group);
 | |
| }
 | |
| 
 | |
| void MultiSettings::create_menu_actions (QMainWindow * main_window, QMenu * menu)
 | |
| {
 | |
|   m_->create_menu_actions (main_window, menu);
 | |
| }
 | |
| 
 | |
| void MultiSettings::select_configuration (QString const& name)
 | |
| {
 | |
|   m_->select_configuration (name);
 | |
| }
 | |
| 
 | |
| QString MultiSettings::configuration_name () const
 | |
| {
 | |
|   return m_->current_;
 | |
| }
 | |
| 
 | |
| bool MultiSettings::exit ()
 | |
| {
 | |
|   return m_->exit ();
 | |
| }
 | |
| 
 | |
| MultiSettings::impl::impl (MultiSettings const * parent, QString const& config_name)
 | |
|   : settings_ {settings_path (), QSettings::IniFormat}
 | |
|   , parent_ {parent}
 | |
|   , main_window_ {nullptr}
 | |
|   , name_change_emit_pending_ {true}
 | |
|   , reposition_type_ {RepositionType::unchanged}
 | |
|   , exit_flag_ {true}
 | |
|   , configurations_group_ {new QActionGroup {this}}
 | |
| {
 | |
|   if (!settings_.isWritable ())
 | |
|     {
 | |
|       throw std::runtime_error {QString {"Cannot access \"%1\" for writing"}
 | |
|         .arg (settings_.fileName ()).toStdString ()};
 | |
|     }
 | |
| 
 | |
|   // deal with transient, now defunct, settings key
 | |
|   if (settings_.contains (multi_settings_current_group_key))
 | |
|     {
 | |
|       current_ = settings_.value (multi_settings_current_group_key).toString ();
 | |
|       settings_.remove (multi_settings_current_group_key);
 | |
|       if (current_.size ())
 | |
|         {
 | |
|           {
 | |
|             SettingsGroup alternatives {&settings_, multi_settings_root_group};
 | |
|             {
 | |
|               SettingsGroup source_group {&settings_, current_};
 | |
|               new_settings_ = get_settings ();
 | |
|             }
 | |
|             settings_.setValue (multi_settings_current_name_key, tr (default_string));
 | |
|           }
 | |
|           reposition_type_ = RepositionType::save_and_replace;
 | |
|           reposition ();
 | |
|         }
 | |
|       else
 | |
|         {
 | |
|           SettingsGroup alternatives {&settings_, multi_settings_root_group};
 | |
|           settings_.setValue (multi_settings_current_name_key, tr (default_string));
 | |
|         }
 | |
|     }
 | |
| 
 | |
|   // bootstrap
 | |
|   QStringList available_configurations;
 | |
|   {
 | |
|     SettingsGroup alternatives {&settings_, multi_settings_root_group};
 | |
|     available_configurations = settings_.childGroups ();
 | |
|     // use last selected configuration
 | |
|     current_ = settings_.value (multi_settings_current_name_key).toString ();
 | |
|     if (!current_.size ())
 | |
|       {
 | |
|         // no configurations so use default name
 | |
|         current_ = tr (default_string);
 | |
|         settings_.setValue (multi_settings_current_name_key, current_);
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   if (config_name.size () && available_configurations.contains (config_name) && config_name != current_)
 | |
|     {
 | |
|       // switch to specified configuration
 | |
|       {
 | |
|         SettingsGroup alternatives {&settings_, multi_settings_root_group};
 | |
|         // save the target settings
 | |
|         SettingsGroup target_group {&settings_, config_name};
 | |
|         new_settings_ = get_settings ();
 | |
|       }
 | |
|       current_ = config_name;
 | |
|       reposition_type_ = RepositionType::save_and_replace;
 | |
|       reposition ();
 | |
|     }
 | |
|   settings_.sync ();
 | |
| }
 | |
| 
 | |
| // do actions that can only be done once all the windows are closed
 | |
| bool MultiSettings::impl::reposition ()
 | |
| {
 | |
|   auto const& current_group = settings_.group ();
 | |
|   if (current_group.size ()) settings_.endGroup ();
 | |
|   switch (reposition_type_)
 | |
|     {
 | |
|     case RepositionType::save_and_replace:
 | |
|       {
 | |
|         // save the current settings with the other alternatives
 | |
|         Dictionary saved_settings {get_settings ()};
 | |
|         SettingsGroup alternatives {&settings_, multi_settings_root_group};
 | |
|         // get the current configuration name
 | |
|         auto const& previous_group_name = settings_.value (multi_settings_current_name_key, tr (default_string)).toString ();
 | |
|         SettingsGroup save_group {&settings_, previous_group_name};
 | |
|         load_from (saved_settings);
 | |
|       }
 | |
|       // fall through
 | |
|     case RepositionType::replace:
 | |
|       // and purge current settings
 | |
|       for (auto const& key: settings_.allKeys ())
 | |
|         {
 | |
|           if (!key.contains (multi_settings_root_group))
 | |
|             {
 | |
|               settings_.remove (key);
 | |
|             }
 | |
|         }
 | |
|       // insert the new settings
 | |
|       load_from (new_settings_, false);
 | |
|       if (!new_settings_.size ())
 | |
|         {
 | |
|           // if we are clearing the current settings then we must
 | |
|           // reset the application font and the font in the
 | |
|           // application style sheet, this is necessary since the
 | |
|           // application instance is not recreated
 | |
|           qApp->setFont (original_font_);
 | |
|           qApp->setStyleSheet (qApp->styleSheet () + "* {" + font_as_stylesheet (original_font_) + '}');
 | |
|         }
 | |
|       // now we have set up the new current we can safely purge it
 | |
|       // from the alternatives
 | |
|       {
 | |
|         SettingsGroup alternatives {&settings_, multi_settings_root_group};
 | |
|         {
 | |
|           SettingsGroup purge_group {&settings_, current_};
 | |
|           settings_.remove (QString {});  // purge entire group
 | |
|         }
 | |
|         // switch to the specified configuration name
 | |
|         settings_.setValue (multi_settings_current_name_key, current_);
 | |
|       }
 | |
|       settings_.sync ();
 | |
|       // fall through
 | |
|     case RepositionType::unchanged:
 | |
|       new_settings_.clear ();
 | |
|       break;
 | |
|     }
 | |
|   if (current_group.size ()) settings_.beginGroup (current_group);
 | |
| 
 | |
|   reposition_type_ = RepositionType::unchanged; // reset
 | |
|   bool exit {exit_flag_};
 | |
|   exit_flag_ = true;           // reset exit flag so normal exit works
 | |
| 
 | |
|   return exit;
 | |
| }
 | |
| 
 | |
| // populate a pop up menu with the configurations sub-menus for
 | |
| // maintenance including select, clone, clone from, delete, rename
 | |
| // and, reset
 | |
| void MultiSettings::impl::create_menu_actions (QMainWindow * main_window, QMenu * menu)
 | |
| {
 | |
|   main_window_ = main_window;
 | |
|   auto const& current_group = settings_.group ();
 | |
|   if (current_group.size ()) settings_.endGroup ();
 | |
|   SettingsGroup alternatives {&settings_, multi_settings_root_group};
 | |
|   // get the current configuration name
 | |
|   auto const& current_configuration_name = settings_.value (multi_settings_current_name_key, tr (default_string)).toString ();
 | |
|   // add the default configuration sub menu
 | |
|   QMenu * default_menu = create_sub_menu (menu, current_configuration_name, configurations_group_);
 | |
|   // and set as the current configuration
 | |
|   default_menu->menuAction ()->setChecked (true);
 | |
| 
 | |
|   // get the existing alternatives
 | |
|   auto const& available_configurations = settings_.childGroups ();
 | |
| 
 | |
|   // add all the other configurations
 | |
|   for (auto const& configuration_name: available_configurations)
 | |
|     {
 | |
|       create_sub_menu (menu, configuration_name, configurations_group_);
 | |
|     }
 | |
| 
 | |
|   if (current_group.size ()) settings_.beginGroup (current_group);
 | |
| 
 | |
|   if (name_change_emit_pending_)
 | |
|     {
 | |
|       Q_EMIT parent_->configurationNameChanged (unescape_ampersands (current_));
 | |
|       name_change_emit_pending_ = false;
 | |
|     }
 | |
| }
 | |
| 
 | |
| // call this at the end of the main program loop to determine if the
 | |
| // main window really wants to quit or to run again with a new configuration
 | |
| bool MultiSettings::impl::exit ()
 | |
| {
 | |
|   // ensure that configuration name changed signal gets fired on restart
 | |
|   name_change_emit_pending_ = true;
 | |
| 
 | |
|   // do any configuration swap required and return exit flag
 | |
|   return reposition ();
 | |
| }
 | |
| 
 | |
| QMenu * MultiSettings::impl::create_sub_menu (QMenu * parent_menu,
 | |
|                                               QString const& menu_title,
 | |
|                                               QActionGroup * action_group)
 | |
| {
 | |
|   auto sub_menu = parent_menu->addMenu (menu_title);
 | |
|   if (action_group) action_group->addAction (sub_menu->menuAction ());
 | |
|   sub_menu->menuAction ()->setCheckable (true);
 | |
| 
 | |
|   // populate sub-menu actions before showing
 | |
|   connect (sub_menu, &QMenu::aboutToShow, [this, parent_menu, sub_menu] () {
 | |
|       // depopulate before populating and showing because on Mac OS X
 | |
|       // there is an issue with depopulating in QMenu::aboutToHide()
 | |
|       // with connections being disconnected before they are actioned
 | |
|       while (!sub_menu->actions ().isEmpty ())
 | |
|         {
 | |
|           sub_menu->removeAction (sub_menu->actions ().last ());
 | |
|         }
 | |
| 
 | |
|       bool is_current {sub_menu->menuAction ()->text () == current_};
 | |
|       if (!is_current)
 | |
|         {
 | |
|           auto select_action = new QAction {tr ("&Switch To"), this};
 | |
|           sub_menu->addAction (select_action);
 | |
|           connect (select_action, &QAction::triggered, [this, sub_menu] (bool) {
 | |
|               select_configuration (sub_menu->title ());
 | |
|             });
 | |
|           sub_menu->addSeparator ();
 | |
|         }
 | |
| 
 | |
|       auto clone_action = new QAction {tr ("&Clone"), this};
 | |
|       sub_menu->addAction (clone_action);
 | |
|       connect (clone_action, &QAction::triggered, [this, parent_menu, sub_menu] (bool) {
 | |
|           clone_configuration (parent_menu, sub_menu);
 | |
|         });
 | |
| 
 | |
|       auto const& current_group = settings_.group ();
 | |
|       if (current_group.size ()) settings_.endGroup ();
 | |
|       SettingsGroup alternatives {&settings_, multi_settings_root_group};
 | |
|       if (settings_.childGroups ().size ())
 | |
|         {
 | |
|           auto clone_into_action = new QAction {tr ("Clone &Into ..."), this};
 | |
|           sub_menu->addAction (clone_into_action);
 | |
|           connect (clone_into_action, &QAction::triggered, [this, sub_menu] (bool) {
 | |
|               clone_into_configuration (sub_menu);
 | |
|             });
 | |
|         }
 | |
|       if (current_group.size ()) settings_.beginGroup (current_group);
 | |
|       auto reset_action = new QAction {tr ("R&eset"), this};
 | |
|       sub_menu->addAction (reset_action);
 | |
|       connect (reset_action, &QAction::triggered, [this, sub_menu] (bool) {
 | |
|           reset_configuration (sub_menu);
 | |
|         });
 | |
| 
 | |
|       auto rename_action = new QAction {tr ("&Rename ..."), this};
 | |
|       sub_menu->addAction (rename_action);
 | |
|       connect (rename_action, &QAction::triggered, [this, sub_menu] (bool) {
 | |
|           rename_configuration (sub_menu);
 | |
|         });
 | |
| 
 | |
|       if (!is_current)
 | |
|         {
 | |
|           auto delete_action = new QAction {tr ("&Delete"), this};
 | |
|           sub_menu->addAction (delete_action);
 | |
|           connect (delete_action, &QAction::triggered, [this, sub_menu] (bool) {
 | |
|               delete_configuration (sub_menu);
 | |
|             });
 | |
|         }
 | |
|     });
 | |
| 
 | |
|   return sub_menu;
 | |
| }
 | |
| 
 | |
| auto MultiSettings::impl::get_settings () const -> Dictionary
 | |
| {
 | |
|   Dictionary settings;
 | |
|   for (auto const& key: settings_.allKeys ())
 | |
|     {
 | |
|       // filter out multi settings group and empty settings
 | |
|       // placeholder
 | |
|       if (!key.contains (multi_settings_root_group)
 | |
|           && !key.contains (multi_settings_place_holder_key))
 | |
|         {
 | |
|           settings[key] = settings_.value (key);
 | |
|         }
 | |
|     }
 | |
|   return settings;
 | |
| }
 | |
| 
 | |
| void MultiSettings::impl::load_from (Dictionary const& dictionary, bool add_placeholder)
 | |
| {
 | |
|   if (dictionary.size ())
 | |
|     {
 | |
|       for (Dictionary::const_iterator iter = dictionary.constBegin ();
 | |
|            iter != dictionary.constEnd (); ++iter)
 | |
|         {
 | |
|           settings_.setValue (iter.key (), iter.value ());
 | |
|         }
 | |
|     }
 | |
|   else if (add_placeholder)
 | |
|     {
 | |
|       // add a placeholder key to stop the alternative configuration
 | |
|       // name from disappearing
 | |
|       settings_.setValue (multi_settings_place_holder_key, QVariant {});
 | |
|     }
 | |
|   settings_.sync ();
 | |
| }
 | |
| 
 | |
| void MultiSettings::impl::select_configuration (QString const& target_name)
 | |
| {
 | |
|   if (main_window_ && target_name != current_)
 | |
|     {
 | |
|       {
 | |
|         auto const& current_group = settings_.group ();
 | |
|         if (current_group.size ()) settings_.endGroup ();
 | |
|         // position to the alternative settings
 | |
|         SettingsGroup alternatives {&settings_, multi_settings_root_group};
 | |
|         // save the target settings
 | |
|         SettingsGroup target_group {&settings_, target_name};
 | |
|         new_settings_ = get_settings ();
 | |
|         if (current_group.size ()) settings_.beginGroup (current_group);
 | |
|       }
 | |
|       // and set up the restart
 | |
|       current_ = target_name;
 | |
|       Q_EMIT parent_->configurationNameChanged (unescape_ampersands (current_));
 | |
|       restart (RepositionType::save_and_replace);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void MultiSettings::impl::clone_configuration (QMenu * parent_menu, QMenu const * menu)
 | |
| {
 | |
|   auto const& current_group = settings_.group ();
 | |
|   if (current_group.size ()) settings_.endGroup ();
 | |
|   auto const& source_name = menu->title ();
 | |
| 
 | |
|   // settings to clone
 | |
|   Dictionary source_settings;
 | |
|   if (source_name == current_)
 | |
|     {
 | |
|       // grab the data to clone from the current settings
 | |
|       source_settings = get_settings ();
 | |
|     }
 | |
|   SettingsGroup alternatives {&settings_, multi_settings_root_group};
 | |
|   if (source_name != current_)
 | |
|     {
 | |
|       SettingsGroup source_group {&settings_, source_name};
 | |
|       source_settings = get_settings ();
 | |
|     }
 | |
| 
 | |
|   // find a new unique name
 | |
|   QString new_name_root {source_name + " - Copy"};;
 | |
|   QString new_name {new_name_root};
 | |
|   unsigned index {0};
 | |
|   do
 | |
|     {
 | |
|       if (index++) new_name = new_name_root + '(' + QString::number (index) + ')';
 | |
|     }
 | |
|   while (settings_.childGroups ().contains (new_name) || new_name == current_);
 | |
|   SettingsGroup new_group {&settings_, new_name};
 | |
|   load_from (source_settings);
 | |
| 
 | |
|   // insert the new configuration sub menu in the parent menu
 | |
|   create_sub_menu (parent_menu, new_name, configurations_group_);
 | |
|   if (current_group.size ()) settings_.beginGroup (current_group);
 | |
| }
 | |
| 
 | |
| void MultiSettings::impl::clone_into_configuration (QMenu const * menu)
 | |
| {
 | |
|   Q_ASSERT (main_window_);
 | |
|   if (!main_window_) return;
 | |
| 
 | |
|   auto const& current_group = settings_.group ();
 | |
|   if (current_group.size ()) settings_.endGroup ();
 | |
|   auto const& target_name = menu->title ();
 | |
| 
 | |
|   // get the current configuration name
 | |
|   QString current_group_name;
 | |
|   QStringList sources;
 | |
|   {
 | |
|     SettingsGroup alternatives {&settings_, multi_settings_root_group};
 | |
|     current_group_name = settings_.value (multi_settings_current_name_key).toString ();
 | |
| 
 | |
|     {
 | |
|       // get the source configuration name for the clone
 | |
|       sources = settings_.childGroups ();
 | |
|       sources << current_group_name;
 | |
|       sources.removeOne (target_name);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // pick a source configuration
 | |
|   ExistingNameDialog dialog {sources, main_window_};
 | |
|   if (sources.size () && (1 == sources.size () || QDialog::Accepted == dialog.exec ()))
 | |
|     {
 | |
|       QString source_name {1 == sources.size () ? sources.at (0) : dialog.name ()};
 | |
|       if (main_window_
 | |
|           && MessageBox::Yes == MessageBox::query_message (main_window_,
 | |
|                                                            tr ("Clone Into Configuration"),
 | |
|                                                            tr ("Confirm overwrite of all values for configuration \"%1\" with values from \"%2\"?")
 | |
|                                                            .arg (unescape_ampersands (target_name))
 | |
|                                                            .arg (unescape_ampersands (source_name))))
 | |
|         {
 | |
|           // grab the data to clone from
 | |
|           if (source_name == current_group_name)
 | |
|             {
 | |
|               // grab the data to clone from the current settings
 | |
|               new_settings_ = get_settings ();
 | |
|             }
 | |
|           else
 | |
|             {
 | |
|               SettingsGroup alternatives {&settings_, multi_settings_root_group};
 | |
|               SettingsGroup source_group {&settings_, source_name};
 | |
|               new_settings_ = get_settings ();
 | |
|             }
 | |
| 
 | |
|           // purge target settings and replace
 | |
|           if (target_name == current_)
 | |
|             {
 | |
|               // restart with new settings
 | |
|               restart (RepositionType::replace);
 | |
|             }
 | |
|           else
 | |
|             {
 | |
|               SettingsGroup alternatives {&settings_, multi_settings_root_group};
 | |
|               SettingsGroup target_group {&settings_, target_name};
 | |
|               settings_.remove (QString {}); // purge entire group
 | |
|               load_from (new_settings_);
 | |
|               new_settings_.clear ();
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|   if (current_group.size ()) settings_.beginGroup (current_group);
 | |
| }
 | |
| 
 | |
| void MultiSettings::impl::reset_configuration (QMenu const * menu)
 | |
| {
 | |
|   Q_ASSERT (main_window_);
 | |
|   if (!main_window_) return;
 | |
| 
 | |
|   auto const& target_name = menu->title ();
 | |
| 
 | |
|   if (!main_window_
 | |
|       || MessageBox::Yes != MessageBox::query_message (main_window_,
 | |
|                                                        tr ("Reset Configuration"),
 | |
|                                                        tr ("Confirm reset to default values for configuration \"%1\"?")
 | |
|                                                        .arg (unescape_ampersands (target_name))))
 | |
|     {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|   if (target_name == current_)
 | |
|     {
 | |
|       // restart with default settings
 | |
|       new_settings_.clear ();
 | |
|       restart (RepositionType::replace);
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       auto const& current_group = settings_.group ();
 | |
|       if (current_group.size ()) settings_.endGroup ();
 | |
|       SettingsGroup alternatives {&settings_, multi_settings_root_group};
 | |
|       SettingsGroup target_group {&settings_, target_name};
 | |
|       settings_.remove (QString {}); // purge entire group
 | |
|       // add a placeholder to stop alternative configuration name
 | |
|       // being lost
 | |
|       settings_.setValue (multi_settings_place_holder_key, QVariant {});
 | |
|       settings_.sync ();
 | |
|       if (current_group.size ()) settings_.beginGroup (current_group);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void MultiSettings::impl::rename_configuration (QMenu * menu)
 | |
| {
 | |
|   Q_ASSERT (main_window_);
 | |
|   if (!main_window_) return;
 | |
| 
 | |
|   auto const& current_group = settings_.group ();
 | |
|   if (current_group.size ()) settings_.endGroup ();
 | |
|   auto const& target_name = menu->title ();
 | |
| 
 | |
|   // gather names we cannot use
 | |
|   SettingsGroup alternatives {&settings_, multi_settings_root_group};
 | |
|   auto invalid_names = settings_.childGroups ();
 | |
|   invalid_names << settings_.value (multi_settings_current_name_key).toString ();
 | |
| 
 | |
|   // get the new name
 | |
|   NameDialog dialog {target_name, invalid_names, main_window_};
 | |
|   if (QDialog::Accepted == dialog.exec ())
 | |
|     {
 | |
|       if (target_name == current_)
 | |
|         {
 | |
|           settings_.setValue (multi_settings_current_name_key, dialog.new_name ());
 | |
|           settings_.sync ();
 | |
|           current_ = dialog.new_name ();
 | |
|           Q_EMIT parent_->configurationNameChanged (unescape_ampersands (current_));
 | |
|         }
 | |
|       else
 | |
|         {
 | |
|           // switch to the target group and fetch the configuration data
 | |
|           Dictionary target_settings;
 | |
|           {
 | |
|             // grab the target configuration settings
 | |
|             SettingsGroup target_group {&settings_, target_name};
 | |
|             target_settings = get_settings ();
 | |
|             // purge the old configuration data
 | |
|             settings_.remove (QString {}); // purge entire group
 | |
|           }
 | |
|           // load into new configuration group name
 | |
|           SettingsGroup target_group {&settings_, dialog.new_name ()};
 | |
|           load_from (target_settings);
 | |
|         }
 | |
|       // change the action text in the menu
 | |
|       menu->setTitle (dialog.new_name ());
 | |
|     }
 | |
|   if (current_group.size ()) settings_.beginGroup (current_group);
 | |
| }
 | |
| 
 | |
| void MultiSettings::impl::delete_configuration (QMenu * menu)
 | |
| {
 | |
|   Q_ASSERT (main_window_);
 | |
|   auto const& target_name = menu->title ();
 | |
| 
 | |
|   if (target_name == current_)
 | |
|     {
 | |
|       return;               // suicide not allowed here
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       if (!main_window_
 | |
|           || MessageBox::Yes != MessageBox::query_message (main_window_,
 | |
|                                                            tr ("Delete Configuration"),
 | |
|                                                            tr ("Confirm deletion of configuration \"%1\"?")
 | |
|                                                            .arg (unescape_ampersands (target_name))))
 | |
|         {
 | |
|           return;
 | |
|         }
 | |
|       auto const& current_group = settings_.group ();
 | |
|       if (current_group.size ()) settings_.endGroup ();
 | |
|       SettingsGroup alternatives {&settings_, multi_settings_root_group};
 | |
|       SettingsGroup target_group {&settings_, target_name};
 | |
|       // purge the configuration data
 | |
|       settings_.remove (QString {}); // purge entire group
 | |
|       settings_.sync ();
 | |
|       if (current_group.size ()) settings_.beginGroup (current_group);
 | |
|     }
 | |
|   // update the menu
 | |
|   menu->deleteLater ();
 | |
| }
 | |
| 
 | |
| void MultiSettings::impl::restart (RepositionType type)
 | |
| {
 | |
|   Q_ASSERT (main_window_);
 | |
|   reposition_type_ = type;
 | |
|   exit_flag_ = false;
 | |
|   main_window_->close ();
 | |
|   main_window_ = nullptr;
 | |
| }
 |