mirror of
https://github.com/saitohirga/WSJT-X.git
synced 2024-11-23 20:58:55 -05:00
Ensure all model proxy caches are flushed before access
This fixes a defect where station detail changes are not saved. The Qt sort and filter proxy models utilize an item cache that must be flushed by callig submit() before accessing the underlying model if the proxy model has been used for updates. Also separated the item model candidate key filter from the implementation internals of the foreign key item delegate so that candidate key filtered models can be used directly as view models. Make the insert new station details band combo box use a candidate key filtered item model to avoid constraint violations. Constraint is zero or one station records per band. git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@5161 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
This commit is contained in:
parent
fdc02e34d4
commit
dad4863e84
@ -184,6 +184,7 @@ set (wsjt_qt_CXXSRCS
|
|||||||
StationList.cpp
|
StationList.cpp
|
||||||
FrequencyLineEdit.cpp
|
FrequencyLineEdit.cpp
|
||||||
FrequencyItemDelegate.cpp
|
FrequencyItemDelegate.cpp
|
||||||
|
CandidateKeyFilter.cpp
|
||||||
ForeignKeyDelegate.cpp
|
ForeignKeyDelegate.cpp
|
||||||
LiveFrequencyValidator.cpp
|
LiveFrequencyValidator.cpp
|
||||||
GetUserId.cpp
|
GetUserId.cpp
|
||||||
|
70
CandidateKeyFilter.cpp
Normal file
70
CandidateKeyFilter.cpp
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#include "CandidateKeyFilter.hpp"
|
||||||
|
|
||||||
|
#include <QModelIndex>
|
||||||
|
#include <QAbstractItemModel>
|
||||||
|
|
||||||
|
#include "pimpl_impl.hpp"
|
||||||
|
|
||||||
|
class CandidateKeyFilter::impl final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit impl (QAbstractItemModel const * referencing_model
|
||||||
|
, int referencing_key_column
|
||||||
|
, int referenced_key_column
|
||||||
|
, int referencing_key_role
|
||||||
|
, int referenced_key_role)
|
||||||
|
: referencing_ {referencing_model}
|
||||||
|
, referencing_key_column_ {referencing_key_column}
|
||||||
|
, referencing_key_role_ {referencing_key_role}
|
||||||
|
, referenced_key_column_ {referenced_key_column}
|
||||||
|
, referenced_key_role_ {referenced_key_role}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QAbstractItemModel const * referencing_;
|
||||||
|
int referencing_key_column_;
|
||||||
|
int referencing_key_role_;
|
||||||
|
int referenced_key_column_;
|
||||||
|
int referenced_key_role_;
|
||||||
|
QModelIndex active_key_;
|
||||||
|
};
|
||||||
|
|
||||||
|
CandidateKeyFilter::CandidateKeyFilter (QAbstractItemModel const * referencing_model
|
||||||
|
, QAbstractItemModel * referenced_model
|
||||||
|
, int referencing_key_column
|
||||||
|
, int referenced_key_column
|
||||||
|
, int referencing_key_role
|
||||||
|
, int referenced_key_role)
|
||||||
|
: QSortFilterProxyModel {nullptr} // ForeignKeyDelegate owns us
|
||||||
|
, m_ {referencing_model, referencing_key_column, referenced_key_column, referencing_key_role, referenced_key_role}
|
||||||
|
{
|
||||||
|
setSourceModel (referenced_model);
|
||||||
|
}
|
||||||
|
|
||||||
|
CandidateKeyFilter::~CandidateKeyFilter ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CandidateKeyFilter::set_active_key (QModelIndex const& index)
|
||||||
|
{
|
||||||
|
if (index.isValid () )
|
||||||
|
{
|
||||||
|
Q_ASSERT (index.column () == m_->referencing_key_column_);
|
||||||
|
m_->active_key_ = index;
|
||||||
|
}
|
||||||
|
invalidateFilter ();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CandidateKeyFilter::filterAcceptsRow (int candidate_row, QModelIndex const& candidate_parent) const
|
||||||
|
{
|
||||||
|
auto candidate_key = sourceModel ()->index (candidate_row, m_->referenced_key_column_, candidate_parent).data (m_->referenced_key_role_);
|
||||||
|
|
||||||
|
// Include the current key.
|
||||||
|
if (m_->active_key_.isValid () && candidate_key == m_->active_key_.data (m_->referencing_key_role_))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter out any candidates already in the referencing key rows.
|
||||||
|
return m_->referencing_->match (m_->referencing_->index (0, m_->referencing_key_column_), m_->referencing_key_role_, candidate_key, 1, Qt::MatchExactly).isEmpty ();
|
||||||
|
}
|
36
CandidateKeyFilter.hpp
Normal file
36
CandidateKeyFilter.hpp
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#ifndef CANDIDATE_KEY_FILTER_HPP_
|
||||||
|
#define CANDIDATE_KEY_FILTER_HPP_
|
||||||
|
|
||||||
|
#include <QSortFilterProxyModel>
|
||||||
|
#include <QModelIndex>
|
||||||
|
|
||||||
|
#include "pimpl_h.hpp"
|
||||||
|
|
||||||
|
class QAbstractItemModel;
|
||||||
|
|
||||||
|
class CandidateKeyFilter final
|
||||||
|
: public QSortFilterProxyModel
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit CandidateKeyFilter (QAbstractItemModel const * referencing_model
|
||||||
|
, QAbstractItemModel * referenced_model
|
||||||
|
, int referencing_key_column = 0
|
||||||
|
, int referenced_key_column = 0
|
||||||
|
, int referencing_key_role = Qt::EditRole
|
||||||
|
, int referenced_key_role = Qt::EditRole);
|
||||||
|
~CandidateKeyFilter ();
|
||||||
|
|
||||||
|
// this key is not to be filtered, usually because we want to allow
|
||||||
|
// it since we are editing the row that contains it this it is valid
|
||||||
|
// even though it is in use
|
||||||
|
void set_active_key (QModelIndex const& index = QModelIndex {});
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool filterAcceptsRow (int candidate_row, QModelIndex const& candidate_parent) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
class impl;
|
||||||
|
pimpl<impl> m_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -155,12 +155,14 @@
|
|||||||
#include <QFontDialog>
|
#include <QFontDialog>
|
||||||
#include <QColorDialog>
|
#include <QColorDialog>
|
||||||
#include <QSerialPortInfo>
|
#include <QSerialPortInfo>
|
||||||
|
#include <QScopedPointer>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
#include "qt_helpers.hpp"
|
#include "qt_helpers.hpp"
|
||||||
#include "SettingsGroup.hpp"
|
#include "SettingsGroup.hpp"
|
||||||
#include "FrequencyLineEdit.hpp"
|
#include "FrequencyLineEdit.hpp"
|
||||||
#include "FrequencyItemDelegate.hpp"
|
#include "FrequencyItemDelegate.hpp"
|
||||||
|
#include "CandidateKeyFilter.hpp"
|
||||||
#include "ForeignKeyDelegate.hpp"
|
#include "ForeignKeyDelegate.hpp"
|
||||||
#include "TransceiverFactory.hpp"
|
#include "TransceiverFactory.hpp"
|
||||||
#include "Transceiver.hpp"
|
#include "Transceiver.hpp"
|
||||||
@ -240,13 +242,13 @@ class StationDialog final
|
|||||||
: public QDialog
|
: public QDialog
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit StationDialog (Bands * bands, QWidget * parent = nullptr)
|
explicit StationDialog (StationList const * stations, Bands * bands, QWidget * parent = nullptr)
|
||||||
: QDialog {parent}
|
: QDialog {parent}
|
||||||
, bands_ {bands}
|
, filtered_bands_ {new CandidateKeyFilter {stations, bands}}
|
||||||
{
|
{
|
||||||
setWindowTitle (QApplication::applicationName () + " - " + tr ("Add Station"));
|
setWindowTitle (QApplication::applicationName () + " - " + tr ("Add Station"));
|
||||||
|
|
||||||
band_.setModel (bands_);
|
band_.setModel (filtered_bands_.data ());
|
||||||
|
|
||||||
auto form_layout = new QFormLayout ();
|
auto form_layout = new QFormLayout ();
|
||||||
form_layout->addRow (tr ("&Band:"), &band_);
|
form_layout->addRow (tr ("&Band:"), &band_);
|
||||||
@ -273,8 +275,14 @@ public:
|
|||||||
return {band_.currentText (), delta_.frequency_delta (), description_.text ()};
|
return {band_.currentText (), delta_.frequency_delta (), description_.text ()};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int exec () override
|
||||||
|
{
|
||||||
|
filtered_bands_->set_active_key ();
|
||||||
|
return QDialog::exec ();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Bands * bands_;
|
QScopedPointer<CandidateKeyFilter> filtered_bands_;
|
||||||
|
|
||||||
QComboBox band_;
|
QComboBox band_;
|
||||||
FrequencyDeltaLineEdit delta_;
|
FrequencyDeltaLineEdit delta_;
|
||||||
@ -724,7 +732,7 @@ Configuration::impl::impl (Configuration * self, QSettings * settings, QWidget *
|
|||||||
, stations_ {&bands_}
|
, stations_ {&bands_}
|
||||||
, next_stations_ {&bands_}
|
, next_stations_ {&bands_}
|
||||||
, frequency_dialog_ {new FrequencyDialog {this}}
|
, frequency_dialog_ {new FrequencyDialog {this}}
|
||||||
, station_dialog_ {new StationDialog {&bands_, this}}
|
, station_dialog_ {new StationDialog {&next_stations_, &bands_, this}}
|
||||||
, rig_active_ {false}
|
, rig_active_ {false}
|
||||||
, have_rig_ {false}
|
, have_rig_ {false}
|
||||||
, rig_changed_ {false}
|
, rig_changed_ {false}
|
||||||
@ -922,7 +930,7 @@ Configuration::impl::impl (Configuration * self, QSettings * settings, QWidget *
|
|||||||
ui_->stations_table_view->setModel (&next_stations_);
|
ui_->stations_table_view->setModel (&next_stations_);
|
||||||
ui_->stations_table_view->sortByColumn (0, Qt::AscendingOrder);
|
ui_->stations_table_view->sortByColumn (0, Qt::AscendingOrder);
|
||||||
ui_->stations_table_view->setColumnWidth (1, 150);
|
ui_->stations_table_view->setColumnWidth (1, 150);
|
||||||
ui_->stations_table_view->setItemDelegateForColumn (0, new ForeignKeyDelegate {&next_stations_, &bands_, 0, this});
|
ui_->stations_table_view->setItemDelegateForColumn (0, new ForeignKeyDelegate {&next_stations_, &bands_, 0, 0, this});
|
||||||
ui_->stations_table_view->setItemDelegateForColumn (1, new FrequencyDeltaItemDelegate {this});
|
ui_->stations_table_view->setItemDelegateForColumn (1, new FrequencyDeltaItemDelegate {this});
|
||||||
|
|
||||||
station_delete_action_ = new QAction {tr ("&Delete"), ui_->stations_table_view};
|
station_delete_action_ = new QAction {tr ("&Delete"), ui_->stations_table_view};
|
||||||
|
@ -1,63 +1,18 @@
|
|||||||
#include "ForeignKeyDelegate.hpp"
|
#include "ForeignKeyDelegate.hpp"
|
||||||
|
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
#include <QSortFilterProxyModel>
|
|
||||||
|
|
||||||
class CandidateKeyFilter final
|
#include "CandidateKeyFilter.hpp"
|
||||||
: public QSortFilterProxyModel
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit CandidateKeyFilter (QAbstractItemModel const * referencing_model
|
|
||||||
, QAbstractItemModel * referenced_model
|
|
||||||
, int referenced_key_column
|
|
||||||
, int referencing_key_role
|
|
||||||
, int referenced_key_role)
|
|
||||||
: QSortFilterProxyModel {nullptr} // ForeignKeyDelegate owns us
|
|
||||||
, referencing_ {referencing_model}
|
|
||||||
, referencing_key_role_ {referencing_key_role}
|
|
||||||
, referenced_key_column_ {referenced_key_column}
|
|
||||||
, referenced_key_role_ {referenced_key_role}
|
|
||||||
{
|
|
||||||
setSourceModel (referenced_model);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_active_key (QModelIndex const& index)
|
|
||||||
{
|
|
||||||
active_key_ = index;
|
|
||||||
invalidateFilter ();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool filterAcceptsRow (int candidate_row, QModelIndex const& candidate_parent) const override
|
|
||||||
{
|
|
||||||
auto candidate_key = sourceModel ()->index (candidate_row, referenced_key_column_, candidate_parent).data (referenced_key_role_);
|
|
||||||
|
|
||||||
// Include the current key.
|
|
||||||
if (candidate_key == active_key_.data (referencing_key_role_))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter out any candidates already in the referencing key rows.
|
|
||||||
return referencing_->match (referencing_->index (0, active_key_.column ()), referencing_key_role_, candidate_key, 1, Qt::MatchExactly).isEmpty ();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
QAbstractItemModel const * referencing_;
|
|
||||||
int referencing_key_role_;
|
|
||||||
int referenced_key_column_;
|
|
||||||
int referenced_key_role_;
|
|
||||||
QModelIndex active_key_;
|
|
||||||
};
|
|
||||||
|
|
||||||
ForeignKeyDelegate::ForeignKeyDelegate (QAbstractItemModel const * referencing_model
|
ForeignKeyDelegate::ForeignKeyDelegate (QAbstractItemModel const * referencing_model
|
||||||
, QAbstractItemModel * referenced_model
|
, QAbstractItemModel * referenced_model
|
||||||
|
, int referencing_key_column
|
||||||
, int referenced_key_column
|
, int referenced_key_column
|
||||||
, QObject * parent
|
, QObject * parent
|
||||||
, int referencing_key_role
|
, int referencing_key_role
|
||||||
, int referenced_key_role)
|
, int referenced_key_role)
|
||||||
: QStyledItemDelegate {parent}
|
: QStyledItemDelegate {parent}
|
||||||
, candidate_key_filter_ {new CandidateKeyFilter {referencing_model, referenced_model, referenced_key_column, referencing_key_role, referenced_key_role}}
|
, candidate_key_filter_ {new CandidateKeyFilter {referencing_model, referenced_model, referencing_key_column, referenced_key_column, referencing_key_role, referenced_key_role}}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ class ForeignKeyDelegate final
|
|||||||
public:
|
public:
|
||||||
explicit ForeignKeyDelegate (QAbstractItemModel const * referencing_model
|
explicit ForeignKeyDelegate (QAbstractItemModel const * referencing_model
|
||||||
, QAbstractItemModel * referenced_model
|
, QAbstractItemModel * referenced_model
|
||||||
|
, int referencing_key_column = 0
|
||||||
, int referenced_key_column = 0
|
, int referenced_key_column = 0
|
||||||
, QObject * parent = nullptr
|
, QObject * parent = nullptr
|
||||||
, int referencing_key_role = Qt::EditRole
|
, int referencing_key_role = Qt::EditRole
|
||||||
|
@ -73,8 +73,9 @@ FrequencyList& FrequencyList::operator = (Frequencies frequencies)
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto FrequencyList::frequencies () const -> Frequencies
|
auto FrequencyList::frequencies () -> Frequencies
|
||||||
{
|
{
|
||||||
|
submit ();
|
||||||
return m_->frequencies ();
|
return m_->frequencies ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ public:
|
|||||||
|
|
||||||
// Load and store contents
|
// Load and store contents
|
||||||
FrequencyList& operator = (Frequencies);
|
FrequencyList& operator = (Frequencies);
|
||||||
Frequencies frequencies () const;
|
Frequencies frequencies ();
|
||||||
|
|
||||||
// Model API
|
// Model API
|
||||||
QModelIndex add (Frequency);
|
QModelIndex add (Frequency);
|
||||||
|
@ -134,8 +134,9 @@ StationList& StationList::operator = (Stations stations)
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto StationList::stations () const -> Stations
|
auto StationList::stations () -> Stations
|
||||||
{
|
{
|
||||||
|
submit ();
|
||||||
return m_->stations ();
|
return m_->stations ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ public:
|
|||||||
|
|
||||||
// Load and store contents.
|
// Load and store contents.
|
||||||
StationList& operator = (Stations);
|
StationList& operator = (Stations);
|
||||||
Stations stations () const;
|
Stations stations ();
|
||||||
|
|
||||||
//
|
//
|
||||||
// Model API
|
// Model API
|
||||||
@ -83,14 +83,20 @@ private:
|
|||||||
pimpl<impl> m_;
|
pimpl<impl> m_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Station equivalence is based on band name alone.
|
// Station equivalence
|
||||||
inline
|
inline
|
||||||
bool operator == (StationList::Station const& lhs, StationList::Station const& rhs)
|
bool operator == (StationList::Station const& lhs, StationList::Station const& rhs)
|
||||||
{
|
{
|
||||||
return lhs.band_name_ == rhs.band_name_;
|
return lhs.band_name_ == rhs.band_name_
|
||||||
|
&& lhs.offset_ == rhs.offset_
|
||||||
|
&& lhs.antenna_description_ == rhs.antenna_description_;
|
||||||
}
|
}
|
||||||
|
|
||||||
Q_DECLARE_METATYPE (StationList::Station);
|
Q_DECLARE_METATYPE (StationList::Station);
|
||||||
Q_DECLARE_METATYPE (StationList::Stations);
|
Q_DECLARE_METATYPE (StationList::Stations);
|
||||||
|
|
||||||
|
#if !defined (QT_NO_DEBUG_STREAM)
|
||||||
|
QDebug operator << (QDebug debug, StationList::Station const&);
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user