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

0001 /**
0002  * \file dirrenamer.h
0003  * Directory renamer.
0004  *
0005  * \b Project: Kid3
0006  * \author Urs Fleisch
0007  * \date 23 Jul 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 <QObject>
0030 #include <QString>
0031 #include <QPersistentModelIndex>
0032 #include "frame.h"
0033 #include "iabortable.h"
0034 #include "kid3api.h"
0035 
0036 class TaggedFile;
0037 class DirNameFormatReplacerContext;
0038 
0039 /**
0040  * Directory renamer.
0041  */
0042 class KID3_CORE_EXPORT DirRenamer : public QObject, public IAbortable {
0043   Q_OBJECT
0044 public:
0045   /**
0046    * Constructor.
0047    * @param parent parent object
0048    */
0049   explicit DirRenamer(QObject* parent = nullptr);
0050 
0051   /**
0052    * Destructor.
0053    */
0054   ~DirRenamer() override;
0055 
0056   /**
0057    * Check if operation is aborted.
0058    *
0059    * @return true if aborted.
0060    */
0061   bool isAborted() const override;
0062 
0063   /**
0064    * Clear state which is reported by isAborted().
0065    */
0066   void clearAborted() override;
0067 
0068   /**
0069    * Set version of tags used to get rename information.
0070    * @param tagVersion tag version
0071    */
0072   void setTagVersion(Frame::TagVersion tagVersion) {
0073     m_tagVersion = tagVersion;
0074   }
0075 
0076   /**
0077    * Set action to be performed.
0078    * @param create true for create action
0079    */
0080   void setAction(bool create) { m_actionCreate = create; }
0081 
0082   /**
0083    * Set format to generate directory names.
0084    * @param format format
0085    */
0086   void setFormat(const QString& format) { m_format = format; }
0087 
0088   /**
0089    * Generate new directory name according to current settings.
0090    *
0091    * @param taggedFile file to get information from
0092    * @param olddir pointer to QString to place old directory name into,
0093    *               NULL if not used
0094    *
0095    * @return new directory name.
0096    */
0097   QString generateNewDirname(TaggedFile* taggedFile, QString* olddir);
0098 
0099   /**
0100    * Clear the rename actions.
0101    * This method has to be called before scheduling new actions.
0102    */
0103   void clearActions();
0104 
0105   /**
0106    * Schedule the actions necessary to rename the directory containing a file.
0107    *
0108    * @param taggedFile file in directory
0109    */
0110   void scheduleAction(TaggedFile* taggedFile);
0111 
0112   /**
0113    * Terminate scheduling of actions.
0114    * Has to be called after completing the scheduleAction() calls. It will
0115    * postprocess the actions when aggregate functions are used.
0116    */
0117   void endScheduleActions();
0118 
0119   /**
0120    * Perform the scheduled rename actions.
0121    *
0122    * @param errorMsg if not 0 and an error occurred, a message is appended here,
0123    *                 otherwise it is not touched
0124    */
0125   void performActions(QString* errorMsg);
0126 
0127   /**
0128    * Set directory name.
0129    * This should be done before calling performActions(), so that the directory
0130    * name is changed when the application directory is renamed.
0131    *
0132    * @param dirName directory name
0133    */
0134   void setDirName(const QString& dirName) { m_dirName = dirName; }
0135 
0136   /**
0137    * Get directory name.
0138    * The directory name should be initialized with the value of
0139    * Kid3Application::getDirName() before performActions() is started and will
0140    * be updated if it is renamed while performing the actions. If it is
0141    * different from the application directory name after performActions(), the
0142    * new directory should be opened.
0143    *
0144    * @return directory.
0145    */
0146   QString getDirName() const { return m_dirName; }
0147 
0148 public slots:
0149   /**
0150    * Abort operation.
0151    */
0152   void abort() override;
0153 
0154 signals:
0155   /**
0156    * Is emitted after an action has been scheduled.
0157    *
0158    * @param actionStrs description of action
0159    */
0160   void actionScheduled(const QStringList& actionStrs);
0161 
0162 private:
0163   /**
0164    * An action performed while renaming a directory.
0165    */
0166   class RenameAction {
0167   public:
0168     /** Action type. */
0169     enum Type {
0170       CreateDirectory,
0171       RenameDirectory,
0172       RenameFile,
0173       ReportError,
0174       NumTypes
0175     };
0176 
0177     /**
0178      * Constructor.
0179      * @param type type of action
0180      * @param src  source file or directory name
0181      * @param dest destination file or directory name
0182      * @param index model index of item to rename
0183      */
0184     RenameAction(Type type, const QString& src, const QString& dest,
0185                  const QPersistentModelIndex& index)
0186       : m_type(type), m_src(src), m_dest(dest), m_index(index) {}
0187 
0188     /**
0189      * Constructor.
0190      */
0191     RenameAction() : m_type(ReportError) {}
0192 
0193     /**
0194      * Test for equality.
0195      * @param rhs right hand side
0196      * @return true if equal.
0197      */
0198     bool operator==(const RenameAction& rhs) const {
0199       return m_type == rhs.m_type && m_src == rhs.m_src && m_dest == rhs.m_dest;
0200     }
0201 
0202     Type m_type;    /**< type of action */
0203     QString m_src;  /**< source file or directory name */
0204     QString m_dest; /**< destination file or directory name */
0205     QPersistentModelIndex m_index; /**< model index of item to rename */
0206   };
0207 
0208   /** List of rename actions. */
0209   typedef QList<RenameAction> RenameActionList;
0210 
0211   /**
0212    * Create a directory if it does not exist.
0213    *
0214    * @param dir      directory path
0215    * @param index    model index of item to rename
0216    * @param errorMsg if not NULL and an error occurred, a message is appended here,
0217    *                 otherwise it is not touched
0218    *
0219    * @return true if directory exists or was created successfully.
0220    */
0221   bool createDirectory(const QString& dir, const QPersistentModelIndex& index,
0222                        QString* errorMsg) const;
0223 
0224   /**
0225    * Rename a directory.
0226    *
0227    * @param olddir   old directory name
0228    * @param newdir   new directory name
0229    * @param errorMsg if not NULL and an error occurred, a message is
0230    *                 appended here, otherwise it is not touched
0231    * @param index    model index of item to rename
0232    *
0233    * @return true if rename successful.
0234    */
0235   bool renameDirectory(
0236     const QString& olddir, const QString& newdir,
0237     const QPersistentModelIndex& index, QString *errorMsg) const;
0238 
0239   /**
0240    * Rename a file.
0241    *
0242    * @param oldfn    old file name
0243    * @param newfn    new file name
0244    * @param errorMsg if not NULL and an error occurred, a message is
0245    *                 appended here, otherwise it is not touched
0246    * @param index    model index of item to rename
0247    *
0248    * @return true if rename successful or newfn already exists.
0249    */
0250   bool renameFile(const QString& oldfn, const QString& newfn,
0251                   const QPersistentModelIndex& index, QString* errorMsg) const;
0252 
0253   /**
0254    * Add a rename action.
0255    *
0256    * @param type type of action
0257    * @param src  source file or directory name
0258    * @param dest destination file or directory name
0259    * @param index model index of item to rename
0260    */
0261   void addAction(RenameAction::Type type, const QString& src,
0262                  const QString& dest,
0263                  const QPersistentModelIndex& index = QPersistentModelIndex());
0264 
0265   /**
0266    * Add a rename action.
0267    *
0268    * @param type type of action
0269    * @param dest destination file or directory name
0270    */
0271   void addAction(RenameAction::Type type, const QString& dest);
0272 
0273   /**
0274    * Check if there is already an action scheduled for this source.
0275    *
0276    * @return true if a rename action for the source exists.
0277    */
0278   bool actionHasSource(const QString& src) const;
0279 
0280   /**
0281    * Check if there is already an action scheduled for this destination.
0282    *
0283    * @return true if a rename or create action for the destination exists.
0284    */
0285   bool actionHasDestination(const QString& dest) const;
0286 
0287   /**
0288    * Replace directory name if there is already a rename action.
0289    *
0290    * @param src directory name, will be replaced if there is a rename action
0291    */
0292   void replaceIfAlreadyRenamed(QString& src) const;
0293 
0294   /**
0295    * Get description of an actions to be performed.
0296    * @return (action, [src,] dst) list describing the action to be
0297    * performed.
0298    */
0299   QStringList describeAction(const RenameAction& action) const;
0300 
0301   DirNameFormatReplacerContext* m_fmtContext;
0302   RenameActionList m_actions;
0303   Frame::TagVersion m_tagVersion;
0304   QString m_format;
0305   QString m_dirName;
0306   bool m_aborted;
0307   bool m_actionCreate;
0308 };