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