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