From e871860642d90104634629d15808afd1d92da0c4 Mon Sep 17 00:00:00 2001
From: Bill Somerville <bill@classdesign.com>
Date: Tue, 8 Jan 2019 18:03:43 +0000
Subject: [PATCH] Option to always start new decode periods at the top of the
 Band Activity window

---
 Configuration.cpp       |   6 ++
 Configuration.hpp       |   1 +
 Configuration.ui        | 185 +++++++++++++++++++++-------------------
 widgets/displaytext.cpp |  48 ++++++++++-
 widgets/displaytext.h   |  15 +++-
 widgets/mainwindow.cpp  |  11 ++-
 6 files changed, 171 insertions(+), 95 deletions(-)

diff --git a/Configuration.cpp b/Configuration.cpp
index 3cc900e57..62718bc5d 100644
--- a/Configuration.cpp
+++ b/Configuration.cpp
@@ -600,6 +600,7 @@ private:
   bool report_in_comments_;
   bool prompt_to_log_;
   bool autoLog_;
+  bool decodes_from_top_;
   bool insert_blank_;
   bool DXCC_;
   bool ppfx_;
@@ -694,6 +695,7 @@ bool Configuration::log_as_RTTY () const {return m_->log_as_RTTY_;}
 bool Configuration::report_in_comments () const {return m_->report_in_comments_;}
 bool Configuration::prompt_to_log () const {return m_->prompt_to_log_;}
 bool Configuration::autoLog() const {return m_->autoLog_;}
+bool Configuration::decodes_from_top () const {return m_->decodes_from_top_;}
 bool Configuration::insert_blank () const {return m_->insert_blank_;}
 bool Configuration::DXCC () const {return m_->DXCC_;}
 bool Configuration::ppfx() const {return m_->ppfx_;}
@@ -1225,6 +1227,7 @@ void Configuration::impl::initialize_models ()
   ui_->report_in_comments_check_box->setChecked (report_in_comments_);
   ui_->prompt_to_log_check_box->setChecked (prompt_to_log_);
   ui_->cbAutoLog->setChecked(autoLog_);
+  ui_->decodes_from_top_check_box->setChecked (decodes_from_top_);
   ui_->insert_blank_check_box->setChecked (insert_blank_);
   ui_->DXCC_check_box->setChecked (DXCC_);
   ui_->ppfx_check_box->setChecked (ppfx_);
@@ -1475,6 +1478,7 @@ void Configuration::impl::read_settings ()
   data_mode_ = settings_->value ("DataMode", QVariant::fromValue (data_mode_none)).value<Configuration::DataMode> ();
   prompt_to_log_ = settings_->value ("PromptToLog", false).toBool ();
   autoLog_ = settings_->value ("AutoLog", false).toBool ();
+  decodes_from_top_ = settings_->value ("DecodesFromTop", false).toBool ();
   insert_blank_ = settings_->value ("InsertBlank", false).toBool ();
   DXCC_ = settings_->value ("DXCCEntity", false).toBool ();
   ppfx_ = settings_->value ("PrincipalPrefix", false).toBool ();
@@ -1575,6 +1579,7 @@ void Configuration::impl::write_settings ()
   settings_->setValue ("DataMode", QVariant::fromValue (data_mode_));
   settings_->setValue ("PromptToLog", prompt_to_log_);
   settings_->setValue ("AutoLog", autoLog_);
+  settings_->setValue ("DecodesFromTop", decodes_from_top_);
   settings_->setValue ("InsertBlank", insert_blank_);
   settings_->setValue ("DXCCEntity", DXCC_);
   settings_->setValue ("PrincipalPrefix", ppfx_);
@@ -2018,6 +2023,7 @@ void Configuration::impl::accept ()
   report_in_comments_ = ui_->report_in_comments_check_box->isChecked ();
   prompt_to_log_ = ui_->prompt_to_log_check_box->isChecked ();
   autoLog_ = ui_->cbAutoLog->isChecked();
+  decodes_from_top_ = ui_->decodes_from_top_check_box->isChecked ();
   insert_blank_ = ui_->insert_blank_check_box->isChecked ();
   DXCC_ = ui_->DXCC_check_box->isChecked ();
   ppfx_ = ui_->ppfx_check_box->isChecked ();
