File indexing completed on 2024-05-19 04:55:57

0001 /**
0002  * \file tagconfig.cpp
0003  * Tag related configuration.
0004  *
0005  * \b Project: Kid3
0006  * \author Urs Fleisch
0007  * \date 29 Jun 2013
0008  *
0009  * Copyright (C) 2013-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 "tagconfig.h"
0028 #include <QCoreApplication>
0029 #include <QVector>
0030 #include <QVariantMap>
0031 #include "taggedfile.h"
0032 #include "frame.h"
0033 #include "isettings.h"
0034 
0035 namespace {
0036 
0037 /** Default value for comment name */
0038 const char* const defaultCommentName = "COMMENT";
0039 
0040 /** Default value for RIFF track name */
0041 const char* const defaultRiffTrackName = "IPRT";
0042 
0043 }
0044 
0045 
0046 /**
0047  * Mapping between star count and rating values.
0048  */
0049 class StarRatingMapping {
0050 public:
0051   /** Maximum number of stars. */
0052   static constexpr int MAX_STAR_COUNT = 5;
0053 
0054   /** Constructor. */
0055   StarRatingMapping();
0056 
0057   /**
0058    * Get star count from rating value.
0059    * @param rating rating value stored in tag frame
0060    * @param type rating type containing frame name and optionally field value,
0061    * e.g. "POPM.Windows Media Player 9 Series" or "RATING"
0062    * @return number of stars (1..5).
0063    */
0064   int starCountFromRating(int rating, const QString& type) const;
0065 
0066   /**
0067    * Get rating value from star count.
0068    * @param starCount number of stars (1..5)
0069    * @param type rating type containing frame name and optionally field value,
0070    * e.g. "POPM.Windows Media Player 9 Series" or "RATING"
0071    * @return rating value stored in tag frame, usually a value between 1 and 255
0072    * or 1 and 100.
0073    */
0074   int starCountToRating(int starCount, const QString& type) const;
0075 
0076   /** Serialize to string list. */
0077   QStringList toStringList() const;
0078 
0079   /** Set from string list. */
0080   void fromStringList(const QStringList& strs);
0081 
0082   /** Get default value for Email field in POPM frame. */
0083   QString defaultPopmEmail() const;
0084 
0085   /** Get mappings. */
0086   const QList<QPair<QString, QVector<int> > >& getMappings() const {
0087     return m_maps;
0088   }
0089 
0090   /** Set mappings. */
0091   void setMappings(const QList<QPair<QString, QVector<int> > >& maps) {
0092     m_maps = maps;
0093   }
0094 
0095 private:
0096   const QVector<int>& valuesForType(const QString& type) const;
0097 
0098   QVector<int> m_wmpValues;
0099   QList<QPair<QString, QVector<int> > > m_maps;
0100 };
0101 
0102 StarRatingMapping::StarRatingMapping()
0103 {
0104   m_wmpValues << 1 << 64 << 128 << 196 << 255;
0105   QVector<int> traktorValues, wmaValues, percentValues;
0106   traktorValues << 51 << 102 << 153 << 204 << 255;
0107   wmaValues << 1<< 25<< 50 << 75 << 99;
0108   percentValues << 20 << 40 << 60 << 80 << 100;
0109   m_maps << qMakePair(QString(QLatin1String("POPM")), m_wmpValues);
0110   m_maps << qMakePair(QString(QLatin1String("POPM.Windows Media Player 9 Series")),
0111                       m_wmpValues);
0112   m_maps << qMakePair(QString(QLatin1String("POPM.traktor@native-instruments.de")),
0113                       traktorValues);
0114   m_maps << qMakePair(QString(QLatin1String("WM/SharedUserRating")), wmaValues);
0115   m_maps << qMakePair(QString(QLatin1String("IRTD")), percentValues);
0116   m_maps << qMakePair(QString(QLatin1String("rate")), percentValues);
0117   m_maps << qMakePair(QString(QLatin1String("RATING")), percentValues);
0118 }
0119 
0120 int StarRatingMapping::starCountFromRating(int rating, const QString& type) const
0121 {
0122   if (rating < 1) {
0123     return 0;
0124   }
0125   const QVector<int>& vals = valuesForType(type);
0126   const bool useWmpHack = vals.at(3) == 196;
0127   for (int i = 1; i < MAX_STAR_COUNT; ++i) {
0128     // This is done the weird way in order to get the same thresholds as
0129     // apparently used in Windows Explorer:
0130     // 1:  1-31, 2: 32-95, 3: 96-159, 4:160-223, 5:224-255
0131     if (int threshold = useWmpHack
0132                           ? (((vals[i - 1] + 1) & ~7) + ((vals[i] + 1) & ~7)) / 2
0133                           : (vals[i - 1] + vals[i] + 1) / 2;
0134       rating < threshold) {
0135       return i;
0136     }
0137   }
0138   return MAX_STAR_COUNT;
0139 }
0140 
0141 int StarRatingMapping::starCountToRating(int starCount, const QString& type) const
0142 {
0143   if (starCount < 1) {
0144     return 0;
0145   }
0146   if (starCount > MAX_STAR_COUNT) {
0147     starCount = MAX_STAR_COUNT;
0148   }
0149   return valuesForType(type).at(starCount - 1);
0150 }
0151 
0152 const QVector<int>& StarRatingMapping::valuesForType(const QString& type) const
0153 {
0154   // First search in the maps for the given type.
0155   for (auto it = m_maps.constBegin(); it != m_maps.constEnd(); ++it) {
0156     if (type == it->first) {
0157       return it->second;
0158     }
0159   }
0160   // If not found, use the first map or the WMP map if no maps are available.
0161   return m_maps.isEmpty() ? m_wmpValues : m_maps.first().second;
0162 }
0163 
0164 QStringList StarRatingMapping::toStringList() const
0165 {
0166   QStringList strs;
0167   for (auto it = m_maps.constBegin(); it != m_maps.constEnd(); ++it) {
0168     QString str = it->first;
0169     for (auto sit = it->second.constBegin(); sit != it->second.constEnd(); ++sit) {
0170       str += QLatin1Char(',');
0171       str += QString::number(*sit);
0172     }
0173     strs.append(str);
0174   }
0175   return strs;
0176 }
0177 
0178 void StarRatingMapping::fromStringList(const QStringList& strs)
0179 {
0180   QList<QPair<QString, QVector<int> > > maps;
0181   QVector<int> values;
0182   for (auto it = strs.constBegin(); it != strs.constEnd(); ++it) {
0183     QStringList parts = it->split(QLatin1Char(','));
0184     if (const int numParts = parts.size(); numParts >= MAX_STAR_COUNT + 1) {
0185       bool ok = false;
0186       values.resize(0);
0187       int lastValue = -1;
0188       for (int i = numParts - MAX_STAR_COUNT; i < numParts; ++i) {
0189         int value = parts.at(i).toInt(&ok);
0190         if (value <= lastValue) {
0191           ok = false;
0192         }
0193         if (!ok) {
0194           break;
0195         }
0196         values.append(value);
0197       }
0198       if (ok) {
0199         const QStringList typeParts = parts.mid(0, numParts - MAX_STAR_COUNT);
0200         const QString type = typeParts.join(QLatin1String(","));
0201         maps.append(qMakePair(type, values));
0202       }
0203     }
0204   }
0205   if (!maps.isEmpty()) {
0206     m_maps.swap(maps);
0207   }
0208 }
0209 
0210 QString StarRatingMapping::defaultPopmEmail() const
0211 {
0212   for (auto it = m_maps.constBegin(); it != m_maps.constEnd(); ++it) {
0213     if (QString type = it->first; type.startsWith(QLatin1String("POPM"))) {
0214       return type.length() > 4 && type.at(4) == QLatin1Char('.')
0215           ? type.mid(5) : QLatin1String("");
0216     }
0217   }
0218   return QString();
0219 }
0220 
0221 
0222 int TagConfig::s_index = -1;
0223 
0224 /**
0225  * Constructor.
0226  */
0227 TagConfig::TagConfig()
0228   : StoredConfig(QLatin1String("Tags")),
0229     m_starRatingMapping(new StarRatingMapping),
0230     m_commentName(QString::fromLatin1(defaultCommentName)),
0231     m_riffTrackName(QString::fromLatin1(defaultRiffTrackName)),
0232     m_pictureNameItem(VP_METADATA_BLOCK_PICTURE),
0233     m_id3v2Version(ID3v2_3_0),
0234     m_textEncodingV1(QLatin1String("ISO-8859-1")),
0235     m_textEncoding(TE_ISO8859_1),
0236     m_quickAccessFrames(FrameCollection::DEFAULT_QUICK_ACCESS_FRAMES),
0237     m_trackNumberDigits(1),
0238     m_taggedFileFeatures(0),
0239     m_maximumPictureSize(131072),
0240     m_markOversizedPictures(false),
0241     m_markStandardViolations(true),
0242     m_onlyCustomGenres(false),
0243     m_markTruncations(true),
0244     m_enableTotalNumberOfTracks(false),
0245     m_genreNotNumeric(true),
0246     m_lowercaseId3RiffChunk(false)
0247 {
0248   m_disabledPlugins << QLatin1String("Id3libMetadata")
0249                     << QLatin1String("Mp4v2Metadata");
0250 }
0251 
0252 /**
0253  * Destructor.
0254  */
0255 TagConfig::~TagConfig()
0256 {
0257   // Must not be inline because of forwared declared QScopedPointer.
0258 }
0259 
0260 /**
0261  * Persist configuration.
0262  *
0263  * @param config configuration
0264  */
0265 void TagConfig::writeToConfig(ISettings* config) const
0266 {
0267   config->beginGroup(m_group);
0268   config->setValue(QLatin1String("MarkTruncations"),
0269                    QVariant(m_markTruncations));
0270   config->setValue(QLatin1String("MarkOversizedPictures"),
0271                    QVariant(m_markOversizedPictures));
0272   config->setValue(QLatin1String("MaximumPictureSize"),
0273                    QVariant(m_maximumPictureSize));
0274   config->setValue(QLatin1String("MarkStandardViolations"),
0275                    QVariant(m_markStandardViolations));
0276   config->setValue(QLatin1String("EnableTotalNumberOfTracks"),
0277                    QVariant(m_enableTotalNumberOfTracks));
0278   config->setValue(QLatin1String("GenreNotNumeric"),
0279                    QVariant(m_genreNotNumeric));
0280   config->setValue(QLatin1String("LowercaseId3RiffChunk"),
0281                    QVariant(m_lowercaseId3RiffChunk));
0282   config->setValue(QLatin1String("CommentName"),
0283                    QVariant(m_commentName));
0284   config->setValue(QLatin1String("PictureNameItem"),
0285                    QVariant(m_pictureNameItem));
0286   config->setValue(QLatin1String("RiffTrackName"),
0287                    QVariant(m_riffTrackName));
0288   config->setValue(QLatin1String("CustomGenres"),
0289                    QVariant(m_customGenres));
0290   config->setValue(QLatin1String("CustomFrames"),
0291                    QVariant(m_customFrames));
0292   config->setValue(QLatin1String("ID3v2Version"),
0293                    QVariant(m_id3v2Version));
0294   config->setValue(QLatin1String("TextEncodingV1"),
0295                    QVariant(m_textEncodingV1));
0296   config->setValue(QLatin1String("TextEncoding"),
0297                    QVariant(m_textEncoding));
0298 #ifdef Q_OS_MAC
0299   // Convince Mac OS X to store a 64-bit value.
0300   config->setValue(QLatin1String("QuickAccessFrames"),
0301                    QVariant(m_quickAccessFrames | (Q_UINT64_C(1) << 63)));
0302 #else
0303   config->setValue(QLatin1String("QuickAccessFrames"),
0304                    QVariant(m_quickAccessFrames));
0305 #endif
0306   config->setValue(QLatin1String("QuickAccessFrameOrder"),
0307                    QVariant(intListToStringList(m_quickAccessFrameOrder)));
0308   config->setValue(QLatin1String("TrackNumberDigits"),
0309                    QVariant(m_trackNumberDigits));
0310   config->setValue(QLatin1String("OnlyCustomGenres"),
0311                    QVariant(m_onlyCustomGenres));
0312   config->setValue(QLatin1String("PluginOrder"),
0313                    QVariant(m_pluginOrder));
0314   config->setValue(QLatin1String("DisabledPlugins"),
0315                    QVariant(m_disabledPlugins));
0316   config->setValue(QLatin1String("StarRatingMapping"),
0317                    QVariant(m_starRatingMapping->toStringList()));
0318   config->endGroup();
0319 }
0320 
0321 /**
0322  * Read persisted configuration.
0323  *
0324  * @param config configuration
0325  */
0326 void TagConfig::readFromConfig(ISettings* config)
0327 {
0328   config->beginGroup(m_group);
0329   m_markTruncations = config->value(QLatin1String("MarkTruncations"),
0330                                     m_markTruncations).toBool();
0331   m_markOversizedPictures = config->value(QLatin1String("MarkOversizedPictures"),
0332                                           m_markOversizedPictures).toBool();
0333   m_maximumPictureSize = config->value(QLatin1String("MaximumPictureSize"),
0334                                        m_maximumPictureSize).toInt();
0335   m_markStandardViolations =
0336       config->value(QLatin1String("MarkStandardViolations"),
0337                     m_markStandardViolations).toBool();
0338   m_enableTotalNumberOfTracks =
0339       config->value(QLatin1String("EnableTotalNumberOfTracks"),
0340                     m_enableTotalNumberOfTracks).toBool();
0341   m_genreNotNumeric = config->value(QLatin1String("GenreNotNumeric"),
0342                                     m_genreNotNumeric).toBool();
0343   m_lowercaseId3RiffChunk = config->value(QLatin1String("LowercaseId3RiffChunk"),
0344                                           m_lowercaseId3RiffChunk).toBool();
0345   m_commentName =
0346       config->value(QLatin1String("CommentName"),
0347                     QString::fromLatin1(defaultCommentName)).toString();
0348   m_pictureNameItem = config->value(QLatin1String("PictureNameItem"),
0349                                     VP_METADATA_BLOCK_PICTURE).toInt();
0350   m_riffTrackName =
0351       config->value(QLatin1String("RiffTrackName"),
0352                     QString::fromLatin1(defaultRiffTrackName)).toString();
0353   m_customGenres = config->value(QLatin1String("CustomGenres"),
0354                                  m_customGenres).toStringList();
0355   m_customFrames = config->value(QLatin1String("CustomFrames"),
0356                                  m_customFrames).toStringList();
0357   m_id3v2Version = config->value(QLatin1String("ID3v2Version"),
0358                                  ID3v2_3_0).toInt();
0359   m_textEncodingV1 = config->value(QLatin1String("TextEncodingV1"),
0360                                    QLatin1String("ISO-8859-1")).toString();
0361   m_textEncoding = config->value(QLatin1String("TextEncoding"),
0362                                  TE_ISO8859_1).toInt();
0363   m_quickAccessFrames =
0364       config->value(QLatin1String("QuickAccessFrames"),
0365                     FrameCollection::DEFAULT_QUICK_ACCESS_FRAMES).toULongLong();
0366 #ifdef Q_OS_MAC
0367   m_quickAccessFrames &= ~(Q_UINT64_C(1) << 63);
0368 #endif
0369   m_quickAccessFrameOrder = stringListToIntList(
0370         config->value(QLatin1String("QuickAccessFrameOrder"), QStringList())
0371         .toStringList());
0372   m_trackNumberDigits = config->value(QLatin1String("TrackNumberDigits"),
0373                                       1).toInt();
0374   m_onlyCustomGenres = config->value(QLatin1String("OnlyCustomGenres"),
0375                                      m_onlyCustomGenres).toBool();
0376   m_pluginOrder = config->value(QLatin1String("PluginOrder"),
0377                                  m_pluginOrder).toStringList();
0378   m_disabledPlugins = config->value(QLatin1String("DisabledPlugins"),
0379                                  m_disabledPlugins).toStringList();
0380   m_starRatingMapping->fromStringList(
0381         config->value(QLatin1String("StarRatingMapping"),
0382                       QStringList()).toStringList());
0383   config->endGroup();
0384 
0385   if (m_pluginOrder.isEmpty()) {
0386     setDefaultPluginOrder();
0387   }
0388 }
0389 
0390 /**
0391  * Set default plugin order.
0392  */
0393 void TagConfig::setDefaultPluginOrder()
0394 {
0395   /** Default to filename format list */
0396   static const char* const defaultPluginOrder[] = {
0397     "Id3libMetadata",
0398     "OggFlacMetadata",
0399     "Mp4v2Metadata",
0400     "TaglibMetadata",
0401     nullptr
0402   };
0403 
0404   m_pluginOrder.clear();
0405   for (const char* const* pn = defaultPluginOrder; *pn != nullptr; ++pn) {
0406     m_pluginOrder += QString::fromLatin1(*pn);
0407   }
0408 }
0409 
0410 /** version used for new ID3v2 tags */
0411 int TagConfig::id3v2Version() const
0412 {
0413   if (m_id3v2Version == ID3v2_3_0 &&
0414       !(taggedFileFeatures() & TaggedFile::TF_ID3v23))
0415     return ID3v2_4_0;
0416   if (m_id3v2Version == ID3v2_4_0 &&
0417       !(taggedFileFeatures() & TaggedFile::TF_ID3v24))
0418     return ID3v2_3_0;
0419   return m_id3v2Version;
0420 }
0421 
0422 /**
0423  * Set features provided by metadata plugins.
0424  * @param taggedFileFeatures bit mask with TaggedFile::Feature flags set
0425  */
0426 void TagConfig::setTaggedFileFeatures(int taggedFileFeatures)
0427 {
0428   if (m_taggedFileFeatures != taggedFileFeatures) {
0429     m_taggedFileFeatures = taggedFileFeatures;
0430     emit taggedFileFeaturesChanged(m_taggedFileFeatures);
0431   }
0432 }
0433 
0434 /** Set true to mark truncated ID3v1.1 fields. */
0435 void TagConfig::setMarkTruncations(bool markTruncations)
0436 {
0437   if (m_markTruncations != markTruncations) {
0438     m_markTruncations = markTruncations;
0439     emit markTruncationsChanged(m_markTruncations);
0440   }
0441 }
0442 
0443 /** Set true to mark oversized pictures. */
0444 void TagConfig::setMarkOversizedPictures(bool markOversizedPictures)
0445 {
0446   if (m_markOversizedPictures != markOversizedPictures) {
0447     m_markOversizedPictures = markOversizedPictures;
0448     emit markOversizedPicturesChanged(m_markOversizedPictures);
0449   }
0450 }
0451 
0452 /** Set maximum size of picture in bytes. */
0453 void TagConfig::setMaximumPictureSize(int maximumPictureSize)
0454 {
0455   if (m_maximumPictureSize != maximumPictureSize) {
0456     m_maximumPictureSize = maximumPictureSize;
0457     emit maximumPictureSizeChanged(m_maximumPictureSize);
0458   }
0459 }
0460 
0461 /** Set true to mark standard violations. */
0462 void TagConfig::setMarkStandardViolations(bool markStandardViolations)
0463 {
0464   if (m_markStandardViolations != markStandardViolations) {
0465     m_markStandardViolations = markStandardViolations;
0466     emit markStandardViolationsChanged(m_markStandardViolations);
0467   }
0468 }
0469 
0470 /** Set true to write total number of tracks into track fields. */
0471 void TagConfig::setEnableTotalNumberOfTracks(bool enableTotalNumberOfTracks)
0472 {
0473   if (m_enableTotalNumberOfTracks != enableTotalNumberOfTracks) {
0474     m_enableTotalNumberOfTracks = enableTotalNumberOfTracks;
0475     emit enableTotalNumberOfTracksChanged(m_enableTotalNumberOfTracks);
0476   }
0477 }
0478 
0479 /** Set true to write genres as text instead of numeric string. */
0480 void TagConfig::setGenreNotNumeric(bool genreNotNumeric)
0481 {
0482   if (m_genreNotNumeric != genreNotNumeric) {
0483     m_genreNotNumeric = genreNotNumeric;
0484     emit genreNotNumericChanged(m_genreNotNumeric);
0485   }
0486 }
0487 
0488 /** Set true to use "id3 " instead of "ID3 " chunk names in WAV files */
0489 void TagConfig::setLowercaseId3RiffChunk(bool lowercaseId3RiffChunk)
0490 {
0491   if (m_lowercaseId3RiffChunk != lowercaseId3RiffChunk) {
0492     m_lowercaseId3RiffChunk = lowercaseId3RiffChunk;
0493     emit lowercaseId3RiffChunkChanged(m_lowercaseId3RiffChunk);
0494   }
0495 }
0496 
0497 /** Set field name used for Vorbis comment entries. */
0498 void TagConfig::setCommentName(const QString& commentName)
0499 {
0500   if (m_commentName != commentName) {
0501     m_commentName = commentName;
0502     emit commentNameChanged(m_commentName);
0503   }
0504 }
0505 
0506 /** Set index of field name used for Vorbis picture entries. */
0507 void TagConfig::setPictureNameIndex(int pictureNameIndex)
0508 {
0509   if (m_pictureNameItem != pictureNameIndex) {
0510     m_pictureNameItem = pictureNameIndex;
0511     emit pictureNameIndexChanged(m_pictureNameItem);
0512   }
0513 }
0514 
0515 /** Set field name used for RIFF track entries. */
0516 void TagConfig::setRiffTrackName(const QString& riffTrackName)
0517 {
0518   if (m_riffTrackName != riffTrackName) {
0519     m_riffTrackName = riffTrackName;
0520     emit riffTrackNameChanged(m_riffTrackName);
0521   }
0522 }
0523 
0524 /** Set custom genres for ID3v2.3. */
0525 void TagConfig::setCustomGenres(const QStringList& customGenres)
0526 {
0527   if (m_customGenres != customGenres) {
0528     m_customGenres = customGenres;
0529     emit customGenresChanged(m_customGenres);
0530   }
0531 }
0532 
0533 /** Set custom frame names. */
0534 void TagConfig::setCustomFrames(const QStringList& customFrames)
0535 {
0536   if (m_customFrames != customFrames) {
0537     m_customFrames = customFrames;
0538     emit customFramesChanged(m_customFrames);
0539   }
0540 }
0541 
0542 /** Set version used for new ID3v2 tags. */
0543 void TagConfig::setId3v2Version(int id3v2Version)
0544 {
0545   if (m_id3v2Version != id3v2Version) {
0546     m_id3v2Version = id3v2Version;
0547     emit id3v2VersionChanged(m_id3v2Version);
0548   }
0549 }
0550 
0551 /** Set text encoding used for new ID3v1 tags. */
0552 void TagConfig::setTextEncodingV1(const QString& textEncodingV1)
0553 {
0554   if (m_textEncodingV1 != textEncodingV1) {
0555     m_textEncodingV1 = textEncodingV1;
0556     emit textEncodingV1Changed(m_textEncodingV1);
0557   }
0558 }
0559 
0560 /** index of ID3v1 text encoding in getTextCodecNames() */
0561 int TagConfig::textEncodingV1Index() const
0562 {
0563   return indexFromTextCodecName(m_textEncodingV1);
0564 }
0565 
0566 /** Set ID3v1 text encoding from index in getTextCodecNames(). */
0567 void TagConfig::setTextEncodingV1Index(int index)
0568 {
0569   if (QString encoding = indexToTextCodecName(index); !encoding.isNull()) {
0570     setTextEncodingV1(encoding);
0571   }
0572 }
0573 
0574 /** Set text encoding used for new ID3v2 tags. */
0575 void TagConfig::setTextEncoding(int textEncoding)
0576 {
0577   if (m_textEncoding != textEncoding) {
0578     m_textEncoding = textEncoding;
0579     emit textEncodingChanged(m_textEncoding);
0580   }
0581 }
0582 
0583 /** Set frames which are displayed for Tag 2 even if not present. */
0584 void TagConfig::setQuickAccessFrames(quint64 quickAccessFrames)
0585 {
0586   if (m_quickAccessFrames != quickAccessFrames) {
0587     m_quickAccessFrames = quickAccessFrames;
0588     emit quickAccessFramesChanged(m_quickAccessFrames);
0589   }
0590 }
0591 
0592 /** Set order of frames which are displayed for Tag 2 even if not present. */
0593 void TagConfig::setQuickAccessFrameOrder(const QList<int>& frameTypes)
0594 {
0595   if (m_quickAccessFrameOrder != frameTypes) {
0596     m_quickAccessFrameOrder = frameTypes;
0597     emit quickAccessFrameOrderChanged(m_quickAccessFrameOrder);
0598   }
0599 }
0600 
0601 /** Set number of digits in track number. */
0602 void TagConfig::setTrackNumberDigits(int trackNumberDigits)
0603 {
0604   if (m_trackNumberDigits != trackNumberDigits) {
0605     m_trackNumberDigits = trackNumberDigits;
0606     emit trackNumberDigitsChanged(m_trackNumberDigits);
0607   }
0608 }
0609 
0610 /** Set true to show only custom genres in combo boxes. */
0611 void TagConfig::setOnlyCustomGenres(bool onlyCustomGenres)
0612 {
0613   if (m_onlyCustomGenres != onlyCustomGenres) {
0614     m_onlyCustomGenres = onlyCustomGenres;
0615     emit onlyCustomGenresChanged(m_onlyCustomGenres);
0616   }
0617 }
0618 
0619 /** Set the order in which meta data plugins are tried when opening a file. */
0620 void TagConfig::setPluginOrder(const QStringList& pluginOrder)
0621 {
0622   if (m_pluginOrder != pluginOrder) {
0623     m_pluginOrder = pluginOrder;
0624     emit pluginOrderChanged(m_pluginOrder);
0625   }
0626 }
0627 
0628 /** Set list of disabled plugins. */
0629 void TagConfig::setDisabledPlugins(const QStringList& disabledPlugins)
0630 {
0631   if (m_disabledPlugins != disabledPlugins) {
0632     m_disabledPlugins = disabledPlugins;
0633     emit disabledPluginsChanged(m_disabledPlugins);
0634   }
0635 }
0636 
0637 /**
0638  * Set list of available plugins.
0639  * @param availablePlugins available plugins
0640  */
0641 void TagConfig::setAvailablePlugins(const QStringList& availablePlugins)
0642 {
0643   if (m_availablePlugins != availablePlugins) {
0644     m_availablePlugins = availablePlugins;
0645     emit availablePluginsChanged(m_availablePlugins);
0646   }
0647 }
0648 
0649 /**
0650  * Get list of star count rating mappings.
0651  * @return star count rating mappings as a list of strings.
0652  */
0653 QStringList TagConfig::starRatingMappingStrings() const
0654 {
0655   return m_starRatingMapping->toStringList();
0656 }
0657 
0658 /**
0659  * Set list of star count rating mappings.
0660  * @param mappings star count rating mappings
0661  */
0662 void TagConfig::setStarRatingMappingStrings(const QStringList& mappings)
0663 {
0664   if (m_starRatingMapping->toStringList() != mappings) {
0665     m_starRatingMapping->fromStringList(mappings);
0666     emit starRatingMappingsChanged();
0667   }
0668 }
0669 
0670 /**
0671  * Get list of star count rating mappings.
0672  * @return star count rating mappings.
0673  */
0674 const QList<QPair<QString, QVector<int> > >& TagConfig::starRatingMappings() const
0675 {
0676   return m_starRatingMapping->getMappings();
0677 }
0678 
0679 /**
0680  * Set list of star count rating mappings.
0681  * @param maps star count rating mappings
0682  */
0683 void TagConfig::setStarRatingMappings(
0684     const QList<QPair<QString, QVector<int> > >& maps)
0685 {
0686   if (m_starRatingMapping->getMappings() != maps) {
0687     m_starRatingMapping->setMappings(maps);
0688     emit starRatingMappingsChanged();
0689   }
0690 }
0691 
0692 /**
0693  * Get star count from rating value.
0694  * @param rating rating value stored in tag frame
0695  * @param type rating type containing frame name and optionally field value,
0696  * e.g. "POPM.Windows Media Player 9 Series" or "RATING"
0697  * @return number of stars (1..5).
0698  */
0699 int TagConfig::starCountFromRating(int rating, const QString& type) const
0700 {
0701   return m_starRatingMapping->starCountFromRating(rating, type);
0702 }
0703 
0704 /**
0705  * Get rating value from star count.
0706  * @param starCount number of stars (1..5)
0707  * @param type rating type containing frame name and optionally field value,
0708  * e.g. "POPM.Windows Media Player 9 Series" or "RATING"
0709  * @return rating value stored in tag frame, usually a value between 1 and 255
0710  * or 1 and 100.
0711  */
0712 int TagConfig::starCountToRating(int starCount, const QString& type) const
0713 {
0714   return m_starRatingMapping->starCountToRating(starCount, type);
0715 }
0716 
0717 /**
0718  * Get default value for Email field in POPM frame.
0719  * @return value for Email field in first POPM entry of star rating mappings.
0720  */
0721 QString TagConfig::defaultPopmEmail() const
0722 {
0723   return m_starRatingMapping->defaultPopmEmail();
0724 }
0725 
0726 /**
0727  * String list of encodings for ID3v2.
0728  */
0729 QStringList TagConfig::getTextEncodingNames()
0730 {
0731   static constexpr int NUM_NAMES = 3;
0732   static const char* const names[NUM_NAMES] = {
0733     QT_TRANSLATE_NOOP("@default", "ISO-8859-1"),
0734     QT_TRANSLATE_NOOP("@default", "UTF16"),
0735     QT_TRANSLATE_NOOP("@default", "UTF8")
0736   };
0737   QStringList strs;
0738   strs.reserve(NUM_NAMES);
0739   for (int i = 0; i < NUM_NAMES; ++i) {
0740     strs.append(QCoreApplication::translate("@default", names[i]));
0741   }
0742   return strs;
0743 }
0744 
0745 /**
0746  * String list of possible versions used for new ID3v2 tags.
0747  */
0748 QStringList TagConfig::getId3v2VersionNames()
0749 {
0750   return {QLatin1String("ID3v2.3.0"), QLatin1String("ID3v2.4.0")};
0751 }
0752 
0753 /**
0754  * String list with suggested field names used for Vorbis comment entries.
0755  */
0756 QStringList TagConfig::getCommentNames()
0757 {
0758   return {QLatin1String("COMMENT"), QLatin1String("DESCRIPTION")};
0759 }
0760 
0761 /**
0762  * String list with possible field names used for Vorbis picture entries.
0763  */
0764 QStringList TagConfig::getPictureNames()
0765 {
0766   return {QLatin1String("METADATA_BLOCK_PICTURE"), QLatin1String("COVERART")};
0767 }
0768 
0769 /**
0770  * String list with suggested field names used for RIFF track entries.
0771  */
0772 QStringList TagConfig::getRiffTrackNames()
0773 {
0774   return {QLatin1String("IPRT"), QLatin1String("ITRK"), QLatin1String("TRCK")};
0775 }
0776 
0777 /**
0778  * Available and selected quick access frames.
0779  */
0780 QVariantList TagConfig::selectedQuickAccessFrames() const {
0781   return getQuickAccessFrameSelection(
0782         quickAccessFrameOrder(), quickAccessFrames(),
0783         customFrameNamesToDisplayNames(customFrames()));
0784 }
0785 
0786 /**
0787  * Set selected quick access frames.
0788  * @param namesSelected list of maps with name, selected and type fields
0789  */
0790 void TagConfig::setSelectedQuickAccessFrames(
0791     const QVariantList& namesSelected) {
0792   QList<int> frameTypes;
0793   quint64 frameMask = 0;
0794   setQuickAccessFrameSelection(namesSelected, frameTypes, frameMask);
0795   setQuickAccessFrameOrder(frameTypes);
0796   setQuickAccessFrames(frameMask);
0797 }
0798 
0799 /**
0800  * Get the available and selected quick access frames.
0801  * @param types ordered frame types as in quickAccessFrameOrder()
0802  * @param frameMask quick access frame selection as in quickAccessFrames()
0803  * @param customFrameNames list of custom frame names as in customFrames()
0804  * @return list of name/type/selected maps.
0805  */
0806 QVariantList TagConfig::getQuickAccessFrameSelection(
0807     const QList<int>& types, quint64 frameMask,
0808     const QStringList& customFrameNames)
0809 {
0810   QList frameTypes(types);
0811   if (frameTypes.size() < Frame::FT_Custom1) {
0812     frameTypes.clear();
0813     frameTypes.reserve(Frame::FT_LastFrame - Frame::FT_FirstFrame + 1);
0814     for (int i = Frame::FT_FirstFrame; i <= Frame::FT_LastFrame; ++i) {
0815       frameTypes.append(i);
0816     }
0817   } else {
0818     for (int i = frameTypes.size(); i <= Frame::FT_LastFrame; ++i) {
0819       frameTypes.append(i);
0820     }
0821   }
0822   QVariantList namesSelected;
0823   const auto constFrameTypes = frameTypes;
0824   for (int frameType : constFrameTypes) {
0825     auto name = Frame::ExtendedType(static_cast<Frame::Type>(frameType))
0826         .getTranslatedName();
0827     if (Frame::isCustomFrameType(static_cast<Frame::Type>(frameType))) {
0828       if (int idx = frameType - Frame::FT_Custom1;
0829           idx >= 0 && idx < customFrameNames.size()) {
0830         name = customFrameNames.at(idx);
0831       } else {
0832         name.clear();
0833       }
0834     }
0835     if (!name.isEmpty()) {
0836       const bool selected = (frameMask & (1ULL << frameType)) != 0ULL;
0837       namesSelected.append(
0838             QVariantMap{{QLatin1String("name"), name},
0839                         {QLatin1String("type"), frameType},
0840                         {QLatin1String("selected"), selected}});
0841     }
0842   }
0843   return namesSelected;
0844 }
0845 
0846 /**
0847  * Set the selected quick access frames.
0848  * @param namesSelected list of name/type/selected maps
0849  * @param frameTypes ordered frame types are returned here,
0850  *        suitable for setQuickAccessFrameOrder()
0851  * @param frameMask the quick access frame selection is returned here,
0852  *        suitable for setQuickAccessFrames()
0853  */
0854 void TagConfig::setQuickAccessFrameSelection(
0855     const QVariantList& namesSelected,
0856     QList<int>& frameTypes, quint64& frameMask)
0857 {
0858   bool isStandardFrameOrder = true;
0859   const int numQuickAccessTags = namesSelected.size();
0860   frameTypes.clear();
0861   frameTypes.reserve(numQuickAccessTags);
0862   frameMask = 0;
0863   for (int row = 0; row < numQuickAccessTags; ++row) {
0864     auto map = namesSelected.at(row).toMap();
0865     auto frameType = map.value(QLatin1String("type")).toInt();
0866     auto selected = map.value(QLatin1String("selected")).toBool();
0867     if (frameType != row) {
0868       isStandardFrameOrder = false;
0869     }
0870     frameTypes.append(frameType);
0871     if (selected) {
0872       frameMask |= 1ULL << frameType;
0873     }
0874   }
0875   if (isStandardFrameOrder) {
0876     frameTypes.clear();
0877   }
0878 }
0879 
0880 /**
0881  * Convert list of custom frame names to display names.
0882  * @param names custom frame names
0883  * @return possibly translated display representations of @a names.
0884  */
0885 QStringList TagConfig::customFrameNamesToDisplayNames(const QStringList& names)
0886 {
0887   QStringList displayNames;
0888   for (const QString& name : names) {
0889     displayNames.append(Frame::getDisplayName(name));
0890   }
0891   return displayNames;
0892 }
0893 
0894 /**
0895  * Convert list of display names to custom frame names.
0896  * @param displayNames displayed frame names
0897  * @return internal representations of @a displayNames.
0898  */
0899 QStringList TagConfig::customFrameNamesFromDisplayNames(
0900     const QStringList& displayNames)
0901 {
0902   QStringList names;
0903   for (const QString& displayName : displayNames) {
0904     QByteArray frameId = Frame::getFrameIdForTranslatedFrameName(displayName);
0905     names.append(frameId.isNull()
0906                  ? Frame::getNameForTranslatedFrameName(displayName)
0907                  : QString::fromLatin1(frameId));
0908   }
0909   return names;
0910 }