File indexing completed on 2024-05-19 04:56:07
0001 /** 0002 * \file modeliterator.cpp 0003 * Iterator for Qt models. 0004 * 0005 * \b Project: Kid3 0006 * \author Urs Fleisch 0007 * \date 26-Mar-2011 0008 * 0009 * Copyright (C) 2011-2024 Urs Fleisch 0010 * 0011 * This file is part of Kid3. 0012 * 0013 * Kid3 is free software; you can redistribute it and/or modify 0014 * it under the terms of the GNU General Public License as published by 0015 * the Free Software Foundation; either version 2 of the License, or 0016 * (at your option) any later version. 0017 * 0018 * Kid3 is distributed in the hope that it will be useful, 0019 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0020 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0021 * GNU General Public License for more details. 0022 * 0023 * You should have received a copy of the GNU General Public License 0024 * along with this program. If not, see <http://www.gnu.org/licenses/>. 0025 */ 0026 0027 #include "modeliterator.h" 0028 #include <QItemSelectionModel> 0029 #include "fileproxymodel.h" 0030 0031 /** 0032 * Constructor. 0033 * 0034 * @param rootIdx root of model to iterate 0035 */ 0036 ModelIterator::ModelIterator(const QPersistentModelIndex& rootIdx) 0037 : m_model(rootIdx.model()) 0038 { 0039 m_nodes.push(rootIdx); 0040 next(); 0041 } 0042 0043 /** 0044 * Check if a next item exists. 0045 * @return true if there is a next index 0046 */ 0047 bool ModelIterator::hasNext() const 0048 { 0049 return m_model && m_nextIdx.isValid(); 0050 } 0051 0052 /** 0053 * Advance iterator and return next item. 0054 * @return next index 0055 */ 0056 QPersistentModelIndex ModelIterator::next() 0057 { 0058 if (!m_model) 0059 return QPersistentModelIndex(); 0060 QPersistentModelIndex result = m_nextIdx; 0061 if (!m_nodes.isEmpty()) { 0062 m_nextIdx = m_nodes.pop(); 0063 if (m_nextIdx.isValid()) { 0064 for (int row = m_model->rowCount(m_nextIdx) - 1; row >= 0; --row) { 0065 m_nodes.push(m_model->index(row, 0, m_nextIdx)); 0066 } 0067 } 0068 } else { 0069 m_nextIdx = QPersistentModelIndex(); 0070 } 0071 return result; 0072 } 0073 0074 /** 0075 * Get next item without moving iterator. 0076 * @return next index 0077 */ 0078 QPersistentModelIndex ModelIterator::peekNext() const 0079 { 0080 if (!m_model) 0081 return QPersistentModelIndex(); 0082 return m_nextIdx; 0083 } 0084 0085 /** 0086 * Advance iterator and return data of next index. 0087 * @param role model item role to get 0088 * @return data of next index 0089 */ 0090 QVariant ModelIterator::nextData(int role) { 0091 if (!m_model) 0092 return QVariant(); 0093 return m_model->data(next(), role); 0094 } 0095 0096 /** 0097 * Get data of next item without moving iterator. 0098 * @param role model item role to get 0099 * @return data of next index 0100 */ 0101 QVariant ModelIterator::peekNextData(int role) const { 0102 if (!m_model) 0103 return QVariant(); 0104 return m_model->data(m_nextIdx, role); 0105 } 0106 0107 0108 /** 0109 * Constructor. 0110 * 0111 * @param rootIdx root of model to iterate 0112 */ 0113 ModelBfsIterator::ModelBfsIterator(const QPersistentModelIndex& rootIdx) 0114 : m_model(rootIdx.model()), m_nextIdx(rootIdx), m_parentIdx(rootIdx), m_row(0) 0115 { 0116 } 0117 0118 /** 0119 * Check if a next item exists. 0120 * @return true if there is a next index 0121 */ 0122 bool ModelBfsIterator::hasNext() const 0123 { 0124 return m_model && m_nextIdx.isValid(); 0125 } 0126 0127 /** 0128 * Advance iterator and return next item. 0129 * @return next index 0130 */ 0131 QPersistentModelIndex ModelBfsIterator::next() 0132 { 0133 if (!m_model) 0134 return QPersistentModelIndex(); 0135 QPersistentModelIndex result = m_nextIdx; 0136 forever { 0137 if (m_parentIdx.isValid() && m_row < m_model->rowCount(m_parentIdx)) { 0138 m_nextIdx = m_model->index(m_row, 0, m_parentIdx); 0139 m_nodes.enqueue(m_nextIdx); 0140 ++m_row; 0141 break; 0142 } 0143 if (!m_nodes.isEmpty()) { 0144 m_parentIdx = m_nodes.dequeue(); 0145 m_row = 0; 0146 } else { 0147 m_nextIdx = QPersistentModelIndex(); 0148 break; 0149 } 0150 } 0151 return result; 0152 } 0153 0154 /** 0155 * Get next item without moving iterator. 0156 * @return next index 0157 */ 0158 QPersistentModelIndex ModelBfsIterator::peekNext() const 0159 { 0160 if (!m_model) 0161 return QPersistentModelIndex(); 0162 return m_nextIdx; 0163 } 0164 0165 /** 0166 * Advance iterator and return data of next index. 0167 * @param role model item role to get 0168 * @return data of next index 0169 */ 0170 QVariant ModelBfsIterator::nextData(int role) { 0171 if (!m_model) 0172 return QVariant(); 0173 return m_model->data(next(), role); 0174 } 0175 0176 /** 0177 * Get data of next item without moving iterator. 0178 * @param role model item role to get 0179 * @return data of next index 0180 */ 0181 QVariant ModelBfsIterator::peekNextData(int role) const { 0182 if (!m_model) 0183 return QVariant(); 0184 return m_model->data(m_nextIdx, role); 0185 } 0186 0187 0188 /** 0189 * Destructor. 0190 */ 0191 AbstractTaggedFileIterator::~AbstractTaggedFileIterator() 0192 { 0193 } 0194 0195 0196 /** 0197 * Constructor. 0198 * 0199 * @param rootIdx root of model to iterate 0200 */ 0201 TaggedFileIterator::TaggedFileIterator(const QPersistentModelIndex& rootIdx) 0202 : m_it(rootIdx), m_nextFile(nullptr) 0203 { 0204 next(); 0205 } 0206 0207 /** 0208 * Advance iterator and return next item. 0209 * @return next file 0210 */ 0211 TaggedFile* TaggedFileIterator::next() 0212 { 0213 TaggedFile* result = m_nextFile; 0214 m_nextFile = nullptr; 0215 while (m_it.hasNext()) { 0216 if (QPersistentModelIndex index = m_it.next(); 0217 (m_nextFile = FileProxyModel::getTaggedFileOfIndex(index)) != nullptr) 0218 break; 0219 } 0220 return result; 0221 } 0222 0223 /** 0224 * Try to close the file handles. 0225 * 0226 * @param index root of model to iterate 0227 */ 0228 void TaggedFileIterator::closeFileHandles(const QPersistentModelIndex& index) 0229 { 0230 TaggedFileIterator it(index); 0231 while (it.hasNext()) { 0232 it.next()->closeFileHandle(); 0233 } 0234 } 0235 0236 0237 /** 0238 * Constructor. 0239 * 0240 * @param rootIdx root of model to iterate 0241 * @param selectModel selection model 0242 * @param allIfNoneSelected treat all files as selected when nothing is 0243 * selected 0244 */ 0245 SelectedTaggedFileIterator::SelectedTaggedFileIterator( 0246 const QPersistentModelIndex& rootIdx, 0247 const QItemSelectionModel* selectModel, 0248 bool allIfNoneSelected) 0249 : m_it(rootIdx), m_nextFile(nullptr), m_selectModel(selectModel), 0250 m_allSelected(!m_selectModel || 0251 (allIfNoneSelected && !m_selectModel->hasSelection())) 0252 { 0253 next(); 0254 } 0255 0256 /** 0257 * Advance iterator and return next item. 0258 * @return next file 0259 */ 0260 TaggedFile* SelectedTaggedFileIterator::next() 0261 { 0262 TaggedFile* result = m_nextFile; 0263 m_nextFile = nullptr; 0264 while (m_it.hasNext()) { 0265 if (QPersistentModelIndex index = m_it.next(); 0266 (m_nextFile = FileProxyModel::getTaggedFileOfIndex(index)) != nullptr && 0267 (m_allSelected || m_selectModel->isSelected(index))) 0268 break; 0269 m_nextFile = nullptr; 0270 } 0271 return result; 0272 } 0273 0274 /** 0275 * Check if nothing is selected. 0276 * @return true if nothing is selected. 0277 */ 0278 bool SelectedTaggedFileIterator::hasNoSelection() const 0279 { 0280 return m_selectModel && !m_selectModel->hasSelection(); 0281 } 0282 0283 /** 0284 * Constructor. 0285 * 0286 * @param index of the directory or a file in it 0287 */ 0288 TaggedFileOfDirectoryIterator::TaggedFileOfDirectoryIterator( 0289 const QPersistentModelIndex& index) 0290 : m_row(0), m_model(index.model()), 0291 m_parentIdx(m_model && m_model->hasChildren(index) 0292 ? index 0293 : QPersistentModelIndex(index.parent())), 0294 m_nextFile(nullptr) 0295 { 0296 next(); 0297 } 0298 0299 /** 0300 * Check if a next item exists. 0301 * @return true if there is a next file 0302 */ 0303 bool TaggedFileOfDirectoryIterator::hasNext() const 0304 { 0305 return m_model && m_nextFile != nullptr; 0306 } 0307 0308 /** 0309 * Advance iterator and return next item. 0310 * @return next file 0311 */ 0312 TaggedFile* TaggedFileOfDirectoryIterator::next() { 0313 if (!m_model) 0314 return nullptr; 0315 TaggedFile* result = m_nextFile; 0316 m_nextFile = nullptr; 0317 while (m_row < m_model->rowCount(m_parentIdx)) { 0318 if (QModelIndex index = m_model->index(m_row++, 0, m_parentIdx); 0319 (m_nextFile = FileProxyModel::getTaggedFileOfIndex(index)) != nullptr) 0320 break; 0321 } 0322 return result; 0323 } 0324 0325 /** 0326 * Get next item without moving iterator. 0327 * @return next file 0328 */ 0329 TaggedFile* TaggedFileOfDirectoryIterator::peekNext() const 0330 { 0331 if (!m_model) 0332 return nullptr; 0333 return m_nextFile; 0334 } 0335 0336 /** 0337 * Get first tagged file in directory. 0338 * @param index of the directory or a file in it 0339 * @return first tagged file in directory, 0 if none. 0340 */ 0341 TaggedFile* TaggedFileOfDirectoryIterator::first( 0342 const QPersistentModelIndex& index) 0343 { 0344 if (TaggedFileOfDirectoryIterator it(index); it.hasNext()) 0345 return it.peekNext(); 0346 return nullptr; 0347 } 0348 0349 0350 /** 0351 * Constructor. 0352 * 0353 * @param index of the directory or a file in it 0354 * @param selectModel selection model 0355 * @param allIfNoneSelected treat all files as selected when nothing is 0356 * selected 0357 */ 0358 SelectedTaggedFileOfDirectoryIterator::SelectedTaggedFileOfDirectoryIterator( 0359 const QPersistentModelIndex& index, 0360 const QItemSelectionModel* selectModel, 0361 bool allIfNoneSelected) 0362 : m_row(0), m_model(index.model()), 0363 m_parentIdx(m_model && m_model->hasChildren(index) 0364 ? index : QPersistentModelIndex(index.parent())), 0365 m_nextFile(nullptr), 0366 m_selectModel(selectModel), 0367 m_allSelected(!m_selectModel || 0368 (allIfNoneSelected && !m_selectModel->hasSelection())) 0369 { 0370 next(); 0371 } 0372 0373 /** 0374 * Check if a next item exists. 0375 * @return true if there is a next file 0376 */ 0377 bool SelectedTaggedFileOfDirectoryIterator::hasNext() const 0378 { 0379 return m_model && m_nextFile != nullptr; 0380 } 0381 0382 /** 0383 * Advance iterator and return next item. 0384 * @return next file 0385 */ 0386 TaggedFile* SelectedTaggedFileOfDirectoryIterator::next() { 0387 if (!m_model) 0388 return nullptr; 0389 TaggedFile* result = m_nextFile; 0390 m_nextFile = nullptr; 0391 while (m_row < m_model->rowCount(m_parentIdx)) { 0392 if (QModelIndex index = m_model->index(m_row++, 0, m_parentIdx); 0393 (m_nextFile = FileProxyModel::getTaggedFileOfIndex(index)) != nullptr && 0394 (m_allSelected || m_selectModel->isSelected(index))) 0395 break; 0396 m_nextFile = nullptr; 0397 } 0398 return result; 0399 } 0400 0401 /** 0402 * Get next item without moving iterator. 0403 * @return next file 0404 */ 0405 TaggedFile* SelectedTaggedFileOfDirectoryIterator::peekNext() const 0406 { 0407 if (!m_model) 0408 return nullptr; 0409 return m_nextFile; 0410 } 0411 0412 0413 /** 0414 * Constructor. 0415 * 0416 * @param selectModel selection model 0417 */ 0418 TaggedFileOfSelectedDirectoriesIterator::TaggedFileOfSelectedDirectoriesIterator( 0419 const QItemSelectionModel* selectModel) : m_model(nullptr), m_dirIdx(0), m_row(0), 0420 m_nextFile(nullptr) 0421 { 0422 if (selectModel && 0423 (m_model = qobject_cast<const FileProxyModel*>(selectModel->model())) 0424 != nullptr) { 0425 const auto indexes = selectModel->selectedRows(); 0426 for (const QModelIndex& index : indexes) { 0427 if (m_model->isDir(index)) { 0428 m_dirIndexes.append(getIndexesOfDirWithSubDirs(index)); 0429 } 0430 } 0431 } 0432 next(); 0433 } 0434 0435 /** 0436 * Get indexes of directory and recursively all subdirectories. 0437 * @param dirIndex index of directory 0438 * @return list with dirIndex and its subdirectories. 0439 */ 0440 QList<QPersistentModelIndex> 0441 TaggedFileOfSelectedDirectoriesIterator::getIndexesOfDirWithSubDirs( 0442 const QModelIndex& dirIndex) const 0443 { 0444 QList<QPersistentModelIndex> dirs; 0445 dirs.append(dirIndex); 0446 for (int dirsPos = 0; dirsPos < dirs.size(); ++dirsPos) { 0447 QPersistentModelIndex parentIndex(dirs.at(dirsPos)); 0448 for (int row = 0; row < m_model->rowCount(parentIndex); ++row) { 0449 if (QModelIndex index(m_model->index(row, 0, parentIndex)); 0450 m_model->isDir(index)) { 0451 dirs.append(index); 0452 } 0453 } 0454 } 0455 return dirs; 0456 } 0457 0458 /** 0459 * Check if a next item exists. 0460 * @return true if there is a next file 0461 */ 0462 bool TaggedFileOfSelectedDirectoriesIterator::hasNext() const 0463 { 0464 return m_model && m_nextFile != nullptr; 0465 } 0466 0467 /** 0468 * Advance iterator and return next item. 0469 * @return next file 0470 */ 0471 TaggedFile* TaggedFileOfSelectedDirectoriesIterator::next() 0472 { 0473 if (!m_model) 0474 return nullptr; 0475 TaggedFile* result = m_nextFile; 0476 m_nextFile = nullptr; 0477 while (!m_nextFile) { 0478 if (m_dirIdx >= m_dirIndexes.size()) 0479 break; 0480 QPersistentModelIndex parentIdx(m_dirIndexes.at(m_dirIdx)); 0481 while (m_row < m_model->rowCount(parentIdx)) { 0482 if (QModelIndex index = m_model->index(m_row++, 0, parentIdx); 0483 (m_nextFile = FileProxyModel::getTaggedFileOfIndex(index)) != nullptr) 0484 break; 0485 } 0486 if (m_row >= m_model->rowCount(parentIdx)) { 0487 ++m_dirIdx; 0488 m_row = 0; 0489 } 0490 } 0491 return result; 0492 } 0493 0494 /** 0495 * Get next item without moving iterator. 0496 * @return next file 0497 */ 0498 TaggedFile* TaggedFileOfSelectedDirectoriesIterator::peekNext() const 0499 { 0500 if (!m_model) 0501 return nullptr; 0502 return m_nextFile; 0503 }