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