[vlc-devel] [PATCH 06/10][RFC][WIP] qt: add cloudstorage integration and improved sidebar sub-trees logic

Diogo Silva dbtdsilva at gmail.com
Thu Aug 17 03:08:14 CEST 2017


This patches includes some modification in order for the cloudstorage
to be user-friendly and also improves the logic behind the sub-trees
in the sidebar (e.g. Podcasts)
---
 modules/gui/qt/components/playlist/selector.cpp    | 287 +++++++++++++++------
 modules/gui/qt/components/playlist/selector.hpp    |  51 +++-
 .../qt/pixmaps/playlist/sidebar-icons/cloud.png    | Bin 0 -> 252 bytes
 .../qt/pixmaps/playlist/sidebar-icons/cloud.svgz   | Bin 0 -> 448 bytes
 modules/gui/qt/vlc.qrc                             |   1 +
 5 files changed, 252 insertions(+), 87 deletions(-)
 create mode 100644 modules/gui/qt/pixmaps/playlist/sidebar-icons/cloud.png
 create mode 100644 modules/gui/qt/pixmaps/playlist/sidebar-icons/cloud.svgz

diff --git a/modules/gui/qt/components/playlist/selector.cpp b/modules/gui/qt/components/playlist/selector.cpp
index 1d41f7f6af..d6c72ba0ca 100644
--- a/modules/gui/qt/components/playlist/selector.cpp
+++ b/modules/gui/qt/components/playlist/selector.cpp
@@ -62,7 +62,7 @@ void SelectorActionButton::paintEvent( QPaintEvent *event )
 }

 PLSelItem::PLSelItem ( QTreeWidgetItem *i, const QString& text )
-    : qitem(i), lblAction( NULL)
+    : qitem(i), lblAction( NULL), pInnerTree( NULL )
 {
     layout = new QHBoxLayout( this );
     layout->setContentsMargins(0,0,0,0);
@@ -75,6 +75,29 @@ PLSelItem::PLSelItem ( QTreeWidgetItem *i, const QString& text )
     setMinimumHeight( height );
 }

+PLSelItem::~PLSelItem()
+{
+    if ( pInnerTree != NULL )
+        delete pInnerTree;
+}
+
+void PLSelItem::createInnerTree( const char* slot_add, const char* slot_remove,
+        const char* slot_activate )
+{
+    QTreeWidgetItem* parent = this->treeItem();
+    pInnerTree = new PLSelItemTree(parent, slot_add, slot_remove,
+            slot_activate);
+    parent->setChildIndicatorPolicy(
+            QTreeWidgetItem::ChildIndicatorPolicy::ShowIndicator);
+}
+
+void PLSelItem::mouseReleaseEvent( QMouseEvent* event )
+{
+    // Emit an internal signal and re-invoke the original callback
+    emit subTreeActivated( this );
+    QWidget::mouseReleaseEvent( event );
+}
+
 void PLSelItem::addAction( ItemAction act, const QString& tooltip )
 {
     if( lblAction ) return; //might change later
@@ -130,10 +153,6 @@ PLSelector::PLSelector( QWidget *p, intf_thread_t *_p_intf )
 #endif
     setMinimumHeight( 120 );

-    /* Podcasts */
-    podcastsParent = NULL;
-    podcastsParentId = -1;
-
     /* Podcast connects */
     CONNECT( THEMIM, playlistItemAppended( int, int ),
              this, plItemAdded( int, int ) );
@@ -160,20 +179,29 @@ PLSelector::PLSelector( QWidget *p, intf_thread_t *_p_intf )
              this, setSource( QTreeWidgetItem *) );
     CONNECT( this, itemClicked( QTreeWidgetItem *, int ),
              this, setSource( QTreeWidgetItem *) );
