diff --git a/taglib/ape/apetag.cpp b/taglib/ape/apetag.cpp index 082fd038..1f3f84a2 100644 --- a/taglib/ape/apetag.cpp +++ b/taglib/ape/apetag.cpp @@ -174,6 +174,70 @@ void APE::Tag::setTrack(uint i) addValue("TRACK", String::number(i), true); } +TagDict APE::Tag::toDict() const +{ + TagDict dict; + ItemListMap::ConstIterator it = itemListMap().begin(); + for (; it != itemListMap().end(); ++it) { + String tagName = it->first.upper(); + // These two tags need to be handled specially; in APE tags the track number is usually + // named TRACK instead of TRACKNUMBER, the date tag is YEAR instead of DATE + // + if (tagName == "TRACK") + tagName = "TRACKNUMBER"; + else if (tagName == "YEAR") + tagName = "DATE"; + if (it->second.type() == Item::Text) + dict[tagName].append(it->second.toStringList()); + } + return dict; +} + +void APE::Tag::fromDict(const TagDict &orig_dict) +{ + TagDict dict(orig_dict); // make a local copy that can be modified + + if (dict.contains("TRACKNUMBER")) { + dict.insert("TRACK", dict["TRACKNUMBER"]); + dict.erase("TRACKNUMBER"); + } + if (dict.contains("DATE")) { + dict.insert("YEAR", dict["DATE"]); + dict.erase("DATE"); + } + + // first check if tags need to be removed completely + StringList toRemove; + ItemListMap::ConstIterator remIt = itemListMap().begin(); + for (; remIt != itemListMap().end(); ++remIt) { + if (remIt->second.type() != APE::Item::Text) + // ignore binary and locator APE items + continue; + if (!dict.contains(remIt->first.upper())) + toRemove.append(remIt->first); + } + + for (StringList::Iterator removeIt = toRemove.begin(); removeIt != toRemove.end(); removeIt++) + removeItem(*removeIt); + + // now sync in the "forward direction + TagDict::ConstIterator it = dict.begin(); + for (; it != dict.end(); ++it) { + const String &tagName = it->first; + if (!(itemListMap().contains(tagName)) || !(itemListMap()[tagName].values() == it->second)) { + if (it->second.size() == 0) + removeItem(tagName); + else { + StringList::ConstIterator valueIt = it->second.begin(); + addValue(tagName, *valueIt, true); + ++valueIt; + for(; valueIt != it->second.end(); ++valueIt) + addValue(tagName, *valueIt, false); + } + } + } +} + APE::Footer *APE::Tag::footer() const { return &d->footer; diff --git a/taglib/ape/apetag.h b/taglib/ape/apetag.h index 13efd5e0..b48d9291 100644 --- a/taglib/ape/apetag.h +++ b/taglib/ape/apetag.h @@ -103,6 +103,25 @@ namespace TagLib { virtual void setYear(uint i); virtual void setTrack(uint i); + /*! + * Implements the unified tag dictionary interface -- export function. + * APE tags are perfectly compatible with the dictionary interface because they + * support both arbitrary tag names and multiple values. Currently only + * APE items of type *Text* are handled by the dictionary interface, while + * *Binary* and *Locator* items are simply ignored. + * + * The only conversion done by this export function is to rename the APE tags + * TRACK to TRACKNUMBER and YEAR to DATE, respectively, in order to be compliant + * with the names used in other formats. + */ + virtual TagDict toDict() const; + + /*! + * Implements the unified tag dictionary interface -- import function. The same + * comments as for the export function apply. + */ + virtual void fromDict(const TagDict &); + /*! * Returns a pointer to the tag's footer. */ diff --git a/taglib/mpeg/id3v2/id3v2tag.h b/taglib/mpeg/id3v2/id3v2tag.h index 715daf04..f67a71e7 100644 --- a/taglib/mpeg/id3v2/id3v2tag.h +++ b/taglib/mpeg/id3v2/id3v2tag.h @@ -262,11 +262,14 @@ namespace TagLib { /*! * Implements the unified tag dictionary interface -- export function. + * This function does some work to translate the hard-specified ID3v2 + * frame types into a free-form string-to-stringlist dictionary. */ virtual TagDict toDict() const; /*! * Implements the unified tag dictionary interface -- import function. + * See the comments in toDict(). */ virtual void fromDict(const TagDict &); diff --git a/taglib/ogg/xiphcomment.h b/taglib/ogg/xiphcomment.h index 988f616d..6ad23c6a 100644 --- a/taglib/ogg/xiphcomment.h +++ b/taglib/ogg/xiphcomment.h @@ -142,11 +142,16 @@ namespace TagLib { /*! * Implements the unified tag dictionary interface -- export function. + * The result is a one-to-one match of the Xiph comment, since it is + * completely compatible with the dictionary interface (in fact, a Xiph + * comment is nothing more than a map from tag names to list of values, + * as is the dict interface). */ virtual TagDict toDict() const; /*! * Implements the unified tag dictionary interface -- import function. + * The tags from the given dict will be stored one-to-one in the file. */ virtual void fromDict(const TagDict &);