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