1 /* This file is part of Step.
2 Copyright (C) 2007 Vladimir Kuznetsov <ks.vladimir@gmail.com>
3
4 Step is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 Step is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with Step; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19 #include "propertiesbrowser.h"
20
21 #include "settings.h"
22
23 #include "worldfactory.h"
24 #include "unitscalc.h"
25
26 #include "worldmodel.h"
27 #include <stepcore/object.h>
28 #include <stepcore/solver.h>
29 #include <stepcore/types.h>
30
31 #include <QAbstractItemModel>
32 #include <QApplication>
33 #include <QHBoxLayout>
34 #include <QItemEditorFactory>
35 #include <QMouseEvent>
36 #include <QTreeView>
37
38 #include <KColorButton>
39 #include <KComboBox>
40 #include <KLineEdit>
41 #include <KLocalizedString>
42
43 #include "choicesmodel.h"
44
45 class PropertiesBrowserModel: public QAbstractItemModel
46 {
47 public:
48 PropertiesBrowserModel(WorldModel* worldModel, QObject* parent = 0);
49
50 QVariant data(const QModelIndex &index, int role) const override;
51 QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
52 QModelIndex parent(const QModelIndex &index) const override;
53 int rowCount(const QModelIndex &parent = QModelIndex()) const override;
54 int columnCount(const QModelIndex &parent = QModelIndex()) const override;
55 QVariant headerData(int section, Qt::Orientation orientation,
56 int role = Qt::DisplayRole) const override;
57
58 Qt::ItemFlags flags(const QModelIndex &index) const override;
59 bool setData(const QModelIndex &index, const QVariant &value, int role) override;
60
61 void setObject(StepCore::Object* object);
object()62 StepCore::Object* object() { return _object; }
63
64 void emitDataChanged(bool dynamicOnly);
65
66 protected:
67 WorldModel* _worldModel;
68 StepCore::Object* _object;
69 StepCore::Item* _item;
70 StepCore::ObjectErrors* _objectErrors;
71 ChoicesModel* _solverChoices;
72 QList<int> _subRows;
73 };
74
PropertiesBrowserModel(WorldModel * worldModel,QObject * parent)75 PropertiesBrowserModel::PropertiesBrowserModel(WorldModel* worldModel, QObject* parent)
76 : QAbstractItemModel(parent), _worldModel(worldModel), _object(NULL)
77 {
78 _solverChoices = new ChoicesModel(this);
79
80 // Prepare solver list
81 foreach(const QString &name, _worldModel->worldFactory()->orderedMetaObjects()) {
82 const StepCore::MetaObject* metaObject = _worldModel->worldFactory()->metaObject(name);
83 if(metaObject->isAbstract()) continue;
84 if(!metaObject->inherits(StepCore::Solver::staticMetaObject())) continue;
85 QString solverName = QString(metaObject->className()).remove(QStringLiteral("Solver"));
86 QStandardItem* item = new QStandardItem(solverName);
87 item->setToolTip(QString(metaObject->descriptionTr()));
88 _solverChoices->appendRow(item);
89 }
90 }
91
setObject(StepCore::Object * object)92 void PropertiesBrowserModel::setObject(StepCore::Object* object)
93 {
94 beginResetModel();
95 _object = object;
96
97 _subRows.clear();
98 if(_object != NULL) {
99 _worldModel->simulationPause();
100
101 _item = dynamic_cast<StepCore::Item*>(_object);
102 if(_item) {
103 if(_item->world()->errorsCalculation()) _objectErrors = _item->objectErrors();
104 else _objectErrors = _item->tryGetObjectErrors();
105 } else {
106 _objectErrors = NULL;
107 }
108
109 for(int i=0; i<_object->metaObject()->propertyCount(); ++i) {
110 const StepCore::MetaProperty* p = _object->metaObject()->property(i);
111 if(p->userTypeId() == qMetaTypeId<StepCore::Vector2dList >())
112 _subRows << p->readVariant(_object).value<StepCore::Vector2dList >().size();
113 else _subRows << 0;
114 }
115 }
116
117 endResetModel();
118 }
119
emitDataChanged(bool dynamicOnly)120 void PropertiesBrowserModel::emitDataChanged(bool dynamicOnly)
121 {
122 if(_object == NULL) return;
123
124 _worldModel->simulationPause();
125
126 _item = dynamic_cast<StepCore::Item*>(_object);
127 if(_item) {
128 if(_item->world()->errorsCalculation()) _objectErrors = _item->objectErrors();
129 else _objectErrors = _item->tryGetObjectErrors();
130 } else {
131 _objectErrors = NULL;
132 }
133
134 for(int i=0; i<_object->metaObject()->propertyCount(); i++) {
135 const StepCore::MetaProperty* p = _object->metaObject()->property(i);
136 if(dynamicOnly && !p->isDynamic()) continue;
137 if(p->userTypeId() == qMetaTypeId<StepCore::Vector2dList >()) {
138 int r = p->readVariant(_object).value<StepCore::Vector2dList >().size();
139 if(r > _subRows[i]) {
140 beginInsertRows(index(i, 0), _subRows[i], r-1);
141 _subRows[i] = r;
142 endInsertRows();
143 } else if(r < _subRows[i]) {
144 beginRemoveRows(index(i, 0), r, _subRows[i]-1);
145 _subRows[i] = r;
146 endRemoveRows();
147 }
148 if(r != 0) emit dataChanged(index(0,0,index(i,0)), index(r-1,1,index(i,0))); // XXX?
149 }
150 emit dataChanged(index(i,1), index(i,1));
151 }
152 //emit dataChanged(index(0,1), index(rowCount()-1,1));
153 }
154
data(const QModelIndex & index,int role) const155 QVariant PropertiesBrowserModel::data(const QModelIndex &index, int role) const
156 {
157 if(_object == NULL) return QVariant();
158
159 if(!index.isValid()) return QVariant();
160
161 if(index.internalId() == 0) {
162 const StepCore::MetaProperty* p = _object->metaObject()->property(index.row());
163 if(role == Qt::DisplayRole || role == Qt::EditRole) {
164 if(index.column() == 0) return p->nameTr();
165 else if(index.column() == 1) {
166 _worldModel->simulationPause();
167
168 // Solver type combobox ?
169 if(index.row() == 1 && dynamic_cast<StepCore::Solver*>(_object)) {
170 if(role == Qt::DisplayRole) return p->readString(_object).remove(QStringLiteral("Solver"));
171 else return QVariant::fromValue(_solverChoices);
172 }
173
174 if(p->userTypeId() == QMetaType::Double ||
175 p->userTypeId() == qMetaTypeId<StepCore::Vector2d>() ||
176 p->userTypeId() == qMetaTypeId<StepCore::Vector2dList >()) {
177 return _worldModel->formatProperty(_object, _objectErrors, p,
178 role == Qt::EditRole ? WorldModel::FormatEditable : WorldModel::FormatFlags(0));
179 } else if(p->userTypeId() == qMetaTypeId<StepCore::Object*>()) {
180 return _worldModel->formatName(p->readVariant(_object).value<StepCore::Object*>());
181 } else if(p->userTypeId() == qMetaTypeId<StepCore::Color>()) {
182 Q_ASSERT( !_objectErrors || !_objectErrors->metaObject()->property(p->name() + "Variance") );
183 Q_ASSERT( p->units().isEmpty() );
184 if(role == Qt::EditRole)
185 return QColor::fromRgba(p->readVariant(_object).value<StepCore::Color>());
186 else
187 return p->readString(_object);
188 } else if(p->userTypeId() == QMetaType::Bool) {
189 Q_ASSERT( !_objectErrors || !_objectErrors->metaObject()->property(p->name() + "Variance") );
190 Q_ASSERT( p->units().isEmpty() );
191 return p->readVariant(_object);
192 } else {
193 // default type
194 // XXX: add error information
195 //if(pe) error = QString::fromUtf8(" ± ").append(pe->readString(_objectErrors)).append(units);
196 //if(pv) qDebug() << "Unhandled property variance type" << endl;
197 Q_ASSERT( !_objectErrors || !_objectErrors->metaObject()->property(p->name() + "Variance") );
198 Q_ASSERT( p->units().isEmpty() );
199 if(role == Qt::EditRole) return _worldModel->formatProperty(_object, _objectErrors, p,
200 WorldModel::FormatHideUnits | WorldModel::FormatEditable);
201 else return _worldModel->formatProperty(_object, _objectErrors, p, WorldModel::FormatHideUnits);
202 }
203 ///*if(p->userTypeId() < (int) QVariant::UserType) return p->readVariant(_object);
204 //else*/ return p->readString(_object); // XXX: default delegate for double looks ugly!
205 }
206 } else if(index.column() == 1 && role == Qt::ForegroundRole) {
207 if(!p->isWritable()) {
208 if(index.row() != 1 || !dynamic_cast<StepCore::Solver*>(_object))
209 return QBrush(Qt::darkGray); // XXX: how to get scheme color ?
210 }
211 } else if(role == Qt::ToolTipRole) {
212 if(index.row() == 1 && index.column() == 1 && dynamic_cast<StepCore::Solver*>(_object)) {
213 return _object->metaObject()->descriptionTr();
214 }
215 return p->descriptionTr(); // XXX: translation
216 } else if(index.column() == 1 && role == Qt::DecorationRole &&
217 p->userTypeId() == qMetaTypeId<StepCore::Color>()) {
218 QPixmap pix(8, 8);
219 pix.fill(QColor::fromRgba(p->readVariant(_object).value<StepCore::Color>()));
220 return pix;
221 } else if(index.column() == 0 && role == Qt::DecorationRole &&
222 p->userTypeId() == qMetaTypeId<StepCore::Vector2dList >() &&
223 rowCount(index) > 0) {
224 // XXX: A hack to have nested properties shifted
225 static QPixmap empySmallPix;
226 if(empySmallPix.isNull()) {
227 empySmallPix = QPixmap(8,8); //XXX
228 empySmallPix.fill(QColor(0,0,0,0));
229 }
230 return empySmallPix;
231 }
232 } else { // index.internalId() != 0
233 const StepCore::MetaProperty* p = _object->metaObject()->property(index.internalId()-1);
234 if(role == Qt::DisplayRole || role == Qt::EditRole) {
235 if(index.column() == 0) return QStringLiteral("%1[%2]").arg(p->nameTr()).arg(index.row());
236 else if(index.column() == 1) {
237 #ifdef __GNUC__
238 #warning XXX: add error information for lists
239 #endif
240 QString units;
241 if(role == Qt::DisplayRole && !p->units().isEmpty())
242 units.append(" [").append(p->units()).append("]");
243 #ifdef STEP_WITH_UNITSCALC
244 // else if(role == Qt::EditRole && !p->units().isEmpty())
245 // units.append(" ").append(p->units());
246 #endif
247 int pr = Settings::floatDisplayPrecision();
248 //int pr = role == Qt::DisplayRole ? Settings::floatDisplayPrecision() : 16;
249 _worldModel->simulationPause();
250 StepCore::Vector2d v =
251 p->readVariant(_object).value<StepCore::Vector2dList >()[index.row()];
252 return QStringLiteral("(%1,%2)%3").arg(v[0], 0, 'g', pr).arg(v[1], 0, 'g', pr).arg(units);
253 }
254 } else if(role == Qt::ForegroundRole && index.column() == 1) {
255 if(!p->isWritable()) {
256 return QBrush(Qt::darkGray); // XXX: how to get scheme color ?
257 }
258 } else if(role == Qt::ToolTipRole) {
259 return p->descriptionTr(); // XXX: translation
260 }
261 }
262
263 return QVariant();
264 }
265
setData(const QModelIndex & index,const QVariant & value,int role)266 bool PropertiesBrowserModel::setData(const QModelIndex &index, const QVariant &value, int role)
267 {
268 if(_object == NULL) return false;
269
270 if(index.isValid() && index.column() == 1 && role == Qt::EditRole) {
271 _worldModel->simulationPause();
272 if(index.internalId() == 0) {
273 if(index.row() == 0) { // name // XXX: do it more generally
274 if(!_worldModel->checkUniqueName(value.toString())) return false; // XXX: error message
275 }
276 if(index.row() == 1 && dynamic_cast<StepCore::Solver*>(_object)) {
277 if(value.toString() != _object->metaObject()->className()) {
278 beginResetModel();
279 _worldModel->beginMacro(i18n("Change solver type"));
280 _object = _worldModel->newSolver(value.toString() + "Solver");
281 Q_ASSERT(_object != NULL);
282 _worldModel->endMacro();
283 endResetModel();
284 }
285 } else {
286 const StepCore::MetaProperty* p = _object->metaObject()->property(index.row());
287 const StepCore::MetaProperty* pv = _objectErrors ?
288 _objectErrors->metaObject()->property(p->name() + "Variance") : NULL;
289
290 if(p->userTypeId() == qMetaTypeId<StepCore::Object*>()) {
291 Q_ASSERT(!pv);
292 StepCore::Object* obj = _worldModel->world()->object(value.toString());
293 if(!obj) return false;
294 _worldModel->beginMacro(i18n("Change %1.%2", _object->name(), p->nameTr()));
295 _worldModel->setProperty(_object, p, QVariant::fromValue(obj));
296 _worldModel->endMacro();
297 return true;
298 } else if(p->userTypeId() == qMetaTypeId<StepCore::Color>()) {
299 Q_ASSERT(!pv);
300 _worldModel->beginMacro(i18n("Change %1.%2", _object->name(), p->nameTr()));
301 _worldModel->setProperty(_object, p, value.type() == QVariant::String ? value :
302 QVariant::fromValue(StepCore::Color(value.value<QColor>().rgba())));
303 _worldModel->endMacro();
304 return true;
305 } else if(p->userTypeId() == qMetaTypeId<bool>()) {
306 Q_ASSERT(!pv);
307 _worldModel->beginMacro(i18n("Change %1.%2", _object->name(), p->nameTr()));
308 _worldModel->setProperty(_object, p, value);
309 _worldModel->endMacro();
310 return true;
311 } else if(p->userTypeId() == qMetaTypeId<QString>()) {
312 Q_ASSERT(!pv);
313 if(index.row() == 0)
314 _worldModel->beginMacro(i18n("Rename %1 to %2", _object->name(), value.toString()));
315 else
316 _worldModel->beginMacro(i18n("Change %1.%2", _object->name(), p->nameTr()));
317 _worldModel->setProperty(_object, p, value);
318 _worldModel->endMacro();
319 return true;
320 }
321
322 QVariant v = value;
323 QVariant vv;
324
325 // Try to find ± sign
326 if(v.canConvert(QVariant::String)) {
327 QString str = v.toString();
328 int idx = str.indexOf(QStringLiteral("±"));
329 if(idx >= 0) {
330 v = str.left(idx);
331 vv = str.mid(idx+1);
332 }
333 }
334
335 #ifdef STEP_WITH_UNITSCALC
336 // Convert units
337 if(p->userTypeId() == QMetaType::Double) {
338 double number = 0;
339 if(UnitsCalc::self()->parseNumber(v.toString(), p->units(), number)) {
340 v = number;
341 } else {
342 return false;
343 }
344 if(vv.isValid()) {
345 if(UnitsCalc::self()->parseNumber(vv.toString(), p->units(), number)) {
346 vv = number;
347 } else {
348 return false;
349 }
350 }
351 }
352 #endif
353
354 if(vv.isValid()) { // We have got variance value
355 if(!pv) {
356 // check if _objectErrors can be created
357 // and current property variance could be set
358 const StepCore::MetaObject* me =
359 _worldModel->worldFactory()->metaObject(
360 _object->metaObject()->className() + "Errors");
361 if(!_item || !me || !me->property(p->name() + "Variance"))
362 return false;
363 }
364
365 bool ok = true;
366 // Calc variance = square(error)
367 if(p->userTypeId() == QMetaType::Double) {
368 vv = StepCore::square(vv.toDouble(&ok));
369 } else if(p->userTypeId() == qMetaTypeId<StepCore::Vector2d>()) {
370 StepCore::Vector2d svv;
371 svv = StepCore::stringToType<StepCore::Vector2d>(vv.toString(), &ok);
372 svv[0] *= svv[0]; svv[1] *= svv[1];
373 vv = QVariant::fromValue(svv);
374 /* XXX
375 * {} else if(p->userTypeId() == qMetaTypeId<StepCore::Vector2dList >())
376 ve = QVariant::fromValue(StepCore::Vector2dList());*/
377 } else {
378 // qDebug() << "Unhandled property variance type" << endl;
379 return false;
380 }
381 if(!ok) return false;
382
383 } else { // vv.isValid()
384 if(pv) { // We have to zero variance since we got exact value
385 if(p->userTypeId() == QMetaType::Double) {
386 vv = 0;
387 } else if(p->userTypeId() == qMetaTypeId<StepCore::Vector2d>()) {
388 StepCore::Vector2d svv = StepCore::Vector2d::Zero();
389 vv = QVariant::fromValue(svv);
390 /* XXX
391 * } else if(p->userTypeId() == qMetaTypeId<StepCore::Vector2dList >())
392 ve = QVariant::fromValue(StepCore::Vector2dList());*/
393 } else {
394 qWarning("Unhandled property variance type");
395 return false;
396 }
397 }
398 }
399
400 _worldModel->beginMacro(i18n("Change %1.%2", _object->name(), p->nameTr()));
401 _worldModel->setProperty(_object, p, v);
402 if(vv.isValid() && !pv) {
403 // XXX: Make this undo-able
404 _objectErrors = _item->objectErrors();
405 pv = _objectErrors->metaObject()->property(p->name() + "Variance");
406 }
407 if(pv) _worldModel->setProperty(_objectErrors, pv, vv);
408 _worldModel->endMacro();
409 }
410 } else {
411 const StepCore::MetaProperty* p = _object->metaObject()->property(index.internalId()-1);
412 StepCore::Vector2dList v =
413 p->readVariant(_object).value<StepCore::Vector2dList >();
414 bool ok;
415 v[index.row()] = StepCore::stringToType<StepCore::Vector2d>(value.toString(), &ok);
416 if(!ok) return true; // dataChanged should be emitted anyway
417 _worldModel->beginMacro(i18n("Change %1.%2", _object->name(), p->nameTr()));
418 _worldModel->setProperty(_object, p, QVariant::fromValue(v));
419 _worldModel->endMacro();
420 }
421 return true;
422 }
423 return false;
424 }
425
index(int row,int column,const QModelIndex & parent) const426 QModelIndex PropertiesBrowserModel::index(int row, int column, const QModelIndex &parent) const
427 {
428 if(_object == NULL) return QModelIndex();
429 if(!parent.isValid()) return createIndex(row, column);
430
431 if(parent.internalId() == 0 && _subRows[parent.row()] != 0)
432 return createIndex(row, column, parent.row()+1);
433
434 return QModelIndex();
435 }
436
parent(const QModelIndex & index) const437 QModelIndex PropertiesBrowserModel::parent(const QModelIndex& index) const
438 {
439 if(index.isValid() && index.internalId() != 0)
440 return createIndex(index.internalId()-1, 0, nullptr);
441 return QModelIndex();
442 }
443
rowCount(const QModelIndex & parent) const444 int PropertiesBrowserModel::rowCount(const QModelIndex &parent) const
445 {
446 if(_object == NULL) return 0;
447 else if(parent.isValid()) {
448 if(parent.column() == 0 && parent.internalId() == 0) return _subRows[parent.row()];
449 return 0;
450 }
451 else return _object->metaObject()->propertyCount();
452 }
453
columnCount(const QModelIndex &) const454 int PropertiesBrowserModel::columnCount(const QModelIndex& /*parent*/) const
455 {
456 return 2;
457 }
458
headerData(int section,Qt::Orientation,int role) const459 QVariant PropertiesBrowserModel::headerData(int section, Qt::Orientation /*orientation*/,
460 int role) const
461 {
462 if (role != Qt::DisplayRole) return QVariant();
463 switch(section) {
464 case 0: return i18n("Property");
465 case 1: return i18n("Value");
466 default: return QVariant();
467 }
468 }
469
flags(const QModelIndex & index) const470 Qt::ItemFlags PropertiesBrowserModel::flags(const QModelIndex &index) const
471 {
472 Qt::ItemFlags flags = QAbstractItemModel::flags(index);
473
474 if(_object && index.isValid() && index.column() == 1) {
475 if(index.internalId() == 0) {
476 if(_object->metaObject()->property(index.row())->isWritable() ||
477 (index.row()==1 && dynamic_cast<StepCore::Solver*>(_object))) flags |= Qt::ItemIsEditable;
478 } else {
479 if(_object->metaObject()->property(index.internalId()-1)->isWritable()) flags |= Qt::ItemIsEditable;
480 }
481 }
482
483 return flags;
484 }
485
createEditor(QWidget * parent,const QStyleOptionViewItem &,const QModelIndex & index) const486 QWidget* PropertiesBrowserDelegate::createEditor(QWidget* parent,
487 const QStyleOptionViewItem& /*option*/, const QModelIndex& index) const
488 {
489 QVariant data = index.data(Qt::EditRole);
490 int userType = data.userType();
491 if(userType == qMetaTypeId<ChoicesModel*>()) {
492 KComboBox* editor = new KComboBox(parent);
493 editor->setModel(data.value<ChoicesModel*>());
494 connect(editor, SIGNAL(activated(int)), this, SLOT(editorActivated()));
495 editor->installEventFilter(const_cast<PropertiesBrowserDelegate*>(this));
496 const_cast<PropertiesBrowserDelegate*>(this)->_editor = editor;
497 const_cast<PropertiesBrowserDelegate*>(this)->_comboBox = editor;
498 const_cast<PropertiesBrowserDelegate*>(this)->_editorType = SolverChoiser;
499 return editor;
500
501 } else if(userType == QMetaType::QColor) {
502 QWidget* editor = new QWidget(parent);
503
504 KLineEdit* lineEdit = new KLineEdit(editor);
505 lineEdit->setFrame(false);
506
507 KColorButton* colorButton = new KColorButton(editor);
508 // XXX: do not use hard-coded pixel sizes
509 colorButton->setMinimumWidth(15);
510 colorButton->setMaximumWidth(15);
511 connect(colorButton, &KColorButton::changed, this, &PropertiesBrowserDelegate::editorActivated);
512
513 QHBoxLayout* layout = new QHBoxLayout(editor);
514 layout->setContentsMargins(0,0,0,0);
515 layout->setSpacing(0);
516 layout->addWidget(lineEdit);
517 layout->addWidget(colorButton);
518
519 editor->setFocusProxy(lineEdit);
520 editor->installEventFilter(const_cast<PropertiesBrowserDelegate*>(this));
521
522 const_cast<PropertiesBrowserDelegate*>(this)->_editor = editor;
523 const_cast<PropertiesBrowserDelegate*>(this)->_colorButton = colorButton;
524 const_cast<PropertiesBrowserDelegate*>(this)->_lineEdit = lineEdit;
525 const_cast<PropertiesBrowserDelegate*>(this)->_editorType = ColorChoiser;
526 return editor;
527
528 } else if(userType == QMetaType::Bool) {
529 KComboBox* editor = new KComboBox(parent);
530 editor->addItem(i18n("false"));
531 editor->addItem(i18n("true"));
532 connect(editor, SIGNAL(activated(int)), this, SLOT(editorActivated()));
533 editor->installEventFilter(const_cast<PropertiesBrowserDelegate*>(this));
534 const_cast<PropertiesBrowserDelegate*>(this)->_editor = editor;
535 const_cast<PropertiesBrowserDelegate*>(this)->_comboBox = editor;
536 const_cast<PropertiesBrowserDelegate*>(this)->_editorType = BoolChoiser;
537 return editor;
538
539 } else {
540 const_cast<PropertiesBrowserDelegate*>(this)->_editorType = Standard;
541 const QItemEditorFactory *factory = itemEditorFactory();
542 if(!factory) factory = QItemEditorFactory::defaultFactory();
543 return factory->createEditor(static_cast<QVariant::Type>(userType), parent);
544 }
545 }
546
setEditorData(QWidget * editor,const QModelIndex & index) const547 void PropertiesBrowserDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const
548 {
549 if(_editorType == SolverChoiser) {
550 QVariant data = index.data(Qt::DisplayRole);
551 ChoicesModel* cm = static_cast<ChoicesModel*>(_comboBox->model());
552 QList<QStandardItem*> items = cm->findItems(data.toString());
553 Q_ASSERT(items.count() == 1);
554 _comboBox->setCurrentIndex( cm->indexFromItem(items[0]).row() );
555 } else if(_editorType == ColorChoiser) {
556 QVariant data = index.data(Qt::EditRole);
557 QVariant data1 = index.data(Qt::DisplayRole);
558 _updating = true;
559 _colorButton->setColor(data.value<QColor>());
560 _lineEdit->setText(data1.toString());
561 _updating = false;
562 } else if(_editorType == BoolChoiser) {
563 bool value = index.data(Qt::EditRole).toBool();
564 _comboBox->setCurrentIndex(value ? 1 : 0);
565 } else QItemDelegate::setEditorData(editor, index);
566 }
567
setModelData(QWidget * editor,QAbstractItemModel * model,const QModelIndex & index) const568 void PropertiesBrowserDelegate::setModelData(QWidget* editor, QAbstractItemModel* model,
569 const QModelIndex& index) const
570 {
571 if(_editorType == SolverChoiser) {
572 model->setData(index, _comboBox->currentText());
573 } else if(_editorType == ColorChoiser) {
574 model->setData(index, _lineEdit->text());
575 } else if(_editorType == BoolChoiser) {
576 model->setData(index, _comboBox->currentIndex());
577 } else QItemDelegate::setModelData(editor, model, index);
578 }
579
editorActivated()580 void PropertiesBrowserDelegate::editorActivated()
581 {
582 if(!_updating) {
583 if(_editorType == ColorChoiser) {
584 QRgb v = _colorButton->color().rgba();
585 _lineEdit->setText(StepCore::typeToString<StepCore::Color>(v));
586 }
587 emit commitData(_editor);
588 emit closeEditor(_editor);
589 }
590 }
591
592 class PropertiesBrowserView: public QTreeView
593 {
594 public:
595 PropertiesBrowserView(QWidget* parent = 0);
596 protected:
597 void changeEvent(QEvent* event) override;
598 void mousePressEvent(QMouseEvent* event) override;
599 void drawBranches(QPainter *painter, const QRect &rect, const QModelIndex &index) const override;
600 QStyleOptionViewItem viewOptions() const override;
601 const int _windowsDecoSize;
602 bool _macStyle;
603 };
604
PropertiesBrowserView(QWidget * parent)605 PropertiesBrowserView::PropertiesBrowserView(QWidget* parent)
606 : QTreeView(parent), _windowsDecoSize(9)
607 {
608 _macStyle = QApplication::style()->inherits("QMacStyle");
609 }
610
changeEvent(QEvent * event)611 void PropertiesBrowserView::changeEvent(QEvent* event)
612 {
613 if(event->type() == QEvent::StyleChange)
614 _macStyle = QApplication::style()->inherits("QMacStyle");
615 }
616
mousePressEvent(QMouseEvent * event)617 void PropertiesBrowserView::mousePressEvent(QMouseEvent* event)
618 {
619 if(columnAt(event->x()) == 0) {
620 QModelIndex idx = indexAt(event->pos());
621 if(idx.isValid() && !idx.parent().isValid() && idx.model()->rowCount(idx) > 0) {
622 QRect primitive = visualRect(idx); primitive.setWidth(indentation());
623 if (!_macStyle) {
624 primitive.moveLeft(primitive.left() + (primitive.width() - _windowsDecoSize)/2);
625 primitive.moveTop(primitive.top() + (primitive.height() - _windowsDecoSize)/2);
626 primitive.setWidth(_windowsDecoSize);
627 primitive.setHeight(_windowsDecoSize);
628 }
629 if(primitive.contains(event->pos())) {
630 setExpanded(idx, !isExpanded(idx));
631
632 return;
633 }
634 }
635 }
636 QTreeView::mousePressEvent(event);
637 }
638
drawBranches(QPainter * painter,const QRect & rect,const QModelIndex & index) const639 void PropertiesBrowserView::drawBranches(QPainter *painter, const QRect &rect, const QModelIndex &index) const
640 {
641 // Inspired by qt-designer code in src/components/propertyeditor/qpropertyeditor.cpp
642 QStyleOptionViewItem opt = viewOptions();
643
644 if(model()->hasChildren(index)) {
645 opt.state |= QStyle::State_Children;
646
647 QRect primitive(rect.left() + rect.width() - indentation(), rect.top(),
648 indentation(), rect.height());
649 if(!index.parent().isValid()) {
650 primitive.moveLeft(0);
651 }
652
653 if (!_macStyle) {
654 primitive.moveLeft(primitive.left() + (primitive.width() - _windowsDecoSize)/2);
655 primitive.moveTop(primitive.top() + (primitive.height() - _windowsDecoSize)/2);
656 primitive.setWidth(_windowsDecoSize);
657 primitive.setHeight(_windowsDecoSize);
658 }
659
660 opt.rect = primitive;
661
662 if(isExpanded(index)) opt.state |= QStyle::State_Open;
663 style()->drawPrimitive(QStyle::PE_IndicatorBranch, &opt, painter, this);
664 }
665 }
666
viewOptions() const667 QStyleOptionViewItem PropertiesBrowserView::viewOptions() const
668 {
669 QStyleOptionViewItem option = QTreeView::viewOptions();
670 option.showDecorationSelected = true;
671 return option;
672 }
673
PropertiesBrowser(WorldModel * worldModel,QWidget * parent)674 PropertiesBrowser::PropertiesBrowser(WorldModel* worldModel, QWidget* parent)
675 : QDockWidget(i18n("Properties"), parent)
676 {
677 _worldModel = worldModel;
678 _propertiesBrowserModel = new PropertiesBrowserModel(worldModel, this);
679 _treeView = new PropertiesBrowserView(this);
680
681 _treeView->setAllColumnsShowFocus(true);
682 _treeView->setRootIsDecorated(false);
683 //_treeView->setAlternatingRowColors(true);
684 _treeView->setSelectionMode(QAbstractItemView::NoSelection);
685 _treeView->setSelectionBehavior(QTreeView::SelectRows);
686 _treeView->setEditTriggers(QAbstractItemView::AllEditTriggers);
687 //_treeView->setEditTriggers(/*QAbstractItemView::CurrentChanged | */QAbstractItemView::SelectedClicked |
688 // QAbstractItemView::EditKeyPressed | QAbstractItemView::AnyKeyPressed);
689 _treeView->setItemDelegate(new PropertiesBrowserDelegate(_treeView));
690
691 _treeView->setModel(_propertiesBrowserModel);
692 worldCurrentChanged(_worldModel->worldIndex(), QModelIndex());
693
694 connect(_worldModel, &QAbstractItemModel::modelReset, this, &PropertiesBrowser::worldModelReset);
695 connect(_worldModel, &WorldModel::worldDataChanged, this, &PropertiesBrowser::worldDataChanged);
696 connect(_worldModel, &QAbstractItemModel::rowsRemoved,
697 this, &PropertiesBrowser::worldRowsRemoved);
698
699 connect(_worldModel->selectionModel(), &QItemSelectionModel::currentChanged,
700 this, &PropertiesBrowser::worldCurrentChanged);
701
702 connect(_treeView->selectionModel(), &QItemSelectionModel::currentChanged,
703 this, &PropertiesBrowser::currentChanged);
704
705 //connect(_treeView, SIGNAL(doubleClicked(QModelIndex)),
706 // this, SLOT(doubleClicked(QModelIndex)));
707
708 connect(_propertiesBrowserModel, &QAbstractItemModel::rowsInserted,
709 this, &PropertiesBrowser::rowsInserted);
710 connect(_propertiesBrowserModel, &QAbstractItemModel::rowsRemoved,
711 this, &PropertiesBrowser::rowsRemoved);
712
713 _treeView->viewport()->installEventFilter(this);
714 //_treeView->setMouseTracking(true);
715
716 setWidget(_treeView);
717 }
718
worldModelReset()719 void PropertiesBrowser::worldModelReset()
720 {
721 _propertiesBrowserModel->setObject(NULL);
722 }
723
worldCurrentChanged(const QModelIndex & current,const QModelIndex &)724 void PropertiesBrowser::worldCurrentChanged(const QModelIndex& current, const QModelIndex& /*previous*/)
725 {
726 _propertiesBrowserModel->setObject(_worldModel->object(current));
727 //_treeView->expandAll();
728 for(int i=0; i<_propertiesBrowserModel->rowCount(); ++i) {
729 QModelIndex index = _propertiesBrowserModel->index(i, 0);
730 if(_propertiesBrowserModel->rowCount(index) <= 10) // XXX: make it configurable
731 _treeView->setExpanded(index, true);
732 }
733 }
734
worldDataChanged(bool dynamicOnly)735 void PropertiesBrowser::worldDataChanged(bool dynamicOnly)
736 {
737 _propertiesBrowserModel->emitDataChanged(dynamicOnly);
738 }
739
worldRowsRemoved(const QModelIndex & parent,int start,int end)740 void PropertiesBrowser::worldRowsRemoved(const QModelIndex& parent, int start, int end)
741 {
742 Q_UNUSED(parent)
743 Q_UNUSED(start)
744 Q_UNUSED(end)
745 if(!_worldModel->objectIndex(_propertiesBrowserModel->object()).isValid())
746 _propertiesBrowserModel->setObject(NULL);
747 }
748
currentChanged(const QModelIndex & current,const QModelIndex &)749 void PropertiesBrowser::currentChanged(const QModelIndex& current, const QModelIndex& /*previous*/)
750 {
751 if(current.isValid() && current.column() == 0)
752 _treeView->selectionModel()->setCurrentIndex(current.sibling(current.row(), 1), QItemSelectionModel::Current);
753 }
754
rowsInserted(const QModelIndex & parent,int start,int end)755 void PropertiesBrowser::rowsInserted(const QModelIndex& parent, int start, int end)
756 {
757 int rowCount = _propertiesBrowserModel->rowCount(parent);
758 if(rowCount > 10 && (rowCount - (start-end+1)) <= 10) {
759 _treeView->setExpanded(parent, false);
760 }
761 }
762
rowsRemoved(const QModelIndex & parent,int start,int end)763 void PropertiesBrowser::rowsRemoved(const QModelIndex& parent, int start, int end)
764 {
765 int rowCount = _propertiesBrowserModel->rowCount(parent);
766 if(rowCount <= 10 && rowCount + (start-end+1) > 10) {
767 _treeView->setExpanded(parent, true);
768 }
769 }
770
771 /*
772 void PropertiesBrowser::doubleClicked(const QModelIndex& index)
773 {
774 qDebug() << "doubleClicked" << endl;
775 if(_propertiesBrowserModel->rowCount(index) > 0) {
776 qDebug() << " doubleClicked!!!" << endl;
777 _treeView->setExpanded(index, !_treeView->isExpanded(index));
778 }
779 }
780 */
781
eventFilter(QObject * object,QEvent * event)782 bool PropertiesBrowser::eventFilter(QObject* object, QEvent* event)
783 {
784 if(object == _treeView->viewport() && event->type() == QEvent::MouseButtonDblClick) {
785 QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
786 QModelIndex index = _treeView->indexAt(mouseEvent->pos());
787 if(_propertiesBrowserModel->rowCount(index) > 0)
788 _treeView->setExpanded(index, !_treeView->isExpanded(index));
789 }
790 return false;
791 }
792
settingsChanged()793 void PropertiesBrowser::settingsChanged()
794 {
795 _propertiesBrowserModel->emitDataChanged(false);
796 }
797
798
799