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