Index: mythtv/libs/libmyth/generictree.h
===================================================================
--- mythtv/libs/libmyth/generictree.h	(revision 12209)
+++ mythtv/libs/libmyth/generictree.h	(working copy)
@@ -29,10 +29,59 @@
 
     GenericTree *findLeaf(int ordering_index = -1);
 
-    GenericTree* findNode(QValueList<int> route_of_branches);
-    GenericTree* recursiveNodeFinder(QValueList<int> route_of_branches);
-    bool checkNode(QValueList<int> route_of_branches);
+    /** \brief Traverse using the given path.
 
+        C should be a container of items that can be looked up using
+        GenericTree::getChild. Ie. QValueList<int> or a QStringList.
+
+        Recursively move through children who has the keys identified
+        by path. For each element in path, find the child by calling
+        GenericTree::getChild, and move to the next branch.
+
+        If the route fails at some point, typically if the path
+        specifies a child that doesn't exist, the function will stop
+        and return NULL. Otherwise, once the end of the path is
+        reached, that node is returned.
+
+        \param path a container of elements that specifies a path.
+        \returns The destination, NULL if path did not exist.
+     */
+    template<typename C>
+    GenericTree* findNode(const C &path) 
+    {
+        GenericTree *result = this;
+        for (typename C::const_iterator it = path.begin(); 
+             result && it != path.end(); ++it)
+            result = result->getChild(*it);
+        return result;
+    }
+
+    /** \brief Detect if a given node is in a given path beneath this node.
+
+        C should be a container of items that can be looked up using
+        GenericTree::getChild. Ie. QValueList<int> or a QStringList.
+
+        Traverses the specified path and checks if node is in the
+        path. If the path does not exist, the method will return
+        false. If the end of the path is reached and node wasn't
+        encounter, the method will return false.
+
+        \param node the node to look for in the path.
+        \param path a container of elements that specifies a path.
+        \returns true if the node was found in the path, false if not
+        or if the path is invalid.
+    */
+    template<typename C> 
+    bool isNodeInPath (GenericTree *node, const C &path) 
+    {
+        GenericTree *climber = this;
+        for (typename C::const_iterator it = path.begin(); 
+             climber && climber != node && it != path.end(); ++it)
+            climber = climber->getChild(*it);
+        return climber && climber == node;
+    }
+
+
     GenericTree *nextSibling(int number_down, int ordering_index = -1);
     GenericTree *prevSibling(int number_up, int ordering_index = -1);
 
@@ -42,6 +91,8 @@
     GenericTree *getChildAt(uint reference, int ordering_index = -1);
     GenericTree *getChildByName(const QString &a_name);
     GenericTree *getChildByInt(int an_int);
+    GenericTree *getChild(int an_int) { return getChildByInt (an_int); }
+    GenericTree *getChild(const QString &a_name) { return getChildByName (a_name); }
 
     QPtrList<GenericTree> *getAllChildren(int ordering_index = -1);
 
Index: mythtv/libs/libmyth/uitypes.cpp
===================================================================
--- mythtv/libs/libmyth/uitypes.cpp	(revision 12209)
+++ mythtv/libs/libmyth/uitypes.cpp	(working copy)
@@ -3288,7 +3288,6 @@
     bins = 0;
     bin_corners.clear();
     screen_corners.clear();
-    route_to_active.clear();
     resized_highlight_images.setAutoDelete(true);
     my_tree_data = NULL;
     current_node = NULL;
@@ -3831,21 +3830,20 @@
     }
 }
 
-QValueList <int> * UIManagedTreeListType::getRouteToActive()
+QValueList <int>  UIManagedTreeListType::getRouteToActive()
 {
+    QValueList<int> route_to_active;
     if (active_node)
     {
-        route_to_active.clear();
         GenericTree *climber = active_node;
 
         route_to_active.push_front(climber->getInt());
-        while( (climber = climber->getParent()) )
+        while( (climber = climber->getParent()) && climber->getParent ())
         {
             route_to_active.push_front(climber->getInt());
         }
-        return &route_to_active;
     }
-    return NULL;
+    return route_to_active;
 }
 
 QStringList UIManagedTreeListType::getRouteToCurrent()
@@ -3855,7 +3853,7 @@
     {
         GenericTree *climber = current_node;
         route_to_current.push_front(climber->getString());
-        while( (climber = climber->getParent()) )
+        while( (climber = climber->getParent()) && climber->getParent ())
         {
             route_to_current.push_front(climber->getString());
         }
Index: mythtv/libs/libmyth/generictree.cpp
===================================================================
--- mythtv/libs/libmyth/generictree.cpp	(revision 12209)
+++ mythtv/libs/libmyth/generictree.cpp	(working copy)
@@ -175,66 +175,6 @@
     return this;
 }
 
