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