File indexing completed on 2024-04-28 03:51:18
0001 /*. 0002 SPDX-FileCopyrightText: 2007 Vladimir Kuznetsov <ks.vladimir@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "propertiesbrowser.h" 0008 0009 #include "settings.h" 0010 0011 #include "worldfactory.h" 0012 #include "unitscalc.h" 0013 0014 #include "worldmodel.h" 0015 #include <stepcore/object.h> 0016 #include <stepcore/solver.h> 0017 #include <stepcore/types.h> 0018 0019 #include <QAbstractItemModel> 0020 #include <QApplication> 0021 #include <QHBoxLayout> 0022 #include <QItemEditorFactory> 0023 #include <QMouseEvent> 0024 #include <QTreeView> 0025 0026 #include <KColorButton> 0027 #include <KComboBox> 0028 #include <KLineEdit> 0029 #include <KLocalizedString> 0030 0031 #include "choicesmodel.h" 0032 0033 class PropertiesBrowserModel: public QAbstractItemModel 0034 { 0035 public: 0036 PropertiesBrowserModel(WorldModel* worldModel, QObject* parent = nullptr); 0037 0038 QVariant data(const QModelIndex &index, int role) const override; 0039 QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; 0040 QModelIndex parent(const QModelIndex &index) const override; 0041 int rowCount(const QModelIndex &parent = QModelIndex()) const override; 0042 int columnCount(const QModelIndex &parent = QModelIndex()) const override; 0043 QVariant headerData(int section, Qt::Orientation orientation, 0044 int role = Qt::DisplayRole) const override; 0045 0046 Qt::ItemFlags flags(const QModelIndex &index) const override; 0047 bool setData(const QModelIndex &index, const QVariant &value, int role) override; 0048 0049 void setObject(StepCore::Object* object); 0050 StepCore::Object* object() { return _object; } 0051 0052 void emitDataChanged(bool dynamicOnly); 0053 0054 protected: 0055 WorldModel* _worldModel; 0056 StepCore::Object* _object; 0057 StepCore::Item* _item; 0058 StepCore::ObjectErrors* _objectErrors; 0059 ChoicesModel* _solverChoices; 0060 QList<int> _subRows; 0061 }; 0062 0063 PropertiesBrowserModel::PropertiesBrowserModel(WorldModel* worldModel, QObject* parent) 0064 : QAbstractItemModel(parent), _worldModel(worldModel), _object(nullptr) 0065 { 0066 _solverChoices = new ChoicesModel(this); 0067 0068 // Prepare solver list 0069 foreach(const QString &name, _worldModel->worldFactory()->orderedMetaObjects()) { 0070 const StepCore::MetaObject* metaObject = _worldModel->worldFactory()->metaObject(name); 0071 if(metaObject->isAbstract()) continue; 0072 if(!metaObject->inherits(StepCore::Solver::staticMetaObject())) continue; 0073 QString solverName = QString(metaObject->className()).remove(QStringLiteral("Solver")); 0074 QStandardItem* item = new QStandardItem(solverName); 0075 item->setToolTip(QString(metaObject->descriptionTr())); 0076 _solverChoices->appendRow(item); 0077 } 0078 } 0079 0080 void PropertiesBrowserModel::setObject(StepCore::Object* object) 0081 { 0082 beginResetModel(); 0083 _object = object; 0084 0085 _subRows.clear(); 0086 if(_object != nullptr) { 0087 _worldModel->simulationPause(); 0088 0089 _item = dynamic_cast<StepCore::Item*>(_object); 0090 if(_item) { 0091 if(_item->world()->errorsCalculation()) _objectErrors = _item->objectErrors(); 0092 else _objectErrors = _item->tryGetObjectErrors(); 0093 } else { 0094 _objectErrors = nullptr; 0095 } 0096 0097 for(int i=0; i<_object->metaObject()->propertyCount(); ++i) { 0098 const StepCore::MetaProperty* p = _object->metaObject()->property(i); 0099 if(p->userTypeId() == qMetaTypeId<StepCore::Vector2dList >()) 0100 _subRows << p->readVariant(_object).value<StepCore::Vector2dList >().size(); 0101 else _subRows << 0; 0102 } 0103 } 0104 0105 endResetModel(); 0106 } 0107 0108 void PropertiesBrowserModel::emitDataChanged(bool dynamicOnly) 0109 { 0110 if(_object == nullptr) return; 0111 0112 _worldModel->simulationPause(); 0113 0114 _item = dynamic_cast<StepCore::Item*>(_object); 0115 if(_item) { 0116 if(_item->world()->errorsCalculation()) _objectErrors = _item->objectErrors(); 0117 else _objectErrors = _item->tryGetObjectErrors(); 0118 } else { 0119 _objectErrors = nullptr; 0120 } 0121 0122 for(int i=0; i<_object->metaObject()->propertyCount(); i++) { 0123 const StepCore::MetaProperty* p = _object->metaObject()->property(i); 0124 if(dynamicOnly && !p->isDynamic()) continue; 0125 if(p->userTypeId() == qMetaTypeId<StepCore::Vector2dList >()) { 0126 int r = p->readVariant(_object).value<StepCore::Vector2dList >().size(); 0127 if(r > _subRows[i]) { 0128 beginInsertRows(index(i, 0), _subRows[i], r-1); 0129 _subRows[i] = r; 0130 endInsertRows(); 0131 } else if(r < _subRows[i]) { 0132 beginRemoveRows(index(i, 0), r, _subRows[i]-1); 0133 _subRows[i] = r; 0134 endRemoveRows(); 0135 } 0136 if(r != 0) emit dataChanged(index(0,0,index(i,0)), index(r-1,1,index(i,0))); // XXX? 0137 } 0138 emit dataChanged(index(i,1), index(i,1)); 0139 } 0140 //emit dataChanged(index(0,1), index(rowCount()-1,1)); 0141 } 0142 0143 QVariant PropertiesBrowserModel::data(const QModelIndex &index, int role) const 0144 { 0145 if(_object == nullptr) return QVariant(); 0146 0147 if(!index.isValid()) return QVariant(); 0148 0149 if(index.internalId() == 0) { 0150 const StepCore::MetaProperty* p = _object->metaObject()->property(index.row()); 0151 if(role == Qt::DisplayRole || role == Qt::EditRole) { 0152 if(index.column() == 0) return p->nameTr(); 0153 else if(index.column() == 1) { 0154 _worldModel->simulationPause(); 0155 0156 // Solver type combobox ? 0157 if(index.row() == 1 && dynamic_cast<StepCore::Solver*>(_object)) { 0158 if(role == Qt::DisplayRole) return p->readString(_object).remove(QStringLiteral("Solver")); 0159 else return QVariant::fromValue(_solverChoices); 0160 } 0161 0162 if(p->userTypeId() == QMetaType::Double || 0163 p->userTypeId() == qMetaTypeId<StepCore::Vector2d>() || 0164 p->userTypeId() == qMetaTypeId<StepCore::Vector2dList >()) { 0165 return _worldModel->formatProperty(_object, _objectErrors, p, 0166 role == Qt::EditRole ? WorldModel::FormatEditable : WorldModel::FormatFlags()); 0167 } else if(p->userTypeId() == qMetaTypeId<StepCore::Object*>()) { 0168 return _worldModel->formatName(p->readVariant(_object).value<StepCore::Object*>()); 0169 } else if(p->userTypeId() == qMetaTypeId<StepCore::Color>()) { 0170 Q_ASSERT( !_objectErrors || !_objectErrors->metaObject()->property(p->name() + "Variance") ); 0171 Q_ASSERT( p->units().isEmpty() ); 0172 if(role == Qt::EditRole) 0173 return QColor::fromRgba(p->readVariant(_object).value<StepCore::Color>()); 0174 else 0175 return p->readString(_object); 0176 } else if(p->userTypeId() == QMetaType::Bool) { 0177 Q_ASSERT( !_objectErrors || !_objectErrors->metaObject()->property(p->name() + "Variance") ); 0178 Q_ASSERT( p->units().isEmpty() ); 0179 return p->readVariant(_object); 0180 } else { 0181 // default type 0182 // XXX: add error information 0183 //if(pe) error = QString::fromUtf8(" ± ").append(pe->readString(_objectErrors)).append(units); 0184 //if(pv) qDebug() << "Unhandled property variance type" << endl; 0185 Q_ASSERT( !_objectErrors || !_objectErrors->metaObject()->property(p->name() + "Variance") ); 0186 Q_ASSERT( p->units().isEmpty() ); 0187 if(role == Qt::EditRole) return _worldModel->formatProperty(_object, _objectErrors, p, 0188 WorldModel::FormatHideUnits | WorldModel::FormatEditable); 0189 else return _worldModel->formatProperty(_object, _objectErrors, p, WorldModel::FormatHideUnits); 0190 } 0191 ///*if(p->userTypeId() < (int) QVariant::UserType) return p->readVariant(_object); 0192 //else*/ return p->readString(_object); // XXX: default delegate for double looks ugly! 0193 } 0194 } else if(index.column() == 1 && role == Qt::ForegroundRole) { 0195 if(!p->isWritable()) { 0196 if(index.row() != 1 || !dynamic_cast<StepCore::Solver*>(_object)) 0197 return QBrush(Qt::darkGray); // XXX: how to get scheme color ? 0198 } 0199 } else if(role == Qt::ToolTipRole) { 0200 if(index.row() == 1 && index.column() == 1 && dynamic_cast<StepCore::Solver*>(_object)) { 0201 return _object->metaObject()->descriptionTr(); 0202 } 0203 return p->descriptionTr(); // XXX: translation 0204 } else if(index.column() == 1 && role == Qt::DecorationRole && 0205 p->userTypeId() == qMetaTypeId<StepCore::Color>()) { 0206 QPixmap pix(8, 8); 0207 pix.fill(QColor::fromRgba(p->readVariant(_object).value<StepCore::Color>())); 0208 return pix; 0209 } else if(index.column() == 0 && role == Qt::DecorationRole && 0210 p->userTypeId() == qMetaTypeId<StepCore::Vector2dList >() && 0211 rowCount(index) > 0) { 0212 // XXX: A hack to have nested properties shifted 0213 static QPixmap empySmallPix; 0214 if(empySmallPix.isNull()) { 0215 empySmallPix = QPixmap(8,8); //XXX 0216 empySmallPix.fill(QColor(0,0,0,0)); 0217 } 0218 return empySmallPix; 0219 } 0220 } else { // index.internalId() != 0 0221 const StepCore::MetaProperty* p = _object->metaObject()->property(index.internalId()-1); 0222 if(role == Qt::DisplayRole || role == Qt::EditRole) { 0223 if(index.column() == 0) return QStringLiteral("%1[%2]").arg(p->nameTr()).arg(index.row()); 0224 else if(index.column() == 1) { 0225 #ifdef __GNUC__ 0226 #warning XXX: add error information for lists 0227 #endif 0228 QString units; 0229 if(role == Qt::DisplayRole && !p->units().isEmpty()) 0230 units.append(" [").append(p->units()).append("]"); 0231 #ifdef STEP_WITH_UNITSCALC 0232 // else if(role == Qt::EditRole && !p->units().isEmpty()) 0233 // units.append(" ").append(p->units()); 0234 #endif 0235 int pr = Settings::floatDisplayPrecision(); 0236 //int pr = role == Qt::DisplayRole ? Settings::floatDisplayPrecision() : 16; 0237 _worldModel->simulationPause(); 0238 StepCore::Vector2d v = 0239 p->readVariant(_object).value<StepCore::Vector2dList >()[index.row()]; 0240 return QStringLiteral("(%1,%2)%3").arg(v[0], 0, 'g', pr).arg(v[1], 0, 'g', pr).arg(units); 0241 } 0242 } else if(role == Qt::ForegroundRole && index.column() == 1) { 0243 if(!p->isWritable()) { 0244 return QBrush(Qt::darkGray); // XXX: how to get scheme color ? 0245 } 0246 } else if(role == Qt::ToolTipRole) { 0247 return p->descriptionTr(); // XXX: translation 0248 } 0249 } 0250 0251 return QVariant(); 0252 } 0253 0254 bool PropertiesBrowserModel::setData(const QModelIndex &index, const QVariant &value, int role) 0255 { 0256 if(_object == nullptr) return false; 0257 0258 if(index.isValid() && index.column() == 1 && role == Qt::EditRole) { 0259 _worldModel->simulationPause(); 0260 if(index.internalId() == 0) { 0261 if(index.row() == 0) { // name // XXX: do it more generally 0262 if(!_worldModel->checkUniqueName(value.toString())) return false; // XXX: error message 0263 } 0264 if(index.row() == 1 && dynamic_cast<StepCore::Solver*>(_object)) { 0265 if(value.toString() != _object->metaObject()->className()) { 0266 beginResetModel(); 0267 _worldModel->beginMacro(i18n("Change solver type")); 0268 _object = _worldModel->newSolver(value.toString() + "Solver"); 0269 Q_ASSERT(_object != nullptr); 0270 _worldModel->endMacro(); 0271 endResetModel(); 0272 } 0273 } else { 0274 const StepCore::MetaProperty* p = _object->metaObject()->property(index.row()); 0275 const StepCore::MetaProperty* pv = _objectErrors ? 0276 _objectErrors->metaObject()->property(p->name() + "Variance") : nullptr; 0277 0278 if(p->userTypeId() == qMetaTypeId<StepCore::Object*>()) { 0279 Q_ASSERT(!pv); 0280 StepCore::Object* obj = _worldModel->world()->object(value.toString()); 0281 if(!obj) return false; 0282 _worldModel->beginMacro(i18n("Change %1.%2", _object->name(), p->nameTr())); 0283 _worldModel->setProperty(_object, p, QVariant::fromValue(obj)); 0284 _worldModel->endMacro(); 0285 return true; 0286 } else if(p->userTypeId() == qMetaTypeId<StepCore::Color>()) { 0287 Q_ASSERT(!pv); 0288 _worldModel->beginMacro(i18n("Change %1.%2", _object->name(), p->nameTr())); 0289 _worldModel->setProperty(_object, p, value.type() == QVariant::String ? value : 0290 QVariant::fromValue(StepCore::Color(value.value<QColor>().rgba()))); 0291 _worldModel->endMacro(); 0292 return true; 0293 } else if(p->userTypeId() == qMetaTypeId<bool>()) { 0294 Q_ASSERT(!pv); 0295 _worldModel->beginMacro(i18n("Change %1.%2", _object->name(), p->nameTr())); 0296 _worldModel->setProperty(_object, p, value); 0297 _worldModel->endMacro(); 0298 return true; 0299 } else if(p->userTypeId() == qMetaTypeId<QString>()) { 0300 Q_ASSERT(!pv); 0301 if(index.row() == 0) 0302 _worldModel->beginMacro(i18n("Rename %1 to %2", _object->name(), value.toString())); 0303 else 0304 _worldModel->beginMacro(i18n("Change %1.%2", _object->name(), p->nameTr())); 0305 _worldModel->setProperty(_object, p, value); 0306 _worldModel->endMacro(); 0307 return true; 0308 } 0309 0310 QVariant v = value; 0311 QVariant vv; 0312 0313 // Try to find ± sign 0314 if(v.canConvert(QVariant::String)) { 0315 QString str = v.toString(); 0316 int idx = str.indexOf(QStringLiteral("±")); 0317 if(idx >= 0) { 0318 v = str.left(idx); 0319 vv = str.mid(idx+1); 0320 } 0321 } 0322 0323 #ifdef STEP_WITH_UNITSCALC 0324 // Convert units 0325 if(p->userTypeId() == QMetaType::Double) { 0326 double number = 0; 0327 if(UnitsCalc::self()->parseNumber(v.toString(), p->units(), number)) { 0328 v = number; 0329 } else { 0330 return false; 0331 } 0332 if(vv.isValid()) { 0333 if(UnitsCalc::self()->parseNumber(vv.toString(), p->units(), number)) { 0334 vv = number; 0335 } else { 0336 return false; 0337 } 0338 } 0339 } 0340 #endif 0341 0342 if(vv.isValid()) { // We have got variance value 0343 if(!pv) { 0344 // check if _objectErrors can be created 0345 // and current property variance could be set 0346 const StepCore::MetaObject* me = 0347 _worldModel->worldFactory()->metaObject( 0348 _object->metaObject()->className() + "Errors"); 0349 if(!_item || !me || !me->property(p->name() + "Variance")) 0350 return false; 0351 } 0352 0353 bool ok = true; 0354 // Calc variance = square(error) 0355 if(p->userTypeId() == QMetaType::Double) { 0356 vv = StepCore::square(vv.toDouble(&ok)); 0357 } else if(p->userTypeId() == qMetaTypeId<StepCore::Vector2d>()) { 0358 StepCore::Vector2d svv; 0359 svv = StepCore::stringToType<StepCore::Vector2d>(vv.toString(), &ok); 0360 svv[0] *= svv[0]; svv[1] *= svv[1]; 0361 vv = QVariant::fromValue(svv); 0362 /* XXX 0363 * {} else if(p->userTypeId() == qMetaTypeId<StepCore::Vector2dList >()) 0364 ve = QVariant::fromValue(StepCore::Vector2dList());*/ 0365 } else { 0366 // qDebug() << "Unhandled property variance type" << endl; 0367 return false; 0368 } 0369 if(!ok) return false; 0370 0371 } else { // vv.isValid() 0372 if(pv) { // We have to zero variance since we got exact value 0373 if(p->userTypeId() == QMetaType::Double) { 0374 vv = 0; 0375 } else if(p->userTypeId() == qMetaTypeId<StepCore::Vector2d>()) { 0376 StepCore::Vector2d svv = StepCore::Vector2d::Zero(); 0377 vv = QVariant::fromValue(svv); 0378 /* XXX 0379 * } else if(p->userTypeId() == qMetaTypeId<StepCore::Vector2dList >()) 0380 ve = QVariant::fromValue(StepCore::Vector2dList());*/ 0381 } else { 0382 qWarning("Unhandled property variance type"); 0383 return false; 0384 } 0385 } 0386 } 0387 0388 _worldModel->beginMacro(i18n("Change %1.%2", _object->name(), p->nameTr())); 0389 _worldModel->setProperty(_object, p, v); 0390 if(vv.isValid() && !pv) { 0391 // XXX: Make this undo-able 0392 _objectErrors = _item->objectErrors(); 0393 pv = _objectErrors->metaObject()->property(p->name() + "Variance"); 0394 } 0395 if(pv) _worldModel->setProperty(_objectErrors, pv, vv); 0396 _worldModel->endMacro(); 0397 } 0398 } else { 0399 const StepCore::MetaProperty* p = _object->metaObject()->property(index.internalId()-1); 0400 StepCore::Vector2dList v = 0401 p->readVariant(_object).value<StepCore::Vector2dList >(); 0402 bool ok; 0403 v[index.row()] = StepCore::stringToType<StepCore::Vector2d>(value.toString(), &ok); 0404 if(!ok) return true; // dataChanged should be emitted anyway 0405 _worldModel->beginMacro(i18n("Change %1.%2", _object->name(), p->nameTr())); 0406 _worldModel->setProperty(_object, p, QVariant::fromValue(v)); 0407 _worldModel->endMacro(); 0408 } 0409 return true; 0410 } 0411 return false; 0412 } 0413 0414 QModelIndex PropertiesBrowserModel::index(int row, int column, const QModelIndex &parent) const 0415 { 0416 if(_object == nullptr) return QModelIndex(); 0417 if(!parent.isValid()) return createIndex(row, column); 0418 0419 if(parent.internalId() == 0 && _subRows[parent.row()] != 0) 0420 return createIndex(row, column, parent.row()+1); 0421 0422 return QModelIndex(); 0423 } 0424 0425 QModelIndex PropertiesBrowserModel::parent(const QModelIndex& index) const 0426 { 0427 if(index.isValid() && index.internalId() != 0) 0428 return createIndex(index.internalId()-1, 0, nullptr); 0429 return QModelIndex(); 0430 } 0431 0432 int PropertiesBrowserModel::rowCount(const QModelIndex &parent) const 0433 { 0434 if(_object == nullptr) return 0; 0435 else if(parent.isValid()) { 0436 if(parent.column() == 0 && parent.internalId() == 0) return _subRows[parent.row()]; 0437 return 0; 0438 } 0439 else return _object->metaObject()->propertyCount(); 0440 } 0441 0442 int PropertiesBrowserModel::columnCount(const QModelIndex& /*parent*/) const 0443 { 0444 return 2; 0445 } 0446 0447 QVariant PropertiesBrowserModel::headerData(int section, Qt::Orientation /*orientation*/, 0448 int role) const 0449 { 0450 if (role != Qt::DisplayRole) return QVariant(); 0451 switch(section) { 0452 case 0: return i18n("Property"); 0453 case 1: return i18n("Value"); 0454 default: return QVariant(); 0455 } 0456 } 0457 0458 Qt::ItemFlags PropertiesBrowserModel::flags(const QModelIndex &index) const 0459 { 0460 Qt::ItemFlags flags = QAbstractItemModel::flags(index); 0461 0462 if(_object && index.isValid() && index.column() == 1) { 0463 if(index.internalId() == 0) { 0464 if(_object->metaObject()->property(index.row())->isWritable() || 0465 (index.row()==1 && dynamic_cast<StepCore::Solver*>(_object))) flags |= Qt::ItemIsEditable; 0466 } else { 0467 if(_object->metaObject()->property(index.internalId()-1)->isWritable()) flags |= Qt::ItemIsEditable; 0468 } 0469 } 0470 0471 return flags; 0472 } 0473 0474 QWidget* PropertiesBrowserDelegate::createEditor(QWidget* parent, 0475 const QStyleOptionViewItem& /*option*/, const QModelIndex& index) const 0476 { 0477 QVariant data = index.data(Qt::EditRole); 0478 int userType = data.userType(); 0479 if(userType == qMetaTypeId<ChoicesModel*>()) { 0480 KComboBox* editor = new KComboBox(parent); 0481 editor->setModel(data.value<ChoicesModel*>()); 0482 connect(editor, SIGNAL(activated(int)), this, SLOT(editorActivated())); 0483 editor->installEventFilter(const_cast<PropertiesBrowserDelegate*>(this)); 0484 const_cast<PropertiesBrowserDelegate*>(this)->_editor = editor; 0485 const_cast<PropertiesBrowserDelegate*>(this)->_comboBox = editor; 0486 const_cast<PropertiesBrowserDelegate*>(this)->_editorType = SolverChoiser; 0487 return editor; 0488 0489 } else if(userType == QMetaType::QColor) { 0490 QWidget* editor = new QWidget(parent); 0491 0492 KLineEdit* lineEdit = new KLineEdit(editor); 0493 lineEdit->setFrame(false); 0494 0495 KColorButton* colorButton = new KColorButton(editor); 0496 // XXX: do not use hard-coded pixel sizes 0497 colorButton->setMinimumWidth(15); 0498 colorButton->setMaximumWidth(15); 0499 connect(colorButton, &KColorButton::changed, this, &PropertiesBrowserDelegate::editorActivated); 0500 0501 QHBoxLayout* layout = new QHBoxLayout(editor); 0502 layout->setContentsMargins(0,0,0,0); 0503 layout->setSpacing(0); 0504 layout->addWidget(lineEdit); 0505 layout->addWidget(colorButton); 0506 0507 editor->setFocusProxy(lineEdit); 0508 editor->installEventFilter(const_cast<PropertiesBrowserDelegate*>(this)); 0509 0510 const_cast<PropertiesBrowserDelegate*>(this)->_editor = editor; 0511 const_cast<PropertiesBrowserDelegate*>(this)->_colorButton = colorButton; 0512 const_cast<PropertiesBrowserDelegate*>(this)->_lineEdit = lineEdit; 0513 const_cast<PropertiesBrowserDelegate*>(this)->_editorType = ColorChoiser; 0514 return editor; 0515 0516 } else if(userType == QMetaType::Bool) { 0517 KComboBox* editor = new KComboBox(parent); 0518 editor->addItem(i18n("false")); 0519 editor->addItem(i18n("true")); 0520 connect(editor, SIGNAL(activated(int)), this, SLOT(editorActivated())); 0521 editor->installEventFilter(const_cast<PropertiesBrowserDelegate*>(this)); 0522 const_cast<PropertiesBrowserDelegate*>(this)->_editor = editor; 0523 const_cast<PropertiesBrowserDelegate*>(this)->_comboBox = editor; 0524 const_cast<PropertiesBrowserDelegate*>(this)->_editorType = BoolChoiser; 0525 return editor; 0526 0527 } else { 0528 const_cast<PropertiesBrowserDelegate*>(this)->_editorType = Standard; 0529 const QItemEditorFactory *factory = itemEditorFactory(); 0530 if(!factory) factory = QItemEditorFactory::defaultFactory(); 0531 return factory->createEditor(static_cast<QVariant::Type>(userType), parent); 0532 } 0533 } 0534 0535 void PropertiesBrowserDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const 0536 { 0537 if(_editorType == SolverChoiser) { 0538 QVariant data = index.data(Qt::DisplayRole); 0539 ChoicesModel* cm = static_cast<ChoicesModel*>(_comboBox->model()); 0540 QList<QStandardItem*> items = cm->findItems(data.toString()); 0541 Q_ASSERT(items.count() == 1); 0542 _comboBox->setCurrentIndex( cm->indexFromItem(items[0]).row() ); 0543 } else if(_editorType == ColorChoiser) { 0544 QVariant data = index.data(Qt::EditRole); 0545 QVariant data1 = index.data(Qt::DisplayRole); 0546 _updating = true; 0547 _colorButton->setColor(data.value<QColor>()); 0548 _lineEdit->setText(data1.toString()); 0549 _updating = false; 0550 } else if(_editorType == BoolChoiser) { 0551 bool value = index.data(Qt::EditRole).toBool(); 0552 _comboBox->setCurrentIndex(value ? 1 : 0); 0553 } else QItemDelegate::setEditorData(editor, index); 0554 } 0555 0556 void PropertiesBrowserDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, 0557 const QModelIndex& index) const 0558 { 0559 if(_editorType == SolverChoiser) { 0560 model->setData(index, _comboBox->currentText()); 0561 } else if(_editorType == ColorChoiser) { 0562 model->setData(index, _lineEdit->text()); 0563 } else if(_editorType == BoolChoiser) { 0564 model->setData(index, _comboBox->currentIndex()); 0565 } else QItemDelegate::setModelData(editor, model, index); 0566 } 0567 0568 void PropertiesBrowserDelegate::editorActivated() 0569 { 0570 if(!_updating) { 0571 if(_editorType == ColorChoiser) { 0572 QRgb v = _colorButton->color().rgba(); 0573 _lineEdit->setText(StepCore::typeToString<StepCore::Color>(v)); 0574 } 0575 emit commitData(_editor); 0576 emit closeEditor(_editor); 0577 } 0578 } 0579 0580 class PropertiesBrowserView: public QTreeView 0581 { 0582 public: 0583 PropertiesBrowserView(QWidget* parent = nullptr); 0584 protected: 0585 void changeEvent(QEvent* event) override; 0586 void mousePressEvent(QMouseEvent* event) override; 0587 void drawBranches(QPainter *painter, const QRect &rect, const QModelIndex &index) const override; 0588 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0589 void initViewItemOption(QStyleOptionViewItem *option) const override; 0590 #else 0591 QStyleOptionViewItem viewOptions() const override; 0592 #endif 0593 const int _windowsDecoSize; 0594 bool _macStyle; 0595 }; 0596 0597 PropertiesBrowserView::PropertiesBrowserView(QWidget* parent) 0598 : QTreeView(parent), _windowsDecoSize(9) 0599 { 0600 _macStyle = QApplication::style()->inherits("QMacStyle"); 0601 } 0602 0603 void PropertiesBrowserView::changeEvent(QEvent* event) 0604 { 0605 if(event->type() == QEvent::StyleChange) 0606 _macStyle = QApplication::style()->inherits("QMacStyle"); 0607 } 0608 0609 void PropertiesBrowserView::mousePressEvent(QMouseEvent* event) 0610 { 0611 if(columnAt(event->x()) == 0) { 0612 QModelIndex idx = indexAt(event->pos()); 0613 if(idx.isValid() && !idx.parent().isValid() && idx.model()->rowCount(idx) > 0) { 0614 QRect primitive = visualRect(idx); primitive.setWidth(indentation()); 0615 if (!_macStyle) { 0616 primitive.moveLeft(primitive.left() + (primitive.width() - _windowsDecoSize)/2); 0617 primitive.moveTop(primitive.top() + (primitive.height() - _windowsDecoSize)/2); 0618 primitive.setWidth(_windowsDecoSize); 0619 primitive.setHeight(_windowsDecoSize); 0620 } 0621 if(primitive.contains(event->pos())) { 0622 setExpanded(idx, !isExpanded(idx)); 0623 0624 return; 0625 } 0626 } 0627 } 0628 QTreeView::mousePressEvent(event); 0629 } 0630 0631 void PropertiesBrowserView::drawBranches(QPainter *painter, const QRect &rect, const QModelIndex &index) const 0632 { 0633 // Inspired by qt-designer code in src/components/propertyeditor/qpropertyeditor.cpp 0634 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0635 QStyleOptionViewItem opt; 0636 initViewItemOption(&opt); 0637 #else 0638 QStyleOptionViewItem opt = viewOptions(); 0639 #endif 0640 0641 if(model()->hasChildren(index)) { 0642 opt.state |= QStyle::State_Children; 0643 0644 QRect primitive(rect.left() + rect.width() - indentation(), rect.top(), 0645 indentation(), rect.height()); 0646 if(!index.parent().isValid()) { 0647 primitive.moveLeft(0); 0648 } 0649 0650 if (!_macStyle) { 0651 primitive.moveLeft(primitive.left() + (primitive.width() - _windowsDecoSize)/2); 0652 primitive.moveTop(primitive.top() + (primitive.height() - _windowsDecoSize)/2); 0653 primitive.setWidth(_windowsDecoSize); 0654 primitive.setHeight(_windowsDecoSize); 0655 } 0656 0657 opt.rect = primitive; 0658 0659 if(isExpanded(index)) opt.state |= QStyle::State_Open; 0660 style()->drawPrimitive(QStyle::PE_IndicatorBranch, &opt, painter, this); 0661 } 0662 } 0663 0664 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0665 void PropertiesBrowserView::initViewItemOption(QStyleOptionViewItem *option) const 0666 { 0667 if (!option) { 0668 return; 0669 } 0670 QTreeView::initViewItemOption(option); 0671 option->showDecorationSelected = true; 0672 } 0673 #else 0674 QStyleOptionViewItem PropertiesBrowserView::viewOptions() const 0675 { 0676 QStyleOptionViewItem option = QTreeView::viewOptions(); 0677 option.showDecorationSelected = true; 0678 return option; 0679 } 0680 #endif 0681 0682 PropertiesBrowser::PropertiesBrowser(WorldModel* worldModel, QWidget* parent) 0683 : QDockWidget(i18n("Properties"), parent) 0684 { 0685 _worldModel = worldModel; 0686 _propertiesBrowserModel = new PropertiesBrowserModel(worldModel, this); 0687 _treeView = new PropertiesBrowserView(this); 0688 0689 _treeView->setAllColumnsShowFocus(true); 0690 _treeView->setRootIsDecorated(false); 0691 //_treeView->setAlternatingRowColors(true); 0692 _treeView->setSelectionMode(QAbstractItemView::NoSelection); 0693 _treeView->setSelectionBehavior(QTreeView::SelectRows); 0694 _treeView->setEditTriggers(QAbstractItemView::AllEditTriggers); 0695 //_treeView->setEditTriggers(/*QAbstractItemView::CurrentChanged | */QAbstractItemView::SelectedClicked | 0696 // QAbstractItemView::EditKeyPressed | QAbstractItemView::AnyKeyPressed); 0697 _treeView->setItemDelegate(new PropertiesBrowserDelegate(_treeView)); 0698 0699 _treeView->setModel(_propertiesBrowserModel); 0700 worldCurrentChanged(_worldModel->worldIndex(), QModelIndex()); 0701 0702 connect(_worldModel, &QAbstractItemModel::modelReset, this, &PropertiesBrowser::worldModelReset); 0703 connect(_worldModel, &WorldModel::worldDataChanged, this, &PropertiesBrowser::worldDataChanged); 0704 connect(_worldModel, &QAbstractItemModel::rowsRemoved, 0705 this, &PropertiesBrowser::worldRowsRemoved); 0706 0707 connect(_worldModel->selectionModel(), &QItemSelectionModel::currentChanged, 0708 this, &PropertiesBrowser::worldCurrentChanged); 0709 0710 connect(_treeView->selectionModel(), &QItemSelectionModel::currentChanged, 0711 this, &PropertiesBrowser::currentChanged); 0712 0713 //connect(_treeView, SIGNAL(doubleClicked(QModelIndex)), 0714 // this, SLOT(doubleClicked(QModelIndex))); 0715 0716 connect(_propertiesBrowserModel, &QAbstractItemModel::rowsInserted, 0717 this, &PropertiesBrowser::rowsInserted); 0718 connect(_propertiesBrowserModel, &QAbstractItemModel::rowsRemoved, 0719 this, &PropertiesBrowser::rowsRemoved); 0720 0721 _treeView->viewport()->installEventFilter(this); 0722 //_treeView->setMouseTracking(true); 0723 0724 setWidget(_treeView); 0725 } 0726 0727 void PropertiesBrowser::worldModelReset() 0728 { 0729 _propertiesBrowserModel->setObject(nullptr); 0730 } 0731 0732 void PropertiesBrowser::worldCurrentChanged(const QModelIndex& current, const QModelIndex& /*previous*/) 0733 { 0734 _propertiesBrowserModel->setObject(_worldModel->object(current)); 0735 //_treeView->expandAll(); 0736 for(int i=0; i<_propertiesBrowserModel->rowCount(); ++i) { 0737 QModelIndex index = _propertiesBrowserModel->index(i, 0); 0738 if(_propertiesBrowserModel->rowCount(index) <= 10) // XXX: make it configurable 0739 _treeView->setExpanded(index, true); 0740 } 0741 } 0742 0743 void PropertiesBrowser::worldDataChanged(bool dynamicOnly) 0744 { 0745 _propertiesBrowserModel->emitDataChanged(dynamicOnly); 0746 } 0747 0748 void PropertiesBrowser::worldRowsRemoved(const QModelIndex& parent, int start, int end) 0749 { 0750 Q_UNUSED(parent) 0751 Q_UNUSED(start) 0752 Q_UNUSED(end) 0753 if(!_worldModel->objectIndex(_propertiesBrowserModel->object()).isValid()) 0754 _propertiesBrowserModel->setObject(nullptr); 0755 } 0756 0757 void PropertiesBrowser::currentChanged(const QModelIndex& current, const QModelIndex& /*previous*/) 0758 { 0759 if(current.isValid() && current.column() == 0) 0760 _treeView->selectionModel()->setCurrentIndex(current.sibling(current.row(), 1), QItemSelectionModel::Current); 0761 } 0762 0763 void PropertiesBrowser::rowsInserted(const QModelIndex& parent, int start, int end) 0764 { 0765 int rowCount = _propertiesBrowserModel->rowCount(parent); 0766 if(rowCount > 10 && (rowCount - (start-end+1)) <= 10) { 0767 _treeView->setExpanded(parent, false); 0768 } 0769 } 0770 0771 void PropertiesBrowser::rowsRemoved(const QModelIndex& parent, int start, int end) 0772 { 0773 int rowCount = _propertiesBrowserModel->rowCount(parent); 0774 if(rowCount <= 10 && rowCount + (start-end+1) > 10) { 0775 _treeView->setExpanded(parent, true); 0776 } 0777 } 0778 0779 /* 0780 void PropertiesBrowser::doubleClicked(const QModelIndex& index) 0781 { 0782 qDebug() << "doubleClicked" << endl; 0783 if(_propertiesBrowserModel->rowCount(index) > 0) { 0784 qDebug() << " doubleClicked!!!" << endl; 0785 _treeView->setExpanded(index, !_treeView->isExpanded(index)); 0786 } 0787 } 0788 */ 0789 0790 bool PropertiesBrowser::eventFilter(QObject* object, QEvent* event) 0791 { 0792 if(object == _treeView->viewport() && event->type() == QEvent::MouseButtonDblClick) { 0793 QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event); 0794 QModelIndex index = _treeView->indexAt(mouseEvent->pos()); 0795 if(_propertiesBrowserModel->rowCount(index) > 0) 0796 _treeView->setExpanded(index, !_treeView->isExpanded(index)); 0797 } 0798 return false; 0799 } 0800 0801 void PropertiesBrowser::settingsChanged() 0802 { 0803 _propertiesBrowserModel->emitDataChanged(false); 0804 } 0805 0806 #include "moc_propertiesbrowser.cpp"