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