From 5a01ce8d599070fe5350ca82be7c164e7e9c4835 Mon Sep 17 00:00:00 2001 From: Pierre Lamot Date: Tue, 29 Aug 2023 16:14:31 +0200 Subject: [PATCH] qt: make NetworkDeviceModel use BaseModel --- modules/gui/qt/network/networkdevicemodel.cpp | 726 ++++++++++-------- modules/gui/qt/network/networkdevicemodel.hpp | 76 +- 2 files changed, 435 insertions(+), 367 deletions(-) diff --git a/modules/gui/qt/network/networkdevicemodel.cpp b/modules/gui/qt/network/networkdevicemodel.cpp index d1f37cbf37..78783d78c3 100644 --- a/modules/gui/qt/network/networkdevicemodel.cpp +++ b/modules/gui/qt/network/networkdevicemodel.cpp @@ -16,43 +16,402 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ +#include + +#include "maininterface/mainctx.hpp" + #include "networkdevicemodel.hpp" #include "networkmediamodel.hpp" +#include "mediatreelistener.hpp" + #include "playlist/media.hpp" #include "playlist/playlist_controller.hpp" + #include "util/shared_input_item.hpp" -#include +#include "util/base_model_p.hpp" +#include "util/locallistcacheloader.hpp" + +namespace +{ + +//represents an entry of the model +struct NetworkDeviceItem +{ + NetworkDeviceItem(const SharedInputItem& item, const NetworkDeviceModel::MediaSourcePtr& mediaSource) + : name(qfu(item->psz_name)) + , mainMrl(QUrl::fromEncoded(item->psz_uri)) + , protocol(mainMrl.scheme()) + , type( static_cast(item->i_type)) + , mediaSource(mediaSource) + , inputItem(item) + { + id = qHash(name) ^ qHash(protocol); + mrls.push_back(std::make_pair(mainMrl, mediaSource)); + + char* artworkUrl = input_item_GetArtworkURL(inputItem.get()); + if (artworkUrl) + { + artwork = QString::fromUtf8(artworkUrl); + free(artworkUrl); + } + } + + uint id; + QString name; + QUrl mainMrl; + std::vector> mrls; + QString protocol; + NetworkDeviceModel::ItemType type; + NetworkDeviceModel::MediaSourcePtr mediaSource; + SharedInputItem inputItem; + QString artwork; +}; + +using NetworkDeviceItemPtr = std::shared_ptr; +using NetworkDeviceModelLoader = LocalListCacheLoader; + +//hash and compare function for std::unordered_set +struct NetworkDeviceItemHash +{ + std::size_t operator()(const NetworkDeviceItemPtr& s) const noexcept + { + return s->id; + } +}; + +struct NetworkDeviceItemEqual +{ + bool operator()(const NetworkDeviceItemPtr& a, const NetworkDeviceItemPtr& b) const noexcept + { + return a->id == b->id + && QString::compare(a->name, b->name, Qt::CaseInsensitive) == 0 + && QString::compare(a->protocol, b->protocol, Qt::CaseInsensitive) == 0; + } +}; + +using NetworkDeviceItemSet = std::unordered_set; + +bool itemMatchPattern(const NetworkDeviceItemPtr& a, const QString& pattern) +{ + return a->name.contains(pattern, Qt::CaseInsensitive); +} + +bool ascendingMrl(const NetworkDeviceItemPtr& a, const NetworkDeviceItemPtr& b) +{ + return (QString::compare(a->mainMrl.toString(), + b->mainMrl.toString(), Qt::CaseInsensitive) <= 0); +} + +bool ascendingName(const NetworkDeviceItemPtr& a, const NetworkDeviceItemPtr& b) +{ + int result = QString::compare(a->name, b->name, Qt::CaseInsensitive); + + if (result != 0) + return (result <= 0); + + return ascendingMrl(a, b); +} + +bool descendingMrl(const NetworkDeviceItemPtr& a, const NetworkDeviceItemPtr& b) +{ + return (QString::compare(a->mainMrl.toString(), + b->mainMrl.toString(), Qt::CaseInsensitive) >= 0); +} + +bool descendingName(const NetworkDeviceItemPtr& a, const NetworkDeviceItemPtr& b) +{ + int result = QString::compare(a->name, b->name, Qt::CaseInsensitive); + + if (result != 0) + return (result >= 0); + + return descendingMrl(a, b); +} + +} + +//handle discovery events from the media source provider +struct NetworkDeviceModel::ListenerCb : public MediaTreeListener::MediaTreeListenerCb { + ListenerCb(NetworkDeviceModel* model, MediaSourcePtr mediaSource) + : model(model) + , mediaSource(std::move(mediaSource)) + {} + + void onItemCleared( MediaTreePtr tree, input_item_node_t* node ) override; + void onItemAdded( MediaTreePtr tree, input_item_node_t* parent, input_item_node_t *const children[], size_t count ) override; + void onItemRemoved( MediaTreePtr tree, input_item_node_t * node, input_item_node_t *const children[], size_t count ) override; + inline void onItemPreparseEnded( MediaTreePtr, input_item_node_t *, enum input_item_preparse_status ) override {} + + NetworkDeviceModel *model; + MediaSourcePtr mediaSource; +}; + +// ListCache specialisation + +template<> +bool ListCache::compareItems(const NetworkDeviceItemPtr& a, const NetworkDeviceItemPtr& b) +{ + //just compare the pointers here + return a == b; +} + +// NetworkDeviceModelPrivate + +class NetworkDeviceModelPrivate + : public BaseModelPrivateT + , public LocalListCacheLoader::ModelSource +{ + Q_DECLARE_PUBLIC(NetworkDeviceModel) +public: + NetworkDeviceModelPrivate(NetworkDeviceModel * pub) + : BaseModelPrivateT(pub) + , m_items(0, NetworkDeviceItemHash{}, NetworkDeviceItemEqual{}) + {} + + NetworkDeviceModelLoader::ItemCompare getSortFunction() const + { + if (m_sortCriteria == "mrl") + { + if (m_sortOrder == Qt::AscendingOrder) + return ascendingMrl; + else + return descendingMrl; + } + else + { + if (m_sortOrder == Qt::AscendingOrder) + return ascendingName; + else + return descendingName; + } + } + + std::unique_ptr> createLoader() const override + { + return std::make_unique( + this, m_searchPattern, + getSortFunction()); + } + + bool initializeModel() override + { + Q_Q(NetworkDeviceModel); + + if (m_qmlInitializing || !q->m_ctx || q->m_sdSource == NetworkDeviceModel::CAT_UNDEFINED || q->m_sourceName.isEmpty()) + return false; + + auto libvlc = vlc_object_instance(q->m_ctx->getIntf()); + + m_listeners.clear(); + m_items.clear(); + + q->m_name = QString {}; + + auto provider = vlc_media_source_provider_Get( libvlc ); + + using SourceMetaPtr = std::unique_ptr; + + SourceMetaPtr providerList( vlc_media_source_provider_List( provider, static_cast(q->m_sdSource) ), + &vlc_media_source_meta_list_Delete ); + if ( providerList == nullptr ) + return false; + + auto nbProviders = vlc_media_source_meta_list_Count( providerList.get() ); + + for ( auto i = 0u; i < nbProviders; ++i ) + { + auto meta = vlc_media_source_meta_list_Get( providerList.get(), i ); + const QString sourceName = qfu( meta->name ); + if ( q->m_sourceName != '*' && q->m_sourceName != sourceName ) + continue; + + q->m_name += q->m_name.isEmpty() ? qfu( meta->longname ) : ", " + qfu( meta->longname ); + emit q->nameChanged(); + + auto mediaSource = vlc_media_source_provider_GetMediaSource( provider, + meta->name ); + if ( mediaSource == nullptr ) + continue; + std::unique_ptr l{ new MediaTreeListener( + MediaTreePtr{ mediaSource->tree }, + std::make_unique(q, MediaSourcePtr{ mediaSource }) ) }; + if ( l->listener == nullptr ) + return false; + m_listeners.push_back( std::move( l ) ); + } + return m_listeners.empty() == false; + } + + const NetworkDeviceItem* getItemForRow(int row) const + { + const NetworkDeviceItemPtr* ref = item(row); + if (ref) + return ref->get(); + return nullptr; + } + + void addItems( + const std::vector& inputList, + const MediaSourcePtr& mediaSource, + bool clear) + { + bool dataChanged = false; + + if (clear) + { + //std::remove_if doesn't work with unordered_set + //due to iterators being const + for (auto it = m_items.begin(); it != m_items.end(); ) + { + it = std::find_if( + it, m_items.end(), + [&mediaSource](const NetworkDeviceItemPtr& item) { + return item->mediaSource == mediaSource; + }); + + if (it != m_items.end()) + it = m_items.erase(it); + } + dataChanged = true; + } + + for (const SharedInputItem & inputItem : inputList) + { + auto newItem = std::make_shared(inputItem, mediaSource); + auto it = m_items.find(newItem); + if (it != m_items.end()) + { + (*it)->mrls.push_back(std::make_pair(newItem->mainMrl, mediaSource)); + } + else + { + m_items.emplace(std::move(newItem)); + dataChanged = true; + } + } + + if (dataChanged) + { + m_modelRevision += 1; + invalidateCache(); + } + } + + void removeItems(const std::vector& inputList, const MediaSourcePtr& mediaSource) + { + bool dataChanged = false; + for (const SharedInputItem& p_item : inputList) + { + auto items = m_items; + + auto oldItem = std::make_shared(p_item, mediaSource); + NetworkDeviceItemSet::iterator it = m_items.find(oldItem); + if (it != m_items.end()) + { + bool found = false; + + const NetworkDeviceItemPtr& item = *it; + if (item->mrls.size() > 1) + { + auto mrlIt = std::find_if( + item->mrls.begin(), item->mrls.end(), + [&oldItem]( const std::pair& mrl ) { + return mrl.first.matches(oldItem->mainMrl, QUrl::StripTrailingSlash) + && mrl.second == oldItem->mediaSource; + }); + + if ( mrlIt != item->mrls.end() ) + { + found = true; + item->mrls.erase( mrlIt ); + } + } + + if (!found) + { + items.erase(it); + dataChanged = true; + } + } + + } + if (dataChanged) + { + m_modelRevision += 1; + invalidateCache(); + } + } + +public: //LocalListCacheLoader::ModelSource + size_t getModelRevision() const override + { + return m_modelRevision; + } + + std::vector getModelData(const QString& pattern) const override + { + std::vector items; + if (pattern.isEmpty()) + { + std::copy( + m_items.cbegin(), m_items.cend(), + std::back_inserter(items)); + } + else + { + std::copy_if( + m_items.cbegin(), m_items.cend(), + std::back_inserter(items), + [&pattern](const NetworkDeviceItemPtr& item){ + return itemMatchPattern(item, pattern); + }); + } + return items; + } + +public: + size_t m_modelRevision = 0; + NetworkDeviceItemSet m_items; + std::vector> m_listeners; +}; NetworkDeviceModel::NetworkDeviceModel( QObject* parent ) - : ClipListModel(parent) + : NetworkDeviceModel(new NetworkDeviceModelPrivate(this), parent) +{ +} + +NetworkDeviceModel::NetworkDeviceModel( NetworkDeviceModelPrivate* priv, QObject* parent) + : BaseModel(priv, parent) { - m_comparator = ascendingName; } QVariant NetworkDeviceModel::data( const QModelIndex& index, int role ) const { + Q_D(const NetworkDeviceModel); if (!m_ctx) return {}; - auto idx = index.row(); - if ( idx < 0 || idx >= count() ) + + const NetworkDeviceItem* item = d->getItemForRow(index.row()); + if (!item) return {}; - const auto& item = m_items[idx]; + switch ( role ) { case NETWORK_NAME: - return item.name; + return item->name; case NETWORK_MRL: - return item.mainMrl; + return item->mainMrl; case NETWORK_TYPE: - return item.type; + return item->type; case NETWORK_PROTOCOL: - return item.protocol; + return item->protocol; case NETWORK_SOURCE: - return item.mediaSource->description; + return item->mediaSource->description; case NETWORK_TREE: - return QVariant::fromValue( NetworkTreeItem(MediaTreePtr{ item.mediaSource->tree }, item.inputItem.get()) ); + return QVariant::fromValue( NetworkTreeItem(MediaTreePtr{ item->mediaSource->tree }, item->inputItem.get()) ); case NETWORK_ARTWORK: - return item.artwork; + return item->artwork; default: return {}; } @@ -73,48 +432,47 @@ QHash NetworkDeviceModel::roleNames() const void NetworkDeviceModel::setCtx(MainCtx* ctx) { - if (ctx) { - m_ctx = ctx; - } - if (m_ctx && m_sdSource != CAT_UNDEFINED && !m_sourceName.isEmpty()) { - initializeMediaSources(); - } + Q_D(NetworkDeviceModel); + if (m_ctx == ctx) + return; + m_ctx = ctx; + d->initializeModel(); emit ctxChanged(); } void NetworkDeviceModel::setSdSource(SDCatType s) { + Q_D(NetworkDeviceModel); + if (m_sdSource == s) + return; m_sdSource = s; - if (m_ctx && m_sdSource != CAT_UNDEFINED && !m_sourceName.isEmpty()) { - initializeMediaSources(); - } + d->initializeModel(); emit sdSourceChanged(); } void NetworkDeviceModel::setSourceName(const QString& sourceName) { + Q_D(NetworkDeviceModel); + if (m_sourceName == sourceName) + return; m_sourceName = sourceName; - if (m_ctx && m_sdSource != CAT_UNDEFINED && !m_sourceName.isEmpty()) { - initializeMediaSources(); - } + d->initializeModel(); emit sourceNameChanged(); } bool NetworkDeviceModel::insertIntoPlaylist(const QModelIndexList &itemIdList, ssize_t playlistIndex) { + Q_D(NetworkDeviceModel); if (!(m_ctx && m_sdSource != CAT_MYCOMPUTER)) return false; QVector medias; medias.reserve( itemIdList.size() ); for ( const QModelIndex &id : itemIdList ) { - if ( !id.isValid() ) - continue; - const int index = id.row(); - if ( index < 0 || index >= count() ) + const NetworkDeviceItem* item = d->getItemForRow(id.row()); + if (!item) continue; - - medias.append( vlc::playlist::Media {m_items[index].inputItem.get()} ); + medias.append( vlc::playlist::Media {item->inputItem.get()} ); } if (medias.isEmpty()) return false; @@ -122,14 +480,17 @@ bool NetworkDeviceModel::insertIntoPlaylist(const QModelIndexList &itemIdList, s return true; } -bool NetworkDeviceModel::addToPlaylist(int index) +bool NetworkDeviceModel::addToPlaylist(int row) { + Q_D(NetworkDeviceModel); if (!(m_ctx && m_sdSource != CAT_MYCOMPUTER)) return false; - if (index < 0 || index >= count() ) + + const NetworkDeviceItem* item = d->getItemForRow(row); + if (!item) return false; - auto item = m_items[index]; - vlc::playlist::Media media{ item.inputItem.get() }; + + vlc::playlist::Media media{ item->inputItem.get() }; m_ctx->getIntf()->p_mainPlaylistController->append( QVector{ media }, false); return true; } @@ -160,15 +521,18 @@ bool NetworkDeviceModel::addToPlaylist(const QModelIndexList &itemIdList) return ret; } -bool NetworkDeviceModel::addAndPlay(int index) +bool NetworkDeviceModel::addAndPlay(int row) { + Q_D(NetworkDeviceModel); if (!(m_ctx && m_sdSource != CAT_MYCOMPUTER)) return false; - if (index < 0 || index >= count() ) + + const NetworkDeviceItem* item = d->getItemForRow(row); + if (!item) return false; - auto item = m_items[index]; - vlc::playlist::Media media{ item.inputItem.get() }; - m_ctx->getIntf()->p_mainPlaylistController->append(QVector{ media }, true); + + vlc::playlist::Media media{ item->inputItem.get() }; + m_ctx->getIntf()->p_mainPlaylistController->append( QVector{ media }, true); return true; } @@ -179,11 +543,11 @@ bool NetworkDeviceModel::addAndPlay(const QVariantList& itemIdList) { if (varValue.canConvert()) { - auto index = varValue.value(); + auto row = varValue.value(); if (!ret) - ret |= addAndPlay(index); + ret |= addAndPlay(row); else - ret |= addToPlaylist(index); + ret |= addToPlaylist(row); } } return ret; @@ -204,100 +568,25 @@ bool NetworkDeviceModel::addAndPlay(const QModelIndexList& itemIdList) return ret; } -QMap NetworkDeviceModel::getDataAt(int idx) -{ - QMap dataDict; - QHash roles = roleNames(); - for (auto role: roles.keys()) { - dataDict[roles[role]] = data(index(idx), role); - } - return dataDict; -} - /* Q_INVOKABLE */ QVariantList NetworkDeviceModel::getItemsForIndexes(const QModelIndexList & indexes) const { + Q_D(const NetworkDeviceModel); QVariantList items; for (const QModelIndex & modelIndex : indexes) { - int index = modelIndex.row(); - - if (index < 0 || index >= count()) + const NetworkDeviceItem* item = d->getItemForRow(modelIndex.row()); + if (!item) continue; - items.append(QVariant::fromValue(SharedInputItem(m_items[index].inputItem.get(), true))); + items.append(QVariant::fromValue(SharedInputItem(item->inputItem.get(), true))); } return items; } -// Protected ClipListModel implementation - -void NetworkDeviceModel::onUpdateSort(const QString & criteria, Qt::SortOrder order) /* override */ -{ - if (criteria == "mrl") - { - if (order == Qt::AscendingOrder) - m_comparator = ascendingMrl; - else - m_comparator = descendingMrl; - } - else - { - if (order == Qt::AscendingOrder) - m_comparator = ascendingName; - else - m_comparator = descendingName; - } -} - -bool NetworkDeviceModel::initializeMediaSources() -{ - auto libvlc = vlc_object_instance(m_ctx->getIntf()); - - m_listeners.clear(); - - clearItems(); - - m_name = QString {}; - - auto provider = vlc_media_source_provider_Get( libvlc ); - - using SourceMetaPtr = std::unique_ptr; - - SourceMetaPtr providerList( vlc_media_source_provider_List( provider, static_cast(m_sdSource) ), - &vlc_media_source_meta_list_Delete ); - if ( providerList == nullptr ) - return false; - - auto nbProviders = vlc_media_source_meta_list_Count( providerList.get() ); - - for ( auto i = 0u; i < nbProviders; ++i ) - { - auto meta = vlc_media_source_meta_list_Get( providerList.get(), i ); - const QString sourceName = qfu( meta->name ); - if ( m_sourceName != '*' && m_sourceName != sourceName ) - continue; - - m_name += m_name.isEmpty() ? qfu( meta->longname ) : ", " + qfu( meta->longname ); - emit nameChanged(); - - auto mediaSource = vlc_media_source_provider_GetMediaSource( provider, - meta->name ); - if ( mediaSource == nullptr ) - continue; - std::unique_ptr l{ new MediaTreeListener( - MediaTreePtr{ mediaSource->tree }, - std::make_unique(this, MediaSourcePtr{ mediaSource }) ) }; - if ( l->listener == nullptr ) - return false; - m_listeners.push_back( std::move( l ) ); - } - return m_listeners.empty() == false; -} - +// NetworkDeviceModel::ListenerCb implementation void NetworkDeviceModel::ListenerCb::onItemCleared( MediaTreePtr tree, input_item_node_t* node ) { @@ -328,36 +617,8 @@ void NetworkDeviceModel::ListenerCb::onItemRemoved( MediaTreePtr tree, input_ite for ( auto i = 0u; i < count; ++i ) itemList.emplace_back( children[i]->p_item ); - QMetaObject::invokeMethod(model, [model=model, itemList=std::move(itemList)]() { - int implicitCount = model->implicitCount(); - - for (auto p_item : itemList) - { - QUrl itemUri = QUrl::fromEncoded(p_item->psz_uri); - auto it = std::find_if( begin( model->m_items ), end( model->m_items ), - [p_item, itemUri](const NetworkDeviceItem & i) { - return QString::compare( qfu(p_item->psz_name), i.name, Qt::CaseInsensitive ) == 0 && - itemUri.scheme() == i.mainMrl.scheme(); - }); - if ( it == end( model->m_items ) ) - continue; - - auto mrlIt = std::find_if( begin( (*it).mrls ), end( (*it).mrls), - [itemUri]( const QUrl& mrl ) { - return mrl.matches(itemUri, QUrl::StripTrailingSlash); - }); - - if ( mrlIt == end( (*it).mrls ) ) - continue; - (*it).mrls.erase( mrlIt ); - if ( (*it).mrls.empty() == false ) - continue; - auto idx = std::distance( begin( model->m_items ), it ); - - model->eraseItem(it, idx, implicitCount); - } - - model->updateItems(); + QMetaObject::invokeMethod(model, [model=model, mediaSource=mediaSource, itemList=std::move(itemList)]() { + model->d_func()->removeItems(itemList, mediaSource); }, Qt::QueuedConnection); } @@ -365,166 +626,15 @@ void NetworkDeviceModel::refreshDeviceList(MediaSourcePtr mediaSource, input_item_node_t * const children[], size_t count, bool clear) { - if (clear) - { - QMetaObject::invokeMethod(this, [this, mediaSource]() - { - int implicitCount = this->implicitCount(); - - int index = 0; - - std::vector::iterator it = m_items.begin(); - - while (it != m_items.end()) - { - if (it->mediaSource != mediaSource) - continue; - - eraseItem(it, index, implicitCount); - - index++; - } - }); - } - std::vector itemList; itemList.reserve(count); - for (size_t i = 0; i < count; i++) - { - input_item_t * item = children[i]->p_item; - - itemList.emplace_back(item); - } + itemList.emplace_back(children[i]->p_item); - QMetaObject::invokeMethod(this, [this, itemList = std::move(itemList), mediaSource]() mutable + QMetaObject::invokeMethod(this, [this, clear, itemList = std::move(itemList), mediaSource]() mutable { - addItems(itemList, mediaSource); + Q_D(NetworkDeviceModel); + d->addItems(itemList, mediaSource, clear); }, Qt::QueuedConnection); } - -void NetworkDeviceModel::addItems(const std::vector & inputList, - const MediaSourcePtr & mediaSource) -{ - // NOTE: We need to check duplicates when we're not sorting by name. Otherwise it's handled via - // the 'name' sorting functions. - bool checkDuplicate = (m_sortCriteria != "name"); - - for (const SharedInputItem & inputItem : inputList) - { - NetworkDeviceItem item; - - item.name = qfu(inputItem->psz_name); - - item.mainMrl = QUrl::fromEncoded(inputItem->psz_uri); - - std::vector::iterator it; - - if (checkDuplicate) - { - it = std::find_if(begin(m_items), end(m_items), [item](const NetworkDeviceItem & i) - { - return matchItem(item, i); - }); - - // NOTE: We don't want the same name and scheme to appear twice in the list. - if (it != end(m_items)) - continue; - - it = std::upper_bound(begin(m_items), end(m_items), item, m_comparator); - - if (it != end(m_items)) - continue; - } - else - { - it = std::upper_bound(begin(m_items), end(m_items), item, m_comparator); - - if (it != end(m_items) && matchItem(item, *it)) - continue; - } - - item.mrls.push_back(item.mainMrl); - - item.protocol = item.mainMrl.scheme(); - - item.type = static_cast(inputItem->i_type); - - item.mediaSource = mediaSource; - - item.inputItem = SharedInputItem(inputItem); - - char * artwork = input_item_GetArtworkURL(inputItem.get()); - if (artwork) - { - item.artwork = QString::fromUtf8(artwork); - - free(artwork); - } - - - insertItem(it, std::move(item)); - } - - updateItems(); -} - -void NetworkDeviceModel::eraseItem(std::vector::iterator & it, - int index, int count) -{ - if (index < count) - { - removeItem(it, index); - } - // NOTE: We don't want to notify the view if the item's position is beyond the - // maximumCount. - else - it = m_items.erase(it); -} - -// Private static function - -/* static */ bool NetworkDeviceModel::matchItem(const NetworkDeviceItem & a, - const NetworkDeviceItem & b) -{ - return (QString::compare(a.name, b.name, Qt::CaseInsensitive) == 0 - && - QString::compare(a.mainMrl.scheme(), b.mainMrl.scheme(), Qt::CaseInsensitive) == 0); -} - -/* static */ bool NetworkDeviceModel::ascendingName(const NetworkDeviceItem & a, - const NetworkDeviceItem & b) -{ - int result = QString::compare(a.name, b.name, Qt::CaseInsensitive); - - if (result != 0) - return (result <= 0); - - return (QString::compare(a.mainMrl.scheme(), b.mainMrl.scheme(), Qt::CaseInsensitive) <= 0); -} - -/* static */ bool NetworkDeviceModel::ascendingMrl(const NetworkDeviceItem & a, - const NetworkDeviceItem & b) -{ - return (QString::compare(a.mainMrl.toString(), - b.mainMrl.toString(), Qt::CaseInsensitive) <= 0); -} - -/* static */ bool NetworkDeviceModel::descendingName(const NetworkDeviceItem & a, - const NetworkDeviceItem & b) -{ - int result = QString::compare(a.name, b.name, Qt::CaseInsensitive); - - if (result != 0) - return (result >= 0); - - return (QString::compare(a.mainMrl.scheme(), b.mainMrl.scheme(), Qt::CaseInsensitive) >= 0); -} - -/* static */ bool NetworkDeviceModel::descendingMrl(const NetworkDeviceItem & a, - const NetworkDeviceItem & b) -{ - return (QString::compare(a.mainMrl.toString(), - b.mainMrl.toString(), Qt::CaseInsensitive) >= 0); -} diff --git a/modules/gui/qt/network/networkdevicemodel.hpp b/modules/gui/qt/network/networkdevicemodel.hpp index 140205ef8a..4825050383 100644 --- a/modules/gui/qt/network/networkdevicemodel.hpp +++ b/modules/gui/qt/network/networkdevicemodel.hpp @@ -28,17 +28,15 @@ #include -#include "mediatreelistener.hpp" -#include "util/cliplistmodel.hpp" +#include "util/base_model.hpp" #include "util/shared_input_item.hpp" #include class MainCtx; -struct NetworkDeviceItem; - -class NetworkDeviceModel : public ClipListModel +class NetworkDeviceModelPrivate; +class NetworkDeviceModel : public BaseModel { Q_OBJECT @@ -90,7 +88,11 @@ public: // Declarations vlc_media_tree_Release); public: NetworkDeviceModel( QObject* parent = nullptr ); + virtual ~NetworkDeviceModel() = default; +protected: + NetworkDeviceModel( NetworkDeviceModelPrivate* priv, QObject* parent = nullptr); +public: QVariant data(const QModelIndex& index, int role) const override; QHash roleNames() const override; @@ -98,84 +100,40 @@ public: void setSdSource(SDCatType s); void setSourceName(const QString& sourceName); - inline MainCtx* getCtx() { return m_ctx; } - inline SDCatType getSdSource() { return m_sdSource; } - inline QString getName() { return m_name; } - inline QString getSourceName() { return m_sourceName; } + inline MainCtx* getCtx() const { return m_ctx; } + inline SDCatType getSdSource() const { return m_sdSource; } + inline QString getName() const { return m_name; } + inline QString getSourceName() const { return m_sourceName; } Q_INVOKABLE bool insertIntoPlaylist( const QModelIndexList& itemIdList, ssize_t playlistIndex ); - Q_INVOKABLE bool addToPlaylist( int index ); + Q_INVOKABLE bool addToPlaylist(int row ); Q_INVOKABLE bool addToPlaylist(const QVariantList& itemIdList); Q_INVOKABLE bool addToPlaylist(const QModelIndexList& itemIdList); - Q_INVOKABLE bool addAndPlay( int index ); + Q_INVOKABLE bool addAndPlay(int row ); Q_INVOKABLE bool addAndPlay(const QVariantList& itemIdList); Q_INVOKABLE bool addAndPlay(const QModelIndexList& itemIdList); - Q_INVOKABLE QMap getDataAt(int index); - Q_INVOKABLE QVariantList getItemsForIndexes(const QModelIndexList & indexes) const; -protected: // ClipListModel implementation - void onUpdateSort(const QString & criteria, Qt::SortOrder order) override; - signals: void ctxChanged(); void sdSourceChanged(); void sourceNameChanged(); void nameChanged(); + void itemsUpdated(); private: - bool initializeMediaSources(); - void refreshDeviceList(MediaSourcePtr mediaSource, input_item_node_t* const children[], size_t count , bool clear); - - void addItems(const std::vector & inputList, const MediaSourcePtr & mediaSource); - - void eraseItem(std::vector::iterator & it, int index, int count); - -private: // Static functions - static bool matchItem(const NetworkDeviceItem & a, const NetworkDeviceItem & b); - - static bool ascendingName(const NetworkDeviceItem & a, const NetworkDeviceItem & b); - static bool ascendingMrl (const NetworkDeviceItem & a, const NetworkDeviceItem & b); - - static bool descendingName(const NetworkDeviceItem & a, const NetworkDeviceItem & b); - static bool descendingMrl (const NetworkDeviceItem & a, const NetworkDeviceItem & b); + bool initializeMediaSources(); private: - struct ListenerCb : public MediaTreeListener::MediaTreeListenerCb { - ListenerCb(NetworkDeviceModel *model, MediaSourcePtr mediaSource) - : model(model) - , mediaSource(std::move(mediaSource)) - {} - - void onItemCleared( MediaTreePtr tree, input_item_node_t* node ) override; - void onItemAdded( MediaTreePtr tree, input_item_node_t* parent, input_item_node_t *const children[], size_t count ) override; - void onItemRemoved( MediaTreePtr tree, input_item_node_t * node, input_item_node_t *const children[], size_t count ) override; - inline void onItemPreparseEnded( MediaTreePtr, input_item_node_t *, enum input_item_preparse_status ) override {} - - NetworkDeviceModel *model; - MediaSourcePtr mediaSource; - }; - MainCtx* m_ctx = nullptr; SDCatType m_sdSource = CAT_UNDEFINED; QString m_sourceName; // '*' -> all sources QString m_name; // source long name - std::vector> m_listeners; -}; - -struct NetworkDeviceItem -{ - QString name; - QUrl mainMrl; - std::vector mrls; - QString protocol; - NetworkDeviceModel::ItemType type; - NetworkDeviceModel::MediaSourcePtr mediaSource; - SharedInputItem inputItem; - QString artwork; + class ListenerCb; + Q_DECLARE_PRIVATE(NetworkDeviceModel) }; #endif // MLNETWORKDEVICEMODEL_HPP