~ubuntu-branches/ubuntu/intrepid/kid3/intrepid

« back to all changes in this revision

Viewing changes to kid3/taglibfile.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Michele Angrisano
  • Date: 2008-01-09 23:20:54 UTC
  • mfrom: (1.1.6 upstream)
  • Revision ID: james.westby@ubuntu.com-20080109232054-gtcjxz4ahdnzbt01
Tags: 0.10-1ubuntu1
* Merge from Debian unstable, remaining changes:
  - debian/rules:
    + Use dh_icons instead dh_iconcache.
  - debian/control:
    + Update maintainer field.

Show diffs side-by-side

added added

removed removed

Lines of Context:
5
5
 * \b Project: Kid3
6
6
 * \author Urs Fleisch
7
7
 * \date 12 Sep 2006
 
8
 *
 
9
 * Copyright (C) 2006-2007  Urs Fleisch
 
10
 *
 
11
 * This file is part of Kid3.
 
12
 *
 
13
 * Kid3 is free software; you can redistribute it and/or modify
 
14
 * it under the terms of the GNU General Public License as published by
 
15
 * the Free Software Foundation; either version 2 of the License, or
 
16
 * (at your option) any later version.
 
17
 *
 
18
 * Kid3 is distributed in the hope that it will be useful,
 
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
21
 * GNU General Public License for more details.
 
22
 *
 
23
 * You should have received a copy of the GNU General Public License
 
24
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
8
25
 */
9
26
 
10
27
#include "taglibfile.h"
14
31
#include <qstring.h>
15
32
#include "qtcompatmac.h"
16
33
#if QT_VERSION >= 0x040000
17
 
#include <Q3CString>
 
34
#include <QByteArray>
18
35
#endif
19
36
 
20
37
#include "standardtags.h"
21
 
#include "taglibframelist.h"
22
38
#include "genres.h"
23
39
#include "dirinfo.h"
 
40
#include "kid3.h"
24
41
#include <sys/stat.h>
25
42
#ifdef WIN32
26
43
#include <sys/utime.h>
42
59
#include <taglib/apetag.h>
43
60
#include <taglib/textidentificationframe.h>
44
61
#include <taglib/commentsframe.h>
 
62
#include <taglib/attachedpictureframe.h>
 
63
#include <taglib/uniquefileidentifierframe.h>
 
64
 
 
65
#ifdef TAGLIB_SUPPORTS_GEOB_FRAMES
 
66
#include <taglib/generalencapsulatedobjectframe.h>
 
67
#else
 
68
#include "taglibext/generalencapsulatedobjectframe.h"
 
69
#endif
 
70
#ifdef TAGLIB_SUPPORTS_URLLINK_FRAMES
 
71
#include <taglib/urllinkframe.h>
 
72
#else
 
73
#include "taglibext/urllinkframe.h"
 
74
#endif
 
75
#ifdef TAGLIB_SUPPORTS_USLT_FRAMES
 
76
#include <taglib/unsynchronizedlyricsframe.h>
 
77
#else
 
78
#include "taglibext/unsynchronizedlyricsframe.h"
 
79
#endif
 
80
 
 
81
#include "taglibext/speex/speexfile.h"
 
82
#include "taglibext/speex/taglib_speexfiletyperesolver.h"
 
83
#include "taglibext/trueaudio/ttafile.h"
 
84
#include "taglibext/trueaudio/taglib_trueaudiofiletyperesolver.h"
 
85
#include "taglibext/wavpack/wvfile.h"
 
86
#include "taglibext/wavpack/taglib_wavpackfiletyperesolver.h"
 
87
#include "taglibext/aac/aacfiletyperesolver.h"
 
88
#include "taglibext/mp2/mp2filetyperesolver.h"
 
89
 
 
90
/** Default text encoding */
 
91
TagLib::String::Type TagLibFile::s_defaultTextEncoding = TagLib::String::Latin1;
45
92
 
46
93
/**
47
94
 * Constructor.
68
115
 */
