83b3e498cd40deb87e0a0b731eb0a4cf0808f109
[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 BufferingOptions : public OptionEditorBox
717 {
718 public:
719         BufferingOptions (RCConfiguration* c) 
720                 : _rc_config (c)
721                 , _playback_adjustment (5, 1, 60, 1, 4)
722                 , _capture_adjustment (5, 1, 60, 1, 4)
723                 , _playback_slider (_playback_adjustment)
724                 , _capture_slider (_capture_adjustment)
725         {
726                 _playback_adjustment.set_value (_rc_config->get_audio_playback_buffer_seconds());
727
728                 Label* l = manage (new Label (_("Playback (seconds of buffering):")));
729                 l->set_name ("OptionsLabel");
730
731                 _playback_slider.set_update_policy (UPDATE_DISCONTINUOUS);
732                 HBox* h = manage (new HBox);
733                 h->set_spacing (4);
734                 h->pack_start (*l, false, false);
735                 h->pack_start (_playback_slider, true, true);
736
737                 _box->pack_start (*h, false, false);
738                 
739                 _capture_adjustment.set_value (_rc_config->get_audio_capture_buffer_seconds());
740
741                 l = manage (new Label (_("Recording (seconds of buffering):")));
742                 l->set_name ("OptionsLabel");
743
744                 _capture_slider.set_update_policy (UPDATE_DISCONTINUOUS);
745                 h = manage (new HBox);
746                 h->set_spacing (4);
747                 h->pack_start (*l, false, false);
748                 h->pack_start (_capture_slider, true, true);
749
750                 _box->pack_start (*h, false, false);
751                 
752                 _capture_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &BufferingOptions::capture_changed));
753                 _playback_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &BufferingOptions::playback_changed));
754         }
755
756         void parameter_changed (string const & p)
757         {
758                 if (p == "playback-buffer-seconds") {
759                         _playback_adjustment.set_value (_rc_config->get_audio_playback_buffer_seconds());
760                 } else if (p == "capture-buffer-seconds") {
761                         _capture_adjustment.set_value (_rc_config->get_audio_capture_buffer_seconds());
762                 }
763         }
764
765         void set_state_from_config ()
766         {
767                 parameter_changed ("playback-buffer-seconds");
768                 parameter_changed ("capture-buffer-seconds");
769         }
770
771 private:
772
773         void playback_changed ()
774         {
775                 _rc_config->set_audio_playback_buffer_seconds ((long) _playback_adjustment.get_value());
776         }
777
778         void capture_changed ()
779         {
780                 _rc_config->set_audio_capture_buffer_seconds ((long) _capture_adjustment.get_value());
781         }
782
783         RCConfiguration* _rc_config;
784         Adjustment _playback_adjustment;
785         Adjustment _capture_adjustment;
786         HScale _playback_slider;
787         HScale _capture_slider;
788 };
789
790 class ControlSurfacesOptions : public OptionEditorBox
791 {
792 public:
793         ControlSurfacesOptions (ArdourDialog& parent)
794                 : _parent (parent)
795         {
796                 _store = ListStore::create (_model);
797                 _view.set_model (_store);
798                 _view.append_column (_("Name"), _model.name);
799                 _view.get_column(0)->set_resizable (true);
800                 _view.get_column(0)->set_expand (true);
801                 _view.append_column_editable (_("Enabled"), _model.enabled);
802                 _view.append_column_editable (_("Feedback"), _model.feedback);
803
804                 _box->pack_start (_view, false, false);
805
806                 Label* label = manage (new Label);
807                 label->set_markup (string_compose (X_("<i>%1</i>"), _("Double-click on a name to edit settings for an enabled protocol")));
808
809                 _box->pack_start (*label, false, false);
810                 label->show ();
811                 
812                 _store->signal_row_changed().connect (sigc::mem_fun (*this, &ControlSurfacesOptions::model_changed));
813                 _view.signal_button_press_event().connect_notify (sigc::mem_fun(*this, &ControlSurfacesOptions::edit_clicked));
814         }
815
816         void parameter_changed (std::string const &)
817         {
818
819         }
820
821         void set_state_from_config ()
822         {
823                 _store->clear ();
824
825                 ControlProtocolManager& m = ControlProtocolManager::instance ();
826                 for (list<ControlProtocolInfo*>::iterator i = m.control_protocol_info.begin(); i != m.control_protocol_info.end(); ++i) {
827
828                         if (!(*i)->mandatory) {
829                                 TreeModel::Row r = *_store->append ();
830                                 r[_model.name] = (*i)->name;
831                                 r[_model.enabled] = ((*i)->protocol || (*i)->requested);
832                                 r[_model.feedback] = ((*i)->protocol && (*i)->protocol->get_feedback ());
833                                 r[_model.protocol_info] = *i;
834                         }
835                 }
836         }
837
838 private:
839
840         void model_changed (TreeModel::Path const &, TreeModel::iterator const & i)
841         {
842                 TreeModel::Row r = *i;
843
844                 ControlProtocolInfo* cpi = r[_model.protocol_info];
845                 if (!cpi) {
846                         return;
847                 }
848
849                 bool const was_enabled = (cpi->protocol != 0);
850                 bool const is_enabled = r[_model.enabled];
851
852                 if (was_enabled != is_enabled) {
853                         if (!was_enabled) {
854                                 ControlProtocolManager::instance().instantiate (*cpi);
855                         } else {
856                                 ControlProtocolManager::instance().teardown (*cpi);
857                         }
858                 }
859
860                 bool const was_feedback = (cpi->protocol && cpi->protocol->get_feedback ());
861                 bool const is_feedback = r[_model.feedback];
862
863                 if (was_feedback != is_feedback && cpi->protocol) {
864                         cpi->protocol->set_feedback (is_feedback);
865                 }
866         }
867
868         void edit_clicked (GdkEventButton* ev)
869         {
870                 if (ev->type != GDK_2BUTTON_PRESS) {
871                         return;
872                 }
873
874                 std::string name;
875                 ControlProtocolInfo* cpi;
876                 TreeModel::Row row;
877                 
878                 row = *(_view.get_selection()->get_selected());
879
880                 Window* win = row[_model.editor];
881                 if (win && !win->is_visible()) {
882                         win->present (); 
883                 } else {
884                         cpi = row[_model.protocol_info];
885                         
886                         if (cpi && cpi->protocol && cpi->protocol->has_editor ()) {
887                                 Box* box = (Box*) cpi->protocol->get_gui ();
888                                 if (box) {
889                                         string title = row[_model.name];
890                                         ArdourDialog* win = new ArdourDialog (_parent, title);
891                                         win->get_vbox()->pack_start (*box, false, false);
892                                         box->show ();
893                                         win->present ();
894                                         row[_model.editor] = win;
895                                 }
896                         }
897                 }
898         }
899
900         class ControlSurfacesModelColumns : public TreeModelColumnRecord
901         {
902         public:
903
904                 ControlSurfacesModelColumns ()
905                 {
906                         add (name);
907                         add (enabled);
908                         add (feedback);
909                         add (protocol_info);
910                         add (editor);
911                 }
912
913                 TreeModelColumn<string> name;
914                 TreeModelColumn<bool> enabled;
915                 TreeModelColumn<bool> feedback;
916                 TreeModelColumn<ControlProtocolInfo*> protocol_info;
917                 TreeModelColumn<Gtk::Window*> editor;
918         };
919
920         Glib::RefPtr<ListStore> _store;
921         ControlSurfacesModelColumns _model;
922         TreeView _view;
923         Gtk::Window& _parent;
924 };
925
926
927 RCOptionEditor::RCOptionEditor ()
928         : OptionEditor (Config, string_compose (_("%1 Preferences"), PROGRAM_NAME))
929         , _rc_config (Config)
930 {
931         /* MISC */
932
933         add_option (_("Misc"), new OptionEditorHeading (_("Metering")));
934
935         ComboOption<float>* mht = new ComboOption<float> (
936                 "meter-hold",
937                 _("Meter hold time"),
938                 sigc::mem_fun (*_rc_config, &RCConfiguration::get_meter_hold),
939                 sigc::mem_fun (*_rc_config, &RCConfiguration::set_meter_hold)
940                 );
941
942         mht->add (MeterHoldOff, _("off"));
943         mht->add (MeterHoldShort, _("short"));
944         mht->add (MeterHoldMedium, _("medium"));
945         mht->add (MeterHoldLong, _("long"));
946
947         add_option (_("Misc"), mht);
948
949         ComboOption<float>* mfo = new ComboOption<float> (
950                 "meter-falloff",
951                 _("Meter fall-off"),
952                 sigc::mem_fun (*_rc_config, &RCConfiguration::get_meter_falloff),
953                 sigc::mem_fun (*_rc_config, &RCConfiguration::set_meter_falloff)
954                 );
955
956         mfo->add (METER_FALLOFF_OFF, _("off"));
957         mfo->add (METER_FALLOFF_SLOWEST, _("slowest"));
958         mfo->add (METER_FALLOFF_SLOW, _("slow"));
959         mfo->add (METER_FALLOFF_MEDIUM, _("medium"));
960         mfo->add (METER_FALLOFF_FAST, _("fast"));
961         mfo->add (METER_FALLOFF_FASTER, _("faster"));
962         mfo->add (METER_FALLOFF_FASTEST, _("fastest"));
963
964         add_option (_("Misc"), mfo);
965
966         add_option (_("Misc"), new OptionEditorHeading (_("Undo")));
967
968         add_option (_("Misc"), new UndoOptions (_rc_config));
969
970         add_option (_("Misc"), new OptionEditorHeading (_("Misc")));
971
972 #ifndef GTKOSX
973         /* font scaling does nothing with GDK/Quartz */
974         add_option (_("Misc"), new FontScalingOptions (_rc_config));
975 #endif
976
977         add_option (_("Misc"),
978              new BoolOption (
979                      "verify-remove-last-capture",
980                      _("Verify removal of last capture"),
981                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_verify_remove_last_capture),
982                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_verify_remove_last_capture)
983                      ));
984
985         add_option (_("Misc"),
986              new BoolOption (
987                      "periodic-safety-backups",
988                      _("Make periodic backups of the session file"),
989                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_periodic_safety_backups),
990                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_periodic_safety_backups)
991                      ));
992
993         add_option (_("Misc"),
994              new BoolOption (
995                      "sync-all-route-ordering",
996                      _("Syncronise editor and mixer track order"),
997                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_sync_all_route_ordering),
998                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_sync_all_route_ordering)
999                      ));
1000
1001         add_option (_("Misc"),
1002              new BoolOption (
1003                      "only-copy-imported-files",
1004                      _("Always copy imported files"),
1005                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_only_copy_imported_files),
1006                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_only_copy_imported_files)
1007                      ));
1008
1009         add_option (_("Misc"),
1010              new BoolOption (
1011                      "default-narrow_ms",
1012                      _("Use narrow mixer strips"),
1013                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_default_narrow_ms),
1014                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_default_narrow_ms)
1015                      ));
1016
1017         add_option (_("Misc"),
1018              new BoolOption (
1019                      "name-new-markers",
1020                      _("Name new markers"),
1021                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_name_new_markers),
1022                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_name_new_markers)
1023                      ));
1024
1025         add_option (_("Misc"), new OptionEditorHeading (_("Click")));
1026         
1027         add_option (_("Misc"), new ClickOptions (_rc_config, this));
1028
1029         /* TRANSPORT */
1030
1031         add_option (_("Transport"),
1032              new BoolOption (
1033                      "latched-record-enable",
1034                      _("Keep record-enable engaged on stop"),
1035                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_latched_record_enable),
1036                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_latched_record_enable)
1037                      ));
1038
1039         add_option (_("Transport"),
1040              new BoolOption (
1041                      "stop-recording-on-xrun",
1042                      _("Stop recording when an xrun occurs"),
1043                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_stop_recording_on_xrun),
1044                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_stop_recording_on_xrun)
1045                      ));
1046
1047         add_option (_("Transport"),
1048              new BoolOption (
1049                      "create-xrun-marker",
1050                      _("Create markers where xruns occur"),
1051                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_create_xrun_marker),
1052                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_create_xrun_marker)
1053                      ));
1054
1055         add_option (_("Transport"),
1056              new BoolOption (
1057                      "stop-at-session-end",
1058                      _("Stop at the end of the session"),
1059                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_stop_at_session_end),
1060                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_stop_at_session_end)
1061                      ));
1062
1063         add_option (_("Transport"),
1064              new BoolOption (
1065                      "primary-clock-delta-edit-cursor",
1066                      _("Primary clock delta to edit cursor"),
1067                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_primary_clock_delta_edit_cursor),
1068                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_primary_clock_delta_edit_cursor)
1069                      ));
1070
1071         add_option (_("Transport"),
1072              new BoolOption (
1073                      "secondary-clock-delta-edit-cursor",
1074                      _("Secondary clock delta to edit cursor"),
1075                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_secondary_clock_delta_edit_cursor),
1076                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_secondary_clock_delta_edit_cursor)
1077                      ));
1078
1079         add_option (_("Transport"),
1080              new BoolOption (
1081                      "disable-disarm-during-roll",
1082                      _("Disable per-track record disarm while rolling"),
1083                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_disable_disarm_during_roll),
1084                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_disable_disarm_during_roll)
1085                      ));
1086
1087         add_option (_("Transport"),
1088              new BoolOption (
1089                      "quieten_at_speed",
1090                      _("12dB gain reduction during fast-forward and fast-rewind"),
1091                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_quieten_at_speed),
1092                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_quieten_at_speed)
1093                      ));
1094
1095         /* EDITOR */
1096
1097         add_option (_("Editor"),
1098              new BoolOption (
1099                      "link-region-and-track-selection",
1100                      _("Link selection of regions and tracks"),
1101                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_link_region_and_track_selection),
1102                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_link_region_and_track_selection)
1103                      ));
1104
1105         add_option (_("Editor"),
1106              new BoolOption (
1107                      "automation-follows-regions",
1108                      _("Move relevant automation when regions are moved"),
1109                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_automation_follows_regions),
1110                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_automation_follows_regions)
1111                      ));
1112
1113         add_option (_("Editor"),
1114              new BoolOption (
1115                      "show-track-meters",
1116                      _("Show meters on tracks in the editor"),
1117                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_show_track_meters),
1118                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_show_track_meters)
1119                      ));
1120
1121         add_option (_("Editor"),
1122              new BoolOption (
1123                      "use-overlap-equivalency",
1124                      _("Use overlap equivalency for regions"),
1125                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_use_overlap_equivalency),
1126                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_use_overlap_equivalency)
1127                      ));
1128
1129         add_option (_("Editor"),
1130              new BoolOption (
1131                      "rubberbanding-snaps-to-grid",
1132                      _("Make rubberband selection rectangle snap to the grid"),
1133                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_rubberbanding_snaps_to_grid),
1134                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_rubberbanding_snaps_to_grid)
1135                      ));
1136
1137         add_option (_("Editor"),
1138              new BoolOption (
1139                      "show-waveforms",
1140                      _("Show waveforms in regions"),
1141                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_show_waveforms),
1142                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_show_waveforms)
1143                      ));
1144
1145         ComboOption<WaveformScale>* wfs = new ComboOption<WaveformScale> (
1146                 "waveform-scale",
1147                 _("Waveform scale"),
1148                 sigc::mem_fun (*_rc_config, &RCConfiguration::get_waveform_scale),
1149                 sigc::mem_fun (*_rc_config, &RCConfiguration::set_waveform_scale)
1150                 );
1151
1152         wfs->add (Linear, _("linear"));
1153         wfs->add (Logarithmic, _("logarithmic"));
1154
1155         add_option (_("Editor"), wfs);
1156
1157         ComboOption<WaveformShape>* wfsh = new ComboOption<WaveformShape> (
1158                 "waveform-shape",
1159                 _("Waveform shape"),
1160                 sigc::mem_fun (*_rc_config, &RCConfiguration::get_waveform_shape),
1161                 sigc::mem_fun (*_rc_config, &RCConfiguration::set_waveform_shape)
1162                 );
1163
1164         wfsh->add (Traditional, _("traditional"));
1165         wfsh->add (Rectified, _("rectified"));
1166
1167         add_option (_("Editor"), wfsh);
1168
1169         add_option (_("Editor"),
1170              new BoolOption (
1171                      "show-waveforms-while-recording",
1172                      _("Show waveforms for audio while it is being recorded"),
1173                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_show_waveforms_while_recording),
1174                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_show_waveforms_while_recording)
1175                      ));
1176
1177         /* AUDIO */
1178
1179         add_option (_("Audio"), new OptionEditorHeading (_("Buffering")));
1180
1181         add_option (_("Audio"), new BufferingOptions (_rc_config));
1182
1183         add_option (_("Audio"), new OptionEditorHeading (_("Monitoring")));
1184
1185         add_option (_("Audio"),
1186              new BoolOption (
1187                      "use-monitor-bus",
1188                      _("Use a monitor bus (allows AFL/PFL and more control)"),
1189                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_use_monitor_bus),
1190                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_use_monitor_bus)
1191                      ));
1192
1193         ComboOption<MonitorModel>* mm = new ComboOption<MonitorModel> (
1194                 "monitoring-model",
1195                 _("Monitoring handled by"),
1196                 sigc::mem_fun (*_rc_config, &RCConfiguration::get_monitoring_model),
1197                 sigc::mem_fun (*_rc_config, &RCConfiguration::set_monitoring_model)
1198                 );
1199
1200 #ifndef __APPLE__
1201         /* no JACK monitoring on CoreAudio */
1202         if (AudioEngine::instance()->can_request_hardware_monitoring()) {
1203                 mm->add (HardwareMonitoring, _("JACK"));
1204         }
1205 #endif
1206         mm->add (SoftwareMonitoring, _("ardour"));
1207         mm->add (ExternalMonitoring, _("audio hardware"));
1208
1209         add_option (_("Audio"), mm);
1210
1211         add_option (_("Audio"),
1212              new BoolOption (
1213                      "tape-machine-mode",
1214                      _("Tape machine mode"),
1215                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_tape_machine_mode),
1216                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_tape_machine_mode)
1217                      ));
1218
1219         add_option (_("Audio"), new OptionEditorHeading (_("Connection of tracks and busses")));
1220
1221         add_option (_("Audio"),
1222                     new BoolOption (
1223                             "auto-connect-standard-busses",
1224                             _("Auto-connect master/monitor busses"),
1225                             sigc::mem_fun (*_rc_config, &RCConfiguration::get_auto_connect_standard_busses),
1226                             sigc::mem_fun (*_rc_config, &RCConfiguration::set_auto_connect_standard_busses)
1227                             ));
1228
1229         ComboOption<AutoConnectOption>* iac = new ComboOption<AutoConnectOption> (
1230                 "input-auto-connect",
1231                 _("Connect track and bus inputs"),
1232                 sigc::mem_fun (*_rc_config, &RCConfiguration::get_input_auto_connect),
1233                 sigc::mem_fun (*_rc_config, &RCConfiguration::set_input_auto_connect)
1234                 );
1235
1236         iac->add (AutoConnectPhysical, _("automatically to physical inputs"));
1237         iac->add (ManualConnect, _("manually"));
1238
1239         add_option (_("Audio"), iac);
1240
1241         ComboOption<AutoConnectOption>* oac = new ComboOption<AutoConnectOption> (
1242                 "output-auto-connect",
1243                 _("Connect track and bus outputs"),
1244                 sigc::mem_fun (*_rc_config, &RCConfiguration::get_output_auto_connect),
1245                 sigc::mem_fun (*_rc_config, &RCConfiguration::set_output_auto_connect)
1246                 );
1247
1248         oac->add (AutoConnectPhysical, _("automatically to physical outputs"));
1249         oac->add (AutoConnectMaster, _("automatically to master outputs"));
1250         oac->add (ManualConnect, _("manually"));
1251
1252         add_option (_("Audio"), oac);
1253
1254         add_option (_("Audio"), new OptionEditorHeading (_("Denormals")));
1255
1256         add_option (_("Audio"),
1257              new BoolOption (
1258                      "denormal-protection",
1259                      _("Use DC bias to protect against denormals"),
1260                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_denormal_protection),
1261                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_denormal_protection)
1262                      ));
1263
1264         ComboOption<DenormalModel>* dm = new ComboOption<DenormalModel> (
1265                 "denormal-model",
1266                 _("Processor handling"),
1267                 sigc::mem_fun (*_rc_config, &RCConfiguration::get_denormal_model),
1268                 sigc::mem_fun (*_rc_config, &RCConfiguration::set_denormal_model)
1269                 );
1270
1271         dm->add (DenormalNone, _("no processor handling"));
1272
1273         FPU fpu;
1274
1275         if (fpu.has_flush_to_zero()) {
1276                 dm->add (DenormalFTZ, _("use FlushToZero"));
1277         }
1278
1279         if (fpu.has_denormals_are_zero()) {
1280                 dm->add (DenormalDAZ, _("use DenormalsAreZero"));
1281         }
1282
1283         if (fpu.has_flush_to_zero() && fpu.has_denormals_are_zero()) {
1284                 dm->add (DenormalFTZDAZ, _("use FlushToZero and DenormalsAreZerO"));
1285         }
1286
1287         add_option (_("Audio"), dm);
1288
1289         add_option (_("Audio"), new OptionEditorHeading (_("Plugins")));
1290
1291         add_option (_("Audio"),
1292              new BoolOption (
1293                      "plugins-stop-with-transport",
1294                      _("Stop plugins when the transport is stopped"),
1295                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_plugins_stop_with_transport),
1296                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_plugins_stop_with_transport)
1297                      ));
1298
1299         add_option (_("Audio"),
1300              new BoolOption (
1301                      "do-not-record-plugins",
1302                      _("Disable plugins during recording"),
1303                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_do_not_record_plugins),
1304                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_do_not_record_plugins)
1305                      ));
1306
1307         add_option (_("Audio"),
1308              new BoolOption (
1309                      "new-plugins-active",
1310                      _("Make new plugins active"),
1311                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_new_plugins_active),
1312                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_new_plugins_active)
1313                      ));
1314
1315         add_option (_("Audio"),
1316              new BoolOption (
1317                      "auto-analyse-audio",
1318                      _("Enable automatic analysis of audio"),
1319                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_auto_analyse_audio),
1320                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_auto_analyse_audio)
1321                      ));
1322
1323         /* SOLO AND MUTE */
1324
1325         add_option (_("Solo / mute"),
1326              new FaderOption (
1327                      "solo-mute-gain",
1328                      _("Solo mute cut (dB)"),
1329                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_solo_mute_gain),
1330                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_solo_mute_gain)
1331                      ));
1332                      
1333         add_option (_("Solo / mute"),
1334              new BoolOption (
1335                      "solo-control-is-listen-control",
1336                      _("Solo controls are Listen controls"),
1337                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_solo_control_is_listen_control),
1338                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_solo_control_is_listen_control)
1339                      ));
1340
1341         ComboOption<ListenPosition>* lp = new ComboOption<ListenPosition> (
1342                 "listen-position",
1343                 _("Listen Position"),
1344                 sigc::mem_fun (*_rc_config, &RCConfiguration::get_listen_position),
1345                 sigc::mem_fun (*_rc_config, &RCConfiguration::set_listen_position)
1346                 );
1347
1348         lp->add (AfterFaderListen, _("after-fader listen"));
1349         lp->add (PreFaderListen, _("pre-fader listen"));
1350
1351         add_option (_("Solo / mute"), lp);
1352
1353         add_option (_("Solo / mute"),
1354              new BoolOption (
1355                      "exclusive-solo",
1356                      _("Exclusive solo"),
1357                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_exclusive_solo),
1358                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_exclusive_solo)
1359                      ));
1360
1361         add_option (_("Solo / mute"),
1362              new BoolOption (
1363                      "show-solo-mutes",
1364                      _("Show solo muting"),
1365                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_show_solo_mutes),
1366                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_show_solo_mutes)
1367                      ));
1368
1369         add_option (_("Solo / mute"),
1370              new BoolOption (
1371                      "solo-mute-override",
1372                      _("Soloing overrides muting"),
1373                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_solo_mute_override),
1374                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_solo_mute_override)
1375                      ));
1376
1377         add_option (_("Solo / mute"), new OptionEditorHeading (_("Default track / bus muting options")));
1378         
1379         add_option (_("Solo / mute"),
1380              new BoolOption (
1381                      "mute-affects-pre-fader",
1382                      _("Mute affects pre-fader sends"),
1383                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_mute_affects_pre_fader),
1384                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_mute_affects_pre_fader)
1385                      ));
1386
1387         add_option (_("Solo / mute"),
1388              new BoolOption (
1389                      "mute-affects-post-fader",
1390                      _("Mute affects post-fader sends"),
1391                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_mute_affects_post_fader),
1392                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_mute_affects_post_fader)
1393                      ));
1394         
1395         add_option (_("Solo / mute"),
1396              new BoolOption (
1397                      "mute-affects-control-outs",
1398                      _("Mute affects control outputs"),
1399                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_mute_affects_control_outs),
1400                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_mute_affects_control_outs)
1401                      ));
1402         
1403         add_option (_("Solo / mute"),
1404              new BoolOption (
1405                      "mute-affects-main-outs",
1406                      _("Mute affects main outputs"),
1407                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_mute_affects_main_outs),
1408                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_mute_affects_main_outs)
1409                      ));
1410
1411         /* MIDI CONTROL */
1412
1413         list<ComboOption<string>* > midi_combos;
1414
1415         midi_combos.push_back (new ComboOption<string> (
1416                                        "mtc-port-name",
1417                                        _("Send/Receive MTC via"),
1418                                        sigc::mem_fun (*_rc_config, &RCConfiguration::get_mtc_port_name),
1419                                        sigc::mem_fun (*_rc_config, &RCConfiguration::set_mtc_port_name)
1420                                        ));
1421
1422         midi_combos.push_back (new ComboOption<string> (
1423                                        "midi-clock-port-name",
1424                                        _("Send/Receive MIDI clock via"),
1425                                        sigc::mem_fun (*_rc_config, &RCConfiguration::get_midi_clock_port_name),
1426                                        sigc::mem_fun (*_rc_config, &RCConfiguration::set_midi_clock_port_name)
1427                                        ));
1428
1429         midi_combos.push_back (new ComboOption<string> (
1430                                        "mmc-port-name",
1431                                        _("Send/Receive MMC via"),
1432                                        sigc::mem_fun (*_rc_config, &RCConfiguration::get_mmc_port_name),
1433                                        sigc::mem_fun (*_rc_config, &RCConfiguration::set_mmc_port_name)
1434                                        ));
1435
1436         midi_combos.push_back (new ComboOption<string> (
1437                                        "midi-port-name",
1438                                        _("Send/Receive MIDI parameter control via"),
1439                                        sigc::mem_fun (*_rc_config, &RCConfiguration::get_midi_port_name),
1440                                        sigc::mem_fun (*_rc_config, &RCConfiguration::set_midi_port_name)
1441                                        ));
1442         
1443         add_option (_("MIDI control"), new MIDIPorts (_rc_config, midi_combos));
1444
1445         for (list<ComboOption<string>* >::iterator i = midi_combos.begin(); i != midi_combos.end(); ++i) {
1446                 add_option (_("MIDI control"), *i);
1447         }
1448
1449         add_option (_("MIDI control"),
1450                     new BoolOption (
1451                             "send-mtc",
1452                             _("Send MIDI Time Code"),
1453                             sigc::mem_fun (*_rc_config, &RCConfiguration::get_send_mtc),
1454                             sigc::mem_fun (*_rc_config, &RCConfiguration::set_send_mtc)
1455                             ));
1456
1457         add_option (_("MIDI control"),
1458                     new BoolOption (
1459                             "mmc-control",
1460                             _("Obey MIDI Machine Control commands"),
1461                             sigc::mem_fun (*_rc_config, &RCConfiguration::get_mmc_control),
1462                             sigc::mem_fun (*_rc_config, &RCConfiguration::set_mmc_control)
1463                             ));
1464
1465
1466         add_option (_("MIDI control"),
1467                     new BoolOption (
1468                             "send-mmc",
1469                             _("Send MIDI Machine Control commands"),
1470                             sigc::mem_fun (*_rc_config, &RCConfiguration::get_send_mmc),
1471                             sigc::mem_fun (*_rc_config, &RCConfiguration::set_send_mmc)
1472                             ));
1473
1474         add_option (_("MIDI control"),
1475                     new BoolOption (
1476                             "midi-feedback",
1477                             _("Send MIDI control feedback"),
1478                             sigc::mem_fun (*_rc_config, &RCConfiguration::get_midi_feedback),
1479                             sigc::mem_fun (*_rc_config, &RCConfiguration::set_midi_feedback)
1480                             ));
1481
1482         add_option (_("MIDI control"),
1483              new SpinOption<uint8_t> (
1484                      "mmc-receive-device-id",
1485                      _("Inbound MMC device ID"),
1486                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_mmc_receive_device_id),
1487                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_mmc_receive_device_id),
1488                      0, 128, 1, 10
1489                      ));
1490
1491         add_option (_("MIDI control"),
1492              new SpinOption<uint8_t> (
1493                      "mmc-send-device-id",
1494                      _("Outbound MMC device ID"),
1495                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_mmc_send_device_id),
1496                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_mmc_send_device_id),
1497                      0, 128, 1, 10
1498                      ));
1499
1500         add_option (_("MIDI control"),
1501              new SpinOption<int32_t> (
1502                      "initial-program-change",
1503                      _("Initial program change"),
1504                      sigc::mem_fun (*_rc_config, &RCConfiguration::get_initial_program_change),
1505                      sigc::mem_fun (*_rc_config, &RCConfiguration::set_initial_program_change),
1506                      -1, 65536, 1, 10
1507                      ));
1508
1509         /* CONTROL SURFACES */
1510
1511         add_option (_("Control surfaces"), new ControlSurfacesOptions (*this));
1512
1513         ComboOption<RemoteModel>* rm = new ComboOption<RemoteModel> (
1514                 "remote-model",
1515                 _("Control surface remote ID"),
1516                 sigc::mem_fun (*_rc_config, &RCConfiguration::get_remote_model),
1517                 sigc::mem_fun (*_rc_config, &RCConfiguration::set_remote_model)
1518                 );
1519
1520         rm->add (UserOrdered, _("assigned by user"));
1521         rm->add (MixerOrdered, _("follows order of mixer"));
1522         rm->add (EditorOrdered, _("follows order of editor"));
1523
1524         add_option (_("Control surfaces"), rm);
1525
1526         /* KEYBOARD */
1527
1528         add_option (_("Keyboard"), new KeyboardOptions);
1529 }
1530
1531