00001 #include <mythtv/mythcontext.h>
00002 #include "treebuilders.h"
00003
00004 typedef struct {
00005 QString field;
00006 MetadataPtrList list;
00007 } Branch;
00008
00009 typedef struct {
00010 QString testStr;
00011 QString dispStr;
00012 } FieldSplitInfo;
00013
00014
00015
00016
00017
00018 static FieldSplitInfo splitArray4[] =
00019 {
00020 {"!\"#$%&'()*+,-./:;<=>?@[\\]^_{|}~", " (...)"},
00021 {"01234", " (0 1 2 3 4)" },
00022 {"56789", " (5 6 7 8 9)" },
00023 {"ABCDE", " (A B C D E)"},
00024 {"FGHIJ", " (F G H I J)"},
00025 {"KLMNO", " (K L M N O)"},
00026 {"PQRST", " (P Q R S T)"},
00027 {"UVWXYZ", " (U V W X Y Z)"}
00028 };
00029
00030 static FieldSplitInfo splitArray16[] =
00031 {
00032 {"!\"#$%&'()*+,-./:;<=>?@[\\]^_{|}~", " (...)"},
00033 {"01234", " (0 1 2 3 4)" },
00034 {"56789", " (5 6 7 8 9)" },
00035 {"AB", " (A B)"},
00036 {"CD", " (C D)"},
00037 {"EF", " (E F)"},
00038 {"GH", " (G H)"},
00039 {"IJ", " (I J)"},
00040 {"KL", " (K L)"},
00041 {"MN", " (M N)"},
00042 {"OP", " (O P)"},
00043 {"QR", " (Q R)"},
00044 {"ST", " (S T)"},
00045 {"UV", " (U V)"},
00046 {"WX", " (W X)"},
00047 {"YZ", " (Y Z)"}
00048 };
00049
00050 static FieldSplitInfo splitArray29[] =
00051 {
00052 {"!\"#$%&'()*+,-./:;<=>?@[\\]^_{|}~", " (...)"},
00053 {"01234", " (0 1 2 3 4)" },
00054 {"56789", " (5 6 7 8 9)" },
00055 {"A", " A"},
00056 {"B", " B"},
00057 {"C", " C"},
00058 {"D", " D"},
00059 {"E", " E"},
00060 {"F", " F"},
00061 {"G", " G"},
00062 {"H", " H"},
00063 {"I", " I"},
00064 {"J", " J"},
00065 {"K", " K"},
00066 {"L", " L"},
00067 {"M", " M"},
00068 {"N", " N"},
00069 {"O", " O"},
00070 {"P", " P"},
00071 {"Q", " Q"},
00072 {"R", " R"},
00073 {"S", " S"},
00074 {"T", " T"},
00075 {"U", " U"},
00076 {"V", " V"},
00077 {"W", " W"},
00078 {"X", " X"},
00079 {"Y", " Y"},
00080 {"Z", " Z"}
00081 };
00082
00083 const int kSplitArray4_Max = sizeof splitArray4 / sizeof splitArray4[0];
00084 const int kSplitArray16_Max = sizeof splitArray16 / sizeof splitArray16[0];
00085 const int kSplitArray29_Max = sizeof splitArray29 / sizeof splitArray29[0];
00086
00087 static QString thePrefix = "the ";
00088
00089 MusicTreeBuilder::MusicTreeBuilder()
00090 {
00091 m_depth = -1;
00092 }
00093
00094 MusicTreeBuilder::~MusicTreeBuilder()
00095 {
00096 }
00097
00098 void MusicTreeBuilder::makeTree(MusicNode *root, const MetadataPtrList &metas)
00099 {
00100 m_depth++;
00101
00102 typedef QMap<QString, Branch*> BranchMap;
00103 BranchMap branches;
00104
00105 Metadata *meta;
00106 QPtrListIterator<Metadata> iter(metas);
00107 while ((meta = iter.current()) != 0)
00108 {
00109 if (isLeafDone(meta))
00110 {
00111 root->addLeaf(meta);
00112 }
00113 else
00114 {
00115 QString field = getField(meta);
00116 QString field_key = field.lower();
00117
00118 if (field_key.left(4) == thePrefix)
00119 field_key = field_key.mid(4);
00120
00121 Branch *branch = branches[field_key];
00122 if (branch == NULL)
00123 {
00124 branch = new Branch;
00125 branch->field = field;
00126 branches[field_key] = branch;
00127 }
00128 branch->list.append(meta);
00129 }
00130
00131 ++iter;
00132 }
00133
00134 for(BranchMap::iterator it = branches.begin(); it != branches.end(); it++)
00135 {
00136 Branch *branch = it.data();
00137 MusicNode *sub_node = createNode(branch->field);
00138 root->addChild(sub_node);
00139 makeTree(sub_node, branch->list);
00140 delete branch;
00141 }
00142
00143 m_depth--;
00144 }
00145
00146 class MusicFieldTreeBuilder : public MusicTreeBuilder
00147 {
00148 public:
00149 MusicFieldTreeBuilder(const QString &paths)
00150 {
00151 m_paths = QStringList::split(' ', paths);
00152 }
00153
00154 ~MusicFieldTreeBuilder()
00155 {
00156 }
00157
00158 void makeTree(MusicNode *root, const MetadataPtrList &metas)
00159 {
00160 if ((uint)getDepth() + 2 >= m_paths.size())
00161 {
00162 root->setLeaves(metas);
00163 return;
00164 }
00165
00166 MusicTreeBuilder::makeTree(root, metas);
00167 }
00168
00169 protected:
00170 MusicNode *createNode(const QString &title)
00171 {
00172 return new MusicNode(title, m_paths[getDepth()]);
00173 }
00174
00175 bool isLeafDone(Metadata *)
00176 {
00177 return false;
00178 }
00179
00180 QString getField(Metadata *meta)
00181 {
00182 QString field = m_paths[getDepth()];
00183
00184 if (field == "splitartist1" ||
00185 field == "splitartist")
00186 {
00187 return getSplitField(meta, field);
00188 }
00189
00190 QString data;
00191 meta->getField(field, &data);
00192 return data;
00193 }
00194
00195 private:
00196 QStringList m_paths;
00197 QMap<QChar, QString> m_split_map;
00198
00199 QString getSplitField(Metadata *meta, const QString &field)
00200 {
00201 QString firstchar_str = meta->FormatArtist().stripWhiteSpace();
00202
00203 if (firstchar_str.left(4).lower() == thePrefix)
00204 firstchar_str = firstchar_str.mid(4,1).upper();
00205 else
00206 firstchar_str = firstchar_str.left(1).upper();
00207
00208 QChar firstchar = firstchar_str[0];
00209 QString split = m_split_map[firstchar];
00210
00211 if (split.isEmpty())
00212 {
00213 if (field == "splitartist1")
00214 {
00215 split = QObject::tr("Artists") + " (" + firstchar + ")";
00216 m_split_map[firstchar] = split;
00217 }
00218 else
00219 {
00220 QString artistGrouping = gContext->GetSetting("ArtistTreeGroups", "none");
00221 if (artistGrouping == "2")
00222 {
00223 int split_max = kSplitArray29_Max;
00224 FieldSplitInfo *splits = splitArray29;
00225
00226 for(int i = 0; i < split_max; i++)
00227 {
00228 if (splits[i].testStr.contains(firstchar))
00229 {
00230 split = QObject::tr("Artists") + splits[i].dispStr;
00231 m_split_map[firstchar] = split;
00232 break;
00233 }
00234 }
00235 }
00236 else
00237 {
00238 if (artistGrouping == "1")
00239 {
00240 int split_max = kSplitArray16_Max;
00241 FieldSplitInfo *splits = splitArray16;
00242
00243 for(int i = 0; i < split_max; i++)
00244 {
00245 if (splits[i].testStr.contains(firstchar))
00246 {
00247 split = QObject::tr("Artists") + splits[i].dispStr;
00248 m_split_map[firstchar] = split;
00249 break;
00250 }
00251 }
00252 }
00253 else
00254 {
00255
00256 int split_max = kSplitArray4_Max;
00257 FieldSplitInfo *splits = splitArray4;
00258
00259 for(int i = 0; i < split_max; i++)
00260 {
00261 if (splits[i].testStr.contains(firstchar))
00262 {
00263 split = QObject::tr("Artists") + splits[i].dispStr;
00264 m_split_map[firstchar] = split;
00265 break;
00266 }
00267 }
00268 }
00269 }
00270 }
00271 }
00272
00273 if (split.isEmpty())
00274 {
00275 split = QObject::tr("Artists") + " (" + firstchar + ")";
00276 m_split_map[firstchar] = split;
00277 }
00278
00279 return split;
00280 }
00281 };
00282
00283 class MusicDirectoryTreeBuilder : public MusicTreeBuilder
00284 {
00285 public:
00286 MusicDirectoryTreeBuilder()
00287 {
00288 m_startdir = gContext->GetSetting("MusicLocation");
00289 }
00290
00291 ~MusicDirectoryTreeBuilder()
00292 {
00293 for(MetaMap::iterator it = m_map.begin(); it != m_map.end(); it++)
00294 delete it.data();
00295 }
00296
00297 protected:
00298 MusicNode *createNode(const QString &title)
00299 {
00300 return new MusicNode(title, "directory");
00301 }
00302
00303 bool isLeafDone(Metadata *meta)
00304 {
00305 return(uint)getDepth() + 1 >= getPathsForMeta(meta)->size();
00306 }
00307
00308 QString getField(Metadata *meta)
00309 {
00310 return getPathsForMeta(meta)->operator[](getDepth());
00311 }
00312
00313 private:
00314 inline QString getStartdir(void) { return m_startdir; }
00315
00316 QStringList* getPathsForMeta(Metadata *meta)
00317 {
00318 QStringList *paths = m_map[meta];
00319
00320 if (paths)
00321 return paths;
00322
00323 QString filename = meta->Filename().remove(0, getStartdir().length());
00324 paths = new QStringList(QStringList::split('/', filename));
00325 m_map[meta] = paths;
00326
00327 return paths;
00328 }
00329
00330 typedef QMap<Metadata*,QStringList*> MetaMap;
00331 MetaMap m_map;
00332 QString m_startdir;
00333
00334 };
00335
00336 MusicTreeBuilder *MusicTreeBuilder::createBuilder(const QString &paths)
00337 {
00338 if (paths == "directory")
00339 return new MusicDirectoryTreeBuilder();
00340
00341 return new MusicFieldTreeBuilder(paths);
00342 }
00343