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