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