[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