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