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