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