mirror of
				https://github.com/saitohirga/WSJT-X.git
				synced 2025-10-29 20:10:28 -04:00 
			
		
		
		
	git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@6583 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
		
			
				
	
	
		
			613 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			613 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "FrequencyList.hpp"
 | |
| 
 | |
| #include <utility>
 | |
| 
 | |
| #include <QAbstractTableModel>
 | |
| #include <QString>
 | |
| #include <QList>
 | |
| #include <QListIterator>
 | |
| #include <QVector>
 | |
| #include <QStringList>
 | |
| #include <QMimeData>
 | |
| #include <QDataStream>
 | |
| #include <QByteArray>
 | |
| #include <QDebug>
 | |
| #include <QDebugStateSaver>
 | |
| 
 | |
| #include "Bands.hpp"
 | |
| #include "pimpl_impl.hpp"
 | |
| 
 | |
| #include "moc_FrequencyList.cpp"
 | |
| 
 | |
| namespace
 | |
| {
 | |
|   FrequencyList::FrequencyItems const default_frequency_list =
 | |
|     {
 | |
|       {136000, Modes::WSPR},
 | |
|       {136130, Modes::JT65},
 | |
|       {474200, Modes::JT65},
 | |
|       {474200, Modes::JT9},
 | |
|       {474200, Modes::WSPR},
 | |
|       {1836600, Modes::WSPR},
 | |
|       {1838000, Modes::JT65},
 | |
|       {1840000, Modes::JT9},
 | |
|       {3576000, Modes::JT65},
 | |
|       {3578000, Modes::JT9},
 | |
|       {3592600, Modes::WSPR},
 | |
|       {5357000, Modes::JT65},
 | |
|       {5287200, Modes::WSPR},
 | |
|       {7038600, Modes::WSPR},
 | |
|       {7076000, Modes::JT65},
 | |
|       {7078000, Modes::JT9},
 | |
|       {10138000, Modes::JT65},
 | |
|       {10138700, Modes::WSPR},
 | |
|       {10140000, Modes::JT9},
 | |
|       {14095600, Modes::WSPR},
 | |
|       {14076000, Modes::JT65},
 | |
|       {14078000, Modes::JT9},
 | |
|       {18102000, Modes::JT65},
 | |
|       {18104000, Modes::JT9},
 | |
|       {18104600, Modes::WSPR},
 | |
|       {21076000, Modes::JT65},
 | |
|       {21078000, Modes::JT9},
 | |
|       {21094600, Modes::WSPR},
 | |
|       {24917000, Modes::JT65},
 | |
|       {24919000, Modes::JT9},
 | |
|       {24924600, Modes::WSPR},
 | |
|       {28076000, Modes::JT65},
 | |
|       {28078000, Modes::JT9},
 | |
|       {28124600, Modes::WSPR},
 | |
|       {50000000, Modes::Echo},
 | |
|       {50276000, Modes::JT65},
 | |
|       {50293000, Modes::WSPR},
 | |
|       {70091000, Modes::JT65},
 | |
|       {70091000, Modes::WSPR},
 | |
|       {144000000, Modes::Echo},
 | |
|       {144100000, Modes::JT4},
 | |
|       {144489000, Modes::JT65},
 | |
|       {144489000, Modes::WSPR},
 | |
|       {222000000, Modes::JT4},
 | |
|       {222000000, Modes::JT65},
 | |
|       {432000000, Modes::Echo},
 | |
|       {432000000, Modes::JT4},
 | |
|       {432000000, Modes::JT65},
 | |
|       {432300000, Modes::WSPR},
 | |
|       {902000000, Modes::JT4},
 | |
|       {902000000, Modes::JT65},
 | |
|       {1296000000, Modes::Echo},
 | |
|       {1296000000, Modes::JT4},
 | |
|       {1296000000, Modes::JT65},
 | |
|       {1296500000, Modes::WSPR},
 | |
|       {2301000000, Modes::Echo},
 | |
|       {2301000000, Modes::JT4},
 | |
|       {2301000000, Modes::JT65},
 | |
|       {2304000000, Modes::Echo},
 | |
|       {2304000000, Modes::JT4},
 | |
|       {2304000000, Modes::JT65},
 | |
|       {2320000000, Modes::Echo},
 | |
|       {2320000000, Modes::JT4},
 | |
|       {2320000000, Modes::JT65},
 | |
|       {3400000000, Modes::Echo},
 | |
|       {3400000000, Modes::JT4},
 | |
|       {3400000000, Modes::JT65},
 | |
|       {3456000000, Modes::JT4},
 | |
|       {3456000000, Modes::JT65},
 | |
|       {5760000000, Modes::Echo},
 | |
|       {5760000000, Modes::JT4},
 | |
|       {5760000000, Modes::JT65},
 | |
|       {10368000000, Modes::Echo},
 | |
|       {10368000000, Modes::JT4},
 | |
|       {10368000000, Modes::JT65},
 | |
|       {24048000000, Modes::Echo},
 | |
|       {24048000000, Modes::JT4},
 | |
|       {24048000000, Modes::JT65},
 | |
|     };
 | |
| }
 | |
