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