File indexing completed on 2024-05-19 04:56:07

0001 /**
0002  * \file modeliterator.h
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 #pragma once
0028 
0029 #include <QPersistentModelIndex>
0030 #include <QStack>
0031 #include <QQueue>
0032 #include "kid3api.h"
0033 
0034 class QItemSelectionModel;
0035 class TaggedFile;
0036 class FileProxyModel;
0037 
0038 /**
0039  * Generic Java-style iterator for Qt models.
0040  * Iterates using preorder traversal. Supports only one column.
0041  *
0042  * Typical usage:
0043  * @code
0044  * // get rootIndex...
0045  * ModelIterator it(rootIndex);
0046  * while (it.hasNext()) {
0047  *   TaggedFile* taggedFile;
0048  *   if (ModelIterator::getTaggedFileOfIndex(it.peekNext(), &taggedFile)
0049  *       && taggedFile) {
0050  *     // do something with taggedFile...
0051  *   }
0052  *   it.next();
0053  * }
0054  * @endcode
0055  */
0056 class ModelIterator {
0057 public:
0058   /**
0059    * Constructor.
0060    *
0061    * @param rootIdx root of model to iterate
0062    */
0063   explicit ModelIterator(const QPersistentModelIndex& rootIdx);
0064 
0065   /**
0066    * Check if a next item exists.
0067    * @return true if there is a next index
0068    */
0069   bool hasNext() const;
0070 
0071   /**
0072    * Advance iterator and return next item.
0073    * @return next index
0074    */
0075   QPersistentModelIndex next();
0076 
0077   /**
0078    * Get next item without moving iterator.
0079    * @return next index
0080    */
0081   QPersistentModelIndex peekNext() const;
0082 
0083   /**
0084    * Advance iterator and return data of next index.
0085    * @param role model item role to get
0086    * @return data of next index
0087    */
0088   QVariant nextData(int role);
0089 
0090   /**
0091    * Get data of next item without moving iterator.
0092    * @param role model item role to get
0093    * @return data of next index
0094    */
0095   QVariant peekNextData(int role) const;
0096 
0097 private:
0098   QStack<QPersistentModelIndex> m_nodes;
0099   const QAbstractItemModel* m_model;
0100   QPersistentModelIndex m_nextIdx;
0101 };
0102 
0103 /**
0104  * Generic Java-style iterator for Qt models.
0105  * Iterates using breadth-first-search. Supports only one column.
0106  */
0107 class ModelBfsIterator {
0108 public:
0109   /**
0110    * Constructor.
0111    *
0112    * @param rootIdx root of model to iterate
0113    */
0114   explicit ModelBfsIterator(const QPersistentModelIndex& rootIdx);
0115 
0116   /**
0117    * Check if a next item exists.
0118    * @return true if there is a next index
0119    */
0120   bool hasNext() const;
0121 
0122   /**
0123    * Advance iterator and return next item.
0124    * @return next index
0125    */
0126   QPersistentModelIndex next();
0127 
0128   /**
0129    * Get next item without moving iterator.
0130    * @return next index
0131    */
0132   QPersistentModelIndex peekNext() const;
0133 
0134   /**
0135    * Advance iterator and return data of next index.
0136    * @param role model item role to get
0137    * @return data of next index
0138    */
0139   QVariant nextData(int role);
0140 
0141   /**
0142    * Get data of next item without moving iterator.
0143    * @param role model item role to get
0144    * @return data of next index
0145    */
0146   QVariant peekNextData(int role) const;
0147 
0148 private:
0149   QQueue<QPersistentModelIndex> m_nodes;
0150   const QAbstractItemModel* m_model;
0151   QPersistentModelIndex m_nextIdx;
0152   QPersistentModelIndex m_parentIdx;
0153   int m_row;
0154 };
0155 
0156 /**
0157  * Abstract base class for tagged file iterators.
0158  */
0159 class KID3_CORE_EXPORT AbstractTaggedFileIterator {
0160 public:
0161   /**
0162    * Destructor.
0163    */
0164   virtual ~AbstractTaggedFileIterator();
0165 
0166   /**
0167    * Check if a next item exists.
0168    * @return true if there is a next file
0169    */
0170   virtual bool hasNext() const = 0;
0171 
0172   /**
0173    * Advance iterator and return next item.
0174    * @return next file
0175    */
0176   virtual TaggedFile* next() = 0;
0177 
0178   /**
0179    * Get next item without moving iterator.
0180    * @return next file
0181    */
0182   virtual TaggedFile* peekNext() const = 0;
0183 };
0184 
0185 /**
0186  * Iterator to iterate over model indexes with tagged files.
0187  * All TaggedFiles returned while hasNext() is true are not null.
0188  *
0189  * Typical usage:
0190  * @code
0191  * // get rootIndex...
0192  * TaggedFileIterator it(rootIndex);
0193  * while (it.hasNext()) {
0194  *   TaggedFile* taggedFile = it.next();
0195  *   // do something with taggedFile...
0196  * }
0197  * @endcode
0198  */
0199 class KID3_CORE_EXPORT TaggedFileIterator : public AbstractTaggedFileIterator {
0200 public:
0201   /**
0202    * Constructor.
0203    *
0204    * @param rootIdx root of model to iterate
0205    */
0206   explicit TaggedFileIterator(const QPersistentModelIndex& rootIdx);
0207 
0208   /**
0209    * Check if a next item exists.
0210    * @return true if there is a next file
0211    */
0212   bool hasNext() const override { return m_nextFile != nullptr; }
0213 
0214   /**
0215    * Advance iterator and return next item.
0216    * @return next file
0217    */
0218   TaggedFile* next() override;
0219 
0220   /**
0221    * Get next item without moving iterator.
0222    * @return next file
0223    */
0224   TaggedFile* peekNext() const override { return m_nextFile; }
0225 
0226   /**
0227    * Try to close the file handles.
0228    *
0229    * @param index root of model to iterate
0230    */
0231   static void closeFileHandles(const QPersistentModelIndex& index);
0232 
0233 private:
0234   ModelIterator m_it;
0235   TaggedFile* m_nextFile;
0236 };
0237 
0238 /**
0239  * Iterator to iterate over model indexes with selected tagged files.
0240  * All TaggedFiles returned while hasNext() is true are not null.
0241  *
0242  * Typical usage:
0243  * @code
0244  * // get rootIndex, selectionModel...
0245  * SelectedTaggedFileIterator it(rootIndex, selectionModel, false);
0246  * while (it.hasNext()) {
0247  *   TaggedFile* taggedFile = it.next();
0248  *   // do something with taggedFile...
0249  * }
0250  * @endcode
0251  */
0252 class SelectedTaggedFileIterator : public AbstractTaggedFileIterator {
0253 public:
0254   /**
0255    * Constructor.
0256    *
0257    * @param rootIdx root of model to iterate
0258    * @param selectModel selection model
0259    * @param allIfNoneSelected treat all files as selected when nothing is
0260    * selected
0261    */
0262   SelectedTaggedFileIterator(const QPersistentModelIndex& rootIdx,
0263                              const QItemSelectionModel* selectModel,
0264                              bool allIfNoneSelected);
0265 
0266   /**
0267    * Check if a next item exists.
0268    * @return true if there is a next file
0269    */
0270   bool hasNext() const override { return m_nextFile != nullptr; }
0271 
0272   /**
0273    * Advance iterator and return next item.
0274    * @return next file
0275    */
0276   TaggedFile* next() override;
0277 
0278   /**
0279    * Get next item without moving iterator.
0280    * @return next file
0281    */
0282   TaggedFile* peekNext() const override { return m_nextFile; }
0283 
0284   /**
0285    * Check if nothing is selected.
0286    * @return true if nothing is selected.
0287    */
0288   bool hasNoSelection() const;
0289 
0290 private:
0291   ModelIterator m_it;
0292   TaggedFile* m_nextFile;
0293   const QItemSelectionModel* m_selectModel;
0294   bool m_allSelected;
0295 };
0296 
0297 /**
0298  * Iterator to iterate tagged files from a single directory.
0299  * All TaggedFiles returned while hasNext() is true are not null.
0300  *
0301  * Typical usage:
0302  * @code
0303  * // get currentIndex...
0304  * TaggedFileOfDirectoryIterator it(currentIndex);
0305  * while (it.hasNext()) {
0306  *   TaggedFile* taggedFile = it.next();
0307  *   // do something with taggedFile...
0308  * }
0309  * @endcode
0310  */
0311 class KID3_CORE_EXPORT TaggedFileOfDirectoryIterator : public AbstractTaggedFileIterator {
0312 public:
0313   /**
0314    * Constructor.
0315    *
0316    * @param index of the directory or a file in it
0317    */
0318   explicit TaggedFileOfDirectoryIterator(const QPersistentModelIndex& index);
0319 
0320   /**
0321    * Check if a next item exists.
0322    * @return true if there is a next file
0323    */
0324   bool hasNext() const override;
0325 
0326   /**
0327    * Advance iterator and return next item.
0328    * @return next file
0329    */
0330   TaggedFile* next() override;
0331 
0332   /**
0333    * Get next item without moving iterator.
0334    * @return next file
0335    */
0336   TaggedFile* peekNext() const override;
0337 
0338   /**
0339    * Get first tagged file in directory.
0340    * @param index of the directory or a file in it
0341    * @return first tagged file in directory, 0 if none.
0342    */
0343   static TaggedFile* first(const QPersistentModelIndex& index);
0344 
0345 private:
0346   int m_row;
0347   const QAbstractItemModel* m_model;
0348   QPersistentModelIndex m_parentIdx;
0349   TaggedFile* m_nextFile;
0350 };
0351 
0352 /**
0353  * Iterator to iterate selected tagged files from a single directory.
0354  * All TaggedFiles returned while hasNext() is true are not null.
0355  *
0356  * Typical usage:
0357  * @code
0358  * // get currentIndex, selectionModel...
0359  * SelectedTaggedFileOfDirectoryIterator it(currentIndex, selectionModel, false);
0360  * while (it.hasNext()) {
0361  *   TaggedFile* taggedFile = it.next();
0362  *   // do something with taggedFile...
0363  * }
0364  * @endcode
0365  */
0366 class SelectedTaggedFileOfDirectoryIterator
0367     : public AbstractTaggedFileIterator {
0368 public:
0369   /**
0370    * Constructor.
0371    *
0372    * @param index of the directory or a file in it
0373    * @param selectModel selection model
0374    * @param allIfNoneSelected treat all files as selected when nothing is
0375    * selected
0376    */
0377   SelectedTaggedFileOfDirectoryIterator(
0378     const QPersistentModelIndex& index,
0379     const QItemSelectionModel* selectModel,
0380     bool allIfNoneSelected);
0381 
0382   /**
0383    * Check if a next item exists.
0384    * @return true if there is a next file
0385    */
0386   bool hasNext() const override;
0387 
0388   /**
0389    * Advance iterator and return next item.
0390    * @return next file
0391    */
0392   TaggedFile* next() override;
0393 
0394   /**
0395    * Get next item without moving iterator.
0396    * @return next file
0397    */
0398   TaggedFile* peekNext() const override;
0399 
0400 private:
0401   int m_row;
0402   const QAbstractItemModel* m_model;
0403   QPersistentModelIndex m_parentIdx;
0404   TaggedFile* m_nextFile;
0405   const QItemSelectionModel* m_selectModel;
0406   bool m_allSelected;
0407 };
0408 
0409 /**
0410  * Iterator to iterate all tagged files from selected directories.
0411  * All TaggedFiles returned while hasNext() is true are not null.
0412  *
0413  * Typical usage:
0414  * @code
0415  * // get selectionModel...
0416  * TaggedFileOfSelectedDirectoriesIterator it(selectionModel);
0417  * while (it.hasNext()) {
0418  *   TaggedFile* taggedFile = it.next();
0419  *   // do something with taggedFile...
0420  * }
0421  * @endcode
0422  */
0423 class TaggedFileOfSelectedDirectoriesIterator
0424     : public AbstractTaggedFileIterator {
0425 public:
0426   /**
0427    * Constructor.
0428    *
0429    * @param selectModel selection model
0430    */
0431   explicit TaggedFileOfSelectedDirectoriesIterator(
0432     const QItemSelectionModel* selectModel);
0433 
0434   /**
0435    * Check if a next item exists.
0436    * @return true if there is a next file
0437    */
0438   bool hasNext() const override;
0439 
0440   /**
0441    * Advance iterator and return next item.
0442    * @return next file
0443    */
0444   TaggedFile* next() override;
0445 
0446   /**
0447    * Get next item without moving iterator.
0448    * @return next file
0449    */
0450   TaggedFile* peekNext() const override;
0451 
0452 private:
0453   /**
0454    * Get indexes of directory and recursively all subdirectories.
0455    * @param dirIndex index of directory
0456    * @return list with dirIndex and its subdirectories.
0457    */
0458   QList<QPersistentModelIndex> getIndexesOfDirWithSubDirs(
0459       const QModelIndex& dirIndex) const;
0460 
0461   const FileProxyModel* m_model;
0462   QList<QPersistentModelIndex> m_dirIndexes;
0463   int m_dirIdx;
0464   int m_row;
0465   TaggedFile* m_nextFile;
0466 };