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