diff options
author | David Boddie <dboddie@trolltech.com> | 2009-08-26 15:50:44 (GMT) |
---|---|---|
committer | David Boddie <dboddie@trolltech.com> | 2009-08-26 15:50:44 (GMT) |
commit | 1be8c6dc12b0158955bf88af450982d6688754d9 (patch) | |
tree | 1b80419c0b8ccd5bab30bc64cf64472b9f6b82c5 | |
parent | 2b7aaf18660fb6639a97b0421563696648384948 (diff) | |
parent | 9385eebd22e005ff6027594ea643a8f46ed2e3e3 (diff) | |
download | Qt-1be8c6dc12b0158955bf88af450982d6688754d9.zip Qt-1be8c6dc12b0158955bf88af450982d6688754d9.tar.gz Qt-1be8c6dc12b0158955bf88af450982d6688754d9.tar.bz2 |
Merge branch '4.6' of git@scm.dev.nokia.troll.no:qt/qt into 4.6
23 files changed, 1731 insertions, 84 deletions
diff --git a/dist/changes-4.6.0 b/dist/changes-4.6.0 index 8c2c2c8..194d670 100644 --- a/dist/changes-4.6.0 +++ b/dist/changes-4.6.0 @@ -49,10 +49,13 @@ information about a particular change. this is that Nokia focuses on OpenGL for desktop hardware accelerated rendering. - - When mixing OpenGL and QPainter calls you need to first call syncState() - on the paint engine, for example "painter->paintEngine()->syncState()". - This is to ensure that the engine flushes any pending drawing and sets up - the GL modelview/projection matrices properly. + - When mixing OpenGL and QPainter calls you need to surround your custom + OpenGL calls with QPainter::beginNativePainting() and + QPainter::endNativePainting(). + This is to ensure that the paint engine flushes any pending drawing and sets + up the GL modelview/projection matrices properly before you can issue custom + OpenGL calls, and to let the paint engine synchronize to the painter state + before resuming regular QPainter based drawing. - Graphics View has undergone heavy optimization work, and as a result of this work, the following behavior changes were introduced. diff --git a/doc/src/examples/overpainting.qdoc b/doc/src/examples/overpainting.qdoc index 91100c0..b19b503 100644 --- a/doc/src/examples/overpainting.qdoc +++ b/doc/src/examples/overpainting.qdoc @@ -159,9 +159,10 @@ \snippet examples/opengl/overpainting/glwidget.cpp 7 - Once the list containing the object has been executed, the matrix stack - needs to be restored to its original state at the start of this function - before we can begin overpainting: + Once the list containing the object has been executed, the GL + states we changed and the matrix stack needs to be restored to its + original state at the start of this function before we can begin + overpainting: \snippet examples/opengl/overpainting/glwidget.cpp 8 diff --git a/examples/opengl/hellogl_es2/glwidget.cpp b/examples/opengl/hellogl_es2/glwidget.cpp index 9a2a83e..50a7797 100644 --- a/examples/opengl/hellogl_es2/glwidget.cpp +++ b/examples/opengl/hellogl_es2/glwidget.cpp @@ -266,7 +266,7 @@ void GLWidget::paintGL() QPainter painter; painter.begin(this); - painter.paintEngine()->syncState(); + painter.beginNativePainting(); glClearColor(0.1f, 0.1f, 0.2f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -302,6 +302,8 @@ void GLWidget::paintGL() glDisable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); + painter.endNativePainting(); + if (m_showBubbles) foreach (Bubble *bubble, bubbles) { bubble->drawBubble(&painter); diff --git a/examples/opengl/overpainting/glwidget.cpp b/examples/opengl/overpainting/glwidget.cpp index a6e6195..cad591f 100644 --- a/examples/opengl/overpainting/glwidget.cpp +++ b/examples/opengl/overpainting/glwidget.cpp @@ -166,6 +166,11 @@ void GLWidget::paintEvent(QPaintEvent *event) //! [7] //! [8] + glShadeModel(GL_FLAT); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + glMatrixMode(GL_MODELVIEW); glPopMatrix(); //! [8] diff --git a/examples/openvg/star/starwidget.cpp b/examples/openvg/star/starwidget.cpp index 1a64fc9..ab11bdb 100644 --- a/examples/openvg/star/starwidget.cpp +++ b/examples/openvg/star/starwidget.cpp @@ -91,7 +91,7 @@ void StarWidget::paintEvent(QPaintEvent *) // Flush the state changes to the OpenVG implementation // and prepare to perform raw OpenVG calls. - painter.paintEngine()->syncState(); + painter.beginNativePainting(); // Cache the path if we haven't already. if (path == VG_INVALID_HANDLE) { @@ -109,7 +109,7 @@ void StarWidget::paintEvent(QPaintEvent *) vgDrawPath(path, VG_FILL_PATH | VG_STROKE_PATH); // Restore normal QPainter operations. - painter.paintEngine()->syncState(); + painter.endNativePainting(); painter.end(); } diff --git a/src/corelib/kernel/qabstractitemmodel.cpp b/src/corelib/kernel/qabstractitemmodel.cpp index 3b7059b..fb0afcc 100644 --- a/src/corelib/kernel/qabstractitemmodel.cpp +++ b/src/corelib/kernel/qabstractitemmodel.cpp @@ -599,6 +599,118 @@ void QAbstractItemModelPrivate::rowsInserted(const QModelIndex &parent, } } +void QAbstractItemModelPrivate::itemsAboutToBeMoved(const QModelIndex &srcParent, int srcFirst, int srcLast, const QModelIndex &destinationParent, int destinationChild, Qt::Orientation orientation) +{ + Q_Q(QAbstractItemModel); + QVector<QPersistentModelIndexData *> persistent_moved_explicitly; + QVector<QPersistentModelIndexData *> persistent_moved_in_source; + QVector<QPersistentModelIndexData *> persistent_moved_in_destination; + + QHash<QModelIndex, QPersistentModelIndexData *>::const_iterator it; + const QHash<QModelIndex, QPersistentModelIndexData *>::const_iterator begin = persistent.indexes.constBegin(); + const QHash<QModelIndex, QPersistentModelIndexData *>::const_iterator end = persistent.indexes.constEnd(); + + const bool sameParent = (srcParent == destinationParent); + const bool movingUp = (srcFirst > destinationChild); + + for ( it = begin; it != end; ++it) { + QPersistentModelIndexData *data = *it; + const QModelIndex &index = data->index; + const QModelIndex &parent = index.parent(); + const bool isSourceIndex = (parent == srcParent); + const bool isDestinationIndex = (parent == destinationParent); + + int childPosition; + if (orientation == Qt::Vertical) + childPosition = index.row(); + else + childPosition = index.column(); + + if (!index.isValid() || !(isSourceIndex || isDestinationIndex ) ) + continue; + + if (!sameParent && isDestinationIndex) { + if (childPosition >= destinationChild) + persistent_moved_in_destination.append(data); + continue; + } + + if (sameParent && movingUp && childPosition < destinationChild) + continue; + + if (sameParent && !movingUp && childPosition < srcFirst ) + continue; + + if (!sameParent && childPosition < srcFirst) + continue; + + if (sameParent && (childPosition > srcLast) && (childPosition >= destinationChild )) + continue; + + if ((childPosition <= srcLast) && (childPosition >= srcFirst)) { + persistent_moved_explicitly.append(data); + } else { + persistent_moved_in_source.append(data); + } + } + persistent.moved.push(persistent_moved_explicitly); + persistent.moved.push(persistent_moved_in_source); + persistent.moved.push(persistent_moved_in_destination); +} + +/*! + \internal + + Moves persistent indexes \a indexes by amount \a change. The change will be either a change in row value or a change in + column value depending on the value of \a orientation. The indexes may also be moved to a different parent if \a parent + differs from the existing parent for the index. +*/ +void QAbstractItemModelPrivate::movePersistentIndexes(QVector<QPersistentModelIndexData *> indexes, int change, const QModelIndex &parent, Qt::Orientation orientation) +{ + QVector<QPersistentModelIndexData *>::const_iterator it; + const QVector<QPersistentModelIndexData *>::const_iterator begin = indexes.constBegin(); + const QVector<QPersistentModelIndexData *>::const_iterator end = indexes.constEnd(); + + for (it = begin; it != end; ++it) + { + QPersistentModelIndexData *data = *it; + + int row = data->index.row(); + int column = data->index.column(); + + if (Qt::Vertical == orientation) + row += change; + else + column += change; + + persistent.indexes.erase(persistent.indexes.find(data->index)); + data->index = q_func()->index(row, column, parent); + if (data->index.isValid()) { + persistent.insertMultiAtEnd(data->index, data); + } else { + qWarning() << "QAbstractItemModel::endMoveRows: Invalid index (" << row << "," << column << ") in model" << q_func(); + } + } +} + +void QAbstractItemModelPrivate::itemsMoved(const QModelIndex &sourceParent, int sourceFirst, int sourceLast, const QModelIndex &destinationParent, int destinationChild, Qt::Orientation orientation) +{ + QVector<QPersistentModelIndexData *> moved_in_destination = persistent.moved.pop(); + QVector<QPersistentModelIndexData *> moved_in_source = persistent.moved.pop(); + QVector<QPersistentModelIndexData *> moved_explicitly = persistent.moved.pop(); + + const bool sameParent = (sourceParent == destinationParent); + const bool movingUp = (sourceFirst > destinationChild); + + const int explicit_change = (!sameParent || movingUp) ? destinationChild - sourceFirst : destinationChild - sourceLast - 1 ; + const int source_change = (!sameParent || !movingUp) ? -1*(sourceLast - sourceFirst + 1) : sourceLast - sourceFirst + 1 ; + const int destination_change = sourceLast - sourceFirst + 1; + + movePersistentIndexes(moved_explicitly, explicit_change, destinationParent, orientation); + movePersistentIndexes(moved_in_source, source_change, sourceParent, orientation); + movePersistentIndexes(moved_in_destination, destination_change, destinationParent, orientation); +} + void QAbstractItemModelPrivate::rowsAboutToBeRemoved(const QModelIndex &parent, int first, int last) { @@ -1370,6 +1482,36 @@ QAbstractItemModel::~QAbstractItemModel() */ /*! + \fn void QAbstractItemModel::rowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow) + + This signal is emitted after rows have been moved within the + model. The items between \a sourceStart and \a sourceEnd + inclusive, under the given \a sourceParent item have been moved to \a destinationParent + starting at the row \a destinationRow. + + \bold{Note:} Components connected to this signal use it to adapt to changes + in the model's dimensions. It can only be emitted by the QAbstractItemModel + implementation, and cannot be explicitly emitted in subclass code. + + \sa beginMoveRows() +*/ + +/*! + \fn void QAbstractItemModel::rowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow) + + This signal is emitted just before rows are moved within the + model. The items that will be moved are those between \a sourceStart and \a sourceEnd + inclusive, under the given \a sourceParent item. They will be moved to \a destinationParent + starting at the row \a destinationRow. + + \bold{Note:} Components connected to this signal use it to adapt to changes + in the model's dimensions. It can only be emitted by the QAbstractItemModel + implementation, and cannot be explicitly emitted in subclass code. + + \sa beginMoveRows() +*/ + +/*! \fn void QAbstractItemModel::columnsInserted(const QModelIndex &parent, int start, int end) This signal is emitted after columns have been inserted into the model. The @@ -2284,6 +2426,116 @@ void QAbstractItemModel::endRemoveRows() } /*! + Returns whether a move operation is valid. + + A move operation is not allowed if it moves a continuous range of rows to a destination within + itself, or if it attempts to move a row to one of its own descendants. + + \internal +*/ +bool QAbstractItemModelPrivate::allowMove(const QModelIndex &srcParent, int start, int end, const QModelIndex &destinationParent, int destinationStart, Qt::Orientation orientation) +{ + Q_Q(QAbstractItemModel); + // Don't move the range within itself. + if ( ( destinationParent == srcParent ) + && ( destinationStart >= start ) + && ( destinationStart <= end + 1) ) + return false; + + QModelIndex destinationAncestor = destinationParent; + int pos = (Qt::Vertical == orientation) ? destinationAncestor.row() : destinationAncestor.column(); + forever { + if (destinationAncestor == srcParent) { + if (pos >= start && pos <= end) + return false; + break; + } + + if (!destinationAncestor.isValid()) + break; + + pos = (Qt::Vertical == orientation) ? destinationAncestor.row() : destinationAncestor.column(); + destinationAncestor = destinationAncestor.parent(); + } + + return true; +} + +/*! + Begins a row move operation. + + When reimplementing a subclass, this method simplifies moving entities + in your model. This method is responsible for moving persistent indexes + in the model, which you would otherwise be required to do yourself. + + Using beginMoveRows and endMoveRows is an alternative to emitting + layoutAboutToBeChanged and layoutChanged directly along with changePersistentIndexes. + layoutAboutToBeChanged is emitted by this method for compatibility reasons. + + The \a sourceParent index corresponds to the parent from which the + rows are moved; \a sourceFirst and \a sourceLast are the row numbers of the + rows to be moved. The \a destinationParent index corresponds to the parent into which + the rows are moved. The \a destinationRow is the row to which the rows will be moved. + That is, the index at row \a sourceFirst in \a sourceParent will become row \a destinationRow + in \a destinationParent. Its siblings will be moved correspondingly. + + Note that \a sourceParent and \a destinationParent may be the same, in which case you must + ensure that the \a destinationRow is not within the range of \a sourceFirst and \a sourceLast. + You must also ensure that you do not attempt to move a row to one of its own chilren or ancestors. + This method returns false if either condition is true, in which case you should abort your move operation. + + \sa endMoveRows() + + \since 4.6 +*/ +bool QAbstractItemModel::beginMoveRows(const QModelIndex &sourceParent, int sourceFirst, int sourceLast, const QModelIndex &destinationParent, int destinationChild) +{ + Q_ASSERT(sourceFirst >= 0); + Q_ASSERT(sourceLast >= sourceFirst); + Q_ASSERT(destinationChild >= 0); + Q_D(QAbstractItemModel); + + if (!d->allowMove(sourceParent, sourceFirst, sourceLast, destinationParent, destinationChild, Qt::Vertical)) { + return false; + } + + d->changes.push(QAbstractItemModelPrivate::Change(sourceParent, sourceFirst, sourceLast)); + int destinationLast = destinationChild + (sourceLast - sourceFirst); + d->changes.push(QAbstractItemModelPrivate::Change(destinationParent, destinationChild, destinationLast)); + + d->itemsAboutToBeMoved(sourceParent, sourceFirst, sourceLast, destinationParent, destinationChild, Qt::Vertical); + emit rowsAboutToBeMoved(sourceParent, sourceFirst, sourceLast, destinationParent, destinationChild); + emit layoutAboutToBeChanged(); + return true; +} + +/*! + Ends a row move operation. + + When implementing a subclass, you must call this + function \e after moving data within the model's underlying data + store. + + layoutChanged is emitted by this method for compatibility reasons. + + \sa beginMoveRows() + + \since 4.6 +*/ +void QAbstractItemModel::endMoveRows() +{ + Q_D(QAbstractItemModel); + + QAbstractItemModelPrivate::Change insertChange = d->changes.pop(); + QAbstractItemModelPrivate::Change removeChange = d->changes.pop(); + + d->itemsMoved(removeChange.parent, removeChange.first, removeChange.last, insertChange.parent, insertChange.first, Qt::Vertical); + + emit rowsMoved(removeChange.parent, removeChange.first, removeChange.last, insertChange.parent, insertChange.first); + emit layoutChanged(); +} + +/*! Begins a column insertion operation. When reimplementing insertColumns() in a subclass, you must call this @@ -2406,6 +2658,81 @@ void QAbstractItemModel::endRemoveColumns() } /*! + Begins a column move operation. + + When reimplementing a subclass, this method simplifies moving entities + in your model. This method is responsible for moving persistent indexes + in the model, which you would otherwise be required to do yourself. + + Using beginMoveColumns and endMoveColumns is an alternative to emitting + layoutAboutToBeChanged and layoutChanged directly along with changePersistentIndexes. + layoutAboutToBeChanged is emitted by this method for compatibility reasons. + + The \a sourceParent index corresponds to the parent from which the + columns are moved; \a sourceFirst and \a sourceLast are the column numbers of the + columns to be moved. The \a destinationParent index corresponds to the parent into which + the columns are moved. The \a destinationColumn is the column to which the columns will be moved. + That is, the index at column \a sourceFirst in \a sourceParent will become column \a destinationColumn + in \a destinationParent. Its siblings will be moved correspondingly. + + Note that \a sourceParent and \a destinationParent may be the same, in which case you must + ensure that the \a destinationColumn is not within the range of \a sourceFirst and \a sourceLast. + You must also ensure that you do not attempt to move a row to one of its own chilren or ancestors. + This method returns false if either condition is true, in which case you should abort your move operation. + + \sa endMoveColumns() + + \since 4.6 +*/ +bool QAbstractItemModel::beginMoveColumns(const QModelIndex &sourceParent, int sourceFirst, int sourceLast, const QModelIndex &destinationParent, int destinationChild) +{ + Q_ASSERT(sourceFirst >= 0); + Q_ASSERT(sourceLast >= sourceFirst); + Q_ASSERT(destinationChild >= 0); + Q_D(QAbstractItemModel); + + if (!d->allowMove(sourceParent, sourceFirst, sourceLast, destinationParent, destinationChild, Qt::Horizontal)) { + return false; + } + + d->changes.push(QAbstractItemModelPrivate::Change(sourceParent, sourceFirst, sourceLast)); + int destinationLast = destinationChild + (sourceLast - sourceFirst); + d->changes.push(QAbstractItemModelPrivate::Change(destinationParent, destinationChild, destinationLast)); + + d->itemsAboutToBeMoved(sourceParent, sourceFirst, sourceLast, destinationParent, destinationChild, Qt::Horizontal); + + emit columnsAboutToBeMoved(sourceParent, sourceFirst, sourceLast, destinationParent, destinationChild); + emit layoutAboutToBeChanged(); + return true; +} + +/*! + Ends a column move operation. + + When implementing a subclass, you must call this + function \e after moving data within the model's underlying data + store. + + layoutChanged is emitted by this method for compatibility reasons. + + \sa beginMoveColumns() + + \since 4.6 +*/ +void QAbstractItemModel::endMoveColumns() +{ + Q_D(QAbstractItemModel); + + QAbstractItemModelPrivate::Change insertChange = d->changes.pop(); + QAbstractItemModelPrivate::Change removeChange = d->changes.pop(); + + d->itemsMoved(removeChange.parent, removeChange.first, removeChange.last, insertChange.parent, insertChange.first, Qt::Horizontal); + + emit columnsMoved(removeChange.parent, removeChange.first, removeChange.last, insertChange.parent, insertChange.first); + emit layoutChanged(); +} + +/*! Resets the model to its original state in any attached views. The view to which the model is attached to will be reset as well. diff --git a/src/corelib/kernel/qabstractitemmodel.h b/src/corelib/kernel/qabstractitemmodel.h index 00f6cb2..b47e4cb 100644 --- a/src/corelib/kernel/qabstractitemmodel.h +++ b/src/corelib/kernel/qabstractitemmodel.h @@ -252,6 +252,13 @@ private: // can only be emitted by QAbstractItemModel void modelAboutToBeReset(); void modelReset(); + void rowsAboutToBeMoved( const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow ); + void rowsMoved( const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row ); + + void columnsAboutToBeMoved( const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationColumn ); + void columnsMoved( const QModelIndex &parent, int start, int end, const QModelIndex &destination, int column ); + + public Q_SLOTS: virtual bool submit(); virtual void revert(); @@ -272,12 +279,18 @@ protected: void beginRemoveRows(const QModelIndex &parent, int first, int last); void endRemoveRows(); + bool beginMoveRows(const QModelIndex &sourceParent, int sourceFirst, int sourceLast, const QModelIndex &destinationParent, int destinationRow); + void endMoveRows(); + void beginInsertColumns(const QModelIndex &parent, int first, int last); void endInsertColumns(); void beginRemoveColumns(const QModelIndex &parent, int first, int last); void endRemoveColumns(); + bool beginMoveColumns(const QModelIndex &sourceParent, int sourceFirst, int sourceLast, const QModelIndex &destinationParent, int destinationColumn); + void endMoveColumns(); + void reset(); void changePersistentIndex(const QModelIndex &from, const QModelIndex &to); diff --git a/src/corelib/kernel/qabstractitemmodel_p.h b/src/corelib/kernel/qabstractitemmodel_p.h index e81e627..aae3cba 100644 --- a/src/corelib/kernel/qabstractitemmodel_p.h +++ b/src/corelib/kernel/qabstractitemmodel_p.h @@ -80,6 +80,7 @@ class Q_CORE_EXPORT QAbstractItemModelPrivate : public QObjectPrivate public: QAbstractItemModelPrivate() : QObjectPrivate(), supportedDragActions(-1), roleNames(defaultRoleNames()) {} void removePersistentIndexData(QPersistentModelIndexData *data); + void movePersistentIndexes(QVector<QPersistentModelIndexData *> indexes, int change, const QModelIndex &parent, Qt::Orientation orientation); void rowsAboutToBeInserted(const QModelIndex &parent, int first, int last); void rowsInserted(const QModelIndex &parent, int first, int last); void rowsAboutToBeRemoved(const QModelIndex &parent, int first, int last); @@ -91,6 +92,10 @@ public: static QAbstractItemModel *staticEmptyModel(); static bool variantLessThan(const QVariant &v1, const QVariant &v2); + void itemsAboutToBeMoved(const QModelIndex &srcParent, int srcFirst, int srcLast, const QModelIndex &destinationParent, int destinationChild, Qt::Orientation); + void itemsMoved(const QModelIndex &srcParent, int srcFirst, int srcLast, const QModelIndex &destinationParent, int destinationChild, Qt::Orientation orientation); + bool allowMove(const QModelIndex &srcParent, int srcFirst, int srcLast, const QModelIndex &destinationParent, int destinationChild, Qt::Orientation orientation); + inline QModelIndex createIndex(int row, int column, void *data = 0) const { return q_func()->createIndex(row, column, data); } @@ -132,6 +137,8 @@ public: Change(const QModelIndex &p, int f, int l) : parent(p), first(f), last(l) {} QModelIndex parent; int first, last; + + bool isValid() { return first >= 0 && last >= 0; } }; QStack<Change> changes; diff --git a/src/gui/effects/qgraphicseffect.cpp b/src/gui/effects/qgraphicseffect.cpp index 0289914..f620878 100644 --- a/src/gui/effects/qgraphicseffect.cpp +++ b/src/gui/effects/qgraphicseffect.cpp @@ -61,13 +61,13 @@ Qt provides the following standard effects: \list - \o QGraphicsGrayScaleEffect - renders the item in shades of gray + \o QGraphicsGrayscaleEffect - renders the item in shades of gray \o QGraphicsColorizeEffect - renders the item in shades of any given color \o QGraphicsPixelizeEffect - pixelizes the item with any pixel size \o QGraphicsBlurEffect - blurs the item by a given radius \o QGraphicsDropShadowEffect - renders a dropshadow behind the item \o QGraphicsOpacityEffect - renders the item with an opacity - \o QGrahicsShaderEffect - renders the item with a pixel shader fragment + \o QGraphicsShaderEffect - renders the item with a pixel shader fragment \endlist diff --git a/src/gui/painting/qpaintengineex_p.h b/src/gui/painting/qpaintengineex_p.h index cf3aad7..1ba2153 100644 --- a/src/gui/painting/qpaintengineex_p.h +++ b/src/gui/painting/qpaintengineex_p.h @@ -204,6 +204,9 @@ public: virtual void sync() {} + virtual void beginNativePainting() {} + virtual void endNativePainting() {} + virtual QPixmapFilter *createPixmapFilter(int /*type*/) const { return 0; } protected: diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index e1a6e80..cba4ad9 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -1889,6 +1889,45 @@ QPaintEngine *QPainter::paintEngine() const return d->engine; } +/*! + Flushes the painting pipeline and prepares for the user issuing + native painting commands. Must be followed by a call to + endNativePainting(). + + \sa endNativePainting() +*/ +void QPainter::beginNativePainting() +{ + Q_D(QPainter); + if (!d->engine) { + qWarning("QPainter::beginNativePainting: Painter not active"); + return; + } + + if (d->extended) + d->extended->beginNativePainting(); +} + +/*! + Restores the painter after manually issuing native painting commands. + Lets the painter restore any native state that it relies on before + calling any other painter commands. + + \sa beginNativePainting() +*/ +void QPainter::endNativePainting() +{ + Q_D(const QPainter); + if (!d->engine) { + qWarning("QPainter::beginNativePainting: Painter not active"); + return; + } + + if (d->extended) + d->extended->endNativePainting(); + else + d->engine->syncState(); +} /*! Returns the font metrics for the painter if the painter is diff --git a/src/gui/painting/qpainter.h b/src/gui/painting/qpainter.h index 14d1cf8..1bb97c6 100644 --- a/src/gui/painting/qpainter.h +++ b/src/gui/painting/qpainter.h @@ -423,6 +423,9 @@ public: static QPaintDevice *redirected(const QPaintDevice *device, QPoint *offset = 0); static void restoreRedirected(const QPaintDevice *device); + void beginNativePainting(); + void endNativePainting(); + #ifdef QT3_SUPPORT inline QT3_SUPPORT void setBackgroundColor(const QColor &color) { setBackground(color); } diff --git a/src/opengl/gl2paintengineex/qglengineshadersource_p.h b/src/opengl/gl2paintengineex/qglengineshadersource_p.h index a8e2e72..cd3cf57 100644 --- a/src/opengl/gl2paintengineex/qglengineshadersource_p.h +++ b/src/opengl/gl2paintengineex/qglengineshadersource_p.h @@ -73,8 +73,8 @@ static const char* const qglslMainVertexShader = "\ }"; static const char* const qglslMainWithTexCoordsVertexShader = "\ - attribute mediump vec2 textureCoordArray; \ - varying mediump vec2 textureCoords; \ + attribute highp vec2 textureCoordArray; \ + varying highp vec2 textureCoords; \ uniform highp float depth;\ void setPosition();\ void main(void) \ @@ -105,9 +105,9 @@ static const char* const qglslPositionWithPatternBrushVertexShader = "\ attribute highp vec4 vertexCoordsArray; \ uniform highp mat4 pmvMatrix; \ uniform mediump vec2 halfViewportSize; \ - uniform mediump vec2 invertedTextureSize; \ - uniform mediump mat3 brushTransform; \ - varying mediump vec2 patternTexCoords; \ + uniform highp vec2 invertedTextureSize; \ + uniform highp mat3 brushTransform; \ + varying highp vec2 patternTexCoords; \ void setPosition(void) { \ gl_Position = pmvMatrix * vertexCoordsArray;\ gl_Position.xy = gl_Position.xy / gl_Position.w; \ @@ -124,9 +124,9 @@ static const char* const qglslAffinePositionWithPatternBrushVertexShader = qglslPositionWithPatternBrushVertexShader; static const char* const qglslPatternBrushSrcFragmentShader = "\ - uniform sampler2D brushTexture;\ + uniform lowp sampler2D brushTexture;\ uniform lowp vec4 patternColor; \ - varying mediump vec2 patternTexCoords;\ + varying highp vec2 patternTexCoords;\ lowp vec4 srcPixel() { \ return patternColor * (1.0 - texture2D(brushTexture, patternTexCoords).r); \ }\n"; @@ -139,7 +139,7 @@ static const char* const qglslPositionWithLinearGradientBrushVertexShader = "\ uniform mediump vec2 halfViewportSize; \ uniform highp vec3 linearData; \ uniform highp mat3 brushTransform; \ - varying mediump float index ; \ + varying mediump float index; \ void setPosition() { \ gl_Position = pmvMatrix * vertexCoordsArray;\ gl_Position.xy = gl_Position.xy / gl_Position.w; \ @@ -155,7 +155,7 @@ static const char* const qglslAffinePositionWithLinearGradientBrushVertexShader = qglslPositionWithLinearGradientBrushVertexShader; static const char* const qglslLinearGradientBrushSrcFragmentShader = "\ - uniform sampler2D brushTexture; \ + uniform lowp sampler2D brushTexture; \ varying mediump float index; \ lowp vec4 srcPixel() { \ mediump vec2 val = vec2(index, 0.5); \ @@ -187,7 +187,7 @@ static const char* const qglslAffinePositionWithConicalGradientBrushVertexShader static const char* const qglslConicalGradientBrushSrcFragmentShader = "\n\ #define INVERSE_2PI 0.1591549430918953358 \n\ - uniform sampler2D brushTexture; \n\ + uniform lowp sampler2D brushTexture; \n\ uniform mediump float angle; \ varying highp vec2 A; \ lowp vec4 srcPixel() { \ @@ -226,7 +226,7 @@ static const char* const qglslAffinePositionWithRadialGradientBrushVertexShader = qglslPositionWithRadialGradientBrushVertexShader; static const char* const qglslRadialGradientBrushSrcFragmentShader = "\ - uniform sampler2D brushTexture; \ + uniform lowp sampler2D brushTexture; \ uniform highp float fmp2_m_radius2; \ uniform highp float inverse_2_fmp2_m_radius2; \ varying highp float b; \ @@ -243,9 +243,9 @@ static const char* const qglslPositionWithTextureBrushVertexShader = "\ attribute highp vec4 vertexCoordsArray; \ uniform highp mat4 pmvMatrix; \ uniform mediump vec2 halfViewportSize; \ - uniform mediump vec2 invertedTextureSize; \ - uniform mediump mat3 brushTransform; \ - varying mediump vec2 brushTextureCoords; \ + uniform highp vec2 invertedTextureSize; \ + uniform highp mat3 brushTransform; \ + varying highp vec2 brushTextureCoords; \ void setPosition(void) { \ gl_Position = pmvMatrix * vertexCoordsArray;\ gl_Position.xy = gl_Position.xy / gl_Position.w; \ @@ -262,16 +262,16 @@ static const char* const qglslAffinePositionWithTextureBrushVertexShader = qglslPositionWithTextureBrushVertexShader; static const char* const qglslTextureBrushSrcFragmentShader = "\ - varying mediump vec2 brushTextureCoords; \ - uniform sampler2D brushTexture; \ + varying highp vec2 brushTextureCoords; \ + uniform lowp sampler2D brushTexture; \ lowp vec4 srcPixel() { \ return texture2D(brushTexture, brushTextureCoords); \ }"; static const char* const qglslTextureBrushSrcWithPatternFragmentShader = "\ - varying mediump vec2 brushTextureCoords; \ + varying highp vec2 brushTextureCoords; \ uniform lowp vec4 patternColor; \ - uniform sampler2D brushTexture; \ + uniform lowp sampler2D brushTexture; \ lowp vec4 srcPixel() { \ return patternColor * (1.0 - texture2D(brushTexture, brushTextureCoords).r); \ }"; @@ -284,15 +284,15 @@ static const char* const qglslSolidBrushSrcFragmentShader = "\ }"; static const char* const qglslImageSrcFragmentShader = "\ - varying mediump vec2 textureCoords; \ - uniform sampler2D imageTexture; \ + varying highp vec2 textureCoords; \ + uniform lowp sampler2D imageTexture; \ lowp vec4 srcPixel() { \ return texture2D(imageTexture, textureCoords); \ }"; static const char* const qglslCustomSrcFragmentShader = "\ varying highp vec2 textureCoords; \ - uniform sampler2D imageTexture; \ + uniform lowp sampler2D imageTexture; \ lowp vec4 customShader(lowp sampler2D texture, highp vec2 coords); \ lowp vec4 srcPixel() { \ return customShader(imageTexture, textureCoords); \ @@ -301,14 +301,14 @@ static const char* const qglslCustomSrcFragmentShader = "\ static const char* const qglslImageSrcWithPatternFragmentShader = "\ varying highp vec2 textureCoords; \ uniform lowp vec4 patternColor; \ - uniform sampler2D imageTexture; \ + uniform lowp sampler2D imageTexture; \ lowp vec4 srcPixel() { \ return patternColor * (1.0 - texture2D(imageTexture, textureCoords).r); \ }\n"; static const char* const qglslNonPremultipliedImageSrcFragmentShader = "\ varying highp vec2 textureCoords; \ - uniform sampler2D imageTexture; \ + uniform lowp sampler2D imageTexture; \ lowp vec4 srcPixel() { \ lowp vec4 sample = texture2D(imageTexture, textureCoords); \ sample.rgb = sample.rgb * sample.a; \ @@ -383,7 +383,7 @@ static const char* const qglslMainFragmentShader = "\ static const char* const qglslMaskFragmentShader = "\ varying highp vec2 textureCoords;\ - uniform sampler2D maskTexture;\ + uniform lowp sampler2D maskTexture;\ lowp vec4 applyMask(lowp vec4 src) \ {\ lowp vec4 mask = texture2D(maskTexture, textureCoords); \ diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp index 136a078..2f565cf 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp @@ -376,6 +376,7 @@ void QGL2PaintEngineExPrivate::useSimpleShader() void QGL2PaintEngineExPrivate::updateBrushTexture() { + Q_Q(QGL2PaintEngineEx); // qDebug("QGL2PaintEngineExPrivate::updateBrushTexture()"); Qt::BrushStyle style = currentBrush->style(); @@ -385,7 +386,7 @@ void QGL2PaintEngineExPrivate::updateBrushTexture() glActiveTexture(GL_TEXTURE0 + QT_BRUSH_TEXTURE_UNIT); ctx->d_func()->bindTexture(texImage, GL_TEXTURE_2D, GL_RGBA, true); - updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, true); + updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, q->state()->renderHints & QPainter::SmoothPixmapTransform); } else if (style >= Qt::LinearGradientPattern && style <= Qt::ConicalGradientPattern) { // Gradiant brush: All the gradiants use the same texture @@ -400,11 +401,11 @@ void QGL2PaintEngineExPrivate::updateBrushTexture() glBindTexture(GL_TEXTURE_2D, texId); if (g->spread() == QGradient::RepeatSpread || g->type() == QGradient::ConicalGradient) - updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, true); + updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, q->state()->renderHints & QPainter::SmoothPixmapTransform); else if (g->spread() == QGradient::ReflectSpread) - updateTextureFilter(GL_TEXTURE_2D, GL_MIRRORED_REPEAT_IBM, true); + updateTextureFilter(GL_TEXTURE_2D, GL_MIRRORED_REPEAT_IBM, q->state()->renderHints & QPainter::SmoothPixmapTransform); else - updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE, true); + updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE, q->state()->renderHints & QPainter::SmoothPixmapTransform); } else if (style == Qt::TexturePattern) { const QPixmap& texPixmap = currentBrush->texture(); @@ -412,7 +413,7 @@ void QGL2PaintEngineExPrivate::updateBrushTexture() glActiveTexture(GL_TEXTURE0 + QT_BRUSH_TEXTURE_UNIT); // TODO: Support y-inverted pixmaps as brushes ctx->d_func()->bindTexture(texPixmap, GL_TEXTURE_2D, GL_RGBA, true); - updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, true); + updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, q->state()->renderHints & QPainter::SmoothPixmapTransform); } brushTextureDirty = false; } @@ -675,7 +676,7 @@ void QGL2PaintEngineExPrivate::drawTexture(const QGLRect& dest, const QGLRect& s glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } -void QGL2PaintEngineEx::sync() +void QGL2PaintEngineEx::beginNativePainting() { Q_D(QGL2PaintEngineEx); ensureActive(); @@ -709,15 +710,25 @@ void QGL2PaintEngineEx::sync() #endif d->lastTexture = GLuint(-1); + d->resetGLState(); + + d->needsSync = true; +} +void QGL2PaintEngineExPrivate::resetGLState() +{ glDisable(GL_BLEND); glActiveTexture(GL_TEXTURE0); - glDisable(GL_DEPTH_TEST); + glDisable(GL_SCISSOR_TEST); glDepthFunc(GL_LESS); glDepthMask(true); glClearDepth(1); +} +void QGL2PaintEngineEx::endNativePainting() +{ + Q_D(QGL2PaintEngineEx); d->needsSync = true; } @@ -1074,6 +1085,7 @@ void QGL2PaintEngineEx::renderHintsChanged() Q_D(QGL2PaintEngineEx); d->lastTexture = GLuint(-1); + d->brushTextureDirty = true; // qDebug("QGL2PaintEngineEx::renderHintsChanged() not implemented!"); } @@ -1101,7 +1113,7 @@ void QGL2PaintEngineEx::drawPixmap(const QRectF& dest, const QPixmap & pixmap, c bool isBitmap = pixmap.isQBitmap(); bool isOpaque = !isBitmap && !pixmap.hasAlphaChannel(); - d->updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, + d->updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE, state()->renderHints & QPainter::SmoothPixmapTransform, texture->id); d->drawTexture(dest, srcRect, pixmap.size(), isOpaque, isBitmap); } @@ -1118,7 +1130,7 @@ void QGL2PaintEngineEx::drawImage(const QRectF& dest, const QImage& image, const QGLTexture *texture = ctx->d_func()->bindTexture(image, GL_TEXTURE_2D, GL_RGBA, true); GLuint id = texture->id; - d->updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, + d->updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE, state()->renderHints & QPainter::SmoothPixmapTransform, id); d->drawTexture(dest, src, image.size(), !image.hasAlphaChannel()); } @@ -1133,7 +1145,7 @@ void QGL2PaintEngineEx::drawTexture(const QRectF &dest, GLuint textureId, const glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT); glBindTexture(GL_TEXTURE_2D, textureId); - d->updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, + d->updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE, state()->renderHints & QPainter::SmoothPixmapTransform, textureId); d->drawTexture(dest, src, size, false); } @@ -1356,6 +1368,8 @@ bool QGL2PaintEngineEx::end() d->drawable.doneCurrent(); d->ctx->d_ptr->active_engine = 0; + d->resetGLState(); + return false; } diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h index 7b734e3..66e7a51 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h @@ -133,7 +133,9 @@ public: inline const QOpenGL2PaintEngineState *state() const { return static_cast<const QOpenGL2PaintEngineState *>(QPaintEngineEx::state()); } - virtual void sync(); + + void beginNativePainting(); + void endNativePainting(); const QGLContext* context(); @@ -168,6 +170,7 @@ public: void setBrush(const QBrush* brush); void transferMode(EngineMode newMode); + void resetGLState(); // fill, drawOutline, drawTexture & drawCachedGlyphs are the rendering entry points: void fill(const QVectorPath &path); diff --git a/src/opengl/qglpixmapfilter.cpp b/src/opengl/qglpixmapfilter.cpp index df7811e..83fddd1 100644 --- a/src/opengl/qglpixmapfilter.cpp +++ b/src/opengl/qglpixmapfilter.cpp @@ -338,7 +338,6 @@ bool QGLPixmapBlurFilter::processGL(QPainter *painter, const QPointF &pos, const QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx *>(painter->paintEngine()); - engine->syncState(); painter->save(); // ensure GL_LINEAR filtering is used diff --git a/src/openvg/qpaintengine_vg.cpp b/src/openvg/qpaintengine_vg.cpp index d2c7b8b..09eb646 100644 --- a/src/openvg/qpaintengine_vg.cpp +++ b/src/openvg/qpaintengine_vg.cpp @@ -3072,43 +3072,42 @@ void QVGPaintEngine::setState(QPainterState *s) } } -// Called from QPaintEngine::syncState() to force a state flush. -// This should be called before and after raw VG operations. -void QVGPaintEngine::updateState(const QPaintEngineState &state) +void QVGPaintEngine::beginNativePainting() { - Q_UNUSED(state); Q_D(QVGPaintEngine); - if (!(d->rawVG)) { - // About to enter raw VG mode: flush pending changes and make - // sure that all matrices are set to the current transformation. - QVGPainterState *s = this->state(); - d->ensurePen(s->pen); - d->ensureBrush(s->brush); - d->ensurePathTransform(); - d->setTransform(VG_MATRIX_IMAGE_USER_TO_SURFACE, d->imageTransform); + // About to enter raw VG mode: flush pending changes and make + // sure that all matrices are set to the current transformation. + QVGPainterState *s = this->state(); + d->ensurePen(s->pen); + d->ensureBrush(s->brush); + d->ensurePathTransform(); + d->setTransform(VG_MATRIX_IMAGE_USER_TO_SURFACE, d->imageTransform); #if !defined(QVG_NO_DRAW_GLYPHS) - d->setTransform(VG_MATRIX_GLYPH_USER_TO_SURFACE, d->pathTransform); + d->setTransform(VG_MATRIX_GLYPH_USER_TO_SURFACE, d->pathTransform); #endif - d->rawVG = true; - } else { - // Exiting raw VG mode: force all state values to be - // explicitly set on the VG engine to undo any changes - // that were made by the raw VG function calls. - QPaintEngine::DirtyFlags dirty = d->dirty; - d->clearModes(); - d->forcePenChange = true; - d->forceBrushChange = true; - d->penType = (VGPaintType)0; - d->brushType = (VGPaintType)0; - d->clearColor = QColor(); - d->fillPaint = d->brushPaint; - restoreState(QPaintEngine::AllDirty); - d->dirty = dirty; - d->rawVG = false; - vgSetPaint(d->penPaint, VG_STROKE_PATH); - vgSetPaint(d->brushPaint, VG_FILL_PATH); - } + d->rawVG = true; +} + +void QVGPaintEngine::endNativePainting() +{ + Q_D(QVGPaintEngine); + // Exiting raw VG mode: force all state values to be + // explicitly set on the VG engine to undo any changes + // that were made by the raw VG function calls. + QPaintEngine::DirtyFlags dirty = d->dirty; + d->clearModes(); + d->forcePenChange = true; + d->forceBrushChange = true; + d->penType = (VGPaintType)0; + d->brushType = (VGPaintType)0; + d->clearColor = QColor(); + d->fillPaint = d->brushPaint; + restoreState(QPaintEngine::AllDirty); + d->dirty = dirty; + d->rawVG = false; + vgSetPaint(d->penPaint, VG_STROKE_PATH); + vgSetPaint(d->brushPaint, VG_FILL_PATH); } QPixmapFilter *QVGPaintEngine::createPixmapFilter(int type) const diff --git a/src/openvg/qpaintengine_vg_p.h b/src/openvg/qpaintengine_vg_p.h index 469ec9e..f0a7838 100644 --- a/src/openvg/qpaintengine_vg_p.h +++ b/src/openvg/qpaintengine_vg_p.h @@ -140,7 +140,8 @@ public: QVGPainterState *state() { return static_cast<QVGPainterState *>(QPaintEngineEx::state()); } const QVGPainterState *state() const { return static_cast<const QVGPainterState *>(QPaintEngineEx::state()); } - void updateState(const QPaintEngineState &state); + void beginNativePainting(); + void endNativePainting(); QPixmapFilter *createPixmapFilter(int type) const; diff --git a/tests/auto/qabstractitemmodel/dynamictreemodel.cpp b/tests/auto/qabstractitemmodel/dynamictreemodel.cpp new file mode 100644 index 0000000..6c3e0cb --- /dev/null +++ b/tests/auto/qabstractitemmodel/dynamictreemodel.cpp @@ -0,0 +1,245 @@ +/* + Copyright (c) 2009 Stephen Kelly <steveire@gmail.com> + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "dynamictreemodel.h" + +#include <QHash> +#include <QList> +#include <QTimer> + +#include <QDebug> + +#include <kdebug.h> + +DynamicTreeModel::DynamicTreeModel(QObject *parent) + : QAbstractItemModel(parent), + nextId(1) +{ +} + +QModelIndex DynamicTreeModel::index(int row, int column, const QModelIndex &parent) const +{ +// if (column != 0) +// return QModelIndex(); + + + if ( column < 0 || row < 0 ) + return QModelIndex(); + + QList<QList<qint64> > childIdColumns = m_childItems.value(parent.internalId()); + + + if (childIdColumns.size() == 0) + return QModelIndex(); + + if (column >= childIdColumns.size()) + return QModelIndex(); + + QList<qint64> rowIds = childIdColumns.at(column); + + if ( row >= rowIds.size()) + return QModelIndex(); + + qint64 id = rowIds.at(row); + + return createIndex(row, column, reinterpret_cast<void *>(id)); + +} + +qint64 DynamicTreeModel::findParentId(qint64 searchId) const +{ + if (searchId <= 0) + return -1; + + QHashIterator<qint64, QList<QList<qint64> > > i(m_childItems); + while (i.hasNext()) + { + i.next(); + QListIterator<QList<qint64> > j(i.value()); + while (j.hasNext()) + { + QList<qint64> l = j.next(); + if (l.contains(searchId)) + { + return i.key(); + } + } + } + return -1; +} + +QModelIndex DynamicTreeModel::parent(const QModelIndex &index) const +{ + if (!index.isValid()) + return QModelIndex(); + + qint64 searchId = index.internalId(); + qint64 parentId = findParentId(searchId); + // Will never happen for valid index, but what the hey... + if (parentId <= 0) + return QModelIndex(); + + qint64 grandParentId = findParentId(parentId); + if (grandParentId < 0) + grandParentId = 0; + + int column = 0; + QList<qint64> childList = m_childItems.value(grandParentId).at(column); + + int row = childList.indexOf(parentId); + + return createIndex(row, column, reinterpret_cast<void *>(parentId)); + +} + +int DynamicTreeModel::rowCount(const QModelIndex &index ) const +{ + QList<QList<qint64> > cols = m_childItems.value(index.internalId()); + + if (cols.size() == 0 ) + return 0; + + if (index.column() > 0) + return 0; + + return cols.at(0).size(); +} + +int DynamicTreeModel::columnCount(const QModelIndex &index ) const +{ +// Q_UNUSED(index); + return m_childItems.value(index.internalId()).size(); +} + +QVariant DynamicTreeModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (Qt::DisplayRole == role) + { + return m_items.value(index.internalId()); + } + return QVariant(); +} + +void DynamicTreeModel::clear() +{ + m_items.clear(); + m_childItems.clear(); + nextId = 1; + reset(); +} + + +ModelChangeCommand::ModelChangeCommand( DynamicTreeModel *model, QObject *parent ) + : QObject(parent), m_model(model), m_numCols(1), m_startRow(-1), m_endRow(-1) +{ + +} + +QModelIndex ModelChangeCommand::findIndex(QList<int> rows) +{ + const int col = 0; + QModelIndex parent = QModelIndex(); + QListIterator<int> i(rows); + while (i.hasNext()) + { + parent = m_model->index(i.next(), col, parent); + Q_ASSERT(parent.isValid()); + } + return parent; +} + +ModelInsertCommand::ModelInsertCommand(DynamicTreeModel *model, QObject *parent ) + : ModelChangeCommand(model, parent) +{ + +} + +void ModelInsertCommand::doCommand() +{ + QModelIndex parent = findIndex(m_rowNumbers); + m_model->beginInsertRows(parent, m_startRow, m_endRow); + qint64 parentId = parent.internalId(); + for (int row = m_startRow; row <= m_endRow; row++) + { + for(int col = 0; col < m_numCols; col++ ) + { + if (m_model->m_childItems[parentId].size() <= col) + { + m_model->m_childItems[parentId].append(QList<qint64>()); + } +// QString name = QUuid::createUuid().toString(); + qint64 id = m_model->newId(); + QString name = QString::number(id); + + m_model->m_items.insert(id, name); + m_model->m_childItems[parentId][col].insert(row, id); + + } + } + m_model->endInsertRows(); +} + + +ModelMoveCommand::ModelMoveCommand(DynamicTreeModel *model, QObject *parent) + : ModelChangeCommand(model, parent) +{ + +} + +void ModelMoveCommand::doCommand() +{ + QModelIndex srcParent = findIndex(m_rowNumbers); + QModelIndex destParent = findIndex(m_destRowNumbers); + + if (!m_model->beginMoveRows(srcParent, m_startRow, m_endRow, destParent, m_destRow)) + { + return; + } + + for (int column = 0; column < m_numCols; ++column) + { + QList<qint64> l = m_model->m_childItems.value(srcParent.internalId())[column].mid(m_startRow, m_endRow - m_startRow + 1 ); + + for (int i = m_startRow; i <= m_endRow ; i++) + { + m_model->m_childItems[srcParent.internalId()][column].removeAt(m_startRow); + } + int d; + if (m_destRow < m_startRow) + d = m_destRow; + else + { + if (srcParent == destParent) + d = m_destRow - (m_endRow - m_startRow + 1); + else + d = m_destRow - (m_endRow - m_startRow) + 1; + } + + foreach(const qint64 id, l) + { + m_model->m_childItems[destParent.internalId()][column].insert(d++, id); + } + } + + m_model->endMoveRows(); +} + diff --git a/tests/auto/qabstractitemmodel/dynamictreemodel.h b/tests/auto/qabstractitemmodel/dynamictreemodel.h new file mode 100644 index 0000000..88e293c --- /dev/null +++ b/tests/auto/qabstractitemmodel/dynamictreemodel.h @@ -0,0 +1,141 @@ +/* + Copyright (c) 2009 Stephen Kelly <steveire@gmail.com> + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef DYNAMICTREEMODEL_H +#define DYNAMICTREEMODEL_H + +#include <QAbstractItemModel> + +#include <QHash> +#include <QList> + +#include <QDebug> + +#include <kdebug.h> + +template<typename T> class QList; + +class DynamicTreeModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + DynamicTreeModel(QObject *parent = 0); + + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &index) const; + int rowCount(const QModelIndex &index = QModelIndex()) const; + int columnCount(const QModelIndex &index = QModelIndex()) const; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + + void clear(); + +protected slots: + + /** + Finds the parent id of the string with id @p searchId. + + Returns -1 if not found. + */ + qint64 findParentId(qint64 searchId) const; + +private: + QHash<qint64, QString> m_items; + QHash<qint64, QList<QList<qint64> > > m_childItems; + qint64 nextId; + qint64 newId() { return nextId++; }; + + QModelIndex m_nextParentIndex; + int m_nextRow; + + int m_depth; + int maxDepth; + + friend class ModelInsertCommand; + friend class ModelMoveCommand; + +}; + + +class ModelChangeCommand : public QObject +{ + Q_OBJECT +public: + + ModelChangeCommand( DynamicTreeModel *model, QObject *parent = 0 ); + + virtual ~ModelChangeCommand() {} + + void setAncestorRowNumbers(QList<int> rowNumbers) { m_rowNumbers = rowNumbers; } + + QModelIndex findIndex(QList<int> rows); + + void setStartRow(int row) { m_startRow = row; } + + void setEndRow(int row) { m_endRow = row; } + + void setNumCols(int cols) { m_numCols = cols; } + + virtual void doCommand() = 0; + +protected: + DynamicTreeModel* m_model; + QList<int> m_rowNumbers; + int m_numCols; + int m_startRow; + int m_endRow; + +}; + +typedef QList<ModelChangeCommand*> ModelChangeCommandList; + +class ModelInsertCommand : public ModelChangeCommand +{ + Q_OBJECT + +public: + + ModelInsertCommand(DynamicTreeModel *model, QObject *parent = 0 ); + virtual ~ModelInsertCommand() {} + + virtual void doCommand(); +}; + +class ModelMoveCommand : public ModelChangeCommand +{ + Q_OBJECT +public: + ModelMoveCommand(DynamicTreeModel *model, QObject *parent); + + virtual ~ModelMoveCommand() {} + + virtual void doCommand(); + + void setDestAncestors( QList<int> rows ) { m_destRowNumbers = rows; } + + void setDestRow(int row) { m_destRow = row; } + +protected: + QList<int> m_destRowNumbers; + int m_destRow; +}; + + +#endif diff --git a/tests/auto/qabstractitemmodel/qabstractitemmodel.pro b/tests/auto/qabstractitemmodel/qabstractitemmodel.pro index 5ad1020..84ed5a2 100644 --- a/tests/auto/qabstractitemmodel/qabstractitemmodel.pro +++ b/tests/auto/qabstractitemmodel/qabstractitemmodel.pro @@ -1,3 +1,6 @@ load(qttest_p4) -SOURCES += tst_qabstractitemmodel.cpp +SOURCES += tst_qabstractitemmodel.cpp dynamictreemodel.cpp +HEADERS += dynamictreemodel.h + QT = core + diff --git a/tests/auto/qabstractitemmodel/tst_qabstractitemmodel.cpp b/tests/auto/qabstractitemmodel/tst_qabstractitemmodel.cpp index e99ce06..9c83474 100644 --- a/tests/auto/qabstractitemmodel/tst_qabstractitemmodel.cpp +++ b/tests/auto/qabstractitemmodel/tst_qabstractitemmodel.cpp @@ -46,6 +46,10 @@ //TESTED_CLASS=QAbstractListModel QAbstractTableModel //TESTED_FILES= +#include "dynamictreemodel.h" + +Q_DECLARE_METATYPE(QModelIndex) + /*! Note that this doesn't test models, but any functionality that QAbstractItemModel shoudl provide */ @@ -86,6 +90,30 @@ private slots: void complexChangesWithPersistent(); + void testMoveSameParentUp_data(); + void testMoveSameParentUp(); + + void testMoveSameParentDown_data(); + void testMoveSameParentDown(); + + void testMoveToGrandParent_data(); + void testMoveToGrandParent(); + + void testMoveToSibling_data(); + void testMoveToSibling(); + + void testMoveToUncle_data(); + void testMoveToUncle(); + + void testMoveToDescendants(); + + void testMoveWithinOwnRange_data(); + void testMoveWithinOwnRange(); + + +private: + DynamicTreeModel *m_model; + }; /*! @@ -242,7 +270,20 @@ void tst_QAbstractItemModel::cleanupTestCase() void tst_QAbstractItemModel::init() { - + m_model = new DynamicTreeModel(this); + + ModelInsertCommand *insertCommand = new ModelInsertCommand(m_model, this); + insertCommand->setNumCols(4); + insertCommand->setStartRow(0); + insertCommand->setEndRow(9); + insertCommand->doCommand(); + + insertCommand = new ModelInsertCommand(m_model, this); + insertCommand->setAncestorRowNumbers(QList<int>() << 5); + insertCommand->setNumCols(4); + insertCommand->setStartRow(0); + insertCommand->setEndRow(9); + insertCommand->doCommand(); } void tst_QAbstractItemModel::cleanup() @@ -815,5 +856,803 @@ void tst_QAbstractItemModel::complexChangesWithPersistent() } +void tst_QAbstractItemModel::testMoveSameParentDown_data() +{ + QTest::addColumn<int>("startRow"); + QTest::addColumn<int>("endRow"); + QTest::addColumn<int>("destRow"); + + // Move from the start to the middle + QTest::newRow("move01") << 0 << 2 << 8; + // Move from the start to the end + QTest::newRow("move02") << 0 << 2 << 10; + // Move from the middle to the middle + QTest::newRow("move03") << 3 << 5 << 8; + // Move from the middle to the end + QTest::newRow("move04") << 3 << 5 << 10; +} + +void tst_QAbstractItemModel::testMoveSameParentDown() +{ + QFETCH( int, startRow); + QFETCH( int, endRow); + QFETCH( int, destRow); + + QList<QPersistentModelIndex> persistentList; + QModelIndexList indexList; + + for (int column = 0; column < m_model->columnCount(); ++column) + { + for (int row= 0; row < m_model->rowCount(); ++row) + { + QModelIndex idx = m_model->index(row, column); + QVERIFY(idx.isValid()); + indexList << idx; + persistentList << QPersistentModelIndex(idx); + } + } + + QModelIndex parent = m_model->index(5, 0); + for (int column = 0; column < m_model->columnCount(); ++column) + { + for (int row= 0; row < m_model->rowCount(parent); ++row) + { + QModelIndex idx = m_model->index(row, column, parent); + QVERIFY(idx.isValid()); + indexList << idx; + persistentList << QPersistentModelIndex(idx); + } + } + + QSignalSpy beforeSpy(m_model, SIGNAL(rowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int))); + QSignalSpy afterSpy(m_model, SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int))); + + ModelMoveCommand *moveCommand = new ModelMoveCommand(m_model, this); + moveCommand->setNumCols(4); + moveCommand->setStartRow(startRow); + moveCommand->setEndRow(endRow); + moveCommand->setDestRow(destRow); + moveCommand->doCommand(); + + QVariantList beforeSignal = beforeSpy.takeAt(0); + QVariantList afterSignal = afterSpy.takeAt(0); + + QCOMPARE(beforeSignal.size(), 5); + QCOMPARE(beforeSignal.at(0).value<QModelIndex>(), QModelIndex()); + QCOMPARE(beforeSignal.at(1).toInt(), startRow); + QCOMPARE(beforeSignal.at(2).toInt(), endRow); + QCOMPARE(beforeSignal.at(3).value<QModelIndex>(), QModelIndex()); + QCOMPARE(beforeSignal.at(4).toInt(), destRow); + + QCOMPARE(afterSignal.size(), 5); + QCOMPARE(afterSignal.at(0).value<QModelIndex>(), QModelIndex()); + QCOMPARE(afterSignal.at(1).toInt(), startRow); + QCOMPARE(afterSignal.at(2).toInt(), endRow); + QCOMPARE(afterSignal.at(3).value<QModelIndex>(), QModelIndex()); + QCOMPARE(afterSignal.at(4).toInt(), destRow); + + for (int i = 0; i < indexList.size(); i++) + { + QModelIndex idx = indexList.at(i); + QModelIndex persistentIndex = persistentList.at(i); + if (idx.parent() == QModelIndex()) + { + int row = idx.row(); + if ( row >= startRow) + { + if (row <= endRow) + { + QCOMPARE(row + destRow - endRow - 1, persistentIndex.row() ); + QCOMPARE(idx.column(), persistentIndex.column()); + QCOMPARE(idx.parent(), persistentIndex.parent()); + QCOMPARE(idx.model(), persistentIndex.model()); + } else if ( row < destRow) + { + QCOMPARE(row - (endRow - startRow + 1), persistentIndex.row() ); + QCOMPARE(idx.column(), persistentIndex.column()); + QCOMPARE(idx.parent(), persistentIndex.parent()); + QCOMPARE(idx.model(), persistentIndex.model()); + } else + { + QCOMPARE(idx, persistentIndex); + } + } else + { + QCOMPARE(idx, persistentIndex); + } + } else + { + QCOMPARE(idx, persistentIndex); + } + } +} + +void tst_QAbstractItemModel::testMoveSameParentUp_data() +{ + QTest::addColumn<int>("startRow"); + QTest::addColumn<int>("endRow"); + QTest::addColumn<int>("destRow"); + + // Move from the middle to the start + QTest::newRow("move01") << 5 << 7 << 0; + // Move from the end to the start + QTest::newRow("move02") << 8 << 9 << 0; + // Move from the middle to the middle + QTest::newRow("move03") << 5 << 7 << 2; + // Move from the end to the middle + QTest::newRow("move04") << 8 << 9 << 5; +} + +void tst_QAbstractItemModel::testMoveSameParentUp() +{ + + QFETCH( int, startRow); + QFETCH( int, endRow); + QFETCH( int, destRow); + + QList<QPersistentModelIndex> persistentList; + QModelIndexList indexList; + + for (int column = 0; column < m_model->columnCount(); ++column) + { + for (int row= 0; row < m_model->rowCount(); ++row) + { + QModelIndex idx = m_model->index(row, column); + QVERIFY(idx.isValid()); + indexList << idx; + persistentList << QPersistentModelIndex(idx); + } + } + + QModelIndex parent = m_model->index(2, 0); + for (int column = 0; column < m_model->columnCount(); ++column) + { + for (int row= 0; row < m_model->rowCount(parent); ++row) + { + QModelIndex idx = m_model->index(row, column, parent); + QVERIFY(idx.isValid()); + indexList << idx; + persistentList << QPersistentModelIndex(idx); + } + } + + QSignalSpy beforeSpy(m_model, SIGNAL(rowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int))); + QSignalSpy afterSpy(m_model, SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int))); + + + ModelMoveCommand *moveCommand = new ModelMoveCommand(m_model, this); + moveCommand->setNumCols(4); + moveCommand->setStartRow(startRow); + moveCommand->setEndRow(endRow); + moveCommand->setDestRow(destRow); + moveCommand->doCommand(); + + QVariantList beforeSignal = beforeSpy.takeAt(0); + QVariantList afterSignal = afterSpy.takeAt(0); + + QCOMPARE(beforeSignal.size(), 5); + QCOMPARE(beforeSignal.at(0).value<QModelIndex>(), QModelIndex()); + QCOMPARE(beforeSignal.at(1).toInt(), startRow); + QCOMPARE(beforeSignal.at(2).toInt(), endRow); + QCOMPARE(beforeSignal.at(3).value<QModelIndex>(), QModelIndex()); + QCOMPARE(beforeSignal.at(4).toInt(), destRow); + + QCOMPARE(afterSignal.size(), 5); + QCOMPARE(afterSignal.at(0).value<QModelIndex>(), QModelIndex()); + QCOMPARE(afterSignal.at(1).toInt(), startRow); + QCOMPARE(afterSignal.at(2).toInt(), endRow); + QCOMPARE(afterSignal.at(3).value<QModelIndex>(), QModelIndex()); + QCOMPARE(afterSignal.at(4).toInt(), destRow); + + + for (int i = 0; i < indexList.size(); i++) + { + QModelIndex idx = indexList.at(i); + QModelIndex persistentIndex = persistentList.at(i); + if (idx.parent() == QModelIndex()) + { + int row = idx.row(); + if ( row >= destRow) + { + if (row < startRow) + { + QCOMPARE(row + endRow - startRow + 1, persistentIndex.row() ); + QCOMPARE(idx.column(), persistentIndex.column()); + QCOMPARE(idx.parent(), persistentIndex.parent()); + QCOMPARE(idx.model(), persistentIndex.model()); + } else if ( row <= endRow) + { + QCOMPARE(row + destRow - startRow, persistentIndex.row() ); + QCOMPARE(idx.column(), persistentIndex.column()); + QCOMPARE(idx.parent(), persistentIndex.parent()); + QCOMPARE(idx.model(), persistentIndex.model()); + } else + { + QCOMPARE(idx, persistentIndex); + } + } else + { + QCOMPARE(idx, persistentIndex); + } + } else + { + QCOMPARE(idx, persistentIndex); + } + } +} + +void tst_QAbstractItemModel::testMoveToGrandParent_data() +{ + QTest::addColumn<int>("startRow"); + QTest::addColumn<int>("endRow"); + QTest::addColumn<int>("destRow"); + + // Move from the start to the middle + QTest::newRow("move01") << 0 << 2 << 8; + // Move from the start to the end + QTest::newRow("move02") << 0 << 2 << 10; + // Move from the middle to the middle + QTest::newRow("move03") << 3 << 5 << 8; + // Move from the middle to the end + QTest::newRow("move04") << 3 << 5 << 10; + + // Move from the middle to the start + QTest::newRow("move05") << 5 << 7 << 0; + // Move from the end to the start + QTest::newRow("move06") << 8 << 9 << 0; + // Move from the middle to the middle + QTest::newRow("move07") << 5 << 7 << 2; + // Move from the end to the middle + QTest::newRow("move08") << 8 << 9 << 5; + + // Moving to the same row in a different parent doesn't confuse things. + QTest::newRow("move09") << 8 << 8 << 8; + + // Moving to the row of my parent and its neighbours doesn't confuse things + QTest::newRow("move09") << 8 << 8 << 4; + QTest::newRow("move10") << 8 << 8 << 5; + QTest::newRow("move11") << 8 << 8 << 6; + + // Moving everything from one parent to another + QTest::newRow("move12") << 0 << 9 << 10; +} + +void tst_QAbstractItemModel::testMoveToGrandParent() +{ + + QFETCH( int, startRow); + QFETCH( int, endRow); + QFETCH( int, destRow); + + QList<QPersistentModelIndex> persistentList; + QModelIndexList indexList; + QModelIndexList parentsList; + + for (int column = 0; column < m_model->columnCount(); ++column) + { + for (int row= 0; row < m_model->rowCount(); ++row) + { + QModelIndex idx = m_model->index(row, column); + QVERIFY(idx.isValid()); + indexList << idx; + parentsList << idx.parent(); + persistentList << QPersistentModelIndex(idx); + } + } + + QModelIndex sourceIndex = m_model->index(5, 0); + for (int column = 0; column < m_model->columnCount(); ++column) + { + for (int row= 0; row < m_model->rowCount(sourceIndex); ++row) + { + QModelIndex idx = m_model->index(row, column, sourceIndex); + QVERIFY(idx.isValid()); + indexList << idx; + parentsList << idx.parent(); + persistentList << QPersistentModelIndex(idx); + } + } + + QSignalSpy beforeSpy(m_model, SIGNAL(rowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int))); + QSignalSpy afterSpy(m_model, SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int))); + + + ModelMoveCommand *moveCommand = new ModelMoveCommand(m_model, this); + moveCommand->setAncestorRowNumbers(QList<int>() << 5); + moveCommand->setNumCols(4); + moveCommand->setStartRow(startRow); + moveCommand->setEndRow(endRow); + moveCommand->setDestRow(destRow); + moveCommand->doCommand(); + + QVariantList beforeSignal = beforeSpy.takeAt(0); + QVariantList afterSignal = afterSpy.takeAt(0); + + QCOMPARE(beforeSignal.size(), 5); + QCOMPARE(beforeSignal.at(0).value<QModelIndex>(), sourceIndex); + QCOMPARE(beforeSignal.at(1).toInt(), startRow); + QCOMPARE(beforeSignal.at(2).toInt(), endRow); + QCOMPARE(beforeSignal.at(3).value<QModelIndex>(), QModelIndex()); + QCOMPARE(beforeSignal.at(4).toInt(), destRow); + + QCOMPARE(afterSignal.size(), 5); + QCOMPARE(afterSignal.at(0).value<QModelIndex>(), sourceIndex); + QCOMPARE(afterSignal.at(1).toInt(), startRow); + QCOMPARE(afterSignal.at(2).toInt(), endRow); + QCOMPARE(afterSignal.at(3).value<QModelIndex>(), QModelIndex()); + QCOMPARE(afterSignal.at(4).toInt(), destRow); + + for (int i = 0; i < indexList.size(); i++) + { + QModelIndex idx = indexList.at(i); + QModelIndex idxParent = parentsList.at(i); + QModelIndex persistentIndex = persistentList.at(i); + int row = idx.row(); + if (idxParent == QModelIndex()) + { + if ( row >= destRow) + { + QCOMPARE(row + endRow - startRow + 1, persistentIndex.row() ); + QCOMPARE(idx.column(), persistentIndex.column()); + QCOMPARE(idxParent, persistentIndex.parent()); + QCOMPARE(idx.model(), persistentIndex.model()); + } else + { + QCOMPARE(idx, persistentIndex); + } + } else + { + if (row < startRow) + { + QCOMPARE(idx, persistentIndex); + } else if (row <= endRow) + { + QCOMPARE(row + destRow - startRow, persistentIndex.row() ); + QCOMPARE(idx.column(), persistentIndex.column()); + QCOMPARE(QModelIndex(), persistentIndex.parent()); + QCOMPARE(idx.model(), persistentIndex.model()); + } else { + QCOMPARE(row - (endRow - startRow + 1), persistentIndex.row() ); + QCOMPARE(idx.column(), persistentIndex.column()); + + if (idxParent.row() >= destRow) + { + QModelIndex adjustedParent; + adjustedParent = idxParent.sibling( idxParent.row() + endRow - startRow + 1, idxParent.column()); + QCOMPARE(adjustedParent, persistentIndex.parent()); + } else + { + QCOMPARE(idxParent, persistentIndex.parent()); + } + QCOMPARE(idx.model(), persistentIndex.model()); + } + } + } +} + +void tst_QAbstractItemModel::testMoveToSibling_data() +{ + QTest::addColumn<int>("startRow"); + QTest::addColumn<int>("endRow"); + QTest::addColumn<int>("destRow"); + + // Move from the start to the middle + QTest::newRow("move01") << 0 << 2 << 8; + // Move from the start to the end + QTest::newRow("move02") << 0 << 2 << 10; + // Move from the middle to the middle + QTest::newRow("move03") << 2 << 4 << 8; + // Move from the middle to the end + QTest::newRow("move04") << 2 << 4 << 10; + + // Move from the middle to the start + QTest::newRow("move05") << 8 << 8 << 0; + // Move from the end to the start + QTest::newRow("move06") << 8 << 9 << 0; + // Move from the middle to the middle + QTest::newRow("move07") << 6 << 8 << 2; + // Move from the end to the middle + QTest::newRow("move08") << 8 << 9 << 5; + + // Moving to the same row in a different parent doesn't confuse things. + QTest::newRow("move09") << 8 << 8 << 8; + + // Moving to the row of my target and its neighbours doesn't confuse things + QTest::newRow("move09") << 8 << 8 << 4; + QTest::newRow("move10") << 8 << 8 << 5; + QTest::newRow("move11") << 8 << 8 << 6; +} + +void tst_QAbstractItemModel::testMoveToSibling() +{ + + QFETCH( int, startRow); + QFETCH( int, endRow); + QFETCH( int, destRow); + + QList<QPersistentModelIndex> persistentList; + QModelIndexList indexList; + QModelIndexList parentsList; + + const int column = 0; + + for (int i= 0; i < m_model->rowCount(); ++i) + { + QModelIndex idx = m_model->index(i, column); + QVERIFY(idx.isValid()); + indexList << idx; + parentsList << idx.parent(); + persistentList << QPersistentModelIndex(idx); + } + + QModelIndex destIndex = m_model->index(5, 0); + QModelIndex sourceIndex; + for (int i= 0; i < m_model->rowCount(destIndex); ++i) + { + QModelIndex idx = m_model->index(i, column, destIndex); + QVERIFY(idx.isValid()); + indexList << idx; + parentsList << idx.parent(); + persistentList << QPersistentModelIndex(idx); + } + + QSignalSpy beforeSpy(m_model, SIGNAL(rowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int))); + QSignalSpy afterSpy(m_model, SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int))); + + + ModelMoveCommand *moveCommand = new ModelMoveCommand(m_model, this); + moveCommand->setNumCols(4); + moveCommand->setStartRow(startRow); + moveCommand->setEndRow(endRow); + moveCommand->setDestAncestors(QList<int>() << 5); + moveCommand->setDestRow(destRow); + moveCommand->doCommand(); + + QVariantList beforeSignal = beforeSpy.takeAt(0); + QVariantList afterSignal = afterSpy.takeAt(0); + + QCOMPARE(beforeSignal.size(), 5); + QCOMPARE(beforeSignal.at(0).value<QModelIndex>(), sourceIndex); + QCOMPARE(beforeSignal.at(1).toInt(), startRow); + QCOMPARE(beforeSignal.at(2).toInt(), endRow); + QCOMPARE(beforeSignal.at(3).value<QModelIndex>(), destIndex); + QCOMPARE(beforeSignal.at(4).toInt(), destRow); + + QCOMPARE(afterSignal.size(), 5); + QCOMPARE(afterSignal.at(0).value<QModelIndex>(), sourceIndex); + QCOMPARE(afterSignal.at(1).toInt(), startRow); + QCOMPARE(afterSignal.at(2).toInt(), endRow); + QCOMPARE(afterSignal.at(3).value<QModelIndex>(), destIndex); + QCOMPARE(afterSignal.at(4).toInt(), destRow); + + for (int i = 0; i < indexList.size(); i++) + { + QModelIndex idx = indexList.at(i); + QModelIndex idxParent = parentsList.at(i); + QModelIndex persistentIndex = persistentList.at(i); + + QModelIndex adjustedDestination = destIndex.sibling(destIndex.row() - (endRow - startRow + 1), destIndex.column()); + int row = idx.row(); + if (idxParent == destIndex) + { + if ( row >= destRow) + { + QCOMPARE(row + endRow - startRow + 1, persistentIndex.row() ); + QCOMPARE(idx.column(), persistentIndex.column()); + if (idxParent.row() > startRow) + { + QCOMPARE(adjustedDestination, persistentIndex.parent()); + } else { + QCOMPARE(destIndex, persistentIndex.parent()); + } + QCOMPARE(idx.model(), persistentIndex.model()); + } else + { + QCOMPARE(idx, persistentIndex); + } + } else + { + if (row < startRow) + { + QCOMPARE(idx, persistentIndex); + } else if (row <= endRow) + { + QCOMPARE(row + destRow - startRow, persistentIndex.row() ); + QCOMPARE(idx.column(), persistentIndex.column()); + if (destIndex.row() > startRow) + { + QCOMPARE(adjustedDestination, persistentIndex.parent()); + } else { + QCOMPARE(destIndex, persistentIndex.parent()); + } + + QCOMPARE(idx.model(), persistentIndex.model()); + + } else { + QCOMPARE(row - (endRow - startRow + 1), persistentIndex.row() ); + QCOMPARE(idx.column(), persistentIndex.column()); + QCOMPARE(idxParent, persistentIndex.parent()); + QCOMPARE(idx.model(), persistentIndex.model()); + } + } + } +} + +void tst_QAbstractItemModel::testMoveToUncle_data() +{ + + QTest::addColumn<int>("startRow"); + QTest::addColumn<int>("endRow"); + QTest::addColumn<int>("destRow"); + + // Move from the start to the middle + QTest::newRow("move01") << 0 << 2 << 8; + // Move from the start to the end + QTest::newRow("move02") << 0 << 2 << 10; + // Move from the middle to the middle + QTest::newRow("move03") << 3 << 5 << 8; + // Move from the middle to the end + QTest::newRow("move04") << 3 << 5 << 10; + + // Move from the middle to the start + QTest::newRow("move05") << 5 << 7 << 0; + // Move from the end to the start + QTest::newRow("move06") << 8 << 9 << 0; + // Move from the middle to the middle + QTest::newRow("move07") << 5 << 7 << 2; + // Move from the end to the middle + QTest::newRow("move08") << 8 << 9 << 5; + + // Moving to the same row in a different parent doesn't confuse things. + QTest::newRow("move09") << 8 << 8 << 8; + + // Moving to the row of my parent and its neighbours doesn't confuse things + QTest::newRow("move09") << 8 << 8 << 4; + QTest::newRow("move10") << 8 << 8 << 5; + QTest::newRow("move11") << 8 << 8 << 6; + + // Moving everything from one parent to another + QTest::newRow("move12") << 0 << 9 << 10; +} + +void tst_QAbstractItemModel::testMoveToUncle() +{ + // Need to have some extra rows available. + ModelInsertCommand *insertCommand = new ModelInsertCommand(m_model, this); + insertCommand->setAncestorRowNumbers(QList<int>() << 9); + insertCommand->setNumCols(4); + insertCommand->setStartRow(0); + insertCommand->setEndRow(9); + insertCommand->doCommand(); + + QFETCH( int, startRow); + QFETCH( int, endRow); + QFETCH( int, destRow); + + QList<QPersistentModelIndex> persistentList; + QModelIndexList indexList; + QModelIndexList parentsList; + + const int column = 0; + + QModelIndex sourceIndex = m_model->index(9, 0); + for (int i= 0; i < m_model->rowCount(sourceIndex); ++i) + { + QModelIndex idx = m_model->index(i, column, sourceIndex); + QVERIFY(idx.isValid()); + indexList << idx; + parentsList << idx.parent(); + persistentList << QPersistentModelIndex(idx); + } + + QModelIndex destIndex = m_model->index(5, 0); + for (int i= 0; i < m_model->rowCount(destIndex); ++i) + { + QModelIndex idx = m_model->index(i, column, destIndex); + QVERIFY(idx.isValid()); + indexList << idx; + parentsList << idx.parent(); + persistentList << QPersistentModelIndex(idx); + } + + QSignalSpy beforeSpy(m_model, SIGNAL(rowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int))); + QSignalSpy afterSpy(m_model, SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int))); + + ModelMoveCommand *moveCommand = new ModelMoveCommand(m_model, this); + moveCommand->setAncestorRowNumbers(QList<int>() << 9); + moveCommand->setNumCols(4); + moveCommand->setStartRow(startRow); + moveCommand->setEndRow(endRow); + moveCommand->setDestAncestors(QList<int>() << 5); + moveCommand->setDestRow(destRow); + moveCommand->doCommand(); + + QVariantList beforeSignal = beforeSpy.takeAt(0); + QVariantList afterSignal = afterSpy.takeAt(0); + + QCOMPARE(beforeSignal.size(), 5); + QCOMPARE(beforeSignal.at(0).value<QModelIndex>(), sourceIndex); + QCOMPARE(beforeSignal.at(1).toInt(), startRow); + QCOMPARE(beforeSignal.at(2).toInt(), endRow); + QCOMPARE(beforeSignal.at(3).value<QModelIndex>(), destIndex); + QCOMPARE(beforeSignal.at(4).toInt(), destRow); + + QCOMPARE(afterSignal.size(), 5); + QCOMPARE(afterSignal.at(0).value<QModelIndex>(), sourceIndex); + QCOMPARE(afterSignal.at(1).toInt(), startRow); + QCOMPARE(afterSignal.at(2).toInt(), endRow); + QCOMPARE(afterSignal.at(3).value<QModelIndex>(), destIndex); + QCOMPARE(afterSignal.at(4).toInt(), destRow); + + for (int i = 0; i < indexList.size(); i++) + { + QModelIndex idx = indexList.at(i); + QModelIndex idxParent = parentsList.at(i); + QModelIndex persistentIndex = persistentList.at(i); + + int row = idx.row(); + if (idxParent == destIndex) + { + if ( row >= destRow) + { + QCOMPARE(row + endRow - startRow + 1, persistentIndex.row() ); + QCOMPARE(idx.column(), persistentIndex.column()); + QCOMPARE(destIndex, persistentIndex.parent()); + QCOMPARE(idx.model(), persistentIndex.model()); + } else + { + QCOMPARE(idx, persistentIndex); + } + } else + { + if (row < startRow) + { + QCOMPARE(idx, persistentIndex); + } else if (row <= endRow) + { + QCOMPARE(row + destRow - startRow, persistentIndex.row() ); + QCOMPARE(idx.column(), persistentIndex.column()); + QCOMPARE(destIndex, persistentIndex.parent()); + QCOMPARE(idx.model(), persistentIndex.model()); + + } else { + QCOMPARE(row - (endRow - startRow + 1), persistentIndex.row() ); + QCOMPARE(idx.column(), persistentIndex.column()); + QCOMPARE(idxParent, persistentIndex.parent()); + QCOMPARE(idx.model(), persistentIndex.model()); + } + } + } +} + +void tst_QAbstractItemModel::testMoveToDescendants() +{ + // Attempt to move a row to its ancestors depth rows deep. + const int depth = 6; + + // Need to have some extra rows available in a tree. + QList<int> rows; + ModelInsertCommand *insertCommand; + for (int i = 0; i < depth; i++) + { + insertCommand = new ModelInsertCommand(m_model, this); + insertCommand->setAncestorRowNumbers(rows); + insertCommand->setNumCols(4); + insertCommand->setStartRow(0); + insertCommand->setEndRow(9); + insertCommand->doCommand(); + rows << 9; + } + + QList<QPersistentModelIndex> persistentList; + QModelIndexList indexList; + QModelIndexList parentsList; + + const int column = 0; + + QModelIndex sourceIndex = m_model->index(9, 0); + for (int i= 0; i < m_model->rowCount(sourceIndex); ++i) + { + QModelIndex idx = m_model->index(i, column, sourceIndex); + QVERIFY(idx.isValid()); + indexList << idx; + parentsList << idx.parent(); + persistentList << QPersistentModelIndex(idx); + } + + QModelIndex destIndex = m_model->index(5, 0); + for (int i= 0; i < m_model->rowCount(destIndex); ++i) + { + QModelIndex idx = m_model->index(i, column, destIndex); + QVERIFY(idx.isValid()); + indexList << idx; + parentsList << idx.parent(); + persistentList << QPersistentModelIndex(idx); + } + + QSignalSpy beforeSpy(m_model, SIGNAL(rowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int))); + QSignalSpy afterSpy(m_model, SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int))); + + ModelMoveCommand *moveCommand; + QList<int> ancestors; + while (ancestors.size() < depth) + { + ancestors << 9; + for (int row = 0; row <= 9; row++) + { + moveCommand = new ModelMoveCommand(m_model, this); + moveCommand->setNumCols(4); + moveCommand->setStartRow(9); + moveCommand->setEndRow(9); + moveCommand->setDestAncestors(ancestors); + moveCommand->setDestRow(row); + moveCommand->doCommand(); + + QVERIFY(beforeSpy.size() == 0); + QVERIFY(afterSpy.size() == 0); + } + } +} + +void tst_QAbstractItemModel::testMoveWithinOwnRange_data() +{ + QTest::addColumn<int>("startRow"); + QTest::addColumn<int>("endRow"); + QTest::addColumn<int>("destRow"); + + QTest::newRow("move01") << 0 << 0 << 0; + QTest::newRow("move02") << 0 << 0 << 1; + QTest::newRow("move03") << 0 << 5 << 0; + QTest::newRow("move04") << 0 << 5 << 1; + QTest::newRow("move05") << 0 << 5 << 2; + QTest::newRow("move06") << 0 << 5 << 3; + QTest::newRow("move07") << 0 << 5 << 4; + QTest::newRow("move08") << 0 << 5 << 5; + QTest::newRow("move09") << 0 << 5 << 6; + QTest::newRow("move08") << 3 << 5 << 5; + QTest::newRow("move08") << 3 << 5 << 6; + QTest::newRow("move09") << 4 << 5 << 5; + QTest::newRow("move10") << 4 << 5 << 6; + QTest::newRow("move11") << 5 << 5 << 5; + QTest::newRow("move12") << 5 << 5 << 6; + QTest::newRow("move13") << 5 << 9 << 9; + QTest::newRow("move14") << 5 << 9 << 10; + QTest::newRow("move15") << 6 << 9 << 9; + QTest::newRow("move16") << 6 << 9 << 10; + QTest::newRow("move17") << 7 << 9 << 9; + QTest::newRow("move18") << 7 << 9 << 10; + QTest::newRow("move19") << 8 << 9 << 9; + QTest::newRow("move20") << 8 << 9 << 10; + QTest::newRow("move21") << 9 << 9 << 9; + QTest::newRow("move22") << 0 << 9 << 10; + +} + +void tst_QAbstractItemModel::testMoveWithinOwnRange() +{ + + QFETCH( int, startRow); + QFETCH( int, endRow); + QFETCH( int, destRow); + + + QSignalSpy beforeSpy(m_model, SIGNAL(rowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int))); + QSignalSpy afterSpy(m_model, SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int))); + + ModelMoveCommand *moveCommand = new ModelMoveCommand(m_model, this); + moveCommand->setNumCols(4); + moveCommand->setStartRow(startRow); + moveCommand->setEndRow(endRow); + moveCommand->setDestRow(destRow); + moveCommand->doCommand(); + + QVERIFY(beforeSpy.size() == 0); + QVERIFY(afterSpy.size() == 0); + + +} + + + QTEST_MAIN(tst_QAbstractItemModel) #include "tst_qabstractitemmodel.moc" diff --git a/tests/auto/qobject/tst_qobject.cpp b/tests/auto/qobject/tst_qobject.cpp index 65dc742..b20c0e0 100644 --- a/tests/auto/qobject/tst_qobject.cpp +++ b/tests/auto/qobject/tst_qobject.cpp @@ -2933,7 +2933,7 @@ class OverloadObject : public QObject friend class tst_QObject; Q_OBJECT signals: - void sig(int i, char c, qreal m = 12) const; + void sig(int i, char c, qreal m = 12); void sig(int i, int j = 12); void sig(QObject *o, QObject *p, QObject *q = 0, QObject *r = 0) const; void other(int a = 0); |