source: trunk/third/libgnomecanvas/libgnomecanvas/gnome-canvas.c @ 20821

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