Fix thinkos in cubasish theme
[ardour.git] / gtk2_ardour / luadialog.cc
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  */
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
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();
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
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
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:
131         LuaDialogLabel (std::string const& title, Gtk::AlignmentEnum xalign)
132                 : LuaDialogWidget ("", "", 0, 2)
133                 , _lbl (title, xalign, Gtk::ALIGN_CENTER, false)
134         { }
135
136         Gtk::Widget* widget ()
137         {
138                 return &_lbl;
139         }
140
141         void assign (luabridge::LuaRef* rv) const { }
142 protected:
143         Gtk::Label _lbl;
144 };
145
146
147 class LuaDialogHeading : public LuaDialogLabel
148 {
149 public:
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:
160         LuaHSeparator ()
161                 : LuaDialogWidget ("", "", 0, 2)
162         {}
163
164         Gtk::Widget* widget ()
165         {
166                 return &_sep;
167         }
168
169         void assign (luabridge::LuaRef* rv) const { }
170 protected:
171         Gtk::HSeparator _sep;
172 };
173
174 class LuaColorPicker : public LuaDialogWidget
175 {
176 public:
177         LuaColorPicker (std::string const& key)
178                 : LuaDialogWidget (key, "", 0, 1)
179         {}
180
181         Gtk::Widget* widget ()
182         {
183                 return &_cs;
184         }
185         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:
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
205         Gtk::Widget* widget ()
206         {
207                 return &_cb;
208         }
209
210         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:
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
228         Gtk::Widget* widget ()
229         {
230                 return &_entry;
231         }
232
233         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:
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
270         Gtk::Widget* widget ()
271         {
272                 return &_box;
273         }
274
275         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:
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
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
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:
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
339         Gtk::Widget* widget ()
340         {
341                 return &_hscale;
342         }
343
344         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:
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
365         Gtk::Widget* widget ()
366         {
367                 return &_spin;
368         }
369
370         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:
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
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
413         Gtk::Widget* widget ()
414         {
415                 return &_hbox;
416         }
417
418         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
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:
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
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
459         Gtk::Widget* widget ()
460         {
461                 return &_dd;
462         }
463
464         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:
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
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:
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
539         Gtk::Widget* widget ()
540         {
541                 return &_fc;
542         }
543
544         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:
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
578         Gtk::Widget* widget ()
579         {
580                 return &_fc;
581         }
582
583         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
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, Gtk::SHRINK);
803                 }
804         }
805 }
806
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
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
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
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
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                 std::cerr << "pulse\n";
877                 _bar.set_pulse_step(.1);
878                 _bar.pulse();
879         } else {
880                 _bar.set_fraction (prog);
881         }
882         ARDOUR::GUIIdle ();
883         return _canceled;
884 }
885
886 void
887 ProgressWindow::done () {
888         Gtk::Dialog::response(_canceled ? Gtk::RESPONSE_CANCEL : Gtk::RESPONSE_OK);
889 }