Allow exposing QItemSelectionModel to QML

This requires being able to create a QItemSelectionModel
without specifiying its model, and also setting the model
later. Also, several classes, like QPersistentModelIndex,
need to be declared as meta-type.

Finally, and in order to introduce the 'model' property,
we need to have a type compatible getter. Hence the new,
non-const model() function.

Where needed, meta-type declarations have been removed from
auto-tests.

[ChangeLog][QtCore][Item Models] QItemSelectionModel
can now be created without a model and have one set later.

Change-Id: If49bed061a5d1012331f335ca7f6e3959ecd3f1c
Reviewed-by: Gabriel de Dietrich <gabriel.dedietrich@theqtcompany.com>
Reviewed-by: J-P Nurmi <jpnurmi@theqtcompany.com>
This commit is contained in:
Gabriel de Dietrich 2015-01-15 19:19:26 +01:00 committed by Caroline Chao
parent ae9d3f4c6c
commit 7ab29c4ebb
6 changed files with 160 additions and 39 deletions

View File

@ -481,4 +481,7 @@ inline uint qHash(const QModelIndex &index) Q_DECL_NOTHROW
QT_END_NAMESPACE
Q_DECLARE_METATYPE(QModelIndexList)
Q_DECLARE_METATYPE(QPersistentModelIndex)
#endif // QABSTRACTITEMMODEL_H

View File

