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