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