69
116
void TagLibFile::readTags(bool force)
70
117
{
71
 
        Q3CString fn = QFile::encodeName(getDirInfo()->getDirname() + QDir::separator() + currentFilename());
 
118
        QCM_QCString fn = QFile::encodeName(getDirInfo()->getDirname() + QDir::separator() + currentFilename());
72
119
 
73
120
        if (force || m_fileRef.isNull()) {
74
121
                m_fileRef = TagLib::FileRef(fn);
85
132
                TagLib::FLAC::File* flacFile;
86
133
#ifdef MPC_ID3V1
87
134
                TagLib::MPC::File* mpcFile;
 
135
                TagLib::WavPack::File* wvFile;
88
136
#endif
 
137
                TagLib::TTA::File* ttaFile;
89
138
                if ((mpegFile = dynamic_cast<TagLib::MPEG::File*>(file)) != 0) {
90
139
                        if (!m_tagV1) {
91
140
                                m_tagV1 = mpegFile->ID3v1Tag();
114
163
                                m_tagV2 = mpcFile->APETag();
115
164
                                markTag2Changed(false);
116
165
                        }
 
166
                } else if ((wvFile = dynamic_cast<TagLib::WavPack::File*>(file)) != 0) {
 
167
                        if (!m_tagV1) {
 
168
                                m_tagV1 = wvFile->ID3v1Tag();
 
169
                                markTag1Changed(false);
 
170
                        }
 
171
                        if (!m_tagV2) {
 
172
                                m_tagV2 = wvFile->APETag();
 
173
                                markTag2Changed(false);
 
174
                        }
117
175
#endif
 
176
                } else if ((ttaFile = dynamic_cast<TagLib::TTA::File*>(file)) != 0) {
 
177
                        if (!m_tagV1) {
 
178
                                m_tagV1 = ttaFile->ID3v1Tag();
 
179
                                markTag1Changed(false);
 
180
                        }
 
181
                        if (!m_tagV2) {
 
182
                                m_tagV2 = ttaFile->ID3v2Tag();
 
183
                                markTag2Changed(false);
 
184
                        }
118
185
                } else {
119
186
                        m_tagV1 = 0;
120
187
                        markTag1Changed(false);
149
216
        }
150
217
 
151
218
        // store time stamp if it has to be preserved
152
 
        Q3CString fn;
 
219
        QCM_QCString fn;
153
220
        bool setUtime = false;
154
221
        struct utimbuf times;
155
222
        if (preserve) {
200
267
                } else {
201
268
                        if ((m_tagV2 && (force || isTag2Changed())) ||
202
269
                                        (m_tagV1 && (force || isTag1Changed()))) {
203
 
                                TagLib::MPC::File* mpcFile = dynamic_cast<TagLib::MPC::File*>(file);
 
270
                                TagLib::TTA::File* ttaFile = dynamic_cast<TagLib::TTA::File*>(file);
204
271
#ifndef MPC_ID3V1
205
272
                                // it does not work if there is also an ID3 tag (bug in TagLib?)
 
273
                                TagLib::MPC::File* mpcFile = dynamic_cast<TagLib::MPC::File*>(file);
 
274
                                TagLib::WavPack::File* wvFile = dynamic_cast<TagLib::WavPack::File*>(file);
206
275
                                if (mpcFile) {
207
276
                                        mpcFile->remove(TagLib::MPC::File::ID3v1 | TagLib::MPC::File::ID3v2);
208
277
                                        fileChanged = true;
 
278
                                } else if (wvFile) {
 
279
                                        wvFile->remove(TagLib::WavPack::File::ID3v1);
 
280
                                        fileChanged = true;
 
281
                                } else
 
282
#endif
 
283
                                if (ttaFile) {
 
284
                                        if (m_tagV1 && (force || isTag1Changed()) && m_tagV1->isEmpty()) {
 
285
                                                ttaFile->remove(TagLib::TTA::File::ID3v1);
 
286
                                                fileChanged = true;
 
287
                                                markTag1Changed(false);
 
288
                                                m_tagV1 = 0;
 
289
                                        }
 
290
                                        if (m_tagV2 && (force || isTag2Changed()) && m_tagV2->isEmpty()) {
 
291
                                                ttaFile->remove(TagLib::TTA::File::ID3v2);
 
292
                                                fileChanged = true;
 
293
                                                markTag2Changed(false);
 
294
                                                m_tagV2 = 0;
 
295
                                        }
209
296
                                }
210
 
#endif
211
297
                                if (m_fileRef.save()) {
212
298
                                        fileChanged = true;
213
299
                                        markTag1Changed(false);
254
340
}
255
341
 
256
342
/**
257
 
 * Remove all ID3v1 tags.
 
343
 * Remove ID3v1 frames.
258
344
 *
259
 
 * @param flt filter specifying which fields to remove
 
345
 * @param flt filter specifying which frames to remove
260
346
 */
261
 
void TagLibFile::removeTagsV1(const StandardTagsFilter& flt)
 
347
void TagLibFile::deleteFramesV1(const FrameFilter& flt)
262
348
{
263
349
        if (m_tagV1) {
264
 
                removeStandardTagsV1(flt);
265
 
        }
266
 
}
267
 
 
268
 
/**
269
 
 * Remove all ID3v2 tags.
270
 
 *
271
 
 * @param flt filter specifying which fields to remove
272
 
 */
273
 
void TagLibFile::removeTagsV2(const StandardTagsFilter& flt)
274
 
{
275
 
        if (m_tagV2) {
276
 
                TagLib::ID3v2::Tag* id3v2Tag;
277
 
                TagLib::Ogg::XiphComment* oggTag;
278
 
                TagLib::APE::Tag* apeTag;
279
 
                if (flt.areAllTrue()) {
280
 
                        if ((id3v2Tag = dynamic_cast<TagLib::ID3v2::Tag*>(m_tagV2)) != 0) {
281
 
                                const TagLib::ID3v2::FrameList& frameList = id3v2Tag->frameList();
282
 
                                for (TagLib::ID3v2::FrameList::ConstIterator it = frameList.begin();
283
 
                                                 it != frameList.end();) {
284
 
                                        id3v2Tag->removeFrame(*it++, true);
285
 
                                }
286
 
                                markTag2Changed();
287
 
                        } else if ((oggTag = dynamic_cast<TagLib::Ogg::XiphComment*>(m_tagV2)) !=
288
 
                                                                 0) {
289
 
                                const TagLib::Ogg::FieldListMap& fieldListMap = oggTag->fieldListMap();
290
 
                                for (TagLib::Ogg::FieldListMap::ConstIterator it = fieldListMap.begin();
291
 
                                                 it != fieldListMap.end();) {
292
 
                                        oggTag->removeField((*it++).first);
293
 
                                }
294
 
                                markTag2Changed();
295
 
                        } else if ((apeTag = dynamic_cast<TagLib::APE::Tag*>(m_tagV2)) != 0) {
296
 
                                const TagLib::APE::ItemListMap& itemListMap = apeTag->itemListMap();
297
 
                                for (TagLib::APE::ItemListMap::ConstIterator it = itemListMap.begin();
298
 
                                                 it != itemListMap.end();) {
299
 
                                        apeTag->removeItem((*it++).first);
300
 
                                }
301
 
                                markTag2Changed();
302
 
                        } else {
303
 
                                removeStandardTagsV2(flt);
304
 
                        }
305
 
                } else {
306
 
                        removeStandardTagsV2(flt);
307
 
                }
 
350
                TaggedFile::deleteFramesV1(flt);
308
351
        }
309
352
}
310
353
 
539
582
                QString qs = TStringToQString(str);
540
583
                int cpPos = 0, n = 0xff;
541
584
                bool ok = false;
542
 
                if (qs[0] == '(' && (cpPos = qs.find(')', 2)) > 1) {
 
585
                if (qs[0] == '(' && (cpPos = qs.QCM_indexOf(')', 2)) > 1) {
543
586
                        n = qs.mid(1, cpPos - 1).toInt(&ok);
544
587
                        if (!ok || n > 0xff) {
545
588
                                n = 0xff;
585
628
                        TagLib::FLAC::File* flacFile;
586
629
#ifdef MPC_ID3V1
587
630
                        TagLib::MPC::File* mpcFile;
 
631
                        TagLib::WavPack::File* wvFile;
588
632
#endif
 
633
                        TagLib::TTA::File* ttaFile;
589
634
                        if ((mpegFile = dynamic_cast<TagLib::MPEG::File*>(file)) != 0) {
590
635
                                m_tagV1 = mpegFile->ID3v1Tag(true);
591
636
                        } else if ((flacFile = dynamic_cast<TagLib::FLAC::File*>(file)) != 0) {
593
638
#ifdef MPC_ID3V1
594
639
                        } else if ((mpcFile = dynamic_cast<TagLib::MPC::File*>(file)) != 0) {
595
640
                                m_tagV1 = mpcFile->ID3v1Tag(true);
 
641
                        } else if ((wvFile = dynamic_cast<TagLib::WavPack::File*>(file)) != 0) {
 
642
                                m_tagV1 = wvFile->ID3v1Tag(true);
596
643
#endif
 
644
                        } else if ((ttaFile = dynamic_cast<TagLib::TTA::File*>(file)) != 0) {
 
645
                                m_tagV1 = ttaFile->ID3v1Tag(true);
597
646
                        }
598
647
                }
599
648
        }
613
662
                        TagLib::MPEG::File* mpegFile;
614
663
                        TagLib::FLAC::File* flacFile;
615
664
                        TagLib::MPC::File* mpcFile;
 
665
                        TagLib::WavPack::File* wvFile;
 
666
                        TagLib::TTA::File* ttaFile;
616
667
                        if ((mpegFile = dynamic_cast<TagLib::MPEG::File*>(file)) != 0) {
617
668
                                m_tagV2 = mpegFile->ID3v2Tag(true);
618
669
                        } else if ((flacFile = dynamic_cast<TagLib::FLAC::File*>(file)) != 0) {
619
670
                                m_tagV2 = flacFile->xiphComment(true);
620
671
                        } else if ((mpcFile = dynamic_cast<TagLib::MPC::File*>(file)) != 0) {
621
672
                                m_tagV2 = mpcFile->APETag(true);
 
673
                        } else if ((wvFile = dynamic_cast<TagLib::WavPack::File*>(file)) != 0) {
 
674
                                m_tagV2 = wvFile->APETag(true);
 
675
                        } else if ((ttaFile = dynamic_cast<TagLib::TTA::File*>(file)) != 0) {
 
676
                                m_tagV2 = ttaFile->ID3v2Tag(true);
622
677
                        }
623
678
                }
624
679
        }
634
689
{
635
690
        if (makeTagV1Settable() && !str.isNull()) {
636
691
                TagLib::String tstr = str.isEmpty() ?
637
 
                        TagLib::String::null : QStringToTString(str);
 
692
                        TagLib::String::null : QSTRING_TO_TSTRING(str);
638
693
                if (!(tstr == m_tagV1->title())) {
639
694
                        QString s = checkTruncation(str, StandardTags::TF_Title);
640
695
                        if (!s.isNull())
641
 
                                m_tagV1->setTitle(QStringToTString(s));
 
696
                                m_tagV1->setTitle(QSTRING_TO_TSTRING(s));
642
697
                        else
643
698
                                m_tagV1->setTitle(tstr);
644
699
                        markTag1Changed();
655
710
{
656
711
        if (makeTagV1Settable() && !str.isNull()) {
657
712
                TagLib::String tstr = str.isEmpty() ?
658
 
                        TagLib::String::null : QStringToTString(str);
 
713
                        TagLib::String::null : QSTRING_TO_TSTRING(str);
659
714
                if (!(tstr == m_tagV1->artist())) {
660
715
                        QString s = checkTruncation(str, StandardTags::TF_Artist);
661
716
                        if (!s.isNull())
662
 
                                m_tagV1->setArtist(QStringToTString(s));
 
717
                                m_tagV1->setArtist(QSTRING_TO_TSTRING(s));
663
718
                        else
664
719
                                m_tagV1->setArtist(tstr);
665
720
                        markTag1Changed();
676
731
{
677
732
        if (makeTagV1Settable() && !str.isNull()) {
678
733
                TagLib::String tstr = str.isEmpty() ?
679
 
                        TagLib::String::null : QStringToTString(str);
 
734
                        TagLib::String::null : QSTRING_TO_TSTRING(str);
680
735
                if (!(tstr == m_tagV1->album())) {
681
736
                        QString s = checkTruncation(str, StandardTags::TF_Album);
682
737
                        if (!s.isNull())
683
 
                                m_tagV1->setAlbum(QStringToTString(s));
 
738
                                m_tagV1->setAlbum(QSTRING_TO_TSTRING(s));
684
739
                        else
685
740
                                m_tagV1->setAlbum(tstr);
686
741
                        markTag1Changed();
697
752
{
698
753
        if (makeTagV1Settable() && !str.isNull()) {
699
754
                TagLib::String tstr = str.isEmpty() ?
700
 
                        TagLib::String::null : QStringToTString(str);
 
755
                        TagLib::String::null : QSTRING_TO_TSTRING(str);
701
756
                if (!(tstr == m_tagV1->comment())) {
702
757
                        QString s = checkTruncation(str, StandardTags::TF_Comment, 28);
703
758
                        if (!s.isNull())
704
 
                                m_tagV1->setComment(QStringToTString(s));
 
759
                                m_tagV1->setComment(QSTRING_TO_TSTRING(s));
705
760
                        else
706
761
                                m_tagV1->setComment(tstr);
707
762
                        markTag1Changed();
752
807
{
753
808
        if (makeTagV1Settable() && !str.isNull()) {
754
809
                TagLib::String tstr = str.isEmpty() ?
755
 
                        TagLib::String::null : QStringToTString(str);
 
810
                        TagLib::String::null : QSTRING_TO_TSTRING(str);
756
811
                if (!(tstr == m_tagV1->genre())) {
757
812
                        m_tagV1->setGenre(tstr);
758
813
                        markTag1Changed();
764
819
}
765
820
 
766
821
/**
 
822
 * Check if string needs Unicode encoding.
 
823
 *
 
824
 * @return true if Unicode needed,
 
825
 *         false if Latin-1 sufficient.
 
826
 */
 
827
static bool needsUnicode(const QString& qstr)
 
828
{
 
829
        bool result = false;
 
830
        uint unicodeSize = qstr.length();
 
831
        const QChar* qcarray = qstr.unicode();
 
832
        for (uint i = 0; i < unicodeSize; ++i) {
 
833
#if QT_VERSION >= 0x040000
 
834
                if (qcarray[i].toLatin1() == 0)
 
835
#else
 
836
                if (qcarray[i].latin1() == 0)
 
837
#endif
 
838
                {
 
839
                        result = true;
 
840
                        break;
 
841
                }
 
842
        }
 
843
        return result;
 
844
}
 
845
 
 
846
/**
 
847
 * Get the configured text encoding.
 
848
 *
 
849
 * @param unicode true if unicode is required
 
850
 *
 
851
 * @return text encoding.
 
852
 */
 
853
static TagLib::String::Type getTextEncodingConfig(bool unicode)
 
854
{
 
855
        TagLib::String::Type enc = TagLibFile::getDefaultTextEncoding();
 
856
        if (unicode && enc == TagLib::String::Latin1) {
 
857
                enc = TagLib::String::UTF16;
 
858
        }
 
859
        return enc;
 
860
}
 
861
 
 
862
/**
767
863
 * Write a Unicode field if the tag is ID3v2 and Latin-1 is not sufficient.
768
864
 *
769
865
 * @param tag     tag
778
874
        TagLib::ID3v2::Tag* id3v2Tag;
779
875
        if (tag && (id3v2Tag = dynamic_cast<TagLib::ID3v2::Tag*>(tag)) != 0) {
780
876
                // first check if this string needs to be stored as unicode
781
 
                bool needsUnicode = false;
782
 
                uint unicodeSize = qstr.length();
783
 
                const QChar* qcarray = qstr.unicode();
784
 
                for (uint i = 0; i < unicodeSize; ++i) {
785
 
                        if (qcarray[i].latin1() == 0) {
786
 
                                needsUnicode = true;
787
 
                                break;
788
 
                        }
789
 
                }
790
 
                if (needsUnicode) {
 
877
                TagLib::String::Type enc = getTextEncodingConfig(needsUnicode(qstr));
 
878
                if (enc != TagLib::String::Latin1) {
791
879
                        TagLib::ByteVector id(frameId);
792
880
                        id3v2Tag->removeFrames(id);
793
881
                        if (!tstr.isEmpty()) {
794
882
                                TagLib::ID3v2::Frame* frame;
795
883
                                if (frameId[0] != 'C') {
796
 
                                        frame = new TagLib::ID3v2::TextIdentificationFrame(id, TagLib::String::UTF16);
 
884
                                        frame = new TagLib::ID3v2::TextIdentificationFrame(id, enc);
797
885
                                } else {
798
 
                                        frame = new TagLib::ID3v2::CommentsFrame(TagLib::String::UTF16);
 
886
                                        frame = new TagLib::ID3v2::CommentsFrame(enc);
799
887
                                }
800
888
                                if (!frame) {
801
889
                                        return false;
828
916
{
829
917
        if (makeTagV2Settable() && !str.isNull()) {
830
918
                TagLib::String tstr = str.isEmpty() ?
831
 
                        TagLib::String::null : QStringToTString(str);
 
919
                        TagLib::String::null : QSTRING_TO_TSTRING(str);
832
920
                if (!(tstr == m_tagV2->title())) {
833
921
                        if (!setId3v2Unicode(m_tagV2, str, tstr, "TIT2")) {
834
922
                                m_tagV2->setTitle(tstr);
847
935
{
848
936
        if (makeTagV2Settable() && !str.isNull()) {
849
937
                TagLib::String tstr = str.isEmpty() ?
850
 
                        TagLib::String::null : QStringToTString(str);
 
938
                        TagLib::String::null : QSTRING_TO_TSTRING(str);
851
939
                if (!(tstr == m_tagV2->artist())) {
852
940
                        if (!setId3v2Unicode(m_tagV2, str, tstr, "TPE1")) {
 
941
                                m_tagV2->setArtist(tstr);
853
942
                        }
854
 
                        m_tagV2->setArtist(tstr);
855
943
                        markTag2Changed();
856
944
                }
857
945
        }
866
954
{
867
955
        if (makeTagV2Settable() && !str.isNull()) {
868
956
                TagLib::String tstr = str.isEmpty() ?
869
 
                        TagLib::String::null : QStringToTString(str);
 
957
                        TagLib::String::null : QSTRING_TO_TSTRING(str);
870
958
                if (!(tstr == m_tagV2->album())) {
871
959
                        if (!setId3v2Unicode(m_tagV2, str, tstr, "TALB")) {
 
960
                                m_tagV2->setAlbum(tstr);
872
961
                        }
873
 
                        m_tagV2->setAlbum(tstr);
874
962
                        markTag2Changed();
875
963
                }
876
964
        }
885
973
{
886
974
        if (makeTagV2Settable() && !str.isNull()) {
887
975
                TagLib::String tstr = str.isEmpty() ?
888
 
                        TagLib::String::null : QStringToTString(str);
 
976
                        TagLib::String::null : QSTRING_TO_TSTRING(str);
889
977
                if (!(tstr == m_tagV2->comment())) {
890
978
                        if (!setId3v2Unicode(m_tagV2, str, tstr, "COMM")) {
 
979
                                m_tagV2->setComment(tstr);
891
980
                        }
892
 
                        m_tagV2->setComment(tstr);
893
981
                        markTag2Changed();
894
982
                }
895
983
        }
904
992
{
905
993
        if (makeTagV2Settable() && num >= 0) {
906
994
                if (num != static_cast<int>(m_tagV2->year())) {
907
 
                        m_tagV2->setYear(num);
 
995
                        if (getDefaultTextEncoding() == TagLib::String::Latin1) {
 
996
                                m_tagV2->setYear(num);
 
997
                        } else {
 
998
                                QString str;
 
999
                                if (num != 0) {
 
1000
                                        str.setNum(num);
 
1001
                                } else {
 
1002
                                        str = "";
 
1003
                                }
 
1004
                                TagLib::String tstr = str.isEmpty() ?
 
1005
                                        TagLib::String::null : QSTRING_TO_TSTRING(str);
 
1006
                                if (!setId3v2Unicode(m_tagV2, str, tstr, "TDRC")) {
 
1007
                                        m_tagV2->setYear(num);
 
1008
                                }
 
1009
                        }
908
1010
                        markTag2Changed();
909
1011
                }
910
1012
        }
926
1028
                                        (numTracks = getTotalNumberOfTracksIfEnabled()) > 0 &&
927
1029
                                        num > 0 &&
928
1030
                                        (frame = new TagLib::ID3v2::TextIdentificationFrame(
929
 
                                                "TRCK", TagLib::String::Latin1)) != 0) {
 
1031
                                                "TRCK", getDefaultTextEncoding())) != 0) {
930
1032
                                TagLib::String str = TagLib::String::number(num);
931
1033
                                str += '/';
932
1034
                                str += TagLib::String::number(numTracks);
944
1046
                                id3v2Tag->addFrame(frame);
945
1047
#endif
946
1048
                        } else {
947
 
                                m_tagV2->setTrack(num);
 
1049
                                if (getDefaultTextEncoding() == TagLib::String::Latin1) {
 
1050
                                        m_tagV2->setTrack(num);
 
1051
                                } else {
 
1052
                                        QString str;
 
1053
                                        if (num != 0) {
 
1054
                                                str.setNum(num);
 
1055
                                        } else {
 
1056
                                                str = "";
 
1057
                                        }
 
1058
                                        TagLib::String tstr = str.isEmpty() ?
 
1059
                                                TagLib::String::null : QSTRING_TO_TSTRING(str);
 
1060
                                        if (!setId3v2Unicode(m_tagV2, str, tstr, "TRCK")) {
 
1061
                                                m_tagV2->setTrack(num);
 
1062
                                        }
 
1063
                                }
948
1064
                        }
949
1065
                        markTag2Changed();
950
1066
                }
960
1076
{
961
1077
        if (makeTagV2Settable() && !str.isNull()) {
962
1078
                TagLib::String tstr = str.isEmpty() ?
963
 
                        TagLib::String::null : QStringToTString(str);
 
1079
                        TagLib::String::null : QSTRING_TO_TSTRING(str);
964
1080
                if (!(tstr == m_tagV2->genre())) {
965
 
                        m_tagV2->setGenre(tstr);
 
1081
                        if (!setId3v2Unicode(m_tagV2, str, tstr, "TCON")) {
 
1082
                                m_tagV2->setGenre(tstr);
 
1083
                        }
966
1084
                        markTag2Changed();
967
1085
                }
968
1086
        }
1001
1119
        TagLib::File* file;
1002
1120
        return (!m_fileRef.isNull() && (file = m_fileRef.file()) != 0 &&
1003
1121
                                        (dynamic_cast<TagLib::MPEG::File*>(file) != 0 ||
1004
 
                                         dynamic_cast<TagLib::FLAC::File*>(file) != 0
 
1122
                                         dynamic_cast<TagLib::FLAC::File*>(file) != 0 ||
 
1123
                                         dynamic_cast<TagLib::TTA::File*>(file) != 0
1005
1124
#ifdef MPC_ID3V1
1006
1125
                                         || dynamic_cast<TagLib::MPC::File*>(file)  != 0
 
1126
                                         || dynamic_cast<TagLib::WavPack::File*>(file) != 0
1007
1127
#endif
1008
1128
                                                ));
1009
1129
}
1035
1155
                TagLib::Vorbis::Properties* oggProperties;
1036
1156
                TagLib::FLAC::Properties* flacProperties;
1037
1157
                TagLib::MPC::Properties* mpcProperties;
 
1158
                TagLib::Speex::Properties* speexProperties;
 
1159
                TagLib::TTA::Properties* ttaProperties;
 
1160
                TagLib::WavPack::Properties* wvProperties;
1038
1161
                if ((mpegProperties =
1039
1162
                                 dynamic_cast<TagLib::MPEG::Properties*>(audioProperties)) != 0) {
 
1163
                        if (getFilename().right(4).QCM_toLower() == ".aac") {
 
1164
                                return "AAC";
 
1165
                        }
1040
1166
                        switch (mpegProperties->version()) {
1041
1167
                                case TagLib::MPEG::Header::Version1:
1042
1168
                                        str += "MPEG 1 ";
1080
1206
                } else if ((mpcProperties =
1081
1207
                                                                dynamic_cast<TagLib::MPC::Properties*>(audioProperties)) != 0) {
1082
1208
                        str += "MPC ";
 
1209
                } else if ((speexProperties =
 
1210
                                                                dynamic_cast<TagLib::Speex::Properties*>(audioProperties)) != 0) {
 
1211
                        str += QString("Speex %1 ").arg(speexProperties->speexVersion());
 
1212
                } else if ((ttaProperties =
 
1213
                                                                dynamic_cast<TagLib::TTA::Properties*>(audioProperties)) != 0) {
 
1214
                        str += "True Audio ";
 
1215
                        str += QString::number(ttaProperties->ttaVersion());
 
1216
                        str += " ";
 
1217
                        str += QString::number(ttaProperties->bitsPerSample());
 
1218
                        str += " bit ";
 
1219
                } else if ((wvProperties =
 
1220
                                                                dynamic_cast<TagLib::WavPack::Properties*>(audioProperties)) != 0) {
 
1221
                        str += "WavPack ";
 
1222
                        str += QString::number(wvProperties->version(), 16);
 
1223
                        str += " ";
 
1224
                        str += QString::number(wvProperties->bitsPerSample());
 
1225
                        str += " bit ";
1083
1226
                }
1084
1227
                int bitrate = audioProperties->bitrate();
1085
1228
                if (bitrate > 0 && bitrate < 999) {
1123
1266
                audioProperties->length() : 0;
1124
1267
}
1125
1268
 
1126
 
/** Frame list for MP3 files. */
1127
 
TagLibFrameList* TagLibFile::s_tagLibFrameList = 0;
1128
 
 
1129
 
/**
1130
 
 * Get frame list for this type of tagged file.
1131
 
 *
1132
 
 * @return frame list.
1133
 
 */
1134
 
FrameList* TagLibFile::getFrameList() const
1135
 
{
1136
 
        if (!s_tagLibFrameList) {
1137
 
                s_tagLibFrameList = new TagLibFrameList();
1138
 
        }
1139
 
        return s_tagLibFrameList;
1140
 
}
1141
 
 
1142
1269
/**
1143
1270
 * Get file extension including the dot.
1144
1271
 *
1158
1285
                        return ".flac";
1159
1286
                } else if (dynamic_cast<TagLib::MPC::File*>(file) != 0) {
1160
1287
                        return ".mpc";
 
1288
                } else if (dynamic_cast<TagLib::Speex::File*>(file) != 0) {
 
1289
                        return ".spx";
 
1290
                } else if (dynamic_cast<TagLib::WavPack::File*>(file) != 0) {
 
1291
                        return ".wv";
 
1292
                } else if (dynamic_cast<TagLib::TTA::File*>(file) != 0) {
 
1293
                        return ".tta";
1161
1294
                }
1162
1295
        }
1163
1296
        return ".mp3";
1230
1363
        return getTagFormat(m_tagV2);
1231
1364
}
1232
1365
 
1233
 
/**
1234
 
 * Clean up static resources.
1235
 
 */
1236
 
void TagLibFile::staticCleanup()
1237
 
{
1238
 
        delete s_tagLibFrameList;
1239
 
        s_tagLibFrameList = 0;
 
1366
 
 
1367
/** Types and descriptions for id3lib frame IDs */
 
1368
static const struct TypeStrOfId {
 
1369
        Frame::Type type;
 
1370
        const char* str;
 
1371
        bool supported;
 
1372
} typeStrOfId[] = {
 
1373
        { Frame::FT_Other,          I18N_NOOP("AENC - Audio encryption"), false },
 
1374
        { Frame::FT_Other,          I18N_NOOP("APIC - Attached picture"), true },
 
1375
        { Frame::FT_Other,          I18N_NOOP("ASPI - Audio seek point index"), false },
 
1376
        { Frame::FT_Comment,        I18N_NOOP("COMM - Comments"), true },
 
1377
        { Frame::FT_Other,          I18N_NOOP("COMR - Commercial"), false },
 
1378
        { Frame::FT_Other,          I18N_NOOP("ENCR - Encryption method registration"), false },
 
1379
        { Frame::FT_Other,          I18N_NOOP("EQU2 - Equalisation (2)"), false },
 
1380
        { Frame::FT_Other,          I18N_NOOP("ETCO - Event timing codes"), false },
 
1381
        { Frame::FT_Other,          I18N_NOOP("GEOB - General encapsulated object"), true },
 
1382
        { Frame::FT_Other,          I18N_NOOP("GRID - Group identification registration"), false },
 
1383
        { Frame::FT_Other,          I18N_NOOP("LINK - Linked information"), false },
 
1384
        { Frame::FT_Other,          I18N_NOOP("MCDI - Music CD identifier"), false },
 
1385
        { Frame::FT_Other,          I18N_NOOP("MLLT - MPEG location lookup table"), false },
 
1386
        { Frame::FT_Other,          I18N_NOOP("OWNE - Ownership frame"), false },
 
1387
        { Frame::FT_Other,          I18N_NOOP("PRIV - Private frame"), false },
 
1388
        { Frame::FT_Other,          I18N_NOOP("PCNT - Play counter"), false },
 
1389
        { Frame::FT_Other,          I18N_NOOP("POPM - Popularimeter"), false },
 
1390
        { Frame::FT_Other,          I18N_NOOP("POSS - Position synchronisation frame"), false },
 
1391
        { Frame::FT_Other,          I18N_NOOP("RBUF - Recommended buffer size"), false },
 
1392
        { Frame::FT_Other,          I18N_NOOP("RVA2 - Relative volume adjustment (2)"), false },
 
1393
        { Frame::FT_Other,          I18N_NOOP("RVRB - Reverb"), false },
 
1394
        { Frame::FT_Other,          I18N_NOOP("SEEK - Seek frame"), false },
 
1395
        { Frame::FT_Other,          I18N_NOOP("SIGN - Signature frame"), false },
 
1396
        { Frame::FT_Other,          I18N_NOOP("SYLT - Synchronized lyric/text"), false },
 
1397
        { Frame::FT_Other,          I18N_NOOP("SYTC - Synchronized tempo codes"), false },
 
1398
        { Frame::FT_Album,          I18N_NOOP("TALB - Album/Movie/Show title"), true },
 
1399
        { Frame::FT_Bpm,            I18N_NOOP("TBPM - BPM (beats per minute)"), true },
 
1400
        { Frame::FT_Composer,       I18N_NOOP("TCOM - Composer"), true },
 
1401
        { Frame::FT_Genre,          I18N_NOOP("TCON - Content type"), true },
 
1402
        { Frame::FT_Copyright,      I18N_NOOP("TCOP - Copyright message"), true },
 
1403
        { Frame::FT_Other,          I18N_NOOP("TDEN - Encoding time"), true },
 
1404
        { Frame::FT_Other,          I18N_NOOP("TDLY - Playlist delay"), true },
 
1405
        { Frame::FT_OriginalDate,   I18N_NOOP("TDOR - Original release time"), true },
 
1406
        { Frame::FT_Date,           I18N_NOOP("TDRC - Recording time"), true },
 
1407
        { Frame::FT_Other,          I18N_NOOP("TDRL - Release time"), true },
 
1408
        { Frame::FT_Other,          I18N_NOOP("TDTG - Tagging time"), true },
 
1409
        { Frame::FT_EncodedBy,      I18N_NOOP("TENC - Encoded by"), true },
 
1410
        { Frame::FT_Lyricist,       I18N_NOOP("TEXT - Lyricist/Text writer"), true },
 
1411
        { Frame::FT_Other,          I18N_NOOP("TFLT - File type"), true },
 
1412
        { Frame::FT_Other,          I18N_NOOP("TIPL - Involved people list"), true },
 
1413
        { Frame::FT_Other,          I18N_NOOP("TIT1 - Content group description"), true },
 
1414
        { Frame::FT_Title,          I18N_NOOP("TIT2 - Title/songname/content description"), true },
 
1415
        { Frame::FT_Subtitle,       I18N_NOOP("TIT3 - Subtitle/Description refinement"), true },
 
1416
        { Frame::FT_Other,          I18N_NOOP("TKEY - Initial key"), true },
 
1417
        { Frame::FT_Language,       I18N_NOOP("TLAN - Language(s)"), true },
 
1418
        { Frame::FT_Other,          I18N_NOOP("TLEN - Length"), true },
 
1419
        { Frame::FT_Other,          I18N_NOOP("TMCL - Musician credits list"), true },
 
1420
        { Frame::FT_Other,          I18N_NOOP("TMED - Media type"), true },
 
1421
        { Frame::FT_Other,          I18N_NOOP("TMOO - Mood"), true },
 
1422
        { Frame::FT_OriginalAlbum,  I18N_NOOP("TOAL - Original album/movie/show title"), true },
 
1423
        { Frame::FT_Other,          I18N_NOOP("TOFN - Original filename"), true },
 
1424
        { Frame::FT_Author,         I18N_NOOP("TOLY - Original lyricist(s)/text writer(s)"), true },
 
1425
        { Frame::FT_OriginalArtist, I18N_NOOP("TOPE - Original artist(s)/performer(s)"), true },
 
1426
        { Frame::FT_Other,          I18N_NOOP("TOWN - File owner/licensee"), true },
 
1427
        { Frame::FT_Artist,         I18N_NOOP("TPE1 - Lead performer(s)/Soloist(s)"), true },
 
1428
        { Frame::FT_Performer,      I18N_NOOP("TPE2 - Band/orchestra/accompaniment"), true },
 
1429
        { Frame::FT_Conductor,      I18N_NOOP("TPE3 - Conductor/performer refinement"), true },
 
1430
        { Frame::FT_Arranger,       I18N_NOOP("TPE4 - Interpreted, remixed, or otherwise modified by"), true },
 
1431
        { Frame::FT_Disc,           I18N_NOOP("TPOS - Part of a set"), true },
 
1432
        { Frame::FT_Other,          I18N_NOOP("TPRO - Produced notice"), true },
 
1433
        { Frame::FT_Publisher,      I18N_NOOP("TPUB - Publisher"), true },
 
1434
        { Frame::FT_Track,          I18N_NOOP("TRCK - Track number/Position in set"), true },
 
1435
        { Frame::FT_Other,          I18N_NOOP("TRSN - Internet radio station name"), true },
 
1436
        { Frame::FT_Other,          I18N_NOOP("TRSO - Internet radio station owner"), true },
 
1437
        { Frame::FT_Other,          I18N_NOOP("TSOA - Album sort order"), true },
 
1438
        { Frame::FT_Other,          I18N_NOOP("TSOP - Performer sort order"), true },
 
1439
        { Frame::FT_Other,          I18N_NOOP("TSOT - Title sort order"), true },
 
1440
        { Frame::FT_Isrc,           I18N_NOOP("TSRC - ISRC (international standard recording code)"), true },
 
1441
        { Frame::FT_Other,          I18N_NOOP("TSSE - Software/Hardware and settings used for encoding"), true },
 
1442
        { Frame::FT_Part,           I18N_NOOP("TSST - Set subtitle"), true },
 
1443
        { Frame::FT_Other,          I18N_NOOP("TXXX - User defined text information"), true },
 
1444
        { Frame::FT_Other,          I18N_NOOP("UFID - Unique file identifier"), true },
 
1445
        { Frame::FT_Other,          I18N_NOOP("USER - Terms of use"), false },
 
1446
        { Frame::FT_Other,          I18N_NOOP("USLT - Unsynchronized lyric/text transcription"), true },
 
1447
        { Frame::FT_Other,          I18N_NOOP("WCOM - Commercial information"), true },
 
1448
        { Frame::FT_Other,          I18N_NOOP("WCOP - Copyright/Legal information"), true },
 
1449
        { Frame::FT_Other,          I18N_NOOP("WOAF - Official audio file webpage"), true },
 
1450
        { Frame::FT_Website,        I18N_NOOP("WOAR - Official artist/performer webpage"), true },
 
1451
        { Frame::FT_Other,          I18N_NOOP("WOAS - Official audio source webpage"), true },
 
1452
        { Frame::FT_Other,          I18N_NOOP("WORS - Official internet radio station homepage"), true },
 
1453
        { Frame::FT_Other,          I18N_NOOP("WPAY - Payment"), true },
 
1454
        { Frame::FT_Other,          I18N_NOOP("WPUB - Official publisher webpage"), true },
 
1455
        { Frame::FT_Other,          I18N_NOOP("WXXX - User defined URL link"), true }
 
1456
};
 
1457
 
 
1458
/**
 
1459
 * Get type and description of frame.
 
1460
 *
 
1461
 * @param id   ID of frame
 
1462
 * @param type the type is returned here
 
1463
 * @param str  the description is returned here
 
1464
 */
 
1465
static void getTypeStringForFrameId(TagLib::ByteVector id, Frame::Type& type,
 
1466
                                                                                                                                                const char*& str)
 
1467
{
 
1468
        static TagLib::Map<TagLib::ByteVector, unsigned> idIndexMap;
 
1469
        if (idIndexMap.isEmpty()) {
 
1470
                for (unsigned i = 0;
 
1471
                                 i < sizeof(typeStrOfId) / sizeof(typeStrOfId[0]);
 
1472
                                 ++i) {
 
1473
                        idIndexMap.insert(TagLib::ByteVector(typeStrOfId[i].str, 4), i);
 
1474
                }
 
1475
        }
 
1476
        if (idIndexMap.contains(id)) {
 
1477
                const TypeStrOfId& ts = typeStrOfId[idIndexMap[id]];
 
1478
                type = ts.type;
 
1479
                str = ts.str;
 
1480
        } else {
 
1481
                type = Frame::FT_UnknownFrame;
 
1482
                str = "????";
 
1483
        }
 
1484
}
 
1485
 
 
1486
/**
 
1487
 * Get string description starting with 4 bytes ID.
 
1488
 *
 
1489
 * @param type type of frame
 
1490
 *
 
1491
 * @return string.
 
1492
 */
 
1493
static const char* getStringForType(Frame::Type type)
 
1494
{
 
1495
        if (type != Frame::FT_Other) {
 
1496
                for (unsigned i = 0;
 
1497
                                 i < sizeof(typeStrOfId) / sizeof(typeStrOfId[0]);
 
1498
                                 ++i) {
 
1499
                        const TypeStrOfId& ts = typeStrOfId[i];
 
1500
                        if (ts.type == type) {
 
1501
                                return ts.str;
 
1502
                        }
 
1503
                }
 
1504
        }
 
1505
        return "????";
 
1506
}
 
1507
 
 
1508
/**
 
1509
 * Get the fields from a text identification frame.
 
1510
 *
 
1511
 * @param tFrame text identification frame
 
1512
 * @param fields the fields are appended to this list
 
1513
 * @param type   frame type
 
1514
 *
 
1515
 * @return text representation of fields (Text or URL).
 
1516
 */
 
1517
static QString getFieldsFromTextFrame(
 
1518
        const TagLib::ID3v2::TextIdentificationFrame* tFrame,
 
1519
        Frame::FieldList& fields, Frame::Type type)
 
1520
{
 
1521
        QString text;
 
1522
        Frame::Field field;
 
1523
        field.m_id = Frame::Field::ID_TextEnc;
 
1524
        field.m_value = tFrame->textEncoding();
 
1525
        fields.push_back(field);
 
1526
 
 
1527
        const TagLib::ID3v2::UserTextIdentificationFrame* txxxFrame;
 
1528
        if (tFrame &&
 
1529
                        (txxxFrame =
 
1530
                         dynamic_cast<const TagLib::ID3v2::UserTextIdentificationFrame*>(tFrame))
 
1531
                        != 0) {
 
1532
                field.m_id = Frame::Field::ID_Description;
 
1533
                field.m_value = TStringToQString(txxxFrame->description());
 
1534
                fields.push_back(field);
 
1535
 
 
1536
                TagLib::StringList slText = tFrame->fieldList();
 
1537
                text = slText.size() > 1 ? TStringToQString(slText[1]) : "";
 
1538
        } else {
 
1539
                text = TStringToQString(tFrame->toString());
 
1540
        }
 
1541
        field.m_id = Frame::Field::ID_Text;
 
1542
        if (type == Frame::FT_Genre) {
 
1543
                text = Genres::getNameString(text);
 
1544
        }
 
1545
        field.m_value = text;
 
1546
        fields.push_back(field);
 
1547
 
 
1548
        return text;
 
1549
}
 
1550
 
 
1551
/**
 
1552
 * Get the fields from an attached picture frame.
 
1553
 *
 
1554
 * @param apicFrame attached picture frame
 
1555
 * @param fields the fields are appended to this list
 
1556
 *
 
1557
 * @return text representation of fields (Text or URL).
 
1558
 */
 
1559
static QString getFieldsFromApicFrame(
 
1560
        const TagLib::ID3v2::AttachedPictureFrame* apicFrame,
 
1561
        Frame::FieldList& fields)
 
1562
{
 
1563
        QString text;
 
1564
        Frame::Field field;
 
1565
        field.m_id = Frame::Field::ID_TextEnc;
 
1566
        field.m_value = apicFrame->textEncoding();
 
1567
        fields.push_back(field);
 
1568
 
 
1569
        // for compatibility with ID3v2.3 id3lib
 
1570
        field.m_id = Frame::Field::ID_ImageFormat;
 
1571
        field.m_value = QString("");
 
1572
        fields.push_back(field);
 
1573
 
 
1574
        field.m_id = Frame::Field::ID_MimeType;
 
1575
        field.m_value = TStringToQString(apicFrame->mimeType());
 
1576
        fields.push_back(field);
 
1577
 
 
1578
        field.m_id = Frame::Field::ID_PictureType;
 
1579
        field.m_value = apicFrame->type();
 
1580
        fields.push_back(field);
 
1581
 
 
1582
        field.m_id = Frame::Field::ID_Description;
 
1583
        text = TStringToQString(apicFrame->description());
 
1584
        field.m_value = text;
 
1585
        fields.push_back(field);
 
1586
 
 
1587
        field.m_id = Frame::Field::ID_Data;
 
1588
        TagLib::ByteVector pic = apicFrame->picture();
 
1589
        QByteArray ba;
 
1590
        QCM_duplicate(ba, pic.data(), pic.size());
 
1591
        field.m_value = ba;
 
1592
        fields.push_back(field);
 
1593
 
 
1594
        return text;
 
1595
}
 
1596
 
 
1597
/**
 
1598
 * Get the fields from a comments frame.
 
1599
 *
 
1600
 * @param commFrame comments frame
 
1601
 * @param fields the fields are appended to this list
 
1602
 *
 
1603
 * @return text representation of fields (Text or URL).
 
1604
 */
 
1605
static QString getFieldsFromCommFrame(
 
1606
        const TagLib::ID3v2::CommentsFrame* commFrame, Frame::FieldList& fields)
 
1607
{
 
1608
        QString text;
 
1609
        Frame::Field field;
 
1610
        field.m_id = Frame::Field::ID_TextEnc;
 
1611
        field.m_value = commFrame->textEncoding();
 
1612
        fields.push_back(field);
 
1613
 
 
1614
        field.m_id = Frame::Field::ID_Language;
 
1615
        TagLib::ByteVector bvLang = commFrame->language();
 
1616
        field.m_value = QString(QCM_QCString(bvLang.data(), bvLang.size() + 1));
 
1617
        fields.push_back(field);
 
1618
 
 
1619
        field.m_id = Frame::Field::ID_Description;
 
1620
        field.m_value = TStringToQString(commFrame->description());
 
1621
        fields.push_back(field);
 
1622
 
 
1623
        field.m_id = Frame::Field::ID_Text;
 
1624
        text = TStringToQString(commFrame->toString());
 
1625
        field.m_value = text;
 
1626
        fields.push_back(field);
 
1627
 
 
1628
        return text;
 
1629
}
 
1630
 
 
1631
/**
 
1632
 * Get the fields from a unique file identifier frame.
 
1633
 *
 
1634
 * @param ufidFrame unique file identifier frame
 
1635
 * @param fields the fields are appended to this list
 
1636
 *
 
1637
 * @return text representation of fields (Text or URL).
 
1638
 */
 
1639
static QString getFieldsFromUfidFrame(
 
1640
        const TagLib::ID3v2::UniqueFileIdentifierFrame* ufidFrame,
 
1641
        Frame::FieldList& fields)
 
1642
{
 
1643
        QString text;
 
1644
        Frame::Field field;
 
1645
        field.m_id = Frame::Field::ID_Owner;
 
1646
        field.m_value = TStringToQString(ufidFrame->owner());
 
1647
        fields.push_back(field);
 
1648
 
 
1649
        field.m_id = Frame::Field::ID_Id;
 
1650
        TagLib::ByteVector id = ufidFrame->identifier();
 
1651
        QByteArray ba;
 
1652
        QCM_duplicate(ba, id.data(), id.size());
 
1653
        field.m_value = ba;
 
1654
        fields.push_back(field);
 
1655
 
 
1656
        return text;
 
1657
}
 
1658
 
 
1659
/**
 
1660
 * Get the fields from a general encapsulated object frame.
 
1661
 *
 
1662
 * @param geobFrame general encapsulated object frame
 
1663
 * @param fields the fields are appended to this list
 
1664
 *
 
1665
 * @return text representation of fields (Text or URL).
 
1666
 */
 
1667
static QString getFieldsFromGeobFrame(
 
1668
        const TagLib::ID3v2::GeneralEncapsulatedObjectFrame* geobFrame,
 
1669
        Frame::FieldList& fields)
 
1670
{
 
1671
        QString text;
 
1672
        Frame::Field field;
 
1673
        field.m_id = Frame::Field::ID_TextEnc;
 
1674
        field.m_value = geobFrame->textEncoding();
 
1675
        fields.push_back(field);
 
1676
 
 
1677
        field.m_id = Frame::Field::ID_MimeType;
 
1678
        field.m_value = TStringToQString(geobFrame->mimeType());
 
1679
        fields.push_back(field);
 
1680
 
 
1681
        field.m_id = Frame::Field::ID_Filename;
 
1682
        field.m_value = TStringToQString(geobFrame->fileName());
 
1683
        fields.push_back(field);
 
1684
 
 
1685
        field.m_id = Frame::Field::ID_Description;
 
1686
        text = TStringToQString(geobFrame->description());
 
1687
        field.m_value = text;
 
1688
        fields.push_back(field);
 
1689
 
 
1690
        field.m_id = Frame::Field::ID_Data;
 
1691
        TagLib::ByteVector obj = geobFrame->object();
 
1692
        QByteArray ba;
 
1693
        QCM_duplicate(ba, obj.data(), obj.size());
 
1694
        field.m_value = ba;
 
1695
        fields.push_back(field);
 
1696
 
 
1697
        return text;
 
1698
}
 
1699
 
 
1700
/**
 
1701
 * Get the fields from a URL link frame.
 
1702
 *
 
1703
 * @param wFrame URL link frame
 
1704
 * @param fields the fields are appended to this list
 
1705
 *
 
1706
 * @return text representation of fields (Text or URL).
 
1707
 */
 
1708
static QString getFieldsFromUrlFrame(
 
1709
        const TagLib::ID3v2::UrlLinkFrame* wFrame, Frame::FieldList& fields)
 
1710
{
 
1711
        QString text;
 
1712
        Frame::Field field;
 
1713
        field.m_id = Frame::Field::ID_Url;
 
1714
        text = TStringToQString(wFrame->url());
 
1715
        field.m_value = text;
 
1716
        fields.push_back(field);
 
1717
 
 
1718
        return text;
 
1719
}
 
1720
 
 
1721
/**
 
1722
 * Get the fields from a user URL link frame.
 
1723
 *
 
1724
 * @param wxxxFrame user URL link frame
 
1725
 * @param fields the fields are appended to this list
 
1726
 *
 
1727
 * @return text representation of fields (Text or URL).
 
1728
 */
 
1729
static QString getFieldsFromUserUrlFrame(
 
1730
        const TagLib::ID3v2::UserUrlLinkFrame* wxxxFrame, Frame::FieldList& fields)
 
1731
{
 
1732
        QString text;
 
1733
        Frame::Field field;
 
1734
        field.m_id = Frame::Field::ID_TextEnc;
 
1735
        field.m_value = wxxxFrame->textEncoding();
 
1736
        fields.push_back(field);
 
1737
 
 
1738
        field.m_id = Frame::Field::ID_Description;
 
1739
        field.m_value = TStringToQString(wxxxFrame->description());
 
1740
        fields.push_back(field);
 
1741
 
 
1742
        field.m_id = Frame::Field::ID_Url;
 
1743
        text = TStringToQString(wxxxFrame->url());
 
1744
        field.m_value = text;
 
1745
        fields.push_back(field);
 
1746
 
 
1747
        return text;
 
1748
}
 
1749
 
 
1750
/**
 
1751
 * Get the fields from an unsynchronized lyrics frame.
 
1752
 * This is copy-pasted from editCommFrame().
 
1753
 *
 
1754
 * @param usltFrame unsynchronized frame
 
1755
 * @param fields the fields are appended to this list
 
1756
 *
 
1757
 * @return text representation of fields (Text or URL).
 
1758
 */
 
1759
static QString getFieldsFromUsltFrame(
 
1760
        const TagLib::ID3v2::UnsynchronizedLyricsFrame* usltFrame,
 
1761
        Frame::FieldList& fields)
 
1762
{
 
1763
        QString text;
 
1764
        Frame::Field field;
 
1765
        field.m_id = Frame::Field::ID_TextEnc;
 
1766
        field.m_value = usltFrame->textEncoding();
 
1767
        fields.push_back(field);
 
1768
 
 
1769
        field.m_id = Frame::Field::ID_Language;
 
1770
        TagLib::ByteVector bvLang = usltFrame->language();
 
1771
        field.m_value = QString(QCM_QCString(bvLang.data(), bvLang.size() + 1));
 
1772
        fields.push_back(field);
 
1773
 
 
1774
        field.m_id = Frame::Field::ID_Description;
 
1775
        field.m_value = TStringToQString(usltFrame->description());
 
1776
        fields.push_back(field);
 
1777
 
 
1778
        field.m_id = Frame::Field::ID_Text;
 
1779
        text = TStringToQString(usltFrame->toString());
 
1780
        field.m_value = text;
 
1781
        fields.push_back(field);
 
1782
 
 
1783
        return text;
 
1784
}
 
1785
 
 
1786
/**
 
1787
 * Get the fields from an unknown frame.
 
1788
 *
 
1789
 * @param unknownFrame unknown frame
 
1790
 * @param fields the fields are appended to this list
 
1791
 *
 
1792
 * @return text representation of fields (Text or URL).
 
1793
 */
 
1794
static QString getFieldsFromUnknownFrame(
 
1795
        const TagLib::ID3v2::Frame* unknownFrame, Frame::FieldList& fields)
 
1796
{
 
1797
        Frame::Field field;
 
1798
        field.m_id = Frame::Field::ID_Data;
 
1799
        TagLib::ByteVector dat = unknownFrame->render();
 
1800
        QByteArray ba;
 
1801
        QCM_duplicate(ba, dat.data(), dat.size());
 
1802
        field.m_value = ba;
 
1803
        fields.push_back(field);
 
1804
        return QString();
 
1805
}
 
1806
 
 
1807
/**
 
1808
 * Get the fields from an ID3v2 tag.
 
1809
 *
 
1810
 * @param frame  frame
 
1811
 * @param fields the fields are appended to this list
 
1812
 * @param type   frame type
 
1813
 *
 
1814
 * @return text representation of fields (Text or URL).
 
1815
 */
 
1816
static QString getFieldsFromId3Frame(const TagLib::ID3v2::Frame* frame,
 
1817
                                                                                                                                                 Frame::FieldList& fields, Frame::Type type)
 
1818
{
 
1819
        if (frame) {
 
1820
                const TagLib::ID3v2::TextIdentificationFrame* tFrame;
 
1821
                const TagLib::ID3v2::AttachedPictureFrame* apicFrame;
 
1822
                const TagLib::ID3v2::CommentsFrame* commFrame;
 
1823
                const TagLib::ID3v2::UniqueFileIdentifierFrame* ufidFrame;
 
1824
                const TagLib::ID3v2::GeneralEncapsulatedObjectFrame* geobFrame;
 
1825
                const TagLib::ID3v2::UserUrlLinkFrame* wxxxFrame;
 
1826
                const TagLib::ID3v2::UrlLinkFrame* wFrame;
 
1827
                const TagLib::ID3v2::UnsynchronizedLyricsFrame* usltFrame;
 
1828
                if ((tFrame =
 
1829
                                 dynamic_cast<const TagLib::ID3v2::TextIdentificationFrame*>(frame)) !=
 
1830
                                0) {
 
1831
                        return getFieldsFromTextFrame(tFrame, fields, type);
 
1832
                } else if ((apicFrame =
 
1833
                                                                dynamic_cast<const TagLib::ID3v2::AttachedPictureFrame*>(frame))
 
1834
                                                         != 0) {
 
1835
                        return getFieldsFromApicFrame(apicFrame, fields);
 
1836
                } else if ((commFrame = dynamic_cast<const TagLib::ID3v2::CommentsFrame*>(
 
1837
                                                                        frame)) != 0) {
 
1838
                        return getFieldsFromCommFrame(commFrame, fields);
 
1839
                } else if ((ufidFrame =
 
1840
                                                                dynamic_cast<const TagLib::ID3v2::UniqueFileIdentifierFrame*>(
 
1841
                                                                        frame)) != 0) {
 
1842
                        return getFieldsFromUfidFrame(ufidFrame, fields);
 
1843
                } else if ((geobFrame =
 
1844
                                                                dynamic_cast<const TagLib::ID3v2::GeneralEncapsulatedObjectFrame*>(
 
1845
                                                                        frame)) != 0) {
 
1846
                        return getFieldsFromGeobFrame(geobFrame, fields);
 
1847
                } else if ((wxxxFrame = dynamic_cast<const TagLib::ID3v2::UserUrlLinkFrame*>(
 
1848
                                                                        frame)) != 0) {
 
1849
                        return getFieldsFromUserUrlFrame(wxxxFrame, fields);
 
1850
                } else if ((wFrame = dynamic_cast<const TagLib::ID3v2::UrlLinkFrame*>(
 
1851
                                                                        frame)) != 0) {
 
1852
                        return getFieldsFromUrlFrame(wFrame, fields);
 
1853
                } else if ((usltFrame = dynamic_cast<const TagLib::ID3v2::UnsynchronizedLyricsFrame*>(
 
1854
                                                                frame)) != 0) {
 
1855
                        return getFieldsFromUsltFrame(usltFrame, fields);
 
1856
                } else {
 
1857
                        TagLib::ByteVector id = frame->frameID();
 
1858
#ifndef TAGLIB_SUPPORTS_URLLINK_FRAMES
 
1859
                        if (id.startsWith("WXXX")) {
 
1860
                                TagLib::ID3v2::UserUrlLinkFrame userUrlLinkFrame(frame->render());
 
1861
                                return getFieldsFromUserUrlFrame(&userUrlLinkFrame, fields);
 
1862
                        } else if (id.startsWith("W")) {
 
1863
                                TagLib::ID3v2::UrlLinkFrame urlLinkFrame(frame->render());
 
1864
                                return getFieldsFromUrlFrame(&urlLinkFrame, fields);
 
1865
                        } else
 
1866
#endif
 
1867
#ifndef TAGLIB_SUPPORTS_USLT_FRAMES
 
1868
                        if (id.startsWith("USLT")) {
 
1869
                                TagLib::ID3v2::UnsynchronizedLyricsFrame usltFrame(frame->render());
 
1870
                                return getFieldsFromUsltFrame(&usltFrame, fields);
 
1871
                        } else
 
1872
#endif
 
1873
#ifndef TAGLIB_SUPPORTS_GEOB_FRAMES
 
1874
                        if (id.startsWith("GEOB")) {
 
1875
                                TagLib::ID3v2::GeneralEncapsulatedObjectFrame geobFrame(frame->render());
 
1876
                                return getFieldsFromGeobFrame(&geobFrame, fields);
 
1877
                        } else
 
1878
#endif
 
1879
                                return getFieldsFromUnknownFrame(frame, fields);
 
1880
                }
 
1881
        }
 
1882
        return QString();
 
1883
}
 
1884
 
 
1885
/**
 
1886
 * Convert a string to a language code byte vector.
 
1887
 *
 
1888
 * @param str string containing language code.
 
1889
 *
 
1890
 * @return 3 byte vector with language code.
 
1891
 */
 
1892
static TagLib::ByteVector languageCodeByteVector(QString str)
 
1893
{
 
1894
        uint len = str.length();
 
1895
        if (len > 3) {
 
1896
                str.truncate(3);
 
1897
        } else if (len < 3) {
 
1898
                for (uint i = len; i < 3; ++i) {
 
1899
                        str += ' ';
 
1900
                }
 
1901
        }
 
1902
        return TagLib::ByteVector(str.QCM_latin1(), str.length());
 
1903
}
 
1904
 
 
1905
/**
 
1906
 * The follwoing template functions are used to uniformly set fields
 
1907
 * in the different ID3v2 frames.
 
1908
 */
 
1909
template <class T>
 
1910
void setTextEncoding(T*, TagLib::String::Type) {}
 
1911
 
 
1912
template <>
 
1913
void setTextEncoding(TagLib::ID3v2::TextIdentificationFrame* f,
 
1914
                                                                                 TagLib::String::Type enc)
 
1915
{
 
1916
        f->setTextEncoding(enc);
 
1917
}
 
1918
 
 
1919
template <>
 
1920
void setTextEncoding(TagLib::ID3v2::AttachedPictureFrame* f,
 
1921
                                                                                 TagLib::String::Type enc)
 
1922
{
 
1923
        f->setTextEncoding(enc);
 
1924
}
 
1925
 
 
1926
template <>
 
1927
void setTextEncoding(TagLib::ID3v2::CommentsFrame* f,
 
1928
                                                                                 TagLib::String::Type enc)
 
1929
{
 
1930
        f->setTextEncoding(enc);
 
1931
}
 
1932
 
 
1933
template <>
 
1934
void setTextEncoding(TagLib::ID3v2::GeneralEncapsulatedObjectFrame* f,
 
1935
                                                                                 TagLib::String::Type enc)
 
1936
{
 
1937
        f->setTextEncoding(enc);
 
1938
}
 
1939
 
 
1940
template <>
 
1941
void setTextEncoding(TagLib::ID3v2::UserUrlLinkFrame* f,
 
1942
                                                                                 TagLib::String::Type enc)
 
1943
{
 
1944
        f->setTextEncoding(enc);
 
1945
}
 
1946
 
 
1947
template <>
 
1948
void setTextEncoding(TagLib::ID3v2::UnsynchronizedLyricsFrame* f,
 
1949
                                                                                 TagLib::String::Type enc)
 
1950
{
 
1951
        f->setTextEncoding(enc);
 
1952
}
 
1953
 
 
1954
 
 
1955
template <class T>
 
1956
void setDescription(T*, const Frame::Field&) {}
 
1957
 
 
1958
template <>
 
1959
void setDescription(TagLib::ID3v2::UserTextIdentificationFrame* f,
 
1960
                                                                                const Frame::Field& fld)
 
1961
{
 
1962
        f->setDescription(QSTRING_TO_TSTRING(fld.m_value.toString()));
 
1963
}
 
1964
 
 
1965
template <>
 
1966
void setDescription(TagLib::ID3v2::AttachedPictureFrame* f,
 
1967
                                                                                const Frame::Field& fld)
 
1968
{
 
1969
        f->setDescription(QSTRING_TO_TSTRING(fld.m_value.toString()));
 
1970
}
 
1971
 
 
1972
template <>
 
1973
void setDescription(TagLib::ID3v2::CommentsFrame* f, const Frame::Field& fld)
 
1974
{
 
1975
        f->setDescription(QSTRING_TO_TSTRING(fld.m_value.toString()));
 
1976
}
 
1977
 
 
1978
template <>
 
1979
void setDescription(TagLib::ID3v2::GeneralEncapsulatedObjectFrame* f,
 
1980
                                                                                const Frame::Field& fld)
 
1981
{
 
1982
        f->setDescription(QSTRING_TO_TSTRING(fld.m_value.toString()));
 
1983
}
 
1984
 
 
1985
template <>
 
1986
void setDescription(TagLib::ID3v2::UserUrlLinkFrame* f, const Frame::Field& fld)
 
1987
{
 
1988
        f->setDescription(QSTRING_TO_TSTRING(fld.m_value.toString()));
 
1989
}
 
1990
 
 
1991
template <>
 
1992
void setDescription(TagLib::ID3v2::UnsynchronizedLyricsFrame* f,
 
1993
                                                                                const Frame::Field& fld)
 
1994
{
 
1995
        f->setDescription(QSTRING_TO_TSTRING(fld.m_value.toString()));
 
1996
}
 
1997
 
 
1998
template <class T>
 
1999
void setMimeType(T*, const Frame::Field&) {}
 
2000
 
 
2001
template <>
 
2002
void setMimeType(TagLib::ID3v2::AttachedPictureFrame* f,
 
2003
                                                                 const Frame::Field& fld)
 
2004
{
 
2005
        f->setMimeType(QSTRING_TO_TSTRING(fld.m_value.toString()));
 
2006
}
 
2007
 
 
2008
template <>
 
2009
void setMimeType(TagLib::ID3v2::GeneralEncapsulatedObjectFrame* f,
 
2010
                                                                 const Frame::Field& fld)
 
2011
{
 
2012
        f->setMimeType(QSTRING_TO_TSTRING(fld.m_value.toString()));
 
2013
}
 
2014
 
 
2015
template <class T>
 
2016
void setPictureType(T*, const Frame::Field&) {}
 
2017
 
 
2018
template <>
 
2019
void setPictureType(TagLib::ID3v2::AttachedPictureFrame* f,
 
2020
                                                                                const Frame::Field& fld)
 
2021
{
 
2022
        f->setType(
 
2023
                static_cast<TagLib::ID3v2::AttachedPictureFrame::Type>(
 
2024
                        fld.m_value.toInt()));
 
2025
}
 
2026
 
 
2027
template <class T>
 
2028
void setData(T*, const Frame::Field&) {}
 
2029
 
 
2030
template <>
 
2031
void setData(TagLib::ID3v2::Frame* f, const Frame::Field& fld)
 
2032
{
 
2033
        QByteArray ba(fld.m_value.toByteArray());
 
2034
        f->setData(TagLib::ByteVector(ba.data(), ba.size()));
 
2035
}
 
2036
 
 
2037
template <>
 
2038
void setData(TagLib::ID3v2::AttachedPictureFrame* f, const Frame::Field& fld)
 
2039
{
 
2040
        QByteArray ba(fld.m_value.toByteArray());
 
2041
        f->setPicture(TagLib::ByteVector(ba.data(), ba.size()));
 
2042
}
 
2043
 
 
2044
template <>
 
2045
void setData(TagLib::ID3v2::GeneralEncapsulatedObjectFrame* f,
 
2046
                                                 const Frame::Field& fld)
 
2047
{
 
2048
        QByteArray ba(fld.m_value.toByteArray());
 
2049
        f->setObject(TagLib::ByteVector(ba.data(), ba.size()));
 
2050
}
 
2051
 
 
2052
template <class T>
 
2053
void setLanguage(T*, const Frame::Field&) {}
 
2054
 
 
2055
template <>
 
2056
void setLanguage(TagLib::ID3v2::CommentsFrame* f, const Frame::Field& fld)
 
2057
{
 
2058
        f->setLanguage(languageCodeByteVector(fld.m_value.toString()));
 
2059
}
 
2060
 
 
2061
template <>
 
2062
void setLanguage(TagLib::ID3v2::UnsynchronizedLyricsFrame* f,
 
2063
                                                                 const Frame::Field& fld)
 
2064
{
 
2065
        f->setLanguage(languageCodeByteVector(fld.m_value.toString()));
 
2066
}
 
2067
 
 
2068
template <class T>
 
2069
void setOwner(T*, const Frame::Field&) {}
 
2070
 
 
2071
template <>
 
2072
void setOwner(TagLib::ID3v2::UniqueFileIdentifierFrame* f,
 
2073
                                                        const Frame::Field& fld)
 
2074
{
 
2075
        f->setOwner(QSTRING_TO_TSTRING(fld.m_value.toString()));
 
2076
}
 
2077
 
 
2078
template <class T>
 
2079
void setIdentifier(T*, const Frame::Field&) {}
 
2080
 
 
2081
template <>
 
2082
void setIdentifier(TagLib::ID3v2::UniqueFileIdentifierFrame* f,
 
2083
                                                                         const Frame::Field& fld)
 
2084
{
 
2085
        QByteArray ba(fld.m_value.toByteArray());
 
2086
        f->setIdentifier(TagLib::ByteVector(ba.data(), ba.size()));
 
2087
}
 
2088
 
 
2089
template <class T>
 
2090
void setFilename(T*, const Frame::Field&) {}
 
2091
 
 
2092
template <>
 
2093
void setFilename(TagLib::ID3v2::GeneralEncapsulatedObjectFrame* f,
 
2094
                                                                 const Frame::Field& fld)
 
2095
{
 
2096
        f->setFileName(QSTRING_TO_TSTRING(fld.m_value.toString()));
 
2097
}
 
2098
 
 
2099
template <class T>
 
2100
void setUrl(T*, const Frame::Field&) {}
 
2101
 
 
2102
template <>
 
2103
void setUrl(TagLib::ID3v2::UrlLinkFrame* f, const Frame::Field& fld)
 
2104
{
 
2105
        f->setUrl(QSTRING_TO_TSTRING(fld.m_value.toString()));
 
2106
}
 
2107
 
 
2108
template <>
 
2109
void setUrl(TagLib::ID3v2::UserUrlLinkFrame* f, const Frame::Field& fld)
 
2110
{
 
2111
        f->setUrl(QSTRING_TO_TSTRING(fld.m_value.toString()));
 
2112
}
 
2113
 
 
2114
template <class T>
 
2115
void setValue(T* f, const TagLib::String& text)
 
2116
{
 
2117
        f->setText(text);
 
2118
}
 
2119
 
 
2120
template <>
 
2121
void setValue(TagLib::ID3v2::AttachedPictureFrame* f, const TagLib::String& text)
 
2122
{
 
2123
        f->setDescription(text);
 
2124
}
 
2125
 
 
2126
/**
 
2127
 * Set the fields in a TagLib ID3v2 frame.
 
2128
 *
 
2129
 * @param tFrame TagLib frame to set
 
2130
 * @param frame  frame with field values
 
2131
 */
 
2132
template <class T>
 
2133
void setTagLibFrame(const TagLibFile* self, T* tFrame, const Frame& frame)
 
2134
{
 
2135
        // If value is changed or field list is empty,
 
2136
        // set from value, else from FieldList.
 
2137
        if (frame.isValueChanged() || frame.getFieldList().empty()) {
 
2138
                QString text(frame.getValue());
 
2139
                if (frame.getType() == Frame::FT_Genre) {
 
2140
                        text = Genres::getNumberString(text, false);
 
2141
                } else if (frame.getType() == Frame::FT_Track) {
 
2142
                        self->addTotalNumberOfTracksIfEnabled(text);
 
2143
                }
 
2144
                setValue(tFrame, QSTRING_TO_TSTRING(text));
 
2145
                setTextEncoding(tFrame, getTextEncodingConfig(needsUnicode(text)));
 
2146
        } else {
 
2147
                for (Frame::FieldList::const_iterator fldIt = frame.getFieldList().begin();
 
2148
                                 fldIt != frame.getFieldList().end();
 
2149
                                 ++fldIt) {
 
2150
                        const Frame::Field& fld = *fldIt;
 
2151
                        switch (fld.m_id) {
 
2152
                                case Frame::Field::ID_Text:
 
2153
                                {
 
2154
                                        QString value(fld.m_value.toString());
 
2155
                                        if (frame.getType() == Frame::FT_Genre) {
 
2156
                                                value = Genres::getNumberString(value, false);
 
2157
                                        } else if (frame.getType() == Frame::FT_Track) {
 
2158
                                                self->addTotalNumberOfTracksIfEnabled(value);
 
2159
                                        }
 
2160
                                        tFrame->setText(QSTRING_TO_TSTRING(value));
 
2161
                                        break;
 
2162
                                }
 
2163
                                case Frame::Field::ID_TextEnc:
 
2164
                                        setTextEncoding(tFrame, static_cast<TagLib::String::Type>(
 
2165
                                                                                                                fld.m_value.toInt()));
 
2166
                                        break;
 
2167
                                case Frame::Field::ID_Description:
 
2168
                                        setDescription(tFrame, fld);
 
2169
                                        break;
 
2170
                                case Frame::Field::ID_MimeType:
 
2171
                                        setMimeType(tFrame, fld);
 
2172
                                        break;
 
2173
                                case Frame::Field::ID_PictureType:
 
2174
                                        setPictureType(tFrame, fld);
 
2175
                                        break;
 
2176
                                case Frame::Field::ID_Data:
 
2177
                                        setData(tFrame, fld);
 
2178
                                        break;
 
2179
                                case Frame::Field::ID_Language:
 
2180
                                        setLanguage(tFrame, fld);
 
2181
                                        break;
 
2182
                                case Frame::Field::ID_Owner:
 
2183
                                        setOwner(tFrame, fld);
 
2184
                                        break;
 
2185
                                case Frame::Field::ID_Id:
 
2186
                                        setIdentifier(tFrame, fld);
 
2187
                                        break;
 
2188
                                case Frame::Field::ID_Filename:
 
2189
                                        setFilename(tFrame, fld);
 
2190
                                        break;
 
2191
                                case Frame::Field::ID_Url:
 
2192
                                        setUrl(tFrame, fld);
 
2193
                                        break;
 
2194
                        }
 
2195
                }
 
2196
        }
 
2197
}
 
2198
 
 
2199
/**
 
2200
 * Modify an ID3v2 frame.
 
2201
 *
 
2202
 * @param id3Frame original ID3v2 frame
 
2203
 * @param frame    frame with fields to set in new frame
 
2204
 */
 
2205
void TagLibFile::setId3v2Frame(
 
2206
        TagLib::ID3v2::Frame* id3Frame, const Frame& frame) const
 
2207
{
 
2208
        if (id3Frame) {
 
2209
                TagLib::ID3v2::TextIdentificationFrame* tFrame;
 
2210
                TagLib::ID3v2::AttachedPictureFrame* apicFrame;
 
2211
                TagLib::ID3v2::CommentsFrame* commFrame;
 
2212
                TagLib::ID3v2::UniqueFileIdentifierFrame* ufidFrame;
 
2213
                TagLib::ID3v2::GeneralEncapsulatedObjectFrame* geobFrame;
 
2214
                TagLib::ID3v2::UserUrlLinkFrame* wxxxFrame;
 
2215
                TagLib::ID3v2::UrlLinkFrame* wFrame;
 
2216
                TagLib::ID3v2::UnsynchronizedLyricsFrame* usltFrame;
 
2217
                if ((tFrame =
 
2218
                                 dynamic_cast<TagLib::ID3v2::TextIdentificationFrame*>(id3Frame))
 
2219
                                != 0) {
 
2220
                        TagLib::ID3v2::UserTextIdentificationFrame* txxxFrame =
 
2221
                                dynamic_cast<TagLib::ID3v2::UserTextIdentificationFrame*>(id3Frame);
 
2222
                        if (txxxFrame) {
 
2223
                                setTagLibFrame(this, txxxFrame, frame);
 
2224
                        } else {
 
2225
                                setTagLibFrame(this, tFrame, frame);
 
2226
                        }
 
2227
                } else if ((apicFrame =
 
2228
                                                                dynamic_cast<TagLib::ID3v2::AttachedPictureFrame*>(id3Frame))
 
2229
                                                         != 0) {
 
2230
                        setTagLibFrame(this, apicFrame, frame);
 
2231
                } else if ((commFrame = dynamic_cast<TagLib::ID3v2::CommentsFrame*>(
 
2232
                                                                        id3Frame)) != 0) {
 
2233
                        setTagLibFrame(this, commFrame, frame);
 
2234
                } else if ((ufidFrame =
 
2235
                                                                dynamic_cast<TagLib::ID3v2::UniqueFileIdentifierFrame*>(
 
2236
                                                                        id3Frame)) != 0) {
 
2237
                        setTagLibFrame(this, ufidFrame, frame);
 
2238
                } else if ((geobFrame =
 
2239
                                                                dynamic_cast<TagLib::ID3v2::GeneralEncapsulatedObjectFrame*>(
 
2240
                                                                        id3Frame)) != 0) {
 
2241
                        setTagLibFrame(this, geobFrame, frame);
 
2242
                } else if ((wxxxFrame = dynamic_cast<TagLib::ID3v2::UserUrlLinkFrame*>(
 
2243
                                                                        id3Frame)) != 0) {
 
2244
                        setTagLibFrame(this, wxxxFrame, frame);
 
2245
                } else if ((wFrame = dynamic_cast<TagLib::ID3v2::UrlLinkFrame*>(
 
2246
                                                                        id3Frame)) != 0) {
 
2247
                        setTagLibFrame(this, wFrame, frame);
 
2248
                } else if ((usltFrame =
 
2249
                                                                dynamic_cast<TagLib::ID3v2::UnsynchronizedLyricsFrame*>(
 
2250
                                                                        id3Frame)) != 0) {
 
2251
                        setTagLibFrame(this, usltFrame, frame);
 
2252
                } else {
 
2253
                        TagLib::ByteVector id(id3Frame->frameID());
 
2254
                        // create temporary objects for frames not known by TagLib,
 
2255
                        // an UnknownFrame copy will be created by the edit method.
 
2256
#ifndef TAGLIB_SUPPORTS_URLLINK_FRAMES
 
2257
                        if (id.startsWith("WXXX")) {
 
2258
                                TagLib::ID3v2::UserUrlLinkFrame userUrlLinkFrame(id3Frame->render());
 
2259
                                setTagLibFrame(this, &userUrlLinkFrame, frame);
 
2260
                                id3Frame->setData(userUrlLinkFrame.render());
 
2261
                        } else if (id.startsWith("W")) {
 
2262
                                TagLib::ID3v2::UrlLinkFrame urlLinkFrame(id3Frame->render());
 
2263
                                setTagLibFrame(this, &urlLinkFrame, frame);
 
2264
                                id3Frame->setData(urlLinkFrame.render());
 
2265
                        } else
 
2266
#endif
 
2267
#ifndef TAGLIB_SUPPORTS_USLT_FRAMES
 
2268
                        if (id.startsWith("USLT")) {
 
2269
                                TagLib::ID3v2::UnsynchronizedLyricsFrame usltFrame(id3Frame->render());
 
2270
                                setTagLibFrame(this, &usltFrame, frame);
 
2271
                                id3Frame->setData(usltFrame.render());
 
2272
                        } else
 
2273
#endif
 
2274
#ifndef TAGLIB_SUPPORTS_GEOB_FRAMES
 
2275
                        if (id.startsWith("GEOB")) {
 
2276
                                TagLib::ID3v2::GeneralEncapsulatedObjectFrame geobFrame(id3Frame->render());
 
2277
                                setTagLibFrame(this, &geobFrame, frame);
 
2278
                                id3Frame->setData(geobFrame.render());
 
2279
                        } else
 
2280
#endif
 
2281
                        {
 
2282
                                setTagLibFrame(this, id3Frame, frame);
 
2283
                        }
 
2284
                }
 
2285
        }
 
2286
}
 
2287
 
 
2288
/**
 
2289
 * Set a frame in the tags 2.
 
2290
 *
 
2291
 * @param frame frame to set
 
2292
 *
 
2293
 * @return true if ok.
 
2294
 */
 
2295
bool TagLibFile::setFrameV2(const Frame& frame)
 
2296
{
 
2297
        // If the frame has an index, change that specific frame
 
2298
        int index = frame.getIndex();
 
2299
        if (index != -1 && m_tagV2) {
 
2300
                TagLib::ID3v2::Tag* id3v2Tag;
 
2301
                TagLib::Ogg::XiphComment* oggTag;
 
2302
                TagLib::APE::Tag* apeTag;
 
2303
                if ((id3v2Tag = dynamic_cast<TagLib::ID3v2::Tag*>(m_tagV2)) != 0) {
 
2304
                        const TagLib::ID3v2::FrameList& frameList = id3v2Tag->frameList();
 
2305
                        if (index < static_cast<int>(frameList.size())) {
 
2306
                                // This is a hack. The frameList should not be modified directly.
 
2307
                                // However when removing the old frame and adding a new frame,
 
2308
                                // the indices of all frames get invalid.
 
2309
                                setId3v2Frame(frameList[index], frame);
 
2310
                                return true;
 
2311
                        }
 
2312
                } else if ((oggTag = dynamic_cast<TagLib::Ogg::XiphComment*>(m_tagV2)) != 0) {
 
2313
                        TagLib::String key = QSTRING_TO_TSTRING(frame.getName(true));
 
2314
                        TagLib::String value = QSTRING_TO_TSTRING(frame.getValue());
 
2315
#ifdef TAGLIB_XIPHCOMMENT_REMOVEFIELD_CRASHES
 
2316
                        oggTag->addField(key, value, true);
 
2317
#else
 
2318
                        // This will crash because TagLib uses an invalidated iterator
 
2319
                        // after calling erase(). I hope this will be fixed in the next
 
2320
                        // version. Until then, remove all fields with that key.
 
2321
                        oggTag->removeField(key, oldValue);
 
2322
                        oggTag->addField(key, value, false);
 
2323
#endif
 
2324
                        if (frame.getType() == Frame::FT_Track) {
 
2325
                                int numTracks = getTotalNumberOfTracksIfEnabled();
 
2326
                                if (numTracks > 0) {
 
2327
                                        oggTag->addField("TRACKTOTAL", TagLib::String::number(numTracks), true);
 
2328
                                }
 
2329
                        }
 
2330
                        return true;
 
2331
                } else if ((apeTag = dynamic_cast<TagLib::APE::Tag*>(m_tagV2)) != 0) {
 
2332
                        apeTag->addValue(QSTRING_TO_TSTRING(frame.getName(true)),
 
2333
                                                                                         QSTRING_TO_TSTRING(frame.getValue()));
 
2334
                        return true;
 
2335
                }
 
2336
        }
 
2337
 
 
2338
        // Try the superclass method
 
2339
        return TaggedFile::setFrameV2(frame);
 
2340
}
 
2341
 
 
2342
/**
 
2343
 * Get name of frame from type.
 
2344
 *
 
2345
 * @param type type
 
2346
 *
 
2347
 * @return name.
 
2348
 */
 
2349
static const char* getVorbisNameFromType(Frame::Type type)
 
2350
{
 
2351
  static const char* const names[] = {
 
2352
                "TITLE",           // FT_Title,
 
2353
                "ARTIST",          // FT_Artist,
 
2354
                "ALBUM",           // FT_Album,
 
2355
                "COMMENT",         // FT_Comment,
 
2356
                "DATE",            // FT_Date,
 
2357
                "TRACKNUMBER",     // FT_Track,
 
2358
                "GENRE",           // FT_Genre,
 
2359
                                   // FT_LastV1Frame = FT_Track,
 
2360
                "ARRANGER",        // FT_Arranger,
 
2361
                "AUTHOR",          // FT_Author,
 
2362
                "BPM",             // FT_Bpm,
 
2363
                "COMPOSER",        // FT_Composer,
 
2364
                "CONDUCTOR",       // FT_Conductor,
 
2365
                "COPYRIGHT",       // FT_Copyright,
 
2366
                "DISCNUMBER",      // FT_Disc,
 
2367
                "ENCODED-BY",      // FT_EncodedBy,
 
2368
                "ISRC",            // FT_Isrc,
 
2369
                "LANGUAGE",        // FT_Language,
 
2370
                "LYRICIST",        // FT_Lyricist,
 
2371
                "ORIGINALALBUM",   // FT_OriginalAlbum,
 
2372
                "ORIGINALARTIST",  // FT_OriginalArtist,
 
2373
                "ORIGINALDATE",    // FT_OriginalDate,
 
2374
                "PART",            // FT_Part,
 
2375
                "PERFORMER",       // FT_Performer,
 
2376
                "PUBLISHER",       // FT_Publisher,
 
2377
                "SUBTITLE",        // FT_Subtitle,
 
2378
                "WEBSITE",         // FT_Website,
 
2379
                                   // FT_LastFrame = FT_Website
 
2380
        };
 
2381
        class not_used { int array_size_check[
 
2382
                        sizeof(names) / sizeof(names[0]) == Frame::FT_LastFrame + 1
 
2383
                        ? 1 : -1 ]; };
 
2384
        return type <= Frame::FT_LastFrame ? names[type] : "UNKNOWN";
 
2385
}
 
2386
 
 
2387
/**
 
2388
 * Get the frame type for a Vorbis name.
 
2389
 *
 
2390
 * @param name Vorbis tag name
 
2391
 *
 
2392
 * @return frame type.
 
2393
 */
 
2394
static Frame::Type getTypeFromVorbisName(QString name)
 
2395
{
 
2396
        static QMap<QString, int> strNumMap;
 
2397
        if (strNumMap.empty()) {
 
2398
                // first time initialization
 
2399
                for (int i = 0; i <= Frame::FT_LastFrame; ++i) {
 
2400
                        Frame::Type type = static_cast<Frame::Type>(i);
 
2401
                        strNumMap.insert(getVorbisNameFromType(type), type);
 
2402
                }
 
2403
                strNumMap.insert("DESCRIPTION", Frame::FT_Comment);
 
2404
        }
 
2405
        QMap<QString, int>::const_iterator it =
 
2406
                strNumMap.find(name.remove(' ').QCM_toUpper());
 
2407
        if (it != strNumMap.end()) {
 
2408
                return static_cast<Frame::Type>(*it);
 
2409
        }
 
2410
        return Frame::FT_Other;
 
2411
}
 
2412
 
 
2413
/**
 
2414
 * Get the frame type for an APE name.
 
2415
 *
 
2416
 * @param name APE tag name
 
2417
 *
 
2418
 * @return frame type.
 
2419
 */
 
2420
static Frame::Type getTypeFromApeName(const QString& name)
 
2421
{
 
2422
        Frame::Type type = getTypeFromVorbisName(name);
 
2423
        if (type == Frame::FT_Other) {
 
2424
                if (name == "YEAR") {
 
2425
                        type = Frame::FT_Date;
 
2426
                } else if (name == "TRACK") {
 
2427
                        type = Frame::FT_Track;
 
2428
                } else if (name == "ENCODED BY") {
 
2429
                        type = Frame::FT_EncodedBy;
 
2430
                }
 
2431
        }
 
2432
        return type;
 
2433
}
 
2434
 
 
2435
/**
 
2436
 * Get internal name of a Vorbis frame.
 
2437
 *
 
2438
 * @param frame frame
 
2439
 *
 
2440
 * @return Vorbis key.
 
2441
 */
 
2442
static QString getVorbisName(const Frame& frame)
 
2443
{
 
2444
        Frame::Type type = frame.getType();
 
2445
        if (type == Frame::FT_Comment) {
 
2446
                return "DESCRIPTION";
 
2447
        } else if (type <= Frame::FT_LastFrame) {
 
2448
                return getVorbisNameFromType(type);
 
2449
        } else {
 
2450
                return frame.getName().remove(' ').QCM_toUpper();
 
2451
        }
 
2452
}
 
2453
 
 
2454
/**
 
2455
 * Get internal name of an APE frame.
 
2456
 *
 
2457
 * @param frame frame
 
2458
 *
 
2459
 * @return APE key.
 
2460
 */
 
2461
static QString getApeName(const Frame& frame)
 
2462
{
 
2463
        Frame::Type type = frame.getType();
 
2464
        if (type == Frame::FT_Date) {
 
2465
                return "YEAR";
 
2466
        } else if (type == Frame::FT_Track) {
 
2467
                return "TRACK";
 
2468
        } else if (type <= Frame::FT_LastFrame) {
 
2469
                return getVorbisNameFromType(type);
 
2470
        } else {
 
2471
                return frame.getName().QCM_toUpper();
 
2472
        }
 
2473
}
 
2474
 
 
2475
/**
 
2476
 * Add a frame in the tags 2.
 
2477
 *
 
2478
 * @param frame frame to add, a field list may be added by this method
 
2479
 *
 
2480
 * @return true if ok.
 
2481
 */
 
2482
bool TagLibFile::addFrameV2(Frame& frame)
 
2483
{
 
2484
        TagLib::String::Type enc = getDefaultTextEncoding();
 
2485
        // Add a new frame.
 
2486
        if (makeTagV2Settable()) {
 
2487
                TagLib::ID3v2::Tag* id3v2Tag;
 
2488
                TagLib::Ogg::XiphComment* oggTag;
 
2489
                TagLib::APE::Tag* apeTag;
 
2490
                if ((id3v2Tag = dynamic_cast<TagLib::ID3v2::Tag*>(m_tagV2)) != 0) {
 
2491
                        QString name = frame.getType() != Frame::FT_Other ?
 
2492
                                QString(getStringForType(frame.getType())) :
 
2493
                                frame.getName();
 
2494
                        QString frameId = name;
 
2495
                        frameId.truncate(4);
 
2496
                        TagLib::ID3v2::Frame* id3Frame = 0;
 
2497
                        if (frameId.startsWith("T")) {
 
2498
                                if (frameId == "TXXX") {
 
2499
                                        id3Frame = new TagLib::ID3v2::UserTextIdentificationFrame(enc);
 
2500
                                } else {
 
2501
                                        id3Frame = new TagLib::ID3v2::TextIdentificationFrame(
 
2502
                                                TagLib::ByteVector(frameId.QCM_latin1(), frameId.length()), enc);
 
2503
                                        id3Frame->setText(""); // is necessary for createFrame() to work
 
2504
                                }
 
2505
                        } else if (frameId == "COMM") {
 
2506
                                id3Frame = new TagLib::ID3v2::CommentsFrame(enc);
 
2507
                        } else if (frameId == "APIC") {
 
2508
                                id3Frame = new TagLib::ID3v2::AttachedPictureFrame;
 
2509
                                if (id3Frame) {
 
2510
                                        ((TagLib::ID3v2::AttachedPictureFrame*)id3Frame)->setTextEncoding(enc);
 
2511
                                }
 
2512
                        } else if (frameId == "UFID") {
 
2513
                                // the bytevector must not be empty
 
2514
                                id3Frame = new TagLib::ID3v2::UniqueFileIdentifierFrame(
 
2515
                                        TagLib::String(), TagLib::ByteVector(" "));
 
2516
                        } else if (frameId == "GEOB") {
 
2517
                                id3Frame = new TagLib::ID3v2::GeneralEncapsulatedObjectFrame;
 
2518
                                if (id3Frame) {
 
2519
                                        ((TagLib::ID3v2::GeneralEncapsulatedObjectFrame*)id3Frame)->setTextEncoding(enc);
 
2520
                                }
 
2521
                        } else if (frameId.startsWith("W")) {
 
2522
                                if (frameId == "WXXX") {
 
2523
                                        id3Frame = new TagLib::ID3v2::UserUrlLinkFrame(enc);
 
2524
                                } else {
 
2525
                                        id3Frame = new TagLib::ID3v2::UrlLinkFrame(
 
2526
                                                TagLib::ByteVector(frameId.QCM_latin1(), frameId.length()));
 
2527
                                        id3Frame->setText("http://"); // is necessary for createFrame() to work
 
2528
                                }
 
2529
                        } else if (frameId == "USLT") {
 
2530
                                id3Frame = new TagLib::ID3v2::UnsynchronizedLyricsFrame(enc);
 
2531
                        }
 
2532
                        if (id3Frame) {
 
2533
                                if (!frame.fieldList().empty()) {
 
2534
                                        frame.setValueFromFieldList();
 
2535
                                        setId3v2Frame(id3Frame, frame);
 
2536
                                }
 
2537
#ifdef WIN32
 
2538
                                // freed in Windows DLL => must be allocated in the same DLL
 
2539
                                TagLib::ID3v2::Frame* dllAllocatedFrame =
 
2540
                                        TagLib::ID3v2::FrameFactory::instance()->createFrame(id3Frame->render());
 
2541
                                if (dllAllocatedFrame) {
 
2542
                                        id3v2Tag->addFrame(dllAllocatedFrame);
 
2543
                                }
 
2544
#else
 
2545
                                id3v2Tag->addFrame(id3Frame);
 
2546
#endif
 
2547
                                frame.setInternalName(name);
 
2548
                                frame.setIndex(id3v2Tag->frameList().size() - 1);
 
2549
                                if (frame.fieldList().empty()) {
 
2550
                                        // add field list to frame
 
2551
                                        getFieldsFromId3Frame(id3Frame, frame.fieldList(), frame.getType());
 
2552
                                        frame.setFieldListFromValue();
 
2553
                                }
 
2554
#ifdef WIN32
 
2555
                                delete id3Frame;
 
2556
#endif
 
2557
                                markTag2Changed();
 
2558
                                return true;
 
2559
                        }
 
2560
                } else if ((oggTag = dynamic_cast<TagLib::Ogg::XiphComment*>(m_tagV2)) != 0) {
 
2561
                        QString name(getVorbisName(frame));
 
2562
                        TagLib::String tname = QSTRING_TO_TSTRING(name);
 
2563
                        oggTag->addField(tname, QSTRING_TO_TSTRING(frame.getValue()));
 
2564
                        frame.setInternalName(name);
 
2565
 
 
2566
                        const TagLib::Ogg::FieldListMap& fieldListMap = oggTag->fieldListMap();
 
2567
                        int index = 0;
 
2568
                        bool found = false;
 
2569
                        for (TagLib::Ogg::FieldListMap::ConstIterator it = fieldListMap.begin();
 
2570
                                         it != fieldListMap.end();
 
2571
                                         ++it) {
 
2572
                                if ((*it).first == tname) {
 
2573
                                        index += (*it).second.size() - 1;
 
2574
                                        found = true;
 
2575
                                        break;
 
2576
                                }
 
2577
                                ++index;
 
2578
                        }
 
2579
                        frame.setIndex(found ? index : -1);
 
2580
                        markTag2Changed();
 
2581
                        return true;
 
2582
                } else if ((apeTag = dynamic_cast<TagLib::APE::Tag*>(m_tagV2)) != 0) {
 
2583
                        QString name(getApeName(frame));
 
2584
                        TagLib::String tname = QSTRING_TO_TSTRING(name);
 
2585
                        TagLib::String tvalue = QSTRING_TO_TSTRING(frame.getValue());
 
2586
                        if (tvalue.isEmpty()) {
 
2587
                                tvalue = " "; // empty values are not added by TagLib
 
2588
                        }
 
2589
                        apeTag->addValue(tname, tvalue, true);
 
2590
                        frame.setInternalName(name);
 
2591
 
 
2592
                        const TagLib::APE::ItemListMap& itemListMap = apeTag->itemListMap();
 
2593
                        int index = 0;
 
2594
                        bool found = false;
 
2595
                        for (TagLib::APE::ItemListMap::ConstIterator it = itemListMap.begin();
 
2596
                                         it != itemListMap.end();
 
2597
                                         ++it) {
 
2598
                                if ((*it).first == tname) {
 
2599
                                        found = true;
 
2600
                                        break;
 
2601
                                }
 
2602
                                ++index;
 
2603
                        }
 
2604
                        frame.setIndex(found ? index : -1);
 
2605
                        markTag2Changed();
 
2606
                        return true;
 
2607
                }
 
2608
        }
 
2609
 
 
2610
        // Try the superclass method
 
2611
        return TaggedFile::addFrameV2(frame);
 
2612
}
 
2613
 
 
2614
/**
 
2615
 * Delete a frame in the tags 2.
 
2616
 *
 
2617
 * @param frame frame to delete.
 
2618
 *
 
2619
 * @return true if ok.
 
2620
 */
 
2621
bool TagLibFile::deleteFrameV2(const Frame& frame)
 
2622
{
 
2623
        // If the frame has an index, delete that specific frame
 
2624
        int index = frame.getIndex();
 
2625
        if (index != -1 && m_tagV2) {
 
2626
                TagLib::ID3v2::Tag* id3v2Tag;
 
2627
                TagLib::Ogg::XiphComment* oggTag;
 
2628
                TagLib::APE::Tag* apeTag;
 
2629
                if ((id3v2Tag = dynamic_cast<TagLib::ID3v2::Tag*>(m_tagV2)) != 0) {
 
2630
                        const TagLib::ID3v2::FrameList& frameList = id3v2Tag->frameList();
 
2631
                        if (index < static_cast<int>(frameList.size())) {
 
2632
                                id3v2Tag->removeFrame(frameList[index]);
 
2633
                                markTag2Changed();
 
2634
                                return true;
 
2635
                        }
 
2636
                } else if ((oggTag = dynamic_cast<TagLib::Ogg::XiphComment*>(m_tagV2)) != 0) {
 
2637
                        TagLib::String key =
 
2638
                                QSTRING_TO_TSTRING(frame.getName(true));
 
2639
#ifdef TAGLIB_XIPHCOMMENT_REMOVEFIELD_CRASHES
 
2640
                        oggTag->removeField(key);
 
2641
#else
 
2642
                        // This will crash because TagLib uses an invalidated iterator
 
2643
                        // after calling erase(). I hope this will be fixed in the next
 
2644
                        // version. Until then, remove all fields with that key.
 
2645
                        oggTag->removeField(key, QSTRING_TO_TSTRING(frame.getValue()));
 
2646
#endif
 
2647
                        markTag2Changed();
 
2648
                        return true;
 
2649
                } else if ((apeTag = dynamic_cast<TagLib::APE::Tag*>(m_tagV2)) != 0) {
 
2650
                        TagLib::String key = QSTRING_TO_TSTRING(frame.getName(true));
 
2651
                        apeTag->removeItem(key);
 
2652
                        markTag2Changed();
 
2653
                        return true;
 
2654
                }
 
2655
        }
 
2656
 
 
2657
        // Try the superclass method
 
2658
        return TaggedFile::deleteFrameV2(frame);
 
2659
}
 
2660
 
 
2661
/**
 
2662
 * Remove ID3v2 frames.
 
2663
 *
 
2664
 * @param flt filter specifying which frames to remove
 
2665
 */
 
2666
void TagLibFile::deleteFramesV2(const FrameFilter& flt)
 
2667
{
 
2668
        if (m_tagV2) {
 
2669
                TagLib::ID3v2::Tag* id3v2Tag;
 
2670
                TagLib::Ogg::XiphComment* oggTag;
 
2671
                TagLib::APE::Tag* apeTag;
 
2672
                if (flt.areAllEnabled()) {
 
2673
                        if ((id3v2Tag = dynamic_cast<TagLib::ID3v2::Tag*>(m_tagV2)) != 0) {
 
2674
                                const TagLib::ID3v2::FrameList& frameList = id3v2Tag->frameList();
 
2675
                                for (TagLib::ID3v2::FrameList::ConstIterator it = frameList.begin();
 
2676
                                                 it != frameList.end();) {
 
2677
                                        id3v2Tag->removeFrame(*it++, true);
 
2678
                                }
 
2679
                                markTag2Changed();
 
2680
                        } else if ((oggTag = dynamic_cast<TagLib::Ogg::XiphComment*>(m_tagV2)) !=
 
2681
                                                                 0) {
 
2682
                                const TagLib::Ogg::FieldListMap& fieldListMap = oggTag->fieldListMap();
 
2683
                                for (TagLib::Ogg::FieldListMap::ConstIterator it = fieldListMap.begin();
 
2684
                                                 it != fieldListMap.end();) {
 
2685
                                        oggTag->removeField((*it++).first);
 
2686
                                }
 
2687
                                markTag2Changed();
 
2688
                        } else if ((apeTag = dynamic_cast<TagLib::APE::Tag*>(m_tagV2)) != 0) {
 
2689
                                const TagLib::APE::ItemListMap& itemListMap = apeTag->itemListMap();
 
2690
                                for (TagLib::APE::ItemListMap::ConstIterator it = itemListMap.begin();
 
2691
                                                 it != itemListMap.end();) {
 
2692
                                        apeTag->removeItem((*it++).first);
 
2693
                                }
 
2694
                                markTag2Changed();
 
2695
                        } else {
 
2696
                                TaggedFile::deleteFramesV2(flt);
 
2697
                        }
 
2698
                } else {
 
2699
                        if ((id3v2Tag = dynamic_cast<TagLib::ID3v2::Tag*>(m_tagV2)) != 0) {
 
2700
                                const TagLib::ID3v2::FrameList& frameList = id3v2Tag->frameList();
 
2701
                                Frame::Type type;
 
2702
                                const char* name;
 
2703
                                for (TagLib::ID3v2::FrameList::ConstIterator it = frameList.begin();
 
2704
                                                 it != frameList.end();) {
 
2705
                                        getTypeStringForFrameId((*it)->frameID(), type, name);
 
2706
                                        if (flt.isEnabled(type, name)) {
 
2707
                                                id3v2Tag->removeFrame(*it++, true);
 
2708
                                        } else {
 
2709
                                                ++it;
 
2710
                                        }
 
2711
                                }
 
2712
                                markTag2Changed();
 
2713
                        } else if ((oggTag = dynamic_cast<TagLib::Ogg::XiphComment*>(m_tagV2)) !=
 
2714
                                                                 0) {
 
2715
                                const TagLib::Ogg::FieldListMap& fieldListMap = oggTag->fieldListMap();
 
2716
                                for (TagLib::Ogg::FieldListMap::ConstIterator it = fieldListMap.begin();
 
2717
                                                 it != fieldListMap.end();) {
 
2718
                                        QString name(TStringToQString((*it).first));
 
2719
                                        if (flt.isEnabled(getTypeFromVorbisName(name), name)) {
 
2720
                                                oggTag->removeField((*it++).first);
 
2721
                                        } else {
 
2722
                                                ++it;
 
2723
                                        }
 
2724
                                }
 
2725
                                markTag2Changed();
 
2726
                        } else if ((apeTag = dynamic_cast<TagLib::APE::Tag*>(m_tagV2)) != 0) {
 
2727
                                const TagLib::APE::ItemListMap& itemListMap = apeTag->itemListMap();
 
2728
                                for (TagLib::APE::ItemListMap::ConstIterator it = itemListMap.begin();
 
2729
                                                 it != itemListMap.end();) {
 
2730
                                        QString name(TStringToQString((*it).first));
 
2731
                                        if (flt.isEnabled(getTypeFromApeName(name), name)) {
 
2732
                                                apeTag->removeItem((*it++).first);
 
2733
                                        } else {
 
2734
                                                ++it;
 
2735
                                        }
 
2736
                                }
 
2737
                                markTag2Changed();
 
2738
                        } else {
 
2739
                                TaggedFile::deleteFramesV2(flt);
 
2740
                        }
 
2741
                }
 
2742
        }
 
2743
}
 
2744
 
 
2745
/**
 
2746
 * Get all frames in tag 2.
 
2747
 *
 
2748
 * @param frames frame collection to set.
 
2749
 */
 
2750
void TagLibFile::getAllFramesV2(FrameCollection& frames)
 
2751
{
 
2752
        frames.clear();
 
2753
        if (m_tagV2) {
 
2754
                TagLib::ID3v2::Tag* id3v2Tag;
 
2755
                TagLib::Ogg::XiphComment* oggTag;
 
2756
                TagLib::APE::Tag* apeTag;
 
2757
                if ((id3v2Tag = dynamic_cast<TagLib::ID3v2::Tag*>(m_tagV2)) != 0) {
 
2758
                        const TagLib::ID3v2::FrameList& frameList = id3v2Tag->frameList();
 
2759
                        int i = 0;
 
2760
                        Frame::Type type;
 
2761
                        const char* name;
 
2762
                        for (TagLib::ID3v2::FrameList::ConstIterator it = frameList.begin();
 
2763
                                         it != frameList.end();
 
2764
                                         ++it) {
 
2765
                                getTypeStringForFrameId((*it)->frameID(), type, name);
 
2766
                                Frame frame(type, TStringToQString((*it)->toString()), name, i++);
 
2767
                                frame.setValue(getFieldsFromId3Frame(*it, frame.fieldList(), type));
 
2768
                                frames.insert(frame);
 
2769
                        }
 
2770
                } else if ((oggTag = dynamic_cast<TagLib::Ogg::XiphComment*>(m_tagV2)) != 0) {
 
2771
                        const TagLib::Ogg::FieldListMap& fieldListMap = oggTag->fieldListMap();
 
2772
                        int i = 0;
 
2773
                        for (TagLib::Ogg::FieldListMap::ConstIterator it = fieldListMap.begin();
 
2774
                                         it != fieldListMap.end();
 
2775
                                         ++it) {
 
2776
                                QString name = TStringToQString((*it).first);
 
2777
                                Frame::Type type = getTypeFromVorbisName(name);
 
2778
                                TagLib::StringList stringList = (*it).second;
 
2779
                                for (TagLib::StringList::ConstIterator slit = stringList.begin();
 
2780
                                                 slit != stringList.end();
 
2781
                                                 ++slit) {
 
2782
                                        frames.insert(Frame(type, TStringToQString(TagLib::String(*slit)),
 
2783
                                                                                                                                 name, i++));
 
2784
                                }
 
2785
                        }
 
2786
                } else if ((apeTag = dynamic_cast<TagLib::APE::Tag*>(m_tagV2)) != 0) {
 
2787
                        const TagLib::APE::ItemListMap& itemListMap = apeTag->itemListMap();
 
2788
                        int i = 0;
 
2789
                        for (TagLib::APE::ItemListMap::ConstIterator it = itemListMap.begin();
 
2790
                                         it != itemListMap.end();
 
2791
                                         ++it) {
 
2792
                                QString name = TStringToQString((*it).first);
 
2793
                                TagLib::StringList values = (*it).second.toStringList();
 
2794
                                Frame::Type type = getTypeFromApeName(name);
 
2795
                                frames.insert(
 
2796
                                        Frame(type, values.size() > 0 ? TStringToQString(values.front()) : "",
 
2797
                                                                name, i++));
 
2798
                        }
 
2799
                } else {
 
2800
                        TaggedFile::getAllFramesV2(frames);
 
2801
                }
 
2802
        }
 
2803
        frames.addMissingStandardFrames();
 
2804
}
 
2805
 
 
2806
/**
 
2807
 * Get a list of frame IDs which can be added.
 
2808
 *
 
2809
 * @return list with frame IDs.
 
2810
 */
 
2811
QStringList TagLibFile::getFrameIds() const
 
2812
{
 
2813
        QStringList lst(TaggedFile::getFrameIds());
 
2814
        TagLib::ID3v2::Tag* id3v2Tag;
 
2815
        if ((id3v2Tag = dynamic_cast<TagLib::ID3v2::Tag*>(m_tagV2)) != 0) {
 
2816
                for (unsigned i = 0; i < sizeof(typeStrOfId) / sizeof(typeStrOfId[0]); ++i) {
 
2817
                        const TypeStrOfId& ts = typeStrOfId[i];
 
2818
                        if (ts.type == Frame::FT_Other && ts.supported && ts.str) {
 
2819
                                lst.append(ts.str);
 
2820
                        }
 
2821
                }
 
2822
        } else {
 
2823
                static const char* const fieldNames[] = {
 
2824
                        "ALBUMARTIST",
 
2825
                        "CATALOGNUMBER",
 
2826
                        "CONTACT",
 
2827
                        "DESCRIPTION",
 
2828
                        "EAN/UPN",
 
2829
                        "ENCODING",
 
2830
                        "ENGINEER",
 
2831
                        "ENSEMBLE",
 
2832
                        "GUEST ARTIST",
 
2833
                        "LABEL",
 
2834
                        "LABELNO",
 
2835
                        "LICENSE",
 
2836
                        "LOCATION",
 
2837
                        "OPUS",
 
2838
                        "ORGANIZATION",
 
2839
                        "PARTNUMBER",
 
2840
                        "PRODUCER",
 
2841
                        "PRODUCTNUMBER",
 
2842
                        "RECORDINGDATE",
 
2843
                        "RELEASE DATE",
 
2844
                        "REMIXER",
 
2845
                        "SOURCE ARTIST",
 
2846
                        "SOURCE MEDIUM",
 
2847
                        "SOURCE WORK",
 
2848
                        "SOURCEMEDIA",
 
2849
                        "SPARS",
 
2850
                        "TRACKTOTAL",
 
2851
                        "VERSION",
 
2852
                        "VOLUME"
 
2853
                };
 
2854
                for (unsigned i = 0; i < sizeof(fieldNames) / sizeof(fieldNames[0]); ++i) {
 
2855
                        lst.append(fieldNames[i]);
 
2856
                }
 
2857
        }
 
2858
        return lst;
 
2859
}
 
2860
 
 
2861
/**
 
2862
 * Static initialization.
 
2863
 * Registers file types.
 
2864
 */
 
2865
void TagLibFile::staticInit()
 
2866
{
 
2867
        TagLib::FileRef::addFileTypeResolver(new SpeexFileTypeResolver);
 
2868
        TagLib::FileRef::addFileTypeResolver(new WavPackFileTypeResolver);
 
2869
        TagLib::FileRef::addFileTypeResolver(new TTAFileTypeResolver);
 
2870
        TagLib::FileRef::addFileTypeResolver(new AACFileTypeResolver);
 
2871
        TagLib::FileRef::addFileTypeResolver(new MP2FileTypeResolver);
 
2872
}
 
2873
 
 
2874
/**
 
2875
 * Set the default text encoding.
 
2876
 *
 
2877
 * @param textEnc default text encoding
 
2878
 */
 
2879
void TagLibFile::setDefaultTextEncoding(MiscConfig::TextEncoding textEnc)
 
2880
{
 
2881
        // Do not use TagLib::ID3v2::FrameFactory::setDefaultTextEncoding(),
 
2882
        // it will change the encoding of existing frames read in, not only
 
2883
        // of newly created frames, which is really not what we want!
 
2884
        switch (textEnc) {
 
2885
                case MiscConfig::TE_ISO8859_1:
 
2886
                        s_defaultTextEncoding = TagLib::String::Latin1;
 
2887
                        break;
 
2888
                case MiscConfig::TE_UTF16:
 
2889
                        s_defaultTextEncoding = TagLib::String::UTF16;
 
2890
                        break;
 
2891
                case MiscConfig::TE_UTF8:
 
2892
                default:
 
2893
                        s_defaultTextEncoding = TagLib::String::UTF8;
 
2894
        }
 
2895
}
 
2896
 
 
2897
/**
 
2898
 * Create an TagLibFile object if it supports the filename's extension.
 
2899
 *
 
2900
 * @param di directory information
 
2901
 * @param fn filename
 
2902
 *
 
2903
 * @return tagged file, 0 if type not supported.
 
2904
 */
 
2905
TaggedFile* TagLibFile::Resolver::createFile(const DirInfo* di,
 
2906
                                                                                                                                                                        const QString& fn) const
 
2907
{
 
2908
        QString ext = fn.right(4).QCM_toLower();
 
2909
        if (((ext == ".mp3" || ext == ".mp2" || ext == ".aac")
 
2910
#ifdef HAVE_ID3LIB
 
2911
                         && Kid3App::s_miscCfg.m_id3v2Version == MiscConfig::ID3v2_4_0
 
2912
#endif
 
2913
                                )
 
2914
                        || ext == ".mpc" || ext == ".ogg" || ext == "flac"
 
2915
                        || ext == ".spx" || ext == ".tta"
 
2916
                        || ext.right(3) == ".wv")
 
2917
                return new TagLibFile(di, fn);
 
2918
        else
 
2919
                return 0;
 
2920
}
 
2921
 
 
2922
/**
 
2923
 * Get a list with all extensions supported by TagLibFile.
 
2924
 *
 
2925
 * @return list of file extensions.
 
2926
 */
 
2927
QStringList TagLibFile::Resolver::getSupportedFileExtensions() const
 
2928
{
 
2929
        return QStringList() << ".flac" << ".mp3" << ".mpc" << ".ogg" <<
 
2930
                ".spx" << ".tta" << ".aac" << ".mp2" << ".wv";
1240
2931
}
1241
2932
 
1242
2933
#endif // HAVE_TAGLIB