diff --git a/Configuration.hpp b/Configuration.hpp
index 1154f6129..9aa0c286b 100644
--- a/Configuration.hpp
+++ b/Configuration.hpp
@@ -119,6 +119,7 @@ public:
   bool report_in_comments () const;
   bool prompt_to_log () const;
   bool autoLog() const;
+  bool decodes_from_top () const;
   bool insert_blank () const;
   bool DXCC () const;
   bool ppfx() const;
diff --git a/Configuration.ui b/Configuration.ui
index da9d71c66..0db97ebae 100644
--- a/Configuration.ui
+++ b/Configuration.ui
@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>542</width>
-    <height>542</height>
+    <width>546</width>
+    <height>536</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -171,17 +171,17 @@
           <string>Display</string>
          </property>
          <layout class="QGridLayout" name="gridLayout_4">
-          <item row="0" column="0">
-           <widget class="QCheckBox" name="insert_blank_check_box">
+          <item row="3" column="0">
+           <widget class="QCheckBox" name="TX_messages_check_box">
             <property name="toolTip">
-             <string>Include a separator line between periods in the band activity window.</string>
+             <string>Show outgoing transmitted messages in the Rx frequency window.</string>
             </property>
             <property name="text">
-             <string>&amp;Blank line between decoding periods</string>
+             <string>&amp;Tx messages to Rx frequency window</string>
             </property>
            </widget>
           </item>
-          <item row="3" column="0">
+          <item row="4" column="0">
            <widget class="QCheckBox" name="DXCC_check_box">
             <property name="toolTip">
              <string>Show if decoded stations are new DXCC entities or worked before.</string>
@@ -194,65 +194,29 @@
             </property>
            </widget>
           </item>
-          <item row="0" column="1">
-           <spacer name="horizontalSpacer_2">
-            <property name="orientation">
-             <enum>Qt::Horizontal</enum>
-            </property>
-            <property name="sizeHint" stdset="0">
-             <size>
-              <width>0</width>
-              <height>0</height>
-             </size>
-            </property>
-           </spacer>
-          </item>
-          <item row="1" column="0">
-           <widget class="QCheckBox" name="miles_check_box">
+          <item row="0" column="0">
+           <widget class="QCheckBox" name="decodes_from_top_check_box">
             <property name="toolTip">
-             <string>Show distance to DX station in miles rather than kilometers.</string>
+             <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Check to have decodes for a new period start at the top of the Band Activity window and not scroll off the top when the window is full.&lt;/p&gt;&lt;p&gt;This is to aid selecting decodes to double-click while decoding is still in progress. Use the Band Activity vertical scroll bar to reveal decodes past the bottom of the window.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
             </property>
             <property name="text">
-             <string>Display dista&amp;nce in miles</string>
+             <string>Start new period decodes at top</string>
             </property>
            </widget>
           </item>
-          <item row="2" column="0">
-           <widget class="QCheckBox" name="TX_messages_check_box">
-            <property name="toolTip">
-             <string>Show outgoing transmitted messages in the Rx frequency window.</string>
-            </property>
+          <item row="4" column="1">
+           <widget class="QCheckBox" name="ppfx_check_box">
             <property name="text">
-             <string>&amp;Tx messages to Rx frequency window</string>
+             <string>Show principal prefix instead of country name</string>
             </property>
            </widget>
           </item>
-          <item row="0" column="2" rowspan="4">
-           <layout class="QVBoxLayout" name="verticalLayout_6">
+          <item row="0" column="1" rowspan="4">
+           <layout class="QHBoxLayout" name="horizontalLayout_21">
             <item>
-             <widget class="QPushButton" name="font_push_button">
-              <property name="toolTip">
-               <string>Set the font characteristics for the application.</string>
-              </property>
-              <property name="text">
-               <string>Font...</string>
-              </property>
-             </widget>
-            </item>
-            <item>
-             <widget class="QPushButton" name="decoded_text_font_push_button">
-              <property name="toolTip">
-               <string>Set the font characteristics for the Band Activity and Rx Frequency areas.</string>
-              </property>
-              <property name="text">
-               <string>Decoded Text Font...</string>
-              </property>
-             </widget>
-            </item>
-            <item>
-             <spacer name="verticalSpacer">
+             <spacer name="horizontalSpacer_2">
               <property name="orientation">
-               <enum>Qt::Vertical</enum>
+               <enum>Qt::Horizontal</enum>
               </property>
               <property name="sizeHint" stdset="0">
                <size>