-GenericTree* GenericTree::findNode(QValueList<int> route_of_branches)
-{
-    // Starting from *this* node (which will often be root) find a set of 
-    // branches that have id's that match the collection passed in 
-    // route_of_branches. Return the end point of those branches (which will 
-    // often be a leaf node).
-    //
-    // In practical terms, mythmusic will use this to force the playback 
-    // screen's ManagedTreeList to move to a given track in a given playlist
-
-    return recursiveNodeFinder(route_of_branches);
-}
-
-GenericTree* GenericTree::recursiveNodeFinder(QValueList<int> route_of_branches)
-{
-    if (checkNode(route_of_branches))
-        return this;
-    else
-    {
-        QPtrListIterator<GenericTree> it(*m_subnodes);
-        GenericTree *child;
-
-        while ((child = it.current()) != 0)
-        {
-            GenericTree *sub_checker;
-            sub_checker = child->recursiveNodeFinder(route_of_branches);
-            if (sub_checker)
-                return sub_checker;
-            else
-                ++it;
-        }
-    }
-
-    return NULL;
-}
-
-bool GenericTree::checkNode(QValueList<int> route_of_branches)
-{
-    bool found_it = true;
-    GenericTree *parent_finder = this;
-
-    // FIXME: slow
-
-    for (int i = route_of_branches.count() - 1; i > -1 && found_it; --i)
-    {
-        if (!(parent_finder->getInt() == (*route_of_branches.at(i))))
-            found_it = false;
-
-        if (i > 0)
-        {
-            if (parent_finder->getParent())
-                parent_finder = parent_finder->getParent();
-            else
-                found_it = false;
-        }
-    }
-
-    return found_it;
-}
-
 int GenericTree::getChildPosition(GenericTree *child, int ordering_index)
 {
     if (ordering_index == -1)
Index: mythtv/libs/libmyth/uitypes.h
===================================================================
--- mythtv/libs/libmyth/uitypes.h	(revision 12209)
+++ mythtv/libs/libmyth/uitypes.h	(working copy)
@@ -1054,7 +1054,7 @@
     void    assignTreeData(GenericTree *a_tree);
     void    moveToNode(QValueList<int> route_of_branches);
     void    moveToNodesFirstChild(QValueList<int> route_of_branchs);
-    QValueList <int>* getRouteToActive();
+    QValueList <int> getRouteToActive();
     QStringList getRouteToCurrent();
     bool    tryToSetActive(QValueList <int> route);
     bool    tryToSetCurrent(QStringList route);
@@ -1140,7 +1140,6 @@
     QPixmap                 right_arrow_image;
     QPtrList<QPixmap>       resized_highlight_images;
     QMap<int, QPixmap*>     highlight_map;
-    QValueList <int>        route_to_active;
     bool                    show_whole_tree;
     bool                    scrambled_parents;
     bool                    color_selectables;
Index: mythtv/libs/libmyth/mythmediamonitor.cpp
===================================================================
--- mythtv/libs/libmyth/mythmediamonitor.cpp	(revision 12209)
+++ mythtv/libs/libmyth/mythmediamonitor.cpp	(working copy)
@@ -888,19 +888,15 @@
             .arg(MythMediaDevice::MediaStatusStrings[pMedia->getStatus()])
             .arg(MythMediaDevice::MediaStatusStrings[oldStatus]));
 
-    // This gets called from outside the main thread so we need
-    // to post an event back to the main thread.
-    // We're only going to pass up events for useable media...
-    if (pMedia->getStatus() == MEDIASTAT_USEABLE || 
-        pMedia->getStatus() == MEDIASTAT_MOUNTED) 
-    {
-        VERBOSE(VB_IMPORTANT, "Posting MediaEvent");
-        QApplication::postEvent((QObject*)gContext->GetMainWindow(), 
-                                new MediaEvent(oldStatus, pMedia));
-    }
-    else if (pMedia->getStatus() == MEDIASTAT_OPEN ||
+    VERBOSE(VB_IMPORTANT, "Posting MediaEvent");
+    QApplication::postEvent((QObject*)gContext->GetMainWindow(), 
+                            new MediaEvent(oldStatus, pMedia));
+
+    if (pMedia->getStatus() == MEDIASTAT_OPEN ||
              pMedia->getStatus() == MEDIASTAT_UNPLUGGED)
     {
         pMedia->clearData();
     }
+
+    return;
 }
Index: mythtv/libs/libmyth/mythcdrom-linux.cpp
===================================================================
--- mythtv/libs/libmyth/mythcdrom-linux.cpp	(revision 12209)
+++ mythtv/libs/libmyth/mythcdrom-linux.cpp	(working copy)
@@ -124,7 +124,6 @@
                 //cout << "Tray open or no disc" << endl;
                 m_MediaType = MEDIATYPE_UNKNOWN;
                 return setStatus(MEDIASTAT_OPEN, OpenedHere);
-                break;
             case CDS_NO_INFO:
             case CDS_DRIVE_NOT_READY:
                 //cout << "No info or drive not ready" << endl;
Index: mythplugins/mythmusic/mythmusic/playbackbox.cpp
===================================================================
--- mythplugins/mythmusic/mythmusic/playbackbox.cpp	(revision 12209)
+++ mythplugins/mythmusic/mythmusic/playbackbox.cpp	(working copy)
@@ -26,7 +26,53 @@
 #include "mainvisual.h"
 #include "smartplaylist.h"
 #include "search.h"
+#include "treebuilders.h"
 
+// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+typedef enum 
+{ 
+    ACTIVE_PLAY_QUEUE = 0
+} PathID;
+
+QValueList<int> setPathTo(QValueList<int> &path, PathID p) 
+{
+    path.clear();
+    switch (p) 
+    {
+    case ACTIVE_PLAY_QUEUE:
+        path.append(1); //  All My Playlists
+        path.append(0); //  Active play Queue
+        break;
+    }
+    return path;
+}
+
+QValueList<int> setPathTo(QValueList<int> &path, GenericTree *node) 
+{
+    path.clear();
+    while (node->getParent()) 
+    {
+        path.prepend (node->getInt());
+        node = node->getParent();
+    }
+    return path;
+}
+
+QValueList<int> getPathTo(PathID p) 
+{
+    QValueList<int> result;
+    return setPathTo(result, p);
+}
+
+QValueList<int> getPathTo(GenericTree *node) 
+{
+    QValueList<int> result;
+    return setPathTo(result, node);
+}
+
+// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
 PlaybackBoxMusic::PlaybackBoxMusic(MythMainWindow *parent, QString window_name,
                                    QString theme_filename, 
                                    PlaylistsContainer *the_playlists,
@@ -67,9 +113,7 @@
 
     menufilters = gContext->GetNumSetting("MusicMenuFilters", 0);
 
-    cd_reader_thread = NULL;
-    cd_watcher = NULL;
-    scan_for_cd = gContext->GetNumSetting("AutoPlayCD", 0);
+    bool check_cd = gContext->GetNumSetting("AutoLookupCD", 0);
  
     // Set our pointers the playlists and the metadata containers
 
@@ -124,6 +168,19 @@
     else
         setRepeatMode(REPEAT_OFF);
 
+    store_manager = NULL;
+    if (check_cd) {
+        store_manager = new StoreManager;
+        connect(store_manager, SIGNAL(storeDetected(Store*)),
+                this, SLOT(storeDetected(Store*)));
+        connect(store_manager, SIGNAL(storeRemoved(Store*)),
+                this, SLOT(storeRemoved(Store*)));
+        connect(store_manager, SIGNAL(storeLoading(Store*)),
+                this, SLOT(storeLoading(Store*)));
+        connect(store_manager, SIGNAL(storeReady(Store*)),
+                this, SLOT(storeReady(Store*)));
+    }
+
     // Set some button values
     
     if (!keyboard_accelerators) 
@@ -225,11 +282,17 @@
         progress = NULL;
     }
 
