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