<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <meta http-equiv="Content-Style-Type" content="text/css" />
  <meta name="generator" content="pandoc" />
  <title></title>
  <style type="text/css">code{white-space: pre;}</style>
</head>
<body>
<p>Hi PaweÅ‚,</p>
<p>Thank you for rebasing the patches to make them easier to review and maintain, very much appreciated!</p>
<p>On 2016-10-27 23:18, PaweÅ‚ Wegner wrote:</p>
<blockquote style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;color:#500050">
<pre><code> ---
  include/vlc_interface.h                      |   1 +
  modules/gui/qt/Makefile.am                   |   2 +
  modules/gui/qt/components/open_panels.cpp    | 233 +++++++++++++++++++++++++++
  modules/gui/qt/components/open_panels.hpp    |  71 ++++++++
  modules/gui/qt/dialogs/open.cpp              |  15 ++
  modules/gui/qt/dialogs/open.hpp              |   4 +
  modules/gui/qt/dialogs_provider.cpp          |   7 +
  modules/gui/qt/dialogs_provider.hpp          |   1 +
  modules/gui/qt/menus.cpp                     |   4 +
  modules/gui/qt/pixmaps/types/type_cloud.png  | Bin 0 -> 252 bytes
  modules/gui/qt/pixmaps/types/type_cloud.svgz | Bin 0 -> 448 bytes
  modules/gui/qt/ui/open_cloud.ui              |  19 +++
  modules/gui/qt/vlc.qrc                       |   1 +
  13 files changed, 358 insertions(+)
  create mode 100644 modules/gui/qt/pixmaps/types/type_cloud.png
  create mode 100644 modules/gui/qt/pixmaps/types/type_cloud.svgz
  create mode 100644 modules/gui/qt/ui/open_cloud.ui

 diff --git a/include/vlc_interface.h b/include/vlc_interface.h
 index a4006f0..d42a19a 100644
 --- a/include/vlc_interface.h
 +++ b/include/vlc_interface.h
 @@ -118,6 +118,7 @@ typedef enum vlc_intf_dialog {
      INTF_DIALOG_NET,
      INTF_DIALOG_CAPTURE,
      INTF_DIALOG_SAT,
 +    INTF_DIALOG_CLOUD,
      INTF_DIALOG_DIRECTORY,

      INTF_DIALOG_STREAMWIZARD,
 diff --git a/modules/gui/qt/Makefile.am b/modules/gui/qt/Makefile.am
 index ab413ac..36525f3 100644
 --- a/modules/gui/qt/Makefile.am
 +++ b/modules/gui/qt/Makefile.am
 @@ -213,6 +213,7 @@ nodist_libqt_plugin_la_SOURCES += \
      ui/open_disk.h \
      ui/open_net.h \
      ui/open_capture.h \
 +    ui/open_cloud.h \
      ui/open.h \
      ui/vlm.h \
      ui/podcast_configuration.h \
 @@ -253,6 +254,7 @@ libqt_plugin_la_UI = \
      ui/open_disk.ui \
      ui/open_net.ui \
      ui/open_capture.ui \
 +    ui/open_cloud.ui \
      ui/open.ui \
      ui/podcast_configuration.ui \
      ui/profiles.ui \
 diff --git a/modules/gui/qt/components/open_panels.cpp b/modules/gui/qt/components/open_panels.cpp
 index 9f05548..e60dbbf 100644
 --- a/modules/gui/qt/components/open_panels.cpp
 +++ b/modules/gui/qt/components/open_panels.cpp
 @@ -53,6 +53,7 @@
  #include <QUrl>
  #include <QMimeData>
  #include <QDropEvent>
 +#include <QDesktopServices>

  #define I_DEVICE_TOOLTIP \
      I_DIR_OR_FOLDER( N_("Select a device or a VIDEO_TS directory"), \
 @@ -1403,3 +1404,235 @@ void CaptureOpenPanel::advancedDialog()
      module_config_free( p_config );
  }

 +#ifdef WITH_LIBCLOUDSTORAGE
 +
 +using cloudstorage::IItem;
 +using cloudstorage::ICloudProvider;
 +
 +namespace
 +{
 +class CloudCallback : public cloudstorage::ICloudProvider::ICallback
 +{
 +public:
 +    Status userConsentRequired( const ICloudProvider& provider ) override
 +    {
 +        QDesktopServices::openUrl( QUrl( provider.authorizeLibraryUrl().c_str() ) );
 +        return Status::WaitForAuthorizationCode;
 +    }
 +
 +    void accepted( const ICloudProvider& ) override
 +    {
 +    }
 +
 +    void declined( const ICloudProvider& ) override
 +    {
 +    }
 +
 +    void error( const ICloudProvider&,
 +                const std::string& ) override
 +    {
 +    }</code></pre>
</blockquote>
<p>See upcoming note regarding errors.</p>
<blockquote style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;color:#500050">
<pre><code> +};
 +
 +class CloudListDirectoryCallback : public cloudstorage::IListDirectoryCallback {
 +public:
 +    CloudListDirectoryCallback( DirectoryModel *model ) : model( model )
 +    {
 +    }
 +
 +    void receivedItem( IItem::Pointer item ) override
 +    {
 +        model->addItem( item );
 +    }
 +
 +    void done( const std::vector<IItem::Pointer>& ) override
 +    {
 +    }
 +
 +    void error( const std::string& ) override
 +    {
 +    }
 +</code></pre>
</blockquote>
<p>It somewhat worries me that the above seems to indicate that errors are simply discarded (since no action is taking in <code>CloudCallback::error</code>, nor <code>CloudListDirectoryCallback::error</code>).</p>
<p>You should handle potential errors, and if such occur (and is fatal enough), display a message to the user using <code>ErrorsDialog::addError</code>.</p>
<blockquote style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;color:#500050">
<pre><code> +private:
 +    DirectoryModel* model;
 +};
 +} // namespace
 +
 +CloudOpenPanel::CloudOpenPanel( QWidget *parent, intf_thread_t *t ) :
 +                                  OpenPanel( parent, t )
 +{
 +    cloudStorage = cloudstorage::ICloudStorage::create();
 +    QWidget* selectProvider = new QWidget( this );
 +    QVBoxLayout* selectProviderLayout = new QVBoxLayout;
 +    for ( auto t : cloudStorage->providers() )
 +    {
 +        QSettings settings;
 +        std::string token = settings.value( t->name().c_str() ).toString().toStdString();</code></pre>
</blockquote>
<p>The name of a <em>cloud-provider</em> is not guaranteed to be unique among the settings in <em>VLC</em>, as such it is probably better to have a prefix that denotes that the setting is related to <em>cloud-storage</em>.</p>
<blockquote style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;color:#500050">
<pre><code> +        t->initialize( {token, std::unique_ptr<CloudCallback>( new CloudCallback ),
 +                       nullptr, nullptr, nullptr, {}} );
 +        QPushButton* button = new QPushButton( t->name().c_str() );
 +        selectProviderLayout->addWidget( button );
 +        connect( button, &QPushButton::pressed, this, &CloudOpenPanel::buttonClicked );
 +    }
 +    selectProvider->setLayout( selectProviderLayout );
 +    CloudListView* selectFile = new CloudListView( this );
 +    selectFile->setModel( &directoryModel );
 +    connect( selectFile, &QListView::doubleClicked, this, &CloudOpenPanel::itemClicked );
 +    connect( selectFile, &CloudListView::selectedItemChanged, this, &CloudOpenPanel::selectionChanged );
 +    QHBoxLayout* mainLayout = new QHBoxLayout( this );
 +    mainLayout->addWidget( selectProvider );
 +    mainLayout->addWidget( selectFile );
 +    setLayout( mainLayout );
 +    ui.setupUi( this );
 +}
 +
 +CloudOpenPanel::~CloudOpenPanel()
 +{
 +    save();
 +}
 +
 +void CloudOpenPanel::save()
 +{
 +    if ( currentProvider )
 +    {
 +        QSettings settings;
 +        settings.setValue( currentProvider->name().c_str(), currentProvider->token().c_str() );</code></pre>
</blockquote>
<p>See previous comment regarding settings and potential name-collision.</p>
<blockquote style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;color:#500050">
<pre><code> +    }
 +}
 +
 +void CloudOpenPanel::clear()
 +{
 +    emit mrlUpdated( QStringList(), "" );
 +}
 +
 +void CloudOpenPanel::keyPressEvent( QKeyEvent *e )
 +{
 +    OpenPanel::keyPressEvent( e );
 +    if ( !e->isAccepted() && e->key() == Qt::Key_Backspace )
 +    {
 +        if ( !directoryStack.empty() )
 +        {
 +            currentDirectory = directoryStack.back();
 +            directoryStack.pop_back();
 +            listDirectory();
 +        }
 +        else
 +        {
 +            directoryModel.clear();
 +            currentDirectory = nullptr;
 +            currentProvider = nullptr;
 +        }
 +        emit mrlUpdated( QStringList(), "" );
 +        e->accept();
 +    }
 +}
 +
 +void CloudOpenPanel::buttonClicked()
 +{
 +    save();
 +    directoryStack.clear();
 +    QPushButton* button = static_cast<QPushButton*>( sender() );
 +    currentProvider = cloudStorage->provider( button->text().toStdString() );
 +    currentDirectory = currentProvider->rootDirectory();
 +    listDirectory();
 +}
 +
 +void CloudOpenPanel::itemClicked( const QModelIndex& index )
 +{
 +    auto item = directoryModel.get( index.row() );
 +    if ( item->type() == cloudstorage::IItem::FileType::Directory )
 +    {
 +        directoryStack.push_back( currentDirectory );
 +        currentDirectory = item;
 +        listDirectory();
 +    }
 +    else
 +    {
 +        itemDataRequest = currentProvider->getItemDataAsync( item->id(), [this]( IItem::Pointer i ) {
 +            if ( !i ) return;
 +            QStringList list;
 +            list << i->url().c_str();
 +            emit methodChanged( qfu( "network-caching" ) );
 +            emit mrlUpdated( list, "" );
 +        } );
 +    }
 +}
 +
 +void CloudOpenPanel::selectionChanged()
 +{
 +    emit mrlUpdated( QStringList(), "" );
 +}
 +
 +void CloudOpenPanel::listDirectory()
 +{
 +    directoryModel.clear();
 +    std::unique_ptr<CloudListDirectoryCallback> callback( new CloudListDirectoryCallback( &directoryModel ) );
 +    listDirectoryRequest = currentProvider->listDirectoryAsync( currentDirectory,
 +                                                                std::move( callback ) );
 +}
 +
 +void CloudOpenPanel::updateMRL()
 +{
 +    emit mrlUpdated( QStringList(), "" );
 +}
 +
 +void CloudListView::selectionChanged( const QItemSelection&, const QItemSelection& )
 +{
 +    emit selectedItemChanged();
 +}
 +
 +void DirectoryModel::addItem( IItem::Pointer item )
 +{
 +  beginInsertRows( QModelIndex(), rowCount(), rowCount() );
 +  {
 +    QMutexLocker lock( &mutex );
 +    list.push_back( item );
 +  }
 +  endInsertRows();
 +}
 +
 +IItem::Pointer DirectoryModel::get( int idx ) const
 +{
 +    QMutexLocker lock( &mutex );
 +    return list[idx];
 +}
 +
 +QVariant DirectoryModel::getName( int idx ) const
 +{
 +    QMutexLocker lock( &mutex );
 +    return QString::fromStdString( list[idx]->filename() );
 +}
 +
 +QVariant DirectoryModel::getIcon( int idx ) const
 +{
 +    QMutexLocker lock( &mutex );
 +    auto item = list[idx];
 +    if ( item->type() == IItem::FileType::Directory )
 +        return QFileIconProvider().icon( QFileIconProvider::Folder );
 +    else
 +        return QFileIconProvider().icon( QFileIconProvider::File );
 +}
 +
 +void DirectoryModel::clear()
 +{
 +    beginRemoveRows( QModelIndex(), 0, rowCount() - 1 );
 +    list.clear();
 +    endRemoveRows();
 +}
 +
 +int DirectoryModel::rowCount( const QModelIndex & ) const
 +{
 +    QMutexLocker lock( &mutex );
 +    return list.size();
 +}
 +
 +QVariant DirectoryModel::data( const QModelIndex &index, int role ) const
 +{
 +    if ( role == Qt::DisplayRole )
 +        return getName( index.row() );
 +    else if ( role == Qt::DecorationRole )
 +        return getIcon( index.row() );
 +    return QVariant();
 +}
 +
 +#endif
 diff --git a/modules/gui/qt/components/open_panels.hpp b/modules/gui/qt/components/open_panels.hpp
 index d124a25..542a3fc 100644
 --- a/modules/gui/qt/components/open_panels.hpp
 +++ b/modules/gui/qt/components/open_panels.hpp
 @@ -38,11 +38,20 @@
  #include "ui/open_disk.h"
  #include "ui/open_net.h"
  #include "ui/open_capture.h"
 +#include "ui/open_cloud.h"

  #include <QFileDialog>
 +#include <QMutex>

  #include <limits.h>

 +#ifdef WITH_LIBCLOUDSTORAGE
 +  #include <ICloudStorage.h>
 +  #include <ICloudProvider.h>
 +  #include <IRequest.h>
 +  #include <IItem.h>
 +#endif
 +
  #define setSpinBoxFreq( spinbox ){ spinbox->setRange ( 0, INT_MAX ); \
      spinbox->setAccelerated( true ); }

 @@ -230,4 +239,66 @@ private slots:
      void advancedDialog();
  };

 +#ifdef WITH_LIBCLOUDSTORAGE
 +class DirectoryModel : public QAbstractListModel
 +{
 +public:
 +    int rowCount( const QModelIndex& parent = QModelIndex() ) const override;
 +    QVariant data( const QModelIndex& index, int ) const override;
 +    void addItem( cloudstorage::IItem::Pointer item );
 +    cloudstorage::IItem::Pointer get( int ) const;
 +    QVariant getName( int ) const;
 +    QVariant getIcon( int ) const;
 +    void clear();
 +
 +private:
 +    mutable QMutex mutex;
 +    std::vector<cloudstorage::IItem::Pointer> list;
 +};</code></pre>
