avoid recursing through the entire canvas when scrolling - only scroll explicitly...
[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 canvas 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 (Duple (_scroll_offset.x, _scroll_offset.y));
211 }
212
213 Duple
214 Canvas::canvas_to_window (Duple const & d, bool rounded) const
215 {
216         Duple wd = d.translate (Duple (-_scroll_offset.x, -_scroll_offset.y));
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         ArdourCanvas::Rect canvas_area = item->item_to_canvas (area);
287         // cerr << "CANVAS " << this << " for " << item << ' ' << item->whatami() << ' ' << item->name << " invalidate " << area << " TRANSLATE AS " << canvas_area << " window = " << canvas_to_window (canvas_area) << std::endl;
288         request_redraw (canvas_area);
289 }
290
291 /** Construct a GtkCanvas */
292 GtkCanvas::GtkCanvas ()
293         : _current_item (0)
294         , _new_current_item (0)
295         , _grabbed_item (0)
296         , _focused_item (0)
297 {
298         /* these are the events we want to know about */
299         add_events (Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::POINTER_MOTION_MASK |
300                     Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK);
301 }
302
303 void
304 GtkCanvas::pick_current_item (int state)
305 {
306         int x;
307         int y;
308
309         /* this version of ::pick_current_item() is called after an item is
310          * added or removed, so we have no coordinates to work from as is the
311          * case with a motion event. Find out where the mouse is and use that.
312          */
313              
314         Glib::RefPtr<const Gdk::Window> pointer_window = Gdk::Display::get_default()->get_window_at_pointer (x, y);
315
316         if (pointer_window != get_window()) {
317                 return;
318         }
319
320         pick_current_item (window_to_canvas (Duple (x, y)), state);
321 }
322                 
323 void
324 GtkCanvas::pick_current_item (Duple const & point, int state)
325 {
326         /* we do not enter/leave items during a drag/grab */
327
328         if (_grabbed_item) {
329                 return;
330         }
331
332         /* find the items at the given position */
333
334         vector<Item const *> items;
335         _root.add_items_at_point (point, items);
336
337         DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("%1 covers %2 items\n", point, items.size()));
338
339 #ifndef NDEBUG
340         if (DEBUG_ENABLED(PBD::DEBUG::CanvasEnterLeave)) {
341                 for (vector<Item const*>::const_iterator it = items.begin(); it != items.end(); ++it) {
342 #ifdef CANVAS_DEBUG
343                         std::cerr << "\tItem " << (*it)->whatami() << '/' << (*it)->name << std::endl;
344 #else
345                         std::cerr << "\tItem " << (*it)->whatami() << std::endl;
346 #endif
347                 }
348         }
349 #endif
350
351         /* put all items at point that are event-sensitive and visible and NOT
352            groups into within_items. Note that items is sorted from bottom to
353            top, but we're going to reverse that for within_items so that its
354            first item is the upper-most item that can be chosen as _current_item.
355         */
356         
357         vector<Item const *>::const_iterator i;
358         list<Item const *> within_items;
359
360         for (i = items.begin(); i != items.end(); ++i) {
361
362                 Item const * new_item = *i;
363
364                 /* We ignore invisible items, groups and items that ignore events */
365
366                 if (!new_item->visible() || new_item->ignore_events() || dynamic_cast<Group const *>(new_item) != 0) {
367                         continue;
368                 }
369                 
370                 within_items.push_front (new_item);
371         }
372
373         if (within_items.empty()) {
374
375                 /* no items at point, just send leave event below */
376                 _new_current_item = 0;
377
378         } else {
379
380                 if (within_items.front() == _current_item) {
381                         /* uppermost item at point is already _current_item */
382                         return;
383                 }
384         
385                 _new_current_item = const_cast<Item*> (within_items.front());
386         }
387
388         if (_new_current_item != _current_item) {
389                 deliver_enter_leave (point, state);
390         }
391 }
392
393 void
394 GtkCanvas::deliver_enter_leave (Duple const & point, int state)
395 {
396         /* setup enter & leave event structures */
397
398         GdkEventCrossing enter_event;
399         enter_event.type = GDK_ENTER_NOTIFY;
400         enter_event.window = get_window()->gobj();
401         enter_event.send_event = 0;
402         enter_event.subwindow = 0;
403         enter_event.mode = GDK_CROSSING_NORMAL;
404         enter_event.focus = FALSE;
405         enter_event.state = state;
406         enter_event.x = point.x;
407         enter_event.y = point.y;
408
409         GdkEventCrossing leave_event = enter_event;
410         leave_event.type = GDK_LEAVE_NOTIFY;
411
412         Item* i;
413         GdkNotifyType enter_detail;
414         GdkNotifyType leave_detail;
415         vector<Item*> items_to_leave_virtual;
416         vector<Item*> items_to_enter_virtual;
417
418         if (_new_current_item == 0) {
419
420                 leave_detail = GDK_NOTIFY_UNKNOWN;
421
422                 if (_current_item) {
423
424                         /* no current item, so also send virtual leave events to the
425                          * entire heirarchy for the current item
426                          */
427
428                         for (i = _current_item->parent(); i ; i = i->parent()) {
429                                 items_to_leave_virtual.push_back (i);
430                         }
431                 }
432
433         } else if (_current_item == 0) {
434
435                 enter_detail = GDK_NOTIFY_UNKNOWN;
436
437                 /* no current item, so also send virtual enter events to the
438                  * entire heirarchy for the new item 
439                  */
440
441                 for (i = _new_current_item->parent(); i ; i = i->parent()) {
442                         items_to_enter_virtual.push_back (i);
443                 }
444
445         } else if (_current_item->is_descendant_of (*_new_current_item)) {
446
447                 /* move from descendant to ancestor (X: "_current_item is an
448                  * inferior ("child") of _new_current_item") 
449                  *
450                  * Deliver "virtual" leave notifications to all items in the
451                  * heirarchy between current and new_current.
452                  */
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 where = window_to_canvas (Duple (ev->x, ev->y));
664
665         copy.button.x = where.x;
666         copy.button.y = where.y;
667         
668         /* Coordinates in the event will be canvas coordinates, correctly adjusted
669            for scroll if this GtkCanvas is in a GtkCanvasViewport.
670         */
671
672         pick_current_item (where, ev->state);
673         DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas button press @ %1, %2 => %3\n", ev->x, ev->y, where));
674         return deliver_event (reinterpret_cast<GdkEvent*>(&copy));
675 }
676
677 /** Handler for GDK button release events.
678  *  @param ev Event.
679  *  @return true if the event was handled.
680  */
681 bool
682 GtkCanvas::on_button_release_event (GdkEventButton* ev)
683 {       
684         /* translate event coordinates from window to canvas */
685
686         GdkEvent copy = *((GdkEvent*)ev);
687         Duple where = window_to_canvas (Duple (ev->x, ev->y));
688         
689         pick_current_item (where, ev->state);
690
691         copy.button.x = where.x;
692         copy.button.y = where.y;
693
694         /* Coordinates in the event will be canvas coordinates, correctly adjusted
695            for scroll if this GtkCanvas is in a GtkCanvasViewport.
696         */
697
698         pick_current_item (where, ev->state);
699         DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas button release @ %1, %2 => %3\n", ev->x, ev->y, where));
700         return deliver_event (reinterpret_cast<GdkEvent*>(&copy));
701 }
702
703 /** Handler for GDK motion events.
704  *  @param ev Event.
705  *  @return true if the event was handled.
706  */
707 bool
708 GtkCanvas::on_motion_notify_event (GdkEventMotion* ev)
709 {
710         /* translate event coordinates from window to canvas */
711
712         GdkEvent copy = *((GdkEvent*)ev);
713         Duple point (ev->x, ev->y);
714         Duple where = window_to_canvas (point);
715
716         copy.motion.x = where.x;
717         copy.motion.y = where.y;
718
719         /* Coordinates in "copy" will be canvas coordinates, 
720         */
721
722         // DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas motion @ %1, %2\n", ev->x, ev->y));
723
724         pick_current_item (where, ev->state);
725
726         /* Now deliver the motion event.  It may seem a little inefficient
727            to recompute the items under the event, but the enter notify/leave
728            events may have deleted canvas items so it is important to
729            recompute the list in deliver_event.
730         */
731
732         return deliver_event (reinterpret_cast<GdkEvent*> (&copy));
733 }
734
735 bool
736 GtkCanvas::on_enter_notify_event (GdkEventCrossing* ev)
737 {
738         Duple where = window_to_canvas (Duple (ev->x, ev->y));
739         pick_current_item (where, ev->state);
740         return true;
741 }
742
743 bool
744 GtkCanvas::on_leave_notify_event (GdkEventCrossing* ev)
745 {
746         _new_current_item = 0;
747         Duple where = window_to_canvas (Duple (ev->x, ev->y));
748         deliver_enter_leave (where, ev->state);
749         return true;
750 }
751
752 /** Called to request a redraw of our canvas.
753  *  @param area Area to redraw, in canvas coordinates.
754  */
755 void
756 GtkCanvas::request_redraw (Rect const & request)
757 {
758         boost::optional<Rect> req = request.intersection (visible_area());
759
760         if (req) {
761                 Rect r = req.get();
762                 Rect area = canvas_to_window (r);
763                 queue_draw_area (area.x0, area.y0, area.width(), area.height());
764         }
765 }
766
767 /** Called to request that we try to get a particular size for ourselves.
768  *  @param size Size to request, in pixels.
769  */
770 void
771 GtkCanvas::request_size (Duple size)
772 {
773         Duple req = size;
774
775         if (req.x > INT_MAX) {
776                 req.x = INT_MAX;
777         }
778
779         if (req.y > INT_MAX) {
780                 req.y = INT_MAX;
781         }
782
783         set_size_request (req.x, req.y);
784 }
785
786 /** `Grab' an item, so that all events are sent to that item until it is `ungrabbed'.
787  *  This is typically used for dragging items around, so that they are grabbed during
788  *  the drag.
789  *  @param item Item to grab.
790  */
791 void
792 GtkCanvas::grab (Item* item)
793 {
794         /* XXX: should this be doing gdk_pointer_grab? */
795         _grabbed_item = item;
796 }
797
798
799 /** `Ungrab' any item that was previously grabbed */
800 void
801 GtkCanvas::ungrab ()
802 {
803         /* XXX: should this be doing gdk_pointer_ungrab? */
804         _grabbed_item = 0;
805 }
806
807 /** Set keyboard focus on an item, so that all keyboard events are sent to that item until the focus
808  *  moves elsewhere.
809  *  @param item Item to grab.
810  */
811 void
812 GtkCanvas::focus (Item* item)
813 {
814         _focused_item = item;
815 }
816
817 void
818 GtkCanvas::unfocus (Item* item)
819 {
820         if (item == _focused_item) {
821                 _focused_item = 0;
822         }
823 }
824
825 /** @return The visible area of the canvas, in canvas coordinates */
826 Rect
827 GtkCanvas::visible_area () const
828 {
829         Distance const xo = _scroll_offset.x;
830         Distance const yo = _scroll_offset.y;
831         return Rect (xo, yo, xo + get_allocation().get_width (), yo + get_allocation().get_height ());
832 }
833
834 /** Create a GtkCanvaSViewport.
835  *  @param hadj Adjustment to use for horizontal scrolling.
836  *  @param vadj Adjustment to use for vertica scrolling.
837  */
838 GtkCanvasViewport::GtkCanvasViewport (Gtk::Adjustment& hadj, Gtk::Adjustment& vadj)
839         : Alignment (0, 0, 1.0, 1.0)
840         , hadjustment (hadj)
841         , vadjustment (vadj)
842 {
843         add (_canvas);
844
845         hadj.signal_value_changed().connect (sigc::mem_fun (*this, &GtkCanvasViewport::scrolled));
846         vadj.signal_value_changed().connect (sigc::mem_fun (*this, &GtkCanvasViewport::scrolled));
847 }
848
849 void
850 GtkCanvasViewport::scrolled ()
851 {
852         _canvas.scroll_to (hadjustment.get_value(), vadjustment.get_value());
853         queue_draw ();
854 }
855
856 /** Handler for when GTK asks us what minimum size we want.
857  *  @param req Requsition to fill in.
858  */
859 void
860 GtkCanvasViewport::on_size_request (Gtk::Requisition* req)
861 {
862         /* force the canvas to size itself */
863         // _canvas.root()->bounding_box(); 
864
865         req->width = 16;
866         req->height = 16;
867 }
868