2 * This file is part of the KDE project
3 * Copyright (C) 2007 Rafael Fernández López <ereslibre@kde.org>
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 "kcategorizedview.h"
22 #include "kcategorizedview_p.h"
24 #include <math.h> // trunc on C99 compliant systems
25 #include <kdefakes.h> // trunc for not C99 compliant systems
29 #include <QPaintEvent>
33 #include "kcategorydrawer.h"
34 #include "kcategorizedsortfilterproxymodel.h"
36 // By defining DOLPHIN_DRAGANDDROP the custom drag and drop implementation of
37 // KCategorizedView is bypassed to have a consistent drag and drop look for all
38 // views. Hopefully transparent pixmaps for drag objects will be supported in
39 // Qt 4.4, so that this workaround can be skipped.
40 #define DOLPHIN_DRAGANDDROP
42 KCategorizedView::Private::Private(KCategorizedView
*listView
)
45 , biggestItemSize(QSize(0, 0))
46 , mouseButtonPressed(false)
47 , rightMouseButtonPressed(false)
49 , dragLeftViewport(false)
54 KCategorizedView::Private::~Private()
58 const QModelIndexList
&KCategorizedView::Private::intersectionSet(const QRect
&rect
)
61 QRect indexVisualRect
;
63 intersectedIndexes
.clear();
67 if (listView
->gridSize().isEmpty())
69 itemHeight
= biggestItemSize
.height();
73 itemHeight
= listView
->gridSize().height();
76 // Lets find out where we should start
77 int top
= proxyModel
->rowCount() - 1;
79 int middle
= (top
+ bottom
) / 2;
82 middle
= (top
+ bottom
) / 2;
84 index
= proxyModel
->index(middle
, 0);
85 indexVisualRect
= visualRect(index
);
86 // We need the whole height (not only the visualRect). This will help us to update
87 // all needed indexes correctly (ereslibre)
88 indexVisualRect
.setHeight(indexVisualRect
.height() + (itemHeight
- indexVisualRect
.height()));
90 if (qMax(indexVisualRect
.topLeft().y(),
91 indexVisualRect
.bottomRight().y()) < qMin(rect
.topLeft().y(),
92 rect
.bottomRight().y()))
102 for (int i
= middle
; i
< proxyModel
->rowCount(); i
++)
104 index
= proxyModel
->index(i
, 0);
105 indexVisualRect
= visualRect(index
);
107 if (rect
.intersects(indexVisualRect
))
108 intersectedIndexes
.append(index
);
110 // If we passed next item, stop searching for hits
111 if (qMax(rect
.bottomRight().y(), rect
.topLeft().y()) <
112 qMin(indexVisualRect
.topLeft().y(),
113 indexVisualRect
.bottomRight().y()))
117 return intersectedIndexes
;
120 QRect
KCategorizedView::Private::visualRectInViewport(const QModelIndex
&index
) const
122 if (!index
.isValid())
125 QString curCategory
= elementsInfo
[index
.row()].category
;
129 if (listView
->layoutDirection() == Qt::LeftToRight
)
131 retRect
= QRect(listView
->spacing(), listView
->spacing() * 2 +
132 categoryDrawer
->categoryHeight(index
, listView
->viewOptions()), 0, 0);
136 retRect
= QRect(listView
->viewport()->width() - listView
->spacing(), listView
->spacing() * 2 +
137 categoryDrawer
->categoryHeight(index
, listView
->viewOptions()), 0, 0);
140 int viewportWidth
= listView
->viewport()->width() - listView
->spacing();
145 if (listView
->gridSize().isEmpty())
147 itemHeight
= biggestItemSize
.height();
148 itemWidth
= biggestItemSize
.width();
152 itemHeight
= listView
->gridSize().height();
153 itemWidth
= listView
->gridSize().width();
156 int itemWidthPlusSeparation
= listView
->spacing() + itemWidth
;
157 int elementsPerRow
= viewportWidth
/ itemWidthPlusSeparation
;
161 int column
= elementsInfo
[index
.row()].relativeOffsetToCategory
% elementsPerRow
;
162 int row
= elementsInfo
[index
.row()].relativeOffsetToCategory
/ elementsPerRow
;
164 if (listView
->layoutDirection() == Qt::LeftToRight
)
166 retRect
.setLeft(retRect
.left() + column
* listView
->spacing() +
171 retRect
.setLeft(retRect
.right() - column
* listView
->spacing() -
172 column
* itemWidth
- itemWidth
);
174 retRect
.setRight(retRect
.right() - column
* listView
->spacing() -
178 foreach (const QString
&category
, categories
)
180 if (category
== curCategory
)
183 float rows
= (float) ((float) categoriesIndexes
[category
].count() /
184 (float) elementsPerRow
);
186 int rowsInt
= categoriesIndexes
[category
].count() / elementsPerRow
;
188 if (rows
- trunc(rows
)) rowsInt
++;
190 retRect
.setTop(retRect
.top() +
191 (rowsInt
* itemHeight
) +
192 categoryDrawer
->categoryHeight(index
, listView
->viewOptions()) +
193 listView
->spacing() * 2);
195 if (listView
->gridSize().isEmpty())
197 retRect
.setTop(retRect
.top() +
198 (rowsInt
* listView
->spacing()));
202 if (listView
->gridSize().isEmpty())
204 retRect
.setTop(retRect
.top() + row
* listView
->spacing() +
209 retRect
.setTop(retRect
.top() + (row
* itemHeight
));
212 retRect
.setWidth(itemWidth
);
214 QModelIndex heightIndex
= proxyModel
->index(index
.row(), 0);
215 if (listView
->gridSize().isEmpty())
217 retRect
.setHeight(listView
->sizeHintForIndex(heightIndex
).height());
221 retRect
.setHeight(qMin(listView
->sizeHintForIndex(heightIndex
).height(),
222 listView
->gridSize().height()));
228 QRect
KCategorizedView::Private::visualCategoryRectInViewport(const QString
&category
)
231 QRect
retRect(listView
->spacing(),
233 listView
->viewport()->width() - listView
->spacing() * 2,
236 if (!proxyModel
->rowCount() || !categories
.contains(category
))
239 QModelIndex index
= proxyModel
->index(0, 0, QModelIndex());
241 int viewportWidth
= listView
->viewport()->width() - listView
->spacing();
246 if (listView
->gridSize().isEmpty())
248 itemHeight
= biggestItemSize
.height();
249 itemWidth
= biggestItemSize
.width();
253 itemHeight
= listView
->gridSize().height();
254 itemWidth
= listView
->gridSize().width();
257 int itemWidthPlusSeparation
= listView
->spacing() + itemWidth
;
258 int elementsPerRow
= viewportWidth
/ itemWidthPlusSeparation
;
263 foreach (const QString
&itCategory
, categories
)
265 if (itCategory
== category
)
268 float rows
= (float) ((float) categoriesIndexes
[itCategory
].count() /
269 (float) elementsPerRow
);
270 int rowsInt
= categoriesIndexes
[itCategory
].count() / elementsPerRow
;
272 if (rows
- trunc(rows
)) rowsInt
++;
274 retRect
.setTop(retRect
.top() +
275 (rowsInt
* itemHeight
) +
276 categoryDrawer
->categoryHeight(index
, listView
->viewOptions()) +
277 listView
->spacing() * 2);
279 if (listView
->gridSize().isEmpty())
281 retRect
.setTop(retRect
.top() +
282 (rowsInt
* listView
->spacing()));
286 retRect
.setHeight(categoryDrawer
->categoryHeight(index
, listView
->viewOptions()));
291 // We're sure elementsPosition doesn't contain index
292 const QRect
&KCategorizedView::Private::cacheIndex(const QModelIndex
&index
)
294 QRect rect
= visualRectInViewport(index
);
295 elementsPosition
[index
.row()] = rect
;
297 return elementsPosition
[index
.row()];
300 // We're sure categoriesPosition doesn't contain category
301 const QRect
&KCategorizedView::Private::cacheCategory(const QString
&category
)
303 QRect rect
= visualCategoryRectInViewport(category
);
304 categoriesPosition
[category
] = rect
;
306 return categoriesPosition
[category
];
309 const QRect
&KCategorizedView::Private::cachedRectIndex(const QModelIndex
&index
)
311 if (elementsPosition
.contains(index
.row())) // If we have it cached
313 return elementsPosition
[index
.row()];
315 else // Otherwise, cache it
317 return cacheIndex(index
);
321 const QRect
&KCategorizedView::Private::cachedRectCategory(const QString
&category
)
323 if (categoriesPosition
.contains(category
)) // If we have it cached
325 return categoriesPosition
[category
];
327 else // Otherwise, cache it and
329 return cacheCategory(category
);
333 QRect
KCategorizedView::Private::visualRect(const QModelIndex
&index
)
335 QRect retRect
= cachedRectIndex(index
);
336 int dx
= -listView
->horizontalOffset();
337 int dy
= -listView
->verticalOffset();
338 retRect
.adjust(dx
, dy
, dx
, dy
);
343 QRect
KCategorizedView::Private::categoryVisualRect(const QString
&category
)
345 QRect retRect
= cachedRectCategory(category
);
346 int dx
= -listView
->horizontalOffset();
347 int dy
= -listView
->verticalOffset();
348 retRect
.adjust(dx
, dy
, dx
, dy
);
353 void KCategorizedView::Private::drawNewCategory(const QModelIndex
&index
,
355 const QStyleOption
&option
,
358 if (!index
.isValid())
363 QStyleOption optionCopy
= option
;
364 const QString category
= proxyModel
->data(index
, KCategorizedSortFilterProxyModel::CategoryDisplayRole
).toString();
366 optionCopy
.state
&= ~QStyle::State_Selected
;
368 if ((listView
->selectionMode() != SingleSelection
) && (listView
->selectionMode() != NoSelection
)) {
369 if ((category
== hoveredCategory
) && !mouseButtonPressed
)
371 optionCopy
.state
|= QStyle::State_MouseOver
;
373 else if ((category
== hoveredCategory
) && mouseButtonPressed
)
375 QPoint initialPressPosition
= listView
->viewport()->mapFromGlobal(QCursor::pos());
376 initialPressPosition
.setY(initialPressPosition
.y() + listView
->verticalOffset());
377 initialPressPosition
.setX(initialPressPosition
.x() + listView
->horizontalOffset());
379 if (initialPressPosition
== this->initialPressPosition
)
381 optionCopy
.state
|= QStyle::State_Selected
;
386 categoryDrawer
->drawCategory(index
,
393 void KCategorizedView::Private::updateScrollbars()
395 // find the last index in the last category
396 QModelIndex lastIndex
= categoriesIndexes
.isEmpty() ? QModelIndex() : categoriesIndexes
[categories
.last()].last();
398 int lastItemBottom
= cachedRectIndex(lastIndex
).top() +
399 listView
->spacing() + (listView
->gridSize().isEmpty() ? biggestItemSize
.height() : listView
->gridSize().height()) - listView
->viewport()->height();
401 listView
->horizontalScrollBar()->setRange(0, 0);
403 listView
->verticalScrollBar()->setSingleStep(listView
->viewport()->height() / 10);
404 listView
->verticalScrollBar()->setPageStep(listView
->viewport()->height());
405 listView
->verticalScrollBar()->setRange(0, lastItemBottom
);
408 void KCategorizedView::Private::drawDraggedItems(QPainter
*painter
)
410 QStyleOptionViewItemV3 option
= listView
->viewOptions();
411 option
.state
&= ~QStyle::State_MouseOver
;
412 foreach (const QModelIndex
&index
, listView
->selectionModel()->selectedIndexes())
414 const int dx
= mousePosition
.x() - initialPressPosition
.x() + listView
->horizontalOffset();
415 const int dy
= mousePosition
.y() - initialPressPosition
.y() + listView
->verticalOffset();
417 option
.rect
= visualRect(index
);
418 option
.rect
.adjust(dx
, dy
, dx
, dy
);
420 if (option
.rect
.intersects(listView
->viewport()->rect()))
422 listView
->itemDelegate(index
)->paint(painter
, option
, index
);
427 void KCategorizedView::Private::layoutChanged(bool forceItemReload
)
429 if ((listView
->viewMode() == KCategorizedView::IconMode
) && proxyModel
&&
430 categoryDrawer
&& proxyModel
->isCategorizedModel() &&
432 (modelSortRole
!= proxyModel
->sortRole()) ||
433 (modelSortColumn
!= proxyModel
->sortColumn()) ||
434 (modelSortOrder
!= proxyModel
->sortOrder()) ||
435 (modelLastRowCount
!= proxyModel
->rowCount()) ||
436 (modelCategorized
!= proxyModel
->isCategorizedModel()))))
438 // Force the view to update all elements
439 listView
->rowsInsertedArtifficial(QModelIndex(), 0, proxyModel
->rowCount() - 1);
441 if (!forceItemReload
)
443 modelSortRole
= proxyModel
->sortRole();
444 modelSortColumn
= proxyModel
->sortColumn();
445 modelSortOrder
= proxyModel
->sortOrder();
446 modelLastRowCount
= proxyModel
->rowCount();
447 modelCategorized
= proxyModel
->isCategorizedModel();
450 else if ((listView
->viewMode() == KCategorizedView::IconMode
) && proxyModel
&&
451 categoryDrawer
&& proxyModel
->isCategorizedModel())
457 void KCategorizedView::Private::drawDraggedItems()
461 foreach (const QModelIndex
&index
, listView
->selectionModel()->selectedIndexes())
463 int dx
= mousePosition
.x() - initialPressPosition
.x() + listView
->horizontalOffset();
464 int dy
= mousePosition
.y() - initialPressPosition
.y() + listView
->verticalOffset();
466 currentRect
= visualRect(index
);
467 currentRect
.adjust(dx
, dy
, dx
, dy
);
469 if (currentRect
.intersects(listView
->viewport()->rect()))
471 rectToUpdate
= rectToUpdate
.united(currentRect
);
475 listView
->viewport()->update(lastDraggedItemsRect
.united(rectToUpdate
));
477 lastDraggedItemsRect
= rectToUpdate
;
481 //==============================================================================
484 KCategorizedView::KCategorizedView(QWidget
*parent
)
486 , d(new Private(this))
490 KCategorizedView::~KCategorizedView()
495 void KCategorizedView::setGridSize(const QSize
&size
)
497 QListView::setGridSize(size
);
499 d
->layoutChanged(true);
502 void KCategorizedView::setModel(QAbstractItemModel
*model
)
504 d
->lastSelection
= QItemSelection();
505 d
->forcedSelectionPosition
= 0;
506 d
->elementsInfo
.clear();
507 d
->elementsPosition
.clear();
508 d
->categoriesIndexes
.clear();
509 d
->categoriesPosition
.clear();
510 d
->categories
.clear();
511 d
->intersectedIndexes
.clear();
512 d
->modelIndexList
.clear();
513 d
->hovered
= QModelIndex();
514 d
->mouseButtonPressed
= false;
515 d
->rightMouseButtonPressed
= false;
519 QObject::disconnect(d
->proxyModel
,
520 SIGNAL(layoutChanged()),
521 this, SLOT(slotLayoutChanged()));
523 QObject::disconnect(d
->proxyModel
,
524 SIGNAL(dataChanged(QModelIndex
,QModelIndex
)),
525 this, SLOT(slotLayoutChanged()));
527 QObject::disconnect(d
->proxyModel
,
528 SIGNAL(rowsRemoved(QModelIndex
,int,int)),
529 this, SLOT(rowsRemoved(QModelIndex
,int,int)));
532 QListView::setModel(model
);
534 d
->proxyModel
= dynamic_cast<KCategorizedSortFilterProxyModel
*>(model
);
538 d
->modelSortRole
= d
->proxyModel
->sortRole();
539 d
->modelSortColumn
= d
->proxyModel
->sortColumn();
540 d
->modelSortOrder
= d
->proxyModel
->sortOrder();
541 d
->modelLastRowCount
= d
->proxyModel
->rowCount();
542 d
->modelCategorized
= d
->proxyModel
->isCategorizedModel();
544 QObject::connect(d
->proxyModel
,
545 SIGNAL(layoutChanged()),
546 this, SLOT(slotLayoutChanged()));
548 QObject::connect(d
->proxyModel
,
549 SIGNAL(dataChanged(QModelIndex
,QModelIndex
)),
550 this, SLOT(slotLayoutChanged()));
552 QObject::connect(d
->proxyModel
,
553 SIGNAL(rowsRemoved(QModelIndex
,int,int)),
554 this, SLOT(rowsRemoved(QModelIndex
,int,int)));
556 if (d
->proxyModel
->rowCount())
558 d
->layoutChanged(true);
563 d
->modelCategorized
= false;
567 QRect
KCategorizedView::visualRect(const QModelIndex
&index
) const
569 if ((viewMode() != KCategorizedView::IconMode
) || !d
->proxyModel
||
570 !d
->categoryDrawer
|| !d
->proxyModel
->isCategorizedModel())
572 return QListView::visualRect(index
);
575 if (!qobject_cast
<const QSortFilterProxyModel
*>(index
.model()))
577 return d
->visualRect(d
->proxyModel
->mapFromSource(index
));
580 return d
->visualRect(index
);
583 KCategoryDrawer
*KCategorizedView::categoryDrawer() const
585 return d
->categoryDrawer
;
588 void KCategorizedView::setCategoryDrawer(KCategoryDrawer
*categoryDrawer
)
590 d
->lastSelection
= QItemSelection();
591 d
->forcedSelectionPosition
= 0;
592 d
->elementsInfo
.clear();
593 d
->elementsPosition
.clear();
594 d
->categoriesIndexes
.clear();
595 d
->categoriesPosition
.clear();
596 d
->categories
.clear();
597 d
->intersectedIndexes
.clear();
598 d
->modelIndexList
.clear();
599 d
->hovered
= QModelIndex();
600 d
->mouseButtonPressed
= false;
601 d
->rightMouseButtonPressed
= false;
603 if (!categoryDrawer
&& d
->proxyModel
)
605 QObject::disconnect(d
->proxyModel
,
606 SIGNAL(layoutChanged()),
607 this, SLOT(slotLayoutChanged()));
609 QObject::disconnect(d
->proxyModel
,
610 SIGNAL(dataChanged(QModelIndex
,QModelIndex
)),
611 this, SLOT(slotLayoutChanged()));
613 QObject::disconnect(d
->proxyModel
,
614 SIGNAL(rowsRemoved(QModelIndex
,int,int)),
615 this, SLOT(rowsRemoved(QModelIndex
,int,int)));
617 else if (categoryDrawer
&& d
->proxyModel
)
619 QObject::connect(d
->proxyModel
,
620 SIGNAL(layoutChanged()),
621 this, SLOT(slotLayoutChanged()));
623 QObject::connect(d
->proxyModel
,
624 SIGNAL(dataChanged(QModelIndex
,QModelIndex
)),
625 this, SLOT(slotLayoutChanged()));
627 QObject::connect(d
->proxyModel
,
628 SIGNAL(rowsRemoved(QModelIndex
,int,int)),
629 this, SLOT(rowsRemoved(QModelIndex
,int,int)));
632 d
->categoryDrawer
= categoryDrawer
;
638 if (d
->proxyModel
->rowCount())
640 d
->layoutChanged(true);
650 QModelIndex
KCategorizedView::indexAt(const QPoint
&point
) const
652 if ((viewMode() != KCategorizedView::IconMode
) || !d
->proxyModel
||
653 !d
->categoryDrawer
|| !d
->proxyModel
->isCategorizedModel())
655 return QListView::indexAt(point
);
660 QModelIndexList item
= d
->intersectionSet(QRect(point
, point
));
662 if (item
.count() == 1)
670 void KCategorizedView::reset()
674 d
->lastSelection
= QItemSelection();
675 d
->forcedSelectionPosition
= 0;
676 d
->elementsInfo
.clear();
677 d
->elementsPosition
.clear();
678 d
->categoriesIndexes
.clear();
679 d
->categoriesPosition
.clear();
680 d
->categories
.clear();
681 d
->intersectedIndexes
.clear();
682 d
->modelIndexList
.clear();
683 d
->hovered
= QModelIndex();
684 d
->biggestItemSize
= QSize(0, 0);
685 d
->mouseButtonPressed
= false;
686 d
->rightMouseButtonPressed
= false;
689 void KCategorizedView::paintEvent(QPaintEvent
*event
)
691 if ((viewMode() != KCategorizedView::IconMode
) || !d
->proxyModel
||
692 !d
->categoryDrawer
|| !d
->proxyModel
->isCategorizedModel())
694 QListView::paintEvent(event
);
698 QStyleOptionViewItemV3 option
= viewOptions();
699 option
.widget
= this;
702 option
.features
|= QStyleOptionViewItemV2::WrapText
;
705 QPainter
painter(viewport());
706 QRect area
= event
->rect();
707 const bool focus
= (hasFocus() || viewport()->hasFocus()) &&
708 currentIndex().isValid();
709 const QStyle::State state
= option
.state
;
710 const bool enabled
= (state
& QStyle::State_Enabled
) != 0;
714 QModelIndexList dirtyIndexes
= d
->intersectionSet(area
);
715 foreach (const QModelIndex
&index
, dirtyIndexes
)
717 option
.state
= state
;
718 option
.rect
= visualRect(index
);
720 if (selectionModel() && selectionModel()->isSelected(index
))
722 option
.state
|= QStyle::State_Selected
;
727 QPalette::ColorGroup cg
;
728 if ((d
->proxyModel
->flags(index
) & Qt::ItemIsEnabled
) == 0)
730 option
.state
&= ~QStyle::State_Enabled
;
731 cg
= QPalette::Disabled
;
735 cg
= QPalette::Normal
;
737 option
.palette
.setCurrentColorGroup(cg
);
740 if (focus
&& currentIndex() == index
)
742 option
.state
|= QStyle::State_HasFocus
;
743 if (this->state() == EditingState
)
744 option
.state
|= QStyle::State_Editing
;
747 // we are only interested to give the mouse over feedback when no
748 // dragging is happening (ereslibre)
749 if ((index
== d
->hovered
) && !d
->mouseButtonPressed
&&
750 (this->state() == QAbstractItemView::NoState
))
751 option
.state
|= QStyle::State_MouseOver
;
753 option
.state
&= ~QStyle::State_MouseOver
;
755 itemDelegate(index
)->paint(&painter
, option
, index
);
759 QStyleOptionViewItem otherOption
;
760 bool intersectedInThePast
= false;
761 foreach (const QString
&category
, d
->categories
)
763 otherOption
= option
;
764 otherOption
.rect
= d
->categoryVisualRect(category
);
765 otherOption
.state
&= ~QStyle::State_MouseOver
;
767 if (otherOption
.rect
.intersects(area
))
769 intersectedInThePast
= true;
771 QModelIndex indexToDraw
= d
->proxyModel
->index(d
->categoriesIndexes
[category
][0].row(), d
->proxyModel
->sortColumn());
773 d
->drawNewCategory(indexToDraw
,
774 d
->proxyModel
->sortRole(), otherOption
, &painter
);
776 else if (intersectedInThePast
)
778 break; // the visible area has been finished, we don't need to keep asking, the rest won't intersect
779 // this is doable because we know that categories are correctly ordered on the list
783 if ((selectionMode() != SingleSelection
) && (selectionMode() != NoSelection
))
785 if (d
->mouseButtonPressed
&& !d
->isDragging
)
787 QPoint start
, end
, initialPressPosition
;
789 initialPressPosition
= d
->initialPressPosition
;
791 initialPressPosition
.setY(initialPressPosition
.y() - verticalOffset());
792 initialPressPosition
.setX(initialPressPosition
.x() - horizontalOffset());
794 if (d
->initialPressPosition
.x() > d
->mousePosition
.x() ||
795 d
->initialPressPosition
.y() > d
->mousePosition
.y())
797 start
= d
->mousePosition
;
798 end
= initialPressPosition
;
802 start
= initialPressPosition
;
803 end
= d
->mousePosition
;
806 QStyleOptionRubberBand yetAnotherOption
;
807 yetAnotherOption
.initFrom(this);
808 yetAnotherOption
.shape
= QRubberBand::Rectangle
;
809 yetAnotherOption
.opaque
= false;
810 yetAnotherOption
.rect
= QRect(start
, end
).intersected(viewport()->rect().adjusted(-16, -16, 16, 16));
812 style()->drawControl(QStyle::CE_RubberBand
, &yetAnotherOption
, &painter
);
817 if (d
->isDragging
&& !d
->dragLeftViewport
)
819 painter
.setOpacity(0.5);
820 d
->drawDraggedItems(&painter
);
826 void KCategorizedView::resizeEvent(QResizeEvent
*event
)
828 QListView::resizeEvent(event
);
830 // Clear the items positions cache
831 d
->elementsPosition
.clear();
832 d
->categoriesPosition
.clear();
833 d
->forcedSelectionPosition
= 0;
835 if ((viewMode() != KCategorizedView::IconMode
) || !d
->proxyModel
||
836 !d
->categoryDrawer
|| !d
->proxyModel
->isCategorizedModel())
841 d
->updateScrollbars();
844 void KCategorizedView::setSelection(const QRect
&rect
,
845 QItemSelectionModel::SelectionFlags flags
)
847 if ((viewMode() != KCategorizedView::IconMode
) || !d
->proxyModel
||
848 !d
->categoryDrawer
|| !d
->proxyModel
->isCategorizedModel())
850 QListView::setSelection(rect
, flags
);
857 if (flags
& QItemSelectionModel::Clear
)
859 selectionModel()->clear();
860 d
->lastSelection
.clear();
863 QModelIndexList dirtyIndexes
= d
->intersectionSet(rect
);
865 // no items affected, just leave
866 if (!dirtyIndexes
.count())
868 selectionModel()->select(d
->lastSelection
, QItemSelectionModel::SelectCurrent
);
874 QModelIndex bottomRight
;
876 if (d
->mouseButtonPressed
|| d
->rightMouseButtonPressed
) // selection with click + drag
878 QItemSelection selection
;
880 QModelIndex prev
= dirtyIndexes
[0];
881 QModelIndex first
= prev
;
882 foreach (const QModelIndex
&index
, dirtyIndexes
)
884 // we have a different interval. non-contiguous items
885 if ((index
.row() - prev
.row()) > 1) {
886 selection
<< QItemSelectionRange(first
, prev
);
894 selection
<< QItemSelectionRange(first
, prev
);
896 if (flags
& QItemSelectionModel::Current
)
898 if (rect
.topLeft() == rect
.bottomRight())
900 selectionModel()->setCurrentIndex(indexAt(rect
.topLeft()), QItemSelectionModel::NoUpdate
);
903 selection
.merge(d
->lastSelection
, flags
);
907 selection
.merge(selectionModel()->selection(), flags
);
909 selectionModel()->select(selection
, QItemSelectionModel::SelectCurrent
);
914 selectionModel()->select(selection
, flags
);
916 else // selection with click + keyboard keys
918 QModelIndex topLeftIndex
= indexAt(QPoint(rect
.topLeft().x(),
919 rect
.topLeft().y()));
920 QModelIndex bottomRightIndex
= indexAt(QPoint(rect
.bottomRight().x(),
921 rect
.bottomRight().y()));
923 // keyboard selection comes "upside down". Let's normalize it
924 if (topLeftIndex
.row() > bottomRightIndex
.row())
926 QModelIndex auxIndex
= topLeftIndex
;
927 topLeftIndex
= bottomRightIndex
;
928 bottomRightIndex
= auxIndex
;
931 int viewportWidth
= viewport()->width() - spacing();
934 if (gridSize().isEmpty())
936 itemWidth
= d
->biggestItemSize
.width();
940 itemWidth
= gridSize().width();
943 int itemWidthPlusSeparation
= spacing() + itemWidth
;
944 int elementsPerRow
= viewportWidth
/ itemWidthPlusSeparation
;
948 QModelIndexList
theoricDirty(dirtyIndexes
);
949 dirtyIndexes
.clear();
950 int first
= model()->rowCount();
953 foreach (const QModelIndex
&index
, theoricDirty
)
955 if ((index
.row() < first
) &&
956 ((((topLeftIndex
.row() / elementsPerRow
) == (index
.row() / elementsPerRow
)) &&
957 ((topLeftIndex
.row() % elementsPerRow
) <= (index
.row() % elementsPerRow
))) ||
958 (topLeftIndex
.row() / elementsPerRow
) != (index
.row() / elementsPerRow
)))
964 if ((index
.row() > last
) &&
965 ((((bottomRightIndex
.row() / elementsPerRow
) == (index
.row() / elementsPerRow
)) &&
966 ((bottomRightIndex
.row() % elementsPerRow
) >= (index
.row() % elementsPerRow
))) ||
967 (bottomRightIndex
.row() / elementsPerRow
) != (index
.row() / elementsPerRow
)))
974 for (int i
= first
; i
<= last
; i
++)
976 dirtyIndexes
<< model()->index(i
, theoricDirty
[0].column(), theoricDirty
[0].parent());
979 QItemSelection
selection(topLeft
, bottomRight
);
981 selectionModel()->select(selection
, flags
);
985 void KCategorizedView::mouseMoveEvent(QMouseEvent
*event
)
987 QListView::mouseMoveEvent(event
);
989 if ((viewMode() != KCategorizedView::IconMode
) || !d
->proxyModel
||
990 !d
->categoryDrawer
|| !d
->proxyModel
->isCategorizedModel())
995 QModelIndexList item
= d
->intersectionSet(QRect(event
->pos(), event
->pos()));
997 if (item
.count() == 1)
999 d
->hovered
= item
[0];
1003 d
->hovered
= QModelIndex();
1006 const QString previousHoveredCategory
= d
->hoveredCategory
;
1008 d
->mousePosition
= event
->pos();
1009 d
->hoveredCategory
= QString();
1011 // Redraw categories
1012 foreach (const QString
&category
, d
->categories
)
1014 if (d
->categoryVisualRect(category
).intersects(QRect(event
->pos(), event
->pos())))
1016 d
->hoveredCategory
= category
;
1017 viewport()->update(d
->categoryVisualRect(category
));
1019 else if ((category
== previousHoveredCategory
) &&
1020 (!d
->categoryVisualRect(previousHoveredCategory
).intersects(QRect(event
->pos(), event
->pos()))))
1022 viewport()->update(d
->categoryVisualRect(category
));
1027 if (d
->mouseButtonPressed
&& !d
->isDragging
)
1029 QPoint start
, end
, initialPressPosition
;
1031 initialPressPosition
= d
->initialPressPosition
;
1033 initialPressPosition
.setY(initialPressPosition
.y() - verticalOffset());
1034 initialPressPosition
.setX(initialPressPosition
.x() - horizontalOffset());
1036 if (d
->initialPressPosition
.x() > d
->mousePosition
.x() ||
1037 d
->initialPressPosition
.y() > d
->mousePosition
.y())
1039 start
= d
->mousePosition
;
1040 end
= initialPressPosition
;
1044 start
= initialPressPosition
;
1045 end
= d
->mousePosition
;
1048 rect
= QRect(start
, end
).adjusted(-16, -16, 16, 16);
1049 rect
= rect
.united(QRect(start
, end
).adjusted(16, 16, -16, -16)).intersected(viewport()->rect());
1051 viewport()->update(rect
);
1055 void KCategorizedView::mousePressEvent(QMouseEvent
*event
)
1057 d
->dragLeftViewport
= false;
1059 if (event
->button() == Qt::LeftButton
)
1061 d
->mouseButtonPressed
= true;
1063 d
->initialPressPosition
= event
->pos();
1064 d
->initialPressPosition
.setY(d
->initialPressPosition
.y() +
1066 d
->initialPressPosition
.setX(d
->initialPressPosition
.x() +
1067 horizontalOffset());
1069 else if (event
->button() == Qt::RightButton
)
1071 d
->rightMouseButtonPressed
= true;
1074 QListView::mousePressEvent(event
);
1076 d
->lastSelection
= selectionModel()->selection();
1078 viewport()->update(d
->categoryVisualRect(d
->hoveredCategory
));
1081 void KCategorizedView::mouseReleaseEvent(QMouseEvent
*event
)
1083 d
->mouseButtonPressed
= false;
1084 d
->rightMouseButtonPressed
= false;
1086 QListView::mouseReleaseEvent(event
);
1088 if ((viewMode() != KCategorizedView::IconMode
) || !d
->proxyModel
||
1089 !d
->categoryDrawer
|| !d
->proxyModel
->isCategorizedModel())
1094 QPoint initialPressPosition
= viewport()->mapFromGlobal(QCursor::pos());
1095 initialPressPosition
.setY(initialPressPosition
.y() + verticalOffset());
1096 initialPressPosition
.setX(initialPressPosition
.x() + horizontalOffset());
1098 if ((selectionMode() != SingleSelection
) && (selectionMode() != NoSelection
) &&
1099 (initialPressPosition
== d
->initialPressPosition
))
1101 foreach(const QString
&category
, d
->categories
)
1103 if (d
->categoryVisualRect(category
).contains(event
->pos()))
1105 QItemSelection selection
= selectionModel()->selection();
1106 QModelIndexList indexList
= d
->categoriesIndexes
[category
];
1108 foreach (const QModelIndex
&index
, indexList
)
1110 QModelIndex selectIndex
= index
.model()->index(index
.row(), 0);
1112 selection
<< QItemSelectionRange(selectIndex
);
1115 selectionModel()->select(selection
, QItemSelectionModel::SelectCurrent
);
1125 QPoint start
, end
, initialPressPosition
;
1127 initialPressPosition
= d
->initialPressPosition
;
1129 initialPressPosition
.setY(initialPressPosition
.y() - verticalOffset());
1130 initialPressPosition
.setX(initialPressPosition
.x() - horizontalOffset());
1132 if (d
->initialPressPosition
.x() > d
->mousePosition
.x() ||
1133 d
->initialPressPosition
.y() > d
->mousePosition
.y())
1135 start
= d
->mousePosition
;
1136 end
= initialPressPosition
;
1140 start
= initialPressPosition
;
1141 end
= d
->mousePosition
;
1144 rect
= QRect(start
, end
).adjusted(-16, -16, 16, 16);
1145 rect
= rect
.united(QRect(start
, end
).adjusted(16, 16, -16, -16)).intersected(viewport()->rect());
1147 viewport()->update(rect
);
1150 if (d
->hovered
.isValid())
1151 viewport()->update(visualRect(d
->hovered
));
1152 else if (!d
->hoveredCategory
.isEmpty())
1153 viewport()->update(d
->categoryVisualRect(d
->hoveredCategory
));
1156 void KCategorizedView::leaveEvent(QEvent
*event
)
1158 d
->hovered
= QModelIndex();
1159 d
->hoveredCategory
= QString();
1161 QListView::leaveEvent(event
);
1164 void KCategorizedView::startDrag(Qt::DropActions supportedActions
)
1166 // FIXME: QAbstractItemView does far better here since it sets the
1167 // pixmap of selected icons to the dragging cursor, but it sets a non
1168 // ARGB window so it is no transparent. Use QAbstractItemView when
1169 // this is fixed on Qt.
1170 // QAbstractItemView::startDrag(supportedActions);
1171 #if !defined(DOLPHIN_DRAGANDDROP)
1172 QListView::startDrag(supportedActions
);
1175 d
->isDragging
= false;
1176 d
->mouseButtonPressed
= false;
1177 d
->rightMouseButtonPressed
= false;
1179 viewport()->update(d
->lastDraggedItemsRect
);
1182 void KCategorizedView::dragMoveEvent(QDragMoveEvent
*event
)
1184 d
->mousePosition
= event
->pos();
1186 if (d
->mouseButtonPressed
)
1188 d
->isDragging
= true;
1192 d
->isDragging
= false;
1195 d
->dragLeftViewport
= false;
1197 #if defined(DOLPHIN_DRAGANDDROP)
1198 QAbstractItemView::dragMoveEvent(event
);
1200 QListView::dragMoveEvent(event
);
1203 if ((viewMode() != KCategorizedView::IconMode
) || !d
->proxyModel
||
1204 !d
->categoryDrawer
|| !d
->proxyModel
->isCategorizedModel())
1209 d
->drawDraggedItems();
1212 void KCategorizedView::dragLeaveEvent(QDragLeaveEvent
*event
)
1214 d
->dragLeftViewport
= true;
1216 #if defined(DOLPHIN_DRAGANDDROP)
1217 QAbstractItemView::dragLeaveEvent(event
);
1219 QListView::dragLeaveEvent(event
);
1223 void KCategorizedView::dropEvent(QDropEvent
*event
)
1225 #if defined(DOLPHIN_DRAGANDDROP)
1226 QAbstractItemView::dropEvent(event
);
1228 QListView::dropEvent(event
);
1232 QModelIndex
KCategorizedView::moveCursor(CursorAction cursorAction
,
1233 Qt::KeyboardModifiers modifiers
)
1235 if ((viewMode() != KCategorizedView::IconMode
) ||
1237 !d
->categoryDrawer
||
1238 d
->categories
.isEmpty() ||
1239 !d
->proxyModel
->isCategorizedModel()
1242 return QListView::moveCursor(cursorAction
, modifiers
);
1245 int viewportWidth
= viewport()->width() - spacing();
1248 if (gridSize().isEmpty())
1250 itemWidth
= d
->biggestItemSize
.width();
1254 itemWidth
= gridSize().width();
1257 int itemWidthPlusSeparation
= spacing() + itemWidth
;
1258 int elementsPerRow
= viewportWidth
/ itemWidthPlusSeparation
;
1259 if (!elementsPerRow
)
1262 QModelIndex current
= selectionModel()->currentIndex();
1264 if (!current
.isValid())
1266 if (cursorAction
== MoveEnd
)
1268 current
= model()->index(model()->rowCount() - 1, 0, QModelIndex());
1269 d
->forcedSelectionPosition
= d
->elementsInfo
[current
.row()].relativeOffsetToCategory
% elementsPerRow
;
1273 current
= model()->index(0, 0, QModelIndex());
1274 d
->forcedSelectionPosition
= 0;
1279 else if (!current
.isValid())
1281 return QModelIndex();
1284 QString lastCategory
= d
->categories
.first();
1285 QString theCategory
= d
->categories
.first();
1286 QString afterCategory
= d
->categories
.first();
1288 bool hasToBreak
= false;
1289 foreach (const QString
&category
, d
->categories
)
1293 afterCategory
= category
;
1298 if (category
== d
->elementsInfo
[current
.row()].category
)
1300 theCategory
= category
;
1307 lastCategory
= category
;
1311 switch (cursorAction
)
1313 case QAbstractItemView::MoveUp
: {
1314 if (d
->elementsInfo
[current
.row()].relativeOffsetToCategory
>= elementsPerRow
)
1316 int indexToMove
= current
.row();
1317 indexToMove
-= qMin(((d
->elementsInfo
[current
.row()].relativeOffsetToCategory
) + d
->forcedSelectionPosition
), elementsPerRow
- d
->forcedSelectionPosition
+ (d
->elementsInfo
[current
.row()].relativeOffsetToCategory
% elementsPerRow
));
1319 return d
->proxyModel
->index(indexToMove
, 0);
1323 int lastCategoryLastRow
= (d
->categoriesIndexes
[lastCategory
].count() - 1) % elementsPerRow
;
1324 int indexToMove
= current
.row() - d
->elementsInfo
[current
.row()].relativeOffsetToCategory
;
1326 if (d
->forcedSelectionPosition
>= lastCategoryLastRow
)
1332 indexToMove
-= qMin((lastCategoryLastRow
- d
->forcedSelectionPosition
+ 1), d
->forcedSelectionPosition
+ elementsPerRow
+ 1);
1335 return d
->proxyModel
->index(indexToMove
, 0);
1339 case QAbstractItemView::MoveDown
: {
1340 if (d
->elementsInfo
[current
.row()].relativeOffsetToCategory
< (d
->categoriesIndexes
[theCategory
].count() - 1 - ((d
->categoriesIndexes
[theCategory
].count() - 1) % elementsPerRow
)))
1342 int indexToMove
= current
.row();
1343 indexToMove
+= qMin(elementsPerRow
, d
->categoriesIndexes
[theCategory
].count() - 1 - d
->elementsInfo
[current
.row()].relativeOffsetToCategory
);
1345 return d
->proxyModel
->index(indexToMove
, 0);
1349 int afterCategoryLastRow
= qMin(elementsPerRow
, d
->categoriesIndexes
[afterCategory
].count());
1350 int indexToMove
= current
.row() + (d
->categoriesIndexes
[theCategory
].count() - d
->elementsInfo
[current
.row()].relativeOffsetToCategory
);
1352 if (d
->forcedSelectionPosition
>= afterCategoryLastRow
)
1354 indexToMove
+= afterCategoryLastRow
- 1;
1358 indexToMove
+= qMin(d
->forcedSelectionPosition
, elementsPerRow
);
1361 return d
->proxyModel
->index(indexToMove
, 0);
1365 case QAbstractItemView::MoveLeft
:
1366 if (layoutDirection() == Qt::RightToLeft
)
1368 if (!(d
->elementsInfo
[current
.row() + 1].relativeOffsetToCategory
% elementsPerRow
))
1371 d
->forcedSelectionPosition
= d
->elementsInfo
[current
.row() + 1].relativeOffsetToCategory
% elementsPerRow
;
1373 #if 0 //follow qt view behavior. lateral movements won't change visual row
1374 if (d
->forcedSelectionPosition
< 0)
1375 d
->forcedSelectionPosition
= (d
->categoriesIndexes
[theCategory
].count() - 1) % elementsPerRow
;
1378 return d
->proxyModel
->index(current
.row() + 1, 0);
1381 if (!(d
->elementsInfo
[current
.row()].relativeOffsetToCategory
% elementsPerRow
))
1384 d
->forcedSelectionPosition
= d
->elementsInfo
[current
.row() - 1].relativeOffsetToCategory
% elementsPerRow
;
1386 #if 0 //follow qt view behavior. lateral movements won't change visual row
1387 if (d
->forcedSelectionPosition
< 0)
1388 d
->forcedSelectionPosition
= (d
->categoriesIndexes
[theCategory
].count() - 1) % elementsPerRow
;
1391 return d
->proxyModel
->index(current
.row() - 1, 0);
1393 case QAbstractItemView::MoveRight
:
1394 if (layoutDirection() == Qt::RightToLeft
)
1396 if (!(d
->elementsInfo
[current
.row()].relativeOffsetToCategory
% elementsPerRow
))
1399 d
->forcedSelectionPosition
= d
->elementsInfo
[current
.row() - 1].relativeOffsetToCategory
% elementsPerRow
;
1401 #if 0 //follow qt view behavior. lateral movements won't change visual row
1402 if (d
->forcedSelectionPosition
< 0)
1403 d
->forcedSelectionPosition
= (d
->categoriesIndexes
[theCategory
].count() - 1) % elementsPerRow
;
1406 return d
->proxyModel
->index(current
.row() - 1, 0);
1409 if (!(d
->elementsInfo
[current
.row() + 1].relativeOffsetToCategory
% elementsPerRow
))
1412 d
->forcedSelectionPosition
= d
->elementsInfo
[current
.row() + 1].relativeOffsetToCategory
% elementsPerRow
;
1414 #if 0 //follow qt view behavior. lateral movements won't change visual row
1415 if (d
->forcedSelectionPosition
< 0)
1416 d
->forcedSelectionPosition
= (d
->categoriesIndexes
[theCategory
].count() - 1) % elementsPerRow
;
1419 return d
->proxyModel
->index(current
.row() + 1, 0);
1425 return QListView::moveCursor(cursorAction
, modifiers
);
1428 void KCategorizedView::rowsInserted(const QModelIndex
&parent
,
1432 QListView::rowsInserted(parent
, start
, end
);
1434 if ((viewMode() != KCategorizedView::IconMode
) || !d
->proxyModel
||
1435 !d
->categoryDrawer
|| !d
->proxyModel
->isCategorizedModel())
1437 d
->forcedSelectionPosition
= 0;
1438 d
->elementsInfo
.clear();
1439 d
->elementsPosition
.clear();
1440 d
->categoriesIndexes
.clear();
1441 d
->categoriesPosition
.clear();
1442 d
->categories
.clear();
1443 d
->intersectedIndexes
.clear();
1444 d
->modelIndexList
.clear();
1445 d
->hovered
= QModelIndex();
1446 d
->biggestItemSize
= QSize(0, 0);
1447 d
->mouseButtonPressed
= false;
1448 d
->rightMouseButtonPressed
= false;
1453 rowsInsertedArtifficial(parent
, start
, end
);
1456 void KCategorizedView::rowsInsertedArtifficial(const QModelIndex
&parent
,
1462 d
->forcedSelectionPosition
= 0;
1463 d
->elementsInfo
.clear();
1464 d
->elementsPosition
.clear();
1465 d
->categoriesIndexes
.clear();
1466 d
->categoriesPosition
.clear();
1467 d
->categories
.clear();
1468 d
->intersectedIndexes
.clear();
1469 d
->modelIndexList
.clear();
1470 d
->hovered
= QModelIndex();
1471 d
->biggestItemSize
= QSize(0, 0);
1472 d
->mouseButtonPressed
= false;
1473 d
->rightMouseButtonPressed
= false;
1475 if (start
> end
|| end
< 0 || start
< 0 || !d
->proxyModel
->rowCount())
1480 // Add all elements mapped to the source model and explore categories
1481 QString prevCategory
= d
->proxyModel
->data(d
->proxyModel
->index(0, d
->proxyModel
->sortColumn()), KCategorizedSortFilterProxyModel::CategoryDisplayRole
).toString();
1482 QString lastCategory
= prevCategory
;
1483 QModelIndexList modelIndexList
;
1484 struct Private::ElementInfo elementInfo
;
1486 for (int k
= 0; k
< d
->proxyModel
->rowCount(); ++k
)
1488 QModelIndex index
= d
->proxyModel
->index(k
, d
->proxyModel
->sortColumn());
1489 QModelIndex indexSize
= d
->proxyModel
->index(k
, 0);
1491 d
->biggestItemSize
= QSize(qMax(sizeHintForIndex(indexSize
).width(),
1492 d
->biggestItemSize
.width()),
1493 qMax(sizeHintForIndex(indexSize
).height(),
1494 d
->biggestItemSize
.height()));
1496 d
->modelIndexList
<< index
;
1498 lastCategory
= d
->proxyModel
->data(index
, KCategorizedSortFilterProxyModel::CategoryDisplayRole
).toString();
1500 elementInfo
.category
= lastCategory
;
1502 if (prevCategory
!= lastCategory
)
1505 d
->categoriesIndexes
.insert(prevCategory
, modelIndexList
);
1506 d
->categories
<< prevCategory
;
1507 modelIndexList
.clear();
1514 elementInfo
.relativeOffsetToCategory
= offset
;
1516 modelIndexList
<< index
;
1517 prevCategory
= lastCategory
;
1519 d
->elementsInfo
.insert(index
.row(), elementInfo
);
1522 d
->categoriesIndexes
.insert(prevCategory
, modelIndexList
);
1523 d
->categories
<< prevCategory
;
1525 d
->updateScrollbars();
1527 // FIXME: We need to safely save the last selection. This is on my TODO
1528 // list (ereslibre).
1529 selectionModel()->clear();
1532 void KCategorizedView::rowsRemoved(const QModelIndex
&parent
,
1536 if ((viewMode() == KCategorizedView::IconMode
) && d
->proxyModel
&&
1537 d
->categoryDrawer
&& d
->proxyModel
->isCategorizedModel())
1539 // Force the view to update all elements
1540 rowsInsertedArtifficial(QModelIndex(), 0, d
->proxyModel
->rowCount() - 1);
1544 void KCategorizedView::updateGeometries()
1546 if ((viewMode() != KCategorizedView::IconMode
) || !d
->proxyModel
||
1547 !d
->categoryDrawer
|| !d
->proxyModel
->isCategorizedModel())
1549 QListView::updateGeometries();
1553 // Avoid QListView::updateGeometries(), since it will try to set another
1554 // range to our scroll bars, what we don't want (ereslibre)
1555 QAbstractItemView::updateGeometries();
1558 void KCategorizedView::slotLayoutChanged()
1563 void KCategorizedView::currentChanged(const QModelIndex
¤t
,
1564 const QModelIndex
&previous
)
1566 // We need to update the forcedSelectionPosition property in order to correctly
1567 // navigate after with keyboard using up & down keys
1569 int viewportWidth
= viewport()->width() - spacing();
1574 if (gridSize().isEmpty())
1576 itemHeight
= d
->biggestItemSize
.height();
1577 itemWidth
= d
->biggestItemSize
.width();
1581 itemHeight
= gridSize().height();
1582 itemWidth
= gridSize().width();
1585 int itemWidthPlusSeparation
= spacing() + itemWidth
;
1586 int elementsPerRow
= viewportWidth
/ itemWidthPlusSeparation
;
1587 if (!elementsPerRow
)
1590 if (d
->mouseButtonPressed
|| d
->rightMouseButtonPressed
)
1591 d
->forcedSelectionPosition
= d
->elementsInfo
[current
.row()].relativeOffsetToCategory
% elementsPerRow
;
1593 QListView::currentChanged(current
, previous
);
1596 #include "kcategorizedview.moc"