+    CONNECT( this, itemExpanded( QTreeWidgetItem * ),
+             this, setSource( QTreeWidgetItem *) );
 }

 PLSelector::~PLSelector()
 {
-    if( podcastsParent )
+    for ( auto& lItem : listItems )
     {
-        int c = podcastsParent->childCount();
+        if (lItem->innerTree() == NULL)
+            continue;
+
+        int c = lItem->treeItem()->childCount();
         for( int i = 0; i < c; i++ )
         {
-            QTreeWidgetItem *item = podcastsParent->child(i);
+            QTreeWidgetItem *item = lItem->treeItem()->child(i);
             input_item_t *p_input = item->data( 0, IN_ITEM_ROLE ).value<input_item_t*>();
             input_item_Release( p_input );
         }
     }
+
+    for (std::vector<PLSelItem*>::iterator it = listItems.begin();
+            it != listItems.end(); ++it)
+        delete *it;
 }

 PLSelItem * putSDData( PLSelItem* item, const char* name, const char* longname )
@@ -279,12 +307,18 @@ void PLSelector::createItems()
             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();
+                selItem->createInnerTree(SLOT(podcastAdd(PLSelItem*)),
+                                         SLOT(podcastRemove(PLSelItem*)),
+                                         nullptr);
                 icon = QIcon( ":/sidebar/podcast" );
             }
+            else if ( name.startsWith( "cloudstorage" ))
+            {
+                selItem->createInnerTree(SLOT(cloudProviderAdd(PLSelItem*)),
+                                         SLOT(cloudProviderRemove(PLSelItem*)),
+                                         SLOT(cloudProviderActivated(PLSelItem*)));
+                icon = QIcon( ":/sidebar/cloud" );
+            }
             else if ( name.startsWith( "lua{" ) )
             {
                 int i_head = name.indexOf( "sd='" ) + 4;
@@ -330,6 +364,14 @@ void PLSelector::createItems()
             selItem = addItem( SD_TYPE, *ppsz_longname );
         }

+        if (selItem->innerTree() != NULL &&
+            selItem->innerTree()->slotAddFunct() != NULL)
+        {
+            selItem->addAction( ADD_ACTION, qtr( "Add a new item" ) );
+            connect( selItem, SIGNAL(action( PLSelItem* )),
+                     this, selItem->innerTree()->slotAddFunct() );
+        }
+
         selItem->treeItem()->setData( 0, SD_CATEGORY_ROLE, *p_category );
         putSDData( selItem, *ppsz_name, *ppsz_longname );
         if ( ! icon.isNull() )
@@ -337,6 +379,8 @@ void PLSelector::createItems()

         free( *ppsz_name );
         free( *ppsz_longname );
+
+        listItems.push_back( selItem );
     }
     free( ppsz_names );
     free( ppsz_longnames );
@@ -359,6 +403,7 @@ void PLSelector::setSource( QTreeWidgetItem *item )
         return;

     bool sd_loaded;
+    playlist_item_t *pl_item = NULL;
     if( i_type == SD_TYPE )
     {
         QString qs = item->data( 0, NAME_ROLE ).toString();
@@ -376,41 +421,33 @@ void PLSelector::setSource( QTreeWidgetItem *item )
                 item->setData( 0, CAP_SEARCH_ROLE, (test.i_capabilities & SD_CAP_SEARCH) );
             }
         }
-    }
-
-    curItem = item;
-
-    /* */
-    playlist_Lock( THEPL );
-    playlist_item_t *pl_item = NULL;