@ -589,33 +589,51 @@ void QItemSelection::split(const QItemSelectionRange &range,
}
void QItemSelectionModelPrivate::initModel(QAbstractItemModel *model)
void QItemSelectionModelPrivate::initModel(QAbstractItemModel *m)
{
this->model = model;
struct Cx {
const char *signal;
const char *slot;
};
static const Cx connections[] = {
{ SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int)) },
{ SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
SLOT(_q_columnsAboutToBeRemoved(QModelIndex,int,int)) },
{ SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
SLOT(_q_rowsAboutToBeInserted(QModelIndex,int,int)) },
{ SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
SLOT(_q_columnsAboutToBeInserted(QModelIndex,int,int)) },
{ SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
SLOT(_q_layoutAboutToBeChanged()) },
{ SIGNAL(columnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
SLOT(_q_layoutAboutToBeChanged()) },
{ SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
SLOT(_q_layoutChanged()) },
{ SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)),
SLOT(_q_layoutChanged()) },
{ SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
SLOT(_q_layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)) },
{ SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)) },
{ SIGNAL(modelReset()),
SLOT(reset()) },
{ 0, 0 }
};
if (model == m)
return;
Q_Q(QItemSelectionModel);
if (model) {
Q_Q(QItemSelectionModel);
QObject::connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
q, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int)));
QObject::connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
q, SLOT(_q_columnsAboutToBeRemoved(QModelIndex,int,int)));
QObject::connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
q, SLOT(_q_rowsAboutToBeInserted(QModelIndex,int,int)));
QObject::connect(model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
q, SLOT(_q_columnsAboutToBeInserted(QModelIndex,int,int)));
QObject::connect(model, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
q, SLOT(_q_layoutAboutToBeChanged()));
QObject::connect(model, SIGNAL(columnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
q, SLOT(_q_layoutAboutToBeChanged()));
QObject::connect(model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
q, SLOT(_q_layoutChanged()));
QObject::connect(model, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)),
q, SLOT(_q_layoutChanged()));
QObject::connect(model, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
q, SLOT(_q_layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
QObject::connect(model, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
q, SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
QObject::connect(model, SIGNAL(modelReset()),
q, SLOT(reset()));
for (const Cx *cx = &connections[0]; cx->signal; cx++)
QObject::disconnect(model, cx->signal, q, cx->slot);
q->reset();
}
model = m;
if (model) {
for (const Cx *cx = &connections[0]; cx->signal; cx++)
QObject::connect(model, cx->signal, q, cx->slot);
}
}
@ -1078,6 +1096,10 @@ void QItemSelectionModelPrivate::_q_layoutChanged(const QList<QPersistentModelIn
selection. All functions operate on both layers; for example,
\l {QTableWidget::selectedItems()}{selecteditems()} will return items from both layers.
\note Since 5.5, \l{QItemSelectionModel::model()}{model},
\l{QItemSelectionModel::hasSelection()}{hasSelection}, and
\l{QItemSelectionModel::currentIndex()}{currentIndex} are meta-object properties.
\sa {Model/View Programming}, QAbstractItemModel, {Chart Example}
*/
@ -1174,6 +1196,16 @@ void QItemSelectionModel::select(const QModelIndex &index, QItemSelectionModel::
\sa select(), currentChanged()
*/
/*!
\fn void QItemSelectionModel::modelChanged(QAbstractItemModel *model)
\since 5.5
This signal is emitted when the model is successfully set with setModel().
\sa model(), setModel()
*/
/*!
\enum QItemSelectionModel::SelectionFlag
@ -1205,6 +1237,10 @@ void QItemSelectionModel::select(const QModelIndex &index, QItemSelectionModel::
void QItemSelectionModel::select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command)
{
Q_D(QItemSelectionModel);
if (!d->model) {
qWarning("QItemSelectionModel: Selecting when no model has been set will result in a no-op.");
return;
}
if (command == NoUpdate)
return;
@ -1312,6 +1348,10 @@ void QItemSelectionModel::clearSelection()
void QItemSelectionModel::setCurrentIndex(const QModelIndex &index, QItemSelectionModel::SelectionFlags command)
{
Q_D(QItemSelectionModel);
if (!d->model) {
qWarning("QItemSelectionModel: Setting the current index when no model has been set will result in a no-op.");
return;
}
if (index == d->currentIndex) {
if (command != NoUpdate)
select(index, command); // select item
@ -1387,6 +1427,8 @@ bool QItemSelectionModel::isSelected(const QModelIndex &index) const
bool QItemSelectionModel::isRowSelected(int row, const QModelIndex &parent) const
{
Q_D(const QItemSelectionModel);
if (!d->model)
return false;
if (parent.isValid() && d->model != parent.model())
return false;
@ -1447,6 +1489,8 @@ bool QItemSelectionModel::isRowSelected(int row, const QModelIndex &parent) cons
bool QItemSelectionModel::isColumnSelected(int column, const QModelIndex &parent) const
{
Q_D(const QItemSelectionModel);
if (!d->model)
return false;
if (parent.isValid() && d->model != parent.model())
return false;
@ -1503,6 +1547,8 @@ bool QItemSelectionModel::isColumnSelected(int column, const QModelIndex &parent
bool QItemSelectionModel::rowIntersectsSelection(int row, const QModelIndex &parent) const
{
Q_D(const QItemSelectionModel);
if (!d->model)
return false;
if (parent.isValid() && d->model != parent.model())
return false;
@ -1535,6 +1581,8 @@ bool QItemSelectionModel::rowIntersectsSelection(int row, const QModelIndex &par
bool QItemSelectionModel::columnIntersectsSelection(int column, const QModelIndex &parent) const
{
Q_D(const QItemSelectionModel);
if (!d->model)
return false;
if (parent.isValid() && d->model != parent.model())
return false;
@ -1671,6 +1719,35 @@ const QItemSelection QItemSelectionModel::selection() const
return selected;
}
/*!
\since 5.5
\property QItemSelectionModel::hasSelection
\internal
*/
/*!
\since 5.5
\property QItemSelectionModel::currentIndex
\internal
*/
/*!
\since 5.5
\property QItemSelectionModel::model
\internal
*/
/*!
\since 5.5
Returns the item model operated on by the selection model.
*/
QAbstractItemModel *QItemSelectionModel::model()
{
return d_func()->model;
}
/*!
Returns the item model operated on by the selection model.
*/
@ -1679,6 +1756,23 @@ const QAbstractItemModel *QItemSelectionModel::model() const
return d_func()->model;
}
/*!
\since 5.5
Sets the model. The modelChanged() signal will be emitted.
\sa model(), modelChanged()
*/
void QItemSelectionModel::setModel(QAbstractItemModel *model)
{
Q_D(QItemSelectionModel);
if (d->model == model)
return;
d->initModel(model);
emit modelChanged(model);
}
/*!
Compares the two selections \a newSelection and \a oldSelection
and emits selectionChanged() with the deselected and selected items.

View File

@ -143,6 +143,10 @@ class QItemSelectionModelPrivate;
class Q_CORE_EXPORT QItemSelectionModel : public QObject
{
Q_OBJECT
Q_PROPERTY(QAbstractItemModel *model READ model WRITE setModel NOTIFY modelChanged)
Q_PROPERTY(bool hasSelection READ hasSelection NOTIFY selectionChanged STORED false DESIGNABLE false)
Q_PROPERTY(QModelIndex currentIndex READ currentIndex NOTIFY currentChanged STORED false DESIGNABLE false)
Q_DECLARE_PRIVATE(QItemSelectionModel)
Q_FLAGS(SelectionFlags)
@ -164,27 +168,31 @@ public:
Q_DECLARE_FLAGS(SelectionFlags, SelectionFlag)
explicit QItemSelectionModel(QAbstractItemModel *model);
explicit QItemSelectionModel(QAbstractItemModel *model = 0);
explicit QItemSelectionModel(QAbstractItemModel *model, QObject *parent);
virtual ~QItemSelectionModel();
QModelIndex currentIndex() const;
bool isSelected(const QModelIndex &index) const;
bool isRowSelected(int row, const QModelIndex &parent) const;
bool isColumnSelected(int column, const QModelIndex &parent) const;
Q_INVOKABLE bool isSelected(const QModelIndex &index) const;
Q_INVOKABLE bool isRowSelected(int row, const QModelIndex &parent) const;
Q_INVOKABLE bool isColumnSelected(int column, const QModelIndex &parent) const;
bool rowIntersectsSelection(int row, const QModelIndex &parent) const;
bool columnIntersectsSelection(int column, const QModelIndex &parent) const;
Q_INVOKABLE bool rowIntersectsSelection(int row, const QModelIndex &parent) const;
Q_INVOKABLE bool columnIntersectsSelection(int column, const QModelIndex &parent) const;
bool hasSelection() const;
QModelIndexList selectedIndexes() const;
QModelIndexList selectedRows(int column = 0) const;
QModelIndexList selectedColumns(int row = 0) const;
const QItemSelection selection() const;
Q_INVOKABLE QModelIndexList selectedIndexes() const;
Q_INVOKABLE QModelIndexList selectedRows(int column = 0) const;
Q_INVOKABLE QModelIndexList selectedColumns(int row = 0) const;
Q_INVOKABLE const QItemSelection selection() const;
// ### Qt 6: Merge these two as "QAbstractItemModel *model() const"
const QAbstractItemModel *model() const;
QAbstractItemModel *model();
void setModel(QAbstractItemModel *model);
public Q_SLOTS:
virtual void setCurrentIndex(const QModelIndex &index, QItemSelectionModel::SelectionFlags command);
@ -201,6 +209,7 @@ Q_SIGNALS:
void currentChanged(const QModelIndex &current, const QModelIndex &previous);
void currentRowChanged(const QModelIndex &current, const QModelIndex &previous);
void currentColumnChanged(const QModelIndex &current, const QModelIndex &previous);
void modelChanged(QAbstractItemModel *model);
protected:
QItemSelectionModel(QItemSelectionModelPrivate &dd, QAbstractItemModel *model);
@ -243,4 +252,7 @@ Q_CORE_EXPORT QDebug operator<<(QDebug, const QItemSelectionRange &);
QT_END_NAMESPACE
Q_DECLARE_METATYPE(QItemSelectionRange)
Q_DECLARE_METATYPE(QItemSelection)
#endif // QITEMSELECTIONMODEL_H

View File

@ -211,7 +211,6 @@ void tst_QAbstractProxyModel::mapFromSource()
QCOMPARE(model.mapFromSource(sourceIndex), mapFromSource);
}
Q_DECLARE_METATYPE(QItemSelection)
void tst_QAbstractProxyModel::mapSelectionFromSource_data()
{
QTest::addColumn<QItemSelection>("selection");

View File

@ -86,6 +86,7 @@ private slots:
void deselectRemovedMiddleRange();
void rangeOperatorLessThan_data();
void rangeOperatorLessThan();
void setModel();
void testDifferentModels();
@ -107,8 +108,6 @@ typedef QList<int> IntList;
typedef QPair<int, int> IntPair;
typedef QList<IntPair> PairList;
Q_DECLARE_METATYPE(QItemSelection)
class QStreamHelper: public QAbstractItemModel
{
public:
@ -2561,6 +2560,21 @@ void tst_QItemSelectionModel::rangeOperatorLessThan()
QVERIFY(r4 < r2);
}
void tst_QItemSelectionModel::setModel()
{
QItemSelectionModel sel;
QVERIFY(!sel.model());
QSignalSpy modelChangedSpy(&sel, SIGNAL(modelChanged(QAbstractItemModel*)));
QStringListModel model(QStringList() << "Blah" << "Blah" << "Blah");
sel.setModel(&model);
QCOMPARE(sel.model(), &model);
QCOMPARE(modelChangedSpy.count(), 1);
sel.select(model.index(0), QItemSelectionModel::Select);
QVERIFY(!sel.selection().isEmpty());
sel.setModel(0);
QVERIFY(sel.selection().isEmpty());
}
void tst_QItemSelectionModel::testDifferentModels()
{
QStandardItemModel model1;

View File

@ -1827,7 +1827,6 @@ void tst_QAbstractItemView::testFocusPolicy()
QVERIFY(!qApp->focusWidget());
}
Q_DECLARE_METATYPE(QItemSelection)
void tst_QAbstractItemView::QTBUG31411_noSelection()
{
QWidget window;