Tweaks to Mixer and Monitor keybindings:
[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
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         void action_toggled ();
207         void action_sensitivity_changed () {}
208         void action_visibility_changed () {}
209
210 protected:
211         virtual void toggled ();
212
213         sigc::slot<bool>       _get; ///< slot to get the configuration variable's value
214         sigc::slot<bool, bool> _set;  ///< slot to set the configuration variable's value
215         Gtk::CheckButton*      _button; ///< UI button
216         Gtk::Label*            _label; ///< label for button, so we can use markup
217 };
218
219 /** Component which provides the UI to handle a boolean option using a GTK CheckButton */
220 class BoolOption : public Option
221 {
222 public:
223         BoolOption (std::string const &, std::string const &, sigc::slot<bool>, sigc::slot<bool, bool>);
224         void set_state_from_config ();
225         void add_to_page (OptionEditorPage*);
226
227         void set_sensitive (bool yn) {
228                 _button->set_sensitive (yn);
229         }
230
231         Gtk::Widget& tip_widget() { return *_button; }
232
233 protected:
234         virtual void toggled ();
235
236         sigc::slot<bool>       _get; ///< slot to get the configuration variable's value
237         sigc::slot<bool, bool> _set;  ///< slot to set the configuration variable's value
238         Gtk::CheckButton*      _button; ///< UI button
239         Gtk::Label*            _label; ///< label for button, so we can use markup
240 };
241
242 class RouteDisplayBoolOption : public BoolOption
243 {
244 public:
245         RouteDisplayBoolOption (std::string const &, std::string const &, sigc::slot<bool>, sigc::slot<bool, bool>);
246
247 protected:
248         virtual void toggled ();
249 };
250
251 /** Component which allows to add any GTK Widget - intended for single buttons and custom stateless objects */
252 class FooOption : public OptionEditorComponent
253 {
254 public:
255         FooOption (Gtk::Widget *w) : _w (w) {}
256
257         void add_to_page (OptionEditorPage* p) {
258                 add_widget_to_page (p, _w);
259         }
260
261         Gtk::Widget& tip_widget() { return *_w; }
262         void set_state_from_config () {}
263         void parameter_changed (std::string const &) {}
264 private:
265         Gtk::Widget *_w;
266 };
267
268 /** Component which provides the UI to handle a string option using a GTK Entry */
269 class EntryOption : public Option
270 {
271 public:
272         EntryOption (std::string const &, std::string const &, sigc::slot<std::string>, sigc::slot<bool, std::string>);
273         void set_state_from_config ();
274         void add_to_page (OptionEditorPage*);
275         void set_sensitive (bool);
276         void set_invalid_chars (std::string i) { _invalid = i; }
277
278         Gtk::Widget& tip_widget() { return *_entry; }
279
280 private:
281         void activated ();
282         bool focus_out (GdkEventFocus*);
283         void filter_text (const Glib::ustring&, int*);
284
285         sigc::slot<std::string> _get; ///< slot to get the configuration variable's value
286         sigc::slot<bool, std::string> _set;  ///< slot to set the configuration variable's value
287         Gtk::Label* _label; ///< UI label
288         Gtk::Entry* _entry; ///< UI entry
289         std::string _invalid;
290 };
291
292
293 /** Component which provides the UI to handle an enumerated option using a GTK ComboBox.
294  *  The template parameter is the enumeration.
295  */
296 template <class T>
297 class ComboOption : public Option
298 {
299 public:
300         /** Construct an ComboOption.
301          *  @param i id
302          *  @param n User-visible name.
303          *  @param g Slot to get the variable's value.
304          *  @param s Slot to set the variable's value.
305          */
306         ComboOption (
307                 std::string const & i,
308                 std::string const & n,
309                 sigc::slot<T> g,
310                 sigc::slot<bool, T> s
311                 )
312                 : Option (i, n)
313                 , _get (g)
314                 , _set (s)
315         {
316                 _label = Gtk::manage (new Gtk::Label (n + ":"));
317                 _label->set_alignment (0, 0.5);
318                 _combo = Gtk::manage (new Gtk::ComboBoxText);
319                 _combo->signal_changed().connect (sigc::mem_fun (*this, &ComboOption::changed));
320         }
321
322         void set_state_from_config ()
323         {
324                 uint32_t r = 0;
325                 while (r < _options.size() && _get () != _options[r]) {
326                         ++r;
327                 }
328
329                 if (r < _options.size()) {
330                         _combo->set_active (r);
331                 }
332         }
333
334         void add_to_page (OptionEditorPage* p)
335         {
336                 add_widgets_to_page (p, _label, _combo);
337         }
338
339         /** Add an allowed value for this option.
340          *  @param e Enumeration.
341          *  @param o User-visible name for this value.
342          */
343         void add (T e, std::string const & o)
344         {
345                 _options.push_back (e);
346                 _combo->append_text (o);
347         }
348
349         void clear ()
350         {
351                 _combo->clear_items();
352                 _options.clear ();
353         }
354
355         void changed ()
356         {
357                 uint32_t const r = _combo->get_active_row_number ();
358                 if (r < _options.size()) {
359                         _set (_options[r]);
360                 }
361         }
362         void set_sensitive (bool yn)
363         {
364                 _combo->set_sensitive (yn);
365         }
366
367         Gtk::Widget& tip_widget() { return *_combo; }
368
369 private:
370         sigc::slot<T> _get;
371         sigc::slot<bool, T> _set;
372         Gtk::Label* _label;
373         Gtk::ComboBoxText* _combo;
374         std::vector<T> _options;
375 };
376
377
378 /** Component which provides the UI for a GTK HScale.
379  */
380 class HSliderOption : public Option
381 {
382 public:
383         HSliderOption (
384                         std::string const& i,
385                         std::string const& n,
386                         sigc::slot<float> g,
387                         sigc::slot<bool, float> s,
388                         double lower, double upper,
389                         double step_increment = 1,
390                         double page_increment = 10,
391                         double mult = 1.0,
392                         bool logarithmic = false
393                 );
394
395         void set_state_from_config ();
396         virtual void changed ();
397         void add_to_page (OptionEditorPage* p);
398         void set_sensitive (bool yn);
399
400         Gtk::Widget& tip_widget() { return _hscale; }
401         Gtk::HScale& scale() { return _hscale; }
402
403 protected:
404         sigc::slot<float> _get;
405         sigc::slot<bool, float> _set;
406         Gtk::Adjustment _adj;
407         Gtk::HScale _hscale;
408         Gtk::Label _label;
409         double _mult;
410         bool _log;
411 };
412
413
414 /** Component which provides the UI to handle an enumerated option using a GTK ComboBox.
415  *  The template parameter is the enumeration.
416  */
417 class ComboStringOption : public Option
418 {
419 public:
420         /** Construct an ComboOption.
421          *  @param i id
422          *  @param n User-visible name.
423          *  @param g Slot to get the variable's value.
424          *  @param s Slot to set the variable's value.
425          */
426         ComboStringOption (
427                 std::string const & i,
428                 std::string const & n,
429                 sigc::slot<std::string> g,
430                 sigc::slot<bool, std::string> s
431                 );
432
433         void set_state_from_config ();
434         void add_to_page (OptionEditorPage* p);
435
436         /** Set the allowed strings for this option
437          *  @param strings a vector of allowed strings
438          */
439         void set_popdown_strings (const std::vector<std::string>& strings);
440
441         void clear ();
442         void changed ();
443         void set_sensitive (bool yn);
444
445         Gtk::Widget& tip_widget() { return *_combo; }
446
447 private:
448         sigc::slot<std::string> _get;
449         sigc::slot<bool, std::string> _set;
450         Gtk::Label* _label;
451         Gtk::ComboBoxText* _combo;
452 };
453
454
455 /** Component which provides the UI to handle a boolean option which needs
456  *  to be represented as a ComboBox to be clear to the user.
457  */
458 class BoolComboOption : public Option
459 {
460 public:
461         BoolComboOption (
462                 std::string const &,
463                 std::string const &,
464                 std::string const &,
465                 std::string const &,
466                 sigc::slot<bool>,
467                 sigc::slot<bool, bool>
468                 );
469
470         void set_state_from_config ();
471         void add_to_page (OptionEditorPage *);
472         void changed ();
473         void set_sensitive (bool);
474
475         Gtk::Widget& tip_widget() { return *_combo; }
476
477 private:
478         sigc::slot<bool> _get;
479         sigc::slot<bool, bool> _set;
480         Gtk::Label* _label;
481         Gtk::ComboBoxText* _combo;
482 };
483
484
485 /** Component which provides the UI to handle an numeric option using a GTK SpinButton */
486 template <class T>
487 class SpinOption : public Option
488 {
489 public:
490         /** Construct an SpinOption.
491          *  @param i id
492          *  @param n User-visible name.
493          *  @param g Slot to get the variable's value.
494          *  @param s Slot to set the variable's value.
495          *  @param min Variable minimum value.
496          *  @param max Variable maximum value.
497          *  @param step Step for the spin button.
498          *  @param page Page step for the spin button.
499          *  @param unit Unit name.
500          *  @param scale Scaling factor (such that for a value x in the spinbutton, x * scale is written to the config)
501          *  @param digits Number of decimal digits to show.
502          */
503         SpinOption (
504                 std::string const & i,
505                 std::string const & n,
506                 sigc::slot<T> g,
507                 sigc::slot<bool, T> s,
508                 T min,
509                 T max,
510                 T step,
511                 T page,
512                 std::string const & unit = "",
513                 float scale = 1,
514                 unsigned digits = 0
515                 )
516                 : Option (i, n)
517                 , _get (g)
518                 , _set (s)
519                 , _scale (scale)
520         {
521                 _label = Gtk::manage (new Gtk::Label (n + ":"));
522                 _label->set_alignment (0, 0.5);
523
524                 _spin = Gtk::manage (new Gtk::SpinButton);
525                 _spin->set_range (min, max);
526                 _spin->set_increments (step, page);
527                 _spin->set_digits(digits);
528
529                 _box = Gtk::manage (new Gtk::HBox);
530                 _box->pack_start (*_spin, true, true);
531                 _box->set_spacing (4);
532                 if (unit.length()) {
533                         _box->pack_start (*Gtk::manage (new Gtk::Label (unit)), false, false);
534                 }
535
536                 _spin->signal_value_changed().connect (sigc::mem_fun (*this, &SpinOption::changed));
537         }
538
539         void set_state_from_config ()
540         {
541                 _spin->set_value (_get () / _scale);
542         }
543
544         void add_to_page (OptionEditorPage* p)
545         {
546                 add_widgets_to_page (p, _label, _box, false);
547         }
548
549         void changed ()
550         {
551                 _set (static_cast<T> (_spin->get_value ()) * _scale);
552         }
553
554         Gtk::Widget& tip_widget() { return *_spin; }
555
556 private:
557         sigc::slot<T> _get;
558         sigc::slot<bool, T> _set;
559         float _scale;
560         Gtk::Label* _label;
561         Gtk::HBox* _box;
562         Gtk::SpinButton* _spin;
563 };
564
565 class FaderOption : public Option
566 {
567 public:
568
569         FaderOption (std::string const &, std::string const &, sigc::slot<ARDOUR::gain_t> g, sigc::slot<bool, ARDOUR::gain_t> s);
570         void set_state_from_config ();
571         void add_to_page (OptionEditorPage *);
572
573         Gtk::Widget& tip_widget() { return *_db_slider; }
574
575 private:
576         void db_changed ();
577         void on_activate ();
578         bool on_key_press (GdkEventKey* ev);
579
580         Gtk::Adjustment _db_adjustment;
581         ArdourWidgets::HSliderController* _db_slider;
582         Gtk::Entry _db_display;
583         Gtk::Label _label;
584         Gtk::HBox _box;
585         Gtk::VBox _fader_centering_box;
586         sigc::slot<ARDOUR::gain_t> _get;
587         sigc::slot<bool, ARDOUR::gain_t> _set;
588 };
589
590 class WidgetOption : public Option
591 {
592   public:
593         WidgetOption (std::string const & i, std::string const & n, Gtk::Widget& w);
594
595         void add_to_page (OptionEditorPage*);
596         void parameter_changed (std::string const &) {}
597         void set_state_from_config () {}
598
599         Gtk::Widget& tip_widget() { return *_widget; }
600
601   private:
602         Gtk::Widget* _widget;
603 };
604
605 class ClockOption : public Option
606 {
607 public:
608         ClockOption (std::string const &, std::string const &, sigc::slot<std::string>, sigc::slot<bool, std::string>);
609         void set_state_from_config ();
610         void add_to_page (OptionEditorPage *);
611         void set_session (ARDOUR::Session *);
612
613         Gtk::Widget& tip_widget() { return _clock; }
614         AudioClock& clock() { return _clock; }
615
616 private:
617         void save_clock_time ();
618         Gtk::Label _label;
619         AudioClock _clock;
620         sigc::slot<std::string> _get;
621         sigc::slot<bool, std::string> _set;
622         ARDOUR::Session *_session;
623 };
624
625 class DirectoryOption : public Option
626 {
627 public:
628         DirectoryOption (std::string const &, std::string const &, sigc::slot<std::string>, sigc::slot<bool, std::string>);
629
630         void set_state_from_config ();
631         void add_to_page (OptionEditorPage *);
632
633         Gtk::Widget& tip_widget() { return _file_chooser; }
634
635 private:
636         void selection_changed ();
637
638         sigc::slot<std::string> _get; ///< slot to get the configuration variable's value
639         sigc::slot<bool, std::string> _set;  ///< slot to set the configuration variable's value
640         Gtk::FileChooserButton _file_chooser;
641 };
642
643 /** Class to represent a single page in an OptionEditor's notebook.
644  *  Pages are laid out using a 3-column table; the 1st column is used
645  *  to indent non-headings, and the 2nd and 3rd for actual content.
646  */
647 class OptionEditorPage
648 {
649 public:
650         OptionEditorPage (Gtk::Notebook&, std::string const &);
651         OptionEditorPage ();
652
653         Gtk::VBox box;
654         Gtk::Table table;
655         std::list<OptionEditorComponent*> components;
656
657 private:
658         void init ();
659 };
660
661 class OptionEditorMiniPage : public OptionEditorComponent, public OptionEditorPage
662 {
663 public:
664         OptionEditorMiniPage ()
665         {
666                 box.pack_start (table, false, false);
667                 box.set_border_width (0);
668         }
669
670         void parameter_changed (std::string const &) = 0;
671         void set_state_from_config () = 0;
672         virtual void add_to_page (OptionEditorPage*);
673
674         Gtk::Widget& tip_widget() { return *table.children().front().get_widget(); }
675 };
676
677 /** The OptionEditor dialog base class */
678 class OptionEditor : virtual public sigc::trackable
679 {
680 public:
681         OptionEditor (PBD::Configuration *);
682         virtual ~OptionEditor ();
683
684         void add_option (std::string const &, OptionEditorComponent *);
685         void add_page (std::string const &, Gtk::Widget& page_widget);
686
687         void set_current_page (std::string const &);
688
689 protected:
690         virtual void parameter_changed (std::string const &);
691
692         PBD::Configuration* _config;
693         Gtk::Notebook& notebook() { return _notebook; }
694         Gtk::TreeView& treeview() { return option_treeview; }
695
696         class OptionColumns : public Gtk::TreeModel::ColumnRecord
697         {
698                 public:
699                         Gtk::TreeModelColumn<std::string> name;
700                         Gtk::TreeModelColumn<Gtk::Widget*> widget;
701
702                         OptionColumns() {
703                                 add (name);
704                                 add (widget);
705                         }
706         };
707
708         OptionColumns option_columns;
709         Glib::RefPtr<Gtk::TreeStore> option_tree;
710
711 private:
712         PBD::ScopedConnection config_connection;
713         Gtk::Notebook _notebook;
714         Gtk::TreeView option_treeview;
715         std::map<std::string, OptionEditorPage*> _pages;
716
717         void add_path_to_treeview (std::string const &, Gtk::Widget&);
718         Gtk::TreeModel::iterator find_path_in_treemodel (std::string const & pn,
719                                                          bool create_missing = false);
720         void treeview_row_selected ();
721 };
722
723 /** The OptionEditor dialog-as-container base class */
724 class OptionEditorContainer : public OptionEditor, public Gtk::VBox
725 {
726 public:
727         OptionEditorContainer (PBD::Configuration *, std::string const &);
728         ~OptionEditorContainer() {}
729 private:
730         Gtk::HBox hpacker;
731 };
732
733 /** The OptionEditor dialog-as-container base class */
734 class OptionEditorWindow : public OptionEditor, public ArdourWindow
735 {
736 public:
737         OptionEditorWindow (PBD::Configuration *, std::string const &);
738         ~OptionEditorWindow() {}
739 private:
740         Gtk::VBox container;
741         Gtk::HBox hpacker;
742 };
743
744 #endif /* __gtk_ardour_option_editor_h__ */