diff --git a/CMakeLists.txt b/CMakeLists.txt index ab92873b3..b7c9809b1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -281,10 +281,9 @@ set (jt9_CXXSRCS ) set (wsjtx_CXXSRCS - logbook/adif.cpp logbook/countrydat.cpp - logbook/countriesworked.cpp logbook/logbook.cpp + logbook/WorkedBefore.cpp psk_reporter.cpp Modulator.cpp Detector.cpp diff --git a/Configuration.cpp b/Configuration.cpp index 0b43de344..6a26537bb 100644 --- a/Configuration.cpp +++ b/Configuration.cpp @@ -525,6 +525,7 @@ private: DecodeHighlightingModel decode_highlighing_model_; DecodeHighlightingModel next_decode_highlighing_model_; + bool highlight_by_mode_; int LotW_days_since_upload_; TransceiverFactory::ParameterPack rig_params_; @@ -708,6 +709,7 @@ bool Configuration::pwrBandTxMemory () const {return m_->pwrBandTxMemory_;} 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_;} void Configuration::set_calibration (CalibrationParams params) { @@ -903,6 +905,7 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network , station_delete_action_ {tr ("&Delete"), nullptr} , station_insert_action_ {tr ("&Insert ..."), nullptr} , station_dialog_ {new StationDialog {&next_stations_, &bands_, this}} + , highlight_by_mode_ {false} , LotW_days_since_upload_ {0} , last_port_type_ {TransceiverFactory::Capabilities::none} , rig_is_dummy_ {false} @@ -1266,6 +1269,7 @@ void Configuration::impl::initialize_models () next_stations_.station_list (stations_.station_list ()); next_decode_highlighing_model_.items (decode_highlighing_model_.items ()); + ui_->highlight_by_mode_check_box->setChecked (highlight_by_mode_); ui_->LotW_days_since_upload_spin_box->setValue (LotW_days_since_upload_); set_rig_invariants (); @@ -1411,6 +1415,7 @@ void Configuration::impl::read_settings () stations_.station_list (settings_->value ("stations").value ()); decode_highlighing_model_.items (settings_->value ("DecodeHighlighting", QVariant::fromValue (DecodeHighlightingModel::default_items ())).value ()); + highlight_by_mode_ = settings_->value("HighlightByMode", false).toBool (); LotW_days_since_upload_ = settings_->value ("LotWDaysSinceLastUpload", 365).toInt (); lotw_users_.set_age_constraint (LotW_days_since_upload_); @@ -1524,6 +1529,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 ("LotWDaysSinceLastUpload", LotW_days_since_upload_); settings_->setValue ("toRTTY", log_as_RTTY_); settings_->setValue ("dBtoComments", report_in_comments_); @@ -2026,6 +2032,7 @@ void Configuration::impl::accept () decode_highlighing_model_.items (next_decode_highlighing_model_.items ()); Q_EMIT self_->decode_highlighting_changed (decode_highlighing_model_); } + highlight_by_mode_ = ui_->highlight_by_mode_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 13165772a..4cd2ded9f 100644 --- a/Configuration.hpp +++ b/Configuration.hpp @@ -176,6 +176,7 @@ public: bool pwrBandTuneMemory () const; LotWUsers const& lotw_users () const; DecodeHighlightingModel const& decode_highlighting () const; + bool highlight_by_mode () const; struct CalibrationParams { diff --git a/Configuration.ui b/Configuration.ui index 7902529fc..7542159f6 100644 --- a/Configuration.ui +++ b/Configuration.ui @@ -6,8 +6,8 @@ 0 0 - 534 - 546 + 527 + 540 @@ -2235,6 +2235,20 @@ Right click for insert and delete options. + + + + + + <html><head/><body><p>Check to indicate new DXCC entities, grid squares, and callsigns per mode.</p></body></html> + + + Highlight by Mode + + + + + @@ -2313,19 +2327,6 @@ Right click for insert and delete options. - - - - Qt::Vertical - - - - 20 - 40 - - - - @@ -2339,6 +2340,19 @@ Right click for insert and delete options. + + + + Qt::Vertical + + + + 20 + 40 + + + + @@ -2905,6 +2919,7 @@ Right click for insert and delete options. stations_table_view highlighting_list_view reset_highlighting_to_defaults_push_button + highlight_by_mode_check_box LotW_CSV_URL_line_edit LotW_CSV_fetch_push_button LotW_days_since_upload_spin_box @@ -2994,12 +3009,12 @@ Right click for insert and delete options. - - - - + + + + diff --git a/displaytext.cpp b/displaytext.cpp index 80aed95fe..e7c9d1a3a 100644 --- a/displaytext.cpp +++ b/displaytext.cpp @@ -11,10 +11,12 @@ #include #include #include +#include #include "Configuration.hpp" #include "LotWUsers.hpp" #include "DecodeHighlightingModel.hpp" +#include "logbook/logbook.h" #include "qt_helpers.hpp" #include "moc_displaytext.cpp" @@ -95,7 +97,7 @@ namespace { auto const& item = it.previous (); auto const& type = std::find (types.begin (), types.end (), item.type_); - if (type != types.end () && *type == item.type_ && item.enabled_) + if (type != types.end () && item.enabled_) { if (item.background_.style () != Qt::NoBrush) { @@ -117,40 +119,40 @@ void DisplayText::appendText(QString const& text, QColor bg, QColor fg auto cursor = textCursor (); cursor.movePosition (QTextCursor::End); auto block_format = cursor.blockFormat (); + auto format = cursor.blockCharFormat (); + format.setFont (char_font_); + format.clearBackground (); if (bg.isValid ()) { block_format.setBackground (bg); } - else - { - block_format.clearBackground (); - } + format.clearForeground (); if (fg.isValid ()) { - block_format.setForeground (fg); + format.setForeground (fg); } - else + if (call2.size () && m_config && m_config->lotw_users ().user (call2)) { - block_format.clearForeground (); - } - if (0 == cursor.position ()) - { - cursor.setBlockFormat (block_format); - auto char_format = cursor.charFormat (); - char_format.setFont (char_font_); - cursor.setCharFormat (char_format); - } - else - { - cursor.insertBlock (block_format); - auto char_format = cursor.charFormat (); - char_format.clearBackground (); - char_format.clearForeground (); - cursor.setCharFormat (char_format); + QColor bg; + QColor fg; + highlight_types types {DecodeHighlightingModel::Highlight::LotW}; + set_colours (m_config, &bg, &fg, types); + if (bg.isValid ()) block_format.setBackground (bg); + if (fg.isValid ()) format.setForeground (fg); + } + + if (cursor.position ()) + { + cursor.insertBlock (block_format, format); + } + else + { + cursor.setBlockFormat (block_format); + cursor.setBlockCharFormat (format); } - QTextCharFormat format = cursor.charFormat(); int text_index {0}; + auto temp_format = format; if (call1.size ()) { auto call_index = text.indexOf (call1); @@ -159,16 +161,16 @@ void DisplayText::appendText(QString const& text, QColor bg, QColor fg auto pos = highlighted_calls_.find (call1); if (pos != highlighted_calls_.end ()) { - cursor.insertText(text.left (call_index), format); + cursor.insertText(text.left (call_index)); if (pos.value ().first.isValid ()) { - format.setBackground (pos.value ().first); + temp_format.setBackground (pos.value ().first); } if (pos.value ().second.isValid ()) { - format.setForeground (pos.value ().second); + temp_format.setForeground (pos.value ().second); } - cursor.insertText(text.mid (call_index, call1.size ()), format); + cursor.insertText(text.mid (call_index, call1.size ()), temp_format); text_index = call_index + call1.size (); } } @@ -181,31 +183,21 @@ void DisplayText::appendText(QString const& text, QColor bg, QColor fg auto pos = highlighted_calls_.find (call2); if (pos != highlighted_calls_.end ()) { - format.setBackground (bg); - format.setForeground (fg); + temp_format = format; cursor.insertText(text.mid (text_index, call_index - text_index), format); if (pos.value ().second.isValid ()) { - format.setBackground (pos.value ().first); + temp_format.setBackground (pos.value ().first); } if (pos.value ().second.isValid ()) { - format.setForeground (pos.value ().second); + temp_format.setForeground (pos.value ().second); } - cursor.insertText(text.mid (call_index, call2.size ()), format); + cursor.insertText(text.mid (call_index, call2.size ()), temp_format); text_index = call_index + call2.size (); } } } - if (call2.size () && m_config && m_config->lotw_users ().user (call2)) - { - QColor bg; - QColor fg; - highlight_types types {DecodeHighlightingModel::Highlight::LotW}; - set_colours (m_config, &bg, &fg, types); - if (bg.isValid ()) format.setBackground (bg); - if (fg.isValid ()) format.setForeground (fg); - } cursor.insertText(text.mid (text_index), format); // position so viewport scrolled to left @@ -215,8 +207,9 @@ void DisplayText::appendText(QString const& text, QColor bg, QColor fg document ()->setMaximumBlockCount (document ()->maximumBlockCount ()); } -QString DisplayText::appendWorkedB4(QString message, QString const& callsign, QString grid, - QColor * bg, QColor * fg, LogBook const& logBook, QString currentBand) +QString DisplayText::appendWorkedB4 (QString message, QString const& callsign, QString const& grid, + QColor * bg, QColor * fg, LogBook const& logBook, + QString const& currentBand, QString const& currentMode) { // allow for seconds int padding {message.indexOf (" ") > 4 ? 2 : 0}; @@ -238,8 +231,8 @@ QString DisplayText::appendWorkedB4(QString message, QString const& callsign, QS if(call.length()<3) return message; if(!call.contains(QRegExp("[0-9]|[A-Z]"))) return message; - logBook.match(/*in*/call,grid,/*out*/countryName,callWorkedBefore,countryWorkedBefore,gridB4); - logBook.match(/*in*/call,grid,/*out*/countryName,callB4onBand,countryB4onBand,gridB4onBand, + logBook.match(/*in*/call,currentMode,grid,/*out*/countryName,callWorkedBefore,countryWorkedBefore,gridB4); + logBook.match(/*in*/call,currentMode,grid,/*out*/countryName,callB4onBand,countryB4onBand,gridB4onBand, /*in*/ currentBand); if(grid=="") { gridB4=true; @@ -247,7 +240,7 @@ QString DisplayText::appendWorkedB4(QString message, QString const& callsign, QS } message = message.trimmed (); - QString appendage{""}; + QString appendage; highlight_types types; // no shortcuts here as some types may be disabled @@ -313,8 +306,9 @@ QString DisplayText::appendWorkedB4(QString message, QString const& callsign, QS } void DisplayText::displayDecodedText(DecodedText const& decodedText, QString const& myCall, + QString const& mode, bool displayDXCCEntity, LogBook const& logBook, - QString currentBand, bool ppfx, bool bCQonly) + QString const& currentBand, bool ppfx, bool bCQonly) { m_bPrincipalPrefix=ppfx; QColor bg; @@ -348,9 +342,17 @@ void DisplayText::displayDecodedText(DecodedText const& decodedText, QString con if(!dxGrid.contains(grid_regexp)) dxGrid=""; message = message.left (message.indexOf (QChar::Nbsp)); // strip appended info if (displayDXCCEntity && CQcall) - // if enabled add the DXCC entity and B4 status to the end of the - // preformated text line t1 - message = appendWorkedB4 (message, decodedText.CQersCall(), dxGrid, &bg, &fg, logBook, currentBand); + { + // if enabled add the DXCC entity and B4 status to the end of the + // preformated text line t1 + auto currentMode = mode; + if ("JT9+JT65" == mode) + { + currentMode = decodedText.isJT65 () ? "JT65" : "JT9"; + } + message = appendWorkedB4 (message, decodedText.CQersCall(), dxGrid, &bg, &fg + , logBook, currentBand, currentMode); + } appendText (message.trimmed (), bg, fg, decodedText.call (), dxCall); } diff --git a/displaytext.h b/displaytext.h index ca9da084f..2ccd8fe4c 100644 --- a/displaytext.h +++ b/displaytext.h @@ -8,11 +8,11 @@ #include #include -#include "logbook/logbook.h" #include "decodedtext.h" class QAction; class Configuration; +class LogBook; class DisplayText : public QTextEdit @@ -23,9 +23,9 @@ public: void set_configuration (Configuration const * configuration) {m_config = configuration;} void setContentFont (QFont const&); void insertLineSpacer(QString const&); - void displayDecodedText(DecodedText const& decodedText, QString const& myCall, - bool displayDXCCEntity, LogBook const& logBook, - QString currentBand="", bool ppfx=false, bool bCQonly=false); + void displayDecodedText(DecodedText const& decodedText, QString const& myCall, QString const& mode, + bool displayDXCCEntity, LogBook const& logBook, + QString const& currentBand=QString {}, bool ppfx=false, bool bCQonly=false); void displayTransmittedText(QString text, QString modeTx, qint32 txFreq, bool bFastMode); void displayQSY(QString text); void displayFoxToBeCalled(QString t, QColor bg = QColor {}, QColor fg = QColor {}); @@ -45,8 +45,9 @@ private: Configuration const * m_config; bool m_bPrincipalPrefix; QString appendWorkedB4(QString message, QString const& callsign - , QString grid, QColor * bg, QColor * fg - , LogBook const& logBook, QString currentBand); + , QString const& grid, QColor * bg, QColor * fg + , LogBook const& logBook, QString const& currentBand + , QString const& currentMode); QFont char_font_; QAction * erase_action_; QHash> highlighted_calls_; diff --git a/logbook/WorkedBefore.cpp b/logbook/WorkedBefore.cpp new file mode 100644 index 000000000..d1eeae71e --- /dev/null +++ b/logbook/WorkedBefore.cpp @@ -0,0 +1,377 @@ +#include "WorkedBefore.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include "countrydat.h" + +#include "pimpl_impl.hpp" + +// worked before set element +struct worked_entry +{ + explicit worked_entry (std::string const& call + , std::string const& grid + , std::string const& band + , std::string const& mode + , std::string const& country) + : call {call} + , grid {grid} + , band {band} + , mode {mode} + , country {country} + { + } + + std::string call; + std::string grid; + std::string band; + std::string mode; + std::string country; +}; + +// tags +struct call_mode_band {}; +struct call_band {}; +struct grid_mode_band {}; +struct grid_band {}; +struct entity_mode_band {}; +struct entity_band {}; + +// set with multiple ordered unique indexes that allow for efficient +// determination of various categories of worked before status +typedef boost::multi_index::multi_index_container< + worked_entry, + boost::multi_index::indexed_by< + // call+mode+band + boost::multi_index::ordered_unique< + boost::multi_index::tag, + boost::multi_index::composite_key< + worked_entry, + boost::multi_index::member, + boost::multi_index::member, + boost::multi_index::member + > + >, + // call+band + boost::multi_index::ordered_unique< + boost::multi_index::tag, + boost::multi_index::composite_key< + worked_entry, + boost::multi_index::member, + boost::multi_index::member + > + >, + // grid+mode+band + boost::multi_index::ordered_unique< + boost::multi_index::tag, + boost::multi_index::composite_key< + worked_entry, + boost::multi_index::member, + boost::multi_index::member, + boost::multi_index::member + > + >, + // grid+band + boost::multi_index::ordered_unique< + boost::multi_index::tag, + boost::multi_index::composite_key< + worked_entry, + boost::multi_index::member, + boost::multi_index::member + > + >, + // country+mode+band + boost::multi_index::ordered_unique< + boost::multi_index::tag, + boost::multi_index::composite_key< + worked_entry, + boost::multi_index::member, + boost::multi_index::member, + boost::multi_index::member + > + >, + // country+band + boost::multi_index::ordered_unique< + boost::multi_index::tag, + boost::multi_index::composite_key< + worked_entry, + boost::multi_index::member, + boost::multi_index::member + > + > + > + > worked_type; + +namespace +{ + auto const logFileName = "wsjtx_log.adi"; + + std::string extractField (QString const& record, QString const& fieldName) + { + int fieldNameIndex = record.indexOf ('<' + fieldName + ':', 0, Qt::CaseInsensitive); + if (fieldNameIndex >=0) + { + int closingBracketIndex = record.indexOf('>',fieldNameIndex); + int fieldLengthIndex = record.indexOf(':',fieldNameIndex); // find the size delimiter + int dataTypeIndex = -1; + if (fieldLengthIndex >= 0) + { + dataTypeIndex = record.indexOf(':',fieldLengthIndex+1); // check for a second : indicating there is a data type + if (dataTypeIndex > closingBracketIndex) + dataTypeIndex = -1; // second : was found but it was beyond the closing > + } + + if ((closingBracketIndex > fieldNameIndex) && (fieldLengthIndex > fieldNameIndex) && (fieldLengthIndex< closingBracketIndex)) + { + int fieldLengthCharCount = closingBracketIndex - fieldLengthIndex -1; + if (dataTypeIndex >= 0) + fieldLengthCharCount -= 2; // data type indicator is always a colon followed by a single character + QString fieldLengthString = record.mid(fieldLengthIndex+1,fieldLengthCharCount); + int fieldLength = fieldLengthString.toInt(); + if (fieldLength > 0) + { + QString field = record.mid(closingBracketIndex+1,fieldLength); + return field.toStdString (); + } + } + } + return ""; + } +} + +class WorkedBefore::impl final +{ +public: + impl () + : path_ {QDir {QStandardPaths::writableLocation (QStandardPaths::DataLocation)}.absoluteFilePath (logFileName)} + { + } + + QString path_; + CountryDat countries_; + worked_type worked_; +}; + +WorkedBefore::WorkedBefore () +{ + QFile inputFile {m_->path_}; + if (inputFile.open (QFile::ReadOnly)) + { + QTextStream in(&inputFile); + QString buffer; + bool pre_read {false}; + int end_position {-1}; + + // skip optional header record + do + { + buffer += in.readLine () + '\n'; + if (buffer.startsWith (QChar {'<'})) // denotes no header + { + pre_read = true; + } + else + { + end_position = buffer.indexOf ("", 0, Qt::CaseInsensitive); + } + } + while (!in.atEnd () && !pre_read && end_position < 0); + if (!pre_read) // found header + { + buffer.remove (0, end_position + 5); + } + while (buffer.size () || !in.atEnd ()) + { + do + { + end_position = buffer.indexOf ("", 0, Qt::CaseInsensitive); + if (!in.atEnd () && end_position < 0) + { + buffer += in.readLine () + '\n'; + } + } + while (!in.atEnd () && end_position < 0); + int record_length {end_position >= 0 ? end_position + 5 : -1}; + auto record = buffer.left (record_length).trimmed (); + auto next_record = buffer.indexOf (QChar {'<'}, record_length); + buffer.remove (0, next_record >=0 ? next_record : buffer.size ()); + record = record.mid (record.indexOf (QChar {'<'})); + auto call = extractField (record, "CALL"); + if (call.size ()) + { + m_->worked_.emplace (call + , extractField (record, "GRIDSQUARE").substr (0, 4) // not interested in 6-digit grids + , extractField (record, "BAND") + , extractField (record, "MODE") + , m_->countries_.find (QString::fromStdString (call)).toStdString ()); + } + } + } +} + +WorkedBefore::~WorkedBefore () +{ +} + +QString const& WorkedBefore::path () const +{ + return m_->path_; +} + +CountryDat const& WorkedBefore::countries () const +{ + return m_->countries_; +} + +bool WorkedBefore::add (QString const& call + , QString const& grid + , QString const& band + , QString const& mode + , QByteArray const& ADIF_record) +{ + if (call.size ()) + { + QFile file {m_->path_}; + if (!file.open(QIODevice::Text | QIODevice::Append)) + { + return false; + } + else + { + QTextStream out {&file}; + if (!file.size ()) + { + out << "WSJT-X ADIF Export" << endl; // new file + } + out << ADIF_record << " " << endl; + } + m_->worked_.emplace (call.toStdString () + , grid.toStdString () + , band.toStdString () + , mode.toStdString () + , m_->countries_.find (call).toStdString ()); + } + return true; +} + +bool WorkedBefore::country_worked (QString const& country, QString const& mode, QString const& band) const +{ + if (mode.size ()) + { + if (band.size ()) + { + return + country.size () + && m_->worked_.get ().end () + != m_->worked_.get ().find (std::make_tuple (country.toStdString () + , mode.toStdString () + , band.toStdString ())); + } + else + { + // partial key lookup + return + country.size () + && m_->worked_.get ().end () + != m_->worked_.get ().find (std::make_tuple (country.toStdString () + , mode.toStdString ())); + } + } + else + { + if (band.size ()) + { + return + country.size () + && m_->worked_.get ().end () + != m_->worked_.get ().find (std::make_tuple (country.toStdString () + , band.toStdString ())); + } + else + { + // partial key lookup + return + country.size () + && m_->worked_.get ().end () + != m_->worked_.get ().find (std::make_tuple (country.toStdString ())); + } + } +} + +bool WorkedBefore::grid_worked (QString const& grid, QString const& mode, QString const& band) const +{ + if (mode.size ()) + { + if (band.size ()) + { + return m_->worked_.get ().end () + != m_->worked_.get ().find (std::make_tuple (grid.toStdString () + , mode.toStdString () + , band.toStdString ())); + } + else + { + // partial key lookup + return m_->worked_.get ().end () + != m_->worked_.get ().find (std::make_tuple (grid.toStdString () + , mode.toStdString ())); + } + } + else + { + if (band.size ()) + { + return m_->worked_.get ().end () + != m_->worked_.get ().find (std::make_tuple (grid.toStdString () + , band.toStdString ())); + } + else + { + // partial key lookup + return m_->worked_.get ().end () + != m_->worked_.get ().find (std::make_tuple (grid.toStdString ())); + } + } +} + +bool WorkedBefore::call_worked (QString const& call, QString const& mode, QString const& band) const +{ + if (mode.size ()) + { + if (band.size ()) + { + return m_->worked_.get ().end () + != m_->worked_.get ().find (std::make_tuple (call.toStdString () + , mode.toStdString () + , band.toStdString ())); + } + else + { + // partial key lookup + return m_->worked_.get ().end () + != m_->worked_.get ().find (std::make_tuple (call.toStdString () + , mode.toStdString ())); + } + } + else + { + if (band.size ()) + { + return m_->worked_.get ().end () + != m_->worked_.get ().find (std::make_tuple (call.toStdString () + , band.toStdString ())); + } + else + { + // partial key lookup + return m_->worked_.get ().end () + != m_->worked_.get ().find (std::make_tuple (call.toStdString ())); + } + } +} diff --git a/logbook/WorkedBefore.hpp b/logbook/WorkedBefore.hpp new file mode 100644 index 000000000..68e32c304 --- /dev/null +++ b/logbook/WorkedBefore.hpp @@ -0,0 +1,34 @@ +#ifndef WORKWED_BEFORE_HPP_ +#define WORKWED_BEFORE_HPP_ + +#include +#include "pimpl_h.hpp" + +class CountryDat; +class QString; +class QByteArray; + +class WorkedBefore final + : private boost::noncopyable +{ +public: + explicit WorkedBefore (); + ~WorkedBefore (); + + QString const& path () const; + CountryDat const& countries () const; + bool add (QString const& call + , QString const& grid + , QString const& band + , QString const& mode + , QByteArray const& ADIF_record); + 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; + +private: + class impl; + pimpl m_; +}; + +#endif diff --git a/logbook/adif.cpp b/logbook/adif.cpp deleted file mode 100644 index 9289c9c10..000000000 --- a/logbook/adif.cpp +++ /dev/null @@ -1,236 +0,0 @@ -#include "adif.h" - -#include -#include -#include -#include - -/* -W1XT20m14.076DM33JT65-21-142011042204171204243541st JT65A QSO. Him: mag loop 20WVK3ACFqf22lb -IK1SOW20m14.076JN35JT65-19-11201104220525010533593VK3ACFqf22lb -W4ABC> ... -*/ - -void ADIF::init(QString const& filename) -{ - _filename = filename; - _data.clear(); - _data2.clear(); -} - - -QString ADIF::extractField(QString const& record, QString const& fieldName) const -{ - int fieldNameIndex = record.indexOf ('<' + fieldName + ':', 0, Qt::CaseInsensitive); - if (fieldNameIndex >=0) - { - int closingBracketIndex = record.indexOf('>',fieldNameIndex); - int fieldLengthIndex = record.indexOf(':',fieldNameIndex); // find the size delimiter - int dataTypeIndex = -1; - if (fieldLengthIndex >= 0) - { - dataTypeIndex = record.indexOf(':',fieldLengthIndex+1); // check for a second : indicating there is a data type - if (dataTypeIndex > closingBracketIndex) - dataTypeIndex = -1; // second : was found but it was beyond the closing > - } - - if ((closingBracketIndex > fieldNameIndex) && (fieldLengthIndex > fieldNameIndex) && (fieldLengthIndex< closingBracketIndex)) - { - int fieldLengthCharCount = closingBracketIndex - fieldLengthIndex -1; - if (dataTypeIndex >= 0) - fieldLengthCharCount -= 2; // data type indicator is always a colon followed by a single character - QString fieldLengthString = record.mid(fieldLengthIndex+1,fieldLengthCharCount); - int fieldLength = fieldLengthString.toInt(); - if (fieldLength > 0) - { - QString field = record.mid(closingBracketIndex+1,fieldLength); - return field; - } - } - } - return ""; -} - - - -void ADIF::load() -{ - _data.clear(); - _data2.clear(); - QFile inputFile(_filename); - if (inputFile.open(QIODevice::ReadOnly)) - { - QTextStream in(&inputFile); - QString buffer; - bool pre_read {false}; - int end_position {-1}; - - // skip optional header record - do - { - buffer += in.readLine () + '\n'; - if (buffer.startsWith (QChar {'<'})) // denotes no header - { - pre_read = true; - } - else - { - end_position = buffer.indexOf ("", 0, Qt::CaseInsensitive); - } - } - while (!in.atEnd () && !pre_read && end_position < 0); - if (!pre_read) // found header - { - buffer.remove (0, end_position + 5); - } - while (buffer.size () || !in.atEnd ()) - { - do - { - end_position = buffer.indexOf ("", 0, Qt::CaseInsensitive); - if (!in.atEnd () && end_position < 0) - { - buffer += in.readLine () + '\n'; - } - } - while (!in.atEnd () && end_position < 0); - int record_length {end_position >= 0 ? end_position + 5 : -1}; - auto record = buffer.left (record_length).trimmed (); - auto next_record = buffer.indexOf (QChar {'<'}, record_length); - buffer.remove (0, next_record >=0 ? next_record : buffer.size ()); - record = record.mid (record.indexOf (QChar {'<'})); - add (extractField (record, "CALL") - , extractField (record, "GRIDSQUARE") - , extractField (record, "BAND") - , extractField (record, "MODE") - , extractField (record, "QSO_DATE")); - } - inputFile.close (); - } -} - - -void ADIF::add(QString const& call, QString const& grid, QString const& band, - QString const& mode, QString const& date) -{ - QSO q; - q.call = call; - q.grid = grid.left(4); //We only want to test matches to 4-character grids. - q.band = band; - q.mode = mode; - q.date = date; - if(q.call.size ()) { - _data.insert(q.call,q); - _data2.insert(q.grid,q); -// qDebug() << "In the log:" << call << grid << band << mode << date; - } -} - -// return true if in the log same band and mode (where JT65 == JT9 == FT8) -bool ADIF::match(QString const& call, QString const& band, QString const& mode) const -{ - QList qsos; - QRegularExpression grid_regexp {"\\A(?![Rr]{2}73)[A-Ra-r]{2}[0-9]{2}([A-Xa-x]{2}){0,1}\\z"}; - if(!call.contains(grid_regexp)) { - qsos = _data.values(call); - } else { - qsos = _data2.values(call); - } -// qDebug() << "AA" << call << qsos.size(); - if (qsos.size()>0) { - QSO q; - foreach(q,qsos) { - if((band.compare(q.band,Qt::CaseInsensitive) == 0) || (band=="") || (q.band=="")) { - if(( - ((mode.compare("JT65",Qt::CaseInsensitive)==0) || - (mode.compare("JT9",Qt::CaseInsensitive)==0) || - (mode.compare("FT8",Qt::CaseInsensitive)==0)) - && - ((q.mode.compare("JT65",Qt::CaseInsensitive)==0) || - (q.mode.compare("JT9",Qt::CaseInsensitive)==0) || - (q.mode.compare("FT8",Qt::CaseInsensitive)==0)) - ) - || (mode.compare(q.mode,Qt::CaseInsensitive)==0) - || (mode=="") - || (q.mode=="") - ) - return true; - } - } - } - return false; -} - -QList ADIF::getCallList() const -{ - QList p; - QMultiHash::const_iterator i = _data.constBegin(); - while (i != _data.constEnd()) - { - p << i.key(); - ++i; - } - return p; -} - - - -int ADIF::getCount() const -{ - return _data.size(); -} - -QByteArray ADIF::QSOToADIF(QString const& hisCall, QString const& hisGrid, QString const& mode, - QString const& rptSent, QString const& rptRcvd, QDateTime const& dateTimeOn, - QDateTime const& dateTimeOff, QString const& band, QString const& comments, - QString const& name, QString const& strDialFreq, QString const& m_myCall, - QString const& m_myGrid, QString const& m_txPower, QString const& operator_call, - QString const& xSent, QString const& xRcvd) -{ - QString t; - t = "" + hisCall; - t += " " + hisGrid; - t += " " + mode; - t += " " + rptSent; - t += " " + rptRcvd; - t += " " + dateTimeOn.date().toString("yyyyMMdd"); - t += " " + dateTimeOn.time().toString("hhmmss"); - t += " " + dateTimeOff.date().toString("yyyyMMdd"); - t += " " + dateTimeOff.time().toString("hhmmss"); - t += " " + band; - t += " " + strDialFreq; - t += " " + m_myCall; - t += " " + m_myGrid; - if(m_txPower!="") t += " " + m_txPower; - if(comments!="") t += " " + comments; - if(name!="") t += " " + name; - if(operator_call!="") t+=" " + operator_call; - if(xRcvd!="") { - QString t1=""; - if(xRcvd.split(" ").size()==2) t1=xRcvd.split(" ").at(1); - if(t1.toInt()>0) { - t += " " + t1; - } else { - t += " " + t1; - } - } - return t.toLatin1(); -} - -// open ADIF file and append the QSO details. Return true on success -bool ADIF::addQSOToFile(QByteArray const& ADIF_record) -{ - QFile f2(_filename); - if (!f2.open(QIODevice::Text | QIODevice::Append)) - return false; - else - { - QTextStream out(&f2); - if (f2.size()==0) - out << "WSJT-X ADIF Export" << endl; // new file - - out << ADIF_record << " " << endl; - f2.close(); - } - return true; -} diff --git a/logbook/adif.h b/logbook/adif.h deleted file mode 100644 index d203227f1..000000000 --- a/logbook/adif.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Reads an ADIF log file into memory - * Searches log for call, band and mode - * VK3ACF July 2013 - */ - - -#ifndef __ADIF_H -#define __ADIF_H - -#if defined (QT5) -#include -#include -#include -#include -#else -#include -#endif - -class QDateTime; - -class ADIF -{ - public: - void init(QString const& filename); - void load(); - void add(QString const& call, const QString &grid, QString const& band, QString const& mode, - QString const& date); - bool match(QString const& call, QString const& band, QString const& mode) const; - QList getCallList() const; - int getCount() const; - - // open ADIF file and append the QSO details. Return true on success - bool addQSOToFile(QByteArray const& ADIF_record); - - QByteArray QSOToADIF(QString const& hisCall, QString const& hisGrid, QString const& mode, - QString const& rptSent, QString const& rptRcvd, QDateTime const& dateTimeOn, - QDateTime const& dateTimeOff, QString const& band, QString const& comments, - QString const& name, QString const& strDialFreq, QString const& m_myCall, - QString const& m_myGrid, QString const& m_txPower, QString const& operator_call, - QString const& xSent, QString const& xRcvd); - -private: - struct QSO - { - QString call,grid,band,mode,date; - }; - - QMultiHash _data; - QMultiHash _data2; - QString _filename; - QString extractField(QString const& line, QString const& fieldName) const; -}; - -#endif - diff --git a/logbook/countriesworked.cpp b/logbook/countriesworked.cpp index 6a991f8a4..bcd73af7b 100644 --- a/logbook/countriesworked.cpp +++ b/logbook/countriesworked.cpp @@ -1,42 +1,24 @@ #include "countriesworked.h" -void CountriesWorked::init(const QStringList countryNames) -{ - _data.clear(); - foreach(QString name,countryNames) - _data.insert(name,false); -} +#include -void CountriesWorked::setAsWorked(const QString countryName) -{ - if (_data.contains(countryName)) - _data.insert(countryName,true); -} - -bool CountriesWorked::getHasWorked(const QString countryName) const -{ - if (_data.contains(countryName)) - return _data.value(countryName); +#include "pimpl_impl.hpp" - return false; -} - -int CountriesWorked::getWorkedCount() const +class CountriesWorkedimpl final { - int count = 0; - foreach (bool value,_data) - if (value) - count += 1; - return count; -} - -int CountriesWorked::getSize() const +public: + // element is country-name+band where the '+' is actual a character + // which allows an efficient lower_bound() search for country-name+ + // to check for ATNOs + std::set worked_; +}; + +void CountriesWorked::add (QString const& country, QString const& band) { - return _data.count(); + m_->worked_.insert (country + '+' + band); +} + +bool CountriesWorked::contains (QString const& country, QString const& band) const +{ + return m_->worked_.end () != m_->worked_.lower_bound (country + '+' + band); } - - - - - - diff --git a/logbook/countriesworked.h b/logbook/countriesworked.h index fedfa7524..ac8aaea6d 100644 --- a/logbook/countriesworked.h +++ b/logbook/countriesworked.h @@ -1,29 +1,24 @@ -/* - * maintains a list of country names that have been worked - * VK3ACF July 2013 - */ +#ifndef COUNTRIES_WORKDED_H_ +#define COUNTRIES_WORKDED_H_ -#ifndef __COUNTRIESWORKDED_H -#define __COUNTRIESWORKDED_H - -#include #include -#include -#include +#include "pimpl_h.hpp" -class CountriesWorked +class QStringList; + +class CountriesWorked final { public: - void init(const QStringList countryNames); - void setAsWorked(const QString countryName); - bool getHasWorked(const QString countryName) const; - int getWorkedCount() const; - int getSize() const; + explicit CountriesWorked (QStringList const& countryNames); + ~CountriesWorked (); + + void add (QString const& country, QString const& band); + bool contains (QString const& country, QString const& band = QString {}) const; private: - QHash _data; + class impl; + pimpl m_; }; #endif - diff --git a/logbook/countrydat.cpp b/logbook/countrydat.cpp index d1997a977..847e65a41 100644 --- a/logbook/countrydat.cpp +++ b/logbook/countrydat.cpp @@ -16,18 +16,61 @@ #include "countrydat.h" +#include +#include #include #include #include #include "Radio.hpp" -void CountryDat::init(const QString filename) +namespace { - _filename = filename; - _data.clear(); + auto countryFileName = "cty.dat"; } -QString CountryDat::_extractName(const QString line) const +CountryDat::CountryDat () +{ + QDir dataPath {QStandardPaths::writableLocation (QStandardPaths::DataLocation)}; + QFile file {dataPath.exists (countryFileName) + ? dataPath.absoluteFilePath (countryFileName) // user override + : QString {":/"} + countryFileName}; // or original in + // the resources FS + if (file.open (QFile::ReadOnly)) + { + QTextStream in {&file}; + while (!in.atEnd ()) + { + QString line1 = in.readLine (); + if (!in.atEnd ()) + { + QString line2 = in.readLine (); + auto name = extractName (line1); + if (name.length () > 0) + { + auto const& continent = line1.mid (36,2); + auto principalPrefix = line1.mid (69,4); + principalPrefix = principalPrefix.mid (0, principalPrefix.indexOf (":")); + name += "; " + principalPrefix + "; " + continent; + bool more {true}; + QStringList prefixes; + while (more) + { + QStringList p = extractPrefix (line2, more); + prefixes += p; + if (more) line2 = in.readLine (); + } + + Q_FOREACH (auto const& p, prefixes) + { + if (p.length() > 0) data_.insert (p, name); + } + } + } + } + } +} + +QString CountryDat::extractName (QString const& line) const { int s1 = line.indexOf(':'); if (s1>=0) @@ -38,7 +81,7 @@ QString CountryDat::_extractName(const QString line) const return ""; } -void CountryDat::_removeBrackets(QString &line, const QString a, const QString b) const +void CountryDat::removeBrackets(QString& line, QString const& a, QString const& b) const { int s1 = line.indexOf(a); while (s1 >= 0) @@ -49,15 +92,15 @@ void CountryDat::_removeBrackets(QString &line, const QString a, const QString b } } -QStringList CountryDat::_extractPrefix(QString &line, bool &more) const +QStringList CountryDat::extractPrefix(QString& line, bool& more) const { line = line.remove(" \n"); line = line.replace(" ",""); - _removeBrackets(line,"(",")"); - _removeBrackets(line,"[","]"); - _removeBrackets(line,"<",">"); - _removeBrackets(line,"~","~"); + removeBrackets(line,"(",")"); + removeBrackets(line,"[","]"); + removeBrackets(line,"<",">"); + removeBrackets(line,"~","~"); int s1 = line.indexOf(';'); more = true; @@ -72,73 +115,24 @@ QStringList CountryDat::_extractPrefix(QString &line, bool &more) const return r; } - -void CountryDat::load() -{ - _data.clear(); - _countryNames.clear(); //used by countriesWorked - - QFile inputFile(_filename); - if (inputFile.open(QIODevice::ReadOnly)) - { - QTextStream in(&inputFile); - while ( !in.atEnd() ) - { - QString line1 = in.readLine(); - if ( !in.atEnd() ) - { - QString line2 = in.readLine(); - - QString name = _extractName(line1); - if (name.length()>0) - { - QString continent=line1.mid(36,2); - QString principalPrefix=line1.mid(69,4); - int i1=principalPrefix.indexOf(":"); - if(i1>0) principalPrefix=principalPrefix.mid(0,i1); - name += "; " + principalPrefix + "; " + continent; - _countryNames << name; - bool more = true; - QStringList prefixs; - while (more) - { - QStringList p = _extractPrefix(line2,more); - prefixs += p; - if (more) - line2 = in.readLine(); - } - - QString p; - foreach(p,prefixs) - { - if (p.length() > 0) - _data.insert(p,name); - } - } - } - } - inputFile.close(); - } -} - // return country name else "" -QString CountryDat::find(QString call) const +QString CountryDat::find (QString const& call) const { - call = call.toUpper (); + auto const& search_string = call.toUpper (); // check for exact match first - if (_data.contains ("=" + call)) + if (data_.contains ("=" + search_string)) { - return fixup (_data.value ("=" + call), call); + return fixup (data_.value ("=" + search_string), search_string); } - auto prefix = Radio::effective_prefix (call); + auto prefix = Radio::effective_prefix (search_string); auto match_candidate = prefix; while (match_candidate.size () >= 1) { - if (_data.contains (match_candidate)) + if (data_.contains (match_candidate)) { - return fixup (_data.value (match_candidate), prefix); + return fixup (data_.value (match_candidate), prefix); } match_candidate = match_candidate.left (match_candidate.size () - 1); } diff --git a/logbook/countrydat.h b/logbook/countrydat.h index d04308924..790bb2328 100644 --- a/logbook/countrydat.h +++ b/logbook/countrydat.h @@ -4,33 +4,27 @@ * VK3ACF July 2013 */ +#ifndef COUNTRY_DAT_H_ +#define COUNTRY_DAT_H_ -#ifndef __COUNTRYDAT_H -#define __COUNTRYDAT_H - - +#include #include -#include #include - -class CountryDat +class CountryDat final + : private boost::noncopyable { -public: - void init(const QString filename); - void load(); - QString find(QString prefix) const; // return country name or "" - QStringList getCountryNames() const { return _countryNames; }; + public: + CountryDat (); + QString find (QString const& call) const; // return country name or "" private: - QString _extractName(const QString line) const; - void _removeBrackets(QString &line, const QString a, const QString b) const; - QStringList _extractPrefix(QString &line, bool &more) const; + QString extractName (QString const& line) const; + void removeBrackets (QString& line, QString const& a, QString const& b) const; + QStringList extractPrefix (QString& line, bool& more) const; QString fixup (QString country, QString const& call) const; - QString _filename; - QStringList _countryNames; - QHash _data; + QHash data_; }; #endif diff --git a/logbook/logbook.cpp b/logbook/logbook.cpp index 7ddc867fb..aa063e42d 100644 --- a/logbook/logbook.cpp +++ b/logbook/logbook.cpp @@ -1,105 +1,74 @@ #include "logbook.h" + +#include #include -#include -#include -#include +#include "countrydat.h" +#include "Configuration.hpp" -namespace +LogBook::LogBook (Configuration const * configuration) + : config_ {configuration} { - auto logFileName = "wsjtx_log.adi"; - auto countryFileName = "cty.dat"; } -void LogBook::init() +void LogBook::match (QString const& call, QString const& mode, QString const& grid, + QString &countryName, + bool &callWorkedBefore, + bool &countryWorkedBefore, + bool &gridWorkedBefore, + QString const& band) const { - QDir dataPath {QStandardPaths::writableLocation (QStandardPaths::DataLocation)}; - QString countryDataFilename; - if (dataPath.exists (countryFileName)) + if (call.length() > 0) { - // User override - countryDataFilename = dataPath.absoluteFilePath (countryFileName); - } - else - { - countryDataFilename = QString {":/"} + countryFileName; - } - - _countries.init(countryDataFilename); - _countries.load(); - - _worked.init(_countries.getCountryNames()); - - _log.init(dataPath.absoluteFilePath (logFileName)); - _log.load(); - - _setAlreadyWorkedFromLog(); - - /* - int QSOcount = _log.getCount(); - int count = _worked.getWorkedCount(); - qDebug() << QSOcount << "QSOs and" << count << "countries worked in file" << logFilename; - */ - - // QString call = "ok1ct"; - // QString countryName; - // bool callWorkedBefore,countryWorkedBefore; - // match(/*in*/call,grid, /*out*/ countryName,callWorkedBefore,countryWorkedBefore); - // qDebug() << countryName; - -} - - -void LogBook::_setAlreadyWorkedFromLog() -{ - QList calls = _log.getCallList(); - QString c; - foreach(c,calls) - { - QString countryName = _countries.find(c); - if (countryName.length() > 0) - { - _worked.setAsWorked(countryName); - //qDebug() << countryName << " worked " << c; - } + auto const& mode_to_check = (config_ && !config_->highlight_by_mode ()) ? QString {} : mode; + callWorkedBefore = worked_before_.call_worked (call, mode_to_check, band); + gridWorkedBefore = worked_before_.grid_worked(grid, mode_to_check, band); + countryName = worked_before_.countries ().find (call); + countryWorkedBefore = worked_before_.country_worked (countryName, mode_to_check, band); } } -void LogBook::match(/*in*/const QString call,QString grid, - /*out*/ QString &countryName, - bool &callWorkedBefore, - bool &countryWorkedBefore, - bool &gridWorkedBefore, - QString currentBand) const +bool LogBook::add (QString const& call + , QString const& grid + , QString const& band + , QString const& mode + , QByteArray const& ADIF_record) { -// if(currentBand=="") qDebug() << "aa" << grid; -// if(currentBand!="") qDebug() << "bb" << grid << currentBand; + return worked_before_.add (call, grid, band, mode, ADIF_record); +} - if (call.length() > 0) { - QString currentMode = "JT9"; // JT65 == JT9 == FT8 in ADIF::match() -// QString currentBand = ""; // match any band - callWorkedBefore = _log.match(call,currentBand,currentMode); - gridWorkedBefore = _log.match(grid,currentBand,currentMode); - countryName = _countries.find(call); - - if (countryName.length() > 0) { // country was found - countryWorkedBefore = _worked.getHasWorked(countryName); +QByteArray LogBook::QSOToADIF (QString const& hisCall, QString const& hisGrid, QString const& mode, + QString const& rptSent, QString const& rptRcvd, QDateTime const& dateTimeOn, + QDateTime const& dateTimeOff, QString const& band, QString const& comments, + QString const& name, QString const& strDialFreq, QString const& myCall, + QString const& myGrid, QString const& txPower, QString const& operator_call, + QString const& xSent, QString const& xRcvd) +{ + QString t; + t = "" + hisCall; + t += " " + hisGrid; + t += " " + mode; + t += " " + rptSent; + t += " " + rptRcvd; + t += " " + dateTimeOn.date().toString("yyyyMMdd"); + t += " " + dateTimeOn.time().toString("hhmmss"); + t += " " + dateTimeOff.date().toString("yyyyMMdd"); + t += " " + dateTimeOff.time().toString("hhmmss"); + t += " " + band; + t += " " + strDialFreq; + t += " " + myCall; + t += " " + myGrid; + if(txPower!="") t += " " + txPower; + if(comments!="") t += " " + comments; + if(name!="") t += " " + name; + if(operator_call!="") t+=" " + operator_call; + if(xRcvd!="") { + QString t1=""; + if(xRcvd.split(" ").size()==2) t1=xRcvd.split(" ").at(1); + if(t1.toInt()>0) { + t += " " + t1; } else { - countryName = "where?"; //error: prefix not found - countryWorkedBefore = false; + t += " " + t1; } -// qDebug() << "Logbook:" << call << currentBand << callWorkedBefore << countryName << countryWorkedBefore; } + return t.toLatin1(); } - -void LogBook::addAsWorked(const QString call, const QString grid, const QString band, - const QString mode, const QString date) -{ - //qDebug() << "adding " << call << " as worked"; - _log.add(call,grid,band,mode,date); - QString countryName = _countries.find(call); - if (countryName.length() > 0) - _worked.setAsWorked(countryName); -} - - - diff --git a/logbook/logbook.h b/logbook/logbook.h index 95af047aa..cb3ce7b02 100644 --- a/logbook/logbook.h +++ b/logbook/logbook.h @@ -3,37 +3,43 @@ * VK3ACF July 2013 */ -#ifndef LOGBOOK_H -#define LOGBOOK_H - +#ifndef LOG_BOOK_H_ +#define LOG_BOOK_H_ +#include #include -#include -#include "countrydat.h" -#include "countriesworked.h" -#include "adif.h" +#include "WorkedBefore.hpp" -class QDir; +class Configuration; +class QByteArray; +class QDateTime; -class LogBook +class LogBook final + : private boost::noncopyable { -public: - void init(); - void match(/*in*/ const QString call, QString grid, - /*out*/ QString &countryName, bool &callWorkedBefore, bool &countryWorkedBefore, - bool &gridWorkedBefore, QString currentBand="") const; - void addAsWorked(const QString call, const QString grid, const QString band, - const QString mode, const QString date); - -private: - CountryDat _countries; - CountriesWorked _worked; - ADIF _log; - - void _setAlreadyWorkedFromLog(); + public: + LogBook (Configuration const *); + 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); + CountryDat const& countries () const {return worked_before_.countries ();} + void match (QString const& call, QString const& mode, QString const& grid, + QString &countryName, bool &callWorkedBefore, bool &countryWorkedBefore, + bool &gridWorkedBefore, QString const& currentBand = QString {}) const; + static QByteArray QSOToADIF (QString const& hisCall, QString const& hisGrid, QString const& mode, + QString const& rptSent, QString const& rptRcvd, QDateTime const& dateTimeOn, + QDateTime const& dateTimeOff, QString const& band, QString const& comments, + QString const& name, QString const& strDialFreq, QString const& myCall, + QString const& m_myGrid, QString const& m_txPower, QString const& operator_call, + QString const& xSent, QString const& xRcvd); + private: + Configuration const * config_; + WorkedBefore worked_before_; }; -#endif // LOGBOOK_H - +#endif diff --git a/logqso.cpp b/logqso.cpp index 64e2728ae..ee06279a0 100644 --- a/logqso.cpp +++ b/logqso.cpp @@ -5,9 +5,8 @@ #include #include #include -#include -#include "logbook/adif.h" +#include "logbook/logbook.h" #include "MessageBox.hpp" #include "Configuration.hpp" #include "Bands.hpp" @@ -120,33 +119,8 @@ void LogQSO::accept() m_comments=comments; QString strDialFreq(QString::number(m_dialFreq / 1.e6,'f',6)); operator_call = ui->loggedOperator->text(); - //Log this QSO to ADIF file "wsjtx_log.adi" - QString filename = "wsjtx_log.adi"; // TODO allow user to set - ADIF adifile; - auto adifilePath = QDir {QStandardPaths::writableLocation (QStandardPaths::DataLocation)}.absoluteFilePath ("wsjtx_log.adi"); - adifile.init(adifilePath); - QByteArray ADIF {adifile.QSOToADIF (hisCall, hisGrid, mode, rptSent, rptRcvd, - m_dateTimeOn, m_dateTimeOff, band, comments, name, strDialFreq, m_myCall, - m_myGrid, m_txPower, operator_call, m_xSent, m_xRcvd)}; - if (!adifile.addQSOToFile (ADIF)) - { - MessageBox::warning_message (this, tr ("Log file error"), - tr ("Cannot open \"%1\"").arg (adifilePath)); - } - - // Log to N1MM Logger - if (m_config->broadcast_to_n1mm() && m_config->valid_n1mm_info()) { - const QHostAddress n1mmhost = QHostAddress(m_config->n1mm_server_name()); - QUdpSocket _sock; - auto rzult = _sock.writeDatagram (ADIF + " ", n1mmhost, quint16(m_config->n1mm_server_port())); - if (rzult == -1) { - MessageBox::warning_message (this, tr ("Error sending log to N1MM"), - tr ("Write returned \"%1\"").arg (rzult)); - } - } - -//Log this QSO to file "wsjtx.log" + //Log this QSO to file "wsjtx.log" static QFile f {QDir {QStandardPaths::writableLocation (QStandardPaths::DataLocation)}.absoluteFilePath ("wsjtx.log")}; if(!f.open(QIODevice::Text | QIODevice::Append)) { MessageBox::warning_message (this, tr ("Log file error"), @@ -165,8 +139,38 @@ void LogQSO::accept() f.close(); } -//Clean up and finish logging - Q_EMIT acceptQSO (m_dateTimeOff, hisCall, hisGrid, m_dialFreq, mode, rptSent, rptRcvd, m_txPower, comments, name,m_dateTimeOn, operator_call, m_myCall, m_myGrid, ADIF); + //Clean up and finish logging + Q_EMIT acceptQSO (m_dateTimeOff + , hisCall + , hisGrid + , m_dialFreq + , mode + , rptSent + , rptRcvd + , m_txPower + , comments + , name + , m_dateTimeOn + , operator_call + , m_myCall + , m_myGrid + , LogBook::QSOToADIF (hisCall + , hisGrid + , mode + , rptSent + , rptRcvd + , m_dateTimeOn + , m_dateTimeOff + , band + , comments + , name + , strDialFreq + , m_myCall + , m_myGrid + , m_txPower + , operator_call + , m_xSent + , m_xRcvd)); QDialog::accept(); } diff --git a/logqso.h b/logqso.h index f70e297d7..e9099ee18 100644 --- a/logqso.h +++ b/logqso.h @@ -2,11 +2,7 @@ #ifndef LogQSO_H #define LogQSO_H -#ifdef QT5 -#include -#else -#include -#endif +#include #include #include diff --git a/mainwindow.cpp b/mainwindow.cpp index efd93ff8b..f00f98db6 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include "revision_utils.hpp" #include "qt_helpers.hpp" @@ -61,6 +62,7 @@ #include "ExchangeValidator.hpp" #include "EqualizationToolsDialog.hpp" #include "LotWUsers.hpp" +#include "logbook/countrydat.h" #include "ui_mainwindow.h" #include "moc_mainwindow.cpp" @@ -353,6 +355,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, }, m_sfx {"P", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A"}, mem_jt9 {shdmem}, + m_logBook {&m_config}, m_msAudioOutputBuffered (0u), m_framesAudioInputBuffered (RX_SAMPLE_RATE / 10), m_downSampleFactor (downSampleFactor), @@ -1296,7 +1299,7 @@ void MainWindow::dataSink(qint64 frames) freqcal_(&dec_data.d2[0],&k,&nkhz,&RxFreq,&ftol,&line[0],80); QString t=QString::fromLatin1(line); DecodedText decodedtext {t}; - ui->decodedTextBrowser->displayDecodedText (decodedtext,m_baseCall,m_config.DXCC(), + ui->decodedTextBrowser->displayDecodedText (decodedtext,m_baseCall,m_mode,m_config.DXCC(), m_logBook,m_currentBand, m_config.ppfx()); if (ui->measure_check_box->isChecked ()) { // Append results text to file "fmt.all". @@ -1538,7 +1541,7 @@ void MainWindow::fastSink(qint64 frames) if(bmsk144 and (line[0]!=0)) { QString message {QString::fromLatin1 (line)}; DecodedText decodedtext {message.replace (QChar::LineFeed, "")}; - ui->decodedTextBrowser->displayDecodedText (decodedtext,m_baseCall,m_config.DXCC(), + ui->decodedTextBrowser->displayDecodedText (decodedtext,m_baseCall,m_mode,m_config.DXCC(), m_logBook,m_currentBand,m_config.ppfx()); m_bDecoded=true; auto_sequence (decodedtext, ui->sbFtol->value (), std::numeric_limits::max ()); @@ -2821,7 +2824,7 @@ void::MainWindow::fast_decode_done() //Left (Band activity) window DecodedText decodedtext {message.replace (QChar::LineFeed, "")}; if(!m_bFastDone) { - ui->decodedTextBrowser->displayDecodedText (decodedtext,m_baseCall,m_config.DXCC(), + ui->decodedTextBrowser->displayDecodedText (decodedtext,m_baseCall,m_mode,m_config.DXCC(), m_logBook,m_currentBand,m_config.ppfx()); } @@ -2968,12 +2971,12 @@ void MainWindow::readFromStdout() //readFromStdout if(!m_bDisplayedOnce) { // This hack sets the font. Surely there's a better way! DecodedText dt{"."}; - ui->decodedTextBrowser->displayDecodedText(dt,m_baseCall,m_config.DXCC(), + ui->decodedTextBrowser->displayDecodedText(dt,m_baseCall,m_mode,m_config.DXCC(), m_logBook,m_currentBand,m_config.ppfx()); m_bDisplayedOnce=true; } } else { - ui->decodedTextBrowser->displayDecodedText(decodedtext0,m_baseCall,m_config.DXCC(), + ui->decodedTextBrowser->displayDecodedText(decodedtext0,m_baseCall,m_mode,m_config.DXCC(), m_logBook,m_currentBand,m_config.ppfx(), (ui->cbCQonly->isVisible() and ui->cbCQonly->isChecked())); } @@ -3008,7 +3011,7 @@ void MainWindow::readFromStdout() //readFromStdout if (bDisplayRight) { // This msg is within 10 hertz of our tuned frequency, or a JT4 or JT65 avg, // or contains MyCall - ui->decodedTextBrowser2->displayDecodedText(decodedtext0,m_baseCall,m_config.DXCC(), + ui->decodedTextBrowser2->displayDecodedText(decodedtext0,m_baseCall,m_mode,m_config.DXCC(), m_logBook,m_currentBand,m_config.ppfx()); if(m_mode!="JT4") { @@ -4536,7 +4539,7 @@ void MainWindow::processMessage (DecodedText const& message, Qt::KeyboardModifie QString s2 = message.string ().trimmed(); if (s1!=s2 and !message.isTX()) { if (!s2.contains(m_baseCall) or m_mode=="MSK144") { // Taken care of elsewhere if for_us and slow mode - ui->decodedTextBrowser2->displayDecodedText(message, m_baseCall,m_config.DXCC(), + ui->decodedTextBrowser2->displayDecodedText(message, m_baseCall,m_mode,m_config.DXCC(), m_logBook,m_currentBand,m_config.ppfx()); } m_QSOText = s2; @@ -5294,11 +5297,30 @@ void MainWindow::acceptQSO (QDateTime const& QSO_date_off, QString const& call, , QString const& my_call, QString const& my_grid, QByteArray const& ADIF) { QString date = QSO_date_on.toString("yyyyMMdd"); - m_logBook.addAsWorked (m_hisCall,grid,m_config.bands()->find(m_freqNominal),m_modeTx,date); + if (!m_logBook.add (m_hisCall, grid, m_config.bands()->find(m_freqNominal), m_modeTx, ADIF)) + { + MessageBox::warning_message (this, tr ("Log file error"), + tr ("Cannot open \"%1\"").arg (m_logBook.path ())); + } - m_messageClient->qso_logged (QSO_date_off, call, grid, dial_freq, mode, rpt_sent, rpt_received, tx_power, comments, name, QSO_date_on, operator_call, my_call, my_grid); + m_messageClient->qso_logged (QSO_date_off, call, grid, dial_freq, mode, rpt_sent, rpt_received + , tx_power, comments, name, QSO_date_on, operator_call, my_call, my_grid); m_messageClient->logged_ADIF (ADIF); - if (m_config.clear_DX () and !m_config.bHound()) clearDX (); + + // Log to N1MM Logger + if (m_config.broadcast_to_n1mm () && m_config.valid_n1mm_info ()) + { + QUdpSocket sock; + if (-1 == sock.writeDatagram (ADIF + " " + , QHostAddress {m_config.n1mm_server_name ()} + , m_config.n1mm_server_port ())) + { + MessageBox::warning_message (this, tr ("Error sending log to N1MM"), + tr ("Write returned \"%1\"").arg (sock.errorString ())); + } + } + + if (m_config.clear_DX () and !m_config.bHound ()) clearDX (); m_dateTimeQSOOn = QDateTime {}; } @@ -6154,7 +6176,7 @@ void MainWindow::vhfWarning() void MainWindow::enable_DXCC_entity (bool on) { if (on and !m_mode.startsWith ("WSPR") and m_mode!="Echo") { - m_logBook.init(); // re-read the log and cty.dat files + //m_logBook.init(); // re-read the log and cty.dat files // ui->gridLayout->setColumnStretch(0,55); // adjust proportions of text displays // ui->gridLayout->setColumnStretch(1,45); } else { @@ -7915,12 +7937,8 @@ 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 - QString countryName,continent; - bool callWorkedBefore,countryWorkedBefore,gridWorkedBefore; - m_logBook.match(/*in*/houndCall,"",/*out*/countryName,callWorkedBefore,countryWorkedBefore, - gridWorkedBefore,/*in*/ m_currentBand); - int i1=countryName.lastIndexOf(";"); - continent=countryName.mid(i1+2,-1); + auto const& countryName = m_logBook.countries ().find (houndCall); + auto const& continent = countryName.mid (countryName.lastIndexOf (';') + 2, -1); //If we are using a directed CQ, ignore Hound calls that do not comply. QString CQtext=ui->comboBoxCQ->currentText();