-    /* Special case for podcast */
-    // FIXME: simplify
-    if( i_type == SD_TYPE )
-    {
+        playlist_Lock( THEPL );
         /* Find the right item for the SD */
         /* FIXME: searching by name - what could possibly go wrong? */
         pl_item = playlist_ChildSearchName( &(THEPL->root),
             vlc_gettext(qtu(item->data(0, LONGNAME_ROLE).toString())) );

-        /* Podcasts */
-        if( item->data( 0, SPECIAL_ROLE ).toInt() == IS_PODCAST )
+        PLSelItem* sel_item = itemWidget( item );
+        if (sel_item != NULL && sel_item->innerTree() != NULL)
         {
-            if( pl_item && !sd_loaded )
+            if ( pl_item && !sd_loaded )
             {
-                podcastsParentId = pl_item->i_id;
-                for( int i=0; i < pl_item->i_children; i++ )
-                    addPodcastItem( pl_item->pp_children[i] );
+                sel_item->innerTree()->setParentId( pl_item->i_id );
+                for ( int i = 0; i < pl_item->i_children; i++ )
+                    addItemOnTree( pl_item->pp_children[i], sel_item );
             }
-            pl_item = NULL; //to prevent activating it
+            pl_item = NULL; // prevent sidebar item activation
         }
+        playlist_Unlock( THEPL );
     }
     else
+    {
         pl_item = item->data( 0, PL_ITEM_ROLE ).value<playlist_item_t*>();
+    }

-    playlist_Unlock( THEPL );
+    curItem = item;

