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 #include "taggedfileselection.h"
0028 #include "taggedfile.h"
0029 #include "trackdata.h"
0030 #include "frametablemodel.h"
0031 #include "fileproxymodel.h"
0032 #include "pictureframe.h"
0033 #include "guiconfig.h"
0034 #include "tagconfig.h"
0035 #include "fileconfig.h"
0036 
0037 /**
0038  * Constructor.
0039  * @param framesModel frame table models for all tags, Frame::Tag_NumValues
0040  * elements
0041  * @param parent parent object
0042  */
0043 TaggedFileSelection::TaggedFileSelection(
0044     FrameTableModel* framesModel[], QObject* parent) : QObject(parent)
0045 {
0046   FOR_ALL_TAGS(tagNr) {
0047     m_framesModel[tagNr] = framesModel[tagNr];
0048     m_tagContext[tagNr] = new TaggedFileSelectionTagContext(this, tagNr);
0049   }
0050   setObjectName(QLatin1String("TaggedFileSelection"));
0051 }
0052 
0053 /**
0054  * Start adding tagged files to selection.
0055  * Has to be called before adding the first file using addTaggedFile().
0056  */
0057 void TaggedFileSelection::beginAddTaggedFiles()
0058 {
0059   m_lastState = m_state;
0060   m_state.m_singleFile = nullptr;
0061   m_state.m_fileCount = 0;
0062   FOR_ALL_TAGS(tagNr) {
0063     m_state.m_tagSupportedCount[tagNr] = 0;
0064     m_state.m_hasTag[tagNr] = false;
0065     m_framesModel[tagNr]->beginFilterDifferent();
0066   }
0067 }
0068 
0069 /**
0070  * End adding tagged files to selection.
0071  * Has to be called after adding the last file using addTaggedFile().
0072  */
0073 void TaggedFileSelection::endAddTaggedFiles()
0074 {
0075   FOR_ALL_TAGS(tagNr) {
0076     m_framesModel[tagNr]->setAllCheckStates(
0077           m_state.m_tagSupportedCount[tagNr] == 1);
0078     m_framesModel[tagNr]->endFilterDifferent();
0079   }
0080   if (GuiConfig::instance().autoHideTags()) {
0081     // If a tag is supposed to be absent, make sure that there is really no
0082     // unsaved data in the tag.
0083     FOR_ALL_TAGS(tagNr) {
0084       if (!m_state.m_hasTag[tagNr] &&
0085           (m_state.m_tagSupportedCount[tagNr] > 0 ||
0086            m_state.m_fileCount == 0)) {
0087         const FrameCollection& frames = m_framesModel[tagNr]->frames();
0088         for (auto it = frames.cbegin(); it != frames.cend(); ++it) {
0089           if (!it->getValue().isEmpty()) {
0090             m_state.m_hasTag[tagNr] = true;
0091             break;
0092           }
0093         }
0094       }
0095     }
0096   }
0097   FOR_ALL_TAGS(tagNr) {
0098     if (TagConfig::instance().markTruncations()) {
0099       m_framesModel[tagNr]->markRows(m_state.m_singleFile
0100                                ? m_state.m_singleFile->getTruncationFlags(tagNr)
0101                                : 0);
0102     }
0103     if (FileConfig::instance().markChanges()) {
0104       m_framesModel[tagNr]->markChangedFrames(m_state.m_singleFile
0105                                ? m_state.m_singleFile->getChangedFrames(tagNr)
0106                                : QList<Frame::ExtendedType>());
0107     }
0108     if (m_state.hasTag(tagNr) != m_lastState.hasTag(tagNr)) {
0109       emit m_tagContext[tagNr]->hasTagChanged(m_state.hasTag(tagNr));
0110     }
0111     if (m_state.isTagUsed(tagNr) != m_lastState.isTagUsed(tagNr)) {
0112       emit m_tagContext[tagNr]->tagUsedChanged(m_state.isTagUsed(tagNr));
0113     }
0114   }
0115 
0116   if (m_state.isEmpty() != m_lastState.isEmpty()) {
0117     emit emptyChanged(m_state.isEmpty());
0118   }
0119   if (m_state.isSingleFileSelected() != m_lastState.isSingleFileSelected()) {
0120     emit singleFileSelectedChanged(m_state.isSingleFileSelected());
0121   }
0122   if (m_state.isSingleFileSelected() || m_lastState.isSingleFileSelected()) {
0123     // The properties depending on the single file may have changed.
0124     emit singleFileChanged();
0125     FOR_ALL_TAGS(tagNr) {
0126       emit m_tagContext[tagNr]->tagFormatChanged();
0127     }
0128   }
0129 }
0130 
0131 /**
0132  * Add a tagged file to the selection.
0133  * @param taggedFile tagged file
0134  */
0135 void TaggedFileSelection::addTaggedFile(TaggedFile* taggedFile)
0136 {
0137   taggedFile = FileProxyModel::readTagsFromTaggedFile(taggedFile);
0138 
0139   FOR_ALL_TAGS(tagNr) {
0140     if (taggedFile->isTagSupported(tagNr)) {
0141       if (m_state.m_tagSupportedCount[tagNr] == 0) {
0142         FrameCollection frames;
0143         taggedFile->getAllFrames(tagNr, frames);
0144         m_framesModel[tagNr]->transferFrames(frames);
0145       } else {
0146         FrameCollection fileFrames;
0147         taggedFile->getAllFrames(tagNr, fileFrames);
0148         m_framesModel[tagNr]->filterDifferent(fileFrames);
0149       }
0150       ++m_state.m_tagSupportedCount[tagNr];
0151     }
0152   }
0153   m_state.m_singleFile = m_state.m_fileCount == 0 ? taggedFile : nullptr;
0154   ++m_state.m_fileCount;
0155 
0156   FOR_ALL_TAGS(tagNr) {
0157     m_state.m_hasTag[tagNr] =
0158         m_state.m_hasTag[tagNr] || taggedFile->hasTag(tagNr);
0159   }
0160 }
0161 
0162 /**
0163  * Get file name.
0164  * @return file name if single file selected, else null string.
0165  */
0166 QString TaggedFileSelection::getFilename() const
0167 {
0168   return m_state.m_singleFile ? m_state.m_singleFile->getFilename() : QString();
0169 }
0170 
0171 /**
0172  * Set file name if single file selected.
0173  * @param fn file name
0174  */
0175 void TaggedFileSelection::setFilename(const QString& fn)
0176 {
0177   if (m_state.m_singleFile && !fn.isEmpty() &&
0178       m_state.m_singleFile->getFilename() != fn) {
0179     m_state.m_singleFile->setFilename(fn);
0180     emit fileNameModified();
0181   }
0182 }
0183 
0184 /**
0185  * Get file path.
0186  * @return absolute file path if single file selected, else null string.
0187  */
0188 QString TaggedFileSelection::getFilePath() const
0189 {
0190   return m_state.m_singleFile
0191       ? m_state.m_singleFile->getAbsFilename() : QString();
0192 }
0193 
0194 /**
0195  * Get string representation of detail information.
0196  * @return information summary as string if single file else null string.
0197  */
0198 QString TaggedFileSelection::getDetailInfo() const
0199 {
0200   TaggedFile::DetailInfo info;
0201   if (m_state.m_singleFile) {
0202     m_state.m_singleFile->getDetailInfo(info);
0203   }
0204   return info.toString();
0205 }
0206 
0207 /**
0208  * Get the format of tag.
0209  * @param tagNr tag number
0210  * @return string describing format of tag 2 if single file selected,
0211  * else null string.
0212  */
0213 QString TaggedFileSelection::getTagFormat(Frame::TagNumber tagNr) const
0214 {
0215   return m_state.m_singleFile ? m_state.m_singleFile->getTagFormat(tagNr)
0216                               : QString();
0217 }
0218 
0219 /**
0220  * Get the format of tag 1.
0221  * @return string describing format of tag 1 if single file selected,
0222  * else null string.
0223  * @deprecated Use tag(Frame::Tag_1)->tagFormat() instead.
0224  */
0225 QString TaggedFileSelection::getTagFormatV1() const
0226 {
0227   return m_tagContext[Frame::Tag_1]->tagFormat();
0228 }
0229 
0230 /**
0231  * Get the format of tag 2.
0232  * @return string describing format of tag 2 if single file selected,
0233  * else null string.
0234  * @deprecated Use tag(Frame::Tag_2)->tagFormat() instead.
0235  */
0236 QString TaggedFileSelection::getTagFormatV2() const
0237 {
0238   return m_tagContext[Frame::Tag_2]->tagFormat();
0239 }
0240 
0241 /**
0242  * Check if filename is changed.
0243  * @return true if single file selected and filename was changed.
0244  */
0245 bool TaggedFileSelection::isFilenameChanged() const
0246 {
0247   return m_state.m_singleFile && m_state.m_singleFile->isFilenameChanged();
0248 }
0249 
0250 /**
0251  * Get data from a picture frame.
0252  * @return picture data, empty if not available.
0253  */
0254 QByteArray TaggedFileSelection::getPicture() const
0255 {
0256   QByteArray data;
0257   const FrameCollection& frames = m_framesModel[Frame::Tag_Picture]->frames();
0258   if (auto it = frames.find(
0259         Frame(Frame::FT_Picture, QLatin1String(""), QLatin1String(""), -1));
0260       it != frames.cend() && !it->isInactive()) {
0261     PictureFrame::getData(*it, data);
0262   }
0263   return data;
0264 }
0265 
0266 /**
0267  * Replace codes in format string with information from the tags.
0268  * @param tagVersion tag version
0269  * @param fmt format string
0270  * @return string with format codes replaced.
0271  */
0272 QString TaggedFileSelection::formatString(Frame::TagVersion tagVersion,
0273                                           const QString& fmt) const
0274 {
0275   if (!m_state.m_singleFile)
0276     return fmt;
0277 
0278   TrackData trackData(*m_state.m_singleFile, tagVersion);
0279   return trackData.formatString(fmt);
0280 }
0281 
0282 /**
0283  * Select changed frames in the tables if multiple files are selected.
0284  */
0285 void TaggedFileSelection::selectChangedFrames()
0286 {
0287   if (m_state.m_fileCount > 1) {
0288     FOR_ALL_TAGS(tagNr) {
0289       m_framesModel[tagNr]->selectChangedFrames();
0290     }
0291   }
0292 }
0293 
0294 /**
0295  * Clear frame collection in frame models not used by current selection.
0296  */
0297 void TaggedFileSelection::clearUnusedFrames()
0298 {
0299   FOR_ALL_TAGS(tagNr) {
0300     if (m_state.m_tagSupportedCount[tagNr] == 0) {
0301       m_framesModel[tagNr]->clearFrames();
0302     }
0303   }
0304 }