replace ::cast_dynamic() with relevant ActionManager::get_*_action() calls
[ardour.git] / gtk2_ardour / option_editor.h
1 /*
2     Copyright (C) 2009 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
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.
13
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., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19 #ifndef __gtk_ardour_option_editor_h__
20 #define __gtk_ardour_option_editor_h__
21
22 #include <gtkmm/checkbutton.h>
23 #include <gtkmm/comboboxtext.h>
24 #include <gtkmm/filechooserbutton.h>
25 #include <gtkmm/label.h>
26 #include <gtkmm/notebook.h>
27 #include <gtkmm/scale.h>
28 #include <gtkmm/spinbutton.h>
29 #include <gtkmm/table.h>
30 #include <gtkmm/treestore.h>
31 #include <gtkmm/treeview.h>
32 #include <gtkmm/window.h>
33
34 #include "widgets/slider_controller.h"
35
36 #include "actions.h"
37 #include "ardour_window.h"
38 #include "audio_clock.h"
39 #include "ardour/types.h"
40
41 /** @file option_editor.h
42  *  @brief Base class for option editing dialog boxes.
43  *
44  *  Code to provided the basis for dialogs which allow the user to edit options
45  *  from an ARDOUR::Configuration class.
46  *
47  *  The idea is that we have an OptionEditor class which is the dialog box.
48  *  This is essentially a GTK Notebook.  OptionEditorComponent objects can
49  *  then be added to the OptionEditor, and these components are arranged on
50  *  the pages of the Notebook.  There is also an OptionEditorComponent hierarchy
51  *  here, providing things like boolean and combobox option components.
52  *
53  *  It is intended that OptionEditor be subclassed to implement a particular
54  *  options dialog.
55  */
56
57 namespace PBD {
58         class Configuration;
59 }
60
61 class OptionEditorPage;
62
63 /** Base class for components of an OptionEditor dialog */
64 class OptionEditorComponent
65 {
66 public:
67         virtual ~OptionEditorComponent() {}
68
69         /** Called when a configuration parameter's value has changed.
70          *  @param p parameter name
71          */
72         virtual void parameter_changed (std::string const & p) = 0;
73
74         /** Called to instruct the object to set its UI state from the configuration */
75         virtual void set_state_from_config () = 0;
76
77         /** Called to instruct the object to add itself to an OptionEditorPage */
78         virtual void add_to_page (OptionEditorPage *) = 0;
79
80         void add_widget_to_page (OptionEditorPage*, Gtk::Widget*);
81         void add_widgets_to_page (OptionEditorPage*, Gtk::Widget*, Gtk::Widget*, bool expand = true);
82
83         void set_note (std::string const &);
84
85         virtual Gtk::Widget& tip_widget() = 0;
86
87 protected:
88         void maybe_add_note (OptionEditorPage *, int);
89
90         std::string _note;
91 };
92
93 /** A component which provides a subheading within the dialog */
94 class OptionEditorHeading : public OptionEditorComponent
95 {
96 public:
97         OptionEditorHeading (std::string const &);
98
99         void parameter_changed (std::string const &) {}
100         void set_state_from_config () {}
101         void add_to_page (OptionEditorPage *);
102
103         Gtk::Widget& tip_widget() { return *_label; }
104
105 private:
106         Gtk::Label* _label; ///< the label used for the heading
107 };
108
109 /** Expanding layout helper to push elements to the left on a single column page  */
110 class OptionEditorBlank : public OptionEditorComponent
111 {
112 public:
113         OptionEditorBlank () {}
114
115         void parameter_changed (std::string const &) {}
116         void set_state_from_config () {}
117         void add_to_page (OptionEditorPage *);
118
119         Gtk::Widget& tip_widget() { return _dummy; }
120
121 private:
122         Gtk::EventBox _dummy;
123 };
124
125 class RcConfigDisplay : public OptionEditorComponent
126 {
127 public:
128         RcConfigDisplay (std::string const &, std::string const &, sigc::slot<std::string>, char s = '\0');
129         void add_to_page (OptionEditorPage *);
130         void parameter_changed (std::string const & p);
131         void set_state_from_config ();
132         Gtk::Widget& tip_widget() { return *_info; }
133 protected:
134         sigc::slot<std::string> _get;
135         Gtk::Label* _label;
136         Gtk::Label* _info;
137         std::string _id;
138         char _sep;
139 };
140
141 class RcActionButton : public OptionEditorComponent
142 {
143 public:
144         RcActionButton (std::string const & t, const Glib::SignalProxy0< void >::SlotType & slot, std::string const & l = "");
145         void add_to_page (OptionEditorPage *);
146
147         void parameter_changed (std::string const & p) {}
148         void set_state_from_config () {}
149         Gtk::Widget& tip_widget() { return *_button; }
150
151 protected:
152         Gtk::Button* _button;
153         Gtk::Label* _label;
154         std::string _name;
155 };
156
157 /** Base class for components which provide UI to change an option */
158 class Option : public OptionEditorComponent
159 {
160 public:
161         /** Construct an Option.
162          *  @param i Option id (e.g. "plugins-stop-with-transport")
163          *  @param n User-visible name (e.g. "Stop plugins when the transport is stopped")
164          */
165         Option (std::string const & i,
166                 std::string const & n
167                 )
168                 : _id (i),
169                   _name (n)
170         {}
171
172         void parameter_changed (std::string const & p)
173         {
174                 if (p == _id) {
175                         set_state_from_config ();
176                 }
177         }
178
179         virtual void set_state_from_config () = 0;
180         virtual void add_to_page (OptionEditorPage*) = 0;
181
182         std::string id () const {
183                 return _id;
184         }
185
186 protected:
187         std::string _id;
188         std::string _name;
189 };
190
191 /** Just a Gtk Checkbutton, masquerading as an option component */
192 class CheckOption : public OptionEditorComponent , public Gtkmm2ext::Activatable, public sigc::trackable
193 {
194 public:
195         CheckOption (std::string const &, std::string const &, Glib::RefPtr<Gtk::Action> act );
196         void set_state_from_config () {}
197         void parameter_changed (std::string const &) {}
198         void add_to_page (OptionEditorPage*);
199
200         void set_sensitive (bool yn) {
201                 _button->set_sensitive (yn);
202         }
203
204         Gtk::Widget& tip_widget() { return *_button; }
205
206 protected:
207         void action_toggled ();
208         void action_sensitivity_changed () {}
209         void action_visibility_changed () {}
210
211         virtual void toggled ();
212
213         Gtk::CheckButton*      _button; ///< UI button
214         Gtk::Label*            _label; ///< label for button, so we can use markup
215 };
216
217 /** Component which provides the UI to handle a boolean option using a GTK CheckButton */
218 class BoolOption : public Option
219 {
220 public:
221         BoolOption (std::string const &, std::string const &, sigc::slot<bool>, sigc::slot<bool, bool>);
222         void set_state_from_config ();
223         void add_to_page (OptionEditorPage*);
224
225         void set_sensitive (bool yn) {
226                 _button->set_sensitive (yn);
227         }
228
229         Gtk::Widget& tip_widget() { return *_button; }
230
231 protected:
232         virtual void toggled ();
233
234         sigc::slot<bool>       _get; ///< slot to get the configuration variable's value
235         sigc::slot<bool, bool> _set;  ///< slot to set the configuration variable's value
236         Gtk::CheckButton*      _button; ///< UI button
237         Gtk::Label*            _label; ///< label for button, so we can use markup
238 };
239
240 class RouteDisplayBoolOption : public BoolOption
241 {
242 public:
243         RouteDisplayBoolOption (std::string const &, std::string const &, sigc::slot<bool>, sigc::slot<bool, bool>);
244
245 protected:
246         virtual void toggled ();
247 };
248
249 /** Component which allows to add any GTK Widget - intended for single buttons and custom stateless objects */
250 class FooOption : public OptionEditorComponent
251 {
252 public:
253         FooOption (Gtk::Widget *w) : _w (w) {}
254
255         void add_to_page (OptionEditorPage* p) {
256                 add_widget_to_page (p, _w);
257         }
258
259         Gtk::Widget& tip_widget() { return *_w; }
260         void set_state_from_config () {}
261         void parameter_changed (std::string const &) {}
262 private:
263         Gtk::Widget *_w;
264 };
265
266 /** Component which provides the UI to handle a string option using a GTK Entry */
267 class EntryOption : public Option
268 {
269 public:
270         EntryOption (std::string const &, std::string const &, sigc::slot<std::string>, sigc::slot<bool, std::string>);
271         void set_state_from_config ();
272         void add_to_page (OptionEditorPage*);
273         void set_sensitive (bool);
274         void set_invalid_chars (std::string i) { _invalid = i; }
275
276         Gtk::Widget& tip_widget() { return *_entry; }
277
278 private:
279         void activated ();
280         bool focus_out (GdkEventFocus*);
281         void filter_text (const Glib::ustring&, int*);
282
283         sigc::slot<std::string> _get; ///< slot to get the configuration variable's value
284         sigc::slot<bool, std::string> _set;  ///< slot to set the configuration variable's value
285         Gtk::Label* _label; ///< UI label
286         Gtk::Entry* _entry; ///< UI entry
287         std::string _invalid;
288 };
289
290
291 /** Component which provides the UI to handle an enumerated option using a GTK ComboBox.
292  *  The template parameter is the enumeration.
293  */
294 template <class T>
295 class ComboOption : public Option
296 {
297 public:
298         /** Construct an ComboOption.
299          *  @param i id
300          *  @param n User-visible name.
301          *  @param g Slot to get the variable's value.
302          *  @param s Slot to set the variable's value.
303          */
304         ComboOption (
305                 std::string const & i,
306                 std::string const & n,
307                 sigc::slot<T> g,
308                 sigc::slot<bool, T> s
309                 )
310                 : Option (i, n)
311                 , _get (g)
312                 , _set (s)
313         {
314                 _label = Gtk::manage (new Gtk::Label (n + ":"));
315                 _label->set_alignment (0, 0.5);
316                 _combo = Gtk::manage (new Gtk::ComboBoxText);
317                 _combo->signal_changed().connect (sigc::mem_fun (*this, &ComboOption::changed));
318         }
319
320         void set_state_from_config ()
321         {
322                 uint32_t r = 0;
323                 while (r < _options.size() && _get () != _options[r]) {
324                         ++r;
325                 }
326
327                 if (r < _options.size()) {
328                         _combo->set_active (r);
329                 }
330         }
331
332         void add_to_page (OptionEditorPage* p)
333         {
334                 add_widgets_to_page (p, _label, _combo);
335         }
336
337         /** Add an allowed value for this option.
338          *  @param e Enumeration.
339          *  @param o User-visible name for this value.
340          */
341         void add (T e, std::string const & o)
342         {
343                 _options.push_back (e);
344                 _combo->append_text (o);
345         }
346
347         void clear ()
348         {
349                 _combo->clear_items();
350                 _options.clear ();
351         }
352
353         void changed ()
354         {
355                 uint32_t const r = _combo->get_active_row_number ();
356                 if (r < _options.size()) {
357                         _set (_options[r]);
358                 }
359         }
360         void set_sensitive (bool yn)
361         {
362                 _combo->set_sensitive (yn);
363         }
364
365         Gtk::Widget& tip_widget() { return *_combo; }
366
367 private:
368         sigc::slot<T> _get;
369         sigc::slot<bool, T> _set;
370         Gtk::Label* _label;
371         Gtk::ComboBoxText* _combo;
372         std::vector<T> _options;
373 };
374
375
376 /** Component which provides the UI for a GTK HScale.
377  */
378 class HSliderOption : public Option
379 {
380 public:
381         HSliderOption (
382                         std::string const& i,
383                         std::string const& n,
384                         sigc::slot<float> g,
385                         sigc::slot<bool, float> s,
386                         double lower, double upper,
387                         double step_increment = 1,
388                         double page_increment = 10,
389                         double mult = 1.0,
390                         bool logarithmic = false
391                 );
392
393         void set_state_from_config ();
394         virtual void changed ();
395         void add_to_page (OptionEditorPage* p);
396         void set_sensitive (bool yn);
397
398         Gtk::Widget& tip_widget() { return _hscale; }
399         Gtk::HScale& scale() { return _hscale; }
400
401 protected:
402         sigc::slot<float> _get;
403         sigc::slot<bool, float> _set;
404         Gtk::Adjustment _adj;
405         Gtk::HScale _hscale;
406         Gtk::Label _label;
407         double _mult;
408         bool _log;
409 };
410
411
412 /** Component which provides the UI to handle an enumerated option using a GTK ComboBox.
413  *  The template parameter is the enumeration.
414  */
415 class ComboStringOption : public Option
416 {
417 public:
418         /** Construct an ComboOption.
419          *  @param i id
420          *  @param n User-visible name.
421          *  @param g Slot to get the variable's value.
422          *  @param s Slot to set the variable's value.
423          */
424         ComboStringOption (
425                 std::string const & i,
426                 std::string const & n,
427                 sigc::slot<std::string> g,
428                 sigc::slot<bool, std::string> s
429                 );
430
431         void set_state_from_config ();
432         void add_to_page (OptionEditorPage* p);
433
434         /** Set the allowed strings for this option
435          *  @param strings a vector of allowed strings
436          */
437         void set_popdown_strings (const std::vector<std::string>& strings);
438
439         void clear ();
440         void changed ();
441         void set_sensitive (bool yn);
442
443         Gtk::Widget& tip_widget() { return *_combo; }
444
445 private:
446         sigc::slot<std::string> _get;
447         sigc::slot<bool, std::string> _set;
448         Gtk::Label* _label;
449         Gtk::ComboBoxText* _combo;
450 };
451
452
453 /** Component which provides the UI to handle a boolean option which needs
454  *  to be represented as a ComboBox to be clear to the user.
455  */
456 class BoolComboOption : public Option
457 {
458 public:
459         BoolComboOption (
460                 std::string const &,
461                 std::string const &,
462                 std::string const &,
463                 std::string const &,
464                 sigc::slot<bool>,
465                 sigc::slot<bool, bool>
466                 );
467
468         void set_state_from_config ();
469         void add_to_page (OptionEditorPage *);
470         void changed ();
471         void set_sensitive (bool);
472
473         Gtk::Widget& tip_widget() { return *_combo; }
474
475 private:
476         sigc::slot<bool> _get;
477         sigc::slot<bool, bool> _set;
478         Gtk::Label* _label;
479         Gtk::ComboBoxText* _combo;
480 };
481
482
483 /** Component which provides the UI to handle an numeric option using a GTK SpinButton */
484 template <class T>
485 class SpinOption : public Option
486 {
487 public:
488         /** Construct an SpinOption.
489          *  @param i id
490          *  @param n User-visible name.
491          *  @param g Slot to get the variable's value.
492          *  @param s Slot to set the variable's value.
493          *  @param min Variable minimum value.
494          *  @param max Variable maximum value.
495          *  @param step Step for the spin button.
496          *  @param page Page step for the spin button.
497          *  @param unit Unit name.
498          *  @param scale Scaling factor (such that for a value x in the spinbutton, x * scale is written to the config)
499          *  @param digits Number of decimal digits to show.
500          */
501         SpinOption (
502                 std::string const & i,
503                 std::string const & n,
504                 sigc::slot<T> g,
505                 sigc::slot<bool, T> s,
506                 T min,
507                 T max,
508                 T step,
509                 T page,
510                 std::string const & unit = "",
511                 float scale = 1,
512                 unsigned digits = 0
513                 )
514                 : Option (i, n)
515                 , _get (g)
516                 , _set (s)
517                 , _scale (scale)
518         {
519                 _label = Gtk::manage (new Gtk::Label (n + ":"));
520                 _label->set_alignment (0, 0.5);
521
522                 _spin = Gtk::manage (new Gtk::SpinButton);
523                 _spin->set_range (min, max);
524                 _spin->set_increments (step, page);
525                 _spin->set_digits(digits);
526
527                 _box = Gtk::manage (new Gtk::HBox);
528                 _box->pack_start (*_spin, true, true);
529                 _box->set_spacing (4);
530                 if (unit.length()) {
531                         _box->pack_start (*Gtk::manage (new Gtk::Label (unit)), false, false);
532                 }
533
534                 _spin->signal_value_changed().connect (sigc::mem_fun (*this, &SpinOption::changed));
535         }
536
537         void set_state_from_config ()
538         {
539                 _spin->set_value (_get () / _scale);
540         }
541
542         void add_to_page (OptionEditorPage* p)
543         {
544                 add_widgets_to_page (p, _label, _box, false);
545         }
546
547         void changed ()
548         {
549                 _set (static_cast<T> (_spin->get_value ()) * _scale);
550         }
551
552         Gtk::Widget& tip_widget() { return *_spin; }
553
554 private:
555         sigc::slot<T> _get;
556         sigc::slot<bool, T> _set;
557         float _scale;
558         Gtk::Label* _label;
559         Gtk::HBox* _box;
560         Gtk::SpinButton* _spin;
561 };
562
563 class FaderOption : public Option
564 {
565 public:
566
567         FaderOption (std::string const &, std::string const &, sigc::slot<ARDOUR::gain_t> g, sigc::slot<bool, ARDOUR::gain_t> s);
568         void set_state_from_config ();
569         void add_to_page (OptionEditorPage *);
570
571         Gtk::Widget& tip_widget() { return *_db_slider; }
572
573 private:
574         void db_changed ();
575         void on_activate ();
576         bool on_key_press (GdkEventKey* ev);
577
578         Gtk::Adjustment _db_adjustment;
579         ArdourWidgets::HSliderController* _db_slider;
580         Gtk::Entry _db_display;
581         Gtk::Label _label;
582         Gtk::HBox _box;
583         Gtk::VBox _fader_centering_box;
584         sigc::slot<ARDOUR::gain_t> _get;
585         sigc::slot<bool, ARDOUR::gain_t> _set;
586 };
587
588 class WidgetOption : public Option
589 {
590   public:
591         WidgetOption (std::string const & i, std::string const & n, Gtk::Widget& w);
592
593         void add_to_page (OptionEditorPage*);
594         void parameter_changed (std::string const &) {}
595         void set_state_from_config () {}
596
597         Gtk::Widget& tip_widget() { return *_widget; }
598
599   private:
600         Gtk::Widget* _widget;
601 };
602
603 class ClockOption : public Option
604 {
605 public:
606         ClockOption (std::string const &, std::string const &, sigc::slot<std::string>, sigc::slot<bool, std::string>);
607         void set_state_from_config ();
608         void add_to_page (OptionEditorPage *);
609         void set_session (ARDOUR::Session *);
610
611         Gtk::Widget& tip_widget() { return _clock; }
612         AudioClock& clock() { return _clock; }
613
614 private:
615         void save_clock_time ();
616         Gtk::Label _label;
617         AudioClock _clock;
618         sigc::slot<std::string> _get;
619         sigc::slot<bool, std::string> _set;
620         ARDOUR::Session *_session;
621 };
622
623 class DirectoryOption : public Option
624 {
625 public:
626         DirectoryOption (std::string const &, std::string const &, sigc::slot<std::string>, sigc::slot<bool, std::string>);
627
628         void set_state_from_config ();
629         void add_to_page (OptionEditorPage *);
630
631         Gtk::Widget& tip_widget() { return _file_chooser; }
632
633 private:
634         void selection_changed ();
635
636         sigc::slot<std::string> _get; ///< slot to get the configuration variable's value
637         sigc::slot<bool, std::string> _set;  ///< slot to set the configuration variable's value
638         Gtk::FileChooserButton _file_chooser;
639 };
640
641 /** Class to represent a single page in an OptionEditor's notebook.
642  *  Pages are laid out using a 3-column table; the 1st column is used
643  *  to indent non-headings, and the 2nd and 3rd for actual content.
644  */
645 class OptionEditorPage
646 {
647 public:
648         OptionEditorPage (Gtk::Notebook&, std::string const &);
649         OptionEditorPage ();
650
651         Gtk::VBox box;
652         Gtk::Table table;
653         std::list<OptionEditorComponent*> components;
654
655 private:
656         void init ();
657 };
658
659 class OptionEditorMiniPage : public OptionEditorComponent, public OptionEditorPage
660 {
661 public:
662         OptionEditorMiniPage ()
663         {
664                 box.pack_start (table, false, false);
665                 box.set_border_width (0);
666         }
667
668         void parameter_changed (std::string const &) = 0;
669         void set_state_from_config () = 0;
670         virtual void add_to_page (OptionEditorPage*);
671
672         Gtk::Widget& tip_widget() { return *table.children().front().get_widget(); }
673 };
674
675 /** The OptionEditor dialog base class */
676 class OptionEditor : virtual public sigc::trackable
677 {
678 public:
679         OptionEditor (PBD::Configuration *);
680         virtual ~OptionEditor ();
681
682         void add_option (std::string const &, OptionEditorComponent *);
683         void add_page (std::string const &, Gtk::Widget& page_widget);
684
685         void set_current_page (std::string const &);
686
687 protected:
688         virtual void parameter_changed (std::string const &);
689
690         PBD::Configuration* _config;
691         Gtk::Notebook& notebook() { return _notebook; }
692         Gtk::TreeView& treeview() { return option_treeview; }
693
694         class OptionColumns : public Gtk::TreeModel::ColumnRecord
695         {
696                 public:
697                         Gtk::TreeModelColumn<std::string> name;
698                         Gtk::TreeModelColumn<Gtk::Widget*> widget;
699
700                         OptionColumns() {
701                                 add (name);
702                                 add (widget);
703                         }
704         };
705
706         OptionColumns option_columns;
707         Glib::RefPtr<Gtk::TreeStore> option_tree;
708
709 private:
710         PBD::ScopedConnection config_connection;
711         Gtk::Notebook _notebook;
712         Gtk::TreeView option_treeview;
713         std::map<std::string, OptionEditorPage*> _pages;
714
715         void add_path_to_treeview (std::string const &, Gtk::Widget&);
716         Gtk::TreeModel::iterator find_path_in_treemodel (std::string const & pn,
717                                                          bool create_missing = false);
718         void treeview_row_selected ();
719 };
720
721 /** The OptionEditor dialog-as-container base class */
722 class OptionEditorContainer : public OptionEditor, public Gtk::VBox
723 {
724 public:
725         OptionEditorContainer (PBD::Configuration *, std::string const &);
726         ~OptionEditorContainer() {}
727 private:
728         Gtk::HBox hpacker;
729 };
730
731 /** The OptionEditor dialog-as-container base class */
732 class OptionEditorWindow : public OptionEditor, public ArdourWindow
733 {
734 public:
735         OptionEditorWindow (PBD::Configuration *, std::string const &);
736         ~OptionEditorWindow() {}
737 private:
738         Gtk::VBox container;
739         Gtk::HBox hpacker;
740 };
741
742 #endif /* __gtk_ardour_option_editor_h__ */