[vlc-devel] [RFC 22/82] qt: playlist: implement Qt playlist list model and controler
Pierre Lamot
pierre at videolabs.io
Fri Feb 1 14:01:26 CET 2019
From: Romain Vimont <rom1v at videolabs.io>
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.
the playlist model provides information about the play queue
the playlist controler allows to manipulate the playback (play, pause, next, ...)
---
modules/gui/qt/Makefile.am | 12 +
modules/gui/qt/components/playlist/media.hpp | 101 +++
.../components/playlist/playlist_common.cpp | 40 ++
.../components/playlist/playlist_common.hpp | 58 ++
.../playlist/playlist_controler.cpp | 667 ++++++++++++++++++
.../playlist/playlist_controler.hpp | 167 +++++
.../playlist/playlist_controler_p.hpp | 69 ++
.../qt/components/playlist/playlist_item.cpp | 79 +++
.../qt/components/playlist/playlist_item.hpp | 103 +++
.../qt/components/playlist/playlist_model.cpp | 418 +++++++++++
.../qt/components/playlist/playlist_model.hpp | 88 +++
.../components/playlist/playlist_model_p.hpp | 72 ++
.../gui/qt/components/playlist_new/media.hpp | 76 ++
modules/gui/qt/qt.cpp | 12 +
14 files changed, 1962 insertions(+)
create mode 100644 modules/gui/qt/components/playlist/media.hpp
create mode 100644 modules/gui/qt/components/playlist/playlist_common.cpp
create mode 100644 modules/gui/qt/components/playlist/playlist_common.hpp
create mode 100644 modules/gui/qt/components/playlist/playlist_controler.cpp
create mode 100644 modules/gui/qt/components/playlist/playlist_controler.hpp
create mode 100644 modules/gui/qt/components/playlist/playlist_controler_p.hpp
create mode 100644 modules/gui/qt/components/playlist/playlist_item.cpp
create mode 100644 modules/gui/qt/components/playlist/playlist_item.hpp
create mode 100644 modules/gui/qt/components/playlist/playlist_model.cpp
create mode 100644 modules/gui/qt/components/playlist/playlist_model.hpp
create mode 100644 modules/gui/qt/components/playlist/playlist_model_p.hpp
create mode 100644 modules/gui/qt/components/playlist_new/media.hpp
diff --git a/modules/gui/qt/Makefile.am b/modules/gui/qt/Makefile.am
index 0f6b7d2904..7d6c517459 100644
--- a/modules/gui/qt/Makefile.am
+++ b/modules/gui/qt/Makefile.am
@@ -109,8 +109,17 @@ libqt_plugin_la_SOURCES = \
gui/qt/components/epg/EPGView.cpp gui/qt/components/epg/EPGView.hpp \
gui/qt/components/epg/EPGWidget.cpp \
gui/qt/components/epg/EPGWidget.hpp \
+ gui/qt/components/playlist/media.hpp \
+ gui/qt/components/playlist/playlist_common.cpp \
+ gui/qt/components/playlist/playlist_common.hpp \
+ gui/qt/components/playlist/playlist_controler.cpp \
+ gui/qt/components/playlist/playlist_controler.hpp \
+ gui/qt/components/playlist/playlist_controler_p.hpp \
gui/qt/components/playlist/playlist_item.cpp \
gui/qt/components/playlist/playlist_item.hpp \
+ gui/qt/components/playlist/playlist_model.cpp \
+ gui/qt/components/playlist/playlist_model.hpp \
+ gui/qt/components/playlist/playlist_model_p.hpp \
gui/qt/components/sout/profile_selector.cpp \
gui/qt/components/sout/profile_selector.hpp \
gui/qt/components/sout/sout_widgets.cpp \
@@ -213,7 +222,10 @@ nodist_libqt_plugin_la_SOURCES = \
gui/qt/components/epg/EPGRuler.moc.cpp \
gui/qt/components/epg/EPGView.moc.cpp \
gui/qt/components/epg/EPGWidget.moc.cpp \
+ gui/qt/components/playlist/playlist_common.moc.cpp \
+ gui/qt/components/playlist/playlist_item.moc.cpp \
gui/qt/components/playlist/playlist_model.moc.cpp \
+ gui/qt/components/playlist/playlist_controler.moc.cpp \
gui/qt/components/sout/profile_selector.moc.cpp \
gui/qt/components/sout/sout_widgets.moc.cpp \
gui/qt/util/animators.moc.cpp \
diff --git a/modules/gui/qt/components/playlist/media.hpp b/modules/gui/qt/components/playlist/media.hpp
new file mode 100644
index 0000000000..7fb2867868
--- /dev/null
+++ b/modules/gui/qt/components/playlist/media.hpp
@@ -0,0 +1,101 @@
+/*****************************************************************************
+ * Copyright (C) 2019 VLC authors and VideoLAN
+ *
+ * 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 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>
+#include <QStringList>
+#include "util/qt_dirs.hpp"
+
+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, QStringList* options = nullptr)
+ {
+ 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();
+
+ if (options && options->count() > 0)
+ {
+ char **ppsz_options = NULL;
+ int i_options = 0;
+
+ ppsz_options = new char *[options->count()];
+ auto optionDeleter = vlc::wrap_carray<char*>(ppsz_options, [&i_options](char *ptr[]) {
+ for(int i = 0; i < i_options; i++)
+ free(ptr[i]);
+ delete[] ptr;
+ });
+
+ for (int i = 0; i < options->count(); i++)
+ {
+ QString option = colon_unescape( options->at(i) );
+ ppsz_options[i] = strdup(option.toUtf8().constData());
+ if (!ppsz_options[i])
+ throw std::bad_alloc();
+ i_options++;
+ }
+ input_item_AddOptions( ptr.get(), i_options, ppsz_options, VLC_INPUT_OPTION_TRUSTED );
+ }
+ }
+
+ operator bool() const
+ {
+ return static_cast<bool>(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/playlist_common.cpp b/modules/gui/qt/components/playlist/playlist_common.cpp
new file mode 100644
index 0000000000..c7b6a20ca2
--- /dev/null
+++ b/modules/gui/qt/components/playlist/playlist_common.cpp
@@ -0,0 +1,40 @@
+/*****************************************************************************
+ * Copyright (C) 2019 VLC authors and VideoLAN
+ *
+ * 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 "playlist_common.hpp"
+
+
+PlaylistPtr::PlaylistPtr(QObject* parent)
+ : m_playlist(nullptr)
+{}
+
+PlaylistPtr::PlaylistPtr(vlc_playlist_t* pl, QObject* parent)
+ : m_playlist(pl)
+{}
+
+PlaylistPtr::PlaylistPtr(const PlaylistPtr& ptr)
+ : m_playlist(ptr.m_playlist)
+{
+}
+
+PlaylistPtr&PlaylistPtr::operator=(const PlaylistPtr& ptr)
+{
+ this->m_playlist = ptr.m_playlist;
+ return *this;
+}
+
diff --git a/modules/gui/qt/components/playlist/playlist_common.hpp b/modules/gui/qt/components/playlist/playlist_common.hpp
new file mode 100644
index 0000000000..2a5161cd88
--- /dev/null
+++ b/modules/gui/qt/components/playlist/playlist_common.hpp
@@ -0,0 +1,58 @@
+/*****************************************************************************
+ * Copyright (C) 2019 VLC authors and VideoLAN
+ *
+ * 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 PLAYLIST_COMMON_HPP
+#define PLAYLIST_COMMON_HPP
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <QObject>
+#include <vlc_playlist.h>
+
+// QObject wrapper to carry playlist ptr through QML
+class PlaylistPtr
+{
+ Q_GADGET
+public:
+ PlaylistPtr(QObject* parent = nullptr);
+ PlaylistPtr(vlc_playlist_t* pl, QObject* parent = nullptr);
+ PlaylistPtr(const PlaylistPtr& ptr);
+ PlaylistPtr& operator=(const PlaylistPtr& ptr);
+
+ vlc_playlist_t* m_playlist = nullptr;
+};
+
+class PlaylistLocker
+{
+public:
+ inline PlaylistLocker(vlc_playlist_t* playlist)
+ : m_playlist(playlist)
+ {
+ vlc_playlist_Lock(m_playlist);
+ }
+
+ inline ~PlaylistLocker()
+ {
+ vlc_playlist_Unlock(m_playlist);
+ }
+
+ vlc_playlist_t* m_playlist;
+};
+
+#endif // PLAYLIST_COMMON_HPP
diff --git a/modules/gui/qt/components/playlist/playlist_controler.cpp b/modules/gui/qt/components/playlist/playlist_controler.cpp
new file mode 100644
index 0000000000..6b7906fcd0
--- /dev/null
+++ b/modules/gui/qt/components/playlist/playlist_controler.cpp
@@ -0,0 +1,667 @@
+/*****************************************************************************
+ * Copyright (C) 2019 VLC authors and VideoLAN
+ *
+ * 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.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "playlist_controler.hpp"
+#include "playlist_controler_p.hpp"
+#include "vlc_player.h"
+#include <algorithm>
+#include <QVariant>
+
+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;
+}
+
+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;
+}
+
+
+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)
+{
+ PlaylistControlerModelPrivate *that = static_cast<PlaylistControlerModelPrivate *>(userdata);
+ auto vec = toVec(items, len);
+ size_t totalCount = vlc_playlist_Count(playlist);
+
+ that->callAsync([=](){
+ if (that->m_playlist != playlist)
+ return;
+ PlaylistControlerModel* q = that->q_func();
+ bool empty = vec.size() == 0;
+ if (that->m_empty != empty)
+ {
+ that->m_empty = empty;
+ emit q->isEmptyChanged(empty);
+ }
+ emit q->itemsReset(vec);
+
+ if (totalCount != that->m_count)
+ {
+ that->m_count = totalCount;
+ emit q->countChanged(totalCount);
+ }
+ });
+}
+
+static void
+on_playlist_items_added(vlc_playlist_t *playlist, size_t index,
+ vlc_playlist_item_t *const items[], size_t len,
+ void *userdata)
+{
+ PlaylistControlerModelPrivate *that = static_cast<PlaylistControlerModelPrivate *>(userdata);
+ auto vec = toVec(items, len);
+ size_t totalCount = vlc_playlist_Count(playlist);
+ that->callAsync([=](){
+ if (that->m_playlist != playlist)
+ return;
+ PlaylistControlerModel* q = that->q_func();
+ if (that->m_empty && vec.size() > 0)
+ {
+ that->m_empty = false;
+ emit q->isEmptyChanged(that->m_empty);
+ }
+ emit q->itemsAdded(index, vec);
+
+ if (totalCount != that->m_count)
+ {
+ that->m_count = totalCount;
+ emit q->countChanged(totalCount);
+ }
+ });
+}
+
+static void
+on_playlist_items_moved(vlc_playlist_t *playlist, size_t index, size_t count,
+ size_t target, void *userdata)
+{
+ PlaylistControlerModelPrivate *that = static_cast<PlaylistControlerModelPrivate *>(userdata);
+ that->callAsync([=](){
+ if (that->m_playlist != playlist)
+ return;
+ emit that->q_func()->itemsMoved(index, count, target);
+ });
+}
+
+static void
+on_playlist_items_removed(vlc_playlist_t *playlist, size_t index, size_t count,
+ void *userdata)
+{
+ PlaylistControlerModelPrivate *that = static_cast<PlaylistControlerModelPrivate *>(userdata);
+ size_t totalCount = vlc_playlist_Count(playlist);
+ bool empty = (totalCount == 0);
+ that->callAsync([=](){
+ if (that->m_playlist != playlist)
+ return;
+ PlaylistControlerModel* q = that->q_func();
+ if (that->m_empty != empty)
+ {
+ that->m_empty = empty;
+ emit q->isEmptyChanged(empty);
+ }
+ emit q->itemsRemoved(index, count);
+ if (totalCount != that->m_count)
+ {
+ that->m_count = totalCount;
+ emit q->countChanged(totalCount);
+ }
+ });
+}
+
+static void
+on_playlist_items_updated(vlc_playlist_t *playlist, size_t index,
+ vlc_playlist_item_t *const items[], size_t len,
+ void *userdata)
+{
+ PlaylistControlerModelPrivate *that = static_cast<PlaylistControlerModelPrivate *>(userdata);
+ auto vec = toVec(items, len);
+ that->callAsync([=](){
+ if (that->m_playlist != playlist)
+ return;
+ emit that->q_func()->itemsUpdated(index, vec);
+ });
+}
+
+static void
+on_playlist_playback_repeat_changed(vlc_playlist_t *playlist,
+ enum vlc_playlist_playback_repeat repeat,
+ void *userdata)
+{
+ PlaylistControlerModelPrivate *that = static_cast<PlaylistControlerModelPrivate *>(userdata);
+ that->callAsync([=](){
+ if (that->m_playlist != playlist)
+ return;
+ PlaylistControlerModel::PlaybackRepeat repeatMode = static_cast<PlaylistControlerModel::PlaybackRepeat>(repeat);
+ if (that->m_repeat != repeatMode )
+ {
+ that->m_repeat = repeatMode;
+ emit that->q_func()->repeatModeChanged(repeatMode);
+ }
+ });
+}
+
+static void
+on_playlist_playback_order_changed(vlc_playlist_t *playlist,
+ enum vlc_playlist_playback_order order,
+ void *userdata)
+{
+ PlaylistControlerModelPrivate *that = static_cast<PlaylistControlerModelPrivate *>(userdata);
+ that->callAsync([=](){
+ if (that->m_playlist != playlist)
+ return;
+ bool isRandom = order == VLC_PLAYLIST_PLAYBACK_ORDER_RANDOM;
+ if (that->m_random != isRandom)
+ {
+ that->m_random = isRandom;
+ emit that->q_func()->randomChanged(isRandom);
+ }
+ });
+}
+
+static void
+on_playlist_current_item_changed(vlc_playlist_t *playlist, ssize_t index,
+ void *userdata)
+{
+ PlaylistControlerModelPrivate *that = static_cast<PlaylistControlerModelPrivate *>(userdata);
+
+
+ vlc_playlist_item_t* playlistItem = nullptr;
+ if (index >= 0)
+ playlistItem = vlc_playlist_Get(playlist, index);
+ PlaylistItem newItem{ playlistItem };
+
+ that->callAsync([=](){
+ PlaylistControlerModel* q = that->q_func();
+
+ if (that->m_playlist != playlist)
+ return;
+ if (that->m_currentIndex != index)
+ {
+ that->m_currentIndex = index;
+ emit q->currentIndexChanged(that->m_currentIndex);
+ }
+ that->m_currentItem = newItem;
+ emit q->currentItemChanged();
+ });
+}
+
+static void
+on_playlist_has_prev_changed(vlc_playlist_t *playlist, bool has_prev,
+ void *userdata)
+{
+ PlaylistControlerModelPrivate *that = static_cast<PlaylistControlerModelPrivate *>(userdata);
+ that->callAsync([=](){
+ if (that->m_playlist != playlist)
+ return;
+ if (that->m_hasPrev != has_prev)
+ {
+ that->m_hasPrev = has_prev;
+ emit that->q_func()->hasPrevChanged(has_prev);
+ }
+ });
+}
+
+static void
+on_playlist_has_next_changed(vlc_playlist_t *playlist, bool has_next,
+ void *userdata)
+{
+ PlaylistControlerModelPrivate *that = static_cast<PlaylistControlerModelPrivate *>(userdata);
+ that->callAsync([=](){
+ if (that->m_playlist != playlist)
+ return;
+ if (that->m_hasNext != has_next)
+ {
+ that->m_hasNext = has_next;
+ emit that->q_func()->hasNextChanged(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,
+};
+
+
+//private API
+
+PlaylistControlerModelPrivate::PlaylistControlerModelPrivate(PlaylistControlerModel* playlistControler)
+ : q_ptr(playlistControler)
+{
+}
+
+PlaylistControlerModelPrivate::~PlaylistControlerModelPrivate()
+{
+ if (m_playlist && m_listener)
+ {
+ PlaylistLocker lock(m_playlist);
+ vlc_playlist_RemoveListener(m_playlist, m_listener);
+ }
+}
+
+//public API
+
+PlaylistControlerModel::PlaylistControlerModel(QObject *parent)
+ : QObject(parent)
+ , d_ptr( new PlaylistControlerModelPrivate(this) )
+{
+}
+
+PlaylistControlerModel::PlaylistControlerModel(vlc_playlist_t *playlist, QObject *parent)
+ : QObject(parent)
+ , d_ptr( new PlaylistControlerModelPrivate(this) )
+{
+ setPlaylistPtr(playlist);
+}
+
+PlaylistControlerModel::~PlaylistControlerModel()
+{
+}
+
+PlaylistItem PlaylistControlerModel::getCurrentItem() const
+{
+ Q_D(const PlaylistControlerModel);
+ return d->m_currentItem;
+}
+
+void PlaylistControlerModel::append(const QVariantList& sourceList, bool startPlaying)
+{
+ QVector<Media> mediaList;
+ std::transform(sourceList.begin(), sourceList.end(),
+ std::back_inserter(mediaList), [](const QVariant& value) {
+ if (value.canConvert<QUrl>())
+ {
+ auto mrl = value.value<QUrl>();
+ return Media(mrl.toString(QUrl::None), mrl.fileName());
+ }
+ else if (value.canConvert<QString>())
+ {
+ auto mrl = value.value<QString>();
+ return Media(mrl, mrl);
+ }
+ return Media{};
+ });
+ append(mediaList, startPlaying);
+}
+
+void PlaylistControlerModel::insert(unsigned index, const QVariantList& sourceList, bool startPlaying)
+{
+ QVector<Media> mediaList;
+ std::transform(sourceList.begin(), sourceList.end(),
+ std::back_inserter(mediaList), [](const QVariant& value) {
+ if (value.canConvert<QUrl>())
+ {
+ auto mrl = value.value<QUrl>();
+ return Media(mrl.toString(QUrl::None), mrl.fileName());
+ }
+ else if (value.canConvert<QString>())
+ {
+ auto mrl = value.value<QString>();
+ return Media(mrl, mrl);
+ }
+ return Media{};
+ });
+ insert(index, mediaList, startPlaying);
+}
+
+
+void
+PlaylistControlerModel::append(const QVector<Media> &media, bool startPlaying)
+{
+ Q_D(PlaylistControlerModel);
+
+ PlaylistLocker locker(d->m_playlist);
+
+ auto rawMedia = toRaw<input_item_t *>(media);
+ int ret = vlc_playlist_Append(d->m_playlist,
+ rawMedia.constData(), rawMedia.size());
+ if (ret != VLC_SUCCESS)
+ throw std::bad_alloc();
+ if (startPlaying)
+ {
+ ssize_t playIndex = (ssize_t)vlc_playlist_Count( d->m_playlist ) - rawMedia.size();
+ ret = vlc_playlist_GoTo(d->m_playlist, playIndex);
+ if (ret != VLC_SUCCESS)
+ return;
+ vlc_playlist_Start(d->m_playlist);
+ }
+}
+
+void
+PlaylistControlerModel::insert(size_t index, const QVector<Media> &media, bool startPlaying)
+{
+ Q_D(PlaylistControlerModel);
+ PlaylistLocker locker(d->m_playlist);
+
+ auto rawMedia = toRaw<input_item_t *>(media);
+ int ret = vlc_playlist_RequestInsert(d->m_playlist, index,
+ rawMedia.constData(), rawMedia.size());
+ if (ret != VLC_SUCCESS)
+ throw std::bad_alloc();
+ if (startPlaying)
+ {
+ ret = vlc_playlist_GoTo(d->m_playlist, index);
+ if (ret != VLC_SUCCESS)
+ return;
+ vlc_playlist_Start(d->m_playlist);
+ }
+}
+
+void
+PlaylistControlerModel::move(const QVector<PlaylistItem> &items, size_t target,
+ ssize_t indexHint)
+{
+ Q_D(PlaylistControlerModel);
+ PlaylistLocker locker(d->m_playlist);
+
+ auto rawItems = toRaw<vlc_playlist_item_t *>(items);
+ int ret = vlc_playlist_RequestMove(d->m_playlist, rawItems.constData(),
+ rawItems.size(), target, indexHint);
+ if (ret != VLC_SUCCESS)
+ throw std::bad_alloc();
+}
+
+void
+PlaylistControlerModel::remove(const QVector<PlaylistItem> &items, ssize_t indexHint)
+{
+ Q_D(PlaylistControlerModel);
+ PlaylistLocker locker(d->m_playlist);
+ auto rawItems = toRaw<vlc_playlist_item_t *>(items);
+ int ret = vlc_playlist_RequestRemove(d->m_playlist, rawItems.constData(),
+ rawItems.size(), indexHint);
+ if (ret != VLC_SUCCESS)
+ throw std::bad_alloc();
+}
+
+void
+PlaylistControlerModel::shuffle()
+{
+ Q_D(PlaylistControlerModel);
+ PlaylistLocker locker(d->m_playlist);
+ vlc_playlist_Shuffle(d->m_playlist);
+}
+
+void
+PlaylistControlerModel::sort(const QVector<vlc_playlist_sort_criterion> &criteria)
+{
+ Q_D(PlaylistControlerModel);
+ PlaylistLocker locker(d->m_playlist);
+ vlc_playlist_Sort(d->m_playlist, criteria.constData(), criteria.size());
+}
+
+void PlaylistControlerModel::sort(PlaylistControlerModel::SortKey key, PlaylistControlerModel::SortOrder order)
+{
+ vlc_playlist_sort_criterion crit = {
+ static_cast<vlc_playlist_sort_key>(key) ,
+ static_cast<vlc_playlist_sort_order>(order)
+ };
+ QVector<vlc_playlist_sort_criterion> criteria { crit };
+ sort( criteria );
+}
+
+void PlaylistControlerModel::play()
+{
+ Q_D(PlaylistControlerModel);
+ PlaylistLocker lock{ d->m_playlist };
+ vlc_playlist_Start( d->m_playlist );
+}
+
+void PlaylistControlerModel::pause()
+{
+ Q_D(PlaylistControlerModel);
+ PlaylistLocker lock{ d->m_playlist };
+ vlc_playlist_Pause( d->m_playlist );
+}
+
+void PlaylistControlerModel::stop()
+{
+ Q_D(PlaylistControlerModel);
+ PlaylistLocker lock{ d->m_playlist };
+ vlc_playlist_Stop( d->m_playlist );
+}
+
+void PlaylistControlerModel::next()
+{
+ Q_D(PlaylistControlerModel);
+ PlaylistLocker lock{ d->m_playlist };
+ vlc_playlist_Next( d->m_playlist );
+}
+
+void PlaylistControlerModel::prev()
+{
+ Q_D(PlaylistControlerModel);
+ PlaylistLocker lock{ d->m_playlist };
+ vlc_playlist_Prev( d->m_playlist );
+}
+
+void PlaylistControlerModel::prevOrReset()
+{
+ Q_D(PlaylistControlerModel);
+ PlaylistLocker lock{ d->m_playlist };
+ bool seek = false;
+ vlc_player_t* player = vlc_playlist_GetPlayer( d->m_playlist );
+ Q_ASSERT(player);
+ if( !vlc_player_IsStarted(player) || vlc_player_GetTime(player) < VLC_TICK_FROM_MS(10) )
+ {
+ int ret = vlc_playlist_Prev( d->m_playlist );
+ if (ret == VLC_SUCCESS)
+ vlc_playlist_Start( d->m_playlist );
+ }
+ else
+ seek = true;
+ if (seek)
+ vlc_player_JumpPos( player, 0.f );
+}
+
+void PlaylistControlerModel::togglePlayPause()
+{
+ Q_D(PlaylistControlerModel);
+ PlaylistLocker lock{ d->m_playlist };
+ vlc_player_t* player = vlc_playlist_GetPlayer( d->m_playlist );
+ if ( vlc_player_IsStarted(player) )
+ vlc_player_TogglePause( player );
+ else
+ vlc_playlist_Start( d->m_playlist );
+}
+
+void PlaylistControlerModel::toggleRandom()
+{
+ Q_D(PlaylistControlerModel);
+ vlc_playlist_playback_order new_order;
+ PlaylistLocker lock{ d->m_playlist };
+ if ( d->m_random )
+ new_order = VLC_PLAYLIST_PLAYBACK_ORDER_NORMAL;
+ else
+ new_order = VLC_PLAYLIST_PLAYBACK_ORDER_RANDOM;
+ vlc_playlist_SetPlaybackOrder( d->m_playlist, new_order );
+ config_PutInt( "random", new_order );
+}
+
+void PlaylistControlerModel::toggleRepeatMode()
+{
+ Q_D(PlaylistControlerModel);
+ vlc_playlist_playback_repeat new_repeat;
+ /* Toggle Normal -> Loop -> Repeat -> Normal ... */
+ switch ( d->m_repeat ) {
+ case VLC_PLAYLIST_PLAYBACK_REPEAT_NONE:
+ new_repeat = VLC_PLAYLIST_PLAYBACK_REPEAT_ALL;
+ break;
+ case VLC_PLAYLIST_PLAYBACK_REPEAT_ALL:
+ new_repeat = VLC_PLAYLIST_PLAYBACK_REPEAT_CURRENT;
+ break;
+ case VLC_PLAYLIST_PLAYBACK_REPEAT_CURRENT:
+ default:
+ new_repeat = VLC_PLAYLIST_PLAYBACK_REPEAT_NONE;
+ break;
+ }
+ {
+ PlaylistLocker lock{ d->m_playlist };
+ vlc_playlist_SetPlaybackRepeat( d->m_playlist, new_repeat );
+ }
+ config_PutInt( "repeat", new_repeat );
+}
+
+void PlaylistControlerModel::clear()
+{
+ Q_D(PlaylistControlerModel);
+ PlaylistLocker lock{ d->m_playlist };
+ vlc_playlist_Clear( d->m_playlist );
+}
+
+void PlaylistControlerModel::goTo(uint index, bool startPlaying)
+{
+ Q_D(PlaylistControlerModel);
+ PlaylistLocker lock{ d->m_playlist };
+ if (index > vlc_playlist_Count( d->m_playlist ) -1)
+ return;
+ vlc_playlist_GoTo( d->m_playlist, index );
+ if (startPlaying)
+ vlc_playlist_Start( d->m_playlist );
+}
+
+bool PlaylistControlerModel::isRandom() const
+{
+ Q_D(const PlaylistControlerModel);
+ return d->m_random;
+}
+
+void PlaylistControlerModel::setRandom(bool random)
+{
+ Q_D(const PlaylistControlerModel);
+ PlaylistLocker lock{ d->m_playlist };
+ vlc_playlist_SetPlaybackOrder( d->m_playlist, random ? VLC_PLAYLIST_PLAYBACK_ORDER_RANDOM : VLC_PLAYLIST_PLAYBACK_ORDER_NORMAL );
+}
+
+PlaylistPtr PlaylistControlerModel::getPlaylistPtr() const
+{
+ Q_D(const PlaylistControlerModel);
+ return PlaylistPtr(d->m_playlist);
+}
+
+void PlaylistControlerModel::setPlaylistPtr(vlc_playlist_t* newPlaylist)
+{
+ Q_D(PlaylistControlerModel);
+ if (d->m_playlist && d->m_listener)
+ {
+ PlaylistLocker locker(d->m_playlist);
+ vlc_playlist_RemoveListener(d->m_playlist, d->m_listener);
+ d->m_playlist = nullptr;
+ d->m_listener = nullptr;
+ }
+ if (newPlaylist)
+ {
+ PlaylistLocker locker(newPlaylist);
+ d->m_playlist = newPlaylist;
+ d->m_listener = vlc_playlist_AddListener(d->m_playlist, &playlist_callbacks, d, true);
+ }
+ emit playlistPtrChanged( PlaylistPtr(newPlaylist) );
+}
+
+void PlaylistControlerModel::setPlaylistPtr(PlaylistPtr ptr)
+{
+ setPlaylistPtr(ptr.m_playlist);
+}
+
+PlaylistControlerModel::PlaybackRepeat PlaylistControlerModel::getRepeatMode() const
+{
+ Q_D(const PlaylistControlerModel);
+ return d->m_repeat;
+}
+
+void PlaylistControlerModel::setRepeatMode(PlaylistControlerModel::PlaybackRepeat mode)
+{
+ Q_D(const PlaylistControlerModel);
+ PlaylistLocker lock{ d->m_playlist };
+ vlc_playlist_SetPlaybackRepeat( d->m_playlist, static_cast<vlc_playlist_playback_repeat>(mode) );
+}
+
+
+bool PlaylistControlerModel::isPlayAndExit() const
+{
+ Q_D(const PlaylistControlerModel);
+ return d->m_isPlayAndExit;
+}
+
+void PlaylistControlerModel::setPlayAndExit(bool enable)
+{
+ Q_D(PlaylistControlerModel);
+ PlaylistLocker lock{ d->m_playlist };
+ vlc_player_t* player = vlc_playlist_GetPlayer( d->m_playlist );
+ vlc_player_SetMediaStoppedAction( player, enable ? VLC_PLAYER_MEDIA_STOPPED_EXIT : VLC_PLAYER_MEDIA_STOPPED_CONTINUE );
+}
+
+bool PlaylistControlerModel::isEmpty() const
+{
+ Q_D(const PlaylistControlerModel);
+ return d->m_empty;
+}
+
+size_t PlaylistControlerModel::count() const
+{
+ Q_D(const PlaylistControlerModel);
+ return d->m_count;
+}
+
+bool PlaylistControlerModel::hasNext() const
+{
+ Q_D(const PlaylistControlerModel);
+ return d->m_hasNext;
+}
+
+bool PlaylistControlerModel::hasPrev() const
+{
+ Q_D(const PlaylistControlerModel);
+ return d->m_hasPrev;
+}
+
+
+ } // namespace playlist
+} // namespace vlc
diff --git a/modules/gui/qt/components/playlist/playlist_controler.hpp b/modules/gui/qt/components/playlist/playlist_controler.hpp
new file mode 100644
index 0000000000..a14def350b
--- /dev/null
+++ b/modules/gui/qt/components/playlist/playlist_controler.hpp
@@ -0,0 +1,167 @@
+/*****************************************************************************
+ * Copyright (C) 2019 VLC authors and VideoLAN
+ *
+ * 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 VLC_QT_PLAYLIST_NEW_HPP_
+#define VLC_QT_PLAYLIST_NEW_HPP_
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <QObject>
+#include <QVector>
+#include "media.hpp"
+#include "playlist_common.hpp"
+#include "playlist_item.hpp"
+
+namespace vlc {
+ namespace playlist {
+
+class PlaylistControlerModelPrivate;
+class PlaylistControlerModel : public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(PlaylistControlerModel)
+
+public:
+ enum PlaybackRepeat
+ {
+ PLAYBACK_REPEAT_NONE = VLC_PLAYLIST_PLAYBACK_REPEAT_NONE,
+ PLAYBACK_REPEAT_CURRENT = VLC_PLAYLIST_PLAYBACK_REPEAT_CURRENT,
+ PLAYBACK_REPEAT_ALL = VLC_PLAYLIST_PLAYBACK_REPEAT_ALL
+ };
+ Q_ENUM(PlaybackRepeat)
+
+ enum SortKey
+ {
+ SORT_KEY_TITLE = VLC_PLAYLIST_SORT_KEY_TITLE,
+ SORT_KEY_DURATION = VLC_PLAYLIST_SORT_KEY_DURATION,
+ SORT_KEY_ARTIST = VLC_PLAYLIST_SORT_KEY_ARTIST,
+ SORT_KEY_ALBUM = VLC_PLAYLIST_SORT_KEY_ALBUM,
+ SORT_KEY_ALBUM_ARTIST = VLC_PLAYLIST_SORT_KEY_ALBUM_ARTIST,
+ SORT_KEY_GENRE = VLC_PLAYLIST_SORT_KEY_GENRE,
+ SORT_KEY_DATE = VLC_PLAYLIST_SORT_KEY_DATE,
+ SORT_KEY_TRACK_NUMBER = VLC_PLAYLIST_SORT_KEY_TRACK_NUMBER,
+ SORT_KEY_DISC_NUMBER = VLC_PLAYLIST_SORT_KEY_DISC_NUMBER,
+ SORT_KEY_URL = VLC_PLAYLIST_SORT_KEY_URL,
+ SORT_KEY_RATIN = VLC_PLAYLIST_SORT_KEY_RATING
+ };
+ Q_ENUM(SortKey)
+
+ enum SortOrder
+ {
+ SORT_ORDER_ASC = VLC_PLAYLIST_SORT_ORDER_ASCENDING,
+ SORT_ORDER_DESC = VLC_PLAYLIST_SORT_ORDER_DESCENDING,
+ };
+ Q_ENUM(SortOrder)
+
+ Q_PROPERTY(PlaylistPtr playlistPtr READ getPlaylistPtr WRITE setPlaylistPtr NOTIFY playlistPtrChanged)
+
+ Q_PROPERTY(PlaylistItem currentItem READ getCurrentItem NOTIFY currentItemChanged)
+
+ Q_PROPERTY(bool hasNext READ hasNext NOTIFY hasNextChanged)
+ Q_PROPERTY(bool hasPrev READ hasPrev NOTIFY hasPrevChanged)
+ Q_PROPERTY(bool random READ isRandom WRITE setRandom NOTIFY randomChanged )
+ Q_PROPERTY(PlaybackRepeat repeatMode READ getRepeatMode WRITE setRepeatMode NOTIFY repeatModeChanged)
+ Q_PROPERTY(bool playAndExit READ isPlayAndExit WRITE setPlayAndExit NOTIFY playAndExitChanged)
+ Q_PROPERTY(bool empty READ isEmpty NOTIFY isEmptyChanged)
+ Q_PROPERTY(size_t count READ count NOTIFY countChanged)
+
+public:
+ Q_INVOKABLE void play();
+ Q_INVOKABLE void pause();
+ Q_INVOKABLE void stop();
+ Q_INVOKABLE void next();
+ Q_INVOKABLE void prev();
+ Q_INVOKABLE void prevOrReset();
+ Q_INVOKABLE void togglePlayPause();
+
+ Q_INVOKABLE void toggleRandom();
+ Q_INVOKABLE void toggleRepeatMode();
+
+ Q_INVOKABLE void clear();
+ Q_INVOKABLE void goTo(uint index, bool startPlaying = false);
+
+ Q_INVOKABLE void append(const QVariantList&, bool startPlaying = false);
+ Q_INVOKABLE void insert(unsigned index, const QVariantList&, bool startPlaying = false);
+
+ void append(const QVector<Media> &, bool startPlaying = false);
+ void insert(size_t index, const QVector<Media> &, bool startPlaying = false);
+ void move(const QVector<PlaylistItem> &, size_t target, ssize_t indexHint);
+ void remove(const QVector<PlaylistItem> &, ssize_t indexHint);
+
+ Q_INVOKABLE void shuffle();
+ void sort(const QVector<vlc_playlist_sort_criterion> &);
+ Q_INVOKABLE void sort(SortKey key, SortOrder order);
+
+public:
+ PlaylistControlerModel(QObject *parent = nullptr);
+ PlaylistControlerModel(vlc_playlist_t *playlist, QObject *parent = nullptr);
+ virtual ~PlaylistControlerModel();
+
+public slots:
+ PlaylistItem getCurrentItem() const;
+
+ bool hasNext() const;
+ bool hasPrev() const;
+
+ bool isRandom() const;
+ void setRandom( bool );
+
+ PlaybackRepeat getRepeatMode() const;
+ void setRepeatMode( PlaybackRepeat mode );
+
+ bool isPlayAndExit() const;
+ void setPlayAndExit(bool );
+
+ bool isEmpty() const;
+ size_t count() const;
+
+ PlaylistPtr getPlaylistPtr() const;
+ void setPlaylistPtr(PlaylistPtr id);
+ void setPlaylistPtr(vlc_playlist_t* newPlaylist);
+
+signals:
+ void playlistPtrChanged( PlaylistPtr );
+
+ void currentItemChanged( );
+
+ void hasNextChanged( bool );
+ void hasPrevChanged( bool );
+ void randomChanged( bool );
+ void playAndExitChanged( bool );
+ void repeatModeChanged( PlaybackRepeat );
+ void isEmptyChanged( bool empty );
+ void countChanged(size_t );
+
+ void currentIndexChanged(ssize_t index);
+ void itemsReset(QVector<PlaylistItem>);
+ void itemsAdded(size_t index, QVector<PlaylistItem>);
+ void itemsMoved(size_t index, size_t count, size_t target);
+ void itemsRemoved(size_t index, size_t count);
+ void itemsUpdated(size_t index, QVector<PlaylistItem>);
+
+private:
+ Q_DECLARE_PRIVATE(PlaylistControlerModel)
+ QScopedPointer<PlaylistControlerModelPrivate> d_ptr;
+};
+
+ } // namespace playlist
+} // namespace vlc
+
+#endif
diff --git a/modules/gui/qt/components/playlist/playlist_controler_p.hpp b/modules/gui/qt/components/playlist/playlist_controler_p.hpp
new file mode 100644
index 0000000000..5e44b0a294
--- /dev/null
+++ b/modules/gui/qt/components/playlist/playlist_controler_p.hpp
@@ -0,0 +1,69 @@
+/*****************************************************************************
+ * Copyright (C) 2019 VLC authors and VideoLAN
+ *
+ * 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 PLAYLIST_CONTROLER_P_HPP
+#define PLAYLIST_CONTROLER_P_HPP
+
+#include "playlist_controler.hpp"
+
+namespace vlc {
+namespace playlist {
+
+class PlaylistControlerModelPrivate
+{
+ Q_DISABLE_COPY(PlaylistControlerModelPrivate)
+public:
+ Q_DECLARE_PUBLIC(PlaylistControlerModel)
+ PlaylistControlerModel * const q_ptr;
+
+public:
+ PlaylistControlerModelPrivate(PlaylistControlerModel* playlistControler);
+ PlaylistControlerModelPrivate() = delete;
+ ~PlaylistControlerModelPrivate();
+
+ ///call function @a fun on object thread
+ template <typename Fun>
+ inline void callAsync(Fun&& fun)
+ {
+ Q_Q(PlaylistControlerModel);
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
+ QMetaObject::invokeMethod(q, std::forward<Fun>(fun), Qt::QueuedConnection, nullptr);
+#else
+ QObject src;
+ QObject::connect(&src, &QObject::destroyed, q, std::forward<Fun>(fun), Qt::QueuedConnection);
+#endif
+ }
+
+ //playlist
+ vlc_playlist_t* m_playlist = nullptr;
+ vlc_playlist_listener_id* m_listener = nullptr;
+
+ ssize_t m_currentIndex = -1;
+ PlaylistItem m_currentItem;
+ bool m_hasNext= false;
+ bool m_hasPrev = false;
+ PlaylistControlerModel::PlaybackRepeat m_repeat = PlaylistControlerModel::PLAYBACK_REPEAT_NONE;
+ bool m_random = false;
+ bool m_isPlayAndExit;
+ bool m_empty = true;
+ size_t m_count = 0;
+};
+
+} //namespace playlist
+} //namespace vlc
+
+#endif // PLAYLIST_CONTROLER_P_HPP
diff --git a/modules/gui/qt/components/playlist/playlist_item.cpp b/modules/gui/qt/components/playlist/playlist_item.cpp
new file mode 100644
index 0000000000..8281daa059
--- /dev/null
+++ b/modules/gui/qt/components/playlist/playlist_item.cpp
@@ -0,0 +1,79 @@
+/*****************************************************************************
+ * Copyright (C) 2019 VLC authors and VideoLAN
+ *
+ * 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 "playlist_item.hpp"
+
+//namespace vlc {
+//namespace playlist {
+
+PlaylistItem::PlaylistItem(vlc_playlist_item_t* item)
+{
+ d = new Data();
+ if (item)
+ {
+ d->item.reset(item);
+ sync();
+ }
+}
+
+QString PlaylistItem::getTitle() const
+{
+ return d->title;
+}
+
+QString PlaylistItem::getArtist() const
+{
+ return d->artist;
+}
+
+QString PlaylistItem::getAlbum() const
+{
+ return d->album;
+}
+
+QUrl PlaylistItem::getArtwork() const
+{
+ return d->artwork;
+}
+
+vlc_tick_t PlaylistItem::getDuration() const
+{
+ return d->duration;
+}
+
+void PlaylistItem::sync() {
+ input_item_t *media = vlc_playlist_item_GetMedia(d->item.get());
+ vlc_mutex_lock(&media->lock);
+ d->title = media->psz_name;
+ d->duration = media->i_duration;
+
+ if (media->p_meta) {
+ d->artist = vlc_meta_Get(media->p_meta, vlc_meta_Artist);
+ d->album = vlc_meta_Get(media->p_meta, vlc_meta_Album);
+ d->artwork = vlc_meta_Get(media->p_meta, vlc_meta_ArtworkURL);
+ }
+ vlc_mutex_unlock(&media->lock);
+}
+
+PlaylistItem::operator bool() const
+{
+ return d;
+}
+
+
+//}
+//}
diff --git a/modules/gui/qt/components/playlist/playlist_item.hpp b/modules/gui/qt/components/playlist/playlist_item.hpp
new file mode 100644
index 0000000000..e10d2a3b08
--- /dev/null
+++ b/modules/gui/qt/components/playlist/playlist_item.hpp
@@ -0,0 +1,103 @@
+/*****************************************************************************
+ * Copyright (C) 2019 VLC authors and VideoLAN
+ *
+ * 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 VLC_QT_PLAYLIST_NEW_ITEM_HPP_
+#define VLC_QT_PLAYLIST_NEW_ITEM_HPP_
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "vlc_player.h"
+#include <vlc_cxx_helpers.hpp>
+#include <vlc_input_item.h>
+#include <vlc_playlist.h>
+#include <QExplicitlySharedDataPointer>
+#include <QUrl>
+#include <QMetaType>
+
+
+//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
+{
+ Q_GADGET
+public:
+ Q_PROPERTY(QString title READ getTitle CONSTANT )
+ Q_PROPERTY(QString artist READ getArtist CONSTANT )
+ Q_PROPERTY(QString album READ getAlbum CONSTANT )
+ Q_PROPERTY(QUrl artwork READ getArtwork CONSTANT )
+ Q_PROPERTY(vlc_tick_t duration READ getDuration CONSTANT )
+
+ PlaylistItem(vlc_playlist_item_t *item = nullptr);
+
+ operator bool() const;
+
+ vlc_playlist_item_t *raw() const {
+ return d ? d->item.get() : nullptr;
+ }
+
+ QString getTitle() const;
+
+ QString getArtist() const;
+
+ QString getAlbum() const;
+
+ QUrl getArtwork() const;
+
+ vlc_tick_t getDuration() const;
+
+
+ void sync();
+
+private:
+ struct Data : public QSharedData {
+ PlaylistItemPtr item;
+
+ /* cached values */
+ QString title;
+ QString artist;
+ QString album;
+ QUrl artwork;
+
+ vlc_tick_t duration;
+ };
+
+ 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(PlaylistItem)
+
+#endif
diff --git a/modules/gui/qt/components/playlist/playlist_model.cpp b/modules/gui/qt/components/playlist/playlist_model.cpp
new file mode 100644
index 0000000000..add1ff1501
--- /dev/null
+++ b/modules/gui/qt/components/playlist/playlist_model.cpp
@@ -0,0 +1,418 @@
+/*****************************************************************************
+ * Copyright (C) 2019 VLC authors and VideoLAN
+ *
+ * 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.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "playlist_model.hpp"
+#include "playlist_model_p.hpp"
+#include <algorithm>
+#include <assert.h>
+
+namespace vlc {
+namespace playlist {
+
+namespace {
+
+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;
+}
+
+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;
+}
+}
+
+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)
+{
+ PlaylistListModelPrivate *that = static_cast<PlaylistListModelPrivate *>(userdata);
+ QVector<PlaylistItem> newContent = toVec(items, len);
+ that->callAsync([=]() {
+ if (that->m_playlist != playlist)
+ return;
+ that->onItemsReset(newContent);
+ });
+}
+
+static void
+on_playlist_items_added(vlc_playlist_t *playlist, size_t index,
+ vlc_playlist_item_t *const items[], size_t len,
+ void *userdata)
+{
+ PlaylistListModelPrivate *that = static_cast<PlaylistListModelPrivate *>(userdata);
+ QVector<PlaylistItem> added = toVec(items, len);
+ that->callAsync([=]() {
+ if (that->m_playlist != playlist)
+ return;
+ that->onItemsAdded(added, index);
+ });
+}
+
+static void
+on_playlist_items_moved(vlc_playlist_t *playlist, size_t index, size_t count,
+ size_t target, void *userdata)
+{
+ PlaylistListModelPrivate *that = static_cast<PlaylistListModelPrivate *>(userdata);
+ that->callAsync([=]() {
+ if (that->m_playlist != playlist)
+ return;
+ that->onItemsMoved(index, count, target);
+ });
+}
+
+static void
+on_playlist_items_removed(vlc_playlist_t *playlist, size_t index, size_t count,
+ void *userdata)
+{
+ PlaylistListModelPrivate *that = static_cast<PlaylistListModelPrivate *>(userdata);
+ that->callAsync([=](){
+ if (that->m_playlist != playlist)
+ return;
+ that->onItemsRemoved(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)
+{
+ PlaylistListModelPrivate *that = static_cast<PlaylistListModelPrivate *>(userdata);
+ QVector<PlaylistItem> updated = toVec(items, len);
+ that->callAsync([=](){
+ if (that->m_playlist != playlist)
+ return;
+ int count = updated.size();
+ for (int i = 0; i < count; ++i)
+ that->m_items[index + i] = updated[i]; /* sync metadata */
+ that->notifyItemsChanged(index, count);
+ });
+}
+
+static void
+on_playlist_current_item_changed(vlc_playlist_t *playlist, ssize_t index,
+ void *userdata)
+{
+ PlaylistListModelPrivate *that = static_cast<PlaylistListModelPrivate *>(userdata);
+ that->callAsync([=](){
+ if (that->m_playlist != playlist)
+ return;
+ ssize_t oldCurrent = that->m_current;
+ that->m_current = index;
+ if (oldCurrent != -1)
+ that->notifyItemsChanged(oldCurrent, 1, {PlaylistListModel::IsCurrentRole});
+ if (index != -1)
+ that->notifyItemsChanged(index, 1, {PlaylistListModel::IsCurrentRole});
+ emit that->q_func()->currentIndexChanged(index);
+ });
+}
+
+} // 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,
+ nullptr,
+ nullptr,
+ on_playlist_current_item_changed,
+ nullptr,
+ nullptr,
+};
+
+// private API
+
+PlaylistListModelPrivate::PlaylistListModelPrivate(PlaylistListModel* playlistListModel)
+ : q_ptr(playlistListModel)
+{
+}
+
+PlaylistListModelPrivate::~PlaylistListModelPrivate()
+{
+ if (m_playlist && m_listener)
+ {
+ PlaylistLocker locker(m_playlist);
+ vlc_playlist_RemoveListener(m_playlist, m_listener);
+ }
+}
+
+void PlaylistListModelPrivate::onItemsReset(const QVector<PlaylistItem>& newContent)
+{
+ Q_Q(PlaylistListModel);
+ q->beginResetModel();
+ m_items = newContent;
+ q->endResetModel();
+}
+
+void PlaylistListModelPrivate::onItemsAdded(const QVector<PlaylistItem>& added, size_t index)
+{
+ Q_Q(PlaylistListModel);
+ int count = added.size();
+ q->beginInsertRows({}, index, index + count - 1);
+ m_items.insert(index, count, nullptr);
+ std::move(added.cbegin(), added.cend(), m_items.begin() + index);
+ q->endInsertRows();
+}
+
+void PlaylistListModelPrivate::onItemsMoved(size_t index, size_t count, size_t target)
+{
+ Q_Q(PlaylistListModel);
+ 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;
+
+ q->beginMoveRows({}, index, index + count - 1, {}, qtTarget);
+ if (index < target)
+ std::rotate(m_items.begin() + index,
+ m_items.begin() + index + count,
+ m_items.begin() + target + count);
+ else
+ std::rotate(m_items.begin() + target,
+ m_items.begin() + index,
+ m_items.begin() + index + count);
+ q->endMoveRows();
+}
+
+void PlaylistListModelPrivate::onItemsRemoved(size_t index, size_t count)
+{
+ Q_Q(PlaylistListModel);
+ q->beginRemoveRows({}, index, index + count - 1);
+ m_items.remove(index, count);
+ q->endRemoveRows();
+}
+
+
+void
+PlaylistListModelPrivate::notifyItemsChanged(int idx, int count, const QVector<int> &roles)
+{
+ Q_Q(PlaylistListModel);
+ QModelIndex first = q->index(idx, 0);
+ QModelIndex last = q->index(idx + count - 1);
+ emit q->dataChanged(first, last, roles);
+}
+
+// public API
+
+PlaylistListModel::PlaylistListModel(QObject *parent)
+ : QAbstractListModel(parent)
+ , d_ptr(new PlaylistListModelPrivate(this))
+{
+}
+
+PlaylistListModel::PlaylistListModel(vlc_playlist_t *raw_playlist, QObject *parent)
+ : QAbstractListModel(parent)
+ , d_ptr(new PlaylistListModelPrivate(this))
+{
+ setPlaylistId(PlaylistPtr(raw_playlist));
+}
+
+PlaylistListModel::~PlaylistListModel()
+{
+}
+
+QHash<int, QByteArray>
+PlaylistListModel::roleNames() const
+{
+ return {
+ { TitleRole, "title" },
+ { DurationRole, "duration" },
+ { IsCurrentRole, "isCurrent" },
+ { ArtistRole , "artist" },
+ { AlbumRole , "album" },
+ { ArtworkRole, "artwork" },
+ };
+}
+
+int
+PlaylistListModel::rowCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent);
+ Q_D(const PlaylistListModel);
+ if (! d->m_playlist)
+ return 0;
+ return d->m_items.size();
+}
+
+const PlaylistItem &
+PlaylistListModel::itemAt(int index) const
+{
+ Q_D(const PlaylistListModel);
+ return d->m_items[index];
+}
+
+void PlaylistListModel::removeItems(const QList<int>& indexes)
+{
+ Q_D(PlaylistListModel);
+ if (!d->m_playlist)
+ return;
+ if (indexes.size() == 0)
+ return;
+ QVector<vlc_playlist_item_t *> itemsToRemove;
+ std::transform(indexes.begin(), indexes.end(),std::back_inserter(itemsToRemove), [&] (int index) {
+ return d->m_items[index].raw();
+ });
+
+ {
+ PlaylistLocker locker(d->m_playlist);
+ int ret = vlc_playlist_RequestRemove(d->m_playlist, itemsToRemove.constData(),
+ itemsToRemove.size(), indexes[0]);
+ if (ret != VLC_SUCCESS)
+ throw std::bad_alloc();
+ }
+}
+
+void PlaylistListModel::moveItems(const QList<int> &indexes, int target)
+{
+ Q_D(PlaylistListModel);
+ if (!d->m_playlist)
+ return;
+ int targetPre = target;
+ int targetPost = target;
+ if (indexes.size() == 0)
+ return;
+ QVector<vlc_playlist_item_t*> itemsToMove;
+ //std::sort(indexes.begin(), indexes.end()); //Indexes are already sorted
+ std::transform(indexes.begin(), indexes.end(),std::back_inserter(itemsToMove), [&] (int index) {
+ return d->m_items[index].raw();
+ });
+ //the target is in the referential of the list before doing the operation,
+ //the playlist expect the target to be defined as the index after the operation
+ for ( const int index : indexes ) {
+ if (index < targetPre)
+ targetPost--;
+ else
+ break;
+ }
+
+ {
+ PlaylistLocker locker(d->m_playlist);
+ int ret = vlc_playlist_RequestMove(d->m_playlist, itemsToMove.constData(),
+ itemsToMove.size(), targetPost, indexes[0]);
+ if (ret != VLC_SUCCESS)
+ throw std::bad_alloc();
+ }
+}
+
+int PlaylistListModel::getCurrentIndex() const
+{
+ Q_D(const PlaylistListModel);
+ return d->m_current;
+}
+
+PlaylistPtr PlaylistListModel::getPlaylistId() const
+{
+ Q_D(const PlaylistListModel);
+ if (!d->m_playlist)
+ return {};
+ return PlaylistPtr(d->m_playlist);
+}
+
+void PlaylistListModel::setPlaylistId(vlc_playlist_t* playlist)
+{
+ Q_D(PlaylistListModel);
+ if (d->m_playlist && d->m_listener)
+ {
+ PlaylistLocker locker(d->m_playlist);
+ vlc_playlist_RemoveListener(d->m_playlist, d->m_listener);
+ d->m_playlist = nullptr;
+ d->m_listener = nullptr;
+ }
+ if (playlist)
+ {
+ PlaylistLocker locker(playlist);
+ d->m_playlist = playlist;
+ d->m_listener = vlc_playlist_AddListener(d->m_playlist, &playlist_callbacks, d, true);
+ }
+ emit playlistIdChanged( PlaylistPtr(d->m_playlist) );
+}
+
+void PlaylistListModel::setPlaylistId(PlaylistPtr id)
+{
+ setPlaylistId(id.m_playlist);
+}
+
+QVariant
+PlaylistListModel::data(const QModelIndex &index, int role) const
+{
+ Q_D(const PlaylistListModel);
+ if (!d->m_playlist)
+ return {};
+
+ ssize_t row = index.row();
+ if (row < 0 || row >= d->m_items.size())
+ return {};
+
+ switch (role)
+ {
+ case TitleRole:
+ return d->m_items[row].getTitle();
+ case IsCurrentRole:
+ return row == d->m_current;
+ case DurationRole:
+ {
+ int64_t t_sec = SEC_FROM_VLC_TICK(d->m_items[row].getDuration());
+ int sec = t_sec % 60;
+ int min = (t_sec / 60) % 60;
+ int hour = t_sec / 3600;
+ if (hour == 0)
+ return QString("%1:%2")
+ .arg(min, 2, 10, QChar('0'))
+ .arg(sec, 2, 10, QChar('0'));
+ else
+ return QString("%1:%2:%3")
+ .arg(hour, 2, 10, QChar('0'))
+ .arg(min, 2, 10, QChar('0'))
+ .arg(sec, 2, 10, QChar('0'));
+ }
+ case ArtistRole:
+ return d->m_items[row].getArtist();
+ case AlbumRole:
+ return d->m_items[row].getAlbum();
+ case ArtworkRole:
+ return d->m_items[row].getArtwork();
+ default:
+ return {};
+ }
+}
+
+} // namespace playlist
+} // namespace vlc
diff --git a/modules/gui/qt/components/playlist/playlist_model.hpp b/modules/gui/qt/components/playlist/playlist_model.hpp
new file mode 100644
index 0000000000..6d1eafb2f4
--- /dev/null
+++ b/modules/gui/qt/components/playlist/playlist_model.hpp
@@ -0,0 +1,88 @@
+/*****************************************************************************
+ * Copyright (C) 2019 VLC authors and VideoLAN
+ *
+ * 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 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_common.hpp"
+#include "playlist_item.hpp"
+
+namespace vlc {
+namespace playlist {
+
+class PlaylistListModelPrivate;
+class PlaylistListModel : public QAbstractListModel
+{
+ Q_OBJECT
+ Q_PROPERTY(PlaylistPtr playlistId READ getPlaylistId WRITE setPlaylistId NOTIFY playlistIdChanged)
+ Q_PROPERTY(int currentIndex READ getCurrentIndex NOTIFY currentIndexChanged)
+
+public:
+ enum Roles
+ {
+ TitleRole = Qt::UserRole,
+ DurationRole,
+ IsCurrentRole,
+ ArtistRole,
+ AlbumRole,
+ ArtworkRole
+ };
+
+ PlaylistListModel(QObject *parent = nullptr);
+ PlaylistListModel(vlc_playlist_t *playlist, QObject *parent = nullptr);
+ ~PlaylistListModel();
+
+ 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;
+
+ Q_INVOKABLE virtual void removeItems(const QList<int> &indexes);
+ Q_INVOKABLE virtual void moveItems(const QList<int> &indexes, int target);
+
+ int getCurrentIndex() const;
+
+public slots:
+ PlaylistPtr getPlaylistId() const;
+ void setPlaylistId(PlaylistPtr id);
+ void setPlaylistId(vlc_playlist_t* playlist);
+
+signals:
+ void playlistIdChanged(const PlaylistPtr& );
+ void currentIndexChanged( int );
+
+private:
+ Q_DECLARE_PRIVATE(PlaylistListModel)
+ QScopedPointer<PlaylistListModelPrivate> d_ptr;
+
+};
+
+
+ } // namespace playlist
+} // namespace vlc
+
+#endif
diff --git a/modules/gui/qt/components/playlist/playlist_model_p.hpp b/modules/gui/qt/components/playlist/playlist_model_p.hpp
new file mode 100644
index 0000000000..315dc1bdde
--- /dev/null
+++ b/modules/gui/qt/components/playlist/playlist_model_p.hpp
@@ -0,0 +1,72 @@
+/*****************************************************************************
+ * playlist_model_p.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 PLAYLIST_MODEL_P_HPP
+#define PLAYLIST_MODEL_P_HPP
+
+#include "playlist_model.hpp"
+
+namespace vlc {
+namespace playlist {
+
+class PlaylistListModelPrivate
+{
+ Q_DISABLE_COPY(PlaylistListModelPrivate)
+public:
+ Q_DECLARE_PUBLIC(PlaylistListModel)
+ PlaylistListModel* const q_ptr;
+
+public:
+ PlaylistListModelPrivate(PlaylistListModel* playlistList);
+ ~PlaylistListModelPrivate();
+
+ ///call function @a fun on object thread
+ template <typename Fun>
+ inline void callAsync(Fun&& fun)
+ {
+ Q_Q(PlaylistListModel);
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
+ QMetaObject::invokeMethod(q, std::forward<Fun>(fun), Qt::QueuedConnection, nullptr);
+#else
+ QObject src;
+ QObject::connect(&src, &QObject::destroyed, q, std::forward<Fun>(fun), Qt::QueuedConnection);
+#endif
+ }
+
+ void onItemsReset(const QVector<PlaylistItem>& items);
+ void onItemsAdded(const QVector<PlaylistItem>& added, size_t index);
+ void onItemsMoved(size_t index, size_t count, size_t target);
+ void onItemsRemoved(size_t index, size_t count);
+
+ void notifyItemsChanged(int index, int count,
+ const QVector<int> &roles = {});
+
+ vlc_playlist_t* m_playlist = nullptr;
+ vlc_playlist_listener_id *m_listener = nullptr;
+
+ /* access only from the UI thread */
+ QVector<PlaylistItem> m_items;
+ ssize_t m_current = -1;
+};
+
+} //namespace playlist
+} //namespace vlc
+
+
+#endif // PLAYLIST_MODEL_P_HPP
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..b69d3bf520
--- /dev/null
+++ b/modules/gui/qt/components/playlist_new/media.hpp
@@ -0,0 +1,76 @@
+/*****************************************************************************
+ * Copyright (C) 2019 VLC authors and VideoLAN
+ *
+ * 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 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/qt.cpp b/modules/gui/qt/qt.cpp
index a0fb7789c4..c5d57ffef1 100644
--- a/modules/gui/qt/qt.cpp
+++ b/modules/gui/qt/qt.cpp
@@ -59,6 +59,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>
@@ -497,6 +500,13 @@ 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");
+}
+
static void *Thread( void *obj )
{
intf_thread_t *p_intf = (intf_thread_t *)obj;
@@ -655,6 +665,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