c13386408bd3ce3ab336ba659af4762015c4ce8d
[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
30 #include "pbd/compose.h"
31 #include "pbd/stacktrace.h"
32
33 #include "canvas/canvas.h"
34 #include "canvas/debug.h"
35 #include "canvas/line.h"
36 #include "canvas/scroll_group.h"
37
38 using namespace std;
39 using namespace ArdourCanvas;
40
41 /** Construct a new Canvas */
42 Canvas::Canvas ()
43         : _root (this)
44 {
45         set_epoch ();
46 }
47
48 void
49 Canvas::scroll_to (Coord x, Coord y)
50 {
51         /* We do things this way because we do not want to recurse through
52            the canvas for every scroll. In the presence of large MIDI
53            tracks this means traversing item lists that include
54            thousands of items (notes).
55
56            This design limits us to moving only those items (groups, typically)
57            that should move in certain ways as we scroll. In other terms, it
58            becomes O(1) rather than O(N).
59         */
60
61         for (list<ScrollGroup*>::iterator i = scrollers.begin(); i != scrollers.end(); ++i) {
62                 (*i)->scroll_to (Duple (x, y));
63         }
64
65         pick_current_item (0); // no current mouse position 
66 }
67
68 void
69 Canvas::add_scroller (ScrollGroup& i)
70 {
71         scrollers.push_back (&i);
72 }
73
74 void
75 Canvas::zoomed ()
76 {
77         pick_current_item (0); // no current mouse position
78 }
79
80 /** Render an area of the canvas.
81  *  @param area Area in window coordinates.
82  *  @param context Cairo context to render to.
83  */
84 void
85 Canvas::render (Rect const & area, Cairo::RefPtr<Cairo::Context> const & context) const
86 {
87 #ifdef CANVAS_DEBUG
88         if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
89                 cerr << this << " RENDER: " << area << endl;
90                 //cerr << "CANVAS @ " << this << endl;
91                 //dump (cerr);
92                 //cerr << "-------------------------\n";
93         }
94 #endif
95
96         render_count = 0;
97         
98         boost::optional<Rect> root_bbox = _root.bounding_box();
99         if (!root_bbox) {
100                 /* the root has no bounding box, so there's nothing to render */
101                 return;
102         }
103
104         boost::optional<Rect> draw = root_bbox->intersection (area);
105         if (draw) {
106                 
107                 /* there's a common area between the root and the requested
108                    area, so render it.
109                 */
110
111                 _root.render (*draw, context);
112
113 #if 0
114                 // This transparently colors the rect being rendered, after it has been drawn.
115                 double r = (random() % 65536) /65536.0;
116                 double g = (random() % 65536) /65536.0;
117                 double b = (random() % 65536) /65536.0;
118                 context->rectangle (draw->x0, draw->y0, draw->x1 - draw->x0, draw->y1 - draw->y0);
119                 context->set_source_rgba (r, g, b, 0.25);
120                 context->fill ();
121 #endif
122
123         }
124
125 }
126
127 ostream&
128 operator<< (ostream& o, Canvas& c)
129 {
130         c.dump (o);
131         return o;
132 }
133
134 std::string
135 Canvas::indent() const
136
137         string s;
138
139         for (int n = 0; n < ArdourCanvas::dump_depth; ++n) {
140                 s += '\t';
141         }
142
143         return s;
144 }
145
146 std::string
147 Canvas::render_indent() const
148
149         string s;
150
151         for (int n = 0; n < ArdourCanvas::render_depth; ++n) {
152                 s += ' ';
153         }
154
155         return s;
156 }
157
158 void
159 Canvas::dump (ostream& o) const
160 {
161         dump_depth = 0;
162         _root.dump (o);
163 }       
164
165 /** Called when an item has been shown or hidden.
166  *  @param item Item that has been shown or hidden.
167  */
168 void
169 Canvas::item_shown_or_hidden (Item* item)
170 {
171         boost::optional<Rect> bbox = item->bounding_box ();
172         if (bbox) {
173                 if (item->item_to_window (*bbox).intersection (visible_area ())) {
174                         queue_draw_item_area (item, bbox.get ());
175                 }
176         }
177 }
178
179 /** Called when an item has a change to its visual properties
180  *  that do NOT affect its bounding box.
181  *  @param item Item that has been modified.
182  */
183 void
184 Canvas::item_visual_property_changed (Item* item)
185 {
186         boost::optional<Rect> bbox = item->bounding_box ();
187         if (bbox) {
188                 if (item->item_to_window (*bbox).intersection (visible_area ())) {
189                         queue_draw_item_area (item, bbox.get ());
190                 }
191         }
192 }
193
194 /** Called when an item has changed, but not moved.
195  *  @param item Item that has changed.
196  *  @param pre_change_bounding_box The bounding box of item before the change,
197  *  in the item's coordinates.
198  */
199 void
200 Canvas::item_changed (Item* item, boost::optional<Rect> pre_change_bounding_box)
201 {
202         
203         Rect window_bbox = visible_area ();
204
205         if (pre_change_bounding_box) {
206
207                 if (item->item_to_window (*pre_change_bounding_box).intersection (window_bbox)) {
208                         /* request a redraw of the item's old bounding box */
209                         queue_draw_item_area (item, pre_change_bounding_box.get ());
210                 }
211         }
212
213         boost::optional<Rect> post_change_bounding_box = item->bounding_box ();
214         if (post_change_bounding_box) {
215                 
216                 if (item->item_to_window (*post_change_bounding_box).intersection (window_bbox)) {
217                         /* request a redraw of the item's new bounding box */
218                         queue_draw_item_area (item, post_change_bounding_box.get ());
219                 }
220         }
221 }
222
223 Duple
224 Canvas::window_to_canvas (Duple const & d) const
225 {
226         /* Find the scroll group that covers d (a window coordinate). Scroll groups are only allowed
227          * as children of the root group, so we just scan its first level
228          * children and see what we can find.
229          */
230
231         std::list<Item*> const& root_children (_root.items());
232         ScrollGroup* sg = 0;
233
234         for (std::list<Item*>::const_iterator i = root_children.begin(); i != root_children.end(); ++i) {
235                 if (((sg = dynamic_cast<ScrollGroup*>(*i)) != 0) && sg->covers_window (d)) {
236                         break;
237                 }
238         }
239
240         if (sg) {
241                 return d.translate (sg->scroll_offset());
242         }
243
244         return d;
245 }
246
247 Duple
248 Canvas::canvas_to_window (Duple const & d, bool rounded) const
249 {
250         /* Find the scroll group that covers d (a canvas coordinate). Scroll groups are only allowed
251          * as children of the root group, so we just scan its first level
252          * children and see what we can find.
253          */
254
255         std::list<Item*> const& root_children (_root.items());
256         ScrollGroup* sg = 0;
257         Duple wd;
258
259         for (std::list<Item*>::const_iterator i = root_children.begin(); i != root_children.end(); ++i) {
260                 if (((sg = dynamic_cast<ScrollGroup*>(*i)) != 0) && sg->covers_canvas (d)) {
261                         break;
262                 }
263         }
264         
265
266         if (sg) {
267                 wd = d.translate (-sg->scroll_offset());
268         } else {
269                 wd = d;
270         }
271
272         /* Note that this intentionally almost always returns integer coordinates */
273
274         if (rounded) {
275                 wd.x = round (wd.x);
276                 wd.y = round (wd.y);
277         }
278
279         return wd;
280 }
281
282 /** Called when an item has moved.
283  *  @param item Item that has moved.
284  *  @param pre_change_parent_bounding_box The bounding box of the item before
285  *  the move, in its parent's coordinates.
286  */
287 void
288 Canvas::item_moved (Item* item, boost::optional<Rect> pre_change_parent_bounding_box)
289 {
290         if (pre_change_parent_bounding_box) {
291                 /* request a redraw of where the item used to be. The box has
292                  * to be in parent coordinate space since the bounding box of
293                  * an item does not change when moved. If we use
294                  * item->item_to_canvas() on the old bounding box, we will be
295
296                  * using the item's new position, and so will compute the wrong
297                  * invalidation area. If we use the parent (which has not
298                  * moved, then this will work.
299                  */
300                 queue_draw_item_area (item->parent(), pre_change_parent_bounding_box.get ());
301         }
302
303         boost::optional<Rect> post_change_bounding_box = item->bounding_box ();
304         if (post_change_bounding_box) {
305                 /* request a redraw of where the item now is */
306                 queue_draw_item_area (item, post_change_bounding_box.get ());
307         }
308 }
309
310 /** Request a redraw of a particular area in an item's coordinates.
311  *  @param item Item.
312  *  @param area Area to redraw in the item's coordinates.
313  */
314 void
315 Canvas::queue_draw_item_area (Item* item, Rect area)
316 {
317         request_redraw (item->item_to_window (area));
318 }
319
320 /** Construct a GtkCanvas */
321 GtkCanvas::GtkCanvas ()
322         : _current_item (0)
323         , _new_current_item (0)
324         , _grabbed_item (0)
325         , _focused_item (0)
326 {
327         /* these are the events we want to know about */
328         add_events (Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::POINTER_MOTION_MASK |
329                     Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK);
330 }
331
332 void
333 GtkCanvas::pick_current_item (int state)
334 {
335         int x;
336         int y;
337
338         /* this version of ::pick_current_item() is called after an item is
339          * added or removed, so we have no coordinates to work from as is the
340          * case with a motion event. Find out where the mouse is and use that.
341          */
342              
343         Glib::RefPtr<const Gdk::Window> pointer_window = Gdk::Display::get_default()->get_window_at_pointer (x, y);
344
345         if (pointer_window != get_window()) {
346                 return;
347         }
348
349         pick_current_item (Duple (x, y), state);
350 }
351
352 /** Given @param point (a position in window coordinates)
353  *  and mouse state @param state, check to see if _current_item
354  *  (which will be used to deliver events) should change.
355  */
356 void
357 GtkCanvas::pick_current_item (Duple const & point, int state)
358 {
359         /* we do not enter/leave items during a drag/grab */
360
361         if (_grabbed_item) {
362                 return;
363         }
364
365         /* find the items at the given window position */
366
367         vector<Item const *> items;
368         _root.add_items_at_point (point, items);
369
370         DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("%1 covers %2 items\n", point, items.size()));
371
372 #ifndef NDEBUG
373         if (DEBUG_ENABLED(PBD::DEBUG::CanvasEnterLeave)) {
374                 for (vector<Item const*>::const_iterator it = items.begin(); it != items.end(); ++it) {
375 #ifdef CANVAS_DEBUG
376                         std::cerr << "\tItem " << (*it)->whatami() << '/' << (*it)->name << std::endl;
377 #else
378                         std::cerr << "\tItem " << (*it)->whatami() << std::endl;
379 #endif
380                 }
381         }
382 #endif
383
384         /* put all items at point that are event-sensitive and visible and NOT
385            groups into within_items. Note that items is sorted from bottom to
386            top, but we're going to reverse that for within_items so that its
387            first item is the upper-most item that can be chosen as _current_item.
388         */
389         
390         vector<Item const *>::const_iterator i;
391         list<Item const *> within_items;
392
393         for (i = items.begin(); i != items.end(); ++i) {
394
395                 Item const * new_item = *i;
396
397                 /* We ignore invisible items, groups and items that ignore events */
398
399                 if (!new_item->visible() || new_item->ignore_events() || dynamic_cast<Group const *>(new_item) != 0) {
400                         continue;
401                 }
402                 
403                 within_items.push_front (new_item);
404         }
405
406         if (within_items.empty()) {
407
408                 /* no items at point, just send leave event below */
409                 _new_current_item = 0;
410
411         } else {
412
413                 if (within_items.front() == _current_item) {
414                         /* uppermost item at point is already _current_item */
415                         return;
416                 }
417         
418                 _new_current_item = const_cast<Item*> (within_items.front());
419         }
420
421         if (_new_current_item != _current_item) {
422                 deliver_enter_leave (point, state);
423         }
424 }
425
426 /** Deliver a series of enter & leave events based on the pointer position being at window
427  * coordinate @param point, and pointer @param state (modifier keys, etc)
428  */
429 void
430 GtkCanvas::deliver_enter_leave (Duple const & point, int state)
431 {
432         /* setup enter & leave event structures */
433
434         GdkEventCrossing enter_event;
435         enter_event.type = GDK_ENTER_NOTIFY;
436         enter_event.window = get_window()->gobj();
437         enter_event.send_event = 0;
438         enter_event.subwindow = 0;
439         enter_event.mode = GDK_CROSSING_NORMAL;
440         enter_event.focus = FALSE;
441         enter_event.state = state;
442
443         /* Events delivered to canvas items are expected to be in canvas
444          * coordinates but @param point is in window coordinates.
445          */
446         
447         Duple c = window_to_canvas (point);
448         enter_event.x = c.x;
449         enter_event.y = c.y;
450
451         GdkEventCrossing leave_event = enter_event;
452         leave_event.type = GDK_LEAVE_NOTIFY;
453
454         Item* i;
455         GdkNotifyType enter_detail;
456         GdkNotifyType leave_detail;
457         vector<Item*> items_to_leave_virtual;
458         vector<Item*> items_to_enter_virtual;
459
460         if (_new_current_item == 0) {
461
462                 leave_detail = GDK_NOTIFY_UNKNOWN;
463
464                 if (_current_item) {
465
466                         /* no current item, so also send virtual leave events to the
467                          * entire heirarchy for the current item
468                          */
469
470                         for (i = _current_item->parent(); i ; i = i->parent()) {
471                                 items_to_leave_virtual.push_back (i);
472                         }
473                 }
474
475         } else if (_current_item == 0) {
476
477                 enter_detail = GDK_NOTIFY_UNKNOWN;
478
479                 /* no current item, so also send virtual enter events to the
480                  * entire heirarchy for the new item 
481                  */
482
483                 for (i = _new_current_item->parent(); i ; i = i->parent()) {
484                         items_to_enter_virtual.push_back (i);
485                 }
486
487         } else if (_current_item->is_descendant_of (*_new_current_item)) {
488
489                 /* move from descendant to ancestor (X: "_current_item is an
490                  * inferior ("child") of _new_current_item") 
491                  *
492                  * Deliver "virtual" leave notifications to all items in the
493                  * heirarchy between current and new_current.
494                  */
495                 
496                 for (i = _current_item->parent(); i && i != _new_current_item; i = i->parent()) {
497                         items_to_leave_virtual.push_back (i);
498                 }
499
500                 enter_detail = GDK_NOTIFY_INFERIOR;
501                 leave_detail = GDK_NOTIFY_ANCESTOR;
502
503
504         } else if (_new_current_item->is_descendant_of (*_current_item)) {
505                 /* move from ancestor to descendant (X: "_new_current_item is
506                  * an inferior ("child") of _current_item")
507                  *
508                  * Deliver "virtual" enter notifications to all items in the
509                  * heirarchy between current and new_current.
510                  */
511
512                 for (i = _new_current_item->parent(); i && i != _current_item; i = i->parent()) {
513                         items_to_enter_virtual.push_back (i);
514                 }
515
516                 enter_detail = GDK_NOTIFY_ANCESTOR;
517                 leave_detail = GDK_NOTIFY_INFERIOR;
518
519         } else {
520
521                 Item const * common_ancestor = _current_item->closest_ancestor_with (*_new_current_item);
522
523                 /* deliver virtual leave events to everything between _current
524                  * and common_ancestor.
525                  */
526
527                 for (i = _current_item->parent(); i && i != common_ancestor; i = i->parent()) {
528                         items_to_leave_virtual.push_back (i);
529                 }
530
531                 /* deliver virtual enter events to everything between
532                  * _new_current and common_ancestor.
533                  */
534
535                 for (i = _new_current_item->parent(); i && i != common_ancestor; i = i->parent()) {
536                         items_to_enter_virtual.push_back (i);
537                 }
538
539                 enter_detail = GDK_NOTIFY_NONLINEAR;
540                 leave_detail = GDK_NOTIFY_NONLINEAR;
541         }
542         
543
544         if (_current_item && !_current_item->ignore_events ()) {
545                 leave_event.detail = leave_detail;
546                 _current_item->Event ((GdkEvent*)&leave_event);
547                 DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("LEAVE %1/%2\n", _current_item->whatami(), _current_item->name));
548         }
549
550         leave_event.detail = GDK_NOTIFY_VIRTUAL;
551         enter_event.detail = GDK_NOTIFY_VIRTUAL;
552
553         for (vector<Item*>::iterator it = items_to_leave_virtual.begin(); it != items_to_leave_virtual.end(); ++it) {
554                 if (!(*it)->ignore_events()) {
555                         DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("leave %1/%2\n", (*it)->whatami(), (*it)->name));
556                         (*it)->Event ((GdkEvent*)&leave_event);
557                 }
558         }
559
560         for (vector<Item*>::iterator it = items_to_enter_virtual.begin(); it != items_to_enter_virtual.end(); ++it) {
561                 if (!(*it)->ignore_events()) {
562                         DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("enter %1/%2\n", (*it)->whatami(), (*it)->name));
563                         (*it)->Event ((GdkEvent*)&enter_event);
564                         // std::cerr << "enter " << (*it)->whatami() << '/' << (*it)->name << std::endl;
565                 }
566         }
567
568         if (_new_current_item && !_new_current_item->ignore_events()) {
569                 enter_event.detail = enter_detail;
570                 DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("ENTER %1/%2\n", _new_current_item->whatami(), _new_current_item->name));
571                 _new_current_item->Event ((GdkEvent*)&enter_event);
572         }
573
574         _current_item = _new_current_item;
575 }
576
577
578 /** Deliver an event to the appropriate item; either the grabbed item, or
579  *  one of the items underneath the event.
580  *  @param point Position that the event has occurred at, in canvas coordinates.
581  *  @param event The event.
582  */
583 bool
584 GtkCanvas::deliver_event (GdkEvent* event)
585 {
586         /* Point in in canvas coordinate space */
587
588         const Item* event_item;
589
590         if (_grabbed_item) {
591                 /* we have a grabbed item, so everything gets sent there */
592                 DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("%1 %2 (%3) was grabbed, send event there\n",
593                                                                        _grabbed_item, _grabbed_item->whatami(), _grabbed_item->name));
594                 event_item = _grabbed_item;
595         } else {
596                 event_item = _current_item;
597         }
598
599         if (!event_item) {
600                 return false;
601         }
602
603         /* run through the items from child to parent, until one claims the event */
604
605         Item* item = const_cast<Item*> (event_item);
606         
607         while (item) {
608
609                 Item* parent = item->parent ();
610
611                 if (!item->ignore_events () && 
612                     item->Event (event)) {
613                         /* this item has just handled the event */
614                         DEBUG_TRACE (
615                                 PBD::DEBUG::CanvasEvents,
616                                 string_compose ("canvas event handled by %1 %2\n", item->whatami(), item->name.empty() ? "[unknown]" : item->name)
617                                 );
618                         
619                         return true;
620                 }
621                 
622                 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)));
623
624                 if ((item = parent) == 0) {
625                         break;
626                 }
627
628         }
629
630         return false;
631 }
632
633 /** Called when an item is being destroyed.
634  *  @param item Item being destroyed.
635  *  @param bounding_box Last known bounding box of the item.
636  */
637 void
638 GtkCanvas::item_going_away (Item* item, boost::optional<Rect> bounding_box)
639 {
640         if (bounding_box) {
641                 queue_draw_item_area (item, bounding_box.get ());
642         }
643         
644         if (_new_current_item == item) {
645                 _new_current_item = 0;
646         }
647
648         if (_grabbed_item == item) {
649                 _grabbed_item = 0;
650         }
651
652         if (_focused_item == item) {
653                 _focused_item = 0;
654         }
655
656         ScrollGroup* sg = dynamic_cast<ScrollGroup*>(item);
657         if (sg) {
658                 scrollers.remove (sg);
659         }
660
661         if (_current_item == item) {
662                 /* no need to send a leave event to this item, since it is going away 
663                  */
664                 _current_item = 0;
665                 pick_current_item (0); // no mouse state
666         }
667         
668 }
669
670 /** Handler for GDK expose events.
671  *  @param ev Event.
672  *  @return true if the event was handled.
673  */
674 bool
675 GtkCanvas::on_expose_event (GdkEventExpose* ev)
676 {
677         Cairo::RefPtr<Cairo::Context> cairo_context = get_window()->create_cairo_context ();
678         render (Rect (ev->area.x, ev->area.y, ev->area.x + ev->area.width, ev->area.y + ev->area.height), cairo_context);
679         return true;
680 }
681
682 /** @return Our Cairo context, or 0 if we don't have one */
683 Cairo::RefPtr<Cairo::Context>
684 GtkCanvas::context ()
685 {
686         Glib::RefPtr<Gdk::Window> w = get_window ();
687         if (!w) {
688                 return Cairo::RefPtr<Cairo::Context> ();
689         }
690
691         return w->create_cairo_context ();
692 }
693
694 /** Handler for GDK button press events.
695  *  @param ev Event.
696  *  @return true if the event was handled.
697  */
698 bool
699 GtkCanvas::on_button_press_event (GdkEventButton* ev)
700 {
701         /* translate event coordinates from window to canvas */
702
703         GdkEvent copy = *((GdkEvent*)ev);
704         Duple winpos = Duple (ev->x, ev->y);
705         Duple where = window_to_canvas (winpos);
706         
707         pick_current_item (winpos, ev->state);
708
709         copy.button.x = where.x;
710         copy.button.y = where.y;
711         
712         /* Coordinates in the event will be canvas coordinates, correctly adjusted
713            for scroll if this GtkCanvas is in a GtkCanvasViewport.
714         */
715
716         DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas button press @ %1, %2 => %3\n", ev->x, ev->y, where));
717         return deliver_event (reinterpret_cast<GdkEvent*>(&copy));
718 }
719
720 /** Handler for GDK button release events.
721  *  @param ev Event.
722  *  @return true if the event was handled.
723  */
724 bool
725 GtkCanvas::on_button_release_event (GdkEventButton* ev)
726 {       
727         /* translate event coordinates from window to canvas */
728
729         GdkEvent copy = *((GdkEvent*)ev);
730         Duple winpos = Duple (ev->x, ev->y);
731         Duple where = window_to_canvas (winpos);
732         
733         pick_current_item (winpos, ev->state);
734
735         copy.button.x = where.x;
736         copy.button.y = where.y;
737
738         /* Coordinates in the event will be canvas coordinates, correctly adjusted
739            for scroll if this GtkCanvas is in a GtkCanvasViewport.
740         */
741
742         DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas button release @ %1, %2 => %3\n", ev->x, ev->y, where));
743         return deliver_event (reinterpret_cast<GdkEvent*>(&copy));
744 }
745
746 /** Handler for GDK motion events.
747  *  @param ev Event.
748  *  @return true if the event was handled.
749  */
750 bool
751 GtkCanvas::on_motion_notify_event (GdkEventMotion* ev)
752 {
753         /* translate event coordinates from window to canvas */
754
755         GdkEvent copy = *((GdkEvent*)ev);
756         Duple point (ev->x, ev->y);
757         Duple where = window_to_canvas (point);
758
759         copy.motion.x = where.x;
760         copy.motion.y = where.y;
761
762         /* Coordinates in "copy" will be canvas coordinates, 
763         */
764
765         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));
766
767         pick_current_item (point, ev->state);
768
769         /* Now deliver the motion event.  It may seem a little inefficient
770            to recompute the items under the event, but the enter notify/leave
771            events may have deleted canvas items so it is important to
772            recompute the list in deliver_event.
773         */
774
775         return deliver_event (reinterpret_cast<GdkEvent*> (&copy));
776 }
777
778 bool
779 GtkCanvas::on_enter_notify_event (GdkEventCrossing* ev)
780 {
781         pick_current_item (Duple (ev->x, ev->y), ev->state);
782         return true;
783 }
784
785 bool
786 GtkCanvas::on_leave_notify_event (GdkEventCrossing* ev)
787 {
788         _new_current_item = 0;
789         deliver_enter_leave (Duple (ev->x, ev->y), ev->state);
790         return true;
791 }
792
793 /** Called to request a redraw of our canvas.
794  *  @param area Area to redraw, in window coordinates.
795  */
796 void
797 GtkCanvas::request_redraw (Rect const & request)
798 {
799         Rect real_area;
800
801         Coord const w = width ();
802         Coord const h = height ();
803
804         /* clamp area requested to actual visible window */
805
806         real_area.x0 = max (0.0, min (w, request.x0));
807         real_area.x1 = max (0.0, min (w, request.x1));
808         real_area.y0 = max (0.0, min (h, request.y0));
809         real_area.y1 = max (0.0, min (h, request.y1));
810
811         queue_draw_area (real_area.x0, real_area.y0, real_area.width(), real_area.height());
812 }
813
814 /** Called to request that we try to get a particular size for ourselves.
815  *  @param size Size to request, in pixels.
816  */
817 void
818 GtkCanvas::request_size (Duple size)
819 {
820         Duple req = size;
821
822         if (req.x > INT_MAX) {
823                 req.x = INT_MAX;
824         }
825
826         if (req.y > INT_MAX) {
827                 req.y = INT_MAX;
828         }
829
830         set_size_request (req.x, req.y);
831 }
832
833 /** `Grab' an item, so that all events are sent to that item until it is `ungrabbed'.
834  *  This is typically used for dragging items around, so that they are grabbed during
835  *  the drag.
836  *  @param item Item to grab.
837  */
838 void
839 GtkCanvas::grab (Item* item)
840 {
841         /* XXX: should this be doing gdk_pointer_grab? */
842         _grabbed_item = item;
843 }
844
845
846 /** `Ungrab' any item that was previously grabbed */
847 void
848 GtkCanvas::ungrab ()
849 {
850         /* XXX: should this be doing gdk_pointer_ungrab? */
851         _grabbed_item = 0;
852 }
853
854 /** Set keyboard focus on an item, so that all keyboard events are sent to that item until the focus
855  *  moves elsewhere.
856  *  @param item Item to grab.
857  */
858 void
859 GtkCanvas::focus (Item* item)
860 {
861         _focused_item = item;
862 }
863
864 void
865 GtkCanvas::unfocus (Item* item)
866 {
867         if (item == _focused_item) {
868                 _focused_item = 0;
869         }
870 }
871
872 /** @return The visible area of the canvas, in window coordinates */
873 Rect
874 GtkCanvas::visible_area () const
875 {
876         return Rect (0, 0, get_allocation().get_width (), get_allocation().get_height ());
877 }
878
879 Coord
880 GtkCanvas::width() const
881 {
882         return get_allocation().get_width();
883 }
884
885 Coord
886 GtkCanvas::height() const
887 {
888         return get_allocation().get_height();
889 }
890
891 /** Create a GtkCanvaSViewport.
892  *  @param hadj Adjustment to use for horizontal scrolling.
893  *  @param vadj Adjustment to use for vertica scrolling.
894  */
895 GtkCanvasViewport::GtkCanvasViewport (Gtk::Adjustment& hadj, Gtk::Adjustment& vadj)
896         : Alignment (0, 0, 1.0, 1.0)
897         , hadjustment (hadj)
898         , vadjustment (vadj)
899 {
900         add (_canvas);
901
902         hadj.signal_value_changed().connect (sigc::mem_fun (*this, &GtkCanvasViewport::scrolled));
903         vadj.signal_value_changed().connect (sigc::mem_fun (*this, &GtkCanvasViewport::scrolled));
904 }
905
906 void
907 GtkCanvasViewport::scrolled ()
908 {
909         _canvas.scroll_to (hadjustment.get_value(), vadjustment.get_value());
910         queue_draw ();
911 }
912
913 /** Handler for when GTK asks us what minimum size we want.
914  *  @param req Requsition to fill in.
915  */
916 void
917 GtkCanvasViewport::on_size_request (Gtk::Requisition* req)
918 {
919         /* force the canvas to size itself */
920         // _canvas.root()->bounding_box(); 
921
922         req->width = 16;
923         req->height = 16;
924 }
925