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