From 46e23ec0f1c6d6fe552e0822105cf4a15424f030 Mon Sep 17 00:00:00 2001
From: Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com>
Date: Tue, 24 Mar 2015 18:01:58 -0400
Subject: [PATCH] video: Offer a configuration proxy model

This avoid using "if" nest in the GUI

It also made possible to enable dynamic device reloading
without extra client complexity.

Refs #69141
---
 CMakeLists.txt                   |   2 +
 src/video/configurationproxy.cpp | 277 +++++++++++++++++++++++++++++++
 src/video/configurationproxy.h   |  48 ++++++
 src/video/devicemodel.cpp        |  13 +-
 4 files changed, 335 insertions(+), 5 deletions(-)
 create mode 100644 src/video/configurationproxy.cpp
 create mode 100644 src/video/configurationproxy.h

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3817efbd..b270fb2a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -212,6 +212,7 @@ SET( libringclient_LIB_SRCS
   src/video/sourcemodel.cpp
   src/video/channel.cpp
   src/video/resolution.cpp
+  src/video/configurationproxy.cpp
   src/audio/alsapluginmodel.cpp
   src/audio/inputdevicemodel.cpp
   src/audio/managermodel.cpp
@@ -333,6 +334,7 @@ SET(libringclient_video_LIB_HDRS
   src/video/channel.h
   src/video/rate.h
   src/video/previewmanager.h
+  src/video/configurationproxy.h
   #The renderer implementations are not exported on purpose
 )
 
diff --git a/src/video/configurationproxy.cpp b/src/video/configurationproxy.cpp
new file mode 100644
index 00000000..16e7c775
--- /dev/null
+++ b/src/video/configurationproxy.cpp
@@ -0,0 +1,277 @@
+/****************************************************************************
+ *   Copyright (C) 2015 by Savoir-Faire Linux                               *
+ *   Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> *
+ *                                                                          *
+ *   This library 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 library 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 General Public License      *
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.  *
+ ***************************************************************************/
+#include "configurationproxy.h"
+
+//Qt
+#include <QtCore/QIdentityProxyModel>
+#include <QtCore/QAbstractItemModel>
+#include <QtCore/QItemSelectionModel>
+
+//Ring
+#include <video/sourcemodel.h>
+#include <video/devicemodel.h>
+#include <video/channel.h>
+#include <video/resolution.h>
+#include <video/rate.h>
+
+namespace ConfigurationProxyPrivate {
+   static QIdentityProxyModel* m_spDeviceModel    = nullptr;
+   static QIdentityProxyModel* m_spChannelModel   = nullptr;
+   static QIdentityProxyModel* m_spResolutionModel= nullptr;
+   static QIdentityProxyModel* m_spRateModel      = nullptr;
+
+   static QItemSelectionModel* m_spDeviceSelectionModel    = nullptr;
+   static QItemSelectionModel* m_spChannelSelectionModel   = nullptr;
+   static QItemSelectionModel* m_spResolutionSelectionModel= nullptr;
+   static QItemSelectionModel* m_spRateSelectionModel      = nullptr;
+
+   //Helper
+   static Video::Device*     currentDevice    ();
+   static Video::Channel*    currentChannel   ();
+   static Video::Resolution* currentResolution();
+//    static Video::Rate*       currentRate      ();
+
+   static void changeDevice    ();
+   static void changeChannel   ();
+   static void changeResolution();
+   static void changeRate      ();
+
+   void updateDeviceSelection    ();
+   void updateChannelSelection   ();
+   void updateResolutionSelection();
+   void updateRateSelection      ();
+}
+
+QAbstractItemModel* Video::ConfigurationProxy::deviceModel()
+{
+   if (!ConfigurationProxyPrivate::m_spDeviceModel) {
+      ConfigurationProxyPrivate::m_spDeviceModel = new QIdentityProxyModel(Video::SourceModel::instance());
+      ConfigurationProxyPrivate::m_spDeviceModel->setSourceModel(Video::DeviceModel::instance());
+      ConfigurationProxyPrivate::updateDeviceSelection();
+   }
+   return ConfigurationProxyPrivate::m_spDeviceModel;
+}
+
+static Video::Device* ConfigurationProxyPrivate::currentDevice()
+{
+   return Video::DeviceModel::instance()->Video::DeviceModel::instance()->activeDevice();
+}
+
+static Video::Channel* ConfigurationProxyPrivate::currentChannel()
+{
+   if (Video::DeviceModel::instance()->Video::DeviceModel::instance()->activeDevice()
+    && Video::DeviceModel::instance()->Video::DeviceModel::instance()->activeDevice()->activeChannel())
+      return Video::DeviceModel::instance()->Video::DeviceModel::instance()->activeDevice()->activeChannel();
+
+   return nullptr;
+}
+
+static Video::Resolution* ConfigurationProxyPrivate::currentResolution()
+{
+   if (Video::DeviceModel::instance()->Video::DeviceModel::instance()->activeDevice()
+    && Video::DeviceModel::instance()->Video::DeviceModel::instance()->activeDevice()->activeChannel()
+    && Video::DeviceModel::instance()->Video::DeviceModel::instance()->activeDevice()->activeChannel()->activeResolution()
+   )
+      return Video::DeviceModel::instance()->Video::DeviceModel::instance()->activeDevice()->activeChannel()->activeResolution();
+   return nullptr;
+}
+
+/*static Video::Rate* ConfigurationProxyPrivate::currentRate()
+{
+   if (Video::DeviceModel::instance()->Video::DeviceModel::instance()->activeDevice()
+    && Video::DeviceModel::instance()->Video::DeviceModel::instance()->activeDevice()->activeChannel()
+    && Video::DeviceModel::instance()->Video::DeviceModel::instance()->activeDevice()->activeChannel()->activeResolution()
+   )
+      return Video::DeviceModel::instance()->Video::DeviceModel::instance()->activeDevice()->activeChannel()->activeResolution()->activeRate();
+
+   return nullptr;
+}*/
+
+void ConfigurationProxyPrivate::changeDevice()
+{
+   Video::ConfigurationProxy::deviceSelectionModel();
+
+   Video::DeviceModel::instance()->setActive(ConfigurationProxyPrivate::m_spDeviceSelectionModel->currentIndex());
+
+   ((QIdentityProxyModel*) Video::ConfigurationProxy::channelModel())->setSourceModel(ConfigurationProxyPrivate::currentDevice());
+   changeChannel();
+}
+
+void ConfigurationProxyPrivate::changeChannel()
+{
+   Video::ConfigurationProxy::channelSelectionModel();
+
+   Video::Device* dev = ConfigurationProxyPrivate::currentDevice();
+
+   if (dev)
+      dev->setActiveChannel(ConfigurationProxyPrivate::m_spChannelSelectionModel->currentIndex().row());
+
+   ((QIdentityProxyModel*) Video::ConfigurationProxy::resolutionModel())->setSourceModel(ConfigurationProxyPrivate::currentChannel());
+
+   updateChannelSelection();
+
+   changeResolution();
+}
+
+void ConfigurationProxyPrivate::changeResolution()
+{
+   Video::ConfigurationProxy::resolutionSelectionModel();
+
+   Video::Channel* chan = ConfigurationProxyPrivate::currentChannel();
+
+   if (chan)
+      chan->setActiveResolution(ConfigurationProxyPrivate::m_spResolutionSelectionModel->currentIndex().row());
+
+   ((QIdentityProxyModel*) Video::ConfigurationProxy::rateModel())->setSourceModel(ConfigurationProxyPrivate::currentResolution());
+
+   updateResolutionSelection();
+
+   changeRate();
+}
+
+void ConfigurationProxyPrivate::changeRate()
+{
+   Video::ConfigurationProxy::rateSelectionModel();
+
+   Video::Resolution* res = ConfigurationProxyPrivate::currentResolution();
+
+   if (res)
+      res->setActiveRate(ConfigurationProxyPrivate::m_spRateSelectionModel->currentIndex().row());
+
+   updateRateSelection();
+}
+
+void ConfigurationProxyPrivate::updateDeviceSelection()
+{
+   const QModelIndex& idx = ConfigurationProxyPrivate::m_spDeviceModel->index(Video::DeviceModel::instance()->activeIndex(),0);
+   Video::ConfigurationProxy::deviceSelectionModel()->setCurrentIndex(idx , QItemSelectionModel::ClearAndSelect);
+}
+
+void ConfigurationProxyPrivate::updateChannelSelection()
+{
+   Video::Device* dev = ConfigurationProxyPrivate::currentDevice();
+   if (dev) {
+      Video::Channel* chan = dev->activeChannel();
+      if (chan)
+         Video::ConfigurationProxy::channelSelectionModel()->setCurrentIndex(dev->index(chan->relativeIndex(),0), QItemSelectionModel::ClearAndSelect );
+   }
+}
+
+void ConfigurationProxyPrivate::updateResolutionSelection()
+{
+   Video::Channel* chan = ConfigurationProxyPrivate::currentChannel();
+   if (chan) {
+      Video::Resolution* res = chan->activeResolution();
+      if (res)
+         Video::ConfigurationProxy::resolutionSelectionModel()->setCurrentIndex(chan->index(res->relativeIndex(),0), QItemSelectionModel::ClearAndSelect);
+   }
+}
+
+void ConfigurationProxyPrivate::updateRateSelection()
+{
+   Video::Resolution* res = ConfigurationProxyPrivate::currentResolution();
+   if (res) {
+      Video::Rate* rate = res->activeRate();
+      if (rate)
+         Video::ConfigurationProxy::rateSelectionModel()->setCurrentIndex(res->index(rate->relativeIndex(),0), QItemSelectionModel::ClearAndSelect);
+   }
+}
+
+QAbstractItemModel* Video::ConfigurationProxy::channelModel()
+{
+   if (!ConfigurationProxyPrivate::m_spChannelModel) {
+      ConfigurationProxyPrivate::m_spChannelModel = new QIdentityProxyModel(Video::SourceModel::instance());
+      Video::Device* dev = ConfigurationProxyPrivate::currentDevice();
+      if (dev) {
+         ConfigurationProxyPrivate::m_spChannelModel->setSourceModel(dev);
+      }
+   }
+   return ConfigurationProxyPrivate::m_spChannelModel;
+}
+
+QAbstractItemModel* Video::ConfigurationProxy::resolutionModel()
+{
+   if (!ConfigurationProxyPrivate::m_spResolutionModel) {
+      ConfigurationProxyPrivate::m_spResolutionModel = new QIdentityProxyModel(Video::SourceModel::instance());
+      Video::Channel* chan = ConfigurationProxyPrivate::currentChannel();
+      if (chan) {
+         ConfigurationProxyPrivate::m_spResolutionModel->setSourceModel(chan);
+      }
+   }
+   return ConfigurationProxyPrivate::m_spResolutionModel;
+}
+
+QAbstractItemModel* Video::ConfigurationProxy::rateModel()
+{
+   if (!ConfigurationProxyPrivate::m_spRateModel) {
+      ConfigurationProxyPrivate::m_spRateModel = new QIdentityProxyModel(Video::SourceModel::instance());
+      ConfigurationProxyPrivate::m_spRateModel->setSourceModel(ConfigurationProxyPrivate::currentResolution());
+   }
+   return ConfigurationProxyPrivate::m_spRateModel;
+}
+
+QItemSelectionModel* Video::ConfigurationProxy::deviceSelectionModel()
+{
+   if (!ConfigurationProxyPrivate::m_spDeviceSelectionModel) {
+      ConfigurationProxyPrivate::m_spDeviceSelectionModel = new QItemSelectionModel(ConfigurationProxyPrivate::m_spDeviceModel);
+
+      //Can happen if a device is removed
+      QObject::connect(Video::DeviceModel::instance(), &Video::DeviceModel::currentIndexChanged,[](int idx) {
+         ConfigurationProxyPrivate::m_spDeviceSelectionModel->setCurrentIndex(ConfigurationProxyPrivate::m_spDeviceModel->index(idx,0), QItemSelectionModel::ClearAndSelect );
+      });
+
+      QObject::connect(ConfigurationProxyPrivate::m_spDeviceSelectionModel,&QItemSelectionModel::currentChanged, &ConfigurationProxyPrivate::changeDevice);
+   }
+   return ConfigurationProxyPrivate::m_spDeviceSelectionModel;
+}
+
+QItemSelectionModel* Video::ConfigurationProxy::channelSelectionModel()
+{
+   if (!ConfigurationProxyPrivate::m_spChannelSelectionModel) {
+      ConfigurationProxyPrivate::m_spChannelSelectionModel = new QItemSelectionModel(ConfigurationProxyPrivate::m_spChannelModel);
+
+      ConfigurationProxyPrivate::updateChannelSelection();
+
+      QObject::connect(ConfigurationProxyPrivate::m_spChannelSelectionModel,&QItemSelectionModel::currentChanged, &ConfigurationProxyPrivate::changeChannel);
+   }
+   return ConfigurationProxyPrivate::m_spChannelSelectionModel;
+}
+
+QItemSelectionModel* Video::ConfigurationProxy::resolutionSelectionModel()
+{
+   if (!ConfigurationProxyPrivate::m_spResolutionSelectionModel) {
+      ConfigurationProxyPrivate::m_spResolutionSelectionModel = new QItemSelectionModel(ConfigurationProxyPrivate::m_spResolutionModel);
+
+      ConfigurationProxyPrivate::updateResolutionSelection();
+
+      QObject::connect(ConfigurationProxyPrivate::m_spResolutionSelectionModel,&QItemSelectionModel::currentChanged, &ConfigurationProxyPrivate::changeResolution);
+   }
+   return ConfigurationProxyPrivate::m_spResolutionSelectionModel;
+}
+
+QItemSelectionModel* Video::ConfigurationProxy::rateSelectionModel()
+{
+   if (!ConfigurationProxyPrivate::m_spRateSelectionModel) {
+      ConfigurationProxyPrivate::m_spRateSelectionModel = new QItemSelectionModel(ConfigurationProxyPrivate::m_spRateModel);
+
+      ConfigurationProxyPrivate::updateRateSelection();
+
+      QObject::connect(ConfigurationProxyPrivate::m_spRateSelectionModel,&QItemSelectionModel::currentChanged, &ConfigurationProxyPrivate::changeRate);
+   }
+   return ConfigurationProxyPrivate::m_spRateSelectionModel;
+}
diff --git a/src/video/configurationproxy.h b/src/video/configurationproxy.h
new file mode 100644
index 00000000..93985684
--- /dev/null
+++ b/src/video/configurationproxy.h
@@ -0,0 +1,48 @@
+/****************************************************************************
+ *   Copyright (C) 2015 by Savoir-Faire Linux                               *
+ *   Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> *
+ *                                                                          *
+ *   This library 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 library 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 General Public License      *
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.  *
+ ***************************************************************************/
+#ifndef CONFIGURATIONPROXY_H
+#define CONFIGURATIONPROXY_H
+
+
+class QAbstractItemModel;
+class QItemSelectionModel;
+
+namespace Video {
+
+/**
+ * This class is used to simplify the configuration process.
+ * Currently, every devices have their own model tree. This
+ * proxy flatten the three to the clients don't have to
+ * implement the managing logic.
+ */
+class ConfigurationProxy {
+public:
+   static QAbstractItemModel* deviceModel    ();
+   static QAbstractItemModel* channelModel   ();
+   static QAbstractItemModel* resolutionModel();
+   static QAbstractItemModel* rateModel      ();
+
+   static QItemSelectionModel* deviceSelectionModel    ();
+   static QItemSelectionModel* channelSelectionModel   ();
+   static QItemSelectionModel* resolutionSelectionModel();
+   static QItemSelectionModel* rateSelectionModel      ();
+};
+
+} //namespace Video
+
+#endif
diff --git a/src/video/devicemodel.cpp b/src/video/devicemodel.cpp
index db84231b..306df031 100644
--- a/src/video/devicemodel.cpp
+++ b/src/video/devicemodel.cpp
@@ -46,7 +46,6 @@ private Q_SLOTS:
 
 Video::DeviceModelPrivate::DeviceModelPrivate() : m_pDummyDevice(nullptr),m_pActiveDevice(nullptr)
 {
-
 }
 
 ///
@@ -61,6 +60,8 @@ d_ptr(new Video::DeviceModelPrivate())
 {
    m_spInstance = this;
    reload();
+   VideoManagerInterface& interface = DBus::VideoManager::instance();
+   connect(&interface, SIGNAL(deviceEvent()), this, SLOT(reload()));
 }
 
 Video::DeviceModel* Video::DeviceModel::instance()
@@ -84,7 +85,7 @@ QHash<int,QByteArray> Video::DeviceModel::roleNames() const
 ///Get data from the model
 QVariant Video::DeviceModel::data( const QModelIndex& idx, int role) const
 {
-   if(idx.column() == 0 && role == Qt::DisplayRole)
+   if(idx.isValid() && idx.column() == 0 && role == Qt::DisplayRole && d_ptr->m_lDevices.size() > idx.row())
       return QVariant(d_ptr->m_lDevices[idx.row()]->id());
    return QVariant();
 }
@@ -127,7 +128,7 @@ Video::DeviceModel::~DeviceModel()
 ///Save the current model over dbus
 void Video::DeviceModel::setActive(const QModelIndex& idx)
 {
-   if (idx.isValid()) {
+   if (idx.isValid() && d_ptr->m_lDevices.size() > idx.row()) {
       VideoManagerInterface& interface = DBus::VideoManager::instance();
       interface.setDefaultDevice(d_ptr->m_lDevices[idx.row()]->id());
       d_ptr->m_pActiveDevice = d_ptr->m_lDevices[idx.row()];
@@ -173,12 +174,15 @@ void Video::DeviceModel::reload()
    }
    foreach(Video::Device* dev, d_ptr->m_hDevices) {
       if (dev && devicesHash.key(dev).isEmpty()) {
-         delete dev;
+         dev->deleteLater();
       }
    }
+   d_ptr->m_pActiveDevice = nullptr;
    d_ptr->m_hDevices.clear();
    d_ptr->m_hDevices = devicesHash;
+   beginResetModel();
    d_ptr->m_lDevices = d_ptr->m_hDevices.values();
+   endResetModel();
 
    emit layoutChanged();
 //    channelModel   ()->reload();
@@ -212,7 +216,6 @@ Video::Device* Video::DeviceModel::activeDevice() const
    return d_ptr->m_pActiveDevice;
 }
 
-
 int Video::DeviceModel::activeIndex() const
 {
    return d_ptr->m_lDevices.indexOf(activeDevice());
-- 
GitLab