project-wizard: Improve gnome shell extension template
[anjuta.git] / libfoocanvas / foo-canvas.c
blobf08ec21de4bdc27c93ed30709262c61c1010ce34
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 * The Gnome Library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) any later version.
13 * The Gnome Library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public
19 * License along with the Gnome Library; see the file COPYING.LIB. If not,
20 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
24 @NOTATION@
27 * FooCanvas widget - Tk-like canvas widget for Gnome
29 * FooCanvas is basically a port of the Tk toolkit's most excellent canvas widget. Tk is
30 * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
33 * Authors: Federico Mena <federico@nuclecu.unam.mx>
34 * Raph Levien <raph@gimp.org>
38 * TO-DO list for the canvas:
40 * - Allow to specify whether FooCanvasImage sizes are in units or pixels (scale or don't scale).
42 * - Implement a flag for foo_canvas_item_reparent() that tells the function to keep the item
43 * visually in the same place, that is, to keep it in the same place with respect to the canvas
44 * origin.
46 * - GC put functions for items.
48 * - Widget item (finish it).
50 * - GList *foo_canvas_gimme_all_items_contained_in_this_area (FooCanvas *canvas, Rectangle area);
52 * - Retrofit all the primitive items with microtile support.
54 * - Curve support for line item.
56 * - Arc item (Havoc has it; to be integrated in FooCanvasEllipse).
58 * - Sane font handling API.
60 * - Get_arg methods for items:
61 * - How to fetch the outline width and know whether it is in pixels or units?
64 #include <config.h>
66 #include <math.h>
67 #include <string.h>
68 #include <stdio.h>
69 #include <gdk/gdkprivate.h>
70 #include <gtk/gtk.h>
71 #include "foo-canvas.h"
72 #include "foo-canvas-i18n.h"
74 #include "foo-canvas-marshal.h"
76 static void foo_canvas_request_update (FooCanvas *canvas);
77 static void group_add (FooCanvasGroup *group,
78 FooCanvasItem *item);
79 static void group_remove (FooCanvasGroup *group,
80 FooCanvasItem *item);
81 static void redraw_and_repick_if_mapped (FooCanvasItem *item);
83 /*** FooCanvasItem ***/
85 /* Some convenience stuff */
86 #define GCI_UPDATE_MASK (FOO_CANVAS_UPDATE_REQUESTED | FOO_CANVAS_UPDATE_DEEP)
87 #define GCI_EPSILON 1e-18
89 enum {
90 ITEM_PROP_0,
91 ITEM_PROP_PARENT,
92 ITEM_PROP_VISIBLE
95 enum {
96 ITEM_EVENT,
97 ITEM_LAST_SIGNAL
100 static void foo_canvas_item_class_init (FooCanvasItemClass *klass);
101 static void foo_canvas_item_init (FooCanvasItem *item);
102 static int emit_event (FooCanvas *canvas, GdkEvent *event);
104 static guint item_signals[ITEM_LAST_SIGNAL];
106 static GtkObjectClass *item_parent_class;
108 static gpointer accessible_item_parent_class;
109 static gpointer accessible_parent_class;
113 * foo_canvas_item_get_type:
115 * Registers the &FooCanvasItem class if necessary, and returns the type ID
116 * associated to it.
118 * Return value: The type ID of the &FooCanvasItem class.
120 GType
121 foo_canvas_item_get_type (void)
123 static GType canvas_item_type = 0;
125 if (!canvas_item_type) {
126 static const GTypeInfo canvas_item_info = {
127 sizeof (FooCanvasItemClass),
128 (GBaseInitFunc) NULL,
129 (GBaseFinalizeFunc) NULL,
130 (GClassInitFunc) foo_canvas_item_class_init,
131 NULL, /* class_finalize */
132 NULL, /* class_data */
133 sizeof (FooCanvasItem),
134 0, /* n_preallocs */
135 (GInstanceInitFunc) foo_canvas_item_init
138 canvas_item_type = g_type_register_static (gtk_object_get_type (),
139 "FooCanvasItem",
140 &canvas_item_info,
144 return canvas_item_type;
147 /* Object initialization function for FooCanvasItem */
148 static void
149 foo_canvas_item_init (FooCanvasItem *item)
151 item->object.flags |= FOO_CANVAS_ITEM_VISIBLE;
155 * foo_canvas_item_new:
156 * @parent: The parent group for the new item.
157 * @type: The object type of the item.
158 * @first_arg_name: A list of object argument name/value pairs, NULL-terminated,
159 * used to configure the item. For example, "fill_color", "black",
160 * "width_units", 5.0, NULL.
161 * @Varargs:
163 * Creates a new canvas item with @parent as its parent group. The item is
164 * created at the top of its parent's stack, and starts up as visible. The item
165 * is of the specified @type, for example, it can be
166 * foo_canvas_rect_get_type(). The list of object arguments/value pairs is
167 * used to configure the item.
169 * Return value: The newly-created item.
171 FooCanvasItem *
172 foo_canvas_item_new (FooCanvasGroup *parent, GType type, const gchar *first_arg_name, ...)
174 FooCanvasItem *item;
175 va_list args;
177 g_return_val_if_fail (FOO_IS_CANVAS_GROUP (parent), NULL);
178 g_return_val_if_fail (g_type_is_a (type, foo_canvas_item_get_type ()), NULL);
180 item = FOO_CANVAS_ITEM (g_object_new (type, NULL));
182 va_start (args, first_arg_name);
183 foo_canvas_item_construct (item, parent, first_arg_name, args);
184 va_end (args);
186 return item;
190 /* Performs post-creation operations on a canvas item (adding it to its parent
191 * group, etc.)
193 static void
194 item_post_create_setup (FooCanvasItem *item)
196 group_add (FOO_CANVAS_GROUP (item->parent), item);
198 redraw_and_repick_if_mapped (item);
201 /* Set_property handler for canvas items */
202 static void
203 foo_canvas_item_set_property (GObject *gobject, guint param_id,
204 const GValue *value, GParamSpec *pspec)
206 FooCanvasItem *item;
208 g_return_if_fail (FOO_IS_CANVAS_ITEM (gobject));
210 item = FOO_CANVAS_ITEM (gobject);
212 switch (param_id) {
213 case ITEM_PROP_PARENT:
214 if (item->parent != NULL) {
215 g_warning ("Cannot set `parent' argument after item has "
216 "already been constructed.");
217 } else if (g_value_get_object (value)) {
218 item->parent = FOO_CANVAS_ITEM (g_value_get_object (value));
219 item->canvas = item->parent->canvas;
220 item_post_create_setup (item);
222 break;
223 case ITEM_PROP_VISIBLE:
224 if (g_value_get_boolean (value)) {
225 foo_canvas_item_show (item);
226 } else {
227 foo_canvas_item_hide (item);
229 break;
230 default:
231 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
232 break;
236 /* Get_property handler for canvas items */
237 static void
238 foo_canvas_item_get_property (GObject *gobject, guint param_id,
239 GValue *value, GParamSpec *pspec)
241 FooCanvasItem *item;
243 g_return_if_fail (FOO_IS_CANVAS_ITEM (gobject));
245 item = FOO_CANVAS_ITEM (gobject);
247 switch (param_id) {
248 case ITEM_PROP_VISIBLE:
249 g_value_set_boolean (value, item->object.flags & FOO_CANVAS_ITEM_VISIBLE);
250 break;
251 default:
252 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
253 break;
258 * foo_canvas_item_construct:
259 * @item: An unconstructed canvas item.
260 * @parent: The parent group for the item.
261 * @first_arg_name: The name of the first argument for configuring the item.
262 * @args: The list of arguments used to configure the item.
264 * Constructs a canvas item; meant for use only by item implementations.
266 void
267 foo_canvas_item_construct (FooCanvasItem *item, FooCanvasGroup *parent,
268 const gchar *first_arg_name, va_list args)
270 g_return_if_fail (FOO_IS_CANVAS_GROUP (parent));
271 g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
273 item->parent = FOO_CANVAS_ITEM (parent);
274 item->canvas = item->parent->canvas;
276 g_object_set_valist (G_OBJECT (item), first_arg_name, args);
278 item_post_create_setup (item);
282 static void
283 redraw_and_repick_if_mapped (FooCanvasItem *item)
285 if (item->object.flags & FOO_CANVAS_ITEM_MAPPED) {
286 foo_canvas_item_request_redraw (item);
287 item->canvas->need_repick = TRUE;
291 /* Dispose handler for canvas items */
292 static void
293 foo_canvas_item_dispose (GObject *object)
295 FooCanvasItem *item;
297 g_return_if_fail (FOO_IS_CANVAS_ITEM (object));
299 item = FOO_CANVAS_ITEM (object);
301 if (item->canvas) {
302 foo_canvas_item_request_redraw (item);
304 /* Make the canvas forget about us */
306 if (item == item->canvas->current_item) {
307 item->canvas->current_item = NULL;
308 item->canvas->need_repick = TRUE;
311 if (item == item->canvas->new_current_item) {
312 item->canvas->new_current_item = NULL;
313 item->canvas->need_repick = TRUE;
316 if (item == item->canvas->grabbed_item) {
317 GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (item->canvas));
318 item->canvas->grabbed_item = NULL;
319 gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
322 if (item == item->canvas->focused_item)
323 item->canvas->focused_item = NULL;
325 /* Normal destroy stuff */
327 if (item->object.flags & FOO_CANVAS_ITEM_MAPPED)
328 (* FOO_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
330 if (item->object.flags & FOO_CANVAS_ITEM_REALIZED)
331 (* FOO_CANVAS_ITEM_GET_CLASS (item)->unrealize) (item);
333 if (item->parent)
334 group_remove (FOO_CANVAS_GROUP (item->parent), item);
336 item->canvas = NULL;
339 G_OBJECT_CLASS (item_parent_class)->dispose (object);
343 /* Realize handler for canvas items */
344 static void
345 foo_canvas_item_realize (FooCanvasItem *item)
347 if (item->parent && !(item->parent->object.flags & FOO_CANVAS_ITEM_REALIZED))
348 (* FOO_CANVAS_ITEM_GET_CLASS (item->parent)->realize) (item->parent);
350 if (item->parent == NULL && !gtk_widget_get_realized (GTK_WIDGET (item->canvas)))
351 gtk_widget_realize (GTK_WIDGET (item->canvas));
353 GTK_OBJECT_SET_FLAGS (item, FOO_CANVAS_ITEM_REALIZED);
355 foo_canvas_item_request_update (item);
358 /* Unrealize handler for canvas items */
359 static void
360 foo_canvas_item_unrealize (FooCanvasItem *item)
362 if (item->object.flags & FOO_CANVAS_ITEM_MAPPED)
363 (* FOO_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
365 GTK_OBJECT_UNSET_FLAGS (item, FOO_CANVAS_ITEM_REALIZED);
368 /* Map handler for canvas items */
369 static void
370 foo_canvas_item_map (FooCanvasItem *item)
372 GTK_OBJECT_SET_FLAGS (item, FOO_CANVAS_ITEM_MAPPED);
375 /* Unmap handler for canvas items */
376 static void
377 foo_canvas_item_unmap (FooCanvasItem *item)
379 GTK_OBJECT_UNSET_FLAGS (item, FOO_CANVAS_ITEM_MAPPED);
382 /* Update handler for canvas items */
383 static void
384 foo_canvas_item_update (FooCanvasItem *item, double i2w_dx, double i2w_dy, int flags)
386 GTK_OBJECT_UNSET_FLAGS (item, FOO_CANVAS_ITEM_NEED_UPDATE);
387 GTK_OBJECT_UNSET_FLAGS (item, FOO_CANVAS_ITEM_NEED_DEEP_UPDATE);
391 * This routine invokes the update method of the item
392 * Please notice, that we take parent to canvas pixel matrix as argument
393 * unlike virtual method ::update, whose argument is item 2 canvas pixel
394 * matrix
396 * I will try to force somewhat meaningful naming for affines (Lauris)
397 * General naming rule is FROM2TO, where FROM and TO are abbreviations
398 * So p2cpx is Parent2CanvasPixel and i2cpx is Item2CanvasPixel
399 * I hope that this helps to keep track of what really happens
403 static void
404 foo_canvas_item_invoke_update (FooCanvasItem *item,
405 double i2w_dx,
406 double i2w_dy,
407 int flags)
409 int child_flags;
411 child_flags = flags;
413 /* apply object flags to child flags */
414 child_flags &= ~FOO_CANVAS_UPDATE_REQUESTED;
416 if (item->object.flags & FOO_CANVAS_ITEM_NEED_UPDATE)
417 child_flags |= FOO_CANVAS_UPDATE_REQUESTED;
419 if (item->object.flags & FOO_CANVAS_ITEM_NEED_DEEP_UPDATE)
420 child_flags |= FOO_CANVAS_UPDATE_DEEP;
422 if (child_flags & GCI_UPDATE_MASK) {
423 if (FOO_CANVAS_ITEM_GET_CLASS (item)->update)
424 FOO_CANVAS_ITEM_GET_CLASS (item)->update (item, i2w_dx, i2w_dy, child_flags);
427 /* If this fail you probably forgot to chain up to
428 * FooCanvasItem::update from a derived class */
429 g_return_if_fail (!(item->object.flags & FOO_CANVAS_ITEM_NEED_UPDATE));
433 * This routine invokes the point method of the item.
434 * The arguments x, y should be in the parent item local coordinates.
437 static double
438 foo_canvas_item_invoke_point (FooCanvasItem *item, double x, double y, int cx, int cy, FooCanvasItem **actual_item)
440 /* Calculate x & y in item local coordinates */
442 if (FOO_CANVAS_ITEM_GET_CLASS (item)->point)
443 return FOO_CANVAS_ITEM_GET_CLASS (item)->point (item, x, y, cx, cy, actual_item);
445 return 1e18;
449 * foo_canvas_item_set:
450 * @item: A canvas item.
451 * @first_arg_name: The list of object argument name/value pairs used to configure the item.
452 * @Varargs:
454 * Configures a canvas item. The arguments in the item are set to the specified
455 * values, and the item is repainted as appropriate.
457 void
458 foo_canvas_item_set (FooCanvasItem *item, const gchar *first_arg_name, ...)
460 va_list args;
462 va_start (args, first_arg_name);
463 foo_canvas_item_set_valist (item, first_arg_name, args);
464 va_end (args);
469 * foo_canvas_item_set_valist:
470 * @item: A canvas item.
471 * @first_arg_name: The name of the first argument used to configure the item.
472 * @args: The list of object argument name/value pairs used to configure the item.
474 * Configures a canvas item. The arguments in the item are set to the specified
475 * values, and the item is repainted as appropriate.
477 void
478 foo_canvas_item_set_valist (FooCanvasItem *item, const gchar *first_arg_name, va_list args)
480 g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
482 g_object_set_valist (G_OBJECT (item), first_arg_name, args);
484 #if 0
485 /* I commented this out, because item implementations have to schedule update/redraw */
486 foo_canvas_item_request_redraw (item);
487 #endif
489 item->canvas->need_repick = TRUE;
494 * foo_canvas_item_move:
495 * @item: A canvas item.
496 * @dx: Horizontal offset.
497 * @dy: Vertical offset.
499 * Moves a canvas item by creating an affine transformation matrix for
500 * translation by using the specified values. This happens in item
501 * local coordinate system, so if you have nontrivial transform, it
502 * most probably does not do, what you want.
504 void
505 foo_canvas_item_move (FooCanvasItem *item, double dx, double dy)
507 g_return_if_fail (item != NULL);
508 g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
510 if (!FOO_CANVAS_ITEM_GET_CLASS (item)->translate) {
511 g_warning ("Item type %s does not implement translate method.\n",
512 g_type_name (G_OBJECT_TYPE (item)));
513 return;
516 (* FOO_CANVAS_ITEM_GET_CLASS (item)->translate) (item, dx, dy);
518 if (item->object.flags & FOO_CANVAS_ITEM_MAPPED)
519 item->canvas->need_repick = TRUE;
521 if (!(item->object.flags & FOO_CANVAS_ITEM_NEED_DEEP_UPDATE)) {
522 item->object.flags |= FOO_CANVAS_ITEM_NEED_DEEP_UPDATE;
523 if (item->parent != NULL)
524 foo_canvas_item_request_update (item->parent);
525 else
526 foo_canvas_request_update (item->canvas);
531 /* Convenience function to reorder items in a group's child list. This puts the
532 * specified link after the "before" link. Returns TRUE if the list was changed.
534 static gboolean
535 put_item_after (GList *link, GList *before)
537 FooCanvasGroup *parent;
539 if (link == before)
540 return FALSE;
542 parent = FOO_CANVAS_GROUP (FOO_CANVAS_ITEM (link->data)->parent);
544 if (before == NULL) {
545 if (link == parent->item_list)
546 return FALSE;
548 link->prev->next = link->next;
550 if (link->next)
551 link->next->prev = link->prev;
552 else
553 parent->item_list_end = link->prev;
555 link->prev = before;
556 link->next = parent->item_list;
557 link->next->prev = link;
558 parent->item_list = link;
559 } else {
560 if ((link == parent->item_list_end) && (before == parent->item_list_end->prev))
561 return FALSE;
563 if (link->next)
564 link->next->prev = link->prev;
566 if (link->prev)
567 link->prev->next = link->next;
568 else {
569 parent->item_list = link->next;
570 parent->item_list->prev = NULL;
573 link->prev = before;
574 link->next = before->next;
576 link->prev->next = link;
578 if (link->next)
579 link->next->prev = link;
580 else
581 parent->item_list_end = link;
583 return TRUE;
588 * foo_canvas_item_raise:
589 * @item: A canvas item.
590 * @positions: Number of steps to raise the item.
592 * Raises the item in its parent's stack by the specified number of positions.
593 * If the number of positions is greater than the distance to the top of the
594 * stack, then the item is put at the top.
596 void
597 foo_canvas_item_raise (FooCanvasItem *item, int positions)
599 GList *link, *before;
600 FooCanvasGroup *parent;
602 g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
603 g_return_if_fail (positions >= 0);
605 if (!item->parent || positions == 0)
606 return;
608 parent = FOO_CANVAS_GROUP (item->parent);
609 link = g_list_find (parent->item_list, item);
610 g_assert (link != NULL);
612 for (before = link; positions && before; positions--)
613 before = before->next;
615 if (!before)
616 before = parent->item_list_end;
618 if (put_item_after (link, before)) {
619 redraw_and_repick_if_mapped (item);
625 * foo_canvas_item_lower:
626 * @item: A canvas item.
627 * @positions: Number of steps to lower the item.
629 * Lowers the item in its parent's stack by the specified number of positions.
630 * If the number of positions is greater than the distance to the bottom of the
631 * stack, then the item is put at the bottom.
633 void
634 foo_canvas_item_lower (FooCanvasItem *item, int positions)
636 GList *link, *before;
637 FooCanvasGroup *parent;
639 g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
640 g_return_if_fail (positions >= 1);
642 if (!item->parent || positions == 0)
643 return;
645 parent = FOO_CANVAS_GROUP (item->parent);
646 link = g_list_find (parent->item_list, item);
647 g_assert (link != NULL);
649 if (link->prev)
650 for (before = link->prev; positions && before; positions--)
651 before = before->prev;
652 else
653 before = NULL;
655 if (put_item_after (link, before)) {
656 redraw_and_repick_if_mapped (item);
662 * foo_canvas_item_raise_to_top:
663 * @item: A canvas item.
665 * Raises an item to the top of its parent's stack.
667 void
668 foo_canvas_item_raise_to_top (FooCanvasItem *item)
670 GList *link;
671 FooCanvasGroup *parent;
673 g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
675 if (!item->parent)
676 return;
678 parent = FOO_CANVAS_GROUP (item->parent);
679 link = g_list_find (parent->item_list, item);
680 g_assert (link != NULL);
682 if (put_item_after (link, parent->item_list_end)) {
683 redraw_and_repick_if_mapped (item);
689 * foo_canvas_item_lower_to_bottom:
690 * @item: A canvas item.
692 * Lowers an item to the bottom of its parent's stack.
694 void
695 foo_canvas_item_lower_to_bottom (FooCanvasItem *item)
697 GList *link;
698 FooCanvasGroup *parent;
700 g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
702 if (!item->parent)
703 return;
705 parent = FOO_CANVAS_GROUP (item->parent);
706 link = g_list_find (parent->item_list, item);
707 g_assert (link != NULL);
709 if (put_item_after (link, NULL)) {
710 redraw_and_repick_if_mapped (item);
715 * foo_canvas_item_send_behind:
716 * @item: A canvas item.
717 * @behind_item: The canvas item to put item behind, or NULL
719 * Moves item to a in the position in the stacking order so that
720 * it is placed immediately below behind_item, or at the top if
721 * behind_item is NULL.
723 void
724 foo_canvas_item_send_behind (FooCanvasItem *item,
725 FooCanvasItem *behind_item)
727 GList *item_list;
728 int item_position, behind_position;
730 g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
732 if (behind_item == NULL) {
733 foo_canvas_item_raise_to_top (item);
734 return;
737 g_return_if_fail (FOO_IS_CANVAS_ITEM (behind_item));
738 g_return_if_fail (item->parent == behind_item->parent);
740 item_list = FOO_CANVAS_GROUP (item->parent)->item_list;
742 item_position = g_list_index (item_list, item);
743 g_assert (item_position != -1);
744 behind_position = g_list_index (item_list, behind_item);
745 g_assert (behind_position != -1);
746 g_assert (item_position != behind_position);
748 if (item_position == behind_position - 1) {
749 return;
752 if (item_position < behind_position) {
753 foo_canvas_item_raise (item, (behind_position - 1) - item_position);
754 } else {
755 foo_canvas_item_lower (item, item_position - behind_position);
760 * foo_canvas_item_show:
761 * @item: A canvas item.
763 * Shows a canvas item. If the item was already shown, then no action is taken.
765 void
766 foo_canvas_item_show (FooCanvasItem *item)
768 g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
770 if (!(item->object.flags & FOO_CANVAS_ITEM_VISIBLE)) {
771 item->object.flags |= FOO_CANVAS_ITEM_VISIBLE;
773 if (!(item->object.flags & FOO_CANVAS_ITEM_REALIZED))
774 (* FOO_CANVAS_ITEM_GET_CLASS (item)->realize) (item);
776 if (item->parent != NULL) {
777 if (!(item->object.flags & FOO_CANVAS_ITEM_MAPPED) &&
778 item->parent->object.flags & FOO_CANVAS_ITEM_MAPPED)
779 (* FOO_CANVAS_ITEM_GET_CLASS (item)->map) (item);
780 } else {
781 if (!(item->object.flags & FOO_CANVAS_ITEM_MAPPED) &&
782 gtk_widget_get_mapped (GTK_WIDGET (item->canvas)))
783 (* FOO_CANVAS_ITEM_GET_CLASS (item)->map) (item);
786 redraw_and_repick_if_mapped (item);
792 * foo_canvas_item_hide:
793 * @item: A canvas item.
795 * Hides a canvas item. If the item was already hidden, then no action is
796 * taken.
798 void
799 foo_canvas_item_hide (FooCanvasItem *item)
801 g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
803 if (item->object.flags & FOO_CANVAS_ITEM_VISIBLE) {
804 item->object.flags &= ~FOO_CANVAS_ITEM_VISIBLE;
806 redraw_and_repick_if_mapped (item);
808 if (item->object.flags & FOO_CANVAS_ITEM_MAPPED)
809 (* FOO_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
811 /* No need to unrealize when we just want to hide */
817 * foo_canvas_item_grab:
818 * @item: A canvas item.
819 * @event_mask: Mask of events that will be sent to this item.
820 * @cursor: If non-NULL, the cursor that will be used while the grab is active.
821 * @etime: The timestamp required for grabbing the mouse, or GDK_CURRENT_TIME.
823 * Specifies that all events that match the specified event mask should be sent
824 * to the specified item, and also grabs the mouse by calling
825 * gdk_pointer_grab(). The event mask is also used when grabbing the pointer.
826 * If @cursor is not NULL, then that cursor is used while the grab is active.
827 * The @etime parameter is the timestamp required for grabbing the mouse.
829 * Return value: If an item was already grabbed, it returns %GDK_GRAB_ALREADY_GRABBED. If
830 * the specified item was hidden by calling foo_canvas_item_hide(), then it
831 * returns %GDK_GRAB_NOT_VIEWABLE. Else, it returns the result of calling
832 * gdk_pointer_grab().
835 foo_canvas_item_grab (FooCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
837 int retval;
839 g_return_val_if_fail (FOO_IS_CANVAS_ITEM (item), GDK_GRAB_NOT_VIEWABLE);
840 g_return_val_if_fail (gtk_widget_get_mapped (GTK_WIDGET (item->canvas)), GDK_GRAB_NOT_VIEWABLE);
842 if (item->canvas->grabbed_item)
843 return GDK_GRAB_ALREADY_GRABBED;
845 if (!(item->object.flags & FOO_CANVAS_ITEM_MAPPED))
846 return GDK_GRAB_NOT_VIEWABLE;
848 retval = gdk_pointer_grab (item->canvas->layout.bin_window,
849 FALSE,
850 event_mask,
851 NULL,
852 cursor,
853 etime);
855 if (retval != GDK_GRAB_SUCCESS)
856 return retval;
858 item->canvas->grabbed_item = item;
859 item->canvas->grabbed_event_mask = event_mask;
860 item->canvas->current_item = item; /* So that events go to the grabbed item */
862 return retval;
867 * foo_canvas_item_ungrab:
868 * @item: A canvas item that holds a grab.
869 * @etime: The timestamp for ungrabbing the mouse.
871 * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
872 * mouse.
874 void
875 foo_canvas_item_ungrab (FooCanvasItem *item, guint32 etime)
877 GdkDisplay *display;
879 g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
881 if (item->canvas->grabbed_item != item)
882 return;
884 display = gtk_widget_get_display (GTK_WIDGET (item->canvas));
885 item->canvas->grabbed_item = NULL;
886 gdk_display_pointer_ungrab (display, etime);
891 * foo_canvas_item_w2i:
892 * @item: A canvas item.
893 * @x: X coordinate to convert (input/output value).
894 * @y: Y coordinate to convert (input/output value).
896 * Converts a coordinate pair from world coordinates to item-relative
897 * coordinates.
899 void
900 foo_canvas_item_w2i (FooCanvasItem *item, double *x, double *y)
902 g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
903 g_return_if_fail (x != NULL);
904 g_return_if_fail (y != NULL);
906 item = item->parent;
907 while (item) {
908 if (FOO_IS_CANVAS_GROUP (item)) {
909 *x -= FOO_CANVAS_GROUP (item)->xpos;
910 *y -= FOO_CANVAS_GROUP (item)->ypos;
913 item = item->parent;
919 * foo_canvas_item_i2w:
920 * @item: A canvas item.
921 * @x: X coordinate to convert (input/output value).
922 * @y: Y coordinate to convert (input/output value).
924 * Converts a coordinate pair from item-relative coordinates to world
925 * coordinates.
927 void
928 foo_canvas_item_i2w (FooCanvasItem *item, double *x, double *y)
930 g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
931 g_return_if_fail (x != NULL);
932 g_return_if_fail (y != NULL);
934 item = item->parent;
935 while (item) {
936 if (FOO_IS_CANVAS_GROUP (item)) {
937 *x += FOO_CANVAS_GROUP (item)->xpos;
938 *y += FOO_CANVAS_GROUP (item)->ypos;
941 item = item->parent;
945 /* Returns whether the item is an inferior of or is equal to the parent. */
946 static int
947 is_descendant (FooCanvasItem *item, FooCanvasItem *parent)
949 for (; item; item = item->parent)
950 if (item == parent)
951 return TRUE;
953 return FALSE;
957 * foo_canvas_item_reparent:
958 * @item: A canvas item.
959 * @new_group: A canvas group.
961 * Changes the parent of the specified item to be the new group. The item keeps
962 * its group-relative coordinates as for its old parent, so the item may change
963 * its absolute position within the canvas.
965 void
966 foo_canvas_item_reparent (FooCanvasItem *item, FooCanvasGroup *new_group)
968 g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
969 g_return_if_fail (FOO_IS_CANVAS_GROUP (new_group));
971 /* Both items need to be in the same canvas */
972 g_return_if_fail (item->canvas == FOO_CANVAS_ITEM (new_group)->canvas);
974 /* The group cannot be an inferior of the item or be the item itself --
975 * this also takes care of the case where the item is the root item of
976 * the canvas. */
977 g_return_if_fail (!is_descendant (FOO_CANVAS_ITEM (new_group), item));
979 /* Everything is ok, now actually reparent the item */
981 g_object_ref (G_OBJECT (item)); /* protect it from the unref in group_remove */
983 foo_canvas_item_request_redraw (item);
985 group_remove (FOO_CANVAS_GROUP (item->parent), item);
986 item->parent = FOO_CANVAS_ITEM (new_group);
987 /* item->canvas is unchanged. */
988 group_add (new_group, item);
990 /* Redraw and repick */
992 redraw_and_repick_if_mapped (item);
994 g_object_unref (G_OBJECT (item));
998 * foo_canvas_item_grab_focus:
999 * @item: A canvas item.
1001 * Makes the specified item take the keyboard focus, so all keyboard events will
1002 * be sent to it. If the canvas widget itself did not have the focus, it grabs
1003 * it as well.
1005 void
1006 foo_canvas_item_grab_focus (FooCanvasItem *item)
1008 FooCanvasItem *focused_item;
1009 GdkEvent ev;
1011 g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
1012 g_return_if_fail (gtk_widget_get_can_focus (GTK_WIDGET (item->canvas)));
1014 focused_item = item->canvas->focused_item;
1016 if (focused_item) {
1017 ev.focus_change.type = GDK_FOCUS_CHANGE;
1018 ev.focus_change.window = GTK_LAYOUT (item->canvas)->bin_window;
1019 ev.focus_change.send_event = FALSE;
1020 ev.focus_change.in = FALSE;
1022 emit_event (item->canvas, &ev);
1025 item->canvas->focused_item = item;
1026 gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
1028 if (focused_item) {
1029 ev.focus_change.type = GDK_FOCUS_CHANGE;
1030 ev.focus_change.window = GTK_LAYOUT (item->canvas)->bin_window;
1031 ev.focus_change.send_event = FALSE;
1032 ev.focus_change.in = TRUE;
1034 emit_event (item->canvas, &ev);
1040 * foo_canvas_item_get_bounds:
1041 * @item: A canvas item.
1042 * @x1: Leftmost edge of the bounding box (return value).
1043 * @y1: Upper edge of the bounding box (return value).
1044 * @x2: Rightmost edge of the bounding box (return value).
1045 * @y2: Lower edge of the bounding box (return value).
1047 * Queries the bounding box of a canvas item. The bounds are returned in the
1048 * coordinate system of the item's parent.
1050 void
1051 foo_canvas_item_get_bounds (FooCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
1053 double tx1, ty1, tx2, ty2;
1055 g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
1057 tx1 = ty1 = tx2 = ty2 = 0.0;
1059 /* Get the item's bounds in its coordinate system */
1061 if (FOO_CANVAS_ITEM_GET_CLASS (item)->bounds)
1062 (* FOO_CANVAS_ITEM_GET_CLASS (item)->bounds) (item, &tx1, &ty1, &tx2, &ty2);
1064 /* Return the values */
1066 if (x1)
1067 *x1 = tx1;
1069 if (y1)
1070 *y1 = ty1;
1072 if (x2)
1073 *x2 = tx2;
1075 if (y2)
1076 *y2 = ty2;
1081 * foo_canvas_item_request_update
1082 * @item: A canvas item.
1084 * To be used only by item implementations. Requests that the canvas queue an
1085 * update for the specified item.
1087 void
1088 foo_canvas_item_request_update (FooCanvasItem *item)
1090 if (NULL == item->canvas)
1091 return;
1093 g_return_if_fail (!item->canvas->doing_update);
1095 if (item->object.flags & FOO_CANVAS_ITEM_NEED_UPDATE)
1096 return;
1098 item->object.flags |= FOO_CANVAS_ITEM_NEED_UPDATE;
1100 if (item->parent != NULL) {
1101 /* Recurse up the tree */
1102 foo_canvas_item_request_update (item->parent);
1103 } else {
1104 /* Have reached the top of the tree, make sure the update call gets scheduled. */
1105 foo_canvas_request_update (item->canvas);
1110 * foo_canvas_item_request_update
1111 * @item: A canvas item.
1113 * Convenience function that informs a canvas that the specified item needs
1114 * to be repainted. To be used by item implementations
1116 void
1117 foo_canvas_item_request_redraw (FooCanvasItem *item)
1119 if (item->object.flags & FOO_CANVAS_ITEM_MAPPED)
1120 foo_canvas_request_redraw (item->canvas,
1121 item->x1, item->y1,
1122 item->x2 + 1, item->y2 + 1);
1127 /*** FooCanvasGroup ***/
1130 enum {
1131 GROUP_PROP_0,
1132 GROUP_PROP_X,
1133 GROUP_PROP_Y
1137 static void foo_canvas_group_class_init (FooCanvasGroupClass *klass);
1138 static void foo_canvas_group_init (FooCanvasGroup *group);
1139 static void foo_canvas_group_set_property(GObject *object,
1140 guint param_id,
1141 const GValue *value,
1142 GParamSpec *pspec);
1143 static void foo_canvas_group_get_property(GObject *object,
1144 guint param_id,
1145 GValue *value,
1146 GParamSpec *pspec);
1148 static void foo_canvas_group_destroy (GtkObject *object);
1150 static void foo_canvas_group_update (FooCanvasItem *item,
1151 double i2w_dx,
1152 double i2w_dy,
1153 int flags);
1154 static void foo_canvas_group_unrealize (FooCanvasItem *item);
1155 static void foo_canvas_group_map (FooCanvasItem *item);
1156 static void foo_canvas_group_unmap (FooCanvasItem *item);
1157 static void foo_canvas_group_draw (FooCanvasItem *item, GdkDrawable *drawable,
1158 GdkEventExpose *expose);
1159 static double foo_canvas_group_point (FooCanvasItem *item, double x, double y,
1160 int cx, int cy,
1161 FooCanvasItem **actual_item);
1162 static void foo_canvas_group_translate (FooCanvasItem *item, double dx, double dy);
1163 static void foo_canvas_group_bounds (FooCanvasItem *item, double *x1, double *y1,
1164 double *x2, double *y2);
1167 static FooCanvasItemClass *group_parent_class;
1171 * foo_canvas_group_get_type:
1173 * Registers the &FooCanvasGroup class if necessary, and returns the type ID
1174 * associated to it.
1176 * Return value: The type ID of the &FooCanvasGroup class.
1178 GType
1179 foo_canvas_group_get_type (void)
1181 static GType group_type = 0;
1183 if (!group_type) {
1184 static const GTypeInfo group_info = {
1185 sizeof (FooCanvasGroupClass),
1186 (GBaseInitFunc) NULL,
1187 (GBaseFinalizeFunc) NULL,
1188 (GClassInitFunc) foo_canvas_group_class_init,
1189 NULL, /* class_finalize */
1190 NULL, /* class_data */
1191 sizeof (FooCanvasGroup),
1192 0, /* n_preallocs */
1193 (GInstanceInitFunc) foo_canvas_group_init
1198 group_type = g_type_register_static (foo_canvas_item_get_type (),
1199 "FooCanvasGroup",
1200 &group_info,
1204 return group_type;
1207 /* Class initialization function for FooCanvasGroupClass */
1208 static void
1209 foo_canvas_group_class_init (FooCanvasGroupClass *klass)
1211 GObjectClass *gobject_class;
1212 GtkObjectClass *object_class;
1213 FooCanvasItemClass *item_class;
1215 gobject_class = (GObjectClass *) klass;
1216 object_class = (GtkObjectClass *) klass;
1217 item_class = (FooCanvasItemClass *) klass;
1219 group_parent_class = g_type_class_peek_parent (klass);
1221 gobject_class->set_property = foo_canvas_group_set_property;
1222 gobject_class->get_property = foo_canvas_group_get_property;
1224 g_object_class_install_property
1225 (gobject_class, GROUP_PROP_X,
1226 g_param_spec_double ("x",
1227 _("X"),
1228 _("X"),
1229 -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
1230 G_PARAM_READWRITE));
1231 g_object_class_install_property
1232 (gobject_class, GROUP_PROP_Y,
1233 g_param_spec_double ("y",
1234 _("Y"),
1235 _("Y"),
1236 -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
1237 G_PARAM_READWRITE));
1239 object_class->destroy = foo_canvas_group_destroy;
1241 item_class->update = foo_canvas_group_update;
1242 item_class->unrealize = foo_canvas_group_unrealize;
1243 item_class->map = foo_canvas_group_map;
1244 item_class->unmap = foo_canvas_group_unmap;
1245 item_class->draw = foo_canvas_group_draw;
1246 item_class->point = foo_canvas_group_point;
1247 item_class->translate = foo_canvas_group_translate;
1248 item_class->bounds = foo_canvas_group_bounds;
1251 /* Object initialization function for FooCanvasGroup */
1252 static void
1253 foo_canvas_group_init (FooCanvasGroup *group)
1255 group->xpos = 0.0;
1256 group->ypos = 0.0;
1259 /* Set_property handler for canvas groups */
1260 static void
1261 foo_canvas_group_set_property (GObject *gobject, guint param_id,
1262 const GValue *value, GParamSpec *pspec)
1264 FooCanvasItem *item;
1265 FooCanvasGroup *group;
1266 double old;
1267 gboolean moved;
1269 g_return_if_fail (FOO_IS_CANVAS_GROUP (gobject));
1271 item = FOO_CANVAS_ITEM (gobject);
1272 group = FOO_CANVAS_GROUP (gobject);
1274 moved = FALSE;
1275 switch (param_id) {
1276 case GROUP_PROP_X:
1277 old = group->xpos;
1278 group->xpos = g_value_get_double (value);
1279 if (old != group->xpos)
1280 moved = TRUE;
1281 break;
1283 case GROUP_PROP_Y:
1284 old = group->ypos;
1285 group->ypos = g_value_get_double (value);
1286 if (old != group->ypos)
1287 moved = TRUE;
1288 break;
1290 default:
1291 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
1292 break;
1295 if (moved) {
1296 item->object.flags |= FOO_CANVAS_ITEM_NEED_DEEP_UPDATE;
1297 if (item->parent != NULL)
1298 foo_canvas_item_request_update (item->parent);
1299 else
1300 foo_canvas_request_update (item->canvas);
1304 /* Get_property handler for canvas groups */
1305 static void
1306 foo_canvas_group_get_property (GObject *gobject, guint param_id,
1307 GValue *value, GParamSpec *pspec)
1309 FooCanvasItem *item;
1310 FooCanvasGroup *group;
1312 g_return_if_fail (FOO_IS_CANVAS_GROUP (gobject));
1314 item = FOO_CANVAS_ITEM (gobject);
1315 group = FOO_CANVAS_GROUP (gobject);
1317 switch (param_id) {
1318 case GROUP_PROP_X:
1319 g_value_set_double (value, group->xpos);
1320 break;
1322 case GROUP_PROP_Y:
1323 g_value_set_double (value, group->ypos);
1324 break;
1326 default:
1327 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
1328 break;
1332 /* Destroy handler for canvas groups */
1333 static void
1334 foo_canvas_group_destroy (GtkObject *object)
1336 FooCanvasGroup *group;
1337 FooCanvasItem *child;
1338 GList *list;
1340 g_return_if_fail (FOO_IS_CANVAS_GROUP (object));
1342 group = FOO_CANVAS_GROUP (object);
1344 list = group->item_list;
1345 while (list) {
1346 child = list->data;
1347 list = list->next;
1349 gtk_object_destroy (GTK_OBJECT (child));
1352 if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
1353 (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
1356 /* Update handler for canvas groups */
1357 static void
1358 foo_canvas_group_update (FooCanvasItem *item, double i2w_dx, double i2w_dy, int flags)
1360 FooCanvasGroup *group;
1361 GList *list;
1362 FooCanvasItem *i;
1363 double bbox_x0, bbox_y0, bbox_x1, bbox_y1;
1364 gboolean first = TRUE;
1366 group = FOO_CANVAS_GROUP (item);
1368 (* group_parent_class->update) (item, i2w_dx, i2w_dy, flags);
1370 bbox_x0 = 0;
1371 bbox_y0 = 0;
1372 bbox_x1 = 0;
1373 bbox_y1 = 0;
1375 for (list = group->item_list; list; list = list->next) {
1376 i = list->data;
1378 foo_canvas_item_invoke_update (i, i2w_dx + group->xpos, i2w_dy + group->ypos, flags);
1380 if (first) {
1381 first = FALSE;
1382 bbox_x0 = i->x1;
1383 bbox_y0 = i->y1;
1384 bbox_x1 = i->x2;
1385 bbox_y1 = i->y2;
1386 } else {
1387 bbox_x0 = MIN (bbox_x0, i->x1);
1388 bbox_y0 = MIN (bbox_y0, i->y1);
1389 bbox_x1 = MAX (bbox_x1, i->x2);
1390 bbox_y1 = MAX (bbox_y1, i->y2);
1393 item->x1 = bbox_x0;
1394 item->y1 = bbox_y0;
1395 item->x2 = bbox_x1;
1396 item->y2 = bbox_y1;
1399 /* Unrealize handler for canvas groups */
1400 static void
1401 foo_canvas_group_unrealize (FooCanvasItem *item)
1403 FooCanvasGroup *group;
1404 GList *list;
1405 FooCanvasItem *i;
1407 group = FOO_CANVAS_GROUP (item);
1409 /* Unmap group before children to avoid flash */
1410 if (item->object.flags & FOO_CANVAS_ITEM_MAPPED)
1411 (* FOO_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
1413 for (list = group->item_list; list; list = list->next) {
1414 i = list->data;
1416 if (i->object.flags & FOO_CANVAS_ITEM_REALIZED)
1417 (* FOO_CANVAS_ITEM_GET_CLASS (i)->unrealize) (i);
1420 (* group_parent_class->unrealize) (item);
1423 /* Map handler for canvas groups */
1424 static void
1425 foo_canvas_group_map (FooCanvasItem *item)
1427 FooCanvasGroup *group;
1428 GList *list;
1429 FooCanvasItem *i;
1431 group = FOO_CANVAS_GROUP (item);
1433 for (list = group->item_list; list; list = list->next) {
1434 i = list->data;
1436 if (i->object.flags & FOO_CANVAS_ITEM_VISIBLE &&
1437 !(i->object.flags & FOO_CANVAS_ITEM_MAPPED)) {
1438 if (!(i->object.flags & FOO_CANVAS_ITEM_REALIZED))
1439 (* FOO_CANVAS_ITEM_GET_CLASS (i)->realize) (i);
1441 (* FOO_CANVAS_ITEM_GET_CLASS (i)->map) (i);
1445 (* group_parent_class->map) (item);
1448 /* Unmap handler for canvas groups */
1449 static void
1450 foo_canvas_group_unmap (FooCanvasItem *item)
1452 FooCanvasGroup *group;
1453 GList *list;
1454 FooCanvasItem *i;
1456 group = FOO_CANVAS_GROUP (item);
1458 for (list = group->item_list; list; list = list->next) {
1459 i = list->data;
1461 if (i->object.flags & FOO_CANVAS_ITEM_MAPPED)
1462 (* FOO_CANVAS_ITEM_GET_CLASS (i)->unmap) (i);
1465 (* group_parent_class->unmap) (item);
1468 /* Draw handler for canvas groups */
1469 static void
1470 foo_canvas_group_draw (FooCanvasItem *item, GdkDrawable *drawable,
1471 GdkEventExpose *expose)
1473 FooCanvasGroup *group;
1474 GList *list;
1475 FooCanvasItem *child = NULL;
1477 group = FOO_CANVAS_GROUP (item);
1479 for (list = group->item_list; list; list = list->next) {
1480 child = list->data;
1482 if ((child->object.flags & FOO_CANVAS_ITEM_MAPPED) &&
1483 (FOO_CANVAS_ITEM_GET_CLASS (child)->draw)) {
1484 GdkRectangle child_rect;
1486 child_rect.x = child->x1;
1487 child_rect.y = child->y1;
1488 child_rect.width = child->x2 - child->x1 + 1;
1489 child_rect.height = child->y2 - child->y1 + 1;
1491 if (gdk_region_rect_in (expose->region, &child_rect) != GDK_OVERLAP_RECTANGLE_OUT)
1492 (* FOO_CANVAS_ITEM_GET_CLASS (child)->draw) (child, drawable, expose);
1497 /* Point handler for canvas groups */
1498 static double
1499 foo_canvas_group_point (FooCanvasItem *item, double x, double y, int cx, int cy,
1500 FooCanvasItem **actual_item)
1502 FooCanvasGroup *group;
1503 GList *list;
1504 FooCanvasItem *child, *point_item;
1505 int x1, y1, x2, y2;
1506 double gx, gy;
1507 double dist, best;
1508 int has_point;
1510 group = FOO_CANVAS_GROUP (item);
1512 x1 = cx - item->canvas->close_enough;
1513 y1 = cy - item->canvas->close_enough;
1514 x2 = cx + item->canvas->close_enough;
1515 y2 = cy + item->canvas->close_enough;
1517 best = 0.0;
1518 *actual_item = NULL;
1520 gx = x - group->xpos;
1521 gy = y - group->ypos;
1523 dist = 0.0; /* keep gcc happy */
1525 for (list = group->item_list; list; list = list->next) {
1526 child = list->data;
1528 if ((child->x1 > x2) || (child->y1 > y2) || (child->x2 < x1) || (child->y2 < y1))
1529 continue;
1531 point_item = NULL; /* cater for incomplete item implementations */
1533 if ((child->object.flags & FOO_CANVAS_ITEM_MAPPED)
1534 && FOO_CANVAS_ITEM_GET_CLASS (child)->point) {
1535 dist = foo_canvas_item_invoke_point (child, gx, gy, cx, cy, &point_item);
1536 has_point = TRUE;
1537 } else
1538 has_point = FALSE;
1540 if (has_point
1541 && point_item
1542 && ((int) (dist * item->canvas->pixels_per_unit + 0.5)
1543 <= item->canvas->close_enough)) {
1544 best = dist;
1545 *actual_item = point_item;
1549 return best;
1552 void
1553 foo_canvas_group_translate (FooCanvasItem *item, double dx, double dy)
1555 FooCanvasGroup *group;
1557 group = FOO_CANVAS_GROUP (item);
1559 group->xpos += dx;
1560 group->ypos += dy;
1563 /* Bounds handler for canvas groups */
1564 static void
1565 foo_canvas_group_bounds (FooCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
1567 FooCanvasGroup *group;
1568 FooCanvasItem *child;
1569 GList *list;
1570 double tx1, ty1, tx2, ty2;
1571 double minx, miny, maxx, maxy;
1572 int set;
1574 group = FOO_CANVAS_GROUP (item);
1576 /* Get the bounds of the first visible item */
1578 child = NULL; /* Unnecessary but eliminates a warning. */
1580 set = FALSE;
1582 for (list = group->item_list; list; list = list->next) {
1583 child = list->data;
1585 if (child->object.flags & FOO_CANVAS_ITEM_MAPPED) {
1586 set = TRUE;
1587 foo_canvas_item_get_bounds (child, &minx, &miny, &maxx, &maxy);
1588 break;
1592 /* If there were no visible items, return an empty bounding box */
1594 if (!set) {
1595 *x1 = *y1 = *x2 = *y2 = 0.0;
1596 return;
1599 /* Now we can grow the bounds using the rest of the items */
1601 list = list->next;
1603 for (; list; list = list->next) {
1604 child = list->data;
1606 if (!(child->object.flags & FOO_CANVAS_ITEM_MAPPED))
1607 continue;
1609 foo_canvas_item_get_bounds (child, &tx1, &ty1, &tx2, &ty2);
1611 if (tx1 < minx)
1612 minx = tx1;
1614 if (ty1 < miny)
1615 miny = ty1;
1617 if (tx2 > maxx)
1618 maxx = tx2;
1620 if (ty2 > maxy)
1621 maxy = ty2;
1624 /* Make the bounds be relative to our parent's coordinate system */
1626 if (item->parent) {
1627 minx += group->xpos;
1628 miny += group->ypos;
1629 maxx += group->xpos;
1630 maxy += group->ypos;
1633 *x1 = minx;
1634 *y1 = miny;
1635 *x2 = maxx;
1636 *y2 = maxy;
1639 /* Adds an item to a group */
1640 static void
1641 group_add (FooCanvasGroup *group, FooCanvasItem *item)
1643 #if GLIB_CHECK_VERSION(2,10,0) && GTK_CHECK_VERSION(2,8,14)
1644 g_object_ref_sink (item);
1645 #else
1646 g_object_ref (item);
1647 gtk_object_sink (GTK_OBJECT (item));
1648 #endif
1650 if (!group->item_list) {
1651 group->item_list = g_list_append (group->item_list, item);
1652 group->item_list_end = group->item_list;
1653 } else
1654 group->item_list_end = g_list_append (group->item_list_end, item)->next;
1656 if (item->object.flags & FOO_CANVAS_ITEM_VISIBLE &&
1657 group->item.object.flags & FOO_CANVAS_ITEM_MAPPED) {
1658 if (!(item->object.flags & FOO_CANVAS_ITEM_REALIZED))
1659 (* FOO_CANVAS_ITEM_GET_CLASS (item)->realize) (item);
1661 if (!(item->object.flags & FOO_CANVAS_ITEM_MAPPED))
1662 (* FOO_CANVAS_ITEM_GET_CLASS (item)->map) (item);
1666 /* Removes an item from a group */
1667 static void
1668 group_remove (FooCanvasGroup *group, FooCanvasItem *item)
1670 GList *children;
1672 g_return_if_fail (FOO_IS_CANVAS_GROUP (group));
1673 g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
1675 for (children = group->item_list; children; children = children->next)
1676 if (children->data == item) {
1677 if (item->object.flags & FOO_CANVAS_ITEM_MAPPED)
1678 (* FOO_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
1680 if (item->object.flags & FOO_CANVAS_ITEM_REALIZED)
1681 (* FOO_CANVAS_ITEM_GET_CLASS (item)->unrealize) (item);
1683 /* Unparent the child */
1685 item->parent = NULL;
1686 /* item->canvas = NULL; */
1687 g_object_unref (G_OBJECT (item));
1689 /* Remove it from the list */
1691 if (children == group->item_list_end)
1692 group->item_list_end = children->prev;
1694 group->item_list = g_list_remove_link (group->item_list, children);
1695 g_list_free (children);
1696 break;
1701 /*** FooCanvas ***/
1704 enum {
1705 DRAW_BACKGROUND,
1706 LAST_SIGNAL
1709 static void foo_canvas_class_init (FooCanvasClass *klass);
1710 static void foo_canvas_init (FooCanvas *canvas);
1711 static void foo_canvas_destroy (GtkObject *object);
1712 static void foo_canvas_map (GtkWidget *widget);
1713 static void foo_canvas_unmap (GtkWidget *widget);
1714 static void foo_canvas_realize (GtkWidget *widget);
1715 static void foo_canvas_unrealize (GtkWidget *widget);
1716 static void foo_canvas_size_allocate (GtkWidget *widget,
1717 GtkAllocation *allocation);
1718 static gint foo_canvas_button (GtkWidget *widget,
1719 GdkEventButton *event);
1720 static gint foo_canvas_motion (GtkWidget *widget,
1721 GdkEventMotion *event);
1722 static gint foo_canvas_expose (GtkWidget *widget,
1723 GdkEventExpose *event);
1724 static gint foo_canvas_key (GtkWidget *widget,
1725 GdkEventKey *event);
1726 static gint foo_canvas_crossing (GtkWidget *widget,
1727 GdkEventCrossing *event);
1728 static gint foo_canvas_focus_in (GtkWidget *widget,
1729 GdkEventFocus *event);
1730 static gint foo_canvas_focus_out (GtkWidget *widget,
1731 GdkEventFocus *event);
1732 static void foo_canvas_request_update_real (FooCanvas *canvas);
1733 static void foo_canvas_draw_background (FooCanvas *canvas,
1734 int x,
1735 int y,
1736 int width,
1737 int height);
1740 static GtkLayoutClass *canvas_parent_class;
1742 static guint canvas_signals[LAST_SIGNAL];
1745 * foo_canvas_get_type:
1747 * Registers the &FooCanvas class if necessary, and returns the type ID
1748 * associated to it.
1750 * Return value: The type ID of the &FooCanvas class.
1752 GType
1753 foo_canvas_get_type (void)
1755 static GType canvas_type = 0;
1757 if (!canvas_type) {
1758 static const GTypeInfo canvas_info = {
1759 sizeof (FooCanvasClass),
1760 (GBaseInitFunc) NULL,
1761 (GBaseFinalizeFunc) NULL,
1762 (GClassInitFunc) foo_canvas_class_init,
1763 NULL, /* class_finalize */
1764 NULL, /* class_data */
1765 sizeof (FooCanvas),
1766 0, /* n_preallocs */
1767 (GInstanceInitFunc) foo_canvas_init
1770 canvas_type = g_type_register_static (gtk_layout_get_type (),
1771 "FooCanvas",
1772 &canvas_info,
1776 return canvas_type;
1779 static void
1780 foo_canvas_get_property (GObject *object,
1781 guint prop_id,
1782 GValue *value,
1783 GParamSpec *pspec)
1785 switch (prop_id) {
1786 default:
1787 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1788 break;
1792 static void
1793 foo_canvas_set_property (GObject *object,
1794 guint prop_id,
1795 const GValue *value,
1796 GParamSpec *pspec)
1798 switch (prop_id) {
1799 default:
1800 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1801 break;
1805 static void
1806 foo_canvas_accessible_adjustment_changed (GtkAdjustment *adjustment,
1807 gpointer data)
1809 AtkObject *atk_obj;
1811 /* The scrollbars have changed */
1812 atk_obj = ATK_OBJECT (data);
1814 g_signal_emit_by_name (atk_obj, "visible_data_changed");
1817 static void
1818 foo_canvas_accessible_initialize (AtkObject *obj,
1819 gpointer data)
1821 FooCanvas *canvas;
1823 if (ATK_OBJECT_CLASS (accessible_parent_class)->initialize != NULL)
1824 ATK_OBJECT_CLASS (accessible_parent_class)->initialize (obj, data);
1826 canvas = FOO_CANVAS (data);
1827 g_signal_connect (canvas->layout.hadjustment,
1828 "value_changed",
1829 G_CALLBACK (foo_canvas_accessible_adjustment_changed),
1830 obj);
1831 g_signal_connect (canvas->layout.vadjustment,
1832 "value_changed",
1833 G_CALLBACK (foo_canvas_accessible_adjustment_changed),
1834 obj);
1836 obj->role = ATK_ROLE_LAYERED_PANE;
1839 static gint
1840 foo_canvas_accessible_get_n_children (AtkObject* obj)
1842 GtkAccessible *accessible;
1843 GtkWidget *widget;
1844 FooCanvas *canvas;
1845 FooCanvasGroup *root_group;
1847 accessible = GTK_ACCESSIBLE (obj);
1848 widget = accessible->widget;
1849 if (widget == NULL) {
1850 /* State is defunct */
1851 return 0;
1854 g_return_val_if_fail (FOO_IS_CANVAS (widget), 0);
1856 canvas = FOO_CANVAS (widget);
1857 root_group = foo_canvas_root (canvas);
1858 g_return_val_if_fail (root_group, 0);
1859 return 1;
1862 static AtkObject*
1863 foo_canvas_accessible_ref_child (AtkObject *obj,
1864 gint i)
1866 GtkAccessible *accessible;
1867 GtkWidget *widget;
1868 FooCanvas *canvas;
1869 FooCanvasGroup *root_group;
1870 AtkObject *atk_object;
1872 /* Canvas only has one child, so return NULL if index is non zero */
1873 if (i != 0) {
1874 return NULL;
1877 accessible = GTK_ACCESSIBLE (obj);
1878 widget = accessible->widget;
1879 if (widget == NULL) {
1880 /* State is defunct */
1881 return NULL;
1884 canvas = FOO_CANVAS (widget);
1885 root_group = foo_canvas_root (canvas);
1886 g_return_val_if_fail (root_group, NULL);
1887 atk_object = atk_gobject_accessible_for_object (G_OBJECT (root_group));
1888 g_object_ref (atk_object);
1890 g_warning ("Accessible support for FooGroup needs to be implemented");
1892 return atk_object;
1895 static void
1896 foo_canvas_accessible_class_init (AtkObjectClass *klass)
1898 accessible_parent_class = g_type_class_peek_parent (klass);
1900 klass->initialize = foo_canvas_accessible_initialize;
1901 klass->get_n_children = foo_canvas_accessible_get_n_children;
1902 klass->ref_child = foo_canvas_accessible_ref_child;
1905 static GType
1906 foo_canvas_accessible_get_type (void)
1908 static GType type = 0;
1910 if (!type) {
1911 AtkObjectFactory *factory;
1912 GType parent_atk_type;
1913 GTypeQuery query;
1914 GTypeInfo tinfo = { 0 };
1916 factory = atk_registry_get_factory (atk_get_default_registry(),
1917 GTK_TYPE_WIDGET);
1918 if (!factory) {
1919 return G_TYPE_INVALID;
1921 parent_atk_type = atk_object_factory_get_accessible_type (factory);
1922 if (!parent_atk_type) {
1923 return G_TYPE_INVALID;
1925 g_type_query (parent_atk_type, &query);
1926 tinfo.class_init = (GClassInitFunc) foo_canvas_accessible_class_init;
1927 tinfo.class_size = query.class_size;
1928 tinfo.instance_size = query.instance_size;
1929 type = g_type_register_static (parent_atk_type,
1930 "FooCanvasAccessibility",
1931 &tinfo, 0);
1933 return type;
1936 static AtkObject *
1937 foo_canvas_accessible_create (GObject *for_object)
1939 GType type;
1940 AtkObject *accessible;
1941 FooCanvas *canvas;
1943 canvas = FOO_CANVAS (for_object);
1944 g_return_val_if_fail (canvas != NULL, NULL);
1946 type = foo_canvas_accessible_get_type ();
1948 if (type == G_TYPE_INVALID) {
1949 return atk_no_op_object_new (for_object);
1952 accessible = g_object_new (type, NULL);
1953 atk_object_initialize (accessible, for_object);
1954 return accessible;
1957 static GType
1958 foo_canvas_accessible_factory_get_accessible_type (void)
1960 return foo_canvas_accessible_get_type ();
1963 static AtkObject*
1964 foo_canvas_accessible_factory_create_accessible (GObject *obj)
1966 AtkObject *accessible;
1968 g_return_val_if_fail (G_IS_OBJECT (obj), NULL);
1970 accessible = foo_canvas_accessible_create (obj);
1972 return accessible;
1975 static void
1976 foo_canvas_accessible_factory_class_init (AtkObjectFactoryClass *klass)
1978 klass->create_accessible = foo_canvas_accessible_factory_create_accessible;
1979 klass->get_accessible_type = foo_canvas_accessible_factory_get_accessible_type;
1982 static GType
1983 foo_canvas_accessible_factory_get_type (void)
1985 static GType type = 0;
1987 if (!type) {
1988 static const GTypeInfo tinfo = {
1989 sizeof (AtkObjectFactoryClass),
1990 (GBaseInitFunc) NULL,
1991 (GBaseFinalizeFunc) NULL,
1992 (GClassInitFunc) foo_canvas_accessible_factory_class_init,
1993 NULL, /* class_finalize */
1994 NULL, /* class_data */
1995 sizeof (AtkObjectFactory),
1996 0, /* n_preallocs */
1997 NULL
1999 type = g_type_register_static (ATK_TYPE_OBJECT_FACTORY,
2000 "FooCanvasAccessibilityFactory",
2001 &tinfo, 0);
2004 return type;
2008 /* Class initialization function for FooCanvasClass */
2009 static void
2010 foo_canvas_class_init (FooCanvasClass *klass)
2012 GObjectClass *gobject_class;
2013 GtkObjectClass *object_class;
2014 GtkWidgetClass *widget_class;
2016 gobject_class = (GObjectClass *)klass;
2017 object_class = (GtkObjectClass *) klass;
2018 widget_class = (GtkWidgetClass *) klass;
2020 canvas_parent_class = g_type_class_peek_parent (klass);
2022 gobject_class->set_property = foo_canvas_set_property;
2023 gobject_class->get_property = foo_canvas_get_property;
2025 object_class->destroy = foo_canvas_destroy;
2027 widget_class->map = foo_canvas_map;
2028 widget_class->unmap = foo_canvas_unmap;
2029 widget_class->realize = foo_canvas_realize;
2030 widget_class->unrealize = foo_canvas_unrealize;
2031 widget_class->size_allocate = foo_canvas_size_allocate;
2032 widget_class->button_press_event = foo_canvas_button;
2033 widget_class->button_release_event = foo_canvas_button;
2034 widget_class->motion_notify_event = foo_canvas_motion;
2035 widget_class->expose_event = foo_canvas_expose;
2036 widget_class->key_press_event = foo_canvas_key;
2037 widget_class->key_release_event = foo_canvas_key;
2038 widget_class->enter_notify_event = foo_canvas_crossing;
2039 widget_class->leave_notify_event = foo_canvas_crossing;
2040 widget_class->focus_in_event = foo_canvas_focus_in;
2041 widget_class->focus_out_event = foo_canvas_focus_out;
2043 klass->draw_background = foo_canvas_draw_background;
2044 klass->request_update = foo_canvas_request_update_real;
2046 canvas_signals[DRAW_BACKGROUND] =
2047 g_signal_new ("draw_background",
2048 G_TYPE_FROM_CLASS (object_class),
2049 G_SIGNAL_RUN_LAST,
2050 G_STRUCT_OFFSET (FooCanvasClass, draw_background),
2051 NULL, NULL,
2052 foo_canvas_marshal_VOID__INT_INT_INT_INT,
2053 G_TYPE_NONE, 4,
2054 G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT);
2056 atk_registry_set_factory_type (atk_get_default_registry (),
2057 FOO_TYPE_CANVAS,
2058 foo_canvas_accessible_factory_get_type ());
2061 /* Callback used when the root item of a canvas is destroyed. The user should
2062 * never ever do this, so we panic if this happens.
2064 static void
2065 panic_root_destroyed (GtkObject *object, gpointer data)
2067 g_error ("Eeeek, root item %p of canvas %p was destroyed!", object, data);
2070 /* Object initialization function for FooCanvas */
2071 static void
2072 foo_canvas_init (FooCanvas *canvas)
2074 GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
2076 gtk_widget_set_redraw_on_allocate (GTK_WIDGET (canvas), FALSE);
2078 canvas->scroll_x1 = 0.0;
2079 canvas->scroll_y1 = 0.0;
2080 canvas->scroll_x2 = canvas->layout.width;
2081 canvas->scroll_y2 = canvas->layout.height;
2083 canvas->pixels_per_unit = 1.0;
2085 canvas->pick_event.type = GDK_LEAVE_NOTIFY;
2086 canvas->pick_event.crossing.x = 0;
2087 canvas->pick_event.crossing.y = 0;
2089 gtk_layout_set_hadjustment (GTK_LAYOUT (canvas), NULL);
2090 gtk_layout_set_vadjustment (GTK_LAYOUT (canvas), NULL);
2092 /* Create the root item as a special case */
2094 canvas->root = FOO_CANVAS_ITEM (g_object_new (foo_canvas_group_get_type (), NULL));
2095 canvas->root->canvas = canvas;
2097 #if GLIB_CHECK_VERSION(2,10,0) && GTK_CHECK_VERSION(2,8,14)
2098 g_object_ref_sink (canvas->root);
2099 #else
2100 g_object_ref (canvas->root);
2101 gtk_object_sink (GTK_OBJECT (canvas->root));
2102 #endif
2104 canvas->root_destroy_id = g_signal_connect (G_OBJECT (canvas->root),
2105 "destroy", G_CALLBACK (panic_root_destroyed), canvas);
2107 canvas->need_repick = TRUE;
2108 canvas->doing_update = FALSE;
2111 /* Convenience function to remove the idle handler of a canvas */
2112 static void
2113 remove_idle (FooCanvas *canvas)
2115 if (canvas->idle_id == 0)
2116 return;
2118 g_source_remove (canvas->idle_id);
2119 canvas->idle_id = 0;
2122 /* Removes the transient state of the canvas (idle handler, grabs). */
2123 static void
2124 shutdown_transients (FooCanvas *canvas)
2126 /* We turn off the need_redraw flag, since if the canvas is mapped again
2127 * it will request a redraw anyways. We do not turn off the need_update
2128 * flag, though, because updates are not queued when the canvas remaps
2129 * itself.
2131 if (canvas->need_redraw) {
2132 canvas->need_redraw = FALSE;
2135 if (canvas->grabbed_item) {
2136 GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (canvas));
2137 canvas->grabbed_item = NULL;
2138 gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
2141 remove_idle (canvas);
2144 /* Destroy handler for FooCanvas */
2145 static void
2146 foo_canvas_destroy (GtkObject *object)
2148 FooCanvas *canvas;
2150 g_return_if_fail (FOO_IS_CANVAS (object));
2152 /* remember, destroy can be run multiple times! */
2154 canvas = FOO_CANVAS (object);
2156 if (canvas->root_destroy_id) {
2157 g_signal_handler_disconnect (G_OBJECT (canvas->root), canvas->root_destroy_id);
2158 canvas->root_destroy_id = 0;
2160 if (canvas->root) {
2161 FooCanvasItem *root = canvas->root;
2162 canvas->root = NULL;
2163 gtk_object_destroy (GTK_OBJECT (root));
2164 g_object_unref (root);
2167 shutdown_transients (canvas);
2169 if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
2170 (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
2174 * foo_canvas_new:
2175 * @void:
2177 * Creates a new empty canvas. If you wish to use the
2178 * &FooCanvasImage item inside this canvas, then you must push the gdk_imlib
2179 * visual and colormap before calling this function, and they can be popped
2180 * afterwards.
2182 * Return value: A newly-created canvas.
2184 GtkWidget *
2185 foo_canvas_new (void)
2187 return GTK_WIDGET (g_object_new (foo_canvas_get_type (), NULL));
2190 /* Map handler for the canvas */
2191 static void
2192 foo_canvas_map (GtkWidget *widget)
2194 FooCanvas *canvas;
2196 g_return_if_fail (FOO_IS_CANVAS (widget));
2198 /* Normal widget mapping stuff */
2200 if (GTK_WIDGET_CLASS (canvas_parent_class)->map)
2201 (* GTK_WIDGET_CLASS (canvas_parent_class)->map) (widget);
2203 canvas = FOO_CANVAS (widget);
2205 /* Map items */
2207 if (canvas->root->object.flags & FOO_CANVAS_ITEM_VISIBLE &&
2208 !(canvas->root->object.flags & FOO_CANVAS_ITEM_MAPPED) &&
2209 FOO_CANVAS_ITEM_GET_CLASS (canvas->root)->map)
2210 (* FOO_CANVAS_ITEM_GET_CLASS (canvas->root)->map) (canvas->root);
2213 /* Unmap handler for the canvas */
2214 static void
2215 foo_canvas_unmap (GtkWidget *widget)
2217 FooCanvas *canvas;
2219 g_return_if_fail (FOO_IS_CANVAS (widget));
2221 canvas = FOO_CANVAS (widget);
2223 shutdown_transients (canvas);
2225 /* Unmap items */
2227 if (FOO_CANVAS_ITEM_GET_CLASS (canvas->root)->unmap)
2228 (* FOO_CANVAS_ITEM_GET_CLASS (canvas->root)->unmap) (canvas->root);
2230 /* Normal widget unmapping stuff */
2232 if (GTK_WIDGET_CLASS (canvas_parent_class)->unmap)
2233 (* GTK_WIDGET_CLASS (canvas_parent_class)->unmap) (widget);
2236 /* Realize handler for the canvas */
2237 static void
2238 foo_canvas_realize (GtkWidget *widget)
2240 FooCanvas *canvas;
2242 g_return_if_fail (FOO_IS_CANVAS (widget));
2244 /* Normal widget realization stuff */
2246 if (GTK_WIDGET_CLASS (canvas_parent_class)->realize)
2247 (* GTK_WIDGET_CLASS (canvas_parent_class)->realize) (widget);
2249 canvas = FOO_CANVAS (widget);
2251 gdk_window_set_events (canvas->layout.bin_window,
2252 (gdk_window_get_events (canvas->layout.bin_window)
2253 | GDK_EXPOSURE_MASK
2254 | GDK_BUTTON_PRESS_MASK
2255 | GDK_BUTTON_RELEASE_MASK
2256 | GDK_POINTER_MOTION_MASK
2257 | GDK_KEY_PRESS_MASK
2258 | GDK_KEY_RELEASE_MASK
2259 | GDK_ENTER_NOTIFY_MASK
2260 | GDK_LEAVE_NOTIFY_MASK
2261 | GDK_FOCUS_CHANGE_MASK));
2263 (* FOO_CANVAS_ITEM_GET_CLASS (canvas->root)->realize) (canvas->root);
2266 /* Unrealize handler for the canvas */
2267 static void
2268 foo_canvas_unrealize (GtkWidget *widget)
2270 FooCanvas *canvas;
2272 g_return_if_fail (FOO_IS_CANVAS (widget));
2274 canvas = FOO_CANVAS (widget);
2276 shutdown_transients (canvas);
2278 /* Unrealize items and parent widget */
2280 (* FOO_CANVAS_ITEM_GET_CLASS (canvas->root)->unrealize) (canvas->root);
2282 if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
2283 (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
2286 /* Handles scrolling of the canvas. Adjusts the scrolling and zooming offset to
2287 * keep as much as possible of the canvas scrolling region in view.
2289 static void
2290 scroll_to (FooCanvas *canvas, int cx, int cy)
2292 int scroll_width, scroll_height;
2293 int right_limit, bottom_limit;
2294 int old_zoom_xofs, old_zoom_yofs;
2295 int changed_x = FALSE, changed_y = FALSE;
2296 int canvas_width, canvas_height;
2298 canvas_width = GTK_WIDGET (canvas)->allocation.width;
2299 canvas_height = GTK_WIDGET (canvas)->allocation.height;
2301 scroll_width = floor ((canvas->scroll_x2 - canvas->scroll_x1) * canvas->pixels_per_unit + 0.5);
2302 scroll_height = floor ((canvas->scroll_y2 - canvas->scroll_y1) * canvas->pixels_per_unit + 0.5);
2304 right_limit = scroll_width - canvas_width;
2305 bottom_limit = scroll_height - canvas_height;
2307 old_zoom_xofs = canvas->zoom_xofs;
2308 old_zoom_yofs = canvas->zoom_yofs;
2310 if (right_limit < 0) {
2311 cx = 0;
2312 if (canvas->center_scroll_region) {
2313 canvas->zoom_xofs = (canvas_width - scroll_width) / 2;
2314 scroll_width = canvas_width;
2315 } else {
2316 canvas->zoom_xofs = 0;
2318 } else if (cx < 0) {
2319 cx = 0;
2320 canvas->zoom_xofs = 0;
2321 } else if (cx > right_limit) {
2322 cx = right_limit;
2323 canvas->zoom_xofs = 0;
2324 } else
2325 canvas->zoom_xofs = 0;
2327 if (bottom_limit < 0) {
2328 cy = 0;
2329 if (canvas->center_scroll_region) {
2330 canvas->zoom_yofs = (canvas_height - scroll_height) / 2;
2331 scroll_height = canvas_height;
2332 } else {
2333 canvas->zoom_yofs = 0;
2335 } else if (cy < 0) {
2336 cy = 0;
2337 canvas->zoom_yofs = 0;
2338 } else if (cy > bottom_limit) {
2339 cy = bottom_limit;
2340 canvas->zoom_yofs = 0;
2341 } else
2342 canvas->zoom_yofs = 0;
2344 if ((canvas->zoom_xofs != old_zoom_xofs) || (canvas->zoom_yofs != old_zoom_yofs)) {
2345 /* This can only occur, if either canvas size or widget size changes */
2346 /* So I think we can request full redraw here */
2347 /* More stuff - we have to mark root as needing fresh affine (Lauris) */
2348 if (!(canvas->root->object.flags & FOO_CANVAS_ITEM_NEED_DEEP_UPDATE)) {
2349 canvas->root->object.flags |= FOO_CANVAS_ITEM_NEED_DEEP_UPDATE;
2350 foo_canvas_request_update (canvas);
2352 gtk_widget_queue_draw (GTK_WIDGET (canvas));
2355 if (((int) canvas->layout.hadjustment->value) != cx) {
2356 canvas->layout.hadjustment->value = cx;
2357 changed_x = TRUE;
2360 if (((int) canvas->layout.vadjustment->value) != cy) {
2361 canvas->layout.vadjustment->value = cy;
2362 changed_y = TRUE;
2365 if ((scroll_width != (int) canvas->layout.width) || (scroll_height != (int) canvas->layout.height)) {
2366 gtk_layout_set_size (GTK_LAYOUT (canvas), scroll_width, scroll_height);
2369 /* Signal GtkLayout that it should do a redraw. */
2370 if (changed_x)
2371 g_signal_emit_by_name (G_OBJECT (canvas->layout.hadjustment), "value_changed");
2372 if (changed_y)
2373 g_signal_emit_by_name (G_OBJECT (canvas->layout.vadjustment), "value_changed");
2376 /* Size allocation handler for the canvas */
2377 static void
2378 foo_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
2380 FooCanvas *canvas;
2382 g_return_if_fail (FOO_IS_CANVAS (widget));
2383 g_return_if_fail (allocation != NULL);
2385 if (GTK_WIDGET_CLASS (canvas_parent_class)->size_allocate)
2386 (* GTK_WIDGET_CLASS (canvas_parent_class)->size_allocate) (widget, allocation);
2388 canvas = FOO_CANVAS (widget);
2390 /* Recenter the view, if appropriate */
2392 canvas->layout.hadjustment->page_size = allocation->width;
2393 canvas->layout.hadjustment->page_increment = allocation->width / 2;
2395 canvas->layout.vadjustment->page_size = allocation->height;
2396 canvas->layout.vadjustment->page_increment = allocation->height / 2;
2398 scroll_to (canvas,
2399 canvas->layout.hadjustment->value,
2400 canvas->layout.vadjustment->value);
2402 g_signal_emit_by_name (G_OBJECT (canvas->layout.hadjustment), "changed");
2403 g_signal_emit_by_name (G_OBJECT (canvas->layout.vadjustment), "changed");
2406 /* Emits an event for an item in the canvas, be it the current item, grabbed
2407 * item, or focused item, as appropriate.
2410 static int
2411 emit_event (FooCanvas *canvas, GdkEvent *event)
2413 GdkEvent ev;
2414 gint finished;
2415 FooCanvasItem *item;
2416 FooCanvasItem *parent;
2417 guint mask;
2419 /* Could be an old pick event */
2420 if (!gtk_widget_get_realized (GTK_WIDGET (canvas))) {
2421 return FALSE;
2424 /* Perform checks for grabbed items */
2426 if (canvas->grabbed_item &&
2427 !is_descendant (canvas->current_item, canvas->grabbed_item)) {
2428 return FALSE;
2431 if (canvas->grabbed_item) {
2432 switch (event->type) {
2433 case GDK_ENTER_NOTIFY:
2434 mask = GDK_ENTER_NOTIFY_MASK;
2435 break;
2437 case GDK_LEAVE_NOTIFY:
2438 mask = GDK_LEAVE_NOTIFY_MASK;
2439 break;
2441 case GDK_MOTION_NOTIFY:
2442 mask = GDK_POINTER_MOTION_MASK;
2443 break;
2445 case GDK_BUTTON_PRESS:
2446 case GDK_2BUTTON_PRESS:
2447 case GDK_3BUTTON_PRESS:
2448 mask = GDK_BUTTON_PRESS_MASK;
2449 break;
2451 case GDK_BUTTON_RELEASE:
2452 mask = GDK_BUTTON_RELEASE_MASK;
2453 break;
2455 case GDK_KEY_PRESS:
2456 mask = GDK_KEY_PRESS_MASK;
2457 break;
2459 case GDK_KEY_RELEASE:
2460 mask = GDK_KEY_RELEASE_MASK;
2461 break;
2463 default:
2464 mask = 0;
2465 break;
2468 if (!(mask & canvas->grabbed_event_mask))
2469 return FALSE;
2472 /* Convert to world coordinates -- we have two cases because of diferent
2473 * offsets of the fields in the event structures.
2476 ev = *event;
2478 switch (ev.type)
2480 case GDK_ENTER_NOTIFY:
2481 case GDK_LEAVE_NOTIFY:
2482 foo_canvas_window_to_world (canvas,
2483 ev.crossing.x, ev.crossing.y,
2484 &ev.crossing.x, &ev.crossing.y);
2485 break;
2487 case GDK_MOTION_NOTIFY:
2488 foo_canvas_window_to_world (canvas,
2489 ev.motion.x, ev.motion.y,
2490 &ev.motion.x, &ev.motion.y);
2491 break;
2493 case GDK_BUTTON_PRESS:
2494 case GDK_2BUTTON_PRESS:
2495 case GDK_3BUTTON_PRESS:
2496 foo_canvas_window_to_world (canvas,
2497 ev.motion.x, ev.motion.y,
2498 &ev.motion.x, &ev.motion.y);
2499 break;
2501 case GDK_BUTTON_RELEASE:
2502 foo_canvas_window_to_world (canvas,
2503 ev.motion.x, ev.motion.y,
2504 &ev.motion.x, &ev.motion.y);
2505 break;
2507 default:
2508 break;
2511 /* Choose where we send the event */
2513 item = canvas->current_item;
2515 if (canvas->focused_item
2516 && ((event->type == GDK_KEY_PRESS) ||
2517 (event->type == GDK_KEY_RELEASE) ||
2518 (event->type == GDK_FOCUS_CHANGE)))
2519 item = canvas->focused_item;
2521 /* The event is propagated up the hierarchy (for if someone connected to
2522 * a group instead of a leaf event), and emission is stopped if a
2523 * handler returns TRUE, just like for GtkWidget events.
2526 finished = FALSE;
2528 while (item && !finished) {
2529 g_object_ref (GTK_OBJECT (item));
2531 g_signal_emit (
2532 G_OBJECT (item), item_signals[ITEM_EVENT], 0,
2533 &ev, &finished);
2535 parent = item->parent;
2536 g_object_unref (GTK_OBJECT (item));
2538 item = parent;
2541 return finished;
2544 /* Re-picks the current item in the canvas, based on the event's coordinates.
2545 * Also emits enter/leave events for items as appropriate.
2547 static int
2548 pick_current_item (FooCanvas *canvas, GdkEvent *event)
2550 int button_down;
2551 double x, y;
2552 int cx, cy;
2553 int retval;
2555 retval = FALSE;
2557 /* If a button is down, we'll perform enter and leave events on the
2558 * current item, but not enter on any other item. This is more or less
2559 * like X pointer grabbing for canvas items.
2561 button_down = canvas->state & (GDK_BUTTON1_MASK
2562 | GDK_BUTTON2_MASK
2563 | GDK_BUTTON3_MASK
2564 | GDK_BUTTON4_MASK
2565 | GDK_BUTTON5_MASK);
2566 if (!button_down)
2567 canvas->left_grabbed_item = FALSE;
2569 /* Save the event in the canvas. This is used to synthesize enter and
2570 * leave events in case the current item changes. It is also used to
2571 * re-pick the current item if the current one gets deleted. Also,
2572 * synthesize an enter event.
2574 if (event != &canvas->pick_event) {
2575 if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
2576 /* these fields have the same offsets in both types of events */
2578 canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
2579 canvas->pick_event.crossing.window = event->motion.window;
2580 canvas->pick_event.crossing.send_event = event->motion.send_event;
2581 canvas->pick_event.crossing.subwindow = NULL;
2582 canvas->pick_event.crossing.x = event->motion.x;
2583 canvas->pick_event.crossing.y = event->motion.y;
2584 canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
2585 canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
2586 canvas->pick_event.crossing.focus = FALSE;
2587 canvas->pick_event.crossing.state = event->motion.state;
2589 /* these fields don't have the same offsets in both types of events */
2591 if (event->type == GDK_MOTION_NOTIFY) {
2592 canvas->pick_event.crossing.x_root = event->motion.x_root;
2593 canvas->pick_event.crossing.y_root = event->motion.y_root;
2594 } else {
2595 canvas->pick_event.crossing.x_root = event->button.x_root;
2596 canvas->pick_event.crossing.y_root = event->button.y_root;
2598 } else
2599 canvas->pick_event = *event;
2602 /* Don't do anything else if this is a recursive call */
2604 if (canvas->in_repick)
2605 return retval;
2607 /* LeaveNotify means that there is no current item, so we don't look for one */
2609 if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
2610 /* these fields don't have the same offsets in both types of events */
2612 if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
2613 x = canvas->pick_event.crossing.x;
2614 y = canvas->pick_event.crossing.y;
2615 } else {
2616 x = canvas->pick_event.motion.x;
2617 y = canvas->pick_event.motion.y;
2620 /* canvas pixel coords */
2622 cx = (int) (x + 0.5);
2623 cy = (int) (y + 0.5);
2625 /* world coords */
2626 foo_canvas_c2w (canvas, cx, cy, &x, &y);
2628 /* find the closest item */
2629 if (canvas->root->object.flags & FOO_CANVAS_ITEM_MAPPED)
2630 foo_canvas_item_invoke_point (canvas->root, x, y, cx, cy,
2631 &canvas->new_current_item);
2632 else
2633 canvas->new_current_item = NULL;
2634 } else
2635 canvas->new_current_item = NULL;
2637 if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item)
2638 return retval; /* current item did not change */
2640 /* Synthesize events for old and new current items */
2642 if ((canvas->new_current_item != canvas->current_item)
2643 && (canvas->current_item != NULL)
2644 && !canvas->left_grabbed_item) {
2645 GdkEvent new_event;
2646 FooCanvasItem *item;
2648 item = canvas->current_item;
2650 new_event = canvas->pick_event;
2651 new_event.type = GDK_LEAVE_NOTIFY;
2653 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
2654 new_event.crossing.subwindow = NULL;
2655 canvas->in_repick = TRUE;
2656 retval = emit_event (canvas, &new_event);
2657 canvas->in_repick = FALSE;
2660 /* new_current_item may have been set to NULL during the call to emit_event() above */
2662 if ((canvas->new_current_item != canvas->current_item) && button_down) {
2663 canvas->left_grabbed_item = TRUE;
2664 return retval;
2667 /* Handle the rest of cases */
2669 canvas->left_grabbed_item = FALSE;
2670 canvas->current_item = canvas->new_current_item;
2672 if (canvas->current_item != NULL) {
2673 GdkEvent new_event;
2675 new_event = canvas->pick_event;
2676 new_event.type = GDK_ENTER_NOTIFY;
2677 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
2678 new_event.crossing.subwindow = NULL;
2679 retval = emit_event (canvas, &new_event);
2682 return retval;
2685 /* Button event handler for the canvas */
2686 static gint
2687 foo_canvas_button (GtkWidget *widget, GdkEventButton *event)
2689 FooCanvas *canvas;
2690 int mask;
2691 int retval;
2693 g_return_val_if_fail (FOO_IS_CANVAS (widget), FALSE);
2694 g_return_val_if_fail (event != NULL, FALSE);
2696 retval = FALSE;
2698 canvas = FOO_CANVAS (widget);
2701 * dispatch normally regardless of the event's window if an item has
2702 * has a pointer grab in effect
2704 if (!canvas->grabbed_item && event->window != canvas->layout.bin_window)
2705 return retval;
2707 switch (event->button) {
2708 case 1:
2709 mask = GDK_BUTTON1_MASK;
2710 break;
2711 case 2:
2712 mask = GDK_BUTTON2_MASK;
2713 break;
2714 case 3:
2715 mask = GDK_BUTTON3_MASK;
2716 break;
2717 case 4:
2718 mask = GDK_BUTTON4_MASK;
2719 break;
2720 case 5:
2721 mask = GDK_BUTTON5_MASK;
2722 break;
2723 default:
2724 mask = 0;
2727 switch (event->type) {
2728 case GDK_BUTTON_PRESS:
2729 case GDK_2BUTTON_PRESS:
2730 case GDK_3BUTTON_PRESS:
2731 /* Pick the current item as if the button were not pressed, and
2732 * then process the event.
2734 canvas->state = event->state;
2735 pick_current_item (canvas, (GdkEvent *) event);
2736 canvas->state ^= mask;
2737 retval = emit_event (canvas, (GdkEvent *) event);
2738 break;
2740 case GDK_BUTTON_RELEASE:
2741 /* Process the event as if the button were pressed, then repick
2742 * after the button has been released
2744 canvas->state = event->state;
2745 retval = emit_event (canvas, (GdkEvent *) event);
2746 event->state ^= mask;
2747 canvas->state = event->state;
2748 pick_current_item (canvas, (GdkEvent *) event);
2749 event->state ^= mask;
2750 break;
2752 default:
2753 g_assert_not_reached ();
2756 return retval;
2759 /* Motion event handler for the canvas */
2760 static gint
2761 foo_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
2763 FooCanvas *canvas;
2765 g_return_val_if_fail (FOO_IS_CANVAS (widget), FALSE);
2766 g_return_val_if_fail (event != NULL, FALSE);
2768 canvas = FOO_CANVAS (widget);
2770 if (event->window != canvas->layout.bin_window)
2771 return FALSE;
2773 canvas->state = event->state;
2774 pick_current_item (canvas, (GdkEvent *) event);
2775 return emit_event (canvas, (GdkEvent *) event);
2778 /* Key event handler for the canvas */
2779 static gint
2780 foo_canvas_key (GtkWidget *widget, GdkEventKey *event)
2782 FooCanvas *canvas;
2784 g_return_val_if_fail (FOO_IS_CANVAS (widget), FALSE);
2785 g_return_val_if_fail (event != NULL, FALSE);
2787 canvas = FOO_CANVAS (widget);
2789 if (emit_event (canvas, (GdkEvent *) event))
2790 return TRUE;
2791 if (event->type == GDK_KEY_RELEASE)
2792 return GTK_WIDGET_CLASS (canvas_parent_class)->key_release_event (widget, event);
2793 else
2794 return GTK_WIDGET_CLASS (canvas_parent_class)->key_press_event (widget, event);
2798 /* Crossing event handler for the canvas */
2799 static gint
2800 foo_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
2802 FooCanvas *canvas;
2804 g_return_val_if_fail (FOO_IS_CANVAS (widget), FALSE);
2805 g_return_val_if_fail (event != NULL, FALSE);
2807 canvas = FOO_CANVAS (widget);
2809 if (event->window != canvas->layout.bin_window)
2810 return FALSE;
2812 canvas->state = event->state;
2813 return pick_current_item (canvas, (GdkEvent *) event);
2816 /* Focus in handler for the canvas */
2817 static gint
2818 foo_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
2820 FooCanvas *canvas;
2822 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
2824 canvas = FOO_CANVAS (widget);
2826 if (canvas->focused_item)
2827 return emit_event (canvas, (GdkEvent *) event);
2828 else
2829 return FALSE;
2832 /* Focus out handler for the canvas */
2833 static gint
2834 foo_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
2836 FooCanvas *canvas;
2838 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2840 canvas = FOO_CANVAS (widget);
2842 if (canvas->focused_item)
2843 return emit_event (canvas, (GdkEvent *) event);
2844 else
2845 return FALSE;
2848 /* Expose handler for the canvas */
2849 static gint
2850 foo_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
2852 FooCanvas *canvas;
2854 canvas = FOO_CANVAS (widget);
2856 if (!gtk_widget_is_drawable (widget) || (event->window != canvas->layout.bin_window)) return FALSE;
2858 #ifdef VERBOSE
2859 g_print ("Expose\n");
2860 #endif
2861 /* If there are any outstanding items that need updating, do them now */
2862 if (canvas->idle_id) {
2863 g_source_remove (canvas->idle_id);
2864 canvas->idle_id = 0;
2866 if (canvas->need_update) {
2867 g_return_val_if_fail (!canvas->doing_update, FALSE);
2869 canvas->doing_update = TRUE;
2870 foo_canvas_item_invoke_update (canvas->root, 0, 0, 0);
2872 g_return_val_if_fail (canvas->doing_update, FALSE);
2874 canvas->doing_update = FALSE;
2876 canvas->need_update = FALSE;
2879 /* Hmmm. Would like to queue antiexposes if the update marked
2880 anything that is gonna get redrawn as invalid */
2883 g_signal_emit (G_OBJECT (canvas), canvas_signals[DRAW_BACKGROUND], 0,
2884 event->area.x, event->area.y,
2885 event->area.width, event->area.height);
2887 if (canvas->root->object.flags & FOO_CANVAS_ITEM_MAPPED)
2888 (* FOO_CANVAS_ITEM_GET_CLASS (canvas->root)->draw) (canvas->root,
2889 canvas->layout.bin_window,
2890 event);
2894 /* Chain up to get exposes on child widgets */
2895 GTK_WIDGET_CLASS (canvas_parent_class)->expose_event (widget, event);
2897 return FALSE;
2900 static void
2901 foo_canvas_draw_background (FooCanvas *canvas,
2902 int x, int y, int width, int height)
2904 cairo_t *cr;
2906 /* By default, we use the style background. */
2907 cr = gdk_cairo_create (canvas->layout.bin_window);
2908 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
2909 gdk_cairo_set_source_color (cr, &GTK_WIDGET (canvas)->style->bg[GTK_STATE_NORMAL]);
2910 cairo_rectangle (cr, x, y, width, height);
2911 cairo_fill(cr);
2912 cairo_destroy (cr);
2915 static void
2916 do_update (FooCanvas *canvas)
2918 /* Cause the update if necessary */
2920 update_again:
2921 if (canvas->need_update) {
2922 g_return_if_fail (!canvas->doing_update);
2924 canvas->doing_update = TRUE;
2925 foo_canvas_item_invoke_update (canvas->root, 0, 0, 0);
2927 g_return_if_fail (canvas->doing_update);
2929 canvas->doing_update = FALSE;
2931 canvas->need_update = FALSE;
2934 /* Pick new current item */
2936 while (canvas->need_repick) {
2937 canvas->need_repick = FALSE;
2938 pick_current_item (canvas, &canvas->pick_event);
2941 /* it is possible that during picking we emitted an event in which
2942 the user then called some function which then requested update
2943 of something. Without this we'd be left in a state where
2944 need_update would have been left TRUE and the canvas would have
2945 been left unpainted. */
2946 if (canvas->need_update) {
2947 goto update_again;
2951 /* Idle handler for the canvas. It deals with pending updates and redraws. */
2952 static gint
2953 idle_handler (gpointer data)
2955 FooCanvas *canvas;
2957 GDK_THREADS_ENTER ();
2959 canvas = FOO_CANVAS (data);
2960 do_update (canvas);
2962 /* Reset idle id */
2963 canvas->idle_id = 0;
2965 GDK_THREADS_LEAVE ();
2967 return FALSE;
2970 /* Convenience function to add an idle handler to a canvas */
2971 static void
2972 add_idle (FooCanvas *canvas)
2974 if (!canvas->idle_id) {
2975 /* We let the update idle handler have higher priority
2976 * than the redraw idle handler so the canvas state
2977 * will be updated during the expose event. canvas in
2978 * expose_event.
2980 canvas->idle_id = g_idle_add_full (GDK_PRIORITY_REDRAW - 20,
2981 idle_handler, canvas, NULL);
2986 * foo_canvas_root:
2987 * @canvas: A canvas.
2989 * Queries the root group of a canvas.
2991 * Return value: The root group of the specified canvas.
2993 FooCanvasGroup *
2994 foo_canvas_root (FooCanvas *canvas)
2996 g_return_val_if_fail (FOO_IS_CANVAS (canvas), NULL);
2998 return FOO_CANVAS_GROUP (canvas->root);
3003 * foo_canvas_set_scroll_region:
3004 * @canvas: A canvas.
3005 * @x1: Leftmost limit of the scrolling region.
3006 * @y1: Upper limit of the scrolling region.
3007 * @x2: Rightmost limit of the scrolling region.
3008 * @y2: Lower limit of the scrolling region.
3010 * Sets the scrolling region of a canvas to the specified rectangle. The canvas
3011 * will then be able to scroll only within this region. The view of the canvas
3012 * is adjusted as appropriate to display as much of the new region as possible.
3014 void
3015 foo_canvas_set_scroll_region (FooCanvas *canvas, double x1, double y1, double x2, double y2)
3017 double wxofs, wyofs;
3018 int xofs, yofs;
3020 g_return_if_fail (FOO_IS_CANVAS (canvas));
3022 if ((canvas->scroll_x1 == x1) && (canvas->scroll_y1 == y1) &&
3023 (canvas->scroll_x2 == x2) && (canvas->scroll_y2 == y2)) {
3024 return;
3028 * Set the new scrolling region. If possible, do not move the visible contents of the
3029 * canvas.
3032 foo_canvas_c2w (canvas,
3033 GTK_LAYOUT (canvas)->hadjustment->value + canvas->zoom_xofs,
3034 GTK_LAYOUT (canvas)->vadjustment->value + canvas->zoom_yofs,
3035 /*canvas->zoom_xofs,
3036 canvas->zoom_yofs,*/
3037 &wxofs, &wyofs);
3039 canvas->scroll_x1 = x1;
3040 canvas->scroll_y1 = y1;
3041 canvas->scroll_x2 = x2;
3042 canvas->scroll_y2 = y2;
3044 foo_canvas_w2c (canvas, wxofs, wyofs, &xofs, &yofs);
3046 scroll_to (canvas, xofs, yofs);
3048 canvas->need_repick = TRUE;
3050 if (!(canvas->root->object.flags & FOO_CANVAS_ITEM_NEED_DEEP_UPDATE)) {
3051 canvas->root->object.flags |= FOO_CANVAS_ITEM_NEED_DEEP_UPDATE;
3052 foo_canvas_request_update (canvas);
3058 * foo_canvas_get_scroll_region:
3059 * @canvas: A canvas.
3060 * @x1: Leftmost limit of the scrolling region (return value).
3061 * @y1: Upper limit of the scrolling region (return value).
3062 * @x2: Rightmost limit of the scrolling region (return value).
3063 * @y2: Lower limit of the scrolling region (return value).
3065 * Queries the scrolling region of a canvas.
3067 void
3068 foo_canvas_get_scroll_region (FooCanvas *canvas, double *x1, double *y1, double *x2, double *y2)
3070 g_return_if_fail (FOO_IS_CANVAS (canvas));
3072 if (x1)
3073 *x1 = canvas->scroll_x1;
3075 if (y1)
3076 *y1 = canvas->scroll_y1;
3078 if (x2)
3079 *x2 = canvas->scroll_x2;
3081 if (y2)
3082 *y2 = canvas->scroll_y2;
3085 void
3086 foo_canvas_set_center_scroll_region (FooCanvas *canvas,
3087 gboolean center_scroll_region)
3089 g_return_if_fail (FOO_IS_CANVAS (canvas));
3091 canvas->center_scroll_region = center_scroll_region != 0;
3093 scroll_to (canvas,
3094 canvas->layout.hadjustment->value,
3095 canvas->layout.vadjustment->value);
3100 * foo_canvas_set_pixels_per_unit:
3101 * @canvas: A canvas.
3102 * @n: The number of pixels that correspond to one canvas unit.
3104 * Sets the zooming factor of a canvas by specifying the number of pixels that
3105 * correspond to one canvas unit.
3107 void
3108 foo_canvas_set_pixels_per_unit (FooCanvas *canvas, double n)
3110 GtkWidget *widget;
3111 double cx, cy;
3112 int x1, y1;
3113 int center_x, center_y;
3114 GdkWindow *window;
3115 GdkWindowAttr attributes;
3116 gint attributes_mask;
3118 g_return_if_fail (FOO_IS_CANVAS (canvas));
3119 g_return_if_fail (n > FOO_CANVAS_EPSILON);
3121 widget = GTK_WIDGET (canvas);
3123 center_x = widget->allocation.width / 2;
3124 center_y = widget->allocation.height / 2;
3126 /* Find the coordinates of the screen center in units. */
3127 cx = (canvas->layout.hadjustment->value + center_x) / canvas->pixels_per_unit + canvas->scroll_x1 + canvas->zoom_xofs;
3128 cy = (canvas->layout.vadjustment->value + center_y) / canvas->pixels_per_unit + canvas->scroll_y1 + canvas->zoom_yofs;
3130 /* Now calculate the new offset of the upper left corner. (round not truncate) */
3131 x1 = ((cx - canvas->scroll_x1) * n) - center_x + .5;
3132 y1 = ((cy - canvas->scroll_y1) * n) - center_y + .5;
3134 canvas->pixels_per_unit = n;
3136 if (!(canvas->root->object.flags & FOO_CANVAS_ITEM_NEED_DEEP_UPDATE)) {
3137 canvas->root->object.flags |= FOO_CANVAS_ITEM_NEED_DEEP_UPDATE;
3138 foo_canvas_request_update (canvas);
3141 /* Map a background None window over the bin_window to avoid
3142 * scrolling the window scroll causing exposes.
3144 window = NULL;
3145 if (gtk_widget_get_mapped (widget)) {
3146 attributes.window_type = GDK_WINDOW_CHILD;
3147 attributes.x = widget->allocation.x;
3148 attributes.y = widget->allocation.y;
3149 attributes.width = widget->allocation.width;
3150 attributes.height = widget->allocation.height;
3151 attributes.wclass = GDK_INPUT_OUTPUT;
3152 attributes.visual = gtk_widget_get_visual (widget);
3153 attributes.colormap = gtk_widget_get_colormap (widget);
3154 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
3156 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3158 window = gdk_window_new (gtk_widget_get_parent_window (widget),
3159 &attributes, attributes_mask);
3160 gdk_window_set_back_pixmap (window, NULL, FALSE);
3161 gdk_window_set_user_data (window, widget);
3163 gdk_window_show (window);
3166 scroll_to (canvas, x1, y1);
3168 /* If we created a an overlapping background None window, remove it how.
3170 * TODO: We would like to temporarily set the bin_window background to
3171 * None to avoid clearing the bin_window to the background, but gdk doesn't
3172 * expose enought to let us do this, so we get a flash-effect here. At least
3173 * it looks better than scroll + expose.
3175 if (window != NULL) {
3176 gdk_window_hide (window);
3177 gdk_window_set_user_data (window, NULL);
3178 gdk_window_destroy (window);
3181 canvas->need_repick = TRUE;
3185 * foo_canvas_scroll_to:
3186 * @canvas: A canvas.
3187 * @cx: Horizontal scrolling offset in canvas pixel units.
3188 * @cy: Vertical scrolling offset in canvas pixel units.
3190 * Makes a canvas scroll to the specified offsets, given in canvas pixel units.
3191 * The canvas will adjust the view so that it is not outside the scrolling
3192 * region. This function is typically not used, as it is better to hook
3193 * scrollbars to the canvas layout's scrolling adjusments.
3195 void
3196 foo_canvas_scroll_to (FooCanvas *canvas, int cx, int cy)
3198 g_return_if_fail (FOO_IS_CANVAS (canvas));
3200 scroll_to (canvas, cx, cy);
3204 * foo_canvas_get_scroll_offsets:
3205 * @canvas: A canvas.
3206 * @cx: Horizontal scrolling offset (return value).
3207 * @cy: Vertical scrolling offset (return value).
3209 * Queries the scrolling offsets of a canvas. The values are returned in canvas
3210 * pixel units.
3212 void
3213 foo_canvas_get_scroll_offsets (FooCanvas *canvas, int *cx, int *cy)
3215 g_return_if_fail (FOO_IS_CANVAS (canvas));
3217 if (cx)
3218 *cx = canvas->layout.hadjustment->value;
3220 if (cy)
3221 *cy = canvas->layout.vadjustment->value;
3225 * foo_canvas_update_now:
3226 * @canvas: A canvas.
3228 * Forces an immediate update and redraw of a canvas. If the canvas does not
3229 * have any pending update or redraw requests, then no action is taken. This is
3230 * typically only used by applications that need explicit control of when the
3231 * display is updated, like games. It is not needed by normal applications.
3233 void
3234 foo_canvas_update_now (FooCanvas *canvas)
3236 g_return_if_fail (FOO_IS_CANVAS (canvas));
3238 if (!(canvas->need_update || canvas->need_redraw))
3239 return;
3240 remove_idle (canvas);
3241 do_update (canvas);
3245 * foo_canvas_get_item_at:
3246 * @canvas: A canvas.
3247 * @x: X position in world coordinates.
3248 * @y: Y position in world coordinates.
3250 * Looks for the item that is under the specified position, which must be
3251 * specified in world coordinates.
3253 * Return value: The sought item, or NULL if no item is at the specified
3254 * coordinates.
3256 FooCanvasItem *
3257 foo_canvas_get_item_at (FooCanvas *canvas, double x, double y)
3259 FooCanvasItem *item;
3260 double dist;
3261 int cx, cy;
3263 g_return_val_if_fail (FOO_IS_CANVAS (canvas), NULL);
3265 foo_canvas_w2c (canvas, x, y, &cx, &cy);
3267 dist = foo_canvas_item_invoke_point (canvas->root, x, y, cx, cy, &item);
3268 if ((int) (dist * canvas->pixels_per_unit + 0.5) <= canvas->close_enough)
3269 return item;
3270 else
3271 return NULL;
3274 /* Queues an update of the canvas */
3275 static void
3276 foo_canvas_request_update (FooCanvas *canvas)
3278 FOO_CANVAS_GET_CLASS (canvas)->request_update (canvas);
3281 static void
3282 foo_canvas_request_update_real (FooCanvas *canvas)
3284 canvas->need_update = TRUE;
3285 add_idle (canvas);
3289 * foo_canvas_request_redraw:
3290 * @canvas: A canvas.
3291 * @x1: Leftmost coordinate of the rectangle to be redrawn.
3292 * @y1: Upper coordinate of the rectangle to be redrawn.
3293 * @x2: Rightmost coordinate of the rectangle to be redrawn, plus 1.
3294 * @y2: Lower coordinate of the rectangle to be redrawn, plus 1.
3296 * Convenience function that informs a canvas that the specified rectangle needs
3297 * to be repainted. The rectangle includes @x1 and @y1, but not @x2 and @y2.
3298 * To be used only by item implementations.
3300 void
3301 foo_canvas_request_redraw (FooCanvas *canvas, int x1, int y1, int x2, int y2)
3303 GdkRectangle bbox;
3305 g_return_if_fail (FOO_IS_CANVAS (canvas));
3307 if (!gtk_widget_is_drawable (GTK_WIDGET (canvas)) || (x1 >= x2) || (y1 >= y2)) return;
3309 bbox.x = x1;
3310 bbox.y = y1;
3311 bbox.width = x2 - x1;
3312 bbox.height = y2 - y1;
3314 gdk_window_invalidate_rect (canvas->layout.bin_window,
3315 &bbox, FALSE);
3319 * foo_canvas_w2c:
3320 * @canvas: A canvas.
3321 * @wx: World X coordinate.
3322 * @wy: World Y coordinate.
3323 * @cx: X pixel coordinate (return value).
3324 * @cy: Y pixel coordinate (return value).
3326 * Converts world coordinates into canvas pixel coordinates.
3328 void
3329 foo_canvas_w2c (FooCanvas *canvas, double wx, double wy, int *cx, int *cy)
3331 double zoom;
3333 g_return_if_fail (FOO_IS_CANVAS (canvas));
3335 zoom = canvas->pixels_per_unit;
3337 if (cx)
3338 *cx = floor ((wx - canvas->scroll_x1)*zoom + canvas->zoom_xofs + 0.5);
3339 if (cy)
3340 *cy = floor ((wy - canvas->scroll_y1)*zoom + canvas->zoom_yofs + 0.5);
3344 * foo_canvas_w2c:
3345 * @canvas: A canvas.
3346 * @world: rectangle in world coordinates.
3347 * @canvas: rectangle in canvase coordinates.
3349 * Converts rectangles in world coordinates into canvas pixel coordinates.
3351 void
3352 foo_canvas_w2c_rect_d (FooCanvas *canvas,
3353 double *x1, double *y1,
3354 double *x2, double *y2)
3356 foo_canvas_w2c_d (canvas,
3357 *x1, *y1,
3358 x1, y1);
3359 foo_canvas_w2c_d (canvas,
3360 *x2, *y2,
3361 x2, y2);
3367 * foo_canvas_w2c_d:
3368 * @canvas: A canvas.
3369 * @wx: World X coordinate.
3370 * @wy: World Y coordinate.
3371 * @cx: X pixel coordinate (return value).
3372 * @cy: Y pixel coordinate (return value).
3374 * Converts world coordinates into canvas pixel coordinates. This version
3375 * produces coordinates in floating point coordinates, for greater precision.
3377 void
3378 foo_canvas_w2c_d (FooCanvas *canvas, double wx, double wy, double *cx, double *cy)
3380 double zoom;
3382 g_return_if_fail (FOO_IS_CANVAS (canvas));
3384 zoom = canvas->pixels_per_unit;
3386 if (cx)
3387 *cx = (wx - canvas->scroll_x1)*zoom + canvas->zoom_xofs;
3388 if (cy)
3389 *cy = (wy - canvas->scroll_y1)*zoom + canvas->zoom_yofs;
3394 * foo_canvas_c2w:
3395 * @canvas: A canvas.
3396 * @cx: Canvas pixel X coordinate.
3397 * @cy: Canvas pixel Y coordinate.
3398 * @wx: X world coordinate (return value).
3399 * @wy: Y world coordinate (return value).
3401 * Converts canvas pixel coordinates to world coordinates.
3403 void
3404 foo_canvas_c2w (FooCanvas *canvas, int cx, int cy, double *wx, double *wy)
3406 double zoom;
3408 g_return_if_fail (FOO_IS_CANVAS (canvas));
3410 zoom = canvas->pixels_per_unit;
3412 if (wx)
3413 *wx = (cx - canvas->zoom_xofs)/zoom + canvas->scroll_x1;
3414 if (wy)
3415 *wy = (cy - canvas->zoom_yofs)/zoom + canvas->scroll_y1;
3420 * foo_canvas_window_to_world:
3421 * @canvas: A canvas.
3422 * @winx: Window-relative X coordinate.
3423 * @winy: Window-relative Y coordinate.
3424 * @worldx: X world coordinate (return value).
3425 * @worldy: Y world coordinate (return value).
3427 * Converts window-relative coordinates into world coordinates. You can use
3428 * this when you need to convert mouse coordinates into world coordinates, for
3429 * example.
3430 * Window coordinates are really the same as canvas coordinates now, but this
3431 * function is here for backwards compatibility reasons.
3433 void
3434 foo_canvas_window_to_world (FooCanvas *canvas, double winx, double winy,
3435 double *worldx, double *worldy)
3437 g_return_if_fail (FOO_IS_CANVAS (canvas));
3439 if (worldx)
3440 *worldx = canvas->scroll_x1 + ((winx - canvas->zoom_xofs)
3441 / canvas->pixels_per_unit);
3443 if (worldy)
3444 *worldy = canvas->scroll_y1 + ((winy - canvas->zoom_yofs)
3445 / canvas->pixels_per_unit);
3450 * foo_canvas_world_to_window:
3451 * @canvas: A canvas.
3452 * @worldx: World X coordinate.
3453 * @worldy: World Y coordinate.
3454 * @winx: X window-relative coordinate.
3455 * @winy: Y window-relative coordinate.
3457 * Converts world coordinates into window-relative coordinates.
3458 * Window coordinates are really the same as canvas coordinates now, but this
3459 * function is here for backwards compatibility reasons.
3461 void
3462 foo_canvas_world_to_window (FooCanvas *canvas, double worldx, double worldy,
3463 double *winx, double *winy)
3465 g_return_if_fail (FOO_IS_CANVAS (canvas));
3467 if (winx)
3468 *winx = (canvas->pixels_per_unit)*(worldx - canvas->scroll_x1) + canvas->zoom_xofs;
3470 if (winy)
3471 *winy = (canvas->pixels_per_unit)*(worldy - canvas->scroll_y1) + canvas->zoom_yofs;
3477 * foo_canvas_get_color:
3478 * @canvas: A canvas.
3479 * @spec: X color specification, or NULL for "transparent".
3480 * @color: Returns the allocated color.
3482 * Allocates a color based on the specified X color specification. As a
3483 * convenience to item implementations, it returns TRUE if the color was
3484 * allocated, or FALSE if the specification was NULL. A NULL color
3485 * specification is considered as "transparent" by the canvas.
3487 * Return value: TRUE if @spec is non-NULL and the color is allocated. If @spec
3488 * is NULL, then returns FALSE.
3491 foo_canvas_get_color (FooCanvas *canvas, const char *spec, GdkColor *color)
3493 GdkColormap *colormap;
3495 g_return_val_if_fail (FOO_IS_CANVAS (canvas), FALSE);
3496 g_return_val_if_fail (color != NULL, FALSE);
3498 if (!spec) {
3499 color->pixel = 0;
3500 color->red = 0;
3501 color->green = 0;
3502 color->blue = 0;
3503 return FALSE;
3506 gdk_color_parse (spec, color);
3508 colormap = gtk_widget_get_colormap (GTK_WIDGET (canvas));
3510 gdk_rgb_find_color (colormap, color);
3512 return TRUE;
3516 * foo_canvas_get_color_pixel:
3517 * @canvas: A canvas.
3518 * @rgba: RGBA color specification.
3520 * Allocates a color from the RGBA value passed into this function. The alpha
3521 * opacity value is discarded, since normal X colors do not support it.
3523 * Return value: Allocated pixel value corresponding to the specified color.
3525 gulong
3526 foo_canvas_get_color_pixel (FooCanvas *canvas, guint rgba)
3528 GdkColormap *colormap;
3529 GdkColor color;
3531 g_return_val_if_fail (FOO_IS_CANVAS (canvas), 0);
3533 color.red = ((rgba & 0xff000000) >> 16) + ((rgba & 0xff000000) >> 24);
3534 color.green = ((rgba & 0x00ff0000) >> 8) + ((rgba & 0x00ff0000) >> 16);
3535 color.blue = (rgba & 0x0000ff00) + ((rgba & 0x0000ff00) >> 8);
3536 color.pixel = 0;
3538 colormap = gtk_widget_get_colormap (GTK_WIDGET (canvas));
3540 gdk_rgb_find_color (colormap, &color);
3542 return color.pixel;
3545 static gboolean
3546 boolean_handled_accumulator (GSignalInvocationHint *ihint,
3547 GValue *return_accu,
3548 const GValue *handler_return,
3549 gpointer dummy)
3551 gboolean continue_emission;
3552 gboolean signal_handled;
3554 signal_handled = g_value_get_boolean (handler_return);
3555 g_value_set_boolean (return_accu, signal_handled);
3556 continue_emission = !signal_handled;
3558 return continue_emission;
3561 static guint
3562 foo_canvas_item_accessible_add_focus_handler (AtkComponent *component,
3563 AtkFocusHandler handler)
3565 GSignalMatchType match_type;
3566 guint signal_id;
3568 match_type = G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC;
3569 signal_id = g_signal_lookup ("focus-event", ATK_TYPE_OBJECT);
3571 if (!g_signal_handler_find (component, match_type, signal_id, 0, NULL,
3572 (gpointer) handler, NULL)) {
3573 return g_signal_connect_closure_by_id (component,
3574 signal_id, 0,
3575 g_cclosure_new (
3576 G_CALLBACK (handler), NULL,
3577 (GClosureNotify) NULL),
3578 FALSE);
3580 return 0;
3583 static void
3584 foo_canvas_item_accessible_get_item_extents (FooCanvasItem *item,
3585 GdkRectangle *rect)
3587 double bx1, bx2, by1, by2;
3588 gint scroll_x, scroll_y;
3589 gint x1, x2, y1, y2;
3591 foo_canvas_item_get_bounds (item, &bx1, &by1, &bx2, &by2);
3592 foo_canvas_w2c_rect_d (item->canvas, &bx1, &by1, &bx2, &by2);
3593 foo_canvas_get_scroll_offsets (item->canvas, &scroll_x, &scroll_y);
3594 x1 = floor (bx1 + .5);
3595 y1 = floor (by1 + .5);
3596 x2 = floor (bx2 + .5);
3597 y2 = floor (by2 + .5);
3598 rect->x = x1 - scroll_x;
3599 rect->y = y1 - scroll_y;
3600 rect->width = x2 - x1;
3601 rect->height = y2 - y1;
3604 static gboolean
3605 foo_canvas_item_accessible_is_item_in_window (FooCanvasItem *item,
3606 GdkRectangle *rect)
3608 GtkWidget *widget;
3609 gboolean retval;
3611 widget = GTK_WIDGET (item->canvas);
3612 if (widget->window) {
3613 int window_width, window_height;
3615 gdk_window_get_geometry (widget->window, NULL, NULL,
3616 &window_width, &window_height, NULL);
3618 * Check whether rectangles intersect
3620 if (rect->x + rect->width < 0 ||
3621 rect->y + rect->height < 0 ||
3622 rect->x > window_width ||
3623 rect->y > window_height) {
3624 retval = FALSE;
3625 } else {
3626 retval = TRUE;
3628 } else {
3629 retval = FALSE;
3631 return retval;
3635 static void
3636 foo_canvas_item_accessible_get_extents (AtkComponent *component,
3637 gint *x,
3638 gint *y,
3639 gint *width,
3640 gint *height,
3641 AtkCoordType coord_type)
3643 AtkGObjectAccessible *atk_gobj;
3644 GObject *obj;
3645 FooCanvasItem *item;
3646 gint window_x, window_y;
3647 gint toplevel_x, toplevel_y;
3648 GdkRectangle rect;
3649 GdkWindow *window;
3650 GtkWidget *canvas;
3652 atk_gobj = ATK_GOBJECT_ACCESSIBLE (component);
3653 obj = atk_gobject_accessible_get_object (atk_gobj);
3655 if (obj == NULL) {
3656 /* item is defunct */
3657 return;
3660 /* Get the CanvasItem */
3661 item = FOO_CANVAS_ITEM (obj);
3663 /* If this item has no parent canvas, something's broken */
3664 g_return_if_fail (GTK_IS_WIDGET (item->canvas));
3666 foo_canvas_item_accessible_get_item_extents (item, &rect);
3667 *width = rect.width;
3668 *height = rect.height;
3669 if (!foo_canvas_item_accessible_is_item_in_window (item, &rect)) {
3670 *x = G_MININT;
3671 *y = G_MININT;
3672 return;
3675 canvas = GTK_WIDGET (item->canvas);
3676 window = gtk_widget_get_parent_window (canvas);
3677 gdk_window_get_origin (window, &window_x, &window_y);
3678 *x = rect.x + window_x;
3679 *y = rect.y + window_y;
3680 if (coord_type == ATK_XY_WINDOW) {
3681 window = gdk_window_get_toplevel (canvas->window);
3682 gdk_window_get_origin (window, &toplevel_x, &toplevel_y);
3683 *x -= toplevel_x;
3684 *y -= toplevel_y;
3686 return;
3689 static gint
3690 foo_canvas_item_accessible_get_mdi_zorder (AtkComponent *component)
3692 AtkGObjectAccessible *atk_gobj;
3693 GObject *g_obj;
3694 FooCanvasItem *item;
3696 atk_gobj = ATK_GOBJECT_ACCESSIBLE (component);
3697 g_obj = atk_gobject_accessible_get_object (atk_gobj);
3698 if (g_obj == NULL) {
3699 /* Object is defunct */
3700 return -1;
3703 item = FOO_CANVAS_ITEM (g_obj);
3704 if (item->parent) {
3705 return g_list_index (FOO_CANVAS_GROUP (item->parent)->item_list, item);
3706 } else {
3707 g_return_val_if_fail (item->canvas->root == item, -1);
3708 return 0;
3712 static gboolean
3713 foo_canvas_item_accessible_grab_focus (AtkComponent *component)
3715 AtkGObjectAccessible *atk_gobj;
3716 GObject *obj;
3717 FooCanvasItem *item;
3718 GtkWidget *toplevel;
3720 atk_gobj = ATK_GOBJECT_ACCESSIBLE (component);
3721 obj = atk_gobject_accessible_get_object (atk_gobj);
3723 item = FOO_CANVAS_ITEM (obj);
3724 if (item == NULL) {
3725 /* item is defunct */
3726 return FALSE;
3729 foo_canvas_item_grab_focus (item);
3730 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (item->canvas));
3731 if (gtk_widget_is_toplevel (toplevel)) {
3732 gtk_window_present (GTK_WINDOW (toplevel));
3735 return TRUE;
3738 static void
3739 foo_canvas_item_accessible_remove_focus_handler (AtkComponent *component,
3740 guint handler_id)
3742 g_signal_handler_disconnect (component, handler_id);
3745 static void
3746 foo_canvas_item_accessible_component_interface_init (AtkComponentIface *iface)
3748 g_return_if_fail (iface != NULL);
3750 iface->add_focus_handler = foo_canvas_item_accessible_add_focus_handler;
3751 iface->get_extents = foo_canvas_item_accessible_get_extents;
3752 iface->get_mdi_zorder = foo_canvas_item_accessible_get_mdi_zorder;
3753 iface->grab_focus = foo_canvas_item_accessible_grab_focus;
3754 iface->remove_focus_handler = foo_canvas_item_accessible_remove_focus_handler;
3757 static gboolean
3758 foo_canvas_item_accessible_is_item_on_screen (FooCanvasItem *item)
3760 GdkRectangle rect;
3762 foo_canvas_item_accessible_get_item_extents (item, &rect);
3763 return foo_canvas_item_accessible_is_item_in_window (item, &rect);
3766 static void
3767 foo_canvas_item_accessible_initialize (AtkObject *obj, gpointer data)
3769 if (ATK_OBJECT_CLASS (accessible_item_parent_class)->initialize != NULL)
3770 ATK_OBJECT_CLASS (accessible_item_parent_class)->initialize (obj, data);
3771 g_object_set_data (G_OBJECT (obj), "atk-component-layer",
3772 GINT_TO_POINTER (ATK_LAYER_MDI));
3775 static AtkStateSet*
3776 foo_canvas_item_accessible_ref_state_set (AtkObject *accessible)
3778 AtkGObjectAccessible *atk_gobj;
3779 GObject *obj;
3780 FooCanvasItem *item;
3781 AtkStateSet *state_set;
3783 state_set = ATK_OBJECT_CLASS (accessible_item_parent_class)->ref_state_set (accessible);
3784 atk_gobj = ATK_GOBJECT_ACCESSIBLE (accessible);
3785 obj = atk_gobject_accessible_get_object (atk_gobj);
3787 item = FOO_CANVAS_ITEM (obj);
3788 if (item == NULL) {
3789 atk_state_set_add_state (state_set, ATK_STATE_DEFUNCT);
3790 } else {
3791 if (item->object.flags & FOO_CANVAS_ITEM_VISIBLE) {
3792 atk_state_set_add_state (state_set, ATK_STATE_VISIBLE);
3794 if (foo_canvas_item_accessible_is_item_on_screen (item)) {
3795 atk_state_set_add_state (state_set, ATK_STATE_SHOWING);
3798 if (gtk_widget_get_can_focus (GTK_WIDGET (item->canvas))) {
3799 atk_state_set_add_state (state_set, ATK_STATE_FOCUSABLE);
3801 if (item->canvas->focused_item == item) {
3802 atk_state_set_add_state (state_set, ATK_STATE_FOCUSED);
3807 return state_set;
3810 static void
3811 foo_canvas_item_accessible_class_init (AtkObjectClass *klass)
3813 accessible_item_parent_class = g_type_class_peek_parent (klass);
3815 klass->initialize = foo_canvas_item_accessible_initialize;
3816 klass->ref_state_set = foo_canvas_item_accessible_ref_state_set;
3819 static GType
3820 foo_canvas_item_accessible_get_type (void)
3822 static GType type = 0;
3824 if (!type) {
3825 static const GInterfaceInfo atk_component_info = {
3826 (GInterfaceInitFunc) foo_canvas_item_accessible_component_interface_init,
3827 (GInterfaceFinalizeFunc) NULL,
3828 NULL
3830 AtkObjectFactory *factory;
3831 GType parent_atk_type;
3832 GTypeQuery query;
3833 GTypeInfo tinfo = { 0 };
3835 factory = atk_registry_get_factory (atk_get_default_registry(),
3836 GTK_TYPE_OBJECT);
3837 if (!factory) {
3838 return G_TYPE_INVALID;
3840 parent_atk_type = atk_object_factory_get_accessible_type (factory);
3841 if (!parent_atk_type) {
3842 return G_TYPE_INVALID;
3844 g_type_query (parent_atk_type, &query);
3845 tinfo.class_init = (GClassInitFunc) foo_canvas_item_accessible_class_init;
3846 tinfo.class_size = query.class_size;
3847 tinfo.instance_size = query.instance_size;
3848 type = g_type_register_static (parent_atk_type,
3849 "FooCanvasItemAccessibility",
3850 &tinfo, 0);
3852 g_type_add_interface_static (type, ATK_TYPE_COMPONENT,
3853 &atk_component_info);
3857 return type;
3860 static AtkObject *
3861 foo_canvas_item_accessible_create (GObject *for_object)
3863 GType type;
3864 AtkObject *accessible;
3865 FooCanvasItem *item;
3867 item = FOO_CANVAS_ITEM (for_object);
3868 g_return_val_if_fail (item != NULL, NULL);
3870 type = foo_canvas_item_accessible_get_type ();
3871 if (type == G_TYPE_INVALID) {
3872 return atk_no_op_object_new (for_object);
3875 accessible = g_object_new (type, NULL);
3876 atk_object_initialize (accessible, for_object);
3877 return accessible;
3880 static GType
3881 foo_canvas_item_accessible_factory_get_accessible_type (void)
3883 return foo_canvas_item_accessible_get_type ();
3886 static AtkObject*
3887 foo_canvas_item_accessible_factory_create_accessible (GObject *obj)
3889 AtkObject *accessible;
3891 g_return_val_if_fail (G_IS_OBJECT (obj), NULL);
3893 accessible = foo_canvas_item_accessible_create (obj);
3895 return accessible;
3898 static void
3899 foo_canvas_item_accessible_factory_class_init (AtkObjectFactoryClass *klass)
3901 klass->create_accessible = foo_canvas_item_accessible_factory_create_accessible;
3902 klass->get_accessible_type = foo_canvas_item_accessible_factory_get_accessible_type;
3905 static GType
3906 foo_canvas_item_accessible_factory_get_type (void)
3908 static GType type = 0;
3910 if (!type) {
3911 static const GTypeInfo tinfo = {
3912 sizeof (AtkObjectFactoryClass),
3913 (GBaseInitFunc) NULL,
3914 (GBaseFinalizeFunc) NULL,
3915 (GClassInitFunc) foo_canvas_item_accessible_factory_class_init,
3916 NULL, /* class_finalize */
3917 NULL, /* class_data */
3918 sizeof (AtkObjectFactory),
3919 0, /* n_preallocs */
3920 NULL
3922 type = g_type_register_static (ATK_TYPE_OBJECT_FACTORY,
3923 "FooCanvasItemAccessibilityFactory",
3924 &tinfo, 0);
3927 return type;
3930 /* Class initialization function for FooCanvasItemClass */
3931 static void
3932 foo_canvas_item_class_init (FooCanvasItemClass *klass)
3934 GObjectClass *gobject_class = (GObjectClass *) klass;
3936 item_parent_class = g_type_class_peek_parent (klass);
3938 gobject_class->set_property = foo_canvas_item_set_property;
3939 gobject_class->get_property = foo_canvas_item_get_property;
3940 gobject_class->dispose = foo_canvas_item_dispose;
3942 g_object_class_install_property
3943 (gobject_class, ITEM_PROP_PARENT,
3944 g_param_spec_object ("parent", NULL, NULL,
3945 FOO_TYPE_CANVAS_ITEM,
3946 G_PARAM_READWRITE));
3948 g_object_class_install_property
3949 (gobject_class, ITEM_PROP_VISIBLE,
3950 g_param_spec_boolean ("visible", NULL, NULL,
3951 TRUE,
3952 G_PARAM_READWRITE));
3954 item_signals[ITEM_EVENT] =
3955 g_signal_new ("event",
3956 G_TYPE_FROM_CLASS (klass),
3957 G_SIGNAL_RUN_LAST,
3958 G_STRUCT_OFFSET (FooCanvasItemClass, event),
3959 boolean_handled_accumulator, NULL,
3960 foo_canvas_marshal_BOOLEAN__BOXED,
3961 G_TYPE_BOOLEAN, 1,
3962 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
3964 klass->realize = foo_canvas_item_realize;
3965 klass->unrealize = foo_canvas_item_unrealize;
3966 klass->map = foo_canvas_item_map;
3967 klass->unmap = foo_canvas_item_unmap;
3968 klass->update = foo_canvas_item_update;
3970 atk_registry_set_factory_type (atk_get_default_registry (),
3971 FOO_TYPE_CANVAS_ITEM,
3972 foo_canvas_item_accessible_factory_get_type ());