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.
23 #include "ardour/dB.h"
24 #include "ardour/rc_configuration.h"
26 #include "gtkmm2ext/utils.h"
28 #include "widgets/ardour_dropdown.h"
29 #include "widgets/slider_controller.h"
31 #include "ardour_dialog.h"
32 #include "luadialog.h"
36 using namespace LuaDialog;
38 /*******************************************************************************
39 * Simple Message Dialog
41 Message::Message (std::string const& title, std::string const& msg, Message::MessageType mt, Message::ButtonType bt)
42 : _message_dialog (msg, false, to_gtk_mt (mt), to_gtk_bt (bt), true)
44 _message_dialog.set_title (title);
51 bool splash_pushed = false;
52 Splash* spl = Splash::instance();
53 if (spl && spl->is_visible()) {
54 spl->pop_back_for (_message_dialog);
58 int rv = _message_dialog.run ();
59 _message_dialog.hide ();
62 spl = Splash::instance();
69 case Gtk::RESPONSE_OK:
71 case Gtk::RESPONSE_CANCEL:
73 case Gtk::RESPONSE_CLOSE:
75 case Gtk::RESPONSE_YES:
77 case Gtk::RESPONSE_NO:
86 Message::to_gtk_bt (ButtonType bt)
90 return Gtk::BUTTONS_OK;
92 return Gtk::BUTTONS_CLOSE;
94 return Gtk::BUTTONS_CANCEL;
96 return Gtk::BUTTONS_YES_NO;
98 return Gtk::BUTTONS_OK_CANCEL;
101 return Gtk::BUTTONS_OK;
105 Message::to_gtk_mt (MessageType mt)
109 return Gtk::MESSAGE_INFO;
111 return Gtk::MESSAGE_WARNING;
113 return Gtk::MESSAGE_QUESTION;
115 return Gtk::MESSAGE_ERROR;
118 return Gtk::MESSAGE_INFO;
122 /* *****************************************************************************
126 class LuaDialogLabel : public LuaDialogWidget
129 LuaDialogLabel (std::string const& title, Gtk::AlignmentEnum xalign)
130 : LuaDialogWidget ("", "", 0, 2)
131 , _lbl (title, xalign, Gtk::ALIGN_CENTER, false)
134 Gtk::Widget* widget ()
139 void assign (luabridge::LuaRef* rv) const { }
145 class LuaDialogHeading : public LuaDialogLabel
148 LuaDialogHeading (std::string const& title, Gtk::AlignmentEnum xalign)
149 : LuaDialogLabel ("<b>" + title + "</b>", xalign)
151 _lbl.set_use_markup ();
155 class LuaHSeparator : public LuaDialogWidget
159 : LuaDialogWidget ("", "", 0, 2)
162 Gtk::Widget* widget ()
167 void assign (luabridge::LuaRef* rv) const { }
169 Gtk::HSeparator _sep;
172 class LuaDialogCheckbox : public LuaDialogWidget
175 LuaDialogCheckbox (std::string const& key, std::string const& title, bool on)
176 : LuaDialogWidget (key, "", 1, 1)
178 if (!title.empty ()) {
179 _cb.add_label (title, false, 0);
184 Gtk::Widget* widget ()
189 void assign (luabridge::LuaRef* rv) const
191 (*rv)[_key] = _cb.get_active ();
195 Gtk::CheckButton _cb;
198 class LuaDialogEntry : public LuaDialogWidget
201 LuaDialogEntry (std::string const& key, std::string const& title, std::string const& dflt)
202 : LuaDialogWidget (key, title)
204 _entry.set_text (dflt);
207 Gtk::Widget* widget ()
212 void assign (luabridge::LuaRef* rv) const
214 (*rv)[_key] = std::string (_entry.get_text ());
221 class LuaDialogFader : public LuaDialogWidget
224 LuaDialogFader (std::string const& key, std::string const& title, double dflt)
225 : LuaDialogWidget (key, title)
226 , _db_adjustment (ARDOUR::gain_to_slider_position_with_max (1.0, ARDOUR::Config->get_max_gain ()), 0, 1, 0.01, 0.1)
228 _db_slider = Gtk::manage (new ArdourWidgets::HSliderController (&_db_adjustment, boost::shared_ptr<PBD::Controllable> (), 220, 18));
230 _fader_centering_box.pack_start (*_db_slider, true, false);
232 _box.set_spacing (4);
233 _box.set_homogeneous (false);
234 _box.pack_start (_fader_centering_box, false, false);
235 _box.pack_start (_db_display, false, false);
236 _box.pack_start (*Gtk::manage (new Gtk::Label ("dB")), false, false);
238 Gtkmm2ext::set_size_request_to_display_given_text (_db_display, "-99.00", 12, 0);
240 _db_adjustment.signal_value_changed ().connect (sigc::mem_fun (*this, &LuaDialogFader::db_changed));
241 _db_display.signal_activate ().connect (sigc::mem_fun (*this, &LuaDialogFader::on_activate));
242 _db_display.signal_key_press_event ().connect (sigc::mem_fun (*this, &LuaDialogFader::on_key_press), false);
244 double coeff_val = dB_to_coefficient (dflt);
245 _db_adjustment.set_value (ARDOUR::gain_to_slider_position_with_max (coeff_val, ARDOUR::Config->get_max_gain ()));
249 Gtk::Widget* widget ()
254 void assign (luabridge::LuaRef* rv) const
256 double const val = ARDOUR::slider_position_to_gain_with_max (_db_adjustment.get_value (), ARDOUR::Config->get_max_gain ());
257 (*rv)[_key] = accurate_coefficient_to_dB (val);
263 double const val = ARDOUR::slider_position_to_gain_with_max (_db_adjustment.get_value (), ARDOUR::Config->get_max_gain ());
266 snprintf (buf, sizeof (buf), "-inf");
268 snprintf (buf, sizeof (buf), "%.2f", accurate_coefficient_to_dB (val));
270 _db_display.set_text (buf);
275 float db_val = atof (_db_display.get_text ().c_str ());
276 double coeff_val = dB_to_coefficient (db_val);
277 _db_adjustment.set_value (ARDOUR::gain_to_slider_position_with_max (coeff_val, ARDOUR::Config->get_max_gain ()));
280 bool on_key_press (GdkEventKey* ev)
282 if (ARDOUR_UI_UTILS::key_is_legal_for_numeric_entry (ev->keyval)) {
288 Gtk::Adjustment _db_adjustment;
289 ArdourWidgets::HSliderController* _db_slider;
290 Gtk::Entry _db_display;
292 Gtk::VBox _fader_centering_box;
295 class LuaDialogSlider : public LuaDialogWidget
298 LuaDialogSlider (std::string const& key, std::string const& title, double lower, double upper, double dflt, int digits, luabridge::LuaRef scalepoints)
299 : LuaDialogWidget (key, title)
300 , _adj (dflt, lower, upper, 1, (upper - lower) / 20, 0)
303 _hscale.set_digits (digits);
304 _hscale.set_draw_value (true);
305 _hscale.set_value_pos (Gtk::POS_TOP);
307 if (!scalepoints.isTable ()) {
311 for (luabridge::Iterator i (scalepoints); !i.isNil (); ++i) {
312 if (!i.key ().isNumber ()) { continue; }
313 if (!i.value ().isString ()) { continue; }
314 _hscale.add_mark (i.key ().cast<double> (), Gtk::POS_BOTTOM, i.value ().cast<std::string> ());
318 Gtk::Widget* widget ()
323 void assign (luabridge::LuaRef* rv) const
325 (*rv)[_key] = _adj.get_value ();
329 Gtk::Adjustment _adj;
333 class LuaDialogSpinBox : public LuaDialogWidget
336 LuaDialogSpinBox (std::string const& key, std::string const& title, double lower, double upper, double dflt, double step, int digits)
337 : LuaDialogWidget (key, title)
338 , _adj (dflt, lower, upper, step, step, 0)
341 _spin.set_digits (digits);
344 Gtk::Widget* widget ()
349 void assign (luabridge::LuaRef* rv) const
351 (*rv)[_key] = _adj.get_value ();
355 Gtk::Adjustment _adj;
356 Gtk::SpinButton _spin;
359 class LuaDialogRadio : public LuaDialogWidget
362 LuaDialogRadio (std::string const& key, std::string const& title, luabridge::LuaRef values, std::string const& dflt)
363 : LuaDialogWidget (key, title)
366 for (luabridge::Iterator i (values); !i.isNil (); ++i) {
367 if (!i.key ().isString ()) { continue; }
368 std::string key = i.key ().cast<std::string> ();
369 Gtk::RadioButton* rb = Gtk::manage (new Gtk::RadioButton (_group, key));
370 _hbox.pack_start (*rb);
371 luabridge::LuaRef* ref = new luabridge::LuaRef (i.value ());
372 _refs.push_back (ref);
373 if (!_rv) { _rv = ref; }
374 rb->signal_toggled ().connect (sigc::bind (
375 sigc::mem_fun (*this, &LuaDialogRadio::rb_toggled), rb, ref
386 for (std::vector<luabridge::LuaRef*>::const_iterator i = _refs.begin (); i != _refs.end (); ++i) {
392 Gtk::Widget* widget ()
397 void assign (luabridge::LuaRef* rv) const
402 (*rv)[_key] = luabridge::Nil ();
407 LuaDialogRadio (LuaDialogRadio const&); // prevent cc
408 void rb_toggled (Gtk::RadioButton* b, luabridge::LuaRef* rv) {
409 if (b->get_active ()) {
415 Gtk::RadioButtonGroup _group;
416 std::vector<luabridge::LuaRef*> _refs;
417 luabridge::LuaRef* _rv;
420 class LuaDialogDropDown : public LuaDialogWidget
423 LuaDialogDropDown (std::string const& key, std::string const& title, luabridge::LuaRef values, std::string const& dflt)
424 : LuaDialogWidget (key, title)
427 populate (_dd.items (), values, dflt);
430 ~LuaDialogDropDown ()
432 for (std::vector<luabridge::LuaRef*>::const_iterator i = _refs.begin (); i != _refs.end (); ++i) {
438 Gtk::Widget* widget ()
443 void assign (luabridge::LuaRef* rv) const
448 (*rv)[_key] = luabridge::Nil ();
453 void populate (Gtk::Menu_Helpers::MenuList& items, luabridge::LuaRef values, std::string const& dflt)
455 using namespace Gtk::Menu_Helpers;
456 std::vector<std::string> keys;
458 for (luabridge::Iterator i (values); !i.isNil (); ++i) {
459 if (!i.key ().isString ()) { continue; }
460 keys.push_back (i.key ().cast<std::string> ());
463 std::sort (keys.begin(), keys.end());
465 for (std::vector<std::string>::const_iterator i = keys.begin (); i != keys.end(); ++i) {
466 std::string key = *i;
468 if (values[key].isTable ()) {
469 Gtk::Menu* menu = Gtk::manage (new Gtk::Menu);
470 items.push_back (MenuElem (key, *menu));
471 populate (menu->items (), values[key], dflt);
474 luabridge::LuaRef* ref = new luabridge::LuaRef (values[key]);
475 _refs.push_back (ref);
476 items.push_back (MenuElem (key,
477 sigc::bind (sigc::mem_fun (*this, &LuaDialogDropDown::dd_select), key, ref)));
479 if (!_rv || key == dflt) {
486 void dd_select (std::string const& key, luabridge::LuaRef* rv) {
491 ArdourWidgets::ArdourDropdown _dd;
492 std::vector<luabridge::LuaRef*> _refs;
493 luabridge::LuaRef* _rv;
496 class LuaFileChooser : public LuaDialogWidget
499 LuaFileChooser (std::string const& key, std::string const& title, Gtk::FileChooserAction a, const std::string& path)
500 : LuaDialogWidget (key, title)
503 if (!path.empty ()) {
505 case Gtk::FILE_CHOOSER_ACTION_OPEN:
506 case Gtk::FILE_CHOOSER_ACTION_SAVE:
507 if (Glib::file_test (path, Glib::FILE_TEST_IS_REGULAR|Glib::FILE_TEST_EXISTS)) {
508 _fc.set_filename (path);
511 case Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER:
512 if (Glib::file_test (path, Glib::FILE_TEST_IS_DIR|Glib::FILE_TEST_EXISTS)) {
513 _fc.set_filename (path);
516 case Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER:
522 Gtk::Widget* widget ()
527 void assign (luabridge::LuaRef* rv) const
529 (*rv)[_key] = std::string (_fc.get_filename ());
533 Gtk::FileChooserButton _fc;
538 /*******************************************************************************
539 * Lua Parameter Dialog
541 Dialog::Dialog (std::string const& title, luabridge::LuaRef lr)
542 :_ad (title, true, false)
545 if (!lr.isTable ()) {
548 for (luabridge::Iterator i (lr); !i.isNil (); ++i) {
549 if (!i.key ().isNumber ()) { continue; }
550 if (!i.value ().isTable ()) { continue; }
551 if (!i.value ()["title"].isString ()) { continue; }
552 if (!i.value ()["type"].isString ()) { continue; }
554 std::string title = i.value ()["title"].cast<std::string> ();
555 std::string type = i.value ()["type"].cast<std::string> ();
558 if (i.value ()["key"].isString ()) {
559 key = i.value ()["key"].cast<std::string> ();
562 LuaDialogWidget* w = NULL;
564 if (type == "heading") {
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 LuaDialogHeading (title, xalign);
575 } else if (type == "label") {
576 Gtk::AlignmentEnum xalign = Gtk::ALIGN_CENTER;
577 if (i.value ()["align"].isString ()) {
578 std::string align = i.value ()["align"].cast <std::string> ();
579 if (align == "left") {
580 xalign = Gtk::ALIGN_LEFT;
581 } else if (align == "right") {
582 xalign = Gtk::ALIGN_RIGHT;
585 w = new LuaDialogLabel (title, xalign);
586 } else if (type == "hseparator") {
587 w = new LuaHSeparator ();
589 /* the following widgets do require a key */
590 else if (key.empty ()) {
593 else if (type == "checkbox") {
595 if (i.value ()["default"].isBoolean ()) {
596 dflt = i.value ()["default"].cast<bool> ();
598 w = new LuaDialogCheckbox (key, title, dflt);
599 } else if (type == "entry") {
601 if (i.value ()["default"].isString ()) {
602 dflt = i.value ()["default"].cast<std::string> ();
604 w = new LuaDialogEntry (key, title, dflt);
605 } else if (type == "radio") {
607 if (!i.value ()["values"].isTable ()) {
610 if (i.value ()["default"].isString ()) {
611 dflt = i.value ()["default"].cast<std::string> ();
613 w = new LuaDialogRadio (key, title, i.value ()["values"], dflt);
614 } else if (type == "fader") {
616 if (i.value ()["default"].isNumber ()) {
617 dflt = i.value ()["default"].cast<double> ();
619 w = new LuaDialogFader (key, title, dflt);
620 } else if (type == "slider") {
621 double lower, upper, dflt;
623 if (!i.value ()["min"].isNumber ()) { continue; }
624 if (!i.value ()["max"].isNumber ()) { continue; }
625 lower = i.value ()["min"].cast<double> ();
626 upper = i.value ()["max"].cast<double> ();
627 if (i.value ()["default"].isNumber ()) {
628 dflt = i.value ()["default"].cast<double> ();
632 if (i.value ()["digits"].isNumber ()) {
633 digits = i.value ()["digits"].cast<int> ();
635 w = new LuaDialogSlider (key, title, lower, upper, dflt, digits, i.value ()["scalepoints"]);
636 } else if (type == "number") {
637 double lower, upper, dflt, step;
639 if (!i.value ()["min"].isNumber ()) { continue; }
640 if (!i.value ()["max"].isNumber ()) { continue; }
641 lower = i.value ()["min"].cast<double> ();
642 upper = i.value ()["max"].cast<double> ();
643 if (i.value ()["default"].isNumber ()) {
644 dflt = i.value ()["default"].cast<double> ();
648 if (i.value ()["step"].isNumber ()) {
649 step = i.value ()["step"].cast<double> ();
653 if (i.value ()["digits"].isNumber ()) {
654 digits = i.value ()["digits"].cast<int> ();
656 w = new LuaDialogSpinBox (key, title, lower, upper, dflt, step, digits);
657 } else if (type == "dropdown") {
659 if (!i.value ()["values"].isTable ()) {
662 if (i.value ()["default"].isString ()) {
663 dflt = i.value ()["default"].cast<std::string> ();
665 w = new LuaDialogDropDown (key, title, i.value ()["values"], dflt);
666 } else if (type == "file") {
668 if (i.value ()["path"].isString ()) {
669 path = i.value ()["path"].cast<std::string> ();
671 w = new LuaFileChooser (key, title, Gtk::FILE_CHOOSER_ACTION_OPEN, path);
672 } else if (type == "folder") {
674 if (i.value ()["path"].isString ()) {
675 path = i.value ()["path"].cast<std::string> ();
677 w = new LuaFileChooser (key, title, Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER, path);
681 if (i.value ()["col"].isNumber ()) {
682 w->set_col (i.value ()["col"].cast<int> ());
684 if (i.value ()["colspan"].isNumber ()) {
685 w->set_span (i.value ()["colspan"].cast<int> ());
687 _widgets.push_back(w);
691 _ad.add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
692 _ad.add_button (Gtk::Stock::OK, Gtk::RESPONSE_ACCEPT);
694 Gtk::Table* table = Gtk::manage (new Gtk::Table ());
695 table->set_col_spacings (20);
696 table->set_row_spacings (8);
697 _ad.get_vbox ()->pack_start (*table);
702 for (DialogWidgets::const_iterator i = _widgets.begin (); i != _widgets.end (); ++i) {
703 int col = (*i)->col();
704 int cend = col + (*i)->span();
706 if (col < last_end) {
711 std::string const& label = (*i)->label ();
712 if (!label.empty ()) {
713 /* items with implicit label (title) */
714 Gtk::Label* lbl = Gtk::manage (new Gtk::Label (label + ":", Gtk::ALIGN_END, Gtk::ALIGN_CENTER, false));
715 if (cend - col > 1) {
716 table->attach (*lbl, col, col + 1, row, row + 1, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
717 table->attach (*((*i)->widget ()), col + 1, cend, row, row + 1, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
719 Gtk::HBox* hb = Gtk::manage (new Gtk::HBox());
721 hb->pack_start (*lbl, true, false);
722 hb->pack_start (*(*i)->widget (), true, false);
723 table->attach (*hb, col, cend, row, row + 1, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
726 table->attach (*((*i)->widget ()), col, cend, row, row + 1, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
733 for (DialogWidgets::const_iterator i = _widgets.begin (); i != _widgets.end () ; ++i) {
740 Dialog::run (lua_State *L)
742 _ad.get_vbox ()->show_all ();
743 switch (_ad.run ()) {
744 case Gtk::RESPONSE_ACCEPT:
751 luabridge::LuaRef rv (luabridge::newTable (L));
752 for (DialogWidgets::const_iterator i = _widgets.begin (); i != _widgets.end () ; ++i) {
755 luabridge::push (L, rv);