Ticket #1287: 8-trees.patch

File 8-trees.patch, 24.3 KB (added by eskil <myth@…>, 18 years ago)

move treebuilding to a object and make it fast

  • mythmusic/mythmusic/mythmusic.pro

     
    3636HEADERS += goom/ifs.h goom/lines.h goom/mythgoom.h goom/drawmethods.h
    3737HEADERS += goom/mmx.h goom/mathtools.h goom/tentacle3d.h goom/v3d.h
    3838HEADERS += editmetadata.h smartplaylist.h search.h genres.h
     39HEADERS += treebuilders.h
    3940
    4041SOURCES += cddecoder.cpp cdrip.cpp decoder.cpp
    4142SOURCES += flacdecoder.cpp flacencoder.cpp maddecoder.cpp main.cpp
     
    5051SOURCES += goom/ifs.c goom/ifs_display.c goom/lines.c goom/surf3d.c
    5152SOURCES += goom/zoom_filter_mmx.c goom/zoom_filter_xmmx.c goom/mythgoom.cpp
    5253SOURCES += avfdecoder.cpp editmetadata.cpp smartplaylist.cpp search.cpp
     54SOURCES += treebuilders.cpp
  • mythmusic/mythmusic/metadata.cpp

     
    1111#include <mythtv/mythdbcon.h>
    1212
    1313#include "metadata.h"
     14#include "treebuilders.h"
    1415
    15 struct FieldSplitInfo {
    16    QString testStr;
    17    QString dispStr;
    18 };
    19 
    20 static FieldSplitInfo splitArray4[] =
    21 {
    22   {"ABCDE", " (A B C D E)"},
    23   {"FGHIJ", " (F G H I J)"},
    24   {"KLMNO", " (K L M N O)"},
    25   {"PQRST", " (P Q R S T)"},
    26   {"UVWXYZ", " (U V W X Y Z)"}
    27 };
    28 const int kSplitArray4_Max = sizeof splitArray4 / sizeof splitArray4[0];
    29 
    30 static FieldSplitInfo splitArray1[] =
    31 {
    32   {"A", " (A)"},
    33   {"B", " (B)"},
    34   {"C", " (C)"},
    35   {"D", " (D)"},
    36   {"E", " (E)"},
    37   {"F", " (F)"},
    38   {"G", " (G)"},
    39   {"H", " (H)"},
    40   {"I", " (I)"},
    41   {"J", " (J)"},
    42   {"K", " (K)"},
    43   {"L", " (L)"},
    44   {"M", " (M)"},
    45   {"N", " (N)"},
    46   {"O", " (O)"},
    47   {"P", " (P)"},
    48   {"Q", " (Q)"},
    49   {"R", " (R)"},
    50   {"S", " (S)"},
    51   {"T", " (T)"},
    52   {"U", " (U)"},
    53   {"V", " (V)"},
    54   {"W", " (W)"},
    55   {"X", " (X)"},
    56   {"Y", " (Y)"},
    57   {"Z", " (Z)"},
    58 };
    59 const int kSplitArray1_Max = sizeof splitArray1 / sizeof splitArray1[0];
    60 
    6116static QString thePrefix = "the ";
    6217
    6318bool operator==(const Metadata& a, const Metadata& b)
     
    454405    }
    455406}
    456407
    457 bool Metadata::areYouFinished(uint depth, uint treedepth, const QString &paths)
    458 {
    459     if(paths == "directory")
    460     {
    461         //  have we made it to directory just above the file name?
    462 
    463         QString working = filename;
    464         working.replace(QRegExp(m_startdir), QString(""));
    465         working = working.section('/', depth);
    466         if(working.contains('/') < 1)
    467         {
    468             return true;
    469         }
    470     }
    471     else
    472     {
    473         if(depth + 1 >= treedepth)
    474         {
    475             return true;
    476         }
    477     }
    478     return false;
    479 }
    480 
    481 void Metadata::getField(const QStringList &tree_levels, QString *data, const QString &paths, uint depth)
    482 {
    483     if(paths == "directory")
    484     {
    485         //  Return directory values as if they were
    486         //  real metadata/TAG values
    487        
    488         QString working = filename;
    489         working.replace(QRegExp(m_startdir), QString(""));
    490         working.replace(QRegExp("/[^/]*$"), QString(""));
    491 
    492         working = working.section('/', depth, depth);       
    493        
    494         *data = working;
    495     }
    496     else
    497     {
    498         getField(tree_levels[depth], data);
    499     }
    500 }
    501 
    502408void Metadata::getField(const QString &field, QString *data)
    503409{
    504410    if (field == "artist")
     
    509415        *data = FormatTitle();
    510416    else if (field == "genre")
    511417        *data = genre;
    512     else if (field == "splitartist")
    513     {
    514         bool set = false;
    515         QString firstchar;
    516         if (FormatArtist().left(4).lower() == thePrefix)
    517             firstchar = FormatArtist().mid(4, 1).upper();
    518         else
    519             firstchar = FormatArtist().left(1).upper();
    520 
    521         for (int i = 0; i < kSplitArray4_Max; i++)
    522         {
    523             if (splitArray4[i].testStr.contains(firstchar))
    524             {
    525                 set = true;
    526                 *data = QObject::tr("Artists") + splitArray4[i].dispStr;
    527             }
    528         }
    529 
    530         if (!set)
    531             *data = QObject::tr("Artists") + " (" + firstchar + ")";
    532     }
    533     else if (field == "splitartist1")
    534     {
    535         bool set = false;
    536         QString firstchar;
    537         if (FormatArtist().left(4).lower() == thePrefix)
    538             firstchar = FormatArtist().mid(4, 1).upper();
    539         else
    540             firstchar = FormatArtist().left(1).upper();
    541 
    542         for (int i = 0; i < kSplitArray1_Max; i++)
    543         {
    544             if (splitArray1[i].testStr.contains(firstchar))
    545             {
    546                 set = true;
    547                 *data = QObject::tr("Artists") + splitArray1[i].dispStr;
    548             }
    549         }
    550 
    551         if (!set)
    552             *data = QObject::tr("Artists") + " (" + firstchar + ")";
    553     }
    554418    else
    555419    {
    556420        cerr << "metadata.o: Something asked me to return data about a field called " << field << endl ;
     
    942786        numLoaded++;
    943787    }
    944788
    945     intoTree(list);
     789    MusicTreeBuilder *builder = MusicTreeBuilder::createBuilder (paths);
     790    builder->makeTree (root_node, list);
     791    delete builder;
    946792}
    947793
    948794void AllMusic::writeTree(GenericTree *tree_to_write_to)
    949795{
    950796    root_node->writeTree(tree_to_write_to, 0);
    951797}
    952798
    953799bool AllMusic::putYourselfOnTheListView(TreeCheckItem *where)
    954800{
    955801    root_node->putYourselfOnTheListView(where, false);
    956802    return true;
    957803}
    958804
    959805void AllMusic::putCDOnTheListView(CDCheckItem *where)
     
    1041824    } 
    1042825}
    1043826
    1044 
    1045 void AllMusic::intoTree(QPtrList<Metadata> &list)
    1046 {
    1047     uint depth = 0;
    1048     QString a_field = "";
    1049 
    1050     QDict<MetadataPtrList> mapping;
    1051     QPtrListIterator<Metadata> iter( list );
    1052     MetadataPtrList *curList;
    1053     mapping.setAutoDelete(true);
    1054 
    1055     Metadata *cur;
    1056     while ((cur = iter.current()) != 0)
    1057     {
    1058         if (cur->areYouFinished(depth, tree_levels.count(), paths, m_startdir))
    1059         {
    1060             //  special case, track is at root level
    1061             //  e.g. an mp3 in the root directory and
    1062             //  paths=directory
    1063             root_node->insert(cur);
    1064             ++iter;
    1065             continue;
    1066         }
    1067 
    1068         cur->getField(tree_levels.first(), &a_field, paths, m_startdir, depth);
    1069         curList = mapping.find(a_field);
    1070         if (!curList)
    1071         {
    1072             curList = new MetadataPtrList;
    1073             mapping.insert(a_field, curList);
    1074         }
    1075         curList->append(cur);
    1076         ++iter;
    1077     }
    1078 
    1079     QDictIterator<MetadataPtrList> rest(mapping);
    1080     while ((curList = rest.current()) != 0)
    1081     {
    1082         a_field = rest.currentKey();
    1083         MusicNode *new_one = new MusicNode(a_field, tree_levels, 0);
    1084         top_nodes.append(new_one);
    1085         new_one->intoTree(tree_levels, *curList, depth + 1);
    1086         ++rest;
    1087     }
    1088 }
    1089 
    1090827QString AllMusic::getLabel(int an_id, bool *error_flag)
    1091828{
    1092829    QString a_label = "";
     
    1232969
    1233970    //  Error checking
     971    QStringList tree_levels = QStringList::split(" ", paths);
    1234972    QStringList::const_iterator it = tree_levels.begin();
    1235973    for (; it != tree_levels.end(); ++it)
    1236974    {
     
    12651003    }
    12661004}
    12671005
    1268 MusicNode::MusicNode(QString a_title, QStringList tree_levels, uint depth)
     1006MusicNode::MusicNode(const QString &a_title, const QString &tree_level)
    12691007{
    12701008    my_title = a_title;
    1271     if (m_paths == "directory")
    1272     {
    1273         my_level = "directory";
    1274     }
    1275     else
    1276     {
    1277         if (depth < tree_levels.count())
    1278             my_level = tree_levels[depth];
    1279         else
    1280         {
    1281             my_level = "I am confused";
    1282             cerr << "metadata.o: Something asked me to look up a StringList entry that doesn't exist" << endl ;
    1283         }
    1284        
    1285     }
    1286 
     1009    my_level = tree_level;
    12871010    my_subnodes.setAutoDelete(true);
    12881011}
    12891012
    12901013MusicNode::~MusicNode()
     
    13111038    m_RandomWeight = gContext->GetNumSetting("IntelliRandomWeight", 2);
    13121039}
    13131040   
    1314 void MusicNode::insert(Metadata* inserter)
    1315 {
    1316     my_tracks.append(inserter);
    1317 }
    1318 
    1319 void MusicNode::intoTree(QStringList tree_levels,
    1320                          MetadataPtrList &list, uint depth)
    1321 {
    1322     QString a_field = "";
    1323     QString a_lowercase_field = "";
    1324     QString a_title = "";
    1325     bool usesPath = false;
    1326 
    1327     if (m_paths == "directory")
    1328         usesPath = true;
    1329     else
    1330     {
    1331         if (depth + 1 >= tree_levels.count())
    1332         {
    1333             my_tracks = list;
    1334             return;
    1335         }
    1336     }
    1337 
    1338     //  Search and create from my node downards
    1339 
    1340     QDict<MetadataPtrList> mapping;
    1341     QPtrListIterator<Metadata> iter(list);
    1342     MetadataPtrList *curList;
    1343     mapping.setAutoDelete(true);
    1344 
    1345     Metadata *cur;
    1346     while ((cur = iter.current()) != 0)
    1347     {
    1348         if (usesPath && cur->areYouFinished(depth, tree_levels.count(), m_paths, m_startdir))
    1349         {
    1350             insert(cur);
    1351             ++iter;
    1352             continue;
    1353         }
    1354  
    1355         cur->getField(tree_levels, &a_field, m_paths, m_startdir, depth);
    1356  
    1357         a_lowercase_field = a_field.lower();
    1358         if (a_lowercase_field.left(4) == "the ")
    1359             a_field = a_field.mid(4);
    1360  
    1361         curList = mapping.find(a_field);
    1362         if (!curList)
    1363         {
    1364             curList = new MetadataPtrList;
    1365             mapping.insert(a_field, curList);
    1366         }
    1367         curList->append(cur);
    1368         ++iter;
    1369     }
    1370  
    1371     QDictIterator<MetadataPtrList> rest(mapping);
    1372     while ((curList = rest.current()) != 0)
    1373     {
    1374         a_field = rest.currentKey();
    1375         MusicNode *new_one = new MusicNode(a_field, tree_levels, depth);
    1376         my_subnodes.append(new_one);
    1377         new_one->intoTree(tree_levels, *curList, depth + 1);
    1378         ++rest;
    1379     }
    1380 }
    1381 
    13821041void MusicNode::putYourselfOnTheListView(TreeCheckItem *parent, bool show_node)
    13831042{
    13841043    TreeCheckItem *current_parent;
  • mythmusic/mythmusic/metadata.h

     
    126126    void updateDatabase(void);
    127127    void setField(const QString &field, const QString &data);
    128     void getField(const QString &field, QString *data);
    129     void getField(const QStringList& tree_levels, QString *data, const QString &paths, const QString &startdir, uint depth);
    130     bool areYouFinished(uint depth, uint treedepth, const QString& paths, const QString& startdir);
     128    void getField(const QString& field, QString *data);
    131129    void fillData();
    132130    void fillDataFromID();
    133131    void persist();
     
    209207};
    210208
    211209class MusicNode
    212 {
    213     //  Not a root of the music tree, and
    214     //  not a leaf, but anything in the
    215     //  middle
    216    
     210{   
    217211  public:
    218212 
    219     MusicNode(QString a_title, QStringList tree_levels, uint depth);
     213    MusicNode(const QString &a_title, const QString &tree_level);
    220214   ~MusicNode();
    221215
    222     void        insert(Metadata* inserter);
    223216    QString     getTitle(){return my_title;}
    224     void        intoTree(QStringList tree_levels, MetadataPtrList &list,
    225                          uint depth);
    226217    void        printYourself(int indent_amount);   // debugging
    227     void        clearTracks() { my_tracks.clear(); }
    228218    void        putYourselfOnTheListView(TreeCheckItem *parent, bool show_node);
    229219    void        writeTree(GenericTree *tree_to_write_to, int a_counter);
    230220    void        sort();
     
    233223    void        setLastPlayMin(double tmp_min) { lastplayMin = tmp_min; }
    234224    void        setLastPlayMax(double tmp_max) { lastplayMax = tmp_max; }
    235225
     226    inline void addChild(MusicNode *child) { my_subnodes.append(child); }
     227    inline void addLeaf(Metadata *leaf) { my_tracks.append(leaf); }
     228    inline void setLeaves(MetadataPtrList leaves) { my_tracks = leaves; }
     229
     230    void clear(void) {
     231        my_tracks.clear();
     232        my_subnodes.clear();
     233    }
     234
    236235    static void SetStaticData(const QString &startdir, const QString &paths);
    237236 
    238237  private:
    239238 
    240239    MetadataPtrList     my_tracks;
    241240    MusicNodePtrList    my_subnodes;
    242     QDict<MusicNode>    my_subnode_hash;
    243241    QString             my_title;
    244242    QString             my_level;
    245243
     
    297295    void        printTree();    // debugging
    298296    void        sortTree();
    299297    void        writeTree(GenericTree *tree_to_write_to);
    300     void        intoTree(QPtrList<Metadata> &list);
    301298    void        setSorting(QString a_paths);
    302     bool        putYourselfOnTheListView(TreeCheckItem *where, int how_many);
     299    bool        putYourselfOnTheListView(TreeCheckItem *where);
    303300    void        putCDOnTheListView(CDCheckItem *where);
    304301    bool        doneLoading(){return done_loading;}
    305302    bool        cleanOutThreads();
     
    330326
    331327    QString     startdir;
    332328    QString     paths;
    333     QStringList tree_levels;
    334329   
    335330   
    336331    MetadataLoadingThread   *metadata_loader;
  • mythmusic/mythmusic/databasebox.cpp

     
    189189        the_playlists->doneLoading())
    190190    {
    191191        //  Good, now lets grab some QListItems
    192         //
    193         //  Say ... I dunno ... 100 at a time?
    194 
    195         if (all_music->putYourselfOnTheListView(allmusic, 100))
     192        if (all_music->putYourselfOnTheListView(allmusic))
    196193        {
    197194            allmusic->setText(tr("All My Music"));
    198195            fill_list_timer->stop();
  • mythmusic/mythmusic/treebuilders.h

     
     1#ifndef TREEBUILDERS_H_
     2#define TREEBUILDERS_H_
     3
     4#include <qstring.h>
     5#include <qstringlist.h>
     6#include <qptrdict.h>
     7#include <qdict.h>
     8
     9#include "metadata.h"
     10
     11/** \class MusicTreeBuilder
     12    \brief superclass for the objects that build trees of \p Metadata
     13   
     14    This is the interface for objects that builds trees of \p Metadata
     15    objects into \p MusicNode objects.
     16
     17    The basic idea of operation is, that the superclass provices a
     18    makeTree method that depends on the subclasses for a few methods
     19    to determine which fields of the metadata objects are used in
     20    building the tree and where the metadata objects go.
     21
     22    This means that the superclass is fairly generic and does not
     23    itself operate on or query the metadata objects, but leaves that
     24    to the subclass. It's only responsibility is to determine the
     25    branches that go into the tree and then add metadata objects as
     26    leafs to the appropriate nodes.
     27
     28    Ie. if you wanted to build the MythMusic tree based on the size of
     29    the audio files by implementing a \p MusicSizeTreeBuilder, the
     30    approach would be to get \p MusicSizeTreeBuilder::getField return
     31    the number of MB or KB blocks depending on the current depth as
     32    obtained by \p getDept. Then you could either overload \p makeTree
     33    to stop at a certain depth (like \p MusicFieldBuilder does) or
     34    make \p MusicSizeTreeBuilder::isLeafDone return true if you've
     35    reached the desired granularity.
     36
     37    \note This method is fairly generic, and uses very little of the
     38    interfaces of \p MusicNode and \p Metadata, so it may someday be
     39    made more generic and of use to other plugins that needs trees.
     40
     41    \note Calling this repeated using the same or new instance on the
     42    same root node (ie. to progressively populate it) hasn't been
     43    tested.
     44 */
     45class MusicTreeBuilder
     46{
     47  public:
     48    virtual ~MusicTreeBuilder();
     49
     50    /** \fn Create a tree using the list of \p Metadata objects and add them to the given root.
     51
     52        This method will recurse and operate of the list the metadata
     53        objects in \p metas. It's implementation may and probably will
     54        differ depending on the subclass of builder you've obtained,
     55        depending on the kind of tree it builds.
     56
     57        It relies heavily on subclasses implementing \p
     58        MusicTreeBuilder::isLeafDone and \p MusicTreeBuilder::getField
     59        for operation, and for performance, the subclasses should
     60        cache computed data in these methods as efficiently as possible.
     61     */
     62    virtual void makeTree(MusicNode *root, const MetadataPtrList &metas);
     63
     64    /** \fn Create an \p MusicTreeBuilder for the appropriate path.
     65
     66        Returns a \p MusicTreeBuilder subclass for building directory
     67        based or "artist album" field based trees.
     68     */
     69    static MusicTreeBuilder *createBuilder(const QString &paths);
     70
     71  protected:
     72    MusicTreeBuilder();
     73
     74    /** \fn Allocates and returns a new \p MusicNode.
     75
     76        Implemented by the subclass. This method should allocate and a
     77        return a \p MusicNode with the approriate "level" set.
     78     */
     79    virtual MusicNode *createNode(const QString &title) = 0;
     80
     81    /** \fn Determine is a \p Metadata should be track at the current depth.
     82 
     83        Ie. the directory builder will return true if the given \p
     84        Metadata's path at the current depth is the filename.
     85
     86         Gets called repeatedly from \p MusicTreeBuilder::makeTree
     87         during tree creation and should only get called once
     88         pr. depth pr. \p Metadata.
     89     */
     90    virtual bool isLeafDone(Metadata *m) = 0;
     91
     92    /** \fn Get the field value for the given \p Metadata at the current depth.
     93
     94         Ie. the field builder will call \p Metadata::getField with
     95         the appropriate field name for the current dept.
     96
     97         Gets called repeatedly from \p MusicTreeBuilder::makeTree
     98         during tree creation and may get called multiple times at the
     99         same depth for the same \p Metadata.
     100     */
     101    virtual QString getField(Metadata *m) = 0; 
     102
     103    /** \fn Get the current depth during tree building.
     104       
     105        While \p MusicTreeBuilder::makeTree is recursing downwards to
     106        build the tree, this method will return the current depth and
     107        can/should be used in the subclass when implemented the
     108        virtual methods.
     109
     110        Ie. the directory builder can use this in isLeafDone to
     111        determine if the \p Metadata object under consideration has no
     112        more elements in it's path.
     113     */
     114    inline int getDepth(void) { return m_depth; }
     115
     116  private:
     117    int m_depth;
     118};
     119
     120#endif /* TREEBUILDERS_H_ */
  • mythmusic/mythmusic/treebuilders.cpp

     
     1#include <mythtv/mythcontext.h>
     2#include "treebuilders.h"
     3
     4typedef struct {
     5    QString field;
     6    MetadataPtrList list;
     7} Branch;
     8
     9typedef struct  {
     10   QString testStr;
     11   QString dispStr;
     12} FieldSplitInfo;
     13
     14static FieldSplitInfo splitArray4[] =
     15{
     16  {"!\"#$%&'()*+,-./:;<=>?@[\\]^_{|}~", " (...)"},
     17  {"01234", " (0 1 2 3 4)" },
     18  {"56789", " (5 6 7 8 9)" },
     19  {"ABCDE", " (A B C D E)"},
     20  {"FGHIJ", " (F G H I J)"},
     21  {"KLMNO", " (K L M N O)"},
     22  {"PQRST", " (P Q R S T)"},
     23  {"UVWXYZ", " (U V W X Y Z)"}
     24};
     25const int kSplitArray4_Max = sizeof splitArray4 / sizeof splitArray4[0];
     26
     27static QString thePrefix = "the ";
     28
     29MusicTreeBuilder::MusicTreeBuilder()
     30{
     31    m_depth = -1;
     32}
     33
     34MusicTreeBuilder::~MusicTreeBuilder()
     35{
     36}
     37
     38void MusicTreeBuilder::makeTree(MusicNode *root, const MetadataPtrList &metas)
     39{
     40    m_depth++;
     41       
     42    typedef QMap<QString, Branch*> BranchMap;
     43    BranchMap branches;
     44   
     45    Metadata *meta;
     46    QPtrListIterator<Metadata> iter(metas);
     47    while ((meta = iter.current()) != 0)
     48    {
     49        if (isLeafDone(meta))
     50        {
     51            root->addLeaf(meta);
     52        }
     53        else
     54        {
     55            QString field = getField(meta);
     56            QString field_key = field.lower();
     57
     58            if (field_key.left(4) == thePrefix)
     59                field_key = field_key.mid(4);
     60
     61            Branch *branch = branches[field_key];
     62            if (branch == NULL)
     63            {
     64                branch = new Branch;
     65                branch->field = field;
     66                branches[field_key] = branch;
     67            }
     68            branch->list.append(meta);
     69        }
     70
     71        ++iter;
     72    }
     73
     74    for(BranchMap::iterator it = branches.begin(); it != branches.end(); it++)
     75    {
     76        Branch *branch = it.data();
     77        MusicNode *sub_node = createNode(branch->field);
     78        root->addChild(sub_node);
     79        makeTree(sub_node, branch->list);
     80        delete branch;
     81    }
     82
     83    m_depth--;
     84}
     85
     86class MusicFieldTreeBuilder : public MusicTreeBuilder
     87{
     88  public:
     89    MusicFieldTreeBuilder(const QString &paths)
     90    {
     91        m_paths = QStringList::split(' ', paths);
     92    }
     93
     94    ~MusicFieldTreeBuilder()
     95    {
     96    }
     97   
     98    void makeTree(MusicNode *root, const MetadataPtrList &metas)
     99    {
     100        if ((uint)getDepth() + 2 >= m_paths.size())
     101        {
     102            root->setLeaves(metas);
     103            return;
     104        }
     105
     106        MusicTreeBuilder::makeTree(root, metas);
     107    }
     108   
     109protected:
     110    MusicNode *createNode(const QString &title)
     111    {
     112        return new MusicNode(title, m_paths[getDepth()]);
     113    }
     114
     115    bool isLeafDone(Metadata *)
     116    {
     117        return false;
     118    }
     119   
     120    QString getField(Metadata *meta)
     121    {
     122        QString field = m_paths[getDepth()];
     123       
     124        if (field == "splitartist1" ||
     125            field == "splitartist")
     126        {
     127            return getSplitField(meta, field);
     128        }
     129
     130        QString data;
     131        meta->getField(field, &data);
     132        return data;
     133    }
     134
     135private:
     136    QStringList m_paths;
     137    //QDict<QString> m_charToSplitMap;
     138    QMap<QChar, QString> m_split_map;
     139
     140    QString getSplitField(Metadata *meta, const QString &field)
     141    {
     142        QString firstchar_str = meta->FormatArtist().stripWhiteSpace();
     143
     144        if (firstchar_str.left(4).lower() == thePrefix)
     145            firstchar_str = firstchar_str.mid(4,1).upper();
     146        else
     147            firstchar_str = firstchar_str.left(1).upper();
     148       
     149        QChar firstchar = firstchar_str[0];
     150        QString split = m_split_map[firstchar];
     151       
     152        if (split.isEmpty())
     153        {
     154            if (field == "splitartist1")
     155            {
     156                split = QObject::tr("Artists ") + "(" + firstchar + ")";
     157                m_split_map[firstchar] = split;
     158            }
     159            else
     160            {
     161                int split_max = kSplitArray4_Max;
     162                FieldSplitInfo *splits = splitArray4;           
     163           
     164                for(int i = 0; i < split_max; i++)
     165                {
     166                    if (splits[i].testStr.contains(firstchar))
     167                    {
     168                        split = QObject::tr("Artists ") + splits[i].dispStr;
     169                        m_split_map[firstchar] = split;
     170                        break;
     171                    }
     172                }
     173            }
     174        }
     175
     176        if (split.isEmpty())
     177        {
     178            split = QObject::tr("Artists") + "(" + firstchar + ")";
     179            m_split_map[firstchar] = split;
     180        }
     181
     182        return split;
     183    }
     184};
     185
     186class MusicDirectoryTreeBuilder : public MusicTreeBuilder
     187{
     188  public:
     189    MusicDirectoryTreeBuilder()
     190    {
     191        m_startdir = gContext->GetSetting("MusicLocation");
     192    }
     193
     194    ~MusicDirectoryTreeBuilder()
     195    {
     196        for(MetaMap::iterator it = m_map.begin(); it != m_map.end(); it++)
     197            delete it.data();
     198    }
     199
     200protected:
     201    MusicNode *createNode(const QString &title)
     202    {
     203        return new MusicNode(title, "directory");
     204    }
     205
     206    bool isLeafDone(Metadata *meta)
     207    {
     208        return(uint)getDepth() + 1 >= getPathsForMeta(meta)->size();
     209    }
     210
     211    QString getField(Metadata *meta)
     212    {
     213        return getPathsForMeta(meta)->operator[](getDepth());
     214    }
     215
     216  private:
     217    inline QString getStartdir(void) { return m_startdir; }
     218
     219    QStringList* getPathsForMeta(Metadata *meta)
     220    {
     221        QStringList *paths = m_map[meta];
     222
     223        if (paths)
     224            return paths;
     225
     226        QString filename = meta->Filename().remove(0, getStartdir().length());
     227        paths = new QStringList(QStringList::split('/', filename));
     228        m_map[meta] = paths;
     229       
     230        return paths;
     231    }
     232
     233    typedef QMap<Metadata*,QStringList*> MetaMap;
     234    MetaMap m_map;
     235    QString m_startdir;
     236
     237};
     238
     239MusicTreeBuilder *MusicTreeBuilder::createBuilder(const QString &paths)
     240{
     241    if (paths == "directory")
     242        return new MusicDirectoryTreeBuilder();
     243
     244    return new MusicFieldTreeBuilder(paths);
     245}
     246