I forgot to swap the function names
[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 static void
254 reset_dpi ()
255 {
256         gtk_settings_set_long_property (gtk_settings_get_default(),
257                                         "gtk-xft-dpi", Config->get_font_scale(), "ardour");
258 }
259
260 static void
261 font_scale_changed (Gtk::Adjustment* adj)
262 {
263         Config->set_font_scale((long)floor (adj->get_value() * 1024));
264         reset_dpi();
265 }
266
267 void
268 OptionEditor::setup_misc_options ()
269 {
270         Gtk::HBox* hbox;
271         Gtk::Adjustment* dpi_adj = new Gtk::Adjustment ((double)Config->get_font_scale() / 1024, 50, 250, 1, 10);
272         Gtk::HScale * dpi_range = new Gtk::HScale (*dpi_adj);
273
274         Label* label = manage (new Label (_("Font Scaling")));
275         label->set_name ("OptionsLabel");
276
277         dpi_range->set_update_policy (Gtk::UPDATE_DISCONTINUOUS);
278         dpi_adj->signal_value_changed().connect (bind (sigc::ptr_fun (font_scale_changed), dpi_adj));
279
280         hbox = manage (new HBox);
281         hbox->set_border_width (5);
282         hbox->set_spacing (10);
283         hbox->pack_start (*label, false, false);
284         hbox->pack_start (*dpi_range, true, true);
285         misc_packer.pack_start (*hbox, false, false);
286
287         label = manage (new Label (_("Short crossfade length (msecs)")));
288         label->set_name ("OptionsLabel");
289         
290         hbox = manage (new HBox);
291         hbox->set_border_width (5);
292         hbox->set_spacing (10);
293         hbox->pack_start (*label, false, false);
294         hbox->pack_start (short_xfade_slider, true, true);
295         misc_packer.pack_start (*hbox, false, false);
296
297         short_xfade_adjustment.signal_value_changed().connect (mem_fun(*this, &OptionEditor::short_xfade_adjustment_changed));
298
299         label = manage (new Label (_("Destructive crossfade length (msecs)")));
300         label->set_name ("OptionsLabel");
301         
302         hbox = manage (new HBox);
303         hbox->set_border_width (5);
304         hbox->set_spacing (10);
305         hbox->pack_start (*label, false, false);
306         hbox->pack_start (destructo_xfade_slider, true, true);
307         misc_packer.pack_start (*hbox, false, false);
308         
309
310         destructo_xfade_adjustment.signal_value_changed().connect (mem_fun(*this, &OptionEditor::destructo_xfade_adjustment_changed));
311
312         hbox = manage (new HBox);
313         hbox->set_border_width (5);
314         hbox->set_spacing (10);
315         hbox->pack_start (limit_history_button, false, false);
316         misc_packer.pack_start (*hbox, false, false);
317
318         label = manage (new Label (_("History depth (commands)")));
319         label->set_name ("OptionsLabel");
320
321         hbox = manage (new HBox);
322         hbox->set_border_width (5);
323         hbox->set_spacing (10);
324         hbox->pack_start (*label, false, false);
325         hbox->pack_start (history_depth_spinner, false, false);
326         misc_packer.pack_start (*hbox, false, false);
327
328         history_depth.signal_value_changed().connect (mem_fun (*this, &OptionEditor::history_depth_changed));
329         saved_history_depth.signal_value_changed().connect (mem_fun (*this, &OptionEditor::saved_history_depth_changed));
330         save_history_button.signal_toggled().connect (mem_fun (*this, &OptionEditor::save_history_toggled));
331         limit_history_button.signal_toggled().connect (mem_fun (*this, &OptionEditor::limit_history_toggled));
332
333         hbox = manage (new HBox);
334         hbox->set_border_width (5);
335         hbox->set_spacing (10);
336         hbox->pack_start (save_history_button, false, false);
337         misc_packer.pack_start (*hbox, false, false);
338
339         label = manage (new Label (_("Saved history depth (commands)")));
340         label->set_name ("OptionsLabel");
341
342         hbox = manage (new HBox);
343         hbox->set_border_width (5);
344         hbox->set_spacing (10);
345         hbox->pack_start (*label, false, false);
346         hbox->pack_start (saved_history_depth_spinner, false, false);
347         misc_packer.pack_start (*hbox, false, false);
348         
349         short_xfade_slider.set_update_policy (UPDATE_DISCONTINUOUS);
350         destructo_xfade_slider.set_update_policy (UPDATE_DISCONTINUOUS);
351
352         destructo_xfade_adjustment.set_value (Config->get_destructive_xfade_msecs());
353
354         misc_packer.show_all ();
355 }
356
357 void
358 OptionEditor::limit_history_toggled ()
359 {
360         bool x = limit_history_button.get_active();
361         
362         if (!x) {
363                 Config->set_history_depth (0);
364                 history_depth_spinner.set_sensitive (false);
365         } else {
366                 if (Config->get_history_depth() == 0) {
367                         /* get back to a sane default */
368                         Config->set_history_depth (20);
369                 }
370                 history_depth_spinner.set_sensitive (true);
371         }
372 }
373
374 void
375 OptionEditor::save_history_toggled ()
376 {
377         bool x = save_history_button.get_active();
378
379         if (x != Config->get_save_history()) {
380                 Config->set_save_history (x);
381                 saved_history_depth_spinner.set_sensitive (x);
382         }
383 }
384
385 void
386 OptionEditor::history_depth_changed()
387 {
388         Config->set_history_depth ((int32_t) floor (history_depth.get_value()));
389 }
390
391 void
392 OptionEditor::saved_history_depth_changed()
393 {
394         Config->set_saved_history_depth ((int32_t) floor (saved_history_depth.get_value()));
395 }
396
397 void
398 OptionEditor::short_xfade_adjustment_changed ()
399 {
400         if (session) {
401                 float val = short_xfade_adjustment.get_value();
402                 
403                 /* val is in msecs */
404                 
405                 Crossfade::set_short_xfade_length ((nframes_t) floor (session->frame_rate() * (val / 1000.0)));
406         }
407 }
408
409 void
410 OptionEditor::destructo_xfade_adjustment_changed ()
411 {
412         float val = destructo_xfade_adjustment.get_value();
413
414         /* val is in msecs */
415
416         
417         Config->set_destructive_xfade_msecs ((uint32_t) floor (val));
418
419         if (session) {
420                 SndFileSource::setup_standard_crossfades (session->frame_rate());
421         } 
422 }
423
424 void
425 OptionEditor::setup_sync_options ()
426 {
427         HBox* hbox;
428         vector<string> dumb;
429
430         smpte_offset_clock.set_mode (AudioClock::SMPTE);
431         smpte_offset_clock.ValueChanged.connect (mem_fun(*this, &OptionEditor::smpte_offset_chosen));
432         
433         smpte_offset_negative_button.set_name ("OptionEditorToggleButton");
434
435         smpte_offset_negative_button.unset_flags (Gtk::CAN_FOCUS);
436
437         Label *smpte_offset_label = manage (new Label (_("SMPTE Offset")));
438         smpte_offset_label->set_name("OptionsLabel");
439         
440         hbox = manage (new HBox);
441         hbox->set_border_width (5);
442         hbox->set_spacing (10);
443         hbox->pack_start (*smpte_offset_label, false, false);
444         hbox->pack_start (smpte_offset_clock, false, false);
445         hbox->pack_start (smpte_offset_negative_button, false, false);
446
447         sync_packer.pack_start (*hbox, false, false);
448         sync_packer.pack_start (synced_timecode_button, false, false);
449
450         smpte_offset_negative_button.signal_clicked().connect (mem_fun(*this, &OptionEditor::smpte_offset_negative_clicked));
451         synced_timecode_button.signal_toggled().connect (mem_fun(*this, &OptionEditor::synced_timecode_toggled));
452 }
453
454 void
455 OptionEditor::smpte_offset_negative_clicked ()
456 {
457         if (session) {
458                 session->set_smpte_offset_negative (smpte_offset_negative_button.get_active());
459         }
460 }
461
462 void
463 OptionEditor::synced_timecode_toggled ()
464 {
465         bool x;
466
467         if ((x = synced_timecode_button.get_active()) != Config->get_timecode_source_is_synced()) {
468                 Config->set_timecode_source_is_synced (x);
469                 Config->save_state();
470         }
471 }
472
473 void
474 OptionEditor::smpte_offset_chosen()
475 {
476         if (session) {
477                 nframes_t frames = smpte_offset_clock.current_duration();
478                 session->set_smpte_offset (frames);
479         }
480 }
481
482
483 void
484 OptionEditor::setup_midi_options ()
485 {
486         HBox* hbox;
487         Label* label;
488
489         midi_port_table.set_row_spacings (6);
490         midi_port_table.set_col_spacings (10);
491
492         redisplay_midi_ports ();
493
494         mmc_receive_device_id_adjustment.set_value (Config->get_mmc_receive_device_id());
495         mmc_send_device_id_adjustment.set_value (Config->get_mmc_send_device_id());
496
497         mmc_receive_device_id_adjustment.signal_value_changed().connect (mem_fun (*this, &OptionEditor::mmc_receive_device_id_adjusted));
498         mmc_send_device_id_adjustment.signal_value_changed().connect (mem_fun (*this, &OptionEditor::mmc_send_device_id_adjusted));
499
500         hbox = manage (new HBox);
501         hbox->set_border_width (6);
502         hbox->pack_start (midi_port_table, true, false);
503
504         midi_packer.pack_start (*hbox, false, false);
505         midi_packer.pack_start (add_midi_port_button, false, false);
506
507         hbox = manage (new HBox);
508         hbox->set_border_width (6);
509         hbox->set_spacing (6);
510         label = (manage (new Label (_("Inbound MMC Device ID")))); 
511         hbox->pack_start (mmc_receive_device_id_spinner, false, false);
512         hbox->pack_start (*label, false, false);
513         midi_packer.pack_start (*hbox, false, false); 
514
515         mmc_receive_device_id_spinner.set_value(Config->get_mmc_receive_device_id ());
516
517         hbox = manage (new HBox);
518         hbox->set_border_width (6);
519         hbox->set_spacing (6);
520         label = (manage (new Label (_("Outbound MMC Device ID")))); 
521         hbox->pack_start (mmc_send_device_id_spinner, false, false);
522         hbox->pack_start (*label, false, false);
523         midi_packer.pack_start (*hbox, false, false);
524
525         mmc_send_device_id_spinner.set_value(Config->get_mmc_send_device_id ());
526
527         add_midi_port_button.signal_clicked().connect (mem_fun (*this, &OptionEditor::add_midi_port));
528 }
529
530 void
531 OptionEditor::redisplay_midi_ports ()
532 {
533         MIDI::Manager::PortMap::const_iterator i;
534         const MIDI::Manager::PortMap& ports = MIDI::Manager::instance()->get_midi_ports();
535         int n;
536
537         /* remove all existing widgets */
538
539         // XXX broken in gtkmm 2.10
540         // midi_port_table.clear ();
541
542         for (vector<Widget*>::iterator w = midi_port_table_widgets.begin(); w != midi_port_table_widgets.end(); ++w) {
543                 midi_port_table.remove (**w);
544         }
545
546         midi_port_table_widgets.clear ();
547
548         midi_port_table.resize (ports.size() + 4, 11);
549
550         Gtk::Label* label;
551
552         label = (manage (new Label (_("Port")))); 
553         label->show ();
554         midi_port_table_widgets.push_back (label);
555         midi_port_table.attach (*label, 0, 1, 0, 1);
556         label = (manage (new Label (_("Offline")))); 
557         label->show ();
558         midi_port_table_widgets.push_back (label);
559         midi_port_table.attach (*label, 1, 2, 0, 1);
560         label = (manage (new Label (_("Trace\nInput")))); 
561         label->show ();
562         midi_port_table_widgets.push_back (label);
563         midi_port_table.attach (*label, 2, 3, 0, 1);
564         label = (manage (new Label (_("Trace\nOutput")))); 
565         label->show ();
566         midi_port_table_widgets.push_back (label);
567         midi_port_table.attach (*label, 3, 4, 0, 1);
568         label = (manage (new Label (_("MTC")))); 
569         label->show ();
570         midi_port_table_widgets.push_back (label);
571         midi_port_table.attach (*label, 4, 5, 0, 1);
572         label = (manage (new Label (_("MMC")))); 
573         label->show ();
574         midi_port_table_widgets.push_back (label);
575         midi_port_table.attach (*label, 6, 7, 0, 1);
576         label = (manage (new Label (_("MIDI Parameter\nControl")))); 
577         label->show ();
578         midi_port_table_widgets.push_back (label);
579         midi_port_table.attach (*label, 8, 9, 0, 1);
580
581         Gtk::HSeparator* hsep = (manage (new HSeparator())); 
582         hsep->show ();
583         midi_port_table_widgets.push_back (hsep);
584         midi_port_table.attach (*hsep, 0, 9, 1, 2);
585         Gtk::VSeparator* vsep = (manage (new VSeparator())); 
586         vsep->show ();
587         midi_port_table_widgets.push_back (vsep);
588         midi_port_table.attach (*vsep, 5, 6, 0, 8);
589         vsep = (manage (new VSeparator())); 
590         vsep->show ();
591         midi_port_table_widgets.push_back (vsep);
592         midi_port_table.attach (*vsep, 7, 8, 0, 8);
593         
594         for (n = 0, i = ports.begin(); i != ports.end(); ++n, ++i) {
595
596                 ToggleButton* tb;
597                 RadioButton* rb;
598                 Button* bb;
599
600                 /* the remove button. create early so we can pass it to various callbacks */
601                 
602                 bb = manage (new Button (Stock::REMOVE));
603                 bb->set_name ("OptionEditorToggleButton");
604                 bb->show ();
605                 midi_port_table_widgets.push_back (bb);
606                 midi_port_table.attach (*bb, 9, 10, n+2, n+3, FILL|EXPAND, FILL);
607                 bb->signal_clicked().connect (bind (mem_fun(*this, &OptionEditor::remove_midi_port), i->second));
608                 bb->set_sensitive (port_removable (i->second));
609
610                 label = (manage (new Label (i->first))); 
611                 label->show ();
612                 midi_port_table_widgets.push_back (label);
613                 midi_port_table.attach (*label, 0, 1, n+2, n+3,FILL|EXPAND, FILL );
614                 
615                 tb = manage (new ToggleButton (_("online")));
616                 tb->set_name ("OptionEditorToggleButton");
617
618                 /* remember, we have to handle the i18n case where the relative
619                    lengths of the strings in language N is different than in english.
620                 */
621
622                 if (strlen (_("offline")) > strlen (_("online"))) {
623                         set_size_request_to_display_given_text (*tb, _("offline"), 15, 12);
624                 } else {
625                         set_size_request_to_display_given_text (*tb, _("online"), 15, 12);
626                 }
627
628                 if (i->second->input()) {
629                         tb->set_active (!i->second->input()->offline());
630                         tb->signal_toggled().connect (bind (mem_fun(*this, &OptionEditor::port_online_toggled), i->second, tb));
631                         i->second->input()->OfflineStatusChanged.connect (bind (mem_fun(*this, &OptionEditor::map_port_online), (*i).second, tb));
632                 }
633                 tb->show ();
634                 midi_port_table_widgets.push_back (tb);
635                 midi_port_table.attach (*tb, 1, 2, n+2, n+3, FILL|EXPAND, FILL);
636
637                 tb = manage (new ToggleButton ());
638                 tb->set_name ("OptionEditorToggleButton");
639                 tb->signal_toggled().connect (bind (mem_fun(*this, &OptionEditor::port_trace_in_toggled), (*i).second, tb));
640                 tb->set_size_request (10, 10);
641                 tb->show ();
642                 midi_port_table_widgets.push_back (tb);
643                 midi_port_table.attach (*tb, 2, 3, n+2, n+3, FILL|EXPAND, FILL);
644
645                 tb = manage (new ToggleButton ());
646                 tb->set_name ("OptionEditorToggleButton");
647                 tb->signal_toggled().connect (bind (mem_fun(*this, &OptionEditor::port_trace_out_toggled), (*i).second, tb));
648                 tb->set_size_request (10, 10);
649                 tb->show ();
650                 midi_port_table_widgets.push_back (tb);
651                 midi_port_table.attach (*tb, 3, 4, n+2, n+3, FILL|EXPAND, FILL);
652
653                 rb = manage (new RadioButton ());
654                 rb->set_name ("OptionEditorToggleButton");
655                 if (n == 0) {
656                         mtc_button_group = rb->get_group();
657                 } else {
658                         rb->set_group (mtc_button_group);
659
660                 }
661                 rb->show ();
662                 midi_port_table_widgets.push_back (rb);
663                 midi_port_table.attach (*rb, 4, 5, n+2, n+3, FILL|EXPAND, FILL);
664                 rb->signal_toggled().connect (bind (mem_fun(*this, &OptionEditor::mtc_port_chosen), (*i).second, rb, bb));
665
666                 if (session && i->second == session->mtc_port()) {
667                         rb->set_active (true);
668                 }
669                 
670                 rb = manage (new RadioButton ());
671                 rb->set_name ("OptionEditorToggleButton");
672                 if (n == 0) {
673                         mmc_button_group = rb->get_group();
674                 } else {
675                         rb->set_group (mmc_button_group);
676                 }
677                 rb->show ();
678                 midi_port_table_widgets.push_back (rb);
679                 midi_port_table.attach (*rb, 6, 7, n+2, n+3, FILL|EXPAND, FILL);
680                 rb->signal_toggled().connect (bind (mem_fun(*this, &OptionEditor::mmc_port_chosen), (*i).second, rb, bb));
681
682                 if (session && i->second == session->mmc_port()) {
683                         rb->set_active (true);
684                 }
685
686                 rb = manage (new RadioButton ());
687                 rb->set_name ("OptionEditorToggleButton");
688                 if (n == 0) {
689                         midi_button_group = rb->get_group();
690                 } else {
691                         rb->set_group (midi_button_group);
692                 }
693                 rb->show ();
694                 midi_port_table_widgets.push_back (rb);
695                 midi_port_table.attach (*rb, 8, 9, n+2, n+3, FILL|EXPAND, FILL);
696                 rb->signal_toggled().connect (bind (mem_fun(*this, &OptionEditor::midi_port_chosen), (*i).second, rb, bb));
697
698                 if (session && i->second == session->midi_port()) {
699                         rb->set_active (true);
700                 }
701
702         }
703
704         midi_port_table.show();
705 }
706
707 void
708 OptionEditor::remove_midi_port (MIDI::Port* port)
709 {
710         MIDI::Manager::instance()->remove_port (port);
711         redisplay_midi_ports ();
712 }
713
714 void
715 OptionEditor::add_midi_port ()
716 {
717         MidiPortDialog dialog;
718
719         dialog.set_position (WIN_POS_MOUSE);
720         dialog.set_transient_for (*this);
721
722         dialog.show ();
723
724         int ret = dialog.run ();
725
726         switch (ret) {
727         case RESPONSE_ACCEPT:
728                 break;
729         default:
730                 return;
731                 break;
732         }
733
734         Glib::ustring mode = dialog.port_mode_combo.get_active_text();
735         std::string smod;
736
737         if (mode == _("input")) {
738                 smod = X_("input");
739         } else if (mode == (_("output"))) {
740                 smod = X_("output");
741         } else {
742                 smod = "duplex";
743         }
744
745
746         XMLNode node (X_("MIDI-port"));
747
748         node.add_property ("tag", dialog.port_name.get_text());
749         node.add_property ("device", X_("ardour")); // XXX this can't be right for all types
750         node.add_property ("type", MIDI::PortFactory::default_port_type());
751         node.add_property ("mode", smod);
752
753         if (MIDI::Manager::instance()->add_port (node) != 0) {
754                 redisplay_midi_ports ();
755         }
756 }
757
758 bool
759 OptionEditor::port_removable (MIDI::Port *port)
760 {
761         if (!session) {
762                 return true;
763         }
764
765         if (port == session->mtc_port() ||
766             port == session->mmc_port() ||
767             port == session->midi_port()) {
768                 return false;
769         }
770         return true;
771 }
772
773 void
774 OptionEditor::mtc_port_chosen (MIDI::Port *port, Gtk::RadioButton* rb, Gtk::Button* bb) 
775 {
776         if (session) {
777                 if (rb->get_active()) {
778                         session->set_mtc_port (port->name());
779                         Config->set_mtc_port_name (port->name());
780                 } else {
781                         session->set_mtc_port ("");
782                 }
783                 bb->set_sensitive (port_removable (port));
784         }
785 }
786
787 void
788 OptionEditor::mmc_port_chosen (MIDI::Port* port, Gtk::RadioButton* rb, Gtk::Button* bb)
789 {
790         if (session) {
791                 if (rb->get_active()) {
792                         session->set_mmc_port (port->name());
793                         Config->set_mtc_port_name (port->name());
794                 } else {
795                         session->set_mmc_port ("");
796                 }
797                 bb->set_sensitive (port_removable (port));
798         }
799 }
800
801 void
802 OptionEditor::midi_port_chosen (MIDI::Port* port, Gtk::RadioButton* rb, Gtk::Button* bb)
803 {
804         if (session) {
805                 if (rb->get_active()) {
806                         session->set_midi_port (port->name());
807                         Config->set_midi_port_name (port->name());
808                 } else {
809                         session->set_midi_port ("");
810                 }
811                 bb->set_sensitive (port_removable (port));
812         }
813 }
814
815 void
816 OptionEditor::port_online_toggled (MIDI::Port* port, ToggleButton* tb)
817 {
818         bool wanted = tb->get_active();
819
820         if (port->input()) {
821                 if (wanted != port->input()->offline()) {
822                         port->input()->set_offline (wanted);
823                 } 
824         }
825 }
826
827 void
828 OptionEditor::map_port_online (MIDI::Port* port, ToggleButton* tb)
829 {
830         bool bstate = tb->get_active ();
831         
832         if (port->input()) {
833                 if (bstate != port->input()->offline()) {
834                         if (port->input()->offline()) {
835                                 tb->set_label (_("offline"));
836                                 tb->set_active (false);
837                         } else {
838                                 tb->set_label (_("online"));
839                                 tb->set_active (true);
840                         }
841                 }
842         }
843 }
844
845 void
846 OptionEditor::mmc_receive_device_id_adjusted ()
847 {
848         uint8_t id = (uint8_t) mmc_receive_device_id_spinner.get_value();
849         Config->set_mmc_receive_device_id (id);
850 }
851
852 void
853 OptionEditor::mmc_send_device_id_adjusted ()
854 {
855         uint8_t id = (uint8_t) mmc_send_device_id_spinner.get_value();
856         Config->set_mmc_send_device_id (id);
857 }
858
859 void
860 OptionEditor::port_trace_in_toggled (MIDI::Port* port, ToggleButton* tb)
861 {
862         bool trace = tb->get_active();
863
864         if (port->input()) {
865                 if (port->input()->tracing() != trace) {
866                         port->input()->trace (trace, &cerr, string (port->name()) + string (" input: "));
867                 }
868         }
869 }
870
871 void
872 OptionEditor::port_trace_out_toggled (MIDI::Port* port, ToggleButton* tb)
873 {
874         bool trace = tb->get_active();
875
876         if (port->output()) {
877                 if (port->output()->tracing() != trace) {
878                         port->output()->trace (trace, &cerr, string (port->name()) + string (" output: "));
879                 }
880         }
881 }
882
883 void
884 OptionEditor::save ()
885 {
886         /* XXX a bit odd that we save the entire session state here */
887
888         ui.save_state ("");
889 }
890
891 gint
892 OptionEditor::wm_close (GdkEventAny *ev)
893 {
894         save ();
895         hide ();
896         return TRUE;
897 }
898
899 void
900 OptionEditor::raid_path_changed ()
901 {
902         if (session) {
903                 Config->set_raid_path (session_raid_entry.get_text());
904         }
905 }
906
907 void
908 OptionEditor::click_browse_clicked ()
909 {
910         SoundFileChooser sfdb (*this, _("Choose Click"), session);
911         
912         sfdb.show_all ();
913         sfdb.present ();
914
915         int result = sfdb.run ();
916  
917         if (result == Gtk::RESPONSE_OK) {
918                 click_chosen(sfdb.get_filename());
919         }
920 }
921
922 void
923 OptionEditor::click_chosen (const string & path)
924 {
925         click_path_entry.set_text (path);
926         click_sound_changed ();
927 }
928
929 void
930 OptionEditor::click_emphasis_browse_clicked ()
931 {
932         SoundFileChooser sfdb (*this, _("Choose Click Emphasis"), session);
933
934         sfdb.show_all ();
935         sfdb.present ();
936
937         int result = sfdb.run ();
938
939         if (result == Gtk::RESPONSE_OK) {
940                 click_emphasis_chosen (sfdb.get_filename());
941         }
942 }
943
944 void
945 OptionEditor::click_emphasis_chosen (const string & path)
946 {       
947         click_emphasis_path_entry.set_text (path);
948         click_emphasis_sound_changed ();
949 }
950
951 void
952 OptionEditor::click_sound_changed ()
953 {
954         if (session) {
955                 string path = click_path_entry.get_text();
956
957                 if (path == Config->get_click_sound()) {
958                         return;
959                 }
960
961                 strip_whitespace_edges (path);
962
963                 if (path == _("internal")) {
964                         Config->set_click_sound ("");
965                 } else {
966                         Config->set_click_sound (path);
967                 }
968         }
969 }
970
971 void
972 OptionEditor::click_emphasis_sound_changed ()
973 {
974         if (session) {
975                 string path = click_emphasis_path_entry.get_text();
976
977                 if (path == Config->get_click_emphasis_sound()) {
978                         return;
979                 }
980
981                 strip_whitespace_edges (path);
982
983                 if (path == _("internal")) {
984                         Config->set_click_emphasis_sound ("");
985                 } else {
986                         Config->set_click_emphasis_sound (path);
987                 }
988         }
989 }
990
991 void
992 OptionEditor::clear_click_editor ()
993 {
994         if (click_io_selector) {
995                 click_packer.remove (*click_io_selector);
996                 click_packer.remove (*click_gpm);
997                 delete click_io_selector;
998                 delete click_gpm;
999                 click_io_selector = 0;
1000                 click_gpm = 0;
1001         }
1002 }
1003
1004 void
1005 OptionEditor::setup_click_editor ()
1006 {
1007         Label* label;
1008         HBox* hpacker = manage (new HBox);
1009
1010         click_path_entry.set_sensitive (true);
1011         click_emphasis_path_entry.set_sensitive (true);
1012
1013         click_path_entry.set_name ("OptionsEntry");
1014         click_emphasis_path_entry.set_name ("OptionsEntry");
1015         
1016         click_path_entry.signal_activate().connect (mem_fun(*this, &OptionEditor::click_sound_changed));
1017         click_emphasis_path_entry.signal_activate().connect (mem_fun(*this, &OptionEditor::click_emphasis_sound_changed));
1018
1019         click_path_entry.signal_focus_out_event().connect (bind (mem_fun(*this, &OptionEditor::focus_out_event_handler), &OptionEditor::click_sound_changed));
1020         click_emphasis_path_entry.signal_focus_out_event().connect (bind (mem_fun(*this, &OptionEditor::focus_out_event_handler), &OptionEditor::click_emphasis_sound_changed));
1021
1022         click_browse_button.set_name ("EditorGTKButton");
1023         click_emphasis_browse_button.set_name ("EditorGTKButton");
1024         click_browse_button.signal_clicked().connect (mem_fun(*this, &OptionEditor::click_browse_clicked));
1025         click_emphasis_browse_button.signal_clicked().connect (mem_fun(*this, &OptionEditor::click_emphasis_browse_clicked));
1026
1027         click_packer.set_border_width (12);
1028         click_packer.set_spacing (5);
1029
1030         click_io_selector = new IOSelector (*session, session->click_io(), false);
1031         click_gpm = new GainMeter (session->click_io(), *session);
1032
1033         click_table.set_col_spacings (10);
1034         
1035         label = manage(new Label(_("Click audio file")));
1036         label->set_name ("OptionsLabel");
1037         click_table.attach (*label, 0, 1, 0, 1, FILL|EXPAND, FILL);
1038         click_table.attach (click_path_entry, 1, 2, 0, 1, Gtk::FILL|Gtk::EXPAND, FILL);
1039         click_table.attach (click_browse_button, 2, 3, 0, 1, FILL|EXPAND, FILL);
1040         
1041         label = manage(new Label(_("Click emphasis audiofile")));
1042         label->set_name ("OptionsLabel");
1043         click_table.attach (*label, 0, 1, 1, 2, FILL|EXPAND, FILL);
1044         click_table.attach (click_emphasis_path_entry, 1, 2, 1, 2, Gtk::FILL|Gtk::EXPAND, FILL);
1045         click_table.attach (click_emphasis_browse_button, 2, 3, 1, 2, FILL|EXPAND, FILL);
1046
1047         hpacker->set_spacing (10);
1048         hpacker->pack_start (*click_io_selector, false, false);
1049         hpacker->pack_start (*click_gpm, false, false);
1050
1051         click_packer.pack_start (click_table, false, false);
1052         click_packer.pack_start (*hpacker, false, false);
1053
1054         click_packer.show_all ();
1055 }
1056
1057 void
1058 OptionEditor::clear_auditioner_editor ()
1059 {
1060         if (auditioner_io_selector) {
1061                 audition_hpacker.remove (*auditioner_io_selector);
1062                 audition_hpacker.remove (*auditioner_gpm);
1063                 delete auditioner_io_selector;
1064                 delete auditioner_gpm;
1065                 auditioner_io_selector = 0;
1066                 auditioner_gpm = 0;
1067         }
1068 }
1069
1070 void
1071 OptionEditor::setup_auditioner_editor ()
1072 {
1073         audition_packer.set_border_width (12);
1074         audition_packer.set_spacing (5);
1075         audition_hpacker.set_spacing (10);
1076
1077         audition_label.set_name ("OptionEditorAuditionerLabel");
1078         audition_label.set_text (_("The auditioner is a dedicated mixer strip used\n"
1079                                    "for listening to specific regions outside the context\n"
1080                                    "of the overall mix. It can be connected just like any\n"
1081                                    "other mixer strip."));
1082         
1083         audition_packer.pack_start (audition_label, false, false, 10);
1084         audition_packer.pack_start (audition_hpacker, false, false);
1085 }
1086
1087 void
1088 OptionEditor::connect_audition_editor ()
1089 {
1090         auditioner_io_selector = new IOSelector (*session, session->the_auditioner(), false);
1091         auditioner_gpm = new GainMeter (session->the_auditioner(), *session);
1092
1093         audition_hpacker.pack_start (*auditioner_io_selector, false, false);
1094         audition_hpacker.pack_start (*auditioner_gpm, false, false);
1095
1096         auditioner_io_selector->show_all ();
1097         auditioner_gpm->show_all ();
1098 }
1099
1100 bool
1101 OptionEditor::focus_out_event_handler (GdkEventFocus* ev, void (OptionEditor::*pmf)()) 
1102 {
1103         (this->*pmf)();
1104         return false;
1105 }
1106
1107 static const struct {
1108     const char *name;
1109     guint   modifier;
1110 } modifiers[] = {
1111
1112 #ifdef GTKOSX 
1113
1114         /* Command = Meta
1115            Option/Alt = Mod1
1116         */
1117
1118         { "Shift", GDK_SHIFT_MASK },
1119         { "Command", GDK_META_MASK },
1120         { "Control", GDK_CONTROL_MASK },
1121         { "Option", GDK_MOD1_MASK },
1122         { "Command-Shift", GDK_MOD1_MASK|GDK_SHIFT_MASK },
1123         { "Command-Option", GDK_MOD1_MASK|GDK_MOD5_MASK },
1124         { "Shift-Option", GDK_SHIFT_MASK|GDK_MOD5_MASK },
1125         { "Shift-Command-Option", GDK_MOD5_MASK|GDK_SHIFT_MASK|GDK_MOD1_MASK },
1126
1127 #else
1128         { "Shift", GDK_SHIFT_MASK },
1129         { "Control", GDK_CONTROL_MASK },
1130         { "Alt (Mod1)", GDK_MOD1_MASK },
1131         { "Control-Shift", GDK_CONTROL_MASK|GDK_SHIFT_MASK },
1132         { "Control-Alt", GDK_CONTROL_MASK|GDK_MOD1_MASK },
1133         { "Shift-Alt", GDK_SHIFT_MASK|GDK_MOD1_MASK },
1134         { "Control-Shift-Alt", GDK_CONTROL_MASK|GDK_SHIFT_MASK|GDK_MOD1_MASK },
1135         { "Mod2", GDK_MOD2_MASK },
1136         { "Mod3", GDK_MOD3_MASK },
1137         { "Mod4", GDK_MOD4_MASK },
1138         { "Mod5", GDK_MOD5_MASK },
1139 #endif
1140         { 0, 0 }
1141 };
1142
1143 void
1144 OptionEditor::setup_keyboard_options ()
1145 {
1146         vector<string> dumb;
1147         Label* label;
1148
1149         keyboard_mouse_table.set_border_width (12);
1150         keyboard_mouse_table.set_row_spacings (5);
1151         keyboard_mouse_table.set_col_spacings (5);
1152
1153         /* internationalize and prepare for use with combos */
1154
1155         for (int i = 0; modifiers[i].name; ++i) {
1156                 dumb.push_back (_(modifiers[i].name));
1157         }
1158
1159         set_popdown_strings (edit_modifier_combo, dumb);
1160         edit_modifier_combo.signal_changed().connect (mem_fun(*this, &OptionEditor::edit_modifier_chosen));
1161
1162         for (int x = 0; modifiers[x].name; ++x) {
1163                 if (modifiers[x].modifier == Keyboard::edit_modifier ()) {
1164                         edit_modifier_combo.set_active_text (_(modifiers[x].name));
1165                         break;
1166                 }
1167         }
1168
1169         label = manage (new Label (_("Edit using")));
1170         label->set_name ("OptionsLabel");
1171         label->set_alignment (1.0, 0.5);
1172                 
1173         keyboard_mouse_table.attach (*label, 0, 1, 0, 1, Gtk::FILL|Gtk::EXPAND, FILL);
1174         keyboard_mouse_table.attach (edit_modifier_combo, 1, 2, 0, 1, Gtk::FILL|Gtk::EXPAND, FILL);
1175
1176         label = manage (new Label (_("+ button")));
1177         label->set_name ("OptionsLabel");
1178         
1179         keyboard_mouse_table.attach (*label, 3, 4, 0, 1, Gtk::FILL|Gtk::EXPAND, FILL);
1180         keyboard_mouse_table.attach (edit_button_spin, 4, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, FILL);
1181
1182         edit_button_spin.set_name ("OptionsEntry");
1183         edit_button_adjustment.set_value (Keyboard::edit_button());
1184         edit_button_adjustment.signal_value_changed().connect (mem_fun(*this, &OptionEditor::edit_button_changed));
1185
1186         set_popdown_strings (delete_modifier_combo, dumb);
1187         delete_modifier_combo.signal_changed().connect (mem_fun(*this, &OptionEditor::delete_modifier_chosen));
1188
1189         for (int x = 0; modifiers[x].name; ++x) {
1190                 if (modifiers[x].modifier == Keyboard::delete_modifier ()) {
1191                         delete_modifier_combo.set_active_text (_(modifiers[x].name));
1192                         break;
1193                 }
1194         }
1195
1196         label = manage (new Label (_("Delete using")));
1197         label->set_name ("OptionsLabel");
1198         label->set_alignment (1.0, 0.5);
1199                 
1200         keyboard_mouse_table.attach (*label, 0, 1, 1, 2, Gtk::FILL|Gtk::EXPAND, FILL);
1201         keyboard_mouse_table.attach (delete_modifier_combo, 1, 2, 1, 2, Gtk::FILL|Gtk::EXPAND, FILL);
1202
1203         label = manage (new Label (_("+ button")));
1204         label->set_name ("OptionsLabel");
1205
1206         keyboard_mouse_table.attach (*label, 3, 4, 1, 2, Gtk::FILL|Gtk::EXPAND, FILL);
1207         keyboard_mouse_table.attach (delete_button_spin, 4, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, FILL);
1208
1209         delete_button_spin.set_name ("OptionsEntry");
1210         delete_button_adjustment.set_value (Keyboard::delete_button());
1211         delete_button_adjustment.signal_value_changed().connect (mem_fun(*this, &OptionEditor::delete_button_changed));
1212
1213         set_popdown_strings (snap_modifier_combo, dumb);
1214         snap_modifier_combo.signal_changed().connect (mem_fun(*this, &OptionEditor::snap_modifier_chosen));
1215         
1216         for (int x = 0; modifiers[x].name; ++x) {
1217                 if (modifiers[x].modifier == (guint) Keyboard::snap_modifier ()) {
1218                         snap_modifier_combo.set_active_text (_(modifiers[x].name));
1219                         break;
1220                 }
1221         }
1222
1223         label = manage (new Label (_("Ignore snap using")));
1224         label->set_name ("OptionsLabel");
1225         label->set_alignment (1.0, 0.5);
1226         
1227         keyboard_mouse_table.attach (*label, 0, 1, 2, 3, Gtk::FILL|Gtk::EXPAND, FILL);
1228         keyboard_mouse_table.attach (snap_modifier_combo, 1, 2, 2, 3, Gtk::FILL|Gtk::EXPAND, FILL);
1229
1230         vector<string> strs;
1231         
1232         for (std::map<std::string,std::string>::iterator bf = Keyboard::binding_files.begin(); bf != Keyboard::binding_files.end(); ++bf) {
1233                 strs.push_back (bf->first);
1234         }
1235         
1236         set_popdown_strings (keyboard_layout_selector, strs);
1237         keyboard_layout_selector.set_active_text (Keyboard::current_binding_name());
1238         keyboard_layout_selector.signal_changed().connect (mem_fun (*this, &OptionEditor::bindings_changed));
1239
1240         label = manage (new Label (_("Keyboard layout")));
1241         label->set_name ("OptionsLabel");
1242         label->set_alignment (1.0, 0.5);
1243
1244         keyboard_mouse_table.attach (*label, 0, 1, 3, 4, Gtk::FILL|Gtk::EXPAND, FILL);
1245         keyboard_mouse_table.attach (keyboard_layout_selector, 1, 2, 3, 4, Gtk::FILL|Gtk::EXPAND, FILL);
1246 }
1247
1248 void
1249 OptionEditor::bindings_changed ()
1250 {
1251         string txt;
1252         
1253         txt = keyboard_layout_selector.get_active_text();
1254
1255         for (std::map<string,string>::iterator i = Keyboard::binding_files.begin(); i != Keyboard::binding_files.end(); ++i) {
1256                 if (txt == i->first) {
1257                         if (Keyboard::load_keybindings (i->second)) {
1258                                 Keyboard::save_keybindings ();
1259                         }
1260                 }
1261         }
1262 }
1263
1264 void
1265 OptionEditor::edit_modifier_chosen ()
1266 {
1267         string txt;
1268         
1269         txt = edit_modifier_combo.get_active_text();
1270
1271         for (int i = 0; modifiers[i].name; ++i) {
1272                 if (txt == _(modifiers[i].name)) {
1273                         Keyboard::set_edit_modifier (modifiers[i].modifier);
1274                         break;
1275                 }
1276         }
1277 }
1278
1279 void
1280 OptionEditor::delete_modifier_chosen ()
1281 {
1282         string txt;
1283         
1284         txt = delete_modifier_combo.get_active_text();
1285
1286         for (int i = 0; modifiers[i].name; ++i) {
1287                 if (txt == _(modifiers[i].name)) {
1288                         Keyboard::set_delete_modifier (modifiers[i].modifier);
1289                         break;
1290                 }
1291         }
1292 }
1293
1294 void
1295 OptionEditor::snap_modifier_chosen ()
1296 {
1297         string txt;
1298         
1299         txt = snap_modifier_combo.get_active_text();
1300
1301         for (int i = 0; modifiers[i].name; ++i) {
1302                 if (txt == _(modifiers[i].name)) {
1303                         Keyboard::set_snap_modifier (modifiers[i].modifier);
1304                         break;
1305                 }
1306         }
1307 }
1308
1309 void
1310 OptionEditor::delete_button_changed ()
1311 {
1312         Keyboard::set_delete_button ((guint) delete_button_adjustment.get_value());
1313 }
1314
1315 void
1316 OptionEditor::edit_button_changed ()
1317 {
1318         Keyboard::set_edit_button ((guint) edit_button_adjustment.get_value());
1319 }
1320
1321 void
1322 OptionEditor::fixup_combo_size (Gtk::ComboBoxText& combo, vector<string>& strings)
1323 {
1324         /* find the widest string */
1325
1326         string::size_type maxlen = 0;
1327         string maxstring;
1328
1329         for (vector<string>::iterator i = strings.begin(); i != strings.end(); ++i) {
1330                 string::size_type l;
1331
1332                 if ((l = (*i).length()) > maxlen) {
1333                         maxlen = l;
1334                         maxstring = *i;
1335                 }
1336         }
1337
1338         /* try to include ascenders and descenders */
1339
1340         if (maxstring.length() > 2) {
1341                 maxstring[0] = 'g';
1342                 maxstring[1] = 'l';
1343         }
1344
1345         const guint32 FUDGE = 10; // Combo's are stupid - they steal space from the entry for the button
1346
1347         set_size_request_to_display_given_text (combo, maxstring.c_str(), 10 + FUDGE, 10);
1348 }
1349
1350 void
1351 OptionEditor::parameter_changed (const char* parameter_name)
1352 {
1353         ENSURE_GUI_THREAD (bind (mem_fun (*this, &OptionEditor::parameter_changed), parameter_name));
1354
1355 #define PARAM_IS(x) (!strcmp (parameter_name, (x)))
1356         
1357         if (PARAM_IS ("timecode-source-is-synced")) {
1358                 synced_timecode_button.set_active (Config->get_timecode_source_is_synced());
1359         } else if (PARAM_IS ("history-depth")) {
1360                 int32_t depth = Config->get_history_depth();
1361                 
1362                 history_depth.set_value (depth);
1363                 history_depth_spinner.set_sensitive (depth != 0);
1364                 limit_history_button.set_active (depth != 0);
1365
1366         } else if (PARAM_IS ("saved-history-depth")) {
1367
1368                 saved_history_depth.set_value (Config->get_saved_history_depth());
1369
1370         } else if (PARAM_IS ("save-history")) {
1371
1372                 bool x = Config->get_save_history();
1373
1374                 save_history_button.set_active (x);
1375                 saved_history_depth_spinner.set_sensitive (x);
1376         } else if (PARAM_IS ("font-scale")) {
1377                 reset_dpi();
1378         }
1379 }