[vlc-devel] [RFC 25/82] qt: add models for tracks, chapters, programs
Pierre Lamot
pierre at videolabs.io
Fri Feb 1 14:01:29 CET 2019
---
modules/gui/qt/Makefile.am | 2 +
modules/gui/qt/util/input_models.cpp | 485 +++++++++++++++++++++++++++
modules/gui/qt/util/input_models.hpp | 219 ++++++++++++
3 files changed, 706 insertions(+)
create mode 100644 modules/gui/qt/util/input_models.cpp
create mode 100644 modules/gui/qt/util/input_models.hpp
diff --git a/modules/gui/qt/Makefile.am b/modules/gui/qt/Makefile.am
index f7966db939..9014d64bbf 100644
--- a/modules/gui/qt/Makefile.am
+++ b/modules/gui/qt/Makefile.am
@@ -125,6 +125,7 @@ libqt_plugin_la_SOURCES = \
gui/qt/components/sout/profiles.hpp \
gui/qt/util/animators.cpp gui/qt/util/animators.hpp \
gui/qt/util/input_slider.cpp gui/qt/util/input_slider.hpp \
+ gui/qt/util/input_models.cpp gui/qt/util/input_models.hpp \
gui/qt/util/timetooltip.cpp gui/qt/util/timetooltip.hpp \
gui/qt/util/customwidgets.cpp gui/qt/util/customwidgets.hpp \
gui/qt/util/searchlineedit.cpp gui/qt/util/searchlineedit.hpp \
@@ -226,6 +227,7 @@ nodist_libqt_plugin_la_SOURCES = \
gui/qt/components/sout/sout_widgets.moc.cpp \
gui/qt/util/animators.moc.cpp \
gui/qt/util/input_slider.moc.cpp \
+ gui/qt/util/input_models.moc.cpp \
gui/qt/util/timetooltip.moc.cpp \
gui/qt/util/customwidgets.moc.cpp \
gui/qt/util/qmleventfilter.moc.cpp \
diff --git a/modules/gui/qt/util/input_models.cpp b/modules/gui/qt/util/input_models.cpp
new file mode 100644
index 0000000000..e381fbb917
--- /dev/null
+++ b/modules/gui/qt/util/input_models.cpp
@@ -0,0 +1,485 @@
+/*****************************************************************************
+ * 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 "input_models.hpp"
+
+//***************************
+// track list model
+//***************************
+
+TrackListModel::TrackListModel(vlc_player_t *player, QObject *parent)
+ : QAbstractListModel(parent)
+ , m_player(player)
+{
+}
+
+Qt::ItemFlags TrackListModel::flags(const QModelIndex &) const
+{
+ return Qt::ItemIsUserCheckable;
+}
+
+int TrackListModel::rowCount(const QModelIndex &) const
+{
+ return m_data.size();
+}
+
+QVariant TrackListModel::data(const QModelIndex &index, int role) const
+{
+ int row = index.row();
+ if (row >= m_data.size())
+ return QVariant{};
+ if (role == Qt::DisplayRole)
+ return m_data[row].m_title;
+ else if (role == Qt::CheckStateRole)
+ return QVariant::fromValue<bool>(m_data[row].m_selected);
+ return QVariant{};
+}
+
+bool TrackListModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ int row = index.row();
+ if (row >= m_data.size())
+ return false;
+ if ( role != Qt::CheckStateRole )
+ return false;
+ if (!value.canConvert<bool>())
+ return false;
+ bool select = value.toBool();
+ vlc_player_locker lock{ m_player };
+
+ if (select)
+ vlc_player_SelectTrack(m_player, m_data[row].m_id.get());
+ else
+ vlc_player_UnselectTrack(m_player, m_data[row].m_id.get());
+ return true;
+}
+
+void TrackListModel::updateTracks(vlc_player_list_action action, const vlc_player_track *track_info)
+{
+ switch (action) {
+ case VLC_PLAYER_LIST_ADDED:
+ beginInsertRows({}, m_data.size(), m_data.size());
+ m_data.append(Data{ track_info });
+ endInsertRows();
+ break;
+
+ case VLC_PLAYER_LIST_REMOVED:
+ {
+ auto it = std::find_if(m_data.begin(), m_data.end(), [&](const Data& t) {
+ return t.m_id.get() == track_info->es_id;
+ });
+ if (it == m_data.end())
+ return;
+
+ int pos = std::distance(m_data.begin(), it);
+ beginRemoveRows({}, pos, pos);
+ m_data.erase(it);
+ endRemoveRows();
+ break;
+ }
+ case VLC_PLAYER_LIST_UPDATED:
+ {
+ int pos = 0;
+ bool found = false;
+ for (Data& d : m_data)
+ {
+ if (d.m_id.get() == track_info->es_id)
+ {
+ d.update(track_info);
+ found = true;
+ break;
+ }
+ pos++;
+ }
+ if (!found)
+ return;
+ QModelIndex dataIndex = index(pos);
+ emit dataChanged(dataIndex, dataIndex, { Qt::DisplayRole, Qt::CheckStateRole });
+ break;
+ }
+ }
+}
+
+void TrackListModel::updateTrackSelection(vlc_es_id_t *trackid, bool selected)
+{
+ if (trackid == NULL)
+ return;
+ QList<Data>::iterator it = std::find_if(m_data.begin(), m_data.end(), [&](const Data& track) {
+ return trackid == track.m_id.get();
+ });
+ if (it == m_data.end())
+ return;
+ size_t pos = std::distance(m_data.begin(), it);
+ it->m_selected = selected;
+ QModelIndex dataIndex = index(pos);
+ emit dataChanged(dataIndex, dataIndex, { Qt::CheckStateRole });
+}
+
+void TrackListModel::clear()
+{
+ beginRemoveRows({}, 0, m_data.size() - 1);
+ m_data.clear();
+ endRemoveRows();
+}
+
+QHash<int, QByteArray> TrackListModel::roleNames() const
+{
+ QHash<int, QByteArray> roleNames = this->QAbstractListModel::roleNames();
+ roleNames[Qt::CheckStateRole] = "checked";
+ return roleNames;
+}
+
+TrackListModel::Data::Data(const vlc_player_track *track_info)
+ : m_title( qfu(track_info->name) )
+ , m_id( track_info->es_id, true )
+ , m_selected( track_info->selected )
+{
+}
+
+void TrackListModel::Data::update(const vlc_player_track *track_info)
+{
+ m_id.reset(track_info->es_id, true);
+ m_title = qfu(track_info->name);
+ m_selected = track_info->selected;
+}
+
+
+//***************************
+// TitleListModel
+//***************************
+
+
+TitleListModel::TitleListModel(vlc_player_t *player, QObject *parent)
+ : QAbstractListModel(parent)
+ , m_player(player)
+{
+}
+
+Qt::ItemFlags TitleListModel::flags(const QModelIndex &) const
+{
+ return Qt::ItemIsUserCheckable;
+}
+
+int TitleListModel::rowCount(const QModelIndex &) const
+{
+ return m_count;
+}
+
+QVariant TitleListModel::data(const QModelIndex &index, int role) const
+{
+ int row = index.row();
+ if (row >= m_count)
+ return QVariant{};
+ const vlc_player_title* title = getTitleAt(row);
+
+ if (role == Qt::DisplayRole)
+ return qfu(title->name);
+ else if (role == Qt::CheckStateRole)
+ return QVariant::fromValue<bool>(row == m_current);
+ return QVariant{};
+}
+
+bool TitleListModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ int row = index.row();
+ if (row < 0 || row >= m_count)
+ return false;
+ if ( role != Qt::CheckStateRole )
+ return false;
+ if (!value.canConvert<bool>())
+ return false;
+ bool select = value.toBool();
+ if (select)
+ {
+ vlc_player_locker lock{ m_player };
+ const vlc_player_title* title = getTitleAt(row);
+ if (!title)
+ return false;
+ vlc_player_SelectTitle(m_player, title);
+ }
+ return true;
+}
+
+void TitleListModel::setCurrent(int current)
+{
+ if (m_count == 0 || m_current == current)
+ return;
+ int oldCurrent = m_current;
+ m_current = current;
+
+ QModelIndex oldIndex = index(oldCurrent);
+ QModelIndex currentIndex = index(current);
+
+ if (oldCurrent >= 0)
+ emit dataChanged(oldIndex, oldIndex, { Qt::CheckStateRole });
+ if ( current >= 0 )
+ emit dataChanged(currentIndex, currentIndex, { Qt::CheckStateRole });
+}
+
+const vlc_player_title *TitleListModel::getTitleAt(size_t index) const
+{
+ return vlc_player_title_list_GetAt(m_titleList.get(), index);
+}
+
+void TitleListModel::resetTitles(vlc_player_title_list *newTitleList)
+{
+ beginResetModel();
+ m_titleList.reset(newTitleList, true);
+ m_current = -1;
+ if (m_titleList)
+ m_count = vlc_player_title_list_GetCount(m_titleList.get());
+ else
+ m_count = 0;
+ endResetModel();
+}
+
+QHash<int, QByteArray> TitleListModel::roleNames() const
+{
+ QHash<int, QByteArray> roleNames = this->QAbstractListModel::roleNames();
+ roleNames[Qt::CheckStateRole] = "checked";
+ return roleNames;
+}
+
+//***************************
+// ChapterListModel
+//***************************
+
+ChapterListModel::ChapterListModel(vlc_player_t *player, QObject *parent)
+ : QAbstractListModel(parent)
+ , m_player(player)
+{
+}
+
+Qt::ItemFlags ChapterListModel::flags(const QModelIndex &) const
+{
+ return Qt::ItemIsUserCheckable;
+}
+
+int ChapterListModel::rowCount(const QModelIndex &) const
+{
+ return m_title ? m_title->chapter_count : 0;
+}
+
+QVariant ChapterListModel::data(const QModelIndex &index, int role) const
+{
+ int row = index.row();
+ if (m_title == nullptr || row < 0 || (size_t)row >= m_title->chapter_count)
+ return QVariant{};
+ const vlc_player_chapter& chapter = m_title->chapters[row];
+
+ if (role == Qt::DisplayRole)
+ return qfu(chapter.name);
+ else if (role == Qt::CheckStateRole)
+ return QVariant::fromValue<bool>(row == m_current);
+ else if (role == ChapterListRoles::TimeRole )
+ return QVariant::fromValue<vlc_tick_t>(chapter.time);
+ return QVariant{};
+}
+
+bool ChapterListModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ int row = index.row();
+ if (m_title == nullptr || (size_t)row >= m_title->chapter_count)
+ return false;
+ if ( role != Qt::CheckStateRole )
+ return false;
+ if (!value.canConvert<bool>())
+ return false;
+ bool select = value.toBool();
+ if (select)
+ {
+ vlc_player_locker lock{ m_player };
+ vlc_player_SelectChapter(m_player, m_title, row);
+ }
+ return true;
+}
+
+QHash<int, QByteArray> ChapterListModel::roleNames() const
+{
+ return QHash<int, QByteArray>{
+ {Qt::DisplayRole, "display"},
+ {Qt::CheckStateRole, "checked"},
+ {ChapterListRoles::TimeRole, "time"}
+ };
+}
+
+void ChapterListModel::setCurrent(int current)
+{
+ if (m_title == nullptr || m_title->chapter_count == 0 || m_current == current)
+ return;
+ int oldCurrent = m_current;
+ m_current = current;
+
+ QModelIndex oldIndex = index(oldCurrent);
+ QModelIndex currentIndex = index(current);
+
+ if (oldCurrent >= 0)
+ emit dataChanged(oldIndex, oldIndex, { Qt::CheckStateRole });
+ if ( current >= 0 )
+ emit dataChanged(currentIndex, currentIndex, { Qt::CheckStateRole });
+}
+
+void ChapterListModel::resetTitle(const vlc_player_title *newTitle)
+{
+ beginResetModel();
+ m_title =newTitle;
+ m_current = -1;
+ endResetModel();
+}
+
+
+//***************************
+// ProgramListModel
+//***************************
+
+
+ProgramListModel::ProgramListModel(vlc_player_t *player, QObject *parent)
+ : QAbstractListModel(parent)
+ , m_player(player)
+{
+}
+
+Qt::ItemFlags ProgramListModel::flags(const QModelIndex &) const
+{
+ return Qt::ItemIsUserCheckable;
+}
+
+int ProgramListModel::rowCount(const QModelIndex &) const
+{
+ return m_data.size();
+}
+
+QVariant ProgramListModel::data(const QModelIndex &index, int role) const
+{
+ int row = index.row();
+ if (row >= m_data.size())
+ return QVariant{};
+ if (role == Qt::DisplayRole)
+ return m_data[row].m_title;
+ else if (role == Qt::CheckStateRole)
+ return QVariant::fromValue<bool>(m_data[row].m_selected);
+ return QVariant{};
+}
+
+bool ProgramListModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ int row = index.row();
+ if (row >= m_data.size())
+ return false;
+ if ( role != Qt::CheckStateRole )
+ return false;
+ if (!value.canConvert<bool>())
+ return false;
+ bool select = value.toBool();
+ vlc_player_locker lock{ m_player };
+
+ if (select)
+ vlc_player_SelectProgram(m_player, m_data[row].m_id);
+ return true;
+}
+
+void ProgramListModel::updatePrograms(vlc_player_list_action action, const vlc_player_program *program)
+{
+ assert(program);
+ switch (action) {
+ case VLC_PLAYER_LIST_ADDED:
+ beginInsertRows({}, m_data.size(), m_data.size());
+ m_data.append(Data{ program });
+ endInsertRows();
+ break;
+
+ case VLC_PLAYER_LIST_REMOVED:
+ {
+ auto it = std::find_if(m_data.begin(), m_data.end(), [&](const Data& t) {
+ return t.m_id == program->group_id;
+ });
+ if (it == m_data.end())
+ return;
+ int pos = std::distance(m_data.begin(), it);
+ beginRemoveRows({}, pos, pos);
+ m_data.erase(it);
+ endRemoveRows();
+ break;
+ }
+ case VLC_PLAYER_LIST_UPDATED:
+ {
+ int pos = 0;
+ bool found = false;
+ for (Data& d : m_data)
+ {
+ if (d.m_id == program->group_id)
+ {
+ d.update(program);
+ found = true;
+ break;
+ }
+ pos++;
+ }
+ if (!found)
+ return;
+ QModelIndex dataIndex = index(pos);
+ emit dataChanged(dataIndex, dataIndex, { Qt::DisplayRole, Qt::CheckStateRole });
+ break;
+ }
+ }
+}
+
+void ProgramListModel::updateProgramSelection(int programid, bool selected)
+{
+ QList<Data>::iterator it = std::find_if(m_data.begin(), m_data.end(), [&](const Data& program) {
+ return programid == program.m_id;
+ });
+ if (it == m_data.end())
+ return;
+ size_t pos = std::distance(m_data.begin(), it);
+ it->m_selected = selected;
+ QModelIndex dataIndex = index(pos);
+ emit dataChanged(dataIndex, dataIndex, { Qt::CheckStateRole });
+}
+
+void ProgramListModel::clear()
+{
+ beginRemoveRows({}, 0, m_data.size() - 1);
+ m_data.clear();
+ endRemoveRows();
+}
+
+QHash<int, QByteArray> ProgramListModel::roleNames() const
+{
+ QHash<int, QByteArray> roleNames = this->QAbstractListModel::roleNames();
+ roleNames[Qt::CheckStateRole] = "checked";
+ return roleNames;
+}
+
+ProgramListModel::Data::Data(const vlc_player_program *program)
+ : m_title(qfu(program->name))
+ , m_id( program->group_id )
+ , m_selected( program->selected )
+ , m_scrambled( program->scrambled )
+{
+ assert(program);
+}
+
+void ProgramListModel::Data::update(const vlc_player_program *program)
+{
+ assert(program);
+ m_title = qfu(program->name);
+ m_id = program->group_id;
+ m_selected = program->selected;
+ m_scrambled = program->scrambled;
+}
+
diff --git a/modules/gui/qt/util/input_models.hpp b/modules/gui/qt/util/input_models.hpp
new file mode 100644
index 0000000000..aab6dfe0f3
--- /dev/null
+++ b/modules/gui/qt/util/input_models.hpp
@@ -0,0 +1,219 @@
+/*****************************************************************************
+ * input_models.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 INPUT_MODELS_HPP
+#define INPUT_MODELS_HPP
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "qt.hpp"
+#include <memory>
+#include <vlc_player.h>
+#include <vlc_cxx_helpers.hpp>
+#include <QAbstractListModel>
+#include <QList>
+
+/**
+ * @brief The TrackListModel class represent the
+ * (audio/video/spu/...)tracks of an input
+ *
+ * the model expose the track title using the Qt::DisplayRole
+ * and the current track using Qt::CheckStateRole
+ * we can select a track by setting the Qt::CheckStateRole to true
+ *
+ * this class expects to be updated by the input manager
+ */
+class TrackListModel : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ TrackListModel(vlc_player_t* player, QObject* parent = nullptr);
+
+ virtual Qt::ItemFlags flags(const QModelIndex &) const override;
+
+ virtual int rowCount(const QModelIndex & = QModelIndex()) const override;
+
+ virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+
+ virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
+
+ void updateTracks(enum vlc_player_list_action action, const vlc_player_track *track_info);
+
+ void updateTrackSelection(vlc_es_id_t *trackid, bool selected);
+
+ void clear();
+
+ QHash<int, QByteArray> roleNames() const override;
+
+private:
+ vlc_player_t* m_player;
+ class Data {
+ public:
+ Data(const vlc_player_track *track_info);
+
+ Data(const Data& data) = default;
+ Data& operator =(const Data& data) = default;
+
+ void update( const vlc_player_track *track_info );
+
+ QString m_title;
+ //vlc_es_id_t *m_id = NULL;
+ vlc_shared_data_ptr_type(vlc_es_id_t, vlc_es_id_Hold, vlc_es_id_Release) m_id;
+ bool m_selected = false;
+ };
+ QList<Data> m_data;
+};
+
+/**
+ * @brief The TitleListModel class represent the
+ * titles of an input
+ *
+ * the model expose the title's name using the Qt::DisplayRole
+ * and the current title using Qt::CheckStateRole
+ * we can select a title by setting the Qt::CheckStateRole to true
+ *
+ * this class expects to be updated by the input manager
+ */
+class TitleListModel : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+
+ TitleListModel(vlc_player_t* player, QObject* parent = nullptr);
+
+ virtual Qt::ItemFlags flags(const QModelIndex &) const override;
+
+ virtual int rowCount(const QModelIndex & = QModelIndex()) const override;
+
+ virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+
+ virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
+
+ void setCurrent(int current);
+
+ const vlc_player_title* getTitleAt( size_t index ) const;
+
+ void resetTitles(vlc_player_title_list* newTitleList);
+
+ QHash<int, QByteArray> roleNames() const override;
+
+private:
+ vlc_player_t* m_player;
+ typedef vlc_shared_data_ptr_type(vlc_player_title_list, vlc_player_title_list_Hold, vlc_player_title_list_Release) PlayerTitleList;
+ PlayerTitleList m_titleList;
+ int m_current = -1;
+ int m_count = 0;
+};
+
+/**
+ * @brief The ChapterListModel class represent the
+ * chapters of an input
+ *
+ * the model expose the chapter's name using the Qt::DisplayRole
+ * and the current chapter using Qt::CheckStateRole
+ * we can select a chapter by setting the Qt::CheckStateRole to true
+ *
+ * this class expects to be updated by the input manager
+ * the parent vlc_player_title isn't hold by this class, it is expected that the property is be valid or null
+ */
+class ChapterListModel : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ //user role
+ enum ChapterListRoles {
+ TimeRole = Qt::UserRole + 1
+ };
+public:
+ ChapterListModel(vlc_player_t* player, QObject* parent = nullptr);
+
+ virtual Qt::ItemFlags flags(const QModelIndex &) const override;
+
+ virtual int rowCount(const QModelIndex & = QModelIndex()) const override;
+
+ virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+
+ virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
+
+ QHash<int, QByteArray> roleNames() const override;
+
+ void setCurrent(int current);
+
+ void resetTitle(const vlc_player_title* newTitle);
+
+private:
+ vlc_player_t* m_player = nullptr;
+ const vlc_player_title* m_title = nullptr;
+ int m_current = -1;
+};
+
+/**
+ * @brief The ProgramListModel class represent the
+ * titles of an input
+ *
+ * the model expose the program's name using the Qt::DisplayRole
+ * and the current program using Qt::CheckStateRole
+ * we can select a program by setting the Qt::CheckStateRole to true
+ *
+ * this class expects to be updated by the input manager
+ */
+class ProgramListModel : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ ProgramListModel(vlc_player_t* player, QObject* parent = nullptr);
+
+ Qt::ItemFlags flags(const QModelIndex &) const override;
+
+ int rowCount(const QModelIndex & = QModelIndex()) const override;
+
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+
+ bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
+
+ void updatePrograms(enum vlc_player_list_action action, const struct vlc_player_program* program);
+
+ void updateProgramSelection(int programid, bool selected);
+
+ void clear();
+
+ QHash<int, QByteArray> roleNames() const override;
+
+private:
+ vlc_player_t* m_player;
+ class Data {
+ public:
+ Data( const struct vlc_player_program* program );
+
+ Data(const Data& data) = default;
+ Data& operator =(const Data& data) = default;
+
+ void update( const struct vlc_player_program* program );
+
+ QString m_title;
+ int m_id;
+ bool m_selected;
+ bool m_scrambled;
+ };
+ QList<Data> m_data;
+};
+
+#endif // INPUT_MODELS_HPP
--
2.19.1
More information about the vlc-devel
mailing list