2 Copyright (C) 2011 Paul Davis
3 Author: Carl Hetherington <cth@carlh.net>
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.
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.
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.
21 /** @file canvas/canvas.cc
22 * @brief Implementation of the main canvas classes.
27 #include <gtkmm/adjustment.h>
28 #include <gtkmm/label.h>
30 #include "pbd/compose.h"
31 #include "pbd/stacktrace.h"
33 #include "canvas/canvas.h"
34 #include "canvas/debug.h"
35 #include "canvas/line.h"
36 #include "canvas/scroll_group.h"
39 using namespace ArdourCanvas;
41 /** Construct a new Canvas */
49 Canvas::scroll_to (Coord x, Coord y)
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).
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).
61 for (list<ScrollGroup*>::iterator i = scrollers.begin(); i != scrollers.end(); ++i) {
62 (*i)->scroll_to (Duple (x, y));
65 pick_current_item (0); // no current mouse position
69 Canvas::add_scroller (ScrollGroup& i)
71 scrollers.push_back (&i);
77 pick_current_item (0); // no current mouse position
80 /** Render an area of the canvas.
81 * @param area Area in window coordinates.
82 * @param context Cairo context to render to.
85 Canvas::render (Rect const & area, Cairo::RefPtr<Cairo::Context> const & context) const
88 if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
89 cerr << this << " RENDER: " << area << endl;
90 //cerr << "CANVAS @ " << this << endl;
92 //cerr << "-------------------------\n";
98 boost::optional<Rect> root_bbox = _root.bounding_box();
100 /* the root has no bounding box, so there's nothing to render */
104 boost::optional<Rect> draw = root_bbox->intersection (area);
107 /* there's a common area between the root and the requested
111 _root.render (*draw, context);
114 if (getenv ("CANVAS_HARLEQUIN_DEBUGGING")) {
115 // This transparently colors the rect being rendered, after it has been drawn.
116 double r = (random() % 65536) /65536.0;
117 double g = (random() % 65536) /65536.0;
118 double b = (random() % 65536) /65536.0;
119 context->rectangle (draw->x0, draw->y0, draw->x1 - draw->x0, draw->y1 - draw->y0);
120 context->set_source_rgba (r, g, b, 0.25);
129 operator<< (ostream& o, Canvas& c)
136 Canvas::indent() const
140 for (int n = 0; n < ArdourCanvas::dump_depth; ++n) {
148 Canvas::render_indent() const
152 for (int n = 0; n < ArdourCanvas::render_depth; ++n) {
160 Canvas::dump (ostream& o) const
166 /** Called when an item has been shown or hidden.
167 * @param item Item that has been shown or hidden.
170 Canvas::item_shown_or_hidden (Item* item)
172 boost::optional<Rect> bbox = item->bounding_box ();
174 if (item->item_to_window (*bbox).intersection (visible_area ())) {
175 queue_draw_item_area (item, bbox.get ());
180 /** Called when an item has a change to its visual properties
181 * that do NOT affect its bounding box.
182 * @param item Item that has been modified.
185 Canvas::item_visual_property_changed (Item* item)
187 boost::optional<Rect> bbox = item->bounding_box ();
189 if (item->item_to_window (*bbox).intersection (visible_area ())) {
190 queue_draw_item_area (item, bbox.get ());
195 /** Called when an item has changed, but not moved.
196 * @param item Item that has changed.
197 * @param pre_change_bounding_box The bounding box of item before the change,
198 * in the item's coordinates.
201 Canvas::item_changed (Item* item, boost::optional<Rect> pre_change_bounding_box)
204 Rect window_bbox = visible_area ();
206 if (pre_change_bounding_box) {
208 if (item->item_to_window (*pre_change_bounding_box).intersection (window_bbox)) {
209 /* request a redraw of the item's old bounding box */
210 queue_draw_item_area (item, pre_change_bounding_box.get ());
214 boost::optional<Rect> post_change_bounding_box = item->bounding_box ();
215 if (post_change_bounding_box) {
217 if (item->item_to_window (*post_change_bounding_box).intersection (window_bbox)) {
218 /* request a redraw of the item's new bounding box */
219 queue_draw_item_area (item, post_change_bounding_box.get ());
225 Canvas::window_to_canvas (Duple const & d) const
227 /* Find the scroll group that covers d (a window coordinate). Scroll groups are only allowed
228 * as children of the root group, so we just scan its first level
229 * children and see what we can find.
232 std::list<Item*> const& root_children (_root.items());
235 /* if the coordinates are negative, clamp to zero and find the item
236 * that covers that "edge" position.
241 if (in_window.x < 0) {
244 if (in_window.y < 0) {
248 for (std::list<Item*>::const_iterator i = root_children.begin(); i != root_children.end(); ++i) {
249 if (((sg = dynamic_cast<ScrollGroup*>(*i)) != 0) && sg->covers_window (in_window)) {
255 return d.translate (sg->scroll_offset());
262 Canvas::canvas_to_window (Duple const & d, bool rounded) const
264 /* Find the scroll group that covers d (a canvas coordinate). Scroll groups are only allowed
265 * as children of the root group, so we just scan its first level
266 * children and see what we can find.
269 std::list<Item*> const& root_children (_root.items());
273 for (std::list<Item*>::const_iterator i = root_children.begin(); i != root_children.end(); ++i) {
274 if (((sg = dynamic_cast<ScrollGroup*>(*i)) != 0) && sg->covers_canvas (d)) {
281 wd = d.translate (-sg->scroll_offset());
286 /* Note that this intentionally almost always returns integer coordinates */
296 /** Called when an item has moved.
297 * @param item Item that has moved.
298 * @param pre_change_parent_bounding_box The bounding box of the item before
299 * the move, in its parent's coordinates.
302 Canvas::item_moved (Item* item, boost::optional<Rect> pre_change_parent_bounding_box)
304 if (pre_change_parent_bounding_box) {
305 /* request a redraw of where the item used to be. The box has
306 * to be in parent coordinate space since the bounding box of
307 * an item does not change when moved. If we use
308 * item->item_to_canvas() on the old bounding box, we will be
310 * using the item's new position, and so will compute the wrong
311 * invalidation area. If we use the parent (which has not
312 * moved, then this will work.
314 queue_draw_item_area (item->parent(), pre_change_parent_bounding_box.get ());
317 boost::optional<Rect> post_change_bounding_box = item->bounding_box ();
318 if (post_change_bounding_box) {
319 /* request a redraw of where the item now is */
320 queue_draw_item_area (item, post_change_bounding_box.get ());
324 /** Request a redraw of a particular area in an item's coordinates.
326 * @param area Area to redraw in the item's coordinates.
329 Canvas::queue_draw_item_area (Item* item, Rect area)
331 request_redraw (item->item_to_window (area));
335 GtkCanvas::re_enter ()
337 DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, "re-enter canvas by request\n");
339 pick_current_item (0);
342 /** Construct a GtkCanvas */
343 GtkCanvas::GtkCanvas ()
345 , _new_current_item (0)
349 /* these are the events we want to know about */
350 add_events (Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::POINTER_MOTION_MASK |
351 Gdk::SCROLL_MASK | Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK);
355 GtkCanvas::pick_current_item (int state)
360 /* this version of ::pick_current_item() is called after an item is
361 * added or removed, so we have no coordinates to work from as is the
362 * case with a motion event. Find out where the mouse is and use that.
365 Glib::RefPtr<const Gdk::Window> pointer_window = Gdk::Display::get_default()->get_window_at_pointer (x, y);
367 if (pointer_window != get_window()) {
371 pick_current_item (Duple (x, y), state);
374 /** Given @param point (a position in window coordinates)
375 * and mouse state @param state, check to see if _current_item
376 * (which will be used to deliver events) should change.
379 GtkCanvas::pick_current_item (Duple const & point, int state)
381 /* we do not enter/leave items during a drag/grab */
387 /* find the items at the given window position */
389 vector<Item const *> items;
390 _root.add_items_at_point (point, items);
392 DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("%1 covers %2 items\n", point, items.size()));
395 if (DEBUG_ENABLED(PBD::DEBUG::CanvasEnterLeave)) {
396 for (vector<Item const*>::const_iterator it = items.begin(); it != items.end(); ++it) {
398 std::cerr << "\tItem " << (*it)->whatami() << '/' << (*it)->name << " ignore events ? " << (*it)->ignore_events() << " vis ? " << (*it)->visible() << std::endl;
400 std::cerr << "\tItem " << (*it)->whatami() << '/' << " ignore events ? " << (*it)->ignore_events() << " vis ? " << (*it)->visible() << std::endl;
406 /* put all items at point that are event-sensitive and visible and NOT
407 groups into within_items. Note that items is sorted from bottom to
408 top, but we're going to reverse that for within_items so that its
409 first item is the upper-most item that can be chosen as _current_item.
412 vector<Item const *>::const_iterator i;
413 list<Item const *> within_items;
415 for (i = items.begin(); i != items.end(); ++i) {
417 Item const * possible_item = *i;
419 /* We ignore invisible items, containers and items that ignore events */
421 if (!possible_item->visible() || possible_item->ignore_events() || dynamic_cast<ArdourCanvas::Container const *>(possible_item) != 0) {
424 within_items.push_front (possible_item);
427 DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("after filtering insensitive + containers, we have %1 items\n", within_items.size()));
429 if (within_items.empty()) {
431 /* no items at point, just send leave event below */
432 _new_current_item = 0;
436 if (within_items.front() == _current_item) {
437 /* uppermost item at point is already _current_item */
438 DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("CURRENT ITEM %1/%2\n", _new_current_item->whatami(), _current_item->name));
442 _new_current_item = const_cast<Item*> (within_items.front());
445 if (_new_current_item != _current_item) {
446 deliver_enter_leave (point, state);
450 DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("CURRENT ITEM %1/%2\n", _new_current_item->whatami(), _current_item->name));
452 DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, "--- no current item\n");
457 /** Deliver a series of enter & leave events based on the pointer position being at window
458 * coordinate @param point, and pointer @param state (modifier keys, etc)
461 GtkCanvas::deliver_enter_leave (Duple const & point, int state)
463 /* setup enter & leave event structures */
465 Glib::RefPtr<Gdk::Window> win = get_window();
471 GdkEventCrossing enter_event;
472 enter_event.type = GDK_ENTER_NOTIFY;
473 enter_event.window = win->gobj();
474 enter_event.send_event = 0;
475 enter_event.subwindow = 0;
476 enter_event.mode = GDK_CROSSING_NORMAL;
477 enter_event.focus = FALSE;
478 enter_event.state = state;
480 /* Events delivered to canvas items are expected to be in canvas
481 * coordinates but @param point is in window coordinates.
484 Duple c = window_to_canvas (point);
488 GdkEventCrossing leave_event = enter_event;
489 leave_event.type = GDK_LEAVE_NOTIFY;
492 GdkNotifyType enter_detail;
493 GdkNotifyType leave_detail;
494 vector<Item*> items_to_leave_virtual;
495 vector<Item*> items_to_enter_virtual;
497 if (_new_current_item == 0) {
499 leave_detail = GDK_NOTIFY_UNKNOWN;
503 /* no current item, so also send virtual leave events to the
504 * entire heirarchy for the current item
507 for (i = _current_item->parent(); i ; i = i->parent()) {
508 items_to_leave_virtual.push_back (i);
512 } else if (_current_item == 0) {
514 enter_detail = GDK_NOTIFY_UNKNOWN;
516 /* no current item, so also send virtual enter events to the
517 * entire heirarchy for the new item
520 for (i = _new_current_item->parent(); i ; i = i->parent()) {
521 items_to_enter_virtual.push_back (i);
524 } else if (_current_item->is_descendant_of (*_new_current_item)) {
526 /* move from descendant to ancestor (X: "_current_item is an
527 * inferior ("child") of _new_current_item")
529 * Deliver "virtual" leave notifications to all items in the
530 * heirarchy between current and new_current.
533 for (i = _current_item->parent(); i && i != _new_current_item; i = i->parent()) {
534 items_to_leave_virtual.push_back (i);
537 enter_detail = GDK_NOTIFY_INFERIOR;
538 leave_detail = GDK_NOTIFY_ANCESTOR;
540 } else if (_new_current_item->is_descendant_of (*_current_item)) {
541 /* move from ancestor to descendant (X: "_new_current_item is
542 * an inferior ("child") of _current_item")
544 * Deliver "virtual" enter notifications to all items in the
545 * heirarchy between current and new_current.
548 for (i = _new_current_item->parent(); i && i != _current_item; i = i->parent()) {
549 items_to_enter_virtual.push_back (i);
552 enter_detail = GDK_NOTIFY_ANCESTOR;
553 leave_detail = GDK_NOTIFY_INFERIOR;
557 Item const * common_ancestor = _current_item->closest_ancestor_with (*_new_current_item);
559 /* deliver virtual leave events to everything between _current
560 * and common_ancestor.
563 for (i = _current_item->parent(); i && i != common_ancestor; i = i->parent()) {
564 items_to_leave_virtual.push_back (i);
567 /* deliver virtual enter events to everything between
568 * _new_current and common_ancestor.
571 for (i = _new_current_item->parent(); i && i != common_ancestor; i = i->parent()) {
572 items_to_enter_virtual.push_back (i);
575 enter_detail = GDK_NOTIFY_NONLINEAR;
576 leave_detail = GDK_NOTIFY_NONLINEAR;
580 if (_current_item && !_current_item->ignore_events ()) {
581 leave_event.detail = leave_detail;
582 _current_item->Event ((GdkEvent*)&leave_event);
583 DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("LEAVE %1/%2\n", _current_item->whatami(), _current_item->name));
586 leave_event.detail = GDK_NOTIFY_VIRTUAL;
587 enter_event.detail = GDK_NOTIFY_VIRTUAL;
589 for (vector<Item*>::iterator it = items_to_leave_virtual.begin(); it != items_to_leave_virtual.end(); ++it) {
590 if (!(*it)->ignore_events()) {
591 DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("leave %1/%2\n", (*it)->whatami(), (*it)->name));
592 (*it)->Event ((GdkEvent*)&leave_event);
596 for (vector<Item*>::iterator it = items_to_enter_virtual.begin(); it != items_to_enter_virtual.end(); ++it) {
597 if (!(*it)->ignore_events()) {
598 DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("enter %1/%2\n", (*it)->whatami(), (*it)->name));
599 (*it)->Event ((GdkEvent*)&enter_event);
600 // std::cerr << "enter " << (*it)->whatami() << '/' << (*it)->name << std::endl;
604 if (_new_current_item && !_new_current_item->ignore_events()) {
605 enter_event.detail = enter_detail;
606 DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("ENTER %1/%2\n", _new_current_item->whatami(), _new_current_item->name));
607 _new_current_item->Event ((GdkEvent*)&enter_event);
610 _current_item = _new_current_item;
614 /** Deliver an event to the appropriate item; either the grabbed item, or
615 * one of the items underneath the event.
616 * @param point Position that the event has occurred at, in canvas coordinates.
617 * @param event The event.
620 GtkCanvas::deliver_event (GdkEvent* event)
622 /* Point in in canvas coordinate space */
624 const Item* event_item;
627 /* we have a grabbed item, so everything gets sent there */
628 DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("%1 %2 (%3) was grabbed, send event there\n",
629 _grabbed_item, _grabbed_item->whatami(), _grabbed_item->name));
630 event_item = _grabbed_item;
632 event_item = _current_item;
639 /* run through the items from child to parent, until one claims the event */
641 Item* item = const_cast<Item*> (event_item);
645 Item* parent = item->parent ();
647 if (!item->ignore_events () &&
648 item->Event (event)) {
649 /* this item has just handled the event */
651 PBD::DEBUG::CanvasEvents,
652 string_compose ("canvas event handled by %1 %2\n", item->whatami(), item->name.empty() ? "[unknown]" : item->name)
658 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)));
660 if ((item = parent) == 0) {
669 /** Called when an item is being destroyed.
670 * @param item Item being destroyed.
671 * @param bounding_box Last known bounding box of the item.
674 GtkCanvas::item_going_away (Item* item, boost::optional<Rect> bounding_box)
677 queue_draw_item_area (item, bounding_box.get ());
680 if (_new_current_item == item) {
681 _new_current_item = 0;
684 if (_grabbed_item == item) {
688 if (_focused_item == item) {
692 ScrollGroup* sg = dynamic_cast<ScrollGroup*>(item);
694 scrollers.remove (sg);
697 if (_current_item == item) {
698 /* no need to send a leave event to this item, since it is going away
701 pick_current_item (0); // no mouse state
706 /** Handler for GDK expose events.
708 * @return true if the event was handled.
711 GtkCanvas::on_expose_event (GdkEventExpose* ev)
713 Cairo::RefPtr<Cairo::Context> cairo_context = get_window()->create_cairo_context ();
714 render (Rect (ev->area.x, ev->area.y, ev->area.x + ev->area.width, ev->area.y + ev->area.height), cairo_context);
718 /** @return Our Cairo context, or 0 if we don't have one */
719 Cairo::RefPtr<Cairo::Context>
720 GtkCanvas::context ()
722 Glib::RefPtr<Gdk::Window> w = get_window ();
724 return Cairo::RefPtr<Cairo::Context> ();
727 return w->create_cairo_context ();
730 /** Handler for GDK scroll events.
732 * @return true if the event was handled.
735 GtkCanvas::on_scroll_event (GdkEventScroll* ev)
737 /* translate event coordinates from window to canvas */
739 GdkEvent copy = *((GdkEvent*)ev);
740 Duple winpos = Duple (ev->x, ev->y);
741 Duple where = window_to_canvas (winpos);
743 pick_current_item (winpos, ev->state);
745 copy.button.x = where.x;
746 copy.button.y = where.y;
748 /* Coordinates in the event will be canvas coordinates, correctly adjusted
749 for scroll if this GtkCanvas is in a GtkCanvasViewport.
752 DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas scroll @ %1, %2 => %3\n", ev->x, ev->y, where));
753 return deliver_event (reinterpret_cast<GdkEvent*>(©));
756 /** Handler for GDK button press events.
758 * @return true if the event was handled.
761 GtkCanvas::on_button_press_event (GdkEventButton* ev)
763 /* translate event coordinates from window to canvas */
765 GdkEvent copy = *((GdkEvent*)ev);
766 Duple winpos = Duple (ev->x, ev->y);
767 Duple where = window_to_canvas (winpos);
769 pick_current_item (winpos, ev->state);
771 copy.button.x = where.x;
772 copy.button.y = where.y;
774 /* Coordinates in the event will be canvas coordinates, correctly adjusted
775 for scroll if this GtkCanvas is in a GtkCanvasViewport.
778 DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas button press @ %1, %2 => %3\n", ev->x, ev->y, where));
779 return deliver_event (reinterpret_cast<GdkEvent*>(©));
782 /** Handler for GDK button release events.
784 * @return true if the event was handled.
787 GtkCanvas::on_button_release_event (GdkEventButton* ev)
789 /* translate event coordinates from window to canvas */
791 GdkEvent copy = *((GdkEvent*)ev);
792 Duple winpos = Duple (ev->x, ev->y);
793 Duple where = window_to_canvas (winpos);
795 pick_current_item (winpos, ev->state);
797 copy.button.x = where.x;
798 copy.button.y = where.y;
800 /* Coordinates in the event will be canvas coordinates, correctly adjusted
801 for scroll if this GtkCanvas is in a GtkCanvasViewport.
804 DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas button release @ %1, %2 => %3\n", ev->x, ev->y, where));
805 return deliver_event (reinterpret_cast<GdkEvent*>(©));
809 GtkCanvas::get_mouse_position (Duple& winpos) const
813 Gdk::ModifierType mask;
814 Glib::RefPtr<Gdk::Window> self = Glib::RefPtr<Gdk::Window>::cast_const (get_window ());
817 std::cerr << " no self window\n";
818 winpos = Duple (0, 0);
822 Glib::RefPtr<Gdk::Window> win = self->get_pointer (x, y, mask);
830 /** Handler for GDK motion events.
832 * @return true if the event was handled.
835 GtkCanvas::on_motion_notify_event (GdkEventMotion* ev)
837 /* translate event coordinates from window to canvas */
839 GdkEvent copy = *((GdkEvent*)ev);
840 Duple point (ev->x, ev->y);
841 Duple where = window_to_canvas (point);
843 copy.motion.x = where.x;
844 copy.motion.y = where.y;
846 /* Coordinates in "copy" will be canvas coordinates,
849 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));
851 MouseMotion (point); /* EMIT SIGNAL */
853 pick_current_item (point, ev->state);
855 /* Now deliver the motion event. It may seem a little inefficient
856 to recompute the items under the event, but the enter notify/leave
857 events may have deleted canvas items so it is important to
858 recompute the list in deliver_event.
861 return deliver_event (reinterpret_cast<GdkEvent*> (©));
865 GtkCanvas::on_enter_notify_event (GdkEventCrossing* ev)
867 pick_current_item (Duple (ev->x, ev->y), ev->state);
872 GtkCanvas::on_leave_notify_event (GdkEventCrossing* ev)
874 _new_current_item = 0;
875 deliver_enter_leave (Duple (ev->x, ev->y), ev->state);
879 /** Called to request a redraw of our canvas.
880 * @param area Area to redraw, in window coordinates.
883 GtkCanvas::request_redraw (Rect const & request)
887 Coord const w = width ();
888 Coord const h = height ();
890 /* clamp area requested to actual visible window */
892 real_area.x0 = max (0.0, min (w, request.x0));
893 real_area.x1 = max (0.0, min (w, request.x1));
894 real_area.y0 = max (0.0, min (h, request.y0));
895 real_area.y1 = max (0.0, min (h, request.y1));
897 queue_draw_area (real_area.x0, real_area.y0, real_area.width(), real_area.height());
900 /** Called to request that we try to get a particular size for ourselves.
901 * @param size Size to request, in pixels.
904 GtkCanvas::request_size (Duple size)
908 if (req.x > INT_MAX) {
912 if (req.y > INT_MAX) {
916 set_size_request (req.x, req.y);
919 /** `Grab' an item, so that all events are sent to that item until it is `ungrabbed'.
920 * This is typically used for dragging items around, so that they are grabbed during
922 * @param item Item to grab.
925 GtkCanvas::grab (Item* item)
927 /* XXX: should this be doing gdk_pointer_grab? */
928 _grabbed_item = item;
932 /** `Ungrab' any item that was previously grabbed */
936 /* XXX: should this be doing gdk_pointer_ungrab? */
940 /** Set keyboard focus on an item, so that all keyboard events are sent to that item until the focus
942 * @param item Item to grab.
945 GtkCanvas::focus (Item* item)
947 _focused_item = item;
951 GtkCanvas::unfocus (Item* item)
953 if (item == _focused_item) {
958 /** @return The visible area of the canvas, in window coordinates */
960 GtkCanvas::visible_area () const
962 return Rect (0, 0, get_allocation().get_width (), get_allocation().get_height ());
966 GtkCanvas::width() const
968 return get_allocation().get_width();
972 GtkCanvas::height() const
974 return get_allocation().get_height();
977 /** Create a GtkCanvaSViewport.
978 * @param hadj Adjustment to use for horizontal scrolling.
979 * @param vadj Adjustment to use for vertica scrolling.
981 GtkCanvasViewport::GtkCanvasViewport (Gtk::Adjustment& hadj, Gtk::Adjustment& vadj)
982 : Alignment (0, 0, 1.0, 1.0)
988 hadj.signal_value_changed().connect (sigc::mem_fun (*this, &GtkCanvasViewport::scrolled));
989 vadj.signal_value_changed().connect (sigc::mem_fun (*this, &GtkCanvasViewport::scrolled));
993 GtkCanvasViewport::scrolled ()
995 _canvas.scroll_to (hadjustment.get_value(), vadjustment.get_value());
999 /** Handler for when GTK asks us what minimum size we want.
1000 * @param req Requsition to fill in.
1003 GtkCanvasViewport::on_size_request (Gtk::Requisition* req)
1005 /* force the canvas to size itself */
1006 // _canvas.root()->bounding_box();