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
23 #include "pbd/convert.h"
24 #include "pbd/enumwriter.h"
26 #include <gtkmm/style.h>
28 #include "gtkmm2ext/cairocell.h"
29 #include "gtkmm2ext/utils.h"
30 #include "gtkmm2ext/rgb_macros.h"
32 #include "ardour/ardour.h"
33 #include "ardour/session.h"
34 #include "ardour/tempo.h"
35 #include "ardour/profile.h"
36 #include <sigc++/bind.h>
38 #include "ardour_ui.h"
39 #include "audio_clock.h"
42 #include "gui_thread.h"
45 using namespace ARDOUR;
50 using Gtkmm2ext::Keyboard;
52 sigc::signal<void> AudioClock::ModeChanged;
53 vector<AudioClock*> AudioClock::clocks;
54 const double AudioClock::info_font_scale_factor = 0.6;
55 const double AudioClock::separator_height = 2.0;
57 AudioClock::AudioClock (const string& clock_name, bool transient, const string& widget_name,
58 bool allow_edit, bool follows_playhead, bool duration, bool with_info)
60 , is_transient (transient)
61 , is_duration (duration)
62 , editable (allow_edit)
63 , _follows_playhead (follows_playhead)
70 , mode_based_info_ratio (1.0)
72 , bbt_reference_time (-1)
77 , drag_field (Field (0))
78 , _canonical_time_is_displayed (true)
82 set_flags (CAN_FOCUS);
84 _layout = Pango::Layout::create (get_pango_context());
85 _layout->set_attributes (normal_attributes);
88 _left_layout = Pango::Layout::create (get_pango_context());
89 _right_layout = Pango::Layout::create (get_pango_context());
92 set_widget_name (widget_name);
94 _mode = BBT; /* lie to force mode switch */
96 set (last_when, true);
99 clocks.push_back (this);
103 AudioClock::~AudioClock ()
105 delete background_attr;
106 delete foreground_attr;
111 AudioClock::set_widget_name (const string& str)
116 set_name (str + " clock");
122 AudioClock::on_realize ()
124 CairoWidget::on_realize ();
130 AudioClock::set_font ()
132 Glib::RefPtr<Gtk::Style> style = get_style ();
133 Pango::FontDescription font;
134 Pango::AttrFontDesc* font_attr;
136 if (!is_realized()) {
137 font = get_font_for_style (get_name());
139 font = style->get_font();
142 font_attr = new Pango::AttrFontDesc (Pango::Attribute::create_attr_font_desc (font));
144 normal_attributes.change (*font_attr);
145 editing_attributes.change (*font_attr);
147 /* now a smaller version of the same font */
150 font.set_size ((int) lrint (font.get_size() * info_font_scale_factor));
151 font.set_weight (Pango::WEIGHT_NORMAL);
152 font_attr = new Pango::AttrFontDesc (Pango::Attribute::create_attr_font_desc (font));
154 info_attributes.change (*font_attr);
160 AudioClock::set_active_state (Gtkmm2ext::ActiveState s)
162 CairoWidget::set_active_state (s);
167 AudioClock::set_colors ()
173 uint32_t editing_color;
175 if (active_state()) {
176 bg_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1 active: background", get_name()));
177 text_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1 active: text", get_name()));
178 editing_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1 active: edited text", get_name()));
180 bg_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: background", get_name()));
181 text_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: text", get_name()));
182 editing_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: edited text", get_name()));
185 UINT_TO_RGBA (bg_color, &r, &g, &b, &a);
186 r = lrint ((r/256.0) * 65535.0);
187 g = lrint ((g/256.0) * 65535.0);
188 b = lrint ((b/256.0) * 65535.0);
189 background_attr = new Pango::AttrColor (Pango::Attribute::create_attr_background (r, g, b));
191 /* store for bg in ::render() */
197 UINT_TO_RGBA (text_color, &r, &g, &b, &a);
198 r = lrint ((r/256.0) * 65535.0);
199 g = lrint ((g/256.0) * 65535.0);
200 b = lrint ((b/256.0) * 65535.0);
201 foreground_attr = new Pango::AttrColor (Pango::Attribute::create_attr_foreground (r, g, b));
203 UINT_TO_RGBA (editing_color, &r, &g, &b, &a);
204 r = lrint ((r/256.0) * 65535.0);
205 g = lrint ((g/256.0) * 65535.0);
206 b = lrint ((b/256.0) * 65535.0);
207 editing_attr = new Pango::AttrColor (Pango::Attribute::create_attr_foreground (r, g, b));
209 normal_attributes.change (*background_attr);
210 normal_attributes.change (*foreground_attr);
212 info_attributes.change (*background_attr);
213 info_attributes.change (*foreground_attr);
215 editing_attributes.change (*background_attr);
216 editing_attributes.change (*foreground_attr);
217 editing_attributes.change (*editing_attr);
220 _layout->set_attributes (normal_attributes);
222 _layout->set_attributes (editing_attributes);
226 _left_layout->set_attributes (info_attributes);
227 _right_layout->set_attributes (info_attributes);
234 AudioClock::render (cairo_t* cr)
237 /* paint entire area the color of the parent window bg
239 XXX try to optimize this so that we just paint the corners and
240 other areas that may be exposed by rounded corners.
243 Gdk::Color bg (get_parent_bg());
244 cairo_rectangle (cr, 0, 0, _width, _height);
245 cairo_stroke_preserve (cr);
246 cairo_set_source_rgb (cr, bg.get_red_p(), bg.get_green_p(), bg.get_blue_p());
250 /* main layout: rounded rect, plus the text */
252 cairo_set_source_rgba (cr, bg_r, bg_g, bg_b, bg_a);
253 Gtkmm2ext::rounded_rectangle (cr, 0, 0, _width, upper_height, 9);
255 cairo_move_to (cr, 6, (upper_height - layout_height) / 2.0);
256 pango_cairo_show_layout (cr, _layout->gobj());
260 double h = _height - upper_height - separator_height;
262 if (mode_based_info_ratio != 1.0) {
264 double left_rect_width = round (((_width - separator_height) * mode_based_info_ratio) + 0.5);
266 cairo_set_source_rgba (cr, bg_r, bg_g, bg_b, bg_a);
267 Gtkmm2ext::rounded_rectangle (cr, 0, upper_height + separator_height, left_rect_width, h, 9);
269 cairo_move_to (cr, 6, upper_height + separator_height + ((h - info_height)/2.0));
270 pango_cairo_show_layout (cr, _left_layout->gobj());
272 Gtkmm2ext::rounded_rectangle (cr, left_rect_width + separator_height, upper_height + separator_height,
273 _width - separator_height - left_rect_width, h, 9);
275 cairo_move_to (cr, 6 + left_rect_width + separator_height, upper_height + separator_height + ((h - info_height)/2.0));
276 pango_cairo_show_layout (cr, _right_layout->gobj());
279 /* no info to display, or just one */
281 cairo_set_source_rgba (cr, bg_r, bg_g, bg_b, bg_a);
282 Gtkmm2ext::rounded_rectangle (cr, 0, upper_height + separator_height, _width, h, 9);
288 AudioClock::on_size_allocate (Gtk::Allocation& alloc)
290 CairoWidget::on_size_allocate (alloc);
293 upper_height = (_height/2.0) - 1.0;
295 upper_height = _height;
300 AudioClock::on_size_request (Gtk::Requisition* req)
302 Glib::RefPtr<Pango::Layout> tmp;
303 Glib::RefPtr<Gtk::Style> style = get_style ();
304 Pango::FontDescription font;
306 tmp = Pango::Layout::create (get_pango_context());
308 if (!is_realized()) {
309 font = get_font_for_style (get_name());
311 font = style->get_font();
314 tmp->set_font_description (font);
316 /* this string is the longest thing we will ever display,
317 and also includes the BBT "|" that may descends below
318 the baseline a bit, and a comma for the minsecs mode
319 where we printf a fractional value (XXX or should)
322 tmp->set_text (" 88|88:88:88,88");
324 tmp->get_pixel_size (req->width, req->height);
326 layout_height = req->height;
327 layout_width = req->width;
329 /* now tackle height, for which we need to know the height of the lower
337 font.set_size ((int) lrint (font.get_size() * info_font_scale_factor));
338 font.set_weight (Pango::WEIGHT_NORMAL);
339 tmp->set_font_description (font);
341 /* we only care about height, so put as much stuff in here
342 as possible that might change the height.
344 tmp->set_text ("qyhH|"); /* one ascender, one descender */
346 tmp->get_pixel_size (w, info_height);
350 req->height += info_height;
351 req->height += separator_height;
356 AudioClock::show_edit_status (int length)
358 editing_attr->set_start_index (edit_string.length() - length);
359 editing_attr->set_end_index (edit_string.length());
361 editing_attributes.change (*background_attr);
362 editing_attributes.change (*foreground_attr);
363 editing_attributes.change (*editing_attr);
365 _layout->set_attributes (editing_attributes);
369 AudioClock::start_edit ()
371 edit_string = _layout->get_text ();
372 pre_edit_string = edit_string;
373 input_string.clear ();
376 show_edit_status (1);
379 Keyboard::magic_widget_grab_focus ();
384 AudioClock::end_edit (bool modify)
392 ok = timecode_validate_edit (edit_string);
396 ok = bbt_validate_edit (edit_string);
407 edit_string = pre_edit_string;
408 input_string.clear ();
409 _layout->set_text (edit_string);
412 _layout->set_attributes (normal_attributes);
413 ValueChanged(); /* EMIT_SIGNAL */
418 _layout->set_attributes (normal_attributes);
419 _layout->set_text (pre_edit_string);
426 /* move focus back to the default widget in the top level window */
428 Keyboard::magic_widget_drop_focus ();
430 Widget* top = get_toplevel();
432 if (top->is_toplevel ()) {
433 Window* win = dynamic_cast<Window*> (top);
440 AudioClock::parse_as_frames_distance (const std::string& str)
444 if (sscanf (str.c_str(), "%" PRId64, &f) == 1) {
452 AudioClock::parse_as_minsec_distance (const std::string& str)
458 AudioClock::parse_as_timecode_distance (const std::string& str)
464 AudioClock::parse_as_bbt_distance (const std::string& str)
470 AudioClock::parse_as_distance (const std::string& str)
474 return parse_as_timecode_distance (str);
477 return parse_as_frames_distance (str);
480 return parse_as_bbt_distance (str);
483 return parse_as_minsec_distance (str);
490 AudioClock::end_edit_relative (bool add)
492 framecnt_t frames = parse_as_distance (input_string);
498 set (current_time() + frames, true);
500 framepos_t c = current_time();
503 set (c - frames, true);
510 /* move focus back to the default widget in the top level window */
512 Keyboard::magic_widget_drop_focus ();
514 Widget* top = get_toplevel();
516 if (top->is_toplevel ()) {
517 Window* win = dynamic_cast<Window*> (top);
523 AudioClock::session_configuration_changed (std::string p)
525 if (p != "timecode-offset" && p != "timecode-offset-negative") {
534 current = current_duration ();
536 current = current_time ();
546 AudioClock::set (framepos_t when, bool force, framecnt_t offset, char which)
548 if ((!force && !is_visible()) || _session == 0) {
552 bool const pdelta = Config->get_primary_clock_delta_edit_cursor ();
553 bool const sdelta = Config->get_secondary_clock_delta_edit_cursor ();
555 if (offset && which == 'p' && pdelta) {
556 when = (when > offset) ? when - offset : offset - when;
557 } else if (offset && which == 's' && sdelta) {
558 when = (when > offset) ? when - offset : offset - when;
561 if (when == last_when && !force) {
565 if (which == 'p' && pdelta && !last_pdelta) {
566 set_name("TransportClockDisplayDelta");
568 } else if (which == 'p' && !pdelta && last_pdelta) {
569 set_name("TransportClockDisplay");
571 } else if (which == 's' && sdelta && !last_sdelta) {
572 set_name("SecondaryClockDisplayDelta");
574 } else if (which == 's' && !sdelta && last_sdelta) {
575 set_name("SecondaryClockDisplay");
583 set_timecode (when, force);
587 set_bbt (when, force);
591 set_minsec (when, force);
595 set_frames (when, force);
600 if (when != last_when || force) {
606 /* we're setting the time from a frames value, so keep it as the canonical value */
607 _canonical_time = when;
608 _canonical_time_is_displayed = false;
612 AudioClock::set_frames (framepos_t when, bool /*force*/)
617 /* XXX with cairo, we can do better, surely? */
619 _layout->set_text ("-----------");
622 _left_layout->set_text ("");
623 _right_layout->set_text ("");
629 snprintf (buf, sizeof (buf), "%10" PRId64, when);
630 _layout->set_text (buf);
633 framecnt_t rate = _session->frame_rate();
635 if (fmod (rate, 1000.0) == 0.000) {
636 sprintf (buf, "%" PRId64 "K", rate/1000);
638 sprintf (buf, "%" PRId64, rate);
641 _left_layout->set_text (buf);
643 float vid_pullup = _session->config.get_video_pullup();
645 if (vid_pullup == 0.0) {
646 _right_layout->set_text (_("none"));
648 sprintf (buf, "%-6.4f", vid_pullup);
649 _right_layout->set_text (buf);
655 AudioClock::set_minsec (framepos_t when, bool force)
665 _layout->set_text ("--:--:--");
668 _left_layout->set_text ("");
669 _right_layout->set_text ("");
676 hrs = (int) floor (left / (_session->frame_rate() * 60.0f * 60.0f));
677 left -= (framecnt_t) floor (hrs * _session->frame_rate() * 60.0f * 60.0f);
678 mins = (int) floor (left / (_session->frame_rate() * 60.0f));
679 left -= (framecnt_t) floor (mins * _session->frame_rate() * 60.0f);
680 secs = (int) floor (left / (float) _session->frame_rate());
681 left -= (framecnt_t) floor (secs * _session->frame_rate());
682 millisecs = floor (left * 1000.0 / (float) _session->frame_rate());
684 snprintf (buf, sizeof (buf), "%02" PRIu32 ":%02" PRIu32 ":%02" PRIu32 ".%03" PRIu32, hrs, mins, secs, millisecs);
685 _layout->set_text (buf);
689 AudioClock::set_timecode (framepos_t when, bool force)
695 _layout->set_text ("--:--:--:--");
697 _left_layout->set_text ("");
698 _right_layout->set_text ("");
705 _session->timecode_duration (when, TC);
707 _session->timecode_time (when, TC);
711 snprintf (buf, sizeof (buf), "-%02" PRIu32 ":%02" PRIu32 ":%02" PRIu32 ":%02" PRIu32, TC.hours, TC.minutes, TC.seconds, TC.frames);
713 snprintf (buf, sizeof (buf), " %02" PRIu32 ":%02" PRIu32 ":%02" PRIu32 ":%02" PRIu32, TC.hours, TC.minutes, TC.seconds, TC.frames);
716 _layout->set_text (buf);
719 double timecode_frames = _session->timecode_frames_per_second();
721 if (fmod(timecode_frames, 1.0) == 0.0) {
722 sprintf (buf, "FPS %u %s", int (timecode_frames), (_session->timecode_drop_frames() ? "D" : ""));
724 sprintf (buf, "%.2f %s", timecode_frames, (_session->timecode_drop_frames() ? "D" : ""));
727 _right_layout->set_text (buf);
732 AudioClock::set_bbt (framepos_t when, bool force)
735 Timecode::BBT_Time BBT;
738 _layout->set_text ("--|--|--");
740 _left_layout->set_text ("");
741 _right_layout->set_text ("");
746 /* handle a common case */
753 _session->tempo_map().bbt_time (when, BBT);
758 _session->tempo_map().bbt_time (when, BBT);
761 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%04" PRIu32, BBT.bars, BBT.beats, BBT.ticks);
762 _layout->set_text (buf);
767 if (bbt_reference_time < 0) {
770 pos = bbt_reference_time;
773 TempoMetric m (_session->tempo_map().metric_at (pos));
775 sprintf (buf, "%-5.2f", m.tempo().beats_per_minute());
776 _left_layout->set_text (buf);
778 sprintf (buf, "%g|%g", m.meter().beats_per_bar(), m.meter().note_divisor());
779 _right_layout->set_text (buf);
784 AudioClock::set_session (Session *s)
786 SessionHandlePtr::set_session (s);
790 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), boost::bind (&AudioClock::session_configuration_changed, this, _1), gui_context());
792 const XMLProperty* prop;
793 XMLNode* node = _session->extra_xml (X_("ClockModes"));
794 AudioClock::Mode amode;
797 for (XMLNodeList::const_iterator i = node->children().begin(); i != node->children().end(); ++i) {
798 if ((prop = (*i)->property (X_("name"))) && prop->value() == _name) {
800 if ((prop = (*i)->property (X_("mode"))) != 0) {
801 amode = AudioClock::Mode (string_2_enum (prop->value(), amode));
804 if ((prop = (*i)->property (X_("on"))) != 0) {
805 set_off (!string_is_affirmative (prop->value()));
812 set (last_when, true);
817 AudioClock::on_key_press_event (GdkEventKey* ev)
823 /* return true for keys that we MIGHT use
826 switch (ev->keyval) {
861 AudioClock::on_key_release_event (GdkEventKey *ev)
870 switch (ev->keyval) {
913 case GDK_KP_Subtract:
914 end_edit_relative (false);
919 end_edit_relative (true);
931 ChangeAborted(); /* EMIT SIGNAL */
938 if (input_string.length() >= insert_max) {
939 /* eat the key event, but do no nothing with it */
943 input_string.insert (input_string.begin(), new_char);
945 string::reverse_iterator ri;
946 vector<int> insert_at;
947 int highlight_length;
949 /* merge with pre-edit-string into edit string */
953 edit_string = input_string;
954 highlight_length = edit_string.length();
958 edit_string = pre_edit_string;
960 /* backup through the original string, till we have
961 * enough digits locations to put all the digits from
965 for (ri = edit_string.rbegin(); ri != edit_string.rend(); ++ri) {
967 insert_at.push_back (edit_string.length() - (ri - edit_string.rbegin()) - 1);
968 if (insert_at.size() == input_string.length()) {
974 if (insert_at.size() != input_string.length()) {
975 error << "something went wrong " << endmsg;
977 for (int i = input_string.length() - 1; i >= 0; --i) {
978 edit_string[insert_at[i]] = input_string[i];
981 highlight_length = edit_string.length() - insert_at.back();
987 if (edit_string != _layout->get_text()) {
988 show_edit_status (highlight_length);
989 _layout->set_text (edit_string);
997 AudioClock::index_to_field (int index) const
1002 return Timecode_Hours;
1003 } else if (index < 7) {
1004 return Timecode_Minutes;
1005 } else if (index < 10) {
1006 return Timecode_Seconds;
1007 } else if (index < 13) {
1008 return Timecode_Frames;
1016 } else if (index < 7) {
1018 } else if (index < 12) {
1027 return Timecode_Hours;
1028 } else if (index < 6) {
1030 } else if (index < 9) {
1032 } else if (index < 12) {
1033 return MS_Milliseconds;
1046 AudioClock::on_button_press_event (GdkEventButton *ev)
1048 switch (ev->button) {
1052 /* make absolutely sure that the pointer is grabbed */
1053 gdk_pointer_grab(ev->window,false ,
1054 GdkEventMask( Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK |Gdk::BUTTON_RELEASE_MASK),
1055 NULL,NULL,ev->time);
1057 drag_start_y = ev->y;
1063 if (_layout->xy_to_index (ev->x * PANGO_SCALE, ev->y * PANGO_SCALE, index, trailing)) {
1064 drag_field = index_to_field (index);
1066 drag_field = Field (0);
1080 AudioClock::on_button_release_event (GdkEventButton *ev)
1084 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1086 if (ev->y > drag_start_y+1 || ev->y < drag_start_y-1 || Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)){
1087 // we actually dragged so return without
1088 // setting editing focus, or we shift clicked
1091 if (ev->button == 1) {
1099 if (Keyboard::is_context_menu_event (ev)) {
1100 if (ops_menu == 0) {
1103 ops_menu->popup (1, ev->time);
1111 AudioClock::on_focus_out_event (GdkEventFocus* ev)
1113 bool ret = CairoWidget::on_focus_out_event (ev);
1121 AudioClock::on_scroll_event (GdkEventScroll *ev)
1126 if (_session == 0 || !editable) {
1130 if (!_layout->xy_to_index (ev->x * PANGO_SCALE, ev->y * PANGO_SCALE, index, trailing)) {
1131 /* not in the main layout */
1135 Field f = index_to_field (index);
1136 framepos_t frames = 0;
1138 switch (ev->direction) {
1141 frames = get_frame_step (f);
1143 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1146 set (current_time() + frames, true);
1147 ValueChanged (); /* EMIT_SIGNAL */
1151 case GDK_SCROLL_DOWN:
1152 frames = get_frame_step (f);
1154 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1158 if ((double)current_time() - (double)frames < 0.0) {
1161 set (current_time() - frames, true);
1164 ValueChanged (); /* EMIT_SIGNAL */
1177 AudioClock::on_motion_notify_event (GdkEventMotion *ev)
1179 if (_session == 0 || !dragging) {
1183 float pixel_frame_scale_factor = 0.2f;
1185 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1186 pixel_frame_scale_factor = 0.1f;
1190 if (Keyboard::modifier_state_contains (ev->state,
1191 Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) {
1193 pixel_frame_scale_factor = 0.025f;
1196 double y_delta = ev->y - drag_y;
1198 drag_accum += y_delta*pixel_frame_scale_factor;
1202 if (trunc (drag_accum) != 0) {
1207 dir = (drag_accum < 0 ? 1:-1);
1208 pos = current_time();
1209 frames = get_frame_step (drag_field,pos,dir);
1211 if (frames != 0 && frames * drag_accum < current_time()) {
1212 set ((framepos_t) floor (pos - drag_accum * frames), false); // minus because up is negative in GTK
1218 ValueChanged(); /* EMIT_SIGNAL */
1225 AudioClock::get_frame_step (Field field, framepos_t pos, int dir)
1228 Timecode::BBT_Time BBT;
1230 case Timecode_Hours:
1231 f = (framecnt_t) floor (3600.0 * _session->frame_rate());
1233 case Timecode_Minutes:
1234 f = (framecnt_t) floor (60.0 * _session->frame_rate());
1236 case Timecode_Seconds:
1237 f = _session->frame_rate();
1239 case Timecode_Frames:
1240 f = (framecnt_t) floor (_session->frame_rate() / _session->timecode_frames_per_second());
1248 f = (framecnt_t) floor (3600.0 * _session->frame_rate());
1251 f = (framecnt_t) floor (60.0 * _session->frame_rate());
1254 f = (framecnt_t) _session->frame_rate();
1256 case MS_Milliseconds:
1257 f = (framecnt_t) floor (_session->frame_rate() / 1000.0);
1264 f = _session->tempo_map().bbt_duration_at (pos,BBT,dir);
1270 f = _session->tempo_map().bbt_duration_at(pos,BBT,dir);
1276 f = _session->tempo_map().bbt_duration_at(pos,BBT,dir);
1279 error << string_compose (_("programming error: %1"), "attempt to get frames from non-text field!") << endmsg;
1288 AudioClock::current_time (framepos_t pos) const
1290 // if (!_canonical_time_is_displayed) {
1291 // return _canonical_time;
1298 ret = timecode_frame_from_display ();
1301 ret = bbt_frame_from_display (pos);
1305 ret = minsec_frame_from_display ();
1309 ret = audio_frame_from_display ();
1317 AudioClock::current_duration (framepos_t pos) const
1323 ret = timecode_frame_from_display ();
1326 ret = bbt_frame_duration_from_display (pos);
1330 ret = minsec_frame_from_display ();
1334 ret = audio_frame_from_display ();
1342 AudioClock::bbt_validate_edit (const string& str)
1346 sscanf (str.c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, &any.bbt.bars, &any.bbt.beats, &any.bbt.ticks);
1348 if (!is_duration && any.bbt.bars == 0) {
1352 if (!is_duration && any.bbt.beats == 0) {
1360 AudioClock::timecode_validate_edit (const string& str)
1364 if (sscanf (_layout->get_text().c_str(), "%" PRId32 ":%" PRId32 ":%" PRId32 ":%" PRId32,
1365 &TC.hours, &TC.minutes, &TC.seconds, &TC.frames) != 4) {
1369 if (TC.minutes > 59 || TC.seconds > 59) {
1373 if (TC.frames > (long)rint(_session->timecode_frames_per_second()) - 1) {
1377 if (_session->timecode_drop_frames()) {
1378 if (TC.minutes % 10 && TC.seconds == 0 && TC.frames < 2) {
1387 AudioClock::timecode_frame_from_display () const
1389 if (_session == 0) {
1396 sscanf (_layout->get_text().c_str(), "%d:%d:%d:%d", &TC.hours, &TC.minutes, &TC.seconds, &TC.frames);
1398 TC.rate = _session->timecode_frames_per_second();
1399 TC.drop= _session->timecode_drop_frames();
1401 _session->timecode_to_sample (TC, sample, false /* use_offset */, false /* use_subframes */ );
1403 // timecode_tester ();
1409 AudioClock::minsec_frame_from_display () const
1411 if (_session == 0) {
1415 int hrs, mins, secs, millisecs;
1416 framecnt_t sr = _session->frame_rate();
1418 sscanf (_layout->get_text().c_str(), "%d:%d:%d:%d", &hrs, &mins, &secs, &millisecs);
1419 return (framepos_t) floor ((hrs * 60.0f * 60.0f * sr) + (mins * 60.0f * sr) + (secs * sr) + (millisecs * sr / 1000.0));
1423 AudioClock::bbt_frame_from_display (framepos_t pos) const
1425 if (_session == 0) {
1426 error << "AudioClock::current_time() called with BBT mode but without session!" << endmsg;
1431 any.type = AnyTime::BBT;
1433 sscanf (_layout->get_text().c_str(), "%" PRId32 "|%" PRId32 "|%" PRId32, &any.bbt.bars, &any.bbt.beats, &any.bbt.ticks);
1438 return _session->any_duration_to_frames (pos, any);
1440 return _session->convert_to_frames (any);
1446 AudioClock::bbt_frame_duration_from_display (framepos_t pos) const
1448 if (_session == 0) {
1449 error << "AudioClock::current_time() called with BBT mode but without session!" << endmsg;
1453 Timecode::BBT_Time bbt;
1455 sscanf (_layout->get_text().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, &bbt.bars, &bbt.beats, &bbt.ticks);
1457 return _session->tempo_map().bbt_duration_at(pos,bbt,1);
1461 AudioClock::audio_frame_from_display () const
1464 sscanf (_layout->get_text().c_str(), "%" PRId64, &f);
1469 AudioClock::build_ops_menu ()
1471 using namespace Menu_Helpers;
1472 ops_menu = new Menu;
1473 MenuList& ops_items = ops_menu->items();
1474 ops_menu->set_name ("ArdourContextMenu");
1476 if (!Profile->get_sae()) {
1477 ops_items.push_back (MenuElem (_("Timecode"), sigc::bind (sigc::mem_fun(*this, &AudioClock::set_mode), Timecode)));
1479 ops_items.push_back (MenuElem (_("Bars:Beats"), sigc::bind (sigc::mem_fun(*this, &AudioClock::set_mode), BBT)));
1480 ops_items.push_back (MenuElem (_("Minutes:Seconds"), sigc::bind (sigc::mem_fun(*this, &AudioClock::set_mode), MinSec)));
1481 ops_items.push_back (MenuElem (_("Samples"), sigc::bind (sigc::mem_fun(*this, &AudioClock::set_mode), Frames)));
1483 if (editable && !is_duration && !_follows_playhead) {
1484 ops_items.push_back (SeparatorElem());
1485 ops_items.push_back (MenuElem (_("Set From Playhead"), sigc::mem_fun(*this, &AudioClock::set_from_playhead)));
1486 ops_items.push_back (MenuElem (_("Locate to This Time"), sigc::mem_fun(*this, &AudioClock::locate)));
1491 AudioClock::set_from_playhead ()
1497 set (_session->transport_frame());
1502 AudioClock::locate ()
1504 if (!_session || is_duration) {
1508 _session->request_locate (current_time(), _session->transport_rolling ());
1512 AudioClock::set_mode (Mode m)
1522 insert_max = 9; // 8 digits + sign [-]2:2:2:2
1523 mode_based_info_ratio = 0.5;
1527 insert_max = 8; // 8 digits, 2|2|4
1528 mode_based_info_ratio = 0.5;
1532 insert_max = 9; // 7 digits 2:2:2.3
1533 mode_based_info_ratio = 1.0;
1537 insert_max = INT_MAX;
1538 mode_based_info_ratio = 1.0;
1542 set (last_when, true);
1544 if (!is_transient) {
1545 ModeChanged (); /* EMIT SIGNAL (the static one)*/
1548 mode_changed (); /* EMIT SIGNAL (the member one) */
1552 AudioClock::set_bbt_reference (framepos_t pos)
1554 bbt_reference_time = pos;
1558 AudioClock::on_style_changed (const Glib::RefPtr<Gtk::Style>& old_style)
1560 CairoWidget::on_style_changed (old_style);
1566 AudioClock::set_is_duration (bool yn)
1568 if (yn == is_duration) {
1573 set (last_when, true, 0, 's');
1577 AudioClock::set_off (bool yn)
1586 _canonical_time = current_time ();
1587 _canonical_time_is_displayed = false;
1589 _canonical_time_is_displayed = true;
1592 /* force a possible redraw */
1594 set (_canonical_time, true);
1598 AudioClock::focus ()
1604 AudioClock::set_draw_background (bool yn)