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/slider_controller.h"
25 #include "gtkmm2ext/utils.h"
27 #include "ardour_dialog.h"
28 #include "ardour_dropdown.h"
29 #include "luadialog.h"
32 using namespace LuaDialog;
34 /*******************************************************************************
35 * Simple Message Dialog
37 Message::Message (std::string const& title, std::string const& msg, Message::MessageType mt, Message::ButtonType bt)
38 : _message_dialog (msg, false, to_gtk_mt (mt), to_gtk_bt (bt), true)
40 _message_dialog.set_title (title);
46 int rv = _message_dialog.run ();
47 _message_dialog.hide ();
49 case Gtk::RESPONSE_OK:
51 case Gtk::RESPONSE_CANCEL:
53 case Gtk::RESPONSE_CLOSE:
55 case Gtk::RESPONSE_YES:
57 case Gtk::RESPONSE_NO:
66 Message::to_gtk_bt (ButtonType bt)
70 return Gtk::BUTTONS_OK;
72 return Gtk::BUTTONS_CLOSE;
74 return Gtk::BUTTONS_CANCEL;
76 return Gtk::BUTTONS_YES_NO;
78 return Gtk::BUTTONS_OK_CANCEL;
81 return Gtk::BUTTONS_OK;
85 Message::to_gtk_mt (MessageType mt)
89 return Gtk::MESSAGE_INFO;
91 return Gtk::MESSAGE_WARNING;
93 return Gtk::MESSAGE_QUESTION;
95 return Gtk::MESSAGE_ERROR;
98 return Gtk::MESSAGE_INFO;
102 /* *****************************************************************************
106 class LuaDialogLabel : public LuaDialogWidget
109 LuaDialogLabel (std::string const& title, Gtk::AlignmentEnum xalign)
110 : LuaDialogWidget ("", "")
111 , _lbl ("<b>" + title + "</b>", xalign, Gtk::ALIGN_CENTER, false)
113 _lbl.set_use_markup ();
116 Gtk::Widget* widget ()
121 void assign (luabridge::LuaRef* rv) const { }
126 class LuaDialogCheckbox : public LuaDialogWidget
129 LuaDialogCheckbox (std::string const& key, std::string const& title, bool on)
130 : LuaDialogWidget (key, "")
136 Gtk::Widget* widget ()
141 void assign (luabridge::LuaRef* rv) const
143 (*rv)[_key] = _cb.get_active ();
147 Gtk::CheckButton _cb;
150 class LuaDialogEntry : public LuaDialogWidget
153 LuaDialogEntry (std::string const& key, std::string const& title, std::string const& dflt)
154 : LuaDialogWidget (key, title)
156 _entry.set_text (dflt);
159 Gtk::Widget* widget ()
164 void assign (luabridge::LuaRef* rv) const
166 (*rv)[_key] = std::string (_entry.get_text ());
173 class LuaDialogFader : public LuaDialogWidget
176 LuaDialogFader (std::string const& key, std::string const& title, double dflt)
177 : LuaDialogWidget (key, title)
178 , _db_adjustment (ARDOUR::gain_to_slider_position_with_max (1.0, ARDOUR::Config->get_max_gain ()), 0, 1, 0.01, 0.1)
180 _db_slider = Gtk::manage (new Gtkmm2ext::HSliderController (&_db_adjustment, boost::shared_ptr<PBD::Controllable> (), 220, 18));
182 _fader_centering_box.pack_start (*_db_slider, true, false);
184 _box.set_spacing (4);
185 _box.set_homogeneous (false);
186 _box.pack_start (_fader_centering_box, false, false);
187 _box.pack_start (_db_display, false, false);
188 _box.pack_start (*Gtk::manage (new Gtk::Label ("dB")), false, false);
190 Gtkmm2ext::set_size_request_to_display_given_text (_db_display, "-99.00", 12, 0);
192 _db_adjustment.signal_value_changed ().connect (sigc::mem_fun (*this, &LuaDialogFader::db_changed));
193 _db_display.signal_activate ().connect (sigc::mem_fun (*this, &LuaDialogFader::on_activate));
194 _db_display.signal_key_press_event ().connect (sigc::mem_fun (*this, &LuaDialogFader::on_key_press), false);
196 double coeff_val = dB_to_coefficient (dflt);
197 _db_adjustment.set_value (ARDOUR::gain_to_slider_position_with_max (coeff_val, ARDOUR::Config->get_max_gain ()));
201 Gtk::Widget* widget ()
206 void assign (luabridge::LuaRef* rv) const
208 double const val = ARDOUR::slider_position_to_gain_with_max (_db_adjustment.get_value (), ARDOUR::Config->get_max_gain ());
209 (*rv)[_key] = accurate_coefficient_to_dB (val);
215 double const val = ARDOUR::slider_position_to_gain_with_max (_db_adjustment.get_value (), ARDOUR::Config->get_max_gain ());
218 snprintf (buf, sizeof (buf), "-inf");
220 snprintf (buf, sizeof (buf), "%.2f", accurate_coefficient_to_dB (val));
222 _db_display.set_text (buf);
227 float db_val = atof (_db_display.get_text ().c_str ());
228 double coeff_val = dB_to_coefficient (db_val);
229 _db_adjustment.set_value (ARDOUR::gain_to_slider_position_with_max (coeff_val, ARDOUR::Config->get_max_gain ()));
232 bool on_key_press (GdkEventKey* ev)
234 if (ARDOUR_UI_UTILS::key_is_legal_for_numeric_entry (ev->keyval)) {
240 Gtk::Adjustment _db_adjustment;
241 Gtkmm2ext::HSliderController* _db_slider;
242 Gtk::Entry _db_display;
244 Gtk::VBox _fader_centering_box;
247 class LuaDialogSlider : public LuaDialogWidget
250 LuaDialogSlider (std::string const& key, std::string const& title, double lower, double upper, double dflt, int digits, luabridge::LuaRef scalepoints)
251 : LuaDialogWidget (key, title)
252 , _adj (dflt, lower, upper, 1, (upper - lower) / 20, 0)
255 _hscale.set_digits (digits);
256 _hscale.set_draw_value (true);
257 _hscale.set_value_pos (Gtk::POS_TOP);
259 if (!scalepoints.isTable ()) {
263 for (luabridge::Iterator i (scalepoints); !i.isNil (); ++i) {
264 if (!i.key ().isNumber ()) { continue; }
265 if (!i.value ().isString ()) { continue; }
266 _hscale.add_mark (i.key ().cast<double> (), Gtk::POS_BOTTOM, i.value ().cast<std::string> ());
270 Gtk::Widget* widget ()
275 void assign (luabridge::LuaRef* rv) const
277 (*rv)[_key] = _adj.get_value ();
281 Gtk::Adjustment _adj;
285 class LuaDialogSpinBox : public LuaDialogWidget
288 LuaDialogSpinBox (std::string const& key, std::string const& title, double lower, double upper, double dflt, double step, int digits)
289 : LuaDialogWidget (key, title)
290 , _adj (dflt, lower, upper, step, step, 0)
293 _spin.set_digits (digits);
296 Gtk::Widget* widget ()
301 void assign (luabridge::LuaRef* rv) const
303 (*rv)[_key] = _adj.get_value ();
307 Gtk::Adjustment _adj;
308 Gtk::SpinButton _spin;
311 class LuaDialogRadio : public LuaDialogWidget
314 LuaDialogRadio (std::string const& key, std::string const& title, luabridge::LuaRef values, std::string const& dflt)
315 : LuaDialogWidget (key, title)
318 for (luabridge::Iterator i (values); !i.isNil (); ++i) {
319 if (!i.key ().isString ()) { continue; }
320 std::string key = i.key ().cast<std::string> ();
321 Gtk::RadioButton* rb = Gtk::manage (new Gtk::RadioButton (_group, key));
322 _hbox.pack_start (*rb);
323 luabridge::LuaRef* ref = new luabridge::LuaRef (i.value ());
324 _refs.push_back (ref);
325 if (!_rv) { _rv = ref; }
326 rb->signal_toggled ().connect (sigc::bind (
327 sigc::mem_fun (*this, &LuaDialogRadio::rb_toggled), rb, ref
338 for (std::vector<luabridge::LuaRef*>::const_iterator i = _refs.begin (); i != _refs.end (); ++i) {
344 Gtk::Widget* widget ()
349 void assign (luabridge::LuaRef* rv) const
354 (*rv)[_key] = luabridge::Nil ();
359 LuaDialogRadio (LuaDialogRadio const&); // prevent cc
360 void rb_toggled (Gtk::RadioButton* b, luabridge::LuaRef* rv) {
361 if (b->get_active ()) {
367 Gtk::RadioButtonGroup _group;
368 std::vector<luabridge::LuaRef*> _refs;
369 luabridge::LuaRef* _rv;
372 class LuaDialogDropDown : public LuaDialogWidget
375 LuaDialogDropDown (std::string const& key, std::string const& title, luabridge::LuaRef values, std::string const& dflt)
376 : LuaDialogWidget (key, title)
379 populate (_dd.items (), values, dflt);
382 ~LuaDialogDropDown ()
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 void populate (Gtk::Menu_Helpers::MenuList& items, luabridge::LuaRef values, std::string const& dflt)
407 using namespace Gtk::Menu_Helpers;
408 for (luabridge::Iterator i (values); !i.isNil (); ++i) {
409 if (!i.key ().isString ()) { continue; }
410 std::string key = i.key ().cast<std::string> ();
411 if (i.value ().isTable ()) {
412 Gtk::Menu* menu = Gtk::manage (new Gtk::Menu);
413 items.push_back (MenuElem (key, *menu));
414 populate (menu->items (), i.value (), dflt);
417 luabridge::LuaRef* ref = new luabridge::LuaRef (i.value ());
418 _refs.push_back (ref);
419 items.push_back (MenuElem (i.key ().cast<std::string> (),
420 sigc::bind (sigc::mem_fun (*this, &LuaDialogDropDown::dd_select), key, ref)));
422 if (!_rv || key == dflt) {
429 void dd_select (std::string const& key, luabridge::LuaRef* rv) {
435 std::vector<luabridge::LuaRef*> _refs;
436 luabridge::LuaRef* _rv;
439 class LuaFileChooser : public LuaDialogWidget
442 LuaFileChooser (std::string const& key, std::string const& title, Gtk::FileChooserAction a, const std::string& path)
443 : LuaDialogWidget (key, title)
446 if (!path.empty ()) {
448 case Gtk::FILE_CHOOSER_ACTION_OPEN:
449 case Gtk::FILE_CHOOSER_ACTION_SAVE:
450 if (Glib::file_test (path, Glib::FILE_TEST_IS_REGULAR|Glib::FILE_TEST_EXISTS)) {
451 _fc.set_filename (path);
454 case Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER:
455 if (Glib::file_test (path, Glib::FILE_TEST_IS_DIR|Glib::FILE_TEST_EXISTS)) {
456 _fc.set_filename (path);
459 case Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER:
465 Gtk::Widget* widget ()
470 void assign (luabridge::LuaRef* rv) const
472 (*rv)[_key] = std::string (_fc.get_filename ());
476 Gtk::FileChooserButton _fc;
481 /*******************************************************************************
482 * Lua Parameter Dialog
484 Dialog::Dialog (std::string const& title, luabridge::LuaRef lr)
485 :_ad (title, true, false)
488 if (!lr.isTable ()) {
491 for (luabridge::Iterator i (lr); !i.isNil (); ++i) {
492 if (!i.key ().isNumber ()) { continue; }
493 if (!i.value ().isTable ()) { continue; }
494 if (!i.value ()["title"].isString ()) { continue; }
495 if (!i.value ()["type"].isString ()) { continue; }
497 std::string title = i.value ()["title"].cast<std::string> ();
498 std::string type = i.value ()["type"].cast<std::string> ();
500 if (type == "heading") {
501 Gtk::AlignmentEnum xalign = Gtk::ALIGN_CENTER;
502 if (i.value ()["align"].isString ()) {
503 std::string align = i.value ()["align"].cast <std::string> ();
504 if (align == "left") {
505 xalign = Gtk::ALIGN_LEFT;
506 } else if (align == "right") {
507 xalign = Gtk::ALIGN_RIGHT;
510 _widgets.push_back (new LuaDialogLabel (title, xalign));
514 if (!i.value ()["key"].isString ()) { continue; }
515 std::string key = i.value ()["key"].cast<std::string> ();
517 if (type == "checkbox") {
519 if (i.value ()["default"].isBoolean ()) {
520 dflt = i.value ()["default"].cast<bool> ();
522 _widgets.push_back (new LuaDialogCheckbox (key, title, dflt));
523 } else if (type == "entry") {
525 if (i.value ()["default"].isString ()) {
526 dflt = i.value ()["default"].cast<std::string> ();
528 _widgets.push_back (new LuaDialogEntry (key, title, dflt));
529 } else if (type == "radio") {
531 if (!i.value ()["values"].isTable ()) {
534 if (i.value ()["default"].isString ()) {
535 dflt = i.value ()["default"].cast<std::string> ();
537 _widgets.push_back (new LuaDialogRadio (key, title, i.value ()["values"], dflt));
538 } else if (type == "fader") {
540 if (i.value ()["default"].isNumber ()) {
541 dflt = i.value ()["default"].cast<double> ();
543 _widgets.push_back (new LuaDialogFader (key, title, dflt));
544 } else if (type == "slider") {
545 double lower, upper, dflt;
547 if (!i.value ()["min"].isNumber ()) { continue; }
548 if (!i.value ()["max"].isNumber ()) { continue; }
549 lower = i.value ()["min"].cast<double> ();
550 upper = i.value ()["max"].cast<double> ();
551 if (i.value ()["default"].isNumber ()) {
552 dflt = i.value ()["default"].cast<double> ();
556 if (i.value ()["digits"].isNumber ()) {
557 digits = i.value ()["digits"].cast<int> ();
559 _widgets.push_back (new LuaDialogSlider (key, title, lower, upper, dflt, digits, i.value ()["scalepoints"]));
560 } else if (type == "number") {
561 double lower, upper, dflt, step;
563 if (!i.value ()["min"].isNumber ()) { continue; }
564 if (!i.value ()["max"].isNumber ()) { continue; }
565 lower = i.value ()["min"].cast<double> ();
566 upper = i.value ()["max"].cast<double> ();
567 if (i.value ()["default"].isNumber ()) {
568 dflt = i.value ()["default"].cast<double> ();
572 if (i.value ()["step"].isNumber ()) {
573 step = i.value ()["step"].cast<double> ();
577 if (i.value ()["digits"].isNumber ()) {
578 digits = i.value ()["digits"].cast<int> ();
580 _widgets.push_back (new LuaDialogSpinBox (key, title, lower, upper, dflt, step, digits));
581 } else if (type == "dropdown") {
583 if (!i.value ()["values"].isTable ()) {
586 if (i.value ()["default"].isString ()) {
587 dflt = i.value ()["default"].cast<std::string> ();
589 _widgets.push_back (new LuaDialogDropDown (key, title, i.value ()["values"], dflt));
590 } else if (type == "file") {
592 if (i.value ()["path"].isString ()) {
593 path = i.value ()["path"].cast<std::string> ();
595 _widgets.push_back (new LuaFileChooser (key, title, Gtk::FILE_CHOOSER_ACTION_OPEN, path));
596 } else if (type == "folder") {
598 if (i.value ()["path"].isString ()) {
599 path = i.value ()["path"].cast<std::string> ();
601 _widgets.push_back (new LuaFileChooser (key, title, Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER, path));
605 _ad.add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
606 _ad.add_button (Gtk::Stock::OK, Gtk::RESPONSE_ACCEPT);
608 Gtk::Table* table = Gtk::manage (new Gtk::Table ());
609 table->set_col_spacings (4);
610 table->set_row_spacings (8);
611 _ad.get_vbox ()->pack_start (*table);
614 for (DialogWidgets::const_iterator i = _widgets.begin (); i != _widgets.end (); ++i) {
615 std::string const& label = (*i)->label ();
616 if (!label.empty ()) {
617 Gtk::Label* lbl = Gtk::manage (new Gtk::Label (label + ":", Gtk::ALIGN_END, Gtk::ALIGN_CENTER, false));
618 table->attach (*lbl, 0, 1, row, row + 1, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
619 table->attach (*((*i)->widget ()), 1, 2, row, row + 1, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
620 } else if ((*i)->key ().empty ()) {
621 table->attach (*((*i)->widget ()), 0, 2, row, row + 1, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
623 table->attach (*((*i)->widget ()), 1, 2, row, row + 1, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
631 for (DialogWidgets::const_iterator i = _widgets.begin (); i != _widgets.end () ; ++i) {
638 Dialog::run (lua_State *L)
640 _ad.get_vbox ()->show_all ();
641 switch (_ad.run ()) {
642 case Gtk::RESPONSE_ACCEPT:
649 luabridge::LuaRef rv (luabridge::newTable (L));
650 for (DialogWidgets::const_iterator i = _widgets.begin (); i != _widgets.end () ; ++i) {
653 luabridge::push (L, rv);