1
2 //
3 // This source file is part of appleseed.
4 // Visit https://appleseedhq.net/ for additional information and resources.
5 //
6 // This software is released under the MIT license.
7 //
8 // Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited
9 // Copyright (c) 2014-2018 Francois Beaune, The appleseedhq Organization
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining a copy
12 // of this software and associated documentation files (the "Software"), to deal
13 // in the Software without restriction, including without limitation the rights
14 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 // copies of the Software, and to permit persons to whom the Software is
16 // furnished to do so, subject to the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be included in
19 // all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 // THE SOFTWARE.
28 //
29
30 // Interface header.
31 #include "entityeditor.h"
32
33 // appleseed.studio headers.
34 #include "mainwindow/project/entitybrowserwindow.h"
35 #include "mainwindow/project/entityinputwidget.h"
36 #include "mainwindow/project/tools.h"
37 #include "utility/doubleslider.h"
38 #include "utility/interop.h"
39 #include "utility/miscellaneous.h"
40 #include "utility/mousewheelfocuseventfilter.h"
41 #include "utility/settingskeys.h"
42
43 // appleseed.renderer headers.
44 #include "renderer/api/project.h"
45
46 // appleseed.foundation headers.
47 #include "foundation/image/color.h"
48 #include "foundation/math/scalar.h"
49 #include "foundation/utility/foreach.h"
50 #include "foundation/utility/iostreamop.h"
51 #include "foundation/utility/string.h"
52
53 // Qt headers.
54 #include <QCheckBox>
55 #include <QColor>
56 #include <QColorDialog>
57 #include <QComboBox>
58 #include <QDialogButtonBox>
59 #include <QDoubleValidator>
60 #include <QFileDialog>
61 #include <QFont>
62 #include <QFormLayout>
63 #include <QHBoxLayout>
64 #include <QIntValidator>
65 #include <QLabel>
66 #include <QLineEdit>
67 #include <QPushButton>
68 #include <QShortcut>
69 #include <QSlider>
70 #include <QString>
71 #include <Qt>
72 #include <QToolButton>
73 #include <QVariant>
74 #include <QVBoxLayout>
75
76 // Boost headers.
77 #include "boost/filesystem/operations.hpp"
78 #include "boost/filesystem/path.hpp"
79
80 // Standard headers.
81 #include <cassert>
82 #include <cmath>
83 #include <limits>
84 #include <sstream>
85 #include <utility>
86
87 using namespace foundation;
88 using namespace renderer;
89 using namespace std;
90 namespace bf = boost::filesystem;
91
92 namespace appleseed {
93 namespace studio {
94
EntityEditor(QWidget * parent,const Project & project,ParamArray & settings,unique_ptr<IFormFactory> form_factory,unique_ptr<IEntityBrowser> entity_browser,unique_ptr<CustomEntityUI> custom_ui,const Dictionary & values)95 EntityEditor::EntityEditor(
96 QWidget* parent,
97 const Project& project,
98 ParamArray& settings,
99 unique_ptr<IFormFactory> form_factory,
100 unique_ptr<IEntityBrowser> entity_browser,
101 unique_ptr<CustomEntityUI> custom_ui,
102 const Dictionary& values)
103 : QObject(parent)
104 , m_parent(parent)
105 , m_project(project)
106 , m_settings(settings)
107 , m_form_factory(move(form_factory))
108 , m_entity_browser(move(entity_browser))
109 , m_custom_ui(move(custom_ui))
110 {
111 if (m_parent->layout() != nullptr)
112 {
113 clear_layout(m_parent->layout());
114 delete m_parent->layout();
115 }
116
117 m_top_layout = new QVBoxLayout(m_parent);
118 m_top_layout->setMargin(7);
119
120 create_connections();
121 rebuild_form(values);
122 }
123
get_values() const124 Dictionary EntityEditor::get_values() const
125 {
126 Dictionary values;
127
128 if (m_custom_ui.get())
129 values.merge(m_custom_ui->get_values());
130
131 values.merge(m_widget_proxies.get_values());
132
133 return values;
134 }
135
rebuild_form(const Dictionary & values)136 void EntityEditor::rebuild_form(const Dictionary& values)
137 {
138 // The mappings were removed when the widgets were deleted.
139 clear_layout(m_top_layout);
140 m_widget_proxies.clear();
141
142 // Collect input metadata.
143 m_form_factory->update(values, m_input_metadata);
144
145 // Create corresponding input widgets.
146 create_form_layout();
147 for (const_each<InputMetadataCollection> i = m_input_metadata; i; ++i)
148 {
149 const bool input_widget_visible = is_input_widget_visible(*i, values);
150 create_input_widgets(*i, input_widget_visible);
151 }
152
153 if (m_custom_ui.get())
154 m_custom_ui->create_widgets(m_top_layout, values);
155 }
156
create_form_layout()157 void EntityEditor::create_form_layout()
158 {
159 m_form_layout = new QFormLayout();
160 m_form_layout->setLabelAlignment(Qt::AlignRight);
161 m_form_layout->setSpacing(10);
162 m_form_layout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
163 m_top_layout->addLayout(m_form_layout);
164 }
165
create_connections()166 void EntityEditor::create_connections()
167 {
168 if (m_custom_ui.get())
169 {
170 connect(
171 m_custom_ui.get(), SIGNAL(signal_apply()),
172 SLOT(slot_apply()));
173 }
174 }
175
get_input_metadata(const string & name) const176 const Dictionary& EntityEditor::get_input_metadata(const string& name) const
177 {
178 for (const_each<InputMetadataCollection> i = m_input_metadata; i; ++i)
179 {
180 const Dictionary& metadata = *i;
181
182 if (metadata.get<string>("name") == name)
183 return metadata;
184 }
185
186 static Dictionary empty_dictionary;
187 return empty_dictionary;
188 }
189
is_input_widget_visible(const Dictionary & metadata,const Dictionary & values) const190 bool EntityEditor::is_input_widget_visible(const Dictionary& metadata, const Dictionary& values) const
191 {
192 if (!metadata.dictionaries().exist("visible_if"))
193 return true;
194
195 // Conditions in a visible_if clause must be all true for the input widget to be visible
196 // (in other words they are combined with AND operations).
197
198 const StringDictionary& visible_if = metadata.dictionary("visible_if").strings();
199
200 for (const auto& condition : visible_if)
201 {
202 const char* key = condition.key();
203 const char* value = condition.value();
204
205 const bool condition_met =
206 values.strings().exist(key)
207 ? values.strings().get<string>(key) == value
208 : get_input_metadata(key).get<string>("default") == value;
209
210 if (!condition_met)
211 return false;
212 }
213
214 return true;
215 }
216
create_input_widgets(const Dictionary & metadata,const bool input_widget_visible)217 void EntityEditor::create_input_widgets(const Dictionary& metadata, const bool input_widget_visible)
218 {
219 const string input_name = metadata.get<string>("name");
220 const string input_type = metadata.get<string>("type");
221
222 unique_ptr<IInputWidgetProxy> widget_proxy =
223 input_type == "text" ? create_text_input_widgets(metadata, input_widget_visible) :
224 input_type == "numeric" ? create_numeric_input_widgets(metadata, input_widget_visible) :
225 input_type == "integer" ? create_integer_input_widgets(metadata, input_widget_visible) :
226 input_type == "boolean" ? create_boolean_input_widgets(metadata, input_widget_visible) :
227 input_type == "enumeration" ? create_enumeration_input_widgets(metadata, input_widget_visible) :
228 input_type == "color" ? create_color_input_widgets(metadata, input_widget_visible) :
229 input_type == "colormap" ? create_colormap_input_widgets(metadata, input_widget_visible) :
230 input_type == "entity" ? create_entity_input_widgets(metadata, input_widget_visible) :
231 input_type == "file" ? create_file_input_widgets(metadata, input_widget_visible) :
232 unique_ptr<IInputWidgetProxy>(nullptr);
233
234 assert(widget_proxy.get());
235
236 const bool rebuild_form =
237 metadata.strings().exist("on_change") &&
238 metadata.get<string>("on_change") == "rebuild_form";
239
240 connect(
241 widget_proxy.get(),
242 SIGNAL(signal_changed()),
243 rebuild_form ? SLOT(slot_rebuild_form()) : SLOT(slot_apply()));
244
245 m_widget_proxies.insert(input_name, move(widget_proxy));
246 }
247
248 namespace
249 {
create_label(const Dictionary & metadata)250 QLabel* create_label(const Dictionary& metadata)
251 {
252 QLabel* label = new QLabel(metadata.get<QString>("label") + ":");
253
254 if (metadata.get<QString>("use") == "required")
255 {
256 QFont font;
257 font.setBold(true);
258 label->setFont(font);
259 }
260
261 return label;
262 }
263
should_be_focused(const Dictionary & metadata)264 bool should_be_focused(const Dictionary& metadata)
265 {
266 return
267 metadata.strings().exist("focus") &&
268 metadata.strings().get<bool>("focus");
269 }
270 }
271
create_text_input_widgets(const Dictionary & metadata,const bool input_widget_visible)272 unique_ptr<IInputWidgetProxy> EntityEditor::create_text_input_widgets(const Dictionary& metadata, const bool input_widget_visible)
273 {
274 QLineEdit* line_edit = new QLineEdit(m_parent);
275
276 if (should_be_focused(metadata))
277 {
278 line_edit->selectAll();
279 line_edit->setFocus();
280 }
281
282 if (input_widget_visible)
283 m_form_layout->addRow(create_label(metadata), line_edit);
284 else line_edit->hide();
285
286 unique_ptr<IInputWidgetProxy> widget_proxy(new LineEditProxy(line_edit));
287 widget_proxy->set(metadata.strings().get<string>("value"));
288
289 return widget_proxy;
290 }
291
create_numeric_input_widgets(const Dictionary & metadata,const bool input_widget_visible)292 unique_ptr<IInputWidgetProxy> EntityEditor::create_numeric_input_widgets(const Dictionary& metadata, const bool input_widget_visible)
293 {
294 const Dictionary& min = metadata.dictionary("min");
295 const Dictionary& max = metadata.dictionary("max");
296
297 const double slider_min = min.get<double>("value");
298 const double slider_max = max.get<double>("value");
299
300 const double validator_min = min.get<string>("type") == "hard" ? slider_min : -numeric_limits<double>::max();
301 const double validator_max = max.get<string>("type") == "hard" ? slider_max : +numeric_limits<double>::max();
302
303 QLineEdit* line_edit = new QLineEdit(m_parent);
304 line_edit->setMaximumWidth(60);
305 line_edit->setValidator(new QDoubleValidator(validator_min, validator_max, 16, line_edit));
306
307 DoubleSlider* slider = new DoubleSlider(Qt::Horizontal, m_parent);
308 slider->setRange(slider_min, slider_max);
309 slider->setPageStep((slider_max - slider_min) / 10.0);
310 new MouseWheelFocusEventFilter(slider);
311 auto adaptor = new LineEditDoubleSliderAdaptor(line_edit, slider);
312 connect(slider, SIGNAL(valueChanged(int)), SLOT(slot_apply()));
313
314 if (should_be_focused(metadata))
315 {
316 line_edit->selectAll();
317 line_edit->setFocus();
318 }
319
320 if (input_widget_visible)
321 {
322 QHBoxLayout* layout = new QHBoxLayout();
323 layout->setSpacing(6);
324 layout->addWidget(line_edit);
325 layout->addWidget(slider);
326 m_form_layout->addRow(create_label(metadata), layout);
327 }
328 else
329 {
330 line_edit->hide();
331 slider->hide();
332 }
333
334 const string value = metadata.strings().get<string>("value");
335 if (!value.empty())
336 {
337 // Keep the first value if there is more than one.
338 istringstream istr(value);
339 double val;
340 istr >> val;
341 adaptor->slot_set_line_edit_value(val);
342 }
343
344 unique_ptr<IInputWidgetProxy> widget_proxy(new LineEditProxy(line_edit));
345
346 return widget_proxy;
347 }
348
create_integer_input_widgets(const Dictionary & metadata,const bool input_widget_visible)349 unique_ptr<IInputWidgetProxy> EntityEditor::create_integer_input_widgets(const Dictionary& metadata, const bool input_widget_visible)
350 {
351 const Dictionary& min = metadata.dictionary("min");
352 const Dictionary& max = metadata.dictionary("max");
353
354 const int slider_min = min.get<int>("value");
355 const int slider_max = max.get<int>("value");
356
357 const int validator_min = slider_min;
358 const int validator_max = slider_max;
359
360 QLineEdit* line_edit = new QLineEdit(m_parent);
361 line_edit->setMaximumWidth(60);
362 line_edit->setValidator(new QIntValidator(validator_min, validator_max, line_edit));
363
364 QSlider* slider = new QSlider(Qt::Horizontal, m_parent);
365 slider->setRange(slider_min, slider_max);
366 new MouseWheelFocusEventFilter(slider);
367 new LineEditSliderAdaptor(line_edit, slider);
368 connect(slider, SIGNAL(valueChanged(int)), SLOT(slot_apply()));
369
370 if (should_be_focused(metadata))
371 {
372 line_edit->selectAll();
373 line_edit->setFocus();
374 }
375
376 if (input_widget_visible)
377 {
378 QHBoxLayout* layout = new QHBoxLayout();
379 layout->setSpacing(6);
380 layout->addWidget(line_edit);
381 layout->addWidget(slider);
382 m_form_layout->addRow(create_label(metadata), layout);
383 }
384 else
385 {
386 line_edit->hide();
387 slider->hide();
388 }
389
390 unique_ptr<IInputWidgetProxy> widget_proxy(new LineEditProxy(line_edit));
391 widget_proxy->set(metadata.strings().get<string>("value"));
392
393 return widget_proxy;
394 }
395
create_boolean_input_widgets(const Dictionary & metadata,const bool input_widget_visible)396 unique_ptr<IInputWidgetProxy> EntityEditor::create_boolean_input_widgets(const Dictionary& metadata, const bool input_widget_visible)
397 {
398 QCheckBox* checkbox = new QCheckBox(m_parent);
399
400 if (should_be_focused(metadata))
401 checkbox->setFocus();
402
403 if (input_widget_visible)
404 m_form_layout->addRow(create_label(metadata), checkbox);
405 else checkbox->hide();
406
407 unique_ptr<IInputWidgetProxy> widget_proxy(new CheckBoxProxy(checkbox));
408 widget_proxy->set(metadata.strings().get<string>("value"));
409
410 return widget_proxy;
411 }
412
create_enumeration_input_widgets(const Dictionary & metadata,const bool input_widget_visible)413 unique_ptr<IInputWidgetProxy> EntityEditor::create_enumeration_input_widgets(const Dictionary& metadata, const bool input_widget_visible)
414 {
415 QComboBox* combo_box = new QComboBox(m_parent);
416 combo_box->setEditable(false);
417 new MouseWheelFocusEventFilter(combo_box);
418
419 const StringDictionary& items = metadata.dictionaries().get("items").strings();
420 for (const_each<StringDictionary> i = items; i; ++i)
421 combo_box->addItem(i->key(), i->value<QString>());
422
423 const QString value = metadata.strings().get<QString>("value");
424 combo_box->setCurrentIndex(combo_box->findData(QVariant::fromValue(value)));
425
426 if (should_be_focused(metadata))
427 combo_box->setFocus();
428
429 if (input_widget_visible)
430 m_form_layout->addRow(create_label(metadata), combo_box);
431 else combo_box->hide();
432
433 unique_ptr<IInputWidgetProxy> widget_proxy(new ComboBoxProxy(combo_box));
434
435 return widget_proxy;
436 }
437
create_color_input_widgets(const Dictionary & metadata,const bool input_widget_visible)438 unique_ptr<IInputWidgetProxy> EntityEditor::create_color_input_widgets(const Dictionary& metadata, const bool input_widget_visible)
439 {
440 QLineEdit* line_edit = new QLineEdit(m_parent);
441
442 QToolButton* picker_button = new QToolButton(m_parent);
443 picker_button->setObjectName("color_picker");
444 const string name = metadata.get<string>("name");
445 connect(picker_button, &QToolButton::clicked, [=]() { emit slot_open_color_picker(QString::fromStdString(name)); });
446
447 if (should_be_focused(metadata))
448 {
449 line_edit->selectAll();
450 line_edit->setFocus();
451 }
452
453 if (input_widget_visible)
454 {
455 QHBoxLayout* layout = new QHBoxLayout();
456 layout->setSpacing(6);
457 layout->addWidget(line_edit);
458 layout->addWidget(picker_button);
459 m_form_layout->addRow(create_label(metadata), layout);
460 }
461 else
462 {
463 line_edit->hide();
464 picker_button->hide();
465 }
466
467 unique_ptr<ColorPickerProxy> widget_proxy(new ColorPickerProxy(line_edit, picker_button));
468
469 if (metadata.strings().exist("value") &&
470 metadata.strings().exist("wavelength_range_widget"))
471 {
472 const string value = metadata.strings().get<string>("value");
473 const string wr_widget = metadata.get<string>("wavelength_range_widget");
474 const IInputWidgetProxy* wr_widget_proxy = m_widget_proxies.get(wr_widget);
475 if (wr_widget_proxy)
476 widget_proxy->set(value, wr_widget_proxy->get());
477 else widget_proxy->set(value);
478 }
479 else widget_proxy->set("0.0 0.0 0.0");
480
481 return unique_ptr<IInputWidgetProxy>(move(widget_proxy));
482 }
483
create_colormap_input_widgets(const Dictionary & metadata,const bool input_widget_visible)484 unique_ptr<IInputWidgetProxy> EntityEditor::create_colormap_input_widgets(const Dictionary& metadata, const bool input_widget_visible)
485 {
486 const string name = metadata.get<string>("name");
487
488 double validator_min, validator_max;
489
490 if (metadata.dictionaries().exist("min"))
491 {
492 const Dictionary& min = metadata.dictionary("min");
493 validator_min = min.get<string>("type") == "hard" ? min.get<double>("value") : -numeric_limits<double>::max();
494 }
495 else
496 validator_min = -numeric_limits<double>::max();
497
498 if (metadata.dictionaries().exist("max"))
499 {
500 const Dictionary& max = metadata.dictionary("max");
501 validator_max = max.get<string>("type") == "hard" ? max.get<double>("value") : +numeric_limits<double>::max();
502 }
503 else
504 validator_max = +numeric_limits<double>::max();
505
506 const QString default_value = metadata.strings().exist("default") ? metadata.get<QString>("default") : "";
507
508 ColorMapInputWidget* input_widget = new ColorMapInputWidget(m_parent);
509 input_widget->set_validator(new QDoubleValidatorWithDefault(validator_min, validator_max, 16, default_value));
510 input_widget->set_default_value(default_value);
511
512 connect(input_widget, &ColorMapInputWidget::signal_bind_button_clicked, [=]() { emit slot_open_entity_browser(QString::fromStdString(name)); });
513
514 if (should_be_focused(metadata))
515 input_widget->set_focus();
516
517 if (input_widget_visible)
518 m_form_layout->addRow(create_label(metadata), input_widget);
519 else input_widget->hide();
520
521 unique_ptr<IInputWidgetProxy> widget_proxy(new ColorMapInputProxy(input_widget));
522 widget_proxy->set(metadata.strings().get<string>("value"));
523
524 return widget_proxy;
525 }
526
create_entity_input_widgets(const Dictionary & metadata,const bool input_widget_visible)527 unique_ptr<IInputWidgetProxy> EntityEditor::create_entity_input_widgets(const Dictionary& metadata, const bool input_widget_visible)
528 {
529 const string name = metadata.get<string>("name");
530
531 EntityInputWidget* input_widget = new EntityInputWidget(m_parent);
532 connect(input_widget, &EntityInputWidget::signal_bind_button_clicked, [=]() { emit slot_open_entity_browser(QString::fromStdString(name)); });
533
534 if (should_be_focused(metadata))
535 input_widget->set_focus();
536
537 if (input_widget_visible)
538 m_form_layout->addRow(create_label(metadata), input_widget);
539 else input_widget->hide();
540
541 unique_ptr<IInputWidgetProxy> widget_proxy(new EntityInputProxy(input_widget));
542 widget_proxy->set(metadata.strings().get<string>("value"));
543
544 return widget_proxy;
545 }
546
create_file_input_widgets(const Dictionary & metadata,const bool input_widget_visible)547 unique_ptr<IInputWidgetProxy> EntityEditor::create_file_input_widgets(const Dictionary& metadata, const bool input_widget_visible)
548 {
549 const string name = metadata.get<string>("name");
550
551 QLineEdit* line_edit = new QLineEdit(m_parent);
552
553 QPushButton* browse_button = new QPushButton("Browse", m_parent);
554 browse_button->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
555 connect(browse_button, &QPushButton::clicked, [=]() { emit slot_open_file_picker(QString::fromStdString(name)); });
556
557 if (should_be_focused(metadata))
558 {
559 line_edit->selectAll();
560 line_edit->setFocus();
561 }
562
563 if (input_widget_visible)
564 {
565 QHBoxLayout* layout = new QHBoxLayout();
566 layout->setSpacing(6);
567 layout->addWidget(line_edit);
568 layout->addWidget(browse_button);
569 m_form_layout->addRow(create_label(metadata), layout);
570 }
571 else
572 {
573 line_edit->hide();
574 browse_button->hide();
575 }
576
577 unique_ptr<IInputWidgetProxy> widget_proxy(new LineEditProxy(line_edit));
578 widget_proxy->set(metadata.strings().get<string>("value"));
579
580 return widget_proxy;
581 }
582
slot_rebuild_form()583 void EntityEditor::slot_rebuild_form()
584 {
585 rebuild_form(get_values());
586
587 emit signal_applied(get_values());
588 }
589
590 namespace
591 {
592 class ForwardAcceptedSignal
593 : public QObject
594 {
595 Q_OBJECT
596
597 public:
ForwardAcceptedSignal(QObject * parent,const QString & widget_name)598 ForwardAcceptedSignal(QObject* parent, const QString& widget_name)
599 : QObject(parent)
600 , m_widget_name(widget_name)
601 {
602 }
603
604 public slots:
slot_accept(const QString & page_name,const QString & entity_name)605 void slot_accept(
606 const QString& page_name,
607 const QString& entity_name)
608 {
609 emit signal_accepted(m_widget_name, page_name, entity_name);
610 }
611
612 signals:
613 void signal_accepted(
614 const QString& widget_name,
615 const QString& page_name,
616 const QString& entity_name);
617
618 private:
619 const QString m_widget_name;
620 };
621 }
622
slot_open_entity_browser(const QString & widget_name)623 void EntityEditor::slot_open_entity_browser(const QString& widget_name)
624 {
625 const Dictionary& metadata = get_input_metadata(widget_name.toStdString());
626
627 EntityBrowserWindow* browser_window =
628 new EntityBrowserWindow(
629 m_parent,
630 metadata.get<string>("label"));
631
632 const Dictionary& entity_types = metadata.dictionaries().get("entity_types");
633
634 for (const_each<StringDictionary> i = entity_types.strings(); i; ++i)
635 {
636 const string entity_type = i->key();
637 const string entity_label = i->value<string>();
638 const StringDictionary entities = m_entity_browser->get_entities(entity_type);
639 browser_window->add_items_page(entity_type, entity_label, entities);
640 }
641
642 ForwardAcceptedSignal* forward_signal =
643 new ForwardAcceptedSignal(browser_window, widget_name);
644 connect(
645 browser_window, SIGNAL(signal_accepted(const QString&, const QString&)),
646 forward_signal, SLOT(slot_accept(const QString&, const QString&)));
647 connect(
648 forward_signal, SIGNAL(signal_accepted(const QString&, const QString&, const QString&)),
649 SLOT(slot_entity_browser_accept(const QString&, const QString&, const QString&)));
650
651 browser_window->showNormal();
652 browser_window->activateWindow();
653 }
654
slot_entity_browser_accept(QString widget_name,QString page_name,QString entity_name)655 void EntityEditor::slot_entity_browser_accept(QString widget_name, QString page_name, QString entity_name)
656 {
657 IInputWidgetProxy* widget_proxy = m_widget_proxies.get(widget_name.toStdString());
658 widget_proxy->set(entity_name.toStdString());
659 widget_proxy->emit_signal_changed();
660
661 // Close the entity browser.
662 qobject_cast<QWidget*>(sender()->parent())->close();
663 }
664
slot_open_color_picker(const QString & widget_name)665 void EntityEditor::slot_open_color_picker(const QString& widget_name)
666 {
667 IInputWidgetProxy* widget_proxy = m_widget_proxies.get(widget_name.toStdString());
668
669 const Dictionary& metadata = get_input_metadata(widget_name.toStdString());
670 const string wr_widget = metadata.get<string>("wavelength_range_widget");
671 const IInputWidgetProxy* wr_widget_proxy = m_widget_proxies.get(wr_widget);
672 const Color3d initial_color =
673 wr_widget_proxy
674 ? ColorPickerProxy::get_color_from_string(widget_proxy->get(), wr_widget_proxy->get())
675 : ColorPickerProxy::get_color_from_string(widget_proxy->get());
676
677 QColorDialog* dialog =
678 new QColorDialog(
679 color_to_qcolor(initial_color),
680 m_parent);
681 dialog->setWindowTitle("Pick Color");
682 dialog->setOptions(QColorDialog::DontUseNativeDialog);
683
684 ForwardColorChangedSignal* forward_signal =
685 new ForwardColorChangedSignal(dialog, widget_name, color_to_qcolor(initial_color));
686 connect(
687 dialog, SIGNAL(currentColorChanged(const QColor&)),
688 forward_signal, SLOT(slot_color_changed(const QColor&)));
689 connect(
690 forward_signal, SIGNAL(signal_color_changed(const QString&, const QColor&)),
691 SLOT(slot_color_changed(const QString&, const QColor&)));
692 connect(
693 dialog, SIGNAL(rejected()),
694 forward_signal, SLOT(slot_color_reset()));
695 connect(
696 forward_signal, SIGNAL(signal_color_reset(const QString&, const QColor&)),
697 SLOT(slot_color_changed(const QString&, const QColor&)));
698
699 dialog->exec();
700 }
701
slot_color_changed(const QString & widget_name,const QColor & color)702 void EntityEditor::slot_color_changed(const QString& widget_name, const QColor& color)
703 {
704 IInputWidgetProxy* widget_proxy = m_widget_proxies.get(widget_name.toStdString());
705 widget_proxy->set(to_string(qcolor_to_color<Color3d>(color)));
706 widget_proxy->emit_signal_changed();
707 }
708
slot_open_file_picker(const QString & widget_name)709 void EntityEditor::slot_open_file_picker(const QString& widget_name)
710 {
711 IInputWidgetProxy* widget_proxy = m_widget_proxies.get(widget_name.toStdString());
712
713 const Dictionary& metadata = get_input_metadata(widget_name.toStdString());
714
715 if (metadata.get<string>("file_picker_mode") == "open")
716 {
717 const QString file_picker_type = metadata.get<QString>("file_picker_type");
718 const QString filter =
719 file_picker_type == "image" ? get_oiio_image_files_filter() :
720 file_picker_type == "project" ? get_project_files_filter() :
721 QString();
722
723 const QString settings_key =
724 file_picker_type == "image" ? SETTINGS_FILE_DIALOG_OIIO_TEXTURES :
725 SETTINGS_FILE_DIALOG_ENTITIES;
726
727 const bf::path project_root_path = bf::path(m_project.get_path()).parent_path();
728 const bf::path file_path = absolute(widget_proxy->get(), project_root_path);
729 const bf::path file_root_path = file_path.parent_path();
730
731 QFileDialog::Options options;
732
733 QString filepath =
734 get_open_filename(
735 m_parent,
736 "Open...",
737 filter,
738 m_settings,
739 settings_key,
740 options);
741
742 if (!filepath.isEmpty())
743 {
744 widget_proxy->set(QDir::toNativeSeparators(filepath).toStdString());
745 widget_proxy->emit_signal_changed();
746 }
747 }
748 }
749
slot_apply()750 void EntityEditor::slot_apply()
751 {
752 emit signal_applied(get_values());
753 }
754
755 } // namespace studio
756 } // namespace appleseed
757
758 #include "mainwindow/project/moc_cpp_entityeditor.cxx"
759