New UDP message SwitchConfiguration(14) to switch to an existing configuration

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.
This commit is contained in:
Bill Somerville 2019-06-13 01:44:28 +01:00
parent fc07bd9287
commit 3f5a996842
No known key found for this signature in database
GPG Key ID: D864B06D1E81618F
12 changed files with 171 additions and 77 deletions

View File

@ -261,6 +261,18 @@ void MessageClient::impl::parse_message (QByteArray const& msg)
} }
break; break;
case NetworkMessage::SwitchConfiguration:
{
QByteArray configuration_name;
in >> configuration_name;
TRACE_UDP ("SwitchConfiguration name:" << configuration_name);
if (check_status (in) != Fail && configuration_name.size ())
{
Q_EMIT self_->switch_configuration (QString::fromUtf8 (configuration_name));
}
}
break;
default: default:
// Ignore // Ignore
// //
@ -444,7 +456,8 @@ void MessageClient::status_update (Frequency f, QString const& mode, QString con
, qint32 rx_df, qint32 tx_df, QString const& de_call , qint32 rx_df, qint32 tx_df, QString const& de_call
, QString const& de_grid, QString const& dx_grid , QString const& de_grid, QString const& dx_grid
, bool watchdog_timeout, QString const& sub_mode , bool watchdog_timeout, QString const& sub_mode
, bool fast_mode, quint8 special_op_mode) , bool fast_mode, quint8 special_op_mode
, QString const& configuration_name)
{ {
if (m_->server_port_ && !m_->server_string_.isEmpty ()) if (m_->server_port_ && !m_->server_string_.isEmpty ())
{ {
@ -453,8 +466,8 @@ void MessageClient::status_update (Frequency f, QString const& mode, QString con
out << f << mode.toUtf8 () << dx_call.toUtf8 () << report.toUtf8 () << tx_mode.toUtf8 () out << f << mode.toUtf8 () << dx_call.toUtf8 () << report.toUtf8 () << tx_mode.toUtf8 ()
<< tx_enabled << transmitting << decoding << rx_df << tx_df << de_call.toUtf8 () << tx_enabled << transmitting << decoding << rx_df << tx_df << de_call.toUtf8 ()
<< de_grid.toUtf8 () << dx_grid.toUtf8 () << watchdog_timeout << sub_mode.toUtf8 () << de_grid.toUtf8 () << dx_grid.toUtf8 () << watchdog_timeout << sub_mode.toUtf8 ()
<< fast_mode << special_op_mode; << fast_mode << special_op_mode << configuration_name.toUtf8 ();
TRACE_UDP ("frequency:" << f << "mode:" << mode << "DX:" << dx_call << "report:" << report << "Tx mode:" << tx_mode << "tx_enabled:" << tx_enabled << "Tx:" << transmitting << "decoding:" << decoding << "Rx df:" << rx_df << "Tx df:" << tx_df << "DE:" << de_call << "DE grid:" << de_grid << "DX grid:" << dx_grid << "w/d t/o:" << watchdog_timeout << "sub_mode:" << sub_mode << "fast mode:" << fast_mode << "spec op mode:" << special_op_mode); TRACE_UDP ("frequency:" << f << "mode:" << mode << "DX:" << dx_call << "report:" << report << "Tx mode:" << tx_mode << "tx_enabled:" << tx_enabled << "Tx:" << transmitting << "decoding:" << decoding << "Rx df:" << rx_df << "Tx df:" << tx_df << "DE:" << de_call << "DE grid:" << de_grid << "DX grid:" << dx_grid << "w/d t/o:" << watchdog_timeout << "sub_mode:" << sub_mode << "fast mode:" << fast_mode << "spec op mode:" << special_op_mode << "configuration name:" << configuration_name);
m_->send_message (out, message); m_->send_message (out, message);
} }
} }

View File

@ -52,7 +52,7 @@ public:
, QString const& tx_mode, bool tx_enabled, bool transmitting, bool decoding , QString const& tx_mode, bool tx_enabled, bool transmitting, bool decoding
, qint32 rx_df, qint32 tx_df, QString const& de_call, QString const& de_grid , qint32 rx_df, qint32 tx_df, QString const& de_call, QString const& de_grid
, QString const& dx_grid, bool watchdog_timeout, QString const& sub_mode , QString const& dx_grid, bool watchdog_timeout, QString const& sub_mode
, bool fast_mode, quint8 special_op_mode); , bool fast_mode, quint8 special_op_mode, QString const& configuration_name);
Q_SLOT void decode (bool is_new, QTime time, qint32 snr, float delta_time, quint32 delta_frequency Q_SLOT void decode (bool is_new, QTime time, qint32 snr, float delta_time, quint32 delta_frequency
, QString const& mode, QString const& message, bool low_confidence , QString const& mode, QString const& message, bool low_confidence
, bool off_air); , bool off_air);
@ -105,6 +105,10 @@ public:
// callsign request for the specified call // callsign request for the specified call
Q_SIGNAL void highlight_callsign (QString const& callsign, QColor const& bg, QColor const& fg, bool last_only); Q_SIGNAL void highlight_callsign (QString const& callsign, QColor const& bg, QColor const& fg, bool last_only);
// this signal is emitted if the server has requested a switch to a
// new configuration
Q_SIGNAL void switch_configuration (QString const& configuration_name);
// this signal is emitted when network errors occur or if a host // this signal is emitted when network errors occur or if a host
// lookup fails // lookup fails
Q_SIGNAL void error (QString const&) const; Q_SIGNAL void error (QString const&) const;

