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