Index: src/collectiondb.cpp
===================================================================
--- src/collectiondb.cpp	(revision 524114)
+++ src/collectiondb.cpp	(working copy)
@@ -2227,6 +2227,7 @@
                             b.setLength( smb.length.toInt() );
                             b.setYear( smb.year.toInt() );
                             b.setTrack( smb.tracknr.toInt() );
+			    b.setClipList( smb.clipList );
                         }
 
                         // check if it's a podcast
Index: src/metabundle.h
===================================================================
--- src/metabundle.h	(revision 524114)
+++ src/metabundle.h	(working copy)
@@ -15,6 +15,7 @@
 #include <taglib/audioproperties.h>
 #include "atomicstring.h"
 #include "atomicurl.h"
+#include "engine/enginebase.h" //ClipList
 
 #include "amarok_export.h"
 
@@ -198,6 +199,8 @@
     int     playCount()  const;
     uint    lastPlay()   const;
     int     filesize()   const;
+    
+    ClipList clipList() const;
 
     QString streamName() const;
     QString streamUrl()  const;
@@ -235,6 +238,8 @@
     virtual void setFilesize( int bytes );
     virtual void setFileType( int type );
     virtual void updateFilesize();
+    
+    virtual void setClipList( const ClipList &chapters );
 
 public: //static helper functions
     static QString prettyBitrate( int );
@@ -273,6 +278,8 @@
     int  m_playCount;
     uint m_lastPlay;
     int  m_filesize;
+    
+    ClipList m_clipList;
 
     int m_type;
 
@@ -322,6 +329,8 @@
 inline int MetaBundle::filesize()   const { return m_filesize == Undetermined ? 0 : m_filesize; }
 inline int MetaBundle::fileType()   const { return m_type; }
 
+inline ClipList MetaBundle::clipList() const { return m_clipList; }
+
 inline KURL     MetaBundle::url()        const { return m_url; }
 inline QString  MetaBundle::filename()   const { return m_url.fileName(); }
 inline QString  MetaBundle::directory()  const
@@ -386,6 +395,8 @@
 inline void MetaBundle::setFilesize( int bytes ) { m_filesize = bytes; }
 inline void MetaBundle::setFileType( int type ) { m_type = type; }
 
