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