-    /* */
     if( pl_item )
     {
         emit categoryActivated( pl_item, false );
@@ -436,19 +473,31 @@ PLSelItem * PLSelector::addItem (
   return selItem;
 }

-PLSelItem *PLSelector::addPodcastItem( playlist_item_t *p_item )
+PLSelItem *PLSelector::addItemOnTree( playlist_item_t *p_item, PLSelItem* sel_item )
 {
     input_item_Hold( p_item->p_input );

     char *psz_name = input_item_GetName( p_item->p_input );
-    PLSelItem *item = addItem( PL_ITEM_TYPE,  psz_name, false, false, podcastsParent );
+    QTreeWidgetItem* parent = sel_item->innerTree()->parent();
+    PLSelItem *item = addItem( PL_ITEM_TYPE,  psz_name, false, false, parent );
     free( psz_name );

-    item->addAction( RM_ACTION, qtr( "Remove this podcast subscription" ) );
     item->treeItem()->setData( 0, PL_ITEM_ROLE, QVariant::fromValue( p_item ) );
     item->treeItem()->setData( 0, PL_ITEM_ID_ROLE, QVariant(p_item->i_id) );
     item->treeItem()->setData( 0, IN_ITEM_ROLE, QVariant::fromValue( p_item->p_input ) );
-    CONNECT( item, action( PLSelItem* ), this, podcastRemove( PLSelItem* ) );
+
+    if (sel_item->innerTree()->slotActivatedFunct() != nullptr)
+    {
+        connect( item, SIGNAL(subTreeActivated( PLSelItem* )),
+                 this, sel_item->innerTree()->slotActivatedFunct() );
+    }
+
+    if (sel_item->innerTree()->slotRemoveFunct() != NULL)
+    {
+        item->addAction( RM_ACTION, qtr( "Remove item" ) );
+        connect( item, SIGNAL(action( PLSelItem* )),
+                 this, sel_item->innerTree()->slotRemoveFunct() );
+    }
     return item;
 }

@@ -503,52 +552,73 @@ void PLSelector::dragMoveEvent ( QDragMoveEvent * event )
 void PLSelector::plItemAdded( int item, int parent )
 {
     updateTotalDuration(playlistItem, "Playlist");
-    if( parent != podcastsParentId || podcastsParent == NULL ) return;

     playlist_Lock( THEPL );

     playlist_item_t *p_item = playlist_ItemGetById( THEPL, item );
-    if( !p_item ) {
+    if( !p_item )
+    {
         playlist_Unlock( THEPL );
         return;
     }

-    int c = podcastsParent->childCount();
-    for( int i = 0; i < c; i++ )
+    PLSelItem* sel_item = NULL;
+
+    for (std::vector<PLSelItem*>::iterator it = listItems.begin();
+            it != listItems.end(); ++it)
+    {
+        PLSelItem* lItem = *it;
+        if (lItem->innerTree() == NULL || lItem->innerTree()->parentId()
+                != parent)
+            continue;
+        sel_item = lItem;
+    }
+
+    if ( sel_item != NULL )
     {
-        QTreeWidgetItem *podItem = podcastsParent->child(i);
-        if( podItem->data( 0, PL_ITEM_ID_ROLE ).toInt() == item )
+        // Check if the item already exists on the tree
+        int childsCounter = sel_item->treeItem()->childCount();
+        for( int i = 0; i < childsCounter; i++ )
         {
-          //msg_Dbg( p_intf, "Podcast already in: (%d) %s", item, p_item->p_input->psz_uri);
-          playlist_Unlock( THEPL );
-          return;
+            QTreeWidgetItem *widgetItem = sel_item->treeItem()->child(i);
+            if( widgetItem->data( 0, PL_ITEM_ID_ROLE ).toInt() == item )
+            {
+                // Item already exists
+                playlist_Unlock( THEPL );
+                return;
+            }
         }
+        // The items does not exists yet
+        addItemOnTree( p_item, sel_item );
+        sel_item->treeItem()->setExpanded( true );
     }

-    //msg_Dbg( p_intf, "Adding podcast: (%d) %s", item, p_item->p_input->psz_uri );
-    addPodcastItem( p_item );
-
     playlist_Unlock( THEPL );
-
-    podcastsParent->setExpanded( true );
 }

 void PLSelector::plItemRemoved( int id )
 {
     updateTotalDuration(playlistItem, "Playlist");
-    if( !podcastsParent ) return;

-    int c = podcastsParent->childCount();
-    for( int i = 0; i < c; i++ )
+    for (std::vector<PLSelItem*>::iterator it = listItems.begin();
+            it != listItems.end(); ++it)
     {
-        QTreeWidgetItem *item = podcastsParent->child(i);
-        if( item->data( 0, PL_ITEM_ID_ROLE ).toInt() == id )
+        PLSelItem* lItem = *it;
+        if (lItem->innerTree() == NULL)
+            continue;
+
+        int c = lItem->treeItem()->childCount();
+        for( int i = 0; i < c; i++ )
         {
-            input_item_t *p_input = item->data( 0, IN_ITEM_ROLE ).value<input_item_t*>();
-            //msg_Dbg( p_intf, "Removing podcast: (%d) %s", id, p_input->psz_uri );
-            input_item_Release( p_input );
-            delete item;
-            return;
+            QTreeWidgetItem *item = lItem->treeItem()->child(i);
+            if( item->data( 0, PL_ITEM_ID_ROLE ).toInt() == id )
+            {
+                input_item_t *p_input = item->data( 0, IN_ITEM_ROLE ).value<input_item_t*>();
+                //msg_Dbg( p_intf, "Removing podcast: (%d) %s", id, p_input->psz_uri );
+                input_item_Release( p_input );
+                delete item;
+                return;
+            }
         }
     }
 }
@@ -557,28 +627,33 @@ 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++ )
+    for (std::vector<PLSelItem*>::iterator it = listItems.begin();
+            it != listItems.end(); ++it)
     {
-        QTreeWidgetItem *item = podcastsParent->child(i);
-        input_item_t *p_input = item->data( 0, IN_ITEM_ROLE ).value<input_item_t*>();
-        if( p_input == arg )
+        PLSelItem* lItem = *it;
+        if (lItem->innerTree() == NULL)
+            continue;
+
+        int c = lItem->treeItem()->childCount();
+        for( int i = 0; i < c; i++ )
         {
-            PLSelItem *si = itemWidget( item );
-            char *psz_name = input_item_GetName( p_input );
-            si->setText( qfu( psz_name ) );
-            free( psz_name );
-            return;
+            QTreeWidgetItem *item = lItem->treeItem()->child(i);
+            input_item_t *p_input = item->data( 0, IN_ITEM_ROLE ).value<input_item_t*>();
+            if( p_input == arg )
+            {
+                PLSelItem *si = itemWidget( item );
+                char *psz_name = input_item_GetName( p_input );
+                si->setText( qfu( psz_name ) );
+                free( psz_name );
+                return;
+            }
         }
     }
 }

