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