src/gui/itemviews/qtreeview.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 **
00003 ** Copyright (C) 1992-2006 Trolltech ASA. All rights reserved.
00004 **
00005 ** This file is part of the QtGui module of the Qt Toolkit.
00006 **
00007 ** This file may be used under the terms of the GNU General Public
00008 ** License version 2.0 as published by the Free Software Foundation
00009 ** and appearing in the file LICENSE.GPL included in the packaging of
00010 ** this file.  Please review the following information to ensure GNU
00011 ** General Public Licensing requirements will be met:
00012 ** http://www.trolltech.com/products/qt/opensource.html
00013 **
00014 ** If you are unsure which license is appropriate for your use, please
00015 ** review the following information:
00016 ** http://www.trolltech.com/products/qt/licensing.html or contact the
00017 ** sales department at sales@trolltech.com.
00018 **
00019 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00020 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00021 **
00022 ****************************************************************************/
00023 #include "qtreeview.h"
00024 
00025 #ifndef QT_NO_TREEVIEW
00026 #include <qheaderview.h>
00027 #include <qitemdelegate.h>
00028 #include <qapplication.h>
00029 #include <qscrollbar.h>
00030 #include <qpainter.h>
00031 #include <qstack.h>
00032 #include <qstyle.h>
00033 #include <qstyleoption.h>
00034 #include <qevent.h>
00035 #include <qpen.h>
00036 #include <qdebug.h>
00037 
00038 #include <private/qtreeview_p.h>
00039 
00156 QTreeView::QTreeView(QWidget *parent)
00157     : QAbstractItemView(*new QTreeViewPrivate, parent)
00158 {
00159     Q_D(QTreeView);
00160     d->initialize();
00161 }
00162 
00166 QTreeView::QTreeView(QTreeViewPrivate &dd, QWidget *parent)
00167     : QAbstractItemView(dd, parent)
00168 {
00169     Q_D(QTreeView);
00170     d->initialize();
00171 }
00172 
00176 QTreeView::~QTreeView()
00177 {
00178 }
00179 
00183 void QTreeView::setModel(QAbstractItemModel *model)
00184 {
00185     Q_D(QTreeView);
00186     if (d->selectionModel) { // support row editing
00187         disconnect(d->selectionModel, SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
00188                    d->model, SLOT(submit()));
00189         disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
00190                    this, SLOT(rowsRemoved(QModelIndex,int,int)));
00191     }
00192     d->viewItems.clear();
00193     d->expandedIndexes.clear();
00194     d->hiddenIndexes.clear();
00195     d->header->setModel(model);
00196     QAbstractItemView::setModel(model);
00197 
00198     // QAbstractItemView connects to a private slot
00199     disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
00200                this, SLOT(_q_rowsRemoved(QModelIndex,int,int)));
00201     // do header layout after the tree
00202     disconnect(d->model, SIGNAL(layoutChanged()),
00203                d->header, SLOT(doItemsLayout()));
00204     // QTreeView has a public slot for this
00205     connect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
00206             this, SLOT(rowsRemoved(QModelIndex,int,int)));
00207 
00208     if (d->sortingEnabled)
00209         sortByColumn(header()->sortIndicatorSection());
00210 }
00211 
00215 void QTreeView::setRootIndex(const QModelIndex &index)
00216 {
00217     Q_D(QTreeView);
00218     d->header->setRootIndex(index);
00219     QAbstractItemView::setRootIndex(index);
00220 }
00221 
00225 void QTreeView::setSelectionModel(QItemSelectionModel *selectionModel)
00226 {
00227     Q_D(QTreeView);
00228     Q_ASSERT(selectionModel);
00229     if (d->selectionModel) {
00230         if (d->allColumnsShowFocus) {
00231             QObject::disconnect(d->selectionModel, SIGNAL(currentChanged(QModelIndex,QModelIndex)),
00232                                 this, SLOT(_q_currentChanged(QModelIndex,QModelIndex)));
00233         }
00234         // support row editing
00235         disconnect(d->selectionModel, SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
00236                    d->model, SLOT(submit()));
00237     }
00238 
00239     d->header->setSelectionModel(selectionModel);
00240     QAbstractItemView::setSelectionModel(selectionModel);
00241 
00242     if (d->selectionModel) {
00243         if (d->allColumnsShowFocus) {
00244             QObject::connect(d->selectionModel, SIGNAL(currentChanged(QModelIndex,QModelIndex)),
00245                              this, SLOT(_q_currentChanged(QModelIndex,QModelIndex)));
00246         }
00247         // support row editing
00248         connect(d->selectionModel, SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
00249                 d->model, SLOT(submit()));
00250     }
00251 }
00252 
00258 QHeaderView *QTreeView::header() const
00259 {
00260     Q_D(const QTreeView);
00261     return d->header;
00262 }
00263 
00272 void QTreeView::setHeader(QHeaderView *header)
00273 {
00274     Q_D(QTreeView);
00275     if (header == d->header || !header)
00276         return;
00277     if (d->header && d->header->parent() == this)
00278         delete d->header;
00279     d->header = header;
00280     d->header->setParent(this);
00281 
00282     if (!d->header->model())
00283         d->header->setModel(d->model);
00284 
00285     connect(d->header, SIGNAL(sectionResized(int,int,int)),
00286             this, SLOT(columnResized(int,int,int)));
00287     connect(d->header, SIGNAL(sectionMoved(int,int,int)),
00288             this, SLOT(columnMoved()));
00289     connect(d->header, SIGNAL(sectionCountChanged(int,int)),
00290             this, SLOT(columnCountChanged(int,int)));
00291     connect(d->header, SIGNAL(sectionHandleDoubleClicked(int)),
00292             this, SLOT(resizeColumnToContents(int)));
00293     connect(d->header, SIGNAL(geometriesChanged()),
00294             this, SLOT(updateGeometries()));
00295     d->header->setFocusProxy(this);
00296 
00297     setSortingEnabled(d->sortingEnabled);
00298 }
00299 
00309 int QTreeView::indentation() const
00310 {
00311     Q_D(const QTreeView);
00312     return d->indent;
00313 }
00314 
00315 void QTreeView::setIndentation(int i)
00316 {
00317     Q_D(QTreeView);
00318     if (i != d->indent) {
00319         d->indent = i;
00320         d->viewport->update();
00321     }
00322 }
00323 
00335 bool QTreeView::rootIsDecorated() const
00336 {
00337     Q_D(const QTreeView);
00338     return d->rootDecoration;
00339 }
00340 
00341 void QTreeView::setRootIsDecorated(bool show)
00342 {
00343     Q_D(QTreeView);
00344     if (show != d->rootDecoration) {
00345         d->rootDecoration = show;
00346         d->viewport->update();
00347     }
00348 }
00349 
00358 bool QTreeView::uniformRowHeights() const
00359 {
00360     Q_D(const QTreeView);
00361     return d->uniformRowHeights;
00362 }
00363 
00364 void QTreeView::setUniformRowHeights(bool uniform)
00365 {
00366     Q_D(QTreeView);
00367     d->uniformRowHeights = uniform;
00368 }
00369 
00378 bool QTreeView::itemsExpandable() const
00379 {
00380     Q_D(const QTreeView);
00381     return d->itemsExpandable;
00382 }
00383 
00384 void QTreeView::setItemsExpandable(bool enable)
00385 {
00386     Q_D(QTreeView);
00387     d->itemsExpandable = enable;
00388 }
00389 
00393 int QTreeView::columnViewportPosition(int column) const
00394 {
00395     Q_D(const QTreeView);
00396     return d->header->sectionViewportPosition(column);
00397 }
00398 
00404 int QTreeView::columnWidth(int column) const
00405 {
00406     Q_D(const QTreeView);
00407     return d->header->sectionSize(column);
00408 }
00409 
00417 void QTreeView::setColumnWidth(int column, int width)
00418 {
00419     Q_D(QTreeView);
00420     d->header->resizeSection(column, width);
00421 }
00422 
00427 int QTreeView::columnAt(int x) const
00428 {
00429     Q_D(const QTreeView);
00430     return d->header->logicalIndexAt(x);
00431 }
00432 
00438 bool QTreeView::isColumnHidden(int column) const
00439 {
00440     Q_D(const QTreeView);
00441     return d->header->isSectionHidden(column);
00442 }
00443 
00449 void QTreeView::setColumnHidden(int column, bool hide)
00450 {
00451     Q_D(QTreeView);
00452     if (column < 0 || column >= d->header->count())
00453         return;
00454     d->header->setSectionHidden(column, hide);
00455 }
00456 
00463 bool QTreeView::isRowHidden(int row, const QModelIndex &parent) const
00464 {
00465     Q_D(const QTreeView);
00466     if (d->hiddenIndexes.isEmpty() || !d->model)
00467         return false;
00468     QModelIndex index = d->model->index(row, 0, parent);
00469     for (int i = 0; i < d->hiddenIndexes.count(); ++i)
00470         if (d->hiddenIndexes.at(i) == index)
00471             return true;
00472     return false;
00473 }
00474 
00480 void QTreeView::setRowHidden(int row, const QModelIndex &parent, bool hide)
00481 {
00482     Q_D(QTreeView);
00483     if (!d->model)
00484         return;
00485     QModelIndex index = d->model->index(row, 0, parent);
00486     if (!index.isValid())
00487         return;
00488 
00489     if (hide) {
00490         QPersistentModelIndex persistent(index);
00491         if (!d->hiddenIndexes.contains(persistent)) d->hiddenIndexes.append(persistent);
00492     } else {
00493         QPersistentModelIndex persistent(index);
00494         int i = d->hiddenIndexes.indexOf(persistent);
00495         if (i >= 0) d->hiddenIndexes.remove(i);
00496     }
00497 
00498     if (hide && isVisible()) {
00499         int p = d->viewIndex(parent);
00500         if (p >= 0) {
00501             const int first = p + 1;
00502             const int last = first + d->viewItems.at(p).total - 1;
00503             for (int i = first; i <= last; ) {
00504                 const int count = d->viewItems.at(i).total + 1;
00505                 if (d->viewItems.at(i).index == index) {
00506                     // remove child and its children
00507                     d->viewItems.remove(i, count);
00508                     // update children count of ancestors 
00509                     int level = d->viewItems.at(p).level;
00510                     do {
00511                         for ( ; int(d->viewItems.at(p).level) != level; --p) ;
00512                         d->viewItems[p].total -= count;
00513                         --level;
00514                     } while (level >= 0);
00515                     break;
00516                 } else {
00517                     i += count;
00518                 }
00519             }
00520             updateGeometries();
00521             d->viewport->update();
00522         }
00523         else
00524             d->doDelayedItemsLayout();
00525     } else {
00526         d->doDelayedItemsLayout();
00527     }
00528 }
00529 
00533 void QTreeView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
00534 {
00535     Q_D(QTreeView);
00536 
00537     // if we are going to do a complete realyout anyway, there is no need to update
00538     if (d->delayedLayout.isActive())
00539         return;
00540 
00541     // refresh the height cache here; we don't really lose anything by getting the size hint,
00542     // since QAbstractItemView::dataChanged() will get the visualRect for the items anyway
00543 
00544     QModelIndex top = (topLeft.column() == 0) ? topLeft
00545                       : d->model->sibling(topLeft.row(), 0, topLeft);
00546     int topViewIndex = d->viewIndex(top);
00547     bool sizeChanged = false;
00548     if (topViewIndex != -1) {
00549         if (topLeft == bottomRight) {
00550             int oldHeight = d->itemHeight(topViewIndex);
00551             d->invalidateHeightCache(topViewIndex);
00552             sizeChanged = (oldHeight != d->itemHeight(topViewIndex));
00553         } else {
00554             QModelIndex bottom = (bottomRight.column() == 0) ? bottomRight
00555                                  : d->model->sibling(bottomRight.row(), 0, bottomRight);
00556             int bottomViewIndex = d->viewIndex(bottom);
00557             for (int i = topViewIndex; i <= bottomViewIndex; ++i) {
00558                 int oldHeight = d->itemHeight(i);
00559                 d->invalidateHeightCache(i);
00560                 sizeChanged |= (oldHeight != d->itemHeight(i));
00561             }
00562         }
00563     }
00564 
00565     if (sizeChanged) {
00566         d->updateScrollBars();
00567         d->viewport->update();
00568     }
00569     QAbstractItemView::dataChanged(topLeft, bottomRight);
00570 }
00571 
00577 void QTreeView::hideColumn(int column)
00578 {
00579     Q_D(QTreeView);
00580     d->header->hideSection(column);
00581 }
00582 
00588 void QTreeView::showColumn(int column)
00589 {
00590     Q_D(QTreeView);
00591     d->header->showSection(column);
00592 }
00593 
00601 void QTreeView::expand(const QModelIndex &index)
00602 {
00603     Q_D(QTreeView);
00604     if (!d->isIndexValid(index))
00605         return;
00606     int i = d->viewIndex(index);
00607     if (i != -1) { // is visible
00608         d->expand(i, true);
00609         if (!d->isAnimating()) {
00610             updateGeometries();
00611             d->viewport->update();
00612         }
00613     } else if (!d->expandedIndexes.contains(index)) {
00614         d->expandedIndexes.append(index);
00615         emit expanded(index);
00616     }
00617 }
00618 
00626 void QTreeView::collapse(const QModelIndex &index)
00627 {
00628     Q_D(QTreeView);
00629     if (!d->isIndexValid(index))
00630         return;
00631     int i = d->viewIndex(index);
00632     if (i != -1) { // is visible
00633         d->collapse(i, true);
00634         if (!d->isAnimating()) {
00635             updateGeometries();
00636             viewport()->update();
00637         }
00638     } else {
00639         int i = d->expandedIndexes.indexOf(index);
00640         if (i != -1) {
00641             d->expandedIndexes.remove(i);
00642             emit collapsed(index);
00643         }
00644     }
00645 }
00646 
00655 bool QTreeView::isExpanded(const QModelIndex &index) const
00656 {
00657     Q_D(const QTreeView);
00658     int i = d->viewIndex(index);
00659     if (i != -1) // is visible
00660         return d->viewItems.at(i).expanded;
00661     return d->expandedIndexes.contains(index);
00662 }
00663 
00670 void QTreeView::setExpanded(const QModelIndex &index, bool expanded)
00671 {
00672     if (expanded)
00673         this->expand(index);
00674     else
00675         this->collapse(index);
00676 }
00677 
00689 void QTreeView::setSortingEnabled(bool enable)
00690 {
00691     Q_D(QTreeView);
00692     d->sortingEnabled = enable;
00693     header()->setSortIndicatorShown(enable);
00694     header()->setClickable(enable);
00695     if (enable) {
00696         connect(header(), SIGNAL(sectionClicked(int)), this, SLOT(sortByColumn(int)));
00697         sortByColumn(header()->sortIndicatorSection());
00698     } else {
00699         disconnect(header(), SIGNAL(sectionClicked(int)), this, SLOT(sortByColumn(int)));
00700     }
00701 }
00702 
00703 bool QTreeView::isSortingEnabled() const
00704 {
00705     Q_D(const QTreeView);
00706     return d->sortingEnabled;
00707 }
00708 
00720 void QTreeView::setAnimated(bool animate)
00721 {
00722     Q_D(QTreeView);
00723     d->animationsEnabled = animate;
00724 }
00725 
00726 bool QTreeView::isAnimated() const
00727 {
00728     Q_D(const QTreeView);
00729     return d->animationsEnabled;
00730 }
00731 
00743 void QTreeView::setAllColumnsShowFocus(bool enable)
00744 {
00745     Q_D(QTreeView);
00746     if (d->allColumnsShowFocus == enable)
00747         return;
00748     if (d->selectionModel) {
00749         if (enable) {
00750             QObject::connect(d->selectionModel, SIGNAL(currentChanged(QModelIndex,QModelIndex)),
00751                              this, SLOT(_q_currentChanged(QModelIndex,QModelIndex)));
00752         } else {
00753             QObject::disconnect(d->selectionModel, SIGNAL(currentChanged(QModelIndex,QModelIndex)),
00754                                 this, SLOT(_q_currentChanged(QModelIndex,QModelIndex)));
00755         }
00756     }
00757     d->allColumnsShowFocus = enable;
00758     d->viewport->update();
00759 }
00760 
00761 bool QTreeView::allColumnsShowFocus() const
00762 {
00763     Q_D(const QTreeView);
00764     return d->allColumnsShowFocus;
00765 }
00766 
00770 void QTreeView::keyboardSearch(const QString &search)
00771 {
00772     Q_D(QTreeView);
00773     if (!d->model->rowCount(d->root) || !d->model->columnCount(d->root))
00774         return;
00775 
00776     QModelIndex start;
00777     if (currentIndex().isValid())
00778         start = currentIndex();
00779     else
00780         start = d->model->index(0, 0, d->root);
00781 
00782     QTime now(QTime::currentTime());
00783     bool skipRow = false;
00784     if (search.isEmpty()
00785         || (d->keyboardInputTime.msecsTo(now) > QApplication::keyboardInputInterval())) {
00786         d->keyboardInput = search;
00787         skipRow = true;
00788     } else {
00789         d->keyboardInput += search;
00790     }
00791     d->keyboardInputTime = now;
00792 
00793     // special case for searches with same key like 'aaaaa'
00794     bool sameKey = false;
00795     if (d->keyboardInput.length() > 1) {
00796         int c = d->keyboardInput.count(d->keyboardInput.at(d->keyboardInput.length() - 1));
00797         sameKey = (c == d->keyboardInput.length());
00798         if (sameKey)
00799             skipRow = true;
00800     }
00801 
00802     // skip if we are searching for the same key or a new search started
00803     if (skipRow) {
00804         if (indexBelow(start).isValid())
00805             start = indexBelow(start);
00806         else
00807             start = d->model->index(0, start.column(), d->root);
00808     }
00809 
00810     int startIndex = d->viewIndex(start);
00811     if (startIndex <= -1)
00812         return;
00813 
00814     int previousLevel = -1;
00815     int bestAbove = -1;
00816     int bestBelow = -1;
00817     QString searchString = sameKey ? QString(d->keyboardInput.at(0)) : d->keyboardInput;
00818     for (int i = 0; i < d->viewItems.count(); ++i) {
00819         if ((int)d->viewItems.at(i).level > previousLevel) {
00820             QModelIndex searchFrom = d->viewItems.at(i).index;
00821             if (searchFrom.parent() == start.parent())
00822                 searchFrom = start;
00823             QModelIndexList match = d->model->match(searchFrom, Qt::DisplayRole, searchString);
00824             if (match.count()) {
00825                 int hitIndex = d->viewIndex(match.at(0));
00826                 if (hitIndex >= 0 && hitIndex < startIndex)
00827                     bestAbove = bestAbove == -1 ? hitIndex : qMin(hitIndex, bestAbove);
00828                 else if (hitIndex >= startIndex)
00829                     bestBelow = bestBelow == -1 ? hitIndex : qMin(hitIndex, bestBelow);
00830             }
00831         }
00832         previousLevel = d->viewItems.at(i).level;
00833     }
00834 
00835     QModelIndex index;
00836     if (bestBelow > -1)
00837         index = d->viewItems.at(bestBelow).index;
00838     else if (bestAbove > -1)
00839         index = d->viewItems.at(bestAbove).index;
00840 
00841     if (index.isValid()) {
00842         QItemSelectionModel::SelectionFlags flags = (d->selectionMode == SingleSelection
00843                                                      ? QItemSelectionModel::SelectionFlags(
00844                                                          QItemSelectionModel::ClearAndSelect
00845                                                          |d->selectionBehaviorFlags())
00846                                                      : QItemSelectionModel::SelectionFlags(
00847                                                          QItemSelectionModel::NoUpdate));
00848         selectionModel()->setCurrentIndex(index, flags);
00849     }
00850 }
00851 
00856 QRect QTreeView::visualRect(const QModelIndex &index) const
00857 {
00858     Q_D(const QTreeView);
00859 
00860     if (!d->isIndexValid(index) || isIndexHidden(index))
00861         return QRect();
00862 
00863     d->executePostedLayout();
00864 
00865     int vi = d->viewIndex(index);
00866     if (vi < 0)
00867         return QRect();
00868 
00869     int x = columnViewportPosition(index.column());
00870     int w = columnWidth(index.column());
00871 
00872     if (index.column() == 0) {
00873         int i = d->indentationForItem(vi);
00874         x += i;
00875         w -= i;
00876     }
00877     int y = d->coordinateForItem(vi);
00878     int h = d->itemHeight(vi);
00879     return QRect(x, y, w, h);
00880 }
00881 
00890 void QTreeView::scrollTo(const QModelIndex &index, ScrollHint hint)
00891 {
00892     Q_D(QTreeView);
00893 
00894     if (!d->isIndexValid(index))
00895         return;
00896 
00897     d->executePostedLayout();
00898     d->updateScrollBars();
00899 
00900     // Expand all parents if the parent(s) of the node are not expanded.
00901     QModelIndex parent = index.parent();
00902     while (parent.isValid() && state() == NoState && d->itemsExpandable) {
00903         if (!isExpanded(parent))
00904             expand(parent);
00905         parent = d->model->parent(parent);
00906     }
00907 
00908     int item = d->viewIndex(index);
00909     if (item < 0)
00910         return;
00911     QRect rect(columnViewportPosition(index.column()),
00912                d->coordinateForItem(item),
00913                columnWidth(index.column()),
00914                d->itemHeight(item));
00915 
00916     if (rect.isEmpty())
00917         return;
00918 
00919     // check if we really need to do anything
00920     QRect area = d->viewport->rect();
00921     if (hint == EnsureVisible && area.contains(rect)) {
00922         d->setDirtyRegion(rect);
00923         return;
00924     }
00925 
00926     // vertical
00927     bool above = (hint == EnsureVisible && (rect.top() < area.top() || area.height() < rect.height()));
00928     bool below = (hint == EnsureVisible && rect.bottom() > area.bottom() && rect.height() < area.height());
00929     if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) {
00930         if (hint == PositionAtTop || above) {
00931             verticalScrollBar()->setValue(item);
00932         } else if (hint == PositionAtCenter || hint == PositionAtBottom || below) {
00933             int y = area.height();
00934             if (hint == PositionAtCenter)
00935                 y = y / 2;
00936             while (y > 0 && item > 0)
00937                 y -= d->itemHeight(item--);
00938             item += 1 + ((y < 0) ? 1 : 0);
00939             verticalScrollBar()->setValue(item);
00940         }
00941     } else { // ScrollPerPixel
00942         int verticalValue = verticalScrollBar()->value();
00943         if (hint == PositionAtTop || above)
00944             verticalValue += rect.top();
00945         else if (hint == PositionAtBottom || below)
00946             verticalValue += rect.bottom() - area.height();
00947         else if (hint == PositionAtCenter)
00948             verticalValue += rect.top() - ((area.height() - rect.height()) / 2);
00949         verticalScrollBar()->setValue(verticalValue);
00950     }
00951 
00952     // horizontal
00953     int viewportWidth = d->viewport->width();
00954     int horizontalOffset = d->header->offset();
00955     int horizontalPosition = d->header->sectionPosition(index.column());
00956     int cellWidth = d->header->sectionSize(index.column());
00957 
00958     if (hint == PositionAtCenter) {
00959         horizontalScrollBar()->setValue(horizontalPosition - ((viewportWidth - cellWidth) / 2));
00960     } else {
00961         if (horizontalPosition - horizontalOffset < 0 || cellWidth > viewportWidth)
00962             horizontalScrollBar()->setValue(horizontalPosition);
00963         else if (horizontalPosition - horizontalOffset + cellWidth > viewportWidth)
00964             horizontalScrollBar()->setValue(horizontalPosition - viewportWidth + cellWidth);
00965     }
00966 }
00967 
00971 void QTreeView::timerEvent(QTimerEvent *event)
00972 {
00973     Q_D(QTreeView);
00974     if (event->timerId() == d->columnResizeTimerID) {
00975         updateGeometries();
00976         killTimer(d->columnResizeTimerID);
00977         d->columnResizeTimerID = 0;
00978         QRect rect;
00979         int viewportHeight = d->viewport->height();
00980         int viewportWidth = d->viewport->width();
00981         for (int i = d->columnsToUpdate.size() - 1; i >= 0; --i) {
00982             int column = d->columnsToUpdate.at(i);
00983             int x = columnViewportPosition(column);
00984             if (isRightToLeft())
00985                 rect |= QRect(0, 0, x + columnWidth(column), viewportHeight);
00986             else
00987                 rect |= QRect(x, 0, viewportWidth - x, viewportHeight);
00988         }
00989         d->viewport->update(rect.normalized());
00990         d->columnsToUpdate.clear();
00991     }
00992     QAbstractItemView::timerEvent(event);
00993 }
00994 
00998 void QTreeView::paintEvent(QPaintEvent *event)
00999 {
01000     Q_D(QTreeView);
01001     bool layout = d->delayedLayout.isActive();
01002     d->delayedLayout.stop();
01003     QPainter painter(viewport());
01004     if (d->isAnimating()) {
01005         drawTree(&painter, event->region() - d->animationRect());
01006         d->drawAnimatedOperation(&painter);
01007     } else {
01008         drawTree(&painter, event->region());
01009 #ifndef QT_NO_DRAGANDDROP
01010         d->paintDropIndicator(&painter);
01011 #endif
01012     }
01013     if (layout)
01014         d->doDelayedItemsLayout();
01015 }
01016 
01024 void QTreeView::drawTree(QPainter *painter, const QRegion &region) const
01025 {
01026     Q_D(const QTreeView);
01027     const QVector<QTreeViewItem> viewItems = d->viewItems;
01028 
01029     if (viewItems.count() == 0 || d->header->count() == 0 || !d->itemDelegate) {
01030         painter->fillRect(region.boundingRect(), palette().brush(QPalette::Base));
01031         return;
01032     }
01033 
01034     QStyleOptionViewItemV2 option = d->viewOptionsV2();
01035     const QStyle::State state = option.state;
01036     const int deviceWidth = painter->device()->width();
01037     const int headerLength = d->header->length();
01038 
01039     int firstVisibleItemOffset = 0;
01040     const int firstVisibleItem = d->firstVisibleItem(&firstVisibleItemOffset);
01041     if (firstVisibleItem < 0)
01042         return;
01043 
01044     QVector<QRect> rects = region.rects();
01045     for (int a = 0; a < rects.size(); ++a) {
01046 
01047         const QRect area = rects.at(a);
01048         d->leftAndRight = d->startAndEndColumns(area);
01049 
01050         int i = firstVisibleItem; // the first item at the top of the viewport
01051         int y = firstVisibleItemOffset; // we may only see part of the first item
01052 
01053         // start at the top of the viewport  and iterate down to the update area
01054         for (; i < viewItems.count(); ++i) {
01055             const int itemHeight = d->itemHeight(i);
01056             if (y + itemHeight >= area.top())
01057                 break;
01058             y += itemHeight;
01059         }
01060 
01061         // paint the visible rows
01062         for (; i < viewItems.count() && y <= area.bottom(); ++i) {
01063             const int itemHeight = d->itemHeight(i);
01064             option.rect.setRect(0, y, 0, itemHeight);
01065             option.state = state | (viewItems.at(i).expanded
01066                                     ? QStyle::State_Open : QStyle::State_None);
01067             d->current = i;
01068             drawRow(painter, option, viewItems.at(i).index);
01069             y += itemHeight;
01070         }
01071 
01072         if (y <= area.bottom()) {
01073             QRect bottomArea(0, y, deviceWidth, area.bottom() - y + 1);
01074             if (area.intersects(bottomArea))
01075                 painter->fillRect(bottomArea, palette().brush(QPalette::Base));
01076         }
01077         if (isRightToLeft()) {
01078             QRect rightArea(0, 0, deviceWidth - headerLength, area.height());
01079             if (headerLength < deviceWidth && area.intersects(rightArea))
01080                 painter->fillRect(rightArea, palette().brush(QPalette::Base));
01081         } else {
01082             QRect leftArea(headerLength, 0, deviceWidth - headerLength, area.height());
01083             if (headerLength < deviceWidth && area.intersects(leftArea))
01084                 painter->fillRect(leftArea, palette().brush(QPalette::Base));
01085         }
01086     }
01087 }
01088 
01090 static inline bool ancestorOf(QObject *widget, QObject *other)
01091 {
01092     for (QObject *parent = other; parent != 0; parent = parent->parent()) {
01093         if (parent == widget)
01094             return true;
01095     }
01096     return false;
01097 }
01098 
01106 void QTreeView::drawRow(QPainter *painter, const QStyleOptionViewItem &option,
01107                         const QModelIndex &index) const
01108 {
01109     Q_D(const QTreeView);
01110     QStyleOptionViewItemV2 opt = option;
01111     const QPoint offset = d->scrollDelayOffset;
01112     const int y = option.rect.y() + offset.y();
01113     const QModelIndex parent = index.parent();
01114     const QHeaderView *header = d->header;
01115     const QModelIndex current = currentIndex();
01116     const QModelIndex hover = d->hover;
01117     const bool reverse = isRightToLeft();
01118     const QStyle::State state = opt.state;
01119     const int left = d->leftAndRight.first;
01120     const int right = d->leftAndRight.second;
01121     const bool alternate = d->alternatingColors;
01122     const bool enabled = (state & QStyle::State_Enabled) != 0;
01123     const bool allColumnsShowFocus = d->allColumnsShowFocus;
01124 
01125     // when the row contains an index widget which has focus,
01126     // we want to paint the entire row as active
01127     bool indexWidgetHasFocus = false;
01128     if ((current.row() == index.row()) && !d->editors.isEmpty()) {
01129         const int r = index.row();
01130         QWidget *fw = QApplication::focusWidget();
01131         for (int c = 0; c < header->count(); ++c) {
01132             if (QWidget *editor = indexWidget(index.sibling(r, c))) {
01133                 if (ancestorOf(editor, fw)) {
01134                     indexWidgetHasFocus = true;
01135                     break;
01136                 }
01137             }
01138         }
01139     }
01140 
01141     bool currentRowHasFocus = false;
01142     if (allColumnsShowFocus && current.isValid()) { // check if the focus index is before or after the visible columns
01143         const int r = index.row();
01144         for (int c = 0; c < left && !currentRowHasFocus; ++c)
01145             currentRowHasFocus = (index.sibling(r, c) == current);
01146         for (int c = right; c < header->count() && !currentRowHasFocus; ++c)
01147             currentRowHasFocus = (index.sibling(r, c) == current);
01148     }
01149 
01150     // ### special case: treeviews with multiple columns draw
01151     // the selections differently than with only one column
01152     opt.showDecorationSelected = (d->selectionBehavior & SelectRows)
01153                                  || option.showDecorationSelected;
01154 
01155     int width, height = option.rect.height();
01156     int position;
01157     int headerSection;
01158     QModelIndex modelIndex;
01159 
01160     QBrush fill;
01161     for (int headerIndex = left; headerIndex <= right; ++headerIndex) {
01162         headerSection = header->logicalIndex(headerIndex);
01163         if (header->isSectionHidden(headerSection))
01164             continue;
01165         position = columnViewportPosition(headerSection) + offset.x();
01166         width = header->sectionSize(headerSection);
01167         modelIndex = d->model->index(index.row(), headerSection, parent);
01168         opt.state = state;
01169         if (!modelIndex.isValid()) {
01170             opt.rect.setRect(position, y, width, height);
01171             painter->fillRect(opt.rect, palette().brush(QPalette::Base));
01172             continue;
01173         }
01174 
01175         // fake activeness when row editor has focus
01176         if (indexWidgetHasFocus)
01177             opt.state |= QStyle::State_Active;
01178 
01179         if (d->selectionModel->isSelected(modelIndex))
01180             opt.state |= QStyle::State_Selected;
01181         if ((current == modelIndex) && hasFocus()) {
01182             if (allColumnsShowFocus)
01183                 currentRowHasFocus = true;
01184             else
01185                 opt.state |= QStyle::State_HasFocus;
01186         }
01187         if (modelIndex == hover)
01188             opt.state |= QStyle::State_MouseOver;
01189         else
01190             opt.state &= ~QStyle::State_MouseOver;
01191 
01192         if (enabled) {
01193             QPalette::ColorGroup cg;
01194             if ((d->model->flags(index) & Qt::ItemIsEnabled) == 0) {
01195                 opt.state &= ~QStyle::State_Enabled;
01196                 cg = QPalette::Disabled;
01197             } else {
01198                 cg = QPalette::Active;
01199             }
01200             opt.palette.setCurrentColorGroup(cg);
01201         }
01202 
01203         if (alternate) {
01204             if (d->current & 1) {
01205                 opt.features |= QStyleOptionViewItemV2::Alternate;
01206                 fill = opt.palette.brush(QPalette::AlternateBase);
01207             } else {
01208                 opt.features &= ~QStyleOptionViewItemV2::Alternate;
01209                 fill = opt.palette.brush(QPalette::Base);
01210             }
01211         } else {
01212             fill = opt.palette.brush(QPalette::Base);
01213         }
01214 
01215         if (headerSection == 0) {
01216             const int i = d->indentationForItem(d->current);
01217             opt.rect.setRect(reverse ? position : i + position, y, width - i, height);
01218             painter->fillRect(opt.rect, fill);
01219             QRect branches(reverse ? position + width - i : position, y, i, height);
01220             QPalette::ColorGroup cg = opt.state & QStyle::State_Enabled
01221                               ? QPalette::Active : QPalette::Disabled;
01222             if (cg == QPalette::Active && !(opt.state & QStyle::State_Active))
01223                 cg = QPalette::Inactive;
01224 
01225             if ((opt.state & QStyle::State_Selected) && option.showDecorationSelected)
01226                 painter->fillRect(branches, opt.palette.brush(cg, QPalette::Highlight));
01227             else
01228                 painter->fillRect(branches, fill);
01229             drawBranches(painter, branches, index);
01230         } else {
01231             opt.rect.setRect(position, y, width, height);
01232             painter->fillRect(opt.rect, fill);
01233         }
01234         itemDelegate()->paint(painter, opt, modelIndex);
01235     }
01236 
01237     if (currentRowHasFocus) {
01238         const int x = (option.showDecorationSelected ? 0 : d->indentationForItem(d->current));
01239         const int width = header->length() - x;
01240         QStyleOptionFocusRect o;
01241         o.QStyleOption::operator=(option);
01242         o.rect.setRect(x - header->offset(), y, width, height);
01243         o.state |= QStyle::State_KeyboardFocusChange;
01244         QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled)
01245                                   ? QPalette::Normal : QPalette::Disabled;
01246         o.backgroundColor = option.palette.color(cg, d->selectionModel->isSelected(index)
01247                                                  ? QPalette::Highlight : QPalette::Background);
01248         style()->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter);
01249     }
01250 }
01251 
01257 void QTreeView::drawBranches(QPainter *painter, const QRect &rect,
01258                              const QModelIndex &index) const
01259 {
01260     Q_D(const QTreeView);
01261     const bool reverse = isRightToLeft();
01262     const int indent = d->indent;
01263     const int outer = d->rootDecoration ? 0 : 1;
01264     const int item = d->current;
01265     const QTreeViewItem &viewItem = d->viewItems.at(item);
01266     int level = viewItem.level;
01267     QRect primitive(reverse ? rect.left() : rect.right(), rect.top(), indent, rect.height());
01268 
01269     QModelIndex parent = index.parent();
01270     QModelIndex current = parent;
01271     QModelIndex ancestor = current.parent();
01272 
01273     QStyleOption opt;
01274     opt.initFrom(this);
01275     QStyle::State extraFlags = QStyle::State_None;
01276     if (isEnabled())
01277         extraFlags |= QStyle::State_Enabled;
01278     if (window()->isActiveWindow())
01279         extraFlags |= QStyle::State_Active;
01280 
01281     QPoint oldBO = painter->brushOrigin();
01282     if (verticalScrollMode() == QAbstractItemView::ScrollPerPixel)
01283         painter->setBrushOrigin(QPoint(0, verticalOffset()));
01284 
01285     if (level >= outer) {
01286         // start with the innermost branch
01287         primitive.moveLeft(reverse ? primitive.left() : primitive.left() - indent);
01288         opt.rect = primitive;
01289 
01290         const bool expanded = viewItem.expanded;
01291         const bool children = (((expanded && viewItem.total > 0)) // already laid out and has children
01292                                 || d->hasVisibleChildren(index)); // not laid out yet, so we don't know
01293         bool moreSiblings = false;
01294         if (d->hiddenIndexes.isEmpty())
01295             moreSiblings = (d->model->rowCount(parent) - 1 > index.row());
01296         else
01297             moreSiblings = ((d->viewItems.size() > item +1)
01298                             && (d->viewItems.at(item + 1).index.parent() == parent));
01299 
01300         opt.state = QStyle::State_Item | extraFlags
01301                     | (moreSiblings ? QStyle::State_Sibling : QStyle::State_None)
01302                     | (children ? QStyle::State_Children : QStyle::State_None)
01303                     | (expanded ? QStyle::State_Open : QStyle::State_None);
01304         style()->drawPrimitive(QStyle::PE_IndicatorBranch, &opt, painter, this);
01305     }
01306     // then go out level by level
01307     for (--level; level >= outer; --level) { // we have already drawn the innermost branch
01308         primitive.moveLeft(reverse ? primitive.left() + indent : primitive.left() - indent);
01309         opt.rect = primitive;
01310         opt.state = extraFlags;
01311         bool moreSiblings = false;
01312         if (d->hiddenIndexes.isEmpty()) {
01313             moreSiblings = (d->model->rowCount(ancestor) - 1 > current.row());
01314         } else {
01315             int successor = item + viewItem.total + 1;
01316             while (successor < d->viewItems.size()
01317                    && d->viewItems.at(successor).level >= uint(level)) {
01318                 const QTreeViewItem &successorItem = d->viewItems.at(successor);
01319                 if (successorItem.level == uint(level)) {
01320                     moreSiblings = true;
01321                     break;
01322                 }
01323                 successor += successorItem.total + 1;
01324             }
01325         }
01326         if (moreSiblings)
01327             opt.state |= QStyle::State_Sibling;
01328         style()->drawPrimitive(QStyle::PE_IndicatorBranch, &opt, painter, this);
01329         current = ancestor;
01330         ancestor = current.parent();
01331     }
01332     painter->setBrushOrigin(oldBO);
01333 }
01334 
01338 void QTreeView::mousePressEvent(QMouseEvent *event)
01339 {
01340     Q_D(QTreeView);
01341     // we want to handle mousePress in EditingState (persistent editors)
01342     if ((state() != NoState && state() != EditingState) || !d->viewport->rect().contains(event->pos())) {
01343         return;
01344     }
01345     int i = d->itemDecorationAt(event->pos());
01346     if (i == -1) {
01347         QAbstractItemView::mousePressEvent(event);
01348     } else if (itemsExpandable() && d->hasVisibleChildren(d->viewItems.at(i).index)) {
01349         if (d->viewItems.at(i).expanded)
01350             d->collapse(i, true);
01351         else
01352             d->expand(i, true);
01353         if (!d->isAnimating()) {
01354             updateGeometries();
01355             viewport()->update();
01356         }
01357     }
01358 }
01359 
01363 void QTreeView::mouseReleaseEvent(QMouseEvent *event)
01364 {
01365     Q_D(QTreeView);
01366     if (d->itemDecorationAt(event->pos()) == -1) { // ### what about expanding/collapsing state ?
01367         QAbstractItemView::mouseReleaseEvent(event);
01368     } else {
01369         if (state() == QAbstractItemView::DragSelectingState)
01370             setState(QAbstractItemView::NoState);
01371     }
01372 }
01373 
01377 void QTreeView::mouseDoubleClickEvent(QMouseEvent *event)
01378 {
01379     Q_D(QTreeView);
01380     if (state() != NoState || !d->viewport->rect().contains(event->pos()))
01381         return;
01382 
01383     int i = d->itemDecorationAt(event->pos());
01384     if (i == -1) {
01385         i = d->itemAtCoordinate(event->y());
01386         if (i == -1)
01387             return; // user clicked outside the items
01388 
01389         // signal handlers may change the model
01390         const QModelIndex &index = d->viewItems.at(i).index;
01391         int column = d->header->logicalIndexAt(event->x());
01392         QPersistentModelIndex persistent = index.sibling(index.row(), column);
01393         emit doubleClicked(persistent);
01394 
01395         if (!persistent.isValid())
01396             return;
01397 
01398         if (edit(persistent, DoubleClicked, event) || state() != NoState)
01399             return; // the double click triggered editing
01400 
01401         if (!style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, 0, this))
01402             emit activated(persistent);
01403 
01404         d->executePostedLayout(); // we need to make sure viewItems is updated
01405         if (d->itemsExpandable && d->hasVisibleChildren(persistent)) {
01406             if (!((i < d->viewItems.count()) && (d->viewItems.at(i).index == persistent))) {
01407                 // find the new index of the item
01408                 for (i = 0; i < d->viewItems.count(); ++i) {
01409                     if (d->viewItems.at(i).index == persistent)
01410                         break;
01411                 }
01412                 if (i == d->viewItems.count())
01413                     return;
01414             }
01415             if (d->viewItems.at(i).expanded)
01416                 d->collapse(i, true);
01417             else
01418                 d->expand(i, true);
01419             updateGeometries();
01420             viewport()->update();
01421         }
01422     }
01423 }
01424 
01428 void QTreeView::mouseMoveEvent(QMouseEvent *event)
01429 {
01430     Q_D(QTreeView);
01431     if (d->itemDecorationAt(event->pos()) == -1) // ### what about expanding/collapsing state ?
01432         QAbstractItemView::mouseMoveEvent(event);
01433 }
01434 
01438 void QTreeView::keyPressEvent(QKeyEvent *event)
01439 {
01440     Q_D(QTreeView);
01441     QModelIndex current = currentIndex();
01442     if (d->isIndexValid(current) && d->model) {
01443         switch (event->key()) {
01444         case Qt::Key_Asterisk: {
01445             QStack<QModelIndex> parents;
01446             parents.push(current);
01447             if (d->itemsExpandable) {
01448                 while (!parents.isEmpty()) {
01449                     QModelIndex parent = parents.pop();
01450                     for (int row = 0; row < d->model->rowCount(parent); ++row) {
01451                         QModelIndex child = d->model->index(row, 0, parent);
01452                         if (!d->isIndexValid(child))
01453                             break;
01454                         parents.push(child);
01455                         expand(child);
01456                     }
01457                 }
01458                 expand(current);
01459             }
01460             break; }
01461         case Qt::Key_Plus:
01462             expand(current);
01463             break;
01464         case Qt::Key_Minus:
01465             collapse(current);
01466             break;
01467         }
01468     }
01469 
01470     QAbstractItemView::keyPressEvent(event);
01471 }
01472 
01476 QModelIndex QTreeView::indexAt(const QPoint &point) const
01477 {
01478     Q_D(const QTreeView);
01479     d->executePostedLayout();
01480 
01481     int visualIndex = d->itemAtCoordinate(point.y());
01482     QModelIndex idx = d->modelIndex(visualIndex);
01483     int column = d->columnAt(point.x());
01484     if (idx.isValid() && column >= 0)
01485         return d->model->sibling(idx.row(), column, idx);
01486     return idx;
01487 }
01488 
01492 QModelIndex QTreeView::indexAbove(const QModelIndex &index) const
01493 {
01494     Q_D(const QTreeView);
01495     if (!d->isIndexValid(index))
01496         return QModelIndex();
01497     d->executePostedLayout();
01498     int i = d->viewIndex(index);
01499     if (--i < 0)
01500         return QModelIndex();
01501     return d->viewItems.at(i).index;
01502 }
01503 
01507 QModelIndex QTreeView::indexBelow(const QModelIndex &index) const
01508 {
01509     Q_D(const QTreeView);
01510     if (!d->isIndexValid(index))
01511         return QModelIndex();
01512     d->executePostedLayout();
01513     int i = d->viewIndex(index);
01514     if (++i >= d->viewItems.count())
01515         return QModelIndex();
01516     return d->viewItems.at(i).index;
01517 }
01518 
01524 void QTreeView::doItemsLayout()
01525 {
01526     Q_D(QTreeView);
01527     d->viewItems.clear(); // prepare for new layout
01528     QModelIndex parent = d->root;
01529     if (d->model->hasChildren(parent)) {
01530         QModelIndex index = d->model->index(0, 0, parent);
01531         d->defaultItemHeight = indexRowSizeHint(index);
01532         d->layout(-1);
01533         d->reexpandChildren(parent);
01534     }
01535     QAbstractItemView::doItemsLayout();
01536     d->header->doItemsLayout();
01537 }
01538 
01542 void QTreeView::reset()
01543 {
01544     Q_D(QTreeView);
01545     d->expandedIndexes.clear();
01546     d->hiddenIndexes.clear();
01547     d->viewItems.clear();
01548     QAbstractItemView::reset();
01549 }
01550 
01559 int QTreeView::horizontalOffset() const
01560 {
01561     Q_D(const QTreeView);
01562     return d->header->offset();
01563 }
01564 
01570 int QTreeView::verticalOffset() const
01571 {
01572     Q_D(const QTreeView);
01573     if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) {
01574         if (uniformRowHeights())
01575             return verticalScrollBar()->value() * d->defaultItemHeight;
01576         // If we are scrolling per item and have non-uniform row heights,
01577         // finding the vertical offset in pixels is going to be relatively slow.
01578         // ### find a faster way to do this
01579         int offset = 0;
01580         for (int i = 0; i < d->viewItems.count(); ++i) {
01581             if (i == verticalScrollBar()->value())
01582                 return offset;
01583             offset += d->itemHeight(i);
01584         }
01585         return 0;
01586     }
01587     // scroll per pixel
01588     return verticalScrollBar()->value();
01589 }
01590 
01595 QModelIndex QTreeView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
01596 {
01597     Q_D(QTreeView);
01598     Q_UNUSED(modifiers);
01599 
01600     d->executePostedLayout();
01601 
01602     QModelIndex current = currentIndex();
01603     if (!current.isValid()) {
01604         int i = 0;
01605         while (i < d->viewItems.count() && d->hiddenIndexes.contains(d->viewItems.at(i).index))
01606             ++i;
01607         return d->viewItems.value(i).index;
01608     }
01609     int vi = qMax(0, d->viewIndex(current));
01610     switch (cursorAction) {
01611     case MoveNext:
01612     case MoveDown:
01613 #ifdef QT_KEYPAD_NAVIGATION
01614         if (vi == d->viewItems.count()-1 && QApplication::keypadNavigationEnabled())
01615             return d->model->index(0, 0, d->root);
01616 #endif
01617         return d->modelIndex(d->below(vi));
01618     case MovePrevious:
01619     case MoveUp:
01620 #ifdef QT_KEYPAD_NAVIGATION
01621         if (vi == 0 && QApplication::keypadNavigationEnabled())
01622             return d->modelIndex(d->viewItems.count() - 1);
01623 #endif
01624         return d->modelIndex(d->above(vi));
01625     case MoveLeft: {
01626         QScrollBar *sb = horizontalScrollBar();
01627         if (d->viewItems.at(vi).expanded && d->itemsExpandable && sb->value() == sb->minimum())
01628             d->collapse(vi, true);
01629         else
01630            sb->setValue(sb->value() - sb->singleStep());
01631         updateGeometries();
01632         viewport()->update();
01633         break;
01634     }
01635     case MoveRight:
01636         if (!d->viewItems.at(vi).expanded && d->itemsExpandable) {
01637             d->expand(vi, true);
01638         }
01639         else {
01640            QScrollBar *sb = horizontalScrollBar();
01641            sb->setValue(sb->value() + sb->singleStep());
01642         }
01643         updateGeometries();
01644         viewport()->update();
01645         break;
01646     case MovePageUp:
01647         return d->modelIndex(d->pageUp(vi));
01648     case MovePageDown:
01649         return d->modelIndex(d->pageDown(vi));
01650     case MoveHome:
01651         return d->model->index(0, 0, d->root);
01652     case MoveEnd:
01653         return d->modelIndex(d->viewItems.count() - 1);
01654     }
01655     return current;
01656 }
01657 
01664 void QTreeView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command)
01665 {
01666     Q_D(QTreeView);
01667     if (!selectionModel())
01668         return;
01669 
01670     QPoint tl(isRightToLeft() ? qMax(rect.left(), rect.right())
01671               : qMin(rect.left(), rect.right()), qMin(rect.top(), rect.bottom()));
01672     QPoint br(isRightToLeft() ? qMin(rect.left(), rect.right()) :
01673               qMax(rect.left(), rect.right()), qMax(rect.top(), rect.bottom()));
01674     QModelIndex topLeft = indexAt(tl);
01675     QModelIndex bottomRight = indexAt(br);
01676     if (selectionBehavior() != SelectRows) {
01677         QItemSelection selection;
01678         if (topLeft.isValid() && bottomRight.isValid()) {
01679             selection.append(QItemSelectionRange(topLeft, bottomRight));
01680             selectionModel()->select(selection, command);
01681         }
01682     } else {
01683         d->select(d->viewIndex(topLeft), d->viewIndex(bottomRight), command);
01684     }
01685 }
01686 
01691 QRegion QTreeView::visualRegionForSelection(const QItemSelection &selection) const
01692 {
01693     Q_D(const QTreeView);
01694     if (selection.isEmpty())
01695         return QRegion();
01696 
01697     QRegion selectionRegion;
01698     for (int i = 0; i < selection.count(); ++i) {
01699         QItemSelectionRange range = selection.at(i);
01700         if (!range.isValid())
01701             continue;
01702         QModelIndex parent = range.parent();
01703         QModelIndex leftIndex = range.topLeft();
01704         int columnCount = d->model->columnCount(parent);
01705         while (leftIndex.isValid() && isIndexHidden(leftIndex)) {
01706             if (leftIndex.column() + 1 < columnCount)
01707                 leftIndex = d->model->index(leftIndex.row(), leftIndex.column() + 1, parent);
01708             else
01709                 leftIndex = QModelIndex();
01710         }
01711         if (!leftIndex.isValid())
01712             continue;
01713         int top = visualRect(leftIndex).top();
01714         QModelIndex rightIndex = range.bottomRight();
01715         while (rightIndex.isValid() && isIndexHidden(rightIndex)) {
01716             if (rightIndex.column() - 1 >= 0)
01717                 rightIndex = d->model->index(rightIndex.row(), rightIndex.column() - 1, parent);
01718             else
01719                 rightIndex = QModelIndex();
01720         }
01721         if (!rightIndex.isValid())
01722             continue;
01723         int bottom = visualRect(rightIndex).bottom();
01724         if (top > bottom)
01725             qSwap<int>(top, bottom);
01726         int height = bottom - top + 1;
01727         for (int c = range.left(); c <= range.right(); ++c)
01728             selectionRegion += QRegion(QRect(columnViewportPosition(c), top,
01729                                              columnWidth(c), height));
01730     }
01731     return selectionRegion;
01732 }
01733 
01737 QModelIndexList QTreeView::selectedIndexes() const
01738 {
01739     QModelIndexList viewSelected;
01740     QModelIndexList modelSelected;
01741     if (selectionModel())
01742         modelSelected = selectionModel()->selectedIndexes();
01743     for (int i = 0; i < modelSelected.count(); ++i) {
01744         // check that neither the parents nor the index is hidden before we add
01745         QModelIndex index = modelSelected.at(i);
01746         while (index.isValid() && !isIndexHidden(index))
01747             index = index.parent();
01748         if (index.isValid())
01749             continue;
01750         viewSelected.append(modelSelected.at(i));
01751     }
01752     return viewSelected;
01753 }
01754 
01758 void QTreeView::scrollContentsBy(int dx, int dy)
01759 {
01760     Q_D(QTreeView);
01761     dx = isRightToLeft() ? -dx : dx;
01762     if (dx) {
01763         if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem) {
01764             int currentScrollbarValue = horizontalScrollBar()->value();
01765             int previousScrollbarValue = currentScrollbarValue + dx; // -(-dx)
01766             d->header->setOffsetToSectionPosition(currentScrollbarValue);
01767             dx = 0;
01768             if (previousScrollbarValue < currentScrollbarValue) { // scrolling right
01769                 for (int c = previousScrollbarValue; c < currentScrollbarValue; ++c) {
01770                     int l = d->header->logicalIndex(c);
01771                     dx -= d->header->sectionSize(l);
01772                 }
01773             } else if (previousScrollbarValue > currentScrollbarValue) { // scrolling left
01774                 for (int c = previousScrollbarValue; c >= currentScrollbarValue; --c) {
01775                     int l = d->header->logicalIndex(c);
01776                     dx += d->header->sectionSize(l);
01777                 }
01778             }
01779         } else {
01780             d->header->setOffset(horizontalScrollBar()->value());
01781         }
01782     }
01783 
01784     if (d->viewItems.isEmpty() || d->defaultItemHeight == 0)
01785         return;
01786 
01787     // guestimate the number of items in the viewport
01788     int viewCount = d->viewport->height() / d->defaultItemHeight;
01789     int maxDeltaY = qMin(d->viewItems.count(), viewCount);
01790     // no need to do a lot of work if we are going to redraw the whole thing anyway
01791     if (qAbs(dy) > qAbs(maxDeltaY) && d->editors.isEmpty()) {
01792         verticalScrollBar()->repaint();
01793         d->viewport->update();
01794         return;
01795     }
01796 
01797     if (dy && verticalScrollMode() == QAbstractItemView::ScrollPerItem) {
01798         int currentScrollbarValue = verticalScrollBar()->value();
01799         int previousScrollbarValue = currentScrollbarValue + dy; // -(-dy)
01800         int currentViewIndex = currentScrollbarValue; // the first visible item
01801         int previousViewIndex = previousScrollbarValue;
01802         const QVector<QTreeViewItem> viewItems = d->viewItems;
01803         dy = 0;
01804         if (previousViewIndex < currentViewIndex) { // scrolling down
01805             for (int i = previousViewIndex; i < currentViewIndex; ++i) {
01806                 if (i < d->viewItems.count())
01807                     dy -= d->itemHeight(i);
01808             }
01809         } else if (previousViewIndex > currentViewIndex) { // scrolling up
01810             for (int i = previousViewIndex - 1; i >= currentViewIndex; --i) {
01811                 if (i < d->viewItems.count())
01812                     dy += d->itemHeight(i);
01813             }
01814         }
01815     }
01816 
01817     d->scrollContentsBy(dx, dy);
01818 }
01819 
01823 void QTreeView::columnMoved()
01824 {
01825     QAbstractItemView::dataChanged(QModelIndex(), QModelIndex());
01826 }
01827 
01831 void QTreeView::reexpand()
01832 {
01833     // do nothing
01834 }
01835 
01840 void QTreeView::rowsInserted(const QModelIndex &parent, int start, int end)
01841 {
01842     Q_D(QTreeView);
01843     d->doDelayedItemsLayout();
01844     QAbstractItemView::rowsInserted(parent, start, end);
01845 }
01846 
01851 void QTreeView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
01852 {
01853     Q_D(QTreeView);
01854     if (d->viewItems.isEmpty()) {
01855         QAbstractItemView::rowsAboutToBeRemoved(parent, start, end);
01856         return;
01857     }
01858 
01859     if (parent == d->root) {
01860         d->viewItems.clear();
01861         d->doDelayedItemsLayout();
01862         QAbstractItemView::rowsAboutToBeRemoved(parent, start, end);
01863         return;
01864     }
01865 
01866     d->executePostedLayout();
01867     setState(CollapsingState);
01868 
01869     // collapse the parent
01870     bool expanded = isExpanded(parent);
01871     d->expandParent.push(expanded);
01872     if (expanded) {
01873         int p = d->viewIndex(parent);
01874         if (p != -1)
01875             d->collapse(p, false);
01876     }
01877 
01878     QAbstractItemView::rowsAboutToBeRemoved(parent, start, end);
01879 }
01880 
01887 void QTreeView::rowsRemoved(const QModelIndex &parent, int start, int end)
01888 {
01889     Q_UNUSED(start);
01890     Q_UNUSED(end);
01891     Q_D(QTreeView);
01892 
01893     if (d->viewItems.isEmpty()) {
01894         d->_q_rowsRemoved(parent, start, end);
01895         return;
01896     }
01897     if (parent == d->root) {
01898         d->viewItems.clear();
01899         d->doDelayedItemsLayout();
01900         d->_q_rowsRemoved(parent, start, end);
01901         return;
01902     }
01903 
01904     bool expanded = d->expandParent.pop();
01905     if (expanded) {
01906         int p = d->viewIndex(parent);
01907         if (p != -1) { // item is visible
01908             d->expand(p, false);
01909             d->viewport->update();
01910         } else if (!d->expandedIndexes.contains(parent)) {
01911             d->expandedIndexes.append(parent);
01912         }
01913     }
01914     if (d->expandParent.isEmpty()) {
01915         setState(NoState);
01916         d->updateScrollBars();
01917     }
01918 }
01919 
01924 void QTreeView::columnCountChanged(int, int)
01925 {
01926     if (isVisible())
01927         updateGeometries();
01928   viewport()->update();
01929 }
01930 
01936 void QTreeView::resizeColumnToContents(int column)
01937 {
01938     Q_D(QTreeView);
01939     d->executePostedLayout();
01940     if (column < 0 || column >= d->header->count())
01941         return;
01942     int contents = sizeHintForColumn(column);
01943     int header = d->header->isHidden() ? 0 : d->header->sectionSizeHint(column);
01944     d->header->resizeSection(column, qMax(contents, header));
01945 }
01946 
01953 void QTreeView::sortByColumn(int column)
01954 {
01955     Q_D(QTreeView);
01956     if (column == -1)
01957         return;
01958     d->model->sort(column, d->header->sortIndicatorOrder());
01959 }
01960 
01968 void QTreeView::sortByColumn(int column, Qt::SortOrder order)
01969 {
01970     Q_D(QTreeView);
01971     d->header->setSortIndicator(column, order);
01972     sortByColumn(column);
01973 }
01974 
01978 void QTreeView::selectAll()
01979 {
01980     Q_D(QTreeView);
01981     if (!selectionModel())
01982         return;
01983     d->select(0, d->viewItems.count() - 1,
01984               QItemSelectionModel::ClearAndSelect
01985               |QItemSelectionModel::Rows);
01986 }
01987 
01994 void QTreeView::expandAll()
01995 {
01996     Q_D(QTreeView);
01997     d->executePostedLayout();
01998     d->expandedIndexes.clear();
01999     for (int i = 0; i < d->viewItems.count(); ++i)
02000         if (!d->viewItems.at(i).expanded)
02001             d->expand(i, false);
02002     updateGeometries();
02003     d->viewport->update();
02004 }
02005 
02013 void QTreeView::collapseAll()
02014 {
02015     Q_D(QTreeView);
02016     d->expandedIndexes.clear();
02017     doItemsLayout();
02018 }
02019 
02027 void QTreeView::columnResized(int column, int /* oldSize */, int /* newSize */)
02028 {
02029     Q_D(QTreeView);
02030     d->columnsToUpdate.append(column);
02031     if (d->columnResizeTimerID == 0)
02032         d->columnResizeTimerID = startTimer(0);
02033 }
02034 
02039 void QTreeView::updateGeometries()
02040 {
02041     Q_D(QTreeView);
02042     if (d->header) {
02043         QSize hint = d->header->isHidden() ? QSize(0, 0) : d->header->sizeHint();
02044         setViewportMargins(0, hint.height(), 0, 0);
02045         QRect vg = d->viewport->geometry();
02046         QRect geometryRect(vg.left(), vg.top() - hint.height(), vg.width(), hint.height());
02047         d->header->setGeometry(geometryRect);
02048         d->header->setOffset(horizontalScrollBar()->value());
02049         if (d->header->isHidden())
02050             QMetaObject::invokeMethod(d->header, "updateGeometries");
02051         d->updateScrollBars();
02052     }
02053     QAbstractItemView::updateGeometries();
02054 }
02055 
02070 int QTreeView::sizeHintForColumn(int column) const
02071 {
02072     Q_D(const QTreeView);
02073     d->executePostedLayout();
02074     if (d->viewItems.isEmpty())
02075         return -1;
02076     int w = 0;
02077     QStyleOptionViewItemV2 option = d->viewOptionsV2();
02078     const QVector<QTreeViewItem> viewItems = d->viewItems;
02079     for (int i = 0; i < viewItems.count(); ++i) {
02080         QModelIndex index = viewItems.at(i).index;
02081         if (index.column() != column)
02082             index = index.sibling(index.row(), column);
02083         int width = d->delegateForIndex(index)->sizeHint(option, index).width();
02084         w = qMax(w, width + (column == 0 ? d->indentationForItem(i) : 0));
02085     }
02086     return w;
02087 }
02088 
02094 int QTreeView::indexRowSizeHint(const QModelIndex &index) const
02095 {
02096     Q_D(const QTreeView);
02097     if (!d->isIndexValid(index) || !d->itemDelegate)
02098         return 0;
02099 
02100     int start = -1;
02101     int end = -1;
02102     int count = d->header->count();
02103     if (count) {
02104         // If the sections have moved, we end up checking too many or too few
02105         start = d->header->logicalIndexAt(0);
02106         end = d->header->logicalIndexAt(viewport()->width());
02107     } else {
02108         // If the header has not been laid out yet, we use the model directly
02109         count = d->model->columnCount(index.parent());
02110     }
02111 
02112     if (isRightToLeft()) {
02113         start = (start == -1 ? count - 1 : start);
02114         end = (end == -1 ? 0 : end);
02115     } else {
02116         start = (start == -1 ? 0 : start);
02117         end = (end == -1 ? count - 1 : end);
02118     }
02119 
02120     int tmp = start;
02121     start = qMin(start, end);
02122     end = qMax(tmp, end);
02123 
02124     int height = -1;
02125     QStyleOptionViewItemV2 option = d->viewOptionsV2();
02126     // ### If we want word wrapping in the items,
02127     // ### we need to go through all the columns
02128     // ### and set the width of the column
02129 
02130     // ### Temporary hack to speed up the function
02131     option.rect.setWidth(-1);
02132     for (int column = start; column <= end; ++column) {
02133         QModelIndex idx = index.sibling(index.row(), column);
02134         if (idx.isValid()) {
02135             if (QWidget *editor = d->editorForIndex(idx))
02136                 height = qMax(height, editor->size().height());
02137             int hint = d->delegateForIndex(idx)->sizeHint(option, idx).height();
02138             height = qMax(height, hint);
02139         }
02140     }
02141 
02142     return height;
02143 }
02144 
02148 void QTreeView::horizontalScrollbarAction(int action)
02149 {
02150     QAbstractItemView::horizontalScrollbarAction(action);
02151 }
02152 
02156 bool QTreeView::isIndexHidden(const QModelIndex &index) const
02157 {
02158     return (isColumnHidden(index.column()) || isRowHidden(index.row(), index.parent()));
02159 }
02160 
02161 /*
02162   private implementation
02163 */
02164 void QTreeViewPrivate::initialize()
02165 {
02166     Q_Q(QTreeView);
02167     q->setSelectionBehavior(QAbstractItemView::SelectRows);
02168     q->setSelectionMode(QAbstractItemView::SingleSelection);
02169     q->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
02170 
02171     QHeaderView *header = new QHeaderView(Qt::Horizontal, q);
02172     header->setMovable(true);
02173     header->setStretchLastSection(true);
02174     q->setHeader(header);
02175 
02176     // animation
02177     QObject::connect(&timeline, SIGNAL(frameChanged(int)), viewport, SLOT(update()));
02178     QObject::connect(&timeline, SIGNAL(finished()), q, SLOT(_q_endAnimatedOperation()));
02179 }
02180 
02181 void QTreeViewPrivate::expand(int item, bool emitSignal)
02182 {
02183     Q_Q(QTreeView);
02184 
02185     if (item == -1 || viewItems.at(item).expanded)
02186         return;
02187 
02188     if (emitSignal && animationsEnabled)
02189         prepareAnimatedOperation(item, AnimatedOperation::Expand);
02190 
02191     q->setState(QAbstractItemView::ExpandingState);
02192     const QModelIndex index = viewItems.at(item).index;
02193     expandedIndexes.append(index);
02194     viewItems[item].expanded = true;
02195     layout(item);
02196     if (model->hasChildren(index))
02197         reexpandChildren(index); // will call expand with emitSignal == false
02198     q->setState(QAbstractItemView::NoState);
02199 
02200     if (emitSignal) {
02201         if (animationsEnabled)
02202             beginAnimatedOperation();
02203         else
02204             emit q->expanded(index);
02205     }
02206     if (model->canFetchMore(index))
02207         model->fetchMore(index);
02208 }
02209 
02210 void QTreeViewPrivate::collapse(int item, bool emitSignal)
02211 {
02212     Q_Q(QTreeView);
02213 
02214     if (item == -1 || expandedIndexes.isEmpty())
02215         return;
02216 
02217     int total = viewItems.at(item).total;
02218     const QModelIndex &modelIndex = viewItems.at(item).index;
02219     int index = expandedIndexes.indexOf(modelIndex);
02220     if (index == -1 || viewItems.at(item).expanded == false)
02221         return; // nothing to do
02222 
02223     if (emitSignal && animationsEnabled)
02224         prepareAnimatedOperation(item, AnimatedOperation::Collapse);
02225 
02226     q->setState(QAbstractItemView::CollapsingState);
02227     expandedIndexes.remove(index);
02228     viewItems[item].expanded = false;
02229     index = item;
02230     QModelIndex parent = modelIndex;
02231     while (parent.isValid() && parent != root) {
02232         Q_ASSERT(index > -1);
02233         viewItems[index].total -= total;
02234         parent = parent.parent();
02235         index = viewIndex(parent);
02236     }
02237     viewItems.remove(item + 1, total); // collapse
02238     q->setState(QAbstractItemView::NoState);
02239 
02240     if (emitSignal) {
02241         if (animationsEnabled)
02242             beginAnimatedOperation();
02243         else
02244             emit q->collapsed(modelIndex);
02245     }
02246 }
02247 
02248 void QTreeViewPrivate::prepareAnimatedOperation(int item, AnimatedOperation::Type type)
02249 {
02250     animatedOperation.item = item;
02251     animatedOperation.type = type;
02252 
02253     int top = coordinateForItem(item) + itemHeight(item);
02254     QRect rect = viewport->rect();
02255     if (type == AnimatedOperation::Collapse) {
02256         int h = 0;
02257         int c = item + viewItems.at(item).total + 1;
02258         for (int i = item + 1; i < c; ++i)
02259             h += itemHeight(i);
02260         rect.setHeight(h);
02261         animatedOperation.duration = h;
02262     }
02263     rect.moveTop(top);
02264     animatedOperation.top = top;
02265     animatedOperation.before = renderTreeToPixmap(rect);
02266 }
02267 
02268 void QTreeViewPrivate::beginAnimatedOperation()
02269 {
02270     Q_Q(QTreeView);
02271 
02272     QRect rect = viewport->rect();
02273     if (animatedOperation.type == AnimatedOperation::Expand) {
02274         int h = 0;
02275         int c = animatedOperation.item + viewItems.at(animatedOperation.item).total + 1;
02276         for (int i = animatedOperation.item + 1; i < c; ++i)
02277             h += itemHeight(i);
02278         rect.setHeight(h);
02279         animatedOperation.duration = h;
02280     }
02281     rect.moveTop(animatedOperation.top);
02282 
02283     animatedOperation.after = renderTreeToPixmap(rect);
02284 
02285     timeline.stop();
02286     timeline.setDuration(1000);
02287     timeline.setFrameRange(animatedOperation.top, animatedOperation.top + animatedOperation.duration);
02288     timeline.start();
02289 
02290     q->setState(QAbstractItemView::AnimatingState);
02291 }
02292 
02293 void QTreeViewPrivate::_q_endAnimatedOperation()
02294 {
02295     Q_Q(QTreeView);
02296     animatedOperation.before = QPixmap();
02297     animatedOperation.after = QPixmap();
02298     q->setState(QAbstractItemView::NoState);
02299     if (animatedOperation.type == AnimatedOperation::Expand)
02300         emit q->expanded(viewItems.at(animatedOperation.item).index);
02301     else // operation == AnimatedOperation::Collapse
02302         emit q->collapse(viewItems.at(animatedOperation.item).index);
02303     q->updateGeometries();
02304     viewport->update();
02305 }
02306 
02307 void QTreeViewPrivate::drawAnimatedOperation(QPainter *painter) const
02308 {
02309     int start = timeline.startFrame();
02310     int end = timeline.endFrame();
02311     bool collapsing = animatedOperation.type == AnimatedOperation::Collapse;
02312     int current = collapsing ? end - timeline.currentFrame() + start : timeline.currentFrame();
02313     const QPixmap top = collapsing ? animatedOperation.before : animatedOperation.after;
02314     painter->drawPixmap(0, start, top, 0, end - current - 1, top.width(), top.height());
02315     const QPixmap bottom = collapsing ? animatedOperation.after : animatedOperation.before;
02316     painter->drawPixmap(0, current, bottom);
02317 }
02318 
02319 QPixmap QTreeViewPrivate::renderTreeToPixmap(const QRect &rect) const
02320 {
02321     Q_Q(const QTreeView);
02322     QPixmap pixmap(rect.size());
02323     QPainter painter(&pixmap);
02324     painter.translate(0, -rect.top());
02325     q->drawTree(&painter, QRegion(rect));
02326     painter.end();
02327     return pixmap;
02328 }
02329 
02330 void QTreeViewPrivate::_q_currentChanged(const QModelIndex &current, const QModelIndex &previous)
02331 {
02332     Q_Q(QTreeView);
02333     if (previous.isValid()) {
02334         QRect previousRect = q->visualRect(previous);
02335         if (allColumnsShowFocus) {
02336             previousRect.setX(0);
02337             previousRect.setWidth(viewport->width());
02338         }
02339         viewport->update(previousRect);
02340     }
02341     if (current.isValid()) {
02342         QRect currentRect = q->visualRect(current);
02343         if (allColumnsShowFocus) {
02344             currentRect.setX(0);
02345             currentRect.setWidth(viewport->width());
02346         }
02347         viewport->update(currentRect);
02348     }
02349 }
02350 
02351 void QTreeViewPrivate::layout(int i)
02352 {
02353     Q_Q(QTreeView);
02354     QModelIndex current;
02355     QModelIndex parent = (i < 0) ? (QModelIndex)root : modelIndex(i);
02356     // modelIndex() will return an index that don't have a parent if column 0 is hidden,
02357     // so we must make sure that parent points to the actual parent that has children.
02358     if (parent != root)
02359         parent = model->index(parent.row(), 0, parent.parent());
02360 
02361     int count = 0;
02362     if (model->hasChildren(parent))
02363         count = model->rowCount(parent);
02364 
02365     if (i == -1) {
02366         viewItems.resize(count);
02367     } else {
02368         if (viewItems[i].total != (uint)count)
02369             viewItems.insert(i + 1, count, QTreeViewItem()); // expand
02370     }
02371 
02372     int first = i + 1;
02373     int level = (i >= 0 ? viewItems.at(i).level + 1 : 0);
02374     int hidden = 0;
02375     int last = 0;
02376 
02377     int firstColumn = 0;
02378     while (q->isColumnHidden(firstColumn) && firstColumn < q->header()->count())
02379         ++firstColumn;
02380 
02381     for (int j = first; j < first + count; ++j) {
02382         current = model->index(j - first, firstColumn, parent);
02383         if (q->isRowHidden(current.row(), parent)) { // slow with lots of hidden rows
02384             ++hidden;
02385             last = j - hidden;
02386         } else {
02387             last = j - hidden;
02388             viewItems[last].index = current;
02389             viewItems[last].level = level;
02390         }
02391     }
02392 
02393     // remove hidden items
02394     if (hidden > 0)
02395         viewItems.remove(last + 1, hidden); // collapse
02396 
02397     while (parent != root) {
02398         Q_ASSERT(i > -1);
02399         viewItems[i].total += count - hidden;
02400         parent = parent.parent();
02401         i = viewIndex(parent);
02402     }
02403 }
02404 
02405 int QTreeViewPrivate::pageUp(int i) const
02406 {
02407     int index = itemAtCoordinate(coordinateForItem(i) - viewport->height());
02408     return index == -1 ? 0 : index;
02409 }
02410 
02411 int QTreeViewPrivate::pageDown(int i) const
02412 {
02413     int index = itemAtCoordinate(coordinateForItem(i) + viewport->height());
02414     return index == -1 ? viewItems.count() - 1 : index;
02415 }
02416 
02417 int QTreeViewPrivate::indentationForItem(int item) const
02418 {
02419     if (item < 0 || item >= viewItems.count())
02420         return 0;
02421     int level = viewItems.at(item).level;
02422     if (rootDecoration)
02423         ++level;
02424     return level * indent;
02425 }
02426 
02427 int QTreeViewPrivate::itemHeight(int item) const
02428 {
02429     if (uniformRowHeights)
02430         return defaultItemHeight;
02431     if (viewItems.isEmpty())
02432         return 0;
02433     const QModelIndex &index = viewItems.at(item).index;
02434     int height = viewItems.at(item).height;
02435     if (height <= 0 && index.isValid()) {
02436         height = q_func()->indexRowSizeHint(index);
02437         viewItems[item].height = height;
02438     }
02439     if (!index.isValid() || height < 0)
02440         return 0;
02441     return height;
02442 }
02443 
02444 
02449 int QTreeViewPrivate::coordinateForItem(int item) const
02450 {
02451     Q_Q(const QTreeView);
02452     if (verticalScrollMode == QAbstractItemView::ScrollPerPixel) {
02453         if (uniformRowHeights)
02454             return (item * defaultItemHeight) - q->verticalScrollBar()->value();
02455         // ### optimize (spans or caching)
02456         int y = 0;
02457         for (int i = 0; i < viewItems.count(); ++i) {
02458             if (i == item)
02459                 return y - q->verticalScrollBar()->value();
02460             y += itemHeight(i);
02461         }
02462     } else { // ScrollPerItem
02463         int topViewItemIndex = q->verticalScrollBar()->value();
02464 //         if (topViewItemIndex == -1) {
02465 //             const_cast<QTreeViewPrivate*>(this)->updateScrollBars();
02466 //             topViewItemIndex = q->verticalScrollBar()->value();
02467 //             Q_ASSERT(topViewItemIndex != -1); // the scrollbars were not updated correctly
02468 //         }
02469         if (uniformRowHeights)
02470             return defaultItemHeight * (item - topViewItemIndex);
02471         if (item >= topViewItemIndex) {
02472             // search in the visible area first and continue down
02473             int viewItemCoordinate = 0;
02474             int viewItemIndex = topViewItemIndex;
02475             while (viewItemIndex < viewItems.count()) {
02476                 if (viewItemIndex == item)
02477                     return viewItemCoordinate;
02478                 viewItemCoordinate += itemHeight(viewItemIndex);
02479                 ++viewItemIndex;
02480             }
02481             // below the last item in the view
02482             Q_ASSERT(false);
02483             return viewItemCoordinate;
02484         } else {
02485             // search the area above the viewport
02486             int viewItemCoordinate = 0;
02487             for (int viewItemIndex = topViewItemIndex; viewItemIndex >= 0; --viewItemIndex) {
02488                 if (viewItemIndex == item)
02489                     return viewItemCoordinate;
02490                 viewItemCoordinate -= itemHeight(viewItemIndex);
02491             }
02492             // above the first item in the view
02493             Q_ASSERT(false);
02494             return viewItemCoordinate;
02495         }
02496     }
02497     return 0;
02498 }
02499 
02507 int QTreeViewPrivate::itemAtCoordinate(int coordinate) const
02508 {
02509     Q_Q(const QTreeView);
02510     const int itemCount = viewItems.count();
02511     if (itemCount == 0)
02512         return -1;
02513     if (uniformRowHeights && defaultItemHeight <= 0)
02514         return -1;
02515     if (verticalScrollMode == QAbstractItemView::ScrollPerPixel) {
02516         if (uniformRowHeights) {
02517             const int viewItemIndex = (coordinate + q->verticalScrollBar()->value()) / defaultItemHeight;
02518             return (viewItemIndex >= itemCount ? -1 : viewItemIndex);
02519         }
02520         // ### optimize
02521         int viewItemCoordinate = 0;
02522         const int contentsCoordinate = coordinate + q->verticalScrollBar()->value();
02523         for (int viewItemIndex = 0; viewItemIndex < viewItems.count(); ++viewItemIndex) {
02524             viewItemCoordinate += itemHeight(viewItemIndex);
02525             if (viewItemCoordinate >= contentsCoordinate)
02526                 return (viewItemIndex >= itemCount ? -1 : viewItemIndex);
02527         }
02528     } else { // ScrollPerItem
02529         int topViewItemIndex = q->verticalScrollBar()->value();
02530         if (uniformRowHeights) {
02531             const int viewItemIndex = topViewItemIndex + (coordinate / defaultItemHeight);
02532             return (viewItemIndex >= itemCount ? -1 : viewItemIndex);
02533         }
02534         if (coordinate >= 0) {
02535             // the coordinate is in or below the viewport
02536             int viewItemCoordinate = 0;
02537             for (int viewItemIndex = topViewItemIndex; viewItemIndex < viewItems.count(); ++viewItemIndex) {
02538                 viewItemCoordinate += itemHeight(viewItemIndex);
02539                 if (viewItemCoordinate > coordinate)
02540                     return (viewItemIndex >= itemCount ? -1 : viewItemIndex);
02541             }
02542         } else {
02543             // the coordinate is above the viewport
02544             int viewItemCoordinate = 0;
02545             for (int viewItemIndex = topViewItemIndex; viewItemIndex >= 0; --viewItemIndex) {
02546                 if (viewItemCoordinate <= coordinate)
02547                     return (viewItemIndex >= itemCount ? -1 : viewItemIndex);
02548                 viewItemCoordinate -= itemHeight(viewItemIndex);
02549             }
02550         }
02551     }
02552     return -1;
02553 }
02554 
02555 int QTreeViewPrivate::viewIndex(const QModelIndex &index) const
02556 {
02557     if (!index.isValid())
02558         return -1;
02559 
02560     int totalCount = viewItems.count();
02561     QModelIndex parent = index.parent();
02562 
02563     // A quick check near the last item to see if we are just incrementing
02564     int start = lastViewedItem > 2 ? lastViewedItem - 2 : 0;
02565     int end = lastViewedItem < totalCount - 2 ? lastViewedItem + 2 : totalCount;
02566     for (int i = start; i < end; ++i) {
02567         const QModelIndex &idx = viewItems.at(i).index;
02568         if (idx.row() == index.row()) {
02569             if (idx.internalId() == index.internalId() || idx.parent() == parent) {// ignore column
02570                 lastViewedItem = i;
02571                 return i;
02572             }
02573         }
02574     }
02575 
02576     // NOTE: this function is slow if the item is outside the visible area
02577     // search in visible items first and below
02578     int t = firstVisibleItem();
02579     t = t > 100 ? t - 100 : 0; // start 100 items above the visible area
02580 
02581     for (int i = t; i < totalCount; ++i) {
02582         const QModelIndex &idx = viewItems.at(i).index;
02583         if (idx.row() == index.row()) {
02584             if (idx.internalId() == index.internalId() || idx.parent() == parent) {// ignore column
02585                 lastViewedItem = i;
02586                 return i;
02587             }
02588         }
02589     }
02590     // search from top to first visible
02591     for (int j = 0; j < t; ++j) {
02592         const QModelIndex &idx = viewItems.at(j).index;
02593         if (idx.row() == index.row()) {
02594             if (idx.internalId() == index.internalId() || idx.parent() == parent) { // ignore column
02595                 lastViewedItem = j;
02596                 return j;
02597             }
02598         }
02599     }
02600     // nothing found
02601     return -1;
02602 }
02603 
02604 QModelIndex QTreeViewPrivate::modelIndex(int i) const
02605 {
02606     return ((i < 0 || i >= viewItems.count())
02607             ? QModelIndex() : viewItems.at(i).index);
02608 }
02609 
02610 int QTreeViewPrivate::firstVisibleItem(int *offset) const
02611 {
02612     Q_Q(const QTreeView);
02613     const int value = q->verticalScrollBar()->value();
02614     if (verticalScrollMode == QAbstractItemView::ScrollPerItem) {
02615         if (offset)
02616             *offset = 0;
02617         return (value < 0 || value >= viewItems.count()) ? -1 : value;
02618     }
02619     // ScrollMode == ScrollPerPixel
02620     if (uniformRowHeights) {
02621         if (offset)
02622             *offset = -(value % defaultItemHeight);
02623         return value / defaultItemHeight;
02624     }
02625     int y = 0; // ### optimize (use spans ?)
02626     for (int i = 0; i < viewItems.count(); ++i) {
02627         y += itemHeight(i); // the height value is cached
02628         if (y > value) {
02629             if (offset)
02630                 *offset = y - value - itemHeight(i);
02631             return i;
02632         }
02633     }
02634     return -1;
02635 }
02636 
02637 int QTreeViewPrivate::columnAt(int x) const
02638 {
02639     return header->logicalIndexAt(x);
02640 }
02641 
02642 void QTreeViewPrivate::relayout(const QModelIndex &parent)
02643 {
02644     Q_Q(QTreeView);
02645     // do a local relayout of the items
02646     if (parent.isValid()) {
02647         int parentViewIndex = viewIndex(parent);
02648         if (parentViewIndex > -1 && viewItems.at(parentViewIndex).expanded) {
02649             collapse(parentViewIndex, false); // remove the current layout
02650             expand(parentViewIndex, false); // do the relayout
02651             q->updateGeometries();
02652             viewport->update();
02653         }
02654     } else {
02655         viewItems.clear();
02656         q->doItemsLayout();
02657     }
02658 }
02659 
02660 void QTreeViewPrivate::reexpandChildren(const QModelIndex &parent)
02661 {
02662     if (!model)
02663         return;
02664     // ### optimize
02665     QVector<int> toBeExpanded;
02666     QVector<QPersistentModelIndex>::iterator it;
02667     for (it = expandedIndexes.begin(); it != expandedIndexes.end(); ) {
02668         QModelIndex index = *it;
02669         if (!index.isValid()) {
02670             it = expandedIndexes.erase(it);
02671         } else if (model->parent(index) == parent) {
02672             int v = viewIndex(index);
02673             if (v >= 0) {
02674                 toBeExpanded.append(v);
02675                 it = expandedIndexes.erase(it);
02676             } else {
02677                 ++it;
02678             }
02679         } else {
02680             ++it;
02681         }
02682     }
02683     qSort(toBeExpanded.begin(), toBeExpanded.end(), qGreater<int>());
02684     for (int i = 0; i < toBeExpanded.count(); ++i)
02685         expand(toBeExpanded.at(i), false);
02686 }
02687 
02688 void QTreeViewPrivate::updateScrollBars()
02689 {
02690     Q_Q(QTreeView);
02691     QSize viewportSize = viewport->size();
02692     if (!viewportSize.isValid())
02693         viewportSize = QSize(0, 0);
02694 
02695     if (verticalScrollMode == QAbstractItemView::ScrollPerItem) {
02696         int itemsInViewport = 0;
02697         if (uniformRowHeights) {
02698             if (defaultItemHeight == 0)
02699                 itemsInViewport = viewItems.count();
02700             else
02701                 itemsInViewport = viewportSize.height() / defaultItemHeight;
02702         } else {
02703             const int itemsCount = viewItems.count();
02704             const int viewportHeight = viewportSize.height();
02705             for (int height = 0, item = itemsCount - 1; item >= 0; --item) {
02706                 height += itemHeight(item);
02707                 if (height > viewportHeight)
02708                     break;
02709                 ++itemsInViewport;
02710             }
02711         }
02712         if (!viewItems.isEmpty())
02713             itemsInViewport = qMax(1, itemsInViewport);
02714         q->verticalScrollBar()->setRange(0, viewItems.count() - itemsInViewport);
02715         q->verticalScrollBar()->setPageStep(itemsInViewport);
02716     } else { // scroll per pixel
02717         int contentsHeight = 0;
02718         if (uniformRowHeights) {
02719             contentsHeight = defaultItemHeight * viewItems.count();
02720         } else { // ### optimize (spans or caching)
02721             for (int i = 0; i < viewItems.count(); ++i)
02722                 contentsHeight += itemHeight(i);
02723         }
02724         q->verticalScrollBar()->setRange(0, contentsHeight - viewportSize.height());
02725         q->verticalScrollBar()->setPageStep(viewportSize.height());
02726     }
02727 
02728     if (horizontalScrollMode == QAbstractItemView::ScrollPerItem) {
02729         const int columnCount = header->count();
02730         const int viewportWidth = viewportSize.width();
02731         int columnsInViewport = 0;
02732         for (int width = 0, column = columnCount - 1; column >= 0; --column) {
02733             int logical = header->logicalIndex(column);
02734             width += header->sectionSize(logical);
02735             if (width > viewportWidth)
02736                 break;
02737             ++columnsInViewport;
02738         }
02739         if (columnCount > 0)
02740             columnsInViewport = qMax(1, columnsInViewport);
02741         q->horizontalScrollBar()->setRange(0, columnCount - columnsInViewport);
02742         q->horizontalScrollBar()->setPageStep(columnsInViewport);
02743     } else { // scroll per pixel
02744         const int horizontalLength = header->length();
02745         const QSize maxSize = q->maximumViewportSize();
02746         if (maxSize.width() >= horizontalLength && q->verticalScrollBar()->maximum() <= 0)
02747             viewportSize = maxSize;
02748         q->horizontalScrollBar()->setPageStep(viewportSize.width());
02749         q->horizontalScrollBar()->setRange(0, qMax(horizontalLength - viewportSize.width(), 0));
02750     }
02751 }
02752 
02753 int QTreeViewPrivate::itemDecorationAt(const QPoint &pos) const
02754 {
02755     Q_Q(const QTreeView);
02756     int x = pos.x();
02757     int column = header->logicalIndexAt(x);
02758     if (column == -1)
02759         return -1; // no logical index at x
02760     int position = header->sectionViewportPosition(column);
02761     int size = header->sectionSize(column);
02762     int cx = (q->isRightToLeft() ? size - x + position : x - position);
02763     int viewItemIndex = itemAtCoordinate(pos.y());
02764     int itemIndentation = indentationForItem(viewItemIndex);
02765     QModelIndex index = modelIndex(viewItemIndex);
02766 
02767     if (!index.isValid() || column != 0
02768         || cx < (itemIndentation - indent) || cx > itemIndentation)
02769         return -1; // pos is outside the decoration rect
02770 
02771     if (!rootDecoration && index.parent() == root)
02772         return -1; // no decoration at root
02773 
02774     QRect rect;
02775     if (q->isRightToLeft())
02776         rect = QRect(position + size - itemIndentation, coordinateForItem(viewItemIndex),
02777                      indent, itemHeight(viewItemIndex));
02778     else
02779         rect = QRect(position + itemIndentation - indent, coordinateForItem(viewItemIndex),
02780                      indent, itemHeight(viewItemIndex));
02781     QStyleOption opt;
02782     opt.initFrom(q);
02783     opt.rect = rect;
02784     QRect returning = q->style()->subElementRect(QStyle::SE_TreeViewDisclosureItem, &opt, q);
02785     if (!returning.contains(pos))
02786         return -1;
02787 
02788     return viewItemIndex;
02789 }
02790 
02791 void QTreeViewPrivate::select(int top, int bottom,
02792                               QItemSelectionModel::SelectionFlags command)
02793 {
02794     Q_Q(QTreeView);
02795     QModelIndex previous;
02796     QItemSelectionRange currentRange;
02797     QStack<QItemSelectionRange> rangeStack;
02798     QItemSelection selection;
02799     for (int i = top; i <= bottom; ++i) {
02800         QModelIndex index = modelIndex(i);
02801         QModelIndex parent = index.parent();
02802         if (previous.isValid() && parent == previous.parent()) {
02803             // same parent
02804             QModelIndex tl = model->index(currentRange.top(), currentRange.left(),
02805                                           currentRange.parent());
02806             currentRange = QItemSelectionRange(tl, index);
02807         } else if (previous.isValid() && parent == model->sibling(previous.row(), 0, previous)) {
02808             // item is child of previous
02809             rangeStack.push(currentRange);
02810             currentRange = QItemSelectionRange(index);
02811         } else {
02812             if (currentRange.isValid())
02813                 selection.append(currentRange);
02814             if (rangeStack.isEmpty()) {
02815                 currentRange = QItemSelectionRange(index);
02816             } else {
02817                 currentRange = rangeStack.pop();
02818                 if (parent == currentRange.parent()) {
02819                     QModelIndex tl = model->index(currentRange.top(),
02820                                                   currentRange.left(),
02821                                                   currentRange.parent());
02822                     currentRange = QItemSelectionRange(tl, index);
02823                 } else {
02824                     selection.append(currentRange);
02825                     currentRange = QItemSelectionRange(index);
02826                 }
02827             }
02828         }
02829         previous = index;
02830     }
02831     if (currentRange.isValid())
02832         selection.append(currentRange);
02833     for (int i = 0; i < rangeStack.count(); ++i)
02834         selection.append(rangeStack.at(i));
02835     q->selectionModel()->select(selection, command);
02836 }
02837 
02838 QPair<int,int> QTreeViewPrivate::startAndEndColumns(const QRect &rect) const
02839 {
02840     Q_Q(const QTreeView);
02841     int start = header->visualIndexAt(rect.left());
02842     int end = header->visualIndexAt(rect.right());
02843     if (q->isRightToLeft()) {
02844         start = (start == -1 ? header->count() - 1 : start);
02845         end = (end == -1 ? 0 : end);
02846     } else {
02847         start = (start == -1 ? 0 : start);
02848         end = (end == -1 ? header->count() - 1 : end);
02849     }
02850     return qMakePair<int,int>(qMin(start, end), qMax(start, end));
02851 }
02852 
02853 bool QTreeViewPrivate::hasVisibleChildren(const QModelIndex& parent) const
02854 {
02855     Q_Q(const QTreeView);
02856     if (model->hasChildren(parent)) {
02857         if (hiddenIndexes.isEmpty())
02858             return true;
02859         if (q->isIndexHidden(parent))
02860             return false;
02861         int rowCount = model->rowCount(parent);
02862         for (int i = 0; i < rowCount; ++i) {
02863             if (!q->isRowHidden(i, parent))
02864                 return true;
02865         }
02866         if (rowCount == 0)
02867             return true;
02868     }
02869     return false;
02870 }
02871 
02872 QStyleOptionViewItemV2 QTreeViewPrivate::viewOptionsV2() const
02873 {
02874     Q_Q(const QTreeView);
02875     QStyleOptionViewItemV2 option = q->viewOptions();
02876     // don't wrap text by default
02877     // option.features = QStyleOptionViewItemV2::WrapText;
02878     return option;
02879 }
02880 
02881 #include "moc_qtreeview.cpp"
02882 
02883 #endif // QT_NO_TREEVIEW

Generated on Thu Mar 15 11:55:00 2007 for Qt 4.2 User's Guide by  doxygen 1.5.1