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