Merge branch 'cairocanvas' of git.ardour.org:ardour/ardour into cairocanvas
[ardour.git] / libs / canvas / canvas.cc
index 93637db2aec58fe9d8ce69ce6eb2487049f5d996..d5d85623612c33f8bbe1a54ca28fada03a5c49b2 100644 (file)
@@ -32,6 +32,7 @@
 
 #include "canvas/canvas.h"
 #include "canvas/debug.h"
+#include "canvas/line.h"
 
 using namespace std;
 using namespace ArdourCanvas;
@@ -69,7 +70,7 @@ Canvas::render (Rect const & area, Cairo::RefPtr<Cairo::Context> const & context
 {
 #ifdef CANVAS_DEBUG
        if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
-               cerr << "RENDER: " << area << endl;
+               cerr << this << " RENDER: " << area << endl;
                //cerr << "CANVAS @ " << this << endl;
                //dump (cerr);
                //cerr << "-------------------------\n";
@@ -86,16 +87,18 @@ Canvas::render (Rect const & area, Cairo::RefPtr<Cairo::Context> const & context
 
        boost::optional<Rect> draw = root_bbox->intersection (area);
        if (draw) {
-
-               // context->rectangle (area.x0, area.y0, area.x1 - area.x0, area.y1 - area.y0);
-               // context->set_source_rgba (1.0, 0, 0, 1.0);
-               // context->fill ();
-
+               
                /* there's a common area between the root and the requested
                   area, so render it.
                */
 
                _root.render (*draw, context);
+
+               // This outlines the rect being rendered, after it has been drawn.
+               // context->rectangle (draw->x0, draw->y0, draw->x1 - draw->x0, draw->y1 - draw->y0);
+               // context->set_source_rgba (1.0, 0, 0, 1.0);
+               // context->stroke ();
+
        }
 
 }
@@ -193,8 +196,12 @@ Duple
 Canvas::canvas_to_window (Duple const & d) const
 {
        Duple wd = d.translate (Duple (-_scroll_offset_x, -_scroll_offset_y));
+
+       /* Note that this intentionally always returns integer coordinates */
+
        wd.x = round (wd.x);
        wd.y = round (wd.y);
+
        return wd;
 }      
 
@@ -208,10 +215,14 @@ Rect
 Canvas::canvas_to_window (Rect const & r) const
 {
        Rect wr = r.translate (Duple (-_scroll_offset_x, -_scroll_offset_y));
-       wr.x0 = floor (wr.x0);
-       wr.x1 = ceil (wr.x1);
-       wr.y0 = floor (wr.y0);
-       wr.y1 = ceil (wr.y1);
+
+       /* Note that this intentionally always returns integer coordinates */
+
+       wr.x0 = round (wr.x0);
+       wr.x1 = round (wr.x1);
+       wr.y0 = round (wr.y0);
+       wr.y1 = round (wr.y1);
+
        return wr;
 }      
 
@@ -251,7 +262,7 @@ void
 Canvas::queue_draw_item_area (Item* item, Rect area)
 {
        ArdourCanvas::Rect canvas_area = item->item_to_canvas (area);
-       // cerr << "CANVAS " << this << " for " << item->whatami() << ' ' << item->name << " invalidate " << area << " TRANSLATE AS " << canvas_area << endl;
+       // cerr << "CANVAS " << this << " for " << item << ' ' << item->whatami() << ' ' << item->name << " invalidate " << area << " TRANSLATE AS " << canvas_area << " window = " << canvas_to_window (canvas_area) << std::endl;
        request_redraw (canvas_area);
 }
 
@@ -301,6 +312,20 @@ GtkCanvas::pick_current_item (Duple const & point, int state)
        vector<Item const *> items;
        _root.add_items_at_point (point, items);
 
+       DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("%1 covers %2 items\n", point, items.size()));
+
+#ifndef NDEBUG
+       if (DEBUG_ENABLED(PBD::DEBUG::CanvasEnterLeave)) {
+               for (vector<Item const*>::const_iterator it = items.begin(); it != items.end(); ++it) {
+#ifdef CANVAS_DEBUG
+                       std::cerr << "\tItem " << (*it)->whatami() << '/' << (*it)->name << std::endl;
+#else
+                       std::cerr << "\tItem " << (*it)->whatami() << std::endl;
+#endif
+               }
+       }
+#endif
+
        /* put all items at point that are event-sensitive and visible and NOT
           groups into within_items. Note that items is sorted from bottom to
           top, but we're going to reverse that for within_items so that its
@@ -314,9 +339,9 @@ GtkCanvas::pick_current_item (Duple const & point, int state)
 
                Item const * new_item = *i;
 
-               /* We ignore groups and we ignore items that ignore events */
+               /* We ignore invisible items, groups and items that ignore events */
 
-               if (new_item->ignore_events() || dynamic_cast<Group const *>(new_item) != 0) {
+               if (!new_item->visible() || new_item->ignore_events() || dynamic_cast<Group const *>(new_item) != 0) {
                        continue;
                }
                
@@ -326,17 +351,21 @@ GtkCanvas::pick_current_item (Duple const & point, int state)
        if (within_items.empty()) {
 
                /* no items at point, just send leave event below */
+               _new_current_item = 0;
 
        } else {
+
                if (within_items.front() == _current_item) {
                        /* uppermost item at point is already _current_item */
                        return;
                }
-               
+       
                _new_current_item = const_cast<Item*> (within_items.front());
        }
 
-       deliver_enter_leave (point, state);
+       if (_new_current_item != _current_item) {
+               deliver_enter_leave (point, state);
+       }
 }
 
 void
@@ -394,7 +423,7 @@ GtkCanvas::deliver_enter_leave (Duple const & point, int state)
        } else if (_current_item->is_descendant_of (*_new_current_item)) {
 
                /* move from descendant to ancestor (X: "_current_item is an
-                * inferior of _new_current_item") 
+                * inferior ("child") of _new_current_item") 
                 *
                 * Deliver "virtual" leave notifications to all items in the
                 * heirarchy between current and new_current.
@@ -411,7 +440,7 @@ GtkCanvas::deliver_enter_leave (Duple const & point, int state)
 
        } else if (_new_current_item->is_descendant_of (*_current_item)) {
                /* move from ancestor to descendant (X: "_new_current_item is
-                * an inferior of _current_item")
+                * an inferior ("child") of _current_item")
                 *
                 * Deliver "virtual" enter notifications to all items in the
                 * heirarchy between current and new_current.
@@ -452,6 +481,7 @@ GtkCanvas::deliver_enter_leave (Duple const & point, int state)
        if (_current_item && !_current_item->ignore_events ()) {
                leave_event.detail = leave_detail;
                _current_item->Event ((GdkEvent*)&leave_event);
+               DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("LEAVE %1/%2\n", _current_item->whatami(), _current_item->name));
        }
 
        leave_event.detail = GDK_NOTIFY_VIRTUAL;
@@ -459,18 +489,22 @@ GtkCanvas::deliver_enter_leave (Duple const & point, int state)
 
        for (vector<Item*>::iterator it = items_to_leave_virtual.begin(); it != items_to_leave_virtual.end(); ++it) {
                if (!(*it)->ignore_events()) {
+                       DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("leave %1/%2\n", (*it)->whatami(), (*it)->name));
                        (*it)->Event ((GdkEvent*)&leave_event);
                }
        }
 
        for (vector<Item*>::iterator it = items_to_enter_virtual.begin(); it != items_to_enter_virtual.end(); ++it) {
                if (!(*it)->ignore_events()) {
+                       DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("enter %1/%2\n", (*it)->whatami(), (*it)->name));
                        (*it)->Event ((GdkEvent*)&enter_event);
+                       // std::cerr << "enter " << (*it)->whatami() << '/' << (*it)->name << std::endl;
                }
        }
 
        if (_new_current_item && !_new_current_item->ignore_events()) {
                enter_event.detail = enter_detail;
+               DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("ENTER %1/%2\n", _new_current_item->whatami(), _new_current_item->name));
                _new_current_item->Event ((GdkEvent*)&enter_event);
        }
 
@@ -488,20 +522,24 @@ GtkCanvas::deliver_event (GdkEvent* event)
 {
        /* Point in in canvas coordinate space */
 
+       const Item* event_item;
+
        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",
                                                                       _grabbed_item, _grabbed_item->whatami(), _grabbed_item->name));