-void PLSelector::podcastAdd( PLSelItem * )
+void PLSelector::podcastAdd( PLSelItem * item)
 {
-    assert( podcastsParent );
+    //assert( podcastsParent );

     bool ok;
     QString url = QInputDialog::getText( this, qtr( "Subscribe" ),
@@ -586,7 +661,8 @@ void PLSelector::podcastAdd( PLSelItem * )
                                          QLineEdit::Normal, QString(), &ok );
     if( !ok || url.isEmpty() ) return;

-    setSource( podcastsParent ); //to load the SD in case it's not loaded
+    //to load the SD in case it's not loaded
+    setSource( item->innerTree()->parent() );

     QString request("ADD:");
     request += url.trimmed();
@@ -613,6 +689,56 @@ void PLSelector::podcastRemove( PLSelItem* item )
     free( psz_uri );
 }

+void PLSelector::cloudProviderAdd( PLSelItem * item )
+{
+    QStringList items;
+    items << "google" << "onedrive" << "dropbox" << "amazon" << "box";
+    items << "youtube" << "yandex" << "amazons3" << "owncloud" << "mega";
+
+    bool ok;
+    QString provider = QInputDialog::getItem( this,
+                           qtr( "Cloud Storage Provider" ),
+                           qtr( "Select the provider to authenticate" ),
+                           items, 0, false, &ok);
+    if( !ok || provider.isEmpty() ) return;
+
+    //to load the SD in case it's not loaded
+    setSource( item->innerTree()->parent() );
+
+    QString request("ADD:" + provider);
+    var_SetString( THEPL, "cloudstorage-request", qtu( request ) );
+}
+
+void PLSelector::cloudProviderRemove( PLSelItem* item )
+{
+    QString question ( qtr( "Do you really want to remove the service %1?" ) );
+    question = question.arg( item->text() );
+    QMessageBox::StandardButton res =
+        QMessageBox::question( this, qtr( "Logout" ), question,
+                               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;
+
+    QString request("RM:");
+    request += qfu( input->psz_name );
+    var_SetString( THEPL, "cloudstorage-request", qtu( request ) );
+}
+
+void PLSelector::cloudProviderActivated( PLSelItem* item )
+{
+    input_item_t *input = item->treeItem()->data( 0, IN_ITEM_ROLE ).
+            value<input_item_t*>();
+    if( !input ) return;
+
+    QString request("ACT:");
+    request += qfu( input->psz_name );
+    var_SetString( THEPL, "cloudstorage-request", qtu( request ) );
+}
+
 PLSelItem * PLSelector::itemWidget( QTreeWidgetItem *item )
 {
     return ( static_cast<PLSelItem*>( QTreeWidget::itemWidget( item, 0 ) ) );
@@ -652,3 +778,12 @@ void PLSelector::wheelEvent( QWheelEvent *e )
     // Accept this event in order to prevent unwanted volume up/down changes
     e->accept();
 }
+
+PLSelItemTree::PLSelItemTree(QTreeWidgetItem* parent,
+            const char* slot_add_funct, const char* slot_remove_funct,
+            const char* slot_activate_funct) :
+        parent_ptr(parent), parent_id(-1), slot_add_funct(slot_add_funct),
+        slot_remove_funct(slot_remove_funct),
+        slot_activate_funct(slot_activate_funct)
+{
+}
diff --git a/modules/gui/qt/components/playlist/selector.hpp b/modules/gui/qt/components/playlist/selector.hpp
index b7d629af91..51f1572291 100644
--- a/modules/gui/qt/components/playlist/selector.hpp
+++ b/modules/gui/qt/components/playlist/selector.hpp
@@ -47,8 +47,7 @@ enum SelectorItemType {
 };

 enum SpecialData {
-    IS_PODCAST = 1,
-    IS_PL,
+    IS_PL = 1,
     IS_ML
 };

@@ -76,11 +75,31 @@ protected:
     void paintEvent( QPaintEvent * ) Q_DECL_OVERRIDE;
 };

