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 ();
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();
}
}
return;
}
- fade_in_handle->show ();
-
/* smallest size for a fade is 64 frames */
width = std::max ((framecnt_t) 64, width);
return;
}
- fade_out_handle->show ();
-
/* smallest size for a fade is 64 frames */
width = std::max ((framecnt_t) 64, width);
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 ();
}
}
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 ();
}
}
#include "evoral/Curve.hpp"
+#include "canvas/debug.h"
+
#include "automation_line.h"
#include "control_point.h"
#include "gui_thread.h"
_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);
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;
case GDK_ENTER_NOTIFY:
set_entered_track (tv);
+ ret = true;
break;
case GDK_LEAVE_NOTIFY:
}
- /* 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
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;
}
- /* 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
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;
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;
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;
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;
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) {
/** 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);
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<Item const *> items;
- _root.add_items_at_point (point, items);
+ if (_grabbed_item) {
+ return;
+ }
GdkEventCrossing enter_event;
enter_event.type = GDK_ENTER_NOTIFY;
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<GdkEvent*> (&leave_event));
- _current_item = 0;
- }
- 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
- */
+ /* find the items at the given position */
+
+ vector<Item const *> 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.
+ */
- // cerr << "E/L: " << items.size() << " to check at " << point << endl;
-#ifdef CANVAS_DEBUG
- // for (vector<Item const*>::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";
+ vector<Item const *>::const_iterator i;
+ vector<Item const *> newly_entered;
+ Item const * new_item;
- for (vector<Item const*>::const_reverse_iterator i = items.rbegin(); i != items.rend(); ++i) {
+ for (i = items.begin(); i != items.end(); ++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";
+ new_item = *i;
+
+ if (new_item->ignore_events() || !new_item->visible()) {
continue;
}
- if (_current_item == new_item) {
- // cerr << "continue2\n";
- continue;
+ pair<set<Item const *>::iterator,bool> res = within_items.insert (new_item);
+
+ if (res.second) {
+ newly_entered.push_back (new_item);
}
+ }
+
+ /* 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"
+ */
- 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<GdkEvent*> (&leave_event));
- queue_draw ();
+ for (set<Item const *>::const_iterator i = within_items.begin(); i != within_items.end(); ) {
+
+ set<Item const *>::const_iterator tmp = i;
+ ++tmp;
+
+ new_item = *i;
+
+ boost::optional<Rect> 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<GdkEvent*> (&leave_event));
+ within_items.erase (i);
+ }
}
- 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<GdkEvent*> (&enter_event));
- queue_draw ();
+ i = tmp;
+ }
+
+ /* for every item in "newly_entered", send an enter event (and propagate it up the
+ item tree until it is handled
+ */
+
+ for (vector<Item const*>::const_iterator i = newly_entered.begin(); i != newly_entered.end(); ++i) {
+ new_item = *i;
+
+
+ if (new_item->Event (reinterpret_cast<GdkEvent*> (&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<Item const *>::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
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
}
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.
*/
_grabbed_item = item;
}
+
/** `Ungrab' any item that was previously grabbed */
void
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
#ifndef __CANVAS_CANVAS_H__
#define __CANVAS_CANVAS_H__
+#include <set>
+
#include <gdkmm/window.h>
#include <gtkmm/eventbox.h>
#include <gtkmm/alignment.h>
#include <cairomm/surface.h>
#include <cairomm/context.h>
+
#include "pbd/signals.h"
#include "canvas/root_group.h"
/** 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<Cairo::Context> const &) const;
/** @return root group */
void request_size (Duple);
void grab (Item *);
void ungrab ();
+ void focus (Item *);
+ void unfocus (Item*);
Cairo::RefPtr<Cairo::Context> context ();
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 *);
void item_going_away (Item *, boost::optional<Rect>);
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<Item const *> 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
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);
ensure_lut ();
vector<Item*> 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",
}
#endif
+ ++render_depth;
+
for (vector<Item*>::const_iterator i = items.begin(); i != items.end(); ++i) {
if (!(*i)->visible ()) {
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<Group*>(*i) == 0) {
+ cerr << _canvas->render_indent() << "render "
+ << ' '
+ << (*i)->whatami()
+ << ' '
+ << (*i)->name
+ << " item = "
+ << item
+ << " intersect = "
+ << draw.get()
+ << endl;
+ }
}
#endif
_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 ()
{
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;
}
+