]> cloud.milkyroute.net Git - dolphin.git/blob - src/klistview.cpp
fix signal foundMountPoint()
[dolphin.git] / src / klistview.cpp
1 /**
2 * This file is part of the KDE project
3 * Copyright (C) 2007 Rafael Fernández López <ereslibre@gmail.com>
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 "klistview.h"
22 #include "klistview_p.h"
23
24 #include <math.h> // trunc
25
26 #include <QApplication>
27 #include <QPainter>
28 #include <QScrollBar>
29 #include <QPaintEvent>
30
31 #include <kdebug.h>
32 #include <kstyle.h>
33
34 #include "kitemcategorizer.h"
35 #include "ksortfilterproxymodel.h"
36
37 class LessThan
38 {
39 public:
40 enum Purpose
41 {
42 GeneralPurpose = 0,
43 CategoryPurpose
44 };
45
46 inline LessThan(const KSortFilterProxyModel *proxyModel,
47 Purpose purpose)
48 : proxyModel(proxyModel)
49 , purpose(purpose)
50 {
51 }
52
53 inline bool operator()(const QModelIndex &left,
54 const QModelIndex &right) const
55 {
56 if (purpose == GeneralPurpose)
57 {
58 return proxyModel->lessThanGeneralPurpose(left, right);
59 }
60
61 return proxyModel->lessThanCategoryPurpose(left, right);
62 }
63
64 private:
65 const KSortFilterProxyModel *proxyModel;
66 const Purpose purpose;
67 };
68
69
70 //==============================================================================
71
72
73 KListView::Private::Private(KListView *listView)
74 : listView(listView)
75 , itemCategorizer(0)
76 , mouseButtonPressed(false)
77 , proxyModel(0)
78 , lastIndex(QModelIndex())
79 {
80 }
81
82 KListView::Private::~Private()
83 {
84 }
85
86 const QModelIndexList &KListView::Private::intersectionSet(const QRect &rect)
87 {
88 QModelIndex index;
89 QRect indexVisualRect;
90
91 intersectedIndexes.clear();
92
93 // Lets find out where we should start
94 int top = proxyModel->rowCount() - 1;
95 int bottom = 0;
96 int middle = (top + bottom) / 2;
97 while (bottom <= top)
98 {
99 middle = (top + bottom) / 2;
100
101 index = elementDictionary[proxyModel->index(middle, 0)];
102 indexVisualRect = visualRect(index);
103
104 if (qMax(indexVisualRect.topLeft().y(),
105 indexVisualRect.bottomRight().y()) < qMin(rect.topLeft().y(),
106 rect.bottomRight().y()))
107 {
108 bottom = middle + 1;
109 }
110 else
111 {
112 top = middle - 1;
113 }
114 }
115
116 int j = 0;
117 for (int i = middle; i < proxyModel->rowCount(); i++)
118 {
119 index = elementDictionary[proxyModel->index(i, 0)];
120 indexVisualRect = visualRect(index);
121
122 if (rect.intersects(indexVisualRect))
123 intersectedIndexes.append(index);
124
125 // If we passed next item, stop searching for hits
126 if (qMax(rect.bottomRight().y(), rect.topLeft().y()) <
127 indexVisualRect.topLeft().y())
128 break;
129
130 j++;
131 }
132
133 return intersectedIndexes;
134 }
135
136 QRect KListView::Private::visualRectInViewport(const QModelIndex &index) const
137 {
138 if (!index.isValid())
139 return QRect();
140
141 QString curCategory = elementsInfo[index].category;
142
143 QRect retRect(listView->spacing(), listView->spacing() * 2 +
144 30 /* categoryHeight */, 0, 0);
145
146 int viewportWidth = listView->viewport()->width() - listView->spacing();
147
148 // We really need all items to be of same size. Otherwise we cannot do this
149 // (ereslibre)
150 // QSize itemSize =
151 // listView->sizeHintForIndex(proxyModel->mapFromSource(index));
152 // int itemHeight = itemSize.height();
153 // int itemWidth = itemSize.width();*/
154 int itemHeight = 107;
155 int itemWidth = 130;
156 int itemWidthPlusSeparation = listView->spacing() + itemWidth;
157 int elementsPerRow = viewportWidth / itemWidthPlusSeparation;
158 if (!elementsPerRow)
159 elementsPerRow++;
160
161 int column = elementsInfo[index].relativeOffsetToCategory % elementsPerRow;
162 int row = elementsInfo[index].relativeOffsetToCategory / elementsPerRow;
163
164 retRect.setLeft(retRect.left() + column * listView->spacing() +
165 column * itemWidth);
166
167 float rows;
168 int rowsInt;
169 foreach (const QString &category, categories)
170 {
171 if (category == curCategory)
172 break;
173
174 rows = (float) ((float) categoriesIndexes[category].count() /
175 (float) elementsPerRow);
176 rowsInt = categoriesIndexes[category].count() / elementsPerRow;
177
178 if (rows - trunc(rows)) rowsInt++;
179
180 retRect.setTop(retRect.top() +
181 (rowsInt * listView->spacing()) +
182 (rowsInt * itemHeight) +
183 30 /* categoryHeight */ +
184 listView->spacing() * 2);
185 }
186
187 retRect.setTop(retRect.top() + row * listView->spacing() +
188 row * itemHeight);
189
190 retRect.setWidth(itemWidth);
191 retRect.setHeight(itemHeight);
192
193 return retRect;
194 }
195
196 QRect KListView::Private::visualCategoryRectInViewport(const QString &category)
197 const
198 {
199 QRect retRect(listView->spacing(),
200 listView->spacing(),
201 listView->viewport()->width() - listView->spacing() * 2,
202 0);
203
204 if (!proxyModel->rowCount() || !categories.contains(category))
205 return QRect();
206
207 QModelIndex index = proxyModel->index(0, 0, QModelIndex());
208
209 int viewportWidth = listView->viewport()->width() - listView->spacing();
210
211 // We really need all items to be of same size. Otherwise we cannot do this
212 // (ereslibre)
213 // QSize itemSize = listView->sizeHintForIndex(index);
214 // int itemHeight = itemSize.height();
215 // int itemWidth = itemSize.width();
216 int itemHeight = 107;
217 int itemWidth = 130;
218 int itemWidthPlusSeparation = listView->spacing() + itemWidth;
219 int elementsPerRow = viewportWidth / itemWidthPlusSeparation;
220
221 if (!elementsPerRow)
222 elementsPerRow++;
223
224 float rows;
225 int rowsInt;
226 foreach (const QString &itCategory, categories)
227 {
228 if (itCategory == category)
229 break;
230
231 rows = (float) ((float) categoriesIndexes[itCategory].count() /
232 (float) elementsPerRow);
233 rowsInt = categoriesIndexes[itCategory].count() / elementsPerRow;
234
235 if (rows - trunc(rows)) rowsInt++;
236
237 retRect.setTop(retRect.top() +
238 (rowsInt * listView->spacing()) +
239 (rowsInt * itemHeight) +
240 30 /* categoryHeight */ +
241 listView->spacing() * 2);
242 }
243
244 retRect.setHeight(30 /* categoryHeight */);
245
246 return retRect;
247 }
248
249 // We're sure elementsPosition doesn't contain index
250 const QRect &KListView::Private::cacheIndex(const QModelIndex &index)
251 {
252 QRect rect = visualRectInViewport(index);
253 elementsPosition[index] = rect;
254
255 return elementsPosition[index];
256 }
257
258 // We're sure categoriesPosition doesn't contain category
259 const QRect &KListView::Private::cacheCategory(const QString &category)
260 {
261 QRect rect = visualCategoryRectInViewport(category);
262 categoriesPosition[category] = rect;
263
264 return categoriesPosition[category];
265 }
266
267 const QRect &KListView::Private::cachedRectIndex(const QModelIndex &index)
268 {
269 if (elementsPosition.contains(index)) // If we have it cached
270 { // return it
271 return elementsPosition[index];
272 }
273 else // Otherwise, cache it
274 { // and return it
275 return cacheIndex(index);
276 }
277 }
278
279 const QRect &KListView::Private::cachedRectCategory(const QString &category)
280 {
281 if (categoriesPosition.contains(category)) // If we have it cached
282 { // return it
283 return categoriesPosition[category];
284 }
285 else // Otherwise, cache it and
286 { // return it
287 return cacheCategory(category);
288 }
289 }
290
291 QRect KListView::Private::visualRect(const QModelIndex &index)
292 {
293 QModelIndex mappedIndex = proxyModel->mapToSource(index);
294
295 QRect retRect = cachedRectIndex(mappedIndex);
296 int dx = -listView->horizontalOffset();
297 int dy = -listView->verticalOffset();
298 retRect.adjust(dx, dy, dx, dy);
299
300 return retRect;
301 }
302
303 QRect KListView::Private::categoryVisualRect(const QString &category)
304 {
305 QRect retRect = cachedRectCategory(category);
306 int dx = -listView->horizontalOffset();
307 int dy = -listView->verticalOffset();
308 retRect.adjust(dx, dy, dx, dy);
309
310 return retRect;
311 }
312
313 void KListView::Private::drawNewCategory(const QString &category,
314 const QStyleOption &option,
315 QPainter *painter)
316 {
317 QColor color = option.palette.color(QPalette::Text);
318
319 painter->save();
320 painter->setRenderHint(QPainter::Antialiasing);
321
322 QStyleOptionButton opt;
323
324 opt.rect = option.rect;
325 opt.palette = option.palette;
326 opt.direction = option.direction;
327 opt.text = category;
328
329 if (option.rect.contains(listView->viewport()->mapFromGlobal(QCursor::pos())) &&
330 !mouseButtonPressed)
331 {
332 const QPalette::ColorGroup group =
333 option.state & QStyle::State_Enabled ?
334 QPalette::Normal : QPalette::Disabled;
335
336 QLinearGradient gradient(option.rect.topLeft(),
337 option.rect.bottomRight());
338 gradient.setColorAt(0,
339 option.palette.color(group,
340 QPalette::Highlight).light());
341 gradient.setColorAt(1, Qt::transparent);
342
343 painter->fillRect(option.rect, gradient);
344 }
345
346 /*if (const KStyle *style = dynamic_cast<const KStyle*>(QApplication::style()))
347 {
348 style->drawControl(KStyle::CE_Category, &opt, painter, this);
349 }
350 else
351 {*/
352 QFont painterFont = painter->font();
353 painterFont.setWeight(QFont::Bold);
354 QFontMetrics metrics(painterFont);
355 painter->setFont(painterFont);
356
357 QPainterPath path;
358 path.addRect(option.rect.left(),
359 option.rect.bottom() - 2,
360 option.rect.width(),
361 2);
362
363 QLinearGradient gradient(option.rect.topLeft(),
364 option.rect.bottomRight());
365 gradient.setColorAt(0, color);
366 gradient.setColorAt(1, Qt::transparent);
367
368 painter->setBrush(gradient);
369 painter->fillPath(path, gradient);
370
371 painter->setPen(color);
372
373 painter->drawText(option.rect, Qt::AlignVCenter | Qt::AlignLeft,
374 metrics.elidedText(category, Qt::ElideRight, option.rect.width()));
375 //}
376 painter->restore();
377 }
378
379
380 void KListView::Private::updateScrollbars()
381 {
382 int lastItemBottom = cachedRectIndex(lastIndex).bottom() +
383 listView->spacing() - listView->viewport()->height();
384 listView->verticalScrollBar()->setRange(0, lastItemBottom);
385 }
386
387
388 //==============================================================================
389
390
391 KListView::KListView(QWidget *parent)
392 : QListView(parent)
393 , d(new Private(this))
394 {
395 }
396
397 KListView::~KListView()
398 {
399 delete d;
400 }
401
402 void KListView::setModel(QAbstractItemModel *model)
403 {
404 if (d->proxyModel)
405 {
406 QObject::disconnect(d->proxyModel,
407 SIGNAL(rowsRemoved(QModelIndex,int,int)),
408 this, SLOT(rowsRemoved(QModelIndex,int,int)));
409
410 QObject::disconnect(d->proxyModel,
411 SIGNAL(sortingRoleChanged()),
412 this, SLOT(slotSortingRoleChanged()));
413 }
414
415 QListView::setModel(model);
416
417 d->proxyModel = dynamic_cast<KSortFilterProxyModel*>(model);
418
419 if (d->proxyModel)
420 {
421 QObject::connect(d->proxyModel,
422 SIGNAL(rowsRemoved(QModelIndex,int,int)),
423 this, SLOT(rowsRemoved(QModelIndex,int,int)));
424
425 QObject::connect(d->proxyModel,
426 SIGNAL(sortingRoleChanged()),
427 this, SLOT(slotSortingRoleChanged()));
428 }
429 }
430
431 QRect KListView::visualRect(const QModelIndex &index) const
432 {
433 if ((viewMode() == KListView::ListMode) || !d->proxyModel ||
434 !d->itemCategorizer)
435 {
436 return QListView::visualRect(index);
437 }
438
439 if (!qobject_cast<const QSortFilterProxyModel*>(index.model()))
440 {
441 return d->visualRect(d->proxyModel->mapFromSource(index));
442 }
443
444 return d->visualRect(index);
445 }
446
447 KItemCategorizer *KListView::itemCategorizer() const
448 {
449 return d->itemCategorizer;
450 }
451
452 void KListView::setItemCategorizer(KItemCategorizer *itemCategorizer)
453 {
454 if (!itemCategorizer && d->proxyModel)
455 {
456 QObject::disconnect(d->proxyModel,
457 SIGNAL(rowsRemoved(QModelIndex,int,int)),
458 this, SLOT(rowsRemoved(QModelIndex,int,int)));
459
460 QObject::disconnect(d->proxyModel,
461 SIGNAL(sortingRoleChanged()),
462 this, SLOT(slotSortingRoleChanged()));
463 }
464 else if (itemCategorizer && d->proxyModel)
465 {
466 QObject::connect(d->proxyModel,
467 SIGNAL(rowsRemoved(QModelIndex,int,int)),
468 this, SLOT(rowsRemoved(QModelIndex,int,int)));
469
470 QObject::connect(d->proxyModel,
471 SIGNAL(sortingRoleChanged()),
472 this, SLOT(slotSortingRoleChanged()));
473 }
474
475 d->itemCategorizer = itemCategorizer;
476
477 if (itemCategorizer)
478 {
479 rowsInserted(QModelIndex(), 0, d->proxyModel->rowCount() - 1);
480 }
481 }
482
483 QModelIndex KListView::indexAt(const QPoint &point) const
484 {
485 if ((viewMode() == KListView::ListMode) || !d->proxyModel ||
486 !d->itemCategorizer)
487 {
488 return QListView::indexAt(point);
489 }
490
491 QModelIndex index;
492
493 QModelIndexList item = d->intersectionSet(QRect(point, point));
494
495 if (item.count() == 1)
496 {
497 index = item[0];
498 }
499
500 d->hovered = index;
501
502 return index;
503 }
504
505 void KListView::reset()
506 {
507 QListView::reset();
508
509 if ((viewMode() == KListView::ListMode) || !d->proxyModel ||
510 !d->itemCategorizer)
511 {
512 return;
513 }
514
515 d->elementsInfo.clear();
516 d->elementsPosition.clear();
517 d->elementDictionary.clear();
518 d->categoriesIndexes.clear();
519 d->categoriesPosition.clear();
520 d->categories.clear();
521 d->intersectedIndexes.clear();
522 d->sourceModelIndexList.clear();
523 d->hovered = QModelIndex();
524 d->mouseButtonPressed = false;
525 }
526
527 void KListView::paintEvent(QPaintEvent *event)
528 {
529 if ((viewMode() == KListView::ListMode) || !d->proxyModel ||
530 !d->itemCategorizer)
531 {
532 QListView::paintEvent(event);
533 return;
534 }
535
536 QStyleOptionViewItemV3 option = viewOptions();
537 QPainter painter(viewport());
538 QRect area = event->rect();
539 const bool focus = (hasFocus() || viewport()->hasFocus()) &&
540 currentIndex().isValid();
541 const QStyle::State state = option.state;
542 const bool enabled = (state & QStyle::State_Enabled) != 0;
543
544 painter.save();
545
546 QModelIndexList dirtyIndexes = d->intersectionSet(area);
547 foreach (const QModelIndex &index, dirtyIndexes)
548 {
549 option.state = state;
550 option.rect = d->visualRect(index);
551
552 if (selectionModel() && selectionModel()->isSelected(index))
553 {
554 option.state |= QStyle::State_Selected;
555 }
556
557 if (enabled)
558 {
559 QPalette::ColorGroup cg;
560 if ((d->proxyModel->flags(index) & Qt::ItemIsEnabled) == 0)
561 {
562 option.state &= ~QStyle::State_Enabled;
563 cg = QPalette::Disabled;
564 }
565 else
566 {
567 cg = QPalette::Normal;
568 }
569 option.palette.setCurrentColorGroup(cg);
570 }
571
572 if (focus && currentIndex() == index)
573 {
574 option.state |= QStyle::State_HasFocus;
575 if (this->state() == EditingState)
576 option.state |= QStyle::State_Editing;
577 }
578
579 if ((index == d->hovered) && !d->mouseButtonPressed)
580 option.state |= QStyle::State_MouseOver;
581 else
582 option.state &= ~QStyle::State_MouseOver;
583
584 itemDelegate(index)->paint(&painter, option, index);
585 }
586
587 // Redraw categories
588 QStyleOptionViewItem otherOption;
589 foreach (const QString &category, d->categories)
590 {
591 otherOption = option;
592 otherOption.rect = d->categoryVisualRect(category);
593
594 if (otherOption.rect.intersects(area))
595 {
596 d->drawNewCategory(category, otherOption, &painter);
597 }
598 }
599
600 if (d->mouseButtonPressed)
601 {
602 QPoint start, end, initialPressPosition;
603
604 initialPressPosition = d->initialPressPosition;
605
606 initialPressPosition.setY(initialPressPosition.y() - verticalOffset());
607 initialPressPosition.setX(initialPressPosition.x() - horizontalOffset());
608
609 if (d->initialPressPosition.x() > d->mousePosition.x() ||
610 d->initialPressPosition.y() > d->mousePosition.y())
611 {
612 start = d->mousePosition;
613 end = initialPressPosition;
614 }
615 else
616 {
617 start = initialPressPosition;
618 end = d->mousePosition;
619 }
620
621 QStyleOptionRubberBand yetAnotherOption;
622 yetAnotherOption.initFrom(this);
623 yetAnotherOption.shape = QRubberBand::Rectangle;
624 yetAnotherOption.opaque = false;
625 yetAnotherOption.rect = QRect(start, end).intersected(viewport()->rect().adjusted(-16, -16, 16, 16));
626 painter.save();
627 style()->drawControl(QStyle::CE_RubberBand, &yetAnotherOption, &painter);
628 painter.restore();
629 }
630
631 painter.restore();
632 }
633
634 void KListView::resizeEvent(QResizeEvent *event)
635 {
636 QListView::resizeEvent(event);
637
638 if ((viewMode() == KListView::ListMode) || !d->proxyModel ||
639 !d->itemCategorizer)
640 {
641 return;
642 }
643
644 // Clear the items positions cache
645 d->elementsPosition.clear();
646 d->categoriesPosition.clear();
647
648 d->updateScrollbars();
649 }
650
651 void KListView::setSelection(const QRect &rect,
652 QItemSelectionModel::SelectionFlags flags)
653 {
654 if ((viewMode() == KListView::ListMode) || !d->proxyModel ||
655 !d->itemCategorizer)
656 {
657 QListView::setSelection(rect, flags);
658 return;
659 }
660
661 // FIXME: I have to rethink and rewrite this method (ereslibre)
662
663 QModelIndexList dirtyIndexes = d->intersectionSet(rect);
664 foreach (const QModelIndex &index, dirtyIndexes)
665 {
666 if (!d->mouseButtonPressed && rect.intersects(visualRect(index)))
667 {
668 selectionModel()->select(index, flags);
669 }
670 else
671 {
672 selectionModel()->select(index, QItemSelectionModel::Select);
673
674 if (d->mouseButtonPressed)
675 d->tempSelected.append(index);
676 }
677 }
678
679 if (d->mouseButtonPressed)
680 {
681 foreach (const QModelIndex &index, selectionModel()->selectedIndexes())
682 {
683 if (!rect.intersects(visualRect(index)))
684 {
685 selectionModel()->select(index, QItemSelectionModel::Deselect);
686
687 if (d->mouseButtonPressed)
688 {
689 d->tempSelected.removeAll(index);
690 }
691 }
692 }
693 }
694 }
695
696 void KListView::mouseMoveEvent(QMouseEvent *event)
697 {
698 QListView::mouseMoveEvent(event);
699
700 d->mousePosition = event->pos();
701
702 if ((viewMode() == KListView::ListMode) || !d->proxyModel ||
703 !d->itemCategorizer)
704 {
705 return;
706 }
707
708 event->accept();
709
710 viewport()->update();
711 }
712
713 void KListView::mousePressEvent(QMouseEvent *event)
714 {
715 QListView::mousePressEvent(event);
716
717 d->tempSelected.clear();
718
719 if ((viewMode() == KListView::ListMode) || !d->proxyModel ||
720 !d->itemCategorizer)
721 {
722 return;
723 }
724
725 event->accept();
726
727 if (event->button() == Qt::LeftButton)
728 {
729 d->mouseButtonPressed = true;
730
731 d->initialPressPosition = event->pos();
732 d->initialPressPosition.setY(d->initialPressPosition.y() +
733 verticalOffset());
734 d->initialPressPosition.setX(d->initialPressPosition.x() +
735 horizontalOffset());
736 }
737
738 viewport()->update();
739 }
740
741 void KListView::mouseReleaseEvent(QMouseEvent *event)
742 {
743 QListView::mouseReleaseEvent(event);
744
745 d->mouseButtonPressed = false;
746
747 if ((viewMode() == KListView::ListMode) || !d->proxyModel ||
748 !d->itemCategorizer)
749 {
750 return;
751 }
752
753 event->accept();
754
755 // FIXME: I have to rethink and rewrite this method (ereslibre)
756
757 QPoint initialPressPosition = viewport()->mapFromGlobal(QCursor::pos());
758 initialPressPosition.setY(initialPressPosition.y() + verticalOffset());
759 initialPressPosition.setX(initialPressPosition.x() + horizontalOffset());
760
761 if (initialPressPosition == d->initialPressPosition)
762 {
763 foreach(const QString &category, d->categories)
764 {
765 if (d->categoryVisualRect(category).contains(event->pos()))
766 {
767 QModelIndex index;
768 QItemSelectionModel::SelectionFlag flag;
769 foreach (const QModelIndex &mappedIndex,
770 d->categoriesIndexes[category])
771 {
772 index = d->proxyModel->mapFromSource(mappedIndex);
773
774 if (selectionModel()->selectedIndexes().contains(index))
775 {
776 flag = QItemSelectionModel::Deselect;
777 }
778 else
779 {
780 flag = QItemSelectionModel::Select;
781 }
782
783 selectionModel()->select(index, flag);
784 }
785 }
786 }
787 }
788
789 viewport()->update();
790 }
791
792 void KListView::leaveEvent(QEvent *event)
793 {
794 QListView::leaveEvent(event);
795
796 d->hovered = QModelIndex();
797
798 if ((viewMode() == KListView::ListMode) || !d->proxyModel ||
799 !d->itemCategorizer)
800 {
801 return;
802 }
803
804 event->accept();
805
806 viewport()->update();
807 }
808
809 void KListView::rowsInserted(const QModelIndex &parent,
810 int start,
811 int end)
812 {
813 QListView::rowsInserted(parent, start, end);
814
815 if ((viewMode() == KListView::ListMode) || !d->proxyModel ||
816 !d->itemCategorizer)
817 {
818 return;
819 }
820
821 rowsInsertedArtifficial(parent, start, end);
822 }
823
824 void KListView::rowsInsertedArtifficial(const QModelIndex &parent,
825 int start,
826 int end)
827 {
828 d->elementsInfo.clear();
829 d->elementsPosition.clear();
830 d->elementDictionary.clear();
831 d->categoriesIndexes.clear();
832 d->categoriesPosition.clear();
833 d->categories.clear();
834 d->intersectedIndexes.clear();
835 d->sourceModelIndexList.clear();
836 d->hovered = QModelIndex();
837 d->mouseButtonPressed = false;
838
839 if (start > end || end < 0 || start < 0 || !d->proxyModel->rowCount())
840 {
841 return;
842 }
843
844 // Add all elements mapped to the source model
845 for (int i = 0; i < d->proxyModel->rowCount(); i++)
846 {
847 d->sourceModelIndexList <<
848 d->proxyModel->mapToSource(d->proxyModel->index(i, 0));
849 }
850
851 // Sort them with the general purpose lessThan method
852 LessThan generalLessThan(d->proxyModel,
853 LessThan::GeneralPurpose);
854
855 qStableSort(d->sourceModelIndexList.begin(), d->sourceModelIndexList.end(),
856 generalLessThan);
857
858 // Explore categories
859 QString prevCategory =
860 d->itemCategorizer->categoryForItem(d->sourceModelIndexList[0],
861 d->proxyModel->sortRole());
862 QString lastCategory = prevCategory;
863 QModelIndexList modelIndexList;
864 struct Private::ElementInfo elementInfo;
865 foreach (const QModelIndex &index, d->sourceModelIndexList)
866 {
867 lastCategory = d->itemCategorizer->categoryForItem(index,
868 d->proxyModel->sortRole());
869
870 elementInfo.category = lastCategory;
871
872 if (prevCategory != lastCategory)
873 {
874 d->categoriesIndexes.insert(prevCategory, modelIndexList);
875 d->categories << prevCategory;
876 modelIndexList.clear();
877 }
878
879 modelIndexList << index;
880 prevCategory = lastCategory;
881
882 d->elementsInfo.insert(index, elementInfo);
883 }
884
885 d->categoriesIndexes.insert(prevCategory, modelIndexList);
886 d->categories << prevCategory;
887
888 // Sort items locally in their respective categories with the category
889 // purpose lessThan
890 LessThan categoryLessThan(d->proxyModel,
891 LessThan::CategoryPurpose);
892
893 foreach (const QString &key, d->categories)
894 {
895 QModelIndexList &indexList = d->categoriesIndexes[key];
896
897 qStableSort(indexList.begin(), indexList.end(), categoryLessThan);
898 }
899
900 d->lastIndex = d->categoriesIndexes[d->categories[d->categories.count() - 1]][d->categoriesIndexes[d->categories[d->categories.count() - 1]].count() - 1];
901
902 // Finally, fill data information of items situation. This will help when
903 // trying to compute an item place in the viewport
904 int i = 0; // position relative to the category beginning
905 int j = 0; // number of elements before current
906 foreach (const QString &key, d->categories)
907 {
908 foreach (const QModelIndex &index, d->categoriesIndexes[key])
909 {
910 struct Private::ElementInfo &elementInfo = d->elementsInfo[index];
911
912 elementInfo.relativeOffsetToCategory = i;
913
914 d->elementDictionary.insert(d->proxyModel->index(j, 0),
915 d->proxyModel->mapFromSource(index));
916
917 i++;
918 j++;
919 }
920
921 i = 0;
922 }
923
924 d->updateScrollbars();
925 }
926
927 void KListView::rowsRemoved(const QModelIndex &parent,
928 int start,
929 int end)
930 {
931 if (d->proxyModel)
932 {
933 // Force the view to update all elements
934 rowsInsertedArtifficial(parent, start, end);
935 }
936 }
937
938 void KListView::updateGeometries()
939 {
940 if ((viewMode() == KListView::ListMode) || !d->proxyModel ||
941 !d->itemCategorizer)
942 {
943 QListView::updateGeometries();
944 return;
945 }
946
947 // Avoid QListView::updateGeometries(), since it will try to set another
948 // range to our scroll bars, what we don't want (ereslibre)
949 QAbstractItemView::updateGeometries();
950 }
951
952 void KListView::slotSortingRoleChanged()
953 {
954 if (d->proxyModel)
955 {
956 // Force the view to update all elements
957 rowsInsertedArtifficial(QModelIndex(), 0, d->proxyModel->rowCount() -
958 1);
959 }
960 }
961
962 #include "klistview.moc"