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