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