Bug 792688 - Failed mail print operation causes crash
[evolution.git] / src / libgnomecanvas / gnome-canvas.c
blob6412673eea246796ff1a015c640e3a8a81b73649
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 #include "evolution-config.h"
79 #include <math.h>
80 #include <string.h>
81 #include <stdio.h>
82 #include <gdk/gdkprivate.h>
83 #include <gtk/gtk.h>
84 #include <cairo-gobject.h>
85 #include "gailcanvas.h"
86 #include "gnome-canvas.h"
87 #include "gnome-canvas-i18n.h"
88 #include "gnome-canvas-util.h"
90 /* We must run our idle update handler *before* GDK wants to redraw. */
91 #define CANVAS_IDLE_PRIORITY (GDK_PRIORITY_REDRAW - 5)
93 static void gnome_canvas_request_update (GnomeCanvas *canvas);
94 static void group_add (GnomeCanvasGroup *group,
95 GnomeCanvasItem *item);
96 static void group_remove (GnomeCanvasGroup *group,
97 GnomeCanvasItem *item);
98 static void add_idle (GnomeCanvas *canvas);
100 /*** GnomeCanvasItem ***/
102 /* Some convenience stuff */
103 #define GCI_UPDATE_MASK \
104 (GNOME_CANVAS_UPDATE_REQUESTED | \
105 GNOME_CANVAS_UPDATE_AFFINE | \
106 GNOME_CANVAS_UPDATE_CLIP | \
107 GNOME_CANVAS_UPDATE_VISIBILITY)
108 #define GCI_EPSILON 1e-18
109 #define GCI_PRINT_MATRIX(s,a) \
110 g_print ("%s %g %g %g %g %g %g\n", \
111 s, (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5])
113 enum {
114 ITEM_PROP_0,
115 ITEM_PROP_PARENT
118 enum {
119 ITEM_EVENT,
120 ITEM_LAST_SIGNAL
123 static gint emit_event (GnomeCanvas *canvas, GdkEvent *event);
125 static guint item_signals[ITEM_LAST_SIGNAL];
127 G_DEFINE_TYPE (
128 GnomeCanvasItem,
129 gnome_canvas_item,
130 G_TYPE_INITIALLY_UNOWNED)
132 /* Object initialization function for GnomeCanvasItem */
133 static void
134 gnome_canvas_item_init (GnomeCanvasItem *item)
136 item->flags |= GNOME_CANVAS_ITEM_VISIBLE;
138 cairo_matrix_init_identity (&item->matrix);
142 * gnome_canvas_item_new:
143 * @parent: The parent group for the new item.
144 * @type: The object type of the item.
145 * @first_arg_name: A list of object argument name/value pairs, NULL-terminated,
146 * used to configure the item. For example, "fill_color", "black",
147 * "width_units", 5.0, NULL.
148 * @Varargs:
150 * Creates a new canvas item with @parent as its parent group. The item is
151 * created at the top of its parent's stack, and starts up as visible. The item
152 * is of the specified @type, for example, it can be
153 * gnome_canvas_rect_get_type(). The list of object arguments/value pairs is
154 * used to configure the item. If you need to pass construct time parameters, you
155 * should use g_object_new() to pass the parameters and
156 * gnome_canvas_item_construct() to set up the canvas item.
158 * Return value: The newly-created item.
160 GnomeCanvasItem *
161 gnome_canvas_item_new (GnomeCanvasGroup *parent,
162 GType type,
163 const gchar *first_arg_name, ...)
165 GnomeCanvasItem *item;
166 va_list args;
168 g_return_val_if_fail (GNOME_IS_CANVAS_GROUP (parent), NULL);
169 g_return_val_if_fail (g_type_is_a (type, gnome_canvas_item_get_type ()), NULL);
171 item = GNOME_CANVAS_ITEM (g_object_new (type, NULL));
173 va_start (args, first_arg_name);
174 gnome_canvas_item_construct (item, parent, first_arg_name, args);
175 va_end (args);
177 return item;
180 /* Performs post-creation operations on a canvas item (adding it to its parent
181 * group, etc.)
183 static void
184 item_post_create_setup (GnomeCanvasItem *item)
186 group_add (GNOME_CANVAS_GROUP (item->parent), item);
188 gnome_canvas_request_redraw (
189 item->canvas, item->x1, item->y1, item->x2 + 1, item->y2 + 1);
190 item->canvas->need_repick = TRUE;
193 /* Set_property handler for canvas items */
194 static void
195 gnome_canvas_item_set_property (GObject *gobject,
196 guint property_id,
197 const GValue *value,
198 GParamSpec *pspec)
200 GnomeCanvasItem *item;
202 g_return_if_fail (GNOME_IS_CANVAS_ITEM (gobject));
204 item = GNOME_CANVAS_ITEM (gobject);
206 switch (property_id) {
207 case ITEM_PROP_PARENT:
208 if (item->parent != NULL) {
209 g_warning ("Cannot set `parent' argument after item has "
210 "already been constructed.");
211 } else if (g_value_get_object (value)) {
212 item->parent = GNOME_CANVAS_ITEM (g_value_get_object (value));
213 item->canvas = item->parent->canvas;
214 item_post_create_setup (item);
216 break;
217 default:
218 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
219 break;
223 /* Get_property handler for canvas items */
224 static void
225 gnome_canvas_item_get_property (GObject *gobject,
226 guint property_id,
227 GValue *value,
228 GParamSpec *pspec)
230 GnomeCanvasItem *item;
232 g_return_if_fail (GNOME_IS_CANVAS_ITEM (gobject));
234 item = GNOME_CANVAS_ITEM (gobject);
236 switch (property_id) {
237 case ITEM_PROP_PARENT:
238 g_value_set_object (value, item->parent);
239 break;
241 default:
242 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
243 break;
248 * gnome_canvas_item_construct:
249 * @item: An unconstructed canvas item.
250 * @parent: The parent group for the item.
251 * @first_arg_name: The name of the first argument for configuring the item.
252 * @args: The list of arguments used to configure the item.
254 * Constructs a canvas item; meant for use only by item implementations.
256 void
257 gnome_canvas_item_construct (GnomeCanvasItem *item,
258 GnomeCanvasGroup *parent,
259 const gchar *first_arg_name,
260 va_list args)
262 g_return_if_fail (GNOME_IS_CANVAS_GROUP (parent));
263 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
265 item->parent = GNOME_CANVAS_ITEM (parent);
266 item->canvas = item->parent->canvas;
268 g_object_set_valist (G_OBJECT (item), first_arg_name, args);
270 item_post_create_setup (item);
273 /* If the item is visible, requests a redraw of it. */
274 static void
275 redraw_if_visible (GnomeCanvasItem *item)
277 if (item->flags & GNOME_CANVAS_ITEM_VISIBLE)
278 gnome_canvas_request_redraw (
279 item->canvas, item->x1, item->y1,
280 item->x2 + 1, item->y2 + 1);
283 /* Standard object dispose function for canvas items */
284 static void
285 gnome_canvas_item_dispose (GObject *object)
287 GnomeCanvasItem *item;
289 g_return_if_fail (GNOME_IS_CANVAS_ITEM (object));
291 item = GNOME_CANVAS_ITEM (object);
293 if (item->canvas)
294 redraw_if_visible (item);
296 /* Make the canvas forget about us */
298 if (item->canvas && item == item->canvas->current_item) {
299 item->canvas->current_item = NULL;
300 item->canvas->need_repick = TRUE;
303 if (item->canvas && item == item->canvas->new_current_item) {
304 item->canvas->new_current_item = NULL;
305 item->canvas->need_repick = TRUE;
308 if (item->canvas && item == item->canvas->grabbed_item) {
309 item->canvas->grabbed_item = NULL;
311 gdk_device_ungrab (
312 item->canvas->grabbed_device, GDK_CURRENT_TIME);
313 g_object_unref (item->canvas->grabbed_device);
314 item->canvas->grabbed_device = NULL;
317 if (item->canvas && item == item->canvas->focused_item)
318 item->canvas->focused_item = NULL;
320 /* Normal dispose stuff */
322 if (item->flags & GNOME_CANVAS_ITEM_MAPPED)
323 (* GNOME_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
325 if (item->flags & GNOME_CANVAS_ITEM_REALIZED)
326 (* GNOME_CANVAS_ITEM_GET_CLASS (item)->unrealize) (item);
328 if (item->parent)
329 group_remove (GNOME_CANVAS_GROUP (item->parent), item);
331 if (GNOME_CANVAS_ITEM_GET_CLASS (item)->dispose)
332 GNOME_CANVAS_ITEM_GET_CLASS (item)->dispose (item);
334 G_OBJECT_CLASS (gnome_canvas_item_parent_class)->dispose (object);
335 /* items should remove any reference to item->canvas after the
336 * first ::dispose */
337 item->canvas = NULL;
340 /* Update handler for canvas items */
341 static void
342 gnome_canvas_item_update (GnomeCanvasItem *item,
343 const cairo_matrix_t *matrix,
344 gint flags)
346 item->flags &= ~GNOME_CANVAS_ITEM_NEED_UPDATE;
347 item->flags &= ~GNOME_CANVAS_ITEM_NEED_AFFINE;
348 item->flags &= ~GNOME_CANVAS_ITEM_NEED_CLIP;
349 item->flags &= ~GNOME_CANVAS_ITEM_NEED_VIS;
352 /* Realize handler for canvas items */
353 static void
354 gnome_canvas_item_realize (GnomeCanvasItem *item)
356 item->flags |= GNOME_CANVAS_ITEM_REALIZED;
358 gnome_canvas_item_request_update (item);
361 /* Unrealize handler for canvas items */
362 static void
363 gnome_canvas_item_unrealize (GnomeCanvasItem *item)
365 item->flags &= ~GNOME_CANVAS_ITEM_REALIZED;
368 /* Map handler for canvas items */
369 static void
370 gnome_canvas_item_map (GnomeCanvasItem *item)
372 item->flags |= GNOME_CANVAS_ITEM_MAPPED;
375 /* Unmap handler for canvas items */
376 static void
377 gnome_canvas_item_unmap (GnomeCanvasItem *item)
379 item->flags &= ~GNOME_CANVAS_ITEM_MAPPED;
382 /* Dispose handler for canvas items */
383 static void
384 gnome_canvas_item_dispose_item (GnomeCanvasItem *item)
386 /* Placeholder so subclasses can safely chain up. */
389 static void
390 gnome_canvas_item_draw (GnomeCanvasItem *item,
391 cairo_t *cr,
392 gint x,
393 gint y,
394 gint width,
395 gint height)
397 /* Placeholder so subclasses can safely chain up. */
400 static GnomeCanvasItem *
401 gnome_canvas_item_point (GnomeCanvasItem *item,
402 gdouble x,
403 gdouble y,
404 gint cx,
405 gint cy)
407 /* Placeholder so subclasses can safely chain up. */
409 return NULL;
412 static void
413 gnome_canvas_item_bounds (GnomeCanvasItem *item,
414 gdouble *x1,
415 gdouble *y1,
416 gdouble *x2,
417 gdouble *y2)
419 /* Placeholder so subclasses can safely chain up. */
422 static gboolean
423 gnome_canvas_item_event (GnomeCanvasItem *item,
424 GdkEvent *event)
426 /* Placeholder so subclasses can safely chain up. */
428 return FALSE; /* event was not handled */
432 * This routine invokes the update method of the item
433 * Please notice, that we take parent to canvas pixel matrix as argument
434 * unlike virtual method ::update, whose argument is item 2 canvas pixel
435 * matrix
437 * I will try to force somewhat meaningful naming for affines (Lauris)
438 * General naming rule is FROM2TO, where FROM and TO are abbreviations
439 * So p2cpx is Parent2CanvasPixel and i2cpx is Item2CanvasPixel
440 * I hope that this helps to keep track of what really happens
444 static void
445 gnome_canvas_item_invoke_update (GnomeCanvasItem *item,
446 const cairo_matrix_t *p2c,
447 gint flags)
449 gint child_flags;
450 cairo_matrix_t i2c;
452 child_flags = flags;
453 if (!(item->flags & GNOME_CANVAS_ITEM_VISIBLE))
454 child_flags &= ~GNOME_CANVAS_UPDATE_IS_VISIBLE;
456 /* Calculate actual item transformation matrix */
458 cairo_matrix_multiply (&i2c, &item->matrix, p2c);
460 /* apply object flags to child flags */
462 child_flags &= ~GNOME_CANVAS_UPDATE_REQUESTED;
464 if (item->flags & GNOME_CANVAS_ITEM_NEED_UPDATE)
465 child_flags |= GNOME_CANVAS_UPDATE_REQUESTED;
467 if (item->flags & GNOME_CANVAS_ITEM_NEED_AFFINE)
468 child_flags |= GNOME_CANVAS_UPDATE_AFFINE;
470 if (item->flags & GNOME_CANVAS_ITEM_NEED_CLIP)
471 child_flags |= GNOME_CANVAS_UPDATE_CLIP;
473 if (item->flags & GNOME_CANVAS_ITEM_NEED_VIS)
474 child_flags |= GNOME_CANVAS_UPDATE_VISIBILITY;
476 if (child_flags & GCI_UPDATE_MASK) {
477 if (GNOME_CANVAS_ITEM_GET_CLASS (item)->update)
478 GNOME_CANVAS_ITEM_GET_CLASS (item)->update (item, &i2c, child_flags);
483 * This routine invokes the point method of the item.
484 * The arguments x, y should be in the parent item local coordinates.
486 * This is potentially evil, as we are relying on matrix inversion (Lauris)
489 static GnomeCanvasItem *
490 gnome_canvas_item_invoke_point (GnomeCanvasItem *item,
491 gdouble x,
492 gdouble y,
493 gint cx,
494 gint cy)
496 cairo_matrix_t inverse;
498 /* Calculate x & y in item local coordinates */
499 inverse = item->matrix;
500 if (cairo_matrix_invert (&inverse) != CAIRO_STATUS_SUCCESS)
501 return NULL;
503 cairo_matrix_transform_point (&inverse, &x, &y);
505 if (GNOME_CANVAS_ITEM_GET_CLASS (item)->point)
506 return GNOME_CANVAS_ITEM_GET_CLASS (item)->point (item, x, y, cx, cy);
508 return NULL;
512 * gnome_canvas_item_set:
513 * @item: A canvas item.
514 * @first_arg_name: The list of object argument name/value pairs used to
515 * configure the item.
516 * @Varargs:
518 * Configures a canvas item. The arguments in the item are set to the
519 * specified values, and the item is repainted as appropriate.
521 void
522 gnome_canvas_item_set (GnomeCanvasItem *item,
523 const gchar *first_arg_name,
524 ...)
526 va_list args;
528 va_start (args, first_arg_name);
529 gnome_canvas_item_set_valist (item, first_arg_name, args);
530 va_end (args);
534 * gnome_canvas_item_set_valist:
535 * @item: A canvas item.
536 * @first_arg_name: The name of the first argument used to configure the item.
537 * @args: The list of object argument name/value pairs used to configure the item.
539 * Configures a canvas item. The arguments in the item are set to the specified
540 * values, and the item is repainted as appropriate.
542 void
543 gnome_canvas_item_set_valist (GnomeCanvasItem *item,
544 const gchar *first_arg_name,
545 va_list args)
547 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
549 g_object_set_valist (G_OBJECT (item), first_arg_name, args);
551 item->canvas->need_repick = TRUE;
555 * gnome_canvas_item_transform:
556 * @item: A canvas item.
557 * @matrix: An affine transformation matrix.
559 * Combines the specified affine transformation matrix with the item's current
560 * transformation.
562 void
563 gnome_canvas_item_transform (GnomeCanvasItem *item,
564 const cairo_matrix_t *matrix)
566 cairo_matrix_t i2p;
568 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
569 g_return_if_fail (matrix != NULL);
571 /* Calculate actual item transformation matrix */
572 cairo_matrix_multiply (&i2p, matrix, &item->matrix);
574 gnome_canvas_item_set_matrix (item, &i2p);
578 * gnome_canvas_item_set_matrix:
579 * @item: A canvas item.
580 * @matrix: An affine transformation matrix or %NULL for the identity matrix.
582 * Makes the item's affine transformation matrix be equal to the specified
583 * matrix. NULL is treated as identity.
585 void
586 gnome_canvas_item_set_matrix (GnomeCanvasItem *item,
587 const cairo_matrix_t *matrix)
589 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
591 if (matrix) {
592 item->matrix = *matrix;
593 } else {
594 cairo_matrix_init_identity (&item->matrix);
597 if (!(item->flags & GNOME_CANVAS_ITEM_NEED_AFFINE)) {
598 /* Request update */
599 item->flags |= GNOME_CANVAS_ITEM_NEED_AFFINE;
600 gnome_canvas_item_request_update (item);
603 item->canvas->need_repick = TRUE;
607 * gnome_canvas_item_move:
608 * @item: A canvas item.
609 * @dx: Horizontal offset.
610 * @dy: Vertical offset.
612 * Moves a canvas item by creating an affine transformation matrix for
613 * translation by using the specified values. This happens in item
614 * local coordinate system, so if you have nontrivial transform, it
615 * most probably does not do, what you want.
617 void
618 gnome_canvas_item_move (GnomeCanvasItem *item,
619 gdouble dx,
620 gdouble dy)
622 cairo_matrix_t translate;
624 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
626 cairo_matrix_init_translate (&translate, dx, dy);
628 gnome_canvas_item_transform (item, &translate);
631 /* Convenience function to reorder items in a group's child list. This puts the
632 * specified link after the "before" link. Returns TRUE if the list was changed.
634 static gboolean
635 put_item_after (GList *link,
636 GList *before)
638 GnomeCanvasGroup *parent;
639 GList *old_before, *old_after;
640 GList *after;
642 parent = GNOME_CANVAS_GROUP (GNOME_CANVAS_ITEM (link->data)->parent);
644 if (before)
645 after = before->next;
646 else
647 after = parent->item_list;
649 if (before == link || after == link)
650 return FALSE;
652 /* Unlink */
654 old_before = link->prev;
655 old_after = link->next;
657 if (old_before)
658 old_before->next = old_after;
659 else
660 parent->item_list = old_after;
662 if (old_after)
663 old_after->prev = old_before;
664 else
665 parent->item_list_end = old_before;
667 /* Relink */
669 link->prev = before;
670 if (before)
671 before->next = link;
672 else
673 parent->item_list = link;
675 link->next = after;
676 if (after)
677 after->prev = link;
678 else
679 parent->item_list_end = link;
681 return TRUE;
685 * gnome_canvas_item_raise:
686 * @item: A canvas item.
687 * @positions: Number of steps to raise the item.
689 * Raises the item in its parent's stack by the specified number of positions.
690 * If the number of positions is greater than the distance to the top of the
691 * stack, then the item is put at the top.
693 void
694 gnome_canvas_item_raise (GnomeCanvasItem *item,
695 gint positions)
697 GList *link, *before;
698 GnomeCanvasGroup *parent;
700 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
701 g_return_if_fail (positions >= 0);
703 if (!item->parent || positions == 0)
704 return;
706 parent = GNOME_CANVAS_GROUP (item->parent);
707 link = g_list_find (parent->item_list, item);
708 g_return_if_fail (link != NULL);
710 for (before = link; positions && before; positions--)
711 before = before->next;
713 if (!before)
714 before = parent->item_list_end;
716 if (put_item_after (link, before)) {
717 redraw_if_visible (item);
718 item->canvas->need_repick = TRUE;
723 * gnome_canvas_item_lower:
724 * @item: A canvas item.
725 * @positions: Number of steps to lower the item.
727 * Lowers the item in its parent's stack by the specified number of positions.
728 * If the number of positions is greater than the distance to the bottom of the
729 * stack, then the item is put at the bottom.
731 void
732 gnome_canvas_item_lower (GnomeCanvasItem *item,
733 gint positions)
735 GList *link, *before;
736 GnomeCanvasGroup *parent;
738 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
739 g_return_if_fail (positions >= 1);
741 if (!item->parent || positions == 0)
742 return;
744 parent = GNOME_CANVAS_GROUP (item->parent);
745 link = g_list_find (parent->item_list, item);
746 g_return_if_fail (link != NULL);
748 if (link->prev)
749 for (before = link->prev; positions && before; positions--)
750 before = before->prev;
751 else
752 before = NULL;
754 if (put_item_after (link, before)) {
755 redraw_if_visible (item);
756 item->canvas->need_repick = TRUE;
761 * gnome_canvas_item_raise_to_top:
762 * @item: A canvas item.
764 * Raises an item to the top of its parent's stack.
766 void
767 gnome_canvas_item_raise_to_top (GnomeCanvasItem *item)
769 GList *link;
770 GnomeCanvasGroup *parent;
772 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
774 if (!item->parent)
775 return;
777 parent = GNOME_CANVAS_GROUP (item->parent);
778 link = g_list_find (parent->item_list, item);
779 g_return_if_fail (link != NULL);
781 if (put_item_after (link, parent->item_list_end)) {
782 redraw_if_visible (item);
783 item->canvas->need_repick = TRUE;
788 * gnome_canvas_item_lower_to_bottom:
789 * @item: A canvas item.
791 * Lowers an item to the bottom of its parent's stack.
793 void
794 gnome_canvas_item_lower_to_bottom (GnomeCanvasItem *item)
796 GList *link;
797 GnomeCanvasGroup *parent;
799 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
801 if (!item->parent)
802 return;
804 parent = GNOME_CANVAS_GROUP (item->parent);
805 link = g_list_find (parent->item_list, item);
806 g_return_if_fail (link != NULL);
808 if (put_item_after (link, NULL)) {
809 redraw_if_visible (item);
810 item->canvas->need_repick = TRUE;
815 * gnome_canvas_item_show:
816 * @item: A canvas item.
818 * Shows a canvas item. If the item was already shown, then no action is taken.
820 void
821 gnome_canvas_item_show (GnomeCanvasItem *item)
823 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
825 if (!(item->flags & GNOME_CANVAS_ITEM_VISIBLE)) {
826 item->flags |= GNOME_CANVAS_ITEM_VISIBLE;
827 gnome_canvas_request_redraw (
828 item->canvas, item->x1, item->y1,
829 item->x2 + 1, item->y2 + 1);
830 item->canvas->need_repick = TRUE;
835 * gnome_canvas_item_hide:
836 * @item: A canvas item.
838 * Hides a canvas item. If the item was already hidden, then no action is
839 * taken.
841 void
842 gnome_canvas_item_hide (GnomeCanvasItem *item)
844 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
846 if (item->flags & GNOME_CANVAS_ITEM_VISIBLE) {
847 item->flags &= ~GNOME_CANVAS_ITEM_VISIBLE;
848 gnome_canvas_request_redraw (
849 item->canvas, item->x1, item->y1,
850 item->x2 + 1, item->y2 + 1);
851 item->canvas->need_repick = TRUE;
856 * gnome_canvas_item_grab:
857 * @item: A canvas item.
858 * @event_mask: Mask of events that will be sent to this item.
859 * @cursor: If non-NULL, the cursor that will be used while the grab is active.
860 * @device: The pointer device to grab.
861 * @etime: The timestamp required for grabbing @device, or GDK_CURRENT_TIME.
863 * Specifies that all events that match the specified event mask should be sent
864 * to the specified item, and also grabs @device by calling gdk_device_grab().
865 * The event mask is also used when grabbing the @device. If @cursor is not
866 * NULL, then that cursor is used while the grab is active. The @etime
867 * parameter is the timestamp required for grabbing the @device.
869 * Return value: If an item was already grabbed, it returns
870 * %GDK_GRAB_ALREADY_GRABBED. If the specified item was hidden by calling
871 * gnome_canvas_item_hide(), then it returns %GDK_GRAB_NOT_VIEWABLE. Else,
872 * it returns the result of calling gdk_device_grab().
874 gint
875 gnome_canvas_item_grab (GnomeCanvasItem *item,
876 guint event_mask,
877 GdkCursor *cursor,
878 GdkDevice *device,
879 guint32 etime)
881 GtkLayout *layout;
882 GdkWindow *bin_window;
883 gint retval;
885 g_return_val_if_fail (
886 GNOME_IS_CANVAS_ITEM (item), GDK_GRAB_NOT_VIEWABLE);
887 g_return_val_if_fail (
888 gtk_widget_get_mapped (GTK_WIDGET (item->canvas)),
889 GDK_GRAB_NOT_VIEWABLE);
890 g_return_val_if_fail (
891 GDK_IS_DEVICE (device), GDK_GRAB_NOT_VIEWABLE);
893 if (item->canvas->grabbed_item)
894 return GDK_GRAB_ALREADY_GRABBED;
896 if (!(item->flags & GNOME_CANVAS_ITEM_VISIBLE))
897 return GDK_GRAB_NOT_VIEWABLE;
899 layout = GTK_LAYOUT (item->canvas);
900 bin_window = gtk_layout_get_bin_window (layout);
902 retval = gdk_device_grab (
903 device, bin_window, GDK_OWNERSHIP_NONE,
904 FALSE, event_mask, cursor, etime);
906 if (retval != GDK_GRAB_SUCCESS)
907 return retval;
909 item->canvas->grabbed_item = item;
910 item->canvas->grabbed_device = g_object_ref (device);
911 item->canvas->grabbed_event_mask = event_mask;
912 item->canvas->current_item = item; /* So that events go to the grabbed item */
914 return retval;
918 * gnome_canvas_item_ungrab:
919 * @item: A canvas item that holds a grab.
920 * @etime: The timestamp for ungrabbing the mouse.
922 * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
923 * mouse.
925 void
926 gnome_canvas_item_ungrab (GnomeCanvasItem *item,
927 guint32 etime)
929 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
931 if (item->canvas->grabbed_item != item)
932 return;
934 item->canvas->grabbed_item = NULL;
936 g_return_if_fail (item->canvas->grabbed_device != NULL);
937 gdk_device_ungrab (item->canvas->grabbed_device, etime);
939 g_object_unref (item->canvas->grabbed_device);
940 item->canvas->grabbed_device = NULL;
943 void
944 gnome_canvas_item_i2w_matrix (GnomeCanvasItem *item,
945 cairo_matrix_t *matrix)
947 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
948 g_return_if_fail (matrix != NULL);
950 cairo_matrix_init_identity (matrix);
952 while (item) {
953 cairo_matrix_multiply (matrix, matrix, &item->matrix);
955 item = item->parent;
959 void
960 gnome_canvas_item_w2i_matrix (GnomeCanvasItem *item,
961 cairo_matrix_t *matrix)
963 cairo_status_t status;
965 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
966 g_return_if_fail (matrix != NULL);
968 gnome_canvas_item_i2w_matrix (item, matrix);
969 status = cairo_matrix_invert (matrix);
970 g_return_if_fail (status == CAIRO_STATUS_SUCCESS);
974 * gnome_canvas_item_w2i:
975 * @item: A canvas item.
976 * @x: X coordinate to convert (input/output value).
977 * @y: Y coordinate to convert (input/output value).
979 * Converts a coordinate pair from world coordinates to item-relative
980 * coordinates.
982 void
983 gnome_canvas_item_w2i (GnomeCanvasItem *item,
984 gdouble *x,
985 gdouble *y)
987 cairo_matrix_t matrix;
989 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
990 g_return_if_fail (x != NULL);
991 g_return_if_fail (y != NULL);
993 gnome_canvas_item_w2i_matrix (item, &matrix);
994 cairo_matrix_transform_point (&matrix, x, y);
998 * gnome_canvas_item_i2w:
999 * @item: A canvas item.
1000 * @x: X coordinate to convert (input/output value).
1001 * @y: Y coordinate to convert (input/output value).
1003 * Converts a coordinate pair from item-relative coordinates to world
1004 * coordinates.
1006 void
1007 gnome_canvas_item_i2w (GnomeCanvasItem *item,
1008 gdouble *x,
1009 gdouble *y)
1011 cairo_matrix_t matrix;
1013 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
1014 g_return_if_fail (x != NULL);
1015 g_return_if_fail (y != NULL);
1017 gnome_canvas_item_i2w_matrix (item, &matrix);
1018 cairo_matrix_transform_point (&matrix, x, y);
1022 * gnome_canvas_item_i2c_matrix:
1023 * @item: A canvas item.
1024 * @matrix: Matrix to take the resulting transformation matrix (return value).
1026 * Gets the affine transform that converts from item-relative coordinates to
1027 * canvas pixel coordinates.
1029 void
1030 gnome_canvas_item_i2c_matrix (GnomeCanvasItem *item,
1031 cairo_matrix_t *matrix)
1033 cairo_matrix_t i2w, w2c;
1035 gnome_canvas_item_i2w_matrix (item, &i2w);
1036 gnome_canvas_w2c_matrix (item->canvas, &w2c);
1037 cairo_matrix_multiply (matrix, &i2w, &w2c);
1040 /* Returns whether the item is an inferior of or is equal to the parent. */
1041 static gint
1042 is_descendant (GnomeCanvasItem *item,
1043 GnomeCanvasItem *parent)
1045 for (; item; item = item->parent)
1046 if (item == parent)
1047 return TRUE;
1049 return FALSE;
1053 * gnome_canvas_item_reparent:
1054 * @item: A canvas item.
1055 * @new_group: A canvas group.
1057 * Changes the parent of the specified item to be the new group. The item keeps
1058 * its group-relative coordinates as for its old parent, so the item may change
1059 * its absolute position within the canvas.
1061 void
1062 gnome_canvas_item_reparent (GnomeCanvasItem *item,
1063 GnomeCanvasGroup *new_group)
1065 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
1066 g_return_if_fail (GNOME_IS_CANVAS_GROUP (new_group));
1068 /* Both items need to be in the same canvas */
1069 g_return_if_fail (item->canvas == GNOME_CANVAS_ITEM (new_group)->canvas);
1071 /* The group cannot be an inferior of the item or be the item itself --
1072 * this also takes care of the case where the item is the root item of
1073 * the canvas. */
1074 g_return_if_fail (!is_descendant (GNOME_CANVAS_ITEM (new_group), item));
1076 /* Everything is ok, now actually reparent the item */
1078 g_object_ref (item); /* protect it from the unref in group_remove */
1080 redraw_if_visible (item);
1082 group_remove (GNOME_CANVAS_GROUP (item->parent), item);
1083 item->parent = GNOME_CANVAS_ITEM (new_group);
1084 group_add (new_group, item);
1086 /* Redraw and repick */
1088 redraw_if_visible (item);
1089 item->canvas->need_repick = TRUE;
1091 g_object_unref (item);
1095 * gnome_canvas_item_grab_focus:
1096 * @item: A canvas item.
1098 * Makes the specified item take the keyboard focus, so all keyboard events will
1099 * be sent to it. If the canvas widget itself did not have the focus, it grabs
1100 * it as well.
1102 void
1103 gnome_canvas_item_grab_focus (GnomeCanvasItem *item)
1105 GnomeCanvasItem *focused_item;
1106 GdkEvent ev;
1108 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
1109 g_return_if_fail (gtk_widget_get_can_focus (GTK_WIDGET (item->canvas)));
1111 focused_item = item->canvas->focused_item;
1113 if (focused_item) {
1114 GtkLayout *layout;
1115 GdkWindow *bin_window;
1117 layout = GTK_LAYOUT (item->canvas);
1118 bin_window = gtk_layout_get_bin_window (layout);
1120 ev.focus_change.type = GDK_FOCUS_CHANGE;
1121 ev.focus_change.window = bin_window;
1122 ev.focus_change.send_event = FALSE;
1123 ev.focus_change.in = FALSE;
1125 emit_event (item->canvas, &ev);
1128 item->canvas->focused_item = item;
1129 gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
1131 if (focused_item) {
1132 GtkLayout *layout;
1133 GdkWindow *bin_window;
1135 layout = GTK_LAYOUT (item->canvas);
1136 bin_window = gtk_layout_get_bin_window (layout);
1138 ev.focus_change.type = GDK_FOCUS_CHANGE;
1139 ev.focus_change.window = bin_window;
1140 ev.focus_change.send_event = FALSE;
1141 ev.focus_change.in = TRUE;
1143 emit_event (item->canvas, &ev);
1148 * gnome_canvas_item_get_bounds:
1149 * @item: A canvas item.
1150 * @x1: Leftmost edge of the bounding box (return value).
1151 * @y1: Upper edge of the bounding box (return value).
1152 * @x2: Rightmost edge of the bounding box (return value).
1153 * @y2: Lower edge of the bounding box (return value).
1155 * Queries the bounding box of a canvas item. The bounds are returned in the
1156 * coordinate system of the item's parent.
1158 void
1159 gnome_canvas_item_get_bounds (GnomeCanvasItem *item,
1160 gdouble *x1,
1161 gdouble *y1,
1162 gdouble *x2,
1163 gdouble *y2)
1165 gdouble tx1, ty1, tx2, ty2;
1167 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
1169 tx1 = ty1 = tx2 = ty2 = 0.0;
1171 /* Get the item's bounds in its coordinate system */
1173 if (GNOME_CANVAS_ITEM_GET_CLASS (item)->bounds)
1174 GNOME_CANVAS_ITEM_GET_CLASS (item)->bounds (
1175 item, &tx1, &ty1, &tx2, &ty2);
1177 /* Make the bounds relative to the item's parent coordinate system */
1178 gnome_canvas_matrix_transform_rect (&item->matrix, &tx1, &ty1, &tx2, &ty2);
1180 /* Return the values */
1182 if (x1)
1183 *x1 = tx1;
1185 if (y1)
1186 *y1 = ty1;
1188 if (x2)
1189 *x2 = tx2;
1191 if (y2)
1192 *y2 = ty2;
1196 * gnome_canvas_item_request_update
1197 * @item: A canvas item.
1199 * To be used only by item implementations. Requests that the canvas queue an
1200 * update for the specified item.
1202 void
1203 gnome_canvas_item_request_update (GnomeCanvasItem *item)
1205 if (item->flags & GNOME_CANVAS_ITEM_NEED_UPDATE)
1206 return;
1208 item->flags |= GNOME_CANVAS_ITEM_NEED_UPDATE;
1210 if (item->parent != NULL) {
1211 /* Recurse up the tree */
1212 gnome_canvas_item_request_update (item->parent);
1213 } else {
1214 /* Have reached the top of the tree, make
1215 * sure the update call gets scheduled. */
1216 gnome_canvas_request_update (item->canvas);
1220 /*** GnomeCanvasGroup ***/
1222 enum {
1223 GROUP_PROP_0,
1224 GROUP_PROP_X,
1225 GROUP_PROP_Y
1228 static void gnome_canvas_group_set_property (GObject *object,
1229 guint property_id,
1230 const GValue *value,
1231 GParamSpec *pspec);
1232 static void gnome_canvas_group_get_property (GObject *object,
1233 guint property_id,
1234 GValue *value,
1235 GParamSpec *pspec);
1237 static void gnome_canvas_group_dispose (GnomeCanvasItem *object);
1239 static void gnome_canvas_group_update (GnomeCanvasItem *item,
1240 const cairo_matrix_t *matrix,
1241 gint flags);
1242 static void gnome_canvas_group_realize (GnomeCanvasItem *item);
1243 static void gnome_canvas_group_unrealize (GnomeCanvasItem *item);
1244 static void gnome_canvas_group_map (GnomeCanvasItem *item);
1245 static void gnome_canvas_group_unmap (GnomeCanvasItem *item);
1246 static void gnome_canvas_group_draw (GnomeCanvasItem *item,
1247 cairo_t *cr,
1248 gint x, gint y,
1249 gint width, gint height);
1250 static GnomeCanvasItem *gnome_canvas_group_point (GnomeCanvasItem *item,
1251 gdouble x, gdouble y,
1252 gint cx, gint cy);
1253 static void gnome_canvas_group_bounds (GnomeCanvasItem *item,
1254 gdouble *x1, gdouble *y1,
1255 gdouble *x2, gdouble *y2);
1257 G_DEFINE_TYPE (
1258 GnomeCanvasGroup,
1259 gnome_canvas_group,
1260 GNOME_TYPE_CANVAS_ITEM)
1262 /* Class initialization function for GnomeCanvasGroupClass */
1263 static void
1264 gnome_canvas_group_class_init (GnomeCanvasGroupClass *class)
1266 GObjectClass *object_class;
1267 GnomeCanvasItemClass *item_class;
1269 object_class = (GObjectClass *) class;
1270 item_class = (GnomeCanvasItemClass *) class;
1272 object_class->set_property = gnome_canvas_group_set_property;
1273 object_class->get_property = gnome_canvas_group_get_property;
1275 g_object_class_install_property (
1276 object_class,
1277 GROUP_PROP_X,
1278 g_param_spec_double (
1279 "x",
1280 "X",
1281 "X",
1282 -G_MAXDOUBLE,
1283 G_MAXDOUBLE,
1284 0.0,
1285 G_PARAM_READABLE |
1286 G_PARAM_WRITABLE));
1288 g_object_class_install_property (
1289 object_class,
1290 GROUP_PROP_Y,
1291 g_param_spec_double (
1292 "y",
1293 "Y",
1294 "Y",
1295 -G_MAXDOUBLE,
1296 G_MAXDOUBLE,
1297 0.0,
1298 G_PARAM_READABLE |
1299 G_PARAM_WRITABLE));
1301 item_class->dispose = gnome_canvas_group_dispose;
1302 item_class->update = gnome_canvas_group_update;
1303 item_class->realize = gnome_canvas_group_realize;
1304 item_class->unrealize = gnome_canvas_group_unrealize;
1305 item_class->map = gnome_canvas_group_map;
1306 item_class->unmap = gnome_canvas_group_unmap;
1307 item_class->draw = gnome_canvas_group_draw;
1308 item_class->point = gnome_canvas_group_point;
1309 item_class->bounds = gnome_canvas_group_bounds;
1312 /* Object initialization function for GnomeCanvasGroup */
1313 static void
1314 gnome_canvas_group_init (GnomeCanvasGroup *group)
1318 /* Set_property handler for canvas groups */
1319 static void
1320 gnome_canvas_group_set_property (GObject *gobject,
1321 guint property_id,
1322 const GValue *value,
1323 GParamSpec *pspec)
1325 GnomeCanvasItem *item;
1327 g_return_if_fail (GNOME_IS_CANVAS_GROUP (gobject));
1329 item = GNOME_CANVAS_ITEM (gobject);
1331 switch (property_id) {
1332 case GROUP_PROP_X:
1333 item->matrix.x0 = g_value_get_double (value);
1334 break;
1336 case GROUP_PROP_Y:
1337 item->matrix.y0 = g_value_get_double (value);
1338 break;
1340 default:
1341 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
1342 break;
1346 /* Get_property handler for canvas groups */
1347 static void
1348 gnome_canvas_group_get_property (GObject *gobject,
1349 guint property_id,
1350 GValue *value,
1351 GParamSpec *pspec)
1353 GnomeCanvasItem *item;
1355 g_return_if_fail (GNOME_IS_CANVAS_GROUP (gobject));
1357 item = GNOME_CANVAS_ITEM (gobject);
1359 switch (property_id) {
1360 case GROUP_PROP_X:
1361 g_value_set_double (value, item->matrix.x0);
1362 break;
1364 case GROUP_PROP_Y:
1365 g_value_set_double (value, item->matrix.y0);
1366 break;
1368 default:
1369 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
1370 break;
1374 /* Dispose handler for canvas groups */
1375 static void
1376 gnome_canvas_group_dispose (GnomeCanvasItem *object)
1378 GnomeCanvasGroup *group;
1380 g_return_if_fail (GNOME_IS_CANVAS_GROUP (object));
1382 group = GNOME_CANVAS_GROUP (object);
1384 while (group->item_list) {
1385 /* child is unref'ed by the child's group_remove (). */
1386 g_object_run_dispose (G_OBJECT (group->item_list->data));
1389 GNOME_CANVAS_ITEM_CLASS (gnome_canvas_group_parent_class)->
1390 dispose (object);
1393 /* Update handler for canvas groups */
1394 static void
1395 gnome_canvas_group_update (GnomeCanvasItem *item,
1396 const cairo_matrix_t *i2c,
1397 gint flags)
1399 GnomeCanvasGroup *group;
1400 GList *list;
1401 GnomeCanvasItem *i;
1402 gdouble x1, y1, x2, y2;
1404 group = GNOME_CANVAS_GROUP (item);
1406 GNOME_CANVAS_ITEM_CLASS (gnome_canvas_group_parent_class)->
1407 update (item, i2c, flags);
1409 x1 = G_MAXDOUBLE;
1410 y1 = G_MAXDOUBLE;
1411 x2 = -G_MAXDOUBLE;
1412 y2 = -G_MAXDOUBLE;
1414 for (list = group->item_list; list; list = list->next) {
1415 i = list->data;
1417 gnome_canvas_item_invoke_update (i, i2c, flags);
1419 x1 = MIN (x1, i->x1);
1420 x2 = MAX (x2, i->x2);
1421 y1 = MIN (y1, i->y1);
1422 y2 = MAX (y2, i->y2);
1424 if (x1 >= x2 || y1 >= y2) {
1425 item->x1 = item->x2 = item->y1 = item->y2 = 0;
1426 } else {
1427 item->x1 = x1;
1428 item->y1 = y1;
1429 item->x2 = x2;
1430 item->y2 = y2;
1434 /* Realize handler for canvas groups */
1435 static void
1436 gnome_canvas_group_realize (GnomeCanvasItem *item)
1438 GnomeCanvasGroup *group;
1439 GList *list;
1440 GnomeCanvasItem *i;
1442 group = GNOME_CANVAS_GROUP (item);
1444 for (list = group->item_list; list; list = list->next) {
1445 i = list->data;
1447 if (!(i->flags & GNOME_CANVAS_ITEM_REALIZED))
1448 (* GNOME_CANVAS_ITEM_GET_CLASS (i)->realize) (i);
1451 GNOME_CANVAS_ITEM_CLASS (gnome_canvas_group_parent_class)->
1452 realize (item);
1455 /* Unrealize handler for canvas groups */
1456 static void
1457 gnome_canvas_group_unrealize (GnomeCanvasItem *item)
1459 GnomeCanvasGroup *group;
1460 GList *list;
1461 GnomeCanvasItem *i;
1463 group = GNOME_CANVAS_GROUP (item);
1465 for (list = group->item_list; list; list = list->next) {
1466 i = list->data;
1468 if (i->flags & GNOME_CANVAS_ITEM_REALIZED)
1469 (* GNOME_CANVAS_ITEM_GET_CLASS (i)->unrealize) (i);
1472 GNOME_CANVAS_ITEM_CLASS (gnome_canvas_group_parent_class)->
1473 unrealize (item);
1476 /* Map handler for canvas groups */
1477 static void
1478 gnome_canvas_group_map (GnomeCanvasItem *item)
1480 GnomeCanvasGroup *group;
1481 GList *list;
1482 GnomeCanvasItem *i;
1484 group = GNOME_CANVAS_GROUP (item);
1486 for (list = group->item_list; list; list = list->next) {
1487 i = list->data;
1489 if (!(i->flags & GNOME_CANVAS_ITEM_MAPPED))
1490 (* GNOME_CANVAS_ITEM_GET_CLASS (i)->map) (i);
1493 GNOME_CANVAS_ITEM_CLASS (gnome_canvas_group_parent_class)->map (item);
1496 /* Unmap handler for canvas groups */
1497 static void
1498 gnome_canvas_group_unmap (GnomeCanvasItem *item)
1500 GnomeCanvasGroup *group;
1501 GList *list;
1502 GnomeCanvasItem *i;
1504 group = GNOME_CANVAS_GROUP (item);
1506 for (list = group->item_list; list; list = list->next) {
1507 i = list->data;
1509 if (i->flags & GNOME_CANVAS_ITEM_MAPPED)
1510 (* GNOME_CANVAS_ITEM_GET_CLASS (i)->unmap) (i);
1513 GNOME_CANVAS_ITEM_CLASS (gnome_canvas_group_parent_class)->unmap (item);
1516 /* Draw handler for canvas groups */
1517 static void
1518 gnome_canvas_group_draw (GnomeCanvasItem *item,
1519 cairo_t *cr,
1520 gint x,
1521 gint y,
1522 gint width,
1523 gint height)
1525 GnomeCanvasGroup *group;
1526 GList *list;
1527 GnomeCanvasItem *child = NULL;
1529 group = GNOME_CANVAS_GROUP (item);
1531 for (list = group->item_list; list; list = list->next) {
1532 child = list->data;
1534 if ((child->flags & GNOME_CANVAS_ITEM_VISIBLE)
1535 && ((child->x1 < (x + width))
1536 && (child->y1 < (y + height))
1537 && (child->x2 > x)
1538 && (child->y2 > y))) {
1539 cairo_save (cr);
1541 GNOME_CANVAS_ITEM_GET_CLASS (child)->draw (
1542 child, cr, x, y, width, height);
1544 cairo_restore (cr);
1549 /* Point handler for canvas groups */
1550 static GnomeCanvasItem *
1551 gnome_canvas_group_point (GnomeCanvasItem *item,
1552 gdouble x,
1553 gdouble y,
1554 gint cx,
1555 gint cy)
1557 GnomeCanvasGroup *group;
1558 GList *list;
1559 GnomeCanvasItem *child, *point_item;
1561 group = GNOME_CANVAS_GROUP (item);
1563 for (list = g_list_last (group->item_list); list; list = list->prev) {
1564 child = list->data;
1566 if ((child->x1 > cx) || (child->y1 > cy))
1567 continue;
1569 if ((child->x2 < cx) || (child->y2 < cy))
1570 continue;
1572 if (!(child->flags & GNOME_CANVAS_ITEM_VISIBLE))
1573 continue;
1575 point_item = gnome_canvas_item_invoke_point (child, x, y, cx, cy);
1576 if (point_item)
1577 return point_item;
1580 return NULL;
1583 /* Bounds handler for canvas groups */
1584 static void
1585 gnome_canvas_group_bounds (GnomeCanvasItem *item,
1586 gdouble *x1,
1587 gdouble *y1,
1588 gdouble *x2,
1589 gdouble *y2)
1591 GnomeCanvasGroup *group;
1592 GnomeCanvasItem *child;
1593 GList *list;
1594 gdouble tx1, ty1, tx2, ty2;
1595 gdouble minx, miny, maxx, maxy;
1596 gint set;
1598 group = GNOME_CANVAS_GROUP (item);
1600 /* Get the bounds of the first visible item */
1602 child = NULL; /* Unnecessary but eliminates a warning. */
1604 set = FALSE;
1606 for (list = group->item_list; list; list = list->next) {
1607 child = list->data;
1609 if (child->flags & GNOME_CANVAS_ITEM_VISIBLE) {
1610 set = TRUE;
1611 gnome_canvas_item_get_bounds (child, &minx, &miny, &maxx, &maxy);
1612 break;
1616 /* If there were no visible items, return an empty bounding box */
1618 if (!set) {
1619 *x1 = *y1 = *x2 = *y2 = 0.0;
1620 return;
1623 /* Now we can grow the bounds using the rest of the items */
1625 list = list->next;
1627 for (; list; list = list->next) {
1628 child = list->data;
1630 if (!(child->flags & GNOME_CANVAS_ITEM_VISIBLE))
1631 continue;
1633 gnome_canvas_item_get_bounds (child, &tx1, &ty1, &tx2, &ty2);
1635 if (tx1 < minx)
1636 minx = tx1;
1638 if (ty1 < miny)
1639 miny = ty1;
1641 if (tx2 > maxx)
1642 maxx = tx2;
1644 if (ty2 > maxy)
1645 maxy = ty2;
1648 *x1 = minx;
1649 *y1 = miny;
1650 *x2 = maxx;
1651 *y2 = maxy;
1654 /* Adds an item to a group */
1655 static void
1656 group_add (GnomeCanvasGroup *group,
1657 GnomeCanvasItem *item)
1659 g_object_ref_sink (item);
1661 if (!group->item_list) {
1662 group->item_list = g_list_append (group->item_list, item);
1663 group->item_list_end = group->item_list;
1664 } else
1665 group->item_list_end = g_list_append (group->item_list_end, item)->next;
1667 if (group->item.flags & GNOME_CANVAS_ITEM_REALIZED)
1668 (* GNOME_CANVAS_ITEM_GET_CLASS (item)->realize) (item);
1670 if (group->item.flags & GNOME_CANVAS_ITEM_MAPPED)
1671 (* GNOME_CANVAS_ITEM_GET_CLASS (item)->map) (item);
1673 g_object_notify (G_OBJECT (item), "parent");
1676 /* Removes an item from a group */
1677 static void
1678 group_remove (GnomeCanvasGroup *group,
1679 GnomeCanvasItem *item)
1681 GList *children;
1683 g_return_if_fail (GNOME_IS_CANVAS_GROUP (group));
1684 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
1686 for (children = group->item_list; children; children = children->next)
1687 if (children->data == item) {
1688 if (item->flags & GNOME_CANVAS_ITEM_MAPPED)
1689 (* GNOME_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
1691 if (item->flags & GNOME_CANVAS_ITEM_REALIZED)
1692 (* GNOME_CANVAS_ITEM_GET_CLASS (item)->unrealize) (item);
1694 /* Unparent the child */
1696 item->parent = NULL;
1697 g_object_unref (item);
1699 /* Remove it from the list */
1701 if (children == group->item_list_end)
1702 group->item_list_end = children->prev;
1704 group->item_list = g_list_remove_link (group->item_list, children);
1705 g_list_free (children);
1706 break;
1710 /*** GnomeCanvas ***/
1712 enum {
1713 DRAW_BACKGROUND,
1714 LAST_SIGNAL
1717 static void gnome_canvas_dispose (GObject *object);
1718 static void gnome_canvas_map (GtkWidget *widget);
1719 static void gnome_canvas_unmap (GtkWidget *widget);
1720 static void gnome_canvas_realize (GtkWidget *widget);
1721 static void gnome_canvas_unrealize (GtkWidget *widget);
1722 static void gnome_canvas_size_allocate (GtkWidget *widget,
1723 GtkAllocation *allocation);
1724 static gint gnome_canvas_draw (GtkWidget *widget,
1725 cairo_t *cr);
1726 static void gnome_canvas_drag_end (GtkWidget *widget,
1727 GdkDragContext *context);
1728 static gint gnome_canvas_button (GtkWidget *widget,
1729 GdkEventButton *event);
1730 static gint gnome_canvas_motion (GtkWidget *widget,
1731 GdkEventMotion *event);
1732 static gboolean gnome_canvas_key (GtkWidget *widget,
1733 GdkEventKey *event);
1734 static gint gnome_canvas_crossing (GtkWidget *widget,
1735 GdkEventCrossing *event);
1736 static gint gnome_canvas_focus_in (GtkWidget *widget,
1737 GdkEventFocus *event);
1738 static gint gnome_canvas_focus_out (GtkWidget *widget,
1739 GdkEventFocus *event);
1740 static void gnome_canvas_request_update_real (GnomeCanvas *canvas);
1741 static void gnome_canvas_draw_background (GnomeCanvas *canvas,
1742 cairo_t *cr,
1743 gint x,
1744 gint y,
1745 gint width,
1746 gint height);
1748 static guint canvas_signals[LAST_SIGNAL];
1750 enum {
1751 PROP_0,
1752 PROP_FOCUSED_ITEM,
1755 G_DEFINE_TYPE (
1756 GnomeCanvas,
1757 gnome_canvas,
1758 GTK_TYPE_LAYOUT)
1760 static void
1761 gnome_canvas_paint_rect (GnomeCanvas *canvas,
1762 cairo_t *cr,
1763 gint x0,
1764 gint y0,
1765 gint x1,
1766 gint y1)
1768 GtkWidget *widget;
1769 GtkAllocation allocation;
1770 GtkScrollable *scrollable;
1771 GtkAdjustment *hadjustment;
1772 GtkAdjustment *vadjustment;
1773 gint draw_x1, draw_y1;
1774 gint draw_x2, draw_y2;
1775 gint draw_width, draw_height;
1776 gdouble hadjustment_value;
1777 gdouble vadjustment_value;
1779 g_return_if_fail (!canvas->need_update);
1781 widget = GTK_WIDGET (canvas);
1782 gtk_widget_get_allocation (widget, &allocation);
1784 scrollable = GTK_SCROLLABLE (canvas);
1785 hadjustment = gtk_scrollable_get_hadjustment (scrollable);
1786 vadjustment = gtk_scrollable_get_vadjustment (scrollable);
1788 hadjustment_value = gtk_adjustment_get_value (hadjustment);
1789 vadjustment_value = gtk_adjustment_get_value (vadjustment);
1791 draw_x1 = MAX (x0, hadjustment_value - canvas->zoom_xofs);
1792 draw_y1 = MAX (y0, vadjustment_value - canvas->zoom_yofs);
1793 draw_x2 = MIN (draw_x1 + allocation.width, x1);
1794 draw_y2 = MIN (draw_y1 + allocation.height, y1);
1796 draw_width = draw_x2 - draw_x1;
1797 draw_height = draw_y2 - draw_y1;
1799 if (draw_width < 1 || draw_height < 1)
1800 return;
1802 canvas->draw_xofs = draw_x1;
1803 canvas->draw_yofs = draw_y1;
1805 cairo_save (cr);
1807 g_signal_emit (
1808 canvas, canvas_signals[DRAW_BACKGROUND], 0, cr,
1809 draw_x1, draw_y1, draw_width, draw_height);
1811 cairo_restore (cr);
1813 if (canvas->root->flags & GNOME_CANVAS_ITEM_VISIBLE) {
1814 cairo_save (cr);
1816 (* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->draw) (
1817 canvas->root, cr,
1818 draw_x1, draw_y1,
1819 draw_width, draw_height);
1821 cairo_restore (cr);
1825 static void
1826 gnome_canvas_get_property (GObject *object,
1827 guint property_id,
1828 GValue *value,
1829 GParamSpec *pspec)
1831 switch (property_id) {
1832 case PROP_FOCUSED_ITEM:
1833 g_value_set_object (value, GNOME_CANVAS (object)->focused_item);
1834 break;
1835 default:
1836 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1837 break;
1841 static void
1842 gnome_canvas_set_property (GObject *object,
1843 guint property_id,
1844 const GValue *value,
1845 GParamSpec *pspec)
1847 switch (property_id) {
1848 case PROP_FOCUSED_ITEM:
1849 GNOME_CANVAS (object)->focused_item = g_value_get_object (value);
1850 break;
1851 default:
1852 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1853 break;
1857 /* Class initialization function for GnomeCanvasClass */
1858 static void
1859 gnome_canvas_class_init (GnomeCanvasClass *class)
1861 GObjectClass *object_class;
1862 GtkWidgetClass *widget_class;
1864 object_class = (GObjectClass *) class;
1865 widget_class = (GtkWidgetClass *) class;
1867 object_class->set_property = gnome_canvas_set_property;
1868 object_class->get_property = gnome_canvas_get_property;
1869 object_class->dispose = gnome_canvas_dispose;
1871 widget_class->map = gnome_canvas_map;
1872 widget_class->unmap = gnome_canvas_unmap;
1873 widget_class->realize = gnome_canvas_realize;
1874 widget_class->unrealize = gnome_canvas_unrealize;
1875 widget_class->size_allocate = gnome_canvas_size_allocate;
1876 widget_class->draw = gnome_canvas_draw;
1877 widget_class->drag_end = gnome_canvas_drag_end;
1878 widget_class->button_press_event = gnome_canvas_button;
1879 widget_class->button_release_event = gnome_canvas_button;
1880 widget_class->motion_notify_event = gnome_canvas_motion;
1881 widget_class->key_press_event = gnome_canvas_key;
1882 widget_class->key_release_event = gnome_canvas_key;
1883 widget_class->enter_notify_event = gnome_canvas_crossing;
1884 widget_class->leave_notify_event = gnome_canvas_crossing;
1885 widget_class->focus_in_event = gnome_canvas_focus_in;
1886 widget_class->focus_out_event = gnome_canvas_focus_out;
1888 class->draw_background = gnome_canvas_draw_background;
1889 class->request_update = gnome_canvas_request_update_real;
1891 g_object_class_install_property (
1892 object_class,
1893 PROP_FOCUSED_ITEM,
1894 g_param_spec_object (
1895 "focused_item",
1896 NULL,
1897 NULL,
1898 GNOME_TYPE_CANVAS_ITEM,
1899 G_PARAM_READABLE |
1900 G_PARAM_WRITABLE));
1902 canvas_signals[DRAW_BACKGROUND] = g_signal_new (
1903 "draw_background",
1904 G_TYPE_FROM_CLASS (object_class),
1905 G_SIGNAL_RUN_LAST,
1906 G_STRUCT_OFFSET (GnomeCanvasClass, draw_background),
1907 NULL, NULL, NULL,
1908 G_TYPE_NONE, 5,
1909 CAIRO_GOBJECT_TYPE_CONTEXT,
1910 G_TYPE_INT,
1911 G_TYPE_INT,
1912 G_TYPE_INT,
1913 G_TYPE_INT);
1915 gtk_widget_class_set_accessible_type (widget_class, GAIL_TYPE_CANVAS);
1916 gail_canvas_a11y_init ();
1919 /* Callback used when the root item of a canvas is destroyed. The user should
1920 * never ever do this, so we panic if this happens.
1922 G_GNUC_NORETURN static void
1923 panic_root_finalized (gpointer data,
1924 GObject *gone_object)
1926 g_error ("Eeeek, root item %p of canvas %p was destroyed!", gone_object, data);
1929 /* Object initialization function for GnomeCanvas */
1930 static void
1931 gnome_canvas_init (GnomeCanvas *canvas)
1933 GtkLayout *layout;
1934 guint layout_width, layout_height;
1936 layout = GTK_LAYOUT (canvas);
1937 gtk_layout_get_size (layout, &layout_width, &layout_height);
1939 gtk_widget_set_can_focus (GTK_WIDGET (canvas), TRUE);
1941 canvas->need_update = FALSE;
1942 canvas->idle_id = 0;
1944 canvas->scroll_x1 = 0.0;
1945 canvas->scroll_y1 = 0.0;
1946 canvas->scroll_x2 = layout_width;
1947 canvas->scroll_y2 = layout_height;
1949 canvas->pick_event.type = GDK_LEAVE_NOTIFY;
1950 canvas->pick_event.crossing.x = 0;
1951 canvas->pick_event.crossing.y = 0;
1953 gtk_scrollable_set_hadjustment (GTK_SCROLLABLE (canvas), NULL);
1954 gtk_scrollable_set_vadjustment (GTK_SCROLLABLE (canvas), NULL);
1956 /* Create the root item as a special case */
1958 canvas->root = GNOME_CANVAS_ITEM (
1959 g_object_new (gnome_canvas_group_get_type (), NULL));
1960 canvas->root->canvas = canvas;
1962 g_object_ref_sink (canvas->root);
1964 g_object_weak_ref (G_OBJECT (canvas->root), panic_root_finalized, canvas);
1966 canvas->need_repick = TRUE;
1969 /* Convenience function to remove the idle handler of a canvas */
1970 static void
1971 remove_idle (GnomeCanvas *canvas)
1973 if (canvas->idle_id == 0)
1974 return;
1976 g_source_remove (canvas->idle_id);
1977 canvas->idle_id = 0;
1980 /* Removes the transient state of the canvas (idle handler, grabs). */
1981 static void
1982 shutdown_transients (GnomeCanvas *canvas)
1984 if (canvas->grabbed_device != NULL) {
1985 gdk_device_ungrab (canvas->grabbed_device, GDK_CURRENT_TIME);
1986 g_object_unref (canvas->grabbed_device);
1987 canvas->grabbed_device = NULL;
1990 canvas->grabbed_item = NULL;
1992 remove_idle (canvas);
1995 /* Dispose handler for GnomeCanvas */
1996 static void
1997 gnome_canvas_dispose (GObject *object)
1999 GnomeCanvas *canvas;
2001 g_return_if_fail (GNOME_IS_CANVAS (object));
2003 /* remember, dispose can be run multiple times! */
2005 canvas = GNOME_CANVAS (object);
2007 if (canvas->root) {
2008 g_object_weak_unref (G_OBJECT (canvas->root), panic_root_finalized, canvas);
2009 g_object_unref (canvas->root);
2010 canvas->root = NULL;
2013 shutdown_transients (canvas);
2015 /* Chain up to parent's dispose() method. */
2016 G_OBJECT_CLASS (gnome_canvas_parent_class)->dispose (object);
2020 * gnome_canvas_new:
2022 * Creates a new empty canvas in non-antialiased mode.
2024 * Return value: A newly-created canvas.
2026 GtkWidget *
2027 gnome_canvas_new (void)
2029 return GTK_WIDGET (g_object_new (gnome_canvas_get_type (), NULL));
2032 /* Map handler for the canvas */
2033 static void
2034 gnome_canvas_map (GtkWidget *widget)
2036 GnomeCanvas *canvas;
2038 g_return_if_fail (GNOME_IS_CANVAS (widget));
2040 /* Normal widget mapping stuff */
2042 GTK_WIDGET_CLASS (gnome_canvas_parent_class)->map (widget);
2044 canvas = GNOME_CANVAS (widget);
2046 if (canvas->need_update)
2047 add_idle (canvas);
2049 /* Map items */
2051 if (GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->map)
2052 (* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->map) (canvas->root);
2055 /* Unmap handler for the canvas */
2056 static void
2057 gnome_canvas_unmap (GtkWidget *widget)
2059 GnomeCanvas *canvas;
2061 g_return_if_fail (GNOME_IS_CANVAS (widget));
2063 canvas = GNOME_CANVAS (widget);
2065 shutdown_transients (canvas);
2067 /* Unmap items */
2069 if (GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->unmap)
2070 (* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->unmap) (canvas->root);
2072 /* Normal widget unmapping stuff */
2074 GTK_WIDGET_CLASS (gnome_canvas_parent_class)->unmap (widget);
2077 /* Realize handler for the canvas */
2078 static void
2079 gnome_canvas_realize (GtkWidget *widget)
2081 GnomeCanvas *canvas;
2082 GtkLayout *layout;
2083 GdkWindow *bin_window;
2085 g_return_if_fail (GNOME_IS_CANVAS (widget));
2087 /* Normal widget realization stuff */
2089 GTK_WIDGET_CLASS (gnome_canvas_parent_class)->realize (widget);
2091 canvas = GNOME_CANVAS (widget);
2093 layout = GTK_LAYOUT (canvas);
2094 bin_window = gtk_layout_get_bin_window (layout);
2096 gdk_window_set_events (
2097 bin_window,
2098 (gdk_window_get_events (bin_window)
2099 | GDK_EXPOSURE_MASK
2100 | GDK_SCROLL_MASK
2101 | GDK_BUTTON_PRESS_MASK
2102 | GDK_BUTTON_RELEASE_MASK
2103 | GDK_POINTER_MOTION_MASK
2104 | GDK_KEY_PRESS_MASK
2105 | GDK_KEY_RELEASE_MASK
2106 | GDK_ENTER_NOTIFY_MASK
2107 | GDK_LEAVE_NOTIFY_MASK
2108 | GDK_FOCUS_CHANGE_MASK));
2110 /* Create our own temporary pixmap gc and realize all the items */
2112 (* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->realize) (canvas->root);
2115 /* Unrealize handler for the canvas */
2116 static void
2117 gnome_canvas_unrealize (GtkWidget *widget)
2119 GnomeCanvas *canvas;
2121 g_return_if_fail (GNOME_IS_CANVAS (widget));
2123 canvas = GNOME_CANVAS (widget);
2125 shutdown_transients (canvas);
2127 /* Unrealize items and parent widget */
2129 (* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->unrealize) (canvas->root);
2131 GTK_WIDGET_CLASS (gnome_canvas_parent_class)->unrealize (widget);
2134 /* Handles scrolling of the canvas. Adjusts the scrolling and zooming offset to
2135 * keep as much as possible of the canvas scrolling region in view.
2137 static void
2138 scroll_to (GnomeCanvas *canvas,
2139 gint cx,
2140 gint cy)
2142 GtkWidget *widget;
2143 GtkAllocation allocation;
2144 GtkScrollable *scrollable;
2145 GtkAdjustment *hadjustment;
2146 GtkAdjustment *vadjustment;
2147 guint layout_width, layout_height;
2148 gint scroll_width, scroll_height;
2149 gint right_limit, bottom_limit;
2150 gint old_zoom_xofs, old_zoom_yofs;
2151 gint canvas_width, canvas_height;
2153 widget = GTK_WIDGET (canvas);
2154 gtk_widget_get_allocation (widget, &allocation);
2156 scrollable = GTK_SCROLLABLE (canvas);
2157 hadjustment = gtk_scrollable_get_hadjustment (scrollable);
2158 vadjustment = gtk_scrollable_get_vadjustment (scrollable);
2160 gtk_layout_get_size (GTK_LAYOUT (canvas), &layout_width, &layout_height);
2162 canvas_width = allocation.width;
2163 canvas_height = allocation.height;
2165 scroll_width =
2166 floor ((canvas->scroll_x2 - canvas->scroll_x1) + 0.5);
2167 scroll_height =
2168 floor ((canvas->scroll_y2 - canvas->scroll_y1) + 0.5);
2170 right_limit = scroll_width - canvas_width;
2171 bottom_limit = scroll_height - canvas_height;
2173 old_zoom_xofs = canvas->zoom_xofs;
2174 old_zoom_yofs = canvas->zoom_yofs;
2176 if (right_limit < 0) {
2177 cx = 0;
2178 canvas->zoom_xofs = (canvas_width - scroll_width) / 2;
2179 scroll_width = canvas_width;
2180 } else if (cx < 0) {
2181 cx = 0;
2182 canvas->zoom_xofs = 0;
2183 } else if (cx > right_limit) {
2184 cx = right_limit;
2185 canvas->zoom_xofs = 0;
2186 } else
2187 canvas->zoom_xofs = 0;
2189 if (bottom_limit < 0) {
2190 cy = 0;
2191 canvas->zoom_yofs = (canvas_height - scroll_height) / 2;
2192 scroll_height = canvas_height;
2193 } else if (cy < 0) {
2194 cy = 0;
2195 canvas->zoom_yofs = 0;
2196 } else if (cy > bottom_limit) {
2197 cy = bottom_limit;
2198 canvas->zoom_yofs = 0;
2199 } else
2200 canvas->zoom_yofs = 0;
2202 if ((canvas->zoom_xofs != old_zoom_xofs) ||
2203 (canvas->zoom_yofs != old_zoom_yofs)) {
2204 /* This can only occur, if either canvas size or widget
2205 * size changes. So I think we can request full redraw
2206 * here. The reason is, that coverage UTA will be
2207 * invalidated by offset change. */
2208 /* FIXME Strictly this is not correct - we have to remove
2209 * our own idle (Lauris) */
2210 /* More stuff - we have to mark root as needing fresh affine
2211 * (Lauris) */
2212 if (!(canvas->root->flags & GNOME_CANVAS_ITEM_NEED_AFFINE)) {
2213 canvas->root->flags |= GNOME_CANVAS_ITEM_NEED_AFFINE;
2214 gnome_canvas_request_update (canvas);
2216 gtk_widget_queue_draw (GTK_WIDGET (canvas));
2219 if (hadjustment)
2220 gtk_adjustment_set_value (hadjustment, cx);
2222 if (vadjustment)
2223 gtk_adjustment_set_value (vadjustment, cy);
2225 if ((scroll_width != (gint) layout_width)
2226 || (scroll_height != (gint) layout_height))
2227 gtk_layout_set_size (GTK_LAYOUT (canvas), scroll_width, scroll_height);
2230 /* Size allocation handler for the canvas */
2231 static void
2232 gnome_canvas_size_allocate (GtkWidget *widget,
2233 GtkAllocation *allocation)
2235 GtkScrollable *scrollable;
2236 GtkAdjustment *hadjustment;
2237 GtkAdjustment *vadjustment;
2239 g_return_if_fail (GNOME_IS_CANVAS (widget));
2240 g_return_if_fail (allocation != NULL);
2242 GTK_WIDGET_CLASS (gnome_canvas_parent_class)->
2243 size_allocate (widget, allocation);
2245 scrollable = GTK_SCROLLABLE (widget);
2246 hadjustment = gtk_scrollable_get_hadjustment (scrollable);
2247 vadjustment = gtk_scrollable_get_vadjustment (scrollable);
2249 /* Recenter the view, if appropriate */
2251 g_object_freeze_notify (G_OBJECT (hadjustment));
2252 g_object_freeze_notify (G_OBJECT (vadjustment));
2254 gtk_adjustment_set_page_size (hadjustment, allocation->width);
2255 gtk_adjustment_set_page_increment (hadjustment, allocation->width / 2);
2257 gtk_adjustment_set_page_size (vadjustment, allocation->height);
2258 gtk_adjustment_set_page_increment (vadjustment, allocation->height / 2);
2260 scroll_to (
2261 GNOME_CANVAS (widget),
2262 gtk_adjustment_get_value (hadjustment),
2263 gtk_adjustment_get_value (vadjustment));
2265 g_object_thaw_notify (G_OBJECT (hadjustment));
2266 g_object_thaw_notify (G_OBJECT (vadjustment));
2269 static gboolean
2270 gnome_canvas_draw (GtkWidget *widget,
2271 cairo_t *cr)
2273 GnomeCanvas *canvas = GNOME_CANVAS (widget);
2274 cairo_rectangle_int_t rect;
2275 GtkLayout *layout;
2276 GtkAdjustment *hadjustment;
2277 GtkAdjustment *vadjustment;
2278 gdouble hadjustment_value;
2279 gdouble vadjustment_value;
2281 layout = GTK_LAYOUT (canvas);
2282 hadjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (layout));
2283 vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (layout));
2285 hadjustment_value = gtk_adjustment_get_value (hadjustment);
2286 vadjustment_value = gtk_adjustment_get_value (vadjustment);
2288 gdk_cairo_get_clip_rectangle (cr, &rect);
2290 if (canvas->need_update) {
2291 cairo_matrix_t w2c;
2293 /* We start updating root with w2c matrix */
2294 gnome_canvas_w2c_matrix (canvas, &w2c);
2296 gnome_canvas_item_invoke_update (canvas->root, &w2c, 0);
2298 canvas->need_update = FALSE;
2301 cairo_save (cr);
2302 cairo_translate (
2304 -canvas->zoom_xofs + rect.x,
2305 -canvas->zoom_yofs + rect.y);
2307 rect.x += hadjustment_value;
2308 rect.y += vadjustment_value;
2310 /* No pending updates, draw exposed area immediately */
2311 gnome_canvas_paint_rect (
2312 canvas, cr,
2313 rect.x, rect.y,
2314 rect.x + rect.width,
2315 rect.y + rect.height);
2316 cairo_restore (cr);
2318 /* And call expose on parent container class */
2319 GTK_WIDGET_CLASS (gnome_canvas_parent_class)->draw (widget, cr);
2321 return FALSE;
2324 static void
2325 gnome_canvas_drag_end (GtkWidget *widget,
2326 GdkDragContext *context)
2328 GnomeCanvas *canvas = GNOME_CANVAS (widget);
2330 if (canvas->grabbed_item) {
2331 gnome_canvas_item_ungrab (canvas->grabbed_item, GDK_CURRENT_TIME);
2334 if (GTK_WIDGET_CLASS (gnome_canvas_parent_class)->drag_end)
2335 GTK_WIDGET_CLASS (gnome_canvas_parent_class)->drag_end (widget, context);
2338 /* Emits an event for an item in the canvas, be it the current item, grabbed
2339 * item, or focused item, as appropriate.
2342 static gint
2343 emit_event (GnomeCanvas *canvas,
2344 GdkEvent *event)
2346 GdkEvent *ev;
2347 gint finished;
2348 GnomeCanvasItem *item;
2349 GnomeCanvasItem *parent;
2350 guint mask;
2352 /* Perform checks for grabbed items */
2354 if (canvas->grabbed_item &&
2355 !is_descendant (canvas->current_item, canvas->grabbed_item)) {
2356 /* I think this warning is annoying and I don't know what it's for
2357 * so I'll disable it for now.
2359 /* g_warning ("emit_event() returning FALSE!\n");*/
2360 return FALSE;
2363 if (canvas->grabbed_item) {
2364 switch (event->type) {
2365 case GDK_ENTER_NOTIFY:
2366 mask = GDK_ENTER_NOTIFY_MASK;
2367 break;
2369 case GDK_LEAVE_NOTIFY:
2370 mask = GDK_LEAVE_NOTIFY_MASK;
2371 break;
2373 case GDK_MOTION_NOTIFY:
2374 mask = GDK_POINTER_MOTION_MASK;
2375 break;
2377 case GDK_BUTTON_PRESS:
2378 case GDK_2BUTTON_PRESS:
2379 case GDK_3BUTTON_PRESS:
2380 mask = GDK_BUTTON_PRESS_MASK;
2381 break;
2383 case GDK_BUTTON_RELEASE:
2384 mask = GDK_BUTTON_RELEASE_MASK;
2385 break;
2387 case GDK_KEY_PRESS:
2388 mask = GDK_KEY_PRESS_MASK;
2389 break;
2391 case GDK_KEY_RELEASE:
2392 mask = GDK_KEY_RELEASE_MASK;
2393 break;
2395 case GDK_SCROLL:
2396 mask = GDK_SCROLL_MASK;
2397 break;
2399 default:
2400 mask = 0;
2401 break;
2404 if (!(mask & canvas->grabbed_event_mask))
2405 return FALSE;
2408 /* Convert to world coordinates -- we have two cases because of diferent
2409 * offsets of the fields in the event structures.
2412 ev = gdk_event_copy (event);
2414 switch (ev->type)
2416 case GDK_ENTER_NOTIFY:
2417 case GDK_LEAVE_NOTIFY:
2418 gnome_canvas_window_to_world (
2419 canvas,
2420 ev->crossing.x, ev->crossing.y,
2421 &ev->crossing.x, &ev->crossing.y);
2422 break;
2424 case GDK_MOTION_NOTIFY:
2425 case GDK_BUTTON_PRESS:
2426 case GDK_2BUTTON_PRESS:
2427 case GDK_3BUTTON_PRESS:
2428 case GDK_BUTTON_RELEASE:
2429 gnome_canvas_window_to_world (
2430 canvas,
2431 ev->motion.x, ev->motion.y,
2432 &ev->motion.x, &ev->motion.y);
2433 break;
2435 default:
2436 break;
2439 /* Choose where we send the event */
2441 item = canvas->current_item;
2443 if (canvas->focused_item
2444 && ((event->type == GDK_KEY_PRESS) ||
2445 (event->type == GDK_KEY_RELEASE) ||
2446 (event->type == GDK_FOCUS_CHANGE)))
2447 item = canvas->focused_item;
2449 /* The event is propagated up the hierarchy (for if someone connected to
2450 * a group instead of a leaf event), and emission is stopped if a
2451 * handler returns TRUE, just like for GtkWidget events.
2454 finished = FALSE;
2456 while (item && !finished) {
2457 g_object_ref (item);
2459 g_signal_emit (
2460 item, item_signals[ITEM_EVENT], 0,
2461 ev, &finished);
2463 parent = item->parent;
2464 g_object_unref (item);
2466 item = parent;
2469 gdk_event_free (ev);
2471 return finished;
2474 /* Re-picks the current item in the canvas, based on the event's coordinates.
2475 * Also emits enter/leave events for items as appropriate.
2477 static gint
2478 pick_current_item (GnomeCanvas *canvas,
2479 GdkEvent *event)
2481 gint button_down;
2482 gdouble x, y;
2483 gint cx, cy;
2484 gint retval;
2486 retval = FALSE;
2488 /* If a button is down, we'll perform enter and leave events on the
2489 * current item, but not enter on any other item. This is more or less
2490 * like X pointer grabbing for canvas items.
2492 button_down = canvas->state & (GDK_BUTTON1_MASK
2493 | GDK_BUTTON2_MASK
2494 | GDK_BUTTON3_MASK
2495 | GDK_BUTTON4_MASK
2496 | GDK_BUTTON5_MASK);
2497 if (!button_down)
2498 canvas->left_grabbed_item = FALSE;
2500 /* Save the event in the canvas. This is used to synthesize enter and
2501 * leave events in case the current item changes. It is also used to
2502 * re-pick the current item if the current one gets deleted. Also,
2503 * synthesize an enter event.
2505 if (event != &canvas->pick_event) {
2506 if ((event->type == GDK_MOTION_NOTIFY) ||
2507 (event->type == GDK_BUTTON_RELEASE)) {
2508 /* these fields have the same offsets in both types of events */
2510 canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
2511 canvas->pick_event.crossing.window = event->motion.window;
2512 canvas->pick_event.crossing.send_event = event->motion.send_event;
2513 canvas->pick_event.crossing.subwindow = NULL;
2514 canvas->pick_event.crossing.x = event->motion.x;
2515 canvas->pick_event.crossing.y = event->motion.y;
2516 canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
2517 canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
2518 canvas->pick_event.crossing.focus = FALSE;
2519 canvas->pick_event.crossing.state = event->motion.state;
2521 /* these fields don't have the same offsets in both types of events */
2523 if (event->type == GDK_MOTION_NOTIFY) {
2524 canvas->pick_event.crossing.x_root = event->motion.x_root;
2525 canvas->pick_event.crossing.y_root = event->motion.y_root;
2526 } else {
2527 canvas->pick_event.crossing.x_root = event->button.x_root;
2528 canvas->pick_event.crossing.y_root = event->button.y_root;
2530 } else
2531 canvas->pick_event = *event;
2534 /* Don't do anything else if this is a recursive call */
2536 if (canvas->in_repick)
2537 return retval;
2539 /* LeaveNotify means that there is no current item, so we don't look for one */
2541 if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
2542 /* these fields don't have the same offsets in both types of events */
2544 if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
2545 x = canvas->pick_event.crossing.x - canvas->zoom_xofs;
2546 y = canvas->pick_event.crossing.y - canvas->zoom_yofs;
2547 } else {
2548 x = canvas->pick_event.motion.x - canvas->zoom_xofs;
2549 y = canvas->pick_event.motion.y - canvas->zoom_yofs;
2552 /* canvas pixel coords */
2554 cx = (gint) (x + 0.5);
2555 cy = (gint) (y + 0.5);
2557 /* world coords */
2559 x = canvas->scroll_x1 + x;
2560 y = canvas->scroll_y1 + y;
2562 /* find the closest item */
2564 if (canvas->root->flags & GNOME_CANVAS_ITEM_VISIBLE)
2565 canvas->new_current_item =
2566 gnome_canvas_item_invoke_point (
2567 canvas->root, x, y, cx, cy);
2568 else
2569 canvas->new_current_item = NULL;
2570 } else
2571 canvas->new_current_item = NULL;
2573 if ((canvas->new_current_item == canvas->current_item)
2574 && !canvas->left_grabbed_item)
2575 return retval; /* current item did not change */
2577 /* Synthesize events for old and new current items */
2579 if ((canvas->new_current_item != canvas->current_item)
2580 && (canvas->current_item != NULL)
2581 && !canvas->left_grabbed_item) {
2582 GdkEvent new_event;
2584 new_event = canvas->pick_event;
2585 new_event.type = GDK_LEAVE_NOTIFY;
2587 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
2588 new_event.crossing.subwindow = NULL;
2589 canvas->in_repick = TRUE;
2590 retval = emit_event (canvas, &new_event);
2591 canvas->in_repick = FALSE;
2594 /* new_current_item may have been set to NULL during the
2595 * call to emit_event() above */
2597 if ((canvas->new_current_item != canvas->current_item) && button_down) {
2598 canvas->left_grabbed_item = TRUE;
2599 return retval;
2602 /* Handle the rest of cases */
2604 canvas->left_grabbed_item = FALSE;
2605 canvas->current_item = canvas->new_current_item;
2607 if (canvas->current_item != NULL) {
2608 GdkEvent new_event;
2610 new_event = canvas->pick_event;
2611 new_event.type = GDK_ENTER_NOTIFY;
2612 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
2613 new_event.crossing.subwindow = NULL;
2614 retval = emit_event (canvas, &new_event);
2617 return retval;
2620 /* Button event handler for the canvas */
2621 static gint
2622 gnome_canvas_button (GtkWidget *widget,
2623 GdkEventButton *event)
2625 GnomeCanvas *canvas;
2626 GtkLayout *layout;
2627 GdkWindow *bin_window;
2628 gint mask;
2629 gint retval;
2631 g_return_val_if_fail (GNOME_IS_CANVAS (widget), FALSE);
2632 g_return_val_if_fail (event != NULL, FALSE);
2634 retval = FALSE;
2636 canvas = GNOME_CANVAS (widget);
2638 layout = GTK_LAYOUT (canvas);
2639 bin_window = gtk_layout_get_bin_window (layout);
2642 * dispatch normally regardless of the event's window if an item has
2643 * has a pointer grab in effect
2645 if (!canvas->grabbed_item && event->window != bin_window)
2646 return retval;
2648 switch (event->button) {
2649 case 1:
2650 mask = GDK_BUTTON1_MASK;
2651 break;
2652 case 2:
2653 mask = GDK_BUTTON2_MASK;
2654 break;
2655 case 3:
2656 mask = GDK_BUTTON3_MASK;
2657 break;
2658 case 4:
2659 mask = GDK_BUTTON4_MASK;
2660 break;
2661 case 5:
2662 mask = GDK_BUTTON5_MASK;
2663 break;
2664 default:
2665 mask = 0;
2668 switch (event->type) {
2669 case GDK_BUTTON_PRESS:
2670 case GDK_2BUTTON_PRESS:
2671 case GDK_3BUTTON_PRESS:
2672 /* Pick the current item as if the button were
2673 * not pressed, and then process the event. */
2674 canvas->state = event->state;
2675 pick_current_item (canvas, (GdkEvent *) event);
2676 canvas->state ^= mask;
2677 retval = emit_event (canvas, (GdkEvent *) event);
2678 break;
2680 case GDK_BUTTON_RELEASE:
2681 /* Process the event as if the button were pressed,
2682 * then repick after the button has been released. */
2683 canvas->state = event->state;
2684 retval = emit_event (canvas, (GdkEvent *) event);
2685 event->state ^= mask;
2686 canvas->state = event->state;
2687 pick_current_item (canvas, (GdkEvent *) event);
2688 event->state ^= mask;
2689 break;
2691 default:
2692 g_warn_if_reached ();
2695 return retval;
2698 /* Motion event handler for the canvas */
2699 static gint
2700 gnome_canvas_motion (GtkWidget *widget,
2701 GdkEventMotion *event)
2703 GnomeCanvas *canvas;
2704 GtkLayout *layout;
2705 GdkWindow *bin_window;
2707 g_return_val_if_fail (GNOME_IS_CANVAS (widget), FALSE);
2708 g_return_val_if_fail (event != NULL, FALSE);
2710 canvas = GNOME_CANVAS (widget);
2712 layout = GTK_LAYOUT (widget);
2713 bin_window = gtk_layout_get_bin_window (layout);
2715 if (event->window != bin_window)
2716 return FALSE;
2718 canvas->state = event->state;
2719 pick_current_item (canvas, (GdkEvent *) event);
2720 return emit_event (canvas, (GdkEvent *) event);
2723 /* Key event handler for the canvas */
2724 static gboolean
2725 gnome_canvas_key (GtkWidget *widget,
2726 GdkEventKey *event)
2728 GnomeCanvas *canvas;
2730 g_return_val_if_fail (GNOME_IS_CANVAS (widget), FALSE);
2731 g_return_val_if_fail (event != NULL, FALSE);
2733 canvas = GNOME_CANVAS (widget);
2735 if (!emit_event (canvas, (GdkEvent *) event)) {
2736 GtkWidgetClass *widget_class;
2738 widget_class = GTK_WIDGET_CLASS (gnome_canvas_parent_class);
2740 if (event->type == GDK_KEY_PRESS) {
2741 if (widget_class->key_press_event)
2742 return (* widget_class->key_press_event) (widget, event);
2743 } else if (event->type == GDK_KEY_RELEASE) {
2744 if (widget_class->key_release_event)
2745 return (* widget_class->key_release_event) (widget, event);
2746 } else
2747 g_warn_if_reached ();
2749 return FALSE;
2750 } else
2751 return TRUE;
2754 /* Crossing event handler for the canvas */
2755 static gint
2756 gnome_canvas_crossing (GtkWidget *widget,
2757 GdkEventCrossing *event)
2759 GnomeCanvas *canvas;
2760 GtkLayout *layout;
2761 GdkWindow *bin_window;
2763 g_return_val_if_fail (GNOME_IS_CANVAS (widget), FALSE);
2764 g_return_val_if_fail (event != NULL, FALSE);
2766 canvas = GNOME_CANVAS (widget);
2768 layout = GTK_LAYOUT (canvas);
2769 bin_window = gtk_layout_get_bin_window (layout);
2771 if (event->window != bin_window)
2772 return FALSE;
2774 /* XXX Detect and disregard synthesized crossing events generated
2775 * by synth_crossing() in gtkwidget.c. The pointer coordinates
2776 * are invalid and pick_current_item() relies on them. */
2777 if (event->x == 0 && event->y == 0 &&
2778 event->x_root == 0 && event->y_root == 0)
2779 return FALSE;
2781 canvas->state = event->state;
2782 return pick_current_item (canvas, (GdkEvent *) event);
2785 /* Focus in handler for the canvas */
2786 static gint
2787 gnome_canvas_focus_in (GtkWidget *widget,
2788 GdkEventFocus *event)
2790 GnomeCanvas *canvas;
2792 /* XXX Can't access flags directly anymore, but is it really needed?
2793 * If so, could we call gtk_widget_send_focus_change() instead? */
2794 #if 0
2795 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
2796 #endif
2798 canvas = GNOME_CANVAS (widget);
2800 if (canvas->focused_item)
2801 return emit_event (canvas, (GdkEvent *) event);
2802 else
2803 return FALSE;
2806 /* Focus out handler for the canvas */
2807 static gint
2808 gnome_canvas_focus_out (GtkWidget *widget,
2809 GdkEventFocus *event)
2811 GnomeCanvas *canvas;
2813 /* XXX Can't access flags directly anymore, but is it really needed?
2814 * If so, could we call gtk_widget_send_focus_change() instead? */
2815 #if 0
2816 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2817 #endif
2819 canvas = GNOME_CANVAS (widget);
2821 if (canvas->focused_item)
2822 return emit_event (canvas, (GdkEvent *) event);
2823 else
2824 return FALSE;
2827 static void
2828 gnome_canvas_draw_background (GnomeCanvas *canvas,
2829 cairo_t *cr,
2830 gint x,
2831 gint y,
2832 gint width,
2833 gint height)
2835 GtkStyleContext *style_context;
2836 GdkRGBA rgba;
2838 style_context = gtk_widget_get_style_context (GTK_WIDGET (canvas));
2839 if (!gtk_style_context_lookup_color (style_context, "theme_bg_color", &rgba))
2840 gdk_rgba_parse (&rgba, "#aaaaaa");
2842 cairo_save (cr);
2843 gdk_cairo_set_source_rgba (cr, &rgba);
2844 cairo_paint (cr);
2845 cairo_restore (cr);
2848 static void
2849 do_update (GnomeCanvas *canvas)
2851 /* Cause the update if necessary */
2853 update_again:
2854 if (canvas->need_update) {
2855 cairo_matrix_t w2c;
2857 /* We start updating root with w2c matrix */
2858 gnome_canvas_w2c_matrix (canvas, &w2c);
2860 gnome_canvas_item_invoke_update (canvas->root, &w2c, 0);
2862 canvas->need_update = FALSE;
2865 /* Pick new current item */
2867 while (canvas->need_repick) {
2868 canvas->need_repick = FALSE;
2869 pick_current_item (canvas, &canvas->pick_event);
2872 /* it is possible that during picking we emitted an event in which
2873 * the user then called some function which then requested update
2874 * of something. Without this we'd be left in a state where
2875 * need_update would have been left TRUE and the canvas would have
2876 * been left unpainted. */
2877 if (canvas->need_update) {
2878 goto update_again;
2882 /* Idle handler for the canvas. It deals with pending updates and redraws. */
2883 static gboolean
2884 idle_handler (gpointer data)
2886 GnomeCanvas *canvas;
2888 canvas = GNOME_CANVAS (data);
2890 do_update (canvas);
2892 /* Reset idle id */
2893 canvas->idle_id = 0;
2895 return FALSE;
2898 /* Convenience function to add an idle handler to a canvas */
2899 static void
2900 add_idle (GnomeCanvas *canvas)
2902 g_return_if_fail (canvas->need_update);
2904 if (!canvas->idle_id)
2905 canvas->idle_id = g_idle_add_full (
2906 CANVAS_IDLE_PRIORITY,
2907 idle_handler,
2908 canvas,
2909 NULL);
2911 /* canvas->idle_id = gtk_idle_add (idle_handler, canvas); */
2915 * gnome_canvas_root:
2916 * @canvas: A canvas.
2918 * Queries the root group of a canvas.
2920 * Return value: The root group of the specified canvas.
2922 GnomeCanvasGroup *
2923 gnome_canvas_root (GnomeCanvas *canvas)
2925 g_return_val_if_fail (GNOME_IS_CANVAS (canvas), NULL);
2927 return GNOME_CANVAS_GROUP (canvas->root);
2931 * gnome_canvas_set_scroll_region:
2932 * @canvas: A canvas.
2933 * @x1: Leftmost limit of the scrolling region.
2934 * @y1: Upper limit of the scrolling region.
2935 * @x2: Rightmost limit of the scrolling region.
2936 * @y2: Lower limit of the scrolling region.
2938 * Sets the scrolling region of a canvas to the specified rectangle. The canvas
2939 * will then be able to scroll only within this region. The view of the canvas
2940 * is adjusted as appropriate to display as much of the new region as possible.
2942 void
2943 gnome_canvas_set_scroll_region (GnomeCanvas *canvas,
2944 gdouble x1,
2945 gdouble y1,
2946 gdouble x2,
2947 gdouble y2)
2949 GtkScrollable *scrollable;
2950 GtkAdjustment *hadjustment;
2951 GtkAdjustment *vadjustment;
2952 gdouble hadjustment_value;
2953 gdouble vadjustment_value;
2954 gdouble wxofs, wyofs;
2955 gint xofs, yofs;
2957 g_return_if_fail (GNOME_IS_CANVAS (canvas));
2959 scrollable = GTK_SCROLLABLE (canvas);
2960 hadjustment = gtk_scrollable_get_hadjustment (scrollable);
2961 vadjustment = gtk_scrollable_get_vadjustment (scrollable);
2963 hadjustment_value = gtk_adjustment_get_value (hadjustment);
2964 vadjustment_value = gtk_adjustment_get_value (vadjustment);
2967 * Set the new scrolling region. If possible, do not move the
2968 * visible contents of the canvas.
2971 gnome_canvas_c2w (
2972 canvas,
2973 hadjustment_value + canvas->zoom_xofs,
2974 vadjustment_value + canvas->zoom_yofs,
2975 &wxofs, &wyofs);
2977 canvas->scroll_x1 = x1;
2978 canvas->scroll_y1 = y1;
2979 canvas->scroll_x2 = x2;
2980 canvas->scroll_y2 = y2;
2982 gnome_canvas_w2c (canvas, wxofs, wyofs, &xofs, &yofs);
2984 scroll_to (canvas, xofs, yofs);
2986 canvas->need_repick = TRUE;
2987 #if 0
2988 /* todo: should be requesting update */
2989 (* GNOME_CANVAS_ITEM_CLASS (canvas->root->object.class)->update) (
2990 canvas->root, NULL, NULL, 0);
2991 #endif
2995 * gnome_canvas_get_scroll_region:
2996 * @canvas: A canvas.
2997 * @x1: Leftmost limit of the scrolling region (return value).
2998 * @y1: Upper limit of the scrolling region (return value).
2999 * @x2: Rightmost limit of the scrolling region (return value).
3000 * @y2: Lower limit of the scrolling region (return value).
3002 * Queries the scrolling region of a canvas.
3004 void
3005 gnome_canvas_get_scroll_region (GnomeCanvas *canvas,
3006 gdouble *x1,
3007 gdouble *y1,
3008 gdouble *x2,
3009 gdouble *y2)
3011 g_return_if_fail (GNOME_IS_CANVAS (canvas));
3013 if (x1)
3014 *x1 = canvas->scroll_x1;
3016 if (y1)
3017 *y1 = canvas->scroll_y1;
3019 if (x2)
3020 *x2 = canvas->scroll_x2;
3022 if (y2)
3023 *y2 = canvas->scroll_y2;
3027 * gnome_canvas_scroll_to:
3028 * @canvas: A canvas.
3029 * @cx: Horizontal scrolling offset in canvas pixel units.
3030 * @cy: Vertical scrolling offset in canvas pixel units.
3032 * Makes a canvas scroll to the specified offsets, given in canvas pixel units.
3033 * The canvas will adjust the view so that it is not outside the scrolling
3034 * region. This function is typically not used, as it is better to hook
3035 * scrollbars to the canvas layout's scrolling adjusments.
3037 void
3038 gnome_canvas_scroll_to (GnomeCanvas *canvas,
3039 gint cx,
3040 gint cy)
3042 g_return_if_fail (GNOME_IS_CANVAS (canvas));
3044 scroll_to (canvas, cx, cy);
3048 * gnome_canvas_get_scroll_offsets:
3049 * @canvas: A canvas.
3050 * @cx: Horizontal scrolling offset (return value).
3051 * @cy: Vertical scrolling offset (return value).
3053 * Queries the scrolling offsets of a canvas. The values are returned in canvas
3054 * pixel units.
3056 void
3057 gnome_canvas_get_scroll_offsets (GnomeCanvas *canvas,
3058 gint *cx,
3059 gint *cy)
3061 GtkAdjustment *adjustment;
3062 GtkScrollable *scrollable;
3064 g_return_if_fail (GNOME_IS_CANVAS (canvas));
3066 scrollable = GTK_SCROLLABLE (canvas);
3068 if (cx) {
3069 adjustment = gtk_scrollable_get_hadjustment (scrollable);
3070 *cx = (gint) gtk_adjustment_get_value (adjustment);
3073 if (cy) {
3074 adjustment = gtk_scrollable_get_vadjustment (scrollable);
3075 *cy = (gint) gtk_adjustment_get_value (adjustment);
3080 * gnome_canvas_get_item_at:
3081 * @canvas: A canvas.
3082 * @x: X position in world coordinates.
3083 * @y: Y position in world coordinates.
3085 * Looks for the item that is under the specified position, which must be
3086 * specified in world coordinates.
3088 * Return value: The sought item, or NULL if no item is at the specified
3089 * coordinates.
3091 GnomeCanvasItem *
3092 gnome_canvas_get_item_at (GnomeCanvas *canvas,
3093 gdouble x,
3094 gdouble y)
3096 gint cx, cy;
3098 g_return_val_if_fail (GNOME_IS_CANVAS (canvas), NULL);
3100 gnome_canvas_w2c (canvas, x, y, &cx, &cy);
3102 return gnome_canvas_item_invoke_point (canvas->root, x, y, cx, cy);
3105 /* Queues an update of the canvas */
3106 static void
3107 gnome_canvas_request_update (GnomeCanvas *canvas)
3109 GNOME_CANVAS_GET_CLASS (canvas)->request_update (canvas);
3112 static void
3113 gnome_canvas_request_update_real (GnomeCanvas *canvas)
3115 if (canvas->need_update)
3116 return;
3118 canvas->need_update = TRUE;
3119 if (gtk_widget_get_mapped ((GtkWidget *) canvas))
3120 add_idle (canvas);
3123 static inline void
3124 get_visible_rect (GnomeCanvas *canvas,
3125 GdkRectangle *visible)
3127 GtkAllocation allocation;
3128 GtkScrollable *scrollable;
3129 GtkAdjustment *hadjustment;
3130 GtkAdjustment *vadjustment;
3131 gdouble hadjustment_value;
3132 gdouble vadjustment_value;
3134 gtk_widget_get_allocation (GTK_WIDGET (canvas), &allocation);
3136 scrollable = GTK_SCROLLABLE (canvas);
3137 hadjustment = gtk_scrollable_get_hadjustment (scrollable);
3138 vadjustment = gtk_scrollable_get_vadjustment (scrollable);
3140 hadjustment_value = gtk_adjustment_get_value (hadjustment);
3141 vadjustment_value = gtk_adjustment_get_value (vadjustment);
3143 visible->x = hadjustment_value - canvas->zoom_xofs;
3144 visible->y = vadjustment_value - canvas->zoom_yofs;
3145 visible->width = allocation.width;
3146 visible->height = allocation.height;
3150 * gnome_canvas_request_redraw:
3151 * @canvas: A canvas.
3152 * @x1: Leftmost coordinate of the rectangle to be redrawn.
3153 * @y1: Upper coordinate of the rectangle to be redrawn.
3154 * @x2: Rightmost coordinate of the rectangle to be redrawn, plus 1.
3155 * @y2: Lower coordinate of the rectangle to be redrawn, plus 1.
3157 * Convenience function that informs a canvas that the specified rectangle needs
3158 * to be repainted. The rectangle includes @x1 and @y1, but not @x2 and @y2. To
3159 * be used only by item implementations.
3161 void
3162 gnome_canvas_request_redraw (GnomeCanvas *canvas,
3163 gint x1,
3164 gint y1,
3165 gint x2,
3166 gint y2)
3168 GdkRectangle area, clip;
3170 g_return_if_fail (GNOME_IS_CANVAS (canvas));
3172 if (!gtk_widget_is_drawable (GTK_WIDGET (canvas)) || (x1 >= x2) || (y1 >= y2))
3173 return;
3175 area.x = x1;
3176 area.y = y1;
3177 area.width = x2 - x1 + 1;
3178 area.height = y2 - y1 + 1;
3180 get_visible_rect (canvas, &clip);
3181 if (!gdk_rectangle_intersect (&area, &clip, &area))
3182 return;
3184 gdk_window_invalidate_rect (
3185 gtk_layout_get_bin_window (GTK_LAYOUT (canvas)),
3186 &area, FALSE);
3190 * gnome_canvas_w2c_matrix:
3191 * @canvas: A canvas.
3192 * @matrix: (out): matrix to initialize
3194 * Gets the transformtion matrix that converts from world coordinates to canvas
3195 * pixel coordinates.
3197 void
3198 gnome_canvas_w2c_matrix (GnomeCanvas *canvas,
3199 cairo_matrix_t *matrix)
3201 g_return_if_fail (GNOME_IS_CANVAS (canvas));
3202 g_return_if_fail (matrix != NULL);
3204 cairo_matrix_init_translate (
3205 matrix, -canvas->scroll_x1, -canvas->scroll_y1);
3209 * gnome_canvas_c2w_matrix:
3210 * @canvas: A canvas.
3211 * @matrix: (out): matrix to initialize
3213 * Gets the transformtion matrix that converts from canvas pixel coordinates to
3214 * world coordinates.
3216 void
3217 gnome_canvas_c2w_matrix (GnomeCanvas *canvas,
3218 cairo_matrix_t *matrix)
3220 g_return_if_fail (GNOME_IS_CANVAS (canvas));
3221 g_return_if_fail (matrix != NULL);
3223 cairo_matrix_init_translate (
3224 matrix, canvas->scroll_x1, canvas->scroll_y1);
3228 * gnome_canvas_w2c:
3229 * @canvas: A canvas.
3230 * @wx: World X coordinate.
3231 * @wy: World Y coordinate.
3232 * @cx: X pixel coordinate (return value).
3233 * @cy: Y pixel coordinate (return value).
3235 * Converts world coordinates into canvas pixel coordinates.
3237 void
3238 gnome_canvas_w2c (GnomeCanvas *canvas,
3239 gdouble wx,
3240 gdouble wy,
3241 gint *cx,
3242 gint *cy)
3244 cairo_matrix_t w2c;
3246 g_return_if_fail (GNOME_IS_CANVAS (canvas));
3248 gnome_canvas_w2c_matrix (canvas, &w2c);
3249 cairo_matrix_transform_point (&w2c, &wx, &wy);
3251 if (cx)
3252 *cx = floor (wx + 0.5);
3253 if (cy)
3254 *cy = floor (wy + 0.5);
3258 * gnome_canvas_w2c_d:
3259 * @canvas: A canvas.
3260 * @wx: World X coordinate.
3261 * @wy: World Y coordinate.
3262 * @cx: X pixel coordinate (return value).
3263 * @cy: Y pixel coordinate (return value).
3265 * Converts world coordinates into canvas pixel coordinates. This
3266 * version returns coordinates in floating point coordinates, for
3267 * greater precision.
3269 void
3270 gnome_canvas_w2c_d (GnomeCanvas *canvas,
3271 gdouble wx,
3272 gdouble wy,
3273 gdouble *cx,
3274 gdouble *cy)
3276 cairo_matrix_t w2c;
3278 g_return_if_fail (GNOME_IS_CANVAS (canvas));
3280 gnome_canvas_w2c_matrix (canvas, &w2c);
3281 cairo_matrix_transform_point (&w2c, &wx, &wy);
3283 if (cx)
3284 *cx = wx;
3285 if (cy)
3286 *cy = wy;
3290 * gnome_canvas_c2w:
3291 * @canvas: A canvas.
3292 * @cx: Canvas pixel X coordinate.
3293 * @cy: Canvas pixel Y coordinate.
3294 * @wx: X world coordinate (return value).
3295 * @wy: Y world coordinate (return value).
3297 * Converts canvas pixel coordinates to world coordinates.
3299 void
3300 gnome_canvas_c2w (GnomeCanvas *canvas,
3301 gint cx,
3302 gint cy,
3303 gdouble *wx,
3304 gdouble *wy)
3306 cairo_matrix_t c2w;
3307 gdouble x, y;
3309 g_return_if_fail (GNOME_IS_CANVAS (canvas));
3311 x = cx;
3312 y = cy;
3313 gnome_canvas_c2w_matrix (canvas, &c2w);
3314 cairo_matrix_transform_point (&c2w, &x, &y);
3316 if (wx)
3317 *wx = x;
3318 if (wy)
3319 *wy = y;
3323 * gnome_canvas_window_to_world:
3324 * @canvas: A canvas.
3325 * @winx: Window-relative X coordinate.
3326 * @winy: Window-relative Y coordinate.
3327 * @worldx: X world coordinate (return value).
3328 * @worldy: Y world coordinate (return value).
3330 * Converts window-relative coordinates into world coordinates. You can use
3331 * this when you need to convert mouse coordinates into world coordinates, for
3332 * example.
3334 void
3335 gnome_canvas_window_to_world (GnomeCanvas *canvas,
3336 gdouble winx,
3337 gdouble winy,
3338 gdouble *worldx,
3339 gdouble *worldy)
3341 g_return_if_fail (GNOME_IS_CANVAS (canvas));
3343 if (worldx)
3344 *worldx = canvas->scroll_x1 + (winx - canvas->zoom_xofs);
3346 if (worldy)
3347 *worldy = canvas->scroll_y1 + (winy - canvas->zoom_yofs);
3351 * gnome_canvas_world_to_window:
3352 * @canvas: A canvas.
3353 * @worldx: World X coordinate.
3354 * @worldy: World Y coordinate.
3355 * @winx: X window-relative coordinate.
3356 * @winy: Y window-relative coordinate.
3358 * Converts world coordinates into window-relative coordinates.
3360 void
3361 gnome_canvas_world_to_window (GnomeCanvas *canvas,
3362 gdouble worldx,
3363 gdouble worldy,
3364 gdouble *winx,
3365 gdouble *winy)
3367 g_return_if_fail (GNOME_IS_CANVAS (canvas));
3369 if (winx)
3370 *winx = (worldx - canvas->scroll_x1) + canvas->zoom_xofs;
3372 if (winy)
3373 *winy = (worldy - canvas->scroll_y1) + canvas->zoom_yofs;
3376 static gboolean
3377 boolean_handled_accumulator (GSignalInvocationHint *ihint,
3378 GValue *return_accu,
3379 const GValue *handler_return,
3380 gpointer dummy)
3382 gboolean continue_emission;
3383 gboolean signal_handled;
3385 signal_handled = g_value_get_boolean (handler_return);
3386 g_value_set_boolean (return_accu, signal_handled);
3387 continue_emission = !signal_handled;
3389 return continue_emission;
3392 /* Class initialization function for GnomeCanvasItemClass */
3393 static void
3394 gnome_canvas_item_class_init (GnomeCanvasItemClass *class)
3396 GObjectClass *gobject_class;
3398 gobject_class = (GObjectClass *) class;
3400 gobject_class->set_property = gnome_canvas_item_set_property;
3401 gobject_class->get_property = gnome_canvas_item_get_property;
3403 g_object_class_install_property (
3404 gobject_class,
3405 ITEM_PROP_PARENT,
3406 g_param_spec_object (
3407 "parent",
3408 NULL,
3409 NULL,
3410 GNOME_TYPE_CANVAS_ITEM,
3411 G_PARAM_READABLE |
3412 G_PARAM_WRITABLE));
3414 item_signals[ITEM_EVENT] = g_signal_new (
3415 "event",
3416 G_TYPE_FROM_CLASS (class),
3417 G_SIGNAL_RUN_LAST,
3418 G_STRUCT_OFFSET (GnomeCanvasItemClass, event),
3419 boolean_handled_accumulator, NULL, NULL,
3420 G_TYPE_BOOLEAN, 1,
3421 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
3423 gobject_class->dispose = gnome_canvas_item_dispose;
3425 class->update = gnome_canvas_item_update;
3426 class->realize = gnome_canvas_item_realize;
3427 class->unrealize = gnome_canvas_item_unrealize;
3428 class->map = gnome_canvas_item_map;
3429 class->unmap = gnome_canvas_item_unmap;
3430 class->dispose = gnome_canvas_item_dispose_item;
3431 class->draw = gnome_canvas_item_draw;
3432 class->point = gnome_canvas_item_point;
3433 class->bounds = gnome_canvas_item_bounds;
3434 class->event = gnome_canvas_item_event;