File indexing completed on 2025-04-27 03:58:27

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2010-01-16
0007  * Description : Item view for listing items in a categorized fashion optionally
0008  *
0009  * SPDX-FileCopyrightText: 2007      by Rafael Fernández López <ereslibre at kde dot org>
0010  * SPDX-FileCopyrightText: 2009-2012 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0011  * SPDX-FileCopyrightText: 2011-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0012  *
0013  * SPDX-License-Identifier: GPL-2.0-or-later
0014  *
0015  * ============================================================ */
0016 
0017 #include "dcategorizedview_p.h"
0018 
0019 namespace Digikam
0020 {
0021 
0022 DCategorizedView::DCategorizedView(QWidget* const parent)
0023     : QListView(parent),
0024       d        (new Private(this))
0025 {
0026 }
0027 
0028 DCategorizedView::~DCategorizedView()
0029 {
0030     delete d;
0031 }
0032 
0033 void DCategorizedView::setGridSize(const QSize& size)
0034 {
0035     QListView::setGridSize(size);
0036 
0037     slotLayoutChanged();
0038 }
0039 
0040 void DCategorizedView::setModel(QAbstractItemModel* model)
0041 {
0042     d->lastSelection           = QItemSelection();
0043     d->forcedSelectionPosition = 0;
0044     d->hovered                 = QModelIndex();
0045     d->mouseButtonPressed      = false;
0046     d->rightMouseButtonPressed = false;
0047     d->elementsInfo.clear();
0048     d->elementsPosition.clear();
0049     d->categoriesIndexes.clear();
0050     d->categoriesPosition.clear();
0051     d->categories.clear();
0052     d->intersectedIndexes.clear();
0053 
0054     if (d->proxyModel)
0055     {
0056         QObject::disconnect(d->proxyModel, SIGNAL(layoutChanged()),
0057                             this, SLOT(slotLayoutChanged()));
0058 
0059         QObject::disconnect(d->proxyModel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
0060                             this, SLOT(rowsRemoved(QModelIndex,int,int)));
0061     }
0062 
0063     QListView::setModel(model);
0064 
0065     d->proxyModel = dynamic_cast<DCategorizedSortFilterProxyModel*>(model);
0066 
0067     if (d->proxyModel)
0068     {
0069         QObject::connect(d->proxyModel, SIGNAL(layoutChanged()),
0070                          this, SLOT(slotLayoutChanged()));
0071 
0072         QObject::connect(d->proxyModel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
0073                          this, SLOT(rowsRemoved(QModelIndex,int,int)));
0074 
0075         if (d->proxyModel->rowCount())
0076         {
0077             slotLayoutChanged();
0078         }
0079     }
0080 }
0081 
0082 QRect DCategorizedView::visualRect(const QModelIndex& index) const
0083 {
0084     if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
0085     {
0086         return QListView::visualRect(index);
0087     }
0088 
0089     if (!qobject_cast<const QSortFilterProxyModel*>(index.model()))
0090     {
0091         return d->visualRect(d->proxyModel->mapFromSource(index));
0092     }
0093 
0094     return d->visualRect(index);
0095 }
0096 
0097 QRect DCategorizedView::categoryVisualRect(const QModelIndex& index) const
0098 {
0099     if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
0100     {
0101         return QRect();
0102     }
0103 
0104     if (!index.isValid())
0105     {
0106         return QRect();
0107     }
0108 
0109     QString category = d->elementsInfo[index.row()].category;
0110 
0111     return d->categoryVisualRect(category);
0112 }
0113 
0114 QModelIndex DCategorizedView::categoryAt(const QPoint& point) const
0115 {
0116     if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
0117     {
0118         return QModelIndex();
0119     }
0120 
0121     // We traverse the categories and find the first where point.y() is below the visualRect
0122 
0123     int     y = 0, lastY = 0;
0124     QString lastCategory;
0125 
0126     Q_FOREACH (const QString& category, d->categories)
0127     {
0128         y = d->categoryVisualRect(category).top();
0129 
0130         if ((point.y() >= lastY) && (point.y() < y))
0131         {
0132             break;
0133         }
0134 
0135         lastY        = y;
0136         y            = 0;
0137         lastCategory = category;
0138     }
0139 
0140     // if lastCategory is the last one in the list y will be 0
0141 
0142     if (!lastCategory.isNull() && (point.y() >= lastY) && ((point.y() < y) || !y))
0143     {
0144         return d->proxyModel->index(d->categoriesIndexes[lastCategory][0], d->proxyModel->sortColumn());
0145     }
0146 
0147     return QModelIndex();
0148 }
0149 
0150 QItemSelectionRange DCategorizedView::categoryRange(const QModelIndex& index) const
0151 {
0152     if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
0153     {
0154         return QItemSelectionRange();
0155     }
0156 
0157     if (!index.isValid())
0158     {
0159         return QItemSelectionRange();
0160     }
0161 
0162     QString category  = d->elementsInfo[index.row()].category;
0163     QModelIndex first = d->proxyModel->index(d->categoriesIndexes[category].first(), d->proxyModel->sortColumn());
0164     QModelIndex last  = d->proxyModel->index(d->categoriesIndexes[category].last(), d->proxyModel->sortColumn());
0165 
0166     return QItemSelectionRange(first, last);
0167 }
0168 
0169 DCategoryDrawer* DCategorizedView::categoryDrawer() const
0170 {
0171     return d->categoryDrawer;
0172 }
0173 
0174 void DCategorizedView::setCategoryDrawer(DCategoryDrawer* categoryDrawer)
0175 {
0176     d->lastSelection           = QItemSelection();
0177     d->forcedSelectionPosition = 0;
0178     d->hovered                 = QModelIndex();
0179     d->mouseButtonPressed      = false;
0180     d->rightMouseButtonPressed = false;
0181     d->elementsInfo.clear();
0182     d->elementsPosition.clear();
0183     d->categoriesIndexes.clear();
0184     d->categoriesPosition.clear();
0185     d->categories.clear();
0186     d->intersectedIndexes.clear();
0187     d->categoryDrawer          = categoryDrawer;
0188 
0189     if (categoryDrawer)
0190     {
0191         if (d->proxyModel)
0192         {
0193             if (d->proxyModel->rowCount())
0194             {
0195                 slotLayoutChanged();
0196             }
0197         }
0198     }
0199     else
0200     {
0201         updateGeometries();
0202     }
0203 }
0204 
0205 void DCategorizedView::setDrawDraggedItems(bool drawDraggedItems)
0206 {
0207     d->drawItemsWhileDragging = drawDraggedItems;
0208 }
0209 
0210 QModelIndex DCategorizedView::indexAt(const QPoint& point) const
0211 {
0212     if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
0213     {
0214         return QListView::indexAt(point);
0215     }
0216 
0217     QModelIndex index;
0218 
0219     const QModelIndexList item = d->intersectionSet(QRect(point, point));
0220 
0221     if (item.count() == 1)
0222     {
0223         index = item[0];
0224     }
0225 
0226     return index;
0227 }
0228 
0229 QModelIndexList DCategorizedView::categorizedIndexesIn(const QRect& rect) const
0230 {
0231     if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
0232     {
0233         return QModelIndexList();
0234     }
0235 
0236     return d->intersectionSet(rect);
0237 }
0238 
0239 void DCategorizedView::reset()
0240 {
0241     QListView::reset();
0242 
0243     d->lastSelection           = QItemSelection();
0244     d->forcedSelectionPosition = 0;
0245     d->hovered                 = QModelIndex();
0246     d->biggestItemSize         = QSize(0, 0);
0247     d->mouseButtonPressed      = false;
0248     d->rightMouseButtonPressed = false;
0249     d->elementsInfo.clear();
0250     d->elementsPosition.clear();
0251     d->categoriesIndexes.clear();
0252     d->categoriesPosition.clear();
0253     d->categories.clear();
0254     d->intersectedIndexes.clear();
0255 }
0256 
0257 void DCategorizedView::paintEvent(QPaintEvent* event)
0258 {
0259     if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
0260     {
0261         QListView::paintEvent(event);
0262         return;
0263     }
0264 
0265     bool alternatingRows          = alternatingRowColors();
0266     QStyleOptionViewItem option;
0267 
0268 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0269 
0270     initViewItemOption(&option);
0271 
0272 #else
0273 
0274     option = viewOptions();
0275 
0276 #endif
0277 
0278     option.widget                 = this;
0279 
0280     if (wordWrap())
0281     {
0282         option.features |= QStyleOptionViewItem::WrapText;
0283     }
0284 
0285     QPainter painter(viewport());
0286     QRect area                = event->rect();
0287     const bool focus          = (hasFocus() || viewport()->hasFocus()) && currentIndex().isValid();
0288     const QStyle::State state = option.state;
0289     const bool enabled        = (state & QStyle::State_Enabled) != 0;
0290 
0291     painter.save();
0292 
0293     QModelIndexList dirtyIndexes = d->intersectionSet(area);
0294     bool alternate               = false;
0295 
0296     if (dirtyIndexes.count())
0297     {
0298         alternate = dirtyIndexes[0].row() % 2;
0299     }
0300 
0301     Q_FOREACH (const QModelIndex& index, dirtyIndexes)
0302     {
0303         if      (alternatingRows && alternate)
0304         {
0305             option.features |= QStyleOptionViewItem::Alternate;
0306             alternate        = false;
0307         }
0308         else if (alternatingRows)
0309         {
0310             option.features &= ~QStyleOptionViewItem::Alternate;
0311             alternate        = true;
0312         }
0313 
0314         option.state = state;
0315         option.rect  = visualRect(index);
0316 
0317         if (selectionModel() && selectionModel()->isSelected(index))
0318         {
0319             option.state |= QStyle::State_Selected;
0320         }
0321 
0322         if (enabled)
0323         {
0324             QPalette::ColorGroup cg;
0325 
0326             if ((d->proxyModel->flags(index) & Qt::ItemIsEnabled) == 0)
0327             {
0328                 option.state &= ~QStyle::State_Enabled;
0329                 cg            = QPalette::Disabled;
0330             }
0331             else
0332             {
0333                 cg = QPalette::Normal;
0334             }
0335 
0336             option.palette.setCurrentColorGroup(cg);
0337         }
0338 
0339         if (focus && (currentIndex() == index))
0340         {
0341             option.state |= QStyle::State_HasFocus;
0342 
0343             if (this->state() == EditingState)
0344             {
0345                 option.state |= QStyle::State_Editing;
0346             }
0347         }
0348 
0349         if (index == d->hovered)
0350         {
0351             option.state |= QStyle::State_MouseOver;
0352         }
0353         else
0354         {
0355             option.state &= ~QStyle::State_MouseOver;
0356         }
0357 
0358 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0359 
0360         itemDelegateForIndex(index)->paint(&painter, option, index);
0361 
0362 #else
0363 
0364         itemDelegate(index)->paint(&painter, option, index);
0365 
0366 #endif
0367 
0368     }
0369 
0370     // Redraw categories
0371 
0372     QStyleOptionViewItem otherOption;
0373     bool                 intersectedInThePast = false;
0374 
0375     Q_FOREACH (const QString& category, d->categories)
0376     {
0377         otherOption        = option;
0378         otherOption.rect   = d->categoryVisualRect(category);
0379         otherOption.state &= ~QStyle::State_MouseOver;
0380 
0381         if      (otherOption.rect.intersects(area))
0382         {
0383             intersectedInThePast    = true;
0384             QModelIndex indexToDraw = d->proxyModel->index(d->categoriesIndexes[category][0],
0385                                                            d->proxyModel->sortColumn());
0386 
0387             d->drawNewCategory(indexToDraw, d->proxyModel->sortRole(), otherOption, &painter);
0388         }
0389 
0390         // cppcheck-suppress knownConditionTrueFalse
0391         else if (intersectedInThePast)
0392         {
0393             // the visible area has been finished, we don't need to keep asking, the rest won't intersect
0394             // this is doable because we know that categories are correctly ordered on the list.
0395 
0396             break;
0397         }
0398     }
0399 
0400     if ((selectionMode() != SingleSelection) && (selectionMode() != NoSelection))
0401     {
0402         if (d->mouseButtonPressed && (QListView::state() != DraggingState))
0403         {
0404             QPoint start, end, initialPressPosition;
0405 
0406             initialPressPosition = d->initialPressPosition;
0407 
0408             initialPressPosition.setY(initialPressPosition.y() - verticalOffset());
0409             initialPressPosition.setX(initialPressPosition.x() - horizontalOffset());
0410 
0411             if ((d->initialPressPosition.x() > d->mousePosition.x()) ||
0412                 (d->initialPressPosition.y() > d->mousePosition.y()))
0413             {
0414                 start = d->mousePosition;
0415                 end   = initialPressPosition;
0416             }
0417             else
0418             {
0419                 start = initialPressPosition;
0420                 end   = d->mousePosition;
0421             }
0422 
0423             QStyleOptionRubberBand yetAnotherOption;
0424             yetAnotherOption.initFrom(this);
0425             yetAnotherOption.shape  = QRubberBand::Rectangle;
0426             yetAnotherOption.opaque = false;
0427             yetAnotherOption.rect   = QRect(start, end).intersected(viewport()->rect().adjusted(-16, -16, 16, 16));
0428             painter.save();
0429             style()->drawControl(QStyle::CE_RubberBand, &yetAnotherOption, &painter);
0430             painter.restore();
0431         }
0432     }
0433 
0434     if (d->drawItemsWhileDragging && (QListView::state() == DraggingState) && !d->dragLeftViewport)
0435     {
0436         painter.setOpacity(0.5);
0437         d->drawDraggedItems(&painter);
0438     }
0439 
0440     painter.restore();
0441 }
0442 
0443 void DCategorizedView::resizeEvent(QResizeEvent* event)
0444 {
0445     QListView::resizeEvent(event);
0446 
0447     // Clear the items positions cache
0448 
0449     d->elementsPosition.clear();
0450     d->categoriesPosition.clear();
0451     d->forcedSelectionPosition = 0;
0452 
0453     if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
0454     {
0455         return;
0456     }
0457 
0458     d->updateScrollbars();
0459 }
0460 
0461 QItemSelection DCategorizedView::Private::selectionForRect(const QRect& rect)
0462 {
0463     QItemSelection selection;
0464     QModelIndex    tl, br;
0465     QModelIndexList intersectedIndexes    = intersectionSet(rect);
0466     QList<QModelIndex>::const_iterator it = intersectedIndexes.constBegin();
0467 
0468     for ( ; it != intersectedIndexes.constEnd() ; ++it)
0469     {
0470         if      (!tl.isValid() && !br.isValid())
0471         {
0472             tl = br = *it;
0473         }
0474         else if ((*it).row() == (tl.row() - 1))
0475         {
0476             tl = *it;   // expand current range
0477         }
0478         else if ((*it).row() == (br.row() + 1))
0479         {
0480             br = (*it); // expand current range
0481         }
0482         else
0483         {
0484             selection.select(tl, br); // select current range
0485             tl = br = *it;            // start new range
0486         }
0487     }
0488 
0489     if      (tl.isValid() && br.isValid())
0490     {
0491         selection.select(tl, br);
0492     }
0493     else if (tl.isValid())
0494     {
0495         selection.select(tl, tl);
0496     }
0497     else if (br.isValid())
0498     {
0499         selection.select(br, br);
0500     }
0501 
0502     return selection;
0503 }
0504 
0505 void DCategorizedView::setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags command)
0506 {
0507     if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
0508     {
0509         QListView::setSelection(rect, command);
0510         return;
0511     }
0512 
0513     QItemSelection selection;
0514 /*
0515     QRect contentsRect = rect.translated(horizontalOffset(), verticalOffset());
0516 */
0517     QModelIndexList intersectedIndexes = d->intersectionSet(rect);
0518 
0519     if ((rect.width() == 1) && (rect.height() == 1))
0520     {
0521         QModelIndex tl;
0522 
0523         if (!intersectedIndexes.isEmpty())
0524         {
0525             tl = intersectedIndexes.last();    // special case for mouse press; only select the top item
0526         }
0527 
0528         if (tl.isValid() && (tl.flags() & Qt::ItemIsEnabled))
0529         {
0530             selection.select(tl, tl);
0531         }
0532     }
0533     else
0534     {
0535         if (state() == DragSelectingState)
0536         {
0537             // visual selection mode (rubberband selection)
0538 
0539             selection = d->selectionForRect(rect);
0540         }
0541         else
0542         {
0543             // logical selection mode (key and mouse click selection)
0544 
0545             QModelIndex tl, br;
0546 
0547             // get the first item
0548 
0549             const QRect topLeft(rect.left(), rect.top(), 1, 1);
0550             intersectedIndexes = d->intersectionSet(topLeft);
0551 
0552             if (!intersectedIndexes.isEmpty())
0553             {
0554                 tl = intersectedIndexes.last();
0555             }
0556 
0557             // get the last item
0558 
0559             const QRect bottomRight(rect.right(), rect.bottom(), 1, 1);
0560             intersectedIndexes = d->intersectionSet(bottomRight);
0561 
0562             if (!intersectedIndexes.isEmpty())
0563             {
0564                 br = intersectedIndexes.last();
0565             }
0566 
0567             // get the ranges
0568 
0569             if (tl.isValid()                     &&
0570                 br.isValid()                     &&
0571                 (tl.flags() & Qt::ItemIsEnabled) &&
0572                 (br.flags() & Qt::ItemIsEnabled))
0573             {
0574                 // first, middle, last in content coordinates
0575 
0576                 QRect middle;
0577                 QRect first    = d->cachedRectIndex(tl);
0578                 QRect last     = d->cachedRectIndex(br);
0579                 QSize fullSize = d->contentsSize();
0580 
0581                 if (flow() == LeftToRight)
0582                 {
0583                     QRect& top    = first;
0584                     QRect& bottom = last;
0585 
0586                     // if bottom is above top, swap them
0587 
0588                     if (top.center().y() > bottom.center().y())
0589                     {
0590                         QRect tmp = top;
0591                         top       = bottom;
0592                         bottom    = tmp;
0593                     }
0594 
0595                     // if the rect are on different lines, expand
0596 
0597                     if      (top.top() != bottom.top())
0598                     {
0599                         // top rectangle
0600 
0601                         if (isRightToLeft())
0602                         {
0603                             top.setLeft(0);
0604                         }
0605                         else
0606                         {
0607                             top.setRight(fullSize.width());
0608                         }
0609 
0610                         // bottom rectangle
0611 
0612                         if (isRightToLeft())
0613                         {
0614                             bottom.setRight(fullSize.width());
0615                         }
0616                         else
0617                         {
0618                             bottom.setLeft(0);
0619                         }
0620                     }
0621                     else if (top.left() > bottom.right())
0622                     {
0623                         if (isRightToLeft())
0624                         {
0625                             bottom.setLeft(top.right());
0626                         }
0627                         else
0628                         {
0629                             bottom.setRight(top.left());
0630                         }
0631                     }
0632                     else
0633                     {
0634                         if (isRightToLeft())
0635                         {
0636                             top.setLeft(bottom.right());
0637                         }
0638                         else
0639                         {
0640                             top.setRight(bottom.left());
0641                         }
0642                     }
0643 
0644                     // middle rectangle
0645 
0646                     if (top.bottom() < bottom.top())
0647                     {
0648                         middle.setTop(top.bottom() + 1);
0649                         middle.setLeft(qMin(top.left(), bottom.left()));
0650                         middle.setBottom(bottom.top() - 1);
0651                         middle.setRight(qMax(top.right(), bottom.right()));
0652                     }
0653                 }
0654                 else
0655                 {
0656                     // TopToBottom
0657 
0658                     QRect& left  = first;
0659                     QRect& right = last;
0660 
0661                     if (left.center().x() > right.center().x())
0662                     {
0663                         std::swap(left, right);
0664                     }
0665 
0666                     int ch = fullSize.height();
0667 
0668                     if      (left.left() != right.left())
0669                     {
0670                         // left rectangle
0671 
0672                         if (isRightToLeft())
0673                         {
0674                             left.setTop(0);
0675                         }
0676                         else
0677                         {
0678                             left.setBottom(ch);
0679                         }
0680 
0681                         // top rectangle
0682 
0683                         if (isRightToLeft())
0684                         {
0685                             right.setBottom(ch);
0686                         }
0687                         else
0688                         {
0689                             right.setTop(0);
0690                         }
0691 
0692                         // only set middle if the
0693 
0694                         middle.setTop(0);
0695                         middle.setBottom(ch);
0696                         middle.setLeft(left.right() + 1);
0697                         middle.setRight(right.left() - 1);
0698                     }
0699                     else if (left.bottom() < right.top())
0700                     {
0701                         left.setBottom(right.top() - 1);
0702                     }
0703                     else
0704                     {
0705                         right.setBottom(left.top() - 1);
0706                     }
0707                 }
0708 
0709                 // get viewport coordinates
0710 
0711                 first                          = first.translated( - horizontalOffset(), - verticalOffset());
0712                 middle                         = middle.translated( - horizontalOffset(), - verticalOffset());
0713                 last                           = last.translated( - horizontalOffset(), - verticalOffset());
0714 
0715                 // do the selections
0716 
0717                 QItemSelection topSelection    = d->selectionForRect(first);
0718                 QItemSelection middleSelection = d->selectionForRect(middle);
0719                 QItemSelection bottomSelection = d->selectionForRect(last);
0720 
0721                 // merge
0722 
0723                 selection.merge(topSelection, QItemSelectionModel::Select);
0724                 selection.merge(middleSelection, QItemSelectionModel::Select);
0725                 selection.merge(bottomSelection, QItemSelectionModel::Select);
0726             }
0727         }
0728     }
0729 
0730     selectionModel()->select(selection, command);
0731 }
0732 
0733 void DCategorizedView::mouseMoveEvent(QMouseEvent* event)
0734 {
0735     QListView::mouseMoveEvent(event);
0736 
0737     // was a dragging started?
0738 
0739     if (state() == DraggingState)
0740     {
0741         d->mouseButtonPressed      = false;
0742         d->rightMouseButtonPressed = false;
0743 
0744         if (d->drawItemsWhileDragging)
0745         {
0746             viewport()->update(d->lastDraggedItemsRect);
0747         }
0748     }
0749 
0750     if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
0751     {
0752         return;
0753     }
0754 
0755     const QModelIndexList item = d->intersectionSet(QRect(event->pos(), event->pos()));
0756 
0757     if (item.count() == 1)
0758     {
0759         d->hovered = item[0];
0760     }
0761     else
0762     {
0763         d->hovered = QModelIndex();
0764     }
0765 
0766     const QString previousHoveredCategory = d->hoveredCategory;
0767 
0768     d->mousePosition = event->pos();
0769 
0770     d->hoveredCategory.clear();
0771 
0772     // Redraw categories
0773 
0774     Q_FOREACH (const QString& category, d->categories)
0775     {
0776         if      (d->categoryVisualRect(category).intersects(QRect(event->pos(), event->pos())))
0777         {
0778             d->hoveredCategory = category;
0779             viewport()->update(d->categoryVisualRect(category));
0780         }
0781         else if ((category == previousHoveredCategory) &&
0782                  (!d->categoryVisualRect(previousHoveredCategory).intersects(QRect(event->pos(), event->pos()))))
0783         {
0784             viewport()->update(d->categoryVisualRect(category));
0785         }
0786     }
0787 
0788     QRect rect;
0789 
0790     if (d->mouseButtonPressed && QListView::state() != DraggingState)
0791     {
0792         QPoint start, end, initialPressPosition;
0793 
0794         initialPressPosition = d->initialPressPosition;
0795 
0796         initialPressPosition.setY(initialPressPosition.y() - verticalOffset());
0797         initialPressPosition.setX(initialPressPosition.x() - horizontalOffset());
0798 
0799         if (d->initialPressPosition.x() > d->mousePosition.x() ||
0800             d->initialPressPosition.y() > d->mousePosition.y())
0801         {
0802             start = d->mousePosition;
0803             end   = initialPressPosition;
0804         }
0805         else
0806         {
0807             start = initialPressPosition;
0808             end   = d->mousePosition;
0809         }
0810 
0811         rect = QRect(start, end).adjusted(-16, -16, 16, 16);
0812         rect = rect.united(QRect(start, end).adjusted(16, 16, -16, -16)).intersected(viewport()->rect());
0813 
0814         viewport()->update(rect);
0815     }
0816 }
0817 
0818 void DCategorizedView::mousePressEvent(QMouseEvent* event)
0819 {
0820     d->dragLeftViewport = false;
0821 
0822     QListView::mousePressEvent(event);
0823 
0824     if      (event->button() == Qt::LeftButton)
0825     {
0826         d->mouseButtonPressed   = true;
0827         d->initialPressPosition = event->pos();
0828         d->initialPressPosition.setY(d->initialPressPosition.y() + verticalOffset());
0829         d->initialPressPosition.setX(d->initialPressPosition.x() + horizontalOffset());
0830     }
0831     else if (event->button() == Qt::RightButton)
0832     {
0833         d->rightMouseButtonPressed = true;
0834     }
0835 
0836     if (selectionModel())
0837     {
0838         d->lastSelection = selectionModel()->selection();
0839     }
0840 
0841     viewport()->update(d->categoryVisualRect(d->hoveredCategory));
0842 }
0843 
0844 void DCategorizedView::mouseReleaseEvent(QMouseEvent* event)
0845 {
0846     d->mouseButtonPressed      = false;
0847     d->rightMouseButtonPressed = false;
0848 
0849     QListView::mouseReleaseEvent(event);
0850 
0851     if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
0852     {
0853         return;
0854     }
0855 
0856     QPoint initPressPos = viewport()->mapFromGlobal(QCursor::pos());
0857     initPressPos.setY(initPressPos.y() + verticalOffset());
0858     initPressPos.setX(initPressPos.x() + horizontalOffset());
0859 
0860     if ((selectionMode() != SingleSelection) &&
0861         (selectionMode() != NoSelection)     &&
0862         (initPressPos == d->initialPressPosition))
0863     {
0864         Q_FOREACH (const QString& category, d->categories)
0865         {
0866             if (d->categoryVisualRect(category).contains(event->pos()) &&
0867                 selectionModel())
0868             {
0869                 QItemSelection selection      = selectionModel()->selection();
0870                 const QVector<int>& indexList = d->categoriesIndexes[category];
0871 
0872                 Q_FOREACH (int row, indexList)
0873                 {
0874                     QModelIndex selectIndex = d->proxyModel->index(row, 0);
0875 
0876                     selection << QItemSelectionRange(selectIndex);
0877                 }
0878 
0879                 selectionModel()->select(selection, QItemSelectionModel::SelectCurrent);
0880 
0881                 break;
0882             }
0883         }
0884     }
0885 
0886     QRect rect;
0887 
0888     if (state() != DraggingState)
0889     {
0890         QPoint start, end, newInitPressPos;
0891 
0892         newInitPressPos = d->initialPressPosition;
0893 
0894         newInitPressPos.setY(newInitPressPos.y() - verticalOffset());
0895         newInitPressPos.setX(newInitPressPos.x() - horizontalOffset());
0896 
0897         if ((d->initialPressPosition.x() > d->mousePosition.x()) ||
0898             (d->initialPressPosition.y() > d->mousePosition.y()))
0899         {
0900             start = d->mousePosition;
0901             end   = newInitPressPos;
0902         }
0903         else
0904         {
0905             start = newInitPressPos;
0906             end   = d->mousePosition;
0907         }
0908 
0909         rect = QRect(start, end).adjusted(-16, -16, 16, 16);
0910         rect = rect.united(QRect(start, end).adjusted(16, 16, -16, -16)).intersected(viewport()->rect());
0911 
0912         viewport()->update(rect);
0913     }
0914 
0915     if      (d->hovered.isValid())
0916     {
0917         viewport()->update(visualRect(d->hovered));
0918     }
0919     else if (!d->hoveredCategory.isEmpty())
0920     {
0921         viewport()->update(d->categoryVisualRect(d->hoveredCategory));
0922     }
0923 }
0924 
0925 void DCategorizedView::leaveEvent(QEvent* event)
0926 {
0927     d->hovered = QModelIndex();
0928     d->hoveredCategory.clear();
0929     d->forcedSelectionPosition = 0;
0930     d->mouseButtonPressed      = false;
0931 
0932     if (state() == DraggingState)
0933     {
0934         setState(NoState);
0935     }
0936 
0937     QListView::leaveEvent(event);
0938 }
0939 
0940 void DCategorizedView::startDrag(Qt::DropActions supportedActions)
0941 {
0942     // FIXME: QAbstractItemView does far better here since it sets the
0943     //        pixmap of selected icons to the dragging cursor, but it sets a non
0944     //        ARGB window so it is no transparent. Use QAbstractItemView when
0945     //        this is fixed on Qt.
0946     // QAbstractItemView::startDrag(supportedActions);
0947 
0948 #if defined(DOLPHIN_DRAGANDDROP)
0949 
0950     Q_UNUSED(supportedActions);
0951 
0952 #else
0953 
0954     QListView::startDrag(supportedActions);
0955 
0956 #endif
0957 
0958 }
0959 
0960 void DCategorizedView::dragMoveEvent(QDragMoveEvent* event)
0961 {
0962 
0963 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0964 
0965     d->mousePosition    = event->position().toPoint();
0966 
0967 #else
0968 
0969     d->mousePosition    = event->pos();
0970 
0971 #endif
0972 
0973     d->dragLeftViewport = false;
0974 
0975 #if defined(DOLPHIN_DRAGANDDROP)
0976 
0977     QAbstractItemView::dragMoveEvent(event);
0978 
0979 #else
0980 
0981     QListView::dragMoveEvent(event);
0982 
0983 #endif
0984 
0985     if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
0986     {
0987         return;
0988     }
0989 
0990 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0991 
0992     d->hovered = indexAt(event->position().toPoint());
0993 
0994 #else
0995 
0996     d->hovered = indexAt(event->pos());
0997 
0998 #endif
0999 
1000 #if !defined(DOLPHIN_DRAGANDDROP)
1001 
1002     d->drawDraggedItems();
1003 
1004 #endif
1005 
1006 }
1007 
1008 void DCategorizedView::dragLeaveEvent(QDragLeaveEvent* event)
1009 {
1010     d->dragLeftViewport = true;
1011 
1012 #if defined(DOLPHIN_DRAGANDDROP)
1013 
1014     QAbstractItemView::dragLeaveEvent(event);
1015 
1016 #else
1017 
1018     QListView::dragLeaveEvent(event);
1019 
1020 #endif
1021 
1022 }
1023 
1024 void DCategorizedView::dropEvent(QDropEvent* event)
1025 {
1026 
1027 #if defined(DOLPHIN_DRAGANDDROP)
1028 
1029     QAbstractItemView::dropEvent(event);
1030 
1031 #else
1032 
1033     QListView::dropEvent(event);
1034 
1035 #endif
1036 
1037 }
1038 
1039 QModelIndex DCategorizedView::moveCursor(CursorAction cursorAction,
1040                                          Qt::KeyboardModifiers modifiers)
1041 {
1042     if ((viewMode() != DCategorizedView::IconMode) ||
1043         !d->proxyModel                             ||
1044         !d->categoryDrawer                         ||
1045         d->categories.isEmpty()                    ||
1046         !d->proxyModel->isCategorizedModel())
1047     {
1048         return QListView::moveCursor(cursorAction, modifiers);
1049     }
1050 
1051     int viewportWidth = viewport()->width() - spacing();
1052     int itemWidth;
1053 
1054     if (gridSize().isEmpty())
1055     {
1056         itemWidth = d->biggestItemSize.width();
1057     }
1058     else
1059     {
1060         itemWidth = gridSize().width();
1061     }
1062 
1063     int itemWidthPlusSeparation = spacing() + itemWidth;
1064 
1065     if (!itemWidthPlusSeparation)
1066     {
1067         ++itemWidthPlusSeparation;
1068     }
1069 
1070     int elementsPerRow = viewportWidth / itemWidthPlusSeparation;
1071 
1072     if (!elementsPerRow)
1073     {
1074         ++elementsPerRow;
1075     }
1076 
1077     QModelIndex current = selectionModel() ? selectionModel()->currentIndex()
1078                                            : QModelIndex();
1079 
1080     if (!current.isValid())
1081     {
1082         if (cursorAction == MoveEnd)
1083         {
1084             current = model()->index(model()->rowCount() - 1, 0, QModelIndex());
1085 /*
1086             d->forcedSelectionPosition = d->elementsInfo[current.row()].relativeOffsetToCategory % elementsPerRow;
1087 */
1088         }
1089         else
1090         {
1091             current                    = model()->index(0, 0, QModelIndex());
1092             d->forcedSelectionPosition = 0;
1093         }
1094 
1095         return current;
1096     }
1097 
1098     QString lastCategory  = d->categories.first();
1099     QString theCategory   = d->categories.first();
1100     QString afterCategory = d->categories.first();
1101     bool hasToBreak       = false;
1102 
1103     Q_FOREACH (const QString& category, d->categories)
1104     {
1105         // cppcheck-suppress knownConditionTrueFalse
1106         if (hasToBreak)
1107         {
1108             afterCategory = category;
1109 
1110             break;
1111         }
1112 
1113         if (category == d->elementsInfo[current.row()].category)
1114         {
1115             theCategory = category;
1116             hasToBreak  = true;
1117         }
1118 
1119         if (!hasToBreak)
1120         {
1121             lastCategory = category;
1122         }
1123     }
1124 
1125     switch (cursorAction)
1126     {
1127         case QAbstractItemView::MovePageUp:
1128         {
1129             // We need to reimplement PageUp/Down as well because
1130             // default QListView implementation will not work properly with our custom layout
1131 
1132             QModelIndexList visibleIndexes = d->intersectionSet(viewport()->rect());
1133 
1134             if (!visibleIndexes.isEmpty())
1135             {
1136                 int indexToMove = qMax(current.row() - visibleIndexes.size(), 0);
1137 
1138                 return d->proxyModel->index(indexToMove, 0);
1139             }
1140 
1141             break;
1142         }
1143 
1144         // fall through
1145 
1146         case QAbstractItemView::MoveUp:
1147         {
1148             if (d->elementsInfo[current.row()].relativeOffsetToCategory >= elementsPerRow)
1149             {
1150                 int indexToMove = current.row();
1151                 indexToMove    -= qMin(((d->elementsInfo[current.row()].relativeOffsetToCategory) +
1152                                   d->forcedSelectionPosition), elementsPerRow - d->forcedSelectionPosition +
1153                                   (d->elementsInfo[current.row()].relativeOffsetToCategory % elementsPerRow));
1154 
1155                 return (d->proxyModel->index(indexToMove, 0));
1156             }
1157             else
1158             {
1159                 int lastCategoryLastRow = (d->categoriesIndexes[lastCategory].count() - 1) % elementsPerRow;
1160                 int indexToMove         = current.row() - d->elementsInfo[current.row()].relativeOffsetToCategory;
1161 
1162                 if (d->forcedSelectionPosition >= lastCategoryLastRow)
1163                 {
1164                     indexToMove -= 1;
1165                 }
1166                 else
1167                 {
1168                     indexToMove -= qMin((lastCategoryLastRow - d->forcedSelectionPosition + 1),
1169                                         d->forcedSelectionPosition + elementsPerRow + 1);
1170                 }
1171 
1172                 return d->proxyModel->index(indexToMove, 0);
1173             }
1174         }
1175 
1176         case QAbstractItemView::MovePageDown:
1177         {
1178             QModelIndexList visibleIndexes = d->intersectionSet(viewport()->rect());
1179 
1180             if (!visibleIndexes.isEmpty())
1181             {
1182                 int indexToMove = qMin(current.row() + visibleIndexes.size(), d->elementsInfo.size() - 1);
1183 
1184                 return d->proxyModel->index(indexToMove, 0);
1185             }
1186         }
1187 
1188         // fall through
1189 
1190         case QAbstractItemView::MoveDown:
1191         {
1192             if (d->elementsInfo[current.row()].relativeOffsetToCategory < (d->categoriesIndexes[theCategory].count() - 1 - ((d->categoriesIndexes[theCategory].count() - 1) % elementsPerRow)))
1193             {
1194                 int indexToMove = current.row();
1195                 indexToMove    += qMin(elementsPerRow, d->categoriesIndexes[theCategory].count() - 1 -
1196                                        d->elementsInfo[current.row()].relativeOffsetToCategory);
1197 
1198                 return d->proxyModel->index(indexToMove, 0);
1199             }
1200             else
1201             {
1202                 int afterCategoryLastRow = qMin(elementsPerRow, d->categoriesIndexes[afterCategory].count());
1203                 int indexToMove          = current.row() + (d->categoriesIndexes[theCategory].count() -
1204                                                             d->elementsInfo[current.row()].relativeOffsetToCategory);
1205 
1206                 if (d->forcedSelectionPosition >= afterCategoryLastRow)
1207                 {
1208                     indexToMove += afterCategoryLastRow - 1;
1209                 }
1210                 else
1211                 {
1212                     indexToMove += qMin(d->forcedSelectionPosition, elementsPerRow);
1213                 }
1214 
1215                 return d->proxyModel->index(indexToMove, 0);
1216             }
1217         }
1218 
1219         case QAbstractItemView::MoveLeft:
1220 
1221             if (layoutDirection() == Qt::RightToLeft)
1222             {
1223                 if (((current.row() + 1) == d->elementsInfo.size()) ||
1224                     !(d->elementsInfo[current.row() + 1].relativeOffsetToCategory % elementsPerRow))
1225                 {
1226                     return current;
1227                 }
1228 
1229                 return d->proxyModel->index(current.row() + 1, 0);
1230             }
1231 
1232             if ((current.row() == 0) ||
1233                 !(d->elementsInfo[current.row()].relativeOffsetToCategory % elementsPerRow))
1234             {
1235                 return current;
1236             }
1237 
1238             return d->proxyModel->index(current.row() - 1, 0);
1239 
1240         case QAbstractItemView::MoveRight:
1241 
1242             if (layoutDirection() == Qt::RightToLeft)
1243             {
1244                 if ((current.row() == 0) ||
1245                     !(d->elementsInfo[current.row()].relativeOffsetToCategory % elementsPerRow))
1246                 {
1247                     return current;
1248                 }
1249 
1250                 return d->proxyModel->index(current.row() - 1, 0);
1251             }
1252 
1253             if (((current.row() + 1) == d->elementsInfo.size()) ||
1254                 !(d->elementsInfo[current.row() + 1].relativeOffsetToCategory % elementsPerRow))
1255             {
1256                 return current;
1257             }
1258 
1259             return d->proxyModel->index(current.row() + 1, 0);
1260 
1261         default:
1262             break;
1263     }
1264 
1265     return QListView::moveCursor(cursorAction, modifiers);
1266 }
1267 
1268 void DCategorizedView::rowsInserted(const QModelIndex& parent, int start, int end)
1269 {
1270     QListView::rowsInserted(parent, start, end);
1271 
1272     if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
1273     {
1274         d->forcedSelectionPosition = 0;
1275         d->hovered                 = QModelIndex();
1276         d->biggestItemSize         = QSize(0, 0);
1277         d->mouseButtonPressed      = false;
1278         d->rightMouseButtonPressed = false;
1279         d->elementsInfo.clear();
1280         d->elementsPosition.clear();
1281         d->categoriesIndexes.clear();
1282         d->categoriesPosition.clear();
1283         d->categories.clear();
1284         d->intersectedIndexes.clear();
1285 
1286         return;
1287     }
1288 
1289     rowsInsertedArtifficial(parent, start, end);
1290 }
1291 
1292 int DCategorizedView::Private::categoryUpperBound(SparseModelIndexVector& modelIndexList, int begin, int averageSize)
1293 {
1294     int end            = modelIndexList.size();
1295     QString category   = proxyModel->data(modelIndexList[begin],
1296                                           DCategorizedSortFilterProxyModel::CategoryDisplayRole).toString();
1297 
1298     // First case: Small category with <10 entries
1299 
1300     const int smallEnd = qMin(end, begin + 10);
1301 
1302     for (int k = begin ; k < smallEnd ; ++k)
1303     {
1304         if (category != proxyModel->data(modelIndexList[k],
1305                                          DCategorizedSortFilterProxyModel::CategoryDisplayRole).toString())
1306         {
1307             return k;
1308         }
1309     }
1310 
1311     begin += 10;
1312 
1313     // Second case: only one category, test last value
1314 
1315     QString value = proxyModel->data(modelIndexList[end - 1],
1316                                      DCategorizedSortFilterProxyModel::CategoryDisplayRole).toString();
1317 
1318     if (value == category)
1319     {
1320         return end;
1321     }
1322 
1323     // Third case: use average of last category sizes
1324 
1325     if (averageSize && ((begin + averageSize) < end))
1326     {
1327         if      (category != proxyModel->data(modelIndexList[begin + averageSize],
1328                                               DCategorizedSortFilterProxyModel::CategoryDisplayRole).toString())
1329         {
1330             end = begin + averageSize;
1331         }
1332         else if (begin + 2*averageSize < end)
1333         {
1334             if (category != proxyModel->data(modelIndexList[begin + 2*averageSize],
1335                                              DCategorizedSortFilterProxyModel::CategoryDisplayRole).toString())
1336             {
1337                 end = begin + 2 * averageSize;
1338             }
1339         }
1340     }
1341 
1342     // now apply a binary search - the model is sorted by category
1343     // from qUpperBound, Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
1344 
1345     int middle;
1346     int n = end - begin;
1347     int half;
1348 
1349     while (n > 0)
1350     {
1351         half   = n >> 1;
1352         middle = begin + half;
1353 
1354         if (category != proxyModel->data(modelIndexList[middle],
1355                                          DCategorizedSortFilterProxyModel::CategoryDisplayRole).toString())
1356         {
1357             n = half;
1358         }
1359         else
1360         {
1361             begin = middle + 1;
1362             n    -= half + 1;
1363         }
1364     }
1365 
1366     return begin;
1367 }
1368 
1369 void DCategorizedView::rowsInsertedArtifficial(const QModelIndex& parent, int start, int end)
1370 {
1371     Q_UNUSED(parent);
1372 
1373     d->forcedSelectionPosition = 0;
1374     d->hovered                 = QModelIndex();
1375     d->biggestItemSize         = QSize(0, 0);
1376     d->mouseButtonPressed      = false;
1377     d->rightMouseButtonPressed = false;
1378     d->elementsInfo.clear();
1379     d->elementsPosition.clear();
1380     d->categoriesIndexes.clear();
1381     d->categoriesPosition.clear();
1382     d->categories.clear();
1383     d->intersectedIndexes.clear();
1384 
1385     if ((start > end) || (end < 0) || (start < 0) || !d->proxyModel->rowCount())
1386     {
1387         return;
1388     }
1389 
1390     // Add all elements mapped to the source model and explore categories
1391 
1392     const int rowCount   = d->proxyModel->rowCount();
1393     const int sortColumn = d->proxyModel->sortColumn();
1394     QString lastCategory = d->proxyModel->data(d->proxyModel->index(0, sortColumn),
1395                                                DCategorizedSortFilterProxyModel::CategoryDisplayRole).toString();
1396     int offset           = -1;
1397 
1398     SparseModelIndexVector modelIndexList(rowCount, d->proxyModel, sortColumn);
1399 
1400     d->elementsInfo      = QVector<Private::ElementInfo>(rowCount);
1401     int categorySizes    = 0;
1402     int categoryCounts   = 0;
1403 
1404     if (uniformItemSizes())
1405     {
1406         // use last index as sample for size hint
1407 
1408         QModelIndex sample = d->proxyModel->index(rowCount - 1, modelColumn(), rootIndex());
1409         d->biggestItemSize = sizeHintForIndex(sample);
1410     }
1411     else
1412     {
1413         QStyleOptionViewItem option;
1414 
1415 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
1416 
1417         initViewItemOption(&option);
1418 
1419 #else
1420 
1421         option = viewOptions();
1422 
1423 #endif
1424 
1425         for (int k = 0 ; k < rowCount ; ++k)
1426         {
1427             QModelIndex indexSize = (sortColumn == 0) ? modelIndexList[k] : d->proxyModel->index(k, 0);
1428 
1429 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
1430 
1431             QSize hint            = itemDelegateForIndex(indexSize)->sizeHint(option, indexSize);
1432 
1433 #else
1434 
1435             QSize hint            = itemDelegate(indexSize)->sizeHint(option, indexSize);
1436 
1437 #endif
1438 
1439             d->biggestItemSize    = QSize(qMax(hint.width(),  d->biggestItemSize.width()),
1440                                           qMax(hint.height(), d->biggestItemSize.height()));
1441         }
1442     }
1443 
1444     for (int k = 0 ; k < rowCount ; )
1445     {
1446         lastCategory   = d->proxyModel->data(modelIndexList[k], DCategorizedSortFilterProxyModel::CategoryDisplayRole).toString();
1447         int upperBound = d->categoryUpperBound(modelIndexList, k, categorySizes / ++categoryCounts);
1448         categorySizes += upperBound - k;
1449         offset         = 0;
1450 
1451         QVector<int> rows(upperBound - k);
1452 
1453         for (int i = k ; i < upperBound ; ++i, ++offset)
1454         {
1455             rows[offset]                         = i;
1456             Private::ElementInfo& elementInfo    = d->elementsInfo[i];
1457             elementInfo.category                 = lastCategory;
1458             elementInfo.relativeOffsetToCategory = offset;
1459         }
1460 
1461         k = upperBound;
1462 
1463         d->categoriesIndexes.insert(lastCategory, rows);
1464         d->categories << lastCategory;
1465     }
1466 
1467     d->updateScrollbars();
1468 
1469     // FIXME: We need to safely save the last selection. This is on my TODO
1470     // list (ereslibre).
1471     // Note: QItemSelectionModel will save it selection in persistend indexes
1472     // on layoutChanged(). All works fine for me.
1473 /*
1474     selectionModel()->clear();
1475 */
1476 }
1477 
1478 void DCategorizedView::rowsRemoved(const QModelIndex& parent, int start, int end)
1479 {
1480     Q_UNUSED(parent);
1481     Q_UNUSED(start);
1482     Q_UNUSED(end);
1483 
1484     if (d->proxyModel && d->categoryDrawer && d->proxyModel->isCategorizedModel())
1485     {
1486         // Force the view to update all elements
1487 
1488         rowsInsertedArtifficial(QModelIndex(), 0, d->proxyModel->rowCount() - 1);
1489     }
1490 }
1491 
1492 void DCategorizedView::updateGeometries()
1493 {
1494     if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
1495     {
1496         QListView::updateGeometries();
1497         return;
1498     }
1499 
1500     // Avoid QListView::updateGeometries(), since it will try to set another
1501     // range to our scroll bars, what we don't want
1502 
1503     QAbstractItemView::updateGeometries();
1504 }
1505 
1506 void DCategorizedView::slotLayoutChanged()
1507 {
1508     if (d->proxyModel && d->categoryDrawer && d->proxyModel->isCategorizedModel())
1509     {
1510         // all cached values are invalidated, recompute immediately
1511 
1512         rowsInsertedArtifficial(QModelIndex(), 0, d->proxyModel->rowCount() - 1);
1513     }
1514 }
1515 
1516 void DCategorizedView::currentChanged(const QModelIndex& current, const QModelIndex& previous)
1517 {
1518     if (!d->proxyModel || !d->categoryDrawer || !d->proxyModel->isCategorizedModel())
1519     {
1520         QListView::currentChanged(current, previous);
1521         return;
1522     }
1523 
1524     // We need to update the forcedSelectionPosition property in order to correctly
1525     // navigate after with keyboard using up & down keys
1526 
1527     int viewportWidth = viewport()->width() - spacing();
1528 
1529 //    int itemHeight;
1530     int itemWidth;
1531 
1532     if (gridSize().isEmpty())
1533     {
1534 //        itemHeight = d->biggestItemSize.height();
1535         itemWidth = d->biggestItemSize.width();
1536     }
1537     else
1538     {
1539 //        itemHeight = gridSize().height();
1540         itemWidth = gridSize().width();
1541     }
1542 
1543     int itemWidthPlusSeparation = spacing() + itemWidth;
1544 
1545     if (!itemWidthPlusSeparation)
1546     {
1547         ++itemWidthPlusSeparation;
1548     }
1549 
1550     int elementsPerRow = viewportWidth / itemWidthPlusSeparation;
1551 
1552     if (!elementsPerRow)
1553     {
1554         ++elementsPerRow;
1555     }
1556 
1557     if (current.isValid())
1558     {
1559         d->forcedSelectionPosition = d->elementsInfo[current.row()].relativeOffsetToCategory % elementsPerRow;
1560     }
1561 
1562     QListView::currentChanged(current, previous);
1563 }
1564 
1565 } // namespace Digikam
1566 
1567 #include "moc_dcategorizedview.cpp"