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

0001 /**
0002  * \file fileproxymodel.h
0003  * Proxy for filesystem model which filters files.
0004  *
0005  * \b Project: Kid3
0006  * \author Urs Fleisch
0007  * \date 22-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 <QSortFilterProxyModel>
0030 #include <QHash>
0031 #include <QSet>
0032 #include <QFileInfo>
0033 #include <QStringList>
0034 #include <QScopedPointer>
0035 #include "taggedfile.h"
0036 #include "kid3api.h"
0037 
0038 class QTimer;
0039 class QFileInfo;
0040 class TaggedFileSystemModel;
0041 class CoreTaggedFileIconProvider;
0042 class ITaggedFileFactory;
0043 
0044 /**
0045  * Proxy for filesystem model which filters files.
0046  */
0047 class KID3_CORE_EXPORT FileProxyModel : public QSortFilterProxyModel {
0048   Q_OBJECT
0049 public:
0050   /**
0051    * Constructor.
0052    *
0053    * @param parent parent object
0054    */
0055   explicit FileProxyModel(QObject* parent = nullptr);
0056 
0057   /**
0058    * Destructor.
0059    */
0060   ~FileProxyModel() override;
0061 
0062   /**
0063    * Get item flags.
0064    * @param index index of item
0065    * @return default flags plus drag enabled depending on
0066    * setExclusiveDraggableIndex().
0067    */
0068   Qt::ItemFlags flags(const QModelIndex& index) const override;
0069 
0070   /**
0071    * Set source model.
0072    * @param sourceModel source model, must be TaggedFileSystemModel
0073    */
0074   void setSourceModel(QAbstractItemModel* sourceModel) override;
0075 
0076   /**
0077    * Check if more data is available.
0078    * @param parent parent index of items to fetch
0079    * @return true if more data available.
0080    */
0081   bool canFetchMore(const QModelIndex& parent) const override;
0082 
0083   /**
0084    * Fetches any available data.
0085    * @param parent parent index of items to fetch
0086    */
0087   void fetchMore(const QModelIndex& parent) override;
0088 
0089   /**
0090    * Sort model.
0091    *
0092    * This method will directly call FileSystemModel::sort() on the
0093    * sourceModel() to take advantage of that specialized behavior. This
0094    * will change the order in the source model.
0095    *
0096    * @param column column to sort
0097    * @param order ascending or descending order
0098    */
0099   void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override;
0100 
0101   /**
0102    * Map role identifiers to role property names in scripting languages.
0103    * @return hash mapping role identifiers to names.
0104    */
0105   QHash<int, QByteArray> roleNames() const override;
0106 
0107   /**
0108    * Check if the model is currently loading a directory.
0109    * @return true if loading is in progress.
0110    */
0111   bool isLoading() const { return m_isLoading; }
0112 
0113   /**
0114    * Sets the name filters to apply against the existing files.
0115    * @param filters list of strings containing wildcards like "*.mp3"
0116    */
0117   void setNameFilters(const QStringList& filters);
0118 
0119   /**
0120    * Filter out a model index.
0121    * @param index source model index which has to be filtered out
0122    */
0123   void filterOutIndex(const QPersistentModelIndex& index);
0124 
0125   /**
0126    * Stop filtering out indexes.
0127    */
0128   void disableFilteringOutIndexes();
0129 
0130   /**
0131    * Check if index filter is active.
0132    * @return true if indexes are filtered out
0133    */
0134   bool isFilteringOutIndexes() const;
0135 
0136   /**
0137    * Make filter changes active after adding indexes to be filtered out.
0138    */
0139   void applyFilteringOutIndexes();
0140 
0141   /**
0142    * Set filters for included and excluded folders.
0143    * @param includeFolders wildcard expressions for folders to be included
0144    * @param excludeFolders wildcard expressions for folders to be excluded
0145    */
0146   void setFolderFilters(const QStringList& includeFolders,
0147                         const QStringList& excludeFolders);
0148 
0149   /**
0150    * Get file information of model index.
0151    * @return file information
0152    */
0153   QFileInfo fileInfo(const QModelIndex& index) const;
0154 
0155   /**
0156    * Get file path of model index.
0157    * @return path to file or directory
0158    */
0159   QString filePath(const QModelIndex& index) const;
0160 
0161   /**
0162    * Get file name of model index.
0163    * @return name of file or directory
0164    */
0165   QString fileName(const QModelIndex& index) const;
0166 
0167   /**
0168    * Check if model index represents directory.
0169    * @return true if directory
0170    */
0171   bool isDir(const QModelIndex& index) const;
0172 
0173   /**
0174    * Delete file of index.
0175    * @return true if ok
0176    */
0177   bool remove(const QModelIndex& index) const;
0178 
0179   /**
0180    * Delete directory of index.
0181    * @return true if ok
0182    */
0183   bool rmdir(const QModelIndex& index) const;
0184 
0185   /**
0186    * Create a directory with @a name in the @a parent model index.
0187    * @return index of created directory.
0188    */
0189   QModelIndex mkdir(const QModelIndex& parent, const QString& name) const;
0190 
0191   /**
0192    * Rename file or directory of @a index to @a newName.
0193    * @return true if ok
0194    */
0195   bool rename(const QModelIndex& index, const QString& newName);
0196 
0197   /**
0198    * Get index for given path and column.
0199    * @param path path to file or directory
0200    * @param column model column
0201    * @return model index, invalid if not found.
0202    */
0203   QModelIndex index(const QString& path, int column = 0) const;
0204 
0205   using QSortFilterProxyModel::index;
0206 
0207   /**
0208    * Count items in model.
0209    * @param rootIndex index of root item
0210    * @param folderCount the folder count is returned here
0211    * @param fileCount the file count is returned here
0212    */
0213   void countItems(const QModelIndex& rootIndex,
0214                   int& folderCount, int& fileCount) const;
0215 
0216   /**
0217    * Check if any file has been modified.
0218    * @return true if at least one of the files in the model has been modified.
0219    */
0220   bool isModified() const { return m_numModifiedFiles > 0; }
0221 
0222   /**
0223    * Set index of item which shall be the only item which can be dragged.
0224    * This can be used to retain the selection while dragging a single item,
0225    * e.g. a picture to be embedded.
0226    * @param index index of single item which is draggable
0227    */
0228   void setExclusiveDraggableIndex(const QPersistentModelIndex& index) {
0229     m_exclusiveDraggableIndex = index;
0230   }
0231 
0232   /**
0233    * Get icon provider.
0234    * @return icon provider.
0235    */
0236   CoreTaggedFileIconProvider* getIconProvider() const;
0237 
0238   /**
0239    * Access to tagged file factories.
0240    * @return reference to tagged file factories.
0241    */
0242   static QList<ITaggedFileFactory*>& taggedFileFactories();
0243 
0244   /**
0245    * Get tagged file of model index.
0246    *
0247    * @param index model index
0248    *
0249    * @return tagged file, 0 is returned if the index does not contain a
0250    * TaggedFile or if has a TaggedFile which is null.
0251    */
0252   static TaggedFile* getTaggedFileOfIndex(const QModelIndex& index);
0253 
0254   /**
0255    * Get directory path if model index is of directory.
0256    *
0257    * @param index model index
0258    *
0259    * @return directory path, null if not directory
0260    */
0261   static QString getPathIfIndexOfDir(const QModelIndex& index);
0262 
0263   /**
0264    * Read tagged file with ID3v2.4.0.
0265    *
0266    * @param taggedFile tagged file
0267    *
0268    * @return tagged file (can be newly created tagged file).
0269    */
0270   static TaggedFile* readWithId3V24(TaggedFile* taggedFile);
0271 
0272   /**
0273    * Read tagged file with ID3v2.3.0.
0274    *
0275    * @param taggedFile tagged file
0276    *
0277    * @return tagged file (can be newly created tagged file).
0278    */
0279   static TaggedFile* readWithId3V23(TaggedFile* taggedFile);
0280 
0281   /**
0282    * Read file with ID3v2.4 if it has an ID3v2.4 or ID3v2.2 tag.
0283    * ID3v2.2 files are also read with ID3v2.4 because id3lib corrupts
0284    * images in ID3v2.2 tags.
0285    *
0286    * @param taggedFile tagged file
0287    *
0288    * @return tagged file (can be new TagLibFile).
0289    */
0290   static TaggedFile* readWithId3V24IfId3V24(TaggedFile* taggedFile);
0291 
0292   /**
0293    * Read tagged file with Ogg FLAC.
0294    *
0295    * @param taggedFile tagged file
0296    *
0297    * @return tagged file (can be newly created tagged file).
0298    */
0299   static TaggedFile* readWithOggFlac(TaggedFile* taggedFile);
0300 
0301   /**
0302    * Try to read Ogg file with invalid tag detail info as an Ogg FLAC file.
0303    *
0304    * @param taggedFile tagged file
0305    *
0306    * @return tagged file (can be new TagLibFile).
0307    */
0308   static TaggedFile* readWithOggFlacIfInvalidOgg(TaggedFile* taggedFile);
0309 
0310   /**
0311    * Call readTags() on tagged file.
0312    * Reread file with other metadata plugin if it is not supported by current
0313    * plugin.
0314    *
0315    * @param taggedFile tagged file
0316    *
0317    * @return tagged file (can be new TaggedFile).
0318    */
0319   static TaggedFile* readTagsFromTaggedFile(TaggedFile* taggedFile);
0320 
0321   /**
0322    * Create name-file pattern pairs for all supported types.
0323    * The order is the same as in createFilterString().
0324    *
0325    * @return pairs containing name, pattern, e.g. ("MP3", "*.mp3"), ...,
0326    * ("All Files", "*").
0327    */
0328   static QList<QPair<QString, QString> > createNameFilters();
0329 
0330 signals:
0331   /**
0332    * Emitted after directory loading when sorting is probably finished.
0333    * This signal is not accurate, it will be emitted 100 ms after
0334    * directoryLoaded() because it is not known when sorting is really finished.
0335    */
0336   void sortingFinished();
0337 
0338   /**
0339    * Emitted when the modification state of a file changes.
0340    * @param index model index
0341    * @param modified true if file is modified
0342    */
0343   void fileModificationChanged(const QModelIndex& index, bool modified);
0344 
0345   /**
0346    * Emitted when modification state changes.
0347    * @param modified true if any file has been modified
0348    * @see isModified()
0349    */
0350   void modifiedChanged(bool modified);
0351 
0352 protected slots:
0353   /**
0354    * Reset internal data of the model.
0355    * Is called from endResetModel().
0356    */
0357 #if QT_VERSION >= 0x060000
0358   void resetInternalData() override;
0359 #else
0360   void resetInternalData();
0361 #endif
0362 
0363 private slots:
0364   /**
0365    * Called when the source model emits fileModificationChanged().
0366    * @param index model index
0367    * @param modified true if file is modified
0368    */
0369   void onFileModificationChanged(const QModelIndex& srcIndex, bool modified);
0370 
0371   /**
0372    * Called when the source model emits directoryLoaded().
0373    */
0374   void onDirectoryLoaded();
0375 
0376   /**
0377    * Emit sortingFinished().
0378    */
0379   void emitSortingFinished();
0380 
0381   /**
0382    * Called when loading the directory starts.
0383    */
0384   void onStartLoading();
0385 
0386 protected:
0387   /**
0388    * Check if row should be included in model.
0389    *
0390    * @param srcRow source row
0391    * @param srcParent source parent
0392    *
0393    * @return true to include row.
0394    */
0395   bool filterAcceptsRow(int srcRow, const QModelIndex& srcParent) const override;
0396 
0397 private:
0398   /**
0399    * Check if a directory path passes the include folder filters.
0400    * @param dirPath absolute path to directory
0401    * @return true if path passes filters.
0402    */
0403   bool passesIncludeFolderFilters(const QString& dirPath) const;
0404 
0405   /**
0406    * Check if a directory path passes the exclude folder filters.
0407    * @param dirPath absolute path to directory
0408    * @return true if path passes filters.
0409    */
0410   bool passesExcludeFolderFilters(const QString& dirPath) const;
0411 
0412   QSet<QPersistentModelIndex> m_filteredOut;
0413   QPersistentModelIndex m_exclusiveDraggableIndex;
0414   QList<QRegularExpression> m_includeFolderFilters;
0415   QList<QRegularExpression> m_excludeFolderFilters;
0416   TaggedFileSystemModel* m_fsModel;
0417   QTimer* m_loadTimer;
0418   QTimer* m_sortTimer;
0419   QStringList m_extensions;
0420   unsigned int m_numModifiedFiles;
0421   bool m_isLoading;
0422 };