1 #include "Configuration.hpp"
2 
3 //
4 // Read me!
5 //
6 // This file defines a configuration dialog with the user. The general
7 // strategy is to expose agreed  configuration parameters via a custom
8 // interface (See  Configuration.hpp). The state exposed  through this
9 // public   interface  reflects   stored  or   derived  data   in  the
10 // Configuration::impl object.   The Configuration::impl  structure is
11 // an implementation of the PIMPL (a.k.a.  Cheshire Cat or compilation
12 // firewall) implementation hiding idiom that allows internal state to
13 // be completely removed from the public interface.
14 //
15 // There  is a  secondary level  of parameter  storage which  reflects
16 // current settings UI  state, these parameters are not  copied to the
17 // state   store  that   the  public   interface  exposes   until  the
18 // Configuration:impl::accept() operation is  successful. The accept()
19 // operation is  tied to the settings  OK button. The normal  and most
20 // convenient place to store this intermediate settings UI state is in
21 // the data models of the UI  controls, if that is not convenient then
22 // separate member variables  must be used to store that  state. It is
23 // important for the user experience that no publicly visible settings
24 // are changed  while the  settings UI are  changed i.e.  all settings
25 // changes   must    be   deferred   until   the    "OK"   button   is
26 // clicked. Conversely, all changes must  be discarded if the settings
27 // UI "Cancel" button is clicked.
28 //
29 // There is  a complication related  to the radio interface  since the
30 // this module offers  the facility to test the  radio interface. This
31 // test means  that the  public visibility to  the radio  being tested
32 // must be  changed.  To  maintain the  illusion of  deferring changes
33 // until they  are accepted, the  original radio related  settings are
34 // stored upon showing  the UI and restored if the  UI is dismissed by
35 // canceling.
36 //
37 // It  should be  noted that  the  settings UI  lives as  long as  the
38 // application client that uses it does. It is simply shown and hidden
39 // as it  is needed rather than  creating it on demand.  This strategy
40 // saves a  lot of  expensive UI  drawing at the  expense of  a little
41 // storage and  gives a  convenient place  to deliver  settings values
42 // from.
43 //
44 // Here is an overview of the high level flow of this module:
45 //
46 // 1)  On  construction the  initial  environment  is initialized  and
47 // initial   values  for   settings  are   read  from   the  QSettings
48 // database. At  this point  default values for  any new  settings are
49 // established by  providing a  default value  to the  QSettings value
50 // queries. This should be the only place where a hard coded value for
51 // a   settings  item   is   defined.   Any   remaining  one-time   UI
52 // initialization is also done. At the end of the constructor a method
53 // initialize_models()  is called  to  load the  UI  with the  current
54 // settings values.
55 //
56 // 2) When the settings UI is displayed by a client calling the exec()
57 // operation, only temporary state need be stored as the UI state will
58 // already mirror the publicly visible settings state.
59 //
60 // 3) As  the user makes  changes to  the settings UI  only validation
61 // need be  carried out since the  UI control data models  are used as
62 // the temporary store of unconfirmed settings.  As some settings will
63 // depend  on each  other a  validate() operation  is available,  this
64 // operation implements a check of any complex multi-field values.
65 //
66 // 4) If the  user discards the settings changes by  dismissing the UI
67 // with the  "Cancel" button;  the reject()  operation is  called. The
68 // reject() operation calls initialize_models()  which will revert all
69 // the  UI visible  state  to  the values  as  at  the initial  exec()
70 // operation.  No   changes  are  moved   into  the  data   fields  in
71 // Configuration::impl that  reflect the  settings state  published by
72 // the public interface (Configuration.hpp).
73 //
74 // 5) If  the user accepts the  settings changes by dismissing  the UI
75 // with the "OK" button; the  accept() operation is called which calls
76 // the validate() operation  again and, if it passes,  the fields that
77 // are used  to deliver  the settings  state are  updated from  the UI
78 // control models  or other temporary  state variables. At the  end of
79 // the accept()  operation, just  before hiding  the UI  and returning
80 // control to the caller; the new  settings values are stored into the
81 // settings database by a call to the write_settings() operation, thus
82 // ensuring that  settings changes are  saved even if  the application
83 // crashes or is subsequently killed.
84 //
85 // 6)  On  destruction,  which   only  happens  when  the  application
86 // terminates,  the settings  are saved  to the  settings database  by
87 // calling the  write_settings() operation. This is  largely redundant
88 // but is still done to save the default values of any new settings on
89 // an initial run.
90 //
91 // To add a new setting:
92 //
93 // 1) Update the UI with the new widget to view and change the value.
94 //
95 // 2)  Add  a member  to  Configuration::impl  to store  the  accepted
96 // setting state. If the setting state is dynamic; add a new signal to
97 // broadcast the setting value.
98 //
99 // 3) Add a  query method to the  public interface (Configuration.hpp)
100 // to access the  new setting value. If the settings  is dynamic; this
101 // step  is optional  since  value  changes will  be  broadcast via  a
102 // signal.
103 //
104 // 4) Add a forwarding operation to implement the new query (3) above.
105 //
106 // 5)  Add a  settings read  call to  read_settings() with  a sensible
107 // default value. If  the setting value is dynamic, add  a signal emit
108 // call to broadcast the setting value change.
109 //
110 // 6) Add  code to  initialize_models() to  load the  widget control's
111 // data model with the current value.
112 //
113 // 7) If there is no convenient data model field, add a data member to
114 // store the proposed new value. Ensure  this member has a valid value
115 // on exit from read_settings().
116 //
117 // 8)  Add  any  required  inter-field validation  to  the  validate()
118 // operation.
119 //
120 // 9) Add code to the accept()  operation to extract the setting value
121 // from  the  widget   control  data  model  and  load   it  into  the
122 // Configuration::impl  member  that  reflects  the  publicly  visible
123 // setting state. If  the setting value is dynamic; add  a signal emit
124 // call to broadcast any changed state of the setting.
125 //
126 // 10) Add  a settings  write call  to save the  setting value  to the
127 // settings database.
128 //
129 
130 #include <stdexcept>
131 #include <iterator>
132 #include <algorithm>
133 #include <functional>
134 #include <limits>
135 #include <cmath>
136 
137 #include <QApplication>
138 #include <QCursor>
139 #include <QMetaType>
140 #include <QList>
141 #include <QPair>
142 #include <QVariant>
143 #include <QSettings>
144 #include <QAudioDeviceInfo>
145 #include <QAudioInput>
146 #include <QDialog>
147 #include <QAction>
148 #include <QFileDialog>
149 #include <QDir>
150 #include <QTemporaryFile>
151 #include <QFormLayout>
152 #include <QString>
153 #include <QStringList>
154 #include <QStringListModel>
155 #include <QLineEdit>
156 #include <QRegularExpression>
157 #include <QRegularExpressionValidator>
158 #include <QIntValidator>
159 #include <QThread>
160 #include <QTimer>
161 #include <QStandardPaths>
162 #include <QFont>
163 #include <QFontDialog>
164 #include <QSerialPortInfo>
165 #include <QScopedPointer>
166 #include <QNetworkInterface>
167 #include <QHostInfo>
168 #include <QHostAddress>
169 #include <QStandardItem>
170 #include <QDebug>
171 
172 #include "pimpl_impl.hpp"
173 #include "Logger.hpp"
174 #include "qt_helpers.hpp"
175 #include "MetaDataRegistry.hpp"
176 #include "SettingsGroup.hpp"
177 #include "widgets/FrequencyLineEdit.hpp"
178 #include "widgets/FrequencyDeltaLineEdit.hpp"
179 #include "item_delegates/CandidateKeyFilter.hpp"
180 #include "item_delegates/ForeignKeyDelegate.hpp"
181 #include "item_delegates/FrequencyDelegate.hpp"
182 #include "item_delegates/FrequencyDeltaDelegate.hpp"
183 #include "Transceiver/TransceiverFactory.hpp"
184 #include "Transceiver/Transceiver.hpp"
185 #include "models/Bands.hpp"
186 #include "models/IARURegions.hpp"
187 #include "models/Modes.hpp"
188 #include "models/FrequencyList.hpp"
189 #include "models/StationList.hpp"
190 #include "Network/NetworkServerLookup.hpp"
191 #include "widgets/MessageBox.hpp"
192 #include "validators/MaidenheadLocatorValidator.hpp"
193 #include "validators/CallsignValidator.hpp"
194 #include "Network/LotWUsers.hpp"
195 #include "models/DecodeHighlightingModel.hpp"
196 #include "logbook/logbook.h"
197 #include "widgets/LazyFillComboBox.hpp"
198 
199 #include "ui_Configuration.h"
200 #include "moc_Configuration.cpp"
201 
202 namespace
203 {
204   // these undocumented flag values when stored in (Qt::UserRole - 1)
205   // of a ComboBox item model index allow the item to be enabled or
206   // disabled
207   int const combo_box_item_enabled (32 | 1);
208   int const combo_box_item_disabled (0);
209 
210 //  QRegExp message_alphabet {"[- A-Za-z0-9+./?]*"};
211   QRegularExpression message_alphabet {"[- @A-Za-z0-9+./?#<>]*"};
212   QRegularExpression RTTY_roundup_exchange_re {
213     R"(
214         (
215            AL|AZ|AR|CA|CO|CT|DE|FL|GA      # 48 contiguous states
216           |ID|IL|IN|IA|KS|KY|LA|ME|MD
217           |MA|MI|MN|MS|MO|MT|NE|NV|NH|NJ
218           |NM|NY|NC|ND|OH|OK|OR|PA|RI|SC
219           |SD|TN|TX|UT|VT|VA|WA|WV|WI|WY
220           |NB|NS|QC|ON|MB|SK|AB|BC|NWT|NF  # VE provinces
221           |LB|NU|YT|PEI
222           |DC                              # District of Columbia
223           |DX                              # anyone else
224           |SCC                             # Slovenia Contest Club contest
225         )
226       )", QRegularExpression::CaseInsensitiveOption | QRegularExpression::ExtendedPatternSyntaxOption};
227 
228   QRegularExpression field_day_exchange_re {
229     R"(
230         (
231            [1-9]                          # # transmitters (1 to 32 inc.)
232           |[0-2]\d
233           |3[0-2]
234         )
235         [A-F]\ *                          # class and optional space
236         (
237            AB|AK|AL|AR|AZ|BC|CO|CT|DE|EB  # ARRL/RAC section
238           |EMA|ENY|EPA|EWA|GA|GTA|IA|ID
239           |IL|IN|KS|KY|LA|LAX|MAR|MB|MDC
240           |ME|MI|MN|MO|MS|MT|NC|ND|NE|NFL
241           |NH|NL|NLI|NM|NNJ|NNY|NT|NTX|NV
242           |OH|OK|ONE|ONN|ONS|OR|ORG|PAC|PE
243           |PR|QC|RI|SB|SC|SCV|SD|SDG|SF
244           |SFL|SJV|SK|SNJ|STX|SV|TN|UT|VA
245           |VI|VT|WCF|WI|WMA|WNY|WPA|WTX
246           |WV|WWA|WY
247           |DX                             # anyone else
248         )
249       )", QRegularExpression::CaseInsensitiveOption | QRegularExpression::ExtendedPatternSyntaxOption};
250 
251   // Magic numbers for file validation
252   constexpr quint32 qrg_magic {0xadbccbdb};
253   constexpr quint32 qrg_version {100}; // M.mm
254 }
255 
256 
257 //
258 // Dialog to get a new Frequency item
259 //
260 class FrequencyDialog final
261   : public QDialog
262 {
263   Q_OBJECT
264 
265 public:
266   using Item = FrequencyList_v2::Item;
267 
FrequencyDialog(IARURegions * regions_model,Modes * modes_model,QWidget * parent=nullptr)268   explicit FrequencyDialog (IARURegions * regions_model, Modes * modes_model, QWidget * parent = nullptr)
269     : QDialog {parent}
270   {
271     setWindowTitle (QApplication::applicationName () + " - " +
272                     tr ("Add Frequency"));
273     region_combo_box_.setModel (regions_model);
274     mode_combo_box_.setModel (modes_model);
275 
276     auto form_layout = new QFormLayout ();
277     form_layout->addRow (tr ("IARU &Region:"), &region_combo_box_);
278     form_layout->addRow (tr ("&Mode:"), &mode_combo_box_);
279     form_layout->addRow (tr ("&Frequency (MHz):"), &frequency_line_edit_);
280 
281     auto main_layout = new QVBoxLayout (this);
282     main_layout->addLayout (form_layout);
283 
284     auto button_box = new QDialogButtonBox {QDialogButtonBox::Ok | QDialogButtonBox::Cancel};
285     main_layout->addWidget (button_box);
286 
287     connect (button_box, &QDialogButtonBox::accepted, this, &FrequencyDialog::accept);
288     connect (button_box, &QDialogButtonBox::rejected, this, &FrequencyDialog::reject);
289   }
290 
item() const291   Item item () const
292   {
293     return {frequency_line_edit_.frequency ()
294         , Modes::value (mode_combo_box_.currentText ())
295         , IARURegions::value (region_combo_box_.currentText ())};
296   }
297 
298 private:
299   QComboBox region_combo_box_;
300   QComboBox mode_combo_box_;
301   FrequencyLineEdit frequency_line_edit_;
302 };
303 
304 
305 //
306 // Dialog to get a new Station item
307 //
308 class StationDialog final
309   : public QDialog
310 {
311   Q_OBJECT
312 
313 public:
StationDialog(StationList const * stations,Bands * bands,QWidget * parent=nullptr)314   explicit StationDialog (StationList const * stations, Bands * bands, QWidget * parent = nullptr)
315     : QDialog {parent}
316     , filtered_bands_ {new CandidateKeyFilter {bands, stations, 0, 0}}
317   {
318     setWindowTitle (QApplication::applicationName () + " - " + tr ("Add Station"));
319 
320     band_.setModel (filtered_bands_.data ());
321 
322     auto form_layout = new QFormLayout ();
323     form_layout->addRow (tr ("&Band:"), &band_);
324     form_layout->addRow (tr ("&Offset (MHz):"), &delta_);
325     form_layout->addRow (tr ("&Antenna:"), &description_);
326 
327     auto main_layout = new QVBoxLayout (this);
328     main_layout->addLayout (form_layout);
329 
330     auto button_box = new QDialogButtonBox {QDialogButtonBox::Ok | QDialogButtonBox::Cancel};
331     main_layout->addWidget (button_box);
332 
333     connect (button_box, &QDialogButtonBox::accepted, this, &StationDialog::accept);
334     connect (button_box, &QDialogButtonBox::rejected, this, &StationDialog::reject);
335 
336     if (delta_.text ().isEmpty ())
337       {
338         delta_.setText ("0");
339       }
340   }
341 
station() const342   StationList::Station station () const
343   {
344     return {band_.currentText (), delta_.frequency_delta (), description_.text ()};
345   }
346 
exec()347   int exec () override
348   {
349     filtered_bands_->set_active_key ();
350     return QDialog::exec ();
351   }
352 
353 private:
354   QScopedPointer<CandidateKeyFilter> filtered_bands_;
355 
356   QComboBox band_;
357   FrequencyDeltaLineEdit delta_;
358   QLineEdit description_;
359 };
360 
361 class RearrangableMacrosModel
362   : public QStringListModel
363 {
364 public:
flags(QModelIndex const & index) const365   Qt::ItemFlags flags (QModelIndex const& index) const override
366   {
367     auto flags = QStringListModel::flags (index);
368     if (index.isValid ())
369       {
370         // disallow drop onto existing items
371         flags &= ~Qt::ItemIsDropEnabled;
372       }
373     return flags;
374   }
375 };
376 
377 
378 //
379 // Class MessageItemDelegate
380 //
381 //	Item delegate for message entry such as free text message macros.
382 //
383 class MessageItemDelegate final
384   : public QStyledItemDelegate
385 {
386 public:
MessageItemDelegate(QObject * parent=nullptr)387   explicit MessageItemDelegate (QObject * parent = nullptr)
388     : QStyledItemDelegate {parent}
389   {
390   }
391 
createEditor(QWidget * parent,QStyleOptionViewItem const &,QModelIndex const &) const392   QWidget * createEditor (QWidget * parent
393                           , QStyleOptionViewItem const& /* option*/
394                           , QModelIndex const& /* index */
395                           ) const override
396   {
397     auto editor = new QLineEdit {parent};
398     editor->setFrame (false);
399     editor->setValidator (new QRegularExpressionValidator {message_alphabet, editor});
400     return editor;
401   }
402 };
403 
404 // Internal implementation of the Configuration class.
405 class Configuration::impl final
406   : public QDialog
407 {
408   Q_OBJECT;
409 
410 public:
411   using FrequencyDelta = Radio::FrequencyDelta;
412   using port_type = Configuration::port_type;
413   using audio_info_type = QPair<QAudioDeviceInfo, QList<QVariant> >;
414 
415   explicit impl (Configuration * self
416                  , QNetworkAccessManager * network_manager
417                  , QDir const& temp_directory
418                  , QSettings * settings
419                  , LogBook * logbook
420                  , QWidget * parent);
421   ~impl ();
422 
423   bool have_rig ();
424 
425   void transceiver_frequency (Frequency);
426   void transceiver_tx_frequency (Frequency);
427   void transceiver_mode (MODE);
428   void transceiver_ptt (bool);
429   void sync_transceiver (bool force_signal);
430 
431   Q_SLOT int exec () override;
432   Q_SLOT void accept () override;
433   Q_SLOT void reject () override;
434   Q_SLOT void done (int) override;
435 
436 private:
437   typedef QList<QAudioDeviceInfo> AudioDevices;
438 
439   void read_settings ();
440   void write_settings ();
441 
442   void find_audio_devices ();
443   QAudioDeviceInfo find_audio_device (QAudio::Mode, QComboBox *, QString const& device_name);
444   void load_audio_devices (QAudio::Mode, QComboBox *, QAudioDeviceInfo *);
445   void update_audio_channels (QComboBox const *, int, QComboBox *, bool);
446 
447   void load_network_interfaces (CheckableItemComboBox *, QStringList current);
448   Q_SLOT void validate_network_interfaces (QString const&);
449   QStringList get_selected_network_interfaces (CheckableItemComboBox *);
450   Q_SLOT void host_info_results (QHostInfo);
451   void check_multicast (QHostAddress const&);
452 
453   void find_tab (QWidget *);
454 
455   void initialize_models ();
split_mode() const456   bool split_mode () const
457   {
458     return
459       (WSJT_RIG_NONE_CAN_SPLIT || !rig_is_dummy_) &&
460       (rig_params_.split_mode != TransceiverFactory::split_mode_none);
461   }
462   void set_cached_mode ();
463   bool open_rig (bool force = false);
464   //bool set_mode ();
465   void close_rig ();
466   TransceiverFactory::ParameterPack gather_rig_data ();
467   void enumerate_rigs ();
468   void set_rig_invariants ();
469   bool validate ();
470   void fill_port_combo_box (QComboBox *);
471   Frequency apply_calibration (Frequency) const;
472   Frequency remove_calibration (Frequency) const;
473 
474   void delete_frequencies ();
475   void load_frequencies ();
476   void merge_frequencies ();
477   void save_frequencies ();
478   void reset_frequencies ();
479   void insert_frequency ();
480   FrequencyList_v2::FrequencyItems read_frequencies_file (QString const&);
481 
482   void delete_stations ();
483   void insert_station ();
484 
485   Q_SLOT void on_font_push_button_clicked ();
486   Q_SLOT void on_decoded_text_font_push_button_clicked ();
487   Q_SLOT void on_PTT_port_combo_box_activated (int);
488   Q_SLOT void on_CAT_port_combo_box_activated (int);
489   Q_SLOT void on_CAT_serial_baud_combo_box_currentIndexChanged (int);
490   Q_SLOT void on_CAT_data_bits_button_group_buttonClicked (int);
491   Q_SLOT void on_CAT_stop_bits_button_group_buttonClicked (int);
492   Q_SLOT void on_CAT_handshake_button_group_buttonClicked (int);
493   Q_SLOT void on_CAT_poll_interval_spin_box_valueChanged (int);
494   Q_SLOT void on_split_mode_button_group_buttonClicked (int);
495   Q_SLOT void on_test_CAT_push_button_clicked ();
496   Q_SLOT void on_test_PTT_push_button_clicked (bool checked);
497   Q_SLOT void on_force_DTR_combo_box_currentIndexChanged (int);
498   Q_SLOT void on_force_RTS_combo_box_currentIndexChanged (int);
499   Q_SLOT void on_rig_combo_box_currentIndexChanged (int);
500   Q_SLOT void on_add_macro_push_button_clicked (bool = false);
501   Q_SLOT void on_delete_macro_push_button_clicked (bool = false);
502   Q_SLOT void on_PTT_method_button_group_buttonClicked (int);
503   Q_SLOT void on_add_macro_line_edit_editingFinished ();
504   Q_SLOT void delete_macro ();
505   void delete_selected_macros (QModelIndexList);
506   Q_SLOT void on_udp_server_line_edit_textChanged (QString const&);
507   Q_SLOT void on_udp_server_line_edit_editingFinished ();
508   Q_SLOT void on_save_path_select_push_button_clicked (bool);
509   Q_SLOT void on_azel_path_select_push_button_clicked (bool);
510   Q_SLOT void on_calibration_intercept_spin_box_valueChanged (double);
511   Q_SLOT void on_calibration_slope_ppm_spin_box_valueChanged (double);
512   Q_SLOT void handle_transceiver_update (TransceiverState const&, unsigned sequence_number);
513   Q_SLOT void handle_transceiver_failure (QString const& reason);
514   Q_SLOT void on_reset_highlighting_to_defaults_push_button_clicked (bool);
515   Q_SLOT void on_rescan_log_push_button_clicked (bool);
516   Q_SLOT void on_LotW_CSV_fetch_push_button_clicked (bool);
517   Q_SLOT void on_cbx2ToneSpacing_clicked(bool);
518   Q_SLOT void on_cbx4ToneSpacing_clicked(bool);
519   Q_SLOT void on_prompt_to_log_check_box_clicked(bool);
520   Q_SLOT void on_cbAutoLog_clicked(bool);
521   Q_SLOT void on_Field_Day_Exchange_textEdited (QString const&);
522   Q_SLOT void on_RTTY_Exchange_textEdited (QString const&);
523 
524   // typenames used as arguments must match registered type names :(
525   Q_SIGNAL void start_transceiver (unsigned seqeunce_number) const;
526   Q_SIGNAL void set_transceiver (Transceiver::TransceiverState const&,
527                                  unsigned sequence_number) const;
528   Q_SIGNAL void stop_transceiver () const;
529 
530   Configuration * const self_;	// back pointer to public interface
531 
532   QThread * transceiver_thread_;
533   TransceiverFactory transceiver_factory_;
534   QList<QMetaObject::Connection> rig_connections_;
535 
536   QScopedPointer<Ui::configuration_dialog> ui_;
537 
538   QNetworkAccessManager * network_manager_;
539   QSettings * settings_;
540   LogBook * logbook_;
541 
542   QDir doc_dir_;
543   QDir data_dir_;
544   QDir temp_dir_;
545   QDir writeable_data_dir_;
546   QDir default_save_directory_;
547   QDir save_directory_;
548   QDir default_azel_directory_;
549   QDir azel_directory_;
550 
551   QFont font_;
552   QFont next_font_;
553 
554   QFont decoded_text_font_;
555   QFont next_decoded_text_font_;
556 
557   LotWUsers lotw_users_;
558 
559   bool restart_sound_input_device_;
560   bool restart_sound_output_device_;
561 
562   Type2MsgGen type_2_msg_gen_;
563 
564   QStringListModel macros_;
565   RearrangableMacrosModel next_macros_;
566   QAction * macro_delete_action_;
567 
568   Bands bands_;
569   IARURegions regions_;
570   IARURegions::Region region_;
571   Modes modes_;
572   FrequencyList_v2 frequencies_;
573   FrequencyList_v2 next_frequencies_;
574   StationList stations_;
575   StationList next_stations_;
576   FrequencyDelta current_offset_;
577   FrequencyDelta current_tx_offset_;
578 
579   QAction * frequency_delete_action_;
580   QAction * frequency_insert_action_;
581   QAction * load_frequencies_action_;
582   QAction * save_frequencies_action_;
583   QAction * merge_frequencies_action_;
584   QAction * reset_frequencies_action_;
585   FrequencyDialog * frequency_dialog_;
586 
587   QAction station_delete_action_;
588   QAction station_insert_action_;
589   StationDialog * station_dialog_;
590 
591   DecodeHighlightingModel decode_highlighing_model_;
592   DecodeHighlightingModel next_decode_highlighing_model_;
593   bool highlight_by_mode_;
594   bool highlight_only_fields_;
595   bool include_WAE_entities_;
596   int LotW_days_since_upload_;
597 
598   TransceiverFactory::ParameterPack rig_params_;
599   TransceiverFactory::ParameterPack saved_rig_params_;
600   TransceiverFactory::Capabilities::PortType last_port_type_;
601   bool rig_is_dummy_;
602   bool rig_active_;
603   bool have_rig_;
604   bool rig_changed_;
605   TransceiverState cached_rig_state_;
606   int rig_resolution_;          // see Transceiver::resolution signal
607   CalibrationParams calibration_;
608   bool frequency_calibration_disabled_; // not persistent
609   unsigned transceiver_command_number_;
610   QString dynamic_grid_;
611 
612   // configuration fields that we publish
613   QString my_callsign_;
614   QString my_grid_;
615   QString FD_exchange_;
616   QString RTTY_exchange_;
617 
618   qint32 id_interval_;
619   qint32 ntrials_;
620   qint32 aggressive_;
621   qint32 RxBandwidth_;
622   double degrade_;
623   double txDelay_;
624   bool id_after_73_;
625   bool tx_QSY_allowed_;
626   bool spot_to_psk_reporter_;
627   bool psk_reporter_tcpip_;
628   bool monitor_off_at_startup_;
629   bool monitor_last_used_;
630   bool log_as_RTTY_;
631   bool report_in_comments_;
632   bool prompt_to_log_;
633   bool autoLog_;
634   bool decodes_from_top_;
635   bool insert_blank_;
636   bool DXCC_;
637   bool ppfx_;
638   bool clear_DX_;
639   bool miles_;
640   bool quick_call_;
641   bool disable_TX_on_73_;
642   bool force_call_1st_;
643   bool alternate_bindings_;
644   int watchdog_;
645   bool TX_messages_;
646   bool enable_VHF_features_;
647   bool decode_at_52s_;
648   bool single_decode_;
649   bool twoPass_;
650   bool bSpecialOp_;
651   int  SelectedActivity_;
652   bool x2ToneSpacing_;
653   bool x4ToneSpacing_;
654   bool use_dynamic_grid_;
655   QString opCall_;
656   QString udp_server_name_;
657   bool udp_server_name_edited_;
658   int dns_lookup_id_;
659   port_type udp_server_port_;
660   QStringList udp_interface_names_;
661   QString loopback_interface_name_;
662   int udp_TTL_;
663   QString n1mm_server_name_;
664   port_type n1mm_server_port_;
665   bool broadcast_to_n1mm_;
666   bool accept_udp_requests_;
667   bool udpWindowToFront_;
668   bool udpWindowRestore_;
669   DataMode data_mode_;
670   bool bLowSidelobes_;
671   bool pwrBandTxMemory_;
672   bool pwrBandTuneMemory_;
673 
674   QAudioDeviceInfo audio_input_device_;
675   QAudioDeviceInfo next_audio_input_device_;
676   AudioDevice::Channel audio_input_channel_;
677   AudioDevice::Channel next_audio_input_channel_;
678   QAudioDeviceInfo audio_output_device_;
679   QAudioDeviceInfo next_audio_output_device_;
680   AudioDevice::Channel audio_output_channel_;
681   AudioDevice::Channel next_audio_output_channel_;
682   // Z
683   QString qrzComUn_;
684   QString qrzComPw_;
685   bool disableWriteALL_;
686   bool disableWriteFoxQSO_;
687   bool colourAll_;
688   bool autoCQfiltering_;
689   bool rxTotxFreq_;
690   bool udpFiltering_;
691   bool highlightDX_;
692   bool dbgScreen_;
693   bool dbgFile_;
694   bool dbgBoth_;
695   bool wdResetAnywhere_;
696   int wd_FT8_;
697   int wd_FT4_;
698   bool wd_Timer_;
699   bool processTailenders_;
700   QString permIgnoreList_;
701   bool showDistance_;
702   bool showBearing_;
703   bool autoTune_;
704   bool showState_;
705   bool rawViewDXCC_;
706   bool clearRx_;
707   int ignoreListReset_;
708   QString separatorColor_;
709 
710   friend class Configuration;
711 };
712 
713 #include "Configuration.moc"
714 
715 
716 // delegate to implementation class
Configuration(QNetworkAccessManager * network_manager,QDir const & temp_directory,QSettings * settings,LogBook * logbook,QWidget * parent)717 Configuration::Configuration (QNetworkAccessManager * network_manager, QDir const& temp_directory,
718                               QSettings * settings, LogBook * logbook, QWidget * parent)
719   : m_ {this, network_manager, temp_directory, settings, logbook, parent}
720 {
721 }
722 
~Configuration()723 Configuration::~Configuration ()
724 {
725 }
726 
doc_dir() const727 QDir Configuration::doc_dir () const {return m_->doc_dir_;}
data_dir() const728 QDir Configuration::data_dir () const {return m_->data_dir_;}
writeable_data_dir() const729 QDir Configuration::writeable_data_dir () const {return m_->writeable_data_dir_;}
temp_dir() const730 QDir Configuration::temp_dir () const {return m_->temp_dir_;}
731 
select_tab(int index)732 void Configuration::select_tab (int index) {m_->ui_->configuration_tabs->setCurrentIndex (index);}
exec()733 int Configuration::exec () {return m_->exec ();}
is_active() const734 bool Configuration::is_active () const {return m_->isVisible ();}
735 
audio_input_device() const736 QAudioDeviceInfo const& Configuration::audio_input_device () const {return m_->audio_input_device_;}
audio_input_channel() const737 AudioDevice::Channel Configuration::audio_input_channel () const {return m_->audio_input_channel_;}
audio_output_device() const738 QAudioDeviceInfo const& Configuration::audio_output_device () const {return m_->audio_output_device_;}
audio_output_channel() const739 AudioDevice::Channel Configuration::audio_output_channel () const {return m_->audio_output_channel_;}
restart_audio_input() const740 bool Configuration::restart_audio_input () const {return m_->restart_sound_input_device_;}
restart_audio_output() const741 bool Configuration::restart_audio_output () const {return m_->restart_sound_output_device_;}
type_2_msg_gen() const742 auto Configuration::type_2_msg_gen () const -> Type2MsgGen {return m_->type_2_msg_gen_;}
my_callsign() const743 QString Configuration::my_callsign () const {return m_->my_callsign_;}
text_font() const744 QFont Configuration::text_font () const {return m_->font_;}
decoded_text_font() const745 QFont Configuration::decoded_text_font () const {return m_->decoded_text_font_;}
id_interval() const746 qint32 Configuration::id_interval () const {return m_->id_interval_;}
ntrials() const747 qint32 Configuration::ntrials() const {return m_->ntrials_;}
aggressive() const748 qint32 Configuration::aggressive() const {return m_->aggressive_;}
degrade() const749 double Configuration::degrade() const {return m_->degrade_;}
txDelay() const750 double Configuration::txDelay() const {return m_->txDelay_;}
RxBandwidth() const751 qint32 Configuration::RxBandwidth() const {return m_->RxBandwidth_;}
id_after_73() const752 bool Configuration::id_after_73 () const {return m_->id_after_73_;}
tx_QSY_allowed() const753 bool Configuration::tx_QSY_allowed () const {return m_->tx_QSY_allowed_;}
spot_to_psk_reporter() const754 bool Configuration::spot_to_psk_reporter () const
755 {
756   // rig must be open and working to spot externally
757   return is_transceiver_online () && m_->spot_to_psk_reporter_;
758 }
psk_reporter_tcpip() const759 bool Configuration::psk_reporter_tcpip () const {return m_->psk_reporter_tcpip_;}
monitor_off_at_startup() const760 bool Configuration::monitor_off_at_startup () const {return m_->monitor_off_at_startup_;}
monitor_last_used() const761 bool Configuration::monitor_last_used () const {return m_->rig_is_dummy_ || m_->monitor_last_used_;}
log_as_RTTY() const762 bool Configuration::log_as_RTTY () const {return m_->log_as_RTTY_;}
report_in_comments() const763 bool Configuration::report_in_comments () const {return m_->report_in_comments_;}
prompt_to_log() const764 bool Configuration::prompt_to_log () const {return m_->prompt_to_log_;}
autoLog() const765 bool Configuration::autoLog() const {return m_->autoLog_;}
decodes_from_top() const766 bool Configuration::decodes_from_top () const {return m_->decodes_from_top_;}
insert_blank() const767 bool Configuration::insert_blank () const {return m_->insert_blank_;}
DXCC() const768 bool Configuration::DXCC () const {return m_->DXCC_;}
ppfx() const769 bool Configuration::ppfx() const {return m_->ppfx_;}
clear_DX() const770 bool Configuration::clear_DX () const {return m_->clear_DX_;}
miles() const771 bool Configuration::miles () const {return m_->miles_;}
quick_call() const772 bool Configuration::quick_call () const {return m_->quick_call_;}
disable_TX_on_73() const773 bool Configuration::disable_TX_on_73 () const {return m_->disable_TX_on_73_;}
force_call_1st() const774 bool Configuration::force_call_1st() const {return m_->force_call_1st_;}
alternate_bindings() const775 bool Configuration::alternate_bindings() const {return m_->alternate_bindings_;}
watchdog() const776 int Configuration::watchdog () const {return m_->watchdog_;}
TX_messages() const777 bool Configuration::TX_messages () const {return m_->TX_messages_;}
enable_VHF_features() const778 bool Configuration::enable_VHF_features () const {return m_->enable_VHF_features_;}
decode_at_52s() const779 bool Configuration::decode_at_52s () const {return m_->decode_at_52s_;}
single_decode() const780 bool Configuration::single_decode () const {return m_->single_decode_;}
twoPass() const781 bool Configuration::twoPass() const {return m_->twoPass_;}
x2ToneSpacing() const782 bool Configuration::x2ToneSpacing() const {return m_->x2ToneSpacing_;}
x4ToneSpacing() const783 bool Configuration::x4ToneSpacing() const {return m_->x4ToneSpacing_;}
split_mode() const784 bool Configuration::split_mode () const {return m_->split_mode ();}
opCall() const785 QString Configuration::opCall() const {return m_->opCall_;}
opCall(QString const & call)786 void Configuration::opCall (QString const& call) {m_->opCall_ = call;}
udp_server_name() const787 QString Configuration::udp_server_name () const {return m_->udp_server_name_;}
udp_server_port() const788 auto Configuration::udp_server_port () const -> port_type {return m_->udp_server_port_;}
udp_interface_names() const789 QStringList Configuration::udp_interface_names () const {return m_->udp_interface_names_;}
udp_TTL() const790 int Configuration::udp_TTL () const {return m_->udp_TTL_;}
accept_udp_requests() const791 bool Configuration::accept_udp_requests () const {return m_->accept_udp_requests_;}
n1mm_server_name() const792 QString Configuration::n1mm_server_name () const {return m_->n1mm_server_name_;}
n1mm_server_port() const793 auto Configuration::n1mm_server_port () const -> port_type {return m_->n1mm_server_port_;}
broadcast_to_n1mm() const794 bool Configuration::broadcast_to_n1mm () const {return m_->broadcast_to_n1mm_;}
lowSidelobes() const795 bool Configuration::lowSidelobes() const {return m_->bLowSidelobes_;}
udpWindowToFront() const796 bool Configuration::udpWindowToFront () const {return m_->udpWindowToFront_;}
udpWindowRestore() const797 bool Configuration::udpWindowRestore () const {return m_->udpWindowRestore_;}
bands()798 Bands * Configuration::bands () {return &m_->bands_;}
bands() const799 Bands const * Configuration::bands () const {return &m_->bands_;}
stations()800 StationList * Configuration::stations () {return &m_->stations_;}
stations() const801 StationList const * Configuration::stations () const {return &m_->stations_;}
region() const802 IARURegions::Region Configuration::region () const {return m_->region_;}
frequencies()803 FrequencyList_v2 * Configuration::frequencies () {return &m_->frequencies_;}
frequencies() const804 FrequencyList_v2 const * Configuration::frequencies () const {return &m_->frequencies_;}
macros()805 QStringListModel * Configuration::macros () {return &m_->macros_;}
macros() const806 QStringListModel const * Configuration::macros () const {return &m_->macros_;}
save_directory() const807 QDir Configuration::save_directory () const {return m_->save_directory_;}
azel_directory() const808 QDir Configuration::azel_directory () const {return m_->azel_directory_;}
rig_name() const809 QString Configuration::rig_name () const {return m_->rig_params_.rig_name;}
pwrBandTxMemory() const810 bool Configuration::pwrBandTxMemory () const {return m_->pwrBandTxMemory_;}
pwrBandTuneMemory() const811 bool Configuration::pwrBandTuneMemory () const {return m_->pwrBandTuneMemory_;}
lotw_users() const812 LotWUsers const& Configuration::lotw_users () const {return m_->lotw_users_;}
decode_highlighting() const813 DecodeHighlightingModel const& Configuration::decode_highlighting () const {return m_->decode_highlighing_model_;}
highlight_by_mode() const814 bool Configuration::highlight_by_mode () const {return m_->highlight_by_mode_;}
highlight_only_fields() const815 bool Configuration::highlight_only_fields () const {return m_->highlight_only_fields_;}
include_WAE_entities() const816 bool Configuration::include_WAE_entities () const {return m_->include_WAE_entities_;}
817 // Z
qrzComPw() const818 QString Configuration::qrzComPw() const {return m_->qrzComPw_;}
qrzComUn() const819 QString Configuration::qrzComUn() const {return m_->qrzComUn_;}
disableWriteALL() const820 bool Configuration::disableWriteALL() const {return m_->disableWriteALL_;}
disableWriteFoxQSO() const821 bool Configuration::disableWriteFoxQSO() const {return m_->disableWriteFoxQSO_;}
colourAll() const822 bool Configuration::colourAll() const {return m_->colourAll_;}
autoCQfiltering() const823 bool Configuration::autoCQfiltering() const {return m_->autoCQfiltering_;}
rxTotxFreq() const824 bool Configuration::rxTotxFreq() const {return m_->rxTotxFreq_;}
udpFiltering() const825 bool Configuration::udpFiltering() const {return m_->udpFiltering_;}
highlightDX() const826 bool Configuration::highlightDX() const {return m_->highlightDX_;}
dbgScreen() const827 bool Configuration::dbgScreen() const {return m_->dbgScreen_;}
dbgFile() const828 bool Configuration::dbgFile() const {return m_->dbgFile_;}
dbgBoth() const829 bool Configuration::dbgBoth() const {return m_->dbgBoth_;}
wdResetAnywhere() const830 bool Configuration::wdResetAnywhere() const {return m_->wdResetAnywhere_;}
wd_FT8() const831 int Configuration::wd_FT8() const {return m_->wd_FT8_;}
wd_FT4() const832 int Configuration::wd_FT4() const {return m_->wd_FT4_;}
wd_Timer() const833 bool Configuration::wd_Timer() const {return m_->wd_Timer_;}
processTailenders() const834 bool Configuration::processTailenders() const {return m_->processTailenders_;}
permIgnoreList() const835 QString Configuration::permIgnoreList() const {return m_->permIgnoreList_;}
showDistance() const836 bool Configuration::showDistance() const {return m_->showDistance_;}
showBearing() const837 bool Configuration::showBearing() const {return m_->showBearing_;}
autoTune() const838 bool Configuration::autoTune() const {return m_->autoTune_;}
showState() const839 bool Configuration::showState() const {return m_->showState_;}
rawViewDXCC() const840 bool Configuration::rawViewDXCC() const {return m_->rawViewDXCC_;}
clearRX() const841 bool Configuration::clearRX() const {return m_->clearRx_;}
ignoreListReset() const842 int Configuration::ignoreListReset() const {return m_->ignoreListReset_;}
separatorColor() const843 QString Configuration::separatorColor() const {return m_->separatorColor_;}
844 
845 
846 
set_calibration(CalibrationParams params)847 void Configuration::set_calibration (CalibrationParams params)
848 {
849   m_->calibration_ = params;
850 }
851 
enable_calibration(bool on)852 void Configuration::enable_calibration (bool on)
853 {
854   auto target_frequency = m_->remove_calibration (m_->cached_rig_state_.frequency ()) - m_->current_offset_;
855   m_->frequency_calibration_disabled_ = !on;
856   transceiver_frequency (target_frequency);
857 }
858 
is_transceiver_online() const859 bool Configuration::is_transceiver_online () const
860 {
861   return m_->rig_active_;
862 }
863 
is_dummy_rig() const864 bool Configuration::is_dummy_rig () const
865 {
866   return m_->rig_is_dummy_;
867 }
868 
transceiver_online()869 bool Configuration::transceiver_online ()
870 {
871   LOG_TRACE (m_->cached_rig_state_);
872   return m_->have_rig ();
873 }
874 
transceiver_resolution() const875 int Configuration::transceiver_resolution () const
876 {
877   return m_->rig_resolution_;
878 }
879 
transceiver_offline()880 void Configuration::transceiver_offline ()
881 {
882   LOG_TRACE (m_->cached_rig_state_);
883   m_->close_rig ();
884 }
885 
transceiver_frequency(Frequency f)886 void Configuration::transceiver_frequency (Frequency f)
887 {
888   LOG_TRACE (f << ' ' << m_->cached_rig_state_);
889   m_->transceiver_frequency (f);
890 }
891 
transceiver_tx_frequency(Frequency f)892 void Configuration::transceiver_tx_frequency (Frequency f)
893 {
894   LOG_TRACE (f << ' ' << m_->cached_rig_state_);
895   m_->transceiver_tx_frequency (f);
896 }
897 
transceiver_mode(MODE mode)898 void Configuration::transceiver_mode (MODE mode)
899 {
900   LOG_TRACE (mode << ' ' << m_->cached_rig_state_);
901   m_->transceiver_mode (mode);
902 }
903 
transceiver_ptt(bool on)904 void Configuration::transceiver_ptt (bool on)
905 {
906   LOG_TRACE (on << ' ' << m_->cached_rig_state_);
907   m_->transceiver_ptt (on);
908 }
909 
sync_transceiver(bool force_signal,bool enforce_mode_and_split)910 void Configuration::sync_transceiver (bool force_signal, bool enforce_mode_and_split)
911 {
912   LOG_TRACE ("force signal: " << force_signal << " enforce_mode_and_split: " << enforce_mode_and_split << ' ' << m_->cached_rig_state_);
913   m_->sync_transceiver (force_signal);
914   if (!enforce_mode_and_split)
915     {
916       m_->transceiver_tx_frequency (0);
917     }
918 }
919 
invalidate_audio_input_device(QString)920 void Configuration::invalidate_audio_input_device (QString /* error */)
921 {
922   m_->audio_input_device_ = QAudioDeviceInfo {};
923 }
924 
invalidate_audio_output_device(QString)925 void Configuration::invalidate_audio_output_device (QString /* error */)
926 {
927   m_->audio_output_device_ = QAudioDeviceInfo {};
928 }
929 
valid_n1mm_info() const930 bool Configuration::valid_n1mm_info () const
931 {
932   // do very rudimentary checking on the n1mm server name and port number.
933   //
934   auto server_name = m_->n1mm_server_name_;
935   auto port_number = m_->n1mm_server_port_;
936   return(!(server_name.trimmed().isEmpty() || port_number == 0));
937 }
938 
my_grid() const939 QString Configuration::my_grid() const
940 {
941   auto the_grid = m_->my_grid_;
942   if (m_->use_dynamic_grid_ && m_->dynamic_grid_.size () >= 4) {
943     the_grid = m_->dynamic_grid_;
944   }
945   return the_grid;
946 }
947 
Field_Day_Exchange() const948 QString Configuration::Field_Day_Exchange() const
949 {
950   return m_->FD_exchange_;
951 }
952 
setEU_VHF_Contest()953 void Configuration::setEU_VHF_Contest()
954 {
955   m_->bSpecialOp_=true;
956   m_->ui_->gbSpecialOpActivity->setChecked(m_->bSpecialOp_);
957   m_->ui_->rbEU_VHF_Contest->setChecked(true);
958   m_->SelectedActivity_ = static_cast<int> (SpecialOperatingActivity::EU_VHF);
959   m_->write_settings();
960 }
961 
RTTY_Exchange() const962 QString Configuration::RTTY_Exchange() const
963 {
964   return m_->RTTY_exchange_;
965 }
966 
special_op_id() const967 auto Configuration::special_op_id () const -> SpecialOperatingActivity
968 {
969   return m_->bSpecialOp_ ? static_cast<SpecialOperatingActivity> (m_->SelectedActivity_) : SpecialOperatingActivity::NONE;
970 }
971 
set_location(QString const & grid_descriptor)972 void Configuration::set_location (QString const& grid_descriptor)
973 {
974   // change the dynamic grid
975   // qDebug () << "Configuration::set_location - location:" << grid_descriptor;
976   m_->dynamic_grid_ = grid_descriptor.trimmed ();
977 }
978 // Z
setSpecial_Hound()979 void Configuration::setSpecial_Hound()
980 {
981   m_->bSpecialOp_=true;
982   m_->ui_->gbSpecialOpActivity->setChecked(m_->bSpecialOp_);
983   m_->ui_->rbHound->setChecked(true);
984   m_->SelectedActivity_ = static_cast<int> (SpecialOperatingActivity::HOUND);
985   m_->write_settings();
986 }
987 
setSpecial_Fox()988 void Configuration::setSpecial_Fox()
989 {
990   m_->bSpecialOp_=true;
991   m_->ui_->gbSpecialOpActivity->setChecked(m_->bSpecialOp_);
992   m_->ui_->rbFox->setChecked(true);
993   m_->SelectedActivity_ = static_cast<int> (SpecialOperatingActivity::FOX);
994   m_->write_settings();
995 }
996 
setSpecial_None()997 void Configuration::setSpecial_None()
998 {
999   m_->bSpecialOp_=false;
1000   m_->ui_->gbSpecialOpActivity->setChecked(m_->bSpecialOp_);
1001   m_->write_settings();
1002 }
1003 
1004 namespace
1005 {
1006 #if defined (Q_OS_MAC)
1007   char const * app_root = "/../../../";
1008 #else
1009   char const * app_root = "/../";
1010 #endif
doc_path()1011   QString doc_path ()
1012   {
1013 #if CMAKE_BUILD
1014     if (QDir::isRelativePath (CMAKE_INSTALL_DOCDIR))
1015       {
1016 	return QApplication::applicationDirPath () + app_root + CMAKE_INSTALL_DOCDIR;
1017       }
1018     return CMAKE_INSTALL_DOCDIR;
1019 #else
1020     return QApplication::applicationDirPath ();
1021 #endif
1022   }
1023 
data_path()1024   QString data_path ()
1025   {
1026 #if CMAKE_BUILD
1027     if (QDir::isRelativePath (CMAKE_INSTALL_DATADIR))
1028       {
1029 	return QApplication::applicationDirPath () + app_root + CMAKE_INSTALL_DATADIR + QChar {'/'} + CMAKE_PROJECT_NAME;
1030       }
1031     return CMAKE_INSTALL_DATADIR;
1032 #else
1033     return QApplication::applicationDirPath ();
1034 #endif
1035   }
1036 }
1037 
impl(Configuration * self,QNetworkAccessManager * network_manager,QDir const & temp_directory,QSettings * settings,LogBook * logbook,QWidget * parent)1038 Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network_manager
1039                            , QDir const& temp_directory, QSettings * settings, LogBook * logbook
1040                            , QWidget * parent)
1041   : QDialog {parent}
1042   , self_ {self}
1043   , transceiver_thread_ {nullptr}
1044   , ui_ {new Ui::configuration_dialog}
1045   , network_manager_ {network_manager}
1046   , settings_ {settings}
1047   , logbook_ {logbook}
1048   , doc_dir_ {doc_path ()}
1049   , data_dir_ {data_path ()}
1050   , temp_dir_ {temp_directory}
1051   , writeable_data_dir_ {QStandardPaths::writableLocation (QStandardPaths::DataLocation)}
1052   , lotw_users_ {network_manager_}
1053   , restart_sound_input_device_ {false}
1054   , restart_sound_output_device_ {false}
1055   , frequencies_ {&bands_}
1056   , next_frequencies_ {&bands_}
1057   , stations_ {&bands_}
1058   , next_stations_ {&bands_}
1059   , current_offset_ {0}
1060   , current_tx_offset_ {0}
1061   , frequency_dialog_ {new FrequencyDialog {&regions_, &modes_, this}}
1062   , station_delete_action_ {tr ("&Delete"), nullptr}
1063   , station_insert_action_ {tr ("&Insert ..."), nullptr}
1064   , station_dialog_ {new StationDialog {&next_stations_, &bands_, this}}
1065   , highlight_by_mode_ {false}
1066   , highlight_only_fields_ {false}
1067   , include_WAE_entities_ {false}
1068   , LotW_days_since_upload_ {0}
1069   , last_port_type_ {TransceiverFactory::Capabilities::none}
1070   , rig_is_dummy_ {false}
1071   , rig_active_ {false}
1072   , have_rig_ {false}
1073   , rig_changed_ {false}
1074   , rig_resolution_ {0}
1075   , frequency_calibration_disabled_ {false}
1076   , transceiver_command_number_ {0}
1077   , degrade_ {0.}               // initialize to zero each run, not
1078                                 // saved in settings
1079   , udp_server_name_edited_ {false}
1080   , dns_lookup_id_ {-1}
1081 {
1082   ui_->setupUi (this);
1083 
1084   {
1085     // Make sure the default save directory exists
1086     QString save_dir {"save"};
1087     default_save_directory_ = writeable_data_dir_;
1088     default_azel_directory_ = writeable_data_dir_;
1089     if (!default_save_directory_.mkpath (save_dir) || !default_save_directory_.cd (save_dir))
1090       {
1091         MessageBox::critical_message (this, tr ("Failed to create save directory"),
1092                                       tr ("path: \"%1\%")
1093                                       .arg (default_save_directory_.absoluteFilePath (save_dir)));
1094         throw std::runtime_error {"Failed to create save directory"};
1095       }
1096 
1097     // we now have a deafult save path that exists
1098 
1099     // make sure samples directory exists
1100     QString samples_dir {"samples"};
1101     if (!default_save_directory_.mkpath (samples_dir))
1102       {
1103         MessageBox::critical_message (this, tr ("Failed to create samples directory"),
1104                                       tr ("path: \"%1\"")
1105                                       .arg (default_save_directory_.absoluteFilePath (samples_dir)));
1106         throw std::runtime_error {"Failed to create samples directory"};
1107       }
1108 
1109     // copy in any new sample files to the sample directory
1110     QDir dest_dir {default_save_directory_};
1111     dest_dir.cd (samples_dir);
1112 
1113     QDir source_dir {":/" + samples_dir};
1114     source_dir.cd (save_dir);
1115     source_dir.cd (samples_dir);
1116     auto list = source_dir.entryInfoList (QStringList {{"*.wav"}}, QDir::Files | QDir::Readable);
1117     Q_FOREACH (auto const& item, list)
1118       {
1119         if (!dest_dir.exists (item.fileName ()))
1120           {
1121             QFile file {item.absoluteFilePath ()};
1122             file.copy (dest_dir.absoluteFilePath (item.fileName ()));
1123           }
1124       }
1125   }
1126 
1127   // this must be done after the default paths above are set
1128   read_settings ();
1129 
1130   // set up dynamic loading of audio devices
__anonb62527010302() 1131   connect (ui_->sound_input_combo_box, &LazyFillComboBox::about_to_show_popup, [this] () {
1132       QGuiApplication::setOverrideCursor (QCursor {Qt::WaitCursor});
1133       load_audio_devices (QAudio::AudioInput, ui_->sound_input_combo_box, &next_audio_input_device_);
1134       update_audio_channels (ui_->sound_input_combo_box, ui_->sound_input_combo_box->currentIndex (), ui_->sound_input_channel_combo_box, false);
1135       ui_->sound_input_channel_combo_box->setCurrentIndex (next_audio_input_channel_);
1136       QGuiApplication::restoreOverrideCursor ();
1137     });
__anonb62527010402() 1138   connect (ui_->sound_output_combo_box, &LazyFillComboBox::about_to_show_popup, [this] () {
1139       QGuiApplication::setOverrideCursor (QCursor {Qt::WaitCursor});
1140       load_audio_devices (QAudio::AudioOutput, ui_->sound_output_combo_box, &next_audio_output_device_);
1141       update_audio_channels (ui_->sound_output_combo_box, ui_->sound_output_combo_box->currentIndex (), ui_->sound_output_channel_combo_box, true);
1142       ui_->sound_output_channel_combo_box->setCurrentIndex (next_audio_output_channel_);
1143       QGuiApplication::restoreOverrideCursor ();
1144     });
1145 
1146   // set up dynamic loading of network interfaces
__anonb62527010502() 1147   connect (ui_->udp_interfaces_combo_box, &LazyFillComboBox::about_to_show_popup, [this] () {
1148       QGuiApplication::setOverrideCursor (QCursor {Qt::WaitCursor});
1149       load_network_interfaces (ui_->udp_interfaces_combo_box, udp_interface_names_);
1150       QGuiApplication::restoreOverrideCursor ();
1151     });
1152   connect (ui_->udp_interfaces_combo_box, &QComboBox::currentTextChanged, this, &Configuration::impl::validate_network_interfaces);
1153 
1154   // set up LoTW users CSV file fetching
__anonb62527010602() 1155   connect (&lotw_users_, &LotWUsers::load_finished, [this] () {
1156       ui_->LotW_CSV_fetch_push_button->setEnabled (true);
1157     });
1158   lotw_users_.set_local_file_path (writeable_data_dir_.absoluteFilePath ("lotw-user-activity.csv"));
1159 
1160   //
1161   // validation
1162   //
1163   ui_->callsign_line_edit->setValidator (new CallsignValidator {this});
1164   ui_->grid_line_edit->setValidator (new MaidenheadLocatorValidator {this});
1165   ui_->add_macro_line_edit->setValidator (new QRegularExpressionValidator {message_alphabet, this});
1166   ui_->Field_Day_Exchange->setValidator (new QRegularExpressionValidator {field_day_exchange_re, this});
1167   ui_->RTTY_Exchange->setValidator (new QRegularExpressionValidator {RTTY_roundup_exchange_re, this});
1168 
1169   //
1170   // assign ids to radio buttons
1171   //
1172   ui_->CAT_data_bits_button_group->setId (ui_->CAT_default_bit_radio_button, TransceiverFactory::default_data_bits);
1173   ui_->CAT_data_bits_button_group->setId (ui_->CAT_7_bit_radio_button, TransceiverFactory::seven_data_bits);
1174   ui_->CAT_data_bits_button_group->setId (ui_->CAT_8_bit_radio_button, TransceiverFactory::eight_data_bits);
1175 
1176   ui_->CAT_stop_bits_button_group->setId (ui_->CAT_default_stop_bit_radio_button, TransceiverFactory::default_stop_bits);
1177   ui_->CAT_stop_bits_button_group->setId (ui_->CAT_one_stop_bit_radio_button, TransceiverFactory::one_stop_bit);
1178   ui_->CAT_stop_bits_button_group->setId (ui_->CAT_two_stop_bit_radio_button, TransceiverFactory::two_stop_bits);
1179 
1180   ui_->CAT_handshake_button_group->setId (ui_->CAT_handshake_default_radio_button, TransceiverFactory::handshake_default);
1181   ui_->CAT_handshake_button_group->setId (ui_->CAT_handshake_none_radio_button, TransceiverFactory::handshake_none);
1182   ui_->CAT_handshake_button_group->setId (ui_->CAT_handshake_xon_radio_button, TransceiverFactory::handshake_XonXoff);
1183   ui_->CAT_handshake_button_group->setId (ui_->CAT_handshake_hardware_radio_button, TransceiverFactory::handshake_hardware);
1184 
1185   ui_->PTT_method_button_group->setId (ui_->PTT_VOX_radio_button, TransceiverFactory::PTT_method_VOX);
1186   ui_->PTT_method_button_group->setId (ui_->PTT_CAT_radio_button, TransceiverFactory::PTT_method_CAT);
1187   ui_->PTT_method_button_group->setId (ui_->PTT_DTR_radio_button, TransceiverFactory::PTT_method_DTR);
1188   ui_->PTT_method_button_group->setId (ui_->PTT_RTS_radio_button, TransceiverFactory::PTT_method_RTS);
1189 
1190   ui_->TX_audio_source_button_group->setId (ui_->TX_source_mic_radio_button, TransceiverFactory::TX_audio_source_front);
1191   ui_->TX_audio_source_button_group->setId (ui_->TX_source_data_radio_button, TransceiverFactory::TX_audio_source_rear);
1192 
1193   ui_->TX_mode_button_group->setId (ui_->mode_none_radio_button, data_mode_none);
1194   ui_->TX_mode_button_group->setId (ui_->mode_USB_radio_button, data_mode_USB);
1195   ui_->TX_mode_button_group->setId (ui_->mode_data_radio_button, data_mode_data);
1196 
1197   ui_->split_mode_button_group->setId (ui_->split_none_radio_button, TransceiverFactory::split_mode_none);
1198   ui_->split_mode_button_group->setId (ui_->split_rig_radio_button, TransceiverFactory::split_mode_rig);
1199   ui_->split_mode_button_group->setId (ui_->split_emulate_radio_button, TransceiverFactory::split_mode_emulate);
1200 
1201   ui_->special_op_activity_button_group->setId (ui_->rbNA_VHF_Contest, static_cast<int> (SpecialOperatingActivity::NA_VHF));
1202   ui_->special_op_activity_button_group->setId (ui_->rbEU_VHF_Contest, static_cast<int> (SpecialOperatingActivity::EU_VHF));
1203   ui_->special_op_activity_button_group->setId (ui_->rbField_Day, static_cast<int> (SpecialOperatingActivity::FIELD_DAY));
1204   ui_->special_op_activity_button_group->setId (ui_->rbRTTY_Roundup, static_cast<int> (SpecialOperatingActivity::RTTY));
1205   ui_->special_op_activity_button_group->setId (ui_->rbWW_DIGI, static_cast<int> (SpecialOperatingActivity::WW_DIGI));
1206   ui_->special_op_activity_button_group->setId (ui_->rbFox, static_cast<int> (SpecialOperatingActivity::FOX));
1207   ui_->special_op_activity_button_group->setId (ui_->rbHound, static_cast<int> (SpecialOperatingActivity::HOUND));
1208 
1209   //
1210   // setup PTT port combo box drop down content
1211   //
1212   fill_port_combo_box (ui_->PTT_port_combo_box);
1213   ui_->PTT_port_combo_box->addItem ("CAT");
1214   ui_->PTT_port_combo_box->setItemData (ui_->PTT_port_combo_box->count () - 1, "Delegate to proxy CAT service", Qt::ToolTipRole);
1215 
1216   //
1217   // setup hooks to keep audio channels aligned with devices
1218   //
1219   {
1220     using namespace std;
1221     using namespace std::placeholders;
1222 
1223     function<void (int)> cb (bind (&Configuration::impl::update_audio_channels, this, ui_->sound_input_combo_box, _1, ui_->sound_input_channel_combo_box, false));
1224     connect (ui_->sound_input_combo_box, static_cast<void (QComboBox::*)(int)> (&QComboBox::currentIndexChanged), cb);
1225     cb = bind (&Configuration::impl::update_audio_channels, this, ui_->sound_output_combo_box, _1, ui_->sound_output_channel_combo_box, true);
1226     connect (ui_->sound_output_combo_box, static_cast<void (QComboBox::*)(int)> (&QComboBox::currentIndexChanged), cb);
1227   }
1228 
1229   //
1230   // setup macros list view
1231   //
1232   ui_->macros_list_view->setModel (&next_macros_);
1233   ui_->macros_list_view->setItemDelegate (new MessageItemDelegate {this});
1234 
1235   macro_delete_action_ = new QAction {tr ("&Delete"), ui_->macros_list_view};
1236   ui_->macros_list_view->insertAction (nullptr, macro_delete_action_);
1237   connect (macro_delete_action_, &QAction::triggered, this, &Configuration::impl::delete_macro);
1238 
1239   // setup IARU region combo box model
1240   ui_->region_combo_box->setModel (&regions_);
1241 
1242   //
1243   // setup working frequencies table model & view
1244   //
1245   frequencies_.sort (FrequencyList_v2::frequency_column);
1246 
1247   ui_->frequencies_table_view->setModel (&next_frequencies_);
1248   ui_->frequencies_table_view->horizontalHeader ()->setSectionResizeMode (QHeaderView::ResizeToContents);
1249   ui_->frequencies_table_view->horizontalHeader ()->setResizeContentsPrecision (0);
1250   ui_->frequencies_table_view->verticalHeader ()->setSectionResizeMode (QHeaderView::ResizeToContents);
1251   ui_->frequencies_table_view->verticalHeader ()->setResizeContentsPrecision (0);
1252   ui_->frequencies_table_view->sortByColumn (FrequencyList_v2::frequency_column, Qt::AscendingOrder);
1253   ui_->frequencies_table_view->setColumnHidden (FrequencyList_v2::frequency_mhz_column, true);
1254 
1255   // delegates
1256   ui_->frequencies_table_view->setItemDelegateForColumn (FrequencyList_v2::frequency_column, new FrequencyDelegate {this});
1257   ui_->frequencies_table_view->setItemDelegateForColumn (FrequencyList_v2::region_column, new ForeignKeyDelegate {&regions_, 0, this});
1258   ui_->frequencies_table_view->setItemDelegateForColumn (FrequencyList_v2::mode_column, new ForeignKeyDelegate {&modes_, 0, this});
1259 
1260   // actions
1261   frequency_delete_action_ = new QAction {tr ("&Delete"), ui_->frequencies_table_view};
1262   ui_->frequencies_table_view->insertAction (nullptr, frequency_delete_action_);
1263   connect (frequency_delete_action_, &QAction::triggered, this, &Configuration::impl::delete_frequencies);
1264 
1265   frequency_insert_action_ = new QAction {tr ("&Insert ..."), ui_->frequencies_table_view};
1266   ui_->frequencies_table_view->insertAction (nullptr, frequency_insert_action_);
1267   connect (frequency_insert_action_, &QAction::triggered, this, &Configuration::impl::insert_frequency);
1268 
1269   load_frequencies_action_ = new QAction {tr ("&Load ..."), ui_->frequencies_table_view};
1270   ui_->frequencies_table_view->insertAction (nullptr, load_frequencies_action_);
1271   connect (load_frequencies_action_, &QAction::triggered, this, &Configuration::impl::load_frequencies);
1272 
1273   save_frequencies_action_ = new QAction {tr ("&Save as ..."), ui_->frequencies_table_view};
1274   ui_->frequencies_table_view->insertAction (nullptr, save_frequencies_action_);
1275   connect (save_frequencies_action_, &QAction::triggered, this, &Configuration::impl::save_frequencies);
1276 
1277   merge_frequencies_action_ = new QAction {tr ("&Merge ..."), ui_->frequencies_table_view};
1278   ui_->frequencies_table_view->insertAction (nullptr, merge_frequencies_action_);
1279   connect (merge_frequencies_action_, &QAction::triggered, this, &Configuration::impl::merge_frequencies);
1280 
1281   reset_frequencies_action_ = new QAction {tr ("&Reset"), ui_->frequencies_table_view};
1282   ui_->frequencies_table_view->insertAction (nullptr, reset_frequencies_action_);
1283   connect (reset_frequencies_action_, &QAction::triggered, this, &Configuration::impl::reset_frequencies);
1284 
1285   //
1286   // setup stations table model & view
1287   //
1288   stations_.sort (StationList::band_column);
1289   ui_->stations_table_view->setModel (&next_stations_);
1290   ui_->stations_table_view->horizontalHeader ()->setSectionResizeMode (QHeaderView::ResizeToContents);
1291   ui_->stations_table_view->horizontalHeader ()->setResizeContentsPrecision (0);
1292   ui_->stations_table_view->verticalHeader ()->setSectionResizeMode (QHeaderView::ResizeToContents);
1293   ui_->stations_table_view->verticalHeader ()->setResizeContentsPrecision (0);
1294   ui_->stations_table_view->sortByColumn (StationList::band_column, Qt::AscendingOrder);
1295 
1296   // stations delegates
1297   ui_->stations_table_view->setItemDelegateForColumn (StationList::offset_column, new FrequencyDeltaDelegate {this});
1298   ui_->stations_table_view->setItemDelegateForColumn (StationList::band_column, new ForeignKeyDelegate {&bands_, &next_stations_, 0, StationList::band_column, this});
1299 
1300   // stations actions
1301   ui_->stations_table_view->addAction (&station_delete_action_);
1302   connect (&station_delete_action_, &QAction::triggered, this, &Configuration::impl::delete_stations);
1303 
1304   ui_->stations_table_view->addAction (&station_insert_action_);
1305   connect (&station_insert_action_, &QAction::triggered, this, &Configuration::impl::insert_station);
1306 
1307   //
1308   // colours and highlighting setup
1309   //
1310   ui_->highlighting_list_view->setModel (&next_decode_highlighing_model_);
1311 
1312   enumerate_rigs ();
1313   initialize_models ();
1314 
1315   audio_input_device_ = next_audio_input_device_;
1316   audio_input_channel_ = next_audio_input_channel_;
1317   audio_output_device_ = next_audio_output_device_;
1318   audio_output_channel_ = next_audio_output_channel_;
1319 
1320   bool fetch_if_needed {false};
1321   for (auto const& item : decode_highlighing_model_.items ())
1322     {
1323       if (DecodeHighlightingModel::Highlight::LotW == item.type_)
1324         {
1325           fetch_if_needed = item.enabled_;
1326           break;
1327         }
1328     }
1329   // load the LoTW users dictionary if it exists, fetch and load if it
1330   // doesn't and we need it
1331   lotw_users_.load (ui_->LotW_CSV_URL_line_edit->text (), fetch_if_needed);
1332 
1333   transceiver_thread_ = new QThread {this};
1334   transceiver_thread_->start ();
1335 }
1336 
~impl()1337 Configuration::impl::~impl ()
1338 {
1339   transceiver_thread_->quit ();
1340   transceiver_thread_->wait ();
1341   write_settings ();
1342 }
1343 
initialize_models()1344 void Configuration::impl::initialize_models ()
1345 {
1346   next_audio_input_device_ = audio_input_device_;
1347   next_audio_input_channel_ = audio_input_channel_;
1348   next_audio_output_device_ = audio_output_device_;
1349   next_audio_output_channel_ = audio_output_channel_;
1350   restart_sound_input_device_ = false;
1351   restart_sound_output_device_ = false;
1352   {
1353     SettingsGroup g {settings_, "Configuration"};
1354     find_audio_devices ();
1355   }
1356   auto pal = ui_->callsign_line_edit->palette ();
1357   if (my_callsign_.isEmpty ())
1358     {
1359       pal.setColor (QPalette::Base, "#ffccff");
1360     }
1361   else
1362     {
1363       pal.setColor (QPalette::Base, Qt::white);
1364     }
1365   ui_->callsign_line_edit->setPalette (pal);
1366   ui_->grid_line_edit->setPalette (pal);
1367   ui_->callsign_line_edit->setText (my_callsign_);
1368   ui_->grid_line_edit->setText (my_grid_);
1369   ui_->use_dynamic_grid->setChecked(use_dynamic_grid_);
1370   ui_->CW_id_interval_spin_box->setValue (id_interval_);
1371   ui_->sbNtrials->setValue (ntrials_);
1372   ui_->sbTxDelay->setValue (txDelay_);
1373   ui_->sbAggressive->setValue (aggressive_);
1374   ui_->sbDegrade->setValue (degrade_);
1375   ui_->sbBandwidth->setValue (RxBandwidth_);
1376   ui_->PTT_method_button_group->button (rig_params_.ptt_type)->setChecked (true);
1377 
1378   ui_->save_path_display_label->setText (save_directory_.absolutePath ());
1379   ui_->azel_path_display_label->setText (azel_directory_.absolutePath ());
1380   ui_->CW_id_after_73_check_box->setChecked (id_after_73_);
1381   ui_->tx_QSY_check_box->setChecked (tx_QSY_allowed_);
1382   ui_->psk_reporter_check_box->setChecked (spot_to_psk_reporter_);
1383   ui_->psk_reporter_tcpip_check_box->setChecked (psk_reporter_tcpip_);
1384   ui_->monitor_off_check_box->setChecked (monitor_off_at_startup_);
1385   ui_->monitor_last_used_check_box->setChecked (monitor_last_used_);
1386   ui_->log_as_RTTY_check_box->setChecked (log_as_RTTY_);
1387   ui_->report_in_comments_check_box->setChecked (report_in_comments_);
1388   ui_->prompt_to_log_check_box->setChecked (prompt_to_log_);
1389   ui_->cbAutoLog->setChecked(autoLog_);
1390   ui_->decodes_from_top_check_box->setChecked (decodes_from_top_);
1391   ui_->insert_blank_check_box->setChecked (insert_blank_);
1392   ui_->DXCC_check_box->setChecked (DXCC_);
1393   ui_->ppfx_check_box->setChecked (ppfx_);
1394   ui_->clear_DX_check_box->setChecked (clear_DX_);
1395   ui_->miles_check_box->setChecked (miles_);
1396   ui_->quick_call_check_box->setChecked (quick_call_);
1397   ui_->disable_TX_on_73_check_box->setChecked (disable_TX_on_73_);
1398   ui_->force_call_1st_check_box->setChecked (force_call_1st_);
1399   ui_->alternate_bindings_check_box->setChecked (alternate_bindings_);
1400   ui_->tx_watchdog_spin_box->setValue (watchdog_);
1401   ui_->TX_messages_check_box->setChecked (TX_messages_);
1402   ui_->enable_VHF_features_check_box->setChecked(enable_VHF_features_);
1403   ui_->decode_at_52s_check_box->setChecked(decode_at_52s_);
1404   ui_->single_decode_check_box->setChecked(single_decode_);
1405   ui_->cbTwoPass->setChecked(twoPass_);
1406   ui_->gbSpecialOpActivity->setChecked(bSpecialOp_);
1407   ui_->special_op_activity_button_group->button (SelectedActivity_)->setChecked (true);
1408   ui_->cbx2ToneSpacing->setChecked(x2ToneSpacing_);
1409   ui_->cbx4ToneSpacing->setChecked(x4ToneSpacing_);
1410   ui_->type_2_msg_gen_combo_box->setCurrentIndex (type_2_msg_gen_);
1411   ui_->rig_combo_box->setCurrentText (rig_params_.rig_name);
1412   ui_->TX_mode_button_group->button (data_mode_)->setChecked (true);
1413   ui_->split_mode_button_group->button (rig_params_.split_mode)->setChecked (true);
1414   ui_->CAT_serial_baud_combo_box->setCurrentText (QString::number (rig_params_.baud));
1415   ui_->CAT_data_bits_button_group->button (rig_params_.data_bits)->setChecked (true);
1416   ui_->CAT_stop_bits_button_group->button (rig_params_.stop_bits)->setChecked (true);
1417   ui_->CAT_handshake_button_group->button (rig_params_.handshake)->setChecked (true);
1418   ui_->checkBoxPwrBandTxMemory->setChecked(pwrBandTxMemory_);
1419   ui_->checkBoxPwrBandTuneMemory->setChecked(pwrBandTuneMemory_);
1420   if (rig_params_.force_dtr)
1421     {
1422       ui_->force_DTR_combo_box->setCurrentIndex (rig_params_.dtr_high ? 1 : 2);
1423     }
1424   else
1425     {
1426       ui_->force_DTR_combo_box->setCurrentIndex (0);
1427     }
1428   if (rig_params_.force_rts)
1429     {
1430       ui_->force_RTS_combo_box->setCurrentIndex (rig_params_.rts_high ? 1 : 2);
1431     }
1432   else
1433     {
1434       ui_->force_RTS_combo_box->setCurrentIndex (0);
1435     }
1436   ui_->TX_audio_source_button_group->button (rig_params_.audio_source)->setChecked (true);
1437   ui_->CAT_poll_interval_spin_box->setValue (rig_params_.poll_interval);
1438   ui_->opCallEntry->setText (opCall_);
1439   ui_->udp_server_line_edit->setText (udp_server_name_);
1440   on_udp_server_line_edit_editingFinished ();
1441   ui_->udp_server_port_spin_box->setValue (udp_server_port_);
1442   load_network_interfaces (ui_->udp_interfaces_combo_box, udp_interface_names_);
1443   if (!udp_interface_names_.size ())
1444     {
1445       udp_interface_names_ = get_selected_network_interfaces (ui_->udp_interfaces_combo_box);
1446     }
1447   ui_->udp_TTL_spin_box->setValue (udp_TTL_);
1448   ui_->accept_udp_requests_check_box->setChecked (accept_udp_requests_);
1449   ui_->n1mm_server_name_line_edit->setText (n1mm_server_name_);
1450   ui_->n1mm_server_port_spin_box->setValue (n1mm_server_port_);
1451   ui_->enable_n1mm_broadcast_check_box->setChecked (broadcast_to_n1mm_);
1452   ui_->udpWindowToFront->setChecked(udpWindowToFront_);
1453   ui_->udpWindowRestore->setChecked(udpWindowRestore_);
1454   ui_->calibration_intercept_spin_box->setValue (calibration_.intercept);
1455   ui_->calibration_slope_ppm_spin_box->setValue (calibration_.slope_ppm);
1456   ui_->rbLowSidelobes->setChecked(bLowSidelobes_);
1457   if(!bLowSidelobes_) ui_->rbMaxSensitivity->setChecked(true);
1458 
1459   if (rig_params_.ptt_port.isEmpty ())
1460     {
1461       if (ui_->PTT_port_combo_box->count ())
1462         {
1463           ui_->PTT_port_combo_box->setCurrentText (ui_->PTT_port_combo_box->itemText (0));
1464         }
1465     }
1466   else
1467     {
1468       ui_->PTT_port_combo_box->setCurrentText (rig_params_.ptt_port);
1469     }
1470 
1471   ui_->region_combo_box->setCurrentIndex (region_);
1472 
1473   next_macros_.setStringList (macros_.stringList ());
1474   next_frequencies_.frequency_list (frequencies_.frequency_list ());
1475   next_stations_.station_list (stations_.station_list ());
1476 
1477   next_decode_highlighing_model_.items (decode_highlighing_model_.items ());
1478   ui_->highlight_by_mode_check_box->setChecked (highlight_by_mode_);
1479   ui_->only_fields_check_box->setChecked (highlight_only_fields_);
1480   ui_->include_WAE_check_box->setChecked (include_WAE_entities_);
1481   ui_->LotW_days_since_upload_spin_box->setValue (LotW_days_since_upload_);
1482   // Z
1483   ui_->le_qrzComPw->setText(qrzComPw_);
1484   ui_->le_qrzComUn->setText(qrzComUn_);
1485   ui_->cb_disableWriteALL->setChecked(disableWriteALL_);
1486   ui_->cb_disableWriteFoxQSO->setChecked(disableWriteFoxQSO_);
1487   ui_->cb_colourAll->setChecked(colourAll_);
1488   ui_->cb_autoCQfiltering->setChecked(autoCQfiltering_);
1489   ui_->cb_rxTotxFreq->setChecked(rxTotxFreq_);
1490   ui_->cb_udpFiltering->setChecked(udpFiltering_);
1491   ui_->cb_highlightDX->setChecked(highlightDX_);
1492   ui_->rb_dbg_Both->setChecked(dbgBoth_);
1493   ui_->rb_dbg_File->setChecked(dbgFile_);
1494   ui_->rb_dbg_Screen->setChecked(dbgScreen_);
1495   ui_->cb_WD_resetAnywhere->setChecked(wdResetAnywhere_);
1496   ui_->sb_WD_FT8->setValue(wd_FT8_);
1497   ui_->sb_WD_FT4->setValue(wd_FT4_);
1498   ui_->gb_WD_Timer->setChecked(wd_Timer_);
1499   ui_->te_permIgnoreList->setText(permIgnoreList_);
1500   ui_->cb_processTailenders->setChecked(processTailenders_);
1501   ui_->cb_showDistance->setChecked(showDistance_);
1502   ui_->cb_showBearing->setChecked(showBearing_);
1503   ui_->cb_autoTune->setChecked(autoTune_);
1504   ui_->cb_showState->setChecked(showState_);
1505   ui_->cb_rawViewDXCC->setChecked(rawViewDXCC_);
1506   ui_->cb_clearRx->setChecked(clearRx_);
1507   ui_->sb_ignoreListReset->setValue(ignoreListReset_);
1508   ui_->le_separatorColor->setText(separatorColor_);
1509 
1510   set_rig_invariants ();
1511 }
1512 
done(int r)1513 void Configuration::impl::done (int r)
1514 {
1515   // do this here since window is still on screen at this point
1516   SettingsGroup g {settings_, "Configuration"};
1517   settings_->setValue ("window/geometry", saveGeometry ());
1518 
1519   QDialog::done (r);
1520 }
1521 
read_settings()1522 void Configuration::impl::read_settings ()
1523 {
1524   SettingsGroup g {settings_, "Configuration"};
1525   restoreGeometry (settings_->value ("window/geometry").toByteArray ());
1526 
1527   my_callsign_ = settings_->value ("MyCall", QString {}).toString ();
1528   my_grid_ = settings_->value ("MyGrid", QString {}).toString ();
1529   FD_exchange_ = settings_->value ("Field_Day_Exchange",QString {}).toString ();
1530   RTTY_exchange_ = settings_->value ("RTTY_Exchange",QString {}).toString ();
1531   ui_->Field_Day_Exchange->setText(FD_exchange_);
1532   ui_->RTTY_Exchange->setText(RTTY_exchange_);
1533   if (next_font_.fromString (settings_->value ("Font", QGuiApplication::font ().toString ()).toString ())
1534       && next_font_ != font_)
1535     {
1536       font_ = next_font_;
1537       Q_EMIT self_->text_font_changed (font_);
1538     }
1539   else
1540     {
1541       next_font_ = font_;
1542     }
1543   if (next_decoded_text_font_.fromString (settings_->value ("DecodedTextFont", "Courier, 10").toString ())
1544       && next_decoded_text_font_ != decoded_text_font_)
1545     {
1546       decoded_text_font_ = next_decoded_text_font_;
1547       next_decode_highlighing_model_.set_font (decoded_text_font_);
1548       ui_->highlighting_list_view->reset ();
1549       Q_EMIT self_->decoded_text_font_changed (decoded_text_font_);
1550     }
1551   else
1552     {
1553       next_decoded_text_font_ = decoded_text_font_;
1554     }
1555 
1556   id_interval_ = settings_->value ("IDint", 0).toInt ();
1557   ntrials_ = settings_->value ("nTrials", 6).toInt ();
1558   txDelay_ = settings_->value ("TxDelay",0.2).toDouble();
1559   aggressive_ = settings_->value ("Aggressive", 0).toInt ();
1560   RxBandwidth_ = settings_->value ("RxBandwidth", 2500).toInt ();
1561   save_directory_.setPath (settings_->value ("SaveDir", default_save_directory_.absolutePath ()).toString ());
1562   azel_directory_.setPath (settings_->value ("AzElDir", default_azel_directory_.absolutePath ()).toString ());
1563 
1564   type_2_msg_gen_ = settings_->value ("Type2MsgGen", QVariant::fromValue (Configuration::type_2_msg_3_full)).value<Configuration::Type2MsgGen> ();
1565 
1566   monitor_off_at_startup_ = settings_->value ("MonitorOFF", false).toBool ();
1567   monitor_last_used_ = settings_->value ("MonitorLastUsed", false).toBool ();
1568   spot_to_psk_reporter_ = settings_->value ("PSKReporter", false).toBool ();
1569   psk_reporter_tcpip_ = settings_->value ("PSKReporterTCPIP", false).toBool ();
1570   id_after_73_ = settings_->value ("After73", false).toBool ();
1571   tx_QSY_allowed_ = settings_->value ("TxQSYAllowed", false).toBool ();
1572   use_dynamic_grid_ = settings_->value ("AutoGrid", false).toBool ();
1573 
1574   macros_.setStringList (settings_->value ("Macros", QStringList {"TNX 73 GL"}).toStringList ());
1575 
1576   region_ = settings_->value ("Region", QVariant::fromValue (IARURegions::ALL)).value<IARURegions::Region> ();
1577 
1578   if (settings_->contains ("FrequenciesForRegionModes"))
1579     {
1580       auto const& v = settings_->value ("FrequenciesForRegionModes");
1581       if (v.isValid ())
1582         {
1583           frequencies_.frequency_list (v.value<FrequencyList_v2::FrequencyItems> ());
1584         }
1585       else
1586         {
1587           frequencies_.reset_to_defaults ();
1588         }
1589     }
1590   else
1591     {
1592       frequencies_.reset_to_defaults ();
1593     }
1594 
1595   stations_.station_list (settings_->value ("stations").value<StationList::Stations> ());
1596 
1597   auto highlight_items = settings_->value ("DecodeHighlighting", QVariant::fromValue (DecodeHighlightingModel::default_items ())).value<DecodeHighlightingModel::HighlightItems> ();
1598   if (!highlight_items.size ()) highlight_items = DecodeHighlightingModel::default_items ();
1599   decode_highlighing_model_.items (highlight_items);
1600   highlight_by_mode_ = settings_->value("HighlightByMode", false).toBool ();
1601   highlight_only_fields_ = settings_->value("OnlyFieldsSought", false).toBool ();
1602   include_WAE_entities_ = settings_->value("IncludeWAEEntities", false).toBool ();
1603   LotW_days_since_upload_ = settings_->value ("LotWDaysSinceLastUpload", 365).toInt ();
1604   lotw_users_.set_age_constraint (LotW_days_since_upload_);
1605 
1606   log_as_RTTY_ = settings_->value ("toRTTY", false).toBool ();
1607   report_in_comments_ = settings_->value("dBtoComments", false).toBool ();
1608   rig_params_.rig_name = settings_->value ("Rig", TransceiverFactory::basic_transceiver_name_).toString ();
1609   rig_is_dummy_ = TransceiverFactory::basic_transceiver_name_ == rig_params_.rig_name;
1610   rig_params_.network_port = settings_->value ("CATNetworkPort").toString ();
1611   rig_params_.usb_port = settings_->value ("CATUSBPort").toString ();
1612   rig_params_.serial_port = settings_->value ("CATSerialPort").toString ();
1613   rig_params_.baud = settings_->value ("CATSerialRate", 4800).toInt ();
1614   rig_params_.data_bits = settings_->value ("CATDataBits", QVariant::fromValue (TransceiverFactory::default_data_bits)).value<TransceiverFactory::DataBits> ();
1615   rig_params_.stop_bits = settings_->value ("CATStopBits", QVariant::fromValue (TransceiverFactory::default_stop_bits)).value<TransceiverFactory::StopBits> ();
1616   rig_params_.handshake = settings_->value ("CATHandshake", QVariant::fromValue (TransceiverFactory::handshake_default)).value<TransceiverFactory::Handshake> ();
1617   rig_params_.force_dtr = settings_->value ("CATForceDTR", false).toBool ();
1618   rig_params_.dtr_high = settings_->value ("DTR", false).toBool ();
1619   rig_params_.force_rts = settings_->value ("CATForceRTS", false).toBool ();
1620   rig_params_.rts_high = settings_->value ("RTS", false).toBool ();
1621   rig_params_.ptt_type = settings_->value ("PTTMethod", QVariant::fromValue (TransceiverFactory::PTT_method_VOX)).value<TransceiverFactory::PTTMethod> ();
1622   rig_params_.audio_source = settings_->value ("TXAudioSource", QVariant::fromValue (TransceiverFactory::TX_audio_source_front)).value<TransceiverFactory::TXAudioSource> ();
1623   rig_params_.ptt_port = settings_->value ("PTTport").toString ();
1624   data_mode_ = settings_->value ("DataMode", QVariant::fromValue (data_mode_none)).value<Configuration::DataMode> ();
1625   bLowSidelobes_ = settings_->value("LowSidelobes",true).toBool();
1626   prompt_to_log_ = settings_->value ("PromptToLog", false).toBool ();
1627   autoLog_ = settings_->value ("AutoLog", false).toBool ();
1628   decodes_from_top_ = settings_->value ("DecodesFromTop", false).toBool ();
1629   insert_blank_ = settings_->value ("InsertBlank", false).toBool ();
1630   DXCC_ = settings_->value ("DXCCEntity", false).toBool ();
1631   ppfx_ = settings_->value ("PrincipalPrefix", false).toBool ();
1632   clear_DX_ = settings_->value ("ClearCallGrid", false).toBool ();
1633   miles_ = settings_->value ("Miles", false).toBool ();
1634   quick_call_ = settings_->value ("QuickCall", false).toBool ();
1635   disable_TX_on_73_ = settings_->value ("73TxDisable", false).toBool ();
1636   force_call_1st_ = settings_->value ("ForceCallFirst", false).toBool ();
1637   alternate_bindings_ = settings_->value ("AlternateBindings", false).toBool ();
1638   watchdog_ = settings_->value ("TxWatchdog", 6).toInt ();
1639   TX_messages_ = settings_->value ("Tx2QSO", true).toBool ();
1640   enable_VHF_features_ = settings_->value("VHFUHF",false).toBool ();
1641   decode_at_52s_ = settings_->value("Decode52",false).toBool ();
1642   single_decode_ = settings_->value("SingleDecode",false).toBool ();
1643   twoPass_ = settings_->value("TwoPass",true).toBool ();
1644   bSpecialOp_ = settings_->value("SpecialOpActivity",false).toBool ();
1645   SelectedActivity_ = settings_->value("SelectedActivity",1).toInt ();
1646   x2ToneSpacing_ = settings_->value("x2ToneSpacing",false).toBool ();
1647   x4ToneSpacing_ = settings_->value("x4ToneSpacing",false).toBool ();
1648   rig_params_.poll_interval = settings_->value ("Polling", 0).toInt ();
1649   rig_params_.split_mode = settings_->value ("SplitMode", QVariant::fromValue (TransceiverFactory::split_mode_none)).value<TransceiverFactory::SplitMode> ();
1650   opCall_ = settings_->value ("OpCall", "").toString ();
1651   udp_server_name_ = settings_->value ("UDPServer", "127.0.0.1").toString ();
1652   udp_interface_names_ = settings_->value ("UDPInterface").toStringList ();
1653   udp_TTL_ = settings_->value ("UDPTTL", 1).toInt ();
1654   udp_server_port_ = settings_->value ("UDPServerPort", 2237).toUInt ();
1655   n1mm_server_name_ = settings_->value ("N1MMServer", "127.0.0.1").toString ();
1656   n1mm_server_port_ = settings_->value ("N1MMServerPort", 2333).toUInt ();
1657   broadcast_to_n1mm_ = settings_->value ("BroadcastToN1MM", false).toBool ();
1658   accept_udp_requests_ = settings_->value ("AcceptUDPRequests", false).toBool ();
1659   udpWindowToFront_ = settings_->value ("udpWindowToFront",false).toBool ();
1660   udpWindowRestore_ = settings_->value ("udpWindowRestore",false).toBool ();
1661   calibration_.intercept = settings_->value ("CalibrationIntercept", 0.).toDouble ();
1662   calibration_.slope_ppm = settings_->value ("CalibrationSlopePPM", 0.).toDouble ();
1663   pwrBandTxMemory_ = settings_->value("pwrBandTxMemory",false).toBool ();
1664   pwrBandTuneMemory_ = settings_->value("pwrBandTuneMemory",false).toBool ();
1665   // Z
1666   qrzComUn_ = settings_->value("qrzComUn").toString();
1667   qrzComPw_ = settings_->value("qrzComPw").toString();
1668   disableWriteALL_ = settings_->value("disableWriteALL").toBool();
1669   disableWriteFoxQSO_ = settings_->value("disableWriteFoxQSO").toBool();
1670   colourAll_ = settings_->value("colourAll").toBool();
1671   autoCQfiltering_ = settings_->value("autoCQfiltering").toBool();
1672   rxTotxFreq_ = settings_->value("rxTotxFreq").toBool();
1673   udpFiltering_ = settings_->value("udpFiltering").toBool();
1674   highlightDX_ = settings_->value("highlightDX").toBool();
1675   dbgScreen_ = settings_->value("dbgScreen").toBool();
1676   dbgBoth_ = settings_->value("dbgBoth").toBool();
1677   dbgFile_ = settings_->value("dbgFile").toBool();
1678   wdResetAnywhere_ = settings_->value("wdResetAnywhere", true).toBool();
1679   wd_FT8_ = settings_->value("wd_FT8",2).toInt ();
1680   wd_FT4_ = settings_->value("wd_FT4",1).toInt ();
1681   wd_Timer_ = settings_->value("wd_Timer", false).toBool();
1682   processTailenders_ = settings_->value("processTailenders", false).toBool();
1683   permIgnoreList_ = settings_->value("permIgnoreList").toString();
1684   showDistance_ = settings_->value("showDistance", false).toBool();
1685   showBearing_ = settings_->value("showBearing", false).toBool();
1686   autoTune_ = settings_->value("autoTune", false).toBool();
1687   showState_ = settings_->value("showState", false).toBool();
1688   rawViewDXCC_ = settings_->value("rawViewDXCC", false).toBool();
1689   clearRx_ = settings_->value("clearRx", false).toBool();
1690   ignoreListReset_ = settings_->value("ignoreListReset",0).toInt ();
1691   separatorColor_ = settings_->value("separatorColor", "#777777").toString();
1692 
1693 }
1694 
find_audio_devices()1695 void Configuration::impl::find_audio_devices ()
1696 {
1697   //
1698   // retrieve audio input device
1699   //
1700   auto saved_name = settings_->value ("SoundInName").toString ();
1701   if (next_audio_input_device_.deviceName () != saved_name || next_audio_input_device_.isNull ())
1702     {
1703       next_audio_input_device_ = find_audio_device (QAudio::AudioInput, ui_->sound_input_combo_box, saved_name);
1704       next_audio_input_channel_ = AudioDevice::fromString (settings_->value ("AudioInputChannel", "Mono").toString ());
1705       update_audio_channels (ui_->sound_input_combo_box, ui_->sound_input_combo_box->currentIndex (), ui_->sound_input_channel_combo_box, false);
1706       ui_->sound_input_channel_combo_box->setCurrentIndex (next_audio_input_channel_);
1707     }
1708 
1709   //
1710   // retrieve audio output device
1711   //
1712   saved_name = settings_->value("SoundOutName").toString();
1713   if (next_audio_output_device_.deviceName () != saved_name || next_audio_output_device_.isNull ())
1714     {
1715       next_audio_output_device_ = find_audio_device (QAudio::AudioOutput, ui_->sound_output_combo_box, saved_name);
1716       next_audio_output_channel_ = AudioDevice::fromString (settings_->value ("AudioOutputChannel", "Mono").toString ());
1717       update_audio_channels (ui_->sound_output_combo_box, ui_->sound_output_combo_box->currentIndex (), ui_->sound_output_channel_combo_box, true);
1718       ui_->sound_output_channel_combo_box->setCurrentIndex (next_audio_output_channel_);
1719     }
1720 }
1721 
write_settings()1722 void Configuration::impl::write_settings ()
1723 {
1724   SettingsGroup g {settings_, "Configuration"};
1725 
1726   settings_->setValue ("MyCall", my_callsign_);
1727   settings_->setValue ("MyGrid", my_grid_);
1728   settings_->setValue ("Field_Day_Exchange", FD_exchange_);
1729   settings_->setValue ("RTTY_Exchange", RTTY_exchange_);
1730   settings_->setValue ("Font", font_.toString ());
1731   settings_->setValue ("DecodedTextFont", decoded_text_font_.toString ());
1732   settings_->setValue ("IDint", id_interval_);
1733   settings_->setValue ("nTrials", ntrials_);
1734   settings_->setValue ("TxDelay", txDelay_);
1735   settings_->setValue ("Aggressive", aggressive_);
1736   settings_->setValue ("RxBandwidth", RxBandwidth_);
1737   settings_->setValue ("PTTMethod", QVariant::fromValue (rig_params_.ptt_type));
1738   settings_->setValue ("PTTport", rig_params_.ptt_port);
1739   settings_->setValue ("SaveDir", save_directory_.absolutePath ());
1740   settings_->setValue ("AzElDir", azel_directory_.absolutePath ());
1741   if (!audio_input_device_.isNull ())
1742     {
1743       settings_->setValue ("SoundInName", audio_input_device_.deviceName ());
1744       settings_->setValue ("AudioInputChannel", AudioDevice::toString (audio_input_channel_));
1745     }
1746   if (!audio_output_device_.isNull ())
1747     {
1748       settings_->setValue ("SoundOutName", audio_output_device_.deviceName ());
1749       settings_->setValue ("AudioOutputChannel", AudioDevice::toString (audio_output_channel_));
1750     }
1751   settings_->setValue ("Type2MsgGen", QVariant::fromValue (type_2_msg_gen_));
1752   settings_->setValue ("MonitorOFF", monitor_off_at_startup_);
1753   settings_->setValue ("MonitorLastUsed", monitor_last_used_);
1754   settings_->setValue ("PSKReporter", spot_to_psk_reporter_);
1755   settings_->setValue ("PSKReporterTCPIP", psk_reporter_tcpip_);
1756   settings_->setValue ("After73", id_after_73_);
1757   settings_->setValue ("TxQSYAllowed", tx_QSY_allowed_);
1758   settings_->setValue ("Macros", macros_.stringList ());
1759   settings_->setValue ("FrequenciesForRegionModes", QVariant::fromValue (frequencies_.frequency_list ()));
1760   settings_->setValue ("stations", QVariant::fromValue (stations_.station_list ()));
1761   settings_->setValue ("DecodeHighlighting", QVariant::fromValue (decode_highlighing_model_.items ()));
1762   settings_->setValue ("HighlightByMode", highlight_by_mode_);
1763   settings_->setValue ("OnlyFieldsSought", highlight_only_fields_);
1764   settings_->setValue ("IncludeWAEEntities", include_WAE_entities_);
1765   settings_->setValue ("LotWDaysSinceLastUpload", LotW_days_since_upload_);
1766   settings_->setValue ("toRTTY", log_as_RTTY_);
1767   settings_->setValue ("dBtoComments", report_in_comments_);
1768   settings_->setValue ("Rig", rig_params_.rig_name);
1769   settings_->setValue ("CATNetworkPort", rig_params_.network_port);
1770   settings_->setValue ("CATUSBPort", rig_params_.usb_port);
1771   settings_->setValue ("CATSerialPort", rig_params_.serial_port);
1772   settings_->setValue ("CATSerialRate", rig_params_.baud);
1773   settings_->setValue ("CATDataBits", QVariant::fromValue (rig_params_.data_bits));
1774   settings_->setValue ("CATStopBits", QVariant::fromValue (rig_params_.stop_bits));
1775   settings_->setValue ("CATHandshake", QVariant::fromValue (rig_params_.handshake));
1776   settings_->setValue ("DataMode", QVariant::fromValue (data_mode_));
1777   settings_->setValue ("LowSidelobes",bLowSidelobes_);
1778   settings_->setValue ("PromptToLog", prompt_to_log_);
1779   settings_->setValue ("AutoLog", autoLog_);
1780   settings_->setValue ("DecodesFromTop", decodes_from_top_);
1781   settings_->setValue ("InsertBlank", insert_blank_);
1782   settings_->setValue ("DXCCEntity", DXCC_);
1783   settings_->setValue ("PrincipalPrefix", ppfx_);
1784   settings_->setValue ("ClearCallGrid", clear_DX_);
1785   settings_->setValue ("Miles", miles_);
1786   settings_->setValue ("QuickCall", quick_call_);
1787   settings_->setValue ("73TxDisable", disable_TX_on_73_);
1788   settings_->setValue ("ForceCallFirst", force_call_1st_);
1789   settings_->setValue ("AlternateBindings", alternate_bindings_);
1790   settings_->setValue ("TxWatchdog", watchdog_);
1791   settings_->setValue ("Tx2QSO", TX_messages_);
1792   settings_->setValue ("CATForceDTR", rig_params_.force_dtr);
1793   settings_->setValue ("DTR", rig_params_.dtr_high);
1794   settings_->setValue ("CATForceRTS", rig_params_.force_rts);
1795   settings_->setValue ("RTS", rig_params_.rts_high);
1796   settings_->setValue ("TXAudioSource", QVariant::fromValue (rig_params_.audio_source));
1797   settings_->setValue ("Polling", rig_params_.poll_interval);
1798   settings_->setValue ("SplitMode", QVariant::fromValue (rig_params_.split_mode));
1799   settings_->setValue ("VHFUHF", enable_VHF_features_);
1800   settings_->setValue ("Decode52", decode_at_52s_);
1801   settings_->setValue ("SingleDecode", single_decode_);
1802   settings_->setValue ("TwoPass", twoPass_);
1803   settings_->setValue ("SelectedActivity", SelectedActivity_);
1804   settings_->setValue ("SpecialOpActivity", bSpecialOp_);
1805   settings_->setValue ("x2ToneSpacing", x2ToneSpacing_);
1806   settings_->setValue ("x4ToneSpacing", x4ToneSpacing_);
1807   settings_->setValue ("OpCall", opCall_);
1808   settings_->setValue ("UDPServer", udp_server_name_);
1809   settings_->setValue ("UDPServerPort", udp_server_port_);
1810   settings_->setValue ("UDPInterface", QVariant::fromValue (udp_interface_names_));
1811   settings_->setValue ("UDPTTL", udp_TTL_);
1812   settings_->setValue ("N1MMServer", n1mm_server_name_);
1813   settings_->setValue ("N1MMServerPort", n1mm_server_port_);
1814   settings_->setValue ("BroadcastToN1MM", broadcast_to_n1mm_);
1815   settings_->setValue ("AcceptUDPRequests", accept_udp_requests_);
1816   settings_->setValue ("udpWindowToFront", udpWindowToFront_);
1817   settings_->setValue ("udpWindowRestore", udpWindowRestore_);
1818   settings_->setValue ("CalibrationIntercept", calibration_.intercept);
1819   settings_->setValue ("CalibrationSlopePPM", calibration_.slope_ppm);
1820   settings_->setValue ("pwrBandTxMemory", pwrBandTxMemory_);
1821   settings_->setValue ("pwrBandTuneMemory", pwrBandTuneMemory_);
1822   settings_->setValue ("Region", QVariant::fromValue (region_));
1823   settings_->setValue ("AutoGrid", use_dynamic_grid_);
1824   settings_->sync ();
1825   // Z
1826   settings_->setValue ("qrzComUn", qrzComUn_);
1827   settings_->setValue ("qrzComPw", qrzComPw_);
1828   settings_->setValue("disableWriteALL", disableWriteALL_);
1829   settings_->setValue("disableWriteFoxQSO", disableWriteFoxQSO_);
1830   settings_->setValue("colourAll", colourAll_);
1831   settings_->setValue("autoCQfiltering", autoCQfiltering_);
1832   settings_->setValue("rxTotxFreq", rxTotxFreq_);
1833   settings_->setValue("udpFiltering", udpFiltering_);
1834   settings_->setValue("highlightDX", highlightDX_);
1835   settings_->setValue("dbgScreen", dbgScreen_);
1836   settings_->setValue("dbgFile", dbgFile_);
1837   settings_->setValue("dbgBoth", dbgBoth_);
1838   settings_->setValue("wdResetAnywhere", wdResetAnywhere_);
1839   settings_->setValue("wd_FT8", wd_FT8_);
1840   settings_->setValue("wd_FT4", wd_FT4_);
1841   settings_->setValue("wd_Timer", wd_Timer_);
1842   settings_->setValue("processTailenders", processTailenders_);
1843   settings_->setValue("permIgnoreList", permIgnoreList_);
1844   settings_->setValue("showDistance", showDistance_);
1845   settings_->setValue("showBearing", showBearing_);
1846   settings_->setValue("autoTune", autoTune_);
1847   settings_->setValue("showState", showState_);
1848   settings_->setValue("rawViewDXCC", rawViewDXCC_);
1849   settings_->setValue("clearRx", clearRx_);
1850   settings_->setValue("ignoreListReset", ignoreListReset_);
1851   settings_->setValue("separatorColor", separatorColor_);
1852 }
1853 
set_rig_invariants()1854 void Configuration::impl::set_rig_invariants ()
1855 {
1856   auto const& rig = ui_->rig_combo_box->currentText ();
1857   auto const& ptt_port = ui_->PTT_port_combo_box->currentText ();
1858   auto ptt_method = static_cast<TransceiverFactory::PTTMethod> (ui_->PTT_method_button_group->checkedId ());
1859 
1860   auto CAT_PTT_enabled = transceiver_factory_.has_CAT_PTT (rig);
1861   auto CAT_indirect_serial_PTT = transceiver_factory_.has_CAT_indirect_serial_PTT (rig);
1862   auto asynchronous_CAT = transceiver_factory_.has_asynchronous_CAT (rig);
1863   auto is_hw_handshake = ui_->CAT_handshake_group_box->isEnabled ()
1864     && TransceiverFactory::handshake_hardware == static_cast<TransceiverFactory::Handshake> (ui_->CAT_handshake_button_group->checkedId ());
1865 
1866   ui_->test_CAT_push_button->setStyleSheet ({});
1867 
1868   ui_->CAT_poll_interval_label->setEnabled (!asynchronous_CAT);
1869   ui_->CAT_poll_interval_spin_box->setEnabled (!asynchronous_CAT);
1870 
1871   auto port_type = transceiver_factory_.CAT_port_type (rig);
1872 
1873   bool is_serial_CAT (TransceiverFactory::Capabilities::serial == port_type);
1874   auto const& cat_port = ui_->CAT_port_combo_box->currentText ();
1875 
1876   // only enable CAT option if transceiver has CAT PTT
1877   ui_->PTT_CAT_radio_button->setEnabled (CAT_PTT_enabled);
1878 
1879   auto enable_ptt_port = TransceiverFactory::PTT_method_CAT != ptt_method && TransceiverFactory::PTT_method_VOX != ptt_method;
1880   ui_->PTT_port_combo_box->setEnabled (enable_ptt_port);
1881   ui_->PTT_port_label->setEnabled (enable_ptt_port);
1882 
1883   if (CAT_indirect_serial_PTT)
1884     {
1885       ui_->PTT_port_combo_box->setItemData (ui_->PTT_port_combo_box->findText ("CAT")
1886                                             , combo_box_item_enabled, Qt::UserRole - 1);
1887     }
1888   else
1889     {
1890       ui_->PTT_port_combo_box->setItemData (ui_->PTT_port_combo_box->findText ("CAT")
1891                                             , combo_box_item_disabled, Qt::UserRole - 1);
1892       if ("CAT" == ui_->PTT_port_combo_box->currentText () && ui_->PTT_port_combo_box->currentIndex () > 0)
1893         {
1894           ui_->PTT_port_combo_box->setCurrentIndex (ui_->PTT_port_combo_box->currentIndex () - 1);
1895         }
1896     }
1897   ui_->PTT_RTS_radio_button->setEnabled (!(is_serial_CAT && ptt_port == cat_port && is_hw_handshake));
1898 
1899   if (TransceiverFactory::basic_transceiver_name_ == rig)
1900     {
1901       // makes no sense with rig as "None"
1902       ui_->monitor_last_used_check_box->setEnabled (false);
1903 
1904       ui_->CAT_control_group_box->setEnabled (false);
1905       ui_->test_CAT_push_button->setEnabled (false);
1906       ui_->test_PTT_push_button->setEnabled (TransceiverFactory::PTT_method_DTR == ptt_method
1907                                              || TransceiverFactory::PTT_method_RTS == ptt_method);
1908       ui_->TX_audio_source_group_box->setEnabled (false);
1909     }
1910   else
1911     {
1912       ui_->monitor_last_used_check_box->setEnabled (true);
1913       ui_->CAT_control_group_box->setEnabled (true);
1914       ui_->test_CAT_push_button->setEnabled (true);
1915       ui_->test_PTT_push_button->setEnabled (false);
1916       ui_->TX_audio_source_group_box->setEnabled (transceiver_factory_.has_CAT_PTT_mic_data (rig) && TransceiverFactory::PTT_method_CAT == ptt_method);
1917       if (port_type != last_port_type_)
1918         {
1919           last_port_type_ = port_type;
1920           switch (port_type)
1921             {
1922             case TransceiverFactory::Capabilities::serial:
1923               fill_port_combo_box (ui_->CAT_port_combo_box);
1924               ui_->CAT_port_combo_box->setCurrentText (rig_params_.serial_port);
1925               if (ui_->CAT_port_combo_box->currentText ().isEmpty () && ui_->CAT_port_combo_box->count ())
1926                 {
1927                   ui_->CAT_port_combo_box->setCurrentText (ui_->CAT_port_combo_box->itemText (0));
1928                 }
1929               ui_->CAT_port_label->setText (tr ("Serial Port:"));
1930               ui_->CAT_port_combo_box->setToolTip (tr ("Serial port used for CAT control"));
1931               ui_->CAT_port_combo_box->setEnabled (true);
1932               break;
1933 
1934             case TransceiverFactory::Capabilities::network:
1935               ui_->CAT_port_combo_box->clear ();
1936               ui_->CAT_port_combo_box->setCurrentText (rig_params_.network_port);
1937               ui_->CAT_port_label->setText (tr ("Network Server:"));
1938               ui_->CAT_port_combo_box->setToolTip (tr ("Optional hostname and port of network service.\n"
1939                                                        "Leave blank for a sensible default on this machine.\n"
1940                                                        "Formats:\n"
1941                                                        "\thostname:port\n"
1942                                                        "\tIPv4-address:port\n"
1943                                                        "\t[IPv6-address]:port"));
1944               ui_->CAT_port_combo_box->setEnabled (true);
1945               break;
1946 
1947             case TransceiverFactory::Capabilities::usb:
1948               ui_->CAT_port_combo_box->clear ();
1949               ui_->CAT_port_combo_box->setCurrentText (rig_params_.usb_port);
1950               ui_->CAT_port_label->setText (tr ("USB Device:"));
1951               ui_->CAT_port_combo_box->setToolTip (tr ("Optional device identification.\n"
1952                                                        "Leave blank for a sensible default for the rig.\n"
1953                                                        "Format:\n"
1954                                                        "\t[VID[:PID[:VENDOR[:PRODUCT]]]]"));
1955               ui_->CAT_port_combo_box->setEnabled (true);
1956               break;
1957 
1958             default:
1959               ui_->CAT_port_combo_box->clear ();
1960               ui_->CAT_port_combo_box->setEnabled (false);
1961               break;
1962             }
1963         }
1964       ui_->CAT_serial_port_parameters_group_box->setEnabled (is_serial_CAT);
1965       ui_->force_DTR_combo_box->setEnabled (is_serial_CAT
1966                                             && (cat_port != ptt_port
1967                                                 || !ui_->PTT_DTR_radio_button->isEnabled ()
1968                                                 || !ui_->PTT_DTR_radio_button->isChecked ()));
1969       ui_->force_RTS_combo_box->setEnabled (is_serial_CAT
1970                                             && !is_hw_handshake
1971                                             && (cat_port != ptt_port
1972                                                 || !ui_->PTT_RTS_radio_button->isEnabled ()
1973                                                 || !ui_->PTT_RTS_radio_button->isChecked ()));
1974     }
1975   ui_->mode_group_box->setEnabled (WSJT_RIG_NONE_CAN_SPLIT
1976                                    || TransceiverFactory::basic_transceiver_name_ != rig);
1977   ui_->split_operation_group_box->setEnabled (WSJT_RIG_NONE_CAN_SPLIT
1978                                               || TransceiverFactory::basic_transceiver_name_ != rig);
1979 }
1980 
validate()1981 bool Configuration::impl::validate ()
1982 {
1983   if (ui_->sound_input_combo_box->currentIndex () < 0
1984       && next_audio_input_device_.isNull ())
1985     {
1986       find_tab (ui_->sound_input_combo_box);
1987       MessageBox::critical_message (this, tr ("Invalid audio input device"));
1988       return false;
1989     }
1990 
1991   if (ui_->sound_input_channel_combo_box->currentIndex () < 0
1992       && next_audio_input_device_.isNull ())
1993     {
1994       find_tab (ui_->sound_input_combo_box);
1995       MessageBox::critical_message (this, tr ("Invalid audio input device"));
1996       return false;
1997     }
1998 
1999   if (ui_->sound_output_combo_box->currentIndex () < 0
2000       && next_audio_output_device_.isNull ())
2001     {
2002       find_tab (ui_->sound_output_combo_box);
2003       MessageBox::information_message (this, tr ("Invalid audio output device"));
2004       // don't reject as we can work without an audio output
2005     }
2006 
2007   if (!ui_->PTT_method_button_group->checkedButton ()->isEnabled ())
2008     {
2009       MessageBox::critical_message (this, tr ("Invalid PTT method"));
2010       return false;
2011     }
2012 
2013   auto ptt_method = static_cast<TransceiverFactory::PTTMethod> (ui_->PTT_method_button_group->checkedId ());
2014   auto ptt_port = ui_->PTT_port_combo_box->currentText ();
2015   if ((TransceiverFactory::PTT_method_DTR == ptt_method || TransceiverFactory::PTT_method_RTS == ptt_method)
2016       && (ptt_port.isEmpty ()
2017           || combo_box_item_disabled == ui_->PTT_port_combo_box->itemData (ui_->PTT_port_combo_box->findText (ptt_port), Qt::UserRole - 1)))
2018     {
2019       MessageBox::critical_message (this, tr ("Invalid PTT port"));
2020       return false;
2021     }
2022 
2023   if (ui_->rbField_Day->isEnabled () && ui_->rbField_Day->isChecked () &&
2024       !ui_->Field_Day_Exchange->hasAcceptableInput ())
2025     {
2026       find_tab (ui_->Field_Day_Exchange);
2027       MessageBox::critical_message (this, tr ("Invalid Contest Exchange")
2028                                     , tr ("You must input a valid ARRL Field Day exchange"));
2029       return false;
2030     }
2031 
2032   if (ui_->rbRTTY_Roundup->isEnabled () && ui_->rbRTTY_Roundup->isChecked () &&
2033       !ui_->RTTY_Exchange->hasAcceptableInput ())
2034     {
2035       find_tab (ui_->RTTY_Exchange);
2036       MessageBox::critical_message (this, tr ("Invalid Contest Exchange")
2037                                     , tr ("You must input a valid ARRL RTTY Roundup exchange"));
2038       return false;
2039     }
2040 
2041   if (dns_lookup_id_ > -1)
2042     {
2043       MessageBox::information_message (this, tr ("Pending DNS lookup, please try again later"));
2044       return false;
2045     }
2046 
2047   return true;
2048 }
2049 
exec()2050 int Configuration::impl::exec ()
2051 {
2052   // macros can be modified in the main window
2053   next_macros_.setStringList (macros_.stringList ());
2054 
2055   have_rig_ = rig_active_;	// record that we started with a rig open
2056   saved_rig_params_ = rig_params_; // used to detect changes that
2057                                    // require the Transceiver to be
2058                                    // re-opened
2059   rig_changed_ = false;
2060 
2061   initialize_models ();
2062 
2063   return QDialog::exec();
2064 }
2065 
gather_rig_data()2066 TransceiverFactory::ParameterPack Configuration::impl::gather_rig_data ()
2067 {
2068   TransceiverFactory::ParameterPack result;
2069   result.rig_name = ui_->rig_combo_box->currentText ();
2070 
2071   switch (transceiver_factory_.CAT_port_type (result.rig_name))
2072     {
2073     case TransceiverFactory::Capabilities::network:
2074       result.network_port = ui_->CAT_port_combo_box->currentText ();
2075       result.usb_port = rig_params_.usb_port;
2076       result.serial_port = rig_params_.serial_port;
2077       break;
2078 
2079     case TransceiverFactory::Capabilities::usb:
2080       result.usb_port = ui_->CAT_port_combo_box->currentText ();
2081       result.network_port = rig_params_.network_port;
2082       result.serial_port = rig_params_.serial_port;
2083       break;
2084 
2085     default:
2086       result.serial_port = ui_->CAT_port_combo_box->currentText ();
2087       result.network_port = rig_params_.network_port;
2088       result.usb_port = rig_params_.usb_port;
2089       break;
2090     }
2091 
2092   result.baud = ui_->CAT_serial_baud_combo_box->currentText ().toInt ();
2093   result.data_bits = static_cast<TransceiverFactory::DataBits> (ui_->CAT_data_bits_button_group->checkedId ());
2094   result.stop_bits = static_cast<TransceiverFactory::StopBits> (ui_->CAT_stop_bits_button_group->checkedId ());
2095   result.handshake = static_cast<TransceiverFactory::Handshake> (ui_->CAT_handshake_button_group->checkedId ());
2096   result.force_dtr = ui_->force_DTR_combo_box->isEnabled () && ui_->force_DTR_combo_box->currentIndex () > 0;
2097   result.dtr_high = ui_->force_DTR_combo_box->isEnabled () && 1 == ui_->force_DTR_combo_box->currentIndex ();
2098   result.force_rts = ui_->force_RTS_combo_box->isEnabled () && ui_->force_RTS_combo_box->currentIndex () > 0;
2099   result.rts_high = ui_->force_RTS_combo_box->isEnabled () && 1 == ui_->force_RTS_combo_box->currentIndex ();
2100   result.poll_interval = ui_->CAT_poll_interval_spin_box->value ();
2101   result.ptt_type = static_cast<TransceiverFactory::PTTMethod> (ui_->PTT_method_button_group->checkedId ());
2102   result.ptt_port = ui_->PTT_port_combo_box->currentText ();
2103   result.audio_source = static_cast<TransceiverFactory::TXAudioSource> (ui_->TX_audio_source_button_group->checkedId ());
2104   result.split_mode = static_cast<TransceiverFactory::SplitMode> (ui_->split_mode_button_group->checkedId ());
2105   return result;
2106 }
2107 
accept()2108 void Configuration::impl::accept ()
2109 {
2110   // Called when OK button is clicked.
2111 
2112   if (!validate ())
2113     {
2114       return;			// not accepting
2115     }
2116 
2117   // extract all rig related configuration parameters into temporary
2118   // structure for checking if the rig needs re-opening without
2119   // actually updating our live state
2120   auto temp_rig_params = gather_rig_data ();
2121 
2122   // open_rig() uses values from models so we use it to validate the
2123   // Transceiver settings before agreeing to accept the configuration
2124   if (temp_rig_params != rig_params_ && !open_rig ())
2125     {
2126       return;			// not accepting
2127     }
2128 
2129   QDialog::accept();            // do this before accessing custom
2130                                 // models so that any changes in
2131                                 // delegates in views get flushed to
2132                                 // the underlying models before we
2133                                 // access them
2134 
2135   sync_transceiver (true);	// force an update
2136 
2137   //
2138   // from here on we are bound to accept the new configuration
2139   // parameters so extract values from models and make them live
2140   //
2141 
2142   if (next_font_ != font_)
2143     {
2144       font_ = next_font_;
2145       Q_EMIT self_->text_font_changed (font_);
2146     }
2147 
2148   if (next_decoded_text_font_ != decoded_text_font_)
2149     {
2150       decoded_text_font_ = next_decoded_text_font_;
2151       next_decode_highlighing_model_.set_font (decoded_text_font_);
2152       ui_->highlighting_list_view->reset ();
2153       Q_EMIT self_->decoded_text_font_changed (decoded_text_font_);
2154     }
2155 
2156   rig_params_ = temp_rig_params; // now we can go live with the rig
2157                                  // related configuration parameters
2158   rig_is_dummy_ = TransceiverFactory::basic_transceiver_name_ == rig_params_.rig_name;
2159 
2160   {
2161     auto const& selected_device = ui_->sound_input_combo_box->currentData ().value<audio_info_type> ().first;
2162     if (selected_device != next_audio_input_device_)
2163       {
2164         next_audio_input_device_ = selected_device;
2165       }
2166   }
2167 
2168   {
2169     auto const& selected_device = ui_->sound_output_combo_box->currentData ().value<audio_info_type> ().first;
2170     if (selected_device != next_audio_output_device_)
2171       {
2172         next_audio_output_device_ = selected_device;
2173       }
2174   }
2175 
2176   if (next_audio_input_channel_ != static_cast<AudioDevice::Channel> (ui_->sound_input_channel_combo_box->currentIndex ()))
2177     {
2178       next_audio_input_channel_ = static_cast<AudioDevice::Channel> (ui_->sound_input_channel_combo_box->currentIndex ());
2179     }
2180   Q_ASSERT (next_audio_input_channel_ <= AudioDevice::Right);
2181 
2182   if (next_audio_output_channel_ != static_cast<AudioDevice::Channel> (ui_->sound_output_channel_combo_box->currentIndex ()))
2183     {
2184       next_audio_output_channel_ = static_cast<AudioDevice::Channel> (ui_->sound_output_channel_combo_box->currentIndex ());
2185     }
2186   Q_ASSERT (next_audio_output_channel_ <= AudioDevice::Both);
2187 
2188   if (audio_input_device_ != next_audio_input_device_ || next_audio_input_device_.isNull ())
2189     {
2190       audio_input_device_ = next_audio_input_device_;
2191       restart_sound_input_device_ = true;
2192     }
2193   if (audio_input_channel_ != next_audio_input_channel_)
2194     {
2195       audio_input_channel_ = next_audio_input_channel_;
2196       restart_sound_input_device_ = true;
2197     }
2198   if (audio_output_device_ != next_audio_output_device_ || next_audio_output_device_.isNull ())
2199     {
2200       audio_output_device_ = next_audio_output_device_;
2201       restart_sound_output_device_ = true;
2202     }
2203   if (audio_output_channel_ != next_audio_output_channel_)
2204     {
2205       audio_output_channel_ = next_audio_output_channel_;
2206       restart_sound_output_device_ = true;
2207     }
2208   // qDebug () << "Configure::accept: audio i/p:" << audio_input_device_.deviceName ()
2209   //           << "chan:" << audio_input_channel_
2210   //           << "o/p:" << audio_output_device_.deviceName ()
2211   //           << "chan:" << audio_output_channel_
2212   //           << "reset i/p:" << restart_sound_input_device_
2213   //           << "reset o/p:" << restart_sound_output_device_;
2214 
2215   my_callsign_ = ui_->callsign_line_edit->text ();
2216   my_grid_ = ui_->grid_line_edit->text ();
2217   FD_exchange_= ui_->Field_Day_Exchange->text ().toUpper ();
2218   RTTY_exchange_= ui_->RTTY_Exchange->text ().toUpper ();
2219   spot_to_psk_reporter_ = ui_->psk_reporter_check_box->isChecked ();
2220   psk_reporter_tcpip_ = ui_->psk_reporter_tcpip_check_box->isChecked ();
2221   id_interval_ = ui_->CW_id_interval_spin_box->value ();
2222   ntrials_ = ui_->sbNtrials->value ();
2223   txDelay_ = ui_->sbTxDelay->value ();
2224   aggressive_ = ui_->sbAggressive->value ();
2225   degrade_ = ui_->sbDegrade->value ();
2226   RxBandwidth_ = ui_->sbBandwidth->value ();
2227   id_after_73_ = ui_->CW_id_after_73_check_box->isChecked ();
2228   tx_QSY_allowed_ = ui_->tx_QSY_check_box->isChecked ();
2229   monitor_off_at_startup_ = ui_->monitor_off_check_box->isChecked ();
2230   monitor_last_used_ = ui_->monitor_last_used_check_box->isChecked ();
2231   type_2_msg_gen_ = static_cast<Type2MsgGen> (ui_->type_2_msg_gen_combo_box->currentIndex ());
2232   log_as_RTTY_ = ui_->log_as_RTTY_check_box->isChecked ();
2233   report_in_comments_ = ui_->report_in_comments_check_box->isChecked ();
2234   prompt_to_log_ = ui_->prompt_to_log_check_box->isChecked ();
2235   autoLog_ = ui_->cbAutoLog->isChecked();
2236   decodes_from_top_ = ui_->decodes_from_top_check_box->isChecked ();
2237   insert_blank_ = ui_->insert_blank_check_box->isChecked ();
2238   DXCC_ = ui_->DXCC_check_box->isChecked ();
2239   ppfx_ = ui_->ppfx_check_box->isChecked ();
2240   clear_DX_ = ui_->clear_DX_check_box->isChecked ();
2241   miles_ = ui_->miles_check_box->isChecked ();
2242   quick_call_ = ui_->quick_call_check_box->isChecked ();
2243   disable_TX_on_73_ = ui_->disable_TX_on_73_check_box->isChecked ();
2244   force_call_1st_ = ui_->force_call_1st_check_box->isChecked ();
2245   alternate_bindings_ = ui_->alternate_bindings_check_box->isChecked ();
2246   watchdog_ = ui_->tx_watchdog_spin_box->value ();
2247   TX_messages_ = ui_->TX_messages_check_box->isChecked ();
2248   data_mode_ = static_cast<DataMode> (ui_->TX_mode_button_group->checkedId ());
2249   bLowSidelobes_ = ui_->rbLowSidelobes->isChecked();
2250   save_directory_.setPath (ui_->save_path_display_label->text ());
2251   azel_directory_.setPath (ui_->azel_path_display_label->text ());
2252   enable_VHF_features_ = ui_->enable_VHF_features_check_box->isChecked ();
2253   decode_at_52s_ = ui_->decode_at_52s_check_box->isChecked ();
2254   single_decode_ = ui_->single_decode_check_box->isChecked ();
2255   twoPass_ = ui_->cbTwoPass->isChecked ();
2256   bSpecialOp_ = ui_->gbSpecialOpActivity->isChecked ();
2257   SelectedActivity_ = ui_->special_op_activity_button_group->checkedId();
2258   x2ToneSpacing_ = ui_->cbx2ToneSpacing->isChecked ();
2259   x4ToneSpacing_ = ui_->cbx4ToneSpacing->isChecked ();
2260   calibration_.intercept = ui_->calibration_intercept_spin_box->value ();
2261   calibration_.slope_ppm = ui_->calibration_slope_ppm_spin_box->value ();
2262   pwrBandTxMemory_ = ui_->checkBoxPwrBandTxMemory->isChecked ();
2263   pwrBandTuneMemory_ = ui_->checkBoxPwrBandTuneMemory->isChecked ();
2264   opCall_=ui_->opCallEntry->text();
2265 
2266   auto new_server = ui_->udp_server_line_edit->text ().trimmed ();
2267   auto new_interfaces = get_selected_network_interfaces (ui_->udp_interfaces_combo_box);
2268   if (new_server != udp_server_name_ || new_interfaces != udp_interface_names_)
2269     {
2270       udp_server_name_ = new_server;
2271       udp_interface_names_ = new_interfaces;
2272       Q_EMIT self_->udp_server_changed (udp_server_name_, udp_interface_names_);
2273     }
2274 
2275   auto new_port = ui_->udp_server_port_spin_box->value ();
2276   if (new_port != udp_server_port_)
2277     {
2278       udp_server_port_ = new_port;
2279       Q_EMIT self_->udp_server_port_changed (udp_server_port_);
2280     }
2281 
2282   auto new_TTL = ui_->udp_TTL_spin_box->value ();
2283   if (new_TTL != udp_TTL_)
2284     {
2285       udp_TTL_ = new_TTL;
2286       Q_EMIT self_->udp_TTL_changed (udp_TTL_);
2287     }
2288 
2289   if (ui_->accept_udp_requests_check_box->isChecked () != accept_udp_requests_)
2290     {
2291       accept_udp_requests_ = ui_->accept_udp_requests_check_box->isChecked ();
2292       Q_EMIT self_->accept_udp_requests_changed (accept_udp_requests_);
2293     }
2294 
2295   n1mm_server_name_ = ui_->n1mm_server_name_line_edit->text ();
2296   n1mm_server_port_ = ui_->n1mm_server_port_spin_box->value ();
2297   broadcast_to_n1mm_ = ui_->enable_n1mm_broadcast_check_box->isChecked ();
2298 
2299   udpWindowToFront_ = ui_->udpWindowToFront->isChecked ();
2300   udpWindowRestore_ = ui_->udpWindowRestore->isChecked ();
2301 
2302   if (macros_.stringList () != next_macros_.stringList ())
2303     {
2304       macros_.setStringList (next_macros_.stringList ());
2305     }
2306 
2307   region_ = IARURegions::value (ui_->region_combo_box->currentText ());
2308 
2309   if (frequencies_.frequency_list () != next_frequencies_.frequency_list ())
2310     {
2311       frequencies_.frequency_list (next_frequencies_.frequency_list ());
2312       frequencies_.sort (FrequencyList_v2::frequency_column);
2313     }
2314 
2315   if (stations_.station_list () != next_stations_.station_list ())
2316     {
2317       stations_.station_list (next_stations_.station_list ());
2318       stations_.sort (StationList::band_column);
2319     }
2320 
2321   if (decode_highlighing_model_.items () != next_decode_highlighing_model_.items ())
2322     {
2323       decode_highlighing_model_.items (next_decode_highlighing_model_.items ());
2324       Q_EMIT self_->decode_highlighting_changed (decode_highlighing_model_);
2325     }
2326   highlight_by_mode_ = ui_->highlight_by_mode_check_box->isChecked ();
2327   highlight_only_fields_ = ui_->only_fields_check_box->isChecked ();
2328   include_WAE_entities_ = ui_->include_WAE_check_box->isChecked ();
2329   LotW_days_since_upload_ = ui_->LotW_days_since_upload_spin_box->value ();
2330   lotw_users_.set_age_constraint (LotW_days_since_upload_);
2331 
2332   if (ui_->use_dynamic_grid->isChecked() && !use_dynamic_grid_ )
2333   {
2334     // turning on so clear it so only the next location update gets used
2335     dynamic_grid_.clear ();
2336   }
2337   use_dynamic_grid_ = ui_->use_dynamic_grid->isChecked();
2338 
2339   // Z
2340   if (qrzComPw_ != ui_->le_qrzComPw->text() || qrzComUn_ != ui_->le_qrzComUn->text()) {
2341     qrzComPw_ = ui_->le_qrzComPw->text();
2342     qrzComUn_ = ui_->le_qrzComUn->text();
2343     Q_EMIT self_->qrz_config_changed();
2344   }
2345 
2346   disableWriteALL_ = ui_->cb_disableWriteALL->isChecked();
2347   disableWriteFoxQSO_ = ui_->cb_disableWriteFoxQSO->isChecked();
2348   colourAll_ = ui_->cb_colourAll->isChecked();
2349   rxTotxFreq_ = ui_->cb_rxTotxFreq->isChecked();
2350   autoCQfiltering_ = ui_->cb_autoCQfiltering->isChecked();
2351   udpFiltering_ = ui_->cb_udpFiltering->isChecked();
2352   highlightDX_ = ui_->cb_highlightDX->isChecked();
2353   dbgScreen_ = ui_->rb_dbg_Screen->isChecked();
2354   dbgFile_ = ui_->rb_dbg_File->isChecked();
2355   dbgBoth_ = ui_->rb_dbg_Both->isChecked();
2356   wdResetAnywhere_ = ui_->cb_WD_resetAnywhere->isChecked();
2357   wd_FT4_ = ui_->sb_WD_FT4->value();
2358   wd_FT8_ = ui_->sb_WD_FT8->value();
2359   wd_Timer_ = ui_->gb_WD_Timer->isChecked();
2360   processTailenders_ = ui_->cb_processTailenders->isChecked();
2361   permIgnoreList_ = ui_->te_permIgnoreList->toPlainText();
2362   showDistance_ = ui_->cb_showDistance->isChecked();
2363   showBearing_ = ui_->cb_showBearing->isChecked();
2364   autoTune_ = ui_->cb_autoTune->isChecked();
2365   showState_ = ui_->cb_showState->isChecked();
2366   rawViewDXCC_ = ui_->cb_rawViewDXCC->isChecked();
2367   clearRx_ = ui_->cb_clearRx->isChecked();
2368   ignoreListReset_ = ui_->sb_ignoreListReset->value();
2369   separatorColor_ = ui_->le_separatorColor->text();
2370 
2371   write_settings ();		// make visible to all
2372 }
2373 
reject()2374 void Configuration::impl::reject ()
2375 {
2376   if (dns_lookup_id_ > -1)
2377     {
2378       QHostInfo::abortHostLookup (dns_lookup_id_);
2379       dns_lookup_id_ = -1;
2380     }
2381 
2382   initialize_models ();		// reverts to settings as at exec ()
2383 
2384   // check if the Transceiver instance changed, in which case we need
2385   // to re open any prior Transceiver type
2386   if (rig_changed_)
2387     {
2388       if (have_rig_)
2389         {
2390           // we have to do this since the rig has been opened since we
2391           // were exec'ed even though it might fail
2392           open_rig ();
2393         }
2394       else
2395         {
2396           close_rig ();
2397         }
2398     }
2399 
2400   // qDebug () << "Configure::reject: audio i/p:" << audio_input_device_.deviceName ()
2401   //           << "chan:" << audio_input_channel_
2402   //           << "o/p:" << audio_output_device_.deviceName ()
2403   //           << "chan:" << audio_output_channel_
2404   //           << "reset i/p:" << restart_sound_input_device_
2405   //           << "reset o/p:" << restart_sound_output_device_;
2406 
2407   QDialog::reject ();
2408 }
2409 
on_font_push_button_clicked()2410 void Configuration::impl::on_font_push_button_clicked ()
2411 {
2412   next_font_ = QFontDialog::getFont (0, next_font_, this);
2413 }
2414 
on_reset_highlighting_to_defaults_push_button_clicked(bool)2415 void Configuration::impl::on_reset_highlighting_to_defaults_push_button_clicked (bool /*checked*/)
2416 {
2417   if (MessageBox::Yes == MessageBox::query_message (this
2418                                                     , tr ("Reset Decode Highlighting")
2419                                                     , tr ("Reset all decode highlighting and priorities to default values")))
2420     {
2421       next_decode_highlighing_model_.items (DecodeHighlightingModel::default_items ());
2422     }
2423 }
2424 
on_rescan_log_push_button_clicked(bool)2425 void Configuration::impl::on_rescan_log_push_button_clicked (bool /*clicked*/)
2426 {
2427   if (logbook_) logbook_->rescan ();
2428 }
2429 
on_LotW_CSV_fetch_push_button_clicked(bool)2430 void Configuration::impl::on_LotW_CSV_fetch_push_button_clicked (bool /*checked*/)
2431 {
2432   lotw_users_.load (ui_->LotW_CSV_URL_line_edit->text (), true, true);
2433   ui_->LotW_CSV_fetch_push_button->setEnabled (false);
2434 }
2435 
on_decoded_text_font_push_button_clicked()2436 void Configuration::impl::on_decoded_text_font_push_button_clicked ()
2437 {
2438   next_decoded_text_font_ = QFontDialog::getFont (0, decoded_text_font_ , this
2439                                                   , tr ("WSJT-X Decoded Text Font Chooser")
2440                                                   , QFontDialog::MonospacedFonts
2441                                                   );
2442 }
2443 
on_PTT_port_combo_box_activated(int)2444 void Configuration::impl::on_PTT_port_combo_box_activated (int /* index */)
2445 {
2446   set_rig_invariants ();
2447 }
2448 
on_CAT_port_combo_box_activated(int)2449 void Configuration::impl::on_CAT_port_combo_box_activated (int /* index */)
2450 {
2451   set_rig_invariants ();
2452 }
2453 
on_CAT_serial_baud_combo_box_currentIndexChanged(int)2454 void Configuration::impl::on_CAT_serial_baud_combo_box_currentIndexChanged (int /* index */)
2455 {
2456   set_rig_invariants ();
2457 }
2458 
on_CAT_handshake_button_group_buttonClicked(int)2459 void Configuration::impl::on_CAT_handshake_button_group_buttonClicked (int /* id */)
2460 {
2461   set_rig_invariants ();
2462 }
2463 
on_rig_combo_box_currentIndexChanged(int)2464 void Configuration::impl::on_rig_combo_box_currentIndexChanged (int /* index */)
2465 {
2466   set_rig_invariants ();
2467 }
2468 
on_CAT_data_bits_button_group_buttonClicked(int)2469 void Configuration::impl::on_CAT_data_bits_button_group_buttonClicked (int /* id */)
2470 {
2471   set_rig_invariants ();
2472 }
2473 
on_CAT_stop_bits_button_group_buttonClicked(int)2474 void Configuration::impl::on_CAT_stop_bits_button_group_buttonClicked (int /* id */)
2475 {
2476   set_rig_invariants ();
2477 }
2478 
on_CAT_poll_interval_spin_box_valueChanged(int)2479 void Configuration::impl::on_CAT_poll_interval_spin_box_valueChanged (int /* value */)
2480 {
2481   set_rig_invariants ();
2482 }
2483 
on_split_mode_button_group_buttonClicked(int)2484 void Configuration::impl::on_split_mode_button_group_buttonClicked (int /* id */)
2485 {
2486   set_rig_invariants ();
2487 }
2488 
on_test_CAT_push_button_clicked()2489 void Configuration::impl::on_test_CAT_push_button_clicked ()
2490 {
2491   if (!validate ())
2492     {
2493       return;
2494     }
2495 
2496   ui_->test_CAT_push_button->setStyleSheet ({});
2497   if (open_rig (true))
2498     {
2499       //Q_EMIT sync (true);
2500     }
2501 
2502   set_rig_invariants ();
2503 }
2504 
on_test_PTT_push_button_clicked(bool checked)2505 void Configuration::impl::on_test_PTT_push_button_clicked (bool checked)
2506 {
2507   ui_->test_PTT_push_button->setChecked (!checked); // let status
2508                                                     // update check us
2509   if (!validate ())
2510     {
2511       return;
2512     }
2513 
2514   if (open_rig ())
2515     {
2516       Q_EMIT self_->transceiver_ptt (checked);
2517     }
2518 }
2519 
on_force_DTR_combo_box_currentIndexChanged(int)2520 void Configuration::impl::on_force_DTR_combo_box_currentIndexChanged (int /* index */)
2521 {
2522   set_rig_invariants ();
2523 }
2524 
on_force_RTS_combo_box_currentIndexChanged(int)2525 void Configuration::impl::on_force_RTS_combo_box_currentIndexChanged (int /* index */)
2526 {
2527   set_rig_invariants ();
2528 }
2529 
on_PTT_method_button_group_buttonClicked(int)2530 void Configuration::impl::on_PTT_method_button_group_buttonClicked (int /* id */)
2531 {
2532   set_rig_invariants ();
2533 }
2534 
on_add_macro_line_edit_editingFinished()2535 void Configuration::impl::on_add_macro_line_edit_editingFinished ()
2536 {
2537   ui_->add_macro_line_edit->setText (ui_->add_macro_line_edit->text ().toUpper ());
2538 }
2539 
on_delete_macro_push_button_clicked(bool)2540 void Configuration::impl::on_delete_macro_push_button_clicked (bool /* checked */)
2541 {
2542   auto selection_model = ui_->macros_list_view->selectionModel ();
2543   if (selection_model->hasSelection ())
2544     {
2545       // delete all selected items
2546       delete_selected_macros (selection_model->selectedRows ());
2547     }
2548 }
2549 
delete_macro()2550 void Configuration::impl::delete_macro ()
2551 {
2552   auto selection_model = ui_->macros_list_view->selectionModel ();
2553   if (!selection_model->hasSelection ())
2554     {
2555       // delete item under cursor if any
2556       auto index = selection_model->currentIndex ();
2557       if (index.isValid ())
2558         {
2559           next_macros_.removeRow (index.row ());
2560         }
2561     }
2562   else
2563     {
2564       // delete the whole selection
2565       delete_selected_macros (selection_model->selectedRows ());
2566     }
2567 }
2568 
delete_selected_macros(QModelIndexList selected_rows)2569 void Configuration::impl::delete_selected_macros (QModelIndexList selected_rows)
2570 {
2571   // sort in reverse row order so that we can delete without changing
2572   // indices underneath us
2573   std::sort (selected_rows.begin (), selected_rows.end (), [] (QModelIndex const& lhs, QModelIndex const& rhs)
2574              {
2575                return rhs.row () < lhs.row (); // reverse row ordering
2576              });
2577 
2578   // now delete them
2579   Q_FOREACH (auto index, selected_rows)
2580     {
2581       next_macros_.removeRow (index.row ());
2582     }
2583 }
2584 
on_add_macro_push_button_clicked(bool)2585 void Configuration::impl::on_add_macro_push_button_clicked (bool /* checked */)
2586 {
2587   if (next_macros_.insertRow (next_macros_.rowCount ()))
2588     {
2589       auto index = next_macros_.index (next_macros_.rowCount () - 1);
2590       ui_->macros_list_view->setCurrentIndex (index);
2591       next_macros_.setData (index, ui_->add_macro_line_edit->text ());
2592       ui_->add_macro_line_edit->clear ();
2593     }
2594 }
2595 
on_udp_server_line_edit_textChanged(QString const &)2596 void Configuration::impl::on_udp_server_line_edit_textChanged (QString const&)
2597 {
2598   udp_server_name_edited_ = true;
2599 }
2600 
on_udp_server_line_edit_editingFinished()2601 void Configuration::impl::on_udp_server_line_edit_editingFinished ()
2602 {
2603   if (udp_server_name_edited_)
2604     {
2605       auto const& server = ui_->udp_server_line_edit->text ().trimmed ();
2606       QHostAddress ha {server};
2607       if (server.size () && ha.isNull ())
2608         {
2609           // queue a host address lookup
2610           // qDebug () << "server host DNS lookup:" << server;
2611 #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
2612           dns_lookup_id_ = QHostInfo::lookupHost (server, this, &Configuration::impl::host_info_results);
2613 #else
2614           dns_lookup_id_ = QHostInfo::lookupHost (server, this, SLOT (host_info_results (QHostInfo)));
2615 #endif
2616         }
2617       else
2618         {
2619           check_multicast (ha);
2620         }
2621     }
2622 }
2623 
host_info_results(QHostInfo host_info)2624 void Configuration::impl::host_info_results (QHostInfo host_info)
2625 {
2626   if (host_info.lookupId () != dns_lookup_id_) return;
2627   dns_lookup_id_ = -1;
2628   if (QHostInfo::NoError != host_info.error ())
2629     {
2630       MessageBox::critical_message (this, tr ("UDP server DNS lookup failed"), host_info.errorString ());
2631     }
2632   else
2633     {
2634       auto const& server_addresses = host_info.addresses ();
2635       // qDebug () << "message server addresses:" << server_addresses;
2636       if (server_addresses.size ())
2637         {
2638           check_multicast (server_addresses[0]);
2639         }
2640     }
2641 }
2642 
check_multicast(QHostAddress const & ha)2643 void Configuration::impl::check_multicast (QHostAddress const& ha)
2644 {
2645   auto is_multicast = is_multicast_address (ha);
2646   ui_->udp_interfaces_label->setVisible (is_multicast);
2647   ui_->udp_interfaces_combo_box->setVisible (is_multicast);
2648   ui_->udp_TTL_label->setVisible (is_multicast);
2649   ui_->udp_TTL_spin_box->setVisible (is_multicast);
2650   if (isVisible ())
2651     {
2652       if (is_MAC_ambiguous_multicast_address (ha))
2653         {
2654           MessageBox::warning_message (this, tr ("MAC-ambiguous multicast groups addresses not supported"));
2655           find_tab (ui_->udp_server_line_edit);
2656           ui_->udp_server_line_edit->clear ();
2657         }
2658     }
2659   udp_server_name_edited_ = false;
2660 }
2661 
delete_frequencies()2662 void Configuration::impl::delete_frequencies ()
2663 {
2664   auto selection_model = ui_->frequencies_table_view->selectionModel ();
2665   selection_model->select (selection_model->selection (), QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
2666   next_frequencies_.removeDisjointRows (selection_model->selectedRows ());
2667   ui_->frequencies_table_view->resizeColumnToContents (FrequencyList_v2::mode_column);
2668 }
2669 
load_frequencies()2670 void Configuration::impl::load_frequencies ()
2671 {
2672   auto file_name = QFileDialog::getOpenFileName (this, tr ("Load Working Frequencies"), writeable_data_dir_.absolutePath (), tr ("Frequency files (*.qrg);;All files (*.*)"));
2673   if (!file_name.isNull ())
2674     {
2675       auto const list = read_frequencies_file (file_name);
2676       if (list.size ()
2677           && (!next_frequencies_.frequency_list ().size ()
2678               || MessageBox::Yes == MessageBox::query_message (this
2679                                                                , tr ("Replace Working Frequencies")
2680                                                                , tr ("Are you sure you want to discard your current "
2681                                                                      "working frequencies and replace them with the "
2682                                                                      "loaded ones?"))))
2683         {
2684           next_frequencies_.frequency_list (list); // update the model
2685         }
2686     }
2687 }
2688 
merge_frequencies()2689 void Configuration::impl::merge_frequencies ()
2690 {
2691   auto file_name = QFileDialog::getOpenFileName (this, tr ("Merge Working Frequencies"), writeable_data_dir_.absolutePath (), tr ("Frequency files (*.qrg);;All files (*.*)"));
2692   if (!file_name.isNull ())
2693     {
2694       next_frequencies_.frequency_list_merge (read_frequencies_file (file_name)); // update the model
2695     }
2696 }
2697 
read_frequencies_file(QString const & file_name)2698 FrequencyList_v2::FrequencyItems Configuration::impl::read_frequencies_file (QString const& file_name)
2699 {
2700   QFile frequencies_file {file_name};
2701   frequencies_file.open (QFile::ReadOnly);
2702   QDataStream ids {&frequencies_file};
2703   FrequencyList_v2::FrequencyItems list;
2704   quint32 magic;
2705   ids >> magic;
2706   if (qrg_magic != magic)
2707     {
2708       MessageBox::warning_message (this, tr ("Not a valid frequencies file"), tr ("Incorrect file magic"));
2709       return list;
2710     }
2711   quint32 version;
2712   ids >> version;
2713   // handle version checks and QDataStream version here if
2714   // necessary
2715   if (version > qrg_version)
2716     {
2717       MessageBox::warning_message (this, tr ("Not a valid frequencies file"), tr ("Version is too new"));
2718       return list;
2719     }
2720 
2721   // de-serialize the data using version if necessary to
2722   // handle old schemata
2723   ids >> list;
2724 
2725   if (ids.status () != QDataStream::Ok || !ids.atEnd ())
2726     {
2727       MessageBox::warning_message (this, tr ("Not a valid frequencies file"), tr ("Contents corrupt"));
2728       list.clear ();
2729       return list;
2730     }
2731 
2732   return list;
2733 }
2734 
save_frequencies()2735 void Configuration::impl::save_frequencies ()
2736 {
2737   auto file_name = QFileDialog::getSaveFileName (this, tr ("Save Working Frequencies"), writeable_data_dir_.absolutePath (), tr ("Frequency files (*.qrg);;All files (*.*)"));
2738   if (!file_name.isNull ())
2739     {
2740       QFile frequencies_file {file_name};
2741       frequencies_file.open (QFile::WriteOnly);
2742       QDataStream ods {&frequencies_file};
2743       auto selection_model = ui_->frequencies_table_view->selectionModel ();
2744       if (selection_model->hasSelection ()
2745           && MessageBox::Yes == MessageBox::query_message (this
2746                                                            , tr ("Only Save Selected  Working Frequencies")
2747                                                            , tr ("Are you sure you want to save only the "
2748                                                                  "working frequencies that are currently selected? "
2749                                                                  "Click No to save all.")))
2750         {
2751           selection_model->select (selection_model->selection (), QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
2752           ods << qrg_magic << qrg_version << next_frequencies_.frequency_list (selection_model->selectedRows ());
2753         }
2754       else
2755         {
2756           ods << qrg_magic << qrg_version << next_frequencies_.frequency_list ();
2757         }
2758     }
2759 }
2760 
reset_frequencies()2761 void Configuration::impl::reset_frequencies ()
2762 {
2763   if (MessageBox::Yes == MessageBox::query_message (this, tr ("Reset Working Frequencies")
2764                                                     , tr ("Are you sure you want to discard your current "
2765                                                           "working frequencies and replace them with default "
2766                                                           "ones?")))
2767     {
2768       next_frequencies_.reset_to_defaults ();
2769     }
2770 }
2771 
insert_frequency()2772 void Configuration::impl::insert_frequency ()
2773 {
2774   if (QDialog::Accepted == frequency_dialog_->exec ())
2775     {
2776       ui_->frequencies_table_view->setCurrentIndex (next_frequencies_.add (frequency_dialog_->item ()));
2777       ui_->frequencies_table_view->resizeColumnToContents (FrequencyList_v2::mode_column);
2778     }
2779 }
2780 
delete_stations()2781 void Configuration::impl::delete_stations ()
2782 {
2783   auto selection_model = ui_->stations_table_view->selectionModel ();
2784   selection_model->select (selection_model->selection (), QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
2785   next_stations_.removeDisjointRows (selection_model->selectedRows ());
2786   ui_->stations_table_view->resizeColumnToContents (StationList::band_column);
2787   ui_->stations_table_view->resizeColumnToContents (StationList::offset_column);
2788 }
2789 
insert_station()2790 void Configuration::impl::insert_station ()
2791 {
2792   if (QDialog::Accepted == station_dialog_->exec ())
2793     {
2794       ui_->stations_table_view->setCurrentIndex (next_stations_.add (station_dialog_->station ()));
2795       ui_->stations_table_view->resizeColumnToContents (StationList::band_column);
2796       ui_->stations_table_view->resizeColumnToContents (StationList::offset_column);
2797     }
2798 }
2799 
on_save_path_select_push_button_clicked(bool)2800 void Configuration::impl::on_save_path_select_push_button_clicked (bool /* checked */)
2801 {
2802   QFileDialog fd {this, tr ("Save Directory"), ui_->save_path_display_label->text ()};
2803   fd.setFileMode (QFileDialog::Directory);
2804   fd.setOption (QFileDialog::ShowDirsOnly);
2805   if (fd.exec ())
2806     {
2807       if (fd.selectedFiles ().size ())
2808         {
2809           ui_->save_path_display_label->setText (fd.selectedFiles ().at (0));
2810         }
2811     }
2812 }
2813 
on_azel_path_select_push_button_clicked(bool)2814 void Configuration::impl::on_azel_path_select_push_button_clicked (bool /* checked */)
2815 {
2816   QFileDialog fd {this, tr ("AzEl Directory"), ui_->azel_path_display_label->text ()};
2817   fd.setFileMode (QFileDialog::Directory);
2818   fd.setOption (QFileDialog::ShowDirsOnly);
2819   if (fd.exec ()) {
2820     if (fd.selectedFiles ().size ()) {
2821       ui_->azel_path_display_label->setText(fd.selectedFiles().at(0));
2822     }
2823   }
2824 }
2825 
on_calibration_intercept_spin_box_valueChanged(double)2826 void Configuration::impl::on_calibration_intercept_spin_box_valueChanged (double)
2827 {
2828   rig_active_ = false;          // force reset
2829 }
2830 
on_calibration_slope_ppm_spin_box_valueChanged(double)2831 void Configuration::impl::on_calibration_slope_ppm_spin_box_valueChanged (double)
2832 {
2833   rig_active_ = false;          // force reset
2834 }
2835 
on_prompt_to_log_check_box_clicked(bool checked)2836 void Configuration::impl::on_prompt_to_log_check_box_clicked(bool checked)
2837 {
2838   if(checked) ui_->cbAutoLog->setChecked(false);
2839 }
2840 
on_cbAutoLog_clicked(bool checked)2841 void Configuration::impl::on_cbAutoLog_clicked(bool checked)
2842 {
2843   if(checked) ui_->prompt_to_log_check_box->setChecked(false);
2844 }
2845 
on_cbx2ToneSpacing_clicked(bool b)2846 void Configuration::impl::on_cbx2ToneSpacing_clicked(bool b)
2847 {
2848   if(b) ui_->cbx4ToneSpacing->setChecked(false);
2849 }
2850 
on_cbx4ToneSpacing_clicked(bool b)2851 void Configuration::impl::on_cbx4ToneSpacing_clicked(bool b)
2852 {
2853   if(b) ui_->cbx2ToneSpacing->setChecked(false);
2854 }
2855 
on_Field_Day_Exchange_textEdited(QString const & exchange)2856 void Configuration::impl::on_Field_Day_Exchange_textEdited (QString const& exchange)
2857 {
2858   auto text = exchange.simplified ().toUpper ();
2859   auto class_pos = text.indexOf (QRegularExpression {R"([A-H])"});
2860   if (class_pos >= 0 && text.size () >= class_pos + 2 && text.at (class_pos + 1) != QChar {' '})
2861     {
2862       text.insert (class_pos + 1, QChar {' '});
2863     }
2864   ui_->Field_Day_Exchange->setText (text);
2865 }
2866 
on_RTTY_Exchange_textEdited(QString const & exchange)2867 void Configuration::impl::on_RTTY_Exchange_textEdited (QString const& exchange)
2868 {
2869   ui_->RTTY_Exchange->setText (exchange.toUpper ());
2870 }
2871 
have_rig()2872 bool Configuration::impl::have_rig ()
2873 {
2874   if (!open_rig ())
2875     {
2876       MessageBox::critical_message (this, tr ("Rig control error")
2877                                     , tr ("Failed to open connection to rig"));
2878     }
2879   return rig_active_;
2880 }
2881 
open_rig(bool force)2882 bool Configuration::impl::open_rig (bool force)
2883 {
2884   auto result = false;
2885 
2886   auto const rig_data = gather_rig_data ();
2887   if (force || !rig_active_ || rig_data != saved_rig_params_)
2888     {
2889       try
2890         {
2891           close_rig ();
2892 
2893           // create a new Transceiver object
2894           auto rig = transceiver_factory_.create (rig_data, transceiver_thread_);
2895           cached_rig_state_ = Transceiver::TransceiverState {};
2896 
2897           // hook up Configuration transceiver control signals to Transceiver slots
2898           //
2899           // these connections cross the thread boundary
2900           rig_connections_ << connect (this, &Configuration::impl::set_transceiver,
2901                                        rig.get (), &Transceiver::set);
2902 
2903           // hook up Transceiver signals to Configuration signals
2904           //
2905           // these connections cross the thread boundary
2906           rig_connections_ << connect (rig.get (), &Transceiver::resolution, this, [=] (int resolution) {
2907               rig_resolution_ = resolution;
2908             });
2909           rig_connections_ << connect (rig.get (), &Transceiver::update, this, &Configuration::impl::handle_transceiver_update);
2910           rig_connections_ << connect (rig.get (), &Transceiver::failure, this, &Configuration::impl::handle_transceiver_failure);
2911 
2912           // setup thread safe startup and close down semantics
2913           rig_connections_ << connect (this, &Configuration::impl::start_transceiver, rig.get (), &Transceiver::start);
2914           rig_connections_ << connect (this, &Configuration::impl::stop_transceiver, rig.get (), &Transceiver::stop);
2915 
2916           auto p = rig.release ();	// take ownership
2917 
2918           // schedule destruction on thread quit
2919           connect (transceiver_thread_, &QThread::finished, p, &QObject::deleteLater);
2920 
2921           // schedule eventual destruction for non-closing situations
2922           //
2923           // must   be   queued    connection   to   avoid   premature
2924           // self-immolation  since finished  signal  is  going to  be
2925           // emitted from  the object that  will get destroyed  in its
2926           // own  stop  slot  i.e.  a   same  thread  signal  to  slot
2927           // connection which by  default will be reduced  to a method
2928           // function call.
2929           connect (p, &Transceiver::finished, p, &Transceiver::deleteLater, Qt::QueuedConnection);
2930 
2931           ui_->test_CAT_push_button->setStyleSheet ({});
2932           rig_active_ = true;
2933           Q_EMIT start_transceiver (++transceiver_command_number_); // start rig on its thread
2934           result = true;
2935         }
2936       catch (std::exception const& e)
2937         {
2938           handle_transceiver_failure (e.what ());
2939         }
2940 
2941       saved_rig_params_ = rig_data;
2942       rig_changed_ = true;
2943     }
2944   else
2945     {
2946       result = true;
2947     }
2948   return result;
2949 }
2950 
set_cached_mode()2951 void Configuration::impl::set_cached_mode ()
2952 {
2953   MODE mode {Transceiver::UNK};
2954   // override cache mode with what we want to enforce which includes
2955   // UNK (unknown) where we want to leave the rig mode untouched
2956   switch (data_mode_)
2957     {
2958     case data_mode_USB: mode = Transceiver::USB; break;
2959     case data_mode_data: mode = Transceiver::DIG_U; break;
2960     default: break;
2961     }
2962 
2963   cached_rig_state_.mode (mode);
2964 }
2965 
transceiver_frequency(Frequency f)2966 void Configuration::impl::transceiver_frequency (Frequency f)
2967 {
2968   cached_rig_state_.online (true); // we want the rig online
2969   set_cached_mode ();
2970 
2971   // apply any offset & calibration
2972   // we store the offset here for use in feedback from the rig, we
2973   // cannot absolutely determine if the offset should apply but by
2974   // simply picking an offset when the Rx frequency is set and
2975   // sticking to it we get sane behaviour
2976   current_offset_ = stations_.offset (f);
2977   cached_rig_state_.frequency (apply_calibration (f + current_offset_));
2978 
2979   // qDebug () << "Configuration::impl::transceiver_frequency: n:" << transceiver_command_number_ + 1 << "f:" << f;
2980   Q_EMIT set_transceiver (cached_rig_state_, ++transceiver_command_number_);
2981 }
2982 
transceiver_tx_frequency(Frequency f)2983 void Configuration::impl::transceiver_tx_frequency (Frequency f)
2984 {
2985   Q_ASSERT (!f || split_mode ());
2986   if (split_mode ())
2987     {
2988       cached_rig_state_.online (true); // we want the rig online
2989       set_cached_mode ();
2990       cached_rig_state_.split (f);
2991       cached_rig_state_.tx_frequency (f);
2992 
2993       // lookup offset for tx and apply calibration
2994       if (f)
2995         {
2996           // apply and offset and calibration
2997           // we store the offset here for use in feedback from the
2998           // rig, we cannot absolutely determine if the offset should
2999           // apply but by simply picking an offset when the Rx
3000           // frequency is set and sticking to it we get sane behaviour
3001           current_tx_offset_ = stations_.offset (f);
3002           cached_rig_state_.tx_frequency (apply_calibration (f + current_tx_offset_));
3003         }
3004 
3005       // qDebug () << "Configuration::impl::transceiver_tx_frequency: n:" << transceiver_command_number_ + 1 << "f:" << f;
3006       Q_EMIT set_transceiver (cached_rig_state_, ++transceiver_command_number_);
3007     }
3008 }
3009 
transceiver_mode(MODE m)3010 void Configuration::impl::transceiver_mode (MODE m)
3011 {
3012   cached_rig_state_.online (true); // we want the rig online
3013   cached_rig_state_.mode (m);
3014   // qDebug () << "Configuration::impl::transceiver_mode: n:" << transceiver_command_number_ + 1 << "m:" << m;
3015   Q_EMIT set_transceiver (cached_rig_state_, ++transceiver_command_number_);
3016 }
3017 
transceiver_ptt(bool on)3018 void Configuration::impl::transceiver_ptt (bool on)
3019 {
3020   cached_rig_state_.online (true); // we want the rig online
3021   set_cached_mode ();
3022   cached_rig_state_.ptt (on);
3023   // qDebug () << "Configuration::impl::transceiver_ptt: n:" << transceiver_command_number_ + 1 << "on:" << on;
3024   Q_EMIT set_transceiver (cached_rig_state_, ++transceiver_command_number_);
3025 }
3026 
sync_transceiver(bool)3027 void Configuration::impl::sync_transceiver (bool /*force_signal*/)
3028 {
3029   // pass this on as cache must be ignored
3030   // Q_EMIT sync (force_signal);
3031 }
3032 
handle_transceiver_update(TransceiverState const & state,unsigned sequence_number)3033 void Configuration::impl::handle_transceiver_update (TransceiverState const& state,
3034                                                      unsigned sequence_number)
3035 {
3036   LOG_TRACE ("#: " << sequence_number << ' ' << state);
3037 
3038   // only follow rig on some information, ignore other stuff
3039   cached_rig_state_.online (state.online ());
3040   cached_rig_state_.frequency (state.frequency ());
3041   cached_rig_state_.mode (state.mode ());
3042   cached_rig_state_.split (state.split ());
3043 
3044   if (state.online ())
3045     {
3046       ui_->test_PTT_push_button->setChecked (state.ptt ());
3047 
3048       if (isVisible ())
3049         {
3050           ui_->test_CAT_push_button->setStyleSheet ("QPushButton {background-color: green;}");
3051 
3052           auto const& rig = ui_->rig_combo_box->currentText ();
3053           auto ptt_method = static_cast<TransceiverFactory::PTTMethod> (ui_->PTT_method_button_group->checkedId ());
3054           auto CAT_PTT_enabled = transceiver_factory_.has_CAT_PTT (rig);
3055           ui_->test_PTT_push_button->setEnabled ((TransceiverFactory::PTT_method_CAT == ptt_method && CAT_PTT_enabled)
3056                                                  || TransceiverFactory::PTT_method_DTR == ptt_method
3057                                                  || TransceiverFactory::PTT_method_RTS == ptt_method);
3058         }
3059     }
3060   else
3061     {
3062       close_rig ();
3063     }
3064 
3065   // pass on to clients if current command is processed
3066   if (sequence_number == transceiver_command_number_)
3067     {
3068       TransceiverState reported_state {state};
3069       // take off calibration & offset
3070       reported_state.frequency (remove_calibration (reported_state.frequency ()) - current_offset_);
3071 
3072       if (reported_state.tx_frequency ())
3073         {
3074           // take off calibration & offset
3075           reported_state.tx_frequency (remove_calibration (reported_state.tx_frequency ()) - current_tx_offset_);
3076         }
3077 
3078       Q_EMIT self_->transceiver_update (reported_state);
3079     }
3080 }
3081 
handle_transceiver_failure(QString const & reason)3082 void Configuration::impl::handle_transceiver_failure (QString const& reason)
3083 {
3084   LOG_ERROR ("handle_transceiver_failure: reason: " << reason);
3085   close_rig ();
3086   ui_->test_PTT_push_button->setChecked (false);
3087 
3088   if (isVisible ())
3089     {
3090       MessageBox::critical_message (this, tr ("Rig failure"), reason);
3091     }
3092   else
3093     {
3094       // pass on if our dialog isn't active
3095       Q_EMIT self_->transceiver_failure (reason);
3096     }
3097 }
3098 
close_rig()3099 void Configuration::impl::close_rig ()
3100 {
3101   ui_->test_PTT_push_button->setEnabled (false);
3102 
3103   // revert to no rig configured
3104   if (rig_active_)
3105     {
3106       ui_->test_CAT_push_button->setStyleSheet ("QPushButton {background-color: red;}");
3107       Q_EMIT stop_transceiver ();
3108       for (auto const& connection: rig_connections_)
3109         {
3110           disconnect (connection);
3111         }
3112       rig_connections_.clear ();
3113       rig_active_ = false;
3114     }
3115 }
3116 
3117 // find the audio device that matches the specified name, also
3118 // populate into the selection combo box with any devices we find in
3119 // the search
find_audio_device(QAudio::Mode mode,QComboBox * combo_box,QString const & device_name)3120 QAudioDeviceInfo Configuration::impl::find_audio_device (QAudio::Mode mode, QComboBox * combo_box
3121                                                          , QString const& device_name)
3122 {
3123   using std::copy;
3124   using std::back_inserter;
3125 
3126   if (device_name.size ())
3127     {
3128       Q_EMIT self_->enumerating_audio_devices ();
3129       auto const& devices = QAudioDeviceInfo::availableDevices (mode);
3130       Q_FOREACH (auto const& p, devices)
3131         {
3132           // qDebug () << "Configuration::impl::find_audio_device: input:" << (QAudio::AudioInput == mode) << "name:" << p.deviceName () << "preferred format:" << p.preferredFormat () << "endians:" << p.supportedByteOrders () << "codecs:" << p.supportedCodecs () << "channels:" << p.supportedChannelCounts () << "rates:" << p.supportedSampleRates () << "sizes:" << p.supportedSampleSizes () << "types:" << p.supportedSampleTypes ();
3133           if (p.deviceName () == device_name)
3134             {
3135               // convert supported channel counts into something we can store in the item model
3136               QList<QVariant> channel_counts;
3137               auto scc = p.supportedChannelCounts ();
3138               copy (scc.cbegin (), scc.cend (), back_inserter (channel_counts));
3139               combo_box->insertItem (0, device_name, QVariant::fromValue (audio_info_type {p, channel_counts}));
3140               combo_box->setCurrentIndex (0);
3141               return p;
3142             }
3143         }
3144       // insert a place holder for the not found device
3145       combo_box->insertItem (0, device_name + " (" + tr ("Not found", "audio device missing") + ")", QVariant::fromValue (audio_info_type {}));
3146       combo_box->setCurrentIndex (0);
3147     }
3148   return {};
3149 }
3150 
3151 // load the available audio devices into the selection combo box
load_audio_devices(QAudio::Mode mode,QComboBox * combo_box,QAudioDeviceInfo * device)3152 void Configuration::impl::load_audio_devices (QAudio::Mode mode, QComboBox * combo_box
3153                                               , QAudioDeviceInfo * device)
3154 {
3155   using std::copy;
3156   using std::back_inserter;
3157 
3158   combo_box->clear ();
3159 
3160   Q_EMIT self_->enumerating_audio_devices ();
3161   int current_index = -1;
3162   auto const& devices = QAudioDeviceInfo::availableDevices (mode);
3163   Q_FOREACH (auto const& p, devices)
3164     {
3165       // qDebug () << "Configuration::impl::load_audio_devices: input:" << (QAudio::AudioInput == mode) << "name:" << p.deviceName () << "preferred format:" << p.preferredFormat () << "endians:" << p.supportedByteOrders () << "codecs:" << p.supportedCodecs () << "channels:" << p.supportedChannelCounts () << "rates:" << p.supportedSampleRates () << "sizes:" << p.supportedSampleSizes () << "types:" << p.supportedSampleTypes ();
3166 
3167       // convert supported channel counts into something we can store in the item model
3168       QList<QVariant> channel_counts;
3169       auto scc = p.supportedChannelCounts ();
3170       copy (scc.cbegin (), scc.cend (), back_inserter (channel_counts));
3171 
3172       combo_box->addItem (p.deviceName (), QVariant::fromValue (audio_info_type {p, channel_counts}));
3173       if (p == *device)
3174         {
3175           current_index = combo_box->count () - 1;
3176         }
3177     }
3178   combo_box->setCurrentIndex (current_index);
3179 }
3180 
3181 // load the available network interfaces into the selection combo box
load_network_interfaces(CheckableItemComboBox * combo_box,QStringList current)3182 void Configuration::impl::load_network_interfaces (CheckableItemComboBox * combo_box, QStringList current)
3183 {
3184   combo_box->clear ();
3185   for (auto const& net_if : QNetworkInterface::allInterfaces ())
3186     {
3187       auto flags = QNetworkInterface::IsUp | QNetworkInterface::CanMulticast;
3188       if ((net_if.flags () & flags) == flags)
3189         {
3190           bool check_it = current.contains (net_if.name ());
3191           if (net_if.flags () & QNetworkInterface::IsLoopBack)
3192             {
3193               loopback_interface_name_ = net_if.name ();
3194               if (!current.size ())
3195                 {
3196                   check_it = true;
3197                 }
3198             }
3199           auto item = combo_box->addCheckItem (net_if.humanReadableName ()
3200                                                , net_if.name ()
3201                                                , check_it ? Qt::Checked : Qt::Unchecked);
3202           auto tip = QString {"name(index): %1(%2) - %3"}.arg (net_if.name ()).arg (net_if.index ())
3203                        .arg (net_if.flags () & QNetworkInterface::IsUp ? "Up" : "Down");
3204           auto hw_addr = net_if.hardwareAddress ();
3205           if (hw_addr.size ())
3206             {
3207               tip += QString {"\nhw: %1"}.arg (net_if.hardwareAddress ());
3208             }
3209           auto aes = net_if.addressEntries ();
3210           if (aes.size ())
3211             {
3212               tip += "\naddresses:";
3213               for (auto const& ae : aes)
3214                 {
3215                   tip += QString {"\n  ip: %1/%2"}.arg (ae.ip ().toString ()).arg (ae.prefixLength ());
3216                 }
3217             }
3218           item->setToolTip (tip);
3219         }
3220     }
3221 }
3222 
3223 // get the select network interfaces from the selection combo box
validate_network_interfaces(QString const &)3224 void Configuration::impl::validate_network_interfaces (QString const& /*text*/)
3225 {
3226   auto model = static_cast<QStandardItemModel *> (ui_->udp_interfaces_combo_box->model ());
3227   bool has_checked {false};
3228   int loopback_row {-1};
3229   for (int row = 0; row < model->rowCount (); ++row)
3230     {
3231       if (model->item (row)->data ().toString () == loopback_interface_name_)
3232         {
3233           loopback_row = row;
3234         }
3235       else if (Qt::Checked == model->item (row)->checkState ())
3236         {
3237           has_checked = true;
3238         }
3239     }
3240   if (loopback_row >= 0)
3241     {
3242       if (!has_checked)
3243         {
3244           model->item (loopback_row)->setCheckState (Qt::Checked);
3245         }
3246       model->item (loopback_row)->setEnabled (has_checked);
3247     }
3248 }
3249 
3250 // get the select network interfaces from the selection combo box
get_selected_network_interfaces(CheckableItemComboBox * combo_box)3251 QStringList Configuration::impl::get_selected_network_interfaces (CheckableItemComboBox * combo_box)
3252 {
3253   QStringList interfaces;
3254   auto model = static_cast<QStandardItemModel *> (combo_box->model ());
3255   for (int row = 0; row < model->rowCount (); ++row)
3256     {
3257       if (Qt::Checked == model->item (row)->checkState ())
3258         {
3259           interfaces << model->item (row)->data ().toString ();
3260         }
3261     }
3262   return interfaces;
3263 }
3264 
3265 // enable only the channels that are supported by the selected audio device
update_audio_channels(QComboBox const * source_combo_box,int index,QComboBox * combo_box,bool allow_both)3266 void Configuration::impl::update_audio_channels (QComboBox const * source_combo_box, int index, QComboBox * combo_box, bool allow_both)
3267 {
3268   // disable all items
3269   for (int i (0); i < combo_box->count (); ++i)
3270     {
3271       combo_box->setItemData (i, combo_box_item_disabled, Qt::UserRole - 1);
3272     }
3273 
3274   Q_FOREACH (QVariant const& v
3275              , (source_combo_box->itemData (index).value<audio_info_type> ().second))
3276     {
3277       // enable valid options
3278       int n {v.toInt ()};
3279       if (2 == n)
3280         {
3281           combo_box->setItemData (AudioDevice::Left, combo_box_item_enabled, Qt::UserRole - 1);
3282           combo_box->setItemData (AudioDevice::Right, combo_box_item_enabled, Qt::UserRole - 1);
3283           if (allow_both)
3284             {
3285               combo_box->setItemData (AudioDevice::Both, combo_box_item_enabled, Qt::UserRole - 1);
3286             }
3287         }
3288       else if (1 == n)
3289         {
3290           combo_box->setItemData (AudioDevice::Mono, combo_box_item_enabled, Qt::UserRole - 1);
3291         }
3292     }
3293 }
3294 
find_tab(QWidget * target)3295 void Configuration::impl::find_tab (QWidget * target)
3296 {
3297   for (auto * parent = target->parentWidget (); parent; parent = parent->parentWidget ())
3298     {
3299       auto index = ui_->configuration_tabs->indexOf (parent);
3300       if (index != -1)
3301         {
3302           ui_->configuration_tabs->setCurrentIndex (index);
3303           break;
3304         }
3305     }
3306   target->setFocus ();
3307 }
3308 
3309 // load all the supported rig names into the selection combo box
enumerate_rigs()3310 void Configuration::impl::enumerate_rigs ()
3311 {
3312   ui_->rig_combo_box->clear ();
3313 
3314   auto rigs = transceiver_factory_.supported_transceivers ();
3315 
3316   for (auto r = rigs.cbegin (); r != rigs.cend (); ++r)
3317     {
3318       if ("None" == r.key ())
3319         {
3320           // put None first
3321           ui_->rig_combo_box->insertItem (0, r.key (), r.value ().model_number_);
3322         }
3323       else
3324         {
3325           int i;
3326           for(i=1;i<ui_->rig_combo_box->count() && (r.key().toLower() > ui_->rig_combo_box->itemText(i).toLower());++i);
3327           if (i < ui_->rig_combo_box->count())  ui_->rig_combo_box->insertItem (i, r.key (), r.value ().model_number_);
3328           else ui_->rig_combo_box->addItem (r.key (), r.value ().model_number_);
3329         }
3330     }
3331 
3332   ui_->rig_combo_box->setCurrentText (rig_params_.rig_name);
3333 }
3334 
fill_port_combo_box(QComboBox * cb)3335 void Configuration::impl::fill_port_combo_box (QComboBox * cb)
3336 {
3337   auto current_text = cb->currentText ();
3338   cb->clear ();
3339   Q_FOREACH (auto const& p, QSerialPortInfo::availablePorts ())
3340     {
3341       if (!p.portName ().contains ( "NULL" )) // virtual serial port pairs
3342         {
3343           // remove possibly confusing Windows device path (OK because
3344           // it gets added back by Hamlib)
3345           cb->addItem (p.systemLocation ().remove (QRegularExpression {R"(^\\\\\.\\)"}));
3346           auto tip = QString {"%1 %2 %3"}.arg (p.manufacturer ()).arg (p.serialNumber ()).arg (p.description ()).trimmed ();
3347           if (tip.size ())
3348             {
3349               cb->setItemData (cb->count () - 1, tip, Qt::ToolTipRole);
3350             }
3351         }
3352     }
3353   cb->addItem ("USB");
3354   cb->setItemData (cb->count () - 1, "Custom USB device", Qt::ToolTipRole);
3355   cb->setEditText (current_text);
3356 }
3357 
apply_calibration(Frequency f) const3358 auto Configuration::impl::apply_calibration (Frequency f) const -> Frequency
3359 {
3360   if (frequency_calibration_disabled_) return f;
3361   return std::llround (calibration_.intercept
3362                        + (1. + calibration_.slope_ppm / 1.e6) * f);
3363 }
3364 
remove_calibration(Frequency f) const3365 auto Configuration::impl::remove_calibration (Frequency f) const -> Frequency
3366 {
3367   if (frequency_calibration_disabled_) return f;
3368   return std::llround ((f - calibration_.intercept)
3369                        / (1. + calibration_.slope_ppm / 1.e6));
3370 }
3371 
3372 ENUM_QDATASTREAM_OPS_IMPL (Configuration, DataMode);
3373 ENUM_QDATASTREAM_OPS_IMPL (Configuration, Type2MsgGen);
3374 
3375 ENUM_CONVERSION_OPS_IMPL (Configuration, DataMode);
3376 ENUM_CONVERSION_OPS_IMPL (Configuration, Type2MsgGen);
3377