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 "tools.h"
32
33 // appleseed.studio headers.
34 #include "mainwindow/project/entityeditorwindow.h"
35 #include "utility/doubleslider.h"
36 #include "utility/interop.h"
37
38 // appleseed.foundation headers.
39 #include "foundation/utility/containers/dictionary.h"
40
41 // Qt headers.
42 #include <QInputDialog>
43 #include <QLineEdit>
44 #include <QMessageBox>
45 #include <QObject>
46 #include <QString>
47
48 // Standard headers.
49 #include <cmath>
50 #include <utility>
51
52 using namespace foundation;
53 using namespace renderer;
54 using namespace std;
55
56 namespace appleseed {
57 namespace studio {
58
get_entity_name_dialog(QWidget * parent,const string & title,const string & label,const string & text)59 string get_entity_name_dialog(
60 QWidget* parent,
61 const string& title,
62 const string& label,
63 const string& text)
64 {
65 QString result;
66 bool ok;
67
68 do
69 {
70 result =
71 QInputDialog::getText(
72 parent,
73 QString::fromStdString(title),
74 QString::fromStdString(label),
75 QLineEdit::Normal,
76 QString::fromStdString(text),
77 &ok);
78 } while (ok && result.isEmpty());
79
80 return ok ? result.toStdString() : string();
81 }
82
open_entity_editor(QWidget * parent,const string & window_title,const Project & project,renderer::ParamArray & settings,unique_ptr<EntityEditor::IFormFactory> form_factory,unique_ptr<EntityEditor::IEntityBrowser> entity_browser,unique_ptr<CustomEntityUI> custom_entity_ui,const Dictionary & values,QObject * receiver,const char * slot_apply,const char * slot_accept,const char * slot_cancel)83 void open_entity_editor(
84 QWidget* parent,
85 const string& window_title,
86 const Project& project,
87 renderer::ParamArray& settings,
88 unique_ptr<EntityEditor::IFormFactory> form_factory,
89 unique_ptr<EntityEditor::IEntityBrowser> entity_browser,
90 unique_ptr<CustomEntityUI> custom_entity_ui,
91 const Dictionary& values,
92 QObject* receiver,
93 const char* slot_apply,
94 const char* slot_accept,
95 const char* slot_cancel)
96 {
97 EntityEditorWindow* editor_window =
98 new EntityEditorWindow(
99 parent,
100 window_title,
101 project,
102 settings,
103 move(form_factory),
104 move(entity_browser),
105 move(custom_entity_ui),
106 values);
107
108 QObject::connect(
109 editor_window, SIGNAL(signal_applied(foundation::Dictionary)),
110 receiver, slot_apply);
111
112 QObject::connect(
113 editor_window, SIGNAL(signal_accepted(foundation::Dictionary)),
114 receiver, slot_accept);
115
116 QObject::connect(
117 editor_window, SIGNAL(signal_canceled(foundation::Dictionary)),
118 receiver, slot_cancel);
119
120 editor_window->showNormal();
121 editor_window->activateWindow();
122 }
123
open_entity_editor(QWidget * parent,const string & window_title,const Project & project,renderer::ParamArray & settings,unique_ptr<EntityEditor::IFormFactory> form_factory,unique_ptr<EntityEditor::IEntityBrowser> entity_browser,const Dictionary & values,QObject * receiver,const char * slot_apply,const char * slot_accept,const char * slot_cancel)124 void open_entity_editor(
125 QWidget* parent,
126 const string& window_title,
127 const Project& project,
128 renderer::ParamArray& settings,
129 unique_ptr<EntityEditor::IFormFactory> form_factory,
130 unique_ptr<EntityEditor::IEntityBrowser> entity_browser,
131 const Dictionary& values,
132 QObject* receiver,
133 const char* slot_apply,
134 const char* slot_accept,
135 const char* slot_cancel)
136 {
137 open_entity_editor(
138 parent,
139 window_title,
140 project,
141 settings,
142 move(form_factory),
143 move(entity_browser),
144 unique_ptr<CustomEntityUI>(),
145 values,
146 receiver,
147 slot_apply,
148 slot_accept,
149 slot_cancel);
150 }
151
open_entity_editor(QWidget * parent,const string & window_title,const Project & project,renderer::ParamArray & settings,unique_ptr<EntityEditor::IFormFactory> form_factory,unique_ptr<EntityEditor::IEntityBrowser> entity_browser,QObject * receiver,const char * slot_apply,const char * slot_accept,const char * slot_cancel)152 void open_entity_editor(
153 QWidget* parent,
154 const string& window_title,
155 const Project& project,
156 renderer::ParamArray& settings,
157 unique_ptr<EntityEditor::IFormFactory> form_factory,
158 unique_ptr<EntityEditor::IEntityBrowser> entity_browser,
159 QObject* receiver,
160 const char* slot_apply,
161 const char* slot_accept,
162 const char* slot_cancel)
163 {
164 open_entity_editor(
165 parent,
166 window_title,
167 project,
168 settings,
169 move(form_factory),
170 move(entity_browser),
171 unique_ptr<CustomEntityUI>(),
172 Dictionary(),
173 receiver,
174 slot_apply,
175 slot_accept,
176 slot_cancel);
177 }
178
show_error_message_box(const string & title,const string & text)179 void show_error_message_box(const string& title, const string& text)
180 {
181 QMessageBox msgbox;
182 msgbox.setWindowTitle(QString::fromStdString(title));
183 msgbox.setIcon(QMessageBox::Critical);
184 msgbox.setText(QString::fromStdString(text));
185 msgbox.setStandardButtons(QMessageBox::Ok);
186 msgbox.exec();
187 }
188
189
190 //
191 // LineEditSliderAdaptor class implementation.
192 //
193
LineEditSliderAdaptor(QLineEdit * line_edit,QSlider * slider)194 LineEditSliderAdaptor::LineEditSliderAdaptor(
195 QLineEdit* line_edit,
196 QSlider* slider)
197 : QObject(line_edit)
198 , m_line_edit(line_edit)
199 , m_slider(slider)
200 {
201 slot_set_slider_value(m_line_edit->text());
202
203 // When the slider is moved, update the line edit's value.
204 connect(
205 m_slider, SIGNAL(valueChanged(const int)),
206 SLOT(slot_set_line_edit_value(const int)));
207
208 // When the line edit value is changed, update the slider's position.
209 connect(
210 m_line_edit, SIGNAL(textChanged(const QString&)),
211 SLOT(slot_set_slider_value(const QString&)));
212
213 // When enter is pressed in the line edit, update the slider's position.
214 connect(
215 m_line_edit, SIGNAL(editingFinished()),
216 SLOT(slot_apply_line_edit_value()));
217 }
218
slot_set_line_edit_value(const int value)219 void LineEditSliderAdaptor::slot_set_line_edit_value(const int value)
220 {
221 const QString new_line_edit_value = QString("%1").arg(value);
222
223 // Don't block signals here, for live edit to work we want the line edit to signal changes.
224 m_line_edit->setText(new_line_edit_value);
225 }
226
slot_set_slider_value(const QString & value)227 void LineEditSliderAdaptor::slot_set_slider_value(const QString& value)
228 {
229 if (value.isEmpty())
230 return;
231
232 const bool were_signals_blocked = m_slider->blockSignals(true);
233 m_slider->setValue(value.toInt());
234 m_slider->blockSignals(were_signals_blocked);
235 }
236
slot_apply_line_edit_value()237 void LineEditSliderAdaptor::slot_apply_line_edit_value()
238 {
239 slot_set_slider_value(m_line_edit->text());
240 }
241
242
243 //
244 // LineEditDoubleSliderAdaptor class implementation.
245 //
246
LineEditDoubleSliderAdaptor(QLineEdit * line_edit,DoubleSlider * slider)247 LineEditDoubleSliderAdaptor::LineEditDoubleSliderAdaptor(
248 QLineEdit* line_edit,
249 DoubleSlider* slider)
250 : QObject(line_edit)
251 , m_line_edit(line_edit)
252 , m_slider(slider)
253 {
254 slot_set_slider_value(m_line_edit->text());
255
256 // When the slider is moved, update the line edit's value.
257 connect(
258 m_slider, SIGNAL(valueChanged(const double)),
259 SLOT(slot_set_line_edit_value(const double)));
260
261 // When the line edit value is changed, update the slider's position.
262 connect(
263 m_line_edit, SIGNAL(textChanged(const QString&)),
264 SLOT(slot_set_slider_value(const QString&)));
265
266 // When enter is pressed in the line edit, update the slider's position.
267 connect(
268 m_line_edit, SIGNAL(editingFinished()),
269 SLOT(slot_apply_line_edit_value()));
270 }
271
slot_set_line_edit_value(const double value)272 void LineEditDoubleSliderAdaptor::slot_set_line_edit_value(const double value)
273 {
274 // Format integer values such as 2 as "2.0" instead of "2".
275 const bool is_integer = floor(value) == value;
276 const QString format_string = is_integer ? "%1.0" : "%1";
277 const QString new_line_edit_value = format_string.arg(value);
278
279 // Don't block signals here, for live edit to work we want the line edit to signal changes.
280 m_line_edit->setText(new_line_edit_value);
281 }
282
slot_set_slider_value(const QString & value)283 void LineEditDoubleSliderAdaptor::slot_set_slider_value(const QString& value)
284 {
285 if (value.isEmpty())
286 return;
287
288 const bool were_signals_blocked = m_slider->blockSignals(true);
289
290 const double new_value = value.toDouble();
291
292 // Adjust range if the new value is outside the current range.
293 // The test is intentionally different than the one in slot_apply_line_edit_value().
294 if (new_value < m_slider->minimum() ||
295 new_value > m_slider->maximum())
296 adjust_slider(new_value);
297
298 m_slider->setValue(new_value);
299
300 m_slider->blockSignals(were_signals_blocked);
301 }
302
slot_apply_line_edit_value()303 void LineEditDoubleSliderAdaptor::slot_apply_line_edit_value()
304 {
305 const bool were_signals_blocked = m_slider->blockSignals(true);
306
307 const double new_value = m_line_edit->text().toDouble();
308
309 // Adjust range if the new value is outside the current range,
310 // or if a value of a significantly smaller magnitude was entered.
311 if (new_value < m_slider->minimum() ||
312 new_value > m_slider->maximum() ||
313 abs(new_value) < (m_slider->maximum() - m_slider->minimum()) / 3.0)
314 adjust_slider(new_value);
315
316 m_slider->setValue(new_value);
317
318 // Force reformatting of the value in the QLineEdit if necessary.
319 if (!m_line_edit->text().isEmpty())
320 slot_set_line_edit_value(new_value);
321
322 m_slider->blockSignals(were_signals_blocked);
323 }
324
adjust_slider(const double new_value)325 void LineEditDoubleSliderAdaptor::adjust_slider(const double new_value)
326 {
327 const double new_min = new_value >= 0.0 ? 0.0 : -2.0 * abs(new_value);
328 const double new_max = new_value == 0.0 ? 1.0 : +2.0 * abs(new_value);
329 m_slider->setRange(new_min, new_max);
330 m_slider->setPageStep((new_max - new_min) / 10.0);
331 }
332
333
334 //
335 // QDoubleValidatorWithDefault class implementation.
336 //
337
QDoubleValidatorWithDefault(const QString & default_value,QObject * parent)338 QDoubleValidatorWithDefault::QDoubleValidatorWithDefault(const QString& default_value, QObject* parent)
339 : QDoubleValidator(parent)
340 , m_default_value(default_value)
341 {
342 }
343
QDoubleValidatorWithDefault(const double bottom,const double top,const int decimals,const QString & default_value,QObject * parent)344 QDoubleValidatorWithDefault::QDoubleValidatorWithDefault(
345 const double bottom,
346 const double top,
347 const int decimals,
348 const QString& default_value,
349 QObject* parent)
350 : QDoubleValidator(bottom, top, decimals, parent)
351 , m_default_value(default_value)
352 {
353 }
354
validate(QString & input,int & pos) const355 QValidator::State QDoubleValidatorWithDefault::validate(QString& input, int& pos) const
356 {
357 return
358 input == m_default_value
359 ? QValidator::Acceptable
360 : QDoubleValidator::validate(input, pos);
361 }
362
363
364 //
365 // ForwardColorChangedSignal class implementation.
366 //
367
ForwardColorChangedSignal(QObject * parent,const QString & widget_name,const QColor & initial_color)368 ForwardColorChangedSignal::ForwardColorChangedSignal(
369 QObject* parent,
370 const QString& widget_name,
371 const QColor& initial_color)
372 : QObject(parent)
373 , m_widget_name(widget_name)
374 , m_initial_color(initial_color)
375 , m_current_color(initial_color)
376 {
377 }
378
slot_color_reset()379 void ForwardColorChangedSignal::slot_color_reset()
380 {
381 if (m_current_color != m_initial_color)
382 emit signal_color_reset(m_widget_name, m_initial_color);
383 }
384
slot_color_changed(const QColor & color)385 void ForwardColorChangedSignal::slot_color_changed(const QColor& color)
386 {
387 m_current_color = color;
388 emit signal_color_changed(m_widget_name, color);
389 }
390
391 } // namespace studio
392 } // namespace appleseed
393