/**************************************************************************** | |
** | |
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). | |
** All rights reserved. | |
** Contact: Nokia Corporation (qt-info@nokia.com) | |
** | |
** This file is part of the tools applications of the Qt Toolkit. | |
** | |
** $QT_BEGIN_LICENSE:LGPL$ | |
** GNU Lesser General Public License Usage | |
** This file may be used under the terms of the GNU Lesser General Public | |
** License version 2.1 as published by the Free Software Foundation and | |
** appearing in the file LICENSE.LGPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU Lesser | |
** General Public License version 2.1 requirements will be met: | |
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. | |
** | |
** In addition, as a special exception, Nokia gives you certain additional | |
** rights. These rights are described in the Nokia Qt LGPL Exception | |
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. | |
** | |
** GNU General Public License Usage | |
** Alternatively, this file may be used under the terms of the GNU General | |
** Public License version 3.0 as published by the Free Software Foundation | |
** and appearing in the file LICENSE.GPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU General | |
** Public License version 3.0 requirements will be met: | |
** http://www.gnu.org/copyleft/gpl.html. | |
** | |
** Other Usage | |
** Alternatively, this file may be used in accordance with the terms and | |
** conditions contained in a signed written agreement between you and Nokia. | |
** | |
** | |
** | |
** | |
** | |
** $QT_END_LICENSE$ | |
** | |
****************************************************************************/ | |
/* | |
node.cpp | |
*/ | |
#include "node.h" | |
#include "tree.h" | |
#include "codemarker.h" | |
#include <QUuid> | |
#include <qdebug.h> | |
QT_BEGIN_NAMESPACE | |
/*! | |
\class Node | |
\brief The Node class is a node in the Tree. | |
A Node represents a class or function or something else | |
from the source code.. | |
*/ | |
/*! | |
When this Node is destroyed, if it has a parent Node, it | |
removes itself from the parent node's child list. | |
*/ | |
Node::~Node() | |
{ | |
if (par) | |
par->removeChild(this); | |
if (rel) | |
rel->removeRelated(this); | |
} | |
/*! | |
Sets this Node's Doc to \a doc. If \a replace is false and | |
this Node already has a Doc, a warning is reported that the | |
Doc is being overridden, and it reports where the previous | |
Doc was found. If \a replace is true, the Doc is replaced | |
silently. | |
*/ | |
void Node::setDoc(const Doc& doc, bool replace) | |
{ | |
if (!d.isEmpty() && !replace) { | |
doc.location().warning(tr("Overrides a previous doc")); | |
d.location().warning(tr("(The previous doc is here)")); | |
} | |
d = doc; | |
} | |
/*! | |
Construct a node with the given \a type and having the | |
given \a parent and \a name. The new node is added to the | |
parent's child list. | |
*/ | |
Node::Node(Type type, InnerNode *parent, const QString& name) | |
: typ(type), | |
acc(Public), | |
saf(UnspecifiedSafeness), | |
pageTyp(NoPageType), | |
sta(Commendable), | |
par(parent), | |
rel(0), | |
nam(name) | |
{ | |
if (par) | |
par->addChild(this); | |
} | |
/*! | |
Returns the node's URL. | |
*/ | |
QString Node::url() const | |
{ | |
return u; | |
} | |
/*! | |
Sets the node's URL to \a url | |
*/ | |
void Node::setUrl(const QString &url) | |
{ | |
u = url; | |
} | |
void Node::setPageType(const QString& t) | |
{ | |
if ((t == "API") || (t == "api")) | |
pageTyp = ApiPage; | |
else if (t == "article") | |
pageTyp = ArticlePage; | |
else if (t == "example") | |
pageTyp = ExamplePage; | |
} | |
/*! | |
Sets the pointer to the node that this node relates to. | |
*/ | |
void Node::setRelates(InnerNode *pseudoParent) | |
{ | |
if (rel) | |
rel->removeRelated(this); | |
rel = pseudoParent; | |
pseudoParent->related.append(this); | |
} | |
/*! | |
This function creates a pair that describes a link. | |
The pair is composed from \a link and \a desc. The | |
\a linkType is the map index the pair is filed under. | |
*/ | |
void Node::setLink(LinkType linkType, const QString &link, const QString &desc) | |
{ | |
QPair<QString,QString> linkPair; | |
linkPair.first = link; | |
linkPair.second = desc; | |
linkMap[linkType] = linkPair; | |
} | |
/*! | |
Sets the information about the project and version a node was introduced | |
in. The string is simplified, removing excess whitespace before being | |
stored. | |
*/ | |
void Node::setSince(const QString &since) | |
{ | |
sinc = since.simplified(); | |
} | |
/*! | |
Returns a string representing the access specifier. | |
*/ | |
QString Node::accessString() const | |
{ | |
switch (acc) { | |
case Protected: | |
return "protected"; | |
case Private: | |
return "private"; | |
case Public: | |
default: | |
break; | |
} | |
return "public"; | |
} | |
/*! | |
Extract a class name from the type \a string and return it. | |
*/ | |
QString Node::extractClassName(const QString &string) const | |
{ | |
QString result; | |
for (int i=0; i<=string.size(); ++i) { | |
QChar ch; | |
if (i != string.size()) | |
ch = string.at(i); | |
QChar lower = ch.toLower(); | |
if ((lower >= QLatin1Char('a') && lower <= QLatin1Char('z')) || | |
ch.digitValue() >= 0 || | |
ch == QLatin1Char('_') || | |
ch == QLatin1Char(':')) { | |
result += ch; | |
} | |
else if (!result.isEmpty()) { | |
if (result != QLatin1String("const")) | |
return result; | |
result.clear(); | |
} | |
} | |
return result; | |
} | |
/*! | |
Returns a string representing the access specifier. | |
*/ | |
QString RelatedClass::accessString() const | |
{ | |
switch (access) { | |
case Node::Protected: | |
return "protected"; | |
case Node::Private: | |
return "private"; | |
case Node::Public: | |
default: | |
break; | |
} | |
return "public"; | |
} | |
/*! | |
*/ | |
Node::Status Node::inheritedStatus() const | |
{ | |
Status parentStatus = Commendable; | |
if (par) | |
parentStatus = par->inheritedStatus(); | |
return (Status)qMin((int)sta, (int)parentStatus); | |
} | |
/*! | |
Returns the thread safeness value for whatever this node | |
represents. But if this node has a parent and the thread | |
safeness value of the parent is the same as the thread | |
safeness value of this node, what is returned is the | |
value \c{UnspecifiedSafeness}. Why? | |
*/ | |
Node::ThreadSafeness Node::threadSafeness() const | |
{ | |
if (par && saf == par->inheritedThreadSafeness()) | |
return UnspecifiedSafeness; | |
return saf; | |
} | |
/*! | |
If this node has a parent, the parent's thread safeness | |
value is returned. Otherwise, this node's thread safeness | |
value is returned. Why? | |
*/ | |
Node::ThreadSafeness Node::inheritedThreadSafeness() const | |
{ | |
if (par && saf == UnspecifiedSafeness) | |
return par->inheritedThreadSafeness(); | |
return saf; | |
} | |
/*! | |
Returns the sanitized file name without the path. | |
If the the file is an html file, the html suffix | |
is removed. Why? | |
*/ | |
QString Node::fileBase() const | |
{ | |
QString base = name(); | |
if (base.endsWith(".html")) | |
base.chop(5); | |
base.replace(QRegExp("[^A-Za-z0-9]+"), " "); | |
base = base.trimmed(); | |
base.replace(" ", "-"); | |
return base.toLower(); | |
} | |
/*! | |
Returns this node's Universally Unique IDentifier as a | |
QString. Creates the UUID first, if it has not been created. | |
*/ | |
QString Node::guid() const | |
{ | |
if (uuid.isEmpty()) { | |
QUuid quuid = QUuid::createUuid(); | |
QString t = quuid.toString(); | |
uuid = "id-" + t.mid(1,t.length()-2); | |
} | |
return uuid; | |
} | |
/*! | |
Composes a string to be used as an href attribute in DITA | |
XML. It is composed of the file name and the UUID separated | |
by a '#'. If this node is a class node, the file name is | |
taken from this node; if this node is a function node, the | |
file name is taken from the parent node of this node. | |
*/ | |
QString Node::ditaXmlHref() | |
{ | |
QString href; | |
if ((type() == Function) || | |
(type() == Property) || | |
(type() == Variable)) { | |
href = parent()->fileBase(); | |
} | |
else { | |
href = fileBase(); | |
} | |
if (!href.endsWith(".xml")) | |
href += ".xml"; | |
return href + "#" + guid(); | |
} | |
/*! | |
\class InnerNode | |
*/ | |
/*! | |
The inner node destructor deletes the children and removes | |
this node from its related nodes. | |
*/ | |
InnerNode::~InnerNode() | |
{ | |
deleteChildren(); | |
removeFromRelated(); | |
} | |
/*! | |
Find the node in this node's children that has the | |
given \a name. If this node is a QML class node, be | |
sure to also look in the children of its property | |
group nodes. Return the matching node or 0. | |
*/ | |
Node *InnerNode::findNode(const QString& name) | |
{ | |
Node *node = childMap.value(name); | |
if (node && node->subType() != QmlPropertyGroup) | |
return node; | |
if ((type() == Fake) && (subType() == QmlClass)) { | |
for (int i=0; i<children.size(); ++i) { | |
Node* n = children.at(i); | |
if (n->subType() == QmlPropertyGroup) { | |
node = static_cast<InnerNode*>(n)->findNode(name); | |
if (node) | |
return node; | |
} | |
} | |
} | |
return primaryFunctionMap.value(name); | |
} | |
/*! | |
Same as the other findNode(), but if the node with the | |
specified \a name is not of the specified \a type, return | |
0. | |
*/ | |
Node *InnerNode::findNode(const QString& name, Type type) | |
{ | |
if (type == Function) { | |
return primaryFunctionMap.value(name); | |
} | |
else { | |
Node *node = childMap.value(name); | |
if (node && node->type() == type) { | |
return node; | |
} | |
else { | |
return 0; | |
} | |
} | |
} | |
/*! | |
Find the function node in this node for the function named \a name. | |
*/ | |
FunctionNode *InnerNode::findFunctionNode(const QString& name) | |
{ | |
return static_cast<FunctionNode *>(primaryFunctionMap.value(name)); | |
} | |
/*! | |
Find the function node in this node that has the same name as \a clone. | |
*/ | |
FunctionNode *InnerNode::findFunctionNode(const FunctionNode *clone) | |
{ | |
QMap<QString, Node *>::ConstIterator c = | |
primaryFunctionMap.find(clone->name()); | |
if (c != primaryFunctionMap.end()) { | |
if (isSameSignature(clone, (FunctionNode *) *c)) { | |
return (FunctionNode *) *c; | |
} | |
else if (secondaryFunctionMap.contains(clone->name())) { | |
const NodeList& secs = secondaryFunctionMap[clone->name()]; | |
NodeList::ConstIterator s = secs.begin(); | |
while (s != secs.end()) { | |
if (isSameSignature(clone, (FunctionNode *) *s)) | |
return (FunctionNode *) *s; | |
++s; | |
} | |
} | |
} | |
return 0; | |
} | |
/*! | |
Returns the list of keys from the primary function map. | |
*/ | |
QStringList InnerNode::primaryKeys() | |
{ | |
QStringList t; | |
QMap<QString, Node*>::iterator i = primaryFunctionMap.begin(); | |
while (i != primaryFunctionMap.end()) { | |
t.append(i.key()); | |
++i; | |
} | |
return t; | |
} | |
/*! | |
Returns the list of keys from the secondary function map. | |
*/ | |
QStringList InnerNode::secondaryKeys() | |
{ | |
QStringList t; | |
QMap<QString, NodeList>::iterator i = secondaryFunctionMap.begin(); | |
while (i != secondaryFunctionMap.end()) { | |
t.append(i.key()); | |
++i; | |
} | |
return t; | |
} | |
/*! | |
*/ | |
void InnerNode::setOverload(const FunctionNode *func, bool overlode) | |
{ | |
Node *node = (Node *) func; | |
Node *&primary = primaryFunctionMap[func->name()]; | |
if (secondaryFunctionMap.contains(func->name())) { | |
NodeList& secs = secondaryFunctionMap[func->name()]; | |
if (overlode) { | |
if (primary == node) { | |
primary = secs.first(); | |
secs.erase(secs.begin()); | |
secs.append(node); | |
} | |
else { | |
secs.removeAll(node); | |
secs.append(node); | |
} | |
} | |
else { | |
if (primary != node) { | |
secs.removeAll(node); | |
secs.prepend(primary); | |
primary = node; | |
} | |
} | |
} | |
} | |
/*! | |
Mark all child nodes that have no documentation as having | |
private access and internal status. qdoc will then ignore | |
them for documentation purposes. | |
*/ | |
void InnerNode::makeUndocumentedChildrenInternal() | |
{ | |
foreach (Node *child, childNodes()) { | |
if (child->doc().isEmpty()) { | |
child->setAccess(Node::Private); | |
child->setStatus(Node::Internal); | |
} | |
} | |
} | |
/*! | |
*/ | |
void InnerNode::normalizeOverloads() | |
{ | |
QMap<QString, Node *>::Iterator p1 = primaryFunctionMap.begin(); | |
while (p1 != primaryFunctionMap.end()) { | |
FunctionNode *primaryFunc = (FunctionNode *) *p1; | |
if (secondaryFunctionMap.contains(primaryFunc->name()) && | |
(primaryFunc->status() != Commendable || | |
primaryFunc->access() == Private)) { | |
NodeList& secs = secondaryFunctionMap[primaryFunc->name()]; | |
NodeList::ConstIterator s = secs.begin(); | |
while (s != secs.end()) { | |
FunctionNode *secondaryFunc = (FunctionNode *) *s; | |
// Any non-obsolete, non-compatibility, non-private functions | |
// (i.e, visible functions) are preferable to the primary | |
// function. | |
if (secondaryFunc->status() == Commendable && | |
secondaryFunc->access() != Private) { | |
*p1 = secondaryFunc; | |
int index = secondaryFunctionMap[primaryFunc->name()].indexOf(secondaryFunc); | |
secondaryFunctionMap[primaryFunc->name()].replace(index, primaryFunc); | |
break; | |
} | |
++s; | |
} | |
} | |
++p1; | |
} | |
QMap<QString, Node *>::ConstIterator p = primaryFunctionMap.begin(); | |
while (p != primaryFunctionMap.end()) { | |
FunctionNode *primaryFunc = (FunctionNode *) *p; | |
if (primaryFunc->isOverload()) | |
primaryFunc->ove = false; | |
if (secondaryFunctionMap.contains(primaryFunc->name())) { | |
NodeList& secs = secondaryFunctionMap[primaryFunc->name()]; | |
NodeList::ConstIterator s = secs.begin(); | |
while (s != secs.end()) { | |
FunctionNode *secondaryFunc = (FunctionNode *) *s; | |
if (!secondaryFunc->isOverload()) | |
secondaryFunc->ove = true; | |
++s; | |
} | |
} | |
++p; | |
} | |
NodeList::ConstIterator c = childNodes().begin(); | |
while (c != childNodes().end()) { | |
if ((*c)->isInnerNode()) | |
((InnerNode *) *c)->normalizeOverloads(); | |
++c; | |
} | |
} | |
/*! | |
*/ | |
void InnerNode::removeFromRelated() | |
{ | |
while (!related.isEmpty()) { | |
Node *p = static_cast<Node *>(related.takeFirst()); | |
if (p != 0 && p->relates() == this) p->clearRelated(); | |
} | |
} | |
/*! | |
*/ | |
void InnerNode::deleteChildren() | |
{ | |
NodeList childrenCopy = children; // `children` will be changed in ~Node() | |
qDeleteAll(childrenCopy); | |
} | |
/*! | |
Returns true because this is an inner node. | |
*/ | |
bool InnerNode::isInnerNode() const | |
{ | |
return true; | |
} | |
/*! | |
*/ | |
const Node *InnerNode::findNode(const QString& name) const | |
{ | |
InnerNode *that = (InnerNode *) this; | |
return that->findNode(name); | |
} | |
/*! | |
*/ | |
const Node *InnerNode::findNode(const QString& name, Type type) const | |
{ | |
InnerNode *that = (InnerNode *) this; | |
return that->findNode(name, type); | |
} | |
/*! | |
Find the function node in this node that has the given \a name. | |
*/ | |
const FunctionNode *InnerNode::findFunctionNode(const QString& name) const | |
{ | |
InnerNode *that = (InnerNode *) this; | |
return that->findFunctionNode(name); | |
} | |
/*! | |
Find the function node in this node that has the same name as \a clone. | |
*/ | |
const FunctionNode *InnerNode::findFunctionNode(const FunctionNode *clone) const | |
{ | |
InnerNode *that = (InnerNode *) this; | |
return that->findFunctionNode(clone); | |
} | |
/*! | |
*/ | |
const EnumNode *InnerNode::findEnumNodeForValue(const QString &enumValue) const | |
{ | |
foreach (const Node *node, enumChildren) { | |
const EnumNode *enume = static_cast<const EnumNode *>(node); | |
if (enume->hasItem(enumValue)) | |
return enume; | |
} | |
return 0; | |
} | |
/*! | |
Returnds the sequence number of the function node \a func | |
in the list of overloaded functions for a class, such that | |
all the functions have the same name as the \a func. | |
*/ | |
int InnerNode::overloadNumber(const FunctionNode *func) const | |
{ | |
Node *node = (Node *) func; | |
if (primaryFunctionMap[func->name()] == node) { | |
return 1; | |
} | |
else { | |
return secondaryFunctionMap[func->name()].indexOf(node) + 2; | |
} | |
} | |
/*! | |
Returns the number of member functions of a class such that | |
the functions are all named \a funcName. | |
*/ | |
int InnerNode::numOverloads(const QString& funcName) const | |
{ | |
if (primaryFunctionMap.contains(funcName)) { | |
return secondaryFunctionMap[funcName].count() + 1; | |
} | |
else { | |
return 0; | |
} | |
} | |
/*! | |
Returns a node list containing all the member functions of | |
some class such that the functions overload the name \a funcName. | |
*/ | |
NodeList InnerNode::overloads(const QString &funcName) const | |
{ | |
NodeList result; | |
Node *primary = primaryFunctionMap.value(funcName); | |
if (primary) { | |
result << primary; | |
result += secondaryFunctionMap[funcName]; | |
} | |
return result; | |
} | |
/*! | |
Construct an inner node (i.e., not a leaf node) of the | |
given \a type and having the given \a parent and \a name. | |
*/ | |
InnerNode::InnerNode(Type type, InnerNode *parent, const QString& name) | |
: Node(type, parent, name) | |
{ | |
switch (type) { | |
case Class: | |
case Namespace: | |
setPageType(ApiPage); | |
break; | |
default: | |
break; | |
} | |
} | |
/*! | |
Appends an \a include file to the list of include files. | |
*/ | |
void InnerNode::addInclude(const QString& include) | |
{ | |
inc.append(include); | |
} | |
/*! | |
Sets the list of include files to \a includes. | |
*/ | |
void InnerNode::setIncludes(const QStringList& includes) | |
{ | |
inc = includes; | |
} | |
/*! | |
f1 is always the clone | |
*/ | |
bool InnerNode::isSameSignature(const FunctionNode *f1, const FunctionNode *f2) | |
{ | |
if (f1->parameters().count() != f2->parameters().count()) | |
return false; | |
if (f1->isConst() != f2->isConst()) | |
return false; | |
QList<Parameter>::ConstIterator p1 = f1->parameters().begin(); | |
QList<Parameter>::ConstIterator p2 = f2->parameters().begin(); | |
while (p2 != f2->parameters().end()) { | |
if ((*p1).hasType() && (*p2).hasType()) { | |
if ((*p1).rightType() != (*p2).rightType()) | |
return false; | |
QString t1 = p1->leftType(); | |
QString t2 = p2->leftType(); | |
if (t1.length() < t2.length()) | |
qSwap(t1, t2); | |
/* | |
### hack for C++ to handle superfluous | |
"Foo::" prefixes gracefully | |
*/ | |
if (t1 != t2 && t1 != (f2->parent()->name() + "::" + t2)) | |
return false; | |
} | |
++p1; | |
++p2; | |
} | |
return true; | |
} | |
/*! | |
Adds the \a child to this node's child list. | |
*/ | |
void InnerNode::addChild(Node *child) | |
{ | |
children.append(child); | |
if ((child->type() == Function) || (child->type() == QmlMethod)) { | |
FunctionNode *func = (FunctionNode *) child; | |
if (!primaryFunctionMap.contains(func->name())) { | |
primaryFunctionMap.insert(func->name(), func); | |
} | |
else { | |
NodeList &secs = secondaryFunctionMap[func->name()]; | |
secs.append(func); | |
} | |
} | |
else { | |
if (child->type() == Enum) | |
enumChildren.append(child); | |
childMap.insert(child->name(), child); | |
} | |
} | |
/*! | |
*/ | |
void InnerNode::removeChild(Node *child) | |
{ | |
children.removeAll(child); | |
enumChildren.removeAll(child); | |
if (child->type() == Function) { | |
QMap<QString, Node *>::Iterator prim = | |
primaryFunctionMap.find(child->name()); | |
NodeList& secs = secondaryFunctionMap[child->name()]; | |
if (prim != primaryFunctionMap.end() && *prim == child) { | |
if (secs.isEmpty()) { | |
primaryFunctionMap.remove(child->name()); | |
} | |
else { | |
primaryFunctionMap.insert(child->name(), secs.takeFirst()); | |
} | |
} | |
else { | |
secs.removeAll(child); | |
} | |
QMap<QString, Node *>::Iterator ent = childMap.find( child->name() ); | |
if (ent != childMap.end() && *ent == child) | |
childMap.erase( ent ); | |
} | |
else { | |
QMap<QString, Node *>::Iterator ent = childMap.find(child->name()); | |
if (ent != childMap.end() && *ent == child) | |
childMap.erase(ent); | |
} | |
} | |
/*! | |
Find the module (QtCore, QtGui, etc.) to which the class belongs. | |
We do this by obtaining the full path to the header file's location | |
and examine everything between "src/" and the filename. This is | |
semi-dirty because we are assuming a particular directory structure. | |
This function is only really useful if the class's module has not | |
been defined in the header file with a QT_MODULE macro or with an | |
\inmodule command in the documentation. | |
*/ | |
QString Node::moduleName() const | |
{ | |
if (!mod.isEmpty()) | |
return mod; | |
QString path = location().filePath(); | |
QString pattern = QString("src") + QDir::separator(); | |
int start = path.lastIndexOf(pattern); | |
if (start == -1) | |
return ""; | |
QString moduleDir = path.mid(start + pattern.size()); | |
int finish = moduleDir.indexOf(QDir::separator()); | |
if (finish == -1) | |
return ""; | |
QString moduleName = moduleDir.left(finish); | |
if (moduleName == "corelib") | |
return "QtCore"; | |
else if (moduleName == "uitools") | |
return "QtUiTools"; | |
else if (moduleName == "gui") | |
return "QtGui"; | |
else if (moduleName == "network") | |
return "QtNetwork"; | |
else if (moduleName == "opengl") | |
return "QtOpenGL"; | |
else if (moduleName == "qt3support") | |
return "Qt3Support"; | |
else if (moduleName == "svg") | |
return "QtSvg"; | |
else if (moduleName == "sql") | |
return "QtSql"; | |
else if (moduleName == "qtestlib") | |
return "QtTest"; | |
else if (moduleDir.contains("webkit")) | |
return "QtWebKit"; | |
else if (moduleName == "xml") | |
return "QtXml"; | |
else | |
return ""; | |
} | |
/*! | |
*/ | |
void InnerNode::removeRelated(Node *pseudoChild) | |
{ | |
related.removeAll(pseudoChild); | |
} | |
/*! | |
\class LeafNode | |
*/ | |
/*! | |
Returns false because this is a LeafNode. | |
*/ | |
bool LeafNode::isInnerNode() const | |
{ | |
return false; | |
} | |
/*! | |
Constructs a leaf node named \a name of the specified | |
\a type. The new leaf node becomes a child of \a parent. | |
*/ | |
LeafNode::LeafNode(Type type, InnerNode *parent, const QString& name) | |
: Node(type, parent, name) | |
{ | |
switch (type) { | |
case Enum: | |
case Function: | |
case Typedef: | |
case Variable: | |
case QmlProperty: | |
case QmlSignal: | |
case QmlMethod: | |
setPageType(ApiPage); | |
break; | |
default: | |
break; | |
} | |
} | |
/*! | |
\class NamespaceNode | |
*/ | |
/*! | |
Constructs a namespace node. | |
*/ | |
NamespaceNode::NamespaceNode(InnerNode *parent, const QString& name) | |
: InnerNode(Namespace, parent, name) | |
{ | |
setPageType(ApiPage); | |
} | |
/*! | |
\class ClassNode | |
\brief This class represents a C++ class. | |
*/ | |
/*! | |
Constructs a class node. A class node will generate an API page. | |
*/ | |
ClassNode::ClassNode(InnerNode *parent, const QString& name) | |
: InnerNode(Class, parent, name) | |
{ | |
hidden = false; | |
abstract = false; | |
setPageType(ApiPage); | |
} | |
/*! | |
*/ | |
void ClassNode::addBaseClass(Access access, | |
ClassNode *node, | |
const QString &dataTypeWithTemplateArgs) | |
{ | |
bases.append(RelatedClass(access, node, dataTypeWithTemplateArgs)); | |
node->derived.append(RelatedClass(access, this)); | |
} | |
/*! | |
*/ | |
void ClassNode::fixBaseClasses() | |
{ | |
int i; | |
i = 0; | |
while (i < bases.size()) { | |
ClassNode* bc = bases.at(i).node; | |
if (bc->access() == Node::Private) { | |
RelatedClass rc = bases.at(i); | |
bases.removeAt(i); | |
ignoredBases.append(rc); | |
const QList<RelatedClass> &bb = bc->baseClasses(); | |
for (int j = bb.size() - 1; j >= 0; --j) | |
bases.insert(i, bb.at(j)); | |
} | |
else { | |
++i; | |
} | |
} | |
i = 0; | |
while (i < derived.size()) { | |
ClassNode* dc = derived.at(i).node; | |
if (dc->access() == Node::Private) { | |
derived.removeAt(i); | |
const QList<RelatedClass> &dd = dc->derivedClasses(); | |
for (int j = dd.size() - 1; j >= 0; --j) | |
derived.insert(i, dd.at(j)); | |
} | |
else { | |
++i; | |
} | |
} | |
} | |
/*! | |
Search the child list to find the property node with the | |
specified \a name. | |
*/ | |
const PropertyNode *ClassNode::findPropertyNode(const QString &name) const | |
{ | |
const Node *n = findNode(name, Node::Property); | |
if (n) | |
return static_cast<const PropertyNode*>(n); | |
const PropertyNode *pn = 0; | |
const QList<RelatedClass> &bases = baseClasses(); | |
if (!bases.isEmpty()) { | |
for (int i = 0; i < bases.size(); ++i) { | |
const ClassNode *cn = bases[i].node; | |
pn = cn->findPropertyNode(name); | |
if (pn) | |
break; | |
} | |
} | |
const QList<RelatedClass>& ignoredBases = ignoredBaseClasses(); | |
if (!ignoredBases.isEmpty()) { | |
for (int i = 0; i < ignoredBases.size(); ++i) { | |
const ClassNode *cn = ignoredBases[i].node; | |
pn = cn->findPropertyNode(name); | |
if (pn) | |
break; | |
} | |
} | |
return pn; | |
} | |
/*! | |
\class FakeNode | |
*/ | |
/*! | |
The type of a FakeNode is Fake, and it has a \a subtype, | |
which specifies the type of FakeNode. The page type for | |
the page index is set here. | |
*/ | |
FakeNode::FakeNode(InnerNode *parent, const QString& name, SubType subtype) | |
: InnerNode(Fake, parent, name), sub(subtype) | |
{ | |
switch (subtype) { | |
case Module: | |
case Page: | |
case Group: | |
setPageType(ArticlePage); | |
break; | |
case QmlClass: | |
case QmlBasicType: | |
setPageType(ApiPage); | |
break; | |
case Example: | |
setPageType(ExamplePage); | |
break; | |
default: | |
break; | |
} | |
} | |
/*! | |
Returns the fake node's title. This is used for the page title. | |
*/ | |
QString FakeNode::title() const | |
{ | |
return tle; | |
} | |
/*! | |
Returns the fake node's full title, which is usually | |
just title(), but for some SubType values is different | |
from title() | |
*/ | |
QString FakeNode::fullTitle() const | |
{ | |
if (sub == File) { | |
if (title().isEmpty()) | |
return name().mid(name().lastIndexOf('/') + 1) + " Example File"; | |
else | |
return title(); | |
} | |
else if (sub == Image) { | |
if (title().isEmpty()) | |
return name().mid(name().lastIndexOf('/') + 1) + " Image File"; | |
else | |
return title(); | |
} | |
else if (sub == HeaderFile) { | |
if (title().isEmpty()) | |
return name(); | |
else | |
return name() + " - " + title(); | |
} | |
else { | |
return title(); | |
} | |
} | |
/*! | |
Returns the subtitle. | |
*/ | |
QString FakeNode::subTitle() const | |
{ | |
if (!stle.isEmpty()) | |
return stle; | |
if ((sub == File) || (sub == Image)) { | |
if (title().isEmpty() && name().contains("/")) | |
return name(); | |
} | |
return QString(); | |
} | |
/*! | |
\class EnumNode | |
*/ | |
/*! | |
The constructor for the node representing an enum type | |
has a \a parent class and an enum type \a name. | |
*/ | |
EnumNode::EnumNode(InnerNode *parent, const QString& name) | |
: LeafNode(Enum, parent, name), ft(0) | |
{ | |
} | |
/*! | |
Add \a item to the enum type's item list. | |
*/ | |
void EnumNode::addItem(const EnumItem& item) | |
{ | |
itms.append(item); | |
names.insert(item.name()); | |
} | |
/*! | |
Returns the access level of the enumeration item named \a name. | |
Apparently it is private if it has been omitted by qdoc's | |
omitvalue command. Otherwise it is public. | |
*/ | |
Node::Access EnumNode::itemAccess(const QString &name) const | |
{ | |
if (doc().omitEnumItemNames().contains(name)) | |
return Private; | |
return Public; | |
} | |
/*! | |
Returns the enum value associated with the enum \a name. | |
*/ | |
QString EnumNode::itemValue(const QString &name) const | |
{ | |
foreach (const EnumItem &item, itms) { | |
if (item.name() == name) | |
return item.value(); | |
} | |
return QString(); | |
} | |
/*! | |
\class TypedefNode | |
*/ | |
/*! | |
*/ | |
TypedefNode::TypedefNode(InnerNode *parent, const QString& name) | |
: LeafNode(Typedef, parent, name), ae(0) | |
{ | |
} | |
/*! | |
*/ | |
void TypedefNode::setAssociatedEnum(const EnumNode *enume) | |
{ | |
ae = enume; | |
} | |
/*! | |
\class Parameter | |
\brief The class Parameter contains one parameter. | |
A parameter can be a function parameter or a macro | |
parameter. | |
*/ | |
/*! | |
Constructs this parameter from the left and right types | |
\a leftType and rightType, the parameter \a name, and the | |
\a defaultValue. In practice, \a rightType is not used, | |
and I don't know what is was meant for. | |
*/ | |
Parameter::Parameter(const QString& leftType, | |
const QString& rightType, | |
const QString& name, | |
const QString& defaultValue) | |
: lef(leftType), rig(rightType), nam(name), def(defaultValue) | |
{ | |
} | |
/*! | |
The standard copy constructor copies the strings from \a p. | |
*/ | |
Parameter::Parameter(const Parameter& p) | |
: lef(p.lef), rig(p.rig), nam(p.nam), def(p.def) | |
{ | |
} | |
/*! | |
Assigning Parameter \a p to this Parameter copies the | |
strings across. | |
*/ | |
Parameter& Parameter::operator=(const Parameter& p) | |
{ | |
lef = p.lef; | |
rig = p.rig; | |
nam = p.nam; | |
def = p.def; | |
return *this; | |
} | |
/*! | |
Reconstructs the text describing the parameter and | |
returns it. If \a value is true, the default value | |
will be included, if there is one. | |
*/ | |
QString Parameter::reconstruct(bool value) const | |
{ | |
QString p = lef + rig; | |
if (!p.endsWith(QChar('*')) && !p.endsWith(QChar('&')) && !p.endsWith(QChar(' '))) | |
p += " "; | |
p += nam; | |
if (value && !def.isEmpty()) | |
p += " = " + def; | |
return p; | |
} | |
/*! | |
\class FunctionNode | |
*/ | |
/*! | |
Construct a function node for a C++ function. It's parent | |
is \a parent, and it's name is \a name. | |
*/ | |
FunctionNode::FunctionNode(InnerNode *parent, const QString& name) | |
: LeafNode(Function, parent, name), | |
met(Plain), | |
vir(NonVirtual), | |
con(false), | |
sta(false), | |
ove(false), | |
att(false), | |
rf(0), | |
ap(0) | |
{ | |
// nothing. | |
} | |
/*! | |
Construct a function node for a QML method or signal, specified | |
by \a type. It's parent is \a parent, and it's name is \a name. | |
If \a attached is true, it is an attached method or signal. | |
*/ | |
FunctionNode::FunctionNode(Type type, InnerNode *parent, const QString& name, bool attached) | |
: LeafNode(type, parent, name), | |
met(Plain), | |
vir(NonVirtual), | |
con(false), | |
sta(false), | |
ove(false), | |
att(attached), | |
rf(0), | |
ap(0) | |
{ | |
// nothing. | |
} | |
/*! | |
Sets the \a virtualness of this function. If the \a virtualness | |
is PureVirtual, and if the parent() is a ClassNode, set the parent's | |
\e abstract flag to true. | |
*/ | |
void FunctionNode::setVirtualness(Virtualness virtualness) | |
{ | |
vir = virtualness; | |
if ((virtualness == PureVirtual) && parent() && | |
(parent()->type() == Node::Class)) | |
parent()->setAbstract(true); | |
} | |
/*! | |
*/ | |
void FunctionNode::setOverload(bool overlode) | |
{ | |
parent()->setOverload(this, overlode); | |
ove = overlode; | |
} | |
/*! | |
Sets the function node's reimplementation flag to \a r. | |
When \a r is true, it is supposed to mean that this function | |
is a reimplementation of a virtual function in a base class, | |
but it really just means the \e reimp command was seen in the | |
qdoc comment. | |
*/ | |
void FunctionNode::setReimp(bool r) | |
{ | |
reimp = r; | |
} | |
/*! | |
*/ | |
void FunctionNode::addParameter(const Parameter& parameter) | |
{ | |
params.append(parameter); | |
} | |
/*! | |
*/ | |
void FunctionNode::borrowParameterNames(const FunctionNode *source) | |
{ | |
QList<Parameter>::Iterator t = params.begin(); | |
QList<Parameter>::ConstIterator s = source->params.begin(); | |
while (s != source->params.end() && t != params.end()) { | |
if (!(*s).name().isEmpty()) | |
(*t).setName((*s).name()); | |
++s; | |
++t; | |
} | |
} | |
/*! | |
If this function is a reimplementation, \a from points | |
to the FunctionNode of the function being reimplemented. | |
*/ | |
void FunctionNode::setReimplementedFrom(FunctionNode *from) | |
{ | |
rf = from; | |
from->rb.append(this); | |
} | |
/*! | |
Sets the "associated" property to \a property. The function | |
might be the setter or getter for a property, for example. | |
*/ | |
void FunctionNode::setAssociatedProperty(PropertyNode *property) | |
{ | |
ap = property; | |
} | |
/*! | |
Returns the overload number for this function obtained | |
from the parent. | |
*/ | |
int FunctionNode::overloadNumber() const | |
{ | |
return parent()->overloadNumber(this); | |
} | |
/*! | |
Returns the number of times this function name has been | |
overloaded, obtained from the parent. | |
*/ | |
int FunctionNode::numOverloads() const | |
{ | |
return parent()->numOverloads(name()); | |
} | |
/*! | |
Returns the list of parameter names. | |
*/ | |
QStringList FunctionNode::parameterNames() const | |
{ | |
QStringList names; | |
QList<Parameter>::ConstIterator p = parameters().begin(); | |
while (p != parameters().end()) { | |
names << (*p).name(); | |
++p; | |
} | |
return names; | |
} | |
/*! | |
Returns a raw list of parameters. If \a names is true, the | |
names are included. If \a values is true, the default values | |
are included, if any are present. | |
*/ | |
QString FunctionNode::rawParameters(bool names, bool values) const | |
{ | |
QString raw; | |
foreach (const Parameter ¶meter, parameters()) { | |
raw += parameter.leftType() + parameter.rightType(); | |
if (names) | |
raw += parameter.name(); | |
if (values) | |
raw += parameter.defaultValue(); | |
} | |
return raw; | |
} | |
/*! | |
Returns the list of reconstructed parameters. If \a values | |
is true, the default values are included, if any are present. | |
*/ | |
QStringList FunctionNode::reconstructParams(bool values) const | |
{ | |
QStringList params; | |
QList<Parameter>::ConstIterator p = parameters().begin(); | |
while (p != parameters().end()) { | |
params << (*p).reconstruct(values); | |
++p; | |
} | |
return params; | |
} | |
/*! | |
Reconstructs and returns the function's signature. If \a values | |
is true, the default values of the parameters are included, if | |
present. | |
*/ | |
QString FunctionNode::signature(bool values) const | |
{ | |
QString s; | |
if (!returnType().isEmpty()) | |
s = returnType() + " "; | |
s += name() + "("; | |
QStringList params = reconstructParams(values); | |
int p = params.size(); | |
if (p > 0) { | |
for (int i=0; i<p; i++) { | |
s += params[i]; | |
if (i < (p-1)) | |
s += ", "; | |
} | |
} | |
s += ")"; | |
return s; | |
} | |
/*! | |
Returns true if the node's status is Internal, or if its | |
parent is a class with internal status. | |
*/ | |
bool FunctionNode::isInternal() const | |
{ | |
if (status() == Internal) | |
return true; | |
if (parent() && parent()->status() == Internal) | |
return true; | |
if (relates() && relates()->status() == Internal) | |
return true; | |
return false; | |
} | |
/*! | |
Print some debugging stuff. | |
*/ | |
void FunctionNode::debug() const | |
{ | |
qDebug("QML METHOD %s rt %s pp %s", | |
qPrintable(name()), qPrintable(rt), qPrintable(pp.join(" "))); | |
} | |
/*! | |
\class PropertyNode | |
This class describes one instance of using the Q_PROPERTY macro. | |
*/ | |
/*! | |
The constructor sets the \a parent and the \a name, but | |
everything else is set to default values. | |
*/ | |
PropertyNode::PropertyNode(InnerNode *parent, const QString& name) | |
: LeafNode(Property, parent, name), | |
sto(Trool_Default), | |
des(Trool_Default), | |
scr(Trool_Default), | |
wri(Trool_Default), | |
usr(Trool_Default), | |
cst(false), | |
fnl(false), | |
rev(-1), | |
overrides(0) | |
{ | |
// nothing. | |
} | |
/*! | |
Sets this property's \e {overridden from} property to | |
\a baseProperty, which indicates that this property | |
overrides \a baseProperty. To begin with, all the values | |
in this property are set to the corresponding values in | |
\a baseProperty. | |
We probably should ensure that the constant and final | |
attributes are not being overridden improperly. | |
*/ | |
void PropertyNode::setOverriddenFrom(const PropertyNode* baseProperty) | |
{ | |
for (int i = 0; i < NumFunctionRoles; ++i) { | |
if (funcs[i].isEmpty()) | |
funcs[i] = baseProperty->funcs[i]; | |
} | |
if (sto == Trool_Default) | |
sto = baseProperty->sto; | |
if (des == Trool_Default) | |
des = baseProperty->des; | |
if (scr == Trool_Default) | |
scr = baseProperty->scr; | |
if (wri == Trool_Default) | |
wri = baseProperty->wri; | |
if (usr == Trool_Default) | |
usr = baseProperty->usr; | |
overrides = baseProperty; | |
} | |
/*! | |
*/ | |
QString PropertyNode::qualifiedDataType() const | |
{ | |
if (setters().isEmpty() && resetters().isEmpty()) { | |
if (dt.contains("*") || dt.contains("&")) { | |
// 'QWidget *' becomes 'QWidget *' const | |
return dt + " const"; | |
} | |
else { | |
/* | |
'int' becomes 'const int' ('int const' is | |
correct C++, but looks wrong) | |
*/ | |
return "const " + dt; | |
} | |
} | |
else { | |
return dt; | |
} | |
} | |
/*! Converts the \a boolean value to an enum representation | |
of the boolean type, which includes an enum value for the | |
\e {default value} of the item, i.e. true, false, or default. | |
*/ | |
PropertyNode::Trool PropertyNode::toTrool(bool boolean) | |
{ | |
return boolean ? Trool_True : Trool_False; | |
} | |
/*! | |
Converts the enum \a troolean back to a boolean value. | |
If \a troolean is neither the true enum value nor the | |
false enum value, the boolean value returned is | |
\a defaultValue. | |
Note that runtimeDesignabilityFunction() should be called | |
first. If that function returns the name of a function, it | |
means the function must be called at runtime to determine | |
whether the property is Designable. | |
*/ | |
bool PropertyNode::fromTrool(Trool troolean, bool defaultValue) | |
{ | |
switch (troolean) { | |
case Trool_True: | |
return true; | |
case Trool_False: | |
return false; | |
default: | |
return defaultValue; | |
} | |
} | |
/*! | |
\class TargetNode | |
*/ | |
/*! | |
*/ | |
TargetNode::TargetNode(InnerNode *parent, const QString& name) | |
: LeafNode(Target, parent, name) | |
{ | |
} | |
/*! | |
Returns false because this is a TargetNode. | |
*/ | |
bool TargetNode::isInnerNode() const | |
{ | |
return false; | |
} | |
#ifdef QDOC_QML | |
bool QmlClassNode::qmlOnly = false; | |
QMultiMap<QString,Node*> QmlClassNode::inheritedBy; | |
/*! | |
Constructs a Qml class node (i.e. a Fake node with the | |
subtype QmlClass. The new node has the given \a parent | |
and \a name and is associated with the C++ class node | |
specified by \a cn which may be null if the the Qml | |
class node is not associated with a C++ class node. | |
*/ | |
QmlClassNode::QmlClassNode(InnerNode *parent, | |
const QString& name, | |
const ClassNode* cn) | |
: FakeNode(parent, name, QmlClass), cnode(cn) | |
{ | |
if (name.startsWith(QLatin1String("QML:"))) | |
setTitle((qmlOnly ? QLatin1String("") : QLatin1String("QML ")) + name.mid(4) + QLatin1String(" Element")); | |
else | |
setTitle((qmlOnly ? QLatin1String("") : QLatin1String("QML ")) + name + QLatin1String(" Element")); | |
} | |
/*! | |
I made this so I could print a debug message here. | |
*/ | |
QmlClassNode::~QmlClassNode() | |
{ | |
#ifdef DEBUG_MULTIPLE_QDOCCONF_FILES | |
qDebug() << "Deleting QmlClassNode:" << name(); | |
#endif | |
} | |
/*! | |
Clear the multimap so that subsequent runs don't try to use | |
nodes from a previous run. | |
*/ | |
void QmlClassNode::clear() | |
{ | |
inheritedBy.clear(); | |
} | |
/*! | |
The base file name for this kind of node has "qml_" | |
prepended to it. | |
But not yet. Still testing. | |
*/ | |
QString QmlClassNode::fileBase() const | |
{ | |
return Node::fileBase(); | |
} | |
/*! | |
Record the fact that QML class \a base is inherited by | |
QML class \a sub. | |
*/ | |
void QmlClassNode::addInheritedBy(const QString& base, Node* sub) | |
{ | |
inheritedBy.insert(base,sub); | |
#ifdef DEBUG_MULTIPLE_QDOCCONF_FILES | |
qDebug() << "QmlClassNode::addInheritedBy(): insert" << base << sub->name() << inheritedBy.size(); | |
#endif | |
} | |
/*! | |
Loads the list \a subs with the nodes of all the subclasses of \a base. | |
*/ | |
void QmlClassNode::subclasses(const QString& base, NodeList& subs) | |
{ | |
subs.clear(); | |
if (inheritedBy.count(base) > 0) { | |
subs = inheritedBy.values(base); | |
#ifdef DEBUG_MULTIPLE_QDOCCONF_FILES | |
qDebug() << "QmlClassNode::subclasses():" << inheritedBy.count(base) << base | |
<< "subs:" << subs.size() << "total size:" << inheritedBy.size(); | |
#endif | |
} | |
} | |
/*! | |
Constructs a Qml basic type node (i.e. a Fake node with | |
the subtype QmlBasicType. The new node has the given | |
\a parent and \a name. | |
*/ | |
QmlBasicTypeNode::QmlBasicTypeNode(InnerNode *parent, | |
const QString& name) | |
: FakeNode(parent, name, QmlBasicType) | |
{ | |
setTitle(name); | |
} | |
/*! | |
Constructor for the Qml property group node. \a parent is | |
always a QmlClassNode. | |
*/ | |
QmlPropGroupNode::QmlPropGroupNode(QmlClassNode* parent, | |
const QString& name, | |
bool attached) | |
: FakeNode(parent, name, QmlPropertyGroup), | |
isdefault(false), | |
att(attached) | |
{ | |
// nothing. | |
} | |
/*! | |
Constructor for the QML property node. | |
*/ | |
QmlPropertyNode::QmlPropertyNode(QmlPropGroupNode *parent, | |
const QString& name, | |
const QString& type, | |
bool attached) | |
: LeafNode(QmlProperty, parent, name), | |
dt(type), | |
sto(Trool_Default), | |
des(Trool_Default), | |
att(attached) | |
{ | |
setPageType(ApiPage); | |
} | |
/*! | |
I don't know what this is. | |
*/ | |
QmlPropertyNode::Trool QmlPropertyNode::toTrool(bool boolean) | |
{ | |
return boolean ? Trool_True : Trool_False; | |
} | |
/*! | |
I don't know what this is either. | |
*/ | |
bool QmlPropertyNode::fromTrool(Trool troolean, bool defaultValue) | |
{ | |
switch (troolean) { | |
case Trool_True: | |
return true; | |
case Trool_False: | |
return false; | |
default: | |
return defaultValue; | |
} | |
} | |
static QString valueType(const QString &n) | |
{ | |
if (n == "QPoint") | |
return "QDeclarativePointValueType"; | |
if (n == "QPointF") | |
return "QDeclarativePointFValueType"; | |
if (n == "QSize") | |
return "QDeclarativeSizeValueType"; | |
if (n == "QSizeF") | |
return "QDeclarativeSizeFValueType"; | |
if (n == "QRect") | |
return "QDeclarativeRectValueType"; | |
if (n == "QRectF") | |
return "QDeclarativeRectFValueType"; | |
if (n == "QVector2D") | |
return "QDeclarativeVector2DValueType"; | |
if (n == "QVector3D") | |
return "QDeclarativeVector3DValueType"; | |
if (n == "QVector4D") | |
return "QDeclarativeVector4DValueType"; | |
if (n == "QQuaternion") | |
return "QDeclarativeQuaternionValueType"; | |
if (n == "QMatrix4x4") | |
return "QDeclarativeMatrix4x4ValueType"; | |
if (n == "QEasingCurve") | |
return "QDeclarativeEasingValueType"; | |
if (n == "QFont") | |
return "QDeclarativeFontValueType"; | |
return QString(); | |
} | |
/*! | |
Returns true if a QML property or attached property is | |
read-only. The algorithm for figuring this out is long | |
amd tedious and almost certainly will break. It currently | |
doesn't work for qmlproperty bool PropertyChanges::explicit, | |
because the tokenizer gets confused on "explicit". | |
*/ | |
bool QmlPropertyNode::isWritable(const Tree* tree) const | |
{ | |
if (wri != Trool_Default) | |
return fromTrool(wri, false); | |
const PropertyNode *pn = correspondingProperty(tree); | |
if (pn) | |
return pn->isWritable(); | |
else { | |
location().warning(tr("Can't determine read-only status of QML property %1; writable assumed.").arg(name())); | |
return true; | |
} | |
} | |
const PropertyNode *QmlPropertyNode::correspondingProperty(const Tree *tree) const | |
{ | |
const PropertyNode *pn; | |
Node* n = parent(); | |
while (n && n->subType() != Node::QmlClass) | |
n = n->parent(); | |
if (n) { | |
const QmlClassNode* qcn = static_cast<const QmlClassNode*>(n); | |
const ClassNode* cn = qcn->classNode(); | |
if (cn) { | |
QStringList dotSplit = name().split(QChar('.')); | |
pn = cn->findPropertyNode(dotSplit[0]); | |
if (pn) { | |
if (dotSplit.size() > 1) { | |
// Find the C++ property corresponding to the QML property in | |
// the property group, <group>.<property>. | |
QStringList path(extractClassName(pn->qualifiedDataType())); | |
const Node* nn = tree->findNode(path,Class); | |
if (nn) { | |
const ClassNode* cn = static_cast<const ClassNode*>(nn); | |
const PropertyNode *pn2 = cn->findPropertyNode(dotSplit[1]); | |
if (pn2) | |
return pn2; // Return the property for the QML property. | |
else | |
return pn; // Return the property for the QML group. | |
} | |
} | |
else | |
return pn; | |
} | |
else { | |
pn = cn->findPropertyNode(dotSplit[0]); | |
if (pn) | |
return pn; | |
} | |
} | |
} | |
return 0; | |
} | |
#endif | |
QT_END_NAMESPACE |