+inline void MetaBundle::setClipList( const ClipList &chapters ) { m_clipList = chapters; }
+
 inline bool MetaBundle::hasExtendedMetaInformation() const
 {
     return ( m_type == mp3 || m_type == ogg ||
Index: src/playlist.cpp
===================================================================
--- src/playlist.cpp	(revision 524114)
+++ src/playlist.cpp	(working copy)
@@ -111,6 +111,18 @@
 
     inline PlaylistItem *operator*() { return (PlaylistItem*)QListViewItemIterator::operator*(); }
 
+    //skip clip items
+    inline MyIterator & operator++()
+    {
+        QListViewItem *curr;
+        do {
+            QListViewItemIterator::operator++();
+            curr = QListViewItemIterator::operator*();
+        }
+        while ( curr && curr->parent() );
+        return *this;
+    }
+
     /// @return the next visible PlaylistItem after item
     static PlaylistItem *nextVisible( PlaylistItem *item )
     {
@@ -1569,7 +1581,7 @@
 }
 
 void
-Playlist::activate( QListViewItem *item )
+Playlist::activate( QListViewItem *it )
 {
     ///item will be played if possible, the playback may be delayed
     ///so we start the glow anyway and hope
@@ -1577,7 +1589,7 @@
     //All internal requests for playback should come via
     //this function please!
 
-    if( !item )
+    if( !it )
     {
         //we have reached the end of the playlist
         EngineController::instance()->stop();
@@ -1587,7 +1599,11 @@
         return;
     }
 
-    #define item static_cast<PlaylistItem*>(item)
+    PlaylistItem *item;
+    if ( it->parent() )
+        item = static_cast<PlaylistItem*>(it->parent() );
+    else
+        item = static_cast<PlaylistItem*>(it);
 
     if( isDynamic() && !m_partyDirt && item != firstChild() && !amaroK::repeatTrack() )
     {
@@ -1649,15 +1665,55 @@
 
     //use PlaylistItem::MetaBundle as it also updates the audioProps
     EngineController::instance()->play( *item );
-    #undef item
+
+    if ( it->parent() ) {
+        ClipItem *clipItem = static_cast<ClipItem*>( it );
+        debug() << "seeking to " << MetaBundle::prettyLength(clipItem->startTime()) << endl;
+        EngineController::instance()->seek( clipItem->startTime() );
+    }
 }
 
 QPair<QString, QRect> Playlist::toolTipText( QWidget*, const QPoint &pos ) const
 {
-    PlaylistItem *item = static_cast<PlaylistItem*>( itemAt( pos ) );
-    if( !item )
+    QListViewItem *it = itemAt( pos );
+    if( !it )
         return QPair<QString, QRect>( QString::null, QRect() );
+    if ( it->parent() ) //clip item
+    {
+        ClipItem *item = static_cast<ClipItem*>( it );
 
+        //TODO(Anx): i18n
+        QString tip = "<table cellpadding=\"2\"><tr><td colspan=\"2\">"
+            "<center><b><font size=\"+1\">Clip Info</font></b></center></td></tr>";
+        tip.append(QString("<tr><td><b>Description:</b></td><td>%1</td></tr>")
+            .arg(item->info().description));
+        tip.append(QString("<tr><td><b>ID:</b></td><td>%1</td></tr>")
+            .arg(item->info().id));
+        tip.append(QString("<tr><td><b>Time:</b></td><td>%1 - %2</td></tr>")
+            .arg(MetaBundle::prettyLength(item->info().startTime * 1e-9))
+            .arg(MetaBundle::prettyLength(item->info().endTime   * 1e-9)));
+
+        if ( item->info().anchorURI.length() > 0 )
+        {
+            tip.append(QString("<tr><td><b>Link:</b></td><td><a href=\"%1\">%2</a></td></tr>")
+                .arg(item->info().anchorURI)
+                .arg(item->info().anchorText));
+        }
+        if ( item->info().imageURI.length() > 0 ) {
+            tip.append(QString("<tr><td colspan=\"2\"><img src=\"%1\" alt=\"%2\" /></td></tr>")
+                .arg(item->info().imageURI)
+                .arg(item->info().imageAlt));
+        }
+
+        tip.append("</table>");
+
+        QSimpleRichText text( tip, font() );
+        text.setWidth( 500 ); //is this reasonable?
+        return QPair<QString, QRect>( tip, QRect(mapToGlobal(pos), QSize( text.widthUsed() - 2, text.height() ) ) );
+    }
+
+    PlaylistItem *item = static_cast<PlaylistItem*>( it );
+
     const QPoint contentsPos = viewportToContents( pos );
     const int col = header()->sectionAt( contentsPos.x() );
 
@@ -3530,6 +3586,9 @@
         return;
     }
 
+    if ( item->parent() ) //no context menu for a clip item
+        return;
+
     #define item static_cast<PlaylistItem*>(item)
 
     enum {
Index: src/engine/enginebase.h
===================================================================
--- src/engine/enginebase.h	(revision 524114)
+++ src/engine/enginebase.h	(working copy)
@@ -14,7 +14,6 @@
 
 #include <sys/types.h>
 
-
 /**
  * @class Engine::Base
  * @author Mark Kretshmann
@@ -244,7 +243,23 @@
         bool  m_isStream;
     };
 
-
+    //FIXME(Anx): Where should this be defined???
+    class ClipInfo {
+    public:
+        bool empty;
+	QString id;
+	QString description;
+        QString track;
+        QString anchorURI;
+        QString anchorText;
+        QString imageURI;
+        QString imageAlt;
+	unsigned long long startTime;
+	unsigned long long endTime;
+    };
+   
+    typedef QValueList<ClipInfo> ClipList;
+    
     class SimpleMetaBundle {
     public:
         QString title;
@@ -257,6 +272,8 @@
         QString length;
         QString year;
         QString tracknr;
+	
+	ClipList clipList;
     };
 
 
@@ -273,4 +290,6 @@
     };
 }
 
+#define ClipList QValueList<Engine::ClipInfo>
+
 #endif
Index: src/engine/gst10/gstengine.cpp
===================================================================
--- src/engine/gst10/gstengine.cpp	(revision 524114)
+++ src/engine/gst10/gstengine.cpp	(working copy)
@@ -120,8 +120,88 @@
       return GST_BUS_DROP;
 }
 
+GstBusSyncReply
+GstEngine::bus_cmml_cb(GstBus*, GstMessage* msg, gpointer) // static
+{
+    DEBUG_FUNC_INFO
 
+    switch ( GST_MESSAGE_TYPE(msg))
+    {
+            case GST_MESSAGE_ERROR:
+            {
+                GError* error;
+                gchar* debug_msg;
+                gst_message_parse_error(msg,&error,&debug_msg);
+                debug() << "whoops, error: " << error->message << endl;
+                instance()->destroyCMMLPipeline();
+                break;
+            }
+            case GST_MESSAGE_EOS:
+            {
+                QTimer::singleShot( 0, instance(), SLOT( endOfCMMLStreamReached() ) );
+                QTimer::singleShot( 0, instance(), SLOT( newCMMLMetaData() ) );
+                break;
+            }
+            case GST_MESSAGE_TAG:
+            {
+                GstTagList* taglist;
+                gst_message_parse_tag(msg,&taglist);
+                gst_tag_list_foreach( taglist, list_foreach_cb, &instance()->m_cmmlBundle );
+                gst_tag_list_free(taglist);
+                break;
+            }
+            default: ;
+            }
+            
+            gst_message_unref( msg );
+            return GST_BUS_DROP;
+}
+
 void
+GstEngine::list_foreach_cb(const GstTagList *taglist, const gchar *tag, gpointer user_data)
+{
+    Engine::SimpleMetaBundle *bundle = (Engine::SimpleMetaBundle*) user_data;
+
+    const GValue *clip = gst_tag_list_get_value_index(taglist,tag,0);
+
+    if ( strcmp(tag,"cmml-clip") == 0  ) {
+        gpointer data = g_value_get_object( clip );
+        
+        Engine::ClipInfo info;
+    
+        gchar *id, *description, *track, *anchor_uri, *anchor_text, *image_alt, *image_uri;
+        guint64 start_time, end_time;
+        bool empty;
+        g_object_get( G_OBJECT(data), "id", &id, "description", &description,
+           "anchor-uri", &anchor_uri, "anchor-text", &anchor_text,
+           "start-time", &start_time, "end-time", &end_time,
+           "img-uri", &image_uri,/* "img-alt", &image_alt,*/
+           "empty", &empty, "track", &track, NULL );
+
+        info.id = QString( id );
+        info.description = QString( description );
+        info.anchorURI = QString( anchor_uri );
+        info.anchorText = QString( anchor_text );
+        info.track = QString( track );
+        //info.imageAlt = QString( image_alt );
+        info.imageURI = QString( image_uri );
+        info.startTime = start_time;
+        info.endTime = end_time;
+        info.empty = empty;
+
+        g_free( id );
+        g_free( description );
+        g_free( track );
+        g_free( anchor_uri );
+        g_free( anchor_text );
+        g_free( image_uri );
+        //g_free( image_alt );
+    
+        bundle->clipList.append( info );
+    }
+}
+
+void
 GstEngine::newPad_cb( GstElement*, GstPad* pad, gboolean, gpointer ) //static
 {
     DEBUG_BLOCK
@@ -191,6 +271,7 @@
 */      , m_delayq(0)
         , m_current(0)
         , m_pipelineFilled( false )
+        , m_cmmlPipelineFilled( false )
         , m_fadeValue( 0.0 )
         , m_equalizerEnabled( false )
         , m_shutdown( false )
@@ -481,6 +562,9 @@
     Engine::Base::load( url, stream );
     debug() << "Loading url: " << url.url() << endl;
 
+    if ( !createCMMLPipeline( QFile::encodeName( url.path() ) ) )
+	    debug() << "Failed to create CMML pipeline" << endl;
+
     if ( !createPipeline() )
         return false;
 
@@ -727,6 +811,14 @@
     emit trackEnded();
 }
 
+void
+GstEngine::endOfCMMLStreamReached()  //SLOT
+{
+    DEBUG_BLOCK
+
+    destroyCMMLPipeline();
+}
+
 /*
 void
 GstEngine::newKioData( KIO::Job*, const QByteArray& array )  //SLOT
@@ -758,6 +850,12 @@
     emit metaData( m_metaBundle );
 }
 
+void
+GstEngine::newCMMLMetaData()  //SLOT
+{
+    emit metaData( m_cmmlBundle );
+}
+
 /*
 void
 GstEngine::kioFinished()  //SLOT
@@ -896,7 +994,6 @@
     return true;
 }
 
-
 void
 GstEngine::destroyPipeline()
 {
@@ -932,6 +1029,63 @@
 }
 */
 
+bool
+GstEngine::createCMMLPipeline( const QCString& filename )
+{
+    DEBUG_BLOCK
+
+    destroyCMMLPipeline();
+
+    //FIXME(Anx): How else can I determine if an ogg stream has cmml data?
+    //if ( filename.right(4).lower() != ".anx" )
+    //return false;
+
+    Engine::SimpleMetaBundle bundle;
+    m_cmmlBundle = bundle;
+    
+    GError *error = 0;
+    m_gst_cmml_pipeline = gst_parse_launch("filesrc name=source ! oggdemux name=demuxer ! cmmldec name=cmml ! fakesink \n", &error );
+    if ( error )
+    {
+        emit statusText( error->message );
+        return false;
+    }
+    g_object_set( G_OBJECT( gst_bin_get_by_name( GST_BIN(m_gst_cmml_pipeline), "source" ) ), "location", (const char*)filename, NULL );
+    g_object_set( G_OBJECT( gst_bin_get_by_name( GST_BIN(m_gst_cmml_pipeline), "cmml" ) ), "wait-clip-end-time", true, NULL );
+
+    gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(m_gst_cmml_pipeline)), bus_cmml_cb, NULL);
+    
+    gst_element_set_state( m_gst_cmml_pipeline, GST_STATE_PLAYING );
+
+    m_cmmlPipelineFilled = true;
+    return true;
+}
+
+void
+GstEngine::destroyCMMLPipeline()
+{
+    DEBUG_BLOCK
+
+/*    // Clear the scope adapter
+    m_mutexScope.lock();
+    gst_adapter_clear( m_gst_adapter );
+    m_mutexScope.unlock();
+*/
+    if ( m_cmmlPipelineFilled ) {
+        debug() << "Unreffing pipeline." << endl;
+        gst_element_set_state( m_gst_cmml_pipeline, GST_STATE_NULL );
+        gst_object_unref( GST_OBJECT( m_gst_cmml_pipeline ) );
+
+        m_cmmlPipelineFilled = false;
+    }
+
+    // Destroy KIO transmission job
+/*    if ( m_transferJob ) {
+        m_transferJob->kill();
+        m_transferJob = 0;
+    } */
+}
+
 gint64 GstEngine::pruneScope()
 {
     if ( !m_pipelineFilled ) return 0; // dont prune if we aren't playing
Index: src/engine/gst10/gstengine.h
===================================================================
--- src/engine/gst10/gstengine.h	(revision 524114)
+++ src/engine/gst10/gstengine.h	(working copy)
@@ -89,6 +89,7 @@
     private slots:
         void handlePipelineError();
         void endOfStreamReached();
+	void endOfCMMLStreamReached();
 //        void kioFinished();
 
         /** Copies incoming data from KIO into StreamSrc's buffer */
@@ -99,6 +100,7 @@
 
         /** Transmits new decoded metadata to the application */
         void newMetaData();
+        void newCMMLMetaData();
 
     private:
         static GstEngine* instance() { return s_instance; }
@@ -121,6 +123,11 @@
         // CALLBACKS:
         /** Bus message */
         static GstBusSyncReply bus_cb( GstBus*, GstMessage*, gpointer );
+	
+	static GstBusSyncReply bus_cmml_cb( GstBus*, GstMessage* msg, gpointer );
+	static void list_foreach_cb(const GstTagList *taglist, const gchar *tag, gpointer );
+	
+
         /** Called when decodebin has generated a new pad */
         static void newPad_cb( GstElement*, GstPad*, gboolean, gpointer );
         /** Used by canDecode(). When called, the format probably can be decoded */
@@ -140,7 +147,13 @@
 
         /** Stops playback, destroys all input pipelines, destroys output pipeline, and frees ressources */
         void destroyPipeline();
+	
+        /** Construct the pipeline to gather CMML data */
+        bool createCMMLPipeline( const QCString& filename );
 
+        /** Frees resources from CMML gathering pipeline */
+        void destroyCMMLPipeline();
+
         /** Beams the streaming buffer status to amaroK */
 //        void sendBufferStatus();
 
@@ -162,6 +175,7 @@
         static GstEngine* s_instance;
 
         GstElement* m_gst_pipeline;
+	GstElement* m_gst_cmml_pipeline;
 
         GstElement* m_gst_src;
         GstElement* m_gst_decodebin;
@@ -203,6 +217,7 @@
         KIO::TransferJob* m_transferJob;
         QMutex            m_mutexScope; */
         bool              m_pipelineFilled;
+	bool              m_cmmlPipelineFilled;
         float             m_fadeValue;
 
         bool              m_equalizerEnabled;
@@ -210,6 +225,7 @@
         QValueList<int>   m_equalizerGains;
 
         Engine::SimpleMetaBundle m_metaBundle;
+        Engine::SimpleMetaBundle m_cmmlBundle;
 
         bool m_shutdown;
         mutable bool m_canDecodeSuccess;
Index: src/playlistitem.cpp
===================================================================
--- src/playlistitem.cpp	(revision 524114)
+++ src/playlistitem.cpp	(working copy)
@@ -1055,4 +1055,33 @@
     }
 }
 
