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 };