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