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