diff options
Diffstat (limited to 'src/gui/itemviews')
-rw-r--r-- | src/gui/itemviews/qabstractitemview.cpp | 22 | ||||
-rw-r--r-- | src/gui/itemviews/qabstractitemview_p.h | 5 | ||||
-rw-r--r-- | src/gui/itemviews/qdirmodel.cpp | 10 | ||||
-rw-r--r-- | src/gui/itemviews/qfileiconprovider.cpp | 2 | ||||
-rw-r--r-- | src/gui/itemviews/qheaderview.cpp | 60 | ||||
-rw-r--r-- | src/gui/itemviews/qitemdelegate.cpp | 6 | ||||
-rw-r--r-- | src/gui/itemviews/qitemselectionmodel.cpp | 88 | ||||
-rw-r--r-- | src/gui/itemviews/qitemselectionmodel_p.h | 2 | ||||
-rw-r--r-- | src/gui/itemviews/qlistview.cpp | 14 | ||||
-rw-r--r-- | src/gui/itemviews/qproxymodel.h | 25 | ||||
-rw-r--r-- | src/gui/itemviews/qsortfilterproxymodel.cpp | 79 | ||||
-rw-r--r-- | src/gui/itemviews/qtableview.cpp | 45 | ||||
-rw-r--r-- | src/gui/itemviews/qtreeview.cpp | 457 | ||||
-rw-r--r-- | src/gui/itemviews/qtreeview_p.h | 21 |
14 files changed, 379 insertions, 457 deletions
diff --git a/src/gui/itemviews/qabstractitemview.cpp b/src/gui/itemviews/qabstractitemview.cpp index 2faf755..97fd6e1 100644 --- a/src/gui/itemviews/qabstractitemview.cpp +++ b/src/gui/itemviews/qabstractitemview.cpp @@ -104,8 +104,10 @@ QAbstractItemViewPrivate::QAbstractItemViewPrivate() horizontalScrollMode(QAbstractItemView::ScrollPerItem), currentIndexSet(false), wrapItemText(false), - delayedPendingLayout(false) + delayedPendingLayout(true), + moveCursorUpdatedView(false) { + keyboardInputTime.invalidate(); } QAbstractItemViewPrivate::~QAbstractItemViewPrivate() @@ -131,8 +133,6 @@ void QAbstractItemViewPrivate::init() viewport->setBackgroundRole(QPalette::Base); - doDelayedItemsLayout(); - q->setAttribute(Qt::WA_InputMethodEnabled); #ifdef QT_SOFTKEYS_ENABLED @@ -2090,7 +2090,7 @@ void QAbstractItemView::focusInEvent(QFocusEvent *event) bool autoScroll = d->autoScroll; d->autoScroll = false; QModelIndex index = moveCursor(MoveNext, Qt::NoModifier); // first visible index - if (index.isValid() && d->isIndexEnabled(index)) + if (index.isValid() && d->isIndexEnabled(index) && event->reason() != Qt::MouseFocusReason) selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate); d->autoScroll = autoScroll; } @@ -2210,6 +2210,7 @@ void QAbstractItemView::keyPressEvent(QKeyEvent *event) #endif QPersistentModelIndex newCurrent; + d->moveCursorUpdatedView = false; switch (event->key()) { case Qt::Key_Down: newCurrent = moveCursor(MoveDown, event->modifiers()); @@ -2266,6 +2267,7 @@ void QAbstractItemView::keyPressEvent(QKeyEvent *event) QRect rect(d->pressedPosition - d->offset(), QSize(1, 1)); setSelection(rect, command); } + event->accept(); return; } } @@ -2297,6 +2299,8 @@ void QAbstractItemView::keyPressEvent(QKeyEvent *event) case Qt::Key_Escape: case Qt::Key_Shift: case Qt::Key_Control: + case Qt::Key_Delete: + case Qt::Key_Backspace: event->ignore(); break; case Qt::Key_Space: @@ -2361,6 +2365,8 @@ void QAbstractItemView::keyPressEvent(QKeyEvent *event) } break; } } + if (d->moveCursorUpdatedView) + event->accept(); } /*! @@ -2837,16 +2843,16 @@ void QAbstractItemView::keyboardSearch(const QString &search) QModelIndex start = currentIndex().isValid() ? currentIndex() : d->model->index(0, 0, d->root); - QTime now(QTime::currentTime()); bool skipRow = false; - if (search.isEmpty() - || (d->keyboardInputTime.msecsTo(now) > QApplication::keyboardInputInterval())) { + bool keyboardTimeWasValid = d->keyboardInputTime.isValid(); + qint64 keyboardInputTimeElapsed = d->keyboardInputTime.restart(); + if (search.isEmpty() || !keyboardTimeWasValid + || keyboardInputTimeElapsed > QApplication::keyboardInputInterval()) { d->keyboardInput = search; skipRow = currentIndex().isValid(); //if it is not valid we should really start at QModelIndex(0,0) } else { d->keyboardInput += search; } - d->keyboardInputTime = now; // special case for searches with same key like 'aaaaa' bool sameKey = false; diff --git a/src/gui/itemviews/qabstractitemview_p.h b/src/gui/itemviews/qabstractitemview_p.h index 82fd1a6..fce74f3 100644 --- a/src/gui/itemviews/qabstractitemview_p.h +++ b/src/gui/itemviews/qabstractitemview_p.h @@ -56,7 +56,6 @@ #include "private/qabstractscrollarea_p.h" #include "private/qabstractitemmodel_p.h" #include "QtGui/qapplication.h" -#include "QtCore/qdatetime.h" #include "QtGui/qevent.h" #include "QtGui/qmime.h" #include "QtGui/qpainter.h" @@ -65,6 +64,7 @@ #include "QtCore/qdebug.h" #include "QtGui/qpainter.h" #include "QtCore/qbasictimer.h" +#include "QtCore/qelapsedtimer.h" #ifndef QT_NO_ITEMVIEWS @@ -390,7 +390,7 @@ public: #endif QString keyboardInput; - QTime keyboardInputTime; + QElapsedTimer keyboardInputTime; bool autoScroll; QBasicTimer autoScrollTimer; @@ -419,6 +419,7 @@ public: bool wrapItemText; mutable bool delayedPendingLayout; + bool moveCursorUpdatedView; private: mutable QBasicTimer delayedLayout; diff --git a/src/gui/itemviews/qdirmodel.cpp b/src/gui/itemviews/qdirmodel.cpp index ea608c1..48599bc 100644 --- a/src/gui/itemviews/qdirmodel.cpp +++ b/src/gui/itemviews/qdirmodel.cpp @@ -185,12 +185,12 @@ void QDirModelPrivate::invalidate() /*! \class QDirModel - + \obsolete \brief The QDirModel class provides a data model for the local filesystem. \ingroup model-view - \note The usage of QDirModel is not recommended anymore. The + The usage of QDirModel is not recommended anymore. The QFileSystemModel class is a more performant alternative. This class provides access to the local filesystem, providing functions @@ -1182,12 +1182,18 @@ QFileInfo QDirModel::fileInfo(const QModelIndex &index) const void QDirModelPrivate::init() { + Q_Q(QDirModel); filters = QDir::AllEntries | QDir::NoDotAndDotDot; sort = QDir::Name; nameFilters << QLatin1String("*"); root.parent = 0; root.info = QFileInfo(); clear(&root); + QHash<int, QByteArray> roles = q->roleNames(); + roles.insertMulti(QDirModel::FileIconRole, "fileIcon"); // == Qt::decoration + roles.insert(QDirModel::FilePathRole, "filePath"); + roles.insert(QDirModel::FileNameRole, "fileName"); + q->setRoleNames(roles); } QDirModelPrivate::QDirNode *QDirModelPrivate::node(int row, QDirNode *parent) const diff --git a/src/gui/itemviews/qfileiconprovider.cpp b/src/gui/itemviews/qfileiconprovider.cpp index fcc61e5..f321ab3 100644 --- a/src/gui/itemviews/qfileiconprovider.cpp +++ b/src/gui/itemviews/qfileiconprovider.cpp @@ -73,7 +73,7 @@ QT_BEGIN_NAMESPACE /*! \class QFileIconProvider - \brief The QFileIconProvider class provides file icons for the QDirModel class. + \brief The QFileIconProvider class provides file icons for the QDirModel and the QFileSystemModel classes. */ /*! diff --git a/src/gui/itemviews/qheaderview.cpp b/src/gui/itemviews/qheaderview.cpp index 8a456e6..cd9ee15 100644 --- a/src/gui/itemviews/qheaderview.cpp +++ b/src/gui/itemviews/qheaderview.cpp @@ -1698,13 +1698,10 @@ void QHeaderView::sectionsInserted(const QModelIndex &parent, if (!d->sectionHidden.isEmpty()) { QBitArray sectionHidden(d->sectionHidden); sectionHidden.resize(sectionHidden.count() + insertCount); - //sectionHidden.fill(false, logicalFirst, logicalLast + 1); - for (int i = logicalFirst; i <= logicalLast; ++i) - // visual == logical in this range (see previous block) - sectionHidden.setBit(i, false); + sectionHidden.fill(false, logicalFirst, logicalLast + 1); for (int j = logicalLast + 1; j < sectionHidden.count(); ++j) - sectionHidden.setBit(d->visualIndex(j), - d->sectionHidden.testBit(d->visualIndex(j - insertCount))); + //here we simply copy the old sectionHidden + sectionHidden.setBit(j, d->sectionHidden.testBit(j - insertCount)); d->sectionHidden = sectionHidden; } @@ -1853,11 +1850,9 @@ void QHeaderViewPrivate::_q_layoutChanged() persistentHiddenSections.clear(); return; } + + QBitArray oldSectionHidden = sectionHidden; bool sectionCountChanged = false; - for (int i = 0; i < sectionHidden.count(); ++i) { - if (sectionHidden.testBit(i)) - q->setSectionHidden(logicalIndex(i), false); - } for (int i = 0; i < persistentHiddenSections.count(); ++i) { QModelIndex index = persistentHiddenSections.at(i); @@ -1866,6 +1861,7 @@ void QHeaderViewPrivate::_q_layoutChanged() ? index.column() : index.row()); q->setSectionHidden(logical, true); + oldSectionHidden.setBit(logical, false); } else if (!sectionCountChanged && (modelSectionCount() != sectionCount)) { sectionCountChanged = true; break; @@ -1873,6 +1869,11 @@ void QHeaderViewPrivate::_q_layoutChanged() } persistentHiddenSections.clear(); + for (int i = 0; i < oldSectionHidden.count(); ++i) { + if (oldSectionHidden.testBit(i)) + q->setSectionHidden(logicalIndex(i), false); + } + // the number of sections changed; we need to reread the state of the model if (sectionCountChanged) q->initializeSections(); @@ -2033,7 +2034,7 @@ bool QHeaderView::event(QEvent *e) updateSection(d->hover); } break; } - case QEvent::Timer: { // ### reimplement timerEvent() instead ? + case QEvent::Timer: { QTimerEvent *te = static_cast<QTimerEvent*>(e); if (te->timerId() == d->delayedResize.timerId()) { d->delayedResize.stop(); @@ -2217,24 +2218,27 @@ void QHeaderView::mouseMoveEvent(QMouseEvent *e) return; } case QHeaderViewPrivate::MoveSection: { - if (qAbs(pos - d->firstPos) >= QApplication::startDragDistance()) { - int indicatorCenter = (d->orientation == Qt::Horizontal - ? d->sectionIndicator->width() - : d->sectionIndicator->height()) / 2; - int centerOffset = indicatorCenter - d->sectionIndicatorOffset; - // This will drop the moved section to the position under the center of the indicator. - // If centerOffset is 0, the section will be moved to the position of the mouse cursor. - int visual = visualIndexAt(pos + centerOffset); + if (qAbs(pos - d->firstPos) >= QApplication::startDragDistance() + || !d->sectionIndicator->isHidden()) { + int visual = visualIndexAt(pos); if (visual == -1) return; - d->target = d->logicalIndex(visual); + int posThreshold = d->headerSectionPosition(visual) + d->headerSectionSize(visual) / 2; + int moving = visualIndex(d->section); + if (visual < moving) { + if (pos < posThreshold) + d->target = d->logicalIndex(visual); + else + d->target = d->logicalIndex(visual + 1); + } else if (visual > moving) { + if (pos > posThreshold) + d->target = d->logicalIndex(visual); + else + d->target = d->logicalIndex(visual - 1); + } else { + d->target = d->section; + } d->updateSectionIndicator(d->section, pos); - } else { - int visual = visualIndexAt(d->firstPos); - if (visual == -1) - return; - d->target = d->logicalIndex(visual); - d->updateSectionIndicator(d->section, d->firstPos); } return; } @@ -2300,7 +2304,7 @@ void QHeaderView::mouseReleaseEvent(QMouseEvent *e) int section = logicalIndexAt(pos); if (section != -1 && section == d->pressed) { d->flipSortIndicator(section); - emit sectionClicked(logicalIndexAt(pos)); + emit sectionClicked(section); } if (d->pressed != -1) updateSection(d->pressed); @@ -2611,7 +2615,7 @@ void QHeaderView::updateGeometries() Q_D(QHeaderView); d->layoutChildren(); if (d->hasAutoResizeSections()) - resizeSections(); + d->doDelayedResizeSections(); } /*! diff --git a/src/gui/itemviews/qitemdelegate.cpp b/src/gui/itemviews/qitemdelegate.cpp index 7d8e103..d5f6fd2 100644 --- a/src/gui/itemviews/qitemdelegate.cpp +++ b/src/gui/itemviews/qitemdelegate.cpp @@ -69,6 +69,7 @@ #include <qdebug.h> #include <qlocale.h> #include <qdialog.h> +#include <qmath.h> #include <limits.h> @@ -1023,7 +1024,7 @@ QPixmap QItemDelegate::decoration(const QStyleOptionViewItem &option, const QVar // hacky but faster version of "QString::sprintf("%d-%d", i, enabled)" static QString qPixmapSerial(quint64 i, bool enabled) { - ushort arr[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', '0' + enabled }; + ushort arr[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', ushort('0' + enabled) }; ushort *ptr = &arr[16]; while (i > 0) { @@ -1148,7 +1149,8 @@ QRect QItemDelegate::textRectangle(QPainter * /*painter*/, const QRect &rect, d->textLayout.setTextOption(d->textOption); d->textLayout.setFont(font); d->textLayout.setText(QItemDelegatePrivate::replaceNewLine(text)); - const QSize size = d->doTextLayout(rect.width()).toSize(); + QSizeF fpSize = d->doTextLayout(rect.width()); + const QSize size = QSize(qCeil(fpSize.width()), qCeil(fpSize.height())); // ###: textRectangle should take style option as argument const int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1; return QRect(0, 0, size.width() + 2 * textMargin, size.height()); diff --git a/src/gui/itemviews/qitemselectionmodel.cpp b/src/gui/itemviews/qitemselectionmodel.cpp index cab002e..d6e68f6 100644 --- a/src/gui/itemviews/qitemselectionmodel.cpp +++ b/src/gui/itemviews/qitemselectionmodel.cpp @@ -527,6 +527,27 @@ void QItemSelection::split(const QItemSelectionRange &range, } } + +void QItemSelectionModelPrivate::initModel(QAbstractItemModel *model) +{ + this->model = model; + 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(layoutAboutToBeChanged()), + q, SLOT(_q_layoutAboutToBeChanged())); + QObject::connect(model, SIGNAL(layoutChanged()), + q, SLOT(_q_layoutChanged())); + } +} + /*! \internal @@ -793,6 +814,10 @@ static QItemSelection mergeIndexes(const QList<QPersistentModelIndex> &indexes) while (++i < colSpans.count()) { QModelIndex nextTl = colSpans.at(i).topLeft(); QModelIndex nextBr = colSpans.at(i).bottomRight(); + + if (nextTl.parent() != tl.parent()) + break; // we can't merge selection ranges from different parents + if ((nextTl.column() == prevTl.column()) && (nextBr.column() == br.column()) && (nextTl.row() == prevTl.row() + 1) && (nextBr.row() == br.row() + 1)) { br = nextBr; @@ -890,21 +915,7 @@ void QItemSelectionModelPrivate::_q_layoutChanged() QItemSelectionModel::QItemSelectionModel(QAbstractItemModel *model) : QObject(*new QItemSelectionModelPrivate, model) { - d_func()->model = model; - if (model) { - connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), - this, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int))); - connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)), - this, SLOT(_q_columnsAboutToBeRemoved(QModelIndex,int,int))); - connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), - this, SLOT(_q_rowsAboutToBeInserted(QModelIndex,int,int))); - connect(model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)), - this, SLOT(_q_columnsAboutToBeInserted(QModelIndex,int,int))); - connect(model, SIGNAL(layoutAboutToBeChanged()), - this, SLOT(_q_layoutAboutToBeChanged())); - connect(model, SIGNAL(layoutChanged()), - this, SLOT(_q_layoutChanged())); - } + d_func()->initModel(model); } /*! @@ -913,21 +924,7 @@ QItemSelectionModel::QItemSelectionModel(QAbstractItemModel *model) QItemSelectionModel::QItemSelectionModel(QAbstractItemModel *model, QObject *parent) : QObject(*new QItemSelectionModelPrivate, parent) { - d_func()->model = model; - if (model) { - connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), - this, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int))); - connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)), - this, SLOT(_q_columnsAboutToBeRemoved(QModelIndex,int,int))); - connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), - this, SLOT(_q_rowsAboutToBeInserted(QModelIndex,int,int))); - connect(model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)), - this, SLOT(_q_columnsAboutToBeInserted(QModelIndex,int,int))); - connect(model, SIGNAL(layoutAboutToBeChanged()), - this, SLOT(_q_layoutAboutToBeChanged())); - connect(model, SIGNAL(layoutChanged()), - this, SLOT(_q_layoutChanged())); - } + d_func()->initModel(model); } /*! @@ -936,21 +933,7 @@ QItemSelectionModel::QItemSelectionModel(QAbstractItemModel *model, QObject *par QItemSelectionModel::QItemSelectionModel(QItemSelectionModelPrivate &dd, QAbstractItemModel *model) : QObject(dd, model) { - d_func()->model = model; - if (model) { - connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), - this, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int))); - connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)), - this, SLOT(_q_columnsAboutToBeRemoved(QModelIndex,int,int))); - connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), - this, SLOT(_q_rowsAboutToBeInserted(QModelIndex,int,int))); - connect(model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)), - this, SLOT(_q_columnsAboutToBeInserted(QModelIndex,int,int))); - connect(model, SIGNAL(layoutAboutToBeChanged()), - this, SLOT(_q_layoutAboutToBeChanged())); - connect(model, SIGNAL(layoutChanged()), - this, SLOT(_q_layoutChanged())); - } + dd.initModel(model); } /*! @@ -958,21 +941,6 @@ QItemSelectionModel::QItemSelectionModel(QItemSelectionModelPrivate &dd, QAbstra */ QItemSelectionModel::~QItemSelectionModel() { - Q_D(QItemSelectionModel); - if (d->model) { - disconnect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), - this, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int))); - disconnect(d->model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)), - this, SLOT(_q_columnsAboutToBeRemoved(QModelIndex,int,int))); - disconnect(d->model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), - this, SLOT(_q_rowsAboutToBeInserted(QModelIndex,int,int))); - disconnect(d->model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)), - this, SLOT(_q_columnsAboutToBeInserted(QModelIndex,int,int))); - disconnect(d->model, SIGNAL(layoutAboutToBeChanged()), - this, SLOT(_q_layoutAboutToBeChanged())); - disconnect(d->model, SIGNAL(layoutChanged()), - this, SLOT(_q_layoutChanged())); - } } /*! diff --git a/src/gui/itemviews/qitemselectionmodel_p.h b/src/gui/itemviews/qitemselectionmodel_p.h index 30583b2..5afa90d 100644 --- a/src/gui/itemviews/qitemselectionmodel_p.h +++ b/src/gui/itemviews/qitemselectionmodel_p.h @@ -70,6 +70,8 @@ public: QItemSelection expandSelection(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command) const; + void initModel(QAbstractItemModel *model); + void _q_rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); void _q_columnsAboutToBeRemoved(const QModelIndex &parent, int start, int end); void _q_rowsAboutToBeInserted(const QModelIndex &parent, int start, int end); diff --git a/src/gui/itemviews/qlistview.cpp b/src/gui/itemviews/qlistview.cpp index b2def39..39ca75a 100644 --- a/src/gui/itemviews/qlistview.cpp +++ b/src/gui/itemviews/qlistview.cpp @@ -1387,6 +1387,9 @@ void QListView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFl /*! \reimp + + Since 4.7, the returned region only contains rectangles intersecting + (or included in) the viewport. */ QRegion QListView::visualRegionForSelection(const QItemSelection &selection) const { @@ -1394,6 +1397,7 @@ QRegion QListView::visualRegionForSelection(const QItemSelection &selection) con // ### NOTE: this is a potential bottleneck in non-static mode int c = d->column; QRegion selectionRegion; + const QRect &viewportRect = d->viewport->rect(); for (int i = 0; i < selection.count(); ++i) { if (!selection.at(i).isValid()) continue; @@ -1405,8 +1409,11 @@ QRegion QListView::visualRegionForSelection(const QItemSelection &selection) con int t = selection.at(i).topLeft().row(); int b = selection.at(i).bottomRight().row(); if (d->viewMode == IconMode || d->isWrapping()) { // in non-static mode, we have to go through all selected items - for (int r = t; r <= b; ++r) - selectionRegion += QRegion(visualRect(d->model->index(r, c, parent))); + for (int r = t; r <= b; ++r) { + const QRect &rect = visualRect(d->model->index(r, c, parent)); + if (viewportRect.intersects(rect)) + selectionRegion += rect; + } } else { // in static mode, we can optimize a bit while (t <= b && d->isHidden(t)) ++t; while (b >= t && d->isHidden(b)) --b; @@ -1414,7 +1421,8 @@ QRegion QListView::visualRegionForSelection(const QItemSelection &selection) con const QModelIndex bottom = d->model->index(b, c, parent); QRect rect(visualRect(top).topLeft(), visualRect(bottom).bottomRight()); - selectionRegion += QRegion(rect); + if (viewportRect.intersects(rect)) + selectionRegion += rect; } } diff --git a/src/gui/itemviews/qproxymodel.h b/src/gui/itemviews/qproxymodel.h index 1a6b14b..f179a7a 100644 --- a/src/gui/itemviews/qproxymodel.h +++ b/src/gui/itemviews/qproxymodel.h @@ -67,19 +67,19 @@ public: // implementing model interface - QModelIndex index(int row, int column, const QModelIndex &parent) const; + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; QModelIndex parent(const QModelIndex &child) const; - int rowCount(const QModelIndex &parent) const; - int columnCount(const QModelIndex &parent) const; - bool hasChildren(const QModelIndex &parent) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + bool hasChildren(const QModelIndex &parent = QModelIndex()) const; - QVariant data(const QModelIndex &index, int role) const; - bool setData(const QModelIndex &index, const QVariant &value, int role); + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); - QVariant headerData(int section, Qt::Orientation orientation, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, - int role); + int role = Qt::EditRole); QStringList mimeTypes() const; QMimeData *mimeData(const QModelIndexList &indexes) const; @@ -87,16 +87,17 @@ public: int row, int column, const QModelIndex &parent); Qt::DropActions supportedDropActions() const; - bool insertRows(int row, int count, const QModelIndex &parent); - bool insertColumns(int column, int count, const QModelIndex &parent); + bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()); + bool insertColumns(int column, int count, const QModelIndex &parent = QModelIndex()); void fetchMore(const QModelIndex &parent); Qt::ItemFlags flags(const QModelIndex &index) const; - void sort(int column, Qt::SortOrder order); + void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, - int hits, Qt::MatchFlags flags) const; + int hits = 1, Qt::MatchFlags flags = + Qt::MatchFlags(Qt::MatchStartsWith|Qt::MatchWrap)) const; QSize span(const QModelIndex &index) const; diff --git a/src/gui/itemviews/qsortfilterproxymodel.cpp b/src/gui/itemviews/qsortfilterproxymodel.cpp index 41c984e..dce5c6a 100644 --- a/src/gui/itemviews/qsortfilterproxymodel.cpp +++ b/src/gui/itemviews/qsortfilterproxymodel.cpp @@ -111,6 +111,35 @@ private: }; +//this struct is used to store what are the rows that are removed +//between a call to rowsAboutToBeRemoved and rowsRemoved +//it avoids readding rows to the mapping that are currently being removed +struct QRowsRemoval +{ + QRowsRemoval(const QModelIndex &parent_source, int start, int end) : parent_source(parent_source), start(start), end(end) + { + } + + QRowsRemoval() : start(-1), end(-1) + { + } + + bool contains(QModelIndex parent, int row) + { + do { + if (parent == parent_source) + return row >= start && row <= end; + row = parent.row(); + parent = parent.parent(); + } while (row >= 0); + return false; + } +private: + QModelIndex parent_source; + int start; + int end; +}; + class QSortFilterProxyModelPrivate : public QAbstractProxyModelPrivate { Q_DECLARE_PUBLIC(QSortFilterProxyModel) @@ -122,10 +151,10 @@ public: QVector<int> proxy_rows; QVector<int> proxy_columns; QVector<QModelIndex> mapped_children; - QMap<QModelIndex, Mapping *>::const_iterator map_iter; + QHash<QModelIndex, Mapping *>::const_iterator map_iter; }; - mutable QMap<QModelIndex, Mapping*> source_index_mapping; + mutable QHash<QModelIndex, Mapping*> source_index_mapping; int source_sort_column; int proxy_sort_column; @@ -139,10 +168,11 @@ public: int filter_role; bool dynamic_sortfilter; + QRowsRemoval itemsBeingRemoved; QModelIndexPairList saved_persistent_indexes; - QMap<QModelIndex, Mapping *>::const_iterator create_mapping( + QHash<QModelIndex, Mapping *>::const_iterator create_mapping( const QModelIndex &source_parent) const; QModelIndex proxy_to_source(const QModelIndex &proxyIndex) const; QModelIndex source_to_proxy(const QModelIndex &sourceIndex) const; @@ -150,14 +180,14 @@ public: void remove_from_mapping(const QModelIndex &source_parent); - inline QMap<QModelIndex, Mapping *>::const_iterator index_to_iterator( + inline QHash<QModelIndex, Mapping *>::const_iterator index_to_iterator( const QModelIndex &proxy_index) const { Q_ASSERT(proxy_index.isValid()); Q_ASSERT(proxy_index.model() == q_func()); const void *p = proxy_index.internalPointer(); Q_ASSERT(p); - QMap<QModelIndex, Mapping *>::const_iterator it = + QHash<QModelIndex, Mapping *>::const_iterator it = static_cast<const Mapping*>(p)->map_iter; Q_ASSERT(it != source_index_mapping.constEnd()); Q_ASSERT(it.value()); @@ -165,7 +195,7 @@ public: } inline QModelIndex create_index(int row, int column, - QMap<QModelIndex, Mapping*>::const_iterator it) const + QHash<QModelIndex, Mapping*>::const_iterator it) const { return q_func()->createIndex(row, column, *it); } @@ -246,7 +276,7 @@ public: virtual void _q_sourceModelDestroyed(); }; -typedef QMap<QModelIndex, QSortFilterProxyModelPrivate::Mapping *> IndexMap; +typedef QHash<QModelIndex, QSortFilterProxyModelPrivate::Mapping *> IndexMap; void QSortFilterProxyModelPrivate::_q_sourceModelDestroyed() { @@ -270,6 +300,11 @@ void QSortFilterProxyModelPrivate::clear_mapping() qDeleteAll(source_index_mapping); source_index_mapping.clear(); + if (dynamic_sortfilter && update_source_sort_column()) { + //update_source_sort_column might have created wrong mapping so we have to clear it again + qDeleteAll(source_index_mapping); + source_index_mapping.clear(); + } // update the persistent indexes update_persistent_indexes(source_indexes); @@ -287,11 +322,13 @@ IndexMap::const_iterator QSortFilterProxyModelPrivate::create_mapping( Mapping *m = new Mapping; int source_rows = model->rowCount(source_parent); + m->source_rows.reserve(source_rows); for (int i = 0; i < source_rows; ++i) { if (q->filterAcceptsRow(i, source_parent)) m->source_rows.append(i); } int source_cols = model->columnCount(source_parent); + m->source_columns.reserve(source_cols); for (int i = 0; i < source_cols; ++i) { if (q->filterAcceptsColumn(i, source_parent)) m->source_columns.append(i); @@ -558,7 +595,7 @@ QVector<QPair<int, QVector<int > > > QSortFilterProxyModelPrivate::proxy_interva int proxy_item = 0; int source_items_index = 0; QVector<int> source_items_in_interval; - bool compare = (orient == Qt::Vertical && source_sort_column >= 0); + bool compare = (orient == Qt::Vertical && source_sort_column >= 0 && dynamic_sortfilter); while (source_items_index < source_items.size()) { source_items_in_interval.clear(); int first_new_source_item = source_items.at(source_items_index); @@ -1089,7 +1126,7 @@ void QSortFilterProxyModelPrivate::_q_sourceDataChanged(const QModelIndex &sourc source_rows_change.append(source_row); } } else { - if (q->filterAcceptsRow(source_row, source_parent)) { + if (!itemsBeingRemoved.contains(source_parent, source_row) && q->filterAcceptsRow(source_row, source_parent)) { // This source row now satisfies the filter, so it must be added source_rows_insert.append(source_row); } @@ -1180,13 +1217,13 @@ void QSortFilterProxyModelPrivate::_q_sourceAboutToBeReset() { Q_Q(QSortFilterProxyModel); q->beginResetModel(); - invalidatePersistentIndexes(); - clear_mapping(); } void QSortFilterProxyModelPrivate::_q_sourceReset() { Q_Q(QSortFilterProxyModel); + invalidatePersistentIndexes(); + clear_mapping(); // All internal structures are deleted in clear() q->endResetModel(); update_source_sort_column(); @@ -1208,11 +1245,6 @@ void QSortFilterProxyModelPrivate::_q_sourceLayoutAboutToBeChanged() void QSortFilterProxyModelPrivate::_q_sourceLayoutChanged() { Q_Q(QSortFilterProxyModel); - if (saved_persistent_indexes.isEmpty()) { - clear_mapping(); - emit q->layoutChanged(); - return; - } qDeleteAll(source_index_mapping); source_index_mapping.clear(); @@ -1220,7 +1252,11 @@ void QSortFilterProxyModelPrivate::_q_sourceLayoutChanged() update_persistent_indexes(saved_persistent_indexes); saved_persistent_indexes.clear(); - update_source_sort_column(); + if (dynamic_sortfilter && update_source_sort_column()) { + //update_source_sort_column might have created wrong mapping so we have to clear it again + qDeleteAll(source_index_mapping); + source_index_mapping.clear(); + } emit q->layoutChanged(); } @@ -1240,13 +1276,14 @@ void QSortFilterProxyModelPrivate::_q_sourceRowsInserted( const QModelIndex &source_parent, int start, int end) { source_items_inserted(source_parent, start, end, Qt::Vertical); - if (update_source_sort_column()) //previous call to update_source_sort_column may fail if the model has no column. + if (update_source_sort_column() && dynamic_sortfilter) //previous call to update_source_sort_column may fail if the model has no column. sort(); // now it should succeed so we need to make sure to sort again } void QSortFilterProxyModelPrivate::_q_sourceRowsAboutToBeRemoved( const QModelIndex &source_parent, int start, int end) { + itemsBeingRemoved = QRowsRemoval(source_parent, start, end); source_items_about_to_be_removed(source_parent, start, end, Qt::Vertical); } @@ -1254,6 +1291,7 @@ void QSortFilterProxyModelPrivate::_q_sourceRowsAboutToBeRemoved( void QSortFilterProxyModelPrivate::_q_sourceRowsRemoved( const QModelIndex &source_parent, int start, int end) { + itemsBeingRemoved = QRowsRemoval(); source_items_removed(source_parent, start, end, Qt::Vertical); } @@ -1277,8 +1315,8 @@ void QSortFilterProxyModelPrivate::_q_sourceColumnsInserted( if (source_parent.isValid()) return; //we sort according to the root column only if (source_sort_column == -1) { - //we update the source_sort_column depending on the prox_sort_column - if (update_source_sort_column()) + //we update the source_sort_column depending on the proxy_sort_column + if (update_source_sort_column() && dynamic_sortfilter) sort(); } else { if (start <= source_sort_column) @@ -1481,7 +1519,6 @@ QSortFilterProxyModel::QSortFilterProxyModel(QObject *parent) d->filter_column = 0; d->filter_role = Qt::DisplayRole; d->dynamic_sortfilter = false; - connect(this, SIGNAL(modelReset()), this, SLOT(invalidate())); } /*! diff --git a/src/gui/itemviews/qtableview.cpp b/src/gui/itemviews/qtableview.cpp index c824a8a..43445b4 100644 --- a/src/gui/itemviews/qtableview.cpp +++ b/src/gui/itemviews/qtableview.cpp @@ -1858,6 +1858,9 @@ void QTableView::setSelection(const QRect &rect, QItemSelectionModel::SelectionF Returns the rectangle from the viewport of the items in the given \a selection. + + Since 4.7, the returned region only contains rectangles intersecting + (or included in) the viewport. */ QRegion QTableView::visualRegionForSelection(const QItemSelection &selection) const { @@ -1867,6 +1870,7 @@ QRegion QTableView::visualRegionForSelection(const QItemSelection &selection) co return QRegion(); QRegion selectionRegion; + const QRect &viewportRect = d->viewport->rect(); bool verticalMoved = verticalHeader()->sectionsMoved(); bool horizontalMoved = horizontalHeader()->sectionsMoved(); @@ -1876,8 +1880,11 @@ QRegion QTableView::visualRegionForSelection(const QItemSelection &selection) co if (range.parent() != d->root || !range.isValid()) continue; for (int r = range.top(); r <= range.bottom(); ++r) - for (int c = range.left(); c <= range.right(); ++c) - selectionRegion += QRegion(visualRect(d->model->index(r, c, d->root))); + for (int c = range.left(); c <= range.right(); ++c) { + const QRect &rangeRect = visualRect(d->model->index(r, c, d->root)); + if (viewportRect.intersects(rangeRect)) + selectionRegion += rangeRect; + } } } else if (horizontalMoved) { for (int i = 0; i < selection.count(); ++i) { @@ -1889,9 +1896,11 @@ QRegion QTableView::visualRegionForSelection(const QItemSelection &selection) co if (top > bottom) qSwap<int>(top, bottom); int height = bottom - top; - for (int c = range.left(); c <= range.right(); ++c) - selectionRegion += QRegion(QRect(columnViewportPosition(c), top, - columnWidth(c), height)); + for (int c = range.left(); c <= range.right(); ++c) { + const QRect rangeRect(columnViewportPosition(c), top, columnWidth(c), height); + if (viewportRect.intersects(rangeRect)) + selectionRegion += rangeRect; + } } } else if (verticalMoved) { for (int i = 0; i < selection.count(); ++i) { @@ -1903,9 +1912,11 @@ QRegion QTableView::visualRegionForSelection(const QItemSelection &selection) co if (left > right) qSwap<int>(left, right); int width = right - left; - for (int r = range.top(); r <= range.bottom(); ++r) - selectionRegion += QRegion(QRect(left, rowViewportPosition(r), - width, rowHeight(r))); + for (int r = range.top(); r <= range.bottom(); ++r) { + const QRect rangeRect(left, rowViewportPosition(r), width, rowHeight(r)); + if (viewportRect.intersects(rangeRect)) + selectionRegion += rangeRect; + } } } else { // nothing moved const int gridAdjust = showGrid() ? 1 : 0; @@ -1926,12 +1937,17 @@ QRegion QTableView::visualRegionForSelection(const QItemSelection &selection) co rleft = columnViewportPosition(range.right()); rright = columnViewportPosition(range.left()) + columnWidth(range.left()); } - selectionRegion += QRect(QPoint(rleft, rtop), QPoint(rright - 1 - gridAdjust, rbottom - 1 - gridAdjust)); + const QRect rangeRect(QPoint(rleft, rtop), QPoint(rright - 1 - gridAdjust, rbottom - 1 - gridAdjust)); + if (viewportRect.intersects(rangeRect)) + selectionRegion += rangeRect; if (d->hasSpans()) { foreach (QSpanCollection::Span *s, d->spans.spansInRect(range.left(), range.top(), range.width(), range.height())) { - if (range.contains(s->top(), s->left(), range.parent())) - selectionRegion += d->visualSpanRect(*s); + if (range.contains(s->top(), s->left(), range.parent())) { + const QRect &visualSpanRect = d->visualSpanRect(*s); + if (viewportRect.intersects(visualSpanRect)) + selectionRegion += visualSpanRect; + } } } } @@ -1968,12 +1984,7 @@ QModelIndexList QTableView::selectedIndexes() const void QTableView::rowCountChanged(int /*oldCount*/, int /*newCount*/ ) { Q_D(QTableView); - updateGeometries(); - if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) - d->verticalHeader->setOffsetToSectionPosition(verticalScrollBar()->value()); - else - d->verticalHeader->setOffset(verticalScrollBar()->value()); - d->viewport->update(); + d->doDelayedItemsLayout(); } /*! diff --git a/src/gui/itemviews/qtreeview.cpp b/src/gui/itemviews/qtreeview.cpp index 37168eb..d934683 100644 --- a/src/gui/itemviews/qtreeview.cpp +++ b/src/gui/itemviews/qtreeview.cpp @@ -674,21 +674,29 @@ void QTreeView::dataChanged(const QModelIndex &topLeft, const QModelIndex &botto // refresh the height cache here; we don't really lose anything by getting the size hint, // since QAbstractItemView::dataChanged() will get the visualRect for the items anyway - int topViewIndex = d->viewIndex(topLeft); - if (topViewIndex == 0) - d->defaultItemHeight = indexRowSizeHint(topLeft); bool sizeChanged = false; + int topViewIndex = d->viewIndex(topLeft); + if (topViewIndex == 0) { + int newDefaultItemHeight = indexRowSizeHint(topLeft); + sizeChanged = d->defaultItemHeight != newDefaultItemHeight; + d->defaultItemHeight = newDefaultItemHeight; + } + if (topViewIndex != -1) { - if (topLeft == bottomRight) { + if (topLeft.row() == bottomRight.row()) { int oldHeight = d->itemHeight(topViewIndex); d->invalidateHeightCache(topViewIndex); - sizeChanged = (oldHeight != d->itemHeight(topViewIndex)); + sizeChanged |= (oldHeight != d->itemHeight(topViewIndex)); + if (topLeft.column() == 0) + d->viewItems[topViewIndex].hasChildren = d->hasVisibleChildren(topLeft); } else { int bottomViewIndex = d->viewIndex(bottomRight); for (int i = topViewIndex; i <= bottomViewIndex; ++i) { int oldHeight = d->itemHeight(i); d->invalidateHeightCache(i); sizeChanged |= (oldHeight != d->itemHeight(i)); + if (topLeft.column() == 0) + d->viewItems[i].hasChildren = d->hasVisibleChildren(d->viewItems.at(i).index); } } } @@ -954,16 +962,16 @@ void QTreeView::keyboardSearch(const QString &search) else start = d->model->index(0, 0, d->root); - QTime now(QTime::currentTime()); bool skipRow = false; - if (search.isEmpty() - || (d->keyboardInputTime.msecsTo(now) > QApplication::keyboardInputInterval())) { + bool keyboardTimeWasValid = d->keyboardInputTime.isValid(); + qint64 keyboardInputTimeElapsed = d->keyboardInputTime.restart(); + if (search.isEmpty() || !keyboardTimeWasValid + || keyboardInputTimeElapsed > QApplication::keyboardInputInterval()) { d->keyboardInput = search; - skipRow = true; + skipRow = currentIndex().isValid(); //if it is not valid we should really start at QModelIndex(0,0) } else { d->keyboardInput += search; } - d->keyboardInputTime = now; // special case for searches with same key like 'aaaaa' bool sameKey = false; @@ -1228,17 +1236,17 @@ bool QTreeView::viewportEvent(QEvent *event) int oldBranch = d->hoverBranch; d->hoverBranch = d->itemDecorationAt(he->pos()); if (oldBranch != d->hoverBranch) { - QModelIndex oldIndex = d->modelIndex(oldBranch), - newIndex = d->modelIndex(d->hoverBranch); - if (oldIndex != newIndex) { - QRect oldRect = visualRect(oldIndex); - QRect newRect = visualRect(newIndex); - oldRect.setLeft(oldRect.left() - d->indent); - newRect.setLeft(newRect.left() - d->indent); - //we need to paint the whole items (including the decoration) so that when the user - //moves the mouse over those elements they are updated - viewport()->update(oldRect); - viewport()->update(newRect); + //we need to paint the whole items (including the decoration) so that when the user + //moves the mouse over those elements they are updated + if (oldBranch >= 0) { + int y = d->coordinateForItem(oldBranch); + int h = d->itemHeight(oldBranch); + viewport()->update(QRect(0, y, viewport()->width(), h)); + } + if (d->hoverBranch >= 0) { + int y = d->coordinateForItem(d->hoverBranch); + int h = d->itemHeight(d->hoverBranch); + viewport()->update(QRect(0, y, viewport()->width(), h)); } } break; } @@ -1993,6 +2001,24 @@ QModelIndex QTreeView::indexBelow(const QModelIndex &index) const void QTreeView::doItemsLayout() { Q_D(QTreeView); + if (d->hasRemovedItems) { + //clean the QSet that may contains old (and this invalid) indexes + d->hasRemovedItems = false; + QSet<QPersistentModelIndex>::iterator it = d->expandedIndexes.begin(); + while (it != d->expandedIndexes.constEnd()) { + if (!it->isValid()) + it = d->expandedIndexes.erase(it); + else + ++it; + } + it = d->hiddenIndexes.begin(); + while (it != d->hiddenIndexes.constEnd()) { + if (!it->isValid()) + it = d->hiddenIndexes.erase(it); + else + ++it; + } + } d->viewItems.clear(); // prepare for new layout QModelIndex parent = d->root; if (d->model->hasChildren(parent)) { @@ -2134,9 +2160,10 @@ QModelIndex QTreeView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifie return d->modelIndex(d->above(vi), current.column()); case MoveLeft: { QScrollBar *sb = horizontalScrollBar(); - if (vi < d->viewItems.count() && d->viewItems.at(vi).expanded && d->itemsExpandable && sb->value() == sb->minimum()) + if (vi < d->viewItems.count() && d->viewItems.at(vi).expanded && d->itemsExpandable && sb->value() == sb->minimum()) { d->collapse(vi, true); - else { + d->moveCursorUpdatedView = true; + } else { bool descend = style()->styleHint(QStyle::SH_ItemView_ArrowKeysNavigateIntoChildren, 0, this); if (descend) { QModelIndex par = current.parent(); @@ -2156,7 +2183,10 @@ QModelIndex QTreeView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifie return next; } + int oldValue = sb->value(); sb->setValue(sb->value() - sb->singleStep()); + if (oldValue != sb->value()) + d->moveCursorUpdatedView = true; } } @@ -2168,6 +2198,7 @@ QModelIndex QTreeView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifie if (vi < d->viewItems.count() && !d->viewItems.at(vi).expanded && d->itemsExpandable && d->hasVisibleChildren(d->viewItems.at(vi).index)) { d->expand(vi, true); + d->moveCursorUpdatedView = true; } else { bool descend = style()->styleHint(QStyle::SH_ItemView_ArrowKeysNavigateIntoChildren, 0, this); if (descend) { @@ -2190,7 +2221,10 @@ QModelIndex QTreeView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifie //last restort: we change the scrollbar value QScrollBar *sb = horizontalScrollBar(); + int oldValue = sb->value(); sb->setValue(sb->value() + sb->singleStep()); + if (oldValue != sb->value()) + d->moveCursorUpdatedView = true; } } updateGeometries(); @@ -2249,6 +2283,9 @@ void QTreeView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFl /*! Returns the rectangle from the viewport of the items in the given \a selection. + + Since 4.7, the returned region only contains rectangles intersecting + (or included in) the viewport. */ QRegion QTreeView::visualRegionForSelection(const QItemSelection &selection) const { @@ -2257,6 +2294,7 @@ QRegion QTreeView::visualRegionForSelection(const QItemSelection &selection) con return QRegion(); QRegion selectionRegion; + const QRect &viewportRect = d->viewport->rect(); for (int i = 0; i < selection.count(); ++i) { QItemSelectionRange range = selection.at(i); if (!range.isValid()) @@ -2289,13 +2327,16 @@ QRegion QTreeView::visualRegionForSelection(const QItemSelection &selection) con qSwap<int>(top, bottom); int height = bottom - top + 1; if (d->header->sectionsMoved()) { - for (int c = range.left(); c <= range.right(); ++c) - selectionRegion += QRegion(QRect(columnViewportPosition(c), top, - columnWidth(c), height)); + for (int c = range.left(); c <= range.right(); ++c) { + const QRect rangeRect(columnViewportPosition(c), top, columnWidth(c), height); + if (viewportRect.intersects(rangeRect)) + selectionRegion += rangeRect; + } } else { QRect combined = leftRect|rightRect; combined.setX(columnViewportPosition(isRightToLeft() ? range.right() : range.left())); - selectionRegion += combined; + if (viewportRect.intersects(combined)) + selectionRegion += combined; } } return selectionRegion; @@ -2402,24 +2443,6 @@ void QTreeView::reexpand() } /*! - \internal -*/ -static bool treeViewItemLessThan(const QTreeViewItem &left, - const QTreeViewItem &right) -{ - if (left.level != right.level) { - Q_ASSERT(left.level > right.level); - QModelIndex leftParent = left.index.parent(); - QModelIndex rightParent = right.index.parent(); - // computer parent, don't get - while (leftParent.isValid() && leftParent.parent() != rightParent) - leftParent = leftParent.parent(); - return (leftParent.row() < right.index.row()); - } - return (left.index.row() < right.index.row()); -} - -/*! Informs the view that the rows from the \a start row to the \a end row inclusive have been inserted into the \a parent model item. */ @@ -2448,84 +2471,7 @@ void QTreeView::rowsInserted(const QModelIndex &parent, int start, int end) const int parentItem = d->viewIndex(parent); if (((parentItem != -1) && d->viewItems.at(parentItem).expanded && updatesEnabled()) || (parent == d->root)) { - const uint childLevel = (parentItem == -1) - ? uint(0) : d->viewItems.at(parentItem).level + 1; - const int firstChildItem = parentItem + 1; - const int lastChildItem = firstChildItem + ((parentItem == -1) - ? d->viewItems.count() - : d->viewItems.at(parentItem).total) - 1; - - if (parentRowCount == end + 1 && start > 0) { - //need to Update hasMoreSiblings - int previousRow = start - 1; - QModelIndex previousSibilingModelIndex = d->model->index(previousRow, 0, parent); - bool isHidden = d->isRowHidden(previousSibilingModelIndex); - while (isHidden && previousRow > 0) { - previousRow--; - previousSibilingModelIndex = d->model->index(previousRow, 0, parent); - isHidden = d->isRowHidden(previousSibilingModelIndex); - } - if (!isHidden) { - const int previousSibilling = d->viewIndex(previousSibilingModelIndex); - if(previousSibilling != -1) - d->viewItems[previousSibilling].hasMoreSiblings = true; - } - } - - QVector<QTreeViewItem> insertedItems(delta); - for (int i = 0; i < delta; ++i) { - QTreeViewItem &item = insertedItems[i]; - item.index = d->model->index(i + start, 0, parent); - item.level = childLevel; - item.hasChildren = d->hasVisibleChildren(item.index); - item.hasMoreSiblings = !((i == delta - 1) && (parentRowCount == end +1)); - } - if (d->viewItems.isEmpty()) - d->defaultItemHeight = indexRowSizeHint(insertedItems[0].index); - - int insertPos; - if (lastChildItem < firstChildItem) { // no children - insertPos = firstChildItem; - } else { - // do a binary search to figure out where to insert - QVector<QTreeViewItem>::iterator it; - it = qLowerBound(d->viewItems.begin() + firstChildItem, - d->viewItems.begin() + lastChildItem + 1, - insertedItems.at(0), treeViewItemLessThan); - insertPos = it - d->viewItems.begin(); - - // update stale model indexes of siblings - for (int item = insertPos; item <= lastChildItem; ) { - Q_ASSERT(d->viewItems.at(item).level == childLevel); - const QModelIndex modelIndex = d->viewItems.at(item).index; - //Q_ASSERT(modelIndex.parent() == parent); - d->viewItems[item].index = d->model->index( - modelIndex.row() + delta, modelIndex.column(), parent); - - if (!d->viewItems[item].index.isValid()) { - // Something really bad is happening, a bad model is - // often the cause. We can't optimize in this case :( - qWarning() << "QTreeView::rowsInserted internal representation of the model has been corrupted, resetting."; - doItemsLayout(); - return; - } - - item += d->viewItems.at(item).total + 1; - } - } - - d->viewItems.insert(insertPos, delta, insertedItems.at(0)); - if (delta > 1) { - qCopy(insertedItems.begin() + 1, insertedItems.end(), - d->viewItems.begin() + insertPos + 1); - } - - if (parentItem != -1) - d->viewItems[parentItem].hasChildren = true; - d->updateChildCount(parentItem, delta); - - updateGeometries(); - viewport()->update(); + d->doDelayedItemsLayout(); } else if ((parentItem != -1) && d->viewItems.at(parentItem).expanded) { d->doDelayedItemsLayout(); } else if (parentItem != -1 && (d->model->rowCount(parent) == end - start + 1)) { @@ -2543,8 +2489,8 @@ void QTreeView::rowsInserted(const QModelIndex &parent, int start, int end) void QTreeView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) { Q_D(QTreeView); - d->rowsRemoved(parent, start, end, false); QAbstractItemView::rowsAboutToBeRemoved(parent, start, end); + d->viewItems.clear(); } /*! @@ -2556,7 +2502,10 @@ void QTreeView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int e void QTreeView::rowsRemoved(const QModelIndex &parent, int start, int end) { Q_D(QTreeView); - d->rowsRemoved(parent, start, end, true); + d->viewItems.clear(); + d->doDelayedItemsLayout(); + d->hasRemovedItems = true; + d->_q_rowsRemoved(parent, start, end); } /*! @@ -2658,17 +2607,8 @@ void QTreeView::expandAll() { Q_D(QTreeView); d->viewItems.clear(); - d->expandedIndexes.clear(); d->interruptDelayedItemsLayout(); - d->layout(-1); - for (int i = 0; i < d->viewItems.count(); ++i) { - if (d->viewItems[i].expanded) - continue; - d->viewItems[i].expanded = true; - d->layout(i); - QModelIndex idx = d->viewItems.at(i).index; - d->expandedIndexes.insert(idx); - } + d->layout(-1, true); updateGeometries(); d->viewport->update(); } @@ -2949,6 +2889,39 @@ void QTreeViewPrivate::expand(int item, bool emitSignal) } } +void QTreeViewPrivate::insertViewItems(int pos, int count, const QTreeViewItem &viewItem) +{ + viewItems.insert(pos, count, viewItem); + QTreeViewItem *items = viewItems.data(); + for (int i = pos + count; i < viewItems.count(); i++) + if (items[i].parentItem >= pos) + items[i].parentItem += count; +} + +void QTreeViewPrivate::removeViewItems(int pos, int count) +{ + viewItems.remove(pos, count); + QTreeViewItem *items = viewItems.data(); + for (int i = pos; i < viewItems.count(); i++) + if (items[i].parentItem >= pos) + items[i].parentItem -= count; +} + +#if 0 +bool QTreeViewPrivate::checkViewItems() const +{ + for (int i = 0; i < viewItems.count(); ++i) { + const QTreeViewItem &vi = viewItems.at(i); + if (vi.parentItem == -1) { + Q_ASSERT(!vi.index.parent().isValid() || vi.index.parent() == root); + } else { + Q_ASSERT(vi.index.parent() == viewItems.at(vi.parentItem).index); + } + } + return true; +} +#endif + void QTreeViewPrivate::collapse(int item, bool emitSignal) { Q_Q(QTreeView); @@ -2977,14 +2950,11 @@ void QTreeViewPrivate::collapse(int item, bool emitSignal) expandedIndexes.erase(it); viewItems[item].expanded = false; int index = item; - QModelIndex parent = modelIndex; - while (parent.isValid() && parent != root) { - Q_ASSERT(index > -1); + while (index > -1) { viewItems[index].total -= total; - parent = parent.parent(); - index = viewIndex(parent); + index = viewItems[index].parentItem; } - viewItems.remove(item + 1, total); // collapse + removeViewItems(item + 1, total); // collapse q->setState(oldState); if (emitSignal) { @@ -3121,7 +3091,14 @@ void QTreeViewPrivate::_q_columnsRemoved(const QModelIndex &parent, int start, i QAbstractItemViewPrivate::_q_columnsRemoved(parent, start, end); } -void QTreeViewPrivate::layout(int i) +/** \internal + creates and initialize the viewItem structure of the children of the element \i + + set \a recursiveExpanding if the function has to expand all the children (called from expandAll) + \a afterIsUninitialized is when we recurse from layout(-1), it means all the items after 'i' are + not yet initialized and need not to be moved + */ +void QTreeViewPrivate::layout(int i, bool recursiveExpanding, bool afterIsUninitialized) { Q_Q(QTreeView); QModelIndex current; @@ -3148,8 +3125,12 @@ void QTreeViewPrivate::layout(int i) defaultItemHeight = q->indexRowSizeHint(index); } viewItems.resize(count); + afterIsUninitialized = true; } else if (viewItems[i].total != (uint)count) { - viewItems.insert(i + 1, count, QTreeViewItem()); // expand + if (!afterIsUninitialized) + insertViewItems(i + 1, count, QTreeViewItem()); // expand + else if (count > 0) + viewItems.resize(viewItems.count() + count); } else { expanding = false; } @@ -3171,15 +3152,18 @@ void QTreeViewPrivate::layout(int i) item->hasMoreSiblings = true; item = &viewItems[last]; item->index = current; + item->parentItem = i; item->level = level; item->height = 0; item->spanning = q->isFirstColumnSpanned(current.row(), parent); item->expanded = false; item->total = 0; item->hasMoreSiblings = false; - if (isIndexExpanded(current)) { + if (recursiveExpanding || isIndexExpanded(current)) { + if (recursiveExpanding) + expandedIndexes.insert(current); item->expanded = true; - layout(last); + layout(last, recursiveExpanding, afterIsUninitialized); item = &viewItems[last]; children += item->total; item->hasChildren = item->total > 0; @@ -3191,17 +3175,19 @@ void QTreeViewPrivate::layout(int i) } // remove hidden items - if (hidden > 0) - viewItems.remove(last + 1, hidden); // collapse + if (hidden > 0) { + if (!afterIsUninitialized) + removeViewItems(last + 1, hidden); + else + viewItems.resize(viewItems.size() - hidden); + } if (!expanding) return; // nothing changed - while (parent != root) { - Q_ASSERT(i > -1); + while (i > -1) { viewItems[i].total += count - hidden; - parent = parent.parent(); - i = viewIndex(parent); + i = viewItems[i].parentItem; } } @@ -3356,46 +3342,39 @@ int QTreeViewPrivate::viewIndex(const QModelIndex &_index) const const int totalCount = viewItems.count(); const QModelIndex index = _index.sibling(_index.row(), 0); + const int row = index.row(); + const qint64 internalId = index.internalId(); - - // A quick check near the last item to see if we are just incrementing - const int start = lastViewedItem > 2 ? lastViewedItem - 2 : 0; - const int end = lastViewedItem < totalCount - 2 ? lastViewedItem + 2 : totalCount; - int row = index.row(); - for (int i = start; i < end; ++i) { - const QModelIndex &idx = viewItems.at(i).index; - if (idx.row() == row) { - if (idx.internalId() == index.internalId()) { - lastViewedItem = i; - return i; - } + // We start nearest to the lastViewedItem + int localCount = qMin(lastViewedItem - 1, totalCount - lastViewedItem); + for (int i = 0; i < localCount; ++i) { + const QModelIndex &idx1 = viewItems.at(lastViewedItem + i).index; + if (idx1.row() == row && idx1.internalId() == internalId) { + lastViewedItem = lastViewedItem + i; + return lastViewedItem; + } + const QModelIndex &idx2 = viewItems.at(lastViewedItem - i - 1).index; + if (idx2.row() == row && idx2.internalId() == internalId) { + lastViewedItem = lastViewedItem - i - 1; + return lastViewedItem; } } - // NOTE: this function is slow if the item is outside the visible area - // search in visible items first and below - int t = firstVisibleItem(); - t = t > 100 ? t - 100 : 0; // start 100 items above the visible area - - for (int i = t; i < totalCount; ++i) { - const QModelIndex &idx = viewItems.at(i).index; - if (idx.row() == row) { - if (idx.internalId() == index.internalId()) { - lastViewedItem = i; - return i; - } + for (int j = qMax(0, lastViewedItem + localCount); j < totalCount; ++j) { + const QModelIndex &idx = viewItems.at(j).index; + if (idx.row() == row && idx.internalId() == internalId) { + lastViewedItem = j; + return j; } } - // search from top to first visible - for (int j = 0; j < t; ++j) { + for (int j = qMin(totalCount, lastViewedItem - localCount) - 1; j >= 0; --j) { const QModelIndex &idx = viewItems.at(j).index; - if (idx.row() == row) { - if (idx.internalId() == index.internalId()) { - lastViewedItem = j; - return j; - } + if (idx.row() == row && idx.internalId() == internalId) { + lastViewedItem = j; + return j; } } + // nothing found return -1; } @@ -3717,120 +3696,6 @@ bool QTreeViewPrivate::hasVisibleChildren(const QModelIndex& parent) const return false; } -void QTreeViewPrivate::rowsRemoved(const QModelIndex &parent, - int start, int end, bool after) -{ - Q_Q(QTreeView); - // if we are going to do a complete relayout anyway, there is no need to update - if (delayedPendingLayout) { - _q_rowsRemoved(parent, start, end); - return; - } - - const int parentItem = viewIndex(parent); - if ((parentItem != -1) || (parent == root)) { - - const uint childLevel = (parentItem == -1) - ? uint(0) : viewItems.at(parentItem).level + 1; - Q_UNUSED(childLevel); // unused in release mode, used in assert below - - const int firstChildItem = parentItem + 1; - int lastChildItem = firstChildItem + ((parentItem == -1) - ? viewItems.count() - : viewItems.at(parentItem).total) - 1; - - const int delta = end - start + 1; - - int previousSibiling = -1; - int removedCount = 0; - for (int item = firstChildItem; item <= lastChildItem; ) { - Q_ASSERT(viewItems.at(item).level == childLevel); - const QModelIndex modelIndex = viewItems.at(item).index; - //Q_ASSERT(modelIndex.parent() == parent); - const int count = viewItems.at(item).total + 1; - if (modelIndex.row() < start) { - previousSibiling = item; - // not affected by the removal - item += count; - } else if (modelIndex.row() <= end) { - // removed - viewItems.remove(item, count); - removedCount += count; - lastChildItem -= count; - } else { - if (after) { - // moved; update the model index - viewItems[item].index = model->index( - modelIndex.row() - delta, modelIndex.column(), parent); - } - item += count; - } - } - - if (previousSibiling != -1 && after && model->rowCount(parent) == start) - viewItems[previousSibiling].hasMoreSiblings = false; - - if (parentItem != -1) { - if (viewItems.at(parentItem).expanded) { - updateChildCount(parentItem, -removedCount); - if (viewItems.at(parentItem).total == 0) - viewItems[parentItem].hasChildren = false; //every children have been removed; - } else if (viewItems[parentItem].hasChildren && !hasVisibleChildren(parent)) { - viewItems[parentItem].hasChildren = false; - } - } - if (after) { - q->updateGeometries(); - viewport->update(); - } else { - //we have removed items: we should at least update the scroll bar values. - // They are used to determine the item geometry. - updateScrollBars(); - } - } else { - // If an ancestor of root is removed then relayout - QModelIndex idx = root; - while (idx.isValid()) { - idx = idx.parent(); - if (idx == parent) { - doDelayedItemsLayout(); - break; - } - } - } - _q_rowsRemoved(parent, start, end); - - QSet<QPersistentModelIndex>::iterator it = expandedIndexes.begin(); - while (it != expandedIndexes.constEnd()) { - if (!it->isValid()) - it = expandedIndexes.erase(it); - else - ++it; - } - it = hiddenIndexes.begin(); - while (it != hiddenIndexes.constEnd()) { - if (!it->isValid()) - it = hiddenIndexes.erase(it); - else - ++it; - } -} - -void QTreeViewPrivate::updateChildCount(const int parentItem, const int delta) -{ - if ((parentItem != -1) && delta) { - int level = viewItems.at(parentItem).level; - int item = parentItem; - do { - Q_ASSERT(item >= 0); - for ( ; int(viewItems.at(item).level) != level; --item) ; - viewItems[item].total += delta; - --level; - } while (level >= 0); - } -} - - void QTreeViewPrivate::_q_sortIndicatorChanged(int column, Qt::SortOrder order) { model->sort(column, order); diff --git a/src/gui/itemviews/qtreeview_p.h b/src/gui/itemviews/qtreeview_p.h index 589a224..261af31 100644 --- a/src/gui/itemviews/qtreeview_p.h +++ b/src/gui/itemviews/qtreeview_p.h @@ -55,6 +55,7 @@ #include "private/qabstractitemview_p.h" #include <QtCore/qvariantanimation.h> +#include <QtCore/qabstractitemmodel.h> #ifndef QT_NO_TREEVIEW @@ -62,9 +63,10 @@ QT_BEGIN_NAMESPACE struct QTreeViewItem { - QTreeViewItem() : expanded(false), spanning(false), hasChildren(false), + QTreeViewItem() : parentItem(-1), expanded(false), spanning(false), hasChildren(false), hasMoreSiblings(false), total(0), level(0), height(0) {} QModelIndex index; // we remove items whenever the indexes are invalidated + int parentItem; // parent item index in viewItems uint expanded : 1; uint spanning : 1; uint hasChildren : 1; // if the item has visible children (even if collapsed) @@ -74,6 +76,8 @@ struct QTreeViewItem int height : 16; // row height }; +Q_DECLARE_TYPEINFO(QTreeViewItem, Q_MOVABLE_TYPE); + class QTreeViewPrivate : public QAbstractItemViewPrivate { Q_DECLARE_PUBLIC(QTreeView) @@ -87,7 +91,7 @@ public: expandsOnDoubleClick(true), allColumnsShowFocus(false), current(0), spanning(false), animationsEnabled(false), columnResizeTimerID(0), - autoExpandDelay(-1), hoverBranch(-1), geometryRecursionBlock(false) {} + autoExpandDelay(-1), hoverBranch(-1), geometryRecursionBlock(false), hasRemovedItems(false) {} ~QTreeViewPrivate() {} void initialize(); @@ -123,7 +127,7 @@ public: void _q_sortIndicatorChanged(int column, Qt::SortOrder order); void _q_modelDestroyed(); - void layout(int item); + void layout(int item, bool recusiveExpanding = false, bool afterIsUninitialized = false); int pageUp(int item) const; int pageDown(int item) const; @@ -136,6 +140,12 @@ public: int viewIndex(const QModelIndex &index) const; QModelIndex modelIndex(int i, int column = 0) const; + void insertViewItems(int pos, int count, const QTreeViewItem &viewItem); + void removeViewItems(int pos, int count); +#if 0 + bool checkViewItems() const; +#endif + int firstVisibleItem(int *offset = 0) const; int columnAt(int x) const; bool hasVisibleChildren( const QModelIndex& parent) const; @@ -155,8 +165,6 @@ public: QPair<int,int> startAndEndColumns(const QRect &rect) const; void updateChildCount(const int parentItem, const int delta); - void rowsRemoved(const QModelIndex &parent, - int start, int end, bool before); void paintAlternatingRowColors(QPainter *painter, QStyleOptionViewItemV4 *option, int y, int bottom) const; @@ -232,6 +240,9 @@ public: // used for blocking recursion when calling setViewportMargins from updateGeometries bool geometryRecursionBlock; + + // If we should clean the set + bool hasRemovedItems; }; QT_END_NAMESPACE |