From 8e6ca932592165de86de51c9c23f72857403b367 Mon Sep 17 00:00:00 2001 From: Brian Moran Date: Fri, 2 Aug 2024 16:46:22 -0700 Subject: [PATCH] highlighting callsigns, annotating callsigns, sort hounds on more criteria --- Network/MessageClient.cpp | 17 ++- Network/MessageClient.hpp | 5 +- Network/NetworkMessage.hpp | 37 ++++++- widgets/activeStations.cpp | 57 ++++++++-- widgets/activeStations.h | 8 +- widgets/displaytext.cpp | 21 ++++ widgets/displaytext.h | 1 + widgets/mainwindow.cpp | 208 ++++++++++++++++++++++++++++--------- widgets/mainwindow.h | 4 + widgets/mainwindow.ui | 15 +++ 10 files changed, 310 insertions(+), 63 deletions(-) diff --git a/Network/MessageClient.cpp b/Network/MessageClient.cpp index 1991f1593..c2100d1b2 100644 --- a/Network/MessageClient.cpp +++ b/Network/MessageClient.cpp @@ -392,7 +392,22 @@ void MessageClient::impl::parse_message (QByteArray const& msg) } break; - default: + case NetworkMessage::AnnotationInfo: { + QByteArray dx_call; + bool sort_order_provided{false}; + quint32 sort_order{std::numeric_limits::max()}; + in >> dx_call >> sort_order_provided >> sort_order; + TRACE_UDP ("External Callsign Info:" << dx_call << "sort_order_provided:" << sort_order_provided + << "sort_order:" << sort_order); + if (sort_order > 50000) sort_order = 50000; + if (check_status(in) != Fail) { + Q_EMIT + self_->annotation_info(QString::fromUtf8(dx_call), sort_order_provided, sort_order); + } + } + break; + + default: // Ignore // // Note that although server heartbeat messages are not diff --git a/Network/MessageClient.hpp b/Network/MessageClient.hpp index 3f21a598a..2c87fe7af 100644 --- a/Network/MessageClient.hpp +++ b/Network/MessageClient.hpp @@ -123,7 +123,10 @@ public: , bool fast_mode, quint32 tr_period, quint32 rx_df, QString const& dx_call , QString const& dx_grid, bool generate_messages); - // this signal is emitted when network errors occur or if a host + // this signal is emitted if the server has sent information about a callsign + Q_SIGNAL void annotation_info (QString const& dx_call, bool sort_order_provided, quint32 sort_order); + + // this signal is emitted when network errors occur or if a host // lookup fails Q_SIGNAL void error (QString const&) const; diff --git a/Network/NetworkMessage.hpp b/Network/NetworkMessage.hpp index c211abb0f..74a268fa9 100644 --- a/Network/NetworkMessage.hpp +++ b/Network/NetworkMessage.hpp @@ -462,6 +462,14 @@ * decoding may be impacted. A rough rule of thumb might be too * limit the number of active highlighting requests to no more * than 100. + * + * Using a callsign of "CLEARALL!" and anything for the + * color values will clear the internal highlighting data. It will + * NOT remove the highlighting on the screen, however. The exclamation + * symbol is used to avoid accidental clearing of all highlighting + * data via a decoded callsign, since an exclamation symbol is not + * a valid character in a callsign. + * * The "Highlight last" field allows the sender to request that * all instances of "Callsign" in the last period only, instead @@ -494,7 +502,33 @@ * fields an empty value implies no change, for the quint32 Rx DF * and Frequency Tolerance fields the maximum quint32 value * implies no change. Invalid or unrecognized values will be - * silently ignored. + * silently ignored. NOTE that if a mode/submode change occurs and + * the current frequency is NOT in the frequency table for that + * mode, a frequency change (to the default frequency for that band + * and mode) may occur. + * + * AnnotationInfo In 16 quint32 + * Id (unique key) utf8 + * DX Call utf8 + * Sort Order Provided bool + * Sort Order quint32 + * + * The server may send this message at any time. Sort orders can be used + * for sorting hound callers when in Fox mode. A typical usage is to + * "score" callsigns based on number of bands and/or modes worked using + * an external logging program during a DXpedition, to be able to give + * preference to calls that have not been worked before on any other + * band or mode. An external program can watch decodes from wsjt-x, + * then use this message to annotate the calls with a sort order. The + * hound queue can be displayed by that sort order. * + * + * If 'sort order provided' is true, the message also specifies a numeric + * sort order for the DX call. + * + * Invalid or unrecognized values will be silently ignored. A sort-order of + * ffffffff will remove the sort-order value from the internal table. + * Callsigns without a sort order will be valued at zero for sorting purposes + * in the hound display. */ #include @@ -526,6 +560,7 @@ namespace NetworkMessage HighlightCallsign, SwitchConfiguration, Configure, + AnnotationInfo, maximum_message_type_ // ONLY add new message types // immediately before here }; diff --git a/widgets/activeStations.cpp b/widgets/activeStations.cpp index 7e5c6e458..6fb2ae6e9 100644 --- a/widgets/activeStations.cpp +++ b/widgets/activeStations.cpp @@ -40,6 +40,26 @@ void ActiveStations::changeFont (QFont const& font) updateGeometry (); } +void ActiveStations::clearStations() { + m_textbuffer.clear(); + m_decodes_by_frequency.clear(); +} + +void ActiveStations::addLine(QString line) { + QString m_textbuffer = ""; + // "012700 -1 0.2 210 ~ KJ7COA JA2HGF -14" + unsigned freq = line.mid(16, 4).toUInt(); + m_decodes_by_frequency[freq] = line; + // show them in frequency order + QMap::const_iterator i = m_decodes_by_frequency.constBegin(); + m_textbuffer.clear(); + while (i != m_decodes_by_frequency.constEnd()) { + m_textbuffer.append(i.value()); + ++i; + } + this->displayRecentStations(m_mode, m_textbuffer); +} + void ActiveStations::read_settings () { SettingsGroup group {settings_, "ActiveStations"}; @@ -60,8 +80,7 @@ void ActiveStations::write_settings () settings_->setValue("WantedOnly",ui->cbWantedOnly->isChecked()); } -void ActiveStations::displayRecentStations(QString mode, QString const& t) -{ +void ActiveStations::setupUi(QString mode) { if(mode!=m_mode) { m_mode=mode; ui->cbReadyOnly->setText(" Ready only"); @@ -71,24 +90,37 @@ void ActiveStations::displayRecentStations(QString mode, QString const& t) ui->cbReadyOnly->setText("* CQ only"); } else if(m_mode=="Q65-pileup") { ui->header_label2->setText(" N Freq Call Grid El Age(h)"); + ui->cbWantedOnly->setText(QCoreApplication::translate("ActiveStations", "Wanted only", nullptr)); + } else if(m_mode=="Fox Mode" || m_mode=="SuperFox Mode" ) { + ui->header_label2->setText(" UTC dB DT Freq " + tr("Message")); + ui->cbWantedOnly->setText(QCoreApplication::translate("ActiveStations", "My call only", nullptr)); + this->setClickOK(true); } else { ui->header_label2->setText(" N Call Grid Az S/N Freq Tx Age Pts"); ui->label->setText("Rate:"); + ui->cbWantedOnly->setText(QCoreApplication::translate("ActiveStations", "Wanted only", nullptr)); } bool b=(m_mode.left(3)=="Q65"); - ui->bandChanges->setVisible(!b); - ui->cbReadyOnly->setVisible(m_mode!="Q65-pileup"); - ui->cbWantedOnly->setVisible(m_mode!="Q65-pileup"); - ui->label_2->setVisible(!b); - ui->label_3->setVisible(!b); - ui->score->setVisible(!b); - ui->sbMaxRecent->setVisible(!b); + bool is_fox_mode =(m_mode=="Fox Mode"); + ui->bandChanges->setVisible(!b && !is_fox_mode); + ui->cbReadyOnly->setVisible(m_mode != "Q65-pileup" && !is_fox_mode); + ui->cbWantedOnly->setVisible(m_mode != "Q65-pileup"); // this is used for "My call only" in Fox mode + ui->label_2->setVisible(!b && !is_fox_mode); + ui->label_3->setVisible(!b && !is_fox_mode); + ui->score->setVisible(!b && !is_fox_mode); + ui->sbMaxRecent->setVisible(!b && !is_fox_mode); - b=(m_mode!="Q65-pileup"); + b=(m_mode!="Q65-pileup" && !is_fox_mode); ui->sbMaxAge->setVisible(b); ui->label->setVisible(b); ui->rate->setVisible(b); } +} + +void ActiveStations::displayRecentStations(QString mode, QString const& t) +{ + setupUi(mode); + bool bClickOK=m_clickOK; m_clickOK=false; ui->RecentStationsPlainTextEdit->setPlainText(t); @@ -143,7 +175,10 @@ void ActiveStations::on_textEdit_clicked() if(text!="") { int nline=text.left(2).toInt(); if(QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier)) nline=-nline; - emit callSandP(nline); + if ("Fox Mode" != m_mode) + emit callSandP(nline); + else + emit queueActiveWindowHound(text); } } } diff --git a/widgets/activeStations.h b/widgets/activeStations.h index cb54970f0..60cdc700c 100644 --- a/widgets/activeStations.h +++ b/widgets/activeStations.h @@ -3,6 +3,7 @@ #define ARRL_DIGI_H_ #include +#include class QSettings; class QFont; @@ -20,6 +21,7 @@ public: explicit ActiveStations(QSettings *, QFont const&, QWidget * parent = 0); ~ActiveStations(); void displayRecentStations(QString mode, QString const&); + void setupUi(QString display_mode); void changeFont (QFont const&); int maxRecent(); int maxAge(); @@ -30,6 +32,8 @@ public: void setRate(int n); void setBandChanges(int n); void setScore(int n); + void clearStations(); + void addLine(QString); bool m_clickOK=false; bool m_bReadyOnly; @@ -41,14 +45,16 @@ private: Q_SIGNAL void callSandP(int nline); Q_SIGNAL void activeStationsDisplay(); Q_SIGNAL void cursorPositionChanged(); + Q_SIGNAL void queueActiveWindowHound(QString text); Q_SLOT void on_cbReadyOnly_toggled(bool b); Q_SLOT void on_cbWantedOnly_toggled(bool b); Q_SLOT void on_textEdit_clicked(); -// qint64 m_msec0=0; QString m_mode=""; QSettings * settings_; + QString m_textbuffer=""; // F/H mode band decodes + QMap m_decodes_by_frequency; // store decodes for F/H band awareness by frequency QScopedPointer ui; }; diff --git a/widgets/displaytext.cpp b/widgets/displaytext.cpp index c00a30961..5bd4ef4ac 100644 --- a/widgets/displaytext.cpp +++ b/widgets/displaytext.cpp @@ -560,6 +560,22 @@ void DisplayText::displayHoundToBeCalled(QString t, bool bAtTop, QColor bg, QCol insertText(t, bg, fg, "", "", bAtTop ? QTextCursor::Start : QTextCursor::End); } +void DisplayText::setHighlightedHoundText(QString t) { + QColor bg; + QColor fg; + highlight_types types{Highlight::Call}; + set_colours(m_config, &bg, &fg, types); + // t is multiple lines of text, each line is a hound calling + // iterate through each line and highlight the callsign + auto lines = t.split(QChar('\n'), Qt::SkipEmptyParts); + clear(); + foreach (auto line, lines) + { + auto fields = line.split(QChar(' '), Qt::SkipEmptyParts); + insertText(line, bg, fg, fields.first(), QString{}); + } +} + namespace { void update_selection (QTextCursor& cursor, QColor const& bg, QColor const& fg) @@ -619,6 +635,11 @@ void DisplayText::highlight_callsign (QString const& callsign, QColor const& bg, { return; } + if (callsign == "CLEARALL!") // programmatic means of clearing all highlighting + { + highlighted_calls_.clear(); + return; + } auto regexp = callsign; // allow for hashed callsigns and escape any regexp metacharacters QRegularExpression target {QString {";$]*"}; // grid exact match excluding RR73 QRegularExpression grid_regexp {"\\A(?![Rr]{2}73)[A-Ra-r]{2}[0-9]{2}([A-Xa-x]{2}){0,1}\\z"}; + QRegularExpression non_r_db_regexp {"\\A[-+]{1}[0-9]{1,2}\\z"}; auto quint32_max = std::numeric_limits::max (); constexpr int N_WIDGETS {38}; constexpr int default_rx_audio_buffer_frames {-1}; // lets Qt decide @@ -697,7 +698,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, // initialize decoded text font and hook up font change signals // defer initialization until after construction otherwise menu fonts do not get set - // with 50 ms delay we are on the save side + // with 50 ms delay we are on the safe side QTimer::singleShot (50, this, SLOT (initialize_fonts ())); connect (&m_config, &Configuration::text_font_changed, [this] (QFont const& font) { set_application_font (font); @@ -3103,9 +3104,15 @@ void MainWindow::on_actionActiveStations_triggered() m_ActiveStationsWidget->activateWindow(); configActiveStations(); connect(m_ActiveStationsWidget.data(), SIGNAL(callSandP(int)),this,SLOT(callSandP2(int))); + // connect up another signal to handle clicks in the Activity window when in Fox mode + connect(m_ActiveStationsWidget.data(), SIGNAL(queueActiveWindowHound(QString)),this,SLOT(queueActiveWindowHound2(QString)),static_cast(Qt::UniqueConnection)); + connect(m_ActiveStationsWidget.data(), SIGNAL(activeStationsDisplay()),this,SLOT(ARRL_Digi_Display())); m_ActiveStationsWidget->setScore(m_score); if(m_mode=="Q65") m_ActiveStationsWidget->setRate(m_score); + QString as_mode = m_mode; + if(m_mode=="FT8" && SpecOp::FOX==m_specOp) as_mode="Fox Mode"; // TODO - active stations for hound mode? + m_ActiveStationsWidget->setupUi(as_mode); } void MainWindow::on_actionOpen_triggered() //Open File @@ -3865,6 +3872,12 @@ void MainWindow::ARRL_Digi_Display() readWidebandDecodes(); return; } + if (m_mode == "Fox Mode") { // ARRL_Digi_Display can be shown for other modes + if (m_ActiveStationsWidget != NULL) { + m_ActiveStationsWidget->setClickOK(true); + } + return; + } QMutableMapIterator icall(m_recentCall); QString deCall,deGrid; int age=0; @@ -3935,10 +3948,53 @@ void MainWindow::ARRL_Digi_Display() t += (t1 + list[k] + "\n"); if(i>=maxRecent) break; } - if(m_ActiveStationsWidget!=NULL) m_ActiveStationsWidget->displayRecentStations(m_mode,t); + bool is_fox_mode = (m_mode=="FT8" && m_specOp == SpecOp::FOX); + if(m_ActiveStationsWidget!=NULL && !is_fox_mode) m_ActiveStationsWidget->displayRecentStations(m_mode,t); m_ActiveStationsWidget->setClickOK(true); } +void MainWindow::queueActiveWindowHound2(QString line) { + // Active Window shows what's going on outside of current F/H display rules (calling below 1000Hz e.g.) + // TODO should we allow calling a station that's calling another station, not us? + if (m_mode == "FT8" and m_specOp == SpecOp::FOX) { + // process the line to get the callsign + QStringList w = line.split(' ', SkipEmptyParts); + // make sure our call is the first in the list, or the station is CQing (not a directed CQ) + if ( (w.size() > 7) && + (w[5] == m_config.my_callsign() || w[5] == "<"+m_config.my_callsign()+">" || w[5]=="CQ") && + ( w[7].contains(grid_regexp) || w[7].contains(non_r_db_regexp) )){ + QString caller = w[6]; + QString grid = ""; + QString db = w[1]; + int db_i = w[1].toInt(); + db = (db_i >=0 ? "+":"") + QStringLiteral("%1").arg(db_i, (db_i >=0 ? 2:3), 10, QLatin1Char('0')); // +00, -01 etc. + // houndcall rpt grid + if (w[7].contains(grid_regexp)) grid = w[7]; + if (w[7].contains(non_r_db_regexp)) { + LOG_INFO(QString("%1 called with signal report %2").arg(caller).arg(w[7])); + } + if (caller.length() > 2) { + // make sure it's not already in the queue + for ( QString hs : m_houndQueue) { + if (hs.startsWith(caller)) { + LOG_INFO(QString("%1 already in queue. Skipping").arg(hs)); + return; + } + } + QString caller_rpt = (caller+" ").mid(0,12)+db; + if (m_houndQueue.count() < MAX_HOUNDS_IN_QUEUE) { + // add it to the queue + m_houndQueue.enqueue(caller_rpt + " " + grid); + refreshHoundQueueDisplay(); + // TODO: remove from active stations window too? + } + } + } else { + LOG_INFO(QString("queueActiveWindowHound2 - skipping %1").arg(line)); + } + } +} + void MainWindow::callSandP2(int n) { bool bCtrl = (n<0); @@ -4035,12 +4091,20 @@ void MainWindow::activeWorked(QString call, QString band) void MainWindow::readFromStdout() //readFromStdout { bool bDisplayPoints = false; + QString all_decodes; if(m_ActiveStationsWidget!=NULL) { bDisplayPoints=(m_mode=="FT4" or m_mode=="FT8") and (m_specOp==SpecOp::ARRL_DIGI or m_ActiveStationsWidget->isVisible()); } while(proc_jt9.canReadLine()) { auto line_read = proc_jt9.readLine (); + if (m_mode == "FT8" and m_specOp == SpecOp::FOX and m_ActiveStationsWidget != NULL) { // see if we should add this to ActiveStations window + QString the_line = QString(line_read); + if (!m_ActiveStationsWidget->wantedOnly() || + (the_line.contains(" " + m_config.my_callsign() + " ") || + the_line.contains(" <" + m_config.my_callsign() + "> "))) + all_decodes.append(line_read); + } if (auto p = std::strpbrk (line_read.constData (), "\n\r")) { // truncate before line ending chars line_read = line_read.left (p - line_read.constData ()); @@ -4124,6 +4188,10 @@ void MainWindow::readFromStdout() //readFromStdout if(m_TRperiod>=60) ntime=4; if (line_read.left(ntime) != m_tBlankLine && QString::fromUtf8(line_read.constData()).left(4).contains(QRegularExpression {"\\d\\d\\d\\d"})) { ui->decodedTextBrowser->new_period (); + if (m_specOp == SpecOp::FOX and m_ActiveStationsWidget != NULL) { // clear the ActiveStations window + m_ActiveStationsWidget->clearStations(); + m_ActiveStationsWidget->displayRecentStations("Fox Mode", ""); + } if (m_config.insert_blank () && SpecOp::FOX != m_specOp) { QString band; @@ -4439,6 +4507,9 @@ void MainWindow::readFromStdout() //readFromStdout } } } + if (m_mode == "FT8" and m_specOp == SpecOp::FOX and m_ActiveStationsWidget != NULL) { + m_ActiveStationsWidget->addLine(all_decodes); + } } // @@ -4652,7 +4723,7 @@ void MainWindow::guiUpdate() m_bTxTime = (t2p >= tx1) and (t2p < tx2); if(m_mode=="Echo") m_bTxTime = m_bTxTime and m_bEchoTxOK; if(m_mode=="FT8" and ui->tx5->currentText().contains("/B ")) { - //FT8 beacon transmissiion from Tx5 only at top of a UTC minute + //FT8 beacon transmission from Tx5 only at top of a UTC minute double t4p=fmod(tsec,4*m_TRperiod); if(t4p >= 30.0) m_bTxTime=false; } @@ -4731,7 +4802,7 @@ void MainWindow::guiUpdate() ui->TxFreqSpinBox->setValue(750); //SuperFox transmits at 750 Hz } else { if (ui->TxFreqSpinBox->value() > 900) { - ui->TxFreqSpinBox->setValue(300); + ui->TxFreqSpinBox->setValue(500); } } } @@ -7226,7 +7297,7 @@ void MainWindow::on_actionFT8_triggered() ui->rh_decodes_title_label->setText(tr ("Rx Frequency")); if(SpecOp::FOX==m_specOp) { ui->lh_decodes_title_label->setText(tr ("Stations calling DXpedition %1").arg (m_config.my_callsign())); - ui->lh_decodes_headings_label->setText( "Call Grid dB Freq Dist Age Continent"); + ui->lh_decodes_headings_label->setText( "Call Grid dB Freq Dist Age Cont Score"); } else { ui->lh_decodes_title_label->setText(tr ("Band Activity")); ui->lh_decodes_headings_label->setText( " UTC dB DT Freq " + tr ("Message")); @@ -7252,16 +7323,18 @@ void MainWindow::on_actionFT8_triggered() if(m_config.superFox()) { ui->TxFreqSpinBox->setValue(750); //SuperFox transmits at 750 Hz } else { - ui->TxFreqSpinBox->setValue(300); + ui->TxFreqSpinBox->setValue(500); } // 01234567890123456789012345678901234567 - displayWidgets(nWidgets("11101000010011100001000000000010000000")); + displayWidgets(nWidgets("11101000010011100001000000000011000000")); + ui->cbRxAll->setText(tr("Show Already Worked")); if(m_config.superFox()) { ui->labDXped->setText(tr ("Super Fox")); } else { ui->labDXped->setText(tr ("Fox")); } on_fox_log_action_triggered(); + if (m_ActiveStationsWidget) m_ActiveStationsWidget->setClickOK(true); // allow clicks } if(SpecOp::HOUND == m_specOp) { ui->houndButton->setChecked(true); @@ -7272,6 +7345,7 @@ void MainWindow::on_actionFT8_triggered() ui->cbHoldTxFreq->setChecked(true); // 01234567890123456789012345678901234567 displayWidgets(nWidgets("11101000010011000001000000000011000000")); + ui->cbRxAll->setText(tr("Rx All Freqs")); if(m_config.superFox()) { ui->labDXped->setText(tr ("Super Hound")); ui->cbRxAll->setEnabled(false); @@ -7288,6 +7362,7 @@ void MainWindow::on_actionFT8_triggered() ui->txb4->setEnabled(false); ui->txb5->setEnabled(false); ui->txb6->setEnabled(false); + if (m_ActiveStationsWidget) m_ActiveStationsWidget->setClickOK(false); } else { switch_mode (Modes::FT8); } @@ -9995,15 +10070,21 @@ QString MainWindow::sortHoundCalls(QString t, int isort, int max_dB) * 2: Grid * 3: SNR (reverse order) * 4: Distance (reverse order) + * 5: Age (reverse order) + * 6: Continent + * 7: User defined (reverse order) + * */ QMap map; QStringList lines,lines2; QString msg,houndCall,t1; QString ABC{"ABCDEFGHIJKLMNOPQRSTUVWXYZ _"}; + QList reverse_sorted{3,4,5,6}; + QString Continents{" AF AN AS EU NA OC SA UN "}; // matches what we get from AD1C's country list QList list; int i,j,k,n,nlines; - bool bReverse=(isort >= 3); + bool bReverse = reverse_sorted.contains(isort); isort=qAbs(isort); // Save only the most recent transmission from each caller. @@ -10012,7 +10093,7 @@ QString MainWindow::sortHoundCalls(QString t, int isort, int max_dB) for(i=0; idecodedTextBrowser->setText(m_houndCallers); // Populate left window with Hound callers + ui->decodedTextBrowser->setHighlightedHoundText(m_houndCallers); // Populate left window with Hound callers QString t1=houndCall + " "; QString t2=rpt; QString t1_with_grid; if(rpt.mid(0,1) != "-" and rpt.mid(0,1) != "+") t2="+" + rpt; if(t2.length()==2) t2=t2.mid(0,1) + "0" + t2.mid(1,1); t1=t1.mid(0,12) + t2; - ui->houndQueueTextBrowser->displayHoundToBeCalled(t1, bTopQueue); // Add hound call and rpt to tb4 + // display the callers, highlighting calls if necessary + ui->houndQueueTextBrowser->insertText(bTopQueue ? t1 + "\n" : t1, QColor{}, QColor{}, houndCall, "", bTopQueue ? QTextCursor::Start : QTextCursor::End); + t1_with_grid=t1 + " " + houndGrid; // Append the grid if (bTopQueue) @@ -10131,6 +10246,9 @@ void MainWindow::selectHound(QString line, bool bTopQueue) QTextCursor cursor = ui->houndQueueTextBrowser->textCursor(); cursor.setPosition(0); // Scroll to top of list ui->houndQueueTextBrowser->setTextCursor(cursor); + cursor = ui->decodedTextBrowser->textCursor(); + cursor.setPosition(0); // Highlighting happens in the forward direction + ui->decodedTextBrowser->setTextCursor(cursor); } //------------------------------------------------------------------------------ @@ -10214,8 +10332,11 @@ void MainWindow::houndCallers() if(t.length()>30) { m_isort=ui->comboBoxHoundSort->currentIndex(); QString t1=sortHoundCalls(t,m_isort,m_max_dB); - ui->decodedTextBrowser->setText(t1); + ui->decodedTextBrowser->setHighlightedHoundText(t1); } + QTextCursor cursor = ui->decodedTextBrowser->textCursor(); + cursor.setPosition(0); // Set scroll at top, in preparation for highlighting messages + ui->decodedTextBrowser->setTextCursor(cursor); f.close(); } } @@ -10252,7 +10373,7 @@ void MainWindow::updateFoxQSOsInProgressDisplay() QString hc = m_foxQSOinProgress.at(i); QString status = m_foxQSO[hc].ncall > m_maxStrikes ? QString(" (rx) ") : QString(" "); QString str = (hc + " ").left(13) + QString::number(m_foxQSO[hc].ncall) + status; - ui->foxTxListTextBrowser->displayHoundToBeCalled(str); + ui->foxTxListTextBrowser->insertText(str, QColor{}, QColor{}, hc, "", QTextCursor::End); } } @@ -10350,7 +10471,7 @@ list1Done: m_foxQSO[hc].rcvd = -99; //Have not received R+rpt m_foxQSO[hc].tFoxRrpt = -1; //Have not received R+rpt m_foxQSO[hc].tFoxTxRR73 = -1; //Have not sent RR73 - rm_tb4(hc); //Remove this Hound from tb4 + refreshHoundQueueDisplay(); if(list2.size()==m_Nslots) { break; @@ -10480,24 +10601,13 @@ void MainWindow::update_foxLogWindow_rate() } } -void MainWindow::rm_tb4(QString houndCall) +void MainWindow::refreshHoundQueueDisplay() { - if(houndCall=="") return; - QString t=""; - QString tb4=ui->houndQueueTextBrowser->toPlainText(); - QStringList list=tb4.split("\n"); - int n=list.size(); - int j=0; - for (int i=0; i0) t += "\n"; - QString line=list.at(i); - if(!line.contains(houndCall + " ")) { - j++; - t += line; - } + ui->houndQueueTextBrowser->clear(); + for (QString line: m_houndQueue) { + auto hc = line.mid(0, 12).trimmed(); + ui->houndQueueTextBrowser->insertText(line, QColor{}, QColor{}, hc, "", QTextCursor::End); } - t.replace("\n\n","\n"); - ui->houndQueueTextBrowser->setText(t); } void MainWindow::doubleClickOnFoxQueue(Qt::KeyboardModifiers modifiers) @@ -10521,14 +10631,9 @@ void MainWindow::doubleClickOnFoxQueue(Qt::KeyboardModifiers modifiers) } } m_houndQueue.prepend(houndLine); - ui->houndQueueTextBrowser->clear(); - for (QString line: m_houndQueue) - { - ui->houndQueueTextBrowser->displayHoundToBeCalled(line.mid(0,16), false); - } + refreshHoundQueueDisplay(); } else { - rm_tb4(houndCall); writeFoxQSO(" Del: " + houndCall); QQueue tmpQueue; while (!m_houndQueue.isEmpty()) @@ -10538,6 +10643,7 @@ void MainWindow::doubleClickOnFoxQueue(Qt::KeyboardModifiers modifiers) if (hc != houndCall) tmpQueue.enqueue(t); } m_houndQueue.swap(tmpQueue); + refreshHoundQueueDisplay(); } } @@ -10559,7 +10665,7 @@ void MainWindow::doubleClickOnFoxInProgress(Qt::KeyboardModifiers modifiers) void MainWindow::foxQueueTopCallCommand() { m_decodedText2 = true; - if(SpecOp::FOX==m_specOp && m_decodedText2 && m_houndQueue.count() < 10) + if(SpecOp::FOX==m_specOp && m_decodedText2 && m_houndQueue.count() < MAX_HOUNDS_IN_QUEUE) { QTextCursor cursor = ui->decodedTextBrowser->textCursor(); @@ -10687,7 +10793,7 @@ void MainWindow::foxTest() hc1=line.mid(i0+6); int i1=hc1.indexOf(" "); hc1=hc1.mid(0,i1); - rm_tb4(hc1); + writeFoxQSO(" Del: " + hc1); QQueue tmpQueue; while(!m_houndQueue.isEmpty()) { @@ -10696,6 +10802,7 @@ void MainWindow::foxTest() if(hc != hc1) tmpQueue.enqueue(t); } m_houndQueue.swap(tmpQueue); + refreshHoundQueueDisplay(); } if(line.contains("Rx:")) { msg=line.mid(37+6); @@ -10870,13 +10977,18 @@ void MainWindow::set_mode (QString const& mode) else if ("Echo" == mode) on_actionEcho_triggered (); } -void MainWindow::configActiveStations() -{ - if(m_ActiveStationsWidget!=NULL and (m_mode=="Q65" or m_mode=="FT4" or m_mode=="FT8")) { - if(m_specOp==SpecOp::Q65_PILEUP) { - m_ActiveStationsWidget->displayRecentStations("Q65-pileup",""); +void MainWindow::configActiveStations() { + if (m_ActiveStationsWidget != NULL and (m_mode == "Q65" or m_mode == "FT4" or m_mode == "FT8")) { + if (m_specOp == SpecOp::Q65_PILEUP) { + m_ActiveStationsWidget->displayRecentStations("Q65-pileup", ""); } else { - m_ActiveStationsWidget->displayRecentStations(m_mode,""); + if (m_specOp == SpecOp::FOX) + if (m_config.superFox()) + m_ActiveStationsWidget->displayRecentStations("SuperFox Mode", ""); + else + m_ActiveStationsWidget->displayRecentStations("Fox Mode", ""); + else + m_ActiveStationsWidget->displayRecentStations(m_mode, ""); } } } diff --git a/widgets/mainwindow.h b/widgets/mainwindow.h index c741a5582..adcb359f8 100644 --- a/widgets/mainwindow.h +++ b/widgets/mainwindow.h @@ -57,6 +57,7 @@ #define MAX_NUM_SYMBOLS 250 #define TX_SAMPLE_RATE 48000 #define NRING 3456000 +#define MAX_HOUNDS_IN_QUEUE 10 extern int volatile itone[MAX_NUM_SYMBOLS]; //Audio tones for all Tx symbols extern int volatile icw[NUM_CW_SYMBOLS]; //Dits for CW ID @@ -345,6 +346,8 @@ private slots: , bool fast_mode, quint32 tr_period, quint32 rx_df, QString const& dx_call , QString const& dx_grid, bool generate_messages); void callSandP2(int nline); + void refreshHoundQueueDisplay(); + void queueActiveWindowHound2(QString text); private: Q_SIGNAL void initializeAudioOutputStream (QAudioDeviceInfo, @@ -702,6 +705,7 @@ private: QMap m_foxQSO; //Key = HoundCall, value = parameters for QSO in progress QMap m_loggedByFox; //Key = HoundCall, value = logged band + QMap m_annotated_callsigns; //Key = HoundCall, value = provided by api call struct FixupQSO //Info for fixing Fox's log from file "FoxQSO.txt" { diff --git a/widgets/mainwindow.ui b/widgets/mainwindow.ui index cc0578413..455ebdb2d 100644 --- a/widgets/mainwindow.ui +++ b/widgets/mainwindow.ui @@ -2375,6 +2375,21 @@ Double-click to reset to the standard 73 message Distance + + + Age + + + + + Continent + + + + + Score + +