FlightGear next
QmlPropertyModel.cxx
Go to the documentation of this file.
1// Copyright (C) 2020 James Turner <james@flightgear.org>
2//
3// This program is free software; you can redistribute it and/or
4// modify it under the terms of the GNU General Public License as
5// published by the Free Software Foundation; either version 2 of the
6// License, or (at your option) any later version.
7//
8// This program is distributed in the hope that it will be useful, but
9// WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11// General Public License for more details.
12//
13// You should have received a copy of the GNU General Public License
14// along with this program; if not, write to the Free Software
15// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16
17#include "config.h"
18
19#include "QmlPropertyModel.hxx"
20
21#include <algorithm>
22
23#include <QDebug>
24#include <QVector>
25
28#include <Main/fg_props.hxx>
29#include <simgear/props/props.hxx>
30
31class FGQmlPropertyModel::PropertyModelPrivate : public SGPropertyChangeListener
32{
33public:
34 void clear()
35 {
36 if (_props) {
37 _props->removeChangeListener(this);
38 _props.clear();
39 _roles.clear();
40 }
41 }
42
44 {
45 _props = fgGetNode(_rootPath.toStdString());
46 if (!_props) {
47 qWarning() << "Passed non-existant path to QmlPropertyModel:" << _rootPath;
48 }
49
50 _props->addChangeListener(this);
51
52 if (_childName.isEmpty()) {
53 // all children, no filtering by name
54 const auto sz = _props->nChildren();
55 _directChildren.resize(sz);
56 for (auto i = 0; i < sz; ++i) {
57 _directChildren[i] = _props->getChild(i);
58 }
59 } else {
60 _directChildren = _props->getChildren(_childName.toStdString());
61 }
62
64 }
65
67 {
68 auto oldRoles = _roles;
69 _roles.clear();
70 for (auto p : _directChildren) {
71 const auto nc = p->nChildren();
72 for (int i = 0; i < nc; ++i) {
73 roleForNode(p->getChild(i)->getNameString());
74 }
75 }
76
77 if (_roles != oldRoles) {
78 // we need to model-reset to tell QML about new names
79 p->beginResetModel();
80 p->endResetModel();
81 }
82 }
83
84 int roleForNode(const std::string& s)
85 {
86 auto it = std::find(_roles.begin(), _roles.end(), s);
87 if (it != _roles.end()) {
88 return static_cast<int>(std::distance(_roles.begin(), it)) + Qt::UserRole;
89 }
90
91 qDebug() << Q_FUNC_INFO << "adding" << QString::fromStdString(s);
92 _roles.push_back(s);
93 return Qt::UserRole + static_cast<int>(_roles.size() - 1);
94 }
95
96 void valueChanged(SGPropertyNode* node) override
97 {
98 auto it = std::find(_directChildren.begin(), _directChildren.end(), node);
99 if (it == _directChildren.end())
100 return;
101
102 doDataChanged(it, node);
103 }
104
105 void doDataChanged(const simgear::PropertyList::iterator& it, SGPropertyNode* node)
106 {
107 QVector<int> roles;
108 roles.append(roleForNode(node->getNameString()));
109 QModelIndex m = p->index(std::distance(_directChildren.begin(), it));
110 p->dataChanged(m, m, roles);
111 }
112
113 void childAdded(SGPropertyNode* parent, SGPropertyNode* child) override
114 {
115 if (parent == _props) {
116 if (!_childName.isEmpty() && (child->getNameString() != _childName.toStdString())) {
117 // doesn't pass name filter, don't care
118 return;
119 }
120
121 int insertRow = child->getIndex();
122 if (!_childName.isEmpty()) {
123 // always an append
124 insertRow = _directChildren.size();
125 }
126
127 p->beginInsertRows(QModelIndex{}, insertRow, insertRow);
128 _directChildren.push_back(child);
129 p->endInsertRows();
130 return;
131 }
132
133 auto it = std::find(_directChildren.begin(), _directChildren.end(), parent);
134 if (it == _directChildren.end())
135 return;
136
137 doDataChanged(it, child);
138 }
139
140 void childRemoved(SGPropertyNode* parent, SGPropertyNode* child) override
141 {
142 if (parent == _props) {
143 if (!_childName.isEmpty() && (child->getNameString() != _childName.toStdString())) {
144 // doesn't pass name filter, don't care
145 return;
146 }
147
148 auto it = std::find(_directChildren.begin(), _directChildren.end(), child);
149 if (it == _directChildren.end()) {
150 SG_LOG(SG_GUI, SG_DEV_ALERT, "Bug in QmlPropertyModel - child not found when removing:" << parent->getPath() << " - " << child->getNameString());
151 return;
152 }
153
154 int row = static_cast<int>(std::distance(_directChildren.begin(), it));
155 p->beginRemoveRows(QModelIndex{}, row, row);
156 _directChildren.erase(it);
157 p->endInsertRows();
158 return;
159 }
160
161 auto it = std::find(_directChildren.begin(), _directChildren.end(), parent);
162 if (it == _directChildren.end())
163 return;
164
165 // actually the value will be null now
166 doDataChanged(it, child);
167 }
168
169 QString _rootPath;
170 QString _childName;
171
173 SGPropertyNode_ptr _props;
174 simgear::PropertyList _directChildren;
175 std::vector<std::string> _roles;
176};
177
179{
180 d->clear();
181}
182
184{
185 return d->_rootPath;
186}
187
189{
190 return d->_childName;
191}
192
193QHash<int, QByteArray> FGQmlPropertyModel::roleNames() const
194{
195 QHash<int, QByteArray> r;
196 for (size_t i = 0; i < d->_roles.size(); ++i) {
197 r[i] = QByteArray::fromStdString(d->_roles.at(i));
198 }
199 return r;
200}
201
202QVariant FGQmlPropertyModel::data(const QModelIndex& m, int role) const
203{
204 auto node = d->_directChildren.at(m.row());
205 const int r = role - Qt::UserRole;
206 assert(r < d->_roles.size());
207 const auto& propName = d->_roles.at(r);
208 const auto prop = node->getChild(propName);
209 if (!prop)
210 return {}; // no data for role
212}
213
215{
216 if (d->_rootPath == rootPath)
217 return;
218
219 beginResetModel();
220 d->_rootPath = rootPath;
221 d->clear();
222 d->computeProps();
223 endResetModel();
224
225 emit rootPathChanged(d->_rootPath);
226}
227
229{
230 if (d->_childName == childName)
231 return;
232
233 beginResetModel();
234 d->_childName = childName;
235 d->clear();
236 d->computeProps();
237 endResetModel();
238
239 emit childNameChanged(d->_childName);
240}
#define i(x)
void doDataChanged(const simgear::PropertyList::iterator &it, SGPropertyNode *node)
void childRemoved(SGPropertyNode *parent, SGPropertyNode *child) override
void childAdded(SGPropertyNode *parent, SGPropertyNode *child) override
void valueChanged(SGPropertyNode *node) override
FGQmlPropertyModel(QObject *parent=nullptr)
void setRootPath(QString rootPath)
QVariant data(const QModelIndex &m, int role) const override
void setChildName(QString childName)
void rootPathChanged(QString rootPath)
void childNameChanged(QString childName)
QHash< int, QByteArray > roleNames() const override
static QVariant propertyValueAsVariant(SGPropertyNode *p)
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
Definition proptest.cpp:27