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