-               return _grabbed_item->Event (event);
+               event_item = _grabbed_item;
+       } else {
+               event_item = _current_item;
        }
 
-       if (!_current_item) {
+       if (!event_item) {
                return false;
        }
 
        /* run through the items from child to parent, until one claims the event */
 
-       Item* item = const_cast<Item*> (_current_item);
+       Item* item = const_cast<Item*> (event_item);
        
        while (item) {
 
@@ -518,7 +556,7 @@ GtkCanvas::deliver_event (GdkEvent* event)
                        return true;
                }
                
-               DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas event left unhandled by %1 %2\n", item->whatami(), item->name.empty() ? "[unknown]" : item->name));
+               DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas event %3 left unhandled by %1 %2\n", item->whatami(), item->name.empty() ? "[unknown]" : item->name, event_type_string (event->type)));
 
                if ((item = parent) == 0) {
                        break;
@@ -540,17 +578,10 @@ GtkCanvas::item_going_away (Item* item, boost::optional<Rect> bounding_box)
                queue_draw_item_area (item, bounding_box.get ());
        }
        
-       /* no need to send a leave event to this item, since it is going away 
-        */
-
        if (_new_current_item == item) {
                _new_current_item = 0;
        }
 
-       if (_current_item == item) {
-               _current_item = 0;
-       }
-
        if (_grabbed_item == item) {
                _grabbed_item = 0;
        }
@@ -559,7 +590,12 @@ GtkCanvas::item_going_away (Item* item, boost::optional<Rect> bounding_box)
                _focused_item = 0;
        }
 
