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