@@ -262,12 +226,62 @@
               </property>
              </spacer>
             </item>
+            <item>
+             <layout class="QVBoxLayout" name="verticalLayout_6">
+              <item>
+               <widget class="QPushButton" name="font_push_button">
+                <property name="toolTip">
+                 <string>Set the font characteristics for the application.</string>
+                </property>
+                <property name="text">
+                 <string>Font...</string>
+                </property>
+               </widget>
+              </item>
+              <item>
+               <widget class="QPushButton" name="decoded_text_font_push_button">
+                <property name="toolTip">
+                 <string>Set the font characteristics for the Band Activity and Rx Frequency areas.</string>
+                </property>
+                <property name="text">
+                 <string>Decoded Text Font...</string>
+                </property>
+               </widget>
+              </item>
+              <item>
+               <spacer name="verticalSpacer">
+                <property name="orientation">
+                 <enum>Qt::Vertical</enum>
+                </property>
+                <property name="sizeHint" stdset="0">
+                 <size>
+                  <width>0</width>
+                  <height>0</height>
+                 </size>
+                </property>
+               </spacer>
+              </item>
+             </layout>
+            </item>
            </layout>
           </item>
-          <item row="4" column="0">
-           <widget class="QCheckBox" name="ppfx_check_box">
+          <item row="1" column="0">
+           <widget class="QCheckBox" name="insert_blank_check_box">
+            <property name="toolTip">
+             <string>Include a separator line between periods in the band activity window.</string>
+            </property>
             <property name="text">
-             <string>Show principal prefix instead of country name</string>
+             <string>&amp;Blank line between decoding periods</string>
+            </property>
+           </widget>
+          </item>
+          <item row="2" column="0">
+           <widget class="QCheckBox" name="miles_check_box">
+            <property name="toolTip">
+             <string>Show distance to DX station in miles rather than kilometers.</string>
+            </property>
+            <property name="text">
+             <string>Display dista&amp;nce in miles</string>
             </property>
            </widget>
           </item>
@@ -333,6 +347,13 @@
             </item>
            </layout>
           </item>
+          <item row="3" column="1">
+           <widget class="QCheckBox" name="decode_at_52s_check_box">
+            <property name="text">
+             <string>Decode after EME delay</string>
+            </property>
+           </widget>
+          </item>
           <item row="0" column="0">
            <widget class="QCheckBox" name="monitor_off_check_box">
             <property name="toolTip">
@@ -346,20 +367,6 @@
             </property>
            </widget>
           </item>
-          <item row="0" column="1">
-           <widget class="QCheckBox" name="enable_VHF_features_check_box">
-            <property name="text">
-             <string>Enable VHF/UHF/Microwave features</string>
-            </property>
-           </widget>
-          </item>
-          <item row="3" column="1">
-           <widget class="QCheckBox" name="decode_at_52s_check_box">
-            <property name="text">
-             <string>Decode after EME delay</string>
-            </property>
-           </widget>
-          </item>
           <item row="2" column="1">
            <widget class="QCheckBox" name="single_decode_check_box">
             <property name="text">
@@ -367,6 +374,13 @@
             </property>
            </widget>
           </item>
+          <item row="0" column="1">
+           <widget class="QCheckBox" name="enable_VHF_features_check_box">
+            <property name="text">
+             <string>Enable VHF/UHF/Microwave features</string>
+            </property>
+           </widget>
+          </item>
           <item row="1" column="1">
            <widget class="QCheckBox" name="tx_QSY_check_box">
             <property name="toolTip">
@@ -440,13 +454,10 @@ quiet period when decoding is done.</string>
             </property>
            </widget>
           </item>
-          <item row="2" column="0">
-           <widget class="QCheckBox" name="quick_call_check_box">
-            <property name="toolTip">
-             <string>Automatic transmission mode.</string>
-            </property>
+          <item row="4" column="0">
+           <widget class="QCheckBox" name="alternate_bindings_check_box">
             <property name="text">
-             <string>Doubl&amp;e-click on call sets Tx enable</string>
+             <string>Alternate F1-F5 bindings</string>
             </property>
            </widget>
           </item>
@@ -461,10 +472,13 @@ text message.</string>
             </property>
            </widget>
           </item>
-          <item row="4" column="0">
-           <widget class="QCheckBox" name="alternate_bindings_check_box">
+          <item row="2" column="0">
+           <widget class="QCheckBox" name="quick_call_check_box">
+            <property name="toolTip">
+             <string>Automatic transmission mode.</string>
+            </property>
             <property name="text">
