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