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