diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2009-03-23 09:18:55 (GMT) |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-03-23 09:18:55 (GMT) |
commit | e5fcad302d86d316390c6b0f62759a067313e8a9 (patch) | |
tree | c2afbf6f1066b6ce261f14341cf6d310e5595bc1 /src/gui/itemviews/qcolumnview.cpp | |
download | Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.zip Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.gz Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.bz2 |
Long live Qt 4.5!
Diffstat (limited to 'src/gui/itemviews/qcolumnview.cpp')
-rw-r--r-- | src/gui/itemviews/qcolumnview.cpp | 1128 |
1 files changed, 1128 insertions, 0 deletions
diff --git a/src/gui/itemviews/qcolumnview.cpp b/src/gui/itemviews/qcolumnview.cpp new file mode 100644 index 0000000..4fb08bb --- /dev/null +++ b/src/gui/itemviews/qcolumnview.cpp @@ -0,0 +1,1128 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qglobal.h> + +#ifndef QT_NO_COLUMNVIEW + +#include "qcolumnview.h" +#include "qcolumnview_p.h" +#include "qcolumnviewgrip_p.h" + +#include <qlistview.h> +#include <qabstractitemdelegate.h> +#include <qscrollbar.h> +#include <qpainter.h> +#include <qdebug.h> +#include <qpainterpath.h> + +QT_BEGIN_NAMESPACE + +#define ANIMATION_DURATION_MSEC 150 + +/*! + \since 4.3 + \class QColumnView + \brief The QColumnView class provides a model/view implementation of a column view. + \ingroup model-view + \ingroup advanced + \mainclass + + QColumnView displays a model in a number of QListViews, one for each + hierarchy in the tree. This is sometimes referred to as a cascading list. + + The QColumnView class is one of the \l{Model/View Classes} + and is part of Qt's \l{Model/View Programming}{model/view framework}. + + QColumnView implements the interfaces defined by the + QAbstractItemView class to allow it to display data provided by + models derived from the QAbstractItemModel class. + + \image qcolumnview.png + + \sa \link model-view-programming.html Model/View Programming\endlink +*/ + +/*! + Constructs a column view with a \a parent to represent a model's + data. Use setModel() to set the model. + + \sa QAbstractItemModel +*/ +QColumnView::QColumnView(QWidget * parent) +: QAbstractItemView(*new QColumnViewPrivate, parent) +{ + Q_D(QColumnView); + d->initialize(); +} + +/*! + \internal +*/ +QColumnView::QColumnView(QColumnViewPrivate & dd, QWidget * parent) +: QAbstractItemView(dd, parent) +{ + Q_D(QColumnView); + d->initialize(); +} + +void QColumnViewPrivate::initialize() +{ + Q_Q(QColumnView); + q->setTextElideMode(Qt::ElideMiddle); + q->connect(¤tAnimation, SIGNAL(frameChanged(int)), + q->horizontalScrollBar(), SLOT(setValue(int))); + q->connect(¤tAnimation, SIGNAL(finished()), q, SLOT(_q_changeCurrentColumn())); + delete itemDelegate; + q->setItemDelegate(new QColumnViewDelegate(q)); +} + +/*! + Destroys the column view. +*/ +QColumnView::~QColumnView() +{ +} + +/*! + \property QColumnView::resizeGripsVisible + \brief the way to specify if the list views gets resize grips or not + + By default, \c visible is set to true + + \sa setRootIndex() +*/ +void QColumnView::setResizeGripsVisible(bool visible) +{ + Q_D(QColumnView); + if (d->showResizeGrips == visible) + return; + d->showResizeGrips = visible; + for (int i = 0; i < d->columns.count(); ++i) { + QAbstractItemView *view = d->columns[i]; + if (visible) { + QColumnViewGrip *grip = new QColumnViewGrip(view); + view->setCornerWidget(grip); + connect(grip, SIGNAL(gripMoved(int)), this, SLOT(_q_gripMoved(int))); + } else { + QWidget *widget = view->cornerWidget(); + view->setCornerWidget(0); + widget->deleteLater(); + } + } +} + +bool QColumnView::resizeGripsVisible() const +{ + Q_D(const QColumnView); + return d->showResizeGrips; +} + +/*! + \reimp +*/ +void QColumnView::setModel(QAbstractItemModel *model) +{ + Q_D(QColumnView); + if (model == d->model) + return; + d->closeColumns(); + QAbstractItemView::setModel(model); +} + +/*! + \reimp +*/ +void QColumnView::setRootIndex(const QModelIndex &index) +{ + Q_D(QColumnView); + if (!model()) + return; + + d->closeColumns(); + Q_ASSERT(d->columns.count() == 0); + + QAbstractItemView *view = d->createColumn(index, true); + if (view->selectionModel()) + view->selectionModel()->deleteLater(); + if (view->model()) + view->setSelectionModel(selectionModel()); + + QAbstractItemView::setRootIndex(index); + d->updateScrollbars(); +} + +/*! + \reimp +*/ +bool QColumnView::isIndexHidden(const QModelIndex &index) const +{ + Q_UNUSED(index); + return false; +} + +/*! + \reimp +*/ +QModelIndex QColumnView::indexAt(const QPoint &point) const +{ + Q_D(const QColumnView); + for (int i = 0; i < d->columns.size(); ++i) { + QPoint topLeft = d->columns.at(i)->frameGeometry().topLeft(); + QPoint adjustedPoint(point.x() - topLeft.x(), point.y() - topLeft.y()); + QModelIndex index = d->columns.at(i)->indexAt(adjustedPoint); + if (index.isValid()) + return index; + } + return QModelIndex(); +} + +/*! + \reimp +*/ +QRect QColumnView::visualRect(const QModelIndex &index) const +{ + if (!index.isValid()) + return QRect(); + + Q_D(const QColumnView); + for (int i = 0; i < d->columns.size(); ++i) { + QRect rect = d->columns.at(i)->visualRect(index); + if (!rect.isNull()) { + rect.translate(d->columns.at(i)->frameGeometry().topLeft()); + return rect; + } + } + return QRect(); +} + +/*! + \reimp + */ +void QColumnView::scrollContentsBy(int dx, int dy) +{ + Q_D(QColumnView); + if (d->columns.isEmpty() || dx == 0) + return; + + dx = isRightToLeft() ? -dx : dx; + for (int i = 0; i < d->columns.count(); ++i) + d->columns.at(i)->move(d->columns.at(i)->x() + dx, 0); + d->offset += dx; + QAbstractItemView::scrollContentsBy(dx, dy); +} + +/*! + \reimp +*/ +void QColumnView::scrollTo(const QModelIndex &index, ScrollHint hint) +{ + Q_D(QColumnView); + Q_UNUSED(hint); + if (!index.isValid() || d->columns.isEmpty()) + return; + + if (d->currentAnimation.state() == QTimeLine::Running) + return; + + d->currentAnimation.stop(); + + // Fill up what is needed to get to index + d->closeColumns(index, true); + + QModelIndex indexParent = index.parent(); + // Find the left edge of the column that contains index + int currentColumn = 0; + int leftEdge = 0; + while (currentColumn < d->columns.size()) { + if (indexParent == d->columns.at(currentColumn)->rootIndex()) + break; + leftEdge += d->columns.at(currentColumn)->width(); + ++currentColumn; + } + + // Don't let us scroll above the root index + if (currentColumn == d->columns.size()) + return; + + int indexColumn = currentColumn; + // Find the width of what we want to show (i.e. the right edge) + int visibleWidth = d->columns.at(currentColumn)->width(); + // We want to always try to show two columns + if (currentColumn + 1 < d->columns.size()) { + ++currentColumn; + visibleWidth += d->columns.at(currentColumn)->width(); + } + + int rightEdge = leftEdge + visibleWidth; + if (isRightToLeft()) { + leftEdge = viewport()->width() - leftEdge; + rightEdge = leftEdge - visibleWidth; + qSwap(rightEdge, leftEdge); + } + + // If it is already visible don't animate + if (leftEdge > -horizontalOffset() + && rightEdge <= ( -horizontalOffset() + viewport()->size().width())) { + d->columns.at(indexColumn)->scrollTo(index); + d->_q_changeCurrentColumn(); + return; + } + + int newScrollbarValue = 0; + if (isRightToLeft()) { + if (leftEdge < 0) { + // scroll to the right + newScrollbarValue = viewport()->size().width() - leftEdge; + } else { + // scroll to the left + newScrollbarValue = rightEdge + horizontalOffset(); + } + } else { + if (leftEdge > -horizontalOffset()) { + // scroll to the right + newScrollbarValue = rightEdge - viewport()->size().width(); + } else { + // scroll to the left + newScrollbarValue = leftEdge; + } + } + + //horizontalScrollBar()->setValue(newScrollbarValue); + //d->_q_changeCurrentColumn(); + //return; + // or do the following currentAnimation + + int oldValue = horizontalScrollBar()->value(); + + if (oldValue < newScrollbarValue) { + d->currentAnimation.setFrameRange(oldValue, newScrollbarValue); + d->currentAnimation.setDirection(QTimeLine::Forward); + d->currentAnimation.setCurrentTime(0); + } else { + d->currentAnimation.setFrameRange(newScrollbarValue, oldValue); + d->currentAnimation.setDirection(QTimeLine::Backward); + } + d->currentAnimation.start(); +} + +/*! + \reimp + Move left should go to the parent index + Move right should go to the child index or down if there is no child +*/ +QModelIndex QColumnView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) +{ + // the child views which have focus get to deal with this first and if + // they don't accept it then it comes up this view and we only grip left/right + Q_UNUSED(modifiers); + if (!model()) + return QModelIndex(); + + QModelIndex current = currentIndex(); + if (isRightToLeft()) { + if (cursorAction == MoveLeft) + cursorAction = MoveRight; + else if (cursorAction == MoveRight) + cursorAction = MoveLeft; + } + switch (cursorAction) { + case MoveLeft: + if (current.parent().isValid() && current.parent() != rootIndex()) + return (current.parent()); + else + return current; + break; + + case MoveRight: + if (model()->hasChildren(current)) + return model()->index(0, 0, current); + else + return current.sibling(current.row() + 1, current.column()); + break; + + default: + break; + } + + return QModelIndex(); +} + +/*! + \reimp +*/ +void QColumnView::resizeEvent(QResizeEvent *event) +{ + Q_D(QColumnView); + d->doLayout(); + d->updateScrollbars(); + if (!isRightToLeft()) { + int diff = event->oldSize().width() - event->size().width(); + if (diff < 0 && horizontalScrollBar()->isVisible() + && horizontalScrollBar()->value() == horizontalScrollBar()->maximum()) { + horizontalScrollBar()->setMaximum(horizontalScrollBar()->maximum() + diff); + } + } + QAbstractItemView::resizeEvent(event); +} + +/*! + \internal +*/ +void QColumnViewPrivate::updateScrollbars() +{ + Q_Q(QColumnView); + if (currentAnimation.state() == QTimeLine::Running) + return; + + // find the total horizontal length of the laid out columns + int horizontalLength = 0; + if (!columns.isEmpty()) { + horizontalLength = (columns.last()->x() + columns.last()->width()) - columns.first()->x(); + if (horizontalLength <= 0) // reverse mode + horizontalLength = (columns.first()->x() + columns.first()->width()) - columns.last()->x(); + } + + QSize viewportSize = q->viewport()->size(); + if (horizontalLength < viewportSize.width() && q->horizontalScrollBar()->value() == 0) { + q->horizontalScrollBar()->setRange(0, 0); + } else { + int visibleLength = qMin(horizontalLength + q->horizontalOffset(), viewportSize.width()); + int hiddenLength = horizontalLength - visibleLength; + if (hiddenLength != q->horizontalScrollBar()->maximum()) + q->horizontalScrollBar()->setRange(0, hiddenLength); + } + if (!columns.isEmpty()) { + int pageStepSize = columns.at(0)->width(); + if (pageStepSize != q->horizontalScrollBar()->pageStep()) + q->horizontalScrollBar()->setPageStep(pageStepSize); + } + bool visible = (q->horizontalScrollBar()->maximum() > 0); + if (visible != q->horizontalScrollBar()->isVisible()) + q->horizontalScrollBar()->setVisible(visible); +} + +/*! + \reimp +*/ +int QColumnView::horizontalOffset() const +{ + Q_D(const QColumnView); + return d->offset; +} + +/*! + \reimp +*/ +int QColumnView::verticalOffset() const +{ + return 0; +} + +/*! + \reimp +*/ +QRegion QColumnView::visualRegionForSelection(const QItemSelection &selection) const +{ + int ranges = selection.count(); + + if (ranges == 0) + return QRect(); + + // Note that we use the top and bottom functions of the selection range + // since the data is stored in rows. + int firstRow = selection.at(0).top(); + int lastRow = selection.at(0).top(); + for (int i = 0; i < ranges; ++i) { + firstRow = qMin(firstRow, selection.at(i).top()); + lastRow = qMax(lastRow, selection.at(i).bottom()); + } + + QModelIndex firstIdx = model()->index(qMin(firstRow, lastRow), 0, rootIndex()); + QModelIndex lastIdx = model()->index(qMax(firstRow, lastRow), 0, rootIndex()); + + if (firstIdx == lastIdx) + return visualRect(firstIdx); + + QRegion firstRegion = visualRect(firstIdx); + QRegion lastRegion = visualRect(lastIdx); + return firstRegion.unite(lastRegion); +} + +/*! + \reimp +*/ +void QColumnView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command) +{ + Q_UNUSED(rect); + Q_UNUSED(command); +} + +/*! + \reimp +*/ +void QColumnView::setSelectionModel(QItemSelectionModel *newSelectionModel) +{ + Q_D(const QColumnView); + for (int i = 0; i < d->columns.size(); ++i) { + if (d->columns.at(i)->selectionModel() == selectionModel()) { + d->columns.at(i)->setSelectionModel(newSelectionModel); + break; + } + } + QAbstractItemView::setSelectionModel(newSelectionModel); +} + +/*! + \reimp +*/ +QSize QColumnView::sizeHint() const +{ + Q_D(const QColumnView); + QSize sizeHint; + for (int i = 0; i < d->columns.size(); ++i) { + sizeHint += d->columns.at(i)->sizeHint(); + } + return sizeHint.expandedTo(QAbstractItemView::sizeHint()); +} + +/*! + \internal + Move all widgets from the corner grip and to the right + */ +void QColumnViewPrivate::_q_gripMoved(int offset) +{ + Q_Q(QColumnView); + + QObject *grip = q->sender(); + Q_ASSERT(grip); + + if (q->isRightToLeft()) + offset = -1 * offset; + + bool found = false; + for (int i = 0; i < columns.size(); ++i) { + if (!found && columns.at(i)->cornerWidget() == grip) { + found = true; + columnSizes[i] = columns.at(i)->width(); + if (q->isRightToLeft()) + columns.at(i)->move(columns.at(i)->x() + offset, 0); + continue; + } + if (!found) + continue; + + int currentX = columns.at(i)->x(); + columns.at(i)->move(currentX + offset, 0); + } + + updateScrollbars(); +} + +/*! + \internal + + Find where the current columns intersect parent's columns + + Delete any extra columns and insert any needed columns. + */ +void QColumnViewPrivate::closeColumns(const QModelIndex &parent, bool build) +{ + if (columns.isEmpty()) + return; + + bool clearAll = !parent.isValid(); + bool passThroughRoot = false; + + QList<QModelIndex> dirsToAppend; + + // Find the last column that matches the parent's tree + int currentColumn = -1; + QModelIndex parentIndex = parent; + while (currentColumn == -1 && parentIndex.isValid()) { + if (columns.isEmpty()) + break; + parentIndex = parentIndex.parent(); + if (root == parentIndex) + passThroughRoot = true; + if (!parentIndex.isValid()) + break; + for (int i = columns.size() - 1; i >= 0; --i) { + if (columns.at(i)->rootIndex() == parentIndex) { + currentColumn = i; + break; + } + } + if (currentColumn == -1) + dirsToAppend.append(parentIndex); + } + + // Someone wants to go to an index that can be reached without changing + // the root index, don't allow them + if (!clearAll && !passThroughRoot && currentColumn == -1) + return; + + if (currentColumn == -1 && parent.isValid()) + currentColumn = 0; + + // Optimization so we don't go deleting and then creating the same thing + bool alreadyExists = false; + if (build && columns.size() > currentColumn + 1) { + bool viewingParent = (columns.at(currentColumn + 1)->rootIndex() == parent); + bool viewingChild = (!model->hasChildren(parent) + && !columns.at(currentColumn + 1)->rootIndex().isValid()); + if (viewingParent || viewingChild) { + currentColumn++; + alreadyExists = true; + } + } + + // Delete columns that don't match our path + for (int i = columns.size() - 1; i > currentColumn; --i) { + QAbstractItemView* notShownAnymore = columns.at(i); + columns.removeAt(i); + notShownAnymore->setVisible(false); + if (notShownAnymore != previewColumn) + notShownAnymore->deleteLater(); + } + + if (columns.isEmpty()) { + offset = 0; + updateScrollbars(); + } + + // Now fill in missing columns + while (!dirsToAppend.isEmpty()) { + QAbstractItemView *newView = createColumn(dirsToAppend.takeLast(), true); + if (!dirsToAppend.isEmpty()) + newView->setCurrentIndex(dirsToAppend.last()); + } + + if (build && !alreadyExists) + createColumn(parent, false); +} + +void QColumnViewPrivate::_q_clicked(const QModelIndex &index) +{ + Q_Q(QColumnView); + QModelIndex parent = index.parent(); + QAbstractItemView *columnClicked = 0; + for (int column = 0; column < columns.count(); ++column) { + if (columns.at(column)->rootIndex() == parent) { + columnClicked = columns[column]; + break; + } + } + if (q->selectionModel() && columnClicked) { + QItemSelectionModel::SelectionFlags flags = QItemSelectionModel::Current; + if (columnClicked->selectionModel()->isSelected(index)) + flags |= QItemSelectionModel::Select; + q->selectionModel()->setCurrentIndex(index, flags); + } +} + +/*! + \internal + Create a new column for \a index. A grip is attached if requested and it is shown + if requested. + + Return the new view + + \sa createColumn() setPreviewWidget() + \sa doLayout() +*/ +QAbstractItemView *QColumnViewPrivate::createColumn(const QModelIndex &index, bool show) +{ + Q_Q(QColumnView); + QAbstractItemView *view = 0; + if (model->hasChildren(index)) { + view = q->createColumn(index); + q->connect(view, SIGNAL(clicked(const QModelIndex &)), + q, SLOT(_q_clicked(const QModelIndex &))); + } else { + if (!previewColumn) + setPreviewWidget(new QWidget(q)); + view = previewColumn; + view->setMinimumWidth(qMax(view->minimumWidth(), previewWidget->minimumWidth())); + } + + q->connect(view, SIGNAL(activated(const QModelIndex &)), + q, SIGNAL(activated(const QModelIndex &))); + q->connect(view, SIGNAL(clicked(const QModelIndex &)), + q, SIGNAL(clicked(const QModelIndex &))); + q->connect(view, SIGNAL(doubleClicked(const QModelIndex &)), + q, SIGNAL(doubleClicked(const QModelIndex &))); + q->connect(view, SIGNAL(entered(const QModelIndex &)), + q, SIGNAL(entered(const QModelIndex &))); + q->connect(view, SIGNAL(pressed(const QModelIndex &)), + q, SIGNAL(pressed(const QModelIndex &))); + + view->setFocusPolicy(Qt::NoFocus); + view->setParent(q->viewport()); + Q_ASSERT(view); + + // Setup corner grip + if (showResizeGrips) { + QColumnViewGrip *grip = new QColumnViewGrip(view); + view->setCornerWidget(grip); + q->connect(grip, SIGNAL(gripMoved(int)), q, SLOT(_q_gripMoved(int))); + } + + if (columnSizes.count() > columns.count()) { + view->setGeometry(0, 0, columnSizes.at(columns.count()), q->viewport()->height()); + } else { + int initialWidth = view->sizeHint().width(); + if (q->isRightToLeft()) + view->setGeometry(q->viewport()->width() - initialWidth, 0, initialWidth, q->viewport()->height()); + else + view->setGeometry(0, 0, initialWidth, q->viewport()->height()); + columnSizes.resize(qMax(columnSizes.count(), columns.count() + 1)); + columnSizes[columns.count()] = initialWidth; + } + if (!columns.isEmpty() && columns.last()->isHidden()) + columns.last()->setVisible(true); + + columns.append(view); + doLayout(); + updateScrollbars(); + if (show && view->isHidden()) + view->setVisible(true); + return view; +} + +/*! + \fn void QColumnView::updatePreviewWidget(const QModelIndex &index) + + This signal is emitted when the preview widget should be updated to + provide rich information about \a index + + \sa previewWidget() + */ + +/*! + To use a custom widget for the final column when you select + an item overload this function and return a widget. + \a index is the root index that will be assigned to the view. + + Return the new view. QColumnView will automatically take ownership of the widget. + + \sa setPreviewWidget() + */ +QAbstractItemView *QColumnView::createColumn(const QModelIndex &index) +{ + QListView *view = new QListView(viewport()); + + initializeColumn(view); + + view->setRootIndex(index); + if (model()->canFetchMore(index)) + model()->fetchMore(index); + + return view; +} + +/*! + Copies the behavior and options of the column view and applies them to + the \a column such as the iconSize(), textElideMode() and + alternatingRowColors(). This can be useful when reimplementing + createColumn(). + + \since 4.4 + \sa createColumn() + */ +void QColumnView::initializeColumn(QAbstractItemView *column) const +{ + Q_D(const QColumnView); + + column->setFrameShape(QFrame::NoFrame); + column->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + column->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + column->setMinimumWidth(100); + column->setAttribute(Qt::WA_MacShowFocusRect, false); + +#ifndef QT_NO_DRAGANDDROP + column->setDragDropMode(dragDropMode()); + column->setDragDropOverwriteMode(dragDropOverwriteMode()); + column->setDropIndicatorShown(showDropIndicator()); +#endif + column->setAlternatingRowColors(alternatingRowColors()); + column->setAutoScroll(hasAutoScroll()); + column->setEditTriggers(editTriggers()); + column->setHorizontalScrollMode(horizontalScrollMode()); + column->setIconSize(iconSize()); + column->setSelectionBehavior(selectionBehavior()); + column->setSelectionMode(selectionMode()); + column->setTabKeyNavigation(tabKeyNavigation()); + column->setTextElideMode(textElideMode()); + column->setVerticalScrollMode(verticalScrollMode()); + + column->setModel(model()); + + // Copy the custom delegate per row + QMapIterator<int, QPointer<QAbstractItemDelegate> > i(d->rowDelegates); + while (i.hasNext()) { + i.next(); + column->setItemDelegateForRow(i.key(), i.value()); + } + + // set the delegate to be the columnview delegate + QAbstractItemDelegate *delegate = column->itemDelegate(); + column->setItemDelegate(d->itemDelegate); + delete delegate; +} + +/*! + Returns the preview widget, or 0 if there is none. + + \sa setPreviewWidget(), updatePreviewWidget() +*/ +QWidget *QColumnView::previewWidget() const +{ + Q_D(const QColumnView); + return d->previewWidget; +} + +/*! + Sets the preview \a widget. + + The \a widget becomes a child of the column view, and will be + destroyed when the column area is deleted or when a new widget is + set. + + \sa previewWidget(), updatePreviewWidget() +*/ +void QColumnView::setPreviewWidget(QWidget *widget) +{ + Q_D(QColumnView); + d->setPreviewWidget(widget); +} + +/*! + \internal +*/ +void QColumnViewPrivate::setPreviewWidget(QWidget *widget) +{ + Q_Q(QColumnView); + if (previewColumn) { + if (!columns.isEmpty() && columns.last() == previewColumn) + columns.removeLast(); + previewColumn->deleteLater(); + } + QColumnViewPreviewColumn *column = new QColumnViewPreviewColumn(q); + column->setPreviewWidget(widget); + previewColumn = column; + previewColumn->hide(); + previewColumn->setFrameShape(QFrame::NoFrame); + previewColumn->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + previewColumn->setSelectionMode(QAbstractItemView::NoSelection); + previewColumn->setMinimumWidth(qMax(previewColumn->verticalScrollBar()->width(), + previewColumn->minimumWidth())); + previewWidget = widget; + previewWidget->setParent(previewColumn->viewport()); +} + +/*! + Sets the column widths to the values given in the \a list. Extra values in the list are + kept and used when the columns are created. + + If list contains too few values, only width of the rest of the columns will not be modified. + + \sa columnWidths(), createColumn() +*/ +void QColumnView::setColumnWidths(const QList<int> &list) +{ + Q_D(QColumnView); + int i = 0; + for (; (i < list.count() && i < d->columns.count()); ++i) { + d->columns.at(i)->resize(list.at(i), d->columns.at(i)->height()); + d->columnSizes[i] = list.at(i); + } + for (; i < list.count(); ++i) + d->columnSizes.append(list.at(i)); +} + +/*! + Returns a list of the width of all the columns in this view. + + \sa setColumnWidths() +*/ +QList<int> QColumnView::columnWidths() const +{ + Q_D(const QColumnView); + QList<int> list; + for (int i = 0; i < d->columns.count(); ++i) + list.append(d->columnSizes.at(i)); + return list; +} + +/*! + \reimp +*/ +void QColumnView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) +{ + Q_D(QColumnView); + if (!current.isValid()) { + QAbstractItemView::currentChanged(current, previous); + return; + } + + QModelIndex currentParent = current.parent(); + // optimize for just moving up/down in a list where the child view doesn't change + if (currentParent == previous.parent() + && model()->hasChildren(current) && model()->hasChildren(previous)) { + for (int i = 0; i < d->columns.size(); ++i) { + if (currentParent == d->columns.at(i)->rootIndex()) { + if (d->columns.size() > i + 1) { + QAbstractItemView::currentChanged(current, previous); + return; + } + break; + } + } + } + + // Scrolling to the right we need to have an empty spot + bool found = false; + if (currentParent == previous) { + for (int i = 0; i < d->columns.size(); ++i) { + if (currentParent == d->columns.at(i)->rootIndex()) { + found = true; + if (d->columns.size() < i + 2) { + d->createColumn(current, false); + } + break; + } + } + } + if (!found) + d->closeColumns(current, true); + + if (!model()->hasChildren(current)) + emit updatePreviewWidget(current); + + QAbstractItemView::currentChanged(current, previous); +} + +/* + We have change the current column and need to update focus and selection models + on the new current column. +*/ +void QColumnViewPrivate::_q_changeCurrentColumn() +{ + Q_Q(QColumnView); + if (columns.isEmpty()) + return; + + QModelIndex current = q->currentIndex(); + if (!current.isValid()) + return; + + // We might have scrolled far to the left so we need to close all of the children + closeColumns(current, true); + + // Set up the "current" column with focus + int currentColumn = qMax(0, columns.size() - 2); + QAbstractItemView *parentColumn = columns.at(currentColumn); + if (q->hasFocus()) + parentColumn->setFocus(Qt::OtherFocusReason); + q->setFocusProxy(parentColumn); + + // find the column that is our current selection model and give it a new one. + for (int i = 0; i < columns.size(); ++i) { + if (columns.at(i)->selectionModel() == q->selectionModel()) { + QItemSelectionModel *replacementSelectionModel = + new QItemSelectionModel(parentColumn->model()); + replacementSelectionModel->setCurrentIndex( + q->selectionModel()->currentIndex(), QItemSelectionModel::Current); + replacementSelectionModel->select( + q->selectionModel()->selection(), QItemSelectionModel::Select); + QAbstractItemView *view = columns.at(i); + view->setSelectionModel(replacementSelectionModel); + view->setFocusPolicy(Qt::NoFocus); + if (columns.size() > i + 1) + view->setCurrentIndex(columns.at(i+1)->rootIndex()); + break; + } + } + parentColumn->selectionModel()->deleteLater(); + parentColumn->setFocusPolicy(Qt::StrongFocus); + parentColumn->setSelectionModel(q->selectionModel()); + // We want the parent selection to stay highlighted (but dimmed depending upon the color theme) + if (currentColumn > 0) { + parentColumn = columns.at(currentColumn - 1); + if (parentColumn->currentIndex() != current.parent()) + parentColumn->setCurrentIndex(current.parent()); + } + + if (columns.last()->isHidden()) { + columns.last()->setVisible(true); + } + if (columns.last()->selectionModel()) + columns.last()->selectionModel()->clear(); + updateScrollbars(); +} + +/*! + \reimp +*/ +void QColumnView::selectAll() +{ + if (!model() || !selectionModel()) + return; + + QModelIndexList indexList = selectionModel()->selectedIndexes(); + QModelIndex parent = rootIndex(); + QItemSelection selection; + if (indexList.count() >= 1) + parent = indexList.at(0).parent(); + if (indexList.count() == 1) { + parent = indexList.at(0); + if (!model()->hasChildren(parent)) + parent = parent.parent(); + else + selection.append(QItemSelectionRange(parent, parent)); + } + + QModelIndex tl = model()->index(0, 0, parent); + QModelIndex br = model()->index(model()->rowCount(parent) - 1, + model()->columnCount(parent) - 1, + parent); + selection.append(QItemSelectionRange(tl, br)); + selectionModel()->select(selection, QItemSelectionModel::ClearAndSelect); +} + +/* + * private object implementation + */ +QColumnViewPrivate::QColumnViewPrivate() +: QAbstractItemViewPrivate() +,showResizeGrips(true) +,offset(0) +,currentAnimation(ANIMATION_DURATION_MSEC) +,previewWidget(0) +,previewColumn(0) +{ +} + +QColumnViewPrivate::~QColumnViewPrivate() +{ +} + +/*! + \internal + Place all of the columns where they belong inside of the viewport, resize as necessary. +*/ +void QColumnViewPrivate::doLayout() +{ + Q_Q(QColumnView); + if (!model || columns.isEmpty()) + return; + + int viewportHeight = q->viewport()->height(); + int x = columns.at(0)->x(); + + if (q->isRightToLeft()) { + x = q->viewport()->width() + q->horizontalOffset(); + for (int i = 0; i < columns.size(); ++i) { + QAbstractItemView *view = columns.at(i); + x -= view->width(); + if (x != view->x() || viewportHeight != view->height()) + view->setGeometry(x, 0, view->width(), viewportHeight); + } + } else { + for (int i = 0; i < columns.size(); ++i) { + QAbstractItemView *view = columns.at(i); + int currentColumnWidth = view->width(); + if (x != view->x() || viewportHeight != view->height()) + view->setGeometry(x, 0, currentColumnWidth, viewportHeight); + x += currentColumnWidth; + } + } +} + +/*! + \internal + + Draws a delegate with a > if an object has children. + + \sa {Model/View Programming}, QItemDelegate +*/ +void QColumnViewDelegate::paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + drawBackground(painter, option, index ); + + bool reverse = (option.direction == Qt::RightToLeft); + int width = ((option.rect.height() * 2) / 3); + // Modify the options to give us room to add an arrow + QStyleOptionViewItemV4 opt = option; + if (reverse) + opt.rect.adjust(width,0,0,0); + else + opt.rect.adjust(0,0,-width,0); + + if (!(index.model()->flags(index) & Qt::ItemIsEnabled)) { + opt.showDecorationSelected = true; + opt.state |= QStyle::State_Selected; + } + + QItemDelegate::paint(painter, opt, index); + + if (reverse) + opt.rect = QRect(option.rect.x(), option.rect.y(), width, option.rect.height()); + else + opt.rect = QRect(option.rect.x() + option.rect.width() - width, option.rect.y(), + width, option.rect.height()); + + // Draw > + if (index.model()->hasChildren(index)) { + const QWidget *view = opt.widget; + QStyle *style = view ? view->style() : qApp->style(); + style->drawPrimitive(QStyle::PE_IndicatorColumnViewArrow, &opt, painter, view); + } +} + +QT_END_NAMESPACE + +#include "moc_qcolumnview.cpp" + +#endif // QT_NO_COLUMNVIEW |