-             <string>Alternate F1-F5 bindings</string>
+             <string>Doubl&amp;e-click on call sets Tx enable</string>
             </property>
            </widget>
           </item>
@@ -472,14 +486,14 @@ text message.</string>
         </widget>
        </item>
        <item>
-        <spacer name="verticalSpacer_11">
+        <spacer name="verticalSpacer_9">
          <property name="orientation">
           <enum>Qt::Vertical</enum>
          </property>
          <property name="sizeHint" stdset="0">
           <size>
-           <width>0</width>
-           <height>0</height>
+           <width>20</width>
+           <height>40</height>
           </size>
          </property>
         </spacer>
@@ -2856,11 +2870,8 @@ Right click for insert and delete options.</string>
   <tabstop>use_dynamic_grid</tabstop>
   <tabstop>region_combo_box</tabstop>
   <tabstop>type_2_msg_gen_combo_box</tabstop>
-  <tabstop>insert_blank_check_box</tabstop>
-  <tabstop>miles_check_box</tabstop>
   <tabstop>TX_messages_check_box</tabstop>
   <tabstop>DXCC_check_box</tabstop>
-  <tabstop>ppfx_check_box</tabstop>
   <tabstop>font_push_button</tabstop>
   <tabstop>decoded_text_font_push_button</tabstop>
   <tabstop>monitor_off_check_box</tabstop>
@@ -3025,9 +3036,9 @@ Right click for insert and delete options.</string>
   </connection>
  </connections>
  <buttongroups>
+  <buttongroup name="TX_audio_source_button_group"/>
   <buttongroup name="TX_mode_button_group"/>
   <buttongroup name="CAT_data_bits_button_group"/>
-  <buttongroup name="TX_audio_source_button_group"/>
   <buttongroup name="CAT_handshake_button_group"/>
   <buttongroup name="CAT_stop_bits_button_group"/>
   <buttongroup name="PTT_method_button_group"/>
diff --git a/widgets/displaytext.cpp b/widgets/displaytext.cpp
index 56e7931be..8058908ba 100644
--- a/widgets/displaytext.cpp
+++ b/widgets/displaytext.cpp
@@ -12,6 +12,8 @@
 #include <QAction>
 #include <QListIterator>
 #include <QRegularExpression>
+#include <QScrollBar>
+#include <QDebug>
 
 #include "Configuration.hpp"
 #include "LotWUsers.hpp"
@@ -25,6 +27,8 @@ DisplayText::DisplayText(QWidget *parent)
   : QTextEdit(parent)
   , m_config {nullptr}
   , erase_action_ {new QAction {tr ("&Erase"), this}}
+  , high_volume_ {false}
+  , modified_vertical_scrollbar_max_ {-1}
 {
   setReadOnly (true);
   setUndoRedoEnabled (false);
@@ -68,8 +72,11 @@ void DisplayText::setContentFont(QFont const& font)
   cursor.movePosition (QTextCursor::StartOfLine);
   cursor.endEditBlock ();
 
-  setTextCursor (cursor);
-  ensureCursorVisible ();
+  if (!high_volume_ || !m_config || !m_config->decodes_from_top ())
+    {
+      setTextCursor (cursor);
+      ensureCursorVisible ();
+    }
 }
 
 void DisplayText::mouseDoubleClickEvent(QMouseEvent *e)
@@ -198,11 +205,44 @@ void DisplayText::appendText(QString const& text, QColor bg, QColor fg
 
   // position so viewport scrolled to left
   cursor.movePosition (QTextCursor::StartOfLine);
-  setTextCursor (cursor);
-  ensureCursorVisible ();
+  if (!high_volume_ || !m_config || !m_config->decodes_from_top ())
+    {
+      setTextCursor (cursor);
+      ensureCursorVisible ();
+    }
   document ()->setMaximumBlockCount (document ()->maximumBlockCount ());
 }
 
