]> git.sesse.net Git - vlc/blobdiff - modules/gui/qt4/components/playlist/selector.cpp
Qt: StandardPanel: Add spinner on network SD nodes.
[vlc] / modules / gui / qt4 / components / playlist / selector.cpp
index b78e9cbe2192fec4d22f0cb471fa334a6e0cda25..6463176aeb16ba9044665f7450d10d6d174b4862 100644 (file)
@@ -31,6 +31,7 @@
 #include "playlist_model.hpp"                /* plMimeData */
 #include "input_manager.hpp"                 /* MainInputManager, for podcast */
 
+#include <QApplication>
 #include <QInputDialog>
 #include <QMessageBox>
 #include <QMimeData>
@@ -39,6 +40,9 @@
 #include <QHBoxLayout>
 #include <QPainter>
 #include <QPalette>
+#include <QScrollBar>
+#include <QResource>
+#include <assert.h>
 
 #include <vlc_playlist.h>
 #include <vlc_services_discovery.h>
@@ -140,6 +144,12 @@ PLSelector::PLSelector( QWidget *p, intf_thread_t *_p_intf )
 
     createItems();
 
+    setRootIsDecorated( false );
+    setIndentation( 5 );
+    /* Expand at least to show level 2 */
+    for ( int i = 0; i < topLevelItemCount(); i++ )
+        expandItem( topLevelItem( i ) );
+
     /***
      * We need to react to both clicks and activation (enter-key) here.
      * We use curItem to avoid rebuilding twice.
@@ -181,29 +191,73 @@ PLSelItem * putPLData( PLSelItem* item, playlist_item_t* plItem )
     return item;
 }
 
+/*
+ * Reads and updates the playlist's duration as [xx:xx] after the label in the tree
+ * item - the treeview item to get the duration for
+ * prefix - the string to use before the time (should be the category name)
+ */
+void PLSelector::updateTotalDuration( PLSelItem* item, const char* prefix )
+{
+    /* Getting  the playlist */
+    QVariant playlistVariant = item->treeItem()->data( 0, PL_ITEM_ROLE );
+    playlist_item_t* node = playlistVariant.value<playlist_item_t*>();
+
+    /* Get the duration of the playlist item */
+    playlist_Lock( THEPL );
+    mtime_t mt_duration = playlist_GetNodeDuration( node );
+    playlist_Unlock( THEPL );
+
+    /* Formatting time */
+    QString qs_timeLabel( prefix );
+
+    int i_seconds = mt_duration / 1000000;
+    int i_minutes = i_seconds / 60;
+    i_seconds = i_seconds % 60;
+    if( i_minutes >= 60 )
+    {
+        int i_hours = i_minutes / 60;
+        i_minutes = i_minutes % 60;
+        qs_timeLabel += QString(" [%1:%2:%3]").arg( i_hours ).arg( i_minutes, 2, 10, QChar('0') ).arg( i_seconds, 2, 10, QChar('0') );
+    }
+    else
+        qs_timeLabel += QString( " [%1:%2]").arg( i_minutes, 2, 10, QChar('0') ).arg( i_seconds, 2, 10, QChar('0') );
+
+    item->setText( qs_timeLabel );
+}
+
 void PLSelector::createItems()
 {
     /* PL */
-    PLSelItem *pl = putPLData( addItem( PL_ITEM_TYPE, N_("Playlist"), true ),
+    playlistItem = putPLData( addItem( PL_ITEM_TYPE, N_("Playlist"), true ),
                               THEPL->p_playing );
-    pl->treeItem()->setData( 0, SPECIAL_ROLE, QVariant( IS_PL ) );
-    setCurrentItem( pl->treeItem() );
+    playlistItem->treeItem()->setData( 0, SPECIAL_ROLE, QVariant( IS_PL ) );
+    playlistItem->treeItem()->setData( 0, Qt::DecorationRole, QIcon( ":/sidebar/playlist" ) );
+    setCurrentItem( playlistItem->treeItem() );
 
     /* ML */
     PLSelItem *ml = putPLData( addItem( PL_ITEM_TYPE, N_("Media Library"), true ),
                               THEPL->p_media_library );
     ml->treeItem()->setData( 0, SPECIAL_ROLE, QVariant( IS_ML ) );
+    ml->treeItem()->setData( 0, Qt::DecorationRole, QIcon( ":/sidebar/library" ) );
 
 #ifdef MEDIA_LIBRARY
     /* SQL ML */
-    addItem( SQL_ML_TYPE, "SQL Media Library" )->treeItem();
+    ml = addItem( SQL_ML_TYPE, "SQL Media Library" )->treeItem();
+    ml->treeItem()->setData( 0, Qt::DecorationRole, QIcon( ":/sidebar/library" ) );
 #endif
 
     /* SD nodes */
-    QTreeWidgetItem *mycomp = addItem( CATEGORY_TYPE, N_("My Computer") )->treeItem();
-    QTreeWidgetItem *devices = addItem( CATEGORY_TYPE, N_("Devices") )->treeItem();
-    QTreeWidgetItem *lan = addItem( CATEGORY_TYPE, N_("Local Network") )->treeItem();
-    QTreeWidgetItem *internet = addItem( CATEGORY_TYPE, N_("Internet") )->treeItem();
+    QTreeWidgetItem *mycomp = addItem( CATEGORY_TYPE, N_("My Computer"), false, true )->treeItem();
+    QTreeWidgetItem *devices = addItem( CATEGORY_TYPE, N_("Devices"), false, true )->treeItem();
+    QTreeWidgetItem *lan = addItem( CATEGORY_TYPE, N_("Local Network"), false, true )->treeItem();
+    QTreeWidgetItem *internet = addItem( CATEGORY_TYPE, N_("Internet"), false, true )->treeItem();
+
+#define NOT_SELECTABLE(w) w->setFlags( w->flags() ^ Qt::ItemIsSelectable );
+    NOT_SELECTABLE( mycomp );
+    NOT_SELECTABLE( devices );
+    NOT_SELECTABLE( lan );
+    NOT_SELECTABLE( internet );
+#undef NOT_SELECTABLE
 
     /* SD subnodes */
     char **ppsz_longnames;
@@ -219,34 +273,72 @@ void PLSelector::createItems()
         //msg_Dbg( p_intf, "Adding a SD item: %s", *ppsz_longname );
 
         PLSelItem *selItem;
+        QIcon icon;
+        QString name( *ppsz_name );
         switch( *p_category )
         {
         case SD_CAT_INTERNET:
             {
-            selItem = addItem( SD_TYPE, *ppsz_longname, false, internet );
-            if( !strncmp( *ppsz_name, "podcast", 7 ) )
+            selItem = addItem( SD_TYPE, *ppsz_longname, false, false, internet );
+            if( name.startsWith( "podcast" ) )
             {
                 selItem->treeItem()->setData( 0, SPECIAL_ROLE, QVariant( IS_PODCAST ) );
                 selItem->addAction( ADD_ACTION, qtr( "Subscribe to a podcast" ) );
                 CONNECT( selItem, action( PLSelItem* ), this, podcastAdd( PLSelItem* ) );
                 podcastsParent = selItem->treeItem();
+                icon = QIcon( ":/sidebar/podcast" );
+            }
+            else if ( name.startsWith( "lua{" ) )
+            {
+                int i_head = name.indexOf( "sd='" ) + 4;
+                int i_tail = name.indexOf( '\'', i_head );
+                name.mid( i_head, i_tail - i_head );
+                QString iconname = QString( ":/sidebar/sd/%1" ).arg( name.mid( i_head, i_tail - i_head ) );
+                QResource resource( iconname );
+                if ( !resource.isValid() )
+                    icon = QIcon( ":/sidebar/network" );
+                else
+                    icon = QIcon( iconname );
             }
             }
             break;
         case SD_CAT_DEVICES:
-            selItem = addItem( SD_TYPE, *ppsz_longname, false, devices );
+            name = name.mid( 0, name.indexOf( '{' ) );
+            selItem = addItem( SD_TYPE, *ppsz_longname, false, false, devices );
+            if ( name == "xcb_apps" )
+                icon = QIcon( ":/sidebar/screen" );
+            else if ( name == "mtp" )
+                icon = QIcon( ":/sidebar/mtp" );
+            else if ( name == "disc" )
+                icon = QIcon( ":/sidebar/disc" );
+            else
+                icon = QIcon( ":/sidebar/capture" );
             break;
         case SD_CAT_LAN:
-            selItem = addItem( SD_TYPE, *ppsz_longname, false, lan );
+            selItem = addItem( SD_TYPE, *ppsz_longname, false, false, lan );
+            icon = QIcon( ":/sidebar/lan" );
             break;
         case SD_CAT_MYCOMPUTER:
-            selItem = addItem( SD_TYPE, *ppsz_longname, false, mycomp );
+            name = name.mid( 0, name.indexOf( '{' ) );
+            selItem = addItem( SD_TYPE, *ppsz_longname, false, false, mycomp );
+            if ( name == "video_dir" )
+                icon = QIcon( ":/sidebar/movie" );
+            else if ( name == "audio_dir" )
+                icon = QIcon( ":/sidebar/music" );
+            else if ( name == "picture_dir" )
+                icon = QIcon( ":/sidebar/pictures" );
+            else
+                icon = QIcon( ":/sidebar/movie" );
             break;
         default:
             selItem = addItem( SD_TYPE, *ppsz_longname );
         }
 
+        selItem->treeItem()->setData( 0, SD_CATEGORY_ROLE, *p_category );
         putSDData( selItem, *ppsz_name, *ppsz_longname );
+        if ( ! icon.isNull() )
+            selItem->treeItem()->setData( 0, Qt::DecorationRole, icon );
+
         free( *ppsz_name );
         free( *ppsz_longname );
     }
@@ -281,8 +373,8 @@ void PLSelector::setSource( QTreeWidgetItem *item )
                 return ;
 
             services_discovery_descriptor_t *p_test = new services_discovery_descriptor_t;
-            playlist_ServicesDiscoveryControl( THEPL, qtu( qs ), SD_CMD_DESCRIPTOR, p_test );
-            if( p_test->i_capabilities & SD_CAP_SEARCH )
+            int i_ret = playlist_ServicesDiscoveryControl( THEPL, qtu( qs ), SD_CMD_DESCRIPTOR, p_test );
+            if( i_ret == VLC_SUCCESS && p_test->i_capabilities & SD_CAP_SEARCH )
                 item->setData( 0, CAP_SEARCH_ROLE, true );
         }
     }
