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 |
✓✗ |
14 |
Property::Property(const std::string& name) : QObject(NULL), name_(name) { |
51 |
✓✗✓✗
|
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 |
✓✗✗✓
|
8 |
if (!prop.hasReadAccess()) return true; |
57 |
|
8 |
T current; |
58 |
✓✗✗✓
|
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 |
✓✓✓✗
|
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 |
✓✗✓✗
|
1 |
static MetaEnum vm; |
425 |
✓✗ |
1 |
if (vm.type.size() == 0) { |
426 |
|
1 |
vm.type = "VisibilityMode"; |
427 |
✓✗✓✗
|
1 |
vm.names.push_back("ON"); |
428 |
✓✗ |
1 |
vm.values.push_back(VISIBILITY_ON); |
429 |
✓✗✓✗
|
1 |
vm.names.push_back("ALWAYS_ON_TOP"); |
430 |
✓✗ |
1 |
vm.values.push_back(ALWAYS_ON_TOP); |
431 |
✓✗✓✗
|
1 |
vm.names.push_back("OFF"); |
432 |
✓✗ |
1 |
vm.values.push_back(VISIBILITY_OFF); |
433 |
|
|
} |
434 |
|
1 |
return &vm; |
435 |
|
|
} |
436 |
|
1 |
MetaEnum* wireFrameModeEnum() { |
437 |
✓✗✓✗
|
1 |
static MetaEnum wm; |
438 |
✓✗ |
1 |
if (wm.type.size() == 0) { |
439 |
|
1 |
wm.type = "VisibilityMode"; |
440 |
✓✗✓✗
|
1 |
wm.names.push_back("FILL"); |
441 |
✓✗ |
1 |
wm.values.push_back(FILL); |
442 |
✓✗✓✗
|
1 |
wm.names.push_back("WIREFRAME"); |
443 |
✓✗ |
1 |
wm.values.push_back(WIREFRAME); |
444 |
✓✗✓✗
|
1 |
wm.names.push_back("FILL_AND_WIREFRAME"); |
445 |
✓✗ |
1 |
wm.values.push_back(FILL_AND_WIREFRAME); |
446 |
|
|
} |
447 |
|
1 |
return &wm; |
448 |
|
|
} |
449 |
|
1 |
MetaEnum* lightingModeEnum() { |
450 |
✓✗✓✗
|
1 |
static MetaEnum lm; |
451 |
✓✗ |
1 |
if (lm.type.size() == 0) { |
452 |
|
1 |
lm.type = "LightingMode"; |
453 |
✓✗✓✗
|
1 |
lm.names.push_back("ON"); |
454 |
✓✗ |
1 |
lm.values.push_back(LIGHT_INFLUENCE_ON); |
455 |
✓✗✓✗
|
1 |
lm.names.push_back("OFF"); |
456 |
✓✗ |
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 |
|
24 |
Wrapper wrapped(prop); |
580 |
|
|
std::pair<PropertyMap_t::iterator, bool> res = |
581 |
✓✗✓✗
|
12 |
properties_.insert(std::make_pair(name, Wrapper(prop))); |
582 |
✓✓ |
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 |
|
4 |
Wrapper wrapped(prop); |
591 |
|
|
std::pair<PropertyMap_t::iterator, bool> res = |
592 |
✓✗✓✗
|
2 |
properties_.insert(std::make_pair(name, Wrapper(prop))); |
593 |
✗✓ |
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 */ |