File indexing completed on 2024-04-28 05:45:07

0001 /*
0002  * SPDX-FileCopyrightText: 2011 Peter Penz <peter.penz19@gmail.com>
0003  * SPDX-FileCopyrightText: 2011 Frank Reininghaus <frank78ac@googlemail.com>
0004  *
0005  * Based on the Itemviews NG project from Trolltech Labs
0006  *
0007  * SPDX-License-Identifier: GPL-2.0-or-later
0008  */
0009 
0010 #include "kitemlistselectionmanager.h"
0011 
0012 KItemListSelectionManager::KItemListSelectionManager(QObject *parent)
0013     : QObject(parent)
0014     , m_currentItem(-1)
0015     , m_anchorItem(-1)
0016     , m_selectedItems()
0017     , m_isAnchoredSelectionActive(false)
0018     , m_model(nullptr)
0019 {
0020 }
0021 
0022 KItemListSelectionManager::~KItemListSelectionManager()
0023 {
0024 }
0025 
0026 void KItemListSelectionManager::setCurrentItem(int current)
0027 {
0028     const int previous = m_currentItem;
0029     const KItemSet previousSelection = selectedItems();
0030 
0031     if (m_model && current >= 0 && current < m_model->count()) {
0032         m_currentItem = current;
0033     } else {
0034         m_currentItem = -1;
0035     }
0036 
0037     if (m_currentItem != previous) {
0038         Q_EMIT currentChanged(m_currentItem, previous);
0039 
0040         if (m_isAnchoredSelectionActive) {
0041             const KItemSet selection = selectedItems();
0042             if (selection != previousSelection) {
0043                 Q_EMIT selectionChanged(selection, previousSelection);
0044             }
0045         }
0046     }
0047 }
0048 
0049 int KItemListSelectionManager::currentItem() const
0050 {
0051     return m_currentItem;
0052 }
0053 
0054 void KItemListSelectionManager::setSelectedItems(const KItemSet &items)
0055 {
0056     if (m_selectedItems != items) {
0057         const KItemSet previous = m_selectedItems;
0058         m_selectedItems = items;
0059         Q_EMIT selectionChanged(m_selectedItems, previous);
0060     }
0061 }
0062 
0063 KItemSet KItemListSelectionManager::selectedItems() const
0064 {
0065     KItemSet selectedItems = m_selectedItems;
0066 
0067     if (m_isAnchoredSelectionActive && m_anchorItem != m_currentItem) {
0068         Q_ASSERT(m_anchorItem >= 0);
0069         Q_ASSERT(m_currentItem >= 0);
0070         const int from = qMin(m_anchorItem, m_currentItem);
0071         const int to = qMax(m_anchorItem, m_currentItem);
0072 
0073         for (int index = from; index <= to; ++index) {
0074             selectedItems.insert(index);
0075         }
0076     }
0077 
0078     return selectedItems;
0079 }
0080 
0081 bool KItemListSelectionManager::isSelected(int index) const
0082 {
0083     if (m_selectedItems.contains(index)) {
0084         return true;
0085     }
0086 
0087     if (m_isAnchoredSelectionActive && m_anchorItem != m_currentItem) {
0088         Q_ASSERT(m_anchorItem >= 0);
0089         Q_ASSERT(m_currentItem >= 0);
0090         const int from = qMin(m_anchorItem, m_currentItem);
0091         const int to = qMax(m_anchorItem, m_currentItem);
0092 
0093         if (from <= index && index <= to) {
0094             return true;
0095         }
0096     }
0097 
0098     return false;
0099 }
0100 
0101 bool KItemListSelectionManager::hasSelection() const
0102 {
0103     return !m_selectedItems.isEmpty() || (m_isAnchoredSelectionActive && m_anchorItem != m_currentItem);
0104 }
0105 
0106 void KItemListSelectionManager::setSelected(int index, int count, SelectionMode mode)
0107 {
0108     if (index < 0 || count < 1 || !m_model || index >= m_model->count()) {
0109         return;
0110     }
0111 
0112     endAnchoredSelection();
0113     const KItemSet previous = selectedItems();
0114 
0115     count = qMin(count, m_model->count() - index);
0116 
0117     const int endIndex = index + count - 1;
0118     switch (mode) {
0119     case Select:
0120         for (int i = index; i <= endIndex; ++i) {
0121             m_selectedItems.insert(i);
0122         }
0123         break;
0124 
0125     case Deselect:
0126         for (int i = index; i <= endIndex; ++i) {
0127             m_selectedItems.remove(i);
0128         }
0129         break;
0130 
0131     case Toggle:
0132         for (int i = index; i <= endIndex; ++i) {
0133             if (m_selectedItems.contains(i)) {
0134                 m_selectedItems.remove(i);
0135             } else {
0136                 m_selectedItems.insert(i);
0137             }
0138         }
0139         break;
0140 
0141     default:
0142         Q_ASSERT(false);
0143         break;
0144     }
0145 
0146     const KItemSet selection = selectedItems();
0147     if (selection != previous) {
0148         Q_EMIT selectionChanged(selection, previous);
0149     }
0150 }
0151 
0152 void KItemListSelectionManager::clearSelection()
0153 {
0154     const KItemSet previous = selectedItems();
0155     if (!previous.isEmpty()) {
0156         m_selectedItems.clear();
0157         m_isAnchoredSelectionActive = false;
0158         Q_EMIT selectionChanged(KItemSet(), previous);
0159     }
0160 }
0161 
0162 void KItemListSelectionManager::replaceSelection(int index, int count)
0163 {
0164     const KItemSet previous = selectedItems();
0165     if (!previous.isEmpty()) {
0166         m_selectedItems.clear();
0167         m_isAnchoredSelectionActive = false;
0168     }
0169     setSelected(index, count);
0170 }
0171 
0172 void KItemListSelectionManager::beginAnchoredSelection(int anchor)
0173 {
0174     if (anchor >= 0 && m_model && anchor < m_model->count()) {
0175         m_isAnchoredSelectionActive = true;
0176         m_anchorItem = anchor;
0177     }
0178 }
0179 
0180 void KItemListSelectionManager::endAnchoredSelection()
0181 {
0182     if (m_isAnchoredSelectionActive && (m_anchorItem != m_currentItem)) {
0183         Q_ASSERT(m_anchorItem >= 0);
0184         Q_ASSERT(m_currentItem >= 0);
0185         const int from = qMin(m_anchorItem, m_currentItem);
0186         const int to = qMax(m_anchorItem, m_currentItem);
0187 
0188         for (int index = from; index <= to; ++index) {
0189             m_selectedItems.insert(index);
0190         }
0191     }
0192 
0193     m_isAnchoredSelectionActive = false;
0194 }
0195 
0196 bool KItemListSelectionManager::isAnchoredSelectionActive() const
0197 {
0198     return m_isAnchoredSelectionActive;
0199 }
0200 
0201 KItemModelBase *KItemListSelectionManager::model() const
0202 {
0203     return m_model;
0204 }
0205 
0206 void KItemListSelectionManager::setModel(KItemModelBase *model)
0207 {
0208     m_model = model;
0209     if (model && model->count() > 0) {
0210         m_currentItem = 0;
0211     }
0212 }
0213 
0214 void KItemListSelectionManager::itemsInserted(const KItemRangeList &itemRanges)
0215 {
0216     // Store the current selection (needed in the selectionChanged() signal)
0217     const KItemSet previousSelection = selectedItems();
0218 
0219     // Update the current item
0220     if (m_currentItem < 0) {
0221         setCurrentItem(0);
0222     } else {
0223         const int previousCurrent = m_currentItem;
0224         int inc = 0;
0225         for (const KItemRange &itemRange : itemRanges) {
0226             if (m_currentItem < itemRange.index) {
0227                 break;
0228             }
0229             inc += itemRange.count;
0230         }
0231         // Calling setCurrentItem would trigger the selectionChanged signal, but we want to
0232         // emit it only once in this function -> change the current item manually and emit currentChanged
0233         m_currentItem += inc;
0234         if (m_currentItem >= m_model->count()) {
0235             m_currentItem = -1;
0236         }
0237         Q_EMIT currentChanged(m_currentItem, previousCurrent);
0238     }
0239 
0240     // Update the anchor item
0241     if (m_anchorItem < 0) {
0242         m_anchorItem = 0;
0243     } else {
0244         int inc = 0;
0245         for (const KItemRange &itemRange : itemRanges) {
0246             if (m_anchorItem < itemRange.index) {
0247                 break;
0248             }
0249             inc += itemRange.count;
0250         }
0251         m_anchorItem += inc;
0252     }
0253 
0254     // Update the selections
0255     if (!m_selectedItems.isEmpty()) {
0256         const KItemSet previous = m_selectedItems;
0257         m_selectedItems.clear();
0258 
0259         for (int index : previous) {
0260             int inc = 0;
0261             for (const KItemRange &itemRange : itemRanges) {
0262                 if (index < itemRange.index) {
0263                     break;
0264                 }
0265                 inc += itemRange.count;
0266             }
0267             m_selectedItems.insert(index + inc);
0268         }
0269     }
0270 
0271     const KItemSet selection = selectedItems();
0272     if (selection != previousSelection) {
0273         Q_EMIT selectionChanged(selection, previousSelection);
0274     }
0275 }
0276 
0277 void KItemListSelectionManager::itemsRemoved(const KItemRangeList &itemRanges)
0278 {
0279     // Store the current selection (needed in the selectionChanged() signal)
0280     const KItemSet previousSelection = selectedItems();
0281     const int previousCurrent = m_currentItem;
0282 
0283     // Update the current item
0284     m_currentItem = indexAfterRangesRemoving(m_currentItem, itemRanges, DiscardRemovedIndex);
0285     if (m_currentItem != previousCurrent) {
0286         Q_EMIT currentChanged(m_currentItem, previousCurrent);
0287         if (m_currentItem < 0) {
0288             // Calling setCurrentItem() would trigger the selectionChanged signal, but we want to
0289             // emit it only once in this function -> change the current item manually and emit currentChanged
0290             m_currentItem = indexAfterRangesRemoving(previousCurrent, itemRanges, AdjustRemovedIndex);
0291             Q_EMIT currentChanged(m_currentItem, -1);
0292         }
0293     }
0294 
0295     // Update the anchor item
0296     if (m_anchorItem >= 0) {
0297         m_anchorItem = indexAfterRangesRemoving(m_anchorItem, itemRanges, DiscardRemovedIndex);
0298         if (m_anchorItem < 0) {
0299             m_isAnchoredSelectionActive = false;
0300         }
0301     }
0302 
0303     // Update the selections and the anchor item
0304     if (!m_selectedItems.isEmpty()) {
0305         const KItemSet previous = m_selectedItems;
0306         m_selectedItems.clear();
0307 
0308         for (int oldIndex : previous) {
0309             const int index = indexAfterRangesRemoving(oldIndex, itemRanges, DiscardRemovedIndex);
0310             if (index >= 0) {
0311                 m_selectedItems.insert(index);
0312             }
0313         }
0314     }
0315 
0316     const KItemSet selection = selectedItems();
0317     if (selection != previousSelection) {
0318         Q_EMIT selectionChanged(selection, previousSelection);
0319     }
0320 
0321     Q_ASSERT(m_currentItem < m_model->count());
0322     Q_ASSERT(m_anchorItem < m_model->count());
0323 }
0324 
0325 void KItemListSelectionManager::itemsMoved(const KItemRange &itemRange, const QList<int> &movedToIndexes)
0326 {
0327     // Store the current selection (needed in the selectionChanged() signal)
0328     const KItemSet previousSelection = selectedItems();
0329 
0330     // Store whether we were doing an anchored selection
0331     const bool wasInAnchoredSelection = isAnchoredSelectionActive();
0332 
0333     // endAnchoredSelection() adds all items between m_currentItem and
0334     // m_anchorItem to m_selectedItems. They can then be moved
0335     // individually later in this function.
0336     endAnchoredSelection();
0337 
0338     // Update the current item
0339     if (m_currentItem >= itemRange.index && m_currentItem < itemRange.index + itemRange.count) {
0340         const int previousCurrentItem = m_currentItem;
0341         const int newCurrentItem = movedToIndexes.at(previousCurrentItem - itemRange.index);
0342 
0343         // Calling setCurrentItem would trigger the selectionChanged signal, but we want to
0344         // emit it only once in this function -> change the current item manually and emit currentChanged
0345         m_currentItem = newCurrentItem;
0346         Q_EMIT currentChanged(newCurrentItem, previousCurrentItem);
0347     }
0348 
0349     // Start a new anchored selection.
0350     if (wasInAnchoredSelection) {
0351         beginAnchoredSelection(m_currentItem);
0352     }
0353 
0354     // Update the selections
0355     if (!m_selectedItems.isEmpty()) {
0356         const KItemSet previous = m_selectedItems;
0357         m_selectedItems.clear();
0358 
0359         for (int index : previous) {
0360             if (index >= itemRange.index && index < itemRange.index + itemRange.count) {
0361                 m_selectedItems.insert(movedToIndexes.at(index - itemRange.index));
0362             } else {
0363                 m_selectedItems.insert(index);
0364             }
0365         }
0366     }
0367 
0368     const KItemSet selection = selectedItems();
0369     if (selection != previousSelection) {
0370         Q_EMIT selectionChanged(selection, previousSelection);
0371     }
0372 }
0373 
0374 int KItemListSelectionManager::indexAfterRangesRemoving(int index, const KItemRangeList &itemRanges, const RangesRemovingBehaviour behaviour) const
0375 {
0376     int dec = 0;
0377     for (const KItemRange &itemRange : itemRanges) {
0378         if (index < itemRange.index) {
0379             break;
0380         }
0381 
0382         dec += itemRange.count;
0383 
0384         const int firstIndexAfterRange = itemRange.index + itemRange.count;
0385         if (index < firstIndexAfterRange) {
0386             // The index is part of the removed range
0387             if (behaviour == DiscardRemovedIndex) {
0388                 return -1;
0389             } else {
0390                 // Use the first item after the range as new index
0391                 index = firstIndexAfterRange;
0392                 break;
0393             }
0394         }
0395     }
0396     return qBound(-1, index - dec, m_model->count() - 1);
0397 }
0398 
0399 #include "moc_kitemlistselectionmanager.cpp"