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