-    if (cd_reader_thread)
+    if (store_manager)
     {
-        cd_watcher->stop();
-        cd_reader_thread->wait();
-        delete cd_reader_thread;
+        disconnect(store_manager, SIGNAL(storeDetected(Store*)),
+                   this, SLOT(storeDetected(Store*)));
+        disconnect(store_manager, SIGNAL(storeRemoved(Store*)),
+                   this, SLOT(storeRemoved(Store*)));
+        disconnect(store_manager, SIGNAL(storeLoading(Store*)),
+                   this, SLOT(storeLoading(Store*)));
+        disconnect(store_manager, SIGNAL(storeReady(Store*)),
+                   this, SLOT(storeReady(Store*)));
+        store_manager->deleteLater();
     }
 
     if (playlist_tree)
@@ -254,9 +317,11 @@
         lcd->switchToTime();
 }
 
-bool PlaybackBoxMusic::onMediaEvent(MythMediaDevice*)
+bool PlaybackBoxMusic::onMediaEvent(MythMediaDevice *device)
 {
-    return scan_for_cd;
+    if (store_manager)
+        return store_manager->onMediaEvent(device);
+    return false;    
 }
 
 void PlaybackBoxMusic::keyPressEvent(QKeyEvent *e)
@@ -512,8 +577,10 @@
 
     playlist_popup->addButton(tr("Search"), this,
                               SLOT(showSearchDialog()));
-    playlist_popup->addButton(tr("From CD"), this,
-                              SLOT(fromCD()));
+    if (!stores.empty())
+        playlist_popup->addButton(tr("From CD"), this,
+                                  SLOT(fromCD()));
+
     playlist_popup->addButton(tr("All Tracks"), this,
                               SLOT(allTracks()));
     if (curMeta)
@@ -556,8 +623,14 @@
 {
     if (!playlist_popup)
         return;
-
-    updatePlaylistFromCD();
+    if(all_music->getCDTrackCount()) {
+        visual_mode_timer->stop();
+        
+        all_playlists->getActive()->ripOutAllCDTracksNow();
+        all_playlists->getActive()->fillSongsFromCD();
+        postUpdate();
+    }
+        
     closePlaylistPopup();
 }
 
@@ -669,19 +742,14 @@
     // store path to current track
     if (curMeta)
     {
-        QValueList <int> *a_route;
-        a_route = music_tree_list->getRouteToActive();
-        branches_to_current_node = *a_route;
+        branches_to_current_node = music_tree_list->getRouteToActive();
         curTrackID = curMeta->ID();
     }
     else
     {
         // No current metadata, so when we come back we'll try and play the 
         // first thing on the active queue
-        branches_to_current_node.clear();
-        branches_to_current_node.append(0); //  Root node
-        branches_to_current_node.append(1); //  We're on a playlist (not "My Music")
-        branches_to_current_node.append(0); //  Active play Queue
+        setPathTo(branches_to_current_node, ACTIVE_PLAY_QUEUE);
         curTrackID = 0;
     }
 
@@ -785,38 +853,13 @@
 
 void PlaybackBoxMusic::playFirstTrack()
 {
-    QValueList <int> branches_to_current_node;
-
     stop();
     wipeTrackInfo();
-    branches_to_current_node.clear();
-    branches_to_current_node.append(0); //  Root node
-    branches_to_current_node.append(1); //  We're on a playlist (not "My Music")
-    branches_to_current_node.append(0); //  Active play Queue
-    music_tree_list->moveToNodesFirstChild(branches_to_current_node);
+    music_tree_list->moveToNodesFirstChild(getPathTo(ACTIVE_PLAY_QUEUE));
 }
 
-void PlaybackBoxMusic::updatePlaylistFromCD()
-{
-    if (!cd_reader_thread)
-    {
-        cd_reader_thread = new ReadCDThread(all_playlists, all_music);
-        cd_reader_thread->start();
-    }
-
-    if (!cd_watcher)
-    {
-        cd_watcher = new QTimer(this);
-        connect(cd_watcher, SIGNAL(timeout()), this, SLOT(occasionallyCheckCD()));
-        cd_watcher->start(1000);
-    }
-
-}
-
 void PlaybackBoxMusic::postUpdate()
 {
-   QValueList <int> branches_to_current_node;
-
    if (visual_mode_delay > 0)
        visual_mode_timer->start(visual_mode_delay * 1000);
 
@@ -825,49 +868,10 @@
    stop();
    wipeTrackInfo();
 
-   // move to first track in list
-   branches_to_current_node.clear();
-   branches_to_current_node.append(0); //  Root node
-   branches_to_current_node.append(1); //  We're on a playlist (not "My Music")
-   branches_to_current_node.append(0); //  Active play Queue
-   music_tree_list->moveToNodesFirstChild(branches_to_current_node);
+   music_tree_list->moveToNodesFirstChild(getPathTo(ACTIVE_PLAY_QUEUE));
    music_tree_list->refresh();
 }
 
