diff --git a/gtk2_ardour/mini_timeline.cc b/gtk2_ardour/mini_timeline.cc
index f9f5cd6a6d..676107ae88 100644
--- a/gtk2_ardour/mini_timeline.cc
+++ b/gtk2_ardour/mini_timeline.cc
@@ -24,6 +24,7 @@
#include "gtkmm2ext/colors.h"
#include "gtkmm2ext/gui_thread.h"
#include "gtkmm2ext/keyboard.h"
+#include "gtkmm2ext/utils.h"
#include "widgets/tooltips.h"
@@ -40,6 +41,7 @@
#define BBT_BAR_CHAR "|"
using namespace ARDOUR;
+using namespace Gtkmm2ext;
MiniTimeline::MiniTimeline ()
: _last_update_sample (-1)
@@ -67,8 +69,11 @@ MiniTimeline::MiniTimeline ()
Location::name_changed.connect (marker_connection, invalidator (*this), boost::bind (&MiniTimeline::update_minitimeline, this), gui_context ());
Location::end_changed.connect (marker_connection, invalidator (*this), boost::bind (&MiniTimeline::update_minitimeline, this), gui_context ());
Location::start_changed.connect (marker_connection, invalidator (*this), boost::bind (&MiniTimeline::update_minitimeline, this), gui_context ());
+ Location::changed.connect (marker_connection, invalidator (*this), boost::bind (&MiniTimeline::update_minitimeline, this), gui_context ());
Location::flags_changed.connect (marker_connection, invalidator (*this), boost::bind (&MiniTimeline::update_minitimeline, this), gui_context ());
+ Temporal::TempoMap::MapChanged.connect (tempo_map_connection, invalidator (*this), boost::bind (&MiniTimeline::update_minitimeline, this), gui_context());
+
ArdourWidgets::set_tooltip (*this,
string_compose (_("Navigation Timeline. Use left-click to locate to time position or marker; scroll-wheel to jump, hold %1 for fine grained and %2 + %3 for extra-fine grained control. Right-click to set display range. The display unit is defined by the primary clock."),
Gtkmm2ext::Keyboard::primary_modifier_name(),
@@ -204,11 +209,6 @@ MiniTimeline::super_rapid_update ()
change = true;
}
- if (_clock_mode == AudioClock::BBT) {
- // TODO check if tempo-map changed
- change = true;
- }
-
if (change) {
_last_update_sample = sample;
update_minitimeline ();
@@ -339,7 +339,7 @@ MiniTimeline::draw_mark (cairo_t* cr, int x0, int x1, const std::string& label,
const int y = PADDING;
int w2 = (h - 1) / 4;
- double h0 = h * .4;
+ double h0 = h * .6;
double h1 = h - h0;
int lw, lh;
@@ -351,30 +351,14 @@ MiniTimeline::draw_mark (cairo_t* cr, int x0, int x1, const std::string& label,
prelight = true;
}
- // TODO cache in set_colors()
+ const double scale = UIConfiguration::instance ().get_ui_scale ();
uint32_t color = UIConfiguration::instance().color (
- prelight ? "entered marker" : "location marker");
+ prelight ? "entered marker" : "location marker");
- double r, g, b, a;
- Gtkmm2ext::color_to_rgba (color, r, g, b, a);
+ //shrink the height of the 'flag' part, a bit.
+ h -= 4*scale;
- if (rw < x0) {
- rw = x1;
- } else {
- cairo_save (cr);
- cairo_rectangle (cr, x0, y, rw - x0, h);
- cairo_set_source_rgba (cr, r, g, b, 0.5); // this should use a shaded color
- cairo_fill_preserve (cr);
- cairo_clip (cr);
-
- // marker label
- cairo_move_to (cr, x0 + w2, y + .5 * (h - lh));
- cairo_set_source_rgb (cr, 0, 0, 0);
- pango_cairo_show_layout (cr, _layout->gobj());
- cairo_restore (cr);
- }
-
- // draw marker on top
+ // draw marker first
cairo_move_to (cr, x0 - .5, y + .5);
cairo_rel_line_to (cr, -w2 , 0);
cairo_rel_line_to (cr, 0, h0);
@@ -382,14 +366,67 @@ MiniTimeline::draw_mark (cairo_t* cr, int x0, int x1, const std::string& label,
cairo_rel_line_to (cr, w2, -h1);
cairo_rel_line_to (cr, 0, -h0);
cairo_close_path (cr);
- cairo_set_source_rgba (cr, r, g, b, 1.0);
+ set_source_rgba (cr, HSV (color).darker (0.35).color ());
cairo_set_line_width (cr, 1.0);
cairo_stroke_preserve (cr);
cairo_fill (cr);
+ if (rw < x0) {
+ rw = x1;
+ } else {
+ cairo_save (cr);
+ set_source_rgba (cr, HSV (color).darker (0.35).color ());
+ cairo_rectangle (cr, x0-5*scale, y, rw - x0 + 4*scale, h);
+ cairo_fill_preserve (cr);
+ cairo_clip (cr);
+
+ // marker label
+ cairo_move_to (cr, x0 + w2 - 4*scale, y + .5 * (h - lh));
+ cairo_set_source_rgb (cr, 0, 0, 0);
+ pango_cairo_show_layout (cr, _layout->gobj());
+ cairo_restore (cr);
+
+ /* right line */
+ cairo_rectangle (cr, rw-2*scale, y, 1*scale, h);
+ cairo_set_source_rgba (cr, 0, 0, 0, 0.7);
+ cairo_fill (cr);
+ }
+
return rw;
}
+void
+MiniTimeline::draw_cue (cairo_t* cr, int x0, int x1, int cue_index, bool& prelight)
+{
+ int y_offs = _marker_height * 1.4; //make room for 'regular' markers
+
+ int h = _marker_height;
+ x0 -= h/2; //left side of circle
+ x1 = x0 + h; //right side of circle
+
+ if (_pointer_y >= y_offs && _pointer_y <= y_offs+h && _pointer_x >= x0 && _pointer_x <= x1) {
+ prelight = true;
+ }
+
+ const double scale = UIConfiguration::instance ().get_ui_scale ();
+ uint32_t color = UIConfiguration::instance().color (
+ prelight ? "entered marker" : "location marker");
+
+ // draw Cue as a circle
+ cairo_arc(cr, x0+h/2, y_offs+h/2, (h/2)-1*scale, 0, 2*M_PI);
+ set_source_rgba (cr, HSV (color).darker (0.35).color ());
+ cairo_fill (cr);
+
+ //draw cue letter
+ _layout->set_text (string_compose (_("%1"), (char) ('A' + cue_index)));
+ cairo_set_source_rgb (cr, 0, 0, 0); //black
+ cairo_move_to (cr, x0 + h/2, y_offs+h/2); //move to center of circle
+ int tw, th;
+ _layout->get_pixel_size (tw, th);
+ cairo_rel_move_to (cr, -tw/2, -th/2); //move to top-left of text
+ pango_cairo_show_layout (cr, _layout->gobj());
+}
+
int
MiniTimeline::draw_edge (cairo_t* cr, int x0, int x1, bool left, const std::string& label, bool& prelight)
{
@@ -475,8 +512,9 @@ MiniTimeline::draw_edge (cairo_t* cr, int x0, int x1, bool left, const std::stri
struct LocationMarker {
- LocationMarker (const std::string& l, Temporal::timepos_t const & w)
- : label (l), when (w) {}
+ LocationMarker (int idx, const std::string& l, Temporal::timepos_t const & w)
+ : cue_index(idx), label (l), when (w) {}
+ int cue_index;
std::string label;
Temporal::timepos_t when;
};
@@ -518,13 +556,13 @@ MiniTimeline::render (Cairo::RefPtr const& ctx, cairo_rectangle_
}
/* time */
- const samplepos_t p = _last_update_sample;
- const samplepos_t lower = (std::max ((samplepos_t)0, (p - _time_span_samples)) / _time_granularity) * _time_granularity;
+ const samplepos_t phead = _last_update_sample; //playhead location
+ const samplepos_t lower = (std::max ((samplepos_t)0, (phead - _time_span_samples)) / _time_granularity) * _time_granularity;
- int dot_left = width * .5 + (lower - p) * _px_per_sample;
+ int dot_left = width * .5 + (lower - phead) * _px_per_sample;
for (int i = 0; i < 2 + _n_labels; ++i) {
samplepos_t when = lower + i * _time_granularity;
- double xpos = width * .5 + (when - p) * _px_per_sample;
+ double xpos = width * .5 + (when - phead) * _px_per_sample;
// TODO round to nearest display TC in +/- 1px
// prefer to display BBT |0 or .0
@@ -545,9 +583,24 @@ MiniTimeline::render (Cairo::RefPtr const& ctx, cairo_rectangle_
}
draw_dots (cr, dot_left, width, height - PADDING - _time_height * .5, text);
+ /* playhead beneath locations */
+ int xc = width * 0.5f;
+ cairo_set_line_width (cr, 1.0);
+ double r,g,b,a; Gtkmm2ext::color_to_rgba(_phead_color, r,g,b,a);
+ cairo_set_source_rgb (cr, r,g,b); // playhead color
+ cairo_move_to (cr, xc - .5, 0);
+ cairo_rel_line_to (cr, 0, height);
+ cairo_stroke (cr);
+ cairo_move_to (cr, xc - .5, height);
+ cairo_rel_line_to (cr, -3, 0);
+ cairo_rel_line_to (cr, 3, -4);
+ cairo_rel_line_to (cr, 3, 4);
+ cairo_close_path (cr);
+ cairo_fill (cr);
+
/* locations */
- samplepos_t lmin = std::max ((samplepos_t)0, (p - _time_span_samples));
- samplepos_t lmax = p + _time_span_samples;
+ samplepos_t lmin = std::max ((samplepos_t)0, (phead - _time_span_samples));
+ samplepos_t lmax = phead + _time_span_samples;
int tw, th;
_layout->set_text (X_("Marker@"));
@@ -565,8 +618,8 @@ MiniTimeline::render (Cairo::RefPtr const& ctx, cairo_rectangle_
const Locations::LocationList& ll (_session->locations ()->list ());
for (Locations::LocationList::const_iterator l = ll.begin(); l != ll.end(); ++l) {
if ((*l)->is_session_range ()) {
- lm.push_back (LocationMarker(_("start"), (*l)->start ()));
- lm.push_back (LocationMarker(_("end"), (*l)->end ()));
+ lm.push_back (LocationMarker(-1, _("start"), (*l)->start ()));
+ lm.push_back (LocationMarker(-1, _("end"), (*l)->end ()));
continue;
}
@@ -574,7 +627,8 @@ MiniTimeline::render (Cairo::RefPtr const& ctx, cairo_rectangle_
continue;
}
- lm.push_back (LocationMarker((*l)->name(), (*l)->start ()));
+ int cue_idx = (*l)->is_cue_marker () ? (*l)->cue_id() : -1;
+ lm.push_back (LocationMarker(cue_idx, (*l)->name(), (*l)->start ()));
}
_jumplist.clear ();
@@ -593,7 +647,7 @@ MiniTimeline::render (Cairo::RefPtr const& ctx, cairo_rectangle_
if (when < lmin) {
outside_left = l;
if (++l != lm.end()) {
- left_limit = floor (width * .5 + (when - p) * _px_per_sample) - 1 - mw;
+ left_limit = floor (width * .5 + (when - phead) * _px_per_sample) - 1 - mw;
} else {
left_limit = width * .5 - mw;
}
@@ -603,16 +657,31 @@ MiniTimeline::render (Cairo::RefPtr const& ctx, cairo_rectangle_
outside_right = l;
break;
}
- int x0 = floor (width * .5 + (when - p) * _px_per_sample);
+ int x0 = floor (width * .5 + (when - phead) * _px_per_sample);
int x1 = width;
const std::string& label = (*l).label;
- if (++l != lm.end()) {
- x1 = floor (width * .5 + (when - p) * _px_per_sample) - 1 - mw;
+ const int cue_index = (*l).cue_index;
+
+ std::vector::const_iterator peek = l;
+ for (peek++; peek != lm.end(); peek++) {
+ if ((*peek).cue_index == -1) {
+ x1 = floor (width * .5 + ((*peek).when.samples() - phead) * _px_per_sample) - 1 - mw;
+ break;
+ }
}
+
+ //draw the mark
bool prelight = false;
- x1 = draw_mark (cr, x0, x1, label, prelight);
- _jumplist.push_back (JumpRange (x0 - mw, x1, when, prelight));
+ if (cue_index >= 0) {
+ draw_cue (cr, x0, x1, cue_index, prelight);
+ } else {
+ x1 = draw_mark (cr, x0, x1, label, prelight);
+ }
+
+ _jumplist.push_back (JumpRange (when, prelight));
right_limit = std::max (x1, right_limit);
+
+ l++;
}
if (outside_left != lm.end ()) {
@@ -622,7 +691,7 @@ MiniTimeline::render (Cairo::RefPtr const& ctx, cairo_rectangle_
bool prelight = false;
x1 = draw_edge (cr, x0, x1, true, (*outside_left).label, prelight);
if (x0 != x1) {
- _jumplist.push_back (JumpRange (x0, x1, (*outside_left).when.samples(), prelight));
+ _jumplist.push_back (JumpRange ((*outside_left).when.samples(), prelight));
right_limit = std::max (x1, right_limit);
}
}
@@ -635,27 +704,12 @@ MiniTimeline::render (Cairo::RefPtr const& ctx, cairo_rectangle_
bool prelight = false;
x0 = draw_edge (cr, x0, x1, false, (*outside_right).label, prelight);
if (x0 != x1) {
- _jumplist.push_back (JumpRange (x0, x1, (*outside_right).when.samples(), prelight));
+ _jumplist.push_back (JumpRange ((*outside_right).when.samples(), prelight));
}
}
}
- /* playhead on top */
- int xc = width * 0.5f;
- cairo_set_line_width (cr, 1.0);
- double r,g,b,a; Gtkmm2ext::color_to_rgba(_phead_color, r,g,b,a);
- cairo_set_source_rgb (cr, r,g,b); // playhead color
- cairo_move_to (cr, xc - .5, 0);
- cairo_rel_line_to (cr, 0, height);
- cairo_stroke (cr);
- cairo_move_to (cr, xc - .5, height);
- cairo_rel_line_to (cr, -3, 0);
- cairo_rel_line_to (cr, 3, -4);
- cairo_rel_line_to (cr, 3, 4);
- cairo_close_path (cr);
- cairo_fill (cr);
-
cairo_pop_group_to_source (cr);
cairo_paint (cr);
}
@@ -711,16 +765,17 @@ MiniTimeline::on_button_release_event (GdkEventButton *ev)
{
if (!_session) { return true; }
if (_session->actively_recording ()) { return true; }
+
+ /* check that the release is still inside the timeline */
if (ev->y < 0 || ev->y > get_height () || ev->x < 0 || ev->x > get_width ()) {
return true;
}
- if (ev->y <= PADDING + _marker_height) {
- for (JumpList::const_iterator i = _jumplist.begin (); i != _jumplist.end(); ++i) {
- if (i->left <= ev->x && ev->x <= i->right) {
- _session->request_locate (i->to);
- return true;
- }
+ /* check whether any marker was prelighted; if so, that's where the user will expect to jump */
+ for (JumpList::const_iterator i = _jumplist.begin (); i != _jumplist.end(); ++i) {
+ if (i->prelight) {
+ _session->request_locate (i->to);
+ return true;
}
}
@@ -743,22 +798,7 @@ MiniTimeline::on_motion_notify_event (GdkEventMotion *ev)
bool need_expose = false;
- for (JumpList::const_iterator i = _jumplist.begin (); i != _jumplist.end(); ++i) {
- if (i->left < ev->x && ev->x < i->right && ev->y <= PADDING + _marker_height) {
- if (!(*i).prelight) {
- need_expose = true;
- break;
- }
- } else {
- if ((*i).prelight) {
- need_expose = true;
- break;
- }
- }
- }
- if (need_expose) {
- update_minitimeline ();
- }
+ update_minitimeline ();
return true;
}
diff --git a/gtk2_ardour/mini_timeline.h b/gtk2_ardour/mini_timeline.h
index 5ab3551828..65a8743ee3 100644
--- a/gtk2_ardour/mini_timeline.h
+++ b/gtk2_ardour/mini_timeline.h
@@ -66,6 +66,7 @@ private:
void update_minitimeline ();
void draw_dots (cairo_t*, int left, int right, int y, Gtkmm2ext::Color);
int draw_mark (cairo_t*, int x0, int x1, const std::string&, bool& prelight);
+ void draw_cue (cairo_t*, int x0, int x1, int cue_index, bool& prelight);
int draw_edge (cairo_t*, int x0, int x1, bool left, const std::string&, bool& prelight);
void render (Cairo::RefPtr const&, cairo_rectangle_t*);
@@ -83,6 +84,7 @@ private:
Glib::RefPtr _layout;
sigc::connection super_rapid_connection;
PBD::ScopedConnectionList marker_connection;
+ PBD::ScopedConnectionList tempo_map_connection;
PBD::ScopedConnectionList session_connection;
samplepos_t _last_update_sample;
@@ -104,10 +106,8 @@ private:
uint32_t _phead_color;
struct JumpRange {
- JumpRange (int l, int r, samplepos_t t, bool p = false)
- : left (l), right (r), to (t), prelight (p) {}
- int left;
- int right;
+ JumpRange (samplepos_t t, bool p = false)
+ : to (t), prelight (p) {}
samplepos_t to;
bool prelight;
};