diff --git a/gtk2_ardour/audio_region_view.cc b/gtk2_ardour/audio_region_view.cc index 0159022f87..0857945669 100644 --- a/gtk2_ardour/audio_region_view.cc +++ b/gtk2_ardour/audio_region_view.cc @@ -172,17 +172,17 @@ AudioRegionView::init (Gdk::Color const & basic_color, bool wfd) if (!_recregion) { fade_in_handle = new ArdourCanvas::Rectangle (group); CANVAS_DEBUG_NAME (fade_in_handle, string_compose ("fade in handle for %1", region()->name())); - fade_in_handle->set_fill_color (UINT_RGBA_CHANGE_A (fill_color, 0)); - fade_in_handle->set_outline_color (RGBA_TO_UINT (0, 0, 0, 0)); - + fade_in_handle->set_outline_color (RGBA_TO_UINT (0, 0, 0, 255)); + fade_in_handle->set_fill_color (UINT_RGBA_CHANGE_A (fade_color, 255)); fade_in_handle->set_data ("regionview", this); + fade_in_handle->hide (); fade_out_handle = new ArdourCanvas::Rectangle (group); CANVAS_DEBUG_NAME (fade_out_handle, string_compose ("fade out handle for %1", region()->name())); - fade_out_handle->set_fill_color (UINT_RGBA_CHANGE_A (fill_color, 0)); - fade_out_handle->set_outline_color (RGBA_TO_UINT (0, 0, 0, 0)); - + fade_out_handle->set_outline_color (RGBA_TO_UINT (0, 0, 0, 255)); + fade_out_handle->set_fill_color (UINT_RGBA_CHANGE_A (fade_color, 255)); fade_out_handle->set_data ("regionview", this); + fade_out_handle->hide (); } setup_fade_handle_positions (); @@ -410,10 +410,9 @@ AudioRegionView::reset_width_dependent_items (double pixel_width) if (pixel_width <= 6.0 || _height < 5.0 || !trackview.session()->config.get_show_region_fades()) { fade_in_handle->hide(); fade_out_handle->hide(); - } - else { - fade_in_handle->show(); - fade_out_handle->show(); + } else { + //fade_in_handle->show(); + //fade_out_handle->show(); } } @@ -533,8 +532,6 @@ AudioRegionView::reset_fade_in_shape_width (boost::shared_ptr ar, f return; } - fade_in_handle->show (); - /* smallest size for a fade is 64 frames */ width = std::max ((framecnt_t) 64, width); @@ -629,8 +626,6 @@ AudioRegionView::reset_fade_out_shape_width (boost::shared_ptr ar, return; } - fade_out_handle->show (); - /* smallest size for a fade is 64 frames */ width = std::max ((framecnt_t) 64, width); @@ -1374,11 +1369,13 @@ AudioRegionView::entered (bool internal_editing) gain_line->add_visibility (AutomationLine::ControlPoints); } + cerr << "Entered! ARV for " << _region->name() << endl; + if (fade_in_handle && !internal_editing) { - fade_in_handle->set_outline_color (RGBA_TO_UINT (0, 0, 0, 255)); - fade_in_handle->set_fill_color (UINT_RGBA_CHANGE_A (fade_color, 255)); - fade_out_handle->set_outline_color (RGBA_TO_UINT (0, 0, 0, 255)); - fade_out_handle->set_fill_color (UINT_RGBA_CHANGE_A (fade_color, 255)); + fade_in_handle->show (); + fade_out_handle->show (); + fade_out_handle->raise_to_top (); + fade_in_handle->raise_to_top (); } } @@ -1392,11 +1389,11 @@ AudioRegionView::exited () gain_line->remove_visibility (AutomationLine::ControlPoints); } + cerr << "Left! ARV for " << _region->name() << endl; + if (fade_in_handle) { - fade_in_handle->set_outline_color (RGBA_TO_UINT (0, 0, 0, 0)); - fade_in_handle->set_fill_color (UINT_RGBA_CHANGE_A (fade_color, 0)); - fade_out_handle->set_outline_color (RGBA_TO_UINT (0, 0, 0, 0)); - fade_out_handle->set_fill_color (UINT_RGBA_CHANGE_A (fade_color, 0)); + fade_in_handle->hide (); + fade_out_handle->hide (); } } diff --git a/gtk2_ardour/automation_line.cc b/gtk2_ardour/automation_line.cc index 17e0792477..eb6a54e1a3 100644 --- a/gtk2_ardour/automation_line.cc +++ b/gtk2_ardour/automation_line.cc @@ -35,6 +35,8 @@ #include "evoral/Curve.hpp" +#include "canvas/debug.h" + #include "automation_line.h" #include "control_point.h" #include "gui_thread.h" @@ -90,8 +92,10 @@ AutomationLine::AutomationLine (const string& name, TimeAxisView& tv, ArdourCanv _height = 0; group = new ArdourCanvas::Group (&parent); + CANVAS_DEBUG_NAME (group, "region gain envelope group"); line = new ArdourCanvas::Curve (group); + CANVAS_DEBUG_NAME (line, "region gain envelope line"); line->set_data ("line", this); line->set_outline_width (2.0); diff --git a/gtk2_ardour/editor_canvas_events.cc b/gtk2_ardour/editor_canvas_events.cc index 5bd0060d23..b119b989d1 100644 --- a/gtk2_ardour/editor_canvas_events.cc +++ b/gtk2_ardour/editor_canvas_events.cc @@ -267,12 +267,11 @@ Editor::canvas_region_view_event (GdkEvent *event, ArdourCanvas::Item* item, Reg break; case GDK_ENTER_NOTIFY: - set_entered_track (&rv->get_time_axis_view ()); set_entered_regionview (rv); + ret = true; break; case GDK_LEAVE_NOTIFY: - set_entered_track (0); set_entered_regionview (0); break; @@ -309,6 +308,7 @@ Editor::canvas_stream_view_event (GdkEvent *event, ArdourCanvas::Item* item, Rou case GDK_ENTER_NOTIFY: set_entered_track (tv); + ret = true; break; case GDK_LEAVE_NOTIFY: @@ -462,9 +462,13 @@ Editor::canvas_fade_in_event (GdkEvent *event, ArdourCanvas::Item* item, AudioRe } - /* proxy for the regionview */ + /* proxy for the regionview, except enter/leave events */ - return canvas_region_view_event (event, rv->get_canvas_group(), rv); + if (event->type == GDK_ENTER_NOTIFY || event->type == GDK_LEAVE_NOTIFY) { + return true; + } else { + return canvas_region_view_event (event, rv->get_canvas_group(), rv); + } } bool @@ -497,12 +501,10 @@ Editor::canvas_fade_in_handle_event (GdkEvent *event, ArdourCanvas::Item* item, break; case GDK_ENTER_NOTIFY: - set_entered_regionview (rv); ret = enter_handler (item, event, FadeInHandleItem); break; case GDK_LEAVE_NOTIFY: - set_entered_regionview (0); ret = leave_handler (item, event, FadeInHandleItem); break; @@ -544,9 +546,13 @@ Editor::canvas_fade_out_event (GdkEvent *event, ArdourCanvas::Item* item, AudioR } - /* proxy for the regionview */ + /* proxy for the regionview, except enter/leave events */ - return canvas_region_view_event (event, rv->get_canvas_group(), rv); + if (event->type == GDK_ENTER_NOTIFY || event->type == GDK_LEAVE_NOTIFY) { + return true; + } else { + return canvas_region_view_event (event, rv->get_canvas_group(), rv); + } } bool @@ -579,12 +585,10 @@ Editor::canvas_fade_out_handle_event (GdkEvent *event, ArdourCanvas::Item* item, break; case GDK_ENTER_NOTIFY: - set_entered_regionview (rv); ret = enter_handler (item, event, FadeOutHandleItem); break; case GDK_LEAVE_NOTIFY: - set_entered_regionview (0); ret = leave_handler (item, event, FadeOutHandleItem); break; @@ -783,12 +787,10 @@ Editor::canvas_frame_handle_event (GdkEvent* event, ArdourCanvas::Item* item, Re ret = motion_handler (item, event); break; case GDK_ENTER_NOTIFY: - set_entered_regionview (rv); ret = enter_handler (item, event, type); break; case GDK_LEAVE_NOTIFY: - set_entered_regionview (0); ret = leave_handler (item, event, type); break; @@ -827,12 +829,10 @@ Editor::canvas_region_view_name_highlight_event (GdkEvent* event, ArdourCanvas:: ret = true; // force this to avoid progagating the event into the regionview break; case GDK_ENTER_NOTIFY: - set_entered_regionview (rv); ret = enter_handler (item, event, RegionViewNameHighlight); break; case GDK_LEAVE_NOTIFY: - set_entered_regionview (0); ret = leave_handler (item, event, RegionViewNameHighlight); break; @@ -869,12 +869,10 @@ Editor::canvas_region_view_name_event (GdkEvent *event, ArdourCanvas::Item* item ret = motion_handler (item, event); break; case GDK_ENTER_NOTIFY: - set_entered_regionview (rv); ret = enter_handler (item, event, RegionViewName); break; case GDK_LEAVE_NOTIFY: - set_entered_regionview (0); ret = leave_handler (item, event, RegionViewName); break; diff --git a/gtk2_ardour/time_axis_view_item.cc b/gtk2_ardour/time_axis_view_item.cc index 6a898855bd..a200b71f9f 100644 --- a/gtk2_ardour/time_axis_view_item.cc +++ b/gtk2_ardour/time_axis_view_item.cc @@ -184,6 +184,7 @@ TimeAxisViewItem::init (ArdourCanvas::Group* parent, double fpp, Gdk::Color cons ArdourCanvas::Rect (0.0, 1.0, trackview.editor().sample_to_pixel(duration), trackview.current_height())); + CANVAS_DEBUG_NAME (frame, string_compose ("frame for %1", get_item_name())); if (_recregion) { diff --git a/libs/canvas/canvas.cc b/libs/canvas/canvas.cc index 30ef25e944..b616e6cf59 100644 --- a/libs/canvas/canvas.cc +++ b/libs/canvas/canvas.cc @@ -234,8 +234,8 @@ Canvas::queue_draw_item_area (Item* item, Rect area) /** Construct a GtkCanvas */ GtkCanvas::GtkCanvas () - : _current_item (0) - , _grabbed_item (0) + : _grabbed_item (0) + , _focused_item (0) { /* these are the events we want to know about */ add_events (Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::POINTER_MOTION_MASK); @@ -294,10 +294,11 @@ GtkCanvas::enter_leave_items (int state) void GtkCanvas::enter_leave_items (Duple const & point, int state) { - /* find the items at the given position */ + /* we do not enter/leave items during a drag/grab */ - vector items; - _root.add_items_at_point (point, items); + if (_grabbed_item) { + return; + } GdkEventCrossing enter_event; enter_event.type = GDK_ENTER_NOTIFY; @@ -310,69 +311,88 @@ GtkCanvas::enter_leave_items (Duple const & point, int state) enter_event.state = state; enter_event.x = point.x; enter_event.y = point.y; + enter_event.detail = GDK_NOTIFY_UNKNOWN; GdkEventCrossing leave_event = enter_event; leave_event.type = GDK_LEAVE_NOTIFY; - leave_event.detail = GDK_NOTIFY_ANCESTOR; - leave_event.subwindow = 0; - if (items.empty()) { - if (_current_item) { - /* leave event */ - // cerr << "E/L: left item " << _current_item->whatami() << '/' << _current_item->name << " for ... nada" << endl; - _current_item->Event (reinterpret_cast (&leave_event)); - _current_item = 0; + /* find the items at the given position */ + + vector items; + _root.add_items_at_point (point, items); + + /* put all items at point that are event-sensitive and visible into within_items, and if this + is a new addition, also put them into newly_entered for later deliver of enter events. + */ + + vector::const_iterator i; + vector newly_entered; + Item const * new_item; + + for (i = items.begin(); i != items.end(); ++i) { + + new_item = *i; + + if (new_item->ignore_events() || !new_item->visible()) { + continue; + } + + pair::iterator,bool> res = within_items.insert (new_item); + + if (res.second) { + newly_entered.push_back (new_item); } - return; } - /* items is sorted from top to bottom, so reverse through it from bottom - * to top to find the lowest, first event-sensitive item and notify that - * we have entered it - */ + /* for every item in "within_items", check that we are still within them. if not, + send a leave event, and remove them from "within_items" + */ + + for (set::const_iterator i = within_items.begin(); i != within_items.end(); ) { + + set::const_iterator tmp = i; + ++tmp; + + new_item = *i; + + boost::optional bbox = new_item->bounding_box(); + + + if (bbox) { + if (!new_item->item_to_canvas (bbox.get()).contains (point)) { + leave_event.detail = GDK_NOTIFY_UNKNOWN; + cerr << string_compose ("\tLeave %1 %2\n", new_item->whatami(), new_item->name); + DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("Leave %1 %2\n", new_item->whatami(), new_item->name)); + (*i)->Event (reinterpret_cast (&leave_event)); + within_items.erase (i); + } + } + + i = tmp; + } - // cerr << "E/L: " << items.size() << " to check at " << point << endl; -#ifdef CANVAS_DEBUG - // for (vector::const_reverse_iterator i = items.rbegin(); i != items.rend(); ++i) { - // cerr << '\t' << (*i)->whatami() << ' ' << (*i)->name << " ignore ? " << (*i)->ignore_events() << " current ? " << (_current_item == (*i)) << endl; - // } -#endif - // cerr << "------------\n"; + /* for every item in "newly_entered", send an enter event (and propagate it up the + item tree until it is handled + */ - for (vector::const_reverse_iterator i = items.rbegin(); i != items.rend(); ++i) { + for (vector::const_iterator i = newly_entered.begin(); i != newly_entered.end(); ++i) { + new_item = *i; - Item const * new_item = *i; -#ifdef CANVAS_DEBUG - // cerr << "\tE/L check out " << new_item->whatami() << ' ' << new_item->name << " ignore ? " << new_item->ignore_events() << " current ? " << (_current_item == new_item) << endl; -#endif - if (new_item->ignore_events()) { - // cerr << "continue1\n"; - continue; - } - if (_current_item == new_item) { - // cerr << "continue2\n"; - continue; - } - - if (_current_item) { - /* leave event */ - DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("Leave %1 %2\n", _current_item->whatami(), _current_item->name)); - _current_item->Event (reinterpret_cast (&leave_event)); - queue_draw (); - } - - if (new_item && _current_item != new_item) { - /* enter event */ - _current_item = new_item; - DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("Enter %1 %2\n", _current_item->whatami(), _current_item->name)); - _current_item->Event (reinterpret_cast (&enter_event)); - queue_draw (); + if (new_item->Event (reinterpret_cast (&enter_event))) { + cerr << string_compose ("\tEntered %1 %2\n", new_item->whatami(), new_item->name); + DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("Enter %1 %2\n", new_item->whatami(), new_item->name)); break; } - - // cerr << "Loop around again\n"; } + +#if 0 + cerr << "Within:\n"; + for (set::const_iterator i = within_items.begin(); i != within_items.end(); ++i) { + cerr << '\t' << (*i)->whatami() << '/' << (*i)->name << endl; + } + cerr << "----\n"; +#endif } /** Deliver an event to the appropriate item; either the grabbed item, or @@ -451,15 +471,19 @@ GtkCanvas::item_going_away (Item* item, boost::optional bounding_box) queue_draw_item_area (item, bounding_box.get ()); } - if (_current_item == item) { - _current_item = 0; - queue_draw (); - } + /* no need to send a leave event to this item, since it is going away + */ + + within_items.erase (item); if (_grabbed_item == item) { _grabbed_item = 0; } + if (_focused_item == item) { + _focused_item = 0; + } + enter_leave_items (0); // no mouse state } @@ -549,6 +573,22 @@ GtkCanvas::on_motion_notify_event (GdkEventMotion* ev) return motion_notify_handler ((GdkEventMotion*) ©); } +bool +GtkCanvas::on_enter_notify_event (GdkEventCrossing* ev) +{ + Duple where = window_to_canvas (Duple (ev->x, ev->y)); + enter_leave_items (where, ev->state); + return true; +} + +bool +GtkCanvas::on_leave_notify_event (GdkEventCrossing* /*ev*/) +{ + cerr << "Clear all within items as we leave\n"; + within_items.clear (); + return true; +} + /** Called to request a redraw of our canvas. * @param area Area to redraw, in canvas coordinates. */ @@ -591,6 +631,7 @@ GtkCanvas::grab (Item* item) _grabbed_item = item; } + /** `Ungrab' any item that was previously grabbed */ void GtkCanvas::ungrab () @@ -599,6 +640,24 @@ GtkCanvas::ungrab () _grabbed_item = 0; } +/** Set keyboard focus on an item, so that all keyboard events are sent to that item until the focus + * moves elsewhere. + * @param item Item to grab. + */ +void +GtkCanvas::focus (Item* item) +{ + _focused_item = item; +} + +void +GtkCanvas::unfocus (Item* item) +{ + if (item == _focused_item) { + _focused_item = 0; + } +} + /** @return The visible area of the canvas, in canvas coordinates */ Rect GtkCanvas::visible_area () const diff --git a/libs/canvas/canvas/canvas.h b/libs/canvas/canvas/canvas.h index e65abf6b27..0575685832 100644 --- a/libs/canvas/canvas/canvas.h +++ b/libs/canvas/canvas/canvas.h @@ -24,11 +24,14 @@ #ifndef __CANVAS_CANVAS_H__ #define __CANVAS_CANVAS_H__ +#include + #include #include #include #include #include + #include "pbd/signals.h" #include "canvas/root_group.h" @@ -63,6 +66,11 @@ public: /** called to ask the canvas' host to `ungrab' any grabbed item */ virtual void ungrab () = 0; + /** called to ask the canvas' host to keyboard focus on an item */ + virtual void focus (Item *) = 0; + /** called to ask the canvas' host to drop keyboard focus on an item */ + virtual void unfocus (Item*) = 0; + void render (Rect const &, Cairo::RefPtr const &) const; /** @return root group */ @@ -126,6 +134,8 @@ public: void request_size (Duple); void grab (Item *); void ungrab (); + void focus (Item *); + void unfocus (Item*); Cairo::RefPtr context (); @@ -136,6 +146,8 @@ protected: bool on_button_press_event (GdkEventButton *); bool on_button_release_event (GdkEventButton* event); bool on_motion_notify_event (GdkEventMotion *); + bool on_enter_notify_event (GdkEventCrossing*); + bool on_leave_notify_event (GdkEventCrossing*); bool button_handler (GdkEventButton *); bool motion_notify_handler (GdkEventMotion *); @@ -148,11 +160,12 @@ private: void item_going_away (Item *, boost::optional); bool send_leave_event (Item const *, double, double) const; - - /** the item that the mouse is currently over, or 0 */ - Item const * _current_item; + /** Items that the pointer is currently within */ + std::set within_items; /** the item that is currently grabbed, or 0 */ Item const * _grabbed_item; + /** the item that currently has key focus or 0 */ + Item const * _focused_item; }; /** A GTK::Alignment with a GtkCanvas inside it plus some Gtk::Adjustments for diff --git a/libs/canvas/canvas/item.h b/libs/canvas/canvas/item.h index 538ac8bb79..7cad50e883 100644 --- a/libs/canvas/canvas/item.h +++ b/libs/canvas/canvas/item.h @@ -88,6 +88,21 @@ public: Group* parent () const { return _parent; } + + uint32_t depth() const; + const Item* closest_ancestor_with (const Item& other) const; + bool common_ancestor_within (uint32_t, const Item& other) const; + + /** returns true if this item is an ancestor of @param candidate, + * and false otherwise. + */ + bool is_ancestor_of (const Item& candidate) const { + return candidate.is_descendant_of (*this); + } + /** returns true if this Item is a descendant of @param candidate, + * and false otherwise. + */ + bool is_descendant_of (const Item& candidate) const; void set_position (Duple); void set_x_position (Coord); diff --git a/libs/canvas/group.cc b/libs/canvas/group.cc index 63ce48fdf2..0e2c1dbf94 100644 --- a/libs/canvas/group.cc +++ b/libs/canvas/group.cc @@ -74,8 +74,6 @@ Group::render (Rect const & area, Cairo::RefPtr context) const ensure_lut (); vector items = _lut->get (area); - ++render_depth; - #ifdef CANVAS_DEBUG if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) { cerr << string_compose ("%1GROUP %2 render %5 %3 items out of %4\n", @@ -83,6 +81,8 @@ Group::render (Rect const & area, Cairo::RefPtr context) const } #endif + ++render_depth; + for (vector::const_iterator i = items.begin(); i != items.end(); ++i) { if (!(*i)->visible ()) { @@ -112,16 +112,18 @@ Group::render (Rect const & area, Cairo::RefPtr context) const if (draw) { #ifdef CANVAS_DEBUG if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) { - cerr << _canvas->render_indent() << " render " - << ' ' - << (*i)->whatami() - << ' ' - << (*i)->name - << " item = " - << item - << " intersect = " - << draw.get() - << endl; + if (dynamic_cast(*i) == 0) { + cerr << _canvas->render_indent() << "render " + << ' ' + << (*i)->whatami() + << ' ' + << (*i)->name + << " item = " + << item + << " intersect = " + << draw.get() + << endl; + } } #endif diff --git a/libs/canvas/item.cc b/libs/canvas/item.cc index 79351846db..534ae6d52e 100644 --- a/libs/canvas/item.cc +++ b/libs/canvas/item.cc @@ -287,6 +287,103 @@ Item::reparent (Group* new_parent) _parent->add (this); } +bool +Item::common_ancestor_within (uint32_t limit, const Item& other) const +{ + uint32_t d1 = depth(); + uint32_t d2 = other.depth(); + const Item* i1 = this; + const Item* i2 = &other; + + /* move towards root until we are at the same level + for both items + */ + + while (d1 != d2) { + if (d1 > d2) { + i1 = i1->parent(); + d1--; + limit--; + } else { + i2 = i2->parent(); + d2--; + limit--; + } + if (limit == 0) { + return false; + } + } + + /* now see if there is a common parent */ + + while (i1 != i2) { + if (i1) { + i1 = i1->parent(); + } + if (i2) { + i2 = i2->parent (); + } + + limit--; + if (limit == 0) { + return false; + } + } + + return true; +} + +const Item* +Item::closest_ancestor_with (const Item& other) const +{ + uint32_t d1 = depth(); + uint32_t d2 = other.depth(); + const Item* i1 = this; + const Item* i2 = &other; + + /* move towards root until we are at the same level + for both items + */ + + while (d1 != d2) { + if (d1 > d2) { + i1 = i1->parent(); + d1--; + } else { + i2 = i2->parent(); + d2--; + } + } + + /* now see if there is a common parent */ + + while (i1 != i2) { + if (i1) { + i1 = i1->parent(); + } + if (i2) { + i2 = i2->parent (); + } + } + + return i1; +} + +bool +Item::is_descendant_of (const Item& candidate) const +{ + Item const * i = _parent; + + while (i) { + if (i == &candidate) { + return true; + } + i = i->parent(); + } + + return false; +} + void Item::grab_focus () { @@ -437,9 +534,22 @@ Item::whatami () const return type.substr (type.find_last_of (':') + 1); } +uint32_t +Item::depth () const +{ + Item* i = _parent; + int d = 0; + while (i) { + ++d; + i = i->parent(); + } + return d; +} + ostream& ArdourCanvas::operator<< (ostream& o, const Item& i) { i.dump (o); return o; } +