NOOP, re-indent using tabs
[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         /* We do things this way because we do not want to recurse through
52            the canvas for every scroll. In the presence of large MIDI
53            tracks this means traversing item lists that include
54            thousands of items (notes).
55
56            This design limits us to moving only those items (groups, typically)
57            that should move in certain ways as we scroll. In other terms, it
58            becomes O(1) rather than O(N).
59         */
60
61         for (list<ScrollGroup*>::iterator i = scrollers.begin(); i != scrollers.end(); ++i) {
62                 (*i)->scroll_to (Duple (x, y));
63         }
64
65         pick_current_item (0); // no current mouse position 
66 }
67
68 void
69 Canvas::add_scroller (ScrollGroup& i)
70 {
71         scrollers.push_back (&i);
72 }
73
74 void
75 Canvas::zoomed ()
76 {
77         pick_current_item (0); // no current mouse position
78 }
79
80 /** Render an area of the canvas.
81  *  @param area Area in window coordinates.
82  *  @param context Cairo context to render to.
83  */
84 void
85 Canvas::render (Rect const & area, Cairo::RefPtr<Cairo::Context> const & context) const
86 {
87 #ifdef CANVAS_DEBUG
88         if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
89                 cerr << this << " RENDER: " << area << endl;
90                 //cerr << "CANVAS @ " << this << endl;
91                 //dump (cerr);
92                 //cerr << "-------------------------\n";
93         }
94 #endif
95
96         render_count = 0;
97         
98         boost::optional<Rect> root_bbox = _root.bounding_box();
99         if (!root_bbox) {
100                 /* the root has no bounding box, so there's nothing to render */
101                 return;
102         }
103
104         boost::optional<Rect> draw = root_bbox->intersection (area);
105         if (draw) {
106                 
107                 /* there's a common area between the root and the requested
108                    area, so render it.
109                 */
110
111                 _root.render (*draw, context);
112
113                 // This outlines the rect being rendered, after it has been drawn.
114                 // context->rectangle (draw->x0, draw->y0, draw->x1 - draw->x0, draw->y1 - draw->y0);
115                 // context->set_source_rgba (1.0, 0, 0, 1.0);
116                 // context->stroke ();
117
118         }
119
120 }
121
122 ostream&
123 operator<< (ostream& o, Canvas& c)
124 {
125         c.dump (o);
126         return o;
127 }
128
129 std::string
130 Canvas::indent() const
131
132         string s;
133
134         for (int n = 0; n < ArdourCanvas::dump_depth; ++n) {
135                 s += '\t';
136         }
137
138         return s;
139 }
140
141 std::string
142 Canvas::render_indent() const
143
144         string s;
145
146         for (int n = 0; n < ArdourCanvas::render_depth; ++n) {
147                 s += ' ';
148         }
149
150         return s;
151 }
152
153 void
154 Canvas::dump (ostream& o) const
155 {
156         dump_depth = 0;
157         _root.dump (o);
158 }       
159
160 /** Called when an item has been shown or hidden.
161  *  @param item Item that has been shown or hidden.
162  */
163 void
164 Canvas::item_shown_or_hidden (Item* item)
165 {
166         boost::optional<Rect> bbox = item->bounding_box ();
167         if (bbox) {
168                 queue_draw_item_area (item, bbox.get ());
169         }
170 }
171
172 /** Called when an item has a change to its visual properties
173  *  that do NOT affect its bounding box.
174  *  @param item Item that has been modified.
175  */
176 void
177 Canvas::item_visual_property_changed (Item* item)
178 {
179         boost::optional<Rect> bbox = item->bounding_box ();
180         if (bbox) {
181                 queue_draw_item_area (item, bbox.get ());
182         }
183 }
184
185 /** Called when an item has changed, but not moved.
186  *  @param item Item that has changed.
187  *  @param pre_change_bounding_box The bounding box of item before the change,
188  *  in the item's coordinates.
189  */
190 void
191 Canvas::item_changed (Item* item, boost::optional<Rect> pre_change_bounding_box)
192 {
193         if (pre_change_bounding_box) {
194                 /* request a redraw of the item's old bounding box */
195                 queue_draw_item_area (item, pre_change_bounding_box.get ());
196         }
197
198         boost::optional<Rect> post_change_bounding_box = item->bounding_box ();
199         if (post_change_bounding_box) {
200                 /* request a redraw of the item's new bounding box */
201                 queue_draw_item_area (item, post_change_bounding_box.get ());
202         }
203 }
204
205 Duple
206 Canvas::window_to_canvas (Duple const & d) const
207 {
208         /* Find the scroll group that covers d (a window coordinate). Scroll groups are only allowed
209          * as children of the root group, so we just scan its first level
210          * children and see what we can find.
211          */
212
213         std::list<Item*> const& root_children (_root.items());
214         ScrollGroup* sg = 0;
215
216         for (std::list<Item*>::const_iterator i = root_children.begin(); i != root_children.end(); ++i) {
217                 if (((sg = dynamic_cast<ScrollGroup*>(*i)) != 0) && sg->covers_window (d)) {
218                         break;
219                 }
220         }
221
222         if (sg) {
223                 return d.translate (sg->scroll_offset());
224         }
225
226         return d;
227 }
228
229 Duple
230 Canvas::canvas_to_window (Duple const & d, bool rounded) const
231 {
232         /* Find the scroll group that covers d (a canvas coordinate). Scroll groups are only allowed
233          * as children of the root group, so we just scan its first level
234          * children and see what we can find.
235          */
236
237         std::list<Item*> const& root_children (_root.items());
238         ScrollGroup* sg = 0;
239         Duple wd;
240
241         for (std::list<Item*>::const_iterator i = root_children.begin(); i != root_children.end(); ++i) {
242                 if (((sg = dynamic_cast<ScrollGroup*>(*i)) != 0) && sg->covers_canvas (d)) {
243                         break;
244                 }
245         }
246         
247
248         if (sg) {
249                 wd = d.translate (-sg->scroll_offset());
250         } else {
251                 wd = d;
252         }
253
254         /* Note that this intentionally almost always returns integer coordinates */
255
256         if (rounded) {
257                 wd.x = round (wd.x);
258                 wd.y = round (wd.y);
259         }
260
261         return wd;
262 }
263
264 /** Called when an item has moved.
265  *  @param item Item that has moved.
266  *  @param pre_change_parent_bounding_box The bounding box of the item before
267  *  the move, in its parent's coordinates.
268  */
269 void
270 Canvas::item_moved (Item* item, boost::optional<Rect> pre_change_parent_bounding_box)
271 {
272         if (pre_change_parent_bounding_box) {
273                 /* request a redraw of where the item used to be. The box has
274                  * to be in parent coordinate space since the bounding box of
275                  * an item does not change when moved. If we use
276                  * item->item_to_canvas() on the old bounding box, we will be
277
278                  * using the item's new position, and so will compute the wrong
279                  * invalidation area. If we use the parent (which has not
280                  * moved, then this will work.
281                  */
282                 queue_draw_item_area (item->parent(), pre_change_parent_bounding_box.get ());
283         }
284
285         boost::optional<Rect> post_change_bounding_box = item->bounding_box ();
286         if (post_change_bounding_box) {
287                 /* request a redraw of where the item now is */
288                 queue_draw_item_area (item, post_change_bounding_box.get ());
289         }
290 }
291
292 /** Request a redraw of a particular area in an item's coordinates.
293  *  @param item Item.
294  *  @param area Area to redraw in the item's coordinates.
295  */
296 void
297 Canvas::queue_draw_item_area (Item* item, Rect area)
298 {
299         request_redraw (item->item_to_window (area));
300 }
301
302 /** Construct a GtkCanvas */
303 GtkCanvas::GtkCanvas ()
304         : _current_item (0)
305         , _new_current_item (0)
306         , _grabbed_item (0)
307         , _focused_item (0)
308 {
309         /* these are the events we want to know about */
310         add_events (Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::POINTER_MOTION_MASK |
311                     Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK);
312 }
313
314 void
315 GtkCanvas::pick_current_item (int state)
316 {
317         int x;
318         int y;
319
320         /* this version of ::pick_current_item() is called after an item is
321          * added or removed, so we have no coordinates to work from as is the
322          * case with a motion event. Find out where the mouse is and use that.
323          */
324              
325         Glib::RefPtr<const Gdk::Window> pointer_window = Gdk::Display::get_default()->get_window_at_pointer (x, y);
326
327         if (pointer_window != get_window()) {
328                 return;
329         }
330
331         pick_current_item (Duple (x, y), state);
332 }
333
334 /** Given @param point (a position in window coordinates)
335  *  and mouse state @param state, check to see if _current_item
336  *  (which will be used to deliver events) should change.
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 window 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
425         /* Events delivered to canvas items are expected to be in canvas
426          * coordinates but @param point is in window coordinates.
427          */
428         
429         Duple c = window_to_canvas (point);
430         enter_event.x = c.x;
431         enter_event.y = c.y;
432
433         GdkEventCrossing leave_event = enter_event;
434         leave_event.type = GDK_LEAVE_NOTIFY;
435
436         Item* i;
437         GdkNotifyType enter_detail;
438         GdkNotifyType leave_detail;
439         vector<Item*> items_to_leave_virtual;
440         vector<Item*> items_to_enter_virtual;
441
442         if (_new_current_item == 0) {
443
444                 leave_detail = GDK_NOTIFY_UNKNOWN;
445
446                 if (_current_item) {
447
448                         /* no current item, so also send virtual leave events to the
449                          * entire heirarchy for the current item
450                          */
451
452                         for (i = _current_item->parent(); i ; i = i->parent()) {
453                                 items_to_leave_virtual.push_back (i);
454                         }
455                 }
456
457         } else if (_current_item == 0) {
458
459                 enter_detail = GDK_NOTIFY_UNKNOWN;
460
461                 /* no current item, so also send virtual enter events to the
462                  * entire heirarchy for the new item 
463                  */
464
465                 for (i = _new_current_item->parent(); i ; i = i->parent()) {
466                         items_to_enter_virtual.push_back (i);
467                 }
468
469         } else if (_current_item->is_descendant_of (*_new_current_item)) {
470
471                 /* move from descendant to ancestor (X: "_current_item is an
472                  * inferior ("child") of _new_current_item") 
473                  *
474                  * Deliver "virtual" leave notifications to all items in the
475                  * heirarchy between current and new_current.
476                  */
477                 
478                 for (i = _current_item->parent(); i && i != _new_current_item; i = i->parent()) {
479                         items_to_leave_virtual.push_back (i);
480                 }
481
482                 enter_detail = GDK_NOTIFY_INFERIOR;
483                 leave_detail = GDK_NOTIFY_ANCESTOR;
484
485
486         } else if (_new_current_item->is_descendant_of (*_current_item)) {
487                 /* move from ancestor to descendant (X: "_new_current_item is
488                  * an inferior ("child") of _current_item")
489                  *
490                  * Deliver "virtual" enter notifications to all items in the
491                  * heirarchy between current and new_current.
492                  */
493
494                 for (i = _new_current_item->parent(); i && i != _current_item; i = i->parent()) {
495                         items_to_enter_virtual.push_back (i);
496                 }
497
498                 enter_detail = GDK_NOTIFY_ANCESTOR;
499                 leave_detail = GDK_NOTIFY_INFERIOR;
500
501         } else {
502
503                 Item const * common_ancestor = _current_item->closest_ancestor_with (*_new_current_item);
504
505                 /* deliver virtual leave events to everything between _current
506                  * and common_ancestor.
507                  */
508
509                 for (i = _current_item->parent(); i && i != common_ancestor; i = i->parent()) {
510                         items_to_leave_virtual.push_back (i);
511                 }
512
513                 /* deliver virtual enter events to everything between
514                  * _new_current and common_ancestor.
515                  */
516
517                 for (i = _new_current_item->parent(); i && i != common_ancestor; i = i->parent()) {
518                         items_to_enter_virtual.push_back (i);
519                 }
520
521                 enter_detail = GDK_NOTIFY_NONLINEAR;
522                 leave_detail = GDK_NOTIFY_NONLINEAR;
523         }
524         
525
526         if (_current_item && !_current_item->ignore_events ()) {
527                 leave_event.detail = leave_detail;
528                 _current_item->Event ((GdkEvent*)&leave_event);
529                 DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("LEAVE %1/%2\n", _current_item->whatami(), _current_item->name));
530         }
531
532         leave_event.detail = GDK_NOTIFY_VIRTUAL;
533         enter_event.detail = GDK_NOTIFY_VIRTUAL;
534
535         for (vector<Item*>::iterator it = items_to_leave_virtual.begin(); it != items_to_leave_virtual.end(); ++it) {
536                 if (!(*it)->ignore_events()) {
537                         DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("leave %1/%2\n", (*it)->whatami(), (*it)->name));
538                         (*it)->Event ((GdkEvent*)&leave_event);
539                 }
540         }
541
542         for (vector<Item*>::iterator it = items_to_enter_virtual.begin(); it != items_to_enter_virtual.end(); ++it) {
543                 if (!(*it)->ignore_events()) {
544                         DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("enter %1/%2\n", (*it)->whatami(), (*it)->name));
545                         (*it)->Event ((GdkEvent*)&enter_event);
546                         // std::cerr << "enter " << (*it)->whatami() << '/' << (*it)->name << std::endl;
547                 }
548         }
549
550         if (_new_current_item && !_new_current_item->ignore_events()) {
551                 enter_event.detail = enter_detail;
552                 DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("ENTER %1/%2\n", _new_current_item->whatami(), _new_current_item->name));
553                 _new_current_item->Event ((GdkEvent*)&enter_event);
554         }
555
556         _current_item = _new_current_item;
557 }
558
559
560 /** Deliver an event to the appropriate item; either the grabbed item, or
561  *  one of the items underneath the event.
562  *  @param point Position that the event has occurred at, in canvas coordinates.
563  *  @param event The event.
564  */
565 bool
566 GtkCanvas::deliver_event (GdkEvent* event)
567 {
568         /* Point in in canvas coordinate space */
569
570         const Item* event_item;
571
572         if (_grabbed_item) {
573                 /* we have a grabbed item, so everything gets sent there */
574                 DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("%1 %2 (%3) was grabbed, send event there\n",
575                                                                        _grabbed_item, _grabbed_item->whatami(), _grabbed_item->name));
576                 event_item = _grabbed_item;
577         } else {
578                 event_item = _current_item;
579         }
580
581         if (!event_item) {
582                 return false;
583         }
584
585         /* run through the items from child to parent, until one claims the event */
586
587         Item* item = const_cast<Item*> (event_item);
588         
589         while (item) {
590
591                 Item* parent = item->parent ();
592
593                 if (!item->ignore_events () && 
594                     item->Event (event)) {
595                         /* this item has just handled the event */
596                         DEBUG_TRACE (
597                                 PBD::DEBUG::CanvasEvents,
598                                 string_compose ("canvas event handled by %1 %2\n", item->whatami(), item->name.empty() ? "[unknown]" : item->name)
599                                 );
600                         
601                         return true;
602                 }
603                 
604                 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)));
605
606                 if ((item = parent) == 0) {
607                         break;
608                 }
609
610         }
611
612         return false;
613 }
614
615 /** Called when an item is being destroyed.
616  *  @param item Item being destroyed.
617  *  @param bounding_box Last known bounding box of the item.
618  */
619 void
620 GtkCanvas::item_going_away (Item* item, boost::optional<Rect> bounding_box)
621 {
622         if (bounding_box) {
623                 queue_draw_item_area (item, bounding_box.get ());
624         }
625         
626         if (_new_current_item == item) {
627                 _new_current_item = 0;
628         }
629
630         if (_grabbed_item == item) {
631                 _grabbed_item = 0;
632         }
633
634         if (_focused_item == item) {
635                 _focused_item = 0;
636         }
637
638         ScrollGroup* sg = dynamic_cast<ScrollGroup*>(item);
639         if (sg) {
640                 scrollers.remove (sg);
641         }
642
643         if (_current_item == item) {
644                 /* no need to send a leave event to this item, since it is going away 
645                  */
646                 _current_item = 0;
647                 pick_current_item (0); // no mouse state
648         }
649         
650 }
651
652 /** Handler for GDK expose events.
653  *  @param ev Event.
654  *  @return true if the event was handled.
655  */
656 bool
657 GtkCanvas::on_expose_event (GdkEventExpose* ev)
658 {
659         Cairo::RefPtr<Cairo::Context> cairo_context = get_window()->create_cairo_context ();
660         render (Rect (ev->area.x, ev->area.y, ev->area.x + ev->area.width, ev->area.y + ev->area.height), cairo_context);
661         return true;
662 }
663
664 /** @return Our Cairo context, or 0 if we don't have one */
665 Cairo::RefPtr<Cairo::Context>
666 GtkCanvas::context ()
667 {
668         Glib::RefPtr<Gdk::Window> w = get_window ();
669         if (!w) {
670                 return Cairo::RefPtr<Cairo::Context> ();
671         }
672
673         return w->create_cairo_context ();
674 }
675
676 /** Handler for GDK button press events.
677  *  @param ev Event.
678  *  @return true if the event was handled.
679  */
680 bool
681 GtkCanvas::on_button_press_event (GdkEventButton* ev)
682 {
683         /* translate event coordinates from window to canvas */
684
685         GdkEvent copy = *((GdkEvent*)ev);
686         Duple winpos = Duple (ev->x, ev->y);
687         Duple where = window_to_canvas (winpos);
688         
689         pick_current_item (winpos, 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         DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas button press @ %1, %2 => %3\n", ev->x, ev->y, where));
699         return deliver_event (reinterpret_cast<GdkEvent*>(&copy));
700 }
701
702 /** Handler for GDK button release events.
703  *  @param ev Event.
704  *  @return true if the event was handled.
705  */
706 bool
707 GtkCanvas::on_button_release_event (GdkEventButton* ev)
708 {       
709         /* translate event coordinates from window to canvas */
710
711         GdkEvent copy = *((GdkEvent*)ev);
712         Duple winpos = Duple (ev->x, ev->y);
713         Duple where = window_to_canvas (winpos);
714         
715         pick_current_item (winpos, ev->state);
716
717         copy.button.x = where.x;
718         copy.button.y = where.y;
719
720         /* Coordinates in the event will be canvas coordinates, correctly adjusted
721            for scroll if this GtkCanvas is in a GtkCanvasViewport.
722         */
723
724         DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas button release @ %1, %2 => %3\n", ev->x, ev->y, where));
725         return deliver_event (reinterpret_cast<GdkEvent*>(&copy));
726 }
727
728 /** Handler for GDK motion events.
729  *  @param ev Event.
730  *  @return true if the event was handled.
731  */
732 bool
733 GtkCanvas::on_motion_notify_event (GdkEventMotion* ev)
734 {
735         /* translate event coordinates from window to canvas */
736
737         GdkEvent copy = *((GdkEvent*)ev);
738         Duple point (ev->x, ev->y);
739         Duple where = window_to_canvas (point);
740
741         copy.motion.x = where.x;
742         copy.motion.y = where.y;
743
744         /* Coordinates in "copy" will be canvas coordinates, 
745         */
746
747         DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas motion @ %1, %2 canvas @ %3, %4\n", ev->x, ev->y, copy.motion.x, copy.motion.y));
748
749         pick_current_item (point, ev->state);
750
751         /* Now deliver the motion event.  It may seem a little inefficient
752            to recompute the items under the event, but the enter notify/leave
753            events may have deleted canvas items so it is important to
754            recompute the list in deliver_event.
755         */
756
757         return deliver_event (reinterpret_cast<GdkEvent*> (&copy));
758 }
759
760 bool
761 GtkCanvas::on_enter_notify_event (GdkEventCrossing* ev)
762 {
763         pick_current_item (Duple (ev->x, ev->y), ev->state);
764         return true;
765 }
766
767 bool
768 GtkCanvas::on_leave_notify_event (GdkEventCrossing* ev)
769 {
770         _new_current_item = 0;
771         deliver_enter_leave (Duple (ev->x, ev->y), ev->state);
772         return true;
773 }
774
775 /** Called to request a redraw of our canvas.
776  *  @param area Area to redraw, in window coordinates.
777  */
778 void
779 GtkCanvas::request_redraw (Rect const & request)
780 {
781         Rect real_area;
782
783         Coord const w = width ();
784         Coord const h = height ();
785
786         /* clamp area requested to actual visible window */
787
788         real_area.x0 = max (0.0, min (w, request.x0));
789         real_area.x1 = max (0.0, min (w, request.x1));
790         real_area.y0 = max (0.0, min (h, request.y0));
791         real_area.y1 = max (0.0, min (h, request.y1));
792
793         queue_draw_area (real_area.x0, real_area.y0, real_area.width(), real_area.height());
794 }
795
796 /** Called to request that we try to get a particular size for ourselves.
797  *  @param size Size to request, in pixels.
798  */
799 void
800 GtkCanvas::request_size (Duple size)
801 {
802         Duple req = size;
803
804         if (req.x > INT_MAX) {
805                 req.x = INT_MAX;
806         }
807
808         if (req.y > INT_MAX) {
809                 req.y = INT_MAX;
810         }
811
812         set_size_request (req.x, req.y);
813 }
814
815 /** `Grab' an item, so that all events are sent to that item until it is `ungrabbed'.
816  *  This is typically used for dragging items around, so that they are grabbed during
817  *  the drag.
818  *  @param item Item to grab.
819  */
820 void
821 GtkCanvas::grab (Item* item)
822 {
823         /* XXX: should this be doing gdk_pointer_grab? */
824         _grabbed_item = item;
825 }
826
827
828 /** `Ungrab' any item that was previously grabbed */
829 void
830 GtkCanvas::ungrab ()
831 {
832         /* XXX: should this be doing gdk_pointer_ungrab? */
833         _grabbed_item = 0;
834 }
835
836 /** Set keyboard focus on an item, so that all keyboard events are sent to that item until the focus
837  *  moves elsewhere.
838  *  @param item Item to grab.
839  */
840 void
841 GtkCanvas::focus (Item* item)
842 {
843         _focused_item = item;
844 }
845
846 void
847 GtkCanvas::unfocus (Item* item)
848 {
849         if (item == _focused_item) {
850                 _focused_item = 0;
851         }
852 }
853
854 /** @return The visible area of the canvas, in canvas coordinates */
855 Rect
856 GtkCanvas::visible_area () const
857 {
858         return Rect (0, 0, get_allocation().get_width (), get_allocation().get_height ());
859 }
860
861 Coord
862 GtkCanvas::width() const
863 {
864         return get_allocation().get_width();
865 }
866
867 Coord
868 GtkCanvas::height() const
869 {
870         return get_allocation().get_height();
871 }
872
873 /** Create a GtkCanvaSViewport.
874  *  @param hadj Adjustment to use for horizontal scrolling.
875  *  @param vadj Adjustment to use for vertica scrolling.
876  */
877 GtkCanvasViewport::GtkCanvasViewport (Gtk::Adjustment& hadj, Gtk::Adjustment& vadj)
878         : Alignment (0, 0, 1.0, 1.0)
879         , hadjustment (hadj)
880         , vadjustment (vadj)
881 {
882         add (_canvas);
883
884         hadj.signal_value_changed().connect (sigc::mem_fun (*this, &GtkCanvasViewport::scrolled));
885         vadj.signal_value_changed().connect (sigc::mem_fun (*this, &GtkCanvasViewport::scrolled));
886 }
887
888 void
889 GtkCanvasViewport::scrolled ()
890 {
891         _canvas.scroll_to (hadjustment.get_value(), vadjustment.get_value());
892         queue_draw ();
893 }
894
895 /** Handler for when GTK asks us what minimum size we want.
896  *  @param req Requsition to fill in.
897  */
898 void
899 GtkCanvasViewport::on_size_request (Gtk::Requisition* req)
900 {
901         /* force the canvas to size itself */
902         // _canvas.root()->bounding_box(); 
903
904         req->width = 16;
905         req->height = 16;
906 }
907