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 "modelnode.h"
27 #include <abstractproperty.h>
28 #include <abstractview.h>
29 #include <model.h>
30 #include <nodemetainfo.h>
31 #include "internalnode_p.h"
32 #include "invalidargumentexception.h"
33 #include "invalididexception.h"
34 #include "invalidmodelnodeexception.h"
35 #include "invalidpropertyexception.h"
36 #include "model_p.h"
37 #include "variantproperty.h"
38 #include "bindingproperty.h"
39 #include "signalhandlerproperty.h"
40 #include "nodeabstractproperty.h"
41 #include "nodelistproperty.h"
42 #include "nodeproperty.h"
43 #include <rewriterview.h>
44 #include "annotation.h"
45 
46 #include <utils/algorithm.h>
47 
48 #include <QHash>
49 #include <QRegularExpression>
50 #include <QSet>
51 #include <QTextStream>
52 
53 namespace QmlDesigner {
54 using namespace QmlDesigner::Internal;
55 
56 /*!
57 \class QmlDesigner::ModelNode
58 \ingroup CoreModel
59 \brief The central class to access the node which can represent a widget, layout
60             or other items. A Node is a part of a tree and has properties.
61 
62 Conceptually ModelNode is an opaque handle to the internal data structures.
63 
64 There is always a root model node in every QmlDesigner::Model:
65 \code
66 QmlDesigner::Model *model = QmlDesigner::Model::create();
67 QmlDesigner::ModelNode rootNode = model->rootNode();
68 \endcode
69 
70 You can add a property to a node:
71 \code
72 childNode.addProperty("pos", QPoint(2, 12));
73 \endcode
74 
75 All the manipulation functions are generating undo commands internally.
76 */
77 
78 
79 
80 /*! \brief internal constructor
81 
82 */
ModelNode(const InternalNodePointer & internalNode,Model * model,const AbstractView * view)83 ModelNode::ModelNode(const InternalNodePointer &internalNode, Model *model, const AbstractView *view):
84         m_internalNode(internalNode),
85         m_model(model),
86         m_view(const_cast<AbstractView*>(view))
87 {
88     Q_ASSERT(!m_model || m_view);
89 }
90 
ModelNode(const ModelNode & modelNode,AbstractView * view)91 ModelNode::ModelNode(const ModelNode &modelNode, AbstractView *view)
92     : m_internalNode(modelNode.m_internalNode),
93       m_model(modelNode.model()),
94       m_view(view)
95 {
96 }
97 
ModelNode(ModelNode && other)98 ModelNode::ModelNode(ModelNode &&other)
99     : m_internalNode(std::move(other.m_internalNode))
100     , m_model(std::move(other.m_model))
101     , m_view(std::move(other.m_view))
102 {
103     other.m_model = {};
104     other.m_view = {};
105 }
106 
operator =(ModelNode && other)107 ModelNode &ModelNode::operator=(ModelNode &&other)
108 {
109     ModelNode newNode = std::move(other);
110 
111     swap(*this, newNode);
112 
113     return *this;
114 }
115 
116 /*! \brief contructs a invalid model node
117 \return invalid node
118 \see invalid
119 */
ModelNode()120 ModelNode::ModelNode():
121         m_internalNode(new InternalNode)
122 {
123 }
124 
125 ModelNode::ModelNode(const ModelNode &other) = default;
126 
127 ModelNode& ModelNode::operator=(const ModelNode &other) = default;
128 
129 /*! \brief does nothing
130 */
131 ModelNode::~ModelNode() = default;
132 
133 /*! \brief returns the name of node which is a short cut to a property like objectName
134 \return name of the node
135 */
id() const136 QString ModelNode::id() const
137 {
138     if (!isValid())
139         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
140 
141     return m_internalNode->id();
142 }
143 
validId()144 QString ModelNode::validId()
145 {
146     if (id().isEmpty())
147         setIdWithRefactoring(view()->generateNewId(simplifiedTypeName()));
148 
149     return id();
150 }
151 
idIsQmlKeyWord(const QString & id)152 static bool idIsQmlKeyWord(const QString& id)
153 {
154     static const QSet<QString> keywords = {"as",         "break",    "case",    "catch",
155                                            "continue",   "debugger", "default", "delete",
156                                            "do",         "else",     "finally", "for",
157                                            "function",   "if",       "import",  "in",
158                                            "instanceof", "new",      "print",   "return",
159                                            "switch",     "this",     "throw",   "try",
160                                            "typeof",     "var",      "void",    "while",
161                                            "with"};
162 
163     return keywords.contains(id);
164 }
165 
isIdToAvoid(const QString & id)166 static bool isIdToAvoid(const QString& id)
167 {
168     static const QSet<QString> ids = {
169         "top",
170         "bottom",
171         "left",
172         "right",
173         "width",
174         "height",
175         "x",
176         "y",
177         "opacity",
178         "parent",
179         "item",
180         "flow",
181         "color",
182         "margin",
183         "padding",
184         "border",
185         "font",
186         "text",
187         "source",
188         "state",
189         "visible",
190         "focus",
191         "data",
192         "clip",
193         "layer",
194         "scale",
195         "enabled",
196         "anchors",
197         "texture",
198         "shaderInfo"
199     };
200 
201     return ids.contains(id);
202 }
203 
idContainsWrongLetter(const QString & id)204 static bool idContainsWrongLetter(const QString& id)
205 {
206     static QRegularExpression idExpr(QStringLiteral("^[a-z_][a-zA-Z0-9_]*$"));
207     return !id.contains(idExpr);
208 }
209 
isValidId(const QString & id)210 bool ModelNode::isValidId(const QString &id)
211 {
212     return id.isEmpty() || (!idContainsWrongLetter(id) && !idIsQmlKeyWord(id) && !isIdToAvoid(id));
213 }
214 
getIdValidityErrorMessage(const QString & id)215 QString ModelNode::getIdValidityErrorMessage(const QString &id)
216 {
217     if (isValidId(id))
218         return {}; // valid
219 
220     if (id.at(0).isUpper())
221         return QObject::tr("ID cannot start with an uppercase character (%1).").arg(id);
222 
223     if (id.at(0).isDigit())
224         return QObject::tr("ID cannot start with a number (%1).").arg(id);
225 
226     if (id.contains(' '))
227         return QObject::tr("ID cannot include whitespace (%1).").arg(id);
228 
229     if (idIsQmlKeyWord(id))
230         return QObject::tr("%1 is a reserved QML keyword.").arg(id);
231 
232     if (isIdToAvoid(id))
233         return QObject::tr("%1 is a reserved property keyword.").arg(id);
234 
235     return QObject::tr("ID includes invalid characters (%1).").arg(id);
236 }
237 
hasId() const238 bool ModelNode::hasId() const
239 {
240     if (!isValid())
241         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
242 
243     return m_internalNode->hasId();
244 }
245 
setIdWithRefactoring(const QString & id)246 void ModelNode::setIdWithRefactoring(const QString& id)
247 {
248     if (model()->rewriterView()
249             && !id.isEmpty()
250             && !m_internalNode->id().isEmpty()) { // refactor the id if they are not empty
251         model()->rewriterView()->renameId(m_internalNode->id(), id);
252     } else {
253         setIdWithoutRefactoring(id);
254     }
255 }
256 
setIdWithoutRefactoring(const QString & id)257 void ModelNode::setIdWithoutRefactoring(const QString &id)
258 {
259     Internal::WriteLocker locker(m_model.data());
260     if (!isValid()) {
261         Q_ASSERT_X(isValid(), Q_FUNC_INFO, "model node is invalid");
262         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
263     }
264 
265     if (!isValidId(id))
266         throw InvalidIdException(__LINE__, __FUNCTION__, __FILE__, id.toUtf8(), InvalidIdException::InvalidCharacters);
267 
268     if (id == m_internalNode->id())
269         return;
270 
271     if (view()->hasId(id))
272         throw InvalidIdException(__LINE__, __FUNCTION__, __FILE__, id.toUtf8(), InvalidIdException::DuplicateId);
273 
274     m_model.data()->d->changeNodeId(internalNode(), id);
275 }
276 
277 /*! \brief the fully-qualified type name of the node is represented as string
278 \return type of the node as a string
279 */
type() const280 TypeName ModelNode::type() const
281 {
282     if (!isValid()) {
283         Q_ASSERT_X(isValid(), Q_FUNC_INFO, "model node is invalid");
284         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
285     }
286     return m_internalNode->type();
287 }
288 
289 /*! \brief minor number of the QML type
290 \return minor number
291 */
minorVersion() const292 int ModelNode::minorVersion() const
293 {
294     if (!isValid()) {
295         Q_ASSERT_X(isValid(), Q_FUNC_INFO, "model node is invalid");
296         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
297     }
298     return m_internalNode->minorVersion();
299 }
300 
301 /*! \brief major number of the QML type
302 \return major number
303 */
majorVersion() const304 int ModelNode::majorVersion() const
305 {
306     if (!isValid()) {
307         Q_ASSERT_X(isValid(), Q_FUNC_INFO, "model node is invalid");
308         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
309     }
310     return m_internalNode->majorVersion();
311 }
312 
313 /*! \return the short-hand type name of the node. */
simplifiedTypeName() const314 QString ModelNode::simplifiedTypeName() const
315 {
316     if (!isValid()) {
317         Q_ASSERT_X(isValid(), Q_FUNC_INFO, "model node is invalid");
318         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
319     }
320 
321     return QString::fromUtf8(type().split('.').constLast());
322 }
323 
displayName() const324 QString ModelNode::displayName() const
325 {
326     if (hasId())
327         return id();
328     return simplifiedTypeName();
329 }
330 
331 /*! \brief Returns whether the node is valid
332 
333 A node is valid if its model still exists, and contains this node.
334 Also, the current state must be a valid one.
335 
336 A node might become invalid if e.g. it or one of its ancestors is deleted.
337 
338 \return is a node valid(true) or invalid(false)
339 */
isValid() const340 bool ModelNode::isValid() const
341 {
342     return !m_model.isNull() && !m_view.isNull() && m_internalNode && m_internalNode->isValid();
343 }
344 
345 /*!
346   \brief Returns whether the root node of the model is one of the anchestors of this node.
347 
348   Will return true also for the root node itself.
349   */
isInHierarchy() const350 bool ModelNode::isInHierarchy() const
351 {
352     if (!isValid()) {
353         Q_ASSERT_X(isValid(), Q_FUNC_INFO, "model node is invalid");
354         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
355     }
356     if (isRootNode())
357         return true;
358     if (!hasParentProperty())
359         return false;
360     return parentProperty().parentModelNode().isInHierarchy();
361 }
362 
363 /*!
364   \brief Returns the property containing this node
365 
366   The NodeAbstractProperty is invalid if this ModelNode has no parent.
367   NodeAbstractProperty can be a NodeProperty containing a single ModelNode, or
368   a NodeListProperty.
369 
370   \return the property containing this ModelNode
371   */
parentProperty() const372 NodeAbstractProperty ModelNode::parentProperty() const
373 {
374     if (!isValid()) {
375         Q_ASSERT_X(isValid(), Q_FUNC_INFO, "model node is invalid");
376         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
377     }
378 
379     if (m_internalNode->parentProperty().isNull()) {
380         Q_ASSERT_X(m_internalNode->parentProperty(), Q_FUNC_INFO, "parentProperty is invalid");
381         throw InvalidPropertyException(__LINE__, __FUNCTION__, __FILE__, "parent");
382     }
383 
384     return NodeAbstractProperty(m_internalNode->parentProperty()->name(), m_internalNode->parentProperty()->propertyOwner(), m_model.data(), view());
385 }
386 
387 
388 /*! \brief the command id is used to compress the some commands together.
389 \param newParentNode parent of this node will be set to this node
390 \param commandId integer which is used to descripe commands which should compressed together to one command
391 
392 For example:
393 \code
394 node.setParentNode(parentNode1);
395 node.setParentNode(parentNode2, 212);
396 node.setParentNode(parentNode3, 212);
397 model->undoStack()->undo();
398 ModelNode parentNode4 = node.parentProperty().parentModelNode();
399 parentNode4 == parentNode1; -> true
400 \endcode
401 
402 \see parentNode childNodes hasChildNodes Model::undo
403 
404 */
405 
setParentProperty(NodeAbstractProperty parent)406 void ModelNode::setParentProperty(NodeAbstractProperty parent)
407 {
408     if (!isValid()) {
409         Q_ASSERT_X(isValid(), Q_FUNC_INFO, "model node is invalid");
410         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
411     }
412 
413     if (!parent.parentModelNode().isValid())
414         throw InvalidArgumentException(__LINE__, __FUNCTION__, __FILE__, "newParentNode");
415 
416     if (*this == parent.parentModelNode()) {
417         Q_ASSERT_X(*this != parent.parentModelNode(), Q_FUNC_INFO, "cannot set parent to itself");
418         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
419     }
420 
421     if (hasParentProperty() && parent == parentProperty())
422         return;
423 
424     parent.reparentHere(*this);
425 }
426 
changeType(const TypeName & typeName,int majorVersion,int minorVersion)427 void ModelNode::changeType(const TypeName &typeName, int majorVersion, int minorVersion)
428 {
429     if (!isValid()) {
430         Q_ASSERT_X(isValid(), Q_FUNC_INFO, "model node is invalid");
431         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
432     }
433 
434     model()->d->changeNodeType(internalNode(), typeName, majorVersion, minorVersion);
435 
436 }
437 
setParentProperty(const ModelNode & newParentNode,const PropertyName & propertyName)438 void ModelNode::setParentProperty(const ModelNode &newParentNode, const PropertyName &propertyName)
439 {
440     setParentProperty(newParentNode.nodeAbstractProperty(propertyName));
441 }
442 
443 /*! \brief test if there is a parent for this node
444 \return true is this node has a parent
445 \see childNodes parentNode setParentNode hasChildNodes Model::undo
446 */
hasParentProperty() const447 bool ModelNode::hasParentProperty() const
448 {
449     if (!isValid()) {
450         Q_ASSERT_X(isValid(), Q_FUNC_INFO, "model node is invalid");
451         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
452     }
453 
454     if (m_internalNode->parentProperty().isNull())
455         return false;
456 
457     return true;
458 }
459 
460 /*!
461   \brief Returns a BindingProperty
462 
463   Note that a valid BindingProperty is returned, if the ModelNode is valid,
464   even if this property does not exist or is not a BindingProperty.
465   Assigning an expression to this BindingProperty will create the property.
466 
467   \return BindingProperty named name
468   */
469 
bindingProperty(const PropertyName & name) const470 BindingProperty ModelNode::bindingProperty(const PropertyName &name) const
471 {
472     if (!isValid())
473         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
474 
475     return BindingProperty(name, m_internalNode, model(), view());
476 }
477 
signalHandlerProperty(const PropertyName & name) const478 SignalHandlerProperty ModelNode::signalHandlerProperty(const PropertyName &name) const
479 {
480     if (!isValid())
481         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
482 
483     return SignalHandlerProperty(name, m_internalNode, model(), view());
484 }
485 
486 
487 /*!
488   \brief Returns a NodeProperty
489 
490   Note that a valid NodeProperty is returned, if the ModelNode is valid,
491   even if this property does not exist or is not a NodeProperty.
492   Assigning a ModelNode to this NodeProperty will create the property.
493 
494   \return NodeProperty named name
495   */
496 
nodeProperty(const PropertyName & name) const497 NodeProperty ModelNode::nodeProperty(const PropertyName &name) const
498 {
499       if (!isValid())
500         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
501 
502     return NodeProperty(name, m_internalNode, model(), view());
503 }
504 
505 
506 /*!
507   \brief Returns a NodeListProperty
508 
509   Note that a valid NodeListProperty is returned, if the ModelNode is valid,
510   even if this property does not exist or is not a NodeListProperty.
511   Assigning a ModelNode to this NodeListProperty will create the property.
512 
513   \return NodeListProperty named name
514   */
515 
nodeListProperty(const PropertyName & name) const516 NodeListProperty ModelNode::nodeListProperty(const PropertyName &name) const
517 {
518     if (!isValid())
519         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
520 
521     return NodeListProperty(name, m_internalNode, model(), view());
522 }
523 
nodeAbstractProperty(const PropertyName & name) const524 NodeAbstractProperty ModelNode::nodeAbstractProperty(const PropertyName &name) const
525 {
526      if (!isValid())
527         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
528 
529      return NodeAbstractProperty(name, m_internalNode, model(), view());
530 }
531 
defaultNodeAbstractProperty() const532 NodeAbstractProperty ModelNode::defaultNodeAbstractProperty() const
533 {
534     return nodeAbstractProperty(metaInfo().defaultPropertyName());
535 }
536 
defaultNodeListProperty() const537 NodeListProperty ModelNode::defaultNodeListProperty() const
538 {
539     return nodeListProperty(metaInfo().defaultPropertyName());
540 }
541 
defaultNodeProperty() const542 NodeProperty ModelNode::defaultNodeProperty() const
543 {
544     return nodeProperty(metaInfo().defaultPropertyName());
545 }
546 
547 /*!
548   \brief Returns a VariantProperty
549 
550   Note that a valid VariantProperty is returned, if the ModelNode is valid,
551   even if this property does not exist or is not a VariantProperty.
552   Assigning a value to this VariantProperty will create the property.
553 
554   \return VariantProperty named name
555   */
556 
variantProperty(const PropertyName & name) const557 VariantProperty ModelNode::variantProperty(const PropertyName &name) const
558 {
559     if (!isValid())
560         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
561 
562     return VariantProperty(name, m_internalNode, model(), view());
563 }
564 
property(const PropertyName & name) const565 AbstractProperty ModelNode::property(const PropertyName &name) const
566 {
567     if (!isValid())
568         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
569 
570     return AbstractProperty(name, m_internalNode, model(), view());
571 }
572 
573 /*! \brief returns a property
574 \param name name of the property
575 \return returns a node property handle. If the property is not set yet, the node property is still valid (lazy reference).
576 
577 It is searching only in the local Property.
578 
579 \see addProperty changePropertyValue removeProperty properties hasProperties
580 */
581 
582 /*! \brief returns a list of all properties
583 \return list of all properties
584 
585 The list of properties
586 
587 */
properties() const588 QList<AbstractProperty> ModelNode::properties() const
589 {
590     if (!isValid())
591         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
592 
593     QList<AbstractProperty> propertyList;
594 
595     foreach (const PropertyName &propertyName, internalNode()->propertyNameList()) {
596         AbstractProperty property(propertyName, internalNode(), model(), view());
597         propertyList.append(property);
598     }
599 
600     return propertyList;
601 }
602 
603 
604 /*! \brief returns a list of all VariantProperties
605 \return list of all VariantProperties
606 
607 The list of all properties containing just an atomic value.
608 
609 */
variantProperties() const610 QList<VariantProperty> ModelNode::variantProperties() const
611 {
612     QList<VariantProperty> propertyList;
613 
614     foreach (const AbstractProperty &abstractProperty, properties())
615         if (abstractProperty.isVariantProperty())
616             propertyList.append(abstractProperty.toVariantProperty());
617     return propertyList;
618 }
619 
nodeAbstractProperties() const620 QList<NodeAbstractProperty> ModelNode::nodeAbstractProperties() const
621 {
622     QList<NodeAbstractProperty> propertyList;
623 
624     foreach (const AbstractProperty &nodeAbstractProperty, properties())
625         if (nodeAbstractProperty.isNodeAbstractProperty())
626             propertyList.append(nodeAbstractProperty.toNodeAbstractProperty());
627     return propertyList;
628 }
629 
nodeProperties() const630 QList<NodeProperty> ModelNode::nodeProperties() const
631 {
632     QList<NodeProperty> propertyList;
633 
634     foreach (const AbstractProperty &nodeProperty, properties())
635         if (nodeProperty.isNodeProperty())
636             propertyList.append(nodeProperty.toNodeProperty());
637     return propertyList;
638 }
639 
nodeListProperties() const640 QList<NodeListProperty> ModelNode::nodeListProperties() const
641 {
642     QList<NodeListProperty> propertyList;
643 
644     foreach (const AbstractProperty &nodeListProperty, properties())
645         if (nodeListProperty.isNodeListProperty())
646             propertyList.append(nodeListProperty.toNodeListProperty());
647     return propertyList;
648 }
649 
650 
651 /*! \brief returns a list of all BindingProperties
652 \return list of all BindingProperties
653 
654 The list of all properties containing an expression.
655 
656 */
bindingProperties() const657 QList<BindingProperty> ModelNode::bindingProperties() const
658 {
659     QList<BindingProperty> propertyList;
660 
661     foreach (const AbstractProperty &bindingProperty, properties())
662         if (bindingProperty.isBindingProperty())
663             propertyList.append(bindingProperty.toBindingProperty());
664     return propertyList;
665 }
666 
signalProperties() const667 QList<SignalHandlerProperty> ModelNode::signalProperties() const
668 {
669     QList<SignalHandlerProperty> propertyList;
670 
671     foreach (const AbstractProperty &property, properties())
672         if (property.isSignalHandlerProperty())
673             propertyList.append(property.toSignalHandlerProperty());
674     return propertyList;
675 }
676 
677 /*!
678 \brief removes a property from this node
679 \param name name of the property
680 
681 Does nothing if the node state does not set this property.
682 
683 \see addProperty property  properties hasProperties
684 */
removeProperty(const PropertyName & name) const685 void ModelNode::removeProperty(const PropertyName &name) const
686 {
687     if (!isValid())
688         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
689 
690     model()->d->checkPropertyName(name);
691 
692     if (internalNode()->hasProperty(name))
693         model()->d->removeProperty(internalNode()->property(name));
694 }
695 
696 /*! \brief removes this node from the node tree
697 */
descendantNodes(const ModelNode & node)698 static QList<ModelNode> descendantNodes(const ModelNode &node)
699 {
700     const QList<ModelNode> children = node.directSubModelNodes();
701     QList<ModelNode> descendants = children;
702     for (const ModelNode &child : children)
703         descendants += descendantNodes(child);
704 
705     return descendants;
706 }
707 
removeModelNodeFromSelection(const ModelNode & node)708 static void removeModelNodeFromSelection(const ModelNode &node)
709 {
710     // remove nodes from the active selection
711     QList<ModelNode> selectedList = node.view()->selectedModelNodes();
712 
713     const QList<ModelNode> descendants = descendantNodes(node);
714     for (const ModelNode &descendantNode : descendants)
715         selectedList.removeAll(descendantNode);
716 
717     selectedList.removeAll(node);
718 
719     node.view()->setSelectedModelNodes(selectedList);
720 }
721 
722 /*! \brief complete removes this ModelNode from the Model
723 */
destroy()724 void ModelNode::destroy()
725 {
726     if (!isValid()) {
727         Q_ASSERT_X(isValid(), Q_FUNC_INFO, "model node is invalid");
728         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
729     }
730 
731     if (isRootNode())
732         throw InvalidArgumentException(__LINE__, __FUNCTION__, __FILE__, "rootNode");
733 
734     removeModelNodeFromSelection(*this);
735     model()->d->removeNode(internalNode());
736 }
737 //\}
738 
739 /*! \name Property Manipulation
740  *  This functions interact with properties.
741  */
742 
743 
744 /*!
745   \brief Returns if the two nodes reference the same entity in the same model
746   */
operator ==(const ModelNode & firstNode,const ModelNode & secondNode)747 bool operator ==(const ModelNode &firstNode, const ModelNode &secondNode)
748 {
749     return firstNode.internalId() == secondNode.internalId();
750 }
751 
752 /*!
753   \brief Returns if the two nodes do not reference the same entity in the same model
754   */
operator !=(const ModelNode & firstNode,const ModelNode & secondNode)755 bool operator !=(const ModelNode &firstNode, const ModelNode &secondNode)
756 {
757     return firstNode.internalId() != secondNode.internalId();
758 }
759 
operator <(const ModelNode & firstNode,const ModelNode & secondNode)760 bool operator <(const ModelNode &firstNode, const ModelNode &secondNode)
761 {
762     return firstNode.internalId() < secondNode.internalId();
763 }
764 
765 
internalNode() const766 Internal::InternalNodePointer ModelNode::internalNode() const
767 {
768     return m_internalNode;
769 }
770 
771 
qHash(const ModelNode & node)772 uint qHash(const ModelNode &node)
773 {
774     return ::qHash(node.internalId());
775 }
776 
777 /*!
778 \brief returns the model of the node
779 \return returns the model of the node
780 */
model() const781 Model *ModelNode::model() const
782 {
783     return m_model.data();
784 }
785 
786 /*!
787 \brief returns the view of the node
788 Each ModelNode belongs to one specific view.
789 \return view of the node
790 */
view() const791 AbstractView *ModelNode::view() const
792 {
793     return m_view.data();
794 }
795 
796 
797 /*!
798 \brief returns all ModelNodes that are direct children of this ModelNode
799 The list contains every ModelNode that belongs to one of this ModelNodes
800 properties.
801 \return a list of all ModelNodes that are direct children
802 */
directSubModelNodes() const803 QList<ModelNode> ModelNode::directSubModelNodes() const
804 {
805     return toModelNodeList(internalNode()->allDirectSubNodes(), view());
806 }
807 
directSubModelNodesOfType(const TypeName & typeName) const808 QList<ModelNode> ModelNode::directSubModelNodesOfType(const TypeName &typeName) const
809 {
810     return Utils::filtered(directSubModelNodes(), [typeName](const ModelNode &node){
811         return node.metaInfo().isValid() && node.metaInfo().isSubclassOf(typeName);
812     });
813 }
814 
subModelNodesOfType(const TypeName & typeName) const815 QList<ModelNode> ModelNode::subModelNodesOfType(const TypeName &typeName) const
816 {
817     return Utils::filtered(allSubModelNodes(), [typeName](const ModelNode &node){
818         return node.metaInfo().isValid() && node.metaInfo().isSubclassOf(typeName);
819     });
820 }
821 
822 /*!
823 \brief returns all ModelNodes that are direct or indirect children of this ModelNode
824 The list contains every ModelNode that is a direct or indirect child of this ModelNode.
825 All children in this list will be implicitly removed if this ModelNode is destroyed.
826 \return a list of all ModelNodes that are direct or indirect children
827 */
828 
allSubModelNodes() const829 QList<ModelNode> ModelNode::allSubModelNodes() const
830 {
831     return toModelNodeList(internalNode()->allSubNodes(), view());
832 }
833 
allSubModelNodesAndThisNode() const834 QList<ModelNode> ModelNode::allSubModelNodesAndThisNode() const
835 {
836     QList<ModelNode> modelNodeList;
837     modelNodeList.append(*this);
838     modelNodeList.append(allSubModelNodes());
839 
840     return modelNodeList;
841 }
842 
843 /*!
844 \brief returns if this ModelNode has any child ModelNodes.
845 
846 \return if this ModelNode has any child ModelNodes
847 */
848 
hasAnySubModelNodes() const849 bool ModelNode::hasAnySubModelNodes() const
850 {
851     return !nodeAbstractProperties().isEmpty();
852 }
853 
metaInfo() const854 const NodeMetaInfo ModelNode::metaInfo() const
855 {
856     if (!isValid()) {
857         Q_ASSERT_X(isValid(), Q_FUNC_INFO, "model node is invalid");
858         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
859     }
860 
861     return NodeMetaInfo(model()->metaInfoProxyModel(), type(), majorVersion(), minorVersion());
862 }
863 
hasMetaInfo() const864 bool ModelNode::hasMetaInfo() const
865 {
866     if (!isValid()) {
867         Q_ASSERT_X(isValid(), Q_FUNC_INFO, "model node is invalid");
868         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
869     }
870 
871     return model()->hasNodeMetaInfo(type(), majorVersion(), minorVersion());
872 }
873 
874 /*! \brief has a node the selection of the model
875 \return true if the node his selection
876 */
isSelected() const877 bool ModelNode::isSelected() const
878 {
879     if (!isValid()) {
880         Q_ASSERT_X(isValid(), Q_FUNC_INFO, "model node is invalid");
881         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
882     }
883     return view()->selectedModelNodes().contains(ModelNode(m_internalNode, m_model.data(), view()));
884 }
885 
886 /*! \briefis this node the root node of the model
887 \return true if it is the root node
888 */
isRootNode() const889 bool ModelNode::isRootNode() const
890 {
891     if (!isValid())
892         return false;
893 
894     return view()->rootModelNode() == *this;
895 }
896 
897 /*! \brief returns the list of all property names
898 \return list of all property names set in this state.
899 
900 The list of properties set in this state.
901 
902 \see addProperty property changePropertyValue removeProperty hasProperties
903 */
propertyNames() const904 PropertyNameList ModelNode::propertyNames() const
905 {
906     if (!isValid())
907         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
908     return internalNode()->propertyNameList();
909 }
910 
911 /*! \brief test a if a property is set for this node
912 \return true if property a property ins this or a ancestor state exists
913 */
hasProperty(const PropertyName & name) const914 bool ModelNode::hasProperty(const PropertyName &name) const
915 {
916     if (!isValid())
917         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
918 
919     return internalNode()->hasProperty(name);
920 }
921 
hasVariantProperty(const PropertyName & name) const922 bool ModelNode::hasVariantProperty(const PropertyName &name) const
923 {
924     return hasProperty(name) && internalNode()->property(name)->isVariantProperty();
925 }
926 
hasBindingProperty(const PropertyName & name) const927 bool ModelNode::hasBindingProperty(const PropertyName &name) const
928 {
929     return hasProperty(name) && internalNode()->property(name)->isBindingProperty();
930 }
931 
hasNodeAbstractProperty(const PropertyName & name) const932 bool ModelNode::hasNodeAbstractProperty(const PropertyName &name) const
933 {
934     return hasProperty(name) && internalNode()->property(name)->isNodeAbstractProperty();
935 }
936 
hasDefaultNodeAbstractProperty() const937 bool ModelNode::hasDefaultNodeAbstractProperty() const
938 {
939     return hasProperty(metaInfo().defaultPropertyName()) && internalNode()->property(metaInfo().defaultPropertyName())->isNodeAbstractProperty();
940 }
941 
hasDefaultNodeListProperty() const942 bool ModelNode::hasDefaultNodeListProperty() const
943 {
944     return hasProperty(metaInfo().defaultPropertyName()) && internalNode()->property(metaInfo().defaultPropertyName())->isNodeListProperty();
945 }
946 
hasDefaultNodeProperty() const947 bool ModelNode::hasDefaultNodeProperty() const
948 {
949     return hasProperty(metaInfo().defaultPropertyName()) && internalNode()->property(metaInfo().defaultPropertyName())->isNodeProperty();
950 }
951 
hasNodeProperty(const PropertyName & name) const952 bool ModelNode::hasNodeProperty(const PropertyName &name) const
953 {
954     return hasProperty(name) && internalNode()->property(name)->isNodeProperty();
955 }
956 
hasNodeListProperty(const PropertyName & name) const957 bool ModelNode::hasNodeListProperty(const PropertyName &name) const
958 {
959     return hasProperty(name) && internalNode()->property(name)->isNodeListProperty();
960 }
961 
recursiveAncestor(const ModelNode & possibleAncestor,const ModelNode & node)962 static bool recursiveAncestor(const ModelNode &possibleAncestor, const ModelNode &node)
963 {
964     if (!node.isValid())
965         return false;
966 
967     if (node.hasParentProperty()) {
968         if (node.parentProperty().parentModelNode() == possibleAncestor)
969            return true;
970         return recursiveAncestor(possibleAncestor, node.parentProperty().parentModelNode());
971     }
972 
973     return false;
974 }
975 
isAncestorOf(const ModelNode & node) const976 bool ModelNode::isAncestorOf(const ModelNode &node) const
977 {
978     return recursiveAncestor(*this, node);
979 }
980 
operator <<(QDebug debug,const ModelNode & modelNode)981 QDebug operator<<(QDebug debug, const ModelNode &modelNode)
982 {
983     if (modelNode.isValid()) {
984         debug.nospace() << "ModelNode("
985                 << modelNode.internalId() << ", "
986                 << modelNode.type() << ", "
987                 << modelNode.id() << ')';
988     } else {
989         debug.nospace() << "ModelNode(invalid)";
990     }
991 
992     return debug.space();
993 }
994 
operator <<(QTextStream & stream,const ModelNode & modelNode)995 QTextStream& operator<<(QTextStream &stream, const ModelNode &modelNode)
996 {
997     if (modelNode.isValid()) {
998         stream << "ModelNode("
999                 << "type: " << modelNode.type() << ", "
1000                 << "id: " << modelNode.id() << ')';
1001     } else {
1002         stream << "ModelNode(invalid)";
1003     }
1004 
1005     return stream;
1006 }
1007 
selectNode()1008 void ModelNode::selectNode()
1009 {
1010     if (!isValid())
1011             throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
1012 
1013     QList<ModelNode> selectedNodeList;
1014     selectedNodeList.append(*this);
1015 
1016     view()->setSelectedModelNodes(selectedNodeList);
1017 }
1018 
deselectNode()1019 void ModelNode::deselectNode()
1020 {
1021     if (!isValid())
1022             throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
1023 
1024     QList<ModelNode> selectedNodeList(view()->selectedModelNodes());
1025     selectedNodeList.removeAll(*this);
1026 
1027     view()->setSelectedModelNodes(selectedNodeList);
1028 }
1029 
variantUserType()1030 int ModelNode::variantUserType()
1031 {
1032     return qMetaTypeId<ModelNode>();
1033 }
1034 
toVariant() const1035 QVariant ModelNode::toVariant() const
1036 {
1037     return QVariant::fromValue(*this);
1038 }
1039 
auxiliaryData(const PropertyName & name) const1040 QVariant ModelNode::auxiliaryData(const PropertyName &name) const
1041 {
1042     if (!isValid())
1043         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
1044 
1045     return internalNode()->auxiliaryData(name);
1046 }
1047 
setAuxiliaryData(const PropertyName & name,const QVariant & data) const1048 void ModelNode::setAuxiliaryData(const PropertyName &name, const QVariant &data) const
1049 {
1050     Internal::WriteLocker locker(m_model.data());
1051     m_model.data()->d->setAuxiliaryData(internalNode(), name, data);
1052 }
1053 
removeAuxiliaryData(const PropertyName & name) const1054 void ModelNode::removeAuxiliaryData(const PropertyName &name) const
1055 {
1056     Internal::WriteLocker locker(m_model.data());
1057     m_model.data()->d->removeAuxiliaryData(internalNode(), name);
1058 }
1059 
hasAuxiliaryData(const PropertyName & name) const1060 bool ModelNode::hasAuxiliaryData(const PropertyName &name) const
1061 {
1062     if (!isValid())
1063         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
1064 
1065     return internalNode()->hasAuxiliaryData(name);
1066 }
1067 
auxiliaryData() const1068 const QHash<PropertyName, QVariant> &ModelNode::auxiliaryData() const
1069 {
1070     if (!isValid())
1071         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
1072 
1073     return internalNode()->auxiliaryData();
1074 }
1075 
customId() const1076 QString ModelNode::customId() const
1077 {
1078     QString result;
1079     if (hasCustomId())
1080         result = auxiliaryData(customIdProperty).value<QString>();
1081 
1082     return result;
1083 }
1084 
hasCustomId() const1085 bool ModelNode::hasCustomId() const
1086 {
1087     return hasAuxiliaryData(customIdProperty);
1088 }
1089 
setCustomId(const QString & str)1090 void ModelNode::setCustomId(const QString &str)
1091 {
1092     setAuxiliaryData(customIdProperty, QVariant::fromValue<QString>(str));
1093 }
1094 
removeCustomId()1095 void ModelNode::removeCustomId()
1096 {
1097     if (hasCustomId()) {
1098         removeAuxiliaryData(customIdProperty);
1099     }
1100 }
1101 
comments() const1102 QVector<Comment> ModelNode::comments() const
1103 {
1104     return annotation().comments();
1105 }
1106 
hasComments() const1107 bool ModelNode::hasComments() const
1108 {
1109     return annotation().hasComments();
1110 }
1111 
setComments(const QVector<Comment> & coms)1112 void ModelNode::setComments(const QVector<Comment> &coms)
1113 {
1114     Annotation anno = annotation();
1115     anno.setComments(coms);
1116 
1117     setAnnotation(anno);
1118 }
1119 
addComment(const Comment & com)1120 void ModelNode::addComment(const Comment &com)
1121 {
1122     Annotation anno = annotation();
1123     anno.addComment(com);
1124 
1125     setAnnotation(anno);
1126 }
1127 
updateComment(const Comment & com,int position)1128 bool ModelNode::updateComment(const Comment &com, int position)
1129 {
1130     bool result = false;
1131     if (hasAnnotation()) {
1132         Annotation anno = annotation();
1133 
1134         if (anno.updateComment(com, position)) {
1135             setAnnotation(anno);
1136             result = true;
1137         }
1138     }
1139 
1140     return result;
1141 }
1142 
annotation() const1143 Annotation ModelNode::annotation() const
1144 {
1145     Annotation result;
1146 
1147     if (hasAnnotation())
1148         result.fromQString(auxiliaryData(annotationProperty).value<QString>());
1149 
1150     return result;
1151 }
1152 
hasAnnotation() const1153 bool ModelNode::hasAnnotation() const
1154 {
1155     return hasAuxiliaryData(annotationProperty);
1156 }
1157 
setAnnotation(const Annotation & annotation)1158 void ModelNode::setAnnotation(const Annotation &annotation)
1159 {
1160     setAuxiliaryData(annotationProperty, QVariant::fromValue<QString>(annotation.toQString()));
1161 }
1162 
removeAnnotation()1163 void ModelNode::removeAnnotation()
1164 {
1165     if (hasAnnotation()) {
1166         removeAuxiliaryData(annotationProperty);
1167     }
1168 }
1169 
globalAnnotation() const1170 Annotation ModelNode::globalAnnotation() const
1171 {
1172     Annotation result;
1173     ModelNode root = view()->rootModelNode();
1174 
1175     if (hasGlobalAnnotation())
1176         result.fromQString(root.auxiliaryData(globalAnnotationProperty).value<QString>());
1177 
1178     return result;
1179 }
1180 
hasGlobalAnnotation() const1181 bool ModelNode::hasGlobalAnnotation() const
1182 {
1183     return view()->rootModelNode().hasAuxiliaryData(globalAnnotationProperty);
1184 }
1185 
setGlobalAnnotation(const Annotation & annotation)1186 void ModelNode::setGlobalAnnotation(const Annotation &annotation)
1187 {
1188     view()->rootModelNode().setAuxiliaryData(globalAnnotationProperty,
1189                                              QVariant::fromValue<QString>(annotation.toQString()));
1190 }
1191 
removeGlobalAnnotation()1192 void ModelNode::removeGlobalAnnotation()
1193 {
1194     if (hasGlobalAnnotation()) {
1195         view()->rootModelNode().removeAuxiliaryData(globalAnnotationProperty);
1196     }
1197 }
1198 
globalStatus() const1199 GlobalAnnotationStatus ModelNode::globalStatus() const
1200 {
1201     GlobalAnnotationStatus result;
1202     ModelNode root = view()->rootModelNode();
1203 
1204     if (hasGlobalAnnotation()) {
1205         result.fromQString(root.auxiliaryData(globalAnnotationStatus).value<QString>());
1206     }
1207 
1208     return result;
1209 }
1210 
hasGlobalStatus() const1211 bool ModelNode::hasGlobalStatus() const
1212 {
1213     return view()->rootModelNode().hasAuxiliaryData(globalAnnotationStatus);
1214 }
1215 
setGlobalStatus(const GlobalAnnotationStatus & status)1216 void ModelNode::setGlobalStatus(const GlobalAnnotationStatus &status)
1217 {
1218     view()->rootModelNode().setAuxiliaryData(globalAnnotationStatus,
1219                                              QVariant::fromValue<QString>(status.toQString()));
1220 }
1221 
removeGlobalStatus()1222 void ModelNode::removeGlobalStatus()
1223 {
1224     if (hasGlobalStatus()) {
1225         view()->rootModelNode().removeAuxiliaryData(globalAnnotationStatus);
1226     }
1227 }
1228 
locked() const1229 bool ModelNode::locked() const
1230 {
1231     if (hasLocked())
1232         return auxiliaryData(lockedProperty).toBool();
1233 
1234     return false;
1235 }
1236 
hasLocked() const1237 bool ModelNode::hasLocked() const
1238 {
1239     return hasAuxiliaryData(lockedProperty);
1240 }
1241 
setLocked(bool value)1242 void ModelNode::setLocked(bool value)
1243 {
1244     if (value) {
1245         setAuxiliaryData(lockedProperty, true);
1246         // Remove newly locked node and all its descendants from potential selection
1247         for (ModelNode node : allSubModelNodesAndThisNode()) {
1248             node.deselectNode();
1249             node.removeAuxiliaryData("timeline_expanded");
1250             node.removeAuxiliaryData("transition_expanded");
1251         }
1252     } else {
1253         removeAuxiliaryData(lockedProperty);
1254     }
1255 }
1256 
isThisOrAncestorLocked(const ModelNode & node)1257 bool ModelNode::isThisOrAncestorLocked(const ModelNode &node)
1258 {
1259     if (!node.isValid())
1260         return false;
1261 
1262     if (node.locked())
1263         return true;
1264 
1265     if (node.isRootNode() || !node.hasParentProperty())
1266         return false;
1267 
1268     return isThisOrAncestorLocked(node.parentProperty().parentModelNode());
1269 }
1270 
setScriptFunctions(const QStringList & scriptFunctionList)1271 void  ModelNode::setScriptFunctions(const QStringList &scriptFunctionList)
1272 {
1273     model()->d->setScriptFunctions(internalNode(), scriptFunctionList);
1274 }
1275 
scriptFunctions() const1276 QStringList  ModelNode::scriptFunctions() const
1277 {
1278     return internalNode()->scriptFunctions();
1279 }
1280 
internalId() const1281 qint32 ModelNode::internalId() const
1282 {
1283     if (m_internalNode.isNull())
1284         return -1;
1285 
1286     return m_internalNode->internalId();
1287 }
1288 
setNodeSource(const QString & newNodeSource)1289 void ModelNode::setNodeSource(const QString &newNodeSource)
1290 {
1291     Internal::WriteLocker locker(m_model.data());
1292 
1293     if (!isValid()) {
1294         Q_ASSERT_X(isValid(), Q_FUNC_INFO, "model node is invalid");
1295         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
1296     }
1297 
1298     if (internalNode()->nodeSource() == newNodeSource)
1299         return;
1300 
1301     m_model.data()->d->setNodeSource(internalNode(), newNodeSource);
1302 }
1303 
setNodeSource(const QString & newNodeSource,NodeSourceType type)1304 void ModelNode::setNodeSource(const QString &newNodeSource, NodeSourceType type)
1305 {
1306     Internal::WriteLocker locker(m_model.data());
1307 
1308     if (!isValid()) {
1309         Q_ASSERT_X(isValid(), Q_FUNC_INFO, "model node is invalid");
1310         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
1311     }
1312 
1313     if (internalNode()->nodeSourceType() == type && internalNode()->nodeSource() == newNodeSource)
1314         return;
1315 
1316     internalNode()->setNodeSourceType(type); // Set type first as it doesn't trigger any notifies
1317     m_model.data()->d->setNodeSource(internalNode(), newNodeSource);
1318 }
1319 
nodeSource() const1320 QString ModelNode::nodeSource() const
1321 {
1322     if (!isValid())
1323         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
1324 
1325     return internalNode()->nodeSource();
1326 }
1327 
convertTypeToImportAlias() const1328 QString ModelNode::convertTypeToImportAlias() const
1329 {
1330     if (!isValid())
1331         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
1332 
1333     if (model()->rewriterView())
1334         return model()->rewriterView()->convertTypeToImportAlias(QString::fromLatin1(type()));
1335 
1336     return QString::fromLatin1(type());
1337 }
1338 
nodeSourceType() const1339 ModelNode::NodeSourceType ModelNode::nodeSourceType() const
1340 {
1341     if (!isValid())
1342         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
1343 
1344     return static_cast<ModelNode::NodeSourceType>(internalNode()->nodeSourceType());
1345 
1346 }
1347 
isComponent() const1348 bool ModelNode::isComponent() const
1349 {
1350     if (!isValid())
1351         throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
1352 
1353     if (!metaInfo().isValid())
1354         return false;
1355 
1356     if (metaInfo().isFileComponent())
1357         return true;
1358 
1359     if (nodeSourceType() == ModelNode::NodeWithComponentSource)
1360         return true;
1361 
1362     if (metaInfo().isView() && hasNodeProperty("delegate")) {
1363         const ModelNode delegateNode = nodeProperty("delegate").modelNode();
1364         if (delegateNode.isValid()) {
1365             if (delegateNode.hasMetaInfo()) {
1366                 const NodeMetaInfo delegateMetaInfo = delegateNode.metaInfo();
1367                 if (delegateMetaInfo.isValid() && delegateMetaInfo.isFileComponent())
1368                     return true;
1369             }
1370             if (delegateNode.nodeSourceType() == ModelNode::NodeWithComponentSource)
1371                 return true;
1372         }
1373     }
1374 
1375     if (metaInfo().isSubclassOf("QtQuick.Loader")) {
1376 
1377         if (hasNodeListProperty("component")) {
1378 
1379         /*
1380          * The component property should be a NodeProperty, but currently is a NodeListProperty, because
1381          * the default property is always implcitly a NodeListProperty. This is something that has to be fixed.
1382          */
1383 
1384             ModelNode componentNode = nodeListProperty("component").toModelNodeList().constFirst();
1385             if (componentNode.nodeSourceType() == ModelNode::NodeWithComponentSource)
1386                 return true;
1387             if (componentNode.metaInfo().isFileComponent())
1388                 return true;
1389         }
1390 
1391         if (hasNodeProperty("sourceComponent")) {
1392             if (nodeProperty("sourceComponent").modelNode().nodeSourceType() == ModelNode::NodeWithComponentSource)
1393                 return true;
1394             if (nodeProperty("sourceComponent").modelNode().metaInfo().isFileComponent())
1395                 return true;
1396         }
1397 
1398         if (hasVariantProperty("source"))
1399             return true;
1400     }
1401 
1402     return false;
1403 }
1404 
isSubclassOf(const TypeName & typeName,int majorVersion,int minorVersion) const1405 bool ModelNode::isSubclassOf(const TypeName &typeName, int majorVersion, int minorVersion) const
1406 {
1407     if (metaInfo().isValid())
1408         return metaInfo().isSubclassOf(typeName, majorVersion, minorVersion);
1409 
1410     return false;
1411 }
1412 
typeIcon() const1413 QIcon ModelNode::typeIcon() const
1414 {
1415     if (isValid()) {
1416         // if node has no own icon, search for it in the itemlibrary
1417         const ItemLibraryInfo *libraryInfo = model()->metaInfo().itemLibraryInfo();
1418         QList <ItemLibraryEntry> itemLibraryEntryList = libraryInfo->entriesForType(
1419                     type(), majorVersion(), minorVersion());
1420         if (!itemLibraryEntryList.isEmpty())
1421             return itemLibraryEntryList.constFirst().typeIcon();
1422         else if (metaInfo().isValid())
1423             return QIcon(QStringLiteral(":/ItemLibrary/images/item-default-icon.png"));
1424     }
1425 
1426     return QIcon(QStringLiteral(":/ItemLibrary/images/item-invalid-icon.png"));
1427 }
1428 
1429 }
1430