-void PlaybackBoxMusic::occasionallyCheckCD()
-{
-    if (cd_reader_thread->getLock()->locked())
-        return;
-
-    if (!scan_for_cd) {
-        cd_watcher->stop();
-        delete cd_watcher;
-        cd_watcher = NULL;
-
-        cd_reader_thread->wait();
-        delete cd_reader_thread;
-        cd_reader_thread = NULL;
-    }
-
-    if (!scan_for_cd || cd_reader_thread->statusChanged())
-    {
-        all_playlists->clearCDList();
-        all_playlists->getActive()->ripOutAllCDTracksNow();
-
-        if(all_music->getCDTrackCount()) {
-            visual_mode_timer->stop();
-
-            all_playlists->getActive()->removeAllTracks();
-            all_playlists->getActive()->fillSongsFromCD();
-
-            postUpdate();
-        }
-    }
-
-    if (scan_for_cd && !cd_reader_thread->running())
-        cd_reader_thread->start();
-}
-
 void PlaybackBoxMusic::updatePlaylistFromSmartPlaylist()
 {
     doUpdatePlaylist("");
@@ -927,15 +931,8 @@
 
             if (tree_is_done)
             {
-                if (scan_for_cd)
-                    updatePlaylistFromCD();
-
                 music_tree_list->showWholeTree(show_whole_tree);
-                QValueList <int> branches_to_current_node;
-                branches_to_current_node.append(0); //  Root node
-                branches_to_current_node.append(1); //  We're on a playlist (not "My Music")
-                branches_to_current_node.append(0); //  Active play Queue
-                music_tree_list->moveToNodesFirstChild(branches_to_current_node);
+                music_tree_list->moveToNodesFirstChild(getPathTo(ACTIVE_PLAY_QUEUE));
                 music_tree_list->refresh();
                 if (show_whole_tree)
                     setContext(1);
@@ -947,6 +944,9 @@
                 if (curMeta) 
                     updateTrackInfo(curMeta);
 
+                if (store_manager)
+                    store_manager->reemitReadyStores();
+
                 return;     // Do not restart Timer
             }
             else
@@ -1576,15 +1576,21 @@
 
 void PlaybackBoxMusic::constructPlaylistTree()
 {
-    if (playlist_tree)
-        delete playlist_tree;
+    if (!playlist_tree) {
+        playlist_tree = new GenericTree(tr("playlist root"), 0);
+        playlist_tree->setAttribute(0, 0);
+        playlist_tree->setAttribute(1, 0);
+        playlist_tree->setAttribute(2, 0);
+        playlist_tree->setAttribute(3, 0);
+    } else {
+        if (playlist_tree->getChild("All My Playlists")) {
+            playlist_tree->removeNode(playlist_tree->getChild("All My Playlists"));
+        }
+        if (playlist_tree->getChild("All My Music")) {
+            playlist_tree->removeNode(playlist_tree->getChild("All My Music"));
+        }
+    }
 
-    playlist_tree = new GenericTree(tr("playlist root"), 0);
-    playlist_tree->setAttribute(0, 0);
-    playlist_tree->setAttribute(1, 0);
-    playlist_tree->setAttribute(2, 0);
-    playlist_tree->setAttribute(3, 0);
-
     // We ask the playlist object to write out the whole tree (all playlists 
     // and all music). It will set attributes for nodes in the tree, such as 
     // whether a node is selectable, how it can be ordered (normal, random, 
@@ -1592,6 +1598,7 @@
 
     all_playlists->writeTree(playlist_tree);
     music_tree_list->assignTreeData(playlist_tree);
+    playlist_tree->reorderSubnodes(1);
     tree_is_done = true;
 }
 
@@ -1603,19 +1610,13 @@
 
     if (curMeta)
     {
-        QValueList <int> *a_route;
-        a_route = music_tree_list->getRouteToActive();
-        branches_to_current_node = *a_route;
+        branches_to_current_node = music_tree_list->getRouteToActive();
     }
     else
     {
         // No current metadata, so when we come back we'll try and play the 
         // first thing on the active queue
-        
-        branches_to_current_node.clear();
-        branches_to_current_node.append(0); //  Root node
-        branches_to_current_node.append(1); //  We're on a playlist (not "My Music")
-        branches_to_current_node.append(0); //  Active play Queue
+        setPathTo(branches_to_current_node, ACTIVE_PLAY_QUEUE);
     }
 
     visual_mode_timer->stop();
@@ -1637,11 +1638,7 @@
     {
         stop();
         wipeTrackInfo();
-        branches_to_current_node.clear();
-        branches_to_current_node.append(0); //  Root node
-        branches_to_current_node.append(1); //  We're on a playlist (not "My Music")
-        branches_to_current_node.append(0); //  Active play Queue
-        music_tree_list->moveToNodesFirstChild(branches_to_current_node);
+        music_tree_list->moveToNodesFirstChild(getPathTo(ACTIVE_PLAY_QUEUE));
     }
     music_tree_list->refresh();
 }
@@ -2135,4 +2132,71 @@
     return (res >= 0);
 }
 
+void PlaybackBoxMusic::storeDetected(Store *store) 
+{
+    (void)store;
+}
 
