2 * Copyright (C) 2017 Robin Gareus <robin@gareus.org>
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include "ardour/dB.h"
22 #include "ardour/rc_configuration.h"
24 #include "gtkmm2ext/utils.h"
26 #include "widgets/ardour_dropdown.h"
27 #include "widgets/slider_controller.h"
29 #include "ardour_dialog.h"
30 #include "luadialog.h"
34 using namespace LuaDialog;
36 /*******************************************************************************
37 * Simple Message Dialog
39 Message::Message (std::string const& title, std::string const& msg, Message::MessageType mt, Message::ButtonType bt)
40 : _message_dialog (msg, false, to_gtk_mt (mt), to_gtk_bt (bt), true)
42 _message_dialog.set_title (title);
49 bool splash_pushed = false;
50 Splash* spl = Splash::instance();
51 if (spl && spl->is_visible()) {
52 spl->pop_back_for (_message_dialog);
56 int rv = _message_dialog.run ();
57 _message_dialog.hide ();
60 spl = Splash::instance();
67 case Gtk::RESPONSE_OK:
69 case Gtk::RESPONSE_CANCEL:
71 case Gtk::RESPONSE_CLOSE:
73 case Gtk::RESPONSE_YES:
75 case Gtk::RESPONSE_NO:
84 Message::to_gtk_bt (ButtonType bt)
88 return Gtk::BUTTONS_OK;
90 return Gtk::BUTTONS_CLOSE;
92 return Gtk::BUTTONS_CANCEL;
94 return Gtk::BUTTONS_YES_NO;
96 return Gtk::BUTTONS_OK_CANCEL;
99 return Gtk::BUTTONS_OK;
103 Message::to_gtk_mt (MessageType mt)
107 return Gtk::MESSAGE_INFO;
109 return Gtk::MESSAGE_WARNING;
111 return Gtk::MESSAGE_QUESTION;
113 return Gtk::MESSAGE_ERROR;
116 return Gtk::MESSAGE_INFO;
120 /* *****************************************************************************
124 class LuaDialogLabel : public LuaDialogWidget
127 LuaDialogLabel (std::string const& title, Gtk::AlignmentEnum xalign)
128 : LuaDialogWidget ("", "", 0, 2)
129 , _lbl (title, xalign, Gtk::ALIGN_CENTER, false)
132 Gtk::Widget* widget ()
137 void assign (luabridge::LuaRef* rv) const { }
143 class LuaDialogHeading : public LuaDialogLabel
146 LuaDialogHeading (std::string const& title, Gtk::AlignmentEnum xalign)
147 : LuaDialogLabel ("<b>" + title + "</b>", xalign)
149 _lbl.set_use_markup ();
153 class LuaHSeparator : public LuaDialogWidget
157 : LuaDialogWidget ("", "", 0, 2)
160 Gtk::Widget* widget ()
165 void assign (luabridge::LuaRef* rv) const { }
167 Gtk::HSeparator _sep;
170 class LuaDialogCheckbox : public LuaDialogWidget
173 LuaDialogCheckbox (std::string const& key, std::string const& title, bool on)
174 : LuaDialogWidget (key, "", 1, 1)
176 if (!title.empty ()) {
177 _cb.add_label (title, false, 0);
182 Gtk::Widget* widget ()
187 void assign (luabridge::LuaRef* rv) const
189 (*rv)[_key] = _cb.get_active ();
193 Gtk::CheckButton _cb;
196 class LuaDialogEntry : public LuaDialogWidget
199 LuaDialogEntry (std::string const& key, std::string const& title, std::string const& dflt)
200 : LuaDialogWidget (key, title)
202 _entry.set_text (dflt);
205 Gtk::Widget* widget ()
210 void assign (luabridge::LuaRef* rv) const
212 (*rv)[_key] = std::string (_entry.get_text ());
219 class LuaDialogFader : public LuaDialogWidget
222 LuaDialogFader (std::string const& key, std::string const& title, double dflt)
223 : LuaDialogWidget (key, title)
224 , _db_adjustment (ARDOUR::gain_to_slider_position_with_max (1.0, ARDOUR::Config->get_max_gain ()), 0, 1, 0.01, 0.1)
226 _db_slider = Gtk::manage (new ArdourWidgets::HSliderController (&_db_adjustment, boost::shared_ptr<PBD::Controllable> (), 220, 18));
228 _fader_centering_box.pack_start (*_db_slider, true, false);
230 _box.set_spacing (4);
231 _box.set_homogeneous (false);
232 _box.pack_start (_fader_centering_box, false, false);
233 _box.pack_start (_db_display, false, false);
234 _box.pack_start (*Gtk::manage (new Gtk::Label ("dB")), false, false);
236 Gtkmm2ext::set_size_request_to_display_given_text (_db_display, "-99.00", 12, 0);
238 _db_adjustment.signal_value_changed ().connect (sigc::mem_fun (*this, &LuaDialogFader::db_changed));
239 _db_display.signal_activate ().connect (sigc::mem_fun (*this, &LuaDialogFader::on_activate));
240 _db_display.signal_key_press_event ().connect (sigc::mem_fun (*this, &LuaDialogFader::on_key_press), false);
242 double coeff_val = dB_to_coefficient (dflt);
243 _db_adjustment.set_value (ARDOUR::gain_to_slider_position_with_max (coeff_val, ARDOUR::Config->get_max_gain ()));
247 Gtk::Widget* widget ()
252 void assign (luabridge::LuaRef* rv) const
254 double const val = ARDOUR::slider_position_to_gain_with_max (_db_adjustment.get_value (), ARDOUR::Config->get_max_gain ());
255 (*rv)[_key] = accurate_coefficient_to_dB (val);
261 double const val = ARDOUR::slider_position_to_gain_with_max (_db_adjustment.get_value (), ARDOUR::Config->get_max_gain ());
264 snprintf (buf, sizeof (buf), "-inf");
266 snprintf (buf, sizeof (buf), "%.2f", accurate_coefficient_to_dB (val));
268 _db_display.set_text (buf);
273 float db_val = atof (_db_display.get_text ().c_str ());
274 double coeff_val = dB_to_coefficient (db_val);
275 _db_adjustment.set_value (ARDOUR::gain_to_slider_position_with_max (coeff_val, ARDOUR::Config->get_max_gain ()));
278 bool on_key_press (GdkEventKey* ev)
280 if (ARDOUR_UI_UTILS::key_is_legal_for_numeric_entry (ev->keyval)) {
286 Gtk::Adjustment _db_adjustment;
287 ArdourWidgets::HSliderController* _db_slider;
288 Gtk::Entry _db_display;
290 Gtk::VBox _fader_centering_box;
293 class LuaDialogSlider : public LuaDialogWidget
296 LuaDialogSlider (std::string const& key, std::string const& title, double lower, double upper, double dflt, int digits, luabridge::LuaRef scalepoints)
297 : LuaDialogWidget (key, title)
298 , _adj (dflt, lower, upper, 1, (upper - lower) / 20, 0)
301 _hscale.set_digits (digits);
302 _hscale.set_draw_value (true);
303 _hscale.set_value_pos (Gtk::POS_TOP);
305 if (!scalepoints.isTable ()) {
309 for (luabridge::Iterator i (scalepoints); !i.isNil (); ++i) {
310 if (!i.key ().isNumber ()) { continue; }
311 if (!i.value ().isString ()) { continue; }
312 _hscale.add_mark (i.key ().cast<double> (), Gtk::POS_BOTTOM, i.value ().cast<std::string> ());
316 Gtk::Widget* widget ()
321 void assign (luabridge::LuaRef* rv) const
323 (*rv)[_key] = _adj.get_value ();
327 Gtk::Adjustment _adj;
331 class LuaDialogSpinBox : public LuaDialogWidget
334 LuaDialogSpinBox (std::string const& key, std::string const& title, double lower, double upper, double dflt, double step, int digits)
335 : LuaDialogWidget (key, title)
336 , _adj (dflt, lower, upper, step, step, 0)
339 _spin.set_digits (digits);
342 Gtk::Widget* widget ()
347 void assign (luabridge::LuaRef* rv) const
349 (*rv)[_key] = _adj.get_value ();
353 Gtk::Adjustment _adj;
354 Gtk::SpinButton _spin;
357 class LuaDialogRadio : public LuaDialogWidget
360 LuaDialogRadio (std::string const& key, std::string const& title, luabridge::LuaRef values, std::string const& dflt)
361 : LuaDialogWidget (key, title)
364 for (luabridge::Iterator i (values); !i.isNil (); ++i) {
365 if (!i.key ().isString ()) { continue; }
366 std::string key = i.key ().cast<std::string> ();
367 Gtk::RadioButton* rb = Gtk::manage (new Gtk::RadioButton (_group, key));
368 _hbox.pack_start (*rb);
369 luabridge::LuaRef* ref = new luabridge::LuaRef (i.value ());
370 _refs.push_back (ref);
371 if (!_rv) { _rv = ref; }
372 rb->signal_toggled ().connect (sigc::bind (
373 sigc::mem_fun (*this, &LuaDialogRadio::rb_toggled), rb, ref
384 for (std::vector<luabridge::LuaRef*>::const_iterator i = _refs.begin (); i != _refs.end (); ++i) {
390 Gtk::Widget* widget ()
395 void assign (luabridge::LuaRef* rv) const
400 (*rv)[_key] = luabridge::Nil ();
405 LuaDialogRadio (LuaDialogRadio const&); // prevent cc
406 void rb_toggled (Gtk::RadioButton* b, luabridge::LuaRef* rv) {
407 if (b->get_active ()) {
413 Gtk::RadioButtonGroup _group;
414 std::vector<luabridge::LuaRef*> _refs;
415 luabridge::LuaRef* _rv;
418 class LuaDialogDropDown : public LuaDialogWidget
421 LuaDialogDropDown (std::string const& key, std::string const& title, luabridge::LuaRef values, std::string const& dflt)
422 : LuaDialogWidget (key, title)
425 populate (_dd.items (), values, dflt);
428 ~LuaDialogDropDown ()
430 for (std::vector<luabridge::LuaRef*>::const_iterator i = _refs.begin (); i != _refs.end (); ++i) {
436 Gtk::Widget* widget ()
441 void assign (luabridge::LuaRef* rv) const
446 (*rv)[_key] = luabridge::Nil ();
451 void populate (Gtk::Menu_Helpers::MenuList& items, luabridge::LuaRef values, std::string const& dflt)
453 using namespace Gtk::Menu_Helpers;
454 for (luabridge::Iterator i (values); !i.isNil (); ++i) {
455 if (!i.key ().isString ()) { continue; }
456 std::string key = i.key ().cast<std::string> ();
457 if (i.value ().isTable ()) {
458 Gtk::Menu* menu = Gtk::manage (new Gtk::Menu);
459 items.push_back (MenuElem (key, *menu));
460 populate (menu->items (), i.value (), dflt);
463 luabridge::LuaRef* ref = new luabridge::LuaRef (i.value ());
464 _refs.push_back (ref);
465 items.push_back (MenuElem (i.key ().cast<std::string> (),
466 sigc::bind (sigc::mem_fun (*this, &LuaDialogDropDown::dd_select), key, ref)));
468 if (!_rv || key == dflt) {
475 void dd_select (std::string const& key, luabridge::LuaRef* rv) {
480 ArdourWidgets::ArdourDropdown _dd;
481 std::vector<luabridge::LuaRef*> _refs;
482 luabridge::LuaRef* _rv;
485 class LuaFileChooser : public LuaDialogWidget
488 LuaFileChooser (std::string const& key, std::string const& title, Gtk::FileChooserAction a, const std::string& path)
489 : LuaDialogWidget (key, title)
492 if (!path.empty ()) {
494 case Gtk::FILE_CHOOSER_ACTION_OPEN:
495 case Gtk::FILE_CHOOSER_ACTION_SAVE:
496 if (Glib::file_test (path, Glib::FILE_TEST_IS_REGULAR|Glib::FILE_TEST_EXISTS)) {
497 _fc.set_filename (path);
500 case Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER:
501 if (Glib::file_test (path, Glib::FILE_TEST_IS_DIR|Glib::FILE_TEST_EXISTS)) {
502 _fc.set_filename (path);
505 case Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER:
511 Gtk::Widget* widget ()
516 void assign (luabridge::LuaRef* rv) const
518 (*rv)[_key] = std::string (_fc.get_filename ());
522 Gtk::FileChooserButton _fc;
527 /*******************************************************************************
528 * Lua Parameter Dialog
530 Dialog::Dialog (std::string const& title, luabridge::LuaRef lr)
531 :_ad (title, true, false)
534 if (!lr.isTable ()) {
537 for (luabridge::Iterator i (lr); !i.isNil (); ++i) {
538 if (!i.key ().isNumber ()) { continue; }
539 if (!i.value ().isTable ()) { continue; }
540 if (!i.value ()["title"].isString ()) { continue; }
541 if (!i.value ()["type"].isString ()) { continue; }
543 std::string title = i.value ()["title"].cast<std::string> ();
544 std::string type = i.value ()["type"].cast<std::string> ();
547 if (i.value ()["key"].isString ()) {
548 key = i.value ()["key"].cast<std::string> ();
551 LuaDialogWidget* w = NULL;
553 if (type == "heading") {
554 Gtk::AlignmentEnum xalign = Gtk::ALIGN_CENTER;
555 if (i.value ()["align"].isString ()) {
556 std::string align = i.value ()["align"].cast <std::string> ();
557 if (align == "left") {
558 xalign = Gtk::ALIGN_LEFT;
559 } else if (align == "right") {
560 xalign = Gtk::ALIGN_RIGHT;
563 w = new LuaDialogHeading (title, xalign);
564 } else if (type == "label") {
565 Gtk::AlignmentEnum xalign = Gtk::ALIGN_CENTER;
566 if (i.value ()["align"].isString ()) {
567 std::string align = i.value ()["align"].cast <std::string> ();
568 if (align == "left") {
569 xalign = Gtk::ALIGN_LEFT;
570 } else if (align == "right") {
571 xalign = Gtk::ALIGN_RIGHT;
574 w = new LuaDialogLabel (title, xalign);
575 } else if (type == "hseparator") {
576 w = new LuaHSeparator ();
578 /* the following widgets do require a key */
579 else if (key.empty ()) {
582 else if (type == "checkbox") {
584 if (i.value ()["default"].isBoolean ()) {
585 dflt = i.value ()["default"].cast<bool> ();
587 w = new LuaDialogCheckbox (key, title, dflt);
588 } else if (type == "entry") {
590 if (i.value ()["default"].isString ()) {
591 dflt = i.value ()["default"].cast<std::string> ();
593 w = new LuaDialogEntry (key, title, dflt);
594 } else if (type == "radio") {
596 if (!i.value ()["values"].isTable ()) {
599 if (i.value ()["default"].isString ()) {
600 dflt = i.value ()["default"].cast<std::string> ();
602 w = new LuaDialogRadio (key, title, i.value ()["values"], dflt);
603 } else if (type == "fader") {
605 if (i.value ()["default"].isNumber ()) {
606 dflt = i.value ()["default"].cast<double> ();
608 w = new LuaDialogFader (key, title, dflt);
609 } else if (type == "slider") {
610 double lower, upper, dflt;
612 if (!i.value ()["min"].isNumber ()) { continue; }
613 if (!i.value ()["max"].isNumber ()) { continue; }
614 lower = i.value ()["min"].cast<double> ();
615 upper = i.value ()["max"].cast<double> ();
616 if (i.value ()["default"].isNumber ()) {
617 dflt = i.value ()["default"].cast<double> ();
621 if (i.value ()["digits"].isNumber ()) {
622 digits = i.value ()["digits"].cast<int> ();
624 w = new LuaDialogSlider (key, title, lower, upper, dflt, digits, i.value ()["scalepoints"]);
625 } else if (type == "number") {
626 double lower, upper, dflt, step;
628 if (!i.value ()["min"].isNumber ()) { continue; }
629 if (!i.value ()["max"].isNumber ()) { continue; }
630 lower = i.value ()["min"].cast<double> ();
631 upper = i.value ()["max"].cast<double> ();
632 if (i.value ()["default"].isNumber ()) {
633 dflt = i.value ()["default"].cast<double> ();
637 if (i.value ()["step"].isNumber ()) {
638 step = i.value ()["step"].cast<double> ();
642 if (i.value ()["digits"].isNumber ()) {
643 digits = i.value ()["digits"].cast<int> ();
645 w = new LuaDialogSpinBox (key, title, lower, upper, dflt, step, digits);
646 } else if (type == "dropdown") {
648 if (!i.value ()["values"].isTable ()) {
651 if (i.value ()["default"].isString ()) {
652 dflt = i.value ()["default"].cast<std::string> ();
654 w = new LuaDialogDropDown (key, title, i.value ()["values"], dflt);
655 } else if (type == "file") {
657 if (i.value ()["path"].isString ()) {
658 path = i.value ()["path"].cast<std::string> ();
660 w = new LuaFileChooser (key, title, Gtk::FILE_CHOOSER_ACTION_OPEN, path);
661 } else if (type == "folder") {
663 if (i.value ()["path"].isString ()) {
664 path = i.value ()["path"].cast<std::string> ();
666 w = new LuaFileChooser (key, title, Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER, path);
670 if (i.value ()["col"].isNumber ()) {
671 w->set_col (i.value ()["col"].cast<int> ());
673 if (i.value ()["colspan"].isNumber ()) {
674 w->set_span (i.value ()["colspan"].cast<int> ());
676 _widgets.push_back(w);
680 _ad.add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
681 _ad.add_button (Gtk::Stock::OK, Gtk::RESPONSE_ACCEPT);
683 Gtk::Table* table = Gtk::manage (new Gtk::Table ());
684 table->set_col_spacings (20);
685 table->set_row_spacings (8);
686 _ad.get_vbox ()->pack_start (*table);
691 for (DialogWidgets::const_iterator i = _widgets.begin (); i != _widgets.end (); ++i) {
692 int col = (*i)->col();
693 int cend = col + (*i)->span();
695 if (col < last_end) {
700 std::string const& label = (*i)->label ();
701 if (!label.empty ()) {
702 /* items with implicit label (title) */
703 Gtk::Label* lbl = Gtk::manage (new Gtk::Label (label + ":", Gtk::ALIGN_END, Gtk::ALIGN_CENTER, false));
704 if (cend - col > 1) {
705 table->attach (*lbl, col, col + 1, row, row + 1, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
706 table->attach (*((*i)->widget ()), col + 1, cend, row, row + 1, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
708 Gtk::HBox* hb = Gtk::manage (new Gtk::HBox());
710 hb->pack_start (*lbl, true, false);
711 hb->pack_start (*(*i)->widget (), true, false);
712 table->attach (*hb, col, cend, row, row + 1, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
715 table->attach (*((*i)->widget ()), col, cend, row, row + 1, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
722 for (DialogWidgets::const_iterator i = _widgets.begin (); i != _widgets.end () ; ++i) {
729 Dialog::run (lua_State *L)
731 _ad.get_vbox ()->show_all ();
732 switch (_ad.run ()) {
733 case Gtk::RESPONSE_ACCEPT:
740 luabridge::LuaRef rv (luabridge::newTable (L));
741 for (DialogWidgets::const_iterator i = _widgets.begin (); i != _widgets.end () ; ++i) {
744 luabridge::push (L, rv);