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

0001 /**
0002  * \file tagsearcher.h
0003  * Search for strings in tags.
0004  *
0005  * \b Project: Kid3
0006  * \author Urs Fleisch
0007  * \date 08 Feb 2014
0008  *
0009  * Copyright (C) 2014-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 <QObject>
0030 #include <QString>
0031 #include <QRegularExpression>
0032 #include <QPersistentModelIndex>
0033 #include "iabortable.h"
0034 #include "frame.h"
0035 #include "kid3api.h"
0036 
0037 class FileProxyModel;
0038 class BiDirFileProxyModelIterator;
0039 class TaggedFile;
0040 
0041 /**
0042  * Searcher for strings in tags.
0043  */
0044 class KID3_CORE_EXPORT TagSearcher : public QObject, public IAbortable {
0045   Q_OBJECT
0046 public:
0047   /**
0048    * Position of found string.
0049    */
0050   class KID3_CORE_EXPORT Position {
0051   public:
0052     /** Part of file where string was found. */
0053     enum Part {
0054       FileName, /**< Found in file name */
0055       Tag1,     /**< Found in tag 1 */
0056       Tag2,     /**< Found in tag 2 */
0057       Tag3      /**< Found in tag 3 */
0058     };
0059 
0060     /**
0061      * Constructor.
0062      */
0063     Position();
0064 
0065     /**
0066      * Clear to invalid position.
0067      */
0068     void clear();
0069 
0070     /**
0071      * Check if position is valid.
0072      * @return true if valid, false if not found.
0073      */
0074     bool isValid() const;
0075 
0076     /**
0077      * Get model index of tagged file.
0078      * @return model index.
0079      */
0080     QPersistentModelIndex getFileIndex() const { return m_fileIndex; }
0081 
0082     /**
0083      * Get part in file where string was found.
0084      * @return part.
0085      */
0086     Part getPart() const { return m_part; }
0087 
0088     /**
0089      * Get index of frame where string was found.
0090      * @return number of frame in frame list (tag 1 or tag 2),
0091      * -1 if string was not found in a tag.
0092      */
0093     int getFrameIndex() const { return m_frameIndex; }
0094 
0095     /**
0096      * Get name of frame where string was found.
0097      * @return name of frame, empty if string was not found in a tag.
0098      */
0099     QString getFrameName() const { return m_frameName; }
0100 
0101     /**
0102      * Get starting position of match.
0103      * @return starting position of match in tag frame value or file name,
0104      * -1 if not found.
0105      */
0106     int getMatchedPos() const { return m_matchedPos; }
0107 
0108     /**
0109      * Get length of match.
0110      * @return number of matched characters, -1 if not found.
0111      */
0112     int getMatchedLength() const { return m_matchedLength; }
0113 
0114     /**
0115      * Convert part in file where string was found to tag number.
0116      * @return tag number, Frame::Tag_NumValues if FileName.
0117      */
0118     static Frame::TagNumber partToTagNumber(Part part) {
0119       return part == FileName
0120           ? Frame::Tag_NumValues : static_cast<Frame::TagNumber>(part - 1);
0121     }
0122 
0123     /**
0124      * Convert tag number to part in file where string was found.
0125      * @return part, FileName if Frame::Tag_NumValues.
0126      */
0127     static Part tagNumberToPart(Frame::TagNumber tagNr) {
0128       return tagNr < Frame::Tag_NumValues
0129           ? static_cast<Part>(tagNr + 1) : FileName;
0130     }
0131 
0132   private:
0133     friend class TagSearcher;
0134 
0135     QString m_frameName;
0136     QPersistentModelIndex m_fileIndex;
0137     Part m_part;
0138     int m_frameIndex;
0139     int m_matchedPos;
0140     int m_matchedLength;
0141   };
0142 
0143   /** Flags controlling search */
0144   enum SearchFlag {
0145     CaseSensitive = 1 << 0, /**< is case sensitive */
0146     Backwards     = 1 << 1, /**< search backwards */
0147     RegExp        = 1 << 2, /**< use regular expressions */
0148     AllFrames     = 1 << 3  /**< search in all frames */
0149   };
0150   Q_DECLARE_FLAGS(SearchFlags, SearchFlag)
0151 
0152   /**
0153    * Search parameters.
0154    */
0155   class KID3_CORE_EXPORT Parameters {
0156   public:
0157     /**
0158      * Constructor.
0159      */
0160     Parameters() : m_frameMask(0), m_flags(AllFrames) {}
0161 
0162     /**
0163      * Get search text.
0164      * @return text.
0165      */
0166     QString getSearchText() const { return m_searchText; }
0167 
0168     /**
0169      * Set search text.
0170      * @param text search text
0171      */
0172     void setSearchText(const QString& text) { m_searchText = text; }
0173 
0174     /**
0175      * Get search text.
0176      * @return text.
0177      */
0178     QString getReplaceText() const { return m_replaceText; }
0179 
0180     /**
0181      * Set search text.
0182      * @param text search text
0183      */
0184     void setReplaceText(const QString& text) { m_replaceText = text; }
0185 
0186     /**
0187      * Get search flags.
0188      * @return flags.
0189      */
0190     SearchFlags getFlags() const { return m_flags; }
0191 
0192     /**
0193      * Set search flags.
0194      * @param flags flags
0195      */
0196     void setFlags(SearchFlags flags) { m_flags = flags; }
0197 
0198     /**
0199      * Get mask with bits set for frames to be searched.
0200      * @return mask containing bits corresponding to Frame::Type and
0201      * additionally TrackDataModel::FT_FileName.
0202      */
0203     quint64 getFrameMask() const { return m_frameMask; }
0204 
0205     /**
0206      * Set mask with bits set for frames to be searched.
0207      * @param frameMask mask with Frame::Type and TrackDataModel::FT_FileName
0208      * bits
0209      */
0210     void setFrameMask(quint64 frameMask) { m_frameMask = frameMask; }
0211 
0212     /**
0213      * Get parameters as variant list.
0214      * @return variant list containing search text, replace text, flags,
0215      * frameMask.
0216      */
0217     QVariantList toVariantList() const;
0218 
0219     /**
0220      * Set parameters from variant list.
0221      * @param lst variant list containing search text, replace text, flags,
0222      * frameMask
0223      */
0224     void fromVariantList(const QVariantList& lst);
0225 
0226   private:
0227     quint64 m_frameMask;
0228     QString m_searchText;
0229     QString m_replaceText;
0230     SearchFlags m_flags;
0231   };
0232 
0233 
0234   /**
0235    * Constructor.
0236    * @param parent parent object
0237    */
0238   explicit TagSearcher(QObject* parent = nullptr);
0239 
0240   /**
0241    * Destructor.
0242    */
0243   ~TagSearcher() override = default;
0244 
0245   /**
0246    * Clear abort flag.
0247    */
0248   void clearAborted() override;
0249 
0250   /**
0251    * Check if dialog was aborted.
0252    * @return true if aborted.
0253    */
0254   bool isAborted() const override;
0255 
0256   /**
0257    * Set model of files to be searched.
0258    * @param model file proxy model
0259    */
0260   void setModel(FileProxyModel* model);
0261 
0262   /**
0263    * Set root index of directory to search.
0264    * @param index root index of directory
0265    */
0266   void setRootIndex(const QPersistentModelIndex& index);
0267 
0268   /**
0269    * Set index of file to start search.
0270    * @param index index of file where search is started
0271    */
0272   void setStartIndex(const QPersistentModelIndex& index);
0273 
0274   /**
0275    * Get position of current match.
0276    * @return position.
0277    */
0278   const Position& getPosition() const { return m_currentPosition; }
0279 
0280 public slots:
0281   /**
0282    * Stop current search, so that the next call to findNext() will use the
0283    * index set with setStartIndex().
0284    */
0285   void abort() override;
0286 
0287   /**
0288    * Find next occurrence of string.
0289    * @param params search parameters
0290    */
0291   void find(const TagSearcher::Parameters& params);
0292 
0293   /**
0294    * Replace found text.
0295    * @param params search parameters
0296    * @return true if replaced.
0297    */
0298   void replace(const TagSearcher::Parameters& params);
0299 
0300   /**
0301    * Replace all occurrences.
0302    * @param params search parameters
0303    */
0304   void replaceAll(const TagSearcher::Parameters& params);
0305 
0306 signals:
0307   /**
0308    * Emitted when a match is found.
0309    * The position of the match is available via getPosition(), it is
0310    * invalid when the end is reached.
0311    */
0312   void textFound();
0313 
0314   /**
0315    * Emitted when a text is replaced.
0316    * The position of the replaced text is available via getPosition().
0317    */
0318   void textReplaced();
0319 
0320   /**
0321    * Progress message while searching.
0322    * @param msg message
0323    */
0324   void progress(const QString& msg);
0325 
0326 private slots:
0327   void searchNextFile(const QPersistentModelIndex& index);
0328   void replaceThenFindNext();
0329 
0330 private:
0331   void setParameters(const Parameters& params);
0332   void findNext(int advanceChars);
0333   void replaceNext();
0334   void continueSearch(int advanceChars);
0335   bool searchInFile(TaggedFile* taggedFile, Position* pos,
0336                     int advanceChars) const;
0337   bool searchInFrames(const FrameCollection& frames,
0338                       Position::Part part, Position* pos,
0339                       int advanceChars) const;
0340   int findInString(const QString& str, int& idx) const;
0341   void replaceString(QString& str) const;
0342   QString getLocationString(const TaggedFile* taggedFile) const;
0343 
0344   FileProxyModel* m_fileProxyModel;
0345   BiDirFileProxyModelIterator* m_iterator;
0346   QPersistentModelIndex m_startIndex;
0347   Position m_currentPosition;
0348   Parameters m_params;
0349   QRegularExpression m_regExp;
0350   bool m_aborted;
0351   bool m_started;
0352 };