+void PlaybackBoxMusic::storeRemoved(Store *store) 
+{
+    if (!stores.contains(store))
+        return;
+
+    DeviceInfo &devinfo = stores[store];
+
+    // If we're showing the store or playing something off it, stop/move away.
+    if (playlist_tree->isNodeInPath(devinfo.tree_node, music_tree_list->getRouteToActive()))
+        stop();
+    if (playlist_tree->isNodeInPath(devinfo.tree_node, music_tree_list->getRouteToCurrent()))
+        music_tree_list->moveToNode(getPathTo(ACTIVE_PLAY_QUEUE));
+
+    // Remove the tree info and the devinfo object.
+    playlist_tree->removeNode(devinfo.tree_node);    
+    delete devinfo.music_node;
+    stores.remove(store);
+    store->removeref();
+
+    // Refresh the tree, rerun constructPlayList to ensure that
+    // any playlist showing CD entries are redrawn.
+    all_music->clearCDData();
+    all_playlists->clearCDList();    
+    constructPlaylistTree();
+
+    music_tree_list->refresh();
+}
+
+void PlaybackBoxMusic::storeLoading(Store *store) 
+{
+    (void)store;
+}
+
+void PlaybackBoxMusic::storeReady(Store *store)
+{
+    store->addref();
+
+    VERBOSE(VB_GENERAL, QString("Store '%1' ready with %2 tracks").
+            arg(store->getTitle()).arg(store->getMetadataList().count()));
+
+    QPtrListIterator<Metadata> it(store->getMetadataList());
+    while (*it) {
+        all_music->addCDTrack(*it);
+        ++it;
+    }
+    all_music->setCDTitle(store->getTitle());
+    
+    MusicNode *node = new MusicNode(store->getName(), "title");
+    MusicTreeBuilder *builder = MusicTreeBuilder::createBuilder("title");
+    builder->makeTree(node, store->getMetadataList());
+
+    DeviceInfo devinfo;
+    devinfo.music_node = node;
+    devinfo.tree_node = node->writeTree(playlist_tree, playlist_tree->childCount());
+    stores[store] = devinfo;
+
+    if (gContext->GetNumSetting("AutoPlayCD", 0))
+        music_tree_list->moveToNode(getPathTo(devinfo.tree_node->getChildAt(0)));
+
+    playlist_tree->reorderSubnodes(1);
+    music_tree_list->refresh();
+}
+
Index: mythplugins/mythmusic/mythmusic/mythmusic.pro
===================================================================
--- mythplugins/mythmusic/mythmusic/mythmusic.pro	(revision 12209)
+++ mythplugins/mythmusic/mythmusic/mythmusic.pro	(working copy)
@@ -36,7 +36,7 @@
 HEADERS += goom/ifs.h goom/lines.h goom/mythgoom.h goom/drawmethods.h
 HEADERS += goom/mmx.h goom/mathtools.h goom/tentacle3d.h goom/v3d.h
 HEADERS += editmetadata.h smartplaylist.h search.h genres.h
-HEADERS += treebuilders.h
+HEADERS += treebuilders.h storemanager.h cdstore.h
 
 SOURCES += cddecoder.cpp cdrip.cpp decoder.cpp 
 SOURCES += flacdecoder.cpp flacencoder.cpp maddecoder.cpp main.cpp
@@ -51,4 +51,4 @@
 SOURCES += goom/ifs.c goom/ifs_display.c goom/lines.c goom/surf3d.c 
 SOURCES += goom/zoom_filter_mmx.c goom/zoom_filter_xmmx.c goom/mythgoom.cpp
 SOURCES += avfdecoder.cpp editmetadata.cpp smartplaylist.cpp search.cpp
-SOURCES += treebuilders.cpp
+SOURCES += treebuilders.cpp storemanager.cpp cdstore.cpp
Index: mythplugins/mythmusic/mythmusic/metadata.cpp
===================================================================
--- mythplugins/mythmusic/mythmusic/metadata.cpp	(revision 12209)
+++ mythplugins/mythmusic/mythmusic/metadata.cpp	(working copy)
@@ -950,7 +950,7 @@
         {
             if( (*anit).Track() == an_id * -1)
             {
-                a_label = QString("CD: %1 ~ %2 - %3").arg((*anit).FormatArtist()).arg((*anit).Track()).arg((*anit).FormatTitle());
+                a_label = QString("CD: %1 ~ %2 - %3").arg((*anit).Track()).arg((*anit).FormatArtist()).arg((*anit).FormatArtist());
                 *error_flag = false;
                 return a_label;
             }
@@ -1168,10 +1168,10 @@
     
 }
 
-void MusicNode::writeTree(GenericTree *tree_to_write_to, int a_counter)
+GenericTree* MusicNode::writeTree(GenericTree *tree_to_write_to, int a_counter)
 {
     
-    GenericTree *sub_node = tree_to_write_to->addNode(my_title);
+    GenericTree *sub_node = tree_to_write_to->addNode(my_title, a_counter);
     sub_node->setAttribute(0, 0);
     sub_node->setAttribute(1, a_counter);
     sub_node->setAttribute(2, rand());
@@ -1231,6 +1231,8 @@
         ++another_counter;
         ++iter;
     }
+
+    return sub_node;
 }
 
 
Index: mythplugins/mythmusic/mythmusic/cddecoder.h
===================================================================
--- mythplugins/mythmusic/mythmusic/cddecoder.h	(revision 12209)
+++ mythplugins/mythmusic/mythmusic/cddecoder.h	(working copy)
@@ -24,6 +24,7 @@
 
     int getNumTracks(void);
     int getNumCDAudioTracks(void);
+    void setDevice (const QString &dev);
 
     Metadata *getMetadata(int track);
     Metadata *getMetadata(void);
