/** Construct a new Canvas */
Canvas::Canvas ()
: _root (this)
- , _log_renders (true)
, _scroll_offset_x (0)
, _scroll_offset_y (0)
{
}
#endif
- // checkpoint ("render", "-> render");
render_count = 0;
- context->save ();
-
-#ifdef CANVAS_DEBUG
- if (getenv ("ARDOUR_REDRAW_CANVAS")) {
- /* light up the canvas to show redraws */
- context->set_source_rgba (random()%255 / 255.0,
- random()%255 / 255.0,
- random()%255 / 255.0,
- 0.3);
- context->rectangle (area.x0, area.y0, area.width(), area.height());
- context->fill ();
- }
-#endif
-
- /* clip to the requested area */
- context->rectangle (area.x0, area.y0, area.width(), area.height());
- context->clip ();
-
boost::optional<Rect> root_bbox = _root.bounding_box();
if (!root_bbox) {
/* the root has no bounding box, so there's nothing to render */
- // checkpoint ("render", "no root bbox");
- context->restore ();
return;
}
- boost::optional<Rect> draw = root_bbox.get().intersection (area);
+ boost::optional<Rect> draw = root_bbox->intersection (area);
if (draw) {
/* there's a common area between the root and the requested
area, so render it.
*/
- // checkpoint ("render", "... root");
- context->stroke ();
_root.render (*draw, context);
}
- if (_log_renders) {
- _renders.push_back (area);
- }
-
- context->restore ();
-
-#ifdef CANVAS_DEBUG
- if (getenv ("ARDOUR_HARLEQUIN_CANVAS")) {
- /* light up the canvas to show redraws */
- context->set_source_rgba (random()%255 / 255.0,
- random()%255 / 255.0,
- random()%255 / 255.0,
- 0.4);
- context->rectangle (area.x0, area.y0, area.width(), area.height());
- context->fill ();
- }
+#if 0
+ /* debug render area */
+ Rect r = _root.item_to_window (area);
+ context->rectangle (r.x0, r.y0, r.x1 - r.x0, r.y1 - r.y0);
+ context->set_source_rgba (1.0, 0.0, 0.0, 1.0);
+ context->stroke ();
#endif
- // checkpoint ("render", "<- render");
}
ostream&
* to be in parent coordinate space since the bounding box of
* an item does not change when moved. If we use
* item->item_to_canvas() on the old bounding box, we will be
+
* using the item's new position, and so will compute the wrong
* invalidation area. If we use the parent (which has not
* moved, then this will work.
*/
-
queue_draw_item_area (item->parent(), pre_change_parent_bounding_box.get ());
}
Canvas::queue_draw_item_area (Item* item, Rect area)
{
ArdourCanvas::Rect canvas_area = item->item_to_canvas (area);
- // cerr << "CANVAS Invalidate " << area << " TRANSLATE AS " << canvas_area << endl;
+ // cerr << "CANVAS " << this << " for " << item->whatami() << ' ' << item->name << " invalidate " << area << " TRANSLATE AS " << canvas_area << endl;
request_redraw (canvas_area);
}
add_events (Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::POINTER_MOTION_MASK);
}
-/** Handler for button presses on the canvas.
- * @param ev GDK event.
- */
-bool
-GtkCanvas::button_handler (GdkEventButton* ev)
-{
- DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas button %3 %1 %1\n", ev->x, ev->y, (ev->type == GDK_BUTTON_PRESS ? "press" : "release")));
- /* The Duple that we are passing in here is in canvas coordinates */
- return deliver_event (Duple (ev->x, ev->y), reinterpret_cast<GdkEvent*> (ev));
-}
-
/** Handler for pointer motion events on the canvas.
* @param ev GDK event.
* @return true if the motion event was handled, otherwise false.
bool
GtkCanvas::motion_notify_handler (GdkEventMotion* ev)
{
+ DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas motion @ %1, %2\n", ev->x, ev->y));
+
if (_grabbed_item) {
/* if we have a grabbed item, it gets just the motion event,
since no enter/leave events can have happened.
Duple point (ev->x, ev->y);
- enter_leave_items (point);
+ enter_leave_items (point, ev->state);
/* Now deliver the motion event. It may seem a little inefficient
to recompute the items under the event, but the enter notify/leave
}
void
-GtkCanvas::enter_leave_items ()
+GtkCanvas::enter_leave_items (int state)
{
int x;
int y;
return;
}
- enter_leave_items (window_to_canvas (Duple (x, y)));
+ enter_leave_items (window_to_canvas (Duple (x, y)), state);
}
void
-GtkCanvas::enter_leave_items (Duple const & point)
+GtkCanvas::enter_leave_items (Duple const & point, int state)
{
/* find the items at the given position */
vector<Item const *> items;
_root.add_items_at_point (point, items);
+ GdkEventCrossing enter_event;
+ enter_event.type = GDK_ENTER_NOTIFY;
+ enter_event.window = get_window()->gobj();
+ enter_event.send_event = 0;
+ enter_event.subwindow = 0;
+ enter_event.mode = GDK_CROSSING_NORMAL;
+ enter_event.detail = GDK_NOTIFY_NONLINEAR;
+ enter_event.focus = FALSE;
+ enter_event.state = state;
+ enter_event.x = point.x;
+ enter_event.y = point.y;
+
+ 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 */
- GdkEventCrossing leave_event;
- leave_event.type = GDK_LEAVE_NOTIFY;
- leave_event.x = point.x;
- leave_event.y = point.y;
- cerr << "Leaving (without entering)" << _current_item->name << endl;
+ 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 bottom to top, so reverse through it from top
- * to bottom to find the first event-sensitive item and notify that
+ /* 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
*/
+ 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";
- Item const * new_item = *i;
+ for (vector<Item const*>::const_reverse_iterator i = items.rbegin(); i != items.rend(); ++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 && _current_item != new_item) {
+
+ if (_current_item == new_item) {
+ // cerr << "continue2\n";
+ continue;
+ }
+
+ if (_current_item) {
/* leave event */
- GdkEventCrossing leave_event;
- leave_event.type = GDK_LEAVE_NOTIFY;
- leave_event.x = point.x;
- leave_event.y = point.y;
- cerr << "Leaving " << _current_item->name << endl;
+ 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 ();
}
if (new_item && _current_item != new_item) {
/* enter event */
- GdkEventCrossing enter_event;
- enter_event.type = GDK_ENTER_NOTIFY;
- enter_event.x = point.x;
- enter_event.y = point.y;
- cerr << "Entering (" << new_item->name << ") " << new_item->whatami() << endl;
- new_item->Event (reinterpret_cast<GdkEvent*> (&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 ();
+ break;
}
-
- _current_item = new_item;
- break;
+
+ // cerr << "Loop around again\n";
}
}
bool
GtkCanvas::deliver_event (Duple point, GdkEvent* event)
{
+ /* Point in in canvas coordinate space */
+
if (_grabbed_item) {
/* we have a grabbed item, so everything gets sent there */
DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("%1 %2 (%3) was grabbed, send event there\n",
if (_current_item == item) {
_current_item = 0;
+ queue_draw ();
}
if (_grabbed_item == item) {
_grabbed_item = 0;
}
- enter_leave_items ();
+ enter_leave_items (0); // no mouse state
}
bool
GtkCanvas::on_expose_event (GdkEventExpose* ev)
{
-
Cairo::RefPtr<Cairo::Context> c = get_window()->create_cairo_context ();
-
- /* WINDOW CANVAS
- * 0,0 _scroll_offset_x, _scroll_offset_y
- */
-
- /* render using canvas coordinates */
- Rect canvas_area (ev->area.x, ev->area.y, ev->area.x + ev->area.width, ev->area.y + ev->area.height);
- canvas_area = canvas_area.translate (Duple (_scroll_offset_x, _scroll_offset_y));
-
- /* things are going to render to the cairo surface with canvas
- * coordinates:
- *
- * an item at window/cairo 0,0 will have canvas_coords _scroll_offset_x,_scroll_offset_y
- *
- * let them render at their natural coordinates by using cairo_translate()
- */
+ render (Rect (ev->area.x, ev->area.y, ev->area.x + ev->area.width, ev->area.y + ev->area.height), c);
- c->translate (-_scroll_offset_x, -_scroll_offset_y);
+#if 1
+ if (_current_item) {
+ boost::optional<Rect> orect = _current_item->bounding_box();
+ if (orect) {
+ Rect r = _current_item->item_to_window (orect.get());
+ c->rectangle (r.x0, r.y0, r.x1 - r.x0, r.y1 - r.y0);
+ c->set_source_rgba (1.0, 0.0, 0.0, 1.0);
+ c->stroke ();
+ }
+ }
+#endif
- render (canvas_area, c);
return true;
}
{
/* translate event coordinates from window to canvas */
- GdkEvent copy = *((GdkEvent*)ev);
Duple where = window_to_canvas (Duple (ev->x, ev->y));
-
- copy.button.x = where.x;
- copy.button.y = where.y;
/* Coordinates in the event will be canvas coordinates, correctly adjusted
for scroll if this GtkCanvas is in a GtkCanvasViewport.
*/
- return button_handler ((GdkEventButton*) ©);
+
+ DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas button press @ %1, %2 => %3\n", ev->x, ev->y, where));
+ return deliver_event (where, reinterpret_cast<GdkEvent*>(ev));
}
/** Handler for GDK button release events.
{
/* translate event coordinates from window to canvas */
- GdkEvent copy = *((GdkEvent*)ev);
Duple where = window_to_canvas (Duple (ev->x, ev->y));
- copy.button.x = where.x;
- copy.button.y = where.y;
-
/* Coordinates in the event will be canvas coordinates, correctly adjusted
for scroll if this GtkCanvas is in a GtkCanvasViewport.
*/
- return button_handler ((GdkEventButton*) ©);
+
+ DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas button release @ %1, %2 => %3\n", ev->x, ev->y, where));
+ return deliver_event (where, reinterpret_cast<GdkEvent*>(ev));
}
/** Handler for GDK motion events.
GtkCanvas::request_redraw (Rect const & request)
{
Rect area = canvas_to_window (request);
- // cerr << "Invalidate " << request << " TRANSLATE AS " << area << endl;
+ // cerr << this << " Invalidate " << request << " TRANSLATE AS " << area << endl;
queue_draw_area (floor (area.x0), floor (area.y0), ceil (area.x1) - floor (area.x0), ceil (area.y1) - floor (area.y0));
}