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);
50 Splash* spl = Splash::instance();
51 if (spl && spl->is_visible()) {
52 spl->pop_back_for (_message_dialog);
53 _splash_pushed = true;
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 ("", "")
129 , _lbl ("<b>" + title + "</b>", xalign, Gtk::ALIGN_CENTER, false)
131 _lbl.set_use_markup ();
134 Gtk::Widget* widget ()
139 void assign (luabridge::LuaRef* rv) const { }
144 class LuaDialogCheckbox : public LuaDialogWidget
147 LuaDialogCheckbox (std::string const& key, std::string const& title, bool on)
148 : LuaDialogWidget (key, "")
154 Gtk::Widget* widget ()
159 void assign (luabridge::LuaRef* rv) const
161 (*rv)[_key] = _cb.get_active ();
165 Gtk::CheckButton _cb;
168 class LuaDialogEntry : public LuaDialogWidget
171 LuaDialogEntry (std::string const& key, std::string const& title, std::string const& dflt)
172 : LuaDialogWidget (key, title)
174 _entry.set_text (dflt);
177 Gtk::Widget* widget ()
182 void assign (luabridge::LuaRef* rv) const
184 (*rv)[_key] = std::string (_entry.get_text ());
191 class LuaDialogFader : public LuaDialogWidget
194 LuaDialogFader (std::string const& key, std::string const& title, double dflt)
195 : LuaDialogWidget (key, title)
196 , _db_adjustment (ARDOUR::gain_to_slider_position_with_max (1.0, ARDOUR::Config->get_max_gain ()), 0, 1, 0.01, 0.1)
198 _db_slider = Gtk::manage (new ArdourWidgets::HSliderController (&_db_adjustment, boost::shared_ptr<PBD::Controllable> (), 220, 18));
200 _fader_centering_box.pack_start (*_db_slider, true, false);
202 _box.set_spacing (4);
203 _box.set_homogeneous (false);
204 _box.pack_start (_fader_centering_box, false, false);
205 _box.pack_start (_db_display, false, false);
206 _box.pack_start (*Gtk::manage (new Gtk::Label ("dB")), false, false);
208 Gtkmm2ext::set_size_request_to_display_given_text (_db_display, "-99.00", 12, 0);
210 _db_adjustment.signal_value_changed ().connect (sigc::mem_fun (*this, &LuaDialogFader::db_changed));
211 _db_display.signal_activate ().connect (sigc::mem_fun (*this, &LuaDialogFader::on_activate));
212 _db_display.signal_key_press_event ().connect (sigc::mem_fun (*this, &LuaDialogFader::on_key_press), false);
214 double coeff_val = dB_to_coefficient (dflt);
215 _db_adjustment.set_value (ARDOUR::gain_to_slider_position_with_max (coeff_val, ARDOUR::Config->get_max_gain ()));
219 Gtk::Widget* widget ()
224 void assign (luabridge::LuaRef* rv) const
226 double const val = ARDOUR::slider_position_to_gain_with_max (_db_adjustment.get_value (), ARDOUR::Config->get_max_gain ());
227 (*rv)[_key] = accurate_coefficient_to_dB (val);
233 double const val = ARDOUR::slider_position_to_gain_with_max (_db_adjustment.get_value (), ARDOUR::Config->get_max_gain ());
236 snprintf (buf, sizeof (buf), "-inf");
238 snprintf (buf, sizeof (buf), "%.2f", accurate_coefficient_to_dB (val));
240 _db_display.set_text (buf);
245 float db_val = atof (_db_display.get_text ().c_str ());
246 double coeff_val = dB_to_coefficient (db_val);
247 _db_adjustment.set_value (ARDOUR::gain_to_slider_position_with_max (coeff_val, ARDOUR::Config->get_max_gain ()));
250 bool on_key_press (GdkEventKey* ev)
252 if (ARDOUR_UI_UTILS::key_is_legal_for_numeric_entry (ev->keyval)) {
258 Gtk::Adjustment _db_adjustment;
259 ArdourWidgets::HSliderController* _db_slider;
260 Gtk::Entry _db_display;
262 Gtk::VBox _fader_centering_box;
265 class LuaDialogSlider : public LuaDialogWidget
268 LuaDialogSlider (std::string const& key, std::string const& title, double lower, double upper, double dflt, int digits, luabridge::LuaRef scalepoints)
269 : LuaDialogWidget (key, title)
270 , _adj (dflt, lower, upper, 1, (upper - lower) / 20, 0)
273 _hscale.set_digits (digits);
274 _hscale.set_draw_value (true);
275 _hscale.set_value_pos (Gtk::POS_TOP);
277 if (!scalepoints.isTable ()) {
281 for (luabridge::Iterator i (scalepoints); !i.isNil (); ++i) {
282 if (!i.key ().isNumber ()) { continue; }
283 if (!i.value ().isString ()) { continue; }
284 _hscale.add_mark (i.key ().cast<double> (), Gtk::POS_BOTTOM, i.value ().cast<std::string> ());
288 Gtk::Widget* widget ()
293 void assign (luabridge::LuaRef* rv) const
295 (*rv)[_key] = _adj.get_value ();
299 Gtk::Adjustment _adj;
303 class LuaDialogSpinBox : public LuaDialogWidget
306 LuaDialogSpinBox (std::string const& key, std::string const& title, double lower, double upper, double dflt, double step, int digits)
307 : LuaDialogWidget (key, title)
308 , _adj (dflt, lower, upper, step, step, 0)
311 _spin.set_digits (digits);
314 Gtk::Widget* widget ()
319 void assign (luabridge::LuaRef* rv) const
321 (*rv)[_key] = _adj.get_value ();
325 Gtk::Adjustment _adj;
326 Gtk::SpinButton _spin;
329 class LuaDialogRadio : public LuaDialogWidget
332 LuaDialogRadio (std::string const& key, std::string const& title, luabridge::LuaRef values, std::string const& dflt)
333 : LuaDialogWidget (key, title)
336 for (luabridge::Iterator i (values); !i.isNil (); ++i) {
337 if (!i.key ().isString ()) { continue; }
338 std::string key = i.key ().cast<std::string> ();
339 Gtk::RadioButton* rb = Gtk::manage (new Gtk::RadioButton (_group, key));
340 _hbox.pack_start (*rb);
341 luabridge::LuaRef* ref = new luabridge::LuaRef (i.value ());
342 _refs.push_back (ref);
343 if (!_rv) { _rv = ref; }
344 rb->signal_toggled ().connect (sigc::bind (
345 sigc::mem_fun (*this, &LuaDialogRadio::rb_toggled), rb, ref
356 for (std::vector<luabridge::LuaRef*>::const_iterator i = _refs.begin (); i != _refs.end (); ++i) {
362 Gtk::Widget* widget ()
367 void assign (luabridge::LuaRef* rv) const
372 (*rv)[_key] = luabridge::Nil ();
377 LuaDialogRadio (LuaDialogRadio const&); // prevent cc
378 void rb_toggled (Gtk::RadioButton* b, luabridge::LuaRef* rv) {
379 if (b->get_active ()) {
385 Gtk::RadioButtonGroup _group;
386 std::vector<luabridge::LuaRef*> _refs;
387 luabridge::LuaRef* _rv;
390 class LuaDialogDropDown : public LuaDialogWidget
393 LuaDialogDropDown (std::string const& key, std::string const& title, luabridge::LuaRef values, std::string const& dflt)
394 : LuaDialogWidget (key, title)
397 populate (_dd.items (), values, dflt);
400 ~LuaDialogDropDown ()
402 for (std::vector<luabridge::LuaRef*>::const_iterator i = _refs.begin (); i != _refs.end (); ++i) {
408 Gtk::Widget* widget ()
413 void assign (luabridge::LuaRef* rv) const
418 (*rv)[_key] = luabridge::Nil ();
423 void populate (Gtk::Menu_Helpers::MenuList& items, luabridge::LuaRef values, std::string const& dflt)
425 using namespace Gtk::Menu_Helpers;
426 for (luabridge::Iterator i (values); !i.isNil (); ++i) {
427 if (!i.key ().isString ()) { continue; }
428 std::string key = i.key ().cast<std::string> ();
429 if (i.value ().isTable ()) {
430 Gtk::Menu* menu = Gtk::manage (new Gtk::Menu);
431 items.push_back (MenuElem (key, *menu));
432 populate (menu->items (), i.value (), dflt);
435 luabridge::LuaRef* ref = new luabridge::LuaRef (i.value ());
436 _refs.push_back (ref);
437 items.push_back (MenuElem (i.key ().cast<std::string> (),
438 sigc::bind (sigc::mem_fun (*this, &LuaDialogDropDown::dd_select), key, ref)));
440 if (!_rv || key == dflt) {
447 void dd_select (std::string const& key, luabridge::LuaRef* rv) {
452 ArdourWidgets::ArdourDropdown _dd;
453 std::vector<luabridge::LuaRef*> _refs;
454 luabridge::LuaRef* _rv;
457 class LuaFileChooser : public LuaDialogWidget
460 LuaFileChooser (std::string const& key, std::string const& title, Gtk::FileChooserAction a, const std::string& path)
461 : LuaDialogWidget (key, title)
464 if (!path.empty ()) {
466 case Gtk::FILE_CHOOSER_ACTION_OPEN:
467 case Gtk::FILE_CHOOSER_ACTION_SAVE:
468 if (Glib::file_test (path, Glib::FILE_TEST_IS_REGULAR|Glib::FILE_TEST_EXISTS)) {
469 _fc.set_filename (path);
472 case Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER:
473 if (Glib::file_test (path, Glib::FILE_TEST_IS_DIR|Glib::FILE_TEST_EXISTS)) {
474 _fc.set_filename (path);
477 case Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER:
483 Gtk::Widget* widget ()
488 void assign (luabridge::LuaRef* rv) const
490 (*rv)[_key] = std::string (_fc.get_filename ());
494 Gtk::FileChooserButton _fc;
499 /*******************************************************************************
500 * Lua Parameter Dialog
502 Dialog::Dialog (std::string const& title, luabridge::LuaRef lr)
503 :_ad (title, true, false)
506 if (!lr.isTable ()) {
509 for (luabridge::Iterator i (lr); !i.isNil (); ++i) {
510 if (!i.key ().isNumber ()) { continue; }
511 if (!i.value ().isTable ()) { continue; }
512 if (!i.value ()["title"].isString ()) { continue; }
513 if (!i.value ()["type"].isString ()) { continue; }
515 std::string title = i.value ()["title"].cast<std::string> ();
516 std::string type = i.value ()["type"].cast<std::string> ();
518 if (type == "heading") {
519 Gtk::AlignmentEnum xalign = Gtk::ALIGN_CENTER;
520 if (i.value ()["align"].isString ()) {
521 std::string align = i.value ()["align"].cast <std::string> ();
522 if (align == "left") {
523 xalign = Gtk::ALIGN_LEFT;
524 } else if (align == "right") {
525 xalign = Gtk::ALIGN_RIGHT;
528 _widgets.push_back (new LuaDialogLabel (title, xalign));
532 if (!i.value ()["key"].isString ()) { continue; }
533 std::string key = i.value ()["key"].cast<std::string> ();
535 if (type == "checkbox") {
537 if (i.value ()["default"].isBoolean ()) {
538 dflt = i.value ()["default"].cast<bool> ();
540 _widgets.push_back (new LuaDialogCheckbox (key, title, dflt));
541 } else if (type == "entry") {
543 if (i.value ()["default"].isString ()) {
544 dflt = i.value ()["default"].cast<std::string> ();
546 _widgets.push_back (new LuaDialogEntry (key, title, dflt));
547 } else if (type == "radio") {
549 if (!i.value ()["values"].isTable ()) {
552 if (i.value ()["default"].isString ()) {
553 dflt = i.value ()["default"].cast<std::string> ();
555 _widgets.push_back (new LuaDialogRadio (key, title, i.value ()["values"], dflt));
556 } else if (type == "fader") {
558 if (i.value ()["default"].isNumber ()) {
559 dflt = i.value ()["default"].cast<double> ();
561 _widgets.push_back (new LuaDialogFader (key, title, dflt));
562 } else if (type == "slider") {
563 double lower, upper, dflt;
565 if (!i.value ()["min"].isNumber ()) { continue; }
566 if (!i.value ()["max"].isNumber ()) { continue; }
567 lower = i.value ()["min"].cast<double> ();
568 upper = i.value ()["max"].cast<double> ();
569 if (i.value ()["default"].isNumber ()) {
570 dflt = i.value ()["default"].cast<double> ();
574 if (i.value ()["digits"].isNumber ()) {
575 digits = i.value ()["digits"].cast<int> ();
577 _widgets.push_back (new LuaDialogSlider (key, title, lower, upper, dflt, digits, i.value ()["scalepoints"]));
578 } else if (type == "number") {
579 double lower, upper, dflt, step;
581 if (!i.value ()["min"].isNumber ()) { continue; }
582 if (!i.value ()["max"].isNumber ()) { continue; }
583 lower = i.value ()["min"].cast<double> ();
584 upper = i.value ()["max"].cast<double> ();
585 if (i.value ()["default"].isNumber ()) {
586 dflt = i.value ()["default"].cast<double> ();
590 if (i.value ()["step"].isNumber ()) {
591 step = i.value ()["step"].cast<double> ();
595 if (i.value ()["digits"].isNumber ()) {
596 digits = i.value ()["digits"].cast<int> ();
598 _widgets.push_back (new LuaDialogSpinBox (key, title, lower, upper, dflt, step, digits));
599 } else if (type == "dropdown") {
601 if (!i.value ()["values"].isTable ()) {
604 if (i.value ()["default"].isString ()) {
605 dflt = i.value ()["default"].cast<std::string> ();
607 _widgets.push_back (new LuaDialogDropDown (key, title, i.value ()["values"], dflt));
608 } else if (type == "file") {
610 if (i.value ()["path"].isString ()) {
611 path = i.value ()["path"].cast<std::string> ();
613 _widgets.push_back (new LuaFileChooser (key, title, Gtk::FILE_CHOOSER_ACTION_OPEN, path));
614 } else if (type == "folder") {
616 if (i.value ()["path"].isString ()) {
617 path = i.value ()["path"].cast<std::string> ();
619 _widgets.push_back (new LuaFileChooser (key, title, Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER, path));
623 _ad.add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
624 _ad.add_button (Gtk::Stock::OK, Gtk::RESPONSE_ACCEPT);
626 Gtk::Table* table = Gtk::manage (new Gtk::Table ());
627 table->set_col_spacings (4);
628 table->set_row_spacings (8);
629 _ad.get_vbox ()->pack_start (*table);
632 for (DialogWidgets::const_iterator i = _widgets.begin (); i != _widgets.end (); ++i) {
633 std::string const& label = (*i)->label ();
634 if (!label.empty ()) {
635 Gtk::Label* lbl = Gtk::manage (new Gtk::Label (label + ":", Gtk::ALIGN_END, Gtk::ALIGN_CENTER, false));
636 table->attach (*lbl, 0, 1, row, row + 1, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
637 table->attach (*((*i)->widget ()), 1, 2, row, row + 1, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
638 } else if ((*i)->key ().empty ()) {
639 table->attach (*((*i)->widget ()), 0, 2, row, row + 1, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
641 table->attach (*((*i)->widget ()), 1, 2, row, row + 1, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
649 for (DialogWidgets::const_iterator i = _widgets.begin (); i != _widgets.end () ; ++i) {
656 Dialog::run (lua_State *L)
658 _ad.get_vbox ()->show_all ();
659 switch (_ad.run ()) {
660 case Gtk::RESPONSE_ACCEPT:
667 luabridge::LuaRef rv (luabridge::newTable (L));
668 for (DialogWidgets::const_iterator i = _widgets.begin (); i != _widgets.end () ; ++i) {
671 luabridge::push (L, rv);