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"