@@ -290,6 +382,7 @@ void PLSelector::setSource( QTreeWidgetItem *item )
     else if( i_type == SQL_ML_TYPE )
     {
         emit categoryActivated( NULL, true );
+        curItem = item;
         return;
     }
 #endif
@@ -327,17 +420,24 @@ void PLSelector::setSource( QTreeWidgetItem *item )
 
     /* */
     if( pl_item )
+    {
         emit categoryActivated( pl_item, false );
+        emit SDCategorySelected(
+                    item->data( 0, SD_CATEGORY_ROLE ).toInt()
+                    & ( SD_CAT_INTERNET | SD_CAT_LAN )
+        );
+    }
 }
 
 PLSelItem * PLSelector::addItem (
-    SelectorItemType type, const char* str, bool drop,
+    SelectorItemType type, const char* str, bool drop, bool bold,
     QTreeWidgetItem* parentItem )
 {
   QTreeWidgetItem *item = parentItem ?
       new QTreeWidgetItem( parentItem ) : new QTreeWidgetItem( this );
 
   PLSelItem *selItem = new PLSelItem( item, qtr( str ) );
+  if ( bold ) selItem->setStyleSheet( "font-weight: bold;" );
   setItemWidget( item, 0, selItem );
   item->setData( 0, TYPE_ROLE, (int)type );
   if( !drop ) item->setFlags( item->flags() & ~Qt::ItemIsDropEnabled );
@@ -350,7 +450,7 @@ PLSelItem *PLSelector::addPodcastItem( playlist_item_t *p_item )
     vlc_gc_incref( p_item->p_input );
 
     char *psz_name = input_item_GetName( p_item->p_input );
-    PLSelItem *item = addItem( PL_ITEM_TYPE,  psz_name, false, podcastsParent );
+    PLSelItem *item = addItem( PL_ITEM_TYPE,  psz_name, false, false, podcastsParent );
     free( psz_name );
 
     item->addAction( RM_ACTION, qtr( "Remove this podcast subscription" ) );
@@ -411,7 +511,8 @@ void PLSelector::dragMoveEvent ( QDragMoveEvent * event )
 
 void PLSelector::plItemAdded( int item, int parent )
 {
-    if( parent != podcastsParentId ) return;
+    updateTotalDuration(playlistItem, "Playlist");
+    if( parent != podcastsParentId || podcastsParent == NULL ) return;
 
     playlist_Lock( THEPL );
 
@@ -443,6 +544,9 @@ void PLSelector::plItemAdded( int item, int parent )
 
 void PLSelector::plItemRemoved( int id )
 {
+    updateTotalDuration(playlistItem, "Playlist");
+    if( !podcastsParent ) return;
+
     int c = podcastsParent->childCount();
     for( int i = 0; i < c; i++ )
     {
@@ -460,6 +564,11 @@ void PLSelector::plItemRemoved( int id )
 
 void PLSelector::inputItemUpdate( input_item_t *arg )
 {
+    updateTotalDuration(playlistItem, "Playlist");
+
+    if( podcastsParent == NULL )
+        return;
+
     int c = podcastsParent->childCount();
     for( int i = 0; i < c; i++ )
     {
@@ -478,6 +587,8 @@ void PLSelector::inputItemUpdate( input_item_t *arg )
 
 void PLSelector::podcastAdd( PLSelItem * )
 {
+    assert( podcastsParent );
+
     bool ok;
     QString url = QInputDialog::getText( this, qtr( "Subscribe" ),
                                          qtr( "Enter URL of the podcast to subscribe to:" ),
@@ -501,9 +612,9 @@ void PLSelector::podcastRemove( PLSelItem* item )
     question = question.arg( item->text() );
     QMessageBox::StandardButton res =
         QMessageBox::question( this, qtr( "Unsubscribe" ), question,
-                               QMessageBox::Ok | QMessageBox::Cancel,
-                               QMessageBox::Cancel );
-    if( res == QMessageBox::Cancel ) return;
+                               QMessageBox::Yes | QMessageBox::No,
+                               QMessageBox::No );
+    if( res == QMessageBox::No ) return;
 
     input_item_t *input = item->treeItem()->data( 0, IN_ITEM_ROLE ).value<input_item_t*>();
     if( !input ) return;
@@ -550,6 +661,12 @@ int PLSelector::getCurrentItemCategory()
 
 void PLSelector::wheelEvent( QWheelEvent *e )
 {
+    if( verticalScrollBar()->isVisible() && (
+        (verticalScrollBar()->value() != verticalScrollBar()->minimum() && e->delta() >= 0 ) ||
+        (verticalScrollBar()->value() != verticalScrollBar()->maximum() && e->delta() < 0 )
+        ) )
+        QApplication::sendEvent(verticalScrollBar(), e);
+
     // Accept this event in order to prevent unwanted volume up/down changes
     e->accept();
 }