Project 'savoirfairelinux/ring-client-windows' was moved to 'savoirfairelinux/jami-client-windows'. Please update any links and bookmarks that may still have the old path.
Select Git revision
currentaccountcombobox.h
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
collectionmodel.cpp 14.74 KiB
/****************************************************************************
* Copyright (C) 2014-2016 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 "collectionmodel.h"
//Qt
#include <QtCore/QCoreApplication>
#include <QtCore/QSortFilterProxyModel>
//Ring
#include "collectionmanagerinterface.h"
#include "globalinstances.h"
#include "interfaces/itemmodelstateserializeri.h"
#include "collectionextensioninterface.h"
#include "private/collectionmodel_p.h"
class ManageableCollectionProxy final : public QSortFilterProxyModel
{
public:
ManageableCollectionProxy(QAbstractItemModel* parent) : QSortFilterProxyModel(parent)
{
setSourceModel(parent);
}
virtual bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override;
};
CollectionModelPrivate::CollectionModelPrivate(CollectionModel* parent) : QObject(parent),q_ptr(parent),
m_pManageableProxy(nullptr),m_NewCollectionMutex(QMutex::Recursive)
{}
CollectionModelPrivate::~CollectionModelPrivate()
{
}
CollectionModelPrivate::ProxyItem::~ProxyItem()
{
foreach(ProxyItem* i, m_Children)
delete i;
}
CollectionModel::CollectionModel(QObject* parent) : QAbstractTableModel(parent), d_ptr(new CollectionModelPrivate(this))
{
load();
}
CollectionModel::~CollectionModel()
{
while (d_ptr->m_lTopLevelBackends.size()) {
CollectionModelPrivate::ProxyItem* item = d_ptr->m_lTopLevelBackends[0];
d_ptr->m_lTopLevelBackends.remove(0);
while (item->m_Children.size()) {
//FIXME I don't think it can currently happen, but there may be
//more than 2 levels.
CollectionModelPrivate::ProxyItem* item2 = item->m_Children[0];
item->m_Children.remove(0);
delete item2;
}
delete item;
}
}
CollectionModel& CollectionModel::instance()
{
static auto instance = new CollectionModel(QCoreApplication::instance());
return *instance;
}
QHash<int,QByteArray> CollectionModel::roleNames() const
{
static QHash<int, QByteArray> roles = QAbstractItemModel::roleNames();
static bool initRoles = false;
if (!initRoles) {
initRoles = true;
roles[static_cast<int>(Role::count ) ] = "count" ;
roles[static_cast<int>(Role::supportNone ) ] = "supportNone" ;
roles[static_cast<int>(Role::supportLoad ) ] = "supportLoad" ;
roles[static_cast<int>(Role::supportSave ) ] = "supportSave" ;
roles[static_cast<int>(Role::supportEdit ) ] = "supportEdit" ;
roles[static_cast<int>(Role::supportProbe ) ] = "supportProbe" ;
roles[static_cast<int>(Role::supportAdd ) ] = "supportAdd" ;
roles[static_cast<int>(Role::supportSave_all ) ] = "supportSave_all" ;
roles[static_cast<int>(Role::supportClear ) ] = "supportClear" ;
roles[static_cast<int>(Role::supportRemove ) ] = "supportRemove" ;
roles[static_cast<int>(Role::supportExport ) ] = "supportExport" ;
roles[static_cast<int>(Role::supportImport ) ] = "supportImport" ;
roles[static_cast<int>(Role::isEnableable ) ] = "isEnableable" ;
roles[static_cast<int>(Role::isDisableable ) ] = "isDisableable" ;
roles[static_cast<int>(Role::isManageable ) ] = "isManageable" ;
roles[static_cast<int>(Role::hasManageableChildren) ] = "hasManageableChildren" ;
}
return roles;
}
QVariant CollectionModel::data (const QModelIndex& idx, int role) const
{
if (idx.isValid()) {
CollectionModelPrivate::ProxyItem* item = static_cast<CollectionModelPrivate::ProxyItem*>(idx.internalPointer());
/*if (idx.column() > 0 && item->collection)
return d_ptr->m_lExtensions[idx.column()-1]->data(item->collection,idx,role);*/
if (item->collection) {
switch(role) {
case Qt::DisplayRole:
return item->collection->name();
case Qt::DecorationRole:
return item->collection->icon();
case Qt::UserRole: // and Role::count
return 'x'+QString::number(item->collection->size());
case Qt::CheckStateRole: {
try {
GlobalInstances::itemModelStateSerializer(); //TODO do better than that
return item->collection->isEnabled()?Qt::Checked:Qt::Unchecked;
} catch (...) {
break;
}
}
case static_cast<int>(Role::hasManageableChildren):
return item->manageableCount ? true : false;
};
//Retro-map to the SupportedFeatures //WARNING if SupportedFeatures change, this need to be updated
if (role > Qt::UserRole && role <= Qt::UserRole+14) {
return (bool) (role == Qt::UserRole+1 ? true : bool(item->collection->supportedFeatures() & ((CollectionInterface::SupportedFeatures)(0x01 << (role - Qt::UserRole - 2)))));
}
}
else {
switch(role) {
case Qt::DisplayRole:
return item->m_AltName;
case static_cast<int>(Role::hasManageableChildren):
return item->manageableCount ? true : false;
}
}
}
return QVariant();
}
int CollectionModel::rowCount (const QModelIndex& parent) const
{
if (!parent.isValid()) {
return d_ptr->m_lTopLevelBackends.size();
}
else {
CollectionModelPrivate::ProxyItem* item = static_cast<CollectionModelPrivate::ProxyItem*>(parent.internalPointer());
return item->m_Children.size();
}
}
int CollectionModel::columnCount (const QModelIndex& parent) const
{
Q_UNUSED(parent)
return 1+d_ptr->m_lExtensions.size();
}
Qt::ItemFlags CollectionModel::flags(const QModelIndex& idx) const
{
if (!idx.isValid())
return 0;
CollectionModelPrivate::ProxyItem* item = static_cast<CollectionModelPrivate::ProxyItem*>(idx.internalPointer());
//Categories can only be displayed
if (!item->collection)
return Qt::ItemIsEnabled;
/*if (idx.column() > 0) {
//Make sure the cell is disabled if the row is
Qt::ItemFlags f = d_ptr->m_lExtensions[idx.column()-1]->flags(item->collection,idx);
return (((f&Qt::ItemIsEnabled)&&(!item->collection->isEnabled()))?f^Qt::ItemIsEnabled:f);
}*/
const bool checkable = item->collection->supportedFeatures() & (CollectionInterface::SupportedFeatures::ENABLEABLE |
CollectionInterface::SupportedFeatures::DISABLEABLE | CollectionInterface::SupportedFeatures::MANAGEABLE );
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | (checkable?Qt::ItemIsUserCheckable:Qt::NoItemFlags);
}
bool CollectionModel::setData (const QModelIndex& idx, const QVariant &value, int role )
{
Q_UNUSED(idx)
Q_UNUSED(value)
Q_UNUSED(role)
/*if (idx.isValid() && idx.column() > 0) {
CollectionModelPrivate::ProxyItem* item = static_cast<CollectionModelPrivate::ProxyItem*>(idx.internalPointer());
return (!item->collection)?false : d_ptr->m_lExtensions[idx.column()-1]->setData(item->collection,idx,value,role);
}*/
if (role == Qt::CheckStateRole && idx.column() == 0) {
CollectionModelPrivate::ProxyItem* item = static_cast<CollectionModelPrivate::ProxyItem*>(idx.internalPointer());
if (item && item->collection) {
const bool old = item->collection->isEnabled();
try {
GlobalInstances::itemModelStateSerializer().setChecked(item->collection,value==Qt::Checked);
emit dataChanged(index(idx.row(),0),index(idx.row(),columnCount()-1));
// The state change has been cached in the interface, but is not applied yet.
// it is necessary to keep track of the collection states that changed
if (d_ptr->m_hPendingChanges.contains(item->collection)) {
d_ptr->m_hPendingChanges.remove(item->collection);
emit checkStateChanged();
}
else if (old != (value==Qt::Checked)) {
d_ptr->m_hPendingChanges[item->collection] = true;
emit checkStateChanged();
}
return true;
} catch (...) {
qDebug() << "no ItemModelStateSerializer to set collection state";
}
}
}
return false;
}
QModelIndex CollectionModel::parent( const QModelIndex& idx ) const
{
if (idx.isValid()) {
CollectionModelPrivate::ProxyItem* item = static_cast<CollectionModelPrivate::ProxyItem*>(idx.internalPointer());
if (!item->parent)
return QModelIndex();
return createIndex(item->row,item->col,item->parent);
}
return QModelIndex();
}
QModelIndex CollectionModel::index( int row, int column, const QModelIndex& parent ) const
{
if (parent.isValid() && parent.model() == this && row < rowCount(parent)) {
CollectionModelPrivate::ProxyItem* parentItem = static_cast<CollectionModelPrivate::ProxyItem*>(parent.internalPointer());
CollectionModelPrivate::ProxyItem* item = nullptr;
if (row < parentItem->m_Children.size())
item = parentItem->m_Children[row];
else {
return QModelIndex();
}
item->row = row; //FIXME dead code?
item->col = column;
return createIndex(row,column,item);
}
else { //Top level
CollectionModelPrivate::ProxyItem* item = nullptr;
if (row < d_ptr->m_lTopLevelBackends.size())
item = d_ptr->m_lTopLevelBackends[row];
else {
return QModelIndex();
}
item->row = row; //FIXME dead code?
item->col = column;
return createIndex(item->row,item->col,item);
}
}
void CollectionModelPrivate::slotUpdate()
{
emit q_ptr->layoutChanged();
}
QVariant CollectionModel::headerData(int section, Qt::Orientation orientation, int role) const
{
Q_UNUSED(section)
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
/*if (section > 0)
return d_ptr->m_lExtensions[section-1]->headerName();*/
return QVariant(tr("Name"));
}
return QVariant();
}
bool CollectionModel::save()
{
try {
// Enable/disabled collections that changed
for (QHash<CollectionInterface*,bool>::const_iterator i = d_ptr->m_hPendingChanges.begin(); i != d_ptr->m_hPendingChanges.end(); ++i) {
i.key()->enable(GlobalInstances::itemModelStateSerializer().isChecked(i.key()));
}
d_ptr->m_hPendingChanges.clear();
return GlobalInstances::itemModelStateSerializer().save();
} catch (...) {
qDebug() << "no ItemModelStateSerializer exists";
}
return false;
}
bool CollectionModel::load()
{
try {
return GlobalInstances::itemModelStateSerializer().load();
} catch (...) {
return false;
}
}
///Return the collection at a given index
CollectionInterface* CollectionModel::collectionAt(const QModelIndex& index)
{
if (!index.isValid())
return nullptr;
return static_cast<CollectionModelPrivate::ProxyItem*>(index.internalPointer())->collection;
}
void CollectionModel::addExtension(CollectionExtensionInterface* extension)
{
emit layoutAboutToBeChanged();
d_ptr->m_lExtensions << extension;
connect(extension,SIGNAL(dataChanged(QModelIndex)),d_ptr.data(),SLOT(slotExtensionDataChanged(QModelIndex)));
emit layoutChanged();
}
void CollectionModelPrivate::slotExtensionDataChanged(const QModelIndex& idx)
{
emit q_ptr->dataChanged(idx,idx);
}
void CollectionModelPrivate::registerNew(CollectionInterface* col)
{
if (!col)
return;
QMutexLocker l(&m_NewCollectionMutex);
ProxyItem* cat = m_hCategories[col->category()];
if (col->category().isEmpty()){
//TODO implement a default category
}
if (!cat) {
cat = new ProxyItem();
cat->parent = nullptr;
cat->row = m_lTopLevelBackends.size();
cat->col = 0;
cat->m_AltName = col->category();
cat->collection = nullptr;
m_hCategories[cat->m_AltName] = cat;
m_hBackendsNodes[col] = cat;
q_ptr->beginInsertRows(QModelIndex(),m_lTopLevelBackends.size(),m_lTopLevelBackends.size());
m_lTopLevelBackends << cat;
q_ptr->endInsertRows();
}
ProxyItem* item = new ProxyItem();
const bool hasParent = col->parent();
ProxyItem* par = hasParent?m_hBackendsNodes[col->parent()] : cat;
item->parent = par;
item->row = par->m_Children.size();
item->col = 0;
q_ptr->beginInsertRows(q_ptr->createIndex(par->row,par->col,par),par->m_Children.size(),par->m_Children.size());
par->m_Children << item;
q_ptr->endInsertRows();
//Make sure the manageable proxy get noticed things changed
if (col->supportedFeatures() & CollectionInterface::SupportedFeatures::MANAGEABLE) {
item->manageableCount++;
while(par) {
par->manageableCount++;
const QModelIndex& idx = q_ptr->createIndex(par->row,0,par);
emit q_ptr->dataChanged(idx,idx);
par = par->parent;
}
}
item->collection = col ;
m_hBackendsNodes[col] = item;
}
bool ManageableCollectionProxy::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
{
CollectionModelPrivate::ProxyItem* item = static_cast<CollectionModelPrivate::ProxyItem*>(sourceModel()->index(source_row,0,source_parent).internalPointer());
return item ? item->manageableCount : false;
}
/**
* Filter the CollectionModel to only keep the manageable collections. Those
* collections can be exposed and configured in the UI
*/
QAbstractItemModel* CollectionModel::manageableCollections() const
{
if (!d_ptr->m_pManageableProxy)
d_ptr->m_pManageableProxy = new ManageableCollectionProxy(const_cast<CollectionModel*>(this));
return d_ptr->m_pManageableProxy;
}
#include <collectionmodel.moc>