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., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23 /*
24   @NOTATION@
25  */
26 /*
27  * GnomeCanvas widget - Tk-like canvas widget for Gnome
28  *
29  * GnomeCanvas 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 GnomeCanvasImage sizes are in units or pixels (scale or don't scale).
41  *
42  * - Implement a flag for gnome_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 *gnome_canvas_gimme_all_items_contained_in_this_area (GnomeCanvas *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 GnomeCanvasEllipse).
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 /*
65  * Raph's TODO list for the antialiased canvas integration:
66  *
67  * - ::point() method for text item not accurate when affine transformed.
68  *
69  * - Clip rectangle not implemented in aa renderer for text item.
70  *
71  * - Clip paths only partially implemented.
72  *
73  * - Add more image loading techniques to work around imlib deficiencies.
74  */
75 
76 #include <config.h>
77 
78 #include <math.h>
79 #include <string.h>
80 #include <stdio.h>
81 #include <gdk/gdkprivate.h>
82 #include <gtk/gtk.h>
83 #include "gailcanvas.h"
84 #include "gnome-canvas.h"
85 #include "gnome-canvas-i18n.h"
86 #include "libart_lgpl/art_rect.h"
87 #include "libart_lgpl/art_rect_uta.h"
88 #include "libart_lgpl/art_uta_rect.h"
89 #include "libart_lgpl/art_uta_ops.h"
90 
91 #include "gnome-canvas-marshal.h"
92 #include "gnome-canvas-marshal.c"
93 
94 
95 /* We must run our idle update handler *before* GDK wants to redraw. */
96 #define CANVAS_IDLE_PRIORITY (GDK_PRIORITY_REDRAW - 5)
97 
98 
99 static void gnome_canvas_request_update (GnomeCanvas      *canvas);
100 static void group_add                   (GnomeCanvasGroup *group,
101 					 GnomeCanvasItem  *item);
102 static void group_remove                (GnomeCanvasGroup *group,
103 					 GnomeCanvasItem  *item);
104 static void add_idle                    (GnomeCanvas      *canvas);
105 
106 
107 /*** GnomeCanvasItem ***/
108 
109 /* Some convenience stuff */
110 #define GCI_UPDATE_MASK (GNOME_CANVAS_UPDATE_REQUESTED | GNOME_CANVAS_UPDATE_AFFINE | GNOME_CANVAS_UPDATE_CLIP | GNOME_CANVAS_UPDATE_VISIBILITY)
111 #define GCI_EPSILON 1e-18
112 #define GCI_PRINT_MATRIX(s,a) g_print ("%s %g %g %g %g %g %g\n", s, (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5])
113 
114 enum {
115 	ITEM_PROP_0,
116 	ITEM_PROP_PARENT
117 };
118 
119 enum {
120 	ITEM_EVENT,
121 	ITEM_LAST_SIGNAL
122 };
123 
124 static void gnome_canvas_item_class_init     (GnomeCanvasItemClass *class);
125 static void gnome_canvas_item_init           (GnomeCanvasItem      *item);
126 static int  emit_event                       (GnomeCanvas *canvas, GdkEvent *event);
127 
128 static guint item_signals[ITEM_LAST_SIGNAL];
129 
130 static GtkObjectClass *item_parent_class;
131 
132 
133 /**
134  * gnome_canvas_item_get_type:
135  *
136  * Registers the &GnomeCanvasItem class if necessary, and returns the type ID
137  * associated to it.
138  *
139  * Return value:  The type ID of the &GnomeCanvasItem class.
140  **/
141 GType
gnome_canvas_item_get_type(void)142 gnome_canvas_item_get_type (void)
143 {
144 	static GType canvas_item_type;
145 
146 	if (!canvas_item_type) {
147 		const GTypeInfo object_info = {
148 			sizeof (GnomeCanvasItemClass),
149 			(GBaseInitFunc) NULL,
150 			(GBaseFinalizeFunc) NULL,
151 			(GClassInitFunc) gnome_canvas_item_class_init,
152 			(GClassFinalizeFunc) NULL,
153 			NULL,			/* class_data */
154 			sizeof (GnomeCanvasItem),
155 			0,			/* n_preallocs */
156 			(GInstanceInitFunc) gnome_canvas_item_init,
157 			NULL			/* value_table */
158 		};
159 
160 		canvas_item_type = g_type_register_static (GTK_TYPE_OBJECT, "GnomeCanvasItem",
161 							   &object_info, 0);
162 	}
163 
164 	return canvas_item_type;
165 }
166 
167 /* Object initialization function for GnomeCanvasItem */
168 static void
gnome_canvas_item_init(GnomeCanvasItem * item)169 gnome_canvas_item_init (GnomeCanvasItem *item)
170 {
171 	item->object.flags |= GNOME_CANVAS_ITEM_VISIBLE;
172 }
173 
174 /**
175  * gnome_canvas_item_new:
176  * @parent: The parent group for the new item.
177  * @type: The object type of the item.
178  * @first_arg_name: A list of object argument name/value pairs, NULL-terminated,
179  * used to configure the item.  For example, "fill_color", "black",
180  * "width_units", 5.0, NULL.
181  * @Varargs:
182  *
183  * Creates a new canvas item with @parent as its parent group.  The item is
184  * created at the top of its parent's stack, and starts up as visible.  The item
185  * is of the specified @type, for example, it can be
186  * gnome_canvas_rect_get_type().  The list of object arguments/value pairs is
187  * used to configure the item. If you need to pass construct time parameters, you
188  * should use g_object_new() to pass the parameters and
189  * gnome_canvas_item_construct() to set up the canvas item.
190  *
191  * Return value: The newly-created item.
192  **/
193 GnomeCanvasItem *
gnome_canvas_item_new(GnomeCanvasGroup * parent,GType type,const gchar * first_arg_name,...)194 gnome_canvas_item_new (GnomeCanvasGroup *parent, GType type, const gchar *first_arg_name, ...)
195 {
196 	GnomeCanvasItem *item;
197 	va_list args;
198 
199 	g_return_val_if_fail (GNOME_IS_CANVAS_GROUP (parent), NULL);
200 	g_return_val_if_fail (g_type_is_a (type, gnome_canvas_item_get_type ()), NULL);
201 
202 	item = GNOME_CANVAS_ITEM (g_object_new (type, NULL));
203 
204 	va_start (args, first_arg_name);
205 	gnome_canvas_item_construct (item, parent, first_arg_name, args);
206 	va_end (args);
207 
208 	return item;
209 }
210 
211 
212 /* Performs post-creation operations on a canvas item (adding it to its parent
213  * group, etc.)
214  */
215 static void
item_post_create_setup(GnomeCanvasItem * item)216 item_post_create_setup (GnomeCanvasItem *item)
217 {
218 	group_add (GNOME_CANVAS_GROUP (item->parent), item);
219 
220 	gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2 + 1, item->y2 + 1);
221 	item->canvas->need_repick = TRUE;
222 }
223 
224 /* Set_property handler for canvas items */
225 static void
gnome_canvas_item_set_property(GObject * gobject,guint param_id,const GValue * value,GParamSpec * pspec)226 gnome_canvas_item_set_property (GObject *gobject, guint param_id,
227 				const GValue *value, GParamSpec *pspec)
228 {
229 	GnomeCanvasItem *item;
230 
231 	g_return_if_fail (GNOME_IS_CANVAS_ITEM (gobject));
232 
233 	item = GNOME_CANVAS_ITEM (gobject);
234 
235 	switch (param_id) {
236 	case ITEM_PROP_PARENT:
237 		if (item->parent != NULL) {
238 		    g_warning ("Cannot set `parent' argument after item has "
239 			       "already been constructed.");
240 		} else if (g_value_get_object (value)) {
241 			item->parent = GNOME_CANVAS_ITEM (g_value_get_object (value));
242 			item->canvas = item->parent->canvas;
243 			item_post_create_setup (item);
244 		}
245 		break;
246 	default:
247 		G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
248 		break;
249 	}
250 }
251 
252 /* Get_property handler for canvas items */
253 static void
gnome_canvas_item_get_property(GObject * gobject,guint param_id,GValue * value,GParamSpec * pspec)254 gnome_canvas_item_get_property (GObject *gobject, guint param_id,
255 				GValue *value, GParamSpec *pspec)
256 {
257 	GnomeCanvasItem *item;
258 
259 	g_return_if_fail (GNOME_IS_CANVAS_ITEM (gobject));
260 
261 	item = GNOME_CANVAS_ITEM (gobject);
262 
263 	switch (param_id) {
264 	case ITEM_PROP_PARENT:
265 		g_value_set_object (value, item->parent);
266 		break;
267 
268 	default:
269 		G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
270 		break;
271 	}
272 }
273 
274 /**
275  * gnome_canvas_item_construct:
276  * @item: An unconstructed canvas item.
277  * @parent: The parent group for the item.
278  * @first_arg_name: The name of the first argument for configuring the item.
279  * @args: The list of arguments used to configure the item.
280  *
281  * Constructs a canvas item; meant for use only by item implementations.
282  **/
283 void
gnome_canvas_item_construct(GnomeCanvasItem * item,GnomeCanvasGroup * parent,const gchar * first_arg_name,va_list args)284 gnome_canvas_item_construct (GnomeCanvasItem *item, GnomeCanvasGroup *parent,
285 			     const gchar *first_arg_name, va_list args)
286 {
287 	g_return_if_fail (GNOME_IS_CANVAS_GROUP (parent));
288 	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
289 
290 	item->parent = GNOME_CANVAS_ITEM (parent);
291 	item->canvas = item->parent->canvas;
292 
293 	g_object_set_valist (G_OBJECT (item), first_arg_name, args);
294 
295 	item_post_create_setup (item);
296 }
297 
298 
299 /* If the item is visible, requests a redraw of it. */
300 static void
redraw_if_visible(GnomeCanvasItem * item)301 redraw_if_visible (GnomeCanvasItem *item)
302 {
303 	if (item->object.flags & GNOME_CANVAS_ITEM_VISIBLE)
304 		gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2 + 1, item->y2 + 1);
305 }
306 
307 /* Standard object dispose function for canvas items */
308 static void
gnome_canvas_item_dispose(GObject * object)309 gnome_canvas_item_dispose (GObject *object)
310 {
311 	GnomeCanvasItem *item;
312 
313 	g_return_if_fail (GNOME_IS_CANVAS_ITEM (object));
314 
315 	item = GNOME_CANVAS_ITEM (object);
316 
317 	if (item->canvas)
318 		redraw_if_visible (item);
319 
320 	/* Make the canvas forget about us */
321 
322 	if (item->canvas && item == item->canvas->current_item) {
323 		item->canvas->current_item = NULL;
324 		item->canvas->need_repick = TRUE;
325 	}
326 
327 	if (item->canvas && item == item->canvas->new_current_item) {
328 		item->canvas->new_current_item = NULL;
329 		item->canvas->need_repick = TRUE;
330 	}
331 
332 	if (item->canvas && item == item->canvas->grabbed_item) {
333 		item->canvas->grabbed_item = NULL;
334 		gdk_pointer_ungrab (GDK_CURRENT_TIME);
335 	}
336 
337 	if (item->canvas && item == item->canvas->focused_item)
338 		item->canvas->focused_item = NULL;
339 
340 	/* Normal destroy stuff */
341 
342 	if (item->object.flags & GNOME_CANVAS_ITEM_MAPPED)
343 		(* GNOME_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
344 
345 	if (item->object.flags & GNOME_CANVAS_ITEM_REALIZED)
346 		(* GNOME_CANVAS_ITEM_GET_CLASS (item)->unrealize) (item);
347 
348 	if (item->parent)
349 		group_remove (GNOME_CANVAS_GROUP (item->parent), item);
350 
351 	g_free (item->xform);
352 	item->xform = NULL;
353 
354 	G_OBJECT_CLASS (item_parent_class)->dispose (object);
355 	/* items should remove any reference to item->canvas after the
356 	   first ::destroy */
357 	item->canvas = NULL;
358 }
359 
360 /* Realize handler for canvas items */
361 static void
gnome_canvas_item_realize(GnomeCanvasItem * item)362 gnome_canvas_item_realize (GnomeCanvasItem *item)
363 {
364 	GTK_OBJECT_SET_FLAGS (item, GNOME_CANVAS_ITEM_REALIZED);
365 
366 	gnome_canvas_item_request_update (item);
367 }
368 
369 /* Unrealize handler for canvas items */
370 static void
gnome_canvas_item_unrealize(GnomeCanvasItem * item)371 gnome_canvas_item_unrealize (GnomeCanvasItem *item)
372 {
373 	GTK_OBJECT_UNSET_FLAGS (item, GNOME_CANVAS_ITEM_REALIZED);
374 }
375 
376 /* Map handler for canvas items */
377 static void
gnome_canvas_item_map(GnomeCanvasItem * item)378 gnome_canvas_item_map (GnomeCanvasItem *item)
379 {
380 	GTK_OBJECT_SET_FLAGS (item, GNOME_CANVAS_ITEM_MAPPED);
381 }
382 
383 /* Unmap handler for canvas items */
384 static void
gnome_canvas_item_unmap(GnomeCanvasItem * item)385 gnome_canvas_item_unmap (GnomeCanvasItem *item)
386 {
387 	GTK_OBJECT_UNSET_FLAGS (item, GNOME_CANVAS_ITEM_MAPPED);
388 }
389 
390 /* Update handler for canvas items */
391 static void
gnome_canvas_item_update(GnomeCanvasItem * item,double * affine,ArtSVP * clip_path,int flags)392 gnome_canvas_item_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
393 {
394 	GTK_OBJECT_UNSET_FLAGS (item, GNOME_CANVAS_ITEM_NEED_UPDATE);
395 	GTK_OBJECT_UNSET_FLAGS (item, GNOME_CANVAS_ITEM_NEED_AFFINE);
396 	GTK_OBJECT_UNSET_FLAGS (item, GNOME_CANVAS_ITEM_NEED_CLIP);
397 	GTK_OBJECT_UNSET_FLAGS (item, GNOME_CANVAS_ITEM_NEED_VIS);
398 }
399 
400 #define noHACKISH_AFFINE
401 
402 /*
403  * This routine invokes the update method of the item
404  * Please notice, that we take parent to canvas pixel matrix as argument
405  * unlike virtual method ::update, whose argument is item 2 canvas pixel
406  * matrix
407  *
408  * I will try to force somewhat meaningful naming for affines (Lauris)
409  * General naming rule is FROM2TO, where FROM and TO are abbreviations
410  * So p2cpx is Parent2CanvasPixel and i2cpx is Item2CanvasPixel
411  * I hope that this helps to keep track of what really happens
412  *
413  */
414 
415 static void
gnome_canvas_item_invoke_update(GnomeCanvasItem * item,double * p2cpx,ArtSVP * clip_path,int flags)416 gnome_canvas_item_invoke_update (GnomeCanvasItem *item, double *p2cpx, ArtSVP *clip_path, int flags)
417 {
418 	int child_flags;
419 	gdouble i2cpx[6];
420 
421 #ifdef HACKISH_AFFINE
422 	double i2w[6], w2c[6], i2c[6];
423 #endif
424 
425 	child_flags = flags;
426 	if (!(item->object.flags & GNOME_CANVAS_ITEM_VISIBLE))
427 		child_flags &= ~GNOME_CANVAS_UPDATE_IS_VISIBLE;
428 
429 	/* Calculate actual item transformation matrix */
430 
431 	if (item->xform) {
432 		if (item->object.flags & GNOME_CANVAS_ITEM_AFFINE_FULL) {
433 			/* Item has full affine */
434 			art_affine_multiply (i2cpx, item->xform, p2cpx);
435 		} else {
436 			/* Item has only translation */
437 			memcpy (i2cpx, p2cpx, 4 * sizeof (gdouble));
438 			i2cpx[4] = item->xform[0] * p2cpx[0] + item->xform[1] * p2cpx[2] + p2cpx[4];
439 			i2cpx[5] = item->xform[0] * p2cpx[1] + item->xform[1] * p2cpx[3] + p2cpx[5];
440 		}
441 	} else {
442 		/* Item has no matrix (i.e. identity) */
443 		memcpy (i2cpx, p2cpx, 6 * sizeof (gdouble));
444 	}
445 
446 #ifdef HACKISH_AFFINE
447 	gnome_canvas_item_i2w_affine (item, i2w);
448 	gnome_canvas_w2c_affine (item->canvas, w2c);
449 	art_affine_multiply (i2c, i2w, w2c);
450 	/* invariant (doesn't hold now): child_affine == i2c */
451 	child_affine = i2c;
452 #endif
453 
454 	/* apply object flags to child flags */
455 
456 	child_flags &= ~GNOME_CANVAS_UPDATE_REQUESTED;
457 
458 	if (item->object.flags & GNOME_CANVAS_ITEM_NEED_UPDATE)
459 		child_flags |= GNOME_CANVAS_UPDATE_REQUESTED;
460 
461 	if (item->object.flags & GNOME_CANVAS_ITEM_NEED_AFFINE)
462 		child_flags |= GNOME_CANVAS_UPDATE_AFFINE;
463 
464 	if (item->object.flags & GNOME_CANVAS_ITEM_NEED_CLIP)
465 		child_flags |= GNOME_CANVAS_UPDATE_CLIP;
466 
467 	if (item->object.flags & GNOME_CANVAS_ITEM_NEED_VIS)
468 		child_flags |= GNOME_CANVAS_UPDATE_VISIBILITY;
469 
470 	if (child_flags & GCI_UPDATE_MASK) {
471 		if (GNOME_CANVAS_ITEM_GET_CLASS (item)->update)
472 			GNOME_CANVAS_ITEM_GET_CLASS (item)->update (item, i2cpx, clip_path, child_flags);
473 	}
474 }
475 
476 /*
477  * This routine invokes the point method of the item.
478  * The arguments x, y should be in the parent item local coordinates.
479  *
480  * This is potentially evil, as we are relying on matrix inversion (Lauris)
481  */
482 
483 static double
gnome_canvas_item_invoke_point(GnomeCanvasItem * item,double x,double y,int cx,int cy,GnomeCanvasItem ** actual_item)484 gnome_canvas_item_invoke_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, GnomeCanvasItem **actual_item)
485 {
486 	/* Calculate x & y in item local coordinates */
487 
488 	if (item->xform) {
489 		if (item->object.flags & GNOME_CANVAS_ITEM_AFFINE_FULL) {
490 			gdouble p2i[6], t;
491 			/* Item has full affine */
492 			art_affine_invert (p2i, item->xform);
493 			t = x * p2i[0] + y * p2i[2] + p2i[4];
494 			y = x * p2i[1] + y * p2i[3] + p2i[5];
495 			x = t;
496 		} else {
497 			/* Item has only translation */
498 			x -= item->xform[0];
499 			y -= item->xform[1];
500 		}
501 	}
502 
503 #ifdef HACKISH_AFFINE
504 	double i2w[6], w2c[6], i2c[6], c2i[6];
505 	ArtPoint c, i;
506 #endif
507 
508 #ifdef HACKISH_AFFINE
509 	gnome_canvas_item_i2w_affine (item, i2w);
510 	gnome_canvas_w2c_affine (item->canvas, w2c);
511 	art_affine_multiply (i2c, i2w, w2c);
512 	art_affine_invert (c2i, i2c);
513 	c.x = cx;
514 	c.y = cy;
515 	art_affine_point (&i, &c, c2i);
516 	x = i.x;
517 	y = i.y;
518 #endif
519 
520 	if (GNOME_CANVAS_ITEM_GET_CLASS (item)->point)
521 		return GNOME_CANVAS_ITEM_GET_CLASS (item)->point (item, x, y, cx, cy, actual_item);
522 
523 	return 1e18;
524 }
525 
526 /**
527  * gnome_canvas_item_set:
528  * @item: A canvas item.
529  * @first_arg_name: The list of object argument name/value pairs used to configure the item.
530  * @Varargs:
531  *
532  * Configures a canvas item.  The arguments in the item are set to the specified
533  * values, and the item is repainted as appropriate.
534  **/
535 void
gnome_canvas_item_set(GnomeCanvasItem * item,const gchar * first_arg_name,...)536 gnome_canvas_item_set (GnomeCanvasItem *item, const gchar *first_arg_name, ...)
537 {
538 	va_list args;
539 
540 	va_start (args, first_arg_name);
541 	gnome_canvas_item_set_valist (item, first_arg_name, args);
542 	va_end (args);
543 }
544 
545 
546 /**
547  * gnome_canvas_item_set_valist:
548  * @item: A canvas item.
549  * @first_arg_name: The name of the first argument used to configure the item.
550  * @args: The list of object argument name/value pairs used to configure the item.
551  *
552  * Configures a canvas item.  The arguments in the item are set to the specified
553  * values, and the item is repainted as appropriate.
554  **/
555 void
gnome_canvas_item_set_valist(GnomeCanvasItem * item,const gchar * first_arg_name,va_list args)556 gnome_canvas_item_set_valist (GnomeCanvasItem *item, const gchar *first_arg_name, va_list args)
557 {
558 	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
559 
560 	g_object_set_valist (G_OBJECT (item), first_arg_name, args);
561 
562 #if 0
563 	/* I commented this out, because item implementations have to schedule update/redraw */
564 	redraw_if_visible (item);
565 #endif
566 
567 	item->canvas->need_repick = TRUE;
568 }
569 
570 
571 /**
572  * gnome_canvas_item_affine_relative:
573  * @item: A canvas item.
574  * @affine: An affine transformation matrix.
575  *
576  * Combines the specified affine transformation matrix with the item's current
577  * transformation. NULL affine is not allowed.
578  **/
579 #define GCIAR_EPSILON 1e-6
580 void
gnome_canvas_item_affine_relative(GnomeCanvasItem * item,const double affine[6])581 gnome_canvas_item_affine_relative (GnomeCanvasItem *item, const double affine[6])
582 {
583 	gdouble i2p[6];
584 
585 	g_return_if_fail (item != NULL);
586 	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
587 	g_return_if_fail (affine != NULL);
588 
589 	/* Calculate actual item transformation matrix */
590 
591 	if (item->xform) {
592 		if (item->object.flags & GNOME_CANVAS_ITEM_AFFINE_FULL) {
593 			/* Item has full affine */
594 			art_affine_multiply (i2p, affine, item->xform);
595 		} else {
596 			/* Item has only translation */
597 			memcpy (i2p, affine, 6 * sizeof (gdouble));
598 			i2p[4] += item->xform[0];
599 			i2p[5] += item->xform[1];
600 		}
601 	} else {
602 		/* Item has no matrix (i.e. identity) */
603 		memcpy (i2p, affine, 6 * sizeof (gdouble));
604 	}
605 
606 	gnome_canvas_item_affine_absolute (item, i2p);
607 }
608 
609 /**
610  * gnome_canvas_item_affine_absolute:
611  * @item: A canvas item.
612  * @affine: An affine transformation matrix.
613  *
614  * Makes the item's affine transformation matrix be equal to the specified
615  * matrix. NULL affine is treated as identity.
616  **/
617 void
gnome_canvas_item_affine_absolute(GnomeCanvasItem * item,const double i2p[6])618 gnome_canvas_item_affine_absolute (GnomeCanvasItem *item, const double i2p[6])
619 {
620 	g_return_if_fail (item != NULL);
621 	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
622 
623 	if (i2p &&
624 	    (fabs (i2p[0] - 1.0) < GCI_EPSILON) &&
625 	    (fabs (i2p[1] - 0.0) < GCI_EPSILON) &&
626 	    (fabs (i2p[2] - 0.0) < GCI_EPSILON) &&
627 	    (fabs (i2p[3] - 1.0) < GCI_EPSILON) &&
628 	    (fabs (i2p[4] - 0.0) < GCI_EPSILON) &&
629 	    (fabs (i2p[5] - 0.0) < GCI_EPSILON)) {
630 		/* We are identity */
631 		i2p = NULL;
632 	}
633 
634 	if (i2p) {
635 		if (item->xform && !(item->object.flags & GNOME_CANVAS_ITEM_AFFINE_FULL)) {
636 			/* We do not want to deal with translation-only affines */
637 			g_free (item->xform);
638 			item->xform = NULL;
639 		}
640 		if (!item->xform) item->xform = g_new (gdouble, 6);
641 		memcpy (item->xform, i2p, 6 * sizeof (gdouble));
642 		item->object.flags |= GNOME_CANVAS_ITEM_AFFINE_FULL;
643 	} else {
644 		if (item->xform) {
645 			g_free (item->xform);
646 			item->xform = NULL;
647 		}
648 	}
649 
650 	if (!(item->object.flags & GNOME_CANVAS_ITEM_NEED_AFFINE)) {
651 		/* Request update */
652 		item->object.flags |= GNOME_CANVAS_ITEM_NEED_AFFINE;
653 		gnome_canvas_item_request_update (item);
654 	}
655 
656 	item->canvas->need_repick = TRUE;
657 }
658 
659 
660 /**
661  * gnome_canvas_item_move:
662  * @item: A canvas item.
663  * @dx: Horizontal offset.
664  * @dy: Vertical offset.
665  *
666  * Moves a canvas item by creating an affine transformation matrix for
667  * translation by using the specified values. This happens in item
668  * local coordinate system, so if you have nontrivial transform, it
669  * most probably does not do, what you want.
670  **/
671 void
gnome_canvas_item_move(GnomeCanvasItem * item,double dx,double dy)672 gnome_canvas_item_move (GnomeCanvasItem *item, double dx, double dy)
673 {
674 	double translate[6];
675 
676 	g_return_if_fail (item != NULL);
677 	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
678 
679 	art_affine_translate (translate, dx, dy);
680 
681 	gnome_canvas_item_affine_relative (item, translate);
682 }
683 
684 /* Convenience function to reorder items in a group's child list.  This puts the
685  * specified link after the "before" link. Returns TRUE if the list was changed.
686  */
687 static gboolean
put_item_after(GList * link,GList * before)688 put_item_after (GList *link, GList *before)
689 {
690 	GnomeCanvasGroup *parent;
691 	GList *old_before, *old_after;
692 	GList *after;
693 
694 	parent = GNOME_CANVAS_GROUP (GNOME_CANVAS_ITEM (link->data)->parent);
695 
696 	if (before)
697 		after = before->next;
698 	else
699 		after = parent->item_list;
700 
701 	if (before == link || after == link)
702 		return FALSE;
703 
704 	/* Unlink */
705 
706 	old_before = link->prev;
707 	old_after = link->next;
708 
709 	if (old_before)
710 		old_before->next = old_after;
711 	else
712 		parent->item_list = old_after;
713 
714 	if (old_after)
715 		old_after->prev = old_before;
716 	else
717 		parent->item_list_end = old_before;
718 
719 	/* Relink */
720 
721 	link->prev = before;
722 	if (before)
723 		before->next = link;
724 	else
725 		parent->item_list = link;
726 
727 	link->next = after;
728 	if (after)
729 		after->prev = link;
730 	else
731 		parent->item_list_end = link;
732 
733 	return TRUE;
734 }
735 
736 
737 /**
738  * gnome_canvas_item_raise:
739  * @item: A canvas item.
740  * @positions: Number of steps to raise the item.
741  *
742  * Raises the item in its parent's stack by the specified number of positions.
743  * If the number of positions is greater than the distance to the top of the
744  * stack, then the item is put at the top.
745  **/
746 void
gnome_canvas_item_raise(GnomeCanvasItem * item,int positions)747 gnome_canvas_item_raise (GnomeCanvasItem *item, int positions)
748 {
749 	GList *link, *before;
750 	GnomeCanvasGroup *parent;
751 
752 	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
753 	g_return_if_fail (positions >= 0);
754 
755 	if (!item->parent || positions == 0)
756 		return;
757 
758 	parent = GNOME_CANVAS_GROUP (item->parent);
759 	link = g_list_find (parent->item_list, item);
760 	g_assert (link != NULL);
761 
762 	for (before = link; positions && before; positions--)
763 		before = before->next;
764 
765 	if (!before)
766 		before = parent->item_list_end;
767 
768 	if (put_item_after (link, before)) {
769 		redraw_if_visible (item);
770 		item->canvas->need_repick = TRUE;
771 	}
772 }
773 
774 
775 /**
776  * gnome_canvas_item_lower:
777  * @item: A canvas item.
778  * @positions: Number of steps to lower the item.
779  *
780  * Lowers the item in its parent's stack by the specified number of positions.
781  * If the number of positions is greater than the distance to the bottom of the
782  * stack, then the item is put at the bottom.
783  **/
784 void
gnome_canvas_item_lower(GnomeCanvasItem * item,int positions)785 gnome_canvas_item_lower (GnomeCanvasItem *item, int positions)
786 {
787 	GList *link, *before;
788 	GnomeCanvasGroup *parent;
789 
790 	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
791 	g_return_if_fail (positions >= 1);
792 
793 	if (!item->parent || positions == 0)
794 		return;
795 
796 	parent = GNOME_CANVAS_GROUP (item->parent);
797 	link = g_list_find (parent->item_list, item);
798 	g_assert (link != NULL);
799 
800 	if (link->prev)
801 		for (before = link->prev; positions && before; positions--)
802 			before = before->prev;
803 	else
804 		before = NULL;
805 
806 	if (put_item_after (link, before)) {
807 		redraw_if_visible (item);
808 		item->canvas->need_repick = TRUE;
809 	}
810 }
811 
812 
813 /**
814  * gnome_canvas_item_raise_to_top:
815  * @item: A canvas item.
816  *
817  * Raises an item to the top of its parent's stack.
818  **/
819 void
gnome_canvas_item_raise_to_top(GnomeCanvasItem * item)820 gnome_canvas_item_raise_to_top (GnomeCanvasItem *item)
821 {
822 	GList *link;
823 	GnomeCanvasGroup *parent;
824 
825 	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
826 
827 	if (!item->parent)
828 		return;
829 
830 	parent = GNOME_CANVAS_GROUP (item->parent);
831 	link = g_list_find (parent->item_list, item);
832 	g_assert (link != NULL);
833 
834 	if (put_item_after (link, parent->item_list_end)) {
835 		redraw_if_visible (item);
836 		item->canvas->need_repick = TRUE;
837 	}
838 }
839 
840 
841 /**
842  * gnome_canvas_item_lower_to_bottom:
843  * @item: A canvas item.
844  *
845  * Lowers an item to the bottom of its parent's stack.
846  **/
847 void
gnome_canvas_item_lower_to_bottom(GnomeCanvasItem * item)848 gnome_canvas_item_lower_to_bottom (GnomeCanvasItem *item)
849 {
850 	GList *link;
851 	GnomeCanvasGroup *parent;
852 
853 	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
854 
855 	if (!item->parent)
856 		return;
857 
858 	parent = GNOME_CANVAS_GROUP (item->parent);
859 	link = g_list_find (parent->item_list, item);
860 	g_assert (link != NULL);
861 
862 	if (put_item_after (link, NULL)) {
863 		redraw_if_visible (item);
864 		item->canvas->need_repick = TRUE;
865 	}
866 }
867 
868 
869 /**
870  * gnome_canvas_item_show:
871  * @item: A canvas item.
872  *
873  * Shows a canvas item.  If the item was already shown, then no action is taken.
874  **/
875 void
gnome_canvas_item_show(GnomeCanvasItem * item)876 gnome_canvas_item_show (GnomeCanvasItem *item)
877 {
878 	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
879 
880 	if (!(item->object.flags & GNOME_CANVAS_ITEM_VISIBLE)) {
881 		item->object.flags |= GNOME_CANVAS_ITEM_VISIBLE;
882 		gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2 + 1, item->y2 + 1);
883 		item->canvas->need_repick = TRUE;
884 	}
885 }
886 
887 
888 /**
889  * gnome_canvas_item_hide:
890  * @item: A canvas item.
891  *
892  * Hides a canvas item.  If the item was already hidden, then no action is
893  * taken.
894  **/
895 void
gnome_canvas_item_hide(GnomeCanvasItem * item)896 gnome_canvas_item_hide (GnomeCanvasItem *item)
897 {
898 	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
899 
900 	if (item->object.flags & GNOME_CANVAS_ITEM_VISIBLE) {
901 		item->object.flags &= ~GNOME_CANVAS_ITEM_VISIBLE;
902 		gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2 + 1, item->y2 + 1);
903 		item->canvas->need_repick = TRUE;
904 	}
905 }
906 
907 
908 /**
909  * gnome_canvas_item_grab:
910  * @item: A canvas item.
911  * @event_mask: Mask of events that will be sent to this item.
912  * @cursor: If non-NULL, the cursor that will be used while the grab is active.
913  * @etime: The timestamp required for grabbing the mouse, or GDK_CURRENT_TIME.
914  *
915  * Specifies that all events that match the specified event mask should be sent
916  * to the specified item, and also grabs the mouse by calling
917  * gdk_pointer_grab().  The event mask is also used when grabbing the pointer.
918  * If @cursor is not NULL, then that cursor is used while the grab is active.
919  * The @etime parameter is the timestamp required for grabbing the mouse.
920  *
921  * Return value: If an item was already grabbed, it returns %GDK_GRAB_ALREADY_GRABBED.  If
922  * the specified item was hidden by calling gnome_canvas_item_hide(), then it
923  * returns %GDK_GRAB_NOT_VIEWABLE.  Else, it returns the result of calling
924  * gdk_pointer_grab().
925  **/
926 int
gnome_canvas_item_grab(GnomeCanvasItem * item,guint event_mask,GdkCursor * cursor,guint32 etime)927 gnome_canvas_item_grab (GnomeCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
928 {
929 	int retval;
930 
931 	g_return_val_if_fail (GNOME_IS_CANVAS_ITEM (item), GDK_GRAB_NOT_VIEWABLE);
932 	g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), GDK_GRAB_NOT_VIEWABLE);
933 
934 	if (item->canvas->grabbed_item)
935 		return GDK_GRAB_ALREADY_GRABBED;
936 
937 	if (!(item->object.flags & GNOME_CANVAS_ITEM_VISIBLE))
938 		return GDK_GRAB_NOT_VIEWABLE;
939 
940 	retval = gdk_pointer_grab (item->canvas->layout.bin_window,
941 				   FALSE,
942 				   event_mask,
943 				   NULL,
944 				   cursor,
945 				   etime);
946 
947 	if (retval != GDK_GRAB_SUCCESS)
948 		return retval;
949 
950 	item->canvas->grabbed_item = item;
951 	item->canvas->grabbed_event_mask = event_mask;
952 	item->canvas->current_item = item; /* So that events go to the grabbed item */
953 
954 	return retval;
955 }
956 
957 
958 /**
959  * gnome_canvas_item_ungrab:
960  * @item: A canvas item that holds a grab.
961  * @etime: The timestamp for ungrabbing the mouse.
962  *
963  * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
964  * mouse.
965  **/
966 void
gnome_canvas_item_ungrab(GnomeCanvasItem * item,guint32 etime)967 gnome_canvas_item_ungrab (GnomeCanvasItem *item, guint32 etime)
968 {
969 	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
970 
971 	if (item->canvas->grabbed_item != item)
972 		return;
973 
974 	item->canvas->grabbed_item = NULL;
975 
976 	gdk_pointer_ungrab (etime);
977 }
978 
979 
980 /**
981  * gnome_canvas_item_i2w_affine:
982  * @item: A canvas item
983  * @affine: An affine transformation matrix (return value).
984  *
985  * Gets the affine transform that converts from the item's coordinate system to
986  * world coordinates.
987  **/
988 void
gnome_canvas_item_i2w_affine(GnomeCanvasItem * item,double affine[6])989 gnome_canvas_item_i2w_affine (GnomeCanvasItem *item, double affine[6])
990 {
991 	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
992 	g_return_if_fail (affine != NULL);
993 
994 	art_affine_identity (affine);
995 
996 	while (item) {
997 		if (item->xform != NULL) {
998 			if (item->object.flags & GNOME_CANVAS_ITEM_AFFINE_FULL) {
999 				art_affine_multiply (affine, affine, item->xform);
1000 			} else {
1001 				affine[4] += item->xform[0];
1002 				affine[5] += item->xform[1];
1003 			}
1004 		}
1005 
1006 		item = item->parent;
1007 	}
1008 }
1009 
1010 /**
1011  * gnome_canvas_item_w2i:
1012  * @item: A canvas item.
1013  * @x: X coordinate to convert (input/output value).
1014  * @y: Y coordinate to convert (input/output value).
1015  *
1016  * Converts a coordinate pair from world coordinates to item-relative
1017  * coordinates.
1018  **/
1019 void
gnome_canvas_item_w2i(GnomeCanvasItem * item,double * x,double * y)1020 gnome_canvas_item_w2i (GnomeCanvasItem *item, double *x, double *y)
1021 {
1022 	double affine[6], inv[6];
1023 	ArtPoint w, i;
1024 
1025 	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
1026 	g_return_if_fail (x != NULL);
1027 	g_return_if_fail (y != NULL);
1028 
1029 	gnome_canvas_item_i2w_affine (item, affine);
1030 	art_affine_invert (inv, affine);
1031 	w.x = *x;
1032 	w.y = *y;
1033 	art_affine_point (&i, &w, inv);
1034 	*x = i.x;
1035 	*y = i.y;
1036 }
1037 
1038 
1039 /**
1040  * gnome_canvas_item_i2w:
1041  * @item: A canvas item.
1042  * @x: X coordinate to convert (input/output value).
1043  * @y: Y coordinate to convert (input/output value).
1044  *
1045  * Converts a coordinate pair from item-relative coordinates to world
1046  * coordinates.
1047  **/
1048 void
gnome_canvas_item_i2w(GnomeCanvasItem * item,double * x,double * y)1049 gnome_canvas_item_i2w (GnomeCanvasItem *item, double *x, double *y)
1050 {
1051 	double affine[6];
1052 	ArtPoint w, i;
1053 
1054 	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
1055 	g_return_if_fail (x != NULL);
1056 	g_return_if_fail (y != NULL);
1057 
1058 	gnome_canvas_item_i2w_affine (item, affine);
1059 	i.x = *x;
1060 	i.y = *y;
1061 	art_affine_point (&w, &i, affine);
1062 	*x = w.x;
1063 	*y = w.y;
1064 }
1065 
1066 /**
1067  * gnome_canvas_item_i2c_affine:
1068  * @item: A canvas item.
1069  * @affine: An affine transformation matrix (return value).
1070  *
1071  * Gets the affine transform that converts from item-relative coordinates to
1072  * canvas pixel coordinates.
1073  **/
1074 void
gnome_canvas_item_i2c_affine(GnomeCanvasItem * item,double affine[6])1075 gnome_canvas_item_i2c_affine (GnomeCanvasItem *item, double affine[6])
1076 {
1077 	double i2w[6], w2c[6];
1078 
1079 	gnome_canvas_item_i2w_affine (item, i2w);
1080 	gnome_canvas_w2c_affine (item->canvas, w2c);
1081 	art_affine_multiply (affine, i2w, w2c);
1082 }
1083 
1084 /* Returns whether the item is an inferior of or is equal to the parent. */
1085 static int
is_descendant(GnomeCanvasItem * item,GnomeCanvasItem * parent)1086 is_descendant (GnomeCanvasItem *item, GnomeCanvasItem *parent)
1087 {
1088 	for (; item; item = item->parent)
1089 		if (item == parent)
1090 			return TRUE;
1091 
1092 	return FALSE;
1093 }
1094 
1095 /**
1096  * gnome_canvas_item_reparent:
1097  * @item: A canvas item.
1098  * @new_group: A canvas group.
1099  *
1100  * Changes the parent of the specified item to be the new group.  The item keeps
1101  * its group-relative coordinates as for its old parent, so the item may change
1102  * its absolute position within the canvas.
1103  **/
1104 void
gnome_canvas_item_reparent(GnomeCanvasItem * item,GnomeCanvasGroup * new_group)1105 gnome_canvas_item_reparent (GnomeCanvasItem *item, GnomeCanvasGroup *new_group)
1106 {
1107 	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
1108 	g_return_if_fail (GNOME_IS_CANVAS_GROUP (new_group));
1109 
1110 	/* Both items need to be in the same canvas */
1111 	g_return_if_fail (item->canvas == GNOME_CANVAS_ITEM (new_group)->canvas);
1112 
1113 	/* The group cannot be an inferior of the item or be the item itself --
1114 	 * this also takes care of the case where the item is the root item of
1115 	 * the canvas.  */
1116 	g_return_if_fail (!is_descendant (GNOME_CANVAS_ITEM (new_group), item));
1117 
1118 	/* Everything is ok, now actually reparent the item */
1119 
1120 	g_object_ref (G_OBJECT (item)); /* protect it from the unref in group_remove */
1121 
1122 	redraw_if_visible (item);
1123 
1124 	group_remove (GNOME_CANVAS_GROUP (item->parent), item);
1125 	item->parent = GNOME_CANVAS_ITEM (new_group);
1126 	group_add (new_group, item);
1127 
1128 	/* Redraw and repick */
1129 
1130 	redraw_if_visible (item);
1131 	item->canvas->need_repick = TRUE;
1132 
1133 	g_object_unref (G_OBJECT (item));
1134 }
1135 
1136 /**
1137  * gnome_canvas_item_grab_focus:
1138  * @item: A canvas item.
1139  *
1140  * Makes the specified item take the keyboard focus, so all keyboard events will
1141  * be sent to it.  If the canvas widget itself did not have the focus, it grabs
1142  * it as well.
1143  **/
1144 void
gnome_canvas_item_grab_focus(GnomeCanvasItem * item)1145 gnome_canvas_item_grab_focus (GnomeCanvasItem *item)
1146 {
1147 	GnomeCanvasItem *focused_item;
1148 	GdkEvent ev;
1149 
1150 	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
1151 	g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
1152 
1153 	focused_item = item->canvas->focused_item;
1154 
1155 	if (focused_item) {
1156 		ev.focus_change.type = GDK_FOCUS_CHANGE;
1157 		ev.focus_change.window = GTK_LAYOUT (item->canvas)->bin_window;
1158 		ev.focus_change.send_event = FALSE;
1159 		ev.focus_change.in = FALSE;
1160 
1161 		emit_event (item->canvas, &ev);
1162 	}
1163 
1164 	item->canvas->focused_item = item;
1165 	gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
1166 
1167 	if (focused_item) {
1168 		ev.focus_change.type = GDK_FOCUS_CHANGE;
1169 		ev.focus_change.window = GTK_LAYOUT (item->canvas)->bin_window;
1170 		ev.focus_change.send_event = FALSE;
1171 		ev.focus_change.in = TRUE;
1172 
1173 		emit_event (item->canvas, &ev);
1174 	}
1175 }
1176 
1177 
1178 /**
1179  * gnome_canvas_item_get_bounds:
1180  * @item: A canvas item.
1181  * @x1: Leftmost edge of the bounding box (return value).
1182  * @y1: Upper edge of the bounding box (return value).
1183  * @x2: Rightmost edge of the bounding box (return value).
1184  * @y2: Lower edge of the bounding box (return value).
1185  *
1186  * Queries the bounding box of a canvas item.  The bounds are returned in the
1187  * coordinate system of the item's parent.
1188  **/
1189 void
gnome_canvas_item_get_bounds(GnomeCanvasItem * item,double * x1,double * y1,double * x2,double * y2)1190 gnome_canvas_item_get_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
1191 {
1192 	double tx1, ty1, tx2, ty2;
1193 	ArtPoint p1, p2, p3, p4;
1194 	ArtPoint q1, q2, q3, q4;
1195 	double min_x1, min_y1, min_x2, min_y2;
1196 	double max_x1, max_y1, max_x2, max_y2;
1197 
1198 	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
1199 
1200 	tx1 = ty1 = tx2 = ty2 = 0.0;
1201 
1202 	/* Get the item's bounds in its coordinate system */
1203 
1204 	if (GNOME_CANVAS_ITEM_GET_CLASS (item)->bounds)
1205 		(* GNOME_CANVAS_ITEM_GET_CLASS (item)->bounds) (item, &tx1, &ty1, &tx2, &ty2);
1206 
1207 	/* Make the bounds relative to the item's parent coordinate system */
1208 
1209 	if (item->xform && (item->object.flags & GNOME_CANVAS_ITEM_AFFINE_FULL)) {
1210 		p1.x = p2.x = tx1;
1211 		p1.y = p4.y = ty1;
1212 		p3.x = p4.x = tx2;
1213 		p2.y = p3.y = ty2;
1214 
1215 		art_affine_point (&q1, &p1, item->xform);
1216 		art_affine_point (&q2, &p2, item->xform);
1217 		art_affine_point (&q3, &p3, item->xform);
1218 		art_affine_point (&q4, &p4, item->xform);
1219 
1220 		if (q1.x < q2.x) {
1221 			min_x1 = q1.x;
1222 			max_x1 = q2.x;
1223 		} else {
1224 			min_x1 = q2.x;
1225 			max_x1 = q1.x;
1226 		}
1227 
1228 		if (q1.y < q2.y) {
1229 			min_y1 = q1.y;
1230 			max_y1 = q2.y;
1231 		} else {
1232 			min_y1 = q2.y;
1233 			max_y1 = q1.y;
1234 		}
1235 
1236 		if (q3.x < q4.x) {
1237 			min_x2 = q3.x;
1238 			max_x2 = q4.x;
1239 		} else {
1240 			min_x2 = q4.x;
1241 			max_x2 = q3.x;
1242 		}
1243 
1244 		if (q3.y < q4.y) {
1245 			min_y2 = q3.y;
1246 			max_y2 = q4.y;
1247 		} else {
1248 			min_y2 = q4.y;
1249 			max_y2 = q3.y;
1250 		}
1251 
1252 		tx1 = MIN (min_x1, min_x2);
1253 		ty1 = MIN (min_y1, min_y2);
1254 		tx2 = MAX (max_x1, max_x2);
1255 		ty2 = MAX (max_y1, max_y2);
1256 	} else if (item->xform) {
1257 		tx1 += item->xform[0];
1258 		ty1 += item->xform[1];
1259 		tx2 += item->xform[0];
1260 		ty2 += item->xform[1];
1261 	}
1262 
1263 	/* Return the values */
1264 
1265 	if (x1)
1266 		*x1 = tx1;
1267 
1268 	if (y1)
1269 		*y1 = ty1;
1270 
1271 	if (x2)
1272 		*x2 = tx2;
1273 
1274 	if (y2)
1275 		*y2 = ty2;
1276 }
1277 
1278 
1279 /**
1280  * gnome_canvas_item_request_update
1281  * @item: A canvas item.
1282  *
1283  * To be used only by item implementations.  Requests that the canvas queue an
1284  * update for the specified item.
1285  **/
1286 void
gnome_canvas_item_request_update(GnomeCanvasItem * item)1287 gnome_canvas_item_request_update (GnomeCanvasItem *item)
1288 {
1289 	if (item->object.flags & GNOME_CANVAS_ITEM_NEED_UPDATE)
1290 		return;
1291 
1292 	item->object.flags |= GNOME_CANVAS_ITEM_NEED_UPDATE;
1293 
1294 	if (item->parent != NULL) {
1295 		/* Recurse up the tree */
1296 		gnome_canvas_item_request_update (item->parent);
1297 	} else {
1298 		/* Have reached the top of the tree, make sure the update call gets scheduled. */
1299 		gnome_canvas_request_update (item->canvas);
1300 	}
1301 }
1302 
1303 /*** GnomeCanvasGroup ***/
1304 
1305 
1306 enum {
1307 	GROUP_PROP_0,
1308 	GROUP_PROP_X,
1309 	GROUP_PROP_Y
1310 };
1311 
1312 
1313 static void gnome_canvas_group_class_init  (GnomeCanvasGroupClass *class);
1314 static void gnome_canvas_group_init        (GnomeCanvasGroup      *group);
1315 static void gnome_canvas_group_set_property(GObject               *object,
1316 					    guint                  param_id,
1317 					    const GValue          *value,
1318 					    GParamSpec            *pspec);
1319 static void gnome_canvas_group_get_property(GObject               *object,
1320 					    guint                  param_id,
1321 					    GValue                *value,
1322 					    GParamSpec            *pspec);
1323 
1324 static void gnome_canvas_group_destroy     (GtkObject             *object);
1325 
1326 static void   gnome_canvas_group_update      (GnomeCanvasItem *item, double *affine,
1327 					      ArtSVP *clip_path, int flags);
1328 static void   gnome_canvas_group_realize     (GnomeCanvasItem *item);
1329 static void   gnome_canvas_group_unrealize   (GnomeCanvasItem *item);
1330 static void   gnome_canvas_group_map         (GnomeCanvasItem *item);
1331 static void   gnome_canvas_group_unmap       (GnomeCanvasItem *item);
1332 static void   gnome_canvas_group_draw        (GnomeCanvasItem *item, GdkDrawable *drawable,
1333 					      int x, int y, int width, int height);
1334 static double gnome_canvas_group_point       (GnomeCanvasItem *item, double x, double y,
1335 					      int cx, int cy,
1336 					      GnomeCanvasItem **actual_item);
1337 static void   gnome_canvas_group_bounds      (GnomeCanvasItem *item, double *x1, double *y1,
1338 					      double *x2, double *y2);
1339 static void   gnome_canvas_group_render      (GnomeCanvasItem *item,
1340 					      GnomeCanvasBuf *buf);
1341 
1342 
1343 static GnomeCanvasItemClass *group_parent_class;
1344 
1345 
1346 /**
1347  * gnome_canvas_group_get_type:
1348  *
1349  * Registers the &GnomeCanvasGroup class if necessary, and returns the type ID
1350  * associated to it.
1351  *
1352  * Return value:  The type ID of the &GnomeCanvasGroup class.
1353  **/
1354 GType
gnome_canvas_group_get_type(void)1355 gnome_canvas_group_get_type (void)
1356 {
1357 	static GType canvas_group_type;
1358 
1359 	if (!canvas_group_type) {
1360 		const GTypeInfo object_info = {
1361 			sizeof (GnomeCanvasGroupClass),
1362 			(GBaseInitFunc) NULL,
1363 			(GBaseFinalizeFunc) NULL,
1364 			(GClassInitFunc) gnome_canvas_group_class_init,
1365 			(GClassFinalizeFunc) NULL,
1366 			NULL,			/* class_data */
1367 			sizeof (GnomeCanvasGroup),
1368 			0,			/* n_preallocs */
1369 			(GInstanceInitFunc) gnome_canvas_group_init,
1370 			NULL			/* value_table */
1371 		};
1372 
1373 		canvas_group_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasGroup",
1374 							    &object_info, 0);
1375 	}
1376 
1377 	return canvas_group_type;
1378 }
1379 
1380 /* Class initialization function for GnomeCanvasGroupClass */
1381 static void
gnome_canvas_group_class_init(GnomeCanvasGroupClass * class)1382 gnome_canvas_group_class_init (GnomeCanvasGroupClass *class)
1383 {
1384 	GObjectClass *gobject_class;
1385 	GtkObjectClass *object_class;
1386 	GnomeCanvasItemClass *item_class;
1387 
1388 	gobject_class = (GObjectClass *) class;
1389 	object_class = (GtkObjectClass *) class;
1390 	item_class = (GnomeCanvasItemClass *) class;
1391 
1392 	group_parent_class = g_type_class_peek_parent (class);
1393 
1394 	gobject_class->set_property = gnome_canvas_group_set_property;
1395 	gobject_class->get_property = gnome_canvas_group_get_property;
1396 
1397 	g_object_class_install_property
1398 		(gobject_class, GROUP_PROP_X,
1399 		 g_param_spec_double ("x",
1400 				      _("X"),
1401 				      _("X"),
1402 				      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
1403 				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
1404 	g_object_class_install_property
1405 		(gobject_class, GROUP_PROP_Y,
1406 		 g_param_spec_double ("y",
1407 				      _("Y"),
1408 				      _("Y"),
1409 				      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
1410 				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
1411 
1412 	object_class->destroy = gnome_canvas_group_destroy;
1413 
1414 	item_class->update = gnome_canvas_group_update;
1415 	item_class->realize = gnome_canvas_group_realize;
1416 	item_class->unrealize = gnome_canvas_group_unrealize;
1417 	item_class->map = gnome_canvas_group_map;
1418 	item_class->unmap = gnome_canvas_group_unmap;
1419 	item_class->draw = gnome_canvas_group_draw;
1420 	item_class->render = gnome_canvas_group_render;
1421 	item_class->point = gnome_canvas_group_point;
1422 	item_class->bounds = gnome_canvas_group_bounds;
1423 }
1424 
1425 /* Object initialization function for GnomeCanvasGroup */
1426 static void
gnome_canvas_group_init(GnomeCanvasGroup * group)1427 gnome_canvas_group_init (GnomeCanvasGroup *group)
1428 {
1429 #if 0
1430 	group->xpos = 0.0;
1431 	group->ypos = 0.0;
1432 #endif
1433 }
1434 
1435 /* Translate handler for canvas groups */
1436 static double *
gnome_canvas_ensure_translate(GnomeCanvasItem * item)1437 gnome_canvas_ensure_translate (GnomeCanvasItem *item)
1438 {
1439 	if (item->xform == NULL) {
1440 		GTK_OBJECT_UNSET_FLAGS (item, GNOME_CANVAS_ITEM_AFFINE_FULL);
1441 		item->xform = g_new (double, 2);
1442 		item->xform[0] = 0.0;
1443 		item->xform[1] = 0.0;
1444 		return item->xform;
1445 	} else if (item->object.flags & GNOME_CANVAS_ITEM_AFFINE_FULL) {
1446 		return item->xform + 4;
1447 	} else {
1448 		return item->xform;
1449 	}
1450 }
1451 
1452 /* Set_property handler for canvas groups */
1453 static void
gnome_canvas_group_set_property(GObject * gobject,guint param_id,const GValue * value,GParamSpec * pspec)1454 gnome_canvas_group_set_property (GObject *gobject, guint param_id,
1455 				 const GValue *value, GParamSpec *pspec)
1456 {
1457 	GnomeCanvasItem *item;
1458 	double *xlat;
1459 
1460 	g_return_if_fail (GNOME_IS_CANVAS_GROUP (gobject));
1461 
1462 	item = GNOME_CANVAS_ITEM (gobject);
1463 
1464 	switch (param_id) {
1465 	case GROUP_PROP_X:
1466 		xlat = gnome_canvas_ensure_translate (item);
1467 		xlat[0] = g_value_get_double (value);
1468 		break;
1469 
1470 	case GROUP_PROP_Y:
1471 		xlat = gnome_canvas_ensure_translate (item);
1472 		xlat[1] = g_value_get_double (value);
1473 		break;
1474 
1475 	default:
1476 		G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
1477 		break;
1478 	}
1479 }
1480 
1481 /* Get_property handler for canvas groups */
1482 static void
gnome_canvas_group_get_property(GObject * gobject,guint param_id,GValue * value,GParamSpec * pspec)1483 gnome_canvas_group_get_property (GObject *gobject, guint param_id,
1484 				 GValue *value, GParamSpec *pspec)
1485 {
1486 	GnomeCanvasItem *item;
1487 
1488 	g_return_if_fail (GNOME_IS_CANVAS_GROUP (gobject));
1489 
1490 	item = GNOME_CANVAS_ITEM (gobject);
1491 
1492 	switch (param_id) {
1493 	case GROUP_PROP_X:
1494 		if (item->xform == NULL)
1495 			g_value_set_double (value, 0);
1496 		else if (GTK_OBJECT (gobject)->flags & GNOME_CANVAS_ITEM_AFFINE_FULL)
1497 			g_value_set_double (value, item->xform[4]);
1498 		else
1499 			g_value_set_double (value, item->xform[0]);
1500 		break;
1501 
1502 	case GROUP_PROP_Y:
1503 		if (item->xform == NULL)
1504 			g_value_set_double (value, 0);
1505 		else if (GTK_OBJECT (gobject)->flags & GNOME_CANVAS_ITEM_AFFINE_FULL)
1506 			g_value_set_double (value, item->xform[5]);
1507 		else
1508 			g_value_set_double (value, item->xform[1]);
1509 		break;
1510 
1511 	default:
1512 		G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
1513 		break;
1514 	}
1515 }
1516 
1517 /* Destroy handler for canvas groups */
1518 static void
gnome_canvas_group_destroy(GtkObject * object)1519 gnome_canvas_group_destroy (GtkObject *object)
1520 {
1521 	GnomeCanvasGroup *group;
1522 
1523 	g_return_if_fail (GNOME_IS_CANVAS_GROUP (object));
1524 
1525 	group = GNOME_CANVAS_GROUP (object);
1526 
1527 	while (group->item_list) {
1528 		// child is unref'ed by the child's group_remove().
1529 		gtk_object_destroy (GTK_OBJECT (group->item_list->data));
1530 	}
1531 
1532 	if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
1533 		(* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
1534 }
1535 
1536 /* Update handler for canvas groups */
1537 static void
gnome_canvas_group_update(GnomeCanvasItem * item,double * affine,ArtSVP * clip_path,int flags)1538 gnome_canvas_group_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
1539 {
1540 	GnomeCanvasGroup *group;
1541 	GList *list;
1542 	GnomeCanvasItem *i;
1543 	ArtDRect bbox, child_bbox;
1544 
1545 	group = GNOME_CANVAS_GROUP (item);
1546 
1547 	(* group_parent_class->update) (item, affine, clip_path, flags);
1548 
1549 	bbox.x0 = 0;
1550 	bbox.y0 = 0;
1551 	bbox.x1 = 0;
1552 	bbox.y1 = 0;
1553 
1554 	for (list = group->item_list; list; list = list->next) {
1555 		i = list->data;
1556 
1557 		gnome_canvas_item_invoke_update (i, affine, clip_path, flags);
1558 
1559 		child_bbox.x0 = i->x1;
1560 		child_bbox.y0 = i->y1;
1561 		child_bbox.x1 = i->x2;
1562 		child_bbox.y1 = i->y2;
1563 		art_drect_union (&bbox, &bbox, &child_bbox);
1564 	}
1565 	item->x1 = bbox.x0;
1566 	item->y1 = bbox.y0;
1567 	item->x2 = bbox.x1;
1568 	item->y2 = bbox.y1;
1569 }
1570 
1571 /* Realize handler for canvas groups */
1572 static void
gnome_canvas_group_realize(GnomeCanvasItem * item)1573 gnome_canvas_group_realize (GnomeCanvasItem *item)
1574 {
1575 	GnomeCanvasGroup *group;
1576 	GList *list;
1577 	GnomeCanvasItem *i;
1578 
1579 	group = GNOME_CANVAS_GROUP (item);
1580 
1581 	for (list = group->item_list; list; list = list->next) {
1582 		i = list->data;
1583 
1584 		if (!(i->object.flags & GNOME_CANVAS_ITEM_REALIZED))
1585 			(* GNOME_CANVAS_ITEM_GET_CLASS (i)->realize) (i);
1586 	}
1587 
1588 	(* group_parent_class->realize) (item);
1589 }
1590 
1591 /* Unrealize handler for canvas groups */
1592 static void
gnome_canvas_group_unrealize(GnomeCanvasItem * item)1593 gnome_canvas_group_unrealize (GnomeCanvasItem *item)
1594 {
1595 	GnomeCanvasGroup *group;
1596 	GList *list;
1597 	GnomeCanvasItem *i;
1598 
1599 	group = GNOME_CANVAS_GROUP (item);
1600 
1601 	for (list = group->item_list; list; list = list->next) {
1602 		i = list->data;
1603 
1604 		if (i->object.flags & GNOME_CANVAS_ITEM_REALIZED)
1605 			(* GNOME_CANVAS_ITEM_GET_CLASS (i)->unrealize) (i);
1606 	}
1607 
1608 	(* group_parent_class->unrealize) (item);
1609 }
1610 
1611 /* Map handler for canvas groups */
1612 static void
gnome_canvas_group_map(GnomeCanvasItem * item)1613 gnome_canvas_group_map (GnomeCanvasItem *item)
1614 {
1615 	GnomeCanvasGroup *group;
1616 	GList *list;
1617 	GnomeCanvasItem *i;
1618 
1619 	group = GNOME_CANVAS_GROUP (item);
1620 
1621 	for (list = group->item_list; list; list = list->next) {
1622 		i = list->data;
1623 
1624 		if (!(i->object.flags & GNOME_CANVAS_ITEM_MAPPED))
1625 			(* GNOME_CANVAS_ITEM_GET_CLASS (i)->map) (i);
1626 	}
1627 
1628 	(* group_parent_class->map) (item);
1629 }
1630 
1631 /* Unmap handler for canvas groups */
1632 static void
gnome_canvas_group_unmap(GnomeCanvasItem * item)1633 gnome_canvas_group_unmap (GnomeCanvasItem *item)
1634 {
1635 	GnomeCanvasGroup *group;
1636 	GList *list;
1637 	GnomeCanvasItem *i;
1638 
1639 	group = GNOME_CANVAS_GROUP (item);
1640 
1641 	for (list = group->item_list; list; list = list->next) {
1642 		i = list->data;
1643 
1644 		if (i->object.flags & GNOME_CANVAS_ITEM_MAPPED)
1645 			(* GNOME_CANVAS_ITEM_GET_CLASS (i)->unmap) (i);
1646 	}
1647 
1648 	(* group_parent_class->unmap) (item);
1649 }
1650 
1651 /* Draw handler for canvas groups */
1652 static void
gnome_canvas_group_draw(GnomeCanvasItem * item,GdkDrawable * drawable,int x,int y,int width,int height)1653 gnome_canvas_group_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
1654 			 int x, int y, int width, int height)
1655 {
1656 	GnomeCanvasGroup *group;
1657 	GList *list;
1658 	GnomeCanvasItem *child = NULL;
1659 
1660 	group = GNOME_CANVAS_GROUP (item);
1661 
1662 	for (list = group->item_list; list; list = list->next) {
1663 		child = list->data;
1664 
1665 		if (((child->object.flags & GNOME_CANVAS_ITEM_VISIBLE)
1666 		     && ((child->x1 < (x + width))
1667 			 && (child->y1 < (y + height))
1668 			 && (child->x2 > x)
1669 			 && (child->y2 > y)))
1670 		    || ((GTK_OBJECT_FLAGS (child) & GNOME_CANVAS_ITEM_ALWAYS_REDRAW)
1671 			&& (child->x1 < child->canvas->redraw_x2)
1672 			&& (child->y1 < child->canvas->redraw_y2)
1673 			&& (child->x2 > child->canvas->redraw_x1)
1674 			&& (child->y2 > child->canvas->redraw_y2)))
1675 			if (GNOME_CANVAS_ITEM_GET_CLASS (child)->draw)
1676 				(* GNOME_CANVAS_ITEM_GET_CLASS (child)->draw) (
1677 					child, drawable, x, y, width, height);
1678 	}
1679 }
1680 
1681 /* Point handler for canvas groups */
1682 static double
gnome_canvas_group_point(GnomeCanvasItem * item,double x,double y,int cx,int cy,GnomeCanvasItem ** actual_item)1683 gnome_canvas_group_point (GnomeCanvasItem *item, double x, double y, int cx, int cy,
1684 			  GnomeCanvasItem **actual_item)
1685 {
1686 	GnomeCanvasGroup *group;
1687 	GList *list;
1688 	GnomeCanvasItem *child, *point_item;
1689 	int x1, y1, x2, y2;
1690 	double gx, gy;
1691 	double dist, best;
1692 	int has_point;
1693 
1694 	group = GNOME_CANVAS_GROUP (item);
1695 
1696 	x1 = cx - item->canvas->close_enough;
1697 	y1 = cy - item->canvas->close_enough;
1698 	x2 = cx + item->canvas->close_enough;
1699 	y2 = cy + item->canvas->close_enough;
1700 
1701 	best = 0.0;
1702 	*actual_item = NULL;
1703 
1704 	gx = x;
1705 	gy = y;
1706 
1707 	dist = 0.0; /* keep gcc happy */
1708 
1709 	for (list = group->item_list; list; list = list->next) {
1710 		child = list->data;
1711 
1712 		if ((child->x1 > x2) || (child->y1 > y2) || (child->x2 < x1) || (child->y2 < y1))
1713 			continue;
1714 
1715 		point_item = NULL; /* cater for incomplete item implementations */
1716 
1717 		if ((child->object.flags & GNOME_CANVAS_ITEM_VISIBLE)
1718 		    && GNOME_CANVAS_ITEM_GET_CLASS (child)->point) {
1719 			dist = gnome_canvas_item_invoke_point (child, gx, gy, cx, cy, &point_item);
1720 			has_point = TRUE;
1721 		} else
1722 			has_point = FALSE;
1723 
1724 		if (has_point
1725 		    && point_item
1726 		    && ((int) (dist * item->canvas->pixels_per_unit + 0.5)
1727 			<= item->canvas->close_enough)) {
1728 			best = dist;
1729 			*actual_item = point_item;
1730 		}
1731 	}
1732 
1733 	return best;
1734 }
1735 
1736 /* Bounds handler for canvas groups */
1737 static void
gnome_canvas_group_bounds(GnomeCanvasItem * item,double * x1,double * y1,double * x2,double * y2)1738 gnome_canvas_group_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
1739 {
1740 	GnomeCanvasGroup *group;
1741 	GnomeCanvasItem *child;
1742 	GList *list;
1743 	double tx1, ty1, tx2, ty2;
1744 	double minx, miny, maxx, maxy;
1745 	int set;
1746 
1747 	group = GNOME_CANVAS_GROUP (item);
1748 
1749 	/* Get the bounds of the first visible item */
1750 
1751 	child = NULL; /* Unnecessary but eliminates a warning. */
1752 
1753 	set = FALSE;
1754 
1755 	for (list = group->item_list; list; list = list->next) {
1756 		child = list->data;
1757 
1758 		if (child->object.flags & GNOME_CANVAS_ITEM_VISIBLE) {
1759 			set = TRUE;
1760 			gnome_canvas_item_get_bounds (child, &minx, &miny, &maxx, &maxy);
1761 			break;
1762 		}
1763 	}
1764 
1765 	/* If there were no visible items, return an empty bounding box */
1766 
1767 	if (!set) {
1768 		*x1 = *y1 = *x2 = *y2 = 0.0;
1769 		return;
1770 	}
1771 
1772 	/* Now we can grow the bounds using the rest of the items */
1773 
1774 	list = list->next;
1775 
1776 	for (; list; list = list->next) {
1777 		child = list->data;
1778 
1779 		if (!(child->object.flags & GNOME_CANVAS_ITEM_VISIBLE))
1780 			continue;
1781 
1782 		gnome_canvas_item_get_bounds (child, &tx1, &ty1, &tx2, &ty2);
1783 
1784 		if (tx1 < minx)
1785 			minx = tx1;
1786 
1787 		if (ty1 < miny)
1788 			miny = ty1;
1789 
1790 		if (tx2 > maxx)
1791 			maxx = tx2;
1792 
1793 		if (ty2 > maxy)
1794 			maxy = ty2;
1795 	}
1796 
1797 	*x1 = minx;
1798 	*y1 = miny;
1799 	*x2 = maxx;
1800 	*y2 = maxy;
1801 }
1802 
1803 /* Render handler for canvas groups */
1804 static void
gnome_canvas_group_render(GnomeCanvasItem * item,GnomeCanvasBuf * buf)1805 gnome_canvas_group_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf)
1806 {
1807 	GnomeCanvasGroup *group;
1808 	GnomeCanvasItem *child;
1809 	GList *list;
1810 
1811 	group = GNOME_CANVAS_GROUP (item);
1812 
1813 	for (list = group->item_list; list; list = list->next) {
1814 		child = list->data;
1815 
1816 		if (((child->object.flags & GNOME_CANVAS_ITEM_VISIBLE)
1817 		     && ((child->x1 < buf->rect.x1)
1818 			 && (child->y1 < buf->rect.y1)
1819 			 && (child->x2 > buf->rect.x0)
1820 			 && (child->y2 > buf->rect.y0)))
1821 		    || ((GTK_OBJECT_FLAGS (child) & GNOME_CANVAS_ITEM_ALWAYS_REDRAW)
1822 			&& (child->x1 < child->canvas->redraw_x2)
1823 			&& (child->y1 < child->canvas->redraw_y2)
1824 			&& (child->x2 > child->canvas->redraw_x1)
1825 			&& (child->y2 > child->canvas->redraw_y2)))
1826 			if (GNOME_CANVAS_ITEM_GET_CLASS (child)->render)
1827 				(* GNOME_CANVAS_ITEM_GET_CLASS (child)->render) (
1828 					child, buf);
1829 	}
1830 }
1831 
1832 /* Adds an item to a group */
1833 static void
group_add(GnomeCanvasGroup * group,GnomeCanvasItem * item)1834 group_add (GnomeCanvasGroup *group, GnomeCanvasItem *item)
1835 {
1836 	g_object_ref_sink (G_OBJECT (item));
1837 
1838 	if (!group->item_list) {
1839 		group->item_list = g_list_append (group->item_list, item);
1840 		group->item_list_end = group->item_list;
1841 	} else
1842 		group->item_list_end = g_list_append (group->item_list_end, item)->next;
1843 
1844 	if (group->item.object.flags & GNOME_CANVAS_ITEM_REALIZED)
1845 		(* GNOME_CANVAS_ITEM_GET_CLASS (item)->realize) (item);
1846 
1847 	if (group->item.object.flags & GNOME_CANVAS_ITEM_MAPPED)
1848 		(* GNOME_CANVAS_ITEM_GET_CLASS (item)->map) (item);
1849 
1850 	g_object_notify (G_OBJECT (item), "parent");
1851 }
1852 
1853 /* Removes an item from a group */
1854 static void
group_remove(GnomeCanvasGroup * group,GnomeCanvasItem * item)1855 group_remove (GnomeCanvasGroup *group, GnomeCanvasItem *item)
1856 {
1857 	GList *children;
1858 
1859 	g_return_if_fail (GNOME_IS_CANVAS_GROUP (group));
1860 	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
1861 
1862 	for (children = group->item_list; children; children = children->next)
1863 		if (children->data == item) {
1864 			if (item->object.flags & GNOME_CANVAS_ITEM_MAPPED)
1865 				(* GNOME_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
1866 
1867 			if (item->object.flags & GNOME_CANVAS_ITEM_REALIZED)
1868 				(* GNOME_CANVAS_ITEM_GET_CLASS (item)->unrealize) (item);
1869 
1870 			/* Unparent the child */
1871 
1872 			item->parent = NULL;
1873 			g_object_unref (G_OBJECT (item));
1874 
1875 			/* Remove it from the list */
1876 
1877 			if (children == group->item_list_end)
1878 				group->item_list_end = children->prev;
1879 
1880 			group->item_list = g_list_remove_link (group->item_list, children);
1881 			g_list_free (children);
1882 			break;
1883 		}
1884 }
1885 
1886 
1887 /*** GnomeCanvas ***/
1888 
1889 
1890 enum {
1891 	DRAW_BACKGROUND,
1892 	RENDER_BACKGROUND,
1893 	LAST_SIGNAL
1894 };
1895 
1896 static void gnome_canvas_class_init          (GnomeCanvasClass *class);
1897 static void gnome_canvas_init                (GnomeCanvas      *canvas);
1898 static void gnome_canvas_destroy             (GtkObject        *object);
1899 static void gnome_canvas_map                 (GtkWidget        *widget);
1900 static void gnome_canvas_unmap               (GtkWidget        *widget);
1901 static void gnome_canvas_realize             (GtkWidget        *widget);
1902 static void gnome_canvas_unrealize           (GtkWidget        *widget);
1903 static void gnome_canvas_size_allocate       (GtkWidget        *widget,
1904 					      GtkAllocation    *allocation);
1905 static gint gnome_canvas_button              (GtkWidget        *widget,
1906 					      GdkEventButton   *event);
1907 static gint gnome_canvas_motion              (GtkWidget        *widget,
1908 					      GdkEventMotion   *event);
1909 static gint gnome_canvas_expose              (GtkWidget        *widget,
1910 					      GdkEventExpose   *event);
1911 static gboolean gnome_canvas_key             (GtkWidget        *widget,
1912 					      GdkEventKey      *event);
1913 static gboolean gnome_canvas_scroll          (GtkWidget        *widget,
1914 					      GdkEventScroll   *event);
1915 static gint gnome_canvas_crossing            (GtkWidget        *widget,
1916 					      GdkEventCrossing *event);
1917 static gint gnome_canvas_focus_in            (GtkWidget        *widget,
1918 					      GdkEventFocus    *event);
1919 static gint gnome_canvas_focus_out           (GtkWidget        *widget,
1920 					      GdkEventFocus    *event);
1921 static void gnome_canvas_request_update_real (GnomeCanvas      *canvas);
1922 static void gnome_canvas_draw_background     (GnomeCanvas      *canvas,
1923 					      GdkDrawable      *drawable,
1924 					      int               x,
1925 					      int               y,
1926 					      int               width,
1927 					      int               height);
1928 
1929 
1930 static GtkLayoutClass *canvas_parent_class;
1931 
1932 static guint canvas_signals[LAST_SIGNAL];
1933 
1934 enum {
1935 	PROP_AA = 1,
1936 	PROP_FOCUSED_ITEM
1937 };
1938 
1939 /**
1940  * gnome_canvas_get_type:
1941  *
1942  * Registers the &GnomeCanvas class if necessary, and returns the type ID
1943  * associated to it.
1944  *
1945  * Return value:  The type ID of the &GnomeCanvas class.
1946  **/
1947 GType
gnome_canvas_get_type(void)1948 gnome_canvas_get_type (void)
1949 {
1950 	static GType canvas_type;
1951 
1952 	if (!canvas_type) {
1953 		const GTypeInfo object_info = {
1954 			sizeof (GnomeCanvasClass),
1955 			(GBaseInitFunc) NULL,
1956 			(GBaseFinalizeFunc) NULL,
1957 			(GClassInitFunc) gnome_canvas_class_init,
1958 			(GClassFinalizeFunc) NULL,
1959 			NULL,			/* class_data */
1960 			sizeof (GnomeCanvas),
1961 			0,			/* n_preallocs */
1962 			(GInstanceInitFunc) gnome_canvas_init,
1963 			NULL			/* value_table */
1964 		};
1965 
1966 		canvas_type = g_type_register_static (GTK_TYPE_LAYOUT, "GnomeCanvas",
1967 						      &object_info, 0);
1968 	}
1969 
1970 	return canvas_type;
1971 }
1972 
1973 static void
gnome_canvas_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1974 gnome_canvas_get_property (GObject    *object,
1975 			   guint       prop_id,
1976 			   GValue     *value,
1977 			   GParamSpec *pspec)
1978 {
1979 	switch (prop_id) {
1980 	case PROP_AA:
1981 		g_value_set_boolean (value, GNOME_CANVAS (object)->aa);
1982 		break;
1983 	case PROP_FOCUSED_ITEM:
1984 		g_value_set_object (value, GNOME_CANVAS (object)->focused_item);
1985 		break;
1986 	default:
1987 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1988 		break;
1989 	}
1990 }
1991 
1992 static void
gnome_canvas_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1993 gnome_canvas_set_property (GObject      *object,
1994 			   guint         prop_id,
1995 			   const GValue *value,
1996 			   GParamSpec   *pspec)
1997 {
1998 	switch (prop_id) {
1999 	case PROP_AA:
2000 		GNOME_CANVAS (object)->aa = g_value_get_boolean (value);
2001 		break;
2002 	case PROP_FOCUSED_ITEM:
2003 		GNOME_CANVAS (object)->focused_item = g_value_get_object (value);
2004 		break;
2005 	default:
2006 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2007 		break;
2008 	}
2009 }
2010 
2011 /* Class initialization function for GnomeCanvasClass */
2012 static void
gnome_canvas_class_init(GnomeCanvasClass * klass)2013 gnome_canvas_class_init (GnomeCanvasClass *klass)
2014 {
2015 	GObjectClass   *gobject_class;
2016 	GtkObjectClass *object_class;
2017 	GtkWidgetClass *widget_class;
2018 
2019 	gobject_class = (GObjectClass *)klass;
2020 	object_class  = (GtkObjectClass *) klass;
2021 	widget_class  = (GtkWidgetClass *) klass;
2022 
2023 	canvas_parent_class = g_type_class_peek_parent (klass);
2024 
2025 	gobject_class->set_property = gnome_canvas_set_property;
2026 	gobject_class->get_property = gnome_canvas_get_property;
2027 
2028 	object_class->destroy = gnome_canvas_destroy;
2029 
2030 	widget_class->map = gnome_canvas_map;
2031 	widget_class->unmap = gnome_canvas_unmap;
2032 	widget_class->realize = gnome_canvas_realize;
2033 	widget_class->unrealize = gnome_canvas_unrealize;
2034 	widget_class->size_allocate = gnome_canvas_size_allocate;
2035 	widget_class->button_press_event = gnome_canvas_button;
2036 	widget_class->button_release_event = gnome_canvas_button;
2037 	widget_class->motion_notify_event = gnome_canvas_motion;
2038 	widget_class->expose_event = gnome_canvas_expose;
2039 	widget_class->key_press_event = gnome_canvas_key;
2040 	widget_class->key_release_event = gnome_canvas_key;
2041 	widget_class->enter_notify_event = gnome_canvas_crossing;
2042 	widget_class->leave_notify_event = gnome_canvas_crossing;
2043 	widget_class->focus_in_event = gnome_canvas_focus_in;
2044 	widget_class->focus_out_event = gnome_canvas_focus_out;
2045 	widget_class->scroll_event = gnome_canvas_scroll;
2046 
2047 	klass->draw_background = gnome_canvas_draw_background;
2048 	klass->render_background = NULL;
2049 	klass->request_update = gnome_canvas_request_update_real;
2050 
2051 	g_object_class_install_property (G_OBJECT_CLASS (object_class),
2052 					 PROP_AA,
2053 					 g_param_spec_boolean ("aa",
2054 							       _("Antialiased"),
2055 							       _("The antialiasing mode of the canvas."),
2056 							       FALSE,
2057 							       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
2058 
2059 	g_object_class_install_property (gobject_class, PROP_FOCUSED_ITEM,
2060 		 			 g_param_spec_object ("focused_item", NULL, NULL,
2061 					 GNOME_TYPE_CANVAS_ITEM,
2062 					 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
2063 
2064 	canvas_signals[DRAW_BACKGROUND] =
2065 		g_signal_new ("draw_background",
2066 			      G_TYPE_FROM_CLASS (object_class),
2067 			      G_SIGNAL_RUN_LAST,
2068 			      G_STRUCT_OFFSET (GnomeCanvasClass, draw_background),
2069 			      NULL, NULL,
2070 			      gnome_canvas_marshal_VOID__OBJECT_INT_INT_INT_INT,
2071 			      G_TYPE_NONE, 5, GDK_TYPE_DRAWABLE,
2072 			      G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT);
2073 	canvas_signals[RENDER_BACKGROUND] =
2074 		g_signal_new ("render_background",
2075 			      G_TYPE_FROM_CLASS (object_class),
2076 			      G_SIGNAL_RUN_LAST,
2077 			      G_STRUCT_OFFSET (GnomeCanvasClass, render_background),
2078 			      NULL, NULL,
2079 			      g_cclosure_marshal_VOID__POINTER,
2080 			      G_TYPE_NONE, 1, G_TYPE_POINTER);
2081 
2082 	gail_canvas_init();
2083 }
2084 
2085 /* Callback used when the root item of a canvas is destroyed.  The user should
2086  * never ever do this, so we panic if this happens.
2087  */
2088 static void
panic_root_destroyed(GtkObject * object,gpointer data)2089 panic_root_destroyed (GtkObject *object, gpointer data)
2090 {
2091 	g_error ("Eeeek, root item %p of canvas %p was destroyed!", object, data);
2092 }
2093 
2094 /* Object initialization function for GnomeCanvas */
2095 static void
gnome_canvas_init(GnomeCanvas * canvas)2096 gnome_canvas_init (GnomeCanvas *canvas)
2097 {
2098 	GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
2099 
2100 	canvas->need_update = FALSE;
2101 	canvas->need_redraw = FALSE;
2102 	canvas->redraw_area = NULL;
2103 	canvas->idle_id = 0;
2104 
2105 	canvas->scroll_x1 = 0.0;
2106 	canvas->scroll_y1 = 0.0;
2107 	canvas->scroll_x2 = canvas->layout.width;
2108 	canvas->scroll_y2 = canvas->layout.height;
2109 
2110 	canvas->pixels_per_unit = 1.0;
2111 
2112 	canvas->pick_event.type = GDK_LEAVE_NOTIFY;
2113 	canvas->pick_event.crossing.x = 0;
2114 	canvas->pick_event.crossing.y = 0;
2115 
2116 	canvas->dither = GDK_RGB_DITHER_MAX;
2117 
2118 	/* This may not be what people want, but it is set to be turned on by
2119 	 * default to have the same initial behavior as the canvas in GNOME 1.4.
2120 	 */
2121 	canvas->center_scroll_region = TRUE;
2122 
2123 	gtk_layout_set_hadjustment (GTK_LAYOUT (canvas), NULL);
2124 	gtk_layout_set_vadjustment (GTK_LAYOUT (canvas), NULL);
2125 
2126 	/* Disable the gtk+ double buffering since the canvas uses it's own. */
2127 	gtk_widget_set_double_buffered (GTK_WIDGET (canvas), FALSE);
2128 
2129 	/* Create the root item as a special case */
2130 
2131 	canvas->root = GNOME_CANVAS_ITEM (g_object_new (gnome_canvas_group_get_type (), NULL));
2132 	canvas->root->canvas = canvas;
2133 
2134 	g_object_ref_sink (canvas->root);
2135 
2136 	canvas->root_destroy_id = g_signal_connect (canvas->root, "destroy",
2137 						    G_CALLBACK (panic_root_destroyed),
2138 						    canvas);
2139 
2140 	canvas->need_repick = TRUE;
2141 }
2142 
2143 /* Convenience function to remove the idle handler of a canvas */
2144 static void
remove_idle(GnomeCanvas * canvas)2145 remove_idle (GnomeCanvas *canvas)
2146 {
2147 	if (canvas->idle_id == 0)
2148 		return;
2149 
2150 	g_source_remove (canvas->idle_id);
2151 	canvas->idle_id = 0;
2152 }
2153 
2154 /* Removes the transient state of the canvas (idle handler, grabs). */
2155 static void
shutdown_transients(GnomeCanvas * canvas)2156 shutdown_transients (GnomeCanvas *canvas)
2157 {
2158 	/* We turn off the need_redraw flag, since if the canvas is mapped again
2159 	 * it will request a redraw anyways.  We do not turn off the need_update
2160 	 * flag, though, because updates are not queued when the canvas remaps
2161 	 * itself.
2162 	 */
2163 	if (canvas->need_redraw) {
2164 		canvas->need_redraw = FALSE;
2165 		art_uta_free (canvas->redraw_area);
2166 		canvas->redraw_area = NULL;
2167 		canvas->redraw_x1 = 0;
2168 		canvas->redraw_y1 = 0;
2169 		canvas->redraw_x2 = 0;
2170 		canvas->redraw_y2 = 0;
2171 	}
2172 
2173 	if (canvas->grabbed_item) {
2174 		canvas->grabbed_item = NULL;
2175 		gdk_pointer_ungrab (GDK_CURRENT_TIME);
2176 	}
2177 
2178 	remove_idle (canvas);
2179 }
2180 
2181 /* Destroy handler for GnomeCanvas */
2182 static void
gnome_canvas_destroy(GtkObject * object)2183 gnome_canvas_destroy (GtkObject *object)
2184 {
2185 	GnomeCanvas *canvas;
2186 
2187 	g_return_if_fail (GNOME_IS_CANVAS (object));
2188 
2189 	/* remember, destroy can be run multiple times! */
2190 
2191 	canvas = GNOME_CANVAS (object);
2192 
2193 	if (canvas->root_destroy_id) {
2194 		g_signal_handler_disconnect (canvas->root, canvas->root_destroy_id);
2195 		canvas->root_destroy_id = 0;
2196 	}
2197 	if (canvas->root) {
2198 		gtk_object_destroy (GTK_OBJECT (canvas->root));
2199 		g_object_unref (G_OBJECT (canvas->root));
2200 		canvas->root = NULL;
2201 	}
2202 
2203 	shutdown_transients (canvas);
2204 
2205 	if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
2206 		(* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
2207 }
2208 
2209 /**
2210  * gnome_canvas_new:
2211  *
2212  * Creates a new empty canvas in non-antialiased mode.
2213  *
2214  * Return value: A newly-created canvas.
2215  **/
2216 GtkWidget *
gnome_canvas_new(void)2217 gnome_canvas_new (void)
2218 {
2219 	return GTK_WIDGET (g_object_new (gnome_canvas_get_type (), NULL));
2220 }
2221 
2222 /**
2223  * gnome_canvas_new_aa:
2224  *
2225  * Creates a new empty canvas in antialiased mode.
2226  *
2227  * Return value: A newly-created antialiased canvas.
2228  **/
2229 GtkWidget *
gnome_canvas_new_aa(void)2230 gnome_canvas_new_aa (void)
2231 {
2232 	return GTK_WIDGET (g_object_new (GNOME_TYPE_CANVAS,
2233 					 "aa", TRUE,
2234 					 NULL));
2235 }
2236 
2237 /* Map handler for the canvas */
2238 static void
gnome_canvas_map(GtkWidget * widget)2239 gnome_canvas_map (GtkWidget *widget)
2240 {
2241 	GnomeCanvas *canvas;
2242 
2243 	g_return_if_fail (GNOME_IS_CANVAS (widget));
2244 
2245 	/* Normal widget mapping stuff */
2246 
2247 	if (GTK_WIDGET_CLASS (canvas_parent_class)->map)
2248 		(* GTK_WIDGET_CLASS (canvas_parent_class)->map) (widget);
2249 
2250 	canvas = GNOME_CANVAS (widget);
2251 
2252 	if (canvas->need_update)
2253 		add_idle (canvas);
2254 
2255 	/* Map items */
2256 
2257 	if (GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->map)
2258 		(* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->map) (canvas->root);
2259 }
2260 
2261 /* Unmap handler for the canvas */
2262 static void
gnome_canvas_unmap(GtkWidget * widget)2263 gnome_canvas_unmap (GtkWidget *widget)
2264 {
2265 	GnomeCanvas *canvas;
2266 
2267 	g_return_if_fail (GNOME_IS_CANVAS (widget));
2268 
2269 	canvas = GNOME_CANVAS (widget);
2270 
2271 	shutdown_transients (canvas);
2272 
2273 	/* Unmap items */
2274 
2275 	if (GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->unmap)
2276 		(* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->unmap) (canvas->root);
2277 
2278 	/* Normal widget unmapping stuff */
2279 
2280 	if (GTK_WIDGET_CLASS (canvas_parent_class)->unmap)
2281 		(* GTK_WIDGET_CLASS (canvas_parent_class)->unmap) (widget);
2282 }
2283 
2284 /* Realize handler for the canvas */
2285 static void
gnome_canvas_realize(GtkWidget * widget)2286 gnome_canvas_realize (GtkWidget *widget)
2287 {
2288 	GnomeCanvas *canvas;
2289 
2290 	g_return_if_fail (GNOME_IS_CANVAS (widget));
2291 
2292 	/* Normal widget realization stuff */
2293 
2294 	if (GTK_WIDGET_CLASS (canvas_parent_class)->realize)
2295 		(* GTK_WIDGET_CLASS (canvas_parent_class)->realize) (widget);
2296 
2297 	canvas = GNOME_CANVAS (widget);
2298 
2299 	gdk_window_set_events (canvas->layout.bin_window,
2300 			       (gdk_window_get_events (canvas->layout.bin_window)
2301 				 | GDK_EXPOSURE_MASK
2302 				 | GDK_BUTTON_PRESS_MASK
2303 				 | GDK_BUTTON_RELEASE_MASK
2304 				 | GDK_POINTER_MOTION_MASK
2305 				 | GDK_KEY_PRESS_MASK
2306 				 | GDK_KEY_RELEASE_MASK
2307 				 | GDK_ENTER_NOTIFY_MASK
2308 				 | GDK_LEAVE_NOTIFY_MASK
2309 				 | GDK_FOCUS_CHANGE_MASK));
2310 
2311 	/* Create our own temporary pixmap gc and realize all the items */
2312 
2313 	canvas->pixmap_gc = gdk_gc_new (canvas->layout.bin_window);
2314 
2315 	(* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->realize) (canvas->root);
2316 }
2317 
2318 /* Unrealize handler for the canvas */
2319 static void
gnome_canvas_unrealize(GtkWidget * widget)2320 gnome_canvas_unrealize (GtkWidget *widget)
2321 {
2322 	GnomeCanvas *canvas;
2323 
2324 	g_return_if_fail (GNOME_IS_CANVAS (widget));
2325 
2326 	canvas = GNOME_CANVAS (widget);
2327 
2328 	shutdown_transients (canvas);
2329 
2330 	/* Unrealize items and parent widget */
2331 
2332 	(* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->unrealize) (canvas->root);
2333 
2334 	g_object_unref (canvas->pixmap_gc);
2335 	canvas->pixmap_gc = NULL;
2336 
2337 	if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
2338 		(* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
2339 }
2340 
2341 /* Handles scrolling of the canvas.  Adjusts the scrolling and zooming offset to
2342  * keep as much as possible of the canvas scrolling region in view.
2343  */
2344 static void
scroll_to(GnomeCanvas * canvas,int cx,int cy)2345 scroll_to (GnomeCanvas *canvas, int cx, int cy)
2346 {
2347 	int scroll_width, scroll_height;
2348 	int right_limit, bottom_limit;
2349 	int old_zoom_xofs, old_zoom_yofs;
2350 	int changed_x = FALSE, changed_y = FALSE;
2351 	int canvas_width, canvas_height;
2352 
2353 	canvas_width = GTK_WIDGET (canvas)->allocation.width;
2354 	canvas_height = GTK_WIDGET (canvas)->allocation.height;
2355 
2356 	scroll_width = floor ((canvas->scroll_x2 - canvas->scroll_x1) * canvas->pixels_per_unit
2357 			      + 0.5);
2358 	scroll_height = floor ((canvas->scroll_y2 - canvas->scroll_y1) * canvas->pixels_per_unit
2359 			       + 0.5);
2360 
2361 	right_limit = scroll_width - canvas_width;
2362 	bottom_limit = scroll_height - canvas_height;
2363 
2364 	old_zoom_xofs = canvas->zoom_xofs;
2365 	old_zoom_yofs = canvas->zoom_yofs;
2366 
2367 	if (right_limit < 0) {
2368 		cx = 0;
2369 
2370 		if (canvas->center_scroll_region) {
2371 			canvas->zoom_xofs = (canvas_width - scroll_width) / 2;
2372 			scroll_width = canvas_width;
2373 		} else
2374 			canvas->zoom_xofs = 0;
2375 	} else if (cx < 0) {
2376 		cx = 0;
2377 		canvas->zoom_xofs = 0;
2378 	} else if (cx > right_limit) {
2379 		cx = right_limit;
2380 		canvas->zoom_xofs = 0;
2381 	} else
2382 		canvas->zoom_xofs = 0;
2383 
2384 	if (bottom_limit < 0) {
2385 		cy = 0;
2386 
2387 		if (canvas->center_scroll_region) {
2388 			canvas->zoom_yofs = (canvas_height - scroll_height) / 2;
2389 			scroll_height = canvas_height;
2390 		} else
2391 			canvas->zoom_yofs = 0;
2392 	} else if (cy < 0) {
2393 		cy = 0;
2394 		canvas->zoom_yofs = 0;
2395 	} else if (cy > bottom_limit) {
2396 		cy = bottom_limit;
2397 		canvas->zoom_yofs = 0;
2398 	} else
2399 		canvas->zoom_yofs = 0;
2400 
2401 	if ((canvas->zoom_xofs != old_zoom_xofs) || (canvas->zoom_yofs != old_zoom_yofs)) {
2402 		/* This can only occur, if either canvas size or widget size changes */
2403 		/* So I think we can request full redraw here */
2404 		/* The reason is, that coverage UTA will be invalidated by offset change */
2405 		/* fixme: Strictly this is not correct - we have to remove our own idle (Lauris) */
2406 		/* More stuff - we have to mark root as needing fresh affine (Lauris) */
2407 		if (!(canvas->root->object.flags & GNOME_CANVAS_ITEM_NEED_AFFINE)) {
2408 			canvas->root->object.flags |= GNOME_CANVAS_ITEM_NEED_AFFINE;
2409 			gnome_canvas_request_update (canvas);
2410 		}
2411 		gtk_widget_queue_draw (GTK_WIDGET (canvas));
2412 	}
2413 
2414 	if (canvas->layout.hadjustment && ((int) canvas->layout.hadjustment->value) != cx) {
2415 		canvas->layout.hadjustment->value = cx;
2416 		changed_x = TRUE;
2417 	}
2418 
2419 	if (canvas->layout.vadjustment && ((int) canvas->layout.vadjustment->value) != cy) {
2420 		canvas->layout.vadjustment->value = cy;
2421 		changed_y = TRUE;
2422 	}
2423 
2424 	if ((scroll_width != (int) canvas->layout.width)
2425 	    || (scroll_height != (int) canvas->layout.height))
2426 		gtk_layout_set_size (GTK_LAYOUT (canvas), scroll_width, scroll_height);
2427 
2428 	/* Signal GtkLayout that it should do a redraw. */
2429 
2430 	if (changed_x)
2431 		g_signal_emit_by_name (canvas->layout.hadjustment, "value_changed");
2432 
2433 	if (changed_y)
2434 		g_signal_emit_by_name (canvas->layout.vadjustment, "value_changed");
2435 }
2436 
2437 /* Size allocation handler for the canvas */
2438 static void
gnome_canvas_size_allocate(GtkWidget * widget,GtkAllocation * allocation)2439 gnome_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
2440 {
2441 	GnomeCanvas *canvas;
2442 
2443 	g_return_if_fail (GNOME_IS_CANVAS (widget));
2444 	g_return_if_fail (allocation != NULL);
2445 
2446 	if (GTK_WIDGET_CLASS (canvas_parent_class)->size_allocate)
2447 		(* GTK_WIDGET_CLASS (canvas_parent_class)->size_allocate) (widget, allocation);
2448 
2449 	canvas = GNOME_CANVAS (widget);
2450 
2451 	/* Recenter the view, if appropriate */
2452 
2453 	canvas->layout.hadjustment->page_size = allocation->width;
2454 	canvas->layout.hadjustment->page_increment = allocation->width / 2;
2455 
2456 	canvas->layout.vadjustment->page_size = allocation->height;
2457 	canvas->layout.vadjustment->page_increment = allocation->height / 2;
2458 
2459 	scroll_to (canvas,
2460 		   canvas->layout.hadjustment->value,
2461 		   canvas->layout.vadjustment->value);
2462 
2463 	g_signal_emit_by_name (canvas->layout.hadjustment, "changed");
2464 	g_signal_emit_by_name (canvas->layout.vadjustment, "changed");
2465 }
2466 
2467 /* Emits an event for an item in the canvas, be it the current item, grabbed
2468  * item, or focused item, as appropriate.
2469  */
2470 
2471 static int
emit_event(GnomeCanvas * canvas,GdkEvent * event)2472 emit_event (GnomeCanvas *canvas, GdkEvent *event)
2473 {
2474 	GdkEvent *ev;
2475 	gint finished;
2476 	GnomeCanvasItem *item;
2477 	GnomeCanvasItem *parent;
2478 	guint mask;
2479 
2480 	/* Perform checks for grabbed items */
2481 
2482 	if (canvas->grabbed_item &&
2483 	    !is_descendant (canvas->current_item, canvas->grabbed_item)) {
2484 		/* I think this warning is annoying and I don't know what it's for
2485 		 * so I'll disable it for now.
2486 		 */
2487 /*                g_warning ("emit_event() returning FALSE!\n");*/
2488 		return FALSE;
2489         }
2490 
2491 	if (canvas->grabbed_item) {
2492 		switch (event->type) {
2493 		case GDK_ENTER_NOTIFY:
2494 			mask = GDK_ENTER_NOTIFY_MASK;
2495 			break;
2496 
2497 		case GDK_LEAVE_NOTIFY:
2498 			mask = GDK_LEAVE_NOTIFY_MASK;
2499 			break;
2500 
2501 		case GDK_MOTION_NOTIFY:
2502 			mask = GDK_POINTER_MOTION_MASK;
2503 			break;
2504 
2505 		case GDK_BUTTON_PRESS:
2506 		case GDK_2BUTTON_PRESS:
2507 		case GDK_3BUTTON_PRESS:
2508 			mask = GDK_BUTTON_PRESS_MASK;
2509 			break;
2510 
2511 		case GDK_BUTTON_RELEASE:
2512 			mask = GDK_BUTTON_RELEASE_MASK;
2513 			break;
2514 
2515 		case GDK_KEY_PRESS:
2516 			mask = GDK_KEY_PRESS_MASK;
2517 			break;
2518 
2519 		case GDK_KEY_RELEASE:
2520 			mask = GDK_KEY_RELEASE_MASK;
2521 			break;
2522 
2523 		case GDK_SCROLL:
2524 			mask = GDK_SCROLL_MASK;
2525 			break;
2526 
2527 		default:
2528 			mask = 0;
2529 			break;
2530 		}
2531 
2532 		if (!(mask & canvas->grabbed_event_mask))
2533 			return FALSE;
2534 	}
2535 
2536 	/* Convert to world coordinates -- we have two cases because of diferent
2537 	 * offsets of the fields in the event structures.
2538 	 */
2539 
2540 	ev = gdk_event_copy (event);
2541 
2542 	switch (ev->type)
2543         {
2544 	case GDK_ENTER_NOTIFY:
2545 	case GDK_LEAVE_NOTIFY:
2546 		gnome_canvas_window_to_world (canvas,
2547 					      ev->crossing.x, ev->crossing.y,
2548 					      &ev->crossing.x, &ev->crossing.y);
2549 		break;
2550 
2551 	case GDK_MOTION_NOTIFY:
2552 	case GDK_BUTTON_PRESS:
2553 	case GDK_2BUTTON_PRESS:
2554 	case GDK_3BUTTON_PRESS:
2555 	case GDK_BUTTON_RELEASE:
2556                 gnome_canvas_window_to_world (canvas,
2557                                               ev->motion.x, ev->motion.y,
2558                                               &ev->motion.x, &ev->motion.y);
2559                 break;
2560 
2561 	default:
2562 		break;
2563 	}
2564 
2565 	/* Choose where we send the event */
2566 
2567 	item = canvas->current_item;
2568 
2569 	if (canvas->focused_item
2570 	    && ((event->type == GDK_KEY_PRESS) ||
2571 		(event->type == GDK_KEY_RELEASE) ||
2572 		(event->type == GDK_FOCUS_CHANGE)))
2573 		item = canvas->focused_item;
2574 
2575 	/* The event is propagated up the hierarchy (for if someone connected to
2576 	 * a group instead of a leaf event), and emission is stopped if a
2577 	 * handler returns TRUE, just like for GtkWidget events.
2578 	 */
2579 
2580 	finished = FALSE;
2581 
2582 	while (item && !finished) {
2583 		g_object_ref (G_OBJECT (item));
2584 
2585 		g_signal_emit (item, item_signals[ITEM_EVENT], 0,
2586 			       ev, &finished);
2587 
2588 		parent = item->parent;
2589 		g_object_unref (G_OBJECT (item));
2590 
2591 		item = parent;
2592 	}
2593 
2594 	gdk_event_free (ev);
2595 
2596 	return finished;
2597 }
2598 
2599 /* Re-picks the current item in the canvas, based on the event's coordinates.
2600  * Also emits enter/leave events for items as appropriate.
2601  */
2602 static int
pick_current_item(GnomeCanvas * canvas,GdkEvent * event)2603 pick_current_item (GnomeCanvas *canvas, GdkEvent *event)
2604 {
2605 	int button_down;
2606 	double x, y;
2607 	int cx, cy;
2608 	int retval;
2609 
2610 	retval = FALSE;
2611 
2612 	/* If a button is down, we'll perform enter and leave events on the
2613 	 * current item, but not enter on any other item.  This is more or less
2614 	 * like X pointer grabbing for canvas items.
2615 	 */
2616 	button_down = canvas->state & (GDK_BUTTON1_MASK
2617 				       | GDK_BUTTON2_MASK
2618 				       | GDK_BUTTON3_MASK
2619 				       | GDK_BUTTON4_MASK
2620 				       | GDK_BUTTON5_MASK);
2621 	if (!button_down)
2622 		canvas->left_grabbed_item = FALSE;
2623 
2624 	/* Save the event in the canvas.  This is used to synthesize enter and
2625 	 * leave events in case the current item changes.  It is also used to
2626 	 * re-pick the current item if the current one gets deleted.  Also,
2627 	 * synthesize an enter event.
2628 	 */
2629 	if (event != &canvas->pick_event) {
2630 		if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
2631 			/* these fields have the same offsets in both types of events */
2632 
2633 			canvas->pick_event.crossing.type       = GDK_ENTER_NOTIFY;
2634 			canvas->pick_event.crossing.window     = event->motion.window;
2635 			canvas->pick_event.crossing.send_event = event->motion.send_event;
2636 			canvas->pick_event.crossing.subwindow  = NULL;
2637 			canvas->pick_event.crossing.x          = event->motion.x;
2638 			canvas->pick_event.crossing.y          = event->motion.y;
2639 			canvas->pick_event.crossing.mode       = GDK_CROSSING_NORMAL;
2640 			canvas->pick_event.crossing.detail     = GDK_NOTIFY_NONLINEAR;
2641 			canvas->pick_event.crossing.focus      = FALSE;
2642 			canvas->pick_event.crossing.state      = event->motion.state;
2643 
2644 			/* these fields don't have the same offsets in both types of events */
2645 
2646 			if (event->type == GDK_MOTION_NOTIFY) {
2647 				canvas->pick_event.crossing.x_root = event->motion.x_root;
2648 				canvas->pick_event.crossing.y_root = event->motion.y_root;
2649 			} else {
2650 				canvas->pick_event.crossing.x_root = event->button.x_root;
2651 				canvas->pick_event.crossing.y_root = event->button.y_root;
2652 			}
2653 		} else
2654 			canvas->pick_event = *event;
2655 	}
2656 
2657 	/* Don't do anything else if this is a recursive call */
2658 
2659 	if (canvas->in_repick)
2660 		return retval;
2661 
2662 	/* LeaveNotify means that there is no current item, so we don't look for one */
2663 
2664 	if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
2665 		/* these fields don't have the same offsets in both types of events */
2666 
2667 		if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
2668 			x = canvas->pick_event.crossing.x - canvas->zoom_xofs;
2669 			y = canvas->pick_event.crossing.y - canvas->zoom_yofs;
2670 		} else {
2671 			x = canvas->pick_event.motion.x - canvas->zoom_xofs;
2672 			y = canvas->pick_event.motion.y - canvas->zoom_yofs;
2673 		}
2674 
2675 		/* canvas pixel coords */
2676 
2677 		cx = (int) (x + 0.5);
2678 		cy = (int) (y + 0.5);
2679 
2680 		/* world coords */
2681 
2682 		x = canvas->scroll_x1 + x / canvas->pixels_per_unit;
2683 		y = canvas->scroll_y1 + y / canvas->pixels_per_unit;
2684 
2685 		/* find the closest item */
2686 
2687 		if (canvas->root->object.flags & GNOME_CANVAS_ITEM_VISIBLE)
2688 			gnome_canvas_item_invoke_point (canvas->root, x, y, cx, cy,
2689 							&canvas->new_current_item);
2690 		else
2691 			canvas->new_current_item = NULL;
2692 	} else
2693 		canvas->new_current_item = NULL;
2694 
2695 	if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item)
2696 		return retval; /* current item did not change */
2697 
2698 	/* Synthesize events for old and new current items */
2699 
2700 	if ((canvas->new_current_item != canvas->current_item)
2701 	    && (canvas->current_item != NULL)
2702 	    && !canvas->left_grabbed_item) {
2703 		GdkEvent new_event;
2704 
2705 		new_event = canvas->pick_event;
2706 		new_event.type = GDK_LEAVE_NOTIFY;
2707 
2708 		new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
2709 		new_event.crossing.subwindow = NULL;
2710 		canvas->in_repick = TRUE;
2711 		retval = emit_event (canvas, &new_event);
2712 		canvas->in_repick = FALSE;
2713 	}
2714 
2715 	/* new_current_item may have been set to NULL during the call to emit_event() above */
2716 
2717 	if ((canvas->new_current_item != canvas->current_item) && button_down) {
2718 		canvas->left_grabbed_item = TRUE;
2719 		return retval;
2720 	}
2721 
2722 	/* Handle the rest of cases */
2723 
2724 	canvas->left_grabbed_item = FALSE;
2725 	canvas->current_item = canvas->new_current_item;
2726 
2727 	if (canvas->current_item != NULL) {
2728 		GdkEvent new_event;
2729 
2730 		new_event = canvas->pick_event;
2731 		new_event.type = GDK_ENTER_NOTIFY;
2732 		new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
2733 		new_event.crossing.subwindow = NULL;
2734 		retval = emit_event (canvas, &new_event);
2735 	}
2736 
2737 	return retval;
2738 }
2739 
2740 /* Button event handler for the canvas */
2741 static gint
gnome_canvas_button(GtkWidget * widget,GdkEventButton * event)2742 gnome_canvas_button (GtkWidget *widget, GdkEventButton *event)
2743 {
2744 	GnomeCanvas *canvas;
2745 	int mask;
2746 	int retval;
2747 
2748 	g_return_val_if_fail (GNOME_IS_CANVAS (widget), FALSE);
2749 	g_return_val_if_fail (event != NULL, FALSE);
2750 
2751 	retval = FALSE;
2752 
2753 	canvas = GNOME_CANVAS (widget);
2754 
2755 	/*
2756 	 * dispatch normally regardless of the event's window if an item has
2757 	 * has a pointer grab in effect
2758 	 */
2759 	if (!canvas->grabbed_item && event->window != canvas->layout.bin_window)
2760 		return retval;
2761 
2762 	switch (event->button) {
2763 	case 1:
2764 		mask = GDK_BUTTON1_MASK;
2765 		break;
2766 	case 2:
2767 		mask = GDK_BUTTON2_MASK;
2768 		break;
2769 	case 3:
2770 		mask = GDK_BUTTON3_MASK;
2771 		break;
2772 	case 4:
2773 		mask = GDK_BUTTON4_MASK;
2774 		break;
2775 	case 5:
2776 		mask = GDK_BUTTON5_MASK;
2777 		break;
2778 	default:
2779 		mask = 0;
2780 	}
2781 
2782 	switch (event->type) {
2783 	case GDK_BUTTON_PRESS:
2784 	case GDK_2BUTTON_PRESS:
2785 	case GDK_3BUTTON_PRESS:
2786 		/* Pick the current item as if the button were not pressed, and
2787 		 * then process the event.
2788 		 */
2789 		canvas->state = event->state;
2790 		pick_current_item (canvas, (GdkEvent *) event);
2791 		canvas->state ^= mask;
2792 		retval = emit_event (canvas, (GdkEvent *) event);
2793 		break;
2794 
2795 	case GDK_BUTTON_RELEASE:
2796 		/* Process the event as if the button were pressed, then repick
2797 		 * after the button has been released
2798 		 */
2799 		canvas->state = event->state;
2800 		retval = emit_event (canvas, (GdkEvent *) event);
2801 		event->state ^= mask;
2802 		canvas->state = event->state;
2803 		pick_current_item (canvas, (GdkEvent *) event);
2804 		event->state ^= mask;
2805 		break;
2806 
2807 	default:
2808 		g_assert_not_reached ();
2809 	}
2810 
2811 	return retval;
2812 }
2813 
2814 /* Motion event handler for the canvas */
2815 static gint
gnome_canvas_motion(GtkWidget * widget,GdkEventMotion * event)2816 gnome_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
2817 {
2818 	GnomeCanvas *canvas;
2819 
2820 	g_return_val_if_fail (GNOME_IS_CANVAS (widget), FALSE);
2821 	g_return_val_if_fail (event != NULL, FALSE);
2822 
2823 	canvas = GNOME_CANVAS (widget);
2824 
2825 	if (event->window != canvas->layout.bin_window)
2826 		return FALSE;
2827 
2828 	canvas->state = event->state;
2829 	pick_current_item (canvas, (GdkEvent *) event);
2830 	return emit_event (canvas, (GdkEvent *) event);
2831 }
2832 
2833 static gboolean
gnome_canvas_scroll(GtkWidget * widget,GdkEventScroll * event)2834 gnome_canvas_scroll (GtkWidget *widget, GdkEventScroll *event)
2835 {
2836 	GnomeCanvas *canvas;
2837 
2838 	g_return_val_if_fail (GNOME_IS_CANVAS (widget), FALSE);
2839 	g_return_val_if_fail (event != NULL, FALSE);
2840 
2841 	canvas = GNOME_CANVAS (widget);
2842 
2843 	if (event->window != canvas->layout.bin_window)
2844 		return FALSE;
2845 
2846 	canvas->state = event->state;
2847 	pick_current_item (canvas, (GdkEvent *) event);
2848 	return emit_event (canvas, (GdkEvent *) event);
2849 }
2850 
2851 /* Key event handler for the canvas */
2852 static gboolean
gnome_canvas_key(GtkWidget * widget,GdkEventKey * event)2853 gnome_canvas_key (GtkWidget *widget, GdkEventKey *event)
2854 {
2855 	GnomeCanvas *canvas;
2856 
2857 	g_return_val_if_fail (GNOME_IS_CANVAS (widget), FALSE);
2858 	g_return_val_if_fail (event != NULL, FALSE);
2859 
2860 	canvas = GNOME_CANVAS (widget);
2861 
2862 	if (!emit_event (canvas, (GdkEvent *) event)) {
2863 		GtkWidgetClass *widget_class;
2864 
2865 		widget_class = GTK_WIDGET_CLASS (canvas_parent_class);
2866 
2867 		if (event->type == GDK_KEY_PRESS) {
2868 			if (widget_class->key_press_event)
2869 				return (* widget_class->key_press_event) (widget, event);
2870 		} else if (event->type == GDK_KEY_RELEASE) {
2871 			if (widget_class->key_release_event)
2872 				return (* widget_class->key_release_event) (widget, event);
2873 		} else
2874 			g_assert_not_reached ();
2875 
2876 		return FALSE;
2877 	} else
2878 		return TRUE;
2879 }
2880 
2881 
2882 /* Crossing event handler for the canvas */
2883 static gint
gnome_canvas_crossing(GtkWidget * widget,GdkEventCrossing * event)2884 gnome_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
2885 {
2886 	GnomeCanvas *canvas;
2887 
2888 	g_return_val_if_fail (GNOME_IS_CANVAS (widget), FALSE);
2889 	g_return_val_if_fail (event != NULL, FALSE);
2890 
2891 	canvas = GNOME_CANVAS (widget);
2892 
2893 	if (event->window != canvas->layout.bin_window)
2894 		return FALSE;
2895 
2896 	canvas->state = event->state;
2897 	return pick_current_item (canvas, (GdkEvent *) event);
2898 }
2899 
2900 /* Focus in handler for the canvas */
2901 static gint
gnome_canvas_focus_in(GtkWidget * widget,GdkEventFocus * event)2902 gnome_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
2903 {
2904 	GnomeCanvas *canvas;
2905 
2906 	GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
2907 
2908 	canvas = GNOME_CANVAS (widget);
2909 
2910 	if (canvas->focused_item)
2911 		return emit_event (canvas, (GdkEvent *) event);
2912 	else
2913 		return FALSE;
2914 }
2915 
2916 /* Focus out handler for the canvas */
2917 static gint
gnome_canvas_focus_out(GtkWidget * widget,GdkEventFocus * event)2918 gnome_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
2919 {
2920 	GnomeCanvas *canvas;
2921 
2922 	GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2923 
2924 	canvas = GNOME_CANVAS (widget);
2925 
2926 	if (canvas->focused_item)
2927 		return emit_event (canvas, (GdkEvent *) event);
2928 	else
2929 		return FALSE;
2930 }
2931 
2932 #define REDRAW_QUANTUM_SIZE 512
2933 
2934 static void
gnome_canvas_paint_rect(GnomeCanvas * canvas,gint x0,gint y0,gint x1,gint y1)2935 gnome_canvas_paint_rect (GnomeCanvas *canvas, gint x0, gint y0, gint x1, gint y1)
2936 {
2937 	GtkWidget *widget;
2938 	gint draw_x1, draw_y1;
2939 	gint draw_x2, draw_y2;
2940 	gint draw_width, draw_height;
2941 
2942 	g_return_if_fail (!canvas->need_update);
2943 
2944 	widget = GTK_WIDGET (canvas);
2945 
2946 	draw_x1 = MAX (x0, canvas->layout.hadjustment->value - canvas->zoom_xofs);
2947 	draw_y1 = MAX (y0, canvas->layout.vadjustment->value - canvas->zoom_yofs);
2948 	draw_x2 = MIN (draw_x1 + GTK_WIDGET (canvas)->allocation.width, x1);
2949 	draw_y2 = MIN (draw_y1 + GTK_WIDGET (canvas)->allocation.height, y1);
2950 
2951 	draw_width = draw_x2 - draw_x1;
2952 	draw_height = draw_y2 - draw_y1;
2953 
2954 	if (draw_width < 1 || draw_height < 1)
2955 		return;
2956 
2957 	canvas->redraw_x1 = draw_x1;
2958 	canvas->redraw_y1 = draw_y1;
2959 	canvas->redraw_x2 = draw_x2;
2960 	canvas->redraw_y2 = draw_y2;
2961 	canvas->draw_xofs = draw_x1;
2962 	canvas->draw_yofs = draw_y1;
2963 
2964 	if (canvas->aa) {
2965 		GnomeCanvasBuf buf;
2966 		guchar *px;
2967 		GdkColor *color;
2968 
2969 		px = g_new (guchar, draw_width * 3 * draw_height);
2970 
2971 		buf.buf = px;
2972 		buf.buf_rowstride = draw_width * 3;
2973 		buf.rect.x0 = draw_x1;
2974 		buf.rect.y0 = draw_y1;
2975 		buf.rect.x1 = draw_x2;
2976 		buf.rect.y1 = draw_y2;
2977 		color = &widget->style->bg[GTK_STATE_NORMAL];
2978 		buf.bg_color = (((color->red & 0xff00) << 8) | (color->green & 0xff00) | (color->blue >> 8));
2979 		buf.is_bg = 1;
2980 		buf.is_buf = 0;
2981 
2982 		g_signal_emit (G_OBJECT (canvas), canvas_signals[RENDER_BACKGROUND], 0, &buf);
2983 
2984 		if (canvas->root->object.flags & GNOME_CANVAS_ITEM_VISIBLE)
2985 			(* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->render) (canvas->root, &buf);
2986 
2987 		if (buf.is_bg) {
2988 			gdk_gc_set_rgb_fg_color (canvas->pixmap_gc, color);
2989 			gdk_draw_rectangle (canvas->layout.bin_window,
2990 					    canvas->pixmap_gc,
2991 					    TRUE,
2992 					    (draw_x1 + canvas->zoom_xofs),
2993 					    (draw_y1 + canvas->zoom_yofs),
2994 					    draw_width, draw_height);
2995 		} else {
2996 			gdk_draw_rgb_image_dithalign (canvas->layout.bin_window,
2997 						      canvas->pixmap_gc,
2998 						      (draw_x1 + canvas->zoom_xofs),
2999 						      (draw_y1 + canvas->zoom_yofs),
3000 						      draw_width, draw_height,
3001 						      canvas->dither,
3002 						      buf.buf,
3003 						      buf.buf_rowstride,
3004 						      draw_x1, draw_y1);
3005 		}
3006 
3007 		g_free (px);
3008 	} else {
3009 		GdkPixmap *pixmap;
3010 
3011 		pixmap = gdk_pixmap_new (canvas->layout.bin_window,
3012 					 draw_width, draw_height,
3013 					 gtk_widget_get_visual (widget)->depth);
3014 
3015 		g_signal_emit (G_OBJECT (canvas), canvas_signals[DRAW_BACKGROUND], 0, pixmap,
3016 			       draw_x1, draw_y1, draw_width, draw_height);
3017 
3018 		if (canvas->root->object.flags & GNOME_CANVAS_ITEM_VISIBLE)
3019 			(* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->draw) (
3020 				canvas->root, pixmap,
3021 				draw_x1, draw_y1,
3022 				draw_width, draw_height);
3023 
3024 		/* Copy the pixmap to the window and clean up */
3025 
3026 		gdk_draw_drawable (canvas->layout.bin_window,
3027 				   canvas->pixmap_gc,
3028 				   pixmap,
3029 				   0, 0,
3030 				   draw_x1 + canvas->zoom_xofs,
3031 				   draw_y1 + canvas->zoom_yofs,
3032 				   draw_width, draw_height);
3033 
3034 		g_object_unref (pixmap);
3035 	}
3036 }
3037 
3038 /* Expose handler for the canvas */
3039 static gint
gnome_canvas_expose(GtkWidget * widget,GdkEventExpose * event)3040 gnome_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
3041 {
3042 	GnomeCanvas *canvas;
3043 	GdkRectangle *rects;
3044 	gint n_rects;
3045 	int i;
3046 
3047 	canvas = GNOME_CANVAS (widget);
3048 
3049 	if (!GTK_WIDGET_DRAWABLE (widget) || (event->window != canvas->layout.bin_window))
3050 		return FALSE;
3051 
3052 #ifdef VERBOSE
3053 	g_print ("Expose\n");
3054 #endif
3055 
3056 	gdk_region_get_rectangles (event->region, &rects, &n_rects);
3057 
3058 	for (i = 0; i < n_rects; i++) {
3059 		ArtIRect rect;
3060 
3061 		rect.x0 = rects[i].x - canvas->zoom_xofs;
3062 		rect.y0 = rects[i].y - canvas->zoom_yofs;
3063 		rect.x1 = rects[i].x + rects[i].width - canvas->zoom_xofs;
3064 		rect.y1 = rects[i].y + rects[i].height - canvas->zoom_yofs;
3065 
3066 		if (canvas->need_update || canvas->need_redraw) {
3067 			ArtUta *uta;
3068 
3069 			/* Update or drawing is scheduled, so just mark exposed area as dirty */
3070 			uta = art_uta_from_irect (&rect);
3071 			gnome_canvas_request_redraw_uta (canvas, uta);
3072 		} else {
3073 			/* No pending updates, draw exposed area immediately */
3074 			gnome_canvas_paint_rect (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
3075 
3076 			/* And call expose on parent container class */
3077 			if (GTK_WIDGET_CLASS (canvas_parent_class)->expose_event)
3078 				(* GTK_WIDGET_CLASS (canvas_parent_class)->expose_event) (
3079 					widget, event);
3080 		}
3081 	}
3082 
3083 	g_free (rects);
3084 
3085 	return FALSE;
3086 }
3087 
3088 /* Repaints the areas in the canvas that need it */
3089 static void
paint(GnomeCanvas * canvas)3090 paint (GnomeCanvas *canvas)
3091 {
3092 	ArtIRect *rects;
3093 	gint n_rects, i;
3094 	ArtIRect visible_rect;
3095 	GdkRegion *region;
3096 
3097 	/* Extract big rectangles from the microtile array */
3098 
3099 	rects = art_rect_list_from_uta (canvas->redraw_area,
3100 					REDRAW_QUANTUM_SIZE, REDRAW_QUANTUM_SIZE,
3101 					&n_rects);
3102 
3103 	art_uta_free (canvas->redraw_area);
3104 	canvas->redraw_area = NULL;
3105 	canvas->need_redraw = FALSE;
3106 
3107 	/* Turn those rectangles into a GdkRegion for exposing */
3108 
3109 	visible_rect.x0 = canvas->layout.hadjustment->value - canvas->zoom_xofs;
3110 	visible_rect.y0 = canvas->layout.vadjustment->value - canvas->zoom_yofs;
3111 	visible_rect.x1 = visible_rect.x0 + GTK_WIDGET (canvas)->allocation.width;
3112 	visible_rect.y1 = visible_rect.y0 + GTK_WIDGET (canvas)->allocation.height;
3113 
3114 	region = gdk_region_new ();
3115 
3116 	for (i = 0; i < n_rects; i++) {
3117 		ArtIRect clipped;
3118 
3119 		art_irect_intersect (&clipped, &visible_rect, rects + i);
3120 		if (!art_irect_empty (&clipped)) {
3121 			GdkRectangle gdkrect;
3122 
3123 			gdkrect.x = clipped.x0 + canvas->zoom_xofs;
3124 			gdkrect.y = clipped.y0 + canvas->zoom_yofs;
3125 			gdkrect.width = clipped.x1 - clipped.x0;
3126 			gdkrect.height = clipped.y1 - clipped.y0;
3127 
3128 			region = gdk_region_rectangle (&gdkrect);
3129 			gdk_window_invalidate_region (canvas->layout.bin_window, region, FALSE);
3130 			gdk_region_destroy (region);
3131 		}
3132 	}
3133 
3134 	art_free (rects);
3135 
3136 	canvas->redraw_x1 = 0;
3137 	canvas->redraw_y1 = 0;
3138 	canvas->redraw_x2 = 0;
3139 	canvas->redraw_y2 = 0;
3140 }
3141 
3142 static void
gnome_canvas_draw_background(GnomeCanvas * canvas,GdkDrawable * drawable,int x,int y,int width,int height)3143 gnome_canvas_draw_background (GnomeCanvas *canvas, GdkDrawable *drawable,
3144 			      int x, int y, int width, int height)
3145 {
3146 	/* By default, we use the style background. */
3147 	gdk_gc_set_foreground (canvas->pixmap_gc,
3148 			       &GTK_WIDGET (canvas)->style->bg[GTK_STATE_NORMAL]);
3149 	gdk_draw_rectangle (drawable,
3150 			    canvas->pixmap_gc,
3151 			    TRUE,
3152 			    0, 0,
3153 			    width, height);
3154 }
3155 
3156 static void
do_update(GnomeCanvas * canvas)3157 do_update (GnomeCanvas *canvas)
3158 {
3159 	/* Cause the update if necessary */
3160 
3161 update_again:
3162 	if (canvas->need_update) {
3163 		gdouble w2cpx[6];
3164 
3165 		/* We start updating root with w2cpx affine */
3166 		w2cpx[0] = canvas->pixels_per_unit;
3167 		w2cpx[1] = 0.0;
3168 		w2cpx[2] = 0.0;
3169 		w2cpx[3] = canvas->pixels_per_unit;
3170 		w2cpx[4] = -canvas->scroll_x1 * canvas->pixels_per_unit;
3171 		w2cpx[5] = -canvas->scroll_y1 * canvas->pixels_per_unit;
3172 
3173 		gnome_canvas_item_invoke_update (canvas->root, w2cpx, NULL, 0);
3174 
3175 		canvas->need_update = FALSE;
3176 	}
3177 
3178 	/* Pick new current item */
3179 
3180 	while (canvas->need_repick) {
3181 		canvas->need_repick = FALSE;
3182 		pick_current_item (canvas, &canvas->pick_event);
3183 	}
3184 
3185 	/* it is possible that during picking we emitted an event in which
3186 	   the user then called some function which then requested update
3187 	   of something.  Without this we'd be left in a state where
3188 	   need_update would have been left TRUE and the canvas would have
3189 	   been left unpainted. */
3190 	if (canvas->need_update) {
3191 		goto update_again;
3192 	}
3193 
3194 	/* Paint if able to */
3195 
3196 	if (GTK_WIDGET_DRAWABLE (canvas) && canvas->need_redraw)
3197 		paint (canvas);
3198 }
3199 
3200 /* Idle handler for the canvas.  It deals with pending updates and redraws. */
3201 static gboolean
idle_handler(gpointer data)3202 idle_handler (gpointer data)
3203 {
3204 	GnomeCanvas *canvas;
3205 
3206 	GDK_THREADS_ENTER ();
3207 
3208 	canvas = GNOME_CANVAS (data);
3209 
3210 	do_update (canvas);
3211 
3212 	/* Reset idle id */
3213 	canvas->idle_id = 0;
3214 
3215 	GDK_THREADS_LEAVE ();
3216 
3217 	return FALSE;
3218 }
3219 
3220 /* Convenience function to add an idle handler to a canvas */
3221 static void
add_idle(GnomeCanvas * canvas)3222 add_idle (GnomeCanvas *canvas)
3223 {
3224 	g_assert (canvas->need_update || canvas->need_redraw);
3225 
3226 	if (!canvas->idle_id)
3227 		canvas->idle_id = g_idle_add_full (CANVAS_IDLE_PRIORITY,
3228 						   idle_handler,
3229 						   canvas,
3230 						   NULL);
3231 
3232 /*  	canvas->idle_id = gtk_idle_add (idle_handler, canvas); */
3233 }
3234 
3235 /**
3236  * gnome_canvas_root:
3237  * @canvas: A canvas.
3238  *
3239  * Queries the root group of a canvas.
3240  *
3241  * Return value: The root group of the specified canvas.
3242  **/
3243 GnomeCanvasGroup *
gnome_canvas_root(GnomeCanvas * canvas)3244 gnome_canvas_root (GnomeCanvas *canvas)
3245 {
3246 	g_return_val_if_fail (GNOME_IS_CANVAS (canvas), NULL);
3247 
3248 	return GNOME_CANVAS_GROUP (canvas->root);
3249 }
3250 
3251 
3252 /**
3253  * gnome_canvas_set_scroll_region:
3254  * @canvas: A canvas.
3255  * @x1: Leftmost limit of the scrolling region.
3256  * @y1: Upper limit of the scrolling region.
3257  * @x2: Rightmost limit of the scrolling region.
3258  * @y2: Lower limit of the scrolling region.
3259  *
3260  * Sets the scrolling region of a canvas to the specified rectangle.  The canvas
3261  * will then be able to scroll only within this region.  The view of the canvas
3262  * is adjusted as appropriate to display as much of the new region as possible.
3263  **/
3264 void
gnome_canvas_set_scroll_region(GnomeCanvas * canvas,double x1,double y1,double x2,double y2)3265 gnome_canvas_set_scroll_region (GnomeCanvas *canvas, double x1, double y1, double x2, double y2)
3266 {
3267 	double wxofs, wyofs;
3268 	int xofs, yofs;
3269 
3270 	g_return_if_fail (GNOME_IS_CANVAS (canvas));
3271 
3272 	/*
3273 	 * Set the new scrolling region.  If possible, do not move the visible contents of the
3274 	 * canvas.
3275 	 */
3276 
3277 	gnome_canvas_c2w (canvas,
3278 			  GTK_LAYOUT (canvas)->hadjustment->value + canvas->zoom_xofs,
3279 			  GTK_LAYOUT (canvas)->vadjustment->value + canvas->zoom_yofs,
3280 			  /*canvas->zoom_xofs,
3281 			  canvas->zoom_yofs,*/
3282 			  &wxofs, &wyofs);
3283 
3284 	canvas->scroll_x1 = x1;
3285 	canvas->scroll_y1 = y1;
3286 	canvas->scroll_x2 = x2;
3287 	canvas->scroll_y2 = y2;
3288 
3289 	gnome_canvas_w2c (canvas, wxofs, wyofs, &xofs, &yofs);
3290 
3291 	scroll_to (canvas, xofs, yofs);
3292 
3293 	canvas->need_repick = TRUE;
3294 #if 0
3295 	/* todo: should be requesting update */
3296 	(* GNOME_CANVAS_ITEM_CLASS (canvas->root->object.klass)->update) (
3297 		canvas->root, NULL, NULL, 0);
3298 #endif
3299 }
3300 
3301 
3302 /**
3303  * gnome_canvas_get_scroll_region:
3304  * @canvas: A canvas.
3305  * @x1: Leftmost limit of the scrolling region (return value).
3306  * @y1: Upper limit of the scrolling region (return value).
3307  * @x2: Rightmost limit of the scrolling region (return value).
3308  * @y2: Lower limit of the scrolling region (return value).
3309  *
3310  * Queries the scrolling region of a canvas.
3311  **/
3312 void
gnome_canvas_get_scroll_region(GnomeCanvas * canvas,double * x1,double * y1,double * x2,double * y2)3313 gnome_canvas_get_scroll_region (GnomeCanvas *canvas, double *x1, double *y1, double *x2, double *y2)
3314 {
3315 	g_return_if_fail (GNOME_IS_CANVAS (canvas));
3316 
3317 	if (x1)
3318 		*x1 = canvas->scroll_x1;
3319 
3320 	if (y1)
3321 		*y1 = canvas->scroll_y1;
3322 
3323 	if (x2)
3324 		*x2 = canvas->scroll_x2;
3325 
3326 	if (y2)
3327 		*y2 = canvas->scroll_y2;
3328 }
3329 
3330 /**
3331  * gnome_canvas_set_center_scroll_region:
3332  * @canvas: A canvas.
3333  * @center_scroll_region: Whether to center the scrolling region in the canvas
3334  * window when it is smaller than the canvas' allocation.
3335  *
3336  * When the scrolling region of the canvas is smaller than the canvas window,
3337  * e.g.  the allocation of the canvas, it can be either centered on the window
3338  * or simply made to be on the upper-left corner on the window.  This function
3339  * lets you configure this property.
3340  **/
3341 void
gnome_canvas_set_center_scroll_region(GnomeCanvas * canvas,gboolean center_scroll_region)3342 gnome_canvas_set_center_scroll_region (GnomeCanvas *canvas, gboolean center_scroll_region)
3343 {
3344 	g_return_if_fail (GNOME_IS_CANVAS (canvas));
3345 
3346 	canvas->center_scroll_region = center_scroll_region != 0;
3347 
3348 	scroll_to (canvas,
3349 		   canvas->layout.hadjustment->value,
3350 		   canvas->layout.vadjustment->value);
3351 }
3352 
3353 /**
3354  * gnome_canvas_get_center_scroll_region:
3355  * @canvas: A canvas.
3356  *
3357  * Returns whether the canvas is set to center the scrolling region in the window
3358  * if the former is smaller than the canvas' allocation.
3359  *
3360  * Return value: Whether the scroll region is being centered in the canvas window.
3361  **/
3362 gboolean
gnome_canvas_get_center_scroll_region(GnomeCanvas * canvas)3363 gnome_canvas_get_center_scroll_region (GnomeCanvas *canvas)
3364 {
3365 	g_return_val_if_fail (GNOME_IS_CANVAS (canvas), FALSE);
3366 
3367 	return canvas->center_scroll_region ? TRUE : FALSE;
3368 }
3369 
3370 /**
3371  * gnome_canvas_set_pixels_per_unit:
3372  * @canvas: A canvas.
3373  * @n: The number of pixels that correspond to one canvas unit.
3374  *
3375  * Sets the zooming factor of a canvas by specifying the number of pixels that
3376  * correspond to one canvas unit.
3377  *
3378  * The anchor point for zooming, i.e. the point that stays fixed and all others
3379  * zoom inwards or outwards from it, depends on whether the canvas is set to
3380  * center the scrolling region or not.  You can control this using the
3381  * gnome_canvas_set_center_scroll_region() function.  If the canvas is set to
3382  * center the scroll region, then the center of the canvas window is used as the
3383  * anchor point for zooming.  Otherwise, the upper-left corner of the canvas
3384  * window is used as the anchor point.
3385  **/
3386 void
gnome_canvas_set_pixels_per_unit(GnomeCanvas * canvas,double n)3387 gnome_canvas_set_pixels_per_unit (GnomeCanvas *canvas, double n)
3388 {
3389 	double ax, ay;
3390 	int x1, y1;
3391 	int anchor_x, anchor_y;
3392 
3393 	g_return_if_fail (GNOME_IS_CANVAS (canvas));
3394 	g_return_if_fail (n > GNOME_CANVAS_EPSILON);
3395 
3396 	if (canvas->center_scroll_region) {
3397 		anchor_x = GTK_WIDGET (canvas)->allocation.width / 2;
3398 		anchor_y = GTK_WIDGET (canvas)->allocation.height / 2;
3399 	} else
3400 		anchor_x = anchor_y = 0;
3401 
3402 	/* Find the coordinates of the anchor point in units. */
3403 	if(canvas->layout.hadjustment) {
3404 		ax = (canvas->layout.hadjustment->value + anchor_x) / canvas->pixels_per_unit + canvas->scroll_x1 + canvas->zoom_xofs;
3405 	} else {
3406 		ax = (0.0                               + anchor_x) / canvas->pixels_per_unit + canvas->scroll_x1 + canvas->zoom_xofs;
3407 	}
3408 	if(canvas->layout.hadjustment) {
3409 		ay = (canvas->layout.vadjustment->value + anchor_y) / canvas->pixels_per_unit + canvas->scroll_y1 + canvas->zoom_yofs;
3410 	} else {
3411 		ay = (0.0                               + anchor_y) / canvas->pixels_per_unit + canvas->scroll_y1 + canvas->zoom_yofs;
3412 	}
3413 
3414 	/* Now calculate the new offset of the upper left corner. */
3415 	x1 = ((ax - canvas->scroll_x1) * n) - anchor_x;
3416 	y1 = ((ay - canvas->scroll_y1) * n) - anchor_y;
3417 
3418 	canvas->pixels_per_unit = n;
3419 
3420 	scroll_to (canvas, x1, y1);
3421 
3422 	if (!(canvas->root->object.flags & GNOME_CANVAS_ITEM_NEED_AFFINE)) {
3423 		canvas->root->object.flags |= GNOME_CANVAS_ITEM_NEED_AFFINE;
3424 		gnome_canvas_request_update (canvas);
3425 	}
3426 
3427 	canvas->need_repick = TRUE;
3428 }
3429 
3430 /**
3431  * gnome_canvas_scroll_to:
3432  * @canvas: A canvas.
3433  * @cx: Horizontal scrolling offset in canvas pixel units.
3434  * @cy: Vertical scrolling offset in canvas pixel units.
3435  *
3436  * Makes a canvas scroll to the specified offsets, given in canvas pixel units.
3437  * The canvas will adjust the view so that it is not outside the scrolling
3438  * region.  This function is typically not used, as it is better to hook
3439  * scrollbars to the canvas layout's scrolling adjusments.
3440  **/
3441 void
gnome_canvas_scroll_to(GnomeCanvas * canvas,int cx,int cy)3442 gnome_canvas_scroll_to (GnomeCanvas *canvas, int cx, int cy)
3443 {
3444 	g_return_if_fail (GNOME_IS_CANVAS (canvas));
3445 
3446 	scroll_to (canvas, cx, cy);
3447 }
3448 
3449 /**
3450  * gnome_canvas_get_scroll_offsets:
3451  * @canvas: A canvas.
3452  * @cx: Horizontal scrolling offset (return value).
3453  * @cy: Vertical scrolling offset (return value).
3454  *
3455  * Queries the scrolling offsets of a canvas.  The values are returned in canvas
3456  * pixel units.
3457  **/
3458 void
gnome_canvas_get_scroll_offsets(GnomeCanvas * canvas,int * cx,int * cy)3459 gnome_canvas_get_scroll_offsets (GnomeCanvas *canvas, int *cx, int *cy)
3460 {
3461 	g_return_if_fail (GNOME_IS_CANVAS (canvas));
3462 
3463 	if (cx)
3464 		*cx = canvas->layout.hadjustment->value;
3465 
3466 	if (cy)
3467 		*cy = canvas->layout.vadjustment->value;
3468 }
3469 
3470 /**
3471  * gnome_canvas_update_now:
3472  * @canvas: A canvas.
3473  *
3474  * Forces an immediate update and redraw of a canvas.  If the canvas does not
3475  * have any pending update or redraw requests, then no action is taken.  This is
3476  * typically only used by applications that need explicit control of when the
3477  * display is updated, like games.  It is not needed by normal applications.
3478  */
3479 void
gnome_canvas_update_now(GnomeCanvas * canvas)3480 gnome_canvas_update_now (GnomeCanvas *canvas)
3481 {
3482 	g_return_if_fail (GNOME_IS_CANVAS (canvas));
3483 
3484 	if (!(canvas->need_update || canvas->need_redraw)) {
3485 		g_assert (canvas->idle_id == 0);
3486 		g_assert (canvas->redraw_area == NULL);
3487 		return;
3488 	}
3489 
3490 	remove_idle (canvas);
3491 	do_update (canvas);
3492 }
3493 
3494 /**
3495  * gnome_canvas_get_item_at:
3496  * @canvas: A canvas.
3497  * @x: X position in world coordinates.
3498  * @y: Y position in world coordinates.
3499  *
3500  * Looks for the item that is under the specified position, which must be
3501  * specified in world coordinates.
3502  *
3503  * Return value: The sought item, or NULL if no item is at the specified
3504  * coordinates.
3505  **/
3506 GnomeCanvasItem *
gnome_canvas_get_item_at(GnomeCanvas * canvas,double x,double y)3507 gnome_canvas_get_item_at (GnomeCanvas *canvas, double x, double y)
3508 {
3509 	GnomeCanvasItem *item;
3510 	double dist;
3511 	int cx, cy;
3512 
3513 	g_return_val_if_fail (GNOME_IS_CANVAS (canvas), NULL);
3514 
3515 	gnome_canvas_w2c (canvas, x, y, &cx, &cy);
3516 
3517 	dist = gnome_canvas_item_invoke_point (canvas->root, x, y, cx, cy, &item);
3518 	if ((int) (dist * canvas->pixels_per_unit + 0.5) <= canvas->close_enough)
3519 		return item;
3520 	else
3521 		return NULL;
3522 }
3523 
3524 /* Queues an update of the canvas */
3525 static void
gnome_canvas_request_update(GnomeCanvas * canvas)3526 gnome_canvas_request_update (GnomeCanvas *canvas)
3527 {
3528 	GNOME_CANVAS_GET_CLASS (canvas)->request_update (canvas);
3529 }
3530 
3531 static void
gnome_canvas_request_update_real(GnomeCanvas * canvas)3532 gnome_canvas_request_update_real (GnomeCanvas *canvas)
3533 {
3534 	if (canvas->need_update)
3535 		return;
3536 
3537 	canvas->need_update = TRUE;
3538 	if (GTK_WIDGET_MAPPED ((GtkWidget *) canvas))
3539 		add_idle (canvas);
3540 }
3541 
3542 /* Computes the union of two microtile arrays while clipping the result to the
3543  * specified rectangle.  Any of the specified utas can be NULL, in which case it
3544  * is taken to be an empty region.
3545  */
3546 static ArtUta *
uta_union_clip(ArtUta * uta1,ArtUta * uta2,ArtIRect * clip)3547 uta_union_clip (ArtUta *uta1, ArtUta *uta2, ArtIRect *clip)
3548 {
3549 	ArtUta *uta;
3550 	ArtUtaBbox *utiles;
3551 	int clip_x1, clip_y1, clip_x2, clip_y2;
3552 	int union_x1, union_y1, union_x2, union_y2;
3553 	int new_x1, new_y1, new_x2, new_y2;
3554 	int x, y;
3555 	int ofs, ofs1, ofs2;
3556 
3557 	g_assert (clip != NULL);
3558 
3559 	/* Compute the tile indices for the clipping rectangle */
3560 
3561 	clip_x1 = clip->x0 >> ART_UTILE_SHIFT;
3562 	clip_y1 = clip->y0 >> ART_UTILE_SHIFT;
3563 	clip_x2 = (clip->x1 >> ART_UTILE_SHIFT) + 1;
3564 	clip_y2 = (clip->y1 >> ART_UTILE_SHIFT) + 1;
3565 
3566 	/* Get the union of the bounds of both utas */
3567 
3568 	if (!uta1) {
3569 		if (!uta2)
3570 			return art_uta_new (clip_x1, clip_y1, clip_x1 + 1, clip_y1 + 1);
3571 
3572 		union_x1 = uta2->x0;
3573 		union_y1 = uta2->y0;
3574 		union_x2 = uta2->x0 + uta2->width;
3575 		union_y2 = uta2->y0 + uta2->height;
3576 	} else {
3577 		if (!uta2) {
3578 			union_x1 = uta1->x0;
3579 			union_y1 = uta1->y0;
3580 			union_x2 = uta1->x0 + uta1->width;
3581 			union_y2 = uta1->y0 + uta1->height;
3582 		} else {
3583 			union_x1 = MIN (uta1->x0, uta2->x0);
3584 			union_y1 = MIN (uta1->y0, uta2->y0);
3585 			union_x2 = MAX (uta1->x0 + uta1->width, uta2->x0 + uta2->width);
3586 			union_y2 = MAX (uta1->y0 + uta1->height, uta2->y0 + uta2->height);
3587 		}
3588 	}
3589 
3590 	/* Clip the union of the bounds */
3591 
3592 	new_x1 = MAX (clip_x1, union_x1);
3593 	new_y1 = MAX (clip_y1, union_y1);
3594 	new_x2 = MIN (clip_x2, union_x2);
3595 	new_y2 = MIN (clip_y2, union_y2);
3596 
3597 	if (new_x1 >= new_x2 || new_y1 >= new_y2)
3598 		return art_uta_new (clip_x1, clip_y1, clip_x1 + 1, clip_y1 + 1);
3599 
3600 	/* Make the new clipped union */
3601 
3602 	uta = art_new (ArtUta, 1);
3603 	uta->x0 = new_x1;
3604 	uta->y0 = new_y1;
3605 	uta->width = new_x2 - new_x1;
3606 	uta->height = new_y2 - new_y1;
3607 	uta->utiles = utiles = art_new (ArtUtaBbox, uta->width * uta->height);
3608 
3609 	ofs = 0;
3610 	ofs1 = ofs2 = 0;
3611 
3612 	for (y = new_y1; y < new_y2; y++) {
3613 		if (uta1)
3614 			ofs1 = (y - uta1->y0) * uta1->width + new_x1 - uta1->x0;
3615 
3616 		if (uta2)
3617 			ofs2 = (y - uta2->y0) * uta2->width + new_x1 - uta2->x0;
3618 
3619 		for (x = new_x1; x < new_x2; x++) {
3620 			ArtUtaBbox bb1, bb2, bb;
3621 
3622 			if (!uta1
3623 			    || x < uta1->x0 || y < uta1->y0
3624 			    || x >= uta1->x0 + uta1->width || y >= uta1->y0 + uta1->height)
3625 				bb1 = 0;
3626 			else
3627 				bb1 = uta1->utiles[ofs1];
3628 
3629 			if (!uta2
3630 			    || x < uta2->x0 || y < uta2->y0
3631 			    || x >= uta2->x0 + uta2->width || y >= uta2->y0 + uta2->height)
3632 				bb2 = 0;
3633 			else
3634 				bb2 = uta2->utiles[ofs2];
3635 
3636 			if (bb1 == 0)
3637 				bb = bb2;
3638 			else if (bb2 == 0)
3639 				bb = bb1;
3640 			else
3641 				bb = ART_UTA_BBOX_CONS (MIN (ART_UTA_BBOX_X0 (bb1),
3642 							     ART_UTA_BBOX_X0 (bb2)),
3643 							MIN (ART_UTA_BBOX_Y0 (bb1),
3644 							     ART_UTA_BBOX_Y0 (bb2)),
3645 							MAX (ART_UTA_BBOX_X1 (bb1),
3646 							     ART_UTA_BBOX_X1 (bb2)),
3647 							MAX (ART_UTA_BBOX_Y1 (bb1),
3648 							     ART_UTA_BBOX_Y1 (bb2)));
3649 
3650 			utiles[ofs] = bb;
3651 
3652 			ofs++;
3653 			ofs1++;
3654 			ofs2++;
3655 		}
3656 	}
3657 
3658 	return uta;
3659 }
3660 
3661 static inline void
get_visible_region(GnomeCanvas * canvas,ArtIRect * visible)3662 get_visible_region (GnomeCanvas *canvas, ArtIRect *visible)
3663 {
3664 	visible->x0 = canvas->layout.hadjustment->value - canvas->zoom_xofs;
3665 	visible->y0 = canvas->layout.vadjustment->value - canvas->zoom_yofs;
3666 	visible->x1 = visible->x0 + GTK_WIDGET (canvas)->allocation.width;
3667 	visible->y1 = visible->y0 + GTK_WIDGET (canvas)->allocation.height;
3668 }
3669 
3670 /**
3671  * gnome_canvas_request_redraw_uta:
3672  * @canvas: A canvas.
3673  * @uta: Microtile array that specifies the area to be redrawn.  It will
3674  * be freed by this function, so the argument you pass will be invalid
3675  * after you call this function.
3676  *
3677  * Informs a canvas that the specified area, given as a microtile array, needs
3678  * to be repainted.  To be used only by item implementations.
3679  **/
3680 void
gnome_canvas_request_redraw_uta(GnomeCanvas * canvas,ArtUta * uta)3681 gnome_canvas_request_redraw_uta (GnomeCanvas *canvas,
3682                                  ArtUta      *uta)
3683 {
3684 	ArtIRect visible;
3685 
3686 	g_return_if_fail (GNOME_IS_CANVAS (canvas));
3687 	g_return_if_fail (uta != NULL);
3688 
3689 	if (!GTK_WIDGET_DRAWABLE (canvas)) {
3690 		art_uta_free (uta);
3691 		return;
3692 	}
3693 
3694 	get_visible_region (canvas, &visible);
3695 
3696 	if (canvas->need_redraw) {
3697 		ArtUta *new_uta;
3698 
3699 		g_assert (canvas->redraw_area != NULL);
3700 		/* ALEX: This can fail if e.g. redraw_uta is called by an item
3701 		   update function and we're called from update_now -> do_update
3702 		   because update_now sets idle_id == 0. There is also some way
3703 		   to get it from the expose handler (see bug #102811).
3704 		   g_assert (canvas->idle_id != 0);  */
3705 
3706 		new_uta = uta_union_clip (canvas->redraw_area, uta, &visible);
3707 		art_uta_free (canvas->redraw_area);
3708 		art_uta_free (uta);
3709 		canvas->redraw_area = new_uta;
3710 		if (canvas->idle_id == 0)
3711 			add_idle (canvas);
3712 	} else {
3713 		ArtUta *new_uta;
3714 
3715 		g_assert (canvas->redraw_area == NULL);
3716 
3717 		new_uta = uta_union_clip (uta, NULL, &visible);
3718 		art_uta_free (uta);
3719 		canvas->redraw_area = new_uta;
3720 
3721 		canvas->need_redraw = TRUE;
3722 		add_idle (canvas);
3723 	}
3724 }
3725 
3726 
3727 /**
3728  * gnome_canvas_request_redraw:
3729  * @canvas: A canvas.
3730  * @x1: Leftmost coordinate of the rectangle to be redrawn.
3731  * @y1: Upper coordinate of the rectangle to be redrawn.
3732  * @x2: Rightmost coordinate of the rectangle to be redrawn, plus 1.
3733  * @y2: Lower coordinate of the rectangle to be redrawn, plus 1.
3734  *
3735  * Convenience function that informs a canvas that the specified rectangle needs
3736  * to be repainted.  This function converts the rectangle to a microtile array
3737  * and feeds it to gnome_canvas_request_redraw_uta().  The rectangle includes
3738  * @x1 and @y1, but not @x2 and @y2.  To be used only by item implementations.
3739  **/
3740 void
gnome_canvas_request_redraw(GnomeCanvas * canvas,int x1,int y1,int x2,int y2)3741 gnome_canvas_request_redraw (GnomeCanvas *canvas, int x1, int y1, int x2, int y2)
3742 {
3743 	ArtUta *uta;
3744 	ArtIRect bbox;
3745 	ArtIRect visible;
3746 	ArtIRect clip;
3747 
3748 	g_return_if_fail (GNOME_IS_CANVAS (canvas));
3749 
3750 	if (!GTK_WIDGET_DRAWABLE (canvas) || (x1 >= x2) || (y1 >= y2))
3751 		return;
3752 
3753 	bbox.x0 = x1;
3754 	bbox.y0 = y1;
3755 	bbox.x1 = x2;
3756 	bbox.y1 = y2;
3757 
3758 	get_visible_region (canvas, &visible);
3759 
3760 	art_irect_intersect (&clip, &bbox, &visible);
3761 
3762 	if (!art_irect_empty (&clip)) {
3763 		uta = art_uta_from_irect (&clip);
3764 		gnome_canvas_request_redraw_uta (canvas, uta);
3765 	}
3766 }
3767 
3768 
3769 /**
3770  * gnome_canvas_w2c_affine:
3771  * @canvas: A canvas.
3772  * @affine: An affine transformation matrix (return value).
3773  *
3774  * Gets the affine transform that converts from world coordinates to canvas
3775  * pixel coordinates.
3776  **/
3777 void
gnome_canvas_w2c_affine(GnomeCanvas * canvas,double affine[6])3778 gnome_canvas_w2c_affine (GnomeCanvas *canvas, double affine[6])
3779 {
3780 	double zooom;
3781 
3782 	g_return_if_fail (GNOME_IS_CANVAS (canvas));
3783 	g_return_if_fail (affine != NULL);
3784 
3785 	zooom = canvas->pixels_per_unit;
3786 
3787 	affine[0] = zooom;
3788 	affine[1] = 0;
3789 	affine[2] = 0;
3790 	affine[3] = zooom;
3791 	affine[4] = -canvas->scroll_x1 * zooom;
3792 	affine[5] = -canvas->scroll_y1 * zooom;
3793 }
3794 
3795 /**
3796  * gnome_canvas_w2c:
3797  * @canvas: A canvas.
3798  * @wx: World X coordinate.
3799  * @wy: World Y coordinate.
3800  * @cx: X pixel coordinate (return value).
3801  * @cy: Y pixel coordinate (return value).
3802  *
3803  * Converts world coordinates into canvas pixel coordinates.
3804  **/
3805 void
gnome_canvas_w2c(GnomeCanvas * canvas,double wx,double wy,int * cx,int * cy)3806 gnome_canvas_w2c (GnomeCanvas *canvas, double wx, double wy, int *cx, int *cy)
3807 {
3808 	double affine[6];
3809 	ArtPoint w, c;
3810 
3811 	g_return_if_fail (GNOME_IS_CANVAS (canvas));
3812 
3813 	gnome_canvas_w2c_affine (canvas, affine);
3814 	w.x = wx;
3815 	w.y = wy;
3816 	art_affine_point (&c, &w, affine);
3817 	if (cx)
3818 		*cx = floor (c.x + 0.5);
3819 	if (cy)
3820 		*cy = floor (c.y + 0.5);
3821 }
3822 
3823 /**
3824  * gnome_canvas_w2c_d:
3825  * @canvas: A canvas.
3826  * @wx: World X coordinate.
3827  * @wy: World Y coordinate.
3828  * @cx: X pixel coordinate (return value).
3829  * @cy: Y pixel coordinate (return value).
3830  *
3831  * Converts world coordinates into canvas pixel coordinates.  This
3832  * version returns coordinates in floating point coordinates, for
3833  * greater precision.
3834  **/
3835 void
gnome_canvas_w2c_d(GnomeCanvas * canvas,double wx,double wy,double * cx,double * cy)3836 gnome_canvas_w2c_d (GnomeCanvas *canvas, double wx, double wy, double *cx, double *cy)
3837 {
3838 	double affine[6];
3839 	ArtPoint w, c;
3840 
3841 	g_return_if_fail (GNOME_IS_CANVAS (canvas));
3842 
3843 	gnome_canvas_w2c_affine (canvas, affine);
3844 	w.x = wx;
3845 	w.y = wy;
3846 	art_affine_point (&c, &w, affine);
3847 	if (cx)
3848 		*cx = c.x;
3849 	if (cy)
3850 		*cy = c.y;
3851 }
3852 
3853 
3854 /**
3855  * gnome_canvas_c2w:
3856  * @canvas: A canvas.
3857  * @cx: Canvas pixel X coordinate.
3858  * @cy: Canvas pixel Y coordinate.
3859  * @wx: X world coordinate (return value).
3860  * @wy: Y world coordinate (return value).
3861  *
3862  * Converts canvas pixel coordinates to world coordinates.
3863  **/
3864 void
gnome_canvas_c2w(GnomeCanvas * canvas,int cx,int cy,double * wx,double * wy)3865 gnome_canvas_c2w (GnomeCanvas *canvas, int cx, int cy, double *wx, double *wy)
3866 {
3867 	double affine[6], inv[6];
3868 	ArtPoint w, c;
3869 
3870 	g_return_if_fail (GNOME_IS_CANVAS (canvas));
3871 
3872 	gnome_canvas_w2c_affine (canvas, affine);
3873 	art_affine_invert (inv, affine);
3874 	c.x = cx;
3875 	c.y = cy;
3876 	art_affine_point (&w, &c, inv);
3877 	if (wx)
3878 		*wx = w.x;
3879 	if (wy)
3880 		*wy = w.y;
3881 }
3882 
3883 
3884 /**
3885  * gnome_canvas_window_to_world:
3886  * @canvas: A canvas.
3887  * @winx: Window-relative X coordinate.
3888  * @winy: Window-relative Y coordinate.
3889  * @worldx: X world coordinate (return value).
3890  * @worldy: Y world coordinate (return value).
3891  *
3892  * Converts window-relative coordinates into world coordinates.  You can use
3893  * this when you need to convert mouse coordinates into world coordinates, for
3894  * example.
3895  **/
3896 void
gnome_canvas_window_to_world(GnomeCanvas * canvas,double winx,double winy,double * worldx,double * worldy)3897 gnome_canvas_window_to_world (GnomeCanvas *canvas, double winx, double winy,
3898 			      double *worldx, double *worldy)
3899 {
3900 	g_return_if_fail (GNOME_IS_CANVAS (canvas));
3901 
3902 	if (worldx)
3903 		*worldx = canvas->scroll_x1 + ((winx - canvas->zoom_xofs)
3904 					       / canvas->pixels_per_unit);
3905 
3906 	if (worldy)
3907 		*worldy = canvas->scroll_y1 + ((winy - canvas->zoom_yofs)
3908 					       / canvas->pixels_per_unit);
3909 }
3910 
3911 
3912 /**
3913  * gnome_canvas_world_to_window:
3914  * @canvas: A canvas.
3915  * @worldx: World X coordinate.
3916  * @worldy: World Y coordinate.
3917  * @winx: X window-relative coordinate.
3918  * @winy: Y window-relative coordinate.
3919  *
3920  * Converts world coordinates into window-relative coordinates.
3921  **/
3922 void
gnome_canvas_world_to_window(GnomeCanvas * canvas,double worldx,double worldy,double * winx,double * winy)3923 gnome_canvas_world_to_window (GnomeCanvas *canvas, double worldx, double worldy,
3924 			      double *winx, double *winy)
3925 {
3926 	g_return_if_fail (GNOME_IS_CANVAS (canvas));
3927 
3928 	if (winx)
3929 		*winx = (canvas->pixels_per_unit)*(worldx - canvas->scroll_x1) + canvas->zoom_xofs;
3930 
3931 	if (winy)
3932 		*winy = (canvas->pixels_per_unit)*(worldy - canvas->scroll_y1) + canvas->zoom_yofs;
3933 }
3934 
3935 
3936 
3937 /**
3938  * gnome_canvas_get_color:
3939  * @canvas: A canvas.
3940  * @spec: X color specification, or NULL for "transparent".
3941  * @color: Returns the allocated color.
3942  *
3943  * Allocates a color based on the specified X color specification.  As a
3944  * convenience to item implementations, it returns TRUE if the color was
3945  * allocated, or FALSE if the specification was NULL.  A NULL color
3946  * specification is considered as "transparent" by the canvas.
3947  *
3948  * Return value: TRUE if @spec is non-NULL and the color is allocated.  If @spec
3949  * is NULL, then returns FALSE.
3950  **/
3951 int
gnome_canvas_get_color(GnomeCanvas * canvas,const char * spec,GdkColor * color)3952 gnome_canvas_get_color (GnomeCanvas *canvas, const char *spec, GdkColor *color)
3953 {
3954 	GdkColormap *colormap;
3955 
3956 	g_return_val_if_fail (GNOME_IS_CANVAS (canvas), FALSE);
3957 	g_return_val_if_fail (color != NULL, FALSE);
3958 
3959 	if (!spec) {
3960 		color->pixel = 0;
3961 		color->red = 0;
3962 		color->green = 0;
3963 		color->blue = 0;
3964 		return FALSE;
3965 	}
3966 
3967 	gdk_color_parse (spec, color);
3968 
3969 	colormap = gtk_widget_get_colormap (GTK_WIDGET (canvas));
3970 
3971 	gdk_rgb_find_color (colormap, color);
3972 
3973 	return TRUE;
3974 }
3975 
3976 /**
3977  * gnome_canvas_get_color_pixel:
3978  * @canvas: A canvas.
3979  * @rgba: RGBA color specification.
3980  *
3981  * Allocates a color from the RGBA value passed into this function.  The alpha
3982  * opacity value is discarded, since normal X colors do not support it.
3983  *
3984  * Return value: Allocated pixel value corresponding to the specified color.
3985  **/
3986 gulong
gnome_canvas_get_color_pixel(GnomeCanvas * canvas,guint rgba)3987 gnome_canvas_get_color_pixel (GnomeCanvas *canvas, guint rgba)
3988 {
3989 	GdkColormap *colormap;
3990 	GdkColor color;
3991 
3992 	g_return_val_if_fail (GNOME_IS_CANVAS (canvas), 0);
3993 
3994 	color.red = ((rgba & 0xff000000) >> 16) + ((rgba & 0xff000000) >> 24);
3995 	color.green = ((rgba & 0x00ff0000) >> 8) + ((rgba & 0x00ff0000) >> 16);
3996 	color.blue = (rgba & 0x0000ff00) + ((rgba & 0x0000ff00) >> 8);
3997 	color.pixel = 0;
3998 
3999 	colormap = gtk_widget_get_colormap (GTK_WIDGET (canvas));
4000 
4001 	gdk_rgb_find_color (colormap, &color);
4002 
4003 	return color.pixel;
4004 }
4005 
4006 
4007 /**
4008  * gnome_canvas_set_stipple_origin:
4009  * @canvas: A canvas.
4010  * @gc: GC on which to set the stipple origin.
4011  *
4012  * Sets the stipple origin of the specified GC as is appropriate for the canvas,
4013  * so that it will be aligned with other stipple patterns used by canvas items.
4014  * This is typically only needed by item implementations.
4015  **/
4016 void
gnome_canvas_set_stipple_origin(GnomeCanvas * canvas,GdkGC * gc)4017 gnome_canvas_set_stipple_origin (GnomeCanvas *canvas, GdkGC *gc)
4018 {
4019 	g_return_if_fail (GNOME_IS_CANVAS (canvas));
4020 	g_return_if_fail (GDK_IS_GC (gc));
4021 
4022 	gdk_gc_set_ts_origin (gc, -canvas->draw_xofs, -canvas->draw_yofs);
4023 }
4024 
4025 /**
4026  * gnome_canvas_set_dither:
4027  * @canvas: A canvas.
4028  * @dither: Type of dithering used to render an antialiased canvas.
4029  *
4030  * Controls dithered rendering for antialiased canvases. The value of
4031  * dither should be #GDK_RGB_DITHER_NONE, #GDK_RGB_DITHER_NORMAL, or
4032  * #GDK_RGB_DITHER_MAX. The default canvas setting is
4033  * #GDK_RGB_DITHER_NORMAL.
4034  **/
4035 void
gnome_canvas_set_dither(GnomeCanvas * canvas,GdkRgbDither dither)4036 gnome_canvas_set_dither (GnomeCanvas *canvas, GdkRgbDither dither)
4037 {
4038 	g_return_if_fail (GNOME_IS_CANVAS (canvas));
4039 
4040 	canvas->dither = dither;
4041 }
4042 
4043 /**
4044  * gnome_canvas_get_dither:
4045  * @canvas: A canvas.
4046  *
4047  * Returns the type of dithering used to render an antialiased canvas.
4048  *
4049  * Return value: The dither setting.
4050  **/
4051 GdkRgbDither
gnome_canvas_get_dither(GnomeCanvas * canvas)4052 gnome_canvas_get_dither (GnomeCanvas *canvas)
4053 {
4054 	g_return_val_if_fail (GNOME_IS_CANVAS (canvas), GDK_RGB_DITHER_NONE);
4055 
4056 	return canvas->dither;
4057 }
4058 
4059 static gboolean
boolean_handled_accumulator(GSignalInvocationHint * ihint,GValue * return_accu,const GValue * handler_return,gpointer dummy)4060 boolean_handled_accumulator (GSignalInvocationHint *ihint,
4061 			     GValue                *return_accu,
4062 			     const GValue          *handler_return,
4063 			     gpointer               dummy)
4064 {
4065 	gboolean continue_emission;
4066 	gboolean signal_handled;
4067 
4068 	signal_handled = g_value_get_boolean (handler_return);
4069 	g_value_set_boolean (return_accu, signal_handled);
4070 	continue_emission = !signal_handled;
4071 
4072 	return continue_emission;
4073 }
4074 
4075 /* Class initialization function for GnomeCanvasItemClass */
4076 static void
gnome_canvas_item_class_init(GnomeCanvasItemClass * class)4077 gnome_canvas_item_class_init (GnomeCanvasItemClass *class)
4078 {
4079 	GObjectClass *gobject_class;
4080 
4081 	gobject_class = (GObjectClass *) class;
4082 
4083 	item_parent_class = g_type_class_peek_parent (class);
4084 
4085 	gobject_class->set_property = gnome_canvas_item_set_property;
4086 	gobject_class->get_property = gnome_canvas_item_get_property;
4087 
4088 	g_object_class_install_property
4089 		(gobject_class, ITEM_PROP_PARENT,
4090 		 g_param_spec_object ("parent", NULL, NULL,
4091 				      GNOME_TYPE_CANVAS_ITEM,
4092 				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
4093 
4094 	item_signals[ITEM_EVENT] =
4095 		g_signal_new ("event",
4096 			      G_TYPE_FROM_CLASS (class),
4097 			      G_SIGNAL_RUN_LAST,
4098 			      G_STRUCT_OFFSET (GnomeCanvasItemClass, event),
4099 			      boolean_handled_accumulator, NULL,
4100 			      gnome_canvas_marshal_BOOLEAN__BOXED,
4101 			      G_TYPE_BOOLEAN, 1,
4102 			      GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
4103 
4104 	gobject_class->dispose = gnome_canvas_item_dispose;
4105 
4106 	class->realize = gnome_canvas_item_realize;
4107 	class->unrealize = gnome_canvas_item_unrealize;
4108 	class->map = gnome_canvas_item_map;
4109 	class->unmap = gnome_canvas_item_unmap;
4110 	class->update = gnome_canvas_item_update;
4111 }
4112