[vlc-devel] [PATCH 2/2] Qt GUI: implemented cloud tab.

Filip Roséen filip at atch.se
Fri Oct 28 05:28:29 CEST 2016


Hi Paweł,

Thank you for rebasing the patches to make them easier to review and
maintain, very much appreciated!

On 2016-10-27 23:18, Paweł Wegner wrote:

> ---
>  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
> +    {
> +    }

See upcoming note regarding errors.

> +};
> +
> +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
> +    {
> +    }
> +

It somewhat worries me that the above seems to indicate that errors
are simply discarded (since no action is taking in
`CloudCallback::error`, nor `CloudListDirectoryCallback::error`).

You should handle potential errors, and if such occur (and is fatal
enough), display a message to the user using
`ErrorsDialog::addError`.

> +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();

The name of a *cloud-provider* is not guaranteed to be unique among
the settings in *VLC*, as such it is probably better to have a prefix
that denotes that the setting is related to *cloud-storage*.

> +        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() );

See previous comment regarding settings and potential name-collision.

> +    }
> +}
> +
> +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;
> +};

Given that `DirectoryModel::list` is protected with a lock every time
it is being accessed (besides in `DirectoryModel::clear`), is it
guaranteed that the *indexes* (used to retrieve items) passed around
actually reflects the current state of the `std::vector` (and will not
be *out-of-bounds*)?

> +
> +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 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/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 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/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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.videolan.org/pipermail/vlc-devel/attachments/20161028/7beb36f0/attachment.html>


More information about the vlc-devel mailing list