2 * This file is part of the KDE project
3 * Copyright (C) 2007 Rafael Fernández López <ereslibre@gmail.com>
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.
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.
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.
21 #include "klistview.h"
22 #include "klistview_p.h"
24 #include <math.h> // trunc on C99 compliant systems
25 #include <kdefakes.h> // trunc for not C99 compliant systems
27 #include <QApplication>
30 #include <QPaintEvent>
35 #include "kitemcategorizer.h"
36 #include "ksortfilterproxymodel.h"
47 inline LessThan(const KSortFilterProxyModel
*proxyModel
,
49 : proxyModel(proxyModel
)
54 inline bool operator()(const QModelIndex
&left
,
55 const QModelIndex
&right
) const
57 if (purpose
== GeneralPurpose
)
59 return proxyModel
->sortOrder() == Qt::AscendingOrder
?
60 proxyModel
->lessThanGeneralPurpose(left
, right
) :
61 !proxyModel
->lessThanGeneralPurpose(left
, right
);
64 return proxyModel
->sortOrder() == Qt::AscendingOrder
?
65 proxyModel
->lessThanCategoryPurpose(left
, right
) :
66 !proxyModel
->lessThanCategoryPurpose(left
, right
);
70 const KSortFilterProxyModel
*proxyModel
;
71 const Purpose purpose
;
75 //==============================================================================
78 KListView::Private::Private(KListView
*listView
)
81 , mouseButtonPressed(false)
84 , lastIndex(QModelIndex())
88 KListView::Private::~Private()
92 const QModelIndexList
&KListView::Private::intersectionSet(const QRect
&rect
)
95 QRect indexVisualRect
;
97 intersectedIndexes
.clear();
99 // Lets find out where we should start
100 int top
= proxyModel
->rowCount() - 1;
102 int middle
= (top
+ bottom
) / 2;
103 while (bottom
<= top
)
105 middle
= (top
+ bottom
) / 2;
107 index
= elementDictionary
[proxyModel
->index(middle
, 0)];
108 indexVisualRect
= visualRect(index
);
110 if (qMax(indexVisualRect
.topLeft().y(),
111 indexVisualRect
.bottomRight().y()) < qMin(rect
.topLeft().y(),
112 rect
.bottomRight().y()))
123 for (int i
= middle
; i
< proxyModel
->rowCount(); i
++)
125 index
= elementDictionary
[proxyModel
->index(i
, 0)];
126 indexVisualRect
= visualRect(index
);
128 if (rect
.intersects(indexVisualRect
))
129 intersectedIndexes
.append(index
);
131 // If we passed next item, stop searching for hits
132 if (qMax(rect
.bottomRight().y(), rect
.topLeft().y()) <
133 indexVisualRect
.topLeft().y())
139 return intersectedIndexes
;
142 QRect
KListView::Private::visualRectInViewport(const QModelIndex
&index
) const
144 if (!index
.isValid())
147 QString curCategory
= elementsInfo
[index
].category
;
149 QRect
retRect(listView
->spacing(), listView
->spacing() * 2 +
150 30 /* categoryHeight */, 0, 0);
152 int viewportWidth
= listView
->viewport()->width() - listView
->spacing();
154 // We really need all items to be of same size. Otherwise we cannot do this
157 // listView->sizeHintForIndex(proxyModel->mapFromSource(index));
158 // int itemHeight = itemSize.height();
159 // int itemWidth = itemSize.width();*/
160 int itemHeight
= 107;
162 int itemWidthPlusSeparation
= listView
->spacing() + itemWidth
;
163 int elementsPerRow
= viewportWidth
/ itemWidthPlusSeparation
;
167 int column
= elementsInfo
[index
].relativeOffsetToCategory
% elementsPerRow
;
168 int row
= elementsInfo
[index
].relativeOffsetToCategory
/ elementsPerRow
;
170 retRect
.setLeft(retRect
.left() + column
* listView
->spacing() +
175 foreach (const QString
&category
, categories
)
177 if (category
== curCategory
)
180 rows
= (float) ((float) categoriesIndexes
[category
].count() /
181 (float) elementsPerRow
);
182 rowsInt
= categoriesIndexes
[category
].count() / elementsPerRow
;
184 if (rows
- trunc(rows
)) rowsInt
++;
186 retRect
.setTop(retRect
.top() +
187 (rowsInt
* listView
->spacing()) +
188 (rowsInt
* itemHeight
) +
189 30 /* categoryHeight */ +
190 listView
->spacing() * 2);
193 retRect
.setTop(retRect
.top() + row
* listView
->spacing() +
196 retRect
.setWidth(itemWidth
);
197 retRect
.setHeight(itemHeight
);
202 QRect
KListView::Private::visualCategoryRectInViewport(const QString
&category
)
205 QRect
retRect(listView
->spacing(),
207 listView
->viewport()->width() - listView
->spacing() * 2,
210 if (!proxyModel
->rowCount() || !categories
.contains(category
))
213 QModelIndex index
= proxyModel
->index(0, 0, QModelIndex());
215 int viewportWidth
= listView
->viewport()->width() - listView
->spacing();
217 // We really need all items to be of same size. Otherwise we cannot do this
219 // QSize itemSize = listView->sizeHintForIndex(index);
220 // int itemHeight = itemSize.height();
221 // int itemWidth = itemSize.width();
222 int itemHeight
= 107;
224 int itemWidthPlusSeparation
= listView
->spacing() + itemWidth
;
225 int elementsPerRow
= viewportWidth
/ itemWidthPlusSeparation
;
232 foreach (const QString
&itCategory
, categories
)
234 if (itCategory
== category
)
237 rows
= (float) ((float) categoriesIndexes
[itCategory
].count() /
238 (float) elementsPerRow
);
239 rowsInt
= categoriesIndexes
[itCategory
].count() / elementsPerRow
;
241 if (rows
- trunc(rows
)) rowsInt
++;
243 retRect
.setTop(retRect
.top() +
244 (rowsInt
* listView
->spacing()) +
245 (rowsInt
* itemHeight
) +
246 30 /* categoryHeight */ +
247 listView
->spacing() * 2);
250 retRect
.setHeight(30 /* categoryHeight */);
255 // We're sure elementsPosition doesn't contain index
256 const QRect
&KListView::Private::cacheIndex(const QModelIndex
&index
)
258 QRect rect
= visualRectInViewport(index
);
259 elementsPosition
[index
] = rect
;
261 return elementsPosition
[index
];
264 // We're sure categoriesPosition doesn't contain category
265 const QRect
&KListView::Private::cacheCategory(const QString
&category
)
267 QRect rect
= visualCategoryRectInViewport(category
);
268 categoriesPosition
[category
] = rect
;
270 return categoriesPosition
[category
];
273 const QRect
&KListView::Private::cachedRectIndex(const QModelIndex
&index
)
275 if (elementsPosition
.contains(index
)) // If we have it cached
277 return elementsPosition
[index
];
279 else // Otherwise, cache it
281 return cacheIndex(index
);
285 const QRect
&KListView::Private::cachedRectCategory(const QString
&category
)
287 if (categoriesPosition
.contains(category
)) // If we have it cached
289 return categoriesPosition
[category
];
291 else // Otherwise, cache it and
293 return cacheCategory(category
);
297 QRect
KListView::Private::visualRect(const QModelIndex
&index
)
299 QModelIndex mappedIndex
= proxyModel
->mapToSource(index
);
301 QRect retRect
= cachedRectIndex(mappedIndex
);
302 int dx
= -listView
->horizontalOffset();
303 int dy
= -listView
->verticalOffset();
304 retRect
.adjust(dx
, dy
, dx
, dy
);
309 QRect
KListView::Private::categoryVisualRect(const QString
&category
)
311 QRect retRect
= cachedRectCategory(category
);
312 int dx
= -listView
->horizontalOffset();
313 int dy
= -listView
->verticalOffset();
314 retRect
.adjust(dx
, dy
, dx
, dy
);
319 void KListView::Private::drawNewCategory(const QString
&category
,
320 const QStyleOption
&option
,
323 QColor color
= option
.palette
.color(QPalette::Text
);
326 painter
->setRenderHint(QPainter::Antialiasing
);
328 QStyleOptionButton opt
;
330 opt
.rect
= option
.rect
;
331 opt
.palette
= option
.palette
;
332 opt
.direction
= option
.direction
;
335 if (option
.rect
.contains(listView
->viewport()->mapFromGlobal(QCursor::pos())) &&
338 const QPalette::ColorGroup group
=
339 option
.state
& QStyle::State_Enabled
?
340 QPalette::Normal
: QPalette::Disabled
;
342 QLinearGradient
gradient(option
.rect
.topLeft(),
343 option
.rect
.bottomRight());
344 gradient
.setColorAt(0,
345 option
.palette
.color(group
,
346 QPalette::Highlight
).light());
347 gradient
.setColorAt(1, Qt::transparent
);
349 painter
->fillRect(option
.rect
, gradient
);
352 /*if (const KStyle *style = dynamic_cast<const KStyle*>(QApplication::style()))
354 style->drawControl(KStyle::CE_Category, &opt, painter, this);
358 QFont painterFont
= painter
->font();
359 painterFont
.setWeight(QFont::Bold
);
360 QFontMetrics
metrics(painterFont
);
361 painter
->setFont(painterFont
);
364 path
.addRect(option
.rect
.left(),
365 option
.rect
.bottom() - 2,
369 QLinearGradient
gradient(option
.rect
.topLeft(),
370 option
.rect
.bottomRight());
371 gradient
.setColorAt(0, color
);
372 gradient
.setColorAt(1, Qt::transparent
);
374 painter
->setBrush(gradient
);
375 painter
->fillPath(path
, gradient
);
377 painter
->setPen(color
);
379 painter
->drawText(option
.rect
, Qt::AlignVCenter
| Qt::AlignLeft
,
380 metrics
.elidedText(category
, Qt::ElideRight
, option
.rect
.width()));
386 void KListView::Private::updateScrollbars()
388 int lastItemBottom
= cachedRectIndex(lastIndex
).bottom() +
389 listView
->spacing() - listView
->viewport()->height();
391 listView
->verticalScrollBar()->setSingleStep(listView
->viewport()->height() / 10);
392 listView
->verticalScrollBar()->setPageStep(listView
->viewport()->height());
393 listView
->verticalScrollBar()->setRange(0, lastItemBottom
);
396 void KListView::Private::drawDraggedItems(QPainter
*painter
)
398 QStyleOptionViewItemV3 option
= listView
->viewOptions();
399 option
.state
&= ~QStyle::State_MouseOver
;
400 foreach (const QModelIndex
&index
, listView
->selectionModel()->selectedIndexes())
402 int dx
= mousePosition
.x() - initialPressPosition
.x() + listView
->horizontalOffset();
403 int dy
= mousePosition
.y() - initialPressPosition
.y() + listView
->verticalOffset();
405 option
.rect
= visualRect(index
);
406 option
.rect
.adjust(dx
, dy
, dx
, dy
);
408 listView
->itemDelegate(index
)->paint(painter
, option
, index
);
413 //==============================================================================
416 KListView::KListView(QWidget
*parent
)
418 , d(new Private(this))
422 KListView::~KListView()
427 void KListView::setModel(QAbstractItemModel
*model
)
431 QObject::disconnect(d
->proxyModel
,
432 SIGNAL(rowsRemoved(QModelIndex
,int,int)),
433 this, SLOT(rowsRemoved(QModelIndex
,int,int)));
435 QObject::disconnect(d
->proxyModel
,
436 SIGNAL(sortingRoleChanged()),
437 this, SLOT(slotSortingRoleChanged()));
440 QListView::setModel(model
);
442 d
->proxyModel
= dynamic_cast<KSortFilterProxyModel
*>(model
);
446 QObject::connect(d
->proxyModel
,
447 SIGNAL(rowsRemoved(QModelIndex
,int,int)),
448 this, SLOT(rowsRemoved(QModelIndex
,int,int)));
450 QObject::connect(d
->proxyModel
,
451 SIGNAL(sortingRoleChanged()),
452 this, SLOT(slotSortingRoleChanged()));
456 QRect
KListView::visualRect(const QModelIndex
&index
) const
458 if ((viewMode() != KListView::IconMode
) || !d
->proxyModel
||
461 return QListView::visualRect(index
);
464 if (!qobject_cast
<const QSortFilterProxyModel
*>(index
.model()))
466 return d
->visualRect(d
->proxyModel
->mapFromSource(index
));
469 return d
->visualRect(index
);
472 KItemCategorizer
*KListView::itemCategorizer() const
474 return d
->itemCategorizer
;
477 void KListView::setItemCategorizer(KItemCategorizer
*itemCategorizer
)
479 if (!itemCategorizer
&& d
->proxyModel
)
481 QObject::disconnect(d
->proxyModel
,
482 SIGNAL(rowsRemoved(QModelIndex
,int,int)),
483 this, SLOT(rowsRemoved(QModelIndex
,int,int)));
485 QObject::disconnect(d
->proxyModel
,
486 SIGNAL(sortingRoleChanged()),
487 this, SLOT(slotSortingRoleChanged()));
489 else if (itemCategorizer
&& d
->proxyModel
)
491 QObject::connect(d
->proxyModel
,
492 SIGNAL(rowsRemoved(QModelIndex
,int,int)),
493 this, SLOT(rowsRemoved(QModelIndex
,int,int)));
495 QObject::connect(d
->proxyModel
,
496 SIGNAL(sortingRoleChanged()),
497 this, SLOT(slotSortingRoleChanged()));
500 d
->itemCategorizer
= itemCategorizer
;
504 rowsInserted(QModelIndex(), 0, d
->proxyModel
->rowCount() - 1);
512 QModelIndex
KListView::indexAt(const QPoint
&point
) const
514 if ((viewMode() != KListView::IconMode
) || !d
->proxyModel
||
517 return QListView::indexAt(point
);
522 QModelIndexList item
= d
->intersectionSet(QRect(point
, point
));
524 if (item
.count() == 1)
534 void KListView::reset()
538 if ((viewMode() != KListView::IconMode
) || !d
->proxyModel
||
544 d
->elementsInfo
.clear();
545 d
->elementsPosition
.clear();
546 d
->elementDictionary
.clear();
547 d
->categoriesIndexes
.clear();
548 d
->categoriesPosition
.clear();
549 d
->isIndexSelected
.clear(); // selection cache
550 d
->categories
.clear();
551 d
->intersectedIndexes
.clear();
552 d
->sourceModelIndexList
.clear();
553 d
->hovered
= QModelIndex();
554 d
->mouseButtonPressed
= false;
557 void KListView::paintEvent(QPaintEvent
*event
)
559 if ((viewMode() != KListView::IconMode
) || !d
->proxyModel
||
562 QListView::paintEvent(event
);
566 QStyleOptionViewItemV3 option
= viewOptions();
567 QPainter
painter(viewport());
568 QRect area
= event
->rect();
569 const bool focus
= (hasFocus() || viewport()->hasFocus()) &&
570 currentIndex().isValid();
571 const QStyle::State state
= option
.state
;
572 const bool enabled
= (state
& QStyle::State_Enabled
) != 0;
576 QModelIndexList dirtyIndexes
= d
->intersectionSet(area
);
577 foreach (const QModelIndex
&index
, dirtyIndexes
)
579 option
.state
= state
;
580 option
.rect
= d
->visualRect(index
);
582 if (selectionModel() && selectionModel()->isSelected(index
))
584 option
.state
|= QStyle::State_Selected
;
589 QPalette::ColorGroup cg
;
590 if ((d
->proxyModel
->flags(index
) & Qt::ItemIsEnabled
) == 0)
592 option
.state
&= ~QStyle::State_Enabled
;
593 cg
= QPalette::Disabled
;
597 cg
= QPalette::Normal
;
599 option
.palette
.setCurrentColorGroup(cg
);
602 if (focus
&& currentIndex() == index
)
604 option
.state
|= QStyle::State_HasFocus
;
605 if (this->state() == EditingState
)
606 option
.state
|= QStyle::State_Editing
;
609 if ((index
== d
->hovered
) && !d
->mouseButtonPressed
)
610 option
.state
|= QStyle::State_MouseOver
;
612 option
.state
&= ~QStyle::State_MouseOver
;
614 itemDelegate(index
)->paint(&painter
, option
, index
);
618 QStyleOptionViewItem otherOption
;
619 foreach (const QString
&category
, d
->categories
)
621 otherOption
= option
;
622 otherOption
.rect
= d
->categoryVisualRect(category
);
624 if (otherOption
.rect
.intersects(area
))
626 d
->drawNewCategory(category
, otherOption
, &painter
);
630 if (d
->mouseButtonPressed
&& !d
->isDragging
)
632 QPoint start
, end
, initialPressPosition
;
634 initialPressPosition
= d
->initialPressPosition
;
636 initialPressPosition
.setY(initialPressPosition
.y() - verticalOffset());
637 initialPressPosition
.setX(initialPressPosition
.x() - horizontalOffset());
639 if (d
->initialPressPosition
.x() > d
->mousePosition
.x() ||
640 d
->initialPressPosition
.y() > d
->mousePosition
.y())
642 start
= d
->mousePosition
;
643 end
= initialPressPosition
;
647 start
= initialPressPosition
;
648 end
= d
->mousePosition
;
651 QStyleOptionRubberBand yetAnotherOption
;
652 yetAnotherOption
.initFrom(this);
653 yetAnotherOption
.shape
= QRubberBand::Rectangle
;
654 yetAnotherOption
.opaque
= false;
655 yetAnotherOption
.rect
= QRect(start
, end
).intersected(viewport()->rect().adjusted(-16, -16, 16, 16));
657 style()->drawControl(QStyle::CE_RubberBand
, &yetAnotherOption
, &painter
);
662 d
->drawDraggedItems(&painter
);
667 void KListView::resizeEvent(QResizeEvent
*event
)
669 QListView::resizeEvent(event
);
671 if ((viewMode() != KListView::IconMode
) || !d
->proxyModel
||
677 // Clear the items positions cache
678 d
->elementsPosition
.clear();
679 d
->categoriesPosition
.clear();
681 d
->updateScrollbars();
684 void KListView::setSelection(const QRect
&rect
,
685 QItemSelectionModel::SelectionFlags flags
)
687 if ((viewMode() != KListView::IconMode
) || !d
->proxyModel
||
690 QListView::setSelection(rect
, flags
);
694 if (flags
& QItemSelectionModel::Clear
)
696 selectionModel()->clear();
697 d
->isIndexSelected
.clear();
698 d
->isTemporarySelected
.clear();
701 QItemSelection selection
;
702 QModelIndexList dirtyIndexes
= d
->intersectionSet(rect
);
703 foreach (const QModelIndex
&index
, dirtyIndexes
)
705 if (!d
->mouseButtonPressed
&& rect
.intersects(visualRect(index
)))
707 if (d
->isIndexSelected
.contains(index
))
709 if (!d
->isIndexSelected
[index
])
710 selection
.select(index
, index
);
712 d
->isIndexSelected
[index
] = true;
716 d
->isIndexSelected
.insert(index
, true);
717 selection
.select(index
, index
);
720 else if (d
->mouseButtonPressed
) // selection cache
722 if (!d
->isIndexSelected
.contains(index
) ||
723 (d
->isIndexSelected
.contains(index
) && !d
->isIndexSelected
[index
]))
725 if (d
->isTemporarySelected
.contains(index
))
727 d
->isTemporarySelected
[index
] = true;
731 d
->isTemporarySelected
.insert(index
, true);
735 if (d
->isIndexSelected
.contains(index
))
737 if (!d
->isIndexSelected
[index
])
738 selection
.select(index
, index
);
740 d
->isIndexSelected
[index
] = true;
744 d
->isIndexSelected
.insert(index
, true);
745 selection
.select(index
, index
);
750 QItemSelection deselect
;
752 foreach (const QModelIndex
&index
, d
->isIndexSelected
.keys())
754 if (!rect
.intersects(visualRect(index
)))
756 if (d
->isTemporarySelected
.contains(index
) &&
757 d
->isTemporarySelected
[index
])
759 deselect
.select(index
, index
);
760 d
->isTemporarySelected
[index
] = false;
761 d
->isIndexSelected
[index
] = false;
766 if (selection
.count())
767 selectionModel()->select(selection
, QItemSelectionModel::Select
);
769 if (deselect
.count())
770 selectionModel()->select(deselect
, QItemSelectionModel::Deselect
);
773 void KListView::mouseMoveEvent(QMouseEvent
*event
)
775 QListView::mouseMoveEvent(event
);
777 d
->mousePosition
= event
->pos();
779 if ((viewMode() != KListView::IconMode
) || !d
->proxyModel
||
787 viewport()->update();
790 void KListView::mousePressEvent(QMouseEvent
*event
)
792 QListView::mousePressEvent(event
);
794 if ((viewMode() != KListView::IconMode
) || !d
->proxyModel
||
802 if (event
->button() == Qt::LeftButton
)
804 d
->mouseButtonPressed
= true;
806 d
->initialPressPosition
= event
->pos();
807 d
->initialPressPosition
.setY(d
->initialPressPosition
.y() +
809 d
->initialPressPosition
.setX(d
->initialPressPosition
.x() +
813 viewport()->update();
816 void KListView::mouseReleaseEvent(QMouseEvent
*event
)
818 QListView::mouseReleaseEvent(event
);
820 d
->mouseButtonPressed
= false;
822 if ((viewMode() != KListView::IconMode
) || !d
->proxyModel
||
830 d
->isTemporarySelected
.clear(); // selection cache
832 QPoint initialPressPosition
= viewport()->mapFromGlobal(QCursor::pos());
833 initialPressPosition
.setY(initialPressPosition
.y() + verticalOffset());
834 initialPressPosition
.setX(initialPressPosition
.x() + horizontalOffset());
836 if (initialPressPosition
== d
->initialPressPosition
)
838 QItemSelection selection
;
839 foreach(const QString
&category
, d
->categories
)
841 if (d
->categoryVisualRect(category
).contains(event
->pos()))
844 foreach (const QModelIndex
&mappedIndex
,
845 d
->categoriesIndexes
[category
])
847 index
= d
->proxyModel
->mapFromSource(mappedIndex
);
849 if (d
->isIndexSelected
.contains(index
))
851 if (!d
->isIndexSelected
[index
])
852 selection
.select(index
, index
);
854 d
->isIndexSelected
[index
] = true;
858 d
->isIndexSelected
.insert(index
, true);
859 selection
.select(index
, index
);
863 selectionModel()->select(selection
, QItemSelectionModel::Toggle
);
870 viewport()->update();
873 void KListView::leaveEvent(QEvent
*event
)
875 QListView::leaveEvent(event
);
877 d
->hovered
= QModelIndex();
879 if ((viewMode() != KListView::IconMode
) || !d
->proxyModel
||
887 viewport()->update();
890 void KListView::startDrag(Qt::DropActions supportedActions
)
892 QListView::startDrag(supportedActions
);
894 d
->isDragging
= false;
895 d
->mouseButtonPressed
= false;
898 void KListView::dragMoveEvent(QDragMoveEvent
*event
)
900 QListView::dragMoveEvent(event
);
902 d
->mousePosition
= event
->pos();
904 if ((viewMode() != KListView::IconMode
) || !d
->proxyModel
||
910 if (d
->mouseButtonPressed
)
912 d
->isDragging
= true;
916 d
->isDragging
= false;
921 viewport()->update();
924 void KListView::rowsInserted(const QModelIndex
&parent
,
928 QListView::rowsInserted(parent
, start
, end
);
930 if ((viewMode() != KListView::IconMode
) || !d
->proxyModel
||
936 rowsInsertedArtifficial(parent
, start
, end
);
939 void KListView::rowsInsertedArtifficial(const QModelIndex
&parent
,
943 d
->elementsInfo
.clear();
944 d
->elementsPosition
.clear();
945 d
->elementDictionary
.clear();
946 d
->categoriesIndexes
.clear();
947 d
->categoriesPosition
.clear();
948 d
->isIndexSelected
.clear(); // selection cache
949 d
->categories
.clear();
950 d
->intersectedIndexes
.clear();
951 d
->sourceModelIndexList
.clear();
952 d
->hovered
= QModelIndex();
953 d
->mouseButtonPressed
= false;
955 if (start
> end
|| end
< 0 || start
< 0 || !d
->proxyModel
->rowCount())
960 // Add all elements mapped to the source model
961 for (int k
= 0; k
< d
->proxyModel
->rowCount(); k
++)
963 d
->sourceModelIndexList
<<
964 d
->proxyModel
->mapToSource(d
->proxyModel
->index(k
, 0));
967 // Sort them with the general purpose lessThan method
968 LessThan
generalLessThan(d
->proxyModel
,
969 LessThan::GeneralPurpose
);
971 qStableSort(d
->sourceModelIndexList
.begin(), d
->sourceModelIndexList
.end(),
974 // Explore categories
975 QString prevCategory
=
976 d
->itemCategorizer
->categoryForItem(d
->sourceModelIndexList
[0],
977 d
->proxyModel
->sortRole());
978 QString lastCategory
= prevCategory
;
979 QModelIndexList modelIndexList
;
980 struct Private::ElementInfo elementInfo
;
981 foreach (const QModelIndex
&index
, d
->sourceModelIndexList
)
983 lastCategory
= d
->itemCategorizer
->categoryForItem(index
,
984 d
->proxyModel
->sortRole());
986 elementInfo
.category
= lastCategory
;
988 if (prevCategory
!= lastCategory
)
990 d
->categoriesIndexes
.insert(prevCategory
, modelIndexList
);
991 d
->categories
<< prevCategory
;
992 modelIndexList
.clear();
995 modelIndexList
<< index
;
996 prevCategory
= lastCategory
;
998 d
->elementsInfo
.insert(index
, elementInfo
);
1001 d
->categoriesIndexes
.insert(prevCategory
, modelIndexList
);
1002 d
->categories
<< prevCategory
;
1004 // Sort items locally in their respective categories with the category
1006 LessThan
categoryLessThan(d
->proxyModel
,
1007 LessThan::CategoryPurpose
);
1009 foreach (const QString
&key
, d
->categories
)
1011 QModelIndexList
&indexList
= d
->categoriesIndexes
[key
];
1013 qStableSort(indexList
.begin(), indexList
.end(), categoryLessThan
);
1016 d
->lastIndex
= d
->categoriesIndexes
[d
->categories
[d
->categories
.count() - 1]][d
->categoriesIndexes
[d
->categories
[d
->categories
.count() - 1]].count() - 1];
1018 // Finally, fill data information of items situation. This will help when
1019 // trying to compute an item place in the viewport
1020 int i
= 0; // position relative to the category beginning
1021 int j
= 0; // number of elements before current
1022 foreach (const QString
&key
, d
->categories
)
1024 foreach (const QModelIndex
&index
, d
->categoriesIndexes
[key
])
1026 struct Private::ElementInfo
&elementInfo
= d
->elementsInfo
[index
];
1028 elementInfo
.relativeOffsetToCategory
= i
;
1030 d
->elementDictionary
.insert(d
->proxyModel
->index(j
, 0),
1031 d
->proxyModel
->mapFromSource(index
));
1040 d
->updateScrollbars();
1043 void KListView::rowsRemoved(const QModelIndex
&parent
,
1049 // Force the view to update all elements
1050 rowsInsertedArtifficial(parent
, start
, end
);
1054 void KListView::updateGeometries()
1056 if ((viewMode() != KListView::IconMode
) || !d
->proxyModel
||
1057 !d
->itemCategorizer
)
1059 QListView::updateGeometries();
1063 // Avoid QListView::updateGeometries(), since it will try to set another
1064 // range to our scroll bars, what we don't want (ereslibre)
1065 QAbstractItemView::updateGeometries();
1068 void KListView::slotSortingRoleChanged()
1070 if ((viewMode() == KListView::IconMode
) && d
->proxyModel
&&
1073 // Force the view to update all elements
1074 rowsInsertedArtifficial(QModelIndex(), 0, d
->proxyModel
->rowCount() - 1);
1078 #include "klistview.moc"