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