1 // FGQmlPropertyNode.cxx - expose SGPropertyNode to QML
2 //
3 // Copyright (C) 2019 James Turner <james@flightgear.org>
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation; either version 2 of the
8 // License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
19 #include "config.h"
20
21 #include "FGQmlPropertyNode.hxx"
22
23 #include <QVector3D>
24 #include <QVector4D>
25 #include <QDebug>
26
27 #include <simgear/props/props.hxx>
28 #include <simgear/props/vectorPropTemplates.hxx>
29 #include <simgear/math/SGMath.hxx>
30
31 #include <Main/fg_props.hxx>
32
33 /////////////////////////////////////////////////////////////////////////
34
FGQmlPropertyNode(QObject * parent)35 FGQmlPropertyNode::FGQmlPropertyNode(QObject *parent) : QObject(parent)
36 {
37
38 }
39
set(QVariant newValue)40 bool FGQmlPropertyNode::set(QVariant newValue)
41 {
42 if (!_prop || !_prop->getAttribute(SGPropertyNode::WRITE))
43 return false;
44
45 // we still need to short circuit setting
46 if (newValue == value()) {
47 return true;
48 }
49
50 switch (_prop->getType()) {
51 case simgear::props::INT:
52 case simgear::props::LONG:
53 _prop->setIntValue(newValue.toInt());
54 case simgear::props::BOOL: _prop->setBoolValue(newValue.toBool());
55 case simgear::props::DOUBLE: _prop->setDoubleValue(newValue.toDouble());
56 case simgear::props::FLOAT: _prop->setFloatValue(newValue.toFloat());
57 case simgear::props::STRING: _prop->setStringValue(newValue.toString().toStdString());
58
59 case simgear::props::VEC3D: {
60 QVector3D v = newValue.value<QVector3D>();
61 _prop->setValue(SGVec3d(v.x(), v.y(), v.z()));
62 }
63
64 case simgear::props::VEC4D: {
65 QVector4D v = newValue.value<QVector4D>();
66 _prop->setValue(SGVec4d(v.x(), v.y(), v.z(), v.w()));
67 }
68
69 default:
70 qWarning() << Q_FUNC_INFO << "handle untyped property writes";
71 break;
72 }
73
74 return true;
75 }
76
value() const77 QVariant FGQmlPropertyNode::value() const
78 {
79 return propertyValueAsVariant(_prop);
80 }
81
path() const82 QString FGQmlPropertyNode::path() const
83 {
84 if (!_prop)
85 return QString();
86
87 return QString::fromStdString(_prop->getPath());
88 }
89
parentProp() const90 FGQmlPropertyNode *FGQmlPropertyNode::parentProp() const
91 {
92 if (!_prop || !_prop->getParent())
93 return nullptr;
94
95 auto pp = new FGQmlPropertyNode;
96 pp->setNode(_prop->getParent());
97 return pp;
98 }
99
setNode(SGPropertyNode_ptr node)100 void FGQmlPropertyNode::setNode(SGPropertyNode_ptr node)
101 {
102 if (node == _prop) {
103 return;
104 }
105
106 if (_prop) {
107 _prop->removeChangeListener(this);
108 }
109
110 _prop = node;
111 if (_prop) {
112 _prop->addChangeListener(this, false);
113 }
114
115 emit parentPropChanged(parentProp());
116 emit pathChanged(path());
117 emit valueChangedNotify(value());
118 emit childPropsChanged();
119 }
120
node() const121 SGPropertyNode_ptr FGQmlPropertyNode::node() const
122 {
123 return _prop;
124 }
125
propertyValueAsVariant(SGPropertyNode * p)126 QVariant FGQmlPropertyNode::propertyValueAsVariant(SGPropertyNode* p)
127 {
128 if (!p)
129 return {};
130
131 switch (p->getType()) {
132 case simgear::props::INT:
133 case simgear::props::LONG:
134 return p->getIntValue();
135 case simgear::props::BOOL: return p->getBoolValue();
136 case simgear::props::DOUBLE: return p->getDoubleValue();
137 case simgear::props::FLOAT: return p->getFloatValue();
138 case simgear::props::STRING: return QString::fromStdString(p->getStringValue());
139
140 case simgear::props::VEC3D: {
141 const SGVec3d v3 = p->getValue<SGVec3d>();
142 return QVariant::fromValue(QVector3D(v3.x(), v3.y(), v3.z()));
143 }
144
145 case simgear::props::VEC4D: {
146 const SGVec4d v4 = p->getValue<SGVec4d>();
147 return QVariant::fromValue(QVector4D(v4.x(), v4.y(), v4.z(), v4.w()));
148 }
149 default:
150 break;
151 }
152
153 return {}; // null qvariant
154 }
155
valueChanged(SGPropertyNode * node)156 void FGQmlPropertyNode::valueChanged(SGPropertyNode *node)
157 {
158 if (node != _prop) {
159 return;
160 }
161 emit valueChangedNotify(value());
162 }
163
childAdded(SGPropertyNode * pr,SGPropertyNode * child)164 void FGQmlPropertyNode::childAdded(SGPropertyNode* pr, SGPropertyNode* child)
165 {
166 if (pr == _prop) {
167 emit childPropsChanged();
168 }
169 }
170
childRemoved(SGPropertyNode * pr,SGPropertyNode * child)171 void FGQmlPropertyNode::childRemoved(SGPropertyNode* pr, SGPropertyNode* child)
172 {
173 if (pr == _prop) {
174 emit childPropsChanged();
175 }
176 }
177
setPath(QString path)178 void FGQmlPropertyNode::setPath(QString path)
179 {
180 SGPropertyNode_ptr node = fgGetNode(path.toStdString(), false /* don't create */);
181 setNode(node);
182 }
183
184
childCount() const185 int FGQmlPropertyNode::childCount() const
186 {
187 if (!_prop)
188 return 0;
189 return _prop->nChildren();
190 }
191
childAt(int index) const192 FGQmlPropertyNode* FGQmlPropertyNode::childAt(int index) const
193 {
194 if (!_prop || (_prop->nChildren() <= index))
195 return nullptr;
196
197 auto pp = new FGQmlPropertyNode;
198 pp->setNode(_prop->getChild(index));
199 return pp;
200 }
201