+void DisplayText::extend_vertical_scrollbar (int min, int max)
+{
+  if (high_volume_
+      && m_config && m_config->decodes_from_top ())
+    {
+      if (max && max != modified_vertical_scrollbar_max_)
+        {
+          auto vp_margins = viewportMargins ();
+          // add enough to vertical scroll bar range to allow last
+          // decode to just scroll of the top of the view port
+          max += viewport ()->height () - vp_margins.top () - vp_margins.bottom ();
+          modified_vertical_scrollbar_max_ = max;
+        }
+      verticalScrollBar ()->setRange (min, max);
+    }
+}
+
+void DisplayText::new_period ()
+{
+  extend_vertical_scrollbar (verticalScrollBar ()->minimum (), verticalScrollBar ()->maximum ());
+  if (high_volume_ && m_config && m_config->decodes_from_top () && !vertical_scroll_connection_)
+    {
+      vertical_scroll_connection_ = connect (verticalScrollBar (), &QScrollBar::rangeChanged
+                                             , [this] (int min, int max) {
+                                               extend_vertical_scrollbar (min, max );
+                                             });
+    }
+  verticalScrollBar ()->setSliderPosition (verticalScrollBar ()->maximum ());
+}
+
 QString DisplayText::appendWorkedB4 (QString message, QString call, QString const& grid,
                                      QColor * bg, QColor * fg, LogBook const& logBook,
                                      QString const& currentBand, QString const& currentMode)
diff --git a/widgets/displaytext.h b/widgets/displaytext.h
index 1f20ac157..feb599303 100644
--- a/widgets/displaytext.h
+++ b/widgets/displaytext.h
@@ -19,8 +19,13 @@ class DisplayText
 {
   Q_OBJECT
 public:
-  explicit DisplayText(QWidget *parent = 0);
-  void set_configuration (Configuration const * configuration) {m_config = configuration;}
+  explicit DisplayText(QWidget *parent = nullptr);
+  void set_configuration (Configuration const * configuration, bool high_volume = false)
+  {
+    disconnect (vertical_scroll_connection_);
+    m_config = configuration;
+    high_volume_ = high_volume;
+  }
   void setContentFont (QFont const&);
   void insertLineSpacer(QString const&);
   void displayDecodedText(DecodedText const& decodedText, QString const& myCall, QString const& mode,
@@ -29,6 +34,7 @@ public:
   void displayTransmittedText(QString text, QString modeTx, qint32 txFreq, bool bFastMode);
   void displayQSY(QString text);
   void displayFoxToBeCalled(QString t, QColor bg = QColor {}, QColor fg = QColor {});
+  void new_period ();
 
   Q_SIGNAL void selectCallsign (Qt::KeyboardModifiers);
   Q_SIGNAL void erased ();
@@ -42,6 +48,8 @@ protected:
   void mouseDoubleClickEvent(QMouseEvent *e);
 
 private:
+  void extend_vertical_scrollbar (int min, int max);
+
   Configuration const * m_config;
   bool m_bPrincipalPrefix;
   QString appendWorkedB4(QString message, QString callsign
@@ -51,6 +59,9 @@ private:
   QFont char_font_;
   QAction * erase_action_;
   QHash<QString, QPair<QColor, QColor>> highlighted_calls_;
+  bool high_volume_;
+  QMetaObject::Connection vertical_scroll_connection_;
+  int modified_vertical_scrollbar_max_;
 };
 
   extern QHash<QString,int> m_LoTW;
diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp
index 2e8b30768..191f2ff48 100644
--- a/widgets/mainwindow.cpp
+++ b/widgets/mainwindow.cpp
@@ -404,7 +404,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
   ui->dxGridEntry->setValidator (new MaidenheadLocatorValidator {this});
   ui->dxCallEntry->setValidator (new CallsignValidator {this});
   ui->sbTR->values ({5, 10, 15, 30});
-  ui->decodedTextBrowser->set_configuration (&m_config);
+  ui->decodedTextBrowser->set_configuration (&m_config, true);
   ui->decodedTextBrowser2->set_configuration (&m_config);
 
   m_baseCall = Radio::base_callsign (m_config.my_callsign ());
@@ -3328,7 +3328,14 @@ void MainWindow::rx_frequency_activity_cleared ()
 
 void MainWindow::decodeBusy(bool b)                             //decodeBusy()
 {
-  if (!b) m_optimizingProgress.reset ();
+  if (!b) {
+    m_optimizingProgress.reset ();
+  } else {
+    if (!m_decoderBusy)
+      {
+        ui->decodedTextBrowser->new_period ();
+      }
+  }
   m_decoderBusy=b;
   ui->DecodeButton->setEnabled(!b);
   ui->actionOpen->setEnabled(!b);