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:"), ®ion_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 {®ions_, &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 (®ions_);
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 {®ions_, 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