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