[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