From 85cb05a8122884ca586d632fe11fc44ea7dd0ece Mon Sep 17 00:00:00 2001
From: Brian Moran <brian@trucentive.com>
Date: Thu, 29 Sep 2022 08:44:31 -0700
Subject: [PATCH] edit new frequency table fields inline; refresh filter
 periodically;

---
 Configuration.cpp        | 103 ++++-------------
 models/Bands.hpp         |   2 +-
 models/FrequencyList.cpp | 234 +++++++++++++++++++++++++++++----------
 models/FrequencyList.hpp |  39 +++++--
 widgets/mainwindow.cpp   |  12 +-
 widgets/mainwindow.h     |   1 +
 6 files changed, 238 insertions(+), 153 deletions(-)

diff --git a/Configuration.cpp b/Configuration.cpp
index 1e7f83e93..9cbd0f6e7 100644
--- a/Configuration.cpp
+++ b/Configuration.cpp
@@ -185,6 +185,7 @@
 #include "item_delegates/ForeignKeyDelegate.hpp"
 #include "item_delegates/FrequencyDelegate.hpp"
 #include "item_delegates/FrequencyDeltaDelegate.hpp"
+#include "item_delegates/MessageItemDelegate.hpp"
 #include "Transceiver/TransceiverFactory.hpp"
 #include "Transceiver/Transceiver.hpp"
 #include "models/Bands.hpp"
@@ -437,31 +438,7 @@ public:
 };
 
 
-//
-// Class MessageItemDelegate
-//
-//	Item delegate for message entry such as free text message macros.
-//
-class MessageItemDelegate final
-  : public QStyledItemDelegate
-{
-public:
-  explicit MessageItemDelegate (QObject * parent = nullptr)
-    : QStyledItemDelegate {parent}
-  {
-  }
 
-  QWidget * createEditor (QWidget * parent
-                          , QStyleOptionViewItem const& /* option*/
-                          , QModelIndex const& /* index */
-                          ) const override
-  {
-    auto editor = new QLineEdit {parent};
-    editor->setFrame (false);
-    editor->setValidator (new QRegularExpressionValidator {message_alphabet, editor});
-    return editor;
-  }
-};
 
 // Internal implementation of the Configuration class.
 class Configuration::impl final
@@ -1268,6 +1245,7 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network
   ui_->frequencies_table_view->verticalHeader ()->setResizeContentsPrecision (0);
   ui_->frequencies_table_view->sortByColumn (FrequencyList_v2::frequency_column, Qt::AscendingOrder);
   ui_->frequencies_table_view->setColumnHidden (FrequencyList_v2::frequency_mhz_column, true);
+  ui_->frequencies_table_view->setColumnHidden (FrequencyList_v2::source_column, true);
 
   // delegates
   ui_->frequencies_table_view->setItemDelegateForColumn (FrequencyList_v2::frequency_column, new FrequencyDelegate {this});
