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