X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Faudio_clock.cc;h=ebece6487cfc83f0723b6c7ec371fb47c58eebde;hb=b3fb75feadf28e35c302149778b544172f808a09;hp=7bbc00c2678b6c2e8ee68b7c715d9a89eabe3548;hpb=3020b224fa2d6e1b6b8a576e8e8e211e0585f2a2;p=ardour.git diff --git a/gtk2_ardour/audio_clock.cc b/gtk2_ardour/audio_clock.cc index 7bbc00c267..ebece6487c 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" @@ -38,13 +39,17 @@ #include "ardour_ui.h" #include "audio_clock.h" -#include "global_signals.h" -#include "utils.h" -#include "keyboard.h" +#include "enums_convert.h" #include "gui_thread.h" -#include "i18n.h" +#include "keyboard.h" +#include "tooltips.h" +#include "ui_config.h" +#include "utils.h" + +#include "pbd/i18n.h" using namespace ARDOUR; +using namespace ARDOUR_UI_UTILS; using namespace PBD; using namespace Gtk; using namespace std; @@ -53,28 +58,27 @@ using Gtkmm2ext::Keyboard; sigc::signal AudioClock::ModeChanged; vector AudioClock::clocks; -const double AudioClock::info_font_scale_factor = 0.50; -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)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) , em_width (0) , _edit_by_click_field (false) , _negative_allowed (false) , edit_is_negative (false) + , _limit_pos (INT64_MAX - 1) + , _with_info (with_info) , editing_attr (0) , foreground_attr (0) , first_height (0) @@ -82,9 +86,6 @@ AudioClock::AudioClock (const string& clock_name, bool transient, const string& , style_resets_first (true) , layout_height (0) , layout_width (0) - , info_height (0) - , upper_height (0) - , mode_based_info_ratio (1.0) , corner_radius (4) , font_size (10240) , editing (false) @@ -94,17 +95,14 @@ 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); _layout = Pango::Layout::create (get_pango_context()); _layout->set_attributes (normal_attributes); - if (with_info) { - _left_layout = Pango::Layout::create (get_pango_context()); - _right_layout = Pango::Layout::create (get_pango_context()); - } - set_widget_name (widget_name); _mode = BBT; /* lie to force mode switch */ @@ -115,8 +113,14 @@ AudioClock::AudioClock (const string& clock_name, bool transient, const string& clocks.push_back (this); } - ColorsChanged.connect (sigc::mem_fun (*this, &AudioClock::set_colors)); - DPIReset.connect (sigc::mem_fun (*this, &AudioClock::dpi_reset)); + _left_btn.set_sizing_text (_("0000000000000")); + // NB right_btn is in a size-group + + _left_btn.set_layout_font (UIConfiguration::instance().get_SmallFont()); + _right_btn.set_layout_font (UIConfiguration::instance().get_SmallFont()); + + UIConfiguration::instance().ColorsChanged.connect (sigc::mem_fun (*this, &AudioClock::set_colors)); + UIConfiguration::instance().DPIReset.connect (sigc::mem_fun (*this, &AudioClock::dpi_reset)); } AudioClock::~AudioClock () @@ -146,47 +150,28 @@ AudioClock::on_realize () Gtk::Requisition req; CairoWidget::on_realize (); - + set_clock_dimensions (req); first_width = req.width; first_height = req.height; - set_font (); + // 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); editing_attributes.change (*font_attr); - - /* now a smaller version of the same font */ - - delete font_attr; - font.set_size (INFO_FONT_SIZE); - font.set_weight (Pango::WEIGHT_NORMAL); - font_attr = new Pango::AttrFontDesc (Pango::Attribute::create_attr_font_desc (font)); - - info_attributes.change (*font_attr); - - /* and an even smaller one */ - delete font_attr; /* get the figure width for the font. This doesn't have to super @@ -222,15 +207,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 = UIConfiguration::instance().color (string_compose ("%1 active: background", get_name())); + text_color = UIConfiguration::instance().color (string_compose ("%1 active: text", get_name())); + editing_color = UIConfiguration::instance().color (string_compose ("%1 active: edited text", get_name())); + cursor_color = UIConfiguration::instance().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 = UIConfiguration::instance().color (string_compose ("%1: background", get_name())); + text_color = UIConfiguration::instance().color (string_compose ("%1: text", get_name())); + editing_color = UIConfiguration::instance().color (string_compose ("%1: edited text", get_name())); + cursor_color = UIConfiguration::instance().color (string_compose ("%1: cursor", get_name())); } /* store for bg and cursor in render() */ @@ -259,16 +244,17 @@ 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); - info_attributes.change (*foreground_attr); editing_attributes.change (*foreground_attr); editing_attributes.change (*editing_attr); @@ -282,101 +268,44 @@ 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::RefPtr const& ctx, cairo_rectangle_t*) { + cairo_t* cr = ctx->cobj(); /* main layout: rounded rect, plus the text */ if (_need_bg) { 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(), upper_height, corner_radius); - } else { - Gtkmm2ext::rounded_rectangle (cr, 0, 0, get_width(), upper_height, corner_radius); - } + Gtkmm2ext::rounded_rectangle (cr, 0, 0, get_width(), get_height(), corner_radius); } else { - cairo_rectangle (cr, 0, 0, get_width(), upper_height); + cairo_rectangle (cr, 0, 0, get_width(), get_height()); } cairo_fill (cr); } - cairo_move_to (cr, (get_width() - layout_width) / 2.0, (upper_height - layout_height) / 2.0); - - pango_cairo_show_layout (cr, _layout->gobj()); - - if (_left_layout) { - - double h = get_height() - upper_height - separator_height; - - if (_need_bg) { - cairo_set_source_rgba (cr, bg_r, bg_g, bg_b, bg_a); - } - - if (mode_based_info_ratio != 1.0) { - - double left_rect_width = round (((get_width() - separator_height) * mode_based_info_ratio) + 0.5); + double lw = layout_width * xscale; + double lh = layout_height * yscale; - 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); - } else { - cairo_rectangle (cr, 0, upper_height + separator_height, left_rect_width, h); - } - cairo_fill (cr); - } - - cairo_move_to (cr, x_leading_padding, upper_height + separator_height + ((h - info_height)/2.0)); - pango_cairo_show_layout (cr, _left_layout->gobj()); + cairo_move_to (cr, (get_width() - lw) / 2.0, (get_height() - lh) / 2.0); - if (_need_bg) { - 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, - h, corner_radius); - } else { - cairo_rectangle (cr, left_rect_width + separator_height, upper_height + separator_height, - get_width() - separator_height - left_rect_width, h); - } - cairo_fill (cr); - } - - - if (_right_layout->get_alignment() == Pango::ALIGN_RIGHT) { - /* right-align does not work per se beacuse layout width is unset. - * Using _right_layout->set_width([value >=0]) would also enable - * word-wrapping which is not wanted here. - * The solution is to custom align the layout depending on its size. - * if it is larger than the available space it will be cropped on the - * right edge rather than override text on the left side. - */ - int x, rw, rh; - _right_layout->get_pixel_size(rw, rh); - 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; - } - cairo_move_to (cr, x, upper_height + separator_height + ((h - info_height)/2.0)); - } else { - cairo_move_to (cr, x_leading_padding + left_rect_width + separator_height, upper_height + separator_height + ((h - info_height)/2.0)); - } - pango_cairo_show_layout (cr, _right_layout->gobj()); + if (xscale != 1.0 || yscale != 1.0) { + cairo_save (cr); + cairo_scale (cr, xscale, yscale); + } - } else { - /* no info to display, or just one */ + pango_cairo_show_layout (cr, _layout->gobj()); - if (_need_bg) { - if (corner_radius) { - Gtkmm2ext::rounded_bottom_half_rectangle (cr, 0, upper_height + separator_height, get_width(), h, corner_radius); - } else { - cairo_rectangle (cr, 0, upper_height + separator_height, get_width(), h); - } - cairo_fill (cr); - } - } + if (xscale != 1.0 || yscale != 1.0) { + cairo_restore (cr); } if (editing) { @@ -400,7 +329,7 @@ AudioClock::render (cairo_t* cr) cairo_rectangle (cr, min (get_width() - 2.0, (double) xcenter + cursor.get_x()/PANGO_SCALE + em_width), - (upper_height - layout_height)/2.0, + (get_height() - layout_height)/2.0, 2.0, cursor.get_height()/PANGO_SCALE); cairo_fill (cr); } else { @@ -412,26 +341,14 @@ AudioClock::render (cairo_t* cr) cairo_set_source_rgba (cr, cursor_r, cursor_g, cursor_b, cursor_a); cairo_rectangle (cr, (get_width()/2.0), - (upper_height - layout_height)/2.0, - 2.0, upper_height); + (get_height() - layout_height)/2.0, + 2.0, get_height()); cairo_fill (cr); } } } } -void -AudioClock::on_size_allocate (Gtk::Allocation& alloc) -{ - CairoWidget::on_size_allocate (alloc); - - if (_left_layout) { - upper_height = (get_height()/2.0) - 1.0; - } else { - upper_height = get_height(); - } -} - void AudioClock::set_clock_dimensions (Gtk::Requisition& req) { @@ -450,7 +367,10 @@ AudioClock::set_clock_dimensions (Gtk::Requisition& req) tmp->set_font_description (font); /* this string is the longest thing we will ever display */ - tmp->set_text (" 88:88:88,888"); + 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); layout_height = req.height; @@ -476,42 +396,6 @@ AudioClock::on_size_request (Gtk::Requisition* req) /* now tackle height, for which we need to know the height of the lower * layout */ - - 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 (INFO_FONT_SIZE); - font.set_weight (Pango::WEIGHT_NORMAL); - tmp->set_font_description (font); - - /* we only care about height, so put as much stuff in here - as possible that might change the height. - */ - tmp->set_text ("qyhH|"); /* one ascender, one descender */ - - tmp->get_pixel_size (w, info_height); - - /* silly extra padding that seems necessary to correct the info - * that pango just gave us. I have no idea why. - */ - - req->height += info_height; - req->height += separator_height; - } } void @@ -537,17 +421,17 @@ AudioClock::start_edit (Field f) 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 (); Keyboard::magic_widget_grab_focus (); @@ -644,7 +528,7 @@ AudioClock::end_edit (bool modify) case BBT: if (is_duration) { - pos = frame_duration_from_bbt_string (0, edit_string); + pos = frame_duration_from_bbt_string (bbt_reference_time, edit_string); } else { pos = frames_from_bbt_string (0, edit_string); } @@ -685,15 +569,8 @@ AudioClock::drop_focus () Keyboard::magic_widget_drop_focus (); if (has_focus()) { - /* move focus back to the default widget in the top level window */ - - Widget* top = get_toplevel(); - - if (top->is_toplevel ()) { - Window* win = dynamic_cast (top); - win->grab_focus (); - } + ARDOUR_UI::instance()->reset_focus (this); } } @@ -775,31 +652,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; @@ -943,36 +820,48 @@ AudioClock::set (framepos_t when, bool force, framecnt_t offset) when = when - offset; } + if (when > _limit_pos) { + when = _limit_pos; + } else if (when < -_limit_pos) { + when = -_limit_pos; + } + 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 } + bool btn_en = false; + if (!editing) { - if (_right_layout) { - _right_layout->set_alignment(Pango::ALIGN_LEFT); - } switch (_mode) { case Timecode: - if (_right_layout) { - _right_layout->set_alignment(Pango::ALIGN_RIGHT); - } set_timecode (when, force); break; case BBT: - set_bbt (when, force); + set_bbt (when, offset, force); + btn_en = true; break; case MinSec: - if (_right_layout) { - _right_layout->set_alignment(Pango::ALIGN_RIGHT); - } set_minsec (when, force); break; @@ -982,6 +871,28 @@ AudioClock::set (framepos_t when, bool force, framecnt_t offset) } } + if (_with_info) { + _left_btn.set_sensitive (btn_en); + _right_btn.set_sensitive (btn_en); + _left_btn.set_visual_state (Gtkmm2ext::NoVisualState); + _right_btn.set_visual_state (Gtkmm2ext::NoVisualState); + if (btn_en) { + _left_btn.set_elements (ArdourButton::Element(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text)); + _right_btn.set_elements (ArdourButton::Element(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text)); + _left_btn.set_alignment (.5, .5); + _right_btn.set_alignment (.5, .5); + set_tooltip (_left_btn, _("Change current tempo")); + set_tooltip (_right_btn, _("Change current time signature")); + } else { + _left_btn.set_elements (ArdourButton::Text); + _right_btn.set_elements (ArdourButton::Text); + _left_btn.set_alignment (0, .5); + _right_btn.set_alignment (1, .5); + set_tooltip (_left_btn, _("")); + set_tooltip (_right_btn, _("")); + } + } + queue_draw (); last_when = when; } @@ -989,7 +900,7 @@ AudioClock::set (framepos_t when, bool force, framecnt_t offset) void AudioClock::set_slave_info () { - if (!_left_layout || !_right_layout) { + if (!_with_info) { return; } @@ -1000,20 +911,16 @@ AudioClock::set_slave_info () switch (sync_src) { case Engine: - _left_layout->set_markup (string_compose ("" TXTSPAN "%2", - INFO_FONT_SIZE, sync_source_to_string(sync_src, true))); - _right_layout->set_text (""); + _left_btn.set_text (sync_source_to_string (sync_src, true), true); + _right_btn.set_text ("", true); break; case MIDIClock: if (slave) { - _left_layout->set_markup (string_compose ("" TXTSPAN "%2", - INFO_FONT_SIZE, sync_source_to_string(sync_src, true))); - _right_layout->set_markup (string_compose ("" TXTSPAN "%2", - INFO_FONT_SIZE, slave->approximate_current_delta())); + _left_btn.set_text (sync_source_to_string (sync_src, true), true); + _right_btn.set_text (slave->approximate_current_delta (), true); } else { - _left_layout->set_markup (string_compose ("" TXTSPAN "%2", - INFO_FONT_SIZE, _("--pending--"))); - _right_layout->set_text (""); + _left_btn.set_text (_("--pending--"), true); + _right_btn.set_text ("", true); } break; case LTC: @@ -1023,23 +930,41 @@ 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 ("" 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 ("" TXTSPAN "%2", - INFO_FONT_SIZE, slave->approximate_current_delta())); + _left_btn.set_text (string_compose ("%1%2", + sync_source_to_string(sync_src, true)[0], + dynamic_cast(slave)->approximate_current_position (), + matching ? "#66ff66" : "#ff3333" + ), true); + _right_btn.set_text (slave->approximate_current_delta (), true); } } else { - _left_layout->set_markup (string_compose ("" TXTSPAN "%2", - INFO_FONT_SIZE, _("--pending--"))); - _right_layout->set_text (""); + _left_btn.set_text (_("--pending--"), true); + _right_btn.set_text ("", true); } break; } } else { - _left_layout->set_markup (string_compose ("" TXTSPAN "INT/%2", - INFO_FONT_SIZE, sync_source_to_string(sync_src, true))); - _right_layout->set_text (""); + _left_btn.set_text (string_compose ("%1/%2", + _("INT"), sync_source_to_string(sync_src, true)), true); + _right_btn.set_text ("", true); + } +} + +void +AudioClock::set_out_of_bounds (bool negative) +{ + if (is_duration) { + if (negative) { + _layout->set_text (" >>> -- <<< "); + } else { + _layout->set_text (" >>> ++ <<< "); + } + } else { + if (negative) { + _layout->set_text (" <<<<<<<<<< "); + } else { + _layout->set_text (" >>>>>>>>>> "); + } } } @@ -1051,12 +976,8 @@ AudioClock::set_frames (framepos_t when, bool /*force*/) if (_off) { _layout->set_text (" ----------"); - - if (_left_layout) { - _left_layout->set_text (""); - _right_layout->set_text (""); - } - + _left_btn.set_text ("", true); + _right_btn.set_text ("", true); return; } @@ -1065,15 +986,17 @@ AudioClock::set_frames (framepos_t when, bool /*force*/) negative = true; } - if (negative) { + if (when >= _limit_pos) { + set_out_of_bounds (negative); + } else if (negative) { snprintf (buf, sizeof (buf), "-%10" PRId64, when); + _layout->set_text (buf); } else { snprintf (buf, sizeof (buf), " %10" PRId64, when); + _layout->set_text (buf); } - _layout->set_text (buf); - - if (_left_layout) { + if (_with_info) { framecnt_t rate = _session->frame_rate(); if (fmod (rate, 100.0) == 0.0) { @@ -1082,65 +1005,73 @@ AudioClock::set_frames (framepos_t when, bool /*force*/) sprintf (buf, "%" PRId64 "Hz", rate); } - _left_layout->set_markup (string_compose ("" TXTSPAN "%2 %3", - INFO_FONT_SIZE, _("SR"), buf)); + _left_btn.set_text (string_compose ("%1 %2", _("SR"), buf), true); float vid_pullup = _session->config.get_video_pullup(); if (vid_pullup == 0.0) { - _right_layout->set_markup (string_compose ("" TXTSPAN "%2 off", - INFO_FONT_SIZE, _("Pull"))); + _right_btn.set_text ("", true); } else { sprintf (buf, _("%+.4f%%"), vid_pullup); - _right_layout->set_markup (string_compose ("" TXTSPAN "%2 %3", - INFO_FONT_SIZE, _("Pull"), buf)); + _right_btn.set_text (string_compose ("%1 %2", _("Pull"), buf), true); } } } 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 (" --:--:--.---"); - - 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 ((double)(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 (" --:--:--.---"); + _left_btn.set_text ("", true); + _right_btn.set_text ("", true); + + return; + } + + if (when >= _limit_pos || when <= -_limit_pos) { + set_out_of_bounds (when < 0); + } else { + print_minsec (when, buf, sizeof (buf), _session->frame_rate()); + _layout->set_text (buf); } - _layout->set_text (buf); set_slave_info(); } @@ -1152,11 +1083,8 @@ AudioClock::set_timecode (framepos_t when, bool /*force*/) if (_off) { _layout->set_text (" --:--:--:--"); - if (_left_layout) { - _left_layout->set_text (""); - _right_layout->set_text (""); - } - + _left_btn.set_text ("", true); + _right_btn.set_text ("", true); return; } @@ -1164,6 +1092,11 @@ AudioClock::set_timecode (framepos_t when, bool /*force*/) when = -when; negative = true; } + if (when >= _limit_pos) { + set_out_of_bounds (negative); + set_slave_info(); + return; + } if (is_duration) { _session->timecode_duration (when, TC); @@ -1179,18 +1112,16 @@ AudioClock::set_timecode (framepos_t when, bool /*force*/) } void -AudioClock::set_bbt (framepos_t when, bool /*force*/) +AudioClock::set_bbt (framepos_t when, framecnt_t offset, bool /*force*/) { - char buf[16]; + char buf[64]; Timecode::BBT_Time BBT; bool negative = false; - if (_off) { + if (_off || when >= _limit_pos || when < -_limit_pos) { _layout->set_text (" ---|--|----"); - if (_left_layout) { - _left_layout->set_text (""); - _right_layout->set_text (""); - } + _left_btn.set_text ("", true); + _right_btn.set_text ("", true); return; } @@ -1206,12 +1137,46 @@ AudioClock::set_bbt (framepos_t when, bool /*force*/) BBT.beats = 0; BBT.ticks = 0; } else { - _session->tempo_map().bbt_time (when, BBT); - BBT.bars--; - BBT.beats--; + TempoMap& tmap (_session->tempo_map()); + + if (offset == 0) { + offset = bbt_reference_time; + } + + const double divisions = tmap.meter_section_at_frame (offset).divisions_per_bar(); + Timecode::BBT_Time sub_bbt; + + if (negative) { + BBT = tmap.bbt_at_beat (tmap.beat_at_frame (offset)); + sub_bbt = tmap.bbt_at_frame (offset - when); + } else { + BBT = tmap.bbt_at_beat (tmap.beat_at_frame (when + offset)); + sub_bbt = tmap.bbt_at_frame (offset); + } + + BBT.bars -= sub_bbt.bars; + + if (BBT.ticks < sub_bbt.ticks) { + if (BBT.beats == 1) { + BBT.bars--; + BBT.beats = divisions; + } else { + BBT.beats--; + } + BBT.ticks = Timecode::BBT_Time::ticks_per_beat - (sub_bbt.ticks - BBT.ticks); + } else { + BBT.ticks -= sub_bbt.ticks; + } + + if (BBT.beats < sub_bbt.beats) { + BBT.bars--; + BBT.beats = divisions - (sub_bbt.beats - BBT.beats); + } else { + BBT.beats -= sub_bbt.beats; + } } } else { - _session->tempo_map().bbt_time (when, BBT); + BBT = _session->tempo_map().bbt_at_frame (when); } if (negative) { @@ -1224,7 +1189,7 @@ AudioClock::set_bbt (framepos_t when, bool /*force*/) _layout->set_text (buf); - if (_right_layout) { + if (_with_info) { framepos_t pos; if (bbt_reference_time < 0) { @@ -1235,13 +1200,19 @@ AudioClock::set_bbt (framepos_t when, bool /*force*/) TempoMetric m (_session->tempo_map().metric_at (pos)); - sprintf (buf, "%-5.1f", m.tempo().beats_per_minute()); - _left_layout->set_markup (string_compose ("" TXTSPAN "%3 %2", - INFO_FONT_SIZE, buf, _("Tempo"))); + if (m.tempo().note_type() == 4) { + snprintf (buf, sizeof(buf), "\u2669 = %.3f", _session->tempo_map().tempo_at_frame (pos).note_types_per_minute()); + _left_btn.set_text (string_compose ("%1", buf), true); + } else if (m.tempo().note_type() == 8) { + snprintf (buf, sizeof(buf), "\u266a = %.3f", _session->tempo_map().tempo_at_frame (pos).note_types_per_minute()); + _left_btn.set_text (string_compose ("%1", buf), true); + } else { + snprintf (buf, sizeof(buf), "%.1f = %.3f", m.tempo().note_type(), _session->tempo_map().tempo_at_frame (pos).note_types_per_minute()); + _left_btn.set_text (string_compose ("%1: %2", S_("Tempo|T"), buf), true); + } - sprintf (buf, "%g/%g", m.meter().divisions_per_bar(), m.meter().note_divisor()); - _right_layout->set_markup (string_compose ("" TXTSPAN "%3 %2", - INFO_FONT_SIZE, buf, _("Meter"))); + snprintf (buf, sizeof(buf), "%g/%g", m.meter().divisions_per_bar(), m.meter().note_divisor()); + _right_btn.set_text (string_compose ("%1: %2", S_("TimeSignature|TS"), buf), true); } } @@ -1252,23 +1223,30 @@ AudioClock::set_session (Session *s) if (_session) { + int64_t limit_sec = UIConfiguration::instance().get_clock_display_limit (); + if (limit_sec > 0) { + _limit_pos = (framecnt_t) floor ((double)(limit_sec * _session->frame_rate())); + } + + Config->ParameterChanged.connect (_session_connections, invalidator (*this), boost::bind (&AudioClock::session_configuration_changed, this, _1), gui_context()); _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()); + _session->tempo_map().MetricPositionChanged.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")); - AudioClock::Mode amode; if (node) { for (XMLNodeList::const_iterator i = node->children().begin(); i != node->children().end(); ++i) { - if ((prop = (*i)->property (X_("name"))) && prop->value() == _name) { + std::string name; + if ((*i)->get_property (X_("name"), name) && name == _name) { - if ((prop = (*i)->property (X_("mode"))) != 0) { - amode = AudioClock::Mode (string_2_enum (prop->value(), amode)); - set_mode (amode); + AudioClock::Mode amode; + if ((*i)->get_property (X_("mode"), amode)) { + set_mode (amode, true); } - if ((prop = (*i)->property (X_("on"))) != 0) { - set_off (!string_is_affirmative (prop->value())); + bool on; + if ((*i)->get_property (X_("on"), on)) { + set_off (!on); } break; } @@ -1373,7 +1351,10 @@ AudioClock::on_key_press_event (GdkEventKey* ev) goto use_input_string; default: - return false; + /* do not allow other keys to passthru to the rest of the GUI + when editing. + */ + return true; } if (!insert_map.empty() && (input_string.length() >= insert_map.size())) { @@ -1552,7 +1533,7 @@ AudioClock::on_button_press_event (GdkEventButton *ev) */ int xcenter = (get_width() - layout_width) /2; - y = ev->y - ((upper_height - layout_height)/2); + y = ev->y - ((get_height() - layout_height)/2); x = ev->x - xcenter; if (!_layout->xy_to_index (x * PANGO_SCALE, y * PANGO_SCALE, index, trailing)) { @@ -1598,7 +1579,7 @@ AudioClock::on_button_release_event (GdkEventButton *ev) int xcenter = (get_width() - layout_width) /2; int index = 0; int trailing; - int y = ev->y - ((upper_height - layout_height)/2); + int y = ev->y - ((get_height() - layout_height)/2); int x = ev->x - xcenter; Field f; @@ -1643,7 +1624,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; @@ -1667,7 +1648,7 @@ AudioClock::on_scroll_event (GdkEventScroll *ev) */ int xcenter = (get_width() - layout_width) /2; - y = ev->y - ((upper_height - layout_height)/2); + y = ev->y - ((get_height() - layout_height)/2); x = ev->x - xcenter; if (!_layout->xy_to_index (x * PANGO_SCALE, y * PANGO_SCALE, index, trailing)) { @@ -1681,7 +1662,7 @@ AudioClock::on_scroll_event (GdkEventScroll *ev) switch (ev->direction) { case GDK_SCROLL_UP: - frames = get_frame_step (f); + frames = get_frame_step (f, current_time(), 1); if (frames != 0) { if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) { frames *= 10; @@ -1692,7 +1673,7 @@ AudioClock::on_scroll_event (GdkEventScroll *ev) break; case GDK_SCROLL_DOWN: - frames = get_frame_step (f); + frames = get_frame_step (f, current_time(), -1); if (frames != 0) { if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) { frames *= 10; @@ -2037,6 +2018,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 () { @@ -2045,18 +2044,18 @@ AudioClock::build_ops_menu () MenuList& ops_items = ops_menu->items(); ops_menu->set_name ("ArdourContextMenu"); - if (!Profile->get_sae()) { - ops_items.push_back (MenuElem (_("Timecode"), sigc::bind (sigc::mem_fun(*this, &AudioClock::set_mode), Timecode))); - } - ops_items.push_back (MenuElem (_("Bars:Beats"), sigc::bind (sigc::mem_fun(*this, &AudioClock::set_mode), BBT))); - ops_items.push_back (MenuElem (_("Minutes:Seconds"), sigc::bind (sigc::mem_fun(*this, &AudioClock::set_mode), MinSec))); - ops_items.push_back (MenuElem (_("Samples"), sigc::bind (sigc::mem_fun(*this, &AudioClock::set_mode), Frames))); + ops_items.push_back (MenuElem (_("Timecode"), sigc::bind (sigc::mem_fun(*this, &AudioClock::set_mode), Timecode, false))); + ops_items.push_back (MenuElem (_("Bars:Beats"), sigc::bind (sigc::mem_fun(*this, &AudioClock::set_mode), BBT, false))); + ops_items.push_back (MenuElem (_("Minutes:Seconds"), sigc::bind (sigc::mem_fun(*this, &AudioClock::set_mode), MinSec, false))); + ops_items.push_back (MenuElem (_("Samples"), sigc::bind (sigc::mem_fun(*this, &AudioClock::set_mode), Frames, false))); if (editable && !_off && !is_duration && !_follows_playhead) { ops_items.push_back (SeparatorElem()); - ops_items.push_back (MenuElem (_("Set From Playhead"), sigc::mem_fun(*this, &AudioClock::set_from_playhead))); + 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 @@ -2081,7 +2080,7 @@ AudioClock::locate () } void -AudioClock::set_mode (Mode m) +AudioClock::set_mode (Mode m, bool noemit) { if (_mode == m) { return; @@ -2093,22 +2092,11 @@ AudioClock::set_mode (Mode m) _layout->set_text (""); - if (_left_layout) { - - _left_layout->set_attributes (info_attributes); - _right_layout->set_attributes (info_attributes); - /* adjust info_height according to font size */ - int ignored; - _left_layout->set_text (" 1234567890"); - _left_layout->get_pixel_size (ignored, info_height); - - _left_layout->set_text (""); - _right_layout->set_text (""); - } + Gtk::Requisition req; + set_clock_dimensions (req); switch (_mode) { case Timecode: - mode_based_info_ratio = 0.6; insert_map.push_back (11); insert_map.push_back (10); insert_map.push_back (8); @@ -2120,7 +2108,6 @@ AudioClock::set_mode (Mode m) break; case BBT: - mode_based_info_ratio = 0.5; insert_map.push_back (11); insert_map.push_back (10); insert_map.push_back (9); @@ -2133,7 +2120,6 @@ AudioClock::set_mode (Mode m) break; case MinSec: - mode_based_info_ratio = 0.6; insert_map.push_back (12); insert_map.push_back (11); insert_map.push_back (10); @@ -2146,17 +2132,16 @@ AudioClock::set_mode (Mode m) break; case Frames: - mode_based_info_ratio = 0.45; break; } set (last_when, true); - if (!is_transient) { - ModeChanged (); /* EMIT SIGNAL (the static one)*/ - } + if (!is_transient && !noemit) { + ModeChanged (); /* EMIT SIGNAL (the static one)*/ + } - mode_changed (); /* EMIT SIGNAL (the member one) */ + mode_changed (); /* EMIT SIGNAL (the member one) */ } void @@ -2173,7 +2158,8 @@ AudioClock::on_style_changed (const Glib::RefPtr& old_style) Gtk::Requisition req; set_clock_dimensions (req); - set_font (); + /* XXXX fix me ... we shouldn't be using GTK styles anyway */ + // set_font (); set_colors (); }