#include <gtkmm/style.h>
#include <sigc++/bind.h>
-#include "gtkmm2ext/cairocell.h"
#include "gtkmm2ext/utils.h"
#include "gtkmm2ext/rgb_macros.h"
+#include "widgets/tooltips.h"
+
#include "ardour/profile.h"
+#include "ardour/lmath.h"
#include "ardour/session.h"
#include "ardour/slave.h"
#include "ardour/tempo.h"
#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 "ui_config.h"
+#include "utils.h"
+
+#include "pbd/i18n.h"
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
+using namespace ArdourWidgets;
using namespace PBD;
using namespace Gtk;
using namespace std;
sigc::signal<void> AudioClock::ModeChanged;
vector<AudioClock*> 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 "<span font-family=\"Sans\" foreground=\"white\">"
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)
, 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)
, 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 */
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 ()
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<Gtk::Style> 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
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() */
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);
}
void
-AudioClock::render (cairo_t* cr)
+AudioClock::set_scale (double x, double y)
{
+ xscale = x;
+ yscale = y;
+
+ queue_draw ();
+}
+
+void
+AudioClock::render (Cairo::RefPtr<Cairo::Context> 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 lw = layout_width * xscale;
+ double lh = layout_height * yscale;
- double h = get_height() - upper_height - separator_height;
+ cairo_move_to (cr, (get_width() - lw) / 2.0, (get_height() - lh) / 2.0);
- 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);
-
- 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());
-
- 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) {
cairo_set_source_rgba (cr, cursor_r, cursor_g, cursor_b, cursor_a);
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);
+ min (get_width() - 2.0,
+ (double) xcenter + cursor.get_x()/PANGO_SCALE + em_width),
+ (get_height() - layout_height)/2.0,
+ 2.0, cursor.get_height()/PANGO_SCALE);
cairo_fill (cr);
} else {
/* we've entered all possible digits, no cursor */
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)
{
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;
/* now tackle height, for which we need to know the height of the lower
* layout
*/
-
- if (_left_layout) {
-
- Glib::RefPtr<Pango::Layout> tmp;
- Glib::RefPtr<Gtk::Style> 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
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 ();
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);
}
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<Window*> (top);
- win->grab_focus ();
- }
+ ARDOUR_UI::instance()->reset_focus (this);
}
}
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;
void
AudioClock::set (framepos_t when, bool force, framecnt_t offset)
{
- if ((!force && !is_visible()) || _session == 0) {
+ if ((!force && !is_visible()) || _session == 0) {
return;
}
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;
}
}
+ 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;
}
void
AudioClock::set_slave_info ()
{
- if (!_left_layout || !_right_layout) {
+ if (!_with_info) {
return;
}
switch (sync_src) {
case Engine:
- _left_layout->set_markup (string_compose ("<span size=\"%1\">" TXTSPAN "%2</span></span>",
- 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 ("<span size=\"%1\">" TXTSPAN "%2</span></span>",
- INFO_FONT_SIZE, sync_source_to_string(sync_src, true)));
- _right_layout->set_markup (string_compose ("<span size=\"%1\">" TXTSPAN "%2</span></span>",
- 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 ("<span size=\"%1\">" TXTSPAN "%2</span></span>",
- INFO_FONT_SIZE, _("--pending--")));
- _right_layout->set_text ("");
+ _left_btn.set_text (_("--pending--"), true);
+ _right_btn.set_text ("", true);
}
break;
case LTC:
TimecodeSlave* tcslave;
if ((tcslave = dynamic_cast<TimecodeSlave*>(_session->slave())) != 0) {
matching = (tcslave->apparent_timecode_format() == _session->config.get_timecode_format());
- _left_layout->set_markup (string_compose ("<span size=\"%1\">" TXTSPAN "%2</span><span foreground=\"%3\">%4</span></span>",
- INFO_FONT_SIZE, sync_source_to_string(sync_src, true)[0], (matching?"green":"red"),
- dynamic_cast<TimecodeSlave*>(slave)->approximate_current_position()));
- _right_layout->set_markup (string_compose ("<span size=\"%1\">" TXTSPAN "%2</span></span>",
- INFO_FONT_SIZE, slave->approximate_current_delta()));
+ _left_btn.set_text (string_compose ("%1<span face=\"monospace\" foreground=\"%3\">%2</span>",
+ sync_source_to_string(sync_src, true)[0],
+ dynamic_cast<TimecodeSlave*>(slave)->approximate_current_position (),
+ matching ? "#66ff66" : "#ff3333"
+ ), true);
+ _right_btn.set_text (slave->approximate_current_delta (), true);
}
} else {
- _left_layout->set_markup (string_compose ("<span size=\"%1\">" TXTSPAN "%2</span></span>",
- 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 ("<span size=\"%1\">" TXTSPAN "INT/%2</span></span>",
- 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 (" >>>>>>>>>> ");
+ }
}
}
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;
}
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) {
sprintf (buf, "%" PRId64 "Hz", rate);
}
- _left_layout->set_markup (string_compose ("<span size=\"%1\">" TXTSPAN "%2 </span><span foreground=\"green\">%3</span></span>",
- 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 ("<span size=\"%1\">" TXTSPAN "%2 </span><span foreground=\"green\">off</span></span>",
- INFO_FONT_SIZE, _("Pull")));
+ _right_btn.set_text ("", true);
} else {
sprintf (buf, _("%+.4f%%"), vid_pullup);
- _right_layout->set_markup (string_compose ("<span size=\"%1\">" TXTSPAN "%2 </span><span foreground=\"green\">%3</span></span>",
- 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();
}
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;
}
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);
}
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;
}
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) {
_layout->set_text (buf);
- if (_right_layout) {
+ if (_with_info) {
framepos_t pos;
if (bbt_reference_time < 0) {
TempoMetric m (_session->tempo_map().metric_at (pos));
- sprintf (buf, "%-5.1f", m.tempo().beats_per_minute());
- _left_layout->set_markup (string_compose ("<span size=\"%1\">" TXTSPAN "%3</span> <span foreground=\"green\">%2</span></span>",
- 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 ("<span size=\"%1\">" TXTSPAN "%3</span> <span foreground=\"green\">%2</span></span>",
- 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);
}
}
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;
}
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())) {
bool
AudioClock::on_button_press_event (GdkEventButton *ev)
{
+ if (!_session || _session->actively_recording()) {
+ /* swallow event, do nothing */
+ return true;
+ }
+
switch (ev->button) {
case 1:
if (editable && !_off) {
*/
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)) {
bool
AudioClock::on_button_release_event (GdkEventButton *ev)
{
+ if (!_session || _session->actively_recording()) {
+ /* swallow event, do nothing */
+ return true;
+ }
+
if (editable && !_off) {
if (dragging) {
gdk_pointer_ungrab (GDK_CURRENT_TIME);
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;
bool ret = CairoWidget::on_focus_out_event (ev);
if (editing) {
- end_edit (false);
+ end_edit (_accept_on_focus_out);
}
return ret;
int index;
int trailing;
- if (editing || _session == 0 || !editable || _off) {
+ if (editing || _session == 0 || !editable || _off || _session->actively_recording()) {
return false;
}
*/
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)) {
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;
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;
bool
AudioClock::on_motion_notify_event (GdkEventMotion *ev)
{
- if (editing || _session == 0 || !dragging) {
+ if (editing || _session == 0 || !dragging || _session->actively_recording()) {
return false;
}
if (Keyboard::modifier_state_contains (ev->state,
- Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) {
-
+ Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) {
pixel_frame_scale_factor = 0.025f;
}
set ((framepos_t) floor (pos - drag_accum * frames), false); // minus because up is negative in GTK
} else {
set (0 , false);
- }
+ }
- drag_accum= 0;
+ drag_accum= 0;
ValueChanged(); /* EMIT_SIGNAL */
}
char ignored[2];
if (sscanf (_layout->get_text().c_str(), "%[- _]%" PRId32 ":%" PRId32 ":%" PRId32 "%[:;]%" PRId32,
- ignored, &hours, &TC.minutes, &TC.seconds, ignored, &TC.frames) != 6) {
+ ignored, &hours, &TC.minutes, &TC.seconds, ignored, &TC.frames) != 6) {
return false;
}
if (is_duration) {
any.bbt.bars++;
any.bbt.beats++;
- return _session->any_duration_to_frames (pos, any);
+ return _session->any_duration_to_frames (pos, any);
} else {
- return _session->convert_to_frames (any);
- }
+ return _session->convert_to_frames (any);
+ }
}
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<Clipboard> cl = Gtk::Clipboard::get();
+ cl->set_text (val.substr(trim));
+}
+
void
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
}
void
-AudioClock::set_mode (Mode m)
+AudioClock::set_mode (Mode m, bool noemit)
{
if (_mode == m) {
return;
_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);
break;
case BBT:
- mode_based_info_ratio = 0.5;
insert_map.push_back (11);
insert_map.push_back (10);
insert_map.push_back (9);
break;
case MinSec:
- mode_based_info_ratio = 0.6;
insert_map.push_back (12);
insert_map.push_back (11);
insert_map.push_back (10);
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
Gtk::Requisition req;
set_clock_dimensions (req);
- set_font ();
+ /* XXXX fix me ... we shouldn't be using GTK styles anyway */
+ // set_font ();
set_colors ();
}