</blockquote>
<p>Given that <code>DirectoryModel::list</code> is protected with a lock every time it is being accessed (besides in <code>DirectoryModel::clear</code>), is it guaranteed that the <em>indexes</em> (used to retrieve items) passed around actually reflects the current state of the <code>std::vector</code> (and will not be <em>out-of-bounds</em>)?</p>
<blockquote style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;color:#500050">
<pre><code> +
 +class CloudListView : public QListView
 +{
 +    Q_OBJECT
 +public:
 +    using QListView::QListView;
 +
 +    void selectionChanged( const QItemSelection &selected, const QItemSelection &deselected ) override;
 +
 +signals:
 +    void selectedItemChanged();
 +};
 +
 +class CloudOpenPanel : public OpenPanel
 +{
 +    Q_OBJECT
 +public:
 +    CloudOpenPanel( QWidget *, intf_thread_t * );
 +    ~CloudOpenPanel();
 +
 +    void save();
 +    void clear();
 +
 +protected:
 +    void keyPressEvent( QKeyEvent* ) override;
 +
 +private:
 +    Ui::OpenCloud ui;
 +    DirectoryModel directoryModel;
 +    cloudstorage::ICloudStorage::Pointer cloudStorage;
 +    cloudstorage::ICloudProvider::Pointer currentProvider;
 +    cloudstorage::IItem::Pointer currentDirectory;
 +    cloudstorage::ICloudProvider::ListDirectoryRequest::Pointer listDirectoryRequest;
 +    cloudstorage::ICloudProvider::GetItemDataRequest::Pointer itemDataRequest;
 +    std::vector<cloudstorage::IItem::Pointer> directoryStack;
 +
 +    void buttonClicked();
 +    void itemClicked( const QModelIndex& );
 +    void selectionChanged();
 +    void listDirectory();
 +
 +public slots:
 +    void updateMRL();
 +};
 +#endif
 +
  #endif
 diff --git a/modules/gui/qt/dialogs/open.cpp b/modules/gui/qt/dialogs/open.cpp
 index 224cfa5..2625393 100644
 --- a/modules/gui/qt/dialogs/open.cpp
 +++ b/modules/gui/qt/dialogs/open.cpp
 @@ -84,6 +84,9 @@ OpenDialog::OpenDialog( QWidget *parent,
      discOpenPanel    = new DiscOpenPanel( this, p_intf );
      netOpenPanel     = new NetOpenPanel( this, p_intf );
      captureOpenPanel = new CaptureOpenPanel( this, p_intf );
 +#ifdef WITH_LIBCLOUDSTORAGE
 +    cloudOpenPanel   = new CloudOpenPanel( this, p_intf );
 +#endif

      /* Insert the tabs */
      ui.Tab->insertTab( OPEN_FILE_TAB, fileOpenPanel, QIcon( ":/type/file-asym" ),
 @@ -94,6 +97,10 @@ OpenDialog::OpenDialog( QWidget *parent,
                         qtr( "&Network" ) );
      ui.Tab->insertTab( OPEN_CAPTURE_TAB, captureOpenPanel,
                         QIcon( ":/type/capture-card" ), qtr( "Capture &Device" ) );
 +#ifdef WITH_LIBCLOUDSTORAGE
 +    ui.Tab->insertTab( OPEN_CLOUD_TAB, cloudOpenPanel, QIcon( ":/type/cloud" ),
 +                       qtr( "&Cloud" ) );
 +#endif

      /* Hide the Slave input widgets */
      ui.slaveLabel->hide();
 @@ -141,6 +148,10 @@ OpenDialog::OpenDialog( QWidget *parent,
               this, updateMRL( const QStringList&, const QString& ) );
      CONNECT( captureOpenPanel, mrlUpdated( const QStringList&, const QString& ),
               this, updateMRL( const QStringList&, const QString& ) );
 +#ifdef WITH_LIBCLOUDSTORAGE
 +    CONNECT( cloudOpenPanel, mrlUpdated( const QStringList&, const QString& ),
 +             this, updateMRL( const QStringList&, const QString& ) );
 +#endif

      CONNECT( fileOpenPanel, methodChanged( const QString& ),
               this, newCachingMethod( const QString& ) );
 @@ -150,6 +161,10 @@ OpenDialog::OpenDialog( QWidget *parent,
               this, newCachingMethod( const QString& ) );
      CONNECT( captureOpenPanel, methodChanged( const QString& ),
               this, newCachingMethod( const QString& ) );
 +#ifdef WITH_LIBCLOUDSTORAGE
 +    CONNECT( cloudOpenPanel, methodChanged( const QString& ),
 +             this, newCachingMethod( const QString& ) );
 +#endif

      /* Advanced frame Connects */
      CONNECT( ui.slaveCheckbox, toggled( bool ), this, updateMRL() );
 diff --git a/modules/gui/qt/dialogs/open.hpp b/modules/gui/qt/dialogs/open.hpp
 index 0ce6f81..f292760 100644
 --- a/modules/gui/qt/dialogs/open.hpp
 +++ b/modules/gui/qt/dialogs/open.hpp
 @@ -40,6 +40,7 @@ enum {
      OPEN_DISC_TAB,
      OPEN_NETWORK_TAB,
      OPEN_CAPTURE_TAB,
 +    OPEN_CLOUD_TAB,
      OPEN_TAB_MAX
  };

 @@ -98,6 +99,9 @@ private:
      NetOpenPanel *netOpenPanel;
      DiscOpenPanel *discOpenPanel;
      CaptureOpenPanel *captureOpenPanel;
 +#ifdef WITH_LIBCLOUDSTORAGE
 +    CloudOpenPanel *cloudOpenPanel;
 +#endif

      int i_action_flag;
      bool b_pl;
 diff --git a/modules/gui/qt/dialogs_provider.cpp b/modules/gui/qt/dialogs_provider.cpp
 index 5951b65..ff1fc98 100644
 --- a/modules/gui/qt/dialogs_provider.cpp
 +++ b/modules/gui/qt/dialogs_provider.cpp
 @@ -139,6 +139,8 @@ void DialogsProvider::customEvent( QEvent *event )
          case INTF_DIALOG_SAT:
          case INTF_DIALOG_CAPTURE:
              openCaptureDialog(); break;
 +        case INTF_DIALOG_CLOUD:
 +            openCloudDialog(); break;
          case INTF_DIALOG_DIRECTORY:
              PLAppendDir(); break;
          case INTF_DIALOG_PLAYLIST:
 @@ -426,6 +428,11 @@ void DialogsProvider::openCaptureDialog()
      openDialog( OPEN_CAPTURE_TAB );
  }

 +void DialogsProvider::openCloudDialog()
 +{
 +    openDialog( OPEN_CLOUD_TAB );
 +}
 +
  /* Same as the open one, but force the enqueue */
  void DialogsProvider::PLAppendDialog( int tab )
  {
 diff --git a/modules/gui/qt/dialogs_provider.hpp b/modules/gui/qt/dialogs_provider.hpp
 index 01faa11..46bc89f 100644
 --- a/modules/gui/qt/dialogs_provider.hpp
 +++ b/modules/gui/qt/dialogs_provider.hpp
 @@ -153,6 +153,7 @@ public slots:
      void openUrlDialog();
      void openNetDialog();
      void openCaptureDialog();
 +    void openCloudDialog();

      void PLAppendDialog( int tab = OPEN_FILE_TAB );
      void MLAppendDialog( int tab = OPEN_FILE_TAB );
 diff --git a/modules/gui/qt/menus.cpp b/modules/gui/qt/menus.cpp
 index 8af0317..78563ff 100644
 --- a/modules/gui/qt/menus.cpp
 +++ b/modules/gui/qt/menus.cpp
 @@ -370,6 +370,10 @@ QMenu *VLCMenuBar::FileMenu( intf_thread_t *p_intf, QWidget *parent, MainInterfa
          ":/type/network", SLOT( openNetDialog() ), "Ctrl+N" );
      addDPStaticEntry( menu, qtr( "Open &Capture Device..." ),
          ":/type/capture-card", SLOT( openCaptureDialog() ), "Ctrl+C" );
 +#ifdef WITH_LIBCLOUDSTORAGE
 +    addDPStaticEntry( menu, qtr( "Open Cloud File..."),
 +        ":/type/cloud", SLOT( openCloudDialog() ) );
 +#endif

      addDPStaticEntry( menu, qtr( "Open &Location from clipboard" ),
                        NULL, SLOT( openUrlDialog() ), "Ctrl+V" );
 diff --git a/modules/gui/qt/pixmaps/types/type_cloud.png b/modules/gui/qt/pixmaps/types/type_cloud.png
 new file mode 100644
 index 0000000000000000000000000000000000000000..f68cb091452b5a315ee2d411ac7a836545c6142a
 GIT binary patch
 literal 252
 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf<Z~8yL>4nJ
 za0`PlBg3pY5<tOuo-U3d7QI(58uBqZN*w(t&&9b^_(I1EnFG8naZ~uVs886@9-ukL
 zY4L@VULq<QGg3C4Of!%EI4S&$&%gK8#)t2(4lJ-c#-u!F!ZhA>iLK#Jq|;m<2ykAL
 zw6Jhh=g)gIhxypN;{g@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/types/type_cloud.svgz b/modules/gui/qt/pixmaps/types/type_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@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/ui/open_cloud.ui b/modules/gui/qt/ui/open_cloud.ui
 new file mode 100644
 index 0000000..9844411
 --- /dev/null
 +++ b/modules/gui/qt/ui/open_cloud.ui
 @@ -0,0 +1,19 @@
 +<?xml version="1.0" encoding="UTF-8"?>
 +<ui version="4.0">
 + <class>OpenCloud</class>
 + <widget class="QWidget" name="OpenCloud">
 +  <property name="geometry">
 +   <rect>
 +    <x>0</x>
 +    <y>0</y>
 +    <width>400</width>
 +    <height>300</height>
 +   </rect>
 +  </property>
 +  <property name="windowTitle">
 +   <string>Form</string>
 +  </property>
 + </widget>
 + <resources/>
 + <connections/>
 +</ui>
 diff --git a/modules/gui/qt/vlc.qrc b/modules/gui/qt/vlc.qrc
 index fbf5bfd..30c4971 100644
 --- a/modules/gui/qt/vlc.qrc
 +++ b/modules/gui/qt/vlc.qrc
 @@ -90,6 +90,7 @@
          <file alias="stream">pixmaps/types/type_stream.png</file>
          <file alias="node">pixmaps/types/type_node.png</file>
          <file alias="playlist">pixmaps/types/type_playlist.png</file>
 +        <file alias="cloud">pixmaps/types/type_cloud.png</file>
      </qresource>
      <qresource prefix="/">
          <file alias="down_arrow">pixmaps/arrow_down_dark.png</file>
 -- 
 2.9.3

 _______________________________________________
 vlc-devel mailing list
 To unsubscribe or modify your subscription options:
 https://mailman.videolan.org/listinfo/vlc-devel</code></pre>
</blockquote>
</body>
</html>