Merge with 2.0-ongoing R3071.
[ardour.git] / gtk2_ardour / option_editor.cc
1 /*
2     Copyright (C) 2001-2006 Paul Davis 
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <pbd/whitespace.h>
21
22 #include <ardour/ardour.h>
23 #include <ardour/session.h>
24 #include <ardour/audioengine.h>
25 #include <ardour/configuration.h>
26 #include <ardour/auditioner.h>
27 #include <ardour/sndfilesource.h>
28 #include <ardour/crossfade.h>
29 #include <midi++/manager.h>
30 #include <midi++/factory.h>
31 #include <gtkmm2ext/stop_signal.h>
32 #include <gtkmm2ext/utils.h>
33 #include <gtkmm2ext/window_title.h>
34
35 #include "public_editor.h"
36 #include "keyboard.h"
37 #include "mixer_ui.h"
38 #include "ardour_ui.h"
39 #include "io_selector.h"
40 #include "gain_meter.h"
41 #include "sfdb_ui.h"
42 #include "utils.h"
43 #include "editing.h"
44 #include "option_editor.h"
45 #include "midi_port_dialog.h"
46 #include "gui_thread.h"
47
48 #include "i18n.h"
49
50 using namespace ARDOUR;
51 using namespace PBD;
52 using namespace Gtk;
53 using namespace Editing;
54 using namespace Gtkmm2ext;
55 using namespace std;
56
57 static vector<string> positional_sync_strings;
58
59 OptionEditor::OptionEditor (ARDOUR_UI& uip, PublicEditor& ed, Mixer_UI& mixui)
60         : ArdourDialog ("options editor", false),
61           ui (uip),
62           editor (ed),
63           mixer (mixui),
64
65           /* Paths */
66           path_table (11, 2),
67
68           /* misc */
69
70           short_xfade_adjustment (0, 1.0, 500.0, 5.0, 100.0),
71           short_xfade_slider (short_xfade_adjustment),
72           destructo_xfade_adjustment (1.0, 1.0, 500.0, 1.0, 100.0),
73           destructo_xfade_slider (destructo_xfade_adjustment),
74           history_depth (20, -1, 100, 1.0, 10.0),
75           saved_history_depth (20, 0, 100, 1.0, 10.0),
76           history_depth_spinner (history_depth),
77           saved_history_depth_spinner (saved_history_depth),
78           limit_history_button (_("Limit undo history")),
79           save_history_button (_("Save undo history")),
80
81           /* Sync */
82
83           smpte_offset_clock (X_("smpteoffset"), false, X_("SMPTEOffsetClock"), true, true),
84           smpte_offset_negative_button (_("SMPTE offset is negative")),
85           synced_timecode_button (_("Timecode source is sample-clock synced")),
86
87           /* MIDI */
88
89           midi_port_table (4, 11),
90           mmc_receive_device_id_adjustment (0.0, 0.0, (double) 0x7f, 1.0, 16.0),
91           mmc_receive_device_id_spinner (mmc_receive_device_id_adjustment),
92           mmc_send_device_id_adjustment (0.0, 0.0, (double) 0x7f, 1.0, 16.0),
93           mmc_send_device_id_spinner (mmc_send_device_id_adjustment),
94           add_midi_port_button (_("Add new MIDI port")),
95
96           /* Click */
97
98           click_table (2, 3),
99           click_browse_button (_("Browse")),
100           click_emphasis_browse_button (_("Browse")),
101
102           /* kbd/mouse */
103
104           keyboard_mouse_table (4, 4),
105           delete_button_adjustment (3, 1, 5),
106           delete_button_spin (delete_button_adjustment),
107           edit_button_adjustment (3, 1, 5),
108           edit_button_spin (edit_button_adjustment)
109           
110 {
111         using namespace Notebook_Helpers;
112
113         click_io_selector = 0;
114         auditioner_io_selector = 0;
115         session = 0;
116         
117         WindowTitle title(Glib::get_application_name());
118         title += _("Preferences");
119         set_title(title.get_string());
120
121         set_default_size (300, 300);
122         set_wmclass (X_("ardour_preferences"), "Ardour");
123
124         set_name ("Preferences");
125         add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
126         
127         VBox *vbox = get_vbox();
128         set_border_width (3);
129
130         vbox->set_spacing (4);
131         vbox->pack_start(notebook);
132
133         signal_delete_event().connect (mem_fun(*this, &OptionEditor::wm_close));
134
135         notebook.set_show_tabs (true);
136         notebook.set_show_border (true);
137         notebook.set_name ("OptionsNotebook");
138
139         setup_sync_options();
140         setup_path_options();
141         setup_misc_options ();
142         setup_keyboard_options ();
143         setup_auditioner_editor ();
144
145         notebook.pages().push_back (TabElem (sync_packer, _("Sync")));
146         notebook.pages().push_back (TabElem (path_table, _("Paths/Files")));
147         notebook.pages().push_back (TabElem (keyboard_mouse_table, _("Kbd/Mouse")));
148         notebook.pages().push_back (TabElem (click_packer, _("Click")));
149         notebook.pages().push_back (TabElem (audition_packer, _("Audition")));
150         notebook.pages().push_back (TabElem (misc_packer, _("Misc")));
151
152         setup_midi_options ();
153         notebook.pages().push_back (TabElem (midi_packer, _("MIDI")));
154
155         set_session (0);
156         show_all_children();
157
158         Config->map_parameters (mem_fun (*this, &OptionEditor::parameter_changed));
159         Config->ParameterChanged.connect (mem_fun (*this, &OptionEditor::parameter_changed));
160 }
161
162 void
163 OptionEditor::set_session (Session *s)
164 {
165         clear_click_editor ();
166         clear_auditioner_editor ();
167
168         click_path_entry.set_text ("");
169         click_emphasis_path_entry.set_text ("");
170         session_raid_entry.set_text ("");
171
172         click_path_entry.set_sensitive (false);
173         click_emphasis_path_entry.set_sensitive (false);
174         session_raid_entry.set_sensitive (false);
175
176         short_xfade_slider.set_sensitive (false);
177         smpte_offset_negative_button.set_sensitive (false);
178
179         smpte_offset_clock.set_session (s);
180
181         if ((session = s) == 0) {
182                 return;
183         }
184
185         click_path_entry.set_sensitive (true);
186         click_emphasis_path_entry.set_sensitive (true);
187         session_raid_entry.set_sensitive (true);
188         short_xfade_slider.set_sensitive (true);
189         smpte_offset_negative_button.set_sensitive (true);
190
191         smpte_offset_clock.set_session (s);
192         smpte_offset_clock.set (s->smpte_offset (), true);
193
194         smpte_offset_negative_button.set_active (session->smpte_offset_negative());
195
196         redisplay_midi_ports ();
197
198         setup_click_editor ();
199         connect_audition_editor ();
200
201         short_xfade_adjustment.set_value ((Crossfade::short_xfade_length() / (float) session->frame_rate()) * 1000.0);
202
203         add_session_paths ();
204 }
205
206 OptionEditor::~OptionEditor ()
207 {
208 }
209
210 void
211 OptionEditor::setup_path_options()
212 {
213         Gtk::Label* label;
214
215         path_table.set_homogeneous (false);
216         path_table.set_border_width (12);
217         path_table.set_row_spacings (5);
218
219         session_raid_entry.set_name ("OptionsEntry");
220
221         session_raid_entry.signal_activate().connect (mem_fun(*this, &OptionEditor::raid_path_changed));
222
223         label = manage(new Label(_("session RAID path")));
224         label->set_name ("OptionsLabel");
225         path_table.attach (*label, 0, 1, 0, 1, FILL|EXPAND, FILL);
226         path_table.attach (session_raid_entry, 1, 3, 0, 1, Gtk::FILL|Gtk::EXPAND, FILL);
227
228         path_table.show_all();
229 }
230
231 void
232 OptionEditor::add_session_paths ()
233 {
234         click_path_entry.set_sensitive (true);
235         click_emphasis_path_entry.set_sensitive (true);
236         session_raid_entry.set_sensitive (true);
237
238         if (Config->get_click_sound().empty()) {
239                 click_path_entry.set_text (_("internal"));
240         } else {
241                 click_path_entry.set_text (Config->get_click_sound());
242         }
243
244         if (Config->get_click_emphasis_sound().empty()) {
245                 click_emphasis_path_entry.set_text (_("internal"));
246         } else {
247                 click_emphasis_path_entry.set_text (Config->get_click_emphasis_sound());
248         }
249
250         session_raid_entry.set_text(session->raid_path());
251 }
252
253 void
254 OptionEditor::setup_misc_options ()
255 {
256         Gtk::HBox* hbox;
257         
258         Label* label = manage (new Label (_("Short crossfade length (msecs)")));
259         label->set_name ("OptionsLabel");
260         
261         hbox = manage (new HBox);
262         hbox->set_border_width (5);
263         hbox->set_spacing (10);
264         hbox->pack_start (*label, false, false);
265         hbox->pack_start (short_xfade_slider, true, true);
266         misc_packer.pack_start (*hbox, false, false);
267
268         short_xfade_adjustment.signal_value_changed().connect (mem_fun(*this, &OptionEditor::short_xfade_adjustment_changed));
269
270         label = manage (new Label (_("Destructive crossfade length (msecs)")));
271         label->set_name ("OptionsLabel");
272         
273         hbox = manage (new HBox);
274         hbox->set_border_width (5);
275         hbox->set_spacing (10);
276         hbox->pack_start (*label, false, false);
277         hbox->pack_start (destructo_xfade_slider, true, true);
278         misc_packer.pack_start (*hbox, false, false);
279         
280
281         destructo_xfade_adjustment.signal_value_changed().connect (mem_fun(*this, &OptionEditor::destructo_xfade_adjustment_changed));
282
283         hbox = manage (new HBox);
284         hbox->set_border_width (5);
285         hbox->set_spacing (10);
286         hbox->pack_start (limit_history_button, false, false);
287         misc_packer.pack_start (*hbox, false, false);
288
289         label = manage (new Label (_("History depth (commands)")));
290         label->set_name ("OptionsLabel");
291
292         hbox = manage (new HBox);
293         hbox->set_border_width (5);
294         hbox->set_spacing (10);
295         hbox->pack_start (*label, false, false);
296         hbox->pack_start (history_depth_spinner, false, false);
297         misc_packer.pack_start (*hbox, false, false);
298
299         history_depth.signal_value_changed().connect (mem_fun (*this, &OptionEditor::history_depth_changed));
300         saved_history_depth.signal_value_changed().connect (mem_fun (*this, &OptionEditor::saved_history_depth_changed));
301         save_history_button.signal_toggled().connect (mem_fun (*this, &OptionEditor::save_history_toggled));
302         limit_history_button.signal_toggled().connect (mem_fun (*this, &OptionEditor::limit_history_toggled));
303
304         hbox = manage (new HBox);
305         hbox->set_border_width (5);
306         hbox->set_spacing (10);
307         hbox->pack_start (save_history_button, false, false);
308         misc_packer.pack_start (*hbox, false, false);
309
310         label = manage (new Label (_("Saved history depth (commands)")));
311         label->set_name ("OptionsLabel");
312
313         hbox = manage (new HBox);
314         hbox->set_border_width (5);
315         hbox->set_spacing (10);
316         hbox->pack_start (*label, false, false);
317         hbox->pack_start (saved_history_depth_spinner, false, false);
318         misc_packer.pack_start (*hbox, false, false);
319         
320         short_xfade_slider.set_update_policy (UPDATE_DISCONTINUOUS);
321         destructo_xfade_slider.set_update_policy (UPDATE_DISCONTINUOUS);
322
323         destructo_xfade_adjustment.set_value (Config->get_destructive_xfade_msecs());
324
325         misc_packer.show_all ();
326 }
327
328 void
329 OptionEditor::limit_history_toggled ()
330 {
331         bool x = limit_history_button.get_active();
332         
333         if (!x) {
334                 Config->set_history_depth (0);
335                 history_depth_spinner.set_sensitive (false);
336         } else {
337                 if (Config->get_history_depth() == 0) {
338                         /* get back to a sane default */
339                         Config->set_history_depth (20);
340                 }
341                 history_depth_spinner.set_sensitive (true);
342         }
343 }
344
345 void
346 OptionEditor::save_history_toggled ()
347 {
348         bool x = save_history_button.get_active();
349
350         if (x != Config->get_save_history()) {
351                 Config->set_save_history (x);
352                 saved_history_depth_spinner.set_sensitive (x);
353         }
354 }
355
356 void
357 OptionEditor::history_depth_changed()
358 {
359         Config->set_history_depth ((int32_t) floor (history_depth.get_value()));
360 }
361
362 void
363 OptionEditor::saved_history_depth_changed()
364 {
365         Config->set_saved_history_depth ((int32_t) floor (saved_history_depth.get_value()));
366 }
367
368 void
369 OptionEditor::short_xfade_adjustment_changed ()
370 {
371         if (session) {
372                 float val = short_xfade_adjustment.get_value();
373                 
374                 /* val is in msecs */
375                 
376                 Crossfade::set_short_xfade_length ((nframes_t) floor (session->frame_rate() * (val / 1000.0)));
377         }
378 }
379
380 void
381 OptionEditor::destructo_xfade_adjustment_changed ()
382 {
383         float val = destructo_xfade_adjustment.get_value();
384
385         /* val is in msecs */
386
387         
388         Config->set_destructive_xfade_msecs ((uint32_t) floor (val));
389
390         if (session) {
391                 SndFileSource::setup_standard_crossfades (session->frame_rate());
392         } 
393 }
394
395 void
396 OptionEditor::setup_sync_options ()
397 {
398         HBox* hbox;
399         vector<string> dumb;
400
401         smpte_offset_clock.set_mode (AudioClock::SMPTE);
402         smpte_offset_clock.ValueChanged.connect (mem_fun(*this, &OptionEditor::smpte_offset_chosen));
403         
404         smpte_offset_negative_button.set_name ("OptionEditorToggleButton");
405
406         smpte_offset_negative_button.unset_flags (Gtk::CAN_FOCUS);
407
408         Label *smpte_offset_label = manage (new Label (_("SMPTE Offset")));
409         smpte_offset_label->set_name("OptionsLabel");
410         
411         hbox = manage (new HBox);
412         hbox->set_border_width (5);
413         hbox->set_spacing (10);
414         hbox->pack_start (*smpte_offset_label, false, false);
415         hbox->pack_start (smpte_offset_clock, false, false);
416         hbox->pack_start (smpte_offset_negative_button, false, false);
417
418         sync_packer.pack_start (*hbox, false, false);
419         sync_packer.pack_start (synced_timecode_button, false, false);
420
421         smpte_offset_negative_button.signal_clicked().connect (mem_fun(*this, &OptionEditor::smpte_offset_negative_clicked));
422         synced_timecode_button.signal_toggled().connect (mem_fun(*this, &OptionEditor::synced_timecode_toggled));
423 }
424
425 void
426 OptionEditor::smpte_offset_negative_clicked ()
427 {
428         if (session) {
429                 session->set_smpte_offset_negative (smpte_offset_negative_button.get_active());
430         }
431 }
432
433 void
434 OptionEditor::synced_timecode_toggled ()
435 {
436         bool x;
437
438         if ((x = synced_timecode_button.get_active()) != Config->get_timecode_source_is_synced()) {
439                 Config->set_timecode_source_is_synced (x);
440                 Config->save_state();
441         }
442 }
443
444 void
445 OptionEditor::smpte_offset_chosen()
446 {
447         if (session) {
448                 nframes_t frames = smpte_offset_clock.current_duration();
449                 session->set_smpte_offset (frames);
450         }
451 }
452
453
454 void
455 OptionEditor::setup_midi_options ()
456 {
457         HBox* hbox;
458         Label* label;
459
460         midi_port_table.set_row_spacings (6);
461         midi_port_table.set_col_spacings (10);
462
463         redisplay_midi_ports ();
464
465         mmc_receive_device_id_adjustment.set_value (Config->get_mmc_receive_device_id());
466         mmc_send_device_id_adjustment.set_value (Config->get_mmc_send_device_id());
467
468         mmc_receive_device_id_adjustment.signal_value_changed().connect (mem_fun (*this, &OptionEditor::mmc_receive_device_id_adjusted));
469         mmc_send_device_id_adjustment.signal_value_changed().connect (mem_fun (*this, &OptionEditor::mmc_send_device_id_adjusted));
470
471         hbox = manage (new HBox);
472         hbox->set_border_width (6);
473         hbox->pack_start (midi_port_table, true, false);
474
475         midi_packer.pack_start (*hbox, false, false);
476         add_midi_port_button.set_label ("Add MIDI port");
477         midi_packer.pack_start (add_midi_port_button, false, false);
478
479         hbox = manage (new HBox);
480         hbox->set_border_width (6);
481         hbox->set_spacing (6);
482         label = (manage (new Label (_("Inbound MMC Device ID")))); 
483         hbox->pack_start (mmc_receive_device_id_spinner, false, false);
484         hbox->pack_start (*label, false, false);
485         midi_packer.pack_start (*hbox, false, false); 
486
487         mmc_receive_device_id_spinner.set_value(Config->get_mmc_receive_device_id ());
488
489         hbox = manage (new HBox);
490         hbox->set_border_width (6);
491         hbox->set_spacing (6);
492         label = (manage (new Label (_("Outbound MMC Device ID")))); 
493         hbox->pack_start (mmc_send_device_id_spinner, false, false);
494         hbox->pack_start (*label, false, false);
495         midi_packer.pack_start (*hbox, false, false);
496
497         mmc_send_device_id_spinner.set_value(Config->get_mmc_send_device_id ());
498
499         add_midi_port_button.signal_clicked().connect (mem_fun (*this, &OptionEditor::add_midi_port));
500 }
501
502 void
503 OptionEditor::redisplay_midi_ports ()
504 {
505         MIDI::Manager::PortMap::const_iterator i;
506         const MIDI::Manager::PortMap& ports = MIDI::Manager::instance()->get_midi_ports();
507         int n;
508
509         /* remove all existing widgets */
510
511         // XXX broken in gtkmm 2.10
512         // midi_port_table.clear ();
513
514         for (vector<Widget*>::iterator w = midi_port_table_widgets.begin(); w != midi_port_table_widgets.end(); ++w) {
515                 midi_port_table.remove (**w);
516         }
517
518         midi_port_table_widgets.clear ();
519
520         midi_port_table.resize (ports.size() + 4, 11);
521
522         Gtk::Label* label;
523
524         label = (manage (new Label (_("Port")))); 
525         label->show ();
526         midi_port_table_widgets.push_back (label);
527         midi_port_table.attach (*label, 0, 1, 0, 1);
528         label = (manage (new Label (_("Offline")))); 
529         label->show ();
530         midi_port_table_widgets.push_back (label);
531         midi_port_table.attach (*label, 1, 2, 0, 1);
532         label = (manage (new Label (_("Trace\nInput")))); 
533         label->show ();
534         midi_port_table_widgets.push_back (label);
535         midi_port_table.attach (*label, 2, 3, 0, 1);
536         label = (manage (new Label (_("Trace\nOutput")))); 
537         label->show ();
538         midi_port_table_widgets.push_back (label);
539         midi_port_table.attach (*label, 3, 4, 0, 1);
540         label = (manage (new Label (_("MTC")))); 
541         label->show ();
542         midi_port_table_widgets.push_back (label);
543         midi_port_table.attach (*label, 4, 5, 0, 1);
544         label = (manage (new Label (_("MMC")))); 
545         label->show ();
546         midi_port_table_widgets.push_back (label);
547         midi_port_table.attach (*label, 6, 7, 0, 1);
548         label = (manage (new Label (_("MIDI Parameter\nControl")))); 
549         label->show ();
550         midi_port_table_widgets.push_back (label);
551         midi_port_table.attach (*label, 8, 9, 0, 1);
552
553         Gtk::HSeparator* hsep = (manage (new HSeparator())); 
554         hsep->show ();
555         midi_port_table_widgets.push_back (hsep);
556         midi_port_table.attach (*hsep, 0, 9, 1, 2);
557         Gtk::VSeparator* vsep = (manage (new VSeparator())); 
558         vsep->show ();
559         midi_port_table_widgets.push_back (vsep);
560         midi_port_table.attach (*vsep, 5, 6, 0, 8);
561         vsep = (manage (new VSeparator())); 
562         vsep->show ();
563         midi_port_table_widgets.push_back (vsep);
564         midi_port_table.attach (*vsep, 7, 8, 0, 8);
565         
566         for (n = 0, i = ports.begin(); i != ports.end(); ++n, ++i) {
567
568                 ToggleButton* tb;
569                 RadioButton* rb;
570                 Button* bb;
571
572                 /* the remove button. create early so we can pass it to various callbacks */
573                 
574                 bb = manage (new Button (Stock::REMOVE));
575                 bb->set_name ("OptionEditorToggleButton");
576                 bb->show ();
577                 midi_port_table_widgets.push_back (bb);
578                 midi_port_table.attach (*bb, 9, 10, n+2, n+3, FILL|EXPAND, FILL);
579                 bb->signal_clicked().connect (bind (mem_fun(*this, &OptionEditor::remove_midi_port), i->second));
580                 bb->set_sensitive (port_removable (i->second));
581
582                 label = (manage (new Label (i->first))); 
583                 label->show ();
584                 midi_port_table_widgets.push_back (label);
585                 midi_port_table.attach (*label, 0, 1, n+2, n+3,FILL|EXPAND, FILL );
586                 
587                 tb = manage (new ToggleButton (_("online")));
588                 tb->set_name ("OptionEditorToggleButton");
589
590                 /* remember, we have to handle the i18n case where the relative
591                    lengths of the strings in language N is different than in english.
592                 */
593
594                 if (strlen (_("offline")) > strlen (_("online"))) {
595                         set_size_request_to_display_given_text (*tb, _("offline"), 15, 12);
596                 } else {
597                         set_size_request_to_display_given_text (*tb, _("online"), 15, 12);
598                 }
599
600                 if (i->second->input()) {
601                         tb->set_active (!i->second->input()->offline());
602                         tb->signal_toggled().connect (bind (mem_fun(*this, &OptionEditor::port_online_toggled), i->second, tb));
603                         i->second->input()->OfflineStatusChanged.connect (bind (mem_fun(*this, &OptionEditor::map_port_online), (*i).second, tb));
604                 }
605                 tb->show ();
606                 midi_port_table_widgets.push_back (tb);
607                 midi_port_table.attach (*tb, 1, 2, n+2, n+3, FILL|EXPAND, FILL);
608
609                 tb = manage (new ToggleButton ());
610                 tb->set_name ("OptionEditorToggleButton");
611                 tb->signal_toggled().connect (bind (mem_fun(*this, &OptionEditor::port_trace_in_toggled), (*i).second, tb));
612                 tb->set_size_request (10, 10);
613                 tb->show ();
614                 midi_port_table_widgets.push_back (tb);
615                 midi_port_table.attach (*tb, 2, 3, n+2, n+3, FILL|EXPAND, FILL);
616
617                 tb = manage (new ToggleButton ());
618                 tb->set_name ("OptionEditorToggleButton");
619                 tb->signal_toggled().connect (bind (mem_fun(*this, &OptionEditor::port_trace_out_toggled), (*i).second, tb));
620                 tb->set_size_request (10, 10);
621                 tb->show ();
622                 midi_port_table_widgets.push_back (tb);
623                 midi_port_table.attach (*tb, 3, 4, n+2, n+3, FILL|EXPAND, FILL);
624
625                 rb = manage (new RadioButton ());
626                 rb->set_name ("OptionEditorToggleButton");
627                 if (n == 0) {
628                         mtc_button_group = rb->get_group();
629                 } else {
630                         rb->set_group (mtc_button_group);
631
632                 }
633                 rb->show ();
634                 midi_port_table_widgets.push_back (rb);
635                 midi_port_table.attach (*rb, 4, 5, n+2, n+3, FILL|EXPAND, FILL);
636                 rb->signal_toggled().connect (bind (mem_fun(*this, &OptionEditor::mtc_port_chosen), (*i).second, rb, bb));
637
638                 if (session && i->second == session->mtc_port()) {
639                         rb->set_active (true);
640                 }
641                 
642                 rb = manage (new RadioButton ());
643                 rb->set_name ("OptionEditorToggleButton");
644                 if (n == 0) {
645                         mmc_button_group = rb->get_group();
646                 } else {
647                         rb->set_group (mmc_button_group);
648                 }
649                 rb->show ();
650                 midi_port_table_widgets.push_back (rb);
651                 midi_port_table.attach (*rb, 6, 7, n+2, n+3, FILL|EXPAND, FILL);
652                 rb->signal_toggled().connect (bind (mem_fun(*this, &OptionEditor::mmc_port_chosen), (*i).second, rb, bb));
653
654                 if (session && i->second == session->mmc_port()) {
655                         rb->set_active (true);
656                 }
657
658                 rb = manage (new RadioButton ());
659                 rb->set_name ("OptionEditorToggleButton");
660                 if (n == 0) {
661                         midi_button_group = rb->get_group();
662                 } else {
663                         rb->set_group (midi_button_group);
664                 }
665                 rb->show ();
666                 midi_port_table_widgets.push_back (rb);
667                 midi_port_table.attach (*rb, 8, 9, n+2, n+3, FILL|EXPAND, FILL);
668                 rb->signal_toggled().connect (bind (mem_fun(*this, &OptionEditor::midi_port_chosen), (*i).second, rb, bb));
669
670                 if (session && i->second == session->midi_port()) {
671                         rb->set_active (true);
672                 }
673
674         }
675
676         midi_port_table.show();
677 }
678
679 void
680 OptionEditor::remove_midi_port (MIDI::Port* port)
681 {
682         MIDI::Manager::instance()->remove_port (port);
683         redisplay_midi_ports ();
684 }
685
686 void
687 OptionEditor::add_midi_port ()
688 {
689         MidiPortDialog dialog;
690
691         dialog.set_position (WIN_POS_MOUSE);
692         dialog.set_transient_for (*this);
693
694         dialog.show ();
695
696         int ret = dialog.run ();
697
698         switch (ret) {
699         case RESPONSE_ACCEPT:
700                 break;
701         default:
702                 return;
703                 break;
704         }
705
706         Glib::ustring mode = dialog.port_mode_combo.get_active_text();
707         std::string smod;
708
709         if (mode == _("input")) {
710                 smod = X_("input");
711         } else if (mode == (_("output"))) {
712                 smod = X_("output");
713         } else {
714                 smod = "duplex";
715         }
716
717
718         XMLNode node (X_("MIDI-port"));
719
720         node.add_property ("tag", dialog.port_name.get_text());
721         node.add_property ("device", X_("ardour")); // XXX this can't be right for all types
722         node.add_property ("type", MIDI::PortFactory::default_port_type());
723         node.add_property ("mode", smod);
724
725         if (MIDI::Manager::instance()->add_port (node) != 0) {
726                 redisplay_midi_ports ();
727         }
728 }
729
730 bool
731 OptionEditor::port_removable (MIDI::Port *port)
732 {
733         if (!session) {
734                 return true;
735         }
736
737         if (port == session->mtc_port() ||
738             port == session->mmc_port() ||
739             port == session->midi_port()) {
740                 return false;
741         }
742         return true;
743 }
744
745 void
746 OptionEditor::mtc_port_chosen (MIDI::Port *port, Gtk::RadioButton* rb, Gtk::Button* bb) 
747 {
748         if (session) {
749                 if (rb->get_active()) {
750                         session->set_mtc_port (port->name());
751                         Config->set_mtc_port_name (port->name());
752                 } else {
753                         session->set_mtc_port ("");
754                 }
755                 bb->set_sensitive (port_removable (port));
756         }
757 }
758
759 void
760 OptionEditor::mmc_port_chosen (MIDI::Port* port, Gtk::RadioButton* rb, Gtk::Button* bb)
761 {
762         if (session) {
763                 if (rb->get_active()) {
764                         session->set_mmc_port (port->name());
765                         Config->set_mtc_port_name (port->name());
766                 } else {
767                         session->set_mmc_port ("");
768                 }
769                 bb->set_sensitive (port_removable (port));
770         }
771 }
772
773 void
774 OptionEditor::midi_port_chosen (MIDI::Port* port, Gtk::RadioButton* rb, Gtk::Button* bb)
775 {
776         if (session) {
777                 if (rb->get_active()) {
778                         session->set_midi_port (port->name());
779                         Config->set_midi_port_name (port->name());
780                 } else {
781                         session->set_midi_port ("");
782                 }
783                 bb->set_sensitive (port_removable (port));
784         }
785 }
786
787 void
788 OptionEditor::port_online_toggled (MIDI::Port* port, ToggleButton* tb)
789 {
790         bool wanted = tb->get_active();
791
792         if (port->input()) {
793                 if (wanted != port->input()->offline()) {
794                         port->input()->set_offline (wanted);
795                 } 
796         }
797 }
798
799 void
800 OptionEditor::map_port_online (MIDI::Port* port, ToggleButton* tb)
801 {
802         bool bstate = tb->get_active ();
803         
804         if (port->input()) {
805                 if (bstate != port->input()->offline()) {
806                         if (port->input()->offline()) {
807                                 tb->set_label (_("offline"));
808                                 tb->set_active (false);
809                         } else {
810                                 tb->set_label (_("online"));
811                                 tb->set_active (true);
812                         }
813                 }
814         }
815 }
816
817 void
818 OptionEditor::mmc_receive_device_id_adjusted ()
819 {
820         uint8_t id = (uint8_t) mmc_receive_device_id_spinner.get_value();
821         Config->set_mmc_receive_device_id (id);
822 }
823
824 void
825 OptionEditor::mmc_send_device_id_adjusted ()
826 {
827         uint8_t id = (uint8_t) mmc_send_device_id_spinner.get_value();
828         Config->set_mmc_send_device_id (id);
829 }
830
831 void
832 OptionEditor::port_trace_in_toggled (MIDI::Port* port, ToggleButton* tb)
833 {
834         bool trace = tb->get_active();
835
836         if (port->input()) {
837                 if (port->input()->tracing() != trace) {
838                         port->input()->trace (trace, &cerr, string (port->name()) + string (" input: "));
839                 }
840         }
841 }
842
843 void
844 OptionEditor::port_trace_out_toggled (MIDI::Port* port, ToggleButton* tb)
845 {
846         bool trace = tb->get_active();
847
848         if (port->output()) {
849                 if (port->output()->tracing() != trace) {
850                         port->output()->trace (trace, &cerr, string (port->name()) + string (" output: "));
851                 }
852         }
853 }
854
855 void
856 OptionEditor::save ()
857 {
858         /* XXX a bit odd that we save the entire session state here */
859
860         ui.save_state ("");
861 }
862
863 gint
864 OptionEditor::wm_close (GdkEventAny *ev)
865 {
866         save ();
867         hide ();
868         return TRUE;
869 }
870
871 void
872 OptionEditor::raid_path_changed ()
873 {
874         if (session) {
875                 Config->set_raid_path (session_raid_entry.get_text());
876         }
877 }
878
879 void
880 OptionEditor::click_browse_clicked ()
881 {
882         SoundFileChooser sfdb (*this, _("Choose Click"), session);
883         
884         sfdb.show_all ();
885         sfdb.present ();
886
887         int result = sfdb.run ();
888  
889         if (result == Gtk::RESPONSE_OK) {
890                 click_chosen(sfdb.get_filename());
891         }
892 }
893
894 void
895 OptionEditor::click_chosen (const string & path)
896 {
897         click_path_entry.set_text (path);
898         click_sound_changed ();
899 }
900
901 void
902 OptionEditor::click_emphasis_browse_clicked ()
903 {
904         SoundFileChooser sfdb (*this, _("Choose Click Emphasis"), session);
905
906         sfdb.show_all ();
907         sfdb.present ();
908
909         int result = sfdb.run ();
910
911         if (result == Gtk::RESPONSE_OK) {
912                 click_emphasis_chosen (sfdb.get_filename());
913         }
914 }
915
916 void
917 OptionEditor::click_emphasis_chosen (const string & path)
918 {       
919         click_emphasis_path_entry.set_text (path);
920         click_emphasis_sound_changed ();
921 }
922
923 void
924 OptionEditor::click_sound_changed ()
925 {
926         if (session) {
927                 string path = click_path_entry.get_text();
928
929                 if (path == Config->get_click_sound()) {
930                         return;
931                 }
932
933                 strip_whitespace_edges (path);
934
935                 if (path == _("internal")) {
936                         Config->set_click_sound ("");
937                 } else {
938                         Config->set_click_sound (path);
939                 }
940         }
941 }
942
943 void
944 OptionEditor::click_emphasis_sound_changed ()
945 {
946         if (session) {
947                 string path = click_emphasis_path_entry.get_text();
948
949                 if (path == Config->get_click_emphasis_sound()) {
950                         return;
951                 }
952
953                 strip_whitespace_edges (path);
954
955                 if (path == _("internal")) {
956                         Config->set_click_emphasis_sound ("");
957                 } else {
958                         Config->set_click_emphasis_sound (path);
959                 }
960         }
961 }
962
963 void
964 OptionEditor::clear_click_editor ()
965 {
966         if (click_io_selector) {
967                 click_packer.remove (*click_io_selector);
968                 click_packer.remove (*click_gpm);
969                 delete click_io_selector;
970                 delete click_gpm;
971                 click_io_selector = 0;
972                 click_gpm = 0;
973         }
974 }
975
976 void
977 OptionEditor::setup_click_editor ()
978 {
979         Label* label;
980         HBox* hpacker = manage (new HBox);
981
982         click_path_entry.set_sensitive (true);
983         click_emphasis_path_entry.set_sensitive (true);
984
985         click_path_entry.set_name ("OptionsEntry");
986         click_emphasis_path_entry.set_name ("OptionsEntry");
987         
988         click_path_entry.signal_activate().connect (mem_fun(*this, &OptionEditor::click_sound_changed));
989         click_emphasis_path_entry.signal_activate().connect (mem_fun(*this, &OptionEditor::click_emphasis_sound_changed));
990
991         click_path_entry.signal_focus_out_event().connect (bind (mem_fun(*this, &OptionEditor::focus_out_event_handler), &OptionEditor::click_sound_changed));
992         click_emphasis_path_entry.signal_focus_out_event().connect (bind (mem_fun(*this, &OptionEditor::focus_out_event_handler), &OptionEditor::click_emphasis_sound_changed));
993
994         click_browse_button.set_name ("EditorGTKButton");
995         click_emphasis_browse_button.set_name ("EditorGTKButton");
996         click_browse_button.signal_clicked().connect (mem_fun(*this, &OptionEditor::click_browse_clicked));
997         click_emphasis_browse_button.signal_clicked().connect (mem_fun(*this, &OptionEditor::click_emphasis_browse_clicked));
998
999         click_packer.set_border_width (12);
1000         click_packer.set_spacing (5);
1001
1002         click_io_selector = new IOSelector (*session, session->click_io(), false);
1003         click_gpm = new GainMeter (session->click_io(), *session);
1004
1005         click_table.set_col_spacings (10);
1006         
1007         label = manage(new Label(_("Click audio file")));
1008         label->set_name ("OptionsLabel");
1009         click_table.attach (*label, 0, 1, 0, 1, FILL|EXPAND, FILL);
1010         click_table.attach (click_path_entry, 1, 2, 0, 1, Gtk::FILL|Gtk::EXPAND, FILL);
1011         click_table.attach (click_browse_button, 2, 3, 0, 1, FILL|EXPAND, FILL);
1012         
1013         label = manage(new Label(_("Click emphasis audiofile")));
1014         label->set_name ("OptionsLabel");
1015         click_table.attach (*label, 0, 1, 1, 2, FILL|EXPAND, FILL);
1016         click_table.attach (click_emphasis_path_entry, 1, 2, 1, 2, Gtk::FILL|Gtk::EXPAND, FILL);
1017         click_table.attach (click_emphasis_browse_button, 2, 3, 1, 2, FILL|EXPAND, FILL);
1018
1019         hpacker->set_spacing (10);
1020         hpacker->pack_start (*click_io_selector, false, false);
1021         hpacker->pack_start (*click_gpm, false, false);
1022
1023         click_packer.pack_start (click_table, false, false);
1024         click_packer.pack_start (*hpacker, false, false);
1025
1026         click_packer.show_all ();
1027 }
1028
1029 void
1030 OptionEditor::clear_auditioner_editor ()
1031 {
1032         if (auditioner_io_selector) {
1033                 audition_hpacker.remove (*auditioner_io_selector);
1034                 audition_hpacker.remove (*auditioner_gpm);
1035                 delete auditioner_io_selector;
1036                 delete auditioner_gpm;
1037                 auditioner_io_selector = 0;
1038                 auditioner_gpm = 0;
1039         }
1040 }
1041
1042 void
1043 OptionEditor::setup_auditioner_editor ()
1044 {
1045         audition_packer.set_border_width (12);
1046         audition_packer.set_spacing (5);
1047         audition_hpacker.set_spacing (10);
1048
1049         audition_label.set_name ("OptionEditorAuditionerLabel");
1050         audition_label.set_text (_("The auditioner is a dedicated mixer strip used\n"
1051                                    "for listening to specific regions outside the context\n"
1052                                    "of the overall mix. It can be connected just like any\n"
1053                                    "other mixer strip."));
1054         
1055         audition_packer.pack_start (audition_label, false, false, 10);
1056         audition_packer.pack_start (audition_hpacker, false, false);
1057 }
1058
1059 void
1060 OptionEditor::connect_audition_editor ()
1061 {
1062         auditioner_io_selector = new IOSelector (*session, session->the_auditioner(), false);
1063         auditioner_gpm = new GainMeter (session->the_auditioner(), *session);
1064
1065         audition_hpacker.pack_start (*auditioner_io_selector, false, false);
1066         audition_hpacker.pack_start (*auditioner_gpm, false, false);
1067
1068         auditioner_io_selector->show_all ();
1069         auditioner_gpm->show_all ();
1070 }
1071
1072 bool
1073 OptionEditor::focus_out_event_handler (GdkEventFocus* ev, void (OptionEditor::*pmf)()) 
1074 {
1075         (this->*pmf)();
1076         return false;
1077 }
1078
1079 static const struct {
1080     const char *name;
1081     guint   modifier;
1082 } modifiers[] = {
1083
1084 #ifdef GTKOSX 
1085
1086         /* Command = Meta
1087            Option/Alt = Mod1
1088         */
1089
1090         { "Shift", GDK_SHIFT_MASK },
1091         { "Command", GDK_META_MASK },
1092         { "Control", GDK_CONTROL_MASK },
1093         { "Option", GDK_MOD1_MASK },
1094         { "Command-Shift", GDK_MOD1_MASK|GDK_SHIFT_MASK },
1095         { "Command-Option", GDK_MOD1_MASK|GDK_MOD5_MASK },
1096         { "Shift-Option", GDK_SHIFT_MASK|GDK_MOD5_MASK },
1097         { "Shift-Command-Option", GDK_MOD5_MASK|GDK_SHIFT_MASK|GDK_MOD1_MASK },
1098
1099 #else
1100         { "Shift", GDK_SHIFT_MASK },
1101         { "Control", GDK_CONTROL_MASK },
1102         { "Alt (Mod1)", GDK_MOD1_MASK },
1103         { "Control-Shift", GDK_CONTROL_MASK|GDK_SHIFT_MASK },
1104         { "Control-Alt", GDK_CONTROL_MASK|GDK_MOD1_MASK },
1105         { "Shift-Alt", GDK_SHIFT_MASK|GDK_MOD1_MASK },
1106         { "Control-Shift-Alt", GDK_CONTROL_MASK|GDK_SHIFT_MASK|GDK_MOD1_MASK },
1107         { "Mod2", GDK_MOD2_MASK },
1108         { "Mod3", GDK_MOD3_MASK },
1109         { "Mod4", GDK_MOD4_MASK },
1110         { "Mod5", GDK_MOD5_MASK },
1111 #endif
1112         { 0, 0 }
1113 };
1114
1115 void
1116 OptionEditor::setup_keyboard_options ()
1117 {
1118         vector<string> dumb;
1119         Label* label;
1120
1121         keyboard_mouse_table.set_border_width (12);
1122         keyboard_mouse_table.set_row_spacings (5);
1123         keyboard_mouse_table.set_col_spacings (5);
1124
1125         /* internationalize and prepare for use with combos */
1126
1127         for (int i = 0; modifiers[i].name; ++i) {
1128                 dumb.push_back (_(modifiers[i].name));
1129         }
1130
1131         set_popdown_strings (edit_modifier_combo, dumb);
1132         edit_modifier_combo.signal_changed().connect (mem_fun(*this, &OptionEditor::edit_modifier_chosen));
1133
1134         for (int x = 0; modifiers[x].name; ++x) {
1135                 if (modifiers[x].modifier == Keyboard::edit_modifier ()) {
1136                         edit_modifier_combo.set_active_text (_(modifiers[x].name));
1137                         break;
1138                 }
1139         }
1140
1141         label = manage (new Label (_("Edit using")));
1142         label->set_name ("OptionsLabel");
1143         label->set_alignment (1.0, 0.5);
1144                 
1145         keyboard_mouse_table.attach (*label, 0, 1, 0, 1, Gtk::FILL|Gtk::EXPAND, FILL);
1146         keyboard_mouse_table.attach (edit_modifier_combo, 1, 2, 0, 1, Gtk::FILL|Gtk::EXPAND, FILL);
1147
1148         label = manage (new Label (_("+ button")));
1149         label->set_name ("OptionsLabel");
1150         
1151         keyboard_mouse_table.attach (*label, 3, 4, 0, 1, Gtk::FILL|Gtk::EXPAND, FILL);
1152         keyboard_mouse_table.attach (edit_button_spin, 4, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, FILL);
1153
1154         edit_button_spin.set_name ("OptionsEntry");
1155         edit_button_adjustment.set_value (Keyboard::edit_button());
1156         edit_button_adjustment.signal_value_changed().connect (mem_fun(*this, &OptionEditor::edit_button_changed));
1157
1158         set_popdown_strings (delete_modifier_combo, dumb);
1159         delete_modifier_combo.signal_changed().connect (mem_fun(*this, &OptionEditor::delete_modifier_chosen));
1160
1161         for (int x = 0; modifiers[x].name; ++x) {
1162                 if (modifiers[x].modifier == Keyboard::delete_modifier ()) {
1163                         delete_modifier_combo.set_active_text (_(modifiers[x].name));
1164                         break;
1165                 }
1166         }
1167
1168         label = manage (new Label (_("Delete using")));
1169         label->set_name ("OptionsLabel");
1170         label->set_alignment (1.0, 0.5);
1171                 
1172         keyboard_mouse_table.attach (*label, 0, 1, 1, 2, Gtk::FILL|Gtk::EXPAND, FILL);
1173         keyboard_mouse_table.attach (delete_modifier_combo, 1, 2, 1, 2, Gtk::FILL|Gtk::EXPAND, FILL);
1174
1175         label = manage (new Label (_("+ button")));
1176         label->set_name ("OptionsLabel");
1177
1178         keyboard_mouse_table.attach (*label, 3, 4, 1, 2, Gtk::FILL|Gtk::EXPAND, FILL);
1179         keyboard_mouse_table.attach (delete_button_spin, 4, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, FILL);
1180
1181         delete_button_spin.set_name ("OptionsEntry");
1182         delete_button_adjustment.set_value (Keyboard::delete_button());
1183         delete_button_adjustment.signal_value_changed().connect (mem_fun(*this, &OptionEditor::delete_button_changed));
1184
1185         set_popdown_strings (snap_modifier_combo, dumb);
1186         snap_modifier_combo.signal_changed().connect (mem_fun(*this, &OptionEditor::snap_modifier_chosen));
1187         
1188         for (int x = 0; modifiers[x].name; ++x) {
1189                 if (modifiers[x].modifier == (guint) Keyboard::snap_modifier ()) {
1190                         snap_modifier_combo.set_active_text (_(modifiers[x].name));
1191                         break;
1192                 }
1193         }
1194
1195         label = manage (new Label (_("Ignore snap using")));
1196         label->set_name ("OptionsLabel");
1197         label->set_alignment (1.0, 0.5);
1198         
1199         keyboard_mouse_table.attach (*label, 0, 1, 2, 3, Gtk::FILL|Gtk::EXPAND, FILL);
1200         keyboard_mouse_table.attach (snap_modifier_combo, 1, 2, 2, 3, Gtk::FILL|Gtk::EXPAND, FILL);
1201
1202         vector<string> strs;
1203         
1204         for (std::map<std::string,std::string>::iterator bf = Keyboard::binding_files.begin(); bf != Keyboard::binding_files.end(); ++bf) {
1205                 strs.push_back (bf->first);
1206         }
1207         
1208         set_popdown_strings (keyboard_layout_selector, strs);
1209         keyboard_layout_selector.set_active_text (Keyboard::current_binding_name());
1210         keyboard_layout_selector.signal_changed().connect (mem_fun (*this, &OptionEditor::bindings_changed));
1211
1212         label = manage (new Label (_("Keyboard layout")));
1213         label->set_name ("OptionsLabel");
1214         label->set_alignment (1.0, 0.5);
1215
1216         keyboard_mouse_table.attach (*label, 0, 1, 3, 4, Gtk::FILL|Gtk::EXPAND, FILL);
1217         keyboard_mouse_table.attach (keyboard_layout_selector, 1, 2, 3, 4, Gtk::FILL|Gtk::EXPAND, FILL);
1218 }
1219
1220 void
1221 OptionEditor::bindings_changed ()
1222 {
1223         string txt;
1224         
1225         txt = keyboard_layout_selector.get_active_text();
1226
1227         for (std::map<string,string>::iterator i = Keyboard::binding_files.begin(); i != Keyboard::binding_files.end(); ++i) {
1228                 if (txt == i->first) {
1229                         if (Keyboard::load_keybindings (i->second)) {
1230                                 Keyboard::save_keybindings ();
1231                         }
1232                 }
1233         }
1234 }
1235
1236 void
1237 OptionEditor::edit_modifier_chosen ()
1238 {
1239         string txt;
1240         
1241         txt = edit_modifier_combo.get_active_text();
1242
1243         for (int i = 0; modifiers[i].name; ++i) {
1244                 if (txt == _(modifiers[i].name)) {
1245                         Keyboard::set_edit_modifier (modifiers[i].modifier);
1246                         break;
1247                 }
1248         }
1249 }
1250
1251 void
1252 OptionEditor::delete_modifier_chosen ()
1253 {
1254         string txt;
1255         
1256         txt = delete_modifier_combo.get_active_text();
1257
1258         for (int i = 0; modifiers[i].name; ++i) {
1259                 if (txt == _(modifiers[i].name)) {
1260                         Keyboard::set_delete_modifier (modifiers[i].modifier);
1261                         break;
1262                 }
1263         }
1264 }
1265
1266 void
1267 OptionEditor::snap_modifier_chosen ()
1268 {
1269         string txt;
1270         
1271         txt = snap_modifier_combo.get_active_text();
1272
1273         for (int i = 0; modifiers[i].name; ++i) {
1274                 if (txt == _(modifiers[i].name)) {
1275                         Keyboard::set_snap_modifier (modifiers[i].modifier);
1276                         break;
1277                 }
1278         }
1279 }
1280
1281 void
1282 OptionEditor::delete_button_changed ()
1283 {
1284         Keyboard::set_delete_button ((guint) delete_button_adjustment.get_value());
1285 }
1286
1287 void
1288 OptionEditor::edit_button_changed ()
1289 {
1290         Keyboard::set_edit_button ((guint) edit_button_adjustment.get_value());
1291 }
1292
1293 void
1294 OptionEditor::fixup_combo_size (Gtk::ComboBoxText& combo, vector<string>& strings)
1295 {
1296         /* find the widest string */
1297
1298         string::size_type maxlen = 0;
1299         string maxstring;
1300
1301         for (vector<string>::iterator i = strings.begin(); i != strings.end(); ++i) {
1302                 string::size_type l;
1303
1304                 if ((l = (*i).length()) > maxlen) {
1305                         maxlen = l;
1306                         maxstring = *i;
1307                 }
1308         }
1309
1310         /* try to include ascenders and descenders */
1311
1312         if (maxstring.length() > 2) {
1313                 maxstring[0] = 'g';
1314                 maxstring[1] = 'l';
1315         }
1316
1317         const guint32 FUDGE = 10; // Combo's are stupid - they steal space from the entry for the button
1318
1319         set_size_request_to_display_given_text (combo, maxstring.c_str(), 10 + FUDGE, 10);
1320 }
1321
1322 void
1323 OptionEditor::parameter_changed (const char* parameter_name)
1324 {
1325         ENSURE_GUI_THREAD (bind (mem_fun (*this, &OptionEditor::parameter_changed), parameter_name));
1326
1327 #define PARAM_IS(x) (!strcmp (parameter_name, (x)))
1328         
1329         if (PARAM_IS ("timecode-source-is-synced")) {
1330                 synced_timecode_button.set_active (Config->get_timecode_source_is_synced());
1331         } else if (PARAM_IS ("history-depth")) {
1332                 int32_t depth = Config->get_history_depth();
1333                 
1334                 history_depth.set_value (depth);
1335                 history_depth_spinner.set_sensitive (depth != 0);
1336                 limit_history_button.set_active (depth != 0);
1337
1338         } else if (PARAM_IS ("saved-history-depth")) {
1339
1340                 saved_history_depth.set_value (Config->get_saved_history_depth());
1341
1342         } else if (PARAM_IS ("save-history")) {
1343
1344                 bool x = Config->get_save_history();
1345
1346                 save_history_button.set_active (x);
1347                 saved_history_depth_spinner.set_sensitive (x);
1348         }
1349 }