mirror of
https://github.com/saitohirga/WSJT-X.git
synced 2024-11-25 13:48:42 -05:00
94cec5bdc1
Working frequencies can be for all regions or for individual IARU regions. This allows each mode band tuple to have one or more working frequencies which can include local ones only offered when the user configures their IARU region. Change working frequency default suggestions to better fit in FT8. General rule is FT8 is at JT65 -2kHz except where that is not possible e.g. where that would fall into a segment not allocated for narrow band data modes. For tight bands like top band sqeeze existing JT65 and JT allocations to allow space for FT8. NOTE: this change changes the WSPR frequency on 80m to allow access to JA stations that currently have no allocation where it was placed. ALSO NOTE: the JT65 and JT9 frequencies for 80m move down 6 kHz, again to accommodate region 3 users. Other appliactions not within our control should be asked to move in step when 1.8.0 is released. git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@7810 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
801 lines
23 KiB
C++
801 lines
23 KiB
C++
#include "FrequencyList.hpp"
|
|
|
|
#include <cstdlib>
|
|
#include <utility>
|
|
#include <limits>
|
|
|
|
#include <QMetaType>
|
|
#include <QAbstractTableModel>
|
|
#include <QString>
|
|
#include <QList>
|
|
#include <QListIterator>
|
|
#include <QVector>
|
|
#include <QStringList>
|
|
#include <QMimeData>
|
|
#include <QDataStream>
|
|
#include <QByteArray>
|
|
#include <QDebugStateSaver>
|
|
|
|
#include "Radio.hpp"
|
|
#include "Bands.hpp"
|
|
#include "pimpl_impl.hpp"
|
|
|
|
#include "moc_FrequencyList.cpp"
|
|
|
|
namespace
|
|
{
|
|
FrequencyList::FrequencyItems const default_frequency_list =
|
|
{
|
|
{198000, Modes::FreqCal, IARURegions::R1}, // BBC Radio 4 Droitwich
|
|
{4996000, Modes::FreqCal, IARURegions::R1}, // RWM time signal
|
|
{9996000, Modes::FreqCal, IARURegions::R1}, // RWM time signal
|
|
{14996000, Modes::FreqCal, IARURegions::R1}, // RWM time signal
|
|
|
|
{660000, Modes::FreqCal, IARURegions::R2},
|
|
{880000, Modes::FreqCal, IARURegions::R2},
|
|
{1210000, Modes::FreqCal, IARURegions::R2},
|
|
|
|
{2500000, Modes::FreqCal, IARURegions::ALL},
|
|
{3330000, Modes::FreqCal, IARURegions::ALL},
|
|
{5000000, Modes::FreqCal, IARURegions::ALL},
|
|
{7850000, Modes::FreqCal, IARURegions::ALL},
|
|
{10000000, Modes::FreqCal, IARURegions::ALL},
|
|
{14670000, Modes::FreqCal, IARURegions::ALL},
|
|
{15000000, Modes::FreqCal, IARURegions::ALL},
|
|
{20000000, Modes::FreqCal, IARURegions::ALL},
|
|
|
|
{136000, Modes::WSPR, IARURegions::ALL},
|
|
{136130, Modes::JT65, IARURegions::ALL},
|
|
{136130, Modes::JT9, IARURegions::ALL},
|
|
|
|
{474200, Modes::JT65, IARURegions::ALL},
|
|
{474200, Modes::JT9, IARURegions::ALL},
|
|
{474200, Modes::WSPR, IARURegions::ALL},
|
|
|
|
{1836600, Modes::WSPR, IARURegions::ALL},
|
|
{1838000, Modes::JT65, IARURegions::ALL}, // squeezed allocations
|
|
{1839000, Modes::JT9, IARURegions::ALL},
|
|
{1840000, Modes::FT8, IARURegions::ALL},
|
|
|
|
{3570000, Modes::JT65, IARURegions::ALL}, // JA compatible
|
|
{3572000, Modes::JT9, IARURegions::ALL},
|
|
{3573000, Modes::FT8, IARURegions::ALL}, // above as below JT65
|
|
// is out of DM allocation
|
|
{3572600, Modes::WSPR, IARURegions::ALL}, // needs guard marker
|
|
// and lock out
|
|
|
|
{7038600, Modes::WSPR, IARURegions::ALL},
|
|
{7074000, Modes::FT8, IARURegions::ALL},
|
|
{7076000, Modes::JT65, IARURegions::ALL},
|
|
{7078000, Modes::JT9, IARURegions::ALL},
|
|
|
|
{10136000, Modes::FT8, IARURegions::ALL},
|
|
{10138000, Modes::JT65, IARURegions::ALL},
|
|
{10138700, Modes::WSPR, IARURegions::ALL},
|
|
{10140000, Modes::JT9, IARURegions::ALL},
|
|
|
|
{14095600, Modes::WSPR, IARURegions::ALL},
|
|
{14074000, Modes::FT8, IARURegions::ALL},
|
|
{14076000, Modes::JT65, IARURegions::ALL},
|
|
{14078000, Modes::JT9, IARURegions::ALL},
|
|
|
|
{18100000, Modes::FT8, IARURegions::ALL},
|
|
{18102000, Modes::JT65, IARURegions::ALL},
|
|
{18104000, Modes::JT9, IARURegions::ALL},
|
|
{18104600, Modes::WSPR, IARURegions::ALL},
|
|
|
|
{21074000, Modes::FT8, IARURegions::ALL},
|
|
{21076000, Modes::JT65, IARURegions::ALL},
|
|
{21078000, Modes::JT9, IARURegions::ALL},
|
|
{21094600, Modes::WSPR, IARURegions::ALL},
|
|
|
|
{24915000, Modes::FT8, IARURegions::ALL},
|
|
{24917000, Modes::JT65, IARURegions::ALL},
|
|
{24919000, Modes::JT9, IARURegions::ALL},
|
|
{24924600, Modes::WSPR, IARURegions::ALL},
|
|
|
|
{28074000, Modes::FT8, IARURegions::ALL},
|
|
{28076000, Modes::JT65, IARURegions::ALL},
|
|
{28078000, Modes::JT9, IARURegions::ALL},
|
|
{28124600, Modes::WSPR, IARURegions::ALL},
|
|
|
|
{50000000, Modes::Echo, IARURegions::ALL},
|
|
{50276000, Modes::JT65, IARURegions::R2},
|
|
{50276000, Modes::JT65, IARURegions::R3},
|
|
{50260000, Modes::MSK144, IARURegions::R2},
|
|
{50260000, Modes::MSK144, IARURegions::R3},
|
|
{50293000, Modes::WSPR, IARURegions::R2},
|
|
{50293000, Modes::WSPR, IARURegions::R3},
|
|
{50310000, Modes::JT65, IARURegions::ALL},
|
|
{50312000, Modes::JT9, IARURegions::ALL},
|
|
{50313000, Modes::FT8, IARURegions::ALL},
|
|
{50360000, Modes::MSK144, IARURegions::R1},
|
|
|
|
{70100000, Modes::FT8, IARURegions::R1},
|
|
{70102000, Modes::JT65, IARURegions::R1},
|
|
{70104000, Modes::JT9, IARURegions::R1},
|
|
{70091000, Modes::WSPR, IARURegions::R1},
|
|
{70230000, Modes::MSK144, IARURegions::R1},
|
|
|
|
{144000000, Modes::Echo, IARURegions::ALL},
|
|
{144120000, Modes::JT65, IARURegions::ALL},
|
|
{144120000, Modes::Echo, IARURegions::ALL},
|
|
{144360000, Modes::MSK144, IARURegions::R1},
|
|
{144150000, Modes::MSK144, IARURegions::R2},
|
|
{144489000, Modes::WSPR, IARURegions::ALL},
|
|
|
|
{222065000, Modes::Echo, IARURegions::R2},
|
|
{222065000, Modes::JT65, IARURegions::R2},
|
|
|
|
{432065000, Modes::Echo, IARURegions::ALL},
|
|
{432065000, Modes::JT65, IARURegions::ALL},
|
|
{432300000, Modes::WSPR, IARURegions::ALL},
|
|
{432360000, Modes::MSK144, IARURegions::ALL},
|
|
|
|
{902065000, Modes::JT65, IARURegions::ALL},
|
|
|
|
{1296065000, Modes::Echo, IARURegions::ALL},
|
|
{1296065000, Modes::JT65, IARURegions::ALL},
|
|
{1296500000, Modes::WSPR, IARURegions::ALL},
|
|
|
|
{2301000000, Modes::Echo, IARURegions::ALL},
|
|
{2301065000, Modes::JT4, IARURegions::ALL},
|
|
{2301065000, Modes::JT65, IARURegions::ALL},
|
|
|
|
{2304065000, Modes::Echo, IARURegions::ALL},
|
|
{2304065000, Modes::JT4, IARURegions::ALL},
|
|
{2304065000, Modes::JT65, IARURegions::ALL},
|
|
|
|
{2320065000, Modes::Echo, IARURegions::ALL},
|
|
{2320065000, Modes::JT4, IARURegions::ALL},
|
|
{2320065000, Modes::JT65, IARURegions::ALL},
|
|
|
|
{3400065000, Modes::Echo, IARURegions::ALL},
|
|
{3400065000, Modes::JT4, IARURegions::ALL},
|
|
{3400065000, Modes::JT65, IARURegions::ALL},
|
|
|
|
{3456065000, Modes::Echo, IARURegions::ALL},
|
|
{3456065000, Modes::JT4, IARURegions::ALL},
|
|
{3456065000, Modes::JT65, IARURegions::ALL},
|
|
|
|
{5760065000, Modes::Echo, IARURegions::ALL},
|
|
{5760065000, Modes::JT4, IARURegions::ALL},
|
|
{5760065000, Modes::JT65, IARURegions::ALL},
|
|
|
|
{10368100000, Modes::Echo, IARURegions::ALL},
|
|
{10368100000, Modes::JT4, IARURegions::ALL},
|
|
{10368100000, Modes::JT65, IARURegions::ALL},
|
|
|
|
{24048100000, Modes::Echo, IARURegions::ALL},
|
|
{24048100000, Modes::JT4, IARURegions::ALL},
|
|
{24048100000, Modes::JT65, IARURegions::ALL},
|
|
};
|
|
}
|
|
|
|
#if !defined (QT_NO_DEBUG_STREAM)
|
|
QDebug operator << (QDebug debug, FrequencyList::Item const& item)
|
|
{
|
|
QDebugStateSaver saver {debug};
|
|
debug.nospace () << "FrequencyItem("
|
|
<< item.frequency_ << ", "
|
|
<< item.region_ << ", "
|
|
<< item.mode_ << ')';
|
|
return debug;
|
|
}
|
|
#endif
|
|
|
|
QDataStream& operator << (QDataStream& os, FrequencyList::Item const& item)
|
|
{
|
|
return os << item.frequency_
|
|
<< item.mode_
|
|
<< item.region_;
|
|
}
|
|
|
|
QDataStream& operator >> (QDataStream& is, FrequencyList::Item& item)
|
|
{
|
|
return is >> item.frequency_
|
|
>> item.mode_
|
|
>> item.region_;
|
|
}
|
|
|
|
class FrequencyList::impl final
|
|
: public QAbstractTableModel
|
|
{
|
|
public:
|
|
impl (Bands const * bands, QObject * parent)
|
|
: QAbstractTableModel {parent}
|
|
, bands_ {bands}
|
|
, region_filter_ {IARURegions::ALL}
|
|
, mode_filter_ {Modes::ALL}
|
|
{
|
|
}
|
|
|
|
FrequencyItems frequency_list (FrequencyItems);
|
|
QModelIndex add (Item);
|
|
void add (FrequencyItems);
|
|
|
|
// 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 {SENTINAL};
|
|
static auto constexpr mime_type = "application/wsjt.Frequencies";
|
|
|
|
Bands const * bands_;
|
|
FrequencyItems frequency_list_;
|
|
Region region_filter_;
|
|
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_;
|
|
}
|
|
|
|
auto FrequencyList::frequency_list (QModelIndexList const& model_index_list) const -> FrequencyItems
|
|
{
|
|
FrequencyItems list;
|
|
Q_FOREACH (auto const& index, model_index_list)
|
|
{
|
|
list << m_->frequency_list_[mapToSource (index).row ()];
|
|
}
|
|
return list;
|
|
}
|
|
|
|
void FrequencyList::frequency_list_merge (FrequencyItems const& items)
|
|
{
|
|
m_->add (items);
|
|
}
|
|
|
|
int FrequencyList::best_working_frequency (Frequency f) const
|
|
{
|
|
int result {-1};
|
|
auto const& target_band = m_->bands_->find (f);
|
|
if (!target_band.isEmpty ())
|
|
{
|
|
Radio::FrequencyDelta delta {std::numeric_limits<Radio::FrequencyDelta>::max ()};
|
|
// 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& candidate_frequency = m_->frequency_list_[source_row].frequency_;
|
|
auto const& band = m_->bands_->find (candidate_frequency);
|
|
if (band == target_band)
|
|
{
|
|
// take closest band match
|
|
Radio::FrequencyDelta new_delta = f - candidate_frequency;
|
|
if (std::abs (new_delta) < std::abs (delta))
|
|
{
|
|
delta = new_delta;
|
|
result = 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 (Region region, Mode mode)
|
|
{
|
|
m_->region_filter_ = region;
|
|
m_->mode_filter_ = mode;
|
|
invalidateFilter ();
|
|
}
|
|
|
|
bool FrequencyList::filterAcceptsRow (int source_row, QModelIndex const& /* parent */) const
|
|
{
|
|
bool result {true};
|
|
auto const& item = m_->frequency_list_[source_row];
|
|
if (m_->region_filter_ != IARURegions::ALL)
|
|
{
|
|
result = IARURegions::ALL == item.region_ || m_->region_filter_ == item.region_;
|
|
}
|
|
if (result && m_->mode_filter_ != Modes::ALL)
|
|
{
|
|
// we pass ALL mode rows unless filtering for FreqCal mode
|
|
result = (Modes::ALL == item.mode_ && m_->mode_filter_ != Modes::FreqCal)
|
|
|| 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 {};
|
|
}
|
|
|
|
void FrequencyList::impl::add (FrequencyItems items)
|
|
{
|
|
// Any Frequency that isn't in the list may be added
|
|
for (auto p = items.begin (); p != items.end ();)
|
|
{
|
|
if (frequency_list_.contains (*p))
|
|
{
|
|
p = items.erase (p);
|
|
}
|
|
else
|
|
{
|
|
++p;
|
|
}
|
|
}
|
|
|
|
if (items.size ())
|
|
{
|
|
auto row = frequency_list_.size ();
|
|
|
|
beginInsertRows (QModelIndex {}, row, row + items.size () - 1);
|
|
frequency_list_.append (items);
|
|
endInsertRows ();
|
|
}
|
|
}
|
|
|
|
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 region_column:
|
|
switch (role)
|
|
{
|
|
case SortRole:
|
|
case Qt::DisplayRole:
|
|
case Qt::EditRole:
|
|
case Qt::AccessibleTextRole:
|
|
item = IARURegions::name (frequency_item.region_);
|
|
break;
|
|
|
|
case Qt::ToolTipRole:
|
|
case Qt::AccessibleDescriptionRole:
|
|
item = tr ("IARU Region");
|
|
break;
|
|
|
|
case Qt::TextAlignmentRole:
|
|
item = Qt::AlignHCenter + Qt::AlignVCenter;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
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 region_column:
|
|
if (value.canConvert<Region> ())
|
|
{
|
|
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:
|
|
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> ())
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
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 region_column: header = tr ("IARU Region"); break;
|
|
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::ALL, IARURegions::ALL});
|
|
}
|
|
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 * () const -> Item const&
|
|
{
|
|
return parent_->frequency_list ().at(parent_->mapToSource (parent_->index (row_, 0)).row ());
|
|
}
|
|
|
|
auto FrequencyList::const_iterator::operator -> () const -> 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_;
|
|
}
|
|
|
|
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 -> const_iterator
|
|
{
|
|
return const_iterator (this, 0);
|
|
}
|
|
|
|
auto FrequencyList::end () const -> const_iterator
|
|
{
|
|
return const_iterator (this, rowCount ());
|
|
}
|
|
|
|
auto FrequencyList::find (Frequency f) const -> const_iterator
|
|
{
|
|
int row {0};
|
|
for (; row < rowCount (); ++row)
|
|
{
|
|
if (m_->frequency_list_[mapToSource (index (row, 0)).row ()].frequency_ == f)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
return const_iterator (this, row);
|
|
}
|
|
|
|
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 (Region region, Mode mode) const -> BandSet
|
|
{
|
|
BandSet result;
|
|
for (auto const& item : m_->frequency_list_)
|
|
{
|
|
if (region == IARURegions::ALL || item.region_ == region
|
|
|| mode == Modes::ALL || item.mode_ == mode)
|
|
{
|
|
result << m_->bands_->find (item.frequency_);
|
|
}
|
|
}
|
|
return result;
|
|
}
|