File indexing completed on 2024-03-24 04:43:40

0001 /* This file is part of the KDE project
0002    Copyright (C) 2008-2018 Jarosław Staniek <staniek@kde.org>
0003 
0004    This library is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This library is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this library; see the file COPYING.LIB.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA 02110-1301, USA.
0018 */
0019 
0020 #include "KPropertyEditorDataModel_p.h"
0021 #include "KPropertyWidgetsFactory.h"
0022 #include "KPropertySet_p.h"
0023 #include "KPropertyEditorView.h"
0024 
0025 #include <QHash>
0026 
0027 class Q_DECL_HIDDEN KPropertyEditorDataModel::Private
0028 {
0029 public:
0030     explicit Private(KPropertyEditorView *_view, KPropertySetIterator::Order _order = KPropertySetIterator::Order::Insertion)
0031         : view(_view), order(_order)
0032     {
0033         Q_ASSERT(view);
0034         if (!view) {
0035             kprCritical() << "KPropertyEditorDataModel requires a KPropertyView object";
0036         }
0037         Q_ASSERT(view->propertySet());
0038         if (!view->propertySet()) {
0039             kprCritical() << "KPropertyEditorDataModel requires a KPropertySet object";
0040         }
0041     }
0042     inline KPropertySet *set() { return view->propertySet(); }
0043     inline KPropertySetPrivate* set_d() { return KPropertySetPrivate::d(set()); }
0044     KPropertyEditorView *view;
0045     KProperty rootItem;
0046     KProperty groupItem; //!< Pseudo group item used for all group items
0047     QHash<QByteArray, QPersistentModelIndex> indicesForNames;
0048     KPropertySetIterator::Order order; //!< order of properties
0049 };
0050 
0051 // -------------------
0052 
0053 //! A property selector offering functor selecting only visible properties.
0054 /*! Used e.g. in EditorDataModel::index(). */
0055 class VisiblePropertySelector : public KPropertySelector
0056 {
0057 public:
0058     VisiblePropertySelector() {}
0059     bool operator()(const KProperty &prop) const override {
0060         return prop.isVisible();
0061     }
0062     KPropertySelector* clone() const override { return new VisiblePropertySelector(); }
0063 };
0064 
0065 // -------------------
0066 
0067 KPropertyEditorDataModel::KPropertyEditorDataModel(KPropertyEditorView *view,
0068                                                    KPropertySetIterator::Order order)
0069         : QAbstractItemModel(view)
0070         , d(new Private(view, order))
0071 {
0072     collectIndices();
0073 }
0074 
0075 KPropertyEditorDataModel::~KPropertyEditorDataModel()
0076 {
0077     delete d;
0078 }
0079 
0080 typedef QPair<QByteArray, QString> NameAndCaption;
0081 
0082 #if 0
0083 static inline bool nameAndCaptionLessThan(const NameAndCaption &n1, const NameAndCaption &n2)
0084 {
0085     return QString::compare(n1.second, n2.second, Qt::CaseInsensitive) < 0;
0086 }
0087 #endif
0088 
0089 void KPropertyEditorDataModel::collectIndices() const
0090 {
0091     d->indicesForNames.clear();
0092     if (!d->set()) {
0093         return;
0094     }
0095     if (d->view->groupsVisible()) {
0096         for (const QByteArray &groupName : d->set_d()->groupNames()) {
0097             const QList<QByteArray>* propertyNames = d->set_d()->propertyNamesForGroup(groupName);
0098             if (!propertyNames) {
0099                 continue;
0100             }
0101             int row = 0; // row within the group
0102             //! @todo Care about sorting
0103             for (const QByteArray &propertyName : *propertyNames) {
0104                 d->indicesForNames.insert(propertyName,
0105                                           QPersistentModelIndex(createIndex(row, 0, d->set_d()->property(propertyName))));
0106             }
0107         }
0108     } else {
0109         KPropertySetIterator it(*d->set(), VisiblePropertySelector());
0110         if (d->order == KPropertySetIterator::Order::Alphabetical) {
0111             it.setOrder(KPropertySetIterator::Order::Alphabetical);
0112         }
0113         for (int row = 0; it.current(); row++, ++it) { // flat list
0114             d->indicesForNames.insert(it.current()->name(),
0115                                       QPersistentModelIndex( createIndex(row, 0, it.current())));
0116         }
0117     }
0118 }
0119 
0120 QModelIndex KPropertyEditorDataModel::indexForPropertyName(const QByteArray& propertyName) const
0121 {
0122     return (const QModelIndex &)d->indicesForNames.value(propertyName);
0123 }
0124 
0125 QModelIndex KPropertyEditorDataModel::indexForColumn(const QModelIndex& index, int column) const
0126 {
0127     if (column == 0)
0128         return index;
0129     return createIndex(index.row(), column, propertyForIndex(index));
0130 }
0131 
0132 int KPropertyEditorDataModel::columnCount(const QModelIndex &parent) const
0133 {
0134     Q_UNUSED(parent);
0135     return 2;
0136 }
0137 
0138 QVariant KPropertyEditorDataModel::data(const QModelIndex &index, int role) const
0139 {
0140     if (!index.isValid() || !d->set()) {
0141         return QVariant();
0142     }
0143     const int col = index.column();
0144     const KProperty *prop = propertyForIndex(index);
0145     switch (role) {
0146     case PropertyGroupRole:
0147         return prop == &d->groupItem;
0148     case Qt::ToolTipRole:
0149         if (d->view->toolTipsVisible() && !prop->description().isEmpty()) {
0150             return prop->description();
0151         }
0152         break;
0153     default:
0154         break;
0155     }
0156     if (col == 0) {
0157         if (prop == &d->groupItem) {
0158             const QByteArray groupName(d->set_d()->groupName(index.row()));
0159             Q_ASSERT(!groupName.isEmpty());
0160             switch(role) {
0161             case Qt::DisplayRole:
0162                 return d->set()->groupCaption(groupName);
0163             case Qt::DecorationRole:
0164                 return QIcon::fromTheme(d->set()->groupIconName(groupName));
0165             default:;
0166             }
0167         } else if (role == Qt::DisplayRole) {
0168             if (!prop->captionForDisplaying().isEmpty()) {
0169                 return prop->captionForDisplaying();
0170             }
0171             return prop->name();
0172         }
0173         else if (role == PropertyModifiedRole) {
0174             return prop->isModified();
0175         }
0176     }
0177     else if (col == 1) {
0178         if (role == Qt::EditRole) {
0179             return prop->value();
0180         }
0181         else if (role == Qt::DisplayRole) {
0182             return KPropertyFactoryManager::self()->propertyValueToLocalizedString(prop);
0183         }
0184     }
0185     return QVariant();
0186 }
0187 
0188 Qt::ItemFlags KPropertyEditorDataModel::flags(const QModelIndex &index) const
0189 {
0190     if (!index.isValid() || !d->set()) {
0191         return Qt::ItemIsEnabled;
0192     }
0193     const int col = index.column();
0194     Qt::ItemFlags f = Qt::ItemIsEnabled;
0195     const KProperty *prop = propertyForIndex(index);
0196     if (prop != &d->groupItem) {
0197         f |= Qt::ItemIsSelectable;
0198         if (col == 1 && prop != &d->rootItem && !prop->isReadOnly() && !d->set()->isReadOnly()) {
0199             f |= Qt::ItemIsEditable;
0200         }
0201     }
0202     return f;
0203 }
0204 
0205 KProperty *KPropertyEditorDataModel::propertyForIndex(const QModelIndex &index) const
0206 {
0207     if (index.isValid()) {
0208         KProperty *item = static_cast<KProperty*>(index.internalPointer());
0209         if (item)
0210             return item;
0211     }
0212     return &d->rootItem;
0213 }
0214 
0215 QVariant KPropertyEditorDataModel::headerData(int section, Qt::Orientation orientation,
0216                                      int role) const
0217 {
0218     if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
0219         if (section == 0) {
0220             return tr("Name", "Property name");
0221         } else {
0222             return tr("Value", "Property value");
0223         }
0224     }
0225     return QVariant();
0226 }
0227 
0228 QModelIndex KPropertyEditorDataModel::index(int row, int column, const QModelIndex &parent) const
0229 {
0230     if (!d->set() || row < 0 || column < 0
0231         || /*!parent.isValid() ||*/ (parent.isValid() && parent.column() != 0))
0232     {
0233         return QModelIndex();
0234     }
0235 
0236     KProperty *parentItem = propertyForIndex(parent);
0237     KProperty *childItem = nullptr;
0238     if (parentItem == &d->rootItem && d->view->groupsVisible() && d->set_d()->hasGroups()) {
0239         // top level with groups: return group item
0240         return createIndex(row, column, &d->groupItem);
0241     } else if (parentItem == &d->rootItem || parentItem == &d->groupItem) {
0242         // top level without groups or group level: return top-level visible property item
0243         if (d->view->groupsVisible() && d->set_d()->hasGroups()) {
0244             const QByteArray groupName(d->set_d()->groupName(parent.row()));
0245             const QList<QByteArray>* propertyNames = d->set_d()->propertyNamesForGroup(groupName);
0246             if (propertyNames) {
0247                 int visiblePropertiesForGroup = -1;
0248                 //! @todo sort?
0249                 for (const QByteArray &propertyName : *propertyNames) {
0250                     KProperty *property = d->set_d()->property(propertyName);
0251                     if (property->isVisible()) {
0252                         ++visiblePropertiesForGroup;
0253                     }
0254                     if (visiblePropertiesForGroup == row) {
0255                         childItem = property;
0256                         break;
0257                     }
0258                 }
0259             }
0260         } else { // all properties, flat
0261             KPropertySetIterator it(*d->set(), VisiblePropertySelector());
0262             if (d->order == KPropertySetIterator::Order::Alphabetical) {
0263                 it.setOrder(KPropertySetIterator::Order::Alphabetical);
0264             }
0265             //! @todo use qBinaryFind()?
0266             for (int visibleRows = 0; visibleRows < row && it.current(); ++it) {
0267                 ++visibleRows;
0268             }
0269             childItem = it.current();
0270         }
0271     } else { // child properties of composed properties
0272         const QList<KProperty*>* children = parentItem->children();
0273         if (children) {
0274             childItem = children->value(row);
0275         }
0276     }
0277     if (!childItem) {
0278         return QModelIndex();
0279     }
0280     return createIndex(row, column, childItem);
0281 }
0282 
0283 QModelIndex KPropertyEditorDataModel::parent(const QModelIndex &index) const
0284 {
0285     if (!index.isValid() || !d->set()) {
0286         return QModelIndex();
0287     }
0288     const KProperty *childItem = propertyForIndex(index);
0289     if (childItem == &d->rootItem || childItem == &d->groupItem) {
0290         // parent for the root or a group item: null
0291         // (parent for a group item is root since group item is top level)
0292         return QModelIndex();
0293     }
0294     KProperty *parentItem = childItem->parent();
0295     if (parentItem) { // child property: parent property is the parent
0296         // find index of parent within the grandparent
0297         const int indexOfParentItem = d->set_d()->indexOfProperty(parentItem);
0298         return createIndex(indexOfParentItem, 0, parentItem);
0299     }
0300     if (d->view->groupsVisible() && d->set_d()->hasGroups()) {
0301         // top-level property within a group: group item is the parent
0302         const QByteArray group(d->set_d()->groupForProperty(childItem));
0303         const int indexOfGroup = d->set_d()->indexOfGroup(group);
0304         return createIndex(indexOfGroup, 0, &d->groupItem);
0305     }
0306     return QModelIndex();
0307 }
0308 
0309 int KPropertyEditorDataModel::rowCount(const QModelIndex &parent) const
0310 {
0311     if (!d->set()) {
0312         return 0;
0313     }
0314     KProperty *parentItem = propertyForIndex(parent);
0315     if (parentItem == &d->rootItem) { // top level: return group count or top-level properties count
0316         if (d->view->groupsVisible() && d->set_d()->hasGroups()) {
0317             return d->set_d()->groupNames().count();
0318         }
0319         return d->set()->count(VisiblePropertySelector()); // number of visible properties
0320     } else if (parentItem == &d->groupItem) { // group level: return property count within the group
0321         const QByteArray groupName = d->set_d()->groupName(parent.row());
0322         Q_ASSERT(!groupName.isEmpty());
0323         const QList<QByteArray>* propertyNames = d->set_d()->propertyNamesForGroup(groupName);
0324         Q_ASSERT(propertyNames);
0325         int visiblePropertiesForGroup = 0;
0326         for(const QByteArray &propertyName : *propertyNames) {
0327             if (d->set_d()->property(propertyName)->isVisible()) {
0328                 ++visiblePropertiesForGroup;
0329             }
0330         }
0331         return visiblePropertiesForGroup;
0332     }
0333     // property level: return child properties count
0334     const QList<KProperty*>* children = parentItem->children();
0335     return children ? children->count() : 0;
0336 }
0337 
0338 bool KPropertyEditorDataModel::setData(const QModelIndex &index, const QVariant &value,
0339                               int role)
0340 {
0341     if (role != Qt::EditRole)
0342         return false;
0343 
0344     KProperty *item = propertyForIndex(index);
0345     if (item == &d->rootItem || item == &d->groupItem)
0346         return false;
0347     item->setValue(value);
0348     //don't do that or cursor position and editor state will be reset:
0349     //emit dataChanged(index, index, {Qt::EditRole});
0350     return true;
0351 }
0352 
0353 bool KPropertyEditorDataModel::setHeaderData(int section, Qt::Orientation orientation,
0354                                     const QVariant &value, int role)
0355 {
0356     Q_UNUSED(section);
0357     Q_UNUSED(orientation);
0358     Q_UNUSED(value);
0359     Q_UNUSED(role);
0360     return false;
0361 }
0362 
0363 QModelIndex KPropertyEditorDataModel::buddy(const QModelIndex & idx) const
0364 {
0365     if (idx.column() == 0)
0366         return index( idx.row(), 1, parent(idx));
0367     return idx;
0368 }
0369 
0370 KPropertySet* KPropertyEditorDataModel::propertySet() const
0371 {
0372     return d->set();
0373 }
0374 
0375 void KPropertyEditorDataModel::setOrder(KPropertySetIterator::Order order)
0376 {
0377     if (d->order != order) {
0378         d->order = order;
0379         collectIndices();
0380     }
0381 }
0382 
0383 KPropertySetIterator::Order KPropertyEditorDataModel::order() const
0384 {
0385     return d->order;
0386 }
0387 
0388 bool KPropertyEditorDataModel::hasChildren(const QModelIndex & parent) const
0389 {
0390     if (!d->set()) {
0391         return false;
0392     }
0393     KProperty *parentItem = propertyForIndex(parent);
0394     if (parentItem == &d->rootItem) { // top level
0395         return d->set()->hasVisibleProperties();
0396     } else if (parentItem == &d->groupItem) { // group level
0397         const QByteArray groupName(d->set_d()->groupName(parent.row()));
0398         Q_ASSERT(!groupName.isEmpty());
0399         const QList<QByteArray>* propertyNames = d->set_d()->propertyNamesForGroup(groupName);
0400         Q_ASSERT(propertyNames);
0401         for (const QByteArray &propertyName : *propertyNames) {
0402             if (d->set_d()->property(propertyName)->isVisible()) {
0403                 return true; // at least one visible property in this group
0404             }
0405         }
0406         return false; // no visible properties in this group
0407     }
0408     // property level
0409     const QList<KProperty*>* children = parentItem->children();
0410     return children && !children->isEmpty();
0411 }
0412 
0413 void KPropertyEditorDataModel::updateGroupsVisibility()
0414 {
0415     beginResetModel();
0416     collectIndices();
0417     endResetModel();
0418 }