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

0001 /**
0002  * \file taggedfileselection.h
0003  * Information about selected tagged files.
0004  *
0005  * \b Project: Kid3
0006  * \author Urs Fleisch
0007  * \date 19 Jun 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 "frame.h"
0031 #include "kid3api.h"
0032 
0033 class FrameTableModel;
0034 class TaggedFile;
0035 class TaggedFileSelectionTagContext;
0036 
0037 /**
0038  * Information about selected tagged files.
0039  */
0040 class KID3_CORE_EXPORT TaggedFileSelection : public QObject {
0041   Q_OBJECT
0042   /** true if no tagged file is selected. */
0043   Q_PROPERTY(bool empty READ isEmpty NOTIFY emptyChanged)
0044   /** true if exactly one file is selected. */
0045   Q_PROPERTY(bool singleFileSelected READ isSingleFileSelected
0046              NOTIFY singleFileSelectedChanged)
0047   /** true if single file selected and filename was changed. */
0048   Q_PROPERTY(bool fileNameChanged READ isFilenameChanged
0049              NOTIFY singleFileChanged)
0050   /** File name if single file selected, else null string. */
0051   Q_PROPERTY(QString fileName READ getFilename WRITE setFilename
0052              NOTIFY singleFileChanged)
0053   /** Absolute file path if single file selected, else null string. */
0054   Q_PROPERTY(QString filePath READ getFilePath NOTIFY singleFileChanged)
0055   /** Detail information if single file selected, else null string. */
0056   Q_PROPERTY(QString detailInfo READ getDetailInfo NOTIFY singleFileChanged)
0057   /**
0058    * Format of tag 1 if single file selected, else null string.
0059    * @deprecated For compatibility with old scripts,
0060    * use tag(Frame.Tag_1).tagFormat instead.
0061    */
0062   Q_PROPERTY(QString tagFormatV1 READ getTagFormatV1 NOTIFY singleFileChanged)
0063   /**
0064    * Format of tag 2 if single file selected, else null string.
0065    * @deprecated For compatibility with old scripts,
0066    * use tag(Frame.Tag_2).tagFormat instead.
0067    */
0068   Q_PROPERTY(QString tagFormatV2 READ getTagFormatV2 NOTIFY singleFileChanged)
0069   /** Picture data, empty if not available.*/
0070   Q_PROPERTY(QByteArray picture READ getPicture NOTIFY singleFileChanged)
0071 public:
0072   /**
0073    * Constructor.
0074    * @param framesModel frame table models for all tags, Frame::Tag_NumValues
0075    * elements
0076    * @param parent parent object
0077    */
0078   TaggedFileSelection(FrameTableModel* framesModel[], QObject* parent = nullptr);
0079 
0080   /**
0081    * Destructor.
0082    */
0083   ~TaggedFileSelection() override = default;
0084 
0085   /**
0086    * Start adding tagged files to selection.
0087    * Has to be called before adding the first file using addTaggedFile().
0088    */
0089   void beginAddTaggedFiles();
0090 
0091   /**
0092    * End adding tagged files to selection.
0093    * Has to be called after adding the last file using addTaggedFile().
0094    */
0095   void endAddTaggedFiles();
0096 
0097   /**
0098    * Add a tagged file to the selection.
0099    * @param taggedFile tagged file
0100    */
0101   void addTaggedFile(TaggedFile* taggedFile);
0102 
0103   /**
0104    * Check if a single file is selected.
0105    * @return if a single file is selected, this tagged file, else 0.
0106    */
0107   TaggedFile* singleFile() const { return m_state.singleFile(); }
0108 
0109   /**
0110    * Check if selection is empty.
0111    * @return true if no tagged file is selected.
0112    */
0113   bool isEmpty() const { return m_state.isEmpty(); }
0114 
0115   /**
0116    * Check if any of the selected files has a tag.
0117    * @param tagNr tag number
0118    * @return true if any of the selected files has a tag.
0119    */
0120   bool hasTag(Frame::TagNumber tagNr) const { return m_state.hasTag(tagNr); }
0121 
0122   /**
0123    * Check if a single file is selected.
0124    * @return true if exactly one file is selected.
0125    */
0126   bool isSingleFileSelected() const { return m_state.isSingleFileSelected(); }
0127 
0128   /**
0129    * Check if tag is used.
0130    * @param tagNr tag number
0131    * @return true if any selected file supports tag.
0132    */
0133   bool isTagUsed(Frame::TagNumber tagNr) const { return m_state.isTagUsed(tagNr); }
0134 
0135   /**
0136    * Get file name.
0137    * @return file name if single file selected, else null string.
0138    */
0139   QString getFilename() const;
0140 
0141   /**
0142    * Set file name if single file selected.
0143    * @param fn file name
0144    */
0145   void setFilename(const QString& fn);
0146 
0147   /**
0148    * Get file path.
0149    * @return absolute file path if single file selected, else null string.
0150    */
0151   QString getFilePath() const;
0152 
0153   /**
0154    * Get string representation of detail information.
0155    * @return information summary as string if single file else null string.
0156    */
0157   QString getDetailInfo() const;
0158 
0159   /**
0160    * Get the format of tag.
0161    * @param tagNr tag number
0162    * @return string describing format of tag 2 if single file selected,
0163    * else null string.
0164    */
0165   QString getTagFormat(Frame::TagNumber tagNr) const;
0166 
0167   /**
0168    * Get context for tag.
0169    * @param tagNr tag number
0170    * @return tag context.
0171    */
0172   Q_INVOKABLE TaggedFileSelectionTagContext* tag(Frame::TagNumber tagNr) const {
0173     return m_tagContext[tagNr];
0174   }
0175 
0176   /**
0177    * Check if filename is changed.
0178    * @return true if single file selected and filename was changed.
0179    */
0180   bool isFilenameChanged() const;
0181 
0182   /**
0183    * Get data from a picture frame.
0184    * @return picture data, empty if not available.
0185    */
0186   QByteArray getPicture() const;
0187 
0188   /**
0189    * Replace codes in format string with information from the tags.
0190    * @param tagVersion tag version
0191    * @param fmt format string
0192    * @return string with format codes replaced.
0193    */
0194   Q_INVOKABLE QString formatString(Frame::TagVersion tagVersion,
0195                                    const QString& fmt) const;
0196 
0197   /**
0198    * Select changed frames in the tables if multiple files are selected.
0199    */
0200   void selectChangedFrames();
0201 
0202   /**
0203    * Clear frame collection in frame models not used by current selection.
0204    */
0205   void clearUnusedFrames();
0206 
0207 signals:
0208   /**
0209    * Emitted when empty changed.
0210    */
0211   void emptyChanged(bool empty);
0212 
0213   /**
0214    * Emitted when singleFileSelected changed.
0215    */
0216   void singleFileSelectedChanged(bool single);
0217 
0218   /**
0219    * Emitted when the single file may have changed.
0220    * This is not accurate, because the file itself may change without notice.
0221    * Therefore this signal is emitted at the end of endAddTaggedFiles() unless
0222    * no single file exists in the current and the last state.
0223    */
0224   void singleFileChanged();
0225 
0226   /**
0227    * Emitted when the file name is modified.
0228    */
0229   void fileNameModified();
0230 
0231 private:
0232   struct State {
0233     State() : m_singleFile(nullptr), m_fileCount(0) {
0234       FOR_ALL_TAGS(tagNr) {
0235         m_tagSupportedCount[tagNr] = 0;
0236         m_hasTag[tagNr] = false;
0237       }
0238     }
0239 
0240     TaggedFile* singleFile() const { return m_singleFile; }
0241     bool isEmpty() const { return m_fileCount == 0; }
0242     bool hasTag(Frame::TagNumber tagNr) const { return m_hasTag[tagNr]; }
0243     bool isSingleFileSelected() const { return m_singleFile != nullptr; }
0244     bool isTagUsed(Frame::TagNumber tagNr) const { return m_tagSupportedCount[tagNr] > 0; }
0245 
0246     /** If a single file is selected, this tagged file, else 0 */
0247     TaggedFile* m_singleFile;
0248     /** Number of selected files */
0249     int m_fileCount;
0250     /** Number of selected files which support tag 1 */
0251     int m_tagSupportedCount[Frame::Tag_NumValues];
0252     /** true if any of the selected files has a tag */
0253     bool m_hasTag[Frame::Tag_NumValues];
0254   };
0255 
0256   QString getTagFormatV1() const;
0257   QString getTagFormatV2() const;
0258 
0259   FrameTableModel* m_framesModel[Frame::Tag_NumValues];
0260   TaggedFileSelectionTagContext* m_tagContext[Frame::Tag_NumValues];
0261   State m_state;
0262   State m_lastState;
0263 };
0264 
0265 /**
0266  * Facade to have a uniform interface for different tags.
0267  */
0268 class KID3_CORE_EXPORT TaggedFileSelectionTagContext : public QObject {
0269   Q_OBJECT
0270   /** true if any of the selected files has a tag. */
0271   Q_PROPERTY(bool hasTag READ hasTag NOTIFY hasTagChanged)
0272   /** true if any selected file supports the tag. */
0273   Q_PROPERTY(bool tagUsed READ isTagUsed NOTIFY tagUsedChanged)
0274   /** Format of tag if single file selected, else null string. */
0275   Q_PROPERTY(QString tagFormat READ tagFormat NOTIFY tagFormatChanged)
0276 public:
0277   /**
0278    * Constructor.
0279    * @param selection tagged file selection
0280    * @param tagNr tag number
0281    */
0282   TaggedFileSelectionTagContext(TaggedFileSelection* selection,
0283                                 Frame::TagNumber tagNr)
0284     : QObject(selection), m_selection(selection), m_tagNr(tagNr),
0285       m_tagVersion(Frame::tagVersionFromNumber(tagNr)) {
0286   }
0287 
0288 signals:
0289   /**
0290    * Emitted when hasTag changed.
0291    */
0292   void hasTagChanged(bool hasTag);
0293 
0294   /**
0295    * Emitted when tagUsed changed.
0296    */
0297   void tagUsedChanged(bool used);
0298 
0299   /**
0300    * Emitted when tagFormat may have changed.
0301    */
0302   void tagFormatChanged();
0303 
0304 private:
0305   friend class TaggedFileSelection;
0306 
0307   bool hasTag() const { return m_selection->hasTag(m_tagNr); }
0308   bool isTagUsed() const { return m_selection->isTagUsed(m_tagNr); }
0309   QString tagFormat() const { return m_selection->getTagFormat(m_tagNr); }
0310 
0311   TaggedFileSelection* m_selection;
0312   const Frame::TagNumber m_tagNr;
0313   const Frame::TagVersion m_tagVersion;
0314 };