• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KDEUI

kcategorizedview.cpp

Go to the documentation of this file.
00001 
00021 #include "kcategorizedview.h"
00022 #include "kcategorizedview_p.h"
00023 
00024 #include <math.h> // trunc on C99 compliant systems
00025 #include <kdefakes.h> // trunc for not C99 compliant systems
00026 
00027 #include <QPainter>
00028 #include <QScrollBar>
00029 #include <QPaintEvent>
00030 
00031 #include <kdebug.h>
00032 
00033 #include "kcategorydrawer.h"
00034 #include "kcategorizedsortfilterproxymodel.h"
00035 
00036 // By defining DOLPHIN_DRAGANDDROP the custom drag and drop implementation of
00037 // KCategorizedView is bypassed to have a consistent drag and drop look for all
00038 // views. Hopefully transparent pixmaps for drag objects will be supported in
00039 // Qt 4.4, so that this workaround can be skipped.
00040 #define DOLPHIN_DRAGANDDROP
00041 
00042 KCategorizedView::Private::Private(KCategorizedView *listView)
00043     : listView(listView)
00044     , categoryDrawer(0)
00045     , biggestItemSize(QSize(0, 0))
00046     , mouseButtonPressed(false)
00047     , rightMouseButtonPressed(false)
00048     , isDragging(false)
00049     , dragLeftViewport(false)
00050     , proxyModel(0)
00051 {
00052 }
00053 
00054 KCategorizedView::Private::~Private()
00055 {
00056 }
00057 
00058 const QModelIndexList &KCategorizedView::Private::intersectionSet(const QRect &rect)
00059 {
00060     QModelIndex index;
00061     QRect indexVisualRect;
00062 
00063     intersectedIndexes.clear();
00064 
00065     int itemHeight;
00066 
00067     if (listView->gridSize().isEmpty())
00068     {
00069         itemHeight = biggestItemSize.height();
00070     }
00071     else
00072     {
00073         itemHeight = listView->gridSize().height();
00074     }
00075 
00076     // Lets find out where we should start
00077     int top = proxyModel->rowCount() - 1;
00078     int bottom = 0;
00079     int middle = (top + bottom) / 2;
00080     while (bottom <= top)
00081     {
00082         middle = (top + bottom) / 2;
00083 
00084         index = proxyModel->index(middle, 0);
00085         indexVisualRect = visualRect(index);
00086         // We need the whole height (not only the visualRect). This will help us to update
00087         // all needed indexes correctly (ereslibre)
00088         indexVisualRect.setHeight(indexVisualRect.height() + (itemHeight - indexVisualRect.height()));
00089 
00090         if (qMax(indexVisualRect.topLeft().y(),
00091                  indexVisualRect.bottomRight().y()) < qMin(rect.topLeft().y(),
00092                                                            rect.bottomRight().y()))
00093         {
00094             bottom = middle + 1;
00095         }
00096         else
00097         {
00098             top = middle - 1;
00099         }
00100     }
00101 
00102     for (int i = middle; i < proxyModel->rowCount(); i++)
00103     {
00104         index = proxyModel->index(i, 0);
00105         indexVisualRect = visualRect(index);
00106 
00107         if (rect.intersects(indexVisualRect))
00108             intersectedIndexes.append(index);
00109 
00110         // If we passed next item, stop searching for hits
00111         if (qMax(rect.bottomRight().y(), rect.topLeft().y()) <
00112                                                   qMin(indexVisualRect.topLeft().y(),
00113                                                        indexVisualRect.bottomRight().y()))
00114             break;
00115     }
00116 
00117     return intersectedIndexes;
00118 }
00119 
00120 QRect KCategorizedView::Private::visualRectInViewport(const QModelIndex &index) const
00121 {
00122     if (!index.isValid())
00123         return QRect();
00124 
00125     QString curCategory = elementsInfo[index.row()].category;
00126 
00127     QRect retRect;
00128     const bool leftToRightFlow = (listView->flow() == QListView::LeftToRight);
00129     
00130     if (leftToRightFlow)
00131     {
00132         if (listView->layoutDirection() == Qt::LeftToRight)
00133         {
00134             retRect = QRect(listView->spacing(), listView->spacing() * 2 +
00135                             categoryDrawer->categoryHeight(index, listView->viewOptions()), 0, 0);
00136         }
00137         else
00138         {
00139             retRect = QRect(listView->viewport()->width() - listView->spacing(), listView->spacing() * 2 +
00140                             categoryDrawer->categoryHeight(index, listView->viewOptions()), 0, 0);
00141         }
00142     }
00143     else
00144     {
00145         retRect = QRect(listView->spacing(), listView->spacing() * 2 +
00146                         categoryDrawer->categoryHeight(index, listView->viewOptions()), 0, 0);
00147     }
00148 
00149     int viewportWidth = listView->viewport()->width() - listView->spacing();
00150 
00151     int itemHeight;
00152     int itemWidth;
00153 
00154     if (listView->gridSize().isEmpty() && leftToRightFlow)
00155     {
00156         itemHeight = biggestItemSize.height();
00157         itemWidth = biggestItemSize.width();
00158     }
00159     else if (leftToRightFlow)
00160     {
00161         itemHeight = listView->gridSize().height();
00162         itemWidth = listView->gridSize().width();
00163     }
00164     else if (listView->gridSize().isEmpty() && !leftToRightFlow)
00165     {
00166         itemHeight = biggestItemSize.height();
00167         itemWidth = listView->viewport()->width() - listView->spacing() * 2;
00168     }
00169     else
00170     {
00171         itemHeight = listView->gridSize().height();
00172         itemWidth = listView->gridSize().width() - listView->spacing() * 2;
00173     }
00174 
00175     int itemWidthPlusSeparation = listView->spacing() + itemWidth;
00176     if (!itemWidthPlusSeparation)
00177         itemWidthPlusSeparation++;
00178     int elementsPerRow = viewportWidth / itemWidthPlusSeparation;
00179     if (!elementsPerRow)
00180         elementsPerRow++;
00181 
00182     int column;
00183     int row;
00184 
00185     if (leftToRightFlow)
00186     {
00187         column = elementsInfo[index.row()].relativeOffsetToCategory % elementsPerRow;
00188         row = elementsInfo[index.row()].relativeOffsetToCategory / elementsPerRow;
00189 
00190         if (listView->layoutDirection() == Qt::LeftToRight)
00191         {
00192             retRect.setLeft(retRect.left() + column * listView->spacing() +
00193                             column * itemWidth);
00194         }
00195         else
00196         {
00197             retRect.setLeft(retRect.right() - column * listView->spacing() -
00198                             column * itemWidth - itemWidth);
00199 
00200             retRect.setRight(retRect.right() - column * listView->spacing() -
00201                             column * itemWidth);
00202         }
00203     }
00204     else
00205     {
00206         elementsPerRow = 1;
00207         column = elementsInfo[index.row()].relativeOffsetToCategory % elementsPerRow;
00208         row = elementsInfo[index.row()].relativeOffsetToCategory / elementsPerRow;
00209     }
00210 
00211     foreach (const QString &category, categories)
00212     {
00213         if (category == curCategory)
00214             break;
00215 
00216         float rows = (float) ((float) categoriesIndexes[category].count() /
00217                               (float) elementsPerRow);
00218 
00219         int rowsInt = categoriesIndexes[category].count() / elementsPerRow;
00220 
00221         if (rows - trunc(rows)) rowsInt++;
00222 
00223         retRect.setTop(retRect.top() +
00224                        (rowsInt * itemHeight) +
00225                        categoryDrawer->categoryHeight(index, listView->viewOptions()) +
00226                        listView->spacing() * 2);
00227 
00228         if (listView->gridSize().isEmpty())
00229         {
00230             retRect.setTop(retRect.top() +
00231                            (rowsInt * listView->spacing()));
00232         }
00233     }
00234 
00235     if (listView->gridSize().isEmpty())
00236     {
00237         retRect.setTop(retRect.top() + row * listView->spacing() +
00238                        (row * itemHeight));
00239     }
00240     else
00241     {
00242         retRect.setTop(retRect.top() + (row * itemHeight));
00243     }
00244 
00245     retRect.setWidth(itemWidth);
00246 
00247     QModelIndex heightIndex = proxyModel->index(index.row(), 0);
00248     if (listView->gridSize().isEmpty())
00249     {
00250         retRect.setHeight(listView->sizeHintForIndex(heightIndex).height());
00251     }
00252     else
00253     {
00254         const QSize sizeHint = listView->sizeHintForIndex(heightIndex);
00255         if (sizeHint.width() < itemWidth && leftToRightFlow) {
00256             retRect.setWidth(sizeHint.width());
00257             retRect.moveLeft(retRect.left() + (itemWidth - sizeHint.width()) / 2);
00258         }
00259         retRect.setHeight(qMin(sizeHint.height(), listView->gridSize().height()));
00260     }
00261 
00262     return retRect;
00263 }
00264 
00265 QRect KCategorizedView::Private::visualCategoryRectInViewport(const QString &category) const
00266 {
00267     QRect retRect(listView->spacing(),
00268                   listView->spacing(),
00269                   listView->viewport()->width() - listView->spacing() * 2,
00270                   0);
00271 
00272     if (!proxyModel || !categoryDrawer || !proxyModel->isCategorizedModel() || !proxyModel->rowCount() || !categories.contains(category))
00273         return QRect();
00274 
00275     QModelIndex index = proxyModel->index(0, 0, QModelIndex());
00276 
00277     int viewportWidth = listView->viewport()->width() - listView->spacing();
00278 
00279     int itemHeight;
00280     int itemWidth;
00281 
00282     if (listView->gridSize().isEmpty())
00283     {
00284         itemHeight = biggestItemSize.height();
00285         itemWidth = biggestItemSize.width();
00286     }
00287     else
00288     {
00289         itemHeight = listView->gridSize().height();
00290         itemWidth = listView->gridSize().width();
00291     }
00292 
00293     int itemWidthPlusSeparation = listView->spacing() + itemWidth;
00294     int elementsPerRow = viewportWidth / itemWidthPlusSeparation;
00295 
00296     if (!elementsPerRow)
00297         elementsPerRow++;
00298 
00299     if (listView->flow() == QListView::TopToBottom)
00300     {
00301         elementsPerRow = 1;
00302     }
00303 
00304     foreach (const QString &itCategory, categories)
00305     {
00306         if (itCategory == category)
00307             break;
00308 
00309         float rows = (float) ((float) categoriesIndexes[itCategory].count() /
00310                               (float) elementsPerRow);
00311         int rowsInt = categoriesIndexes[itCategory].count() / elementsPerRow;
00312 
00313         if (rows - trunc(rows)) rowsInt++;
00314 
00315         retRect.setTop(retRect.top() +
00316                        (rowsInt * itemHeight) +
00317                        categoryDrawer->categoryHeight(index, listView->viewOptions()) +
00318                        listView->spacing() * 2);
00319 
00320         if (listView->gridSize().isEmpty())
00321         {
00322             retRect.setTop(retRect.top() +
00323                            (rowsInt * listView->spacing()));
00324         }
00325     }
00326 
00327     retRect.setHeight(categoryDrawer->categoryHeight(index, listView->viewOptions()));
00328 
00329     return retRect;
00330 }
00331 
00332 // We're sure elementsPosition doesn't contain index
00333 const QRect &KCategorizedView::Private::cacheIndex(const QModelIndex &index)
00334 {
00335     QRect rect = visualRectInViewport(index);
00336     QHash<int, QRect>::iterator it = elementsPosition.insert(index.row(), rect);
00337 
00338     return *it;
00339 }
00340 
00341 // We're sure categoriesPosition doesn't contain category
00342 const QRect &KCategorizedView::Private::cacheCategory(const QString &category)
00343 {
00344     QRect rect = visualCategoryRectInViewport(category);
00345     QHash<QString, QRect>::iterator it = categoriesPosition.insert(category, rect);
00346 
00347     return *it;
00348 }
00349 
00350 const QRect &KCategorizedView::Private::cachedRectIndex(const QModelIndex &index)
00351 {
00352     QHash<int, QRect>::const_iterator it = elementsPosition.constFind(index.row());
00353     if (it != elementsPosition.constEnd()) // If we have it cached
00354     {                                        // return it
00355         return *it;
00356     }
00357     else                                     // Otherwise, cache it
00358     {                                        // and return it
00359         return cacheIndex(index);
00360     }
00361 }
00362 
00363 const QRect &KCategorizedView::Private::cachedRectCategory(const QString &category)
00364 {
00365     QHash<QString, QRect>::const_iterator it = categoriesPosition.constFind(category);
00366     if (it != categoriesPosition.constEnd()) // If we have it cached
00367     {                                                // return it
00368         return *it;
00369     }
00370     else                                            // Otherwise, cache it and
00371     {                                               // return it
00372         return cacheCategory(category);
00373     }
00374 }
00375 
00376 QRect KCategorizedView::Private::visualRect(const QModelIndex &index)
00377 {
00378     QRect retRect = cachedRectIndex(index);
00379     int dx = -listView->horizontalOffset();
00380     int dy = -listView->verticalOffset();
00381     retRect.adjust(dx, dy, dx, dy);
00382 
00383     return retRect;
00384 }
00385 
00386 QRect KCategorizedView::Private::categoryVisualRect(const QString &category)
00387 {
00388     QRect retRect = cachedRectCategory(category);
00389     int dx = -listView->horizontalOffset();
00390     int dy = -listView->verticalOffset();
00391     retRect.adjust(dx, dy, dx, dy);
00392 
00393     return retRect;
00394 }
00395 
00396 void KCategorizedView::Private::drawNewCategory(const QModelIndex &index,
00397                                                 int sortRole,
00398                                                 const QStyleOption &option,
00399                                                 QPainter *painter)
00400 {
00401     if (!index.isValid())
00402     {
00403         return;
00404     }
00405 
00406     QStyleOption optionCopy = option;
00407     const QString category = proxyModel->data(index, KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString();
00408 
00409     optionCopy.state &= ~QStyle::State_Selected;
00410 
00411     if ((listView->selectionMode() != SingleSelection) && (listView->selectionMode() != NoSelection)) {
00412         if ((category == hoveredCategory) && !mouseButtonPressed)
00413         {
00414             optionCopy.state |= QStyle::State_MouseOver;
00415         }
00416         else if ((category == hoveredCategory) && mouseButtonPressed)
00417         {
00418             QPoint initialPressPosition = listView->viewport()->mapFromGlobal(QCursor::pos());
00419             initialPressPosition.setY(initialPressPosition.y() + listView->verticalOffset());
00420             initialPressPosition.setX(initialPressPosition.x() + listView->horizontalOffset());
00421 
00422             if (initialPressPosition == this->initialPressPosition)
00423             {
00424                 optionCopy.state |= QStyle::State_Selected;
00425             }
00426         }
00427     }
00428 
00429     categoryDrawer->drawCategory(index,
00430                                  sortRole,
00431                                  optionCopy,
00432                                  painter);
00433 }
00434 
00435 
00436 void KCategorizedView::Private::updateScrollbars()
00437 {
00438     // find the last index in the last category
00439     QModelIndex lastIndex = categoriesIndexes.isEmpty() ? QModelIndex() : categoriesIndexes[categories.last()].last();
00440 
00441     int lastItemBottom = cachedRectIndex(lastIndex).top() +
00442                          listView->spacing() + (listView->gridSize().isEmpty() ? biggestItemSize.height() : listView->gridSize().height()) - listView->viewport()->height();
00443 
00444     listView->horizontalScrollBar()->setRange(0, 0);
00445 
00446     if (listView->verticalScrollMode() == QAbstractItemView::ScrollPerItem)
00447     {
00448         listView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
00449     }
00450 
00451     if (listView->horizontalScrollMode() == QAbstractItemView::ScrollPerItem)
00452     {
00453         listView->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
00454     }
00455 
00456     listView->verticalScrollBar()->setSingleStep(listView->viewport()->height() / 10);
00457     listView->verticalScrollBar()->setPageStep(listView->viewport()->height());
00458     listView->verticalScrollBar()->setRange(0, lastItemBottom);
00459 }
00460 
00461 void KCategorizedView::Private::drawDraggedItems(QPainter *painter)
00462 {
00463     QStyleOptionViewItemV4 option = listView->viewOptions();
00464     option.state &= ~QStyle::State_MouseOver;
00465     foreach (const QModelIndex &index, listView->selectionModel()->selectedIndexes())
00466     {
00467         const int dx = mousePosition.x() - initialPressPosition.x() + listView->horizontalOffset();
00468         const int dy = mousePosition.y() - initialPressPosition.y() + listView->verticalOffset();
00469 
00470         option.rect = visualRect(index);
00471         option.rect.adjust(dx, dy, dx, dy);
00472 
00473         if (option.rect.intersects(listView->viewport()->rect()))
00474         {
00475             listView->itemDelegate(index)->paint(painter, option, index);
00476         }
00477     }
00478 }
00479 
00480 void KCategorizedView::Private::layoutChanged(bool forceItemReload)
00481 {
00482     if (proxyModel && categoryDrawer && proxyModel->isCategorizedModel() &&
00483         ((forceItemReload ||
00484           (modelSortRole != proxyModel->sortRole()) ||
00485           (modelSortColumn != proxyModel->sortColumn()) ||
00486           (modelSortOrder != proxyModel->sortOrder()) ||
00487           (modelLastRowCount != proxyModel->rowCount()) ||
00488           (modelCategorized != proxyModel->isCategorizedModel()))))
00489     {
00490         // Force the view to update all elements
00491         listView->rowsInsertedArtifficial(QModelIndex(), 0, proxyModel->rowCount() - 1);
00492 
00493         if (!forceItemReload)
00494         {
00495             modelSortRole = proxyModel->sortRole();
00496             modelSortColumn = proxyModel->sortColumn();
00497             modelSortOrder = proxyModel->sortOrder();
00498             modelLastRowCount = proxyModel->rowCount();
00499             modelCategorized = proxyModel->isCategorizedModel();
00500         }
00501     }
00502 
00503     if (proxyModel && categoryDrawer && proxyModel->isCategorizedModel())
00504     {
00505         updateScrollbars();
00506     }
00507 }
00508 
00509 void KCategorizedView::Private::drawDraggedItems()
00510 {
00511     QRect rectToUpdate;
00512     QRect currentRect;
00513     foreach (const QModelIndex &index, listView->selectionModel()->selectedIndexes())
00514     {
00515         int dx = mousePosition.x() - initialPressPosition.x() + listView->horizontalOffset();
00516         int dy = mousePosition.y() - initialPressPosition.y() + listView->verticalOffset();
00517 
00518         currentRect = visualRect(index);
00519         currentRect.adjust(dx, dy, dx, dy);
00520 
00521         if (currentRect.intersects(listView->viewport()->rect()))
00522         {
00523             rectToUpdate = rectToUpdate.united(currentRect);
00524         }
00525     }
00526 
00527     listView->viewport()->update(lastDraggedItemsRect.united(rectToUpdate));
00528 
00529     lastDraggedItemsRect = rectToUpdate;
00530 }
00531 
00532 
00533 //==============================================================================
00534 
00535 
00536 KCategorizedView::KCategorizedView(QWidget *parent)
00537     : QListView(parent)
00538     , d(new Private(this))
00539 {
00540 }
00541 
00542 KCategorizedView::~KCategorizedView()
00543 {
00544     delete d;
00545 }
00546 
00547 void KCategorizedView::setGridSize(const QSize &size)
00548 {
00549     QListView::setGridSize(size);
00550 
00551     d->layoutChanged(true);
00552 }
00553 
00554 void KCategorizedView::setModel(QAbstractItemModel *model)
00555 {
00556     d->lastSelection = QItemSelection();
00557     d->forcedSelectionPosition = 0;
00558     d->elementsInfo.clear();
00559     d->elementsPosition.clear();
00560     d->categoriesIndexes.clear();
00561     d->categoriesPosition.clear();
00562     d->categories.clear();
00563     d->intersectedIndexes.clear();
00564     d->modelIndexList.clear();
00565     d->hovered = QModelIndex();
00566     d->mouseButtonPressed = false;
00567     d->rightMouseButtonPressed = false;
00568 
00569     if (d->proxyModel)
00570     {
00571         QObject::disconnect(d->proxyModel,
00572                             SIGNAL(layoutChanged()),
00573                             this, SLOT(slotLayoutChanged()));
00574 
00575         QObject::disconnect(d->proxyModel,
00576                             SIGNAL(rowsRemoved(QModelIndex,int,int)),
00577                             this, SLOT(rowsRemoved(QModelIndex,int,int)));
00578     }
00579 
00580     QListView::setModel(model);
00581 
00582     d->proxyModel = dynamic_cast<KCategorizedSortFilterProxyModel*>(model);
00583 
00584     if (d->proxyModel)
00585     {
00586         d->modelSortRole = d->proxyModel->sortRole();
00587         d->modelSortColumn = d->proxyModel->sortColumn();
00588         d->modelSortOrder = d->proxyModel->sortOrder();
00589         d->modelLastRowCount = d->proxyModel->rowCount();
00590         d->modelCategorized = d->proxyModel->isCategorizedModel();
00591 
00592         QObject::connect(d->proxyModel,
00593                          SIGNAL(layoutChanged()),
00594                          this, SLOT(slotLayoutChanged()));
00595 
00596         QObject::connect(d->proxyModel,
00597                          SIGNAL(rowsRemoved(QModelIndex,int,int)),
00598                          this, SLOT(rowsRemoved(QModelIndex,int,int)));
00599 
00600         if (d->proxyModel->rowCount())
00601         {
00602             d->layoutChanged(true);
00603         }
00604     }
00605     else
00606     {
00607         d->modelCategorized = false;
00608     }
00609 }
00610 
00611 QRect KCategorizedView::visualRect(const QModelIndex &index) const
00612 {
00613     if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
00614     {
00615         return QListView::visualRect(index);
00616     }
00617 
00618     if (!qobject_cast<const QSortFilterProxyModel*>(index.model()))
00619     {
00620         return d->visualRect(d->proxyModel->mapFromSource(index));
00621     }
00622 
00623     return d->visualRect(index);
00624 }
00625 
00626 KCategoryDrawer *KCategorizedView::categoryDrawer() const
00627 {
00628     return d->categoryDrawer;
00629 }
00630 
00631 void KCategorizedView::setCategoryDrawer(KCategoryDrawer *categoryDrawer)
00632 {
00633     d->lastSelection = QItemSelection();
00634     d->forcedSelectionPosition = 0;
00635     d->elementsInfo.clear();
00636     d->elementsPosition.clear();
00637     d->categoriesIndexes.clear();
00638     d->categoriesPosition.clear();
00639     d->categories.clear();
00640     d->intersectedIndexes.clear();
00641     d->modelIndexList.clear();
00642     d->hovered = QModelIndex();
00643     d->mouseButtonPressed = false;
00644     d->rightMouseButtonPressed = false;
00645 
00646     if (!categoryDrawer && d->proxyModel)
00647     {
00648         QObject::disconnect(d->proxyModel,
00649                             SIGNAL(layoutChanged()),
00650                             this, SLOT(slotLayoutChanged()));
00651 
00652         QObject::disconnect(d->proxyModel,
00653                             SIGNAL(rowsRemoved(QModelIndex,int,int)),
00654                             this, SLOT(rowsRemoved(QModelIndex,int,int)));
00655     }
00656     else if (categoryDrawer && d->proxyModel)
00657     {
00658         QObject::connect(d->proxyModel,
00659                          SIGNAL(layoutChanged()),
00660                          this, SLOT(slotLayoutChanged()));
00661 
00662         QObject::connect(d->proxyModel,
00663                          SIGNAL(rowsRemoved(QModelIndex,int,int)),
00664                          this, SLOT(rowsRemoved(QModelIndex,int,int)));
00665     }
00666 
00667     d->categoryDrawer = categoryDrawer;
00668 
00669     if (categoryDrawer)
00670     {
00671         if (d->proxyModel)
00672         {
00673             if (d->proxyModel->rowCount())
00674             {
00675                 d->layoutChanged(true);
00676             }
00677         }
00678     }
00679     else
00680     {
00681         updateGeometries();
00682     }
00683 }
00684 
00685 QModelIndex KCategorizedView::indexAt(const QPoint &point) const
00686 {
00687     if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
00688     {
00689         return QListView::indexAt(point);
00690     }
00691 
00692     QModelIndex index;
00693 
00694     const QModelIndexList item = d->intersectionSet(QRect(point, point));
00695 
00696     if (item.count() == 1)
00697     {
00698         index = item[0];
00699     }
00700 
00701     return index;
00702 }
00703 
00704 void KCategorizedView::reset()
00705 {
00706     QListView::reset();
00707 
00708     d->lastSelection = QItemSelection();
00709     d->forcedSelectionPosition = 0;
00710     d->elementsInfo.clear();
00711     d->elementsPosition.clear();
00712     d->categoriesIndexes.clear();
00713     d->categoriesPosition.clear();
00714     d->categories.clear();
00715     d->intersectedIndexes.clear();
00716     d->modelIndexList.clear();
00717     d->hovered = QModelIndex();
00718     d->biggestItemSize = QSize(0, 0);
00719     d->mouseButtonPressed = false;
00720     d->rightMouseButtonPressed = false;
00721 }
00722 
00723 void KCategorizedView::paintEvent(QPaintEvent *event)
00724 {
00725     if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
00726     {
00727         QListView::paintEvent(event);
00728         return;
00729     }
00730 
00731     bool alternatingRows = alternatingRowColors();
00732 
00733     QStyleOptionViewItemV4 option = viewOptions();
00734     option.widget = this;
00735     if (wordWrap())
00736     {
00737         option.features |= QStyleOptionViewItemV4::WrapText;
00738     }
00739 
00740     QPainter painter(viewport());
00741     QRect area = event->rect();
00742     const bool focus = (hasFocus() || viewport()->hasFocus()) &&
00743                         currentIndex().isValid();
00744     const QStyle::State state = option.state;
00745     const bool enabled = (state & QStyle::State_Enabled) != 0;
00746 
00747     painter.save();
00748 
00749     QModelIndexList dirtyIndexes = d->intersectionSet(area);
00750     bool alternate = false;
00751     if (dirtyIndexes.count())
00752     {
00753         alternate = dirtyIndexes[0].row() % 2;
00754     }
00755     foreach (const QModelIndex &index, dirtyIndexes)
00756     {
00757         if (alternatingRows && alternate)
00758         {
00759             option.features |= QStyleOptionViewItemV4::Alternate;
00760             alternate = false;
00761         }
00762         else if (alternatingRows)
00763         {
00764             option.features &= ~QStyleOptionViewItemV4::Alternate;
00765             alternate = true;
00766         }
00767         option.state = state;
00768         option.rect = visualRect(index);
00769 
00770         if (selectionModel() && selectionModel()->isSelected(index))
00771         {
00772             option.state |= QStyle::State_Selected;
00773         }
00774 
00775         if (enabled)
00776         {
00777             QPalette::ColorGroup cg;
00778             if ((d->proxyModel->flags(index) & Qt::ItemIsEnabled) == 0)
00779             {
00780                 option.state &= ~QStyle::State_Enabled;
00781                 cg = QPalette::Disabled;
00782             }
00783             else
00784             {
00785                 cg = QPalette::Normal;
00786             }
00787             option.palette.setCurrentColorGroup(cg);
00788         }
00789 
00790         if (focus && currentIndex() == index)
00791         {
00792             option.state |= QStyle::State_HasFocus;
00793             if (this->state() == EditingState)
00794                 option.state |= QStyle::State_Editing;
00795         }
00796 
00797         if (index == d->hovered)
00798             option.state |= QStyle::State_MouseOver;
00799         else
00800             option.state &= ~QStyle::State_MouseOver;
00801 
00802         itemDelegate(index)->paint(&painter, option, index);
00803     }
00804 
00805     // Redraw categories
00806     QStyleOptionViewItemV4 otherOption;
00807     bool intersectedInThePast = false;
00808     foreach (const QString &category, d->categories)
00809     {
00810         otherOption = option;
00811         otherOption.rect = d->categoryVisualRect(category);
00812         otherOption.state &= ~QStyle::State_MouseOver;
00813 
00814         if (otherOption.rect.intersects(area))
00815         {
00816             intersectedInThePast = true;
00817 
00818             QModelIndex indexToDraw = d->proxyModel->index(d->categoriesIndexes[category][0].row(), d->proxyModel->sortColumn());
00819 
00820             d->drawNewCategory(indexToDraw,
00821                                d->proxyModel->sortRole(), otherOption, &painter);
00822         }
00823         else if (intersectedInThePast)
00824         {
00825             break; // the visible area has been finished, we don't need to keep asking, the rest won't intersect
00826                 // this is doable because we know that categories are correctly ordered on the list
00827         }
00828     }
00829 
00830     if ((selectionMode() != SingleSelection) && (selectionMode() != NoSelection))
00831     {
00832         if (d->mouseButtonPressed && !d->isDragging)
00833         {
00834             QPoint start, end, initialPressPosition;
00835 
00836             initialPressPosition = d->initialPressPosition;
00837 
00838             initialPressPosition.setY(initialPressPosition.y() - verticalOffset());
00839             initialPressPosition.setX(initialPressPosition.x() - horizontalOffset());
00840 
00841             if (d->initialPressPosition.x() > d->mousePosition.x() ||
00842                 d->initialPressPosition.y() > d->mousePosition.y())
00843             {
00844                 start = d->mousePosition;
00845                 end = initialPressPosition;
00846             }
00847             else
00848             {
00849                 start = initialPressPosition;
00850                 end = d->mousePosition;
00851             }
00852 
00853             QStyleOptionRubberBand yetAnotherOption;
00854             yetAnotherOption.initFrom(this);
00855             yetAnotherOption.shape = QRubberBand::Rectangle;
00856             yetAnotherOption.opaque = false;
00857             yetAnotherOption.rect = QRect(start, end).intersected(viewport()->rect().adjusted(-16, -16, 16, 16));
00858             painter.save();
00859             style()->drawControl(QStyle::CE_RubberBand, &yetAnotherOption, &painter);
00860             painter.restore();
00861         }
00862     }
00863 
00864     if (d->isDragging && !d->dragLeftViewport)
00865     {
00866         painter.setOpacity(0.5);
00867         d->drawDraggedItems(&painter);
00868     }
00869 
00870     painter.restore();
00871 }
00872 
00873 void KCategorizedView::resizeEvent(QResizeEvent *event)
00874 {
00875     QListView::resizeEvent(event);
00876 
00877     // Clear the items positions cache
00878     d->elementsPosition.clear();
00879     d->categoriesPosition.clear();
00880     d->forcedSelectionPosition = 0;
00881 
00882     if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
00883     {
00884         return;
00885     }
00886 
00887     d->updateScrollbars();
00888 }
00889 
00890 void KCategorizedView::setSelection(const QRect &rect,
00891                                     QItemSelectionModel::SelectionFlags flags)
00892 {
00893     if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
00894     {
00895         QListView::setSelection(rect, flags);
00896         return;
00897     }
00898 
00899     if (!flags)
00900         return;
00901 
00902     if (flags & QItemSelectionModel::Clear)
00903     {
00904         selectionModel()->clear();
00905         d->lastSelection.clear();
00906     }
00907 
00908     QModelIndexList dirtyIndexes = d->intersectionSet(rect);
00909 
00910     // no items affected, just leave
00911     if (!dirtyIndexes.count())
00912     {
00913         selectionModel()->select(d->lastSelection, QItemSelectionModel::SelectCurrent);
00914 
00915         return;
00916     }
00917 
00918     QModelIndex topLeft;
00919     QModelIndex bottomRight;
00920 
00921     if (d->mouseButtonPressed || d->rightMouseButtonPressed) // selection with click + drag
00922     {
00923         QItemSelection selection;
00924 
00925         QModelIndex prev = dirtyIndexes[0];
00926         QModelIndex first = prev;
00927         foreach (const QModelIndex &index, dirtyIndexes)
00928         {
00929             // we have a different interval. non-contiguous items
00930             if ((index.row() - prev.row()) > 1) {
00931                 selection << QItemSelectionRange(first, prev);
00932 
00933                 first = index;
00934             }
00935 
00936             prev = index;
00937         }
00938 
00939         selection << QItemSelectionRange(first, prev);
00940 
00941         if (flags & QItemSelectionModel::Current)
00942         {
00943             if (rect.topLeft() == rect.bottomRight())
00944             {
00945                 selectionModel()->setCurrentIndex(indexAt(rect.topLeft()), QItemSelectionModel::NoUpdate);
00946             }
00947 
00948             selection.merge(d->lastSelection, flags);
00949         }
00950         else
00951         {
00952             selection.merge(selectionModel()->selection(), flags);
00953 
00954             selectionModel()->select(selection, QItemSelectionModel::SelectCurrent);
00955 
00956             return;
00957         }
00958 
00959         selectionModel()->select(selection, flags);
00960     }
00961     else // selection with click + keyboard keys
00962     {
00963         QModelIndex topLeftIndex = indexAt(QPoint(rect.topLeft().x(),
00964                                                   rect.topLeft().y()));
00965         QModelIndex bottomRightIndex = indexAt(QPoint(rect.bottomRight().x(),
00966                                                       rect.bottomRight().y()));
00967 
00968         // keyboard selection comes "upside down". Let's normalize it
00969         if (topLeftIndex.row() > bottomRightIndex.row())
00970         {
00971             QModelIndex auxIndex = topLeftIndex;
00972             topLeftIndex = bottomRightIndex;
00973             bottomRightIndex = auxIndex;
00974         }
00975 
00976         int viewportWidth = viewport()->width() - spacing();
00977         int itemWidth;
00978 
00979         if (gridSize().isEmpty())
00980         {
00981             itemWidth = d->biggestItemSize.width();
00982         }
00983         else
00984         {
00985             itemWidth = gridSize().width();
00986         }
00987 
00988         int itemWidthPlusSeparation = spacing() + itemWidth;
00989         if (!itemWidthPlusSeparation)
00990             itemWidthPlusSeparation++;
00991         int elementsPerRow = viewportWidth / itemWidthPlusSeparation;
00992         if (!elementsPerRow)
00993             elementsPerRow++;
00994 
00995         QModelIndexList theoricDirty(dirtyIndexes);
00996         dirtyIndexes.clear();
00997         int first = model()->rowCount();
00998         int last = 0;
00999 
01000         foreach (const QModelIndex &index, theoricDirty)
01001         {
01002             if ((index.row() < first) &&
01003                 ((((topLeftIndex.row() / elementsPerRow) == (index.row() / elementsPerRow)) &&
01004                   ((topLeftIndex.row() % elementsPerRow) <= (index.row() % elementsPerRow))) ||
01005                  (topLeftIndex.row() / elementsPerRow) != (index.row() / elementsPerRow)))
01006             {
01007                 first = index.row();
01008                 topLeft = index;
01009             }
01010 
01011             if ((index.row() > last) &&
01012                 ((((bottomRightIndex.row() / elementsPerRow) == (index.row() / elementsPerRow)) &&
01013                   ((bottomRightIndex.row() % elementsPerRow) >= (index.row() % elementsPerRow))) ||
01014                  (bottomRightIndex.row() / elementsPerRow) != (index.row() / elementsPerRow)))
01015             {
01016                 last = index.row();
01017                 bottomRight = index;
01018             }
01019         }
01020 
01021         for (int i = first; i <= last; i++)
01022         {
01023             dirtyIndexes << model()->index(i, theoricDirty[0].column(), theoricDirty[0].parent());
01024         }
01025 
01026         QItemSelection selection(topLeft, bottomRight);
01027 
01028         selectionModel()->select(selection, flags);
01029     }
01030 }
01031 
01032 void KCategorizedView::mouseMoveEvent(QMouseEvent *event)
01033 {
01034     QListView::mouseMoveEvent(event);
01035 
01036     if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
01037     {
01038         return;
01039     }
01040 
01041     const QModelIndexList item = d->intersectionSet(QRect(event->pos(), event->pos()));
01042 
01043     if (item.count() == 1)
01044     {
01045         d->hovered = item[0];
01046     }
01047     else
01048     {
01049         d->hovered = QModelIndex();
01050     }
01051 
01052     const QString previousHoveredCategory = d->hoveredCategory;
01053 
01054     d->mousePosition = event->pos();
01055     d->hoveredCategory.clear();
01056 
01057     // Redraw categories
01058     foreach (const QString &category, d->categories)
01059     {
01060         if (d->categoryVisualRect(category).intersects(QRect(event->pos(), event->pos())))
01061         {
01062             d->hoveredCategory = category;
01063             viewport()->update(d->categoryVisualRect(category));
01064         }
01065         else if ((category == previousHoveredCategory) &&
01066                  (!d->categoryVisualRect(previousHoveredCategory).intersects(QRect(event->pos(), event->pos()))))
01067         {
01068             viewport()->update(d->categoryVisualRect(category));
01069         }
01070     }
01071 
01072     QRect rect;
01073     if (d->mouseButtonPressed && !d->isDragging)
01074     {
01075         QPoint start, end, initialPressPosition;
01076 
01077         initialPressPosition = d->initialPressPosition;
01078 
01079         initialPressPosition.setY(initialPressPosition.y() - verticalOffset());
01080         initialPressPosition.setX(initialPressPosition.x() - horizontalOffset());
01081 
01082         if (d->initialPressPosition.x() > d->mousePosition.x() ||
01083             d->initialPressPosition.y() > d->mousePosition.y())
01084         {
01085             start = d->mousePosition;
01086             end = initialPressPosition;
01087         }
01088         else
01089         {
01090             start = initialPressPosition;
01091             end = d->mousePosition;
01092         }
01093 
01094         rect = QRect(start, end).adjusted(-16, -16, 16, 16);
01095         rect = rect.united(QRect(start, end).adjusted(16, 16, -16, -16)).intersected(viewport()->rect());
01096 
01097         viewport()->update(rect);
01098     }
01099 }
01100 
01101 void KCategorizedView::mousePressEvent(QMouseEvent *event)
01102 {
01103     d->dragLeftViewport = false;
01104 
01105     if (event->button() == Qt::LeftButton)
01106     {
01107         d->mouseButtonPressed = true;
01108 
01109         d->initialPressPosition = event->pos();
01110         d->initialPressPosition.setY(d->initialPressPosition.y() +
01111                                                               verticalOffset());
01112         d->initialPressPosition.setX(d->initialPressPosition.x() +
01113                                                             horizontalOffset());
01114     }
01115     else if (event->button() == Qt::RightButton)
01116     {
01117         d->rightMouseButtonPressed = true;
01118     }
01119 
01120     QListView::mousePressEvent(event);
01121 
01122     if (selectionModel())
01123     {
01124         d->lastSelection = selectionModel()->selection();
01125     }
01126 
01127     viewport()->update(d->categoryVisualRect(d->hoveredCategory));
01128 }
01129 
01130 void KCategorizedView::mouseReleaseEvent(QMouseEvent *event)
01131 {
01132     d->mouseButtonPressed = false;
01133     d->rightMouseButtonPressed = false;
01134 
01135     QListView::mouseReleaseEvent(event);
01136 
01137     if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
01138     {
01139         return;
01140     }
01141 
01142     QPoint initialPressPosition = viewport()->mapFromGlobal(QCursor::pos());
01143     initialPressPosition.setY(initialPressPosition.y() + verticalOffset());
01144     initialPressPosition.setX(initialPressPosition.x() + horizontalOffset());
01145 
01146     if ((selectionMode() != SingleSelection) && (selectionMode() != NoSelection) &&
01147         (initialPressPosition == d->initialPressPosition))
01148     {
01149         foreach(const QString &category, d->categories)
01150         {
01151             if (d->categoryVisualRect(category).contains(event->pos()) &&
01152                 selectionModel())
01153             {
01154                 QItemSelection selection = selectionModel()->selection();
01155                 QModelIndexList indexList = d->categoriesIndexes[category];
01156 
01157                 foreach (const QModelIndex &index, indexList)
01158                 {
01159                     QModelIndex selectIndex = index.model()->index(index.row(), 0);
01160 
01161                     selection << QItemSelectionRange(selectIndex);
01162                 }
01163 
01164                 selectionModel()->select(selection, QItemSelectionModel::SelectCurrent);
01165 
01166                 break;
01167             }
01168         }
01169     }
01170 
01171     QRect rect;
01172     if (!d->isDragging)
01173     {
01174         QPoint start, end, initialPressPosition;
01175 
01176         initialPressPosition = d->initialPressPosition;
01177 
01178         initialPressPosition.setY(initialPressPosition.y() - verticalOffset());
01179         initialPressPosition.setX(initialPressPosition.x() - horizontalOffset());
01180 
01181         if (d->initialPressPosition.x() > d->mousePosition.x() ||
01182             d->initialPressPosition.y() > d->mousePosition.y())
01183         {
01184             start = d->mousePosition;
01185             end = initialPressPosition;
01186         }
01187         else
01188         {
01189             start = initialPressPosition;
01190             end = d->mousePosition;
01191         }
01192 
01193         rect = QRect(start, end).adjusted(-16, -16, 16, 16);
01194         rect = rect.united(QRect(start, end).adjusted(16, 16, -16, -16)).intersected(viewport()->rect());
01195 
01196         viewport()->update(rect);
01197     }
01198 
01199     if (d->hovered.isValid())
01200         viewport()->update(visualRect(d->hovered));
01201     else if (!d->hoveredCategory.isEmpty())
01202         viewport()->update(d->categoryVisualRect(d->hoveredCategory));
01203 }
01204 
01205 void KCategorizedView::leaveEvent(QEvent *event)
01206 {
01207     d->hovered = QModelIndex();
01208     d->hoveredCategory.clear();
01209 
01210     QListView::leaveEvent(event);
01211 }
01212 
01213 void KCategorizedView::startDrag(Qt::DropActions supportedActions)
01214 {
01215     // FIXME: QAbstractItemView does far better here since it sets the
01216     //        pixmap of selected icons to the dragging cursor, but it sets a non
01217     //        ARGB window so it is no transparent. Use QAbstractItemView when
01218     //        this is fixed on Qt.
01219     // QAbstractItemView::startDrag(supportedActions);
01220 #if defined(DOLPHIN_DRAGANDDROP)
01221     Q_UNUSED(supportedActions);
01222 #else
01223     QListView::startDrag(supportedActions);
01224 #endif
01225 
01226     d->isDragging = false;
01227     d->mouseButtonPressed = false;
01228     d->rightMouseButtonPressed = false;
01229 
01230     viewport()->update(d->lastDraggedItemsRect);
01231 }
01232 
01233 void KCategorizedView::dragMoveEvent(QDragMoveEvent *event)
01234 {
01235     d->mousePosition = event->pos();
01236 
01237     if (d->mouseButtonPressed)
01238     {
01239         d->isDragging = true;
01240     }
01241     else
01242     {
01243         d->isDragging = false;
01244     }
01245 
01246     d->dragLeftViewport = false;
01247 
01248 #if defined(DOLPHIN_DRAGANDDROP)
01249     QAbstractItemView::dragMoveEvent(event);
01250 #else
01251     QListView::dragMoveEvent(event);
01252 #endif
01253 
01254     if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
01255     {
01256         return;
01257     }
01258     
01259     d->hovered = indexAt(event->pos());
01260 
01261 #if !defined(DOLPHIN_DRAGANDDROP)
01262     d->drawDraggedItems();
01263 #endif
01264 }
01265 
01266 void KCategorizedView::dragLeaveEvent(QDragLeaveEvent *event)
01267 {
01268     d->dragLeftViewport = true;
01269 
01270 #if defined(DOLPHIN_DRAGANDDROP)
01271     QAbstractItemView::dragLeaveEvent(event);
01272 #else
01273     QListView::dragLeaveEvent(event);
01274 #endif
01275 }
01276 
01277 void KCategorizedView::dropEvent(QDropEvent *event)
01278 {
01279 #if defined(DOLPHIN_DRAGANDDROP)
01280     QAbstractItemView::dropEvent(event);
01281 #else
01282     QListView::dropEvent(event);
01283 #endif
01284 }
01285 
01286 QModelIndex KCategorizedView::moveCursor(CursorAction cursorAction,
01287                                          Qt::KeyboardModifiers modifiers)
01288 {
01289     if ((viewMode() != KCategorizedView::IconMode) ||
01290         !d->proxyModel ||
01291         !d->categoryDrawer ||
01292          d->categories.isEmpty() ||
01293         !d->proxyModel->isCategorizedModel())
01294     {
01295         return QListView::moveCursor(cursorAction, modifiers);
01296     }
01297 
01298     int viewportWidth = viewport()->width() - spacing();
01299     int itemWidth;
01300 
01301     if (gridSize().isEmpty())
01302     {
01303         itemWidth = d->biggestItemSize.width();
01304     }
01305     else
01306     {
01307         itemWidth = gridSize().width();
01308     }
01309 
01310     int itemWidthPlusSeparation = spacing() + itemWidth;
01311     if (!itemWidthPlusSeparation)
01312         itemWidthPlusSeparation++;
01313     int elementsPerRow = viewportWidth / itemWidthPlusSeparation;
01314     if (!elementsPerRow)
01315         elementsPerRow++;
01316 
01317     QModelIndex current = selectionModel() ? selectionModel()->currentIndex()
01318                                            : QModelIndex();
01319 
01320     if (!current.isValid())
01321     {
01322         if (cursorAction == MoveEnd)
01323         {
01324             current = model()->index(model()->rowCount() - 1, 0, QModelIndex());
01325             d->forcedSelectionPosition = d->elementsInfo[current.row()].relativeOffsetToCategory % elementsPerRow;
01326         }
01327         else
01328         {
01329             current = model()->index(0, 0, QModelIndex());
01330             d->forcedSelectionPosition = 0;
01331         }
01332 
01333         return current;
01334     }
01335 
01336     QString lastCategory = d->categories.first();
01337     QString theCategory = d->categories.first();
01338     QString afterCategory = d->categories.first();
01339 
01340     bool hasToBreak = false;
01341     foreach (const QString &category, d->categories)
01342     {
01343         if (hasToBreak)
01344         {
01345             afterCategory = category;
01346 
01347             break;
01348         }
01349 
01350         if (category == d->elementsInfo[current.row()].category)
01351         {
01352             theCategory = category;
01353 
01354             hasToBreak = true;
01355         }
01356 
01357         if (!hasToBreak)
01358         {
01359             lastCategory = category;
01360         }
01361     }
01362 
01363     switch (cursorAction)
01364     {
01365         case QAbstractItemView::MoveUp: {
01366             if (d->elementsInfo[current.row()].relativeOffsetToCategory >= elementsPerRow)
01367             {
01368                 int indexToMove = current.row();
01369                 indexToMove -= qMin(((d->elementsInfo[current.row()].relativeOffsetToCategory) + d->forcedSelectionPosition), elementsPerRow - d->forcedSelectionPosition + (d->elementsInfo[current.row()].relativeOffsetToCategory % elementsPerRow));
01370 
01371                 return d->proxyModel->index(indexToMove, 0);
01372             }
01373             else
01374             {
01375                 int lastCategoryLastRow = (d->categoriesIndexes[lastCategory].count() - 1) % elementsPerRow;
01376                 int indexToMove = current.row() - d->elementsInfo[current.row()].relativeOffsetToCategory;
01377 
01378                 if (d->forcedSelectionPosition >= lastCategoryLastRow)
01379                 {
01380                     indexToMove -= 1;
01381                 }
01382                 else
01383                 {
01384                     indexToMove -= qMin((lastCategoryLastRow - d->forcedSelectionPosition + 1), d->forcedSelectionPosition + elementsPerRow + 1);
01385                 }
01386 
01387                 return d->proxyModel->index(indexToMove, 0);
01388             }
01389         }
01390 
01391         case QAbstractItemView::MoveDown: {
01392             if (d->elementsInfo[current.row()].relativeOffsetToCategory < (d->categoriesIndexes[theCategory].count() - 1 - ((d->categoriesIndexes[theCategory].count() - 1) % elementsPerRow)))
01393             {
01394                 int indexToMove = current.row();
01395                 indexToMove += qMin(elementsPerRow, d->categoriesIndexes[theCategory].count() - 1 - d->elementsInfo[current.row()].relativeOffsetToCategory);
01396 
01397                 return d->proxyModel->index(indexToMove, 0);
01398             }
01399             else
01400             {
01401                 int afterCategoryLastRow = qMin(elementsPerRow, d->categoriesIndexes[afterCategory].count());
01402                 int indexToMove = current.row() + (d->categoriesIndexes[theCategory].count() - d->elementsInfo[current.row()].relativeOffsetToCategory);
01403 
01404                 if (d->forcedSelectionPosition >= afterCategoryLastRow)
01405                 {
01406                     indexToMove += afterCategoryLastRow - 1;
01407                 }
01408                 else
01409                 {
01410                     indexToMove += qMin(d->forcedSelectionPosition, elementsPerRow);
01411                 }
01412 
01413                 return d->proxyModel->index(indexToMove, 0);
01414             }
01415         }
01416 
01417         case QAbstractItemView::MoveLeft:
01418             if (layoutDirection() == Qt::RightToLeft)
01419             {
01420                 if (!(d->elementsInfo[current.row() + 1].relativeOffsetToCategory % elementsPerRow))
01421                     return current;
01422 
01423                 d->forcedSelectionPosition = d->elementsInfo[current.row() + 1].relativeOffsetToCategory % elementsPerRow;
01424 
01425 #if 0 //follow qt view behavior. lateral movements won't change visual row
01426                 if (d->forcedSelectionPosition < 0)
01427                     d->forcedSelectionPosition = (d->categoriesIndexes[theCategory].count() - 1) % elementsPerRow;
01428 #endif
01429 
01430                 return d->proxyModel->index(current.row() + 1, 0);
01431             }
01432 
01433             if (!(d->elementsInfo[current.row()].relativeOffsetToCategory % elementsPerRow))
01434                 return current;
01435 
01436             d->forcedSelectionPosition = d->elementsInfo[current.row() - 1].relativeOffsetToCategory % elementsPerRow;
01437 
01438 #if 0 //follow qt view behavior. lateral movements won't change visual row
01439             if (d->forcedSelectionPosition < 0)
01440                 d->forcedSelectionPosition = (d->categoriesIndexes[theCategory].count() - 1) % elementsPerRow;
01441 #endif
01442 
01443             return d->proxyModel->index(current.row() - 1, 0);
01444 
01445         case QAbstractItemView::MoveRight:
01446             if (layoutDirection() == Qt::RightToLeft)
01447             {
01448                 if (!(d->elementsInfo[current.row()].relativeOffsetToCategory % elementsPerRow))
01449                     return current;
01450 
01451                 d->forcedSelectionPosition = d->elementsInfo[current.row() - 1].relativeOffsetToCategory % elementsPerRow;
01452 
01453 #if 0 //follow qt view behavior. lateral movements won't change visual row
01454                 if (d->forcedSelectionPosition < 0)
01455                     d->forcedSelectionPosition = (d->categoriesIndexes[theCategory].count() - 1) % elementsPerRow;
01456 #endif
01457 
01458                 return d->proxyModel->index(current.row() - 1, 0);
01459             }
01460 
01461             if (!(d->elementsInfo[current.row() + 1].relativeOffsetToCategory % elementsPerRow))
01462                 return current;
01463 
01464             d->forcedSelectionPosition = d->elementsInfo[current.row() + 1].relativeOffsetToCategory % elementsPerRow;
01465 
01466 #if 0 //follow qt view behavior. lateral movements won't change visual row
01467             if (d->forcedSelectionPosition < 0)
01468                 d->forcedSelectionPosition = (d->categoriesIndexes[theCategory].count() - 1) % elementsPerRow;
01469 #endif
01470 
01471             return d->proxyModel->index(current.row() + 1, 0);
01472 
01473         default:
01474             break;
01475     }
01476 
01477     return QListView::moveCursor(cursorAction, modifiers);
01478 }
01479 
01480 void KCategorizedView::rowsInserted(const QModelIndex &parent,
01481                                     int start,
01482                                     int end)
01483 {
01484     QListView::rowsInserted(parent, start, end);
01485 
01486     if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
01487     {
01488         d->forcedSelectionPosition = 0;
01489         d->elementsInfo.clear();
01490         d->elementsPosition.clear();
01491         d->categoriesIndexes.clear();
01492         d->categoriesPosition.clear();
01493         d->categories.clear();
01494         d->intersectedIndexes.clear();
01495         d->modelIndexList.clear();
01496         d->hovered = QModelIndex();
01497         d->biggestItemSize = QSize(0, 0);
01498         d->mouseButtonPressed = false;
01499         d->rightMouseButtonPressed = false;
01500 
01501         return;
01502     }
01503 
01504     rowsInsertedArtifficial(parent, start, end);
01505 }
01506 
01507 void KCategorizedView::rowsInsertedArtifficial(const QModelIndex &parent,
01508                                                int start,
01509                                                int end)
01510 {
01511     Q_UNUSED(parent);
01512 
01513     d->forcedSelectionPosition = 0;
01514     d->elementsInfo.clear();
01515     d->elementsPosition.clear();
01516     d->categoriesIndexes.clear();
01517     d->categoriesPosition.clear();
01518     d->categories.clear();
01519     d->intersectedIndexes.clear();
01520     d->modelIndexList.clear();
01521     d->hovered = QModelIndex();
01522     d->biggestItemSize = QSize(0, 0);
01523     d->mouseButtonPressed = false;
01524     d->rightMouseButtonPressed = false;
01525 
01526     if (start > end || end < 0 || start < 0 || !d->proxyModel->rowCount())
01527     {
01528         return;
01529     }
01530 
01531     // Add all elements mapped to the source model and explore categories
01532     QString prevCategory = d->proxyModel->data(d->proxyModel->index(0, d->proxyModel->sortColumn()), KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString();
01533     QString lastCategory = prevCategory;
01534     QModelIndexList modelIndexList;
01535     struct Private::ElementInfo elementInfo;
01536     int offset = -1;
01537     for (int k = 0; k < d->proxyModel->rowCount(); ++k)
01538     {
01539         QModelIndex index = d->proxyModel->index(k, d->proxyModel->sortColumn());
01540         QModelIndex indexSize = d->proxyModel->index(k, 0);
01541 
01542         d->biggestItemSize = QSize(qMax(sizeHintForIndex(indexSize).width(),
01543                                         d->biggestItemSize.width()),
01544                                    qMax(sizeHintForIndex(indexSize).height(),
01545                                         d->biggestItemSize.height()));
01546 
01547         d->modelIndexList << index;
01548 
01549         lastCategory = d->proxyModel->data(index, KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString();
01550 
01551         elementInfo.category = lastCategory;
01552 
01553         if (prevCategory != lastCategory)
01554         {
01555             offset = 0;
01556             d->categoriesIndexes.insert(prevCategory, modelIndexList);
01557             d->categories << prevCategory;
01558             modelIndexList.clear();
01559         }
01560         else
01561         {
01562             offset++;
01563         }
01564 
01565         elementInfo.relativeOffsetToCategory = offset;
01566 
01567         modelIndexList << index;
01568         prevCategory = lastCategory;
01569 
01570         d->elementsInfo.insert(index.row(), elementInfo);
01571     }
01572 
01573     d->categoriesIndexes.insert(prevCategory, modelIndexList);
01574     d->categories << prevCategory;
01575 
01576     d->updateScrollbars();
01577 
01578     // FIXME: We need to safely save the last selection. This is on my TODO
01579     // list (ereslibre).
01580     selectionModel()->clear();
01581 }
01582 
01583 void KCategorizedView::rowsRemoved(const QModelIndex &parent,
01584                                    int start,
01585                                    int end)
01586 {
01587     Q_UNUSED(parent);
01588     Q_UNUSED(start);
01589     Q_UNUSED(end);
01590     if (d->proxyModel && d->categoryDrawer && d->proxyModel->isCategorizedModel())
01591     {
01592         // Force the view to update all elements
01593         rowsInsertedArtifficial(QModelIndex(), 0, d->proxyModel->rowCount() - 1);
01594     }
01595 }
01596 
01597 void KCategorizedView::updateGeometries()
01598 {
01599     if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
01600     {
01601         QListView::updateGeometries();
01602         return;
01603     }
01604 
01605     // Avoid QListView::updateGeometries(), since it will try to set another
01606     // range to our scroll bars, what we don't want (ereslibre)
01607     QAbstractItemView::updateGeometries();
01608 }
01609 
01610 void KCategorizedView::slotLayoutChanged()
01611 {
01612     d->layoutChanged();
01613 }
01614 
01615 void KCategorizedView::currentChanged(const QModelIndex &current,
01616                                       const QModelIndex &previous)
01617 {
01618     // We need to update the forcedSelectionPosition property in order to correctly
01619     // navigate after with keyboard using up & down keys
01620 
01621     int viewportWidth = viewport()->width() - spacing();
01622 
01623     int itemHeight;
01624     int itemWidth;
01625 
01626     if (gridSize().isEmpty())
01627     {
01628         itemHeight = d->biggestItemSize.height();
01629         itemWidth = d->biggestItemSize.width();
01630     }
01631     else
01632     {
01633         itemHeight = gridSize().height();
01634         itemWidth = gridSize().width();
01635     }
01636 
01637     int itemWidthPlusSeparation = spacing() + itemWidth;
01638     if (!itemWidthPlusSeparation)
01639         itemWidthPlusSeparation++;
01640     int elementsPerRow = viewportWidth / itemWidthPlusSeparation;
01641     if (!elementsPerRow)
01642         elementsPerRow++;
01643 
01644     if (d->mouseButtonPressed || d->rightMouseButtonPressed)
01645         d->forcedSelectionPosition = d->elementsInfo[current.row()].relativeOffsetToCategory % elementsPerRow;
01646 
01647     QListView::currentChanged(current, previous);
01648 }
01649 
01650 void KCategorizedView::dataChanged(const QModelIndex &topLeft,
01651                                    const QModelIndex &bottomRight)
01652 {
01653     if (topLeft == bottomRight)
01654     {
01655         d->cacheIndex(topLeft);
01656     }
01657     else
01658     {
01659         const int columnStart = topLeft.column();
01660         const int columnEnd = bottomRight.column();
01661         const int rowStart = topLeft.row();
01662         const int rowEnd = bottomRight.row();
01663 
01664         for (int row = rowStart; row <= rowEnd; ++row)
01665         {
01666             for (int column = columnStart; column <= columnEnd; ++column)
01667             {
01668                 d->cacheIndex(d->proxyModel->index(row, column));
01669             }
01670         }
01671     }
01672 
01673     QListView::dataChanged(topLeft, bottomRight);
01674     slotLayoutChanged();
01675 }
01676 
01677 #include "kcategorizedview.moc"

KDEUI

Skip menu "KDEUI"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal