1 /*
2 * Copyright (C) 2017-2019 Robin Gareus <robin@gareus.org>
3 * Copyright (C) 2018 Nikolaus Gullotta <nikolaus.gullotta@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include <algorithm>
21
22 #include <gtkmm.h>
23
24 #include "ardour/dB.h"
25 #include "ardour/rc_configuration.h"
26
27 #include "gtkmm2ext/utils.h"
28
29 #include "widgets/ardour_dropdown.h"
30 #include "widgets/slider_controller.h"
31
32 #include "stripable_colorpicker.h"
33 #include "ardour_dialog.h"
34 #include "luadialog.h"
35 #include "splash.h"
36 #include "utils.h"
37
38 using namespace LuaDialog;
39
40 /* *****************************************************************************
41 * Simple Message Dialog
42 */
Message(std::string const & title,std::string const & msg,Message::MessageType mt,Message::ButtonType bt)43 Message::Message (std::string const& title, std::string const& msg, Message::MessageType mt, Message::ButtonType bt)
44 : _message_dialog (msg, true, to_gtk_mt (mt), to_gtk_bt (bt), true)
45 {
46 _message_dialog.set_title (title);
47 }
48
49 int
run()50 Message::run ()
51 {
52
53 bool splash_pushed = false;
54 Splash* spl = Splash::instance();
55 if (spl && spl->is_visible()) {
56 spl->pop_back_for (_message_dialog);
57 splash_pushed = true;
58 }
59
60 int rv = _message_dialog.run ();
61 _message_dialog.hide ();
62
63 if (splash_pushed) {
64 spl = Splash::instance();
65 if (spl) {
66 spl->pop_front_for (_message_dialog);
67 }
68 }
69
70 switch (rv) {
71 case Gtk::RESPONSE_OK:
72 return 0;
73 case Gtk::RESPONSE_CANCEL:
74 return 1;
75 case Gtk::RESPONSE_CLOSE:
76 return 2;
77 case Gtk::RESPONSE_YES:
78 return 3;
79 case Gtk::RESPONSE_NO:
80 return 4;
81 default:
82 break;
83 }
84 return -1;
85 }
86
87 Gtk::ButtonsType
to_gtk_bt(ButtonType bt)88 Message::to_gtk_bt (ButtonType bt)
89 {
90 switch (bt) {
91 case OK:
92 return Gtk::BUTTONS_OK;
93 case Close:
94 return Gtk::BUTTONS_CLOSE;
95 case Cancel:
96 return Gtk::BUTTONS_CANCEL;
97 case Yes_No:
98 return Gtk::BUTTONS_YES_NO;
99 case OK_Cancel:
100 return Gtk::BUTTONS_OK_CANCEL;
101 }
102 assert (0);
103 return Gtk::BUTTONS_OK;
104 }
105
106 Gtk::MessageType
to_gtk_mt(MessageType mt)107 Message::to_gtk_mt (MessageType mt)
108 {
109 switch (mt) {
110 case Info:
111 return Gtk::MESSAGE_INFO;
112 case Warning:
113 return Gtk::MESSAGE_WARNING;
114 case Question:
115 return Gtk::MESSAGE_QUESTION;
116 case Error:
117 return Gtk::MESSAGE_ERROR;
118 }
119 assert (0);
120 return Gtk::MESSAGE_INFO;
121 }
122
123
124 /* *****************************************************************************
125 * Lua Dialog Widgets
126 */
127
128 class LuaDialogLabel : public LuaDialogWidget
129 {
130 public:
LuaDialogLabel(std::string const & title,Gtk::AlignmentEnum xalign)131 LuaDialogLabel (std::string const& title, Gtk::AlignmentEnum xalign)
132 : LuaDialogWidget ("", "", 0, 2)
133 , _lbl (title, xalign, Gtk::ALIGN_CENTER, false)
134 { }
135
widget()136 Gtk::Widget* widget ()
137 {
138 return &_lbl;
139 }
140
assign(luabridge::LuaRef * rv) const141 void assign (luabridge::LuaRef* rv) const { }
142 protected:
143 Gtk::Label _lbl;
144 };
145
146
147 class LuaDialogHeading : public LuaDialogLabel
148 {
149 public:
LuaDialogHeading(std::string const & title,Gtk::AlignmentEnum xalign)150 LuaDialogHeading (std::string const& title, Gtk::AlignmentEnum xalign)
151 : LuaDialogLabel ("<b>" + title + "</b>", xalign)
152 {
153 _lbl.set_use_markup ();
154 }
155 };
156
157 class LuaHSeparator : public LuaDialogWidget
158 {
159 public:
LuaHSeparator()160 LuaHSeparator ()
161 : LuaDialogWidget ("", "", 0, 2)
162 {}
163
widget()164 Gtk::Widget* widget ()
165 {
166 return &_sep;
167 }
168
assign(luabridge::LuaRef * rv) const169 void assign (luabridge::LuaRef* rv) const { }
170 protected:
171 Gtk::HSeparator _sep;
172 };
173
174 class LuaColorPicker : public LuaDialogWidget
175 {
176 public:
LuaColorPicker(std::string const & key)177 LuaColorPicker (std::string const& key)
178 : LuaDialogWidget (key, "", 0, 1)
179 {}
180
widget()181 Gtk::Widget* widget ()
182 {
183 return &_cs;
184 }
assign(luabridge::LuaRef * rv) const185 void assign (luabridge::LuaRef* rv) const {
186 uint32_t rgba = ARDOUR_UI_UTILS::gdk_color_to_rgba(_cs.get_color());
187 (*rv)[_key] = rgba;
188 }
189 protected:
190 Gtk::ColorButton _cs;
191 };
192
193 class LuaDialogCheckbox : public LuaDialogWidget
194 {
195 public:
LuaDialogCheckbox(std::string const & key,std::string const & title,bool on)196 LuaDialogCheckbox (std::string const& key, std::string const& title, bool on)
197 : LuaDialogWidget (key, "", 1, 1)
198 {
199 if (!title.empty ()) {
200 _cb.add_label (title, false, 0);
201 }
202 _cb.set_active (on);
203 }
204
widget()205 Gtk::Widget* widget ()
206 {
207 return &_cb;
208 }
209
assign(luabridge::LuaRef * rv) const210 void assign (luabridge::LuaRef* rv) const
211 {
212 (*rv)[_key] = _cb.get_active ();
213 }
214
215 protected:
216 Gtk::CheckButton _cb;
217 };
218
219 class LuaDialogEntry : public LuaDialogWidget
220 {
221 public:
LuaDialogEntry(std::string const & key,std::string const & title,std::string const & dflt)222 LuaDialogEntry (std::string const& key, std::string const& title, std::string const& dflt)
223 : LuaDialogWidget (key, title)
224 {
225 _entry.set_text (dflt);
226 }
227
widget()228 Gtk::Widget* widget ()
229 {
230 return &_entry;
231 }
232
assign(luabridge::LuaRef * rv) const233 void assign (luabridge::LuaRef* rv) const
234 {
235 (*rv)[_key] = std::string (_entry.get_text ());
236 }
237
238 protected:
239 Gtk::Entry _entry;
240 };
241
242 class LuaDialogFader : public LuaDialogWidget
243 {
244 public:
LuaDialogFader(std::string const & key,std::string const & title,double dflt)245 LuaDialogFader (std::string const& key, std::string const& title, double dflt)
246 : LuaDialogWidget (key, title)
247 , _db_adjustment (ARDOUR::gain_to_slider_position_with_max (1.0, ARDOUR::Config->get_max_gain ()), 0, 1, 0.01, 0.1)
248 {
249 _db_slider = Gtk::manage (new ArdourWidgets::HSliderController (&_db_adjustment, boost::shared_ptr<PBD::Controllable> (), 220, 18));
250
251 _fader_centering_box.pack_start (*_db_slider, true, false);
252
253 _box.set_spacing (4);
254 _box.set_homogeneous (false);
255 _box.pack_start (_fader_centering_box, false, false);
256 _box.pack_start (_db_display, false, false);
257 _box.pack_start (*Gtk::manage (new Gtk::Label ("dB")), false, false);
258
259 Gtkmm2ext::set_size_request_to_display_given_text (_db_display, "-99.00", 12, 0);
260
261 _db_adjustment.signal_value_changed ().connect (sigc::mem_fun (*this, &LuaDialogFader::db_changed));
262 _db_display.signal_activate ().connect (sigc::mem_fun (*this, &LuaDialogFader::on_activate));
263 _db_display.signal_key_press_event ().connect (sigc::mem_fun (*this, &LuaDialogFader::on_key_press), false);
264
265 double coeff_val = dB_to_coefficient (dflt);
266 _db_adjustment.set_value (ARDOUR::gain_to_slider_position_with_max (coeff_val, ARDOUR::Config->get_max_gain ()));
267 db_changed ();
268 }
269
widget()270 Gtk::Widget* widget ()
271 {
272 return &_box;
273 }
274
assign(luabridge::LuaRef * rv) const275 void assign (luabridge::LuaRef* rv) const
276 {
277 double const val = ARDOUR::slider_position_to_gain_with_max (_db_adjustment.get_value (), ARDOUR::Config->get_max_gain ());
278 (*rv)[_key] = accurate_coefficient_to_dB (val);
279 }
280
281 protected:
db_changed()282 void db_changed ()
283 {
284 double const val = ARDOUR::slider_position_to_gain_with_max (_db_adjustment.get_value (), ARDOUR::Config->get_max_gain ());
285 char buf[16];
286 if (val == 0.0) {
287 snprintf (buf, sizeof (buf), "-inf");
288 } else {
289 snprintf (buf, sizeof (buf), "%.2f", accurate_coefficient_to_dB (val));
290 }
291 _db_display.set_text (buf);
292 }
293
on_activate()294 void on_activate ()
295 {
296 float db_val = atof (_db_display.get_text ().c_str ());
297 double coeff_val = dB_to_coefficient (db_val);
298 _db_adjustment.set_value (ARDOUR::gain_to_slider_position_with_max (coeff_val, ARDOUR::Config->get_max_gain ()));
299 }
300
on_key_press(GdkEventKey * ev)301 bool on_key_press (GdkEventKey* ev)
302 {
303 if (ARDOUR_UI_UTILS::key_is_legal_for_numeric_entry (ev->keyval)) {
304 return false;
305 }
306 return true;
307 }
308
309 Gtk::Adjustment _db_adjustment;
310 ArdourWidgets::HSliderController* _db_slider;
311 Gtk::Entry _db_display;
312 Gtk::HBox _box;
313 Gtk::VBox _fader_centering_box;
314 };
315
316 class LuaDialogSlider : public LuaDialogWidget
317 {
318 public:
LuaDialogSlider(std::string const & key,std::string const & title,double lower,double upper,double dflt,int digits,luabridge::LuaRef scalepoints)319 LuaDialogSlider (std::string const& key, std::string const& title, double lower, double upper, double dflt, int digits, luabridge::LuaRef scalepoints)
320 : LuaDialogWidget (key, title)
321 , _adj (dflt, lower, upper, 1, (upper - lower) / 20, 0)
322 , _hscale (_adj)
323 {
324 _hscale.set_digits (digits);
325 _hscale.set_draw_value (true);
326 _hscale.set_value_pos (Gtk::POS_TOP);
327
328 if (!scalepoints.isTable ()) {
329 return;
330 }
331
332 for (luabridge::Iterator i (scalepoints); !i.isNil (); ++i) {
333 if (!i.key ().isNumber ()) { continue; }
334 if (!i.value ().isString ()) { continue; }
335 _hscale.add_mark (i.key ().cast<double> (), Gtk::POS_BOTTOM, i.value ().cast<std::string> ());
336 }
337 }
338
widget()339 Gtk::Widget* widget ()
340 {
341 return &_hscale;
342 }
343
assign(luabridge::LuaRef * rv) const344 void assign (luabridge::LuaRef* rv) const
345 {
346 (*rv)[_key] = _adj.get_value ();
347 }
348
349 protected:
350 Gtk::Adjustment _adj;
351 Gtk::HScale _hscale;
352 };
353
354 class LuaDialogSpinBox : public LuaDialogWidget
355 {
356 public:
LuaDialogSpinBox(std::string const & key,std::string const & title,double lower,double upper,double dflt,double step,int digits)357 LuaDialogSpinBox (std::string const& key, std::string const& title, double lower, double upper, double dflt, double step, int digits)
358 : LuaDialogWidget (key, title)
359 , _adj (dflt, lower, upper, step, step, 0)
360 , _spin (_adj)
361 {
362 _spin.set_digits (digits);
363 }
364
widget()365 Gtk::Widget* widget ()
366 {
367 return &_spin;
368 }
369
assign(luabridge::LuaRef * rv) const370 void assign (luabridge::LuaRef* rv) const
371 {
372 (*rv)[_key] = _adj.get_value ();
373 }
374
375 protected:
376 Gtk::Adjustment _adj;
377 Gtk::SpinButton _spin;
378 };
379
380 class LuaDialogRadio : public LuaDialogWidget
381 {
382 public:
LuaDialogRadio(std::string const & key,std::string const & title,luabridge::LuaRef values,std::string const & dflt)383 LuaDialogRadio (std::string const& key, std::string const& title, luabridge::LuaRef values, std::string const& dflt)
384 : LuaDialogWidget (key, title)
385 , _rv (0)
386 {
387 for (luabridge::Iterator i (values); !i.isNil (); ++i) {
388 if (!i.key ().isString ()) { continue; }
389 std::string key = i.key ().cast<std::string> ();
390 Gtk::RadioButton* rb = Gtk::manage (new Gtk::RadioButton (_group, key));
391 _hbox.pack_start (*rb);
392 luabridge::LuaRef* ref = new luabridge::LuaRef (i.value ());
393 _refs.push_back (ref);
394 if (!_rv) { _rv = ref; }
395 rb->signal_toggled ().connect (sigc::bind (
396 sigc::mem_fun (*this, &LuaDialogRadio::rb_toggled), rb, ref
397 ) , false);
398
399 if (key == dflt) {
400 rb->set_active ();
401 }
402 }
403 }
404
~LuaDialogRadio()405 ~LuaDialogRadio ()
406 {
407 for (std::vector<luabridge::LuaRef*>::const_iterator i = _refs.begin (); i != _refs.end (); ++i) {
408 delete *i;
409 }
410 _refs.clear ();
411 }
412
widget()413 Gtk::Widget* widget ()
414 {
415 return &_hbox;
416 }
417
assign(luabridge::LuaRef * rv) const418 void assign (luabridge::LuaRef* rv) const
419 {
420 if (_rv) {
421 (*rv)[_key] = *_rv;
422 } else {
423 (*rv)[_key] = luabridge::Nil ();
424 }
425 }
426
427 protected:
428 LuaDialogRadio (LuaDialogRadio const&); // prevent cc
rb_toggled(Gtk::RadioButton * b,luabridge::LuaRef * rv)429 void rb_toggled (Gtk::RadioButton* b, luabridge::LuaRef* rv) {
430 if (b->get_active ()) {
431 _rv = rv;
432 }
433 }
434
435 Gtk::HBox _hbox;
436 Gtk::RadioButtonGroup _group;
437 std::vector<luabridge::LuaRef*> _refs;
438 luabridge::LuaRef* _rv;
439 };
440
441 class LuaDialogDropDown : public LuaDialogWidget
442 {
443 public:
LuaDialogDropDown(std::string const & key,std::string const & title,luabridge::LuaRef values,std::string const & dflt)444 LuaDialogDropDown (std::string const& key, std::string const& title, luabridge::LuaRef values, std::string const& dflt)
445 : LuaDialogWidget (key, title)
446 , _rv (0)
447 {
448 populate (_dd.items (), values, dflt);
449 }
450
~LuaDialogDropDown()451 ~LuaDialogDropDown ()
452 {
453 for (std::vector<luabridge::LuaRef*>::const_iterator i = _refs.begin (); i != _refs.end (); ++i) {
454 delete *i;
455 }
456 _refs.clear ();
457 }
458
widget()459 Gtk::Widget* widget ()
460 {
461 return &_dd;
462 }
463
assign(luabridge::LuaRef * rv) const464 void assign (luabridge::LuaRef* rv) const
465 {
466 if (_rv) {
467 (*rv)[_key] = *_rv;
468 } else {
469 (*rv)[_key] = luabridge::Nil ();
470 }
471 }
472
473 protected:
populate(Gtk::Menu_Helpers::MenuList & items,luabridge::LuaRef values,std::string const & dflt)474 void populate (Gtk::Menu_Helpers::MenuList& items, luabridge::LuaRef values, std::string const& dflt)
475 {
476 using namespace Gtk::Menu_Helpers;
477 std::vector<std::string> keys;
478
479 for (luabridge::Iterator i (values); !i.isNil (); ++i) {
480 if (!i.key ().isString ()) { continue; }
481 keys.push_back (i.key ().cast<std::string> ());
482 }
483
484 std::sort (keys.begin(), keys.end());
485
486 for (std::vector<std::string>::const_iterator i = keys.begin (); i != keys.end(); ++i) {
487 std::string key = *i;
488
489 if (values[key].isTable ()) {
490 Gtk::Menu* menu = Gtk::manage (new Gtk::Menu);
491 items.push_back (MenuElem (key, *menu));
492 populate (menu->items (), values[key], dflt);
493 continue;
494 }
495 luabridge::LuaRef* ref = new luabridge::LuaRef (values[key]);
496 _refs.push_back (ref);
497 items.push_back (MenuElem (key,
498 sigc::bind (sigc::mem_fun (*this, &LuaDialogDropDown::dd_select), key, ref)));
499
500 if (!_rv || key == dflt) {
501 _rv = ref;
502 _dd.set_text (key);
503 }
504 }
505 }
506
dd_select(std::string const & key,luabridge::LuaRef * rv)507 void dd_select (std::string const& key, luabridge::LuaRef* rv) {
508 _dd.set_text (key);
509 _rv = rv;
510 }
511
512 ArdourWidgets::ArdourDropdown _dd;
513 std::vector<luabridge::LuaRef*> _refs;
514 luabridge::LuaRef* _rv;
515 };
516
517 class LuaFileChooser : public LuaDialogWidget
518 {
519 public:
LuaFileChooser(std::string const & key,std::string const & title,Gtk::FileChooserAction a,const std::string & path)520 LuaFileChooser (std::string const& key, std::string const& title, Gtk::FileChooserAction a, const std::string& path)
521 : LuaDialogWidget (key, title)
522 , _fc (a)
523 {
524 Gtkmm2ext::add_volume_shortcuts (_fc);
525 if (!path.empty ()) {
526 switch (a) {
527 case Gtk::FILE_CHOOSER_ACTION_OPEN:
528 case Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER:
529 _fc.set_filename (path);
530 break;
531 case Gtk::FILE_CHOOSER_ACTION_SAVE:
532 case Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER:
533 /* not supported by Gtk::FileChooserButton */
534 break;
535 }
536 }
537 }
538
widget()539 Gtk::Widget* widget ()
540 {
541 return &_fc;
542 }
543
assign(luabridge::LuaRef * rv) const544 void assign (luabridge::LuaRef* rv) const
545 {
546 (*rv)[_key] = std::string (_fc.get_filename ());
547 }
548
549 protected:
550 Gtk::FileChooserButton _fc;
551 };
552
553
554 class LuaFileChooserWidget : public LuaDialogWidget
555 {
556 public:
LuaFileChooserWidget(std::string const & key,std::string const & title,Gtk::FileChooserAction a,const std::string & path)557 LuaFileChooserWidget (std::string const& key, std::string const& title, Gtk::FileChooserAction a, const std::string& path)
558 : LuaDialogWidget (key, title)
559 , _fc (a)
560 {
561 Gtkmm2ext::add_volume_shortcuts (_fc);
562 if (!path.empty ()) {
563 switch (a) {
564 case Gtk::FILE_CHOOSER_ACTION_OPEN:
565 case Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER:
566 _fc.set_filename (path);
567 break;
568 case Gtk::FILE_CHOOSER_ACTION_SAVE:
569 case Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER:
570 _fc.set_filename (path);
571 _fc.set_current_name (Glib::path_get_basename (path));
572 break;
573 break;
574 }
575 }
576 }
577
widget()578 Gtk::Widget* widget ()
579 {
580 return &_fc;
581 }
582
assign(luabridge::LuaRef * rv) const583 void assign (luabridge::LuaRef* rv) const
584 {
585 (*rv)[_key] = std::string (_fc.get_filename ());
586 }
587
588 protected:
589 Gtk::FileChooserWidget _fc;
590 };
591
592 /* *****************************************************************************
593 * Lua Parameter Dialog
594 */
595
Dialog(std::string const & title,luabridge::LuaRef lr)596 Dialog::Dialog (std::string const& title, luabridge::LuaRef lr)
597 :_ad (title, true, false)
598 , _title (title)
599 {
600 if (!lr.isTable ()) {
601 return;
602 }
603 for (luabridge::Iterator i (lr); !i.isNil (); ++i) {
604 if (!i.key ().isNumber ()) { continue; }
605 if (!i.value ().isTable ()) { continue; }
606 if (!i.value ()["title"].isString ()) { continue; }
607 if (!i.value ()["type"].isString ()) { continue; }
608
609 std::string title = i.value ()["title"].cast<std::string> ();
610 std::string type = i.value ()["type"].cast<std::string> ();
611 std::string key;
612
613 if (i.value ()["key"].isString ()) {
614 key = i.value ()["key"].cast<std::string> ();
615 }
616
617 LuaDialogWidget* w = NULL;
618
619 if (type == "heading") {
620 Gtk::AlignmentEnum xalign = Gtk::ALIGN_CENTER;
621 if (i.value ()["align"].isString ()) {
622 std::string align = i.value ()["align"].cast <std::string> ();
623 if (align == "left") {
624 xalign = Gtk::ALIGN_LEFT;
625 } else if (align == "right") {
626 xalign = Gtk::ALIGN_RIGHT;
627 }
628 }
629 w = new LuaDialogHeading (title, xalign);
630 } else if (type == "label") {
631 Gtk::AlignmentEnum xalign = Gtk::ALIGN_CENTER;
632 if (i.value ()["align"].isString ()) {
633 std::string align = i.value ()["align"].cast <std::string> ();
634 if (align == "left") {
635 xalign = Gtk::ALIGN_LEFT;
636 } else if (align == "right") {
637 xalign = Gtk::ALIGN_RIGHT;
638 }
639 }
640 w = new LuaDialogLabel (title, xalign);
641 } else if (type == "hseparator") {
642 w = new LuaHSeparator ();
643 }
644 /* the following widgets do require a key */
645 else if (key.empty ()) {
646 continue;
647 }
648 else if (type == "checkbox") {
649 bool dflt = false;
650 if (i.value ()["default"].isBoolean ()) {
651 dflt = i.value ()["default"].cast<bool> ();
652 }
653 w = new LuaDialogCheckbox (key, title, dflt);
654 } else if (type == "entry") {
655 std::string dflt;
656 if (i.value ()["default"].isString ()) {
657 dflt = i.value ()["default"].cast<std::string> ();
658 }
659 w = new LuaDialogEntry (key, title, dflt);
660 } else if (type == "radio") {
661 std::string dflt;
662 if (!i.value ()["values"].isTable ()) {
663 continue;
664 }
665 if (i.value ()["default"].isString ()) {
666 dflt = i.value ()["default"].cast<std::string> ();
667 }
668 w = new LuaDialogRadio (key, title, i.value ()["values"], dflt);
669 } else if (type == "fader") {
670 double dflt = 0;
671 if (i.value ()["default"].isNumber ()) {
672 dflt = i.value ()["default"].cast<double> ();
673 }
674 w = new LuaDialogFader (key, title, dflt);
675 } else if (type == "slider") {
676 double lower, upper, dflt;
677 int digits = 0;
678 if (!i.value ()["min"].isNumber ()) { continue; }
679 if (!i.value ()["max"].isNumber ()) { continue; }
680 lower = i.value ()["min"].cast<double> ();
681 upper = i.value ()["max"].cast<double> ();
682 if (i.value ()["default"].isNumber ()) {
683 dflt = i.value ()["default"].cast<double> ();
684 } else {
685 dflt = lower;
686 }
687 if (i.value ()["digits"].isNumber ()) {
688 digits = i.value ()["digits"].cast<int> ();
689 }
690 w = new LuaDialogSlider (key, title, lower, upper, dflt, digits, i.value ()["scalepoints"]);
691 } else if (type == "number") {
692 double lower, upper, dflt, step;
693 int digits = 0;
694 if (!i.value ()["min"].isNumber ()) { continue; }
695 if (!i.value ()["max"].isNumber ()) { continue; }
696 lower = i.value ()["min"].cast<double> ();
697 upper = i.value ()["max"].cast<double> ();
698 if (i.value ()["default"].isNumber ()) {
699 dflt = i.value ()["default"].cast<double> ();
700 } else {
701 dflt = lower;
702 }
703 if (i.value ()["step"].isNumber ()) {
704 step = i.value ()["step"].cast<double> ();
705 } else {
706 step = 1.0;
707 }
708 if (i.value ()["digits"].isNumber ()) {
709 digits = i.value ()["digits"].cast<int> ();
710 }
711 w = new LuaDialogSpinBox (key, title, lower, upper, dflt, step, digits);
712 } else if (type == "dropdown") {
713 std::string dflt;
714 if (!i.value ()["values"].isTable ()) {
715 continue;
716 }
717 if (i.value ()["default"].isString ()) {
718 dflt = i.value ()["default"].cast<std::string> ();
719 }
720 w = new LuaDialogDropDown (key, title, i.value ()["values"], dflt);
721 } else if (type == "file") {
722 std::string path;
723 if (i.value ()["path"].isString ()) {
724 path = i.value ()["path"].cast<std::string> ();
725 }
726 w = new LuaFileChooser (key, title, Gtk::FILE_CHOOSER_ACTION_OPEN, path);
727 } else if (type == "folder") {
728 std::string path;
729 if (i.value ()["path"].isString ()) {
730 path = i.value ()["path"].cast<std::string> ();
731 }
732 w = new LuaFileChooser (key, title, Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER, path);
733 } else if (type == "createfile") {
734 std::string path;
735 if (i.value ()["path"].isString ()) {
736 path = i.value ()["path"].cast<std::string> ();
737 }
738 w = new LuaFileChooserWidget (key, title, Gtk::FILE_CHOOSER_ACTION_SAVE, path);
739 } else if (type == "createdir") {
740 std::string path;
741 if (i.value ()["path"].isString ()) {
742 path = i.value ()["path"].cast<std::string> ();
743 }
744 w = new LuaFileChooserWidget (key, title, Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER, path);
745 } else if (type == "color") {
746 w = new LuaColorPicker (key);
747 }
748
749 if (w) {
750 if (i.value ()["col"].isNumber ()) {
751 w->set_col (i.value ()["col"].cast<int> ());
752 }
753 if (i.value ()["colspan"].isNumber ()) {
754 w->set_span (i.value ()["colspan"].cast<int> ());
755 }
756 _widgets.push_back(w);
757 }
758 }
759
760 _ad.add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
761 _ad.add_button (Gtk::Stock::OK, Gtk::RESPONSE_ACCEPT);
762
763 Gtk::Table* table = Gtk::manage (new Gtk::Table ());
764 table->set_col_spacings (20);
765 table->set_row_spacings (8);
766 table->signal_size_allocate ().connect (sigc::mem_fun (this, &Dialog::table_size_alloc));
767
768 _scroller.set_shadow_type(Gtk::SHADOW_NONE);
769 _scroller.set_border_width(0);
770 _scroller.add (*table);
771 _scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_NEVER);
772
773 _ad.get_vbox ()->pack_start (_scroller);
774
775 int row = 0;
776 int last_end = -1;
777
778 for (DialogWidgets::const_iterator i = _widgets.begin (); i != _widgets.end (); ++i) {
779 int col = (*i)->col();
780 int cend = col + (*i)->span();
781
782 if (col < last_end) {
783 ++row;
784 }
785 last_end = cend;
786
787 std::string const& label = (*i)->label ();
788 if (!label.empty ()) {
789 /* items with implicit label (title) */
790 Gtk::Label* lbl = Gtk::manage (new Gtk::Label (label + ":", Gtk::ALIGN_END, Gtk::ALIGN_CENTER, false));
791 if (cend - col > 1) {
792 table->attach (*lbl, col, col + 1, row, row + 1, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
793 table->attach (*((*i)->widget ()), col + 1, cend, row, row + 1, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
794 } else {
795 Gtk::HBox* hb = Gtk::manage (new Gtk::HBox());
796 hb->set_spacing(4);
797 hb->pack_start (*lbl, true, false);
798 hb->pack_start (*(*i)->widget (), true, false);
799 table->attach (*hb, col, cend, row, row + 1, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
800 }
801 } else {
802 table->attach (*((*i)->widget ()), col, cend, row, row + 1, Gtk::FILL | Gtk::EXPAND, (_widgets.size() == 1) ? (Gtk::FILL | Gtk::EXPAND) : Gtk::SHRINK);
803 }
804 }
805 }
806
~Dialog()807 Dialog::~Dialog ()
808 {
809 for (DialogWidgets::const_iterator i = _widgets.begin (); i != _widgets.end () ; ++i) {
810 delete *i;
811 }
812 _widgets.clear ();
813 }
814
815 int
run(lua_State * L)816 Dialog::run (lua_State *L)
817 {
818 _ad.get_vbox ()->show_all ();
819 switch (_ad.run ()) {
820 case Gtk::RESPONSE_ACCEPT:
821 break;
822 default:
823 lua_pushnil (L);
824 return 1;
825 }
826
827 luabridge::LuaRef rv (luabridge::newTable (L));
828 for (DialogWidgets::const_iterator i = _widgets.begin (); i != _widgets.end () ; ++i) {
829 (*i)->assign (&rv);
830 }
831 luabridge::push (L, rv);
832 return 1;
833 }
834
835 void
table_size_alloc(Gtk::Allocation & allocation)836 Dialog::table_size_alloc (Gtk::Allocation& allocation)
837 {
838 /* XXX: consider using 0.75 * screen-height instead of 512 */
839 if (allocation.get_height () > 512) {
840 _scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
841 _ad.set_size_request (-1, 512);
842 }
843 }
844
845 /* *****************************************************************************
846 * Lua Progress Dialog
847 */
848
ProgressWindow(std::string const & title,bool allow_cancel)849 ProgressWindow::ProgressWindow (std::string const& title, bool allow_cancel)
850 : ArdourDialog (title, true)
851 , _canceled (false)
852 {
853 _bar.set_orientation (Gtk::PROGRESS_LEFT_TO_RIGHT);
854
855 set_border_width (12);
856 get_vbox()->set_spacing (6);
857 get_vbox()->pack_start (_bar, false, false);
858
859 if (allow_cancel) {
860 using namespace Gtk;
861 Button* b = add_button (Stock::CANCEL, RESPONSE_CANCEL);
862 b->signal_clicked().connect (sigc::mem_fun (*this, &ProgressWindow::cancel_clicked));
863 }
864
865 set_default_size (200, -1);
866 show_all ();
867 }
868
869 bool
progress(float prog,std::string const & text)870 ProgressWindow::progress (float prog, std::string const& text)
871 {
872 if (!text.empty ()) {
873 _bar.set_text (text);
874 }
875 if (prog < 0 || prog > 1) {
876 _bar.set_pulse_step(.1);
877 _bar.pulse();
878 } else {
879 _bar.set_fraction (prog);
880 }
881 ARDOUR::GUIIdle ();
882 return _canceled;
883 }
884
885 void
done()886 ProgressWindow::done () {
887 Gtk::Dialog::response(_canceled ? Gtk::RESPONSE_CANCEL : Gtk::RESPONSE_OK);
888 }
889