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.
5  *
6  * This file is part of the Gnome Library.
7  *
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.
12  *
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.
17  *
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., 51 Franklin Street - Suite 500,
21  * Boston, MA 02110-1335, USA.
22  */
23 /*
24   @NOTATION@
25  */
26 /*
27  * EelCanvas widget - Tk-like canvas widget for Gnome
28  *
29  * EelCanvas 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.
31  *
32  *
33  * Authors: Federico Mena <federico@nuclecu.unam.mx>
34  *          Raph Levien <raph@gimp.org>
35  */
36 
37 /*
38  * TO-DO list for the canvas:
39  *
40  * - Allow to specify whether EelCanvasImage sizes are in units or pixels (scale or don't scale).
41  *
42  * - Implement a flag for eel_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.
45  *
46  * - GC put functions for items.
47  *
48  * - Widget item (finish it).
49  *
50  * - GList *eel_canvas_gimme_all_items_contained_in_this_area (EelCanvas *canvas, Rectangle area);
51  *
52  * - Retrofit all the primitive items with microtile support.
53  *
54  * - Curve support for line item.
55  *
56  * - Arc item (Havoc has it; to be integrated in EelCanvasEllipse).
57  *
58  * - Sane font handling API.
59  *
60  * - Get_arg methods for items:
61  *   - How to fetch the outline width and know whether it is in pixels or units?
62  */
63 
64 #include <config.h>
65 
66 #include <math.h>
67 #include <string.h>
68 #include <stdio.h>
69 #include <gdk/gdkprivate.h>
70 #include <gtk/gtk.h>
71 #include <glib/gi18n-lib.h>
72 #include <cairo-gobject.h>
73 #include "eel-canvas.h"
74 
75 static void eel_canvas_request_update (EelCanvas      *canvas);
76 static void group_add                   (EelCanvasGroup *group,
77 					 EelCanvasItem  *item);
78 static void group_remove                (EelCanvasGroup *group,
79 					 EelCanvasItem  *item);
80 static void redraw_and_repick_if_mapped (EelCanvasItem *item);
81 
82 /*** EelCanvasItem ***/
83 
84 /* Some convenience stuff */
85 #define GCI_UPDATE_MASK (EEL_CANVAS_UPDATE_REQUESTED | EEL_CANVAS_UPDATE_DEEP)
86 #define GCI_EPSILON 1e-18
87 
88 enum {
89 	ITEM_PROP_0,
90 	ITEM_PROP_PARENT,
91 	ITEM_PROP_VISIBLE
92 };
93 
94 enum {
95 	ITEM_DESTROY,
96 	ITEM_EVENT,
97 	ITEM_LAST_SIGNAL
98 };
99 
100 static void eel_canvas_item_class_init     (EelCanvasItemClass *klass);
101 static void eel_canvas_item_init           (EelCanvasItem      *item);
102 static int  emit_event                       (EelCanvas *canvas, GdkEvent *event);
103 
104 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
105 
106 static GObjectClass *item_parent_class;
107 
108 static gpointer accessible_item_parent_class;
109 static gpointer accessible_parent_class;
110 
111 
112 /**
113  * eel_canvas_item_get_type:
114  *
115  * Registers the &EelCanvasItem class if necessary, and returns the type ID
116  * associated to it.
117  *
118  * Return value:  The type ID of the &EelCanvasItem class.
119  **/
120 GType
eel_canvas_item_get_type(void)121 eel_canvas_item_get_type (void)
122 {
123 	static GType canvas_item_type = 0;
124 
125 	if (!canvas_item_type) {
126 		static const GTypeInfo canvas_item_info = {
127 			sizeof (EelCanvasItemClass),
128 			(GBaseInitFunc) NULL,
129 			(GBaseFinalizeFunc) NULL,
130 			(GClassInitFunc) eel_canvas_item_class_init,
131 			NULL,           /* class_finalize */
132 			NULL,           /* class_data */
133 			sizeof (EelCanvasItem),
134 			0,              /* n_preallocs */
135 			(GInstanceInitFunc) eel_canvas_item_init
136 		};
137 
138 		canvas_item_type = g_type_register_static (G_TYPE_INITIALLY_UNOWNED,
139 							   "EelCanvasItem",
140 							   &canvas_item_info,
141 							   0);
142 	}
143 
144 	return canvas_item_type;
145 }
146 
147 /* Object initialization function for EelCanvasItem */
148 static void
eel_canvas_item_init(EelCanvasItem * item)149 eel_canvas_item_init (EelCanvasItem *item)
150 {
151 	item->flags |= EEL_CANVAS_ITEM_VISIBLE;
152 }
153 
154 /**
155  * eel_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:
162  *
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  * eel_canvas_rect_get_type().  The list of object arguments/value pairs is
167  * used to configure the item.
168  *
169  * Return value: The newly-created item.
170  **/
171 EelCanvasItem *
eel_canvas_item_new(EelCanvasGroup * parent,GType type,const gchar * first_arg_name,...)172 eel_canvas_item_new (EelCanvasGroup *parent, GType type, const gchar *first_arg_name, ...)
173 {
174 	EelCanvasItem *item;
175 	va_list args;
176 
177 	g_return_val_if_fail (EEL_IS_CANVAS_GROUP (parent), NULL);
178 	g_return_val_if_fail (g_type_is_a (type, eel_canvas_item_get_type ()), NULL);
179 
180 	item = EEL_CANVAS_ITEM (g_object_new (type, NULL));
181 
182 	va_start (args, first_arg_name);
183 	eel_canvas_item_construct (item, parent, first_arg_name, args);
184 	va_end (args);
185 
186 	return item;
187 }
188 
189 
190 /* Performs post-creation operations on a canvas item (adding it to its parent
191  * group, etc.)
192  */
193 static void
item_post_create_setup(EelCanvasItem * item)194 item_post_create_setup (EelCanvasItem *item)
195 {
196 	group_add (EEL_CANVAS_GROUP (item->parent), item);
197 
198 	redraw_and_repick_if_mapped (item);
199 }
200 
201 /* Set_property handler for canvas items */
202 static void
eel_canvas_item_set_property(GObject * gobject,guint param_id,const GValue * value,GParamSpec * pspec)203 eel_canvas_item_set_property (GObject *gobject, guint param_id,
204 			      const GValue *value, GParamSpec *pspec)
205 {
206 	EelCanvasItem *item;
207 
208 	g_return_if_fail (EEL_IS_CANVAS_ITEM (gobject));
209 
210 	item = EEL_CANVAS_ITEM (gobject);
211 
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 = EEL_CANVAS_ITEM (g_value_get_object (value));
219 			item->canvas = item->parent->canvas;
220 			item_post_create_setup (item);
221 		}
222 		break;
223 	case ITEM_PROP_VISIBLE:
224 		if (g_value_get_boolean (value)) {
225 			eel_canvas_item_show (item);
226 		} else {
227 			eel_canvas_item_hide (item);
228 		}
229 		break;
230 	default:
231 		G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
232 		break;
233 	}
234 }
235 
236 /* Get_property handler for canvas items */
237 static void
eel_canvas_item_get_property(GObject * gobject,guint param_id,GValue * value,GParamSpec * pspec)238 eel_canvas_item_get_property (GObject *gobject, guint param_id,
239 			      GValue *value, GParamSpec *pspec)
240 {
241 	EelCanvasItem *item;
242 
243 	g_return_if_fail (EEL_IS_CANVAS_ITEM (gobject));
244 
245 	item = EEL_CANVAS_ITEM (gobject);
246 
247 	switch (param_id) {
248 	case ITEM_PROP_VISIBLE:
249 		g_value_set_boolean (value, item->flags & EEL_CANVAS_ITEM_VISIBLE);
250 		break;
251 	default:
252 		G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
253 		break;
254 	}
255 }
256 
257 /**
258  * eel_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.
263  *
264  * Constructs a canvas item; meant for use only by item implementations.
265  **/
266 void
eel_canvas_item_construct(EelCanvasItem * item,EelCanvasGroup * parent,const gchar * first_arg_name,va_list args)267 eel_canvas_item_construct (EelCanvasItem *item, EelCanvasGroup *parent,
268 			   const gchar *first_arg_name, va_list args)
269 {
270 	g_return_if_fail (EEL_IS_CANVAS_GROUP (parent));
271 	g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
272 
273 	item->parent = EEL_CANVAS_ITEM (parent);
274 	item->canvas = item->parent->canvas;
275 
276 	g_object_set_valist (G_OBJECT (item), first_arg_name, args);
277 
278 	item_post_create_setup (item);
279 }
280 
281 
282 static void
redraw_and_repick_if_mapped(EelCanvasItem * item)283 redraw_and_repick_if_mapped (EelCanvasItem *item)
284 {
285 	if (item->flags & EEL_CANVAS_ITEM_MAPPED) {
286 		eel_canvas_item_request_redraw (item);
287 		item->canvas->need_repick = TRUE;
288 	}
289 }
290 
291 /* Dispose handler for canvas items */
292 static void
eel_canvas_item_dispose(GObject * object)293 eel_canvas_item_dispose (GObject *object)
294 {
295 	EelCanvasItem *item;
296 
297 	g_return_if_fail (EEL_IS_CANVAS_ITEM (object));
298 
299 	item = EEL_CANVAS_ITEM (object);
300 
301 	if (item->canvas) {
302 		eel_canvas_item_request_redraw (item);
303 
304 		/* Make the canvas forget about us */
305 
306 		if (item == item->canvas->current_item) {
307 			item->canvas->current_item = NULL;
308 			item->canvas->need_repick = TRUE;
309 		}
310 
311 		if (item == item->canvas->new_current_item) {
312 			item->canvas->new_current_item = NULL;
313 			item->canvas->need_repick = TRUE;
314 		}
315 
316 		eel_canvas_item_ungrab (item, GDK_CURRENT_TIME);
317 
318 		if (item == item->canvas->focused_item)
319 			item->canvas->focused_item = NULL;
320 
321 		/* Normal destroy stuff */
322 
323 		if (item->flags & EEL_CANVAS_ITEM_MAPPED)
324 			(* EEL_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
325 
326 		if (item->flags & EEL_CANVAS_ITEM_REALIZED)
327 			(* EEL_CANVAS_ITEM_GET_CLASS (item)->unrealize) (item);
328 
329 		if (item->parent)
330 			group_remove (EEL_CANVAS_GROUP (item->parent), item);
331 
332 		item->canvas = NULL;
333 	}
334 
335 	g_object_set_data (object, "in-destruction", GINT_TO_POINTER (1));
336 	g_signal_emit (object, item_signals[ITEM_DESTROY], 0);
337 
338 	g_object_set_data (object, "in-destruction", NULL);
339 
340 	G_OBJECT_CLASS (item_parent_class)->dispose (object);
341 }
342 
343 void
eel_canvas_item_destroy(EelCanvasItem * item)344 eel_canvas_item_destroy (EelCanvasItem *item)
345 {
346 	if (g_object_get_data (G_OBJECT (item), "in-destruction") == NULL) {
347 		g_object_run_dispose (G_OBJECT (item));
348 	}
349 }
350 
351 /* Realize handler for canvas items */
352 static void
eel_canvas_item_realize(EelCanvasItem * item)353 eel_canvas_item_realize (EelCanvasItem *item)
354 {
355 	if (item->parent && !(item->parent->flags & EEL_CANVAS_ITEM_REALIZED))
356 		(* EEL_CANVAS_ITEM_GET_CLASS (item->parent)->realize) (item->parent);
357 
358 	if (item->parent == NULL && !gtk_widget_get_realized (GTK_WIDGET (item->canvas)))
359 		gtk_widget_realize (GTK_WIDGET (item->canvas));
360 
361 	item->flags |= EEL_CANVAS_ITEM_REALIZED;
362 
363 	eel_canvas_item_request_update (item);
364 }
365 
366 /* Unrealize handler for canvas items */
367 static void
eel_canvas_item_unrealize(EelCanvasItem * item)368 eel_canvas_item_unrealize (EelCanvasItem *item)
369 {
370 	if (item->flags & EEL_CANVAS_ITEM_MAPPED)
371 		(* EEL_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
372 
373 	item->flags &= ~(EEL_CANVAS_ITEM_REALIZED);
374 }
375 
376 /* Map handler for canvas items */
377 static void
eel_canvas_item_map(EelCanvasItem * item)378 eel_canvas_item_map (EelCanvasItem *item)
379 {
380 	item->flags |= EEL_CANVAS_ITEM_MAPPED;
381 }
382 
383 /* Unmap handler for canvas items */
384 static void
eel_canvas_item_unmap(EelCanvasItem * item)385 eel_canvas_item_unmap (EelCanvasItem *item)
386 {
387 	item->flags &= ~(EEL_CANVAS_ITEM_MAPPED);
388 }
389 
390 /* Update handler for canvas items */
391 static void
eel_canvas_item_update(EelCanvasItem * item,double i2w_dx,double i2w_dy,int flags)392 eel_canvas_item_update (EelCanvasItem *item, double i2w_dx, double i2w_dy, int flags)
393 {
394 	item->flags &= ~(EEL_CANVAS_ITEM_NEED_UPDATE);
395 	item->flags &= ~(EEL_CANVAS_ITEM_NEED_DEEP_UPDATE);
396 }
397 
398 /*
399  * This routine invokes the update method of the item
400  * Please notice, that we take parent to canvas pixel matrix as argument
401  * unlike virtual method ::update, whose argument is item 2 canvas pixel
402  * matrix
403  *
404  * I will try to force somewhat meaningful naming for affines (Lauris)
405  * General naming rule is FROM2TO, where FROM and TO are abbreviations
406  * So p2cpx is Parent2CanvasPixel and i2cpx is Item2CanvasPixel
407  * I hope that this helps to keep track of what really happens
408  *
409  */
410 
411 static void
eel_canvas_item_invoke_update(EelCanvasItem * item,double i2w_dx,double i2w_dy,int flags)412 eel_canvas_item_invoke_update (EelCanvasItem *item,
413 			       double i2w_dx,
414 			       double i2w_dy,
415 			       int flags)
416 {
417 	int child_flags;
418 
419 	child_flags = flags;
420 
421 	/* apply object flags to child flags */
422 	child_flags &= ~EEL_CANVAS_UPDATE_REQUESTED;
423 
424 	if (item->flags & EEL_CANVAS_ITEM_NEED_UPDATE)
425 		child_flags |= EEL_CANVAS_UPDATE_REQUESTED;
426 
427 	if (item->flags & EEL_CANVAS_ITEM_NEED_DEEP_UPDATE)
428 		child_flags |= EEL_CANVAS_UPDATE_DEEP;
429 
430 	if (child_flags & GCI_UPDATE_MASK) {
431 		if (EEL_CANVAS_ITEM_GET_CLASS (item)->update)
432 			EEL_CANVAS_ITEM_GET_CLASS (item)->update (item, i2w_dx, i2w_dy, child_flags);
433 	}
434 
435 	/* If this fail you probably forgot to chain up to
436 	 * EelCanvasItem::update from a derived class */
437  	g_return_if_fail (!(item->flags & EEL_CANVAS_ITEM_NEED_UPDATE));
438 }
439 
440 /*
441  * This routine invokes the point method of the item.
442  * The arguments x, y should be in the parent item local coordinates.
443  */
444 
445 static double
eel_canvas_item_invoke_point(EelCanvasItem * item,double x,double y,int cx,int cy,EelCanvasItem ** actual_item)446 eel_canvas_item_invoke_point (EelCanvasItem *item, double x, double y, int cx, int cy, EelCanvasItem **actual_item)
447 {
448 	/* Calculate x & y in item local coordinates */
449 
450 	if (EEL_CANVAS_ITEM_GET_CLASS (item)->point)
451 		return EEL_CANVAS_ITEM_GET_CLASS (item)->point (item, x, y, cx, cy, actual_item);
452 
453 	return 1e18;
454 }
455 
456 /**
457  * eel_canvas_item_set:
458  * @item: A canvas item.
459  * @first_arg_name: The list of object argument name/value pairs used to configure the item.
460  * @Varargs:
461  *
462  * Configures a canvas item.  The arguments in the item are set to the specified
463  * values, and the item is repainted as appropriate.
464  **/
465 void
eel_canvas_item_set(EelCanvasItem * item,const gchar * first_arg_name,...)466 eel_canvas_item_set (EelCanvasItem *item, const gchar *first_arg_name, ...)
467 {
468 	va_list args;
469 
470 	va_start (args, first_arg_name);
471 	eel_canvas_item_set_valist (item, first_arg_name, args);
472 	va_end (args);
473 }
474 
475 
476 /**
477  * eel_canvas_item_set_valist:
478  * @item: A canvas item.
479  * @first_arg_name: The name of the first argument used to configure the item.
480  * @args: The list of object argument name/value pairs used to configure the item.
481  *
482  * Configures a canvas item.  The arguments in the item are set to the specified
483  * values, and the item is repainted as appropriate.
484  **/
485 void
eel_canvas_item_set_valist(EelCanvasItem * item,const gchar * first_arg_name,va_list args)486 eel_canvas_item_set_valist (EelCanvasItem *item, const gchar *first_arg_name, va_list args)
487 {
488 	g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
489 
490 	g_object_set_valist (G_OBJECT (item), first_arg_name, args);
491 
492 	item->canvas->need_repick = TRUE;
493 }
494 
495 
496 /**
497  * eel_canvas_item_move:
498  * @item: A canvas item.
499  * @dx: Horizontal offset.
500  * @dy: Vertical offset.
501  *
502  * Moves a canvas item by creating an affine transformation matrix for
503  * translation by using the specified values. This happens in item
504  * local coordinate system, so if you have nontrivial transform, it
505  * most probably does not do, what you want.
506  **/
507 void
eel_canvas_item_move(EelCanvasItem * item,double dx,double dy)508 eel_canvas_item_move (EelCanvasItem *item, double dx, double dy)
509 {
510         g_return_if_fail (item != NULL);
511         g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
512 
513         if (!EEL_CANVAS_ITEM_GET_CLASS (item)->translate) {
514                 g_warning ("Item type %s does not implement translate method.\n",
515                            g_type_name (G_OBJECT_TYPE (item)));
516                 return;
517         }
518 
519         (* EEL_CANVAS_ITEM_GET_CLASS (item)->translate) (item, dx, dy);
520 
521 	if (item->flags & EEL_CANVAS_ITEM_MAPPED)
522 		item->canvas->need_repick = TRUE;
523 
524 	if (!(item->flags & EEL_CANVAS_ITEM_NEED_DEEP_UPDATE)) {
525 		item->flags |= EEL_CANVAS_ITEM_NEED_DEEP_UPDATE;
526 		if (item->parent != NULL)
527 			eel_canvas_item_request_update (item->parent);
528 		else
529 			eel_canvas_request_update (item->canvas);
530 	}
531 
532 }
533 
534 static void
eel_canvas_queue_resize(EelCanvas * canvas)535 eel_canvas_queue_resize (EelCanvas *canvas)
536 {
537 	if (gtk_widget_is_drawable (GTK_WIDGET (canvas)))
538 		gtk_widget_queue_resize (GTK_WIDGET (canvas));
539 }
540 
541 /* Convenience function to reorder items in a group's child list.  This puts the
542  * specified link after the "before" link. Returns TRUE if the list was changed.
543  */
544 static gboolean
put_item_after(GList * link,GList * before)545 put_item_after (GList *link, GList *before)
546 {
547 	EelCanvasGroup *parent;
548 
549 	if (link == before)
550 		return FALSE;
551 
552 	parent = EEL_CANVAS_GROUP (EEL_CANVAS_ITEM (link->data)->parent);
553 
554 	if (before == NULL) {
555 		if (link == parent->item_list)
556 			return FALSE;
557 
558 		link->prev->next = link->next;
559 
560 		if (link->next)
561 			link->next->prev = link->prev;
562 		else
563 			parent->item_list_end = link->prev;
564 
565 		link->prev = before;
566 		link->next = parent->item_list;
567 		link->next->prev = link;
568 		parent->item_list = link;
569 	} else {
570 		if ((link == parent->item_list_end) && (before == parent->item_list_end->prev))
571 			return FALSE;
572 
573 		if (link->next)
574 			link->next->prev = link->prev;
575 
576 		if (link->prev)
577 			link->prev->next = link->next;
578 		else {
579 			parent->item_list = link->next;
580 			parent->item_list->prev = NULL;
581 		}
582 
583 		link->prev = before;
584 		link->next = before->next;
585 
586 		link->prev->next = link;
587 
588 		if (link->next)
589 			link->next->prev = link;
590 		else
591 			parent->item_list_end = link;
592 	}
593 	return TRUE;
594 }
595 
596 
597 /**
598  * eel_canvas_item_raise:
599  * @item: A canvas item.
600  * @positions: Number of steps to raise the item.
601  *
602  * Raises the item in its parent's stack by the specified number of positions.
603  * If the number of positions is greater than the distance to the top of the
604  * stack, then the item is put at the top.
605  **/
606 void
eel_canvas_item_raise(EelCanvasItem * item,int positions)607 eel_canvas_item_raise (EelCanvasItem *item, int positions)
608 {
609 	GList *link, *before;
610 	EelCanvasGroup *parent;
611 
612 	g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
613 	g_return_if_fail (positions >= 0);
614 
615 	if (!item->parent || positions == 0)
616 		return;
617 
618 	parent = EEL_CANVAS_GROUP (item->parent);
619 	link = g_list_find (parent->item_list, item);
620 	g_assert (link != NULL);
621 
622 	for (before = link; positions && before; positions--)
623 		before = before->next;
624 
625 	if (!before)
626 		before = parent->item_list_end;
627 
628 	if (put_item_after (link, before)) {
629 		redraw_and_repick_if_mapped (item);
630 	}
631 }
632 
633 
634 /**
635  * eel_canvas_item_lower:
636  * @item: A canvas item.
637  * @positions: Number of steps to lower the item.
638  *
639  * Lowers the item in its parent's stack by the specified number of positions.
640  * If the number of positions is greater than the distance to the bottom of the
641  * stack, then the item is put at the bottom.
642  **/
643 void
eel_canvas_item_lower(EelCanvasItem * item,int positions)644 eel_canvas_item_lower (EelCanvasItem *item, int positions)
645 {
646 	GList *link, *before;
647 	EelCanvasGroup *parent;
648 
649 	g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
650 	g_return_if_fail (positions >= 1);
651 
652 	if (!item->parent || positions == 0)
653 		return;
654 
655 	parent = EEL_CANVAS_GROUP (item->parent);
656 	link = g_list_find (parent->item_list, item);
657 	g_assert (link != NULL);
658 
659 	if (link->prev)
660 		for (before = link->prev; positions && before; positions--)
661 			before = before->prev;
662 	else
663 		before = NULL;
664 
665 	if (put_item_after (link, before)) {
666 		redraw_and_repick_if_mapped (item);
667 	}
668 }
669 
670 
671 /**
672  * eel_canvas_item_raise_to_top:
673  * @item: A canvas item.
674  *
675  * Raises an item to the top of its parent's stack.
676  **/
677 void
eel_canvas_item_raise_to_top(EelCanvasItem * item)678 eel_canvas_item_raise_to_top (EelCanvasItem *item)
679 {
680 	GList *link;
681 	EelCanvasGroup *parent;
682 
683 	g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
684 
685 	if (!item->parent)
686 		return;
687 
688 	parent = EEL_CANVAS_GROUP (item->parent);
689 	link = g_list_find (parent->item_list, item);
690 	g_assert (link != NULL);
691 
692 	if (put_item_after (link, parent->item_list_end)) {
693 		redraw_and_repick_if_mapped (item);
694 	}
695 }
696 
697 
698 /**
699  * eel_canvas_item_lower_to_bottom:
700  * @item: A canvas item.
701  *
702  * Lowers an item to the bottom of its parent's stack.
703  **/
704 void
eel_canvas_item_lower_to_bottom(EelCanvasItem * item)705 eel_canvas_item_lower_to_bottom (EelCanvasItem *item)
706 {
707 	GList *link;
708 	EelCanvasGroup *parent;
709 
710 	g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
711 
712 	if (!item->parent)
713 		return;
714 
715 	parent = EEL_CANVAS_GROUP (item->parent);
716 	link = g_list_find (parent->item_list, item);
717 	g_assert (link != NULL);
718 
719 	if (put_item_after (link, NULL)) {
720 		redraw_and_repick_if_mapped (item);
721 	}
722 }
723 
724 /**
725  * eel_canvas_item_send_behind:
726  * @item: A canvas item.
727  * @behind_item: The canvas item to put item behind, or NULL
728  *
729  * Moves item to a in the position in the stacking order so that
730  * it is placed immediately below behind_item, or at the top if
731  * behind_item is NULL.
732  **/
733 void
eel_canvas_item_send_behind(EelCanvasItem * item,EelCanvasItem * behind_item)734 eel_canvas_item_send_behind (EelCanvasItem *item,
735 			     EelCanvasItem *behind_item)
736 {
737 	GList *item_list;
738 	int item_position, behind_position;
739 
740 	g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
741 
742 	if (behind_item == NULL) {
743 		eel_canvas_item_raise_to_top (item);
744 		return;
745 	}
746 
747 	g_return_if_fail (EEL_IS_CANVAS_ITEM (behind_item));
748 	g_return_if_fail (item->parent == behind_item->parent);
749 
750 	item_list = EEL_CANVAS_GROUP (item->parent)->item_list;
751 
752 	item_position = g_list_index (item_list, item);
753 	g_assert (item_position != -1);
754 	behind_position = g_list_index (item_list, behind_item);
755 	g_assert (behind_position != -1);
756 	g_assert (item_position != behind_position);
757 
758 	if (item_position == behind_position - 1) {
759 		return;
760 	}
761 
762 	if (item_position < behind_position) {
763 		eel_canvas_item_raise (item, (behind_position - 1) - item_position);
764 	} else {
765 		eel_canvas_item_lower (item, item_position - behind_position);
766 	}
767 }
768 
769 /**
770  * eel_canvas_item_show:
771  * @item: A canvas item.
772  *
773  * Shows a canvas item.  If the item was already shown, then no action is taken.
774  **/
775 void
eel_canvas_item_show(EelCanvasItem * item)776 eel_canvas_item_show (EelCanvasItem *item)
777 {
778 	g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
779 
780 	if (!(item->flags & EEL_CANVAS_ITEM_VISIBLE)) {
781 		item->flags |= EEL_CANVAS_ITEM_VISIBLE;
782 
783 		if (!(item->flags & EEL_CANVAS_ITEM_REALIZED))
784 			(* EEL_CANVAS_ITEM_GET_CLASS (item)->realize) (item);
785 
786 		if (item->parent != NULL) {
787 			if (!(item->flags & EEL_CANVAS_ITEM_MAPPED) &&
788 			    (item->parent->flags & EEL_CANVAS_ITEM_MAPPED))
789 				(* EEL_CANVAS_ITEM_GET_CLASS (item)->map) (item);
790 		} else {
791 			if (!(item->flags & EEL_CANVAS_ITEM_MAPPED) &&
792 			    gtk_widget_get_mapped (GTK_WIDGET (item->canvas)))
793 				(* EEL_CANVAS_ITEM_GET_CLASS (item)->map) (item);
794 		}
795 
796 		redraw_and_repick_if_mapped (item);
797 		eel_canvas_queue_resize (item->canvas);
798 	}
799 }
800 
801 
802 /**
803  * eel_canvas_item_hide:
804  * @item: A canvas item.
805  *
806  * Hides a canvas item.  If the item was already hidden, then no action is
807  * taken.
808  **/
809 void
eel_canvas_item_hide(EelCanvasItem * item)810 eel_canvas_item_hide (EelCanvasItem *item)
811 {
812 	g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
813 
814 	if (item->flags & EEL_CANVAS_ITEM_VISIBLE) {
815 		item->flags &= ~EEL_CANVAS_ITEM_VISIBLE;
816 
817 		redraw_and_repick_if_mapped (item);
818 
819 		if (item->flags & EEL_CANVAS_ITEM_MAPPED)
820 			(* EEL_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
821 
822 		eel_canvas_queue_resize (item->canvas);
823 
824 		/* No need to unrealize when we just want to hide */
825 	}
826 }
827 
828 
829 /**
830  * eel_canvas_item_grab:
831  * @item: A canvas item.
832  * @event_mask: Mask of events that will be sent to this item.
833  * @cursor: If non-NULL, the cursor that will be used while the grab is active.
834  * @etime: The timestamp required for grabbing the mouse, or GDK_CURRENT_TIME.
835  *
836  * Specifies that all events that match the specified event mask should be sent
837  * to the specified item, and also grabs the mouse by calling
838  * gdk_pointer_grab().  The event mask is also used when grabbing the pointer.
839  * If @cursor is not NULL, then that cursor is used while the grab is active.
840  * The @etime parameter is the timestamp required for grabbing the mouse.
841  *
842  * Return value: If an item was already grabbed, it returns %GDK_GRAB_ALREADY_GRABBED.  If
843  * the specified item was hidden by calling eel_canvas_item_hide(), then it
844  * returns %GDK_GRAB_NOT_VIEWABLE.  Else, it returns the result of calling
845  * gdk_pointer_grab().
846  **/
847 GdkGrabStatus
eel_canvas_item_grab(EelCanvasItem * item,GdkEventMask event_mask,GdkCursor * cursor,guint32 timestamp)848 eel_canvas_item_grab (EelCanvasItem *item,
849 		      GdkEventMask event_mask,
850 		      GdkCursor *cursor,
851 		      guint32 timestamp)
852 {
853 	GdkGrabStatus retval;
854 	GdkDisplay *display;
855 	GdkDeviceManager *manager;
856 	GdkDevice *device;
857 
858 	g_return_val_if_fail (EEL_IS_CANVAS_ITEM (item), GDK_GRAB_NOT_VIEWABLE);
859 	g_return_val_if_fail (gtk_widget_get_mapped (GTK_WIDGET (item->canvas)),
860 			      GDK_GRAB_NOT_VIEWABLE);
861 
862 	if (item->canvas->grabbed_item)
863 		return GDK_GRAB_ALREADY_GRABBED;
864 
865 	if (!(item->flags & EEL_CANVAS_ITEM_MAPPED))
866 		return GDK_GRAB_NOT_VIEWABLE;
867 
868 	display = gtk_widget_get_display (GTK_WIDGET (item->canvas));
869 	manager = gdk_display_get_device_manager (display);
870 	device = gdk_device_manager_get_client_pointer (manager);
871 
872 	retval = gdk_device_grab (device,
873 				  gtk_layout_get_bin_window (GTK_LAYOUT (item->canvas)),
874 				  GDK_OWNERSHIP_NONE,
875 				  FALSE,
876 				  event_mask,
877 				  cursor,
878 				  timestamp);
879 
880 	if (retval != GDK_GRAB_SUCCESS)
881 		return retval;
882 
883 	item->canvas->grabbed_item = item;
884 	item->canvas->grabbed_event_mask = event_mask;
885 	item->canvas->current_item = item; /* So that events go to the grabbed item */
886 
887 	return retval;
888 }
889 
890 
891 /**
892  * eel_canvas_item_ungrab:
893  * @item: A canvas item that holds a grab.
894  * @etime: The timestamp for ungrabbing the mouse.
895  *
896  * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
897  * mouse.
898  **/
899 void
eel_canvas_item_ungrab(EelCanvasItem * item,guint32 etime)900 eel_canvas_item_ungrab (EelCanvasItem *item, guint32 etime)
901 {
902 	GdkDisplay *display;
903 	GdkDeviceManager *manager;
904 	GdkDevice *device;
905 
906 	g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
907 
908 	if (item->canvas->grabbed_item != item)
909 		return;
910 
911 	display = gtk_widget_get_display (GTK_WIDGET (item->canvas));
912 	manager = gdk_display_get_device_manager (display);
913 	device = gdk_device_manager_get_client_pointer (manager);
914 
915 	item->canvas->grabbed_item = NULL;
916 	gdk_device_ungrab (device, etime);
917 }
918 
919 /**
920  * eel_canvas_item_w2i:
921  * @item: A canvas item.
922  * @x: X coordinate to convert (input/output value).
923  * @y: Y coordinate to convert (input/output value).
924  *
925  * Converts a coordinate pair from world coordinates to item-relative
926  * coordinates.
927  **/
928 void
eel_canvas_item_w2i(EelCanvasItem * item,double * x,double * y)929 eel_canvas_item_w2i (EelCanvasItem *item, double *x, double *y)
930 {
931 	g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
932 	g_return_if_fail (x != NULL);
933 	g_return_if_fail (y != NULL);
934 
935 	item = item->parent;
936 	while (item) {
937 		if (EEL_IS_CANVAS_GROUP (item)) {
938 			*x -= EEL_CANVAS_GROUP (item)->xpos;
939 			*y -= EEL_CANVAS_GROUP (item)->ypos;
940 		}
941 
942 		item = item->parent;
943 	}
944 }
945 
946 
947 /**
948  * eel_canvas_item_i2w:
949  * @item: A canvas item.
950  * @x: X coordinate to convert (input/output value).
951  * @y: Y coordinate to convert (input/output value).
952  *
953  * Converts a coordinate pair from item-relative coordinates to world
954  * coordinates.
955  **/
956 void
eel_canvas_item_i2w(EelCanvasItem * item,double * x,double * y)957 eel_canvas_item_i2w (EelCanvasItem *item, double *x, double *y)
958 {
959 	g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
960 	g_return_if_fail (x != NULL);
961 	g_return_if_fail (y != NULL);
962 
963 	item = item->parent;
964 	while (item) {
965 		if (EEL_IS_CANVAS_GROUP (item)) {
966 			*x += EEL_CANVAS_GROUP (item)->xpos;
967 			*y += EEL_CANVAS_GROUP (item)->ypos;
968 		}
969 
970 		item = item->parent;
971 	}
972 }
973 
974 /* Returns whether the item is an inferior of or is equal to the parent. */
975 static int
is_descendant(EelCanvasItem * item,EelCanvasItem * parent)976 is_descendant (EelCanvasItem *item, EelCanvasItem *parent)
977 {
978 	for (; item; item = item->parent)
979 		if (item == parent)
980 			return TRUE;
981 
982 	return FALSE;
983 }
984 
985 /**
986  * eel_canvas_item_reparent:
987  * @item: A canvas item.
988  * @new_group: A canvas group.
989  *
990  * Changes the parent of the specified item to be the new group.  The item keeps
991  * its group-relative coordinates as for its old parent, so the item may change
992  * its absolute position within the canvas.
993  **/
994 void
eel_canvas_item_reparent(EelCanvasItem * item,EelCanvasGroup * new_group)995 eel_canvas_item_reparent (EelCanvasItem *item, EelCanvasGroup *new_group)
996 {
997 	g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
998 	g_return_if_fail (EEL_IS_CANVAS_GROUP (new_group));
999 
1000 	/* Both items need to be in the same canvas */
1001 	g_return_if_fail (item->canvas == EEL_CANVAS_ITEM (new_group)->canvas);
1002 
1003 	/* The group cannot be an inferior of the item or be the item itself --
1004 	 * this also takes care of the case where the item is the root item of
1005 	 * the canvas.  */
1006 	g_return_if_fail (!is_descendant (EEL_CANVAS_ITEM (new_group), item));
1007 
1008 	/* Everything is ok, now actually reparent the item */
1009 
1010 	g_object_ref (G_OBJECT (item)); /* protect it from the unref in group_remove */
1011 
1012 	eel_canvas_item_request_redraw (item);
1013 
1014 	group_remove (EEL_CANVAS_GROUP (item->parent), item);
1015 	item->parent = EEL_CANVAS_ITEM (new_group);
1016 	/* item->canvas is unchanged.  */
1017 	group_add (new_group, item);
1018 
1019 	/* Redraw and repick */
1020 
1021 	redraw_and_repick_if_mapped (item);
1022 
1023 	g_object_unref (G_OBJECT (item));
1024 }
1025 
1026 /**
1027  * eel_canvas_item_grab_focus:
1028  * @item: A canvas item.
1029  *
1030  * Makes the specified item take the keyboard focus, so all keyboard events will
1031  * be sent to it.  If the canvas widget itself did not have the focus, it grabs
1032  * it as well.
1033  **/
1034 void
eel_canvas_item_grab_focus(EelCanvasItem * item)1035 eel_canvas_item_grab_focus (EelCanvasItem *item)
1036 {
1037 	EelCanvasItem *focused_item;
1038 	GdkEvent ev;
1039 
1040 	g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
1041 	g_return_if_fail (gtk_widget_get_can_focus (GTK_WIDGET (item->canvas)));
1042 
1043 	focused_item = item->canvas->focused_item;
1044 
1045 	if (focused_item) {
1046 		ev.focus_change.type = GDK_FOCUS_CHANGE;
1047 		ev.focus_change.window = gtk_layout_get_bin_window (GTK_LAYOUT (item->canvas));
1048 		ev.focus_change.send_event = FALSE;
1049 		ev.focus_change.in = FALSE;
1050 
1051 		emit_event (item->canvas, &ev);
1052 	}
1053 
1054 	item->canvas->focused_item = item;
1055 	gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
1056 
1057 	if (focused_item) {
1058 		ev.focus_change.type = GDK_FOCUS_CHANGE;
1059 		ev.focus_change.window = gtk_layout_get_bin_window (GTK_LAYOUT (item->canvas));
1060 		ev.focus_change.send_event = FALSE;
1061 		ev.focus_change.in = TRUE;
1062 
1063 		emit_event (item->canvas, &ev);
1064 	}
1065 }
1066 
1067 
1068 /**
1069  * eel_canvas_item_get_bounds:
1070  * @item: A canvas item.
1071  * @x1: Leftmost edge of the bounding box (return value).
1072  * @y1: Upper edge of the bounding box (return value).
1073  * @x2: Rightmost edge of the bounding box (return value).
1074  * @y2: Lower edge of the bounding box (return value).
1075  *
1076  * Queries the bounding box of a canvas item.  The bounds are returned in the
1077  * coordinate system of the item's parent.
1078  **/
1079 void
eel_canvas_item_get_bounds(EelCanvasItem * item,double * x1,double * y1,double * x2,double * y2)1080 eel_canvas_item_get_bounds (EelCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
1081 {
1082 	double tx1, ty1, tx2, ty2;
1083 
1084 	g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
1085 
1086 	tx1 = ty1 = tx2 = ty2 = 0.0;
1087 
1088 	/* Get the item's bounds in its coordinate system */
1089 
1090 	if (EEL_CANVAS_ITEM_GET_CLASS (item)->bounds)
1091 		(* EEL_CANVAS_ITEM_GET_CLASS (item)->bounds) (item, &tx1, &ty1, &tx2, &ty2);
1092 
1093 	/* Return the values */
1094 
1095 	if (x1)
1096 		*x1 = tx1;
1097 
1098 	if (y1)
1099 		*y1 = ty1;
1100 
1101 	if (x2)
1102 		*x2 = tx2;
1103 
1104 	if (y2)
1105 		*y2 = ty2;
1106 }
1107 
1108 
1109 /**
1110  * eel_canvas_item_request_update
1111  * @item: A canvas item.
1112  *
1113  * To be used only by item implementations.  Requests that the canvas queue an
1114  * update for the specified item.
1115  **/
1116 void
eel_canvas_item_request_update(EelCanvasItem * item)1117 eel_canvas_item_request_update (EelCanvasItem *item)
1118 {
1119 	if (NULL == item->canvas)
1120 		return;
1121 
1122 	g_return_if_fail (!item->canvas->doing_update);
1123 
1124 	if (item->flags & EEL_CANVAS_ITEM_NEED_UPDATE)
1125 		return;
1126 
1127 	item->flags |= EEL_CANVAS_ITEM_NEED_UPDATE;
1128 
1129 	if (item->parent != NULL) {
1130 		/* Recurse up the tree */
1131 		eel_canvas_item_request_update (item->parent);
1132 	} else {
1133 		/* Have reached the top of the tree, make sure the update call gets scheduled. */
1134 		eel_canvas_request_update (item->canvas);
1135 	}
1136 }
1137 
1138 /**
1139  * eel_canvas_item_request_update
1140  * @item: A canvas item.
1141  *
1142  * Convenience function that informs a canvas that the specified item needs
1143  * to be repainted. To be used by item implementations
1144  **/
1145 void
eel_canvas_item_request_redraw(EelCanvasItem * item)1146 eel_canvas_item_request_redraw (EelCanvasItem *item)
1147 {
1148 	if (item->flags & EEL_CANVAS_ITEM_MAPPED)
1149 		eel_canvas_request_redraw (item->canvas,
1150 					   item->x1, item->y1,
1151 					   item->x2 + 1, item->y2 + 3);
1152 }
1153 
1154 
1155 
1156 /*** EelCanvasGroup ***/
1157 
1158 
1159 enum {
1160 	GROUP_PROP_0,
1161 	GROUP_PROP_X,
1162 	GROUP_PROP_Y
1163 };
1164 
1165 
1166 static void eel_canvas_group_class_init  (EelCanvasGroupClass *klass);
1167 static void eel_canvas_group_init        (EelCanvasGroup      *group);
1168 static void eel_canvas_group_set_property(GObject               *object,
1169 					    guint                  param_id,
1170 					    const GValue          *value,
1171 					    GParamSpec            *pspec);
1172 static void eel_canvas_group_get_property(GObject               *object,
1173 					    guint                  param_id,
1174 					    GValue                *value,
1175 					    GParamSpec            *pspec);
1176 
1177 static void eel_canvas_group_destroy     (EelCanvasItem           *object);
1178 
1179 static void   eel_canvas_group_update      (EelCanvasItem *item,
1180 					      double           i2w_dx,
1181 					      double           i2w_dy,
1182 					      int              flags);
1183 static void   eel_canvas_group_unrealize   (EelCanvasItem *item);
1184 static void   eel_canvas_group_map         (EelCanvasItem *item);
1185 static void   eel_canvas_group_unmap       (EelCanvasItem *item);
1186 static void   eel_canvas_group_draw        (EelCanvasItem  *item,
1187                                             cairo_t        *cr,
1188                                             cairo_region_t *region);
1189 static double eel_canvas_group_point       (EelCanvasItem *item, double x, double y,
1190 					      int cx, int cy,
1191 					      EelCanvasItem **actual_item);
1192 static void   eel_canvas_group_translate   (EelCanvasItem *item, double dx, double dy);
1193 static void   eel_canvas_group_bounds      (EelCanvasItem *item, double *x1, double *y1,
1194 					      double *x2, double *y2);
1195 
1196 
1197 static EelCanvasItemClass *group_parent_class;
1198 
1199 
1200 /**
1201  * eel_canvas_group_get_type:
1202  *
1203  * Registers the &EelCanvasGroup class if necessary, and returns the type ID
1204  * associated to it.
1205  *
1206  * Return value:  The type ID of the &EelCanvasGroup class.
1207  **/
1208 GType
eel_canvas_group_get_type(void)1209 eel_canvas_group_get_type (void)
1210 {
1211 	static GType group_type = 0;
1212 
1213 	if (!group_type) {
1214 		static const GTypeInfo group_info = {
1215 			sizeof (EelCanvasGroupClass),
1216 			(GBaseInitFunc) NULL,
1217 			(GBaseFinalizeFunc) NULL,
1218 			(GClassInitFunc) eel_canvas_group_class_init,
1219 			NULL,           /* class_finalize */
1220 			NULL,           /* class_data */
1221 			sizeof (EelCanvasGroup),
1222 			0,              /* n_preallocs */
1223 			(GInstanceInitFunc) eel_canvas_group_init
1224 
1225 
1226 		};
1227 
1228 		group_type = g_type_register_static (eel_canvas_item_get_type (),
1229 						     "EelCanvasGroup",
1230 						     &group_info,
1231 						     0);
1232 	}
1233 
1234 	return group_type;
1235 }
1236 
1237 /* Class initialization function for EelCanvasGroupClass */
1238 static void
eel_canvas_group_class_init(EelCanvasGroupClass * klass)1239 eel_canvas_group_class_init (EelCanvasGroupClass *klass)
1240 {
1241 	GObjectClass *gobject_class;
1242 	EelCanvasItemClass *item_class;
1243 
1244 	gobject_class = (GObjectClass *) klass;
1245 	item_class = (EelCanvasItemClass *) klass;
1246 
1247 	group_parent_class = g_type_class_peek_parent (klass);
1248 
1249 	gobject_class->set_property = eel_canvas_group_set_property;
1250 	gobject_class->get_property = eel_canvas_group_get_property;
1251 
1252 	g_object_class_install_property
1253 		(gobject_class, GROUP_PROP_X,
1254 		 g_param_spec_double ("x",
1255 				      _("X"),
1256 				      _("X"),
1257 				      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
1258 				      G_PARAM_READWRITE));
1259 	g_object_class_install_property
1260 		(gobject_class, GROUP_PROP_Y,
1261 		 g_param_spec_double ("y",
1262 				      _("Y"),
1263 				      _("Y"),
1264 				      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
1265 				      G_PARAM_READWRITE));
1266 
1267 	item_class->destroy = eel_canvas_group_destroy;
1268 	item_class->update = eel_canvas_group_update;
1269 	item_class->unrealize = eel_canvas_group_unrealize;
1270 	item_class->map = eel_canvas_group_map;
1271 	item_class->unmap = eel_canvas_group_unmap;
1272 	item_class->draw = eel_canvas_group_draw;
1273 	item_class->point = eel_canvas_group_point;
1274 	item_class->translate = eel_canvas_group_translate;
1275 	item_class->bounds = eel_canvas_group_bounds;
1276 }
1277 
1278 /* Object initialization function for EelCanvasGroup */
1279 static void
eel_canvas_group_init(EelCanvasGroup * group)1280 eel_canvas_group_init (EelCanvasGroup *group)
1281 {
1282 	group->xpos = 0.0;
1283 	group->ypos = 0.0;
1284 }
1285 
1286 /* Set_property handler for canvas groups */
1287 static void
eel_canvas_group_set_property(GObject * gobject,guint param_id,const GValue * value,GParamSpec * pspec)1288 eel_canvas_group_set_property (GObject *gobject, guint param_id,
1289 			       const GValue *value, GParamSpec *pspec)
1290 {
1291 	EelCanvasItem *item;
1292 	EelCanvasGroup *group;
1293 	double old;
1294 	gboolean moved;
1295 
1296 	g_return_if_fail (EEL_IS_CANVAS_GROUP (gobject));
1297 
1298 	item = EEL_CANVAS_ITEM (gobject);
1299 	group = EEL_CANVAS_GROUP (gobject);
1300 
1301 	moved = FALSE;
1302 	switch (param_id) {
1303 	case GROUP_PROP_X:
1304 		old = group->xpos;
1305 		group->xpos = g_value_get_double (value);
1306 		if (old != group->xpos)
1307 			moved = TRUE;
1308 		break;
1309 
1310 	case GROUP_PROP_Y:
1311 		old = group->ypos;
1312 		group->ypos = g_value_get_double (value);
1313 		if (old != group->ypos)
1314 			moved = TRUE;
1315 		break;
1316 
1317 	default:
1318 		G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
1319 		break;
1320 	}
1321 
1322 	if (moved) {
1323 		item->flags |= EEL_CANVAS_ITEM_NEED_DEEP_UPDATE;
1324 		if (item->parent != NULL)
1325 			eel_canvas_item_request_update (item->parent);
1326 		else
1327 			eel_canvas_request_update (item->canvas);
1328 	}
1329 }
1330 
1331 /* Get_property handler for canvas groups */
1332 static void
eel_canvas_group_get_property(GObject * gobject,guint param_id,GValue * value,GParamSpec * pspec)1333 eel_canvas_group_get_property (GObject *gobject, guint param_id,
1334 				 GValue *value, GParamSpec *pspec)
1335 {
1336 	EelCanvasGroup *group;
1337 
1338 	g_return_if_fail (EEL_IS_CANVAS_GROUP (gobject));
1339 
1340 	group = EEL_CANVAS_GROUP (gobject);
1341 
1342 	switch (param_id) {
1343 	case GROUP_PROP_X:
1344 		g_value_set_double (value, group->xpos);
1345 		break;
1346 
1347 	case GROUP_PROP_Y:
1348 		g_value_set_double (value, group->ypos);
1349 		break;
1350 
1351 	default:
1352 		G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
1353 		break;
1354 	}
1355 }
1356 
1357 /* Destroy handler for canvas groups */
1358 static void
eel_canvas_group_destroy(EelCanvasItem * object)1359 eel_canvas_group_destroy (EelCanvasItem *object)
1360 {
1361 	EelCanvasGroup *group;
1362 	EelCanvasItem *child;
1363 	GList *list;
1364 
1365 	g_return_if_fail (EEL_IS_CANVAS_GROUP (object));
1366 
1367 	group = EEL_CANVAS_GROUP (object);
1368 
1369 	list = group->item_list;
1370 	while (list) {
1371 		child = list->data;
1372 		list = list->next;
1373 
1374 		eel_canvas_item_destroy (child);
1375 	}
1376 
1377 	if (EEL_CANVAS_ITEM_CLASS (group_parent_class)->destroy)
1378 		(* EEL_CANVAS_ITEM_CLASS (group_parent_class)->destroy) (object);
1379 }
1380 
1381 /* Update handler for canvas groups */
1382 static void
eel_canvas_group_update(EelCanvasItem * item,double i2w_dx,double i2w_dy,int flags)1383 eel_canvas_group_update (EelCanvasItem *item, double i2w_dx, double i2w_dy, int flags)
1384 {
1385 	EelCanvasGroup *group;
1386 	GList *list;
1387 	EelCanvasItem *i;
1388 	double bbox_x0, bbox_y0, bbox_x1, bbox_y1;
1389 	gboolean first = TRUE;
1390 
1391 	group = EEL_CANVAS_GROUP (item);
1392 
1393 	(* group_parent_class->update) (item, i2w_dx, i2w_dy, flags);
1394 
1395 	bbox_x0 = 0;
1396 	bbox_y0 = 0;
1397 	bbox_x1 = 0;
1398 	bbox_y1 = 0;
1399 
1400 	for (list = group->item_list; list; list = list->next) {
1401 		i = list->data;
1402 
1403 		eel_canvas_item_invoke_update (i, i2w_dx + group->xpos, i2w_dy + group->ypos, flags);
1404 
1405 		if (first) {
1406 			first = FALSE;
1407 			bbox_x0 = i->x1;
1408 			bbox_y0 = i->y1;
1409 			bbox_x1 = i->x2;
1410 			bbox_y1 = i->y2;
1411 		} else {
1412 			bbox_x0 = MIN (bbox_x0, i->x1);
1413 			bbox_y0 = MIN (bbox_y0, i->y1);
1414 			bbox_x1 = MAX (bbox_x1, i->x2);
1415 			bbox_y1 = MAX (bbox_y1, i->y2);
1416 		}
1417 	}
1418 	item->x1 = bbox_x0;
1419 	item->y1 = bbox_y0;
1420 	item->x2 = bbox_x1;
1421 	item->y2 = bbox_y1;
1422 }
1423 
1424 /* Unrealize handler for canvas groups */
1425 static void
eel_canvas_group_unrealize(EelCanvasItem * item)1426 eel_canvas_group_unrealize (EelCanvasItem *item)
1427 {
1428 	EelCanvasGroup *group;
1429 	GList *list;
1430 	EelCanvasItem *i;
1431 
1432 	group = EEL_CANVAS_GROUP (item);
1433 
1434 	/* Unmap group before children to avoid flash */
1435 	if (item->flags & EEL_CANVAS_ITEM_MAPPED)
1436 		(* EEL_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
1437 
1438 	for (list = group->item_list; list; list = list->next) {
1439 		i = list->data;
1440 
1441 		if (i->flags & EEL_CANVAS_ITEM_REALIZED)
1442 			(* EEL_CANVAS_ITEM_GET_CLASS (i)->unrealize) (i);
1443 	}
1444 
1445 	(* group_parent_class->unrealize) (item);
1446 }
1447 
1448 /* Map handler for canvas groups */
1449 static void
eel_canvas_group_map(EelCanvasItem * item)1450 eel_canvas_group_map (EelCanvasItem *item)
1451 {
1452 	EelCanvasGroup *group;
1453 	GList *list;
1454 	EelCanvasItem *i;
1455 
1456 	group = EEL_CANVAS_GROUP (item);
1457 
1458 	for (list = group->item_list; list; list = list->next) {
1459 		i = list->data;
1460 
1461 		if ((i->flags & EEL_CANVAS_ITEM_VISIBLE) &&
1462 		    !(i->flags & EEL_CANVAS_ITEM_MAPPED)) {
1463 			if (!(i->flags & EEL_CANVAS_ITEM_REALIZED))
1464 				(* EEL_CANVAS_ITEM_GET_CLASS (i)->realize) (i);
1465 
1466 			(* EEL_CANVAS_ITEM_GET_CLASS (i)->map) (i);
1467 		}
1468 	}
1469 
1470 	(* group_parent_class->map) (item);
1471 }
1472 
1473 /* Unmap handler for canvas groups */
1474 static void
eel_canvas_group_unmap(EelCanvasItem * item)1475 eel_canvas_group_unmap (EelCanvasItem *item)
1476 {
1477 	EelCanvasGroup *group;
1478 	GList *list;
1479 	EelCanvasItem *i;
1480 
1481 	group = EEL_CANVAS_GROUP (item);
1482 
1483 	for (list = group->item_list; list; list = list->next) {
1484 		i = list->data;
1485 
1486 		if (i->flags & EEL_CANVAS_ITEM_MAPPED)
1487 			(* EEL_CANVAS_ITEM_GET_CLASS (i)->unmap) (i);
1488 	}
1489 
1490 	(* group_parent_class->unmap) (item);
1491 }
1492 
1493 /* Draw handler for canvas groups */
1494 static void
eel_canvas_group_draw(EelCanvasItem * item,cairo_t * cr,cairo_region_t * region)1495 eel_canvas_group_draw (EelCanvasItem  *item,
1496                        cairo_t        *cr,
1497                        cairo_region_t *region)
1498 {
1499 	EelCanvasGroup *group;
1500 	GList *list;
1501 	EelCanvasItem *child = NULL;
1502 
1503 	group = EEL_CANVAS_GROUP (item);
1504 
1505 	for (list = group->item_list; list; list = list->next) {
1506 		child = list->data;
1507 
1508 		if ((child->flags & EEL_CANVAS_ITEM_MAPPED) &&
1509 		    (EEL_CANVAS_ITEM_GET_CLASS (child)->draw)) {
1510 			GdkRectangle child_rect;
1511 
1512 			child_rect.x = child->x1;
1513 			child_rect.y = child->y1;
1514 			child_rect.width = child->x2 - child->x1 + 1;
1515 			child_rect.height = child->y2 - child->y1 + 1;
1516 
1517 			if (cairo_region_contains_rectangle (region, &child_rect) != CAIRO_REGION_OVERLAP_OUT)
1518 				EEL_CANVAS_ITEM_GET_CLASS (child)->draw (child, cr, region);
1519 		}
1520 	}
1521 }
1522 
1523 /* Point handler for canvas groups */
1524 static double
eel_canvas_group_point(EelCanvasItem * item,double x,double y,int cx,int cy,EelCanvasItem ** actual_item)1525 eel_canvas_group_point (EelCanvasItem *item, double x, double y, int cx, int cy,
1526 			EelCanvasItem **actual_item)
1527 {
1528 	EelCanvasGroup *group;
1529 	GList *list;
1530 	EelCanvasItem *child, *point_item;
1531 	int x1, y1, x2, y2;
1532 	double gx, gy;
1533 	double dist, best;
1534 	int has_point;
1535 
1536 	group = EEL_CANVAS_GROUP (item);
1537 
1538 	x1 = cx - item->canvas->close_enough;
1539 	y1 = cy - item->canvas->close_enough;
1540 	x2 = cx + item->canvas->close_enough;
1541 	y2 = cy + item->canvas->close_enough;
1542 
1543 	best = 0.0;
1544 	*actual_item = NULL;
1545 
1546 	gx = x - group->xpos;
1547 	gy = y - group->ypos;
1548 
1549 	dist = 0.0; /* keep gcc happy */
1550 
1551 	for (list = group->item_list; list; list = list->next) {
1552 		child = list->data;
1553 
1554 		if ((child->x1 > x2) || (child->y1 > y2) || (child->x2 < x1) || (child->y2 < y1))
1555 			continue;
1556 
1557 		point_item = NULL; /* cater for incomplete item implementations */
1558 
1559 		if ((child->flags & EEL_CANVAS_ITEM_MAPPED)
1560 		    && EEL_CANVAS_ITEM_GET_CLASS (child)->point) {
1561 			dist = eel_canvas_item_invoke_point (child, gx, gy, cx, cy, &point_item);
1562 			has_point = TRUE;
1563 		} else
1564 			has_point = FALSE;
1565 
1566 		if (has_point
1567 		    && point_item
1568 		    && ((int) (dist * item->canvas->pixels_per_unit + 0.5)
1569 			<= item->canvas->close_enough)) {
1570 			best = dist;
1571 			*actual_item = point_item;
1572 		}
1573 	}
1574 
1575 	return best;
1576 }
1577 
1578 void
eel_canvas_group_translate(EelCanvasItem * item,double dx,double dy)1579 eel_canvas_group_translate (EelCanvasItem *item, double dx, double dy)
1580 {
1581         EelCanvasGroup *group;
1582 
1583         group = EEL_CANVAS_GROUP (item);
1584 
1585         group->xpos += dx;
1586         group->ypos += dy;
1587 }
1588 
1589 /* Bounds handler for canvas groups */
1590 static void
eel_canvas_group_bounds(EelCanvasItem * item,double * x1,double * y1,double * x2,double * y2)1591 eel_canvas_group_bounds (EelCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
1592 {
1593 	EelCanvasGroup *group;
1594 	EelCanvasItem *child;
1595 	GList *list;
1596 	double tx1, ty1, tx2, ty2;
1597 	double minx, miny, maxx, maxy;
1598 	int set;
1599 
1600 	group = EEL_CANVAS_GROUP (item);
1601 
1602 	/* Get the bounds of the first visible item */
1603 
1604 	child = NULL; /* Unnecessary but eliminates a warning. */
1605 
1606 	set = FALSE;
1607 
1608 	for (list = group->item_list; list; list = list->next) {
1609 		child = list->data;
1610 
1611 		if (child->flags & EEL_CANVAS_ITEM_MAPPED) {
1612 			set = TRUE;
1613 			eel_canvas_item_get_bounds (child, &minx, &miny, &maxx, &maxy);
1614 			break;
1615 		}
1616 	}
1617 
1618 	/* If there were no visible items, return an empty bounding box */
1619 
1620 	if (!set) {
1621 		*x1 = *y1 = *x2 = *y2 = 0.0;
1622 		return;
1623 	}
1624 
1625 	/* Now we can grow the bounds using the rest of the items */
1626 
1627 	list = list->next;
1628 
1629 	for (; list; list = list->next) {
1630 		child = list->data;
1631 
1632 		if (!(child->flags & EEL_CANVAS_ITEM_MAPPED))
1633 			continue;
1634 
1635 		eel_canvas_item_get_bounds (child, &tx1, &ty1, &tx2, &ty2);
1636 
1637 		if (tx1 < minx)
1638 			minx = tx1;
1639 
1640 		if (ty1 < miny)
1641 			miny = ty1;
1642 
1643 		if (tx2 > maxx)
1644 			maxx = tx2;
1645 
1646 		if (ty2 > maxy)
1647 			maxy = ty2;
1648 	}
1649 
1650 	/* Make the bounds be relative to our parent's coordinate system */
1651 
1652 	if (item->parent) {
1653 		minx += group->xpos;
1654 		miny += group->ypos;
1655 		maxx += group->xpos;
1656 		maxy += group->ypos;
1657 	}
1658 
1659 	*x1 = minx;
1660 	*y1 = miny;
1661 	*x2 = maxx;
1662 	*y2 = maxy;
1663 }
1664 
1665 /* Adds an item to a group */
1666 static void
group_add(EelCanvasGroup * group,EelCanvasItem * item)1667 group_add (EelCanvasGroup *group, EelCanvasItem *item)
1668 {
1669 	g_object_ref_sink (item);
1670 
1671 /* FIXME: relatively inefficient way to add to the list, appending to the list causes it to be read from the top each time.
1672  	prepend, followed by a reverse if necessary is the recommended approach.  However as this list append is a number of
1673   	levels of call down, correcting it is not as easy as it could be */
1674 
1675 	if (!group->item_list) {
1676 		group->item_list = g_list_append (group->item_list, item);
1677 		group->item_list_end = group->item_list;
1678 	} else
1679 		group->item_list_end = g_list_append (group->item_list_end, item)->next;
1680 
1681 	if ((item->flags & EEL_CANVAS_ITEM_VISIBLE) &&
1682 	    (group->item.flags & EEL_CANVAS_ITEM_MAPPED)) {
1683 		if (!(item->flags & EEL_CANVAS_ITEM_REALIZED))
1684 			(* EEL_CANVAS_ITEM_GET_CLASS (item)->realize) (item);
1685 
1686 		if (!(item->flags & EEL_CANVAS_ITEM_MAPPED))
1687 			(* EEL_CANVAS_ITEM_GET_CLASS (item)->map) (item);
1688 	}
1689 
1690 	if (item->flags & EEL_CANVAS_ITEM_VISIBLE)
1691 		eel_canvas_queue_resize (EEL_CANVAS_ITEM (group)->canvas);
1692 }
1693 
1694 /* Removes an item from a group */
1695 static void
group_remove(EelCanvasGroup * group,EelCanvasItem * item)1696 group_remove (EelCanvasGroup *group, EelCanvasItem *item)
1697 {
1698 	GList *children;
1699 
1700 	g_return_if_fail (EEL_IS_CANVAS_GROUP (group));
1701 	g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
1702 
1703 	for (children = group->item_list; children; children = children->next)
1704 		if (children->data == item) {
1705 			if (item->flags & EEL_CANVAS_ITEM_MAPPED) {
1706 				(* EEL_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
1707 			}
1708 
1709 			if (item->flags & EEL_CANVAS_ITEM_REALIZED)
1710 				(* EEL_CANVAS_ITEM_GET_CLASS (item)->unrealize) (item);
1711 
1712 			if (item->flags & EEL_CANVAS_ITEM_VISIBLE)
1713 				eel_canvas_queue_resize (item->canvas);
1714 
1715 			/* Unparent the child */
1716 
1717 			item->parent = NULL;
1718 			/* item->canvas = NULL; */
1719 			g_object_unref (G_OBJECT (item));
1720 
1721 			/* Remove it from the list */
1722 
1723 			if (children == group->item_list_end)
1724 				group->item_list_end = children->prev;
1725 
1726 			group->item_list = g_list_remove_link (group->item_list, children);
1727 			g_list_free (children);
1728 			break;
1729 		}
1730 }
1731 
1732 
1733 /*** EelCanvas ***/
1734 
1735 
1736 enum {
1737 	DRAW_BACKGROUND,
1738 	LAST_SIGNAL
1739 };
1740 
1741 static void eel_canvas_class_init          (EelCanvasClass *klass);
1742 static void eel_canvas_init                (EelCanvas      *canvas);
1743 static void eel_canvas_destroy             (GtkWidget        *object);
1744 static void eel_canvas_map                 (GtkWidget        *widget);
1745 static void eel_canvas_unmap               (GtkWidget        *widget);
1746 static void eel_canvas_realize             (GtkWidget        *widget);
1747 static void eel_canvas_unrealize           (GtkWidget        *widget);
1748 static void eel_canvas_size_allocate       (GtkWidget        *widget,
1749 					    GtkAllocation    *allocation);
1750 static gint eel_canvas_button              (GtkWidget        *widget,
1751 					    GdkEventButton   *event);
1752 static gint eel_canvas_motion              (GtkWidget        *widget,
1753 					    GdkEventMotion   *event);
1754 static gint eel_canvas_draw                (GtkWidget        *widget,
1755                                             cairo_t          *cr);
1756 static gint eel_canvas_key                 (GtkWidget        *widget,
1757 					    GdkEventKey      *event);
1758 static gint eel_canvas_crossing            (GtkWidget        *widget,
1759 					    GdkEventCrossing *event);
1760 static gint eel_canvas_focus_in            (GtkWidget        *widget,
1761 					    GdkEventFocus    *event);
1762 static gint eel_canvas_focus_out           (GtkWidget        *widget,
1763 					    GdkEventFocus    *event);
1764 static void eel_canvas_request_update_real (EelCanvas      *canvas);
1765 static void eel_canvas_draw_background     (EelCanvas      *canvas,
1766                                             cairo_t        *cr);
1767 static AtkObject *eel_canvas_get_accessible (GtkWidget       *widget);
1768 
1769 
1770 static GtkLayoutClass *canvas_parent_class;
1771 
1772 static guint canvas_signals[LAST_SIGNAL] = { 0 };
1773 
1774 /**
1775  * eel_canvas_get_type:
1776  *
1777  * Registers the &EelCanvas class if necessary, and returns the type ID
1778  * associated to it.
1779  *
1780  * Return value:  The type ID of the &EelCanvas class.
1781  **/
1782 GType
eel_canvas_get_type(void)1783 eel_canvas_get_type (void)
1784 {
1785 	static GType canvas_type = 0;
1786 
1787 	if (!canvas_type) {
1788 		static const GTypeInfo canvas_info = {
1789 			sizeof (EelCanvasClass),
1790 			(GBaseInitFunc) NULL,
1791 			(GBaseFinalizeFunc) NULL,
1792 			(GClassInitFunc) eel_canvas_class_init,
1793 			NULL,           /* class_finalize */
1794 			NULL,           /* class_data */
1795 			sizeof (EelCanvas),
1796 			0,              /* n_preallocs */
1797 			(GInstanceInitFunc) eel_canvas_init
1798 		};
1799 
1800 		canvas_type = g_type_register_static (gtk_layout_get_type (),
1801 						      "EelCanvas",
1802 						      &canvas_info,
1803 						      0);
1804 	}
1805 
1806 	return canvas_type;
1807 }
1808 
1809 static void
eel_canvas_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1810 eel_canvas_get_property (GObject    *object,
1811 			   guint       prop_id,
1812 			   GValue     *value,
1813 			   GParamSpec *pspec)
1814 {
1815 	switch (prop_id) {
1816 	default:
1817 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1818 		break;
1819 	}
1820 }
1821 
1822 static void
eel_canvas_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1823 eel_canvas_set_property (GObject      *object,
1824 			   guint         prop_id,
1825 			   const GValue *value,
1826 			   GParamSpec   *pspec)
1827 {
1828 	switch (prop_id) {
1829 	default:
1830 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1831 		break;
1832 	}
1833 }
1834 
1835 static void
eel_canvas_accessible_adjustment_changed(GtkAdjustment * adjustment,AtkObject * obj)1836 eel_canvas_accessible_adjustment_changed (GtkAdjustment *adjustment,
1837 		AtkObject *obj)
1838 {
1839 	/* The scrollbars have changed */
1840 
1841 	g_signal_emit_by_name (obj, "visible_data_changed");
1842 }
1843 
1844 static void
accessible_destroy_cb(GtkWidget * widget,AtkObject * obj)1845 accessible_destroy_cb (GtkWidget *widget,
1846 		AtkObject *obj)
1847 {
1848 	gtk_accessible_set_widget (GTK_ACCESSIBLE(obj), NULL);
1849 	atk_object_notify_state_change (obj, ATK_STATE_DEFUNCT, TRUE);
1850 }
1851 
1852 static gboolean
accessible_focus_cb(GtkWidget * widget,GdkEventFocus * event)1853 accessible_focus_cb (GtkWidget     *widget,
1854 		     GdkEventFocus *event)
1855 {
1856 	atk_object_notify_state_change ((AtkObject *)gtk_widget_get_accessible (widget), ATK_STATE_FOCUSED, event->in);
1857 
1858 	return FALSE;
1859 }
1860 
1861 static void
accessible_notify_cb(GObject * obj,GParamSpec * pspec)1862 accessible_notify_cb (GObject    *obj,
1863 		      GParamSpec *pspec)
1864 {
1865 	GtkWidget* widget = GTK_WIDGET (obj);
1866 	AtkObject* atk_obj = gtk_widget_get_accessible (widget);
1867 	AtkState state;
1868 	gboolean value;
1869 
1870 	if (strcmp (pspec->name, "visible") == 0) {
1871 		state = ATK_STATE_VISIBLE;
1872 		value = gtk_widget_get_visible (widget);
1873 	} else if (strcmp (pspec->name, "sensitive") == 0) {
1874 		state = ATK_STATE_SENSITIVE;
1875 		value = gtk_widget_get_sensitive (widget);
1876 
1877 		atk_object_notify_state_change (atk_obj, ATK_STATE_ENABLED, value);
1878 	} else {
1879 		g_assert_not_reached ();
1880 	}
1881 
1882 	atk_object_notify_state_change (atk_obj, state, value);
1883 }
1884 
1885 /* Translate GtkWidget::size-allocate to AtkComponent::bounds-changed */
1886 static void
accessible_size_allocate_cb(GtkWidget * widget,GtkAllocation * allocation)1887 accessible_size_allocate_cb (GtkWidget     *widget,
1888 			     GtkAllocation *allocation)
1889 {
1890 	AtkObject* accessible = gtk_widget_get_accessible (widget);
1891 	AtkRectangle rect;
1892 
1893 	rect.x = allocation->x;
1894 	rect.y = allocation->y;
1895 	rect.width = allocation->width;
1896 	rect.height = allocation->height;
1897 
1898 	g_signal_emit_by_name (accessible, "bounds_changed", &rect);
1899 }
1900 
1901 /* Translate GtkWidget mapped state into AtkObject showing */
1902 static void
accessible_map_cb(GtkWidget * widget)1903 accessible_map_cb (GtkWidget *widget)
1904 {
1905 	AtkObject *accessible = gtk_widget_get_accessible (widget);
1906 	atk_object_notify_state_change (accessible, ATK_STATE_SHOWING,
1907 	                                gtk_widget_get_mapped (widget));
1908 }
1909 
1910 static void
eel_canvas_accessible_initialize(AtkObject * obj,gpointer data)1911 eel_canvas_accessible_initialize (AtkObject *obj,
1912 				  gpointer   data)
1913 {
1914 	EelCanvas *canvas = data;
1915 
1916 	if (ATK_OBJECT_CLASS (accessible_parent_class)->initialize != NULL) {
1917 		ATK_OBJECT_CLASS (accessible_parent_class)->initialize (obj, data);
1918 	}
1919 
1920 	gtk_accessible_set_widget (GTK_ACCESSIBLE (obj), GTK_WIDGET (data));
1921 	g_signal_connect (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (canvas)),
1922 			  "value_changed",
1923 			  G_CALLBACK (eel_canvas_accessible_adjustment_changed),
1924 			  obj);
1925 	g_signal_connect (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (canvas)),
1926 			  "value_changed",
1927 			  G_CALLBACK (eel_canvas_accessible_adjustment_changed),
1928 			  obj);
1929 
1930 	obj->role = ATK_ROLE_LAYERED_PANE;
1931 
1932 	/* below adapted from gtkwidgetaccessible.c */
1933 
1934 	g_signal_connect_after (canvas, "destroy",
1935 				G_CALLBACK (accessible_destroy_cb), obj);
1936 	g_signal_connect_after (canvas, "focus-in-event",
1937 				G_CALLBACK (accessible_focus_cb), NULL);
1938 	g_signal_connect_after (canvas, "focus-out-event",
1939 				G_CALLBACK (accessible_focus_cb), NULL);
1940 	g_signal_connect (canvas, "notify::visible",
1941 			  G_CALLBACK (accessible_notify_cb), NULL);
1942 	g_signal_connect (canvas, "notify::sensitive",
1943 			  G_CALLBACK (accessible_notify_cb), NULL);
1944 	g_signal_connect (canvas, "size-allocate",
1945 			  G_CALLBACK (accessible_size_allocate_cb), NULL);
1946 	g_signal_connect (canvas, "map",
1947 			  G_CALLBACK (accessible_map_cb), NULL);
1948 	g_signal_connect (canvas, "unmap",
1949 			  G_CALLBACK (accessible_map_cb), NULL);
1950 }
1951 
1952 static gint
eel_canvas_accessible_get_n_children(AtkObject * obj)1953 eel_canvas_accessible_get_n_children (AtkObject* obj)
1954 {
1955 	GtkWidget *widget;
1956 	EelCanvas *canvas;
1957 	EelCanvasGroup *root_group;
1958 
1959 	widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
1960 
1961 	if (widget == NULL) {
1962 		return 0;
1963 	}
1964 
1965 	g_return_val_if_fail (EEL_IS_CANVAS (widget), 0);
1966 
1967 	canvas = EEL_CANVAS (widget);
1968 	root_group = eel_canvas_root (canvas);
1969 	g_return_val_if_fail (root_group, 0);
1970 
1971 	return 1;
1972 }
1973 
1974 static AtkObject*
eel_canvas_accessible_ref_child(AtkObject * obj,gint i)1975 eel_canvas_accessible_ref_child (AtkObject *obj,
1976                                  gint       i)
1977 {
1978 	GtkWidget *widget;
1979 	EelCanvas *canvas;
1980 	EelCanvasGroup *root_group;
1981 	AtkObject *atk_object;
1982 
1983 	/* Canvas only has one child, so return NULL if index is non zero */
1984 	if (i != 0) {
1985         	return NULL;
1986 	}
1987 
1988 	widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
1989 
1990 	if (widget == NULL) {
1991 		return NULL;
1992 	}
1993 
1994 	canvas = EEL_CANVAS (widget);
1995 	root_group = eel_canvas_root (canvas);
1996 	g_return_val_if_fail (root_group, NULL);
1997 
1998 	atk_object = atk_gobject_accessible_for_object (G_OBJECT (root_group));
1999 
2000 	return g_object_ref (atk_object);
2001 }
2002 
2003 static gboolean
eel_canvas_accessible_all_parents_visible(GtkWidget * widget)2004 eel_canvas_accessible_all_parents_visible (GtkWidget *widget)
2005 {
2006 	GtkWidget *iter_parent = NULL;
2007 	gboolean result = TRUE;
2008 
2009 	for (iter_parent = gtk_widget_get_parent (widget); iter_parent != NULL;
2010 	     iter_parent = gtk_widget_get_parent (iter_parent)) {
2011 		if (!gtk_widget_get_visible (iter_parent)) {
2012 			result = FALSE;
2013 			break;
2014 		}
2015 	}
2016 
2017 	return result;
2018 }
2019 
2020 static gboolean
eel_canvas_accessible_on_screen(GtkWidget * widget)2021 eel_canvas_accessible_on_screen (GtkWidget *widget)
2022 {
2023 	GtkAllocation allocation;
2024 	GtkWidget *viewport;
2025 	gboolean return_value = TRUE;
2026 
2027 	gtk_widget_get_allocation (widget, &allocation);
2028 
2029 	viewport = gtk_widget_get_ancestor (widget, GTK_TYPE_VIEWPORT);
2030 
2031 	if (viewport) {
2032 		GtkAllocation viewport_allocation;
2033 		GtkAdjustment *adjustment;
2034 		GdkRectangle visible_rect;
2035 
2036 		gtk_widget_get_allocation (viewport, &viewport_allocation);
2037 
2038 		adjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (viewport));
2039 		visible_rect.y = gtk_adjustment_get_value (adjustment);
2040 		adjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (viewport));
2041 		visible_rect.x = gtk_adjustment_get_value (adjustment);
2042 		visible_rect.width = viewport_allocation.width;
2043 		visible_rect.height = viewport_allocation.height;
2044 
2045 		if (((allocation.x + allocation.width) < visible_rect.x) ||
2046 		    ((allocation.y + allocation.height) < visible_rect.y) ||
2047 		    (allocation.x > (visible_rect.x + visible_rect.width)) ||
2048 		    (allocation.y > (visible_rect.y + visible_rect.height))) {
2049 			return_value = FALSE;
2050 		}
2051 	} else {
2052 		/* Check whether the widget has been placed off the screen.
2053 		 * The widget may be MAPPED as when toolbar items do not
2054 		 * fit on the toolbar.
2055 		 */
2056 		if (allocation.x + allocation.width <= 0 &&
2057 		    allocation.y + allocation.height <= 0) {
2058 			return_value = FALSE;
2059 		}
2060 	}
2061 
2062 	return return_value;
2063 }
2064 
2065 static AtkStateSet *
eel_canvas_accessible_ref_state_set(AtkObject * accessible)2066 eel_canvas_accessible_ref_state_set (AtkObject *accessible)
2067 {
2068 	GtkWidget *widget;
2069 	AtkStateSet *state_set;
2070 
2071 	widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
2072 	state_set = ATK_OBJECT_CLASS (accessible_parent_class)->ref_state_set (accessible);
2073 
2074 	if (widget == NULL) {
2075 		atk_state_set_add_state (state_set, ATK_STATE_DEFUNCT);
2076 	} else {
2077 		if (gtk_widget_is_sensitive (widget)) {
2078 			atk_state_set_add_state (state_set, ATK_STATE_SENSITIVE);
2079 			atk_state_set_add_state (state_set, ATK_STATE_ENABLED);
2080 		}
2081 
2082 		if (gtk_widget_get_can_focus (widget)) {
2083 			atk_state_set_add_state (state_set, ATK_STATE_FOCUSABLE);
2084 		}
2085 		/*
2086 		 * We do not currently generate notifications when an ATK object
2087 		 * corresponding to a GtkWidget changes visibility by being scrolled
2088 		 * on or off the screen.  The testcase for this is the main window
2089 		 * of the testgtk application in which a set of buttons in a GtkVBox
2090 		 * is in a scrolled window with a viewport.
2091 		 *
2092 		 * To generate the notifications we would need to do the following:
2093 		 * 1) Find the GtkViewport among the ancestors of the objects
2094 		 * 2) Create an accessible for the viewport
2095 		 * 3) Connect to the value-changed signal on the viewport
2096 		 * 4) When the signal is received we need to traverse the children
2097 		 *    of the viewport and check whether the children are visible or not
2098 		 *    visible; we may want to restrict this to the widgets for which
2099 		 *    accessible objects have been created.
2100 		 * 5) We probably need to store a variable on_screen in the
2101 		 *    GtkWidgetAccessible data structure so we can determine whether
2102 		 *    the value has changed.
2103 		 */
2104 		if (gtk_widget_get_visible (widget)) {
2105 			atk_state_set_add_state (state_set, ATK_STATE_VISIBLE);
2106 
2107 			if (eel_canvas_accessible_on_screen (widget) &&
2108 			    gtk_widget_get_mapped (widget) &&
2109 			    eel_canvas_accessible_all_parents_visible (widget)) {
2110 				atk_state_set_add_state (state_set, ATK_STATE_SHOWING);
2111 			}
2112 		}
2113 
2114 		if (gtk_widget_has_focus (widget)) {
2115 			if (g_object_get_data (G_OBJECT (accessible), "gail-focus-object") == NULL) {
2116 				atk_state_set_add_state (state_set, ATK_STATE_FOCUSED);
2117 			}
2118 		}
2119 
2120 		if (gtk_widget_has_default (widget)) {
2121 			atk_state_set_add_state (state_set, ATK_STATE_DEFAULT);
2122 		}
2123 	}
2124 	return state_set;
2125 }
2126 
2127 static void
eel_canvas_accessible_get_extents(AtkComponent * component,gint * x,gint * y,gint * width,gint * height,AtkCoordType coord_type)2128 eel_canvas_accessible_get_extents (AtkComponent   *component,
2129                                    gint           *x,
2130                                    gint           *y,
2131                                    gint           *width,
2132                                    gint           *height,
2133                                    AtkCoordType    coord_type)
2134 {
2135 	GdkWindow *window;
2136 	gint x_window, y_window;
2137 	gint x_toplevel, y_toplevel;
2138 	GtkWidget *widget;
2139 	GtkAllocation allocation;
2140 
2141 	widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
2142 
2143 	if (widget == NULL) {
2144 		return;
2145 	}
2146 
2147 	gtk_widget_get_allocation (widget, &allocation);
2148 	*width = allocation.width;
2149 	*height = allocation.height;
2150 
2151 	if (!eel_canvas_accessible_on_screen (widget) ||
2152 	    !gtk_widget_is_drawable (widget)) {
2153 		*x = G_MININT;
2154 		*y = G_MININT;
2155 
2156 		return;
2157 	}
2158 
2159 	if (gtk_widget_get_parent (widget)) {
2160 		*x = allocation.x;
2161 		*y = allocation.y;
2162 		window = gtk_widget_get_parent_window (widget);
2163 	} else {
2164 		*x = 0;
2165 		*y = 0;
2166 		window = gtk_widget_get_window (widget);
2167 	}
2168 
2169 	gdk_window_get_origin (window, &x_window, &y_window);
2170 	*x += x_window;
2171 	*y += y_window;
2172 
2173 	if (coord_type == ATK_XY_WINDOW) {
2174 		window = gdk_window_get_toplevel (gtk_widget_get_window (widget));
2175 		gdk_window_get_origin (window, &x_toplevel, &y_toplevel);
2176 
2177 		*x -= x_toplevel;
2178 		*y -= y_toplevel;
2179 	}
2180 }
2181 
2182 static void
eel_canvas_accessible_get_size(AtkComponent * component,gint * width,gint * height)2183 eel_canvas_accessible_get_size (AtkComponent *component,
2184                                 gint         *width,
2185                                 gint         *height)
2186 {
2187 	GtkWidget *widget;
2188 
2189 	widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
2190 
2191 	if (widget == NULL) {
2192 	  return;
2193 	}
2194 
2195 	*width = gtk_widget_get_allocated_width (widget);
2196 	*height = gtk_widget_get_allocated_height (widget);
2197 }
2198 
2199 static void
eel_canvas_accessible_component_init(gpointer iface,gpointer data)2200 eel_canvas_accessible_component_init (gpointer iface, gpointer data)
2201 {
2202 	AtkComponentIface *component;
2203 
2204 	g_assert (G_TYPE_FROM_INTERFACE(iface) == ATK_TYPE_COMPONENT);
2205 
2206 	component = iface;
2207 	component->get_extents = eel_canvas_accessible_get_extents;
2208 	component->get_size = eel_canvas_accessible_get_size;
2209 }
2210 
G_DEFINE_TYPE_WITH_CODE(EelCanvasAccessible,eel_canvas_accessible,GTK_TYPE_ACCESSIBLE,G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT,eel_canvas_accessible_component_init))2211 G_DEFINE_TYPE_WITH_CODE (EelCanvasAccessible, eel_canvas_accessible, GTK_TYPE_ACCESSIBLE,
2212 			 G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT, eel_canvas_accessible_component_init))
2213 
2214 static void
2215 eel_canvas_accessible_class_init (EelCanvasAccessibleClass *klass)
2216 {
2217 	AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass);
2218 
2219  	accessible_parent_class = g_type_class_peek_parent (klass);
2220 
2221 	atk_class->initialize = eel_canvas_accessible_initialize;
2222 	atk_class->get_n_children = eel_canvas_accessible_get_n_children;
2223 	atk_class->ref_child = eel_canvas_accessible_ref_child;
2224 	/* below adapted from gtkwidgetaccessible.c */
2225 	atk_class->ref_state_set = eel_canvas_accessible_ref_state_set;
2226 }
2227 
2228 static void
eel_canvas_accessible_init(EelCanvasAccessible * accessible)2229 eel_canvas_accessible_init (EelCanvasAccessible *accessible)
2230 {
2231 }
2232 
2233 static AtkObject *
eel_canvas_accessible_create(GObject * for_object)2234 eel_canvas_accessible_create (GObject *for_object)
2235 {
2236 	GType type;
2237 	AtkObject *accessible;
2238 	EelCanvas *canvas;
2239 
2240 	canvas = EEL_CANVAS (for_object);
2241 	g_return_val_if_fail (canvas != NULL, NULL);
2242 
2243 	type = eel_canvas_accessible_get_type ();
2244 
2245 	if (type == G_TYPE_INVALID) {
2246 		return atk_no_op_object_new (for_object);
2247 	}
2248 
2249 	accessible = g_object_new (type, NULL);
2250 	atk_object_initialize (accessible, for_object);
2251 	return accessible;
2252 }
2253 
2254 static GType
eel_canvas_accessible_factory_get_accessible_type(void)2255 eel_canvas_accessible_factory_get_accessible_type (void)
2256 {
2257 	return eel_canvas_accessible_get_type ();
2258 }
2259 
2260 static AtkObject*
eel_canvas_accessible_factory_create_accessible(GObject * obj)2261 eel_canvas_accessible_factory_create_accessible (GObject *obj)
2262 {
2263 	g_return_val_if_fail (G_IS_OBJECT (obj), NULL);
2264 
2265 	return eel_canvas_accessible_create (obj);
2266 }
2267 
2268 static void
eel_canvas_accessible_factory_class_init(AtkObjectFactoryClass * klass)2269 eel_canvas_accessible_factory_class_init (AtkObjectFactoryClass *klass)
2270 {
2271 	klass->create_accessible = eel_canvas_accessible_factory_create_accessible;
2272 	klass->get_accessible_type = eel_canvas_accessible_factory_get_accessible_type;
2273 }
2274 
2275 static GType
eel_canvas_accessible_factory_get_type(void)2276 eel_canvas_accessible_factory_get_type (void)
2277 {
2278 	static GType type = 0;
2279 
2280 	if (!type) {
2281 		static const GTypeInfo tinfo = {
2282 			sizeof (AtkObjectFactoryClass),
2283 			(GBaseInitFunc) NULL,
2284 			(GBaseFinalizeFunc) NULL,
2285 			(GClassInitFunc) eel_canvas_accessible_factory_class_init,
2286 			NULL,		/* class_finalize */
2287 			NULL,		/* class_data */
2288 			sizeof (AtkObjectFactory),
2289 			0,		/* n_preallocs */
2290 			NULL
2291 		};
2292 		type = g_type_register_static (ATK_TYPE_OBJECT_FACTORY,
2293 					       "EelCanvasAccessibilityFactory",
2294 					       &tinfo, 0);
2295 	}
2296 	return type;
2297 }
2298 
2299 
2300 /* Class initialization function for EelCanvasClass */
2301 static void
eel_canvas_class_init(EelCanvasClass * klass)2302 eel_canvas_class_init (EelCanvasClass *klass)
2303 {
2304 	GObjectClass   *gobject_class;
2305 	GtkWidgetClass *widget_class;
2306 
2307 	gobject_class = (GObjectClass *)klass;
2308 	widget_class  = (GtkWidgetClass *) klass;
2309 
2310 	canvas_parent_class = g_type_class_peek_parent (klass);
2311 
2312 	gobject_class->set_property = eel_canvas_set_property;
2313 	gobject_class->get_property = eel_canvas_get_property;
2314 
2315 	widget_class->destroy = eel_canvas_destroy;
2316 	widget_class->map = eel_canvas_map;
2317 	widget_class->unmap = eel_canvas_unmap;
2318 	widget_class->realize = eel_canvas_realize;
2319 	widget_class->unrealize = eel_canvas_unrealize;
2320 	widget_class->size_allocate = eel_canvas_size_allocate;
2321 	widget_class->button_press_event = eel_canvas_button;
2322 	widget_class->button_release_event = eel_canvas_button;
2323 	widget_class->motion_notify_event = eel_canvas_motion;
2324 	widget_class->draw = eel_canvas_draw;
2325 	widget_class->key_press_event = eel_canvas_key;
2326 	widget_class->key_release_event = eel_canvas_key;
2327 	widget_class->enter_notify_event = eel_canvas_crossing;
2328 	widget_class->leave_notify_event = eel_canvas_crossing;
2329 	widget_class->focus_in_event = eel_canvas_focus_in;
2330 	widget_class->focus_out_event = eel_canvas_focus_out;
2331 	widget_class->get_accessible = eel_canvas_get_accessible;
2332 
2333 	klass->draw_background = eel_canvas_draw_background;
2334 	klass->request_update = eel_canvas_request_update_real;
2335 
2336 	canvas_signals[DRAW_BACKGROUND] =
2337 		g_signal_new ("draw_background",
2338 			      G_TYPE_FROM_CLASS (klass),
2339 			      G_SIGNAL_RUN_LAST,
2340 			      G_STRUCT_OFFSET (EelCanvasClass, draw_background),
2341 			      NULL, NULL,
2342                               g_cclosure_marshal_VOID__BOXED,
2343 			      G_TYPE_NONE, 1,
2344                               CAIRO_GOBJECT_TYPE_CONTEXT);
2345 
2346 	atk_registry_set_factory_type (atk_get_default_registry (),
2347 				       EEL_TYPE_CANVAS,
2348 				       eel_canvas_accessible_factory_get_type ());
2349 }
2350 
2351 /* Callback used when the root item of a canvas is destroyed.  The user should
2352  * never ever do this, so we panic if this happens.
2353  */
2354 static void
panic_root_destroyed(GtkWidget * object,gpointer data)2355 panic_root_destroyed (GtkWidget *object, gpointer data)
2356 {
2357 	g_error ("Eeeek, root item %p of canvas %p was destroyed!", object, data);
2358 }
2359 
2360 /* Object initialization function for EelCanvas */
2361 static void
eel_canvas_init(EelCanvas * canvas)2362 eel_canvas_init (EelCanvas *canvas)
2363 {
2364 	guint width, height;
2365 	gtk_widget_set_can_focus (GTK_WIDGET (canvas), TRUE);
2366 
2367 	gtk_widget_set_redraw_on_allocate (GTK_WIDGET (canvas), FALSE);
2368 
2369 	canvas->scroll_x1 = 0.0;
2370 	canvas->scroll_y1 = 0.0;
2371 	gtk_layout_get_size (GTK_LAYOUT (canvas),
2372 			     &width, &height);
2373 	canvas->scroll_x2 = width;
2374 	canvas->scroll_y2 = height;
2375 
2376 	canvas->pixels_per_unit = 1.0;
2377 
2378 	canvas->pick_event.type = GDK_LEAVE_NOTIFY;
2379 	canvas->pick_event.crossing.x = 0;
2380 	canvas->pick_event.crossing.y = 0;
2381 
2382 	gtk_scrollable_set_hadjustment (GTK_SCROLLABLE (canvas), NULL);
2383 	gtk_scrollable_set_vadjustment (GTK_SCROLLABLE (canvas), NULL);
2384 
2385 	/* Create the root item as a special case */
2386 
2387 	canvas->root = EEL_CANVAS_ITEM (g_object_new (eel_canvas_group_get_type (), NULL));
2388 	canvas->root->canvas = canvas;
2389 
2390 	g_object_ref_sink (canvas->root);
2391 
2392 	canvas->root_destroy_id = g_signal_connect (G_OBJECT (canvas->root),
2393 		"destroy", G_CALLBACK (panic_root_destroyed), canvas);
2394 
2395 	canvas->need_repick = TRUE;
2396 	canvas->doing_update = FALSE;
2397 }
2398 
2399 /* Convenience function to remove the idle handler of a canvas */
2400 static void
remove_idle(EelCanvas * canvas)2401 remove_idle (EelCanvas *canvas)
2402 {
2403 	if (canvas->idle_id == 0)
2404 		return;
2405 
2406 	g_source_remove (canvas->idle_id);
2407 	canvas->idle_id = 0;
2408 }
2409 
2410 /* Removes the transient state of the canvas (idle handler, grabs). */
2411 static void
shutdown_transients(EelCanvas * canvas)2412 shutdown_transients (EelCanvas *canvas)
2413 {
2414 	/* We turn off the need_redraw flag, since if the canvas is mapped again
2415 	 * it will request a redraw anyways.  We do not turn off the need_update
2416 	 * flag, though, because updates are not queued when the canvas remaps
2417 	 * itself.
2418 	 */
2419 	if (canvas->need_redraw) {
2420 		canvas->need_redraw = FALSE;
2421 	}
2422 
2423 	if (canvas->grabbed_item) {
2424 		eel_canvas_item_ungrab (canvas->grabbed_item, GDK_CURRENT_TIME);
2425 	}
2426 
2427 	remove_idle (canvas);
2428 }
2429 
2430 /* Destroy handler for EelCanvas */
2431 static void
eel_canvas_destroy(GtkWidget * object)2432 eel_canvas_destroy (GtkWidget *object)
2433 {
2434 	EelCanvas *canvas;
2435 
2436 	g_return_if_fail (EEL_IS_CANVAS (object));
2437 
2438 	/* remember, destroy can be run multiple times! */
2439 
2440 	canvas = EEL_CANVAS (object);
2441 
2442 	if (canvas->root_destroy_id) {
2443 		g_signal_handler_disconnect (G_OBJECT (canvas->root), canvas->root_destroy_id);
2444 		canvas->root_destroy_id = 0;
2445 	}
2446 	if (canvas->root) {
2447 		EelCanvasItem *root = canvas->root;
2448 		canvas->root = NULL;
2449 		eel_canvas_item_destroy (root);
2450 		g_object_unref (root);
2451 	}
2452 
2453 	shutdown_transients (canvas);
2454 
2455 	if (GTK_WIDGET_CLASS (canvas_parent_class)->destroy)
2456 		(* GTK_WIDGET_CLASS (canvas_parent_class)->destroy) (object);
2457 }
2458 
2459 /**
2460  * eel_canvas_new:
2461  * @void:
2462  *
2463  * Creates a new empty canvas.  If you wish to use the
2464  * &EelCanvasImage item inside this canvas, then you must push the gdk_imlib
2465  * visual and colormap before calling this function, and they can be popped
2466  * afterwards.
2467  *
2468  * Return value: A newly-created canvas.
2469  **/
2470 GtkWidget *
eel_canvas_new(void)2471 eel_canvas_new (void)
2472 {
2473 	return GTK_WIDGET (g_object_new (eel_canvas_get_type (), NULL));
2474 }
2475 
2476 /* Map handler for the canvas */
2477 static void
eel_canvas_map(GtkWidget * widget)2478 eel_canvas_map (GtkWidget *widget)
2479 {
2480 	EelCanvas *canvas;
2481 
2482 	g_return_if_fail (EEL_IS_CANVAS (widget));
2483 
2484 	/* Normal widget mapping stuff */
2485 
2486 	if (GTK_WIDGET_CLASS (canvas_parent_class)->map)
2487 		(* GTK_WIDGET_CLASS (canvas_parent_class)->map) (widget);
2488 
2489 	canvas = EEL_CANVAS (widget);
2490 
2491 	/* Map items */
2492 
2493 	if ((canvas->root->flags & EEL_CANVAS_ITEM_VISIBLE) &&
2494 	    !(canvas->root->flags & EEL_CANVAS_ITEM_MAPPED) &&
2495 	    EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->map)
2496 		(* EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->map) (canvas->root);
2497 }
2498 
2499 /* Unmap handler for the canvas */
2500 static void
eel_canvas_unmap(GtkWidget * widget)2501 eel_canvas_unmap (GtkWidget *widget)
2502 {
2503 	EelCanvas *canvas;
2504 
2505 	g_return_if_fail (EEL_IS_CANVAS (widget));
2506 
2507 	canvas = EEL_CANVAS (widget);
2508 
2509 	shutdown_transients (canvas);
2510 
2511 	/* Unmap items */
2512 
2513 	if (EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->unmap)
2514 		(* EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->unmap) (canvas->root);
2515 
2516 	/* Normal widget unmapping stuff */
2517 
2518 	if (GTK_WIDGET_CLASS (canvas_parent_class)->unmap)
2519 		(* GTK_WIDGET_CLASS (canvas_parent_class)->unmap) (widget);
2520 }
2521 
2522 /* Realize handler for the canvas */
2523 static void
eel_canvas_realize(GtkWidget * widget)2524 eel_canvas_realize (GtkWidget *widget)
2525 {
2526 	EelCanvas *canvas;
2527 
2528 	g_return_if_fail (EEL_IS_CANVAS (widget));
2529 
2530 	/* Normal widget realization stuff */
2531 
2532 	if (GTK_WIDGET_CLASS (canvas_parent_class)->realize)
2533 		(* GTK_WIDGET_CLASS (canvas_parent_class)->realize) (widget);
2534 
2535 	canvas = EEL_CANVAS (widget);
2536 
2537 	gdk_window_set_events (gtk_layout_get_bin_window (GTK_LAYOUT (canvas)),
2538 			       (gdk_window_get_events (gtk_layout_get_bin_window (GTK_LAYOUT (canvas)))
2539 				 | GDK_EXPOSURE_MASK
2540 				 | GDK_BUTTON_PRESS_MASK
2541 				 | GDK_BUTTON_RELEASE_MASK
2542 				 | GDK_POINTER_MOTION_MASK
2543 				 | GDK_KEY_PRESS_MASK
2544 				 | GDK_KEY_RELEASE_MASK
2545 				 | GDK_ENTER_NOTIFY_MASK
2546 				 | GDK_LEAVE_NOTIFY_MASK
2547 				 | GDK_FOCUS_CHANGE_MASK));
2548 
2549 	/* Create our own temporary pixmap gc and realize all the items */
2550 
2551 	(* EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->realize) (canvas->root);
2552 }
2553 
2554 /* Unrealize handler for the canvas */
2555 static void
eel_canvas_unrealize(GtkWidget * widget)2556 eel_canvas_unrealize (GtkWidget *widget)
2557 {
2558 	EelCanvas *canvas;
2559 
2560 	g_return_if_fail (EEL_IS_CANVAS (widget));
2561 
2562 	canvas = EEL_CANVAS (widget);
2563 
2564 	shutdown_transients (canvas);
2565 
2566 	/* Unrealize items and parent widget */
2567 
2568 	(* EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->unrealize) (canvas->root);
2569 
2570 	if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
2571 		(* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
2572 }
2573 
2574 /* Handles scrolling of the canvas.  Adjusts the scrolling and zooming offset to
2575  * keep as much as possible of the canvas scrolling region in view.
2576  */
2577 static void
scroll_to(EelCanvas * canvas,int cx,int cy)2578 scroll_to (EelCanvas *canvas, int cx, int cy)
2579 {
2580 	int scroll_width, scroll_height;
2581 	int right_limit, bottom_limit;
2582 	int old_zoom_xofs, old_zoom_yofs;
2583 	int changed_x = FALSE, changed_y = FALSE;
2584 	int canvas_width, canvas_height;
2585 	GtkAllocation allocation;
2586 	GtkAdjustment *vadjustment, *hadjustment;
2587 	guint width, height;
2588 
2589 	gtk_widget_get_allocation (GTK_WIDGET (canvas), &allocation);
2590 	canvas_width = allocation.width;
2591 	canvas_height = allocation.height;
2592 
2593 	scroll_width = floor ((canvas->scroll_x2 - canvas->scroll_x1) * canvas->pixels_per_unit + 0.5);
2594 	scroll_height = floor ((canvas->scroll_y2 - canvas->scroll_y1) * canvas->pixels_per_unit + 0.5);
2595 
2596 	right_limit = scroll_width - canvas_width;
2597 	bottom_limit = scroll_height - canvas_height;
2598 
2599 	old_zoom_xofs = canvas->zoom_xofs;
2600 	old_zoom_yofs = canvas->zoom_yofs;
2601 
2602 	if (right_limit < 0) {
2603 		cx = 0;
2604 		if (canvas->center_scroll_region) {
2605 			canvas->zoom_xofs = (canvas_width - scroll_width) / 2;
2606 			scroll_width = canvas_width;
2607 		} else {
2608 			canvas->zoom_xofs = 0;
2609 		}
2610 	} else if (cx < 0) {
2611 		cx = 0;
2612 		canvas->zoom_xofs = 0;
2613 	} else if (cx > right_limit) {
2614 		cx = right_limit;
2615 		canvas->zoom_xofs = 0;
2616 	} else
2617 		canvas->zoom_xofs = 0;
2618 
2619 	if (bottom_limit < 0) {
2620 		cy = 0;
2621 		if (canvas->center_scroll_region) {
2622 			canvas->zoom_yofs = (canvas_height - scroll_height) / 2;
2623 			scroll_height = canvas_height;
2624 		} else {
2625 			canvas->zoom_yofs = 0;
2626 		}
2627 	} else if (cy < 0) {
2628 		cy = 0;
2629 		canvas->zoom_yofs = 0;
2630 	} else if (cy > bottom_limit) {
2631 		cy = bottom_limit;
2632 		canvas->zoom_yofs = 0;
2633 	} else
2634 		canvas->zoom_yofs = 0;
2635 
2636 	if ((canvas->zoom_xofs != old_zoom_xofs) || (canvas->zoom_yofs != old_zoom_yofs)) {
2637 		/* This can only occur, if either canvas size or widget size changes */
2638 		/* So I think we can request full redraw here */
2639 		/* More stuff - we have to mark root as needing fresh affine (Lauris) */
2640 		if (!(canvas->root->flags & EEL_CANVAS_ITEM_NEED_DEEP_UPDATE)) {
2641 			canvas->root->flags |= EEL_CANVAS_ITEM_NEED_DEEP_UPDATE;
2642 			eel_canvas_request_update (canvas);
2643 		}
2644 		gtk_widget_queue_draw (GTK_WIDGET (canvas));
2645 	}
2646 
2647 	hadjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (canvas));
2648 	vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (canvas));
2649 
2650 	if (((int) gtk_adjustment_get_value (hadjustment)) != cx) {
2651 		gtk_adjustment_set_value (hadjustment, cx);
2652 		changed_x = TRUE;
2653 	}
2654 
2655 	if (((int) gtk_adjustment_get_value (vadjustment)) != cy) {
2656 		gtk_adjustment_set_value (vadjustment, cy);
2657 		changed_y = TRUE;
2658 	}
2659 
2660 	gtk_layout_get_size (&canvas->layout, &width, &height);
2661 	if ((scroll_width != (int) width )|| (scroll_height != (int) height)) {
2662 		gtk_layout_set_size (GTK_LAYOUT (canvas), scroll_width, scroll_height);
2663 	}
2664 
2665 	/* Signal GtkLayout that it should do a redraw. */
2666 	if (changed_x)
2667 		g_signal_emit_by_name (hadjustment, "value_changed");
2668 	if (changed_y)
2669 		g_signal_emit_by_name (vadjustment, "value_changed");
2670 }
2671 
2672 /* Size allocation handler for the canvas */
2673 static void
eel_canvas_size_allocate(GtkWidget * widget,GtkAllocation * allocation)2674 eel_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
2675 {
2676 	EelCanvas *canvas;
2677 	GtkAdjustment *vadjustment, *hadjustment;
2678 
2679 	g_return_if_fail (EEL_IS_CANVAS (widget));
2680 	g_return_if_fail (allocation != NULL);
2681 
2682 	if (GTK_WIDGET_CLASS (canvas_parent_class)->size_allocate)
2683 		(* GTK_WIDGET_CLASS (canvas_parent_class)->size_allocate) (widget, allocation);
2684 
2685 	canvas = EEL_CANVAS (widget);
2686 
2687 	/* Recenter the view, if appropriate */
2688 
2689 	hadjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (canvas));
2690 	vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (canvas));
2691 
2692 	gtk_adjustment_set_page_size (hadjustment, allocation->width);
2693 	gtk_adjustment_set_page_increment (hadjustment, allocation->width / 2);
2694 
2695 	gtk_adjustment_set_page_size (vadjustment, allocation->height);
2696 	gtk_adjustment_set_page_increment (vadjustment, allocation->height / 2);
2697 
2698 	scroll_to (canvas,
2699 		   gtk_adjustment_get_value (hadjustment),
2700 		   gtk_adjustment_get_value (vadjustment));
2701 
2702 	g_signal_emit_by_name (hadjustment, "changed");
2703 	g_signal_emit_by_name (vadjustment, "changed");
2704 }
2705 
2706 /* Emits an event for an item in the canvas, be it the current item, grabbed
2707  * item, or focused item, as appropriate.
2708  */
2709 
2710 static int
emit_event(EelCanvas * canvas,GdkEvent * event)2711 emit_event (EelCanvas *canvas, GdkEvent *event)
2712 {
2713 	GdkEvent ev;
2714 	gint finished;
2715 	EelCanvasItem *item;
2716 	EelCanvasItem *parent;
2717 	guint mask;
2718 
2719 	/* Could be an old pick event */
2720 	if (!gtk_widget_get_realized (GTK_WIDGET (canvas))) {
2721 		return FALSE;
2722 	}
2723 
2724 	/* Perform checks for grabbed items */
2725 
2726 	if (canvas->grabbed_item &&
2727 	    !is_descendant (canvas->current_item, canvas->grabbed_item)) {
2728 		return FALSE;
2729         }
2730 
2731 	if (canvas->grabbed_item) {
2732 		switch ((int) event->type) {
2733 		case GDK_ENTER_NOTIFY:
2734 			mask = GDK_ENTER_NOTIFY_MASK;
2735 			break;
2736 
2737 		case GDK_LEAVE_NOTIFY:
2738 			mask = GDK_LEAVE_NOTIFY_MASK;
2739 			break;
2740 
2741 		case GDK_MOTION_NOTIFY:
2742 			mask = GDK_POINTER_MOTION_MASK;
2743 			break;
2744 
2745 		case GDK_BUTTON_PRESS:
2746 		case GDK_2BUTTON_PRESS:
2747 		case GDK_3BUTTON_PRESS:
2748 			mask = GDK_BUTTON_PRESS_MASK;
2749 			break;
2750 
2751 		case GDK_BUTTON_RELEASE:
2752 			mask = GDK_BUTTON_RELEASE_MASK;
2753 			break;
2754 
2755 		case GDK_KEY_PRESS:
2756 			mask = GDK_KEY_PRESS_MASK;
2757 			break;
2758 
2759 		case GDK_KEY_RELEASE:
2760 			mask = GDK_KEY_RELEASE_MASK;
2761 			break;
2762 
2763 		default:
2764 			mask = 0;
2765 			break;
2766 		}
2767 
2768 		if (!(mask & canvas->grabbed_event_mask))
2769 			return FALSE;
2770 	}
2771 
2772 	/* Convert to world coordinates -- we have two cases because of diferent
2773 	 * offsets of the fields in the event structures.
2774 	 */
2775 
2776 	ev = *event;
2777 
2778 	switch ((int) ev.type)
2779         {
2780 	case GDK_ENTER_NOTIFY:
2781 	case GDK_LEAVE_NOTIFY:
2782 		eel_canvas_window_to_world (canvas,
2783 					      ev.crossing.x, ev.crossing.y,
2784 					      &ev.crossing.x, &ev.crossing.y);
2785 		break;
2786 
2787 	case GDK_MOTION_NOTIFY:
2788                 eel_canvas_window_to_world (canvas,
2789                                               ev.motion.x, ev.motion.y,
2790                                               &ev.motion.x, &ev.motion.y);
2791                 break;
2792 
2793 	case GDK_BUTTON_PRESS:
2794 	case GDK_2BUTTON_PRESS:
2795 	case GDK_3BUTTON_PRESS:
2796                 eel_canvas_window_to_world (canvas,
2797                                               ev.motion.x, ev.motion.y,
2798                                               &ev.motion.x, &ev.motion.y);
2799                 break;
2800 
2801 	case GDK_BUTTON_RELEASE:
2802 		eel_canvas_window_to_world (canvas,
2803 					      ev.motion.x, ev.motion.y,
2804 					      &ev.motion.x, &ev.motion.y);
2805 		break;
2806 
2807 	default:
2808 		break;
2809 	}
2810 
2811 	/* Choose where we send the event */
2812 
2813 	item = canvas->current_item;
2814 
2815 	if (canvas->focused_item
2816 	    && ((event->type == GDK_KEY_PRESS) ||
2817 		(event->type == GDK_KEY_RELEASE) ||
2818 		(event->type == GDK_FOCUS_CHANGE)))
2819 		item = canvas->focused_item;
2820 
2821 	/* The event is propagated up the hierarchy (for if someone connected to
2822 	 * a group instead of a leaf event), and emission is stopped if a
2823 	 * handler returns TRUE, just like for GtkWidget events.
2824 	 */
2825 
2826 	finished = FALSE;
2827 
2828 	while (item && !finished) {
2829 		g_object_ref (item);
2830 
2831 		g_signal_emit (
2832 		       G_OBJECT (item), item_signals[ITEM_EVENT], 0,
2833 			&ev, &finished);
2834 
2835 		parent = item->parent;
2836 		g_object_unref (item);
2837 
2838 		item = parent;
2839 	}
2840 
2841 	return finished;
2842 }
2843 
2844 /* Re-picks the current item in the canvas, based on the event's coordinates.
2845  * Also emits enter/leave events for items as appropriate.
2846  */
2847 static int
pick_current_item(EelCanvas * canvas,GdkEvent * event)2848 pick_current_item (EelCanvas *canvas, GdkEvent *event)
2849 {
2850 	int button_down;
2851 	double x, y;
2852 	int cx, cy;
2853 	int retval;
2854 
2855 	retval = FALSE;
2856 
2857 	/* If a button is down, we'll perform enter and leave events on the
2858 	 * current item, but not enter on any other item.  This is more or less
2859 	 * like X pointer grabbing for canvas items.
2860 	 */
2861 	button_down = canvas->state & (GDK_BUTTON1_MASK
2862 				       | GDK_BUTTON2_MASK
2863 				       | GDK_BUTTON3_MASK
2864 				       | GDK_BUTTON4_MASK
2865 				       | GDK_BUTTON5_MASK);
2866 	if (!button_down)
2867 		canvas->left_grabbed_item = FALSE;
2868 
2869 	/* Save the event in the canvas.  This is used to synthesize enter and
2870 	 * leave events in case the current item changes.  It is also used to
2871 	 * re-pick the current item if the current one gets deleted.  Also,
2872 	 * synthesize an enter event.
2873 	 */
2874 	if (event != &canvas->pick_event) {
2875 		if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
2876 			/* these fields have the same offsets in both types of events */
2877 
2878 			canvas->pick_event.crossing.type       = GDK_ENTER_NOTIFY;
2879 			canvas->pick_event.crossing.window     = event->motion.window;
2880 			canvas->pick_event.crossing.send_event = event->motion.send_event;
2881 			canvas->pick_event.crossing.subwindow  = NULL;
2882 			canvas->pick_event.crossing.x          = event->motion.x;
2883 			canvas->pick_event.crossing.y          = event->motion.y;
2884 			canvas->pick_event.crossing.mode       = GDK_CROSSING_NORMAL;
2885 			canvas->pick_event.crossing.detail     = GDK_NOTIFY_NONLINEAR;
2886 			canvas->pick_event.crossing.focus      = FALSE;
2887 			canvas->pick_event.crossing.state      = event->motion.state;
2888 
2889 			/* these fields don't have the same offsets in both types of events */
2890 
2891 			if (event->type == GDK_MOTION_NOTIFY) {
2892 				canvas->pick_event.crossing.x_root = event->motion.x_root;
2893 				canvas->pick_event.crossing.y_root = event->motion.y_root;
2894 			} else {
2895 				canvas->pick_event.crossing.x_root = event->button.x_root;
2896 				canvas->pick_event.crossing.y_root = event->button.y_root;
2897 			}
2898 		} else
2899 			canvas->pick_event = *event;
2900 	}
2901 
2902 	/* Don't do anything else if this is a recursive call */
2903 
2904 	if (canvas->in_repick)
2905 		return retval;
2906 
2907 	/* LeaveNotify means that there is no current item, so we don't look for one */
2908 
2909 	if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
2910 		/* these fields don't have the same offsets in both types of events */
2911 
2912 		if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
2913 			x = canvas->pick_event.crossing.x;
2914 			y = canvas->pick_event.crossing.y;
2915 		} else {
2916 			x = canvas->pick_event.motion.x;
2917 			y = canvas->pick_event.motion.y;
2918 		}
2919 
2920 		/* canvas pixel coords */
2921 
2922 		cx = (int) (x + 0.5);
2923 		cy = (int) (y + 0.5);
2924 
2925 		/* world coords */
2926 		eel_canvas_c2w (canvas, cx, cy, &x, &y);
2927 
2928 		/* find the closest item */
2929 		if (canvas->root->flags & EEL_CANVAS_ITEM_MAPPED)
2930 			eel_canvas_item_invoke_point (canvas->root, x, y, cx, cy,
2931 							&canvas->new_current_item);
2932 		else
2933 			canvas->new_current_item = NULL;
2934 	} else
2935 		canvas->new_current_item = NULL;
2936 
2937 	if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item)
2938 		return retval; /* current item did not change */
2939 
2940 	/* Synthesize events for old and new current items */
2941 
2942 	if ((canvas->new_current_item != canvas->current_item)
2943 	    && (canvas->current_item != NULL)
2944 	    && !canvas->left_grabbed_item) {
2945 		GdkEvent new_event;
2946 
2947 		new_event = canvas->pick_event;
2948 		new_event.type = GDK_LEAVE_NOTIFY;
2949 
2950 		new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
2951 		new_event.crossing.subwindow = NULL;
2952 		canvas->in_repick = TRUE;
2953 		retval = emit_event (canvas, &new_event);
2954 		canvas->in_repick = FALSE;
2955 	}
2956 
2957 	/* new_current_item may have been set to NULL during the call to emit_event() above */
2958 
2959 	if ((canvas->new_current_item != canvas->current_item) && button_down) {
2960 		canvas->left_grabbed_item = TRUE;
2961 		return retval;
2962 	}
2963 
2964 	/* Handle the rest of cases */
2965 
2966 	canvas->left_grabbed_item = FALSE;
2967 	canvas->current_item = canvas->new_current_item;
2968 
2969 	if (canvas->current_item != NULL) {
2970 		GdkEvent new_event;
2971 
2972 		new_event = canvas->pick_event;
2973 		new_event.type = GDK_ENTER_NOTIFY;
2974 		new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
2975 		new_event.crossing.subwindow = NULL;
2976 		retval = emit_event (canvas, &new_event);
2977 	}
2978 
2979 	return retval;
2980 }
2981 
2982 /* Button event handler for the canvas */
2983 static gint
eel_canvas_button(GtkWidget * widget,GdkEventButton * event)2984 eel_canvas_button (GtkWidget *widget, GdkEventButton *event)
2985 {
2986 	EelCanvas *canvas;
2987 	int mask;
2988 	int retval;
2989 
2990 	g_return_val_if_fail (EEL_IS_CANVAS (widget), FALSE);
2991 	g_return_val_if_fail (event != NULL, FALSE);
2992 
2993 	/* Don't handle extra mouse button events */
2994 	if (event->button > 5)
2995 		return FALSE;
2996 
2997 	retval = FALSE;
2998 
2999 	canvas = EEL_CANVAS (widget);
3000 
3001 	/*
3002 	 * dispatch normally regardless of the event's window if an item has
3003 	 * has a pointer grab in effect
3004 	 */
3005 	if (!canvas->grabbed_item && event->window != gtk_layout_get_bin_window (GTK_LAYOUT (canvas)))
3006 		return retval;
3007 
3008 	switch (event->button) {
3009 	case 1:
3010 		mask = GDK_BUTTON1_MASK;
3011 		break;
3012 	case 2:
3013 		mask = GDK_BUTTON2_MASK;
3014 		break;
3015 	case 3:
3016 		mask = GDK_BUTTON3_MASK;
3017 		break;
3018 	case 4:
3019 		mask = GDK_BUTTON4_MASK;
3020 		break;
3021 	case 5:
3022 		mask = GDK_BUTTON5_MASK;
3023 		break;
3024 	default:
3025 		mask = 0;
3026 	}
3027 
3028 	switch ((int) event->type) {
3029 	case GDK_BUTTON_PRESS:
3030 	case GDK_2BUTTON_PRESS:
3031 	case GDK_3BUTTON_PRESS:
3032 		/* Pick the current item as if the button were not pressed, and
3033 		 * then process the event.
3034 		 */
3035 		event->state ^= mask;
3036 		canvas->state = event->state;
3037 		pick_current_item (canvas, (GdkEvent *) event);
3038 		event->state ^= mask;
3039 		canvas->state = event->state;
3040 		retval = emit_event (canvas, (GdkEvent *) event);
3041 		break;
3042 
3043 	case GDK_BUTTON_RELEASE:
3044 		/* Process the event as if the button were pressed, then repick
3045 		 * after the button has been released
3046 		 */
3047 		canvas->state = event->state;
3048 		retval = emit_event (canvas, (GdkEvent *) event);
3049 		event->state ^= mask;
3050 		canvas->state = event->state;
3051 		pick_current_item (canvas, (GdkEvent *) event);
3052 		event->state ^= mask;
3053 		break;
3054 
3055 	default:
3056 		g_assert_not_reached ();
3057 	}
3058 
3059 	return retval;
3060 }
3061 
3062 /* Motion event handler for the canvas */
3063 static gint
eel_canvas_motion(GtkWidget * widget,GdkEventMotion * event)3064 eel_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
3065 {
3066 	EelCanvas *canvas;
3067 
3068 	g_return_val_if_fail (EEL_IS_CANVAS (widget), FALSE);
3069 	g_return_val_if_fail (event != NULL, FALSE);
3070 
3071 	canvas = EEL_CANVAS (widget);
3072 
3073 	if (event->window != gtk_layout_get_bin_window (GTK_LAYOUT (canvas)))
3074 		return FALSE;
3075 
3076 	canvas->state = event->state;
3077 	pick_current_item (canvas, (GdkEvent *) event);
3078 	return emit_event (canvas, (GdkEvent *) event);
3079 }
3080 
3081 /* Key event handler for the canvas */
3082 static gint
eel_canvas_key(GtkWidget * widget,GdkEventKey * event)3083 eel_canvas_key (GtkWidget *widget, GdkEventKey *event)
3084 {
3085 	EelCanvas *canvas;
3086 
3087 	g_return_val_if_fail (EEL_IS_CANVAS (widget), FALSE);
3088 	g_return_val_if_fail (event != NULL, FALSE);
3089 
3090 	canvas = EEL_CANVAS (widget);
3091 
3092 	if (emit_event (canvas, (GdkEvent *) event))
3093 		return TRUE;
3094 	if (event->type == GDK_KEY_RELEASE)
3095 		return GTK_WIDGET_CLASS (canvas_parent_class)->key_release_event (widget, event);
3096 	else
3097 		return GTK_WIDGET_CLASS (canvas_parent_class)->key_press_event (widget, event);
3098 }
3099 
3100 
3101 /* Crossing event handler for the canvas */
3102 static gint
eel_canvas_crossing(GtkWidget * widget,GdkEventCrossing * event)3103 eel_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
3104 {
3105 	EelCanvas *canvas;
3106 
3107 	g_return_val_if_fail (EEL_IS_CANVAS (widget), FALSE);
3108 	g_return_val_if_fail (event != NULL, FALSE);
3109 
3110 	canvas = EEL_CANVAS (widget);
3111 
3112 	if (event->window != gtk_layout_get_bin_window (GTK_LAYOUT (canvas)))
3113 		return FALSE;
3114 
3115 	canvas->state = event->state;
3116 	return pick_current_item (canvas, (GdkEvent *) event);
3117 }
3118 
3119 /* Focus in handler for the canvas */
3120 static gint
eel_canvas_focus_in(GtkWidget * widget,GdkEventFocus * event)3121 eel_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
3122 {
3123 	EelCanvas *canvas;
3124 
3125 	canvas = EEL_CANVAS (widget);
3126 
3127 	if (canvas->focused_item)
3128 		return emit_event (canvas, (GdkEvent *) event);
3129 	else
3130 		return FALSE;
3131 }
3132 
3133 static AtkObject *
eel_canvas_get_accessible(GtkWidget * widget)3134 eel_canvas_get_accessible (GtkWidget *widget)
3135 {
3136 	return atk_gobject_accessible_for_object (G_OBJECT (widget));
3137 }
3138 
3139 /* Focus out handler for the canvas */
3140 static gint
eel_canvas_focus_out(GtkWidget * widget,GdkEventFocus * event)3141 eel_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
3142 {
3143 	EelCanvas *canvas;
3144 
3145 	canvas = EEL_CANVAS (widget);
3146 
3147 	if (canvas->focused_item)
3148 		return emit_event (canvas, (GdkEvent *) event);
3149 	else
3150 		return FALSE;
3151 }
3152 
3153 
3154 static cairo_region_t *
eel_cairo_get_clip_region(cairo_t * cr)3155 eel_cairo_get_clip_region (cairo_t *cr)
3156 {
3157         cairo_rectangle_list_t *list;
3158         cairo_region_t *region;
3159         int i;
3160 
3161         list = cairo_copy_clip_rectangle_list (cr);
3162         if (list->status == CAIRO_STATUS_CLIP_NOT_REPRESENTABLE) {
3163                 cairo_rectangle_int_t clip_rect;
3164 
3165                 cairo_rectangle_list_destroy (list);
3166 
3167                 if (!gdk_cairo_get_clip_rectangle (cr, &clip_rect))
3168                         return NULL;
3169                 return cairo_region_create_rectangle (&clip_rect);
3170         }
3171 
3172 
3173         region = cairo_region_create ();
3174         for (i = list->num_rectangles - 1; i >= 0; --i) {
3175                 cairo_rectangle_t *rect = &list->rectangles[i];
3176                 cairo_rectangle_int_t clip_rect;
3177 
3178                 clip_rect.x = floor (rect->x);
3179                 clip_rect.y = floor (rect->y);
3180                 clip_rect.width = ceil (rect->x + rect->width) - clip_rect.x;
3181                 clip_rect.height = ceil (rect->y + rect->height) - clip_rect.y;
3182 
3183                 if (cairo_region_union_rectangle (region, &clip_rect) != CAIRO_STATUS_SUCCESS) {
3184                         cairo_region_destroy (region);
3185                         region = NULL;
3186                         break;
3187                 }
3188         }
3189 
3190         cairo_rectangle_list_destroy (list);
3191         return region;
3192 }
3193 
3194 /* Expose handler for the canvas */
3195 static gboolean
eel_canvas_draw(GtkWidget * widget,cairo_t * cr)3196 eel_canvas_draw (GtkWidget *widget, cairo_t *cr)
3197 {
3198 	EelCanvas *canvas;
3199         GdkWindow *bin_window;
3200         cairo_region_t *region;
3201 
3202         if (!gdk_cairo_get_clip_rectangle (cr, NULL))
3203                 return FALSE;
3204 
3205         bin_window = gtk_layout_get_bin_window (GTK_LAYOUT (widget));
3206 
3207         if (!gtk_cairo_should_draw_window (cr, bin_window))
3208             return FALSE;
3209 
3210         cairo_save (cr);
3211 
3212         gtk_cairo_transform_to_window (cr, widget, bin_window);
3213 
3214         region = eel_cairo_get_clip_region (cr);
3215 
3216         if (region == NULL) {
3217             cairo_restore (cr);
3218             return FALSE;
3219         }
3220 
3221 #ifdef VERBOSE
3222 	g_print ("Draw\n");
3223 #endif
3224 	/* If there are any outstanding items that need updating, do them now */
3225 	canvas = EEL_CANVAS (widget);
3226 	if (canvas->idle_id) {
3227 		g_source_remove (canvas->idle_id);
3228 		canvas->idle_id = 0;
3229 	}
3230 	if (canvas->need_update) {
3231 		g_return_val_if_fail (!canvas->doing_update, FALSE);
3232 
3233 		canvas->doing_update = TRUE;
3234 		eel_canvas_item_invoke_update (canvas->root, 0, 0, 0);
3235 
3236 		g_return_val_if_fail (canvas->doing_update, FALSE);
3237 
3238 		canvas->doing_update = FALSE;
3239 
3240 		canvas->need_update = FALSE;
3241 	}
3242 
3243 	/* Hmmm. Would like to queue antiexposes if the update marked
3244 	   anything that is gonna get redrawn as invalid */
3245 
3246 	g_signal_emit (G_OBJECT (canvas), canvas_signals[DRAW_BACKGROUND], 0,
3247                        cr);
3248 
3249 	if (canvas->root->flags & EEL_CANVAS_ITEM_MAPPED)
3250 		EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->draw (canvas->root, cr, region);
3251 
3252     	cairo_restore (cr);
3253 
3254 	/* Chain up to get exposes on child widgets */
3255         if (GTK_WIDGET_CLASS (canvas_parent_class)->draw)
3256                 GTK_WIDGET_CLASS (canvas_parent_class)->draw (widget, cr);
3257 
3258         cairo_region_destroy (region);
3259 	return FALSE;
3260 }
3261 
3262 static void
eel_canvas_draw_background(EelCanvas * canvas,cairo_t * cr)3263 eel_canvas_draw_background (EelCanvas *canvas,
3264                             cairo_t   *cr)
3265 {
3266         cairo_rectangle_int_t rect;
3267 	GtkStyleContext *style_context;
3268 	GdkRGBA color;
3269 
3270         if (!gdk_cairo_get_clip_rectangle (cr, &rect))
3271               return;
3272 
3273         cairo_save (cr);
3274 	/* By default, we use the style background. */
3275 	style_context = gtk_widget_get_style_context (GTK_WIDGET (canvas));
3276 	gtk_style_context_get_background_color (style_context, GTK_STATE_FLAG_NORMAL, &color);
3277 	gdk_cairo_set_source_rgba (cr, &color);
3278 	gdk_cairo_rectangle (cr, &rect);
3279 	cairo_fill (cr);
3280         cairo_restore (cr);
3281 }
3282 
3283 static void
do_update(EelCanvas * canvas)3284 do_update (EelCanvas *canvas)
3285 {
3286 	/* Cause the update if necessary */
3287 
3288 update_again:
3289 	if (canvas->need_update) {
3290 		g_return_if_fail (!canvas->doing_update);
3291 
3292 		canvas->doing_update = TRUE;
3293 		eel_canvas_item_invoke_update (canvas->root, 0, 0, 0);
3294 
3295 		g_return_if_fail (canvas->doing_update);
3296 
3297 		canvas->doing_update = FALSE;
3298 
3299 		canvas->need_update = FALSE;
3300 	}
3301 
3302 	/* Pick new current item */
3303 
3304 	while (canvas->need_repick) {
3305 		canvas->need_repick = FALSE;
3306 		pick_current_item (canvas, &canvas->pick_event);
3307 	}
3308 
3309 	/* it is possible that during picking we emitted an event in which
3310 	   the user then called some function which then requested update
3311 	   of something.  Without this we'd be left in a state where
3312 	   need_update would have been left TRUE and the canvas would have
3313 	   been left unpainted. */
3314 	if (canvas->need_update) {
3315 		goto update_again;
3316 	}
3317 }
3318 
3319 /* Idle handler for the canvas.  It deals with pending updates and redraws. */
3320 static gint
idle_handler(gpointer data)3321 idle_handler (gpointer data)
3322 {
3323 	EelCanvas *canvas;
3324 
3325 	canvas = EEL_CANVAS (data);
3326 	do_update (canvas);
3327 
3328 	/* Reset idle id */
3329 	canvas->idle_id = 0;
3330 
3331 	return FALSE;
3332 }
3333 
3334 /* Convenience function to add an idle handler to a canvas */
3335 static void
add_idle(EelCanvas * canvas)3336 add_idle (EelCanvas *canvas)
3337 {
3338 	if (!canvas->idle_id) {
3339 		/* We let the update idle handler have higher priority
3340 		 * than the redraw idle handler so the canvas state
3341 		 * will be updated during the expose event.  canvas in
3342 		 * expose_event.
3343 		 */
3344 		canvas->idle_id = g_idle_add_full (GDK_PRIORITY_REDRAW - 20,
3345 						   idle_handler, canvas, NULL);
3346 	}
3347 }
3348 
3349 /**
3350  * eel_canvas_root:
3351  * @canvas: A canvas.
3352  *
3353  * Queries the root group of a canvas.
3354  *
3355  * Return value: The root group of the specified canvas.
3356  **/
3357 EelCanvasGroup *
eel_canvas_root(EelCanvas * canvas)3358 eel_canvas_root (EelCanvas *canvas)
3359 {
3360 	g_return_val_if_fail (EEL_IS_CANVAS (canvas), NULL);
3361 
3362 	return EEL_CANVAS_GROUP (canvas->root);
3363 }
3364 
3365 
3366 /**
3367  * eel_canvas_set_scroll_region:
3368  * @canvas: A canvas.
3369  * @x1: Leftmost limit of the scrolling region.
3370  * @y1: Upper limit of the scrolling region.
3371  * @x2: Rightmost limit of the scrolling region.
3372  * @y2: Lower limit of the scrolling region.
3373  *
3374  * Sets the scrolling region of a canvas to the specified rectangle.  The canvas
3375  * will then be able to scroll only within this region.  The view of the canvas
3376  * is adjusted as appropriate to display as much of the new region as possible.
3377  **/
3378 void
eel_canvas_set_scroll_region(EelCanvas * canvas,double x1,double y1,double x2,double y2)3379 eel_canvas_set_scroll_region (EelCanvas *canvas, double x1, double y1, double x2, double y2)
3380 {
3381 	double wxofs, wyofs;
3382 	int xofs, yofs;
3383 
3384 	g_return_if_fail (EEL_IS_CANVAS (canvas));
3385 
3386 	if ((canvas->scroll_x1 == x1) && (canvas->scroll_y1 == y1) &&
3387 	    (canvas->scroll_x2 == x2) && (canvas->scroll_y2 == y2)) {
3388 		return;
3389 	}
3390 
3391 	/*
3392 	 * Set the new scrolling region.  If possible, do not move the visible contents of the
3393 	 * canvas.
3394 	 */
3395 
3396 	eel_canvas_c2w (canvas,
3397 			  gtk_adjustment_get_value (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (canvas))) + canvas->zoom_xofs,
3398 			  gtk_adjustment_get_value (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (canvas))) + canvas->zoom_yofs,
3399 			  /*canvas->zoom_xofs,
3400 			  canvas->zoom_yofs,*/
3401 			  &wxofs, &wyofs);
3402 
3403 	canvas->scroll_x1 = x1;
3404 	canvas->scroll_y1 = y1;
3405 	canvas->scroll_x2 = x2;
3406 	canvas->scroll_y2 = y2;
3407 
3408 	eel_canvas_w2c (canvas, wxofs, wyofs, &xofs, &yofs);
3409 
3410 	scroll_to (canvas, xofs, yofs);
3411 
3412 	canvas->need_repick = TRUE;
3413 
3414 	if (!(canvas->root->flags & EEL_CANVAS_ITEM_NEED_DEEP_UPDATE)) {
3415 		canvas->root->flags |= EEL_CANVAS_ITEM_NEED_DEEP_UPDATE;
3416 		eel_canvas_request_update (canvas);
3417 	}
3418 }
3419 
3420 
3421 /**
3422  * eel_canvas_get_scroll_region:
3423  * @canvas: A canvas.
3424  * @x1: Leftmost limit of the scrolling region (return value).
3425  * @y1: Upper limit of the scrolling region (return value).
3426  * @x2: Rightmost limit of the scrolling region (return value).
3427  * @y2: Lower limit of the scrolling region (return value).
3428  *
3429  * Queries the scrolling region of a canvas.
3430  **/
3431 void
eel_canvas_get_scroll_region(EelCanvas * canvas,double * x1,double * y1,double * x2,double * y2)3432 eel_canvas_get_scroll_region (EelCanvas *canvas, double *x1, double *y1, double *x2, double *y2)
3433 {
3434 	g_return_if_fail (EEL_IS_CANVAS (canvas));
3435 
3436 	if (x1)
3437 		*x1 = canvas->scroll_x1;
3438 
3439 	if (y1)
3440 		*y1 = canvas->scroll_y1;
3441 
3442 	if (x2)
3443 		*x2 = canvas->scroll_x2;
3444 
3445 	if (y2)
3446 		*y2 = canvas->scroll_y2;
3447 }
3448 
3449 void
eel_canvas_set_center_scroll_region(EelCanvas * canvas,gboolean center_scroll_region)3450 eel_canvas_set_center_scroll_region (EelCanvas *canvas,
3451 				     gboolean center_scroll_region)
3452 {
3453 	g_return_if_fail (EEL_IS_CANVAS (canvas));
3454 
3455 	canvas->center_scroll_region = center_scroll_region != 0;
3456 
3457 	scroll_to (canvas,
3458 		   gtk_adjustment_get_value (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (&canvas->layout))),
3459 		   gtk_adjustment_get_value (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (&canvas->layout))));
3460 }
3461 
3462 
3463 /**
3464  * eel_canvas_set_pixels_per_unit:
3465  * @canvas: A canvas.
3466  * @n: The number of pixels that correspond to one canvas unit.
3467  *
3468  * Sets the zooming factor of a canvas by specifying the number of pixels that
3469  * correspond to one canvas unit.
3470  **/
3471 void
eel_canvas_set_pixels_per_unit(EelCanvas * canvas,double n)3472 eel_canvas_set_pixels_per_unit (EelCanvas *canvas, double n)
3473 {
3474 	GtkWidget *widget;
3475 	double cx, cy;
3476 	int x1, y1;
3477 	int center_x, center_y;
3478 	GdkWindow *window;
3479 	GdkWindowAttr attributes;
3480 	gint attributes_mask;
3481 	GtkAllocation allocation;
3482 	GtkAdjustment *vadjustment, *hadjustment;
3483 
3484 	g_return_if_fail (EEL_IS_CANVAS (canvas));
3485 	g_return_if_fail (n > EEL_CANVAS_EPSILON);
3486 
3487 	widget = GTK_WIDGET (canvas);
3488 
3489 	gtk_widget_get_allocation (widget, &allocation);
3490 	center_x = allocation.width / 2;
3491 	center_y = allocation.height / 2;
3492 
3493 	/* Find the coordinates of the screen center in units. */
3494 	hadjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (canvas));
3495 	vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (canvas));
3496 	cx = (gtk_adjustment_get_value (hadjustment) + center_x) / canvas->pixels_per_unit + canvas->scroll_x1 + canvas->zoom_xofs;
3497 	cy = (gtk_adjustment_get_value (vadjustment) + center_y) / canvas->pixels_per_unit + canvas->scroll_y1 + canvas->zoom_yofs;
3498 
3499 	/* Now calculate the new offset of the upper left corner. (round not truncate) */
3500 	x1 = ((cx - canvas->scroll_x1) * n) - center_x + .5;
3501 	y1 = ((cy - canvas->scroll_y1) * n) - center_y + .5;
3502 
3503 	canvas->pixels_per_unit = n;
3504 
3505 	if (!(canvas->root->flags & EEL_CANVAS_ITEM_NEED_DEEP_UPDATE)) {
3506 		canvas->root->flags |= EEL_CANVAS_ITEM_NEED_DEEP_UPDATE;
3507 		eel_canvas_request_update (canvas);
3508 	}
3509 
3510 	/* Map a background None window over the bin_window to avoid
3511 	 * scrolling the window scroll causing exposes.
3512 	 */
3513 	window = NULL;
3514 	if (gtk_widget_get_mapped (widget)) {
3515 		attributes.window_type = GDK_WINDOW_CHILD;
3516 		gtk_widget_get_allocation (widget, &allocation);
3517 		attributes.x = allocation.x;
3518 		attributes.y = allocation.y;
3519 		attributes.width = allocation.width;
3520 		attributes.height = allocation.height;
3521 		attributes.wclass = GDK_INPUT_OUTPUT;
3522 		attributes.visual = gtk_widget_get_visual (widget);
3523 		attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
3524 
3525 		attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
3526 
3527 		window = gdk_window_new (gtk_widget_get_parent_window (widget),
3528 					 &attributes, attributes_mask);
3529 		gdk_window_set_user_data (window, widget);
3530 
3531 		gdk_window_show (window);
3532 	}
3533 
3534 	scroll_to (canvas, x1, y1);
3535 
3536 	/* If we created a an overlapping background None window, remove it how.
3537 	 *
3538 	 * TODO: We would like to temporarily set the bin_window background to
3539 	 * None to avoid clearing the bin_window to the background, but gdk doesn't
3540 	 * expose enought to let us do this, so we get a flash-effect here. At least
3541 	 * it looks better than scroll + expose.
3542 	 */
3543 	if (window != NULL) {
3544 		gdk_window_hide (window);
3545 		gdk_window_set_user_data (window, NULL);
3546 		gdk_window_destroy (window);
3547 	}
3548 
3549 	canvas->need_repick = TRUE;
3550 }
3551 
3552 /**
3553  * eel_canvas_scroll_to:
3554  * @canvas: A canvas.
3555  * @cx: Horizontal scrolling offset in canvas pixel units.
3556  * @cy: Vertical scrolling offset in canvas pixel units.
3557  *
3558  * Makes a canvas scroll to the specified offsets, given in canvas pixel units.
3559  * The canvas will adjust the view so that it is not outside the scrolling
3560  * region.  This function is typically not used, as it is better to hook
3561  * scrollbars to the canvas layout's scrolling adjusments.
3562  **/
3563 void
eel_canvas_scroll_to(EelCanvas * canvas,int cx,int cy)3564 eel_canvas_scroll_to (EelCanvas *canvas, int cx, int cy)
3565 {
3566 	g_return_if_fail (EEL_IS_CANVAS (canvas));
3567 
3568 	scroll_to (canvas, cx, cy);
3569 }
3570 
3571 /**
3572  * eel_canvas_get_scroll_offsets:
3573  * @canvas: A canvas.
3574  * @cx: Horizontal scrolling offset (return value).
3575  * @cy: Vertical scrolling offset (return value).
3576  *
3577  * Queries the scrolling offsets of a canvas.  The values are returned in canvas
3578  * pixel units.
3579  **/
3580 void
eel_canvas_get_scroll_offsets(EelCanvas * canvas,int * cx,int * cy)3581 eel_canvas_get_scroll_offsets (EelCanvas *canvas, int *cx, int *cy)
3582 {
3583 	g_return_if_fail (EEL_IS_CANVAS (canvas));
3584 
3585 	if (cx)
3586 		*cx = gtk_adjustment_get_value (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (canvas)));
3587 
3588 	if (cy)
3589 		*cy = gtk_adjustment_get_value (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (canvas)));
3590 }
3591 
3592 /**
3593  * eel_canvas_update_now:
3594  * @canvas: A canvas.
3595  *
3596  * Forces an immediate update and redraw of a canvas.  If the canvas does not
3597  * have any pending update or redraw requests, then no action is taken.  This is
3598  * typically only used by applications that need explicit control of when the
3599  * display is updated, like games.  It is not needed by normal applications.
3600  */
3601 void
eel_canvas_update_now(EelCanvas * canvas)3602 eel_canvas_update_now (EelCanvas *canvas)
3603 {
3604 	g_return_if_fail (EEL_IS_CANVAS (canvas));
3605 
3606 	if (!(canvas->need_update || canvas->need_redraw))
3607 		return;
3608 	remove_idle (canvas);
3609 	do_update (canvas);
3610 }
3611 
3612 /**
3613  * eel_canvas_get_item_at:
3614  * @canvas: A canvas.
3615  * @x: X position in world coordinates.
3616  * @y: Y position in world coordinates.
3617  *
3618  * Looks for the item that is under the specified position, which must be
3619  * specified in world coordinates.
3620  *
3621  * Return value: The sought item, or NULL if no item is at the specified
3622  * coordinates.
3623  **/
3624 EelCanvasItem *
eel_canvas_get_item_at(EelCanvas * canvas,double x,double y)3625 eel_canvas_get_item_at (EelCanvas *canvas, double x, double y)
3626 {
3627 	EelCanvasItem *item;
3628 	double dist;
3629 	int cx, cy;
3630 
3631 	g_return_val_if_fail (EEL_IS_CANVAS (canvas), NULL);
3632 
3633 	eel_canvas_w2c (canvas, x, y, &cx, &cy);
3634 
3635 	dist = eel_canvas_item_invoke_point (canvas->root, x, y, cx, cy, &item);
3636 	if ((int) (dist * canvas->pixels_per_unit + 0.5) <= canvas->close_enough)
3637 		return item;
3638 	else
3639 		return NULL;
3640 }
3641 
3642 /* Queues an update of the canvas */
3643 static void
eel_canvas_request_update(EelCanvas * canvas)3644 eel_canvas_request_update (EelCanvas *canvas)
3645 {
3646 	EEL_CANVAS_GET_CLASS (canvas)->request_update (canvas);
3647 }
3648 
3649 static void
eel_canvas_request_update_real(EelCanvas * canvas)3650 eel_canvas_request_update_real (EelCanvas *canvas)
3651 {
3652 	canvas->need_update = TRUE;
3653 	add_idle (canvas);
3654 }
3655 
3656 /**
3657  * eel_canvas_request_redraw:
3658  * @canvas: A canvas.
3659  * @x1: Leftmost coordinate of the rectangle to be redrawn.
3660  * @y1: Upper coordinate of the rectangle to be redrawn.
3661  * @x2: Rightmost coordinate of the rectangle to be redrawn, plus 1.
3662  * @y2: Lower coordinate of the rectangle to be redrawn, plus 1.
3663  *
3664  * Convenience function that informs a canvas that the specified rectangle needs
3665  * to be repainted.  The rectangle includes @x1 and @y1, but not @x2 and @y2.
3666  * To be used only by item implementations.
3667  **/
3668 void
eel_canvas_request_redraw(EelCanvas * canvas,int x1,int y1,int x2,int y2)3669 eel_canvas_request_redraw (EelCanvas *canvas, int x1, int y1, int x2, int y2)
3670 {
3671 	GdkRectangle bbox;
3672 
3673 	g_return_if_fail (EEL_IS_CANVAS (canvas));
3674 
3675 	if (!gtk_widget_is_drawable (GTK_WIDGET (canvas))
3676 	    || (x1 >= x2) || (y1 >= y2)) return;
3677 
3678 	bbox.x = x1;
3679 	bbox.y = y1;
3680 	bbox.width = x2 - x1;
3681 	bbox.height = y2 - y1;
3682 
3683 	gdk_window_invalidate_rect (gtk_layout_get_bin_window (GTK_LAYOUT (canvas)),
3684 				    &bbox, FALSE);
3685 }
3686 
3687 /**
3688  * eel_canvas_w2c:
3689  * @canvas: A canvas.
3690  * @wx: World X coordinate.
3691  * @wy: World Y coordinate.
3692  * @cx: X pixel coordinate (return value).
3693  * @cy: Y pixel coordinate (return value).
3694  *
3695  * Converts world coordinates into canvas pixel coordinates.
3696  **/
3697 void
eel_canvas_w2c(EelCanvas * canvas,double wx,double wy,int * cx,int * cy)3698 eel_canvas_w2c (EelCanvas *canvas, double wx, double wy, int *cx, int *cy)
3699 {
3700 	double zoom;
3701 
3702 	g_return_if_fail (EEL_IS_CANVAS (canvas));
3703 
3704 	zoom = canvas->pixels_per_unit;
3705 
3706 	if (cx)
3707 		*cx = floor ((wx - canvas->scroll_x1)*zoom + canvas->zoom_xofs + 0.5);
3708 	if (cy)
3709 		*cy = floor ((wy - canvas->scroll_y1)*zoom + canvas->zoom_yofs + 0.5);
3710 }
3711 
3712 /**
3713  * eel_canvas_w2c:
3714  * @canvas: A canvas.
3715  * @world: rectangle in world coordinates.
3716  * @canvas: rectangle in canvase coordinates.
3717  *
3718  * Converts rectangles in world coordinates into canvas pixel coordinates.
3719  **/
3720 void
eel_canvas_w2c_rect_d(EelCanvas * canvas,double * x1,double * y1,double * x2,double * y2)3721 eel_canvas_w2c_rect_d (EelCanvas *canvas,
3722 			 double *x1, double *y1,
3723 			 double *x2, double *y2)
3724 {
3725 	eel_canvas_w2c_d (canvas,
3726 			    *x1, *y1,
3727 			    x1, y1);
3728 	eel_canvas_w2c_d (canvas,
3729 			    *x2, *y2,
3730 			    x2, y2);
3731 }
3732 
3733 
3734 
3735 /**
3736  * eel_canvas_w2c_d:
3737  * @canvas: A canvas.
3738  * @wx: World X coordinate.
3739  * @wy: World Y coordinate.
3740  * @cx: X pixel coordinate (return value).
3741  * @cy: Y pixel coordinate (return value).
3742  *
3743  * Converts world coordinates into canvas pixel coordinates.  This version
3744  * produces coordinates in floating point coordinates, for greater precision.
3745  **/
3746 void
eel_canvas_w2c_d(EelCanvas * canvas,double wx,double wy,double * cx,double * cy)3747 eel_canvas_w2c_d (EelCanvas *canvas, double wx, double wy, double *cx, double *cy)
3748 {
3749 	double zoom;
3750 
3751 	g_return_if_fail (EEL_IS_CANVAS (canvas));
3752 
3753 	zoom = canvas->pixels_per_unit;
3754 
3755 	if (cx)
3756 		*cx = (wx - canvas->scroll_x1)*zoom + canvas->zoom_xofs;
3757 	if (cy)
3758 		*cy = (wy - canvas->scroll_y1)*zoom + canvas->zoom_yofs;
3759 }
3760 
3761 
3762 /**
3763  * eel_canvas_c2w:
3764  * @canvas: A canvas.
3765  * @cx: Canvas pixel X coordinate.
3766  * @cy: Canvas pixel Y coordinate.
3767  * @wx: X world coordinate (return value).
3768  * @wy: Y world coordinate (return value).
3769  *
3770  * Converts canvas pixel coordinates to world coordinates.
3771  **/
3772 void
eel_canvas_c2w(EelCanvas * canvas,int cx,int cy,double * wx,double * wy)3773 eel_canvas_c2w (EelCanvas *canvas, int cx, int cy, double *wx, double *wy)
3774 {
3775 	double zoom;
3776 
3777 	g_return_if_fail (EEL_IS_CANVAS (canvas));
3778 
3779 	zoom = canvas->pixels_per_unit;
3780 
3781 	if (wx)
3782 		*wx = (cx - canvas->zoom_xofs)/zoom + canvas->scroll_x1;
3783 	if (wy)
3784 		*wy = (cy - canvas->zoom_yofs)/zoom + canvas->scroll_y1;
3785 }
3786 
3787 
3788 /**
3789  * eel_canvas_window_to_world:
3790  * @canvas: A canvas.
3791  * @winx: Window-relative X coordinate.
3792  * @winy: Window-relative Y coordinate.
3793  * @worldx: X world coordinate (return value).
3794  * @worldy: Y world coordinate (return value).
3795  *
3796  * Converts window-relative coordinates into world coordinates.  You can use
3797  * this when you need to convert mouse coordinates into world coordinates, for
3798  * example.
3799  * Window coordinates are really the same as canvas coordinates now, but this
3800  * function is here for backwards compatibility reasons.
3801  **/
3802 void
eel_canvas_window_to_world(EelCanvas * canvas,double winx,double winy,double * worldx,double * worldy)3803 eel_canvas_window_to_world (EelCanvas *canvas, double winx, double winy,
3804 			      double *worldx, double *worldy)
3805 {
3806 	g_return_if_fail (EEL_IS_CANVAS (canvas));
3807 
3808 	if (worldx)
3809 		*worldx = canvas->scroll_x1 + ((winx - canvas->zoom_xofs)
3810 					       / canvas->pixels_per_unit);
3811 
3812 	if (worldy)
3813 		*worldy = canvas->scroll_y1 + ((winy - canvas->zoom_yofs)
3814 					       / canvas->pixels_per_unit);
3815 }
3816 
3817 
3818 /**
3819  * eel_canvas_world_to_window:
3820  * @canvas: A canvas.
3821  * @worldx: World X coordinate.
3822  * @worldy: World Y coordinate.
3823  * @winx: X window-relative coordinate.
3824  * @winy: Y window-relative coordinate.
3825  *
3826  * Converts world coordinates into window-relative coordinates.
3827  * Window coordinates are really the same as canvas coordinates now, but this
3828  * function is here for backwards compatibility reasons.
3829  **/
3830 void
eel_canvas_world_to_window(EelCanvas * canvas,double worldx,double worldy,double * winx,double * winy)3831 eel_canvas_world_to_window (EelCanvas *canvas, double worldx, double worldy,
3832 			    double *winx, double *winy)
3833 {
3834 	g_return_if_fail (EEL_IS_CANVAS (canvas));
3835 
3836 	if (winx)
3837 		*winx = (canvas->pixels_per_unit)*(worldx - canvas->scroll_x1) + canvas->zoom_xofs;
3838 
3839 	if (winy)
3840 		*winy = (canvas->pixels_per_unit)*(worldy - canvas->scroll_y1) + canvas->zoom_yofs;
3841 }
3842 
3843 static gboolean
boolean_handled_accumulator(GSignalInvocationHint * ihint,GValue * return_accu,const GValue * handler_return,gpointer data)3844 boolean_handled_accumulator (GSignalInvocationHint *ihint,
3845 			     GValue                *return_accu,
3846 			     const GValue          *handler_return,
3847 			     gpointer               data)
3848 {
3849 	gboolean signal_handled;
3850 
3851 	signal_handled = g_value_get_boolean (handler_return);
3852 	g_value_set_boolean (return_accu, signal_handled);
3853 
3854 	return !signal_handled;
3855 }
3856 
3857 static guint
eel_canvas_item_accessible_add_focus_handler(AtkComponent * component,AtkFocusHandler handler)3858 eel_canvas_item_accessible_add_focus_handler (AtkComponent    *component,
3859                                               AtkFocusHandler handler)
3860 {
3861  	GSignalMatchType match_type;
3862 	guint signal_id;
3863 
3864 	match_type = G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC;
3865 	signal_id = g_signal_lookup ("focus-event", ATK_TYPE_OBJECT);
3866 
3867 	if (!g_signal_handler_find (component, match_type, signal_id, 0, NULL,
3868                                     (gpointer) handler, NULL)) {
3869 		return g_signal_connect_closure_by_id (component,
3870                                                        signal_id, 0,
3871                                                        g_cclosure_new (
3872                                                        G_CALLBACK (handler), NULL,
3873                                                        (GClosureNotify) NULL),
3874                                                        FALSE);
3875 	}
3876 	return 0;
3877 }
3878 
3879 static void
eel_canvas_item_accessible_get_item_extents(EelCanvasItem * item,GdkRectangle * rect)3880 eel_canvas_item_accessible_get_item_extents (EelCanvasItem *item,
3881                                              GdkRectangle  *rect)
3882 {
3883  	double bx1, bx2, by1, by2;
3884 	gint scroll_x, scroll_y;
3885 	gint x1, x2, y1, y2;
3886 
3887 	eel_canvas_item_get_bounds (item, &bx1, &by1, &bx2, &by2);
3888 	eel_canvas_w2c_rect_d (item->canvas, &bx1, &by1, &bx2, &by2);
3889 	eel_canvas_get_scroll_offsets (item->canvas, &scroll_x, &scroll_y);
3890 	x1 = floor (bx1 + .5);
3891 	y1 = floor (by1 + .5);
3892 	x2 = floor (bx2 + .5);
3893 	y2 = floor (by2 + .5);
3894 	rect->x = x1 - scroll_x;
3895 	rect->y = y1 - scroll_y;
3896 	rect->width = x2 - x1;
3897 	rect->height = y2 - y1;
3898 }
3899 
3900 static gboolean
eel_canvas_item_accessible_is_item_in_window(EelCanvasItem * item,GdkRectangle * rect)3901 eel_canvas_item_accessible_is_item_in_window (EelCanvasItem *item,
3902                                               GdkRectangle  *rect)
3903 {
3904  	GtkWidget *widget;
3905 	gboolean retval;
3906 
3907 	widget = GTK_WIDGET (item->canvas);
3908 	if (gtk_widget_get_window (widget)) {
3909 		int window_width, window_height;
3910 
3911 		gdk_window_get_geometry (gtk_widget_get_window (widget), NULL, NULL,
3912                                          &window_width, &window_height);
3913 		/*
3914                  * Check whether rectangles intersect
3915 		 */
3916                 if (rect->x + rect->width < 0 ||
3917                     rect->y + rect->height < 0 ||
3918                     rect->x > window_width  ||
3919                     rect->y > window_height) {
3920 			retval = FALSE;
3921 		} else {
3922                         retval = TRUE;
3923 		}
3924 	} else {
3925                 retval = FALSE;
3926 	}
3927         return retval;
3928 }
3929 
3930 
3931 static void
eel_canvas_item_accessible_get_extents(AtkComponent * component,gint * x,gint * y,gint * width,gint * height,AtkCoordType coord_type)3932 eel_canvas_item_accessible_get_extents (AtkComponent *component,
3933                                         gint		*x,
3934                                         gint		*y,
3935                                         gint		*width,
3936                                         gint		*height,
3937                                         AtkCoordType coord_type)
3938 {
3939 	GObject *obj;
3940 	EelCanvasItem *item;
3941 	gint window_x, window_y;
3942 	gint toplevel_x, toplevel_y;
3943 	GdkRectangle rect;
3944 	GdkWindow *window;
3945 	GtkWidget *canvas;
3946 
3947 	obj = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (component));
3948 
3949 	if (obj == NULL) {
3950 		/* item is defunct */
3951 		return;
3952 	}
3953 
3954         /* Get the CanvasItem */
3955 	item = EEL_CANVAS_ITEM (obj);
3956 
3957 	/* If this item has no parent canvas, something's broken */
3958 	g_return_if_fail (GTK_IS_WIDGET (item->canvas));
3959 
3960 	eel_canvas_item_accessible_get_item_extents (item, &rect);
3961 	*width = rect.width;
3962 	*height = rect.height;
3963 	if (!eel_canvas_item_accessible_is_item_in_window (item, &rect)) {
3964 		*x = G_MININT;
3965 		*y = G_MININT;
3966 		return;
3967 	}
3968 
3969         canvas = GTK_WIDGET (item->canvas);
3970 	window = gtk_widget_get_parent_window (canvas);
3971 	gdk_window_get_origin (window, &window_x, &window_y);
3972 	*x = rect.x + window_x;
3973 	*y = rect.y + window_y;
3974 	if (coord_type == ATK_XY_WINDOW) {
3975 		window = gdk_window_get_toplevel (gtk_widget_get_window (canvas));
3976 		gdk_window_get_origin (window, &toplevel_x, &toplevel_y);
3977 		*x -= toplevel_x;
3978 		*y -= toplevel_y;
3979 	}
3980         return;
3981 }
3982 
3983 static gint
eel_canvas_item_accessible_get_mdi_zorder(AtkComponent * component)3984 eel_canvas_item_accessible_get_mdi_zorder (AtkComponent *component)
3985 {
3986 	GObject *g_obj;
3987 	EelCanvasItem *item;
3988 
3989 	g_obj = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (component));
3990 	if (g_obj == NULL) {
3991 		/* Object is defunct */
3992 		return -1;
3993 	}
3994 
3995 	item = EEL_CANVAS_ITEM (g_obj);
3996 	if (item->parent) {
3997        		return g_list_index (EEL_CANVAS_GROUP (item->parent)->item_list, item);
3998 	} else {
3999 		g_return_val_if_fail (item->canvas->root == item, -1);
4000 		return 0;
4001 	}
4002 }
4003 
4004 static gboolean
eel_canvas_item_accessible_grab_focus(AtkComponent * component)4005 eel_canvas_item_accessible_grab_focus (AtkComponent *component)
4006 {
4007 	GObject *obj;
4008 	EelCanvasItem *item;
4009 	GtkWidget *toplevel;
4010 
4011 	obj = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (component));
4012 
4013 	item = EEL_CANVAS_ITEM (obj);
4014 	if (item == NULL) {
4015 		/* item is defunct */
4016 		return FALSE;
4017 	}
4018 
4019         eel_canvas_item_grab_focus (item);
4020 	toplevel = gtk_widget_get_toplevel (GTK_WIDGET (item->canvas));
4021 	if (gtk_widget_is_toplevel (toplevel)) {
4022 		gtk_window_present (GTK_WINDOW (toplevel));
4023 	}
4024 
4025 	return TRUE;
4026 }
4027 
4028 static void
eel_canvas_item_accessible_remove_focus_handler(AtkComponent * component,guint handler_id)4029 eel_canvas_item_accessible_remove_focus_handler (AtkComponent *component,
4030                                                  guint		handler_id)
4031 {
4032  	g_signal_handler_disconnect (component, handler_id);
4033 }
4034 
4035 static void
eel_canvas_item_accessible_component_interface_init(AtkComponentIface * iface)4036 eel_canvas_item_accessible_component_interface_init (AtkComponentIface *iface)
4037 {
4038 	g_return_if_fail (iface != NULL);
4039 
4040 	iface->add_focus_handler = eel_canvas_item_accessible_add_focus_handler;
4041 	iface->get_extents = eel_canvas_item_accessible_get_extents;
4042 	iface->get_mdi_zorder = eel_canvas_item_accessible_get_mdi_zorder;
4043 	iface->grab_focus = eel_canvas_item_accessible_grab_focus;
4044       	iface->remove_focus_handler = eel_canvas_item_accessible_remove_focus_handler;
4045 }
4046 
4047 static gboolean
eel_canvas_item_accessible_is_item_on_screen(EelCanvasItem * item)4048 eel_canvas_item_accessible_is_item_on_screen (EelCanvasItem *item)
4049 {
4050 	GdkRectangle rect;
4051 
4052 	eel_canvas_item_accessible_get_item_extents (item, &rect);
4053 	return eel_canvas_item_accessible_is_item_in_window (item, &rect);
4054 }
4055 
4056 static void
eel_canvas_item_accessible_initialize(AtkObject * obj,gpointer data)4057 eel_canvas_item_accessible_initialize (AtkObject *obj, gpointer data)
4058 {
4059 	if (ATK_OBJECT_CLASS (accessible_item_parent_class)->initialize != NULL)
4060 		ATK_OBJECT_CLASS (accessible_item_parent_class)->initialize (obj, data);
4061 	g_object_set_data (G_OBJECT (obj), "atk-component-layer",
4062 			   GINT_TO_POINTER (ATK_LAYER_MDI));
4063 }
4064 
4065 static AtkStateSet*
eel_canvas_item_accessible_ref_state_set(AtkObject * accessible)4066 eel_canvas_item_accessible_ref_state_set (AtkObject *accessible)
4067 {
4068 	GObject *obj;
4069  	EelCanvasItem *item;
4070 	AtkStateSet *state_set;
4071 
4072 	state_set = ATK_OBJECT_CLASS (accessible_item_parent_class)->ref_state_set (accessible);
4073 	obj = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (accessible));
4074 
4075 	item = EEL_CANVAS_ITEM (obj);
4076 	if (item == NULL) {
4077 		atk_state_set_add_state (state_set, ATK_STATE_DEFUNCT);
4078 	} else {
4079                 if (item->flags & EEL_CANVAS_ITEM_VISIBLE) {
4080 			atk_state_set_add_state (state_set, ATK_STATE_VISIBLE);
4081 
4082 			if (eel_canvas_item_accessible_is_item_on_screen (item)) {
4083   				atk_state_set_add_state (state_set, ATK_STATE_SHOWING);
4084        			}
4085 		}
4086         	if (gtk_widget_get_can_focus (GTK_WIDGET (item->canvas))) {
4087 			atk_state_set_add_state (state_set, ATK_STATE_FOCUSABLE);
4088 
4089 			if (item->canvas->focused_item == item) {
4090 				atk_state_set_add_state (state_set, ATK_STATE_FOCUSED);
4091 			}
4092 		}
4093 	}
4094 
4095         return state_set;
4096 }
4097 
4098 static void
eel_canvas_item_accessible_class_init(AtkObjectClass * klass)4099 eel_canvas_item_accessible_class_init (AtkObjectClass *klass)
4100 {
4101  	accessible_item_parent_class = g_type_class_peek_parent (klass);
4102 
4103 	klass->initialize = eel_canvas_item_accessible_initialize;
4104 	klass->ref_state_set = eel_canvas_item_accessible_ref_state_set;
4105 }
4106 
4107 static GType
eel_canvas_item_accessible_get_type(void)4108 eel_canvas_item_accessible_get_type (void)
4109 {
4110 	static GType type = 0;
4111 
4112 	if (!type) {
4113 		static const GInterfaceInfo atk_component_info = {
4114 			(GInterfaceInitFunc) eel_canvas_item_accessible_component_interface_init,
4115                  	(GInterfaceFinalizeFunc) NULL,
4116 			NULL
4117 		};
4118 		AtkObjectFactory *factory;
4119 		GType parent_atk_type;
4120 		GTypeQuery query;
4121 		GTypeInfo tinfo = { 0 };
4122 
4123 		factory = atk_registry_get_factory (atk_get_default_registry(),
4124 						    G_TYPE_INITIALLY_UNOWNED);
4125 		if (!factory) {
4126 			return G_TYPE_INVALID;
4127 		}
4128 		parent_atk_type = atk_object_factory_get_accessible_type (factory);
4129 		if (!parent_atk_type) {
4130 			return G_TYPE_INVALID;
4131 		}
4132 		g_type_query (parent_atk_type, &query);
4133 		tinfo.class_init = (GClassInitFunc) eel_canvas_item_accessible_class_init;
4134 		tinfo.class_size = query.class_size;
4135 		tinfo.instance_size = query.instance_size;
4136 		type = g_type_register_static (parent_atk_type,
4137 					       "EelCanvasItemAccessibility",
4138 					       &tinfo, 0);
4139 
4140 		g_type_add_interface_static (type, ATK_TYPE_COMPONENT,
4141 					     &atk_component_info);
4142 
4143 	}
4144 	return type;
4145 }
4146 
4147 static AtkObject *
eel_canvas_item_accessible_create(GObject * for_object)4148 eel_canvas_item_accessible_create (GObject *for_object)
4149 {
4150 	GType type;
4151 	AtkObject *accessible;
4152 	EelCanvasItem *item;
4153 
4154 	item = EEL_CANVAS_ITEM (for_object);
4155 	g_return_val_if_fail (item != NULL, NULL);
4156 
4157 	type = eel_canvas_item_accessible_get_type ();
4158 	if (type == G_TYPE_INVALID) {
4159 		return atk_no_op_object_new (for_object);
4160 	}
4161 
4162         accessible = g_object_new (type, NULL);
4163 	atk_object_initialize (accessible, for_object);
4164 	return accessible;
4165 }
4166 
4167 static GType
eel_canvas_item_accessible_factory_get_accessible_type(void)4168 eel_canvas_item_accessible_factory_get_accessible_type (void)
4169 {
4170 	return eel_canvas_item_accessible_get_type ();
4171 }
4172 
4173 static AtkObject*
eel_canvas_item_accessible_factory_create_accessible(GObject * obj)4174 eel_canvas_item_accessible_factory_create_accessible (GObject *obj)
4175 {
4176 	g_return_val_if_fail (G_IS_OBJECT (obj), NULL);
4177 
4178 	return eel_canvas_item_accessible_create (obj);
4179 }
4180 
4181 static void
eel_canvas_item_accessible_factory_class_init(AtkObjectFactoryClass * klass)4182 eel_canvas_item_accessible_factory_class_init (AtkObjectFactoryClass *klass)
4183 {
4184 	klass->create_accessible = eel_canvas_item_accessible_factory_create_accessible;
4185 	klass->get_accessible_type = eel_canvas_item_accessible_factory_get_accessible_type;
4186 }
4187 
4188 static GType
eel_canvas_item_accessible_factory_get_type(void)4189 eel_canvas_item_accessible_factory_get_type (void)
4190 {
4191 	static GType type = 0;
4192 
4193 	if (!type) {
4194 		static const GTypeInfo tinfo = {
4195 			sizeof (AtkObjectFactoryClass),
4196 			(GBaseInitFunc) NULL,
4197 			(GBaseFinalizeFunc) NULL,
4198 			(GClassInitFunc) eel_canvas_item_accessible_factory_class_init,
4199 			NULL,		/* class_finalize */
4200 			NULL,		/* class_data */
4201 			sizeof (AtkObjectFactory),
4202 			0,		/* n_preallocs */
4203 			NULL
4204 		};
4205 		type = g_type_register_static (ATK_TYPE_OBJECT_FACTORY,
4206 					       "EelCanvasItemAccessibilityFactory",
4207 					       &tinfo, 0);
4208 	}
4209 	return type;
4210 }
4211 
4212 /* Class initialization function for EelCanvasItemClass */
4213 static void
eel_canvas_item_class_init(EelCanvasItemClass * klass)4214 eel_canvas_item_class_init (EelCanvasItemClass *klass)
4215 {
4216 	GObjectClass *gobject_class = (GObjectClass *) klass;
4217 
4218 	item_parent_class = g_type_class_peek_parent (klass);
4219 
4220 	gobject_class->set_property = eel_canvas_item_set_property;
4221 	gobject_class->get_property = eel_canvas_item_get_property;
4222 	gobject_class->dispose = eel_canvas_item_dispose;
4223 
4224 	g_object_class_install_property
4225 		(gobject_class, ITEM_PROP_PARENT,
4226 		 g_param_spec_object ("parent", NULL, NULL,
4227 				      EEL_TYPE_CANVAS_ITEM,
4228 				      G_PARAM_READWRITE));
4229 
4230 	g_object_class_install_property
4231 		(gobject_class, ITEM_PROP_VISIBLE,
4232 		 g_param_spec_boolean ("visible", NULL, NULL,
4233 				      TRUE,
4234 				      G_PARAM_READWRITE));
4235 
4236 	item_signals[ITEM_EVENT] =
4237 		g_signal_new ("event",
4238 			      G_TYPE_FROM_CLASS (klass),
4239 			      G_SIGNAL_RUN_LAST,
4240 			      G_STRUCT_OFFSET (EelCanvasItemClass, event),
4241 			      boolean_handled_accumulator, NULL,
4242 			      g_cclosure_marshal_generic,
4243 			      G_TYPE_BOOLEAN, 1,
4244 			      GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
4245 
4246         item_signals[ITEM_DESTROY] =
4247 		g_signal_new ("destroy",
4248 			      G_TYPE_FROM_CLASS (klass),
4249 			      G_SIGNAL_RUN_CLEANUP | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
4250 			      G_STRUCT_OFFSET (EelCanvasItemClass, destroy),
4251 			      NULL, NULL,
4252 			      g_cclosure_marshal_VOID__VOID,
4253 			      G_TYPE_NONE, 0);
4254 
4255 	klass->realize = eel_canvas_item_realize;
4256 	klass->unrealize = eel_canvas_item_unrealize;
4257 	klass->map = eel_canvas_item_map;
4258 	klass->unmap = eel_canvas_item_unmap;
4259 	klass->update = eel_canvas_item_update;
4260 
4261 	atk_registry_set_factory_type (atk_get_default_registry (),
4262                                        EEL_TYPE_CANVAS_ITEM,
4263                                        eel_canvas_item_accessible_factory_get_type ());
4264 }
4265