use window-based coordinates when picking current item so that we get per-item (per...
[ardour.git] / libs / canvas / canvas.cc
1 /*
2     Copyright (C) 2011 Paul Davis
3     Author: Carl Hetherington <cth@carlh.net>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19 */
20
21 /** @file  canvas/canvas.cc
22  *  @brief Implementation of the main canvas classes.
23  */
24
25 #include <list>
26 #include <cassert>
27 #include <gtkmm/adjustment.h>
28 #include <gtkmm/label.h>
29
30 #include "pbd/compose.h"
31 #include "pbd/stacktrace.h"
32
33 #include "canvas/canvas.h"
34 #include "canvas/debug.h"
35 #include "canvas/line.h"
36 #include "canvas/scroll_group.h"
37
38 using namespace std;
39 using namespace ArdourCanvas;
40
41 /** Construct a new Canvas */
42 Canvas::Canvas ()
43         : _root (this)
44 {
45         set_epoch ();
46 }
47
48 void
49 Canvas::scroll_to (Coord x, Coord y)
50 {
51         _scroll_offset = Duple (x, y);
52
53         /* We do things this way because we do not want to recurse through
54            the canvas for every scroll. In the presence of large MIDI
55            tracks this means traversing item lists that include
56            thousands of items (notes).
57
58            This design limits us to moving only those items (groups, typically)
59            that should move in certain ways as we scroll. In other terms, it
60            becomes O(1) rather than O(N).
61         */
62
63         for (list<ScrollGroup*>::iterator i = scrollers.begin(); i != scrollers.end(); ++i) {
64                 (*i)->scroll_to (_scroll_offset);
65         }
66
67         pick_current_item (0); // no current mouse position 
68 }
69
70 void
71 Canvas::add_scroller (ScrollGroup& i)
72 {
73         scrollers.push_back (&i);
74 }
75
76 void
77 Canvas::zoomed ()
78 {
79         pick_current_item (0); // no current mouse position
80 }
81
82 /** Render an area of the canvas.
83  *  @param area Area in window coordinates.
84  *  @param context Cairo context to render to.
85  */
86 void
87 Canvas::render (Rect const & area, Cairo::RefPtr<Cairo::Context> const & context) const
88 {
89 #ifdef CANVAS_DEBUG
90         if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
91                 cerr << this << " RENDER: " << area << endl;
92                 //cerr << "CANVAS @ " << this << endl;
93                 //dump (cerr);
94                 //cerr << "-------------------------\n";
95         }
96 #endif
97
98         render_count = 0;
99         
100         boost::optional<Rect> root_bbox = _root.bounding_box();
101         if (!root_bbox) {
102                 /* the root has no bounding box, so there's nothing to render */
103                 return;
104         }
105
106         boost::optional<Rect> draw = root_bbox->intersection (area);
107         if (draw) {
108                 
109                 /* there's a common area between the root and the requested
110                    area, so render it.
111                 */
112
113                 _root.render (*draw, context);
114
115                 // This outlines the rect being rendered, after it has been drawn.
116                 // context->rectangle (draw->x0, draw->y0, draw->x1 - draw->x0, draw->y1 - draw->y0);
117                 // context->set_source_rgba (1.0, 0, 0, 1.0);
118                 // context->stroke ();
119
120         }
121
122 }
123
124 ostream&
125 operator<< (ostream& o, Canvas& c)
126 {
127         c.dump (o);
128         return o;
129 }
130
131 std::string
132 Canvas::indent() const
133
134         string s;
135
136         for (int n = 0; n < ArdourCanvas::dump_depth; ++n) {
137                 s += '\t';
138         }
139
140         return s;
141 }
142
143 std::string
144 Canvas::render_indent() const
145
146         string s;
147
148         for (int n = 0; n < ArdourCanvas::render_depth; ++n) {
149                 s += ' ';
150         }
151
152         return s;
153 }
154
155 void
156 Canvas::dump (ostream& o) const
157 {
158         dump_depth = 0;
159         _root.dump (o);
160 }       
161
162 /** Called when an item has been shown or hidden.
163  *  @param item Item that has been shown or hidden.
164  */
165 void
166 Canvas::item_shown_or_hidden (Item* item)
167 {
168         boost::optional<Rect> bbox = item->bounding_box ();
169         if (bbox) {
170                 queue_draw_item_area (item, bbox.get ());
171         }
172 }
173
174 /** Called when an item has a change to its visual properties
175  *  that do NOT affect its bounding box.
176  *  @param item Item that has been modified.
177  */
178 void
179 Canvas::item_visual_property_changed (Item* item)
180 {
181         boost::optional<Rect> bbox = item->bounding_box ();
182         if (bbox) {
183                 queue_draw_item_area (item, bbox.get ());
184         }
185 }
186
187 /** Called when an item has changed, but not moved.
188  *  @param item Item that has changed.
189  *  @param pre_change_bounding_box The bounding box of item before the change,
190  *  in the item's coordinates.
191  */
192 void
193 Canvas::item_changed (Item* item, boost::optional<Rect> pre_change_bounding_box)
194 {
195         if (pre_change_bounding_box) {
196                 /* request a redraw of the item's old bounding box */
197                 queue_draw_item_area (item, pre_change_bounding_box.get ());
198         }
199
200         boost::optional<Rect> post_change_bounding_box = item->bounding_box ();
201         if (post_change_bounding_box) {
202                 /* request a redraw of the item's new bounding box */
203                 queue_draw_item_area (item, post_change_bounding_box.get ());
204         }
205 }
206
207 Duple
208 Canvas::window_to_canvas (Duple const & d) const
209 {
210         return d.translate (_scroll_offset);
211 }
212
213 Duple
214 Canvas::canvas_to_window (Duple const & d, bool rounded) const
215 {
216         Duple wd = d.translate (-_scroll_offset);
217
218         /* Note that this intentionally almost always returns integer coordinates */
219
220         if (rounded) {
221                 wd.x = round (wd.x);
222                 wd.y = round (wd.y);
223         }
224
225         return wd;
226 }
227
228 Rect
229 Canvas::window_to_canvas (Rect const & r) const
230 {
231         return r.translate (Duple (_scroll_offset.x, _scroll_offset.y));
232 }
233
234 Rect
235 Canvas::canvas_to_window (Rect const & r, bool rounded) const
236 {
237         Rect wr = r.translate (Duple (-_scroll_offset.x, -_scroll_offset.y));
238
239         /* Note that this intentionally almost always returns integer coordinates */
240
241         if (rounded) {
242                 wr.x0 = round (wr.x0);
243                 wr.x1 = round (wr.x1);
244                 wr.y0 = round (wr.y0);
245                 wr.y1 = round (wr.y1);
246         }
247
248         return wr;
249 }       
250
251 /** Called when an item has moved.
252  *  @param item Item that has moved.
253  *  @param pre_change_parent_bounding_box The bounding box of the item before
254  *  the move, in its parent's coordinates.
255  */
256 void
257 Canvas::item_moved (Item* item, boost::optional<Rect> pre_change_parent_bounding_box)
258 {
259         if (pre_change_parent_bounding_box) {
260                 /* request a redraw of where the item used to be. The box has
261                  * to be in parent coordinate space since the bounding box of
262                  * an item does not change when moved. If we use
263                  * item->item_to_canvas() on the old bounding box, we will be
264
265                  * using the item's new position, and so will compute the wrong
266                  * invalidation area. If we use the parent (which has not
267                  * moved, then this will work.
268                  */
269                 queue_draw_item_area (item->parent(), pre_change_parent_bounding_box.get ());
270         }
271
272         boost::optional<Rect> post_change_bounding_box = item->bounding_box ();
273         if (post_change_bounding_box) {
274                 /* request a redraw of where the item now is */
275                 queue_draw_item_area (item, post_change_bounding_box.get ());
276         }
277 }
278
279 /** Request a redraw of a particular area in an item's coordinates.
280  *  @param item Item.
281  *  @param area Area to redraw in the item's coordinates.
282  */
283 void
284 Canvas::queue_draw_item_area (Item* item, Rect area)
285 {
286         request_redraw (item->item_to_window (area));
287 }
288
289 /** Construct a GtkCanvas */
290 GtkCanvas::GtkCanvas ()
291         : _current_item (0)
292         , _new_current_item (0)
293         , _grabbed_item (0)
294         , _focused_item (0)
295 {
296         /* these are the events we want to know about */
297         add_events (Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::POINTER_MOTION_MASK |
298                     Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK);
299 }
300
301 void
302 GtkCanvas::pick_current_item (int state)
303 {
304         int x;
305         int y;
306
307         /* this version of ::pick_current_item() is called after an item is
308          * added or removed, so we have no coordinates to work from as is the
309          * case with a motion event. Find out where the mouse is and use that.
310          */
311              
312         Glib::RefPtr<const Gdk::Window> pointer_window = Gdk::Display::get_default()->get_window_at_pointer (x, y);
313
314         if (pointer_window != get_window()) {
315                 return;
316         }
317
318         pick_current_item (Duple (x, y), state);
319 }
320                 
321 void
322 GtkCanvas::pick_current_item (Duple const & point, int state)
323 {
324         /* we do not enter/leave items during a drag/grab */
325
326         if (_grabbed_item) {
327                 return;
328         }
329
330         /* find the items at the given position */
331
332         vector<Item const *> items;
333         _root.add_items_at_point (point, items);
334
335         DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("%1 covers %2 items\n", point, items.size()));
336
337 #ifndef NDEBUG
338         if (DEBUG_ENABLED(PBD::DEBUG::CanvasEnterLeave)) {
339                 for (vector<Item const*>::const_iterator it = items.begin(); it != items.end(); ++it) {
340 #ifdef CANVAS_DEBUG
341                         std::cerr << "\tItem " << (*it)->whatami() << '/' << (*it)->name << std::endl;
342 #else
343                         std::cerr << "\tItem " << (*it)->whatami() << std::endl;
344 #endif
345                 }
346         }
347 #endif
348
349         /* put all items at point that are event-sensitive and visible and NOT
350            groups into within_items. Note that items is sorted from bottom to
351            top, but we're going to reverse that for within_items so that its
352            first item is the upper-most item that can be chosen as _current_item.
353         */
354         
355         vector<Item const *>::const_iterator i;
356         list<Item const *> within_items;
357
358         for (i = items.begin(); i != items.end(); ++i) {
359
360                 Item const * new_item = *i;
361
362                 /* We ignore invisible items, groups and items that ignore events */
363
364                 if (!new_item->visible() || new_item->ignore_events() || dynamic_cast<Group const *>(new_item) != 0) {
365                         continue;
366                 }
367                 
368                 within_items.push_front (new_item);
369         }
370
371         if (within_items.empty()) {
372
373                 /* no items at point, just send leave event below */
374                 _new_current_item = 0;
375
376         } else {
377
378                 if (within_items.front() == _current_item) {
379                         /* uppermost item at point is already _current_item */
380                         return;
381                 }
382         
383                 _new_current_item = const_cast<Item*> (within_items.front());
384         }
385
386         if (_new_current_item != _current_item) {
387                 deliver_enter_leave (point, state);
388         }
389 }
390
391 /** Deliver a series of enter & leave events based on the pointer position being at window
392  * coordinate @param point, and pointer @param state (modifier keys, etc)
393  */
394 void
395 GtkCanvas::deliver_enter_leave (Duple const & point, int state)
396 {
397         /* setup enter & leave event structures */
398
399         GdkEventCrossing enter_event;
400         enter_event.type = GDK_ENTER_NOTIFY;
401         enter_event.window = get_window()->gobj();
402         enter_event.send_event = 0;
403         enter_event.subwindow = 0;
404         enter_event.mode = GDK_CROSSING_NORMAL;
405         enter_event.focus = FALSE;
406         enter_event.state = state;
407         enter_event.x = point.x;
408         enter_event.y = point.y;
409
410         GdkEventCrossing leave_event = enter_event;
411         leave_event.type = GDK_LEAVE_NOTIFY;
412
413         Item* i;
414         GdkNotifyType enter_detail;
415         GdkNotifyType leave_detail;
416         vector<Item*> items_to_leave_virtual;
417         vector<Item*> items_to_enter_virtual;
418
419         if (_new_current_item == 0) {
420
421                 leave_detail = GDK_NOTIFY_UNKNOWN;
422
423                 if (_current_item) {
424
425                         /* no current item, so also send virtual leave events to the
426                          * entire heirarchy for the current item
427                          */
428
429                         for (i = _current_item->parent(); i ; i = i->parent()) {
430                                 items_to_leave_virtual.push_back (i);
431                         }
432                 }
433
434         } else if (_current_item == 0) {
435
436                 enter_detail = GDK_NOTIFY_UNKNOWN;
437
438                 /* no current item, so also send virtual enter events to the
439                  * entire heirarchy for the new item 
440                  */
441
442                 for (i = _new_current_item->parent(); i ; i = i->parent()) {
443                         items_to_enter_virtual.push_back (i);
444                 }
445
446         } else if (_current_item->is_descendant_of (*_new_current_item)) {
447
448                 /* move from descendant to ancestor (X: "_current_item is an
449                  * inferior ("child") of _new_current_item") 
450                  *
451                  * Deliver "virtual" leave notifications to all items in the
452                  * heirarchy between current and new_current.
453                  */
454                 
455                 for (i = _current_item->parent(); i && i != _new_current_item; i = i->parent()) {
456                         items_to_leave_virtual.push_back (i);
457                 }
458
459                 enter_detail = GDK_NOTIFY_INFERIOR;
460                 leave_detail = GDK_NOTIFY_ANCESTOR;
461
462
463         } else if (_new_current_item->is_descendant_of (*_current_item)) {
464                 /* move from ancestor to descendant (X: "_new_current_item is
465                  * an inferior ("child") of _current_item")
466                  *
467                  * Deliver "virtual" enter notifications to all items in the
468                  * heirarchy between current and new_current.
469                  */
470
471                 for (i = _new_current_item->parent(); i && i != _current_item; i = i->parent()) {
472                         items_to_enter_virtual.push_back (i);
473                 }
474
475                 enter_detail = GDK_NOTIFY_ANCESTOR;
476                 leave_detail = GDK_NOTIFY_INFERIOR;
477
478         } else {
479
480                 Item const * common_ancestor = _current_item->closest_ancestor_with (*_new_current_item);
481
482                 /* deliver virtual leave events to everything between _current
483                  * and common_ancestor.
484                  */
485
486                 for (i = _current_item->parent(); i && i != common_ancestor; i = i->parent()) {
487                         items_to_leave_virtual.push_back (i);
488                 }
489
490                 /* deliver virtual enter events to everything between
491                  * _new_current and common_ancestor.
492                  */
493
494                 for (i = _new_current_item->parent(); i && i != common_ancestor; i = i->parent()) {
495                         items_to_enter_virtual.push_back (i);
496                 }
497
498                 enter_detail = GDK_NOTIFY_NONLINEAR;
499                 leave_detail = GDK_NOTIFY_NONLINEAR;
500         }
501         
502
503         if (_current_item && !_current_item->ignore_events ()) {
504                 leave_event.detail = leave_detail;
505                 _current_item->Event ((GdkEvent*)&leave_event);
506                 DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("LEAVE %1/%2\n", _current_item->whatami(), _current_item->name));
507         }
508
509         leave_event.detail = GDK_NOTIFY_VIRTUAL;
510         enter_event.detail = GDK_NOTIFY_VIRTUAL;
511
512         for (vector<Item*>::iterator it = items_to_leave_virtual.begin(); it != items_to_leave_virtual.end(); ++it) {
513                 if (!(*it)->ignore_events()) {
514                         DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("leave %1/%2\n", (*it)->whatami(), (*it)->name));
515                         (*it)->Event ((GdkEvent*)&leave_event);
516                 }
517         }
518
519         for (vector<Item*>::iterator it = items_to_enter_virtual.begin(); it != items_to_enter_virtual.end(); ++it) {
520                 if (!(*it)->ignore_events()) {
521                         DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("enter %1/%2\n", (*it)->whatami(), (*it)->name));
522                         (*it)->Event ((GdkEvent*)&enter_event);
523                         // std::cerr << "enter " << (*it)->whatami() << '/' << (*it)->name << std::endl;
524                 }
525         }
526
527         if (_new_current_item && !_new_current_item->ignore_events()) {
528                 enter_event.detail = enter_detail;
529                 DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("ENTER %1/%2\n", _new_current_item->whatami(), _new_current_item->name));
530                 _new_current_item->Event ((GdkEvent*)&enter_event);
531         }
532
533         _current_item = _new_current_item;
534 }
535
536
537 /** Deliver an event to the appropriate item; either the grabbed item, or
538  *  one of the items underneath the event.
539  *  @param point Position that the event has occurred at, in canvas coordinates.
540  *  @param event The event.
541  */
542 bool
543 GtkCanvas::deliver_event (GdkEvent* event)
544 {
545         /* Point in in canvas coordinate space */
546
547         const Item* event_item;
548
549         if (_grabbed_item) {
550                 /* we have a grabbed item, so everything gets sent there */
551                 DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("%1 %2 (%3) was grabbed, send event there\n",
552                                                                        _grabbed_item, _grabbed_item->whatami(), _grabbed_item->name));
553                 event_item = _grabbed_item;
554         } else {
555                 event_item = _current_item;
556         }
557
558         if (!event_item) {
559                 return false;
560         }
561
562         /* run through the items from child to parent, until one claims the event */
563
564         Item* item = const_cast<Item*> (event_item);
565         
566         while (item) {
567
568                 Item* parent = item->parent ();
569
570                 if (!item->ignore_events () && 
571                     item->Event (event)) {
572                         /* this item has just handled the event */
573                         DEBUG_TRACE (
574                                 PBD::DEBUG::CanvasEvents,
575                                 string_compose ("canvas event handled by %1 %2\n", item->whatami(), item->name.empty() ? "[unknown]" : item->name)
576                                 );
577                         
578                         return true;
579                 }
580                 
581                 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)));
582
583                 if ((item = parent) == 0) {
584                         break;
585                 }
586
587         }
588
589         return false;
590 }
591
592 /** Called when an item is being destroyed.
593  *  @param item Item being destroyed.
594  *  @param bounding_box Last known bounding box of the item.
595  */
596 void
597 GtkCanvas::item_going_away (Item* item, boost::optional<Rect> bounding_box)
598 {
599         if (bounding_box) {
600                 queue_draw_item_area (item, bounding_box.get ());
601         }
602         
603         if (_new_current_item == item) {
604                 _new_current_item = 0;
605         }
606
607         if (_grabbed_item == item) {
608                 _grabbed_item = 0;
609         }
610
611         if (_focused_item == item) {
612                 _focused_item = 0;
613         }
614
615         ScrollGroup* sg = dynamic_cast<ScrollGroup*>(item);
616         if (sg) {
617                 scrollers.remove (sg);
618         }
619
620         if (_current_item == item) {
621                 /* no need to send a leave event to this item, since it is going away 
622                  */
623                 _current_item = 0;
624                 pick_current_item (0); // no mouse state
625         }
626         
627 }
628
629 /** Handler for GDK expose events.
630  *  @param ev Event.
631  *  @return true if the event was handled.
632  */
633 bool
634 GtkCanvas::on_expose_event (GdkEventExpose* ev)
635 {
636         Cairo::RefPtr<Cairo::Context> cairo_context = get_window()->create_cairo_context ();
637         render (Rect (ev->area.x, ev->area.y, ev->area.x + ev->area.width, ev->area.y + ev->area.height), cairo_context);
638         return true;
639 }
640
641 /** @return Our Cairo context, or 0 if we don't have one */
642 Cairo::RefPtr<Cairo::Context>
643 GtkCanvas::context ()
644 {
645         Glib::RefPtr<Gdk::Window> w = get_window ();
646         if (!w) {
647                 return Cairo::RefPtr<Cairo::Context> ();
648         }
649
650         return w->create_cairo_context ();
651 }
652
653 /** Handler for GDK button press events.
654  *  @param ev Event.
655  *  @return true if the event was handled.
656  */
657 bool
658 GtkCanvas::on_button_press_event (GdkEventButton* ev)
659 {
660         /* translate event coordinates from window to canvas */
661
662         GdkEvent copy = *((GdkEvent*)ev);
663         Duple winpos = Duple (ev->x, ev->y);
664         Duple where = window_to_canvas (winpos);
665         
666         pick_current_item (winpos, ev->state);
667
668         copy.button.x = where.x;
669         copy.button.y = where.y;
670         
671         /* Coordinates in the event will be canvas coordinates, correctly adjusted
672            for scroll if this GtkCanvas is in a GtkCanvasViewport.
673         */
674
675         DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas button press @ %1, %2 => %3\n", ev->x, ev->y, where));
676         return deliver_event (reinterpret_cast<GdkEvent*>(&copy));
677 }
678
679 /** Handler for GDK button release events.
680  *  @param ev Event.
681  *  @return true if the event was handled.
682  */
683 bool
684 GtkCanvas::on_button_release_event (GdkEventButton* ev)
685 {       
686         /* translate event coordinates from window to canvas */
687
688         GdkEvent copy = *((GdkEvent*)ev);
689         Duple winpos = Duple (ev->x, ev->y);
690         Duple where = window_to_canvas (winpos);
691         
692         pick_current_item (winpos, ev->state);
693
694         copy.button.x = where.x;
695         copy.button.y = where.y;
696
697         /* Coordinates in the event will be canvas coordinates, correctly adjusted
698            for scroll if this GtkCanvas is in a GtkCanvasViewport.
699         */
700
701         pick_current_item (where, ev->state);
702         DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas button release @ %1, %2 => %3\n", ev->x, ev->y, where));
703         return deliver_event (reinterpret_cast<GdkEvent*>(&copy));
704 }
705
706 /** Handler for GDK motion events.
707  *  @param ev Event.
708  *  @return true if the event was handled.
709  */
710 bool
711 GtkCanvas::on_motion_notify_event (GdkEventMotion* ev)
712 {
713         /* translate event coordinates from window to canvas */
714
715         GdkEvent copy = *((GdkEvent*)ev);
716         Duple point (ev->x, ev->y);
717         Duple where = window_to_canvas (point);
718
719         copy.motion.x = where.x;
720         copy.motion.y = where.y;
721
722         /* Coordinates in "copy" will be canvas coordinates, 
723         */
724
725         // DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas motion @ %1, %2\n", ev->x, ev->y));
726
727         pick_current_item (point, ev->state);
728
729         /* Now deliver the motion event.  It may seem a little inefficient
730            to recompute the items under the event, but the enter notify/leave
731            events may have deleted canvas items so it is important to
732            recompute the list in deliver_event.
733         */
734
735         return deliver_event (reinterpret_cast<GdkEvent*> (&copy));
736 }
737
738 bool
739 GtkCanvas::on_enter_notify_event (GdkEventCrossing* ev)
740 {
741         pick_current_item (Duple (ev->x, ev->y), ev->state);
742         return true;
743 }
744
745 bool
746 GtkCanvas::on_leave_notify_event (GdkEventCrossing* ev)
747 {
748         _new_current_item = 0;
749         deliver_enter_leave (Duple (ev->x, ev->y), ev->state);
750         return true;
751 }
752
753 /** Called to request a redraw of our canvas.
754  *  @param area Area to redraw, in window coordinates.
755  */
756 void
757 GtkCanvas::request_redraw (Rect const & request)
758 {
759         queue_draw_area (request.x0, request.y0, request.width(), request.height());
760 }
761
762 /** Called to request that we try to get a particular size for ourselves.
763  *  @param size Size to request, in pixels.
764  */
765 void
766 GtkCanvas::request_size (Duple size)
767 {
768         Duple req = size;
769
770         if (req.x > INT_MAX) {
771                 req.x = INT_MAX;
772         }
773
774         if (req.y > INT_MAX) {
775                 req.y = INT_MAX;
776         }
777
778         set_size_request (req.x, req.y);
779 }
780
781 /** `Grab' an item, so that all events are sent to that item until it is `ungrabbed'.
782  *  This is typically used for dragging items around, so that they are grabbed during
783  *  the drag.
784  *  @param item Item to grab.
785  */
786 void
787 GtkCanvas::grab (Item* item)
788 {
789         /* XXX: should this be doing gdk_pointer_grab? */
790         _grabbed_item = item;
791 }
792
793
794 /** `Ungrab' any item that was previously grabbed */
795 void
796 GtkCanvas::ungrab ()
797 {
798         /* XXX: should this be doing gdk_pointer_ungrab? */
799         _grabbed_item = 0;
800 }
801
802 /** Set keyboard focus on an item, so that all keyboard events are sent to that item until the focus
803  *  moves elsewhere.
804  *  @param item Item to grab.
805  */
806 void
807 GtkCanvas::focus (Item* item)
808 {
809         _focused_item = item;
810 }
811
812 void
813 GtkCanvas::unfocus (Item* item)
814 {
815         if (item == _focused_item) {
816                 _focused_item = 0;
817         }
818 }
819
820 /** @return The visible area of the canvas, in canvas coordinates */
821 Rect
822 GtkCanvas::visible_area () const
823 {
824         Distance const xo = _scroll_offset.x;
825         Distance const yo = _scroll_offset.y;
826         return Rect (xo, yo, xo + get_allocation().get_width (), yo + get_allocation().get_height ());
827 }
828
829 /** Create a GtkCanvaSViewport.
830  *  @param hadj Adjustment to use for horizontal scrolling.
831  *  @param vadj Adjustment to use for vertica scrolling.
832  */
833 GtkCanvasViewport::GtkCanvasViewport (Gtk::Adjustment& hadj, Gtk::Adjustment& vadj)
834         : Alignment (0, 0, 1.0, 1.0)
835         , hadjustment (hadj)
836         , vadjustment (vadj)
837 {
838         add (_canvas);
839
840         hadj.signal_value_changed().connect (sigc::mem_fun (*this, &GtkCanvasViewport::scrolled));
841         vadj.signal_value_changed().connect (sigc::mem_fun (*this, &GtkCanvasViewport::scrolled));
842 }
843
844 void
845 GtkCanvasViewport::scrolled ()
846 {
847         _canvas.scroll_to (hadjustment.get_value(), vadjustment.get_value());
848         queue_draw ();
849 }
850
851 /** Handler for when GTK asks us what minimum size we want.
852  *  @param req Requsition to fill in.
853  */
854 void
855 GtkCanvasViewport::on_size_request (Gtk::Requisition* req)
856 {
857         /* force the canvas to size itself */
858         // _canvas.root()->bounding_box(); 
859
860         req->width = 16;
861         req->height = 16;
862 }
863