GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/node-property.cpp Lines: 54 326 16.6 %
Date: 2020-05-14 11:23:33 Branches: 49 1072 4.6 %

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 <sstream>
20
21
#include <QCheckBox>
22
#include <QColorDialog>
23
#include <QComboBox>
24
#include <QDebug>
25
#include <QDoubleSpinBox>
26
#include <QFormLayout>
27
#include <QLabel>
28
#include <QLineEdit>
29
#include <QPushButton>
30
31
#include <gepetto/gui/dialog/configuration.hh>
32
33
namespace gepetto {
34
namespace viewer {
35
  struct Initializer {
36
1
    Initializer() {
37
1
      qRegisterMetaType<osgVector2>("osgVector2");
38
1
      qRegisterMetaType<osgVector3>("osgVector3");
39
1
      qRegisterMetaType<osgVector4>("osgVector4");
40
1
      qRegisterMetaType<Configuration>("Configuration");
41
1
    }
42
  };
43
44
1
  static Initializer initializer;
45
46
  template <typename T> inline bool invalidType(const std::string& name, T) {
47
    throw std::invalid_argument ("Property " + name + " is not of type " + details::property_type<T>::to_string());
48
  }
49
50
13
  Property::Property(const std::string& name) :
51
    QObject(NULL),
52
13
    name_ (name)
53
  {
54

13
    setObjectName(QString::fromStdString(name));
55
13
  }
56
57
  template<typename T>
58
4
  bool check_if_value_changed(Property& prop, const T& v)
59
  {
60








4
    if(!prop.hasReadAccess()) return true;
61
4
    T current;
62








4
    if(!prop.get(current)) return true;
63
    //std::cout << "current " << current << std::endl;
64
4
    return current != v;
65
  }
66
67
  inline QColor qColor(const osgVector4& v)
68
  {
69
    return QColor::fromRgbF((qreal)v[0], (qreal)v[1], (qreal)v[2], (qreal)v[3]);
70
  }
71
72
#define SET_IMPLEMENTATION(v,emit2)                                            \
73
  if (!check_if_value_changed(*this,v)) return true;                           \
74
  if (impl_set(v)) { emit valueChanged(v); emit2; return true; }               \
75
  return false
76
77
  bool Property::set(      void             ) { bool b = impl_set( ); if (b) { emit valueChanged( ); } return b; }
78
  bool Property::set(const bool          & v) { SET_IMPLEMENTATION(v,); }
79
  bool Property::set(const int           & v) { SET_IMPLEMENTATION(v,); }
80
  bool Property::set(const float         & v) { SET_IMPLEMENTATION(v, emit valueChanged(static_cast<double>(v))); }
81
  bool Property::set(const std::string   & v) { SET_IMPLEMENTATION(v, emit valueChanged(QString::fromStdString(v))); }
82
  bool Property::set(const osgVector2    & v) { SET_IMPLEMENTATION(v,); }
83
  bool Property::set(const osgVector3    & v) { SET_IMPLEMENTATION(v,); }
84
  bool Property::set(const osgVector4    & v) { SET_IMPLEMENTATION(v, emit valueChanged(qColor(v))); }
85

4
  bool Property::set(const Configuration & v) { SET_IMPLEMENTATION(v,); }
86
87
  bool Property::get(void             ) { return impl_get( ); }
88
  bool Property::get(bool          & v) { return impl_get(v); }
89
  bool Property::get(int           & v) { return impl_get(v); }
90
  bool Property::get(float         & v) { return impl_get(v); }
91
  bool Property::get(std::string   & v) { return impl_get(v); }
92
  bool Property::get(osgVector2    & v) { return impl_get(v); }
93
  bool Property::get(osgVector3    & v) { return impl_get(v); }
94
  bool Property::get(osgVector4    & v) { return impl_get(v); }
95
4
  bool Property::get(Configuration & v) { return impl_get(v); }
96
97
  bool Property::impl_set(      void             ) { throw std::invalid_argument ("Property " + name_ + " is not of type void."); }
98
  bool Property::impl_set(const bool          & v) { return invalidType(name_,v); }
99
  bool Property::impl_set(const int           & v) { return invalidType(name_,v); }
100
  bool Property::impl_set(const float         & v) { return invalidType(name_,v); }
101
  bool Property::impl_set(const std::string   & v) { return invalidType(name_,v); }
102
  bool Property::impl_set(const osgVector2    & v) { return invalidType(name_,v); }
103
  bool Property::impl_set(const osgVector3    & v) { return invalidType(name_,v); }
104
  bool Property::impl_set(const osgVector4    & v) { return invalidType(name_,v); }
105
  bool Property::impl_set(const Configuration & v) { return invalidType(name_,v); }
106
107
  bool Property::impl_get(void             ) { throw std::invalid_argument ("Property " + name_ + " is not of type void."); }
108
  bool Property::impl_get(bool          & v) { return invalidType(name_,v); }
109
  bool Property::impl_get(int           & v) { return invalidType(name_,v); }
110
  bool Property::impl_get(float         & v) { return invalidType(name_,v); }
111
  bool Property::impl_get(std::string   & v) { return invalidType(name_,v); }
112
  bool Property::impl_get(osgVector2    & v) { return invalidType(name_,v); }
113
  bool Property::impl_get(osgVector3    & v) { return invalidType(name_,v); }
114
  bool Property::impl_get(osgVector4    & v) { return invalidType(name_,v); }
115
  bool Property::impl_get(Configuration & v) { return invalidType(name_,v); }
116
117
  bool Property::set(const double& v) { return set(static_cast<float>(v)); }
118
119
  bool Property::get(double      & v)
120
  {
121
    float _v = static_cast<float>(v);
122
    bool res = get (_v);
123
    v = static_cast<double>(_v);
124
    return res;
125
  }
126
127
  bool Property::set(const QString& v) { return set(v.toStdString()); }
128
129
  bool Property::get(QString      & v)
130
  {
131
    std::string _v = v.toStdString();
132
    bool res = get (_v);
133
    v = QString::fromStdString(_v);
134
    return res;
135
  }
136
137
  bool Property::set(const QColor& v)
138
  {
139
    return set(osgVector4((float)v.redF(), (float)v.greenF(), (float)v.blueF(),
140
          (float)v.alphaF()));
141
  }
142
143
  bool Property::get(QColor      & v)
144
  {
145
    osgVector4 _v ((float)v.redF(), (float)v.greenF(), (float)v.blueF(),
146
          (float)v.alphaF());
147
    bool res = get (_v);
148
    v = qColor(_v);
149
    return res;
150
  }
151
152
  QWidget* VoidProperty::guiEditor ()
153
  {
154
    QString toolTip (
155
        "Python:\n"
156
        "  gui.callVoidProperty(nodeName,\"%1\")");
157
    QPushButton* button = new QPushButton(objectName());
158
    button->setToolTip (toolTip.arg(objectName()));
159
    connect(button, SIGNAL(clicked()), SLOT(set(void)));
160
    return button;
161
  }
162
163
  namespace details {
164
    template <typename Scalar, typename QtScalar, typename QtSpinBox>
165
    inline void setSpinBoxRange(const Property* prop, QtSpinBox* sb)
166
    {
167
      const Range<Scalar>* range = dynamic_cast<const Range<Scalar>*>(prop);
168
      if (range) {
169
        if (range->hasMin()) sb->setMinimum(static_cast<QtScalar>(range->min));
170
        if (range->hasMax()) sb->setMaximum(static_cast<QtScalar>(range->max));
171
        sb->setSingleStep(static_cast<QtScalar>(range->step));
172
#if QT_VERSION > QT_VERSION_CHECK(5, 12, 0)
173
        if (range->adaptiveDecimal) sb->setStepType (QAbstractSpinBox::AdaptiveDecimalStepType);
174
#endif
175
      }
176
    }
177
178
    template <> QWidget* buildEditor<bool         > (Property* prop)
179
    {
180
      QString toolTip (
181
          "Python:\n"
182
          "  gui.getBoolProperty(nodeName,\"%1\")\n"
183
          "  gui.setBoolProperty(nodeName,\"%1\",boolean)");
184
      QCheckBox* cb = new QCheckBox;
185
      cb->setToolTip (toolTip.arg(prop->objectName()));
186
      bool value;
187
      /* bool success = */ prop->get(value);
188
      cb->setChecked(value);
189
      if (prop->hasWriteAccess()) {
190
        prop->connect(cb, SIGNAL(toggled(bool)), SLOT(set(bool)), Qt::DirectConnection);
191
        cb->connect(prop, SIGNAL(valueChanged(bool)), SLOT(setChecked(bool)), Qt::DirectConnection);
192
      } else
193
        cb->setEnabled(false);
194
      return cb;
195
    }
196
197
    template <> QWidget* buildEditor<std::string  > (Property* prop)
198
    {
199
      QString toolTip (
200
          "Python:\n"
201
          "  gui.getStringProperty(nodeName,\"%1\")\n"
202
          "  gui.setStringProperty(nodeName,\"%1\",str)");
203
      QLineEdit* le = new QLineEdit;
204
      le->setToolTip (toolTip.arg(prop->objectName()));
205
      std::string value;
206
      /* bool success = */ prop->get(value);
207
      le->setText(QString::fromStdString(value));
208
      if (prop->hasWriteAccess()) {
209
        prop->connect(le, SIGNAL(textChanged(QString)), SLOT(set(QString)), Qt::DirectConnection);
210
        le->connect(prop, SIGNAL(valueChanged(QString)), SLOT(setText(QString)), Qt::DirectConnection);
211
      } else
212
        le->setReadOnly(true);
213
      return le;
214
    }
215
216
    template <> QWidget* buildEditor<float> (Property* prop)
217
    {
218
      QString toolTip (
219
          "Python:\n"
220
          "  gui.getFloatProperty(nodeName,\"%1\")\n"
221
          "  gui.setFloatProperty(nodeName,\"%1\",float)");
222
      QDoubleSpinBox* dsb = new QDoubleSpinBox;
223
      dsb->setObjectName(prop->objectName());
224
      dsb->setToolTip (toolTip.arg(prop->objectName()));
225
      float value;
226
      /* bool success = */ prop->get(value);
227
      dsb->setValue(value);
228
      if (prop->hasWriteAccess()) {
229
        prop->connect(dsb, SIGNAL(valueChanged(double)), SLOT(set(double)), Qt::DirectConnection);
230
        dsb->connect(prop, SIGNAL(valueChanged(double)), SLOT(setValue(double)), Qt::DirectConnection);
231
      } else
232
        dsb->setEnabled(false);
233
      setSpinBoxRange<float, double>(prop, dsb);
234
      return dsb;
235
    }
236
237
    template <> QWidget* buildEditor<int          > (Property* prop)
238
    {
239
      QString toolTip (
240
          "Python:\n"
241
          "  gui.getIntProperty(nodeName,\"%1\")\n"
242
          "  gui.setIntProperty(nodeName,\"%1\",int)");
243
      QSpinBox* dsb = new QSpinBox;
244
      dsb->setToolTip (toolTip.arg(prop->objectName()));
245
      int value;
246
      /* bool success = */ prop->get(value);
247
      dsb->setValue(value);
248
      if (prop->hasWriteAccess()) {
249
        prop->connect(dsb, SIGNAL(valueChanged(int)), SLOT(set(int)), Qt::DirectConnection);
250
        dsb->connect(prop, SIGNAL(valueChanged(int)), SLOT(setValue(int)), Qt::DirectConnection);
251
      } else
252
        dsb->setEnabled(false);
253
      setSpinBoxRange<int, int>(prop, dsb);
254
      return dsb;
255
    }
256
257
    template<typename VectorType> struct traits { };
258
    template<> struct traits <osgVector2> { typedef gui::Vector2Dialog Dialog_t; };
259
    template<> struct traits <osgVector3> { typedef gui::Vector3Dialog Dialog_t; };
260
    template<> struct traits <osgVector4> { typedef gui::Vector4Dialog Dialog_t; };
261
262
    template<typename VectorType>
263
    QWidget* buildVectorNEditor (const QString& name, int N, Property* prop)
264
    {
265
      typedef typename traits<VectorType>::Dialog_t Dialog_t;
266
        QString toolTip (
267
          "Python:\n"
268
          "  gui.getVector3Property(nodeName,\"%1\")\n"
269
          "  gui.setVector3Property(nodeName,\"%1\",int)");
270
      QPushButton* button = new QPushButton("Set value");
271
      button->setToolTip (toolTip.arg(name));
272
273
      /// Vector3 dialog should be opened in a different place
274
      Dialog_t* cfgDialog = new Dialog_t(prop);
275
      cfgDialog->setValueFromProperty(prop);
276
      switch (N) {
277
        case 2:
278
          prop->connect (cfgDialog, SIGNAL(valueChanged (osgVector2)),
279
              SLOT(set(osgVector2)), Qt::DirectConnection);
280
          cfgDialog->connect (prop, SIGNAL(valueChanged (osgVector2)),
281
              SLOT(set(osgVector2)), Qt::DirectConnection);
282
          break;
283
        case 3:
284
          prop->connect (cfgDialog, SIGNAL(valueChanged (osgVector3)),
285
              SLOT(set(osgVector3)), Qt::DirectConnection);
286
          cfgDialog->connect (prop, SIGNAL(valueChanged (osgVector3)),
287
              SLOT(set(osgVector3)), Qt::DirectConnection);
288
          break;
289
        case 4:
290
          prop->connect (cfgDialog, SIGNAL(valueChanged (osgVector4)),
291
              SLOT(set(osgVector4)), Qt::DirectConnection);
292
          cfgDialog->connect (prop, SIGNAL(valueChanged (osgVector4)),
293
              SLOT(set(osgVector4)), Qt::DirectConnection);
294
          break;
295
        default:
296
          break;
297
      }
298
      cfgDialog->setProperty("propertyName", name);
299
      cfgDialog->connect(button, SIGNAL(clicked()), SLOT(show()));
300
301
      return button;
302
    }
303
304
    template <> QWidget* buildEditor<osgVector2   > (Property* prop)
305
    {
306
      if (!prop->hasWriteAccess()) return NULL;
307
      return buildVectorNEditor<osgVector2> (prop->objectName(), 2, prop);
308
    }
309
310
    template <> QWidget* buildEditor<osgVector3   > (Property* prop)
311
    {
312
      if (!prop->hasWriteAccess()) return NULL;
313
      return buildVectorNEditor<osgVector3> (prop->objectName(), 3, prop);
314
    }
315
316
    QWidget* buildColorEditor (QString& name, Property* prop)
317
    {
318
      QString toolTip (
319
          "Python:\n"
320
          "  gui.getColorProperty(nodeName,\"%1\")\n"
321
          "  gui.setColorProperty(nodeName,\"%1\",int)");
322
      QColor color;
323
      /* bool success = */ prop->get(color);
324
325
      QPushButton* button = new QPushButton("Select color");
326
      button->setToolTip (toolTip.arg(name));
327
      // Set icon for current color value
328
329
      /// Color dialog should be opened in a different place
330
      QColorDialog* colorDialog = new QColorDialog(color, button);
331
      colorDialog->setOption(QColorDialog::ShowAlphaChannel, true);
332
      colorDialog->setOption(QColorDialog::NoButtons, true);
333
334
      colorDialog->setProperty("propertyName", name);
335
      colorDialog->connect(button, SIGNAL(clicked()), SLOT(open()));
336
337
      prop->connect (colorDialog, SIGNAL(currentColorChanged(QColor)), SLOT(set(QColor)), Qt::DirectConnection);
338
#if __cplusplus >= 201103L and (QT_VERSION >= QT_VERSION_CHECK(5,7,0))
339
      QObject::connect (prop,
340
          QOverload<const QColor &>::of(&Property::valueChanged),
341
            colorDialog, &QColorDialog::setCurrentColor,
342
          //SLOT(set(QColor)),
343
          Qt::DirectConnection);
344
#endif
345
346
      return button;
347
    }
348
349
    template <> QWidget* buildEditor<osgVector4   > (Property* prop)
350
    {
351
      if (!prop->hasWriteAccess()) return NULL;
352
353
      QString name (prop->objectName());
354
      if (name.contains ("color", Qt::CaseInsensitive))
355
        return buildColorEditor(name, prop);
356
357
      return buildVectorNEditor<osgVector4> (prop->objectName(), 4, prop);
358
    }
359
360
    template <> QWidget* buildEditor<Configuration> (Property* prop)
361
    {
362
      // We could use buildVectorNEditor. The only bad point is that tooltip
363
      // which will say setVector3Property instead of setConfigurationProperty...
364
      if (!prop->hasWriteAccess()) return NULL;
365
366
      QString name (QString::fromStdString(prop->name()));
367
      QPushButton* button = new QPushButton("Set transform");
368
369
      /// Color dialog should be opened in a different place
370
      gui::ConfigurationDialog* cfgDialog = new gui::ConfigurationDialog(prop);
371
      cfgDialog->setValueFromProperty(prop);
372
      prop->connect (cfgDialog, SIGNAL(valueChanged (Configuration)),
373
          SLOT(set(Configuration)), Qt::DirectConnection);
374
      cfgDialog->connect (prop, SIGNAL(valueChanged (Configuration)),
375
          SLOT(set(Configuration)), Qt::DirectConnection);
376
377
      cfgDialog->setProperty("propertyName", name);
378
      cfgDialog->connect(button, SIGNAL(clicked()), SLOT(show()));
379
380
      return button;
381
    }
382
  } // namespace details
383
384
  int MetaEnum::from_string (const std::string& s)
385
  {
386
    for (std::size_t i = 0; i < names.size(); ++i)
387
      if (s == names[i]) return values[i];
388
    throw std::invalid_argument("Unknown name of enum (" + type + "): " + s);
389
  }
390
  std::string MetaEnum::to_string (const int& v)
391
  {
392
    for (std::size_t i = 0; i < names.size(); ++i)
393
      if (v == values[i]) return names[i];
394
    throw std::invalid_argument("Unknown enum value (" + type + ")");
395
  }
396
397
1
  MetaEnum* visibilityModeEnum ()
398
  {
399

1
    static MetaEnum vm;
400
1
    if (vm.type.size() == 0) {
401
1
      vm.type = "VisibilityMode";
402

1
      vm.names .push_back ("ON"           ); vm.values.push_back (VISIBILITY_ON );
403

1
      vm.names .push_back ("ALWAYS_ON_TOP"); vm.values.push_back (ALWAYS_ON_TOP );
404

1
      vm.names .push_back ("OFF"          ); vm.values.push_back (VISIBILITY_OFF);
405
    }
406
1
    return &vm;
407
  }
408
1
  MetaEnum* wireFrameModeEnum  ()
409
  {
410

1
    static MetaEnum wm;
411
1
    if (wm.type.size() == 0) {
412
1
      wm.type = "VisibilityMode";
413

1
      wm.names .push_back ("FILL"              ); wm.values.push_back (FILL              );
414

1
      wm.names .push_back ("WIREFRAME"         ); wm.values.push_back (WIREFRAME         );
415

1
      wm.names .push_back ("FILL_AND_WIREFRAME"); wm.values.push_back (FILL_AND_WIREFRAME);
416
    }
417
1
    return &wm;
418
  }
419
1
  MetaEnum* lightingModeEnum  ()
420
  {
421

1
    static MetaEnum lm;
422
1
    if (lm.type.size() == 0) {
423
1
      lm.type = "LightingMode";
424

1
      lm.names .push_back ("ON" ); lm.values.push_back (LIGHT_INFLUENCE_ON );
425

1
      lm.names .push_back ("OFF"); lm.values.push_back (LIGHT_INFLUENCE_OFF);
426
    }
427
1
    return &lm;
428
  }
429
  MetaEnum* glImmediateModeEnum ()
430
  {
431
    static MetaEnum lm;
432
    if (lm.type.size() == 0) {
433
      lm.type = "GLImmediateMode";
434
      lm.names.push_back ("GL_LINES"         ); lm.values.push_back (GL_LINES         );
435
      lm.names.push_back ("GL_POINTS"        ); lm.values.push_back (GL_POINTS        );
436
      lm.names.push_back ("GL_LINE_STRIP"    ); lm.values.push_back (GL_LINE_STRIP    );
437
      lm.names.push_back ("GL_LINE_LOOP"     ); lm.values.push_back (GL_LINE_LOOP     );
438
      lm.names.push_back ("GL_POLYGON"       ); lm.values.push_back (GL_POLYGON       );
439
      lm.names.push_back ("GL_QUADS"         ); lm.values.push_back (GL_QUADS         );
440
      lm.names.push_back ("GL_QUAD_STRIP"    ); lm.values.push_back (GL_QUAD_STRIP    );
441
      lm.names.push_back ("GL_TRIANGLE_STRIP"); lm.values.push_back (GL_TRIANGLE_STRIP);
442
      lm.names.push_back ("GL_TRIANGLES"     ); lm.values.push_back (GL_TRIANGLES     );
443
      lm.names.push_back ("GL_TRIANGLE_FAN"  ); lm.values.push_back (GL_TRIANGLE_FAN  );
444
    }
445
    return &lm;
446
  }
447
448
  bool EnumProperty::impl_set(const int& value)
449
  {
450
    const MetaEnum& me = *metaEnum();
451
    for (std::size_t i = 0; i < me.values.size(); ++i)
452
      if (me.values[i] == value)
453
        return IntProperty::impl_set(value);
454
    std::ostringstream oss;
455
    oss << "Invalid value " << value << " for enum " << me.type << ". "
456
      "Possible values are ";
457
    for (std::size_t i = 0; i < me.values.size(); ++i)
458
      oss << me.names[i] << " (" << me.values[i] << "), ";
459
    throw std::invalid_argument(oss.str());
460
    return false;
461
  }
462
463
  bool EnumProperty::impl_set(const std::string& value)
464
  {
465
    const MetaEnum& me = *metaEnum();
466
    for (std::size_t i = 0; i < me.names.size(); ++i)
467
      if (me.names[i] == value)
468
        return IntProperty::impl_set(me.values[i]);
469
    std::ostringstream oss;
470
    oss << "Invalid value " << value << " for enum " << me.type << ". "
471
      "Possible values are ";
472
    for (std::size_t i = 0; i < me.values.size(); ++i)
473
      oss << me.names[i] << " (" << me.values[i] << "), ";
474
    throw std::invalid_argument(oss.str());
475
    return false;
476
  }
477
478
  bool EnumProperty::impl_get(int& v)
479
  {
480
    return IntProperty::impl_get (v);
481
  }
482
483
  bool EnumProperty::impl_get(std::string& str)
484
  {
485
    int value;
486
    if (!IntProperty::impl_get(value)) return false;
487
    const MetaEnum& me = *metaEnum();
488
    for (std::size_t i = 0; i < me.names.size(); ++i)
489
      if (me.values[i] == value) {
490
        str = me.names[i];
491
        return true;
492
      }
493
    return false;
494
  }
495
496
  QWidget* EnumProperty::guiEditor ()
497
  {
498
    QString toolTip (
499
        "Python:\n"
500
        "As an integer:\n"
501
        "  gui.getIntProperty(nodeName,\"%1\")\n"
502
        "  gui.setIntProperty(nodeName,\"%1\",int)\n"
503
        "or as a string:\n"
504
        "  gui.getStringProperty(nodeName,\"%1\")\n"
505
        "  gui.setStringProperty(nodeName,\"%1\",string)");
506
507
    QComboBox* cb = new QComboBox;
508
    cb->setToolTip (toolTip.arg(name().c_str()));
509
    std::string value;
510
    /* bool success = */ get(value);
511
    for (std::size_t i = 0; i < metaEnum_->values.size(); ++i) {
512
      cb->addItem(metaEnum_->names[i].c_str(), metaEnum_->values[i]);
513
      if (value == metaEnum_->names[i])
514
        cb->setCurrentIndex(i);
515
    }
516
    if (hasWriteAccess()) {
517
      connect(cb, SIGNAL(currentTextChanged(QString)), SLOT(set(QString)), Qt::DirectConnection);
518
      // On Qt4, the combo box will not be updated.
519
#if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
520
      cb->connect(this, SIGNAL(valueChanged(QString)), SLOT(setCurrentText(QString)), Qt::DirectConnection);
521
#endif
522
    } else
523
      cb->setEnabled(false);
524
    return cb;
525
  }
526
527
  Property* Properties::property(const std::string& name) const
528
  {
529
    PropertyMap_t::const_iterator _prop = properties_.find(name);
530
    if (_prop == properties_.end())
531
      throw std::invalid_argument("Unknown property " + name);
532
    return _prop->second.p;
533
  }
534
535
  bool Properties::hasProperty(const std::string& name) const
536
  {
537
    PropertyMap_t::const_iterator _prop = properties_.find(name);
538
    return (_prop != properties_.end());
539
  }
540
541
11
  void Properties::addProperty(const PropertyPtr_t& prop)
542
  {
543
11
    addProperty(prop->name(), prop);
544
11
  }
545
546
11
  void Properties::addProperty(const std::string& name, const PropertyPtr_t& prop)
547
  {
548

11
    properties_.insert(std::make_pair(name, Wrapper(prop)));
549
11
  }
550
551
2
  void Properties::addProperty(Property* prop)
552
  {
553
2
    addProperty(prop->name(), prop);
554
2
  }
555
556
2
  void Properties::addProperty(const std::string& name, Property* prop)
557
  {
558

2
    properties_.insert(std::make_pair(name, Wrapper(prop)));
559
2
  }
560
561
  void addPropertyEditor(QFormLayout* l, const std::string& _name,
562
      Property* prop)
563
  {
564
    QString name (QString::fromStdString(_name));
565
    QWidget* field = prop->guiEditor();
566
    if (field != NULL) {
567
      QLabel* label (new QLabel (name + ':'));
568
      label->setToolTip (field->toolTip());
569
      field->setProperty("propertyName", name);
570
      l->addRow(label, field);
571
    } else {
572
      qDebug() << "Unhandled property" << name << "of type" << prop->type().c_str() << ".";
573
    }
574
  }
575
576
  QWidget* Properties::guiEditor ()
577
  {
578
    QWidget* editor = new QWidget;
579
    QFormLayout* l = new QFormLayout(editor);
580
581
    for (PropertyMap_t::const_iterator _prop = properties_.begin();
582
        _prop != properties_.end(); ++_prop)
583
    {
584
      if (!_prop->second->hasReadAccess()) continue;
585
      addPropertyEditor (l, _prop->first,_prop->second.p);
586
    }
587
    return editor;
588
  }
589
} /* namespace viewer */
590
591

3
} /* namespace gepetto */