View File

@ -247,9 +247,10 @@ void MessageServer::impl::parse_message (QHostAddress const& sender, port_type s
QByteArray sub_mode; QByteArray sub_mode;
bool fast_mode {false}; bool fast_mode {false};
quint8 special_op_mode {0}; quint8 special_op_mode {0};
QByteArray configuration_name;
in >> f >> mode >> dx_call >> report >> tx_mode >> tx_enabled >> transmitting >> decoding in >> f >> mode >> dx_call >> report >> tx_mode >> tx_enabled >> transmitting >> decoding
>> rx_df >> tx_df >> de_call >> de_grid >> dx_grid >> watchdog_timeout >> sub_mode >> rx_df >> tx_df >> de_call >> de_grid >> dx_grid >> watchdog_timeout >> sub_mode
>> fast_mode >> special_op_mode; >> fast_mode >> special_op_mode >> configuration_name;
if (check_status (in) != Fail) if (check_status (in) != Fail)
{ {
Q_EMIT self_->status_update (id, f, QString::fromUtf8 (mode), QString::fromUtf8 (dx_call) Q_EMIT self_->status_update (id, f, QString::fromUtf8 (mode), QString::fromUtf8 (dx_call)
@ -258,7 +259,7 @@ void MessageServer::impl::parse_message (QHostAddress const& sender, port_type s
, QString::fromUtf8 (de_call), QString::fromUtf8 (de_grid) , QString::fromUtf8 (de_call), QString::fromUtf8 (de_grid)
, QString::fromUtf8 (dx_grid), watchdog_timeout , QString::fromUtf8 (dx_grid), watchdog_timeout
, QString::fromUtf8 (sub_mode), fast_mode , QString::fromUtf8 (sub_mode), fast_mode
, special_op_mode); , special_op_mode, QString::fromUtf8 (configuration_name));
} }
} }
break; break;
@ -541,3 +542,15 @@ void MessageServer::highlight_callsign (QString const& id, QString const& callsi
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_); m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
} }
} }
void MessageServer::switch_configuration (QString const& id, QString const& configuration_name)
{
auto iter = m_->clients_.find (id);
if (iter != std::end (m_->clients_))
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::SwitchConfiguration, id, (*iter).negotiated_schema_number_};
out << configuration_name.toUtf8 ();
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
}
}

View File

