f375594cc6e316e95ebabe019f5213f00b29686b
[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 "utils.h"
45
46 #include "i18n.h"
47
48 using namespace std;
49 using namespace ARDOUR;
50 using namespace Gtkmm2ext;
51 using namespace Gtk;
52 using namespace sigc;
53
54 int     
55 ARDOUR_UI::setup_windows ()
56 {
57         using namespace Menu_Helpers;
58
59         if (create_editor ()) {
60                 error << _("UI: cannot setup editor") << endmsg;
61                 return -1;
62         }
63
64         if (create_mixer ()) {
65                 error << _("UI: cannot setup mixer") << endmsg;
66                 return -1;
67         }
68
69         if (create_meter_bridge ()) {
70                 error << _("UI: cannot setup meter_bridge") << endmsg;
71                 return -1;
72         }
73
74         /* all other dialogs are created conditionally */
75
76         we_have_dependents ();
77
78         setup_clock ();
79         setup_transport();
80         setup_adjustables ();
81         build_menu_bar ();
82
83         top_packer.pack_start (menu_bar_base, false, false);
84         top_packer.pack_start (transport_frame, false, false);
85
86         editor->add_toplevel_controls (top_packer);
87
88         return 0;
89 }
90
91
92 void
93 ARDOUR_UI::setup_adjustables ()
94
95 {
96         adjuster_table.set_homogeneous (true);
97
98         online_control_strings.push_back (_("MMC + Local"));
99         online_control_strings.push_back (_("MMC"));
100         online_control_strings.push_back (_("Local"));
101
102         online_control_button = new GlobalClickBox ("CONTROL",
103                                                     online_control_strings);
104
105         online_control_button->adjustment.signal_value_changed().connect(mem_fun(*this,&ARDOUR_UI::control_methods_adjusted));
106
107         mmc_id_strings.push_back ("1");
108         mmc_id_strings.push_back ("2");
109         mmc_id_strings.push_back ("3");
110         mmc_id_strings.push_back ("4");
111         mmc_id_strings.push_back ("5");
112         mmc_id_strings.push_back ("6");
113         mmc_id_strings.push_back ("7");
114         mmc_id_strings.push_back ("8");
115         mmc_id_strings.push_back ("9");
116
117         mmc_id_button = new GlobalClickBox (_("MMC ID"), mmc_id_strings);
118
119         mmc_id_button->adjustment.signal_value_changed().connect (mem_fun(*this,&ARDOUR_UI::mmc_device_id_adjusted));
120
121         adjuster_table.attach (*online_control_button, 0, 2, 1, 2, FILL|EXPAND, FILL, 5, 5);
122         adjuster_table.attach (*mmc_id_button, 2, 3, 1, 2, FILL, FILL, 5, 5);
123 }
124
125 #include "transport_xpms"
126
127 void
128 ARDOUR_UI::transport_stopped ()
129 {
130         roll_button.set_active (false);
131         play_selection_button.set_active (false);
132         auto_loop_button.set_active (false);
133
134         shuttle_fract = 0;
135         shuttle_box.queue_draw ();
136
137         update_disk_space ();
138 }
139
140 static const double SHUTTLE_FRACT_SPEED1=0.48412291827; /* derived from A1,A2 */
141
142 void
143 ARDOUR_UI::transport_rolling ()
144 {
145         if (session->get_play_range()) {
146
147                 play_selection_button.set_active (true);
148                 roll_button.set_active (false);
149                 auto_loop_button.set_active (false);
150
151         } else if (session->get_auto_loop ()) {
152
153                 auto_loop_button.set_active (true);
154                 play_selection_button.set_active (false);
155                 roll_button.set_active (false);
156
157         } else {
158
159                 roll_button.set_active (true);
160                 play_selection_button.set_active (false);
161                 auto_loop_button.set_active (false);
162         }
163
164         /* reset shuttle controller */
165
166         shuttle_fract = SHUTTLE_FRACT_SPEED1;  /* speed = 1.0, believe it or not */
167         shuttle_box.queue_draw ();
168 }
169
170 void
171 ARDOUR_UI::transport_rewinding ()
172 {
173         roll_button.set_active (true);
174         play_selection_button.set_active (false);
175         auto_loop_button.set_active (false);
176 }
177
178 void
179 ARDOUR_UI::transport_forwarding ()
180 {
181         roll_button.set_active (true);
182         play_selection_button.set_active (false);
183         auto_loop_button.set_active (false);
184 }
185
186 void
187 ARDOUR_UI::setup_transport ()
188 {
189         transport_tearoff = manage (new TearOff (transport_tearoff_hbox));
190         transport_tearoff->set_name ("TransportBase");
191
192         transport_hbox.pack_start (*transport_tearoff, true, false);
193
194         transport_base.set_name ("TransportBase");
195         transport_base.add (transport_hbox);
196
197         transport_frame.set_shadow_type (Gtk::SHADOW_OUT);
198         transport_frame.set_name ("BaseFrame");
199         transport_frame.add (transport_base);
200
201         transport_tearoff->Detach.connect (bind (mem_fun(*this, &ARDOUR_UI::detach_tearoff), static_cast<Gtk::Box*>(&top_packer), 
202                                                  static_cast<Gtk::Widget*>(&transport_frame)));
203         transport_tearoff->Attach.connect (bind (mem_fun(*this, &ARDOUR_UI::reattach_tearoff), static_cast<Gtk::Box*> (&top_packer), 
204                                                  static_cast<Gtk::Widget*> (&transport_frame), 1));
205
206
207         goto_start_button.add (*(manage (new Image (Gdk::Pixbuf::create_from_xpm_data(start_xpm)))));
208         goto_end_button.add (*(manage (new Image (Gdk::Pixbuf::create_from_xpm_data(end_xpm)))));
209         roll_button.add (*(manage (new Image (Gdk::Pixbuf::create_from_xpm_data(arrow_xpm)))));
210         
211         stop_button.add (*(manage (new Image (Gdk::Pixbuf::create_from_xpm_data(stop_xpm)))));
212         play_selection_button.add (*(manage (new Image (Gdk::Pixbuf::create_from_xpm_data(play_selection_xpm)))));
213         rec_button.add (*(manage (new Image (Gdk::Pixbuf::create_from_xpm_data(rec_xpm)))));
214         auto_loop_button.add (*(manage (new Image (Gdk::Pixbuf::create_from_xpm_data(loop_xpm)))));
215
216         ARDOUR_UI::instance()->tooltips().set_tip (roll_button, _("Play from playhead"));
217         ARDOUR_UI::instance()->tooltips().set_tip (stop_button, _("Stop playback"));
218         ARDOUR_UI::instance()->tooltips().set_tip (play_selection_button, _("Play range/selection"));
219         ARDOUR_UI::instance()->tooltips().set_tip (goto_start_button, _("Go to start of session"));
220         ARDOUR_UI::instance()->tooltips().set_tip (goto_end_button, _("Go to end of session"));
221         ARDOUR_UI::instance()->tooltips().set_tip (auto_loop_button, _("Play loop range"));
222         ARDOUR_UI::instance()->tooltips().set_tip (auto_return_button, _("Return to last playback start when stopped"));
223         ARDOUR_UI::instance()->tooltips().set_tip (auto_play_button, _("Start playback after any locate"));
224         ARDOUR_UI::instance()->tooltips().set_tip (auto_input_button, _("Be sensible about input monitoring"));
225         ARDOUR_UI::instance()->tooltips().set_tip (punch_in_button, _("Start recording at auto-punch start"));
226         ARDOUR_UI::instance()->tooltips().set_tip (punch_out_button, _("Stop recording at auto-punch end"));
227         ARDOUR_UI::instance()->tooltips().set_tip (click_button, _("Enable/Disable audio click"));
228         ARDOUR_UI::instance()->tooltips().set_tip (follow_button, _("Enable/Disable follow playhead"));
229         ARDOUR_UI::instance()->tooltips().set_tip (shuttle_box, _("Shuttle speed control"));
230         ARDOUR_UI::instance()->tooltips().set_tip (shuttle_units_button, _("Select semitones or %%-age for speed display"));
231         ARDOUR_UI::instance()->tooltips().set_tip (shuttle_style_button, _("Select sprung or wheel behaviour"));
232         ARDOUR_UI::instance()->tooltips().set_tip (speed_display_box, _("Current transport speed"));
233         
234         shuttle_box.set_flags (CAN_FOCUS);
235         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);
236         shuttle_box.set_size_request (100, 15);
237
238         shuttle_box.set_name ("TransportButton");
239         goto_start_button.set_name ("TransportButton");
240         goto_end_button.set_name ("TransportButton");
241         roll_button.set_name ("TransportButton");
242         stop_button.set_name ("TransportButton");
243         play_selection_button.set_name ("TransportButton");
244         rec_button.set_name ("TransportRecButton");
245         auto_loop_button.set_name ("TransportButton");
246         auto_return_button.set_name ("TransportButton");
247         auto_play_button.set_name ("TransportButton");
248         auto_input_button.set_name ("TransportButton");
249         punch_in_button.set_name ("TransportButton");
250         punch_out_button.set_name ("TransportButton");
251         click_button.set_name ("TransportButton");
252         follow_button.set_name ("TransportButton");
253         
254         goto_start_button.unset_flags (Gtk::CAN_FOCUS);
255         goto_end_button.unset_flags (Gtk::CAN_FOCUS);
256         roll_button.unset_flags (Gtk::CAN_FOCUS);
257         stop_button.unset_flags (Gtk::CAN_FOCUS);
258         play_selection_button.unset_flags (Gtk::CAN_FOCUS);
259         rec_button.unset_flags (Gtk::CAN_FOCUS);
260         auto_loop_button.unset_flags (Gtk::CAN_FOCUS);
261         auto_return_button.unset_flags (Gtk::CAN_FOCUS);
262         auto_play_button.unset_flags (Gtk::CAN_FOCUS);
263         auto_input_button.unset_flags (Gtk::CAN_FOCUS);
264         punch_out_button.unset_flags (Gtk::CAN_FOCUS);
265         punch_in_button.unset_flags (Gtk::CAN_FOCUS);
266         click_button.unset_flags (Gtk::CAN_FOCUS);
267         follow_button.unset_flags (Gtk::CAN_FOCUS);
268         
269         goto_start_button.set_events (goto_start_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
270         goto_end_button.set_events (goto_end_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
271         roll_button.set_events (roll_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
272         stop_button.set_events (stop_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
273         play_selection_button.set_events (play_selection_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
274         rec_button.set_events (rec_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
275         auto_loop_button.set_events (auto_loop_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
276         auto_return_button.set_events (auto_return_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
277         auto_play_button.set_events (auto_play_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
278         auto_input_button.set_events (auto_input_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
279         click_button.set_events (click_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
280         follow_button.set_events (click_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
281         punch_in_button.set_events (punch_in_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
282         punch_out_button.set_events (punch_out_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
283
284         goto_start_button.signal_clicked().connect (mem_fun(*this,&ARDOUR_UI::transport_goto_start));
285         goto_end_button.signal_clicked().connect (mem_fun(*this,&ARDOUR_UI::transport_goto_end));
286
287         roll_button.signal_button_release_event().connect (mem_fun(*this,&ARDOUR_UI::mouse_transport_roll));
288         play_selection_button.signal_button_release_event().connect (mem_fun(*this,&ARDOUR_UI::mouse_transport_play_selection));
289         auto_loop_button.signal_button_release_event().connect (mem_fun(*this,&ARDOUR_UI::mouse_transport_loop));
290
291         stop_button.signal_button_release_event().connect (mem_fun(*this,&ARDOUR_UI::mouse_transport_stop));
292         rec_button.signal_button_release_event().connect (mem_fun(*this,&ARDOUR_UI::mouse_transport_record));
293
294         shuttle_box.signal_button_press_event().connect (mem_fun(*this, &ARDOUR_UI::shuttle_box_button_press));
295         shuttle_box.signal_button_release_event().connect (mem_fun(*this, &ARDOUR_UI::shuttle_box_button_release));
296         shuttle_box.signal_motion_notify_event().connect (mem_fun(*this, &ARDOUR_UI::shuttle_box_motion));
297         shuttle_box.signal_expose_event().connect (mem_fun(*this, &ARDOUR_UI::shuttle_box_expose));
298
299         /* clocks, etc. */
300
301         ARDOUR_UI::Clock.connect (bind (mem_fun (primary_clock, &AudioClock::set), false));
302         ARDOUR_UI::Clock.connect (bind (mem_fun (secondary_clock, &AudioClock::set), false));
303
304         primary_clock.set_mode (AudioClock::SMPTE);
305         primary_clock.set_name ("TransportClockDisplay");
306         secondary_clock.set_mode (AudioClock::BBT);
307         secondary_clock.set_name ("TransportClockDisplay");
308
309
310         primary_clock.ValueChanged.connect (mem_fun(*this, &ARDOUR_UI::primary_clock_value_changed));
311         secondary_clock.ValueChanged.connect (mem_fun(*this, &ARDOUR_UI::secondary_clock_value_changed));
312
313         ARDOUR_UI::instance()->tooltips().set_tip (primary_clock, _("Primary clock"));
314         ARDOUR_UI::instance()->tooltips().set_tip (secondary_clock, _("secondary clock"));
315
316         /* options */
317
318         auto_return_button.signal_toggled().connect (mem_fun(*this,&ARDOUR_UI::toggle_auto_return));
319         auto_play_button.signal_toggled().connect (mem_fun(*this,&ARDOUR_UI::toggle_auto_play));
320         auto_input_button.signal_toggled().connect (mem_fun(*this,&ARDOUR_UI::toggle_auto_input));
321         click_button.signal_toggled().connect (mem_fun(*this,&ARDOUR_UI::toggle_click));
322         follow_button.signal_toggled().connect (mem_fun(*this,&ARDOUR_UI::toggle_follow));
323         punch_in_button.signal_toggled().connect (mem_fun(*this,&ARDOUR_UI::toggle_punch_in));
324         punch_out_button.signal_toggled().connect (mem_fun(*this,&ARDOUR_UI::toggle_punch_out));
325
326         preroll_button.unset_flags (Gtk::CAN_FOCUS);
327         preroll_button.set_events (preroll_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
328         preroll_button.set_name ("TransportButton");
329
330         postroll_button.unset_flags (Gtk::CAN_FOCUS);
331         postroll_button.set_events (postroll_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
332         postroll_button.set_name ("TransportButton");
333
334         preroll_clock.set_mode (AudioClock::MinSec);
335         preroll_clock.set_name ("TransportClockDisplay");
336         postroll_clock.set_mode (AudioClock::MinSec);
337         postroll_clock.set_name ("TransportClockDisplay");
338
339         /* alerts */
340
341         /* CANNOT bind these to clicked or toggled, must use pressed or released */
342
343         solo_alert_button.set_name ("TransportSoloAlert");
344         solo_alert_button.signal_pressed().connect (mem_fun(*this,&ARDOUR_UI::solo_alert_toggle));
345         auditioning_alert_button.set_name ("TransportAuditioningAlert");
346         auditioning_alert_button.signal_pressed().connect (mem_fun(*this,&ARDOUR_UI::audition_alert_toggle));
347
348         alert_box.pack_start (solo_alert_button);
349         alert_box.pack_start (auditioning_alert_button);
350
351         transport_tearoff_hbox.set_border_width (5);
352
353         transport_tearoff_hbox.pack_start (goto_start_button, false, false);
354         transport_tearoff_hbox.pack_start (goto_end_button, false, false);
355
356         Gtk::Frame* sframe = manage (new Frame);
357         Gtk::VBox*  svbox = manage (new VBox);
358         Gtk::HBox*  shbox = manage (new HBox);
359
360         sframe->set_shadow_type (Gtk::SHADOW_IN);
361         sframe->add (shuttle_box);
362
363         shuttle_box.set_name (X_("ShuttleControl"));
364
365         speed_display_box.add (speed_display_label);
366         set_size_request_to_display_given_text (speed_display_box, _("stopped"), 2, 2);
367         speed_display_box.set_name (X_("ShuttleDisplay"));
368
369         shuttle_units_button.set_name (X_("ShuttleButton"));
370         shuttle_units_button.signal_clicked().connect (mem_fun(*this, &ARDOUR_UI::shuttle_unit_clicked));
371         
372         shuttle_style_button.set_name (X_("ShuttleButton"));
373         shuttle_style_button.signal_clicked().connect (mem_fun(*this, &ARDOUR_UI::shuttle_style_clicked));
374
375         Gtk::Frame* sdframe = manage (new Frame);
376
377         sdframe->set_shadow_type (Gtk::SHADOW_IN);
378         sdframe->add (speed_display_box);
379
380         shbox->pack_start (*sdframe, false, false);
381         shbox->pack_start (shuttle_units_button, true, true);
382         shbox->pack_start (shuttle_style_button, false, false);
383         
384         svbox->pack_start (*sframe, false, false);
385         svbox->pack_start (*shbox, false, false);
386
387         transport_tearoff_hbox.pack_start (*svbox, false, false, 5);
388
389         transport_tearoff_hbox.pack_start (auto_loop_button, false, false);
390         transport_tearoff_hbox.pack_start (play_selection_button, false, false);
391         transport_tearoff_hbox.pack_start (roll_button, false, false);
392         transport_tearoff_hbox.pack_start (stop_button, false, false);
393         transport_tearoff_hbox.pack_start (rec_button, false, false, 10);
394
395         transport_tearoff_hbox.pack_start (primary_clock, false, false, 5);
396         transport_tearoff_hbox.pack_start (secondary_clock, false, false, 5);
397
398         transport_tearoff_hbox.pack_start (punch_in_button, false, false);
399         transport_tearoff_hbox.pack_start (punch_out_button, false, false);
400         transport_tearoff_hbox.pack_start (auto_input_button, false, false);
401         transport_tearoff_hbox.pack_start (auto_return_button, false, false);
402         transport_tearoff_hbox.pack_start (auto_play_button, false, false);
403         transport_tearoff_hbox.pack_start (click_button, false, false);
404         transport_tearoff_hbox.pack_start (follow_button, false, false);
405         
406         /* desensitize */
407
408         set_transport_sensitivity (false);
409
410         /* catch up with editor state */
411
412         follow_changed ();
413
414 //      transport_tearoff_hbox.pack_start (preroll_button, false, false);
415 //      transport_tearoff_hbox.pack_start (preroll_clock, false, false);
416
417 //      transport_tearoff_hbox.pack_start (postroll_button, false, false);
418 //      transport_tearoff_hbox.pack_start (postroll_clock, false, false);
419
420         transport_tearoff_hbox.pack_start (alert_box, false, false, 5);
421 }
422
423 void
424 ARDOUR_UI::setup_clock ()
425 {
426         ARDOUR_UI::Clock.connect (bind (mem_fun (big_clock, &AudioClock::set), false));
427         
428         big_clock_window = new ArdourDialog ("big clock window");
429         
430         big_clock_window->set_border_width (0);
431         big_clock_window->add  (big_clock);
432         big_clock_window->set_title (_("ardour: clock"));
433         big_clock_window->set_type_hint (Gdk::WINDOW_TYPE_HINT_MENU);
434
435         big_clock_window->signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), static_cast<Gtk::Window*>(big_clock_window)));
436
437         big_clock_window->Hiding.connect (mem_fun(*this, &ARDOUR_UI::big_clock_hiding));
438 }
439
440 void
441 ARDOUR_UI::detach_tearoff (Gtk::Box* b, Gtk::Widget* w)
442 {
443         editor->ensure_float (*transport_tearoff->tearoff_window());
444         b->remove (*w);
445 }
446
447 void
448 ARDOUR_UI::reattach_tearoff (Gtk::Box* b, Gtk::Widget* w, int32_t n)
449 {
450         b->pack_start (*w);
451         b->reorder_child (*w, n);
452 }
453
454 void
455 ARDOUR_UI::soloing_changed (bool onoff)
456 {
457         if (solo_alert_button.get_active() != onoff) {
458                 solo_alert_button.set_active (onoff);
459         }
460 }
461
462 void
463 ARDOUR_UI::_auditioning_changed (bool onoff)
464 {
465         if (auditioning_alert_button.get_active() != onoff) {
466                 auditioning_alert_button.set_active (onoff);
467                 set_transport_sensitivity (!onoff);
468         }
469 }
470
471 void
472 ARDOUR_UI::auditioning_changed (bool onoff)
473 {
474         Gtkmm2ext::UI::instance()->call_slot(bind (mem_fun(*this, &ARDOUR_UI::_auditioning_changed), onoff));
475 }
476
477 void
478 ARDOUR_UI::audition_alert_toggle ()
479 {
480         if (session) {
481                 session->cancel_audition();
482         }
483 }
484
485 void
486 ARDOUR_UI::solo_alert_toggle ()
487 {
488         if (session) {
489                 session->set_all_solo (!session->soloing());
490         }
491 }
492
493 void
494 ARDOUR_UI::solo_blink (bool onoff)
495 {
496         if (session == 0) {
497                 return;
498         }
499         
500         if (session->soloing()) {
501                 if (onoff) {
502                         solo_alert_button.set_state (Gtk::STATE_ACTIVE);
503                 } else {
504                         solo_alert_button.set_state (Gtk::STATE_NORMAL);
505                 }
506         } else {
507                 solo_alert_button.set_active (false);
508                 solo_alert_button.set_state (Gtk::STATE_NORMAL);
509         }
510 }
511
512 void
513 ARDOUR_UI::audition_blink (bool onoff)
514 {
515         if (session == 0) {
516                 return;
517         }
518         
519         if (session->is_auditioning()) {
520                 if (onoff) {
521                         auditioning_alert_button.set_state (Gtk::STATE_ACTIVE);
522                 } else {
523                         auditioning_alert_button.set_state (Gtk::STATE_NORMAL);
524                 }
525         } else {
526                 auditioning_alert_button.set_active (false);
527                 auditioning_alert_button.set_state (Gtk::STATE_NORMAL);
528         }
529 }
530
531
532 gint
533 ARDOUR_UI::shuttle_box_button_press (GdkEventButton* ev)
534 {
535         if (!session) {
536                 return TRUE;
537         }
538
539         switch (ev->button) {
540         case 1:
541                 shuttle_box.add_modal_grab ();
542                 shuttle_grabbed = true;
543                 mouse_shuttle (ev->x, true);
544                 break;
545
546         case 2:
547         case 3:
548                 return TRUE;
549                 break;
550
551         case 4:
552                 break;
553         case 5:
554                 break;
555         }
556
557         return TRUE;
558 }
559
560 gint
561 ARDOUR_UI::shuttle_box_button_release (GdkEventButton* ev)
562 {
563         if (!session) {
564                 return TRUE;
565         }
566
567         switch (ev->button) {
568         case 1:
569                 mouse_shuttle (ev->x, true);
570                 shuttle_grabbed = false;
571                 shuttle_box.remove_modal_grab ();
572                 if (shuttle_behaviour == Sprung) {
573                         shuttle_fract = SHUTTLE_FRACT_SPEED1;
574                         session->request_transport_speed (1.0);
575                         shuttle_box.queue_draw ();
576                 }
577                 return TRUE;
578
579         case 2:
580                 if (session->transport_rolling()) {
581                         shuttle_fract = SHUTTLE_FRACT_SPEED1;
582                         session->request_transport_speed (1.0);
583                 } else {
584                         shuttle_fract = 0;
585                 }
586                 shuttle_box.queue_draw ();
587                 return TRUE;
588
589         case 3:
590                 return TRUE;
591                 
592         case 4:
593                 shuttle_fract += 0.005;
594                 break;
595         case 5:
596                 shuttle_fract -= 0.005;
597                 break;
598         }
599
600         use_shuttle_fract (true);
601
602         return TRUE;
603 }
604
605 gint
606 ARDOUR_UI::shuttle_box_motion (GdkEventMotion* ev)
607 {
608         if (!session || !shuttle_grabbed) {
609                 return TRUE;
610         }
611
612         return mouse_shuttle (ev->x, false);
613 }
614
615 gint
616 ARDOUR_UI::mouse_shuttle (double x, bool force)
617 {
618         double half_width = shuttle_box.get_width() / 2.0;
619         double distance = x - half_width;
620
621         if (distance > 0) {
622                 distance = min (distance, half_width);
623         } else {
624                 distance = max (distance, -half_width);
625         }
626
627         shuttle_fract = distance / half_width;
628         use_shuttle_fract (force);
629         return TRUE;
630 }
631
632 void
633 ARDOUR_UI::use_shuttle_fract (bool force)
634 {
635         struct timeval now;
636         struct timeval diff;
637         
638         /* do not attempt to submit a motion-driven transport speed request
639            more than once per process cycle.
640          */
641
642         gettimeofday (&now, 0);
643         timersub (&now, &last_shuttle_request, &diff);
644
645         if (!force && (diff.tv_usec + (diff.tv_sec * 1000000)) < engine->usecs_per_cycle()) {
646                 return;
647         }
648         
649         last_shuttle_request = now;
650
651         bool neg = (shuttle_fract < 0.0);
652
653         double fract = 1 - sqrt (1 - (shuttle_fract * shuttle_fract)); // Formula A1
654
655         if (neg) {
656                 fract = -fract;
657         }
658
659         session->request_transport_speed (8.0 * fract); // Formula A2
660         shuttle_box.queue_draw ();
661 }
662
663 gint
664 ARDOUR_UI::shuttle_box_expose (GdkEventExpose* event)
665 {
666         gint x;
667         Glib::RefPtr<Gdk::Window> win (shuttle_box.get_window());
668
669         /* redraw the background */
670
671         win->draw_rectangle (shuttle_box.get_style()->get_bg_gc (shuttle_box.get_state()),
672                              true,
673                              event->area.x, event->area.y,
674                              event->area.width, event->area.height);
675
676
677         x = (gint) floor ((shuttle_box.get_width() / 2.0) + (0.5 * (shuttle_box.get_width() * shuttle_fract)));
678
679         /* draw line */
680
681         win->draw_line (shuttle_box.get_style()->get_fg_gc (shuttle_box.get_state()),
682                         x,
683                         0,
684                         x,
685                         shuttle_box.get_height());
686         return TRUE;
687 }
688
689 void
690 ARDOUR_UI::shuttle_style_clicked ()
691 {
692         shuttle_style_menu->popup (1, 0);
693 }
694
695 void
696 ARDOUR_UI::shuttle_unit_clicked ()
697 {
698         shuttle_unit_menu->popup (1, 0);
699 }
700
701 void
702 ARDOUR_UI::set_shuttle_units (ShuttleUnits u)
703 {
704         switch ((shuttle_units = u)) {
705         case Percentage:
706                 static_cast<Gtk::Label*>(shuttle_units_button.get_child())->set_text ("% ");
707                 break;
708         case Semitones:
709                 static_cast<Gtk::Label*>(shuttle_units_button.get_child())->set_text (_("st"));
710                 break;
711         }
712 }
713
714 void
715 ARDOUR_UI::set_shuttle_behaviour (ShuttleBehaviour b)
716 {
717         switch ((shuttle_behaviour = b)) {
718         case Sprung:
719                 static_cast<Gtk::Label*>(shuttle_style_button.get_child())->set_text (_("sprung"));
720                 shuttle_fract = 0.0;
721                 shuttle_box.queue_draw ();
722                 if (session) {
723                         if (session->transport_rolling()) {
724                                 shuttle_fract = SHUTTLE_FRACT_SPEED1;
725                                 session->request_transport_speed (1.0);
726                         }
727                 }
728                 break;
729         case Wheel:
730                 static_cast<Gtk::Label*>(shuttle_style_button.get_child())->set_text (_("wheel"));
731                 break;
732         }
733 }
734
735 void
736 ARDOUR_UI::update_speed_display ()
737 {
738         if (!session) {
739                 speed_display_label.set_text (_("stopped"));
740                 return;
741         }
742
743         char buf[32];
744         float x = session->transport_speed ();
745
746         if (x != 0) {
747                 if (shuttle_units == Percentage) {
748                         snprintf (buf, sizeof (buf), "%.4f", x);
749                 } else {
750                         if (x < 0) {
751                                 snprintf (buf, sizeof (buf), "< %.1f", 12.0 * fast_log2 (-x));
752                         } else {
753                                 snprintf (buf, sizeof (buf), "> %.1f", 12.0 * fast_log2 (x));
754                         }
755                 }
756                 speed_display_label.set_text (buf);
757         } else {
758                 speed_display_label.set_text (_("stopped"));
759         }
760 }       
761         
762 void
763 ARDOUR_UI::set_transport_sensitivity (bool yn)
764 {
765         goto_start_button.set_sensitive (yn);
766         goto_end_button.set_sensitive (yn);
767         roll_button.set_sensitive (yn);
768         stop_button.set_sensitive (yn);
769         play_selection_button.set_sensitive (yn);
770         rec_button.set_sensitive (yn);
771         auto_loop_button.set_sensitive (yn);
772         shuttle_box.set_sensitive (yn);
773 }