Remove spurious semicolon.
[ardour.git] / gtk2_ardour / audio_clock.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 <cstdio> // for sprintf
21 #include <cmath>
22
23 #include "pbd/convert.h"
24 #include "pbd/enumwriter.h"
25
26 #include <gtkmm/style.h>
27
28 #include "gtkmm2ext/cairocell.h"
29 #include "gtkmm2ext/utils.h"
30 #include "gtkmm2ext/rgb_macros.h"
31
32 #include "ardour/ardour.h"
33 #include "ardour/session.h"
34 #include "ardour/tempo.h"
35 #include "ardour/profile.h"
36 #include "ardour/slave.h"
37 #include <sigc++/bind.h>
38
39 #include "ardour_ui.h"
40 #include "audio_clock.h"
41 #include "global_signals.h"
42 #include "utils.h"
43 #include "keyboard.h"
44 #include "gui_thread.h"
45 #include "i18n.h"
46
47 using namespace ARDOUR;
48 using namespace PBD;
49 using namespace Gtk;
50 using namespace std;
51
52 using Gtkmm2ext::Keyboard;
53
54 sigc::signal<void> AudioClock::ModeChanged;
55 vector<AudioClock*> AudioClock::clocks;
56 const double AudioClock::info_font_scale_factor = 0.6;
57 const double AudioClock::separator_height = 2.0;
58 const double AudioClock::x_leading_padding = 6.0;
59
60 AudioClock::AudioClock (const string& clock_name, bool transient, const string& widget_name,
61                         bool allow_edit, bool follows_playhead, bool duration, bool with_info)
62         : _name (clock_name)
63         , is_transient (transient)
64         , is_duration (duration)
65         , editable (allow_edit)
66         , _follows_playhead (follows_playhead)
67         , _off (false)
68         , ops_menu (0)
69         , editing_attr (0)
70         , foreground_attr (0)
71         , mode_based_info_ratio (1.0)
72         , editing (false)
73         , bbt_reference_time (-1)
74         , last_when(0)
75         , last_pdelta (0)
76         , last_sdelta (0)
77         , dragging (false)
78         , drag_field (Field (0))
79
80 {
81         set_flags (CAN_FOCUS);
82
83         _layout = Pango::Layout::create (get_pango_context());
84         _layout->set_attributes (normal_attributes);
85
86         if (with_info) {
87                 _left_layout = Pango::Layout::create (get_pango_context());
88                 _right_layout = Pango::Layout::create (get_pango_context());
89         }
90
91         set_widget_name (widget_name);
92
93         _mode = BBT; /* lie to force mode switch */
94         set_mode (Timecode);
95         set (last_when, true);
96
97         if (!is_transient) {
98                 clocks.push_back (this);
99         }
100
101         ColorsChanged.connect (sigc::mem_fun (*this, &AudioClock::set_colors));
102 }
103
104 AudioClock::~AudioClock ()
105 {
106         delete foreground_attr;
107         delete editing_attr;
108 }
109
110 void
111 AudioClock::set_widget_name (const string& str)
112 {
113         if (str.empty()) {
114                 set_name ("clock");
115         } else {
116                 set_name (str + " clock");
117         }
118
119         if (is_realized()) {
120                 set_colors ();
121         }
122 }
123
124
125 void
126 AudioClock::on_realize ()
127 {
128         CairoWidget::on_realize ();
129         set_font ();
130         set_colors ();
131 }
132
133 void
134 AudioClock::set_font ()
135 {
136         Glib::RefPtr<Gtk::Style> style = get_style ();
137         Pango::FontDescription font; 
138         Pango::AttrFontDesc* font_attr;
139
140         if (!is_realized()) {
141                 font = get_font_for_style (get_name());
142         } else {
143                 font = style->get_font();
144         }
145
146         font_attr = new Pango::AttrFontDesc (Pango::Attribute::create_attr_font_desc (font));
147
148         normal_attributes.change (*font_attr);
149         editing_attributes.change (*font_attr);
150
151         /* now a smaller version of the same font */
152
153         delete font_attr;
154         font.set_size ((int) lrint (font.get_size() * info_font_scale_factor));
155         font.set_weight (Pango::WEIGHT_NORMAL);
156         font_attr = new Pango::AttrFontDesc (Pango::Attribute::create_attr_font_desc (font));
157  
158         info_attributes.change (*font_attr);
159         
160         delete font_attr;
161 }
162
163 void
164 AudioClock::set_active_state (Gtkmm2ext::ActiveState s)
165 {
166         CairoWidget::set_active_state (s);
167         set_colors ();
168 }
169
170 void
171 AudioClock::set_colors ()
172 {
173         int r, g, b, a;
174
175         uint32_t bg_color;
176         uint32_t text_color;
177         uint32_t editing_color;
178
179         if (active_state()) {
180                 bg_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1 active: background", get_name()));
181                 text_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1 active: text", get_name()));
182                 editing_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1 active: edited text", get_name()));
183         } else {
184                 bg_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: background", get_name()));
185                 text_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: text", get_name()));
186                 editing_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: edited text", get_name()));
187         }
188
189         /* store for bg in render() */
190
191         UINT_TO_RGBA (bg_color, &r, &g, &b, &a);
192         r = lrint ((r/256.0) * 65535.0);
193         g = lrint ((g/256.0) * 65535.0);
194         b = lrint ((b/256.0) * 65535.0);
195         bg_r = r/256.0;
196         bg_g = g/256.0;
197         bg_b = b/256.0;
198         bg_a = a/256.0;
199
200         UINT_TO_RGBA (text_color, &r, &g, &b, &a);
201         r = lrint ((r/256.0) * 65535.0);
202         g = lrint ((g/256.0) * 65535.0);
203         b = lrint ((b/256.0) * 65535.0);
204         foreground_attr = new Pango::AttrColor (Pango::Attribute::create_attr_foreground (r, g, b));
205
206         UINT_TO_RGBA (editing_color, &r, &g, &b, &a);
207         r = lrint ((r/256.0) * 65535.0);
208         g = lrint ((g/256.0) * 65535.0);
209         b = lrint ((b/256.0) * 65535.0);
210         editing_attr = new Pango::AttrColor (Pango::Attribute::create_attr_foreground (r, g, b));
211         
212         normal_attributes.change (*foreground_attr);
213         info_attributes.change (*foreground_attr);
214         editing_attributes.change (*foreground_attr);
215         editing_attributes.change (*editing_attr);
216
217         if (!editing) {
218                 _layout->set_attributes (normal_attributes);
219         } else {
220                 _layout->set_attributes (editing_attributes);
221         }
222
223         if (_left_layout) {
224                 _left_layout->set_attributes (info_attributes);
225                 _right_layout->set_attributes (info_attributes);
226         }
227
228         queue_draw ();
229 }
230
231 void
232 AudioClock::render (cairo_t* cr)
233 {
234         /* main layout: rounded rect, plus the text */
235         
236         if (_need_bg) {
237                 cairo_set_source_rgba (cr, bg_r, bg_g, bg_b, bg_a);
238                 Gtkmm2ext::rounded_rectangle (cr, 0, 0, get_width(), upper_height, 9);
239                 cairo_fill (cr);
240         }
241
242         cairo_move_to (cr, x_leading_padding, (upper_height - layout_height) / 2.0);
243         pango_cairo_show_layout (cr, _layout->gobj());
244
245         if (_left_layout) {
246
247                 double h = get_height() - upper_height - separator_height;
248
249                 if (_need_bg) {
250                         cairo_set_source_rgba (cr, bg_r, bg_g, bg_b, bg_a);
251                 }
252
253                 if (mode_based_info_ratio != 1.0) {
254
255                         double left_rect_width = round (((get_width() - separator_height) * mode_based_info_ratio) + 0.5);
256
257                         if (_need_bg) {
258                                 Gtkmm2ext::rounded_rectangle (cr, 0, upper_height + separator_height, left_rect_width, h, 9);
259                                 cairo_fill (cr);
260                         }
261
262                         cairo_move_to (cr, x_leading_padding, upper_height + separator_height + ((h - info_height)/2.0));
263                         pango_cairo_show_layout (cr, _left_layout->gobj());
264                         
265                         if (_need_bg) {
266                                 Gtkmm2ext::rounded_rectangle (cr, left_rect_width + separator_height, upper_height + separator_height, 
267                                                               get_width() - separator_height - left_rect_width, h, 9);
268                                 cairo_fill (cr);        
269                         }
270
271                         cairo_move_to (cr, x_leading_padding + left_rect_width + separator_height, upper_height + separator_height + ((h - info_height)/2.0));
272                         pango_cairo_show_layout (cr, _right_layout->gobj());
273
274                 } else {
275                         /* no info to display, or just one */
276
277                         if (_need_bg) {
278                                 Gtkmm2ext::rounded_rectangle (cr, 0, upper_height + separator_height, get_width(), h, 9);
279                                 cairo_fill (cr);
280                         }
281                 }
282         }
283
284         if (editing) {
285                 const double cursor_width = 12; /* need em width here, not 16 */
286
287                 if (!insert_map.empty()) {
288                         Pango::Rectangle cursor = _layout->get_cursor_strong_pos (insert_map[input_string.length()]);
289                         
290                         cairo_set_source_rgba (cr, 0.9, 0.1, 0.1, 0.8);
291                         cairo_rectangle (cr, 
292                                          x_leading_padding + cursor.get_x()/PANGO_SCALE + cursor_width,
293                                          (upper_height - layout_height)/2.0, 
294                                          2.0, cursor.get_height()/PANGO_SCALE);
295                         cairo_fill (cr);        
296                 } else {
297                         if (input_string.empty()) {
298                                 cairo_set_source_rgba (cr, 0.9, 0.1, 0.1, 0.8);
299                                 cairo_rectangle (cr, 
300                                                  (get_width()/2.0),
301                                                  (upper_height - layout_height)/2.0, 
302                                                  2.0, upper_height);
303                                 cairo_fill (cr);
304                         }
305                 }
306         }
307 }
308
309 void
310 AudioClock::on_size_allocate (Gtk::Allocation& alloc)
311 {
312         CairoWidget::on_size_allocate (alloc);
313         
314         if (_left_layout) {
315                 upper_height = (get_height()/2.0) - 1.0;
316         } else {
317                 upper_height = get_height();
318         }
319 }
320
321 void
322 AudioClock::on_size_request (Gtk::Requisition* req)
323 {
324         Glib::RefPtr<Pango::Layout> tmp;
325         Glib::RefPtr<Gtk::Style> style = get_style ();
326         Pango::FontDescription font; 
327
328         tmp = Pango::Layout::create (get_pango_context());
329
330         if (!is_realized()) {
331                 font = get_font_for_style (get_name());
332         } else {
333                 font = style->get_font();
334         }
335
336         tmp->set_font_description (font);
337
338         /* this string is the longest thing we will ever display,
339            and also includes the BBT "|" that may descends below
340            the baseline a bit, and a comma for the minsecs mode
341            where we printf a fractional value (XXX or should)
342         */
343
344         tmp->set_text (" 88|88:88:88,888"); 
345
346         tmp->get_pixel_size (req->width, req->height);
347
348         layout_height = req->height;
349         layout_width = req->width;
350
351         /* now tackle height, for which we need to know the height of the lower
352          * layout
353          */
354
355         if (_left_layout) {
356
357                 int w;
358
359                 font.set_size ((int) lrint (font.get_size() * info_font_scale_factor));
360                 font.set_weight (Pango::WEIGHT_NORMAL);
361                 tmp->set_font_description (font);
362
363                 /* we only care about height, so put as much stuff in here
364                    as possible that might change the height.
365                 */
366                 tmp->set_text ("qyhH|"); /* one ascender, one descender */
367                 
368                 tmp->get_pixel_size (w, info_height);
369                 
370                 /* silly extra padding that seems necessary to correct the info
371                  * that pango just gave us. I have no idea why.
372                  */
373
374                 info_height += 4;
375
376                 req->height += info_height;
377                 req->height += separator_height;
378         }
379 }
380
381 void
382 AudioClock::show_edit_status (int length)
383 {
384         editing_attr->set_start_index (edit_string.length() - length);
385         editing_attr->set_end_index (edit_string.length());
386         
387         editing_attributes.change (*foreground_attr);
388         editing_attributes.change (*editing_attr);
389
390         _layout->set_attributes (editing_attributes);
391 }
392
393 void
394 AudioClock::start_edit ()
395 {
396         pre_edit_string = _layout->get_text ();
397         if (!insert_map.empty()) {
398                 edit_string = pre_edit_string;
399         } else {
400                 edit_string.clear ();
401                 _layout->set_text ("");
402         }
403         input_string.clear ();
404         editing = true;
405
406         queue_draw ();
407
408         Keyboard::magic_widget_grab_focus ();
409         grab_focus ();
410 }
411
412 void
413 AudioClock::end_edit (bool modify)
414 {
415         if (modify) {
416
417                 bool ok = true;
418                 
419                 switch (_mode) {
420                 case Timecode:
421                         ok = timecode_validate_edit (edit_string);
422                         break;
423                         
424                 case BBT:
425                         ok = bbt_validate_edit (edit_string);
426                         break;
427                         
428                 case MinSec:
429                         break;
430                         
431                 case Frames:
432                         break;
433                 }
434                 
435                 if (!ok) {
436                         edit_string = pre_edit_string;
437                         input_string.clear ();
438                         _layout->set_text (edit_string);
439                         show_edit_status (0);
440                         /* edit attributes remain in use */
441                 } else {
442
443                         editing = false;
444                         framepos_t pos = 0; /* stupid gcc */
445
446                         switch (_mode) {
447                         case Timecode:
448                                 pos = frames_from_timecode_string (edit_string);
449                                 break;
450                                 
451                         case BBT:
452                                 if (is_duration) {
453                                         pos = frame_duration_from_bbt_string (0, edit_string);
454                                 } else {
455                                         pos = frames_from_bbt_string (0, edit_string);
456                                 }
457                                 break;
458                                 
459                         case MinSec:
460                                 pos = frames_from_minsec_string (edit_string);
461                                 break;
462                                 
463                         case Frames:
464                                 pos = frames_from_audioframes_string (edit_string);
465                                 break;
466                         }
467
468                         set (pos, true);
469                         _layout->set_attributes (normal_attributes);
470                         ValueChanged(); /* EMIT_SIGNAL */
471                 }
472
473         } else {
474
475                 editing = false;
476                 _layout->set_attributes (normal_attributes);
477                 _layout->set_text (pre_edit_string);
478         }
479
480         queue_draw ();
481
482         if (!editing) {
483                 drop_focus ();
484         }
485 }
486
487 void
488 AudioClock::drop_focus ()
489 {
490         /* move focus back to the default widget in the top level window */
491         
492         Keyboard::magic_widget_drop_focus ();
493         Widget* top = get_toplevel();
494         if (top->is_toplevel ()) {
495                 Window* win = dynamic_cast<Window*> (top);
496                 win->grab_focus ();
497         }
498 }
499
500 framecnt_t 
501 AudioClock::parse_as_frames_distance (const std::string& str)
502 {
503         framecnt_t f;
504
505         if (sscanf (str.c_str(), "%" PRId64, &f) == 1) {
506                 return f;
507         }
508
509         return 0;
510 }
511
512 framecnt_t 
513 AudioClock::parse_as_minsec_distance (const std::string& str)
514 {
515         framecnt_t sr = _session->frame_rate();
516         int msecs;
517         int secs;
518         int mins;
519         int hrs;
520
521         switch (str.length()) {
522         case 0:
523                 return 0;
524         case 1:
525         case 2:
526         case 3:
527         case 4:
528                 sscanf (str.c_str(), "%" PRId32, &msecs);
529                 return msecs * (sr / 1000);
530                 
531         case 5:
532                 sscanf (str.c_str(), "%1" PRId32 "%" PRId32, &secs, &msecs);
533                 return (secs * sr) + (msecs * (sr/1000));
534
535         case 6:
536                 sscanf (str.c_str(), "%2" PRId32 "%" PRId32, &secs, &msecs);
537                 return (secs * sr) + (msecs * (sr/1000));
538
539         case 7:
540                 sscanf (str.c_str(), "%1" PRId32 "%2" PRId32 "%" PRId32, &mins, &secs, &msecs);
541                 return (mins * 60 * sr) + (secs * sr) + (msecs * (sr/1000));
542
543         case 8:
544                 sscanf (str.c_str(), "%2" PRId32 "%2" PRId32 "%" PRId32, &mins, &secs, &msecs);
545                 return (mins * 60 * sr) + (secs * sr) + (msecs * (sr/1000));
546
547         case 9:
548                 sscanf (str.c_str(), "%1" PRId32 "%2" PRId32 "%2" PRId32 "%" PRId32, &hrs, &mins, &secs, &msecs);
549                 return (hrs * 3600 * sr) + (mins * 60 * sr) + (secs * sr) + (msecs * (sr/1000));
550
551         case 10:
552                 sscanf (str.c_str(), "%1" PRId32 "%2" PRId32 "%2" PRId32 "%" PRId32, &hrs, &mins, &secs, &msecs);
553                 return (hrs * 3600 * sr) + (mins * 60 * sr) + (secs * sr) + (msecs * (sr/1000));
554         
555         default:
556                 break;
557         }
558
559         return 0;
560 }
561
562 framecnt_t 
563 AudioClock::parse_as_timecode_distance (const std::string& str)
564 {
565         double fps = _session->timecode_frames_per_second();
566         framecnt_t sr = _session->frame_rate();
567         int frames;
568         int secs;
569         int mins;
570         int hrs;
571         
572         switch (str.length()) {
573         case 0:
574                 return 0;
575         case 1:
576         case 2:
577                 sscanf (str.c_str(), "%" PRId32, &frames);
578                 return lrint ((frames/(float)fps) * sr);
579
580         case 3:
581                 sscanf (str.c_str(), "%1" PRId32 "%" PRId32, &secs, &frames);
582                 return (secs * sr) + lrint ((frames/(float)fps) * sr);
583
584         case 4:
585                 sscanf (str.c_str(), "%2" PRId32 "%" PRId32, &secs, &frames);
586                 return (secs * sr) + lrint ((frames/(float)fps) * sr);
587                 
588         case 5:
589                 sscanf (str.c_str(), "%1" PRId32 "%2" PRId32 "%" PRId32, &mins, &secs, &frames);
590                 return (mins * 60 * sr) + (secs * sr) + lrint ((frames/(float)fps) * sr);
591
592         case 6:
593                 sscanf (str.c_str(), "%2" PRId32 "%2" PRId32 "%" PRId32, &mins, &secs, &frames);
594                 return (mins * 60 * sr) + (secs * sr) + lrint ((frames/(float)fps) * sr);
595
596         case 7:
597                 sscanf (str.c_str(), "%1" PRId32 "%2" PRId32 "%2" PRId32 "%" PRId32, &hrs, &mins, &secs, &frames);
598                 return (hrs * 3600 * sr) + (mins * 60 * sr) + (secs * sr) + lrint ((frames/(float)fps) * sr);
599
600         case 8:
601                 sscanf (str.c_str(), "%2" PRId32 "%2" PRId32 "%2" PRId32 "%" PRId32, &hrs, &mins, &secs, &frames);
602                 return (hrs * 3600 * sr) + (mins * 60 * sr) + (secs * sr) + lrint ((frames/(float)fps) * sr);
603         
604         default:
605                 break;
606         }
607
608         return 0;
609 }
610
611 framecnt_t 
612 AudioClock::parse_as_bbt_distance (const std::string& str)
613 {
614         return 0;
615 }
616
617 framecnt_t 
618 AudioClock::parse_as_distance (const std::string& instr)
619 {
620         string str = instr;
621
622         /* the input string is in reverse order */
623         
624         std::reverse (str.begin(), str.end());
625
626         switch (_mode) {
627         case Timecode:
628                 return parse_as_timecode_distance (str);
629                 break;
630         case Frames:
631                 return parse_as_frames_distance (str);
632                 break;
633         case BBT:
634                 return parse_as_bbt_distance (str);
635                 break;
636         case MinSec:
637                 return parse_as_minsec_distance (str);
638                 break;
639         }
640         return 0;
641 }
642
643 void
644 AudioClock::end_edit_relative (bool add)
645 {
646         framecnt_t frames = parse_as_distance (input_string);
647
648         editing = false;
649
650         editing = false;
651         _layout->set_attributes (normal_attributes);
652
653         if (frames != 0) {
654                 if (add) {
655                         set (current_time() + frames, true);
656                 } else {
657                         framepos_t c = current_time();
658
659                         if (c > frames) {
660                                 set (c - frames, true);
661                         } else {
662                                 set (0, true);
663                         }
664                 }
665                 ValueChanged (); /* EMIT SIGNAL */
666         }
667
668         input_string.clear ();
669         queue_draw ();
670         drop_focus ();
671 }
672
673 void
674 AudioClock::session_configuration_changed (std::string p)
675 {
676         if (p == "sync-source" || p == "external-sync") {
677                 set (current_time(), true);
678                 return;
679         }
680
681         if (p != "timecode-offset" && p != "timecode-offset-negative") {
682                 return;
683         }
684
685         framecnt_t current;
686
687         switch (_mode) {
688         case Timecode:
689                 if (is_duration) {
690                         current = current_duration ();
691                 } else {
692                         current = current_time ();
693                 }
694                 set (current, true);
695                 break;
696         default:
697                 break;
698         }
699 }
700
701 void
702 AudioClock::set (framepos_t when, bool force, framecnt_t offset)
703 {
704         if ((!force && !is_visible()) || _session == 0) {
705                 return;
706         }
707
708         if (is_duration) {
709                 when = when - offset;
710         } 
711
712         if (when == last_when && !force) {
713                 return;
714         }
715
716         if (!editing) {
717
718                 switch (_mode) {
719                 case Timecode:
720                         set_timecode (when, force);
721                         break;
722                         
723                 case BBT:
724                         set_bbt (when, force);
725                         break;
726                         
727                 case MinSec:
728                         set_minsec (when, force);
729                         break;
730                         
731                 case Frames:
732                         set_frames (when, force);
733                         break;
734                 }
735         }
736
737         queue_draw ();
738         last_when = when;
739 }
740
741 void
742 AudioClock::set_frames (framepos_t when, bool /*force*/)
743 {
744         char buf[32];
745         bool negative = false;
746
747         if (_off) {
748                 _layout->set_text ("\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012");
749
750                 if (_left_layout) {
751                         _left_layout->set_text ("");
752                         _right_layout->set_text ("");
753                 }
754                 
755                 return;
756         }
757         
758         if (when < 0) {
759                 when = -when;
760                 negative = true;
761         }
762
763         if (negative) {
764                 snprintf (buf, sizeof (buf), "-%10" PRId64, when);
765         } else {
766                 snprintf (buf, sizeof (buf), " %10" PRId64, when);
767         }
768
769         _layout->set_text (buf);
770
771         if (_left_layout) {
772                 framecnt_t rate = _session->frame_rate();
773
774                 if (fmod (rate, 100.0) == 0.0) {
775                         sprintf (buf, "SR %.1fkHz", rate/1000.0);
776                 } else {
777                         sprintf (buf, "SR %" PRId64, rate);
778                 }
779
780                 _left_layout->set_text (buf);
781
782                 float vid_pullup = _session->config.get_video_pullup();
783
784                 if (vid_pullup == 0.0) {
785                         _right_layout->set_text (_("pullup: \u2012"));
786                 } else {
787                         sprintf (buf, _("pullup %-6.4f"), vid_pullup);
788                         _right_layout->set_text (buf);
789                 }
790         }
791 }
792
793 void
794 AudioClock::set_minsec (framepos_t when, bool force)
795 {
796         char buf[32];
797         framecnt_t left;
798         int hrs;
799         int mins;
800         int secs;
801         int millisecs;
802         bool negative = false;
803
804         if (_off) {
805                 _layout->set_text ("\u2012\u2012:\u2012\u2012:\u2012\u2012.\u2012\u2012\u2012");
806
807                 if (_left_layout) {
808                         _left_layout->set_text ("");
809                         _right_layout->set_text ("");
810                 }
811                 
812                 return;
813         }       
814
815         if (when < 0) {
816                 when = -when;
817                 negative = true;
818         }
819
820         left = when;
821         hrs = (int) floor (left / (_session->frame_rate() * 60.0f * 60.0f));
822         left -= (framecnt_t) floor (hrs * _session->frame_rate() * 60.0f * 60.0f);
823         mins = (int) floor (left / (_session->frame_rate() * 60.0f));
824         left -= (framecnt_t) floor (mins * _session->frame_rate() * 60.0f);
825         secs = (int) floor (left / (float) _session->frame_rate());
826         left -= (framecnt_t) floor (secs * _session->frame_rate());
827         millisecs = floor (left * 1000.0 / (float) _session->frame_rate());
828         
829         if (negative) {
830                 snprintf (buf, sizeof (buf), "-%02" PRId32 ":%02" PRId32 ":%02" PRId32 ".%03" PRId32, hrs, mins, secs, millisecs);
831         } else {
832                 snprintf (buf, sizeof (buf), " %02" PRId32 ":%02" PRId32 ":%02" PRId32 ".%03" PRId32, hrs, mins, secs, millisecs);
833         }
834
835         _layout->set_text (buf);
836 }
837
838 void
839 AudioClock::set_timecode (framepos_t when, bool force)
840 {
841         char buf[32];
842         Timecode::Time TC;
843         bool negative = false;
844
845         if (_off) {
846                 _layout->set_text ("\u2012\u2012:\u2012\u2012:\u2012\u2012:\u2012\u2012");
847                 if (_left_layout) {
848                         _left_layout->set_text ("");
849                         _right_layout->set_text ("");
850                 }
851                 
852                 return;
853         }
854
855         if (when < 0) {
856                 when = -when;
857                 negative = true;
858         }
859
860         if (is_duration) {
861                 _session->timecode_duration (when, TC);
862         } else {
863                 _session->timecode_time (when, TC);
864         }
865         
866         if (TC.negative || negative) {
867                 snprintf (buf, sizeof (buf), "-%02" PRIu32 ":%02" PRIu32 ":%02" PRIu32 ":%02" PRIu32, TC.hours, TC.minutes, TC.seconds, TC.frames);
868         } else {
869                 snprintf (buf, sizeof (buf), " %02" PRIu32 ":%02" PRIu32 ":%02" PRIu32 ":%02" PRIu32, TC.hours, TC.minutes, TC.seconds, TC.frames);
870         }
871
872         _layout->set_text (buf);
873
874         if (_left_layout) {
875
876                 if (_session->config.get_external_sync()) {
877                         switch (_session->config.get_sync_source()) {
878                         case JACK:
879                                 _left_layout->set_text ("JACK");
880                                 break;
881                         case MTC:
882                                 _left_layout->set_text ("MTC");
883                                 break;
884                         case MIDIClock:
885                                 _left_layout->set_text ("M-Clock");
886                                 break;
887                         }
888                 } else {
889                         _left_layout->set_text ("INT");
890                 }
891
892                 double timecode_frames = _session->timecode_frames_per_second();
893         
894                 if (fmod(timecode_frames, 1.0) == 0.0) {
895                         sprintf (buf, "FPS %u %s", int (timecode_frames), (_session->timecode_drop_frames() ? "D" : ""));
896                 } else {
897                         sprintf (buf, "%.2f %s", timecode_frames, (_session->timecode_drop_frames() ? "D" : ""));
898                 }
899
900                 _right_layout->set_text (buf);
901         }
902 }
903
904 void
905 AudioClock::set_bbt (framepos_t when, bool force)
906 {
907         char buf[16];
908         Timecode::BBT_Time BBT;
909         bool negative = false;
910
911         if (_off) {
912                 _layout->set_text ("\u2012\u2012\u2012|\u2012\u2012|\u2012\u2012\u2012\u2012");
913                 if (_left_layout) {
914                         _left_layout->set_text ("");
915                         _right_layout->set_text ("");
916                 }
917                 return;
918         }
919
920         if (when < 0) {
921                 when = -when;
922                 negative = true;
923         }
924
925         /* handle a common case */
926         if (is_duration) {
927                 if (when == 0) {
928                         BBT.bars = 0;
929                         BBT.beats = 0;
930                         BBT.ticks = 0;
931                 } else {
932                         _session->tempo_map().bbt_time (when, BBT);
933                         BBT.bars--;
934                         BBT.beats--;
935                 }
936         } else {
937                 _session->tempo_map().bbt_time (when, BBT);
938         }
939
940         if (negative) {
941                 snprintf (buf, sizeof (buf), "-%03" PRIu32 "|%02" PRIu32 "|%04" PRIu32, BBT.bars, BBT.beats, BBT.ticks);
942         } else {
943                 snprintf (buf, sizeof (buf), " %03" PRIu32 "|%02" PRIu32 "|%04" PRIu32, BBT.bars, BBT.beats, BBT.ticks);
944         }
945
946         _layout->set_text (buf);
947                  
948         if (_right_layout) {
949                 framepos_t pos;
950
951                 if (bbt_reference_time < 0) {
952                         pos = when;
953                 } else {
954                         pos = bbt_reference_time;
955                 }
956
957                 TempoMetric m (_session->tempo_map().metric_at (pos));
958
959                 sprintf (buf, "%-5.2f", m.tempo().beats_per_minute());
960                 _left_layout->set_text (buf);
961
962                 sprintf (buf, "%g|%g", m.meter().beats_per_bar(), m.meter().note_divisor());
963                 _right_layout->set_text (buf);
964         }
965 }
966
967 void
968 AudioClock::set_session (Session *s)
969 {
970         SessionHandlePtr::set_session (s);
971
972         if (_session) {
973
974                 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), boost::bind (&AudioClock::session_configuration_changed, this, _1), gui_context());
975
976                 const XMLProperty* prop;
977                 XMLNode* node = _session->extra_xml (X_("ClockModes"));
978                 AudioClock::Mode amode;
979
980                 if (node) {
981                         for (XMLNodeList::const_iterator i = node->children().begin(); i != node->children().end(); ++i) {
982                                 if ((prop = (*i)->property (X_("name"))) && prop->value() == _name) {
983
984                                         if ((prop = (*i)->property (X_("mode"))) != 0) {
985                                                 amode = AudioClock::Mode (string_2_enum (prop->value(), amode));
986                                                 set_mode (amode);
987                                         }
988                                         if ((prop = (*i)->property (X_("on"))) != 0) {
989                                                 set_off (!string_is_affirmative (prop->value()));
990                                         }
991                                         break;
992                                 }
993                         }
994                 }
995
996                 set (last_when, true);
997         }
998 }
999
1000 bool
1001 AudioClock::on_key_press_event (GdkEventKey* ev)
1002 {
1003         if (!editing) {
1004                 return false;
1005         }
1006         
1007         /* return true for keys that we MIGHT use 
1008            at release
1009         */
1010         switch (ev->keyval) {
1011         case GDK_0:
1012         case GDK_KP_0:
1013         case GDK_1:
1014         case GDK_KP_1:
1015         case GDK_2:
1016         case GDK_KP_2:
1017         case GDK_3:
1018         case GDK_KP_3:
1019         case GDK_4:
1020         case GDK_KP_4:
1021         case GDK_5:
1022         case GDK_KP_5:
1023         case GDK_6:
1024         case GDK_KP_6:
1025         case GDK_7:
1026         case GDK_KP_7:
1027         case GDK_8:
1028         case GDK_KP_8:
1029         case GDK_9:
1030         case GDK_KP_9:
1031         case GDK_period:
1032         case GDK_comma:
1033         case GDK_KP_Decimal:
1034         case GDK_Tab:
1035         case GDK_Return:
1036         case GDK_KP_Enter:
1037         case GDK_Escape:
1038                 return true;
1039         default:
1040                 return false;
1041         }
1042 }
1043
1044 bool
1045 AudioClock::on_key_release_event (GdkEventKey *ev)
1046 {
1047         if (!editing) {
1048                 return false;
1049         }
1050
1051         string new_text;
1052         char new_char = 0;
1053
1054         switch (ev->keyval) {
1055         case GDK_0:
1056         case GDK_KP_0:
1057                 new_char = '0';
1058                 break;
1059         case GDK_1:
1060         case GDK_KP_1:
1061                 new_char = '1';
1062                 break;
1063         case GDK_2:
1064         case GDK_KP_2:
1065                 new_char = '2';
1066                 break;
1067         case GDK_3:
1068         case GDK_KP_3:
1069                 new_char = '3';
1070                 break;
1071         case GDK_4:
1072         case GDK_KP_4:
1073                 new_char = '4';
1074                 break;
1075         case GDK_5:
1076         case GDK_KP_5:
1077                 new_char = '5';
1078                 break;
1079         case GDK_6:
1080         case GDK_KP_6:
1081                 new_char = '6';
1082                 break;
1083         case GDK_7:
1084         case GDK_KP_7:
1085                 new_char = '7';
1086                 break;
1087         case GDK_8:
1088         case GDK_KP_8:
1089                 new_char = '8';
1090                 break;
1091         case GDK_9:
1092         case GDK_KP_9:
1093                 new_char = '9';
1094                 break;
1095
1096         case GDK_minus:
1097         case GDK_KP_Subtract:
1098                 end_edit_relative (false);
1099                 return true;
1100                 break;
1101
1102         case GDK_plus:
1103         case GDK_KP_Add:
1104                 end_edit_relative (true);
1105                 return true;
1106                 break;
1107
1108         case GDK_Tab:
1109         case GDK_Return:
1110         case GDK_KP_Enter:
1111                 end_edit (true);
1112                 return true;
1113                 break;
1114
1115         case GDK_Escape:
1116                 end_edit (false);
1117                 ChangeAborted();  /*  EMIT SIGNAL  */
1118                 return true;
1119
1120         case GDK_Delete:
1121         case GDK_BackSpace:
1122                 input_string = input_string.substr (1, input_string.length() - 1);
1123                 goto use_input_string;
1124
1125         default:
1126                 return false;
1127         }
1128
1129         if (!insert_map.empty() && (input_string.length() >= insert_map.size())) {
1130                 /* eat the key event, but do no nothing with it */
1131                 return true;
1132         }
1133
1134         input_string.insert (input_string.begin(), new_char);
1135
1136   use_input_string:
1137
1138         string::reverse_iterator ri;
1139         vector<int> insert_at;
1140         int highlight_length = 0;
1141         char buf[32];
1142         framepos_t pos;
1143
1144         /* merge with pre-edit-string into edit string */
1145         
1146         switch (_mode) {
1147         case Frames:
1148                 /* get this one in the right order, and to the right width */
1149                 if (ev->keyval != GDK_Delete && ev->keyval != GDK_BackSpace) {
1150                         edit_string.push_back (new_char);
1151                 } else {
1152                         edit_string = edit_string.substr (0, edit_string.length() - 1);
1153                 }
1154                 if (!edit_string.empty()) {
1155                         sscanf (edit_string.c_str(), "%" PRId64, &pos);
1156                         snprintf (buf, sizeof (buf), " %10" PRId64, pos);
1157                         edit_string = buf;
1158                 }
1159                 highlight_length = edit_string.length();
1160                 break;
1161                 
1162         default:
1163                 edit_string = pre_edit_string;
1164                 
1165                 /* backup through the original string, till we have
1166                  * enough digits locations to put all the digits from
1167                  * the input string.
1168                  */
1169                 
1170                 for (ri = edit_string.rbegin(); ri != edit_string.rend(); ++ri) {
1171                         if (isdigit (*ri)) {
1172                                 insert_at.push_back (edit_string.length() - (ri - edit_string.rbegin()) - 1);
1173                                 if (insert_at.size() == input_string.length()) {
1174                                         break;
1175                                 }
1176                         }
1177                 }
1178                 
1179                 if (insert_at.size() != input_string.length()) {
1180                         error << "something went wrong " << endmsg;
1181                 } else {
1182                         for (int i = input_string.length() - 1; i >= 0; --i) {
1183                                 edit_string[insert_at[i]] = input_string[i];
1184                         }
1185                         
1186                         highlight_length = edit_string.length() - insert_at.back();
1187                 }
1188                 
1189                 break;
1190         }
1191         
1192         if (edit_string != _layout->get_text()) {
1193                 show_edit_status (highlight_length);
1194                 _layout->set_text (edit_string);
1195                 queue_draw ();
1196         } 
1197
1198         return true;
1199 }
1200
1201 AudioClock::Field
1202 AudioClock::index_to_field (int index) const
1203 {
1204         switch (_mode) {
1205         case Timecode:
1206                 if (index < 4) {
1207                         return Timecode_Hours;
1208                 } else if (index < 7) {
1209                         return Timecode_Minutes;
1210                 } else if (index < 10) {
1211                         return Timecode_Seconds;
1212                 } else if (index < 13) {
1213                         return Timecode_Frames;
1214                 }
1215                 break;
1216         case BBT:
1217                 if (index < 5) {
1218                         return Bars;
1219                 } else if (index < 7) {
1220                         return Beats;
1221                 } else if (index < 12) {
1222                         return Ticks;
1223                 }
1224                 break;
1225         case MinSec:
1226                 if (index < 3) {
1227                         return Timecode_Hours;
1228                 } else if (index < 6) {
1229                         return MS_Minutes;
1230                 } else if (index < 9) {
1231                         return MS_Seconds;
1232                 } else if (index < 12) {
1233                         return MS_Milliseconds;
1234                 }
1235                 break;
1236         case Frames:
1237                 return AudioFrames;
1238                 break;
1239         }
1240
1241         return Field (0);
1242 }
1243
1244 bool
1245 AudioClock::on_button_press_event (GdkEventButton *ev)
1246 {
1247         switch (ev->button) {
1248         case 1:
1249                 if (editable && !_off) {
1250                         dragging = true;
1251                         /* make absolutely sure that the pointer is grabbed */
1252                         gdk_pointer_grab(ev->window,false ,
1253                                          GdkEventMask( Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK |Gdk::BUTTON_RELEASE_MASK),
1254                                          NULL,NULL,ev->time);
1255                         drag_accum = 0;
1256                         drag_start_y = ev->y;
1257                         drag_y = ev->y;
1258                         
1259                         int index;
1260                         int trailing;
1261                         int y;
1262                         int x;
1263
1264                         /* the text has been centered vertically, so adjust
1265                          * x and y. 
1266                          */
1267
1268                         y = ev->y - ((upper_height - layout_height)/2);
1269                         x = ev->x - x_leading_padding;
1270                         
1271                         if (_layout->xy_to_index (x * PANGO_SCALE, y * PANGO_SCALE, index, trailing)) {                 
1272                                 drag_field = index_to_field (index);
1273                         } else {
1274                                 drag_field = Field (0);
1275                         }
1276                 }
1277                 break;
1278                 
1279         default:
1280                 return false;
1281                 break;
1282         }
1283
1284         return true;
1285 }
1286
1287 bool
1288 AudioClock::on_button_release_event (GdkEventButton *ev)
1289 {
1290         if (editable && !_off) {
1291                 if (dragging) {
1292                         gdk_pointer_ungrab (GDK_CURRENT_TIME);
1293                         dragging = false;
1294                         if (ev->y > drag_start_y+1 || ev->y < drag_start_y-1 || Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)){
1295                                 // we actually dragged so return without
1296                                 // setting editing focus, or we shift clicked
1297                                 return true;
1298                         } else {
1299                                 if (ev->button == 1) {
1300                                         start_edit ();
1301                                 }
1302                         }
1303
1304                 }
1305         }
1306
1307         if (Keyboard::is_context_menu_event (ev)) {
1308                 if (ops_menu == 0) {
1309                         build_ops_menu ();
1310                 }
1311                 ops_menu->popup (1, ev->time);
1312                 return true;
1313         }
1314
1315         return false;
1316 }
1317
1318 bool
1319 AudioClock::on_focus_out_event (GdkEventFocus* ev)
1320 {
1321         bool ret = CairoWidget::on_focus_out_event (ev);
1322
1323         if (editing) {
1324                 end_edit (false);
1325         }
1326
1327         return ret;
1328 }
1329
1330 bool
1331 AudioClock::on_scroll_event (GdkEventScroll *ev)
1332 {
1333         int index;
1334         int trailing;
1335
1336         if (editing || _session == 0 || !editable || _off) {
1337                 return false;
1338         }
1339
1340         int y;
1341         int x;
1342         
1343         /* the text has been centered vertically, so adjust
1344          * x and y. 
1345          */
1346
1347         y = ev->y - ((upper_height - layout_height)/2);
1348         x = ev->x - x_leading_padding;
1349
1350         if (!_layout->xy_to_index (x * PANGO_SCALE, y * PANGO_SCALE, index, trailing)) {
1351                 /* not in the main layout */
1352                 return false;
1353         }
1354         
1355         Field f = index_to_field (index);
1356         framepos_t frames = 0;
1357
1358         switch (ev->direction) {
1359
1360         case GDK_SCROLL_UP:
1361                 frames = get_frame_step (f);
1362                 if (frames != 0) {
1363                         if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1364                                 frames *= 10;
1365                         }
1366                         set (current_time() + frames, true);
1367                         ValueChanged (); /* EMIT_SIGNAL */
1368                 }
1369                 break;
1370                 
1371         case GDK_SCROLL_DOWN:
1372                 frames = get_frame_step (f);
1373                 if (frames != 0) {
1374                         if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1375                                 frames *= 10;
1376                         }
1377                         
1378                         if ((double)current_time() - (double)frames < 0.0) {
1379                                 set (0, true);
1380                         } else {
1381                                 set (current_time() - frames, true);
1382                         }
1383                         
1384                         ValueChanged (); /* EMIT_SIGNAL */
1385                 }
1386                 break;
1387                 
1388         default:
1389                 return false;
1390                 break;
1391         }
1392         
1393         return true;
1394 }
1395
1396 bool
1397 AudioClock::on_motion_notify_event (GdkEventMotion *ev)
1398 {
1399         if (editing || _session == 0 || !dragging) {
1400                 return false;
1401         }
1402
1403         float pixel_frame_scale_factor = 0.2f;
1404
1405         if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier))  {
1406                 pixel_frame_scale_factor = 0.1f;
1407         }
1408
1409
1410         if (Keyboard::modifier_state_contains (ev->state,
1411                                                Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) {
1412
1413                 pixel_frame_scale_factor = 0.025f;
1414         }
1415
1416         double y_delta = ev->y - drag_y;
1417
1418         drag_accum +=  y_delta*pixel_frame_scale_factor;
1419
1420         drag_y = ev->y;
1421
1422         if (trunc (drag_accum) != 0) {
1423
1424                 framepos_t frames;
1425                 framepos_t pos;
1426                 int dir;
1427                 dir = (drag_accum < 0 ? 1:-1);
1428                 pos = current_time();
1429                 frames = get_frame_step (drag_field,pos,dir);
1430
1431                 if (frames  != 0 &&  frames * drag_accum < current_time()) {
1432                         set ((framepos_t) floor (pos - drag_accum * frames), false); // minus because up is negative in GTK
1433                 } else {
1434                         set (0 , false);
1435                 }
1436
1437                 drag_accum= 0;
1438                 ValueChanged();  /* EMIT_SIGNAL */
1439         }
1440
1441         return true;
1442 }
1443
1444 framepos_t
1445 AudioClock::get_frame_step (Field field, framepos_t pos, int dir)
1446 {
1447         framecnt_t f = 0;
1448         Timecode::BBT_Time BBT;
1449         switch (field) {
1450         case Timecode_Hours:
1451                 f = (framecnt_t) floor (3600.0 * _session->frame_rate());
1452                 break;
1453         case Timecode_Minutes:
1454                 f = (framecnt_t) floor (60.0 * _session->frame_rate());
1455                 break;
1456         case Timecode_Seconds:
1457                 f = _session->frame_rate();
1458                 break;
1459         case Timecode_Frames:
1460                 f = (framecnt_t) floor (_session->frame_rate() / _session->timecode_frames_per_second());
1461                 break;
1462
1463         case AudioFrames:
1464                 f = 1;
1465                 break;
1466
1467         case MS_Hours:
1468                 f = (framecnt_t) floor (3600.0 * _session->frame_rate());
1469                 break;
1470         case MS_Minutes:
1471                 f = (framecnt_t) floor (60.0 * _session->frame_rate());
1472                 break;
1473         case MS_Seconds:
1474                 f = (framecnt_t) _session->frame_rate();
1475                 break;
1476         case MS_Milliseconds:
1477                 f = (framecnt_t) floor (_session->frame_rate() / 1000.0);
1478                 break;
1479
1480         case Bars:
1481                 BBT.bars = 1;
1482                 BBT.beats = 0;
1483                 BBT.ticks = 0;
1484                 f = _session->tempo_map().bbt_duration_at (pos,BBT,dir);
1485                 break;
1486         case Beats:
1487                 BBT.bars = 0;
1488                 BBT.beats = 1;
1489                 BBT.ticks = 0;
1490                 f = _session->tempo_map().bbt_duration_at(pos,BBT,dir);
1491                 break;
1492         case Ticks:
1493                 BBT.bars = 0;
1494                 BBT.beats = 0;
1495                 BBT.ticks = 1;
1496                 f = _session->tempo_map().bbt_duration_at(pos,BBT,dir);
1497                 break;
1498         default:
1499                 error << string_compose (_("programming error: %1"), "attempt to get frames from non-text field!") << endmsg;
1500                 f = 0;
1501                 break;
1502         }
1503
1504         return f;
1505 }
1506
1507 framepos_t
1508 AudioClock::current_time (framepos_t pos) const
1509 {
1510         return last_when;
1511 }
1512
1513 framepos_t
1514 AudioClock::current_duration (framepos_t pos) const
1515 {
1516         framepos_t ret = 0;
1517
1518         switch (_mode) {
1519         case Timecode:
1520                 ret = last_when;
1521                 break;
1522         case BBT:
1523                 ret = frame_duration_from_bbt_string (pos, _layout->get_text());
1524                 break;
1525
1526         case MinSec:
1527                 ret = last_when;
1528                 break;
1529
1530         case Frames:
1531                 ret = last_when;
1532                 break;
1533         }
1534
1535         return ret;
1536 }
1537
1538 bool
1539 AudioClock::bbt_validate_edit (const string& str)
1540 {
1541         AnyTime any;
1542
1543         sscanf (str.c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, &any.bbt.bars, &any.bbt.beats, &any.bbt.ticks);
1544         
1545         if (!is_duration && any.bbt.bars == 0) {
1546                 return false;
1547         }
1548
1549         if (!is_duration && any.bbt.beats == 0) {
1550                 return false;
1551         }
1552
1553         return true;
1554 }
1555
1556 bool
1557 AudioClock::timecode_validate_edit (const string& str)
1558 {
1559         Timecode::Time TC;
1560
1561         if (sscanf (_layout->get_text().c_str(), "%" PRId32 ":%" PRId32 ":%" PRId32 ":%" PRId32, 
1562                     &TC.hours, &TC.minutes, &TC.seconds, &TC.frames) != 4) {
1563                 return false;
1564         }
1565
1566         if (TC.minutes > 59 || TC.seconds > 59) {
1567                 return false;
1568         }
1569
1570         if (TC.frames > (long)rint(_session->timecode_frames_per_second()) - 1) {
1571                 return false;
1572         }
1573
1574         if (_session->timecode_drop_frames()) {
1575                 if (TC.minutes % 10 && TC.seconds == 0 && TC.frames < 2) {
1576                         return false;
1577                 }
1578         }
1579
1580         return true;
1581 }
1582
1583 framepos_t
1584 AudioClock::frames_from_timecode_string (const string& str) const
1585 {
1586         if (_session == 0) {
1587                 return 0;
1588         }
1589
1590         Timecode::Time TC;
1591         framepos_t sample;
1592
1593         if (sscanf (str.c_str(), "%d:%d:%d:%d", &TC.hours, &TC.minutes, &TC.seconds, &TC.frames) != 4) {
1594                 error << string_compose (_("programming error: %1 %2"), "badly formatted timecode clock string", str) << endmsg;
1595                 return 0;
1596         }
1597
1598         TC.rate = _session->timecode_frames_per_second();
1599         TC.drop= _session->timecode_drop_frames();
1600
1601         _session->timecode_to_sample (TC, sample, false /* use_offset */, false /* use_subframes */ );
1602         
1603         // timecode_tester ();
1604
1605         return sample;
1606 }
1607
1608 framepos_t
1609 AudioClock::frames_from_minsec_string (const string& str) const
1610 {
1611         if (_session == 0) {
1612                 return 0;
1613         }
1614
1615         int hrs, mins, secs, millisecs;
1616         framecnt_t sr = _session->frame_rate();
1617
1618         if (sscanf (str.c_str(), "%d:%d:%d.%d", &hrs, &mins, &secs, &millisecs) != 4) {
1619                 error << string_compose (_("programming error: %1 %2"), "badly formatted minsec clock string", str) << endmsg;
1620                 return 0;
1621         }
1622
1623         return (framepos_t) floor ((hrs * 60.0f * 60.0f * sr) + (mins * 60.0f * sr) + (secs * sr) + (millisecs * sr / 1000.0));
1624 }
1625
1626 framepos_t
1627 AudioClock::frames_from_bbt_string (framepos_t pos, const string& str) const
1628 {
1629         if (_session == 0) {
1630                 error << "AudioClock::current_time() called with BBT mode but without session!" << endmsg;
1631                 return 0;
1632         }
1633
1634         AnyTime any;
1635         any.type = AnyTime::BBT;
1636
1637         sscanf (str.c_str(), "%" PRId32 "|%" PRId32 "|%" PRId32, &any.bbt.bars, &any.bbt.beats, &any.bbt.ticks);
1638         
1639         if (is_duration) {
1640                 any.bbt.bars++;
1641                 any.bbt.beats++;
1642                 return _session->any_duration_to_frames (pos, any);
1643         } else {
1644                 return _session->convert_to_frames (any);
1645         }
1646 }
1647
1648
1649 framepos_t
1650 AudioClock::frame_duration_from_bbt_string (framepos_t pos, const string& str) const
1651 {
1652         if (_session == 0) {
1653                 error << "AudioClock::current_time() called with BBT mode but without session!" << endmsg;
1654                 return 0;
1655         }
1656
1657         Timecode::BBT_Time bbt;
1658
1659         sscanf (str.c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, &bbt.bars, &bbt.beats, &bbt.ticks);
1660
1661         return _session->tempo_map().bbt_duration_at(pos,bbt,1);
1662 }
1663
1664 framepos_t
1665 AudioClock::frames_from_audioframes_string (const string& str) const
1666 {
1667         framepos_t f;
1668         sscanf (str.c_str(), "%" PRId64, &f);
1669         return f;
1670 }
1671
1672 void
1673 AudioClock::build_ops_menu ()
1674 {
1675         using namespace Menu_Helpers;
1676         ops_menu = new Menu;
1677         MenuList& ops_items = ops_menu->items();
1678         ops_menu->set_name ("ArdourContextMenu");
1679
1680         if (!Profile->get_sae()) {
1681                 ops_items.push_back (MenuElem (_("Timecode"), sigc::bind (sigc::mem_fun(*this, &AudioClock::set_mode), Timecode)));
1682         }
1683         ops_items.push_back (MenuElem (_("Bars:Beats"), sigc::bind (sigc::mem_fun(*this, &AudioClock::set_mode), BBT)));
1684         ops_items.push_back (MenuElem (_("Minutes:Seconds"), sigc::bind (sigc::mem_fun(*this, &AudioClock::set_mode), MinSec)));
1685         ops_items.push_back (MenuElem (_("Samples"), sigc::bind (sigc::mem_fun(*this, &AudioClock::set_mode), Frames)));
1686
1687         if (editable && !_off && !is_duration && !_follows_playhead) {
1688                 ops_items.push_back (SeparatorElem());
1689                 ops_items.push_back (MenuElem (_("Set From Playhead"), sigc::mem_fun(*this, &AudioClock::set_from_playhead)));
1690                 ops_items.push_back (MenuElem (_("Locate to This Time"), sigc::mem_fun(*this, &AudioClock::locate)));
1691         }
1692 }
1693
1694 void
1695 AudioClock::set_from_playhead ()
1696 {
1697         if (!_session) {
1698                 return;
1699         }
1700
1701         set (_session->transport_frame());
1702         ValueChanged ();
1703 }
1704
1705 void
1706 AudioClock::locate ()
1707 {
1708         if (!_session || is_duration) {
1709                 return;
1710         }
1711
1712         _session->request_locate (current_time(), _session->transport_rolling ());
1713 }
1714
1715 void
1716 AudioClock::set_mode (Mode m)
1717 {
1718         if (_mode == m) {
1719                 return;
1720         }
1721
1722         _mode = m;
1723
1724         insert_map.clear();
1725
1726         _layout->set_text ("");
1727
1728         if (_left_layout) {
1729                 _left_layout->set_text ("");
1730                 _right_layout->set_text ("");
1731         }
1732
1733         switch (_mode) {
1734         case Timecode:
1735                 mode_based_info_ratio = 0.5;
1736                 insert_map.push_back (11);
1737                 insert_map.push_back (10);
1738                 insert_map.push_back (8);
1739                 insert_map.push_back (7);
1740                 insert_map.push_back (5);
1741                 insert_map.push_back (4);
1742                 insert_map.push_back (2);
1743                 insert_map.push_back (1);
1744                 break;
1745                 
1746         case BBT:
1747                 mode_based_info_ratio = 0.5;
1748                 insert_map.push_back (11);
1749                 insert_map.push_back (10);
1750                 insert_map.push_back (9);
1751                 insert_map.push_back (8);
1752                 insert_map.push_back (6);
1753                 insert_map.push_back (5);       
1754                 insert_map.push_back (3);       
1755                 insert_map.push_back (2);       
1756                 insert_map.push_back (1);       
1757                 break;
1758                 
1759         case MinSec:
1760                 mode_based_info_ratio = 1.0;
1761                 insert_map.push_back (12);
1762                 insert_map.push_back (11);
1763                 insert_map.push_back (10);
1764                 insert_map.push_back (8);
1765                 insert_map.push_back (7);
1766                 insert_map.push_back (5);
1767                 insert_map.push_back (4);
1768                 insert_map.push_back (2);       
1769                 insert_map.push_back (1);       
1770                 break;
1771                 
1772         case Frames:
1773                 mode_based_info_ratio = 0.5;
1774                 break;
1775         }
1776
1777         set (last_when, true);
1778
1779         if (!is_transient) {
1780                 ModeChanged (); /* EMIT SIGNAL (the static one)*/
1781         }
1782
1783         mode_changed (); /* EMIT SIGNAL (the member one) */
1784 }
1785
1786 void
1787 AudioClock::set_bbt_reference (framepos_t pos)
1788 {
1789         bbt_reference_time = pos;
1790 }
1791
1792 void
1793 AudioClock::on_style_changed (const Glib::RefPtr<Gtk::Style>& old_style)
1794 {
1795         CairoWidget::on_style_changed (old_style);
1796         set_font ();
1797         set_colors ();
1798 }
1799
1800 void
1801 AudioClock::set_editable (bool yn)
1802 {
1803         editable = yn;
1804 }
1805
1806 void
1807 AudioClock::set_is_duration (bool yn)
1808 {
1809         if (yn == is_duration) {
1810                 return;
1811         }
1812
1813         is_duration = yn;
1814         set (last_when, true);
1815 }
1816
1817 void
1818 AudioClock::set_off (bool yn) 
1819 {
1820         if (_off == yn) {
1821                 return;
1822         }
1823
1824         _off = yn;
1825
1826         /* force a redraw. last_when will be preserved, but the clock text will
1827          * change 
1828          */
1829         
1830         set (last_when, true);
1831 }
1832
1833 void
1834 AudioClock::focus ()
1835 {
1836         start_edit ();
1837 }
1838
1839