+void PlaylistItem::setClipList( const ClipList &clips )
+{
+    super::setClipList( clips );
 
+    QListViewItem * clip = firstChild();
+    QListViewItem * next_clip;
+    while( clip ) {
+        next_clip = clip->nextSibling();
+        delete clip;
+        clip = next_clip;
+    }
+
+    QListViewItem *clip_item = 0;
+    foreachType( ClipList, clips )
+    {
+        clip_item = new ClipItem( this, clip_item, *it );
+        clip_item->setText( Title, "   " + (*it).id +
+            " ( " + MetaBundle::prettyLength((*it).startTime * 1e-9) + " )" );
+        clip_item->setText( Length,
+            MetaBundle::prettyLength(((*it).endTime-(*it).startTime)*1e-9) );
+    }
+    setOpen(true);
+    update(); //???
+}
+
+ClipItem::ClipItem( QListViewItem* it, QListViewItem* after, const Engine::ClipInfo &info ) : QListViewItem( it, after ),
+  m_startTime(info.startTime*1e-6), m_info(info)
+{
+    
+}
Index: src/metabundle.cpp
===================================================================
--- src/metabundle.cpp	(revision 524114)
+++ src/metabundle.cpp	(working copy)
@@ -292,7 +292,8 @@
            discNumber() == bundle.discNumber() &&
            length()     == bundle.length() &&
            bitrate()    == bundle.bitrate() &&
