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