1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 #include "dynamicpropertiesmodel.h"
27 
28 #include "connectionview.h"
29 
30 #include <nodemetainfo.h>
31 #include <nodeproperty.h>
32 #include <variantproperty.h>
33 #include <bindingproperty.h>
34 #include <rewritingexception.h>
35 #include <rewritertransaction.h>
36 #include <qmldesignerplugin.h>
37 #include <qmldesignerconstants.h>
38 
39 #include <utils/fileutils.h>
40 
41 #include <QMessageBox>
42 #include <QTimer>
43 #include <QUrl>
44 
45 namespace {
46 
compareVariantProperties(const QmlDesigner::VariantProperty & variantProperty01,const QmlDesigner::VariantProperty & variantProperty02)47 bool compareVariantProperties(const QmlDesigner::VariantProperty &variantProperty01, const QmlDesigner::VariantProperty &variantProperty02)
48 {
49     if (variantProperty01.parentModelNode() != variantProperty02.parentModelNode())
50         return false;
51     if (variantProperty01.name() != variantProperty02.name())
52         return false;
53     return true;
54 }
55 
idOrTypeNameForNode(const QmlDesigner::ModelNode & modelNode)56 QString idOrTypeNameForNode(const QmlDesigner::ModelNode &modelNode)
57 {
58     QString idLabel = modelNode.id();
59     if (idLabel.isEmpty())
60         idLabel = modelNode.simplifiedTypeName();
61 
62     return idLabel;
63 }
64 
unusedProperty(const QmlDesigner::ModelNode & modelNode)65 QmlDesigner::PropertyName unusedProperty(const QmlDesigner::ModelNode &modelNode)
66 {
67     QmlDesigner::PropertyName propertyName = "property";
68     int i = 0;
69     if (modelNode.metaInfo().isValid()) {
70         while (true) {
71             const QmlDesigner::PropertyName currentPropertyName = propertyName + QString::number(i).toLatin1();
72             if (!modelNode.hasProperty(currentPropertyName) && !modelNode.metaInfo().hasProperty(currentPropertyName))
73                 return currentPropertyName;
74             i++;
75         }
76     }
77 
78     return propertyName;
79 }
80 
convertVariantForTypeName(const QVariant & variant,const QmlDesigner::TypeName & typeName)81 QVariant convertVariantForTypeName(const QVariant &variant, const QmlDesigner::TypeName &typeName)
82 {
83     QVariant returnValue = variant;
84 
85     if (typeName == "int") {
86         bool ok;
87         returnValue = variant.toInt(&ok);
88         if (!ok)
89             returnValue = 0;
90     } else if (typeName == "real") {
91         bool ok;
92         returnValue = variant.toReal(&ok);
93         if (!ok)
94             returnValue = 0.0;
95 
96     } else if (typeName == "string") {
97         returnValue = variant.toString();
98 
99     } else if (typeName == "bool") {
100         returnValue = variant.toBool();
101     } else if (typeName == "url") {
102         returnValue = variant.toUrl();
103     } else if (typeName == "color") {
104         if (QColor::isValidColor(variant.toString())) {
105             returnValue = variant.toString();
106         } else {
107             returnValue = QColor(Qt::black);
108         }
109     } else if (typeName == "Item") {
110         returnValue = 0;
111     }
112 
113     return returnValue;
114 }
115 
116 } //internal namespace
117 
118 namespace QmlDesigner {
119 
120 namespace Internal {
121 
DynamicPropertiesModel(ConnectionView * parent)122 DynamicPropertiesModel::DynamicPropertiesModel(ConnectionView *parent)
123     : QStandardItemModel(parent)
124     , m_connectionView(parent)
125 {
126     connect(this, &QStandardItemModel::dataChanged, this, &DynamicPropertiesModel::handleDataChanged);
127 }
128 
resetModel()129 void DynamicPropertiesModel::resetModel()
130 {
131     beginResetModel();
132     clear();
133     setHorizontalHeaderLabels(
134         QStringList({tr("Item"), tr("Property"), tr("Property Type"), tr("Property Value")}));
135 
136     if (connectionView()->isAttached()) {
137         for (const ModelNode &modelNode : connectionView()->selectedModelNodes())
138             addModelNode(modelNode);
139     }
140 
141     endResetModel();
142 }
143 
144 
145 //Method creates dynamic BindingProperty with the same name and type as old VariantProperty
146 //Value copying is optional
replaceVariantWithBinding(const PropertyName & name,bool copyValue)147 BindingProperty DynamicPropertiesModel::replaceVariantWithBinding(const PropertyName &name, bool copyValue)
148 {
149     if (connectionView()->selectedModelNodes().count() == 1) {
150         const ModelNode modelNode = connectionView()->selectedModelNodes().constFirst();
151         if (modelNode.isValid()) {
152             if (modelNode.hasVariantProperty(name)) {
153                 try {
154                     VariantProperty vprop = modelNode.variantProperty(name);
155                     TypeName oldType = vprop.dynamicTypeName();
156                     QVariant oldValue = vprop.value();
157 
158                     modelNode.removeProperty(name);
159 
160                     BindingProperty bprop = modelNode.bindingProperty(name);
161                     if (bprop.isValid()) {
162                         if (copyValue)
163                             bprop.setDynamicTypeNameAndExpression(oldType, oldValue.toString());
164                         return bprop;
165                     }
166                 } catch (RewritingException &e) {
167                     m_exceptionError = e.description();
168                     QTimer::singleShot(200, this, &DynamicPropertiesModel::handleException);
169                 }
170             }
171         }
172     } else {
173         qWarning() << "DynamicPropertiesModel::replaceVariantWithBinding: no selected nodes";
174     }
175 
176     return BindingProperty();
177 }
178 
179 
180 //Finds selected property, and changes it to empty value (QVariant())
181 //If it's a BindingProperty, then replaces it with empty VariantProperty
resetProperty(const PropertyName & name)182 void DynamicPropertiesModel::resetProperty(const PropertyName &name)
183 {
184     if (connectionView()->selectedModelNodes().count() == 1) {
185         const ModelNode modelNode = connectionView()->selectedModelNodes().constFirst();
186         if (modelNode.isValid()) {
187             if (modelNode.hasProperty(name)) {
188                 try {
189                     AbstractProperty abProp = modelNode.property(name);
190 
191                     if (abProp.isVariantProperty()) {
192                         VariantProperty property = abProp.toVariantProperty();
193                         QVariant newValue = convertVariantForTypeName(QVariant("none.none"), property.dynamicTypeName());
194                         property.setDynamicTypeNameAndValue(property.dynamicTypeName(),
195                                                             newValue);
196                     }
197                     else if (abProp.isBindingProperty()) {
198                         BindingProperty property = abProp.toBindingProperty();
199                         TypeName oldType = property.dynamicTypeName();
200 
201                         //removing old property, to create the new one with the same name:
202                         modelNode.removeProperty(name);
203 
204                         VariantProperty newProperty = modelNode.variantProperty(name);
205                         QVariant newValue = convertVariantForTypeName(QVariant("none.none"), oldType);
206                         newProperty.setDynamicTypeNameAndValue(oldType,
207                                                                newValue);
208                     }
209 
210                 } catch (RewritingException &e) {
211                     m_exceptionError = e.description();
212                     QTimer::singleShot(200, this, &DynamicPropertiesModel::handleException);
213                 }
214             }
215         }
216     }
217     else {
218         qWarning() << "DynamicPropertiesModel::resetProperty: no selected nodes";
219     }
220 }
221 
bindingPropertyChanged(const BindingProperty & bindingProperty)222 void DynamicPropertiesModel::bindingPropertyChanged(const BindingProperty &bindingProperty)
223 {
224     if (!bindingProperty.isDynamic())
225         return;
226 
227     m_handleDataChanged = false;
228 
229     QList<ModelNode> selectedNodes = connectionView()->selectedModelNodes();
230     if (!selectedNodes.contains(bindingProperty.parentModelNode()))
231         return;
232     if (!m_lock) {
233         int rowNumber = findRowForBindingProperty(bindingProperty);
234 
235         if (rowNumber == -1) {
236             addBindingProperty(bindingProperty);
237         } else {
238             updateBindingProperty(rowNumber);
239         }
240     }
241 
242     m_handleDataChanged = true;
243 }
244 
variantPropertyChanged(const VariantProperty & variantProperty)245 void DynamicPropertiesModel::variantPropertyChanged(const VariantProperty &variantProperty)
246 {
247     if (!variantProperty.isDynamic())
248         return;
249 
250     m_handleDataChanged = false;
251 
252     QList<ModelNode> selectedNodes = connectionView()->selectedModelNodes();
253     if (!selectedNodes.contains(variantProperty.parentModelNode()))
254         return;
255     if (!m_lock) {
256         int rowNumber = findRowForVariantProperty(variantProperty);
257 
258         if (rowNumber == -1) {
259             addVariantProperty(variantProperty);
260         } else {
261             updateVariantProperty(rowNumber);
262         }
263     }
264 
265     m_handleDataChanged = true;
266 }
267 
bindingRemoved(const BindingProperty & bindingProperty)268 void DynamicPropertiesModel::bindingRemoved(const BindingProperty &bindingProperty)
269 {
270     m_handleDataChanged = false;
271 
272     QList<ModelNode> selectedNodes = connectionView()->selectedModelNodes();
273     if (!selectedNodes.contains(bindingProperty.parentModelNode()))
274         return;
275     if (!m_lock) {
276         int rowNumber = findRowForBindingProperty(bindingProperty);
277         removeRow(rowNumber);
278     }
279 
280     m_handleDataChanged = true;
281 }
282 
selectionChanged(const QList<ModelNode> & selectedNodes)283 void DynamicPropertiesModel::selectionChanged(const QList<ModelNode> &selectedNodes)
284 {
285     Q_UNUSED(selectedNodes)
286     m_handleDataChanged = false;
287     resetModel();
288     m_handleDataChanged = true;
289 }
290 
connectionView() const291 ConnectionView *DynamicPropertiesModel::connectionView() const
292 {
293     return m_connectionView;
294 }
295 
abstractPropertyForRow(int rowNumber) const296 AbstractProperty DynamicPropertiesModel::abstractPropertyForRow(int rowNumber) const
297 {
298     const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt();
299     const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2).toString();
300 
301     ModelNode  modelNode = connectionView()->modelNodeForInternalId(internalId);
302 
303     if (modelNode.isValid())
304         return modelNode.property(targetPropertyName.toUtf8());
305 
306     return AbstractProperty();
307 }
308 
bindingPropertyForRow(int rowNumber) const309 BindingProperty DynamicPropertiesModel::bindingPropertyForRow(int rowNumber) const
310 {
311     const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt();
312     const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2).toString();
313 
314     ModelNode  modelNode = connectionView()->modelNodeForInternalId(internalId);
315 
316     if (modelNode.isValid())
317         return modelNode.bindingProperty(targetPropertyName.toUtf8());
318 
319     return BindingProperty();
320 }
321 
variantPropertyForRow(int rowNumber) const322 VariantProperty DynamicPropertiesModel::variantPropertyForRow(int rowNumber) const
323 {
324     const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt();
325     const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2).toString();
326 
327     ModelNode  modelNode = connectionView()->modelNodeForInternalId(internalId);
328 
329     if (modelNode.isValid())
330         return modelNode.variantProperty(targetPropertyName.toUtf8());
331 
332     return VariantProperty();
333 }
334 
possibleTargetProperties(const BindingProperty & bindingProperty) const335 QStringList DynamicPropertiesModel::possibleTargetProperties(const BindingProperty &bindingProperty) const
336 {
337     const ModelNode modelNode = bindingProperty.parentModelNode();
338 
339     if (!modelNode.isValid()) {
340         qWarning() << " BindingModel::possibleTargetPropertiesForRow invalid model node";
341         return QStringList();
342     }
343 
344     NodeMetaInfo metaInfo = modelNode.metaInfo();
345 
346     if (metaInfo.isValid()) {
347         QStringList possibleProperties;
348         foreach (const PropertyName &propertyName, metaInfo.propertyNames()) {
349             if (metaInfo.propertyIsWritable(propertyName))
350                 possibleProperties << QString::fromUtf8(propertyName);
351         }
352 
353         return possibleProperties;
354     }
355 
356     return QStringList();
357 }
358 
addDynamicPropertyForCurrentNode()359 void DynamicPropertiesModel::addDynamicPropertyForCurrentNode()
360 {
361     QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_PROPERTY_ADDED);
362 
363     if (connectionView()->selectedModelNodes().count() == 1) {
364         const ModelNode modelNode = connectionView()->selectedModelNodes().constFirst();
365         if (modelNode.isValid()) {
366             try {
367                 modelNode.variantProperty(unusedProperty(modelNode)).setDynamicTypeNameAndValue("string", QLatin1String("none.none"));
368             } catch (RewritingException &e) {
369                 m_exceptionError = e.description();
370                 QTimer::singleShot(200, this, &DynamicPropertiesModel::handleException);
371             }
372         }
373     } else {
374         qWarning() << " BindingModel::addBindingForCurrentNode not one node selected";
375     }
376 }
377 
possibleSourceProperties(const BindingProperty & bindingProperty) const378 QStringList DynamicPropertiesModel::possibleSourceProperties(const BindingProperty &bindingProperty) const
379 {
380     const QString expression = bindingProperty.expression();
381     const QStringList stringlist = expression.split(QLatin1String("."));
382 
383     PropertyName typeName;
384 
385     if (bindingProperty.parentModelNode().metaInfo().isValid()) {
386         typeName = bindingProperty.parentModelNode().metaInfo().propertyTypeName(bindingProperty.name());
387     } else {
388         qWarning() << " BindingModel::possibleSourcePropertiesForRow no meta info for target node";
389     }
390 
391     const QString &id = stringlist.constFirst();
392 
393     ModelNode modelNode = getNodeByIdOrParent(id, bindingProperty.parentModelNode());
394 
395     if (!modelNode.isValid()) {
396         qWarning() << " BindingModel::possibleSourcePropertiesForRow invalid model node";
397         return QStringList();
398     }
399 
400     NodeMetaInfo metaInfo = modelNode.metaInfo();
401 
402     if (metaInfo.isValid())  {
403         QStringList possibleProperties;
404         foreach (const PropertyName &propertyName, metaInfo.propertyNames()) {
405             if (metaInfo.propertyTypeName(propertyName) == typeName) //### todo proper check
406                 possibleProperties << QString::fromUtf8(propertyName);
407         }
408         return possibleProperties;
409     } else {
410         qWarning() << " BindingModel::possibleSourcePropertiesForRow no meta info for source node";
411     }
412 
413     return QStringList();
414 }
415 
deleteDynamicPropertyByRow(int rowNumber)416 void DynamicPropertiesModel::deleteDynamicPropertyByRow(int rowNumber)
417 {
418     BindingProperty bindingProperty = bindingPropertyForRow(rowNumber);
419     if (bindingProperty.isValid()) {
420         bindingProperty.parentModelNode().removeProperty(bindingProperty.name());
421     }
422 
423     VariantProperty variantProperty = variantPropertyForRow(rowNumber);
424 
425     if (variantProperty.isValid()) {
426         variantProperty.parentModelNode().removeProperty(variantProperty.name());
427     }
428 
429     resetModel();
430 }
431 
addProperty(const QVariant & propertyValue,const QString & propertyType,const AbstractProperty & abstractProperty)432 void DynamicPropertiesModel::addProperty(const QVariant &propertyValue,
433                                          const QString &propertyType,
434                                          const AbstractProperty &abstractProperty)
435 {
436     QList<QStandardItem*> items;
437 
438     QStandardItem *idItem;
439     QStandardItem *propertyNameItem;
440     QStandardItem *propertyTypeItem;
441     QStandardItem *propertyValueItem;
442 
443     idItem = new QStandardItem(idOrTypeNameForNode(abstractProperty.parentModelNode()));
444     updateCustomData(idItem, abstractProperty);
445 
446     propertyNameItem = new QStandardItem(QString::fromUtf8(abstractProperty.name()));
447 
448     items.append(idItem);
449     items.append(propertyNameItem);
450 
451 
452     propertyTypeItem = new QStandardItem(propertyType);
453     items.append(propertyTypeItem);
454 
455     propertyValueItem = new QStandardItem();
456     propertyValueItem->setData(propertyValue, Qt::DisplayRole);
457     items.append(propertyValueItem);
458 
459     appendRow(items);
460 }
461 
addBindingProperty(const BindingProperty & property)462 void DynamicPropertiesModel::addBindingProperty(const BindingProperty &property)
463 {
464     QVariant value = property.expression();
465     QString type = QString::fromLatin1(property.dynamicTypeName());
466     addProperty(value, type, property);
467 }
468 
addVariantProperty(const VariantProperty & property)469 void DynamicPropertiesModel::addVariantProperty(const VariantProperty &property)
470 {
471     QVariant value = property.value();
472     QString type = QString::fromLatin1(property.dynamicTypeName());
473     addProperty(value, type, property);
474 }
475 
updateBindingProperty(int rowNumber)476 void DynamicPropertiesModel::updateBindingProperty(int rowNumber)
477 {
478     BindingProperty bindingProperty = bindingPropertyForRow(rowNumber);
479 
480     if (bindingProperty.isValid()) {
481         QString propertyName = QString::fromUtf8(bindingProperty.name());
482         updateDisplayRole(rowNumber, PropertyNameRow, propertyName);
483         QString value = bindingProperty.expression();
484         QString type = QString::fromUtf8(bindingProperty.dynamicTypeName());
485         updateDisplayRole(rowNumber, PropertyTypeRow, type);
486         updateDisplayRole(rowNumber, PropertyValueRow, value);
487     }
488 }
489 
updateVariantProperty(int rowNumber)490 void DynamicPropertiesModel::updateVariantProperty(int rowNumber)
491 {
492     VariantProperty variantProperty = variantPropertyForRow(rowNumber);
493 
494     if (variantProperty.isValid()) {
495         QString propertyName = QString::fromUtf8(variantProperty.name());
496         updateDisplayRole(rowNumber, PropertyNameRow, propertyName);
497         QVariant value = variantProperty.value();
498         QString type = QString::fromUtf8(variantProperty.dynamicTypeName());
499         updateDisplayRole(rowNumber, PropertyTypeRow, type);
500 
501         updateDisplayRoleFromVariant(rowNumber, PropertyValueRow, value);
502     }
503 }
504 
addModelNode(const ModelNode & modelNode)505 void DynamicPropertiesModel::addModelNode(const ModelNode &modelNode)
506 {
507     foreach (const BindingProperty &bindingProperty, modelNode.bindingProperties()) {
508         if (bindingProperty.isDynamic())
509             addBindingProperty(bindingProperty);
510     }
511 
512     foreach (const VariantProperty &variantProperty, modelNode.variantProperties()) {
513         if (variantProperty.isDynamic())
514             addVariantProperty(variantProperty);
515     }
516 }
517 
updateValue(int row)518 void DynamicPropertiesModel::updateValue(int row)
519 {
520     BindingProperty bindingProperty = bindingPropertyForRow(row);
521 
522     if (bindingProperty.isBindingProperty()) {
523         const QString expression = data(index(row, PropertyValueRow)).toString();
524 
525         RewriterTransaction transaction = connectionView()->beginRewriterTransaction(QByteArrayLiteral("DynamicPropertiesModel::updateValue"));
526         try {
527             bindingProperty.setDynamicTypeNameAndExpression(bindingProperty.dynamicTypeName(), expression);
528             transaction.commit(); //committing in the try block
529         } catch (Exception &e) {
530             m_exceptionError = e.description();
531             QTimer::singleShot(200, this, &DynamicPropertiesModel::handleException);
532         }
533         return;
534     }
535 
536     VariantProperty variantProperty = variantPropertyForRow(row);
537 
538     if (variantProperty.isVariantProperty()) {
539         const QVariant value = data(index(row, PropertyValueRow));
540 
541         RewriterTransaction transaction = connectionView()->beginRewriterTransaction(QByteArrayLiteral("DynamicPropertiesModel::updateValue"));
542         try {
543             variantProperty.setDynamicTypeNameAndValue(variantProperty.dynamicTypeName(), value);
544             transaction.commit(); //committing in the try block
545         } catch (Exception &e) {
546             m_exceptionError = e.description();
547             QTimer::singleShot(200, this, &DynamicPropertiesModel::handleException);
548         }
549     }
550 }
551 
updatePropertyName(int rowNumber)552 void DynamicPropertiesModel::updatePropertyName(int rowNumber)
553 {
554     const PropertyName newName = data(index(rowNumber, PropertyNameRow)).toString().toUtf8();
555     if (newName.isEmpty()) {
556         qWarning() << "DynamicPropertiesModel::updatePropertyName invalid property name";
557         return;
558     }
559 
560     BindingProperty bindingProperty = bindingPropertyForRow(rowNumber);
561 
562     ModelNode targetNode = bindingProperty.parentModelNode();
563 
564     if (bindingProperty.isBindingProperty()) {
565         connectionView()->executeInTransaction("DynamicPropertiesModel::updatePropertyName", [bindingProperty, newName, &targetNode](){
566             const QString expression = bindingProperty.expression();
567             const PropertyName dynamicPropertyType = bindingProperty.dynamicTypeName();
568 
569             targetNode.bindingProperty(newName).setDynamicTypeNameAndExpression(dynamicPropertyType, expression);
570             targetNode.removeProperty(bindingProperty.name());
571         });
572 
573         updateCustomData(rowNumber, targetNode.bindingProperty(newName));
574         return;
575     }
576 
577     VariantProperty variantProperty = variantPropertyForRow(rowNumber);
578 
579     if (variantProperty.isVariantProperty()) {
580         const QVariant value = variantProperty.value();
581         const PropertyName dynamicPropertyType = variantProperty.dynamicTypeName();
582         ModelNode targetNode = variantProperty.parentModelNode();
583 
584         connectionView()->executeInTransaction("DynamicPropertiesModel::updatePropertyName", [=](){
585             targetNode.variantProperty(newName).setDynamicTypeNameAndValue(dynamicPropertyType, value);
586             targetNode.removeProperty(variantProperty.name());
587         });
588 
589         updateCustomData(rowNumber, targetNode.variantProperty(newName));
590     }
591 }
592 
updatePropertyType(int rowNumber)593 void DynamicPropertiesModel::updatePropertyType(int rowNumber)
594 {
595 
596     const TypeName newType = data(index(rowNumber, PropertyTypeRow)).toString().toLatin1();
597 
598     if (newType.isEmpty()) {
599         qWarning() << "DynamicPropertiesModel::updatePropertyName invalid property type";
600         return;
601     }
602 
603     BindingProperty bindingProperty = bindingPropertyForRow(rowNumber);
604 
605     if (bindingProperty.isBindingProperty()) {
606         const QString expression = bindingProperty.expression();
607         const PropertyName propertyName = bindingProperty.name();
608         ModelNode targetNode = bindingProperty.parentModelNode();
609 
610         connectionView()->executeInTransaction("DynamicPropertiesModel::updatePropertyType", [=](){
611             targetNode.removeProperty(bindingProperty.name());
612             targetNode.bindingProperty(propertyName).setDynamicTypeNameAndExpression(newType, expression);
613         });
614 
615         updateCustomData(rowNumber, targetNode.bindingProperty(propertyName));
616         return;
617     }
618 
619     VariantProperty variantProperty = variantPropertyForRow(rowNumber);
620 
621     if (variantProperty.isVariantProperty()) {
622         const QVariant value = variantProperty.value();
623         ModelNode targetNode = variantProperty.parentModelNode();
624         const PropertyName propertyName = variantProperty.name();
625 
626         connectionView()->executeInTransaction("DynamicPropertiesModel::updatePropertyType", [=](){
627             targetNode.removeProperty(variantProperty.name());
628             if (newType == "alias") { //alias properties have to be bindings
629                 targetNode.bindingProperty(propertyName).setDynamicTypeNameAndExpression(newType, QLatin1String("none.none"));
630             } else {
631                 targetNode.variantProperty(propertyName).setDynamicTypeNameAndValue(newType, convertVariantForTypeName(value, newType));
632             }
633         });
634 
635         updateCustomData(rowNumber, targetNode.variantProperty(propertyName));
636 
637         if (variantProperty.isVariantProperty()) {
638             updateVariantProperty(rowNumber);
639         } else if (bindingProperty.isBindingProperty()) {
640             updateBindingProperty(rowNumber);
641         }
642     }
643 }
644 
getNodeByIdOrParent(const QString & id,const ModelNode & targetNode) const645 ModelNode DynamicPropertiesModel::getNodeByIdOrParent(const QString &id, const ModelNode &targetNode) const
646 {
647     ModelNode modelNode;
648 
649     if (id != QLatin1String("parent")) {
650         modelNode = connectionView()->modelNodeForId(id);
651     } else {
652         if (targetNode.hasParentProperty()) {
653             modelNode = targetNode.parentProperty().parentModelNode();
654         }
655     }
656     return modelNode;
657 }
658 
updateCustomData(QStandardItem * item,const AbstractProperty & property)659 void DynamicPropertiesModel::updateCustomData(QStandardItem *item, const AbstractProperty &property)
660 {
661     item->setData(property.parentModelNode().internalId(), Qt::UserRole + 1);
662     item->setData(property.name(), Qt::UserRole + 2);
663 }
664 
updateCustomData(int row,const AbstractProperty & property)665 void DynamicPropertiesModel::updateCustomData(int row, const AbstractProperty &property)
666 {
667     QStandardItem* idItem = item(row, 0);
668     updateCustomData(idItem, property);
669 }
670 
findRowForBindingProperty(const BindingProperty & bindingProperty) const671 int DynamicPropertiesModel::findRowForBindingProperty(const BindingProperty &bindingProperty) const
672 {
673     for (int i=0; i < rowCount(); i++) {
674         if (compareBindingProperties(bindingPropertyForRow(i), bindingProperty))
675             return i;
676     }
677     //not found
678     return -1;
679 }
680 
findRowForVariantProperty(const VariantProperty & variantProperty) const681 int DynamicPropertiesModel::findRowForVariantProperty(const VariantProperty &variantProperty) const
682 {
683     for (int i=0; i < rowCount(); i++) {
684         if (compareVariantProperties(variantPropertyForRow(i), variantProperty))
685             return i;
686     }
687     //not found
688     return -1;
689 }
690 
getExpressionStrings(const BindingProperty & bindingProperty,QString * sourceNode,QString * sourceProperty)691 bool DynamicPropertiesModel::getExpressionStrings(const BindingProperty &bindingProperty, QString *sourceNode, QString *sourceProperty)
692 {
693     //### todo we assume no expressions yet
694 
695     const QString expression = bindingProperty.expression();
696 
697     if (true) {
698         const QStringList stringList = expression.split(QLatin1String("."));
699 
700         *sourceNode = stringList.constFirst();
701 
702         QString propertyName;
703 
704         for (int i=1; i < stringList.count(); i++) {
705             propertyName += stringList.at(i);
706             if (i != stringList.count() - 1)
707                 propertyName += QLatin1String(".");
708         }
709         *sourceProperty = propertyName;
710     }
711     return true;
712 }
713 
updateDisplayRole(int row,int columns,const QString & string)714 void DynamicPropertiesModel::updateDisplayRole(int row, int columns, const QString &string)
715 {
716     QModelIndex modelIndex = index(row, columns);
717     if (data(modelIndex).toString() != string)
718         setData(modelIndex, string);
719 }
720 
updateDisplayRoleFromVariant(int row,int columns,const QVariant & variant)721 void DynamicPropertiesModel::updateDisplayRoleFromVariant(int row, int columns, const QVariant &variant)
722 {
723     QModelIndex modelIndex = index(row, columns);
724     if (data(modelIndex) != variant)
725         setData(modelIndex, variant);
726 }
727 
728 
handleDataChanged(const QModelIndex & topLeft,const QModelIndex & bottomRight)729 void DynamicPropertiesModel::handleDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
730 {
731     if (!m_handleDataChanged)
732         return;
733 
734     if (topLeft != bottomRight) {
735         qWarning() << "BindingModel::handleDataChanged multi edit?";
736         return;
737     }
738 
739     m_lock = true;
740 
741     int currentColumn = topLeft.column();
742     int currentRow = topLeft.row();
743 
744     switch (currentColumn) {
745     case TargetModelNodeRow: {
746         //updating user data
747     } break;
748     case PropertyNameRow: {
749         updatePropertyName(currentRow);
750     } break;
751     case PropertyTypeRow: {
752         updatePropertyType(currentRow);
753     } break;
754     case PropertyValueRow: {
755         updateValue(currentRow);
756     } break;
757 
758     default: qWarning() << "BindingModel::handleDataChanged column" << currentColumn;
759     }
760 
761     m_lock = false;
762 }
763 
handleException()764 void DynamicPropertiesModel::handleException()
765 {
766     QMessageBox::warning(nullptr, tr("Error"), m_exceptionError);
767     resetModel();
768 }
769 
770 } // namespace Internal
771 
772 } // namespace QmlDesigner
773