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