From c6688534cd7176eb4e8fe19d3a7271d5a39cb3be Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Sun, 24 May 2020 16:33:14 +0100 Subject: [PATCH] Performance improvements to decode highlighting The Highlight Callsign (13) UDP message now operates in a slightly different way. The "Highlight last" field, when true valued, causes all instances of the specified callsign to be highlighted in the decoding period. This allows external applications to highlight DX callsigns even when multiple stations are calling them. Before this was unlikely to work since the external application would have to respond to Decode (2) UDP messages exceedingly quickly to guarantee successful highlighting before another decode with the same DX call was printed. There should be no changes required to external applications acting as servers to the WSJT-X UDP Message Protocol, although using the version of the Highlight Callsign (13) with "Highlight last" should not be required for adhoc callsign highlighting. It should be reserved for commonly recurring targets and limited to no more than 100 active highlighting requests at any one time, otherwise there may be performance impacts on WSJT-X. --- Network/NetworkMessage.hpp | 16 ++++-- widgets/displaytext.cpp | 114 ++++++++++++++++++++++++------------- widgets/displaytext.h | 2 +- 3 files changed, 87 insertions(+), 45 deletions(-) diff --git a/Network/NetworkMessage.hpp b/Network/NetworkMessage.hpp index d01a3475e..8055e2a49 100644 --- a/Network/NetworkMessage.hpp +++ b/Network/NetworkMessage.hpp @@ -447,16 +447,20 @@ * The server may send this message at any time. The message * specifies the background and foreground color that will be * used to highlight the specified callsign in the decoded - * messages printed in the Band Activity panel. The WSJT-X + * messages printed in the Band Activity panel. The WSJT-X * clients maintain a list of such instructions and apply them to * all decoded messages in the band activity window. To clear - * highlighting send an invalid QColor value for either or both - * of the background and foreground fields. + * and cancel highlighting send an invalid QColor value for + * either or both of the background and foreground fields. When + * using this mode the total number of callsign highlighting + * requests should be limited otherwise the performance of WSJT-X + * decoding may be impacted. A rough rule of thumb might be too + * limit the number of active highlighting requests to no more + * than 100. * * The "Highlight last" field allows the sender to request that - * the last instance only instead of all instances of the - * specified call be highlighted or have it's highlighting - * cleared. + * all instances of "Callsign" in the last period only, instead + * of all instances in all periods, be highlighted. * * * SwitchConfiguration In 14 quint32 diff --git a/widgets/displaytext.cpp b/widgets/displaytext.cpp index db00e2587..b3ccf8eb2 100644 --- a/widgets/displaytext.cpp +++ b/widgets/displaytext.cpp @@ -497,60 +497,92 @@ namespace { void update_selection (QTextCursor& cursor, QColor const& bg, QColor const& fg) { - if (!cursor.isNull ()) + QTextCharFormat format {cursor.charFormat ()}; + if (bg.isValid ()) { - QTextCharFormat format {cursor.charFormat ()}; - if (bg.isValid ()) - { - format.setBackground (bg); - } - else - { - format.clearBackground (); - } - if (fg.isValid ()) - { - format.setForeground (fg); - } - else - { - format.clearForeground (); - } - cursor.mergeCharFormat (format); + format.setBackground (bg); } + else + { + format.clearBackground (); + } + if (fg.isValid ()) + { + format.setForeground (fg); + } + else + { + format.clearForeground (); + } + cursor.mergeCharFormat (format); } void reset_selection (QTextCursor& cursor) { - if (!cursor.isNull ()) + // restore previous text format, we rely on the text + // char format at he start of the selection being the + // old one which should be the case + auto c2 = cursor; + c2.setPosition (c2.selectionStart ()); + cursor.setCharFormat (c2.charFormat ()); + } +} + +namespace +{ + QString get_timestamp (QTextCursor& cursor) + { + QString timestamp; + if (cursor.movePosition (QTextCursor::PreviousCharacter) + && cursor.movePosition (QTextCursor::StartOfLine) + && cursor.movePosition (QTextCursor::EndOfWord, QTextCursor::KeepAnchor) + && cursor.hasSelection ()) { - // restore previous text format, we rely on the text - // char format at he start of the selection being the - // old one which should be the case - auto c2 = cursor; - c2.setPosition (c2.selectionStart ()); - cursor.setCharFormat (c2.charFormat ()); + timestamp = cursor.selectedText (); + cursor.movePosition (QTextCursor::StartOfLine); } + return timestamp; } } void DisplayText::highlight_callsign (QString const& callsign, QColor const& bg, - QColor const& fg, bool last_only) + QColor const& fg, bool last_period_only) { + // qDebug () << "DisplayText::highlight_callsign: callsign:" << callsign << "last period:" << last_period_only; + if (!callsign.size ()) + { + return; + } + QRegularExpression target {QString {"?"}, QRegularExpression::DontCaptureOption}; QTextCharFormat old_format {currentCharFormat ()}; QTextCursor cursor {document ()}; - if (last_only) + if (last_period_only) { + // highlight each instance of the given callsign (word) in the + // current period cursor.movePosition (QTextCursor::End); - cursor = document ()->find (callsign, cursor - , QTextDocument::FindBackward | QTextDocument::FindWholeWords); - if (bg.isValid () || fg.isValid ()) + QTextCursor period_start {cursor}; + QTextCursor prior {cursor}; + auto period_timestamp = get_timestamp (period_start); + while (period_timestamp.size () && period_timestamp == get_timestamp (prior)) { - update_selection (cursor, bg, fg); + period_start = prior; } - else + while (!cursor.isNull () && cursor > period_start) { - reset_selection (cursor); + cursor = document ()->find (target, cursor + , QTextDocument::FindBackward | QTextDocument::FindWholeWords); + if (!cursor.isNull () && cursor.hasSelection ()) + { + if (bg.isValid () || fg.isValid ()) + { + update_selection (cursor, bg, fg); + } + else + { + reset_selection (cursor); + } + } } } else @@ -569,8 +601,11 @@ void DisplayText::highlight_callsign (QString const& callsign, QColor const& bg, } while (!cursor.isNull ()) { - cursor = document ()->find (callsign, cursor, QTextDocument::FindWholeWords); - update_selection (cursor, bg, fg); + cursor = document ()->find (target, cursor, QTextDocument::FindWholeWords); + if (!cursor.isNull () && cursor.hasSelection ()) + { + update_selection (cursor, bg, fg); + } } } else if (pos != highlighted_calls_.end ()) @@ -579,8 +614,11 @@ void DisplayText::highlight_callsign (QString const& callsign, QColor const& bg, QTextCursor cursor {document ()}; while (!cursor.isNull ()) { - cursor = document ()->find (callsign, cursor, QTextDocument::FindWholeWords); - reset_selection (cursor); + cursor = document ()->find (target, cursor, QTextDocument::FindWholeWords); + if (!cursor.isNull () && cursor.hasSelection ()) + { + reset_selection (cursor); + } } } } diff --git a/widgets/displaytext.h b/widgets/displaytext.h index 5aa8c7103..fb8bb6134 100644 --- a/widgets/displaytext.h +++ b/widgets/displaytext.h @@ -43,7 +43,7 @@ public: Q_SLOT void appendText (QString const& text, QColor bg = QColor {}, QColor fg = QColor {} , QString const& call1 = QString {}, QString const& call2 = QString {}); Q_SLOT void erase (); - Q_SLOT void highlight_callsign (QString const& callsign, QColor const& bg, QColor const& fg, bool last_only); + Q_SLOT void highlight_callsign (QString const& callsign, QColor const& bg, QColor const& fg, bool last_period_only); private: void mouseDoubleClickEvent (QMouseEvent *) override;