+class PLSelItemTree
+{
+public:
+    PLSelItemTree(QTreeWidgetItem*, const char* = nullptr,
+            const char* = nullptr, const char * = nullptr);
+    void setParentId(int id) { parent_id = id; }
+    int parentId() const { return parent_id; }
+    QTreeWidgetItem* parent() const { return parent_ptr; }
+    const char* slotAddFunct() const { return slot_add_funct; }
+    const char* slotRemoveFunct() const { return slot_remove_funct; }
+    const char* slotActivatedFunct() const { return slot_activate_funct; }
+private:
+    QTreeWidgetItem*    parent_ptr;
+    int                 parent_id;
+    const char*         slot_add_funct;
+    const char*         slot_remove_funct;
+    const char*         slot_activate_funct;
+};
+
 class PLSelItem : public QWidget
 {
     Q_OBJECT
 public:
     PLSelItem( QTreeWidgetItem*, const QString& );
+    virtual ~PLSelItem();

     void setText( const QString& text ) { lbl->setText( text ); }
     QString text() const { return lbl->text(); }
@@ -88,6 +107,12 @@ public:
     void addAction( ItemAction, const QString& toolTip = 0 );
     QTreeWidgetItem *treeItem() { return qitem; }

+    void createInnerTree( const char* = nullptr, const char* = nullptr,
+                          const char* = nullptr );
+    PLSelItemTree* innerTree() const { return pInnerTree; }
+protected:
+    void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
+
 public slots:
     void showAction() { if( lblAction ) lblAction->show();  }
     void hideAction() { if( lblAction ) lblAction->hide(); }
@@ -96,16 +121,18 @@ private slots:
     void triggerAction() { emit action( this ); }

 signals:
+    void subTreeActivated( PLSelItem* );
     void action( PLSelItem* );

 private:
     inline void enterEvent( QEvent* ){ showAction(); }
     inline void leaveEvent( QEvent* ){ hideAction(); }

-    QTreeWidgetItem*     qitem;
-    QFramelessButton* lblAction;
-    QLabel*              lbl;
-    QHBoxLayout*         layout;
+    QTreeWidgetItem *   qitem;
+    QFramelessButton *  lblAction;
+    QLabel*             lbl;
+    QHBoxLayout*        layout;
+    PLSelItemTree*      pInnerTree;
 };

 #include <vlc_input_item.h>
@@ -133,7 +160,7 @@ private:
     void createItems();
     PLSelItem * addItem ( SelectorItemType type, const char* str,
             bool drop = false, bool bold = false, QTreeWidgetItem* parentItem = 0 );
-    PLSelItem * addPodcastItem( playlist_item_t *p_item );
+    PLSelItem * addItemOnTree( playlist_item_t *p_item, PLSelItem* sel_item );

     PLSelItem* playlistItem;

@@ -141,10 +168,9 @@ private:

     inline PLSelItem * itemWidget( QTreeWidgetItem * );

-    intf_thread_t    *p_intf;
-    QTreeWidgetItem  *podcastsParent;
-    int               podcastsParentId;
-    QTreeWidgetItem  *curItem;
+    intf_thread_t *             p_intf;
+    QTreeWidgetItem *           curItem;
+    std::vector<PLSelItem *>    listItems;

 private slots:
     void setSource( QTreeWidgetItem *item );
@@ -153,6 +179,9 @@ private slots:
     void inputItemUpdate( input_item_t * );
     void podcastAdd( PLSelItem* );
     void podcastRemove( PLSelItem* );
+    void cloudProviderAdd( PLSelItem* );
+    void cloudProviderRemove( PLSelItem* );
+    void cloudProviderActivated( PLSelItem* );

 signals:
     void categoryActivated( playlist_item_t *, bool );
