X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Faudio_clock.cc;h=b64e54e95e6413d1f7120adac40de3c98ac31f59;hb=2d47183dc962bddd2084676b59e9f2c4a0abe33b;hp=5b8a0c6efaf5e552174f0c2d288bd03110d8ae35;hpb=2508f3ba9473a47ae35cd80f519735c8eb0169f1;p=ardour.git diff --git a/gtk2_ardour/audio_clock.cc b/gtk2_ardour/audio_clock.cc index 5b8a0c6efa..b64e54e95e 100644 --- a/gtk2_ardour/audio_clock.cc +++ b/gtk2_ardour/audio_clock.cc @@ -31,6 +31,7 @@ #include "gtkmm2ext/rgb_macros.h" #include "ardour/profile.h" +#include "ardour/lmath.h" #include "ardour/session.h" #include "ardour/slave.h" #include "ardour/tempo.h" @@ -45,6 +46,7 @@ #include "i18n.h" using namespace ARDOUR; +using namespace ARDOUR_UI_UTILS; using namespace PBD; using namespace Gtk; using namespace std; @@ -53,25 +55,26 @@ using Gtkmm2ext::Keyboard; sigc::signal AudioClock::ModeChanged; vector AudioClock::clocks; -const double AudioClock::info_font_scale_factor = 0.5; +const double AudioClock::info_font_scale_factor = 0.68; const double AudioClock::separator_height = 0.0; const double AudioClock::x_leading_padding = 6.0; #define BBT_BAR_CHAR "|" #define BBT_SCANF_FORMAT "%" PRIu32 "%*c%" PRIu32 "%*c%" PRIu32 -#define INFO_FONT_SIZE ((int)round(font_size * info_font_scale_factor)) +#define INFO_FONT_SIZE ((int)lrint(font_size * info_font_scale_factor)) +#define TXTSPAN "" AudioClock::AudioClock (const string& clock_name, bool transient, const string& widget_name, - bool allow_edit, bool follows_playhead, bool duration, bool with_info) + bool allow_edit, bool follows_playhead, bool duration, bool with_info, + bool accept_on_focus_out) : ops_menu (0) , _name (clock_name) , is_transient (transient) , is_duration (duration) , editable (allow_edit) , _follows_playhead (follows_playhead) + , _accept_on_focus_out (accept_on_focus_out) , _off (false) - , _fixed_width (true) - , layout_x_offset (0) , em_width (0) , _edit_by_click_field (false) , _negative_allowed (false) @@ -80,12 +83,13 @@ AudioClock::AudioClock (const string& clock_name, bool transient, const string& , foreground_attr (0) , first_height (0) , first_width (0) + , style_resets_first (true) , layout_height (0) , layout_width (0) , info_height (0) , upper_height (0) , mode_based_info_ratio (1.0) - , corner_radius (9) + , corner_radius (4) , font_size (10240) , editing (false) , bbt_reference_time (-1) @@ -94,7 +98,8 @@ AudioClock::AudioClock (const string& clock_name, bool transient, const string& , last_sdelta (0) , dragging (false) , drag_field (Field (0)) - + , xscale (1.0) + , yscale (1.0) { set_flags (CAN_FOCUS); @@ -144,26 +149,27 @@ AudioClock::set_widget_name (const string& str) void AudioClock::on_realize () { + Gtk::Requisition req; + CairoWidget::on_realize (); - set_font (); + + set_clock_dimensions (req); + + first_width = req.width; + first_height = req.height; + + // XXX FIX ME: define font based on ... ??? + // set_font (); set_colors (); } void -AudioClock::set_font () +AudioClock::set_font (Pango::FontDescription font) { Glib::RefPtr style = get_style (); - Pango::FontDescription font; Pango::AttrFontDesc* font_attr; - if (!is_realized()) { - font = get_font_for_style (get_name()); - } else { - font = style->get_font(); - } - font_size = font.get_size(); - font_attr = new Pango::AttrFontDesc (Pango::Attribute::create_attr_font_desc (font)); normal_attributes.change (*font_attr); @@ -172,7 +178,7 @@ AudioClock::set_font () /* now a smaller version of the same font */ delete font_attr; - font.set_size ((int) lrint (font_size * info_font_scale_factor)); + font.set_size (INFO_FONT_SIZE); font.set_weight (Pango::WEIGHT_NORMAL); font_attr = new Pango::AttrFontDesc (Pango::Attribute::create_attr_font_desc (font)); @@ -215,15 +221,15 @@ AudioClock::set_colors () uint32_t cursor_color; if (active_state()) { - bg_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1 active: background", get_name())); - text_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1 active: text", get_name())); - editing_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1 active: edited text", get_name())); - cursor_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1 active: cursor", get_name())); + bg_color = ARDOUR_UI::config()->color (string_compose ("%1 active: background", get_name())); + text_color = ARDOUR_UI::config()->color (string_compose ("%1 active: text", get_name())); + editing_color = ARDOUR_UI::config()->color (string_compose ("%1 active: edited text", get_name())); + cursor_color = ARDOUR_UI::config()->color (string_compose ("%1 active: cursor", get_name())); } else { - bg_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: background", get_name())); - text_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: text", get_name())); - editing_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: edited text", get_name())); - cursor_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: cursor", get_name())); + bg_color = ARDOUR_UI::config()->color (string_compose ("%1: background", get_name())); + text_color = ARDOUR_UI::config()->color (string_compose ("%1: text", get_name())); + editing_color = ARDOUR_UI::config()->color (string_compose ("%1: edited text", get_name())); + cursor_color = ARDOUR_UI::config()->color (string_compose ("%1: cursor", get_name())); } /* store for bg and cursor in render() */ @@ -252,12 +258,14 @@ AudioClock::set_colors () r = lrint ((r/255.0) * 65535.0); g = lrint ((g/255.0) * 65535.0); b = lrint ((b/255.0) * 65535.0); + delete foreground_attr; foreground_attr = new Pango::AttrColor (Pango::Attribute::create_attr_foreground (r, g, b)); UINT_TO_RGBA (editing_color, &r, &g, &b, &a); r = lrint ((r/255.0) * 65535.0); g = lrint ((g/255.0) * 65535.0); b = lrint ((b/255.0) * 65535.0); + delete editing_attr; editing_attr = new Pango::AttrColor (Pango::Attribute::create_attr_foreground (r, g, b)); normal_attributes.change (*foreground_attr); @@ -275,7 +283,16 @@ AudioClock::set_colors () } void -AudioClock::render (cairo_t* cr) +AudioClock::set_scale (double x, double y) +{ + xscale = x; + yscale = y; + + queue_draw (); +} + +void +AudioClock::render (cairo_t* cr, cairo_rectangle_t*) { /* main layout: rounded rect, plus the text */ @@ -283,7 +300,7 @@ AudioClock::render (cairo_t* cr) cairo_set_source_rgba (cr, bg_r, bg_g, bg_b, bg_a); if (corner_radius) { if (_left_layout) { - Gtkmm2ext::rounded_top_half_rectangle (cr, 0, 0, get_width() - corner_radius/2.0, upper_height, corner_radius); + Gtkmm2ext::rounded_top_half_rectangle (cr, 0, 0, get_width(), upper_height, corner_radius); } else { Gtkmm2ext::rounded_rectangle (cr, 0, 0, get_width(), upper_height, corner_radius); } @@ -293,15 +310,22 @@ AudioClock::render (cairo_t* cr) cairo_fill (cr); } - if (!_fixed_width) { - cairo_move_to (cr, layout_x_offset, 0); - } else { - int xcenter = layout_x_offset > corner_radius/4.0 ? 0 : (get_width() - _mode_width[_mode]) /2; - cairo_move_to (cr, layout_x_offset + xcenter, (upper_height - layout_height) / 2.0); - } + double lw = layout_width * xscale; + double lh = layout_height * yscale; + + cairo_move_to (cr, (get_width() - lw) / 2.0, (upper_height - lh) / 2.0); + if (xscale != 1.0 || yscale != 1.0) { + cairo_save (cr); + cairo_scale (cr, xscale, yscale); + } + pango_cairo_show_layout (cr, _layout->gobj()); + if (xscale != 1.0 || yscale != 1.0) { + cairo_restore (cr); + } + if (_left_layout) { double h = get_height() - upper_height - separator_height; @@ -312,13 +336,13 @@ AudioClock::render (cairo_t* cr) if (mode_based_info_ratio != 1.0) { - double left_rect_width = round (((get_width() - separator_height) * mode_based_info_ratio) + 0.5); + double left_rect_width = get_left_rect_width(); if (_need_bg) { if (corner_radius) { Gtkmm2ext::rounded_bottom_half_rectangle (cr, 0, upper_height + separator_height, left_rect_width + (separator_height == 0 ? corner_radius : 0), - h - corner_radius/2.0, corner_radius); + h, corner_radius); } else { cairo_rectangle (cr, 0, upper_height + separator_height, left_rect_width, h); } @@ -332,8 +356,8 @@ AudioClock::render (cairo_t* cr) if (corner_radius) { Gtkmm2ext::rounded_bottom_half_rectangle (cr, left_rect_width + separator_height, upper_height + separator_height, - get_width() - separator_height - left_rect_width - corner_radius/2.0, - h - corner_radius/2.0, corner_radius); + get_width() - separator_height - left_rect_width, + h, corner_radius); } else { cairo_rectangle (cr, left_rect_width + separator_height, upper_height + separator_height, get_width() - separator_height - left_rect_width, h); @@ -352,7 +376,7 @@ AudioClock::render (cairo_t* cr) */ int x, rw, rh; _right_layout->get_pixel_size(rw, rh); - x = get_width() - rw - separator_height - x_leading_padding - corner_radius/2.0; + x = get_width() - rw - separator_height - x_leading_padding; if (x < x_leading_padding + left_rect_width + separator_height) { /* rather cut off the right end than overlap with the text on the left */ x = x_leading_padding + left_rect_width + separator_height; @@ -380,7 +404,7 @@ AudioClock::render (cairo_t* cr) if (editing) { if (!insert_map.empty()) { - int xcenter = layout_x_offset > corner_radius/4.0 ? 0 : (get_width() - _mode_width[_mode]) /2; + int xcenter = (get_width() - layout_width) /2; if (input_string.length() < insert_map.size()) { Pango::Rectangle cursor; @@ -395,18 +419,11 @@ AudioClock::render (cairo_t* cr) } cairo_set_source_rgba (cr, cursor_r, cursor_g, cursor_b, cursor_a); - if (!_fixed_width) { - cairo_rectangle (cr, - min (get_width() - 2.0, - (double) cursor.get_x()/PANGO_SCALE + layout_x_offset + xcenter + em_width), 0, - 2.0, cursor.get_height()/PANGO_SCALE); - } else { - cairo_rectangle (cr, - min (get_width() - 2.0, - (double) layout_x_offset + xcenter + cursor.get_x()/PANGO_SCALE + em_width), - (upper_height - layout_height)/2.0, - 2.0, cursor.get_height()/PANGO_SCALE); - } + cairo_rectangle (cr, + min (get_width() - 2.0, + (double) xcenter + cursor.get_x()/PANGO_SCALE + em_width), + (upper_height - layout_height)/2.0, + 2.0, cursor.get_height()/PANGO_SCALE); cairo_fill (cr); } else { /* we've entered all possible digits, no cursor */ @@ -415,17 +432,10 @@ AudioClock::render (cairo_t* cr) } else { if (input_string.empty()) { cairo_set_source_rgba (cr, cursor_r, cursor_g, cursor_b, cursor_a); - if (!_fixed_width) { - cairo_rectangle (cr, - (get_width()/2.0), - 0, - 2.0, upper_height); - } else { - cairo_rectangle (cr, - (get_width()/2.0), - (upper_height - layout_height)/2.0, - 2.0, upper_height); - } + cairo_rectangle (cr, + (get_width()/2.0), + (upper_height - layout_height)/2.0, + 2.0, upper_height); cairo_fill (cr); } } @@ -442,33 +452,11 @@ AudioClock::on_size_allocate (Gtk::Allocation& alloc) } else { upper_height = get_height(); } - - if (_fixed_width) { - /* center display in available space - * NB. this only works if the containing widget is not the - * layout itself (eg. the session->property dialog) - */ - layout_x_offset = (get_width() - layout_width)/2.0; - } else { - /* left justify */ - layout_x_offset = 0; - } } void -AudioClock::on_size_request (Gtk::Requisition* req) +AudioClock::set_clock_dimensions (Gtk::Requisition& req) { - /* even for non fixed width clocks, the size we *ask* for never changes, - even though the size we receive might. so once we've computed it, - just return it. - */ - - if (first_width) { - req->width = first_width; - req->height = first_height; - return; - } - Glib::RefPtr tmp; Glib::RefPtr style = get_style (); Pango::FontDescription font; @@ -483,46 +471,32 @@ AudioClock::on_size_request (Gtk::Requisition* req) tmp->set_font_description (font); - if (_fixed_width) { - int ignored; - tmp->set_text ("-88:88:88:88"); - tmp->get_pixel_size (_mode_width[Timecode], ignored); - tmp->set_text (" 88888|88|8888"); - tmp->get_pixel_size (_mode_width[BBT], ignored); - tmp->set_text (" 88:88:88,888"); - tmp->get_pixel_size (_mode_width[MinSec], ignored); - tmp->set_text (" 8888888888"); - tmp->get_pixel_size (_mode_width[Frames], ignored); - - /* this string is the longest thing we will ever display, - it does not include the BBT bar char that may descend - below the baseline. - note; depending on BPM setting this may actually - not be sufficient for 24h worth of BBT - */ + /* this string is the longest thing we will ever display */ + if (_mode == MinSec) + tmp->set_text (" 88:88:88,888 "); + else + tmp->set_text (" 88:88:88,88 "); + tmp->get_pixel_size (req.width, req.height); - tmp->set_text (" 88888888888::,"); - } else { - switch (_mode) { - case Timecode: - tmp->set_text ("-88:88:88:88"); - break; - case BBT: - tmp->set_text (" 88888|88|8888"); - break; - case MinSec: - tmp->set_text (" 88:88:88,888"); - break; - case Frames: - tmp->set_text (" 8888888888"); - break; - } - } + layout_height = req.height; + layout_width = req.width; +} + +void +AudioClock::on_size_request (Gtk::Requisition* req) +{ + /* even for non fixed width clocks, the size we *ask* for never changes, + even though the size we receive might. so once we've computed it, + just return it. + */ - tmp->get_pixel_size (req->width, req->height); + if (first_width) { + req->width = first_width; + req->height = first_height; + return; + } - layout_height = req->height; - layout_width = req->width; + set_clock_dimensions (*req); /* now tackle height, for which we need to know the height of the lower * layout @@ -530,9 +504,22 @@ AudioClock::on_size_request (Gtk::Requisition* req) if (_left_layout) { + Glib::RefPtr tmp; + Glib::RefPtr style = get_style (); + Pango::FontDescription font; int w; + + tmp = Pango::Layout::create (get_pango_context()); + + if (!is_realized()) { + font = get_font_for_style (get_name()); + } else { + font = style->get_font(); + } + + tmp->set_font_description (font); - font.set_size ((int) lrint (font.get_size() * info_font_scale_factor)); + font.set_size (INFO_FONT_SIZE); font.set_weight (Pango::WEIGHT_NORMAL); tmp->set_font_description (font); @@ -550,9 +537,6 @@ AudioClock::on_size_request (Gtk::Requisition* req) req->height += info_height; req->height += separator_height; } - - first_height = req->height; - first_width = req->width; } void @@ -570,28 +554,30 @@ AudioClock::show_edit_status (int length) void AudioClock::start_edit (Field f) { - pre_edit_string = _layout->get_text (); - if (!insert_map.empty()) { - edit_string = pre_edit_string; - } else { - edit_string.clear (); - _layout->set_text (""); - } - - input_string.clear (); - editing = true; - edit_is_negative = false; + if (!editing) { + pre_edit_string = _layout->get_text (); + if (!insert_map.empty()) { + edit_string = pre_edit_string; + } else { + edit_string.clear (); + _layout->set_text (""); + } + + input_string.clear (); + editing = true; + edit_is_negative = false; + + if (f) { + input_string = get_field (f); + show_edit_status (merge_input_and_edit_string ()); + _layout->set_text (edit_string); + } + + queue_draw (); - if (f) { - input_string = get_field (f); - show_edit_status (merge_input_and_edit_string ()); - _layout->set_text (edit_string); + Keyboard::magic_widget_grab_focus (); + grab_focus (); } - - queue_draw (); - - Keyboard::magic_widget_grab_focus (); - grab_focus (); } string @@ -659,6 +645,9 @@ AudioClock::end_edit (bool modify) break; case Frames: + if (edit_string.length() < 1) { + edit_string = pre_edit_string; + } break; } @@ -811,31 +800,31 @@ AudioClock::parse_as_timecode_distance (const std::string& str) case 1: case 2: sscanf (str.c_str(), "%" PRId32, &frames); - return lrint ((frames/(float)fps) * sr); + return llrint ((frames/(float)fps) * sr); case 3: sscanf (str.c_str(), "%1" PRId32 "%" PRId32, &secs, &frames); - return (secs * sr) + lrint ((frames/(float)fps) * sr); + return (secs * sr) + llrint ((frames/(float)fps) * sr); case 4: sscanf (str.c_str(), "%2" PRId32 "%" PRId32, &secs, &frames); - return (secs * sr) + lrint ((frames/(float)fps) * sr); + return (secs * sr) + llrint ((frames/(float)fps) * sr); case 5: sscanf (str.c_str(), "%1" PRId32 "%2" PRId32 "%" PRId32, &mins, &secs, &frames); - return (mins * 60 * sr) + (secs * sr) + lrint ((frames/(float)fps) * sr); + return (mins * 60 * sr) + (secs * sr) + llrint ((frames/(float)fps) * sr); case 6: sscanf (str.c_str(), "%2" PRId32 "%2" PRId32 "%" PRId32, &mins, &secs, &frames); - return (mins * 60 * sr) + (secs * sr) + lrint ((frames/(float)fps) * sr); + return (mins * 60 * sr) + (secs * sr) + llrint ((frames/(float)fps) * sr); case 7: sscanf (str.c_str(), "%1" PRId32 "%2" PRId32 "%2" PRId32 "%" PRId32, &hrs, &mins, &secs, &frames); - return (hrs * 3600 * sr) + (mins * 60 * sr) + (secs * sr) + lrint ((frames/(float)fps) * sr); + return (hrs * 3600 * sr) + (mins * 60 * sr) + (secs * sr) + llrint ((frames/(float)fps) * sr); case 8: sscanf (str.c_str(), "%2" PRId32 "%2" PRId32 "%2" PRId32 "%" PRId32, &hrs, &mins, &secs, &frames); - return (hrs * 3600 * sr) + (mins * 60 * sr) + (secs * sr) + lrint ((frames/(float)fps) * sr); + return (hrs * 3600 * sr) + (mins * 60 * sr) + (secs * sr) + llrint ((frames/(float)fps) * sr); default: break; @@ -915,7 +904,7 @@ AudioClock::end_edit_relative (bool add) } else { framepos_t c = current_time(); - if (c > frames) { + if (c > frames || _negative_allowed) { set (c - frames, true); } else { set (0, true); @@ -929,9 +918,20 @@ AudioClock::end_edit_relative (bool add) drop_focus (); } +void +AudioClock::session_property_changed (const PropertyChange&) +{ + set (last_when, true); +} + void AudioClock::session_configuration_changed (std::string p) { + if (_negative_allowed) { + /* session option editor clock */ + return; + } + if (p == "sync-source" || p == "external-sync") { set (current_time(), true); return; @@ -969,12 +969,24 @@ AudioClock::set (framepos_t when, bool force, framecnt_t offset) } if (when == last_when && !force) { +#if 0 // XXX return if no change and no change forced. verify Aug/2014 if (_mode != Timecode && _mode != MinSec) { /* may need to force display of TC source * time, so don't return early. */ + /* ^^ Why was that?, delta times? + * Timecode FPS, pull-up/down, etc changes + * trigger a 'session_property_changed' which + * eventually calls set(last_when, true) + * + * re-rendering the clock every 40ms or so just + * because we can is not ideal. + */ return; } +#else + return; +#endif } if (!editing) { @@ -1024,19 +1036,19 @@ AudioClock::set_slave_info () Slave* slave = _session->slave(); switch (sync_src) { - case JACK: - _left_layout->set_markup (string_compose ("%2", + case Engine: + _left_layout->set_markup (string_compose ("" TXTSPAN "%2", INFO_FONT_SIZE, sync_source_to_string(sync_src, true))); _right_layout->set_text (""); break; case MIDIClock: if (slave) { - _left_layout->set_markup (string_compose ("%2", + _left_layout->set_markup (string_compose ("" TXTSPAN "%2", INFO_FONT_SIZE, sync_source_to_string(sync_src, true))); - _right_layout->set_markup (string_compose ("%2", + _right_layout->set_markup (string_compose ("" TXTSPAN "%2", INFO_FONT_SIZE, slave->approximate_current_delta())); } else { - _left_layout->set_markup (string_compose ("%2", + _left_layout->set_markup (string_compose ("" TXTSPAN "%2", INFO_FONT_SIZE, _("--pending--"))); _right_layout->set_text (""); } @@ -1048,21 +1060,21 @@ AudioClock::set_slave_info () TimecodeSlave* tcslave; if ((tcslave = dynamic_cast(_session->slave())) != 0) { matching = (tcslave->apparent_timecode_format() == _session->config.get_timecode_format()); - _left_layout->set_markup (string_compose ("%2%4", + _left_layout->set_markup (string_compose ("" TXTSPAN "%2%4", INFO_FONT_SIZE, sync_source_to_string(sync_src, true)[0], (matching?"green":"red"), dynamic_cast(slave)->approximate_current_position())); - _right_layout->set_markup (string_compose ("%2", + _right_layout->set_markup (string_compose ("" TXTSPAN "%2", INFO_FONT_SIZE, slave->approximate_current_delta())); } } else { - _left_layout->set_markup (string_compose ("%2", + _left_layout->set_markup (string_compose ("" TXTSPAN "%2", INFO_FONT_SIZE, _("--pending--"))); _right_layout->set_text (""); } break; } } else { - _left_layout->set_markup (string_compose ("INT/%2", + _left_layout->set_markup (string_compose ("" TXTSPAN "INT/%2", INFO_FONT_SIZE, sync_source_to_string(sync_src, true))); _right_layout->set_text (""); } @@ -1075,7 +1087,7 @@ AudioClock::set_frames (framepos_t when, bool /*force*/) bool negative = false; if (_off) { - _layout->set_text ("\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012"); + _layout->set_text (" ----------"); if (_left_layout) { _left_layout->set_text (""); @@ -1107,64 +1119,74 @@ AudioClock::set_frames (framepos_t when, bool /*force*/) sprintf (buf, "%" PRId64 "Hz", rate); } - _left_layout->set_markup (string_compose ("%2 %3", + _left_layout->set_markup (string_compose ("" TXTSPAN "%2 %3", INFO_FONT_SIZE, _("SR"), buf)); float vid_pullup = _session->config.get_video_pullup(); if (vid_pullup == 0.0) { - _right_layout->set_markup (string_compose ("%2", - INFO_FONT_SIZE, _("pullup: \u2012"))); + _right_layout->set_markup (string_compose ("" TXTSPAN "%2 off", + INFO_FONT_SIZE, _("Pull"))); } else { - sprintf (buf, _("%+-6.4f%%"), vid_pullup); - _right_layout->set_markup (string_compose ("%2", - INFO_FONT_SIZE, buf)); + sprintf (buf, _("%+.4f%%"), vid_pullup); + _right_layout->set_markup (string_compose ("" TXTSPAN "%2 %3", + INFO_FONT_SIZE, _("Pull"), buf)); } } } void -AudioClock::set_minsec (framepos_t when, bool /*force*/) +AudioClock::print_minsec (framepos_t when, char* buf, size_t bufsize, float frame_rate) { - char buf[32]; framecnt_t left; int hrs; int mins; int secs; int millisecs; - bool negative = false; - - if (_off) { - _layout->set_text ("\u2012\u2012:\u2012\u2012:\u2012\u2012.\u2012\u2012\u2012"); - - if (_left_layout) { - _left_layout->set_text (""); - _right_layout->set_text (""); - } - - return; - } + bool negative; if (when < 0) { when = -when; negative = true; + } else { + negative = false; } left = when; - hrs = (int) floor (left / (_session->frame_rate() * 60.0f * 60.0f)); - left -= (framecnt_t) floor (hrs * _session->frame_rate() * 60.0f * 60.0f); - mins = (int) floor (left / (_session->frame_rate() * 60.0f)); - left -= (framecnt_t) floor (mins * _session->frame_rate() * 60.0f); - secs = (int) floor (left / (float) _session->frame_rate()); - left -= (framecnt_t) floor (secs * _session->frame_rate()); - millisecs = floor (left * 1000.0 / (float) _session->frame_rate()); + hrs = (int) floor (left / (frame_rate * 60.0f * 60.0f)); + left -= (framecnt_t) floor (hrs * frame_rate * 60.0f * 60.0f); + mins = (int) floor (left / (frame_rate * 60.0f)); + left -= (framecnt_t) floor (mins * frame_rate * 60.0f); + secs = (int) floor (left / (float) frame_rate); + left -= (framecnt_t) floor ((double)(secs * frame_rate)); + millisecs = floor (left * 1000.0 / (float) frame_rate); if (negative) { - snprintf (buf, sizeof (buf), "-%02" PRId32 ":%02" PRId32 ":%02" PRId32 ".%03" PRId32, hrs, mins, secs, millisecs); + snprintf (buf, bufsize, "-%02" PRId32 ":%02" PRId32 ":%02" PRId32 ".%03" PRId32, hrs, mins, secs, millisecs); } else { - snprintf (buf, sizeof (buf), " %02" PRId32 ":%02" PRId32 ":%02" PRId32 ".%03" PRId32, hrs, mins, secs, millisecs); + snprintf (buf, bufsize, " %02" PRId32 ":%02" PRId32 ":%02" PRId32 ".%03" PRId32, hrs, mins, secs, millisecs); } +} + +void +AudioClock::set_minsec (framepos_t when, bool /*force*/) +{ + char buf[32]; + + if (_off) { + _layout->set_text (" --:--:--.---"); + + if (_left_layout) { + _left_layout->set_text (""); + _right_layout->set_text (""); + } + + return; + } + + print_minsec (when, buf, sizeof (buf), _session->frame_rate()); + _layout->set_text (buf); set_slave_info(); } @@ -1176,7 +1198,7 @@ AudioClock::set_timecode (framepos_t when, bool /*force*/) bool negative = false; if (_off) { - _layout->set_text ("\u2012\u2012:\u2012\u2012:\u2012\u2012:\u2012\u2012"); + _layout->set_text (" --:--:--:--"); if (_left_layout) { _left_layout->set_text (""); _right_layout->set_text (""); @@ -1211,7 +1233,7 @@ AudioClock::set_bbt (framepos_t when, bool /*force*/) bool negative = false; if (_off) { - _layout->set_text ("\u2012\u2012\u2012|\u2012\u2012|\u2012\u2012\u2012\u2012"); + _layout->set_text (" ---|--|----"); if (_left_layout) { _left_layout->set_text (""); _right_layout->set_text (""); @@ -1260,13 +1282,13 @@ AudioClock::set_bbt (framepos_t when, bool /*force*/) TempoMetric m (_session->tempo_map().metric_at (pos)); - sprintf (buf, "%-5.2f", m.tempo().beats_per_minute()); - _left_layout->set_markup (string_compose ("Tempo: %2", - INFO_FONT_SIZE, buf)); + sprintf (buf, "%-5.1f", m.tempo().beats_per_minute()); + _left_layout->set_markup (string_compose ("" TXTSPAN "%3 %2", + INFO_FONT_SIZE, buf, _("Tempo"))); sprintf (buf, "%g/%g", m.meter().divisions_per_bar(), m.meter().note_divisor()); - _right_layout->set_markup (string_compose ("Meter: %2", - INFO_FONT_SIZE, buf)); + _right_layout->set_markup (string_compose ("" TXTSPAN "%3 %2", + INFO_FONT_SIZE, buf, _("Meter"))); } } @@ -1278,6 +1300,7 @@ AudioClock::set_session (Session *s) if (_session) { _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), boost::bind (&AudioClock::session_configuration_changed, this, _1), gui_context()); + _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), boost::bind (&AudioClock::session_property_changed, this, _1), gui_context()); const XMLProperty* prop; XMLNode* node = _session->extra_xml (X_("ClockModes")); @@ -1361,6 +1384,9 @@ AudioClock::on_key_press_event (GdkEventKey* ev) case GDK_KP_Subtract: if (_negative_allowed && input_string.empty()) { edit_is_negative = true; + edit_string.replace(0,1,"-"); + _layout->set_text (edit_string); + queue_draw (); } else { end_edit_relative (false); } @@ -1428,6 +1454,16 @@ AudioClock::on_key_press_event (GdkEventKey* ev) highlight_length = merge_input_and_edit_string (); } + if (edit_is_negative) { + edit_string.replace(0,1,"-"); + } else { + if (!pre_edit_string.empty() && (pre_edit_string.at(0) == '-')) { + edit_string.replace(0,1,"_"); + } else { + edit_string.replace(0,1," "); + } + } + show_edit_status (highlight_length); _layout->set_text (edit_string); queue_draw (); @@ -1561,10 +1597,10 @@ AudioClock::on_button_press_event (GdkEventButton *ev) /* the text has been centered vertically, so adjust * x and y. */ - int xcenter = !_fixed_width || layout_x_offset > corner_radius/4.0 ? 0 : (get_width() - _mode_width[_mode]) /2; + int xcenter = (get_width() - layout_width) /2; y = ev->y - ((upper_height - layout_height)/2); - x = ev->x - layout_x_offset - xcenter; + x = ev->x - xcenter; if (!_layout->xy_to_index (x * PANGO_SCALE, y * PANGO_SCALE, index, trailing)) { /* pretend it is a character on the far right */ @@ -1606,11 +1642,11 @@ AudioClock::on_button_release_event (GdkEventButton *ev) if (_edit_by_click_field) { - int xcenter = !_fixed_width || layout_x_offset > corner_radius/4.0 ? 0 : (get_width() - _mode_width[_mode]) /2; + int xcenter = (get_width() - layout_width) /2; int index = 0; int trailing; int y = ev->y - ((upper_height - layout_height)/2); - int x = ev->x - layout_x_offset - xcenter; + int x = ev->x - xcenter; Field f; if (!_layout->xy_to_index (x * PANGO_SCALE, y * PANGO_SCALE, index, trailing)) { @@ -1654,7 +1690,7 @@ AudioClock::on_focus_out_event (GdkEventFocus* ev) bool ret = CairoWidget::on_focus_out_event (ev); if (editing) { - end_edit (false); + end_edit (_accept_on_focus_out); } return ret; @@ -1677,9 +1713,9 @@ AudioClock::on_scroll_event (GdkEventScroll *ev) * x and y. */ - int xcenter = !_fixed_width || layout_x_offset > corner_radius/4.0 ? 0 : (get_width() - _mode_width[_mode]) /2; + int xcenter = (get_width() - layout_width) /2; y = ev->y - ((upper_height - layout_height)/2); - x = ev->x - layout_x_offset - xcenter; + x = ev->x - xcenter; if (!_layout->xy_to_index (x * PANGO_SCALE, y * PANGO_SCALE, index, trailing)) { /* not in the main layout */ @@ -1709,7 +1745,7 @@ AudioClock::on_scroll_event (GdkEventScroll *ev) frames *= 10; } - if ((double)current_time() - (double)frames < 0.0) { + if (!_negative_allowed && (double)current_time() - (double)frames < 0.0) { set (0, true); } else { set (current_time() - frames, true); @@ -1753,7 +1789,7 @@ AudioClock::on_motion_notify_event (GdkEventMotion *ev) drag_y = ev->y; - if (trunc (drag_accum) != 0) { + if (floor (drag_accum) != 0) { framepos_t frames; framepos_t pos; @@ -1897,9 +1933,23 @@ bool AudioClock::timecode_validate_edit (const string&) { Timecode::Time TC; + int hours; + char ignored[2]; - if (sscanf (_layout->get_text().c_str(), "%" PRId32 ":%" PRId32 ":%" PRId32 ":%" PRId32, - &TC.hours, &TC.minutes, &TC.seconds, &TC.frames) != 4) { + if (sscanf (_layout->get_text().c_str(), "%[- _]%" PRId32 ":%" PRId32 ":%" PRId32 "%[:;]%" PRId32, + ignored, &hours, &TC.minutes, &TC.seconds, ignored, &TC.frames) != 6) { + return false; + } + + if (hours < 0) { + TC.hours = hours * -1; + TC.negative = true; + } else { + TC.hours = hours; + TC.negative = false; + } + + if (TC.negative && !_negative_allowed) { return false; } @@ -1945,19 +1995,23 @@ AudioClock::frames_from_timecode_string (const string& str) const Timecode::Time TC; framepos_t sample; + char ignored[2]; + int hours; - if (sscanf (str.c_str(), "%d:%d:%d:%d", &TC.hours, &TC.minutes, &TC.seconds, &TC.frames) != 4) { + if (sscanf (str.c_str(), "%[- _]%d:%d:%d%[:;]%d", ignored, &hours, &TC.minutes, &TC.seconds, ignored, &TC.frames) != 6) { error << string_compose (_("programming error: %1 %2"), "badly formatted timecode clock string", str) << endmsg; return 0; } - - TC.negative = edit_is_negative; + TC.hours = abs(hours); TC.rate = _session->timecode_frames_per_second(); TC.drop= _session->timecode_drop_frames(); _session->timecode_to_sample (TC, sample, false /* use_offset */, false /* use_subframes */ ); // timecode_tester (); + if (edit_is_negative) { + sample = - sample; + } return sample; } @@ -2009,7 +2063,7 @@ framepos_t AudioClock::frame_duration_from_bbt_string (framepos_t pos, const string& str) const { if (_session == 0) { - error << "AudioClock::current_time() called with BBT mode but without session!" << endmsg; + error << "AudioClock::frame_duration_from_bbt_string() called with BBT mode but without session!" << endmsg; return 0; } @@ -2030,6 +2084,24 @@ AudioClock::frames_from_audioframes_string (const string& str) const return f; } +void +AudioClock::copy_text_to_clipboard () const +{ + string val; + if (editing) { + val = pre_edit_string; + } else { + val = _layout->get_text (); + } + const size_t trim = val.find_first_not_of(" "); + if (trim == string::npos) { + assert(0); // empty clock, can't be right. + return; + } + Glib::RefPtr cl = Gtk::Clipboard::get(); + cl->set_text (val.substr(trim)); +} + void AudioClock::build_ops_menu () { @@ -2050,6 +2122,8 @@ AudioClock::build_ops_menu () ops_items.push_back (MenuElem (_("Set From Playhead"), sigc::mem_fun(*this, &AudioClock::set_from_playhead))); ops_items.push_back (MenuElem (_("Locate to This Time"), sigc::mem_fun(*this, &AudioClock::locate))); } + ops_items.push_back (SeparatorElem()); + ops_items.push_back (MenuElem (_("Copy to clipboard"), sigc::mem_fun(*this, &AudioClock::copy_text_to_clipboard))); } void @@ -2086,6 +2160,9 @@ AudioClock::set_mode (Mode m) _layout->set_text (""); + Gtk::Requisition req; + set_clock_dimensions (req); + if (_left_layout) { _left_layout->set_attributes (info_attributes); @@ -2101,7 +2178,7 @@ AudioClock::set_mode (Mode m) switch (_mode) { case Timecode: - mode_based_info_ratio = 0.5; + mode_based_info_ratio = 0.6; insert_map.push_back (11); insert_map.push_back (10); insert_map.push_back (8); @@ -2126,7 +2203,7 @@ AudioClock::set_mode (Mode m) break; case MinSec: - mode_based_info_ratio = 0.5; + mode_based_info_ratio = 0.6; insert_map.push_back (12); insert_map.push_back (11); insert_map.push_back (10); @@ -2139,7 +2216,7 @@ AudioClock::set_mode (Mode m) break; case Frames: - mode_based_info_ratio = 0.5; + mode_based_info_ratio = 0.45; break; } @@ -2149,13 +2226,6 @@ AudioClock::set_mode (Mode m) ModeChanged (); /* EMIT SIGNAL (the static one)*/ } - if (!_fixed_width) { - /* display is different, allow us to resize */ - first_width = 0; - first_height = 0; - queue_resize (); - } - mode_changed (); /* EMIT SIGNAL (the member one) */ } @@ -2169,9 +2239,12 @@ void AudioClock::on_style_changed (const Glib::RefPtr& old_style) { CairoWidget::on_style_changed (old_style); - first_width = 0; - first_height = 0; - set_font (); + + Gtk::Requisition req; + set_clock_dimensions (req); + + /* XXXX fix me ... we shouldn't be using GTK styles anyway */ + // set_font (); set_colors (); } @@ -2223,12 +2296,6 @@ AudioClock::set_corner_radius (double r) queue_resize (); } -void -AudioClock::set_fixed_width (bool yn) -{ - _fixed_width = yn; -} - void AudioClock::dpi_reset () {