replaced slot() with mem_fun() and ptr_fun().
[ardour.git] / gtk2_ardour / option_editor.cc
1 /*
2     Copyright (C) 2001 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     $Id$
19 */
20
21 #include <ardour/session.h>
22 #include <ardour/audioengine.h>
23 #include <ardour/configuration.h>
24 #include <ardour/auditioner.h>
25 #include <ardour/crossfade.h>
26 #include <midi++/manager.h>
27 #include <gtkmm2ext/stop_signal.h>
28 #include <gtkmm2ext/utils.h>
29
30 #include "public_editor.h"
31 #include "mixer_ui.h"
32 #include "ardour_ui.h"
33 #include "io_selector.h"
34 #include "gain_meter.h"
35 #include "library_ui.h"
36 #include "utils.h"
37 #include "editing.h"
38 #include "option_editor.h"
39
40 #include "i18n.h"
41
42 using namespace ARDOUR;
43 using namespace Gtk;
44 using namespace Editing;
45 using namespace Gtkmm2ext;
46
47 static const gchar *psync_strings[] = {
48         N_("Internal"),
49         N_("Slave to MTC"),
50         N_("Sync with JACK"),
51         N_("never used but stops crashes"),
52         0
53 };
54
55 static const gchar *lmode_strings[] = {
56         N_("Later regions are higher"),
57         N_("Most recently added/moved/trimmed regions are higher"),
58         N_("Most recently added regions are higher"),
59         0
60 };
61
62 static const gchar *xfl_strings[] = {
63         N_("Span entire region overlap"),
64         N_("Short fades at the start of the overlap"),
65         0
66 };
67
68 static vector<string> positional_sync_strings;
69 static vector<string> layer_mode_strings;
70 static vector<string> xfade_model_strings;
71
72 OptionEditor::OptionEditor (ARDOUR_UI& uip, PublicEditor& ed, Mixer_UI& mixui)
73         : ArdourDialog ("option editor"),
74           ui (uip),
75           editor (ed),
76           mixer (mixui),
77
78           path_table (9, 2),
79
80           /* Fades */
81
82           auto_xfade_button (_("Automatically create crossfades")),
83           xfade_active_button (_("New full-overlap crossfades are unmuted")),
84           layer_mode_label (_("Region layering mode")),
85           xfade_model_label (_("Crossfade model")),
86           short_xfade_adjustment (0, 1.0, 500.0, 5.0, 100.0),
87           short_xfade_slider (short_xfade_adjustment),
88
89           /* solo */
90           solo_latched_button (_("Latched solo")),
91           solo_via_bus_button (_("Solo via bus")),
92
93           /* display */
94
95           show_waveforms_button (_("Show waveforms")),
96           show_waveforms_recording_button (_("Show waveforms while recording")),
97           mixer_strip_width_button (_("Narrow mixer strips")),
98           show_measures_button (_("Show measure lines")),
99           follow_playhead_button (_("Follow playhead")),
100           
101           /* Sync */
102
103           send_mtc_button (_("Send MTC")),
104           send_mmc_button (_("Send MMC")),
105           jack_time_master_button (_("JACK time master")),
106           smpte_offset_clock (X_("SMPTEOffsetClock"), true, true),
107           smpte_offset_negative_button (_("SMPTE offset is negative")),
108
109           /* MIDI */
110
111           midi_feedback_button (_("Send MIDI parameter feedback")),
112           midi_control_button (_("MIDI parameter control")),
113           mmc_control_button (_("MMC control")),
114           
115           /* Click */
116
117           click_table (2, 3),
118           click_browse_button (_("Browse")),
119           click_emphasis_browse_button (_("Browse")),
120
121           /* kbd/mouse */
122
123           keyboard_mouse_table (3, 4),
124           delete_button_adjustment (3, 1, 5),
125           delete_button_spin (delete_button_adjustment),
126           edit_button_adjustment (3, 1, 5),
127           edit_button_spin (edit_button_adjustment),
128
129           /* Misc */
130
131           auto_connect_inputs_button (_("Auto-connect new track inputs to hardware")),
132           auto_connect_output_physical_button (_("Auto-connect new track outputs to hardware")),
133           auto_connect_output_master_button (_("Auto-connect new track outputs to master bus")),
134           auto_connect_output_manual_button (_("Manually connect new track outputs")),
135           hw_monitor_button(_("Use Hardware Monitoring")),
136           sw_monitor_button(_("Use Software Monitoring")),
137           plugins_stop_button (_("Stop plugins with transport")),
138           plugins_on_rec_button (_("Run plugins while recording")),
139           verify_remove_last_capture_button (_("Verify remove last capture")),
140           stop_rec_on_xrun_button (_("Stop recording on xrun")),
141           stop_at_end_button (_("Stop transport at end of session")),
142           debug_keyboard_button (_("Debug keyboard events")),
143           speed_quieten_button (_("-12dB gain reduction for ffwd/rew"))
144           
145 {
146         using namespace Notebook_Helpers;
147
148         click_io_selector = 0;
149         auditioner_io_selector = 0;
150
151         set_default_size (300, 300);
152         set_title (_("ardour: options editor"));
153         set_wmclass (_("ardour_option_editor"), "Ardour");
154
155         set_name ("OptionsWindow");
156         add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
157         
158         layer_mode_label.set_name ("OptionsLabel");
159         xfade_model_label.set_name ("OptionsLabel");
160         
161         VBox *vbox = manage(new VBox);
162         add (*vbox);
163         set_border_width (3);
164
165         vbox->set_spacing (4);
166         vbox->pack_start(notebook);
167
168         delete_event.connect (mem_fun(*this, &OptionEditor::wm_close));
169
170         notebook.set_show_tabs (true);
171         notebook.set_show_border (true);
172         notebook.set_name ("OptionsNotebook");
173
174         setup_sync_options();
175         setup_path_options();
176         setup_fade_options ();
177         setup_solo_options ();
178         setup_display_options ();
179         setup_misc_options ();
180         setup_keyboard_options ();
181         setup_auditioner_editor ();
182
183         notebook.pages().push_back (TabElem (misc_packer, _("Misc")));
184         notebook.pages().push_back (TabElem (sync_packer, _("Sync")));
185         notebook.pages().push_back (TabElem (path_table, _("Paths/Files")));
186         notebook.pages().push_back (TabElem (display_packer, _("Display")));
187         notebook.pages().push_back (TabElem (keyboard_mouse_table, _("Kbd/Mouse")));
188         notebook.pages().push_back (TabElem (click_packer, _("Click")));
189         notebook.pages().push_back (TabElem (audition_packer, _("Audition")));
190         notebook.pages().push_back (TabElem (fade_packer, _("Layers & Fades")));
191         notebook.pages().push_back (TabElem (solo_packer, _("Solo")));
192
193         if (!MIDI::Manager::instance()->get_midi_ports().empty()) {
194                 setup_midi_options ();
195                 notebook.pages().push_back (TabElem (midi_packer, _("MIDI")));
196         }
197
198         set_session (0);
199 }
200
201 void
202 OptionEditor::set_session (Session *s)
203 {
204         clear_click_editor ();
205         clear_auditioner_editor ();
206
207         click_path_entry.set_text ("");
208         click_emphasis_path_entry.set_text ("");
209         session_raid_entry.set_text ("");
210
211         send_mtc_button.set_sensitive (false);
212         send_mmc_button.set_sensitive (false);
213         midi_feedback_button.set_sensitive (false);
214         midi_control_button.set_sensitive (false);
215         mmc_control_button.set_sensitive (false);
216         click_path_entry.set_sensitive (false);
217         click_emphasis_path_entry.set_sensitive (false);
218         session_raid_entry.set_sensitive (false);
219         plugins_on_rec_button.set_sensitive (false);
220         verify_remove_last_capture_button.set_sensitive (false);
221         slave_type_combo.set_sensitive (false);
222         solo_latched_button.set_sensitive (false);
223         solo_via_bus_button.set_sensitive (false);
224         smpte_fps_combo.set_sensitive (false);
225         meter_hold_combo.set_sensitive (false);
226         meter_falloff_combo.set_sensitive (false);
227         auto_connect_inputs_button.set_sensitive (false);
228         auto_connect_output_physical_button.set_sensitive (false);
229         auto_connect_output_master_button.set_sensitive (false);
230         auto_connect_output_manual_button.set_sensitive (false);
231         layer_mode_combo.set_sensitive (false);
232         short_xfade_slider.set_sensitive (false);
233         smpte_offset_negative_button.set_sensitive (false);
234
235         smpte_offset_clock.set_session (s);
236
237         if ((session = s) == 0) {
238                 return;
239         }
240
241         send_mtc_button.set_sensitive (true);
242         send_mmc_button.set_sensitive (true);
243         midi_feedback_button.set_sensitive (true);
244         midi_control_button.set_sensitive (true);
245         mmc_control_button.set_sensitive (true);
246         click_path_entry.set_sensitive (true);
247         click_emphasis_path_entry.set_sensitive (true);
248         session_raid_entry.set_sensitive (true);
249         plugins_on_rec_button.set_sensitive (true);
250         verify_remove_last_capture_button.set_sensitive (true);
251         slave_type_combo.set_sensitive (true);
252         solo_latched_button.set_sensitive (true);
253         solo_via_bus_button.set_sensitive (true);
254         smpte_fps_combo.set_sensitive (true);
255         meter_hold_combo.set_sensitive (true);
256         meter_falloff_combo.set_sensitive (true);
257         auto_connect_inputs_button.set_sensitive (true);
258         auto_connect_output_physical_button.set_sensitive (true);
259         auto_connect_output_master_button.set_sensitive (true);
260         auto_connect_output_manual_button.set_sensitive (true);
261         layer_mode_combo.set_sensitive (true);
262         short_xfade_slider.set_sensitive (true);
263         smpte_offset_negative_button.set_sensitive (true);
264
265         if (!s->smpte_drop_frames) {
266                 // non-drop frames
267                 if (s->smpte_frames_per_second == 24.0)
268                         smpte_fps_combo.get_entry ()->set_text (_("24 FPS"));
269                 else if (s->smpte_frames_per_second == 25.0)
270                         smpte_fps_combo.get_entry ()->set_text (_("25 FPS"));
271                 else if (s->smpte_frames_per_second == 30.0)
272                         smpte_fps_combo.get_entry ()->set_text (_("30 FPS"));
273                 else
274                         smpte_fps_combo.get_entry ()->set_text (_("???"));
275         } else {
276                 // drop frames
277                 if (floor(s->smpte_frames_per_second) == 29.0)
278                         smpte_fps_combo.get_entry ()->set_text (_("30 FPS drop"));
279                 else
280                         smpte_fps_combo.get_entry ()->set_text (_("???"));
281         }
282         
283         smpte_offset_clock.set_session (s);
284         smpte_offset_clock.set (s->smpte_offset (), true);
285
286         smpte_offset_negative_button.set_active (session->smpte_offset_negative());
287         send_mtc_button.set_active (session->get_send_mtc());
288
289         /* MIDI I/O */
290
291         send_mmc_button.set_active (session->get_send_mmc());
292         midi_control_button.set_active (session->get_midi_control());
293         midi_feedback_button.set_active (session->get_midi_feedback());
294         mmc_control_button.set_active (session->get_mmc_control());
295
296         /* set up port assignments */
297
298         map<MIDI::Port*,vector<RadioButton*> >::iterator res;
299
300         if (session->mtc_port()) {
301                 if ((res = port_toggle_buttons.find (session->mtc_port())) != port_toggle_buttons.end()) {
302                         (*res).second[MtcIndex]->set_active (true);
303                 }
304         } 
305
306         if (session->mmc_port ()) {
307                 if ((res = port_toggle_buttons.find (session->mmc_port())) != port_toggle_buttons.end()) {
308                         (*res).second[MmcIndex]->set_active (true);
309                 } 
310         }
311
312         if (session->midi_port()) {
313                 if ((res = port_toggle_buttons.find (session->midi_port())) != port_toggle_buttons.end()) {
314                         (*res).second[MidiIndex]->set_active (true);
315                 }
316         }
317
318         auto_connect_inputs_button.set_active (session->get_input_auto_connect());
319
320         Session::AutoConnectOption oac = session->get_output_auto_connect();
321         if (oac & Session::AutoConnectPhysical) {
322                 auto_connect_output_physical_button.set_active (true);
323         } else if (oac & Session::AutoConnectMaster) {
324                 auto_connect_output_master_button.set_active (true);
325         } else {
326                 auto_connect_output_manual_button.set_active (true);
327         }
328
329         setup_click_editor ();
330         connect_audition_editor ();
331
332         plugins_on_rec_button.set_active (session->get_recording_plugins ());
333         verify_remove_last_capture_button.set_active (Config->get_verify_remove_last_capture());
334
335         layer_mode_combo.get_entry()->set_text (layer_mode_strings[session->get_layer_model()]);
336         xfade_model_combo.get_entry()->set_text (xfade_model_strings[session->get_xfade_model()]);
337
338         short_xfade_adjustment.set_value ((Crossfade::short_xfade_length() / (float) session->frame_rate()) * 1000.0);
339
340         xfade_active_button.set_active (session->get_crossfades_active());
341         solo_latched_button.set_active (session->solo_latched());
342         solo_via_bus_button.set_active (session->solo_model() == Session::SoloBus);
343         
344         add_session_paths ();
345
346         vector<string> dumb;
347         dumb.push_back (positional_sync_strings[Session::None]);
348         dumb.push_back (positional_sync_strings[Session::JACK]);
349         if (session->mtc_port()) {
350                 dumb.push_back (positional_sync_strings[Session::MTC]);
351         } 
352         slave_type_combo.set_popdown_strings (dumb);
353
354         // meter stuff
355         if (session->meter_falloff() == 0.0f) {
356                 meter_falloff_combo.get_entry ()->set_text (_("Off"));
357         } else if (session->meter_falloff() <= 0.3f) {
358                 meter_falloff_combo.get_entry ()->set_text (_("Slowest"));
359         } else if (session->meter_falloff() <= 0.4f) {
360                 meter_falloff_combo.get_entry ()->set_text (_("Slow"));
361         } else if (session->meter_falloff() <= 0.8f) {
362                 meter_falloff_combo.get_entry ()->set_text (_("Medium"));
363         } else if (session->meter_falloff() <= 1.4f) {
364                 meter_falloff_combo.get_entry ()->set_text (_("Fast"));
365         } else if (session->meter_falloff() <= 2.0f) {
366                 meter_falloff_combo.get_entry ()->set_text (_("Faster"));
367         } else {
368                 meter_falloff_combo.get_entry ()->set_text (_("Fastest"));
369         }
370
371         switch ((int) floor (session->meter_hold())) {
372         case 0:
373                 meter_hold_combo.get_entry ()->set_text (_("Off"));
374                 break;
375         case 40:
376                 meter_hold_combo.get_entry ()->set_text (_("Short"));
377                 break;
378         case 100:
379                 meter_hold_combo.get_entry ()->set_text (_("Medium"));
380                 break;
381         case 200:
382                 meter_hold_combo.get_entry ()->set_text (_("Long"));
383                 break;
384         }
385         
386         session_control_changed (Session::SlaveType);
387         session_control_changed (Session::AlignChoice);
388         session->ControlChanged.connect (mem_fun(*this, &OptionEditor::queue_session_control_changed));
389 }
390
391 OptionEditor::~OptionEditor ()
392 {
393 }
394
395 static const gchar *native_format_strings[] = {
396         N_("Broadcast WAVE/floating point"),
397         N_("WAVE/floating point"),
398         0
399 };
400
401 void
402 OptionEditor::setup_path_options()
403 {
404         Gtk::Label* label;
405
406         path_table.set_homogeneous (true);
407         path_table.set_border_width (12);
408         path_table.set_row_spacings (5);
409
410         session_raid_entry.set_name ("OptionsEntry");
411
412         session_raid_entry.activate.connect (mem_fun(*this, &OptionEditor::raid_path_changed));
413
414         session_raid_entry.signal_focus_in_event().connect (mem_fun (Keyboard::the_keyboard(), &Keyboard::focus_in_handler));
415         session_raid_entry.signal_focus_out_event().connect (bind (mem_fun(*this, &OptionEditor::focus_out_event_handler), &OptionEditor::raid_path_changed));
416
417         label = manage(new Label(_("session RAID path")));
418         label->set_name ("OptionsLabel");
419         path_table.attach (*label, 0, 1, 0, 1, 0, 0);
420         path_table.attach (session_raid_entry, 1, 3, 0, 1, Gtk::FILL|Gtk::EXPAND, 0);
421
422         label = manage(new Label(_("Native Format")));
423         label->set_name ("OptionsLabel");
424         path_table.attach (*label, 0, 1, 1, 2, 0, 0);
425         path_table.attach (native_format_combo, 1, 3, 1, 2, Gtk::FILL|Gtk::EXPAND, 0);
426
427         vector<string> nfstrings = internationalize (native_format_strings);
428
429         native_format_combo.set_popdown_strings (nfstrings),
430         native_format_combo.get_entry()->set_editable (false);
431         native_format_combo.get_entry()->set_name ("OptionsEntry");
432         native_format_combo.set_use_arrows_always (true);
433         native_format_combo.get_popwin()->unmap_event.connect (mem_fun(*this, &OptionEditor::native_format_chosen));
434
435         fixup_combo_size (native_format_combo, nfstrings);
436
437         if (Config->get_native_format_is_bwf()) {
438                 native_format_combo.get_entry()->set_text (native_format_strings[0]);
439         } else {
440                 native_format_combo.get_entry()->set_text (native_format_strings[1]);
441         }
442         
443         path_table.show_all();
444 }
445
446 void
447 OptionEditor::add_session_paths ()
448 {
449         click_path_entry.set_sensitive (true);
450         click_emphasis_path_entry.set_sensitive (true);
451         session_raid_entry.set_sensitive (true);
452
453         if (session->click_sound.length() == 0) {
454                 click_path_entry.set_text (_("internal"));
455         } else {
456                 click_path_entry.set_text (session->click_sound);
457         }
458
459         if (session->click_emphasis_sound.length() == 0) {
460                 click_emphasis_path_entry.set_text (_("internal"));
461         } else {
462                 click_emphasis_path_entry.set_text (session->click_emphasis_sound);
463         }
464
465         session_raid_entry.set_text(session->raid_path());
466 }
467
468 void
469 OptionEditor::setup_fade_options ()
470 {
471         Gtk::HBox* hbox;
472         vector<string> dumb;
473         
474         auto_xfade_button.set_name ("OptionEditorToggleButton");
475         xfade_active_button.set_name ("OptionEditorToggleButton");
476
477         hbox = manage (new HBox);
478         hbox->set_border_width (12);
479         hbox->pack_start (auto_xfade_button, false, false);
480         fade_packer.pack_start (*hbox, false, false);
481
482         hbox = manage (new HBox);
483         hbox->set_border_width (12);
484         hbox->pack_start (xfade_active_button, false, false);
485         fade_packer.pack_start (*hbox, false, false);
486
487         layer_mode_strings = internationalize (lmode_strings);
488
489         dumb.push_back (lmode_strings[Session::LaterHigher]);
490         dumb.push_back (lmode_strings[Session::MoveAddHigher]);
491         dumb.push_back (lmode_strings[Session::AddHigher]);
492         layer_mode_combo.set_popdown_strings (dumb);
493
494         layer_mode_combo.set_use_arrows_always (true);
495         layer_mode_combo.set_value_in_list (true, false);
496         layer_mode_combo.get_entry()->set_editable (false);
497         layer_mode_combo.get_entry()->set_name ("OptionsEntry");
498         layer_mode_combo.get_popwin()->unmap_event.connect (mem_fun(*this, &OptionEditor::layer_mode_chosen));
499
500         fixup_combo_size (layer_mode_combo, layer_mode_strings);
501
502         hbox = manage (new HBox);
503         hbox->set_border_width (5);
504         hbox->set_spacing (10);
505         hbox->pack_start (layer_mode_label, false, false);
506         hbox->pack_start (layer_mode_combo, false, false);
507         fade_packer.pack_start (*hbox, false, false);
508
509         xfade_model_strings = internationalize (xfl_strings);
510
511         dumb.clear ();
512         dumb.push_back (xfade_model_strings[FullCrossfade]);
513         dumb.push_back (xfade_model_strings[ShortCrossfade]);
514         xfade_model_combo.set_popdown_strings (dumb);
515
516         xfade_model_combo.set_use_arrows_always (true);
517         xfade_model_combo.set_value_in_list (true, false);
518         xfade_model_combo.get_entry()->set_editable (false);
519         xfade_model_combo.get_entry()->set_name ("OptionsEntry");
520         xfade_model_combo.get_popwin()->unmap_event.connect (mem_fun(*this, &OptionEditor::xfade_model_chosen));
521
522         fixup_combo_size (xfade_model_combo, xfade_model_strings);
523
524         hbox = manage (new HBox);
525         hbox->set_border_width (5);
526         hbox->set_spacing (10);
527         hbox->pack_start (xfade_model_label, false, false);
528         hbox->pack_start (xfade_model_combo, false, false);
529         fade_packer.pack_start (*hbox, false, false);
530
531         auto_xfade_button.set_active (Config->get_auto_xfade());
532         /* xfade and layer mode active requires session */
533
534         auto_xfade_button.signal_clicked().connect (mem_fun(*this, &OptionEditor::auto_xfade_clicked));
535         xfade_active_button.signal_clicked().connect (mem_fun(*this, &OptionEditor::xfade_active_clicked));
536         
537         Label* short_xfade_label = manage (new Label (_("Short crossfade length (msecs)")));
538         short_xfade_label->set_name ("OptionsLabel");
539         
540         hbox = manage (new HBox);
541         hbox->set_border_width (5);
542         hbox->set_spacing (10);
543         hbox->pack_start (*short_xfade_label, false, false);
544         hbox->pack_start (short_xfade_slider, true, true);
545         fade_packer.pack_start (*hbox, false, false);
546
547         short_xfade_adjustment.value_changed.connect (mem_fun(*this, &OptionEditor::short_xfade_adjustment_changed));
548
549         fade_packer.show_all ();
550 }
551
552 void
553 OptionEditor::short_xfade_adjustment_changed ()
554 {
555         if (session) {
556                 float val = short_xfade_adjustment.get_value();
557                 
558                 /* val is in msecs */
559                 
560                 Crossfade::set_short_xfade_length ((jack_nframes_t) floor (session->frame_rate() * (val / 1000.0)));
561         }
562 }
563
564 gint
565 OptionEditor::layer_mode_chosen (GdkEventAny* ev)
566 {
567         if (!session) {
568                 return FALSE;
569         }
570
571         string which = layer_mode_combo.get_entry()->get_text ();
572
573         if (which == layer_mode_strings[Session::LaterHigher]) {
574                 session->set_layer_model (Session::LaterHigher);
575         } else if (which == layer_mode_strings[Session::MoveAddHigher]) {
576                 session->set_layer_model (Session::MoveAddHigher);
577         } else if (which == layer_mode_strings[Session::AddHigher]) {
578                 session->set_layer_model (Session::AddHigher);
579         }
580         return FALSE;
581 }
582
583 gint
584 OptionEditor::xfade_model_chosen (GdkEventAny* ev)
585 {
586         if (!session) {
587                 return FALSE;
588         }
589
590         string which = xfade_model_combo.get_entry()->get_text ();
591
592         if (which == xfade_model_strings[FullCrossfade]) {
593                 session->set_xfade_model (FullCrossfade);
594         } else if (which == xfade_model_strings[ShortCrossfade]) {
595                 session->set_xfade_model (ShortCrossfade);
596         }
597         return FALSE;
598 }
599
600 void
601 OptionEditor::auto_xfade_clicked ()
602 {
603         Config->set_auto_xfade (auto_xfade_button.get_active());
604 }
605
606 void
607 OptionEditor::xfade_active_clicked ()
608 {
609         if (session) {
610                 session->set_crossfades_active (xfade_active_button.get_active());
611         }
612 }
613
614 void
615 OptionEditor::setup_solo_options ()
616 {
617         Gtk::HBox* hbox;
618
619         solo_via_bus_button.set_name ("OptionEditorToggleButton");
620         solo_latched_button.set_name ("OptionEditorToggleButton");
621
622         hbox = manage (new HBox);
623         hbox->set_border_width (12);
624         hbox->pack_start (solo_via_bus_button, false, false);
625         solo_packer.pack_start (*hbox, false, false);
626
627         hbox = manage (new HBox);
628         hbox->set_border_width (12);
629         hbox->pack_start (solo_latched_button, false, false);
630         solo_packer.pack_start (*hbox, false, false);
631
632         solo_via_bus_button.signal_clicked().connect 
633                 (mem_fun(*this, &OptionEditor::solo_via_bus_clicked));
634         solo_latched_button.signal_clicked().connect 
635                 (mem_fun(*this, &OptionEditor::solo_latched_clicked));
636
637         solo_packer.show_all ();
638 }
639
640 void
641 OptionEditor::solo_via_bus_clicked ()
642 {
643         if (!session) {
644                 return;
645         }
646
647         if (solo_via_bus_button.get_active()) {
648                 session->set_solo_model (Session::SoloBus);
649         } else {
650                 session->set_solo_model (Session::InverseMute);
651         }
652 }
653
654 void
655 OptionEditor::solo_latched_clicked ()
656 {
657         if (!session) {
658                 return;
659         }
660
661         bool x = solo_latched_button.get_active();
662
663         if (x != session->solo_latched()) {
664                 session->set_solo_latched (x);
665         }
666 }
667
668 void
669 OptionEditor::setup_display_options ()
670 {
671         HBox* hbox;
672         vector<string> dumb;
673
674         display_packer.set_border_width (12);
675         display_packer.set_spacing (5);
676
677         show_waveforms_button.set_name ("OptionEditorToggleButton");
678         show_waveforms_recording_button.set_name ("OptionEditorToggleButton");
679         show_measures_button.set_name ("OptionEditorToggleButton");
680         follow_playhead_button.set_name ("OptionEditorToggleButton");
681         mixer_strip_width_button.set_name ("OptionEditorToggleButton");
682
683         mixer_strip_width_button.set_active (mixer.get_strip_width() == Narrow);
684
685         hbox = manage (new HBox);
686         hbox->set_border_width (8);
687         hbox->pack_start (show_waveforms_button, false, false);
688         display_packer.pack_start (*hbox, false, false);
689
690         hbox = manage (new HBox);
691         hbox->set_border_width (8);
692         hbox->pack_start (show_waveforms_recording_button, false, false);
693         display_packer.pack_start (*hbox, false, false);
694         
695         hbox = manage (new HBox);
696         hbox->set_border_width (8);
697         hbox->pack_start (show_measures_button, false, false);
698         display_packer.pack_start (*hbox, false, false);
699
700         hbox = manage (new HBox);
701         hbox->set_border_width (8);
702         hbox->pack_start (mixer_strip_width_button, false, false);
703         display_packer.pack_start (*hbox, false, false);
704
705         hbox = manage (new HBox);
706         hbox->set_border_width (8);
707         hbox->pack_start (follow_playhead_button, false, false);
708         display_packer.pack_start (*hbox, false, false);
709
710         Label *meter_hold_label = manage (new Label (_("Meter Peak Hold")));
711         meter_hold_label->set_name ("OptionsLabel");
712         dumb.clear ();
713         dumb.push_back (_("Off"));
714         dumb.push_back (_("Short"));
715         dumb.push_back (_("Medium"));
716         dumb.push_back (_("Long"));
717         meter_hold_combo.set_popdown_strings (dumb);
718         meter_hold_combo.set_use_arrows_always (true);
719         meter_hold_combo.set_value_in_list (true, false);
720         meter_hold_combo.get_entry()->set_editable (false);
721         meter_hold_combo.get_entry()->set_name ("OptionsEntry");
722         meter_hold_combo.get_popwin()->unmap_event.connect (mem_fun(*this, &OptionEditor::meter_hold_chosen));
723         hbox = manage (new HBox);
724         hbox->set_border_width (8);
725         hbox->set_spacing (8);
726         hbox->pack_start (*meter_hold_label, false, false);
727         hbox->pack_start (meter_hold_combo, false, false);
728         display_packer.pack_start (*hbox, false, false);
729
730         Label *meter_falloff_label = manage (new Label (_("Meter Falloff")));
731         meter_falloff_label->set_name ("OptionsLabel");
732         dumb.clear ();
733         dumb.push_back (_("Off"));
734         dumb.push_back (_("Slowest"));
735         dumb.push_back (_("Slow"));
736         dumb.push_back (_("Medium"));
737         dumb.push_back (_("Fast"));
738         dumb.push_back (_("Faster"));
739         dumb.push_back (_("Fastest"));
740         meter_falloff_combo.set_popdown_strings (dumb);
741         meter_falloff_combo.set_use_arrows_always (true);
742         meter_falloff_combo.set_value_in_list (true, false);
743         meter_falloff_combo.get_entry()->set_editable (false);
744         meter_falloff_combo.get_entry()->set_name ("OptionsEntry");
745         meter_falloff_combo.get_popwin()->unmap_event.connect (mem_fun(*this, &OptionEditor::meter_falloff_chosen));
746         hbox = manage (new HBox);
747         hbox->set_border_width (8);
748         hbox->set_spacing (8);
749         hbox->pack_start (*meter_falloff_label, false, false);
750         hbox->pack_start (meter_falloff_combo, false, false);
751         display_packer.pack_start (*hbox, false, false);
752         
753         
754         show_waveforms_button.signal_clicked().connect (mem_fun(*this, &OptionEditor::show_waveforms_clicked));
755         show_waveforms_recording_button.signal_clicked().connect (mem_fun(*this, &OptionEditor::show_waveforms_recording_clicked));
756         show_measures_button.signal_clicked().connect (mem_fun(*this, &OptionEditor::show_measures_clicked));
757         mixer_strip_width_button.signal_clicked().connect (mem_fun(*this, &OptionEditor::strip_width_clicked));
758         follow_playhead_button.signal_clicked().connect (mem_fun(*this, &OptionEditor::follow_playhead_clicked));
759
760         editor.DisplayControlChanged.connect (mem_fun(*this, &OptionEditor::display_control_changed));
761
762         show_measures_button.set_active (editor.show_measures());
763         show_waveforms_button.set_active (editor.show_waveforms());
764         show_waveforms_recording_button.set_active (editor.show_waveforms_recording());
765         follow_playhead_button.set_active (editor.follow_playhead());
766 }
767
768 gint
769 OptionEditor::meter_hold_chosen (GdkEventAny* ev)
770 {
771         if (session) {
772                 string str = meter_hold_combo.get_entry()->get_text();
773                 
774                 if (str == _("Off")) {
775                         session->set_meter_hold (0);
776                 } else if (str == _("Short")) {
777                         session->set_meter_hold (40);
778                 } else if (str == _("Medium")) {
779                         session->set_meter_hold (100);
780                 } else if (str == _("Long")) {
781                         session->set_meter_hold (200);
782                 }
783         }
784
785         return TRUE;
786 }
787
788 gint
789 OptionEditor::meter_falloff_chosen (GdkEventAny* ev)
790 {
791         if (session) {
792                 string str = meter_falloff_combo.get_entry()->get_text();
793                 
794                 if (str == _("Off")) {
795                         session->set_meter_falloff (0.0f);
796                 } else if (str == _("Slowest")) {
797                         session->set_meter_falloff (0.266f); // 6.6 dB/sec falloff at update rate of 40 ms
798                 } else if (str == _("Slow")) {
799                         session->set_meter_falloff (0.342f); // 8.6 dB/sec falloff at update rate of 40 ms
800                 } else if (str == _("Medium")) {
801                         session->set_meter_falloff  (0.7f);
802                 } else if (str == _("Fast")) {
803                         session->set_meter_falloff (1.1f);
804                 } else if (str == _("Faster")) {
805                         session->set_meter_falloff (1.5f);
806                 } else if (str == _("Fastest")) {
807                         session->set_meter_falloff (2.5f);
808                 }
809         }
810
811         return TRUE;
812 }
813
814 void
815 OptionEditor::display_control_changed (Editing::DisplayControl dc)
816 {
817         ToggleButton* button = 0;
818         bool val = true;
819
820         switch (dc) {
821         case ShowMeasures:
822                 val = editor.show_measures ();
823                 button = &show_measures_button;
824                 break;
825         case ShowWaveforms:
826                 val = editor.show_waveforms ();
827                 button = &show_waveforms_button;
828                 break;
829         case ShowWaveformsRecording:
830                 val = editor.show_waveforms_recording ();
831                 button = &show_waveforms_recording_button;
832                 break;
833         case FollowPlayhead:
834                 val = editor.follow_playhead ();
835                 button = &follow_playhead_button;
836                 break;
837         }
838
839         if (button->get_active() != val) {
840                 button->set_active (val);
841         }
842 }
843
844 void
845 OptionEditor::setup_sync_options ()
846 {
847         Label *slave_type_label = manage (new Label (_("Positional Sync")));
848         HBox* hbox;
849         vector<string> dumb;
850
851         slave_type_label->set_name("OptionsLabel");
852         positional_sync_strings = internationalize (psync_strings);
853
854         slave_type_combo.set_use_arrows_always (true);
855         slave_type_combo.set_value_in_list (true, false);
856         slave_type_combo.get_entry()->set_editable (false);
857         slave_type_combo.get_entry()->set_name ("OptionsEntry");
858         slave_type_combo.get_popwin()->unmap_event.connect (mem_fun(*this, &OptionEditor::slave_type_chosen));
859
860         dumb.clear ();
861         dumb.push_back (X_("24 FPS"));
862         dumb.push_back (X_("25 FPS"));
863         dumb.push_back (X_("30 FPS drop"));
864         dumb.push_back (X_("30 FPS non-drop"));
865         
866         smpte_fps_combo.set_popdown_strings (dumb);
867         smpte_fps_combo.set_use_arrows_always (true);
868         smpte_fps_combo.set_value_in_list (true, false);
869         smpte_fps_combo.get_entry()->set_editable (false);
870         smpte_fps_combo.get_entry()->set_name ("OptionsEntry");
871         smpte_fps_combo.get_popwin()->unmap_event.connect (mem_fun(*this, &OptionEditor::smpte_fps_chosen));
872         
873         smpte_offset_clock.set_mode (AudioClock::SMPTE);
874         smpte_offset_clock.ValueChanged.connect (mem_fun(*this, &OptionEditor::smpte_offset_chosen));
875         
876         send_mtc_button.set_name ("OptionEditorToggleButton");
877         jack_time_master_button.set_name ("OptionEditorToggleButton");
878         smpte_offset_negative_button.set_name ("OptionEditorToggleButton");
879
880         send_mtc_button.unset_flags (Gtk::CAN_FOCUS);
881         jack_time_master_button.unset_flags (Gtk::CAN_FOCUS);
882         smpte_offset_negative_button.unset_flags (Gtk::CAN_FOCUS);
883
884         hbox = manage (new HBox);
885         hbox->set_border_width (5);
886         hbox->set_spacing (10);
887         hbox->pack_start (*slave_type_label, false, false);
888         hbox->pack_start (slave_type_combo, false, false);
889
890         sync_packer.pack_start (*hbox, false, false);
891         
892         hbox = manage (new HBox);
893         hbox->set_border_width (5);
894         hbox->pack_start (send_mtc_button, false, false);
895         sync_packer.pack_start (*hbox, false, false);
896
897         hbox = manage (new HBox);
898         hbox->set_border_width (5);
899         hbox->pack_start (jack_time_master_button, false, false);
900         sync_packer.pack_start (*hbox, false, false);
901
902         Label *smpte_fps_label = manage (new Label (_("SMPTE Frames/second")));
903         Label *smpte_offset_label = manage (new Label (_("SMPTE Offset")));
904         smpte_fps_label->set_name("OptionsLabel");
905         smpte_offset_label->set_name("OptionsLabel");
906         
907         hbox = manage (new HBox);
908         hbox->set_border_width (5);
909         hbox->set_spacing (10);
910         hbox->pack_start (*smpte_fps_label, false, false);
911         hbox->pack_start (smpte_fps_combo, false, false);
912
913         sync_packer.pack_start (*hbox, false, false);
914
915         hbox = manage (new HBox);
916         hbox->set_border_width (5);
917         hbox->set_spacing (10);
918         hbox->pack_start (*smpte_offset_label, false, false);
919         hbox->pack_start (smpte_offset_clock, false, false);
920         hbox->pack_start (smpte_offset_negative_button, false, false);
921
922         sync_packer.pack_start (*hbox, false, false);
923
924         jack_time_master_button.set_active (Config->get_jack_time_master());
925
926         send_mtc_button.button_press_event.connect (bind (mem_fun(*this, &OptionEditor::send_mtc_toggled), &send_mtc_button));
927         jack_time_master_button.signal_clicked().connect (mem_fun(*this, &OptionEditor::jack_time_master_clicked));
928         smpte_offset_negative_button.signal_clicked().connect (mem_fun(*this, &OptionEditor::smpte_offset_negative_clicked));
929 }
930
931 void
932 OptionEditor::smpte_offset_negative_clicked ()
933 {
934         if (session) {
935                 session->set_smpte_offset_negative (smpte_offset_negative_button.get_active());
936         }
937 }
938
939 gint
940 OptionEditor::smpte_fps_chosen (GdkEventAny* ev)
941 {
942         if (session) {
943                 string str = smpte_fps_combo.get_entry()->get_text();
944                 
945                 if (str == X_("24 FPS")) {
946                         session->set_smpte_type (24.0, false);
947                 } else if (str == X_("25 FPS")) {
948                         session->set_smpte_type (25.0, false);
949                 } else if (str == X_("30 FPS drop")) {
950                         session->set_smpte_type (29.97, true);
951                 } else if (str == X_("30 FPS non-drop")) {
952                         session->set_smpte_type (30.0, false);
953                 }
954         }
955         
956         return TRUE;
957 }
958
959 void
960 OptionEditor::smpte_offset_chosen()
961 {
962         if (session) {
963                 jack_nframes_t frames = smpte_offset_clock.current_duration();
964                 session->set_smpte_offset (frames);
965         }
966 }
967
968
969 void
970 OptionEditor::setup_midi_options ()
971 {
972         HBox* hbox;
973         MIDI::Manager::PortMap::const_iterator i;
974         const MIDI::Manager::PortMap& ports = MIDI::Manager::instance()->get_midi_ports();
975         int n;
976         ToggleButton* tb;
977         RadioButton* rb;
978         RadioButton* first_mtc_button = 0;
979         RadioButton* first_mmc_button = 0;
980         RadioButton* first_midi_button = 0;
981
982         Gtk::Table* table = manage (new Table (ports.size() + 4, 9));
983
984         table->set_row_spacings (6);
985         table->set_col_spacings (10);
986
987         table->attach (*(manage (new Label (X_("Port")))), 0, 1, 0, 1);
988         table->attach (*(manage (new Label (X_("Offline")))), 1, 2, 0, 1);
989         table->attach (*(manage (new Label (X_("Trace\nInput")))), 2, 3, 0, 1);
990         table->attach (*(manage (new Label (X_("Trace\nOutput")))), 3, 4, 0, 1);
991         table->attach (*(manage (new Label (X_("MTC")))), 4, 5, 0, 1);
992         table->attach (*(manage (new Label (X_("MMC")))), 6, 7, 0, 1);
993         table->attach (*(manage (new Label (X_("MIDI Parameter\nControl")))), 8, 9, 0, 1);
994
995         table->attach (*(manage (new HSeparator())), 0, 9, 1, 2);
996         table->attach (*(manage (new VSeparator())), 5, 6, 0, 8);
997         table->attach (*(manage (new VSeparator())), 7, 8, 0, 8);
998         
999         for (n = 0, i = ports.begin(); i != ports.end(); ++n, ++i) {
1000
1001                 pair<MIDI::Port*,vector<RadioButton*> > newpair;
1002
1003                 newpair.first = i->second;
1004
1005                 table->attach (*(manage (new Label (i->first))), 0, 1, n+2, n+3, 0, 0);
1006                 tb = manage (new ToggleButton (_("online")));
1007                 tb->set_name ("OptionEditorToggleButton");
1008
1009                 /* remember, we have to handle the i18n case where the relative
1010                    lengths of the strings in language N is different than in english.
1011                 */
1012
1013                 if (strlen (_("offline")) > strlen (_("online"))) {
1014                         set_size_request_to_display_given_text (*tb, _("offline"), 15, 12);
1015                 } else {
1016                         set_size_request_to_display_given_text (*tb, _("online"), 15, 12);
1017                 }
1018
1019                 tb->set_active (!(*i).second->input()->offline());
1020                 tb->button_press_event.connect (bind (mem_fun(*this, &OptionEditor::port_online_toggled), (*i).second, tb));
1021                 (*i).second->input()->OfflineStatusChanged.connect (bind (mem_fun(*this, &OptionEditor::map_port_online), (*i).second, tb));
1022                 table->attach (*tb, 1, 2, n+2, n+3, 0, 0);
1023
1024                 tb = manage (new ToggleButton ());
1025                 tb->set_name ("OptionEditorToggleButton");
1026                 tb->button_press_event.connect (bind (mem_fun(*this, &OptionEditor::port_trace_in_toggled), (*i).second, tb));
1027                 tb->set_size_request (10, 10);
1028                 table->attach (*tb, 2, 3, n+2, n+3, 0, 0);
1029
1030                 tb = manage (new ToggleButton ());
1031                 tb->set_name ("OptionEditorToggleButton");
1032                 tb->button_press_event.connect (bind (mem_fun(*this, &OptionEditor::port_trace_out_toggled), (*i).second, tb));
1033                 tb->set_size_request (10, 10);
1034                 table->attach (*tb, 3, 4, n+2, n+3, 0, 0);
1035
1036                 rb = manage (new RadioButton ());
1037                 newpair.second.push_back (rb);
1038                 rb->set_name ("OptionEditorToggleButton");
1039                 if (n == 0) {
1040                         first_mtc_button = rb;
1041                 } else {
1042                         rb->set_group (first_mtc_button->group());
1043                 }
1044                 table->attach (*rb, 4, 5, n+2, n+3, 0, 0);
1045                 rb->button_press_event.connect (bind (mem_fun(*this, &OptionEditor::mtc_port_chosen), (*i).second, rb));
1046
1047                 if (Config->get_mtc_port_name() == i->first) {
1048                         rb->set_active (true);
1049                 }
1050                 
1051                 rb = manage (new RadioButton ());
1052                 newpair.second.push_back (rb);
1053                 rb->set_name ("OptionEditorToggleButton");
1054                 if (n == 0) {
1055                         first_mmc_button = rb;
1056                 } else {
1057                         rb->set_group (first_mmc_button->group());
1058                 }
1059                 table->attach (*rb, 6, 7, n+2, n+3, 0, 0);
1060                 rb->button_press_event.connect (bind (mem_fun(*this, &OptionEditor::mmc_port_chosen), (*i).second, rb));
1061
1062                 if (Config->get_mmc_port_name() == i->first) {
1063                         rb->set_active (true);
1064                 }
1065
1066                 rb = manage (new RadioButton ());
1067                 newpair.second.push_back (rb);
1068                 rb->set_name ("OptionEditorToggleButton");
1069                 if (n == 0) {
1070                         first_midi_button = rb;
1071                 } else {
1072                         rb->set_group (first_midi_button->group());
1073                 }
1074                 table->attach (*rb, 8, 9, n+2, n+3, 0, 0);
1075                 rb->button_press_event.connect (bind (mem_fun(*this, &OptionEditor::midi_port_chosen), (*i).second, rb));
1076
1077                 if (Config->get_midi_port_name() == i->first) {
1078                         rb->set_active (true);
1079                 }
1080                 
1081                 port_toggle_buttons.insert (newpair);
1082         }
1083
1084         table->show_all ();
1085
1086         hbox = manage (new HBox);
1087         hbox->set_border_width (6);
1088         hbox->pack_start (*table, true, false);
1089         midi_packer.pack_start (*hbox, false, false);
1090         
1091         VBox* mmcbuttonbox = manage (new VBox);
1092
1093         mmc_control_button.set_name ("OptionEditorToggleButton");
1094
1095         hbox = manage (new HBox);
1096         hbox->set_border_width (6);
1097         hbox->pack_start (mmc_control_button, false, false, 36);
1098         mmcbuttonbox->pack_start (*hbox, false, false);
1099
1100         midi_control_button.set_name ("OptionEditorToggleButton");
1101
1102         hbox = manage (new HBox);
1103         hbox->set_border_width (6);
1104         hbox->pack_start (midi_control_button, false, false, 36);
1105         mmcbuttonbox->pack_start (*hbox, false, false);
1106
1107         send_mmc_button.set_name ("OptionEditorToggleButton");
1108
1109         hbox = manage (new HBox);
1110         hbox->set_border_width (6);
1111         hbox->pack_start (send_mmc_button, false, false, 36);
1112         mmcbuttonbox->pack_start (*hbox, false, false);
1113         
1114         midi_feedback_button.set_name ("OptionEditorToggleButton");
1115
1116         hbox = manage (new HBox);
1117         hbox->set_border_width (6);
1118         hbox->pack_start (midi_feedback_button, false, false, 36);
1119         mmcbuttonbox->pack_start (*hbox, false, false);
1120
1121         midi_packer.pack_start (*mmcbuttonbox, false, false);
1122
1123         mmc_control_button.toggled.connect (bind (mem_fun(*this, &OptionEditor::mmc_control_toggled), &mmc_control_button));
1124         midi_control_button.toggled.connect (bind (mem_fun(*this, &OptionEditor::midi_control_toggled), &midi_control_button));
1125         send_mmc_button.toggled.connect (bind (mem_fun(*this, &OptionEditor::send_mmc_toggled), &send_mmc_button));
1126         midi_feedback_button.toggled.connect (bind (mem_fun(*this, &OptionEditor::midi_feedback_toggled), &midi_feedback_button));
1127 }
1128
1129 gint
1130 OptionEditor::mtc_port_chosen (GdkEventButton* ev, MIDI::Port *port, Gtk::RadioButton* rb) 
1131 {
1132         if (session) {
1133                 if (!rb->get_active()) {
1134                         if (port) {
1135                                 session->set_mtc_port (port->name());
1136                                 Config->set_mtc_port_name (port->name());
1137                         } else {
1138                                 session->set_mtc_port ("");
1139                         }
1140
1141                         /* update sync options to reflect MTC port availability */
1142
1143                         vector<string> dumb;
1144                         dumb.push_back (positional_sync_strings[Session::None]);
1145                         dumb.push_back (positional_sync_strings[Session::JACK]);
1146
1147                         if (session->mtc_port()) {
1148                                 dumb.push_back (positional_sync_strings[Session::MTC]);
1149                         }
1150                         slave_type_combo.set_popdown_strings (dumb);
1151
1152                         rb->set_active (true);
1153                 }
1154         }
1155
1156         return stop_signal (*rb, "button_press_event");
1157 }
1158
1159 gint
1160 OptionEditor::mmc_port_chosen (GdkEventButton* ev, MIDI::Port* port, Gtk::RadioButton* rb)
1161 {
1162         if (session) {
1163                 if (!rb->get_active()) {
1164                         if (port) {
1165                                 session->set_mmc_port (port->name());
1166                                 Config->set_mtc_port_name (port->name());
1167                         } else {
1168                                 session->set_mmc_port ("");
1169                         }
1170                         rb->set_active (true);
1171                 }
1172         }
1173         return stop_signal (*rb, "button_press_event");
1174 }
1175
1176 gint
1177 OptionEditor::midi_port_chosen (GdkEventButton* ev, MIDI::Port* port, Gtk::RadioButton* rb)
1178 {
1179         if (session) {
1180                 if (!rb->get_active()) {
1181                         if (port) {
1182                                 session->set_midi_port (port->name());
1183                                 Config->set_midi_port_name (port->name());
1184                         } else {
1185                                 session->set_midi_port ("");
1186                         }
1187                         rb->set_active (true);
1188                 }
1189         }
1190         return stop_signal (*rb, "button_press_event");
1191 }
1192
1193 gint
1194 OptionEditor::port_online_toggled (GdkEventButton* ev, MIDI::Port* port, ToggleButton* tb)
1195 {
1196         bool wanted = tb->get_active(); /* it hasn't changed at this point */
1197
1198         if (wanted != port->input()->offline()) {
1199                 port->input()->set_offline (wanted);
1200         } 
1201         return stop_signal (*tb, "button_press_event");
1202 }
1203
1204 void
1205 OptionEditor::map_port_online (MIDI::Port* port, ToggleButton* tb)
1206 {
1207         if (port->input()->offline()) {
1208                 static_cast<Label*>(tb->get_child())->set_text (_("offline"));
1209                 tb->set_active (false);
1210         } else {
1211                 static_cast<Label*>(tb->get_child())->set_text (_("online"));
1212                 tb->set_active (true);
1213         }
1214 }
1215
1216 gint
1217 OptionEditor::port_trace_in_toggled (GdkEventButton* ev, MIDI::Port* port, ToggleButton* tb)
1218 {
1219         /* XXX not very good MVC style here */
1220
1221         port->input()->trace (!tb->get_active(), &cerr, string (port->name()) + string (" input: "));
1222         tb->set_active (!tb->get_active());
1223         return stop_signal (*tb, "button_press_event");
1224 }
1225
1226 gint
1227 OptionEditor::port_trace_out_toggled (GdkEventButton* ev,MIDI::Port* port, ToggleButton* tb)
1228 {
1229         /* XXX not very good MVC style here */
1230
1231         port->output()->trace (!tb->get_active(), &cerr, string (port->name()) + string (" output: "));
1232         tb->set_active (!tb->get_active());
1233         return stop_signal (*tb, "button_press_event");
1234 }
1235
1236 gint
1237 OptionEditor::send_mtc_toggled (GdkEventButton *ev, CheckButton *button)
1238 {
1239         if (session) {
1240                 session->set_send_mtc (!button->get_active());
1241         }
1242         return stop_signal (*button, "button_press_event");
1243 }
1244
1245 void
1246 OptionEditor::send_mmc_toggled (CheckButton *button)
1247 {
1248         if (session) {
1249                 session->set_send_mmc (button->get_active());
1250         }
1251 }
1252
1253 void
1254 OptionEditor::mmc_control_toggled (CheckButton *button)
1255 {
1256         if (session) {
1257                 session->set_mmc_control (button->get_active());
1258         }
1259 }
1260
1261 void
1262 OptionEditor::midi_control_toggled (CheckButton *button)
1263 {
1264         if (session) {
1265                 session->set_midi_control (button->get_active());
1266         }
1267 }
1268
1269 void
1270 OptionEditor::midi_feedback_toggled (CheckButton *button)
1271 {
1272         if (session) {
1273                 session->set_midi_feedback (button->get_active());
1274         }
1275 }
1276
1277 void
1278 OptionEditor::save ()
1279 {
1280         /* XXX a bit odd that we save the entire session state here */
1281
1282         ui.save_state ("");
1283 }
1284
1285 gint
1286 OptionEditor::wm_close (GdkEventAny *ev)
1287 {
1288         save ();
1289         just_close_win();
1290         return TRUE;
1291 }
1292
1293 void
1294 OptionEditor::jack_time_master_clicked ()
1295 {
1296         bool yn = jack_time_master_button.get_active();
1297
1298         Config->set_jack_time_master (yn);
1299
1300         if (session) {
1301                 session->engine().reset_timebase ();
1302         }
1303 }
1304
1305 void
1306 OptionEditor::raid_path_changed ()
1307 {
1308         if (session) {
1309                 session->set_raid_path (session_raid_entry.get_text());
1310         }
1311 }
1312
1313 void
1314 OptionEditor::click_browse_clicked ()
1315 {
1316         SoundFileSelector& sfdb (ARDOUR_UI::instance()->get_sfdb_window());
1317         sigc::connection c = sfdb.Action.connect (mem_fun(*this, &OptionEditor::click_chosen));
1318         
1319         sfdb.run (_("Use as click"), false, true);
1320         c.disconnect ();
1321 }
1322
1323 void
1324 OptionEditor::click_chosen (vector<string> paths, bool ignore)
1325 {
1326         string path;
1327
1328         if (!paths.empty()) {
1329                 path = paths.front();
1330         } else {
1331                 return;
1332         }
1333
1334         click_path_entry.set_text (path);
1335         click_sound_changed ();
1336 }
1337
1338 void
1339 OptionEditor::click_emphasis_browse_clicked ()
1340 {
1341         SoundFileSelector& sfdb (ARDOUR_UI::instance()->get_sfdb_window());
1342         sigc::connection c = sfdb.Action.connect (mem_fun(*this, &OptionEditor::click_emphasis_chosen));
1343
1344         sfdb.run (_("Use as click emphasis"), false, true);
1345         c.disconnect ();
1346
1347 }
1348
1349 void
1350 OptionEditor::click_emphasis_chosen (vector<string> paths, bool ignore)
1351 {       
1352         string path;
1353
1354         if (!paths.empty()) {
1355                 path = paths.front();
1356         } else {
1357                 return;
1358         }
1359
1360         click_emphasis_path_entry.set_text (path);
1361         click_emphasis_sound_changed ();
1362 }
1363
1364 void
1365 OptionEditor::click_sound_changed ()
1366 {
1367         if (session) {
1368                 string path = click_path_entry.get_text();
1369
1370                 if (path == session->click_sound) {
1371                         return;
1372                 }
1373
1374                 if (path.length() == 0) {
1375
1376                         session->set_click_sound ("");
1377
1378                 } else {
1379
1380                         strip_whitespace_edges (path);
1381                         
1382                         if (path == _("internal")) {
1383                                 session->set_click_sound ("");
1384                         } else {
1385                                 session->set_click_sound (path);
1386                         }
1387                 }
1388         }
1389 }
1390
1391 void
1392 OptionEditor::click_emphasis_sound_changed ()
1393 {
1394         if (session) {
1395                 string path = click_emphasis_path_entry.get_text();
1396
1397                 if (path == session->click_emphasis_sound) {
1398                         return;
1399                 }
1400
1401                 if (path.length() == 0) {
1402
1403                         session->set_click_emphasis_sound ("");
1404
1405                 } else {
1406
1407                         strip_whitespace_edges (path);
1408
1409                         if (path == _("internal")) {
1410                                 session->set_click_emphasis_sound ("");
1411                         } else {
1412                                 session->set_click_emphasis_sound (path);
1413                         }
1414                 }
1415         }
1416 }
1417
1418 void
1419 OptionEditor::show_waveforms_clicked ()
1420 {
1421         editor.set_show_waveforms (show_waveforms_button.get_active());
1422 }
1423
1424 void
1425 OptionEditor::show_waveforms_recording_clicked ()
1426 {
1427         editor.set_show_waveforms_recording (show_waveforms_recording_button.get_active());
1428 }
1429
1430 void
1431 OptionEditor::show_measures_clicked ()
1432 {
1433         editor.set_show_measures (show_measures_button.get_active());
1434 }
1435
1436 void
1437 OptionEditor::follow_playhead_clicked ()
1438 {
1439         editor.set_follow_playhead (follow_playhead_button.get_active());
1440 }
1441
1442 void
1443 OptionEditor::strip_width_clicked ()
1444 {
1445         mixer.set_strip_width (mixer_strip_width_button.get_active() ? Narrow : Wide);
1446 }
1447
1448
1449 void
1450 OptionEditor::just_close_win()
1451 {
1452         hide_all();
1453         ARDOUR_UI::instance()->allow_focus(false);
1454 }
1455
1456 void
1457 OptionEditor::queue_session_control_changed (Session::ControlType t)
1458 {
1459         ui.call_slot (bind (mem_fun(*this, &OptionEditor::session_control_changed), t));
1460 }
1461
1462 void
1463 OptionEditor::session_control_changed (Session::ControlType t)
1464 {
1465         switch (t) {
1466         case Session::SlaveType:
1467                 switch (session->slave_source()) {
1468                 case Session::None:
1469                         slave_type_combo.get_entry()->set_text (positional_sync_strings[Session::None]);
1470                         break;
1471                 case Session::MTC:
1472                         slave_type_combo.get_entry()->set_text (positional_sync_strings[Session::MTC]);
1473                         break;
1474                 case Session::JACK:
1475                         slave_type_combo.get_entry()->set_text (positional_sync_strings[Session::JACK]);
1476                         break;
1477                 default:
1478                         slave_type_combo.get_entry()->set_text (_("--unknown--"));
1479                         break;
1480                 }
1481                 
1482                 break;
1483
1484         case Session::SendMTC:
1485                 map_some_session_state (send_mtc_button, &Session::get_send_mtc);
1486                 break;
1487
1488         case Session::SendMMC:
1489                 map_some_session_state (send_mmc_button, &Session::get_send_mmc);
1490                 break;
1491
1492         case Session::MMCControl:       
1493                 map_some_session_state (mmc_control_button, &Session::get_mmc_control);
1494                 break;
1495
1496         case Session::MidiFeedback:       
1497                 map_some_session_state (midi_feedback_button, &Session::get_midi_feedback);
1498                 break;
1499
1500         case Session::MidiControl:       
1501                  map_some_session_state (midi_control_button, &Session::get_midi_control);
1502                 break;
1503         
1504         default:
1505                 break;
1506         }
1507 }
1508
1509 gint
1510 OptionEditor::native_format_chosen (GdkEventAny *ignored)
1511 {
1512         string which;
1513
1514         if (session == 0) {
1515                 return FALSE;
1516         }
1517
1518         bool use_bwf = (native_format_combo.get_entry()->get_text() == native_format_strings[0]);
1519
1520         if (use_bwf != Config->get_native_format_is_bwf()) {
1521                 Config->set_native_format_is_bwf (use_bwf);
1522                 session->reset_native_file_format ();
1523         }
1524
1525         return TRUE;
1526 }
1527
1528 gint
1529 OptionEditor::slave_type_chosen (GdkEventAny *ignored)
1530 {
1531         string which;
1532
1533         if (session == 0) {
1534                 return FALSE;
1535         }
1536
1537         which = slave_type_combo.get_entry()->get_text();
1538
1539         if (which == positional_sync_strings[Session::None]) {
1540                 session->request_slave_source (Session::None);
1541         } else if (which == positional_sync_strings[Session::MTC]) {
1542                 session->request_slave_source (Session::MTC);
1543         } else if (which == positional_sync_strings[Session::JACK]) {
1544                 session->request_slave_source (Session::JACK);
1545         } 
1546         return FALSE;
1547 }
1548
1549 void
1550 OptionEditor::clear_click_editor ()
1551 {
1552         if (click_io_selector) {
1553                 click_packer.remove (*click_io_selector);
1554                 click_packer.remove (*click_gpm);
1555                 delete click_io_selector;
1556                 delete click_gpm;
1557                 click_io_selector = 0;
1558                 click_gpm = 0;
1559         }
1560 }
1561
1562 void
1563 OptionEditor::setup_click_editor ()
1564 {
1565         Label* label;
1566         HBox* hpacker = manage (new HBox);
1567
1568         click_path_entry.set_sensitive (true);
1569         click_emphasis_path_entry.set_sensitive (true);
1570
1571         click_path_entry.set_name ("OptionsEntry");
1572         click_emphasis_path_entry.set_name ("OptionsEntry");
1573         
1574         click_path_entry.activate.connect (mem_fun(*this, &OptionEditor::click_sound_changed));
1575         click_emphasis_path_entry.activate.connect (mem_fun(*this, &OptionEditor::click_emphasis_sound_changed));
1576
1577         click_path_entry.signal_focus_in_event().connect (mem_fun (Keyboard::the_keyboard(), &Keyboard::focus_in_handler));
1578         click_path_entry.signal_focus_out_event().connect (bind (mem_fun(*this, &OptionEditor::focus_out_event_handler), &OptionEditor::click_sound_changed));
1579         click_emphasis_path_entry.signal_focus_in_event().connect (mem_fun (Keyboard::the_keyboard(), &Keyboard::focus_in_handler));
1580         click_emphasis_path_entry.signal_focus_out_event().connect (bind (mem_fun(*this, &OptionEditor::focus_out_event_handler), &OptionEditor::click_emphasis_sound_changed));
1581
1582         click_browse_button.set_name ("EditorGTKButton");
1583         click_emphasis_browse_button.set_name ("EditorGTKButton");
1584         click_browse_button.signal_clicked().connect (mem_fun(*this, &OptionEditor::click_browse_clicked));
1585         click_emphasis_browse_button.signal_clicked().connect (mem_fun(*this, &OptionEditor::click_emphasis_browse_clicked));
1586
1587         click_packer.set_border_width (12);
1588         click_packer.set_spacing (5);
1589
1590         click_io_selector = new IOSelector (*session, session->click_io(), false);
1591         click_gpm = new GainMeter (session->click_io(), *session);
1592
1593         click_table.set_col_spacings (10);
1594         
1595         label = manage(new Label(_("Click audio file")));
1596         label->set_name ("OptionsLabel");
1597         click_table.attach (*label, 0, 1, 0, 1, 0, 0);
1598         click_table.attach (click_path_entry, 1, 2, 0, 1, Gtk::FILL|Gtk::EXPAND, 0);
1599         click_table.attach (click_browse_button, 2, 3, 0, 1, 0, 0);
1600         
1601         label = manage(new Label(_("Click emphasis audiofile")));
1602         label->set_name ("OptionsLabel");
1603         click_table.attach (*label, 0, 1, 1, 2, 0, 0);
1604         click_table.attach (click_emphasis_path_entry, 1, 2, 1, 2, Gtk::FILL|Gtk::EXPAND, 0);
1605         click_table.attach (click_emphasis_browse_button, 2, 3, 1, 2, 0, 0);
1606
1607         hpacker->set_spacing (10);
1608         hpacker->pack_start (*click_io_selector, false, false);
1609         hpacker->pack_start (*click_gpm, false, false);
1610
1611         click_packer.pack_start (click_table, false, false);
1612         click_packer.pack_start (*hpacker, false, false);
1613
1614         click_packer.show_all ();
1615 }
1616
1617 void
1618 OptionEditor::clear_auditioner_editor ()
1619 {
1620         if (auditioner_io_selector) {
1621                 audition_hpacker.remove (*auditioner_io_selector);
1622                 audition_hpacker.remove (*auditioner_gpm);
1623                 delete auditioner_io_selector;
1624                 delete auditioner_gpm;
1625                 auditioner_io_selector = 0;
1626                 auditioner_gpm = 0;
1627         }
1628 }
1629
1630 void
1631 OptionEditor::setup_auditioner_editor ()
1632 {
1633         audition_packer.set_border_width (12);
1634         audition_packer.set_spacing (5);
1635         audition_hpacker.set_spacing (10);
1636
1637         audition_label.set_name ("OptionEditorAuditionerLabel");
1638         audition_label.set_text (_("The auditioner is a dedicated mixer strip used\n"
1639                                    "for listening to specific regions outside the context\n"
1640                                    "of the overall mix. It can be connected just like any\n"
1641                                    "other mixer strip."));
1642         
1643         audition_packer.pack_start (audition_label, false, false, 10);
1644         audition_packer.pack_start (audition_hpacker, false, false);
1645 }
1646
1647 void
1648 OptionEditor::connect_audition_editor ()
1649 {
1650         auditioner_io_selector = new IOSelector (*session, session->the_auditioner(), false);
1651         auditioner_gpm = new GainMeter (session->the_auditioner(), *session);
1652
1653         audition_hpacker.pack_start (*auditioner_io_selector, false, false);
1654         audition_hpacker.pack_start (*auditioner_gpm, false, false);
1655
1656         auditioner_io_selector->show_all ();
1657         auditioner_gpm->show_all ();
1658 }
1659
1660 gint
1661 OptionEditor::focus_out_event_handler (GdkEventFocus* ev, void (OptionEditor::*pmf)()) 
1662 {
1663         (this->*pmf)();
1664         return Keyboard::the_keyboard().focus_out_handler (ev);
1665 }
1666
1667 void
1668 OptionEditor::setup_misc_options()
1669 {
1670         Gtk::Table* table = manage (new Table (4, 2));  
1671         table->set_homogeneous (true);
1672
1673         misc_packer.set_border_width (8);
1674         misc_packer.set_spacing (3);
1675         misc_packer.pack_start (*table, true, true);
1676
1677         table->attach (hw_monitor_button, 0, 1, 0, 1, Gtk::FILL, 0, 8, 0);
1678         table->attach (sw_monitor_button, 0, 1, 1, 2, Gtk::FILL, 0, 8, 0);
1679         table->attach (plugins_stop_button, 0, 1, 2, 3, Gtk::FILL, 0, 8, 0);
1680         table->attach (plugins_on_rec_button, 0, 1, 3, 4, Gtk::FILL, 0, 8, 0);
1681         table->attach (verify_remove_last_capture_button, 0, 1, 4, 5, Gtk::FILL, 0, 8, 0);
1682
1683         table->attach (stop_rec_on_xrun_button, 1, 2, 0, 1, Gtk::FILL, 0, 8, 0);
1684         table->attach (stop_at_end_button, 1, 2, 1, 2, Gtk::FILL, 0, 8, 0);
1685         table->attach (debug_keyboard_button, 1, 2, 2, 3, Gtk::FILL, 0, 8, 0);
1686         table->attach (speed_quieten_button, 1, 2, 3, 4, Gtk::FILL, 0, 8, 0);
1687
1688         Gtk::VBox* connect_box = manage (new VBox);
1689         connect_box->set_spacing (3);
1690         connect_box->set_border_width (8);
1691
1692         auto_connect_output_manual_button.set_group (auto_connect_output_master_button.group());
1693         auto_connect_output_physical_button.set_group (auto_connect_output_master_button.group());
1694
1695         Gtk::HBox* useless_box = manage (new HBox);
1696         useless_box->pack_start (auto_connect_inputs_button, false, false);
1697         connect_box->pack_start (*useless_box, false, false);
1698         connect_box->pack_start (auto_connect_output_master_button, false, false);
1699         connect_box->pack_start (auto_connect_output_physical_button, false, false);
1700         connect_box->pack_start (auto_connect_output_manual_button, false);
1701
1702         misc_packer.pack_start (*connect_box, false, false);
1703         
1704         hw_monitor_button.set_name ("OptionEditorToggleButton");
1705         sw_monitor_button.set_name ("OptionEditorToggleButton");
1706         plugins_stop_button.set_name ("OptionEditorToggleButton");
1707         plugins_on_rec_button.set_name ("OptionEditorToggleButton");
1708         verify_remove_last_capture_button.set_name ("OptionEditorToggleButton");
1709         auto_connect_inputs_button.set_name ("OptionEditorToggleButton");
1710         auto_connect_output_physical_button.set_name ("OptionEditorToggleButton");
1711         auto_connect_output_master_button.set_name ("OptionEditorToggleButton");
1712         auto_connect_output_manual_button.set_name ("OptionEditorToggleButton");
1713         stop_rec_on_xrun_button.set_name ("OptionEditorToggleButton");
1714         stop_at_end_button.set_name ("OptionEditorToggleButton");
1715         debug_keyboard_button.set_name ("OptionEditorToggleButton");
1716         speed_quieten_button.set_name ("OptionEditorToggleButton");
1717
1718         hw_monitor_button.set_active (Config->get_use_hardware_monitoring());
1719         sw_monitor_button.set_active (!Config->get_no_sw_monitoring());
1720         plugins_stop_button.set_active (Config->get_plugins_stop_with_transport());
1721         stop_rec_on_xrun_button.set_active (Config->get_stop_recording_on_xrun());
1722         stop_at_end_button.set_active (Config->get_stop_at_session_end());
1723         debug_keyboard_button.set_active (false);
1724         speed_quieten_button.set_active (Config->get_quieten_at_speed() != 1.0f);
1725
1726         hw_monitor_button.signal_clicked().connect (mem_fun(*this, &OptionEditor::hw_monitor_clicked));
1727         sw_monitor_button.signal_clicked().connect (mem_fun(*this, &OptionEditor::sw_monitor_clicked));
1728         plugins_stop_button.signal_clicked().connect (mem_fun(*this, &OptionEditor::plugins_stop_with_transport_clicked));
1729         plugins_on_rec_button.signal_clicked().connect (mem_fun(*this, &OptionEditor::plugins_on_while_recording_clicked));
1730         verify_remove_last_capture_button.signal_clicked().connect (mem_fun(*this, &OptionEditor::verify_remove_last_capture_clicked));
1731         auto_connect_inputs_button.signal_clicked().connect (mem_fun(*this, &OptionEditor::auto_connect_inputs_clicked));
1732         auto_connect_output_physical_button.signal_clicked().connect (mem_fun(*this, &OptionEditor::auto_connect_output_physical_clicked));
1733         auto_connect_output_master_button.signal_clicked().connect (mem_fun(*this, &OptionEditor::auto_connect_output_master_clicked));
1734         auto_connect_output_manual_button.signal_clicked().connect (mem_fun(*this, &OptionEditor::auto_connect_output_manual_clicked));
1735         stop_rec_on_xrun_button.signal_clicked().connect (mem_fun(*this, &OptionEditor::stop_rec_on_xrun_clicked));
1736         stop_at_end_button.signal_clicked().connect (mem_fun(*this, &OptionEditor::stop_at_end_clicked));
1737         debug_keyboard_button.signal_clicked().connect (mem_fun(*this, &OptionEditor::debug_keyboard_clicked));
1738         speed_quieten_button.signal_clicked().connect (mem_fun(*this, &OptionEditor::speed_quieten_clicked));
1739 }
1740
1741 void
1742 OptionEditor::speed_quieten_clicked ()
1743 {
1744         if (speed_quieten_button.get_active()) {
1745                 Config->set_quieten_at_speed (0.251189); // -12dB reduction for ffwd or rewind
1746         } else {
1747                 Config->set_quieten_at_speed (1.0); /* no change */
1748         }
1749 }
1750
1751 void
1752 OptionEditor::debug_keyboard_clicked ()
1753 {
1754         extern bool debug_keyboard;
1755         debug_keyboard = debug_keyboard_button.get_active ();
1756 }
1757
1758 void
1759 OptionEditor::auto_connect_inputs_clicked ()
1760 {
1761         if (session) {
1762                 session->set_input_auto_connect (auto_connect_inputs_button.get_active());
1763         }
1764 }
1765
1766 void
1767 OptionEditor::auto_connect_output_master_clicked ()
1768 {
1769         if (session) {
1770                 if (auto_connect_output_master_button.get_active()) {
1771                         session->set_output_auto_connect (Session::AutoConnectMaster);
1772                 } 
1773         }
1774 }
1775
1776 void
1777 OptionEditor::auto_connect_output_physical_clicked ()
1778 {
1779         if (session) {
1780                 if (auto_connect_output_physical_button.get_active()) {
1781                         session->set_output_auto_connect (Session::AutoConnectPhysical);
1782                 } 
1783         }
1784 }
1785
1786 void
1787 OptionEditor::auto_connect_output_manual_clicked ()
1788 {
1789         if (session) {
1790                 if (auto_connect_output_manual_button.get_active()) {
1791                         session->set_output_auto_connect (Session::AutoConnectOption (0));
1792                 } 
1793         }
1794 }
1795
1796 void
1797 OptionEditor::hw_monitor_clicked ()
1798 {
1799         Config->set_use_hardware_monitoring (hw_monitor_button.get_active());
1800 }
1801
1802 void
1803 OptionEditor::sw_monitor_clicked ()
1804 {
1805         Config->set_no_sw_monitoring (!sw_monitor_button.get_active());
1806 }
1807
1808 void
1809 OptionEditor::plugins_stop_with_transport_clicked ()
1810 {
1811         Config->set_plugins_stop_with_transport (plugins_stop_button.get_active());
1812 }
1813
1814 void
1815 OptionEditor::plugins_on_while_recording_clicked ()
1816 {
1817         if (session) {
1818                 session->set_recording_plugins (plugins_on_rec_button.get_active());
1819         }
1820 }
1821
1822 void
1823 OptionEditor::verify_remove_last_capture_clicked ()
1824 {
1825         Config->set_verify_remove_last_capture(verify_remove_last_capture_button.get_active());
1826 }
1827
1828 void
1829 OptionEditor::stop_rec_on_xrun_clicked ()
1830 {
1831         Config->set_stop_recording_on_xrun (stop_rec_on_xrun_button.get_active());
1832 }
1833
1834 void
1835 OptionEditor::stop_at_end_clicked ()
1836 {
1837         Config->set_stop_at_session_end (stop_at_end_button.get_active());
1838 }
1839                                                   
1840 static const struct {
1841     const char *name;
1842     guint   modifier;
1843 } modifiers[] = {
1844         { "Shift", GDK_SHIFT_MASK },
1845         { "Control", GDK_CONTROL_MASK },
1846         { "Alt (Mod1)", GDK_MOD1_MASK },
1847         { "Control-Shift", GDK_CONTROL_MASK|GDK_SHIFT_MASK },
1848         { "Control-Alt", GDK_CONTROL_MASK|GDK_MOD1_MASK },
1849         { "Shift-Alt", GDK_SHIFT_MASK|GDK_MOD1_MASK },
1850         { "Control-Shift-Alt", GDK_CONTROL_MASK|GDK_SHIFT_MASK|GDK_MOD1_MASK },
1851         { "Mod2", GDK_MOD2_MASK },
1852         { "Mod3", GDK_MOD3_MASK },
1853         { "Mod4", GDK_MOD4_MASK },
1854         { "Mod5", GDK_MOD5_MASK },
1855         { 0, 0 }
1856 };
1857
1858 void
1859 OptionEditor::setup_keyboard_options ()
1860 {
1861         vector<string> dumb;
1862         Label* label;
1863
1864         keyboard_mouse_table.set_border_width (12);
1865         keyboard_mouse_table.set_row_spacings (5);
1866         keyboard_mouse_table.set_col_spacings (5);
1867
1868         /* internationalize and prepare for use with combos */
1869
1870         for (int i = 0; modifiers[i].name; ++i) {
1871                 dumb.push_back (_(modifiers[i].name));
1872         }
1873
1874         edit_modifier_combo.set_popdown_strings (dumb);
1875         edit_modifier_combo.get_entry()->set_editable (false);
1876         edit_modifier_combo.get_entry()->set_name ("OptionsEntry");
1877         edit_modifier_combo.set_use_arrows_always (true);
1878         edit_modifier_combo.get_popwin()->unmap_event.connect (mem_fun(*this, &OptionEditor::edit_modifier_chosen));
1879
1880         for (int x = 0; modifiers[x].name; ++x) {
1881                 if (modifiers[x].modifier == Keyboard::edit_modifier ()) {
1882                         edit_modifier_combo.get_entry()->set_text (_(modifiers[x].name));
1883                         break;
1884                 }
1885         }
1886
1887         label = manage (new Label (_("Edit using")));
1888         label->set_name ("OptionsLabel");
1889         label->set_alignment (1.0, 0.5);
1890                 
1891         keyboard_mouse_table.attach (*label, 0, 1, 0, 1, Gtk::FILL|Gtk::EXPAND, 0);
1892         keyboard_mouse_table.attach (edit_modifier_combo, 1, 2, 0, 1, Gtk::FILL|Gtk::EXPAND, 0);
1893
1894         label = manage (new Label (_("+ button")));
1895         label->set_name ("OptionsLabel");
1896         
1897         keyboard_mouse_table.attach (*label, 3, 4, 0, 1, Gtk::FILL|Gtk::EXPAND, 0);
1898         keyboard_mouse_table.attach (edit_button_spin, 4, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, 0);
1899
1900         edit_button_spin.set_name ("OptionsEntry");
1901         edit_button_adjustment.set_value (Keyboard::edit_button());
1902         edit_button_adjustment.value_changed.connect (mem_fun(*this, &OptionEditor::edit_button_changed));
1903
1904         delete_modifier_combo.set_popdown_strings (dumb);
1905         delete_modifier_combo.get_entry()->set_editable (false);
1906         delete_modifier_combo.get_entry()->set_name ("OptionsEntry");
1907         delete_modifier_combo.set_use_arrows_always (true);
1908         delete_modifier_combo.get_popwin()->unmap_event.connect (mem_fun(*this, &OptionEditor::delete_modifier_chosen));
1909
1910         for (int x = 0; modifiers[x].name; ++x) {
1911                 if (modifiers[x].modifier == Keyboard::delete_modifier ()) {
1912                         delete_modifier_combo.get_entry()->set_text (_(modifiers[x].name));
1913                         break;
1914                 }
1915         }
1916
1917         label = manage (new Label (_("Delete using")));
1918         label->set_name ("OptionsLabel");
1919         label->set_alignment (1.0, 0.5);
1920                 
1921         keyboard_mouse_table.attach (*label, 0, 1, 1, 2, Gtk::FILL|Gtk::EXPAND, 0);
1922         keyboard_mouse_table.attach (delete_modifier_combo, 1, 2, 1, 2, Gtk::FILL|Gtk::EXPAND, 0);
1923
1924         label = manage (new Label (_("+ button")));
1925         label->set_name ("OptionsLabel");
1926
1927         keyboard_mouse_table.attach (*label, 3, 4, 1, 2, Gtk::FILL|Gtk::EXPAND, 0);
1928         keyboard_mouse_table.attach (delete_button_spin, 4, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, 0);
1929
1930         delete_button_spin.set_name ("OptionsEntry");
1931         delete_button_adjustment.set_value (Keyboard::delete_button());
1932         delete_button_adjustment.value_changed.connect (mem_fun(*this, &OptionEditor::delete_button_changed));
1933
1934         snap_modifier_combo.set_popdown_strings (dumb);
1935         snap_modifier_combo.get_entry()->set_editable (false);
1936         snap_modifier_combo.get_entry()->set_name ("OptionsEntry");
1937         snap_modifier_combo.set_use_arrows_always (true);
1938         snap_modifier_combo.get_popwin()->unmap_event.connect (mem_fun(*this, &OptionEditor::snap_modifier_chosen));
1939         
1940         for (int x = 0; modifiers[x].name; ++x) {
1941                 if (modifiers[x].modifier == (guint) Keyboard::snap_modifier ()) {
1942                         snap_modifier_combo.get_entry()->set_text (_(modifiers[x].name));
1943                         break;
1944                 }
1945         }
1946
1947         label = manage (new Label (_("Ignore snap using")));
1948         label->set_name ("OptionsLabel");
1949         label->set_alignment (1.0, 0.5);
1950         
1951         keyboard_mouse_table.attach (*label, 0, 1, 2, 3, Gtk::FILL|Gtk::EXPAND, 0);
1952         keyboard_mouse_table.attach (snap_modifier_combo, 1, 2, 2, 3, Gtk::FILL|Gtk::EXPAND, 0);
1953 }
1954
1955 gint
1956 OptionEditor::edit_modifier_chosen (GdkEventAny *ev)
1957 {
1958         string txt;
1959         
1960         txt = edit_modifier_combo.get_entry()->get_text();
1961
1962         for (int i = 0; modifiers[i].name; ++i) {
1963                 if (txt == _(modifiers[i].name)) {
1964                         Keyboard::set_edit_modifier (modifiers[i].modifier);
1965                         break;
1966                 }
1967         }
1968         return TRUE;
1969 }
1970
1971 gint
1972 OptionEditor::delete_modifier_chosen (GdkEventAny *ev)
1973 {
1974         string txt;
1975         
1976         txt = delete_modifier_combo.get_entry()->get_text();
1977
1978         for (int i = 0; modifiers[i].name; ++i) {
1979                 if (txt == _(modifiers[i].name)) {
1980                         Keyboard::set_delete_modifier (modifiers[i].modifier);
1981                         break;
1982                 }
1983         }
1984         return TRUE;
1985 }
1986
1987 gint
1988 OptionEditor::snap_modifier_chosen (GdkEventAny *ev)
1989 {
1990         string txt;
1991         
1992         txt = snap_modifier_combo.get_entry()->get_text();
1993
1994         for (int i = 0; modifiers[i].name; ++i) {
1995                 if (txt == _(modifiers[i].name)) {
1996                         Keyboard::set_snap_modifier (modifiers[i].modifier);
1997                         break;
1998                 }
1999         }
2000         return TRUE;
2001 }
2002
2003 void
2004 OptionEditor::delete_button_changed ()
2005 {
2006         Keyboard::set_delete_button ((guint) delete_button_adjustment.get_value());
2007 }
2008
2009 void
2010 OptionEditor::edit_button_changed ()
2011 {
2012         Keyboard::set_edit_button ((guint) edit_button_adjustment.get_value());
2013 }
2014
2015 void
2016 OptionEditor::fixup_combo_size (Gtk::Combo& combo, vector<string>& strings)
2017 {
2018         /* find the widest string */
2019
2020         string::size_type maxlen = 0;
2021         string maxstring;
2022
2023         for (vector<string>::iterator i = strings.begin(); i != strings.end(); ++i) {
2024                 string::size_type l;
2025
2026                 if ((l = (*i).length()) > maxlen) {
2027                         maxlen = l;
2028                         maxstring = *i;
2029                 }
2030         }
2031
2032         /* try to include ascenders and descenders */
2033
2034         if (maxstring.length() > 2) {
2035                 maxstring[0] = 'g';
2036                 maxstring[1] = 'l';
2037         }
2038
2039         const guint32 FUDGE = 10; // Combo's are stupid - they steal space from the entry for the button
2040
2041         set_size_request_to_display_given_text (*combo.get_entry(), maxstring.c_str(), 10 + FUDGE, 10);
2042 }
2043
2044 void
2045 OptionEditor::map_some_session_state (CheckButton& button, bool (Session::*get)() const)
2046 {
2047         if (session) {
2048                 button.set_active ((session->*get)());
2049         }
2050 }
2051