• Skip to content
  • Skip to link menu
KDE 4.1 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 "kcategorydrawer.h"
00032 #include "kcategorizedsortfilterproxymodel.h"
00033 
00034 // By defining DOLPHIN_DRAGANDDROP the custom drag and drop implementation of
00035 // KCategorizedView is bypassed to have a consistent drag and drop look for all
00036 // views. Hopefully transparent pixmaps for drag objects will be supported in
00037 // Qt 4.4, so that this workaround can be skipped.
00038 #define DOLPHIN_DRAGANDDROP
00039 
00040 KCategorizedView::Private::Private(KCategorizedView *listView)
00041     : listView(listView)
00042     , categoryDrawer(0)
00043     , biggestItemSize(QSize(0, 0))
00044     , mouseButtonPressed(false)
00045     , rightMouseButtonPressed(false)
00046     , isDragging(false)
00047     , dragLeftViewport(false)
00048     , proxyModel(0)
00049 {
00050 }
00051 
00052 KCategorizedView::Private::~Private()
00053 {
00054 }
00055 
00056 const QModelIndexList &KCategorizedView::Private::intersectionSet(const QRect &rect)
00057 {
00058     QModelIndex index;
00059     QRect indexVisualRect;
00060 
00061     intersectedIndexes.clear();
00062 
00063     int itemHeight;
00064 
00065     if (listView->gridSize().isEmpty())
00066     {
00067         itemHeight = biggestItemSize.height();
00068     }
00069     else
00070     {
00071         itemHeight = listView->gridSize().height();
00072     }
00073 
00074     // Lets find out where we should start
00075     int top = proxyModel->rowCount() - 1;
00076     int bottom = 0;
00077     int middle = (top + bottom) / 2;
00078     while (bottom <= top)
00079     {
00080         middle = (top + bottom) / 2;
00081 
00082         index = proxyModel->index(middle, 0);
00083         indexVisualRect = visualRect(index);
00084         // We need the whole height (not only the visualRect). This will help us to update
00085         // all needed indexes correctly (ereslibre)
00086         indexVisualRect.setHeight(indexVisualRect.height() + (itemHeight - indexVisualRect.height()));
00087 
00088         if (qMax(indexVisualRect.topLeft().y(),
00089                  indexVisualRect.bottomRight().y()) < qMin(rect.topLeft().y(),
00090                                                            rect.bottomRight().y()))
00091         {
00092             bottom = middle + 1;
00093         }
00094         else
00095         {
00096             top = middle - 1;
00097         }
00098     }
00099 
00100     for (int i = middle; i < proxyModel->rowCount(); i++)
00101     {
00102         index = proxyModel->index(i, 0);
00103         indexVisualRect = visualRect(index);
00104 
00105         if (rect.intersects(indexVisualRect))
00106             intersectedIndexes.append(index);
00107 
00108         // If we passed next item, stop searching for hits
00109         if (qMax(rect.bottomRight().y(), rect.topLeft().y()) <
00110                                                   qMin(indexVisualRect.topLeft().y(),
00111                                                        indexVisualRect.bottomRight().y()))
00112             break;
00113     }
00114 
00115     return intersectedIndexes;
00116 }
00117 
00118 QRect KCategorizedView::Private::visualRectInViewport(const QModelIndex &index) const
00119 {
00120     if (!index.isValid())
00121         return QRect();
00122 
00123     QString curCategory = elementsInfo[index.row()].category;
00124 
00125     QRect retRect;
00126 
00127     if (listView->flow() == QListView::LeftToRight)
00128     {
00129         if (listView->layoutDirection() == Qt::LeftToRight)
00130         {
00131             retRect = QRect(listView->spacing(), listView->spacing() * 2 +
00132                             categoryDrawer->categoryHeight(index, listView->viewOptions()), 0, 0);
00133         }
00134         else
00135         {
00136             retRect = QRect(listView->viewport()->width() - listView->spacing(), listView->spacing() * 2 +
00137                             categoryDrawer->categoryHeight(index, listView->viewOptions()), 0, 0);
00138         }
00139     }
00140     else
00141     {
00142         retRect = QRect(listView->spacing(), listView->spacing() * 2 +
00143                         categoryDrawer->categoryHeight(index, listView->viewOptions()), 0, 0);
00144     }
00145 
00146     int viewportWidth = listView->viewport()->width() - listView->spacing();
00147 
00148     int itemHeight;
00149     int itemWidth;
00150 
00151     if (listView->gridSize().isEmpty() && (listView->flow() == QListView::LeftToRight))
00152     {
00153         itemHeight = biggestItemSize.height();
00154         itemWidth = biggestItemSize.width();
00155     }
00156     else if (listView->flow() == QListView::LeftToRight)
00157     {
00158         itemHeight = listView->gridSize().height();
00159         itemWidth = listView->gridSize().width();
00160     }
00161     else if (listView->gridSize().isEmpty() && (listView->flow() == QListView::TopToBottom))
00162     {
00163         itemHeight = biggestItemSize.height();
00164         itemWidth = listView->viewport()->width() - listView->spacing() * 2;
00165     }
00166     else
00167     {
00168         itemHeight = listView->gridSize().height();
00169         itemWidth = listView->gridSize().width() - listView->spacing() * 2;
00170     }
00171 
00172     int itemWidthPlusSeparation = listView->spacing() + itemWidth;
00173     if (!itemWidthPlusSeparation)
00174         itemWidthPlusSeparation++;
00175     int elementsPerRow = viewportWidth / itemWidthPlusSeparation;
00176     if (!elementsPerRow)
00177         elementsPerRow++;
00178 
00179     int column;
00180     int row;
00181 
00182     if (listView->flow() == QListView::LeftToRight)
00183     {
00184         column = elementsInfo[index.row()].relativeOffsetToCategory % elementsPerRow;
00185         row = elementsInfo[index.row()].relativeOffsetToCategory / elementsPerRow;
00186 
00187         if (listView->layoutDirection() == Qt::LeftToRight)
00188         {
00189             retRect.setLeft(retRect.left() + column * listView->spacing() +
00190                             column * itemWidth);
00191         }
00192         else
00193         {
00194             retRect.setLeft(retRect.right() - column * listView->spacing() -
00195                             column * itemWidth - itemWidth);
00196 
00197             retRect.setRight(retRect.right() - column * listView->spacing() -
00198                             column * itemWidth);
00199         }
00200     }
00201     else
00202     {
00203         elementsPerRow = 1;
00204         column = elementsInfo[index.row()].relativeOffsetToCategory % elementsPerRow;
00205         row = elementsInfo[index.row()].relativeOffsetToCategory / elementsPerRow;
00206     }
00207 
00208     foreach (const QString &category, categories)
00209     {
00210         if (category == curCategory)
00211             break;
00212 
00213         float rows = (float) ((float) categoriesIndexes[category].count() /
00214                               (float) elementsPerRow);
00215 
00216         int rowsInt = categoriesIndexes[category].count() / elementsPerRow;
00217 
00218         if (rows - trunc(rows)) rowsInt++;
00219 
00220         retRect.setTop(retRect.top() +
00221                        (rowsInt * itemHeight) +
00222                        categoryDrawer->categoryHeight(index, listView->viewOptions()) +
00223                        listView->spacing() * 2);
00224 
00225         if (listView->gridSize().isEmpty())
00226         {
00227             retRect.setTop(retRect.top() +
00228                            (rowsInt * listView->spacing()));
00229         }
00230     }
00231 
00232     if (listView->gridSize().isEmpty())
00233     {
00234         retRect.setTop(retRect.top() + row * listView->spacing() +
00235                        (row * itemHeight));
00236     }
00237     else
00238     {
00239         retRect.setTop(retRect.top() + (row * itemHeight));
00240     }
00241 
00242     retRect.setWidth(itemWidth);
00243 
00244     QModelIndex heightIndex = proxyModel->index(index.row(), 0);
00245     if (listView->gridSize().isEmpty())
00246     {
00247         retRect.setHeight(listView->sizeHintForIndex(heightIndex).height());
00248     }
00249     else
00250     {
00251         const QSize sizeHint = listView->sizeHintForIndex(heightIndex);
00252         if (sizeHint.width() < itemWidth) {
00253             retRect.setWidth(sizeHint.width());
00254             retRect.moveLeft(retRect.left() + (itemWidth - sizeHint.width()) / 2);
00255         }
00256         retRect.setHeight(qMin(sizeHint.height(), listView->gridSize().height()));
00257     }
00258 
00259     return retRect;
00260 }
00261 
00262 QRect KCategorizedView::Private::visualCategoryRectInViewport(const QString &category) const
00263 {
00264     QRect retRect(listView->spacing(),
00265                   listView->spacing(),
00266                   listView->viewport()->width() - listView->spacing() * 2,
00267                   0);
00268 
00269     if (!proxyModel || !categoryDrawer || !proxyModel->isCategorizedModel() || !proxyModel->rowCount() || !categories.contains(category))
00270         return QRect();
00271 
00272     QModelIndex index = proxyModel->index(0, 0, QModelIndex());
00273 
00274     int viewportWidth = listView->viewport()->width() - listView->spacing();
00275 
00276     int itemHeight;
00277     int itemWidth;
00278 
00279     if (listView->gridSize().isEmpty())
00280     {
00281         itemHeight = biggestItemSize.height();
00282         itemWidth = biggestItemSize.width();
00283     }
00284     else
00285     {
00286         itemHeight = listView->gridSize().height();
00287         itemWidth = listView->gridSize().width();
00288     }
00289 
00290     int itemWidthPlusSeparation = listView->spacing() + itemWidth;
00291     int elementsPerRow = viewportWidth / itemWidthPlusSeparation;
00292 
00293     if (!elementsPerRow)
00294         elementsPerRow++;
00295 
00296     if (listView->flow() == QListView::TopToBottom)
00297     {
00298         elementsPerRow = 1;
00299     }
00300 
00301     foreach (const QString &itCategory, categories)
00302     {
00303         if (itCategory == category)
00304             break;
00305 
00306         float rows = (float) ((float) categoriesIndexes[itCategory].count() /
00307                               (float) elementsPerRow);
00308         int rowsInt = categoriesIndexes[itCategory].count() / elementsPerRow;
00309 
00310         if (rows - trunc(rows)) rowsInt++;
00311 
00312         retRect.setTop(retRect.top() +
00313                        (rowsInt * itemHeight) +
00314                        categoryDrawer->categoryHeight(index, listView->viewOptions()) +
00315                        listView->spacing() * 2);
00316 
00317         if (listView->gridSize().isEmpty())
00318         {
00319             retRect.setTop(retRect.top() +
00320                            (rowsInt * listView->spacing()));
00321         }
00322     }
00323 
00324     retRect.setHeight(categoryDrawer->categoryHeight(index, listView->viewOptions()));
00325 
00326     return retRect;
00327 }
00328 
00329 // We're sure elementsPosition doesn't contain index
00330 const QRect &KCategorizedView::Private::cacheIndex(const QModelIndex &index)
00331 {
00332     QRect rect = visualRectInViewport(index);
00333     QHash<int, QRect>::iterator it = elementsPosition.insert(index.row(), rect);
00334 
00335     return *it;
00336 }
00337 
00338 // We're sure categoriesPosition doesn't contain category
00339 const QRect &KCategorizedView::Private::cacheCategory(const QString &category)
00340 {
00341     QRect rect = visualCategoryRectInViewport(category);
00342     QHash<QString, QRect>::iterator it = categoriesPosition.insert(category, rect);
00343 
00344     return *it;
00345 }
00346 
00347 const QRect &KCategorizedView::Private::cachedRectIndex(const QModelIndex &index)
00348 {
00349     QHash<int, QRect>::const_iterator it = elementsPosition.constFind(index.row());
00350     if (it != elementsPosition.constEnd()) // If we have it cached
00351     {                                        // return it
00352         return *it;
00353     }
00354     else                                     // Otherwise, cache it
00355     {                                        // and return it
00356         return cacheIndex(index);
00357     }
00358 }
00359 
00360 const QRect &KCategorizedView::Private::cachedRectCategory(const QString &category)
00361 {
00362     QHash<QString, QRect>::const_iterator it = categoriesPosition.constFind(category);
00363     if (it != categoriesPosition.constEnd()) // If we have it cached
00364     {                                                // return it
00365         return *it;
00366     }
00367     else                                            // Otherwise, cache it and
00368     {                                               // return it
00369         return cacheCategory(category);
00370     }
00371 }
00372 
00373 QRect KCategorizedView::Private::visualRect(const QModelIndex &index)
00374 {
00375     QRect retRect = cachedRectIndex(index);
00376     int dx = -listView->horizontalOffset();
00377     int dy = -listView->verticalOffset();
00378     retRect.adjust(dx, dy, dx, dy);
00379 
00380     return retRect;
00381 }
00382 
00383 QRect KCategorizedView::Private::categoryVisualRect(const QString &category)
00384 {
00385     QRect retRect = cachedRectCategory(category);
00386     int dx = -listView->horizontalOffset();
00387     int dy = -listView->verticalOffset();
00388     retRect.adjust(dx, dy, dx, dy);
00389 
00390     return retRect;
00391 }
00392 
00393 void KCategorizedView::Private::drawNewCategory(const QModelIndex &index,
00394                                                 int sortRole,
00395                                                 const QStyleOption &option,
00396                                                 QPainter *painter)
00397 {
00398     if (!index.isValid())
00399     {
00400         return;
00401     }
00402 
00403     QStyleOption optionCopy = option;
00404     const QString category = proxyModel->data(index, KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString();
00405 
00406     optionCopy.state &= ~QStyle::State_Selected;
00407 
00408     if ((listView->selectionMode() != SingleSelection) && (listView->selectionMode() != NoSelection)) {
00409         if ((category == hoveredCategory) && !mouseButtonPressed)
00410         {
00411             optionCopy.state |= QStyle::State_MouseOver;
00412         }
00413         else if ((category == hoveredCategory) && mouseButtonPressed)
00414         {
00415             QPoint initialPressPosition = listView->viewport()->mapFromGlobal(QCursor::pos());
00416             initialPressPosition.setY(initialPressPosition.y() + listView->verticalOffset());
00417             initialPressPosition.setX(initialPressPosition.x() + listView->horizontalOffset());
00418 
00419             if (initialPressPosition == this->initialPressPosition)
00420             {
00421                 optionCopy.state |= QStyle::State_Selected;
00422             }
00423         }
00424     }
00425 
00426     categoryDrawer->drawCategory(index,
00427                                  sortRole,
00428                                  optionCopy,
00429                                  painter);
00430 }
00431 
00432 
00433 void KCategorizedView::Private::updateScrollbars()
00434 {
00435     // find the last index in the last category
00436     QModelIndex lastIndex = categoriesIndexes.isEmpty() ? QModelIndex() : categoriesIndexes[categories.last()].last();
00437 
00438     int lastItemBottom = cachedRectIndex(lastIndex).top() +
00439                          listView->spacing() + (listView->gridSize().isEmpty() ? biggestItemSize.height() : listView->gridSize().height()) - listView->viewport()->height();
00440 
00441     listView->horizontalScrollBar()->setRange(0, 0);
00442 
00443     if (listView->verticalScrollMode() == QAbstractItemView::ScrollPerItem)
00444     {
00445         listView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
00446     }
00447 
00448     if (listView->horizontalScrollMode() == QAbstractItemView::ScrollPerItem)
00449     {
00450         listView->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
00451     }
00452 
00453     listView->verticalScrollBar()->setSingleStep(listView->viewport()->height() / 10);
00454     listView->verticalScrollBar()->setPageStep(listView->viewport()->height());
00455     listView->verticalScrollBar()->setRange(0, lastItemBottom);
00456 }
00457 
00458 void KCategorizedView::Private::drawDraggedItems(QPainter *painter)
00459 {
00460     QStyleOptionViewItemV4 option = listView->viewOptions();
00461     option.state &= ~QStyle::State_MouseOver;
00462     foreach (const QModelIndex &index, listView->selectionModel()->selectedIndexes())
00463     {
00464         const int dx = mousePosition.x() - initialPressPosition.x() + listView->horizontalOffset();
00465         const int dy = mousePosition.y() - initialPressPosition.y() + listView->verticalOffset();
00466 
00467         option.rect = visualRect(index);
00468         option.rect.adjust(dx, dy, dx, dy);
00469 
00470         if (option.rect.intersects(listView->viewport()->rect()))
00471         {
00472             listView->itemDelegate(index)->paint(painter, option, index);
00473         }
00474     }
00475 }
00476 
00477 void KCategorizedView::Private::layoutChanged(bool forceItemReload)
00478 {
00479     if (proxyModel && categoryDrawer && proxyModel->isCategorizedModel() &&
00480         ((forceItemReload ||
00481           (modelSortRole != proxyModel->sortRole()) ||
00482           (modelSortColumn != proxyModel->sortColumn()) ||
00483           (modelSortOrder != proxyModel->sortOrder()) ||
00484           (modelLastRowCount != proxyModel->rowCount()) ||
00485           (modelCategorized != proxyModel->isCategorizedModel()))))
00486     {
00487         // Force the view to update all elements
00488         listView->rowsInsertedArtifficial(QModelIndex(), 0, proxyModel->rowCount() - 1);
00489 
00490         if (!forceItemReload)
00491         {
00492             modelSortRole = proxyModel->sortRole();
00493             modelSortColumn = proxyModel->sortColumn();
00494             modelSortOrder = proxyModel->sortOrder();
00495             modelLastRowCount = proxyModel->rowCount();
00496             modelCategorized = proxyModel->isCategorizedModel();
00497         }
00498     }
00499 
00500     if (proxyModel && categoryDrawer && proxyModel->isCategorizedModel())
00501     {
00502         updateScrollbars();
00503     }
00504 }
00505 
00506 void KCategorizedView::Private::drawDraggedItems()
00507 {
00508     QRect rectToUpdate;
00509     QRect currentRect;
00510     foreach (const QModelIndex &index, listView->selectionModel()->selectedIndexes())
00511     {
00512         int dx = mousePosition.x() - initialPressPosition.x() + listView->horizontalOffset();
00513         int dy = mousePosition.y() - initialPressPosition.y() + listView->verticalOffset();
00514 
00515         currentRect = visualRect(index);
00516         currentRect.adjust(dx, dy, dx, dy);
00517 
00518         if (currentRect.intersects(listView->viewport()->rect()))
00519         {
00520             rectToUpdate = rectToUpdate.united(currentRect);
00521         }
00522     }
00523 
00524     listView->viewport()->update(lastDraggedItemsRect.united(rectToUpdate));
00525 
00526     lastDraggedItemsRect = rectToUpdate;
00527 }
00528 
00529 
00530 //==============================================================================
00531 
00532 
00533 KCategorizedView::KCategorizedView(QWidget *parent)
00534     : QListView(parent)
00535     , d(new Private(this))
00536 {
00537 }
00538 
00539 KCategorizedView::~KCategorizedView()
00540 {
00541     delete d;
00542 }
00543 
00544 void KCategorizedView::setGridSize(const QSize &size)
00545 {
00546     QListView::setGridSize(size);
00547 
00548     d->layoutChanged(true);
00549 }
00550 
00551 void KCategorizedView::setModel(QAbstractItemModel *model)
00552 {
00553     d->lastSelection = QItemSelection();
00554     d->forcedSelectionPosition = 0;
00555     d->elementsInfo.clear();
00556     d->elementsPosition.clear();
00557     d->categoriesIndexes.clear();
00558     d->categoriesPosition.clear();
00559     d->categories.clear();
00560     d->intersectedIndexes.clear();
00561     d->modelIndexList.clear();
00562     d->hovered = QModelIndex();
00563     d->mouseButtonPressed = false;
00564     d->rightMouseButtonPressed = false;
00565 
00566     if (d->proxyModel)
00567     {
00568         QObject::disconnect(d->proxyModel,
00569                             SIGNAL(layoutChanged()),
00570                             this, SLOT(slotLayoutChanged()));
00571 
00572         QObject::disconnect(d->proxyModel,
00573                             SIGNAL(rowsRemoved(QModelIndex,int,int)),
00574                             this, SLOT(rowsRemoved(QModelIndex,int,int)));
00575     }
00576 
00577     QListView::setModel(model);
00578 
00579     d->proxyModel = dynamic_cast<KCategorizedSortFilterProxyModel*>(model);
00580 
00581     if (d->proxyModel)
00582     {
00583         d->modelSortRole = d->proxyModel->sortRole();
00584         d->modelSortColumn = d->proxyModel->sortColumn();
00585         d->modelSortOrder = d->proxyModel->sortOrder();
00586         d->modelLastRowCount = d->proxyModel->rowCount();
00587         d->modelCategorized = d->proxyModel->isCategorizedModel();
00588 
00589         QObject::connect(d->proxyModel,
00590                          SIGNAL(layoutChanged()),
00591                          this, SLOT(slotLayoutChanged()));
00592 
00593         QObject::connect(d->proxyModel,
00594                          SIGNAL(rowsRemoved(QModelIndex,int,int)),
00595                          this, SLOT(rowsRemoved(QModelIndex,int,int)));
00596 
00597         if (d->proxyModel->rowCount())
00598         {
00599             d->layoutChanged(true);
00600         }
00601     }
00602     else
00603     {
00604         d->modelCategorized = false;
00605     }
00606 }
00607 
00608 QRect KCategorizedView::visualRect(const QModelIndex &index) const
00609 {
00610     if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
00611     {
00612         return QListView::visualRect(index);
00613     }
00614 
00615     if (!qobject_cast<const QSortFilterProxyModel*>(index.model()))
00616     {
00617         return d->visualRect(d->proxyModel->mapFromSource(index));
00618     }
00619 
00620     return d->visualRect(index);
00621 }
00622 
00623 KCategoryDrawer *KCategorizedView::categoryDrawer() const
00624 {
00625     return d->categoryDrawer;
00626 }
00627 
00628 void KCategorizedView::setCategoryDrawer(KCategoryDrawer *categoryDrawer)
00629 {
00630     d->lastSelection = QItemSelection();
00631     d->forcedSelectionPosition = 0;
00632     d->elementsInfo.clear();
00633     d->elementsPosition.clear();
00634     d->categoriesIndexes.clear();
00635     d->categoriesPosition.clear();
00636     d->categories.clear();
00637     d->intersectedIndexes.clear();
00638     d->modelIndexList.clear();
00639     d->hovered = QModelIndex();
00640     d->mouseButtonPressed = false;
00641     d->rightMouseButtonPressed = false;
00642 
00643     if (!categoryDrawer && d->proxyModel)
00644     {
00645         QObject::disconnect(d->proxyModel,
00646                             SIGNAL(layoutChanged()),
00647                             this, SLOT(slotLayoutChanged()));
00648 
00649         QObject::disconnect(d->proxyModel,
00650                             SIGNAL(rowsRemoved(QModelIndex,int,int)),
00651                             this, SLOT(rowsRemoved(QModelIndex,int,int)));
00652     }
00653     else if (categoryDrawer && d->proxyModel)
00654     {
00655         QObject::connect(d->proxyModel,
00656                          SIGNAL(layoutChanged()),
00657                          this, SLOT(slotLayoutChanged()));
00658 
00659         QObject::connect(d->proxyModel,
00660                          SIGNAL(rowsRemoved(QModelIndex,int,int)),
00661                          this, SLOT(rowsRemoved(QModelIndex,int,int)));
00662     }
00663 
00664     d->categoryDrawer = categoryDrawer;
00665 
00666     if (categoryDrawer)
00667     {
00668         if (d->proxyModel)
00669         {
00670             if (d->proxyModel->rowCount())
00671             {
00672                 d->layoutChanged(true);
00673             }
00674         }
00675     }
00676     else
00677     {
00678         updateGeometries();
00679     }
00680 }
00681 
00682 QModelIndex KCategorizedView::indexAt(const QPoint &point) const
00683 {
00684     if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
00685     {
00686         return QListView::indexAt(point);
00687     }
00688 
00689     QModelIndex index;
00690 
00691     const QModelIndexList item = d->intersectionSet(QRect(point, point));
00692 
00693     if (item.count() == 1)
00694     {
00695         index = item[0];
00696     }
00697 
00698     return index;
00699 }
00700 
00701 void KCategorizedView::reset()
00702 {
00703     QListView::reset();
00704 
00705     d->lastSelection = QItemSelection();
00706     d->forcedSelectionPosition = 0;
00707     d->elementsInfo.clear();
00708     d->elementsPosition.clear();
00709     d->categoriesIndexes.clear();
00710     d->categoriesPosition.clear();
00711     d->categories.clear();
00712     d->intersectedIndexes.clear();
00713     d->modelIndexList.clear();
00714     d->hovered = QModelIndex();
00715     d->biggestItemSize = QSize(0, 0);
00716     d->mouseButtonPressed = false;
00717     d->rightMouseButtonPressed = false;
00718 }
00719 
00720 void KCategorizedView::paintEvent(QPaintEvent *event)
00721 {
00722     if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
00723     {
00724         QListView::paintEvent(event);
00725         return;
00726     }
00727 
00728     bool alternatingRows = alternatingRowColors();
00729 
00730     QStyleOptionViewItemV4 option = viewOptions();
00731     option.widget = this;
00732     if (wordWrap())
00733     {
00734         option.features |= QStyleOptionViewItemV4::WrapText;
00735     }
00736 
00737     QPainter painter(viewport());
00738     QRect area = event->rect();
00739     const bool focus = (hasFocus() || viewport()->hasFocus()) &&
00740                         currentIndex().isValid();
00741     const QStyle::State state = option.state;
00742     const bool enabled = (state & QStyle::State_Enabled) != 0;
00743 
00744     painter.save();
00745 
00746     QModelIndexList dirtyIndexes = d->intersectionSet(area);
00747     bool alternate = false;
00748     if (dirtyIndexes.count())
00749     {
00750         alternate = dirtyIndexes[0].row() % 2;
00751     }
00752     foreach (const QModelIndex &index, dirtyIndexes)
00753     {
00754         if (alternatingRows && alternate)
00755         {
00756             option.features |= QStyleOptionViewItemV4::Alternate;
00757             alternate = false;
00758         }
00759         else if (alternatingRows)
00760         {
00761             option.features &= ~QStyleOptionViewItemV4::Alternate;
00762             alternate = true;
00763         }
00764         option.state = state;
00765         option.rect = visualRect(index);
00766 
00767         if (selectionModel() && selectionModel()->isSelected(index))
00768         {
00769             option.state |= QStyle::State_Selected;
00770         }
00771 
00772         if (enabled)
00773         {
00774             QPalette::ColorGroup cg;
00775             if ((d->proxyModel->flags(index) & Qt::ItemIsEnabled) == 0)
00776             {
00777                 option.state &= ~QStyle::State_Enabled;
00778                 cg = QPalette::Disabled;
00779             }
00780             else
00781             {
00782                 cg = QPalette::Normal;
00783             }
00784             option.palette.setCurrentColorGroup(cg);
00785         }
00786 
00787         if (focus && currentIndex() == index)
00788         {
00789             option.state |= QStyle::State_HasFocus;
00790             if (this->state() == EditingState)
00791                 option.state |= QStyle::State_Editing;
00792         }
00793 
00794         // we are only interested to give the mouse over feedback when no
00795         // dragging is happening (ereslibre)
00796         if ((index == d->hovered) && !d->mouseButtonPressed &&
00797             (this->state() == QAbstractItemView::NoState))
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 = QString();
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 = QString();
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     QListView::startDrag(supportedActions);
01222 #endif
01223 
01224     d->isDragging = false;
01225     d->mouseButtonPressed = false;
01226     d->rightMouseButtonPressed = false;
01227 
01228     viewport()->update(d->lastDraggedItemsRect);
01229 }
01230 
01231 void KCategorizedView::dragMoveEvent(QDragMoveEvent *event)
01232 {
01233     d->mousePosition = event->pos();
01234 
01235     if (d->mouseButtonPressed)
01236     {
01237         d->isDragging = true;
01238     }
01239     else
01240     {
01241         d->isDragging = false;
01242     }
01243 
01244     d->dragLeftViewport = false;
01245 
01246 #if defined(DOLPHIN_DRAGANDDROP)
01247     QAbstractItemView::dragMoveEvent(event);
01248 #else
01249     QListView::dragMoveEvent(event);
01250 #endif
01251 
01252     if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
01253     {
01254         return;
01255     }
01256 
01257     d->drawDraggedItems();
01258 }
01259 
01260 void KCategorizedView::dragLeaveEvent(QDragLeaveEvent *event)
01261 {
01262     d->dragLeftViewport = true;
01263 
01264 #if defined(DOLPHIN_DRAGANDDROP)
01265     QAbstractItemView::dragLeaveEvent(event);
01266 #else
01267     QListView::dragLeaveEvent(event);
01268 #endif
01269 }
01270 
01271 void KCategorizedView::dropEvent(QDropEvent *event)
01272 {
01273 #if defined(DOLPHIN_DRAGANDDROP)
01274     QAbstractItemView::dropEvent(event);
01275 #else
01276     QListView::dropEvent(event);
01277 #endif
01278 }
01279 
01280 QModelIndex KCategorizedView::moveCursor(CursorAction cursorAction,
01281                                          Qt::KeyboardModifiers modifiers)
01282 {
01283     if ((viewMode() != KCategorizedView::IconMode) ||
01284         !d->proxyModel ||
01285         !d->categoryDrawer ||
01286          d->categories.isEmpty() ||
01287         !d->proxyModel->isCategorizedModel())
01288     {
01289         return QListView::moveCursor(cursorAction, modifiers);
01290     }
01291 
01292     int viewportWidth = viewport()->width() - spacing();
01293     int itemWidth;
01294 
01295     if (gridSize().isEmpty())
01296     {
01297         itemWidth = d->biggestItemSize.width();
01298     }
01299     else
01300     {
01301         itemWidth = gridSize().width();
01302     }
01303 
01304     int itemWidthPlusSeparation = spacing() + itemWidth;
01305     if (!itemWidthPlusSeparation)
01306         itemWidthPlusSeparation++;
01307     int elementsPerRow = viewportWidth / itemWidthPlusSeparation;
01308     if (!elementsPerRow)
01309         elementsPerRow++;
01310 
01311     QModelIndex current = selectionModel() ? selectionModel()->currentIndex()
01312                                            : QModelIndex();
01313 
01314     if (!current.isValid())
01315     {
01316         if (cursorAction == MoveEnd)
01317         {
01318             current = model()->index(model()->rowCount() - 1, 0, QModelIndex());
01319             d->forcedSelectionPosition = d->elementsInfo[current.row()].relativeOffsetToCategory % elementsPerRow;
01320         }
01321         else
01322         {
01323             current = model()->index(0, 0, QModelIndex());
01324             d->forcedSelectionPosition = 0;
01325         }
01326 
01327         return current;
01328     }
01329 
01330     QString lastCategory = d->categories.first();
01331     QString theCategory = d->categories.first();
01332     QString afterCategory = d->categories.first();
01333 
01334     bool hasToBreak = false;
01335     foreach (const QString &category, d->categories)
01336     {
01337         if (hasToBreak)
01338         {
01339             afterCategory = category;
01340 
01341             break;
01342         }
01343 
01344         if (category == d->elementsInfo[current.row()].category)
01345         {
01346             theCategory = category;
01347 
01348             hasToBreak = true;
01349         }
01350 
01351         if (!hasToBreak)
01352         {
01353             lastCategory = category;
01354         }
01355     }
01356 
01357     switch (cursorAction)
01358     {
01359         case QAbstractItemView::MoveUp: {
01360             if (d->elementsInfo[current.row()].relativeOffsetToCategory >= elementsPerRow)
01361             {
01362                 int indexToMove = current.row();
01363                 indexToMove -= qMin(((d->elementsInfo[current.row()].relativeOffsetToCategory) + d->forcedSelectionPosition), elementsPerRow - d->forcedSelectionPosition + (d->elementsInfo[current.row()].relativeOffsetToCategory % elementsPerRow));
01364 
01365                 return d->proxyModel->index(indexToMove, 0);
01366             }
01367             else
01368             {
01369                 int lastCategoryLastRow = (d->categoriesIndexes[lastCategory].count() - 1) % elementsPerRow;
01370                 int indexToMove = current.row() - d->elementsInfo[current.row()].relativeOffsetToCategory;
01371 
01372                 if (d->forcedSelectionPosition >= lastCategoryLastRow)
01373                 {
01374                     indexToMove -= 1;
01375                 }
01376                 else
01377                 {
01378                     indexToMove -= qMin((lastCategoryLastRow - d->forcedSelectionPosition + 1), d->forcedSelectionPosition + elementsPerRow + 1);
01379                 }
01380 
01381                 return d->proxyModel->index(indexToMove, 0);
01382             }
01383         }
01384 
01385         case QAbstractItemView::MoveDown: {
01386             if (d->elementsInfo[current.row()].relativeOffsetToCategory < (d->categoriesIndexes[theCategory].count() - 1 - ((d->categoriesIndexes[theCategory].count() - 1) % elementsPerRow)))
01387             {
01388                 int indexToMove = current.row();
01389                 indexToMove += qMin(elementsPerRow, d->categoriesIndexes[theCategory].count() - 1 - d->elementsInfo[current.row()].relativeOffsetToCategory);
01390 
01391                 return d->proxyModel->index(indexToMove, 0);
01392             }
01393             else
01394             {
01395                 int afterCategoryLastRow = qMin(elementsPerRow, d->categoriesIndexes[afterCategory].count());
01396                 int indexToMove = current.row() + (d->categoriesIndexes[theCategory].count() - d->elementsInfo[current.row()].relativeOffsetToCategory);
01397 
01398                 if (d->forcedSelectionPosition >= afterCategoryLastRow)
01399                 {
01400                     indexToMove += afterCategoryLastRow - 1;
01401                 }
01402                 else
01403                 {
01404                     indexToMove += qMin(d->forcedSelectionPosition, elementsPerRow);
01405                 }
01406 
01407                 return d->proxyModel->index(indexToMove, 0);
01408             }
01409         }
01410 
01411         case QAbstractItemView::MoveLeft:
01412             if (layoutDirection() == Qt::RightToLeft)
01413             {
01414                 if (!(d->elementsInfo[current.row() + 1].relativeOffsetToCategory % elementsPerRow))
01415                     return current;
01416 
01417                 d->forcedSelectionPosition = d->elementsInfo[current.row() + 1].relativeOffsetToCategory % elementsPerRow;
01418 
01419 #if 0 //follow qt view behavior. lateral movements won't change visual row
01420                 if (d->forcedSelectionPosition < 0)
01421                     d->forcedSelectionPosition = (d->categoriesIndexes[theCategory].count() - 1) % elementsPerRow;
01422 #endif
01423 
01424                 return d->proxyModel->index(current.row() + 1, 0);
01425             }
01426 
01427             if (!(d->elementsInfo[current.row()].relativeOffsetToCategory % elementsPerRow))
01428                 return current;
01429 
01430             d->forcedSelectionPosition = d->elementsInfo[current.row() - 1].relativeOffsetToCategory % elementsPerRow;
01431 
01432 #if 0 //follow qt view behavior. lateral movements won't change visual row
01433             if (d->forcedSelectionPosition < 0)
01434                 d->forcedSelectionPosition = (d->categoriesIndexes[theCategory].count() - 1) % elementsPerRow;
01435 #endif
01436 
01437             return d->proxyModel->index(current.row() - 1, 0);
01438 
01439         case QAbstractItemView::MoveRight:
01440             if (layoutDirection() == Qt::RightToLeft)
01441             {
01442                 if (!(d->elementsInfo[current.row()].relativeOffsetToCategory % elementsPerRow))
01443                     return current;
01444 
01445                 d->forcedSelectionPosition = d->elementsInfo[current.row() - 1].relativeOffsetToCategory % elementsPerRow;
01446 
01447 #if 0 //follow qt view behavior. lateral movements won't change visual row
01448                 if (d->forcedSelectionPosition < 0)
01449                     d->forcedSelectionPosition = (d->categoriesIndexes[theCategory].count() - 1) % elementsPerRow;
01450 #endif
01451 
01452                 return d->proxyModel->index(current.row() - 1, 0);
01453             }
01454 
01455             if (!(d->elementsInfo[current.row() + 1].relativeOffsetToCategory % elementsPerRow))
01456                 return current;
01457 
01458             d->forcedSelectionPosition = d->elementsInfo[current.row() + 1].relativeOffsetToCategory % elementsPerRow;
01459 
01460 #if 0 //follow qt view behavior. lateral movements won't change visual row
01461             if (d->forcedSelectionPosition < 0)
01462                 d->forcedSelectionPosition = (d->categoriesIndexes[theCategory].count() - 1) % elementsPerRow;
01463 #endif
01464 
01465             return d->proxyModel->index(current.row() + 1, 0);
01466 
01467         default:
01468             break;
01469     }
01470 
01471     return QListView::moveCursor(cursorAction, modifiers);
01472 }
01473 
01474 void KCategorizedView::rowsInserted(const QModelIndex &parent,
01475                                     int start,
01476                                     int end)
01477 {
01478     QListView::rowsInserted(parent, start, end);
01479 
01480     if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
01481     {
01482         d->forcedSelectionPosition = 0;
01483         d->elementsInfo.clear();
01484         d->elementsPosition.clear();
01485         d->categoriesIndexes.clear();
01486         d->categoriesPosition.clear();
01487         d->categories.clear();
01488         d->intersectedIndexes.clear();
01489         d->modelIndexList.clear();
01490         d->hovered = QModelIndex();
01491         d->biggestItemSize = QSize(0, 0);
01492         d->mouseButtonPressed = false;
01493         d->rightMouseButtonPressed = false;
01494 
01495         return;
01496     }
01497 
01498     rowsInsertedArtifficial(parent, start, end);
01499 }
01500 
01501 void KCategorizedView::rowsInsertedArtifficial(const QModelIndex &parent,
01502                                                int start,
01503                                                int end)
01504 {
01505     Q_UNUSED(parent);
01506 
01507     d->forcedSelectionPosition = 0;
01508     d->elementsInfo.clear();
01509     d->elementsPosition.clear();
01510     d->categoriesIndexes.clear();
01511     d->categoriesPosition.clear();
01512     d->categories.clear();
01513     d->intersectedIndexes.clear();
01514     d->modelIndexList.clear();
01515     d->hovered = QModelIndex();
01516     d->biggestItemSize = QSize(0, 0);
01517     d->mouseButtonPressed = false;
01518     d->rightMouseButtonPressed = false;
01519 
01520     if (start > end || end < 0 || start < 0 || !d->proxyModel->rowCount())
01521     {
01522         return;
01523     }
01524 
01525     // Add all elements mapped to the source model and explore categories
01526     QString prevCategory = d->proxyModel->data(d->proxyModel->index(0, d->proxyModel->sortColumn()), KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString();
01527     QString lastCategory = prevCategory;
01528     QModelIndexList modelIndexList;
01529     struct Private::ElementInfo elementInfo;
01530     int offset = -1;
01531     for (int k = 0; k < d->proxyModel->rowCount(); ++k)
01532     {
01533         QModelIndex index = d->proxyModel->index(k, d->proxyModel->sortColumn());
01534         QModelIndex indexSize = d->proxyModel->index(k, 0);
01535 
01536         d->biggestItemSize = QSize(qMax(sizeHintForIndex(indexSize).width(),
01537                                         d->biggestItemSize.width()),
01538                                    qMax(sizeHintForIndex(indexSize).height(),
01539                                         d->biggestItemSize.height()));
01540 
01541         d->modelIndexList << index;
01542 
01543         lastCategory = d->proxyModel->data(index, KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString();
01544 
01545         elementInfo.category = lastCategory;
01546 
01547         if (prevCategory != lastCategory)
01548         {
01549             offset = 0;
01550             d->categoriesIndexes.insert(prevCategory, modelIndexList);
01551             d->categories << prevCategory;
01552             modelIndexList.clear();
01553         }
01554         else
01555         {
01556             offset++;
01557         }
01558 
01559         elementInfo.relativeOffsetToCategory = offset;
01560 
01561         modelIndexList << index;
01562         prevCategory = lastCategory;
01563 
01564         d->elementsInfo.insert(index.row(), elementInfo);
01565     }
01566 
01567     d->categoriesIndexes.insert(prevCategory, modelIndexList);
01568     d->categories << prevCategory;
01569 
01570     d->updateScrollbars();
01571 
01572     // FIXME: We need to safely save the last selection. This is on my TODO
01573     // list (ereslibre).
01574     selectionModel()->clear();
01575 }
01576 
01577 void KCategorizedView::rowsRemoved(const QModelIndex &parent,
01578                                    int start,
01579                                    int end)
01580 {
01581     if (d->proxyModel && d->categoryDrawer && d->proxyModel->isCategorizedModel())
01582     {
01583         // Force the view to update all elements
01584         rowsInsertedArtifficial(QModelIndex(), 0, d->proxyModel->rowCount() - 1);
01585     }
01586 }
01587 
01588 void KCategorizedView::updateGeometries()
01589 {
01590     if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
01591     {
01592         QListView::updateGeometries();
01593         return;
01594     }
01595 
01596     // Avoid QListView::updateGeometries(), since it will try to set another
01597     // range to our scroll bars, what we don't want (ereslibre)
01598     QAbstractItemView::updateGeometries();
01599 }
01600 
01601 void KCategorizedView::slotLayoutChanged()
01602 {
01603     d->layoutChanged();
01604 }
01605 
01606 void KCategorizedView::currentChanged(const QModelIndex &current,
01607                                       const QModelIndex &previous)
01608 {
01609     // We need to update the forcedSelectionPosition property in order to correctly
01610     // navigate after with keyboard using up & down keys
01611 
01612     int viewportWidth = viewport()->width() - spacing();
01613 
01614     int itemHeight;
01615     int itemWidth;
01616 
01617     if (gridSize().isEmpty())
01618     {
01619         itemHeight = d->biggestItemSize.height();
01620         itemWidth = d->biggestItemSize.width();
01621     }
01622     else
01623     {
01624         itemHeight = gridSize().height();
01625         itemWidth = gridSize().width();
01626     }
01627 
01628     int itemWidthPlusSeparation = spacing() + itemWidth;
01629     if (!itemWidthPlusSeparation)
01630         itemWidthPlusSeparation++;
01631     int elementsPerRow = viewportWidth / itemWidthPlusSeparation;
01632     if (!elementsPerRow)
01633         elementsPerRow++;
01634 
01635     if (d->mouseButtonPressed || d->rightMouseButtonPressed)
01636         d->forcedSelectionPosition = d->elementsInfo[current.row()].relativeOffsetToCategory % elementsPerRow;
01637 
01638     QListView::currentChanged(current, previous);
01639 }
01640 
01641 void KCategorizedView::dataChanged(const QModelIndex &topLeft,
01642                                    const QModelIndex &bottomRight)
01643 {
01644     if (topLeft == bottomRight)
01645     {
01646         d->cacheIndex(topLeft);
01647     }
01648     else
01649     {
01650         const int columnStart = topLeft.column();
01651         const int columnEnd = bottomRight.column();
01652         const int rowStart = topLeft.row();
01653         const int rowEnd = bottomRight.row();
01654 
01655         for (int row = rowStart; row <= rowEnd; ++row)
01656         {
01657             for (int column = columnStart; column <= columnEnd; ++column)
01658             {
01659                 d->cacheIndex(d->proxyModel->index(row, column));
01660             }
01661         }
01662     }
01663 
01664     QListView::dataChanged(topLeft, bottomRight);
01665     slotLayoutChanged();
01666 }
01667 
01668 #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
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
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