[vlc-devel] [PATCH 12/12] qt: playlist: implement Qt playlist list model
Romain Vimont
rom1v at videolabs.io
Thu Oct 11 23:14:50 CEST 2018
Wrap the playlist, playlist items and media (input items) into C++
classes, for convenience and automatic memory management.
Use them to implement a list model to be used by a Qt list view.
---
modules/gui/qt/Makefile.am | 8 +
.../gui/qt/components/playlist_new/media.hpp | 78 ++++++
.../qt/components/playlist_new/playlist.cpp | 261 ++++++++++++++++++
.../qt/components/playlist_new/playlist.hpp | 104 +++++++
.../components/playlist_new/playlist_item.hpp | 96 +++++++
.../playlist_new/playlist_model.cpp | 184 ++++++++++++
.../playlist_new/playlist_model.hpp | 80 ++++++
modules/gui/qt/qt.cpp | 16 ++
8 files changed, 827 insertions(+)
create mode 100644 modules/gui/qt/components/playlist_new/media.hpp
create mode 100644 modules/gui/qt/components/playlist_new/playlist.cpp
create mode 100644 modules/gui/qt/components/playlist_new/playlist.hpp
create mode 100644 modules/gui/qt/components/playlist_new/playlist_item.hpp
create mode 100644 modules/gui/qt/components/playlist_new/playlist_model.cpp
create mode 100644 modules/gui/qt/components/playlist_new/playlist_model.hpp
diff --git a/modules/gui/qt/Makefile.am b/modules/gui/qt/Makefile.am
index a37baeb1a9..d5476e7508 100644
--- a/modules/gui/qt/Makefile.am
+++ b/modules/gui/qt/Makefile.am
@@ -120,6 +120,14 @@ libqt_plugin_la_SOURCES = \
gui/qt/components/playlist/selector.cpp \
gui/qt/components/playlist/selector.hpp \
gui/qt/components/playlist/sorting.h \
+ gui/qt/components/playlist_new/media.hpp \
+ gui/qt/components/playlist_new/playlist.cpp \
+ gui/qt/components/playlist_new/playlist.hpp \
+ gui/qt/components/playlist_new/playlist.moc.cpp \
+ gui/qt/components/playlist_new/playlist_item.hpp \
+ gui/qt/components/playlist_new/playlist_model.cpp \
+ gui/qt/components/playlist_new/playlist_model.hpp \
+ gui/qt/components/playlist_new/playlist_model.moc.cpp \
gui/qt/components/sout/profile_selector.cpp \
gui/qt/components/sout/profile_selector.hpp \
gui/qt/components/sout/sout_widgets.cpp \
diff --git a/modules/gui/qt/components/playlist_new/media.hpp b/modules/gui/qt/components/playlist_new/media.hpp
new file mode 100644
index 0000000000..03f2a4b47f
--- /dev/null
+++ b/modules/gui/qt/components/playlist_new/media.hpp
@@ -0,0 +1,78 @@
+/*****************************************************************************
+ * media_item.hpp
+ *****************************************************************************
+ * Copyright (C) 2018 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 VLC_QT_MEDIA_HPP_
+#define VLC_QT_MEDIA_HPP_
+
+#include <vlc_cxx_helpers.hpp>
+#include <vlc_common.h>
+#include <vlc_input_item.h>
+#include <QString>
+
+namespace vlc {
+ namespace playlist {
+
+using InputItemPtr = vlc_shared_data_ptr_type(input_item_t,
+ input_item_Hold,
+ input_item_Release);
+
+class Media
+{
+public:
+ Media(input_item_t *media = nullptr)
+ {
+ if (media)
+ {
+ /* the media must be unique in the playlist */
+ ptr.reset(input_item_Copy(media), false);
+ if (!ptr)
+ throw std::bad_alloc();
+ }
+ }
+
+ Media(QString uri, QString name)
+ {
+ auto uUri = uri.toUtf8();
+ auto uName = name.toUtf8();
+ const char *rawUri = uUri.isNull() ? nullptr : uUri.constData();
+ const char *rawName = uName.isNull() ? nullptr : uName.constData();
+ ptr.reset(input_item_New(rawUri, rawName), false);
+ if (!ptr)
+ throw std::bad_alloc();
+ }
+
+ operator bool() const
+ {
+ return ptr;
+ }
+
+ input_item_t *raw() const
+ {
+ return ptr.get();
+ }
+
+private:
+ InputItemPtr ptr;
+};
+
+ } // namespace playlist
+} // namespace vlc
+
+#endif
diff --git a/modules/gui/qt/components/playlist_new/playlist.cpp b/modules/gui/qt/components/playlist_new/playlist.cpp
new file mode 100644
index 0000000000..08235cec5e
--- /dev/null
+++ b/modules/gui/qt/components/playlist_new/playlist.cpp
@@ -0,0 +1,261 @@
+/*****************************************************************************
+ * playlist.cpp
+ *****************************************************************************
+ * Copyright (C) 2018 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "playlist.hpp"
+#include <algorithm>
+
+namespace vlc {
+ namespace playlist {
+
+static QVector<PlaylistItem> toVec(vlc_playlist_item_t *const items[],
+ size_t len)
+{
+ QVector<PlaylistItem> vec;
+ for (size_t i = 0; i < len; ++i)
+ vec.push_back(items[i]);
+ return vec;
+}
+
+extern "C" { // for C callbacks
+
+static void
+on_playlist_items_reset(vlc_playlist_t *playlist,
+ vlc_playlist_item_t *const items[],
+ size_t len, void *userdata)
+{
+ VLC_UNUSED(playlist);
+ Playlist *this_ = static_cast<Playlist *>(userdata);
+ emit this_->playlistItemsReset(toVec(items, len));
+}
+
+static void
+on_playlist_items_added(vlc_playlist_t *playlist, size_t index,
+ vlc_playlist_item_t *const items[], size_t len,
+ void *userdata)
+{
+ VLC_UNUSED(playlist);
+ Playlist *this_ = static_cast<Playlist *>(userdata);
+ emit this_->playlistItemsAdded(index, toVec(items, len));
+}
+
+static void
+on_playlist_items_moved(vlc_playlist_t *playlist, size_t index, size_t count,
+ size_t target, void *userdata)
+{
+ VLC_UNUSED(playlist);
+ Playlist *this_ = static_cast<Playlist *>(userdata);
+ emit this_->playlistItemsMoved(index, count, target);
+}
+
+static void
+on_playlist_items_removed(vlc_playlist_t *playlist, size_t index, size_t count,
+ void *userdata)
+{
+ VLC_UNUSED(playlist);
+ Playlist *this_ = static_cast<Playlist *>(userdata);
+ emit this_->playlistItemsRemoved(index, count);
+}
+
+static void
+on_playlist_items_updated(vlc_playlist_t *playlist, size_t index,
+ vlc_playlist_item_t *const items[], size_t len,
+ void *userdata)
+{
+ VLC_UNUSED(playlist);
+ Playlist *this_ = static_cast<Playlist *>(userdata);
+ emit this_->playlistItemsUpdated(index, toVec(items, len));
+}
+
+static void
+on_playlist_playback_repeat_changed(vlc_playlist_t *playlist,
+ enum vlc_playlist_playback_repeat repeat,
+ void *userdata)
+{
+ VLC_UNUSED(playlist);
+ Playlist *this_ = static_cast<Playlist *>(userdata);
+ emit this_->playlistPlaybackRepeatChanged(repeat);
+}
+
+static void
+on_playlist_playback_order_changed(vlc_playlist_t *playlist,
+ enum vlc_playlist_playback_order order,
+ void *userdata)
+{
+ VLC_UNUSED(playlist);
+ Playlist *this_ = static_cast<Playlist *>(userdata);
+ emit this_->playlistPlaybackOrderChanged(order);
+}
+
+static void
+on_playlist_current_item_changed(vlc_playlist_t *playlist, ssize_t index,
+ void *userdata)
+{
+ VLC_UNUSED(playlist);
+ Playlist *this_ = static_cast<Playlist *>(userdata);
+ emit this_->playlistCurrentItemChanged(index);
+}
+
+static void
+on_playlist_has_prev_changed(vlc_playlist_t *playlist, bool has_prev,
+ void *userdata)
+{
+ VLC_UNUSED(playlist);
+ Playlist *this_ = static_cast<Playlist *>(userdata);
+ emit this_->playlistHasPrevChanged(has_prev);
+}
+
+static void
+on_playlist_has_next_changed(vlc_playlist_t *playlist, bool has_next,
+ void *userdata)
+{
+ VLC_UNUSED(playlist);
+ Playlist *this_ = static_cast<Playlist *>(userdata);
+ emit this_->playlistHasNextChanged(has_next);
+}
+
+} // extern "C"
+
+static const struct vlc_playlist_callbacks playlist_callbacks = {
+ /* C++ (before C++20) does not support designated initializers */
+ on_playlist_items_reset,
+ on_playlist_items_added,
+ on_playlist_items_moved,
+ on_playlist_items_removed,
+ on_playlist_items_updated,
+ on_playlist_playback_repeat_changed,
+ on_playlist_playback_order_changed,
+ on_playlist_current_item_changed,
+ on_playlist_has_prev_changed,
+ on_playlist_has_next_changed,
+};
+
+Playlist::Playlist(vlc_playlist_t *playlist, QObject *parent)
+ : QObject(parent)
+ , playlist(playlist)
+{
+ PlaylistLocker locker(this);
+ listener = vlc_playlist_AddListener(playlist, &playlist_callbacks, this,
+ false);
+ if (!listener)
+ throw std::bad_alloc();
+}
+
+Playlist::~Playlist()
+{
+ PlaylistLocker locker(this);
+ vlc_playlist_RemoveListener(playlist, listener);
+}
+
+void Playlist::lock()
+{
+ vlc_playlist_Lock(playlist);
+}
+
+void Playlist::unlock()
+{
+ vlc_playlist_Unlock(playlist);
+}
+
+vlc_playlist_t *
+Playlist::raw()
+{
+ return playlist;
+}
+
+template <typename RAW, typename WRAPPER>
+static QVector<RAW> toRaw(const QVector<WRAPPER> &items)
+{
+ QVector<RAW> vec;
+ int count = items.size();
+ vec.reserve(count);
+ for (int i = 0; i < count; ++i)
+ vec.push_back(items[i].raw());
+ return vec;
+}
+
+void
+Playlist::append(const QVector<Media> &media)
+{
+ PlaylistLocker locker(this);
+
+ auto rawMedia = toRaw<input_item_t *>(media);
+ int ret = vlc_playlist_Append(playlist,
+ rawMedia.constData(), rawMedia.size());
+ if (ret != VLC_SUCCESS)
+ throw std::bad_alloc();
+}
+
+void
+Playlist::insert(size_t index, const QVector<Media> &media)
+{
+ PlaylistLocker locker(this);
+
+ auto rawMedia = toRaw<input_item_t *>(media);
+ int ret = vlc_playlist_RequestInsert(playlist, index,
+ rawMedia.constData(), rawMedia.size());
+ if (ret != VLC_SUCCESS)
+ throw std::bad_alloc();
+}
+
+void
+Playlist::move(const QVector<PlaylistItem> &items, size_t target,
+ ssize_t indexHint)
+{
+ PlaylistLocker locker(this);
+
+ auto rawItems = toRaw<vlc_playlist_item_t *>(items);
+ int ret = vlc_playlist_RequestMove(playlist, rawItems.constData(),
+ rawItems.size(), target, indexHint);
+ if (ret != VLC_SUCCESS)
+ throw std::bad_alloc();
+}
+
+void
+Playlist::remove(const QVector<PlaylistItem> &items, ssize_t indexHint)
+{
+ PlaylistLocker locker(this);
+
+ auto rawItems = toRaw<vlc_playlist_item_t *>(items);
+ int ret = vlc_playlist_RequestRemove(playlist, rawItems.constData(),
+ rawItems.size(), indexHint);
+ if (ret != VLC_SUCCESS)
+ throw std::bad_alloc();
+}
+
+void
+Playlist::shuffle()
+{
+ PlaylistLocker locker(this);
+ vlc_playlist_Shuffle(playlist);
+}
+
+void
+Playlist::sort(const QVector<vlc_playlist_sort_criterion> &criteria)
+{
+ PlaylistLocker locker(this);
+ vlc_playlist_Sort(playlist, criteria.constData(), criteria.size());
+}
+
+ } // namespace playlist
+} // namespace vlc
diff --git a/modules/gui/qt/components/playlist_new/playlist.hpp b/modules/gui/qt/components/playlist_new/playlist.hpp
new file mode 100644
index 0000000000..9e1dfab9af
--- /dev/null
+++ b/modules/gui/qt/components/playlist_new/playlist.hpp
@@ -0,0 +1,104 @@
+/*****************************************************************************
+ * playlist.hpp
+ *****************************************************************************
+ * Copyright (C) 2018 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 VLC_QT_PLAYLIST_NEW_HPP_
+#define VLC_QT_PLAYLIST_NEW_HPP_
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <QObject>
+#include <QVector>
+#include <vlc_playlist_new.h>
+#include "media.hpp"
+#include "playlist_item.hpp"
+
+namespace vlc {
+ namespace playlist {
+
+/**
+ * Core playlist wrapper.
+ *
+ * This wrapper redispatches the core playlist events as Qt signals, and
+ * exposes functions to apply changes on the playlist.
+ *
+ * When a user requests to insert, move or remove items, before the core
+ * playlist lock is successfully acquired, another client may have changed the
+ * list. Therefore, this wrapper calls the vlc_playlist_Request*() functions
+ * from the core playlist to solve conflicts automatically.
+ *
+ * The actual changes applied are notified through the callbacks (signals).
+ */
+class Playlist : public QObject
+{
+ Q_OBJECT
+
+public:
+ Playlist(vlc_playlist_t *playlist, QObject *parent = nullptr);
+ ~Playlist();
+
+ vlc_playlist_t *raw();
+
+ void lock();
+ void unlock();
+
+ void append(const QVector<Media> &);
+ void insert(size_t index, const QVector<Media> &);
+ void move(const QVector<PlaylistItem> &, size_t target, ssize_t indexHint);
+ void remove(const QVector<PlaylistItem> &, ssize_t indexHint);
+
+ void shuffle();
+ void sort(const QVector<vlc_playlist_sort_criterion> &);
+
+signals:
+ void playlistItemsReset(QVector<PlaylistItem>);
+ void playlistItemsAdded(size_t index, QVector<PlaylistItem>);
+ void playlistItemsMoved(size_t index, size_t count, size_t target);
+ void playlistItemsRemoved(size_t index, size_t count);
+ void playlistItemsUpdated(size_t index, QVector<PlaylistItem>);
+ void playlistPlaybackRepeatChanged(enum vlc_playlist_playback_repeat);
+ void playlistPlaybackOrderChanged(enum vlc_playlist_playback_order);
+ void playlistCurrentItemChanged(ssize_t index);
+ void playlistHasPrevChanged(bool hasPrev);
+ void playlistHasNextChanged(bool hasNext);
+
+private:
+ vlc_playlist_t *playlist;
+ vlc_playlist_listener_id *listener;
+};
+
+class PlaylistLocker {
+ Playlist *playlist;
+public:
+ PlaylistLocker(Playlist *playlist) : playlist(playlist)
+ {
+ playlist->lock();
+ }
+ ~PlaylistLocker()
+ {
+ playlist->unlock();
+ }
+};
+
+ } // namespace playlist
+} // namespace vlc
+
+#endif
diff --git a/modules/gui/qt/components/playlist_new/playlist_item.hpp b/modules/gui/qt/components/playlist_new/playlist_item.hpp
new file mode 100644
index 0000000000..936cb1ec41
--- /dev/null
+++ b/modules/gui/qt/components/playlist_new/playlist_item.hpp
@@ -0,0 +1,96 @@
+/*****************************************************************************
+ * playlist_item.hpp
+ *****************************************************************************
+ * Copyright (C) 2018 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 VLC_QT_PLAYLIST_NEW_ITEM_HPP_
+#define VLC_QT_PLAYLIST_NEW_ITEM_HPP_
+
+#include <vlc_cxx_helpers.hpp>
+#include <vlc_input_item.h>
+#include <vlc_playlist_new.h>
+#include <QExplicitlySharedDataPointer>
+
+namespace vlc {
+ namespace playlist {
+
+using PlaylistItemPtr = vlc_shared_data_ptr_type(vlc_playlist_item_t,
+ vlc_playlist_item_Hold,
+ vlc_playlist_item_Release);
+
+/**
+ * Playlist item wrapper.
+ *
+ * It contains both the PlaylistItemPtr and cached data saved while the playlist
+ * is locked, so that the fields may be read without synchronization or race
+ * conditions.
+ */
+class PlaylistItem
+{
+public:
+ PlaylistItem(vlc_playlist_item_t *item = nullptr)
+ {
+ if (item)
+ {
+ d = new Data();
+ d->item.reset(item);
+ sync();
+ }
+ }
+
+ operator bool() const
+ {
+ return d;
+ }
+
+ vlc_playlist_item_t *raw() const {
+ return d ? d->item.get() : nullptr;
+ }
+
+ QString getTitle() const
+ {
+ return d->title;
+ }
+
+ void sync() {
+ input_item_t *media = vlc_playlist_item_GetMedia(d->item.get());
+ vlc_mutex_lock(&media->lock);
+ d->title = media->psz_name;
+ vlc_mutex_unlock(&media->lock);
+ }
+
+private:
+ struct Data : public QSharedData {
+ PlaylistItemPtr item;
+
+ /* cached values */
+ QString title;
+ };
+
+ QExplicitlySharedDataPointer<Data> d;
+};
+
+/* PlaylistItem has the same size as a raw pointer */
+static_assert(sizeof(PlaylistItem) == sizeof(void *));
+
+ } // namespace playlist
+} // namespace vlc
+
+Q_DECLARE_METATYPE(vlc::playlist::PlaylistItem);
+
+#endif
diff --git a/modules/gui/qt/components/playlist_new/playlist_model.cpp b/modules/gui/qt/components/playlist_new/playlist_model.cpp
new file mode 100644
index 0000000000..be14f63273
--- /dev/null
+++ b/modules/gui/qt/components/playlist_new/playlist_model.cpp
@@ -0,0 +1,184 @@
+/*****************************************************************************
+ * playlist_model.cpp
+ *****************************************************************************
+ * Copyright (C) 2018 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "playlist_model.hpp"
+#include <algorithm>
+#include <assert.h>
+
+namespace vlc {
+ namespace playlist {
+
+PlaylistModel::PlaylistModel(Playlist *playlist, QObject *parent)
+ : QAbstractListModel(parent)
+ , playlist(playlist)
+{
+ /* Do not use a Qt::AutoConnection, because in case the changes have been
+ * requested from the Qt UI thread, the slot will be executed directly, like
+ * with a Qt::DirectConnection, which would possibly break the order in
+ * which the events received by the core playlist are handled. In that case,
+ * the indices provided by the events would be invalid. */
+ connect(playlist, &Playlist::playlistItemsReset,
+ this, &PlaylistModel::onPlaylistItemsReset,
+ Qt::QueuedConnection);
+ connect(playlist, &Playlist::playlistItemsAdded,
+ this, &PlaylistModel::onPlaylistItemsAdded,
+ Qt::QueuedConnection);
+ connect(playlist, &Playlist::playlistItemsMoved,
+ this, &PlaylistModel::onPlaylistItemsMoved,
+ Qt::QueuedConnection);
+ connect(playlist, &Playlist::playlistItemsRemoved,
+ this, &PlaylistModel::onPlaylistItemsRemoved,
+ Qt::QueuedConnection);
+ connect(playlist, &Playlist::playlistItemsUpdated,
+ this, &PlaylistModel::onPlaylistItemsUpdated,
+ Qt::QueuedConnection);
+ connect(playlist, &Playlist::playlistCurrentItemChanged,
+ this, &PlaylistModel::onPlaylistCurrentItemChanged,
+ Qt::QueuedConnection);
+}
+
+void
+PlaylistModel::notifyItemsChanged(int idx, int count, const QVector<int> &roles)
+{
+ QModelIndex first = index(idx, 0);
+ QModelIndex last = index(idx + count - 1);
+ emit dataChanged(first, last, roles);
+}
+
+void
+PlaylistModel::onPlaylistItemsReset(QVector<PlaylistItem> newContent)
+{
+ beginResetModel();
+ items.swap(newContent);
+ endResetModel();
+}
+
+void
+PlaylistModel::onPlaylistItemsAdded(size_t index, QVector<PlaylistItem> added)
+{
+ int count = added.size();
+ beginInsertRows({}, index, index + count - 1);
+ items.insert(index, count, nullptr);
+ std::move(added.cbegin(), added.cend(), items.begin() + index);
+ endInsertRows();
+}
+
+void
+PlaylistModel::onPlaylistItemsMoved(size_t index, size_t count, size_t target)
+{
+ size_t qtTarget = target;
+ if (qtTarget > index)
+ /* Qt interprets the target index as the index of the insertion _before_
+ * the move, while the playlist core interprets it as the new index of
+ * the slice _after_ the move. */
+ qtTarget += count;
+
+ beginMoveRows({}, index, index + count - 1, {}, qtTarget);
+ if (index < target)
+ std::rotate(items.begin() + index,
+ items.begin() + index + count,
+ items.begin() + target + count);
+ else
+ std::rotate(items.begin() + target,
+ items.begin() + index,
+ items.begin() + index + count);
+ endMoveRows();
+}
+
+void
+PlaylistModel::onPlaylistItemsRemoved(size_t index, size_t count)
+{
+ beginRemoveRows({}, index, index + count - 1);
+ items.remove(index, count);
+ endRemoveRows();
+}
+
+void
+PlaylistModel::onPlaylistItemsUpdated(size_t index,
+ QVector<PlaylistItem> updated)
+{
+ int count = updated.size();
+ for (int i = 0; i < count; ++i)
+ {
+ assert(items[index + i].raw() == updated[i].raw());
+ items[index + i] = updated[i]; /* sync metadata */
+ }
+ notifyItemsChanged(index, count);
+}
+
+void
+PlaylistModel::onPlaylistCurrentItemChanged(ssize_t index)
+{
+ ssize_t oldCurrent = current;
+ current = index;
+ if (oldCurrent != -1)
+ notifyItemsChanged(oldCurrent, 1, {IsCurrentRole});
+ if (index != -1)
+ notifyItemsChanged(index, 1, {IsCurrentRole});
+}
+
+QHash<int, QByteArray>
+PlaylistModel::roleNames() const
+{
+ return {
+ { TitleRole, "title" },
+ { IsCurrentRole, "isCurrent" },
+ };
+}
+
+int
+PlaylistModel::rowCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent);
+ return items.size();
+}
+
+const PlaylistItem &
+PlaylistModel::itemAt(int index) const
+{
+ return items[index];
+}
+
+int
+PlaylistModel::count() const
+{
+ return rowCount();
+}
+
+QVariant
+PlaylistModel::data(const QModelIndex &index, int role) const
+{
+ switch (role)
+ {
+ case TitleRole:
+ return items[index.row()].getTitle();
+ case IsCurrentRole:
+ return index.row() != -1 && index.row() == current;
+ default:
+ return {};
+ }
+}
+
+ } // namespace playlist
+} // namespace vlc
diff --git a/modules/gui/qt/components/playlist_new/playlist_model.hpp b/modules/gui/qt/components/playlist_new/playlist_model.hpp
new file mode 100644
index 0000000000..b7cafe3b78
--- /dev/null
+++ b/modules/gui/qt/components/playlist_new/playlist_model.hpp
@@ -0,0 +1,80 @@
+/*****************************************************************************
+ * playlist_model.hpp
+ *****************************************************************************
+ * Copyright (C) 2018 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 VLC_QT_PLAYLIST_NEW_MODEL_HPP_
+#define VLC_QT_PLAYLIST_NEW_MODEL_HPP_
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <QAbstractListModel>
+#include <QVector>
+#include "playlist.hpp"
+
+namespace vlc {
+ namespace playlist {
+
+class PlaylistModel : public QAbstractListModel
+{
+ Q_OBJECT
+ Q_ENUMS(Roles)
+
+public:
+ enum Roles
+ {
+ TitleRole = Qt::UserRole,
+ IsCurrentRole,
+ };
+
+ PlaylistModel(Playlist *playlist, QObject *parent = nullptr);
+
+ QHash<int, QByteArray> roleNames() const override;
+ int rowCount(const QModelIndex &parent = {}) const override;
+ QVariant data(const QModelIndex &index,
+ int role = Qt::DisplayRole) const override;
+
+ /* provided for convenience */
+ const PlaylistItem &itemAt(int index) const;
+ int count() const;
+
+private slots:
+ void onPlaylistItemsReset(QVector<PlaylistItem>);
+ void onPlaylistItemsAdded(size_t index, QVector<PlaylistItem>);
+ void onPlaylistItemsMoved(size_t index, size_t count, size_t target);
+ void onPlaylistItemsRemoved(size_t index, size_t count);
+ void onPlaylistItemsUpdated(size_t index, QVector<PlaylistItem>);
+ void onPlaylistCurrentItemChanged(ssize_t index);
+
+private:
+ void notifyItemsChanged(int index, int count,
+ const QVector<int> &roles = {});
+
+ Playlist *playlist;
+
+ /* access only from the UI thread */
+ QVector<PlaylistItem> items;
+ ssize_t current = -1;
+};
+
+ } // namespace playlist
+} // namespace vlc
+
+#endif
diff --git a/modules/gui/qt/qt.cpp b/modules/gui/qt/qt.cpp
index 6fdbda06fa..42cb126d0a 100644
--- a/modules/gui/qt/qt.cpp
+++ b/modules/gui/qt/qt.cpp
@@ -60,6 +60,9 @@ extern "C" char **environ;
#include "util/qvlcapp.hpp" /* QVLCApplication definition */
#include "components/playlist/playlist_model.hpp" /* for ~PLModel() */
+#include <QVector>
+#include "components/playlist_new/playlist_item.hpp"
+
#include <vlc_plugin.h>
#include <vlc_vout_window.h>
#ifndef X_DISPLAY_MISSING
@@ -488,6 +491,17 @@ static void Close( vlc_object_t *p_this )
busy = false;
}
+static inline void qRegisterMetaTypes()
+{
+ // register all types used by signal/slots
+ qRegisterMetaType<size_t>("size_t");
+ qRegisterMetaType<ssize_t>("ssize_t");
+ qRegisterMetaType<vlc::playlist::PlaylistItem>("PlaylistItem");
+ qRegisterMetaType<QVector<vlc::playlist::PlaylistItem>>("QVector<PlaylistItem>");
+ qRegisterMetaType<enum vlc_playlist_playback_order>("vlc_playlist_playback_order");
+ qRegisterMetaType<enum vlc_playlist_playback_repeat>("vlc_playlist_playback_repeat");
+}
+
static void *Thread( void *obj )
{
intf_thread_t *p_intf = (intf_thread_t *)obj;
@@ -624,6 +638,8 @@ static void *Thread( void *obj )
if( s_style.compare("") != 0 )
QApplication::setStyle( s_style );
+ qRegisterMetaTypes();
+
/* Launch */
app.exec();
--
2.19.1
More information about the vlc-devel
mailing list