Updated Spanish translation
[evolution.git] / libgnomecanvas / gnome-canvas.c
blobfecce972b2cdece4652226d9f423b57859a9861f
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: 8; c-basic-offset: 8 -*- */
2 /*
3 * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
4 * All rights reserved.
6 * This file is part of the Gnome Library.
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * published by the Free Software Foundation; either the
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * for more details.
17 * You should have received a copy of the GNU Lesser General Public License
18 * License along with the Gnome Library; see the file COPYING.LIB. If not,
21 @NOTATION@
24 * GnomeCanvas widget - Tk-like canvas widget for Gnome
26 * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas
27 * widget. Tk is copyrighted by the Regents of the University of California,
28 * Sun Microsystems, and other parties.
31 * Authors: Federico Mena <federico@nuclecu.unam.mx>
32 * Raph Levien <raph@gimp.org>
36 * TO-DO list for the canvas:
38 * - Allow to specify whether GnomeCanvasImage sizes are in units or pixels
39 * (scale or don't scale).
41 * - Implement a flag for gnome_canvas_item_reparent() that tells the function
42 * to keep the item visually in the same place, that is, to keep it in the
43 * same place with respect to the canvas origin.
45 * - GC put functions for items.
47 * - Widget item (finish it).
49 * - GList *
50 * gnome_canvas_gimme_all_items_contained_in_this_area (GnomeCanvas *canvas,
51 * Rectangle area);
53 * - Retrofit all the primitive items with microtile support.
55 * - Curve support for line item.
57 * - Arc item (Havoc has it; to be integrated in GnomeCanvasEllipse).
59 * - Sane font handling API.
61 * - Get_arg methods for items:
62 * - How to fetch the outline width and know whether it is in pixels or units?
66 * Raph's TODO list for the antialiased canvas integration:
68 * - ::point() method for text item not accurate when affine transformed.
70 * - Clip rectangle not implemented in aa renderer for text item.
72 * - Clip paths only partially implemented.
74 * - Add more image loading techniques to work around imlib deficiencies.
77 #ifdef HAVE_CONFIG_H
78 #include <config.h>
79 #endif
81 #include <math.h>
82 #include <string.h>
83 #include <stdio.h>
84 #include <gdk/gdkprivate.h>
85 #include <gtk/gtk.h>
86 #include <cairo-gobject.h>
87 #include "gailcanvas.h"
88 #include "gnome-canvas.h"
89 #include "gnome-canvas-i18n.h"
90 #include "gnome-canvas-util.h"
92 /* We must run our idle update handler *before* GDK wants to redraw. */
93 #define CANVAS_IDLE_PRIORITY (GDK_PRIORITY_REDRAW - 5)
95 static void gnome_canvas_request_update (GnomeCanvas *canvas);
96 static void group_add (GnomeCanvasGroup *group,
97 GnomeCanvasItem *item);
98 static void group_remove (GnomeCanvasGroup *group,
99 GnomeCanvasItem *item);
100 static void add_idle (GnomeCanvas *canvas);
102 /*** GnomeCanvasItem ***/
104 /* Some convenience stuff */
105 #define GCI_UPDATE_MASK \
106 (GNOME_CANVAS_UPDATE_REQUESTED | \
107 GNOME_CANVAS_UPDATE_AFFINE | \
108 GNOME_CANVAS_UPDATE_CLIP | \
109 GNOME_CANVAS_UPDATE_VISIBILITY)
110 #define GCI_EPSILON 1e-18
111 #define GCI_PRINT_MATRIX(s,a) \
112 g_print ("%s %g %g %g %g %g %g\n", \
113 s, (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5])
115 enum {
116 ITEM_PROP_0,
117 ITEM_PROP_PARENT
120 enum {
121 ITEM_EVENT,
122 ITEM_LAST_SIGNAL
125 static gint emit_event (GnomeCanvas *canvas, GdkEvent *event);
127 static guint item_signals[ITEM_LAST_SIGNAL];
129 G_DEFINE_TYPE (
130 GnomeCanvasItem,
131 gnome_canvas_item,
132 G_TYPE_INITIALLY_UNOWNED)
134 /* Object initialization function for GnomeCanvasItem */
135 static void
136 gnome_canvas_item_init (GnomeCanvasItem *item)
138 item->flags |= GNOME_CANVAS_ITEM_VISIBLE;
140 cairo_matrix_init_identity (&item->matrix);
144 * gnome_canvas_item_new:
145 * @parent: The parent group for the new item.
146 * @type: The object type of the item.
147 * @first_arg_name: A list of object argument name/value pairs, NULL-terminated,
148 * used to configure the item. For example, "fill_color", "black",
149 * "width_units", 5.0, NULL.
150 * @Varargs:
152 * Creates a new canvas item with @parent as its parent group. The item is
153 * created at the top of its parent's stack, and starts up as visible. The item
154 * is of the specified @type, for example, it can be
155 * gnome_canvas_rect_get_type(). The list of object arguments/value pairs is
156 * used to configure the item. If you need to pass construct time parameters, you
157 * should use g_object_new() to pass the parameters and
158 * gnome_canvas_item_construct() to set up the canvas item.
160 * Return value: The newly-created item.
162 GnomeCanvasItem *
163 gnome_canvas_item_new (GnomeCanvasGroup *parent,
164 GType type,
165 const gchar *first_arg_name, ...)
167 GnomeCanvasItem *item;
168 va_list args;
170 g_return_val_if_fail (GNOME_IS_CANVAS_GROUP (parent), NULL);
171 g_return_val_if_fail (g_type_is_a (type, gnome_canvas_item_get_type ()), NULL);
173 item = GNOME_CANVAS_ITEM (g_object_new (type, NULL));
175 va_start (args, first_arg_name);
176 gnome_canvas_item_construct (item, parent, first_arg_name, args);
177 va_end (args);
179 return item;
182 /* Performs post-creation operations on a canvas item (adding it to its parent
183 * group, etc.)
185 static void
186 item_post_create_setup (GnomeCanvasItem *item)
188 group_add (GNOME_CANVAS_GROUP (item->parent), item);
190 gnome_canvas_request_redraw (
191 item->canvas, item->x1, item->y1, item->x2 + 1, item->y2 + 1);
192 item->canvas->need_repick = TRUE;
195 /* Set_property handler for canvas items */
196 static void
197 gnome_canvas_item_set_property (GObject *gobject,
198 guint property_id,
199 const GValue *value,
200 GParamSpec *pspec)
202 GnomeCanvasItem *item;
204 g_return_if_fail (GNOME_IS_CANVAS_ITEM (gobject));
206 item = GNOME_CANVAS_ITEM (gobject);
208 switch (property_id) {
209 case ITEM_PROP_PARENT:
210 if (item->parent != NULL) {
211 g_warning ("Cannot set `parent' argument after item has "
212 "already been constructed.");
213 } else if (g_value_get_object (value)) {
214 item->parent = GNOME_CANVAS_ITEM (g_value_get_object (value));
215 item->canvas = item->parent->canvas;
216 item_post_create_setup (item);
218 break;
219 default:
220 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
221 break;
225 /* Get_property handler for canvas items */
226 static void
227 gnome_canvas_item_get_property (GObject *gobject,
228 guint property_id,
229 GValue *value,
230 GParamSpec *pspec)
232 GnomeCanvasItem *item;
234 g_return_if_fail (GNOME_IS_CANVAS_ITEM (gobject));
236 item = GNOME_CANVAS_ITEM (gobject);
238 switch (property_id) {
239 case ITEM_PROP_PARENT:
240 g_value_set_object (value, item->parent);
241 break;
243 default:
244 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
245 break;
250 * gnome_canvas_item_construct:
251 * @item: An unconstructed canvas item.
252 * @parent: The parent group for the item.
253 * @first_arg_name: The name of the first argument for configuring the item.
254 * @args: The list of arguments used to configure the item.
256 * Constructs a canvas item; meant for use only by item implementations.
258 void
259 gnome_canvas_item_construct (GnomeCanvasItem *item,
260 GnomeCanvasGroup *parent,
261 const gchar *first_arg_name,
262 va_list args)
264 g_return_if_fail (GNOME_IS_CANVAS_GROUP (parent));
265 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
267 item->parent = GNOME_CANVAS_ITEM (parent);
268 item->canvas = item->parent->canvas;
270 g_object_set_valist (G_OBJECT (item), first_arg_name, args);
272 item_post_create_setup (item);
275 /* If the item is visible, requests a redraw of it. */
276 static void
277 redraw_if_visible (GnomeCanvasItem *item)
279 if (item->flags & GNOME_CANVAS_ITEM_VISIBLE)
280 gnome_canvas_request_redraw (
281 item->canvas, item->x1, item->y1,
282 item->x2 + 1, item->y2 + 1);
285 /* Standard object dispose function for canvas items */
286 static void
287 gnome_canvas_item_dispose (GObject *object)
289 GnomeCanvasItem *item;
291 g_return_if_fail (GNOME_IS_CANVAS_ITEM (object));
293 item = GNOME_CANVAS_ITEM (object);
295 if (item->canvas)
296 redraw_if_visible (item);
298 /* Make the canvas forget about us */
300 if (item->canvas && item == item->canvas->current_item) {
301 item->canvas->current_item = NULL;
302 item->canvas->need_repick = TRUE;
305 if (item->canvas && item == item->canvas->new_current_item) {
306 item->canvas->new_current_item = NULL;
307 item->canvas->need_repick = TRUE;
310 if (item->canvas && item == item->canvas->grabbed_item) {
311 item->canvas->grabbed_item = NULL;
313 gdk_device_ungrab (
314 item->canvas->grabbed_device, GDK_CURRENT_TIME);
315 g_object_unref (item->canvas->grabbed_device);
316 item->canvas->grabbed_device = NULL;
319 if (item->canvas && item == item->canvas->focused_item)
320 item->canvas->focused_item = NULL;
322 /* Normal dispose stuff */
324 if (item->flags & GNOME_CANVAS_ITEM_MAPPED)
325 (* GNOME_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
327 if (item->flags & GNOME_CANVAS_ITEM_REALIZED)
328 (* GNOME_CANVAS_ITEM_GET_CLASS (item)->unrealize) (item);
330 if (item->parent)
331 group_remove (GNOME_CANVAS_GROUP (item->parent), item);
333 if (GNOME_CANVAS_ITEM_GET_CLASS (item)->dispose)
334 GNOME_CANVAS_ITEM_GET_CLASS (item)->dispose (item);
336 G_OBJECT_CLASS (gnome_canvas_item_parent_class)->dispose (object);
337 /* items should remove any reference to item->canvas after the
338 * first ::dispose */
339 item->canvas = NULL;
342 /* Update handler for canvas items */
343 static void
344 gnome_canvas_item_update (GnomeCanvasItem *item,
345 const cairo_matrix_t *matrix,
346 gint flags)
348 item->flags &= ~GNOME_CANVAS_ITEM_NEED_UPDATE;
349 item->flags &= ~GNOME_CANVAS_ITEM_NEED_AFFINE;
350 item->flags &= ~GNOME_CANVAS_ITEM_NEED_CLIP;
351 item->flags &= ~GNOME_CANVAS_ITEM_NEED_VIS;
354 /* Realize handler for canvas items */
355 static void
356 gnome_canvas_item_realize (GnomeCanvasItem *item)
358 item->flags |= GNOME_CANVAS_ITEM_REALIZED;
360 gnome_canvas_item_request_update (item);
363 /* Unrealize handler for canvas items */
364 static void
365 gnome_canvas_item_unrealize (GnomeCanvasItem *item)
367 item->flags &= ~GNOME_CANVAS_ITEM_REALIZED;
370 /* Map handler for canvas items */
371 static void
372 gnome_canvas_item_map (GnomeCanvasItem *item)
374 item->flags |= GNOME_CANVAS_ITEM_MAPPED;
377 /* Unmap handler for canvas items */
378 static void
379 gnome_canvas_item_unmap (GnomeCanvasItem *item)
381 item->flags &= ~GNOME_CANVAS_ITEM_MAPPED;
384 /* Dispose handler for canvas items */
385 static void
386 gnome_canvas_item_dispose_item (GnomeCanvasItem *item)
388 /* Placeholder so subclasses can safely chain up. */
391 static void
392 gnome_canvas_item_draw (GnomeCanvasItem *item,
393 cairo_t *cr,
394 gint x,
395 gint y,
396 gint width,
397 gint height)
399 /* Placeholder so subclasses can safely chain up. */
402 static GnomeCanvasItem *
403 gnome_canvas_item_point (GnomeCanvasItem *item,
404 gdouble x,
405 gdouble y,
406 gint cx,
407 gint cy)
409 /* Placeholder so subclasses can safely chain up. */
411 return NULL;
414 static void
415 gnome_canvas_item_bounds (GnomeCanvasItem *item,
416 gdouble *x1,
417 gdouble *y1,
418 gdouble *x2,
419 gdouble *y2)
421 /* Placeholder so subclasses can safely chain up. */
424 static gboolean
425 gnome_canvas_item_event (GnomeCanvasItem *item,
426 GdkEvent *event)
428 /* Placeholder so subclasses can safely chain up. */
430 return FALSE; /* event was not handled */
434 * This routine invokes the update method of the item
435 * Please notice, that we take parent to canvas pixel matrix as argument
436 * unlike virtual method ::update, whose argument is item 2 canvas pixel
437 * matrix
439 * I will try to force somewhat meaningful naming for affines (Lauris)
440 * General naming rule is FROM2TO, where FROM and TO are abbreviations
441 * So p2cpx is Parent2CanvasPixel and i2cpx is Item2CanvasPixel
442 * I hope that this helps to keep track of what really happens
446 static void
447 gnome_canvas_item_invoke_update (GnomeCanvasItem *item,
448 const cairo_matrix_t *p2c,
449 gint flags)
451 gint child_flags;
452 cairo_matrix_t i2c;
454 child_flags = flags;
455 if (!(item->flags & GNOME_CANVAS_ITEM_VISIBLE))
456 child_flags &= ~GNOME_CANVAS_UPDATE_IS_VISIBLE;
458 /* Calculate actual item transformation matrix */
460 cairo_matrix_multiply (&i2c, &item->matrix, p2c);
462 /* apply object flags to child flags */
464 child_flags &= ~GNOME_CANVAS_UPDATE_REQUESTED;
466 if (item->flags & GNOME_CANVAS_ITEM_NEED_UPDATE)
467 child_flags |= GNOME_CANVAS_UPDATE_REQUESTED;
469 if (item->flags & GNOME_CANVAS_ITEM_NEED_AFFINE)
470 child_flags |= GNOME_CANVAS_UPDATE_AFFINE;
472 if (item->flags & GNOME_CANVAS_ITEM_NEED_CLIP)
473 child_flags |= GNOME_CANVAS_UPDATE_CLIP;
475 if (item->flags & GNOME_CANVAS_ITEM_NEED_VIS)
476 child_flags |= GNOME_CANVAS_UPDATE_VISIBILITY;
478 if (child_flags & GCI_UPDATE_MASK) {
479 if (GNOME_CANVAS_ITEM_GET_CLASS (item)->update)
480 GNOME_CANVAS_ITEM_GET_CLASS (item)->update (item, &i2c, child_flags);
485 * This routine invokes the point method of the item.
486 * The arguments x, y should be in the parent item local coordinates.
488 * This is potentially evil, as we are relying on matrix inversion (Lauris)
491 static GnomeCanvasItem *
492 gnome_canvas_item_invoke_point (GnomeCanvasItem *item,
493 gdouble x,
494 gdouble y,
495 gint cx,
496 gint cy)
498 cairo_matrix_t inverse;
500 /* Calculate x & y in item local coordinates */
501 inverse = item->matrix;
502 if (cairo_matrix_invert (&inverse) != CAIRO_STATUS_SUCCESS)
503 return NULL;
505 cairo_matrix_transform_point (&inverse, &x, &y);
507 if (GNOME_CANVAS_ITEM_GET_CLASS (item)->point)
508 return GNOME_CANVAS_ITEM_GET_CLASS (item)->point (item, x, y, cx, cy);
510 return NULL;
514 * gnome_canvas_item_set:
515 * @item: A canvas item.
516 * @first_arg_name: The list of object argument name/value pairs used to
517 * configure the item.
518 * @Varargs:
520 * Configures a canvas item. The arguments in the item are set to the
521 * specified values, and the item is repainted as appropriate.
523 void
524 gnome_canvas_item_set (GnomeCanvasItem *item,
525 const gchar *first_arg_name,
526 ...)
528 va_list args;
530 va_start (args, first_arg_name);
531 gnome_canvas_item_set_valist (item, first_arg_name, args);
532 va_end (args);
536 * gnome_canvas_item_set_valist:
537 * @item: A canvas item.
538 * @first_arg_name: The name of the first argument used to configure the item.
539 * @args: The list of object argument name/value pairs used to configure the item.
541 * Configures a canvas item. The arguments in the item are set to the specified
542 * values, and the item is repainted as appropriate.
544 void
545 gnome_canvas_item_set_valist (GnomeCanvasItem *item,
546 const gchar *first_arg_name,
547 va_list args)
549 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
551 g_object_set_valist (G_OBJECT (item), first_arg_name, args);
553 item->canvas->need_repick = TRUE;
557 * gnome_canvas_item_transform:
558 * @item: A canvas item.
559 * @matrix: An affine transformation matrix.
561 * Combines the specified affine transformation matrix with the item's current
562 * transformation.
564 void
565 gnome_canvas_item_transform (GnomeCanvasItem *item,
566 const cairo_matrix_t *matrix)
568 cairo_matrix_t i2p;
570 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
571 g_return_if_fail (matrix != NULL);
573 /* Calculate actual item transformation matrix */
574 cairo_matrix_multiply (&i2p, matrix, &item->matrix);
576 gnome_canvas_item_set_matrix (item, &i2p);
580 * gnome_canvas_item_set_matrix:
581 * @item: A canvas item.
582 * @matrix: An affine transformation matrix or %NULL for the identity matrix.
584 * Makes the item's affine transformation matrix be equal to the specified
585 * matrix. NULL is treated as identity.
587 void
588 gnome_canvas_item_set_matrix (GnomeCanvasItem *item,
589 const cairo_matrix_t *matrix)
591 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
593 if (matrix) {
594 item->matrix = *matrix;
595 } else {
596 cairo_matrix_init_identity (&item->matrix);
599 if (!(item->flags & GNOME_CANVAS_ITEM_NEED_AFFINE)) {
600 /* Request update */
601 item->flags |= GNOME_CANVAS_ITEM_NEED_AFFINE;
602 gnome_canvas_item_request_update (item);
605 item->canvas->need_repick = TRUE;
609 * gnome_canvas_item_move:
610 * @item: A canvas item.
611 * @dx: Horizontal offset.
612 * @dy: Vertical offset.
614 * Moves a canvas item by creating an affine transformation matrix for
615 * translation by using the specified values. This happens in item
616 * local coordinate system, so if you have nontrivial transform, it
617 * most probably does not do, what you want.
619 void
620 gnome_canvas_item_move (GnomeCanvasItem *item,
621 gdouble dx,
622 gdouble dy)
624 cairo_matrix_t translate;
626 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
628 cairo_matrix_init_translate (&translate, dx, dy);
630 gnome_canvas_item_transform (item, &translate);
633 /* Convenience function to reorder items in a group's child list. This puts the
634 * specified link after the "before" link. Returns TRUE if the list was changed.
636 static gboolean
637 put_item_after (GList *link,
638 GList *before)
640 GnomeCanvasGroup *parent;
641 GList *old_before, *old_after;
642 GList *after;
644 parent = GNOME_CANVAS_GROUP (GNOME_CANVAS_ITEM (link->data)->parent);
646 if (before)
647 after = before->next;
648 else
649 after = parent->item_list;
651 if (before == link || after == link)
652 return FALSE;
654 /* Unlink */
656 old_before = link->prev;
657 old_after = link->next;
659 if (old_before)
660 old_before->next = old_after;
661 else
662 parent->item_list = old_after;
664 if (old_after)
665 old_after->prev = old_before;
666 else
667 parent->item_list_end = old_before;
669 /* Relink */
671 link->prev = before;
672 if (before)
673 before->next = link;
674 else
675 parent->item_list = link;
677 link->next = after;
678 if (after)
679 after->prev = link;
680 else
681 parent->item_list_end = link;
683 return TRUE;
687 * gnome_canvas_item_raise:
688 * @item: A canvas item.
689 * @positions: Number of steps to raise the item.
691 * Raises the item in its parent's stack by the specified number of positions.
692 * If the number of positions is greater than the distance to the top of the
693 * stack, then the item is put at the top.
695 void
696 gnome_canvas_item_raise (GnomeCanvasItem *item,
697 gint positions)
699 GList *link, *before;
700 GnomeCanvasGroup *parent;
702 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
703 g_return_if_fail (positions >= 0);
705 if (!item->parent || positions == 0)
706 return;
708 parent = GNOME_CANVAS_GROUP (item->parent);
709 link = g_list_find (parent->item_list, item);
710 g_return_if_fail (link != NULL);
712 for (before = link; positions && before; positions--)
713 before = before->next;
715 if (!before)
716 before = parent->item_list_end;
718 if (put_item_after (link, before)) {
719 redraw_if_visible (item);
720 item->canvas->need_repick = TRUE;
725 * gnome_canvas_item_lower:
726 * @item: A canvas item.
727 * @positions: Number of steps to lower the item.
729 * Lowers the item in its parent's stack by the specified number of positions.
730 * If the number of positions is greater than the distance to the bottom of the
731 * stack, then the item is put at the bottom.
733 void
734 gnome_canvas_item_lower (GnomeCanvasItem *item,
735 gint positions)
737 GList *link, *before;
738 GnomeCanvasGroup *parent;
740 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
741 g_return_if_fail (positions >= 1);
743 if (!item->parent || positions == 0)
744 return;
746 parent = GNOME_CANVAS_GROUP (item->parent);
747 link = g_list_find (parent->item_list, item);
748 g_return_if_fail (link != NULL);
750 if (link->prev)
751 for (before = link->prev; positions && before; positions--)
752 before = before->prev;
753 else
754 before = NULL;
756 if (put_item_after (link, before)) {
757 redraw_if_visible (item);
758 item->canvas->need_repick = TRUE;
763 * gnome_canvas_item_raise_to_top:
764 * @item: A canvas item.
766 * Raises an item to the top of its parent's stack.
768 void
769 gnome_canvas_item_raise_to_top (GnomeCanvasItem *item)
771 GList *link;
772 GnomeCanvasGroup *parent;
774 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
776 if (!item->parent)
777 return;
779 parent = GNOME_CANVAS_GROUP (item->parent);
780 link = g_list_find (parent->item_list, item);
781 g_return_if_fail (link != NULL);
783 if (put_item_after (link, parent->item_list_end)) {
784 redraw_if_visible (item);
785 item->canvas->need_repick = TRUE;
790 * gnome_canvas_item_lower_to_bottom:
791 * @item: A canvas item.
793 * Lowers an item to the bottom of its parent's stack.
795 void
796 gnome_canvas_item_lower_to_bottom (GnomeCanvasItem *item)
798 GList *link;
799 GnomeCanvasGroup *parent;
801 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
803 if (!item->parent)
804 return;
806 parent = GNOME_CANVAS_GROUP (item->parent);
807 link = g_list_find (parent->item_list, item);
808 g_return_if_fail (link != NULL);
810 if (put_item_after (link, NULL)) {
811 redraw_if_visible (item);
812 item->canvas->need_repick = TRUE;
817 * gnome_canvas_item_show:
818 * @item: A canvas item.
820 * Shows a canvas item. If the item was already shown, then no action is taken.
822 void
823 gnome_canvas_item_show (GnomeCanvasItem *item)
825 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
827 if (!(item->flags & GNOME_CANVAS_ITEM_VISIBLE)) {
828 item->flags |= GNOME_CANVAS_ITEM_VISIBLE;
829 gnome_canvas_request_redraw (
830 item->canvas, item->x1, item->y1,
831 item->x2 + 1, item->y2 + 1);
832 item->canvas->need_repick = TRUE;
837 * gnome_canvas_item_hide:
838 * @item: A canvas item.
840 * Hides a canvas item. If the item was already hidden, then no action is
841 * taken.
843 void
844 gnome_canvas_item_hide (GnomeCanvasItem *item)
846 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
848 if (item->flags & GNOME_CANVAS_ITEM_VISIBLE) {
849 item->flags &= ~GNOME_CANVAS_ITEM_VISIBLE;
850 gnome_canvas_request_redraw (
851 item->canvas, item->x1, item->y1,
852 item->x2 + 1, item->y2 + 1);
853 item->canvas->need_repick = TRUE;
858 * gnome_canvas_item_grab:
859 * @item: A canvas item.
860 * @event_mask: Mask of events that will be sent to this item.
861 * @cursor: If non-NULL, the cursor that will be used while the grab is active.
862 * @device: The pointer device to grab.
863 * @etime: The timestamp required for grabbing @device, or GDK_CURRENT_TIME.
865 * Specifies that all events that match the specified event mask should be sent
866 * to the specified item, and also grabs @device by calling gdk_device_grab().
867 * The event mask is also used when grabbing the @device. If @cursor is not
868 * NULL, then that cursor is used while the grab is active. The @etime
869 * parameter is the timestamp required for grabbing the @device.
871 * Return value: If an item was already grabbed, it returns
872 * %GDK_GRAB_ALREADY_GRABBED. If the specified item was hidden by calling
873 * gnome_canvas_item_hide(), then it returns %GDK_GRAB_NOT_VIEWABLE. Else,
874 * it returns the result of calling gdk_device_grab().
876 gint
877 gnome_canvas_item_grab (GnomeCanvasItem *item,
878 guint event_mask,
879 GdkCursor *cursor,
880 GdkDevice *device,
881 guint32 etime)
883 GtkLayout *layout;
884 GdkWindow *bin_window;
885 gint retval;
887 g_return_val_if_fail (
888 GNOME_IS_CANVAS_ITEM (item), GDK_GRAB_NOT_VIEWABLE);
889 g_return_val_if_fail (
890 gtk_widget_get_mapped (GTK_WIDGET (item->canvas)),
891 GDK_GRAB_NOT_VIEWABLE);
892 g_return_val_if_fail (
893 GDK_IS_DEVICE (device), GDK_GRAB_NOT_VIEWABLE);
895 if (item->canvas->grabbed_item)
896 return GDK_GRAB_ALREADY_GRABBED;
898 if (!(item->flags & GNOME_CANVAS_ITEM_VISIBLE))
899 return GDK_GRAB_NOT_VIEWABLE;
901 layout = GTK_LAYOUT (item->canvas);
902 bin_window = gtk_layout_get_bin_window (layout);
904 retval = gdk_device_grab (
905 device, bin_window, GDK_OWNERSHIP_NONE,
906 FALSE, event_mask, cursor, etime);
908 if (retval != GDK_GRAB_SUCCESS)
909 return retval;
911 item->canvas->grabbed_item = item;
912 item->canvas->grabbed_device = g_object_ref (device);
913 item->canvas->grabbed_event_mask = event_mask;
914 item->canvas->current_item = item; /* So that events go to the grabbed item */
916 return retval;
920 * gnome_canvas_item_ungrab:
921 * @item: A canvas item that holds a grab.
922 * @etime: The timestamp for ungrabbing the mouse.
924 * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
925 * mouse.
927 void
928 gnome_canvas_item_ungrab (GnomeCanvasItem *item,
929 guint32 etime)
931 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
933 if (item->canvas->grabbed_item != item)
934 return;
936 item->canvas->grabbed_item = NULL;
938 g_return_if_fail (item->canvas->grabbed_device != NULL);
939 gdk_device_ungrab (item->canvas->grabbed_device, etime);
941 g_object_unref (item->canvas->grabbed_device);
942 item->canvas->grabbed_device = NULL;
945 void
946 gnome_canvas_item_i2w_matrix (GnomeCanvasItem *item,
947 cairo_matrix_t *matrix)
949 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
950 g_return_if_fail (matrix != NULL);
952 cairo_matrix_init_identity (matrix);
954 while (item) {
955 cairo_matrix_multiply (matrix, matrix, &item->matrix);
957 item = item->parent;
961 void
962 gnome_canvas_item_w2i_matrix (GnomeCanvasItem *item,
963 cairo_matrix_t *matrix)
965 cairo_status_t status;
967 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
968 g_return_if_fail (matrix != NULL);
970 gnome_canvas_item_i2w_matrix (item, matrix);
971 status = cairo_matrix_invert (matrix);
972 g_return_if_fail (status == CAIRO_STATUS_SUCCESS);
976 * gnome_canvas_item_w2i:
977 * @item: A canvas item.
978 * @x: X coordinate to convert (input/output value).
979 * @y: Y coordinate to convert (input/output value).
981 * Converts a coordinate pair from world coordinates to item-relative
982 * coordinates.
984 void
985 gnome_canvas_item_w2i (GnomeCanvasItem *item,
986 gdouble *x,
987 gdouble *y)
989 cairo_matrix_t matrix;
991 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
992 g_return_if_fail (x != NULL);
993 g_return_if_fail (y != NULL);
995 gnome_canvas_item_w2i_matrix (item, &matrix);
996 cairo_matrix_transform_point (&matrix, x, y);
1000 * gnome_canvas_item_i2w:
1001 * @item: A canvas item.
1002 * @x: X coordinate to convert (input/output value).
1003 * @y: Y coordinate to convert (input/output value).
1005 * Converts a coordinate pair from item-relative coordinates to world
1006 * coordinates.
1008 void
1009 gnome_canvas_item_i2w (GnomeCanvasItem *item,
1010 gdouble *x,
1011 gdouble *y)
1013 cairo_matrix_t matrix;
1015 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
1016 g_return_if_fail (x != NULL);
1017 g_return_if_fail (y != NULL);
1019 gnome_canvas_item_i2w_matrix (item, &matrix);
1020 cairo_matrix_transform_point (&matrix, x, y);
1024 * gnome_canvas_item_i2c_matrix:
1025 * @item: A canvas item.
1026 * @matrix: Matrix to take the resulting transformation matrix (return value).
1028 * Gets the affine transform that converts from item-relative coordinates to
1029 * canvas pixel coordinates.
1031 void
1032 gnome_canvas_item_i2c_matrix (GnomeCanvasItem *item,
1033 cairo_matrix_t *matrix)
1035 cairo_matrix_t i2w, w2c;
1037 gnome_canvas_item_i2w_matrix (item, &i2w);
1038 gnome_canvas_w2c_matrix (item->canvas, &w2c);
1039 cairo_matrix_multiply (matrix, &i2w, &w2c);
1042 /* Returns whether the item is an inferior of or is equal to the parent. */
1043 static gint
1044 is_descendant (GnomeCanvasItem *item,
1045 GnomeCanvasItem *parent)
1047 for (; item; item = item->parent)
1048 if (item == parent)
1049 return TRUE;
1051 return FALSE;
1055 * gnome_canvas_item_reparent:
1056 * @item: A canvas item.
1057 * @new_group: A canvas group.
1059 * Changes the parent of the specified item to be the new group. The item keeps
1060 * its group-relative coordinates as for its old parent, so the item may change
1061 * its absolute position within the canvas.
1063 void
1064 gnome_canvas_item_reparent (GnomeCanvasItem *item,
1065 GnomeCanvasGroup *new_group)
1067 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
1068 g_return_if_fail (GNOME_IS_CANVAS_GROUP (new_group));
1070 /* Both items need to be in the same canvas */
1071 g_return_if_fail (item->canvas == GNOME_CANVAS_ITEM (new_group)->canvas);
1073 /* The group cannot be an inferior of the item or be the item itself --
1074 * this also takes care of the case where the item is the root item of
1075 * the canvas. */
1076 g_return_if_fail (!is_descendant (GNOME_CANVAS_ITEM (new_group), item));
1078 /* Everything is ok, now actually reparent the item */
1080 g_object_ref (item); /* protect it from the unref in group_remove */
1082 redraw_if_visible (item);
1084 group_remove (GNOME_CANVAS_GROUP (item->parent), item);
1085 item->parent = GNOME_CANVAS_ITEM (new_group);
1086 group_add (new_group, item);
1088 /* Redraw and repick */
1090 redraw_if_visible (item);
1091 item->canvas->need_repick = TRUE;
1093 g_object_unref (item);
1097 * gnome_canvas_item_grab_focus:
1098 * @item: A canvas item.
1100 * Makes the specified item take the keyboard focus, so all keyboard events will
1101 * be sent to it. If the canvas widget itself did not have the focus, it grabs
1102 * it as well.
1104 void
1105 gnome_canvas_item_grab_focus (GnomeCanvasItem *item)
1107 GnomeCanvasItem *focused_item;
1108 GdkEvent ev;
1110 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
1111 g_return_if_fail (gtk_widget_get_can_focus (GTK_WIDGET (item->canvas)));
1113 focused_item = item->canvas->focused_item;
1115 if (focused_item) {
1116 GtkLayout *layout;
1117 GdkWindow *bin_window;
1119 layout = GTK_LAYOUT (item->canvas);
1120 bin_window = gtk_layout_get_bin_window (layout);
1122 ev.focus_change.type = GDK_FOCUS_CHANGE;
1123 ev.focus_change.window = bin_window;
1124 ev.focus_change.send_event = FALSE;
1125 ev.focus_change.in = FALSE;
1127 emit_event (item->canvas, &ev);
1130 item->canvas->focused_item = item;
1131 gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
1133 if (focused_item) {
1134 GtkLayout *layout;
1135 GdkWindow *bin_window;
1137 layout = GTK_LAYOUT (item->canvas);
1138 bin_window = gtk_layout_get_bin_window (layout);
1140 ev.focus_change.type = GDK_FOCUS_CHANGE;
1141 ev.focus_change.window = bin_window;
1142 ev.focus_change.send_event = FALSE;
1143 ev.focus_change.in = TRUE;
1145 emit_event (item->canvas, &ev);
1150 * gnome_canvas_item_get_bounds:
1151 * @item: A canvas item.
1152 * @x1: Leftmost edge of the bounding box (return value).
1153 * @y1: Upper edge of the bounding box (return value).
1154 * @x2: Rightmost edge of the bounding box (return value).
1155 * @y2: Lower edge of the bounding box (return value).
1157 * Queries the bounding box of a canvas item. The bounds are returned in the
1158 * coordinate system of the item's parent.
1160 void
1161 gnome_canvas_item_get_bounds (GnomeCanvasItem *item,
1162 gdouble *x1,
1163 gdouble *y1,
1164 gdouble *x2,
1165 gdouble *y2)
1167 gdouble tx1, ty1, tx2, ty2;
1169 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
1171 tx1 = ty1 = tx2 = ty2 = 0.0;
1173 /* Get the item's bounds in its coordinate system */
1175 if (GNOME_CANVAS_ITEM_GET_CLASS (item)->bounds)
1176 GNOME_CANVAS_ITEM_GET_CLASS (item)->bounds (
1177 item, &tx1, &ty1, &tx2, &ty2);
1179 /* Make the bounds relative to the item's parent coordinate system */
1180 gnome_canvas_matrix_transform_rect (&item->matrix, &tx1, &ty1, &tx2, &ty2);
1182 /* Return the values */
1184 if (x1)
1185 *x1 = tx1;
1187 if (y1)
1188 *y1 = ty1;
1190 if (x2)
1191 *x2 = tx2;
1193 if (y2)
1194 *y2 = ty2;
1198 * gnome_canvas_item_request_update
1199 * @item: A canvas item.
1201 * To be used only by item implementations. Requests that the canvas queue an
1202 * update for the specified item.
1204 void
1205 gnome_canvas_item_request_update (GnomeCanvasItem *item)
1207 if (item->flags & GNOME_CANVAS_ITEM_NEED_UPDATE)
1208 return;
1210 item->flags |= GNOME_CANVAS_ITEM_NEED_UPDATE;
1212 if (item->parent != NULL) {
1213 /* Recurse up the tree */
1214 gnome_canvas_item_request_update (item->parent);
1215 } else {
1216 /* Have reached the top of the tree, make
1217 * sure the update call gets scheduled. */
1218 gnome_canvas_request_update (item->canvas);
1222 /*** GnomeCanvasGroup ***/
1224 enum {
1225 GROUP_PROP_0,
1226 GROUP_PROP_X,
1227 GROUP_PROP_Y
1230 static void gnome_canvas_group_set_property (GObject *object,
1231 guint property_id,
1232 const GValue *value,
1233 GParamSpec *pspec);
1234 static void gnome_canvas_group_get_property (GObject *object,
1235 guint property_id,
1236 GValue *value,
1237 GParamSpec *pspec);
1239 static void gnome_canvas_group_dispose (GnomeCanvasItem *object);
1241 static void gnome_canvas_group_update (GnomeCanvasItem *item,
1242 const cairo_matrix_t *matrix,
1243 gint flags);
1244 static void gnome_canvas_group_realize (GnomeCanvasItem *item);
1245 static void gnome_canvas_group_unrealize (GnomeCanvasItem *item);
1246 static void gnome_canvas_group_map (GnomeCanvasItem *item);
1247 static void gnome_canvas_group_unmap (GnomeCanvasItem *item);
1248 static void gnome_canvas_group_draw (GnomeCanvasItem *item,
1249 cairo_t *cr,
1250 gint x, gint y,
1251 gint width, gint height);
1252 static GnomeCanvasItem *gnome_canvas_group_point (GnomeCanvasItem *item,
1253 gdouble x, gdouble y,
1254 gint cx, gint cy);
1255 static void gnome_canvas_group_bounds (GnomeCanvasItem *item,
1256 gdouble *x1, gdouble *y1,
1257 gdouble *x2, gdouble *y2);
1259 G_DEFINE_TYPE (
1260 GnomeCanvasGroup,
1261 gnome_canvas_group,
1262 GNOME_TYPE_CANVAS_ITEM)
1264 /* Class initialization function for GnomeCanvasGroupClass */
1265 static void
1266 gnome_canvas_group_class_init (GnomeCanvasGroupClass *class)
1268 GObjectClass *object_class;
1269 GnomeCanvasItemClass *item_class;
1271 object_class = (GObjectClass *) class;
1272 item_class = (GnomeCanvasItemClass *) class;
1274 object_class->set_property = gnome_canvas_group_set_property;
1275 object_class->get_property = gnome_canvas_group_get_property;
1277 g_object_class_install_property (
1278 object_class,
1279 GROUP_PROP_X,
1280 g_param_spec_double (
1281 "x",
1282 "X",
1283 "X",
1284 -G_MAXDOUBLE,
1285 G_MAXDOUBLE,
1286 0.0,
1287 G_PARAM_READABLE |
1288 G_PARAM_WRITABLE));
1290 g_object_class_install_property (
1291 object_class,
1292 GROUP_PROP_Y,
1293 g_param_spec_double (
1294 "y",
1295 "Y",
1296 "Y",
1297 -G_MAXDOUBLE,
1298 G_MAXDOUBLE,
1299 0.0,
1300 G_PARAM_READABLE |
1301 G_PARAM_WRITABLE));
1303 item_class->dispose = gnome_canvas_group_dispose;
1304 item_class->update = gnome_canvas_group_update;
1305 item_class->realize = gnome_canvas_group_realize;
1306 item_class->unrealize = gnome_canvas_group_unrealize;
1307 item_class->map = gnome_canvas_group_map;
1308 item_class->unmap = gnome_canvas_group_unmap;
1309 item_class->draw = gnome_canvas_group_draw;
1310 item_class->point = gnome_canvas_group_point;
1311 item_class->bounds = gnome_canvas_group_bounds;
1314 /* Object initialization function for GnomeCanvasGroup */
1315 static void
1316 gnome_canvas_group_init (GnomeCanvasGroup *group)
1320 /* Set_property handler for canvas groups */
1321 static void
1322 gnome_canvas_group_set_property (GObject *gobject,
1323 guint property_id,
1324 const GValue *value,
1325 GParamSpec *pspec)
1327 GnomeCanvasItem *item;
1329 g_return_if_fail (GNOME_IS_CANVAS_GROUP (gobject));
1331 item = GNOME_CANVAS_ITEM (gobject);
1333 switch (property_id) {
1334 case GROUP_PROP_X:
1335 item->matrix.x0 = g_value_get_double (value);
1336 break;
1338 case GROUP_PROP_Y:
1339 item->matrix.y0 = g_value_get_double (value);
1340 break;
1342 default:
1343 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
1344 break;
1348 /* Get_property handler for canvas groups */
1349 static void
1350 gnome_canvas_group_get_property (GObject *gobject,
1351 guint property_id,
1352 GValue *value,
1353 GParamSpec *pspec)
1355 GnomeCanvasItem *item;
1357 g_return_if_fail (GNOME_IS_CANVAS_GROUP (gobject));
1359 item = GNOME_CANVAS_ITEM (gobject);
1361 switch (property_id) {
1362 case GROUP_PROP_X:
1363 g_value_set_double (value, item->matrix.x0);
1364 break;
1366 case GROUP_PROP_Y:
1367 g_value_set_double (value, item->matrix.y0);
1368 break;
1370 default:
1371 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
1372 break;
1376 /* Dispose handler for canvas groups */
1377 static void
1378 gnome_canvas_group_dispose (GnomeCanvasItem *object)
1380 GnomeCanvasGroup *group;
1382 g_return_if_fail (GNOME_IS_CANVAS_GROUP (object));
1384 group = GNOME_CANVAS_GROUP (object);
1386 while (group->item_list) {
1387 /* child is unref'ed by the child's group_remove (). */
1388 g_object_run_dispose (G_OBJECT (group->item_list->data));
1391 GNOME_CANVAS_ITEM_CLASS (gnome_canvas_group_parent_class)->
1392 dispose (object);
1395 /* Update handler for canvas groups */
1396 static void
1397 gnome_canvas_group_update (GnomeCanvasItem *item,
1398 const cairo_matrix_t *i2c,
1399 gint flags)
1401 GnomeCanvasGroup *group;
1402 GList *list;
1403 GnomeCanvasItem *i;
1404 gdouble x1, y1, x2, y2;
1406 group = GNOME_CANVAS_GROUP (item);
1408 GNOME_CANVAS_ITEM_CLASS (gnome_canvas_group_parent_class)->
1409 update (item, i2c, flags);
1411 x1 = G_MAXDOUBLE;
1412 y1 = G_MAXDOUBLE;
1413 x2 = -G_MAXDOUBLE;
1414 y2 = -G_MAXDOUBLE;
1416 for (list = group->item_list; list; list = list->next) {
1417 i = list->data;
1419 gnome_canvas_item_invoke_update (i, i2c, flags);
1421 x1 = MIN (x1, i->x1);
1422 x2 = MAX (x2, i->x2);
1423 y1 = MIN (y1, i->y1);
1424 y2 = MAX (y2, i->y2);
1426 if (x1 >= x2 || y1 >= y2) {
1427 item->x1 = item->x2 = item->y1 = item->y2 = 0;
1428 } else {
1429 item->x1 = x1;
1430 item->y1 = y1;
1431 item->x2 = x2;
1432 item->y2 = y2;
1436 /* Realize handler for canvas groups */
1437 static void
1438 gnome_canvas_group_realize (GnomeCanvasItem *item)
1440 GnomeCanvasGroup *group;
1441 GList *list;
1442 GnomeCanvasItem *i;
1444 group = GNOME_CANVAS_GROUP (item);
1446 for (list = group->item_list; list; list = list->next) {
1447 i = list->data;
1449 if (!(i->flags & GNOME_CANVAS_ITEM_REALIZED))
1450 (* GNOME_CANVAS_ITEM_GET_CLASS (i)->realize) (i);
1453 GNOME_CANVAS_ITEM_CLASS (gnome_canvas_group_parent_class)->
1454 realize (item);
1457 /* Unrealize handler for canvas groups */
1458 static void
1459 gnome_canvas_group_unrealize (GnomeCanvasItem *item)
1461 GnomeCanvasGroup *group;
1462 GList *list;
1463 GnomeCanvasItem *i;
1465 group = GNOME_CANVAS_GROUP (item);
1467 for (list = group->item_list; list; list = list->next) {
1468 i = list->data;
1470 if (i->flags & GNOME_CANVAS_ITEM_REALIZED)
1471 (* GNOME_CANVAS_ITEM_GET_CLASS (i)->unrealize) (i);
1474 GNOME_CANVAS_ITEM_CLASS (gnome_canvas_group_parent_class)->
1475 unrealize (item);
1478 /* Map handler for canvas groups */
1479 static void
1480 gnome_canvas_group_map (GnomeCanvasItem *item)
1482 GnomeCanvasGroup *group;
1483 GList *list;
1484 GnomeCanvasItem *i;
1486 group = GNOME_CANVAS_GROUP (item);
1488 for (list = group->item_list; list; list = list->next) {
1489 i = list->data;
1491 if (!(i->flags & GNOME_CANVAS_ITEM_MAPPED))
1492 (* GNOME_CANVAS_ITEM_GET_CLASS (i)->map) (i);
1495 GNOME_CANVAS_ITEM_CLASS (gnome_canvas_group_parent_class)->map (item);
1498 /* Unmap handler for canvas groups */
1499 static void
1500 gnome_canvas_group_unmap (GnomeCanvasItem *item)
1502 GnomeCanvasGroup *group;
1503 GList *list;
1504 GnomeCanvasItem *i;
1506 group = GNOME_CANVAS_GROUP (item);
1508 for (list = group->item_list; list; list = list->next) {
1509 i = list->data;
1511 if (i->flags & GNOME_CANVAS_ITEM_MAPPED)
1512 (* GNOME_CANVAS_ITEM_GET_CLASS (i)->unmap) (i);
1515 GNOME_CANVAS_ITEM_CLASS (gnome_canvas_group_parent_class)->unmap (item);
1518 /* Draw handler for canvas groups */
1519 static void
1520 gnome_canvas_group_draw (GnomeCanvasItem *item,
1521 cairo_t *cr,
1522 gint x,
1523 gint y,
1524 gint width,
1525 gint height)
1527 GnomeCanvasGroup *group;
1528 GList *list;
1529 GnomeCanvasItem *child = NULL;
1531 group = GNOME_CANVAS_GROUP (item);
1533 for (list = group->item_list; list; list = list->next) {
1534 child = list->data;
1536 if ((child->flags & GNOME_CANVAS_ITEM_VISIBLE)
1537 && ((child->x1 < (x + width))
1538 && (child->y1 < (y + height))
1539 && (child->x2 > x)
1540 && (child->y2 > y))) {
1541 cairo_save (cr);
1543 GNOME_CANVAS_ITEM_GET_CLASS (child)->draw (
1544 child, cr, x, y, width, height);
1546 cairo_restore (cr);
1551 /* Point handler for canvas groups */
1552 static GnomeCanvasItem *
1553 gnome_canvas_group_point (GnomeCanvasItem *item,
1554 gdouble x,
1555 gdouble y,
1556 gint cx,
1557 gint cy)
1559 GnomeCanvasGroup *group;
1560 GList *list;
1561 GnomeCanvasItem *child, *point_item;
1563 group = GNOME_CANVAS_GROUP (item);
1565 for (list = g_list_last (group->item_list); list; list = list->prev) {
1566 child = list->data;
1568 if ((child->x1 > cx) || (child->y1 > cy))
1569 continue;
1571 if ((child->x2 < cx) || (child->y2 < cy))
1572 continue;
1574 if (!(child->flags & GNOME_CANVAS_ITEM_VISIBLE))
1575 continue;
1577 point_item = gnome_canvas_item_invoke_point (child, x, y, cx, cy);
1578 if (point_item)
1579 return point_item;
1582 return NULL;
1585 /* Bounds handler for canvas groups */
1586 static void
1587 gnome_canvas_group_bounds (GnomeCanvasItem *item,
1588 gdouble *x1,
1589 gdouble *y1,
1590 gdouble *x2,
1591 gdouble *y2)
1593 GnomeCanvasGroup *group;
1594 GnomeCanvasItem *child;
1595 GList *list;
1596 gdouble tx1, ty1, tx2, ty2;
1597 gdouble minx, miny, maxx, maxy;
1598 gint set;
1600 group = GNOME_CANVAS_GROUP (item);
1602 /* Get the bounds of the first visible item */
1604 child = NULL; /* Unnecessary but eliminates a warning. */
1606 set = FALSE;
1608 for (list = group->item_list; list; list = list->next) {
1609 child = list->data;
1611 if (child->flags & GNOME_CANVAS_ITEM_VISIBLE) {
1612 set = TRUE;
1613 gnome_canvas_item_get_bounds (child, &minx, &miny, &maxx, &maxy);
1614 break;
1618 /* If there were no visible items, return an empty bounding box */
1620 if (!set) {
1621 *x1 = *y1 = *x2 = *y2 = 0.0;
1622 return;
1625 /* Now we can grow the bounds using the rest of the items */
1627 list = list->next;
1629 for (; list; list = list->next) {
1630 child = list->data;
1632 if (!(child->flags & GNOME_CANVAS_ITEM_VISIBLE))
1633 continue;
1635 gnome_canvas_item_get_bounds (child, &tx1, &ty1, &tx2, &ty2);
1637 if (tx1 < minx)
1638 minx = tx1;
1640 if (ty1 < miny)
1641 miny = ty1;
1643 if (tx2 > maxx)
1644 maxx = tx2;
1646 if (ty2 > maxy)
1647 maxy = ty2;
1650 *x1 = minx;
1651 *y1 = miny;
1652 *x2 = maxx;
1653 *y2 = maxy;
1656 /* Adds an item to a group */
1657 static void
1658 group_add (GnomeCanvasGroup *group,
1659 GnomeCanvasItem *item)
1661 g_object_ref_sink (item);
1663 if (!group->item_list) {
1664 group->item_list = g_list_append (group->item_list, item);
1665 group->item_list_end = group->item_list;
1666 } else
1667 group->item_list_end = g_list_append (group->item_list_end, item)->next;
1669 if (group->item.flags & GNOME_CANVAS_ITEM_REALIZED)
1670 (* GNOME_CANVAS_ITEM_GET_CLASS (item)->realize) (item);
1672 if (group->item.flags & GNOME_CANVAS_ITEM_MAPPED)
1673 (* GNOME_CANVAS_ITEM_GET_CLASS (item)->map) (item);
1675 g_object_notify (G_OBJECT (item), "parent");
1678 /* Removes an item from a group */
1679 static void
1680 group_remove (GnomeCanvasGroup *group,
1681 GnomeCanvasItem *item)
1683 GList *children;
1685 g_return_if_fail (GNOME_IS_CANVAS_GROUP (group));
1686 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
1688 for (children = group->item_list; children; children = children->next)
1689 if (children->data == item) {
1690 if (item->flags & GNOME_CANVAS_ITEM_MAPPED)
1691 (* GNOME_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
1693 if (item->flags & GNOME_CANVAS_ITEM_REALIZED)
1694 (* GNOME_CANVAS_ITEM_GET_CLASS (item)->unrealize) (item);
1696 /* Unparent the child */
1698 item->parent = NULL;
1699 g_object_unref (item);
1701 /* Remove it from the list */
1703 if (children == group->item_list_end)
1704 group->item_list_end = children->prev;
1706 group->item_list = g_list_remove_link (group->item_list, children);
1707 g_list_free (children);
1708 break;
1712 /*** GnomeCanvas ***/
1714 enum {
1715 DRAW_BACKGROUND,
1716 LAST_SIGNAL
1719 static void gnome_canvas_dispose (GObject *object);
1720 static void gnome_canvas_map (GtkWidget *widget);
1721 static void gnome_canvas_unmap (GtkWidget *widget);
1722 static void gnome_canvas_realize (GtkWidget *widget);
1723 static void gnome_canvas_unrealize (GtkWidget *widget);
1724 static void gnome_canvas_size_allocate (GtkWidget *widget,
1725 GtkAllocation *allocation);
1726 static gint gnome_canvas_draw (GtkWidget *widget,
1727 cairo_t *cr);
1728 static void gnome_canvas_drag_end (GtkWidget *widget,
1729 GdkDragContext *context);
1730 static gint gnome_canvas_button (GtkWidget *widget,
1731 GdkEventButton *event);
1732 static gint gnome_canvas_motion (GtkWidget *widget,
1733 GdkEventMotion *event);
1734 static gboolean gnome_canvas_key (GtkWidget *widget,
1735 GdkEventKey *event);
1736 static gint gnome_canvas_crossing (GtkWidget *widget,
1737 GdkEventCrossing *event);
1738 static gint gnome_canvas_focus_in (GtkWidget *widget,
1739 GdkEventFocus *event);
1740 static gint gnome_canvas_focus_out (GtkWidget *widget,
1741 GdkEventFocus *event);
1742 static void gnome_canvas_request_update_real (GnomeCanvas *canvas);
1743 static void gnome_canvas_draw_background (GnomeCanvas *canvas,
1744 cairo_t *cr,
1745 gint x,
1746 gint y,
1747 gint width,
1748 gint height);
1750 static guint canvas_signals[LAST_SIGNAL];
1752 enum {
1753 PROP_0,
1754 PROP_FOCUSED_ITEM,
1757 G_DEFINE_TYPE (
1758 GnomeCanvas,
1759 gnome_canvas,
1760 GTK_TYPE_LAYOUT)
1762 static void
1763 gnome_canvas_paint_rect (GnomeCanvas *canvas,
1764 cairo_t *cr,
1765 gint x0,
1766 gint y0,
1767 gint x1,
1768 gint y1)
1770 GtkWidget *widget;
1771 GtkAllocation allocation;
1772 GtkScrollable *scrollable;
1773 GtkAdjustment *hadjustment;
1774 GtkAdjustment *vadjustment;
1775 gint draw_x1, draw_y1;
1776 gint draw_x2, draw_y2;
1777 gint draw_width, draw_height;
1778 gdouble hadjustment_value;
1779 gdouble vadjustment_value;
1781 g_return_if_fail (!canvas->need_update);
1783 widget = GTK_WIDGET (canvas);
1784 gtk_widget_get_allocation (widget, &allocation);
1786 scrollable = GTK_SCROLLABLE (canvas);
1787 hadjustment = gtk_scrollable_get_hadjustment (scrollable);
1788 vadjustment = gtk_scrollable_get_vadjustment (scrollable);
1790 hadjustment_value = gtk_adjustment_get_value (hadjustment);
1791 vadjustment_value = gtk_adjustment_get_value (vadjustment);
1793 draw_x1 = MAX (x0, hadjustment_value - canvas->zoom_xofs);
1794 draw_y1 = MAX (y0, vadjustment_value - canvas->zoom_yofs);
1795 draw_x2 = MIN (draw_x1 + allocation.width, x1);
1796 draw_y2 = MIN (draw_y1 + allocation.height, y1);
1798 draw_width = draw_x2 - draw_x1;
1799 draw_height = draw_y2 - draw_y1;
1801 if (draw_width < 1 || draw_height < 1)
1802 return;
1804 canvas->draw_xofs = draw_x1;
1805 canvas->draw_yofs = draw_y1;
1807 cairo_save (cr);
1809 g_signal_emit (
1810 canvas, canvas_signals[DRAW_BACKGROUND], 0, cr,
1811 draw_x1, draw_y1, draw_width, draw_height);
1813 cairo_restore (cr);
1815 if (canvas->root->flags & GNOME_CANVAS_ITEM_VISIBLE) {
1816 cairo_save (cr);
1818 (* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->draw) (
1819 canvas->root, cr,
1820 draw_x1, draw_y1,
1821 draw_width, draw_height);
1823 cairo_restore (cr);
1827 static void
1828 gnome_canvas_get_property (GObject *object,
1829 guint property_id,
1830 GValue *value,
1831 GParamSpec *pspec)
1833 switch (property_id) {
1834 case PROP_FOCUSED_ITEM:
1835 g_value_set_object (value, GNOME_CANVAS (object)->focused_item);
1836 break;
1837 default:
1838 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1839 break;
1843 static void
1844 gnome_canvas_set_property (GObject *object,
1845 guint property_id,
1846 const GValue *value,
1847 GParamSpec *pspec)
1849 switch (property_id) {
1850 case PROP_FOCUSED_ITEM:
1851 GNOME_CANVAS (object)->focused_item = g_value_get_object (value);
1852 break;
1853 default:
1854 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1855 break;
1859 /* Class initialization function for GnomeCanvasClass */
1860 static void
1861 gnome_canvas_class_init (GnomeCanvasClass *class)
1863 GObjectClass *object_class;
1864 GtkWidgetClass *widget_class;
1866 object_class = (GObjectClass *) class;
1867 widget_class = (GtkWidgetClass *) class;
1869 object_class->set_property = gnome_canvas_set_property;
1870 object_class->get_property = gnome_canvas_get_property;
1871 object_class->dispose = gnome_canvas_dispose;
1873 widget_class->map = gnome_canvas_map;
1874 widget_class->unmap = gnome_canvas_unmap;
1875 widget_class->realize = gnome_canvas_realize;
1876 widget_class->unrealize = gnome_canvas_unrealize;
1877 widget_class->size_allocate = gnome_canvas_size_allocate;
1878 widget_class->draw = gnome_canvas_draw;
1879 widget_class->drag_end = gnome_canvas_drag_end;
1880 widget_class->button_press_event = gnome_canvas_button;
1881 widget_class->button_release_event = gnome_canvas_button;
1882 widget_class->motion_notify_event = gnome_canvas_motion;
1883 widget_class->key_press_event = gnome_canvas_key;
1884 widget_class->key_release_event = gnome_canvas_key;
1885 widget_class->enter_notify_event = gnome_canvas_crossing;
1886 widget_class->leave_notify_event = gnome_canvas_crossing;
1887 widget_class->focus_in_event = gnome_canvas_focus_in;
1888 widget_class->focus_out_event = gnome_canvas_focus_out;
1890 class->draw_background = gnome_canvas_draw_background;
1891 class->request_update = gnome_canvas_request_update_real;
1893 g_object_class_install_property (
1894 object_class,
1895 PROP_FOCUSED_ITEM,
1896 g_param_spec_object (
1897 "focused_item",
1898 NULL,
1899 NULL,
1900 GNOME_TYPE_CANVAS_ITEM,
1901 G_PARAM_READABLE |
1902 G_PARAM_WRITABLE));
1904 canvas_signals[DRAW_BACKGROUND] = g_signal_new (
1905 "draw_background",
1906 G_TYPE_FROM_CLASS (object_class),
1907 G_SIGNAL_RUN_LAST,
1908 G_STRUCT_OFFSET (GnomeCanvasClass, draw_background),
1909 NULL, NULL, NULL,
1910 G_TYPE_NONE, 5,
1911 CAIRO_GOBJECT_TYPE_CONTEXT,
1912 G_TYPE_INT,
1913 G_TYPE_INT,
1914 G_TYPE_INT,
1915 G_TYPE_INT);
1917 gtk_widget_class_set_accessible_type (widget_class, GAIL_TYPE_CANVAS);
1918 gail_canvas_a11y_init ();
1921 /* Callback used when the root item of a canvas is destroyed. The user should
1922 * never ever do this, so we panic if this happens.
1924 G_GNUC_NORETURN static void
1925 panic_root_finalized (gpointer data,
1926 GObject *gone_object)
1928 g_error ("Eeeek, root item %p of canvas %p was destroyed!", gone_object, data);
1931 /* Object initialization function for GnomeCanvas */
1932 static void
1933 gnome_canvas_init (GnomeCanvas *canvas)
1935 GtkLayout *layout;
1936 guint layout_width, layout_height;
1938 layout = GTK_LAYOUT (canvas);
1939 gtk_layout_get_size (layout, &layout_width, &layout_height);
1941 gtk_widget_set_can_focus (GTK_WIDGET (canvas), TRUE);
1943 canvas->need_update = FALSE;
1944 canvas->idle_id = 0;
1946 canvas->scroll_x1 = 0.0;
1947 canvas->scroll_y1 = 0.0;
1948 canvas->scroll_x2 = layout_width;
1949 canvas->scroll_y2 = layout_height;
1951 canvas->pick_event.type = GDK_LEAVE_NOTIFY;
1952 canvas->pick_event.crossing.x = 0;
1953 canvas->pick_event.crossing.y = 0;
1955 gtk_scrollable_set_hadjustment (GTK_SCROLLABLE (canvas), NULL);
1956 gtk_scrollable_set_vadjustment (GTK_SCROLLABLE (canvas), NULL);
1958 /* Create the root item as a special case */
1960 canvas->root = GNOME_CANVAS_ITEM (
1961 g_object_new (gnome_canvas_group_get_type (), NULL));
1962 canvas->root->canvas = canvas;
1964 g_object_ref_sink (canvas->root);
1966 g_object_weak_ref (G_OBJECT (canvas->root), panic_root_finalized, canvas);
1968 canvas->need_repick = TRUE;
1971 /* Convenience function to remove the idle handler of a canvas */
1972 static void
1973 remove_idle (GnomeCanvas *canvas)
1975 if (canvas->idle_id == 0)
1976 return;
1978 g_source_remove (canvas->idle_id);
1979 canvas->idle_id = 0;
1982 /* Removes the transient state of the canvas (idle handler, grabs). */
1983 static void
1984 shutdown_transients (GnomeCanvas *canvas)
1986 if (canvas->grabbed_device != NULL) {
1987 gdk_device_ungrab (canvas->grabbed_device, GDK_CURRENT_TIME);
1988 g_object_unref (canvas->grabbed_device);
1989 canvas->grabbed_device = NULL;
1992 canvas->grabbed_item = NULL;
1994 remove_idle (canvas);
1997 /* Dispose handler for GnomeCanvas */
1998 static void
1999 gnome_canvas_dispose (GObject *object)
2001 GnomeCanvas *canvas;
2003 g_return_if_fail (GNOME_IS_CANVAS (object));
2005 /* remember, dispose can be run multiple times! */
2007 canvas = GNOME_CANVAS (object);
2009 if (canvas->root) {
2010 g_object_weak_unref (G_OBJECT (canvas->root), panic_root_finalized, canvas);
2011 g_object_unref (canvas->root);
2012 canvas->root = NULL;
2015 shutdown_transients (canvas);
2017 /* Chain up to parent's dispose() method. */
2018 G_OBJECT_CLASS (gnome_canvas_parent_class)->dispose (object);
2022 * gnome_canvas_new:
2024 * Creates a new empty canvas in non-antialiased mode.
2026 * Return value: A newly-created canvas.
2028 GtkWidget *
2029 gnome_canvas_new (void)
2031 return GTK_WIDGET (g_object_new (gnome_canvas_get_type (), NULL));
2034 /* Map handler for the canvas */
2035 static void
2036 gnome_canvas_map (GtkWidget *widget)
2038 GnomeCanvas *canvas;
2040 g_return_if_fail (GNOME_IS_CANVAS (widget));
2042 /* Normal widget mapping stuff */
2044 GTK_WIDGET_CLASS (gnome_canvas_parent_class)->map (widget);
2046 canvas = GNOME_CANVAS (widget);
2048 if (canvas->need_update)
2049 add_idle (canvas);
2051 /* Map items */
2053 if (GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->map)
2054 (* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->map) (canvas->root);
2057 /* Unmap handler for the canvas */
2058 static void
2059 gnome_canvas_unmap (GtkWidget *widget)
2061 GnomeCanvas *canvas;
2063 g_return_if_fail (GNOME_IS_CANVAS (widget));
2065 canvas = GNOME_CANVAS (widget);
2067 shutdown_transients (canvas);
2069 /* Unmap items */
2071 if (GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->unmap)
2072 (* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->unmap) (canvas->root);
2074 /* Normal widget unmapping stuff */
2076 GTK_WIDGET_CLASS (gnome_canvas_parent_class)->unmap (widget);
2079 /* Realize handler for the canvas */
2080 static void
2081 gnome_canvas_realize (GtkWidget *widget)
2083 GnomeCanvas *canvas;
2084 GtkLayout *layout;
2085 GdkWindow *bin_window;
2087 g_return_if_fail (GNOME_IS_CANVAS (widget));
2089 /* Normal widget realization stuff */
2091 GTK_WIDGET_CLASS (gnome_canvas_parent_class)->realize (widget);
2093 canvas = GNOME_CANVAS (widget);
2095 layout = GTK_LAYOUT (canvas);
2096 bin_window = gtk_layout_get_bin_window (layout);
2098 gdk_window_set_events (
2099 bin_window,
2100 (gdk_window_get_events (bin_window)
2101 | GDK_EXPOSURE_MASK
2102 | GDK_SCROLL_MASK
2103 | GDK_BUTTON_PRESS_MASK
2104 | GDK_BUTTON_RELEASE_MASK
2105 | GDK_POINTER_MOTION_MASK
2106 | GDK_KEY_PRESS_MASK
2107 | GDK_KEY_RELEASE_MASK
2108 | GDK_ENTER_NOTIFY_MASK
2109 | GDK_LEAVE_NOTIFY_MASK
2110 | GDK_FOCUS_CHANGE_MASK));
2112 /* Create our own temporary pixmap gc and realize all the items */
2114 (* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->realize) (canvas->root);
2117 /* Unrealize handler for the canvas */
2118 static void
2119 gnome_canvas_unrealize (GtkWidget *widget)
2121 GnomeCanvas *canvas;
2123 g_return_if_fail (GNOME_IS_CANVAS (widget));
2125 canvas = GNOME_CANVAS (widget);
2127 shutdown_transients (canvas);
2129 /* Unrealize items and parent widget */
2131 (* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->unrealize) (canvas->root);
2133 GTK_WIDGET_CLASS (gnome_canvas_parent_class)->unrealize (widget);
2136 /* Handles scrolling of the canvas. Adjusts the scrolling and zooming offset to
2137 * keep as much as possible of the canvas scrolling region in view.
2139 static void
2140 scroll_to (GnomeCanvas *canvas,
2141 gint cx,
2142 gint cy)
2144 GtkWidget *widget;
2145 GtkAllocation allocation;
2146 GtkScrollable *scrollable;
2147 GtkAdjustment *hadjustment;
2148 GtkAdjustment *vadjustment;
2149 guint layout_width, layout_height;
2150 gint scroll_width, scroll_height;
2151 gint right_limit, bottom_limit;
2152 gint old_zoom_xofs, old_zoom_yofs;
2153 gint canvas_width, canvas_height;
2155 widget = GTK_WIDGET (canvas);
2156 gtk_widget_get_allocation (widget, &allocation);
2158 scrollable = GTK_SCROLLABLE (canvas);
2159 hadjustment = gtk_scrollable_get_hadjustment (scrollable);
2160 vadjustment = gtk_scrollable_get_vadjustment (scrollable);
2162 gtk_layout_get_size (GTK_LAYOUT (canvas), &layout_width, &layout_height);
2164 canvas_width = allocation.width;
2165 canvas_height = allocation.height;
2167 scroll_width =
2168 floor ((canvas->scroll_x2 - canvas->scroll_x1) + 0.5);
2169 scroll_height =
2170 floor ((canvas->scroll_y2 - canvas->scroll_y1) + 0.5);
2172 right_limit = scroll_width - canvas_width;
2173 bottom_limit = scroll_height - canvas_height;
2175 old_zoom_xofs = canvas->zoom_xofs;
2176 old_zoom_yofs = canvas->zoom_yofs;
2178 if (right_limit < 0) {
2179 cx = 0;
2180 canvas->zoom_xofs = (canvas_width - scroll_width) / 2;
2181 scroll_width = canvas_width;
2182 } else if (cx < 0) {
2183 cx = 0;
2184 canvas->zoom_xofs = 0;
2185 } else if (cx > right_limit) {
2186 cx = right_limit;
2187 canvas->zoom_xofs = 0;
2188 } else
2189 canvas->zoom_xofs = 0;
2191 if (bottom_limit < 0) {
2192 cy = 0;
2193 canvas->zoom_yofs = (canvas_height - scroll_height) / 2;
2194 scroll_height = canvas_height;
2195 } else if (cy < 0) {
2196 cy = 0;
2197 canvas->zoom_yofs = 0;
2198 } else if (cy > bottom_limit) {
2199 cy = bottom_limit;
2200 canvas->zoom_yofs = 0;
2201 } else
2202 canvas->zoom_yofs = 0;
2204 if ((canvas->zoom_xofs != old_zoom_xofs) ||
2205 (canvas->zoom_yofs != old_zoom_yofs)) {
2206 /* This can only occur, if either canvas size or widget
2207 * size changes. So I think we can request full redraw
2208 * here. The reason is, that coverage UTA will be
2209 * invalidated by offset change. */
2210 /* FIXME Strictly this is not correct - we have to remove
2211 * our own idle (Lauris) */
2212 /* More stuff - we have to mark root as needing fresh affine
2213 * (Lauris) */
2214 if (!(canvas->root->flags & GNOME_CANVAS_ITEM_NEED_AFFINE)) {
2215 canvas->root->flags |= GNOME_CANVAS_ITEM_NEED_AFFINE;
2216 gnome_canvas_request_update (canvas);
2218 gtk_widget_queue_draw (GTK_WIDGET (canvas));
2221 if (hadjustment)
2222 gtk_adjustment_set_value (hadjustment, cx);
2224 if (vadjustment)
2225 gtk_adjustment_set_value (vadjustment, cy);
2227 if ((scroll_width != (gint) layout_width)
2228 || (scroll_height != (gint) layout_height))
2229 gtk_layout_set_size (GTK_LAYOUT (canvas), scroll_width, scroll_height);
2232 /* Size allocation handler for the canvas */
2233 static void
2234 gnome_canvas_size_allocate (GtkWidget *widget,
2235 GtkAllocation *allocation)
2237 GtkScrollable *scrollable;
2238 GtkAdjustment *hadjustment;
2239 GtkAdjustment *vadjustment;
2241 g_return_if_fail (GNOME_IS_CANVAS (widget));
2242 g_return_if_fail (allocation != NULL);
2244 GTK_WIDGET_CLASS (gnome_canvas_parent_class)->
2245 size_allocate (widget, allocation);
2247 scrollable = GTK_SCROLLABLE (widget);
2248 hadjustment = gtk_scrollable_get_hadjustment (scrollable);
2249 vadjustment = gtk_scrollable_get_vadjustment (scrollable);
2251 /* Recenter the view, if appropriate */
2253 g_object_freeze_notify (G_OBJECT (hadjustment));
2254 g_object_freeze_notify (G_OBJECT (vadjustment));
2256 gtk_adjustment_set_page_size (hadjustment, allocation->width);
2257 gtk_adjustment_set_page_increment (hadjustment, allocation->width / 2);
2259 gtk_adjustment_set_page_size (vadjustment, allocation->height);
2260 gtk_adjustment_set_page_increment (vadjustment, allocation->height / 2);
2262 scroll_to (
2263 GNOME_CANVAS (widget),
2264 gtk_adjustment_get_value (hadjustment),
2265 gtk_adjustment_get_value (vadjustment));
2267 g_object_thaw_notify (G_OBJECT (hadjustment));
2268 g_object_thaw_notify (G_OBJECT (vadjustment));
2271 static gboolean
2272 gnome_canvas_draw (GtkWidget *widget,
2273 cairo_t *cr)
2275 GnomeCanvas *canvas = GNOME_CANVAS (widget);
2276 cairo_rectangle_int_t rect;
2277 GtkLayout *layout;
2278 GtkAdjustment *hadjustment;
2279 GtkAdjustment *vadjustment;
2280 gdouble hadjustment_value;
2281 gdouble vadjustment_value;
2283 layout = GTK_LAYOUT (canvas);
2284 hadjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (layout));
2285 vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (layout));
2287 hadjustment_value = gtk_adjustment_get_value (hadjustment);
2288 vadjustment_value = gtk_adjustment_get_value (vadjustment);
2290 gdk_cairo_get_clip_rectangle (cr, &rect);
2292 if (canvas->need_update) {
2293 cairo_matrix_t w2c;
2295 /* We start updating root with w2c matrix */
2296 gnome_canvas_w2c_matrix (canvas, &w2c);
2298 gnome_canvas_item_invoke_update (canvas->root, &w2c, 0);
2300 canvas->need_update = FALSE;
2303 cairo_save (cr);
2304 cairo_translate (
2306 -canvas->zoom_xofs + rect.x,
2307 -canvas->zoom_yofs + rect.y);
2309 rect.x += hadjustment_value;
2310 rect.y += vadjustment_value;
2312 /* No pending updates, draw exposed area immediately */
2313 gnome_canvas_paint_rect (
2314 canvas, cr,
2315 rect.x, rect.y,
2316 rect.x + rect.width,
2317 rect.y + rect.height);
2318 cairo_restore (cr);
2320 /* And call expose on parent container class */
2321 GTK_WIDGET_CLASS (gnome_canvas_parent_class)->draw (widget, cr);
2323 return FALSE;
2326 static void
2327 gnome_canvas_drag_end (GtkWidget *widget,
2328 GdkDragContext *context)
2330 GnomeCanvas *canvas = GNOME_CANVAS (widget);
2332 if (canvas->grabbed_item) {
2333 gnome_canvas_item_ungrab (canvas->grabbed_item, GDK_CURRENT_TIME);
2336 if (GTK_WIDGET_CLASS (gnome_canvas_parent_class)->drag_end)
2337 GTK_WIDGET_CLASS (gnome_canvas_parent_class)->drag_end (widget, context);
2340 /* Emits an event for an item in the canvas, be it the current item, grabbed
2341 * item, or focused item, as appropriate.
2344 static gint
2345 emit_event (GnomeCanvas *canvas,
2346 GdkEvent *event)
2348 GdkEvent *ev;
2349 gint finished;
2350 GnomeCanvasItem *item;
2351 GnomeCanvasItem *parent;
2352 guint mask;
2354 /* Perform checks for grabbed items */
2356 if (canvas->grabbed_item &&
2357 !is_descendant (canvas->current_item, canvas->grabbed_item)) {
2358 /* I think this warning is annoying and I don't know what it's for
2359 * so I'll disable it for now.
2361 /* g_warning ("emit_event() returning FALSE!\n");*/
2362 return FALSE;
2365 if (canvas->grabbed_item) {
2366 switch (event->type) {
2367 case GDK_ENTER_NOTIFY:
2368 mask = GDK_ENTER_NOTIFY_MASK;
2369 break;
2371 case GDK_LEAVE_NOTIFY:
2372 mask = GDK_LEAVE_NOTIFY_MASK;
2373 break;
2375 case GDK_MOTION_NOTIFY:
2376 mask = GDK_POINTER_MOTION_MASK;
2377 break;
2379 case GDK_BUTTON_PRESS:
2380 case GDK_2BUTTON_PRESS:
2381 case GDK_3BUTTON_PRESS:
2382 mask = GDK_BUTTON_PRESS_MASK;
2383 break;
2385 case GDK_BUTTON_RELEASE:
2386 mask = GDK_BUTTON_RELEASE_MASK;
2387 break;
2389 case GDK_KEY_PRESS:
2390 mask = GDK_KEY_PRESS_MASK;
2391 break;
2393 case GDK_KEY_RELEASE:
2394 mask = GDK_KEY_RELEASE_MASK;
2395 break;
2397 case GDK_SCROLL:
2398 mask = GDK_SCROLL_MASK;
2399 break;
2401 default:
2402 mask = 0;
2403 break;
2406 if (!(mask & canvas->grabbed_event_mask))
2407 return FALSE;
2410 /* Convert to world coordinates -- we have two cases because of diferent
2411 * offsets of the fields in the event structures.
2414 ev = gdk_event_copy (event);
2416 switch (ev->type)
2418 case GDK_ENTER_NOTIFY:
2419 case GDK_LEAVE_NOTIFY:
2420 gnome_canvas_window_to_world (
2421 canvas,
2422 ev->crossing.x, ev->crossing.y,
2423 &ev->crossing.x, &ev->crossing.y);
2424 break;
2426 case GDK_MOTION_NOTIFY:
2427 case GDK_BUTTON_PRESS:
2428 case GDK_2BUTTON_PRESS:
2429 case GDK_3BUTTON_PRESS:
2430 case GDK_BUTTON_RELEASE:
2431 gnome_canvas_window_to_world (
2432 canvas,
2433 ev->motion.x, ev->motion.y,
2434 &ev->motion.x, &ev->motion.y);
2435 break;
2437 default:
2438 break;
2441 /* Choose where we send the event */
2443 item = canvas->current_item;
2445 if (canvas->focused_item
2446 && ((event->type == GDK_KEY_PRESS) ||
2447 (event->type == GDK_KEY_RELEASE) ||
2448 (event->type == GDK_FOCUS_CHANGE)))
2449 item = canvas->focused_item;
2451 /* The event is propagated up the hierarchy (for if someone connected to
2452 * a group instead of a leaf event), and emission is stopped if a
2453 * handler returns TRUE, just like for GtkWidget events.
2456 finished = FALSE;
2458 while (item && !finished) {
2459 g_object_ref (item);
2461 g_signal_emit (
2462 item, item_signals[ITEM_EVENT], 0,
2463 ev, &finished);
2465 parent = item->parent;
2466 g_object_unref (item);
2468 item = parent;
2471 gdk_event_free (ev);
2473 return finished;
2476 /* Re-picks the current item in the canvas, based on the event's coordinates.
2477 * Also emits enter/leave events for items as appropriate.
2479 static gint
2480 pick_current_item (GnomeCanvas *canvas,
2481 GdkEvent *event)
2483 gint button_down;
2484 gdouble x, y;
2485 gint cx, cy;
2486 gint retval;
2488 retval = FALSE;
2490 /* If a button is down, we'll perform enter and leave events on the
2491 * current item, but not enter on any other item. This is more or less
2492 * like X pointer grabbing for canvas items.
2494 button_down = canvas->state & (GDK_BUTTON1_MASK
2495 | GDK_BUTTON2_MASK
2496 | GDK_BUTTON3_MASK
2497 | GDK_BUTTON4_MASK
2498 | GDK_BUTTON5_MASK);
2499 if (!button_down)
2500 canvas->left_grabbed_item = FALSE;
2502 /* Save the event in the canvas. This is used to synthesize enter and
2503 * leave events in case the current item changes. It is also used to
2504 * re-pick the current item if the current one gets deleted. Also,
2505 * synthesize an enter event.
2507 if (event != &canvas->pick_event) {
2508 if ((event->type == GDK_MOTION_NOTIFY) ||
2509 (event->type == GDK_BUTTON_RELEASE)) {
2510 /* these fields have the same offsets in both types of events */
2512 canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
2513 canvas->pick_event.crossing.window = event->motion.window;
2514 canvas->pick_event.crossing.send_event = event->motion.send_event;
2515 canvas->pick_event.crossing.subwindow = NULL;
2516 canvas->pick_event.crossing.x = event->motion.x;
2517 canvas->pick_event.crossing.y = event->motion.y;
2518 canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
2519 canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
2520 canvas->pick_event.crossing.focus = FALSE;
2521 canvas->pick_event.crossing.state = event->motion.state;
2523 /* these fields don't have the same offsets in both types of events */
2525 if (event->type == GDK_MOTION_NOTIFY) {
2526 canvas->pick_event.crossing.x_root = event->motion.x_root;
2527 canvas->pick_event.crossing.y_root = event->motion.y_root;
2528 } else {
2529 canvas->pick_event.crossing.x_root = event->button.x_root;
2530 canvas->pick_event.crossing.y_root = event->button.y_root;
2532 } else
2533 canvas->pick_event = *event;
2536 /* Don't do anything else if this is a recursive call */
2538 if (canvas->in_repick)
2539 return retval;
2541 /* LeaveNotify means that there is no current item, so we don't look for one */
2543 if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
2544 /* these fields don't have the same offsets in both types of events */
2546 if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
2547 x = canvas->pick_event.crossing.x - canvas->zoom_xofs;
2548 y = canvas->pick_event.crossing.y - canvas->zoom_yofs;
2549 } else {
2550 x = canvas->pick_event.motion.x - canvas->zoom_xofs;
2551 y = canvas->pick_event.motion.y - canvas->zoom_yofs;
2554 /* canvas pixel coords */
2556 cx = (gint) (x + 0.5);
2557 cy = (gint) (y + 0.5);
2559 /* world coords */
2561 x = canvas->scroll_x1 + x;
2562 y = canvas->scroll_y1 + y;
2564 /* find the closest item */
2566 if (canvas->root->flags & GNOME_CANVAS_ITEM_VISIBLE)
2567 canvas->new_current_item =
2568 gnome_canvas_item_invoke_point (
2569 canvas->root, x, y, cx, cy);
2570 else
2571 canvas->new_current_item = NULL;
2572 } else
2573 canvas->new_current_item = NULL;
2575 if ((canvas->new_current_item == canvas->current_item)
2576 && !canvas->left_grabbed_item)
2577 return retval; /* current item did not change */
2579 /* Synthesize events for old and new current items */
2581 if ((canvas->new_current_item != canvas->current_item)
2582 && (canvas->current_item != NULL)
2583 && !canvas->left_grabbed_item) {
2584 GdkEvent new_event;
2586 new_event = canvas->pick_event;
2587 new_event.type = GDK_LEAVE_NOTIFY;
2589 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
2590 new_event.crossing.subwindow = NULL;
2591 canvas->in_repick = TRUE;
2592 retval = emit_event (canvas, &new_event);
2593 canvas->in_repick = FALSE;
2596 /* new_current_item may have been set to NULL during the
2597 * call to emit_event() above */
2599 if ((canvas->new_current_item != canvas->current_item) && button_down) {
2600 canvas->left_grabbed_item = TRUE;
2601 return retval;
2604 /* Handle the rest of cases */
2606 canvas->left_grabbed_item = FALSE;
2607 canvas->current_item = canvas->new_current_item;
2609 if (canvas->current_item != NULL) {
2610 GdkEvent new_event;
2612 new_event = canvas->pick_event;
2613 new_event.type = GDK_ENTER_NOTIFY;
2614 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
2615 new_event.crossing.subwindow = NULL;
2616 retval = emit_event (canvas, &new_event);
2619 return retval;
2622 /* Button event handler for the canvas */
2623 static gint
2624 gnome_canvas_button (GtkWidget *widget,
2625 GdkEventButton *event)
2627 GnomeCanvas *canvas;
2628 GtkLayout *layout;
2629 GdkWindow *bin_window;
2630 gint mask;
2631 gint retval;
2633 g_return_val_if_fail (GNOME_IS_CANVAS (widget), FALSE);
2634 g_return_val_if_fail (event != NULL, FALSE);
2636 retval = FALSE;
2638 canvas = GNOME_CANVAS (widget);
2640 layout = GTK_LAYOUT (canvas);
2641 bin_window = gtk_layout_get_bin_window (layout);
2644 * dispatch normally regardless of the event's window if an item has
2645 * has a pointer grab in effect
2647 if (!canvas->grabbed_item && event->window != bin_window)
2648 return retval;
2650 switch (event->button) {
2651 case 1:
2652 mask = GDK_BUTTON1_MASK;
2653 break;
2654 case 2:
2655 mask = GDK_BUTTON2_MASK;
2656 break;
2657 case 3:
2658 mask = GDK_BUTTON3_MASK;
2659 break;
2660 case 4:
2661 mask = GDK_BUTTON4_MASK;
2662 break;
2663 case 5:
2664 mask = GDK_BUTTON5_MASK;
2665 break;
2666 default:
2667 mask = 0;
2670 switch (event->type) {
2671 case GDK_BUTTON_PRESS:
2672 case GDK_2BUTTON_PRESS:
2673 case GDK_3BUTTON_PRESS:
2674 /* Pick the current item as if the button were
2675 * not pressed, and then process the event. */
2676 canvas->state = event->state;
2677 pick_current_item (canvas, (GdkEvent *) event);
2678 canvas->state ^= mask;
2679 retval = emit_event (canvas, (GdkEvent *) event);
2680 break;
2682 case GDK_BUTTON_RELEASE:
2683 /* Process the event as if the button were pressed,
2684 * then repick after the button has been released. */
2685 canvas->state = event->state;
2686 retval = emit_event (canvas, (GdkEvent *) event);
2687 event->state ^= mask;
2688 canvas->state = event->state;
2689 pick_current_item (canvas, (GdkEvent *) event);
2690 event->state ^= mask;
2691 break;
2693 default:
2694 g_warn_if_reached ();
2697 return retval;
2700 /* Motion event handler for the canvas */
2701 static gint
2702 gnome_canvas_motion (GtkWidget *widget,
2703 GdkEventMotion *event)
2705 GnomeCanvas *canvas;
2706 GtkLayout *layout;
2707 GdkWindow *bin_window;
2709 g_return_val_if_fail (GNOME_IS_CANVAS (widget), FALSE);
2710 g_return_val_if_fail (event != NULL, FALSE);
2712 canvas = GNOME_CANVAS (widget);
2714 layout = GTK_LAYOUT (widget);
2715 bin_window = gtk_layout_get_bin_window (layout);
2717 if (event->window != bin_window)
2718 return FALSE;
2720 canvas->state = event->state;
2721 pick_current_item (canvas, (GdkEvent *) event);
2722 return emit_event (canvas, (GdkEvent *) event);
2725 /* Key event handler for the canvas */
2726 static gboolean
2727 gnome_canvas_key (GtkWidget *widget,
2728 GdkEventKey *event)
2730 GnomeCanvas *canvas;
2732 g_return_val_if_fail (GNOME_IS_CANVAS (widget), FALSE);
2733 g_return_val_if_fail (event != NULL, FALSE);
2735 canvas = GNOME_CANVAS (widget);
2737 if (!emit_event (canvas, (GdkEvent *) event)) {
2738 GtkWidgetClass *widget_class;
2740 widget_class = GTK_WIDGET_CLASS (gnome_canvas_parent_class);
2742 if (event->type == GDK_KEY_PRESS) {
2743 if (widget_class->key_press_event)
2744 return (* widget_class->key_press_event) (widget, event);
2745 } else if (event->type == GDK_KEY_RELEASE) {
2746 if (widget_class->key_release_event)
2747 return (* widget_class->key_release_event) (widget, event);
2748 } else
2749 g_warn_if_reached ();
2751 return FALSE;
2752 } else
2753 return TRUE;
2756 /* Crossing event handler for the canvas */
2757 static gint
2758 gnome_canvas_crossing (GtkWidget *widget,
2759 GdkEventCrossing *event)
2761 GnomeCanvas *canvas;
2762 GtkLayout *layout;
2763 GdkWindow *bin_window;
2765 g_return_val_if_fail (GNOME_IS_CANVAS (widget), FALSE);
2766 g_return_val_if_fail (event != NULL, FALSE);
2768 canvas = GNOME_CANVAS (widget);
2770 layout = GTK_LAYOUT (canvas);
2771 bin_window = gtk_layout_get_bin_window (layout);
2773 if (event->window != bin_window)
2774 return FALSE;
2776 /* XXX Detect and disregard synthesized crossing events generated
2777 * by synth_crossing() in gtkwidget.c. The pointer coordinates
2778 * are invalid and pick_current_item() relies on them. */
2779 if (event->x == 0 && event->y == 0 &&
2780 event->x_root == 0 && event->y_root == 0)
2781 return FALSE;
2783 canvas->state = event->state;
2784 return pick_current_item (canvas, (GdkEvent *) event);
2787 /* Focus in handler for the canvas */
2788 static gint
2789 gnome_canvas_focus_in (GtkWidget *widget,
2790 GdkEventFocus *event)
2792 GnomeCanvas *canvas;
2794 /* XXX Can't access flags directly anymore, but is it really needed?
2795 * If so, could we call gtk_widget_send_focus_change() instead? */
2796 #if 0
2797 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
2798 #endif
2800 canvas = GNOME_CANVAS (widget);
2802 if (canvas->focused_item)
2803 return emit_event (canvas, (GdkEvent *) event);
2804 else
2805 return FALSE;
2808 /* Focus out handler for the canvas */
2809 static gint
2810 gnome_canvas_focus_out (GtkWidget *widget,
2811 GdkEventFocus *event)
2813 GnomeCanvas *canvas;
2815 /* XXX Can't access flags directly anymore, but is it really needed?
2816 * If so, could we call gtk_widget_send_focus_change() instead? */
2817 #if 0
2818 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2819 #endif
2821 canvas = GNOME_CANVAS (widget);
2823 if (canvas->focused_item)
2824 return emit_event (canvas, (GdkEvent *) event);
2825 else
2826 return FALSE;
2829 static void
2830 gnome_canvas_draw_background (GnomeCanvas *canvas,
2831 cairo_t *cr,
2832 gint x,
2833 gint y,
2834 gint width,
2835 gint height)
2837 GtkStyleContext *style_context;
2838 GdkRGBA rgba;
2840 style_context = gtk_widget_get_style_context (GTK_WIDGET (canvas));
2841 if (!gtk_style_context_lookup_color (style_context, "theme_bg_color", &rgba))
2842 gdk_rgba_parse (&rgba, "#aaaaaa");
2844 cairo_save (cr);
2845 gdk_cairo_set_source_rgba (cr, &rgba);
2846 cairo_paint (cr);
2847 cairo_restore (cr);
2850 static void
2851 do_update (GnomeCanvas *canvas)
2853 /* Cause the update if necessary */
2855 update_again:
2856 if (canvas->need_update) {
2857 cairo_matrix_t w2c;
2859 /* We start updating root with w2c matrix */
2860 gnome_canvas_w2c_matrix (canvas, &w2c);
2862 gnome_canvas_item_invoke_update (canvas->root, &w2c, 0);
2864 canvas->need_update = FALSE;
2867 /* Pick new current item */
2869 while (canvas->need_repick) {
2870 canvas->need_repick = FALSE;
2871 pick_current_item (canvas, &canvas->pick_event);
2874 /* it is possible that during picking we emitted an event in which
2875 * the user then called some function which then requested update
2876 * of something. Without this we'd be left in a state where
2877 * need_update would have been left TRUE and the canvas would have
2878 * been left unpainted. */
2879 if (canvas->need_update) {
2880 goto update_again;
2884 /* Idle handler for the canvas. It deals with pending updates and redraws. */
2885 static gboolean
2886 idle_handler (gpointer data)
2888 GnomeCanvas *canvas;
2890 canvas = GNOME_CANVAS (data);
2892 do_update (canvas);
2894 /* Reset idle id */
2895 canvas->idle_id = 0;
2897 return FALSE;
2900 /* Convenience function to add an idle handler to a canvas */
2901 static void
2902 add_idle (GnomeCanvas *canvas)
2904 g_return_if_fail (canvas->need_update);
2906 if (!canvas->idle_id)
2907 canvas->idle_id = g_idle_add_full (
2908 CANVAS_IDLE_PRIORITY,
2909 idle_handler,
2910 canvas,
2911 NULL);
2913 /* canvas->idle_id = gtk_idle_add (idle_handler, canvas); */
2917 * gnome_canvas_root:
2918 * @canvas: A canvas.
2920 * Queries the root group of a canvas.
2922 * Return value: The root group of the specified canvas.
2924 GnomeCanvasGroup *
2925 gnome_canvas_root (GnomeCanvas *canvas)
2927 g_return_val_if_fail (GNOME_IS_CANVAS (canvas), NULL);
2929 return GNOME_CANVAS_GROUP (canvas->root);
2933 * gnome_canvas_set_scroll_region:
2934 * @canvas: A canvas.
2935 * @x1: Leftmost limit of the scrolling region.
2936 * @y1: Upper limit of the scrolling region.
2937 * @x2: Rightmost limit of the scrolling region.
2938 * @y2: Lower limit of the scrolling region.
2940 * Sets the scrolling region of a canvas to the specified rectangle. The canvas
2941 * will then be able to scroll only within this region. The view of the canvas
2942 * is adjusted as appropriate to display as much of the new region as possible.
2944 void
2945 gnome_canvas_set_scroll_region (GnomeCanvas *canvas,
2946 gdouble x1,
2947 gdouble y1,
2948 gdouble x2,
2949 gdouble y2)
2951 GtkScrollable *scrollable;
2952 GtkAdjustment *hadjustment;
2953 GtkAdjustment *vadjustment;
2954 gdouble hadjustment_value;
2955 gdouble vadjustment_value;
2956 gdouble wxofs, wyofs;
2957 gint xofs, yofs;
2959 g_return_if_fail (GNOME_IS_CANVAS (canvas));
2961 scrollable = GTK_SCROLLABLE (canvas);
2962 hadjustment = gtk_scrollable_get_hadjustment (scrollable);
2963 vadjustment = gtk_scrollable_get_vadjustment (scrollable);
2965 hadjustment_value = gtk_adjustment_get_value (hadjustment);
2966 vadjustment_value = gtk_adjustment_get_value (vadjustment);
2969 * Set the new scrolling region. If possible, do not move the
2970 * visible contents of the canvas.
2973 gnome_canvas_c2w (
2974 canvas,
2975 hadjustment_value + canvas->zoom_xofs,
2976 vadjustment_value + canvas->zoom_yofs,
2977 &wxofs, &wyofs);
2979 canvas->scroll_x1 = x1;
2980 canvas->scroll_y1 = y1;
2981 canvas->scroll_x2 = x2;
2982 canvas->scroll_y2 = y2;
2984 gnome_canvas_w2c (canvas, wxofs, wyofs, &xofs, &yofs);
2986 scroll_to (canvas, xofs, yofs);
2988 canvas->need_repick = TRUE;
2989 #if 0
2990 /* todo: should be requesting update */
2991 (* GNOME_CANVAS_ITEM_CLASS (canvas->root->object.class)->update) (
2992 canvas->root, NULL, NULL, 0);
2993 #endif
2997 * gnome_canvas_get_scroll_region:
2998 * @canvas: A canvas.
2999 * @x1: Leftmost limit of the scrolling region (return value).
3000 * @y1: Upper limit of the scrolling region (return value).
3001 * @x2: Rightmost limit of the scrolling region (return value).
3002 * @y2: Lower limit of the scrolling region (return value).
3004 * Queries the scrolling region of a canvas.
3006 void
3007 gnome_canvas_get_scroll_region (GnomeCanvas *canvas,
3008 gdouble *x1,
3009 gdouble *y1,
3010 gdouble *x2,
3011 gdouble *y2)
3013 g_return_if_fail (GNOME_IS_CANVAS (canvas));
3015 if (x1)
3016 *x1 = canvas->scroll_x1;
3018 if (y1)
3019 *y1 = canvas->scroll_y1;
3021 if (x2)
3022 *x2 = canvas->scroll_x2;
3024 if (y2)
3025 *y2 = canvas->scroll_y2;
3029 * gnome_canvas_scroll_to:
3030 * @canvas: A canvas.
3031 * @cx: Horizontal scrolling offset in canvas pixel units.
3032 * @cy: Vertical scrolling offset in canvas pixel units.
3034 * Makes a canvas scroll to the specified offsets, given in canvas pixel units.
3035 * The canvas will adjust the view so that it is not outside the scrolling
3036 * region. This function is typically not used, as it is better to hook
3037 * scrollbars to the canvas layout's scrolling adjusments.
3039 void
3040 gnome_canvas_scroll_to (GnomeCanvas *canvas,
3041 gint cx,
3042 gint cy)
3044 g_return_if_fail (GNOME_IS_CANVAS (canvas));
3046 scroll_to (canvas, cx, cy);
3050 * gnome_canvas_get_scroll_offsets:
3051 * @canvas: A canvas.
3052 * @cx: Horizontal scrolling offset (return value).
3053 * @cy: Vertical scrolling offset (return value).
3055 * Queries the scrolling offsets of a canvas. The values are returned in canvas
3056 * pixel units.
3058 void
3059 gnome_canvas_get_scroll_offsets (GnomeCanvas *canvas,
3060 gint *cx,
3061 gint *cy)
3063 GtkAdjustment *adjustment;
3064 GtkScrollable *scrollable;
3066 g_return_if_fail (GNOME_IS_CANVAS (canvas));
3068 scrollable = GTK_SCROLLABLE (canvas);
3070 if (cx) {
3071 adjustment = gtk_scrollable_get_hadjustment (scrollable);
3072 *cx = (gint) gtk_adjustment_get_value (adjustment);
3075 if (cy) {
3076 adjustment = gtk_scrollable_get_vadjustment (scrollable);
3077 *cy = (gint) gtk_adjustment_get_value (adjustment);
3082 * gnome_canvas_get_item_at:
3083 * @canvas: A canvas.
3084 * @x: X position in world coordinates.
3085 * @y: Y position in world coordinates.
3087 * Looks for the item that is under the specified position, which must be
3088 * specified in world coordinates.
3090 * Return value: The sought item, or NULL if no item is at the specified
3091 * coordinates.
3093 GnomeCanvasItem *
3094 gnome_canvas_get_item_at (GnomeCanvas *canvas,
3095 gdouble x,
3096 gdouble y)
3098 gint cx, cy;
3100 g_return_val_if_fail (GNOME_IS_CANVAS (canvas), NULL);
3102 gnome_canvas_w2c (canvas, x, y, &cx, &cy);
3104 return gnome_canvas_item_invoke_point (canvas->root, x, y, cx, cy);
3107 /* Queues an update of the canvas */
3108 static void
3109 gnome_canvas_request_update (GnomeCanvas *canvas)
3111 GNOME_CANVAS_GET_CLASS (canvas)->request_update (canvas);
3114 static void
3115 gnome_canvas_request_update_real (GnomeCanvas *canvas)
3117 if (canvas->need_update)
3118 return;
3120 canvas->need_update = TRUE;
3121 if (gtk_widget_get_mapped ((GtkWidget *) canvas))
3122 add_idle (canvas);
3125 static inline void
3126 get_visible_rect (GnomeCanvas *canvas,
3127 GdkRectangle *visible)
3129 GtkAllocation allocation;
3130 GtkScrollable *scrollable;
3131 GtkAdjustment *hadjustment;
3132 GtkAdjustment *vadjustment;
3133 gdouble hadjustment_value;
3134 gdouble vadjustment_value;
3136 gtk_widget_get_allocation (GTK_WIDGET (canvas), &allocation);
3138 scrollable = GTK_SCROLLABLE (canvas);
3139 hadjustment = gtk_scrollable_get_hadjustment (scrollable);
3140 vadjustment = gtk_scrollable_get_vadjustment (scrollable);
3142 hadjustment_value = gtk_adjustment_get_value (hadjustment);
3143 vadjustment_value = gtk_adjustment_get_value (vadjustment);
3145 visible->x = hadjustment_value - canvas->zoom_xofs;
3146 visible->y = vadjustment_value - canvas->zoom_yofs;
3147 visible->width = allocation.width;
3148 visible->height = allocation.height;
3152 * gnome_canvas_request_redraw:
3153 * @canvas: A canvas.
3154 * @x1: Leftmost coordinate of the rectangle to be redrawn.
3155 * @y1: Upper coordinate of the rectangle to be redrawn.
3156 * @x2: Rightmost coordinate of the rectangle to be redrawn, plus 1.
3157 * @y2: Lower coordinate of the rectangle to be redrawn, plus 1.
3159 * Convenience function that informs a canvas that the specified rectangle needs
3160 * to be repainted. The rectangle includes @x1 and @y1, but not @x2 and @y2. To
3161 * be used only by item implementations.
3163 void
3164 gnome_canvas_request_redraw (GnomeCanvas *canvas,
3165 gint x1,
3166 gint y1,
3167 gint x2,
3168 gint y2)
3170 GdkRectangle area, clip;
3172 g_return_if_fail (GNOME_IS_CANVAS (canvas));
3174 if (!gtk_widget_is_drawable (GTK_WIDGET (canvas)) || (x1 >= x2) || (y1 >= y2))
3175 return;
3177 area.x = x1;
3178 area.y = y1;
3179 area.width = x2 - x1 + 1;
3180 area.height = y2 - y1 + 1;
3182 get_visible_rect (canvas, &clip);
3183 if (!gdk_rectangle_intersect (&area, &clip, &area))
3184 return;
3186 gdk_window_invalidate_rect (
3187 gtk_layout_get_bin_window (GTK_LAYOUT (canvas)),
3188 &area, FALSE);
3192 * gnome_canvas_w2c_matrix:
3193 * @canvas: A canvas.
3194 * @matrix: (out): matrix to initialize
3196 * Gets the transformtion matrix that converts from world coordinates to canvas
3197 * pixel coordinates.
3199 void
3200 gnome_canvas_w2c_matrix (GnomeCanvas *canvas,
3201 cairo_matrix_t *matrix)
3203 g_return_if_fail (GNOME_IS_CANVAS (canvas));
3204 g_return_if_fail (matrix != NULL);
3206 cairo_matrix_init_translate (
3207 matrix, -canvas->scroll_x1, -canvas->scroll_y1);
3211 * gnome_canvas_c2w_matrix:
3212 * @canvas: A canvas.
3213 * @matrix: (out): matrix to initialize
3215 * Gets the transformtion matrix that converts from canvas pixel coordinates to
3216 * world coordinates.
3218 void
3219 gnome_canvas_c2w_matrix (GnomeCanvas *canvas,
3220 cairo_matrix_t *matrix)
3222 g_return_if_fail (GNOME_IS_CANVAS (canvas));
3223 g_return_if_fail (matrix != NULL);
3225 cairo_matrix_init_translate (
3226 matrix, canvas->scroll_x1, canvas->scroll_y1);
3230 * gnome_canvas_w2c:
3231 * @canvas: A canvas.
3232 * @wx: World X coordinate.
3233 * @wy: World Y coordinate.
3234 * @cx: X pixel coordinate (return value).
3235 * @cy: Y pixel coordinate (return value).
3237 * Converts world coordinates into canvas pixel coordinates.
3239 void
3240 gnome_canvas_w2c (GnomeCanvas *canvas,
3241 gdouble wx,
3242 gdouble wy,
3243 gint *cx,
3244 gint *cy)
3246 cairo_matrix_t w2c;
3248 g_return_if_fail (GNOME_IS_CANVAS (canvas));
3250 gnome_canvas_w2c_matrix (canvas, &w2c);
3251 cairo_matrix_transform_point (&w2c, &wx, &wy);
3253 if (cx)
3254 *cx = floor (wx + 0.5);
3255 if (cy)
3256 *cy = floor (wy + 0.5);
3260 * gnome_canvas_w2c_d:
3261 * @canvas: A canvas.
3262 * @wx: World X coordinate.
3263 * @wy: World Y coordinate.
3264 * @cx: X pixel coordinate (return value).
3265 * @cy: Y pixel coordinate (return value).
3267 * Converts world coordinates into canvas pixel coordinates. This
3268 * version returns coordinates in floating point coordinates, for
3269 * greater precision.
3271 void
3272 gnome_canvas_w2c_d (GnomeCanvas *canvas,
3273 gdouble wx,
3274 gdouble wy,
3275 gdouble *cx,
3276 gdouble *cy)
3278 cairo_matrix_t w2c;
3280 g_return_if_fail (GNOME_IS_CANVAS (canvas));
3282 gnome_canvas_w2c_matrix (canvas, &w2c);
3283 cairo_matrix_transform_point (&w2c, &wx, &wy);
3285 if (cx)
3286 *cx = wx;
3287 if (cy)
3288 *cy = wy;
3292 * gnome_canvas_c2w:
3293 * @canvas: A canvas.
3294 * @cx: Canvas pixel X coordinate.
3295 * @cy: Canvas pixel Y coordinate.
3296 * @wx: X world coordinate (return value).
3297 * @wy: Y world coordinate (return value).
3299 * Converts canvas pixel coordinates to world coordinates.
3301 void
3302 gnome_canvas_c2w (GnomeCanvas *canvas,
3303 gint cx,
3304 gint cy,
3305 gdouble *wx,
3306 gdouble *wy)
3308 cairo_matrix_t c2w;
3309 gdouble x, y;
3311 g_return_if_fail (GNOME_IS_CANVAS (canvas));
3313 x = cx;
3314 y = cy;
3315 gnome_canvas_c2w_matrix (canvas, &c2w);
3316 cairo_matrix_transform_point (&c2w, &x, &y);
3318 if (wx)
3319 *wx = x;
3320 if (wy)
3321 *wy = y;
3325 * gnome_canvas_window_to_world:
3326 * @canvas: A canvas.
3327 * @winx: Window-relative X coordinate.
3328 * @winy: Window-relative Y coordinate.
3329 * @worldx: X world coordinate (return value).
3330 * @worldy: Y world coordinate (return value).
3332 * Converts window-relative coordinates into world coordinates. You can use
3333 * this when you need to convert mouse coordinates into world coordinates, for
3334 * example.
3336 void
3337 gnome_canvas_window_to_world (GnomeCanvas *canvas,
3338 gdouble winx,
3339 gdouble winy,
3340 gdouble *worldx,
3341 gdouble *worldy)
3343 g_return_if_fail (GNOME_IS_CANVAS (canvas));
3345 if (worldx)
3346 *worldx = canvas->scroll_x1 + (winx - canvas->zoom_xofs);
3348 if (worldy)
3349 *worldy = canvas->scroll_y1 + (winy - canvas->zoom_yofs);
3353 * gnome_canvas_world_to_window:
3354 * @canvas: A canvas.
3355 * @worldx: World X coordinate.
3356 * @worldy: World Y coordinate.
3357 * @winx: X window-relative coordinate.
3358 * @winy: Y window-relative coordinate.
3360 * Converts world coordinates into window-relative coordinates.
3362 void
3363 gnome_canvas_world_to_window (GnomeCanvas *canvas,
3364 gdouble worldx,
3365 gdouble worldy,
3366 gdouble *winx,
3367 gdouble *winy)
3369 g_return_if_fail (GNOME_IS_CANVAS (canvas));
3371 if (winx)
3372 *winx = (worldx - canvas->scroll_x1) + canvas->zoom_xofs;
3374 if (winy)
3375 *winy = (worldy - canvas->scroll_y1) + canvas->zoom_yofs;
3378 static gboolean
3379 boolean_handled_accumulator (GSignalInvocationHint *ihint,
3380 GValue *return_accu,
3381 const GValue *handler_return,
3382 gpointer dummy)
3384 gboolean continue_emission;
3385 gboolean signal_handled;
3387 signal_handled = g_value_get_boolean (handler_return);
3388 g_value_set_boolean (return_accu, signal_handled);
3389 continue_emission = !signal_handled;
3391 return continue_emission;
3394 /* Class initialization function for GnomeCanvasItemClass */
3395 static void
3396 gnome_canvas_item_class_init (GnomeCanvasItemClass *class)
3398 GObjectClass *gobject_class;
3400 gobject_class = (GObjectClass *) class;
3402 gobject_class->set_property = gnome_canvas_item_set_property;
3403 gobject_class->get_property = gnome_canvas_item_get_property;
3405 g_object_class_install_property (
3406 gobject_class,
3407 ITEM_PROP_PARENT,
3408 g_param_spec_object (
3409 "parent",
3410 NULL,
3411 NULL,
3412 GNOME_TYPE_CANVAS_ITEM,
3413 G_PARAM_READABLE |
3414 G_PARAM_WRITABLE));
3416 item_signals[ITEM_EVENT] = g_signal_new (
3417 "event",
3418 G_TYPE_FROM_CLASS (class),
3419 G_SIGNAL_RUN_LAST,
3420 G_STRUCT_OFFSET (GnomeCanvasItemClass, event),
3421 boolean_handled_accumulator, NULL, NULL,
3422 G_TYPE_BOOLEAN, 1,
3423 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
3425 gobject_class->dispose = gnome_canvas_item_dispose;
3427 class->update = gnome_canvas_item_update;
3428 class->realize = gnome_canvas_item_realize;
3429 class->unrealize = gnome_canvas_item_unrealize;
3430 class->map = gnome_canvas_item_map;
3431 class->unmap = gnome_canvas_item_unmap;
3432 class->dispose = gnome_canvas_item_dispose_item;
3433 class->draw = gnome_canvas_item_draw;
3434 class->point = gnome_canvas_item_point;
3435 class->bounds = gnome_canvas_item_bounds;
3436 class->event = gnome_canvas_item_event;