[vlc-commits] [Git][videolan/vlc][master] 20 commits: qt: compare UUID of addons instead of pointer in ServiceDiscoveryModel
Steve Lhomme (@robUx4)
gitlab at videolan.org
Fri Dec 20 09:11:54 UTC 2024
Steve Lhomme pushed to branch master at VideoLAN / VLC
Commits:
49305c06 by Pierre Lamot at 2024-12-20T08:54:36+00:00
qt: compare UUID of addons instead of pointer in ServiceDiscoveryModel
addonChanged callback will change the pointer, but the item still represent the
same addon
- - - - -
1e3783f7 by Pierre Lamot at 2024-12-20T08:54:36+00:00
qt: use cache updateItem when addon changes in SDModel
- - - - -
53d6bd8d by Pierre Lamot at 2024-12-20T08:54:36+00:00
addons: mark addons_uuid_to_psz as VLC_MALLOC
- - - - -
98e9d4e4 by Pierre Lamot at 2024-12-20T08:54:36+00:00
qt: fix memory leak when creating ServiceDiscovery items
- - - - -
65a39a06 by Pierre Lamot at 2024-12-20T08:54:36+00:00
qt: ensure addon_entry_t are locked before accessing their members in ServiceDiscoveryModel
- - - - -
d7c536d1 by Pierre Lamot at 2024-12-20T08:54:36+00:00
qt: move private fields from ServiceDiscoveryModel to private class
- - - - -
73bdbffe by Pierre Lamot at 2024-12-20T08:54:36+00:00
qt: fix variable typo in ServiceDiscoveryModel
- - - - -
9657bd4d by Pierre Lamot at 2024-12-20T08:54:36+00:00
qt: allow filtering by ServiceDiscoveryModel by state or type
- - - - -
67cbed92 by Pierre Lamot at 2024-12-20T08:54:36+00:00
qt: explicitly gather from VLC or third party repository in ServiceDiscoveryModel
this allows requesting or refreshing the model explicilty
- - - - -
074fd4a1 by Pierre Lamot at 2024-12-20T08:54:36+00:00
qt: expose addon score without pre-scaling
views can choose their scale, maxScore is exposed to allow the conversion
- - - - -
dfd92283 by Pierre Lamot at 2024-12-20T08:54:36+00:00
qt: expose more roles in ServiceDiscovery model
SERVICE prefix is removed from the roletype as the model can be used for other
addons types
- - - - -
817f7051 by Pierre Lamot at 2024-12-20T08:54:36+00:00
qt: allow (un)installing addons using setData in ServiceDiscoveryModel
- - - - -
1169bb3f by Pierre Lamot at 2024-12-20T08:54:36+00:00
qt: expose method to get various representation of addon_state_t in ServiceDiscoveryModel
- - - - -
93e2cea5 by Pierre Lamot at 2024-12-20T08:54:36+00:00
qt: replace code generation with a lambda in plugins.cpp
- - - - -
f6831468 by Pierre Lamot at 2024-12-20T08:54:36+00:00
qt: don't inherit AddonItemDelegate from ExtensionItemDelegate
The data from their model are unrelated (extension_t and addon_entry_t), so
their model shouldn't inherit one from another.
instead a paint function is provided where the data to render is passed as arguments
- - - - -
dcc290de by Pierre Lamot at 2024-12-20T08:54:36+00:00
qt: allow initializing BaseModel from C++
methods are public in QQmlParserStatus
- - - - -
290b9467 by Pierre Lamot at 2024-12-20T08:54:36+00:00
qt: use service discovery model in Plugin.cpp
Thus avoiding needing two models to represent the same data
- - - - -
9a8eba6f by Pierre Lamot at 2024-12-20T08:54:36+00:00
qt: remove obsolete AddonsListModel and AddonsSortFilterProxyModel
- - - - -
7fc14a2f by Pierre Lamot at 2024-12-20T08:54:36+00:00
qt: remove obosolete AddonManager
- - - - -
346c18b7 by Pierre Lamot at 2024-12-20T08:54:36+00:00
qt: rename ServiceDiscoveryModel into AddonModel
ServiceDiscoveryModel also handle the other types of addons
- - - - -
14 changed files:
- include/vlc_addons.h
- modules/gui/qt/Makefile.am
- − modules/gui/qt/dialogs/plugins/addons_manager.cpp
- − modules/gui/qt/dialogs/plugins/addons_manager.hpp
- modules/gui/qt/dialogs/plugins/plugins.cpp
- modules/gui/qt/dialogs/plugins/plugins.hpp
- modules/gui/qt/maininterface/mainui.cpp
- modules/gui/qt/meson.build
- modules/gui/qt/network/servicesdiscoverymodel.cpp → modules/gui/qt/network/addonsmodel.cpp
- modules/gui/qt/network/servicesdiscoverymodel.hpp → modules/gui/qt/network/addonsmodel.hpp
- modules/gui/qt/network/qml/ServicesManage.qml
- modules/gui/qt/qt.cpp
- modules/gui/qt/util/base_model.hpp
- po/POTFILES.in
Changes:
=====================================
include/vlc_addons.h
=====================================
@@ -200,7 +200,7 @@ static inline bool addons_uuid_read( const char *psz_uuid, addon_uuid_t *p_uuid
return true;
}
-static inline char * addons_uuid_to_psz( const addon_uuid_t * p_uuid )
+VLC_MALLOC static inline char * addons_uuid_to_psz( const addon_uuid_t * p_uuid )
{
char *psz = (char*) calloc( ADDON_UUID_PSZ_SIZE + 1 , sizeof(char) );
if ( psz )
=====================================
modules/gui/qt/Makefile.am
=====================================
@@ -128,7 +128,6 @@ libqt_plugin_la_SOURCES = \
dialogs/open/open.cpp dialogs/open/open.hpp \
dialogs/open/open_panels.cpp dialogs/open/open_panels.hpp \
dialogs/open/openurl.cpp dialogs/open/openurl.hpp \
- dialogs/plugins/addons_manager.cpp dialogs/plugins/addons_manager.hpp \
dialogs/plugins/plugins.cpp dialogs/plugins/plugins.hpp \
dialogs/podcast/podcast_configuration.cpp \
dialogs/podcast/podcast_configuration.hpp \
@@ -251,6 +250,8 @@ libqt_plugin_la_SOURCES = \
menus/qml_menu_wrapper.cpp \
menus/qml_menu_wrapper.hpp \
menus/menus.cpp menus/menus.hpp \
+ network/addonsmodel.cpp \
+ network/addonsmodel.hpp \
network/mediatreelistener.cpp \
network/mediatreelistener.hpp \
network/devicesourceprovider.cpp \
@@ -263,8 +264,6 @@ libqt_plugin_la_SOURCES = \
network/networksourcesmodel.hpp \
network/networkmediamodel.cpp \
network/networkmediamodel.hpp \
- network/servicesdiscoverymodel.cpp \
- network/servicesdiscoverymodel.hpp \
network/standardpathmodel.cpp \
network/standardpathmodel.hpp \
network/vlcmediasourcewrapper.hpp \
@@ -402,7 +401,6 @@ nodist_libqt_plugin_la_SOURCES = \
dialogs/open/open.moc.cpp \
dialogs/open/open_panels.moc.cpp \
dialogs/open/openurl.moc.cpp \
- dialogs/plugins/addons_manager.moc.cpp \
dialogs/plugins/plugins.moc.cpp \
dialogs/podcast/podcast_configuration.moc.cpp \
dialogs/preferences/complete_preferences.moc.cpp \
@@ -454,12 +452,12 @@ nodist_libqt_plugin_la_SOURCES = \
menus/custom_menus.moc.cpp \
menus/qml_menu_wrapper.moc.cpp \
menus/menus.moc.cpp \
+ network/addonsmodel.moc.cpp \
network/devicesourceprovider.moc.cpp \
network/networkdevicemodel.moc.cpp \
network/networkbasemodel.moc.cpp \
network/networksourcesmodel.moc.cpp \
network/networkmediamodel.moc.cpp \
- network/servicesdiscoverymodel.moc.cpp \
network/standardpathmodel.moc.cpp \
style/colorcontext.moc.cpp \
style/systempalette.moc.cpp \
=====================================
modules/gui/qt/dialogs/plugins/addons_manager.cpp deleted
=====================================
@@ -1,143 +0,0 @@
-/*****************************************************************************
- * addons_manager.cpp: Addons manager for Qt
- ****************************************************************************
- * Copyright (C) 2013 VideoLAN and authors
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
- *****************************************************************************/
-
-#include "addons_manager.hpp"
-#include <QApplication>
-
-const QEvent::Type AddonManagerEvent::AddedEvent =
- (QEvent::Type)QEvent::registerEventType();
-const QEvent::Type AddonManagerEvent::ChangedEvent =
- (QEvent::Type)QEvent::registerEventType();
-const QEvent::Type AddonManagerEvent::DiscoveryEndedEvent =
- (QEvent::Type)QEvent::registerEventType();
-
-AddonsManager::AddonsManager( qt_intf_t *p_intf )
-{
- struct addons_manager_owner owner =
- {
- this,
- addonFoundCallback,
- addonsDiscoveryEndedCallback,
- addonChangedCallback,
- };
-
- p_manager = addons_manager_New( VLC_OBJECT(p_intf), &owner );
-}
-
-AddonsManager::~AddonsManager()
-{
- if ( p_manager )
- addons_manager_Delete( p_manager );
-}
-
-void AddonsManager::findNewAddons()
-{
- addons_manager_Gather( p_manager, "repo://" );
-}
-
-void AddonsManager::findDesignatedAddon( QString uri )
-{
- addons_manager_Gather( p_manager, qtu(uri) );
-}
-
-void AddonsManager::findInstalled()
-{
- addons_manager_LoadCatalog( p_manager );
-}
-
-void AddonsManager::install( QByteArray id )
-{
- Q_ASSERT( id.size() == sizeof(addon_uuid_t) );
- addon_uuid_t addonid;
- memcpy( &addonid, id.constData(), sizeof(addon_uuid_t) );
- addons_manager_Install( p_manager, addonid );
-}
-
-void AddonsManager::remove( QByteArray id )
-{
- Q_ASSERT( id.size() == sizeof(addon_uuid_t) );
- addon_uuid_t addonid;
- memcpy( &addonid, id.constData(), sizeof(addon_uuid_t) );
- addons_manager_Remove( p_manager, addonid );
-}
-
-QString AddonsManager::getAddonType( int i_type )
-{
- switch ( i_type )
- {
- case ADDON_SKIN2:
- return qtr( "Skins" );
- case ADDON_PLAYLIST_PARSER:
- return qtr("Playlist parsers");
- case ADDON_SERVICE_DISCOVERY:
- return qtr("Service Discovery");
- case ADDON_INTERFACE:
- return qtr("Interfaces");
- case ADDON_META:
- return qtr("Art and meta fetchers");
- case ADDON_EXTENSION:
- return qtr("Extensions");
- default:
- return qtr("Unknown");
- }
-}
-
-void AddonsManager::addonFoundCallback( addons_manager_t *manager,
- addon_entry_t *entry )
-{
- AddonsManager *me = (AddonsManager *) manager->owner.sys;
- QEvent *ev = new AddonManagerEvent( AddonManagerEvent::AddedEvent,
- entry );
- QApplication::postEvent( me, ev );
-}
-
-void AddonsManager::addonsDiscoveryEndedCallback( addons_manager_t *manager )
-{
- AddonsManager *me = (AddonsManager *) manager->owner.sys;
- QEvent *ev = new QEvent( AddonManagerEvent::DiscoveryEndedEvent );
- QApplication::postEvent( me, ev );
-}
-
-void AddonsManager::addonChangedCallback( addons_manager_t *manager,
- addon_entry_t *entry )
-{
- AddonsManager *me = (AddonsManager *) manager->owner.sys;
- QEvent *ev = new AddonManagerEvent( AddonManagerEvent::ChangedEvent,
- entry );
- QApplication::postEvent( me, ev );
-}
-
-void AddonsManager::customEvent( QEvent *event )
-{
- if ( event->type() == AddonManagerEvent::AddedEvent )
- {
- AddonManagerEvent *ev = static_cast<AddonManagerEvent *>(event);
- emit addonAdded( ev->entry() );
- }
- else if ( event->type() == AddonManagerEvent::ChangedEvent )
- {
- AddonManagerEvent *ev = static_cast<AddonManagerEvent *>(event);
- emit addonChanged( ev->entry() );
- }
- else if ( event->type() == AddonManagerEvent::DiscoveryEndedEvent )
- {
- emit discoveryEnded();
- }
-}
=====================================
modules/gui/qt/dialogs/plugins/addons_manager.hpp deleted
=====================================
@@ -1,90 +0,0 @@
-/*****************************************************************************
- * addons_manager.hpp: Addons manager for Qt
- ****************************************************************************
- * Copyright (C) 2013 VideoLAN and authors
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
- *****************************************************************************/
-
-#ifndef ADDONS_MANAGER_HPP
-#define ADDONS_MANAGER_HPP
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include "qt.hpp"
-#include "util/singleton.hpp"
-
-#include <vlc_addons.h>
-
-#include <QObject>
-#include <QEvent>
-
-
-class AddonManagerEvent : public QEvent
-{
-public:
- static const QEvent::Type AddedEvent;
- static const QEvent::Type ChangedEvent;
- static const QEvent::Type DiscoveryEndedEvent;
-
- AddonManagerEvent( QEvent::Type type, addon_entry_t *_p_entry )
- : QEvent( type ), p_entry( _p_entry )
- {
- addon_entry_Hold( p_entry );
- }
- virtual ~AddonManagerEvent()
- {
- addon_entry_Release( p_entry );
- }
-
- addon_entry_t *entry() const { return p_entry; }
-
-private:
- addon_entry_t *p_entry;
-};
-
-class AddonsManager : public QObject, public Singleton<AddonsManager>
-{
- Q_OBJECT
- friend class Singleton<AddonsManager>;
-
-public:
- AddonsManager( qt_intf_t * );
- virtual ~AddonsManager();
- static void addonFoundCallback( addons_manager_t *, addon_entry_t * );
- static void addonsDiscoveryEndedCallback( addons_manager_t * );
- static void addonChangedCallback( addons_manager_t *, addon_entry_t * );
- void customEvent( QEvent * );
- void install( QByteArray id );
- void remove( QByteArray id );
- static QString getAddonType( int );
-
-signals:
- void addonAdded( addon_entry_t * );
- void addonChanged( const addon_entry_t * );
- void discoveryEnded();
-
-public slots:
- void findNewAddons();
- void findDesignatedAddon( QString uri );
- void findInstalled();
-
-private:
- addons_manager_t* p_manager;
-};
-
-#endif // ADDONS_MANAGER_HPP
=====================================
modules/gui/qt/dialogs/plugins/plugins.cpp
=====================================
@@ -29,7 +29,7 @@
#include "widgets/native/searchlineedit.hpp"
#include "dialogs/extensions/extensions_manager.hpp"
-#include "addons_manager.hpp"
+#include "network/addonsmodel.hpp"
#include "widgets/native/animators.hpp"
#include "util/imagehelper.hpp"
@@ -66,6 +66,7 @@
#include <QStackedWidget>
#include <QPainterPath>
#include <QSignalMapper>
+#include <QtQml/QQmlFile>
//match the image source (width/height)
#define SCORE_ICON_WIDTH_SCALE 4
@@ -302,28 +303,6 @@ void ExtensionTab::moreInformation()
dlg.exec();
}
-static QIcon iconFromCategory( int type )
-{
- switch( type )
- {
- case ADDON_EXTENSION:
- return QIcon( ":/addons/addon_yellow.svg" );
- case ADDON_PLAYLIST_PARSER:
- return QIcon( ":/addons/addon_green.svg" );
- case ADDON_SERVICE_DISCOVERY:
- return QIcon( ":/addons/addon_red.svg" );
- case ADDON_SKIN2:
- return QIcon( ":/addons/addon_cyan.svg" );
- case ADDON_INTERFACE:
- return QIcon( ":/addons/addon_blue.svg" );
- case ADDON_META:
- return QIcon( ":/addons/addon_magenta.svg" );
- default:
- return QIcon( ":/addons/default.svg" );
- }
- vlc_assert_unreachable();
-}
-
/* Add-ons tab */
AddonsTab::AddonsTab( qt_intf_t *p_intf_ ) : QVLCFrame( p_intf_ )
{
@@ -354,49 +333,53 @@ AddonsTab::AddonsTab( qt_intf_t *p_intf_ ) : QVLCFrame( p_intf_ )
leftPane->layout()->addWidget( searchInput );
leftPane->layout()->addItem( new QSpacerItem( 0, 10 ) );
- QToolButton * button;
signalMapper = new QSignalMapper();
-#define ADD_CATEGORY( label, ltooltip, numb ) \
- button = new QToolButton( this );\
- button->setIcon( iconFromCategory( numb ) ); \
- button->setText( label );\
- button->setToolTip( ltooltip );\
- button->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );\
- button->setIconSize( QSize( 32, 32 ) );\
- button->setSizePolicy(QSizePolicy::MinimumExpanding,QSizePolicy::Maximum) ;\
- button->setMinimumSize( 32, 32 );\
- button->setAutoRaise( true );\
- button->setCheckable( true );\
- if ( numb == -1 ) button->setChecked( true );\
- button->setAutoExclusive( true );\
- connect( button, &QToolButton::clicked, signalMapper, QOverload<>::of(&QSignalMapper::map) );\
- signalMapper->setMapping( button, numb );\
- leftPane->layout()->addWidget( button );
-
- ADD_CATEGORY( qtr("All"), qtr("Interface Settings"),
- -1 );
- ADD_CATEGORY( qtr("Skins"),
+
+ auto addCategory = [this, leftPane]( const QString& label, const QString& ltooltip, AddonsModel::Type type) {
+ auto button = new QToolButton( this );
+
+ QString iconpath = QQmlFile::urlToLocalFileOrQrc(AddonsModel::getIconForType( type ));
+ button->setIcon( QIcon{ iconpath } );
+ button->setText( label );
+ button->setToolTip( ltooltip );
+ button->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
+ button->setIconSize( QSize( 32, 32 ) );
+ button->setSizePolicy(QSizePolicy::MinimumExpanding,QSizePolicy::Maximum) ;
+ button->setMinimumSize( 32, 32 );
+ button->setAutoRaise( true );
+ button->setCheckable( true );
+ if ( type == AddonsModel::Type::TYPE_NONE )
+ button->setChecked( true );
+ button->setAutoExclusive( true );
+ connect( button, &QToolButton::clicked, signalMapper, QOverload<>::of(&QSignalMapper::map) );
+ signalMapper->setMapping( button, static_cast<int>(type) );
+ leftPane->layout()->addWidget( button );
+ };
+
+ addCategory( qtr("All"), qtr("Interface Settings"),
+ AddonsModel::Type::TYPE_NONE );
+ addCategory( qtr("Skins"),
qtr( "Skins customize player's appearance."
" You can activate them through preferences." ),
- ADDON_SKIN2 );
- ADD_CATEGORY( qtr("Playlist parsers"),
+ AddonsModel::Type::TYPE_SKIN2 );
+ addCategory( qtr("Playlist parsers"),
qtr( "Playlist parsers add new capabilities to read"
" internet streams or extract meta data." ),
- ADDON_PLAYLIST_PARSER );
- ADD_CATEGORY( qtr("Service Discovery"),
+ AddonsModel::Type::TYPE_PLAYLIST_PARSER );
+ addCategory( qtr("Service Discovery"),
qtr( "Service discoveries adds new sources to your playlist"
" such as web radios, video websites, ..." ),
- ADDON_SERVICE_DISCOVERY );
- ADD_CATEGORY( qtr("Interfaces"),
+ AddonsModel::Type::TYPE_SERVICE_DISCOVERY );
+ addCategory( qtr("Interfaces"),
"",
- ADDON_INTERFACE );
- ADD_CATEGORY( qtr("Art and meta fetchers"),
+ AddonsModel::Type::TYPE_INTERFACE );
+ addCategory( qtr("Art and meta fetchers"),
qtr( "Retrieves extra info and art for playlist items" ),
- ADDON_META );
- ADD_CATEGORY( qtr("Extensions"),
+ AddonsModel::Type::TYPE_META );
+ addCategory( qtr("Extensions"),
qtr( "Extensions brings various enhancements."
" Check descriptions for more details" ),
- ADDON_EXTENSION );
+ AddonsModel::Type::TYPE_EXTENSION );
// Right Pane
rightPane->layout()->setContentsMargins(0, 0, 0, 0);
@@ -420,20 +403,44 @@ AddonsTab::AddonsTab( qt_intf_t *p_intf_ ) : QVLCFrame( p_intf_ )
switchStack->insertWidget( WITHONLINEADDONS, installedOnlyBox );
connect( installedOnlyBox, &QCheckBox::stateChanged, this, &AddonsTab::installChecked );
+ // Model
+ m_model = std::make_unique<AddonsModel>( );
+ //model expect QMLlike behavior
+ m_model->classBegin();
+ m_model->setCtx(p_intf->p_mi);
+ connect( signalMapper, &QSignalMapper::mappedInt,
+ m_model.get(), [model = m_model.get()](int mapped){
+ model->setTypeFilter(static_cast<AddonsModel::Type>(mapped));
+ });
+
+ connect( searchInput, &SearchLineEdit::textChanged,
+ m_model.get(), &AddonsModel::setSearchPattern );
+
+
+ // Update button
QPushButton *reposyncButton = new QPushButton( QIcon( ":/menu/update.svg" ),
qtr("Find more addons online") );
reposyncButton->setSizePolicy( QSizePolicy::Ignored, QSizePolicy::Preferred );
switchStack->insertWidget( ONLYLOCALADDONS, reposyncButton );
switchStack->setCurrentIndex( ONLYLOCALADDONS );
connect( reposyncButton, &QPushButton::clicked, this, &AddonsTab::reposync );
+ connect(
+ m_model.get(), &AddonsModel::loadingChanged,
+ this, [this]() {
+ if ( !m_model->loading()) {
+ spinnerAnimation->stop();
+ addonsView->viewport()->update();
+ }
+ });
leftPane->layout()->addItem( new QSpacerItem( 0, 0, QSizePolicy::Maximum, QSizePolicy::Expanding ) );
// Main View
- AddonsManager *AM = AddonsManager::getInstance( p_intf );
// ListView
addonsView = new QListView( this );
+ addonsView->setModel(m_model.get());
+
connect( addonsView, &QListView::activated, this, &AddonsTab::moreInformation );
layout->addWidget( addonsView );
@@ -453,30 +460,9 @@ AddonsTab::AddonsTab( qt_intf_t *p_intf_ ) : QVLCFrame( p_intf_ )
addonsView->setDropIndicatorShown( true );
addonsView->setDragDropMode( QAbstractItemView::DropOnly );
- // Model
- AddonsListModel *model = new AddonsListModel( AM, addonsView );
- addonsModel = new AddonsSortFilterProxyModel( addonsView );
- addonsModel->setDynamicSortFilter( true );
- addonsModel->setFilterCaseSensitivity( Qt::CaseInsensitive );
- addonsModel->setSortRole( Qt::DisplayRole );
- addonsModel->sort( 0, Qt::AscendingOrder );
- addonsModel->setSourceModel( model );
- addonsModel->setFilterRole( Qt::DisplayRole );
- addonsView->setModel( addonsModel );
-
- connect( signalMapper, &QSignalMapper::mappedInt,
- addonsModel, &AddonsSortFilterProxyModel::setTypeFilter );
-
- connect( searchInput, &SearchLineEdit::textChanged,
- addonsModel, &AddonsSortFilterProxyModel::setFilterFixedString );
-
connect( addonsView->selectionModel(), &QItemSelectionModel::currentChanged,
addonsView, QOverload<const QModelIndex&>::of(&QListView::edit) );
- connect( AM, SIGNAL(addonAdded( addon_entry_t * )),
- model, SLOT(addonAdded( addon_entry_t * )) );
- connect( AM, SIGNAL(addonChanged( const addon_entry_t * )),
- model, SLOT(addonChanged( const addon_entry_t * )) );
QList<QString> frames;
frames << ":/misc/wait1.svg";
@@ -487,6 +473,9 @@ AddonsTab::AddonsTab( qt_intf_t *p_intf_ ) : QVLCFrame( p_intf_ )
connect( spinnerAnimation, &PixmapAnimator::pixmapReady,
addonsView->viewport(), QOverload<>::of(&QWidget::update) );
addonsView->viewport()->installEventFilter( this );
+
+ //model expect QMLlike behavior
+ m_model->componentComplete();
}
AddonsTab::~AddonsTab()
@@ -528,7 +517,7 @@ bool AddonsTab::eventFilter( QObject *obj, QEvent *event )
point -= QPoint( textsize.width() / 2, -spinner.height() );
painter.drawText( point, text );
}
- else if ( addonsModel->rowCount() == 0 )
+ else if ( m_model->rowCount() == 0 )
{
QWidget *viewport = qobject_cast<QWidget *>( obj );
if ( !viewport ) break;
@@ -540,14 +529,6 @@ bool AddonsTab::eventFilter( QObject *obj, QEvent *event )
painter.drawText( point, text );
}
break;
- case QEvent::Show:
- if ( !b_localdone && addonsView->model()->rowCount() < 1 )
- {
- b_localdone = true;
- AddonsManager *AM = AddonsManager::getInstance( p_intf );
- AM->findInstalled();
- }
- break;
case QEvent::DragEnter:
{
QDragEnterEvent *dragEvent = static_cast<QDragEnterEvent *>(event);
@@ -578,8 +559,7 @@ bool AddonsTab::eventFilter( QObject *obj, QEvent *event )
return false;
if ( dropEvent->mimeData()->urls().count() )
{
- AddonsManager *AM = AddonsManager::getInstance( p_intf );
- AM->findDesignatedAddon( dropEvent->mimeData()->urls().first().toString() );
+ m_model->loadFromExternalRepository(dropEvent->mimeData()->urls().first());
dropEvent->acceptProposedAction();
}
return true;
@@ -603,9 +583,9 @@ void AddonsTab::moreInformation()
void AddonsTab::installChecked( int i )
{
if ( i == Qt::Checked )
- addonsModel->setStatusFilter( ADDON_INSTALLED );
+ m_model->setStateFilter( AddonsModel::State::STATE_INSTALLED );
else
- addonsModel->setStatusFilter( 0 );
+ m_model->setStateFilter( AddonsModel::State::STATE_NONE );
}
void AddonsTab::reposync()
@@ -614,11 +594,10 @@ void AddonsTab::reposync()
if ( tab )
{
tab->setCurrentIndex( WITHONLINEADDONS );
- AddonsManager *AM = AddonsManager::getInstance( p_intf );
- connect( AM, &AddonsManager::discoveryEnded, spinnerAnimation, &PixmapAnimator::stop );
- connect( AM, &AddonsManager::discoveryEnded, addonsView->viewport(), QOverload<>::of(&QWidget::update) );
+
spinnerAnimation->start();
- AM->findNewAddons();
+
+ m_model->loadFromDefaultRepository();
}
}
@@ -758,300 +737,22 @@ QModelIndex ExtensionListModel::index( int row, int column,
return createIndex( row, 0, extensions.at( row ) );
}
-AddonsListModel::Addon::Addon( addon_entry_t *p_entry_ )
-{
- p_entry = p_entry_;
- addon_entry_Hold( p_entry );
-}
-
-AddonsListModel::Addon::~Addon()
-{
- addon_entry_Release( p_entry );
-}
-
-bool AddonsListModel::Addon::operator==( const Addon & other ) const
-{
- //return data( IDRole ) == other.data( IDRole );
- return p_entry == other.p_entry;
-}
-
-bool AddonsListModel::Addon::operator==( const addon_entry_t * p_other ) const
-{
- return p_entry == p_other;
-}
-
-QVariant AddonsListModel::Addon::data( int role ) const
-{
- QVariant returnval;
-
- vlc_mutex_lock( &p_entry->lock );
- switch( role )
- {
- case Qt::DisplayRole:
- {
- returnval = qfu( p_entry->psz_name );
- break;
- }
- case Qt::DecorationRole:
- if ( p_entry->psz_image_data )
- {
- QPixmap pixmap;
- pixmap.loadFromData( QByteArray::fromBase64( QByteArray( p_entry->psz_image_data ) ),
- 0,
- Qt::AutoColor
- );
- returnval = pixmap;
- }
- else if ( p_entry->e_flags & ADDON_BROKEN )
- returnval = QPixmap( ":/addons/addon_broken.svg" );
- else
- returnval = QPixmap( ":/addons/addon_default.svg" );
- break;
- case Qt::ToolTipRole:
- {
- if ( !( p_entry->e_flags & ADDON_MANAGEABLE ) )
- {
- returnval = qtr("This addon has been installed manually. VLC can't manage it by itself.");
- }
- break;
- }
- case SummaryRole:
- returnval = qfu( p_entry->psz_summary );
- break;
- case DescriptionRole:
- returnval = qfu( p_entry->psz_description );
- break;
- case TypeRole:
- returnval = QVariant( (int) p_entry->e_type );
- break;
- case UUIDRole:
- returnval = QByteArray( (const char *) p_entry->uuid, (int) sizeof( addon_uuid_t ) );
- break;
- case FlagsRole:
- returnval = QVariant( (int) p_entry->e_flags );
- break;
- case StateRole:
- returnval = QVariant( (int) p_entry->e_state );
- break;
- case DownloadsCountRole:
- returnval = QVariant( (double) p_entry->i_downloads );
- break;
- case ScoreRole:
- returnval = QVariant( (int) p_entry->i_score );
- break;
- case VersionRole:
- returnval = QVariant( qfu(p_entry->psz_version) );
- break;
- case AuthorRole:
- returnval = qfu( p_entry->psz_author );
- break;
- case LinkRole:
- returnval = qfu( p_entry->psz_source_uri );
- break;
- case FilenameRole:
- {
- QList<QString> list;
- addon_file_t *p_file;
- ARRAY_FOREACH( p_file, p_entry->files )
- list << qfu( p_file->psz_filename );
- returnval = QVariant( list );
- break;
- }
- default:
- break;
- }
- vlc_mutex_unlock( &p_entry->lock );
-
- return returnval;
-}
-
-AddonsListModel::AddonsListModel( AddonsManager *AM_, QObject *parent )
- :ExtensionListModel( parent ), AM( AM_ )
-{
-
-}
-
-AddonsListModel::~AddonsListModel()
-{
- qDeleteAll( addons );
-}
-
-void AddonsListModel::addonAdded( addon_entry_t *p_entry )
-{
- beginInsertRows( QModelIndex(), addons.count(), addons.count() );
- addons << new Addon( p_entry );
- insertRow( addons.count() - 1 );
- endInsertRows();
-}
-
-void AddonsListModel::addonChanged( const addon_entry_t *p_entry )
-{
- int row = 0;
- foreach ( const Addon *addon, addons )
- {
- if ( *addon == p_entry )
- {
- emit dataChanged( index( row, 0 ), index( row, 0 ) );
- break;
- }
- row++;
- }
-}
-
-int AddonsListModel::rowCount( const QModelIndex & ) const
-{
- return addons.count();
-}
-
-Qt::ItemFlags AddonsListModel::flags( const QModelIndex &index ) const
-{
- Qt::ItemFlags i_flags = ExtensionListModel::flags( index );
- int i_state = data( index, StateRole ).toInt();
-
- if ( i_state == ADDON_UNINSTALLING || i_state == ADDON_INSTALLING )
- i_flags &= ~Qt::ItemIsEnabled;
-
- i_flags |= Qt::ItemIsEditable;
-
- return i_flags;
-}
-
-bool AddonsListModel::setData( const QModelIndex &index, const QVariant &value, int role )
-{
- /* We NEVER set values directly */
- if ( role == StateRole )
- {
- int i_value = value.toInt();
- if ( i_value == ADDON_INSTALLING )
- {
- AM->install( data( index, UUIDRole ).toByteArray() );
- }
- else if ( i_value == ADDON_UNINSTALLING )
- {
- AM->remove( data( index, UUIDRole ).toByteArray() );
- }
- }
- else if ( role == StateRole + 1 )
- {
- emit dataChanged( index, index );
- }
- return true;
-}
-
-QColor AddonsListModel::getColorByAddonType( int i_type )
-{
- QColor color;
- switch( i_type )
- {
- case ADDON_EXTENSION:
- color = QColor(0xDB, 0xC5, 0x40);
- break;
- case ADDON_PLAYLIST_PARSER:
- color = QColor(0x36, 0xBB, 0x59);
- break;
- case ADDON_SERVICE_DISCOVERY:
- color = QColor(0xDB, 0x52, 0x40);
- break;
- case ADDON_SKIN2:
- color = QColor(0x8B, 0xD6, 0xFC);
- break;
- case ADDON_INTERFACE:
- color = QColor(0x00, 0x13, 0x85);
- break;
- case ADDON_META:
- color = QColor(0xCD, 0x23, 0xBF);
- break;
- case ADDON_PLUGIN:
- case ADDON_UNKNOWN:
- case ADDON_OTHER:
- default:
- break;
- }
- return color;
-}
-
-QVariant AddonsListModel::data( const QModelIndex& index, int role ) const
-{
- if( !index.isValid() )
- return QVariant();
-
- return ((Addon *)index.internalPointer())->data( role );
-}
-
-QModelIndex AddonsListModel::index( int row, int column,
- const QModelIndex& ) const
-{
- if( column != 0 )
- return QModelIndex();
- if( row < 0 || row >= addons.count() )
- return QModelIndex();
-
- return createIndex( row, 0, addons.at( row ) );
-}
-
-/* Sort Filter */
-AddonsSortFilterProxyModel::AddonsSortFilterProxyModel( QObject *parent )
- : QSortFilterProxyModel( parent )
-{
- i_type_filter = -1;
- i_status_filter = 0;
-}
-
-void AddonsSortFilterProxyModel::setTypeFilter( int type )
-{
- i_type_filter = type;
- invalidateFilter();
-}
-
-void AddonsSortFilterProxyModel::setStatusFilter( int flags )
-{
- i_status_filter = flags;
- invalidateFilter();
-}
-
-bool AddonsSortFilterProxyModel::filterAcceptsRow( int source_row,
- const QModelIndex &source_parent ) const
-{
- if ( !QSortFilterProxyModel::filterAcceptsRow( source_row, source_parent ) )
- return false;
-
- QModelIndex item = sourceModel()->index( source_row, 0, source_parent );
-
- if ( i_type_filter > -1 &&
- item.data( AddonsListModel::TypeRole ).toInt() != i_type_filter )
- return false;
-
- if ( i_status_filter > 0 &&
- ( item.data( AddonsListModel::StateRole ).toInt() & i_status_filter ) != i_status_filter )
- return false;
-
- return true;
-}
-
-/* Extension List Widget Item */
-ExtensionItemDelegate::ExtensionItemDelegate( QObject *parent )
- : QStyledItemDelegate( parent )
-{
- margins = QMargins( 4, 4, 4, 4 );
-}
-
-ExtensionItemDelegate::~ExtensionItemDelegate()
-{
-}
-
-void ExtensionItemDelegate::paint( QPainter *painter,
- const QStyleOptionViewItem &option,
- const QModelIndex &index ) const
+static void commonPaint(
+ QPainter *painter,
+ const QStyleOptionViewItem &option,
+ const QMargins& margins,
+ const QPixmap& icon,
+ const QString& name,
+ const QString& description
+ )
{
QStyleOptionViewItem opt = option;
- initStyleOption( &opt, index );
// Draw background
if ( opt.state & QStyle::State_Selected )
painter->fillRect( opt.rect, opt.palette.highlight() );
// Icon
- QPixmap icon = index.data( Qt::DecorationRole ).value<QPixmap>();
if( !icon.isNull() )
{
painter->drawPixmap( opt.rect.left() + margins.left(),
@@ -1077,18 +778,50 @@ void ExtensionItemDelegate::paint( QPainter *painter,
- margins.right(),
- margins.bottom() - opt.fontMetrics.height() );
- painter->drawText( textrect, Qt::AlignLeft,
- index.data( Qt::DisplayRole ).toString() );
+ painter->drawText( textrect, Qt::AlignLeft, name);
font.setBold( false );
painter->setFont( font );
painter->drawText( textrect.translated( 0, option.fontMetrics.height() ),
Qt::AlignLeft,
- index.data( ExtensionListModel::SummaryRole ).toString() );
+ description );
painter->restore();
}
+/* Extension List Widget Item */
+ExtensionItemDelegate::ExtensionItemDelegate( QObject *parent )
+ : QStyledItemDelegate( parent )
+{
+ margins = QMargins( 4, 4, 4, 4 );
+}
+
+ExtensionItemDelegate::~ExtensionItemDelegate()
+{
+}
+
+void ExtensionItemDelegate::paint( QPainter *painter,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index ) const
+{
+ QStyleOptionViewItem opt = option;
+ initStyleOption( &opt, index );
+
+ /* Draw common base */
+ QString name = index.data( Qt::DisplayRole ).toString();
+ QPixmap icon = index.data( Qt::DecorationRole ).value<QPixmap>();
+ QString description = index.data( ExtensionListModel::SummaryRole ).toString();
+
+ commonPaint(
+ painter,
+ option,
+ margins,
+ icon,
+ name,
+ description
+ );
+}
+
QSize ExtensionItemDelegate::sizeHint( const QStyleOptionViewItem &option,
const QModelIndex &index ) const
{
@@ -1111,9 +844,8 @@ void ExtensionItemDelegate::initStyleOption( QStyleOptionViewItem *option,
}
AddonItemDelegate::AddonItemDelegate( QObject *parent )
- : ExtensionItemDelegate( parent )
- , animator( NULL )
- , progressbar( NULL )
+ : QStyledItemDelegate( parent )
+ , margins( 4,4,4,4 )
{ }
AddonItemDelegate::~AddonItemDelegate()
@@ -1126,11 +858,11 @@ void AddonItemDelegate::paint( QPainter *painter,
const QModelIndex &index ) const
{
QStyleOptionViewItem newopt = option;
- int i_state = index.data( AddonsListModel::StateRole ).toInt();
- int i_type = index.data( AddonsListModel::TypeRole ).toInt();
+ auto i_state = index.data( AddonsModel::Role::STATE ).value<AddonsModel::State>();
+ auto i_type = index.data( AddonsModel::Role::TYPE ).value<AddonsModel::Type>();
/* Draw Background gradient by addon type */
- QColor backgroundColor = AddonsListModel::getColorByAddonType( i_type );
+ QColor backgroundColor = AddonsModel::getColorForType( i_type );
if ( backgroundColor.isValid() )
{
@@ -1146,15 +878,27 @@ void AddonItemDelegate::paint( QPainter *painter,
}
/* Draw base info from parent */
- ExtensionItemDelegate::paint( painter, newopt, index );
-
initStyleOption( &newopt, index );
+ /* Draw common base */
+ QString name = index.data( Qt::DisplayRole ).toString();
+ QPixmap icon = index.data( Qt::DecorationRole ).value<QPixmap>();
+ QString description = index.data( AddonsModel::Role::SUMMARY ).toString();
+
+ commonPaint(
+ painter,
+ newopt,
+ margins,
+ icon,
+ name,
+ description
+ );
+
painter->save();
painter->setRenderHint( QPainter::TextAntialiasing );
/* Addon status */
- if ( i_state == ADDON_INSTALLED )
+ if ( i_state == AddonsModel::State::STATE_INSTALLED )
{
painter->save();
painter->setRenderHint( QPainter::Antialiasing );
@@ -1192,14 +936,14 @@ void AddonItemDelegate::paint( QPainter *painter,
textrect.translate( 0, newopt.fontMetrics.height() * 2 );
/* Version */
- QString version = index.data( AddonsListModel::VersionRole ).toString();
+ QString version = index.data( AddonsModel::Role::ADDON_VERSION).toString();
if ( !version.isEmpty() )
painter->drawText( textrect, Qt::AlignLeft, qtr("Version %1").arg( version ) );
textrect.translate( 0, newopt.fontMetrics.height() );
/* Score */
- int i_score = index.data( AddonsListModel::ScoreRole ).toInt();
+ int i_score = index.data( AddonsModel::Role::SCORE).toInt();
QPixmap scoreicon;
if ( i_score )
{
@@ -1207,7 +951,7 @@ void AddonItemDelegate::paint( QPainter *painter,
int i_scoreicon_width = i_scoreicon_height * SCORE_ICON_WIDTH_SCALE;
scoreicon = ImageHelper::loadSvgToPixmap( ":/addons/addon_score.svg",
i_scoreicon_width, i_scoreicon_height );
- int i_width = ( (float) i_score / ADDON_MAX_SCORE ) * i_scoreicon_width;
+ int i_width = ( (float) i_score / AddonsModel::getMaxScore() ) * i_scoreicon_width;
/* Erase the end (value) of our pixmap with a shadow */
QPainter erasepainter( &scoreicon );
erasepainter.setCompositionMode( QPainter::CompositionMode_SourceIn );
@@ -1219,7 +963,7 @@ void AddonItemDelegate::paint( QPainter *painter,
}
/* Downloads # */
- int i_downloads = index.data( AddonsListModel::DownloadsCountRole ).toInt();
+ int i_downloads = index.data( AddonsModel::DOWNLOAD_COUNT).toInt();
if ( i_downloads )
painter->drawText( textrect.translated( scoreicon.width() + margins.left(), 0 ),
Qt::AlignLeft, qtr("%1 downloads").arg( i_downloads ) );
@@ -1230,12 +974,14 @@ void AddonItemDelegate::paint( QPainter *painter,
{
if ( animator->isRunning() && animator->getIndex() == index )
{
- if ( i_state != ADDON_INSTALLING && i_state != ADDON_UNINSTALLING )
+ if ( i_state != AddonsModel::State::STATE_INSTALLING
+ && i_state != AddonsModel::State::STATE_UNINSTALLING )
animator->run( false );
}
/* Create our installation progress overlay */
- if ( i_state == ADDON_INSTALLING || i_state == ADDON_UNINSTALLING )
+ if ( i_state == AddonsModel::State::STATE_INSTALLING
+ || i_state == AddonsModel::State::STATE_UNINSTALLING )
{
painter->save();
painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
@@ -1269,6 +1015,15 @@ QSize AddonItemDelegate::sizeHint( const QStyleOptionViewItem &option,
return QSize();
}
+void AddonItemDelegate::initStyleOption( QStyleOptionViewItem *option,
+ const QModelIndex &index ) const
+{
+ QStyledItemDelegate::initStyleOption( option, index );
+ option->decorationSize = QSize( option->rect.height(), option->rect.height() );
+ option->decorationSize -= QSize( margins.left() + margins.right(),
+ margins.top() + margins.bottom() );
+}
+
QWidget *AddonItemDelegate::createEditor( QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index) const
@@ -1286,10 +1041,10 @@ QWidget *AddonItemDelegate::createEditor( QWidget *parent,
connect( infoButton, &QPushButton::clicked, this, &AddonItemDelegate::showInfo );
editorWidget->layout()->addWidget( infoButton );
- if ( ADDON_MANAGEABLE &
- index.data( AddonsListModel::FlagsRole ).toInt() )
+ if ( index.data( AddonsModel::Role::MANAGEABLE).toBool() )
{
- if ( index.data( AddonsListModel::StateRole ).toInt() == ADDON_INSTALLED )
+ if ( index.data( AddonsModel::Role::STATE ).value<AddonsModel::State>()
+ == AddonsModel::State::STATE_INSTALLED )
installButton = new QPushButton( QIcon( ":/menu/remove.svg" ),
qtr("&Uninstall"), parent );
else
@@ -1319,12 +1074,12 @@ void AddonItemDelegate::updateEditorGeometry( QWidget *editor,
void AddonItemDelegate::setModelData( QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index ) const
{
- model->setData( index, editor->property("Addon::state"), AddonsListModel::StateRole );
+ model->setData( index, editor->property("Addon::state"), AddonsModel::Role::STATE);
}
void AddonItemDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const
{
- editor->setProperty("Addon::state", index.data( AddonsListModel::StateRole ) );
+ editor->setProperty("Addon::state", index.data( AddonsModel::Role::STATE ) );
}
void AddonItemDelegate::setAnimator( DelegateAnimationHelper *animator_ )
@@ -1344,13 +1099,13 @@ void AddonItemDelegate::editButtonClicked()
{
QWidget *editor = qobject_cast<QWidget *>(sender()->parent());
if ( !editor ) return;
- int value = editor->property("Addon::state").toInt();
- if ( value == ADDON_INSTALLED )
+ auto value = editor->property("Addon::state").value<AddonsModel::State>();
+ if ( value == AddonsModel::State::STATE_INSTALLED )
/* uninstall */
- editor->setProperty("Addon::state", ADDON_UNINSTALLING );
+ editor->setProperty("Addon::state", QVariant::fromValue(AddonsModel::State::STATE_UNINSTALLING) );
else
/* install */
- editor->setProperty("Addon::state", ADDON_INSTALLING );
+ editor->setProperty("Addon::state", QVariant::fromValue(AddonsModel::State::STATE_INSTALLING) );
emit commitData( editor );
emit closeEditor( editor );
}
@@ -1449,7 +1204,7 @@ AddonInfoDialog::AddonInfoDialog( const QModelIndex &index,
setWindowModality( Qt::WindowModal );
// Window title
- setWindowTitle( qtr( "About" ) + " " + index.data(Qt::DisplayRole).toString() );
+ setWindowTitle( qtr( "About" ) + " " + index.data(AddonsModel::Role::NAME).toString() );
// Layout
QGridLayout *layout = new QGridLayout( this );
@@ -1465,7 +1220,7 @@ AddonInfoDialog::AddonInfoDialog( const QModelIndex &index,
layout->addWidget( iconLabel, 1, 0, 2, 1 );
// Title
- label = new QLabel( index.data(Qt::DisplayRole).toString(), this );
+ label = new QLabel( index.data(AddonsModel::Role::NAME).toString(), this );
QFont font = label->font();
font.setBold( true );
font.setPointSizeF( font.pointSizeF() * 1.3f );
@@ -1483,12 +1238,12 @@ AddonInfoDialog::AddonInfoDialog( const QModelIndex &index,
layout->addWidget( textContent, 1, 1, 4, -1 );
// Type
- QString type = AddonsManager::getAddonType( index.data(AddonsListModel::TypeRole).toInt() );
+ QString type = AddonsModel::getLabelForType( index.data(AddonsModel::Role::TYPE).value<AddonsModel::Type>() );
textContent->append( QString("<b>%1:</b> %2<br/>")
.arg( qtr("Type") ).arg( type ) );
// Version
- QString version = index.data(ExtensionListModel::VersionRole).toString();
+ QString version = index.data(AddonsModel::Role::ADDON_VERSION).toString();
if ( !version.isEmpty() )
{
textContent->append( QString("<b>%1:</b> %2<br/>")
@@ -1496,7 +1251,7 @@ AddonInfoDialog::AddonInfoDialog( const QModelIndex &index,
}
// Author
- QString author = index.data(ExtensionListModel::AuthorRole).toString();
+ QString author = index.data(AddonsModel::Role::AUTHOR).toString();
if ( !author.isEmpty() )
{
textContent->append( QString("<b>%1:</b> %2<br/>")
@@ -1505,10 +1260,10 @@ AddonInfoDialog::AddonInfoDialog( const QModelIndex &index,
// Summary
textContent->append( QString("%1<br/>\n")
- .arg( index.data(AddonsListModel::SummaryRole).toString() ) );
+ .arg( index.data(AddonsModel::Role::SUMMARY).toString() ) );
// Description
- QString description = index.data(AddonsListModel::DescriptionRole).toString();
+ QString description = index.data(AddonsModel::Role::DESCRIPTION).toString();
if ( !description.isEmpty() )
{
textContent->append( QString("<hr/>\n%1")
@@ -1516,7 +1271,7 @@ AddonInfoDialog::AddonInfoDialog( const QModelIndex &index,
}
// URL
- QString sourceUrl = index.data(ExtensionListModel::LinkRole).toString();
+ QString sourceUrl = index.data(AddonsModel::Role::LINK).toString();
if ( !sourceUrl.isEmpty() )
{
label = new QLabel( "<b>" + qtr( "Website" ) + ":</b>", this );
@@ -1528,7 +1283,7 @@ AddonInfoDialog::AddonInfoDialog( const QModelIndex &index,
}
// Script files
- QList<QVariant> list = index.data(ExtensionListModel::FilenameRole).toList();
+ QList<QVariant> list = index.data(AddonsModel::Role::FILENAME).toList();
if ( ! list.empty() )
{
label = new QLabel( "<b>" + qtr( "Files" ) + ":</b>", this );
=====================================
modules/gui/qt/dialogs/plugins/plugins.hpp
=====================================
@@ -50,10 +50,9 @@ class ExtensionListItem;
class SearchLineEdit;
class ExtensionCopy;
class ExtensionsManager;
-class AddonsManager;
class PixmapAnimator;
class DelegateAnimationHelper;
-class AddonsSortFilterProxyModel;
+class AddonsModel;
extern "C" {
typedef struct extension_t extension_t;
@@ -148,11 +147,11 @@ private:
WITHONLINEADDONS
};
QListView *addonsView;
- AddonsSortFilterProxyModel *addonsModel;
/* Wait spinner */
PixmapAnimator *spinnerAnimation;
bool b_localdone;
QSignalMapper *signalMapper;
+ std::unique_ptr<AddonsModel> m_model;
};
class PluginTreeItem : public QTreeWidgetItem
@@ -211,74 +210,6 @@ private:
QList<ExtensionCopy*> extensions;
};
-class AddonsListModel: public ExtensionListModel
-{
- Q_OBJECT
-
-public:
- AddonsListModel( AddonsManager *AM, QObject *parent = 0 );
- virtual ~AddonsListModel();
- QVariant data( const QModelIndex& index, int role ) const override;
- QModelIndex index( int row, int column = 0,
- const QModelIndex& = QModelIndex() ) const override;
- int rowCount( const QModelIndex& = QModelIndex() ) const override;
- Qt::ItemFlags flags( const QModelIndex &index ) const override;
- bool setData( const QModelIndex &index, const QVariant &value, int role ) override;
-
- enum
- {
- TypeRole = FilenameRole + 1,
- DescriptionRole,
- UUIDRole,
- FlagsRole,
- StateRole,
- DownloadsCountRole,
- ScoreRole
- };
-
- static QColor getColorByAddonType( int );
-
-protected slots:
- void addonAdded( addon_entry_t * );
- void addonChanged( const addon_entry_t * );
-
-protected:
-
- class Addon
- {
- public:
- Addon( addon_entry_t * );
- ~Addon();
- bool operator==( const Addon & other ) const;
- bool operator==( const addon_entry_t * p_other ) const;
- QVariant data( int ) const;
- private:
- addon_entry_t * p_entry;
- };
-
- QList<Addon*> addons;
- AddonsManager *AM;
-};
-
-class AddonsSortFilterProxyModel : public QSortFilterProxyModel
-{
- Q_OBJECT
-
-public:
- AddonsSortFilterProxyModel( QObject *parent = 0 );
-
-public slots:
- virtual void setTypeFilter( int );
- virtual void setStatusFilter( int );
-
-protected:
- bool filterAcceptsRow( int, const QModelIndex & ) const override;
-
-private:
- int i_type_filter;
- int i_status_filter;
-};
-
class ExtensionItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
@@ -300,7 +231,7 @@ protected:
};
-class AddonItemDelegate : public ExtensionItemDelegate
+class AddonItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
@@ -313,6 +244,8 @@ public:
const QModelIndex &index ) const override;
QSize sizeHint( const QStyleOptionViewItem &option,
const QModelIndex &index ) const override;
+ void initStyleOption( QStyleOptionViewItem *option,
+ const QModelIndex &index ) const override;
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
@@ -327,8 +260,9 @@ signals:
void showInfo();
protected:
- DelegateAnimationHelper *animator;
- QWidget *progressbar;
+ QMargins margins;
+ DelegateAnimationHelper *animator = nullptr;
+ QWidget *progressbar = nullptr;
};
class ExtensionInfoDialog : public QVLCDialog
@@ -346,4 +280,3 @@ public:
};
#endif
-
=====================================
modules/gui/qt/maininterface/mainui.cpp
=====================================
@@ -54,7 +54,7 @@
#include "network/networkmediamodel.hpp"
#include "network/networkdevicemodel.hpp"
#include "network/networksourcesmodel.hpp"
-#include "network/servicesdiscoverymodel.hpp"
+#include "network/addonsmodel.hpp"
#include "network/standardpathmodel.hpp"
#include "menus/qml_menu_wrapper.hpp"
@@ -289,7 +289,7 @@ void MainUI::registerQMLTypes()
qmlRegisterType<NetworkMediaModel>( uri, versionMajor, versionMinor, "NetworkMediaModel");
qmlRegisterType<NetworkDeviceModel>( uri, versionMajor, versionMinor, "NetworkDeviceModel");
qmlRegisterType<NetworkSourcesModel>( uri, versionMajor, versionMinor, "NetworkSourcesModel");
- qmlRegisterType<ServicesDiscoveryModel>( uri, versionMajor, versionMinor, "ServicesDiscoveryModel");
+ qmlRegisterType<AddonsModel>( uri, versionMajor, versionMinor, "ServicesDiscoveryModel");
qmlRegisterType<StandardPathModel>( uri, versionMajor, versionMinor, "StandardPathModel");
qmlRegisterType<MLFoldersModel>( uri, versionMajor, versionMinor, "MLFolderModel");
@@ -380,7 +380,7 @@ void MainUI::registerQMLTypes()
qmlRegisterType<NetworkMediaModel>( uri, versionMajor, versionMinor, "NetworkMediaModel");
qmlRegisterType<NetworkDeviceModel>( uri, versionMajor, versionMinor, "NetworkDeviceModel");
qmlRegisterType<NetworkSourcesModel>( uri, versionMajor, versionMinor, "NetworkSourcesModel");
- qmlRegisterType<ServicesDiscoveryModel>( uri, versionMajor, versionMinor, "ServicesDiscoveryModel");
+ qmlRegisterType<AddonsModel>( uri, versionMajor, versionMinor, "ServicesDiscoveryModel");
qmlRegisterType<MLFoldersModel>( uri, versionMajor, versionMinor, "MLFolderModel");
qmlRegisterType<MLRecentsModel>( uri, versionMajor, versionMinor, "MLRecentModel" );
=====================================
modules/gui/qt/meson.build
=====================================
@@ -49,7 +49,6 @@ moc_headers = files(
'dialogs/open/open.hpp',
'dialogs/open/open_panels.hpp',
'dialogs/open/openurl.hpp',
- 'dialogs/plugins/addons_manager.hpp',
'dialogs/plugins/plugins.hpp',
'dialogs/podcast/podcast_configuration.hpp',
'dialogs/preferences/complete_preferences.hpp',
@@ -102,12 +101,12 @@ moc_headers = files(
'menus/custom_menus.hpp',
'menus/qml_menu_wrapper.hpp',
'menus/menus.hpp',
+ 'network/addonsmodel.hpp',
'network/devicesourceprovider.hpp',
'network/networkbasemodel.hpp',
'network/networkdevicemodel.hpp',
'network/networksourcesmodel.hpp',
'network/networkmediamodel.hpp',
- 'network/servicesdiscoverymodel.hpp',
'network/standardpathmodel.hpp',
'style/systempalette.hpp',
'style/colorcontext.hpp',
@@ -234,8 +233,6 @@ some_sources = files(
'dialogs/open/open_panels.hpp',
'dialogs/open/openurl.cpp',
'dialogs/open/openurl.hpp',
- 'dialogs/plugins/addons_manager.cpp',
- 'dialogs/plugins/addons_manager.hpp',
'dialogs/plugins/plugins.cpp',
'dialogs/plugins/plugins.hpp',
'dialogs/podcast/podcast_configuration.cpp',
@@ -363,6 +360,8 @@ some_sources = files(
'menus/qml_menu_wrapper.hpp',
'menus/menus.cpp',
'menus/menus.hpp',
+ 'network/addonsmodel.cpp',
+ 'network/addonsmodel.hpp',
'network/mediatreelistener.cpp',
'network/mediatreelistener.hpp',
'network/devicesourceprovider.cpp',
@@ -375,8 +374,6 @@ some_sources = files(
'network/networksourcesmodel.hpp',
'network/networkmediamodel.cpp',
'network/networkmediamodel.hpp',
- 'network/servicesdiscoverymodel.cpp',
- 'network/servicesdiscoverymodel.hpp',
'network/standardpathmodel.cpp',
'network/standardpathmodel.hpp',
'network/vlcmediasourcewrapper.hpp',
=====================================
modules/gui/qt/network/servicesdiscoverymodel.cpp → modules/gui/qt/network/addonsmodel.cpp
=====================================
@@ -16,7 +16,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
-#include "servicesdiscoverymodel.hpp"
+#include "addonsmodel.hpp"
#include "util/locallistbasemodel.hpp"
@@ -27,27 +27,54 @@
#include <memory>
#include <QPixmap>
+#include <QQmlFile>
#include <vlc_media_source.h>
#include <vlc_addons.h>
#include <vlc_cxx_helpers.hpp>
#include <vlc_addons.h>
+#define CHECK_ADDON_TYPE_MATCH(type) \
+ static_assert(static_cast<addon_type_t>(AddonsModel::Type::TYPE_ ## type) == ADDON_##type)
+
+CHECK_ADDON_TYPE_MATCH(UNKNOWN);
+CHECK_ADDON_TYPE_MATCH(PLAYLIST_PARSER);
+CHECK_ADDON_TYPE_MATCH(SERVICE_DISCOVERY);
+CHECK_ADDON_TYPE_MATCH(SKIN2);
+CHECK_ADDON_TYPE_MATCH(PLUGIN);
+CHECK_ADDON_TYPE_MATCH(INTERFACE);
+CHECK_ADDON_TYPE_MATCH(META);
+CHECK_ADDON_TYPE_MATCH(OTHER);
+
+#undef CHECK_ADDON_TYPE_MATCH
+
+#define CHECK_ADDON_STATE_MATCH(type) \
+ static_assert(static_cast<addon_state_t>(AddonsModel::State::STATE_ ## type) == ADDON_##type)
+
+CHECK_ADDON_STATE_MATCH(INSTALLING);
+CHECK_ADDON_STATE_MATCH(INSTALLED);
+CHECK_ADDON_STATE_MATCH(UNINSTALLING);
+
+#undef CHECK_ADDON_STATE_MATCH
+
namespace {
using AddonPtr = vlc_shared_data_ptr_type(addon_entry_t,
addon_entry_Hold, addon_entry_Release);
-class SDItem {
+class AddonItem {
public:
- SDItem( AddonPtr addon )
+ //
+ AddonItem( AddonPtr addon )
{
+ vlc_mutex_locker locker{&addon->lock};
name = qfu( addon->psz_name );
- summery = qfu( addon->psz_summary ).trimmed();
+ summary = qfu( addon->psz_summary ).trimmed();
description = qfu( addon->psz_description ).trimmed();
author = qfu( addon->psz_author );
sourceUrl = QUrl( addon->psz_source_uri );
entry = addon;
+ uuid = QByteArray( reinterpret_cast<const char*>(addon->uuid), sizeof( addon_uuid_t ));
if ( addon->psz_image_data ) {
char *cDir = config_GetUserDir( VLC_CACHE_DIR );
@@ -59,9 +86,9 @@ public:
dir.cd("art");
dir.mkdir("qt-addon-covers");
dir.cd("qt-addon-covers");
-
- QString id = addons_uuid_to_psz( &addon->uuid );
- QString filename = QString("addon_thumbnail_%1.png").arg(id);
+ char* uuidStr = addons_uuid_to_psz( &addon->uuid );
+ QString filename = QString("addon_thumbnail_%1.png").arg(uuidStr);
+ free(uuidStr);
QString absoluteFilePath = dir.absoluteFilePath(filename);
if ( !QFileInfo::exists( absoluteFilePath )) {
@@ -82,8 +109,9 @@ public:
}
public:
+ QByteArray uuid;
QString name;
- QString summery;
+ QString summary;
QString description;
QString author;
QUrl sourceUrl;
@@ -93,59 +121,70 @@ public:
} //namespace
-using SDItemPtr = std::shared_ptr<SDItem> ;
-using SDItemList = std::vector<SDItemPtr> ;
+using AddonItemPtr = std::shared_ptr<AddonItem> ;
+using AddonItemList = std::vector<AddonItemPtr> ;
// ListCache specialisation
template<>
-bool ListCache<SDItemPtr>::compareItems(const SDItemPtr& a, const SDItemPtr& b)
+bool ListCache<AddonItemPtr>::compareItems(const AddonItemPtr& a, const AddonItemPtr& b)
{
//just compare the pointers here
- return a == b;
+ return a->uuid == b->uuid;
}
-// ServicesDiscoveryModelPrivate
+// AddonsModelPrivate
-class ServicesDiscoveryModelPrivate
- : public LocalListBaseModelPrivate<SDItemPtr>
+class AddonsModelPrivate
+ : public LocalListBaseModelPrivate<AddonItemPtr>
{
public:
- Q_DECLARE_PUBLIC(ServicesDiscoveryModel)
+ Q_DECLARE_PUBLIC(AddonsModel)
public: //ctor/dtor
- ServicesDiscoveryModelPrivate(ServicesDiscoveryModel* pub)
- : LocalListBaseModelPrivate<SDItemPtr>(pub)
+ AddonsModelPrivate(AddonsModel* pub)
+ : LocalListBaseModelPrivate<AddonItemPtr>(pub)
{
}
- ~ServicesDiscoveryModelPrivate()
+ ~AddonsModelPrivate()
{
if ( m_manager )
addons_manager_Delete( m_manager );
}
public:
- const SDItem* getItemForRow(int row) const
+ const AddonItem* getItemForRow(int row) const
{
- const SDItemPtr* ref = item(row);
+ const AddonItemPtr* ref = item(row);
if (ref)
return ref->get();
return nullptr;
}
+ bool gatherRepository(const char* uri)
+ {
+ Q_Q(AddonsModel);
+ if (!m_manager)
+ return false;
+ m_loading = true;
+ emit q->loadingChanged();
+ addons_manager_Gather( m_manager, uri);
+ return true;
+ }
+
public: //BaseModelPrivateT implementation
bool initializeModel() override;
- LocalListCacheLoader<SDItemPtr>::ItemCompare getSortFunction() const override
+ LocalListCacheLoader<AddonItemPtr>::ItemCompare getSortFunction() const override
{
if (m_sortOrder == Qt::SortOrder::DescendingOrder)
- return [](const SDItemPtr& a, const SDItemPtr& b){
+ return [](const AddonItemPtr& a, const AddonItemPtr& b){
return QString::compare(a->name, b->name) > 0;
};
else
- return [](const SDItemPtr& a, const SDItemPtr& b) {
+ return [](const AddonItemPtr& a, const AddonItemPtr& b) {
return QString::compare(a->name, b->name) < 0;
};
}
@@ -159,123 +198,387 @@ public: //discovery callbacks
public: //LocalListCacheLoader implementation
//return the data matching the pattern
- SDItemList getModelData(const QString& pattern) const override
+ AddonItemList getModelData(const QString& pattern) const override
{
- if (pattern.isEmpty())
+ if (pattern.isEmpty()
+ && m_typeFilter == AddonsModel::Type::TYPE_NONE
+ && m_stateFilter == AddonsModel::State::STATE_NONE)
return m_items;
- SDItemList items;
+ AddonItemList items;
std::copy_if(
m_items.cbegin(), m_items.cend(),
std::back_inserter(items),
- [&pattern](const SDItemPtr& item) {
+ [&pattern, filter = m_typeFilter, state = m_stateFilter](const AddonItemPtr& item) {
+ if (state != AddonsModel::State::STATE_NONE
+ && static_cast<AddonsModel::State>(item->entry->e_state) != state)
+ return false;
+ if (filter != AddonsModel::Type::TYPE_NONE
+ && static_cast<AddonsModel::Type>(item->entry->e_type) != filter)
+ return false;
return item->name.contains(pattern, Qt::CaseInsensitive);
});
return items;
}
public: // data
+ MainCtx* m_ctx = nullptr;
addons_manager_t* m_manager = nullptr;
- SDItemList m_items;
+ AddonItemList m_items;
+ AddonsModel::Type m_typeFilter = AddonsModel::Type::TYPE_NONE;
+ AddonsModel::State m_stateFilter = AddonsModel::State::STATE_NONE;
};
-ServicesDiscoveryModel::ServicesDiscoveryModel( QObject* parent )
- : BaseModel( new ServicesDiscoveryModelPrivate(this), parent )
+AddonsModel::AddonsModel( QObject* parent )
+ : BaseModel( new AddonsModelPrivate(this), parent )
{
}
-QVariant ServicesDiscoveryModel::data( const QModelIndex& index, int role ) const
+QVariant AddonsModel::data( const QModelIndex& index, int role ) const
{
- Q_D(const ServicesDiscoveryModel);
- if (!m_ctx)
+ Q_D(const AddonsModel);
+ if (!d->m_ctx)
return {};
- const SDItem* item = d->getItemForRow(index.row());
+ const AddonItem* item = d->getItemForRow(index.row());
if (!item)
return {};
switch ( role )
{
- case Role::SERVICE_NAME:
+ case Qt::DisplayRole:
+ case Role::NAME:
return item->name;
- case Role::SERVICE_AUTHOR:
+ case Role::AUTHOR:
return item->author;
- case Role::SERVICE_SUMMARY:
- return item->summery;
- case Role::SERVICE_DESCRIPTION:
+ case Role::SUMMARY:
+ return item->summary;
+ case Role::DESCRIPTION:
return item->description;
- case Role::SERVICE_DOWNLOADS:
+ case Role::DOWNLOADS:
+ {
+ vlc_mutex_locker locker{&item->entry->lock};
return QVariant::fromValue( item->entry->i_downloads );
- case Role::SERVICE_SCORE:
- return item->entry->i_score / 100;
- case Role::SERVICE_STATE:
+ }
+ case Role::SCORE:
+ {
+ vlc_mutex_locker locker{&item->entry->lock};
+ return item->entry->i_score;
+ }
+ case Role::STATE:
+ {
+ vlc_mutex_locker locker{&item->entry->lock};
return item->entry->e_state;
- case Role::SERVICE_ARTWORK:
+ }
+ case Role::ARTWORK:
+ {
+ vlc_mutex_locker locker{&item->entry->lock};
return item->artworkUrl;
+ }
+ case Role::TYPE:
+ {
+ vlc_mutex_locker locker{&item->entry->lock};
+ return QVariant::fromValue(item->entry->e_type);
+ }
+ case Role::LINK:
+ {
+ vlc_mutex_locker locker{&item->entry->lock};
+ return QString{ item->entry->psz_source_uri };
+ }
+ case Role::ADDON_VERSION:
+ {
+ vlc_mutex_locker locker{&item->entry->lock};
+ return qfu(item->entry->psz_version);
+ }
+ case Role::UUID:
+ return item->uuid;
+ case Role::DOWNLOAD_COUNT:
+ {
+ vlc_mutex_locker locker{&item->entry->lock};
+ return QVariant::fromValue(item->entry->i_downloads);
+ }
+ case Role::FILENAME:
+ {
+ vlc_mutex_locker locker{&item->entry->lock};
+ QList<QString> list;
+ addon_file_t *p_file;
+ ARRAY_FOREACH( p_file, item->entry->files )
+ list << qfu( p_file->psz_filename );
+ return QVariant{ list };
+ }
+ case Role::BROKEN:
+ {
+ vlc_mutex_locker locker{&item->entry->lock};
+ return item->entry->e_flags & ADDON_BROKEN;
+ }
+ case Role::MANAGEABLE:
+ {
+ vlc_mutex_locker locker{&item->entry->lock};
+ return item->entry->e_flags & ADDON_MANAGEABLE;
+ }
+ case Role::UPDATABLE:
+ {
+ vlc_mutex_locker locker{&item->entry->lock};
+ return item->entry->e_flags & ADDON_UPDATABLE;
+ }
+
+ case Qt::ToolTipRole:
+ {
+ vlc_mutex_locker locker{&item->entry->lock};
+ if ( !( item->entry->e_flags & ADDON_MANAGEABLE ) )
+ {
+ return qtr("This addon has been installed manually. VLC can't manage it by itself.");
+ }
+ return QVariant{};
+ }
+ case Qt::DecorationRole:
+ return QPixmap{QQmlFile::urlToLocalFileOrQrc(item->artworkUrl)};
default:
return {};
}
}
-QHash<int, QByteArray> ServicesDiscoveryModel::roleNames() const
+bool AddonsModel::setData(const QModelIndex& index, const QVariant &value, int role)
+{
+ if ( role == Role::STATE )
+ {
+ auto i_value = value.value<AddonsModel::State>();
+ if ( i_value == AddonsModel::State::STATE_INSTALLING )
+ {
+ installService(index.row());
+ }
+ else if ( i_value == AddonsModel::State::STATE_UNINSTALLING )
+ {
+ removeService(index.row());
+ }
+ }
+ return true;
+}
+
+Qt::ItemFlags AddonsModel::flags( const QModelIndex &index ) const
+{
+ Q_D(const AddonsModel);
+ Qt::ItemFlags qtFlags = BaseModel::flags(index);
+
+ const AddonItem* item = d->getItemForRow(index.row());
+ if (!item)
+ return qtFlags;
+
+ {
+ vlc_mutex_locker locker{&item->entry->lock};
+ addon_state_t addonState = item->entry->e_state;
+ if (addonState == ADDON_INSTALLING || addonState == ADDON_UNINSTALLING)
+ qtFlags &= ~Qt::ItemIsEnabled;
+
+ }
+ qtFlags |= Qt::ItemIsEditable;
+
+ return qtFlags;
+}
+
+QHash<int, QByteArray> AddonsModel::roleNames() const
{
return {
- { Role::SERVICE_NAME, "name" },
- { Role::SERVICE_AUTHOR, "author"},
- { Role::SERVICE_SUMMARY, "summary" },
- { Role::SERVICE_DESCRIPTION, "description" },
- { Role::SERVICE_DOWNLOADS, "downloads" },
- { Role::SERVICE_SCORE, "score" },
- { Role::SERVICE_STATE, "state" },
- { Role::SERVICE_ARTWORK, "artwork" }
+ { Role::NAME, "name" },
+ { Role::AUTHOR, "author"},
+ { Role::SUMMARY, "summary" },
+ { Role::DESCRIPTION, "description" },
+ { Role::DOWNLOADS, "downloads" },
+ { Role::SCORE, "score" },
+ { Role::STATE, "state" },
+ { Role::ARTWORK, "artwork" },
+ { Role::TYPE, "type" },
+ { Role::LINK, "link" },
+ { Role::FILENAME, "filename" },
+ { Role::ADDON_VERSION, "version" },
+ { Role::UUID, "uuid" },
+ { Role::DOWNLOAD_COUNT, "downloadCount" },
+ { Role::BROKEN, "broken" },
+ { Role::MANAGEABLE, "manageable" },
+ { Role::UPDATABLE, "updatable" },
};
}
-void ServicesDiscoveryModel::installService(int idx)
+void AddonsModel::installService(int idx)
{
- Q_D(ServicesDiscoveryModel);
+ Q_D(AddonsModel);
- const SDItem* item = d->getItemForRow(idx);
+ const AddonItem* item = d->getItemForRow(idx);
if (!item)
return;
addon_uuid_t uuid;
- memcpy( uuid, item->entry->uuid, sizeof( uuid ) );
+ assert(sizeof(uuid) == item->uuid.size());
+ memcpy( uuid, item->uuid.constData(), sizeof( uuid ) );
addons_manager_Install( d->m_manager, uuid );
}
-void ServicesDiscoveryModel::removeService(int idx)
+void AddonsModel::removeService(int idx)
{
- Q_D(ServicesDiscoveryModel);
+ Q_D(AddonsModel);
- const SDItem* item = d->getItemForRow(idx);
+ const AddonItem* item = d->getItemForRow(idx);
if (!item)
return;
addon_uuid_t uuid;
- memcpy( uuid, item->entry->uuid, sizeof( uuid ) );
+ assert(sizeof(uuid) == item->uuid.size());
+ memcpy( uuid, item->uuid.constData(), sizeof( uuid ) );
addons_manager_Remove( d->m_manager, uuid );
}
+void AddonsModel::loadFromDefaultRepository()
+{
+ Q_D(AddonsModel);
+ d->gatherRepository("repo://");
+}
-void ServicesDiscoveryModel::setCtx(MainCtx* ctx)
+void AddonsModel::loadFromExternalRepository(QUrl uri)
{
- Q_D(ServicesDiscoveryModel);
+ Q_D(AddonsModel);
+ d->gatherRepository(uri.toEncoded().constData());
+}
- if (ctx == m_ctx)
+void AddonsModel::setCtx(MainCtx* ctx)
+{
+ Q_D(AddonsModel);
+
+ if (ctx == d->m_ctx)
return;
assert(ctx);
- m_ctx = ctx;
+ d->m_ctx = ctx;
d->initializeModel();
emit ctxChanged();
}
-static void addonFoundCallback( addons_manager_t *manager, addon_entry_t *entry )
+MainCtx* AddonsModel::getCtx() const
+{
+ Q_D(const AddonsModel);
+ return d->m_ctx;
+}
+
+AddonsModel::Type AddonsModel::getTypeFilter() const
+{
+ Q_D(const AddonsModel);
+ return d->m_typeFilter;
+}
+
+void AddonsModel::setTypeFilter(AddonsModel::Type type)
{
- if (entry->e_type != ADDON_SERVICE_DISCOVERY)
+ Q_D(AddonsModel);
+ if (type == d->m_typeFilter)
return;
- ServicesDiscoveryModelPrivate* d = (ServicesDiscoveryModelPrivate*) manager->owner.sys;
+ d->m_typeFilter = type;
+
+ d->m_revision++;
+ invalidateCache();
+
+ emit typeFilterChanged();
+}
+
+AddonsModel::State AddonsModel::getStateFilter() const
+{
+ Q_D(const AddonsModel);
+ return d->m_stateFilter;
+}
+
+void AddonsModel::setStateFilter(AddonsModel::State state)
+{
+ Q_D(AddonsModel);
+ if (state == d->m_stateFilter)
+ return;
+ d->m_stateFilter = state;
+
+ d->m_revision++;
+ invalidateCache();
+
+ emit stateFilterChanged();
+}
+
+QString AddonsModel::getLabelForType(AddonsModel::Type type)
+{
+ switch ( type )
+ {
+ case Type::TYPE_SKIN2 :
+ return qtr( "Skins" );
+ case Type::TYPE_PLAYLIST_PARSER:
+ return qtr("Playlist parsers");
+ case Type::TYPE_SERVICE_DISCOVERY:
+ return qtr("Service Discovery");
+ case Type::TYPE_INTERFACE:
+ return qtr("Interfaces");
+ case Type::TYPE_META:
+ return qtr("Art and meta fetchers");
+ case Type::TYPE_EXTENSION:
+ return qtr("Extensions");
+ default:
+ return qtr("Unknown");
+ }
+}
+
+QColor AddonsModel::getColorForType(AddonsModel::Type type)
+{
+ QColor color;
+ switch( type )
+ {
+ case Type::TYPE_EXTENSION:
+ color = QColor(0xDB, 0xC5, 0x40);
+ break;
+ case Type::TYPE_PLAYLIST_PARSER:
+ color = QColor(0x36, 0xBB, 0x59);
+ break;
+ case Type::TYPE_SERVICE_DISCOVERY:
+ color = QColor(0xDB, 0x52, 0x40);
+ break;
+ case Type::TYPE_SKIN2:
+ color = QColor(0x8B, 0xD6, 0xFC);
+ break;
+ case Type::TYPE_INTERFACE:
+ color = QColor(0x00, 0x13, 0x85);
+ break;
+ case Type::TYPE_META:
+ color = QColor(0xCD, 0x23, 0xBF);
+ break;
+ case Type::TYPE_PLUGIN:
+ case Type::TYPE_UNKNOWN:
+ case Type::TYPE_OTHER:
+ default:
+ break;
+ }
+ return color;
+}
+
+QString AddonsModel::getIconForType(AddonsModel::Type type)
+{
+ switch( type )
+ {
+ case AddonsModel::Type::TYPE_EXTENSION:
+ return QStringLiteral("qrc:///addons/addon_yellow.svg");
+ case AddonsModel::Type::TYPE_PLAYLIST_PARSER:
+ return QStringLiteral("qrc:///addons/addon_green.svg");
+ case AddonsModel::Type::TYPE_SERVICE_DISCOVERY:
+ return QStringLiteral("qrc:///addons/addon_red.svg");
+ case AddonsModel::Type::TYPE_SKIN2:
+ return QStringLiteral("qrc:///addons/addon_cyan.svg");
+ case AddonsModel::Type::TYPE_INTERFACE:
+ return QStringLiteral("qrc:///addons/addon_blue.svg");
+ case AddonsModel::Type::TYPE_META:
+ return QStringLiteral("qrc:///addons/addon_magenta.svg");
+ default:
+ return QStringLiteral("qrc:///addons/addon_default.svg");
+ }
+ vlc_assert_unreachable();
+}
+
+int AddonsModel::getMaxScore()
+{
+ return ADDON_MAX_SCORE;
+}
+
+static void addonFoundCallback( addons_manager_t *manager, addon_entry_t *entry )
+{
+ AddonsModelPrivate* d = (AddonsModelPrivate*) manager->owner.sys;
QMetaObject::invokeMethod( d->q_func(), [d, entryPtr = AddonPtr(entry)]()
{
d->addonFound( std::move( entryPtr ) );
@@ -284,7 +587,7 @@ static void addonFoundCallback( addons_manager_t *manager, addon_entry_t *entry
static void addonsDiscoveryEndedCallback( addons_manager_t *manager )
{
- ServicesDiscoveryModelPrivate* d = (ServicesDiscoveryModelPrivate*) manager->owner.sys;
+ AddonsModelPrivate* d = (AddonsModelPrivate*) manager->owner.sys;
QMetaObject::invokeMethod( d->q_func(), [d]()
{
d->discoveryEnded();
@@ -293,19 +596,17 @@ static void addonsDiscoveryEndedCallback( addons_manager_t *manager )
static void addonChangedCallback( addons_manager_t *manager, addon_entry_t *entry )
{
- if (entry->e_type != ADDON_SERVICE_DISCOVERY)
- return;
- ServicesDiscoveryModelPrivate* d = (ServicesDiscoveryModelPrivate*) manager->owner.sys;
+ AddonsModelPrivate* d = (AddonsModelPrivate*) manager->owner.sys;
QMetaObject::invokeMethod( d->q_func(), [d, entryPtr = AddonPtr(entry)]()
{
d->addonChanged( std::move( entryPtr ) );
}, Qt::QueuedConnection);
}
-bool ServicesDiscoveryModelPrivate::initializeModel()
+bool AddonsModelPrivate::initializeModel()
{
- Q_Q(ServicesDiscoveryModel);
- if (m_qmlInitializing || !q->m_ctx)
+ Q_Q(AddonsModel);
+ if (m_qmlInitializing || !m_ctx)
return false;
if ( m_manager )
@@ -319,41 +620,39 @@ bool ServicesDiscoveryModelPrivate::initializeModel()
addonChangedCallback,
};
- m_manager = addons_manager_New( VLC_OBJECT( q->m_ctx->getIntf() ), &owner );
+ m_manager = addons_manager_New( VLC_OBJECT( m_ctx->getIntf() ), &owner );
assert( m_manager );
emit q->loadingChanged();
addons_manager_LoadCatalog( m_manager );
- addons_manager_Gather( m_manager, "repo://" );
+
+ m_revision++;
+ m_loading = false;
+ emit q->loadingChanged();
return true;
}
-void ServicesDiscoveryModelPrivate::addonFound( AddonPtr addon )
+void AddonsModelPrivate::addonFound( AddonPtr addon )
{
- m_items.emplace_back(std::make_shared<SDItem>(addon));
- m_revision++;
- invalidateCache();
+ m_items.emplace_back(std::make_shared<AddonItem>(addon));
}
-void ServicesDiscoveryModelPrivate::addonChanged( AddonPtr addon )
+void AddonsModelPrivate::addonChanged( AddonPtr addon )
{
- for ( size_t r = 0; r < m_items.size(); ++r )
- {
- if ( memcmp( m_items[r]->entry->uuid, addon->uuid, sizeof( addon->uuid ) ) )
- continue;
+ AddonItemPtr sdItem = std::make_shared<AddonItem>(addon);
+ m_cache->updateItem(std::move(sdItem));
- m_items[r] = std::make_shared<SDItem>(addon);
- break;
- }
m_revision++;
invalidateCache();
}
-void ServicesDiscoveryModelPrivate::discoveryEnded()
+void AddonsModelPrivate::discoveryEnded()
{
- Q_Q(ServicesDiscoveryModel);
+ Q_Q(AddonsModel);
assert( m_loading );
m_loading = false;
emit q->loadingChanged();
+ m_revision++;
+ invalidateCache();
}
=====================================
modules/gui/qt/network/servicesdiscoverymodel.hpp → modules/gui/qt/network/addonsmodel.hpp
=====================================
@@ -16,67 +16,111 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
-#ifndef MLServicesDiscoveryModel_HPP
-#define MLServicesDiscoveryModel_HPP
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
+#ifndef ADDONS_MODEL_HPP
+#define ADDONS_MODEL_HPP
#include "util/base_model.hpp"
#include "maininterface/mainctx.hpp"
-class ServicesDiscoveryModelPrivate;
-class ServicesDiscoveryModel : public BaseModel
+class AddonsModelPrivate;
+class AddonsModel : public BaseModel
{
Q_OBJECT
public:
Q_PROPERTY(MainCtx* ctx READ getCtx WRITE setCtx NOTIFY ctxChanged FINAL)
+ //filter list by to match addonTypeFilter, set to NONE to disable filtering
+ Q_PROPERTY(Type typeFilter READ getTypeFilter WRITE setTypeFilter NOTIFY typeFilterChanged FINAL)
+ Q_PROPERTY(State stateFilter READ getStateFilter WRITE setStateFilter NOTIFY stateFilterChanged FINAL)
+
+ Q_PROPERTY(int maxScore READ getMaxScore CONSTANT FINAL)
- enum State // equivalent to addon_state_t
+ enum class State // equivalent to addon_state_t
{
- NOTINSTALLED = 0,
- INSTALLING,
- INSTALLED,
- UNINSTALLING
+ STATE_NOTINSTALLED = 0,
+ STATE_INSTALLING,
+ STATE_INSTALLED,
+ STATE_UNINSTALLING,
+ STATE_NONE //not defined in addon_state_t
};
Q_ENUM(State)
- enum Role {
- SERVICE_NAME = Qt::UserRole + 1,
- SERVICE_AUTHOR,
- SERVICE_SUMMARY,
- SERVICE_DESCRIPTION,
- SERVICE_DOWNLOADS,
- SERVICE_SCORE,
- SERVICE_STATE,
- SERVICE_ARTWORK
+ enum Role : int {
+ NAME = Qt::UserRole + 1,
+ AUTHOR,
+ SUMMARY,
+ DESCRIPTION,
+ DOWNLOADS,
+ SCORE,
+ STATE,
+ TYPE,
+ ARTWORK,
+ LINK,
+ FILENAME,
+ ADDON_VERSION, //VERSION conflicts with config.h define
+ UUID,
+ DOWNLOAD_COUNT,
+ BROKEN,
+ MANAGEABLE,
+ UPDATABLE,
};
Q_ENUM(Role)
- explicit ServicesDiscoveryModel(QObject* parent = nullptr);
+ enum class Type // equivalent as addon_type_t
+ {
+ TYPE_UNKNOWN = 0,
+ TYPE_EXTENSION,
+ TYPE_PLAYLIST_PARSER,
+ TYPE_SERVICE_DISCOVERY,
+ TYPE_SKIN2,
+ TYPE_PLUGIN,
+ TYPE_INTERFACE,
+ TYPE_META,
+ TYPE_OTHER,
+ TYPE_NONE //not defined in addon_type_t
+ };
+ Q_ENUM(Type)
+
+
+ explicit AddonsModel(QObject* parent = nullptr);
public: //QAbstractListModel override
QVariant data(const QModelIndex& index, int role) const override;
+ bool setData(const QModelIndex& index, const QVariant &value, int role) override;
+ Qt::ItemFlags flags( const QModelIndex &index ) const override;
QHash<int, QByteArray> roleNames() const override;
public: //invokable functions
Q_INVOKABLE void installService(int idx);
Q_INVOKABLE void removeService(int idx);
+ Q_INVOKABLE void loadFromDefaultRepository();
+ Q_INVOKABLE void loadFromExternalRepository(QUrl uri);
+
+ Q_INVOKABLE static QString getLabelForType(Type type);
+ Q_INVOKABLE static QColor getColorForType(Type type);
+ Q_INVOKABLE static QString getIconForType(Type type);
+
+ static int getMaxScore();
+
public: // properties
void setCtx(MainCtx* ctx);
- inline MainCtx* getCtx() const { return m_ctx; }
+ MainCtx* getCtx() const;
+
+ State getStateFilter() const;
+ void setStateFilter(State state);
+
+ Type getTypeFilter() const;
+ void setTypeFilter(Type type);
signals:
void ctxChanged();
+ void stateFilterChanged();
+ void typeFilterChanged();
private:
- MainCtx* m_ctx = nullptr;
-
- Q_DECLARE_PRIVATE(ServicesDiscoveryModel);
+ Q_DECLARE_PRIVATE(AddonsModel);
};
-#endif // MLServicesDiscoveryModel_HPP
+#endif // ADDONS_MODEL_HPP
=====================================
modules/gui/qt/network/qml/ServicesManage.qml
=====================================
@@ -38,9 +38,14 @@ Widgets.ListViewExt {
ctx: MainCtx
+ typeFilter: ServicesDiscoveryModel.TYPE_SERVICE_DISCOVERY
searchPattern: MainCtx.search.pattern
sortOrder: MainCtx.sort.order
sortCriteria: MainCtx.sort.criteria
+
+ Component.onCompleted: {
+ discoveryModel.loadFromDefaultRepository()
+ }
}
topMargin: VLCStyle.margin_large
@@ -111,25 +116,25 @@ Widgets.ListViewExt {
id: action_btn
focus: true
- iconTxt: model.state === ServicesDiscoveryModel.INSTALLED ? VLCIcons.del : VLCIcons.add
- busy: model.state === ServicesDiscoveryModel.INSTALLING || model.state === ServicesDiscoveryModel.UNINSTALLING
+ iconTxt: model.state === ServicesDiscoveryModel.STATE_INSTALLED ? VLCIcons.del : VLCIcons.add
+ busy: model.state === ServicesDiscoveryModel.STATE_INSTALLING || model.state === ServicesDiscoveryModel.STATE_UNINSTALLING
text: {
switch(model.state) {
- case ServicesDiscoveryModel.INSTALLED:
+ case ServicesDiscoveryModel.STATE_INSTALLED:
return qsTr("Remove")
- case ServicesDiscoveryModel.NOTINSTALLED:
+ case ServicesDiscoveryModel.STATE_NOTINSTALLED:
return qsTr("Install")
- case ServicesDiscoveryModel.INSTALLING:
+ case ServicesDiscoveryModel.STATE_INSTALLING:
return qsTr("Installing")
- case ServicesDiscoveryModel.UNINSTALLING:
+ case ServicesDiscoveryModel.STATE_UNINSTALLING:
return qsTr("Uninstalling")
}
}
onClicked: {
- if (model.state === ServicesDiscoveryModel.NOTINSTALLED)
+ if (model.state === ServicesDiscoveryModel.STATE_NOTINSTALLED)
discoveryModel.installService(index)
- else if (model.state === ServicesDiscoveryModel.INSTALLED)
+ else if (model.state === ServicesDiscoveryModel.STATE_INSTALLED)
discoveryModel.removeService(index)
}
}
@@ -146,7 +151,9 @@ Widgets.ListViewExt {
}
Widgets.CaptionLabel {
- text: qsTr("Score: %1/5 Downloads: %2").arg(model.score).arg(model.downloads)
+ text: qsTr("Score: %1/5 Downloads: %2")
+ .arg( (5 * model.score / discoveryModel.maxScore).toFixed(1) )
+ .arg(model.downloads)
topPadding: VLCStyle.margin_xsmall
color: servicesView.colorContext.fg.secondary
Layout.fillWidth: true
=====================================
modules/gui/qt/qt.cpp
=====================================
@@ -78,7 +78,6 @@ extern "C" char **environ;
#endif
#include "style/defaultthemeproviders.hpp"
#include "dialogs/extensions/extensions_manager.hpp" /* Extensions manager */
-#include "dialogs/plugins/addons_manager.hpp" /* Addons manager */
#include "dialogs/help/help.hpp" /* Launch Update */
#include "util/dismiss_popup_event_filter.hpp"
#include "maininterface/compositor.hpp"
@@ -1124,7 +1123,6 @@ static void *ThreadCleanup( qt_intf_t *p_intf, CleanupReason cleanupReason )
/* */
ExtensionsManager::killInstance();
- AddonsManager::killInstance();
/* Destroy all remaining windows,
because some are connected to some slots
=====================================
modules/gui/qt/util/base_model.hpp
=====================================
@@ -97,7 +97,7 @@ signals:
public:
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
-protected:
+public:
void classBegin() override;
void componentComplete() override;
=====================================
po/POTFILES.in
=====================================
@@ -709,8 +709,6 @@ modules/gui/qt/dialogs/open/openurl.cpp
modules/gui/qt/dialogs/open/openurl.hpp
modules/gui/qt/dialogs/playlists/playlists.cpp
modules/gui/qt/dialogs/playlists/playlists.hpp
-modules/gui/qt/dialogs/plugins/addons_manager.cpp
-modules/gui/qt/dialogs/plugins/addons_manager.hpp
modules/gui/qt/dialogs/plugins/plugins.cpp
modules/gui/qt/dialogs/plugins/plugins.hpp
modules/gui/qt/dialogs/podcast/podcast_configuration.cpp
@@ -809,6 +807,7 @@ modules/gui/qt/menus/menus.hpp
modules/gui/qt/menus/qml/Menubar.qml
modules/gui/qt/menus/qml_menu_wrapper.cpp
modules/gui/qt/menus/qml_menu_wrapper.hpp
+modules/gui/qt/network/addonsmodel.cpp
modules/gui/qt/network/standardpathmodel.cpp
modules/gui/qt/network/standardpathmodel.hpp
modules/gui/qt/network/qml/BrowseDeviceView.qml
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/cac68167eb562b4e74777bcedc686cd797f6325c...346c18b719ae5775e7e41a6084c07c65a89bd79b
--
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/cac68167eb562b4e74777bcedc686cd797f6325c...346c18b719ae5775e7e41a6084c07c65a89bd79b
You're receiving this email because of your account on code.videolan.org.
VideoLAN code repository instance
More information about the vlc-commits
mailing list