Select Git revision
collectionmodel.cpp
-
Guillaume Roguez authored
Change-Id: Ie560fab55336079277a710380e25d0889889606a
Guillaume Roguez authoredChange-Id: Ie560fab55336079277a710380e25d0889889606a
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
collectionmodel.cpp 14.74 KiB
/****************************************************************************
* Copyright (C) 2014-2017 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>