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