fix problems with expose/redraws from canvas where requested area goes way outside...
[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         /* Find the scroll group that covers d (a window coordinate). Scroll groups are only allowed
211          * as children of the root group, so we just scan its first level
212          * children and see what we can find.
213          */
214
215         std::list<Item*> const& root_children (_root.items());
216         ScrollGroup* sg = 0;
217
218         for (std::list<Item*>::const_iterator i = root_children.begin(); i != root_children.end(); ++i) {
219                 if (((sg = dynamic_cast<ScrollGroup*>(*i)) != 0) && sg->covers_window (d)) {
220                         break;
221                 }
222         }
223
224         if (sg) {
225                 return d.translate (sg->scroll_offset());
226         }
227
228         /* fallback to global canvas offset ... it would be nice to remove this */
229
230         return d.translate (_scroll_offset);
231 }
232
233 Duple
234 Canvas::canvas_to_window (Duple const & d, bool rounded) const
235 {
236         /* Find the scroll group that covers d (a canvas coordinate). Scroll groups are only allowed
237          * as children of the root group, so we just scan its first level
238          * children and see what we can find.
239          */
240
241         std::list<Item*> const& root_children (_root.items());
242         ScrollGroup* sg = 0;
243         Duple wd;
244
245         for (std::list<Item*>::const_iterator i = root_children.begin(); i != root_children.end(); ++i) {
246                 if (((sg = dynamic_cast<ScrollGroup*>(*i)) != 0) && sg->covers_canvas (d)) {
247                         break;
248                 }
249         }
250         
251
252         if (sg) {
253                 wd = d.translate (-sg->scroll_offset());
254         } else {
255                 wd = d.translate (-_scroll_offset);
256         }
257
258         /* Note that this intentionally almost always returns integer coordinates */
259
260         if (rounded) {
261                 wd.x = round (wd.x);
262                 wd.y = round (wd.y);
263         }
264
265         return wd;
266 }
267
268 /** Called when an item has moved.
269  *  @param item Item that has moved.
270  *  @param pre_change_parent_bounding_box The bounding box of the item before
271  *  the move, in its parent's coordinates.
272  */
273 void
274 Canvas::item_moved (Item* item, boost::optional<Rect> pre_change_parent_bounding_box)
275 {
276         if (pre_change_parent_bounding_box) {
277                 /* request a redraw of where the item used to be. The box has
278                  * to be in parent coordinate space since the bounding box of
279                  * an item does not change when moved. If we use
280                  * item->item_to_canvas() on the old bounding box, we will be
281
282                  * using the item's new position, and so will compute the wrong
283                  * invalidation area. If we use the parent (which has not
284                  * moved, then this will work.
285                  */
286                 queue_draw_item_area (item->parent(), pre_change_parent_bounding_box.get ());
287         }
288
289         boost::optional<Rect> post_change_bounding_box = item->bounding_box ();
290         if (post_change_bounding_box) {
291                 /* request a redraw of where the item now is */
292                 queue_draw_item_area (item, post_change_bounding_box.get ());
293         }
294 }
295
296 /** Request a redraw of a particular area in an item's coordinates.
297  *  @param item Item.
298  *  @param area Area to redraw in the item's coordinates.
299  */
300 void
301 Canvas::queue_draw_item_area (Item* item, Rect area)
302 {
303         request_redraw (item->item_to_window (area));
304 }
305
306 /** Construct a GtkCanvas */
307 GtkCanvas::GtkCanvas ()
308         : _current_item (0)
309         , _new_current_item (0)
310         , _grabbed_item (0)
311         , _focused_item (0)
312 {
313         /* these are the events we want to know about */
314         add_events (Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::POINTER_MOTION_MASK |
315                     Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK);
316 }
317
318 void
319 GtkCanvas::pick_current_item (int state)
320 {
321         int x;
322         int y;
323
324         /* this version of ::pick_current_item() is called after an item is
325          * added or removed, so we have no coordinates to work from as is the
326          * case with a motion event. Find out where the mouse is and use that.
327          */
328              
329         Glib::RefPtr<const Gdk::Window> pointer_window = Gdk::Display::get_default()->get_window_at_pointer (x, y);
330
331         if (pointer_window != get_window()) {
332                 return;
333         }
334
335         pick_current_item (Duple (x, y), state);
336 }
337                 
338 void
339 GtkCanvas::pick_current_item (Duple const & point, int state)
340 {
341         /* we do not enter/leave items during a drag/grab */
342
343         if (_grabbed_item) {
344                 return;
345         }
346
347         /* find the items at the given position */
348
349         vector<Item const *> items;
350         _root.add_items_at_point (point, items);
351
352         DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("%1 covers %2 items\n", point, items.size()));
353
354 #ifndef NDEBUG
355         if (DEBUG_ENABLED(PBD::DEBUG::CanvasEnterLeave)) {
356                 for (vector<Item const*>::const_iterator it = items.begin(); it != items.end(); ++it) {
357 #ifdef CANVAS_DEBUG
358                         std::cerr << "\tItem " << (*it)->whatami() << '/' << (*it)->name << std::endl;
359 #else
360                         std::cerr << "\tItem " << (*it)->whatami() << std::endl;
361 #endif
362                 }
363         }
364 #endif
365
366         /* put all items at point that are event-sensitive and visible and NOT
367            groups into within_items. Note that items is sorted from bottom to
368            top, but we're going to reverse that for within_items so that its
369            first item is the upper-most item that can be chosen as _current_item.
370         */
371         
372         vector<Item const *>::const_iterator i;
373         list<Item const *> within_items;
374
375         for (i = items.begin(); i != items.end(); ++i) {
376
377                 Item const * new_item = *i;
378
379                 /* We ignore invisible items, groups and items that ignore events */
380
381                 if (!new_item->visible() || new_item->ignore_events() || dynamic_cast<Group const *>(new_item) != 0) {
382                         continue;
383                 }
384                 
385                 within_items.push_front (new_item);
386         }
387
388         if (within_items.empty()) {
389
390                 /* no items at point, just send leave event below */
391                 _new_current_item = 0;
392
393         } else {
394
395                 if (within_items.front() == _current_item) {
396                         /* uppermost item at point is already _current_item */
397                         return;
398                 }
399         
400                 _new_current_item = const_cast<Item*> (within_items.front());
401         }
402
403         if (_new_current_item != _current_item) {
404                 deliver_enter_leave (point, state);
405         }
406 }
407
408 /** Deliver a series of enter & leave events based on the pointer position being at window
409  * coordinate @param point, and pointer @param state (modifier keys, etc)
410  */
411 void
412 GtkCanvas::deliver_enter_leave (Duple const & point, int state)
413 {
414         /* setup enter & leave event structures */
415
416         GdkEventCrossing enter_event;
417         enter_event.type = GDK_ENTER_NOTIFY;
418         enter_event.window = get_window()->gobj();
419         enter_event.send_event = 0;
420         enter_event.subwindow = 0;
421         enter_event.mode = GDK_CROSSING_NORMAL;
422         enter_event.focus = FALSE;
423         enter_event.state = state;
424         enter_event.x = point.x;
425         enter_event.y = point.y;
426
427         GdkEventCrossing leave_event = enter_event;
428         leave_event.type = GDK_LEAVE_NOTIFY;
429
430         Item* i;
431         GdkNotifyType enter_detail;
432         GdkNotifyType leave_detail;
433         vector<Item*> items_to_leave_virtual;
434         vector<Item*> items_to_enter_virtual;
435
436         if (_new_current_item == 0) {
437
438                 leave_detail = GDK_NOTIFY_UNKNOWN;
439
440                 if (_current_item) {
441
442                         /* no current item, so also send virtual leave events to the
443                          * entire heirarchy for the current item
444                          */
445
446                         for (i = _current_item->parent(); i ; i = i->parent()) {
447                                 items_to_leave_virtual.push_back (i);
448                         }
449                 }
450
451         } else if (_current_item == 0) {
452
453                 enter_detail = GDK_NOTIFY_UNKNOWN;
454
455                 /* no current item, so also send virtual enter events to the
456                  * entire heirarchy for the new item 
457                  */
458
459                 for (i = _new_current_item->parent(); i ; i = i->parent()) {
460                         items_to_enter_virtual.push_back (i);
461                 }
462
463         } else if (_current_item->is_descendant_of (*_new_current_item)) {
464
465                 /* move from descendant to ancestor (X: "_current_item is an
466                  * inferior ("child") of _new_current_item") 
467                  *
468                  * Deliver "virtual" leave notifications to all items in the
469                  * heirarchy between current and new_current.
470                  */
471                 
472                 for (i = _current_item->parent(); i && i != _new_current_item; i = i->parent()) {
473                         items_to_leave_virtual.push_back (i);
474                 }
475
476                 enter_detail = GDK_NOTIFY_INFERIOR;
477                 leave_detail = GDK_NOTIFY_ANCESTOR;
478
479
480         } else if (_new_current_item->is_descendant_of (*_current_item)) {
481                 /* move from ancestor to descendant (X: "_new_current_item is
482                  * an inferior ("child") of _current_item")
483                  *
484                  * Deliver "virtual" enter notifications to all items in the
485                  * heirarchy between current and new_current.
486                  */
487
488                 for (i = _new_current_item->parent(); i && i != _current_item; i = i->parent()) {
489                         items_to_enter_virtual.push_back (i);
490                 }
491
492                 enter_detail = GDK_NOTIFY_ANCESTOR;
493                 leave_detail = GDK_NOTIFY_INFERIOR;
494
495         } else {
496
497                 Item const * common_ancestor = _current_item->closest_ancestor_with (*_new_current_item);
498
499                 /* deliver virtual leave events to everything between _current
500                  * and common_ancestor.
501                  */
502
503                 for (i = _current_item->parent(); i && i != common_ancestor; i = i->parent()) {
504                         items_to_leave_virtual.push_back (i);
505                 }
506
507                 /* deliver virtual enter events to everything between
508                  * _new_current and common_ancestor.
509                  */
510
511                 for (i = _new_current_item->parent(); i && i != common_ancestor; i = i->parent()) {
512                         items_to_enter_virtual.push_back (i);
513                 }
514
515                 enter_detail = GDK_NOTIFY_NONLINEAR;
516                 leave_detail = GDK_NOTIFY_NONLINEAR;
517         }
518         
519
520         if (_current_item && !_current_item->ignore_events ()) {
521                 leave_event.detail = leave_detail;
522                 _current_item->Event ((GdkEvent*)&leave_event);
523                 DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("LEAVE %1/%2\n", _current_item->whatami(), _current_item->name));
524         }
525
526         leave_event.detail = GDK_NOTIFY_VIRTUAL;
527         enter_event.detail = GDK_NOTIFY_VIRTUAL;
528
529         for (vector<Item*>::iterator it = items_to_leave_virtual.begin(); it != items_to_leave_virtual.end(); ++it) {
530                 if (!(*it)->ignore_events()) {
531                         DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("leave %1/%2\n", (*it)->whatami(), (*it)->name));
532                         (*it)->Event ((GdkEvent*)&leave_event);
533                 }
534         }
535
536         for (vector<Item*>::iterator it = items_to_enter_virtual.begin(); it != items_to_enter_virtual.end(); ++it) {
537                 if (!(*it)->ignore_events()) {
538                         DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("enter %1/%2\n", (*it)->whatami(), (*it)->name));
539                         (*it)->Event ((GdkEvent*)&enter_event);
540                         // std::cerr << "enter " << (*it)->whatami() << '/' << (*it)->name << std::endl;
541                 }
542         }
543
544         if (_new_current_item && !_new_current_item->ignore_events()) {
545                 enter_event.detail = enter_detail;
546                 DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("ENTER %1/%2\n", _new_current_item->whatami(), _new_current_item->name));
547                 _new_current_item->Event ((GdkEvent*)&enter_event);
548         }
549
550         _current_item = _new_current_item;
551 }
552
553
554 /** Deliver an event to the appropriate item; either the grabbed item, or
555  *  one of the items underneath the event.
556  *  @param point Position that the event has occurred at, in canvas coordinates.
557  *  @param event The event.
558  */
559 bool
560 GtkCanvas::deliver_event (GdkEvent* event)
561 {
562         /* Point in in canvas coordinate space */
563
564         const Item* event_item;
565
566         if (_grabbed_item) {
567                 /* we have a grabbed item, so everything gets sent there */
568                 DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("%1 %2 (%3) was grabbed, send event there\n",
569                                                                        _grabbed_item, _grabbed_item->whatami(), _grabbed_item->name));
570                 event_item = _grabbed_item;
571         } else {
572                 event_item = _current_item;
573         }
574
575         if (!event_item) {
576                 return false;
577         }
578
579         /* run through the items from child to parent, until one claims the event */
580
581         Item* item = const_cast<Item*> (event_item);
582         
583         while (item) {
584
585                 Item* parent = item->parent ();
586
587                 if (!item->ignore_events () && 
588                     item->Event (event)) {
589                         /* this item has just handled the event */
590                         DEBUG_TRACE (
591                                 PBD::DEBUG::CanvasEvents,
592                                 string_compose ("canvas event handled by %1 %2\n", item->whatami(), item->name.empty() ? "[unknown]" : item->name)
593                                 );
594                         
595                         return true;
596                 }
597                 
598                 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)));
599
600                 if ((item = parent) == 0) {
601                         break;
602                 }
603
604         }
605
606         return false;
607 }
608
609 /** Called when an item is being destroyed.
610  *  @param item Item being destroyed.
611  *  @param bounding_box Last known bounding box of the item.
612  */
613 void
614 GtkCanvas::item_going_away (Item* item, boost::optional<Rect> bounding_box)
615 {
616         if (bounding_box) {
617                 queue_draw_item_area (item, bounding_box.get ());
618         }
619         
620         if (_new_current_item == item) {
621                 _new_current_item = 0;
622         }
623
624         if (_grabbed_item == item) {
625                 _grabbed_item = 0;
626         }
627
628         if (_focused_item == item) {
629                 _focused_item = 0;
630         }
631
632         ScrollGroup* sg = dynamic_cast<ScrollGroup*>(item);
633         if (sg) {
634                 scrollers.remove (sg);
635         }
636
637         if (_current_item == item) {
638                 /* no need to send a leave event to this item, since it is going away 
639                  */
640                 _current_item = 0;
641                 pick_current_item (0); // no mouse state
642         }
643         
644 }
645
646 /** Handler for GDK expose events.
647  *  @param ev Event.
648  *  @return true if the event was handled.
649  */
650 bool
651 GtkCanvas::on_expose_event (GdkEventExpose* ev)
652 {
653         Cairo::RefPtr<Cairo::Context> cairo_context = get_window()->create_cairo_context ();
654         render (Rect (ev->area.x, ev->area.y, ev->area.x + ev->area.width, ev->area.y + ev->area.height), cairo_context);
655         return true;
656 }
657
658 /** @return Our Cairo context, or 0 if we don't have one */
659 Cairo::RefPtr<Cairo::Context>
660 GtkCanvas::context ()
661 {
662         Glib::RefPtr<Gdk::Window> w = get_window ();
663         if (!w) {
664                 return Cairo::RefPtr<Cairo::Context> ();
665         }
666
667         return w->create_cairo_context ();
668 }
669
670 /** Handler for GDK button press events.
671  *  @param ev Event.
672  *  @return true if the event was handled.
673  */
674 bool
675 GtkCanvas::on_button_press_event (GdkEventButton* ev)
676 {
677         /* translate event coordinates from window to canvas */
678
679         GdkEvent copy = *((GdkEvent*)ev);
680         Duple winpos = Duple (ev->x, ev->y);
681         Duple where = window_to_canvas (winpos);
682         
683         pick_current_item (winpos, ev->state);
684
685         copy.button.x = where.x;
686         copy.button.y = where.y;
687         
688         /* Coordinates in the event will be canvas coordinates, correctly adjusted
689            for scroll if this GtkCanvas is in a GtkCanvasViewport.
690         */
691
692         DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas button press @ %1, %2 => %3\n", ev->x, ev->y, where));
693         return deliver_event (reinterpret_cast<GdkEvent*>(&copy));
694 }
695
696 /** Handler for GDK button release events.
697  *  @param ev Event.
698  *  @return true if the event was handled.
699  */
700 bool
701 GtkCanvas::on_button_release_event (GdkEventButton* ev)
702 {       
703         /* translate event coordinates from window to canvas */
704
705         GdkEvent copy = *((GdkEvent*)ev);
706         Duple winpos = Duple (ev->x, ev->y);
707         Duple where = window_to_canvas (winpos);
708         
709         pick_current_item (winpos, ev->state);
710
711         copy.button.x = where.x;
712         copy.button.y = where.y;
713
714         /* Coordinates in the event will be canvas coordinates, correctly adjusted
715            for scroll if this GtkCanvas is in a GtkCanvasViewport.
716         */
717
718         DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas button release @ %1, %2 => %3\n", ev->x, ev->y, where));
719         return deliver_event (reinterpret_cast<GdkEvent*>(&copy));
720 }
721
722 /** Handler for GDK motion events.
723  *  @param ev Event.
724  *  @return true if the event was handled.
725  */
726 bool
727 GtkCanvas::on_motion_notify_event (GdkEventMotion* ev)
728 {
729         /* translate event coordinates from window to canvas */
730
731         GdkEvent copy = *((GdkEvent*)ev);
732         Duple point (ev->x, ev->y);
733         Duple where = window_to_canvas (point);
734
735         copy.motion.x = where.x;
736         copy.motion.y = where.y;
737
738         /* Coordinates in "copy" will be canvas coordinates, 
739         */
740
741         // DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas motion @ %1, %2\n", ev->x, ev->y));
742
743         pick_current_item (point, ev->state);
744
745         /* Now deliver the motion event.  It may seem a little inefficient
746            to recompute the items under the event, but the enter notify/leave
747            events may have deleted canvas items so it is important to
748            recompute the list in deliver_event.
749         */
750
751         return deliver_event (reinterpret_cast<GdkEvent*> (&copy));
752 }
753
754 bool
755 GtkCanvas::on_enter_notify_event (GdkEventCrossing* ev)
756 {
757         pick_current_item (Duple (ev->x, ev->y), ev->state);
758         return true;
759 }
760
761 bool
762 GtkCanvas::on_leave_notify_event (GdkEventCrossing* ev)
763 {
764         _new_current_item = 0;
765         deliver_enter_leave (Duple (ev->x, ev->y), ev->state);
766         return true;
767 }
768
769 /** Called to request a redraw of our canvas.
770  *  @param area Area to redraw, in window coordinates.
771  */
772 void
773 GtkCanvas::request_redraw (Rect const & request)
774 {
775         Rect real_area;
776
777         Coord const w = width ();
778         Coord const h = height ();
779
780         /* clamp area requested to actual visible window */
781
782         real_area.x0 = max (0.0, min (w, request.x0));
783         real_area.x1 = max (0.0, min (w, request.x1));
784         real_area.y0 = max (0.0, min (h, request.y0));
785         real_area.y1 = max (0.0, min (h, request.y1));
786
787         queue_draw_area (real_area.x0, real_area.y0, real_area.width(), real_area.height());
788 }
789
790 /** Called to request that we try to get a particular size for ourselves.
791  *  @param size Size to request, in pixels.
792  */
793 void
794 GtkCanvas::request_size (Duple size)
795 {
796         Duple req = size;
797
798         if (req.x > INT_MAX) {
799                 req.x = INT_MAX;
800         }
801
802         if (req.y > INT_MAX) {
803                 req.y = INT_MAX;
804         }
805
806         set_size_request (req.x, req.y);
807 }
808
809 /** `Grab' an item, so that all events are sent to that item until it is `ungrabbed'.
810  *  This is typically used for dragging items around, so that they are grabbed during
811  *  the drag.
812  *  @param item Item to grab.
813  */
814 void
815 GtkCanvas::grab (Item* item)
816 {
817         /* XXX: should this be doing gdk_pointer_grab? */
818         _grabbed_item = item;
819 }
820
821
822 /** `Ungrab' any item that was previously grabbed */
823 void
824 GtkCanvas::ungrab ()
825 {
826         /* XXX: should this be doing gdk_pointer_ungrab? */
827         _grabbed_item = 0;
828 }
829
830 /** Set keyboard focus on an item, so that all keyboard events are sent to that item until the focus
831  *  moves elsewhere.
832  *  @param item Item to grab.
833  */
834 void
835 GtkCanvas::focus (Item* item)
836 {
837         _focused_item = item;
838 }
839
840 void
841 GtkCanvas::unfocus (Item* item)
842 {
843         if (item == _focused_item) {
844                 _focused_item = 0;
845         }
846 }
847
848 /** @return The visible area of the canvas, in canvas coordinates */
849 Rect
850 GtkCanvas::visible_area () const
851 {
852         return Rect (0, 0, get_allocation().get_width (), get_allocation().get_height ());
853 }
854
855 Coord
856 GtkCanvas::width() const
857 {
858         return get_allocation().get_width();
859 }
860
861 Coord
862 GtkCanvas::height() const
863 {
864         return get_allocation().get_height();
865 }
866
867 /** Create a GtkCanvaSViewport.
868  *  @param hadj Adjustment to use for horizontal scrolling.
869  *  @param vadj Adjustment to use for vertica scrolling.
870  */
871 GtkCanvasViewport::GtkCanvasViewport (Gtk::Adjustment& hadj, Gtk::Adjustment& vadj)
872         : Alignment (0, 0, 1.0, 1.0)
873         , hadjustment (hadj)
874         , vadjustment (vadj)
875 {
876         add (_canvas);
877
878         hadj.signal_value_changed().connect (sigc::mem_fun (*this, &GtkCanvasViewport::scrolled));
879         vadj.signal_value_changed().connect (sigc::mem_fun (*this, &GtkCanvasViewport::scrolled));
880 }
881
882 void
883 GtkCanvasViewport::scrolled ()
884 {
885         _canvas.scroll_to (hadjustment.get_value(), vadjustment.get_value());
886         queue_draw ();
887 }
888
889 /** Handler for when GTK asks us what minimum size we want.
890  *  @param req Requsition to fill in.
891  */
892 void
893 GtkCanvasViewport::on_size_request (Gtk::Requisition* req)
894 {
895         /* force the canvas to size itself */
896         // _canvas.root()->bounding_box(); 
897
898         req->width = 16;
899         req->height = 16;
900 }
901