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