fix segv
[ardour.git] / gtk2_ardour / ardour_ui2.cc
1 /*
2     Copyright (C) 1999 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 <fcntl.h>
22 #include <signal.h>
23 #include <unistd.h>
24 #include <cerrno>
25 #include <iostream>
26 #include <cmath>
27
28 #include <sigc++/bind.h>
29 #include <pbd/error.h>
30 #include <pbd/basename.h>
31 #include <pbd/fastlog.h>
32 #include <gtkmm2ext/pix.h>
33 #include <gtkmm2ext/utils.h>
34 #include <gtkmm2ext/click_box.h>
35 #include <gtkmm2ext/tearoff.h>
36
37 #include <ardour/audioengine.h>
38 #include <ardour/ardour.h>
39 #include <ardour/route.h>
40
41 #include "ardour_ui.h"
42 #include "public_editor.h"
43 #include "audio_clock.h"
44 #include "actions.h"
45 #include "utils.h"
46
47 #include "i18n.h"
48
49 using namespace std;
50 using namespace ARDOUR;
51 using namespace Gtkmm2ext;
52 using namespace Gtk;
53 using namespace Glib;
54 using namespace sigc;
55
56
57 static const gchar *psync_strings[] = {
58         N_("Internal"),
59         N_("Slave to MTC"),
60         N_("Sync with JACK"),
61         0
62 };
63
64 static vector<string> positional_sync_strings;
65
66 int     
67 ARDOUR_UI::setup_windows ()
68 {
69         using namespace Menu_Helpers;
70
71         if (create_editor ()) {
72                 error << _("UI: cannot setup editor") << endmsg;
73                 return -1;
74         }
75
76         if (create_mixer ()) {
77                 error << _("UI: cannot setup mixer") << endmsg;
78                 return -1;
79         }
80
81         /* all other dialogs are created conditionally */
82
83         we_have_dependents ();
84
85         setup_clock ();
86         setup_transport();
87         setup_adjustables ();
88         build_menu_bar ();
89
90         top_packer.pack_start (menu_bar_base, false, false);
91         top_packer.pack_start (transport_frame, false, false);
92
93         editor->add_toplevel_controls (top_packer);
94
95         return 0;
96 }
97
98 void
99 ARDOUR_UI::setup_adjustables ()
100 {
101         adjuster_table.set_homogeneous (true);
102
103         online_control_strings.push_back (_("MMC + Local"));
104         online_control_strings.push_back (_("MMC"));
105         online_control_strings.push_back (_("Local"));
106
107         online_control_button = new GlobalClickBox ("CONTROL",
108                                                     online_control_strings);
109
110         online_control_button->adjustment.signal_value_changed().connect(mem_fun(*this,&ARDOUR_UI::control_methods_adjusted));
111
112         mmc_id_strings.push_back ("1");
113         mmc_id_strings.push_back ("2");
114         mmc_id_strings.push_back ("3");
115         mmc_id_strings.push_back ("4");
116         mmc_id_strings.push_back ("5");
117         mmc_id_strings.push_back ("6");
118         mmc_id_strings.push_back ("7");
119         mmc_id_strings.push_back ("8");
120         mmc_id_strings.push_back ("9");
121
122         mmc_id_button = new GlobalClickBox (_("MMC ID"), mmc_id_strings);
123
124         mmc_id_button->adjustment.signal_value_changed().connect (mem_fun(*this,&ARDOUR_UI::mmc_device_id_adjusted));
125
126         adjuster_table.attach (*online_control_button, 0, 2, 1, 2, FILL|EXPAND, FILL, 5, 5);
127         adjuster_table.attach (*mmc_id_button, 2, 3, 1, 2, FILL, FILL, 5, 5);
128 }
129
130 static const gchar * loop_xpm[] = {
131 "19 19 3 1",
132 "       c None",
133 ".      c #000000",
134 "+      c #FFFFFF",
135 "       ...         ",
136 "       .+..        ",
137 "       .++..       ",
138 "     ...+++....    ",
139 "   ...++++++++...  ",
140 "  ..+++.+++..+++.. ",
141 " ..++...++.....++..",
142 " .++.. .+..   ..++.",
143 " .+..  ...     ..+.",
144 " .+.            .+.",
145 " .+..     ...  ..+.",
146 " .++..   ..+. ..++.",
147 " ..++.....++...++..",
148 "  ..+++..+++.+++.. ",
149 "   ...++++++++...  ",
150 "     ....+++...    ",
151 "        ..++.      ",
152 "         ..+.      ",
153 "          ...      "};
154
155 void
156 ARDOUR_UI::transport_stopped ()
157 {
158         stop_button.set_active (true);
159         roll_button.set_active (false);
160         play_selection_button.set_active (false);
161         auto_loop_button.set_active (false);
162
163         shuttle_fract = 0;
164         shuttle_box.queue_draw ();
165
166         update_disk_space ();
167 }
168
169 static const double SHUTTLE_FRACT_SPEED1=0.48412291827; /* derived from A1,A2 */
170
171 void
172 ARDOUR_UI::transport_rolling ()
173 {
174         stop_button.set_active (false);
175
176         if (session->get_play_range()) {
177
178                 play_selection_button.set_active (true);
179                 roll_button.set_active (false);
180                 auto_loop_button.set_active (false);
181
182         } else if (session->get_auto_loop ()) {
183
184                 auto_loop_button.set_active (true);
185                 play_selection_button.set_active (false);
186                 roll_button.set_active (false);
187
188         } else {
189
190                 roll_button.set_active (true);
191                 play_selection_button.set_active (false);
192                 auto_loop_button.set_active (false);
193         }
194
195         /* reset shuttle controller */
196
197         shuttle_fract = SHUTTLE_FRACT_SPEED1;  /* speed = 1.0, believe it or not */
198         shuttle_box.queue_draw ();
199 }
200
201 void
202 ARDOUR_UI::transport_rewinding ()
203 {
204         stop_button.set_active (false);
205         roll_button.set_active (true);
206         play_selection_button.set_active (false);
207         auto_loop_button.set_active (false);
208 }
209
210 void
211 ARDOUR_UI::transport_forwarding ()
212 {
213         stop_button.set_active (false);
214         roll_button.set_active (true);
215         play_selection_button.set_active (false);
216         auto_loop_button.set_active (false);
217 }
218
219 void
220 ARDOUR_UI::setup_transport ()
221 {
222         transport_tearoff = manage (new TearOff (transport_tearoff_hbox));
223         transport_tearoff->set_name ("TransportBase");
224
225         transport_hbox.pack_start (*transport_tearoff, true, false);
226
227         transport_base.set_name ("TransportBase");
228         transport_base.add (transport_hbox);
229
230         transport_frame.set_shadow_type (SHADOW_OUT);
231         transport_frame.set_name ("BaseFrame");
232         transport_frame.add (transport_base);
233
234         transport_tearoff->Detach.connect (bind (mem_fun(*this, &ARDOUR_UI::detach_tearoff), static_cast<Box*>(&top_packer), 
235                                                  static_cast<Widget*>(&transport_frame)));
236         transport_tearoff->Attach.connect (bind (mem_fun(*this, &ARDOUR_UI::reattach_tearoff), static_cast<Box*> (&top_packer), 
237                                                  static_cast<Widget*> (&transport_frame), 1));
238
239         vector<Gdk::Color> colors;
240         Gdk::Color c;
241
242         /* record button has 3 color states, so we set 2 extra here */
243
244         c.set_rgb_p (0.91, 0.68, 0.68);
245         colors.push_back (c);
246         c.set_rgb_p (1, 0, 0);
247         colors.push_back (c);
248         rec_button.set_colors (colors);
249
250         colors.clear ();
251
252         /* other buttons get 2 color states, so add one here */
253
254         c.set_rgb_p (0.66, 0.97, 0.18);
255         colors.push_back (c);
256
257         stop_button.set_colors (colors);
258         roll_button.set_colors (colors);
259         auto_loop_button.set_colors (colors);
260         play_selection_button.set_colors (colors);
261         goto_start_button.set_colors (colors);
262         goto_end_button.set_colors (colors);
263         
264         Widget* w;
265
266         stop_button.set_active (true);
267
268         w = manage (new Image (Stock::MEDIA_PREVIOUS, ICON_SIZE_BUTTON));
269         w->show();
270         goto_start_button.add (*w);
271         w = manage (new Image (Stock::MEDIA_NEXT, ICON_SIZE_BUTTON));
272         w->show();
273         goto_end_button.add (*w);
274         w = manage (new Image (Stock::MEDIA_PLAY, ICON_SIZE_BUTTON));
275         w->show();
276         roll_button.add (*w);
277         w = manage (new Image (Stock::MEDIA_STOP, ICON_SIZE_BUTTON));
278         w->show();
279         stop_button.add (*w);
280         w = manage (new Image (Stock::MEDIA_PLAY, ICON_SIZE_BUTTON));
281         w->show();
282         play_selection_button.add (*w);
283         w = manage (new Image (Stock::MEDIA_RECORD, ICON_SIZE_BUTTON));
284         w->show();
285         rec_button.add (*w);
286         w = manage (new Image (Gdk::Pixbuf::create_from_xpm_data(loop_xpm)));
287         w->show();
288         auto_loop_button.add (*w);
289
290         RefPtr<Action> act;
291
292         act = ActionManager::get_action (X_("Transport"), X_("Stop"));
293         act->connect_proxy (stop_button);
294         act = ActionManager::get_action (X_("Transport"), X_("Roll"));
295         act->connect_proxy (roll_button);
296         act = ActionManager::get_action (X_("Transport"), X_("Record"));
297         act->connect_proxy (rec_button);
298         act = ActionManager::get_action (X_("Transport"), X_("GotoStart"));
299         act->connect_proxy (goto_start_button);
300         act = ActionManager::get_action (X_("Transport"), X_("GotoEnd"));
301         act->connect_proxy (goto_end_button);
302         act = ActionManager::get_action (X_("Transport"), X_("Loop"));
303         act->connect_proxy (auto_loop_button);
304         act = ActionManager::get_action (X_("Transport"), X_("PlaySelection"));
305         act->connect_proxy (play_selection_button);
306         act = ActionManager::get_action (X_("Transport"), X_("ToggleTimeMaster"));
307         act->connect_proxy (time_master_button);
308
309         ARDOUR_UI::instance()->tooltips().set_tip (roll_button, _("Play from playhead"));
310         ARDOUR_UI::instance()->tooltips().set_tip (stop_button, _("Stop playback"));
311         ARDOUR_UI::instance()->tooltips().set_tip (play_selection_button, _("Play range/selection"));
312         ARDOUR_UI::instance()->tooltips().set_tip (goto_start_button, _("Go to start of session"));
313         ARDOUR_UI::instance()->tooltips().set_tip (goto_end_button, _("Go to end of session"));
314         ARDOUR_UI::instance()->tooltips().set_tip (auto_loop_button, _("Play loop range"));
315         ARDOUR_UI::instance()->tooltips().set_tip (auto_return_button, _("Return to last playback start when stopped"));
316         ARDOUR_UI::instance()->tooltips().set_tip (auto_play_button, _("Start playback after any locate"));
317         ARDOUR_UI::instance()->tooltips().set_tip (auto_input_button, _("Be sensible about input monitoring"));
318         ARDOUR_UI::instance()->tooltips().set_tip (punch_in_button, _("Start recording at auto-punch start"));
319         ARDOUR_UI::instance()->tooltips().set_tip (punch_out_button, _("Stop recording at auto-punch end"));
320         ARDOUR_UI::instance()->tooltips().set_tip (click_button, _("Enable/Disable audio click"));
321         ARDOUR_UI::instance()->tooltips().set_tip (time_master_button, _("Does Ardour control the time?"));
322         ARDOUR_UI::instance()->tooltips().set_tip (shuttle_box, _("Shuttle speed control"));
323         ARDOUR_UI::instance()->tooltips().set_tip (shuttle_units_button, _("Select semitones or %%-age for speed display"));
324         ARDOUR_UI::instance()->tooltips().set_tip (speed_display_box, _("Current transport speed"));
325         
326         shuttle_box.set_flags (CAN_FOCUS);
327         shuttle_box.set_events (shuttle_box.get_events() | Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::POINTER_MOTION_MASK);
328         shuttle_box.set_size_request (100, 15);
329
330         shuttle_box.set_name ("TransportButton");
331         goto_start_button.set_name ("TransportButton");
332         goto_end_button.set_name ("TransportButton");
333         roll_button.set_name ("TransportButton");
334         stop_button.set_name ("TransportButton");
335         play_selection_button.set_name ("TransportButton");
336         rec_button.set_name ("TransportRecButton");
337         auto_loop_button.set_name ("TransportButton");
338         auto_return_button.set_name ("TransportButton");
339         auto_play_button.set_name ("TransportButton");
340         auto_input_button.set_name ("TransportButton");
341         punch_in_button.set_name ("TransportButton");
342         punch_out_button.set_name ("TransportButton");
343         click_button.set_name ("TransportButton");
344         time_master_button.set_name ("TransportButton");
345
346         goto_start_button.unset_flags (CAN_FOCUS);
347         goto_end_button.unset_flags (CAN_FOCUS);
348         roll_button.unset_flags (CAN_FOCUS);
349         stop_button.unset_flags (CAN_FOCUS);
350         play_selection_button.unset_flags (CAN_FOCUS);
351         rec_button.unset_flags (CAN_FOCUS);
352         auto_loop_button.unset_flags (CAN_FOCUS);
353         auto_return_button.unset_flags (CAN_FOCUS);
354         auto_play_button.unset_flags (CAN_FOCUS);
355         auto_input_button.unset_flags (CAN_FOCUS);
356         punch_out_button.unset_flags (CAN_FOCUS);
357         punch_in_button.unset_flags (CAN_FOCUS);
358         click_button.unset_flags (CAN_FOCUS);
359         time_master_button.unset_flags (CAN_FOCUS);
360         
361         goto_start_button.set_events (goto_start_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
362         goto_end_button.set_events (goto_end_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
363         roll_button.set_events (roll_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
364         stop_button.set_events (stop_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
365         play_selection_button.set_events (play_selection_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
366         rec_button.set_events (rec_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
367         auto_loop_button.set_events (auto_loop_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
368         auto_return_button.set_events (auto_return_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
369         auto_play_button.set_events (auto_play_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
370         auto_input_button.set_events (auto_input_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
371         click_button.set_events (click_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
372         punch_in_button.set_events (punch_in_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
373         punch_out_button.set_events (punch_out_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
374         time_master_button.set_events (punch_out_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
375
376         shuttle_box.signal_button_press_event().connect (mem_fun(*this, &ARDOUR_UI::shuttle_box_button_press));
377         shuttle_box.signal_button_release_event().connect (mem_fun(*this, &ARDOUR_UI::shuttle_box_button_release));
378         shuttle_box.signal_motion_notify_event().connect (mem_fun(*this, &ARDOUR_UI::shuttle_box_motion));
379         shuttle_box.signal_expose_event().connect (mem_fun(*this, &ARDOUR_UI::shuttle_box_expose));
380
381         /* clocks, etc. */
382
383         ARDOUR_UI::Clock.connect (bind (mem_fun (primary_clock, &AudioClock::set), false));
384         ARDOUR_UI::Clock.connect (bind (mem_fun (secondary_clock, &AudioClock::set), false));
385
386         primary_clock.set_mode (AudioClock::SMPTE);
387         secondary_clock.set_mode (AudioClock::BBT);
388
389         primary_clock.ValueChanged.connect (mem_fun(*this, &ARDOUR_UI::primary_clock_value_changed));
390         secondary_clock.ValueChanged.connect (mem_fun(*this, &ARDOUR_UI::secondary_clock_value_changed));
391
392         ARDOUR_UI::instance()->tooltips().set_tip (primary_clock, _("Primary clock"));
393         ARDOUR_UI::instance()->tooltips().set_tip (secondary_clock, _("secondary clock"));
394
395         /* options */
396
397         auto_return_button.signal_toggled().connect (mem_fun(*this,&ARDOUR_UI::toggle_auto_return));
398         auto_play_button.signal_toggled().connect (mem_fun(*this,&ARDOUR_UI::toggle_auto_play));
399         auto_input_button.signal_toggled().connect (mem_fun(*this,&ARDOUR_UI::toggle_auto_input));
400         click_button.signal_toggled().connect (mem_fun(*this,&ARDOUR_UI::toggle_click));
401         punch_in_button.signal_toggled().connect (mem_fun(*this,&ARDOUR_UI::toggle_punch_in));
402         punch_out_button.signal_toggled().connect (mem_fun(*this,&ARDOUR_UI::toggle_punch_out));
403
404         preroll_button.unset_flags (CAN_FOCUS);
405         preroll_button.set_events (preroll_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
406         preroll_button.set_name ("TransportButton");
407
408         postroll_button.unset_flags (CAN_FOCUS);
409         postroll_button.set_events (postroll_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
410         postroll_button.set_name ("TransportButton");
411
412         preroll_clock.set_mode (AudioClock::MinSec);
413         preroll_clock.set_name ("TransportClockDisplay");
414         postroll_clock.set_mode (AudioClock::MinSec);
415         postroll_clock.set_name ("TransportClockDisplay");
416
417         /* alerts */
418
419         /* CANNOT bind these to clicked or toggled, must use pressed or released */
420
421         solo_alert_button.set_name ("TransportSoloAlert");
422         solo_alert_button.signal_pressed().connect (mem_fun(*this,&ARDOUR_UI::solo_alert_toggle));
423         auditioning_alert_button.set_name ("TransportAuditioningAlert");
424         auditioning_alert_button.signal_pressed().connect (mem_fun(*this,&ARDOUR_UI::audition_alert_toggle));
425
426         alert_box.pack_start (solo_alert_button);
427         alert_box.pack_start (auditioning_alert_button);
428
429         transport_tearoff_hbox.set_border_width (5);
430
431         transport_tearoff_hbox.pack_start (goto_start_button, false, false);
432         transport_tearoff_hbox.pack_start (goto_end_button, false, false);
433
434         Frame* sframe = manage (new Frame);
435         VBox*  svbox = manage (new VBox);
436         HBox*  shbox = manage (new HBox);
437
438         sframe->set_shadow_type (SHADOW_IN);
439         sframe->add (shuttle_box);
440
441         shuttle_box.set_name (X_("ShuttleControl"));
442
443         speed_display_box.add (speed_display_label);
444         speed_display_box.set_name (X_("ShuttleDisplay"));
445
446         shuttle_units_button.set_name (X_("ShuttleButton"));
447         shuttle_units_button.signal_clicked().connect (mem_fun(*this, &ARDOUR_UI::shuttle_unit_clicked));
448         
449         shuttle_style_button.set_name (X_("ShuttleButton"));
450
451         vector<string> shuttle_strings;
452         shuttle_strings.push_back (_("sprung"));
453         shuttle_strings.push_back (_("wheel"));
454         set_popdown_strings (shuttle_style_button, shuttle_strings);
455         shuttle_style_button.signal_changed().connect (mem_fun (*this, &ARDOUR_UI::shuttle_style_changed));
456
457         Frame* sdframe = manage (new Frame);
458
459         sdframe->set_shadow_type (SHADOW_IN);
460         sdframe->add (speed_display_box);
461
462         positional_sync_strings = internationalize (psync_strings);
463
464         set_popdown_strings (sync_option_combo, positional_sync_strings);
465         sync_option_combo.set_active_text (positional_sync_strings.front());
466         sync_option_combo.signal_changed().connect (mem_fun (*this, &ARDOUR_UI::sync_option_changed));
467
468         shbox->pack_start (*sdframe, false, false);
469         shbox->pack_start (shuttle_units_button, true, true);
470         shbox->pack_start (shuttle_style_button, false, false);
471         
472         svbox->pack_start (*sframe, false, false);
473         svbox->pack_start (*shbox, false, false);
474
475         transport_tearoff_hbox.pack_start (*svbox, false, false, 5);
476
477         transport_tearoff_hbox.pack_start (auto_loop_button, false, false);
478         transport_tearoff_hbox.pack_start (play_selection_button, false, false);
479         transport_tearoff_hbox.pack_start (roll_button, false, false);
480         transport_tearoff_hbox.pack_start (stop_button, false, false);
481         transport_tearoff_hbox.pack_start (rec_button, false, false, 10);
482
483         transport_tearoff_hbox.pack_start (primary_clock, false, false, 5);
484         transport_tearoff_hbox.pack_start (secondary_clock, false, false, 5);
485
486         transport_tearoff_hbox.pack_start (sync_option_combo, false, false);
487         transport_tearoff_hbox.pack_start (time_master_button, false, false);
488         transport_tearoff_hbox.pack_start (punch_in_button, false, false);
489         transport_tearoff_hbox.pack_start (punch_in_button, false, false);
490         transport_tearoff_hbox.pack_start (punch_out_button, false, false);
491         transport_tearoff_hbox.pack_start (auto_input_button, false, false);
492         transport_tearoff_hbox.pack_start (auto_return_button, false, false);
493         transport_tearoff_hbox.pack_start (auto_play_button, false, false);
494         transport_tearoff_hbox.pack_start (click_button, false, false);
495         
496         /* desensitize */
497
498         set_transport_sensitivity (false);
499
500 //      transport_tearoff_hbox.pack_start (preroll_button, false, false);
501 //      transport_tearoff_hbox.pack_start (preroll_clock, false, false);
502
503 //      transport_tearoff_hbox.pack_start (postroll_button, false, false);
504 //      transport_tearoff_hbox.pack_start (postroll_clock, false, false);
505
506         transport_tearoff_hbox.pack_start (alert_box, false, false, 5);
507 }
508
509 void
510 ARDOUR_UI::setup_clock ()
511 {
512         ARDOUR_UI::Clock.connect (bind (mem_fun (big_clock, &AudioClock::set), false));
513         
514         big_clock_window = new Window (WINDOW_TOPLEVEL);
515         
516         big_clock_window->set_border_width (0);
517         big_clock_window->add  (big_clock);
518         big_clock_window->set_title (_("ardour: clock"));
519         big_clock_window->set_type_hint (Gdk::WINDOW_TYPE_HINT_MENU);
520         big_clock_window->signal_realize().connect (bind (sigc::ptr_fun (set_decoration), big_clock_window,  (Gdk::DECOR_BORDER|Gdk::DECOR_RESIZEH)));
521         big_clock_window->signal_unmap().connect (bind (sigc::ptr_fun(&ActionManager::uncheck_toggleaction), X_("<Actions>/Common/ToggleBigClock")));
522
523         manage_window (*big_clock_window);
524 }
525
526 void
527 ARDOUR_UI::manage_window (Window& win)
528 {
529         win.signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), &win));
530         win.signal_enter_notify_event().connect (bind (mem_fun (Keyboard::the_keyboard(), &Keyboard::enter_window), &win));
531         win.signal_leave_notify_event().connect (bind (mem_fun (Keyboard::the_keyboard(), &Keyboard::leave_window), &win));
532 }
533
534 void
535 ARDOUR_UI::detach_tearoff (Box* b, Widget* w)
536 {
537         editor->ensure_float (transport_tearoff->tearoff_window());
538         b->remove (*w);
539 }
540
541 void
542 ARDOUR_UI::reattach_tearoff (Box* b, Widget* w, int32_t n)
543 {
544         b->pack_start (*w);
545         b->reorder_child (*w, n);
546 }
547
548 void
549 ARDOUR_UI::soloing_changed (bool onoff)
550 {
551         if (solo_alert_button.get_active() != onoff) {
552                 solo_alert_button.set_active (onoff);
553         }
554 }
555
556 void
557 ARDOUR_UI::_auditioning_changed (bool onoff)
558 {
559         if (auditioning_alert_button.get_active() != onoff) {
560                 auditioning_alert_button.set_active (onoff);
561                 set_transport_sensitivity (!onoff);
562         }
563 }
564
565 void
566 ARDOUR_UI::auditioning_changed (bool onoff)
567 {
568         Gtkmm2ext::UI::instance()->call_slot(bind (mem_fun(*this, &ARDOUR_UI::_auditioning_changed), onoff));
569 }
570
571 void
572 ARDOUR_UI::audition_alert_toggle ()
573 {
574         if (session) {
575                 session->cancel_audition();
576         }
577 }
578
579 void
580 ARDOUR_UI::solo_alert_toggle ()
581 {
582         if (session) {
583                 session->set_all_solo (!session->soloing());
584         }
585 }
586
587 void
588 ARDOUR_UI::solo_blink (bool onoff)
589 {
590         if (session == 0) {
591                 return;
592         }
593         
594         if (session->soloing()) {
595                 if (onoff) {
596                         solo_alert_button.set_state (STATE_ACTIVE);
597                 } else {
598                         solo_alert_button.set_state (STATE_NORMAL);
599                 }
600         } else {
601                 solo_alert_button.set_active (false);
602                 solo_alert_button.set_state (STATE_NORMAL);
603         }
604 }
605
606 void
607 ARDOUR_UI::audition_blink (bool onoff)
608 {
609         if (session == 0) {
610                 return;
611         }
612         
613         if (session->is_auditioning()) {
614                 if (onoff) {
615                         auditioning_alert_button.set_state (STATE_ACTIVE);
616                 } else {
617                         auditioning_alert_button.set_state (STATE_NORMAL);
618                 }
619         } else {
620                 auditioning_alert_button.set_active (false);
621                 auditioning_alert_button.set_state (STATE_NORMAL);
622         }
623 }
624
625
626 gint
627 ARDOUR_UI::shuttle_box_button_press (GdkEventButton* ev)
628 {
629         if (!session) {
630                 return TRUE;
631         }
632
633         switch (ev->button) {
634         case 1:
635                 shuttle_box.add_modal_grab ();
636                 shuttle_grabbed = true;
637                 mouse_shuttle (ev->x, true);
638                 break;
639
640         case 2:
641         case 3:
642                 return TRUE;
643                 break;
644         }
645
646         return TRUE;
647 }
648
649 gint
650 ARDOUR_UI::shuttle_box_button_release (GdkEventButton* ev)
651 {
652         if (!session) {
653                 return TRUE;
654         }
655
656         switch (ev->button) {
657         case 1:
658                 mouse_shuttle (ev->x, true);
659                 shuttle_grabbed = false;
660                 shuttle_box.remove_modal_grab ();
661                 if (shuttle_behaviour == Sprung) {
662                         shuttle_fract = SHUTTLE_FRACT_SPEED1;
663                         session->request_transport_speed (1.0);
664                         shuttle_box.queue_draw ();
665                 }
666                 return TRUE;
667
668         case 2:
669                 if (session->transport_rolling()) {
670                         shuttle_fract = SHUTTLE_FRACT_SPEED1;
671                         session->request_transport_speed (1.0);
672                 } else {
673                         shuttle_fract = 0;
674                 }
675                 shuttle_box.queue_draw ();
676                 return TRUE;
677
678         case 3:
679                 return TRUE;
680                 
681         case 4:
682                 shuttle_fract += 0.005;
683                 break;
684         case 5:
685                 shuttle_fract -= 0.005;
686                 break;
687         }
688
689         use_shuttle_fract (true);
690
691         return TRUE;
692 }
693
694 gint
695 ARDOUR_UI::shuttle_box_motion (GdkEventMotion* ev)
696 {
697         if (!session || !shuttle_grabbed) {
698                 return TRUE;
699         }
700
701         return mouse_shuttle (ev->x, false);
702 }
703
704 gint
705 ARDOUR_UI::mouse_shuttle (double x, bool force)
706 {
707         double half_width = shuttle_box.get_width() / 2.0;
708         double distance = x - half_width;
709
710         if (distance > 0) {
711                 distance = min (distance, half_width);
712         } else {
713                 distance = max (distance, -half_width);
714         }
715
716         shuttle_fract = distance / half_width;
717         use_shuttle_fract (force);
718         return TRUE;
719 }
720
721 void
722 ARDOUR_UI::use_shuttle_fract (bool force)
723 {
724         struct timeval now;
725         struct timeval diff;
726         
727         /* do not attempt to submit a motion-driven transport speed request
728            more than once per process cycle.
729          */
730
731         gettimeofday (&now, 0);
732         timersub (&now, &last_shuttle_request, &diff);
733
734         if (!force && (diff.tv_usec + (diff.tv_sec * 1000000)) < engine->usecs_per_cycle()) {
735                 return;
736         }
737         
738         last_shuttle_request = now;
739
740         bool neg = (shuttle_fract < 0.0);
741
742         double fract = 1 - sqrt (1 - (shuttle_fract * shuttle_fract)); // Formula A1
743
744         if (neg) {
745                 fract = -fract;
746         }
747
748         session->request_transport_speed (8.0 * fract); // Formula A2
749         shuttle_box.queue_draw ();
750 }
751
752 gint
753 ARDOUR_UI::shuttle_box_expose (GdkEventExpose* event)
754 {
755         gint x;
756         Glib::RefPtr<Gdk::Window> win (shuttle_box.get_window());
757
758         /* redraw the background */
759
760         win->draw_rectangle (shuttle_box.get_style()->get_bg_gc (shuttle_box.get_state()),
761                              true,
762                              event->area.x, event->area.y,
763                              event->area.width, event->area.height);
764
765
766         x = (gint) floor ((shuttle_box.get_width() / 2.0) + (0.5 * (shuttle_box.get_width() * shuttle_fract)));
767
768         /* draw line */
769
770         win->draw_line (shuttle_box.get_style()->get_fg_gc (shuttle_box.get_state()),
771                         x,
772                         0,
773                         x,
774                         shuttle_box.get_height());
775         return TRUE;
776 }
777
778 void
779 ARDOUR_UI::shuttle_unit_clicked ()
780 {
781         if (shuttle_unit_menu == 0) {
782                 shuttle_unit_menu = dynamic_cast<Menu*> (ActionManager::get_widget ("/ShuttleUnitPopup"));
783         }
784         shuttle_unit_menu->popup (1, 0);
785 }
786
787 void
788 ARDOUR_UI::set_shuttle_units (ShuttleUnits u)
789 {
790         switch ((shuttle_units = u)) {
791         case Percentage:
792                 static_cast<Label*>(shuttle_units_button.get_child())->set_text ("% ");
793                 break;
794         case Semitones:
795                 static_cast<Label*>(shuttle_units_button.get_child())->set_text (_("st"));
796                 break;
797         }
798 }
799
800 void
801 ARDOUR_UI::shuttle_style_changed ()
802 {
803         ustring str = shuttle_style_button.get_active_text ();
804
805         if (str == _("sprung")) {
806                 set_shuttle_behaviour (Sprung);
807         } else if (str == _("wheel")) {
808                 set_shuttle_behaviour (Wheel);
809         }
810 }
811
812
813 void
814 ARDOUR_UI::set_shuttle_behaviour (ShuttleBehaviour b)
815 {
816         switch ((shuttle_behaviour = b)) {
817         case Sprung:
818                 shuttle_style_button.set_active_text (_("sprung"));
819                 shuttle_fract = 0.0;
820                 shuttle_box.queue_draw ();
821                 if (session) {
822                         if (session->transport_rolling()) {
823                                 shuttle_fract = SHUTTLE_FRACT_SPEED1;
824                                 session->request_transport_speed (1.0);
825                         }
826                 }
827                 break;
828         case Wheel:
829                 shuttle_style_button.set_active_text (_("wheel"));
830                 break;
831         }
832 }
833
834 void
835 ARDOUR_UI::update_speed_display ()
836 {
837         if (!session) {
838                 if (last_speed_displayed != 0) {
839                         speed_display_label.set_text (_("stopped"));
840                         last_speed_displayed = 0;
841                 }
842                 return;
843         }
844
845         char buf[32];
846         float x = session->transport_speed ();
847
848         if (x != last_speed_displayed) {
849
850                 if (x != 0) {
851                         if (shuttle_units == Percentage) {
852                                 snprintf (buf, sizeof (buf), "%.4f", x);
853                         } else {
854                                 if (x < 0) {
855                                         snprintf (buf, sizeof (buf), "< %.1f", 12.0 * fast_log2 (-x));
856                                 } else {
857                                         snprintf (buf, sizeof (buf), "> %.1f", 12.0 * fast_log2 (x));
858                                 }
859                         }
860                         speed_display_label.set_text (buf);
861                 } else {
862                         speed_display_label.set_text (_("stopped"));
863                 }
864
865                 last_speed_displayed = x;
866         }
867 }       
868         
869 void
870 ARDOUR_UI::set_transport_sensitivity (bool yn)
871 {
872         ActionManager::set_sensitive (ActionManager::transport_sensitive_actions, yn);
873         shuttle_box.set_sensitive (yn);
874 }
875
876 void
877 ARDOUR_UI::editor_realized ()
878 {
879         set_size_request_to_display_given_text (speed_display_box, _("stopped"), 2, 2);
880         /* XXX: this should really be saved in instant.xml or something similar and restored from there */
881         shuttle_style_button.set_active_text (_("sprung"));
882         const guint32 FUDGE = 20; // Combo's are stupid - they steal space from the entry for the button
883         set_size_request_to_display_given_text (shuttle_style_button, _("sprung"), 2+FUDGE, 10);
884 }
885
886 void
887 ARDOUR_UI::sync_option_changed ()
888 {
889         string which;
890
891         if (session == 0) {
892                 return;
893         }
894
895         which = sync_option_combo.get_active_text();
896
897         if (which == positional_sync_strings[Session::None]) {
898                 session->request_slave_source (Session::None);
899         } else if (which == positional_sync_strings[Session::MTC]) {
900                 session->request_slave_source (Session::MTC);
901         } else if (which == positional_sync_strings[Session::JACK]) {
902                 session->request_slave_source (Session::JACK);
903         } 
904 }