diff --git a/AudioDevice.cpp b/AudioDevice.cpp index a557aa66e..8dae74707 100644 --- a/AudioDevice.cpp +++ b/AudioDevice.cpp @@ -1,18 +1,5 @@ #include "AudioDevice.hpp" -#include - -namespace -{ - struct init - { - init () - { - qRegisterMetaType ("AudioDevice::Channel"); - } - } static_initializer; -} - bool AudioDevice::initialize (OpenMode mode, Channel channel) { m_channel = channel; diff --git a/Bands.cpp b/Bands.cpp index 747eb7f41..0f23c478e 100644 --- a/Bands.cpp +++ b/Bands.cpp @@ -1,196 +1,193 @@ -#include "Bands.hpp" - -#include - -#include -#include - -namespace -{ - // Local structure to hold a single ADIF band definition. - struct ADIF_band - { - char const * const name_; - Radio::Frequency lower_bound_; - Radio::Frequency upper_bound_; - }; - - // Table of ADIF band definitions as defined in the ADIF - // specification. - ADIF_band constexpr ADIF_bands[] = { - {"2190m", 136000u, 137000u}, - {"630m", 472000u, 479000u}, - {"560m", 501000u, 504000u}, - {"160m", 1800000u, 2000000u}, - {"80m", 3500000u, 4000000u}, - {"60m", 5102000u, 5406500u}, - {"40m", 7000000u, 7300000u}, - {"30m", 10000000u, 10150000u}, - {"20m", 14000000u, 14350000u}, - {"17m", 18068000u, 18168000u}, - {"15m", 21000000u, 21450000u}, - {"12m", 24890000u, 24990000u}, - {"10m", 28000000u, 29700000u}, - {"6m", 50000000u, 54000000u}, - {"4m", 70000000u, 71000000u}, - {"2m", 144000000u, 148000000u}, - {"1.25m", 222000000u, 225000000u}, - {"70cm", 420000000u, 450000000u}, - {"33cm", 902000000u, 928000000u}, - {"23cm", 1240000000u, 1300000000u}, - {"13cm", 2300000000u, 2450000000u}, - {"9cm", 3300000000u, 3500000000u}, - {"6cm", 5650000000u, 5925000000u}, - {"3cm", 10000000000u, 10500000000u}, - {"1.25cm", 24000000000u, 24250000000u}, - {"6mm", 47000000000u, 47200000000u}, - {"4mm", 75500000000u, 81000000000u}, - {"2.5mm", 119980000000u, 120020000000u}, - {"2mm", 142000000000u, 149000000000u}, - {"1mm", 241000000000u, 250000000000u}, - }; - - auto constexpr out_of_band = "OOB"; - - int constexpr table_rows () - { - return sizeof (ADIF_bands) / sizeof (ADIF_bands[0]); - } -} - -Bands::Bands (QObject * parent) - : QAbstractTableModel {parent} -{ -} - -QModelIndex Bands::find (QVariant const& v) const -{ - auto f = v.value (); - auto end_iter = ADIF_bands + table_rows (); - auto row_iter = std::find_if (ADIF_bands, end_iter, [f] (ADIF_band const& band) { - return band.lower_bound_ <= f && f <= band.upper_bound_; - }); - if (row_iter != end_iter) - { - return index (row_iter - ADIF_bands, 0); // return the band row index - } - - return QModelIndex {}; -} - -int Bands::rowCount (QModelIndex const& parent) const -{ - return parent.isValid () ? 0 : table_rows (); -} - -int Bands::columnCount (QModelIndex const& parent) const -{ - return parent.isValid () ? 0 : 3; -} - -Qt::ItemFlags Bands::flags (QModelIndex const& index) const -{ - return QAbstractTableModel::flags (index) | Qt::ItemIsDropEnabled; -} - -QVariant Bands::data (QModelIndex const& index, int role) const -{ - QVariant item; - - if (!index.isValid ()) - { - // Hijack root for OOB string. - if (Qt::DisplayRole == role) - { - item = out_of_band; - } - } - else - { - auto row = index.row (); - auto column = index.column (); - - if (row < table_rows ()) - { - switch (role) - { - case Qt::ToolTipRole: - case Qt::AccessibleDescriptionRole: - switch (column) - { - case 0: item = tr ("Band name"); break; - case 1: item = tr ("Lower frequency limit"); break; - case 2: item = tr ("Upper frequency limit"); break; - } - break; - - case SortRole: - case Qt::DisplayRole: - case Qt::EditRole: - switch (column) - { - case 0: - if (SortRole == role) - { - // band name sorts by lower bound - item = ADIF_bands[row].lower_bound_; - } - else - { - item = ADIF_bands[row].name_; - } - break; - - case 1: item = ADIF_bands[row].lower_bound_; break; - case 2: item = ADIF_bands[row].upper_bound_; break; - } - break; - - case Qt::AccessibleTextRole: - switch (column) - { - case 0: item = ADIF_bands[row].name_; break; - case 1: item = ADIF_bands[row].lower_bound_; break; - case 2: item = ADIF_bands[row].upper_bound_; break; - } - break; - - case Qt::TextAlignmentRole: - switch (column) - { - case 0: - item = Qt::AlignHCenter + Qt::AlignVCenter; - break; - - case 1: - case 2: - item = Qt::AlignRight + Qt::AlignVCenter; - break; - } - break; - } - } - } - return item; -} - -QVariant Bands::headerData (int section, Qt::Orientation orientation, int role) const -{ - QVariant result; - - if (Qt::DisplayRole == role && Qt::Horizontal == orientation) - { - switch (section) - { - case 0: result = tr ("Band"); break; - case 1: result = tr ("Lower Limit"); break; - case 2: result = tr ("Upper Limit"); break; - } - } - else - { - result = QAbstractTableModel::headerData (section, orientation, role); - } - - return result; -} +#include "Bands.hpp" + +#include + +#include +#include + +namespace +{ + // Local structure to hold a single ADIF band definition. + + // Table of ADIF band definitions as defined in the ADIF + // specification. + Bands::ADIFBand constexpr ADIF_bands[] = { + {"2190m", 136000u, 137000u}, + {"630m", 472000u, 479000u}, + {"560m", 501000u, 504000u}, + {"160m", 1800000u, 2000000u}, + {"80m", 3500000u, 4000000u}, + {"60m", 5102000u, 5406500u}, + {"40m", 7000000u, 7300000u}, + {"30m", 10000000u, 10150000u}, + {"20m", 14000000u, 14350000u}, + {"17m", 18068000u, 18168000u}, + {"15m", 21000000u, 21450000u}, + {"12m", 24890000u, 24990000u}, + {"10m", 28000000u, 29700000u}, + {"6m", 50000000u, 54000000u}, + {"4m", 70000000u, 71000000u}, + {"2m", 144000000u, 148000000u}, + {"1.25m", 222000000u, 225000000u}, + {"70cm", 420000000u, 450000000u}, + {"33cm", 902000000u, 928000000u}, + {"23cm", 1240000000u, 1300000000u}, + {"13cm", 2300000000u, 2450000000u}, + {"9cm", 3300000000u, 3500000000u}, + {"6cm", 5650000000u, 5925000000u}, + {"3cm", 10000000000u, 10500000000u}, + {"1.25cm",24000000000u, 24250000000u}, + {"6mm", 47000000000u, 47200000000u}, + {"4mm", 75500000000u, 81000000000u}, + {"2.5mm", 119980000000u,120020000000u}, + {"2mm", 142000000000u,149000000000u}, + {"1mm", 241000000000u,250000000000u}, + }; + + Bands::ADIFBand constexpr oob = {"OOB", 0, std::numeric_limits::max ()}; + + int constexpr table_rows () + { + return sizeof (ADIF_bands) / sizeof (ADIF_bands[0]); + } +} + +Bands::Bands (QObject * parent) + : QAbstractTableModel {parent} +{ +} + +auto Bands::find (Frequency f) const -> ADIFBand const * +{ + auto const& end_iter = ADIF_bands + table_rows (); + auto const& row_iter = std::find_if (ADIF_bands, end_iter, [f] (ADIFBand const& band) { + return band.lower_bound_ <= f && f <= band.upper_bound_; + }); + if (row_iter != end_iter) + { + return row_iter; + } + return &oob; +} + +auto Bands::out_of_band () const -> ADIFBand const * +{ + return &oob; +} + +int Bands::rowCount (QModelIndex const& parent) const +{ + return parent.isValid () ? 0 : table_rows (); +} + +int Bands::columnCount (QModelIndex const& parent) const +{ + return parent.isValid () ? 0 : 3; +} + +Qt::ItemFlags Bands::flags (QModelIndex const& index) const +{ + return QAbstractTableModel::flags (index) | Qt::ItemIsDropEnabled; +} + +QVariant Bands::data (QModelIndex const& index, int role) const +{ + QVariant item; + + if (!index.isValid ()) + { + // Hijack root for OOB string. + if (Qt::DisplayRole == role) + { + item = oob.name_; + } + } + else + { + auto row = index.row (); + auto column = index.column (); + + if (row < table_rows ()) + { + switch (role) + { + case Qt::ToolTipRole: + case Qt::AccessibleDescriptionRole: + switch (column) + { + case 0: item = tr ("Band name"); break; + case 1: item = tr ("Lower frequency limit"); break; + case 2: item = tr ("Upper frequency limit"); break; + } + break; + + case SortRole: + case Qt::DisplayRole: + case Qt::EditRole: + switch (column) + { + case 0: + if (SortRole == role) + { + // band name sorts by lower bound + item = ADIF_bands[row].lower_bound_; + } + else + { + item = ADIF_bands[row].name_; + } + break; + + case 1: item = ADIF_bands[row].lower_bound_; break; + case 2: item = ADIF_bands[row].upper_bound_; break; + } + break; + + case Qt::AccessibleTextRole: + switch (column) + { + case 0: item = ADIF_bands[row].name_; break; + case 1: item = ADIF_bands[row].lower_bound_; break; + case 2: item = ADIF_bands[row].upper_bound_; break; + } + break; + + case Qt::TextAlignmentRole: + switch (column) + { + case 0: + item = Qt::AlignHCenter + Qt::AlignVCenter; + break; + + case 1: + case 2: + item = Qt::AlignRight + Qt::AlignVCenter; + break; + } + break; + } + } + } + return item; +} + +QVariant Bands::headerData (int section, Qt::Orientation orientation, int role) const +{ + QVariant result; + + if (Qt::DisplayRole == role && Qt::Horizontal == orientation) + { + switch (section) + { + case 0: result = tr ("Band"); break; + case 1: result = tr ("Lower Limit"); break; + case 2: result = tr ("Upper Limit"); break; + } + } + else + { + result = QAbstractTableModel::headerData (section, orientation, role); + } + + return result; +} diff --git a/Bands.hpp b/Bands.hpp index 41d3810af..13c9fd2c8 100644 --- a/Bands.hpp +++ b/Bands.hpp @@ -1,58 +1,68 @@ -#ifndef BANDS_HPP__ -#define BANDS_HPP__ - -#include - -#include "Radio.hpp" - -// -// Class Bands -// -// Encapsulates information about amateur radio bands as defined by -// the ADIF specification. The model is immutable. The rows are -// stored in asscending order of frequency. -// -// Responsibilities -// -// Provides a well known band name mapped to lower and upper -// frequency limits. Also provides a convenience operation to -// determine the band details for any given frequency, the result of -// which may be invalid if the given frequency doesn't lie within a -// recognised band. -// -// Collaborations -// -// Implements the QAbstractTableModel interface as an immutable table -// where rows are bands and columns are band name, lower frequency -// limit and, upper ferquency limit respectively. -// -class Bands final - : public QAbstractTableModel -{ -public: - explicit Bands (QObject * parent = nullptr); - - // - // Model API - // - QModelIndex find (QVariant const&) const; // find band Frequency is in - - // Custom role for sorting. - static int constexpr SortRole = Qt::UserRole; - - // Implement the QAbstractTableModel interface - int rowCount (QModelIndex const& parent = QModelIndex {}) const override; - int columnCount (QModelIndex const& parent = QModelIndex {}) const override; - Qt::ItemFlags flags (QModelIndex const& = QModelIndex {}) const override; - QVariant headerData (int section, Qt::Orientation, int = Qt::DisplayRole) const override; - - // The value return for the Qt::DisplayRole role for the root of the - // model (invalid index) is a special string representing out of - // band. - // - // All columns return a number for the custom role SortRole, this - // number defines a strict frequency order for the rows. - QVariant data (QModelIndex const&, int role = Qt::DisplayRole) const override; -}; - -#endif +#ifndef BANDS_HPP__ +#define BANDS_HPP__ + +#include + +#include "Radio.hpp" + +// +// Class Bands +// +// Encapsulates information about amateur radio bands as defined by +// the ADIF specification. The model is immutable. The rows are +// stored in asscending order of frequency. +// +// Responsibilities +// +// Provides a well known band name mapped to lower and upper +// frequency limits. Also provides a convenience operation to +// determine the band details for any given frequency, the result of +// which may be invalid if the given frequency doesn't lie within a +// recognised band. +// +// Collaborations +// +// Implements the QAbstractTableModel interface as an immutable table +// where rows are bands and columns are band name, lower frequency +// limit and, upper ferquency limit respectively. +// +class Bands final + : public QAbstractTableModel +{ +public: + using Frequency = Radio::Frequency; + + struct ADIFBand + { + char const * const name_; + Radio::Frequency lower_bound_; + Radio::Frequency upper_bound_; + }; + + explicit Bands (QObject * parent = nullptr); + + // + // Model API + // + ADIFBand const * find (Frequency) const; // find band Frequency is in + ADIFBand const * out_of_band () const; + + // Custom role for sorting. + static int constexpr SortRole = Qt::UserRole; + + // Implement the QAbstractTableModel interface + int rowCount (QModelIndex const& parent = QModelIndex {}) const override; + int columnCount (QModelIndex const& parent = QModelIndex {}) const override; + Qt::ItemFlags flags (QModelIndex const& = QModelIndex {}) const override; + QVariant headerData (int section, Qt::Orientation, int = Qt::DisplayRole) const override; + + // The value return for the Qt::DisplayRole role for the root of the + // model (invalid index) is a special string representing out of + // band. + // + // All columns return a number for the custom role SortRole, this + // number defines a strict frequency order for the rows. + QVariant data (QModelIndex const&, int role = Qt::DisplayRole) const override; +}; + +#endif diff --git a/CMakeLists.txt b/CMakeLists.txt index 562070557..9bce7d3a3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -177,11 +177,13 @@ endif (APPLE) # set (wsjt_qt_CXXSRCS qt_helpers.cpp + MetaDataRegistry.cpp NetworkServerLookup.cpp revision_utils.cpp WFPalette.cpp Radio.cpp Bands.cpp + Modes.cpp FrequencyList.cpp StationList.cpp FrequencyLineEdit.cpp diff --git a/CandidateKeyFilter.cpp b/CandidateKeyFilter.cpp index c0e048563..47b36b68f 100644 --- a/CandidateKeyFilter.cpp +++ b/CandidateKeyFilter.cpp @@ -8,11 +8,11 @@ class CandidateKeyFilter::impl final { public: - explicit impl (QAbstractItemModel const * referencing_model + explicit impl (int referenced_key_column + , int referenced_key_role + , QAbstractItemModel const * referencing_model , int referencing_key_column - , int referenced_key_column - , int referencing_key_role - , int referenced_key_role) + , int referencing_key_role) : referencing_ {referencing_model} , referencing_key_column_ {referencing_key_column} , referencing_key_role_ {referencing_key_role} @@ -29,14 +29,25 @@ public: QModelIndex active_key_; }; -CandidateKeyFilter::CandidateKeyFilter (QAbstractItemModel const * referencing_model - , QAbstractItemModel * referenced_model - , int referencing_key_column +CandidateKeyFilter::CandidateKeyFilter (QAbstractItemModel * referenced_model , int referenced_key_column - , int referencing_key_role + , QObject * parent , int referenced_key_role) - : QSortFilterProxyModel {nullptr} // ForeignKeyDelegate owns us - , m_ {referencing_model, referencing_key_column, referenced_key_column, referencing_key_role, referenced_key_role} + : QSortFilterProxyModel {parent} + , m_ {referenced_key_column, referenced_key_role, nullptr, 0, Qt::EditRole} +{ + setSourceModel (referenced_model); +} + +CandidateKeyFilter::CandidateKeyFilter (QAbstractItemModel * referenced_model + , QAbstractItemModel const * referencing_model + , int referenced_key_column + , int referencing_key_column + , QObject * parent + , int referenced_key_role + , int referencing_key_role) + : QSortFilterProxyModel {parent} + , m_ {referenced_key_column, referenced_key_role, referencing_model, referencing_key_column, referencing_key_role} { setSourceModel (referenced_model); } @@ -47,16 +58,24 @@ CandidateKeyFilter::~CandidateKeyFilter () void CandidateKeyFilter::set_active_key (QModelIndex const& index) { - if (index.isValid () ) + if (m_->referencing_) { - Q_ASSERT (index.column () == m_->referencing_key_column_); - m_->active_key_ = index; + if (index.isValid () ) + { + Q_ASSERT (index.column () == m_->referencing_key_column_); + m_->active_key_ = index; + } + invalidateFilter (); } - invalidateFilter (); } bool CandidateKeyFilter::filterAcceptsRow (int candidate_row, QModelIndex const& candidate_parent) const { + if (!m_->referencing_) // many to many passes all + { + return true; + } + auto candidate_key = sourceModel ()->index (candidate_row, m_->referenced_key_column_, candidate_parent).data (m_->referenced_key_role_); // Include the current key. diff --git a/CandidateKeyFilter.hpp b/CandidateKeyFilter.hpp index 1a5304b20..3fdd85243 100644 --- a/CandidateKeyFilter.hpp +++ b/CandidateKeyFilter.hpp @@ -12,12 +12,17 @@ class CandidateKeyFilter final : public QSortFilterProxyModel { public: - explicit CandidateKeyFilter (QAbstractItemModel const * referencing_model - , QAbstractItemModel * referenced_model - , int referencing_key_column = 0 - , int referenced_key_column = 0 - , int referencing_key_role = Qt::EditRole + explicit CandidateKeyFilter (QAbstractItemModel * referenced_model + , int referenced_key_column + , QObject * parent = nullptr , int referenced_key_role = Qt::EditRole); + explicit CandidateKeyFilter (QAbstractItemModel * referenced_model + , QAbstractItemModel const * referencing_model + , int referenced_key_column + , int referencing_key_column + , QObject * parent = nullptr + , int referenced_key_role = Qt::EditRole + , int referencing_key_role = Qt::EditRole); ~CandidateKeyFilter (); // this key is not to be filtered, usually because we want to allow diff --git a/Configuration.cpp b/Configuration.cpp index 0804dbbc0..591a72d6e 100644 --- a/Configuration.cpp +++ b/Configuration.cpp @@ -171,6 +171,7 @@ #include "TransceiverFactory.hpp" #include "Transceiver.hpp" #include "Bands.hpp" +#include "Modes.hpp" #include "FrequencyList.hpp" #include "StationList.hpp" #include "NetworkServerLookup.hpp" @@ -182,17 +183,6 @@ namespace { - struct init - { - init () - { - qRegisterMetaType ("Configuration::DataMode"); - qRegisterMetaTypeStreamOperators ("Configuration::DataMode"); - qRegisterMetaType ("Configuration::Type2MsgGen"); - qRegisterMetaTypeStreamOperators ("Configuration::Type2MsgGen"); - } - } static_initializer; - // these undocumented flag values when stored in (Qt::UserRole - 1) // of a ComboBox item model index allow the item to be enabled or // disabled @@ -210,14 +200,17 @@ class FrequencyDialog final : public QDialog { public: - using Frequency = Radio::Frequency; + using Item = FrequencyList::Item; - explicit FrequencyDialog (QWidget * parent = nullptr) + explicit FrequencyDialog (Modes * modes_model, QWidget * parent = nullptr) : QDialog {parent} { setWindowTitle (QApplication::applicationName () + " - " + tr ("Add Frequency")); + mode_combo_box_.setModel (modes_model); + auto form_layout = new QFormLayout (); + form_layout->addRow (tr ("&Mode:"), &mode_combo_box_); form_layout->addRow (tr ("&Frequency (MHz):"), &frequency_line_edit_); auto main_layout = new QVBoxLayout (this); @@ -230,12 +223,13 @@ public: connect (button_box, &QDialogButtonBox::rejected, this, &FrequencyDialog::reject); } - Frequency frequency () const + Item item () const { - return frequency_line_edit_.frequency (); + return {frequency_line_edit_.frequency (), Modes::value (mode_combo_box_.currentText ())}; } private: + QComboBox mode_combo_box_; FrequencyLineEdit frequency_line_edit_; }; @@ -249,7 +243,7 @@ class StationDialog final public: explicit StationDialog (StationList const * stations, Bands * bands, QWidget * parent = nullptr) : QDialog {parent} - , filtered_bands_ {new CandidateKeyFilter {stations, bands}} + , filtered_bands_ {new CandidateKeyFilter {bands, stations, 0, 0}} { setWindowTitle (QApplication::applicationName () + " - " + tr ("Add Station")); @@ -311,6 +305,7 @@ public: }; +// // Class MessageItemDelegate // // Item delegate for message entry such as free text message macros. @@ -336,7 +331,6 @@ public: } }; - // Internal implementation of the Configuration class. class Configuration::impl final : public QDialog @@ -416,6 +410,7 @@ private: void delete_selected_macros (QModelIndexList); Q_SLOT void on_save_path_select_push_button_clicked (bool); Q_SLOT void delete_frequencies (); + Q_SLOT void on_reset_frequencies_push_button_clicked (bool); Q_SLOT void insert_frequency (); Q_SLOT void delete_stations (); Q_SLOT void insert_station (); @@ -469,8 +464,9 @@ private: QStringListModel macros_; RearrangableMacrosModel next_macros_; QAction * macro_delete_action_; - + Bands bands_; + Modes modes_; FrequencyList frequencies_; FrequencyList next_frequencies_; StationList stations_; @@ -697,24 +693,17 @@ void Configuration::sync_transceiver (bool force_signal, bool enforce_mode_and_s m_->sync_transceiver (force_signal); } - Configuration::impl::impl (Configuration * self, QSettings * settings, QWidget * parent) : QDialog {parent} , self_ {self} , ui_ {new Ui::configuration_dialog} , settings_ {settings} , doc_dir_ {QApplication::applicationDirPath ()} - , frequencies_ { - { 136000, 136130, 474200, 1836600, 1838000, 3576000, 3592600, 5287200, 5357000, - 7038600, 7076000, 10138000, 10138700, 14076000, 14095600, 18102000, 18104600, - 21076000, 21094600, 24917000, 24924600, 28076000, 28124600, 50276000, 50293000, - 70091000, 144000000, 144489000, 222000000, 432000000, 432300000, - 902000000, 1296000000, 1296500000, 2301000000, 2304000000, 2320000000, 3400000000, - 3456000000, 5760000000,10368000000, 24048000000 } - } + , frequencies_ {&bands_} + , next_frequencies_ {&bands_} , stations_ {&bands_} , next_stations_ {&bands_} - , frequency_dialog_ {new FrequencyDialog {this}} + , frequency_dialog_ {new FrequencyDialog {&modes_, this}} , station_dialog_ {new StationDialog {&next_stations_, &bands_, this}} , rig_active_ {false} , have_rig_ {false} @@ -891,12 +880,12 @@ Configuration::impl::impl (Configuration * self, QSettings * settings, QWidget * // // setup working frequencies table model & view // - frequencies_.sort (0); + frequencies_.sort (FrequencyList::frequency_column); ui_->frequencies_table_view->setModel (&next_frequencies_); - ui_->frequencies_table_view->sortByColumn (0, Qt::AscendingOrder); - ui_->frequencies_table_view->setItemDelegateForColumn (0, new FrequencyItemDelegate {&bands_, this}); - ui_->frequencies_table_view->setColumnHidden (1, true); + ui_->frequencies_table_view->sortByColumn (FrequencyList::frequency_column, Qt::AscendingOrder); + ui_->frequencies_table_view->setItemDelegateForColumn (FrequencyList::mode_column, new ForeignKeyDelegate {&modes_, 0, this}); + ui_->frequencies_table_view->setColumnHidden (FrequencyList::frequency_mhz_column, true); frequency_delete_action_ = new QAction {tr ("&Delete"), ui_->frequencies_table_view}; ui_->frequencies_table_view->insertAction (nullptr, frequency_delete_action_); @@ -910,13 +899,11 @@ Configuration::impl::impl (Configuration * self, QSettings * settings, QWidget * // // setup stations table model & view // - stations_.sort (0); + stations_.sort (StationList::band_column); ui_->stations_table_view->setModel (&next_stations_); - ui_->stations_table_view->sortByColumn (0, Qt::AscendingOrder); - ui_->stations_table_view->setColumnWidth (1, 150); - ui_->stations_table_view->setItemDelegateForColumn (0, new ForeignKeyDelegate {&next_stations_, &bands_, 0, 0, this}); - ui_->stations_table_view->setItemDelegateForColumn (1, new FrequencyDeltaItemDelegate {this}); + ui_->stations_table_view->sortByColumn (StationList::band_column, Qt::AscendingOrder); + ui_->stations_table_view->setItemDelegateForColumn (StationList::band_column, new ForeignKeyDelegate {&bands_, &next_stations_, 0, StationList::band_column, this}); station_delete_action_ = new QAction {tr ("&Delete"), ui_->stations_table_view}; ui_->stations_table_view->insertAction (nullptr, station_delete_action_); @@ -1041,8 +1028,8 @@ void Configuration::impl::initialize_models () } next_macros_.setStringList (macros_.stringList ()); - next_frequencies_ = frequencies_.frequencies (); - next_stations_ = stations_.stations (); + next_frequencies_.frequency_list (frequencies_.frequency_list ()); + next_stations_.station_list (stations_.station_list ()); set_rig_invariants (); } @@ -1170,10 +1157,10 @@ void Configuration::impl::read_settings () if (settings_->contains ("frequencies")) { - frequencies_ = settings_->value ("frequencies").value (); + frequencies_.frequency_list (settings_->value ("frequencies").value ()); } - stations_ = settings_->value ("stations").value (); + stations_.station_list (settings_->value ("stations").value ()); log_as_RTTY_ = settings_->value ("toRTTY", false).toBool (); report_in_comments_ = settings_->value("dBtoComments", false).toBool (); @@ -1262,8 +1249,8 @@ void Configuration::impl::write_settings () settings_->setValue ("After73", id_after_73_); settings_->setValue ("TxQSYAllowed", tx_QSY_allowed_); settings_->setValue ("Macros", macros_.stringList ()); - settings_->setValue ("frequencies", QVariant::fromValue (frequencies_.frequencies ())); - settings_->setValue ("stations", QVariant::fromValue (stations_.stations ())); + settings_->setValue ("frequencies", QVariant::fromValue (frequencies_.frequency_list ())); + settings_->setValue ("stations", QVariant::fromValue (stations_.station_list ())); settings_->setValue ("toRTTY", log_as_RTTY_); settings_->setValue ("dBtoComments", report_in_comments_); settings_->setValue ("Rig", rig_params_.rig_name); @@ -1674,16 +1661,16 @@ void Configuration::impl::accept () macros_.setStringList (next_macros_.stringList ()); } - if (frequencies_.frequencies () != next_frequencies_.frequencies ()) + if (frequencies_.frequency_list () != next_frequencies_.frequency_list ()) { - frequencies_ = next_frequencies_.frequencies (); - frequencies_.sort (0); + frequencies_.frequency_list (next_frequencies_.frequency_list ()); + frequencies_.sort (FrequencyList::frequency_column); } - if (stations_.stations () != next_stations_.stations ()) + if (stations_.station_list () != next_stations_.station_list ()) { - stations_ = next_stations_.stations (); - stations_.sort (0); + stations_.station_list(next_stations_.station_list ()); + stations_.sort (StationList::band_column); } write_settings (); // make visible to all @@ -1975,13 +1962,26 @@ void Configuration::impl::delete_frequencies () auto selection_model = ui_->frequencies_table_view->selectionModel (); selection_model->select (selection_model->selection (), QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); next_frequencies_.removeDisjointRows (selection_model->selectedRows ()); + ui_->frequencies_table_view->resizeColumnToContents (FrequencyList::mode_column); +} + +void Configuration::impl::on_reset_frequencies_push_button_clicked (bool /* checked */) +{ + if (QMessageBox::Yes == QMessageBox::question (this, tr ("Reset Working Frequencies") + , tr ("Are you sure you want to discard your current " + "working frequencies and replace them with default " + "ones?"))) + { + next_frequencies_.reset_to_defaults (); + } } void Configuration::impl::insert_frequency () { if (QDialog::Accepted == frequency_dialog_->exec ()) { - ui_->frequencies_table_view->setCurrentIndex (next_frequencies_.add (frequency_dialog_->frequency ())); + ui_->frequencies_table_view->setCurrentIndex (next_frequencies_.add (frequency_dialog_->item ())); + ui_->frequencies_table_view->resizeColumnToContents (FrequencyList::mode_column); } } @@ -1990,6 +1990,8 @@ void Configuration::impl::delete_stations () auto selection_model = ui_->stations_table_view->selectionModel (); selection_model->select (selection_model->selection (), QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); next_stations_.removeDisjointRows (selection_model->selectedRows ()); + ui_->stations_table_view->resizeColumnToContents (StationList::band_column); + ui_->stations_table_view->resizeColumnToContents (StationList::offset_column); } void Configuration::impl::insert_station () @@ -1997,6 +1999,8 @@ void Configuration::impl::insert_station () if (QDialog::Accepted == station_dialog_->exec ()) { ui_->stations_table_view->setCurrentIndex (next_stations_.add (station_dialog_->station ())); + ui_->stations_table_view->resizeColumnToContents (StationList::band_column); + ui_->stations_table_view->resizeColumnToContents (StationList::offset_column); } } diff --git a/Configuration.ui b/Configuration.ui index f264b2c4e..f2647f624 100644 --- a/Configuration.ui +++ b/Configuration.ui @@ -1807,9 +1807,15 @@ for assessing propagation and system performance. Working Frequencies - - + + + + + 1 + 0 + + Qt::ActionsContextMenu @@ -1836,113 +1842,128 @@ for assessing propagation and system performance. - - - - See WSPR documentattion Appendix C for details of how to determine these factors for your radio. - - - Frequency Calibration - - - - QFormLayout::AllNonFixedFieldsGrow - - - - - Intercept: + + + + + + + + Reset + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + See WSPR documentattion Appendix C for details of how to determine these factors for your radio. + + + Frequency Calibration + + + + QFormLayout::AllNonFixedFieldsGrow - - calibration_intercept_spin_box - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - Hz - - - 2 - - - -99999.990000000005239 - - - 99999.990000000005239 - - - 0.100000000000000 - - - - - - - Slope: - - - calibration_slope_ppm_spin_box - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - ppm - - - 4 - - - -999.999900000000025 - - - 999.999900000000025 - - - 0.100000000000000 - - - 0.000000000000000 - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - + + + + Intercept: + + + calibration_intercept_spin_box + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + Hz + + + 2 + + + -99999.990000000005239 + + + 99999.990000000005239 + + + 0.100000000000000 + + + + + + + Slope: + + + calibration_slope_ppm_spin_box + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + ppm + + + 4 + + + -999.999900000000025 + + + 999.999900000000025 + + + 0.100000000000000 + + + 0.000000000000000 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + @@ -1992,6 +2013,9 @@ Right click for insert and delete options. true + + true + true @@ -2274,6 +2298,7 @@ soundcard changes udpWindowToFront udpWindowRestore frequencies_table_view + reset_frequencies_push_button calibration_intercept_spin_box calibration_slope_ppm_spin_box stations_table_view @@ -2351,12 +2376,12 @@ soundcard changes - - - - - + + + + + diff --git a/ForeignKeyDelegate.cpp b/ForeignKeyDelegate.cpp index 18817e74f..03f918213 100644 --- a/ForeignKeyDelegate.cpp +++ b/ForeignKeyDelegate.cpp @@ -4,15 +4,24 @@ #include "CandidateKeyFilter.hpp" -ForeignKeyDelegate::ForeignKeyDelegate (QAbstractItemModel const * referencing_model - , QAbstractItemModel * referenced_model - , int referencing_key_column +ForeignKeyDelegate::ForeignKeyDelegate (QAbstractItemModel * referenced_model , int referenced_key_column , QObject * parent - , int referencing_key_role , int referenced_key_role) : QStyledItemDelegate {parent} - , candidate_key_filter_ {new CandidateKeyFilter {referencing_model, referenced_model, referencing_key_column, referenced_key_column, referencing_key_role, referenced_key_role}} + , candidate_key_filter_ {new CandidateKeyFilter {referenced_model, referenced_key_column, nullptr, referenced_key_role}} +{ +} + +ForeignKeyDelegate::ForeignKeyDelegate (QAbstractItemModel * referenced_model + , QAbstractItemModel const * referencing_model + , int referenced_key_column + , int referencing_key_column + , QObject * parent + , int referenced_key_role + , int referencing_key_role) + : QStyledItemDelegate {parent} + , candidate_key_filter_ {new CandidateKeyFilter {referenced_model, referencing_model, referenced_key_column, referencing_key_column, nullptr, referenced_key_role, referencing_key_role}} { } diff --git a/ForeignKeyDelegate.hpp b/ForeignKeyDelegate.hpp index 9e0dc06ab..2ffb0e85b 100644 --- a/ForeignKeyDelegate.hpp +++ b/ForeignKeyDelegate.hpp @@ -17,13 +17,20 @@ class ForeignKeyDelegate final : public QStyledItemDelegate { public: - explicit ForeignKeyDelegate (QAbstractItemModel const * referencing_model - , QAbstractItemModel * referenced_model - , int referencing_key_column = 0 - , int referenced_key_column = 0 + // many to many relationship + explicit ForeignKeyDelegate (QAbstractItemModel * referenced_model + , int referenced_key_column , QObject * parent = nullptr - , int referencing_key_role = Qt::EditRole , int referenced_key_role = Qt::EditRole); + + // one to many (referenced to referencing) relationship + explicit ForeignKeyDelegate (QAbstractItemModel * referenced_model + , QAbstractItemModel const * referencing_model + , int referenced_key_column + , int referencing_key_column + , QObject * parent = nullptr + , int referenced_key_role = Qt::EditRole + , int referencing_key_role = Qt::EditRole); ~ForeignKeyDelegate (); QWidget * createEditor (QWidget * parent, QStyleOptionViewItem const&, QModelIndex const&) const override; diff --git a/FrequencyItemDelegate.cpp b/FrequencyItemDelegate.cpp index 8e9ebd5dc..8ae72416b 100644 --- a/FrequencyItemDelegate.cpp +++ b/FrequencyItemDelegate.cpp @@ -1,15 +1,6 @@ #include "FrequencyItemDelegate.hpp" -#include "Radio.hpp" #include "FrequencyLineEdit.hpp" -#include "Bands.hpp" - -QString FrequencyItemDelegate::displayText (QVariant const& value, QLocale const& locale) const -{ - auto frequency = value.value (); - auto band_name = bands_->data (bands_->find (frequency)); - return Radio::pretty_frequency_MHz_string (frequency, locale) + " MHz (" + band_name.toString () + ')'; -} QWidget * FrequencyItemDelegate::createEditor (QWidget * parent , QStyleOptionViewItem const& /* option */ @@ -21,11 +12,6 @@ QWidget * FrequencyItemDelegate::createEditor (QWidget * parent } -QString FrequencyDeltaItemDelegate::displayText (QVariant const& value, QLocale const& locale) const -{ - return Radio::pretty_frequency_MHz_string (value.value (), locale) + " MHz"; -} - QWidget * FrequencyDeltaItemDelegate::createEditor (QWidget * parent , QStyleOptionViewItem const& /* option */ , QModelIndex const& /* index */) const diff --git a/FrequencyItemDelegate.hpp b/FrequencyItemDelegate.hpp index 55ec3b099..138b577c6 100644 --- a/FrequencyItemDelegate.hpp +++ b/FrequencyItemDelegate.hpp @@ -1,53 +1,45 @@ -#ifndef FREQUENCY_ITEM_DELEGATE_HPP_ -#define FREQUENCY_ITEM_DELEGATE_HPP_ - -#include - -class Bands; - -// -// Class FrequencyItemDelegate -// -// Item delegate for displaying and editing a Frequency item in a -// view that uses a FrequencyLineEdit as an item delegate for the -// edit role. -// -class FrequencyItemDelegate final - : public QStyledItemDelegate -{ -public: - explicit FrequencyItemDelegate (Bands const * bands, QObject * parent = nullptr) - : QStyledItemDelegate {parent} - , bands_ {bands} - { - } - - QString displayText (QVariant const& value, QLocale const&) const override; - QWidget * createEditor (QWidget * parent, QStyleOptionViewItem const&, QModelIndex const&) const override; - -private: - Bands const * bands_; -}; - - -// -// Class FrequencyDeltaItemDelegate -// -// Item delegate for displaying and editing a FrequencyDelta item -// in a view that uses a FrequencyDeltaLineEdit as an item -// delegate for the edit role. -// -class FrequencyDeltaItemDelegate final - : public QStyledItemDelegate -{ -public: - explicit FrequencyDeltaItemDelegate (QObject * parent = nullptr) - : QStyledItemDelegate {parent} - { - } - - QString displayText (QVariant const& value, QLocale const&) const override; - QWidget * createEditor (QWidget * parent, QStyleOptionViewItem const&, QModelIndex const&) const override; -}; - -#endif +#ifndef FREQUENCY_ITEM_DELEGATE_HPP_ +#define FREQUENCY_ITEM_DELEGATE_HPP_ + +#include + +// +// Class FrequencyItemDelegate +// +// Item delegate for displaying and editing a Frequency item in a +// view that uses a FrequencyLineEdit as an item delegate for the +// edit role. +// +class FrequencyItemDelegate final + : public QStyledItemDelegate +{ +public: + explicit FrequencyItemDelegate (QObject * parent = nullptr) + : QStyledItemDelegate {parent} + { + } + + QWidget * createEditor (QWidget * parent, QStyleOptionViewItem const&, QModelIndex const&) const override; +}; + + +// +// Class FrequencyDeltaItemDelegate +// +// Item delegate for displaying and editing a FrequencyDelta item +// in a view that uses a FrequencyDeltaLineEdit as an item +// delegate for the edit role. +// +class FrequencyDeltaItemDelegate final + : public QStyledItemDelegate +{ +public: + explicit FrequencyDeltaItemDelegate (QObject * parent = nullptr) + : QStyledItemDelegate {parent} + { + } + + QWidget * createEditor (QWidget * parent, QStyleOptionViewItem const&, QModelIndex const&) const override; +}; + +#endif diff --git a/FrequencyList.cpp b/FrequencyList.cpp index 76cac1f15..bf820188b 100644 --- a/FrequencyList.cpp +++ b/FrequencyList.cpp @@ -11,25 +11,120 @@ #include #include #include -#include +#include "Bands.hpp" #include "pimpl_impl.hpp" +namespace +{ + FrequencyList::FrequencyItems const default_frequency_list = + { + {136000, Modes::WSPR}, + {136130, Modes::JT65}, + {474200, Modes::JT65}, + {474200, Modes::JT9}, + {474200, Modes::WSPR}, + {1836600, Modes::WSPR}, + {1838000, Modes::JT65}, + {1840000, Modes::JT9}, + {3576000, Modes::JT65}, + {3578000, Modes::JT9}, + {3559260, Modes::WSPR}, + {5357000, Modes::JT65}, + {5287200, Modes::WSPR}, + {7038600, Modes::WSPR}, + {7076000, Modes::JT65}, + {7078000, Modes::JT9}, + {10138000, Modes::JT65}, + {10138700, Modes::WSPR}, + {10140000, Modes::JT9}, + {14095600, Modes::WSPR}, + {14076000, Modes::JT65}, + {14078000, Modes::JT9}, + {18102000, Modes::JT65}, + {18104000, Modes::JT9}, + {18104460, Modes::WSPR}, + {21076000, Modes::JT65}, + {21078000, Modes::JT9}, + {21094600, Modes::WSPR}, + {24917000, Modes::JT65}, + {24919000, Modes::JT9}, + {24924600, Modes::WSPR}, + {28076000, Modes::JT65}, + {28078000, Modes::JT9}, + {28124600, Modes::WSPR}, + {50276000, Modes::JT65}, + {50293000, Modes::WSPR}, + {70091000, Modes::JT65}, + {70091000, Modes::WSPR}, + {144000000, Modes::JT4}, + {144489000, Modes::JT65}, + {144489000, Modes::WSPR}, + {222000000, Modes::JT4}, + {222000000, Modes::JT65}, + {432000000, Modes::JT4}, + {432000000, Modes::JT65}, + {432300000, Modes::WSPR}, + {902000000, Modes::JT4}, + {902000000, Modes::JT65}, + {1296000000, Modes::JT4}, + {1296000000, Modes::JT65}, + {1296500000, Modes::WSPR}, + {2301000000, Modes::JT4}, + {2301000000, Modes::JT65}, + {2304000000, Modes::JT4}, + {2304000000, Modes::JT65}, + {2320000000, Modes::JT4}, + {2320000000, Modes::JT65}, + {3400000000, Modes::JT4}, + {3400000000, Modes::JT65}, + {3456000000, Modes::JT4}, + {3456000000, Modes::JT65}, + {5760000000, Modes::JT4}, + {5760000000, Modes::JT65}, + {10368000000, Modes::JT4}, + {10368000000, Modes::JT65}, + {24048000000, Modes::JT4}, + {24048000000, Modes::JT65}, + }; +} + +#if !defined (QT_NO_DEBUG_STREAM) +QDebug operator << (QDebug debug, FrequencyList::Item const& item) +{ + debug.nospace () << "FrequencyItem(" + << item.frequency_ << ", " + << item.mode_ << ')'; + return debug.space (); +} +#endif + +QDataStream& operator << (QDataStream& os, FrequencyList::Item const& item) +{ + return os << item.frequency_ + << item.mode_; +} + +QDataStream& operator >> (QDataStream& is, FrequencyList::Item& item) +{ + return is >> item.frequency_ + >> item.mode_; +} + class FrequencyList::impl final : public QAbstractTableModel { public: - impl (Frequencies frequencies, QObject * parent) + impl (Bands const * bands, QObject * parent) : QAbstractTableModel {parent} - , frequencies_ {frequencies} + , bands_ {bands} + , mode_filter_ {Modes::NULL_MODE} { } - Frequencies const& frequencies () const {return frequencies_;} - void assign (Frequencies); - QModelIndex add (Frequency); + FrequencyItems frequency_list (FrequencyItems); + QModelIndex add (Item); -protected: // Implement the QAbstractTableModel interface int rowCount (QModelIndex const& parent = QModelIndex {}) const override; int columnCount (QModelIndex const& parent = QModelIndex {}) const override; @@ -42,21 +137,17 @@ protected: QStringList mimeTypes () const override; QMimeData * mimeData (QModelIndexList const&) const override; -private: - static int constexpr num_cols {2}; + static int constexpr num_cols {3}; static auto constexpr mime_type ="application/wsjt.Frequencies"; - Frequencies frequencies_; + Bands const * bands_; + FrequencyItems frequency_list_; + Mode mode_filter_; }; -FrequencyList::FrequencyList (QObject * parent) - : FrequencyList {{}, parent} -{ -} - -FrequencyList::FrequencyList (Frequencies frequencies, QObject * parent) +FrequencyList::FrequencyList (Bands const * bands, QObject * parent) : QSortFilterProxyModel {parent} - , m_ {frequencies, parent} + , m_ {bands, parent} { setSourceModel (&*m_); setSortRole (SortRole); @@ -66,25 +157,52 @@ FrequencyList::~FrequencyList () { } -FrequencyList& FrequencyList::operator = (Frequencies frequencies) +auto FrequencyList::frequency_list (FrequencyItems frequency_list) -> FrequencyItems { - m_->assign (frequencies); - return *this; + return m_->frequency_list (frequency_list); } -auto FrequencyList::frequencies () const -> Frequencies +auto FrequencyList::frequency_list () const -> FrequencyItems const& { - return m_->frequencies (); + return m_->frequency_list_; } -QModelIndex FrequencyList::add (Frequency f) +QModelIndex FrequencyList::best_working_frequency (Frequency f, Mode mode) const +{ + auto const& target_band = m_->bands_->find (f); + if (target_band != m_->bands_->out_of_band ()) + { + // find a frequency in the same band that is allowed for the + // target mode + for (int row = 0; row < rowCount (); ++row) + { + auto const& source_row = mapToSource (index (row, 0)).row (); + auto const& band = m_->bands_->find (m_->frequency_list_[source_row].frequency_); + if (band->name_ == target_band->name_) + { + if (m_->frequency_list_[source_row].mode_ == mode) + { + return index (row, 0); + } + } + } + } + return QModelIndex {}; +} + +void FrequencyList::reset_to_defaults () +{ + m_->frequency_list (default_frequency_list); +} + +QModelIndex FrequencyList::add (Item f) { return mapFromSource (m_->add (f)); } -bool FrequencyList::remove (Frequency f) +bool FrequencyList::remove (Item f) { - auto row = m_->frequencies ().indexOf (f); + auto row = m_->frequency_list_.indexOf (f); if (0 > row) { @@ -123,38 +241,54 @@ bool FrequencyList::removeDisjointRows (QModelIndexList rows) result = false; } } + return result; +} +void FrequencyList::filter (Mode mode) +{ + m_->mode_filter_ = mode; + invalidateFilter (); +} + +bool FrequencyList::filterAcceptsRow (int source_row, QModelIndex const& /* parent */) const +{ + bool result {true}; + if (m_->mode_filter_ != Modes::NULL_MODE) + { + auto const& item = m_->frequency_list_[source_row]; + result = item.mode_ == Modes::NULL_MODE || m_->mode_filter_ == item.mode_; + } return result; } -void FrequencyList::impl::assign (Frequencies frequencies) +auto FrequencyList::impl::frequency_list (FrequencyItems frequency_list) -> FrequencyItems { beginResetModel (); - std::swap (frequencies_, frequencies); + std::swap (frequency_list_, frequency_list); endResetModel (); + return frequency_list; } -QModelIndex FrequencyList::impl::add (Frequency f) +QModelIndex FrequencyList::impl::add (Item f) { // Any Frequency that isn't in the list may be added - if (!frequencies_.contains (f)) + if (!frequency_list_.contains (f)) { - auto row = frequencies_.size (); + auto row = frequency_list_.size (); beginInsertRows (QModelIndex {}, row, row); - frequencies_.append (f); + frequency_list_.append (f); endInsertRows (); return index (row, 0); } - return QModelIndex {}; } int FrequencyList::impl::rowCount (QModelIndex const& parent) const { - return parent.isValid () ? 0 : frequencies_.size (); + return parent.isValid () ? 0 : frequency_list_.size (); } int FrequencyList::impl::columnCount (QModelIndex const& parent) const @@ -165,26 +299,17 @@ int FrequencyList::impl::columnCount (QModelIndex const& parent) const Qt::ItemFlags FrequencyList::impl::flags (QModelIndex const& index) const { auto result = QAbstractTableModel::flags (index) | Qt::ItemIsDropEnabled; - auto row = index.row (); auto column = index.column (); - if (index.isValid () - && row < frequencies_.size () + && row < frequency_list_.size () && column < num_cols) { - switch (column) + if (frequency_mhz_column != column) { - case 0: - result |= Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; - break; - - case 1: - result |= Qt::ItemIsDragEnabled; - break; + result |= Qt::ItemIsEditable | Qt::ItemIsDragEnabled; } } - return result; } @@ -192,25 +317,51 @@ QVariant FrequencyList::impl::data (QModelIndex const& index, int role) const { QVariant item; - auto row = index.row (); - auto column = index.column (); + auto const& row = index.row (); + auto const& column = index.column (); if (index.isValid () - && row < frequencies_.size () + && row < frequency_list_.size () && column < num_cols) { - auto frequency = frequencies_.at (row); - + auto const& frequency_item = frequency_list_.at (row); switch (column) { - case 0: + case mode_column: switch (role) { case SortRole: case Qt::DisplayRole: case Qt::EditRole: case Qt::AccessibleTextRole: - item = frequency; + item = Modes::name (frequency_item.mode_); + break; + + case Qt::ToolTipRole: + case Qt::AccessibleDescriptionRole: + item = tr ("Mode"); + break; + + case Qt::TextAlignmentRole: + item = Qt::AlignHCenter + Qt::AlignVCenter; + break; + } + break; + + case frequency_column: + switch (role) + { + case SortRole: + case Qt::EditRole: + case Qt::AccessibleTextRole: + item = frequency_item.frequency_; + break; + + case Qt::DisplayRole: + { + auto const& band = bands_->find (frequency_item.frequency_); + item = Radio::pretty_frequency_MHz_string (frequency_item.frequency_) + " MHz (" + band->name_ + ')'; + } break; case Qt::ToolTipRole: @@ -224,22 +375,24 @@ QVariant FrequencyList::impl::data (QModelIndex const& index, int role) const } break; - case 1: + case frequency_mhz_column: switch (role) { - case Qt::DisplayRole: case Qt::EditRole: case Qt::AccessibleTextRole: - item = static_cast (frequency / 1.e6); + item = frequency_item.frequency_ / 1.e6; break; - case SortRole: // use the underlying Frequency value - item = frequency; + case Qt::DisplayRole: + { + auto const& band = bands_->find (frequency_item.frequency_); + item = Radio::pretty_frequency_MHz_string (frequency_item.frequency_) + " MHz (" + band->name_ + ')'; + } break; case Qt::ToolTipRole: case Qt::AccessibleDescriptionRole: - item = tr ("Frequency MHz"); + item = tr ("Frequency (MHz)"); break; case Qt::TextAlignmentRole: @@ -249,7 +402,6 @@ QVariant FrequencyList::impl::data (QModelIndex const& index, int role) const break; } } - return item; } @@ -257,21 +409,44 @@ bool FrequencyList::impl::setData (QModelIndex const& model_index, QVariant cons { bool changed {false}; - auto row = model_index.row (); + auto const& row = model_index.row (); if (model_index.isValid () && Qt::EditRole == role - && row < frequencies_.size () - && 0 == model_index.column () - && value.canConvert ()) + && row < frequency_list_.size ()) { - auto frequency = value.value (); - auto original_frequency = frequencies_.at (row); - if (frequency != original_frequency) + QVector roles; + roles << role; + + auto& item = frequency_list_[row]; + switch (model_index.column ()) { - frequencies_.replace (row, frequency); - Q_EMIT dataChanged (model_index, index (model_index.row (), 1), QVector {} << role); + case mode_column: + if (value.canConvert ()) + { + auto mode = Modes::value (value.toString ()); + if (mode != item.mode_) + { + item.mode_ = mode; + Q_EMIT dataChanged (model_index, model_index, roles); + changed = true; + } + } + break; + + case frequency_column: + if (value.canConvert ()) + { + auto frequency = value.value (); + if (frequency != item.frequency_) + { + item.frequency_ = frequency; + // mark derived column (1) changed as well + Q_EMIT dataChanged (index (model_index.row (), 1), model_index, roles); + changed = true; + } + } + break; } - changed = true; } return changed; @@ -280,22 +455,21 @@ bool FrequencyList::impl::setData (QModelIndex const& model_index, QVariant cons QVariant FrequencyList::impl::headerData (int section, Qt::Orientation orientation, int role) const { QVariant header; - if (Qt::DisplayRole == role && Qt::Horizontal == orientation && section < num_cols) { switch (section) { - case 0: header = tr ("Frequency"); break; - case 1: header = tr ("Frequency (MHz)"); break; + case mode_column: header = tr ("Mode"); break; + case frequency_column: header = tr ("Frequency"); break; + case frequency_mhz_column: header = tr ("Frequency (MHz)"); break; } } else { header = QAbstractTableModel::headerData (section, orientation, role); } - return header; } @@ -306,12 +480,11 @@ bool FrequencyList::impl::removeRows (int row, int count, QModelIndex const& par beginRemoveRows (parent, row, row + count - 1); for (auto r = 0; r < count; ++r) { - frequencies_.removeAt (row); + frequency_list_.removeAt (row); } endRemoveRows (); return true; } - return false; } @@ -322,12 +495,11 @@ bool FrequencyList::impl::insertRows (int row, int count, QModelIndex const& par beginInsertRows (parent, row, row + count - 1); for (auto r = 0; r < count; ++r) { - frequencies_.insert (row, Frequency {}); + frequency_list_.insert (row, Item {0, Mode::NULL_MODE}); } endInsertRows (); return true; } - return false; } @@ -346,7 +518,7 @@ QMimeData * FrequencyList::impl::mimeData (QModelIndexList const& items) const Q_FOREACH (auto const& item, items) { - if (item.isValid ()) + if (item.isValid () && frequency_column == item.column ()) { stream << QString {data (item, Qt::DisplayRole).toString ()}; } diff --git a/FrequencyList.hpp b/FrequencyList.hpp index c22659231..dd0bb782a 100644 --- a/FrequencyList.hpp +++ b/FrequencyList.hpp @@ -3,9 +3,13 @@ #include "pimpl_h.hpp" +#include #include #include "Radio.hpp" +#include "Modes.hpp" + +class Bands; // // Class FrequencyList @@ -31,23 +35,45 @@ class FrequencyList final : public QSortFilterProxyModel { + Q_OBJECT; + public: using Frequency = Radio::Frequency; - using Frequencies = Radio::Frequencies; + using Mode = Modes::Mode; - explicit FrequencyList (QObject * parent = nullptr); - explicit FrequencyList (Frequencies, QObject * parent = nullptr); + struct Item + { + Frequency frequency_; + Mode mode_; + }; + using FrequencyItems = QList; + + enum Column {mode_column, frequency_column, frequency_mhz_column}; + + explicit FrequencyList (Bands const *, QObject * parent = nullptr); ~FrequencyList (); // Load and store contents - FrequencyList& operator = (Frequencies); - Frequencies frequencies () const; + FrequencyItems frequency_list (FrequencyItems); + FrequencyItems const& frequency_list () const; + + // Find nearest best working frequency given a frequency and mode + QModelIndex best_working_frequency (Frequency, Mode) const; + + // Set filter + void filter (Mode); + + // Reset + Q_SLOT void reset_to_defaults (); // Model API - QModelIndex add (Frequency); - bool remove (Frequency); + QModelIndex add (Item); + bool remove (Item); bool removeDisjointRows (QModelIndexList); + // Proxy API + bool filterAcceptsRow (int source_row, QModelIndex const& parent) const override; + // Custom roles. static int constexpr SortRole = Qt::UserRole; @@ -56,4 +82,22 @@ private: pimpl m_; }; +inline +bool operator == (FrequencyList::Item const& lhs, FrequencyList::Item const& rhs) +{ + return + lhs.frequency_ == rhs.frequency_ + && lhs.mode_ == rhs.mode_; +} + +QDataStream& operator << (QDataStream&, FrequencyList::Item const&); +QDataStream& operator >> (QDataStream&, FrequencyList::Item&); + +#if !defined (QT_NO_DEBUG_STREAM) +QDebug operator << (QDebug, FrequencyList::Item const&); +#endif + +Q_DECLARE_METATYPE (FrequencyList::Item); +Q_DECLARE_METATYPE (FrequencyList::FrequencyItems); + #endif diff --git a/LiveFrequencyValidator.cpp b/LiveFrequencyValidator.cpp index f234122c1..77863c31c 100644 --- a/LiveFrequencyValidator.cpp +++ b/LiveFrequencyValidator.cpp @@ -1,92 +1,87 @@ -#include "LiveFrequencyValidator.hpp" - -#include -#include -#include -#include - -#include "Bands.hpp" -#include "FrequencyList.hpp" - -#include "moc_LiveFrequencyValidator.cpp" - -LiveFrequencyValidator::LiveFrequencyValidator (QComboBox * combo_box - , Bands const * bands - , FrequencyList const * frequencies - , QWidget * parent) - : QRegExpValidator { - QRegExp { // frequency in MHz or band - bands->data (QModelIndex {}).toString () // out of band string - + QString {R"(|((\d{0,6}(\)"} // up to 6 digits - + QLocale {}.decimalPoint () // (followed by decimal separator - + R"(\d{0,2})?)([Mm]{1,2}|([Cc][Mm])))|(\d{0,4}(\)" // followed by up to 2 digits and either 'm' or 'cm' or 'mm' (case insensitive)) - + QLocale {}.decimalPoint () // or a decimal separator - + R"(\d{0,6})?))" // followed by up to 6 digits - } - , parent - } - , bands_ {bands} - , frequencies_ {frequencies} - , combo_box_ {combo_box} -{ -} - -auto LiveFrequencyValidator::validate (QString& input, int& pos) const -> State -{ - auto state = QRegExpValidator::validate (input, pos); - - // by never being Acceptable we force fixup calls on ENTER or - // losing focus - return Acceptable == state ? Intermediate : state; -} - -void LiveFrequencyValidator::fixup (QString& input) const -{ - QRegExpValidator::fixup (input); - - auto out_of_band = bands_->data (QModelIndex {}).toString (); - - if (!out_of_band.startsWith (input)) - { - if (input.contains ('m', Qt::CaseInsensitive)) - { - input = input.toLower (); - - QVector frequencies; - for (int r = 0; r < frequencies_->rowCount (); ++r) - { - auto frequency = frequencies_->index (r, 0).data (); - auto band_index = bands_->find (frequency); - if (band_index.data ().toString () == input) - { - frequencies << frequency; - } - } - if (!frequencies.isEmpty ()) - { - Q_EMIT valid (frequencies.first ().value ()); - } - else - { - input = QString {}; - } - } - else - { - // frequency input - auto f = Radio::frequency (input, 6); - input = bands_->data (bands_->find (f)).toString (); - Q_EMIT valid (f); - } - - if (out_of_band == input) - { - combo_box_->lineEdit ()->setStyleSheet ("QLineEdit {color: yellow; background-color : red;}"); - } - else - { - combo_box_->lineEdit ()->setStyleSheet ({}); - } - combo_box_->setCurrentText (input); - } -} +#include "LiveFrequencyValidator.hpp" + +#include +#include +#include +#include + +#include "Bands.hpp" +#include "FrequencyList.hpp" + +#include "moc_LiveFrequencyValidator.cpp" + +LiveFrequencyValidator::LiveFrequencyValidator (QComboBox * combo_box + , Bands const * bands + , FrequencyList const * frequencies + , QWidget * parent) + : QRegExpValidator { + QRegExp { // frequency in MHz or band + bands->data (QModelIndex {}).toString () // out of band string + + QString {R"(|((\d{0,6}(\)"} // or up to 6 digits + + QLocale {}.decimalPoint () // (followed by decimal separator + + R"(\d{0,2})?)([Mm]{1,2}|([Cc][Mm])))|(\d{0,4}(\)" // followed by up to 2 digits and either 'm' or 'cm' or 'mm' (case insensitive)) + + QLocale {}.decimalPoint () // or a decimal separator + + R"(\d{0,6})?))" // followed by up to 6 digits + } + , parent + } + , bands_ {bands} + , frequencies_ {frequencies} + , combo_box_ {combo_box} +{ +} + +auto LiveFrequencyValidator::validate (QString& input, int& pos) const -> State +{ + auto state = QRegExpValidator::validate (input, pos); + // by never being Acceptable we force fixup calls on ENTER or + // losing focus + return Acceptable == state ? Intermediate : state; +} + +void LiveFrequencyValidator::fixup (QString& input) const +{ + QRegExpValidator::fixup (input); + auto const& out_of_band = bands_->out_of_band (); + if (!QString {out_of_band->name_}.startsWith (input)) + { + if (input.contains ('m', Qt::CaseInsensitive)) + { + input = input.toLower (); + + QVector frequencies; + Q_FOREACH (auto const& item, frequencies_->frequency_list ()) + { + if (bands_->find (item.frequency_)->name_ == input) + { + frequencies << item.frequency_; + } + } + if (!frequencies.isEmpty ()) + { + Q_EMIT valid (frequencies.first ().value ()); + } + else + { + input = QString {}; + } + } + else + { + // frequency input + auto f = Radio::frequency (input, 6); + input = bands_->find (f)->name_; + Q_EMIT valid (f); + } + + if (bands_->out_of_band ()->name_ == input) + { + combo_box_->lineEdit ()->setStyleSheet ("QLineEdit {color: yellow; background-color : red;}"); + } + else + { + combo_box_->lineEdit ()->setStyleSheet ({}); + } + combo_box_->setCurrentText (input); + } +} diff --git a/MetaDataRegistry.cpp b/MetaDataRegistry.cpp new file mode 100644 index 000000000..94664cbce --- /dev/null +++ b/MetaDataRegistry.cpp @@ -0,0 +1,72 @@ +#include "MetaDataRegistry.hpp" + +#include +#include +#include + +#include "Radio.hpp" +#include "FrequencyList.hpp" +#include "AudioDevice.hpp" +#include "Configuration.hpp" +#include "StationList.hpp" +#include "Transceiver.hpp" +#include "TransceiverFactory.hpp" +#include "WFPalette.hpp" + +#include "FrequencyLineEdit.hpp" + +void register_types () +{ + // Default custom item view delegates + auto factory = new QItemEditorFactory; + + // Radio namespace + auto frequency_type_id = qRegisterMetaType ("Frequency"); + factory->registerEditor (frequency_type_id, new QStandardItemEditorCreator ()); + auto frequency_delta_type_id = qRegisterMetaType ("FrequencyDelta"); + factory->registerEditor (frequency_delta_type_id, new QStandardItemEditorCreator ()); + + // Frequency list model + qRegisterMetaType ("Item"); + qRegisterMetaTypeStreamOperators ("Item"); + qRegisterMetaType ("FrequencyItems"); + qRegisterMetaTypeStreamOperators ("FrequencyItems"); + + // Audio device + qRegisterMetaType ("AudioDevice::Channel"); + + // Configuration + qRegisterMetaType ("Configuration::DataMode"); + qRegisterMetaTypeStreamOperators ("Configuration::DataMode"); + qRegisterMetaType ("Configuration::Type2MsgGen"); + qRegisterMetaTypeStreamOperators ("Configuration::Type2MsgGen"); + + // Station details + qRegisterMetaType ("Station"); + qRegisterMetaTypeStreamOperators ("Station"); + qRegisterMetaType ("Stations"); + qRegisterMetaTypeStreamOperators ("Stations"); + + // Transceiver + qRegisterMetaType ("Transceiver::TransceiverState"); + qRegisterMetaType ("Transceiver::MODE"); + + // Transceiver factory + qRegisterMetaType ("TransceiverFactory::DataBits"); + qRegisterMetaTypeStreamOperators ("TransceiverFactory::DataBits"); + qRegisterMetaType ("TransceiverFactory::StopBits"); + qRegisterMetaTypeStreamOperators ("TransceiverFactory::StopBits"); + qRegisterMetaType ("TransceiverFactory::Handshake"); + qRegisterMetaTypeStreamOperators ("TransceiverFactory::Handshake"); + qRegisterMetaType ("TransceiverFactory::PTTMethod"); + qRegisterMetaTypeStreamOperators ("TransceiverFactory::PTTMethod"); + qRegisterMetaType ("TransceiverFactory::TXAudioSource"); + qRegisterMetaTypeStreamOperators ("TransceiverFactory::TXAudioSource"); + qRegisterMetaType ("TransceiverFactory::SplitMode"); + qRegisterMetaTypeStreamOperators ("TransceiverFactory::SplitMode"); + + // Waterfall palette + qRegisterMetaTypeStreamOperators ("Colours"); + + QItemEditorFactory::setDefaultFactory (factory); +} diff --git a/MetaDataRegistry.hpp b/MetaDataRegistry.hpp new file mode 100644 index 000000000..8bf3c80dc --- /dev/null +++ b/MetaDataRegistry.hpp @@ -0,0 +1,6 @@ +#ifndef META_DATA_REGISTRY_HPP__ +#define META_DATA_REGISTRY_HPP__ + +void register_types (); + +#endif diff --git a/Modes.cpp b/Modes.cpp new file mode 100644 index 000000000..adae5a8aa --- /dev/null +++ b/Modes.cpp @@ -0,0 +1,92 @@ +#include "Modes.hpp" + +#include +#include + +namespace +{ + char const * const mode_names[] = + { + "", + "JT65", + "JT9", + "JT9W-1", + "JT4", + "WSPR", + }; +} + +Modes::Modes (QObject * parent) + : QAbstractListModel {parent} +{ +} + +char const * Modes::name (Mode m) +{ + return mode_names[static_cast (m)]; +} + +auto Modes::value (QString const& s) -> Mode +{ + auto end = mode_names + sizeof (mode_names) / sizeof (mode_names[0]); + auto p = std::find_if (mode_names, end + , [&s] (char const * const name) { + return name == s; + }); + return p != end ? static_cast (p - mode_names) : NULL_MODE; +} + +QVariant Modes::data (QModelIndex const& index, int role) const +{ + QVariant item; + + if (index.isValid ()) + { + auto const& row = index.row (); + switch (role) + { + case Qt::ToolTipRole: + case Qt::AccessibleDescriptionRole: + item = tr ("Mode"); + break; + + case Qt::EditRole: + item = static_cast (row); + break; + + case Qt::DisplayRole: + case Qt::AccessibleTextRole: + item = mode_names[row]; + break; + + case Qt::TextAlignmentRole: + item = Qt::AlignHCenter + Qt::AlignVCenter; + break; + } + } + + return item; +} + +QVariant Modes::headerData (int section, Qt::Orientation orientation, int role) const +{ + QVariant result; + + if (Qt::DisplayRole == role && Qt::Horizontal == orientation) + { + result = tr ("Mode"); + } + else + { + result = QAbstractListModel::headerData (section, orientation, role); + } + + return result; +} + +#if !defined (QT_NO_DEBUG_STREAM) +ENUM_QDEBUG_OPS_IMPL (Modes, Mode); +#endif + +ENUM_QDATASTREAM_OPS_IMPL (Modes, Mode); +ENUM_CONVERSION_OPS_IMPL (Modes, Mode); diff --git a/Modes.hpp b/Modes.hpp new file mode 100644 index 000000000..47d548acd --- /dev/null +++ b/Modes.hpp @@ -0,0 +1,49 @@ +#ifndef MODES_HPP__ +#define MODES_HPP__ + +#include + +#include "qt_helpers.hpp" + +class Modes final + : public QAbstractListModel +{ + Q_OBJECT; + Q_ENUMS (Mode); + +public: + enum Mode + { + NULL_MODE, + JT65, + JT9, + JT9W_1, + JT4, + WSPR, + }; + + explicit Modes (QObject * parent = nullptr); + + static char const * name (Mode); + static Mode value (QString const&); + + // Implement the QAbstractListModel interface + int rowCount (QModelIndex const& parent = QModelIndex {}) const override + { + return parent.isValid () ? 0 : 6; // the number of modes in the + // Mode enumeration class + } + QVariant data (QModelIndex const&, int role = Qt::DisplayRole) const override; + QVariant headerData (int section, Qt::Orientation, int = Qt::DisplayRole) const override; +}; + +Q_DECLARE_METATYPE (Modes::Mode); + +#if !defined (QT_NO_DEBUG_STREAM) +ENUM_QDEBUG_OPS_DECL (Modes, Mode); +#endif + +ENUM_QDATASTREAM_OPS_DECL (Modes, Mode); +ENUM_CONVERSION_OPS_DECL (Modes, Mode); + +#endif diff --git a/Radio.cpp b/Radio.cpp index 961fc1d46..496640edd 100644 --- a/Radio.cpp +++ b/Radio.cpp @@ -2,7 +2,6 @@ #include -#include #include #include #include @@ -14,19 +13,6 @@ namespace Radio { namespace { - struct init - { - init () - { - qRegisterMetaType ("Frequency"); - - qRegisterMetaType ("Frequencies"); - qRegisterMetaTypeStreamOperators ("Frequencies"); - - qRegisterMetaType ("FrequencyDelta"); - } - } static_initaializer; - double constexpr MHz_factor {1.e6}; int constexpr frequency_precsion {6}; diff --git a/Radio.hpp b/Radio.hpp index 3d3211bbd..ef3631099 100644 --- a/Radio.hpp +++ b/Radio.hpp @@ -3,7 +3,6 @@ #include #include -#include class QVariant; class QString; @@ -18,7 +17,6 @@ namespace Radio // Frequency types // using Frequency = quint64; - using Frequencies = QList; using FrequencyDelta = qint64; // @@ -48,7 +46,6 @@ namespace Radio } Q_DECLARE_METATYPE (Radio::Frequency); -Q_DECLARE_METATYPE (Radio::Frequencies); Q_DECLARE_METATYPE (Radio::FrequencyDelta); #endif diff --git a/StationList.cpp b/StationList.cpp index 94c09d650..2c57c896e 100644 --- a/StationList.cpp +++ b/StationList.cpp @@ -19,20 +19,6 @@ #include "Bands.hpp" -namespace -{ - struct init - { - init () - { - qRegisterMetaType ("Station"); - qRegisterMetaTypeStreamOperators ("Station"); - qRegisterMetaType ("Stations"); - qRegisterMetaTypeStreamOperators ("Stations"); - } - } static_initializer; -} - #if !defined (QT_NO_DEBUG_STREAM) QDebug operator << (QDebug debug, StationList::Station const& station) { @@ -70,12 +56,10 @@ public: { } - Stations const& stations () const {return stations_;} - void assign (Stations); + Stations station_list (Stations); QModelIndex add (Station); FrequencyDelta offset (Frequency) const; -protected: // Implement the QAbstractTableModel interface. int rowCount (QModelIndex const& parent = QModelIndex {}) const override; int columnCount (QModelIndex const& parent = QModelIndex {}) const override; @@ -90,7 +74,6 @@ protected: QMimeData * mimeData (QModelIndexList const&) const override; bool dropMimeData (QMimeData const *, Qt::DropAction, int row, int column, QModelIndex const& parent) override; -private: // Helper method for band validation. QModelIndex first_matching_band (QString const& band_name) const { @@ -127,15 +110,14 @@ StationList::~StationList () { } -StationList& StationList::operator = (Stations stations) +auto StationList::station_list (Stations stations) -> Stations { - m_->assign (stations); - return *this; + return m_->station_list (stations); } -auto StationList::stations () const -> Stations +auto StationList::station_list () const -> Stations const& { - return m_->stations (); + return m_->stations_; } QModelIndex StationList::add (Station s) @@ -145,13 +127,11 @@ QModelIndex StationList::add (Station s) bool StationList::remove (Station s) { - auto row = m_->stations ().indexOf (s); - + auto row = m_->stations_.indexOf (s); if (0 > row) { return false; } - return removeRow (row); } @@ -194,11 +174,12 @@ auto StationList::offset (Frequency f) const -> FrequencyDelta } -void StationList::impl::assign (Stations stations) +auto StationList::impl::station_list (Stations stations) -> Stations { beginResetModel (); std::swap (stations_, stations); endResetModel (); + return stations; } QModelIndex StationList::impl::add (Station s) @@ -221,22 +202,19 @@ QModelIndex StationList::impl::add (Station s) auto StationList::impl::offset (Frequency f) const -> FrequencyDelta { // Lookup band for frequency - auto band_index = bands_->find (f); - if (band_index.isValid ()) + auto const& band = bands_->find (f); + if (band != bands_->out_of_band ()) { - auto band_name = band_index.data ().toString (); - // Lookup station for band - for (int i = 0; i < stations ().size (); ++i) + for (int i = 0; i < stations_.size (); ++i) { - if (stations_[i].band_name_ == band_name) + if (stations_[i].band_name_ == band->name_) { return stations_[i].offset_; } } } - - return 0; // no offset + return 0; // no offset } int StationList::impl::rowCount (QModelIndex const& parent) const @@ -260,7 +238,7 @@ Qt::ItemFlags StationList::impl::flags (QModelIndex const& index) const && row < stations_.size () && column < num_columns) { - if (2 == column) + if (description_column == column) { result |= Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; } @@ -289,7 +267,7 @@ QVariant StationList::impl::data (QModelIndex const& index, int role) const { switch (column) { - case 0: // band name + case band_column: switch (role) { case SortRole: @@ -318,19 +296,19 @@ QVariant StationList::impl::data (QModelIndex const& index, int role) const } break; - case 1: // frequency offset + case offset_column: { auto frequency_offset = stations_.at (row).offset_; switch (role) { + case SortRole: + case Qt::EditRole: case Qt::AccessibleTextRole: item = frequency_offset; break; - case SortRole: case Qt::DisplayRole: - case Qt::EditRole: - item = frequency_offset; + item = Radio::pretty_frequency_MHz_string (frequency_offset) + " MHz"; break; case Qt::ToolTipRole: @@ -345,7 +323,7 @@ QVariant StationList::impl::data (QModelIndex const& index, int role) const } break; - case 2: // antenna description + case description_column: switch (role) { case SortRole: @@ -379,9 +357,9 @@ QVariant StationList::impl::headerData (int section, Qt::Orientation orientation { switch (section) { - case 0: header = tr ("Band"); break; - case 1: header = tr ("Offset"); break; - case 2: header = tr ("Antenna Description"); break; + case band_column: header = tr ("Band"); break; + case offset_column: header = tr ("Offset"); break; + case description_column: header = tr ("Antenna Description"); break; } } else @@ -407,7 +385,7 @@ bool StationList::impl::setData (QModelIndex const& model_index, QVariant const& switch (model_index.column ()) { - case 0: + case band_column: { // Check if band name is valid. auto band_index = first_matching_band (value.toString ()); @@ -420,7 +398,7 @@ bool StationList::impl::setData (QModelIndex const& model_index, QVariant const& } break; - case 1: + case offset_column: { stations_[row].offset_ = value.value (); Q_EMIT dataChanged (model_index, model_index, roles); @@ -428,7 +406,7 @@ bool StationList::impl::setData (QModelIndex const& model_index, QVariant const& } break; - case 2: + case description_column: stations_[row].antenna_description_ = value.toString (); Q_EMIT dataChanged (model_index, model_index, roles); changed = true; @@ -508,9 +486,8 @@ bool StationList::impl::dropMimeData (QMimeData const * data, Qt::DropAction act { return true; } - if (parent.isValid () - && 2 == parent.column () + && description_column == parent.column () && data->hasFormat (mime_type)) { QByteArray encoded_data {data->data (mime_type)}; @@ -534,17 +511,16 @@ bool StationList::impl::dropMimeData (QMimeData const * data, Qt::DropAction act QString frequency_string; stream >> frequency_string; auto frequency = Radio::frequency (frequency_string, 0); - auto band_index = bands_->find (frequency); + auto const& band = bands_->find (frequency); if (stations_.cend () == std::find_if (stations_.cbegin () , stations_.cend () - , [&band_index] (Station const& s) {return s.band_name_ == band_index.data ().toString ();})) + , [&band] (Station const& s) {return s.band_name_ == band->name_;})) { - add (Station {band_index.data ().toString (), 0, QString {}}); + // not found so add it + add (Station {band->name_, 0, QString {}}); } } - return true; } - return false; } diff --git a/StationList.hpp b/StationList.hpp index 75f641275..047302f9a 100644 --- a/StationList.hpp +++ b/StationList.hpp @@ -59,13 +59,15 @@ public: using Stations = QList; + enum Column {band_column, offset_column, description_column}; + explicit StationList (Bands const * bands, QObject * parent = nullptr); explicit StationList (Bands const * bands, Stations, QObject * parent = nullptr); ~StationList (); - // Load and store contents. - StationList& operator = (Stations); - Stations stations () const; + // Load and query contents. + Stations station_list (Stations); + Stations const& station_list () const; // // Model API @@ -83,10 +85,6 @@ private: pimpl m_; }; -#if !defined (QT_NO_DEBUG_STREAM) -QDebug operator << (QDebug debug, StationList::Station const&); -#endif - // Station equivalence inline bool operator == (StationList::Station const& lhs, StationList::Station const& rhs) @@ -96,6 +94,13 @@ bool operator == (StationList::Station const& lhs, StationList::Station const& r && lhs.antenna_description_ == rhs.antenna_description_; } +QDataStream& operator << (QDataStream&, StationList::Station const&); +QDataStream& operator >> (QDataStream&, StationList::Station&); + +#if !defined (QT_NO_DEBUG_STREAM) +QDebug operator << (QDebug debug, StationList::Station const&); +#endif + Q_DECLARE_METATYPE (StationList::Station); Q_DECLARE_METATYPE (StationList::Stations); diff --git a/Transceiver.cpp b/Transceiver.cpp index 2665a2783..31a29be83 100644 --- a/Transceiver.cpp +++ b/Transceiver.cpp @@ -2,18 +2,6 @@ #include "moc_Transceiver.cpp" -namespace -{ - struct init - { - init () - { - qRegisterMetaType ("Transceiver::TransceiverState"); - qRegisterMetaType ("Transceiver::MODE"); - } - } static_initialization; -} - #if !defined (QT_NO_DEBUG_STREAM) ENUM_QDEBUG_OPS_IMPL (Transceiver, MODE); diff --git a/TransceiverFactory.cpp b/TransceiverFactory.cpp index cfe58487e..19b92b9c5 100644 --- a/TransceiverFactory.cpp +++ b/TransceiverFactory.cpp @@ -21,25 +21,6 @@ char const * const TransceiverFactory::basic_transceiver_name_ = "None"; namespace { - struct init - { - init () - { - qRegisterMetaType ("TransceiverFactory::DataBits"); - qRegisterMetaTypeStreamOperators ("TransceiverFactory::DataBits"); - qRegisterMetaType ("TransceiverFactory::StopBits"); - qRegisterMetaTypeStreamOperators ("TransceiverFactory::StopBits"); - qRegisterMetaType ("TransceiverFactory::Handshake"); - qRegisterMetaTypeStreamOperators ("TransceiverFactory::Handshake"); - qRegisterMetaType ("TransceiverFactory::PTTMethod"); - qRegisterMetaTypeStreamOperators ("TransceiverFactory::PTTMethod"); - qRegisterMetaType ("TransceiverFactory::TXAudioSource"); - qRegisterMetaTypeStreamOperators ("TransceiverFactory::TXAudioSource"); - qRegisterMetaType ("TransceiverFactory::SplitMode"); - qRegisterMetaTypeStreamOperators ("TransceiverFactory::SplitMode"); - } - } static_initializer; - enum // supported non-hamlib radio interfaces { NonHamlibBaseId = 9899 diff --git a/WFPalette.cpp b/WFPalette.cpp index 22dda7f99..1dd65fd24 100644 --- a/WFPalette.cpp +++ b/WFPalette.cpp @@ -33,14 +33,6 @@ namespace { int constexpr points {256}; - struct init - { - init () - { - qRegisterMetaTypeStreamOperators ("Colours"); - } - } static_initaializer; - using Colours = WFPalette::Colours; // ensure that palette colours are useable for interpolation diff --git a/main.cpp b/main.cpp index 43a0ccb09..a2bf312db 100644 --- a/main.cpp +++ b/main.cpp @@ -23,7 +23,7 @@ #endif #include "revision_utils.hpp" - +#include "MetaDataRegistry.hpp" #include "SettingsGroup.hpp" #include "TraceFile.hpp" #include "mainwindow.h" @@ -31,6 +31,8 @@ int main(int argc, char *argv[]) { + register_types (); // make the Qt magic happen + // Multiple instances: QSharedMemory mem_jt9; diff --git a/mainwindow.cpp b/mainwindow.cpp index 05efa55e4..0c0f3de30 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -61,27 +61,6 @@ namespace } } -class BandAndFrequencyItemDelegate final - : public QStyledItemDelegate -{ -public: - explicit BandAndFrequencyItemDelegate (Bands const * bands, QObject * parent = nullptr) - : QStyledItemDelegate {parent} - , bands_ {bands} - { - } - - QString displayText (QVariant const& v, QLocale const&) const override - { - return Radio::pretty_frequency_MHz_string (Radio::frequency (v, 6)) - + QChar::Nbsp - + '(' + (bands_->data (bands_->find (Radio::frequency (v, 6)))).toString () + ')'; - } - -private: - Bands const * bands_; -}; - //--------------------------------------------------- MainWindow constructor MainWindow::MainWindow(bool multiple, QSettings * settings, QSharedMemory *shdmem, unsigned downSampleFactor, QWidget *parent) : @@ -278,15 +257,11 @@ MainWindow::MainWindow(bool multiple, QSettings * settings, QSharedMemory *shdme // Hook up working frequencies. ui->bandComboBox->setModel (m_config.frequencies ()); - ui->bandComboBox->setModelColumn (1); // MHz + ui->bandComboBox->setModelColumn (FrequencyList::frequency_mhz_column); - // Add delegate to show bands alongside frequencies in combo box - // popup list. - ui->bandComboBox->view ()->setItemDelegateForColumn (1, new BandAndFrequencyItemDelegate {m_config.bands (), this}); - - // combo box drop downs are limited to the drop down selector width, - // this almost random increase improves the situation - ui->bandComboBox->view ()->setMinimumWidth (ui->bandComboBox->view ()->sizeHintForColumn (1) + 40); + // combo box drop down width defaults to the line edit + decorator width, + // here we change that to the column width size hint of the model column + ui->bandComboBox->view ()->setMinimumWidth (ui->bandComboBox->view ()->sizeHintForColumn (FrequencyList::frequency_mhz_column)); // Enable live band combo box entry validation and action. auto band_validator = new LiveFrequencyValidator {ui->bandComboBox @@ -646,7 +621,6 @@ void MainWindow::writeSettings() m_settings->setValue("GUItab",ui->tabWidget->currentIndex()); m_settings->setValue("OutBufSize",outBufSize); m_settings->setValue("LockTxFreq",m_lockTxFreq); - m_settings->setValue("Plus2kHz",m_plus2kHz); m_settings->setValue("PctTx",m_pctx); m_settings->setValue("dBm",m_dBm); m_settings->setValue("UploadSpots",m_uploadSpots); @@ -736,8 +710,6 @@ void MainWindow::readSettings() outBufSize=m_settings->value("OutBufSize",4096).toInt(); m_lockTxFreq=m_settings->value("LockTxFreq",false).toBool(); ui->cbTxLock->setChecked(m_lockTxFreq); - m_plus2kHz=m_settings->value("Plus2kHz",false).toBool(); - ui->cbPlus2kHz->setChecked(m_plus2kHz); ui->sunriseBands->setText(m_settings->value("SunriseBands","").toString()); on_sunriseBands_editingFinished(); ui->dayBands->setText(m_settings->value("DayBands","").toString()); @@ -1176,29 +1148,33 @@ void MainWindow::qsy (Frequency f) void MainWindow::displayDialFrequency () { // lookup band - auto bands_model = m_config.bands (); - QString t {bands_model->data(bands_model->find(m_dialFreq)).toString()}; - ui->bandComboBox->setCurrentText (t); - m_wideGraph->setRxBand(t); + auto const& band_name = m_config.bands ()->find (m_dialFreq)->name_; + ui->bandComboBox->setCurrentText (band_name); + m_wideGraph->setRxBand (band_name); - // search working frequencies for one we are within 10kHz of - auto frequencies = m_config.frequencies (); + // search working frequencies for one we are within 10kHz of (1 Mhz + // of on VHF and up) bool valid {false}; - quint64 min_offset=99999999; - - for (int row = 0; row < frequencies->rowCount (); ++row) { + quint64 min_offset {99999999}; + auto const& frequencies = m_config.frequencies (); + for (int row = 0; row < frequencies->rowCount (); ++row) + { + auto const& source_row = frequencies->mapToSource (frequencies->index (row, 0)).row (); + auto const& item = frequencies->frequency_list ()[source_row]; // we need to do specific checks for above and below here to // ensure that we can use unsigned Radio::Frequency since we // potentially use the full 64-bit unsigned range. - auto working_frequency = frequencies->data (frequencies->index (row, 0)).value (); - auto offset = m_dialFreq > working_frequency ? m_dialFreq - working_frequency : working_frequency - m_dialFreq; - if(offset working_frequency ? m_dialFreq - working_frequency : working_frequency - m_dialFreq; + if (offset < min_offset) { + m_freqNominal = working_frequency; + min_offset = offset; } } - if ((min_offset < 10000u) or (m_config.enable_VHF_features() and - min_offset < 1000000u)) valid = true; + if (min_offset < 10000u or (m_config.enable_VHF_features() and + min_offset < 1000000u)) { + valid = true; + } ui->labDialFreq->setProperty ("oob", !valid); // the following sequence is necessary to update the style @@ -1672,15 +1648,16 @@ void MainWindow::readFromStdout() //readFromStdout msgBox("Cannot open \"" + f.fileName () + "\" for append:" + f.errorString ()); } - if (m_config.insert_blank () && m_blankLine) { - QString band; - if (QDateTime::currentMSecsSinceEpoch() / 1000 - m_secBandChanged > 50) { - auto const& bands_model = m_config.bands (); - band = ' ' + bands_model->data (bands_model->find (m_dialFreq + ui->TxFreqSpinBox->value ())).toString (); - } - ui->decodedTextBrowser->insertLineSpacer (band.rightJustified (40, '-')); - m_blankLine = false; - } + if (m_config.insert_blank () && m_blankLine) + { + QString band; + if (QDateTime::currentMSecsSinceEpoch() / 1000 - m_secBandChanged > 50) + { + band = ' ' + QString {m_config.bands ()->find (m_dialFreq)->name_}; + } + ui->decodedTextBrowser->insertLineSpacer (band.rightJustified (40, '-')); + m_blankLine = false; + } DecodedText decodedtext; decodedtext = t.replace("\n",""); //t.replace("\n","").mid(0,t.length()-4); @@ -2251,9 +2228,8 @@ void MainWindow::startTx2() transmit (snr); signalMeter->setValue(0); if(m_mode.mid(0,4)=="WSPR" and !m_tune) { - auto const& bands_model = m_config.bands (); t = " Transmiting " + m_mode + " ----------------------- " + - bands_model->data(bands_model->find(m_dialFreq)).toString (); + m_config.bands ()->find (m_dialFreq)->name_; ui->decodedTextBrowser->append(t.rightJustified (71, '-')); QFile f {m_dataDir.absoluteFilePath ("ALL_WSPR.TXT")}; @@ -2986,10 +2962,8 @@ void MainWindow::acceptQSO2(QDateTime const& QSO_date, QString const& call, QStr , QString const& tx_power, QString const& comments , QString const& name) { - auto const& bands_model = m_config.bands (); - auto band = bands_model->data (bands_model->find (m_dialFreq + ui->TxFreqSpinBox->value ())).toString (); QString date = m_dateTimeQSO.toString("yyyyMMdd"); - m_logBook.addAsWorked(m_hisCall,band,m_modeTx,date); + m_logBook.addAsWorked (m_hisCall, m_config.bands ()->find (m_dialFreq)->name_, m_modeTx, date); m_messageClient->qso_logged (QSO_date, call, grid, dial_freq, mode, rpt_sent, rpt_received, tx_power, comments, name); @@ -3009,6 +2983,7 @@ void MainWindow::acceptQSO2(QDateTime const& QSO_date, QString const& call, QStr void MainWindow::on_actionJT9_1_triggered() { m_mode="JT9"; + switch_mode (Modes::JT9); if(m_modeTx!="JT9") on_pbTxMode_clicked(); statusChanged(); m_TRperiod=60; @@ -3036,6 +3011,7 @@ void MainWindow::on_actionJT9_1_triggered() void MainWindow::on_actionJT9W_1_triggered() { m_mode="JT9W-1"; + switch_mode (Modes::JT9W_1); if(m_modeTx!="JT9") on_pbTxMode_clicked(); statusChanged(); m_TRperiod=60; @@ -3068,6 +3044,7 @@ void MainWindow::on_actionJT65_triggered() on_pbTxMode_clicked(); } m_mode="JT65"; + switch_mode (Modes::JT65); if(m_modeTx!="JT65") on_pbTxMode_clicked(); statusChanged(); m_TRperiod=60; @@ -3105,6 +3082,7 @@ void MainWindow::on_actionJT65_triggered() void MainWindow::on_actionJT9_JT65_triggered() { m_mode="JT9+JT65"; + switch_mode (Modes::JT65); if(m_modeTx != "JT65") m_modeTx="JT9"; m_nSubMode=0; //Dual-mode always means JT9 and JT65A statusChanged(); @@ -3133,6 +3111,7 @@ void MainWindow::on_actionJT9_JT65_triggered() void MainWindow::on_actionJT4_triggered() { m_mode="JT4"; + switch_mode (Modes::JT4); m_modeTx="JT4"; statusChanged(); m_TRperiod=60; @@ -3171,6 +3150,7 @@ void MainWindow::on_actionJT4_triggered() void MainWindow::on_actionWSPR_2_triggered() { m_mode="WSPR-2"; + switch_mode (Modes::WSPR); m_modeTx="WSPR-2"; //### not needed ?? ### statusChanged(); m_TRperiod=120; @@ -3194,8 +3174,20 @@ void MainWindow::on_actionWSPR_2_triggered() void MainWindow::on_actionWSPR_15_triggered() { msgBox("WSPR-15 is not yet available"); + switch_mode (Modes::WSPR); } +void MainWindow::switch_mode (Mode mode) +{ + auto f = m_dialFreq; + m_config.frequencies ()->filter (mode); + auto const& index = m_config.frequencies ()->best_working_frequency (f, mode); + if (index.isValid ()) + { + ui->bandComboBox->setCurrentIndex (index.row ()); + on_bandComboBox_activated (index.row ()); + } +} void MainWindow::WSPR_config(bool b) { @@ -3328,26 +3320,44 @@ bool MainWindow::gridOK(QString g) return b; } +void MainWindow::on_bandComboBox_currentIndexChanged (int index) +{ + auto const& frequencies = m_config.frequencies (); + auto const& source_index = frequencies->mapToSource (frequencies->index (index, FrequencyList::frequency_column)); + Frequency frequency {m_dialFreq}; + if (source_index.isValid ()) + { + frequency = frequencies->frequency_list ()[source_index.row ()].frequency_; + } + + // Lookup band + auto const& band = m_config.bands ()->find (frequency); + auto const& out_of_band = m_config.bands ()->out_of_band (); + if (out_of_band != band) + { + ui->bandComboBox->lineEdit ()->setStyleSheet ({}); + ui->bandComboBox->setCurrentText (band->name_); + } + else + { + ui->bandComboBox->lineEdit ()->setStyleSheet ("QLineEdit {color: yellow; background-color : red;}"); + ui->bandComboBox->setCurrentText (out_of_band->name_); + } + displayDialFrequency (); +} + void MainWindow::on_bandComboBox_activated (int index) { - auto frequencies = m_config.frequencies (); - auto frequency = frequencies->data (frequencies->index (index, 0)); - // Lookup band - auto bands = m_config.bands (); - auto band_index = bands->find (frequency); - if (band_index.isValid ()) { - ui->bandComboBox->lineEdit ()->setStyleSheet ({}); - ui->bandComboBox->setCurrentText (band_index.data ().toString ()); - } else { - ui->bandComboBox->lineEdit ()->setStyleSheet ("QLineEdit {color: yellow; background-color : red;}"); - ui->bandComboBox->setCurrentText (bands->data (QModelIndex {}).toString ()); - } - auto f = frequency.value (); - if (m_plus2kHz) f += 2000; + auto const& frequencies = m_config.frequencies (); + auto const& source_index = frequencies->mapToSource (frequencies->index (index, FrequencyList::frequency_column)); + Frequency frequency {m_dialFreq}; + if (source_index.isValid ()) + { + frequency = frequencies->frequency_list ()[source_index.row ()].frequency_; + } m_bandEdited = true; - band_changed (f); - m_wideGraph->setRxBand(band_index.data().toString()); -// qDebug() << "bandComboBox_activated" << index << 0.000001*f; + band_changed (frequency); + m_wideGraph->setRxBand (m_config.bands ()->find (frequency)->name_); } void MainWindow::band_changed (Frequency f) @@ -3607,23 +3617,6 @@ void MainWindow::on_cbTxLock_clicked(bool checked) if(m_lockTxFreq) on_pbR2T_clicked(); } -void MainWindow::on_cbPlus2kHz_toggled(bool checked) -{ - m_plus2kHz = checked; - - if (m_config.transceiver_online (false)) { // update state only if not starting up - psk_Reporter->sendReport(); // Upload any queued spots before changing band - auto f = m_dialFreq; - if (m_plus2kHz) { - f += 2000; - } else { - f -= 2000; - } - m_bandEdited = true; - band_changed (f); - } -} - void MainWindow::handle_transceiver_update (Transceiver::TransceiverState s) { transmitDisplay (s.ptt ()); @@ -3868,9 +3861,6 @@ void MainWindow::transmitDisplay (bool transmitting) ui->cbTxLock->setEnabled (QSY_allowed); } - // Allow +2kHz only when not transmitting or if TX QSYs are allowed - ui->cbPlus2kHz->setEnabled (!transmitting || m_config.tx_QSY_allowed ()); - // the following are always disallowed in transmit ui->menuMode->setEnabled (!transmitting); ui->bandComboBox->setEnabled (!transmitting); @@ -4173,9 +4163,8 @@ void MainWindow::p1ReadFromStdout() //p1readFromStdout if (m_config.insert_blank () && m_blankLine) { QString band; - auto const& bands_model = m_config.bands (); Frequency f=1000000.0*rxFields.at(3).toDouble()+0.5; - band = ' ' + bands_model->data (bands_model->find (f)).toString (); + band = ' ' + m_config.bands ()->find (f)->name_; ui->decodedTextBrowser->append(band.rightJustified (71, '-')); m_blankLine = false; } @@ -4355,10 +4344,9 @@ void MainWindow::bandHopping() m_band00=iband; auto frequencies = m_config.frequencies (); - for (int i=0; i<99; i++) { - auto frequency=frequencies->data (frequencies->index (i, 0)); + for (int row = 0; row < frequencies->rowCount (); ++row) { + auto frequency=frequencies->data (frequencies->index (row, FrequencyList::frequency_column)); auto f = frequency.value(); - if(f==0) break; if(f==f0) { on_bandComboBox_activated(i); //Set new band // qDebug() << nhr << nmin << int(sec) << "Band selected" << i << 0.000001*f0 << 0.000001*f; diff --git a/mainwindow.h b/mainwindow.h index d3ae358c2..ca6c87c90 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -22,6 +22,7 @@ #include "soundout.h" #include "commons.h" #include "Radio.hpp" +#include "Modes.hpp" #include "Configuration.hpp" #include "Transceiver.hpp" #include "psk_reporter.h" @@ -65,6 +66,7 @@ class MainWindow : public QMainWindow public: using Frequency = Radio::Frequency; + using Mode = Modes::Mode; // Multiple instances: call MainWindow() with *thekey explicit MainWindow(bool multiple, QSettings *, QSharedMemory *shdmem, @@ -176,12 +178,12 @@ private slots: , QString const& rpt_sent, QString const& rpt_received , QString const& tx_power, QString const& comments , QString const& name); + void on_bandComboBox_currentIndexChanged (int index); void on_bandComboBox_activated (int index); void on_readFreq_clicked(); void on_pbTxMode_clicked(); void on_RxFreqSpinBox_valueChanged(int n); void on_cbTxLock_clicked(bool checked); - void on_cbPlus2kHz_toggled(bool checked); void on_outAttenuation_valueChanged (int); void rigOpen (); void handle_transceiver_update (Transceiver::TransceiverState); @@ -233,8 +235,6 @@ private slots: void on_graylineDuration_editingFinished(); private: - void enable_DXCC_entity (bool on); - Q_SIGNAL void initializeAudioOutputStream (QAudioDeviceInfo, unsigned channels, unsigned msBuffered) const; Q_SIGNAL void stopAudioOutputStream () const; @@ -370,7 +370,6 @@ private: bool m_lockTxFreq; bool m_tx2QSO; bool m_CATerror; - bool m_plus2kHz; bool m_bAstroData; bool m_bEME; bool m_bShMsgs; @@ -507,6 +506,8 @@ private: void replyToCQ (QTime, qint32 snr, float delta_time, quint32 delta_frequency, QString const& mode, QString const& message_text); void replayDecodes (); void postDecode (bool is_new, QString const& message); + void enable_DXCC_entity (bool on); + void switch_mode (Mode); void bandHopping(); }; diff --git a/mainwindow.ui b/mainwindow.ui index e634e1bfd..776ef0a84 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -1348,16 +1348,6 @@ - - - - Add 2 kHz to requested dial frequency - - - +2 kHz - - -