First stage of options rework.
[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 "ardour_dialog.h"
29
30 /** @file option_editor.h
31  *  @brief Base class for option editing dialog boxes.
32  *
33  *  Code to provided the basis for dialogs which allow the user to edit options
34  *  from an ARDOUR::Configuration class.
35  *
36  *  The idea is that we have an OptionEditor class which is the dialog box.
37  *  This is essentially a GTK Notebook.  OptionEditorComponent objects can
38  *  then be added to the OptionEditor, and these components are arranged on
39  *  the pages of the Notebook.  There is also an OptionEditorComponent hierarchy
40  *  here, providing things like boolean and combobox option components.
41  *
42  *  It is intended that OptionEditor be subclassed to implement a particular
43  *  options dialog.
44  */
45
46 namespace ARDOUR {
47         class Configuration;
48 }
49
50 class OptionEditorPage;
51
52 /** Base class for components of an OptionEditor dialog */
53 class OptionEditorComponent
54 {
55 public:
56         /** Called when a configuration parameter's value has changed.
57          *  @param p parameter name
58          */
59         virtual void parameter_changed (std::string const & p) = 0;
60
61         /** Called to instruct the object to set its UI state from the configuration */
62         virtual void set_state_from_config () = 0;
63
64         /** Called to instruct the object to add itself to an OptionEditorPage */
65         virtual void add_to_page (OptionEditorPage *) = 0;
66
67         void add_widget_to_page (OptionEditorPage*, Gtk::Widget*);
68         void add_widgets_to_page (OptionEditorPage*, Gtk::Widget*, Gtk::Widget*);
69 };
70
71 /** A component which provides a subheading within the dialog */
72 class OptionEditorHeading : public OptionEditorComponent
73 {
74 public:
75         OptionEditorHeading (std::string const &);
76
77         void parameter_changed (std::string const &) {}
78         void set_state_from_config () {}
79         void add_to_page (OptionEditorPage *);
80
81 private:
82         Gtk::Label* _label; ///< the label used for the heading
83 };
84
85 /** A component which provides a box into which a subclass can put arbitrary widgets */
86 class OptionEditorBox : public OptionEditorComponent
87 {
88 public:
89
90         /** Construct an OpenEditorBox */
91         OptionEditorBox ()
92         {
93                 _box = Gtk::manage (new Gtk::VBox);
94                 _box->set_spacing (4);
95         }
96
97         void parameter_changed (std::string const &) = 0;
98         void set_state_from_config () = 0;
99         void add_to_page (OptionEditorPage *);
100
101 protected:
102         
103         Gtk::VBox* _box; ///< constituent box for subclasses to add widgets to
104 };
105
106 /** Base class for components which provide UI to change an option */
107 class Option : public OptionEditorComponent {
108         
109 public:
110         /** Construct an Option.
111          *  @param i Option id (e.g. "plugins-stop-with-transport")
112          *  @param n User-visible name (e.g. "Stop plugins when the transport is stopped")
113          */
114         Option (std::string const & i,
115                 std::string const & n
116                 )
117                 : _id (i),
118                   _name (n)
119         {}
120
121         void parameter_changed (std::string const & p)
122         {
123                 if (p == _id) {
124                         set_state_from_config ();
125                 }
126         }
127         
128         virtual void set_state_from_config () = 0;
129         virtual void add_to_page (OptionEditorPage*) = 0;
130
131         std::string id () const {
132                 return _id;
133         }
134         
135 private:
136         
137         std::string _id;
138         std::string _name;
139 };
140
141 /** Component which provides the UI to handle a boolean option using a GTK CheckButton */
142 class BoolOption : public Option {
143
144 public:
145         
146         BoolOption (std::string const &, std::string const &, sigc::slot<bool>, sigc::slot<bool, bool>);
147         void set_state_from_config ();
148         void toggled ();
149         void add_to_page (OptionEditorPage*);
150         
151 private:
152         
153         sigc::slot<bool> _get; ///< slot to get the configuration variable's value
154         sigc::slot<bool, bool> _set;  ///< slot to set the configuration variable's value
155         Gtk::CheckButton* _button; ///< UI button
156 };
157
158 /** Component which provides the UI to handle an enumerated option using a GTK CheckButton.
159  *  The template parameter is the enumeration.
160  */
161 template <class T>
162 class ComboOption : public Option {
163         
164 public:
165
166         /** Construct an ComboOption.
167          *  @param i id
168          *  @param n User-visible name.
169          *  @param g Slot to get the variable's value.
170          *  @param s Slot to set the variable's value.
171          */
172         ComboOption (
173                 std::string const & i,
174                 std::string const & n,
175                 sigc::slot<T> g,
176                 sigc::slot<bool, T> s
177                 )
178                 : Option (i, n),
179                   _get (g),
180                   _set (s)
181         {
182                 _label = manage (new Gtk::Label (n + ":"));
183                 _label->set_alignment (1, 0.5);
184                 _combo = manage (new Gtk::ComboBoxText);
185                 _combo->signal_changed().connect (sigc::mem_fun (*this, &ComboOption::changed));
186         }
187
188         void set_state_from_config () {
189                 uint32_t r = 0;
190                 while (r < _options.size() && _get () != _options[r]) {
191                         ++r;
192                 }
193                 
194                 if (r < _options.size()) {
195                         _combo->set_active (r);
196                 }
197         }
198
199         void add_to_page (OptionEditorPage* p)
200         {
201                 add_widgets_to_page (p, _label, _combo);
202         }
203
204         /** Add an allowed value for this option.
205          *  @param e Enumeration.
206          *  @param o User-visible name for this value.
207          */
208         void add (T e, std::string const & o) {
209                 _options.push_back (e);
210                 _combo->append_text (o);
211         }
212         
213         void changed () {
214                 uint32_t const r = _combo->get_active_row_number ();
215                 if (r < _options.size()) {
216                         _set (_options[r]);
217                 }
218         }
219
220 private:
221         
222         sigc::slot<T> _get;
223         sigc::slot<bool, T> _set;
224         Gtk::Label* _label;
225         Gtk::ComboBoxText* _combo;
226         std::vector<T> _options;
227 };
228
229
230 /** Component which provides the UI to handle an numeric option using a GTK SpinButton */
231 template <class T>
232 class SpinOption : public Option
233 {
234 public:
235         /** Construct an SpinOption.
236          *  @param i id
237          *  @param n User-visible name.
238          *  @param g Slot to get the variable's value.
239          *  @param s Slot to set the variable's value.
240          *  @param min Variable minimum value.
241          *  @param max Variable maximum value.
242          *  @param step Step for the spin button.
243          *  @param page Page step for the spin button.
244          */
245         SpinOption (
246                 std::string const & i,
247                 std::string const & n,
248                 sigc::slot<T> g,
249                 sigc::slot<bool, T> s,
250                 T min,
251                 T max,
252                 T step,
253                 T page
254                 )
255                 : Option (i, n),
256                   _get (g),
257                   _set (s)
258         {
259                 _label = manage (new Gtk::Label (n + ":"));
260                 _label->set_alignment (1, 0.5);
261                 _spin = manage (new Gtk::SpinButton);
262                 _spin->set_range (min, max);
263                 _spin->set_increments (step, page);
264                 _spin->signal_value_changed().connect (sigc::mem_fun (*this, &SpinOption::changed));
265         }
266
267         void set_state_from_config ()
268         {
269                 _spin->set_value (_get ());
270         }
271
272         void add_to_page (OptionEditorPage* p)
273         {
274                 add_widgets_to_page (p, _label, _spin);
275         }
276         
277         void changed ()
278         {
279                 _set (static_cast<T> (_spin->get_value ()));
280         }
281         
282 private:
283         sigc::slot<T> _get;
284         sigc::slot<bool, T> _set;
285         Gtk::Label* _label;
286         Gtk::SpinButton* _spin;
287 };
288
289 /** Class to represent a single page in an OptionEditor's notebook.
290  *  Pages are laid out using a 3-column table; the 1st column is used
291  *  to indent non-headings, and the 2nd and 3rd for actual content.
292  */
293 class OptionEditorPage
294 {
295 public:
296         OptionEditorPage (Gtk::Notebook&, std::string const &);
297
298         Gtk::VBox box;
299         Gtk::Table table;
300         std::list<OptionEditorComponent*> components;
301 };
302
303 /** The OptionEditor dialog base class */
304 class OptionEditor : public ArdourDialog
305 {
306 public:
307         OptionEditor (ARDOUR::Configuration *, std::string const &);
308         ~OptionEditor ();
309
310         void add (std::string const &, OptionEditorComponent *);
311
312 protected:
313         
314         ARDOUR::Configuration* _config;
315         
316 private:
317         void parameter_changed (std::string const &);
318
319         Gtk::Notebook _notebook;
320         std::map<std::string, OptionEditorPage*> _pages;
321 };
322
323 #endif /* __gtk_ardour_option_editor_h__ */
324
325