Changeset View
Changeset View
Standalone View
Standalone View
src/KPropertyEditorDataModel.cpp
1 | /* This file is part of the KDE project | 1 | /* This file is part of the KDE project | ||
---|---|---|---|---|---|
2 | Copyright (C) 2008-2009 Jarosław Staniek <staniek@kde.org> | 2 | Copyright (C) 2008-2017 Jarosław Staniek <staniek@kde.org> | ||
3 | 3 | | |||
4 | This library is free software; you can redistribute it and/or | 4 | This library is free software; you can redistribute it and/or | ||
5 | modify it under the terms of the GNU Library General Public | 5 | modify it under the terms of the GNU Library General Public | ||
6 | License as published by the Free Software Foundation; either | 6 | License as published by the Free Software Foundation; either | ||
7 | version 2 of the License, or (at your option) any later version. | 7 | version 2 of the License, or (at your option) any later version. | ||
8 | 8 | | |||
9 | This library is distributed in the hope that it will be useful, | 9 | This library is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
Show All 18 Lines | 28 | public: | |||
29 | explicit Private(KPropertySet *_set, KPropertySetIterator::Order _order = KPropertySetIterator::InsertionOrder) | 29 | explicit Private(KPropertySet *_set, KPropertySetIterator::Order _order = KPropertySetIterator::InsertionOrder) | ||
30 | : set(_set), order(_order) | 30 | : set(_set), order(_order) | ||
31 | { | 31 | { | ||
32 | Q_ASSERT(set); | 32 | Q_ASSERT(set); | ||
33 | if (!set) { | 33 | if (!set) { | ||
34 | kprCritical() << "KPropertyEditorDataModel requires a KPropertySet object"; | 34 | kprCritical() << "KPropertyEditorDataModel requires a KPropertySet object"; | ||
35 | } | 35 | } | ||
36 | } | 36 | } | ||
37 | inline KPropertySetPrivate* set_d() { return KPropertySetPrivate::d(set); } | ||||
37 | KPropertySet *set; | 38 | KPropertySet *set; | ||
38 | KProperty rootItem; | 39 | KProperty rootItem; | ||
40 | KProperty groupItem; //!< Pseudo group item used for all group items | ||||
39 | QHash<QByteArray, QPersistentModelIndex> indicesForNames; | 41 | QHash<QByteArray, QPersistentModelIndex> indicesForNames; | ||
40 | KPropertySetIterator::Order order; //!< order of properties | 42 | KPropertySetIterator::Order order; //!< order of properties | ||
43 | bool groupsVisible = true; | ||||
41 | }; | 44 | }; | ||
42 | 45 | | |||
43 | // ------------------- | 46 | // ------------------- | ||
44 | 47 | | |||
45 | //! A property selector offering functor selecting only visible properties. | 48 | //! A property selector offering functor selecting only visible properties. | ||
46 | /*! Used e.g. in EditorDataModel::index(). */ | 49 | /*! Used e.g. in EditorDataModel::index(). */ | ||
47 | class VisiblePropertySelector : public KPropertySelector | 50 | class VisiblePropertySelector : public KPropertySelector | ||
48 | { | 51 | { | ||
Show All 26 Lines | |||||
75 | static inline bool nameAndCaptionLessThan(const NameAndCaption &n1, const NameAndCaption &n2) | 78 | static inline bool nameAndCaptionLessThan(const NameAndCaption &n1, const NameAndCaption &n2) | ||
76 | { | 79 | { | ||
77 | return QString::compare(n1.second, n2.second, Qt::CaseInsensitive) < 0; | 80 | return QString::compare(n1.second, n2.second, Qt::CaseInsensitive) < 0; | ||
78 | } | 81 | } | ||
79 | #endif | 82 | #endif | ||
80 | 83 | | |||
81 | void KPropertyEditorDataModel::collectIndices() const | 84 | void KPropertyEditorDataModel::collectIndices() const | ||
82 | { | 85 | { | ||
86 | d->indicesForNames.clear(); | ||||
87 | if (d->groupsVisible) { | ||||
88 | for (const QByteArray &groupName : d->set_d()->groupNames) { | ||||
89 | const QList<QByteArray>* propertyNames = d->set_d()->propertiesOfGroup.value(groupName); | ||||
90 | if (!propertyNames) { | ||||
91 | continue; | ||||
92 | } | ||||
93 | int row = 0; // row within the group | ||||
94 | //! @todo Care about sorting | ||||
95 | for (const QByteArray &propertyName : *propertyNames) { | ||||
96 | d->indicesForNames.insert(propertyName, | ||||
97 | QPersistentModelIndex(createIndex(row, 0, d->set_d()->property(propertyName)))); | ||||
98 | } | ||||
99 | } | ||||
100 | } else { | ||||
83 | KPropertySetIterator it(*d->set, VisiblePropertySelector()); | 101 | KPropertySetIterator it(*d->set, VisiblePropertySelector()); | ||
84 | if (d->order == KPropertySetIterator::AlphabeticalOrder) { | 102 | if (d->order == KPropertySetIterator::AlphabeticalOrder) { | ||
85 | it.setOrder(KPropertySetIterator::AlphabeticalOrder); | 103 | it.setOrder(KPropertySetIterator::AlphabeticalOrder); | ||
86 | } | 104 | } | ||
87 | d->indicesForNames.clear(); | 105 | for (int row = 0; it.current(); row++, ++it) { // flat list | ||
88 | for (int row = 0; it.current(); row++, ++it) { | 106 | d->indicesForNames.insert(it.current()->name(), | ||
89 | // kprDebug() << it.current()->name() << "->" << row; | 107 | QPersistentModelIndex( createIndex(row, 0, it.current()))); | ||
90 | d->indicesForNames.insert( it.current()->name(), QPersistentModelIndex( createIndex(row, 0, it.current()) ) ); | 108 | } | ||
91 | } | 109 | } | ||
92 | } | 110 | } | ||
93 | 111 | | |||
94 | QModelIndex KPropertyEditorDataModel::indexForPropertyName(const QByteArray& propertyName) const | 112 | QModelIndex KPropertyEditorDataModel::indexForPropertyName(const QByteArray& propertyName) const | ||
95 | { | 113 | { | ||
96 | return (const QModelIndex &)d->indicesForNames.value(propertyName); | 114 | return (const QModelIndex &)d->indicesForNames.value(propertyName); | ||
97 | } | 115 | } | ||
98 | 116 | | |||
Show All 11 Lines | |||||
110 | } | 128 | } | ||
111 | 129 | | |||
112 | QVariant KPropertyEditorDataModel::data(const QModelIndex &index, int role) const | 130 | QVariant KPropertyEditorDataModel::data(const QModelIndex &index, int role) const | ||
113 | { | 131 | { | ||
114 | if (!index.isValid()) | 132 | if (!index.isValid()) | ||
115 | return QVariant(); | 133 | return QVariant(); | ||
116 | 134 | | |||
117 | const int col = index.column(); | 135 | const int col = index.column(); | ||
136 | const KProperty *prop = propertyForIndex(index); | ||||
137 | if (role == PropertyGroupRole) { | ||||
138 | return prop == &d->groupItem; | ||||
139 | } | ||||
118 | if (col == 0) { | 140 | if (col == 0) { | ||
119 | KProperty *prop = propertyForIndex(index); | 141 | if (prop == &d->groupItem) { | ||
120 | if (role == Qt::DisplayRole) { | 142 | const QByteArray groupName(d->set_d()->groupNames.value(index.row())); | ||
143 | Q_ASSERT(!groupName.isEmpty()); | ||||
144 | switch(role) { | ||||
145 | case Qt::DisplayRole: | ||||
146 | return d->set->groupCaption(groupName); | ||||
147 | case Qt::DecorationRole: | ||||
148 | return QIcon::fromTheme(d->set->groupIconName(groupName)); | ||||
149 | default:; | ||||
150 | } | ||||
151 | } else if (role == Qt::DisplayRole) { | ||||
121 | if (!prop->caption().isEmpty()) | 152 | if (!prop->caption().isEmpty()) | ||
122 | return prop->caption(); | 153 | return prop->caption(); | ||
123 | return prop->name(); | 154 | return prop->name(); | ||
124 | } | 155 | } | ||
125 | else if (role == PropertyModifiedRole) { | 156 | else if (role == PropertyModifiedRole) { | ||
126 | return prop->isModified(); | 157 | return prop->isModified(); | ||
127 | } | 158 | } | ||
128 | } | 159 | } | ||
129 | else if (col == 1) { | 160 | else if (col == 1) { | ||
130 | KProperty *prop = propertyForIndex(index); | | |||
131 | if (role == Qt::EditRole) { | 161 | if (role == Qt::EditRole) { | ||
132 | return prop->value(); | 162 | return prop->value(); | ||
133 | } | 163 | } | ||
134 | else if (role == Qt::DisplayRole) { | 164 | else if (role == Qt::DisplayRole) { | ||
135 | return KPropertyFactoryManager::self()->propertyValueToLocalizedString(prop); | 165 | return KPropertyFactoryManager::self()->propertyValueToLocalizedString(prop); | ||
136 | } | 166 | } | ||
137 | } | 167 | } | ||
138 | return QVariant(); | 168 | return QVariant(); | ||
139 | } | 169 | } | ||
140 | 170 | | |||
141 | Qt::ItemFlags KPropertyEditorDataModel::flags(const QModelIndex &index) const | 171 | Qt::ItemFlags KPropertyEditorDataModel::flags(const QModelIndex &index) const | ||
142 | { | 172 | { | ||
143 | if (!index.isValid()) | 173 | if (!index.isValid()) | ||
144 | return Qt::ItemIsEnabled; | 174 | return Qt::ItemIsEnabled; | ||
145 | 175 | | |||
146 | const int col = index.column(); | 176 | const int col = index.column(); | ||
147 | Qt::ItemFlags f = Qt::ItemIsEnabled | Qt::ItemIsSelectable; | 177 | Qt::ItemFlags f = Qt::ItemIsEnabled; | ||
148 | KProperty *prop = propertyForIndex(index); | 178 | const KProperty *prop = propertyForIndex(index); | ||
149 | if (prop) { | 179 | if (prop != &d->groupItem) { | ||
150 | if (col == 1 && !prop->isReadOnly() && !d->set->isReadOnly()) { | 180 | f |= Qt::ItemIsSelectable; | ||
181 | if (col == 1 && prop != &d->rootItem && !prop->isReadOnly() && !d->set->isReadOnly()) { | ||||
151 | f |= Qt::ItemIsEditable; | 182 | f |= Qt::ItemIsEditable; | ||
152 | } | 183 | } | ||
153 | } | 184 | } | ||
154 | return f; | 185 | return f; | ||
155 | } | 186 | } | ||
156 | 187 | | |||
157 | KProperty *KPropertyEditorDataModel::propertyForIndex(const QModelIndex &index) const | 188 | KProperty *KPropertyEditorDataModel::propertyForIndex(const QModelIndex &index) const | ||
158 | { | 189 | { | ||
Show All 15 Lines | 204 | } else { | |||
174 | return tr("Value", "Property value"); | 205 | return tr("Value", "Property value"); | ||
175 | } | 206 | } | ||
176 | } | 207 | } | ||
177 | return QVariant(); | 208 | return QVariant(); | ||
178 | } | 209 | } | ||
179 | 210 | | |||
180 | QModelIndex KPropertyEditorDataModel::index(int row, int column, const QModelIndex &parent) const | 211 | QModelIndex KPropertyEditorDataModel::index(int row, int column, const QModelIndex &parent) const | ||
181 | { | 212 | { | ||
182 | if (parent.isValid() && parent.column() != 0) | 213 | //qDebug() << row << column << parent; | ||
214 | if (row < 0 || column < 0 || /*!parent.isValid() ||*/ (parent.isValid() && parent.column() != 0)) { | ||||
piggz: to be left? | |||||
183 | return QModelIndex(); | 215 | return QModelIndex(); | ||
216 | } | ||||
184 | 217 | | |||
185 | KProperty *parentItem = propertyForIndex(parent); | 218 | KProperty *parentItem = propertyForIndex(parent); | ||
186 | KProperty *childItem; | 219 | KProperty *childItem = nullptr; | ||
187 | if (parentItem == &d->rootItem) { // special case: top level | 220 | if (parentItem == &d->rootItem && d->groupsVisible && d->set_d()->hasGroups()) { | ||
188 | int visibleRows = 0; | 221 | // top level with groups: return group item | ||
222 | return createIndex(row, column, &d->groupItem); | ||||
223 | } else if (parentItem == &d->rootItem || parentItem == &d->groupItem) { | ||||
224 | // top level without groups or group level: return top-level visible property item | ||||
225 | if (d->groupsVisible && d->set_d()->hasGroups()) { | ||||
226 | const QByteArray groupName(d->set_d()->groupNames.value(parent.row())); | ||||
227 | const QList<QByteArray>* propertyNames = d->set_d()->propertiesOfGroup.value(groupName); | ||||
228 | if (propertyNames) { | ||||
229 | int visiblePropertiesForGroup = -1; | ||||
230 | //! @todo sort? | ||||
231 | for (const QByteArray &propertyName : *propertyNames) { | ||||
232 | KProperty *property = d->set_d()->property(propertyName); | ||||
233 | if (property->isVisible()) { | ||||
234 | ++visiblePropertiesForGroup; | ||||
235 | } | ||||
236 | if (visiblePropertiesForGroup == row) { | ||||
237 | childItem = property; | ||||
238 | break; | ||||
239 | } | ||||
240 | } | ||||
241 | } | ||||
242 | } else { // all properties, flat | ||||
189 | KPropertySetIterator it(*d->set, VisiblePropertySelector()); | 243 | KPropertySetIterator it(*d->set, VisiblePropertySelector()); | ||
190 | if (d->order == KPropertySetIterator::AlphabeticalOrder) { | 244 | if (d->order == KPropertySetIterator::AlphabeticalOrder) { | ||
191 | it.setOrder(KPropertySetIterator::AlphabeticalOrder); | 245 | it.setOrder(KPropertySetIterator::AlphabeticalOrder); | ||
192 | } | 246 | } | ||
193 | //! @todo use qBinaryFind()? | 247 | //! @todo use qBinaryFind()? | ||
194 | for (; visibleRows < row && it.current(); visibleRows++, ++it) | 248 | for (int visibleRows = 0; visibleRows < row && it.current(); ++it) { | ||
195 | ; | 249 | ++visibleRows; | ||
250 | } | ||||
196 | childItem = it.current(); | 251 | childItem = it.current(); | ||
197 | } else { | 252 | } | ||
253 | } else { // child properties of composed properties | ||||
198 | const QList<KProperty*>* children = parentItem->children(); | 254 | const QList<KProperty*>* children = parentItem->children(); | ||
199 | if (!children) | 255 | if (children) { | ||
200 | return QModelIndex(); | | |||
201 | childItem = children->value(row); | 256 | childItem = children->value(row); | ||
202 | } | 257 | } | ||
203 | if (!childItem) | 258 | } | ||
259 | if (!childItem) { | ||||
204 | return QModelIndex(); | 260 | return QModelIndex(); | ||
261 | } | ||||
205 | return createIndex(row, column, childItem); | 262 | return createIndex(row, column, childItem); | ||
206 | } | 263 | } | ||
207 | 264 | | |||
208 | QModelIndex KPropertyEditorDataModel::parent(const QModelIndex &index) const | 265 | QModelIndex KPropertyEditorDataModel::parent(const QModelIndex &index) const | ||
209 | { | 266 | { | ||
210 | if (!index.isValid()) | 267 | if (!index.isValid()) { | ||
211 | return QModelIndex(); | 268 | return QModelIndex(); | ||
212 | 269 | } | |||
213 | KProperty *childItem = propertyForIndex(index); | 270 | const KProperty *childItem = propertyForIndex(index); | ||
271 | if (childItem == &d->rootItem || childItem == &d->groupItem) { | ||||
272 | // parent for the root or a group item: null | ||||
273 | // (parent for a group item is root since group item is top level) | ||||
274 | return QModelIndex(); | ||||
275 | } | ||||
214 | KProperty *parentItem = childItem->parent(); | 276 | KProperty *parentItem = childItem->parent(); | ||
215 | 277 | if (parentItem) { // child property: parent property is the parent | |||
216 | if (!parentItem) | 278 | // find index of parent within the grandparent | ||
279 | const int indexOfParentItem = d->set_d()->indexOfProperty(parentItem); | ||||
280 | return createIndex(indexOfParentItem, 0, parentItem); | ||||
281 | } | ||||
282 | if (d->groupsVisible && d->set_d()->hasGroups()) { | ||||
283 | // top-level property within a group: group item is the parent | ||||
284 | const QByteArray group(d->set_d()->groupForProperty(childItem)); | ||||
285 | const int indexOfGroup = d->set_d()->groupNames.indexOf(group); | ||||
286 | return createIndex(indexOfGroup, 0, &d->groupItem); | ||||
287 | } | ||||
217 | return QModelIndex(); | 288 | return QModelIndex(); | ||
218 | | ||||
219 | const QList<KProperty*>* children = parentItem->children(); | | |||
220 | Q_ASSERT(children); | | |||
221 | const int indexOfItem = children->indexOf(childItem); | | |||
222 | Q_ASSERT(indexOfItem != -1); | | |||
223 | | ||||
224 | return createIndex(indexOfItem, 0, parentItem); | | |||
225 | } | 289 | } | ||
226 | 290 | | |||
227 | int KPropertyEditorDataModel::rowCount(const QModelIndex &parent) const | 291 | int KPropertyEditorDataModel::rowCount(const QModelIndex &parent) const | ||
228 | { | 292 | { | ||
229 | KProperty *parentItem = propertyForIndex(parent); | 293 | KProperty *parentItem = propertyForIndex(parent); | ||
230 | if (!parentItem || parentItem == &d->rootItem) { // top level | 294 | if (parentItem == &d->rootItem) { // top level: return group count or top-level properties count | ||
231 | return d->set->count(VisiblePropertySelector()); | 295 | if (d->groupsVisible && d->set_d()->hasGroups()) { | ||
296 | return d->set_d()->groupNames.count(); | ||||
297 | } | ||||
298 | return d->set->count(VisiblePropertySelector()); // number of visible properties | ||||
299 | } else if (parentItem == &d->groupItem) { // group level: return property count within the group | ||||
300 | const QByteArray groupName = d->set_d()->groupNames.value(parent.row()); | ||||
301 | Q_ASSERT(!groupName.isEmpty()); | ||||
302 | const QList<QByteArray>* propertyNames = d->set_d()->propertiesOfGroup.value(groupName); | ||||
303 | Q_ASSERT(propertyNames); | ||||
304 | int visiblePropertiesForGroup = 0; | ||||
305 | for(const QByteArray &propertyName : *propertyNames) { | ||||
306 | if (d->set_d()->property(propertyName)->isVisible()) { | ||||
307 | ++visiblePropertiesForGroup; | ||||
308 | } | ||||
309 | } | ||||
310 | return visiblePropertiesForGroup; | ||||
232 | } | 311 | } | ||
312 | // property level: return child properties count | ||||
233 | const QList<KProperty*>* children = parentItem->children(); | 313 | const QList<KProperty*>* children = parentItem->children(); | ||
234 | return children ? children->count() : 0; | 314 | return children ? children->count() : 0; | ||
235 | } | 315 | } | ||
236 | 316 | | |||
237 | bool KPropertyEditorDataModel::setData(const QModelIndex &index, const QVariant &value, | 317 | bool KPropertyEditorDataModel::setData(const QModelIndex &index, const QVariant &value, | ||
238 | int role) | 318 | int role) | ||
239 | { | 319 | { | ||
240 | if (role != Qt::EditRole) | 320 | if (role != Qt::EditRole) | ||
241 | return false; | 321 | return false; | ||
242 | 322 | | |||
243 | KProperty *item = propertyForIndex(index); | 323 | KProperty *item = propertyForIndex(index); | ||
244 | if (item == &d->rootItem) | 324 | if (item == &d->rootItem || item == &d->groupItem) | ||
245 | return false; | 325 | return false; | ||
246 | item->setValue(value); | 326 | item->setValue(value); | ||
247 | //don't do that or cursor position and editor state will be reset: | 327 | //don't do that or cursor position and editor state will be reset: | ||
248 | //emit dataChanged(index, index, {Qt::EditRole}); | 328 | //emit dataChanged(index, index, {Qt::EditRole}); | ||
249 | return true; | 329 | return true; | ||
250 | } | 330 | } | ||
251 | 331 | | |||
252 | bool KPropertyEditorDataModel::setHeaderData(int section, Qt::Orientation orientation, | 332 | bool KPropertyEditorDataModel::setHeaderData(int section, Qt::Orientation orientation, | ||
Show All 29 Lines | |||||
282 | KPropertySetIterator::Order KPropertyEditorDataModel::order() const | 362 | KPropertySetIterator::Order KPropertyEditorDataModel::order() const | ||
283 | { | 363 | { | ||
284 | return d->order; | 364 | return d->order; | ||
285 | } | 365 | } | ||
286 | 366 | | |||
287 | bool KPropertyEditorDataModel::hasChildren(const QModelIndex & parent) const | 367 | bool KPropertyEditorDataModel::hasChildren(const QModelIndex & parent) const | ||
288 | { | 368 | { | ||
289 | KProperty *parentItem = propertyForIndex(parent); | 369 | KProperty *parentItem = propertyForIndex(parent); | ||
290 | if (!parentItem || parentItem == &d->rootItem) { // top level | 370 | if (parentItem == &d->rootItem) { // top level | ||
291 | return d->set->hasVisibleProperties(); | 371 | return d->set->hasVisibleProperties(); | ||
372 | } else if (parentItem == &d->groupItem) { // group level | ||||
373 | const QByteArray groupName(d->set_d()->groupNames.value(parent.row())); | ||||
374 | Q_ASSERT(!groupName.isEmpty()); | ||||
375 | const QList<QByteArray>* propertyNames = d->set_d()->propertiesOfGroup.value(groupName); | ||||
376 | Q_ASSERT(propertyNames); | ||||
377 | for (const QByteArray &propertyName : *propertyNames) { | ||||
378 | if (d->set_d()->property(propertyName)->isVisible()) { | ||||
379 | return true; // at least one visible property in this group | ||||
292 | } | 380 | } | ||
381 | } | ||||
382 | return false; // no visible properties in this group | ||||
383 | } | ||||
384 | // property level | ||||
293 | const QList<KProperty*>* children = parentItem->children(); | 385 | const QList<KProperty*>* children = parentItem->children(); | ||
294 | return children && !children->isEmpty(); | 386 | return children && !children->isEmpty(); | ||
295 | } | 387 | } | ||
388 | | ||||
389 | bool KPropertyEditorDataModel::groupsVisible() const | ||||
390 | { | ||||
391 | return d->groupsVisible; | ||||
392 | } | ||||
393 | | ||||
394 | void KPropertyEditorDataModel::setGroupsVisible(bool set) | ||||
395 | { | ||||
396 | if (d->groupsVisible == set) { | ||||
397 | return; | ||||
398 | } | ||||
399 | beginResetModel(); | ||||
piggz: Full reset needed? | |||||
I think so, the model radically changes: d->indicesForNames is refreshed. What we can add here *later* is selecting again the same row and ensuring it's visible. This needs a bit larger testing. staniek: I think so, the model radically changes: d->indicesForNames is refreshed.
What we can add here… | |||||
400 | d->groupsVisible = set; | ||||
401 | collectIndices(); | ||||
402 | endResetModel(); | ||||
403 | } |
to be left?