Delete a canvas tooltip after we've finished with it
[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         , _single_exposure (1)
386         , current_tooltip_item (0)
387         , tooltip_window (0)
388 {
389         /* these are the events we want to know about */
390         add_events (Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::POINTER_MOTION_MASK |
391                     Gdk::SCROLL_MASK | Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK |
392                     Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK);
393 }
394
395 void
396 GtkCanvas::pick_current_item (int state)
397 {
398         int x;
399         int y;
400
401         /* this version of ::pick_current_item() is called after an item is
402          * added or removed, so we have no coordinates to work from as is the
403          * case with a motion event. Find out where the mouse is and use that.
404          */
405              
406         Glib::RefPtr<const Gdk::Window> pointer_window = Gdk::Display::get_default()->get_window_at_pointer (x, y);
407
408         if (pointer_window != get_window()) {
409                 return;
410         }
411
412         pick_current_item (Duple (x, y), state);
413 }
414
415 /** Given @param point (a position in window coordinates)
416  *  and mouse state @param state, check to see if _current_item
417  *  (which will be used to deliver events) should change.
418  */
419 void
420 GtkCanvas::pick_current_item (Duple const & point, int state)
421 {
422         /* we do not enter/leave items during a drag/grab */
423
424         if (_grabbed_item) {
425                 return;
426         }
427
428         /* find the items at the given window position */
429
430         vector<Item const *> items;
431         _root.add_items_at_point (point, items);
432
433         DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("%1 covers %2 items\n", point, items.size()));
434
435 #ifndef NDEBUG
436         if (DEBUG_ENABLED(PBD::DEBUG::CanvasEnterLeave)) {
437                 for (vector<Item const*>::const_iterator it = items.begin(); it != items.end(); ++it) {
438 #ifdef CANVAS_DEBUG
439                         std::cerr << "\tItem " << (*it)->whatami() << '/' << (*it)->name << " ignore events ? " << (*it)->ignore_events() << " vis ? " << (*it)->visible() << std::endl;
440 #else
441                         std::cerr << "\tItem " << (*it)->whatami() << '/' << " ignore events ? " << (*it)->ignore_events() << " vis ? " << (*it)->visible() << std::endl;
442 #endif
443                 }
444         }
445 #endif
446
447         /* put all items at point that are event-sensitive and visible and NOT
448            groups into within_items. Note that items is sorted from bottom to
449            top, but we're going to reverse that for within_items so that its
450            first item is the upper-most item that can be chosen as _current_item.
451         */
452         
453         vector<Item const *>::const_iterator i;
454         list<Item const *> within_items;
455
456         for (i = items.begin(); i != items.end(); ++i) {
457
458                 Item const * possible_item = *i;
459
460                 /* We ignore invisible items, containers and items that ignore events */
461
462                 if (!possible_item->visible() || possible_item->ignore_events() || dynamic_cast<ArdourCanvas::Container const *>(possible_item) != 0) {
463                         continue;
464                 }
465                 within_items.push_front (possible_item);
466         }
467
468         DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("after filtering insensitive + containers, we have  %1 items\n", within_items.size()));
469
470         if (within_items.empty()) {
471
472                 /* no items at point, just send leave event below */
473                 _new_current_item = 0;
474
475         } else {
476
477                 if (within_items.front() == _current_item) {
478                         /* uppermost item at point is already _current_item */
479                         DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("CURRENT ITEM %1/%2\n", _new_current_item->whatami(), _current_item->name));
480                         return;
481                 }
482         
483                 _new_current_item = const_cast<Item*> (within_items.front());
484         }
485
486         if (_new_current_item != _current_item) {
487                 deliver_enter_leave (point, state);
488         }
489
490         if (_current_item) {
491                 DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("CURRENT ITEM %1/%2\n", _new_current_item->whatami(), _current_item->name));
492         } else {
493                 DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, "--- no current item\n");
494         }
495
496 }
497
498 /** Deliver a series of enter & leave events based on the pointer position being at window
499  * coordinate @param point, and pointer @param state (modifier keys, etc)
500  */
501 void
502 GtkCanvas::deliver_enter_leave (Duple const & point, int state)
503 {
504         /* setup enter & leave event structures */
505
506         Glib::RefPtr<Gdk::Window> win = get_window();
507
508         if (!win) {
509                 return;
510         }
511
512         GdkEventCrossing enter_event;
513         enter_event.type = GDK_ENTER_NOTIFY;
514         enter_event.window = win->gobj();
515         enter_event.send_event = 0;
516         enter_event.subwindow = 0;
517         enter_event.mode = GDK_CROSSING_NORMAL;
518         enter_event.focus = FALSE;
519         enter_event.state = state;
520
521         /* Events delivered to canvas items are expected to be in canvas
522          * coordinates but @param point is in window coordinates.
523          */
524         
525         Duple c = window_to_canvas (point);
526         enter_event.x = c.x;
527         enter_event.y = c.y;
528
529         GdkEventCrossing leave_event = enter_event;
530         leave_event.type = GDK_LEAVE_NOTIFY;
531
532         Item* i;
533         GdkNotifyType enter_detail = GDK_NOTIFY_UNKNOWN;
534         GdkNotifyType leave_detail = GDK_NOTIFY_UNKNOWN;
535         vector<Item*> items_to_leave_virtual;
536         vector<Item*> items_to_enter_virtual;
537
538         if (_new_current_item == 0) {
539
540                 leave_detail = GDK_NOTIFY_UNKNOWN;
541
542                 if (_current_item) {
543
544                         /* no current item, so also send virtual leave events to the
545                          * entire heirarchy for the current item
546                          */
547
548                         for (i = _current_item->parent(); i ; i = i->parent()) {
549                                 items_to_leave_virtual.push_back (i);
550                         }
551                 }
552
553         } else if (_current_item == 0) {
554
555                 enter_detail = GDK_NOTIFY_UNKNOWN;
556
557                 /* no current item, so also send virtual enter events to the
558                  * entire heirarchy for the new item 
559                  */
560
561                 for (i = _new_current_item->parent(); i ; i = i->parent()) {
562                         items_to_enter_virtual.push_back (i);
563                 }
564
565         } else if (_current_item->is_descendant_of (*_new_current_item)) {
566
567                 /* move from descendant to ancestor (X: "_current_item is an
568                  * inferior ("child") of _new_current_item") 
569                  *
570                  * Deliver "virtual" leave notifications to all items in the
571                  * heirarchy between current and new_current.
572                  */
573                 
574                 for (i = _current_item->parent(); i && i != _new_current_item; i = i->parent()) {
575                         items_to_leave_virtual.push_back (i);
576                 }
577
578                 enter_detail = GDK_NOTIFY_INFERIOR;
579                 leave_detail = GDK_NOTIFY_ANCESTOR;
580
581         } else if (_new_current_item->is_descendant_of (*_current_item)) {
582                 /* move from ancestor to descendant (X: "_new_current_item is
583                  * an inferior ("child") of _current_item")
584                  *
585                  * Deliver "virtual" enter notifications to all items in the
586                  * heirarchy between current and new_current.
587                  */
588
589                 for (i = _new_current_item->parent(); i && i != _current_item; i = i->parent()) {
590                         items_to_enter_virtual.push_back (i);
591                 }
592
593                 enter_detail = GDK_NOTIFY_ANCESTOR;
594                 leave_detail = GDK_NOTIFY_INFERIOR;
595
596         } else {
597
598                 Item const * common_ancestor = _current_item->closest_ancestor_with (*_new_current_item);
599
600                 /* deliver virtual leave events to everything between _current
601                  * and common_ancestor.
602                  */
603
604                 for (i = _current_item->parent(); i && i != common_ancestor; i = i->parent()) {
605                         items_to_leave_virtual.push_back (i);
606                 }
607
608                 /* deliver virtual enter events to everything between
609                  * _new_current and common_ancestor.
610                  */
611
612                 for (i = _new_current_item->parent(); i && i != common_ancestor; i = i->parent()) {
613                         items_to_enter_virtual.push_back (i);
614                 }
615
616                 enter_detail = GDK_NOTIFY_NONLINEAR;
617                 leave_detail = GDK_NOTIFY_NONLINEAR;
618         }
619         
620
621         if (_current_item && !_current_item->ignore_events ()) {
622                 leave_event.detail = leave_detail;
623                 _current_item->Event ((GdkEvent*)&leave_event);
624                 DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("LEAVE %1/%2\n", _current_item->whatami(), _current_item->name));
625         }
626
627         leave_event.detail = GDK_NOTIFY_VIRTUAL;
628         enter_event.detail = GDK_NOTIFY_VIRTUAL;
629
630         for (vector<Item*>::iterator it = items_to_leave_virtual.begin(); it != items_to_leave_virtual.end(); ++it) {
631                 if (!(*it)->ignore_events()) {
632                         DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("leave %1/%2\n", (*it)->whatami(), (*it)->name));
633                         (*it)->Event ((GdkEvent*)&leave_event);
634                 }
635         }
636
637         for (vector<Item*>::iterator it = items_to_enter_virtual.begin(); it != items_to_enter_virtual.end(); ++it) {
638                 if (!(*it)->ignore_events()) {
639                         DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("enter %1/%2\n", (*it)->whatami(), (*it)->name));
640                         (*it)->Event ((GdkEvent*)&enter_event);
641                         // std::cerr << "enter " << (*it)->whatami() << '/' << (*it)->name << std::endl;
642                 }
643         }
644
645         if (_new_current_item && !_new_current_item->ignore_events()) {
646                 enter_event.detail = enter_detail;
647                 DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("ENTER %1/%2\n", _new_current_item->whatami(), _new_current_item->name));
648                 start_tooltip_timeout (_new_current_item);
649                 _new_current_item->Event ((GdkEvent*)&enter_event);
650         }
651
652         _current_item = _new_current_item;
653 }
654
655
656 /** Deliver an event to the appropriate item; either the grabbed item, or
657  *  one of the items underneath the event.
658  *  @param point Position that the event has occurred at, in canvas coordinates.
659  *  @param event The event.
660  */
661 bool
662 GtkCanvas::deliver_event (GdkEvent* event)
663 {
664         /* Point in in canvas coordinate space */
665
666         const Item* event_item;
667
668         if (_grabbed_item) {
669                 /* we have a grabbed item, so everything gets sent there */
670                 DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("%1 %2 (%3) was grabbed, send event there\n",
671                                                                        _grabbed_item, _grabbed_item->whatami(), _grabbed_item->name));
672                 event_item = _grabbed_item;
673         } else {
674                 event_item = _current_item;
675         }
676
677         if (!event_item) {
678                 return false;
679         }
680
681         /* run through the items from child to parent, until one claims the event */
682
683         Item* item = const_cast<Item*> (event_item);
684         
685         while (item) {
686
687                 Item* parent = item->parent ();
688
689                 if (!item->ignore_events () && 
690                     item->Event (event)) {
691                         /* this item has just handled the event */
692                         DEBUG_TRACE (
693                                 PBD::DEBUG::CanvasEvents,
694                                 string_compose ("canvas event handled by %1 %2\n", item->whatami(), item->name.empty() ? "[unknown]" : item->name)
695                                 );
696                         
697                         return true;
698                 }
699                 
700                 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)));
701
702                 if ((item = parent) == 0) {
703                         break;
704                 }
705
706         }
707
708         return false;
709 }
710
711 /** Called when an item is being destroyed.
712  *  @param item Item being destroyed.
713  *  @param bounding_box Last known bounding box of the item.
714  */
715 void
716 GtkCanvas::item_going_away (Item* item, boost::optional<Rect> bounding_box)
717 {
718         if (bounding_box) {
719                 queue_draw_item_area (item, bounding_box.get ());
720         }
721         
722         if (_new_current_item == item) {
723                 _new_current_item = 0;
724         }
725
726         if (_grabbed_item == item) {
727                 _grabbed_item = 0;
728         }
729
730         if (_focused_item == item) {
731                 _focused_item = 0;
732         }
733
734         if (current_tooltip_item) {
735                 current_tooltip_item = 0;
736                 stop_tooltip_timeout ();
737         }
738
739         ScrollGroup* sg = dynamic_cast<ScrollGroup*>(item);
740         if (sg) {
741                 scrollers.remove (sg);
742         }
743
744         if (_current_item == item) {
745                 /* no need to send a leave event to this item, since it is going away 
746                  */
747                 _current_item = 0;
748                 pick_current_item (0); // no mouse state
749         }
750         
751 }
752
753 void
754 GtkCanvas::on_size_allocate (Gtk::Allocation& a)
755 {
756         EventBox::on_size_allocate (a);
757 #ifdef USE_CAIRO_IMAGE_SURFACE
758         /* allocate an image surface as large as the canvas itself */
759
760         canvas_image.clear ();
761         canvas_image = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, a.get_width(), a.get_height());
762 #endif
763 }
764
765 /** Handler for GDK expose events.
766  *  @param ev Event.
767  *  @return true if the event was handled.
768  */
769 bool
770 GtkCanvas::on_expose_event (GdkEventExpose* ev)
771 {
772 #ifdef USE_CAIRO_IMAGE_SURFACE
773         if (!canvas_image) {
774                 canvas_image = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, get_width(), get_height());
775         }
776         Cairo::RefPtr<Cairo::Context> draw_context = Cairo::Context::create (canvas_image);
777         Cairo::RefPtr<Cairo::Context> window_context = get_window()->create_cairo_context ();
778 #else 
779         Cairo::RefPtr<Cairo::Context> draw_context = get_window()->create_cairo_context ();
780 #endif
781
782         /* draw background color */
783         
784         draw_context->rectangle (ev->area.x, ev->area.y, ev->area.width, ev->area.height);
785         draw_context->clip_preserve ();
786         set_source_rgba (draw_context, _bg_color);
787         draw_context->fill ();
788         
789         /* render canvas */
790                 if ( _single_exposure ) {
791         
792                         render (Rect (ev->area.x, ev->area.y, ev->area.x + ev->area.width, ev->area.y + ev->area.height), draw_context);
793
794                 } else {
795                         GdkRectangle* rects;
796                         gint nrects;
797                         
798                         gdk_region_get_rectangles (ev->region, &rects, &nrects);
799                         for (gint n = 0; n < nrects; ++n) {
800                                 draw_context->set_identity_matrix();  //reset the cairo matrix, just in case someone left it transformed after drawing ( cough )
801                                 render (Rect (rects[n].x, rects[n].y, rects[n].x + rects[n].width, rects[n].y + rects[n].height), draw_context);
802                         }
803                         g_free (rects);
804                 }
805                 
806 #ifdef USE_CAIRO_IMAGE_SURFACE
807         /* now blit our private surface back to the GDK one */
808
809         window_context->rectangle (ev->area.x, ev->area.y, ev->area.width, ev->area.height);
810         window_context->clip ();
811         window_context->set_source (canvas_image, 0, 0);
812         window_context->set_operator (Cairo::OPERATOR_SOURCE);
813         window_context->paint ();
814 #endif
815
816         return true;
817 }
818
819 /** Handler for GDK scroll events.
820  *  @param ev Event.
821  *  @return true if the event was handled.
822  */
823 bool
824 GtkCanvas::on_scroll_event (GdkEventScroll* ev)
825 {
826         /* translate event coordinates from window to canvas */
827
828         GdkEvent copy = *((GdkEvent*)ev);
829         Duple winpos = Duple (ev->x, ev->y);
830         Duple where = window_to_canvas (winpos);
831         
832         pick_current_item (winpos, ev->state);
833
834         copy.button.x = where.x;
835         copy.button.y = where.y;
836         
837         /* Coordinates in the event will be canvas coordinates, correctly adjusted
838            for scroll if this GtkCanvas is in a GtkCanvasViewport.
839         */
840
841         DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas scroll @ %1, %2 => %3\n", ev->x, ev->y, where));
842         return deliver_event (reinterpret_cast<GdkEvent*>(&copy));
843 }
844
845 /** Handler for GDK key press events.
846  *  @param ev Event.
847  *  @return true if the event was handled.
848  */
849 bool
850 GtkCanvas::on_key_press_event (GdkEventKey* ev)
851 {
852         DEBUG_TRACE (PBD::DEBUG::CanvasEvents, "canvas key press\n");
853         return deliver_event (reinterpret_cast<GdkEvent*>(ev));
854 }
855
856 /** Handler for GDK key release events.
857  *  @param ev Event.
858  *  @return true if the event was handled.
859  */
860 bool
861 GtkCanvas::on_key_release_event (GdkEventKey* ev)
862 {
863         DEBUG_TRACE (PBD::DEBUG::CanvasEvents, "canvas key release\n");
864         return deliver_event (reinterpret_cast<GdkEvent*>(ev));
865 }
866
867 /** Handler for GDK button press events.
868  *  @param ev Event.
869  *  @return true if the event was handled.
870  */
871 bool
872 GtkCanvas::on_button_press_event (GdkEventButton* ev)
873 {
874         /* translate event coordinates from window to canvas */
875
876         GdkEvent copy = *((GdkEvent*)ev);
877         Duple winpos = Duple (ev->x, ev->y);
878         Duple where = window_to_canvas (winpos);
879         
880         pick_current_item (winpos, ev->state);
881
882         copy.button.x = where.x;
883         copy.button.y = where.y;
884         
885         /* Coordinates in the event will be canvas coordinates, correctly adjusted
886            for scroll if this GtkCanvas is in a GtkCanvasViewport.
887         */
888
889         DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas button press @ %1, %2 => %3\n", ev->x, ev->y, where));
890         return deliver_event (reinterpret_cast<GdkEvent*>(&copy));
891 }
892
893 /** Handler for GDK button release events.
894  *  @param ev Event.
895  *  @return true if the event was handled.
896  */
897 bool
898 GtkCanvas::on_button_release_event (GdkEventButton* ev)
899 {       
900         /* translate event coordinates from window to canvas */
901
902         GdkEvent copy = *((GdkEvent*)ev);
903         Duple winpos = Duple (ev->x, ev->y);
904         Duple where = window_to_canvas (winpos);
905         
906         pick_current_item (winpos, ev->state);
907
908         copy.button.x = where.x;
909         copy.button.y = where.y;
910
911         /* Coordinates in the event will be canvas coordinates, correctly adjusted
912            for scroll if this GtkCanvas is in a GtkCanvasViewport.
913         */
914
915         DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas button release @ %1, %2 => %3\n", ev->x, ev->y, where));
916         return deliver_event (reinterpret_cast<GdkEvent*>(&copy));
917 }
918
919 bool
920 GtkCanvas::get_mouse_position (Duple& winpos) const
921 {
922         int x;
923         int y;
924         Gdk::ModifierType mask;
925         Glib::RefPtr<Gdk::Window> self = Glib::RefPtr<Gdk::Window>::cast_const (get_window ());
926
927         if (!self) {
928                 std::cerr << " no self window\n";
929                 winpos = Duple (0, 0);
930                 return false;
931         }
932
933         Glib::RefPtr<Gdk::Window> win = self->get_pointer (x, y, mask);
934
935         winpos.x = x;
936         winpos.y = y;
937
938         return true;
939 }
940
941 /** Handler for GDK motion events.
942  *  @param ev Event.
943  *  @return true if the event was handled.
944  */
945 bool
946 GtkCanvas::on_motion_notify_event (GdkEventMotion* ev)
947 {
948         hide_tooltip ();
949
950         /* translate event coordinates from window to canvas */
951
952         GdkEvent copy = *((GdkEvent*)ev);
953         Duple point (ev->x, ev->y);
954         Duple where = window_to_canvas (point);
955
956         copy.motion.x = where.x;
957         copy.motion.y = where.y;
958
959         /* Coordinates in "copy" will be canvas coordinates, 
960         */
961
962         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));
963
964         MouseMotion (point); /* EMIT SIGNAL */
965
966         pick_current_item (point, ev->state);
967
968         /* Now deliver the motion event.  It may seem a little inefficient
969            to recompute the items under the event, but the enter notify/leave
970            events may have deleted canvas items so it is important to
971            recompute the list in deliver_event.
972         */
973
974         return deliver_event (reinterpret_cast<GdkEvent*> (&copy));
975 }
976
977 bool
978 GtkCanvas::on_enter_notify_event (GdkEventCrossing* ev)
979 {
980         pick_current_item (Duple (ev->x, ev->y), ev->state);
981         return true;
982 }
983
984 bool
985 GtkCanvas::on_leave_notify_event (GdkEventCrossing* ev)
986 {
987         switch (ev->detail) {
988         case GDK_NOTIFY_ANCESTOR:
989         case GDK_NOTIFY_UNKNOWN:
990         case GDK_NOTIFY_VIRTUAL:
991         case GDK_NOTIFY_NONLINEAR:
992         case GDK_NOTIFY_NONLINEAR_VIRTUAL:
993                 /* leaving window, cancel any tooltips */
994                 stop_tooltip_timeout ();
995                 hide_tooltip ();
996                 break;
997         default:
998                 /* we don't care about any other kind
999                    of leave event (notably GDK_NOTIFY_INFERIOR)
1000                 */
1001                 break;
1002         }
1003         _new_current_item = 0;
1004         deliver_enter_leave (Duple (ev->x, ev->y), ev->state);
1005         return true;
1006 }
1007
1008 /** Called to request a redraw of our canvas.
1009  *  @param area Area to redraw, in window coordinates.
1010  */
1011 void
1012 GtkCanvas::request_redraw (Rect const & request)
1013 {
1014         Rect real_area;
1015
1016         Coord const w = width ();
1017         Coord const h = height ();
1018
1019         /* clamp area requested to actual visible window */
1020
1021         real_area.x0 = max (0.0, min (w, request.x0));
1022         real_area.x1 = max (0.0, min (w, request.x1));
1023         real_area.y0 = max (0.0, min (h, request.y0));
1024         real_area.y1 = max (0.0, min (h, request.y1));
1025
1026         queue_draw_area (real_area.x0, real_area.y0, real_area.width(), real_area.height());
1027 }
1028
1029 /** Called to request that we try to get a particular size for ourselves.
1030  *  @param size Size to request, in pixels.
1031  */
1032 void
1033 GtkCanvas::request_size (Duple size)
1034 {
1035         Duple req = size;
1036
1037         if (req.x > INT_MAX) {
1038                 req.x = INT_MAX;
1039         }
1040
1041         if (req.y > INT_MAX) {
1042                 req.y = INT_MAX;
1043         }
1044
1045         set_size_request (req.x, req.y);
1046 }
1047
1048 /** `Grab' an item, so that all events are sent to that item until it is `ungrabbed'.
1049  *  This is typically used for dragging items around, so that they are grabbed during
1050  *  the drag.
1051  *  @param item Item to grab.
1052  */
1053 void
1054 GtkCanvas::grab (Item* item)
1055 {
1056         /* XXX: should this be doing gdk_pointer_grab? */
1057         _grabbed_item = item;
1058 }
1059
1060
1061 /** `Ungrab' any item that was previously grabbed */
1062 void
1063 GtkCanvas::ungrab ()
1064 {
1065         /* XXX: should this be doing gdk_pointer_ungrab? */
1066         _grabbed_item = 0;
1067 }
1068
1069 /** Set keyboard focus on an item, so that all keyboard events are sent to that item until the focus
1070  *  moves elsewhere.
1071  *  @param item Item to grab.
1072  */
1073 void
1074 GtkCanvas::focus (Item* item)
1075 {
1076         _focused_item = item;
1077 }
1078
1079 void
1080 GtkCanvas::unfocus (Item* item)
1081 {
1082         if (item == _focused_item) {
1083                 _focused_item = 0;
1084         }
1085 }
1086
1087 /** @return The visible area of the canvas, in window coordinates */
1088 Rect
1089 GtkCanvas::visible_area () const
1090 {
1091         return Rect (0, 0, get_allocation().get_width (), get_allocation().get_height ());
1092 }
1093
1094 Coord
1095 GtkCanvas::width() const
1096 {
1097         return get_allocation().get_width();
1098 }
1099
1100 Coord
1101 GtkCanvas::height() const
1102 {
1103         return get_allocation().get_height();
1104 }
1105
1106 void
1107 GtkCanvas::start_tooltip_timeout (Item* item)
1108 {
1109         stop_tooltip_timeout ();
1110
1111         if (item) {
1112                 current_tooltip_item = item;
1113
1114                 /* wait for the first idle that happens after this is
1115                    called. this means that we've stopped processing events, which
1116                    in turn implies that the user has stopped doing stuff for a
1117                    little while.
1118                 */
1119
1120                 Glib::signal_idle().connect (sigc::mem_fun (*this, &GtkCanvas::really_start_tooltip_timeout));
1121         }
1122 }
1123
1124 bool
1125 GtkCanvas::really_start_tooltip_timeout ()
1126 {
1127         /* an idle has occured since we entered a tooltip-bearing widget. Now
1128          * wait 1 second and if the timeout isn't cancelled, show the tooltip.
1129          */
1130
1131         if (current_tooltip_item) {
1132                 tooltip_timeout_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &GtkCanvas::show_tooltip), tooltip_timeout_msecs);
1133         }
1134
1135         return false; /* this is called from an idle callback, don't call it again */
1136 }
1137
1138 void
1139 GtkCanvas::stop_tooltip_timeout ()
1140 {
1141         current_tooltip_item = 0;
1142         tooltip_timeout_connection.disconnect ();
1143 }
1144
1145 bool
1146 GtkCanvas::show_tooltip ()
1147 {
1148         Rect tooltip_item_bbox;
1149
1150         if (!current_tooltip_item || current_tooltip_item->tooltip().empty() || !current_tooltip_item->bounding_box()) {
1151                 return false;
1152         }
1153
1154         if (!tooltip_window) {
1155                 tooltip_window = new Gtk::Window (Gtk::WINDOW_POPUP);
1156                 tooltip_label = manage (new Gtk::Label);
1157                 tooltip_label->show ();
1158                 tooltip_window->add (*tooltip_label);
1159                 tooltip_window->set_border_width (6);
1160                 tooltip_window->set_name ("tooltip");
1161         }
1162
1163         tooltip_label->set_text (current_tooltip_item->tooltip());
1164
1165         /* figure out where to position the tooltip */
1166
1167         Gtk::Widget* toplevel = get_toplevel();
1168         assert (toplevel);
1169         int pointer_x, pointer_y;
1170         Gdk::ModifierType mask;
1171
1172         (void) toplevel->get_window()->get_pointer (pointer_x, pointer_y, mask);
1173
1174         Duple tooltip_window_origin (pointer_x, pointer_y);
1175         
1176         /* convert to root window coordinates */
1177
1178         int win_x, win_y;
1179         dynamic_cast<Gtk::Window*>(toplevel)->get_position (win_x, win_y);
1180         
1181         tooltip_window_origin = tooltip_window_origin.translate (Duple (win_x, win_y));
1182
1183         /* we don't want the pointer to be inside the window when it is
1184          * displayed, because then we generate a leave/enter event pair when
1185          * the window is displayed then hidden - the enter event will
1186          * trigger a new tooltip timeout.
1187          *
1188          * So move the window right of the pointer position by just a enough
1189          * to get it away from the pointer.
1190          */
1191
1192         tooltip_window_origin.x += 20;
1193
1194         /* move the tooltip window into position */
1195
1196         tooltip_window->move (tooltip_window_origin.x, tooltip_window_origin.y);
1197
1198         /* ready to show */
1199
1200         tooltip_window->present ();
1201         
1202         /* called from a timeout handler, don't call it again */
1203
1204         return false;
1205 }
1206
1207 void
1208 GtkCanvas::hide_tooltip ()
1209 {
1210         /* hide it if its there */
1211
1212         if (tooltip_window) {
1213                 tooltip_window->hide ();
1214
1215                 // Delete the tooltip window so it'll get re-created
1216                 // (i.e. properly re-sized) on the next usage.
1217                 delete tooltip_window;
1218                 tooltip_window = NULL;
1219         }
1220 }
1221
1222 /** Create a GtkCanvaSViewport.
1223  *  @param hadj Adjustment to use for horizontal scrolling.
1224  *  @param vadj Adjustment to use for vertica scrolling.
1225  */
1226 GtkCanvasViewport::GtkCanvasViewport (Gtk::Adjustment& hadj, Gtk::Adjustment& vadj)
1227         : Alignment (0, 0, 1.0, 1.0)
1228         , hadjustment (hadj)
1229         , vadjustment (vadj)
1230 {
1231         add (_canvas);
1232
1233         hadj.signal_value_changed().connect (sigc::mem_fun (*this, &GtkCanvasViewport::scrolled));
1234         vadj.signal_value_changed().connect (sigc::mem_fun (*this, &GtkCanvasViewport::scrolled));
1235 }
1236
1237 void
1238 GtkCanvasViewport::scrolled ()
1239 {
1240         _canvas.scroll_to (hadjustment.get_value(), vadjustment.get_value());
1241         queue_draw ();
1242 }
1243
1244 /** Handler for when GTK asks us what minimum size we want.
1245  *  @param req Requsition to fill in.
1246  */
1247 void
1248 GtkCanvasViewport::on_size_request (Gtk::Requisition* req)
1249 {
1250         /* force the canvas to size itself */
1251         // _canvas.root()->bounding_box(); 
1252
1253         req->width = 16;
1254         req->height = 16;
1255 }
1256