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