Remove restrictions on multiple configurations maintenance

The initial conservative implementation has been replaced by a simpler
one that swaps configurations around in the settings file as specified
by the multiple configurations menu options.

git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@6631 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
This commit is contained in:
Bill Somerville 2016-04-21 18:56:59 +00:00
parent 1cae0b8aa9
commit 169692fc0a
2 changed files with 251 additions and 201 deletions

View File

@ -27,6 +27,8 @@
#include <QList> #include <QList>
#include <QMetaObject> #include <QMetaObject>
#include "SettingsGroup.hpp"
#include "pimpl_impl.hpp" #include "pimpl_impl.hpp"
namespace namespace
@ -34,6 +36,8 @@ namespace
char const * default_string = QT_TRANSLATE_NOOP ("MultiSettings", "Default"); char const * default_string = QT_TRANSLATE_NOOP ("MultiSettings", "Default");
char const * multi_settings_root_group = "MultiSettings"; char const * multi_settings_root_group = "MultiSettings";
char const * multi_settings_current_group_key = "CurrentMultiSettingsConfiguration"; char const * multi_settings_current_group_key = "CurrentMultiSettingsConfiguration";
char const * multi_settings_current_name_key = "CurrentName";
char const * multi_settings_place_holder_key = "MultiSettingsPlaceHolder";
// calculate a useable and unique settings file path // calculate a useable and unique settings file path
QString settings_path () QString settings_path ()
@ -77,7 +81,7 @@ namespace
name_line_edit_.setValidator (name_validator); name_line_edit_.setValidator (name_validator);
connect (&name_line_edit_, &QLineEdit::textChanged, [current_names, button_box] (QString const& name) { connect (&name_line_edit_, &QLineEdit::textChanged, [current_names, button_box] (QString const& name) {
bool valid {name.trimmed () != tr (default_string) && !current_names.contains (name.trimmed ())}; bool valid {!current_names.contains (name.trimmed ())};
button_box->button (QDialogButtonBox::Ok)->setEnabled (valid); button_box->button (QDialogButtonBox::Ok)->setEnabled (valid);
}); });
connect (button_box, &QDialogButtonBox::accepted, this, &QDialog::accept); connect (button_box, &QDialogButtonBox::accepted, this, &QDialog::accept);
@ -155,15 +159,8 @@ private:
// extract all settings from the current QSettings group // extract all settings from the current QSettings group
Dictionary get_settings () const; Dictionary get_settings () const;
// leave the current group and move to the multi settings root group
void switch_to_root_group ();
// starting from he multi settings root group, switch back to the
// current group
void switch_to_group (QString const& group_name);
// write the settings values from the dictionary to the current group // write the settings values from the dictionary to the current group
void load_from (Dictionary const&); void load_from (Dictionary const&, bool add_placeholder = true);
// switch to this configuration // switch to this configuration
void select_configuration (QMainWindow *); void select_configuration (QMainWindow *);
@ -183,9 +180,11 @@ private:
// remove a configuration // remove a configuration
void delete_configuration (QMainWindow *); void delete_configuration (QMainWindow *);
QString current_; // current/new configuration or empty for default QString current_;
QStringList available_; // all non-default configurations
// including new one // action to take on restart
enum class RepositionType {unchanged, replace, save_and_replace} reposition_type_;
Dictionary new_settings_;
bool exit_flag_; // false means loop around with new bool exit_flag_; // false means loop around with new
// configuration // configuration
QActionGroup * configurations_group_; QActionGroup * configurations_group_;
@ -226,6 +225,7 @@ bool MultiSettings::exit ()
MultiSettings::impl::impl () MultiSettings::impl::impl ()
: settings_ {settings_path (), QSettings::IniFormat} : settings_ {settings_path (), QSettings::IniFormat}
, reposition_type_ {RepositionType::unchanged}
, exit_flag_ {true} , exit_flag_ {true}
, configurations_group_ {new QActionGroup {this}} , configurations_group_ {new QActionGroup {this}}
, select_action_ {new QAction {tr ("&Switch To"), this}} , select_action_ {new QAction {tr ("&Switch To"), this}}
@ -241,33 +241,93 @@ MultiSettings::impl::impl ()
throw std::runtime_error {QString {"Cannot access \"%1\" for writing"} throw std::runtime_error {QString {"Cannot access \"%1\" for writing"}
.arg (settings_.fileName ()).toStdString ()}; .arg (settings_.fileName ()).toStdString ()};
} }
current_ = settings_.value (multi_settings_current_group_key).toString ();
reposition (); // 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
{
SettingsGroup alternatives {&settings_, multi_settings_root_group};
current_ = settings_.value (multi_settings_current_name_key).toString ();
if (!current_.size ())
{
current_ = tr (default_string);
settings_.setValue (multi_settings_current_name_key, current_);
}
}
settings_.sync ();
} }
// do actions that can only be done once all the windows are closed
bool MultiSettings::impl::reposition () bool MultiSettings::impl::reposition ()
{ {
// save new current and reposition settings switch (reposition_type_)
// assumes settings are positioned at the root
settings_.setValue (multi_settings_current_group_key, current_);
settings_.beginGroup (multi_settings_root_group);
available_ = settings_.childGroups ();
if (current_.size ()) // new configuration is not the default
{ {
if (!available_.contains (current_)) 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 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 ())
{ {
// insert new group name as it may not have been created yet if (!key.contains (multi_settings_root_group))
available_ << current_; {
settings_.remove (key);
}
} }
// switch to the specified configuration // insert the new settings
settings_.beginGroup (current_); load_from (new_settings_, false);
} // now we have set up the new current we can safely purge it
else // from the alternatives
{ {
settings_.endGroup (); // back to root for default configuration SettingsGroup alternatives {&settings_, multi_settings_root_group};
{
SettingsGroup purge_group {&settings_, current_};
settings_.remove (""); // 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;
} }
reposition_type_ = RepositionType::unchanged; // reset
bool exit {exit_flag_}; bool exit {exit_flag_};
exit_flag_ = true; // reset exit flag so normal exit works exit_flag_ = true; // reset exit flag so normal exit works
return exit; return exit;
} }
@ -276,20 +336,23 @@ bool MultiSettings::impl::reposition ()
// and, reset // and, reset
void MultiSettings::impl::create_menu_actions (QMainWindow * main_window, QMenu * menu) void MultiSettings::impl::create_menu_actions (QMainWindow * main_window, QMenu * menu)
{ {
SettingsGroup alternatives {&settings_, multi_settings_root_group};
// get the current configuration name
auto current_configuration_name = settings_.value (multi_settings_current_name_key, tr (default_string)).toString ();
// add the default configuration sub menu // add the default configuration sub menu
QMenu * default_menu = create_sub_menu (menu, tr (default_string), configurations_group_); QMenu * default_menu = create_sub_menu (menu, current_configuration_name, configurations_group_);
// and set as the current configuration
default_menu->menuAction ()->setChecked (true);
QStringList available_configurations;
// get the existing alternatives
available_configurations = settings_.childGroups ();
// add all the other configurations // add all the other configurations
for (auto const& configuration_name: available_) for (auto const& configuration_name: available_configurations)
{ {
QMenu * configuration_menu = create_sub_menu (menu, configuration_name, configurations_group_); create_sub_menu (menu, configuration_name, configurations_group_);
if (current_ == configuration_name)
{
default_menu = configuration_menu;
}
} }
// and set the current configuration
default_menu->menuAction ()->setChecked (true);
// hook up configuration actions // hook up configuration actions
action_connections_ << connect (select_action_, &QAction::triggered, [this, main_window] (bool) { action_connections_ << connect (select_action_, &QAction::triggered, [this, main_window] (bool) {
@ -322,12 +385,7 @@ bool MultiSettings::impl::exit ()
} }
action_connections_.clear (); action_connections_.clear ();
if (settings_.group ().size ()) // not default configuration // do any configuration swap required and return exit flag
{
// back to the settings root
settings_.endGroup ();
settings_.endGroup ();
}
return reposition (); return reposition ();
} }
@ -345,17 +403,17 @@ QMenu * MultiSettings::impl::create_sub_menu (QMenu * parent_menu,
sub_menu->addAction (rename_action_); sub_menu->addAction (rename_action_);
sub_menu->addAction (reset_action_); sub_menu->addAction (reset_action_);
sub_menu->addAction (delete_action_); sub_menu->addAction (delete_action_);
// disable disallowed actions before showing sub menu
connect (sub_menu, &QMenu::aboutToShow, [this, sub_menu] () { connect (sub_menu, &QMenu::aboutToShow, [this, sub_menu] () {
bool is_default {sub_menu->menuAction ()->text () == tr (default_string)}; bool is_current {sub_menu->menuAction ()->text () == current_};
bool is_current {sub_menu->menuAction ()->text () == current_
|| (current_.isEmpty () && is_default)};
select_action_->setEnabled (!is_current); select_action_->setEnabled (!is_current);
clone_into_action_->setEnabled (!is_current); delete_action_->setEnabled (!is_current);
rename_action_->setEnabled (!is_current && !is_default); SettingsGroup alternatives {&settings_, multi_settings_root_group};
reset_action_->setEnabled (!is_current); clone_into_action_->setEnabled (settings_.childGroups ().size ());
delete_action_->setEnabled (!is_default && !is_current);
active_sub_menu_ = sub_menu; active_sub_menu_ = sub_menu;
}); });
return sub_menu; return sub_menu;
} }
@ -364,9 +422,8 @@ auto MultiSettings::impl::get_settings () const -> Dictionary
Dictionary settings; Dictionary settings;
for (auto const& key: settings_.allKeys ()) for (auto const& key: settings_.allKeys ())
{ {
// filter out multi settings keys // filter out multi settings group
if (!key.contains (multi_settings_current_group_key) if (!key.contains (multi_settings_root_group))
&& !key.contains (multi_settings_root_group))
{ {
settings[key] = settings_.value (key); settings[key] = settings_.value (key);
} }
@ -374,48 +431,43 @@ auto MultiSettings::impl::get_settings () const -> Dictionary
return settings; return settings;
} }
void MultiSettings::impl::switch_to_root_group () void MultiSettings::impl::load_from (Dictionary const& dictionary, bool add_placeholder)
{ {
if (current_.size ()) if (dictionary.size ())
{ {
settings_.endGroup (); for (Dictionary::const_iterator iter = dictionary.constBegin ();
iter != dictionary.constEnd (); ++iter)
{
settings_.setValue (iter.key (), iter.value ());
}
} }
else else if (add_placeholder)
{ {
settings_.beginGroup (multi_settings_root_group); // add a placeholder key to stop the alternative configuration
} // name from disappearing
} settings_.setValue (multi_settings_place_holder_key, QVariant {});
void MultiSettings::impl::switch_to_group (QString const& group_name)
{
if (group_name.size () && group_name != tr (default_string))
{
settings_.beginGroup (group_name);
}
else
{
settings_.endGroup (); // back to root for default
}
}
void MultiSettings::impl::load_from (Dictionary const& dictionary)
{
for (Dictionary::const_iterator iter = dictionary.constBegin ();
iter != dictionary.constEnd (); ++iter)
{
settings_.setValue (iter.key (), iter.value ());
} }
settings_.sync ();
} }
void MultiSettings::impl::select_configuration (QMainWindow * main_window) void MultiSettings::impl::select_configuration (QMainWindow * main_window)
{ {
if (active_sub_menu_) if (active_sub_menu_)
{ {
auto const& name = active_sub_menu_->title (); auto const& target_name = active_sub_menu_->title ();
if (name != current_) if (target_name != current_)
{ {
current_ = tr (default_string) == name ? QString {} : name; {
// 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 ();
}
// and set up the restart
current_ = target_name;
reposition_type_ = RepositionType::save_and_replace;
exit_flag_ = false; exit_flag_ = false;
main_window->close (); main_window->close ();
} }
@ -426,15 +478,24 @@ void MultiSettings::impl::clone_configuration (QMenu * menu)
{ {
if (active_sub_menu_) if (active_sub_menu_)
{ {
auto const& name = active_sub_menu_->title (); auto const& source_name = active_sub_menu_->title ();
// grab the data to clone // settings to clone
Dictionary old_settings {get_settings ()}; Dictionary source_settings;
if (source_name == current_)
switch_to_root_group (); {
// 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 // find a new unique name
QString new_name_root {name + " - Copy"};; QString new_name_root {source_name + " - Copy"};;
QString new_name {new_name_root}; QString new_name {new_name_root};
unsigned index {0}; unsigned index {0};
do do
@ -442,14 +503,8 @@ void MultiSettings::impl::clone_configuration (QMenu * menu)
if (index++) new_name = new_name_root + '(' + QString::number (index) + ')'; if (index++) new_name = new_name_root + '(' + QString::number (index) + ')';
} }
while (settings_.childGroups ().contains (new_name)); while (settings_.childGroups ().contains (new_name));
settings_.beginGroup (new_name); SettingsGroup new_group {&settings_, new_name};
load_from (source_settings);
// Clone the settings
load_from (old_settings);
// switch back to current group
settings_.endGroup ();
switch_to_group (current_);
// insert the new configuration sub menu in the parent menu // insert the new configuration sub menu in the parent menu
create_sub_menu (menu, new_name, configurations_group_); create_sub_menu (menu, new_name, configurations_group_);
@ -460,17 +515,24 @@ void MultiSettings::impl::clone_into_configuration (QMainWindow * main_window)
{ {
if (active_sub_menu_) if (active_sub_menu_)
{ {
auto const& name = active_sub_menu_->title (); auto const& target_name = active_sub_menu_->title ();
switch_to_root_group (); // 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
QStringList sources {settings_.childGroups ()};
if (name != tr (default_string))
{ {
sources.removeOne (name); // get the source configuration name for the clone
sources << tr (default_string); sources = settings_.childGroups ();
sources << current_group_name;
sources.removeOne (target_name);
} }
}
// pick a source configuration
ExistingNameDialog dialog {sources, main_window}; ExistingNameDialog dialog {sources, main_window};
if (sources.size () && (1 == sources.size () || QDialog::Accepted == dialog.exec ())) if (sources.size () && (1 == sources.size () || QDialog::Accepted == dialog.exec ()))
{ {
@ -478,57 +540,40 @@ void MultiSettings::impl::clone_into_configuration (QMainWindow * main_window)
if (QMessageBox::Yes == QMessageBox::question (main_window, if (QMessageBox::Yes == QMessageBox::question (main_window,
tr ("Clone Into Configuration"), tr ("Clone Into Configuration"),
tr ("Confirm overwrite of all values for configuration \"%1\" with values from \"%2\"?") tr ("Confirm overwrite of all values for configuration \"%1\" with values from \"%2\"?")
.arg (name) .arg (target_name)
.arg (source_name))) .arg (source_name)))
{ {
// grab the data to clone // grab the data to clone from
switch_to_group (source_name); if (source_name == current_group_name)
Dictionary clone_settings {get_settings ()};
if (tr (default_string) == source_name)
{ {
settings_.beginGroup (multi_settings_root_group); // grab the data to clone from the current settings
new_settings_ = get_settings ();
} }
else else
{ {
settings_.endGroup (); SettingsGroup alternatives {&settings_, multi_settings_root_group};
SettingsGroup source_group {&settings_, source_name};
new_settings_ = get_settings ();
} }
// purge target settings // purge target settings and replace
if (tr (default_string) == name) if (target_name == current_)
{ {
settings_.endGroup (); // restart with new settings
// piecemeal reset for default configuration reposition_type_ = RepositionType::replace;
for (auto const& key: settings_.allKeys ()) exit_flag_ = false;
{ main_window->close ();
if (!key.contains (multi_settings_current_group_key)
&& !key.contains (multi_settings_root_group))
{
settings_.remove (key);
}
}
} }
else else
{ {
settings_.beginGroup (name); SettingsGroup alternatives {&settings_, multi_settings_root_group};
SettingsGroup target_group {&settings_, target_name};
settings_.remove (""); // purge entire group settings_.remove (""); // purge entire group
} load_from (new_settings_);
new_settings_.clear ();
// load the settings
load_from (clone_settings);
if (tr (default_string) == name)
{
settings_.beginGroup (multi_settings_root_group);
}
else
{
settings_.endGroup ();
} }
} }
} }
switch_to_group (current_);
} }
} }
@ -536,39 +581,34 @@ void MultiSettings::impl::reset_configuration (QMainWindow * main_window)
{ {
if (active_sub_menu_) if (active_sub_menu_)
{ {
auto const& name = active_sub_menu_->title (); auto const& target_name = active_sub_menu_->title ();
if (QMessageBox::Yes != QMessageBox::question (main_window, if (QMessageBox::Yes != QMessageBox::question (main_window,
tr ("Reset Configuration"), tr ("Reset Configuration"),
tr ("Confirm reset to default values for configuration \"%1\"?") tr ("Confirm reset to default values for configuration \"%1\"?")
.arg (name))) .arg (target_name)))
{ {
return; return;
} }
switch_to_root_group (); if (target_name == current_)
if (tr (default_string) == name)
{ {
settings_.endGroup (); // restart with default settings
// piecemeal reset for default configuration reposition_type_ = RepositionType::replace;
for (auto const& key: settings_.allKeys ()) new_settings_.clear ();
{ exit_flag_ = false;
if (!key.contains (multi_settings_current_group_key) main_window->close ();
&& !key.contains (multi_settings_root_group))
{
settings_.remove (key);
}
}
settings_.beginGroup (multi_settings_root_group);
} }
else else
{ {
settings_.beginGroup (name); SettingsGroup alternatives {&settings_, multi_settings_root_group};
SettingsGroup target_group {&settings_, target_name};
settings_.remove (""); // purge entire group settings_.remove (""); // purge entire group
settings_.endGroup (); // add a placeholder to stop alternative configuration name
// being lost
settings_.setValue (multi_settings_place_holder_key, QVariant {});
settings_.sync ();
} }
switch_to_group (current_);
} }
} }
@ -576,34 +616,41 @@ void MultiSettings::impl::rename_configuration (QMainWindow * main_window)
{ {
if (active_sub_menu_) if (active_sub_menu_)
{ {
auto const& name = active_sub_menu_->title (); auto const& target_name = active_sub_menu_->title ();
switch_to_root_group (); // 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 // get the new name
NameDialog dialog {name, settings_.childGroups (), main_window}; NameDialog dialog {target_name, invalid_names, main_window};
if (QDialog::Accepted == dialog.exec ()) if (QDialog::Accepted == dialog.exec ())
{ {
// switch to the target group and fetch the configuration data if (target_name == current_)
settings_.beginGroup (name); {
settings_.setValue (multi_settings_current_name_key, dialog.new_name ());
// Clone the settings settings_.sync ();
Dictionary target_settings {get_settings ()}; current_ = dialog.new_name ();
settings_.endGroup (); }
settings_.beginGroup (dialog.new_name ()); else
load_from (target_settings); {
// switch to the target group and fetch the configuration data
// purge the old configuration data Dictionary target_settings;
settings_.endGroup (); {
settings_.beginGroup (name); // grab the target configuration settings
settings_.remove (""); // purge entire group SettingsGroup target_group {&settings_, target_name};
settings_.endGroup (); target_settings = get_settings ();
// purge the old configuration data
settings_.remove (""); // 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 // change the action text in the menu
active_sub_menu_->setTitle (dialog.new_name ()); active_sub_menu_->setTitle (dialog.new_name ());
} }
switch_to_group (current_);
} }
} }
@ -611,23 +658,28 @@ void MultiSettings::impl::delete_configuration (QMainWindow * main_window)
{ {
if (active_sub_menu_) if (active_sub_menu_)
{ {
auto const& name = active_sub_menu_->title (); auto const& target_name = active_sub_menu_->title ();
if (QMessageBox::Yes != QMessageBox::question (main_window, if (target_name == current_)
tr ("Delete Configuration"),
tr ("Confirm deletion of configuration \"%1\"?")
.arg (name)))
{ {
return; return; // suicide not allowed here
} }
else
switch_to_root_group (); {
if (QMessageBox::Yes != QMessageBox::question (main_window,
settings_.beginGroup (name); tr ("Delete Configuration"),
settings_.remove (""); // purge entire group tr ("Confirm deletion of configuration \"%1\"?")
settings_.endGroup (); .arg (target_name)))
switch_to_group (current_); {
return;
}
SettingsGroup alternatives {&settings_, multi_settings_root_group};
SettingsGroup target_group {&settings_, target_name};
// purge the configuration data
settings_.remove (""); // purge entire group
settings_.sync ();
}
// update the menu
active_sub_menu_->deleteLater (), active_sub_menu_ = nullptr; active_sub_menu_->deleteLater (), active_sub_menu_ = nullptr;
} }
} }

View File

@ -15,14 +15,12 @@ class QMenu;
// settings as stored in a QSettings INI style file. As far as the // settings as stored in a QSettings INI style file. As far as the
// application is concerned it uses the QSettings instance returned // application is concerned it uses the QSettings instance returned
// by the MultiSettings::settings() method as if it were the one and // by the MultiSettings::settings() method as if it were the one and
// only QSettings object. The only caution is the the application // only QSettings object. The alternative settings are stored as
// must not end the outer settings group since the alternative // QSettings groups which are children of a root level group called
// settings are actually maintained as QSettings groups which are // MultiSettings. The current settings are themselves stored at the
// children of a root level group called MultiSettings. The default // root so the QSettings group name MultiSettings is reserved. Also
// settings are themselves stored at the root so the QSettings group // at the root level a key called CurrentMultiSettingsConfiguration
// name MultiSettings is reserved. Also at the root level a key // is reserved to store the current configuration name.
// called CurrentMultiSettingsConfiguration is reserved to store the
// current configuration name.
// //
// //
// Example Usage: // Example Usage: