mirror of
				https://github.com/saitohirga/WSJT-X.git
				synced 2025-11-03 21:40:52 -05:00 
			
		
		
		
	Due to what appears to be a Qt bug, any in progress user edit to a table fields is not updated in the underlying data models until QDialog::accept() is called, this means that model validation before calling QDialog::accept() is tricky. git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@5164 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
		
			
				
	
	
		
			358 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			358 lines
		
	
	
		
			8.3 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 "pimpl_impl.hpp"
 | 
						|
 | 
						|
class FrequencyList::impl final
 | 
						|
  : public QAbstractTableModel
 | 
						|
{
 | 
						|
public:
 | 
						|
  impl (Frequencies frequencies, QObject * parent)
 | 
						|
    : QAbstractTableModel {parent}
 | 
						|
    , frequencies_ {frequencies}
 | 
						|
  {
 | 
						|
  }
 | 
						|
 | 
						|
  Frequencies const& frequencies () const {return frequencies_;}
 | 
						|
  void assign (Frequencies);
 | 
						|
  QModelIndex add (Frequency);
 | 
						|
 | 
						|
protected:
 | 
						|
  // 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;
 | 
						|
 | 
						|
private:
 | 
						|
  static int constexpr num_cols {2};
 | 
						|
  static auto constexpr mime_type ="application/wsjt.Frequencies";
 | 
						|
 | 
						|
  Frequencies frequencies_;
 | 
						|
};
 | 
						|
 | 
						|
FrequencyList::FrequencyList (QObject * parent)
 | 
						|
  : FrequencyList {{}, parent}
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
FrequencyList::FrequencyList (Frequencies frequencies, QObject * parent)
 | 
						|
  : QSortFilterProxyModel {parent}
 | 
						|
  , m_ {frequencies, parent}
 | 
						|
{
 | 
						|
  setSourceModel (&*m_);
 | 
						|
  setSortRole (SortRole);
 | 
						|
}
 | 
						|
 | 
						|
FrequencyList::~FrequencyList ()
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
FrequencyList& FrequencyList::operator = (Frequencies frequencies)
 | 
						|
{
 | 
						|
  m_->assign (frequencies);
 | 
						|
  return *this;
 | 
						|
}
 | 
						|
 | 
						|
auto FrequencyList::frequencies () const -> Frequencies
 | 
						|
{
 | 
						|
  return m_->frequencies ();
 | 
						|
}
 | 
						|
 | 
						|
QModelIndex FrequencyList::add (Frequency f)
 | 
						|
{
 | 
						|
  return mapFromSource (m_->add (f));
 | 
						|
}
 | 
						|
 | 
						|
bool FrequencyList::remove (Frequency f)
 | 
						|
{
 | 
						|
  auto row = m_->frequencies ().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::impl::assign (Frequencies frequencies)
 | 
						|
{
 | 
						|
  beginResetModel ();
 | 
						|
  std::swap (frequencies_, frequencies);
 | 
						|
  endResetModel ();
 | 
						|
}
 | 
						|
 | 
						|
QModelIndex FrequencyList::impl::add (Frequency f)
 | 
						|
{
 | 
						|
  // Any Frequency that isn't in the list may be added
 | 
						|
  if (!frequencies_.contains (f))
 | 
						|
    {
 | 
						|
      auto row = frequencies_.size ();
 | 
						|
 | 
						|
      beginInsertRows (QModelIndex {}, row, row);
 | 
						|
      frequencies_.append (f);
 | 
						|
      endInsertRows ();
 | 
						|
 | 
						|
      return index (row, 0);
 | 
						|
    }
 | 
						|
 | 
						|
  return QModelIndex {};
 | 
						|
}
 | 
						|
 | 
						|
int FrequencyList::impl::rowCount (QModelIndex const& parent) const
 | 
						|
{
 | 
						|
  return parent.isValid () ? 0 : frequencies_.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 < frequencies_.size ()
 | 
						|
      && column < num_cols)
 | 
						|
    {
 | 
						|
      switch (column)
 | 
						|
        {
 | 
						|
        case 0:
 | 
						|
          result |= Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
 | 
						|
          break;
 | 
						|
 | 
						|
        case 1:
 | 
						|
          result |= Qt::ItemIsDragEnabled;
 | 
						|
          break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
QVariant FrequencyList::impl::data (QModelIndex const& index, int role) const
 | 
						|
{
 | 
						|
  QVariant item;
 | 
						|
 | 
						|
  auto row = index.row ();
 | 
						|
  auto column = index.column ();
 | 
						|
 | 
						|
  if (index.isValid ()
 | 
						|
      && row < frequencies_.size ()
 | 
						|
      && column < num_cols)
 | 
						|
    {
 | 
						|
      auto frequency = frequencies_.at (row);
 | 
						|
 | 
						|
      switch (column)
 | 
						|
        {
 | 
						|
        case 0:
 | 
						|
          switch (role)
 | 
						|
            {
 | 
						|
            case SortRole:
 | 
						|
            case Qt::DisplayRole:
 | 
						|
            case Qt::EditRole:
 | 
						|
            case Qt::AccessibleTextRole:
 | 
						|
              item = frequency;
 | 
						|
              break;
 | 
						|
 | 
						|
            case Qt::ToolTipRole:
 | 
						|
            case Qt::AccessibleDescriptionRole:
 | 
						|
              item = tr ("Frequency");
 | 
						|
              break;
 | 
						|
 | 
						|
            case Qt::TextAlignmentRole:
 | 
						|
              item = Qt::AlignRight + Qt::AlignVCenter;
 | 
						|
              break;
 | 
						|
            }
 | 
						|
          break;
 | 
						|
 | 
						|
        case 1:
 | 
						|
          switch (role)
 | 
						|
            {
 | 
						|
            case Qt::DisplayRole:
 | 
						|
            case Qt::EditRole:
 | 
						|
            case Qt::AccessibleTextRole:
 | 
						|
              item = static_cast<double> (frequency / 1.e6);
 | 
						|
              break;
 | 
						|
 | 
						|
            case SortRole:	// use the underlying Frequency value
 | 
						|
              item = frequency;
 | 
						|
              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 row = model_index.row ();
 | 
						|
  if (model_index.isValid ()
 | 
						|
      && Qt::EditRole == role
 | 
						|
      && row < frequencies_.size ()
 | 
						|
      && 0 == model_index.column ()
 | 
						|
      && value.canConvert<Frequency> ())
 | 
						|
    {
 | 
						|
      auto frequency = value.value<Frequency> ();
 | 
						|
      auto original_frequency = frequencies_.at (row);
 | 
						|
      if (frequency != original_frequency)
 | 
						|
        {
 | 
						|
          frequencies_.replace (row, frequency);
 | 
						|
          Q_EMIT dataChanged (model_index, index (model_index.row (), 1), QVector<int> {} << role);
 | 
						|
        }
 | 
						|
      changed = true;
 | 
						|
    }
 | 
						|
 | 
						|
  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 0: header = tr ("Frequency"); break;
 | 
						|
        case 1: 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)
 | 
						|
        {
 | 
						|
          frequencies_.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)
 | 
						|
        {
 | 
						|
          frequencies_.insert (row, Frequency {});
 | 
						|
        }
 | 
						|
      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 ())
 | 
						|
        {
 | 
						|
          stream << QString {data (item, Qt::DisplayRole).toString ()};
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
  mime_data->setData (mime_type, encoded_data);
 | 
						|
  return mime_data;
 | 
						|
}
 |