-           sampleRate() == bundle.sampleRate();
+           sampleRate() == bundle.sampleRate() &&
+           clipList().count() == bundle.clipList().count();
     // FIXME: check for size equality?
 }
 
@@ -522,6 +523,7 @@
     setLastPlay( bundle.lastPlay() );
     setFileType( bundle.fileType() );
     setFilesize( bundle.filesize() );
+    setClipList( bundle.clipList() );
     if( bundle.m_podcastBundle )
         setPodcastBundle( *bundle.m_podcastBundle );
     else
Index: src/enginecontroller.cpp
===================================================================
--- src/enginecontroller.cpp	(revision 524114)
+++ src/enginecontroller.cpp	(working copy)
@@ -489,7 +489,6 @@
     // Prevent spamming by ignoring repeated identical data (some servers repeat it every 10 seconds)
     if ( m_lastMetadata.contains( bundle ) )
         return;
-
     // We compare the new item with the last two items, because mth.house currently cycles
     // two messages alternating, which gets very annoying
     if ( m_lastMetadata.count() == 2 )
@@ -508,13 +507,18 @@
 
 void EngineController::slotEngineMetaData( const Engine::SimpleMetaBundle &simpleBundle ) //SLOT
 {
-    if ( !m_bundle.url().isLocalFile() )
+   //FIXME(Anx): Why is this only non-local files?  For annodexed files, the clip info is available via this slot
+    //if ( !m_bundle.url().isLocalFile() ) 
     {
         MetaBundle bundle = m_bundle;
-        bundle.setArtist( simpleBundle.artist );
-        bundle.setTitle( simpleBundle.title );
-        bundle.setComment( simpleBundle.comment );
-        bundle.setAlbum( simpleBundle.album );
+        if ( !simpleBundle.artist.isEmpty() )
+            bundle.setArtist( simpleBundle.artist );
+        if ( !simpleBundle.title.isEmpty() )
+            bundle.setTitle( simpleBundle.title );
+        if ( !simpleBundle.comment.isEmpty() )
+            bundle.setComment( simpleBundle.comment );
+        if ( !simpleBundle.album.isEmpty() )
+            bundle.setAlbum( simpleBundle.album );
 
         if( !simpleBundle.genre.isEmpty() )
             bundle.setGenre( simpleBundle.genre );
@@ -526,6 +530,8 @@
             bundle.setYear( simpleBundle.year.toInt() );
         if( !simpleBundle.tracknr.isEmpty() )
             bundle.setTrack( simpleBundle.tracknr.toInt() );
+        if ( !simpleBundle.clipList.isEmpty() )
+            bundle.setClipList( simpleBundle.clipList );
 
         slotStreamMetaData( bundle );
     }
Index: src/playlistitem.h
===================================================================
--- src/playlistitem.h	(revision 524114)
+++ src/playlistitem.h	(working copy)
@@ -11,6 +11,7 @@
 
 #include "metabundle.h" //baseclass
 #include "amarok_export.h"
+#include "enginebase.h"
 
 #include <klistview.h> //baseclass
 #include <kurl.h>      //stack allocated
@@ -63,6 +64,7 @@
     virtual void setScore( int score );
     virtual void setPlayCount( int playcount );
     virtual void setLastPlay( uint lastplay );
+    virtual void setClipList( const ClipList &chapters );
 
     public:
         PlaylistItem( QListView*, QListViewItem* ); //used by PlaylistLoader
@@ -178,6 +180,29 @@
         static const QString &editingText();
 };
 
+class ClipItem : public QListViewItem
+{
+    private:
+        uint m_startTime;
+        Engine::ClipInfo m_info;
+
+    public:
+        ClipItem( QListViewItem* parent, QListViewItem* after, const Engine::ClipInfo &info );
+
+        uint startTime() const;
+        Engine::ClipInfo info() const;
+};
+
+inline uint ClipItem::startTime() const
+{
+    return m_startTime;
+}
+
+inline Engine::ClipInfo ClipItem::info() const
+{
+    return m_info;
+}
+
 class PLItemList: public QPtrList<PlaylistItem>
 {
     public:
Index: configure.in.in
===================================================================
--- configure.in.in	(revision 524114)
+++ configure.in.in	(working copy)
@@ -945,7 +945,7 @@
 # BEGIN DO_NOT_COMPILE CHECK
 ###############################################################################
 
-if test x$build_xine = xno -a x$build_helix = xno; then
+if test x$build_xine = xno -a x$build_helix = xno -a x$have_gst10 = xno; then
 
     DO_NOT_COMPILE="$DO_NOT_COMPILE amarok"
     no_engine=yes
