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