@ -72,6 +72,9 @@ public:
, QColor const& bg = QColor {}, QColor const& fg = QColor {} , QColor const& bg = QColor {}, QColor const& fg = QColor {}
, bool last_only = false); , bool last_only = false);
// ask the client with identification 'id' to switch configuration
Q_SLOT void switch_configuration (QString const& id, QString const& configuration_name);
// the following signals are emitted when a client broadcasts the // the following signals are emitted when a client broadcasts the
// matching message // matching message
Q_SIGNAL void client_opened (QString const& id, QString const& version, QString const& revision); Q_SIGNAL void client_opened (QString const& id, QString const& version, QString const& revision);
@ -80,7 +83,7 @@ public:
, bool transmitting, bool decoding, qint32 rx_df, qint32 tx_df , bool transmitting, bool decoding, qint32 rx_df, qint32 tx_df
, QString const& de_call, QString const& de_grid, QString const& dx_grid , QString const& de_call, QString const& de_grid, QString const& dx_grid
, bool watchdog_timeout, QString const& sub_mode, bool fast_mode , bool watchdog_timeout, QString const& sub_mode, bool fast_mode
, quint8 special_op_mode); , quint8 special_op_mode, QString const& configuration_name);
Q_SIGNAL void client_closed (QString const& id); Q_SIGNAL void client_closed (QString const& id);
Q_SIGNAL void decode (bool is_new, QString const& id, QTime time, qint32 snr, float delta_time Q_SIGNAL void decode (bool is_new, QString const& id, QTime time, qint32 snr, float delta_time
, quint32 delta_frequency, QString const& mode, QString const& message , quint32 delta_frequency, QString const& mode, QString const& message

View File

@ -159,13 +159,16 @@ public:
bool exit (); bool exit ();
QSettings settings_; QSettings settings_;
QString current_;
// switch to this configuration
void select_configuration (QString const& target_name);
private: private:
using Dictionary = QMap<QString, QVariant>; using Dictionary = QMap<QString, QVariant>;
// create a configuration maintenance sub menu // create a configuration maintenance sub menu
QMenu * create_sub_menu (QMainWindow * main_window, QMenu * create_sub_menu (QMenu * parent,
QMenu * parent,
QString const& menu_title, QString const& menu_title,
QActionGroup * = nullptr); QActionGroup * = nullptr);
@ -175,32 +178,32 @@ private:
// 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&, bool add_placeholder = true); void load_from (Dictionary const&, bool add_placeholder = true);
// switch to this configuration
void select_configuration (QMainWindow *, QMenu const *);
// clone this configuration // clone this configuration
void clone_configuration (QMainWindow * main_window, QMenu *, QMenu const *); void clone_configuration (QMenu *, QMenu const *);
// update this configuration from another // update this configuration from another
void clone_into_configuration (QMainWindow *, QMenu const *); void clone_into_configuration (QMenu const *);
// reset configuration to default values // reset configuration to default values
void reset_configuration (QMainWindow *, QMenu const *); void reset_configuration (QMenu const *);
// change configuration name // change configuration name
void rename_configuration (QMainWindow *, QMenu *); void rename_configuration (QMenu *);
// remove a configuration // remove a configuration
void delete_configuration (QMainWindow *, QMenu *); 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 MultiSettings const * parent_; // required for emitting signals
QMainWindow * main_window_;
bool name_change_emit_pending_; // delayed until menu built bool name_change_emit_pending_; // delayed until menu built
QFont original_font_; QFont original_font_;
QString current_;
// action to take on restart RepositionType reposition_type_;
enum class RepositionType {unchanged, replace, save_and_replace} reposition_type_;
Dictionary new_settings_; Dictionary new_settings_;
bool exit_flag_; // false means loop around with new bool exit_flag_; // false means loop around with new
// configuration // configuration
@ -267,6 +270,16 @@ void MultiSettings::create_menu_actions (QMainWindow * main_window, QMenu * menu
m_->create_menu_actions (main_window, 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 () bool MultiSettings::exit ()
{ {
return m_->exit (); return m_->exit ();
@ -275,6 +288,7 @@ bool MultiSettings::exit ()
MultiSettings::impl::impl (MultiSettings const * parent, QString const& config_name) MultiSettings::impl::impl (MultiSettings const * parent, QString const& config_name)
: settings_ {settings_path (), QSettings::IniFormat} : settings_ {settings_path (), QSettings::IniFormat}
, parent_ {parent} , parent_ {parent}
, main_window_ {nullptr}
, name_change_emit_pending_ {true} , name_change_emit_pending_ {true}
, reposition_type_ {RepositionType::unchanged} , reposition_type_ {RepositionType::unchanged}
, exit_flag_ {true} , exit_flag_ {true}
@ -411,13 +425,14 @@ 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)
{ {
main_window_ = main_window;
auto const& current_group = settings_.group (); auto const& current_group = settings_.group ();
if (current_group.size ()) settings_.endGroup (); if (current_group.size ()) settings_.endGroup ();
SettingsGroup alternatives {&settings_, multi_settings_root_group}; SettingsGroup alternatives {&settings_, multi_settings_root_group};
// get the current configuration name // get the current configuration name
auto const& current_configuration_name = settings_.value (multi_settings_current_name_key, tr (default_string)).toString (); auto const& 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 (main_window, menu, current_configuration_name, configurations_group_); QMenu * default_menu = create_sub_menu (menu, current_configuration_name, configurations_group_);
// and set as the current configuration // and set as the current configuration
default_menu->menuAction ()->setChecked (true); default_menu->menuAction ()->setChecked (true);
@ -427,7 +442,7 @@ void MultiSettings::impl::create_menu_actions (QMainWindow * main_window, QMenu
// add all the other configurations // add all the other configurations
for (auto const& configuration_name: available_configurations) for (auto const& configuration_name: available_configurations)
{ {
create_sub_menu (main_window, menu, configuration_name, configurations_group_); create_sub_menu (menu, configuration_name, configurations_group_);
} }
if (current_group.size ()) settings_.beginGroup (current_group); if (current_group.size ()) settings_.beginGroup (current_group);
@ -450,8 +465,7 @@ bool MultiSettings::impl::exit ()
return reposition (); return reposition ();
} }
QMenu * MultiSettings::impl::create_sub_menu (QMainWindow * main_window, QMenu * MultiSettings::impl::create_sub_menu (QMenu * parent_menu,
QMenu * parent_menu,
QString const& menu_title, QString const& menu_title,
QActionGroup * action_group) QActionGroup * action_group)
{ {
@ -460,7 +474,7 @@ QMenu * MultiSettings::impl::create_sub_menu (QMainWindow * main_window,
sub_menu->menuAction ()->setCheckable (true); sub_menu->menuAction ()->setCheckable (true);
// populate sub-menu actions before showing // populate sub-menu actions before showing
connect (sub_menu, &QMenu::aboutToShow, [this, main_window, parent_menu, sub_menu] () { connect (sub_menu, &QMenu::aboutToShow, [this, parent_menu, sub_menu] () {
// depopulate before populating and showing because on Mac OS X // depopulate before populating and showing because on Mac OS X
// there is an issue with depopulating in QMenu::aboutToHide() // there is an issue with depopulating in QMenu::aboutToHide()
// with connections being disconnected before they are actioned // with connections being disconnected before they are actioned
@ -474,16 +488,16 @@ QMenu * MultiSettings::impl::create_sub_menu (QMainWindow * main_window,
{ {
auto select_action = new QAction {tr ("&Switch To"), this}; auto select_action = new QAction {tr ("&Switch To"), this};
sub_menu->addAction (select_action); sub_menu->addAction (select_action);
connect (select_action, &QAction::triggered, [this, main_window, sub_menu] (bool) { connect (select_action, &QAction::triggered, [this, sub_menu] (bool) {
select_configuration (main_window, sub_menu); select_configuration (sub_menu->title ());
}); });
sub_menu->addSeparator (); sub_menu->addSeparator ();
} }
auto clone_action = new QAction {tr ("&Clone"), this}; auto clone_action = new QAction {tr ("&Clone"), this};
sub_menu->addAction (clone_action); sub_menu->addAction (clone_action);
connect (clone_action, &QAction::triggered, [this, main_window, parent_menu, sub_menu] (bool) { connect (clone_action, &QAction::triggered, [this, parent_menu, sub_menu] (bool) {
clone_configuration (main_window, parent_menu, sub_menu); clone_configuration (parent_menu, sub_menu);
}); });
auto const& current_group = settings_.group (); auto const& current_group = settings_.group ();
@ -493,29 +507,29 @@ QMenu * MultiSettings::impl::create_sub_menu (QMainWindow * main_window,
{ {
auto clone_into_action = new QAction {tr ("Clone &Into ..."), this}; auto clone_into_action = new QAction {tr ("Clone &Into ..."), this};
sub_menu->addAction (clone_into_action); sub_menu->addAction (clone_into_action);
connect (clone_into_action, &QAction::triggered, [this, main_window, sub_menu] (bool) { connect (clone_into_action, &QAction::triggered, [this, sub_menu] (bool) {
clone_into_configuration (main_window, sub_menu); clone_into_configuration (sub_menu);
}); });
} }
if (current_group.size ()) settings_.beginGroup (current_group); if (current_group.size ()) settings_.beginGroup (current_group);
auto reset_action = new QAction {tr ("R&eset"), this}; auto reset_action = new QAction {tr ("R&eset"), this};
sub_menu->addAction (reset_action); sub_menu->addAction (reset_action);
connect (reset_action, &QAction::triggered, [this, main_window, sub_menu] (bool) { connect (reset_action, &QAction::triggered, [this, sub_menu] (bool) {
reset_configuration (main_window, sub_menu); reset_configuration (sub_menu);
}); });
auto rename_action = new QAction {tr ("&Rename ..."), this}; auto rename_action = new QAction {tr ("&Rename ..."), this};
sub_menu->addAction (rename_action); sub_menu->addAction (rename_action);
connect (rename_action, &QAction::triggered, [this, main_window, sub_menu] (bool) { connect (rename_action, &QAction::triggered, [this, sub_menu] (bool) {
rename_configuration (main_window, sub_menu); rename_configuration (sub_menu);
}); });
if (!is_current) if (!is_current)
{ {
auto delete_action = new QAction {tr ("&Delete"), this}; auto delete_action = new QAction {tr ("&Delete"), this};
sub_menu->addAction (delete_action); sub_menu->addAction (delete_action);
connect (delete_action, &QAction::triggered, [this, main_window, sub_menu] (bool) { connect (delete_action, &QAction::triggered, [this, sub_menu] (bool) {
delete_configuration (main_window, sub_menu); delete_configuration (sub_menu);
}); });
} }
}); });
@ -558,11 +572,9 @@ void MultiSettings::impl::load_from (Dictionary const& dictionary, bool add_plac
settings_.sync (); settings_.sync ();
} }
void MultiSettings::impl::select_configuration (QMainWindow * main_window, QMenu const * menu) void MultiSettings::impl::select_configuration (QString const& target_name)
{ {
auto const& target_name = menu->title (); if (main_window_ && target_name != current_)
if (target_name != current_)
{ {
{ {
auto const& current_group = settings_.group (); auto const& current_group = settings_.group ();
@ -577,13 +589,11 @@ void MultiSettings::impl::select_configuration (QMainWindow * main_window, QMenu
// and set up the restart // and set up the restart
current_ = target_name; current_ = target_name;
Q_EMIT parent_->configurationNameChanged (unescape_ampersands (current_)); Q_EMIT parent_->configurationNameChanged (unescape_ampersands (current_));
reposition_type_ = RepositionType::save_and_replace; restart (RepositionType::save_and_replace);
exit_flag_ = false;
main_window->close ();
} }
} }
void MultiSettings::impl::clone_configuration (QMainWindow * main_window, QMenu * parent_menu, QMenu const * menu) void MultiSettings::impl::clone_configuration (QMenu * parent_menu, QMenu const * menu)
{ {
auto const& current_group = settings_.group (); auto const& current_group = settings_.group ();
if (current_group.size ()) settings_.endGroup (); if (current_group.size ()) settings_.endGroup ();
@ -616,12 +626,15 @@ void MultiSettings::impl::clone_configuration (QMainWindow * main_window, QMenu
load_from (source_settings); load_from (source_settings);
// insert the new configuration sub menu in the parent menu // insert the new configuration sub menu in the parent menu
create_sub_menu (main_window, parent_menu, new_name, configurations_group_); create_sub_menu (parent_menu, new_name, configurations_group_);
if (current_group.size ()) settings_.beginGroup (current_group); if (current_group.size ()) settings_.beginGroup (current_group);
} }
void MultiSettings::impl::clone_into_configuration (QMainWindow * main_window, QMenu const * menu) void MultiSettings::impl::clone_into_configuration (QMenu const * menu)
{ {
Q_ASSERT (main_window_);
if (!main_window_) return;
auto const& current_group = settings_.group (); auto const& current_group = settings_.group ();
if (current_group.size ()) settings_.endGroup (); if (current_group.size ()) settings_.endGroup ();
auto const& target_name = menu->title (); auto const& target_name = menu->title ();
@ -642,15 +655,16 @@ void MultiSettings::impl::clone_into_configuration (QMainWindow * main_window, Q
} }
// pick a source configuration // 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 ()))
{ {
QString source_name {1 == sources.size () ? sources.at (0) : dialog.name ()}; QString source_name {1 == sources.size () ? sources.at (0) : dialog.name ()};
if (MessageBox::Yes == MessageBox::query_message (main_window, if (main_window_
tr ("Clone Into Configuration"), && MessageBox::Yes == MessageBox::query_message (main_window_,
tr ("Confirm overwrite of all values for configuration \"%1\" with values from \"%2\"?") tr ("Clone Into Configuration"),
.arg (unescape_ampersands (target_name)) tr ("Confirm overwrite of all values for configuration \"%1\" with values from \"%2\"?")
.arg (unescape_ampersands (source_name)))) .arg (unescape_ampersands (target_name))
.arg (unescape_ampersands (source_name))))
{ {
// grab the data to clone from // grab the data to clone from
if (source_name == current_group_name) if (source_name == current_group_name)
@ -669,9 +683,7 @@ void MultiSettings::impl::clone_into_configuration (QMainWindow * main_window, Q
if (target_name == current_) if (target_name == current_)
{ {
// restart with new settings // restart with new settings
reposition_type_ = RepositionType::replace; restart (RepositionType::replace);
exit_flag_ = false;
main_window->close ();
} }
else else
{ {
@ -686,14 +698,18 @@ void MultiSettings::impl::clone_into_configuration (QMainWindow * main_window, Q
if (current_group.size ()) settings_.beginGroup (current_group); if (current_group.size ()) settings_.beginGroup (current_group);
} }
void MultiSettings::impl::reset_configuration (QMainWindow * main_window, QMenu const * menu) void MultiSettings::impl::reset_configuration (QMenu const * menu)
{ {
Q_ASSERT (main_window_);
if (!main_window_) return;
auto const& target_name = menu->title (); auto const& target_name = menu->title ();
if (MessageBox::Yes != MessageBox::query_message (main_window, if (!main_window_
tr ("Reset Configuration"), || MessageBox::Yes != MessageBox::query_message (main_window_,
tr ("Confirm reset to default values for configuration \"%1\"?") tr ("Reset Configuration"),
.arg (unescape_ampersands (target_name)))) tr ("Confirm reset to default values for configuration \"%1\"?")
.arg (unescape_ampersands (target_name))))
{ {
return; return;
} }
@ -701,10 +717,8 @@ void MultiSettings::impl::reset_configuration (QMainWindow * main_window, QMenu
if (target_name == current_) if (target_name == current_)
{ {
// restart with default settings // restart with default settings
reposition_type_ = RepositionType::replace;
new_settings_.clear (); new_settings_.clear ();
exit_flag_ = false; restart (RepositionType::replace);
main_window->close ();
} }
else else
{ {
@ -721,8 +735,11 @@ void MultiSettings::impl::reset_configuration (QMainWindow * main_window, QMenu
} }
} }
void MultiSettings::impl::rename_configuration (QMainWindow * main_window, QMenu * menu) void MultiSettings::impl::rename_configuration (QMenu * menu)
{ {
Q_ASSERT (main_window_);
if (!main_window_) return;
auto const& current_group = settings_.group (); auto const& current_group = settings_.group ();
if (current_group.size ()) settings_.endGroup (); if (current_group.size ()) settings_.endGroup ();
auto const& target_name = menu->title (); auto const& target_name = menu->title ();
@ -733,7 +750,7 @@ void MultiSettings::impl::rename_configuration (QMainWindow * main_window, QMenu
invalid_names << settings_.value (multi_settings_current_name_key).toString (); invalid_names << settings_.value (multi_settings_current_name_key).toString ();
// get the new name // get the new name
NameDialog dialog {target_name, invalid_names, main_window}; NameDialog dialog {target_name, invalid_names, main_window_};
if (QDialog::Accepted == dialog.exec ()) if (QDialog::Accepted == dialog.exec ())
{ {
if (target_name == current_) if (target_name == current_)
@ -764,8 +781,9 @@ void MultiSettings::impl::rename_configuration (QMainWindow * main_window, QMenu
if (current_group.size ()) settings_.beginGroup (current_group); if (current_group.size ()) settings_.beginGroup (current_group);
} }
void MultiSettings::impl::delete_configuration (QMainWindow * main_window, QMenu * menu) void MultiSettings::impl::delete_configuration (QMenu * menu)
{ {
Q_ASSERT (main_window_);
auto const& target_name = menu->title (); auto const& target_name = menu->title ();
if (target_name == current_) if (target_name == current_)
@ -774,10 +792,11 @@ void MultiSettings::impl::delete_configuration (QMainWindow * main_window, QMenu
} }
else else
{ {
if (MessageBox::Yes != MessageBox::query_message (main_window, if (!main_window_
tr ("Delete Configuration"), || MessageBox::Yes != MessageBox::query_message (main_window_,
tr ("Confirm deletion of configuration \"%1\"?") tr ("Delete Configuration"),
.arg (unescape_ampersands (target_name)))) tr ("Confirm deletion of configuration \"%1\"?")
.arg (unescape_ampersands (target_name))))
{ {
return; return;
} }
@ -793,3 +812,12 @@ void MultiSettings::impl::delete_configuration (QMainWindow * main_window, QMenu
// update the menu // update the menu
menu->deleteLater (); menu->deleteLater ();
} }
void MultiSettings::impl::restart (RepositionType type)
{
Q_ASSERT (main_window_);
reposition_type_ = type;
exit_flag_ = false;
main_window_->close ();
main_window_ = nullptr;
}

View File

@ -80,6 +80,10 @@ public:
// action is triggered. // action is triggered.
void create_menu_actions (QMainWindow *, QMenu *); void create_menu_actions (QMainWindow *, QMenu *);
// switch to this configuration if it exists
Q_SLOT void select_configuration (QString const& name);
QString configuration_name () const;
// Access to the QSettings object instance. // Access to the QSettings object instance.
QSettings * settings (); QSettings * settings ();

View File

@ -124,7 +124,8 @@
* Tx Watchdog bool * Tx Watchdog bool
* Sub-mode utf8 * Sub-mode utf8
* Fast mode bool * Fast mode bool
* Special operation mode quint8 * Special Operation Mode quint8
* Configuration Name utf8
* *
* WSJT-X sends this status message when various internal state * WSJT-X sends this status message when various internal state
* changes to allow the server to track the relevant state of each * changes to allow the server to track the relevant state of each
@ -145,7 +146,8 @@
* When the Tx DF changes, * When the Tx DF changes,
* When settings are exited, * When settings are exited,
* When the DX call or grid changes, * When the DX call or grid changes,
* When the Tx watchdog is set or reset. * When the Tx watchdog is set or reset,
* When the configuration name changes.
* *
* The Special operation mode is an enumeration that indicates the * The Special operation mode is an enumeration that indicates the
* setting selected in the WSJT-X "Settings->Advanced->Special * setting selected in the WSJT-X "Settings->Advanced->Special
@ -414,6 +416,15 @@
* the last instance only instead of all instances of the * the last instance only instead of all instances of the
* specified call be highlighted or have it's highlighting * specified call be highlighted or have it's highlighting
* cleared. * cleared.
*
*
* Switch Configuration In 14 quint32
* Id (unique key) utf8
* Configuration Name utf8
*
* The server may send this message at any time. The message
* specifies the name of the configuration to switch to. The new
* configuration must exist.
*/ */
#include <QDataStream> #include <QDataStream>
@ -443,6 +454,7 @@ namespace NetworkMessage
Location, Location,
LoggedADIF, LoggedADIF,
HighlightCallsign, HighlightCallsign,
SwitchConfiguration,
maximum_message_type_ // ONLY add new message types maximum_message_type_ // ONLY add new message types
// immediately before here // immediately before here
}; };

View File

@ -139,6 +139,7 @@ ClientWidget::ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemMod
, rx_df_label_ {new QLabel} , rx_df_label_ {new QLabel}
, tx_df_label_ {new QLabel} , tx_df_label_ {new QLabel}
, report_label_ {new QLabel} , report_label_ {new QLabel}
, configuration_line_edit_ {new QLineEdit}
, columns_resized_ {false} , columns_resized_ {false}
{ {
// set up widgets // set up widgets
@ -155,6 +156,7 @@ ClientWidget::ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemMod
auto form_layout = new QFormLayout; auto form_layout = new QFormLayout;
form_layout->addRow (tr ("Free text:"), message_line_edit_); form_layout->addRow (tr ("Free text:"), message_line_edit_);
form_layout->addRow (tr ("Temporary grid:"), grid_line_edit_); form_layout->addRow (tr ("Temporary grid:"), grid_line_edit_);
form_layout->addRow (tr ("Configuration name:"), configuration_line_edit_);
message_line_edit_->setValidator (new QRegExpValidator {message_alphabet, this}); message_line_edit_->setValidator (new QRegExpValidator {message_alphabet, this});
grid_line_edit_->setValidator (new MaidenheadLocatorValidator {this}); grid_line_edit_->setValidator (new MaidenheadLocatorValidator {this});
connect (message_line_edit_, &QLineEdit::textEdited, [this] (QString const& text) { connect (message_line_edit_, &QLineEdit::textEdited, [this] (QString const& text) {
@ -166,6 +168,9 @@ ClientWidget::ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemMod
connect (grid_line_edit_, &QLineEdit::editingFinished, [this] () { connect (grid_line_edit_, &QLineEdit::editingFinished, [this] () {
Q_EMIT location (id_, grid_line_edit_->text ()); Q_EMIT location (id_, grid_line_edit_->text ());
}); });
connect (configuration_line_edit_, &QLineEdit::editingFinished, [this] () {
Q_EMIT switch_configuration (id_, configuration_line_edit_->text ());
});
auto decodes_page = new QWidget; auto decodes_page = new QWidget;
auto decodes_layout = new QVBoxLayout {decodes_page}; auto decodes_layout = new QVBoxLayout {decodes_page};
@ -266,7 +271,7 @@ void ClientWidget::update_status (QString const& id, Frequency f, QString const&
, bool transmitting, bool decoding, qint32 rx_df, qint32 tx_df , bool transmitting, bool decoding, qint32 rx_df, qint32 tx_df
, QString const& de_call, QString const& de_grid, QString const& dx_grid , QString const& de_call, QString const& de_grid, QString const& dx_grid
, bool watchdog_timeout, QString const& sub_mode, bool fast_mode , bool watchdog_timeout, QString const& sub_mode, bool fast_mode
, quint8 special_op_mode) , quint8 special_op_mode, QString const& configuration_name)
{ {
if (id == id_) if (id == id_)
{ {
@ -304,6 +309,10 @@ void ClientWidget::update_status (QString const& id, Frequency f, QString const&
halt_tx_button_->setEnabled (transmitting); halt_tx_button_->setEnabled (transmitting);
update_dynamic_property (mode_label_, "decoding", decoding); update_dynamic_property (mode_label_, "decoding", decoding);
update_dynamic_property (tx_df_label_, "watchdog_timeout", watchdog_timeout); update_dynamic_property (tx_df_label_, "watchdog_timeout", watchdog_timeout);
if (!configuration_line_edit_->hasFocus ())
{
configuration_line_edit_->setText (configuration_name);
}
} }
} }

View File

@ -34,7 +34,7 @@ public:
, bool transmitting, bool decoding, qint32 rx_df, qint32 tx_df , bool transmitting, bool decoding, qint32 rx_df, qint32 tx_df
, QString const& de_call, QString const& de_grid, QString const& dx_grid , QString const& de_call, QString const& de_grid, QString const& dx_grid
, bool watchdog_timeout, QString const& sub_mode, bool fast_mode , bool watchdog_timeout, QString const& sub_mode, bool fast_mode
, quint8 special_op_mode); , quint8 special_op_mode, QString const& configuration_name);
Q_SLOT void decode_added (bool is_new, QString const& client_id, QTime, qint32 snr Q_SLOT void decode_added (bool is_new, QString const& client_id, QTime, qint32 snr
, float delta_time, quint32 delta_frequency, QString const& mode , float delta_time, quint32 delta_frequency, QString const& mode
, QString const& message, bool low_confidence, bool off_air); , QString const& message, bool low_confidence, bool off_air);
@ -52,8 +52,10 @@ public:
Q_SIGNAL void highlight_callsign (QString const& id, QString const& call Q_SIGNAL void highlight_callsign (QString const& id, QString const& call
, QColor const& bg = QColor {}, QColor const& fg = QColor {} , QColor const& bg = QColor {}, QColor const& fg = QColor {}
, bool last_only = false); , bool last_only = false);
Q_SIGNAL void switch_configuration (QString const& id, QString const& configuration_name);
private: private:
QString id_; QString id_;
QListWidget const * calls_of_interest_; QListWidget const * calls_of_interest_;
class IdFilterModel final class IdFilterModel final
@ -94,6 +96,7 @@ private:
QLabel * rx_df_label_; QLabel * rx_df_label_;
QLabel * tx_df_label_; QLabel * tx_df_label_;
QLabel * report_label_; QLabel * report_label_;
QLineEdit * configuration_line_edit_;
bool columns_resized_; bool columns_resized_;
}; };

View File

@ -256,6 +256,7 @@ void MessageAggregatorMainWindow::add_client (QString const& id, QString const&
connect (dock, &ClientWidget::location, server_, &MessageServer::location); connect (dock, &ClientWidget::location, server_, &MessageServer::location);
connect (view_action, &QAction::toggled, dock, &ClientWidget::setVisible); connect (view_action, &QAction::toggled, dock, &ClientWidget::setVisible);
connect (dock, &ClientWidget::highlight_callsign, server_, &MessageServer::highlight_callsign); connect (dock, &ClientWidget::highlight_callsign, server_, &MessageServer::highlight_callsign);
connect (dock, &ClientWidget::switch_configuration, server_, &MessageServer::switch_configuration);
dock_widgets_[id] = dock; dock_widgets_[id] = dock;
server_->replay (id); // request decodes and status server_->replay (id); // request decodes and status
} }

View File

@ -51,7 +51,7 @@ public:
, bool /*transmitting*/, bool /*decoding*/, qint32 /*rx_df*/, qint32 /*tx_df*/ , bool /*transmitting*/, bool /*decoding*/, qint32 /*rx_df*/, qint32 /*tx_df*/
, QString const& /*de_call*/, QString const& /*de_grid*/, QString const& /*dx_grid*/ , QString const& /*de_call*/, QString const& /*de_grid*/, QString const& /*dx_grid*/
, bool /* watchdog_timeout */, QString const& sub_mode, bool /*fast_mode*/ , bool /* watchdog_timeout */, QString const& sub_mode, bool /*fast_mode*/
, quint8 /*special_op_mode*/) , quint8 /*special_op_mode*/, QString const& /*configuration_name*/)
{ {
if (id == id_) if (id == id_)
{ {

View File

@ -560,6 +560,8 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
connect (m_messageClient, &MessageClient::highlight_callsign, ui->decodedTextBrowser, &DisplayText::highlight_callsign); connect (m_messageClient, &MessageClient::highlight_callsign, ui->decodedTextBrowser, &DisplayText::highlight_callsign);
connect (m_messageClient, &MessageClient::switch_configuration, m_multi_settings, &MultiSettings::select_configuration);
// Hook up WSPR band hopping // Hook up WSPR band hopping
connect (ui->band_hopping_schedule_push_button, &QPushButton::clicked connect (ui->band_hopping_schedule_push_button, &QPushButton::clicked
, &m_WSPR_band_hopping, &WSPRBandHopping::show_dialog); , &m_WSPR_band_hopping, &WSPRBandHopping::show_dialog);
@ -719,6 +721,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
else { else {
config_label.hide (); config_label.hide ();
} }
statusUpdate ();
}); });
m_multi_settings->create_menu_actions (this, ui->menuConfig); m_multi_settings->create_menu_actions (this, ui->menuConfig);
m_configurations_button = m_rigErrorMessageBox.addButton (tr ("Configurations...") m_configurations_button = m_rigErrorMessageBox.addButton (tr ("Configurations...")
@ -7908,7 +7911,8 @@ void MainWindow::statusUpdate () const
m_config.my_callsign (), m_config.my_grid (), m_config.my_callsign (), m_config.my_grid (),
m_hisGrid, m_tx_watchdog, m_hisGrid, m_tx_watchdog,
submode != QChar::Null ? QString {submode} : QString {}, m_bFastMode, submode != QChar::Null ? QString {submode} : QString {}, m_bFastMode,
static_cast<quint8> (m_config.special_op_id ())); static_cast<quint8> (m_config.special_op_id ()),
m_multi_settings->configuration_name ());
} }
void MainWindow::childEvent (QChildEvent * e) void MainWindow::childEvent (QChildEvent * e)