33 #include "kcategorizedview_p.h"
40 #include <QPaintEvent>
47 struct KCategorizedView::Private::Item
59 struct KCategorizedView::Private::Block
64 , firstIndex(QModelIndex())
65 , quarantineStart(QModelIndex())
67 , outOfQuarantine(false)
75 return firstIndex != rhs.firstIndex;
78 static bool lessThan(
const Block &left,
const Block &right)
80 Q_ASSERT(left.firstIndex.isValid());
81 Q_ASSERT(right.firstIndex.isValid());
82 return left.firstIndex.row() < right.firstIndex.row();
87 QPersistentModelIndex firstIndex;
94 QPersistentModelIndex quarantineStart;
100 bool outOfQuarantine;
111 , categoryDrawerV2(0)
112 , categoryDrawerV3(0)
114 , alternatingBlockColors(false)
115 , collapsibleBlocks(false)
116 , hoveredBlock(new Block())
117 , hoveredIndex(QModelIndex())
118 , pressedPosition(
QPoint())
119 , rubberBandRect(
QRect())
123 KCategorizedView::Private::~Private()
128 bool KCategorizedView::Private::isCategorized()
const
130 return proxyModel && categoryDrawer && proxyModel->isCategorizedModel();
133 QStyleOptionViewItemV4 KCategorizedView::Private::blockRect(
const QModelIndex &representative)
135 QStyleOptionViewItemV4 option(q->viewOptions());
136 const int height = categoryDrawer->categoryHeight(representative, option);
138 QPoint pos = blockPosition(categoryDisplay);
140 option.rect.setTopLeft(pos);
141 option.rect.setWidth(viewportWidth() + categoryDrawer->leftMargin() + categoryDrawer->rightMargin());
142 option.rect.setHeight(height + blockHeight(categoryDisplay));
143 option.rect = mapToViewport(option.rect);
150 const int rowCount = proxyModel->rowCount();
152 const QRect rect = _rect.normalized();
156 int top = rowCount - 1;
157 while (bottom <= top) {
158 const int middle = (bottom + top) / 2;
159 const QModelIndex index = proxyModel->index(middle, q->modelColumn(), q->rootIndex());
160 const QRect itemRect = q->visualRect(index);
161 if (itemRect.bottomRight().y() <= rect.topLeft().y()) {
168 const QModelIndex bottomIndex = proxyModel->index(bottom, q->modelColumn(), q->rootIndex());
173 while (bottom <= top) {
174 const int middle = (bottom + top) / 2;
175 const QModelIndex index = proxyModel->index(middle, q->modelColumn(), q->rootIndex());
176 const QRect itemRect = q->visualRect(index);
177 if (itemRect.topLeft().y() <= rect.bottomRight().y()) {
184 const QModelIndex topIndex = proxyModel->index(top, q->modelColumn(), q->rootIndex());
186 return qMakePair(bottomIndex, topIndex);
189 QPoint KCategorizedView::Private::blockPosition(
const QString &category)
191 Block &block = blocks[category];
193 if (block.outOfQuarantine && !block.topLeft.isNull()) {
194 return block.topLeft;
197 QPoint res(categorySpacing, 0);
199 const QModelIndex index = block.firstIndex;
203 const QModelIndex categoryIndex = block.firstIndex;
204 if (index.row() < categoryIndex.row()) {
207 res.ry() += categoryDrawer->categoryHeight(categoryIndex, q->viewOptions()) + categorySpacing;
208 if (index.row() == categoryIndex.row()) {
211 res.ry() += blockHeight(it.key());
214 block.outOfQuarantine =
true;
220 int KCategorizedView::Private::blockHeight(
const QString &category)
222 Block &block = blocks[category];
224 if (block.collapsed) {
228 if (block.height > -1) {
232 const QModelIndex firstIndex = block.firstIndex;
233 const QModelIndex lastIndex = proxyModel->index(firstIndex.row() + block.items.count() - 1, q->modelColumn(), q->rootIndex());
234 const QRect topLeft = q->visualRect(firstIndex);
235 QRect bottomRight = q->visualRect(lastIndex);
238 bottomRight.setHeight(qMax(bottomRight.height(), q->gridSize().height()));
240 if (!q->uniformItemSizes()) {
241 bottomRight.setHeight(highestElementInLastRow(block) + q->spacing() * 2);
245 const int height = bottomRight.bottomRight().y() - topLeft.topLeft().y() + 1;
246 block.height = height;
251 int KCategorizedView::Private::viewportWidth()
const
253 return q->viewport()->width() - categorySpacing * 2 - categoryDrawer->leftMargin() - categoryDrawer->rightMargin();
256 void KCategorizedView::Private::regenerateAllElements()
260 block.outOfQuarantine =
false;
261 block.quarantineStart = block.firstIndex;
266 void KCategorizedView::Private::rowsInserted(
const QModelIndex &parent,
int start,
int end)
268 if (!isCategorized()) {
272 for (
int i = start; i <=
end; ++i) {
273 const QModelIndex index = proxyModel->index(i, q->modelColumn(), parent);
275 Q_ASSERT(index.isValid());
277 const QString category = categoryForIndex(index);
279 Block &block = blocks[category];
285 const QModelIndex firstIndex = block.firstIndex;
286 if (!firstIndex.isValid() || index.row() < firstIndex.row()) {
287 block.firstIndex = index;
291 Q_ASSERT(block.firstIndex.isValid());
293 const int firstIndexRow = block.firstIndex.row();
295 block.items.insert(index.row() - firstIndexRow, Private::Item());
298 q->visualRect(index);
299 q->viewport()->update();
304 const QModelIndex lastIndex = proxyModel->index(end, q->modelColumn(), parent);
305 const QString category = categoryForIndex(lastIndex);
306 Private::Block &block = blocks[category];
307 block.quarantineStart = block.firstIndex;
313 const QModelIndex firstIndex = proxyModel->index(start, q->modelColumn(), parent);
314 const QString category = categoryForIndex(firstIndex);
315 const QModelIndex firstAffectedCategory = blocks[category].firstIndex;
320 foreach (
const Block &block, blockList) {
321 firstIndexesRows << block.firstIndex.row();
325 Private::Block &block = *it;
326 if (block.firstIndex.row() > firstAffectedCategory.row()) {
327 block.outOfQuarantine =
false;
328 block.alternate = firstIndexesRows.indexOf(block.firstIndex.row()) % 2;
329 }
else if (block.firstIndex.row() == firstAffectedCategory.row()) {
330 block.alternate = firstIndexesRows.indexOf(block.firstIndex.row()) % 2;
337 QRect KCategorizedView::Private::mapToViewport(
const QRect &rect)
const
339 const int dx = -q->horizontalOffset();
340 const int dy = -q->verticalOffset();
341 return rect.adjusted(dx, dy, dx, dy);
344 QRect KCategorizedView::Private::mapFromViewport(
const QRect &rect)
const
346 const int dx = q->horizontalOffset();
347 const int dy = q->verticalOffset();
348 return rect.adjusted(dx, dy, dx, dy);
351 int KCategorizedView::Private::highestElementInLastRow(
const Block &block)
const
354 const QModelIndex lastIndex = proxyModel->index(block.firstIndex.row() + block.items.count() - 1, q->modelColumn(), q->rootIndex());
355 const QRect prevRect = q->visualRect(lastIndex);
356 int res = prevRect.height();
357 QModelIndex prevIndex = proxyModel->index(lastIndex.row() - 1, q->modelColumn(), q->rootIndex());
358 if (!prevIndex.isValid()) {
362 const QRect tempRect = q->visualRect(prevIndex);
363 if (tempRect.topLeft().y() < prevRect.topLeft().y()) {
366 res = qMax(res, tempRect.height());
367 if (prevIndex == block.firstIndex) {
370 prevIndex = proxyModel->index(prevIndex.row() - 1, q->modelColumn(), q->rootIndex());
376 bool KCategorizedView::Private::hasGrid()
const
378 const QSize gridSize = q->gridSize();
379 return gridSize.isValid() && !gridSize.isNull();
382 QString KCategorizedView::Private::categoryForIndex(
const QModelIndex &index)
const
384 const QModelIndex categoryIndex = index.model()->index(index.row(), proxyModel->sortColumn(), index.parent());
388 void KCategorizedView::Private::leftToRightVisualRect(
const QModelIndex &index, Item &item,
389 const Block &block,
const QPoint &blockPos)
const
391 const int firstIndexRow = block.firstIndex.row();
394 const int relativeRow = index.row() - firstIndexRow;
395 const int maxItemsPerRow = qMax(viewportWidth() / q->gridSize().width(), 1);
396 if (q->layoutDirection() == Qt::LeftToRight) {
397 item.topLeft.rx() = (relativeRow % maxItemsPerRow) * q->gridSize().width() + blockPos.x() + categoryDrawer->leftMargin();
399 item.topLeft.rx() = viewportWidth() - ((relativeRow % maxItemsPerRow) + 1) * q->gridSize().width() + categoryDrawer->leftMargin() + categorySpacing;
401 item.topLeft.ry() = (relativeRow / maxItemsPerRow) * q->gridSize().height();
403 if (q->uniformItemSizes()) {
404 const int relativeRow = index.row() - firstIndexRow;
405 const QSize itemSize = q->sizeHintForIndex(index);
406 const int maxItemsPerRow = qMax((viewportWidth() - q->spacing()) / (itemSize.width() + q->spacing()), 1);
407 if (q->layoutDirection() == Qt::LeftToRight) {
408 item.topLeft.rx() = (relativeRow % maxItemsPerRow) * itemSize.width() + blockPos.x() + categoryDrawer->leftMargin();
410 item.topLeft.rx() = viewportWidth() - (relativeRow % maxItemsPerRow) * itemSize.width() + categoryDrawer->leftMargin() + categorySpacing;
412 item.topLeft.ry() = (relativeRow / maxItemsPerRow) * itemSize.height();
414 const QSize currSize = q->sizeHintForIndex(index);
415 if (index != block.firstIndex) {
416 const int viewportW = viewportWidth() - q->spacing();
417 QModelIndex prevIndex = proxyModel->index(index.row() - 1, q->modelColumn(), q->rootIndex());
418 QRect prevRect = q->visualRect(prevIndex);
419 prevRect = mapFromViewport(prevRect);
420 if ((prevRect.bottomRight().x() + 1) + currSize.width() - blockPos.x() + q->spacing() > viewportW) {
424 prevIndex = proxyModel->index(prevIndex.row() - 1, q->modelColumn(), q->rootIndex());
425 const QRect tempRect = q->visualRect(prevIndex);
426 if (tempRect.topLeft().y() < prevRect.topLeft().y()) {
429 if (tempRect.bottomRight().y() > prevRect.bottomRight().y()) {
432 if (prevIndex == block.firstIndex) {
436 if (q->layoutDirection() == Qt::LeftToRight) {
437 item.topLeft.rx() = categoryDrawer->leftMargin() + blockPos.x() + q->spacing();
439 item.topLeft.rx() = viewportWidth() - currSize.width() + categoryDrawer->leftMargin() + categorySpacing;
441 item.topLeft.ry() = (prevRect.bottomRight().y() + 1) + q->spacing() - blockPos.y();
443 if (q->layoutDirection() == Qt::LeftToRight) {
444 item.topLeft.rx() = (prevRect.bottomRight().x() + 1) + q->spacing();
446 item.topLeft.rx() = (prevRect.bottomLeft().x() - 1) - q->spacing() - item.size.width() + categoryDrawer->leftMargin() + categorySpacing;
448 item.topLeft.ry() = prevRect.topLeft().y() - blockPos.y();
451 if (q->layoutDirection() == Qt::LeftToRight) {
452 item.topLeft.rx() = blockPos.x() + categoryDrawer->leftMargin() + q->spacing();
454 item.topLeft.rx() = viewportWidth() - currSize.width() + categoryDrawer->leftMargin() + categorySpacing;
456 item.topLeft.ry() = q->spacing();
460 item.size = q->sizeHintForIndex(index);
463 void KCategorizedView::Private::topToBottomVisualRect(
const QModelIndex &index, Item &item,
464 const Block &block,
const QPoint &blockPos)
const
466 const int firstIndexRow = block.firstIndex.row();
469 const int relativeRow = index.row() - firstIndexRow;
470 item.topLeft.rx() = blockPos.x() + categoryDrawer->leftMargin();
471 item.topLeft.ry() = relativeRow * q->gridSize().height();
473 if (q->uniformItemSizes()) {
474 const int relativeRow = index.row() - firstIndexRow;
475 const QSize itemSize = q->sizeHintForIndex(index);
476 item.topLeft.rx() = blockPos.x() + categoryDrawer->leftMargin();
477 item.topLeft.ry() = relativeRow * itemSize.height();
479 if (index != block.firstIndex) {
480 QModelIndex prevIndex = proxyModel->index(index.row() - 1, q->modelColumn(), q->rootIndex());
481 QRect prevRect = q->visualRect(prevIndex);
482 prevRect = mapFromViewport(prevRect);
483 item.topLeft.rx() = blockPos.x() + categoryDrawer->leftMargin() + q->spacing();
484 item.topLeft.ry() = (prevRect.bottomRight().y() + 1) + q->spacing() - blockPos.y();
486 item.topLeft.rx() = blockPos.x() + categoryDrawer->leftMargin() + q->spacing();
487 item.topLeft.ry() = q->spacing();
491 item.size = q->sizeHintForIndex(index);
492 item.size.setWidth(viewportWidth());
495 void KCategorizedView::Private::_k_slotCollapseOrExpandClicked(QModelIndex)
505 , d(new Private(this))
516 if (d->proxyModel == model) {
523 disconnect(d->proxyModel, SIGNAL(layoutChanged()),
this, SLOT(
slotLayoutChanged()));
529 connect(d->proxyModel, SIGNAL(layoutChanged()),
this, SLOT(
slotLayoutChanged()));
532 QListView::setModel(model);
535 if (model->rowCount()) {
547 d->regenerateAllElements();
548 QListView::setGridSize(size);
553 if (!d->isCategorized()) {
554 return QListView::visualRect(index);
557 if (!index.isValid()) {
561 const QString category = d->categoryForIndex(index);
563 if (!d->blocks.contains(category)) {
567 Private::Block &block = d->blocks[category];
568 const int firstIndexRow = block.firstIndex.row();
570 Q_ASSERT(block.firstIndex.isValid());
572 if (index.row() - firstIndexRow < 0 || index.row() - firstIndexRow >= block.items.count()) {
576 const QPoint blockPos = d->blockPosition(category);
578 Private::Item &ritem = block.items[index.row() - firstIndexRow];
580 if (ritem.topLeft.isNull() || (block.quarantineStart.isValid() &&
581 index.row() >= block.quarantineStart.row())) {
582 if (flow() == LeftToRight) {
583 d->leftToRightVisualRect(index, ritem, block, blockPos);
585 d->topToBottomVisualRect(index, ritem, block, blockPos);
589 const bool wasLastIndex = (index.row() == (block.firstIndex.row() + block.items.count() - 1));
590 if (index.row() == block.quarantineStart.row()) {
592 block.quarantineStart = QModelIndex();
594 const QModelIndex nextIndex = d->proxyModel->index(index.row() + 1, modelColumn(), rootIndex());
595 block.quarantineStart = nextIndex;
603 Private::Item item(ritem);
604 item.topLeft.ry() += blockPos.y();
606 const QSize sizeHint = item.size;
609 const QSize sizeGrid = gridSize();
610 const QSize resultingSize = sizeHint.boundedTo(sizeGrid);
611 QRect res(item.topLeft.x() + ((sizeGrid.width() - resultingSize.width()) / 2),
612 item.topLeft.y(), resultingSize.width(), resultingSize.height());
613 if (block.collapsed) {
616 res.setLeft(-resultingSize.width());
619 return d->mapToViewport(res);
622 QRect res(item.topLeft.x(), item.topLeft.y(), sizeHint.width(), sizeHint.height());
623 if (block.collapsed) {
626 res.setLeft(-sizeHint.width());
629 return d->mapToViewport(res);
634 return d->categoryDrawer;
639 if (d->categoryDrawerV2) {
640 disconnect(d->categoryDrawerV2, SIGNAL(collapseOrExpandClicked(QModelIndex)),
641 this, SLOT(_k_slotCollapseOrExpandClicked(QModelIndex)));
648 if (d->categoryDrawerV2) {
649 connect(d->categoryDrawerV2, SIGNAL(collapseOrExpandClicked(QModelIndex)),
650 this, SLOT(_k_slotCollapseOrExpandClicked(QModelIndex)));
656 return d->categorySpacing;
661 if (d->categorySpacing == categorySpacing) {
668 Private::Block &block = *it;
669 block.outOfQuarantine =
false;
675 return d->alternatingBlockColors;
680 d->alternatingBlockColors = enable;
685 return d->collapsibleBlocks;
690 d->collapsibleBlocks = enable;
696 const Private::Block &block = d->blocks[category];
697 if (block.height == -1) {
700 QModelIndex current = block.firstIndex;
701 const int first = current.row();
702 for (
int i = 1; i <= block.items.count(); ++i) {
703 if (current.isValid()) {
706 current = d->proxyModel->index(first + i, modelColumn(), rootIndex());
718 if (!d->isCategorized()) {
719 return QListView::indexAt(point);
722 const int rowCount = d->proxyModel->rowCount();
724 return QModelIndex();
729 int top = rowCount - 1;
730 while (bottom <= top) {
731 const int middle = (bottom + top) / 2;
732 const QModelIndex index = d->proxyModel->index(middle, modelColumn(), rootIndex());
734 const int verticalOff = verticalOffset();
735 int horizontalOff = horizontalOffset();
736 if (layoutDirection() == Qt::RightToLeft) {
739 rect.topLeft().ry() += verticalOff;
740 rect.topLeft().rx() += horizontalOff;
741 rect.bottomRight().ry() += verticalOff;
742 rect.bottomRight().rx() += horizontalOff;
743 if (rect.contains(point)) {
744 if (index.model()->flags(index) & Qt::ItemIsEnabled) {
747 return QModelIndex();
749 bool directionCondition;
750 if (layoutDirection() == Qt::LeftToRight) {
751 directionCondition = point.x() > rect.bottomRight().x();
753 directionCondition = point.x() < rect.bottomLeft().x();
755 if (point.y() > rect.bottomRight().y() ||
756 (point.y() > rect.topLeft().y() && point.y() < rect.bottomRight().y() && directionCondition)) {
762 return QModelIndex();
773 if (!d->isCategorized()) {
774 QListView::paintEvent(event);
780 QPainter p(viewport());
783 Q_ASSERT(selectionModel()->model() == d->proxyModel);
787 while (it != d->blocks.constEnd()) {
788 const Private::Block &block = *it;
789 const QModelIndex categoryIndex = d->proxyModel->index(block.firstIndex.row(), d->proxyModel->sortColumn(), rootIndex());
790 QStyleOptionViewItemV4 option(viewOptions());
791 option.features |= d->alternatingBlockColors && block.alternate ? QStyleOptionViewItemV4::Alternate
792 : QStyleOptionViewItemV4::None;
793 option.state |= !d->collapsibleBlocks || !block.collapsed ? QStyle::State_Open
794 : QStyle::State_None;
795 const int height = d->categoryDrawer->categoryHeight(categoryIndex, option);
796 QPoint pos = d->blockPosition(it.key());
798 option.rect.setTopLeft(pos);
799 option.rect.setWidth(d->viewportWidth() + d->categoryDrawer->leftMargin() + d->categoryDrawer->rightMargin());
800 option.rect.setHeight(height + d->blockHeight(it.key()));
801 option.rect = d->mapToViewport(option.rect);
802 if (!option.rect.intersects(viewport()->rect())) {
806 d->categoryDrawer->drawCategory(categoryIndex, d->proxyModel->sortRole(), option, &p);
811 if (intersecting.first.isValid() && intersecting.second.isValid()) {
813 int i = intersecting.first.row();
814 int indexToCheckIfBlockCollapsed = i;
815 QModelIndex categoryIndex;
817 Private::Block *block = 0;
818 while (i <= intersecting.second.row()) {
820 if (i == indexToCheckIfBlockCollapsed) {
821 categoryIndex = d->proxyModel->index(i, d->proxyModel->sortColumn(), rootIndex());
823 block = &d->blocks[category];
824 indexToCheckIfBlockCollapsed = block->firstIndex.row() + block->items.count();
825 if (block->collapsed) {
826 i = indexToCheckIfBlockCollapsed;
834 const bool alternateItem = (i - block->firstIndex.row()) % 2;
836 const QModelIndex index = d->proxyModel->index(i, modelColumn(), rootIndex());
837 const Qt::ItemFlags flags = d->proxyModel->flags(index);
838 QStyleOptionViewItemV4 option(viewOptions());
840 option.widget =
this;
841 option.features |= wordWrap() ? QStyleOptionViewItemV2::WrapText
842 : QStyleOptionViewItemV2::None;
843 option.features |= alternatingRowColors() && alternateItem ? QStyleOptionViewItemV4::Alternate
844 : QStyleOptionViewItemV4::None;
845 if (flags & Qt::ItemIsSelectable) {
846 option.state |= selectionModel()->isSelected(index) ? QStyle::State_Selected
847 : QStyle::State_None;
849 option.state &= ~QStyle::State_Selected;
851 option.state |= (index == currentIndex()) ? QStyle::State_HasFocus
852 : QStyle::State_None;
853 if (!(flags & Qt::ItemIsEnabled)) {
854 option.state &= ~QStyle::State_Enabled;
856 option.state |= (index == d->hoveredIndex) ? QStyle::State_MouseOver
857 : QStyle::State_None;
860 itemDelegate(index)->paint(&p, option, index);
867 if (isSelectionRectVisible() && d->rubberBandRect.isValid()) {
868 QStyleOptionRubberBand opt;
870 opt.shape = QRubberBand::Rectangle;
872 opt.rect = d->mapToViewport(d->rubberBandRect).intersected(viewport()->rect().adjusted(-16, -16, 16, 16));
874 style()->drawControl(QStyle::CE_RubberBand, &opt, &p);
884 d->regenerateAllElements();
885 QListView::resizeEvent(event);
889 QItemSelectionModel::SelectionFlags flags)
891 if (!d->isCategorized()) {
892 QListView::setSelection(rect, flags);
896 if (rect.topLeft() == rect.bottomRight()) {
897 const QModelIndex index =
indexAt(rect.topLeft());
898 selectionModel()->select(index, flags);
904 QItemSelection selection;
907 QModelIndex firstIndex;
908 QModelIndex lastIndex;
909 for (
int i = intersecting.first.row(); i <= intersecting.second.row(); ++i) {
910 const QModelIndex index = d->proxyModel->index(i, modelColumn(), rootIndex());
911 const bool visualRectIntersects =
visualRect(index).intersects(rect);
912 if (firstIndex.isValid()) {
913 if (visualRectIntersects) {
916 selection << QItemSelectionRange(firstIndex, lastIndex);
917 firstIndex = QModelIndex();
919 }
else if (visualRectIntersects) {
925 if (firstIndex.isValid()) {
926 selection << QItemSelectionRange(firstIndex, lastIndex);
929 selectionModel()->select(selection, flags);
934 QListView::mouseMoveEvent(event);
935 d->hoveredIndex =
indexAt(event->pos());
936 const SelectionMode itemViewSelectionMode = selectionMode();
937 if (state() == DragSelectingState && isSelectionRectVisible() && itemViewSelectionMode != SingleSelection
938 && itemViewSelectionMode != NoSelection) {
939 QRect rect(d->pressedPosition, event->pos() +
QPoint(horizontalOffset(), verticalOffset()));
940 rect = rect.normalized();
941 update(rect.united(d->rubberBandRect));
942 d->rubberBandRect = rect;
944 if (!d->categoryDrawerV2) {
948 while (it != d->blocks.constEnd()) {
949 const Private::Block &block = *it;
950 const QModelIndex categoryIndex = d->proxyModel->index(block.firstIndex.row(), d->proxyModel->sortColumn(), rootIndex());
951 QStyleOptionViewItemV4 option(viewOptions());
952 const int height = d->categoryDrawer->categoryHeight(categoryIndex, option);
953 QPoint pos = d->blockPosition(it.key());
955 option.rect.setTopLeft(pos);
956 option.rect.setWidth(d->viewportWidth() + d->categoryDrawer->leftMargin() + d->categoryDrawer->rightMargin());
957 option.rect.setHeight(height + d->blockHeight(it.key()));
958 option.rect = d->mapToViewport(option.rect);
959 const QPoint mousePos = viewport()->mapFromGlobal(QCursor::pos());
960 if (option.rect.contains(mousePos)) {
961 if (d->categoryDrawerV3 && d->hoveredBlock->height != -1 && *d->hoveredBlock != block) {
962 const QModelIndex categoryIndex = d->proxyModel->index(d->hoveredBlock->firstIndex.row(), d->proxyModel->sortColumn(), rootIndex());
963 const QStyleOptionViewItemV4 option = d->blockRect(categoryIndex);
964 d->categoryDrawerV3->mouseLeft(categoryIndex, option.rect);
965 *d->hoveredBlock =
block;
966 d->hoveredCategory = it.key();
967 viewport()->update(option.rect);
968 }
else if (d->hoveredBlock->height == -1) {
969 *d->hoveredBlock =
block;
970 d->hoveredCategory = it.key();
971 }
else if (d->categoryDrawerV3) {
972 d->categoryDrawerV3->mouseMoved(categoryIndex, option.rect, event);
974 d->categoryDrawerV2->mouseButtonMoved(categoryIndex, event);
976 viewport()->update(option.rect);
981 if (d->categoryDrawerV3 && d->hoveredBlock->height != -1) {
982 const QModelIndex categoryIndex = d->proxyModel->index(d->hoveredBlock->firstIndex.row(), d->proxyModel->sortColumn(), rootIndex());
983 const QStyleOptionViewItemV4 option = d->blockRect(categoryIndex);
984 d->categoryDrawerV3->mouseLeft(categoryIndex, option.rect);
985 *d->hoveredBlock = Private::Block();
986 d->hoveredCategory =
QString();
987 viewport()->update(option.rect);
993 if (event->button() == Qt::LeftButton) {
994 d->pressedPosition =
event->pos();
995 d->pressedPosition.rx() += horizontalOffset();
996 d->pressedPosition.ry() += verticalOffset();
998 if (!d->categoryDrawerV2) {
999 QListView::mousePressEvent(event);
1003 while (it != d->blocks.constEnd()) {
1004 const Private::Block &block = *it;
1005 const QModelIndex categoryIndex = d->proxyModel->index(block.firstIndex.row(), d->proxyModel->sortColumn(), rootIndex());
1006 const QStyleOptionViewItemV4 option = d->blockRect(categoryIndex);
1007 const QPoint mousePos = viewport()->mapFromGlobal(QCursor::pos());
1008 if (option.rect.contains(mousePos)) {
1009 if (d->categoryDrawerV3) {
1010 d->categoryDrawerV3->mouseButtonPressed(categoryIndex, option.rect, event);
1012 d->categoryDrawerV2->mouseButtonPressed(categoryIndex, event);
1014 viewport()->update(option.rect);
1015 if (!event->isAccepted()) {
1016 QListView::mousePressEvent(event);
1022 QListView::mousePressEvent(event);
1027 d->pressedPosition =
QPoint();
1028 d->rubberBandRect =
QRect();
1029 if (!d->categoryDrawerV2) {
1030 QListView::mouseReleaseEvent(event);
1034 while (it != d->blocks.constEnd()) {
1035 const Private::Block &block = *it;
1036 const QModelIndex categoryIndex = d->proxyModel->index(block.firstIndex.row(), d->proxyModel->sortColumn(), rootIndex());
1037 const QStyleOptionViewItemV4 option = d->blockRect(categoryIndex);
1038 const QPoint mousePos = viewport()->mapFromGlobal(QCursor::pos());
1039 if (option.rect.contains(mousePos)) {
1040 if (d->categoryDrawerV3) {
1041 d->categoryDrawerV3->mouseButtonReleased(categoryIndex, option.rect, event);
1043 d->categoryDrawerV2->mouseButtonReleased(categoryIndex, event);
1045 viewport()->update(option.rect);
1046 if (!event->isAccepted()) {
1047 QListView::mouseReleaseEvent(event);
1053 QListView::mouseReleaseEvent(event);
1058 QListView::leaveEvent(event);
1059 if (d->hoveredIndex.isValid()) {
1060 viewport()->update(
visualRect(d->hoveredIndex));
1061 d->hoveredIndex = QModelIndex();
1063 if (d->categoryDrawerV3 && d->hoveredBlock->height != -1) {
1064 const QModelIndex categoryIndex = d->proxyModel->index(d->hoveredBlock->firstIndex.row(), d->proxyModel->sortColumn(), rootIndex());
1065 const QStyleOptionViewItemV4 option = d->blockRect(categoryIndex);
1066 d->categoryDrawerV3->mouseLeft(categoryIndex, option.rect);
1067 *d->hoveredBlock = Private::Block();
1068 d->hoveredCategory =
QString();
1069 viewport()->update(option.rect);
1075 QListView::startDrag(supportedActions);
1080 QListView::dragMoveEvent(event);
1081 d->hoveredIndex =
indexAt(event->pos());
1086 QListView::dragEnterEvent(event);
1091 QListView::dragLeaveEvent(event);
1096 QListView::dropEvent(event);
1102 Qt::KeyboardModifiers modifiers)
1104 if (!d->isCategorized()) {
1105 return QListView::moveCursor(cursorAction, modifiers);
1108 const QModelIndex current = currentIndex();
1110 if (!current.isValid()) {
1111 const int rowCount = d->proxyModel->rowCount(rootIndex());
1113 return QModelIndex();
1115 return d->proxyModel->index(0, modelColumn(), rootIndex());
1118 switch (cursorAction) {
1120 if (!current.row()) {
1121 return QModelIndex();
1123 const QModelIndex previous = d->proxyModel->index(current.row() - 1, modelColumn(), rootIndex());
1125 if (previousRect.top() == currentRect.top()) {
1129 return QModelIndex();
1132 if (current.row() == d->proxyModel->rowCount() - 1) {
1133 return QModelIndex();
1135 const QModelIndex
next = d->proxyModel->index(current.row() + 1, modelColumn(), rootIndex());
1137 if (nextRect.top() == currentRect.top()) {
1141 return QModelIndex();
1144 if (d->hasGrid() || uniformItemSizes()) {
1145 const QModelIndex current = currentIndex();
1146 const QSize itemSize = d->hasGrid() ? gridSize()
1147 : sizeHintForIndex(current);
1148 const Private::Block &block = d->blocks[d->categoryForIndex(current)];
1149 const int maxItemsPerRow = qMax(d->viewportWidth() / itemSize.width(), 1);
1150 const bool canMove = current.row() + maxItemsPerRow < block.firstIndex.row() +
1151 block.items.count();
1154 return d->proxyModel->index(current.row() + maxItemsPerRow, modelColumn(), rootIndex());
1157 const int currentRelativePos = (current.row() - block.firstIndex.row()) % maxItemsPerRow;
1158 const QModelIndex nextIndex = d->proxyModel->index(block.firstIndex.row() + block.items.count(), modelColumn(), rootIndex());
1160 if (!nextIndex.isValid()) {
1161 return QModelIndex();
1164 const Private::Block &nextBlock = d->blocks[d->categoryForIndex(nextIndex)];
1166 if (nextBlock.items.count() <= currentRelativePos) {
1167 return QModelIndex();
1170 if (currentRelativePos < (block.items.count() % maxItemsPerRow)) {
1171 return d->proxyModel->index(nextBlock.firstIndex.row() + currentRelativePos, modelColumn(), rootIndex());
1174 return QModelIndex();
1178 if (d->hasGrid() || uniformItemSizes()) {
1179 const QModelIndex current = currentIndex();
1180 const QSize itemSize = d->hasGrid() ? gridSize()
1181 : sizeHintForIndex(current);
1182 const Private::Block &block = d->blocks[d->categoryForIndex(current)];
1183 const int maxItemsPerRow = qMax(d->viewportWidth() / itemSize.width(), 1);
1184 const bool canMove = current.row() - maxItemsPerRow >= block.firstIndex.row();
1187 return d->proxyModel->index(current.row() - maxItemsPerRow, modelColumn(), rootIndex());
1190 const int currentRelativePos = (current.row() - block.firstIndex.row()) % maxItemsPerRow;
1191 const QModelIndex prevIndex = d->proxyModel->index(block.firstIndex.row() - 1, modelColumn(), rootIndex());
1193 if (!prevIndex.isValid()) {
1194 return QModelIndex();
1197 const Private::Block &prevBlock = d->blocks[d->categoryForIndex(prevIndex)];
1199 if (prevBlock.items.count() <= currentRelativePos) {
1200 return QModelIndex();
1203 const int remainder = prevBlock.items.count() % maxItemsPerRow;
1204 if (currentRelativePos < remainder) {
1205 return d->proxyModel->index(prevBlock.firstIndex.row() + prevBlock.items.count() - remainder + currentRelativePos, modelColumn(), rootIndex());
1208 return QModelIndex();
1215 return QModelIndex();
1222 if (!d->isCategorized()) {
1223 QListView::rowsAboutToBeRemoved(parent, start, end);
1227 *d->hoveredBlock = Private::Block();
1228 d->hoveredCategory =
QString();
1230 if (end - start + 1 == d->proxyModel->rowCount()) {
1232 QListView::rowsAboutToBeRemoved(parent, start, end);
1273 int alreadyRemoved = 0;
1274 for (
int i = start; i <=
end; ++i) {
1275 const QModelIndex index = d->proxyModel->index(i, modelColumn(), parent);
1277 Q_ASSERT(index.isValid());
1279 const QString category = d->categoryForIndex(index);
1281 if (lastCategory != category) {
1282 lastCategory = category;
1286 Private::Block &block = d->blocks[category];
1287 block.items.removeAt(i - block.firstIndex.row() - alreadyRemoved);
1290 if (!block.items.count()) {
1291 listOfCategoriesMarkedForRemoval << category;
1296 viewport()->update();
1301 const QModelIndex lastIndex = d->proxyModel->index(end, modelColumn(), parent);
1302 const QString category = d->categoryForIndex(lastIndex);
1303 Private::Block &block = d->blocks[category];
1304 if (block.items.count() && start <= block.firstIndex.row() && end >= block.firstIndex.row()) {
1305 block.firstIndex = d->proxyModel->index(end + 1, modelColumn(), parent);
1307 block.quarantineStart = block.firstIndex;
1311 Q_FOREACH (
const QString &category, listOfCategoriesMarkedForRemoval) {
1312 d->blocks.remove(category);
1321 foreach (
const Private::Block &block, blockList) {
1322 firstIndexesRows << block.firstIndex.row();
1326 Private::Block &block = *it;
1327 if (block.firstIndex.row() > start) {
1328 block.outOfQuarantine =
false;
1329 block.alternate = firstIndexesRows.indexOf(block.firstIndex.row()) % 2;
1330 }
else if (block.firstIndex.row() == start) {
1331 block.alternate = firstIndexesRows.indexOf(block.firstIndex.row()) % 2;
1337 QListView::rowsAboutToBeRemoved(parent, start, end);
1342 const int oldVerticalOffset = verticalOffset();
1343 const Qt::ScrollBarPolicy verticalP = verticalScrollBarPolicy(), horizontalP = horizontalScrollBarPolicy();
1359 if (d->isCategorized()) {
1360 setVerticalScrollBarPolicy((verticalP == Qt::ScrollBarAlwaysOn || verticalScrollBar()->isVisibleTo(
this)) ?
1361 Qt::ScrollBarAlwaysOn : Qt::ScrollBarAlwaysOff);
1362 setHorizontalScrollBarPolicy((horizontalP == Qt::ScrollBarAlwaysOn || horizontalScrollBar()->isVisibleTo(
this)) ?
1363 Qt::ScrollBarAlwaysOn : Qt::ScrollBarAlwaysOff);
1367 QListView::updateGeometries();
1369 if (!d->isCategorized()) {
1373 const int rowCount = d->proxyModel->rowCount();
1375 verticalScrollBar()->setRange(0, 0);
1377 horizontalScrollBar()->setRange(0, 0);
1381 const QModelIndex lastIndex = d->proxyModel->index(rowCount - 1, modelColumn(), rootIndex());
1382 Q_ASSERT(lastIndex.isValid());
1386 lastItemRect.setSize(lastItemRect.size().expandedTo(gridSize()));
1388 if (uniformItemSizes()) {
1389 QSize itemSize = sizeHintForIndex(lastIndex);
1390 itemSize.setHeight(itemSize.height() + spacing());
1391 lastItemRect.setSize(itemSize);
1393 QSize itemSize = sizeHintForIndex(lastIndex);
1394 const QString category = d->categoryForIndex(lastIndex);
1395 itemSize.setHeight(d->highestElementInLastRow(d->blocks[category]) + spacing());
1396 lastItemRect.setSize(itemSize);
1400 const int bottomRange = lastItemRect.bottomRight().y() + verticalOffset() - viewport()->height();
1402 if (verticalScrollMode() == ScrollPerItem) {
1403 verticalScrollBar()->setSingleStep(lastItemRect.height());
1404 const int rowsPerPage = qMax(viewport()->height() / lastItemRect.height(), 1);
1405 verticalScrollBar()->setPageStep(rowsPerPage * lastItemRect.height());
1408 verticalScrollBar()->setRange(0, bottomRange);
1409 verticalScrollBar()->setValue(oldVerticalOffset);
1415 horizontalScrollBar()->setRange(0, 0);
1419 setVerticalScrollBarPolicy(verticalP);
1420 setHorizontalScrollBarPolicy(horizontalP);
1422 bool validRange = verticalScrollBar()->maximum() != verticalScrollBar()->minimum();
1423 if (verticalP == Qt::ScrollBarAsNeeded && (verticalScrollBar()->isVisibleTo(
this) != validRange))
1424 verticalScrollBar()->setVisible(validRange);
1425 validRange = horizontalScrollBar()->maximum() > horizontalScrollBar()->minimum();
1426 if (horizontalP == Qt::ScrollBarAsNeeded && (horizontalScrollBar()->isVisibleTo(
this) != validRange))
1427 horizontalScrollBar()->setVisible(validRange);
1432 const QModelIndex &previous)
1434 QListView::currentChanged(current, previous);
1438 const QModelIndex &bottomRight)
1440 QListView::dataChanged(topLeft, bottomRight);
1441 if (!d->isCategorized()) {
1445 *d->hoveredBlock = Private::Block();
1446 d->hoveredCategory =
QString();
1449 int i = topLeft.row();
1450 int indexToCheck = i;
1451 QModelIndex categoryIndex;
1453 Private::Block *
block;
1454 while (i <= bottomRight.row()) {
1455 const QModelIndex currIndex = d->proxyModel->index(i, modelColumn(), rootIndex());
1456 if (i == indexToCheck) {
1457 categoryIndex = d->proxyModel->index(i, d->proxyModel->sortColumn(), rootIndex());
1459 block = &d->blocks[category];
1460 block->quarantineStart = currIndex;
1461 indexToCheck = block->firstIndex.row() + block->items.count();
1473 QListView::rowsInserted(parent, start, end);
1474 if (!d->isCategorized()) {
1478 *d->hoveredBlock = Private::Block();
1479 d->hoveredCategory =
QString();
1480 d->rowsInserted(parent, start, end);
1483 #ifndef KDE_NO_DEPRECATED
1494 #ifndef KDE_NO_DEPRECATED
1507 if (!d->isCategorized()) {
1512 *d->hoveredBlock = Private::Block();
1513 d->hoveredCategory =
QString();
1514 if (d->proxyModel->rowCount()) {
1515 d->rowsInserted(rootIndex(), 0, d->proxyModel->rowCount() - 1);
1521 #include "kcategorizedview.moc"
virtual void leaveEvent(QEvent *event)
Reimplemented from QWidget.
virtual void mouseMoveEvent(QMouseEvent *event)
Reimplemented from QWidget.
virtual void currentChanged(const QModelIndex ¤t, const QModelIndex &previous)
Reimplemented from QAbstractItemView.
virtual QModelIndex indexAt(const QPoint &point) const
Reimplemented from QAbstractItemView.
int categorySpacing() const
virtual void rowsRemoved(const QModelIndex &parent, int start, int end)
virtual void resizeEvent(QResizeEvent *event)
Reimplemented from QWidget.
bool alternatingBlockColors() const
virtual void rowsInsertedArtifficial(const QModelIndex &parent, int start, int end)
virtual void updateGeometries()
Reimplemented from QAbstractItemView.
virtual void dragEnterEvent(QDragEnterEvent *event)
Reimplemented from QAbstractItemView.
bool collapsibleBlocks() const
void setCollapsibleBlocks(bool enable)
Sets whether blocks can be collapsed or not.
This class lets you categorize a view.
virtual void dragLeaveEvent(QDragLeaveEvent *event)
Reimplemented from QAbstractItemView.
bool operator!=(const KEntry &k1, const KEntry &k2)
virtual void slotLayoutChanged()
void setGridSizeOwn(const QSize &size)
void setCategoryDrawer(KCategoryDrawer *categoryDrawer)
The category drawer that will be used for drawing categories.
Item view for listing items in a categorized fashion optionally.
KCategoryDrawer * categoryDrawer() const
Returns the current category drawer.
void setGridSize(const QSize &size)
Calls to setGridSizeOwn().
virtual void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
Reimplemented from QAbstractItemView.
virtual void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
Reimplemented from QAbstractItemView.
virtual QRect visualRect(const QModelIndex &index) const
Reimplemented from QAbstractItemView.
virtual void reset()
Reimplemented from QAbstractItemView.
virtual void setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags flags)
Reimplemented from QAbstractItemView.
virtual void setModel(QAbstractItemModel *model)
Reimplemented from QAbstractItemView.
KAction * next(const QObject *recvr, const char *slot, QObject *parent)
Scroll down one page.
KCategorizedView(QWidget *parent=0)
bool lessThan(const QString &left, const QString &right)
virtual void rowsInserted(const QModelIndex &parent, int start, int end)
Reimplemented from QAbstractItemView.
QModelIndexList block(const QString &category)
virtual void dropEvent(QDropEvent *event)
Reimplemented from QAbstractItemView.
void setAlternatingBlockColors(bool enable)
Sets whether blocks should be drawn with alternating colors.
virtual void mouseReleaseEvent(QMouseEvent *event)
Reimplemented from QWidget.
KGuiItem reset()
Returns the 'Reset' gui item.
void setCategorySpacing(int categorySpacing)
Stablishes the category spacing.
This role is used for asking the category to a given index.
virtual QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
Reimplemented from QAbstractItemView.
virtual void mousePressEvent(QMouseEvent *event)
Reimplemented from QWidget.
virtual void paintEvent(QPaintEvent *event)
Reimplemented from QWidget.
const KShortcut & end()
Goto end of the document.
virtual void startDrag(Qt::DropActions supportedActions)
Reimplemented from QAbstractItemView.
virtual void dragMoveEvent(QDragMoveEvent *event)
Reimplemented from QAbstractItemView.