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