From 381faca99aa8603f2e38125feab669c8148a0656 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Wed, 29 May 2019 23:35:18 +0100 Subject: [PATCH 01/10] Add option to include or exclude WAE extra entities in DXCC entity lookups Also refactored object relationships as a start to implementing contest multiplier highlighting. --- Configuration.cpp | 8 +++- Configuration.hpp | 1 + Configuration.ui | 83 ++++++++++++++++++++++++---------------- logbook/AD1CCty.cpp | 52 ++++++++++++++++--------- logbook/AD1CCty.hpp | 8 ++-- logbook/WorkedBefore.cpp | 9 +++-- logbook/WorkedBefore.hpp | 3 +- logbook/logbook.cpp | 24 ++++++++++++ logbook/logbook.h | 8 ++++ models/CabrilloLog.cpp | 13 +++++++ models/CabrilloLog.hpp | 5 ++- widgets/logqso.cpp | 9 ++--- widgets/logqso.h | 8 ++-- widgets/mainwindow.cpp | 28 ++++---------- widgets/mainwindow.h | 4 -- 15 files changed, 169 insertions(+), 94 deletions(-) diff --git a/Configuration.cpp b/Configuration.cpp index d1cc8d172..11c7be369 100644 --- a/Configuration.cpp +++ b/Configuration.cpp @@ -564,6 +564,7 @@ private: DecodeHighlightingModel decode_highlighing_model_; DecodeHighlightingModel next_decode_highlighing_model_; bool highlight_by_mode_; + bool include_WAE_entities_; int LotW_days_since_upload_; TransceiverFactory::ParameterPack rig_params_; @@ -745,6 +746,7 @@ bool Configuration::pwrBandTuneMemory () const {return m_->pwrBandTuneMemory_;} LotWUsers const& Configuration::lotw_users () const {return m_->lotw_users_;} DecodeHighlightingModel const& Configuration::decode_highlighting () const {return m_->decode_highlighing_model_;} bool Configuration::highlight_by_mode () const {return m_->highlight_by_mode_;} +bool Configuration::include_WAE_entities () const {return m_->include_WAE_entities_;} void Configuration::set_calibration (CalibrationParams params) { @@ -950,6 +952,7 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network , station_insert_action_ {tr ("&Insert ..."), nullptr} , station_dialog_ {new StationDialog {&next_stations_, &bands_, this}} , highlight_by_mode_ {false} + , include_WAE_entities_ {false} , LotW_days_since_upload_ {0} , last_port_type_ {TransceiverFactory::Capabilities::none} , rig_is_dummy_ {false} @@ -1319,6 +1322,7 @@ void Configuration::impl::initialize_models () next_decode_highlighing_model_.items (decode_highlighing_model_.items ()); ui_->highlight_by_mode_check_box->setChecked (highlight_by_mode_); + ui_->include_WAE_check_box->setChecked (include_WAE_entities_); ui_->LotW_days_since_upload_spin_box->setValue (LotW_days_since_upload_); set_rig_invariants (); @@ -1467,6 +1471,7 @@ void Configuration::impl::read_settings () if (!highlight_items.size ()) highlight_items = DecodeHighlightingModel::default_items (); decode_highlighing_model_.items (highlight_items); highlight_by_mode_ = settings_->value("HighlightByMode", false).toBool (); + include_WAE_entities_ = settings_->value("IncludeWAEEntities", false).toBool (); LotW_days_since_upload_ = settings_->value ("LotWDaysSinceLastUpload", 365).toInt (); lotw_users_.set_age_constraint (LotW_days_since_upload_); @@ -1579,7 +1584,7 @@ void Configuration::impl::write_settings () settings_->setValue ("FrequenciesForRegionModes", QVariant::fromValue (frequencies_.frequency_list ())); settings_->setValue ("stations", QVariant::fromValue (stations_.station_list ())); settings_->setValue ("DecodeHighlighting", QVariant::fromValue (decode_highlighing_model_.items ())); - settings_->setValue ("HighlightByMode", highlight_by_mode_); + settings_->setValue ("IncludeWAEEntities", include_WAE_entities_); settings_->setValue ("LotWDaysSinceLastUpload", LotW_days_since_upload_); settings_->setValue ("toRTTY", log_as_RTTY_); settings_->setValue ("dBtoComments", report_in_comments_); @@ -2116,6 +2121,7 @@ void Configuration::impl::accept () Q_EMIT self_->decode_highlighting_changed (decode_highlighing_model_); } highlight_by_mode_ = ui_->highlight_by_mode_check_box->isChecked (); + include_WAE_entities_ = ui_->include_WAE_check_box->isChecked (); LotW_days_since_upload_ = ui_->LotW_days_since_upload_spin_box->value (); lotw_users_.set_age_constraint (LotW_days_since_upload_); diff --git a/Configuration.hpp b/Configuration.hpp index 9535605d9..80f5a5cd1 100644 --- a/Configuration.hpp +++ b/Configuration.hpp @@ -177,6 +177,7 @@ public: LotWUsers const& lotw_users () const; DecodeHighlightingModel const& decode_highlighting () const; bool highlight_by_mode () const; + bool include_WAE_entities () const; enum class SpecialOperatingActivity {NONE, NA_VHF, EU_VHF, FIELD_DAY, RTTY, FOX, HOUND}; SpecialOperatingActivity special_op_id () const; diff --git a/Configuration.ui b/Configuration.ui index e32f4a198..a9fe51d45 100644 --- a/Configuration.ui +++ b/Configuration.ui @@ -2294,6 +2294,23 @@ Right click for insert and delete options. + + + + + + + + + Include extra WAE entities + + + include_WAE_check_box + + + + + @@ -2306,35 +2323,6 @@ Right click for insert and delete options. Logbook of the World User Validation - - - - Age of last upload less than: - - - LotW_days_since_upload_spin_box - - - - - - - <html><head/><body><p>Adjust this spin box to set the age threshold of LotW user's last upload date that is accepted as a current LotW user.</p></body></html> - - - days - - - 0 - - - 9999 - - - 365 - - - @@ -2369,6 +2357,35 @@ Right click for insert and delete options. + + + + Age of last upload less than: + + + LotW_days_since_upload_spin_box + + + + + + + <html><head/><body><p>Adjust this spin box to set the age threshold of LotW user's last upload date that is accepted as a current LotW user.</p></body></html> + + + days + + + 0 + + + 9999 + + + 365 + + + @@ -3085,13 +3102,13 @@ Right click for insert and delete options. - + - - + - + + diff --git a/logbook/AD1CCty.cpp b/logbook/AD1CCty.cpp index 58ed375f6..23d34fb43 100644 --- a/logbook/AD1CCty.cpp +++ b/logbook/AD1CCty.cpp @@ -16,6 +16,7 @@ #include #include #include +#include "Configuration.hpp" #include "Radio.hpp" #include "pimpl_impl.hpp" @@ -155,14 +156,16 @@ typedef multi_index_container< class AD1CCty::impl final { public: - explicit impl () + using entity_by_id = entities_type::index::type; + + explicit impl (Configuration const * configuration) + : configuration_ {configuration} { } - Record fixup (QString call, prefix const& p) const + entity_by_id::iterator lookup_entity (QString call, prefix const& p) const { call = call.toUpper (); - using entity_by_id = entities_type::index::type; entity_by_id::iterator e; // iterator into entity set // @@ -171,23 +174,26 @@ public: if (call.startsWith ("KG4") && call.size () != 5 && call.size () != 3) { // KG4 2x1 and 2x3 calls that map to Gitmo are mainland US not Gitmo - e = entities_.project (entities_.get ().find ("K")); + return entities_.project (entities_.get ().find ("K")); } else { - e = entities_.get ().find (p.entity_id_); + return entities_.get ().find (p.entity_id_); } - + } + + Record fixup (prefix const& p, entity const& e) const + { Record result; - result.continent = e->continent_; - result.CQ_zone = e->CQ_zone_; - result.ITU_zone = e->ITU_zone_; - result.entity_name = e->name_; - result.WAE_only = e->WAE_only_; - result.latitude = e->lat_; - result.longtitude = e->long_; - result.UTC_offset = e->UTC_offset_; - result.primary_prefix = e->primary_prefix_; + result.continent = e.continent_; + result.CQ_zone = e.CQ_zone_; + result.ITU_zone = e.ITU_zone_; + result.entity_name = e.name_; + result.WAE_only = e.WAE_only_; + result.latitude = e.lat_; + result.longtitude = e.long_; + result.UTC_offset = e.UTC_offset_; + result.primary_prefix = e.primary_prefix_; // check for overrides bool ok1 {true}, ok2 {true}, ok3 {true}, ok4 {true}, ok5 {true}; @@ -220,6 +226,7 @@ public: return false; } + Configuration const * configuration_; QString path_; entities_type entities_; prefixes_type prefixes_; @@ -307,8 +314,13 @@ char const * AD1CCty::continent (Continent c) } } -AD1CCty::AD1CCty () +AD1CCty::AD1CCty (Configuration const * configuration) + : m_ {configuration} { + Q_ASSERT (configuration); + // TODO: G4WJS - consider doing the following asynchronously to + // speed up startup. Not urgent as it takes less than 1s on a Core + // i7 reading BIG CTY.DAT. QDir dataPath {QStandardPaths::writableLocation (QStandardPaths::DataLocation)}; m_->path_ = dataPath.exists (file_name) ? dataPath.absoluteFilePath (file_name) // user override @@ -389,7 +401,7 @@ auto AD1CCty::lookup (QString const& call) const -> Record auto p = m_->prefixes_.find (exact_search); if (p != m_->prefixes_.end () && p->exact_) { - return m_->fixup (call, *p); + return m_->fixup (*p, *m_->lookup_entity (call, *p)); } } while (search_prefix.size ()) @@ -397,9 +409,11 @@ auto AD1CCty::lookup (QString const& call) const -> Record auto p = m_->prefixes_.find (search_prefix); if (p != m_->prefixes_.end ()) { - if (!p->exact_ || call.size () == search_prefix.size ()) + impl::entity_by_id::iterator e = m_->lookup_entity (call, *p); + if ((m_->configuration_->include_WAE_entities () || !e->WAE_only_) + && (!p->exact_ || call.size () == search_prefix.size ())) { - return m_->fixup (call, *p); + return m_->fixup (*p, *e); } } search_prefix = search_prefix.left (search_prefix.size () - 1); diff --git a/logbook/AD1CCty.hpp b/logbook/AD1CCty.hpp index 37cada3e9..4a485afa9 100644 --- a/logbook/AD1CCty.hpp +++ b/logbook/AD1CCty.hpp @@ -1,17 +1,19 @@ #ifndef AD1C_CTY_HPP_ #define AD1C_CTY_HPP_ -#include #include +#include #include "pimpl_h.hpp" +class QString; +class Configuration; + // // AD1CCty - Fast access database of Jim Reisert, AD1C's, cty.dat // entity and entity override information file. // class AD1CCty final : public QObject - , private boost::noncopyable { Q_OBJECT @@ -39,7 +41,7 @@ public: QString primary_prefix; }; - explicit AD1CCty (); + explicit AD1CCty (Configuration const *); ~AD1CCty (); Record lookup (QString const& call) const; diff --git a/logbook/WorkedBefore.cpp b/logbook/WorkedBefore.cpp index 31380b4c3..5edc51d3e 100644 --- a/logbook/WorkedBefore.cpp +++ b/logbook/WorkedBefore.cpp @@ -18,7 +18,7 @@ #include #include #include - +#include "Configuration.hpp" #include "qt_helpers.hpp" #include "pimpl_impl.hpp" @@ -361,8 +361,9 @@ namespace class WorkedBefore::impl final { public: - impl () + impl (Configuration const * configuration) : path_ {QDir {QStandardPaths::writableLocation (QStandardPaths::DataLocation)}.absoluteFilePath (logFileName)} + , prefixes_ {configuration} { } @@ -379,8 +380,10 @@ public: worked_before_database_type worked_; }; -WorkedBefore::WorkedBefore () +WorkedBefore::WorkedBefore (Configuration const * configuration) + : m_ {configuration} { + Q_ASSERT (configuration); connect (&m_->loader_watcher_, &QFutureWatcher::finished, [this] () { QString error; size_t n {0}; diff --git a/logbook/WorkedBefore.hpp b/logbook/WorkedBefore.hpp index 90a31813a..8b115351f 100644 --- a/logbook/WorkedBefore.hpp +++ b/logbook/WorkedBefore.hpp @@ -5,6 +5,7 @@ #include "AD1CCty.hpp" #include "pimpl_h.hpp" +class Configuration; class CountryDat; class QString; class QByteArray; @@ -17,7 +18,7 @@ class WorkedBefore final public: using Continent = AD1CCty::Continent; - explicit WorkedBefore (); + explicit WorkedBefore (Configuration const *); ~WorkedBefore (); Q_SLOT void reload (); diff --git a/logbook/logbook.cpp b/logbook/logbook.cpp index 0e855c88f..bde52228c 100644 --- a/logbook/logbook.cpp +++ b/logbook/logbook.cpp @@ -3,12 +3,16 @@ #include #include "Configuration.hpp" #include "AD1CCty.hpp" +#include "models/CabrilloLog.hpp" +#include "models/FoxLog.hpp" #include "moc_logbook.cpp" LogBook::LogBook (Configuration const * configuration) : config_ {configuration} + , worked_before_ {configuration} { + Q_ASSERT (configuration); connect (&worked_before_, &WorkedBefore::finished_loading, this, &LogBook::finished_loading); } @@ -136,3 +140,23 @@ QByteArray LogBook::QSOToADIF (QString const& hisCall, QString const& hisGrid, Q } return t.toLatin1(); } + +CabrilloLog * LogBook::contest_log () +{ + // lazy create of Cabrillo log object instance + if (!m_contest_log) + { + m_contest_log.reset (new CabrilloLog {config_}); + } + return m_contest_log.data (); +} + +FoxLog * LogBook::fox_log () +{ + // lazy create of Fox log object instance + if (!m_fox_log) + { + m_fox_log.reset (new FoxLog {config_}); + } + return m_fox_log.data (); +} diff --git a/logbook/logbook.h b/logbook/logbook.h index 1850aa01f..2fd73bd3b 100644 --- a/logbook/logbook.h +++ b/logbook/logbook.h @@ -8,12 +8,15 @@ #include #include +#include #include "WorkedBefore.hpp" class Configuration; class QByteArray; class QDateTime; +class CabrilloLog; +class FoxLog; class LogBook final : public QObject @@ -43,9 +46,14 @@ public: Q_SIGNAL void finished_loading (int worked_before_record_count, QString const& error) const; + CabrilloLog * contest_log (); + FoxLog * fox_log (); + private: Configuration const * config_; WorkedBefore worked_before_; + QScopedPointer m_contest_log; + QScopedPointer m_fox_log; }; #endif diff --git a/models/CabrilloLog.cpp b/models/CabrilloLog.cpp index 6849cdd61..6a637f07d 100644 --- a/models/CabrilloLog.cpp +++ b/models/CabrilloLog.cpp @@ -12,6 +12,7 @@ #include #include "Configuration.hpp" #include "Bands.hpp" +#include "logbook/AD1CCty.hpp" #include "qt_db_helpers.hpp" #include "pimpl_impl.hpp" @@ -232,3 +233,15 @@ void CabrilloLog::export_qsos (QTextStream& stream) const .arg (m_->export_query_.value (rcvd_index).toString (), -13); } } + +QSet CabrilloLog::unique_DXCC_entities (AD1CCty const& countries) const +{ + QSqlQuery q {"SELECT UNIQUE CALL FROM cabrillo_log"}; + auto call_index = q.record ().indexOf ("call"); + QSet entities; + while (q.next ()) + { + entities << countries.lookup (q.value(call_index).toString ()).primary_prefix; + } + return entities; +} diff --git a/models/CabrilloLog.hpp b/models/CabrilloLog.hpp index 43b9e8f94..ad0255297 100644 --- a/models/CabrilloLog.hpp +++ b/models/CabrilloLog.hpp @@ -2,14 +2,16 @@ #define CABRILLO_LOG_HPP_ #include +#include +#include #include "Radio.hpp" #include "pimpl_h.hpp" class Configuration; class QDateTime; -class QString; class QSqlTableModel; class QTextStream; +class AD1CCty; class CabrilloLog final : private boost::noncopyable @@ -28,6 +30,7 @@ public: QSqlTableModel * model (); void reset (); void export_qsos (QTextStream&) const; + QSet unique_DXCC_entities (AD1CCty const&) const; private: class impl; diff --git a/widgets/logqso.cpp b/widgets/logqso.cpp index acbb340bc..a9aa18341 100644 --- a/widgets/logqso.cpp +++ b/widgets/logqso.cpp @@ -16,11 +16,12 @@ #include "moc_logqso.cpp" LogQSO::LogQSO(QString const& programTitle, QSettings * settings - , Configuration const * config, QWidget *parent) + , Configuration const * config, LogBook * log, QWidget *parent) : QDialog {parent, Qt::WindowStaysOnTopHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint} , ui(new Ui::LogQSO) , m_settings (settings) , m_config {config} + , m_log {log} { ui->setupUi(this); setWindowTitle(programTitle + " - Log QSO"); @@ -57,8 +58,7 @@ void LogQSO::storeSettings () const void LogQSO::initLogQSO(QString const& hisCall, QString const& hisGrid, QString mode, QString const& rptSent, QString const& rptRcvd, QDateTime const& dateTimeOn, QDateTime const& dateTimeOff, - Radio::Frequency dialFreq, bool noSuffix, QString xSent, QString xRcvd, - CabrilloLog * cabrillo_log) + Radio::Frequency dialFreq, bool noSuffix, QString xSent, QString xRcvd) { if(!isHidden()) return; ui->call->setText (hisCall); @@ -100,7 +100,6 @@ void LogQSO::initLogQSO(QString const& hisCall, QString const& hisGrid, QString ui->loggedOperator->setText(m_config->opCall()); ui->exchSent->setText (xSent); ui->exchRcvd->setText (xRcvd); - m_cabrilloLog = cabrillo_log; using SpOp = Configuration::SpecialOperatingActivity; auto special_op = m_config->special_op_id (); @@ -158,7 +157,7 @@ void LogQSO::accept() return; // without accepting } - if (!m_cabrilloLog->add_QSO (m_dialFreq, dateTimeOff, hisCall, xsent, xrcvd)) + if (!m_log->contest_log ()->add_QSO (m_dialFreq, dateTimeOff, hisCall, xsent, xrcvd)) { show (); MessageBox::warning_message (this, tr ("Invalid QSO Data"), diff --git a/widgets/logqso.h b/widgets/logqso.h index 82a75b7bd..739a601f7 100644 --- a/widgets/logqso.h +++ b/widgets/logqso.h @@ -17,19 +17,19 @@ namespace Ui { class QSettings; class Configuration; class QByteArray; -class CabrilloLog; +class LogBook; class LogQSO : public QDialog { Q_OBJECT public: - explicit LogQSO(QString const& programTitle, QSettings *, Configuration const *, QWidget *parent = 0); + explicit LogQSO(QString const& programTitle, QSettings *, Configuration const *, LogBook *, QWidget *parent = 0); ~LogQSO(); void initLogQSO(QString const& hisCall, QString const& hisGrid, QString mode, QString const& rptSent, QString const& rptRcvd, QDateTime const& dateTimeOn, QDateTime const& dateTimeOff, Radio::Frequency dialFreq, - bool noSuffix, QString xSent, QString xRcvd, CabrilloLog *); + bool noSuffix, QString xSent, QString xRcvd); public slots: void accept(); @@ -54,12 +54,12 @@ private: QScopedPointer ui; QSettings * m_settings; Configuration const * m_config; + LogBook * m_log; QString m_txPower; QString m_comments; Radio::Frequency m_dialFreq; QString m_myCall; QString m_myGrid; - CabrilloLog * m_cabrilloLog; }; #endif // LogQSO_H diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index ef5dd616b..ef1ca5705 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -244,7 +244,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, m_echoGraph (new EchoGraph(m_settings)), m_fastGraph (new FastGraph(m_settings)), // no parent so that it has a taskbar icon - m_logDlg (new LogQSO (program_title (), m_settings, &m_config, nullptr)), + m_logDlg (new LogQSO (program_title (), m_settings, &m_config, &m_logBook, nullptr)), m_lastDialFreq {0}, m_dialFreqRxWSPR {0}, m_detector {new Detector {RX_SAMPLE_RATE, double(NTMAX), downSampleFactor}}, @@ -2544,16 +2544,14 @@ void MainWindow::on_actionAstronomical_data_toggled (bool checked) void MainWindow::on_fox_log_action_triggered() { - if (!m_foxLog) m_foxLog.reset (new FoxLog {&m_config}); if (!m_foxLogWindow) { - m_foxLogWindow.reset (new FoxLogWindow {m_settings, &m_config, m_foxLog.data ()}); + m_foxLogWindow.reset (new FoxLogWindow {m_settings, &m_config, m_logBook.fox_log ()}); // Connect signals from fox log window connect (this, &MainWindow::finished, m_foxLogWindow.data (), &FoxLogWindow::close); connect (m_foxLogWindow.data (), &FoxLogWindow::reset_log_model, [this] () { - if (!m_foxLog) m_foxLog.reset (new FoxLog {&m_config}); - m_foxLog->reset (); + m_logBook.fox_log ()->reset (); }); } m_foxLogWindow->showNormal (); @@ -2563,10 +2561,9 @@ void MainWindow::on_fox_log_action_triggered() void MainWindow::on_contest_log_action_triggered() { - if (!m_cabrilloLog) m_cabrilloLog.reset (new CabrilloLog {&m_config}); if (!m_contestLogWindow) { - m_contestLogWindow.reset (new CabrilloLogWindow {m_settings, &m_config, m_cabrilloLog->model ()}); + m_contestLogWindow.reset (new CabrilloLogWindow {m_settings, &m_config, m_logBook.contest_log ()->model ()}); // Connect signals from contest log window connect (this, &MainWindow::finished, m_contestLogWindow.data (), &CabrilloLogWindow::close); @@ -5521,15 +5518,9 @@ void MainWindow::on_logQSOButton_clicked() //Log QSO button default: break; } - auto special_op = m_config.special_op_id (); - if (SpecOp::NONE < special_op && special_op < SpecOp::FOX) - { - if (!m_cabrilloLog) m_cabrilloLog.reset (new CabrilloLog {&m_config}); - } m_logDlg->initLogQSO (m_hisCall, grid, m_modeTx, m_rptSent, m_rptRcvd, m_dateTimeQSOOn, dateTimeQSOOff, m_freqNominal + - ui->TxFreqSpinBox->value(), m_noSuffix, m_xSent, m_xRcvd, - m_cabrilloLog.data ()); + ui->TxFreqSpinBox->value(), m_noSuffix, m_xSent, m_xRcvd); m_inQSOwith=""; } @@ -6369,15 +6360,13 @@ void MainWindow::on_reset_cabrillo_log_action_triggered () "for export in your Cabrillo log."))) { if(m_config.RTTY_Exchange()!="SCC") ui->sbSerialNumber->setValue(1); - if (!m_cabrilloLog) m_cabrilloLog.reset (new CabrilloLog {&m_config}); - m_cabrilloLog->reset (); + m_logBook.contest_log ()->reset (); } } void MainWindow::on_actionExport_Cabrillo_log_triggered() { - if (!m_cabrilloLog) m_cabrilloLog.reset (new CabrilloLog {&m_config}); - if (QDialog::Accepted == ExportCabrillo {m_settings, &m_config, m_cabrilloLog.data ()}.exec()) + if (QDialog::Accepted == ExportCabrillo {m_settings, &m_config, m_logBook.contest_log ()}.exec()) { MessageBox::information_message (this, tr ("Cabrillo Log saved")); } @@ -8459,9 +8448,8 @@ list2Done: m_hisGrid=m_foxQSO[hc1].grid; m_rptSent=m_foxQSO[hc1].sent; m_rptRcvd=m_foxQSO[hc1].rcvd; - if (!m_foxLog) m_foxLog.reset (new FoxLog {&m_config}); if (!m_foxLogWindow) on_fox_log_action_triggered (); - if (m_foxLog->add_QSO (QSO_time, m_hisCall, m_hisGrid, m_rptSent, m_rptRcvd, m_lastBand)) + if (m_logBook.fox_log ()->add_QSO (QSO_time, m_hisCall, m_hisGrid, m_rptSent, m_rptRcvd, m_lastBand)) { writeFoxQSO (QString {" Log: %1 %2 %3 %4 %5"}.arg (m_hisCall).arg (m_hisGrid) .arg (m_rptSent).arg (m_rptRcvd).arg (m_lastBand)); diff --git a/widgets/mainwindow.h b/widgets/mainwindow.h index ffa71c1fe..03d009a76 100644 --- a/widgets/mainwindow.h +++ b/widgets/mainwindow.h @@ -72,9 +72,7 @@ class WideGraph; class LogQSO; class Transceiver; class MessageAveraging; -class FoxLog; class FoxLogWindow; -class CabrilloLog; class CabrilloLogWindow; class ColorHighlighting; class MessageClient; @@ -376,9 +374,7 @@ private: QScopedPointer m_prefixes; QScopedPointer m_mouseCmnds; QScopedPointer m_msgAvgWidget; - QScopedPointer m_foxLog; QScopedPointer m_foxLogWindow; - QScopedPointer m_cabrilloLog; QScopedPointer m_contestLogWindow; QScopedPointer m_colorHighlighting; Transceiver::TransceiverState m_rigState; From 94b86c3b2991a490ff48f2204d140706d38514ba Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Thu, 30 May 2019 04:10:55 +0100 Subject: [PATCH 02/10] Finally fixed the default item delegate creation mechanism --- Configuration.cpp | 6 ------ MetaDataRegistry.cpp | 33 +++++++++++++++++++++++++-------- MetaDataRegistry.hpp | 3 --- 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/Configuration.cpp b/Configuration.cpp index 11c7be369..08ee2e9d3 100644 --- a/Configuration.cpp +++ b/Configuration.cpp @@ -1122,9 +1122,6 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network ui_->frequencies_table_view->setColumnHidden (FrequencyList_v2::frequency_mhz_column, true); // delegates - auto frequencies_item_delegate = new QStyledItemDelegate {this}; - frequencies_item_delegate->setItemEditorFactory (item_editor_factory ()); - ui_->frequencies_table_view->setItemDelegate (frequencies_item_delegate); ui_->frequencies_table_view->setItemDelegateForColumn (FrequencyList_v2::region_column, new ForeignKeyDelegate {®ions_, 0, this}); ui_->frequencies_table_view->setItemDelegateForColumn (FrequencyList_v2::mode_column, new ForeignKeyDelegate {&modes_, 0, this}); @@ -1163,9 +1160,6 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network ui_->stations_table_view->sortByColumn (StationList::band_column, Qt::AscendingOrder); // stations delegates - auto stations_item_delegate = new QStyledItemDelegate {this}; - stations_item_delegate->setItemEditorFactory (item_editor_factory ()); - ui_->stations_table_view->setItemDelegate (stations_item_delegate); ui_->stations_table_view->setItemDelegateForColumn (StationList::band_column, new ForeignKeyDelegate {&bands_, &next_stations_, 0, StationList::band_column, this}); // stations actions diff --git a/MetaDataRegistry.cpp b/MetaDataRegistry.cpp index 6ce9a7f92..32af71b4d 100644 --- a/MetaDataRegistry.cpp +++ b/MetaDataRegistry.cpp @@ -17,14 +17,33 @@ #include "widgets/FrequencyLineEdit.hpp" #include "widgets/DateTimeEdit.hpp" -QItemEditorFactory * item_editor_factory () +namespace { - static QItemEditorFactory * our_item_editor_factory = new QItemEditorFactory; - return our_item_editor_factory; + class ItemEditorFactory final + : public QItemEditorFactory + { + public: + ItemEditorFactory () + : default_factory_ {QItemEditorFactory::defaultFactory ()} + { + } + + QWidget * createEditor (int user_type, QWidget * parent) const override + { + auto editor = QItemEditorFactory::createEditor (user_type, parent); + return editor ? editor : default_factory_->createEditor (user_type, parent); + } + + private: + QItemEditorFactory const * default_factory_; + }; } void register_types () { + auto item_editor_factory = new ItemEditorFactory; + QItemEditorFactory::setDefaultFactory (item_editor_factory); + // types in Radio.hpp are registered in their own translation unit // as they are needed in the wsjtx_udp shared library too @@ -32,12 +51,10 @@ void register_types () // used as signal/slot connection arguments since the new Qt 5.5 // Q_ENUM macro only seems to register the unqualified name - item_editor_factory ()->registerEditor (qMetaTypeId (), new QStandardItemEditorCreator ()); + item_editor_factory->registerEditor (qMetaTypeId (), new QStandardItemEditorCreator ()); //auto frequency_delta_type_id = qRegisterMetaType ("FrequencyDelta"); - item_editor_factory ()->registerEditor (qMetaTypeId (), new QStandardItemEditorCreator ()); - auto factory = new QItemEditorFactory; - factory->registerEditor (qMetaTypeId (), new QStandardItemEditorCreator ()); - QItemEditorFactory::setDefaultFactory (factory); + item_editor_factory->registerEditor (qMetaTypeId (), new QStandardItemEditorCreator ()); + item_editor_factory->registerEditor (qMetaTypeId (), new QStandardItemEditorCreator ()); // Frequency list model qRegisterMetaTypeStreamOperators ("Item_v2"); diff --git a/MetaDataRegistry.hpp b/MetaDataRegistry.hpp index 43000d201..8bf3c80dc 100644 --- a/MetaDataRegistry.hpp +++ b/MetaDataRegistry.hpp @@ -1,9 +1,6 @@ #ifndef META_DATA_REGISTRY_HPP__ #define META_DATA_REGISTRY_HPP__ -class QItemEditorFactory; - -QItemEditorFactory * item_editor_factory (); void register_types (); #endif From 425b0e89a8ace7fa461bcb588b4d0209cb09fb88 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Thu, 30 May 2019 21:30:48 +0100 Subject: [PATCH 03/10] Move 40m FT4 frequency up 500 Hz to clear W1AW code practice QRG This is still provisional but by zero beating W1AW we should avoid most QRM to W1AW. --- models/FrequencyList.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/models/FrequencyList.cpp b/models/FrequencyList.cpp index b135a512a..c0170a499 100644 --- a/models/FrequencyList.cpp +++ b/models/FrequencyList.cpp @@ -127,7 +127,9 @@ namespace {7074000, Modes::FT8, IARURegions::ALL}, {7076000, Modes::JT65, IARURegions::ALL}, {7078000, Modes::JT9, IARURegions::ALL}, - {7047000, Modes::FT4, IARURegions::ALL}, // provisional + {7047500, Modes::FT4, IARURegions::ALL}, // provisional - moved + // up 500Hz to clear + // W1AW code practice QRG // Band plans (all USB dial unless stated otherwise) // From 19c46774b494f29360f179d8c66acb199da3801e Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Thu, 30 May 2019 22:20:09 +0100 Subject: [PATCH 04/10] Moving toward multiplier and dupe detection for contest modes There's not much to show for this so far but some of the necessary infrastructure is in place. --- CMakeLists.txt | 4 + Configuration.cpp | 5 + MetaDataRegistry.cpp | 4 - Radio.cpp | 8 +- Radio.hpp | 4 +- item_delegates/FrequencyDelegate.cpp | 27 ++++ item_delegates/FrequencyDelegate.hpp | 21 +++ item_delegates/FrequencyDeltaDelegate.cpp | 26 +++ item_delegates/FrequencyDeltaDelegate.hpp | 21 +++ item_delegates/item_delegates.pri | 6 +- logbook/Multiplier.cpp | 45 ++++++ logbook/Multiplier.hpp | 30 ++++ logbook/WorkedBefore.cpp | 4 +- logbook/WorkedBefore.hpp | 2 +- logbook/logbook.cpp | 35 ++++- logbook/logbook.h | 10 +- logbook/logbook.pri | 6 +- main.cpp | 2 + models/CabrilloLog.cpp | 183 ++++++++++++++++------ models/CabrilloLog.hpp | 17 +- qt_db_helpers.hpp | 1 + widgets/CabrilloLogWindow.cpp | 10 +- widgets/FrequencyDeltaLineEdit.cpp | 54 +++++++ widgets/FrequencyDeltaLineEdit.hpp | 29 ++++ widgets/FrequencyLineEdit.cpp | 18 --- widgets/FrequencyLineEdit.hpp | 18 +-- widgets/displaytext.cpp | 2 +- widgets/displaytext.h | 2 - widgets/logqso.cpp | 2 +- widgets/mainwindow.cpp | 2 +- widgets/widgets.pri | 3 +- 31 files changed, 473 insertions(+), 128 deletions(-) create mode 100644 item_delegates/FrequencyDelegate.cpp create mode 100644 item_delegates/FrequencyDelegate.hpp create mode 100644 item_delegates/FrequencyDeltaDelegate.cpp create mode 100644 item_delegates/FrequencyDeltaDelegate.hpp create mode 100644 logbook/Multiplier.cpp create mode 100644 logbook/Multiplier.hpp create mode 100644 widgets/FrequencyDeltaLineEdit.cpp create mode 100644 widgets/FrequencyDeltaLineEdit.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d5d18467c..a2d6dd396 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -237,6 +237,7 @@ set (wsjt_qt_CXXSRCS models/FrequencyList.cpp models/StationList.cpp widgets/FrequencyLineEdit.cpp + widgets/FrequencyDeltaLineEdit.cpp item_delegates/CandidateKeyFilter.cpp item_delegates/ForeignKeyDelegate.cpp validators/LiveFrequencyValidator.cpp @@ -279,9 +280,12 @@ set (wsjt_qt_CXXSRCS widgets/CabrilloLogWindow.cpp item_delegates/CallsignDelegate.cpp item_delegates/MaidenheadLocatorDelegate.cpp + item_delegates/FrequencyDelegate.cpp + item_delegates/FrequencyDeltaDelegate.cpp models/CabrilloLog.cpp logbook/AD1CCty.cpp logbook/WorkedBefore.cpp + logbook/Multiplier.cpp ) set (wsjt_qtmm_CXXSRCS diff --git a/Configuration.cpp b/Configuration.cpp index 08ee2e9d3..b78739d11 100644 --- a/Configuration.cpp +++ b/Configuration.cpp @@ -167,8 +167,11 @@ #include "MetaDataRegistry.hpp" #include "SettingsGroup.hpp" #include "widgets/FrequencyLineEdit.hpp" +#include "widgets/FrequencyDeltaLineEdit.hpp" #include "item_delegates/CandidateKeyFilter.hpp" #include "item_delegates/ForeignKeyDelegate.hpp" +#include "item_delegates/FrequencyDelegate.hpp" +#include "item_delegates/FrequencyDeltaDelegate.hpp" #include "TransceiverFactory.hpp" #include "Transceiver.hpp" #include "models/Bands.hpp" @@ -1122,6 +1125,7 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network ui_->frequencies_table_view->setColumnHidden (FrequencyList_v2::frequency_mhz_column, true); // delegates + ui_->frequencies_table_view->setItemDelegateForColumn (FrequencyList_v2::frequency_column, new FrequencyDelegate {this}); ui_->frequencies_table_view->setItemDelegateForColumn (FrequencyList_v2::region_column, new ForeignKeyDelegate {®ions_, 0, this}); ui_->frequencies_table_view->setItemDelegateForColumn (FrequencyList_v2::mode_column, new ForeignKeyDelegate {&modes_, 0, this}); @@ -1160,6 +1164,7 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network ui_->stations_table_view->sortByColumn (StationList::band_column, Qt::AscendingOrder); // stations delegates + ui_->stations_table_view->setItemDelegateForColumn (StationList::offset_column, new FrequencyDeltaDelegate {this}); ui_->stations_table_view->setItemDelegateForColumn (StationList::band_column, new ForeignKeyDelegate {&bands_, &next_stations_, 0, StationList::band_column, this}); // stations actions diff --git a/MetaDataRegistry.cpp b/MetaDataRegistry.cpp index 32af71b4d..e66a43008 100644 --- a/MetaDataRegistry.cpp +++ b/MetaDataRegistry.cpp @@ -14,7 +14,6 @@ #include "WFPalette.hpp" #include "models/IARURegions.hpp" #include "models/DecodeHighlightingModel.hpp" -#include "widgets/FrequencyLineEdit.hpp" #include "widgets/DateTimeEdit.hpp" namespace @@ -51,9 +50,6 @@ void register_types () // used as signal/slot connection arguments since the new Qt 5.5 // Q_ENUM macro only seems to register the unqualified name - item_editor_factory->registerEditor (qMetaTypeId (), new QStandardItemEditorCreator ()); - //auto frequency_delta_type_id = qRegisterMetaType ("FrequencyDelta"); - item_editor_factory->registerEditor (qMetaTypeId (), new QStandardItemEditorCreator ()); item_editor_factory->registerEditor (qMetaTypeId (), new QStandardItemEditorCreator ()); // Frequency list model diff --git a/Radio.cpp b/Radio.cpp index 3940815f4..e5b34c35d 100644 --- a/Radio.cpp +++ b/Radio.cpp @@ -74,14 +74,14 @@ namespace Radio } - QString frequency_MHz_string (Frequency f, QLocale const& locale) + QString frequency_MHz_string (Frequency f, int precision, QLocale const& locale) { - return locale.toString (f / MHz_factor, 'f', frequency_precsion); + return locale.toString (f / MHz_factor, 'f', precision); } - QString frequency_MHz_string (FrequencyDelta d, QLocale const& locale) + QString frequency_MHz_string (FrequencyDelta d, int precision, QLocale const& locale) { - return locale.toString (d / MHz_factor, 'f', frequency_precsion); + return locale.toString (d / MHz_factor, 'f', precision); } QString pretty_frequency_MHz_string (Frequency f, QLocale const& locale) diff --git a/Radio.hpp b/Radio.hpp index 7f8b0a0f4..e63abf4b8 100644 --- a/Radio.hpp +++ b/Radio.hpp @@ -42,8 +42,8 @@ namespace Radio // // Frequency type formatting // - QString UDP_EXPORT frequency_MHz_string (Frequency, QLocale const& = QLocale ()); - QString UDP_EXPORT frequency_MHz_string (FrequencyDelta, QLocale const& = QLocale ()); + QString UDP_EXPORT frequency_MHz_string (Frequency, int precision = 6, QLocale const& = QLocale ()); + QString UDP_EXPORT frequency_MHz_string (FrequencyDelta, int precision = 6, QLocale const& = QLocale ()); QString UDP_EXPORT pretty_frequency_MHz_string (Frequency, QLocale const& = QLocale ()); QString UDP_EXPORT pretty_frequency_MHz_string (double, int scale, QLocale const& = QLocale ()); QString UDP_EXPORT pretty_frequency_MHz_string (FrequencyDelta, QLocale const& = QLocale ()); diff --git a/item_delegates/FrequencyDelegate.cpp b/item_delegates/FrequencyDelegate.cpp new file mode 100644 index 000000000..b899c0563 --- /dev/null +++ b/item_delegates/FrequencyDelegate.cpp @@ -0,0 +1,27 @@ +#include "FrequencyDelegate.hpp" + +#include "widgets/FrequencyLineEdit.hpp" + +FrequencyDelegate::FrequencyDelegate (QObject * parent) + : QStyledItemDelegate {parent} +{ +} + +QWidget * FrequencyDelegate::createEditor (QWidget * parent, QStyleOptionViewItem const& + , QModelIndex const&) const +{ + auto * editor = new FrequencyLineEdit {parent}; + editor->setFrame (false); + return editor; +} + +void FrequencyDelegate::setEditorData (QWidget * editor, QModelIndex const& index) const +{ + static_cast (editor)->frequency (index.model ()->data (index, Qt::EditRole).value ()); +} + +void FrequencyDelegate::setModelData (QWidget * editor, QAbstractItemModel * model, QModelIndex const& index) const +{ + model->setData (index, static_cast (editor)->frequency (), Qt::EditRole); +} + diff --git a/item_delegates/FrequencyDelegate.hpp b/item_delegates/FrequencyDelegate.hpp new file mode 100644 index 000000000..76fc5545d --- /dev/null +++ b/item_delegates/FrequencyDelegate.hpp @@ -0,0 +1,21 @@ +#ifndef FREQUENCY_DELEGATE_HPP_ +#define FREQUENCY_DELEGATE_HPP_ + +#include + +// +// Class FrequencyDelegate +// +// Item delegate for editing a frequency in Hertz but displayed in MHz +// +class FrequencyDelegate final + : public QStyledItemDelegate +{ +public: + explicit FrequencyDelegate (QObject * parent = nullptr); + QWidget * createEditor (QWidget * parent, QStyleOptionViewItem const&, QModelIndex const&) const override; + void setEditorData (QWidget * editor, QModelIndex const&) const override; + void setModelData (QWidget * editor, QAbstractItemModel *, QModelIndex const&) const override; +}; + +#endif diff --git a/item_delegates/FrequencyDeltaDelegate.cpp b/item_delegates/FrequencyDeltaDelegate.cpp new file mode 100644 index 000000000..40d90dc58 --- /dev/null +++ b/item_delegates/FrequencyDeltaDelegate.cpp @@ -0,0 +1,26 @@ +#include "FrequencyDeltaDelegate.hpp" + +#include "widgets/FrequencyDeltaLineEdit.hpp" + +FrequencyDeltaDelegate::FrequencyDeltaDelegate (QObject * parent) + : QStyledItemDelegate {parent} +{ +} + +QWidget * FrequencyDeltaDelegate::createEditor (QWidget * parent, QStyleOptionViewItem const& + , QModelIndex const&) const +{ + auto * editor = new FrequencyDeltaLineEdit {parent}; + editor->setFrame (false); + return editor; +} + +void FrequencyDeltaDelegate::setEditorData (QWidget * editor, QModelIndex const& index) const +{ + static_cast (editor)->frequency_delta (index.model ()->data (index, Qt::EditRole).value ()); +} + +void FrequencyDeltaDelegate::setModelData (QWidget * editor, QAbstractItemModel * model, QModelIndex const& index) const +{ + model->setData (index, static_cast (editor)->frequency_delta (), Qt::EditRole); +} diff --git a/item_delegates/FrequencyDeltaDelegate.hpp b/item_delegates/FrequencyDeltaDelegate.hpp new file mode 100644 index 000000000..121bc3d94 --- /dev/null +++ b/item_delegates/FrequencyDeltaDelegate.hpp @@ -0,0 +1,21 @@ +#ifndef FREQUENCY_DELTA_DELEGATE_HPP_ +#define FREQUENCY_DELTA_DELEGATE_HPP_ + +#include + +// +// Class FrequencyDeltaDelegate +// +// Item delegate for editing a frequency delta in Hertz but displayed in MHz +// +class FrequencyDeltaDelegate final + : public QStyledItemDelegate +{ +public: + explicit FrequencyDeltaDelegate (QObject * parent = nullptr); + QWidget * createEditor (QWidget * parent, QStyleOptionViewItem const&, QModelIndex const&) const override; + void setEditorData (QWidget * editor, QModelIndex const&) const override; + void setModelData (QWidget * editor, QAbstractItemModel *, QModelIndex const&) const override; +}; + +#endif diff --git a/item_delegates/item_delegates.pri b/item_delegates/item_delegates.pri index 026e1ca57..819cb2e0c 100644 --- a/item_delegates/item_delegates.pri +++ b/item_delegates/item_delegates.pri @@ -1,11 +1,13 @@ SOURCES += \ item_delegates/ForeignKeyDelegate.cpp \ - item_delegates/FrequencyItemDelegate.cpp \ + item_delegates/FrequencyDelegate.cpp \ + item_delegates/FrequencyDeltaDelegate.cpp \ item_delegates/CallsignDelegate.cpp \ item_delegates/MaidenheadLocatorItemDelegate.cpp HEADERS += \ item_delegates/ForeignKeyDelegate.hpp \ - item_delegates/FrequencyItemDelegate.hpp \ + item_delegates/FrequencyDelegate.hpp \ + item_delegates/FrequencyDeltaDelegate.hpp \ item_delegates/CallsignDelegate.hpp \ item_delegates/MaidenheadLocatorDelegate.hpp diff --git a/logbook/Multiplier.cpp b/logbook/Multiplier.cpp new file mode 100644 index 000000000..64f6cc6f3 --- /dev/null +++ b/logbook/Multiplier.cpp @@ -0,0 +1,45 @@ +#include "Multiplier.hpp" + +#include +#include +#include +#include "models/CabrilloLog.hpp" +#include "pimpl_impl.hpp" + +class Multiplier::impl +{ +public: + impl (AD1CCty const * countries) + : countries_ {countries} + { + } + + AD1CCty const * countries_; + worked_set entities_worked_; + worked_set grids_worked_; +}; + +Multiplier::Multiplier (AD1CCty const * countries) + : m_ {countries} +{ +} + +Multiplier::~Multiplier () +{ +} + +void Multiplier::reload (CabrilloLog const * log) +{ + m_->entities_worked_ = log->unique_DXCC_entities (m_->countries_); + qDebug () << "Entities worked:" << m_->entities_worked_; +} + +auto Multiplier::entities_worked () const -> worked_set const& +{ + return m_->entities_worked_; +} + +auto Multiplier::grids_worked () const -> worked_set const& +{ + return m_->grids_worked_; +} diff --git a/logbook/Multiplier.hpp b/logbook/Multiplier.hpp new file mode 100644 index 000000000..2271dd775 --- /dev/null +++ b/logbook/Multiplier.hpp @@ -0,0 +1,30 @@ +#ifndef MULTIPLIER_HPP_ +#define MULTIPLIER_HPP_ + +#include +#include +#include "pimpl_h.hpp" + +class QString; +class AD1CCty; +class CabrilloLog; + +class Multiplier final + : private boost::noncopyable +{ +public: + using worked_item = QPair; + using worked_set = QSet; + + explicit Multiplier (AD1CCty const *); + ~Multiplier (); + void reload (CabrilloLog const *); + worked_set const& entities_worked () const; + worked_set const& grids_worked () const; + +private: + class impl; + pimpl m_; +}; + +#endif diff --git a/logbook/WorkedBefore.cpp b/logbook/WorkedBefore.cpp index 5edc51d3e..aa65ab651 100644 --- a/logbook/WorkedBefore.cpp +++ b/logbook/WorkedBefore.cpp @@ -415,9 +415,9 @@ QString const& WorkedBefore::path () const return m_->path_; } -AD1CCty const& WorkedBefore::countries () const +AD1CCty const * WorkedBefore::countries () const { - return m_->prefixes_; + return &m_->prefixes_; } bool WorkedBefore::add (QString const& call diff --git a/logbook/WorkedBefore.hpp b/logbook/WorkedBefore.hpp index 8b115351f..1aae1aca4 100644 --- a/logbook/WorkedBefore.hpp +++ b/logbook/WorkedBefore.hpp @@ -29,7 +29,7 @@ public: , QByteArray const& ADIF_record); QString const& path () const; - AD1CCty const& countries () const; + AD1CCty const * countries () const; bool country_worked (QString const& call, QString const& mode, QString const& band) const; bool grid_worked (QString const& grid, QString const& mode, QString const& band) const; bool call_worked (QString const& call, QString const& mode, QString const& band) const; diff --git a/logbook/logbook.cpp b/logbook/logbook.cpp index bde52228c..a154664d5 100644 --- a/logbook/logbook.cpp +++ b/logbook/logbook.cpp @@ -3,6 +3,8 @@ #include #include "Configuration.hpp" #include "AD1CCty.hpp" +#include "Multiplier.hpp" +#include "logbook/AD1CCty.hpp" #include "models/CabrilloLog.hpp" #include "models/FoxLog.hpp" @@ -16,6 +18,10 @@ LogBook::LogBook (Configuration const * configuration) connect (&worked_before_, &WorkedBefore::finished_loading, this, &LogBook::finished_loading); } +LogBook::~LogBook () +{ +} + void LogBook::match (QString const& call, QString const& mode, QString const& grid, AD1CCty::Record const& looked_up, bool& callB4, @@ -144,19 +150,36 @@ QByteArray LogBook::QSOToADIF (QString const& hisCall, QString const& hisGrid, Q CabrilloLog * LogBook::contest_log () { // lazy create of Cabrillo log object instance - if (!m_contest_log) + if (!contest_log_) { - m_contest_log.reset (new CabrilloLog {config_}); + contest_log_.reset (new CabrilloLog {config_}); + if (!multiplier_) + { + multiplier_.reset (new Multiplier {countries ()}); + } + connect (contest_log_.data (), &CabrilloLog::data_changed, [this] () { + multiplier_->reload (contest_log_.data ()); + }); } - return m_contest_log.data (); + return contest_log_.data (); +} + +Multiplier const * LogBook::multiplier () const +{ + // lazy create of Multiplier object instance + if (!multiplier_) + { + multiplier_.reset (new Multiplier {countries ()}); + } + return multiplier_.data (); } FoxLog * LogBook::fox_log () { // lazy create of Fox log object instance - if (!m_fox_log) + if (!fox_log_) { - m_fox_log.reset (new FoxLog {config_}); + fox_log_.reset (new FoxLog {config_}); } - return m_fox_log.data (); + return fox_log_.data (); } diff --git a/logbook/logbook.h b/logbook/logbook.h index 2fd73bd3b..c87009e48 100644 --- a/logbook/logbook.h +++ b/logbook/logbook.h @@ -16,6 +16,7 @@ class Configuration; class QByteArray; class QDateTime; class CabrilloLog; +class Multiplier; class FoxLog; class LogBook final @@ -25,13 +26,14 @@ class LogBook final public: LogBook (Configuration const *); + ~LogBook (); QString const& path () const {return worked_before_.path ();} bool add (QString const& call , QString const& grid , QString const& band , QString const& mode , QByteArray const& ADIF_record); - AD1CCty const& countries () const {return worked_before_.countries ();} + AD1CCty const * countries () const {return worked_before_.countries ();} void rescan (); void match (QString const& call, QString const& mode, QString const& grid, AD1CCty::Record const&, bool& callB4, bool& countryB4, @@ -47,13 +49,15 @@ public: Q_SIGNAL void finished_loading (int worked_before_record_count, QString const& error) const; CabrilloLog * contest_log (); + Multiplier const * multiplier () const; FoxLog * fox_log (); private: Configuration const * config_; WorkedBefore worked_before_; - QScopedPointer m_contest_log; - QScopedPointer m_fox_log; + QScopedPointer contest_log_; + QScopedPointer mutable multiplier_; + QScopedPointer fox_log_; }; #endif diff --git a/logbook/logbook.pri b/logbook/logbook.pri index fcac62524..e67cdebd3 100644 --- a/logbook/logbook.pri +++ b/logbook/logbook.pri @@ -2,10 +2,12 @@ SOURCES += \ logbook/countriesworked.cpp \ logbook/logbook.cpp \ logbook/AD1CCty.cpp \ - logbook/WorkedBefore.cpp + logbook/WorkedBefore.cpp \ + logbook/Multiplier.cpp HEADERS += \ logbook/WorkedBefore.hpp \ logbook/logbook.h \ logbook/countriesworked.h \ - logbook/AD1CCty.hpp + logbook/AD1CCty.hpp \ + logbook/Multiplier.hpp diff --git a/main.cpp b/main.cpp index 1ea0c6a49..9070ce33f 100644 --- a/main.cpp +++ b/main.cpp @@ -380,10 +380,12 @@ int main(int argc, char *argv[]) } catch (std::exception const& e) { + MessageBox::critical_message (nullptr, QApplication::translate ("main", "Fatal error"), e.what ()); std::cerr << "Error: " << e.what () << '\n'; } catch (...) { + MessageBox::critical_message (nullptr, QApplication::translate ("main", "Unexpected fatal error")); std::cerr << "Unexpected fatal error\n"; throw; // hoping the runtime might tell us more about the exception } diff --git a/models/CabrilloLog.cpp b/models/CabrilloLog.cpp index 6a637f07d..5c3b0ef5b 100644 --- a/models/CabrilloLog.cpp +++ b/models/CabrilloLog.cpp @@ -20,59 +20,122 @@ class CabrilloLog::impl final : public QSqlTableModel { public: - impl (Configuration const *); + impl (CabrilloLog *, Configuration const *); - QVariant data (QModelIndex const& index, int role) const + int columnCount (QModelIndex const& /*index */) const override { - auto value = QSqlTableModel::data (index, role); - if (index.column () == fieldIndex ("when") - && (Qt::DisplayRole == role || Qt::EditRole == role)) + return QSqlTableModel::columnCount () + 1; + } + + Qt::ItemFlags flags (QModelIndex const& index) const override + { + auto flags = QSqlTableModel::flags (index); + if (index.isValid () && index.column () == columnCount (index) - 1) { - auto t = QDateTime::fromMSecsSinceEpoch (value.toULongLong () * 1000ull, Qt::UTC); + flags = Qt::ItemIsEnabled; + } + return flags; + } + + QVariant data (QModelIndex const& model_index, int role) const override + { + QVariant value; + if (model_index.isValid () && model_index.column () == columnCount (model_index) - 1) + { // derive band column if (Qt::DisplayRole == role) { - QLocale locale; - return locale.toString (t, locale.dateFormat (QLocale::ShortFormat) + " hh:mm:ss"); + value = configuration_->bands ()->find (QSqlTableModel::data (index (model_index.row (), fieldIndex ("frequency"))).toULongLong ()); + } + } + else + { + value = QSqlTableModel::data (model_index, role); + if (model_index.column () == fieldIndex ("frequency") && Qt::DisplayRole == role) + { + value = Radio::frequency_MHz_string (value.value (), 3); // kHz precision + } + else if (model_index.column () == fieldIndex ("when") + && (Qt::DisplayRole == role || Qt::EditRole == role)) + { // adjust date/time to Qt format + auto t = QDateTime::fromMSecsSinceEpoch (value.toULongLong () * 1000ull, Qt::UTC); + if (Qt::DisplayRole == role) + { + QLocale locale; + return locale.toString (t, locale.dateFormat (QLocale::ShortFormat) + " hh:mm:ss"); + } + value = t; } - value = t; } return value; } QString cabrillo_frequency_string (Radio::Frequency frequency) const; + void create_table (); + CabrilloLog * self_; Configuration const * configuration_; QSqlQuery mutable dupe_query_; QSqlQuery mutable export_query_; + bool adding_row_; }; -CabrilloLog::impl::impl (Configuration const * configuration) - : QSqlTableModel {} +CabrilloLog::impl::impl (CabrilloLog * self, Configuration const * configuration) + : self_ {self} , configuration_ {configuration} + , adding_row_ {false} { if (!database ().tables ().contains ("cabrillo_log")) + { + create_table (); + } + + setEditStrategy (QSqlTableModel::OnFieldChange); + setTable ("cabrillo_log"); + if (-1 != fieldIndex ("band")) // schema out of date { QSqlQuery query; SQL_error_check (query, static_cast (&QSqlQuery::exec), - "CREATE TABLE cabrillo_log (" - " id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," - " frequency INTEGER NOT NULL," - " \"when\" DATETIME NOT NULL," - " call VARCHAR(20) NOT NULL," - " exchange_sent VARCHAR(32) NOT NULL," - " exchange_rcvd VARCHAR(32) NOT NULL," - " band VARCHAR(6) NOT NULL" - ")"); + "DROP TABLE IF EXISTS cabrillo_log_backup"); + SQL_error_check (query, static_cast (&QSqlQuery::exec), + "CREATE TABLE cabrillo_log_backup AS SELECT * FROM cabrillo_log"); + SQL_error_check (query, static_cast (&QSqlQuery::exec), + "DROP TABLE cabrillo_log"); + create_table (); + setTable ("cabrillo_log"); } + setHeaderData (fieldIndex ("frequency"), Qt::Horizontal, tr ("Freq(MHz)")); + setHeaderData (fieldIndex ("mode"), Qt::Horizontal, tr ("Mode")); + setHeaderData (fieldIndex ("when"), Qt::Horizontal, tr ("Date & Time(UTC)")); + setHeaderData (fieldIndex ("call"), Qt::Horizontal, tr ("Call")); + setHeaderData (fieldIndex ("exchange_sent"), Qt::Horizontal, tr ("Sent")); + setHeaderData (fieldIndex ("exchange_rcvd"), Qt::Horizontal, tr ("Rcvd")); + setHeaderData (columnCount (QModelIndex {}) - 1, Qt::Horizontal, tr ("Band")); + + // This descending order by time is important, it makes the view + // place the latest row at the top, without this the model/view + // interactions are both sluggish and unhelpful. + setSort (fieldIndex ("when"), Qt::DescendingOrder); + + connect (this, &CabrilloLog::impl::modelReset, self_, &CabrilloLog::data_changed); + connect (this, &CabrilloLog::impl::dataChanged, [this] (QModelIndex const& tl, QModelIndex const& br) { + if (!adding_row_ && !(tl == br)) // ignore single cell changes + // as a another change for the + // whole row will follow + { + Q_EMIT self_->data_changed (); + } + }); + + SQL_error_check (*this, &QSqlTableModel::select); + SQL_error_check (dupe_query_, &QSqlQuery::prepare, "SELECT " - " COUNT(*) " + " frequency " " FROM " " cabrillo_log " " WHERE " - " call = :call " - " AND band = :band"); + " call = :call "); SQL_error_check (export_query_, &QSqlQuery::prepare, "SELECT " @@ -85,29 +148,28 @@ CabrilloLog::impl::impl (Configuration const * configuration) " cabrillo_log " " ORDER BY " " \"when\""); - - setEditStrategy (QSqlTableModel::OnFieldChange); - setTable ("cabrillo_log"); - setHeaderData (fieldIndex ("frequency"), Qt::Horizontal, tr ("Freq(kHz)")); - setHeaderData (fieldIndex ("when"), Qt::Horizontal, tr ("Date & Time(UTC)")); - setHeaderData (fieldIndex ("call"), Qt::Horizontal, tr ("Call")); - setHeaderData (fieldIndex ("exchange_sent"), Qt::Horizontal, tr ("Sent")); - setHeaderData (fieldIndex ("exchange_rcvd"), Qt::Horizontal, tr ("Rcvd")); - setHeaderData (fieldIndex ("band"), Qt::Horizontal, tr ("Band")); +} - // This descending order by time is important, it makes the view - // place the latest row at the top, without this the model/view - // interactions are both sluggish and unhelpful. - setSort (fieldIndex ("when"), Qt::DescendingOrder); - - SQL_error_check (*this, &QSqlTableModel::select); +void CabrilloLog::impl::create_table () +{ + QSqlQuery query; + SQL_error_check (query, static_cast (&QSqlQuery::exec), + "CREATE TABLE cabrillo_log (" + " id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," + " frequency INTEGER NOT NULL," + " mode VARCHAR(6) NOT NULL," + " \"when\" DATETIME NOT NULL," + " call VARCHAR(20) NOT NULL," + " exchange_sent VARCHAR(32) NOT NULL," + " exchange_rcvd VARCHAR(32) NOT NULL" + ")"); } // frequency here is in kHz QString CabrilloLog::impl::cabrillo_frequency_string (Radio::Frequency frequency) const { QString result; - auto band = configuration_->bands ()->find (frequency * 1000ull); + auto band = configuration_->bands ()->find (frequency); if ("1mm" == band) result = "LIGHT"; else if ("2mm" == band) result = "241G"; else if ("2.5mm" == band) result = "134G"; @@ -125,12 +187,15 @@ QString CabrilloLog::impl::cabrillo_frequency_string (Radio::Frequency frequency else if ("2m" == band) result = "144"; else if ("4m" == band) result = "70"; else if ("6m" == band) result = "50"; - else result = QString::number (frequency); + else result = QString::number (frequency / 1000ull); return result; } -CabrilloLog::CabrilloLog (Configuration const * configuration) - : m_ {configuration} +#include "moc_CabrilloLog.cpp" + +CabrilloLog::CabrilloLog (Configuration const * configuration, QObject * parent) + : QObject {parent} + , m_ {this, configuration} { Q_ASSERT (configuration); } @@ -159,11 +224,12 @@ namespace } } -bool CabrilloLog::add_QSO (Frequency frequency, QDateTime const& when, QString const& call +bool CabrilloLog::add_QSO (Frequency frequency, QString const& mode, QDateTime const& when, QString const& call , QString const& exchange_sent, QString const& exchange_received) { auto record = m_->record (); - record.setValue ("frequency", frequency / 1000ull); // kHz + record.setValue ("frequency", frequency); + record.setValue ("mode", mode); if (!when.isNull ()) { record.setValue ("when", when.toMSecsSinceEpoch () / 1000ull); @@ -175,15 +241,16 @@ bool CabrilloLog::add_QSO (Frequency frequency, QDateTime const& when, QString c set_value_maybe_null (record, "call", call); set_value_maybe_null (record, "exchange_sent", exchange_sent); set_value_maybe_null (record, "exchange_rcvd", exchange_received); - set_value_maybe_null (record, "band", m_->configuration_->bands ()->find (frequency)); if (m_->isDirty ()) { m_->revert (); // discard any uncommitted changes } m_->setEditStrategy (QSqlTableModel::OnManualSubmit); ConditionalTransaction transaction {*m_}; + m_->adding_row_ = true; auto ok = m_->insertRecord (-1, record); transaction.submit (); + m_->adding_row_ = false; m_->setEditStrategy (QSqlTableModel::OnFieldChange); return ok; } @@ -191,10 +258,18 @@ bool CabrilloLog::add_QSO (Frequency frequency, QDateTime const& when, QString c bool CabrilloLog::dupe (Frequency frequency, QString const& call) const { m_->dupe_query_.bindValue (":call", call); - m_->dupe_query_.bindValue (":band", m_->configuration_->bands ()->find (frequency)); SQL_error_check (m_->dupe_query_, static_cast (&QSqlQuery::exec)); - m_->dupe_query_.next (); - return m_->dupe_query_.value (0).toInt (); + auto record = m_->dupe_query_.record (); + auto frequency_index = record.indexOf ("frequency"); + while (m_->dupe_query_.next ()) + { + if (m_->configuration_->bands ()->find (m_->dupe_query_.value (frequency_index).toULongLong ()) + == m_->configuration_->bands ()->find (frequency)) + { + return true; + } + } + return false; } void CabrilloLog::reset () @@ -209,6 +284,7 @@ void CabrilloLog::reset () transaction.submit (); m_->select (); // to refresh views m_->setEditStrategy (QSqlTableModel::OnFieldChange); + Q_EMIT data_changed (); } } @@ -217,6 +293,7 @@ void CabrilloLog::export_qsos (QTextStream& stream) const SQL_error_check (m_->export_query_, static_cast (&QSqlQuery::exec)); auto record = m_->export_query_.record (); auto frequency_index = record.indexOf ("frequency"); + // auto mode_index = record.indexOf ("mode"); auto when_index = record.indexOf ("when"); auto call_index = record.indexOf ("call"); auto sent_index = record.indexOf ("exchange_sent"); @@ -234,14 +311,16 @@ void CabrilloLog::export_qsos (QTextStream& stream) const } } -QSet CabrilloLog::unique_DXCC_entities (AD1CCty const& countries) const +auto CabrilloLog::unique_DXCC_entities (AD1CCty const * countries) const -> worked_set { - QSqlQuery q {"SELECT UNIQUE CALL FROM cabrillo_log"}; + QSqlQuery q {"SELECT DISTINCT BAND, CALL FROM cabrillo_log"}; + auto band_index = q.record ().indexOf ("band"); auto call_index = q.record ().indexOf ("call"); - QSet entities; + worked_set entities; while (q.next ()) { - entities << countries.lookup (q.value(call_index).toString ()).primary_prefix; + entities << worked_item {q.value (band_index).toString () + , countries->lookup (q.value (call_index).toString ()).primary_prefix}; } return entities; } diff --git a/models/CabrilloLog.hpp b/models/CabrilloLog.hpp index ad0255297..97d8f0d88 100644 --- a/models/CabrilloLog.hpp +++ b/models/CabrilloLog.hpp @@ -1,8 +1,9 @@ #ifndef CABRILLO_LOG_HPP_ #define CABRILLO_LOG_HPP_ -#include +#include #include +#include #include #include "Radio.hpp" #include "pimpl_h.hpp" @@ -14,23 +15,29 @@ class QTextStream; class AD1CCty; class CabrilloLog final - : private boost::noncopyable + : public QObject { + Q_OBJECT + public: using Frequency = Radio::Frequency; + using worked_item = QPair; + using worked_set = QSet; - explicit CabrilloLog (Configuration const *); + explicit CabrilloLog (Configuration const *, QObject * parent = nullptr); ~CabrilloLog (); // returns false if insert fails - bool add_QSO (Frequency, QDateTime const&, QString const& call + bool add_QSO (Frequency, QString const& mode, QDateTime const&, QString const& call , QString const& report_sent, QString const& report_received); bool dupe (Frequency, QString const& call) const; QSqlTableModel * model (); void reset (); void export_qsos (QTextStream&) const; - QSet unique_DXCC_entities (AD1CCty const&) const; + worked_set unique_DXCC_entities (AD1CCty const *) const; + + Q_SIGNAL void data_changed () const; private: class impl; diff --git a/qt_db_helpers.hpp b/qt_db_helpers.hpp index 49a082f72..dcf9d0333 100644 --- a/qt_db_helpers.hpp +++ b/qt_db_helpers.hpp @@ -6,6 +6,7 @@ #include #include #include +#include "boost/core/noncopyable.hpp" template void SQL_error_check (T&& object, Func func, Args&&... args) diff --git a/widgets/CabrilloLogWindow.cpp b/widgets/CabrilloLogWindow.cpp index 2f7b44d4a..6fd753136 100644 --- a/widgets/CabrilloLogWindow.cpp +++ b/widgets/CabrilloLogWindow.cpp @@ -1,10 +1,12 @@ #include "CabrilloLogWindow.hpp" +#include #include #include #include #include "Configuration.hpp" #include "models/Bands.hpp" +#include "item_delegates/FrequencyDelegate.hpp" #include "item_delegates/ForeignKeyDelegate.hpp" #include "item_delegates/CallsignDelegate.hpp" #include "pimpl_impl.hpp" @@ -29,7 +31,7 @@ namespace switch (index.column ()) { case 1: - case 6: + case 7: return Qt::AlignRight + Qt::AlignVCenter; default: break; @@ -63,10 +65,10 @@ CabrilloLogWindow::CabrilloLogWindow (QSettings * settings, Configuration const m_->format_model_.setSourceModel (m_->log_model_); m_->ui_.log_table_view->setModel (&m_->format_model_); set_log_view (m_->ui_.log_table_view); - m_->ui_.log_table_view->setItemDelegateForColumn (3, new CallsignDelegate {this}); - m_->ui_.log_table_view->setItemDelegateForColumn (6, new ForeignKeyDelegate {configuration->bands (), 0, this}); + m_->ui_.log_table_view->setItemDelegateForColumn (1, new FrequencyDelegate {this}); + m_->ui_.log_table_view->setItemDelegateForColumn (4, new CallsignDelegate {this}); auto h_header = m_->ui_.log_table_view->horizontalHeader (); - h_header->moveSection (6, 1); // band to first column + h_header->moveSection (7, 1); // band to first column } CabrilloLogWindow::~CabrilloLogWindow () diff --git a/widgets/FrequencyDeltaLineEdit.cpp b/widgets/FrequencyDeltaLineEdit.cpp new file mode 100644 index 000000000..b7ed144b6 --- /dev/null +++ b/widgets/FrequencyDeltaLineEdit.cpp @@ -0,0 +1,54 @@ +#include "FrequencyDeltaLineEdit.hpp" + +#include + +#include +#include +#include + +#include "moc_FrequencyDeltaLineEdit.cpp" + +namespace +{ + class MHzValidator + : public QDoubleValidator + { + public: + MHzValidator (double bottom, double top, QObject * parent = nullptr) + : QDoubleValidator {bottom, top, 6, parent} + { + } + + State validate (QString& input, int& pos) const override + { + State result = QDoubleValidator::validate (input, pos); + if (Acceptable == result) + { + bool ok; + (void)QLocale {}.toDouble (input, &ok); + if (!ok) + { + result = Intermediate; + } + } + return result; + } + }; +} + +FrequencyDeltaLineEdit::FrequencyDeltaLineEdit (QWidget * parent) + : QLineEdit (parent) +{ + setValidator (new MHzValidator {-std::numeric_limits::max () / 10.e6, + std::numeric_limits::max () / 10.e6, this}); +} + +auto FrequencyDeltaLineEdit::frequency_delta () const -> FrequencyDelta +{ + return Radio::frequency_delta (text (), 6); +} + +void FrequencyDeltaLineEdit::frequency_delta (FrequencyDelta d) +{ + setText (Radio::frequency_MHz_string (d)); +} diff --git a/widgets/FrequencyDeltaLineEdit.hpp b/widgets/FrequencyDeltaLineEdit.hpp new file mode 100644 index 000000000..caa00ecba --- /dev/null +++ b/widgets/FrequencyDeltaLineEdit.hpp @@ -0,0 +1,29 @@ +#ifndef FREQUENCY_DELTA_LINE_EDIT_HPP_ +#define FREQUENCY_DELTA_LINE_EDIT_HPP_ + +#include + +#include "Radio.hpp" + +class QWidget; + +// +// MHz frequency delta line edit with validation +// +class FrequencyDeltaLineEdit final + : public QLineEdit +{ + Q_OBJECT; + Q_PROPERTY (FrequencyDelta frequency_delta READ frequency_delta WRITE frequency_delta USER true); + +public: + using FrequencyDelta = Radio::FrequencyDelta; + + explicit FrequencyDeltaLineEdit (QWidget * parent = nullptr); + + // Property frequency_delta implementation + FrequencyDelta frequency_delta () const; + void frequency_delta (FrequencyDelta); +}; + +#endif diff --git a/widgets/FrequencyLineEdit.cpp b/widgets/FrequencyLineEdit.cpp index 78f8b2e1f..1ca2ba20b 100644 --- a/widgets/FrequencyLineEdit.cpp +++ b/widgets/FrequencyLineEdit.cpp @@ -51,21 +51,3 @@ void FrequencyLineEdit::frequency (Frequency f) { setText (Radio::frequency_MHz_string (f)); } - - -FrequencyDeltaLineEdit::FrequencyDeltaLineEdit (QWidget * parent) - : QLineEdit (parent) -{ - setValidator (new MHzValidator {-std::numeric_limits::max () / 10.e6, - std::numeric_limits::max () / 10.e6, this}); -} - -auto FrequencyDeltaLineEdit::frequency_delta () const -> FrequencyDelta -{ - return Radio::frequency_delta (text (), 6); -} - -void FrequencyDeltaLineEdit::frequency_delta (FrequencyDelta d) -{ - setText (Radio::frequency_MHz_string (d)); -} diff --git a/widgets/FrequencyLineEdit.hpp b/widgets/FrequencyLineEdit.hpp index dec0feb00..7e535f5a9 100644 --- a/widgets/FrequencyLineEdit.hpp +++ b/widgets/FrequencyLineEdit.hpp @@ -8,7 +8,7 @@ class QWidget; // -// MHz frequency line edits with validation +// MHz frequency line edit with validation // class FrequencyLineEdit final : public QLineEdit @@ -26,20 +26,4 @@ public: void frequency (Frequency); }; -class FrequencyDeltaLineEdit final - : public QLineEdit -{ - Q_OBJECT; - Q_PROPERTY (FrequencyDelta frequency_delta READ frequency_delta WRITE frequency_delta USER true); - -public: - using FrequencyDelta = Radio::FrequencyDelta; - - explicit FrequencyDeltaLineEdit (QWidget * parent = nullptr); - - // Property frequency_delta implementation - FrequencyDelta frequency_delta () const; - void frequency_delta (FrequencyDelta); -}; - #endif diff --git a/widgets/displaytext.cpp b/widgets/displaytext.cpp index c0bcd4129..53d7cd5cd 100644 --- a/widgets/displaytext.cpp +++ b/widgets/displaytext.cpp @@ -271,7 +271,7 @@ QString DisplayText::appendWorkedB4 (QString message, QString call, QString cons if(call.length()<3) return message; if(!call.contains(QRegExp("[0-9]|[A-Z]"))) return message; - auto const& looked_up = logBook.countries ().lookup (call); + auto const& looked_up = logBook.countries ()->lookup (call); logBook.match (call, currentMode, grid, looked_up, callB4, countryB4, gridB4, continentB4, CQZoneB4, ITUZoneB4); logBook.match (call, currentMode, grid, looked_up, callB4onBand, countryB4onBand, gridB4onBand, continentB4onBand, CQZoneB4onBand, ITUZoneB4onBand, currentBand); diff --git a/widgets/displaytext.h b/widgets/displaytext.h index 7f308bef8..8b580b8a2 100644 --- a/widgets/displaytext.h +++ b/widgets/displaytext.h @@ -65,6 +65,4 @@ private: int modified_vertical_scrollbar_max_; }; - extern QHash m_LoTW; - #endif // DISPLAYTEXT_H diff --git a/widgets/logqso.cpp b/widgets/logqso.cpp index a9aa18341..2eaedf116 100644 --- a/widgets/logqso.cpp +++ b/widgets/logqso.cpp @@ -157,7 +157,7 @@ void LogQSO::accept() return; // without accepting } - if (!m_log->contest_log ()->add_QSO (m_dialFreq, dateTimeOff, hisCall, xsent, xrcvd)) + if (!m_log->contest_log ()->add_QSO (m_dialFreq, mode, dateTimeOff, hisCall, xsent, xrcvd)) { show (); MessageBox::warning_message (this, tr ("Invalid QSO Data"), diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index ef1ca5705..277dbfdd7 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -8267,7 +8267,7 @@ void MainWindow::houndCallers() if(!ui->textBrowser4->toPlainText().contains(paddedHoundCall)) { if(m_loggedByFox[houndCall].contains(m_lastBand)) continue; //already logged on this band if(m_foxQSO.contains(houndCall)) continue; //still in the QSO map - auto const& entity = m_logBook.countries ().lookup (houndCall); + auto const& entity = m_logBook.countries ()->lookup (houndCall); auto const& continent = AD1CCty::continent (entity.continent); //If we are using a directed CQ, ignore Hound calls that do not comply. diff --git a/widgets/widgets.pri b/widgets/widgets.pri index eef0c7857..3c2cb2444 100644 --- a/widgets/widgets.pri +++ b/widgets/widgets.pri @@ -8,13 +8,14 @@ SOURCES += \ widgets/fastplot.cpp widgets/MessageBox.cpp \ widgets/colorhighlighting.cpp widgets/ExportCabrillo.cpp \ widgets/AbstractLogWindow.cpp \ + widgets/FrequencyLineEdit.cpp widgets/FrequencyDeltaLineEdit.cpp \ widgets/FoxLogWindow.cpp widgets/CabrilloLogWindow.cpp HEADERS += \ widgets/mainwindow.h widgets/plotter.h \ widgets/about.h widgets/widegraph.h \ widgets/displaytext.h widgets/logqso.h widgets/LettersSpinBox.hpp \ - widgets/FrequencyLineEdit.hpp widgets/signalmeter.h \ + widgets/FrequencyLineEdit.hpp widgets/FrequencyDeltaLineEdit.hpp widgets/signalmeter.h \ widgets/meterwidget.h widgets/messageaveraging.h \ widgets/echoplot.h widgets/echograph.h widgets/fastgraph.h \ widgets/fastplot.h widgets/MessageBox.hpp widgets/colorhighlighting.h \ From 80c819c326135eecc6fcf5cd6d5b6941c9566c6b Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Thu, 30 May 2019 23:29:46 +0100 Subject: [PATCH 05/10] Use a new name for the revised cabrillo_log(_v2) database table This avoids most backward compatibility issues but contest logs cannot be carried forward to v2.1.0. --- models/CabrilloLog.cpp | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/models/CabrilloLog.cpp b/models/CabrilloLog.cpp index 5c3b0ef5b..dfba3d439 100644 --- a/models/CabrilloLog.cpp +++ b/models/CabrilloLog.cpp @@ -84,26 +84,13 @@ CabrilloLog::impl::impl (CabrilloLog * self, Configuration const * configuration , configuration_ {configuration} , adding_row_ {false} { - if (!database ().tables ().contains ("cabrillo_log")) + if (!database ().tables ().contains ("cabrillo_log_v2")) { create_table (); } setEditStrategy (QSqlTableModel::OnFieldChange); - setTable ("cabrillo_log"); - if (-1 != fieldIndex ("band")) // schema out of date - { - QSqlQuery query; - SQL_error_check (query, static_cast (&QSqlQuery::exec), - "DROP TABLE IF EXISTS cabrillo_log_backup"); - SQL_error_check (query, static_cast (&QSqlQuery::exec), - "CREATE TABLE cabrillo_log_backup AS SELECT * FROM cabrillo_log"); - SQL_error_check (query, static_cast (&QSqlQuery::exec), - "DROP TABLE cabrillo_log"); - create_table (); - setTable ("cabrillo_log"); - } - + setTable ("cabrillo_log_v2"); setHeaderData (fieldIndex ("frequency"), Qt::Horizontal, tr ("Freq(MHz)")); setHeaderData (fieldIndex ("mode"), Qt::Horizontal, tr ("Mode")); setHeaderData (fieldIndex ("when"), Qt::Horizontal, tr ("Date & Time(UTC)")); @@ -133,7 +120,7 @@ CabrilloLog::impl::impl (CabrilloLog * self, Configuration const * configuration "SELECT " " frequency " " FROM " - " cabrillo_log " + " cabrillo_log_v2 " " WHERE " " call = :call "); @@ -145,7 +132,7 @@ CabrilloLog::impl::impl (CabrilloLog * self, Configuration const * configuration " , call" " , exchange_rcvd" " FROM " - " cabrillo_log " + " cabrillo_log_v2 " " ORDER BY " " \"when\""); } @@ -154,7 +141,7 @@ void CabrilloLog::impl::create_table () { QSqlQuery query; SQL_error_check (query, static_cast (&QSqlQuery::exec), - "CREATE TABLE cabrillo_log (" + "CREATE TABLE cabrillo_log_v2 (" " id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," " frequency INTEGER NOT NULL," " mode VARCHAR(6) NOT NULL," @@ -313,7 +300,7 @@ void CabrilloLog::export_qsos (QTextStream& stream) const auto CabrilloLog::unique_DXCC_entities (AD1CCty const * countries) const -> worked_set { - QSqlQuery q {"SELECT DISTINCT BAND, CALL FROM cabrillo_log"}; + QSqlQuery q {"SELECT DISTINCT BAND, CALL FROM cabrillo_log_v2"}; auto band_index = q.record ().indexOf ("band"); auto call_index = q.record ().indexOf ("call"); worked_set entities; From a867a3ca18580469e593ebb572b3292121fe2887 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Tue, 28 May 2019 18:58:12 +0100 Subject: [PATCH 06/10] Updates to Omni-Rig interface in line with suggestions from Alex, VE3NEA Includes tidying up some old and obsolete code. --- DXLabSuiteCommanderTransceiver.cpp | 4 +- DXLabSuiteCommanderTransceiver.hpp | 2 +- HRDTransceiver.cpp | 2 +- HRDTransceiver.hpp | 2 +- HamlibTransceiver.cpp | 4 +- HamlibTransceiver.hpp | 2 +- OmniRigTransceiver.cpp | 303 ++++++++++++++++------------- OmniRigTransceiver.hpp | 7 +- PollingTransceiver.cpp | 70 +++---- PollingTransceiver.hpp | 4 +- TransceiverBase.cpp | 33 +++- TransceiverBase.hpp | 2 - 12 files changed, 239 insertions(+), 196 deletions(-) diff --git a/DXLabSuiteCommanderTransceiver.cpp b/DXLabSuiteCommanderTransceiver.cpp index 4e9270cfe..d745ec17f 100644 --- a/DXLabSuiteCommanderTransceiver.cpp +++ b/DXLabSuiteCommanderTransceiver.cpp @@ -127,7 +127,7 @@ int DXLabSuiteCommanderTransceiver::do_start () throw error {tr ("DX Lab Suite Commander didn't respond correctly reading frequency: ") + reply}; } - poll (); + do_poll (); return resolution; } @@ -247,7 +247,7 @@ void DXLabSuiteCommanderTransceiver::do_mode (MODE m) update_mode (m); } -void DXLabSuiteCommanderTransceiver::poll () +void DXLabSuiteCommanderTransceiver::do_poll () { #if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS bool quiet {false}; diff --git a/DXLabSuiteCommanderTransceiver.hpp b/DXLabSuiteCommanderTransceiver.hpp index b02fbef12..1196678b8 100644 --- a/DXLabSuiteCommanderTransceiver.hpp +++ b/DXLabSuiteCommanderTransceiver.hpp @@ -39,7 +39,7 @@ protected: void do_mode (MODE) override; void do_ptt (bool on) override; - void poll () override; + void do_poll () override; private: MODE get_mode (bool no_debug = false); diff --git a/HRDTransceiver.cpp b/HRDTransceiver.cpp index 3b9f90e23..c44914145 100644 --- a/HRDTransceiver.cpp +++ b/HRDTransceiver.cpp @@ -885,7 +885,7 @@ bool HRDTransceiver::is_button_checked (int button_index, bool no_debug) return "1" == reply; } -void HRDTransceiver::poll () +void HRDTransceiver::do_poll () { #if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS bool quiet {false}; diff --git a/HRDTransceiver.hpp b/HRDTransceiver.hpp index e17134d53..6ccd72d52 100644 --- a/HRDTransceiver.hpp +++ b/HRDTransceiver.hpp @@ -48,7 +48,7 @@ protected: void do_ptt (bool on) override; // Implement the PollingTransceiver interface. - void poll () override; + void do_poll () override; private: QString send_command (QString const&, bool no_debug = false, bool prepend_context = true, bool recurse = false); diff --git a/HamlibTransceiver.cpp b/HamlibTransceiver.cpp index f2e55bb4b..da0623e22 100644 --- a/HamlibTransceiver.cpp +++ b/HamlibTransceiver.cpp @@ -632,7 +632,7 @@ int HamlibTransceiver::do_start () resolution = -1; // best guess } - poll (); + do_poll (); TRACE_CAT ("HamlibTransceiver", "exit" << state () << "reversed =" << reversed_ << "resolution = " << resolution); return resolution; @@ -898,7 +898,7 @@ void HamlibTransceiver::do_mode (MODE mode) update_mode (mode); } -void HamlibTransceiver::poll () +void HamlibTransceiver::do_poll () { #if !WSJT_TRACE_CAT_POLLS #if defined (NDEBUG) diff --git a/HamlibTransceiver.hpp b/HamlibTransceiver.hpp index c8c0f74d7..17f1c28d3 100644 --- a/HamlibTransceiver.hpp +++ b/HamlibTransceiver.hpp @@ -40,7 +40,7 @@ class HamlibTransceiver final void do_mode (MODE) override; void do_ptt (bool) override; - void poll () override; + void do_poll () override; void error_check (int ret_code, QString const& doing) const; void set_conf (char const * item, char const * value); diff --git a/OmniRigTransceiver.cpp b/OmniRigTransceiver.cpp index 09f1e5cfa..4d3bbf1f8 100644 --- a/OmniRigTransceiver.cpp +++ b/OmniRigTransceiver.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "qt_helpers.hpP" @@ -109,6 +110,15 @@ OmniRigTransceiver::OmniRigTransceiver (std::unique_ptr wrapped { } +// returns false on time out +bool OmniRigTransceiver::await_notification_with_timeout (int timeout) +{ + QEventLoop el; + connect (this, &OmniRigTransceiver::notified, &el, [&el] () {el.exit (1);}); + QTimer::singleShot (timeout, Qt::CoarseTimer, &el, [&el] () {el.exit (0);}); + return 1 == el.exec (); // wait for notify or timer +} + int OmniRigTransceiver::do_start () { TRACE_CAT ("OmniRigTransceiver", "starting"); @@ -182,101 +192,97 @@ int OmniRigTransceiver::do_start () .arg (readable_params_, 8, 16, QChar ('0')) .arg (writable_params_, 8, 16, QChar ('0')) .arg (rig_number_).toLocal8Bit ()); - - offline_timer_.reset (new QTimer); - offline_timer_->setSingleShot (true); - offline_timer_->setInterval (5 * 1000); - connect (&*offline_timer_, &QTimer::timeout, this, &OmniRigTransceiver::timeout_check); - - for (unsigned tries {0}; tries < 10; ++tries) + if (OmniRig::ST_ONLINE != rig_->Status ()) { - QThread::msleep (100); // wait until OmniRig polls the rig - auto f = rig_->GetRxFrequency (); - int resolution {0}; - if (f) + throw_qstring ("OmniRig: " + rig_->StatusStr ()); + } + await_notification_with_timeout (1000); + update_rx_frequency (rig_->GetRxFrequency ()); + qDebug () << "Initial state:" << state (); + int resolution {0}; + if (OmniRig::PM_UNKNOWN == rig_->Vfo () + && (writable_params_ & (OmniRig::PM_VFOA | OmniRig::PM_VFOB)) + == (OmniRig::PM_VFOA | OmniRig::PM_VFOB)) + { + // start with VFO A (probably MAIN) on rigs that we + // can't query VFO but can set explicitly + rig_->SetVfo (OmniRig::PM_VFOA); + } + auto f = state ().frequency (); + if (f % 10) return resolution; // 1Hz resolution + auto test_frequency = f - f % 100 + 55; + if (OmniRig::PM_FREQ & writable_params_) + { + rig_->SetFreq (test_frequency); + } + else if (reversed_ && (OmniRig::PM_FREQB & writable_params_)) + { + rig_->SetFreqB (test_frequency); + } + else if (!reversed_ && (OmniRig::PM_FREQA & writable_params_)) + { + rig_->SetFreqA (test_frequency); + } + else + { + throw_qstring (tr ("OmniRig: don't know how to set rig frequency")); + } + if (!await_notification_with_timeout (1000)) + { + TRACE_CAT ("OmniRigTransceiver", "do_start 1: wait timed out"); + throw_qstring (tr ("OmniRig: timeout waiting for update from rig")); + } + switch (rig_->GetRxFrequency () - test_frequency) + { + case -5: resolution = -1; break; // 10Hz truncated + case 5: resolution = 1; break; // 10Hz rounded + case -15: resolution = -2; break; // 20Hz truncated + case -55: resolution = -2; break; // 100Hz truncated + case 45: resolution = 2; break; // 100Hz rounded + } + if (1 == resolution) // may be 20Hz rounded + { + test_frequency = f - f % 100 + 51; + if (OmniRig::PM_FREQ & writable_params_) { - if (OmniRig::PM_UNKNOWN == rig_->Vfo () - && (writable_params_ & (OmniRig::PM_VFOA | OmniRig::PM_VFOB)) - == (OmniRig::PM_VFOA | OmniRig::PM_VFOB)) - { - // start with VFO A (probably MAIN) on rigs that we - // can't query VFO but can set explicitly - rig_->SetVfo (OmniRig::PM_VFOA); - } - if (f % 10) return resolution; // 1Hz resolution - auto test_frequency = f - f % 100 + 55; - if (OmniRig::PM_FREQ & writable_params_) - { - rig_->SetFreq (test_frequency); - } - else if (reversed_ && (OmniRig::PM_FREQB & writable_params_)) - { - rig_->SetFreqB (test_frequency); - } - else if (!reversed_ && (OmniRig::PM_FREQA & writable_params_)) - { - rig_->SetFreqA (test_frequency); - } - else - { - throw_qstring (tr ("OmniRig: don't know how to set rig frequency")); - } - switch (rig_->GetRxFrequency () - test_frequency) - { - case -5: resolution = -1; break; // 10Hz truncated - case 5: resolution = 1; break; // 10Hz rounded - case -15: resolution = -2; break; // 20Hz truncated - case -55: resolution = -2; break; // 100Hz truncated - case 45: resolution = 2; break; // 100Hz rounded - } - if (1 == resolution) // may be 20Hz rounded - { - test_frequency = f - f % 100 + 51; - if (OmniRig::PM_FREQ & writable_params_) - { - rig_->SetFreq (test_frequency); - } - else if (reversed_ && (OmniRig::PM_FREQB & writable_params_)) - { - rig_->SetFreqB (test_frequency); - } - else if (!reversed_ && (OmniRig::PM_FREQA & writable_params_)) - { - rig_->SetFreqA (test_frequency); - } - if (9 == rig_->GetRxFrequency () - test_frequency) - { - resolution = 2; // 20Hz rounded - } - } - if (OmniRig::PM_FREQ & writable_params_) - { - rig_->SetFreq (f); - } - else if (reversed_ && (OmniRig::PM_FREQB & writable_params_)) - { - rig_->SetFreqB (f); - } - else if (!reversed_ && (OmniRig::PM_FREQA & writable_params_)) - { - rig_->SetFreqA (f); - } - update_rx_frequency (f); - return resolution; + rig_->SetFreq (test_frequency); + } + else if (reversed_ && (OmniRig::PM_FREQB & writable_params_)) + { + rig_->SetFreqB (test_frequency); + } + else if (!reversed_ && (OmniRig::PM_FREQA & writable_params_)) + { + rig_->SetFreqA (test_frequency); + } + if (!await_notification_with_timeout (2000)) + { + TRACE_CAT ("OmniRigTransceiver", "do_start 2: wait timed out"); + throw_qstring (tr ("OmniRig: timeout waiting for update from rig")); + } + if (9 == rig_->GetRxFrequency () - test_frequency) + { + resolution = 2; // 20Hz rounded } } - throw_qstring (tr ("OmniRig: Initialization timed out")); - return 0; // keep compiler happy + if (OmniRig::PM_FREQ & writable_params_) + { + rig_->SetFreq (f); + } + else if (reversed_ && (OmniRig::PM_FREQB & writable_params_)) + { + rig_->SetFreqB (f); + } + else if (!reversed_ && (OmniRig::PM_FREQA & writable_params_)) + { + rig_->SetFreqA (f); + } + update_rx_frequency (f); + return resolution; } void OmniRigTransceiver::do_stop () { - if (offline_timer_) - { - offline_timer_->stop (); - offline_timer_.reset (); - } - QThread::msleep (200); // leave some time for pending // commands at the server end if (port_) @@ -300,17 +306,6 @@ void OmniRigTransceiver::do_stop () TRACE_CAT ("OmniRigTransceiver", "stopped"); } -void OmniRigTransceiver::do_sync (bool force_signal, bool /*no_poll*/) -{ - // nothing much we can do here, we just have to let OmniRig do its - // stuff and its first poll should send us and update that will - // trigger a update signal from us. Any attempt to query OmniRig - // leads to a whole mess of trouble since its internal state is - // garbage until it has done its first rig poll. - send_update_signal_ = force_signal; - update_complete (); -} - void OmniRigTransceiver::handle_COM_exception (int code, QString source, QString desc, QString help) { TRACE_CAT ("OmniRigTransceiver", QString::number (code) + " at " + source + ": " + desc + " (" + help + ')'); @@ -319,16 +314,20 @@ void OmniRigTransceiver::handle_COM_exception (int code, QString source, QString void OmniRigTransceiver::handle_visible_change () { + if (!omni_rig_ || omni_rig_->isNull ()) return; TRACE_CAT ("OmniRigTransceiver", "visibility change: visibility =" << omni_rig_->DialogVisible ()); } void OmniRigTransceiver::handle_rig_type_change (int rig_number) { + if (!omni_rig_ || omni_rig_->isNull ()) return; + TRACE_CAT ("OmniRigTransceiver", "rig type change: rig =" << rig_number); if (rig_number_ == rig_number) { + if (!rig_ || rig_->isNull ()) return; readable_params_ = rig_->ReadableParams (); writable_params_ = rig_->WriteableParams (); - TRACE_CAT ("OmniRigTransceiver", QString {"OmniRig rig type change to: %1 readable params = 0x%2 writable params = 0x%3 for rig %4"} + TRACE_CAT ("OmniRigTransceiver", QString {"rig type change to: %1 readable params = 0x%2 writable params = 0x%3 for rig %4"} .arg (rig_->RigType ()) .arg (readable_params_, 8, 16, QChar ('0')) .arg (writable_params_, 8, 16, QChar ('0')) @@ -338,48 +337,43 @@ void OmniRigTransceiver::handle_rig_type_change (int rig_number) void OmniRigTransceiver::handle_status_change (int rig_number) { + if (!omni_rig_ || omni_rig_->isNull ()) return; + TRACE_CAT ("OmniRigTransceiver", QString {"status change for rig %1"}.arg (rig_number).toLocal8Bit ()); if (rig_number_ == rig_number) { + if (!rig_ || rig_->isNull ()) return; auto const& status = rig_->StatusStr ().toLocal8Bit (); - TRACE_CAT ("OmniRigTransceiver", QString {"OmniRig status change: new status for rig %1 = "}.arg (rig_number).toLocal8Bit () << status); + TRACE_CAT ("OmniRigTransceiver", "OmniRig status change: new status = " << status); if (OmniRig::ST_ONLINE != rig_->Status ()) { - if (!offline_timer_->isActive ()) - { - offline_timer_->start (); // give OmniRig time to recover - } - } - else - { - offline_timer_->stop (); - update_rx_frequency (rig_->GetRxFrequency ()); - update_complete (); - TRACE_CAT ("OmniRigTransceiver", "OmniRig frequency:" << state ().frequency ()); + offline ("Rig went offline"); } + // else + // { + // update_rx_frequency (rig_->GetRxFrequency ()); + // update_complete (); + // TRACE_CAT ("OmniRigTransceiver", "frequency:" << state ().frequency ()); + // } } } -void OmniRigTransceiver::timeout_check () -{ - offline ("Rig went offline"); -} - void OmniRigTransceiver::handle_params_change (int rig_number, int params) { - if (rig_number_ == rig_number) - { - TRACE_CAT ("OmniRigTransceiver", QString {"OmniRig params change: params = 0x%1 for rig %2"} + if (!omni_rig_ || omni_rig_->isNull ()) return; + TRACE_CAT ("OmniRigTransceiver", QString {"params change: params = 0x%1 for rig %2"} .arg (params, 8, 16, QChar ('0')) .arg (rig_number).toLocal8Bit () << "state before:" << state ()); + if (rig_number_ == rig_number) + { + if (!rig_ || rig_->isNull ()) return; // starting_ = false; TransceiverState old_state {state ()}; auto need_frequency = false; - // state_.online = true; // sometimes we don't get an initial - // // OmniRig::ST_ONLINE status change - // // event + if (params & OmniRig::PM_VFOAA) { + TRACE_CAT ("OmniRigTransceiver", "VFOAA"); update_split (false); reversed_ = false; update_rx_frequency (rig_->FreqA ()); @@ -387,6 +381,7 @@ void OmniRigTransceiver::handle_params_change (int rig_number, int params) } if (params & OmniRig::PM_VFOAB) { + TRACE_CAT ("OmniRigTransceiver", "VFOAB"); update_split (true); reversed_ = false; update_rx_frequency (rig_->FreqA ()); @@ -394,6 +389,7 @@ void OmniRigTransceiver::handle_params_change (int rig_number, int params) } if (params & OmniRig::PM_VFOBA) { + TRACE_CAT ("OmniRigTransceiver", "VFOBA"); update_split (true); reversed_ = true; update_other_frequency (rig_->FreqA ()); @@ -401,6 +397,7 @@ void OmniRigTransceiver::handle_params_change (int rig_number, int params) } if (params & OmniRig::PM_VFOBB) { + TRACE_CAT ("OmniRigTransceiver", "VFOBB"); update_split (false); reversed_ = true; update_other_frequency (rig_->FreqA ()); @@ -408,64 +405,75 @@ void OmniRigTransceiver::handle_params_change (int rig_number, int params) } if (params & OmniRig::PM_VFOA) { + TRACE_CAT ("OmniRigTransceiver", "VFOA"); reversed_ = false; need_frequency = true; } if (params & OmniRig::PM_VFOB) { + TRACE_CAT ("OmniRigTransceiver", "VFOB"); reversed_ = true; need_frequency = true; } if (params & OmniRig::PM_FREQ) { + TRACE_CAT ("OmniRigTransceiver", "FREQ"); need_frequency = true; } if (params & OmniRig::PM_FREQA) { + auto f = rig_->FreqA (); + TRACE_CAT ("OmniRigTransceiver", "FREQA = " << f); if (reversed_) { - update_other_frequency (rig_->FreqA ()); + update_other_frequency (f); } else { - update_rx_frequency (rig_->FreqA ()); + update_rx_frequency (f); } } if (params & OmniRig::PM_FREQB) { + auto f = rig_->FreqB (); + TRACE_CAT ("OmniRigTransceiver", "FREQB = " << f); if (reversed_) { - update_rx_frequency (rig_->FreqB ()); + update_rx_frequency (f); } else { - update_other_frequency (rig_->FreqB ()); + update_other_frequency (f); } } if (need_frequency) { if (readable_params_ & OmniRig::PM_FREQA) { + auto f = rig_->FreqA (); + TRACE_CAT ("OmniRigTransceiver", "FREQA = " << f); if (reversed_) { - update_other_frequency (rig_->FreqA ()); + update_other_frequency (f); } else { - update_rx_frequency (rig_->FreqA ()); + update_rx_frequency (f); } need_frequency = false; } if (readable_params_ & OmniRig::PM_FREQB) { + auto f = rig_->FreqB (); + TRACE_CAT ("OmniRigTransceiver", "FREQB = " << f); if (reversed_) { - update_rx_frequency (rig_->FreqB ()); + update_rx_frequency (f); } else { - update_other_frequency (rig_->FreqB ()); + update_other_frequency (f); } need_frequency = false; } @@ -473,89 +481,113 @@ void OmniRigTransceiver::handle_params_change (int rig_number, int params) if (need_frequency && (readable_params_ & OmniRig::PM_FREQ) && !state ().ptt ()) { - update_rx_frequency (rig_->Freq ()); + auto f = rig_->Freq (); + TRACE_CAT ("OmniRigTransceiver", "FREQ = " << f); + update_rx_frequency (f); } if (params & OmniRig::PM_PITCH) { + TRACE_CAT ("OmniRigTransceiver", "PITCH"); } if (params & OmniRig::PM_RITOFFSET) { + TRACE_CAT ("OmniRigTransceiver", "RITOFFSET"); } if (params & OmniRig::PM_RIT0) { + TRACE_CAT ("OmniRigTransceiver", "RIT0"); } if (params & OmniRig::PM_VFOEQUAL) { auto f = readable_params_ & OmniRig::PM_FREQA ? rig_->FreqA () : rig_->Freq (); + auto m = map_mode (rig_->Mode ()); + TRACE_CAT ("OmniRigTransceiver", QString {"VFOEQUAL f=%1 m=%2"}.arg (f).arg (m)); update_rx_frequency (f); update_other_frequency (f); - update_mode (map_mode (rig_->Mode ())); + update_mode (m); } if (params & OmniRig::PM_VFOSWAP) { - auto temp = state ().tx_frequency (); + TRACE_CAT ("OmniRigTransceiver", "VFOSWAP"); + auto f = state ().tx_frequency (); update_other_frequency (state ().frequency ()); - update_rx_frequency (temp); + update_rx_frequency (f); update_mode (map_mode (rig_->Mode ())); } if (params & OmniRig::PM_SPLITON) { + TRACE_CAT ("OmniRigTransceiver", "SPLITON"); update_split (true); } if (params & OmniRig::PM_SPLITOFF) { + TRACE_CAT ("OmniRigTransceiver", "SPLITOFF"); update_split (false); } if (params & OmniRig::PM_RITON) { + TRACE_CAT ("OmniRigTransceiver", "RITON"); } if (params & OmniRig::PM_RITOFF) { + TRACE_CAT ("OmniRigTransceiver", "RITOFF"); } if (params & OmniRig::PM_XITON) { + TRACE_CAT ("OmniRigTransceiver", "XITON"); } if (params & OmniRig::PM_XITOFF) { + TRACE_CAT ("OmniRigTransceiver", "XITOFF"); } if (params & OmniRig::PM_RX) { + TRACE_CAT ("OmniRigTransceiver", "RX"); update_PTT (false); } if (params & OmniRig::PM_TX) { + TRACE_CAT ("OmniRigTransceiver", "TX"); update_PTT (); } if (params & OmniRig::PM_CW_U) { + TRACE_CAT ("OmniRigTransceiver", "CW-R"); update_mode (CW_R); } if (params & OmniRig::PM_CW_L) { + TRACE_CAT ("OmniRigTransceiver", "CW"); update_mode (CW); } if (params & OmniRig::PM_SSB_U) { + TRACE_CAT ("OmniRigTransceiver", "USB"); update_mode (USB); } if (params & OmniRig::PM_SSB_L) { + TRACE_CAT ("OmniRigTransceiver", "LSB"); update_mode (LSB); } if (params & OmniRig::PM_DIG_U) { + TRACE_CAT ("OmniRigTransceiver", "DATA-U"); update_mode (DIG_U); } if (params & OmniRig::PM_DIG_L) { + TRACE_CAT ("OmniRigTransceiver", "DATA-L"); update_mode (DIG_L); } if (params & OmniRig::PM_AM) { + TRACE_CAT ("OmniRigTransceiver", "AM"); update_mode (AM); } if (params & OmniRig::PM_FM) { + TRACE_CAT ("OmniRigTransceiver", "FM"); update_mode (FM); } @@ -566,6 +598,7 @@ void OmniRigTransceiver::handle_params_change (int rig_number, int params) } TRACE_CAT ("OmniRigTransceiver", "OmniRig params change: state after:" << state ()); } + Q_EMIT notified (); } void OmniRigTransceiver::handle_custom_reply (int rig_number, QVariant const& command, QVariant const& reply) @@ -573,8 +606,10 @@ void OmniRigTransceiver::handle_custom_reply (int rig_number, QVariant const& co (void)command; (void)reply; + if (!omni_rig_ || omni_rig_->isNull ()) return; if (rig_number_ == rig_number) { + if (!rig_ || rig_->isNull ()) return; TRACE_CAT ("OmniRigTransceiver", "custom command" << command.toString ().toLocal8Bit () << "with reply" << reply.toString ().toLocal8Bit () << QString ("for rig %1").arg (rig_number).toLocal8Bit ()); diff --git a/OmniRigTransceiver.hpp b/OmniRigTransceiver.hpp index c63266b69..2ba5fd4d3 100644 --- a/OmniRigTransceiver.hpp +++ b/OmniRigTransceiver.hpp @@ -38,10 +38,11 @@ public: void do_tx_frequency (Frequency, MODE, bool no_ignore) override; void do_mode (MODE) override; void do_ptt (bool on) override; - void do_sync (bool force_signal, bool no_poll) override; private: - Q_SLOT void timeout_check (); + bool await_notification_with_timeout (int timeout); + Q_SIGNAL void notified () const; + // Q_SLOT void timeout_check (); Q_SLOT void handle_COM_exception (int, QString, QString, QString); Q_SLOT void handle_visible_change (); Q_SLOT void handle_rig_type_change (int rig_number); @@ -62,7 +63,7 @@ private: QString rig_type_; int readable_params_; int writable_params_; - QScopedPointer offline_timer_; + // QScopedPointer offline_timer_; bool send_update_signal_; bool reversed_; // some rigs can reverse VFOs }; diff --git a/PollingTransceiver.cpp b/PollingTransceiver.cpp index b2bbab179..ed47144af 100644 --- a/PollingTransceiver.cpp +++ b/PollingTransceiver.cpp @@ -129,52 +129,46 @@ bool PollingTransceiver::do_pre_update () return true; } -void PollingTransceiver::do_sync (bool force_signal, bool no_poll) -{ - if (!no_poll) poll (); // tell sub-classes to update our state - - // Signal new state if it is directly requested or, what we expected - // or, hasn't become what we expected after polls_to_stabilize - // polls. Unsolicited changes will be signalled immediately unless - // they intervene in a expected sequence where they will be delayed. - if (retries_) - { - --retries_; - if (force_signal || state () == next_state_ || !retries_) - { - // our client wants a signal regardless - // or the expected state has arrived - // or there are no more retries - force_signal = true; - } - } - else if (force_signal || state () != last_signalled_state_) - { - // here is the normal passive polling path either our client has - // requested a state update regardless of change or state has - // changed asynchronously - force_signal = true; - } - - if (force_signal) - { - // reset everything, record and signal the current state - retries_ = 0; - next_state_ = state (); - last_signalled_state_ = state (); - update_complete (true); - } -} - void PollingTransceiver::handle_timeout () { QString message; + bool force_signal {false}; // we must catch all exceptions here since we are called by Qt and // inform our parent of the failure via the offline() message try { - do_sync (); + do_poll (); // tell sub-classes to update our state + + // Signal new state if it what we expected or, hasn't become + // what we expected after polls_to_stabilize polls. Unsolicited + // changes will be signalled immediately unless they intervene + // in a expected sequence where they will be delayed. + if (retries_) + { + --retries_; + if (state () == next_state_ || !retries_) + { + // the expected state has arrived or there are no more + // retries + force_signal = true; + } + } + else if (state () != last_signalled_state_) + { + // here is the normal passive polling path where state has + // changed asynchronously + force_signal = true; + } + + if (force_signal) + { + // reset everything, record and signal the current state + retries_ = 0; + next_state_ = state (); + last_signalled_state_ = state (); + update_complete (true); + } } catch (std::exception const& e) { diff --git a/PollingTransceiver.hpp b/PollingTransceiver.hpp index 0945c84ac..a9e639f9d 100644 --- a/PollingTransceiver.hpp +++ b/PollingTransceiver.hpp @@ -39,11 +39,9 @@ protected: QObject * parent); protected: - void do_sync (bool force_signal = false, bool no_poll = false) override final; - // Sub-classes implement this and fetch what they can from the rig // in a non-intrusive manner. - virtual void poll () = 0; + virtual void do_poll () = 0; void do_post_start () override final; void do_post_stop () override final; diff --git a/TransceiverBase.cpp b/TransceiverBase.cpp index ef77d51e4..43a88a4e1 100644 --- a/TransceiverBase.cpp +++ b/TransceiverBase.cpp @@ -19,10 +19,10 @@ void TransceiverBase::start (unsigned sequence_number) noexcept QString message; try { + last_sequence_number_ = sequence_number; may_update u {this, true}; shutdown (); startup (); - last_sequence_number_ = sequence_number; } catch (std::exception const& e) { @@ -46,6 +46,7 @@ void TransceiverBase::set (TransceiverState const& s, QString message; try { + last_sequence_number_ = sequence_number; may_update u {this, true}; bool was_online {requested_.online ()}; if (!s.online () && was_online) @@ -115,7 +116,6 @@ void TransceiverBase::set (TransceiverState const& s, // record what actually changed requested_.ptt (actual_.ptt ()); } - last_sequence_number_ = sequence_number; } catch (std::exception const& e) { @@ -133,10 +133,27 @@ void TransceiverBase::set (TransceiverState const& s, void TransceiverBase::startup () { - Q_EMIT resolution (do_start ()); - do_post_start (); - actual_.online (true); - requested_.online (true); + QString message; + try + { + actual_.online (true); + requested_.online (true); + auto res = do_start (); + do_post_start (); + Q_EMIT resolution (res); + } + catch (std::exception const& e) + { + message = e.what (); + } + catch (...) + { + message = unexpected; + } + if (!message.isEmpty ()) + { + offline (message); + } } void TransceiverBase::shutdown () @@ -163,8 +180,8 @@ void TransceiverBase::shutdown () } do_stop (); do_post_stop (); - actual_.online (false); - requested_.online (false); + actual_ = TransceiverState {}; + requested_ = TransceiverState {}; } void TransceiverBase::stop () noexcept diff --git a/TransceiverBase.hpp b/TransceiverBase.hpp index ea981d44b..7d43cc32e 100644 --- a/TransceiverBase.hpp +++ b/TransceiverBase.hpp @@ -112,8 +112,6 @@ protected: virtual void do_ptt (bool = true) = 0; virtual void do_post_ptt (bool = true) {} - virtual void do_sync (bool force_signal = false, bool no_poll = false) = 0; - virtual bool do_pre_update () {return true;} // sub classes report rig state changes with these methods From fef64fc0e094ef1ccb1c006f655f8146e999b396 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Thu, 30 May 2019 23:36:02 +0100 Subject: [PATCH 07/10] Remove a diagnostic --- logbook/Multiplier.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/logbook/Multiplier.cpp b/logbook/Multiplier.cpp index 64f6cc6f3..6323e4feb 100644 --- a/logbook/Multiplier.cpp +++ b/logbook/Multiplier.cpp @@ -31,7 +31,6 @@ Multiplier::~Multiplier () void Multiplier::reload (CabrilloLog const * log) { m_->entities_worked_ = log->unique_DXCC_entities (m_->countries_); - qDebug () << "Entities worked:" << m_->entities_worked_; } auto Multiplier::entities_worked () const -> worked_set const& From 42173f181616942ae7a6d691b7e9d49fe0fe8e34 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Fri, 31 May 2019 00:13:43 +0100 Subject: [PATCH 08/10] Minor release notes additions --- Release_Notes.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Release_Notes.txt b/Release_Notes.txt index efc7bb42c..60fa0aa20 100644 --- a/Release_Notes.txt +++ b/Release_Notes.txt @@ -37,6 +37,8 @@ OTHER IMPROVEMENTS - Improved S/N calculation for FT4 mode - N1MM Logger+ now uses the standard WSJT-X UDP messages - OK/Cancel buttons on Log QSO window maintain fixed positions + - Enhancements to the Omni-Rig CAT interface + - New setting option to include or exclude WAE entities BUG FIXES - Fix generation of Tx5 message when one callsign is nonstandard @@ -44,6 +46,7 @@ BUG FIXES - Fix a bug that caused mode switch from FT4 to FT8 - Fix a bug that caused FT4 to do WSPR-style band hopping - Fix a bug that caused a Fortran bounds error + - Repaired field editing in the Contest Log window Release: WSJT-X 2.1.0-rc5 From 3456bdb9498678f358486eb178ba41471cbd4a6a Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Fri, 31 May 2019 03:10:46 +0100 Subject: [PATCH 09/10] Update for new FT4 sample .WAV file --- samples/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index 6be6a6df2..271d2d3c4 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -1,5 +1,5 @@ set (SAMPLE_FILES - FT4/190106_000112.wav + FT4/000000_000002.wav FT8/181201_180245.wav ISCAT/ISCAT-A/VK7MO_110401_235515.wav ISCAT/ISCAT-B/K0AWU_100714_115000.wav From f61fc47efb34b0bb138b55c4016b250ce40efa58 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Fri, 31 May 2019 03:11:53 +0100 Subject: [PATCH 10/10] Bump RC number --- Versions.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Versions.cmake b/Versions.cmake index f07339063..45cd79820 100644 --- a/Versions.cmake +++ b/Versions.cmake @@ -2,5 +2,5 @@ set (WSJTX_VERSION_MAJOR 2) set (WSJTX_VERSION_MINOR 1) set (WSJTX_VERSION_PATCH 0) -set (WSJTX_RC 6) # release candidate number, comment out or zero for development versions +set (WSJTX_RC 7) # release candidate number, comment out or zero for development versions set (WSJTX_VERSION_IS_RELEASE 0) # set to 1 for final release build