X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Fshuttle_control.cc;h=d879ce50ff25d786fa4d541a82098f4b6e75d302;hb=446baca43b4fbf9208dddf87547fa663fa578b75;hp=5ff771ecf16c1ef57dce4c2516d813d10732c474;hpb=458db0525cf4cafab10904acf78b76a802081e8b;p=ardour.git diff --git a/gtk2_ardour/shuttle_control.cc b/gtk2_ardour/shuttle_control.cc index 5ff771ecf1..d879ce50ff 100644 --- a/gtk2_ardour/shuttle_control.cc +++ b/gtk2_ardour/shuttle_control.cc @@ -18,9 +18,7 @@ #include -#include - -#include +#include #include "ardour/ardour.h" #include "ardour/audioengine.h" @@ -29,75 +27,104 @@ #include "gtkmm2ext/keyboard.h" #include "gtkmm2ext/gui_thread.h" +#include "gtkmm2ext/cairocell.h" +#include "gtkmm2ext/utils.h" +#include "gtkmm2ext/rgb_macros.h" +#include "actions.h" #include "ardour_ui.h" +#include "rgb_macros.h" #include "shuttle_control.h" +#include "i18n.h" + using namespace Gtk; using namespace Gtkmm2ext; using namespace ARDOUR; using std::min; using std::max; +gboolean qt (gboolean, gint, gint, gboolean, Gtk::Tooltip*, gpointer) +{ + return FALSE; +} + ShuttleControl::ShuttleControl () - : _controllable (new ShuttleControllable (*this)) - , binding_proxy (_controllable) + : _controllable (new ShuttleControllable (*this)) + , binding_proxy (_controllable) { - ARDOUR_UI::instance()->set_tip (*this, _("Shuttle speed control (Context-click for options)")); + ARDOUR_UI::instance()->set_tip (*this, _("Shuttle speed control (Context-click for options)")); - pattern = 0; + pattern = 0; + shine_pattern = 0; last_shuttle_request = 0; last_speed_displayed = -99999999; shuttle_grabbed = false; + shuttle_speed_on_grab = 0; shuttle_fract = 0.0; shuttle_max_speed = 8.0f; shuttle_style_menu = 0; shuttle_unit_menu = 0; - shuttle_context_menu = 0; + shuttle_context_menu = 0; + _hovering = false; - set_flags (CAN_FOCUS); - add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::POINTER_MOTION_MASK|Gdk::SCROLL_MASK); - set_size_request (100, 15); + set_flags (CAN_FOCUS); + add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::POINTER_MOTION_MASK|Gdk::SCROLL_MASK); + set_size_request (85, 20); set_name (X_("ShuttleControl")); - Config->ParameterChanged.connect (parameter_connection, MISSING_INVALIDATOR, ui_bind (&ShuttleControl::parameter_changed, this, _1), gui_context()); + Config->ParameterChanged.connect (parameter_connection, MISSING_INVALIDATOR, boost::bind (&ShuttleControl::parameter_changed, this, _1), gui_context()); + + /* gtkmm 2.4: the C++ wrapper doesn't work */ + g_signal_connect ((GObject*) gobj(), "query-tooltip", G_CALLBACK (qt), NULL); + // signal_query_tooltip().connect (sigc::mem_fun (*this, &ShuttleControl::on_query_tooltip)); } ShuttleControl::~ShuttleControl () { cairo_pattern_destroy (pattern); + cairo_pattern_destroy (shine_pattern); } void ShuttleControl::set_session (Session *s) { - SessionHandlePtr::set_session (s); - - if (_session) { - set_sensitive (true); - _session->add_controllable (_controllable); - } else { - set_sensitive (false); - } + SessionHandlePtr::set_session (s); + + if (_session) { + set_sensitive (true); + _session->add_controllable (_controllable); + } else { + set_sensitive (false); + } } void ShuttleControl::on_size_allocate (Gtk::Allocation& alloc) { - if (pattern) { - cairo_pattern_destroy (pattern); - pattern = 0; - } - - pattern = cairo_pattern_create_linear (0, 0, alloc.get_width(), alloc.get_height()); - - /* add 3 color stops */ - - cairo_pattern_add_color_stop_rgb (pattern, 0.0, 0, 0, 0); - cairo_pattern_add_color_stop_rgb (pattern, 0.5, 0.0, 0.0, 1.0); - cairo_pattern_add_color_stop_rgb (pattern, 1.0, 0, 0, 0); + if (pattern) { + cairo_pattern_destroy (pattern); + pattern = 0; + cairo_pattern_destroy (shine_pattern); + shine_pattern = 0; + } - DrawingArea::on_size_allocate (alloc); + CairoWidget::on_size_allocate ( alloc); + + //background + pattern = cairo_pattern_create_linear (0, 0, 0, alloc.get_height()); + uint32_t col = ARDOUR_UI::config()->color ("shuttle"); + int r,b,g,a; + UINT_TO_RGBA(col, &r, &g, &b, &a); + cairo_pattern_add_color_stop_rgb (pattern, 0.0, r/400.0, g/400.0, b/400.0); + cairo_pattern_add_color_stop_rgb (pattern, 0.4, r/255.0, g/255.0, b/255.0); + cairo_pattern_add_color_stop_rgb (pattern, 1.0, r/512.0, g/512.0, b/512.0); + + //reflection + shine_pattern = cairo_pattern_create_linear (0.0, 0.0, 0.0, 10); + cairo_pattern_add_color_stop_rgba (shine_pattern, 0, 1,1,1,0.0); + cairo_pattern_add_color_stop_rgba (shine_pattern, 0.2, 1,1,1,0.4); + cairo_pattern_add_color_stop_rgba (shine_pattern, 1, 1,1,1,0.1); } void @@ -105,10 +132,25 @@ ShuttleControl::map_transport_state () { float speed = _session->transport_speed (); - if (speed != 0.0) { - shuttle_fract = SHUTTLE_FRACT_SPEED1; /* speed = 1.0, believe it or not */ - } else { + if ( (fabsf( speed - last_speed_displayed) < 0.005f) // dead-zone + && !( speed == 1.f && last_speed_displayed != 1.f) + && !( speed == 0.f && last_speed_displayed != 0.f) + ) + { + return; // nothing to see here, move along. + } + + // Q: is there a good reason why we re-calculate this every time? + if (fabs(speed) <= (2*DBL_EPSILON)) { shuttle_fract = 0; + } else { + if (Config->get_shuttle_units() == Semitones) { + bool reverse; + int semi = speed_as_semitones (speed, reverse); + shuttle_fract = semitones_as_fract (semi, reverse); + } else { + shuttle_fract = speed/shuttle_max_speed; + } } queue_draw (); @@ -125,63 +167,64 @@ ShuttleControl::build_shuttle_context_menu () Menu* speed_menu = manage (new Menu()); MenuList& speed_items = speed_menu->items(); - RadioMenuItem::Group group; + Menu* units_menu = manage (new Menu); + MenuList& units_items = units_menu->items(); + RadioMenuItem::Group units_group; + + units_items.push_back (RadioMenuElem (units_group, _("Percent"), sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_units), Percentage))); + if (Config->get_shuttle_units() == Percentage) { + static_cast(&units_items.back())->set_active(); + } + units_items.push_back (RadioMenuElem (units_group, _("Semitones"), sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_units), Semitones))); + if (Config->get_shuttle_units() == Semitones) { + static_cast(&units_items.back())->set_active(); + } + items.push_back (MenuElem (_("Units"), *units_menu)); + + Menu* style_menu = manage (new Menu); + MenuList& style_items = style_menu->items(); + RadioMenuItem::Group style_group; + + style_items.push_back (RadioMenuElem (style_group, _("Sprung"), sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_style), Sprung))); + if (Config->get_shuttle_behaviour() == Sprung) { + static_cast(&style_items.back())->set_active(); + } + style_items.push_back (RadioMenuElem (style_group, _("Wheel"), sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_style), Wheel))); + if (Config->get_shuttle_behaviour() == Wheel) { + static_cast(&style_items.back())->set_active(); + } + + items.push_back (MenuElem (_("Mode"), *style_menu)); + + RadioMenuItem::Group speed_group; - speed_items.push_back (RadioMenuElem (group, "8", sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_max_speed), 8.0f))); + speed_items.push_back (RadioMenuElem (speed_group, "8", sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_max_speed), 8.0f))); if (shuttle_max_speed == 8.0) { static_cast(&speed_items.back())->set_active (); } - speed_items.push_back (RadioMenuElem (group, "6", sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_max_speed), 6.0f))); + speed_items.push_back (RadioMenuElem (speed_group, "6", sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_max_speed), 6.0f))); if (shuttle_max_speed == 6.0) { static_cast(&speed_items.back())->set_active (); } - speed_items.push_back (RadioMenuElem (group, "4", sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_max_speed), 4.0f))); + speed_items.push_back (RadioMenuElem (speed_group, "4", sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_max_speed), 4.0f))); if (shuttle_max_speed == 4.0) { static_cast(&speed_items.back())->set_active (); } - speed_items.push_back (RadioMenuElem (group, "3", sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_max_speed), 3.0f))); + speed_items.push_back (RadioMenuElem (speed_group, "3", sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_max_speed), 3.0f))); if (shuttle_max_speed == 3.0) { static_cast(&speed_items.back())->set_active (); } - speed_items.push_back (RadioMenuElem (group, "2", sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_max_speed), 2.0f))); + speed_items.push_back (RadioMenuElem (speed_group, "2", sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_max_speed), 2.0f))); if (shuttle_max_speed == 2.0) { static_cast(&speed_items.back())->set_active (); } - speed_items.push_back (RadioMenuElem (group, "1.5", sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_max_speed), 1.5f))); + speed_items.push_back (RadioMenuElem (speed_group, "1.5", sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_max_speed), 1.5f))); if (shuttle_max_speed == 1.5) { static_cast(&speed_items.back())->set_active (); } items.push_back (MenuElem (_("Maximum speed"), *speed_menu)); - - Menu* units_menu = manage (new Menu); - MenuList& units_items = units_menu->items(); - RadioMenuItem::Group units_group; - - units_items.push_back (RadioMenuElem (units_group, _("Percent"), sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_units), Percentage))); - if (Config->get_shuttle_units() == Percentage) { - static_cast(&units_items.back())->set_active(); - } - units_items.push_back (RadioMenuElem (units_group, _("Semitones"), sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_units), Semitones))); - if (Config->get_shuttle_units() == Semitones) { - static_cast(&units_items.back())->set_active(); - } - items.push_back (MenuElem (_("Units"), *units_menu)); - - Menu* style_menu = manage (new Menu); - MenuList& style_items = style_menu->items(); - RadioMenuItem::Group style_group; - style_items.push_back (RadioMenuElem (style_group, _("Sprung"), sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_style), Sprung))); - if (Config->get_shuttle_behaviour() == Sprung) { - static_cast(&style_items.back())->set_active(); - } - style_items.push_back (RadioMenuElem (style_group, _("Wheel"), sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_style), Wheel))); - if (Config->get_shuttle_behaviour() == Wheel) { - static_cast(&style_items.back())->set_active(); - } - - items.push_back (MenuElem (_("Mode"), *style_menu)); } void @@ -198,6 +241,7 @@ void ShuttleControl::set_shuttle_max_speed (float speed) { shuttle_max_speed = speed; + last_speed_displayed = -99999999; } bool @@ -218,9 +262,19 @@ ShuttleControl::on_button_press_event (GdkEventButton* ev) switch (ev->button) { case 1: - add_modal_grab (); - shuttle_grabbed = true; - mouse_shuttle (ev->x, true); + if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) { + if (_session->transport_rolling()) { + _session->request_transport_speed (1.0); + } + } else { + add_modal_grab (); + shuttle_grabbed = true; + shuttle_speed_on_grab = _session->transport_speed (); + mouse_shuttle (ev->x, true); + gdk_pointer_grab(ev->window,false, + GdkEventMask( Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK |Gdk::BUTTON_RELEASE_MASK), + NULL,NULL,ev->time); + } break; case 2: @@ -241,29 +295,26 @@ ShuttleControl::on_button_release_event (GdkEventButton* ev) switch (ev->button) { case 1: - mouse_shuttle (ev->x, true); - shuttle_grabbed = false; - remove_modal_grab (); - if (Config->get_shuttle_behaviour() == Sprung) { - if (_session->config.get_auto_play()) { - shuttle_fract = SHUTTLE_FRACT_SPEED1; - _session->request_transport_speed (1.0); + if (shuttle_grabbed) { + shuttle_grabbed = false; + remove_modal_grab (); + gdk_pointer_ungrab (GDK_CURRENT_TIME); + + if (Config->get_shuttle_behaviour() == Sprung) { + if (shuttle_speed_on_grab == 0 ) { + _session->request_transport_speed (1.0); + } + _session->request_transport_speed (shuttle_speed_on_grab); } else { - shuttle_fract = 0; - _session->request_transport_speed (0.0); + mouse_shuttle (ev->x, true); } - queue_draw (); } return true; case 2: if (_session->transport_rolling()) { - shuttle_fract = SHUTTLE_FRACT_SPEED1; - _session->request_transport_speed (1.0); - } else { - shuttle_fract = 0; + _session->request_transport_speed (1.0, Config->get_shuttle_behaviour() == Wheel); } - queue_draw (); return true; case 3: @@ -272,30 +323,82 @@ ShuttleControl::on_button_release_event (GdkEventButton* ev) } - use_shuttle_fract (true); - return true; } +bool +ShuttleControl::on_query_tooltip (int, int, bool, const Glib::RefPtr&) +{ + return false; +} + bool ShuttleControl::on_scroll_event (GdkEventScroll* ev) { - if (!_session) { + if (!_session || Config->get_shuttle_behaviour() != Wheel) { return true; } - switch (ev->direction) { + bool semis = (Config->get_shuttle_units() == Semitones); + switch (ev->direction) { case GDK_SCROLL_UP: - shuttle_fract += 0.005; + case GDK_SCROLL_RIGHT: + if (semis) { + if (shuttle_fract == 0) { + shuttle_fract = semitones_as_fract (1, false); + } else { + bool rev; + int st = fract_as_semitones (shuttle_fract, rev); + shuttle_fract = semitones_as_fract (st + 1, rev); + } + } else { + shuttle_fract += 0.00125; + } break; case GDK_SCROLL_DOWN: - shuttle_fract -= 0.005; + case GDK_SCROLL_LEFT: + if (semis) { + if (shuttle_fract == 0) { + shuttle_fract = semitones_as_fract (1, true); + } else { + bool rev; + int st = fract_as_semitones (shuttle_fract, rev); + shuttle_fract = semitones_as_fract (st - 1, rev); + } + } else { + shuttle_fract -= 0.00125; + } break; default: - /* scroll left/right */ return false; } + + if (semis) { + + float lower_side_of_dead_zone = semitones_as_fract (-24, true); + float upper_side_of_dead_zone = semitones_as_fract (-24, false); + + /* if we entered the "dead zone" (-24 semitones in forward or reverse), jump + to the far side of it. + */ + + if (shuttle_fract > lower_side_of_dead_zone && shuttle_fract < upper_side_of_dead_zone) { + switch (ev->direction) { + case GDK_SCROLL_UP: + case GDK_SCROLL_RIGHT: + shuttle_fract = upper_side_of_dead_zone; + break; + case GDK_SCROLL_DOWN: + case GDK_SCROLL_LEFT: + shuttle_fract = lower_side_of_dead_zone; + break; + default: + /* impossible, checked above */ + return false; + } + } + } use_shuttle_fract (true); @@ -315,35 +418,81 @@ ShuttleControl::on_motion_notify_event (GdkEventMotion* ev) gint ShuttleControl::mouse_shuttle (double x, bool force) { - double const half_width = get_width() / 2.0; - double distance = x - half_width; + double const center = get_width() / 2.0; + double distance_from_center = x - center; - if (distance > 0) { - distance = min (distance, half_width); + if (distance_from_center > 0) { + distance_from_center = min (distance_from_center, center); } else { - distance = max (distance, -half_width); + distance_from_center = max (distance_from_center, -center); } - shuttle_fract = distance / half_width; + /* compute shuttle fract as expressing how far between the center + and the edge we are. positive values indicate we are right of + center, negative values indicate left of center + */ + + shuttle_fract = distance_from_center / center; // center == half the width use_shuttle_fract (force); return true; } void -ShuttleControl::set_shuttle_fract (double f) +ShuttleControl::set_shuttle_fract (double f, bool zero_ok) { shuttle_fract = f; - use_shuttle_fract (false); + use_shuttle_fract (false, zero_ok); +} + +int +ShuttleControl::speed_as_semitones (float speed, bool& reverse) +{ + assert (speed != 0.0); + + if (speed < 0.0) { + reverse = true; + return (int) round (12.0 * fast_log2 (-speed)); + } else { + reverse = false; + return (int) round (12.0 * fast_log2 (speed)); + } +} + +float +ShuttleControl::semitones_as_speed (int semi, bool reverse) +{ + if (reverse) { + return -pow (2.0, (semi / 12.0)); + } else { + return pow (2.0, (semi / 12.0)); + } +} + +float +ShuttleControl::semitones_as_fract (int semi, bool reverse) +{ + float speed = semitones_as_speed (semi, reverse); + return speed/4.0; /* 4.0 is the maximum speed for a 24 semitone shift */ +} + +int +ShuttleControl::fract_as_semitones (float fract, bool& reverse) +{ + assert (fract != 0.0); + return speed_as_semitones (fract * 4.0, reverse); } void -ShuttleControl::use_shuttle_fract (bool force) +ShuttleControl::use_shuttle_fract (bool force, bool zero_ok) { microseconds_t now = get_microseconds(); + shuttle_fract = max (-1.0f, shuttle_fract); + shuttle_fract = min (1.0f, shuttle_fract); + /* do not attempt to submit a motion-driven transport speed request more than once per process cycle. - */ + */ if (!force && (last_shuttle_request - now) < (microseconds_t) AudioEngine::instance()->usecs_per_cycle()) { return; @@ -354,78 +503,102 @@ ShuttleControl::use_shuttle_fract (bool force) double speed = 0; if (Config->get_shuttle_units() == Semitones) { - - double const step = 1.0 / 24.0; // range is 24 semitones up & down - double const semitones = round (shuttle_fract / step); - speed = pow (2.0, (semitones / 12.0)); - - } else { - - bool const neg = (shuttle_fract < 0.0); - double fract = 1 - sqrt (1 - (shuttle_fract * shuttle_fract)); // Formula A1 - - if (neg) { - fract = -fract; + if (shuttle_fract != 0.0) { + bool reverse; + int semi = fract_as_semitones (shuttle_fract, reverse); + speed = semitones_as_speed (semi, reverse); + } else { + speed = 0.0; } - - speed = shuttle_max_speed * fract; + } else { + speed = shuttle_max_speed * shuttle_fract; } - _session->request_transport_speed_nonzero (speed); + if (zero_ok) { + _session->request_transport_speed (speed, Config->get_shuttle_behaviour() == Wheel); + } else { + _session->request_transport_speed_nonzero (speed, Config->get_shuttle_behaviour() == Wheel); + } } -bool -ShuttleControl::on_expose_event (GdkEventExpose* event) +void +ShuttleControl::render (cairo_t* cr, cairo_rectangle_t*) { - cairo_text_extents_t extents; - Glib::RefPtr win (get_window()); - Glib::RefPtr style (get_style()); + cairo_text_extents_t extents; - cairo_t* cr = gdk_cairo_create (win->gobj()); + //black border + cairo_set_source_rgb (cr, 0, 0.0, 0.0); + rounded_rectangle (cr, 0, 0, get_width(), get_height(), 4); + cairo_fill (cr); - cairo_set_source (cr, pattern); - cairo_rectangle (cr, 0.0, 0.0, get_width(), get_height()); - cairo_fill_preserve (cr); + float speed = 0.0; - cairo_set_source_rgb (cr, 0, 0, 0.0); - cairo_stroke (cr); + if (_session) { + speed = _session->transport_speed (); + } /* Marker */ - - double x = (get_width() / 2.0) + (0.5 * (get_width() * shuttle_fract)); - cairo_move_to (cr, x, 0); - cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); - cairo_line_to (cr, x, get_height()); + float visual_fraction = std::min (1.0f, speed / shuttle_max_speed); + float marker_size = get_height() - 5.0; + float avail_width = get_width() - marker_size - 4; + float x = get_width() * 0.5 + visual_fraction * avail_width * 0.5; +// cairo_set_source_rgb (cr, 0, 1, 0.0); + cairo_set_source (cr, pattern); + if (speed == 1.0) { + cairo_move_to( cr, x, 2.5); + cairo_line_to( cr, x + marker_size * .577, 2.5 + marker_size * 0.5); + cairo_line_to( cr, x, 2.5 + marker_size); + cairo_close_path(cr); + } else if ( speed ==0.0 ) + rounded_rectangle (cr, x, 2.5, marker_size, marker_size, 1); + else + cairo_arc (cr, x, 2.5 + marker_size * .5, marker_size * 0.47, 0, 2.0 * M_PI); + cairo_set_line_width (cr, 1.75); cairo_stroke (cr); /* speed text */ char buf[32]; - float speed = 0.0; - - if (_session) { - speed = _session->transport_speed (); - } if (speed != 0) { + if (Config->get_shuttle_units() == Percentage) { - snprintf (buf, sizeof (buf), "%d%%", (int) round (speed * 100)); + + if (speed == 1.0) { + snprintf (buf, sizeof (buf), "%s", _("Playing")); + } else { + if (speed < 0.0) { + snprintf (buf, sizeof (buf), "<<< %.1f%%", -speed * 100.f); + } else { + snprintf (buf, sizeof (buf), ">>> %.1f%%", speed * 100.f); + } + } + } else { - if (speed < 0) { - snprintf (buf, sizeof (buf), "< %d semitones", (int) round (12.0 * fast_log2 (-speed))); + + bool reversed; + int semi = speed_as_semitones (speed, reversed); + + if (reversed) { + snprintf (buf, sizeof (buf), _("<<< %+d semitones"), semi); } else { - snprintf (buf, sizeof (buf), "> %d semitones", (int) round (12.0 * fast_log2 (speed))); + snprintf (buf, sizeof (buf), _(">>> %+d semitones"), semi); } } + } else { - snprintf (buf, sizeof (buf), _("Stopped")); + snprintf (buf, sizeof (buf), "%s", _("Stopped")); } last_speed_displayed = speed; - cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); - cairo_text_extents (cr, buf, &extents); - cairo_move_to (cr, 10, extents.height + 2); + // TODO use a proper pango layout, scale font + cairo_set_source_rgb (cr, 0.6, 0.6, 0.6); + cairo_set_font_size (cr, 13.0); + cairo_text_extents (cr, "0|", &extents); // note the descender + const float text_ypos = (get_height() + extents.height - 1.) * .5; + + cairo_move_to (cr, 10, text_ypos); cairo_show_text (cr, buf); /* style text */ @@ -433,21 +606,24 @@ ShuttleControl::on_expose_event (GdkEventExpose* event) switch (Config->get_shuttle_behaviour()) { case Sprung: - snprintf (buf, sizeof (buf), _("Sprung")); + snprintf (buf, sizeof (buf), "%s", _("Sprung")); break; case Wheel: - snprintf (buf, sizeof (buf), _("Wheel")); + snprintf (buf, sizeof (buf), "%s", _("Wheel")); break; } - cairo_text_extents (cr, buf, &extents); - - cairo_move_to (cr, get_width() - (fabs(extents.x_advance) + 5), extents.height + 2); + cairo_text_extents (cr, buf, &extents); + cairo_move_to (cr, get_width() - (fabs(extents.x_advance) + 5), text_ypos); cairo_show_text (cr, buf); - cairo_destroy (cr); - - return true; + if (ARDOUR_UI::config()->get_widget_prelight()) { + if (_hovering) { + rounded_rectangle (cr, 1, 1, get_width()-2, get_height()-2, 4.0); + cairo_set_source_rgba (cr, 1, 1, 1, 0.2); + cairo_fill (cr); + } + } } void @@ -462,79 +638,89 @@ ShuttleControl::shuttle_unit_clicked () void ShuttleControl::set_shuttle_style (ShuttleBehaviour s) { - Config->set_shuttle_behaviour (s); + Config->set_shuttle_behaviour (s); } void ShuttleControl::set_shuttle_units (ShuttleUnits s) { - Config->set_shuttle_units (s); + Config->set_shuttle_units (s); } -void -ShuttleControl::update_speed_display () -{ - if (_session->transport_speed() != last_speed_displayed) { - queue_draw (); - } -} - ShuttleControl::ShuttleControllable::ShuttleControllable (ShuttleControl& s) - : PBD::Controllable (X_("Shuttle")) - , sc (s) -{ -} - -void -ShuttleControl::ShuttleControllable::set_id (const std::string& str) + : PBD::Controllable (X_("Shuttle")) + , sc (s) { - _id = str; } void ShuttleControl::ShuttleControllable::set_value (double val) { - double fract; - - if (val == 0.5) { - fract = 0.0; - } else { - if (val < 0.5) { - fract = -((0.5 - val)/0.5); - } else { - fract = ((val - 0.5)/0.5); - } - } - - sc.set_shuttle_fract (fract); + sc.set_shuttle_fract ((val - lower()) / (upper() - lower()), true); } -double +double ShuttleControl::ShuttleControllable::get_value () const { - return sc.get_shuttle_fract (); + return lower() + (sc.get_shuttle_fract () * (upper() - lower())); } void ShuttleControl::parameter_changed (std::string p) { - if (p == "shuttle-behaviour") { + if (p == "shuttle-behaviour") { switch (Config->get_shuttle_behaviour ()) { case Sprung: - shuttle_fract = 0.0; + /* back to Sprung - reset to speed = 1.0 if playing + */ if (_session) { if (_session->transport_rolling()) { - shuttle_fract = SHUTTLE_FRACT_SPEED1; - _session->request_transport_speed (1.0); + if (_session->transport_speed() == 1.0) { + queue_draw (); + } else { + /* reset current speed and + revert to 1.0 as the default + */ + _session->request_transport_speed (1.0); + /* redraw when speed changes */ + } + } else { + queue_draw (); } } break; + case Wheel: + queue_draw (); break; } - queue_draw (); - + } else if (p == "shuttle-units") { - queue_draw (); - } + queue_draw (); + } +} + + +bool +ShuttleControl::on_enter_notify_event (GdkEventCrossing* ev) +{ + _hovering = true; + + if (ARDOUR_UI::config()->get_widget_prelight()) { + queue_draw (); + } + + return CairoWidget::on_enter_notify_event (ev); +} + +bool +ShuttleControl::on_leave_notify_event (GdkEventCrossing* ev) +{ + _hovering = false; + + if (ARDOUR_UI::config()->get_widget_prelight()) { + queue_draw (); + } + + return CairoWidget::on_leave_notify_event (ev); }