Zoom session when the mouse pointer is moved up and down during a playhead drag.
[ardour2.git] / gtk2_ardour / audio_clock.cc
blob6ab075b0ec7d2b23d16e8ccd5c4ecd1d4ab0e488
1 /*
2 Copyright (C) 1999 Paul Davis
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.
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.
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.
20 #include <cstdio> // for sprintf
21 #include <cmath>
23 #include "pbd/convert.h"
24 #include "pbd/enumwriter.h"
26 #include <gtkmm/style.h>
27 #include <gtkmm2ext/utils.h>
29 #include "ardour/ardour.h"
30 #include "ardour/session.h"
31 #include "ardour/tempo.h"
32 #include "ardour/profile.h"
33 #include <sigc++/bind.h>
35 #include "ardour_ui.h"
36 #include "audio_clock.h"
37 #include "utils.h"
38 #include "keyboard.h"
39 #include "gui_thread.h"
40 #include "i18n.h"
42 using namespace ARDOUR;
43 using namespace PBD;
44 using namespace Gtk;
45 using namespace std;
47 using Gtkmm2ext::Keyboard;
49 using PBD::atoi;
50 using PBD::atof;
52 sigc::signal<void> AudioClock::ModeChanged;
53 vector<AudioClock*> AudioClock::clocks;
55 const uint32_t AudioClock::field_length[(int) AudioClock::AudioFrames+1] = {
56 2, /* Timecode_Hours */
57 2, /* Timecode_Minutes */
58 2, /* Timecode_Seconds */
59 2, /* Timecode_Frames */
60 2, /* MS_Hours */
61 2, /* MS_Minutes */
62 2, /* MS_Seconds */
63 3, /* MS_Milliseconds */
64 3, /* Bars */
65 2, /* Beats */
66 4, /* Tick */
67 10 /* Audio Frame */
70 AudioClock::AudioClock (const string& clock_name, bool transient, const string& widget_name,
71 bool allow_edit, bool follows_playhead, bool duration, bool with_info)
72 : _name (clock_name),
73 is_transient (transient),
74 is_duration (duration),
75 editable (allow_edit),
76 _follows_playhead (follows_playhead),
77 colon1 (":"),
78 colon2 (":"),
79 colon3 (":"),
80 colon4 (":"),
81 colon5 (":"),
82 period1 ("."),
83 b1 ("|"),
84 b2 ("|"),
85 last_when(0),
86 _canonical_time_is_displayed (true),
87 _canonical_time (0)
89 /* XXX: these are leaked, but I don't suppose it's the end of the world */
91 _eboxes[Timecode_Hours] = new EventBox;
92 _eboxes[Timecode_Minutes] = new EventBox;
93 _eboxes[Timecode_Seconds] = new EventBox;
94 _eboxes[Timecode_Frames] = new EventBox;
95 _eboxes[MS_Hours] = new EventBox;
96 _eboxes[MS_Minutes] = new EventBox;
97 _eboxes[MS_Seconds] = new EventBox;
98 _eboxes[MS_Milliseconds] = new EventBox;
99 _eboxes[Bars] = new EventBox;
100 _eboxes[Beats] = new EventBox;
101 _eboxes[Ticks] = new EventBox;
102 _eboxes[AudioFrames] = new EventBox;
104 _labels[Timecode_Hours] = new Label;
105 _labels[Timecode_Minutes] = new Label;
106 _labels[Timecode_Seconds] = new Label;
107 _labels[Timecode_Frames] = new Label;
108 _labels[MS_Hours] = new Label;
109 _labels[MS_Minutes] = new Label;
110 _labels[MS_Seconds] = new Label;
111 _labels[MS_Milliseconds] = new Label;
112 _labels[Bars] = new Label;
113 _labels[Beats] = new Label;
114 _labels[Ticks] = new Label;
115 _labels[AudioFrames] = new Label;
117 last_when = 0;
118 last_pdelta = 0;
119 last_sdelta = 0;
120 key_entry_state = 0;
121 ops_menu = 0;
122 dragging = false;
123 bbt_reference_time = -1;
125 if (with_info) {
126 frames_upper_info_label = manage (new Label);
127 frames_lower_info_label = manage (new Label);
128 timecode_upper_info_label = manage (new Label);
129 timecode_lower_info_label = manage (new Label);
130 bbt_upper_info_label = manage (new Label);
131 bbt_lower_info_label = manage (new Label);
133 frames_upper_info_label->set_name ("AudioClockFramesUpperInfo");
134 frames_lower_info_label->set_name ("AudioClockFramesLowerInfo");
135 timecode_upper_info_label->set_name ("AudioClockTimecodeUpperInfo");
136 timecode_lower_info_label->set_name ("AudioClockTimecodeLowerInfo");
137 bbt_upper_info_label->set_name ("AudioClockBBTUpperInfo");
138 bbt_lower_info_label->set_name ("AudioClockBBTLowerInfo");
140 Gtkmm2ext::set_size_request_to_display_given_text(*timecode_upper_info_label, "23.98",0,0);
141 Gtkmm2ext::set_size_request_to_display_given_text(*timecode_lower_info_label, "NDF",0,0);
143 Gtkmm2ext::set_size_request_to_display_given_text(*bbt_upper_info_label, "88|88",0,0);
144 Gtkmm2ext::set_size_request_to_display_given_text(*bbt_lower_info_label, "888.88",0,0);
146 frames_info_box.pack_start (*frames_upper_info_label, true, true);
147 frames_info_box.pack_start (*frames_lower_info_label, true, true);
148 timecode_info_box.pack_start (*timecode_upper_info_label, true, true);
149 timecode_info_box.pack_start (*timecode_lower_info_label, true, true);
150 bbt_info_box.pack_start (*bbt_upper_info_label, true, true);
151 bbt_info_box.pack_start (*bbt_lower_info_label, true, true);
153 } else {
154 frames_upper_info_label = 0;
155 frames_lower_info_label = 0;
156 timecode_upper_info_label = 0;
157 timecode_lower_info_label = 0;
158 bbt_upper_info_label = 0;
159 bbt_lower_info_label = 0;
162 frames_packer.set_homogeneous (false);
163 frames_packer.set_border_width (2);
164 frames_packer.pack_start (*_eboxes[AudioFrames], false, false);
166 if (with_info) {
167 frames_packer.pack_start (frames_info_box, false, false, 5);
170 frames_packer_hbox.pack_start (frames_packer, true, false);
172 for (std::map<Field, EventBox*>::iterator i = _eboxes.begin(); i != _eboxes.end(); ++i) {
173 i->second->add (*_labels[i->first]);
176 timecode_packer.set_homogeneous (false);
177 timecode_packer.set_border_width (2);
178 timecode_packer.pack_start (*_eboxes[Timecode_Hours], false, false);
179 timecode_packer.pack_start (colon1, false, false);
180 timecode_packer.pack_start (*_eboxes[Timecode_Minutes], false, false);
181 timecode_packer.pack_start (colon2, false, false);
182 timecode_packer.pack_start (*_eboxes[Timecode_Seconds], false, false);
183 timecode_packer.pack_start (colon3, false, false);
184 timecode_packer.pack_start (*_eboxes[Timecode_Frames], false, false);
186 if (with_info) {
187 timecode_packer.pack_start (timecode_info_box, false, false, 5);
190 timecode_packer_hbox.pack_start (timecode_packer, true, false);
192 bbt_packer.set_homogeneous (false);
193 bbt_packer.set_border_width (2);
194 bbt_packer.pack_start (*_eboxes[Bars], false, false);
195 bbt_packer.pack_start (b1, false, false);
196 bbt_packer.pack_start (*_eboxes[Beats], false, false);
197 bbt_packer.pack_start (b2, false, false);
198 bbt_packer.pack_start (*_eboxes[Ticks], false, false);
200 if (with_info) {
201 bbt_packer.pack_start (bbt_info_box, false, false, 5);
204 bbt_packer_hbox.pack_start (bbt_packer, true, false);
206 minsec_packer.set_homogeneous (false);
207 minsec_packer.set_border_width (2);
208 minsec_packer.pack_start (*_eboxes[MS_Hours], false, false);
209 minsec_packer.pack_start (colon4, false, false);
210 minsec_packer.pack_start (*_eboxes[MS_Minutes], false, false);
211 minsec_packer.pack_start (colon5, false, false);
212 minsec_packer.pack_start (*_eboxes[MS_Seconds], false, false);
213 minsec_packer.pack_start (period1, false, false);
214 minsec_packer.pack_start (*_eboxes[MS_Milliseconds], false, false);
216 minsec_packer_hbox.pack_start (minsec_packer, true, false);
218 clock_frame.set_shadow_type (SHADOW_IN);
219 clock_frame.set_name ("BaseFrame");
221 clock_frame.add (clock_base);
223 set_widget_name (widget_name);
225 _mode = BBT; /* lie to force mode switch */
226 set_mode (Timecode);
228 pack_start (clock_frame, true, true);
230 /* the clock base handles button releases for menu popup regardless of
231 editable status. if the clock is editable, the clock base is where
232 we pass focus to after leaving the last editable "field", which
233 will then shutdown editing till the user starts it up again.
235 it does this because the focus out event on the field disables
236 keyboard event handling, and we don't connect anything up to
237 notice focus in on the clock base. hence, keyboard event handling
238 stays disabled.
241 clock_base.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::SCROLL_MASK);
242 clock_base.signal_button_release_event().connect (sigc::bind (sigc::mem_fun (*this, &AudioClock::field_button_release_event), Timecode_Hours));
244 if (editable) {
245 setup_events ();
248 set (last_when, true);
250 if (!is_transient) {
251 clocks.push_back (this);
255 void
256 AudioClock::set_widget_name (string name)
258 Widget::set_name (name);
260 clock_base.set_name (name);
262 for (std::map<Field, EventBox*>::iterator i = _eboxes.begin(); i != _eboxes.end(); ++i) {
263 i->second->set_name (name);
266 for (std::map<Field, Label*>::iterator i = _labels.begin(); i != _labels.end(); ++i) {
267 i->second->set_name (name);
270 colon1.set_name (name);
271 colon2.set_name (name);
272 colon3.set_name (name);
273 colon4.set_name (name);
274 colon5.set_name (name);
275 b1.set_name (name);
276 b2.set_name (name);
277 period1.set_name (name);
279 queue_draw ();
282 void
283 AudioClock::setup_events ()
285 clock_base.set_flags (CAN_FOCUS);
287 for (std::map<Field, EventBox*>::iterator i = _eboxes.begin(); i != _eboxes.end(); ++i) {
288 i->second->add_events (
289 Gdk::BUTTON_PRESS_MASK |
290 Gdk::BUTTON_RELEASE_MASK |
291 Gdk::KEY_PRESS_MASK |
292 Gdk::KEY_RELEASE_MASK |
293 Gdk::FOCUS_CHANGE_MASK |
294 Gdk::POINTER_MOTION_MASK |
295 Gdk::SCROLL_MASK);
297 i->second->set_flags (CAN_FOCUS);
298 i->second->signal_motion_notify_event().connect (sigc::bind (sigc::mem_fun (*this, &AudioClock::field_motion_notify_event), i->first));
299 i->second->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &AudioClock::field_button_press_event), i->first));
300 i->second->signal_button_release_event().connect (sigc::bind (sigc::mem_fun (*this, &AudioClock::field_button_release_event), i->first));
301 i->second->signal_scroll_event().connect (sigc::bind (sigc::mem_fun (*this, &AudioClock::field_button_scroll_event), i->first));
302 i->second->signal_key_press_event().connect (sigc::bind (sigc::mem_fun (*this, &AudioClock::field_key_press_event), i->first));
303 i->second->signal_key_release_event().connect (sigc::bind (sigc::mem_fun (*this, &AudioClock::field_key_release_event), i->first));
304 i->second->signal_focus_in_event().connect (sigc::bind (sigc::mem_fun (*this, &AudioClock::field_focus_in_event), i->first));
305 i->second->signal_focus_out_event().connect (sigc::bind (sigc::mem_fun (*this, &AudioClock::field_focus_out_event), i->first));
308 clock_base.signal_focus_in_event().connect (sigc::mem_fun (*this, &AudioClock::drop_focus_handler));
311 bool
312 AudioClock::drop_focus_handler (GdkEventFocus*)
314 Keyboard::magic_widget_drop_focus ();
315 return false;
318 void
319 AudioClock::on_realize ()
321 HBox::on_realize ();
323 /* styles are not available until the widgets are bound to a window */
325 set_size_requests ();
328 void
329 AudioClock::set (framepos_t when, bool force, framecnt_t offset, char which)
331 if ((!force && !is_visible()) || _session == 0) {
332 return;
335 bool const pdelta = Config->get_primary_clock_delta_edit_cursor ();
336 bool const sdelta = Config->get_secondary_clock_delta_edit_cursor ();
338 if (offset && which == 'p' && pdelta) {
339 when = (when > offset) ? when - offset : offset - when;
340 } else if (offset && which == 's' && sdelta) {
341 when = (when > offset) ? when - offset : offset - when;
344 if (when == last_when && !force) {
345 return;
348 if (which == 'p' && pdelta && !last_pdelta) {
349 set_widget_name("TransportClockDisplayDelta");
350 last_pdelta = true;
351 } else if (which == 'p' && !pdelta && last_pdelta) {
352 set_widget_name("TransportClockDisplay");
353 last_pdelta = false;
354 } else if (which == 's' && sdelta && !last_sdelta) {
355 set_widget_name("SecondaryClockDisplayDelta");
356 last_sdelta = true;
357 } else if (which == 's' && !sdelta && last_sdelta) {
358 set_widget_name("SecondaryClockDisplay");
359 last_sdelta = false;
362 switch (_mode) {
363 case Timecode:
364 set_timecode (when, force);
365 break;
367 case BBT:
368 set_bbt (when, force);
369 break;
371 case MinSec:
372 set_minsec (when, force);
373 break;
375 case Frames:
376 set_frames (when, force);
377 break;
379 case Off:
380 break;
383 last_when = when;
385 /* we're setting the time from a frames value, so keep it as the canonical value */
386 _canonical_time = when;
387 _canonical_time_is_displayed = false;
390 void
391 AudioClock::session_configuration_changed (std::string p)
393 if (p != "timecode-offset" && p != "timecode-offset-negative") {
394 return;
397 framecnt_t current;
399 switch (_mode) {
400 case Timecode:
401 if (is_duration) {
402 current = current_duration ();
403 } else {
404 current = current_time ();
406 set (current, true);
407 break;
408 default:
409 break;
413 void
414 AudioClock::set_frames (framepos_t when, bool /*force*/)
416 char buf[32];
417 snprintf (buf, sizeof (buf), "%" PRId64, when);
418 _labels[AudioFrames]->set_text (buf);
420 if (frames_upper_info_label) {
421 framecnt_t rate = _session->frame_rate();
423 if (fmod (rate, 1000.0) == 0.000) {
424 sprintf (buf, "%" PRId64 "K", rate/1000);
425 } else {
426 sprintf (buf, "%.3fK", rate/1000.0f);
429 if (frames_upper_info_label->get_text() != buf) {
430 frames_upper_info_label->set_text (buf);
433 float vid_pullup = _session->config.get_video_pullup();
435 if (vid_pullup == 0.0) {
436 if (frames_lower_info_label->get_text () != _("none")) {
437 frames_lower_info_label->set_text(_("none"));
439 } else {
440 sprintf (buf, "%-6.4f", vid_pullup);
441 if (frames_lower_info_label->get_text() != buf) {
442 frames_lower_info_label->set_text (buf);
448 void
449 AudioClock::set_minsec (framepos_t when, bool force)
451 char buf[32];
452 framecnt_t left;
453 int hrs;
454 int mins;
455 int secs;
456 int millisecs;
458 left = when;
459 hrs = (int) floor (left / (_session->frame_rate() * 60.0f * 60.0f));
460 left -= (framecnt_t) floor (hrs * _session->frame_rate() * 60.0f * 60.0f);
461 mins = (int) floor (left / (_session->frame_rate() * 60.0f));
462 left -= (framecnt_t) floor (mins * _session->frame_rate() * 60.0f);
463 secs = (int) floor (left / (float) _session->frame_rate());
464 left -= (framecnt_t) floor (secs * _session->frame_rate());
465 millisecs = floor (left * 1000.0 / (float) _session->frame_rate());
467 if (force || hrs != ms_last_hrs) {
468 sprintf (buf, "%02d", hrs);
469 _labels[MS_Hours]->set_text (buf);
470 ms_last_hrs = hrs;
473 if (force || mins != ms_last_mins) {
474 sprintf (buf, "%02d", mins);
475 _labels[MS_Minutes]->set_text (buf);
476 ms_last_mins = mins;
479 if (force || secs != ms_last_secs) {
480 sprintf (buf, "%02d", secs);
481 _labels[MS_Seconds]->set_text (buf);
482 ms_last_secs = secs;
485 if (force || millisecs != ms_last_millisecs) {
486 sprintf (buf, "%03d", millisecs);
487 _labels[MS_Milliseconds]->set_text (buf);
488 ms_last_millisecs = millisecs;
492 void
493 AudioClock::set_timecode (framepos_t when, bool force)
495 char buf[32];
496 Timecode::Time timecode;
498 if (is_duration) {
499 _session->timecode_duration (when, timecode);
500 } else {
501 _session->timecode_time (when, timecode);
504 if (force || timecode.hours != last_hrs || timecode.negative != last_negative) {
505 if (timecode.negative) {
506 sprintf (buf, "-%02" PRIu32, timecode.hours);
507 } else {
508 sprintf (buf, " %02" PRIu32, timecode.hours);
510 _labels[Timecode_Hours]->set_text (buf);
511 last_hrs = timecode.hours;
512 last_negative = timecode.negative;
515 if (force || timecode.minutes != last_mins) {
516 sprintf (buf, "%02" PRIu32, timecode.minutes);
517 _labels[Timecode_Minutes]->set_text (buf);
518 last_mins = timecode.minutes;
521 if (force || timecode.seconds != last_secs) {
522 sprintf (buf, "%02" PRIu32, timecode.seconds);
523 _labels[Timecode_Seconds]->set_text (buf);
524 last_secs = timecode.seconds;
527 if (force || timecode.frames != last_frames) {
528 sprintf (buf, "%02" PRIu32, timecode.frames);
529 _labels[Timecode_Frames]->set_text (buf);
530 last_frames = timecode.frames;
533 if (timecode_upper_info_label) {
534 double timecode_frames = _session->timecode_frames_per_second();
536 if (fmod(timecode_frames, 1.0) == 0.0) {
537 sprintf (buf, "%u", int (timecode_frames));
538 } else {
539 sprintf (buf, "%.2f", timecode_frames);
542 if (timecode_upper_info_label->get_text() != buf) {
543 timecode_upper_info_label->set_text (buf);
546 if ((fabs(timecode_frames - 29.97) < 0.0001) || timecode_frames == 30) {
547 if (_session->timecode_drop_frames()) {
548 sprintf (buf, "DF");
549 } else {
550 sprintf (buf, "NDF");
552 } else {
553 // there is no drop frame alternative
554 buf[0] = '\0';
557 if (timecode_lower_info_label->get_text() != buf) {
558 timecode_lower_info_label->set_text (buf);
563 void
564 AudioClock::set_bbt (framepos_t when, bool force)
566 char buf[16];
567 Timecode::BBT_Time bbt;
569 /* handle a common case */
570 if (is_duration) {
571 if (when == 0) {
572 bbt.bars = 0;
573 bbt.beats = 0;
574 bbt.ticks = 0;
575 } else {
576 _session->tempo_map().bbt_time (when, bbt);
577 bbt.bars--;
578 bbt.beats--;
580 } else {
581 _session->tempo_map().bbt_time (when, bbt);
584 sprintf (buf, "%03" PRIu32, bbt.bars);
585 if (force || _labels[Bars]->get_text () != buf) {
586 _labels[Bars]->set_text (buf);
588 sprintf (buf, "%02" PRIu32, bbt.beats);
589 if (force || _labels[Beats]->get_text () != buf) {
590 _labels[Beats]->set_text (buf);
592 sprintf (buf, "%04" PRIu32, bbt.ticks);
593 if (force || _labels[Ticks]->get_text () != buf) {
594 _labels[Ticks]->set_text (buf);
597 if (bbt_upper_info_label) {
598 framepos_t pos;
600 if (bbt_reference_time < 0) {
601 pos = when;
602 } else {
603 pos = bbt_reference_time;
606 TempoMetric m (_session->tempo_map().metric_at (pos));
608 sprintf (buf, "%-5.2f", m.tempo().beats_per_minute());
609 if (bbt_lower_info_label->get_text() != buf) {
610 bbt_lower_info_label->set_text (buf);
612 sprintf (buf, "%g|%g", m.meter().beats_per_bar(), m.meter().note_divisor());
613 if (bbt_upper_info_label->get_text() != buf) {
614 bbt_upper_info_label->set_text (buf);
619 void
620 AudioClock::set_session (Session *s)
622 SessionHandlePtr::set_session (s);
624 if (_session) {
626 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), boost::bind (&AudioClock::session_configuration_changed, this, _1), gui_context());
628 XMLProperty* prop;
629 XMLNode* node = _session->extra_xml (X_("ClockModes"));
630 AudioClock::Mode amode;
632 if (node) {
633 if ((prop = node->property (_name)) != 0) {
634 amode = AudioClock::Mode (string_2_enum (prop->value(), amode));
635 set_mode (amode);
639 set (last_when, true);
643 void
644 AudioClock::focus ()
646 switch (_mode) {
647 case Timecode:
648 _eboxes[Timecode_Hours]->grab_focus ();
649 break;
651 case BBT:
652 _eboxes[Bars]->grab_focus ();
653 break;
655 case MinSec:
656 _eboxes[MS_Hours]->grab_focus ();
657 break;
659 case Frames:
660 _eboxes[AudioFrames]->grab_focus ();
661 break;
663 case Off:
664 break;
669 bool
670 AudioClock::field_key_press_event (GdkEventKey */*ev*/, Field /*field*/)
672 /* all key activity is handled on key release */
673 return true;
676 bool
677 AudioClock::field_key_release_event (GdkEventKey *ev, Field field)
679 Label *label = _labels[field];
680 string new_text;
681 char new_char = 0;
682 bool move_on = false;
684 switch (ev->keyval) {
685 case GDK_0:
686 case GDK_KP_0:
687 new_char = '0';
688 break;
689 case GDK_1:
690 case GDK_KP_1:
691 new_char = '1';
692 break;
693 case GDK_2:
694 case GDK_KP_2:
695 new_char = '2';
696 break;
697 case GDK_3:
698 case GDK_KP_3:
699 new_char = '3';
700 break;
701 case GDK_4:
702 case GDK_KP_4:
703 new_char = '4';
704 break;
705 case GDK_5:
706 case GDK_KP_5:
707 new_char = '5';
708 break;
709 case GDK_6:
710 case GDK_KP_6:
711 new_char = '6';
712 break;
713 case GDK_7:
714 case GDK_KP_7:
715 new_char = '7';
716 break;
717 case GDK_8:
718 case GDK_KP_8:
719 new_char = '8';
720 break;
721 case GDK_9:
722 case GDK_KP_9:
723 new_char = '9';
724 break;
726 case GDK_period:
727 case GDK_KP_Decimal:
728 if (_mode == MinSec && field == MS_Seconds) {
729 new_char = '.';
730 } else {
731 return false;
733 break;
735 case GDK_Tab:
736 case GDK_Return:
737 case GDK_KP_Enter:
738 move_on = true;
739 break;
741 case GDK_Escape:
742 key_entry_state = 0;
743 clock_base.grab_focus ();
744 ChangeAborted(); /* EMIT SIGNAL */
745 return true;
747 default:
748 return false;
751 if (!move_on) {
753 if (key_entry_state == 0) {
755 /* initialize with a fresh new string */
757 if (field != AudioFrames) {
758 for (uint32_t xn = 0; xn < field_length[field] - 1; ++xn) {
759 new_text += '0';
761 } else {
762 new_text = "";
765 } else {
767 string existing = label->get_text();
768 if (existing.length() >= field_length[field]) {
769 new_text = existing.substr (1, field_length[field] - 1);
770 } else {
771 new_text = existing.substr (0, field_length[field] - 1);
775 new_text += new_char;
776 label->set_text (new_text);
777 _canonical_time_is_displayed = true;
778 key_entry_state++;
781 if (key_entry_state == field_length[field]) {
782 move_on = true;
785 if (move_on) {
787 if (key_entry_state) {
789 switch (field) {
790 case Timecode_Hours:
791 case Timecode_Minutes:
792 case Timecode_Seconds:
793 case Timecode_Frames:
794 // Check Timecode fields for sanity (may also adjust fields)
795 timecode_sanitize_display();
796 break;
797 case Bars:
798 case Beats:
799 case Ticks:
800 // Bars should never be, unless this clock is for a duration
801 if (atoi (_labels[Bars]->get_text()) == 0 && !is_duration) {
802 _labels[Bars]->set_text("001");
803 _canonical_time_is_displayed = true;
805 // beats should never be 0, unless this clock is for a duration
806 if (atoi (_labels[Beats]->get_text()) == 0 && !is_duration) {
807 _labels[Beats]->set_text("01");
808 _canonical_time_is_displayed = true;
810 break;
811 default:
812 break;
815 ValueChanged(); /* EMIT_SIGNAL */
818 /* move on to the next field.
821 switch (field) {
823 /* Timecode */
825 case Timecode_Hours:
826 _eboxes[Timecode_Minutes]->grab_focus ();
827 break;
828 case Timecode_Minutes:
829 _eboxes[Timecode_Seconds]->grab_focus ();
830 break;
831 case Timecode_Seconds:
832 _eboxes[Timecode_Frames]->grab_focus ();
833 break;
834 case Timecode_Frames:
835 clock_base.grab_focus ();
836 break;
838 /* audio frames */
839 case AudioFrames:
840 clock_base.grab_focus ();
841 break;
843 /* Min:Sec */
845 case MS_Hours:
846 _eboxes[MS_Minutes]->grab_focus ();
847 break;
848 case MS_Minutes:
849 _eboxes[MS_Seconds]->grab_focus ();
850 break;
851 case MS_Seconds:
852 _eboxes[MS_Milliseconds]->grab_focus ();
853 break;
854 case MS_Milliseconds:
855 clock_base.grab_focus ();
856 break;
858 /* BBT */
860 case Bars:
861 _eboxes[Beats]->grab_focus ();
862 break;
863 case Beats:
864 _eboxes[Ticks]->grab_focus ();
865 break;
866 case Ticks:
867 clock_base.grab_focus ();
868 break;
870 default:
871 break;
876 //if user hit Enter, lose focus
877 switch (ev->keyval) {
878 case GDK_Return:
879 case GDK_KP_Enter:
880 clock_base.grab_focus ();
883 return true;
886 bool
887 AudioClock::field_focus_in_event (GdkEventFocus */*ev*/, Field field)
889 key_entry_state = 0;
891 Keyboard::magic_widget_grab_focus ();
893 _eboxes[field]->set_flags (HAS_FOCUS);
894 _eboxes[field]->set_state (STATE_ACTIVE);
896 return false;
899 bool
900 AudioClock::field_focus_out_event (GdkEventFocus */*ev*/, Field field)
902 _eboxes[field]->unset_flags (HAS_FOCUS);
903 _eboxes[field]->set_state (STATE_NORMAL);
905 Keyboard::magic_widget_drop_focus ();
907 return false;
910 bool
911 AudioClock::field_button_release_event (GdkEventButton *ev, Field field)
913 if (dragging) {
914 gdk_pointer_ungrab (GDK_CURRENT_TIME);
915 dragging = false;
916 if (ev->y > drag_start_y+1 || ev->y < drag_start_y-1 || Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)){
917 // we actually dragged so return without setting editing focus, or we shift clicked
918 return true;
922 if (!editable) {
923 if (ops_menu == 0) {
924 build_ops_menu ();
926 ops_menu->popup (1, ev->time);
927 return true;
930 if (Keyboard::is_context_menu_event (ev)) {
931 if (ops_menu == 0) {
932 build_ops_menu ();
934 ops_menu->popup (1, ev->time);
935 return true;
938 switch (ev->button) {
939 case 1:
940 _eboxes[field]->grab_focus ();
941 break;
943 default:
944 break;
947 return true;
950 bool
951 AudioClock::field_button_press_event (GdkEventButton *ev, Field /*field*/)
953 if (_session == 0) {
954 return false;
957 framepos_t frames = 0;
959 switch (ev->button) {
960 case 1:
961 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
962 set (frames, true);
963 ValueChanged (); /* EMIT_SIGNAL */
966 /* make absolutely sure that the pointer is grabbed */
967 gdk_pointer_grab(ev->window,false ,
968 GdkEventMask( Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK |Gdk::BUTTON_RELEASE_MASK),
969 NULL,NULL,ev->time);
970 dragging = true;
971 drag_accum = 0;
972 drag_start_y = ev->y;
973 drag_y = ev->y;
974 break;
976 case 2:
977 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
978 set (frames, true);
979 ValueChanged (); /* EMIT_SIGNAL */
981 break;
983 case 3:
984 /* used for context sensitive menu */
985 return false;
986 break;
988 default:
989 return false;
990 break;
993 return true;
996 bool
997 AudioClock::field_button_scroll_event (GdkEventScroll *ev, Field field)
999 if (_session == 0) {
1000 return false;
1003 framepos_t frames = 0;
1005 switch (ev->direction) {
1007 case GDK_SCROLL_UP:
1008 frames = get_frames (field);
1009 if (frames != 0) {
1010 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1011 frames *= 10;
1013 set (current_time() + frames, true);
1014 ValueChanged (); /* EMIT_SIGNAL */
1016 break;
1018 case GDK_SCROLL_DOWN:
1019 frames = get_frames (field);
1020 if (frames != 0) {
1021 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1022 frames *= 10;
1025 if ((double)current_time() - (double)frames < 0.0) {
1026 set (0, true);
1027 } else {
1028 set (current_time() - frames, true);
1031 ValueChanged (); /* EMIT_SIGNAL */
1033 break;
1035 default:
1036 return false;
1037 break;
1040 return true;
1043 bool
1044 AudioClock::field_motion_notify_event (GdkEventMotion *ev, Field field)
1046 if (_session == 0 || !dragging) {
1047 return false;
1050 float pixel_frame_scale_factor = 0.2f;
1053 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1054 pixel_frame_scale_factor = 0.1f;
1058 if (Keyboard::modifier_state_contains (ev->state,
1059 Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) {
1061 pixel_frame_scale_factor = 0.025f;
1064 double y_delta = ev->y - drag_y;
1066 drag_accum += y_delta*pixel_frame_scale_factor;
1068 drag_y = ev->y;
1070 if (trunc(drag_accum) != 0) {
1072 framepos_t frames;
1073 framepos_t pos;
1074 int dir;
1075 dir = (drag_accum < 0 ? 1:-1);
1076 pos = current_time();
1077 frames = get_frames (field,pos,dir);
1079 if (frames != 0 && frames * drag_accum < current_time()) {
1081 set ((framepos_t) floor (pos - drag_accum * frames), false); // minus because up is negative in computer-land
1083 } else {
1084 set (0 , false);
1088 drag_accum= 0;
1089 ValueChanged(); /* EMIT_SIGNAL */
1094 return true;
1097 framepos_t
1098 AudioClock::get_frames (Field field, framepos_t pos, int dir)
1100 framecnt_t frames = 0;
1101 Timecode::BBT_Time bbt;
1102 switch (field) {
1103 case Timecode_Hours:
1104 frames = (framecnt_t) floor (3600.0 * _session->frame_rate());
1105 break;
1106 case Timecode_Minutes:
1107 frames = (framecnt_t) floor (60.0 * _session->frame_rate());
1108 break;
1109 case Timecode_Seconds:
1110 frames = _session->frame_rate();
1111 break;
1112 case Timecode_Frames:
1113 frames = (framecnt_t) floor (_session->frame_rate() / _session->timecode_frames_per_second());
1114 break;
1116 case AudioFrames:
1117 frames = 1;
1118 break;
1120 case MS_Hours:
1121 frames = (framecnt_t) floor (3600.0 * _session->frame_rate());
1122 break;
1123 case MS_Minutes:
1124 frames = (framecnt_t) floor (60.0 * _session->frame_rate());
1125 break;
1126 case MS_Seconds:
1127 frames = (framecnt_t) _session->frame_rate();
1128 break;
1129 case MS_Milliseconds:
1130 frames = (framecnt_t) floor (_session->frame_rate() / 1000.0);
1131 break;
1133 case Bars:
1134 bbt.bars = 1;
1135 bbt.beats = 0;
1136 bbt.ticks = 0;
1137 frames = _session->tempo_map().bbt_duration_at(pos,bbt,dir);
1138 break;
1139 case Beats:
1140 bbt.bars = 0;
1141 bbt.beats = 1;
1142 bbt.ticks = 0;
1143 frames = _session->tempo_map().bbt_duration_at(pos,bbt,dir);
1144 break;
1145 case Ticks:
1146 bbt.bars = 0;
1147 bbt.beats = 0;
1148 bbt.ticks = 1;
1149 frames = _session->tempo_map().bbt_duration_at(pos,bbt,dir);
1150 break;
1153 return frames;
1156 framepos_t
1157 AudioClock::current_time (framepos_t pos) const
1159 if (!_canonical_time_is_displayed) {
1160 return _canonical_time;
1163 framepos_t ret = 0;
1165 switch (_mode) {
1166 case Timecode:
1167 ret = timecode_frame_from_display ();
1168 break;
1169 case BBT:
1170 ret = bbt_frame_from_display (pos);
1171 break;
1173 case MinSec:
1174 ret = minsec_frame_from_display ();
1175 break;
1177 case Frames:
1178 ret = audio_frame_from_display ();
1179 break;
1181 case Off:
1182 break;
1185 return ret;
1188 framepos_t
1189 AudioClock::current_duration (framepos_t pos) const
1191 framepos_t ret = 0;
1193 switch (_mode) {
1194 case Timecode:
1195 ret = timecode_frame_from_display ();
1196 break;
1197 case BBT:
1198 ret = bbt_frame_duration_from_display (pos);
1199 break;
1201 case MinSec:
1202 ret = minsec_frame_from_display ();
1203 break;
1205 case Frames:
1206 ret = audio_frame_from_display ();
1207 break;
1209 case Off:
1210 break;
1213 return ret;
1216 void
1217 AudioClock::timecode_sanitize_display()
1219 // Check Timecode fields for sanity, possibly adjusting values
1220 if (atoi (_labels[Timecode_Minutes]->get_text()) > 59) {
1221 _labels[Timecode_Minutes]->set_text("59");
1222 _canonical_time_is_displayed = true;
1225 if (atoi (_labels[Timecode_Seconds]->get_text()) > 59) {
1226 _labels[Timecode_Seconds]->set_text("59");
1227 _canonical_time_is_displayed = true;
1230 switch ((long)rint(_session->timecode_frames_per_second())) {
1231 case 24:
1232 if (atoi (_labels[Timecode_Frames]->get_text()) > 23) {
1233 _labels[Timecode_Frames]->set_text("23");
1234 _canonical_time_is_displayed = true;
1236 break;
1237 case 25:
1238 if (atoi (_labels[Timecode_Frames]->get_text()) > 24) {
1239 _labels[Timecode_Frames]->set_text("24");
1240 _canonical_time_is_displayed = true;
1242 break;
1243 case 30:
1244 if (atoi (_labels[Timecode_Frames]->get_text()) > 29) {
1245 _labels[Timecode_Frames]->set_text("29");
1246 _canonical_time_is_displayed = true;
1248 break;
1249 default:
1250 break;
1253 if (_session->timecode_drop_frames()) {
1254 if ((atoi (_labels[Timecode_Minutes]->get_text()) % 10) && (atoi (_labels[Timecode_Seconds]->get_text()) == 0) && (atoi (_labels[Timecode_Frames]->get_text()) < 2)) {
1255 _labels[Timecode_Frames]->set_text("02");
1256 _canonical_time_is_displayed = true;
1261 /** This is necessary because operator[] isn't const with std::map.
1262 * @param f Field.
1263 * @return Label widget.
1265 Label const *
1266 AudioClock::label (Field f) const
1268 std::map<Field, Label*>::const_iterator i = _labels.find (f);
1269 assert (i != _labels.end ());
1271 return i->second;
1274 framepos_t
1275 AudioClock::timecode_frame_from_display () const
1277 if (_session == 0) {
1278 return 0;
1281 Timecode::Time timecode;
1282 framepos_t sample;
1284 timecode.hours = atoi (label (Timecode_Hours)->get_text());
1285 timecode.minutes = atoi (label (Timecode_Minutes)->get_text());
1286 timecode.seconds = atoi (label (Timecode_Seconds)->get_text());
1287 timecode.frames = atoi (label (Timecode_Frames)->get_text());
1288 timecode.rate = _session->timecode_frames_per_second();
1289 timecode.drop= _session->timecode_drop_frames();
1291 _session->timecode_to_sample (timecode, sample, false /* use_offset */, false /* use_subframes */ );
1294 #if 0
1295 #define Timecode_SAMPLE_TEST_1
1296 #define Timecode_SAMPLE_TEST_2
1297 #define Timecode_SAMPLE_TEST_3
1298 #define Timecode_SAMPLE_TEST_4
1299 #define Timecode_SAMPLE_TEST_5
1300 #define Timecode_SAMPLE_TEST_6
1301 #define Timecode_SAMPLE_TEST_7
1303 // Testcode for timecode<->sample conversions (P.S.)
1304 Timecode::Time timecode1;
1305 framepos_t sample1;
1306 framepos_t oldsample = 0;
1307 Timecode::Time timecode2;
1308 framecnt_t sample_increment;
1310 sample_increment = (framecnt_t)rint(_session->frame_rate() / _session->timecode_frames_per_second);
1312 #ifdef Timecode_SAMPLE_TEST_1
1313 // Test 1: use_offset = false, use_subframes = false
1314 cout << "use_offset = false, use_subframes = false" << endl;
1315 for (int i = 0; i < 108003; i++) {
1316 _session->timecode_to_sample( timecode1, sample1, false /* use_offset */, false /* use_subframes */ );
1317 _session->sample_to_timecode( sample1, timecode2, false /* use_offset */, false /* use_subframes */ );
1319 if ((i > 0) && ( ((sample1 - oldsample) != sample_increment) && ((sample1 - oldsample) != (sample_increment + 1)) && ((sample1 - oldsample) != (sample_increment - 1)))) {
1320 cout << "ERROR: sample increment not right: " << (sample1 - oldsample) << " != " << sample_increment << endl;
1321 cout << "timecode1: " << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> ";
1322 cout << "sample: " << sample1 << endl;
1323 cout << "sample: " << sample1 << " -> ";
1324 cout << "timecode2: " << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl;
1325 break;
1328 if (timecode2.hours != timecode1.hours || timecode2.minutes != timecode1.minutes || timecode2.seconds != timecode2.seconds || timecode2.frames != timecode1.frames) {
1329 cout << "ERROR: timecode2 not equal timecode1" << endl;
1330 cout << "timecode1: " << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> ";
1331 cout << "sample: " << sample1 << endl;
1332 cout << "sample: " << sample1 << " -> ";
1333 cout << "timecode2: " << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl;
1334 break;
1336 oldsample = sample1;
1337 _session->timecode_increment( timecode1 );
1340 cout << "sample_increment: " << sample_increment << endl;
1341 cout << "sample: " << sample1 << " -> ";
1342 cout << "timecode: " << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl;
1343 #endif
1345 #ifdef Timecode_SAMPLE_TEST_2
1346 // Test 2: use_offset = true, use_subframes = false
1347 cout << "use_offset = true, use_subframes = false" << endl;
1349 timecode1.hours = 0;
1350 timecode1.minutes = 0;
1351 timecode1.seconds = 0;
1352 timecode1.frames = 0;
1353 timecode1.subframes = 0;
1354 sample1 = oldsample = 0;
1356 _session->sample_to_timecode( sample1, timecode1, true /* use_offset */, false /* use_subframes */ );
1357 cout << "Starting at sample: " << sample1 << " -> ";
1358 cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << endl;
1360 for (int i = 0; i < 108003; i++) {
1361 _session->timecode_to_sample( timecode1, sample1, true /* use_offset */, false /* use_subframes */ );
1362 _session->sample_to_timecode( sample1, timecode2, true /* use_offset */, false /* use_subframes */ );
1364 // cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> ";
1365 // cout << "sample: " << sample1 << endl;
1366 // cout << "sample: " << sample1 << " -> ";
1367 // cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl;
1369 if ((i > 0) && ( ((sample1 - oldsample) != sample_increment) && ((sample1 - oldsample) != (sample_increment + 1)) && ((sample1 - oldsample) != (sample_increment - 1)))) {
1370 cout << "ERROR: sample increment not right: " << (sample1 - oldsample) << " != " << sample_increment << endl;
1371 cout << "timecode1: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> ";
1372 cout << "sample: " << sample1 << endl;
1373 cout << "sample: " << sample1 << " -> ";
1374 cout << "timecode2: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl;
1375 break;
1378 if (timecode2.hours != timecode1.hours || timecode2.minutes != timecode1.minutes || timecode2.seconds != timecode2.seconds || timecode2.frames != timecode1.frames) {
1379 cout << "ERROR: timecode2 not equal timecode1" << endl;
1380 cout << "timecode1: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> ";
1381 cout << "sample: " << sample1 << endl;
1382 cout << "sample: " << sample1 << " -> ";
1383 cout << "timecode2: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl;
1384 break;
1386 oldsample = sample1;
1387 _session->timecode_increment( timecode1 );
1390 cout << "sample_increment: " << sample_increment << endl;
1391 cout << "sample: " << sample1 << " -> ";
1392 cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl;
1393 #endif
1395 #ifdef Timecode_SAMPLE_TEST_3
1396 // Test 3: use_offset = true, use_subframes = false, decrement
1397 cout << "use_offset = true, use_subframes = false, decrement" << endl;
1399 _session->sample_to_timecode( sample1, timecode1, true /* use_offset */, false /* use_subframes */ );
1400 cout << "Starting at sample: " << sample1 << " -> ";
1401 cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << endl;
1403 for (int i = 0; i < 108003; i++) {
1404 _session->timecode_to_sample( timecode1, sample1, true /* use_offset */, false /* use_subframes */ );
1405 _session->sample_to_timecode( sample1, timecode2, true /* use_offset */, false /* use_subframes */ );
1407 // cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> ";
1408 // cout << "sample: " << sample1 << endl;
1409 // cout << "sample: " << sample1 << " -> ";
1410 // cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl;
1412 if ((i > 0) && ( ((oldsample - sample1) != sample_increment) && ((oldsample - sample1) != (sample_increment + 1)) && ((oldsample - sample1) != (sample_increment - 1)))) {
1413 cout << "ERROR: sample increment not right: " << (oldsample - sample1) << " != " << sample_increment << endl;
1414 cout << "timecode1: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> ";
1415 cout << "sample: " << sample1 << endl;
1416 cout << "sample: " << sample1 << " -> ";
1417 cout << "timecode2: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl;
1418 break;
1421 if (timecode2.hours != timecode1.hours || timecode2.minutes != timecode1.minutes || timecode2.seconds != timecode2.seconds || timecode2.frames != timecode1.frames) {
1422 cout << "ERROR: timecode2 not equal timecode1" << endl;
1423 cout << "timecode1: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> ";
1424 cout << "sample: " << sample1 << endl;
1425 cout << "sample: " << sample1 << " -> ";
1426 cout << "timecode2: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl;
1427 break;
1429 oldsample = sample1;
1430 _session->timecode_decrement( timecode1 );
1433 cout << "sample_decrement: " << sample_increment << endl;
1434 cout << "sample: " << sample1 << " -> ";
1435 cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl;
1436 #endif
1439 #ifdef Timecode_SAMPLE_TEST_4
1440 // Test 4: use_offset = true, use_subframes = true
1441 cout << "use_offset = true, use_subframes = true" << endl;
1443 for (long sub = 5; sub < 80; sub += 5) {
1444 timecode1.hours = 0;
1445 timecode1.minutes = 0;
1446 timecode1.seconds = 0;
1447 timecode1.frames = 0;
1448 timecode1.subframes = 0;
1449 sample1 = oldsample = (sample_increment * sub) / 80;
1451 _session->sample_to_timecode( sample1, timecode1, true /* use_offset */, true /* use_subframes */ );
1453 cout << "starting at sample: " << sample1 << " -> ";
1454 cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << endl;
1456 for (int i = 0; i < 108003; i++) {
1457 _session->timecode_to_sample( timecode1, sample1, true /* use_offset */, true /* use_subframes */ );
1458 _session->sample_to_timecode( sample1, timecode2, true /* use_offset */, true /* use_subframes */ );
1460 if ((i > 0) && ( ((sample1 - oldsample) != sample_increment) && ((sample1 - oldsample) != (sample_increment + 1)) && ((sample1 - oldsample) != (sample_increment - 1)))) {
1461 cout << "ERROR: sample increment not right: " << (sample1 - oldsample) << " != " << sample_increment << endl;
1462 cout << "timecode1: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> ";
1463 cout << "sample: " << sample1 << endl;
1464 cout << "sample: " << sample1 << " -> ";
1465 cout << "timecode2: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl;
1466 //break;
1469 if (timecode2.hours != timecode1.hours || timecode2.minutes != timecode1.minutes || timecode2.seconds != timecode2.seconds || timecode2.frames != timecode1.frames || timecode2.subframes != timecode1.subframes) {
1470 cout << "ERROR: timecode2 not equal timecode1" << endl;
1471 cout << "timecode1: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> ";
1472 cout << "sample: " << sample1 << endl;
1473 cout << "sample: " << sample1 << " -> ";
1474 cout << "timecode2: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl;
1475 break;
1477 oldsample = sample1;
1478 _session->timecode_increment( timecode1 );
1481 cout << "sample_increment: " << sample_increment << endl;
1482 cout << "sample: " << sample1 << " -> ";
1483 cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl;
1485 for (int i = 0; i < 108003; i++) {
1486 _session->timecode_to_sample( timecode1, sample1, true /* use_offset */, true /* use_subframes */ );
1487 _session->sample_to_timecode( sample1, timecode2, true /* use_offset */, true /* use_subframes */ );
1489 if ((i > 0) && ( ((oldsample - sample1) != sample_increment) && ((oldsample - sample1) != (sample_increment + 1)) && ((oldsample - sample1) != (sample_increment - 1)))) {
1490 cout << "ERROR: sample increment not right: " << (oldsample - sample1) << " != " << sample_increment << endl;
1491 cout << "timecode1: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> ";
1492 cout << "sample: " << sample1 << endl;
1493 cout << "sample: " << sample1 << " -> ";
1494 cout << "timecode2: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl;
1495 //break;
1498 if (timecode2.hours != timecode1.hours || timecode2.minutes != timecode1.minutes || timecode2.seconds != timecode2.seconds || timecode2.frames != timecode1.frames || timecode2.subframes != timecode1.subframes) {
1499 cout << "ERROR: timecode2 not equal timecode1" << endl;
1500 cout << "timecode1: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> ";
1501 cout << "sample: " << sample1 << endl;
1502 cout << "sample: " << sample1 << " -> ";
1503 cout << "timecode2: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl;
1504 break;
1506 oldsample = sample1;
1507 _session->timecode_decrement( timecode1 );
1510 cout << "sample_decrement: " << sample_increment << endl;
1511 cout << "sample: " << sample1 << " -> ";
1512 cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl;
1514 #endif
1517 #ifdef Timecode_SAMPLE_TEST_5
1518 // Test 5: use_offset = true, use_subframes = false, increment seconds
1519 cout << "use_offset = true, use_subframes = false, increment seconds" << endl;
1521 timecode1.hours = 0;
1522 timecode1.minutes = 0;
1523 timecode1.seconds = 0;
1524 timecode1.frames = 0;
1525 timecode1.subframes = 0;
1526 sample1 = oldsample = 0;
1527 sample_increment = _session->frame_rate();
1529 _session->sample_to_timecode( sample1, timecode1, true /* use_offset */, false /* use_subframes */ );
1530 cout << "Starting at sample: " << sample1 << " -> ";
1531 cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << endl;
1533 for (int i = 0; i < 3600; i++) {
1534 _session->timecode_to_sample( timecode1, sample1, true /* use_offset */, false /* use_subframes */ );
1535 _session->sample_to_timecode( sample1, timecode2, true /* use_offset */, false /* use_subframes */ );
1537 // cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> ";
1538 // cout << "sample: " << sample1 << endl;
1539 // cout << "sample: " << sample1 << " -> ";
1540 // cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl;
1542 // if ((i > 0) && ( ((sample1 - oldsample) != sample_increment) && ((sample1 - oldsample) != (sample_increment + 1)) && ((sample1 - oldsample) != (sample_increment - 1))))
1543 // {
1544 // cout << "ERROR: sample increment not right: " << (sample1 - oldsample) << " != " << sample_increment << endl;
1545 // break;
1546 // }
1548 if (timecode2.hours != timecode1.hours || timecode2.minutes != timecode1.minutes || timecode2.seconds != timecode2.seconds || timecode2.frames != timecode1.frames) {
1549 cout << "ERROR: timecode2 not equal timecode1" << endl;
1550 cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> ";
1551 cout << "sample: " << sample1 << endl;
1552 cout << "sample: " << sample1 << " -> ";
1553 cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl;
1554 break;
1556 oldsample = sample1;
1557 _session->timecode_increment_seconds( timecode1 );
1560 cout << "sample_increment: " << sample_increment << endl;
1561 cout << "sample: " << sample1 << " -> ";
1562 cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl;
1563 #endif
1566 #ifdef Timecode_SAMPLE_TEST_6
1567 // Test 6: use_offset = true, use_subframes = false, increment minutes
1568 cout << "use_offset = true, use_subframes = false, increment minutes" << endl;
1570 timecode1.hours = 0;
1571 timecode1.minutes = 0;
1572 timecode1.seconds = 0;
1573 timecode1.frames = 0;
1574 timecode1.subframes = 0;
1575 sample1 = oldsample = 0;
1576 sample_increment = _session->frame_rate() * 60;
1578 _session->sample_to_timecode( sample1, timecode1, true /* use_offset */, false /* use_subframes */ );
1579 cout << "Starting at sample: " << sample1 << " -> ";
1580 cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << endl;
1582 for (int i = 0; i < 60; i++) {
1583 _session->timecode_to_sample( timecode1, sample1, true /* use_offset */, false /* use_subframes */ );
1584 _session->sample_to_timecode( sample1, timecode2, true /* use_offset */, false /* use_subframes */ );
1586 // cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> ";
1587 // cout << "sample: " << sample1 << endl;
1588 // cout << "sample: " << sample1 << " -> ";
1589 // cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl;
1591 // if ((i > 0) && ( ((sample1 - oldsample) != sample_increment) && ((sample1 - oldsample) != (sample_increment + 1)) && ((sample1 - oldsample) != (sample_increment - 1))))
1592 // {
1593 // cout << "ERROR: sample increment not right: " << (sample1 - oldsample) << " != " << sample_increment << endl;
1594 // break;
1595 // }
1597 if (timecode2.hours != timecode1.hours || timecode2.minutes != timecode1.minutes || timecode2.seconds != timecode2.seconds || timecode2.frames != timecode1.frames) {
1598 cout << "ERROR: timecode2 not equal timecode1" << endl;
1599 cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> ";
1600 cout << "sample: " << sample1 << endl;
1601 cout << "sample: " << sample1 << " -> ";
1602 cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl;
1603 break;
1605 oldsample = sample1;
1606 _session->timecode_increment_minutes( timecode1 );
1609 cout << "sample_increment: " << sample_increment << endl;
1610 cout << "sample: " << sample1 << " -> ";
1611 cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl;
1612 #endif
1614 #ifdef Timecode_SAMPLE_TEST_7
1615 // Test 7: use_offset = true, use_subframes = false, increment hours
1616 cout << "use_offset = true, use_subframes = false, increment hours" << endl;
1618 timecode1.hours = 0;
1619 timecode1.minutes = 0;
1620 timecode1.seconds = 0;
1621 timecode1.frames = 0;
1622 timecode1.subframes = 0;
1623 sample1 = oldsample = 0;
1624 sample_increment = _session->frame_rate() * 60 * 60;
1626 _session->sample_to_timecode( sample1, timecode1, true /* use_offset */, false /* use_subframes */ );
1627 cout << "Starting at sample: " << sample1 << " -> ";
1628 cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << endl;
1630 for (int i = 0; i < 10; i++) {
1631 _session->timecode_to_sample( timecode1, sample1, true /* use_offset */, false /* use_subframes */ );
1632 _session->sample_to_timecode( sample1, timecode2, true /* use_offset */, false /* use_subframes */ );
1634 // cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> ";
1635 // cout << "sample: " << sample1 << endl;
1636 // cout << "sample: " << sample1 << " -> ";
1637 // cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl;
1639 // if ((i > 0) && ( ((sample1 - oldsample) != sample_increment) && ((sample1 - oldsample) != (sample_increment + 1)) && ((sample1 - oldsample) != (sample_increment - 1))))
1640 // {
1641 // cout << "ERROR: sample increment not right: " << (sample1 - oldsample) << " != " << sample_increment << endl;
1642 // break;
1643 // }
1645 if (timecode2.hours != timecode1.hours || timecode2.minutes != timecode1.minutes || timecode2.seconds != timecode2.seconds || timecode2.frames != timecode1.frames) {
1646 cout << "ERROR: timecode2 not equal timecode1" << endl;
1647 cout << "timecode: " << (timecode1.negative ? "-" : "") << timecode1.hours << ":" << timecode1.minutes << ":" << timecode1.seconds << ":" << timecode1.frames << "::" << timecode1.subframes << " -> ";
1648 cout << "sample: " << sample1 << endl;
1649 cout << "sample: " << sample1 << " -> ";
1650 cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl;
1651 break;
1653 oldsample = sample1;
1654 _session->timecode_increment_hours( timecode1 );
1657 cout << "sample_increment: " << sample_increment << endl;
1658 cout << "sample: " << sample1 << " -> ";
1659 cout << "timecode: " << (timecode2.negative ? "-" : "") << timecode2.hours << ":" << timecode2.minutes << ":" << timecode2.seconds << ":" << timecode2.frames << "::" << timecode2.subframes << endl;
1660 #endif
1662 #endif
1664 return sample;
1667 framepos_t
1668 AudioClock::minsec_frame_from_display () const
1670 if (_session == 0) {
1671 return 0;
1674 int hrs = atoi (label (MS_Hours)->get_text());
1675 int mins = atoi (label (MS_Minutes)->get_text());
1676 int secs = atoi (label (MS_Seconds)->get_text());
1677 int millisecs = atoi (label (MS_Milliseconds)->get_text());
1679 framecnt_t sr = _session->frame_rate();
1681 return (framepos_t) floor ((hrs * 60.0f * 60.0f * sr) + (mins * 60.0f * sr) + (secs * sr) + (millisecs * sr / 1000.0));
1684 framepos_t
1685 AudioClock::bbt_frame_from_display (framepos_t pos) const
1687 if (_session == 0) {
1688 error << "AudioClock::current_time() called with BBT mode but without session!" << endmsg;
1689 return 0;
1692 AnyTime any;
1693 any.type = AnyTime::BBT;
1695 any.bbt.bars = atoi (label (Bars)->get_text());
1696 any.bbt.beats = atoi (label (Beats)->get_text());
1697 any.bbt.ticks = atoi (label (Ticks)->get_text());
1699 if (is_duration) {
1700 any.bbt.bars++;
1701 any.bbt.beats++;
1702 return _session->any_duration_to_frames (pos, any);
1703 } else {
1704 return _session->convert_to_frames (any);
1709 framepos_t
1710 AudioClock::bbt_frame_duration_from_display (framepos_t pos) const
1712 if (_session == 0) {
1713 error << "AudioClock::current_time() called with BBT mode but without session!" << endmsg;
1714 return 0;
1717 Timecode::BBT_Time bbt;
1720 bbt.bars = atoi (label (Bars)->get_text());
1721 bbt.beats = atoi (label (Beats)->get_text());
1722 bbt.ticks = atoi (label (Ticks)->get_text());
1724 return _session->tempo_map().bbt_duration_at(pos,bbt,1);
1727 framepos_t
1728 AudioClock::audio_frame_from_display () const
1730 return (framepos_t) atoi (label (AudioFrames)->get_text ());
1733 void
1734 AudioClock::build_ops_menu ()
1736 using namespace Menu_Helpers;
1737 ops_menu = new Menu;
1738 MenuList& ops_items = ops_menu->items();
1739 ops_menu->set_name ("ArdourContextMenu");
1741 if (!Profile->get_sae()) {
1742 ops_items.push_back (MenuElem (_("Timecode"), sigc::bind (sigc::mem_fun(*this, &AudioClock::set_mode), Timecode)));
1744 ops_items.push_back (MenuElem (_("Bars:Beats"), sigc::bind (sigc::mem_fun(*this, &AudioClock::set_mode), BBT)));
1745 ops_items.push_back (MenuElem (_("Minutes:Seconds"), sigc::bind (sigc::mem_fun(*this, &AudioClock::set_mode), MinSec)));
1746 ops_items.push_back (MenuElem (_("Samples"), sigc::bind (sigc::mem_fun(*this, &AudioClock::set_mode), Frames)));
1747 ops_items.push_back (MenuElem (_("Off"), sigc::bind (sigc::mem_fun(*this, &AudioClock::set_mode), Off)));
1749 if (editable && !is_duration && !_follows_playhead) {
1750 ops_items.push_back (SeparatorElem());
1751 ops_items.push_back (MenuElem (_("Set From Playhead"), sigc::mem_fun(*this, &AudioClock::set_from_playhead)));
1752 ops_items.push_back (MenuElem (_("Locate to This Time"), sigc::mem_fun(*this, &AudioClock::locate)));
1756 void
1757 AudioClock::set_from_playhead ()
1759 if (!_session) {
1760 return;
1763 set (_session->transport_frame());
1764 ValueChanged ();
1767 void
1768 AudioClock::locate ()
1770 if (!_session || is_duration) {
1771 return;
1774 _session->request_locate (current_time(), _session->transport_rolling ());
1777 void
1778 AudioClock::set_mode (Mode m)
1780 /* slightly tricky: this is called from within the ARDOUR_UI
1781 constructor by some of its clock members. at that time
1782 the instance pointer is unset, so we have to be careful.
1783 the main idea is to drop keyboard focus in case we had
1784 started editing the clock and then we switch clock mode.
1787 clock_base.grab_focus ();
1789 if (_mode == m) {
1790 return;
1793 clock_base.remove ();
1795 _mode = m;
1797 switch (_mode) {
1798 case Timecode:
1799 clock_base.add (timecode_packer_hbox);
1800 break;
1802 case BBT:
1803 clock_base.add (bbt_packer_hbox);
1804 break;
1806 case MinSec:
1807 clock_base.add (minsec_packer_hbox);
1808 break;
1810 case Frames:
1811 clock_base.add (frames_packer_hbox);
1812 break;
1814 case Off:
1815 clock_base.add (off_hbox);
1816 break;
1819 set_size_requests ();
1821 set (last_when, true);
1822 clock_base.show_all ();
1823 key_entry_state = 0;
1825 if (!is_transient) {
1826 ModeChanged (); /* EMIT SIGNAL (the static one)*/
1829 mode_changed (); /* EMIT SIGNAL (the member one) */
1832 void
1833 AudioClock::set_size_requests ()
1835 /* note that in some fonts, "88" is narrower than "00" */
1837 switch (_mode) {
1838 case Timecode:
1839 Gtkmm2ext::set_size_request_to_display_given_text (*_labels[Timecode_Hours], "-88", 5, 5);
1840 Gtkmm2ext::set_size_request_to_display_given_text (*_labels[Timecode_Minutes], "88", 5, 5);
1841 Gtkmm2ext::set_size_request_to_display_given_text (*_labels[Timecode_Seconds], "88", 5, 5);
1842 Gtkmm2ext::set_size_request_to_display_given_text (*_labels[Timecode_Frames], "88", 5, 5);
1843 break;
1845 case BBT:
1846 Gtkmm2ext::set_size_request_to_display_given_text (*_labels[Bars], "-888", 5, 5);
1847 Gtkmm2ext::set_size_request_to_display_given_text (*_labels[Beats], "88", 5, 5);
1848 Gtkmm2ext::set_size_request_to_display_given_text (*_labels[Ticks], "8888", 5, 5);
1849 break;
1851 case MinSec:
1852 Gtkmm2ext::set_size_request_to_display_given_text (*_labels[MS_Hours], "88", 5, 5);
1853 Gtkmm2ext::set_size_request_to_display_given_text (*_labels[MS_Minutes], "88", 5, 5);
1854 Gtkmm2ext::set_size_request_to_display_given_text (*_labels[MS_Seconds], "88", 5, 5);
1855 Gtkmm2ext::set_size_request_to_display_given_text (*_labels[MS_Milliseconds], "888", 5, 5);
1856 break;
1858 case Frames:
1859 Gtkmm2ext::set_size_request_to_display_given_text (*_labels[AudioFrames], "8888888888", 5, 5);
1860 break;
1862 case Off:
1863 Gtkmm2ext::set_size_request_to_display_given_text (off_hbox, "00000", 5, 5);
1864 break;
1869 void
1870 AudioClock::set_bbt_reference (framepos_t pos)
1872 bbt_reference_time = pos;
1875 void
1876 AudioClock::on_style_changed (const Glib::RefPtr<Gtk::Style>& old_style)
1878 HBox::on_style_changed (old_style);
1880 /* propagate style changes to all component widgets that should inherit the main one */
1882 Glib::RefPtr<RcStyle> rcstyle = get_modifier_style();
1884 clock_base.modify_style (rcstyle);
1886 for (std::map<Field, Label*>::iterator i = _labels.begin(); i != _labels.end(); ++i) {
1887 i->second->modify_style (rcstyle);
1890 for (std::map<Field, EventBox*>::iterator i = _eboxes.begin(); i != _eboxes.end(); ++i) {
1891 i->second->modify_style (rcstyle);
1894 colon1.modify_style (rcstyle);
1895 colon2.modify_style (rcstyle);
1896 colon3.modify_style (rcstyle);
1897 colon4.modify_style (rcstyle);
1898 colon5.modify_style (rcstyle);
1899 b1.modify_style (rcstyle);
1900 b2.modify_style (rcstyle);
1901 period1.modify_style (rcstyle);
1903 set_size_requests ();
1906 void
1907 AudioClock::set_is_duration (bool yn)
1909 if (yn == is_duration) {
1910 return;
1913 is_duration = yn;
1914 set (last_when, true, 0, 's');