@@ -2052,7 +2030,6 @@ TransceiverFactory::ParameterPack Configuration::impl::gather_rig_data ()
 void Configuration::impl::accept ()
 {
   // Called when OK button is clicked.
-
   if (!validate ())
     {
       return;			// not accepting
@@ -2627,59 +2604,19 @@ FrequencyList_v2::FrequencyItems Configuration::impl::read_frequencies_file (QSt
   FrequencyList_v2::FrequencyItems list;
   FrequencyList_v2_100::FrequencyItems list_v100;
 
-  // read file as json if ends with qrg.json
-    if (file_name.endsWith(".qrg.json", Qt::CaseInsensitive))
+  // read file as json if ends with qrg.json.
+  if (file_name.endsWith(".qrg.json", Qt::CaseInsensitive))
+    {
+      try
         {
-        QJsonDocument doc = QJsonDocument::fromJson(frequencies_file.readAll());
-        if (doc.isNull())
-            {
-            MessageBox::critical_message (this, tr ("Error reading frequencies file"), tr ("%1 - Invalid Format").arg (file_name));
-            return list;
-            }
-        QJsonObject obj = doc.object();
-        if (obj.isEmpty())
-            {
-            MessageBox::critical_message (this, tr ("Error reading frequencies file"), tr ("%1 - Information Missing ").arg (file_name));
-            return list;
-            }
-        QJsonArray arr = obj["frequencies"].toArray();
-        if (arr.isEmpty())
-            {
-            MessageBox::critical_message (this, tr ("Error reading frequencies file"), tr ("No Frequencies were found"));
-            return list;
-            }
-        int valid_entry_count = 0;
-        int skipped_entry_count = 0;
-        for (auto const &item: arr)
-          {
-            QString mode_s, region_s;
-            QJsonObject obj = item.toObject();
-            FrequencyList_v2::Item freq;
-            region_s = obj["region"].toString();
-            mode_s = obj["mode"].toString();
-
-            freq.frequency_ = obj["frequency"].toString().toDouble() * 1e6;
-            freq.region_ = IARURegions::value(region_s);
-            freq.mode_ = Modes::value(mode_s);
-            freq.description_ = obj["description"].toString();
-            freq.source_ = obj["source"].toString();
-            freq.start_time_ = QDateTime::fromString(obj["start_time"].toString(), Qt::ISODate);
-            freq.end_time_ = QDateTime::fromString(obj["end_time"].toString(), Qt::ISODate);
-            //MessageBox::critical_message (this, tr ("Entry"), tr ("Entry: %1 ").arg(freq.toString()+"[sane:" +freq.isSane() + "] [region:" + obj["region"].toString() + "] [mode:" + obj["mode"].toString()+"] "));
-            if ((freq.mode_ != Modes::ALL || QString::compare("ALL", mode_s)) &&
-                (freq.region_ != IARURegions::ALL || QString::compare("ALL", region_s, Qt::CaseInsensitive)) &&
-                freq.isSane())
-              {
-                list.push_back(freq);
-                valid_entry_count++;
-              } else
-              skipped_entry_count++;
-          }
-        MessageBox::information_message(this, tr("Loaded Frequencies from %1").arg(file_name),
-                                        tr("Entries Valid/Skipped %1").arg(QString::number(valid_entry_count) + "/" +
-                                                                           QString::number(skipped_entry_count)));
-        return list;
-      }
+          list = FrequencyList_v2::from_json_file(&frequencies_file);
+        }
+      catch (ReadFileException const &e)
+        {
+          MessageBox::critical_message(this, tr("Error reading frequency file"), e.message_);
+        }
+      return list;
+    }
 
   quint32 magic;
   ids >> magic;
@@ -2727,9 +2664,13 @@ void Configuration::impl::save_frequencies ()
   auto file_name = QFileDialog::getSaveFileName (this, tr ("Save Working Frequencies"), writeable_data_dir_.absolutePath (), tr ("Frequency files (*.qrg *.qrg.json);;All files (*.*)"));
   if (!file_name.isNull ())
     {
+      bool b_write_json = file_name.endsWith(".qrg.json", Qt::CaseInsensitive);
+
       QFile frequencies_file {file_name};
       frequencies_file.open (QFile::WriteOnly);
+
       QDataStream ods {&frequencies_file};
+
       auto selection_model = ui_->frequencies_table_view->selectionModel ();
       if (selection_model->hasSelection ()
           && MessageBox::Yes == MessageBox::query_message (this
@@ -2739,9 +2680,9 @@ void Configuration::impl::save_frequencies ()
                                                                  "Click No to save all.")))
         {
           selection_model->select (selection_model->selection (), QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
-          if (file_name.endsWith(".qrg.json", Qt::CaseInsensitive))
+          if (b_write_json)
             {
-              next_frequencies_.to_json_stream(&ods, "0x" + QString::number(qrg_magic, 16).toUpper(),
+              next_frequencies_.to_json_file(&frequencies_file, "0x" + QString::number(qrg_magic, 16).toUpper(),
                                                "0x" + QString::number(qrg_version, 16).toUpper(),
                                                next_frequencies_.frequency_list(selection_model->selectedRows()));
             } else
@@ -2751,9 +2692,9 @@ void Configuration::impl::save_frequencies ()
         }
       else
         {
-          if (file_name.endsWith(".qrg.json", Qt::CaseInsensitive))
+          if (b_write_json)
             {
-              next_frequencies_.to_json_stream(&ods,
+              next_frequencies_.to_json_file(&frequencies_file,
                                                "0x" + QString::number(qrg_magic, 16).toUpper(),
                                                "0x" + QString::number(qrg_version, 16).toUpper(),
                                                              next_frequencies_.frequency_list());
diff --git a/models/Bands.hpp b/models/Bands.hpp
index 170d4c756..77ae5766b 100644
--- a/models/Bands.hpp
+++ b/models/Bands.hpp
@@ -24,7 +24,7 @@
 //
 //  Implements the QAbstractTableModel interface as an immutable table
 //  where rows  are bands and  columns are band name,  lower frequency
-//  limit and, upper ferquency limit respectively.
+//  limit and, upper frequency limit respectively.
 //
 class Bands final
   : public QAbstractTableModel
diff --git a/models/FrequencyList.cpp b/models/FrequencyList.cpp
index e04a7a184..b5d12e859 100644
--- a/models/FrequencyList.cpp
+++ b/models/FrequencyList.cpp
@@ -23,11 +23,13 @@
 #include <QJsonArray>
 #include <QCoreApplication>
 #include <QFile>
+#include <QException>
 
 #include "Radio.hpp"
 #include "Bands.hpp"
 #include "pimpl_impl.hpp"
 #include "revision_utils.hpp"
+#include "Logger.hpp"
 
 #include "moc_FrequencyList.cpp"
 
@@ -378,7 +380,6 @@ QString FrequencyList_v2::Item::toString () const
       << end_time_.toString(Qt::ISODate) << ", "
       << description_ << ", "
       << source_ << ')';
-
   return string;
 }
 
@@ -423,6 +424,7 @@ public:
     , bands_ {bands}
     , region_filter_ {IARURegions::ALL}
     , mode_filter_ {Modes::ALL}
+    , filter_on_time_ {false}
   {
   }
 
@@ -449,6 +451,7 @@ public:
   FrequencyItems frequency_list_;
   Region region_filter_;
   Mode mode_filter_;
+  bool filter_on_time_;
 };
 
 FrequencyList_v2::FrequencyList_v2 (Bands const * bands, QObject * parent)
@@ -564,7 +567,7 @@ bool FrequencyList_v2::removeDisjointRows (QModelIndexList rows)
 
   // We must work with source model indexes because we don't want row
   // removes to invalidate model indexes we haven't yet processed. We
-  // achieve that by processing them in decending row order.
+  // achieve that by processing them in descending row order.
   for (int r = 0; r < rows.size (); ++r)
     {
       rows[r] = mapToSource (rows[r]);
@@ -585,10 +588,16 @@ bool FrequencyList_v2::removeDisjointRows (QModelIndexList rows)
   return result;
 }
 
-void FrequencyList_v2::filter (Region region, Mode mode)
+void FrequencyList_v2::filter (Region region, Mode mode, bool filter_on_time)
 {
   m_->region_filter_ = region;
   m_->mode_filter_ = mode;
+  m_->filter_on_time_ = filter_on_time;
+  invalidateFilter ();
+}
+
+void FrequencyList_v2::filter_refresh ()
+{
   invalidateFilter ();
 }
 
@@ -606,6 +615,11 @@ bool FrequencyList_v2::filterAcceptsRow (int source_row, QModelIndex const& /* p
       result = (Modes::ALL == item.mode_ && m_->mode_filter_ != Modes::FreqCal)
         || m_->mode_filter_ == item.mode_;
     }
+  if (result && m_->filter_on_time_)
+    {
+      result = (!item.start_time_.isValid() || item.start_time_ <= QDateTime::currentDateTimeUtc ()) &&
+              (!item.end_time_.isValid() || item.end_time_ >= QDateTime::currentDateTimeUtc ());
+    }
   return result;
 }
 
@@ -796,7 +810,7 @@ QVariant FrequencyList_v2::impl::data (QModelIndex const& index, int role) const
               item = Qt::AlignRight + Qt::AlignVCenter;
               break;
             }
-          break;
+            break;
 
           case description_column:
             switch (role)
@@ -810,14 +824,14 @@ QVariant FrequencyList_v2::impl::data (QModelIndex const& index, int role) const
 
                 case Qt::ToolTipRole:
                 case Qt::AccessibleDescriptionRole:
-                  item = tr ("Description");
+                  item = tr("Description");
                 break;
 
                 case Qt::TextAlignmentRole:
                   item = Qt::AlignLeft + Qt::AlignVCenter;
                 break;
               }
-          break;
+            break;
 
           case source_column:
             switch (role)
@@ -826,9 +840,6 @@ QVariant FrequencyList_v2::impl::data (QModelIndex const& index, int role) const
                 case Qt::DisplayRole:
                 case Qt::EditRole:
                 case Qt::AccessibleTextRole:
-                  item = frequency_item.start_time_ == frequency_item.end_time_
-                    ? tr ("Equal")
-                    : tr ("NOTEQUAL");
                   item = frequency_item.source_;
                 break;
 
@@ -847,6 +858,8 @@ QVariant FrequencyList_v2::impl::data (QModelIndex const& index, int role) const
             switch (role)
               {
                 case SortRole:
+                  item = frequency_item.start_time_;
+                break;
                 case Qt::DisplayRole:
                 case Qt::EditRole:
                 case Qt::AccessibleTextRole:
@@ -868,6 +881,8 @@ QVariant FrequencyList_v2::impl::data (QModelIndex const& index, int role) const
             switch (role)
               {
                 case SortRole:
+                  item = frequency_item.end_time_;
+                break;
                 case Qt::DisplayRole:
                 case Qt::EditRole:
                 case Qt::AccessibleTextRole:
@@ -902,45 +917,111 @@ bool FrequencyList_v2::impl::setData (QModelIndex const& model_index, QVariant c
       roles << role;
 
       auto& item = frequency_list_[row];
-      switch (model_index.column ())
+      switch (model_index.column())
         {
-        case region_column:
-          {
-            auto region = IARURegions::value (value.toString ());
-            if (region != item.region_)
-              {
-                item.region_ = region;
-                Q_EMIT dataChanged (model_index, model_index, roles);
-                changed = true;
-              }
-            }
-          break;
-
-        case mode_column:
-          {
-            auto mode = Modes::value (value.toString ());
-            if (mode != item.mode_)
-              {
-                item.mode_ = mode;
-                Q_EMIT dataChanged (model_index, model_index, roles);
-                changed = true;
-              }
-          }
-          break;
-
-        case frequency_column:
-          if (value.canConvert<Frequency> ())
+          case region_column:
             {
-              Radio::Frequency frequency {qvariant_cast <Radio::Frequency> (value)};
-              if (frequency != item.frequency_)
+              auto region = IARURegions::value(value.toString());
+              if (region != item.region_)
                 {
-                  item.frequency_ = frequency;
-                  // mark derived column (1) changed as well
-                  Q_EMIT dataChanged (index (model_index.row (), 1), model_index, roles);
+                  item.region_ = region;
+                  Q_EMIT dataChanged(model_index, model_index, roles);
                   changed = true;
                 }
             }
           break;
+
+          case mode_column:
+            {
+              auto mode = Modes::value(value.toString());
+              if (mode != item.mode_)
+                {
+                  item.mode_ = mode;
+                  Q_EMIT dataChanged(model_index, model_index, roles);
+                  changed = true;
+                }
+            }
+          break;
+
+          case frequency_column:
+            {
+              if (value.canConvert<Frequency>())
+                {
+                  Radio::Frequency frequency{qvariant_cast<Radio::Frequency>(value)};
+                  if (frequency != item.frequency_)
+                    {
+                      item.frequency_ = frequency;
+                      // mark derived column (1) changed as well
+                      Q_EMIT dataChanged(index(model_index.row(), 1), model_index, roles);
+                      changed = true;
+                    }
+                }
+            }
+          break;
+
+          case description_column:
+            {
+              if (value.toString() != item.description_)
+                {
+                  item.description_ = value.toString();
+                  Q_EMIT dataChanged(model_index, model_index, roles);
+                  changed = true;
+                }
+            }
+          break;
+
+          case source_column:
+            {
+              if (value.toString() != item.source_)
+                {
+                  item.source_ = value.toString();
+                  Q_EMIT dataChanged(model_index, model_index, roles);
+                  changed = true;
+                }
+            }
+          break;
+
+          case start_time_column:
+            {
+              QDateTime start_time = QDateTime::fromString(value.toString(), Qt::ISODate);
+              LOG_INFO(QString{"start_time = %1 - isEmpty %2"}.arg(value.toString()).arg(value.toString().isEmpty()));
+              if (value.toString().isEmpty())
+                { // empty string is valid
+                  start_time = QDateTime();
+                }
+              if (start_time.isValid() || start_time.isNull())
+                {
+                  item.start_time_ = start_time;
+                  if (item.end_time_.isValid() && !item.start_time_.isNull() && item.end_time_ < item.start_time_)
+                    {
+                      item.end_time_ = item.start_time_;
+                    }
+                  Q_EMIT dataChanged(model_index, index(model_index.row(), end_time_column), roles);
+                  changed = true;
+                }
+            }
+          break;
+
+          case end_time_column:
+            {
+              QDateTime end_time = QDateTime::fromString(value.toString(), Qt::ISODate);
+              if (value.toString().isEmpty())
+                { // empty string is valid
+                  end_time = QDateTime();
+                }
+              if (end_time.isValid() || end_time.isNull())
+                {
+                  item.end_time_ = end_time;
+                  if (item.start_time_.isValid() && !item.end_time_.isNull() && end_time <= item.start_time_)
+                    {
+                      item.start_time_ = end_time;
+                    }
+                  Q_EMIT dataChanged(index(model_index.row(), start_time_column), model_index, roles);
+                  changed = true;
+                }
+            }
+          break;
+
         }
     }
 
@@ -1104,8 +1185,57 @@ auto FrequencyList_v2::all_bands (Region region, Mode mode) const -> BandSet
   return result;
 }
 
+FrequencyList_v2::FrequencyItems FrequencyList_v2::from_json_file(QFile *input_file)
+{
+  FrequencyList_v2::FrequencyItems list;
+  QJsonDocument doc = QJsonDocument::fromJson(input_file->readAll());
+  if (doc.isNull())
+    {
+      throw ReadFileException {tr ("Failed to parse JSON file")};
+    }
+  QJsonObject obj = doc.object();
+  if (obj.isEmpty())
+    {
+      throw ReadFileException{tr("Information Missing")};
+    }
+  QJsonArray arr = obj["frequencies"].toArray();
+  if (arr.isEmpty())
+    {
+      throw ReadFileException{tr ("No Frequencies were found")};
+    }
+  int valid_entry_count = 0;
+  int skipped_entry_count = 0;
+  for (auto const &item: arr)
+    {
+      QString mode_s, region_s;
+      QJsonObject obj = item.toObject();
+      FrequencyList_v2::Item freq;
+      region_s = obj["region"].toString();
+      mode_s = obj["mode"].toString();
+
+      freq.frequency_ = obj["frequency"].toString().toDouble() * 1e6;
+      freq.region_ = IARURegions::value(region_s);
+      freq.mode_ = Modes::value(mode_s);
+      freq.description_ = obj["description"].toString();
+      freq.source_ = obj["source"].toString();
+      freq.start_time_ = QDateTime::fromString(obj["start_time"].toString(), Qt::ISODate);
+      freq.end_time_ = QDateTime::fromString(obj["end_time"].toString(), Qt::ISODate);
+      if ((freq.mode_ != Modes::ALL || QString::compare("ALL", mode_s)) &&
+          (freq.region_ != IARURegions::ALL || QString::compare("ALL", region_s, Qt::CaseInsensitive)) &&
+          freq.isSane())
+        {
+          list.push_back(freq);
+          valid_entry_count++;
+        } else
+        skipped_entry_count++;
+    }
+  //MessageBox::information_message(this, tr("Loaded Frequencies from %1").arg(file_name),
+  //                                tr("Entries Valid/Skipped %1").arg(QString::number(valid_entry_count) + "/" +
+  //                                                                   QString::number(skipped_entry_count)));
+  return list;
+}
 // write JSON format to a file
-void FrequencyList_v2::to_json_stream(QDataStream *ods, QString magic_s, QString version_s,
+void FrequencyList_v2::to_json_file(QFile *output_file, QString magic_s, QString version_s,
                                                     FrequencyItems const &frequency_items)
 {
   QJsonObject jobject{
@@ -1122,25 +1252,7 @@ void FrequencyList_v2::to_json_stream(QDataStream *ods, QString magic_s, QString
   jobject["frequencies"] = array;
 
   QJsonDocument d = QJsonDocument(jobject);
-  ods->writeRawData(d.toJson().data(), d.toJson().size());
-}
-
-QTextStream& qStdOut()
-{
-  static QTextStream ts( stdout );
-  return ts;
-}
-
-FrequencyList_v2::FrequencyItems FrequencyList_v2::from_json_file(QFile *input_file)
-{
-  // attempt to read the file as JSON
-  FrequencyList_v2::FrequencyItems list;
-  QByteArray jsonData = input_file->readAll();
-  QJsonDocument jsonDoc(QJsonDocument::fromJson(jsonData));
-  QJsonArray array = jsonDoc.object().value("frequencies").toArray();
-  qStdOut() << "Frequencies read";
-  qStdOut() << array.count();
-  return list;
+  output_file->write(d.toJson());
 }
 
 // previous version 100 of the FrequencyList_v2 class
diff --git a/models/FrequencyList.hpp b/models/FrequencyList.hpp
index 40002762c..75b4adcf9 100644
--- a/models/FrequencyList.hpp
+++ b/models/FrequencyList.hpp
@@ -9,6 +9,7 @@
 #include <QJsonObject>
 #include <QJsonDocument>
 #include <QFile>
+#include <QException>
 
 #include "Radio.hpp"
 #include "IARURegions.hpp"
@@ -56,19 +57,19 @@ public:
     Frequency frequency_;
     Mode mode_;
     Region region_;
-    QString toString () const;
-    bool isSane() const;
-    QJsonObject toJson () const;
     QString description_;
     QString source_;
     QDateTime start_time_;
     QDateTime end_time_;
+    QString toString () const;
+    bool isSane() const;
+    QJsonObject toJson () const;
 
   };
   using FrequencyItems = QList<Item>;
   using BandSet = QSet<QString>;
 
-  enum Column {region_column, mode_column, frequency_column, frequency_mhz_column, description_column, start_time_column, end_time_column, source_column,  SENTINAL};
+  enum Column {region_column, mode_column, frequency_column, frequency_mhz_column, description_column, start_time_column, end_time_column, source_column, SENTINAL};
 
   // an iterator that meets the requirements of the C++ for range statement
   class const_iterator
@@ -89,7 +90,6 @@ public:
   private:
     FrequencyList_v2 const * parent_;
     int row_;
-    //qint32 qrg_version_;
   };
 
   explicit FrequencyList_v2 (Bands const *, QObject * parent = nullptr);
@@ -101,8 +101,8 @@ public:
   FrequencyItems const& frequency_list () const;
   FrequencyItems frequency_list (QModelIndexList const&) const;
   void frequency_list_merge (FrequencyItems const&);
-  void to_json_stream(QDataStream *, QString, QString, FrequencyItems const&);
-  FrequencyList_v2::FrequencyItems from_json_file(QFile *);
+  void to_json_file(QFile *, QString, QString, FrequencyItems const&);
+  static FrequencyItems from_json_file(QFile *);
 
   // Iterators for the sorted and filtered items
   //
@@ -131,7 +131,7 @@ public:
   int best_working_frequency (QString const& band) const;
 
   // Set filter
-  Q_SLOT void filter (Region, Mode);
+  Q_SLOT void filter (Region, Mode, bool);
 
   // Reset
   Q_SLOT void reset_to_defaults ();
@@ -144,6 +144,9 @@ public:
   // Proxy API
   bool filterAcceptsRow (int source_row, QModelIndex const& parent) const override;
 
+  // Refresh the filter based on the current filter settings (underlying data may have changed)
+  void filter_refresh ();
+
   // Custom roles.
   static int constexpr SortRole = Qt::UserRole;
 
@@ -158,7 +161,11 @@ bool operator == (FrequencyList_v2::Item const& lhs, FrequencyList_v2::Item cons
   return
     lhs.frequency_ == rhs.frequency_
     && lhs.region_ == rhs.region_
-    && lhs.mode_ == rhs.mode_;
+    && lhs.mode_ == rhs.mode_
+    && lhs.description_ == rhs.description_
+    && lhs.source_ == rhs.source_
+    && lhs.start_time_ == rhs.start_time_
+    && lhs.end_time_ == rhs.end_time_;
 }
 
 QDataStream& operator << (QDataStream&, FrequencyList_v2::Item const&);
@@ -224,4 +231,18 @@ QDataStream& operator >> (QDataStream&, FrequencyList::Item&);
 Q_DECLARE_METATYPE (FrequencyList::Item);
 Q_DECLARE_METATYPE (FrequencyList::FrequencyItems);
 
+class ReadFileException : public QException {
+public:
+  ReadFileException (QString const& message)
+    : message_ {message}
+  {
+  }
+
+  void raise () const override { throw *this; }
+  ReadFileException * clone () const override { return new ReadFileException {*this}; }
+
+  QString filename_;
+  QString message_;
+};
+
 #endif
diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp
index 29cccad88..6de200886 100644
--- a/widgets/mainwindow.cpp
+++ b/widgets/mainwindow.cpp
@@ -1035,6 +1035,8 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
   m_wideGraph->setMode(m_mode);
 
   connect (&minuteTimer, &QTimer::timeout, this, &MainWindow::on_the_minute);
+  connect (&minuteTimer, &QTimer::timeout, this, &MainWindow::invalidate_frequencies_filter);
+
   minuteTimer.setSingleShot (true);
   minuteTimer.start (ms_minute_error () + 60 * 1000);
 
@@ -1079,6 +1081,14 @@ void MainWindow::splash_done ()
   m_splash && m_splash->close ();
 }
 
+void MainWindow::invalidate_frequencies_filter ()
+{
+  // every interval, invalidate the frequency filter, so that if any
+  // working frequency goes in/out of scope, we pick it up.
+  m_config.frequencies ()->filter_refresh ();
+  ui->bandComboBox->update ();
+}
+
 void MainWindow::on_the_minute ()
 {
   if (minuteTimer.isSingleShot ())
@@ -7159,7 +7169,7 @@ void MainWindow::on_actionFreqCal_triggered()
 void MainWindow::switch_mode (Mode mode)
 {
   m_fastGraph->setMode(m_mode);
-  m_config.frequencies ()->filter (m_config.region (), mode);
+  m_config.frequencies ()->filter (m_config.region (), mode, true); // filter on current time
   auto const& row = m_config.frequencies ()->best_working_frequency (m_freqNominal);
   ui->bandComboBox->setCurrentIndex (row);
   if (row >= 0) {
diff --git a/widgets/mainwindow.h b/widgets/mainwindow.h
index 0c7cf3647..dca2e9cbb 100644
--- a/widgets/mainwindow.h
+++ b/widgets/mainwindow.h
@@ -824,6 +824,7 @@ private:
   void subProcessError (QProcess *, QProcess::ProcessError);
   void statusUpdate () const;
   void update_watchdog_label ();
+  void invalidate_frequencies_filter ();
   void on_the_minute ();
   void add_child_to_event_filter (QObject *);
   void remove_child_from_event_filter (QObject *);