| 
 | |
| #if !defined (QT_NO_DEBUG_STREAM)
 | |
| QDebug operator << (QDebug debug, FrequencyList::Item const& item)
 | |
| {
 | |
|   QDebugStateSaver saver {debug};
 | |
|   debug.nospace () << "FrequencyItem("
 | |
|                    << item.frequency_ << ", "
 | |
|                    << item.mode_ << ')';
 | |
|   return debug;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| QDataStream& operator << (QDataStream& os, FrequencyList::Item const& item)
 | |
| {
 | |
|   return os << item.frequency_
 | |
|             << item.mode_;
 | |
| }
 | |
| 
 | |
| QDataStream& operator >> (QDataStream& is, FrequencyList::Item& item)
 | |
| {
 | |
|   return is >> item.frequency_
 | |
|             >> item.mode_;
 | |
| }
 | |
| 
 | |
| class FrequencyList::impl final
 | |
|   : public QAbstractTableModel
 | |
| {
 | |
| public:
 | |
|   impl (Bands const * bands, QObject * parent)
 | |
|     : QAbstractTableModel {parent}
 | |
|     , bands_ {bands}
 | |
|     , mode_filter_ {Modes::NULL_MODE}
 | |
|   {
 | |
|   }
 | |
| 
 | |
|   FrequencyItems frequency_list (FrequencyItems);
 | |
|   QModelIndex add (Item);
 | |
| 
 | |
|   // Implement the QAbstractTableModel interface
 | |
|   int rowCount (QModelIndex const& parent = QModelIndex {}) const override;
 | |
|   int columnCount (QModelIndex const& parent = QModelIndex {}) const override;
 | |
|   Qt::ItemFlags flags (QModelIndex const& = QModelIndex {}) const override;
 | |
|   QVariant data (QModelIndex const&, int role = Qt::DisplayRole) const override;
 | |
|   bool setData (QModelIndex const&, QVariant const& value, int role = Qt::EditRole) override;
 | |
|   QVariant headerData (int section, Qt::Orientation, int = Qt::DisplayRole) const override;
 | |
|   bool removeRows (int row, int count, QModelIndex const& parent = QModelIndex {}) override;
 | |
|   bool insertRows (int row, int count, QModelIndex const& parent = QModelIndex {}) override;
 | |
|   QStringList mimeTypes () const override;
 | |
|   QMimeData * mimeData (QModelIndexList const&) const override;
 | |
| 
 | |
|   static int constexpr num_cols {3};
 | |
|   static auto constexpr mime_type ="application/wsjt.Frequencies";
 | |
| 
 | |
|   Bands const * bands_;
 | |
|   FrequencyItems frequency_list_;
 | |
|   Mode mode_filter_;
 | |
| };
 | |
| 
 | |
| FrequencyList::FrequencyList (Bands const * bands, QObject * parent)
 | |
|   : QSortFilterProxyModel {parent}
 | |
|   , m_ {bands, parent}
 | |
| {
 | |
|   setSourceModel (&*m_);
 | |
|   setSortRole (SortRole);
 | |
| }
 | |
| 
 | |
| FrequencyList::~FrequencyList ()
 | |
| {
 | |
| }
 | |
| 
 | |
| auto FrequencyList::frequency_list (FrequencyItems frequency_list) -> FrequencyItems
 | |
| {
 | |
|   return m_->frequency_list (frequency_list);
 | |
| }
 | |
| 
 | |
| auto FrequencyList::frequency_list () const -> FrequencyItems const&
 | |
| {
 | |
|   return m_->frequency_list_;
 | |
| }
 | |
| 
 | |
| int FrequencyList::best_working_frequency (Frequency f) const
 | |
| {
 | |
|   int result {-1};
 | |
|   auto const& target_band = m_->bands_->find (f);
 | |
|   if (!target_band.isEmpty ())
 | |
|     {
 | |
|       // find a frequency in the same band that is allowed
 | |
|       for (int row = 0; row < rowCount (); ++row)
 | |
|         {
 | |
|           auto const& source_row = mapToSource (index (row, 0)).row ();
 | |
|           auto const& band = m_->bands_->find (m_->frequency_list_[source_row].frequency_);
 | |
|           if (band == target_band)
 | |
|             {
 | |
|               return row;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| int FrequencyList::best_working_frequency (QString const& target_band) const
 | |
| {
 | |
|   int result {-1};
 | |
|   if (!target_band.isEmpty ())
 | |
|     {
 | |
|       // find a frequency in the same band that is allowed
 | |
|       for (int row = 0; row < rowCount (); ++row)
 | |
|         {
 | |
|           auto const& source_row = mapToSource (index (row, 0)).row ();
 | |
|           auto const& band = m_->bands_->find (m_->frequency_list_[source_row].frequency_);
 | |
|           if (band == target_band)
 | |
|             {
 | |
|               return row;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| void FrequencyList::reset_to_defaults ()
 | |
| {
 | |
|   m_->frequency_list (default_frequency_list);
 | |
| }
 | |
| 
 | |
| QModelIndex FrequencyList::add (Item f)
 | |
| {
 | |
|   return mapFromSource (m_->add (f));
 | |
| }
 | |
| 
 | |
| bool FrequencyList::remove (Item f)
 | |
| {
 | |
|   auto row = m_->frequency_list_.indexOf (f);
 | |
| 
 | |
|   if (0 > row)
 | |
|     {
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|   return m_->removeRow (row);
 | |
| }
 | |
| 
 | |
| namespace
 | |
| {
 | |
|   bool row_is_higher (QModelIndex const& lhs, QModelIndex const& rhs)
 | |
|   {
 | |
|     return lhs.row () > rhs.row ();
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool FrequencyList::removeDisjointRows (QModelIndexList rows)
 | |
| {
 | |
|   bool result {true};
 | |
| 
 | |
|   // 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.
 | |
|   for (int r = 0; r < rows.size (); ++r)
 | |
|     {
 | |
|       rows[r] = mapToSource (rows[r]);
 | |
|     }
 | |
| 
 | |
|   // reverse sort by row
 | |
|   qSort (rows.begin (), rows.end (), row_is_higher);
 | |
|   Q_FOREACH (auto index, rows)
 | |
|     {
 | |
|       if (result && !m_->removeRow (index.row ()))
 | |
|         {
 | |
|           result = false;
 | |
|         }
 | |
|     }
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| void FrequencyList::filter (Mode mode)
 | |
| {
 | |
|   m_->mode_filter_ = mode;
 | |
|   invalidateFilter ();
 | |
| }
 | |
| 
 | |
| bool FrequencyList::filterAcceptsRow (int source_row, QModelIndex const& /* parent */) const
 | |
| {
 | |
|   bool result {true};
 | |
|   if (m_->mode_filter_ != Modes::NULL_MODE)
 | |
|     {
 | |
|       auto const& item = m_->frequency_list_[source_row];
 | |
|       result = item.mode_ == Modes::NULL_MODE || m_->mode_filter_ == item.mode_;
 | |
|     }
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| 
 | |
| auto FrequencyList::impl::frequency_list (FrequencyItems frequency_list) -> FrequencyItems
 | |
| {
 | |
|   beginResetModel ();
 | |
|   std::swap (frequency_list_, frequency_list);
 | |
|   endResetModel ();
 | |
|   return frequency_list;
 | |
| }
 | |
| 
 | |
| QModelIndex FrequencyList::impl::add (Item f)
 | |
| {
 | |
|   // Any Frequency that isn't in the list may be added
 | |
|   if (!frequency_list_.contains (f))
 | |
|     {
 | |
|       auto row = frequency_list_.size ();
 | |
| 
 | |
|       beginInsertRows (QModelIndex {}, row, row);
 | |
|       frequency_list_.append (f);
 | |
|       endInsertRows ();
 | |
| 
 | |
|       return index (row, 0);
 | |
|     }
 | |
|   return QModelIndex {};
 | |
| }
 | |
| 
 | |
| int FrequencyList::impl::rowCount (QModelIndex const& parent) const
 | |
| {
 | |
|   return parent.isValid () ? 0 : frequency_list_.size ();
 | |
| }
 | |
| 
 | |
| int FrequencyList::impl::columnCount (QModelIndex const& parent) const
 | |
| {
 | |
|   return parent.isValid () ? 0 : num_cols;
 | |
| }
 | |
| 
 | |
| Qt::ItemFlags FrequencyList::impl::flags (QModelIndex const& index) const
 | |
| {
 | |
|   auto result = QAbstractTableModel::flags (index) | Qt::ItemIsDropEnabled;
 | |
|   auto row = index.row ();
 | |
|   auto column = index.column ();
 | |
|   if (index.isValid ()
 | |
|       && row < frequency_list_.size ()
 | |
|       && column < num_cols)
 | |
|     {
 | |
|       if (frequency_mhz_column != column)
 | |
|         {
 | |
|           result |= Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
 | |
|         }
 | |
|     }
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| QVariant FrequencyList::impl::data (QModelIndex const& index, int role) const
 | |
| {
 | |
|   QVariant item;
 | |
| 
 | |
|   auto const& row = index.row ();
 | |
|   auto const& column = index.column ();
 | |
| 
 | |
|   if (index.isValid ()
 | |
|       && row < frequency_list_.size ()
 | |
|       && column < num_cols)
 | |
|     {
 | |
|       auto const& frequency_item = frequency_list_.at (row);
 | |
|       switch (column)
 | |
|         {
 | |
|         case mode_column:
 | |
|           switch (role)
 | |
|             {
 | |
|             case SortRole:
 | |
|             case Qt::DisplayRole:
 | |
|             case Qt::EditRole:
 | |
|             case Qt::AccessibleTextRole:
 | |
|               item = Modes::name (frequency_item.mode_);
 | |
|               break;
 | |
| 
 | |
|             case Qt::ToolTipRole:
 | |
|             case Qt::AccessibleDescriptionRole:
 | |
|               item = tr ("Mode");
 | |
|               break;
 | |
| 
 | |
|             case Qt::TextAlignmentRole:
 | |
|               item = Qt::AlignHCenter + Qt::AlignVCenter;
 | |
|               break;
 | |
|             }
 | |
|           break;
 | |
| 
 | |
|         case frequency_column:
 | |
|           switch (role)
 | |
|             {
 | |
|             case SortRole:
 | |
|             case Qt::EditRole:
 | |
|             case Qt::AccessibleTextRole:
 | |
|               item = frequency_item.frequency_;
 | |
|               break;
 | |
| 
 | |
|             case Qt::DisplayRole:
 | |
|               {
 | |
|                 auto const& band = bands_->find (frequency_item.frequency_);
 | |
|                 item = Radio::pretty_frequency_MHz_string (frequency_item.frequency_)
 | |
|                   + " MHz (" + (band.isEmpty () ? "OOB" : band) + ')';
 | |
|               }
 | |
|               break;
 | |
| 
 | |
|             case Qt::ToolTipRole:
 | |
|             case Qt::AccessibleDescriptionRole:
 | |
|               item = tr ("Frequency");
 | |
|               break;
 | |
| 
 | |
|             case Qt::TextAlignmentRole:
 | |
|               item = Qt::AlignRight + Qt::AlignVCenter;
 | |
|               break;
 | |
|             }
 | |
|           break;
 | |
| 
 | |
|         case frequency_mhz_column:
 | |
|           switch (role)
 | |
|             {
 | |
|             case Qt::EditRole:
 | |
|             case Qt::AccessibleTextRole:
 | |
|               item = Radio::frequency_MHz_string (frequency_item.frequency_);
 | |
|               break;
 | |
| 
 | |
|             case Qt::DisplayRole:
 | |
|               {
 | |
|                 auto const& band = bands_->find (frequency_item.frequency_);
 | |
|                 item = Radio::pretty_frequency_MHz_string (frequency_item.frequency_)
 | |
|                   + " MHz (" + (band.isEmpty () ? "OOB" : band) + ')';
 | |
|               }
 | |
|               break;
 | |
| 
 | |
|             case Qt::ToolTipRole:
 | |
|             case Qt::AccessibleDescriptionRole:
 | |
|               item = tr ("Frequency (MHz)");
 | |
|               break;
 | |
| 
 | |
|             case Qt::TextAlignmentRole:
 | |
|               item = Qt::AlignRight + Qt::AlignVCenter;
 | |
|               break;
 | |
|             }
 | |
|           break;
 | |
|         }
 | |
|     }
 | |
|   return item;
 | |
| }
 | |
| 
 | |
| bool FrequencyList::impl::setData (QModelIndex const& model_index, QVariant const& value, int role)
 | |
| {
 | |
|   bool changed {false};
 | |
| 
 | |
|   auto const& row = model_index.row ();
 | |
|   if (model_index.isValid ()
 | |
|       && Qt::EditRole == role
 | |
|       && row < frequency_list_.size ())
 | |
|     {
 | |
|       QVector<int> roles;
 | |
|       roles << role;
 | |
| 
 | |
|       auto& item = frequency_list_[row];
 | |
|       switch (model_index.column ())
 | |
|         {
 | |
|         case mode_column:
 | |
|           if (value.canConvert<Mode> ())
 | |
|             {
 | |
|               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> ())
 | |
|             {
 | |
|               auto frequency = value.value<Frequency> ();
 | |
|               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;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|   return changed;
 | |
| }
 | |
| 
 | |
| QVariant FrequencyList::impl::headerData (int section, Qt::Orientation orientation, int role) const
 | |
| {
 | |
|   QVariant header;
 | |
|   if (Qt::DisplayRole == role
 | |
|       && Qt::Horizontal == orientation
 | |
|       && section < num_cols)
 | |
|     {
 | |
|       switch (section)
 | |
|         {
 | |
|         case mode_column: header = tr ("Mode"); break;
 | |
|         case frequency_column: header = tr ("Frequency"); break;
 | |
|         case frequency_mhz_column: header = tr ("Frequency (MHz)"); break;
 | |
|         }
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       header = QAbstractTableModel::headerData (section, orientation, role);
 | |
|     }
 | |
|   return header;
 | |
| }
 | |
| 
 | |
| bool FrequencyList::impl::removeRows (int row, int count, QModelIndex const& parent)
 | |
| {
 | |
|   if (0 < count && (row + count) <= rowCount (parent))
 | |
|     {
 | |
|       beginRemoveRows (parent, row, row + count - 1);
 | |
|       for (auto r = 0; r < count; ++r)
 | |
|         {
 | |
|           frequency_list_.removeAt (row);
 | |
|         }
 | |
|       endRemoveRows ();
 | |
|       return true;
 | |
|     }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| bool FrequencyList::impl::insertRows (int row, int count, QModelIndex const& parent)
 | |
| {
 | |
|   if (0 < count)
 | |
|     {
 | |
|       beginInsertRows (parent, row, row + count - 1);
 | |
|       for (auto r = 0; r < count; ++r)
 | |
|         {
 | |
|           frequency_list_.insert (row, Item {0, Mode::NULL_MODE});
 | |
|         }
 | |
|       endInsertRows ();
 | |
|       return true;
 | |
|     }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| QStringList FrequencyList::impl::mimeTypes () const
 | |
| {
 | |
|   QStringList types;
 | |
|   types << mime_type;
 | |
|   return types;
 | |
| }
 | |
| 
 | |
| QMimeData * FrequencyList::impl::mimeData (QModelIndexList const& items) const
 | |
| {
 | |
|   QMimeData * mime_data = new QMimeData {};
 | |
|   QByteArray encoded_data;
 | |
|   QDataStream stream {&encoded_data, QIODevice::WriteOnly};
 | |
| 
 | |
|   Q_FOREACH (auto const& item, items)
 | |
|     {
 | |
|       if (item.isValid () && frequency_column == item.column ())
 | |
|         {
 | |
|           stream << frequency_list_.at (item.row ());
 | |
|         }
 | |
|     }
 | |
| 
 | |
|   mime_data->setData (mime_type, encoded_data);
 | |
|   return mime_data;
 | |
| }
 | |
| 
 | |
| auto FrequencyList::const_iterator::operator * () -> Item const&
 | |
| {
 | |
|   return parent_->frequency_list ().at(parent_->mapToSource (parent_->index (row_, 0)).row ());
 | |
| }
 | |
| 
 | |
| bool FrequencyList::const_iterator::operator != (const_iterator const& rhs) const
 | |
| {
 | |
|   return parent_ != rhs.parent_ || row_ != rhs.row_;
 | |
| }
 | |
| 
 | |
| auto FrequencyList::const_iterator::operator ++ () -> const_iterator&
 | |
| {
 | |
|   ++row_;
 | |
|   return *this;
 | |
| }
 | |
| 
 | |
| auto FrequencyList::begin () const -> FrequencyList::const_iterator
 | |
| {
 | |
|   return const_iterator (this, 0);
 | |
| }
 | |
| 
 | |
| auto FrequencyList::end () const -> FrequencyList::const_iterator
 | |
| {
 | |
|   return const_iterator (this, rowCount ());
 | |
| }
 | |
| 
 | |
| auto FrequencyList::filtered_bands () const -> BandSet
 | |
| {
 | |
|   BandSet result;
 | |
|   for (auto const& item : *this)
 | |
|     {
 | |
|       result << m_->bands_->find (item.frequency_);
 | |
|     }
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| auto FrequencyList::all_bands (Mode mode) const -> BandSet
 | |
| {
 | |
|   BandSet result;
 | |
|   for (auto const& item : m_->frequency_list_)
 | |
|     {
 | |
|       if (mode == Modes::NULL_MODE || item.mode_ == mode)
 | |
|         {
 | |
|           result << m_->bands_->find (item.frequency_);
 | |
|         }
 | |
|     }
 | |
|   return result;
 | |
| }
 |