3dbdfa859811d0c4776d686b19387bc4db6783ee
[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 (" 88888888888,|"); 
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         Keyboard::magic_widget_drop_focus ();
491
492         if (has_focus()) {
493
494                 /* move focus back to the default widget in the top level window */
495                 
496                 Widget* top = get_toplevel();
497                 
498                 if (top->is_toplevel ()) {
499                         Window* win = dynamic_cast<Window*> (top);
500                         win->grab_focus ();
501                 }
502         }
503 }
504
505 framecnt_t 
506 AudioClock::parse_as_frames_distance (const std::string& str)
507 {
508         framecnt_t f;
509
510         if (sscanf (str.c_str(), "%" PRId64, &f) == 1) {
511                 return f;
512         }
513
514         return 0;
515 }
516
517 framecnt_t 
518 AudioClock::parse_as_minsec_distance (const std::string& str)
519 {
520         framecnt_t sr = _session->frame_rate();
521         int msecs;
522         int secs;
523         int mins;
524         int hrs;
525
526         switch (str.length()) {
527         case 0:
528                 return 0;
529         case 1:
530         case 2:
531         case 3:
532         case 4:
533                 sscanf (str.c_str(), "%" PRId32, &msecs);
534                 return msecs * (sr / 1000);
535                 
536         case 5:
537                 sscanf (str.c_str(), "%1" PRId32 "%" PRId32, &secs, &msecs);
538                 return (secs * sr) + (msecs * (sr/1000));
539
540         case 6:
541                 sscanf (str.c_str(), "%2" PRId32 "%" PRId32, &secs, &msecs);
542                 return (secs * sr) + (msecs * (sr/1000));
543
544         case 7:
545                 sscanf (str.c_str(), "%1" PRId32 "%2" PRId32 "%" PRId32, &mins, &secs, &msecs);
546                 return (mins * 60 * sr) + (secs * sr) + (msecs * (sr/1000));
547
548         case 8:
549                 sscanf (str.c_str(), "%2" PRId32 "%2" PRId32 "%" PRId32, &mins, &secs, &msecs);
550                 return (mins * 60 * sr) + (secs * sr) + (msecs * (sr/1000));
551
552         case 9:
553                 sscanf (str.c_str(), "%1" PRId32 "%2" PRId32 "%2" PRId32 "%" PRId32, &hrs, &mins, &secs, &msecs);
554                 return (hrs * 3600 * sr) + (mins * 60 * sr) + (secs * sr) + (msecs * (sr/1000));
555
556         case 10:
557                 sscanf (str.c_str(), "%1" PRId32 "%2" PRId32 "%2" PRId32 "%" PRId32, &hrs, &mins, &secs, &msecs);
558                 return (hrs * 3600 * sr) + (mins * 60 * sr) + (secs * sr) + (msecs * (sr/1000));
559         
560         default:
561                 break;
562         }
563
564         return 0;
565 }
566
567 framecnt_t 
568 AudioClock::parse_as_timecode_distance (const std::string& str)
569 {
570         double fps = _session->timecode_frames_per_second();
571         framecnt_t sr = _session->frame_rate();
572         int frames;
573         int secs;
574         int mins;
575         int hrs;
576         
577         switch (str.length()) {
578         case 0:
579                 return 0;
580         case 1:
581         case 2:
582                 sscanf (str.c_str(), "%" PRId32, &frames);
583                 return lrint ((frames/(float)fps) * sr);
584
585         case 3:
586                 sscanf (str.c_str(), "%1" PRId32 "%" PRId32, &secs, &frames);
587                 return (secs * sr) + lrint ((frames/(float)fps) * sr);
588
589         case 4:
590                 sscanf (str.c_str(), "%2" PRId32 "%" PRId32, &secs, &frames);
591                 return (secs * sr) + lrint ((frames/(float)fps) * sr);
592                 
593         case 5:
594                 sscanf (str.c_str(), "%1" PRId32 "%2" PRId32 "%" PRId32, &mins, &secs, &frames);
595                 return (mins * 60 * sr) + (secs * sr) + lrint ((frames/(float)fps) * sr);
596
597         case 6:
598                 sscanf (str.c_str(), "%2" PRId32 "%2" PRId32 "%" PRId32, &mins, &secs, &frames);
599                 return (mins * 60 * sr) + (secs * sr) + lrint ((frames/(float)fps) * sr);
600
601         case 7:
602                 sscanf (str.c_str(), "%1" PRId32 "%2" PRId32 "%2" PRId32 "%" PRId32, &hrs, &mins, &secs, &frames);
603                 return (hrs * 3600 * sr) + (mins * 60 * sr) + (secs * sr) + lrint ((frames/(float)fps) * sr);
604
605         case 8:
606                 sscanf (str.c_str(), "%2" PRId32 "%2" PRId32 "%2" PRId32 "%" PRId32, &hrs, &mins, &secs, &frames);
607                 return (hrs * 3600 * sr) + (mins * 60 * sr) + (secs * sr) + lrint ((frames/(float)fps) * sr);
608         
609         default:
610                 break;
611         }
612
613         return 0;
614 }
615
616 framecnt_t 
617 AudioClock::parse_as_bbt_distance (const std::string& str)
618 {
619         return 0;
620 }
621
622 framecnt_t 
623 AudioClock::parse_as_distance (const std::string& instr)
624 {
625         string str = instr;
626
627         /* the input string is in reverse order */
628         
629         std::reverse (str.begin(), str.end());
630
631         switch (_mode) {
632         case Timecode:
633                 return parse_as_timecode_distance (str);
634                 break;
635         case Frames:
636                 return parse_as_frames_distance (str);
637                 break;
638         case BBT:
639                 return parse_as_bbt_distance (str);
640                 break;
641         case MinSec:
642                 return parse_as_minsec_distance (str);
643                 break;
644         }
645         return 0;
646 }
647
648 void
649 AudioClock::end_edit_relative (bool add)
650 {
651         framecnt_t frames = parse_as_distance (input_string);
652
653         editing = false;
654
655         editing = false;
656         _layout->set_attributes (normal_attributes);
657
658         if (frames != 0) {
659                 if (add) {
660                         set (current_time() + frames, true);
661                 } else {
662                         framepos_t c = current_time();
663
664                         if (c > frames) {
665                                 set (c - frames, true);
666                         } else {
667                                 set (0, true);
668                         }
669                 }
670                 ValueChanged (); /* EMIT SIGNAL */
671         }
672
673         input_string.clear ();
674         queue_draw ();
675         drop_focus ();
676 }
677
678 void
679 AudioClock::session_configuration_changed (std::string p)
680 {
681         if (p == "sync-source" || p == "external-sync") {
682                 set (current_time(), true);
683                 return;
684         }
685
686         if (p != "timecode-offset" && p != "timecode-offset-negative") {
687                 return;
688         }
689
690         framecnt_t current;
691
692         switch (_mode) {
693         case Timecode:
694                 if (is_duration) {
695                         current = current_duration ();
696                 } else {
697                         current = current_time ();
698                 }
699                 set (current, true);
700                 break;
701         default:
702                 break;
703         }
704 }
705
706 void
707 AudioClock::set (framepos_t when, bool force, framecnt_t offset)
708 {
709         if ((!force && !is_visible()) || _session == 0) {
710                 return;
711         }
712
713         if (is_duration) {
714                 when = when - offset;
715         } 
716
717         if (when == last_when && !force) {
718                 return;
719         }
720
721         if (!editing) {
722
723                 switch (_mode) {
724                 case Timecode:
725                         set_timecode (when, force);
726                         break;
727                         
728                 case BBT:
729                         set_bbt (when, force);
730                         break;
731                         
732                 case MinSec:
733                         set_minsec (when, force);
734                         break;
735                         
736                 case Frames:
737                         set_frames (when, force);
738                         break;
739                 }
740         }
741
742         queue_draw ();
743         last_when = when;
744 }
745
746 void
747 AudioClock::set_frames (framepos_t when, bool /*force*/)
748 {
749         char buf[32];
750         bool negative = false;
751
752         if (_off) {
753                 _layout->set_text ("\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012");
754
755                 if (_left_layout) {
756                         _left_layout->set_text ("");
757                         _right_layout->set_text ("");
758                 }
759                 
760                 return;
761         }
762         
763         if (when < 0) {
764                 when = -when;
765                 negative = true;
766         }
767
768         if (negative) {
769                 snprintf (buf, sizeof (buf), "-%10" PRId64, when);
770         } else {
771                 snprintf (buf, sizeof (buf), " %10" PRId64, when);
772         }
773
774         _layout->set_text (buf);
775
776         if (_left_layout) {
777                 framecnt_t rate = _session->frame_rate();
778
779                 if (fmod (rate, 100.0) == 0.0) {
780                         sprintf (buf, "SR %.1fkHz", rate/1000.0);
781                 } else {
782                         sprintf (buf, "SR %" PRId64, rate);
783                 }
784
785                 _left_layout->set_text (buf);
786
787                 float vid_pullup = _session->config.get_video_pullup();
788
789                 if (vid_pullup == 0.0) {
790                         _right_layout->set_text (_("pullup: \u2012"));
791                 } else {
792                         sprintf (buf, _("pullup %-6.4f"), vid_pullup);
793                         _right_layout->set_text (buf);
794                 }
795         }
796 }
797
798 void
799 AudioClock::set_minsec (framepos_t when, bool force)
800 {
801         char buf[32];
802         framecnt_t left;
803         int hrs;
804         int mins;
805         int secs;
806         int millisecs;
807         bool negative = false;
808
809         if (_off) {
810                 _layout->set_text ("\u2012\u2012:\u2012\u2012:\u2012\u2012.\u2012\u2012\u2012");
811
812                 if (_left_layout) {
813                         _left_layout->set_text ("");
814                         _right_layout->set_text ("");
815                 }
816                 
817                 return;
818         }       
819
820         if (when < 0) {
821                 when = -when;
822                 negative = true;
823         }
824
825         left = when;
826         hrs = (int) floor (left / (_session->frame_rate() * 60.0f * 60.0f));
827         left -= (framecnt_t) floor (hrs * _session->frame_rate() * 60.0f * 60.0f);
828         mins = (int) floor (left / (_session->frame_rate() * 60.0f));
829         left -= (framecnt_t) floor (mins * _session->frame_rate() * 60.0f);
830         secs = (int) floor (left / (float) _session->frame_rate());
831         left -= (framecnt_t) floor (secs * _session->frame_rate());
832         millisecs = floor (left * 1000.0 / (float) _session->frame_rate());
833         
834         if (negative) {
835                 snprintf (buf, sizeof (buf), "-%02" PRId32 ":%02" PRId32 ":%02" PRId32 ".%03" PRId32, hrs, mins, secs, millisecs);
836         } else {
837                 snprintf (buf, sizeof (buf), " %02" PRId32 ":%02" PRId32 ":%02" PRId32 ".%03" PRId32, hrs, mins, secs, millisecs);
838         }
839
840         _layout->set_text (buf);
841 }
842
843 void
844 AudioClock::set_timecode (framepos_t when, bool force)
845 {
846         char buf[32];
847         Timecode::Time TC;
848         bool negative = false;
849
850         if (_off) {
851                 _layout->set_text ("\u2012\u2012:\u2012\u2012:\u2012\u2012:\u2012\u2012");
852                 if (_left_layout) {
853                         _left_layout->set_text ("");
854                         _right_layout->set_text ("");
855                 }
856                 
857                 return;
858         }
859
860         if (when < 0) {
861                 when = -when;
862                 negative = true;
863         }
864
865         if (is_duration) {
866                 _session->timecode_duration (when, TC);
867         } else {
868                 _session->timecode_time (when, TC);
869         }
870         
871         if (TC.negative || negative) {
872                 snprintf (buf, sizeof (buf), "-%02" PRIu32 ":%02" PRIu32 ":%02" PRIu32 ":%02" PRIu32, TC.hours, TC.minutes, TC.seconds, TC.frames);
873         } else {
874                 snprintf (buf, sizeof (buf), " %02" PRIu32 ":%02" PRIu32 ":%02" PRIu32 ":%02" PRIu32, TC.hours, TC.minutes, TC.seconds, TC.frames);
875         }
876
877         _layout->set_text (buf);
878
879         if (_left_layout) {
880
881                 if (_session->config.get_external_sync()) {
882                         switch (_session->config.get_sync_source()) {
883                         case JACK:
884                                 _left_layout->set_text ("JACK");
885                                 break;
886                         case MTC:
887                                 _left_layout->set_text ("MTC");
888                                 break;
889                         case MIDIClock:
890                                 _left_layout->set_text ("M-Clock");
891                                 break;
892                         }
893                 } else {
894                         _left_layout->set_text ("INT");
895                 }
896
897                 double timecode_frames = _session->timecode_frames_per_second();
898         
899                 if (fmod(timecode_frames, 1.0) == 0.0) {
900                         sprintf (buf, "FPS %u %s", int (timecode_frames), (_session->timecode_drop_frames() ? "D" : ""));
901                 } else {
902                         sprintf (buf, "%.2f %s", timecode_frames, (_session->timecode_drop_frames() ? "D" : ""));
903                 }
904
905                 _right_layout->set_text (buf);
906         }
907 }
908
909 void
910 AudioClock::set_bbt (framepos_t when, bool force)
911 {
912         char buf[16];
913         Timecode::BBT_Time BBT;
914         bool negative = false;
915
916         if (_off) {
917                 _layout->set_text ("\u2012\u2012\u2012|\u2012\u2012|\u2012\u2012\u2012\u2012");
918                 if (_left_layout) {
919                         _left_layout->set_text ("");
920                         _right_layout->set_text ("");
921                 }
922                 return;
923         }
924
925         if (when < 0) {
926                 when = -when;
927                 negative = true;
928         }
929
930         /* handle a common case */
931         if (is_duration) {
932                 if (when == 0) {
933                         BBT.bars = 0;
934                         BBT.beats = 0;
935                         BBT.ticks = 0;
936                 } else {
937                         _session->tempo_map().bbt_time (when, BBT);
938                         BBT.bars--;
939                         BBT.beats--;
940                 }
941         } else {
942                 _session->tempo_map().bbt_time (when, BBT);
943         }
944
945         if (negative) {
946                 snprintf (buf, sizeof (buf), "-%03" PRIu32 "|%02" PRIu32 "|%04" PRIu32, BBT.bars, BBT.beats, BBT.ticks);
947         } else {
948                 snprintf (buf, sizeof (buf), " %03" PRIu32 "|%02" PRIu32 "|%04" PRIu32, BBT.bars, BBT.beats, BBT.ticks);
949         }
950
951         _layout->set_text (buf);
952                  
953         if (_right_layout) {
954                 framepos_t pos;
955
956                 if (bbt_reference_time < 0) {
957                         pos = when;
958                 } else {
959                         pos = bbt_reference_time;
960                 }
961
962                 TempoMetric m (_session->tempo_map().metric_at (pos));
963
964                 sprintf (buf, "%-5.2f", m.tempo().beats_per_minute());
965                 _left_layout->set_text (buf);
966
967                 sprintf (buf, "%g|%g", m.meter().beats_per_bar(), m.meter().note_divisor());
968                 _right_layout->set_text (buf);
969         }
970 }
971
972 void
973 AudioClock::set_session (Session *s)
974 {
975         SessionHandlePtr::set_session (s);
976
977         if (_session) {
978
979                 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), boost::bind (&AudioClock::session_configuration_changed, this, _1), gui_context());
980
981                 const XMLProperty* prop;
982                 XMLNode* node = _session->extra_xml (X_("ClockModes"));
983                 AudioClock::Mode amode;
984
985                 if (node) {
986                         for (XMLNodeList::const_iterator i = node->children().begin(); i != node->children().end(); ++i) {
987                                 if ((prop = (*i)->property (X_("name"))) && prop->value() == _name) {
988
989                                         if ((prop = (*i)->property (X_("mode"))) != 0) {
990                                                 amode = AudioClock::Mode (string_2_enum (prop->value(), amode));
991                                                 set_mode (amode);
992                                         }
993                                         if ((prop = (*i)->property (X_("on"))) != 0) {
994                                                 set_off (!string_is_affirmative (prop->value()));
995                                         }
996                                         break;
997                                 }
998                         }
999                 }
1000
1001                 set (last_when, true);
1002         }
1003 }
1004
1005 bool
1006 AudioClock::on_key_press_event (GdkEventKey* ev)
1007 {
1008         if (!editing) {
1009                 return false;
1010         }
1011         
1012         /* return true for keys that we MIGHT use 
1013            at release
1014         */
1015         switch (ev->keyval) {
1016         case GDK_0:
1017         case GDK_KP_0:
1018         case GDK_1:
1019         case GDK_KP_1:
1020         case GDK_2:
1021         case GDK_KP_2:
1022         case GDK_3:
1023         case GDK_KP_3:
1024         case GDK_4:
1025         case GDK_KP_4:
1026         case GDK_5:
1027         case GDK_KP_5:
1028         case GDK_6:
1029         case GDK_KP_6:
1030         case GDK_7:
1031         case GDK_KP_7:
1032         case GDK_8:
1033         case GDK_KP_8:
1034         case GDK_9:
1035         case GDK_KP_9:
1036         case GDK_period:
1037         case GDK_comma:
1038         case GDK_KP_Decimal:
1039         case GDK_Tab:
1040         case GDK_Return:
1041         case GDK_KP_Enter:
1042         case GDK_Escape:
1043                 return true;
1044         default:
1045                 return false;
1046         }
1047 }
1048
1049 bool
1050 AudioClock::on_key_release_event (GdkEventKey *ev)
1051 {
1052         if (!editing) {
1053                 return false;
1054         }
1055
1056         string new_text;
1057         char new_char = 0;
1058
1059         switch (ev->keyval) {
1060         case GDK_0:
1061         case GDK_KP_0:
1062                 new_char = '0';
1063                 break;
1064         case GDK_1:
1065         case GDK_KP_1:
1066                 new_char = '1';
1067                 break;
1068         case GDK_2:
1069         case GDK_KP_2:
1070                 new_char = '2';
1071                 break;
1072         case GDK_3:
1073         case GDK_KP_3:
1074                 new_char = '3';
1075                 break;
1076         case GDK_4:
1077         case GDK_KP_4:
1078                 new_char = '4';
1079                 break;
1080         case GDK_5:
1081         case GDK_KP_5:
1082                 new_char = '5';
1083                 break;
1084         case GDK_6:
1085         case GDK_KP_6:
1086                 new_char = '6';
1087                 break;
1088         case GDK_7:
1089         case GDK_KP_7:
1090                 new_char = '7';
1091                 break;
1092         case GDK_8:
1093         case GDK_KP_8:
1094                 new_char = '8';
1095                 break;
1096         case GDK_9:
1097         case GDK_KP_9:
1098                 new_char = '9';
1099                 break;
1100
1101         case GDK_minus:
1102         case GDK_KP_Subtract:
1103                 end_edit_relative (false);
1104                 return true;
1105                 break;
1106
1107         case GDK_plus:
1108         case GDK_KP_Add:
1109                 end_edit_relative (true);
1110                 return true;
1111                 break;
1112
1113         case GDK_Tab:
1114         case GDK_Return:
1115         case GDK_KP_Enter:
1116                 end_edit (true);
1117                 return true;
1118                 break;
1119
1120         case GDK_Escape:
1121                 end_edit (false);
1122                 ChangeAborted();  /*  EMIT SIGNAL  */
1123                 return true;
1124
1125         case GDK_Delete:
1126         case GDK_BackSpace:
1127                 input_string = input_string.substr (1, input_string.length() - 1);
1128                 goto use_input_string;
1129
1130         default:
1131                 return false;
1132         }
1133
1134         if (!insert_map.empty() && (input_string.length() >= insert_map.size())) {
1135                 /* eat the key event, but do no nothing with it */
1136                 return true;
1137         }
1138
1139         input_string.insert (input_string.begin(), new_char);
1140
1141   use_input_string:
1142
1143         string::reverse_iterator ri;
1144         vector<int> insert_at;
1145         int highlight_length = 0;
1146         char buf[32];
1147         framepos_t pos;
1148
1149         /* merge with pre-edit-string into edit string */
1150         
1151         switch (_mode) {
1152         case Frames:
1153                 /* get this one in the right order, and to the right width */
1154                 if (ev->keyval != GDK_Delete && ev->keyval != GDK_BackSpace) {
1155                         edit_string.push_back (new_char);
1156                 } else {
1157                         edit_string = edit_string.substr (0, edit_string.length() - 1);
1158                 }
1159                 if (!edit_string.empty()) {
1160                         sscanf (edit_string.c_str(), "%" PRId64, &pos);
1161                         snprintf (buf, sizeof (buf), " %10" PRId64, pos);
1162                         edit_string = buf;
1163                 }
1164                 highlight_length = edit_string.length();
1165                 break;
1166                 
1167         default:
1168                 edit_string = pre_edit_string;
1169                 
1170                 /* backup through the original string, till we have
1171                  * enough digits locations to put all the digits from
1172                  * the input string.
1173                  */
1174                 
1175                 for (ri = edit_string.rbegin(); ri != edit_string.rend(); ++ri) {
1176                         if (isdigit (*ri)) {
1177                                 insert_at.push_back (edit_string.length() - (ri - edit_string.rbegin()) - 1);
1178                                 if (insert_at.size() == input_string.length()) {
1179                                         break;
1180                                 }
1181                         }
1182                 }
1183                 
1184                 if (insert_at.size() != input_string.length()) {
1185                         error << "something went wrong " << endmsg;
1186                 } else {
1187                         for (int i = input_string.length() - 1; i >= 0; --i) {
1188                                 edit_string[insert_at[i]] = input_string[i];
1189                         }
1190                         
1191                         highlight_length = edit_string.length() - insert_at.back();
1192                 }
1193                 
1194                 break;
1195         }
1196         
1197         if (edit_string != _layout->get_text()) {
1198                 show_edit_status (highlight_length);
1199                 _layout->set_text (edit_string);
1200                 queue_draw ();
1201         } 
1202
1203         return true;
1204 }
1205
1206 AudioClock::Field
1207 AudioClock::index_to_field (int index) const
1208 {
1209         switch (_mode) {
1210         case Timecode:
1211                 if (index < 4) {
1212                         return Timecode_Hours;
1213                 } else if (index < 7) {
1214                         return Timecode_Minutes;
1215                 } else if (index < 10) {
1216                         return Timecode_Seconds;
1217                 } else {
1218                         return Timecode_Frames;
1219                 }
1220                 break;
1221         case BBT:
1222                 if (index < 5) {
1223                         return Bars;
1224                 } else if (index < 7) {
1225                         return Beats;
1226                 } else {
1227                         return Ticks;
1228                 }
1229                 break;
1230         case MinSec:
1231                 if (index < 3) {
1232                         return Timecode_Hours;
1233                 } else if (index < 6) {
1234                         return MS_Minutes;
1235                 } else if (index < 9) {
1236                         return MS_Seconds;
1237                 } else {
1238                         return MS_Milliseconds;
1239                 }
1240                 break;
1241         case Frames:
1242                 return AudioFrames;
1243                 break;
1244         }
1245
1246         return Field (0);
1247 }
1248
1249 bool
1250 AudioClock::on_button_press_event (GdkEventButton *ev)
1251 {
1252         switch (ev->button) {
1253         case 1:
1254                 if (editable && !_off) {
1255                         dragging = true;
1256                         /* make absolutely sure that the pointer is grabbed */
1257                         gdk_pointer_grab(ev->window,false ,
1258                                          GdkEventMask( Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK |Gdk::BUTTON_RELEASE_MASK),
1259                                          NULL,NULL,ev->time);
1260                         drag_accum = 0;
1261                         drag_start_y = ev->y;
1262                         drag_y = ev->y;
1263                         
1264                         int index;
1265                         int trailing;
1266                         int y;
1267                         int x;
1268
1269                         /* the text has been centered vertically, so adjust
1270                          * x and y. 
1271                          */
1272
1273                         y = ev->y - ((upper_height - layout_height)/2);
1274                         x = ev->x - x_leading_padding;
1275                         
1276                         if (_layout->xy_to_index (x * PANGO_SCALE, y * PANGO_SCALE, index, trailing)) {                 
1277                                 drag_field = index_to_field (index);
1278                         } else {
1279                                 drag_field = Field (0);
1280                         }
1281                 }
1282                 break;
1283                 
1284         default:
1285                 return false;
1286                 break;
1287         }
1288
1289         return true;
1290 }
1291
1292 bool
1293 AudioClock::on_button_release_event (GdkEventButton *ev)
1294 {
1295         if (editable && !_off) {
1296                 if (dragging) {
1297                         gdk_pointer_ungrab (GDK_CURRENT_TIME);
1298                         dragging = false;
1299                         if (ev->y > drag_start_y+1 || ev->y < drag_start_y-1 || Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)){
1300                                 // we actually dragged so return without
1301                                 // setting editing focus, or we shift clicked
1302                                 return true;
1303                         } else {
1304                                 if (ev->button == 1) {
1305                                         start_edit ();
1306                                 }
1307                         }
1308
1309                 }
1310         }
1311
1312         if (Keyboard::is_context_menu_event (ev)) {
1313                 if (ops_menu == 0) {
1314                         build_ops_menu ();
1315                 }
1316                 ops_menu->popup (1, ev->time);
1317                 return true;
1318         }
1319
1320         return false;
1321 }
1322
1323 bool
1324 AudioClock::on_focus_out_event (GdkEventFocus* ev)
1325 {
1326         bool ret = CairoWidget::on_focus_out_event (ev);
1327
1328         if (editing) {
1329                 end_edit (false);
1330         }
1331
1332         return ret;
1333 }
1334
1335 bool
1336 AudioClock::on_scroll_event (GdkEventScroll *ev)
1337 {
1338         int index;
1339         int trailing;
1340
1341         if (editing || _session == 0 || !editable || _off) {
1342                 return false;
1343         }
1344
1345         int y;
1346         int x;
1347         
1348         /* the text has been centered vertically, so adjust
1349          * x and y. 
1350          */
1351
1352         y = ev->y - ((upper_height - layout_height)/2);
1353         x = ev->x - x_leading_padding;
1354
1355         if (!_layout->xy_to_index (x * PANGO_SCALE, y * PANGO_SCALE, index, trailing)) {
1356                 /* not in the main layout */
1357                 return false;
1358         }
1359         
1360         Field f = index_to_field (index);
1361         framepos_t frames = 0;
1362
1363         switch (ev->direction) {
1364
1365         case GDK_SCROLL_UP:
1366                 frames = get_frame_step (f);
1367                 if (frames != 0) {
1368                         if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1369                                 frames *= 10;
1370                         }
1371                         set (current_time() + frames, true);
1372                         ValueChanged (); /* EMIT_SIGNAL */
1373                 }
1374                 break;
1375                 
1376         case GDK_SCROLL_DOWN:
1377                 frames = get_frame_step (f);
1378                 if (frames != 0) {
1379                         if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1380                                 frames *= 10;
1381                         }
1382                         
1383                         if ((double)current_time() - (double)frames < 0.0) {
1384                                 set (0, true);
1385                         } else {
1386                                 set (current_time() - frames, true);
1387                         }
1388                         
1389                         ValueChanged (); /* EMIT_SIGNAL */
1390                 }
1391                 break;
1392                 
1393         default:
1394                 return false;
1395                 break;
1396         }
1397         
1398         return true;
1399 }
1400
1401 bool
1402 AudioClock::on_motion_notify_event (GdkEventMotion *ev)
1403 {
1404         if (editing || _session == 0 || !dragging) {
1405                 return false;
1406         }
1407
1408         float pixel_frame_scale_factor = 0.2f;
1409
1410         if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier))  {
1411                 pixel_frame_scale_factor = 0.1f;
1412         }
1413
1414
1415         if (Keyboard::modifier_state_contains (ev->state,
1416                                                Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) {
1417
1418                 pixel_frame_scale_factor = 0.025f;
1419         }
1420
1421         double y_delta = ev->y - drag_y;
1422
1423         drag_accum +=  y_delta*pixel_frame_scale_factor;
1424
1425         drag_y = ev->y;
1426
1427         if (trunc (drag_accum) != 0) {
1428
1429                 framepos_t frames;
1430                 framepos_t pos;
1431                 int dir;
1432                 dir = (drag_accum < 0 ? 1:-1);
1433                 pos = current_time();
1434                 frames = get_frame_step (drag_field,pos,dir);
1435
1436                 if (frames  != 0 &&  frames * drag_accum < current_time()) {
1437                         set ((framepos_t) floor (pos - drag_accum * frames), false); // minus because up is negative in GTK
1438                 } else {
1439                         set (0 , false);
1440                 }
1441
1442                 drag_accum= 0;
1443                 ValueChanged();  /* EMIT_SIGNAL */
1444         }
1445
1446         return true;
1447 }
1448
1449 framepos_t
1450 AudioClock::get_frame_step (Field field, framepos_t pos, int dir)
1451 {
1452         framecnt_t f = 0;
1453         Timecode::BBT_Time BBT;
1454         switch (field) {
1455         case Timecode_Hours:
1456                 f = (framecnt_t) floor (3600.0 * _session->frame_rate());
1457                 break;
1458         case Timecode_Minutes:
1459                 f = (framecnt_t) floor (60.0 * _session->frame_rate());
1460                 break;
1461         case Timecode_Seconds:
1462                 f = _session->frame_rate();
1463                 break;
1464         case Timecode_Frames:
1465                 f = (framecnt_t) floor (_session->frame_rate() / _session->timecode_frames_per_second());
1466                 break;
1467
1468         case AudioFrames:
1469                 f = 1;
1470                 break;
1471
1472         case MS_Hours:
1473                 f = (framecnt_t) floor (3600.0 * _session->frame_rate());
1474                 break;
1475         case MS_Minutes:
1476                 f = (framecnt_t) floor (60.0 * _session->frame_rate());
1477                 break;
1478         case MS_Seconds:
1479                 f = (framecnt_t) _session->frame_rate();
1480                 break;
1481         case MS_Milliseconds:
1482                 f = (framecnt_t) floor (_session->frame_rate() / 1000.0);
1483                 break;
1484
1485         case Bars:
1486                 BBT.bars = 1;
1487                 BBT.beats = 0;
1488                 BBT.ticks = 0;
1489                 f = _session->tempo_map().bbt_duration_at (pos,BBT,dir);
1490                 break;
1491         case Beats:
1492                 BBT.bars = 0;
1493                 BBT.beats = 1;
1494                 BBT.ticks = 0;
1495                 f = _session->tempo_map().bbt_duration_at(pos,BBT,dir);
1496                 break;
1497         case Ticks:
1498                 BBT.bars = 0;
1499                 BBT.beats = 0;
1500                 BBT.ticks = 1;
1501                 f = _session->tempo_map().bbt_duration_at(pos,BBT,dir);
1502                 break;
1503         default:
1504                 error << string_compose (_("programming error: %1"), "attempt to get frames from non-text field!") << endmsg;
1505                 f = 0;
1506                 break;
1507         }
1508
1509         return f;
1510 }
1511
1512 framepos_t
1513 AudioClock::current_time (framepos_t pos) const
1514 {
1515         return last_when;
1516 }
1517
1518 framepos_t
1519 AudioClock::current_duration (framepos_t pos) const
1520 {
1521         framepos_t ret = 0;
1522
1523         switch (_mode) {
1524         case Timecode:
1525                 ret = last_when;
1526                 break;
1527         case BBT:
1528                 ret = frame_duration_from_bbt_string (pos, _layout->get_text());
1529                 break;
1530
1531         case MinSec:
1532                 ret = last_when;
1533                 break;
1534
1535         case Frames:
1536                 ret = last_when;
1537                 break;
1538         }
1539
1540         return ret;
1541 }
1542
1543 bool
1544 AudioClock::bbt_validate_edit (const string& str)
1545 {
1546         AnyTime any;
1547
1548         sscanf (str.c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, &any.bbt.bars, &any.bbt.beats, &any.bbt.ticks);
1549         
1550         if (!is_duration && any.bbt.bars == 0) {
1551                 return false;
1552         }
1553
1554         if (!is_duration && any.bbt.beats == 0) {
1555                 return false;
1556         }
1557
1558         return true;
1559 }
1560
1561 bool
1562 AudioClock::timecode_validate_edit (const string& str)
1563 {
1564         Timecode::Time TC;
1565
1566         if (sscanf (_layout->get_text().c_str(), "%" PRId32 ":%" PRId32 ":%" PRId32 ":%" PRId32, 
1567                     &TC.hours, &TC.minutes, &TC.seconds, &TC.frames) != 4) {
1568                 return false;
1569         }
1570
1571         if (TC.minutes > 59 || TC.seconds > 59) {
1572                 return false;
1573         }
1574
1575         if (TC.frames > (long)rint(_session->timecode_frames_per_second()) - 1) {
1576                 return false;
1577         }
1578
1579         if (_session->timecode_drop_frames()) {
1580                 if (TC.minutes % 10 && TC.seconds == 0 && TC.frames < 2) {
1581                         return false;
1582                 }
1583         }
1584
1585         return true;
1586 }
1587
1588 framepos_t
1589 AudioClock::frames_from_timecode_string (const string& str) const
1590 {
1591         if (_session == 0) {
1592                 return 0;
1593         }
1594
1595         Timecode::Time TC;
1596         framepos_t sample;
1597
1598         if (sscanf (str.c_str(), "%d:%d:%d:%d", &TC.hours, &TC.minutes, &TC.seconds, &TC.frames) != 4) {
1599                 error << string_compose (_("programming error: %1 %2"), "badly formatted timecode clock string", str) << endmsg;
1600                 return 0;
1601         }
1602
1603         TC.rate = _session->timecode_frames_per_second();
1604         TC.drop= _session->timecode_drop_frames();
1605
1606         _session->timecode_to_sample (TC, sample, false /* use_offset */, false /* use_subframes */ );
1607         
1608         // timecode_tester ();
1609
1610         return sample;
1611 }
1612
1613 framepos_t
1614 AudioClock::frames_from_minsec_string (const string& str) const
1615 {
1616         if (_session == 0) {
1617                 return 0;
1618         }
1619
1620         int hrs, mins, secs, millisecs;
1621         framecnt_t sr = _session->frame_rate();
1622
1623         if (sscanf (str.c_str(), "%d:%d:%d.%d", &hrs, &mins, &secs, &millisecs) != 4) {
1624                 error << string_compose (_("programming error: %1 %2"), "badly formatted minsec clock string", str) << endmsg;
1625                 return 0;
1626         }
1627
1628         return (framepos_t) floor ((hrs * 60.0f * 60.0f * sr) + (mins * 60.0f * sr) + (secs * sr) + (millisecs * sr / 1000.0));
1629 }
1630
1631 framepos_t
1632 AudioClock::frames_from_bbt_string (framepos_t pos, const string& str) const
1633 {
1634         if (_session == 0) {
1635                 error << "AudioClock::current_time() called with BBT mode but without session!" << endmsg;
1636                 return 0;
1637         }
1638
1639         AnyTime any;
1640         any.type = AnyTime::BBT;
1641
1642         sscanf (str.c_str(), "%" PRId32 "|%" PRId32 "|%" PRId32, &any.bbt.bars, &any.bbt.beats, &any.bbt.ticks);
1643         
1644         if (is_duration) {
1645                 any.bbt.bars++;
1646                 any.bbt.beats++;
1647                 return _session->any_duration_to_frames (pos, any);
1648         } else {
1649                 return _session->convert_to_frames (any);
1650         }
1651 }
1652
1653
1654 framepos_t
1655 AudioClock::frame_duration_from_bbt_string (framepos_t pos, const string& str) const
1656 {
1657         if (_session == 0) {
1658                 error << "AudioClock::current_time() called with BBT mode but without session!" << endmsg;
1659                 return 0;
1660         }
1661
1662         Timecode::BBT_Time bbt;
1663
1664         sscanf (str.c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, &bbt.bars, &bbt.beats, &bbt.ticks);
1665
1666         return _session->tempo_map().bbt_duration_at(pos,bbt,1);
1667 }
1668
1669 framepos_t
1670 AudioClock::frames_from_audioframes_string (const string& str) const
1671 {
1672         framepos_t f;
1673         sscanf (str.c_str(), "%" PRId64, &f);
1674         return f;
1675 }
1676
1677 void
1678 AudioClock::build_ops_menu ()
1679 {
1680         using namespace Menu_Helpers;
1681         ops_menu = new Menu;
1682         MenuList& ops_items = ops_menu->items();
1683         ops_menu->set_name ("ArdourContextMenu");
1684
1685         if (!Profile->get_sae()) {
1686                 ops_items.push_back (MenuElem (_("Timecode"), sigc::bind (sigc::mem_fun(*this, &AudioClock::set_mode), Timecode)));
1687         }
1688         ops_items.push_back (MenuElem (_("Bars:Beats"), sigc::bind (sigc::mem_fun(*this, &AudioClock::set_mode), BBT)));
1689         ops_items.push_back (MenuElem (_("Minutes:Seconds"), sigc::bind (sigc::mem_fun(*this, &AudioClock::set_mode), MinSec)));
1690         ops_items.push_back (MenuElem (_("Samples"), sigc::bind (sigc::mem_fun(*this, &AudioClock::set_mode), Frames)));
1691
1692         if (editable && !_off && !is_duration && !_follows_playhead) {
1693                 ops_items.push_back (SeparatorElem());
1694                 ops_items.push_back (MenuElem (_("Set From Playhead"), sigc::mem_fun(*this, &AudioClock::set_from_playhead)));
1695                 ops_items.push_back (MenuElem (_("Locate to This Time"), sigc::mem_fun(*this, &AudioClock::locate)));
1696         }
1697 }
1698
1699 void
1700 AudioClock::set_from_playhead ()
1701 {
1702         if (!_session) {
1703                 return;
1704         }
1705
1706         set (_session->transport_frame());
1707         ValueChanged ();
1708 }
1709
1710 void
1711 AudioClock::locate ()
1712 {
1713         if (!_session || is_duration) {
1714                 return;
1715         }
1716
1717         _session->request_locate (current_time(), _session->transport_rolling ());
1718 }
1719
1720 void
1721 AudioClock::set_mode (Mode m)
1722 {
1723         if (_mode == m) {
1724                 return;
1725         }
1726
1727         _mode = m;
1728
1729         insert_map.clear();
1730
1731         _layout->set_text ("");
1732
1733         if (_left_layout) {
1734                 _left_layout->set_text ("");
1735                 _right_layout->set_text ("");
1736         }
1737
1738         switch (_mode) {
1739         case Timecode:
1740                 mode_based_info_ratio = 0.5;
1741                 insert_map.push_back (11);
1742                 insert_map.push_back (10);
1743                 insert_map.push_back (8);
1744                 insert_map.push_back (7);
1745                 insert_map.push_back (5);
1746                 insert_map.push_back (4);
1747                 insert_map.push_back (2);
1748                 insert_map.push_back (1);
1749                 break;
1750                 
1751         case BBT:
1752                 mode_based_info_ratio = 0.5;
1753                 insert_map.push_back (11);
1754                 insert_map.push_back (10);
1755                 insert_map.push_back (9);
1756                 insert_map.push_back (8);
1757                 insert_map.push_back (6);
1758                 insert_map.push_back (5);       
1759                 insert_map.push_back (3);       
1760                 insert_map.push_back (2);       
1761                 insert_map.push_back (1);       
1762                 break;
1763                 
1764         case MinSec:
1765                 mode_based_info_ratio = 1.0;
1766                 insert_map.push_back (12);
1767                 insert_map.push_back (11);
1768                 insert_map.push_back (10);
1769                 insert_map.push_back (8);
1770                 insert_map.push_back (7);
1771                 insert_map.push_back (5);
1772                 insert_map.push_back (4);
1773                 insert_map.push_back (2);       
1774                 insert_map.push_back (1);       
1775                 break;
1776                 
1777         case Frames:
1778                 mode_based_info_ratio = 0.5;
1779                 break;
1780         }
1781
1782         set (last_when, true);
1783
1784         if (!is_transient) {
1785                 ModeChanged (); /* EMIT SIGNAL (the static one)*/
1786         }
1787
1788         mode_changed (); /* EMIT SIGNAL (the member one) */
1789 }
1790
1791 void
1792 AudioClock::set_bbt_reference (framepos_t pos)
1793 {
1794         bbt_reference_time = pos;
1795 }
1796
1797 void
1798 AudioClock::on_style_changed (const Glib::RefPtr<Gtk::Style>& old_style)
1799 {
1800         CairoWidget::on_style_changed (old_style);
1801         set_font ();
1802         set_colors ();
1803 }
1804
1805 void
1806 AudioClock::set_editable (bool yn)
1807 {
1808         editable = yn;
1809 }
1810
1811 void
1812 AudioClock::set_is_duration (bool yn)
1813 {
1814         if (yn == is_duration) {
1815                 return;
1816         }
1817
1818         is_duration = yn;
1819         set (last_when, true);
1820 }
1821
1822 void
1823 AudioClock::set_off (bool yn) 
1824 {
1825         if (_off == yn) {
1826                 return;
1827         }
1828
1829         _off = yn;
1830
1831         /* force a redraw. last_when will be preserved, but the clock text will
1832          * change 
1833          */
1834         
1835         set (last_when, true);
1836 }
1837
1838 void
1839 AudioClock::focus ()
1840 {
1841         start_edit ();
1842 }
1843
1844