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