GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/node-property.cpp Lines: 63 356 17.7 %
Date: 2024-04-14 11:13:22 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
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 */