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