Index: mythplugins/mythmusic/mythmusic/playlist.cpp
===================================================================
--- mythplugins/mythmusic/mythmusic/playlist.cpp	(revision 12209)
+++ mythplugins/mythmusic/mythmusic/playlist.cpp	(working copy)
@@ -28,8 +28,7 @@
 {
     if (cd_flag)
     {
-        int val = (index_value < 0) ? -index_value : index_value;
-        label = all_available_music->getLabel(val, &bad_reference);
+        label = all_available_music->getLabel(index_value, &bad_reference);
         return;
     }
 
@@ -237,6 +236,13 @@
 
 void PlaylistsContainer::clearCDList()
 {
+    QPtrListIterator<Playlist> it(*all_other_playlists);
+    while (*it) {
+        (*it)->ripOutAllCDTracksNow();
+        ++it;
+    }
+    active_playlist->ripOutAllCDTracksNow();
+    backup_playlist->ripOutAllCDTracksNow();
     cd_playlist.clear();
 }
 
Index: mythplugins/mythmusic/mythmusic/databasebox.h
===================================================================
--- mythplugins/mythmusic/mythmusic/databasebox.h	(revision 12209)
+++ mythplugins/mythmusic/mythmusic/databasebox.h	(working copy)
@@ -4,38 +4,25 @@
 #include <qwidget.h>
 #include <qdialog.h>
 #include <qstringlist.h>
-#include <qthread.h>
 #include <qtimer.h>
 #include <qptrlist.h>
 
 #include "metadata.h"
 #include "playlist.h"
+#include "storemanager.h"
 #include <mythtv/mythwidgets.h>
 #include <mythtv/lcddevice.h>
 #include <mythtv/uilistbtntype.h>
+#include <mythtv/mythobservable.h>
+#include <mythtv/mythmedia.h>
 
 class TreeCheckItem;
+class CDManager;
 
-class ReadCDThread : public QThread
-{
-  public:
-
-    ReadCDThread(PlaylistsContainer *all_the_playlist, AllMusic *all_the_music);
-    virtual void run();
-    bool    statusChanged(){return cd_status_changed;}
-    QMutex  *getLock(){return &music_lock;}
-
-  private:
-
-    AllMusic*           all_music;
-    PlaylistsContainer* the_playlists;
-    bool                cd_status_changed;
-    QMutex              music_lock;
-};
-
 class DatabaseBox : public MythThemedDialog
 {
     Q_OBJECT
+    typedef QValueList<MythMediaDevice*> MythMediaPtrList;
   public:
     DatabaseBox(PlaylistsContainer *all_playlists,
                 AllMusic *music_ptr, MythMainWindow *parent, 
@@ -46,6 +33,7 @@
     void dealWithTracks(PlaylistItem *item_ptr);
     void setCDTitle(const QString& title);
     void fillCD(void);
+    bool onMediaEvent(MythMediaDevice *dev);
     
   protected slots:
     void selected(UIListGenericTree *);
@@ -74,12 +62,17 @@
     void CreateCDMP3();
     void BlankCDRW();
 
+    void storeDetected(Store*);
+    void storeRemoved(Store*);
+    void storeLoading(Store*);
+    void storeReady(Store*);
+
   private:
     void doSelected(UIListGenericTree *, bool cd_flag);
     void doPlaylistPopup(TreeCheckItem *item_ptr);
     void doActivePopup(PlaylistTitle *item_ptr);
     void checkParent(UIListGenericTree *);
- 
+
     void checkTree(UIListGenericTree *startingpoint = NULL);
     QPixmap getPixmap(QString &level);
 
@@ -105,16 +98,13 @@
 
     MythPopupBox        *error_popup;
 
-    ReadCDThread        *cd_reader_thread;
-    QTimer              *cd_watcher;
-    bool                cd_checking_flag;
+    StoreManager        *store_manager;
 
     QTimer              *fill_list_timer;
     int                 wait_counter;
     int                 numb_wait_dots;
 
     QStringList         treelevels;
-
     QPtrList<UITextType> m_lines;
 };
 
Index: mythplugins/mythmusic/mythmusic/metadata.h
===================================================================
--- mythplugins/mythmusic/mythmusic/metadata.h	(revision 12209)
+++ mythplugins/mythmusic/mythmusic/metadata.h	(working copy)
@@ -219,7 +219,20 @@
     QString     getTitle(){return my_title;}
     void        printYourself(int indent_amount);   // debugging
     void        putYourselfOnTheListView(TreeCheckItem *parent, bool show_node);
-    void        writeTree(GenericTree *tree_to_write_to, int a_counter);
+    /** \brief Write the node, it's leafs and children into a GenericTree.
+
+        Creates a new node in tree_to_write_to with the music nodes
+        title, then recursively adds all leaves and subnodes. Subnodes
+        are added with recursive calls to writeTree, with a a_counter
+        value of zero and incremented by one for each iteration.
+
+        The call returns a pointer to the node added to tree_to_write_to.
+
+        \param tree_to_write_to the GenericTree to write to
+        \param a_counter value to set the new nodes 2nd attribute
+        \returns the node created
+     */
+    GenericTree*writeTree(GenericTree *tree_to_write_to, int a_counter);
     void        sort();
     void        setPlayCountMin(int tmp_min) { playcountMin = tmp_min; }
     void        setPlayCountMax(int tmp_max) { playcountMax = tmp_max; }
Index: mythplugins/mythmusic/mythmusic/databasebox.cpp
===================================================================
--- mythplugins/mythmusic/mythmusic/databasebox.cpp	(revision 12209)
+++ mythplugins/mythmusic/mythmusic/databasebox.cpp	(working copy)
@@ -13,14 +13,16 @@
 #include "metadata.h"
 #include "databasebox.h"
 #include "treecheckitem.h"
-#include "cddecoder.h"
 #include "playlist.h"
+#include "storemanager.h"
 
 #include <mythtv/dialogbox.h>
 #include <mythtv/mythcontext.h>
 #include <mythtv/lcddevice.h>
 #include <mythtv/uitypes.h>
 #include <mythtv/uilistbtntype.h>
+#include <mythtv/mythmedia.h>
+#include <mythtv/mythmediamonitor.h>
 
 DatabaseBox::DatabaseBox(PlaylistsContainer *all_playlists,
                          AllMusic *music_ptr, MythMainWindow *parent, 
@@ -38,10 +40,7 @@
     }
     all_music = music_ptr;
 
-    //  Do we check the CD?
-    cd_checking_flag = false;
-    cd_checking_flag = gContext->GetNumSetting("AutoLookupCD");
-
+    bool check_cd = gContext->GetNumSetting("AutoLookupCD");
     QString treelev = gContext->GetSetting("TreeLevels", "artist album title");
     QStringList treelevels = QStringList::split(" ", treelev.lower());
 
@@ -98,31 +97,29 @@
     rootNode = new UIListGenericTree(NULL, "Root Music Node");
 
     allmusic = new TreeCheckItem(rootNode, tr("All My Music"), "genre", 0);
-    if (cd_checking_flag)
-        cditem = new CDCheckItem(rootNode, tr("Blechy Blech Blah"), "cd", 0);
+    if (check_cd)
+        cditem = new CDCheckItem(rootNode, all_music->getCDTitle (), "cd", 0);
     alllists = new TreeCheckItem(rootNode, tr("All My Playlists"), "genre", 0);
     allcurrent = new PlaylistTitle(rootNode, tr("Active Play Queue"));
 
     tree->SetTree(rootNode);
 
-    cd_reader_thread = NULL;
-    if (cd_checking_flag)
-    {
-        // Start the CD checking thread, and set up a timer to make it check 
-        // occasionally
+    store_manager = NULL;
+    if (check_cd) {
+        store_manager = new StoreManager;
 
-        cd_reader_thread = new ReadCDThread(the_playlists, all_music);
+        connect(store_manager, SIGNAL(storeDetected(Store*)),
+                this, SLOT(storeDetected(Store*)));
+        connect(store_manager, SIGNAL(storeRemoved(Store*)),
+                this, SLOT(storeRemoved(Store*)));
+        connect(store_manager, SIGNAL(storeLoading(Store*)),
+                this, SLOT(storeLoading(Store*)));
+        connect(store_manager, SIGNAL(storeReady(Store*)),
+                this, SLOT(storeReady(Store*)));
 
-        // filling initialy before thread running
-        fillCD();
-
-        cd_reader_thread->start();
-    
-        cd_watcher = new QTimer(this);
-        connect(cd_watcher, SIGNAL(timeout()), this, SLOT(occasionallyCheckCD()));
-        cd_watcher->start(1000); // Every second?
+        store_manager->reemitReadyStores();
     }
-    
+
     // Set a timer to keep redoing the fillList stuff until the metadata and 
     // playlist loading threads are done
 
@@ -135,12 +132,17 @@
 
 DatabaseBox::~DatabaseBox()
 {
-    if (cd_reader_thread)
+    if (store_manager)
     {
-        cd_watcher->stop();
-
-        cd_reader_thread->wait();
-        delete cd_reader_thread;
+        disconnect(store_manager, SIGNAL(storeDetected(Store*)),
+                   this, SLOT(storeDetected(Store*)));
+        disconnect(store_manager, SIGNAL(storeRemoved(Store*)),
+                   this, SLOT(storeRemoved(Store*)));
+        disconnect(store_manager, SIGNAL(storeLoading(Store*)),
+                   this, SLOT(storeLoading(Store*)));
+        disconnect(store_manager, SIGNAL(storeReady(Store*)),
+                   this, SLOT(storeReady(Store*)));
+        store_manager->deleteLater();
     }
 
     all_music->resetListings();
@@ -211,23 +213,6 @@
         showWaiting(); 
 }
 
-void DatabaseBox::occasionallyCheckCD()
-{
-    if (cd_reader_thread->getLock()->locked())
-        return;
-
-    if (cd_reader_thread->statusChanged())
-    {
-        if (active_playlist)
-        {
-            active_playlist->ripOutAllCDTracksNow();
-            fillCD();
-        }
-    }
-    if (!cd_reader_thread->running())
-        cd_reader_thread->start();
-}
-
 void DatabaseBox::copyNewPlaylist()
 {
     if (!active_popup)
@@ -429,6 +414,38 @@
     delete record_progress;
 }
 
+void DatabaseBox::storeDetected(Store *store) 
+{
+    store->addref();
+}
+
+void DatabaseBox::storeRemoved(Store *store) 
+{
+    all_music->clearCDData();
+    the_playlists->clearCDList();
+    fillCD();
+    store->removeref();
+}
+
+void DatabaseBox::storeLoading(Store *store) 
+{
+    (void)store;
+}
+
+void DatabaseBox::storeReady(Store *store)
+{
+    all_music->setCDTitle(store->getTitle());
+
+    Metadata *track;
+    QPtrListIterator<Metadata> it(store->getMetadataList());
+    while ((track = it.current()) != 0) {
+        all_music->addCDTrack(track);
+        ++it;
+    }
+
+    fillCD();
+}
+
 void DatabaseBox::deletePlaylist()
 {
     if (!playlist_popup)
@@ -492,8 +509,6 @@
 
 void DatabaseBox::fillCD(void)
 {
-    QMutexLocker locker(cd_reader_thread->getLock());
-
     if (cditem)
     {
 
@@ -554,9 +569,17 @@
         }
 
         tree->Redraw();
+        tree->enter ();
     }
 }
 
+bool DatabaseBox::onMediaEvent(MythMediaDevice *device)
+{
+    if (store_manager)
+        return store_manager->onMediaEvent(device);
+    return false;
+}
+
 void DatabaseBox::doMenus(UIListGenericTree *item)
 {
     if (dynamic_cast<CDCheckItem*>(item))
@@ -765,6 +788,7 @@
         cerr << "databasebox.o: That's odd ... there's something I don't "
                 "recognize on a ListView" << endl;
     }
+    tree->MoveDown();
 }
 
 
@@ -1223,103 +1247,4 @@
         cditem->setText(title);
 }
 
-ReadCDThread::ReadCDThread(PlaylistsContainer *all_the_playlists, 
-                           AllMusic *all_the_music)
-{
-    the_playlists = all_the_playlists;
-    all_music = all_the_music;
-    cd_status_changed = false;
-}
 
-void ReadCDThread::run()
-{
-    // lock all_music and cd_status_changed while running thread
-    QMutexLocker locker(getLock());
-
-    CdDecoder *decoder = new CdDecoder("cda", NULL, NULL, NULL);
-    int tracknum = decoder->getNumCDAudioTracks();
-
-    bool redo = false;
-
-    if (tracknum != all_music->getCDTrackCount())
-    {
-        cd_status_changed = true;
-        VERBOSE(VB_IMPORTANT, QString("Set cd_status_changed to true"));
-    }
-    else
-        cd_status_changed = false;
-
-    if (tracknum == 0)
-    {
-        //  No CD, or no recognizable CD
-        all_music->clearCDData();
-        the_playlists->clearCDList();
-    }
-    else if (tracknum > 0)
-    {
-        // Check the last track to see if it's differen than whatever it was 
-        // before
-        Metadata *checker = decoder->getLastMetadata();
-        if (checker)
-        {
-            if (!all_music->checkCDTrack(checker))
-            {
-                redo = true;
-                cd_status_changed = true;
-                all_music->clearCDData();
-                the_playlists->clearCDList();
-            }
-            else
-                cd_status_changed = false;
-            delete checker;
-        }
-        else
-        {
-            cerr << "databasebox.o: The cddecoder said it had audio tracks, "
-                    "but it won't tell me about them" << endl;
-        }
-    } 
-
-    int tracks = decoder->getNumTracks();
-    bool setTitle = false;
-
-    for (int actual_tracknum = 1; 
-         redo && actual_tracknum <= tracks; actual_tracknum++)
-    {
-        Metadata *track = decoder->getMetadata(actual_tracknum);
-        if (track)
-        {
-            all_music->addCDTrack(track);
-
-            if (!setTitle)
-            {
-            
-                QString parenttitle = " ";
-                if (track->FormatArtist().length() > 0)
-                {
-                    parenttitle += track->FormatArtist();
-                    parenttitle += " ~ "; 
-                }
-
-                if (track->Album().length() > 0)
-                    parenttitle += track->Album();
-                else
-                {
-                    parenttitle = " " + QObject::tr("Unknown");
-                    cerr << "databasebox.o: Couldn't find your CD. It may not "
-                            "be in the freedb database." << endl;
-                    cerr << "               More likely, however, is that you "
-                            "need to delete ~/.cddb and" << endl;
-                    cerr << "               ~/.cdserverrc and restart "
-                            "mythmusic. Have a nice day." << endl;
-                }
-                all_music->setCDTitle(parenttitle);
-                setTitle = true;
-            }
-            delete track;
-        }
-    }
-
-    delete decoder;
-}
-
Index: mythplugins/mythmusic/mythmusic/playbackbox.h
===================================================================
--- mythplugins/mythmusic/mythmusic/playbackbox.h	(revision 12209)
+++ mythplugins/mythmusic/mythmusic/playbackbox.h	(working copy)
@@ -14,13 +14,14 @@
 #include "playlist.h"
 #include "editmetadata.h"
 #include "databasebox.h"
+#include "storemanager.h"
 
 class Output;
 class Decoder;
 
 class PlaybackBoxMusic : public MythThemedDialog
 {
-    Q_OBJECT
+    Q_OBJECT;
 
   public:
 
@@ -77,8 +78,6 @@
     void toggleFullBlankVisualizer();
     void end();
 
-    void occasionallyCheckCD();
-
     // popup menu
     void showMenu();
     void closePlaylistPopup();
@@ -93,6 +92,11 @@
     bool getInsertPLOptions(InsertPLOption &insertOption,
                             PlayPLOption &playOption, bool &bRemoveDups);
 
+    void storeDetected(Store*);
+    void storeRemoved(Store*);
+    void storeLoading(Store*);
+    void storeReady(Store*);
+
   signals:
 
     void dummy();   // debugging
@@ -104,7 +108,6 @@
     void updatePlaylistFromSmartPlaylist();
     void doUpdatePlaylist(QString whereClause);
     void CycleVisualizer(void);
-    void updatePlaylistFromCD(void);
     void setTrackOnLCD(Metadata *mdata);
     void updateTrackInfo(Metadata *mdata);
     void openOutputDevice(void);
@@ -151,10 +154,18 @@
 
     bool menufilters;
 
-    ReadCDThread *cd_reader_thread;
-    QTimer *cd_watcher;
-    bool cd_checking_flag;
-    bool scan_for_cd;
+    StoreManager *store_manager;
+    struct DeviceInfo 
+    {
+        MusicNode *music_node;
+        GenericTree *tree_node;
+    };
+    typedef QMap<Store*, DeviceInfo> StoreToInfo;
+    /** Mapping from a MythMediaDevice to the MusicNode that contains
+     * the contents, the GenericTree node that was inserted into \p
+     * playlist_tree and the metadata pointers.
+     */
+    StoreToInfo stores;
 
     MainVisual *mainvisual;
 
@@ -164,7 +175,6 @@
     QTimer *lcd_update_timer;
     QTimer *banner_timer;
     int visualizer_status;
-
     bool showrating;
     bool vis_is_big;
     bool tree_is_done;
Index: mythplugins/mythmusic/mythmusic/cddecoder.cpp
===================================================================
--- mythplugins/mythmusic/mythmusic/cddecoder.cpp	(revision 12209)
+++ mythplugins/mythmusic/mythmusic/cddecoder.cpp	(working copy)
@@ -310,6 +310,11 @@
     return retval;
 }
 
+void CdDecoder::setDevice(const QString &dev) 
+{ 
+    devicename = dev; 
+}
+
 Metadata* CdDecoder::getMetadata(int track)
 {
     settracknum = track;
