make "exclusive solo" apply to listen as well as SiP
[ardour.git] / gtk2_ardour / rc_option_editor.cc
1 #include <gtkmm/liststore.h>
2 #include <gtkmm/stock.h>
3 #include <gtkmm/scale.h>
4 #include <gtkmm2ext/utils.h>
5 #include <gtkmm2ext/slider_controller.h>
6
7 #include "pbd/fpu.h"
8
9 #include "midi++/manager.h"
10 #include "midi++/factory.h"
11
12 #include "ardour/audioengine.h"
13 #include "ardour/dB.h"
14 #include "ardour/rc_configuration.h"
15 #include "ardour/control_protocol_manager.h"
16 #include "control_protocol/control_protocol.h"
17
18 #include "gui_thread.h"
19 #include "midi_tracer.h"
20 #include "rc_option_editor.h"
21 #include "utils.h"
22 #include "midi_port_dialog.h"
23 #include "sfdb_ui.h"
24 #include "keyboard.h"
25 #include "i18n.h"
26
27 using namespace std;
28 using namespace Gtk;
29 using namespace Gtkmm2ext;
30 using namespace PBD;
31 using namespace ARDOUR;
32
33 class MIDIPorts : public OptionEditorBox
34 {
35 public:
36         MIDIPorts (RCConfiguration* c, list<ComboOption<string>* > const & o)
37                 : _rc_config (c),
38                   _add_port_button (Stock::ADD),
39                   _port_combos (o)
40         {
41                 _store = ListStore::create (_model);
42                 _view.set_model (_store);
43                 _view.append_column (_("Name"), _model.name);
44                 _view.get_column(0)->set_resizable (true);
45                 _view.get_column(0)->set_expand (true);
46                 _view.append_column_editable (_("Online"), _model.online);
47                 _view.append_column_editable (_("Trace input"), _model.trace_input);
48                 _view.append_column_editable (_("Trace output"), _model.trace_output);
49
50                 HBox* h = manage (new HBox);
51                 h->set_spacing (4);
52                 h->pack_start (_view, true, true);
53
54                 VBox* v = manage (new VBox);
55                 v->set_spacing (4);
56                 v->pack_start (_add_port_button, false, false);
57                 h->pack_start (*v, false, false);
58
59                 _box->pack_start (*h);
60
61                 ports_changed ();
62
63                 _store->signal_row_changed().connect (sigc::mem_fun (*this, &MIDIPorts::model_changed));
64
65                 _add_port_button.signal_clicked().connect (sigc::mem_fun (*this, &MIDIPorts::add_port_clicked));
66         }
67
68         void parameter_changed (string const &) {}
69         void set_state_from_config () {}
70
71 private:
72
73         typedef std::map<MIDI::Port*,MidiTracer*> PortTraceMap;
74         PortTraceMap port_input_trace_map;
75         PortTraceMap port_output_trace_map;
76
77         void model_changed (TreeModel::Path const &, TreeModel::iterator const & i)
78         {
79                 TreeModel::Row r = *i;
80
81                 MIDI::Port* port = r[_model.port];
82                 if (!port) {
83                         return;
84                 }
85
86                 if (port->input()) {
87
88                         if (r[_model.online] == port->input()->offline()) {
89                                 port->input()->set_offline (!r[_model.online]);
90                         }
91
92                         if (r[_model.trace_input] != port->input()->tracing()) {
93                                 PortTraceMap::iterator x = port_input_trace_map.find (port);
94                                 MidiTracer* mt;
95
96                                 if (x == port_input_trace_map.end()) {
97                                          mt = new MidiTracer (port->name() + string (" [input]"), *port->input());
98                                          port_input_trace_map.insert (pair<MIDI::Port*,MidiTracer*> (port, mt));
99                                 } else {
100                                         mt = x->second;
101                                 }
102                                 mt->present ();
103                         }
104                 }
105
106                 if (port->output()) {
107
108                         if (r[_model.trace_output] != port->output()->tracing()) {
109                                 PortTraceMap::iterator x = port_output_trace_map.find (port);
110                                 MidiTracer* mt;
111
112                                 if (x == port_output_trace_map.end()) {
113                                         mt = new MidiTracer (port->name() + string (" [output]"), *port->output());
114                                         port_output_trace_map.insert (pair<MIDI::Port*,MidiTracer*> (port, mt));
115                                 } else {
116                                         mt = x->second;
117                                 }
118                                 mt->present ();
119                         }
120
121                 }
122         }
123
124         void setup_ports_combo (ComboOption<string>* c)
125         {
126                 c->clear ();
127                 MIDI::Manager::PortList const & ports = MIDI::Manager::instance()->get_midi_ports ();
128                 for (MIDI::Manager::PortList::const_iterator i = ports.begin(); i != ports.end(); ++i) {
129                         c->add ((*i)->name(), (*i)->name());
130                 }
131         }       
132
133         void ports_changed ()
134         {
135                 /* XXX: why is this coming from here? */
136                 MIDI::Manager::PortList const & ports = MIDI::Manager::instance()->get_midi_ports ();
137
138                 _store->clear ();
139                 port_connections.drop_connections ();
140
141                 for (MIDI::Manager::PortList::const_iterator i = ports.begin(); i != ports.end(); ++i) {
142
143                         TreeModel::Row r = *_store->append ();
144
145                         r[_model.name] = (*i)->name();
146
147                         if ((*i)->input()) {
148                                 r[_model.online] = !(*i)->input()->offline();
149                                 (*i)->input()->OfflineStatusChanged.connect (port_connections, MISSING_INVALIDATOR, boost::bind (&MIDIPorts::port_offline_changed, this, (*i)), gui_context());
150                                 r[_model.trace_input] = (*i)->input()->tracing();
151                         }
152
153                         if ((*i)->output()) {
154                                 r[_model.trace_output] = (*i)->output()->tracing();
155                         }
156
157                         r[_model.port] = (*i);
158                 }
159
160                 for (list<ComboOption<string>* >::iterator i = _port_combos.begin(); i != _port_combos.end(); ++i) {
161                         setup_ports_combo (*i);
162                 }
163         }
164
165         void port_offline_changed (MIDI::Port* p)
166         {
167                 if (!p->input()) {
168                         return;
169                 }
170
171                 for (TreeModel::Children::iterator i = _store->children().begin(); i != _store->children().end(); ++i) {
172                         if ((*i)[_model.port] == p) {
173                                 (*i)[_model.online] = !p->input()->offline();
174                         }
175                 }
176         }
177
178         void add_port_clicked ()
179         {
180                 MidiPortDialog dialog;
181
182                 dialog.set_position (WIN_POS_MOUSE);
183
184                 dialog.show ();
185
186                 int const r = dialog.run ();
187
188                 switch (r) {
189                 case RESPONSE_ACCEPT:
190                         break;
191                 default:
192                         return;
193                         break;
194                 }
195
196                 Glib::ustring const mode = dialog.port_mode_combo.get_active_text ();
197                 string smod;
198
199                 if (mode == _("input")) {
200                         smod = X_("input");
201                 } else if (mode == (_("output"))) {
202                         smod = X_("output");
203                 } else {
204                         smod = "duplex";
205                 }
206
207                 XMLNode node (X_("MIDI-port"));
208
209                 node.add_property ("tag", dialog.port_name.get_text());
210                 node.add_property ("device", X_("ardour")); // XXX this can't be right for all types
211                 node.add_property ("type", MIDI::PortFactory::default_port_type());
212                 node.add_property ("mode", smod);
213
214                 if (MIDI::Manager::instance()->add_port (node) != 0) {
215                         cerr << " there are now " << MIDI::Manager::instance()->nports() << endl;
216                         ports_changed ();
217                 }
218         }
219
220         class MIDIModelColumns : public TreeModelColumnRecord
221         {
222         public:
223                 MIDIModelColumns ()
224                 {
225                         add (name);
226                         add (online);
227                         add (trace_input);
228                         add (trace_output);
229                         add (port);
230                 }
231
232                 TreeModelColumn<string> name;
233                 TreeModelColumn<bool> online;
234                 TreeModelColumn<bool> trace_input;
235                 TreeModelColumn<bool> trace_output;
236                 TreeModelColumn<MIDI::Port*> port;
237         };
238
239         RCConfiguration* _rc_config;
240         Glib::RefPtr<ListStore> _store;
241         MIDIModelColumns _model;
242         TreeView _view;
243         Button _add_port_button;
244         ComboBoxText _mtc_combo;
245         ComboBoxText _midi_clock_combo;
246         ComboBoxText _mmc_combo;
247         ComboBoxText _mpc_combo;
248         list<ComboOption<string>* > _port_combos;
249         PBD::ScopedConnectionList port_connections;
250 };
251
252
253 class ClickOptions : public OptionEditorBox
254 {
255 public:
256         ClickOptions (RCConfiguration* c, ArdourDialog* p)
257                 : _rc_config (c),
258                   _parent (p)
259         {
260                 Table* t = manage (new Table (2, 3));
261                 t->set_spacings (4);
262
263                 Label* l = manage (new Label (_("Click audio file:")));
264                 l->set_alignment (0, 0.5);
265                 t->attach (*l, 0, 1, 0, 1, FILL);
266                 t->attach (_click_path_entry, 1, 2, 0, 1, FILL);
267                 Button* b = manage (new Button (_("Browse...")));
268                 b->signal_clicked().connect (sigc::mem_fun (*this, &ClickOptions::click_browse_clicked));
269                 t->attach (*b, 2, 3, 0, 1, FILL);
270
271                 l = manage (new Label (_("Click emphasis audio file:")));
272                 l->set_alignment (0, 0.5);
273                 t->attach (*l, 0, 1, 1, 2, FILL);
274                 t->attach (_click_emphasis_path_entry, 1, 2, 1, 2, FILL);
275                 b = manage (new Button (_("Browse...")));
276                 b->signal_clicked().connect (sigc::mem_fun (*this, &ClickOptions::click_emphasis_browse_clicked));
277                 t->attach (*b, 2, 3, 1, 2, FILL);
278
279                 _box->pack_start (*t, false, false);
280         }
281
282         void parameter_changed (string const & p)
283         {
284                 if (p == "click-sound") {
285                         _click_path_entry.set_text (_rc_config->get_click_sound());
286                 } else if (p == "click-emphasis-sound") {
287                         _click_emphasis_path_entry.set_text (_rc_config->get_click_emphasis_sound());
288                 }
289         }
290
291         void set_state_from_config ()
292         {
293                 parameter_changed ("click-sound");
294                 parameter_changed ("click-emphasis-sound");
295         }
296
297 private:
298
299         void click_browse_clicked ()
300         {
301                 SoundFileChooser sfdb (*_parent, _("Choose Click"));
302
303                 sfdb.show_all ();
304                 sfdb.present ();
305
306                 if (sfdb.run () == RESPONSE_OK) {
307                         click_chosen (sfdb.get_filename());
308                 }
309         }
310
311         void click_chosen (string const & path)
312         {
313                 _click_path_entry.set_text (path);
314                 _rc_config->set_click_sound (path);
315         }
316
317         void click_emphasis_browse_clicked ()
318         {
319                 SoundFileChooser sfdb (*_parent, _("Choose Click Emphasis"));
320
321                 sfdb.show_all ();
322                 sfdb.present ();
323
324                 if (sfdb.run () == RESPONSE_OK) {
325                         click_emphasis_chosen (sfdb.get_filename());
326                 }
327         }
328
329         void click_emphasis_chosen (string const & path)
330         {
331                 _click_emphasis_path_entry.set_text (path);
332                 _rc_config->set_click_emphasis_sound (path);
333         }
334
335         RCConfiguration* _rc_config;
336         ArdourDialog* _parent;
337         Entry _click_path_entry;
338         Entry _click_emphasis_path_entry;
339 };
340
341 class UndoOptions : public OptionEditorBox
342 {
343 public:
344         UndoOptions (RCConfiguration* c) :
345                 _rc_config (c),
346                 _limit_undo_button (_("Limit undo history to")),
347                 _save_undo_button (_("Save undo history of"))
348         {
349                 Table* t = new Table (2, 3);
350                 t->set_spacings (4);
351
352                 t->attach (_limit_undo_button, 0, 1, 0, 1, FILL);
353                 _limit_undo_spin.set_range (0, 512);
354                 _limit_undo_spin.set_increments (1, 10);
355                 t->attach (_limit_undo_spin, 1, 2, 0, 1, FILL | EXPAND);
356                 Label* l = manage (new Label (_("commands")));
357                 l->set_alignment (0, 0.5);
358                 t->attach (*l, 2, 3, 0, 1);
359
360                 t->attach (_save_undo_button, 0, 1, 1, 2, FILL);
361                 _save_undo_spin.set_range (0, 512);
362                 _save_undo_spin.set_increments (1, 10);
363                 t->attach (_save_undo_spin, 1, 2, 1, 2, FILL | EXPAND);
364                 l = manage (new Label (_("commands")));
365                 l->set_alignment (0, 0.5);
366                 t->attach (*l, 2, 3, 1, 2);
367
368                 _box->pack_start (*t);
369
370                 _limit_undo_button.signal_toggled().connect (sigc::mem_fun (*this, &UndoOptions::limit_undo_toggled));
371                 _limit_undo_spin.signal_value_changed().connect (sigc::mem_fun (*this, &UndoOptions::limit_undo_changed));
372                 _save_undo_button.signal_toggled().connect (sigc::mem_fun (*this, &UndoOptions::save_undo_toggled));
373                 _save_undo_spin.signal_value_changed().connect (sigc::mem_fun (*this, &UndoOptions::save_undo_changed));
374         }
375
376         void parameter_changed (string const & p)
377         {
378                 if (p == "history-depth") {
379                         int32_t const d = _rc_config->get_history_depth();
380                         _limit_undo_button.set_active (d != 0);
381                         _limit_undo_spin.set_sensitive (d != 0);
382                         _limit_undo_spin.set_value (d);
383                 } else if (p == "save-history") {
384                         bool const x = _rc_config->get_save_history ();
385                         _save_undo_button.set_active (x);
386                         _save_undo_spin.set_sensitive (x);
387                 } else if (p == "save-history-depth") {
388                         _save_undo_spin.set_value (_rc_config->get_saved_history_depth());
389                 }
390         }
391
392         void set_state_from_config ()
393         {
394                 parameter_changed ("save-history");
395                 parameter_changed ("history-depth");
396                 parameter_changed ("save-history-depth");
397         }
398
399         void limit_undo_toggled ()
400         {
401                 bool const x = _limit_undo_button.get_active ();
402                 _limit_undo_spin.set_sensitive (x);
403                 int32_t const n = x ? 16 : 0;
404                 _limit_undo_spin.set_value (n);
405                 _rc_config->set_history_depth (n);
406         }
407
408         void limit_undo_changed ()
409         {
410                 _rc_config->set_history_depth (_limit_undo_spin.get_value_as_int ());
411         }
412
413         void save_undo_toggled ()
414         {
415                 bool const x = _save_undo_button.get_active ();
416                 _rc_config->set_save_history (x);
417         }
418
419         void save_undo_changed ()
420         {
421                 _rc_config->set_saved_history_depth (_save_undo_spin.get_value_as_int ());
422         }
423
424 private:
425         RCConfiguration* _rc_config;
426         CheckButton _limit_undo_button;
427         SpinButton _limit_undo_spin;
428         CheckButton _save_undo_button;
429         SpinButton _save_undo_spin;
430 };
431
432
433
434 static const struct {
435     const char *name;
436     guint modifier;
437 } modifiers[] = {
438
439         { "Unmodified", 0 },
440
441 #ifdef GTKOSX
442
443         /* Command = Meta
444            Option/Alt = Mod1
445         */
446         { "Shift", GDK_SHIFT_MASK },
447         { "Command", GDK_META_MASK },
448         { "Control", GDK_CONTROL_MASK },
449         { "Option", GDK_MOD1_MASK },
450         { "Command-Shift", GDK_META_MASK|GDK_SHIFT_MASK },
451         { "Command-Option", GDK_MOD1_MASK|GDK_META_MASK },
452         { "Shift-Option", GDK_SHIFT_MASK|GDK_MOD1_MASK },
453         { "Shift-Command-Option", GDK_MOD5_MASK|GDK_SHIFT_MASK|GDK_META_MASK },
454
455 #else
456         { "Shift", GDK_SHIFT_MASK },
457         { "Control", GDK_CONTROL_MASK },
458         { "Alt (Mod1)", GDK_MOD1_MASK },
459         { "Control-Shift", GDK_CONTROL_MASK|GDK_SHIFT_MASK },
460         { "Control-Alt", GDK_CONTROL_MASK|GDK_MOD1_MASK },
461         { "Shift-Alt", GDK_SHIFT_MASK|GDK_MOD1_MASK },
462         { "Control-Shift-Alt", GDK_CONTROL_MASK|GDK_SHIFT_MASK|GDK_MOD1_MASK },
463         { "Mod2", GDK_MOD2_MASK },
464         { "Mod3", GDK_MOD3_MASK },
465         { "Mod4", GDK_MOD4_MASK },
466         { "Mod5", GDK_MOD5_MASK },
467 #endif
468         { 0, 0 }
469 };
470
471
472 class KeyboardOptions : public OptionEditorBox
473 {
474 public:
475         KeyboardOptions () :
476                   _delete_button_adjustment (3, 1, 12),
477                   _delete_button_spin (_delete_button_adjustment),
478                   _edit_button_adjustment (3, 1, 5),
479                   _edit_button_spin (_edit_button_adjustment)
480
481         {
482                 /* internationalize and prepare for use with combos */
483
484                 vector<string> dumb;
485                 for (int i = 0; modifiers[i].name; ++i) {
486                         dumb.push_back (_(modifiers[i].name));
487                 }
488
489                 set_popdown_strings (_edit_modifier_combo, dumb);
490                 _edit_modifier_combo.signal_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::edit_modifier_chosen));
491
492                 for (int x = 0; modifiers[x].name; ++x) {
493                         if (modifiers[x].modifier == Keyboard::edit_modifier ()) {
494                                 _edit_modifier_combo.set_active_text (_(modifiers[x].name));
495                                 break;
496                         }
497                 }
498
499                 Table* t = manage (new Table (4, 4));
500                 t->set_spacings (4);
501
502                 Label* l = manage (new Label (_("Edit using:")));
503                 l->set_name ("OptionsLabel");
504                 l->set_alignment (0, 0.5);
505
506                 t->attach (*l, 0, 1, 0, 1, FILL | EXPAND, FILL);
507                 t->attach (_edit_modifier_combo, 1, 2, 0, 1, FILL | EXPAND, FILL);
508
509                 l = manage (new Label (_("+ button")));
510                 l->set_name ("OptionsLabel");
511
512                 t->attach (*l, 3, 4, 0, 1, FILL | EXPAND, FILL);
513                 t->attach (_edit_button_spin, 4, 5, 0, 1, FILL | EXPAND, FILL);
514
515                 _edit_button_spin.set_name ("OptionsEntry");
516                 _edit_button_adjustment.set_value (Keyboard::edit_button());
517                 _edit_button_adjustment.signal_value_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::edit_button_changed));
518
519                 set_popdown_strings (_delete_modifier_combo, dumb);
520                 _delete_modifier_combo.signal_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::delete_modifier_chosen));
521
522                 for (int x = 0; modifiers[x].name; ++x) {
523                         if (modifiers[x].modifier == Keyboard::delete_modifier ()) {
524                                 _delete_modifier_combo.set_active_text (_(modifiers[x].name));
525                                 break;
526                         }
527                 }
528
529                 l = manage (new Label (_("Delete using:")));
530                 l->set_name ("OptionsLabel");
531                 l->set_alignment (0, 0.5);
532
533                 t->attach (*l, 0, 1, 1, 2, FILL | EXPAND, FILL);
534                 t->attach (_delete_modifier_combo, 1, 2, 1, 2, FILL | EXPAND, FILL);
535
536                 l = manage (new Label (_("+ button")));
537                 l->set_name ("OptionsLabel");
538
539                 t->attach (*l, 3, 4, 1, 2, FILL | EXPAND, FILL);
540                 t->attach (_delete_button_spin, 4, 5, 1, 2, FILL | EXPAND, FILL);
541
542                 _delete_button_spin.set_name ("OptionsEntry");
543                 _delete_button_adjustment.set_value (Keyboard::delete_button());
544                 _delete_button_adjustment.signal_value_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::delete_button_changed));
545
546                 set_popdown_strings (_snap_modifier_combo, dumb);
547                 _snap_modifier_combo.signal_changed().connect (sigc::mem_fun(*this, &KeyboardOptions::snap_modifier_chosen));
548
549                 for (int x = 0; modifiers[x].name; ++x) {
550                         if (modifiers[x].modifier == (guint) Keyboard::snap_modifier ()) {
551                                 _snap_modifier_combo.set_active_text (_(modifiers[x].name));
552                                 break;
553                         }
554                 }
555
556                 l = manage (new Label (_("Toggle snap using:")));
557                 l->set_name ("OptionsLabel");
558                 l->set_alignment (0, 0.5);
559
560                 t->attach (*l, 0, 1, 2, 3, FILL | EXPAND, FILL);
561                 t->attach (_snap_modifier_combo, 1, 2, 2, 3, FILL | EXPAND, FILL);
562
563                 vector<string> strs;
564
565                 for (map<string,string>::iterator bf = Keyboard::binding_files.begin(); bf != Keyboard::binding_files.end(); ++bf) {
566                         strs.push_back (bf->first);
567                 }
568
569                 set_popdown_strings (_keyboard_layout_selector, strs);
570                 _keyboard_layout_selector.set_active_text (Keyboard::current_binding_name());
571                 _keyboard_layout_selector.signal_changed().connect (sigc::mem_fun (*this, &KeyboardOptions::bindings_changed));
572
573                 l = manage (new Label (_("Keyboard layout:")));
574                 l->set_name ("OptionsLabel");
575                 l->set_alignment (0, 0.5);
576
577                 t->attach (*l, 0, 1, 3, 4, FILL | EXPAND, FILL);
578                 t->attach (_keyboard_layout_selector, 1, 2, 3, 4, FILL | EXPAND, FILL);
579
580                 _box->pack_start (*t, false, false);
581         }
582
583         void parameter_changed (string const &)
584         {
585                 /* XXX: these aren't really config options... */
586         }
587
588         void set_state_from_config ()
589         {
590                 /* XXX: these aren't really config options... */
591         }
592
593 private:
594
595         void bindings_changed ()
596         {
597                 string const txt = _keyboard_layout_selector.get_active_text();
598
599                 /* XXX: config...?  for all this keyboard stuff */
600
601                 for (map<string,string>::iterator i = Keyboard::binding_files.begin(); i != Keyboard::binding_files.end(); ++i) {
602                         if (txt == i->first) {
603                                 if (Keyboard::load_keybindings (i->second)) {
604                                         Keyboard::save_keybindings ();
605                                 }
606                         }
607                 }
608         }
609
610         void edit_modifier_chosen ()
611         {
612                 string const txt = _edit_modifier_combo.get_active_text();
613
614                 for (int i = 0; modifiers[i].name; ++i) {
615                         if (txt == _(modifiers[i].name)) {
616                                 Keyboard::set_edit_modifier (modifiers[i].modifier);
617                                 break;
618                         }
619                 }
620         }
621
622         void delete_modifier_chosen ()
623         {
624                 string const txt = _delete_modifier_combo.get_active_text();
625
626                 for (int i = 0; modifiers[i].name; ++i) {
627                         if (txt == _(modifiers[i].name)) {
628                                 Keyboard::set_delete_modifier (modifiers[i].modifier);
629                                 break;
630                         }
631                 }
632         }
633
634         void snap_modifier_chosen ()
635         {
636                 string const txt = _snap_modifier_combo.get_active_text();
637
638                 for (int i = 0; modifiers[i].name; ++i) {
639                         if (txt == _(modifiers[i].name)) {
640                                 Keyboard::set_snap_modifier (modifiers[i].modifier);
641                                 break;
642                         }
643                 }
644         }
645
646         void delete_button_changed ()
647         {
648                 Keyboard::set_delete_button (_delete_button_spin.get_value_as_int());
649         }
650
651         void edit_button_changed ()
652         {
653                 Keyboard::set_edit_button (_edit_button_spin.get_value_as_int());
654         }
655
656         ComboBoxText _keyboard_layout_selector;
657         ComboBoxText _edit_modifier_combo;
658         ComboBoxText _delete_modifier_combo;
659         ComboBoxText _snap_modifier_combo;
660         Adjustment _delete_button_adjustment;
661         SpinButton _delete_button_spin;
662         Adjustment _edit_button_adjustment;
663         SpinButton _edit_button_spin;
664 };
665
666 class FontScalingOptions : public OptionEditorBox
667 {
668 public:
669         FontScalingOptions (RCConfiguration* c) :
670                 _rc_config (c),
671                 _dpi_adjustment (50, 50, 250, 1, 10),
672                 _dpi_slider (_dpi_adjustment)
673         {
674                 _dpi_adjustment.set_value (_rc_config->get_font_scale () / 1024);
675
676                 Label* l = manage (new Label (_("Font scaling:")));
677                 l->set_name ("OptionsLabel");
678
679                 _dpi_slider.set_update_policy (UPDATE_DISCONTINUOUS);
680                 HBox* h = manage (new HBox);
681                 h->set_spacing (4);
682                 h->pack_start (*l, false, false);
683                 h->pack_start (_dpi_slider, true, true);
684
685                 _box->pack_start (*h, false, false);
686
687                 _dpi_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &FontScalingOptions::dpi_changed));
688         }
689
690         void parameter_changed (string const & p)
691         {
692                 if (p == "font-scale") {
693                         _dpi_adjustment.set_value (_rc_config->get_font_scale() / 1024);
694                 }
695         }
696
697         void set_state_from_config ()
698         {
699                 parameter_changed ("font-scale");
700         }
701
702 private:
703
704         void dpi_changed ()
705         {
706                 _rc_config->set_font_scale ((long) floor (_dpi_adjustment.get_value() * 1024));
707                 /* XXX: should be triggered from the parameter changed signal */
708                 reset_dpi ();
709         }
710
711         RCConfiguration* _rc_config;
712         Adjustment _dpi_adjustment;
713         HScale _dpi_slider;
714 };
715
716 class SoloMuteOptions : public OptionEditorBox
717 {
718 public:
719         SoloMuteOptions (RCConfiguration* c) :
720                 _rc_config (c),
721                 // 0.781787 is the value needed for gain to be set to 0.
722                 _db_adjustment (0.781787, 0.0, 1.0, 0.01, 0.1)
723
724         {
725                 if ((pix = ::get_icon ("fader_belt_h")) == 0) {
726                         throw failed_constructor();
727                 }
728
729                 _db_slider = manage (new HSliderController (pix,
730                                                             &_db_adjustment,
731                                                             115,
732                                                             false));
733
734                 parameter_changed ("solo-mute-gain");
735
736                 Label* l = manage (new Label (_("Solo mute cut (dB):")));
737                 l->set_name ("OptionsLabel");
738
739                 HBox* h = manage (new HBox);
740                 h->set_spacing (4);
741                 h->pack_start (*l, false, false);
742                 h->pack_start (*_db_slider, false, false);
743                 h->pack_start (_db_display, false, false);
744                 h->show_all ();
745
746                 set_size_request_to_display_given_text (_db_display, "-99.0", 12, 12);
747
748                 _box->pack_start (*h, false, false);
749
750                 _db_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &SoloMuteOptions::db_changed));
751         }
752
753         void parameter_changed (string const & p)
754         {
755                 if (p == "solo-mute-gain") {
756                         gain_t val = _rc_config->get_solo_mute_gain();
757
758                         _db_adjustment.set_value (gain_to_slider_position (val));
759
760                         char buf[16];
761
762                         if (val == 0.0) {
763                                 snprintf (buf, sizeof (buf), "-inf");
764                         } else {
765                                 snprintf (buf, sizeof (buf), "%.2f", accurate_coefficient_to_dB (val));
766                         }
767
768                         _db_display.set_text (buf);
769                 }
770         }
771
772         void set_state_from_config ()
773         {
774                 parameter_changed ("solo-mute-gain");
775         }
776
777 private:
778
779         void db_changed ()
780         {
781                 _rc_config->set_solo_mute_gain (slider_position_to_gain (_db_adjustment.get_value()));
782         }
783
784         RCConfiguration* _rc_config;
785         Adjustment _db_adjustment;
786         Gtkmm2ext::HSliderController* _db_slider;
787         Glib::RefPtr<Gdk::Pixbuf> pix;
788         Entry _db_display;
789 };
790
791
792 class ControlSurfacesOptions : public OptionEditorBox
793 {
794 public:
795         ControlSurfacesOptions (ArdourDialog& parent)
796                 : _parent (parent)
797         {
798                 _store = ListStore::create (_model);
799                 _view.set_model (_store);
800                 _view.append_column (_("Name"), _model.name);
801                 _view.get_column(0)->set_resizable (true);
802                 _view.get_column(0)->set_expand (true);
803                 _view.append_column_editable (_("Enabled"), _model.enabled);
804                 _view.append_column_editable (_("Feedback"), _model.feedback);
805
806                 _box->pack_start (_view, false, false);
807
808                 Label* label = manage (new Label);
809                 label->set_markup (string_compose (X_("<i>%1</i>"), _("Double-click on a name to edit settings for an enabled protocol")));
810
811                 _box->pack_start (*label, false, false);
812                 label->show ();
813                 
814                 _store->signal_row_changed().connect (sigc::mem_fun (*this, &ControlSurfacesOptions::model_changed));
815                 _view.signal_button_press_event().connect_notify (sigc::mem_fun(*this, &ControlSurfacesOptions::edit_clicked));
816         }
817
818         void parameter_changed (std::string const &)
819         {
820
821         }
822
823         void set_state_from_config ()
824         {
825                 _store->clear ();
826
827                 ControlProtocolManager& m = ControlProtocolManager::instance ();
828                 for (list<ControlProtocolInfo*>::iterator i = m.control_protocol_info.begin(); i != m.control_protocol_info.end(); ++i) {
829
830                         if (!(*i)->mandatory) {
831                                 TreeModel::Row r = *_store->append ();
832                                 r[_model.name] = (*i)->name;
833                                 r[_model.enabled] = ((*i)->protocol || (*i)->requested);
834                                 r[_model.feedback] = ((*i)->protocol && (*i)->protocol->get_feedback ());
835                                 r[_model.protocol_info] = *i;
836                         }
837                 }
838         }
839
840 private:
841
842         void model_changed (TreeModel::Path const &, TreeModel::iterator const & i)
843         {
844                 TreeModel::Row r = *i;
845
846                 ControlProtocolInfo* cpi = r[_model.protocol_info];
847                 if (!cpi) {
848                         return;
849                 }
850
851                 bool const was_enabled = (cpi->protocol != 0);
852                 bool const is_enabled = r[_model.enabled];
853
854                 if (was_enabled != is_enabled) {
855                         if (!was_enabled) {
856                                 ControlProtocolManager::instance().instantiate (*cpi);
857                         } else {
858                                 ControlProtocolManager::instance().teardown (*cpi);
859                         }
860                 }
861
862                 bool const was_feedback = (cpi->protocol && cpi->protocol->get_feedback ());
863                 bool const is_feedback = r[_model.feedback];
864
865                 if (was_feedback != is_feedback && cpi->protocol) {
866                         cpi->protocol->set_feedback (is_feedback);
867                 }
868         }
869
870         void edit_clicked (GdkEventButton* ev)
871         {
872                 if (ev->type != GDK_2BUTTON_PRESS) {
873                         return;
874                 }
875
876                 std::string name;
877                 ControlProtocolInfo* cpi;
878                 TreeModel::Row row;
879                 
880                 row = *(_view.get_selection()->get_selected());
881
882                 Window* win = row[_model.editor];
883                 if (win && !win->is_visible()) {
884                         win->present (); 
885                 } else {
886                         cpi = row[_model.protocol_info];
887                         
888                         if (cpi && cpi->protocol && cpi->protocol->has_editor ()) {
889                                 Box* box = (Box*) cpi->protocol->get_gui ();
890                                 if (box) {
891                                         string title = row[_model.name];
892                                         ArdourDialog* win = new ArdourDialog (_parent, title);
893                                         win->get_vbox()->pack_start (*box, false, false);
894                                         box->show ();
895                                         win->present ();
896                                         row[_model.editor] = win;
897                                 }
898                         }
899                 }
900         }
901
902         class ControlSurfacesModelColumns : public TreeModelColumnRecord
903         {
904         public:
905
906                 ControlSurfacesModelColumns ()
907                 {
908                         add (name);
909                         add (enabled);
910                         add (feedback);
911                         add (protocol_info);
912                         add (editor);
913                 }
914
915                 TreeModelColumn<string> name;
916                 TreeModelColumn<bool> enabled;
917                 TreeModelColumn<bool> feedback;
918                 TreeModelColumn<ControlProtocolInfo*> protocol_info;
919                 TreeModelColumn<Gtk::Window*> editor;
920         };
921
922         Glib::RefPtr<ListStore> _store;
923         ControlSurfacesModelColumns _model;
924         TreeView _view;
925         Gtk::Window& _parent;
926 };
927
928
929 RCOptionEditor::RCOptionEditor ()
930         : OptionEditor (Config, string_compose (_("%1 Preferences"), PROGRAM_NAME))
931         , _rc_config (Config)
932 {
933         /* MISC */
934
935         add_option (_("Misc"), new OptionEditorHeading (_("Metering")));
936
937         ComboOption<float>* mht = new ComboOption<float> (
938                 "meter-hold",
939                 _("Meter hold time"),
940                 sigc::mem_fun (*_rc_config, &RCConfiguration::get_meter_hold),
941                 sigc::mem_fun (*_rc_config, &RCConfiguration::set_meter_hold)
942                 );
943
944         mht->add (MeterHoldOff, _("off"));
945         mht->add (MeterHoldShort, _("short"));
946         mht->add (MeterHoldMedium, _("medium"));
947         mht->add (MeterHoldLong, _("long"));
948
949         add_option (_("Misc"), mht);
950
951         ComboOption<float>* mfo = new ComboOption<float> (
952                 "meter-falloff",
953                 _("Meter fall-off"),
954                 sigc::mem_fun (*_rc_config, &RCConfiguration::get_meter_falloff),
955                 sigc::mem_fun (*_rc_config, &RCConfiguration::set_meter_falloff)
956                 );
957
958         mfo->add (METER_FALLOFF_OFF, _("off"));
959         mfo->add (METER_FALLOFF_SLOWEST, _("slowest"));
960         mfo->add (METER_FALLOFF_SLOW, _("slow"));
961         mfo->add (METER_FALLOFF_MEDIUM, _("medium"));
962         mfo->add (METER_FALLOFF_FAST, _("fast"));
963         mfo->add (METER_FALLOFF_FASTER, _("faster"));
964         mfo->add (METER_FALLOFF_FASTEST, _("fastest"));
965
966         add_option (_("Misc"), mfo);
967
968         add_option (_("Misc"), new OptionEditorHeading (_("Undo")));
969
970         add_option (_("Misc"), new UndoOptions (_rc_config));
971
972         add_option (_("Misc"), new OptionEditorHeading (_("Misc")));
973
974 #ifndef GTKOSX
975         /* font scaling does nothing with GDK/Quartz */
976         add_option (_("Misc"), new FontScalingOptions (_rc_config));
977 #endif
978
979         add_option (_("Misc"),
980              new BoolOption (
981                      "verify-remove-last-capture",
982                      _("Verify removal of last capture"),
983                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_verify_remove_last_capture),
984                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_verify_remove_last_capture)
985                      ));
986
987         add_option (_("Misc"),
988              new BoolOption (
989                      "periodic-safety-backups",
990                      _("Make periodic backups of the session file"),
991                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_periodic_safety_backups),
992                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_periodic_safety_backups)
993                      ));
994
995         add_option (_("Misc"),
996              new BoolOption (
997                      "sync-all-route-ordering",
998                      _("Syncronise editor and mixer track order"),
999                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_sync_all_route_ordering),
1000                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_sync_all_route_ordering)
1001                      ));
1002
1003         add_option (_("Misc"),
1004              new BoolOption (
1005                      "only-copy-imported-files",
1006                      _("Always copy imported files"),
1007                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_only_copy_imported_files),
1008                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_only_copy_imported_files)
1009                      ));
1010
1011         add_option (_("Misc"),
1012              new BoolOption (
1013                      "default-narrow_ms",
1014                      _("Use narrow mixer strips"),
1015                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_default_narrow_ms),
1016                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_default_narrow_ms)
1017                      ));
1018
1019         add_option (_("Misc"),
1020              new BoolOption (
1021                      "name-new-markers",
1022                      _("Name new markers"),
1023                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_name_new_markers),
1024                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_name_new_markers)
1025                      ));
1026
1027         /* TRANSPORT */
1028
1029         add_option (_("Transport"),
1030              new BoolOption (
1031                      "latched-record-enable",
1032                      _("Keep record-enable engaged on stop"),
1033                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_latched_record_enable),
1034                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_latched_record_enable)
1035                      ));
1036
1037         add_option (_("Transport"),
1038              new BoolOption (
1039                      "stop-recording-on-xrun",
1040                      _("Stop recording when an xrun occurs"),
1041                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_stop_recording_on_xrun),
1042                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_stop_recording_on_xrun)
1043                      ));
1044
1045         add_option (_("Transport"),
1046              new BoolOption (
1047                      "create-xrun-marker",
1048                      _("Create markers where xruns occur"),
1049                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_create_xrun_marker),
1050                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_create_xrun_marker)
1051                      ));
1052
1053         add_option (_("Transport"),
1054              new BoolOption (
1055                      "stop-at-session-end",
1056                      _("Stop at the end of the session"),
1057                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_stop_at_session_end),
1058                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_stop_at_session_end)
1059                      ));
1060
1061         add_option (_("Transport"),
1062              new BoolOption (
1063                      "primary-clock-delta-edit-cursor",
1064                      _("Primary clock delta to edit cursor"),
1065                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_primary_clock_delta_edit_cursor),
1066                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_primary_clock_delta_edit_cursor)
1067                      ));
1068
1069         add_option (_("Transport"),
1070              new BoolOption (
1071                      "secondary-clock-delta-edit-cursor",
1072                      _("Secondary clock delta to edit cursor"),
1073                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_secondary_clock_delta_edit_cursor),
1074                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_secondary_clock_delta_edit_cursor)
1075                      ));
1076
1077         add_option (_("Transport"),
1078              new BoolOption (
1079                      "disable-disarm-during-roll",
1080                      _("Disable per-track record disarm while rolling"),
1081                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_disable_disarm_during_roll),
1082                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_disable_disarm_during_roll)
1083                      ));
1084
1085         add_option (_("Transport"),
1086              new BoolOption (
1087                      "quieten_at_speed",
1088                      _("12dB gain reduction during fast-forward and fast-rewind"),
1089                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_quieten_at_speed),
1090                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_quieten_at_speed)
1091                      ));
1092
1093         /* EDITOR */
1094
1095         add_option (_("Editor"),
1096              new BoolOption (
1097                      "link-region-and-track-selection",
1098                      _("Link selection of regions and tracks"),
1099                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_link_region_and_track_selection),
1100                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_link_region_and_track_selection)
1101                      ));
1102
1103         add_option (_("Editor"),
1104              new BoolOption (
1105                      "automation-follows-regions",
1106                      _("Move relevant automation when regions are moved"),
1107                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_automation_follows_regions),
1108                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_automation_follows_regions)
1109                      ));
1110
1111         add_option (_("Editor"),
1112              new BoolOption (
1113                      "show-track-meters",
1114                      _("Show meters on tracks in the editor"),
1115                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_show_track_meters),
1116                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_show_track_meters)
1117                      ));
1118
1119         add_option (_("Editor"),
1120              new BoolOption (
1121                      "use-overlap-equivalency",
1122                      _("Use overlap equivalency for regions"),
1123                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_use_overlap_equivalency),
1124                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_use_overlap_equivalency)
1125                      ));
1126
1127         add_option (_("Editor"),
1128              new BoolOption (
1129                      "rubberbanding-snaps-to-grid",
1130                      _("Make rubberband selection rectangle snap to the grid"),
1131                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_rubberbanding_snaps_to_grid),
1132                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_rubberbanding_snaps_to_grid)
1133                      ));
1134
1135         add_option (_("Editor"),
1136              new BoolOption (
1137                      "show-waveforms",
1138                      _("Show waveforms in regions"),
1139                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_show_waveforms),
1140                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_show_waveforms)
1141                      ));
1142
1143         ComboOption<WaveformScale>* wfs = new ComboOption<WaveformScale> (
1144                 "waveform-scale",
1145                 _("Waveform scale"),
1146                 sigc::mem_fun (*_rc_config, &RCConfiguration::get_waveform_scale),
1147                 sigc::mem_fun (*_rc_config, &RCConfiguration::set_waveform_scale)
1148                 );
1149
1150         wfs->add (Linear, _("linear"));
1151         wfs->add (Logarithmic, _("logarithmic"));
1152
1153         add_option (_("Editor"), wfs);
1154
1155         ComboOption<WaveformShape>* wfsh = new ComboOption<WaveformShape> (
1156                 "waveform-shape",
1157                 _("Waveform shape"),
1158                 sigc::mem_fun (*_rc_config, &RCConfiguration::get_waveform_shape),
1159                 sigc::mem_fun (*_rc_config, &RCConfiguration::set_waveform_shape)
1160                 );
1161
1162         wfsh->add (Traditional, _("traditional"));
1163         wfsh->add (Rectified, _("rectified"));
1164
1165         add_option (_("Editor"), wfsh);
1166
1167         add_option (_("Editor"),
1168              new BoolOption (
1169                      "show-waveforms-while-recording",
1170                      _("Show waveforms for audio while it is being recorded"),
1171                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_show_waveforms_while_recording),
1172                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_show_waveforms_while_recording)
1173                      ));
1174
1175         /* AUDIO */
1176
1177         add_option (_("Audio"), new OptionEditorHeading (_("Solo")));
1178
1179         add_option (_("Audio"),
1180              new BoolOption (
1181                      "solo-control-is-listen-control",
1182                      _("Solo controls are Listen controls"),
1183                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_solo_control_is_listen_control),
1184                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_solo_control_is_listen_control)
1185                      ));
1186
1187         ComboOption<ListenPosition>* lp = new ComboOption<ListenPosition> (
1188                 "listen-position",
1189                 _("Listen Position"),
1190                 sigc::mem_fun (*_rc_config, &RCConfiguration::get_listen_position),
1191                 sigc::mem_fun (*_rc_config, &RCConfiguration::set_listen_position)
1192                 );
1193
1194         lp->add (AfterFaderListen, _("after-fader listen"));
1195         lp->add (PreFaderListen, _("pre-fader listen"));
1196
1197         add_option (_("Audio"), lp);
1198         add_option (_("Audio"), new SoloMuteOptions (_rc_config));
1199
1200         add_option (_("Audio"),
1201              new BoolOption (
1202                      "exclusive-solo",
1203                      _("Exclusive solo"),
1204                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_exclusive_solo),
1205                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_exclusive_solo)
1206                      ));
1207
1208         add_option (_("Audio"),
1209              new BoolOption (
1210                      "show-solo-mutes",
1211                      _("Show solo muting"),
1212                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_show_solo_mutes),
1213                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_show_solo_mutes)
1214                      ));
1215
1216         add_option (_("Audio"),
1217              new BoolOption (
1218                      "solo-mute-override",
1219                      _("Soloing overrides muting"),
1220                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_solo_mute_override),
1221                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_solo_mute_override)
1222                      ));
1223
1224         add_option (_("Audio"), new OptionEditorHeading (_("Monitoring")));
1225
1226         add_option (_("Audio"),
1227              new BoolOption (
1228                      "use-monitor-bus",
1229                      _("Use a monitor bus (allows AFL/PFL and more control)"),
1230                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_use_monitor_bus),
1231                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_use_monitor_bus)
1232                      ));
1233
1234         ComboOption<MonitorModel>* mm = new ComboOption<MonitorModel> (
1235                 "monitoring-model",
1236                 _("Monitoring handled by"),
1237                 sigc::mem_fun (*_rc_config, &RCConfiguration::get_monitoring_model),
1238                 sigc::mem_fun (*_rc_config, &RCConfiguration::set_monitoring_model)
1239                 );
1240
1241 #ifndef __APPLE__
1242         /* no JACK monitoring on CoreAudio */
1243         if (AudioEngine::instance()->can_request_hardware_monitoring()) {
1244                 mm->add (HardwareMonitoring, _("JACK"));
1245         }
1246 #endif
1247         mm->add (SoftwareMonitoring, _("ardour"));
1248         mm->add (ExternalMonitoring, _("audio hardware"));
1249
1250         add_option (_("Audio"), mm);
1251
1252         add_option (_("Audio"),
1253              new BoolOption (
1254                      "tape-machine-mode",
1255                      _("Tape machine mode"),
1256                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_tape_machine_mode),
1257                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_tape_machine_mode)
1258                      ));
1259
1260         add_option (_("Audio"), new OptionEditorHeading (_("Connection of tracks and busses")));
1261
1262         add_option (_("Audio"),
1263                     new BoolOption (
1264                             "auto-connect-standard-busses",
1265                             _("Auto-connect master/monitor busses"),
1266                             sigc::mem_fun (*_rc_config, &RCConfiguration::get_auto_connect_standard_busses),
1267                             sigc::mem_fun (*_rc_config, &RCConfiguration::set_auto_connect_standard_busses)
1268                             ));
1269
1270         ComboOption<AutoConnectOption>* iac = new ComboOption<AutoConnectOption> (
1271                 "input-auto-connect",
1272                 _("Connect track and bus inputs"),
1273                 sigc::mem_fun (*_rc_config, &RCConfiguration::get_input_auto_connect),
1274                 sigc::mem_fun (*_rc_config, &RCConfiguration::set_input_auto_connect)
1275                 );
1276
1277         iac->add (AutoConnectPhysical, _("automatically to physical inputs"));
1278         iac->add (ManualConnect, _("manually"));
1279
1280         add_option (_("Audio"), iac);
1281
1282         ComboOption<AutoConnectOption>* oac = new ComboOption<AutoConnectOption> (
1283                 "output-auto-connect",
1284                 _("Connect track and bus outputs"),
1285                 sigc::mem_fun (*_rc_config, &RCConfiguration::get_output_auto_connect),
1286                 sigc::mem_fun (*_rc_config, &RCConfiguration::set_output_auto_connect)
1287                 );
1288
1289         oac->add (AutoConnectPhysical, _("automatically to physical outputs"));
1290         oac->add (AutoConnectMaster, _("automatically to master outputs"));
1291         oac->add (ManualConnect, _("manually"));
1292
1293         add_option (_("Audio"), oac);
1294
1295         add_option (_("Audio"), new OptionEditorHeading (_("Denormals")));
1296
1297         add_option (_("Audio"),
1298              new BoolOption (
1299                      "denormal-protection",
1300                      _("Use DC bias to protect against denormals"),
1301                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_denormal_protection),
1302                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_denormal_protection)
1303                      ));
1304
1305         ComboOption<DenormalModel>* dm = new ComboOption<DenormalModel> (
1306                 "denormal-model",
1307                 _("Processor handling"),
1308                 sigc::mem_fun (*_rc_config, &RCConfiguration::get_denormal_model),
1309                 sigc::mem_fun (*_rc_config, &RCConfiguration::set_denormal_model)
1310                 );
1311
1312         dm->add (DenormalNone, _("no processor handling"));
1313
1314         FPU fpu;
1315
1316         if (fpu.has_flush_to_zero()) {
1317                 dm->add (DenormalFTZ, _("use FlushToZero"));
1318         }
1319
1320         if (fpu.has_denormals_are_zero()) {
1321                 dm->add (DenormalDAZ, _("use DenormalsAreZero"));
1322         }
1323
1324         if (fpu.has_flush_to_zero() && fpu.has_denormals_are_zero()) {
1325                 dm->add (DenormalFTZDAZ, _("use FlushToZero and DenormalsAreZerO"));
1326         }
1327
1328         add_option (_("Audio"), dm);
1329
1330         add_option (_("Audio"), new OptionEditorHeading (_("Plugins")));
1331
1332         add_option (_("Audio"),
1333              new BoolOption (
1334                      "plugins-stop-with-transport",
1335                      _("Stop plugins when the transport is stopped"),
1336                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_plugins_stop_with_transport),
1337                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_plugins_stop_with_transport)
1338                      ));
1339
1340         add_option (_("Audio"),
1341              new BoolOption (
1342                      "do-not-record-plugins",
1343                      _("Disable plugins during recording"),
1344                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_do_not_record_plugins),
1345                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_do_not_record_plugins)
1346                      ));
1347
1348         add_option (_("Audio"),
1349              new BoolOption (
1350                      "new-plugins-active",
1351                      _("Make new plugins active"),
1352                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_new_plugins_active),
1353                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_new_plugins_active)
1354                      ));
1355
1356         add_option (_("Audio"),
1357              new BoolOption (
1358                      "auto-analyse-audio",
1359                      _("Enable automatic analysis of audio"),
1360                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_auto_analyse_audio),
1361                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_auto_analyse_audio)
1362                      ));
1363
1364         /* MIDI CONTROL */
1365
1366         list<ComboOption<string>* > midi_combos;
1367
1368         midi_combos.push_back (new ComboOption<string> (
1369                                        "mtc-port-name",
1370                                        _("Send/Receive MTC via"),
1371                                        sigc::mem_fun (*_rc_config, &RCConfiguration::get_mtc_port_name),
1372                                        sigc::mem_fun (*_rc_config, &RCConfiguration::set_mtc_port_name)
1373                                        ));
1374
1375         midi_combos.push_back (new ComboOption<string> (
1376                                        "midi-clock-port-name",
1377                                        _("Send/Receive MIDI clock via"),
1378                                        sigc::mem_fun (*_rc_config, &RCConfiguration::get_midi_clock_port_name),
1379                                        sigc::mem_fun (*_rc_config, &RCConfiguration::set_midi_clock_port_name)
1380                                        ));
1381
1382         midi_combos.push_back (new ComboOption<string> (
1383                                        "mmc-port-name",
1384                                        _("Send/Receive MMC via"),
1385                                        sigc::mem_fun (*_rc_config, &RCConfiguration::get_mmc_port_name),
1386                                        sigc::mem_fun (*_rc_config, &RCConfiguration::set_mmc_port_name)
1387                                        ));
1388
1389         midi_combos.push_back (new ComboOption<string> (
1390                                        "midi-port-name",
1391                                        _("Send/Receive MIDI parameter control via"),
1392                                        sigc::mem_fun (*_rc_config, &RCConfiguration::get_midi_port_name),
1393                                        sigc::mem_fun (*_rc_config, &RCConfiguration::set_midi_port_name)
1394                                        ));
1395         
1396         add_option (_("MIDI control"), new MIDIPorts (_rc_config, midi_combos));
1397
1398         for (list<ComboOption<string>* >::iterator i = midi_combos.begin(); i != midi_combos.end(); ++i) {
1399                 add_option (_("MIDI control"), *i);
1400         }
1401
1402         add_option (_("MIDI control"),
1403                     new BoolOption (
1404                             "send-mtc",
1405                             _("Send MIDI Time Code"),
1406                             sigc::mem_fun (*_rc_config, &RCConfiguration::get_send_mtc),
1407                             sigc::mem_fun (*_rc_config, &RCConfiguration::set_send_mtc)
1408                             ));
1409
1410         add_option (_("MIDI control"),
1411                     new BoolOption (
1412                             "mmc-control",
1413                             _("Obey MIDI Machine Control commands"),
1414                             sigc::mem_fun (*_rc_config, &RCConfiguration::get_mmc_control),
1415                             sigc::mem_fun (*_rc_config, &RCConfiguration::set_mmc_control)
1416                             ));
1417
1418
1419         add_option (_("MIDI control"),
1420                     new BoolOption (
1421                             "send-mmc",
1422                             _("Send MIDI Machine Control commands"),
1423                             sigc::mem_fun (*_rc_config, &RCConfiguration::get_send_mmc),
1424                             sigc::mem_fun (*_rc_config, &RCConfiguration::set_send_mmc)
1425                             ));
1426
1427         add_option (_("MIDI control"),
1428                     new BoolOption (
1429                             "midi-feedback",
1430                             _("Send MIDI control feedback"),
1431                             sigc::mem_fun (*_rc_config, &RCConfiguration::get_midi_feedback),
1432                             sigc::mem_fun (*_rc_config, &RCConfiguration::set_midi_feedback)
1433                             ));
1434
1435         add_option (_("MIDI control"),
1436              new SpinOption<uint8_t> (
1437                      "mmc-receive-device-id",
1438                      _("Inbound MMC device ID"),
1439                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_mmc_receive_device_id),
1440                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_mmc_receive_device_id),
1441                      0, 128, 1, 10
1442                      ));
1443
1444         add_option (_("MIDI control"),
1445              new SpinOption<uint8_t> (
1446                      "mmc-send-device-id",
1447                      _("Outbound MMC device ID"),
1448                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_mmc_send_device_id),
1449                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_mmc_send_device_id),
1450                      0, 128, 1, 10
1451                      ));
1452
1453         add_option (_("MIDI control"),
1454              new SpinOption<int32_t> (
1455                      "initial-program-change",
1456                      _("Initial program change"),
1457                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_initial_program_change),
1458                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_initial_program_change),
1459                      -1, 65536, 1, 10
1460                      ));
1461
1462         /* CONTROL SURFACES */
1463
1464         add_option (_("Control surfaces"), new ControlSurfacesOptions (*this));
1465
1466         ComboOption<RemoteModel>* rm = new ComboOption<RemoteModel> (
1467                 "remote-model",
1468                 _("Control surface remote ID"),
1469                 sigc::mem_fun (*_rc_config, &RCConfiguration::get_remote_model),
1470                 sigc::mem_fun (*_rc_config, &RCConfiguration::set_remote_model)
1471                 );
1472
1473         rm->add (UserOrdered, _("assigned by user"));
1474         rm->add (MixerOrdered, _("follows order of mixer"));
1475         rm->add (EditorOrdered, _("follows order of editor"));
1476
1477         add_option (_("Control surfaces"), rm);
1478
1479         /* CLICK */
1480
1481         add_option (_("Click"), new ClickOptions (_rc_config, this));
1482
1483         /* KEYBOARD */
1484
1485         add_option (_("Keyboard"), new KeyboardOptions);
1486 }
1487
1488