]> cloud.milkyroute.net Git - dolphin.git/blob - src/kcategorizedview.cpp
Improve the categorized view for the list mode
[dolphin.git] / src / kcategorizedview.cpp
1 /**
2 * This file is part of the KDE project
3 * Copyright (C) 2007 Rafael Fernández López <ereslibre@kde.org>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB. If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 #include "kcategorizedview.h"
22 #include "kcategorizedview_p.h"
23
24 #include <math.h> // trunc on C99 compliant systems
25 #include <kdefakes.h> // trunc for not C99 compliant systems
26
27 #include <QPainter>
28 #include <QScrollBar>
29 #include <QPaintEvent>
30
31 #include <kstyle.h>
32
33 #include "kcategorydrawer.h"
34 #include "kcategorizedsortfilterproxymodel.h"
35
36 // By defining DOLPHIN_DRAGANDDROP the custom drag and drop implementation of
37 // KCategorizedView is bypassed to have a consistent drag and drop look for all
38 // views. Hopefully transparent pixmaps for drag objects will be supported in
39 // Qt 4.4, so that this workaround can be skipped.
40 #define DOLPHIN_DRAGANDDROP
41
42 KCategorizedView::Private::Private(KCategorizedView *listView)
43 : listView(listView)
44 , categoryDrawer(0)
45 , biggestItemSize(QSize(0, 0))
46 , mouseButtonPressed(false)
47 , rightMouseButtonPressed(false)
48 , isDragging(false)
49 , dragLeftViewport(false)
50 , proxyModel(0)
51 {
52 }
53
54 KCategorizedView::Private::~Private()
55 {
56 }
57
58 const QModelIndexList &KCategorizedView::Private::intersectionSet(const QRect &rect)
59 {
60 QModelIndex index;
61 QRect indexVisualRect;
62
63 intersectedIndexes.clear();
64
65 int itemHeight;
66
67 if (listView->gridSize().isEmpty())
68 {
69 itemHeight = biggestItemSize.height();
70 }
71 else
72 {
73 itemHeight = listView->gridSize().height();
74 }
75
76 // Lets find out where we should start
77 int top = proxyModel->rowCount() - 1;
78 int bottom = 0;
79 int middle = (top + bottom) / 2;
80 while (bottom <= top)
81 {
82 middle = (top + bottom) / 2;
83
84 index = proxyModel->index(middle, 0);
85 indexVisualRect = visualRect(index);
86 // We need the whole height (not only the visualRect). This will help us to update
87 // all needed indexes correctly (ereslibre)
88 indexVisualRect.setHeight(indexVisualRect.height() + (itemHeight - indexVisualRect.height()));
89
90 if (qMax(indexVisualRect.topLeft().y(),
91 indexVisualRect.bottomRight().y()) < qMin(rect.topLeft().y(),
92 rect.bottomRight().y()))
93 {
94 bottom = middle + 1;
95 }
96 else
97 {
98 top = middle - 1;
99 }
100 }
101
102 for (int i = middle; i < proxyModel->rowCount(); i++)
103 {
104 index = proxyModel->index(i, 0);
105 indexVisualRect = visualRect(index);
106
107 if (rect.intersects(indexVisualRect))
108 intersectedIndexes.append(index);
109
110 // If we passed next item, stop searching for hits
111 if (qMax(rect.bottomRight().y(), rect.topLeft().y()) <
112 qMin(indexVisualRect.topLeft().y(),
113 indexVisualRect.bottomRight().y()))
114 break;
115 }
116
117 return intersectedIndexes;
118 }
119
120 QRect KCategorizedView::Private::visualRectInViewport(const QModelIndex &index) const
121 {
122 if (!index.isValid())
123 return QRect();
124
125 QString curCategory = elementsInfo[index.row()].category;
126
127 QRect retRect;
128
129 if (listView->flow() == QListView::LeftToRight)
130 {
131 if (listView->layoutDirection() == Qt::LeftToRight)
132 {
133 retRect = QRect(listView->spacing(), listView->spacing() * 2 +
134 categoryDrawer->categoryHeight(index, listView->viewOptions()), 0, 0);
135 }
136 else
137 {
138 retRect = QRect(listView->viewport()->width() - listView->spacing(), listView->spacing() * 2 +
139 categoryDrawer->categoryHeight(index, listView->viewOptions()), 0, 0);
140 }
141 }
142 else
143 {
144 retRect = QRect(listView->spacing(), listView->spacing() * 2 +
145 categoryDrawer->categoryHeight(index, listView->viewOptions()), 0, 0);
146 }
147
148 int viewportWidth = listView->viewport()->width() - listView->spacing();
149
150 int itemHeight;
151 int itemWidth;
152
153 if (listView->gridSize().isEmpty() && (listView->flow() == QListView::LeftToRight))
154 {
155 itemHeight = biggestItemSize.height();
156 itemWidth = biggestItemSize.width();
157 }
158 else if (listView->flow() == QListView::LeftToRight)
159 {
160 itemHeight = listView->gridSize().height();
161 itemWidth = listView->gridSize().width();
162 }
163 else if (listView->gridSize().isEmpty() && (listView->flow() == QListView::TopToBottom))
164 {
165 itemHeight = biggestItemSize.height();
166 itemWidth = listView->viewport()->width() - listView->spacing() * 2;
167 }
168 else
169 {
170 itemHeight = listView->gridSize().height();
171 itemWidth = listView->gridSize().width() - listView->spacing() * 2;
172 }
173
174 int itemWidthPlusSeparation = listView->spacing() + itemWidth;
175 if (!itemWidthPlusSeparation)
176 itemWidthPlusSeparation++;
177 int elementsPerRow = viewportWidth / itemWidthPlusSeparation;
178 if (!elementsPerRow)
179 elementsPerRow++;
180
181 int column;
182 int row;
183
184 if (listView->flow() == QListView::LeftToRight)
185 {
186 column = elementsInfo[index.row()].relativeOffsetToCategory % elementsPerRow;
187 row = elementsInfo[index.row()].relativeOffsetToCategory / elementsPerRow;
188
189 if (listView->layoutDirection() == Qt::LeftToRight)
190 {
191 retRect.setLeft(retRect.left() + column * listView->spacing() +
192 column * itemWidth);
193 }
194 else
195 {
196 retRect.setLeft(retRect.right() - column * listView->spacing() -
197 column * itemWidth - itemWidth);
198
199 retRect.setRight(retRect.right() - column * listView->spacing() -
200 column * itemWidth);
201 }
202 }
203 else
204 {
205 elementsPerRow = 1;
206 column = elementsInfo[index.row()].relativeOffsetToCategory % elementsPerRow;
207 row = elementsInfo[index.row()].relativeOffsetToCategory / elementsPerRow;
208 }
209
210 foreach (const QString &category, categories)
211 {
212 if (category == curCategory)
213 break;
214
215 float rows = (float) ((float) categoriesIndexes[category].count() /
216 (float) elementsPerRow);
217
218 int rowsInt = categoriesIndexes[category].count() / elementsPerRow;
219
220 if (rows - trunc(rows)) rowsInt++;
221
222 retRect.setTop(retRect.top() +
223 (rowsInt * itemHeight) +
224 categoryDrawer->categoryHeight(index, listView->viewOptions()) +
225 listView->spacing() * 2);
226
227 if (listView->gridSize().isEmpty())
228 {
229 retRect.setTop(retRect.top() +
230 (rowsInt * listView->spacing()));
231 }
232 }
233
234 if (listView->gridSize().isEmpty())
235 {
236 retRect.setTop(retRect.top() + row * listView->spacing() +
237 (row * itemHeight));
238 }
239 else
240 {
241 retRect.setTop(retRect.top() + (row * itemHeight));
242 }
243
244 retRect.setWidth(itemWidth);
245
246 QModelIndex heightIndex = proxyModel->index(index.row(), 0);
247 if (listView->gridSize().isEmpty())
248 {
249 retRect.setHeight(listView->sizeHintForIndex(heightIndex).height());
250 }
251 else
252 {
253 retRect.setHeight(qMin(listView->sizeHintForIndex(heightIndex).height(),
254 listView->gridSize().height()));
255 }
256
257 return retRect;
258 }
259
260 QRect KCategorizedView::Private::visualCategoryRectInViewport(const QString &category) const
261 {
262 QRect retRect(listView->spacing(),
263 listView->spacing(),
264 listView->viewport()->width() - listView->spacing() * 2,
265 0);
266
267 if (!proxyModel->rowCount() || !categories.contains(category))
268 return QRect();
269
270 QModelIndex index = proxyModel->index(0, 0, QModelIndex());
271
272 int viewportWidth = listView->viewport()->width() - listView->spacing();
273
274 int itemHeight;
275 int itemWidth;
276
277 if (listView->gridSize().isEmpty())
278 {
279 itemHeight = biggestItemSize.height();
280 itemWidth = biggestItemSize.width();
281 }
282 else
283 {
284 itemHeight = listView->gridSize().height();
285 itemWidth = listView->gridSize().width();
286 }
287
288 int itemWidthPlusSeparation = listView->spacing() + itemWidth;
289 int elementsPerRow = viewportWidth / itemWidthPlusSeparation;
290
291 if (!elementsPerRow)
292 elementsPerRow++;
293
294 if (listView->flow() == QListView::TopToBottom)
295 {
296 elementsPerRow = 1;
297 }
298
299 foreach (const QString &itCategory, categories)
300 {
301 if (itCategory == category)
302 break;
303
304 float rows = (float) ((float) categoriesIndexes[itCategory].count() /
305 (float) elementsPerRow);
306 int rowsInt = categoriesIndexes[itCategory].count() / elementsPerRow;
307
308 if (rows - trunc(rows)) rowsInt++;
309
310 retRect.setTop(retRect.top() +
311 (rowsInt * itemHeight) +
312 categoryDrawer->categoryHeight(index, listView->viewOptions()) +
313 listView->spacing() * 2);
314
315 if (listView->gridSize().isEmpty())
316 {
317 retRect.setTop(retRect.top() +
318 (rowsInt * listView->spacing()));
319 }
320 }
321
322 retRect.setHeight(categoryDrawer->categoryHeight(index, listView->viewOptions()));
323
324 return retRect;
325 }
326
327 // We're sure elementsPosition doesn't contain index
328 const QRect &KCategorizedView::Private::cacheIndex(const QModelIndex &index)
329 {
330 QRect rect = visualRectInViewport(index);
331 elementsPosition[index.row()] = rect;
332
333 return elementsPosition[index.row()];
334 }
335
336 // We're sure categoriesPosition doesn't contain category
337 const QRect &KCategorizedView::Private::cacheCategory(const QString &category)
338 {
339 QRect rect = visualCategoryRectInViewport(category);
340 categoriesPosition[category] = rect;
341
342 return categoriesPosition[category];
343 }
344
345 const QRect &KCategorizedView::Private::cachedRectIndex(const QModelIndex &index)
346 {
347 if (elementsPosition.contains(index.row())) // If we have it cached
348 { // return it
349 return elementsPosition[index.row()];
350 }
351 else // Otherwise, cache it
352 { // and return it
353 return cacheIndex(index);
354 }
355 }
356
357 const QRect &KCategorizedView::Private::cachedRectCategory(const QString &category)
358 {
359 if (categoriesPosition.contains(category)) // If we have it cached
360 { // return it
361 return categoriesPosition[category];
362 }
363 else // Otherwise, cache it and
364 { // return it
365 return cacheCategory(category);
366 }
367 }
368
369 QRect KCategorizedView::Private::visualRect(const QModelIndex &index)
370 {
371 QRect retRect = cachedRectIndex(index);
372 int dx = -listView->horizontalOffset();
373 int dy = -listView->verticalOffset();
374 retRect.adjust(dx, dy, dx, dy);
375
376 return retRect;
377 }
378
379 QRect KCategorizedView::Private::categoryVisualRect(const QString &category)
380 {
381 QRect retRect = cachedRectCategory(category);
382 int dx = -listView->horizontalOffset();
383 int dy = -listView->verticalOffset();
384 retRect.adjust(dx, dy, dx, dy);
385
386 return retRect;
387 }
388
389 void KCategorizedView::Private::drawNewCategory(const QModelIndex &index,
390 int sortRole,
391 const QStyleOption &option,
392 QPainter *painter)
393 {
394 if (!index.isValid())
395 {
396 return;
397 }
398
399 QStyleOption optionCopy = option;
400 const QString category = proxyModel->data(index, KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString();
401
402 optionCopy.state &= ~QStyle::State_Selected;
403
404 if ((listView->selectionMode() != SingleSelection) && (listView->selectionMode() != NoSelection)) {
405 if ((category == hoveredCategory) && !mouseButtonPressed)
406 {
407 optionCopy.state |= QStyle::State_MouseOver;
408 }
409 else if ((category == hoveredCategory) && mouseButtonPressed)
410 {
411 QPoint initialPressPosition = listView->viewport()->mapFromGlobal(QCursor::pos());
412 initialPressPosition.setY(initialPressPosition.y() + listView->verticalOffset());
413 initialPressPosition.setX(initialPressPosition.x() + listView->horizontalOffset());
414
415 if (initialPressPosition == this->initialPressPosition)
416 {
417 optionCopy.state |= QStyle::State_Selected;
418 }
419 }
420 }
421
422 categoryDrawer->drawCategory(index,
423 sortRole,
424 optionCopy,
425 painter);
426 }
427
428
429 void KCategorizedView::Private::updateScrollbars()
430 {
431 // find the last index in the last category
432 QModelIndex lastIndex = categoriesIndexes.isEmpty() ? QModelIndex() : categoriesIndexes[categories.last()].last();
433
434 int lastItemBottom = cachedRectIndex(lastIndex).top() +
435 listView->spacing() + (listView->gridSize().isEmpty() ? biggestItemSize.height() : listView->gridSize().height()) - listView->viewport()->height();
436
437 listView->horizontalScrollBar()->setRange(0, 0);
438
439 listView->verticalScrollBar()->setSingleStep(listView->viewport()->height() / 10);
440 listView->verticalScrollBar()->setPageStep(listView->viewport()->height());
441 listView->verticalScrollBar()->setRange(0, lastItemBottom);
442 }
443
444 void KCategorizedView::Private::drawDraggedItems(QPainter *painter)
445 {
446 QStyleOptionViewItemV3 option = listView->viewOptions();
447 option.state &= ~QStyle::State_MouseOver;
448 foreach (const QModelIndex &index, listView->selectionModel()->selectedIndexes())
449 {
450 const int dx = mousePosition.x() - initialPressPosition.x() + listView->horizontalOffset();
451 const int dy = mousePosition.y() - initialPressPosition.y() + listView->verticalOffset();
452
453 option.rect = visualRect(index);
454 option.rect.adjust(dx, dy, dx, dy);
455
456 if (option.rect.intersects(listView->viewport()->rect()))
457 {
458 listView->itemDelegate(index)->paint(painter, option, index);
459 }
460 }
461 }
462
463 void KCategorizedView::Private::layoutChanged(bool forceItemReload)
464 {
465 if (proxyModel && categoryDrawer && proxyModel->isCategorizedModel() &&
466 ((forceItemReload ||
467 (modelSortRole != proxyModel->sortRole()) ||
468 (modelSortColumn != proxyModel->sortColumn()) ||
469 (modelSortOrder != proxyModel->sortOrder()) ||
470 (modelLastRowCount != proxyModel->rowCount()) ||
471 (modelCategorized != proxyModel->isCategorizedModel()))))
472 {
473 // Force the view to update all elements
474 listView->rowsInsertedArtifficial(QModelIndex(), 0, proxyModel->rowCount() - 1);
475
476 if (!forceItemReload)
477 {
478 modelSortRole = proxyModel->sortRole();
479 modelSortColumn = proxyModel->sortColumn();
480 modelSortOrder = proxyModel->sortOrder();
481 modelLastRowCount = proxyModel->rowCount();
482 modelCategorized = proxyModel->isCategorizedModel();
483 }
484 }
485 else if (proxyModel && categoryDrawer && proxyModel->isCategorizedModel())
486 {
487 updateScrollbars();
488 }
489 }
490
491 void KCategorizedView::Private::drawDraggedItems()
492 {
493 QRect rectToUpdate;
494 QRect currentRect;
495 foreach (const QModelIndex &index, listView->selectionModel()->selectedIndexes())
496 {
497 int dx = mousePosition.x() - initialPressPosition.x() + listView->horizontalOffset();
498 int dy = mousePosition.y() - initialPressPosition.y() + listView->verticalOffset();
499
500 currentRect = visualRect(index);
501 currentRect.adjust(dx, dy, dx, dy);
502
503 if (currentRect.intersects(listView->viewport()->rect()))
504 {
505 rectToUpdate = rectToUpdate.united(currentRect);
506 }
507 }
508
509 listView->viewport()->update(lastDraggedItemsRect.united(rectToUpdate));
510
511 lastDraggedItemsRect = rectToUpdate;
512 }
513
514
515 //==============================================================================
516
517
518 KCategorizedView::KCategorizedView(QWidget *parent)
519 : QListView(parent)
520 , d(new Private(this))
521 {
522 setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
523 setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
524 }
525
526 KCategorizedView::~KCategorizedView()
527 {
528 delete d;
529 }
530
531 void KCategorizedView::setGridSize(const QSize &size)
532 {
533 QListView::setGridSize(size);
534
535 d->layoutChanged(true);
536 }
537
538 void KCategorizedView::setModel(QAbstractItemModel *model)
539 {
540 d->lastSelection = QItemSelection();
541 d->forcedSelectionPosition = 0;
542 d->elementsInfo.clear();
543 d->elementsPosition.clear();
544 d->categoriesIndexes.clear();
545 d->categoriesPosition.clear();
546 d->categories.clear();
547 d->intersectedIndexes.clear();
548 d->modelIndexList.clear();
549 d->hovered = QModelIndex();
550 d->mouseButtonPressed = false;
551 d->rightMouseButtonPressed = false;
552
553 if (d->proxyModel)
554 {
555 QObject::disconnect(d->proxyModel,
556 SIGNAL(layoutChanged()),
557 this, SLOT(slotLayoutChanged()));
558
559 QObject::disconnect(d->proxyModel,
560 SIGNAL(dataChanged(QModelIndex,QModelIndex)),
561 this, SLOT(slotLayoutChanged()));
562
563 QObject::disconnect(d->proxyModel,
564 SIGNAL(rowsRemoved(QModelIndex,int,int)),
565 this, SLOT(rowsRemoved(QModelIndex,int,int)));
566 }
567
568 QListView::setModel(model);
569
570 d->proxyModel = dynamic_cast<KCategorizedSortFilterProxyModel*>(model);
571
572 if (d->proxyModel)
573 {
574 d->modelSortRole = d->proxyModel->sortRole();
575 d->modelSortColumn = d->proxyModel->sortColumn();
576 d->modelSortOrder = d->proxyModel->sortOrder();
577 d->modelLastRowCount = d->proxyModel->rowCount();
578 d->modelCategorized = d->proxyModel->isCategorizedModel();
579
580 QObject::connect(d->proxyModel,
581 SIGNAL(layoutChanged()),
582 this, SLOT(slotLayoutChanged()));
583
584 QObject::connect(d->proxyModel,
585 SIGNAL(dataChanged(QModelIndex,QModelIndex)),
586 this, SLOT(slotLayoutChanged()));
587
588 QObject::connect(d->proxyModel,
589 SIGNAL(rowsRemoved(QModelIndex,int,int)),
590 this, SLOT(rowsRemoved(QModelIndex,int,int)));
591
592 if (d->proxyModel->rowCount())
593 {
594 d->layoutChanged(true);
595 }
596 }
597 else
598 {
599 d->modelCategorized = false;
600 }
601 }
602
603 QRect KCategorizedView::visualRect(const QModelIndex &index) const
604 {
605 if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
606 {
607 return QListView::visualRect(index);
608 }
609
610 if (!qobject_cast<const QSortFilterProxyModel*>(index.model()))
611 {
612 return d->visualRect(d->proxyModel->mapFromSource(index));
613 }
614
615 return d->visualRect(index);
616 }
617
618 KCategoryDrawer *KCategorizedView::categoryDrawer() const
619 {
620 return d->categoryDrawer;
621 }
622
623 void KCategorizedView::setCategoryDrawer(KCategoryDrawer *categoryDrawer)
624 {
625 d->lastSelection = QItemSelection();
626 d->forcedSelectionPosition = 0;
627 d->elementsInfo.clear();
628 d->elementsPosition.clear();
629 d->categoriesIndexes.clear();
630 d->categoriesPosition.clear();
631 d->categories.clear();
632 d->intersectedIndexes.clear();
633 d->modelIndexList.clear();
634 d->hovered = QModelIndex();
635 d->mouseButtonPressed = false;
636 d->rightMouseButtonPressed = false;
637
638 if (!categoryDrawer && d->proxyModel)
639 {
640 QObject::disconnect(d->proxyModel,
641 SIGNAL(layoutChanged()),
642 this, SLOT(slotLayoutChanged()));
643
644 QObject::disconnect(d->proxyModel,
645 SIGNAL(dataChanged(QModelIndex,QModelIndex)),
646 this, SLOT(slotLayoutChanged()));
647
648 QObject::disconnect(d->proxyModel,
649 SIGNAL(rowsRemoved(QModelIndex,int,int)),
650 this, SLOT(rowsRemoved(QModelIndex,int,int)));
651 }
652 else if (categoryDrawer && d->proxyModel)
653 {
654 QObject::connect(d->proxyModel,
655 SIGNAL(layoutChanged()),
656 this, SLOT(slotLayoutChanged()));
657
658 QObject::connect(d->proxyModel,
659 SIGNAL(dataChanged(QModelIndex,QModelIndex)),
660 this, SLOT(slotLayoutChanged()));
661
662 QObject::connect(d->proxyModel,
663 SIGNAL(rowsRemoved(QModelIndex,int,int)),
664 this, SLOT(rowsRemoved(QModelIndex,int,int)));
665 }
666
667 d->categoryDrawer = categoryDrawer;
668
669 if (categoryDrawer)
670 {
671 if (d->proxyModel)
672 {
673 if (d->proxyModel->rowCount())
674 {
675 d->layoutChanged(true);
676 }
677 }
678 }
679 else
680 {
681 updateGeometries();
682 }
683 }
684
685 QModelIndex KCategorizedView::indexAt(const QPoint &point) const
686 {
687 if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
688 {
689 return QListView::indexAt(point);
690 }
691
692 QModelIndex index;
693
694 QModelIndexList item = d->intersectionSet(QRect(point, point));
695
696 if (item.count() == 1)
697 {
698 index = item[0];
699 }
700
701 return index;
702 }
703
704 void KCategorizedView::reset()
705 {
706 QListView::reset();
707
708 d->lastSelection = QItemSelection();
709 d->forcedSelectionPosition = 0;
710 d->elementsInfo.clear();
711 d->elementsPosition.clear();
712 d->categoriesIndexes.clear();
713 d->categoriesPosition.clear();
714 d->categories.clear();
715 d->intersectedIndexes.clear();
716 d->modelIndexList.clear();
717 d->hovered = QModelIndex();
718 d->biggestItemSize = QSize(0, 0);
719 d->mouseButtonPressed = false;
720 d->rightMouseButtonPressed = false;
721 }
722
723 void KCategorizedView::paintEvent(QPaintEvent *event)
724 {
725 if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
726 {
727 QListView::paintEvent(event);
728 return;
729 }
730
731 QStyleOptionViewItemV3 option = viewOptions();
732 option.widget = this;
733 if (wordWrap())
734 {
735 option.features |= QStyleOptionViewItemV2::WrapText;
736 }
737
738 QPainter painter(viewport());
739 QRect area = event->rect();
740 const bool focus = (hasFocus() || viewport()->hasFocus()) &&
741 currentIndex().isValid();
742 const QStyle::State state = option.state;
743 const bool enabled = (state & QStyle::State_Enabled) != 0;
744
745 painter.save();
746
747 QModelIndexList dirtyIndexes = d->intersectionSet(area);
748 foreach (const QModelIndex &index, dirtyIndexes)
749 {
750 option.state = state;
751 option.rect = visualRect(index);
752
753 if (selectionModel() && selectionModel()->isSelected(index))
754 {
755 option.state |= QStyle::State_Selected;
756 }
757
758 if (enabled)
759 {
760 QPalette::ColorGroup cg;
761 if ((d->proxyModel->flags(index) & Qt::ItemIsEnabled) == 0)
762 {
763 option.state &= ~QStyle::State_Enabled;
764 cg = QPalette::Disabled;
765 }
766 else
767 {
768 cg = QPalette::Normal;
769 }
770 option.palette.setCurrentColorGroup(cg);
771 }
772
773 if (focus && currentIndex() == index)
774 {
775 option.state |= QStyle::State_HasFocus;
776 if (this->state() == EditingState)
777 option.state |= QStyle::State_Editing;
778 }
779
780 // we are only interested to give the mouse over feedback when no
781 // dragging is happening (ereslibre)
782 if ((index == d->hovered) && !d->mouseButtonPressed &&
783 (this->state() == QAbstractItemView::NoState))
784 option.state |= QStyle::State_MouseOver;
785 else
786 option.state &= ~QStyle::State_MouseOver;
787
788 itemDelegate(index)->paint(&painter, option, index);
789 }
790
791 // Redraw categories
792 QStyleOptionViewItem otherOption;
793 bool intersectedInThePast = false;
794 foreach (const QString &category, d->categories)
795 {
796 otherOption = option;
797 otherOption.rect = d->categoryVisualRect(category);
798 otherOption.state &= ~QStyle::State_MouseOver;
799
800 if (otherOption.rect.intersects(area))
801 {
802 intersectedInThePast = true;
803
804 QModelIndex indexToDraw = d->proxyModel->index(d->categoriesIndexes[category][0].row(), d->proxyModel->sortColumn());
805
806 d->drawNewCategory(indexToDraw,
807 d->proxyModel->sortRole(), otherOption, &painter);
808 }
809 else if (intersectedInThePast)
810 {
811 break; // the visible area has been finished, we don't need to keep asking, the rest won't intersect
812 // this is doable because we know that categories are correctly ordered on the list
813 }
814 }
815
816 if ((selectionMode() != SingleSelection) && (selectionMode() != NoSelection))
817 {
818 if (d->mouseButtonPressed && !d->isDragging)
819 {
820 QPoint start, end, initialPressPosition;
821
822 initialPressPosition = d->initialPressPosition;
823
824 initialPressPosition.setY(initialPressPosition.y() - verticalOffset());
825 initialPressPosition.setX(initialPressPosition.x() - horizontalOffset());
826
827 if (d->initialPressPosition.x() > d->mousePosition.x() ||
828 d->initialPressPosition.y() > d->mousePosition.y())
829 {
830 start = d->mousePosition;
831 end = initialPressPosition;
832 }
833 else
834 {
835 start = initialPressPosition;
836 end = d->mousePosition;
837 }
838
839 QStyleOptionRubberBand yetAnotherOption;
840 yetAnotherOption.initFrom(this);
841 yetAnotherOption.shape = QRubberBand::Rectangle;
842 yetAnotherOption.opaque = false;
843 yetAnotherOption.rect = QRect(start, end).intersected(viewport()->rect().adjusted(-16, -16, 16, 16));
844 painter.save();
845 style()->drawControl(QStyle::CE_RubberBand, &yetAnotherOption, &painter);
846 painter.restore();
847 }
848 }
849
850 if (d->isDragging && !d->dragLeftViewport)
851 {
852 painter.setOpacity(0.5);
853 d->drawDraggedItems(&painter);
854 }
855
856 painter.restore();
857 }
858
859 void KCategorizedView::resizeEvent(QResizeEvent *event)
860 {
861 QListView::resizeEvent(event);
862
863 // Clear the items positions cache
864 d->elementsPosition.clear();
865 d->categoriesPosition.clear();
866 d->forcedSelectionPosition = 0;
867
868 if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
869 {
870 return;
871 }
872
873 d->updateScrollbars();
874 }
875
876 void KCategorizedView::setSelection(const QRect &rect,
877 QItemSelectionModel::SelectionFlags flags)
878 {
879 if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
880 {
881 QListView::setSelection(rect, flags);
882 return;
883 }
884
885 if (!flags)
886 return;
887
888 if (flags & QItemSelectionModel::Clear)
889 {
890 selectionModel()->clear();
891 d->lastSelection.clear();
892 }
893
894 QModelIndexList dirtyIndexes = d->intersectionSet(rect);
895
896 // no items affected, just leave
897 if (!dirtyIndexes.count())
898 {
899 selectionModel()->select(d->lastSelection, QItemSelectionModel::SelectCurrent);
900
901 return;
902 }
903
904 QModelIndex topLeft;
905 QModelIndex bottomRight;
906
907 if (d->mouseButtonPressed || d->rightMouseButtonPressed) // selection with click + drag
908 {
909 QItemSelection selection;
910
911 QModelIndex prev = dirtyIndexes[0];
912 QModelIndex first = prev;
913 foreach (const QModelIndex &index, dirtyIndexes)
914 {
915 // we have a different interval. non-contiguous items
916 if ((index.row() - prev.row()) > 1) {
917 selection << QItemSelectionRange(first, prev);
918
919 first = index;
920 }
921
922 prev = index;
923 }
924
925 selection << QItemSelectionRange(first, prev);
926
927 if (flags & QItemSelectionModel::Current)
928 {
929 if (rect.topLeft() == rect.bottomRight())
930 {
931 selectionModel()->setCurrentIndex(indexAt(rect.topLeft()), QItemSelectionModel::NoUpdate);
932 }
933
934 selection.merge(d->lastSelection, flags);
935 }
936 else
937 {
938 selection.merge(selectionModel()->selection(), flags);
939
940 selectionModel()->select(selection, QItemSelectionModel::SelectCurrent);
941
942 return;
943 }
944
945 selectionModel()->select(selection, flags);
946 }
947 else // selection with click + keyboard keys
948 {
949 QModelIndex topLeftIndex = indexAt(QPoint(rect.topLeft().x(),
950 rect.topLeft().y()));
951 QModelIndex bottomRightIndex = indexAt(QPoint(rect.bottomRight().x(),
952 rect.bottomRight().y()));
953
954 // keyboard selection comes "upside down". Let's normalize it
955 if (topLeftIndex.row() > bottomRightIndex.row())
956 {
957 QModelIndex auxIndex = topLeftIndex;
958 topLeftIndex = bottomRightIndex;
959 bottomRightIndex = auxIndex;
960 }
961
962 int viewportWidth = viewport()->width() - spacing();
963 int itemWidth;
964
965 if (gridSize().isEmpty())
966 {
967 itemWidth = d->biggestItemSize.width();
968 }
969 else
970 {
971 itemWidth = gridSize().width();
972 }
973
974 int itemWidthPlusSeparation = spacing() + itemWidth;
975 if (!itemWidthPlusSeparation)
976 itemWidthPlusSeparation++;
977 int elementsPerRow = viewportWidth / itemWidthPlusSeparation;
978 if (!elementsPerRow)
979 elementsPerRow++;
980
981 QModelIndexList theoricDirty(dirtyIndexes);
982 dirtyIndexes.clear();
983 int first = model()->rowCount();
984 int last = 0;
985
986 foreach (const QModelIndex &index, theoricDirty)
987 {
988 if ((index.row() < first) &&
989 ((((topLeftIndex.row() / elementsPerRow) == (index.row() / elementsPerRow)) &&
990 ((topLeftIndex.row() % elementsPerRow) <= (index.row() % elementsPerRow))) ||
991 (topLeftIndex.row() / elementsPerRow) != (index.row() / elementsPerRow)))
992 {
993 first = index.row();
994 topLeft = index;
995 }
996
997 if ((index.row() > last) &&
998 ((((bottomRightIndex.row() / elementsPerRow) == (index.row() / elementsPerRow)) &&
999 ((bottomRightIndex.row() % elementsPerRow) >= (index.row() % elementsPerRow))) ||
1000 (bottomRightIndex.row() / elementsPerRow) != (index.row() / elementsPerRow)))
1001 {
1002 last = index.row();
1003 bottomRight = index;
1004 }
1005 }
1006
1007 for (int i = first; i <= last; i++)
1008 {
1009 dirtyIndexes << model()->index(i, theoricDirty[0].column(), theoricDirty[0].parent());
1010 }
1011
1012 QItemSelection selection(topLeft, bottomRight);
1013
1014 selectionModel()->select(selection, flags);
1015 }
1016 }
1017
1018 void KCategorizedView::mouseMoveEvent(QMouseEvent *event)
1019 {
1020 QListView::mouseMoveEvent(event);
1021
1022 if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
1023 {
1024 return;
1025 }
1026
1027 QModelIndexList item = d->intersectionSet(QRect(event->pos(), event->pos()));
1028
1029 if (item.count() == 1)
1030 {
1031 d->hovered = item[0];
1032 }
1033 else
1034 {
1035 d->hovered = QModelIndex();
1036 }
1037
1038 const QString previousHoveredCategory = d->hoveredCategory;
1039
1040 d->mousePosition = event->pos();
1041 d->hoveredCategory = QString();
1042
1043 // Redraw categories
1044 foreach (const QString &category, d->categories)
1045 {
1046 if (d->categoryVisualRect(category).intersects(QRect(event->pos(), event->pos())))
1047 {
1048 d->hoveredCategory = category;
1049 viewport()->update(d->categoryVisualRect(category));
1050 }
1051 else if ((category == previousHoveredCategory) &&
1052 (!d->categoryVisualRect(previousHoveredCategory).intersects(QRect(event->pos(), event->pos()))))
1053 {
1054 viewport()->update(d->categoryVisualRect(category));
1055 }
1056 }
1057
1058 QRect rect;
1059 if (d->mouseButtonPressed && !d->isDragging)
1060 {
1061 QPoint start, end, initialPressPosition;
1062
1063 initialPressPosition = d->initialPressPosition;
1064
1065 initialPressPosition.setY(initialPressPosition.y() - verticalOffset());
1066 initialPressPosition.setX(initialPressPosition.x() - horizontalOffset());
1067
1068 if (d->initialPressPosition.x() > d->mousePosition.x() ||
1069 d->initialPressPosition.y() > d->mousePosition.y())
1070 {
1071 start = d->mousePosition;
1072 end = initialPressPosition;
1073 }
1074 else
1075 {
1076 start = initialPressPosition;
1077 end = d->mousePosition;
1078 }
1079
1080 rect = QRect(start, end).adjusted(-16, -16, 16, 16);
1081 rect = rect.united(QRect(start, end).adjusted(16, 16, -16, -16)).intersected(viewport()->rect());
1082
1083 viewport()->update(rect);
1084 }
1085 }
1086
1087 void KCategorizedView::mousePressEvent(QMouseEvent *event)
1088 {
1089 d->dragLeftViewport = false;
1090
1091 if (event->button() == Qt::LeftButton)
1092 {
1093 d->mouseButtonPressed = true;
1094
1095 d->initialPressPosition = event->pos();
1096 d->initialPressPosition.setY(d->initialPressPosition.y() +
1097 verticalOffset());
1098 d->initialPressPosition.setX(d->initialPressPosition.x() +
1099 horizontalOffset());
1100 }
1101 else if (event->button() == Qt::RightButton)
1102 {
1103 d->rightMouseButtonPressed = true;
1104 }
1105
1106 QListView::mousePressEvent(event);
1107
1108 d->lastSelection = selectionModel()->selection();
1109
1110 viewport()->update(d->categoryVisualRect(d->hoveredCategory));
1111 }
1112
1113 void KCategorizedView::mouseReleaseEvent(QMouseEvent *event)
1114 {
1115 d->mouseButtonPressed = false;
1116 d->rightMouseButtonPressed = false;
1117
1118 QListView::mouseReleaseEvent(event);
1119
1120 if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
1121 {
1122 return;
1123 }
1124
1125 QPoint initialPressPosition = viewport()->mapFromGlobal(QCursor::pos());
1126 initialPressPosition.setY(initialPressPosition.y() + verticalOffset());
1127 initialPressPosition.setX(initialPressPosition.x() + horizontalOffset());
1128
1129 if ((selectionMode() != SingleSelection) && (selectionMode() != NoSelection) &&
1130 (initialPressPosition == d->initialPressPosition))
1131 {
1132 foreach(const QString &category, d->categories)
1133 {
1134 if (d->categoryVisualRect(category).contains(event->pos()))
1135 {
1136 QItemSelection selection = selectionModel()->selection();
1137 QModelIndexList indexList = d->categoriesIndexes[category];
1138
1139 foreach (const QModelIndex &index, indexList)
1140 {
1141 QModelIndex selectIndex = index.model()->index(index.row(), 0);
1142
1143 selection << QItemSelectionRange(selectIndex);
1144 }
1145
1146 selectionModel()->select(selection, QItemSelectionModel::SelectCurrent);
1147
1148 break;
1149 }
1150 }
1151 }
1152
1153 QRect rect;
1154 if (!d->isDragging)
1155 {
1156 QPoint start, end, initialPressPosition;
1157
1158 initialPressPosition = d->initialPressPosition;
1159
1160 initialPressPosition.setY(initialPressPosition.y() - verticalOffset());
1161 initialPressPosition.setX(initialPressPosition.x() - horizontalOffset());
1162
1163 if (d->initialPressPosition.x() > d->mousePosition.x() ||
1164 d->initialPressPosition.y() > d->mousePosition.y())
1165 {
1166 start = d->mousePosition;
1167 end = initialPressPosition;
1168 }
1169 else
1170 {
1171 start = initialPressPosition;
1172 end = d->mousePosition;
1173 }
1174
1175 rect = QRect(start, end).adjusted(-16, -16, 16, 16);
1176 rect = rect.united(QRect(start, end).adjusted(16, 16, -16, -16)).intersected(viewport()->rect());
1177
1178 viewport()->update(rect);
1179 }
1180
1181 if (d->hovered.isValid())
1182 viewport()->update(visualRect(d->hovered));
1183 else if (!d->hoveredCategory.isEmpty())
1184 viewport()->update(d->categoryVisualRect(d->hoveredCategory));
1185 }
1186
1187 void KCategorizedView::leaveEvent(QEvent *event)
1188 {
1189 d->hovered = QModelIndex();
1190 d->hoveredCategory = QString();
1191
1192 QListView::leaveEvent(event);
1193 }
1194
1195 void KCategorizedView::startDrag(Qt::DropActions supportedActions)
1196 {
1197 // FIXME: QAbstractItemView does far better here since it sets the
1198 // pixmap of selected icons to the dragging cursor, but it sets a non
1199 // ARGB window so it is no transparent. Use QAbstractItemView when
1200 // this is fixed on Qt.
1201 // QAbstractItemView::startDrag(supportedActions);
1202 #if !defined(DOLPHIN_DRAGANDDROP)
1203 QListView::startDrag(supportedActions);
1204 #endif
1205
1206 d->isDragging = false;
1207 d->mouseButtonPressed = false;
1208 d->rightMouseButtonPressed = false;
1209
1210 viewport()->update(d->lastDraggedItemsRect);
1211 }
1212
1213 void KCategorizedView::dragMoveEvent(QDragMoveEvent *event)
1214 {
1215 d->mousePosition = event->pos();
1216
1217 if (d->mouseButtonPressed)
1218 {
1219 d->isDragging = true;
1220 }
1221 else
1222 {
1223 d->isDragging = false;
1224 }
1225
1226 d->dragLeftViewport = false;
1227
1228 #if defined(DOLPHIN_DRAGANDDROP)
1229 QAbstractItemView::dragMoveEvent(event);
1230 #else
1231 QListView::dragMoveEvent(event);
1232 #endif
1233
1234 if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
1235 {
1236 return;
1237 }
1238
1239 d->drawDraggedItems();
1240 }
1241
1242 void KCategorizedView::dragLeaveEvent(QDragLeaveEvent *event)
1243 {
1244 d->dragLeftViewport = true;
1245
1246 #if defined(DOLPHIN_DRAGANDDROP)
1247 QAbstractItemView::dragLeaveEvent(event);
1248 #else
1249 QListView::dragLeaveEvent(event);
1250 #endif
1251 }
1252
1253 void KCategorizedView::dropEvent(QDropEvent *event)
1254 {
1255 #if defined(DOLPHIN_DRAGANDDROP)
1256 QAbstractItemView::dropEvent(event);
1257 #else
1258 QListView::dropEvent(event);
1259 #endif
1260 }
1261
1262 QModelIndex KCategorizedView::moveCursor(CursorAction cursorAction,
1263 Qt::KeyboardModifiers modifiers)
1264 {
1265 if ((viewMode() != KCategorizedView::IconMode) ||
1266 !d->proxyModel ||
1267 !d->categoryDrawer ||
1268 d->categories.isEmpty() ||
1269 !d->proxyModel->isCategorizedModel())
1270 {
1271 return QListView::moveCursor(cursorAction, modifiers);
1272 }
1273
1274 int viewportWidth = viewport()->width() - spacing();
1275 int itemWidth;
1276
1277 if (gridSize().isEmpty())
1278 {
1279 itemWidth = d->biggestItemSize.width();
1280 }
1281 else
1282 {
1283 itemWidth = gridSize().width();
1284 }
1285
1286 int itemWidthPlusSeparation = spacing() + itemWidth;
1287 if (!itemWidthPlusSeparation)
1288 itemWidthPlusSeparation++;
1289 int elementsPerRow = viewportWidth / itemWidthPlusSeparation;
1290 if (!elementsPerRow)
1291 elementsPerRow++;
1292
1293 QModelIndex current = selectionModel()->currentIndex();
1294
1295 if (!current.isValid())
1296 {
1297 if (cursorAction == MoveEnd)
1298 {
1299 current = model()->index(model()->rowCount() - 1, 0, QModelIndex());
1300 d->forcedSelectionPosition = d->elementsInfo[current.row()].relativeOffsetToCategory % elementsPerRow;
1301 }
1302 else
1303 {
1304 current = model()->index(0, 0, QModelIndex());
1305 d->forcedSelectionPosition = 0;
1306 }
1307
1308 return current;
1309 }
1310 else if (!current.isValid())
1311 {
1312 return QModelIndex();
1313 }
1314
1315 QString lastCategory = d->categories.first();
1316 QString theCategory = d->categories.first();
1317 QString afterCategory = d->categories.first();
1318
1319 bool hasToBreak = false;
1320 foreach (const QString &category, d->categories)
1321 {
1322 if (hasToBreak)
1323 {
1324 afterCategory = category;
1325
1326 break;
1327 }
1328
1329 if (category == d->elementsInfo[current.row()].category)
1330 {
1331 theCategory = category;
1332
1333 hasToBreak = true;
1334 }
1335
1336 if (!hasToBreak)
1337 {
1338 lastCategory = category;
1339 }
1340 }
1341
1342 switch (cursorAction)
1343 {
1344 case QAbstractItemView::MoveUp: {
1345 if (d->elementsInfo[current.row()].relativeOffsetToCategory >= elementsPerRow)
1346 {
1347 int indexToMove = current.row();
1348 indexToMove -= qMin(((d->elementsInfo[current.row()].relativeOffsetToCategory) + d->forcedSelectionPosition), elementsPerRow - d->forcedSelectionPosition + (d->elementsInfo[current.row()].relativeOffsetToCategory % elementsPerRow));
1349
1350 return d->proxyModel->index(indexToMove, 0);
1351 }
1352 else
1353 {
1354 int lastCategoryLastRow = (d->categoriesIndexes[lastCategory].count() - 1) % elementsPerRow;
1355 int indexToMove = current.row() - d->elementsInfo[current.row()].relativeOffsetToCategory;
1356
1357 if (d->forcedSelectionPosition >= lastCategoryLastRow)
1358 {
1359 indexToMove -= 1;
1360 }
1361 else
1362 {
1363 indexToMove -= qMin((lastCategoryLastRow - d->forcedSelectionPosition + 1), d->forcedSelectionPosition + elementsPerRow + 1);
1364 }
1365
1366 return d->proxyModel->index(indexToMove, 0);
1367 }
1368 }
1369
1370 case QAbstractItemView::MoveDown: {
1371 if (d->elementsInfo[current.row()].relativeOffsetToCategory < (d->categoriesIndexes[theCategory].count() - 1 - ((d->categoriesIndexes[theCategory].count() - 1) % elementsPerRow)))
1372 {
1373 int indexToMove = current.row();
1374 indexToMove += qMin(elementsPerRow, d->categoriesIndexes[theCategory].count() - 1 - d->elementsInfo[current.row()].relativeOffsetToCategory);
1375
1376 return d->proxyModel->index(indexToMove, 0);
1377 }
1378 else
1379 {
1380 int afterCategoryLastRow = qMin(elementsPerRow, d->categoriesIndexes[afterCategory].count());
1381 int indexToMove = current.row() + (d->categoriesIndexes[theCategory].count() - d->elementsInfo[current.row()].relativeOffsetToCategory);
1382
1383 if (d->forcedSelectionPosition >= afterCategoryLastRow)
1384 {
1385 indexToMove += afterCategoryLastRow - 1;
1386 }
1387 else
1388 {
1389 indexToMove += qMin(d->forcedSelectionPosition, elementsPerRow);
1390 }
1391
1392 return d->proxyModel->index(indexToMove, 0);
1393 }
1394 }
1395
1396 case QAbstractItemView::MoveLeft:
1397 if (layoutDirection() == Qt::RightToLeft)
1398 {
1399 if (!(d->elementsInfo[current.row() + 1].relativeOffsetToCategory % elementsPerRow))
1400 return current;
1401
1402 d->forcedSelectionPosition = d->elementsInfo[current.row() + 1].relativeOffsetToCategory % elementsPerRow;
1403
1404 #if 0 //follow qt view behavior. lateral movements won't change visual row
1405 if (d->forcedSelectionPosition < 0)
1406 d->forcedSelectionPosition = (d->categoriesIndexes[theCategory].count() - 1) % elementsPerRow;
1407 #endif
1408
1409 return d->proxyModel->index(current.row() + 1, 0);
1410 }
1411
1412 if (!(d->elementsInfo[current.row()].relativeOffsetToCategory % elementsPerRow))
1413 return current;
1414
1415 d->forcedSelectionPosition = d->elementsInfo[current.row() - 1].relativeOffsetToCategory % elementsPerRow;
1416
1417 #if 0 //follow qt view behavior. lateral movements won't change visual row
1418 if (d->forcedSelectionPosition < 0)
1419 d->forcedSelectionPosition = (d->categoriesIndexes[theCategory].count() - 1) % elementsPerRow;
1420 #endif
1421
1422 return d->proxyModel->index(current.row() - 1, 0);
1423
1424 case QAbstractItemView::MoveRight:
1425 if (layoutDirection() == Qt::RightToLeft)
1426 {
1427 if (!(d->elementsInfo[current.row()].relativeOffsetToCategory % elementsPerRow))
1428 return current;
1429
1430 d->forcedSelectionPosition = d->elementsInfo[current.row() - 1].relativeOffsetToCategory % elementsPerRow;
1431
1432 #if 0 //follow qt view behavior. lateral movements won't change visual row
1433 if (d->forcedSelectionPosition < 0)
1434 d->forcedSelectionPosition = (d->categoriesIndexes[theCategory].count() - 1) % elementsPerRow;
1435 #endif
1436
1437 return d->proxyModel->index(current.row() - 1, 0);
1438 }
1439
1440 if (!(d->elementsInfo[current.row() + 1].relativeOffsetToCategory % elementsPerRow))
1441 return current;
1442
1443 d->forcedSelectionPosition = d->elementsInfo[current.row() + 1].relativeOffsetToCategory % elementsPerRow;
1444
1445 #if 0 //follow qt view behavior. lateral movements won't change visual row
1446 if (d->forcedSelectionPosition < 0)
1447 d->forcedSelectionPosition = (d->categoriesIndexes[theCategory].count() - 1) % elementsPerRow;
1448 #endif
1449
1450 return d->proxyModel->index(current.row() + 1, 0);
1451
1452 default:
1453 break;
1454 }
1455
1456 return QListView::moveCursor(cursorAction, modifiers);
1457 }
1458
1459 void KCategorizedView::rowsInserted(const QModelIndex &parent,
1460 int start,
1461 int end)
1462 {
1463 QListView::rowsInserted(parent, start, end);
1464
1465 if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
1466 {
1467 d->forcedSelectionPosition = 0;
1468 d->elementsInfo.clear();
1469 d->elementsPosition.clear();
1470 d->categoriesIndexes.clear();
1471 d->categoriesPosition.clear();
1472 d->categories.clear();
1473 d->intersectedIndexes.clear();
1474 d->modelIndexList.clear();
1475 d->hovered = QModelIndex();
1476 d->biggestItemSize = QSize(0, 0);
1477 d->mouseButtonPressed = false;
1478 d->rightMouseButtonPressed = false;
1479
1480 return;
1481 }
1482
1483 rowsInsertedArtifficial(parent, start, end);
1484 }
1485
1486 void KCategorizedView::rowsInsertedArtifficial(const QModelIndex &parent,
1487 int start,
1488 int end)
1489 {
1490 Q_UNUSED(parent);
1491
1492 d->forcedSelectionPosition = 0;
1493 d->elementsInfo.clear();
1494 d->elementsPosition.clear();
1495 d->categoriesIndexes.clear();
1496 d->categoriesPosition.clear();
1497 d->categories.clear();
1498 d->intersectedIndexes.clear();
1499 d->modelIndexList.clear();
1500 d->hovered = QModelIndex();
1501 d->biggestItemSize = QSize(0, 0);
1502 d->mouseButtonPressed = false;
1503 d->rightMouseButtonPressed = false;
1504
1505 if (start > end || end < 0 || start < 0 || !d->proxyModel->rowCount())
1506 {
1507 return;
1508 }
1509
1510 // Add all elements mapped to the source model and explore categories
1511 QString prevCategory = d->proxyModel->data(d->proxyModel->index(0, d->proxyModel->sortColumn()), KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString();
1512 QString lastCategory = prevCategory;
1513 QModelIndexList modelIndexList;
1514 struct Private::ElementInfo elementInfo;
1515 int offset = -1;
1516 for (int k = 0; k < d->proxyModel->rowCount(); ++k)
1517 {
1518 QModelIndex index = d->proxyModel->index(k, d->proxyModel->sortColumn());
1519 QModelIndex indexSize = d->proxyModel->index(k, 0);
1520
1521 d->biggestItemSize = QSize(qMax(sizeHintForIndex(indexSize).width(),
1522 d->biggestItemSize.width()),
1523 qMax(sizeHintForIndex(indexSize).height(),
1524 d->biggestItemSize.height()));
1525
1526 d->modelIndexList << index;
1527
1528 lastCategory = d->proxyModel->data(index, KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString();
1529
1530 elementInfo.category = lastCategory;
1531
1532 if (prevCategory != lastCategory)
1533 {
1534 offset = 0;
1535 d->categoriesIndexes.insert(prevCategory, modelIndexList);
1536 d->categories << prevCategory;
1537 modelIndexList.clear();
1538 }
1539 else
1540 {
1541 offset++;
1542 }
1543
1544 elementInfo.relativeOffsetToCategory = offset;
1545
1546 modelIndexList << index;
1547 prevCategory = lastCategory;
1548
1549 d->elementsInfo.insert(index.row(), elementInfo);
1550 }
1551
1552 d->categoriesIndexes.insert(prevCategory, modelIndexList);
1553 d->categories << prevCategory;
1554
1555 d->updateScrollbars();
1556
1557 // FIXME: We need to safely save the last selection. This is on my TODO
1558 // list (ereslibre).
1559 selectionModel()->clear();
1560 }
1561
1562 void KCategorizedView::rowsRemoved(const QModelIndex &parent,
1563 int start,
1564 int end)
1565 {
1566 if (d->proxyModel && d->categoryDrawer && d->proxyModel->isCategorizedModel())
1567 {
1568 // Force the view to update all elements
1569 rowsInsertedArtifficial(QModelIndex(), 0, d->proxyModel->rowCount() - 1);
1570 }
1571 }
1572
1573 void KCategorizedView::updateGeometries()
1574 {
1575 if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
1576 {
1577 QListView::updateGeometries();
1578 return;
1579 }
1580
1581 // Avoid QListView::updateGeometries(), since it will try to set another
1582 // range to our scroll bars, what we don't want (ereslibre)
1583 QAbstractItemView::updateGeometries();
1584 }
1585
1586 void KCategorizedView::slotLayoutChanged()
1587 {
1588 d->layoutChanged();
1589 }
1590
1591 void KCategorizedView::currentChanged(const QModelIndex &current,
1592 const QModelIndex &previous)
1593 {
1594 // We need to update the forcedSelectionPosition property in order to correctly
1595 // navigate after with keyboard using up & down keys
1596
1597 int viewportWidth = viewport()->width() - spacing();
1598
1599 int itemHeight;
1600 int itemWidth;
1601
1602 if (gridSize().isEmpty())
1603 {
1604 itemHeight = d->biggestItemSize.height();
1605 itemWidth = d->biggestItemSize.width();
1606 }
1607 else
1608 {
1609 itemHeight = gridSize().height();
1610 itemWidth = gridSize().width();
1611 }
1612
1613 int itemWidthPlusSeparation = spacing() + itemWidth;
1614 if (!itemWidthPlusSeparation)
1615 itemWidthPlusSeparation++;
1616 int elementsPerRow = viewportWidth / itemWidthPlusSeparation;
1617 if (!elementsPerRow)
1618 elementsPerRow++;
1619
1620 if (d->mouseButtonPressed || d->rightMouseButtonPressed)
1621 d->forcedSelectionPosition = d->elementsInfo[current.row()].relativeOffsetToCategory % elementsPerRow;
1622
1623 QListView::currentChanged(current, previous);
1624 }
1625
1626 #include "kcategorizedview.moc"