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