src/gui/itemviews/qcolumnview.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtGui module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include <qglobal.h>
       
    43 
       
    44 #ifndef QT_NO_COLUMNVIEW
       
    45 
       
    46 #include "qcolumnview.h"
       
    47 #include "qcolumnview_p.h"
       
    48 #include "qcolumnviewgrip_p.h"
       
    49 
       
    50 #include <qlistview.h>
       
    51 #include <qabstractitemdelegate.h>
       
    52 #include <qscrollbar.h>
       
    53 #include <qpainter.h>
       
    54 #include <qdebug.h>
       
    55 
       
    56 QT_BEGIN_NAMESPACE
       
    57 
       
    58 #define ANIMATION_DURATION_MSEC 150
       
    59 
       
    60 /*!
       
    61     \since 4.3
       
    62     \class QColumnView
       
    63     \brief The QColumnView class provides a model/view implementation of a column view.
       
    64     \ingroup model-view
       
    65     \ingroup advanced
       
    66 
       
    67 
       
    68     QColumnView displays a model in a number of QListViews, one for each
       
    69     hierarchy in the tree.  This is sometimes referred to as a cascading list.
       
    70 
       
    71     The QColumnView class is one of the \l{Model/View Classes}
       
    72     and is part of Qt's \l{Model/View Programming}{model/view framework}.
       
    73 
       
    74     QColumnView implements the interfaces defined by the
       
    75     QAbstractItemView class to allow it to display data provided by
       
    76     models derived from the QAbstractItemModel class.
       
    77 
       
    78     \image qcolumnview.png
       
    79 
       
    80     \sa \link model-view-programming.html Model/View Programming\endlink
       
    81 */
       
    82 
       
    83 /*!
       
    84     Constructs a column view with a \a parent to represent a model's
       
    85     data. Use setModel() to set the model.
       
    86 
       
    87     \sa QAbstractItemModel
       
    88 */
       
    89 QColumnView::QColumnView(QWidget * parent)
       
    90 :  QAbstractItemView(*new QColumnViewPrivate, parent)
       
    91 {
       
    92     Q_D(QColumnView);
       
    93     d->initialize();
       
    94 }
       
    95 
       
    96 /*!
       
    97     \internal
       
    98 */
       
    99 QColumnView::QColumnView(QColumnViewPrivate & dd, QWidget * parent)
       
   100 :  QAbstractItemView(dd, parent)
       
   101 {
       
   102     Q_D(QColumnView);
       
   103     d->initialize();
       
   104 }
       
   105 
       
   106 void QColumnViewPrivate::initialize()
       
   107 {
       
   108     Q_Q(QColumnView);
       
   109     q->setTextElideMode(Qt::ElideMiddle);
       
   110 #ifndef QT_NO_ANIMATION
       
   111     QObject::connect(&currentAnimation, SIGNAL(finished()), q, SLOT(_q_changeCurrentColumn()));
       
   112     currentAnimation.setDuration(ANIMATION_DURATION_MSEC);
       
   113     currentAnimation.setTargetObject(hbar);
       
   114     currentAnimation.setPropertyName("value");
       
   115     currentAnimation.setEasingCurve(QEasingCurve::InOutQuad);
       
   116 #endif //QT_NO_ANIMATION
       
   117     delete itemDelegate;
       
   118     q->setItemDelegate(new QColumnViewDelegate(q));
       
   119 }
       
   120 
       
   121 /*!
       
   122     Destroys the column view.
       
   123 */
       
   124 QColumnView::~QColumnView()
       
   125 {
       
   126 }
       
   127 
       
   128 /*!
       
   129     \property QColumnView::resizeGripsVisible
       
   130     \brief the way to specify if the list views gets resize grips or not
       
   131 
       
   132     By default, \c visible is set to true
       
   133 
       
   134     \sa setRootIndex()
       
   135 */
       
   136 void QColumnView::setResizeGripsVisible(bool visible)
       
   137 {
       
   138     Q_D(QColumnView);
       
   139     if (d->showResizeGrips == visible)
       
   140         return;
       
   141     d->showResizeGrips = visible;
       
   142     for (int i = 0; i < d->columns.count(); ++i) {
       
   143         QAbstractItemView *view = d->columns[i];
       
   144         if (visible) {
       
   145             QColumnViewGrip *grip = new QColumnViewGrip(view);
       
   146             view->setCornerWidget(grip);
       
   147             connect(grip, SIGNAL(gripMoved(int)), this, SLOT(_q_gripMoved(int)));
       
   148         } else {
       
   149             QWidget *widget = view->cornerWidget();
       
   150             view->setCornerWidget(0);
       
   151             widget->deleteLater();
       
   152         }
       
   153     }
       
   154 }
       
   155 
       
   156 bool QColumnView::resizeGripsVisible() const
       
   157 {
       
   158     Q_D(const QColumnView);
       
   159     return d->showResizeGrips;
       
   160 }
       
   161 
       
   162 /*!
       
   163     \reimp
       
   164 */
       
   165 void QColumnView::setModel(QAbstractItemModel *model)
       
   166 {
       
   167     Q_D(QColumnView);
       
   168     if (model == d->model)
       
   169         return;
       
   170     d->closeColumns();
       
   171     QAbstractItemView::setModel(model);
       
   172 }
       
   173 
       
   174 /*!
       
   175     \reimp
       
   176 */
       
   177 void QColumnView::setRootIndex(const QModelIndex &index)
       
   178 {
       
   179     Q_D(QColumnView);
       
   180     if (!model())
       
   181         return;
       
   182 
       
   183     d->closeColumns();
       
   184     Q_ASSERT(d->columns.count() == 0);
       
   185 
       
   186     QAbstractItemView *view = d->createColumn(index, true);
       
   187     if (view->selectionModel())
       
   188         view->selectionModel()->deleteLater();
       
   189     if (view->model())
       
   190         view->setSelectionModel(selectionModel());
       
   191 
       
   192     QAbstractItemView::setRootIndex(index);
       
   193     d->updateScrollbars();
       
   194 }
       
   195 
       
   196 /*!
       
   197     \reimp
       
   198 */
       
   199 bool QColumnView::isIndexHidden(const QModelIndex &index) const
       
   200 {
       
   201     Q_UNUSED(index);
       
   202     return false;
       
   203 }
       
   204 
       
   205 /*!
       
   206     \reimp
       
   207 */
       
   208 QModelIndex QColumnView::indexAt(const QPoint &point) const
       
   209 {
       
   210     Q_D(const QColumnView);
       
   211     for (int i = 0; i < d->columns.size(); ++i) {
       
   212         QPoint topLeft = d->columns.at(i)->frameGeometry().topLeft();
       
   213         QPoint adjustedPoint(point.x() - topLeft.x(), point.y() - topLeft.y());
       
   214         QModelIndex index = d->columns.at(i)->indexAt(adjustedPoint);
       
   215         if (index.isValid())
       
   216             return index;
       
   217     }
       
   218     return QModelIndex();
       
   219 }
       
   220 
       
   221 /*!
       
   222     \reimp
       
   223 */
       
   224 QRect QColumnView::visualRect(const QModelIndex &index) const
       
   225 {
       
   226     if (!index.isValid())
       
   227         return QRect();
       
   228 
       
   229     Q_D(const QColumnView);
       
   230     for (int i = 0; i < d->columns.size(); ++i) {
       
   231         QRect rect = d->columns.at(i)->visualRect(index);
       
   232         if (!rect.isNull()) {
       
   233             rect.translate(d->columns.at(i)->frameGeometry().topLeft());
       
   234             return rect;
       
   235         }
       
   236     }
       
   237     return QRect();
       
   238 }
       
   239 
       
   240 /*!
       
   241     \reimp
       
   242  */
       
   243 void QColumnView::scrollContentsBy(int dx, int dy)
       
   244 {
       
   245     Q_D(QColumnView);
       
   246     if (d->columns.isEmpty() || dx == 0)
       
   247         return;
       
   248 
       
   249     dx = isRightToLeft() ? -dx : dx;
       
   250     for (int i = 0; i < d->columns.count(); ++i)
       
   251         d->columns.at(i)->move(d->columns.at(i)->x() + dx, 0);
       
   252     d->offset += dx;
       
   253     QAbstractItemView::scrollContentsBy(dx, dy);
       
   254 }
       
   255 
       
   256 /*!
       
   257     \reimp
       
   258 */
       
   259 void QColumnView::scrollTo(const QModelIndex &index, ScrollHint hint)
       
   260 {
       
   261     Q_D(QColumnView);
       
   262     Q_UNUSED(hint);
       
   263     if (!index.isValid() || d->columns.isEmpty())
       
   264         return;
       
   265 
       
   266 #ifndef QT_NO_ANIMATION
       
   267     if (d->currentAnimation.state() == QPropertyAnimation::Running)
       
   268         return;
       
   269 
       
   270     d->currentAnimation.stop();
       
   271 #endif //QT_NO_ANIMATION
       
   272 
       
   273     // Fill up what is needed to get to index
       
   274     d->closeColumns(index, true);
       
   275 
       
   276     QModelIndex indexParent = index.parent();
       
   277     // Find the left edge of the column that contains index
       
   278     int currentColumn = 0;
       
   279     int leftEdge = 0;
       
   280     while (currentColumn < d->columns.size()) {
       
   281         if (indexParent == d->columns.at(currentColumn)->rootIndex())
       
   282             break;
       
   283         leftEdge += d->columns.at(currentColumn)->width();
       
   284         ++currentColumn;
       
   285     }
       
   286 
       
   287     // Don't let us scroll above the root index
       
   288     if (currentColumn == d->columns.size())
       
   289         return;
       
   290 
       
   291     int indexColumn = currentColumn;
       
   292     // Find the width of what we want to show (i.e. the right edge)
       
   293     int visibleWidth = d->columns.at(currentColumn)->width();
       
   294     // We want to always try to show two columns
       
   295     if (currentColumn + 1 < d->columns.size()) {
       
   296         ++currentColumn;
       
   297         visibleWidth += d->columns.at(currentColumn)->width();
       
   298     }
       
   299 
       
   300     int rightEdge = leftEdge + visibleWidth;
       
   301     if (isRightToLeft()) {
       
   302         leftEdge = viewport()->width() - leftEdge;
       
   303         rightEdge = leftEdge - visibleWidth;
       
   304         qSwap(rightEdge, leftEdge);
       
   305     }
       
   306 
       
   307     // If it is already visible don't animate
       
   308     if (leftEdge > -horizontalOffset()
       
   309         && rightEdge <= ( -horizontalOffset() + viewport()->size().width())) {
       
   310             d->columns.at(indexColumn)->scrollTo(index);
       
   311             d->_q_changeCurrentColumn();
       
   312             return;
       
   313     }
       
   314 
       
   315     int newScrollbarValue = 0;
       
   316     if (isRightToLeft()) {
       
   317         if (leftEdge < 0) {
       
   318             // scroll to the right
       
   319             newScrollbarValue = viewport()->size().width() - leftEdge;
       
   320         } else {
       
   321             // scroll to the left
       
   322             newScrollbarValue = rightEdge + horizontalOffset();
       
   323         }
       
   324     } else {
       
   325         if (leftEdge > -horizontalOffset()) {
       
   326             // scroll to the right
       
   327             newScrollbarValue = rightEdge - viewport()->size().width();
       
   328         } else {
       
   329             // scroll to the left
       
   330             newScrollbarValue = leftEdge;
       
   331         }
       
   332     }
       
   333 
       
   334 #ifndef QT_NO_ANIMATION
       
   335     d->currentAnimation.setEndValue(newScrollbarValue);
       
   336     d->currentAnimation.start();
       
   337 #else
       
   338     horizontalScrollBar()->setValue(newScrollbarValue);
       
   339 #endif //QT_NO_ANIMATION
       
   340 }
       
   341 
       
   342 /*!
       
   343     \reimp
       
   344     Move left should go to the parent index
       
   345     Move right should go to the child index or down if there is no child
       
   346 */
       
   347 QModelIndex QColumnView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
       
   348 {
       
   349     // the child views which have focus get to deal with this first and if
       
   350     // they don't accept it then it comes up this view and we only grip left/right
       
   351     Q_UNUSED(modifiers);
       
   352     if (!model())
       
   353         return QModelIndex();
       
   354 
       
   355     QModelIndex current = currentIndex();
       
   356     if (isRightToLeft()) {
       
   357         if (cursorAction == MoveLeft)
       
   358             cursorAction = MoveRight;
       
   359         else if (cursorAction == MoveRight)
       
   360             cursorAction = MoveLeft;
       
   361     }
       
   362     switch (cursorAction) {
       
   363     case MoveLeft:
       
   364         if (current.parent().isValid() && current.parent() != rootIndex())
       
   365             return (current.parent());
       
   366         else
       
   367             return current;
       
   368         break;
       
   369 
       
   370     case MoveRight:
       
   371         if (model()->hasChildren(current))
       
   372             return model()->index(0, 0, current);
       
   373         else
       
   374             return current.sibling(current.row() + 1, current.column());
       
   375         break;
       
   376 
       
   377     default:
       
   378         break;
       
   379     }
       
   380 
       
   381     return QModelIndex();
       
   382 }
       
   383 
       
   384 /*!
       
   385     \reimp
       
   386 */
       
   387 void QColumnView::resizeEvent(QResizeEvent *event)
       
   388 {
       
   389     Q_D(QColumnView);
       
   390     d->doLayout();
       
   391     d->updateScrollbars();
       
   392     if (!isRightToLeft()) {
       
   393         int diff = event->oldSize().width() - event->size().width();
       
   394         if (diff < 0 && horizontalScrollBar()->isVisible()
       
   395             && horizontalScrollBar()->value() == horizontalScrollBar()->maximum()) {
       
   396             horizontalScrollBar()->setMaximum(horizontalScrollBar()->maximum() + diff);
       
   397         }
       
   398     }
       
   399     QAbstractItemView::resizeEvent(event);
       
   400 }
       
   401 
       
   402 /*!
       
   403     \internal
       
   404 */
       
   405 void QColumnViewPrivate::updateScrollbars()
       
   406 {
       
   407     Q_Q(QColumnView);
       
   408 #ifndef QT_NO_ANIMATION
       
   409     if (currentAnimation.state() == QPropertyAnimation::Running)
       
   410         return;
       
   411 #endif //QT_NO_ANIMATION
       
   412 
       
   413     // find the total horizontal length of the laid out columns
       
   414     int horizontalLength = 0;
       
   415     if (!columns.isEmpty()) {
       
   416         horizontalLength = (columns.last()->x() + columns.last()->width()) - columns.first()->x();
       
   417         if (horizontalLength <= 0) // reverse mode
       
   418             horizontalLength = (columns.first()->x() + columns.first()->width()) - columns.last()->x();
       
   419     }
       
   420 
       
   421     QSize viewportSize = viewport->size();
       
   422     if (horizontalLength < viewportSize.width() && hbar->value() == 0) {
       
   423         hbar->setRange(0, 0);
       
   424     } else {
       
   425         int visibleLength = qMin(horizontalLength + q->horizontalOffset(), viewportSize.width());
       
   426         int hiddenLength = horizontalLength - visibleLength;
       
   427         if (hiddenLength != hbar->maximum())
       
   428             hbar->setRange(0, hiddenLength);
       
   429     }
       
   430     if (!columns.isEmpty()) {
       
   431         int pageStepSize = columns.at(0)->width();
       
   432         if (pageStepSize != hbar->pageStep())
       
   433             hbar->setPageStep(pageStepSize);
       
   434     }
       
   435     bool visible = (hbar->maximum() > 0);
       
   436     if (visible != hbar->isVisible())
       
   437         hbar->setVisible(visible);
       
   438 }
       
   439 
       
   440 /*!
       
   441     \reimp
       
   442 */
       
   443 int QColumnView::horizontalOffset() const
       
   444 {
       
   445     Q_D(const QColumnView);
       
   446     return d->offset;
       
   447 }
       
   448 
       
   449 /*!
       
   450     \reimp
       
   451 */
       
   452 int QColumnView::verticalOffset() const
       
   453 {
       
   454     return 0;
       
   455 }
       
   456 
       
   457 /*!
       
   458     \reimp
       
   459 */
       
   460 QRegion QColumnView::visualRegionForSelection(const QItemSelection &selection) const
       
   461 {
       
   462     int ranges = selection.count();
       
   463 
       
   464     if (ranges == 0)
       
   465         return QRect();
       
   466 
       
   467     // Note that we use the top and bottom functions of the selection range
       
   468     // since the data is stored in rows.
       
   469     int firstRow = selection.at(0).top();
       
   470     int lastRow = selection.at(0).top();
       
   471     for (int i = 0; i < ranges; ++i) {
       
   472         firstRow = qMin(firstRow, selection.at(i).top());
       
   473         lastRow = qMax(lastRow, selection.at(i).bottom());
       
   474     }
       
   475 
       
   476     QModelIndex firstIdx = model()->index(qMin(firstRow, lastRow), 0, rootIndex());
       
   477     QModelIndex lastIdx = model()->index(qMax(firstRow, lastRow), 0, rootIndex());
       
   478 
       
   479     if (firstIdx == lastIdx)
       
   480         return visualRect(firstIdx);
       
   481 
       
   482     QRegion firstRegion = visualRect(firstIdx);
       
   483     QRegion lastRegion = visualRect(lastIdx);
       
   484     return firstRegion.unite(lastRegion);
       
   485 }
       
   486 
       
   487 /*!
       
   488     \reimp
       
   489 */
       
   490 void QColumnView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command)
       
   491 {
       
   492     Q_UNUSED(rect);
       
   493     Q_UNUSED(command);
       
   494 }
       
   495 
       
   496 /*!
       
   497     \reimp
       
   498 */
       
   499 void QColumnView::setSelectionModel(QItemSelectionModel *newSelectionModel)
       
   500 {
       
   501     Q_D(const QColumnView);
       
   502     for (int i = 0; i < d->columns.size(); ++i) {
       
   503         if (d->columns.at(i)->selectionModel() == selectionModel()) {
       
   504             d->columns.at(i)->setSelectionModel(newSelectionModel);
       
   505             break;
       
   506         }
       
   507     }
       
   508     QAbstractItemView::setSelectionModel(newSelectionModel);
       
   509 }
       
   510 
       
   511 /*!
       
   512     \reimp
       
   513 */
       
   514 QSize QColumnView::sizeHint() const
       
   515 {
       
   516     Q_D(const QColumnView);
       
   517     QSize sizeHint;
       
   518     for (int i = 0; i < d->columns.size(); ++i) {
       
   519         sizeHint += d->columns.at(i)->sizeHint();
       
   520     }
       
   521     return sizeHint.expandedTo(QAbstractItemView::sizeHint());
       
   522 }
       
   523 
       
   524 /*!
       
   525     \internal
       
   526     Move all widgets from the corner grip and to the right
       
   527   */
       
   528 void QColumnViewPrivate::_q_gripMoved(int offset)
       
   529 {
       
   530     Q_Q(QColumnView);
       
   531 
       
   532     QObject *grip = q->sender();
       
   533     Q_ASSERT(grip);
       
   534 
       
   535     if (q->isRightToLeft())
       
   536         offset = -1 * offset;
       
   537 
       
   538     bool found = false;
       
   539     for (int i = 0; i < columns.size(); ++i) {
       
   540         if (!found && columns.at(i)->cornerWidget() == grip) {
       
   541             found = true;
       
   542             columnSizes[i] = columns.at(i)->width();
       
   543             if (q->isRightToLeft())
       
   544                 columns.at(i)->move(columns.at(i)->x() + offset, 0);
       
   545             continue;
       
   546         }
       
   547         if (!found)
       
   548             continue;
       
   549 
       
   550         int currentX = columns.at(i)->x();
       
   551         columns.at(i)->move(currentX + offset, 0);
       
   552     }
       
   553 
       
   554     updateScrollbars();
       
   555 }
       
   556 
       
   557 /*!
       
   558     \internal
       
   559 
       
   560     Find where the current columns intersect parent's columns
       
   561 
       
   562     Delete any extra columns and insert any needed columns.
       
   563   */
       
   564 void QColumnViewPrivate::closeColumns(const QModelIndex &parent, bool build)
       
   565 {
       
   566     if (columns.isEmpty())
       
   567         return;
       
   568 
       
   569     bool clearAll = !parent.isValid();
       
   570     bool passThroughRoot = false;
       
   571 
       
   572     QList<QModelIndex> dirsToAppend;
       
   573 
       
   574     // Find the last column that matches the parent's tree
       
   575     int currentColumn = -1;
       
   576     QModelIndex parentIndex = parent;
       
   577     while (currentColumn == -1 && parentIndex.isValid()) {
       
   578         if (columns.isEmpty())
       
   579             break;
       
   580         parentIndex = parentIndex.parent();
       
   581         if (root == parentIndex)
       
   582             passThroughRoot = true;
       
   583         if (!parentIndex.isValid())
       
   584             break;
       
   585         for (int i = columns.size() - 1; i >= 0; --i) {
       
   586             if (columns.at(i)->rootIndex() == parentIndex) {
       
   587                 currentColumn = i;
       
   588                 break;
       
   589             }
       
   590         }
       
   591         if (currentColumn == -1)
       
   592             dirsToAppend.append(parentIndex);
       
   593     }
       
   594 
       
   595     // Someone wants to go to an index that can be reached without changing
       
   596     // the root index, don't allow them
       
   597     if (!clearAll && !passThroughRoot && currentColumn == -1)
       
   598         return;
       
   599 
       
   600     if (currentColumn == -1 && parent.isValid())
       
   601         currentColumn = 0;
       
   602 
       
   603     // Optimization so we don't go deleting and then creating the same thing
       
   604     bool alreadyExists = false;
       
   605     if (build && columns.size() > currentColumn + 1) {
       
   606         bool viewingParent = (columns.at(currentColumn + 1)->rootIndex() == parent);
       
   607         bool viewingChild = (!model->hasChildren(parent)
       
   608                              && !columns.at(currentColumn + 1)->rootIndex().isValid());
       
   609         if (viewingParent || viewingChild) {
       
   610             currentColumn++;
       
   611             alreadyExists = true;
       
   612         }
       
   613     }
       
   614 
       
   615     // Delete columns that don't match our path
       
   616     for (int i = columns.size() - 1; i > currentColumn; --i) {
       
   617         QAbstractItemView* notShownAnymore = columns.at(i);
       
   618         columns.removeAt(i);
       
   619         notShownAnymore->setVisible(false);
       
   620         if (notShownAnymore != previewColumn)
       
   621             notShownAnymore->deleteLater();
       
   622     }
       
   623 
       
   624     if (columns.isEmpty()) {
       
   625         offset = 0;
       
   626         updateScrollbars();
       
   627     }
       
   628 
       
   629     // Now fill in missing columns
       
   630     while (!dirsToAppend.isEmpty()) {
       
   631         QAbstractItemView *newView = createColumn(dirsToAppend.takeLast(), true);
       
   632         if (!dirsToAppend.isEmpty())
       
   633             newView->setCurrentIndex(dirsToAppend.last());
       
   634     }
       
   635 
       
   636     if (build && !alreadyExists)
       
   637         createColumn(parent, false);
       
   638 }
       
   639 
       
   640 void QColumnViewPrivate::_q_clicked(const QModelIndex &index)
       
   641 {
       
   642     Q_Q(QColumnView);
       
   643     QModelIndex parent = index.parent();
       
   644     QAbstractItemView *columnClicked = 0;
       
   645     for (int column = 0; column < columns.count(); ++column) {
       
   646         if (columns.at(column)->rootIndex() == parent) {
       
   647             columnClicked = columns[column];
       
   648             break;
       
   649         }
       
   650     }
       
   651     if (q->selectionModel() && columnClicked) {
       
   652         QItemSelectionModel::SelectionFlags flags = QItemSelectionModel::Current;
       
   653         if (columnClicked->selectionModel()->isSelected(index))
       
   654             flags |= QItemSelectionModel::Select;
       
   655         q->selectionModel()->setCurrentIndex(index, flags);
       
   656     }
       
   657 }
       
   658 
       
   659 /*!
       
   660     \internal
       
   661     Create a new column for \a index.  A grip is attached if requested and it is shown
       
   662     if requested.
       
   663 
       
   664     Return the new view
       
   665 
       
   666     \sa createColumn() setPreviewWidget()
       
   667     \sa doLayout()
       
   668 */
       
   669 QAbstractItemView *QColumnViewPrivate::createColumn(const QModelIndex &index, bool show)
       
   670 {
       
   671     Q_Q(QColumnView);
       
   672     QAbstractItemView *view = 0;
       
   673     if (model->hasChildren(index)) {
       
   674         view = q->createColumn(index);
       
   675         q->connect(view, SIGNAL(clicked(const QModelIndex &)),
       
   676                    q, SLOT(_q_clicked(const QModelIndex &)));
       
   677     } else {
       
   678         if (!previewColumn)
       
   679             setPreviewWidget(new QWidget(q));
       
   680         view = previewColumn;
       
   681         view->setMinimumWidth(qMax(view->minimumWidth(), previewWidget->minimumWidth()));
       
   682     }
       
   683 
       
   684     q->connect(view, SIGNAL(activated(const QModelIndex &)),
       
   685             q, SIGNAL(activated(const QModelIndex &)));
       
   686     q->connect(view, SIGNAL(clicked(const QModelIndex &)),
       
   687             q, SIGNAL(clicked(const QModelIndex &)));
       
   688     q->connect(view, SIGNAL(doubleClicked(const QModelIndex &)),
       
   689             q, SIGNAL(doubleClicked(const QModelIndex &)));
       
   690     q->connect(view, SIGNAL(entered(const QModelIndex &)),
       
   691             q, SIGNAL(entered(const QModelIndex &)));
       
   692     q->connect(view, SIGNAL(pressed(const QModelIndex &)),
       
   693             q, SIGNAL(pressed(const QModelIndex &)));
       
   694 
       
   695     view->setFocusPolicy(Qt::NoFocus);
       
   696     view->setParent(viewport);
       
   697     Q_ASSERT(view);
       
   698 
       
   699     // Setup corner grip
       
   700     if (showResizeGrips) {
       
   701         QColumnViewGrip *grip = new QColumnViewGrip(view);
       
   702         view->setCornerWidget(grip);
       
   703         q->connect(grip, SIGNAL(gripMoved(int)), q, SLOT(_q_gripMoved(int)));
       
   704     }
       
   705 
       
   706     if (columnSizes.count() > columns.count()) {
       
   707         view->setGeometry(0, 0, columnSizes.at(columns.count()), viewport->height());
       
   708     } else {
       
   709         int initialWidth = view->sizeHint().width();
       
   710         if (q->isRightToLeft())
       
   711             view->setGeometry(viewport->width() - initialWidth, 0, initialWidth, viewport->height());
       
   712         else
       
   713             view->setGeometry(0, 0, initialWidth, viewport->height());
       
   714         columnSizes.resize(qMax(columnSizes.count(), columns.count() + 1));
       
   715         columnSizes[columns.count()] = initialWidth;
       
   716     }
       
   717     if (!columns.isEmpty() && columns.last()->isHidden())
       
   718         columns.last()->setVisible(true);
       
   719 
       
   720     columns.append(view);
       
   721     doLayout();
       
   722     updateScrollbars();
       
   723     if (show && view->isHidden())
       
   724         view->setVisible(true);
       
   725     return view;
       
   726 }
       
   727 
       
   728 /*!
       
   729     \fn void QColumnView::updatePreviewWidget(const QModelIndex &index)
       
   730 
       
   731     This signal is emitted when the preview widget should be updated to
       
   732     provide rich information about \a index
       
   733 
       
   734     \sa previewWidget()
       
   735  */
       
   736 
       
   737 /*!
       
   738     To use a custom widget for the final column when you select
       
   739     an item overload this function and return a widget.
       
   740     \a index is the root index that will be assigned to the view.
       
   741 
       
   742     Return the new view.  QColumnView will automatically take ownership of the widget.
       
   743 
       
   744     \sa setPreviewWidget()
       
   745  */
       
   746 QAbstractItemView *QColumnView::createColumn(const QModelIndex &index)
       
   747 {
       
   748     QListView *view = new QListView(viewport());
       
   749 
       
   750     initializeColumn(view);
       
   751 
       
   752     view->setRootIndex(index);
       
   753     if (model()->canFetchMore(index))
       
   754         model()->fetchMore(index);
       
   755 
       
   756     return view;
       
   757 }
       
   758 
       
   759 /*!
       
   760     Copies the behavior and options of the column view and applies them to
       
   761     the \a column such as the iconSize(), textElideMode() and
       
   762     alternatingRowColors(). This can be useful when reimplementing
       
   763     createColumn().
       
   764 
       
   765     \since 4.4
       
   766     \sa createColumn()
       
   767  */
       
   768 void QColumnView::initializeColumn(QAbstractItemView *column) const
       
   769 {
       
   770     Q_D(const QColumnView);
       
   771 
       
   772     column->setFrameShape(QFrame::NoFrame);
       
   773     column->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
       
   774     column->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
       
   775     column->setMinimumWidth(100);
       
   776     column->setAttribute(Qt::WA_MacShowFocusRect, false);
       
   777 
       
   778 #ifndef QT_NO_DRAGANDDROP
       
   779     column->setDragDropMode(dragDropMode());
       
   780     column->setDragDropOverwriteMode(dragDropOverwriteMode());
       
   781     column->setDropIndicatorShown(showDropIndicator());
       
   782 #endif
       
   783     column->setAlternatingRowColors(alternatingRowColors());
       
   784     column->setAutoScroll(hasAutoScroll());
       
   785     column->setEditTriggers(editTriggers());
       
   786     column->setHorizontalScrollMode(horizontalScrollMode());
       
   787     column->setIconSize(iconSize());
       
   788     column->setSelectionBehavior(selectionBehavior());
       
   789     column->setSelectionMode(selectionMode());
       
   790     column->setTabKeyNavigation(tabKeyNavigation());
       
   791     column->setTextElideMode(textElideMode());
       
   792     column->setVerticalScrollMode(verticalScrollMode());
       
   793 
       
   794     column->setModel(model());
       
   795 
       
   796     // Copy the custom delegate per row
       
   797     QMapIterator<int, QPointer<QAbstractItemDelegate> > i(d->rowDelegates);
       
   798     while (i.hasNext()) {
       
   799         i.next();
       
   800         column->setItemDelegateForRow(i.key(), i.value());
       
   801     }
       
   802 
       
   803     // set the delegate to be the columnview delegate
       
   804     QAbstractItemDelegate *delegate = column->itemDelegate();
       
   805     column->setItemDelegate(d->itemDelegate);
       
   806     delete delegate;
       
   807 }
       
   808 
       
   809 /*!
       
   810     Returns the preview widget, or 0 if there is none.
       
   811 
       
   812     \sa setPreviewWidget(), updatePreviewWidget()
       
   813 */
       
   814 QWidget *QColumnView::previewWidget() const
       
   815 {
       
   816     Q_D(const QColumnView);
       
   817     return d->previewWidget;
       
   818 }
       
   819 
       
   820 /*!
       
   821     Sets the preview \a widget.
       
   822 
       
   823     The \a widget becomes a child of the column view, and will be
       
   824     destroyed when the column area is deleted or when a new widget is
       
   825     set.
       
   826 
       
   827     \sa previewWidget(), updatePreviewWidget()
       
   828 */
       
   829 void QColumnView::setPreviewWidget(QWidget *widget)
       
   830 {
       
   831     Q_D(QColumnView);
       
   832     d->setPreviewWidget(widget);
       
   833 }
       
   834 
       
   835 /*!
       
   836     \internal
       
   837 */
       
   838 void QColumnViewPrivate::setPreviewWidget(QWidget *widget)
       
   839 {
       
   840     Q_Q(QColumnView);
       
   841     if (previewColumn) {
       
   842         if (!columns.isEmpty() && columns.last() == previewColumn)
       
   843             columns.removeLast();
       
   844         previewColumn->deleteLater();
       
   845     }
       
   846     QColumnViewPreviewColumn *column = new QColumnViewPreviewColumn(q);
       
   847     column->setPreviewWidget(widget);
       
   848     previewColumn = column;
       
   849     previewColumn->hide();
       
   850     previewColumn->setFrameShape(QFrame::NoFrame);
       
   851     previewColumn->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
       
   852     previewColumn->setSelectionMode(QAbstractItemView::NoSelection);
       
   853     previewColumn->setMinimumWidth(qMax(previewColumn->verticalScrollBar()->width(),
       
   854                 previewColumn->minimumWidth()));
       
   855     previewWidget = widget;
       
   856     previewWidget->setParent(previewColumn->viewport());
       
   857 }
       
   858 
       
   859 /*!
       
   860     Sets the column widths to the values given in the \a list.  Extra values in the list are
       
   861     kept and used when the columns are created.
       
   862 
       
   863     If list contains too few values, only width of the rest of the columns will not be modified.
       
   864 
       
   865     \sa columnWidths(), createColumn()
       
   866 */
       
   867 void QColumnView::setColumnWidths(const QList<int> &list)
       
   868 {
       
   869     Q_D(QColumnView);
       
   870     int i = 0;
       
   871     for (; (i < list.count() && i < d->columns.count()); ++i) {
       
   872         d->columns.at(i)->resize(list.at(i), d->columns.at(i)->height());
       
   873         d->columnSizes[i] = list.at(i);
       
   874     }
       
   875     for (; i < list.count(); ++i)
       
   876         d->columnSizes.append(list.at(i));
       
   877 }
       
   878 
       
   879 /*!
       
   880     Returns a list of the width of all the columns in this view.
       
   881 
       
   882     \sa setColumnWidths()
       
   883 */
       
   884 QList<int> QColumnView::columnWidths() const
       
   885 {
       
   886     Q_D(const QColumnView);
       
   887     QList<int> list;
       
   888     for (int i = 0; i < d->columns.count(); ++i)
       
   889         list.append(d->columnSizes.at(i));
       
   890     return list;
       
   891 }
       
   892 
       
   893 /*!
       
   894     \reimp
       
   895 */
       
   896 void QColumnView::rowsInserted(const QModelIndex &parent, int start, int end)
       
   897 {
       
   898     QAbstractItemView::rowsInserted(parent, start, end);
       
   899     d_func()->checkColumnCreation(parent);
       
   900 }
       
   901 
       
   902 /*!
       
   903     \reimp
       
   904 */
       
   905 void QColumnView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
       
   906 {
       
   907     Q_D(QColumnView);
       
   908     if (!current.isValid()) {
       
   909         QAbstractItemView::currentChanged(current, previous);
       
   910         return;
       
   911     }
       
   912 
       
   913     QModelIndex currentParent = current.parent();
       
   914     // optimize for just moving up/down in a list where the child view doesn't change
       
   915     if (currentParent == previous.parent()
       
   916             && model()->hasChildren(current) && model()->hasChildren(previous)) {
       
   917         for (int i = 0; i < d->columns.size(); ++i) {
       
   918             if (currentParent == d->columns.at(i)->rootIndex()) {
       
   919                 if (d->columns.size() > i + 1) {
       
   920                     QAbstractItemView::currentChanged(current, previous);
       
   921                     return;
       
   922                 }
       
   923                 break;
       
   924             }
       
   925         }
       
   926     }
       
   927 
       
   928     // Scrolling to the right we need to have an empty spot
       
   929     bool found = false;
       
   930     if (currentParent == previous) {
       
   931         for (int i = 0; i < d->columns.size(); ++i) {
       
   932             if (currentParent == d->columns.at(i)->rootIndex()) {
       
   933                 found = true;
       
   934                 if (d->columns.size() < i + 2) {
       
   935                     d->createColumn(current, false);
       
   936                 }
       
   937                 break;
       
   938             }
       
   939         }
       
   940     }
       
   941     if (!found)
       
   942         d->closeColumns(current, true);
       
   943 
       
   944     if (!model()->hasChildren(current))
       
   945         emit updatePreviewWidget(current);
       
   946 
       
   947     QAbstractItemView::currentChanged(current, previous);
       
   948 }
       
   949 
       
   950 /*
       
   951     We have change the current column and need to update focus and selection models
       
   952     on the new current column.
       
   953 */
       
   954 void QColumnViewPrivate::_q_changeCurrentColumn()
       
   955 {
       
   956     Q_Q(QColumnView);
       
   957     if (columns.isEmpty())
       
   958         return;
       
   959 
       
   960     QModelIndex current = q->currentIndex();
       
   961     if (!current.isValid())
       
   962         return;
       
   963 
       
   964     // We might have scrolled far to the left so we need to close all of the children
       
   965     closeColumns(current, true);
       
   966 
       
   967     // Set up the "current" column with focus
       
   968     int currentColumn = qMax(0, columns.size() - 2);
       
   969     QAbstractItemView *parentColumn = columns.at(currentColumn);
       
   970     if (q->hasFocus())
       
   971         parentColumn->setFocus(Qt::OtherFocusReason);
       
   972     q->setFocusProxy(parentColumn);
       
   973 
       
   974     // find the column that is our current selection model and give it a new one.
       
   975     for (int i = 0; i < columns.size(); ++i) {
       
   976         if (columns.at(i)->selectionModel() == q->selectionModel()) {
       
   977             QItemSelectionModel *replacementSelectionModel =
       
   978                 new QItemSelectionModel(parentColumn->model());
       
   979             replacementSelectionModel->setCurrentIndex(
       
   980                 q->selectionModel()->currentIndex(), QItemSelectionModel::Current);
       
   981             replacementSelectionModel->select(
       
   982                 q->selectionModel()->selection(), QItemSelectionModel::Select);
       
   983             QAbstractItemView *view = columns.at(i);
       
   984             view->setSelectionModel(replacementSelectionModel);
       
   985             view->setFocusPolicy(Qt::NoFocus);
       
   986             if (columns.size() > i + 1)
       
   987                 view->setCurrentIndex(columns.at(i+1)->rootIndex());
       
   988             break;
       
   989         }
       
   990     }
       
   991     parentColumn->selectionModel()->deleteLater();
       
   992     parentColumn->setFocusPolicy(Qt::StrongFocus);
       
   993     parentColumn->setSelectionModel(q->selectionModel());
       
   994     // We want the parent selection to stay highlighted (but dimmed depending upon the color theme)
       
   995     if (currentColumn > 0) {
       
   996         parentColumn = columns.at(currentColumn - 1);
       
   997         if (parentColumn->currentIndex() != current.parent())
       
   998             parentColumn->setCurrentIndex(current.parent());
       
   999     }
       
  1000 
       
  1001     if (columns.last()->isHidden()) {
       
  1002         columns.last()->setVisible(true);
       
  1003     }
       
  1004     if (columns.last()->selectionModel())
       
  1005         columns.last()->selectionModel()->clear();
       
  1006     updateScrollbars();
       
  1007 }
       
  1008 
       
  1009 /*!
       
  1010     \reimp
       
  1011 */
       
  1012 void QColumnView::selectAll()
       
  1013 {
       
  1014     if (!model() || !selectionModel())
       
  1015         return;
       
  1016 
       
  1017     QModelIndexList indexList = selectionModel()->selectedIndexes();
       
  1018     QModelIndex parent = rootIndex();
       
  1019     QItemSelection selection;
       
  1020     if (indexList.count() >= 1)
       
  1021         parent = indexList.at(0).parent();
       
  1022     if (indexList.count() == 1) {
       
  1023         parent = indexList.at(0);
       
  1024         if (!model()->hasChildren(parent))
       
  1025             parent = parent.parent();
       
  1026         else
       
  1027             selection.append(QItemSelectionRange(parent, parent));
       
  1028     }
       
  1029 
       
  1030     QModelIndex tl = model()->index(0, 0, parent);
       
  1031     QModelIndex br = model()->index(model()->rowCount(parent) - 1,
       
  1032                                     model()->columnCount(parent) - 1,
       
  1033                                     parent);
       
  1034     selection.append(QItemSelectionRange(tl, br));
       
  1035     selectionModel()->select(selection, QItemSelectionModel::ClearAndSelect);
       
  1036 }
       
  1037 
       
  1038 /*
       
  1039  * private object implementation
       
  1040  */
       
  1041 QColumnViewPrivate::QColumnViewPrivate()
       
  1042 :  QAbstractItemViewPrivate()
       
  1043 ,showResizeGrips(true)
       
  1044 ,offset(0)
       
  1045 ,previewWidget(0)
       
  1046 ,previewColumn(0)
       
  1047 {
       
  1048 }
       
  1049 
       
  1050 QColumnViewPrivate::~QColumnViewPrivate()
       
  1051 {
       
  1052 }
       
  1053 
       
  1054 /*!
       
  1055     \internal
       
  1056 
       
  1057   */
       
  1058 void QColumnViewPrivate::_q_columnsInserted(const QModelIndex &parent, int start, int end)
       
  1059 {
       
  1060     QAbstractItemViewPrivate::_q_columnsInserted(parent, start, end);
       
  1061     checkColumnCreation(parent);
       
  1062 }
       
  1063 
       
  1064 /*!
       
  1065     \internal
       
  1066 
       
  1067     Makes sure we create a corresponding column as a result of changing the model.
       
  1068 
       
  1069   */
       
  1070 void QColumnViewPrivate::checkColumnCreation(const QModelIndex &parent)
       
  1071 {
       
  1072     if (parent == q_func()->currentIndex() && model->hasChildren(parent)) {
       
  1073         //the parent has children and is the current
       
  1074         //let's try to find out if there is already a mapping that is good
       
  1075         for (int i = 0; i < columns.count(); ++i) {
       
  1076             QAbstractItemView *view = columns.at(i);
       
  1077             if (view->rootIndex() == parent) {
       
  1078                 if (view == previewColumn) {
       
  1079                     //let's recreate the parent
       
  1080                     closeColumns(parent, false);
       
  1081                     createColumn(parent, true /*show*/);
       
  1082                 }
       
  1083                 break;
       
  1084             }
       
  1085         }
       
  1086     }
       
  1087 }
       
  1088 
       
  1089 /*!
       
  1090     \internal
       
  1091     Place all of the columns where they belong inside of the viewport, resize as necessary.
       
  1092 */
       
  1093 void QColumnViewPrivate::doLayout()
       
  1094 {
       
  1095     Q_Q(QColumnView);
       
  1096     if (!model || columns.isEmpty())
       
  1097         return;
       
  1098 
       
  1099     int viewportHeight = viewport->height();
       
  1100     int x = columns.at(0)->x();
       
  1101 
       
  1102     if (q->isRightToLeft()) {
       
  1103         x = viewport->width() + q->horizontalOffset();
       
  1104         for (int i = 0; i < columns.size(); ++i) {
       
  1105             QAbstractItemView *view = columns.at(i);
       
  1106             x -= view->width();
       
  1107             if (x != view->x() || viewportHeight != view->height())
       
  1108                 view->setGeometry(x, 0, view->width(), viewportHeight);
       
  1109         }
       
  1110     } else {
       
  1111         for (int i = 0; i < columns.size(); ++i) {
       
  1112             QAbstractItemView *view = columns.at(i);
       
  1113             int currentColumnWidth = view->width();
       
  1114             if (x != view->x() || viewportHeight != view->height())
       
  1115                 view->setGeometry(x, 0, currentColumnWidth, viewportHeight);
       
  1116             x += currentColumnWidth;
       
  1117         }
       
  1118     }
       
  1119 }
       
  1120 
       
  1121 /*!
       
  1122     \internal
       
  1123 
       
  1124     Draws a delegate with a > if an object has children.
       
  1125 
       
  1126     \sa {Model/View Programming}, QItemDelegate
       
  1127 */
       
  1128 void QColumnViewDelegate::paint(QPainter *painter,
       
  1129                           const QStyleOptionViewItem &option,
       
  1130                           const QModelIndex &index) const
       
  1131 {
       
  1132     drawBackground(painter, option, index );
       
  1133 
       
  1134     bool reverse = (option.direction == Qt::RightToLeft);
       
  1135     int width = ((option.rect.height() * 2) / 3);
       
  1136     // Modify the options to give us room to add an arrow
       
  1137     QStyleOptionViewItemV4 opt = option;
       
  1138     if (reverse)
       
  1139         opt.rect.adjust(width,0,0,0);
       
  1140     else
       
  1141         opt.rect.adjust(0,0,-width,0);
       
  1142 
       
  1143     if (!(index.model()->flags(index) & Qt::ItemIsEnabled)) {
       
  1144         opt.showDecorationSelected = true;
       
  1145         opt.state |= QStyle::State_Selected;
       
  1146     }
       
  1147 
       
  1148     QItemDelegate::paint(painter, opt, index);
       
  1149 
       
  1150     if (reverse)
       
  1151         opt.rect = QRect(option.rect.x(), option.rect.y(), width, option.rect.height());
       
  1152     else
       
  1153         opt.rect = QRect(option.rect.x() + option.rect.width() - width, option.rect.y(),
       
  1154                          width, option.rect.height());
       
  1155 
       
  1156     // Draw >
       
  1157     if (index.model()->hasChildren(index)) {
       
  1158         const QWidget *view = opt.widget;
       
  1159         QStyle *style = view ? view->style() : QApplication::style();
       
  1160         style->drawPrimitive(QStyle::PE_IndicatorColumnViewArrow, &opt, painter, view);
       
  1161     }
       
  1162 }
       
  1163 
       
  1164 QT_END_NAMESPACE
       
  1165 
       
  1166 #include "moc_qcolumnview.cpp"
       
  1167 
       
  1168 #endif // QT_NO_COLUMNVIEW