-       pick_current_item (0); // no mouse state
+       if (_current_item == item) {
+               /* no need to send a leave event to this item, since it is going away 
+                */
+               _current_item = 0;
+               pick_current_item (0); // no mouse state
+       }
        
 }
 
@@ -570,10 +606,8 @@ GtkCanvas::item_going_away (Item* item, boost::optional<Rect> bounding_box)
 bool
 GtkCanvas::on_expose_event (GdkEventExpose* ev)
 {
-       Cairo::RefPtr<Cairo::Context> c = get_window()->create_cairo_context ();
-
-       render (Rect (ev->area.x, ev->area.y, ev->area.x + ev->area.width, ev->area.y + ev->area.height), c);
-
+       Cairo::RefPtr<Cairo::Context> cairo_context = get_window()->create_cairo_context ();
+       render (Rect (ev->area.x, ev->area.y, ev->area.x + ev->area.width, ev->area.y + ev->area.height), cairo_context);
        return true;
 }
 
@@ -660,15 +694,6 @@ GtkCanvas::on_motion_notify_event (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.
-               */
-               DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("%1 %2 (%3) was grabbed, send MOTION event there\n",
-                                                                      _grabbed_item, _grabbed_item->whatami(), _grabbed_item->name));
-               return _grabbed_item->Event (reinterpret_cast<GdkEvent*> (&copy));
-       }
-
        pick_current_item (where, ev->state);
 
        /* Now deliver the motion event.  It may seem a little inefficient
@@ -703,8 +728,13 @@ GtkCanvas::on_leave_notify_event (GdkEventCrossing* ev)
 void
 GtkCanvas::request_redraw (Rect const & request)
 {
-       Rect area = canvas_to_window (request);
-       queue_draw_area (floor (area.x0), floor (area.y0), ceil (area.width()), ceil (area.height()));
+       boost::optional<Rect> req = request.intersection (visible_area());
+
+       if (req) {
+               Rect r = req.get();
+               Rect area = canvas_to_window (r);
+               queue_draw_area (area.x0, area.y0, area.width(), area.height());
+       }
 }
 
 /** Called to request that we try to get a particular size for ourselves.