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