Directory: | ./ |
---|---|
File: | src/node-property.cpp |
Date: | 2024-12-20 15:53:58 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 63 | 357 | 17.6% |
Branches: | 50 | 776 | 6.4% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | // Copyright (c) 2018, Joseph Mirabel | ||
2 | // Authors: Joseph Mirabel (joseph.mirabel@laas.fr) | ||
3 | // | ||
4 | // This file is part of gepetto-viewer. | ||
5 | // gepetto-viewer is free software: you can redistribute it | ||
6 | // and/or modify it under the terms of the GNU Lesser General Public | ||
7 | // License as published by the Free Software Foundation, either version | ||
8 | // 3 of the License, or (at your option) any later version. | ||
9 | // | ||
10 | // gepetto-viewer is distributed in the hope that it will be | ||
11 | // useful, but WITHOUT ANY WARRANTY; without even the implied warranty | ||
12 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | // General Lesser Public License for more details. You should have | ||
14 | // received a copy of the GNU Lesser General Public License along with | ||
15 | // gepetto-viewer. If not, see <http://www.gnu.org/licenses/>. | ||
16 | |||
17 | #include <gepetto/viewer/node-property.h> | ||
18 | |||
19 | #include <QCheckBox> | ||
20 | #include <QColorDialog> | ||
21 | #include <QComboBox> | ||
22 | #include <QDebug> | ||
23 | #include <QDoubleSpinBox> | ||
24 | #include <QFormLayout> | ||
25 | #include <QLabel> | ||
26 | #include <QLineEdit> | ||
27 | #include <QPushButton> | ||
28 | #include <gepetto/gui/dialog/configuration.hh> | ||
29 | #include <sstream> | ||
30 | |||
31 | namespace gepetto { | ||
32 | namespace viewer { | ||
33 | struct Initializer { | ||
34 | 1 | Initializer() { | |
35 | 1 | qRegisterMetaType<osgVector2>("osgVector2"); | |
36 | 1 | qRegisterMetaType<osgVector3>("osgVector3"); | |
37 | 1 | qRegisterMetaType<osgVector4>("osgVector4"); | |
38 | 1 | qRegisterMetaType<Configuration>("Configuration"); | |
39 | 1 | } | |
40 | }; | ||
41 | |||
42 | static Initializer initializer; | ||
43 | |||
44 | template <typename T> | ||
45 | ✗ | inline bool invalidType(const std::string& name, T) { | |
46 | ✗ | throw std::invalid_argument("Property " + name + " is not of type " + | |
47 | details::property_type<T>::to_string()); | ||
48 | } | ||
49 | |||
50 |
1/2✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
|
14 | Property::Property(const std::string& name) : QObject(NULL), name_(name) { |
51 |
2/4✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 14 times.
✗ Branch 5 not taken.
|
14 | setObjectName(QString::fromStdString(name)); |
52 | 14 | } | |
53 | |||
54 | template <typename T> | ||
55 | 8 | bool check_if_value_changed(Property& prop, const T& v) { | |
56 |
2/4✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
|
8 | if (!prop.hasReadAccess()) return true; |
57 | 8 | T current; | |
58 |
2/4✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
|
8 | if (!prop.get(current)) return true; |
59 | 8 | return current != v; | |
60 | } | ||
61 | |||
62 | ✗ | inline QColor qColor(const osgVector4& v) { | |
63 | ✗ | return QColor::fromRgbF((qreal)v[0], (qreal)v[1], (qreal)v[2], (qreal)v[3]); | |
64 | } | ||
65 | |||
66 | #define SET_IMPLEMENTATION(v, emit2) \ | ||
67 | if (!check_if_value_changed(*this, v)) return true; \ | ||
68 | if (impl_set(v)) { \ | ||
69 | emit valueChanged(v); \ | ||
70 | emit2; \ | ||
71 | return true; \ | ||
72 | } \ | ||
73 | return false | ||
74 | |||
75 | ✗ | bool Property::set(void) { | |
76 | ✗ | bool b = impl_set(); | |
77 | ✗ | if (b) { | |
78 | ✗ | emit valueChanged(); | |
79 | } | ||
80 | ✗ | return b; | |
81 | } | ||
82 | ✗ | bool Property::set(const bool& v) { SET_IMPLEMENTATION(v, ); } | |
83 | ✗ | bool Property::set(const int& v) { SET_IMPLEMENTATION(v, ); } | |
84 | ✗ | bool Property::set(const float& v) { | |
85 | ✗ | SET_IMPLEMENTATION(v, emit valueChanged(static_cast<double>(v))); | |
86 | } | ||
87 | ✗ | bool Property::set(const std::string& v) { | |
88 | ✗ | SET_IMPLEMENTATION(v, emit valueChanged(QString::fromStdString(v))); | |
89 | } | ||
90 | ✗ | bool Property::set(const osgVector2& v) { SET_IMPLEMENTATION(v, ); } | |
91 | ✗ | bool Property::set(const osgVector3& v) { SET_IMPLEMENTATION(v, ); } | |
92 | ✗ | bool Property::set(const osgVector4& v) { | |
93 | ✗ | SET_IMPLEMENTATION(v, emit valueChanged(qColor(v))); | |
94 | } | ||
95 |
3/4✓ Branch 1 taken 1 times.
✓ Branch 2 taken 3 times.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
|
4 | bool Property::set(const Configuration& v) { SET_IMPLEMENTATION(v, ); } |
96 | |||
97 | ✗ | bool Property::get(void) { return impl_get(); } | |
98 | ✗ | bool Property::get(bool& v) { return impl_get(v); } | |
99 | ✗ | bool Property::get(int& v) { return impl_get(v); } | |
100 | ✗ | bool Property::get(float& v) { return impl_get(v); } | |
101 | ✗ | bool Property::get(std::string& v) { return impl_get(v); } | |
102 | ✗ | bool Property::get(osgVector2& v) { return impl_get(v); } | |
103 | ✗ | bool Property::get(osgVector3& v) { return impl_get(v); } | |
104 | ✗ | bool Property::get(osgVector4& v) { return impl_get(v); } | |
105 | 4 | bool Property::get(Configuration& v) { return impl_get(v); } | |
106 | |||
107 | ✗ | bool Property::impl_set(void) { | |
108 | ✗ | throw std::invalid_argument("Property " + name_ + " is not of type void."); | |
109 | } | ||
110 | ✗ | bool Property::impl_set(const bool& v) { return invalidType(name_, v); } | |
111 | ✗ | bool Property::impl_set(const int& v) { return invalidType(name_, v); } | |
112 | ✗ | bool Property::impl_set(const float& v) { return invalidType(name_, v); } | |
113 | ✗ | bool Property::impl_set(const std::string& v) { return invalidType(name_, v); } | |
114 | ✗ | bool Property::impl_set(const osgVector2& v) { return invalidType(name_, v); } | |
115 | ✗ | bool Property::impl_set(const osgVector3& v) { return invalidType(name_, v); } | |
116 | ✗ | bool Property::impl_set(const osgVector4& v) { return invalidType(name_, v); } | |
117 | ✗ | bool Property::impl_set(const Configuration& v) { | |
118 | ✗ | return invalidType(name_, v); | |
119 | } | ||
120 | |||
121 | ✗ | bool Property::impl_get(void) { | |
122 | ✗ | throw std::invalid_argument("Property " + name_ + " is not of type void."); | |
123 | } | ||
124 | ✗ | bool Property::impl_get(bool& v) { return invalidType(name_, v); } | |
125 | ✗ | bool Property::impl_get(int& v) { return invalidType(name_, v); } | |
126 | ✗ | bool Property::impl_get(float& v) { return invalidType(name_, v); } | |
127 | ✗ | bool Property::impl_get(std::string& v) { return invalidType(name_, v); } | |
128 | ✗ | bool Property::impl_get(osgVector2& v) { return invalidType(name_, v); } | |
129 | ✗ | bool Property::impl_get(osgVector3& v) { return invalidType(name_, v); } | |
130 | ✗ | bool Property::impl_get(osgVector4& v) { return invalidType(name_, v); } | |
131 | ✗ | bool Property::impl_get(Configuration& v) { return invalidType(name_, v); } | |
132 | |||
133 | ✗ | bool Property::set(const double& v) { return set(static_cast<float>(v)); } | |
134 | |||
135 | ✗ | bool Property::get(double& v) { | |
136 | ✗ | float _v = static_cast<float>(v); | |
137 | ✗ | bool res = get(_v); | |
138 | ✗ | v = static_cast<double>(_v); | |
139 | ✗ | return res; | |
140 | } | ||
141 | |||
142 | ✗ | bool Property::set(const QString& v) { return set(v.toStdString()); } | |
143 | |||
144 | ✗ | bool Property::get(QString& v) { | |
145 | ✗ | std::string _v = v.toStdString(); | |
146 | ✗ | bool res = get(_v); | |
147 | ✗ | v = QString::fromStdString(_v); | |
148 | ✗ | return res; | |
149 | } | ||
150 | |||
151 | ✗ | bool Property::set(const QColor& v) { | |
152 | ✗ | return set(osgVector4((float)v.redF(), (float)v.greenF(), (float)v.blueF(), | |
153 | ✗ | (float)v.alphaF())); | |
154 | } | ||
155 | |||
156 | ✗ | bool Property::get(QColor& v) { | |
157 | ✗ | osgVector4 _v((float)v.redF(), (float)v.greenF(), (float)v.blueF(), | |
158 | ✗ | (float)v.alphaF()); | |
159 | ✗ | bool res = get(_v); | |
160 | ✗ | v = qColor(_v); | |
161 | ✗ | return res; | |
162 | } | ||
163 | |||
164 | ✗ | QWidget* VoidProperty::guiEditor() { | |
165 | QString toolTip( | ||
166 | "Python:\n" | ||
167 | ✗ | " gui.callVoidProperty(nodeName,\"%1\")"); | |
168 | ✗ | QPushButton* button = new QPushButton(objectName()); | |
169 | ✗ | button->setToolTip(toolTip.arg(objectName())); | |
170 | ✗ | QObject::connect(button, &QAbstractButton::clicked, [this]() { get(); }); | |
171 | ✗ | return button; | |
172 | } | ||
173 | |||
174 | namespace details { | ||
175 | template <typename Scalar, typename QtScalar, typename QtSpinBox> | ||
176 | ✗ | inline void setSpinBoxRange(const Property* prop, QtSpinBox* sb) { | |
177 | ✗ | const Range<Scalar>* range = dynamic_cast<const Range<Scalar>*>(prop); | |
178 | ✗ | if (range) { | |
179 | ✗ | if (range->hasMin()) sb->setMinimum(static_cast<QtScalar>(range->min)); | |
180 | ✗ | if (range->hasMax()) sb->setMaximum(static_cast<QtScalar>(range->max)); | |
181 | ✗ | sb->setSingleStep(static_cast<QtScalar>(range->step)); | |
182 | #if QT_VERSION > QT_VERSION_CHECK(5, 12, 0) | ||
183 | ✗ | if (range->adaptiveDecimal) | |
184 | ✗ | sb->setStepType(QAbstractSpinBox::AdaptiveDecimalStepType); | |
185 | #endif | ||
186 | } | ||
187 | } | ||
188 | |||
189 | template <> | ||
190 | ✗ | QWidget* buildEditor<bool>(Property* prop) { | |
191 | QString toolTip( | ||
192 | "Python:\n" | ||
193 | " gui.getBoolProperty(nodeName,\"%1\")\n" | ||
194 | ✗ | " gui.setBoolProperty(nodeName,\"%1\",boolean)"); | |
195 | ✗ | QCheckBox* cb = new QCheckBox; | |
196 | ✗ | cb->setToolTip(toolTip.arg(prop->objectName())); | |
197 | bool value; | ||
198 | ✗ | /* bool success = */ prop->get(value); | |
199 | ✗ | cb->setChecked(value); | |
200 | ✗ | if (prop->hasWriteAccess()) { | |
201 | ✗ | prop->connect(cb, SIGNAL(toggled(bool)), SLOT(set(bool)), | |
202 | Qt::DirectConnection); | ||
203 | ✗ | cb->connect(prop, SIGNAL(valueChanged(bool)), SLOT(setChecked(bool)), | |
204 | Qt::DirectConnection); | ||
205 | } else | ||
206 | ✗ | cb->setEnabled(false); | |
207 | ✗ | return cb; | |
208 | } | ||
209 | |||
210 | template <> | ||
211 | ✗ | QWidget* buildEditor<std::string>(Property* prop) { | |
212 | QString toolTip( | ||
213 | "Python:\n" | ||
214 | " gui.getStringProperty(nodeName,\"%1\")\n" | ||
215 | ✗ | " gui.setStringProperty(nodeName,\"%1\",str)"); | |
216 | ✗ | QLineEdit* le = new QLineEdit; | |
217 | ✗ | le->setToolTip(toolTip.arg(prop->objectName())); | |
218 | ✗ | std::string value; | |
219 | ✗ | /* bool success = */ prop->get(value); | |
220 | ✗ | le->setText(QString::fromStdString(value)); | |
221 | ✗ | if (prop->hasWriteAccess()) { | |
222 | ✗ | prop->connect(le, SIGNAL(textChanged(QString)), SLOT(set(QString)), | |
223 | Qt::DirectConnection); | ||
224 | ✗ | le->connect(prop, SIGNAL(valueChanged(QString)), SLOT(setText(QString)), | |
225 | Qt::DirectConnection); | ||
226 | } else | ||
227 | ✗ | le->setReadOnly(true); | |
228 | ✗ | return le; | |
229 | } | ||
230 | |||
231 | template <> | ||
232 | ✗ | QWidget* buildEditor<float>(Property* prop) { | |
233 | QString toolTip( | ||
234 | "Python:\n" | ||
235 | " gui.getFloatProperty(nodeName,\"%1\")\n" | ||
236 | ✗ | " gui.setFloatProperty(nodeName,\"%1\",float)"); | |
237 | ✗ | QDoubleSpinBox* dsb = new QDoubleSpinBox; | |
238 | ✗ | dsb->setObjectName(prop->objectName()); | |
239 | ✗ | dsb->setToolTip(toolTip.arg(prop->objectName())); | |
240 | float value; | ||
241 | ✗ | /* bool success = */ prop->get(value); | |
242 | ✗ | dsb->setValue(value); | |
243 | ✗ | if (prop->hasWriteAccess()) { | |
244 | ✗ | prop->connect(dsb, SIGNAL(valueChanged(double)), SLOT(set(double)), | |
245 | Qt::DirectConnection); | ||
246 | ✗ | dsb->connect(prop, SIGNAL(valueChanged(double)), SLOT(setValue(double)), | |
247 | Qt::DirectConnection); | ||
248 | } else | ||
249 | ✗ | dsb->setEnabled(false); | |
250 | ✗ | setSpinBoxRange<float, double>(prop, dsb); | |
251 | ✗ | dsb->setDecimals(15); | |
252 | ✗ | return dsb; | |
253 | } | ||
254 | |||
255 | template <> | ||
256 | ✗ | QWidget* buildEditor<int>(Property* prop) { | |
257 | QString toolTip( | ||
258 | "Python:\n" | ||
259 | " gui.getIntProperty(nodeName,\"%1\")\n" | ||
260 | ✗ | " gui.setIntProperty(nodeName,\"%1\",int)"); | |
261 | ✗ | QSpinBox* dsb = new QSpinBox; | |
262 | ✗ | dsb->setToolTip(toolTip.arg(prop->objectName())); | |
263 | int value; | ||
264 | ✗ | /* bool success = */ prop->get(value); | |
265 | ✗ | dsb->setValue(value); | |
266 | ✗ | if (prop->hasWriteAccess()) { | |
267 | ✗ | prop->connect(dsb, SIGNAL(valueChanged(int)), SLOT(set(int)), | |
268 | Qt::DirectConnection); | ||
269 | ✗ | dsb->connect(prop, SIGNAL(valueChanged(int)), SLOT(setValue(int)), | |
270 | Qt::DirectConnection); | ||
271 | } else | ||
272 | ✗ | dsb->setEnabled(false); | |
273 | ✗ | setSpinBoxRange<int, int>(prop, dsb); | |
274 | ✗ | return dsb; | |
275 | } | ||
276 | |||
277 | template <typename VectorType> | ||
278 | struct traits {}; | ||
279 | template <> | ||
280 | struct traits<osgVector2> { | ||
281 | typedef gui::Vector2Dialog Dialog_t; | ||
282 | }; | ||
283 | template <> | ||
284 | struct traits<osgVector3> { | ||
285 | typedef gui::Vector3Dialog Dialog_t; | ||
286 | }; | ||
287 | template <> | ||
288 | struct traits<osgVector4> { | ||
289 | typedef gui::Vector4Dialog Dialog_t; | ||
290 | }; | ||
291 | |||
292 | template <typename VectorType> | ||
293 | ✗ | QWidget* buildVectorNEditor(const QString& name, int N, Property* prop) { | |
294 | typedef typename traits<VectorType>::Dialog_t Dialog_t; | ||
295 | ✗ | QString toolTip( | |
296 | "Python:\n" | ||
297 | " gui.getVector3Property(nodeName,\"%1\")\n" | ||
298 | " gui.setVector3Property(nodeName,\"%1\",int)"); | ||
299 | ✗ | QPushButton* button = new QPushButton("Set value"); | |
300 | ✗ | button->setToolTip(toolTip.arg(name)); | |
301 | |||
302 | /// Vector3 dialog should be opened in a different place | ||
303 | ✗ | Dialog_t* cfgDialog = new Dialog_t(prop); | |
304 | ✗ | cfgDialog->setValueFromProperty(prop); | |
305 | ✗ | switch (N) { | |
306 | ✗ | case 2: | |
307 | ✗ | prop->connect(cfgDialog, SIGNAL(valueChanged(osgVector2)), | |
308 | SLOT(set(osgVector2)), Qt::DirectConnection); | ||
309 | ✗ | cfgDialog->connect(prop, SIGNAL(valueChanged(osgVector2)), | |
310 | SLOT(set(osgVector2)), Qt::DirectConnection); | ||
311 | ✗ | break; | |
312 | ✗ | case 3: | |
313 | ✗ | prop->connect(cfgDialog, SIGNAL(valueChanged(osgVector3)), | |
314 | SLOT(set(osgVector3)), Qt::DirectConnection); | ||
315 | ✗ | cfgDialog->connect(prop, SIGNAL(valueChanged(osgVector3)), | |
316 | SLOT(set(osgVector3)), Qt::DirectConnection); | ||
317 | ✗ | break; | |
318 | ✗ | case 4: | |
319 | ✗ | prop->connect(cfgDialog, SIGNAL(valueChanged(osgVector4)), | |
320 | SLOT(set(osgVector4)), Qt::DirectConnection); | ||
321 | ✗ | cfgDialog->connect(prop, SIGNAL(valueChanged(osgVector4)), | |
322 | SLOT(set(osgVector4)), Qt::DirectConnection); | ||
323 | ✗ | break; | |
324 | ✗ | default: | |
325 | ✗ | break; | |
326 | } | ||
327 | ✗ | cfgDialog->setProperty("propertyName", name); | |
328 | ✗ | cfgDialog->connect(button, SIGNAL(clicked()), SLOT(show())); | |
329 | |||
330 | ✗ | return button; | |
331 | } | ||
332 | |||
333 | template <> | ||
334 | ✗ | QWidget* buildEditor<osgVector2>(Property* prop) { | |
335 | ✗ | if (!prop->hasWriteAccess()) return NULL; | |
336 | ✗ | return buildVectorNEditor<osgVector2>(prop->objectName(), 2, prop); | |
337 | } | ||
338 | |||
339 | template <> | ||
340 | ✗ | QWidget* buildEditor<osgVector3>(Property* prop) { | |
341 | ✗ | if (!prop->hasWriteAccess()) return NULL; | |
342 | ✗ | return buildVectorNEditor<osgVector3>(prop->objectName(), 3, prop); | |
343 | } | ||
344 | |||
345 | ✗ | QWidget* buildColorEditor(QString& name, Property* prop) { | |
346 | QString toolTip( | ||
347 | "Python:\n" | ||
348 | " gui.getColorProperty(nodeName,\"%1\")\n" | ||
349 | ✗ | " gui.setColorProperty(nodeName,\"%1\",int)"); | |
350 | ✗ | QColor color; | |
351 | ✗ | /* bool success = */ prop->get(color); | |
352 | |||
353 | ✗ | QPushButton* button = new QPushButton("Select color"); | |
354 | ✗ | button->setToolTip(toolTip.arg(name)); | |
355 | // Set icon for current color value | ||
356 | |||
357 | /// Color dialog should be opened in a different place | ||
358 | ✗ | QColorDialog* colorDialog = new QColorDialog(color, button); | |
359 | ✗ | colorDialog->setOption(QColorDialog::ShowAlphaChannel, true); | |
360 | ✗ | colorDialog->setOption(QColorDialog::NoButtons, true); | |
361 | |||
362 | ✗ | colorDialog->setProperty("propertyName", name); | |
363 | ✗ | colorDialog->connect(button, SIGNAL(clicked()), SLOT(open())); | |
364 | |||
365 | ✗ | prop->connect(colorDialog, SIGNAL(currentColorChanged(QColor)), | |
366 | SLOT(set(QColor)), Qt::DirectConnection); | ||
367 | #if __cplusplus >= 201103L and (QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)) | ||
368 | ✗ | QObject::connect(prop, QOverload<const QColor&>::of(&Property::valueChanged), | |
369 | colorDialog, &QColorDialog::setCurrentColor, | ||
370 | // SLOT(set(QColor)), | ||
371 | Qt::DirectConnection); | ||
372 | #endif | ||
373 | |||
374 | ✗ | return button; | |
375 | } | ||
376 | |||
377 | template <> | ||
378 | ✗ | QWidget* buildEditor<osgVector4>(Property* prop) { | |
379 | ✗ | if (!prop->hasWriteAccess()) return NULL; | |
380 | |||
381 | ✗ | QString name(prop->objectName()); | |
382 | ✗ | if (name.contains("color", Qt::CaseInsensitive)) | |
383 | ✗ | return buildColorEditor(name, prop); | |
384 | |||
385 | ✗ | return buildVectorNEditor<osgVector4>(prop->objectName(), 4, prop); | |
386 | } | ||
387 | |||
388 | template <> | ||
389 | ✗ | QWidget* buildEditor<Configuration>(Property* prop) { | |
390 | // We could use buildVectorNEditor. The only bad point is that tooltip | ||
391 | // which will say setVector3Property instead of setConfigurationProperty... | ||
392 | ✗ | if (!prop->hasWriteAccess()) return NULL; | |
393 | |||
394 | ✗ | QString name(QString::fromStdString(prop->name())); | |
395 | ✗ | QPushButton* button = new QPushButton("Set transform"); | |
396 | |||
397 | /// Color dialog should be opened in a different place | ||
398 | ✗ | gui::ConfigurationDialog* cfgDialog = new gui::ConfigurationDialog(prop); | |
399 | ✗ | cfgDialog->setValueFromProperty(prop); | |
400 | ✗ | prop->connect(cfgDialog, SIGNAL(valueChanged(Configuration)), | |
401 | SLOT(set(Configuration)), Qt::DirectConnection); | ||
402 | ✗ | cfgDialog->connect(prop, SIGNAL(valueChanged(Configuration)), | |
403 | SLOT(set(Configuration)), Qt::DirectConnection); | ||
404 | |||
405 | ✗ | cfgDialog->setProperty("propertyName", name); | |
406 | ✗ | cfgDialog->connect(button, SIGNAL(clicked()), SLOT(show())); | |
407 | |||
408 | ✗ | return button; | |
409 | } | ||
410 | } // namespace details | ||
411 | |||
412 | ✗ | int MetaEnum::from_string(const std::string& s) { | |
413 | ✗ | for (std::size_t i = 0; i < names.size(); ++i) | |
414 | ✗ | if (s == names[i]) return values[i]; | |
415 | ✗ | throw std::invalid_argument("Unknown name of enum (" + type + "): " + s); | |
416 | } | ||
417 | ✗ | std::string MetaEnum::to_string(const int& v) { | |
418 | ✗ | for (std::size_t i = 0; i < names.size(); ++i) | |
419 | ✗ | if (v == values[i]) return names[i]; | |
420 | ✗ | throw std::invalid_argument("Unknown enum value (" + type + ")"); | |
421 | } | ||
422 | |||
423 | 1 | MetaEnum* visibilityModeEnum() { | |
424 |
2/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
|
1 | static MetaEnum vm; |
425 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (vm.type.size() == 0) { |
426 | 1 | vm.type = "VisibilityMode"; | |
427 |
2/4✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
|
1 | vm.names.push_back("ON"); |
428 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | vm.values.push_back(VISIBILITY_ON); |
429 |
2/4✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
|
1 | vm.names.push_back("ALWAYS_ON_TOP"); |
430 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | vm.values.push_back(ALWAYS_ON_TOP); |
431 |
2/4✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
|
1 | vm.names.push_back("OFF"); |
432 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | vm.values.push_back(VISIBILITY_OFF); |
433 | } | ||
434 | 1 | return &vm; | |
435 | } | ||
436 | 1 | MetaEnum* wireFrameModeEnum() { | |
437 |
2/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
|
1 | static MetaEnum wm; |
438 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (wm.type.size() == 0) { |
439 | 1 | wm.type = "VisibilityMode"; | |
440 |
2/4✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
|
1 | wm.names.push_back("FILL"); |
441 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | wm.values.push_back(FILL); |
442 |
2/4✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
|
1 | wm.names.push_back("WIREFRAME"); |
443 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | wm.values.push_back(WIREFRAME); |
444 |
2/4✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
|
1 | wm.names.push_back("FILL_AND_WIREFRAME"); |
445 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | wm.values.push_back(FILL_AND_WIREFRAME); |
446 | } | ||
447 | 1 | return &wm; | |
448 | } | ||
449 | 1 | MetaEnum* lightingModeEnum() { | |
450 |
2/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
|
1 | static MetaEnum lm; |
451 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (lm.type.size() == 0) { |
452 | 1 | lm.type = "LightingMode"; | |
453 |
2/4✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
|
1 | lm.names.push_back("ON"); |
454 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | lm.values.push_back(LIGHT_INFLUENCE_ON); |
455 |
2/4✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
|
1 | lm.names.push_back("OFF"); |
456 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | lm.values.push_back(LIGHT_INFLUENCE_OFF); |
457 | } | ||
458 | 1 | return &lm; | |
459 | } | ||
460 | ✗ | MetaEnum* glImmediateModeEnum() { | |
461 | ✗ | static MetaEnum lm; | |
462 | ✗ | if (lm.type.size() == 0) { | |
463 | ✗ | lm.type = "GLImmediateMode"; | |
464 | ✗ | lm.names.push_back("GL_LINES"); | |
465 | ✗ | lm.values.push_back(GL_LINES); | |
466 | ✗ | lm.names.push_back("GL_POINTS"); | |
467 | ✗ | lm.values.push_back(GL_POINTS); | |
468 | ✗ | lm.names.push_back("GL_LINE_STRIP"); | |
469 | ✗ | lm.values.push_back(GL_LINE_STRIP); | |
470 | ✗ | lm.names.push_back("GL_LINE_LOOP"); | |
471 | ✗ | lm.values.push_back(GL_LINE_LOOP); | |
472 | ✗ | lm.names.push_back("GL_POLYGON"); | |
473 | ✗ | lm.values.push_back(GL_POLYGON); | |
474 | ✗ | lm.names.push_back("GL_QUADS"); | |
475 | ✗ | lm.values.push_back(GL_QUADS); | |
476 | ✗ | lm.names.push_back("GL_QUAD_STRIP"); | |
477 | ✗ | lm.values.push_back(GL_QUAD_STRIP); | |
478 | ✗ | lm.names.push_back("GL_TRIANGLE_STRIP"); | |
479 | ✗ | lm.values.push_back(GL_TRIANGLE_STRIP); | |
480 | ✗ | lm.names.push_back("GL_TRIANGLES"); | |
481 | ✗ | lm.values.push_back(GL_TRIANGLES); | |
482 | ✗ | lm.names.push_back("GL_TRIANGLE_FAN"); | |
483 | ✗ | lm.values.push_back(GL_TRIANGLE_FAN); | |
484 | } | ||
485 | ✗ | return &lm; | |
486 | } | ||
487 | |||
488 | ✗ | bool EnumProperty::impl_set(const int& value) { | |
489 | ✗ | const MetaEnum& me = *metaEnum(); | |
490 | ✗ | for (std::size_t i = 0; i < me.values.size(); ++i) | |
491 | ✗ | if (me.values[i] == value) return IntProperty::impl_set(value); | |
492 | ✗ | std::ostringstream oss; | |
493 | ✗ | oss << "Invalid value " << value << " for enum " << me.type | |
494 | << ". " | ||
495 | ✗ | "Possible values are "; | |
496 | ✗ | for (std::size_t i = 0; i < me.values.size(); ++i) | |
497 | ✗ | oss << me.names[i] << " (" << me.values[i] << "), "; | |
498 | ✗ | throw std::invalid_argument(oss.str()); | |
499 | return false; | ||
500 | } | ||
501 | |||
502 | ✗ | bool EnumProperty::impl_set(const std::string& value) { | |
503 | ✗ | const MetaEnum& me = *metaEnum(); | |
504 | ✗ | for (std::size_t i = 0; i < me.names.size(); ++i) | |
505 | ✗ | if (me.names[i] == value) return IntProperty::impl_set(me.values[i]); | |
506 | ✗ | std::ostringstream oss; | |
507 | ✗ | oss << "Invalid value " << value << " for enum " << me.type | |
508 | << ". " | ||
509 | ✗ | "Possible values are "; | |
510 | ✗ | for (std::size_t i = 0; i < me.values.size(); ++i) | |
511 | ✗ | oss << me.names[i] << " (" << me.values[i] << "), "; | |
512 | ✗ | throw std::invalid_argument(oss.str()); | |
513 | return false; | ||
514 | } | ||
515 | |||
516 | ✗ | bool EnumProperty::impl_get(int& v) { return IntProperty::impl_get(v); } | |
517 | |||
518 | ✗ | bool EnumProperty::impl_get(std::string& str) { | |
519 | int value; | ||
520 | ✗ | if (!IntProperty::impl_get(value)) return false; | |
521 | ✗ | const MetaEnum& me = *metaEnum(); | |
522 | ✗ | for (std::size_t i = 0; i < me.names.size(); ++i) | |
523 | ✗ | if (me.values[i] == value) { | |
524 | ✗ | str = me.names[i]; | |
525 | ✗ | return true; | |
526 | } | ||
527 | ✗ | return false; | |
528 | } | ||
529 | |||
530 | ✗ | QWidget* EnumProperty::guiEditor() { | |
531 | QString toolTip( | ||
532 | "Python:\n" | ||
533 | "As an integer:\n" | ||
534 | " gui.getIntProperty(nodeName,\"%1\")\n" | ||
535 | " gui.setIntProperty(nodeName,\"%1\",int)\n" | ||
536 | "or as a string:\n" | ||
537 | " gui.getStringProperty(nodeName,\"%1\")\n" | ||
538 | ✗ | " gui.setStringProperty(nodeName,\"%1\",string)"); | |
539 | |||
540 | ✗ | QComboBox* cb = new QComboBox; | |
541 | ✗ | cb->setToolTip(toolTip.arg(name().c_str())); | |
542 | ✗ | std::string value; | |
543 | ✗ | /* bool success = */ get(value); | |
544 | ✗ | for (unsigned i = 0; i < metaEnum_->values.size(); ++i) { | |
545 | ✗ | cb->addItem(metaEnum_->names[i].c_str(), metaEnum_->values[i]); | |
546 | ✗ | if (value == metaEnum_->names[i]) cb->setCurrentIndex(i); | |
547 | } | ||
548 | ✗ | if (hasWriteAccess()) { | |
549 | ✗ | connect(cb, SIGNAL(currentTextChanged(QString)), SLOT(set(QString)), | |
550 | Qt::DirectConnection); | ||
551 | // On Qt4, the combo box will not be updated. | ||
552 | #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0)) | ||
553 | ✗ | cb->connect(this, SIGNAL(valueChanged(QString)), | |
554 | SLOT(setCurrentText(QString)), Qt::DirectConnection); | ||
555 | #endif | ||
556 | } else | ||
557 | ✗ | cb->setEnabled(false); | |
558 | ✗ | return cb; | |
559 | } | ||
560 | |||
561 | ✗ | Property* Properties::property(const std::string& name) const { | |
562 | ✗ | PropertyMap_t::const_iterator _prop = properties_.find(name); | |
563 | ✗ | if (_prop == properties_.end()) | |
564 | ✗ | throw std::invalid_argument("Unknown property " + name); | |
565 | ✗ | return _prop->second.p; | |
566 | } | ||
567 | |||
568 | ✗ | bool Properties::hasProperty(const std::string& name) const { | |
569 | ✗ | PropertyMap_t::const_iterator _prop = properties_.find(name); | |
570 | ✗ | return (_prop != properties_.end()); | |
571 | } | ||
572 | |||
573 | 12 | void Properties::addProperty(const PropertyPtr_t& prop) { | |
574 | 12 | addProperty(prop->name(), prop); | |
575 | 12 | } | |
576 | |||
577 | 12 | void Properties::addProperty(const std::string& name, | |
578 | const PropertyPtr_t& prop) { | ||
579 | 12 | Wrapper wrapped(prop); | |
580 | std::pair<PropertyMap_t::iterator, bool> res = | ||
581 |
2/4✓ Branch 3 taken 12 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 12 times.
✗ Branch 7 not taken.
|
12 | properties_.insert(std::make_pair(name, Wrapper(prop))); |
582 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 11 times.
|
12 | if (!res.second) res.first->second = prop; |
583 | 12 | } | |
584 | |||
585 | 2 | void Properties::addProperty(Property* prop) { | |
586 | 2 | addProperty(prop->name(), prop); | |
587 | 2 | } | |
588 | |||
589 | 2 | void Properties::addProperty(const std::string& name, Property* prop) { | |
590 | 2 | Wrapper wrapped(prop); | |
591 | std::pair<PropertyMap_t::iterator, bool> res = | ||
592 |
2/4✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
|
2 | properties_.insert(std::make_pair(name, Wrapper(prop))); |
593 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (!res.second) res.first->second = prop; |
594 | 2 | } | |
595 | |||
596 | ✗ | void addPropertyEditor(QFormLayout* l, const std::string& _name, | |
597 | Property* prop) { | ||
598 | ✗ | QString name(QString::fromStdString(_name)); | |
599 | ✗ | QWidget* field = prop->guiEditor(); | |
600 | ✗ | if (field != NULL) { | |
601 | ✗ | QLabel* label(new QLabel(name + ':')); | |
602 | ✗ | label->setToolTip(field->toolTip()); | |
603 | ✗ | field->setProperty("propertyName", name); | |
604 | ✗ | l->addRow(label, field); | |
605 | } else { | ||
606 | ✗ | qDebug() << "Unhandled property" << name << "of type" | |
607 | << prop->type().c_str() << "."; | ||
608 | } | ||
609 | } | ||
610 | |||
611 | ✗ | QWidget* Properties::guiEditor() { | |
612 | ✗ | QWidget* editor = new QWidget; | |
613 | ✗ | QFormLayout* l = new QFormLayout(editor); | |
614 | |||
615 | ✗ | for (PropertyMap_t::const_iterator _prop = properties_.begin(); | |
616 | ✗ | _prop != properties_.end(); ++_prop) { | |
617 | ✗ | if (!_prop->second->hasReadAccess()) continue; | |
618 | ✗ | addPropertyEditor(l, _prop->first, _prop->second.p); | |
619 | } | ||
620 | ✗ | return editor; | |
621 | } | ||
622 | } /* namespace viewer */ | ||
623 | |||
624 | } /* namespace gepetto */ | ||
625 |