]> cloud.milkyroute.net Git - dolphin.git/blob - src/klistview.cpp
Fix the problem "the scrollbar remains if it was shown on the
[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
390 listView->verticalScrollBar()->setSingleStep(listView->viewport()->height() / 10);
391 listView->verticalScrollBar()->setPageStep(listView->viewport()->height());
392 listView->verticalScrollBar()->setRange(0, lastItemBottom);
393 }
394
395
396 //==============================================================================
397
398
399 KListView::KListView(QWidget *parent)
400 : QListView(parent)
401 , d(new Private(this))
402 {
403 }
404
405 KListView::~KListView()
406 {
407 delete d;
408 }
409
410 void KListView::setModel(QAbstractItemModel *model)
411 {
412 if (d->proxyModel)
413 {
414 QObject::disconnect(d->proxyModel,
415 SIGNAL(rowsRemoved(QModelIndex,int,int)),
416 this, SLOT(rowsRemoved(QModelIndex,int,int)));
417
418 QObject::disconnect(d->proxyModel,
419 SIGNAL(sortingRoleChanged()),
420 this, SLOT(slotSortingRoleChanged()));
421 }
422
423 QListView::setModel(model);
424
425 d->proxyModel = dynamic_cast<KSortFilterProxyModel*>(model);
426
427 if (d->proxyModel)
428 {
429 QObject::connect(d->proxyModel,
430 SIGNAL(rowsRemoved(QModelIndex,int,int)),
431 this, SLOT(rowsRemoved(QModelIndex,int,int)));
432
433 QObject::connect(d->proxyModel,
434 SIGNAL(sortingRoleChanged()),
435 this, SLOT(slotSortingRoleChanged()));
436 }
437 }
438
439 QRect KListView::visualRect(const QModelIndex &index) const
440 {
441 if ((viewMode() == KListView::ListMode) || !d->proxyModel ||
442 !d->itemCategorizer)
443 {
444 return QListView::visualRect(index);
445 }
446
447 if (!qobject_cast<const QSortFilterProxyModel*>(index.model()))
448 {
449 return d->visualRect(d->proxyModel->mapFromSource(index));
450 }
451
452 return d->visualRect(index);
453 }
454
455 KItemCategorizer *KListView::itemCategorizer() const
456 {
457 return d->itemCategorizer;
458 }
459
460 void KListView::setItemCategorizer(KItemCategorizer *itemCategorizer)
461 {
462 if (!itemCategorizer && d->proxyModel)
463 {
464 QObject::disconnect(d->proxyModel,
465 SIGNAL(rowsRemoved(QModelIndex,int,int)),
466 this, SLOT(rowsRemoved(QModelIndex,int,int)));
467
468 QObject::disconnect(d->proxyModel,
469 SIGNAL(sortingRoleChanged()),
470 this, SLOT(slotSortingRoleChanged()));
471 }
472 else if (itemCategorizer && d->proxyModel)
473 {
474 QObject::connect(d->proxyModel,
475 SIGNAL(rowsRemoved(QModelIndex,int,int)),
476 this, SLOT(rowsRemoved(QModelIndex,int,int)));
477
478 QObject::connect(d->proxyModel,
479 SIGNAL(sortingRoleChanged()),
480 this, SLOT(slotSortingRoleChanged()));
481 }
482
483 d->itemCategorizer = itemCategorizer;
484
485 if (itemCategorizer)
486 {
487 rowsInserted(QModelIndex(), 0, d->proxyModel->rowCount() - 1);
488 }
489 else
490 {
491 updateGeometries();
492 }
493 }
494
495 QModelIndex KListView::indexAt(const QPoint &point) const
496 {
497 if ((viewMode() == KListView::ListMode) || !d->proxyModel ||
498 !d->itemCategorizer)
499 {
500 return QListView::indexAt(point);
501 }
502
503 QModelIndex index;
504
505 QModelIndexList item = d->intersectionSet(QRect(point, point));
506
507 if (item.count() == 1)
508 {
509 index = item[0];
510 }
511
512 d->hovered = index;
513
514 return index;
515 }
516
517 void KListView::reset()
518 {
519 QListView::reset();
520
521 if ((viewMode() == KListView::ListMode) || !d->proxyModel ||
522 !d->itemCategorizer)
523 {
524 return;
525 }
526
527 d->elementsInfo.clear();
528 d->elementsPosition.clear();
529 d->elementDictionary.clear();
530 d->categoriesIndexes.clear();
531 d->categoriesPosition.clear();
532 d->categories.clear();
533 d->intersectedIndexes.clear();
534 d->sourceModelIndexList.clear();
535 d->hovered = QModelIndex();
536 d->mouseButtonPressed = false;
537 }
538
539 void KListView::paintEvent(QPaintEvent *event)
540 {
541 if ((viewMode() == KListView::ListMode) || !d->proxyModel ||
542 !d->itemCategorizer)
543 {
544 QListView::paintEvent(event);
545 return;
546 }
547
548 QStyleOptionViewItemV3 option = viewOptions();
549 QPainter painter(viewport());
550 QRect area = event->rect();
551 const bool focus = (hasFocus() || viewport()->hasFocus()) &&
552 currentIndex().isValid();
553 const QStyle::State state = option.state;
554 const bool enabled = (state & QStyle::State_Enabled) != 0;
555
556 painter.save();
557
558 QModelIndexList dirtyIndexes = d->intersectionSet(area);
559 foreach (const QModelIndex &index, dirtyIndexes)
560 {
561 option.state = state;
562 option.rect = d->visualRect(index);
563
564 if (selectionModel() && selectionModel()->isSelected(index))
565 {
566 option.state |= QStyle::State_Selected;
567 }
568
569 if (enabled)
570 {
571 QPalette::ColorGroup cg;
572 if ((d->proxyModel->flags(index) & Qt::ItemIsEnabled) == 0)
573 {
574 option.state &= ~QStyle::State_Enabled;
575 cg = QPalette::Disabled;
576 }
577 else
578 {
579 cg = QPalette::Normal;
580 }
581 option.palette.setCurrentColorGroup(cg);
582 }
583
584 if (focus && currentIndex() == index)
585 {
586 option.state |= QStyle::State_HasFocus;
587 if (this->state() == EditingState)
588 option.state |= QStyle::State_Editing;
589 }
590
591 if ((index == d->hovered) && !d->mouseButtonPressed)
592 option.state |= QStyle::State_MouseOver;
593 else
594 option.state &= ~QStyle::State_MouseOver;
595
596 itemDelegate(index)->paint(&painter, option, index);
597 }
598
599 // Redraw categories
600 QStyleOptionViewItem otherOption;
601 foreach (const QString &category, d->categories)
602 {
603 otherOption = option;
604 otherOption.rect = d->categoryVisualRect(category);
605
606 if (otherOption.rect.intersects(area))
607 {
608 d->drawNewCategory(category, otherOption, &painter);
609 }
610 }
611
612 if (d->mouseButtonPressed)
613 {
614 QPoint start, end, initialPressPosition;
615
616 initialPressPosition = d->initialPressPosition;
617
618 initialPressPosition.setY(initialPressPosition.y() - verticalOffset());
619 initialPressPosition.setX(initialPressPosition.x() - horizontalOffset());
620
621 if (d->initialPressPosition.x() > d->mousePosition.x() ||
622 d->initialPressPosition.y() > d->mousePosition.y())
623 {
624 start = d->mousePosition;
625 end = initialPressPosition;
626 }
627 else
628 {
629 start = initialPressPosition;
630 end = d->mousePosition;
631 }
632
633 QStyleOptionRubberBand yetAnotherOption;
634 yetAnotherOption.initFrom(this);
635 yetAnotherOption.shape = QRubberBand::Rectangle;
636 yetAnotherOption.opaque = false;
637 yetAnotherOption.rect = QRect(start, end).intersected(viewport()->rect().adjusted(-16, -16, 16, 16));
638 painter.save();
639 style()->drawControl(QStyle::CE_RubberBand, &yetAnotherOption, &painter);
640 painter.restore();
641 }
642
643 painter.restore();
644 }
645
646 void KListView::resizeEvent(QResizeEvent *event)
647 {
648 QListView::resizeEvent(event);
649
650 if ((viewMode() == KListView::ListMode) || !d->proxyModel ||
651 !d->itemCategorizer)
652 {
653 return;
654 }
655
656 // Clear the items positions cache
657 d->elementsPosition.clear();
658 d->categoriesPosition.clear();
659
660 d->updateScrollbars();
661 }
662
663 void KListView::setSelection(const QRect &rect,
664 QItemSelectionModel::SelectionFlags flags)
665 {
666 if ((viewMode() == KListView::ListMode) || !d->proxyModel ||
667 !d->itemCategorizer)
668 {
669 QListView::setSelection(rect, flags);
670 return;
671 }
672
673 // FIXME: I have to rethink and rewrite this method (ereslibre)
674
675 QModelIndexList dirtyIndexes = d->intersectionSet(rect);
676 foreach (const QModelIndex &index, dirtyIndexes)
677 {
678 if (!d->mouseButtonPressed && rect.intersects(visualRect(index)))
679 {
680 selectionModel()->select(index, flags);
681 }
682 else
683 {
684 selectionModel()->select(index, QItemSelectionModel::Select);
685
686 if (d->mouseButtonPressed)
687 d->tempSelected.append(index);
688 }
689 }
690
691 if (d->mouseButtonPressed)
692 {
693 foreach (const QModelIndex &index, selectionModel()->selectedIndexes())
694 {
695 if (!rect.intersects(visualRect(index)))
696 {
697 selectionModel()->select(index, QItemSelectionModel::Deselect);
698
699 if (d->mouseButtonPressed)
700 {
701 d->tempSelected.removeAll(index);
702 }
703 }
704 }
705 }
706 }
707
708 void KListView::mouseMoveEvent(QMouseEvent *event)
709 {
710 QListView::mouseMoveEvent(event);
711
712 d->mousePosition = event->pos();
713
714 if ((viewMode() == KListView::ListMode) || !d->proxyModel ||
715 !d->itemCategorizer)
716 {
717 return;
718 }
719
720 event->accept();
721
722 viewport()->update();
723 }
724
725 void KListView::mousePressEvent(QMouseEvent *event)
726 {
727 QListView::mousePressEvent(event);
728
729 d->tempSelected.clear();
730
731 if ((viewMode() == KListView::ListMode) || !d->proxyModel ||
732 !d->itemCategorizer)
733 {
734 return;
735 }
736
737 event->accept();
738
739 if (event->button() == Qt::LeftButton)
740 {
741 d->mouseButtonPressed = true;
742
743 d->initialPressPosition = event->pos();
744 d->initialPressPosition.setY(d->initialPressPosition.y() +
745 verticalOffset());
746 d->initialPressPosition.setX(d->initialPressPosition.x() +
747 horizontalOffset());
748 }
749
750 viewport()->update();
751 }
752
753 void KListView::mouseReleaseEvent(QMouseEvent *event)
754 {
755 QListView::mouseReleaseEvent(event);
756
757 d->mouseButtonPressed = false;
758
759 if ((viewMode() == KListView::ListMode) || !d->proxyModel ||
760 !d->itemCategorizer)
761 {
762 return;
763 }
764
765 event->accept();
766
767 // FIXME: I have to rethink and rewrite this method (ereslibre)
768
769 QPoint initialPressPosition = viewport()->mapFromGlobal(QCursor::pos());
770 initialPressPosition.setY(initialPressPosition.y() + verticalOffset());
771 initialPressPosition.setX(initialPressPosition.x() + horizontalOffset());
772
773 if (initialPressPosition == d->initialPressPosition)
774 {
775 foreach(const QString &category, d->categories)
776 {
777 if (d->categoryVisualRect(category).contains(event->pos()))
778 {
779 QModelIndex index;
780 QItemSelectionModel::SelectionFlag flag;
781 foreach (const QModelIndex &mappedIndex,
782 d->categoriesIndexes[category])
783 {
784 index = d->proxyModel->mapFromSource(mappedIndex);
785
786 if (selectionModel()->selectedIndexes().contains(index))
787 {
788 flag = QItemSelectionModel::Deselect;
789 }
790 else
791 {
792 flag = QItemSelectionModel::Select;
793 }
794
795 selectionModel()->select(index, flag);
796 }
797 }
798 }
799 }
800
801 viewport()->update();
802 }
803
804 void KListView::leaveEvent(QEvent *event)
805 {
806 QListView::leaveEvent(event);
807
808 d->hovered = QModelIndex();
809
810 if ((viewMode() == KListView::ListMode) || !d->proxyModel ||
811 !d->itemCategorizer)
812 {
813 return;
814 }
815
816 event->accept();
817
818 viewport()->update();
819 }
820
821 void KListView::rowsInserted(const QModelIndex &parent,
822 int start,
823 int end)
824 {
825 QListView::rowsInserted(parent, start, end);
826
827 if ((viewMode() == KListView::ListMode) || !d->proxyModel ||
828 !d->itemCategorizer)
829 {
830 return;
831 }
832
833 rowsInsertedArtifficial(parent, start, end);
834 }
835
836 void KListView::rowsInsertedArtifficial(const QModelIndex &parent,
837 int start,
838 int end)
839 {
840 d->elementsInfo.clear();
841 d->elementsPosition.clear();
842 d->elementDictionary.clear();
843 d->categoriesIndexes.clear();
844 d->categoriesPosition.clear();
845 d->categories.clear();
846 d->intersectedIndexes.clear();
847 d->sourceModelIndexList.clear();
848 d->hovered = QModelIndex();
849 d->mouseButtonPressed = false;
850
851 if (start > end || end < 0 || start < 0 || !d->proxyModel->rowCount())
852 {
853 return;
854 }
855
856 // Add all elements mapped to the source model
857 for (int k = 0; k < d->proxyModel->rowCount(); k++)
858 {
859 d->sourceModelIndexList <<
860 d->proxyModel->mapToSource(d->proxyModel->index(k, 0));
861 }
862
863 // Sort them with the general purpose lessThan method
864 LessThan generalLessThan(d->proxyModel,
865 LessThan::GeneralPurpose);
866
867 qStableSort(d->sourceModelIndexList.begin(), d->sourceModelIndexList.end(),
868 generalLessThan);
869
870 // Explore categories
871 QString prevCategory =
872 d->itemCategorizer->categoryForItem(d->sourceModelIndexList[0],
873 d->proxyModel->sortRole());
874 QString lastCategory = prevCategory;
875 QModelIndexList modelIndexList;
876 struct Private::ElementInfo elementInfo;
877 foreach (const QModelIndex &index, d->sourceModelIndexList)
878 {
879 lastCategory = d->itemCategorizer->categoryForItem(index,
880 d->proxyModel->sortRole());
881
882 elementInfo.category = lastCategory;
883
884 if (prevCategory != lastCategory)
885 {
886 d->categoriesIndexes.insert(prevCategory, modelIndexList);
887 d->categories << prevCategory;
888 modelIndexList.clear();
889 }
890
891 modelIndexList << index;
892 prevCategory = lastCategory;
893
894 d->elementsInfo.insert(index, elementInfo);
895 }
896
897 d->categoriesIndexes.insert(prevCategory, modelIndexList);
898 d->categories << prevCategory;
899
900 // Sort items locally in their respective categories with the category
901 // purpose lessThan
902 LessThan categoryLessThan(d->proxyModel,
903 LessThan::CategoryPurpose);
904
905 foreach (const QString &key, d->categories)
906 {
907 QModelIndexList &indexList = d->categoriesIndexes[key];
908
909 qStableSort(indexList.begin(), indexList.end(), categoryLessThan);
910 }
911
912 d->lastIndex = d->categoriesIndexes[d->categories[d->categories.count() - 1]][d->categoriesIndexes[d->categories[d->categories.count() - 1]].count() - 1];
913
914 // Finally, fill data information of items situation. This will help when
915 // trying to compute an item place in the viewport
916 int i = 0; // position relative to the category beginning
917 int j = 0; // number of elements before current
918 foreach (const QString &key, d->categories)
919 {
920 foreach (const QModelIndex &index, d->categoriesIndexes[key])
921 {
922 struct Private::ElementInfo &elementInfo = d->elementsInfo[index];
923
924 elementInfo.relativeOffsetToCategory = i;
925
926 d->elementDictionary.insert(d->proxyModel->index(j, 0),
927 d->proxyModel->mapFromSource(index));
928
929 i++;
930 j++;
931 }
932
933 i = 0;
934 }
935
936 d->updateScrollbars();
937 }
938
939 void KListView::rowsRemoved(const QModelIndex &parent,
940 int start,
941 int end)
942 {
943 if (d->proxyModel)
944 {
945 // Force the view to update all elements
946 rowsInsertedArtifficial(parent, start, end);
947 }
948 }
949
950 void KListView::updateGeometries()
951 {
952 if ((viewMode() == KListView::ListMode) || !d->proxyModel ||
953 !d->itemCategorizer)
954 {
955 QListView::updateGeometries();
956 return;
957 }
958
959 // Avoid QListView::updateGeometries(), since it will try to set another
960 // range to our scroll bars, what we don't want (ereslibre)
961 QAbstractItemView::updateGeometries();
962 }
963
964 void KListView::slotSortingRoleChanged()
965 {
966 if ((viewMode() == KListView::IconMode) && d->proxyModel &&
967 d->itemCategorizer)
968 {
969 // Force the view to update all elements
970 rowsInsertedArtifficial(QModelIndex(), 0, d->proxyModel->rowCount() - 1);
971 }
972 }
973
974 #include "klistview.moc"