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"
33 using namespace LuaDialog;
35 /*******************************************************************************
36 * Simple Message Dialog
38 Message::Message (std::string const& title, std::string const& msg, Message::MessageType mt, Message::ButtonType bt)
39 : _message_dialog (msg, false, to_gtk_mt (mt), to_gtk_bt (bt), true)
41 _message_dialog.set_title (title);
47 int rv = _message_dialog.run ();
48 _message_dialog.hide ();
50 case Gtk::RESPONSE_OK:
52 case Gtk::RESPONSE_CANCEL:
54 case Gtk::RESPONSE_CLOSE:
56 case Gtk::RESPONSE_YES:
58 case Gtk::RESPONSE_NO:
67 Message::to_gtk_bt (ButtonType bt)
71 return Gtk::BUTTONS_OK;
73 return Gtk::BUTTONS_CLOSE;
75 return Gtk::BUTTONS_CANCEL;
77 return Gtk::BUTTONS_YES_NO;
79 return Gtk::BUTTONS_OK_CANCEL;
82 return Gtk::BUTTONS_OK;
86 Message::to_gtk_mt (MessageType mt)
90 return Gtk::MESSAGE_INFO;
92 return Gtk::MESSAGE_WARNING;
94 return Gtk::MESSAGE_QUESTION;
96 return Gtk::MESSAGE_ERROR;
99 return Gtk::MESSAGE_INFO;
103 /* *****************************************************************************
107 class LuaDialogLabel : public LuaDialogWidget
110 LuaDialogLabel (std::string const& title, Gtk::AlignmentEnum xalign)
111 : LuaDialogWidget ("", "")
112 , _lbl ("<b>" + title + "</b>", xalign, Gtk::ALIGN_CENTER, false)
114 _lbl.set_use_markup ();
117 Gtk::Widget* widget ()
122 void assign (luabridge::LuaRef* rv) const { }
127 class LuaDialogCheckbox : public LuaDialogWidget
130 LuaDialogCheckbox (std::string const& key, std::string const& title, bool on)
131 : LuaDialogWidget (key, "")
137 Gtk::Widget* widget ()
142 void assign (luabridge::LuaRef* rv) const
144 (*rv)[_key] = _cb.get_active ();
148 Gtk::CheckButton _cb;
151 class LuaDialogEntry : public LuaDialogWidget
154 LuaDialogEntry (std::string const& key, std::string const& title, std::string const& dflt)
155 : LuaDialogWidget (key, title)
157 _entry.set_text (dflt);
160 Gtk::Widget* widget ()
165 void assign (luabridge::LuaRef* rv) const
167 (*rv)[_key] = std::string (_entry.get_text ());
174 class LuaDialogFader : public LuaDialogWidget
177 LuaDialogFader (std::string const& key, std::string const& title, double dflt)
178 : LuaDialogWidget (key, title)
179 , _db_adjustment (ARDOUR::gain_to_slider_position_with_max (1.0, ARDOUR::Config->get_max_gain ()), 0, 1, 0.01, 0.1)
181 _db_slider = Gtk::manage (new ArdourWidgets::HSliderController (&_db_adjustment, boost::shared_ptr<PBD::Controllable> (), 220, 18));
183 _fader_centering_box.pack_start (*_db_slider, true, false);
185 _box.set_spacing (4);
186 _box.set_homogeneous (false);
187 _box.pack_start (_fader_centering_box, false, false);
188 _box.pack_start (_db_display, false, false);
189 _box.pack_start (*Gtk::manage (new Gtk::Label ("dB")), false, false);
191 Gtkmm2ext::set_size_request_to_display_given_text (_db_display, "-99.00", 12, 0);
193 _db_adjustment.signal_value_changed ().connect (sigc::mem_fun (*this, &LuaDialogFader::db_changed));
194 _db_display.signal_activate ().connect (sigc::mem_fun (*this, &LuaDialogFader::on_activate));
195 _db_display.signal_key_press_event ().connect (sigc::mem_fun (*this, &LuaDialogFader::on_key_press), false);
197 double coeff_val = dB_to_coefficient (dflt);
198 _db_adjustment.set_value (ARDOUR::gain_to_slider_position_with_max (coeff_val, ARDOUR::Config->get_max_gain ()));
202 Gtk::Widget* widget ()
207 void assign (luabridge::LuaRef* rv) const
209 double const val = ARDOUR::slider_position_to_gain_with_max (_db_adjustment.get_value (), ARDOUR::Config->get_max_gain ());
210 (*rv)[_key] = accurate_coefficient_to_dB (val);
216 double const val = ARDOUR::slider_position_to_gain_with_max (_db_adjustment.get_value (), ARDOUR::Config->get_max_gain ());
219 snprintf (buf, sizeof (buf), "-inf");
221 snprintf (buf, sizeof (buf), "%.2f", accurate_coefficient_to_dB (val));
223 _db_display.set_text (buf);
228 float db_val = atof (_db_display.get_text ().c_str ());
229 double coeff_val = dB_to_coefficient (db_val);
230 _db_adjustment.set_value (ARDOUR::gain_to_slider_position_with_max (coeff_val, ARDOUR::Config->get_max_gain ()));
233 bool on_key_press (GdkEventKey* ev)
235 if (ARDOUR_UI_UTILS::key_is_legal_for_numeric_entry (ev->keyval)) {
241 Gtk::Adjustment _db_adjustment;
242 ArdourWidgets::HSliderController* _db_slider;
243 Gtk::Entry _db_display;
245 Gtk::VBox _fader_centering_box;
248 class LuaDialogSlider : public LuaDialogWidget
251 LuaDialogSlider (std::string const& key, std::string const& title, double lower, double upper, double dflt, int digits, luabridge::LuaRef scalepoints)
252 : LuaDialogWidget (key, title)
253 , _adj (dflt, lower, upper, 1, (upper - lower) / 20, 0)
256 _hscale.set_digits (digits);
257 _hscale.set_draw_value (true);
258 _hscale.set_value_pos (Gtk::POS_TOP);
260 if (!scalepoints.isTable ()) {
264 for (luabridge::Iterator i (scalepoints); !i.isNil (); ++i) {
265 if (!i.key ().isNumber ()) { continue; }
266 if (!i.value ().isString ()) { continue; }
267 _hscale.add_mark (i.key ().cast<double> (), Gtk::POS_BOTTOM, i.value ().cast<std::string> ());
271 Gtk::Widget* widget ()
276 void assign (luabridge::LuaRef* rv) const
278 (*rv)[_key] = _adj.get_value ();
282 Gtk::Adjustment _adj;
286 class LuaDialogSpinBox : public LuaDialogWidget
289 LuaDialogSpinBox (std::string const& key, std::string const& title, double lower, double upper, double dflt, double step, int digits)
290 : LuaDialogWidget (key, title)
291 , _adj (dflt, lower, upper, step, step, 0)
294 _spin.set_digits (digits);
297 Gtk::Widget* widget ()
302 void assign (luabridge::LuaRef* rv) const
304 (*rv)[_key] = _adj.get_value ();
308 Gtk::Adjustment _adj;
309 Gtk::SpinButton _spin;
312 class LuaDialogRadio : public LuaDialogWidget
315 LuaDialogRadio (std::string const& key, std::string const& title, luabridge::LuaRef values, std::string const& dflt)
316 : LuaDialogWidget (key, title)
319 for (luabridge::Iterator i (values); !i.isNil (); ++i) {
320 if (!i.key ().isString ()) { continue; }
321 std::string key = i.key ().cast<std::string> ();
322 Gtk::RadioButton* rb = Gtk::manage (new Gtk::RadioButton (_group, key));
323 _hbox.pack_start (*rb);
324 luabridge::LuaRef* ref = new luabridge::LuaRef (i.value ());
325 _refs.push_back (ref);
326 if (!_rv) { _rv = ref; }
327 rb->signal_toggled ().connect (sigc::bind (
328 sigc::mem_fun (*this, &LuaDialogRadio::rb_toggled), rb, ref
339 for (std::vector<luabridge::LuaRef*>::const_iterator i = _refs.begin (); i != _refs.end (); ++i) {
345 Gtk::Widget* widget ()
350 void assign (luabridge::LuaRef* rv) const
355 (*rv)[_key] = luabridge::Nil ();
360 LuaDialogRadio (LuaDialogRadio const&); // prevent cc
361 void rb_toggled (Gtk::RadioButton* b, luabridge::LuaRef* rv) {
362 if (b->get_active ()) {
368 Gtk::RadioButtonGroup _group;
369 std::vector<luabridge::LuaRef*> _refs;
370 luabridge::LuaRef* _rv;
373 class LuaDialogDropDown : public LuaDialogWidget
376 LuaDialogDropDown (std::string const& key, std::string const& title, luabridge::LuaRef values, std::string const& dflt)
377 : LuaDialogWidget (key, title)
380 populate (_dd.items (), values, dflt);
383 ~LuaDialogDropDown ()
385 for (std::vector<luabridge::LuaRef*>::const_iterator i = _refs.begin (); i != _refs.end (); ++i) {
391 Gtk::Widget* widget ()
396 void assign (luabridge::LuaRef* rv) const
401 (*rv)[_key] = luabridge::Nil ();
406 void populate (Gtk::Menu_Helpers::MenuList& items, luabridge::LuaRef values, std::string const& dflt)
408 using namespace Gtk::Menu_Helpers;
409 for (luabridge::Iterator i (values); !i.isNil (); ++i) {
410 if (!i.key ().isString ()) { continue; }
411 std::string key = i.key ().cast<std::string> ();
412 if (i.value ().isTable ()) {
413 Gtk::Menu* menu = Gtk::manage (new Gtk::Menu);
414 items.push_back (MenuElem (key, *menu));
415 populate (menu->items (), i.value (), dflt);
418 luabridge::LuaRef* ref = new luabridge::LuaRef (i.value ());
419 _refs.push_back (ref);
420 items.push_back (MenuElem (i.key ().cast<std::string> (),
421 sigc::bind (sigc::mem_fun (*this, &LuaDialogDropDown::dd_select), key, ref)));
423 if (!_rv || key == dflt) {
430 void dd_select (std::string const& key, luabridge::LuaRef* rv) {
435 ArdourWidgets::ArdourDropdown _dd;
436 std::vector<luabridge::LuaRef*> _refs;
437 luabridge::LuaRef* _rv;
440 class LuaFileChooser : public LuaDialogWidget
443 LuaFileChooser (std::string const& key, std::string const& title, Gtk::FileChooserAction a, const std::string& path)
444 : LuaDialogWidget (key, title)
447 if (!path.empty ()) {
449 case Gtk::FILE_CHOOSER_ACTION_OPEN:
450 case Gtk::FILE_CHOOSER_ACTION_SAVE:
451 if (Glib::file_test (path, Glib::FILE_TEST_IS_REGULAR|Glib::FILE_TEST_EXISTS)) {
452 _fc.set_filename (path);
455 case Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER:
456 if (Glib::file_test (path, Glib::FILE_TEST_IS_DIR|Glib::FILE_TEST_EXISTS)) {
457 _fc.set_filename (path);
460 case Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER:
466 Gtk::Widget* widget ()
471 void assign (luabridge::LuaRef* rv) const
473 (*rv)[_key] = std::string (_fc.get_filename ());
477 Gtk::FileChooserButton _fc;
482 /*******************************************************************************
483 * Lua Parameter Dialog
485 Dialog::Dialog (std::string const& title, luabridge::LuaRef lr)
486 :_ad (title, true, false)
489 if (!lr.isTable ()) {
492 for (luabridge::Iterator i (lr); !i.isNil (); ++i) {
493 if (!i.key ().isNumber ()) { continue; }
494 if (!i.value ().isTable ()) { continue; }
495 if (!i.value ()["title"].isString ()) { continue; }
496 if (!i.value ()["type"].isString ()) { continue; }
498 std::string title = i.value ()["title"].cast<std::string> ();
499 std::string type = i.value ()["type"].cast<std::string> ();
501 if (type == "heading") {
502 Gtk::AlignmentEnum xalign = Gtk::ALIGN_CENTER;
503 if (i.value ()["align"].isString ()) {
504 std::string align = i.value ()["align"].cast <std::string> ();
505 if (align == "left") {
506 xalign = Gtk::ALIGN_LEFT;
507 } else if (align == "right") {
508 xalign = Gtk::ALIGN_RIGHT;
511 _widgets.push_back (new LuaDialogLabel (title, xalign));
515 if (!i.value ()["key"].isString ()) { continue; }
516 std::string key = i.value ()["key"].cast<std::string> ();
518 if (type == "checkbox") {
520 if (i.value ()["default"].isBoolean ()) {
521 dflt = i.value ()["default"].cast<bool> ();
523 _widgets.push_back (new LuaDialogCheckbox (key, title, dflt));
524 } else if (type == "entry") {
526 if (i.value ()["default"].isString ()) {
527 dflt = i.value ()["default"].cast<std::string> ();
529 _widgets.push_back (new LuaDialogEntry (key, title, dflt));
530 } else if (type == "radio") {
532 if (!i.value ()["values"].isTable ()) {
535 if (i.value ()["default"].isString ()) {
536 dflt = i.value ()["default"].cast<std::string> ();
538 _widgets.push_back (new LuaDialogRadio (key, title, i.value ()["values"], dflt));
539 } else if (type == "fader") {
541 if (i.value ()["default"].isNumber ()) {
542 dflt = i.value ()["default"].cast<double> ();
544 _widgets.push_back (new LuaDialogFader (key, title, dflt));
545 } else if (type == "slider") {
546 double lower, upper, dflt;
548 if (!i.value ()["min"].isNumber ()) { continue; }
549 if (!i.value ()["max"].isNumber ()) { continue; }
550 lower = i.value ()["min"].cast<double> ();
551 upper = i.value ()["max"].cast<double> ();
552 if (i.value ()["default"].isNumber ()) {
553 dflt = i.value ()["default"].cast<double> ();
557 if (i.value ()["digits"].isNumber ()) {
558 digits = i.value ()["digits"].cast<int> ();
560 _widgets.push_back (new LuaDialogSlider (key, title, lower, upper, dflt, digits, i.value ()["scalepoints"]));
561 } else if (type == "number") {
562 double lower, upper, dflt, step;
564 if (!i.value ()["min"].isNumber ()) { continue; }
565 if (!i.value ()["max"].isNumber ()) { continue; }
566 lower = i.value ()["min"].cast<double> ();
567 upper = i.value ()["max"].cast<double> ();
568 if (i.value ()["default"].isNumber ()) {
569 dflt = i.value ()["default"].cast<double> ();
573 if (i.value ()["step"].isNumber ()) {
574 step = i.value ()["step"].cast<double> ();
578 if (i.value ()["digits"].isNumber ()) {
579 digits = i.value ()["digits"].cast<int> ();
581 _widgets.push_back (new LuaDialogSpinBox (key, title, lower, upper, dflt, step, digits));
582 } else if (type == "dropdown") {
584 if (!i.value ()["values"].isTable ()) {
587 if (i.value ()["default"].isString ()) {
588 dflt = i.value ()["default"].cast<std::string> ();
590 _widgets.push_back (new LuaDialogDropDown (key, title, i.value ()["values"], dflt));
591 } else if (type == "file") {
593 if (i.value ()["path"].isString ()) {
594 path = i.value ()["path"].cast<std::string> ();
596 _widgets.push_back (new LuaFileChooser (key, title, Gtk::FILE_CHOOSER_ACTION_OPEN, path));
597 } else if (type == "folder") {
599 if (i.value ()["path"].isString ()) {
600 path = i.value ()["path"].cast<std::string> ();
602 _widgets.push_back (new LuaFileChooser (key, title, Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER, path));
606 _ad.add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
607 _ad.add_button (Gtk::Stock::OK, Gtk::RESPONSE_ACCEPT);
609 Gtk::Table* table = Gtk::manage (new Gtk::Table ());
610 table->set_col_spacings (4);
611 table->set_row_spacings (8);
612 _ad.get_vbox ()->pack_start (*table);
615 for (DialogWidgets::const_iterator i = _widgets.begin (); i != _widgets.end (); ++i) {
616 std::string const& label = (*i)->label ();
617 if (!label.empty ()) {
618 Gtk::Label* lbl = Gtk::manage (new Gtk::Label (label + ":", Gtk::ALIGN_END, Gtk::ALIGN_CENTER, false));
619 table->attach (*lbl, 0, 1, row, row + 1, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
620 table->attach (*((*i)->widget ()), 1, 2, row, row + 1, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
621 } else if ((*i)->key ().empty ()) {
622 table->attach (*((*i)->widget ()), 0, 2, row, row + 1, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
624 table->attach (*((*i)->widget ()), 1, 2, row, row + 1, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
632 for (DialogWidgets::const_iterator i = _widgets.begin (); i != _widgets.end () ; ++i) {
639 Dialog::run (lua_State *L)
641 _ad.get_vbox ()->show_all ();
642 switch (_ad.run ()) {
643 case Gtk::RESPONSE_ACCEPT:
650 luabridge::LuaRef rv (luabridge::newTable (L));
651 for (DialogWidgets::const_iterator i = _widgets.begin (); i != _widgets.end () ; ++i) {
654 luabridge::push (L, rv);