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 }