ScrollGroups are kept in their own list, so ::window_to_canvas() does not need to...
[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 #include <gtkmm/window.h>
30
31 #include "pbd/compose.h"
32 #include "pbd/stacktrace.h"
33
34 #include "canvas/canvas.h"
35 #include "canvas/colors.h"
36 #include "canvas/debug.h"
37 #include "canvas/line.h"
38 #include "canvas/scroll_group.h"
39 #include "canvas/utils.h"
40
41 using namespace std;
42 using namespace ArdourCanvas;
43
44 uint32_t Canvas::tooltip_timeout_msecs = 750;
45
46 /** Construct a new Canvas */
47 Canvas::Canvas ()
48         : _root (this)
49         , _bg_color (rgba_to_color (0, 1.0, 0.0, 1.0))
50 {
51         set_epoch ();
52 }
53
54 void
55 Canvas::scroll_to (Coord x, Coord y)
56 {
57         /* We do things this way because we do not want to recurse through
58            the canvas for every scroll. In the presence of large MIDI
59            tracks this means traversing item lists that include
60            thousands of items (notes).
61
62            This design limits us to moving only those items (groups, typically)
63            that should move in certain ways as we scroll. In other terms, it
64            becomes O(1) rather than O(N).
65         */
66
67         for (list<ScrollGroup*>::iterator i = scrollers.begin(); i != scrollers.end(); ++i) {
68                 (*i)->scroll_to (Duple (x, y));
69         }
70
71         pick_current_item (0); // no current mouse position 
72 }
73
74 void
75 Canvas::add_scroller (ScrollGroup& i)
76 {
77         scrollers.push_back (&i);
78 }
79
80 void
81 Canvas::zoomed ()
82 {
83         pick_current_item (0); // no current mouse position
84 }
85
86 /** Render an area of the canvas.
87  *  @param area Area in window coordinates.
88  *  @param context Cairo context to render to.
89  */
90 void
91 Canvas::render (Rect const & area, Cairo::RefPtr<Cairo::Context> const & context) const
92 {
93 #ifdef CANVAS_DEBUG
94         if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
95                 cerr << this << " RENDER: " << area << endl;
96                 //cerr << "CANVAS @ " << this << endl;
97                 //dump (cerr);
98                 //cerr << "-------------------------\n";
99         }
100 #endif
101
102         render_count = 0;
103         
104         boost::optional<Rect> root_bbox = _root.bounding_box();
105         if (!root_bbox) {
106                 /* the root has no bounding box, so there's nothing to render */
107                 return;
108         }
109
110         boost::optional<Rect> draw = root_bbox->intersection (area);
111         if (draw) {
112                 
113                 /* there's a common area between the root and the requested
114                    area, so render it.
115                 */
116
117                 _root.render (*draw, context);
118
119 #if defined CANVAS_DEBUG && !PLATFORM_WINDOWS
120                 if (getenv ("CANVAS_HARLEQUIN_DEBUGGING")) {
121                         // This transparently colors the rect being rendered, after it has been drawn.
122                         double r = (random() % 65536) /65536.0;
123                         double g = (random() % 65536) /65536.0;
124                         double b = (random() % 65536) /65536.0;
125                         context->rectangle (draw->x0, draw->y0, draw->x1 - draw->x0, draw->y1 - draw->y0);
126                         context->set_source_rgba (r, g, b, 0.25);
127                         context->fill ();
128                 }
129 #endif
130         }
131
132 }
133
134 ostream&
135 operator<< (ostream& o, Canvas& c)
136 {
137         c.dump (o);
138         return o;
139 }
140
141 std::string
142 Canvas::indent() const
143
144         string s;
145
146         for (int n = 0; n < ArdourCanvas::dump_depth; ++n) {
147                 s += '\t';
148         }
149
150         return s;
151 }
152
153 std::string
154 Canvas::render_indent() const
155
156         string s;
157
158         for (int n = 0; n < ArdourCanvas::render_depth; ++n) {
159                 s += ' ';
160         }
161
162         return s;
163 }
164
165 void
166 Canvas::dump (ostream& o) const
167 {
168         dump_depth = 0;
169         _root.dump (o);
170 }       
171
172 /** Called when an item has been shown or hidden.
173  *  @param item Item that has been shown or hidden.
174  */
175 void
176 Canvas::item_shown_or_hidden (Item* item)
177 {
178         boost::optional<Rect> bbox = item->bounding_box ();
179         if (bbox) {
180                 if (item->item_to_window (*bbox).intersection (visible_area ())) {
181                         queue_draw_item_area (item, bbox.get ());
182                 }
183         }
184 }
185
186 /** Called when an item has a change to its visual properties
187  *  that do NOT affect its bounding box.
188  *  @param item Item that has been modified.
189  */
190 void
191 Canvas::item_visual_property_changed (Item* item)
192 {
193         boost::optional<Rect> bbox = item->bounding_box ();
194         if (bbox) {
195                 if (item->item_to_window (*bbox).intersection (visible_area ())) {
196                         queue_draw_item_area (item, bbox.get ());
197                 }
198         }
199 }
200
201 /** Called when an item has changed, but not moved.
202  *  @param item Item that has changed.
203  *  @param pre_change_bounding_box The bounding box of item before the change,
204  *  in the item's coordinates.
205  */
206 void
207 Canvas::item_changed (Item* item, boost::optional<Rect> pre_change_bounding_box)
208 {
209         
210         Rect window_bbox = visible_area ();
211
212         if (pre_change_bounding_box) {
213
214                 if (item->item_to_window (*pre_change_bounding_box).intersection (window_bbox)) {
215                         /* request a redraw of the item's old bounding box */
216                         queue_draw_item_area (item, pre_change_bounding_box.get ());
217                 }
218         }
219
220         boost::optional<Rect> post_change_bounding_box = item->bounding_box ();
221         if (post_change_bounding_box) {
222                 
223                 if (item->item_to_window (*post_change_bounding_box).intersection (window_bbox)) {
224                         /* request a redraw of the item's new bounding box */
225                         queue_draw_item_area (item, post_change_bounding_box.get ());
226                 }
227         }
228 }
229
230 Duple
231 Canvas::window_to_canvas (Duple const & d) const
232 {
233         ScrollGroup* best_group = 0;
234         ScrollGroup* sg = 0;
235
236         /* if the coordinates are negative, clamp to zero and find the item
237          * that covers that "edge" position.
238          */
239
240         Duple in_window (d);
241
242         if (in_window.x < 0) {
243                 in_window.x = 0;
244         }
245         if (in_window.y < 0) {
246                 in_window.y = 0;
247         }
248
249         for (list<ScrollGroup*>::const_iterator s = scrollers.begin(); s != scrollers.end(); ++s) {
250
251                 if ((*s)->covers_window (in_window)) {
252                         sg = *s;
253
254                         /* XXX January 22nd 2015: leaving this in place for now
255                          * but I think it fixes a bug that really should be
256                          * fixed in a different way (and will be) by my next
257                          * commit. But it may still be relevant. 
258                          */
259
260                         /* If scroll groups overlap, choose the one with the highest sensitivity,
261                            that is, choose an HV scroll group over an H or V
262                            only group. 
263                         */
264                         if (!best_group || sg->sensitivity() > best_group->sensitivity()) {
265                                 best_group = sg;
266                                 if (sg->sensitivity() == (ScrollGroup::ScrollsVertically | ScrollGroup::ScrollsHorizontally)) {
267                                         /* Can't do any better than this. */
268                                         break;
269                                 }
270                         }
271                 }
272         }
273
274         if (best_group) {
275                 return d.translate (best_group->scroll_offset());
276         }
277
278         return d;
279 }
280
281 Duple
282 Canvas::canvas_to_window (Duple const & d, bool rounded) const
283 {
284         /* Find the scroll group that covers d (a canvas coordinate). Scroll groups are only allowed
285          * as children of the root group, so we just scan its first level
286          * children and see what we can find.
287          */
288
289         std::list<Item*> const& root_children (_root.items());
290         ScrollGroup* sg = 0;
291         Duple wd;
292
293         for (std::list<Item*>::const_iterator i = root_children.begin(); i != root_children.end(); ++i) {
294                 if (((sg = dynamic_cast<ScrollGroup*>(*i)) != 0) && sg->covers_canvas (d)) {
295                         break;
296                 }
297         }
298
299         if (sg) {
300                 wd = d.translate (-sg->scroll_offset());
301         } else {
302                 wd = d;
303         }
304
305         /* Note that this intentionally almost always returns integer coordinates */
306
307         if (rounded) {
308                 wd.x = round (wd.x);
309                 wd.y = round (wd.y);
310         }
311
312         return wd;
313 }
314
315 /** Called when an item has moved.
316  *  @param item Item that has moved.
317  *  @param pre_change_parent_bounding_box The bounding box of the item before
318  *  the move, in its parent's coordinates.
319  */
320 void
321 Canvas::item_moved (Item* item, boost::optional<Rect> pre_change_parent_bounding_box)
322 {
323         if (pre_change_parent_bounding_box) {
324                 /* request a redraw of where the item used to be. The box has
325                  * to be in parent coordinate space since the bounding box of
326                  * an item does not change when moved. If we use
327                  * item->item_to_canvas() on the old bounding box, we will be
328
329                  * using the item's new position, and so will compute the wrong
330                  * invalidation area. If we use the parent (which has not
331                  * moved, then this will work.
332                  */
333                 queue_draw_item_area (item->parent(), pre_change_parent_bounding_box.get ());
334         }
335
336         boost::optional<Rect> post_change_bounding_box = item->bounding_box ();
337         if (post_change_bounding_box) {
338                 /* request a redraw of where the item now is */
339                 queue_draw_item_area (item, post_change_bounding_box.get ());
340         }
341 }
342
343 /** Request a redraw of a particular area in an item's coordinates.
344  *  @param item Item.
345  *  @param area Area to redraw in the item's coordinates.
346  */
347 void
348 Canvas::queue_draw_item_area (Item* item, Rect area)
349 {
350         request_redraw (item->item_to_window (area));
351 }
352
353 void
354 Canvas::set_tooltip_timeout (uint32_t msecs)
355 {
356         tooltip_timeout_msecs = msecs;
357 }
358
359 void
360 Canvas::set_background_color (Color c)
361 {
362         _bg_color = c;
363
364         boost::optional<Rect> r = _root.bounding_box();
365
366         if (r) {
367                 request_redraw (_root.item_to_window (r.get()));
368         }
369 }
370
371 void
372 GtkCanvas::re_enter ()
373 {
374         DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, "re-enter canvas by request\n");
375         _current_item = 0;
376         pick_current_item (0);
377 }
378
379 /** Construct a GtkCanvas */
380 GtkCanvas::GtkCanvas ()
381         : _current_item (0)
382         , _new_current_item (0)
383         , _grabbed_item (0)
384         , _focused_item (0)
385         , current_tooltip_item (0)
386         , tooltip_window (0)
387 {
388         /* these are the events we want to know about */
389         add_events (Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::POINTER_MOTION_MASK |
390                     Gdk::SCROLL_MASK | Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK |
391                     Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK);
392 }
393
394 void
395 GtkCanvas::pick_current_item (int state)
396 {
397         int x;
398         int y;
399
400         /* this version of ::pick_current_item() is called after an item is
401          * added or removed, so we have no coordinates to work from as is the
402          * case with a motion event. Find out where the mouse is and use that.
403          */
404              
405         Glib::RefPtr<const Gdk::Window> pointer_window = Gdk::Display::get_default()->get_window_at_pointer (x, y);
406
407         if (pointer_window != get_window()) {
408                 return;
409         }
410
411         pick_current_item (Duple (x, y), state);
412 }
413
414 /** Given @param point (a position in window coordinates)
415  *  and mouse state @param state, check to see if _current_item
416  *  (which will be used to deliver events) should change.
417  */
418 void
419 GtkCanvas::pick_current_item (Duple const & point, int state)
420 {
421         /* we do not enter/leave items during a drag/grab */
422
423         if (_grabbed_item) {
424                 return;
425         }
426
427         /* find the items at the given window position */
428
429         vector<Item const *> items;
430         _root.add_items_at_point (point, items);
431
432         DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("%1 covers %2 items\n", point, items.size()));
433
434 #ifndef NDEBUG
435         if (DEBUG_ENABLED(PBD::DEBUG::CanvasEnterLeave)) {
436                 for (vector<Item const*>::const_iterator it = items.begin(); it != items.end(); ++it) {
437 #ifdef CANVAS_DEBUG
438                         std::cerr << "\tItem " << (*it)->whatami() << '/' << (*it)->name << " ignore events ? " << (*it)->ignore_events() << " vis ? " << (*it)->visible() << std::endl;
439 #else
440                         std::cerr << "\tItem " << (*it)->whatami() << '/' << " ignore events ? " << (*it)->ignore_events() << " vis ? " << (*it)->visible() << std::endl;
441 #endif
442                 }
443         }
444 #endif
445
446         /* put all items at point that are event-sensitive and visible and NOT
447            groups into within_items. Note that items is sorted from bottom to
448            top, but we're going to reverse that for within_items so that its
449            first item is the upper-most item that can be chosen as _current_item.
450         */
451         
452         vector<Item const *>::const_iterator i;
453         list<Item const *> within_items;
454
455         for (i = items.begin(); i != items.end(); ++i) {
456
457                 Item const * possible_item = *i;
458
459                 /* We ignore invisible items, containers and items that ignore events */
460
461                 if (!possible_item->visible() || possible_item->ignore_events() || dynamic_cast<ArdourCanvas::Container const *>(possible_item) != 0) {
462                         continue;
463                 }
464                 within_items.push_front (possible_item);
465         }
466
467         DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("after filtering insensitive + containers, we have  %1 items\n", within_items.size()));
468
469         if (within_items.empty()) {
470
471                 /* no items at point, just send leave event below */
472                 _new_current_item = 0;
473
474         } else {
475
476                 if (within_items.front() == _current_item) {
477                         /* uppermost item at point is already _current_item */
478                         DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("CURRENT ITEM %1/%2\n", _new_current_item->whatami(), _current_item->name));
479                         return;
480                 }
481         
482                 _new_current_item = const_cast<Item*> (within_items.front());
483         }
484
485         if (_new_current_item != _current_item) {
486                 deliver_enter_leave (point, state);
487         }
488
489         if (_current_item) {
490                 DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("CURRENT ITEM %1/%2\n", _new_current_item->whatami(), _current_item->name));
491         } else {
492                 DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, "--- no current item\n");
493         }
494
495 }
496
497 /** Deliver a series of enter & leave events based on the pointer position being at window
498  * coordinate @param point, and pointer @param state (modifier keys, etc)
499  */
500 void
501 GtkCanvas::deliver_enter_leave (Duple const & point, int state)
502 {
503         /* setup enter & leave event structures */
504
505         Glib::RefPtr<Gdk::Window> win = get_window();
506
507         if (!win) {
508                 return;
509         }
510
511         GdkEventCrossing enter_event;
512         enter_event.type = GDK_ENTER_NOTIFY;
513         enter_event.window = win->gobj();
514         enter_event.send_event = 0;
515         enter_event.subwindow = 0;
516         enter_event.mode = GDK_CROSSING_NORMAL;
517         enter_event.focus = FALSE;
518         enter_event.state = state;
519
520         /* Events delivered to canvas items are expected to be in canvas
521          * coordinates but @param point is in window coordinates.
522          */
523         
524         Duple c = window_to_canvas (point);
525         enter_event.x = c.x;
526         enter_event.y = c.y;
527
528         GdkEventCrossing leave_event = enter_event;
529         leave_event.type = GDK_LEAVE_NOTIFY;
530
531         Item* i;
532         GdkNotifyType enter_detail = GDK_NOTIFY_UNKNOWN;
533         GdkNotifyType leave_detail = GDK_NOTIFY_UNKNOWN;
534         vector<Item*> items_to_leave_virtual;
535         vector<Item*> items_to_enter_virtual;
536
537         if (_new_current_item == 0) {
538
539                 leave_detail = GDK_NOTIFY_UNKNOWN;
540
541                 if (_current_item) {
542
543                         /* no current item, so also send virtual leave events to the
544                          * entire heirarchy for the current item
545                          */
546
547                         for (i = _current_item->parent(); i ; i = i->parent()) {
548                                 items_to_leave_virtual.push_back (i);
549                         }
550                 }
551
552         } else if (_current_item == 0) {
553
554                 enter_detail = GDK_NOTIFY_UNKNOWN;
555
556                 /* no current item, so also send virtual enter events to the
557                  * entire heirarchy for the new item 
558                  */
559
560                 for (i = _new_current_item->parent(); i ; i = i->parent()) {
561                         items_to_enter_virtual.push_back (i);
562                 }
563
564         } else if (_current_item->is_descendant_of (*_new_current_item)) {
565
566                 /* move from descendant to ancestor (X: "_current_item is an
567                  * inferior ("child") of _new_current_item") 
568                  *
569                  * Deliver "virtual" leave notifications to all items in the
570                  * heirarchy between current and new_current.
571                  */
572                 
573                 for (i = _current_item->parent(); i && i != _new_current_item; i = i->parent()) {
574                         items_to_leave_virtual.push_back (i);
575                 }
576
577                 enter_detail = GDK_NOTIFY_INFERIOR;
578                 leave_detail = GDK_NOTIFY_ANCESTOR;
579
580         } else if (_new_current_item->is_descendant_of (*_current_item)) {
581                 /* move from ancestor to descendant (X: "_new_current_item is
582                  * an inferior ("child") of _current_item")
583                  *
584                  * Deliver "virtual" enter notifications to all items in the
585                  * heirarchy between current and new_current.
586                  */
587
588                 for (i = _new_current_item->parent(); i && i != _current_item; i = i->parent()) {
589                         items_to_enter_virtual.push_back (i);
590                 }
591
592                 enter_detail = GDK_NOTIFY_ANCESTOR;
593                 leave_detail = GDK_NOTIFY_INFERIOR;
594
595         } else {
596
597                 Item const * common_ancestor = _current_item->closest_ancestor_with (*_new_current_item);
598
599                 /* deliver virtual leave events to everything between _current
600                  * and common_ancestor.
601                  */
602
603                 for (i = _current_item->parent(); i && i != common_ancestor; i = i->parent()) {
604                         items_to_leave_virtual.push_back (i);
605                 }
606
607                 /* deliver virtual enter events to everything between
608                  * _new_current and common_ancestor.
609                  */
610
611                 for (i = _new_current_item->parent(); i && i != common_ancestor; i = i->parent()) {
612                         items_to_enter_virtual.push_back (i);
613                 }
614
615                 enter_detail = GDK_NOTIFY_NONLINEAR;
616                 leave_detail = GDK_NOTIFY_NONLINEAR;
617         }
618         
619
620         if (_current_item && !_current_item->ignore_events ()) {
621                 leave_event.detail = leave_detail;
622                 _current_item->Event ((GdkEvent*)&leave_event);
623                 DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("LEAVE %1/%2\n", _current_item->whatami(), _current_item->name));
624         }
625
626         leave_event.detail = GDK_NOTIFY_VIRTUAL;
627         enter_event.detail = GDK_NOTIFY_VIRTUAL;
628
629         for (vector<Item*>::iterator it = items_to_leave_virtual.begin(); it != items_to_leave_virtual.end(); ++it) {
630                 if (!(*it)->ignore_events()) {
631                         DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("leave %1/%2\n", (*it)->whatami(), (*it)->name));
632                         (*it)->Event ((GdkEvent*)&leave_event);
633                 }
634         }
635
636         for (vector<Item*>::iterator it = items_to_enter_virtual.begin(); it != items_to_enter_virtual.end(); ++it) {
637                 if (!(*it)->ignore_events()) {
638                         DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("enter %1/%2\n", (*it)->whatami(), (*it)->name));
639                         (*it)->Event ((GdkEvent*)&enter_event);
640                         // std::cerr << "enter " << (*it)->whatami() << '/' << (*it)->name << std::endl;
641                 }
642         }
643
644         if (_new_current_item && !_new_current_item->ignore_events()) {
645                 enter_event.detail = enter_detail;
646                 DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("ENTER %1/%2\n", _new_current_item->whatami(), _new_current_item->name));
647                 start_tooltip_timeout (_new_current_item);
648                 _new_current_item->Event ((GdkEvent*)&enter_event);
649         }
650
651         _current_item = _new_current_item;
652 }
653
654
655 /** Deliver an event to the appropriate item; either the grabbed item, or
656  *  one of the items underneath the event.
657  *  @param point Position that the event has occurred at, in canvas coordinates.
658  *  @param event The event.
659  */
660 bool
661 GtkCanvas::deliver_event (GdkEvent* event)
662 {
663         /* Point in in canvas coordinate space */
664
665         const Item* event_item;
666
667         if (_grabbed_item) {
668                 /* we have a grabbed item, so everything gets sent there */
669                 DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("%1 %2 (%3) was grabbed, send event there\n",
670                                                                        _grabbed_item, _grabbed_item->whatami(), _grabbed_item->name));
671                 event_item = _grabbed_item;
672         } else {
673                 event_item = _current_item;
674         }
675
676         if (!event_item) {
677                 return false;
678         }
679
680         /* run through the items from child to parent, until one claims the event */
681
682         Item* item = const_cast<Item*> (event_item);
683         
684         while (item) {
685
686                 Item* parent = item->parent ();
687
688                 if (!item->ignore_events () && 
689                     item->Event (event)) {
690                         /* this item has just handled the event */
691                         DEBUG_TRACE (
692                                 PBD::DEBUG::CanvasEvents,
693                                 string_compose ("canvas event handled by %1 %2\n", item->whatami(), item->name.empty() ? "[unknown]" : item->name)
694                                 );
695                         
696                         return true;
697                 }
698                 
699                 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)));
700
701                 if ((item = parent) == 0) {
702                         break;
703                 }
704
705         }
706
707         return false;
708 }
709
710 /** Called when an item is being destroyed.
711  *  @param item Item being destroyed.
712  *  @param bounding_box Last known bounding box of the item.
713  */
714 void
715 GtkCanvas::item_going_away (Item* item, boost::optional<Rect> bounding_box)
716 {
717         if (bounding_box) {
718                 queue_draw_item_area (item, bounding_box.get ());
719         }
720         
721         if (_new_current_item == item) {
722                 _new_current_item = 0;
723         }
724
725         if (_grabbed_item == item) {
726                 _grabbed_item = 0;
727         }
728
729         if (_focused_item == item) {
730                 _focused_item = 0;
731         }
732
733         if (current_tooltip_item) {
734                 current_tooltip_item = 0;
735                 stop_tooltip_timeout ();
736         }
737
738         ScrollGroup* sg = dynamic_cast<ScrollGroup*>(item);
739         if (sg) {
740                 scrollers.remove (sg);
741         }
742
743         if (_current_item == item) {
744                 /* no need to send a leave event to this item, since it is going away 
745                  */
746                 _current_item = 0;
747                 pick_current_item (0); // no mouse state
748         }
749         
750 }
751
752 void
753 GtkCanvas::on_size_allocate (Gtk::Allocation& a)
754 {
755         EventBox::on_size_allocate (a);
756 #ifdef USE_CAIRO_IMAGE_SURFACE
757         /* allocate an image surface as large as the canvas itself */
758
759         canvas_image.clear ();
760         canvas_image = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, a.get_width(), a.get_height());
761 #endif
762 }
763
764 /** Handler for GDK expose events.
765  *  @param ev Event.
766  *  @return true if the event was handled.
767  */
768 bool
769 GtkCanvas::on_expose_event (GdkEventExpose* ev)
770 {
771 #ifdef USE_CAIRO_IMAGE_SURFACE
772         if (!canvas_image) {
773                 canvas_image = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, get_width(), get_height());
774         }
775         Cairo::RefPtr<Cairo::Context> draw_context = Cairo::Context::create (canvas_image);
776         Cairo::RefPtr<Cairo::Context> window_context = get_window()->create_cairo_context ();
777 #else 
778         Cairo::RefPtr<Cairo::Context> draw_context = get_window()->create_cairo_context ();
779 #endif
780
781         /* draw background color */
782         
783         draw_context->rectangle (ev->area.x, ev->area.y, ev->area.width, ev->area.height);
784         draw_context->clip_preserve ();
785         set_source_rgba (draw_context, _bg_color);
786         draw_context->fill ();
787         
788         /* render canvas */
789         
790         render (Rect (ev->area.x, ev->area.y, ev->area.x + ev->area.width, ev->area.y + ev->area.height), draw_context);
791
792 #ifdef USE_CAIRO_IMAGE_SURFACE
793         /* now blit our private surface back to the GDK one */
794
795         window_context->rectangle (ev->area.x, ev->area.y, ev->area.width, ev->area.height);
796         window_context->clip ();
797         window_context->set_source (canvas_image, 0, 0);
798         window_context->set_operator (Cairo::OPERATOR_SOURCE);
799         window_context->paint ();
800 #endif
801
802         return true;
803 }
804
805 /** Handler for GDK scroll events.
806  *  @param ev Event.
807  *  @return true if the event was handled.
808  */
809 bool
810 GtkCanvas::on_scroll_event (GdkEventScroll* ev)
811 {
812         /* translate event coordinates from window to canvas */
813
814         GdkEvent copy = *((GdkEvent*)ev);
815         Duple winpos = Duple (ev->x, ev->y);
816         Duple where = window_to_canvas (winpos);
817         
818         pick_current_item (winpos, ev->state);
819
820         copy.button.x = where.x;
821         copy.button.y = where.y;
822         
823         /* Coordinates in the event will be canvas coordinates, correctly adjusted
824            for scroll if this GtkCanvas is in a GtkCanvasViewport.
825         */
826
827         DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas scroll @ %1, %2 => %3\n", ev->x, ev->y, where));
828         return deliver_event (reinterpret_cast<GdkEvent*>(&copy));
829 }
830
831 /** Handler for GDK key press events.
832  *  @param ev Event.
833  *  @return true if the event was handled.
834  */
835 bool
836 GtkCanvas::on_key_press_event (GdkEventKey* ev)
837 {
838         DEBUG_TRACE (PBD::DEBUG::CanvasEvents, "canvas key press\n");
839         return deliver_event (reinterpret_cast<GdkEvent*>(ev));
840 }
841
842 /** Handler for GDK key release events.
843  *  @param ev Event.
844  *  @return true if the event was handled.
845  */
846 bool
847 GtkCanvas::on_key_release_event (GdkEventKey* ev)
848 {
849         DEBUG_TRACE (PBD::DEBUG::CanvasEvents, "canvas key release\n");
850         return deliver_event (reinterpret_cast<GdkEvent*>(ev));
851 }
852
853 /** Handler for GDK button press events.
854  *  @param ev Event.
855  *  @return true if the event was handled.
856  */
857 bool
858 GtkCanvas::on_button_press_event (GdkEventButton* ev)
859 {
860         /* translate event coordinates from window to canvas */
861
862         GdkEvent copy = *((GdkEvent*)ev);
863         Duple winpos = Duple (ev->x, ev->y);
864         Duple where = window_to_canvas (winpos);
865         
866         pick_current_item (winpos, ev->state);
867
868         copy.button.x = where.x;
869         copy.button.y = where.y;
870         
871         /* Coordinates in the event will be canvas coordinates, correctly adjusted
872            for scroll if this GtkCanvas is in a GtkCanvasViewport.
873         */
874
875         DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas button press @ %1, %2 => %3\n", ev->x, ev->y, where));
876         return deliver_event (reinterpret_cast<GdkEvent*>(&copy));
877 }
878
879 /** Handler for GDK button release events.
880  *  @param ev Event.
881  *  @return true if the event was handled.
882  */
883 bool
884 GtkCanvas::on_button_release_event (GdkEventButton* ev)
885 {       
886         /* translate event coordinates from window to canvas */
887
888         GdkEvent copy = *((GdkEvent*)ev);
889         Duple winpos = Duple (ev->x, ev->y);
890         Duple where = window_to_canvas (winpos);
891         
892         pick_current_item (winpos, ev->state);
893
894         copy.button.x = where.x;
895         copy.button.y = where.y;
896
897         /* Coordinates in the event will be canvas coordinates, correctly adjusted
898            for scroll if this GtkCanvas is in a GtkCanvasViewport.
899         */
900
901         DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas button release @ %1, %2 => %3\n", ev->x, ev->y, where));
902         return deliver_event (reinterpret_cast<GdkEvent*>(&copy));
903 }
904
905 bool
906 GtkCanvas::get_mouse_position (Duple& winpos) const
907 {
908         int x;
909         int y;
910         Gdk::ModifierType mask;
911         Glib::RefPtr<Gdk::Window> self = Glib::RefPtr<Gdk::Window>::cast_const (get_window ());
912
913         if (!self) {
914                 std::cerr << " no self window\n";
915                 winpos = Duple (0, 0);
916                 return false;
917         }
918
919         Glib::RefPtr<Gdk::Window> win = self->get_pointer (x, y, mask);
920
921         winpos.x = x;
922         winpos.y = y;
923
924         return true;
925 }
926
927 /** Handler for GDK motion events.
928  *  @param ev Event.
929  *  @return true if the event was handled.
930  */
931 bool
932 GtkCanvas::on_motion_notify_event (GdkEventMotion* ev)
933 {
934         hide_tooltip ();
935
936         /* translate event coordinates from window to canvas */
937
938         GdkEvent copy = *((GdkEvent*)ev);
939         Duple point (ev->x, ev->y);
940         Duple where = window_to_canvas (point);
941
942         copy.motion.x = where.x;
943         copy.motion.y = where.y;
944
945         /* Coordinates in "copy" will be canvas coordinates, 
946         */
947
948         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));
949
950         MouseMotion (point); /* EMIT SIGNAL */
951
952         pick_current_item (point, ev->state);
953
954         /* Now deliver the motion event.  It may seem a little inefficient
955            to recompute the items under the event, but the enter notify/leave
956            events may have deleted canvas items so it is important to
957            recompute the list in deliver_event.
958         */
959
960         return deliver_event (reinterpret_cast<GdkEvent*> (&copy));
961 }
962
963 bool
964 GtkCanvas::on_enter_notify_event (GdkEventCrossing* ev)
965 {
966         pick_current_item (Duple (ev->x, ev->y), ev->state);
967         return true;
968 }
969
970 bool
971 GtkCanvas::on_leave_notify_event (GdkEventCrossing* ev)
972 {
973         switch (ev->detail) {
974         case GDK_NOTIFY_ANCESTOR:
975         case GDK_NOTIFY_UNKNOWN:
976         case GDK_NOTIFY_VIRTUAL:
977         case GDK_NOTIFY_NONLINEAR:
978         case GDK_NOTIFY_NONLINEAR_VIRTUAL:
979                 /* leaving window, cancel any tooltips */
980                 stop_tooltip_timeout ();
981                 hide_tooltip ();
982                 break;
983         default:
984                 /* we don't care about any other kind
985                    of leave event (notably GDK_NOTIFY_INFERIOR)
986                 */
987                 break;
988         }
989         _new_current_item = 0;
990         deliver_enter_leave (Duple (ev->x, ev->y), ev->state);
991         return true;
992 }
993
994 /** Called to request a redraw of our canvas.
995  *  @param area Area to redraw, in window coordinates.
996  */
997 void
998 GtkCanvas::request_redraw (Rect const & request)
999 {
1000         Rect real_area;
1001
1002         Coord const w = width ();
1003         Coord const h = height ();
1004
1005         /* clamp area requested to actual visible window */
1006
1007         real_area.x0 = max (0.0, min (w, request.x0));
1008         real_area.x1 = max (0.0, min (w, request.x1));
1009         real_area.y0 = max (0.0, min (h, request.y0));
1010         real_area.y1 = max (0.0, min (h, request.y1));
1011
1012         queue_draw_area (real_area.x0, real_area.y0, real_area.width(), real_area.height());
1013 }
1014
1015 /** Called to request that we try to get a particular size for ourselves.
1016  *  @param size Size to request, in pixels.
1017  */
1018 void
1019 GtkCanvas::request_size (Duple size)
1020 {
1021         Duple req = size;
1022
1023         if (req.x > INT_MAX) {
1024                 req.x = INT_MAX;
1025         }
1026
1027         if (req.y > INT_MAX) {
1028                 req.y = INT_MAX;
1029         }
1030
1031         set_size_request (req.x, req.y);
1032 }
1033
1034 /** `Grab' an item, so that all events are sent to that item until it is `ungrabbed'.
1035  *  This is typically used for dragging items around, so that they are grabbed during
1036  *  the drag.
1037  *  @param item Item to grab.
1038  */
1039 void
1040 GtkCanvas::grab (Item* item)
1041 {
1042         /* XXX: should this be doing gdk_pointer_grab? */
1043         _grabbed_item = item;
1044 }
1045
1046
1047 /** `Ungrab' any item that was previously grabbed */
1048 void
1049 GtkCanvas::ungrab ()
1050 {
1051         /* XXX: should this be doing gdk_pointer_ungrab? */
1052         _grabbed_item = 0;
1053 }
1054
1055 /** Set keyboard focus on an item, so that all keyboard events are sent to that item until the focus
1056  *  moves elsewhere.
1057  *  @param item Item to grab.
1058  */
1059 void
1060 GtkCanvas::focus (Item* item)
1061 {
1062         _focused_item = item;
1063 }
1064
1065 void
1066 GtkCanvas::unfocus (Item* item)
1067 {
1068         if (item == _focused_item) {
1069                 _focused_item = 0;
1070         }
1071 }
1072
1073 /** @return The visible area of the canvas, in window coordinates */
1074 Rect
1075 GtkCanvas::visible_area () const
1076 {
1077         return Rect (0, 0, get_allocation().get_width (), get_allocation().get_height ());
1078 }
1079
1080 Coord
1081 GtkCanvas::width() const
1082 {
1083         return get_allocation().get_width();
1084 }
1085
1086 Coord
1087 GtkCanvas::height() const
1088 {
1089         return get_allocation().get_height();
1090 }
1091
1092 void
1093 GtkCanvas::start_tooltip_timeout (Item* item)
1094 {
1095         stop_tooltip_timeout ();
1096
1097         if (item) {
1098                 current_tooltip_item = item;
1099
1100                 /* wait for the first idle that happens after this is
1101                    called. this means that we've stopped processing events, which
1102                    in turn implies that the user has stopped doing stuff for a
1103                    little while.
1104                 */
1105
1106                 Glib::signal_idle().connect (sigc::mem_fun (*this, &GtkCanvas::really_start_tooltip_timeout));
1107         }
1108 }
1109
1110 bool
1111 GtkCanvas::really_start_tooltip_timeout ()
1112 {
1113         /* an idle has occured since we entered a tooltip-bearing widget. Now
1114          * wait 1 second and if the timeout isn't cancelled, show the tooltip.
1115          */
1116
1117         if (current_tooltip_item) {
1118                 tooltip_timeout_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &GtkCanvas::show_tooltip), tooltip_timeout_msecs);
1119         }
1120
1121         return false; /* this is called from an idle callback, don't call it again */
1122 }
1123
1124 void
1125 GtkCanvas::stop_tooltip_timeout ()
1126 {
1127         current_tooltip_item = 0;
1128         tooltip_timeout_connection.disconnect ();
1129 }
1130
1131 bool
1132 GtkCanvas::show_tooltip ()
1133 {
1134         Rect tooltip_item_bbox;
1135
1136         if (!current_tooltip_item || current_tooltip_item->tooltip().empty() || !current_tooltip_item->bounding_box()) {
1137                 return false;
1138         }
1139
1140         if (!tooltip_window) {
1141                 tooltip_window = new Gtk::Window (Gtk::WINDOW_POPUP);
1142                 tooltip_label = manage (new Gtk::Label);
1143                 tooltip_label->show ();
1144                 tooltip_window->add (*tooltip_label);
1145                 tooltip_window->set_border_width (6);
1146                 tooltip_window->set_name ("tooltip");
1147         }
1148
1149         tooltip_label->set_text (current_tooltip_item->tooltip());
1150
1151         /* figure out where to position the tooltip */
1152
1153         Gtk::Widget* toplevel = get_toplevel();
1154         assert (toplevel);
1155         int pointer_x, pointer_y;
1156         Gdk::ModifierType mask;
1157
1158         (void) toplevel->get_window()->get_pointer (pointer_x, pointer_y, mask);
1159
1160         Duple tooltip_window_origin (pointer_x, pointer_y);
1161         
1162         /* convert to root window coordinates */
1163
1164         int win_x, win_y;
1165         dynamic_cast<Gtk::Window*>(toplevel)->get_position (win_x, win_y);
1166         
1167         tooltip_window_origin = tooltip_window_origin.translate (Duple (win_x, win_y));
1168
1169         /* we don't want the pointer to be inside the window when it is
1170          * displayed, because then we generate a leave/enter event pair when
1171          * the window is displayed then hidden - the enter event will
1172          * trigger a new tooltip timeout.
1173          *
1174          * So move the window right of the pointer position by just a enough
1175          * to get it away from the pointer.
1176          */
1177
1178         tooltip_window_origin.x += 20;
1179
1180         /* move the tooltip window into position */
1181
1182         tooltip_window->move (tooltip_window_origin.x, tooltip_window_origin.y);
1183
1184         /* ready to show */
1185
1186         tooltip_window->present ();
1187         
1188         /* called from a timeout handler, don't call it again */
1189
1190         return false;
1191 }
1192
1193 void
1194 GtkCanvas::hide_tooltip ()
1195 {
1196         /* hide it if its there */
1197
1198         if (tooltip_window) {
1199                 tooltip_window->hide ();
1200         }
1201 }
1202
1203 /** Create a GtkCanvaSViewport.
1204  *  @param hadj Adjustment to use for horizontal scrolling.
1205  *  @param vadj Adjustment to use for vertica scrolling.
1206  */
1207 GtkCanvasViewport::GtkCanvasViewport (Gtk::Adjustment& hadj, Gtk::Adjustment& vadj)
1208         : Alignment (0, 0, 1.0, 1.0)
1209         , hadjustment (hadj)
1210         , vadjustment (vadj)
1211 {
1212         add (_canvas);
1213
1214         hadj.signal_value_changed().connect (sigc::mem_fun (*this, &GtkCanvasViewport::scrolled));
1215         vadj.signal_value_changed().connect (sigc::mem_fun (*this, &GtkCanvasViewport::scrolled));
1216 }
1217
1218 void
1219 GtkCanvasViewport::scrolled ()
1220 {
1221         _canvas.scroll_to (hadjustment.get_value(), vadjustment.get_value());
1222         queue_draw ();
1223 }
1224
1225 /** Handler for when GTK asks us what minimum size we want.
1226  *  @param req Requsition to fill in.
1227  */
1228 void
1229 GtkCanvasViewport::on_size_request (Gtk::Requisition* req)
1230 {
1231         /* force the canvas to size itself */
1232         // _canvas.root()->bounding_box(); 
1233
1234         req->width = 16;
1235         req->height = 16;
1236 }
1237