diff --git a/modules/gui/qt/pixmaps/playlist/sidebar-icons/cloud.png b/modules/gui/qt/pixmaps/playlist/sidebar-icons/cloud.png
new file mode 100644
index 0000000000000000000000000000000000000000..f68cb091452b5a315ee2d411ac7a836545c6142a
GIT binary patch
literal 252
zcmeAS at N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf<Z~8yL>4nJ
za0`PlBg3pY5<tOuo-U3d7QI(58uBqZN*w(t&&9b^_(I1EnFG8naZ~uVs886 at 9-ukL
zY4L at VULq<QGg3C4Of!%EI4S&$&%gK8#)t2(4lJ-c#-u!F!ZhA>iLK#Jq|;m<2ykAL
zw6Jhh=g)gIhxypN;{g at Ta%C^3?TC}lmHEIkd$!Qz&3=!rRJS&8U)j%iT<GfNUB|jJ
uTgANX{>z#iU|HLAl0RqT`U}6rzp^R)a6cNT-Mkj)QU*^~KbLh*2~7a8Qd>X(

literal 0
HcmV?d00001

diff --git a/modules/gui/qt/pixmaps/playlist/sidebar-icons/cloud.svgz b/modules/gui/qt/pixmaps/playlist/sidebar-icons/cloud.svgz
new file mode 100644
index 0000000000000000000000000000000000000000..4258dd1fce7fe73b0ec7a3860a6e28144023a4c5
GIT binary patch
literal 448
zcmV;x0YCm9iwFpKToPCS17mD&b!0Acc4q)>P+f1FFcADJu3zp2ufG9bfk?TFqDpBS
zscEY|RRpX9g>ne!amlamnncw}tt_usyEC)1&aTeCI&$*k)E at hsN)VJ>71_J}=iS4P
z`x`Qy4usr)y}tXfBOAu~cfI3$|F9=tzTFZDlJlES8$veCJP%vWmt}!P!*M+D+p!#)
zwwgF1c|anIqnwmb&-JFrf^_5CLo?^pN;RA*Y3rQcmCt_sDJhxe=g#NU_vK^f*<)Ef
z9meDDzTWnJAp#GQ!hKIK{Uh<z`sMm~&glh+lBKQZ=I<ojdOHneg#(7sPkuc4wb)NN
z{dX57gb3c>Alq}-_D}x<O6MG3yR^uLa&8E2^ijJMWTdnPRS70RMFJF4VAfYdjMdmO
zD<Nf4CJ<w^ki6y<GcmFSOR*HG1PqhHfR5s%VyMduUIZ!@gJmj44NM5Bm~vo|XH6I`
z!7-ac8d(8$XA=}q+M*S$A{9G?V4{VqL_ny>2rM}PE=q%plZ{GZNIDUT${>#gP%6=-
qPO!gbsRl#H&ZGi;3Wy_P^g1WRi%)o=yvV|T7JmRtZbsyP0ssJ2!P9R5

literal 0
HcmV?d00001

diff --git a/modules/gui/qt/vlc.qrc b/modules/gui/qt/vlc.qrc
index 59055fb6bb..3883111699 100644
--- a/modules/gui/qt/vlc.qrc
+++ b/modules/gui/qt/vlc.qrc
@@ -133,6 +133,7 @@
         <file alias="library">pixmaps/playlist/sidebar-icons/library.png</file>
         <file alias="capture">pixmaps/playlist/sidebar-icons/capture.png</file>
         <file alias="lan">pixmaps/playlist/sidebar-icons/lan.png</file>
+        <file alias="cloud">pixmaps/playlist/sidebar-icons/cloud.png</file>
     </qresource>
     <qresource prefix="/sidebar/sd">
         <file alias="icecast">pixmaps/playlist/sidebar-icons/sd/icecast.png</file>
--
2.14.1


More information about the vlc-devel mailing list