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
30 * widget. Tk is copyrighted by the Regents of the University of California,
31 * Sun Microsystems, and other parties.
32 *
33 *
34 * Authors: Federico Mena <federico@nuclecu.unam.mx>
35 * Raph Levien <raph@gimp.org>
36 */
37
38 /*
39 * TO-DO list for the canvas:
40 *
41 * - Allow to specify whether GnomeCanvasImage sizes are in units or pixels
42 * (scale or don't scale).
43 *
44 * - Implement a flag for gnome_canvas_item_reparent() that tells the function
45 * to keep the item visually in the same place, that is, to keep it in the
46 * same place with respect to the canvas origin.
47 *
48 * - GC put functions for items.
49 *
50 * - Widget item (finish it).
51 *
52 * - GList *
53 * gnome_canvas_gimme_all_items_contained_in_this_area (GnomeCanvas *canvas,
54 * Rectangle area);
55 *
56 * - Retrofit all the primitive items with microtile support.
57 *
58 * - Curve support for line item.
59 *
60 * - Arc item (Havoc has it; to be integrated in GnomeCanvasEllipse).
61 *
62 * - Sane font handling API.
63 *
64 * - Get_arg methods for items:
65 * - How to fetch the outline width and know whether it is in pixels or units?
66 */
67
68 /*
69 * Raph's TODO list for the antialiased canvas integration:
70 *
71 * - ::point() method for text item not accurate when affine transformed.
72 *
73 * - Clip rectangle not implemented in aa renderer for text item.
74 *
75 * - Clip paths only partially implemented.
76 *
77 * - Add more image loading techniques to work around imlib deficiencies.
78 */
79
80 #ifdef HAVE_CONFIG_H
81 #include <config.h>
82 #endif
83
84 #include <math.h>
85 #include <string.h>
86 #include <stdio.h>
87 #include <gdk/gdkprivate.h>
88 #include <gtk/gtk.h>
89 #include <cairo-gobject.h>
90 #include "gailcanvas.h"
91 #include "gnome-canvas.h"
92 #include "gnome-canvas-i18n.h"
93 #include "gnome-canvas-util.h"
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 static void gnome_canvas_request_update (GnomeCanvas *canvas);
99 static void group_add (GnomeCanvasGroup *group,
100 GnomeCanvasItem *item);
101 static void group_remove (GnomeCanvasGroup *group,
102 GnomeCanvasItem *item);
103 static void add_idle (GnomeCanvas *canvas);
104
105 /*** GnomeCanvasItem ***/
106
107 /* Some convenience stuff */
108 #define GCI_UPDATE_MASK \
109 (GNOME_CANVAS_UPDATE_REQUESTED | \
110 GNOME_CANVAS_UPDATE_AFFINE | \
111 GNOME_CANVAS_UPDATE_CLIP | \
112 GNOME_CANVAS_UPDATE_VISIBILITY)
113 #define GCI_EPSILON 1e-18
114 #define GCI_PRINT_MATRIX(s,a) \
115 g_print ("%s %g %g %g %g %g %g\n", \
116 s, (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5])
117
118 enum {
119 ITEM_PROP_0,
120 ITEM_PROP_PARENT
121 };
122
123 enum {
124 ITEM_EVENT,
125 ITEM_LAST_SIGNAL
126 };
127
128 static gint emit_event (GnomeCanvas *canvas, GdkEvent *event);
129
130 static guint item_signals[ITEM_LAST_SIGNAL];
131
132 G_DEFINE_TYPE (
133 GnomeCanvasItem,
134 gnome_canvas_item,
135 G_TYPE_INITIALLY_UNOWNED)
136
137 /* Object initialization function for GnomeCanvasItem */
138 static void
139 gnome_canvas_item_init (GnomeCanvasItem *item)
140 {
141 item->flags |= GNOME_CANVAS_ITEM_VISIBLE;
142
143 cairo_matrix_init_identity (&item->matrix);
144 }
145
146 /**
147 * gnome_canvas_item_new:
148 * @parent: The parent group for the new item.
149 * @type: The object type of the item.
150 * @first_arg_name: A list of object argument name/value pairs, NULL-terminated,
151 * used to configure the item. For example, "fill_color", "black",
152 * "width_units", 5.0, NULL.
153 * @Varargs:
154 *
155 * Creates a new canvas item with @parent as its parent group. The item is
156 * created at the top of its parent's stack, and starts up as visible. The item
157 * is of the specified @type, for example, it can be
158 * gnome_canvas_rect_get_type(). The list of object arguments/value pairs is
159 * used to configure the item. If you need to pass construct time parameters, you
160 * should use g_object_new() to pass the parameters and
161 * gnome_canvas_item_construct() to set up the canvas item.
162 *
163 * Return value: The newly-created item.
164 **/
165 GnomeCanvasItem *
166 gnome_canvas_item_new (GnomeCanvasGroup *parent,
167 GType type,
168 const gchar *first_arg_name, ...)
169 {
170 GnomeCanvasItem *item;
171 va_list args;
172
173 g_return_val_if_fail (GNOME_IS_CANVAS_GROUP (parent), NULL);
174 g_return_val_if_fail (g_type_is_a (type, gnome_canvas_item_get_type ()), NULL);
175
176 item = GNOME_CANVAS_ITEM (g_object_new (type, NULL));
177
178 va_start (args, first_arg_name);
179 gnome_canvas_item_construct (item, parent, first_arg_name, args);
180 va_end (args);
181
182 return item;
183 }
184
185 /* Performs post-creation operations on a canvas item (adding it to its parent
186 * group, etc.)
187 */
188 static void
189 item_post_create_setup (GnomeCanvasItem *item)
190 {
191 group_add (GNOME_CANVAS_GROUP (item->parent), item);
192
193 gnome_canvas_request_redraw (
194 item->canvas, item->x1, item->y1, item->x2 + 1, item->y2 + 1);
195 item->canvas->need_repick = TRUE;
196 }
197
198 /* Set_property handler for canvas items */
199 static void
200 gnome_canvas_item_set_property (GObject *gobject,
201 guint property_id,
202 const GValue *value,
203 GParamSpec *pspec)
204 {
205 GnomeCanvasItem *item;
206
207 g_return_if_fail (GNOME_IS_CANVAS_ITEM (gobject));
208
209 item = GNOME_CANVAS_ITEM (gobject);
210
211 switch (property_id) {
212 case ITEM_PROP_PARENT:
213 if (item->parent != NULL) {
214 g_warning ("Cannot set `parent' argument after item has "
215 "already been constructed.");
216 } else if (g_value_get_object (value)) {
217 item->parent = GNOME_CANVAS_ITEM (g_value_get_object (value));
218 item->canvas = item->parent->canvas;
219 item_post_create_setup (item);
220 }
221 break;
222 default:
223 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
224 break;
225 }
226 }
227
228 /* Get_property handler for canvas items */
229 static void
230 gnome_canvas_item_get_property (GObject *gobject,
231 guint property_id,
232 GValue *value,
233 GParamSpec *pspec)
234 {
235 GnomeCanvasItem *item;
236
237 g_return_if_fail (GNOME_IS_CANVAS_ITEM (gobject));
238
239 item = GNOME_CANVAS_ITEM (gobject);
240
241 switch (property_id) {
242 case ITEM_PROP_PARENT:
243 g_value_set_object (value, item->parent);
244 break;
245
246 default:
247 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
248 break;
249 }
250 }
251
252 /**
253 * gnome_canvas_item_construct:
254 * @item: An unconstructed canvas item.
255 * @parent: The parent group for the item.
256 * @first_arg_name: The name of the first argument for configuring the item.
257 * @args: The list of arguments used to configure the item.
258 *
259 * Constructs a canvas item; meant for use only by item implementations.
260 **/
261 void
262 gnome_canvas_item_construct (GnomeCanvasItem *item,
263 GnomeCanvasGroup *parent,
264 const gchar *first_arg_name,
265 va_list args)
266 {
267 g_return_if_fail (GNOME_IS_CANVAS_GROUP (parent));
268 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
269
270 item->parent = GNOME_CANVAS_ITEM (parent);
271 item->canvas = item->parent->canvas;
272
273 g_object_set_valist (G_OBJECT (item), first_arg_name, args);
274
275 item_post_create_setup (item);
276 }
277
278 /* If the item is visible, requests a redraw of it. */
279 static void
280 redraw_if_visible (GnomeCanvasItem *item)
281 {
282 if (item->flags & GNOME_CANVAS_ITEM_VISIBLE)
283 gnome_canvas_request_redraw (
284 item->canvas, item->x1, item->y1,
285 item->x2 + 1, item->y2 + 1);
286 }
287
288 /* Standard object dispose function for canvas items */
289 static void
290 gnome_canvas_item_dispose (GObject *object)
291 {
292 GnomeCanvasItem *item;
293
294 g_return_if_fail (GNOME_IS_CANVAS_ITEM (object));
295
296 item = GNOME_CANVAS_ITEM (object);
297
298 if (item->canvas)
299 redraw_if_visible (item);
300
301 /* Make the canvas forget about us */
302
303 if (item->canvas && item == item->canvas->current_item) {
304 item->canvas->current_item = NULL;
305 item->canvas->need_repick = TRUE;
306 }
307
308 if (item->canvas && item == item->canvas->new_current_item) {
309 item->canvas->new_current_item = NULL;
310 item->canvas->need_repick = TRUE;
311 }
312
313 if (item->canvas && item == item->canvas->grabbed_item) {
314 item->canvas->grabbed_item = NULL;
315 gdk_pointer_ungrab (GDK_CURRENT_TIME);
316 }
317
318 if (item->canvas && item == item->canvas->focused_item)
319 item->canvas->focused_item = NULL;
320
321 /* Normal dispose stuff */
322
323 if (item->flags & GNOME_CANVAS_ITEM_MAPPED)
324 (* GNOME_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
325
326 if (item->flags & GNOME_CANVAS_ITEM_REALIZED)
327 (* GNOME_CANVAS_ITEM_GET_CLASS (item)->unrealize) (item);
328
329 if (item->parent)
330 group_remove (GNOME_CANVAS_GROUP (item->parent), item);
331
332 if (GNOME_CANVAS_ITEM_GET_CLASS (item)->dispose)
333 GNOME_CANVAS_ITEM_GET_CLASS (item)->dispose (item);
334
335 G_OBJECT_CLASS (gnome_canvas_item_parent_class)->dispose (object);
336 /* items should remove any reference to item->canvas after the
337 * first ::dispose */
338 item->canvas = NULL;
339 }
340
341 /* Update handler for canvas items */
342 static void
343 gnome_canvas_item_update (GnomeCanvasItem *item,
344 const cairo_matrix_t *matrix,
345 gint flags)
346 {
347 item->flags &= ~GNOME_CANVAS_ITEM_NEED_UPDATE;
348 item->flags &= ~GNOME_CANVAS_ITEM_NEED_AFFINE;
349 item->flags &= ~GNOME_CANVAS_ITEM_NEED_CLIP;
350 item->flags &= ~GNOME_CANVAS_ITEM_NEED_VIS;
351 }
352
353 /* Realize handler for canvas items */
354 static void
355 gnome_canvas_item_realize (GnomeCanvasItem *item)
356 {
357 item->flags |= GNOME_CANVAS_ITEM_REALIZED;
358
359 gnome_canvas_item_request_update (item);
360 }
361
362 /* Unrealize handler for canvas items */
363 static void
364 gnome_canvas_item_unrealize (GnomeCanvasItem *item)
365 {
366 item->flags &= ~GNOME_CANVAS_ITEM_REALIZED;
367 }
368
369 /* Map handler for canvas items */
370 static void
371 gnome_canvas_item_map (GnomeCanvasItem *item)
372 {
373 item->flags |= GNOME_CANVAS_ITEM_MAPPED;
374 }
375
376 /* Unmap handler for canvas items */
377 static void
378 gnome_canvas_item_unmap (GnomeCanvasItem *item)
379 {
380 item->flags &= ~GNOME_CANVAS_ITEM_MAPPED;
381 }
382
383 /* Dispose handler for canvas items */
384 static void
385 gnome_canvas_item_dispose_item (GnomeCanvasItem *item)
386 {
387 /* Placeholder so subclasses can safely chain up. */
388 }
389
390 /*
391 * This routine invokes the update method of the item
392 * Please notice, that we take parent to canvas pixel matrix as argument
393 * unlike virtual method ::update, whose argument is item 2 canvas pixel
394 * matrix
395 *
396 * I will try to force somewhat meaningful naming for affines (Lauris)
397 * General naming rule is FROM2TO, where FROM and TO are abbreviations
398 * So p2cpx is Parent2CanvasPixel and i2cpx is Item2CanvasPixel
399 * I hope that this helps to keep track of what really happens
400 *
401 */
402
403 static void
404 gnome_canvas_item_invoke_update (GnomeCanvasItem *item,
405 const cairo_matrix_t *p2c,
406 gint flags)
407 {
408 gint child_flags;
409 cairo_matrix_t i2c;
410
411 child_flags = flags;
412 if (!(item->flags & GNOME_CANVAS_ITEM_VISIBLE))
413 child_flags &= ~GNOME_CANVAS_UPDATE_IS_VISIBLE;
414
415 /* Calculate actual item transformation matrix */
416
417 cairo_matrix_multiply (&i2c, &item->matrix, p2c);
418
419 /* apply object flags to child flags */
420
421 child_flags &= ~GNOME_CANVAS_UPDATE_REQUESTED;
422
423 if (item->flags & GNOME_CANVAS_ITEM_NEED_UPDATE)
424 child_flags |= GNOME_CANVAS_UPDATE_REQUESTED;
425
426 if (item->flags & GNOME_CANVAS_ITEM_NEED_AFFINE)
427 child_flags |= GNOME_CANVAS_UPDATE_AFFINE;
428
429 if (item->flags & GNOME_CANVAS_ITEM_NEED_CLIP)
430 child_flags |= GNOME_CANVAS_UPDATE_CLIP;
431
432 if (item->flags & GNOME_CANVAS_ITEM_NEED_VIS)
433 child_flags |= GNOME_CANVAS_UPDATE_VISIBILITY;
434
435 if (child_flags & GCI_UPDATE_MASK) {
436 if (GNOME_CANVAS_ITEM_GET_CLASS (item)->update)
437 GNOME_CANVAS_ITEM_GET_CLASS (item)->update (item, &i2c, child_flags);
438 }
439 }
440
441 /*
442 * This routine invokes the point method of the item.
443 * The arguments x, y should be in the parent item local coordinates.
444 *
445 * This is potentially evil, as we are relying on matrix inversion (Lauris)
446 */
447
448 static GnomeCanvasItem *
449 gnome_canvas_item_invoke_point (GnomeCanvasItem *item,
450 gdouble x,
451 gdouble y,
452 gint cx,
453 gint cy)
454 {
455 cairo_matrix_t inverse;
456
457 /* Calculate x & y in item local coordinates */
458 inverse = item->matrix;
459 if (cairo_matrix_invert (&inverse) != CAIRO_STATUS_SUCCESS)
460 return NULL;
461
462 cairo_matrix_transform_point (&inverse, &x, &y);
463
464 if (GNOME_CANVAS_ITEM_GET_CLASS (item)->point)
465 return GNOME_CANVAS_ITEM_GET_CLASS (item)->point (item, x, y, cx, cy);
466
467 return NULL;
468 }
469
470 /**
471 * gnome_canvas_item_set:
472 * @item: A canvas item.
473 * @first_arg_name: The list of object argument name/value pairs used to
474 * configure the item.
475 * @Varargs:
476 *
477 * Configures a canvas item. The arguments in the item are set to the
478 * specified values, and the item is repainted as appropriate.
479 **/
480 void
481 gnome_canvas_item_set (GnomeCanvasItem *item,
482 const gchar *first_arg_name,
483 ...)
484 {
485 va_list args;
486
487 va_start (args, first_arg_name);
488 gnome_canvas_item_set_valist (item, first_arg_name, args);
489 va_end (args);
490 }
491
492 /**
493 * gnome_canvas_item_set_valist:
494 * @item: A canvas item.
495 * @first_arg_name: The name of the first argument used to configure the item.
496 * @args: The list of object argument name/value pairs used to configure the item.
497 *
498 * Configures a canvas item. The arguments in the item are set to the specified
499 * values, and the item is repainted as appropriate.
500 **/
501 void
502 gnome_canvas_item_set_valist (GnomeCanvasItem *item,
503 const gchar *first_arg_name,
504 va_list args)
505 {
506 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
507
508 g_object_set_valist (G_OBJECT (item), first_arg_name, args);
509
510 item->canvas->need_repick = TRUE;
511 }
512
513 /**
514 * gnome_canvas_item_transform:
515 * @item: A canvas item.
516 * @matrix: An affine transformation matrix.
517 *
518 * Combines the specified affine transformation matrix with the item's current
519 * transformation.
520 **/
521 void
522 gnome_canvas_item_transform (GnomeCanvasItem *item,
523 const cairo_matrix_t *matrix)
524 {
525 cairo_matrix_t i2p;
526
527 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
528 g_return_if_fail (matrix != NULL);
529
530 /* Calculate actual item transformation matrix */
531 cairo_matrix_multiply (&i2p, matrix, &item->matrix);
532
533 gnome_canvas_item_set_matrix (item, &i2p);
534 }
535
536 /**
537 * gnome_canvas_item_set_matrix:
538 * @item: A canvas item.
539 * @matrix: An affine transformation matrix or %NULL for the identity matrix.
540 *
541 * Makes the item's affine transformation matrix be equal to the specified
542 * matrix. NULL is treated as identity.
543 **/
544 void
545 gnome_canvas_item_set_matrix (GnomeCanvasItem *item,
546 const cairo_matrix_t *matrix)
547 {
548 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
549
550 if (matrix) {
551 item->matrix = *matrix;
552 } else {
553 cairo_matrix_init_identity (&item->matrix);
554 }
555
556 if (!(item->flags & GNOME_CANVAS_ITEM_NEED_AFFINE)) {
557 /* Request update */
558 item->flags |= GNOME_CANVAS_ITEM_NEED_AFFINE;
559 gnome_canvas_item_request_update (item);
560 }
561
562 item->canvas->need_repick = TRUE;
563 }
564
565 /**
566 * gnome_canvas_item_move:
567 * @item: A canvas item.
568 * @dx: Horizontal offset.
569 * @dy: Vertical offset.
570 *
571 * Moves a canvas item by creating an affine transformation matrix for
572 * translation by using the specified values. This happens in item
573 * local coordinate system, so if you have nontrivial transform, it
574 * most probably does not do, what you want.
575 **/
576 void
577 gnome_canvas_item_move (GnomeCanvasItem *item,
578 gdouble dx,
579 gdouble dy)
580 {
581 cairo_matrix_t translate;
582
583 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
584
585 cairo_matrix_init_translate (&translate, dx, dy);
586
587 gnome_canvas_item_transform (item, &translate);
588 }
589
590 /* Convenience function to reorder items in a group's child list. This puts the
591 * specified link after the "before" link. Returns TRUE if the list was changed.
592 */
593 static gboolean
594 put_item_after (GList *link,
595 GList *before)
596 {
597 GnomeCanvasGroup *parent;
598 GList *old_before, *old_after;
599 GList *after;
600
601 parent = GNOME_CANVAS_GROUP (GNOME_CANVAS_ITEM (link->data)->parent);
602
603 if (before)
604 after = before->next;
605 else
606 after = parent->item_list;
607
608 if (before == link || after == link)
609 return FALSE;
610
611 /* Unlink */
612
613 old_before = link->prev;
614 old_after = link->next;
615
616 if (old_before)
617 old_before->next = old_after;
618 else
619 parent->item_list = old_after;
620
621 if (old_after)
622 old_after->prev = old_before;
623 else
624 parent->item_list_end = old_before;
625
626 /* Relink */
627
628 link->prev = before;
629 if (before)
630 before->next = link;
631 else
632 parent->item_list = link;
633
634 link->next = after;
635 if (after)
636 after->prev = link;
637 else
638 parent->item_list_end = link;
639
640 return TRUE;
641 }
642
643 /**
644 * gnome_canvas_item_raise:
645 * @item: A canvas item.
646 * @positions: Number of steps to raise the item.
647 *
648 * Raises the item in its parent's stack by the specified number of positions.
649 * If the number of positions is greater than the distance to the top of the
650 * stack, then the item is put at the top.
651 **/
652 void
653 gnome_canvas_item_raise (GnomeCanvasItem *item,
654 gint positions)
655 {
656 GList *link, *before;
657 GnomeCanvasGroup *parent;
658
659 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
660 g_return_if_fail (positions >= 0);
661
662 if (!item->parent || positions == 0)
663 return;
664
665 parent = GNOME_CANVAS_GROUP (item->parent);
666 link = g_list_find (parent->item_list, item);
667 g_return_if_fail (link != NULL);
668
669 for (before = link; positions && before; positions--)
670 before = before->next;
671
672 if (!before)
673 before = parent->item_list_end;
674
675 if (put_item_after (link, before)) {
676 redraw_if_visible (item);
677 item->canvas->need_repick = TRUE;
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
678 }
679 }
680
681 /**
682 * gnome_canvas_item_lower:
683 * @item: A canvas item.
684 * @positions: Number of steps to lower the item.
685 *
686 * Lowers the item in its parent's stack by the specified number of positions.
687 * If the number of positions is greater than the distance to the bottom of the
688 * stack, then the item is put at the bottom.
689 **/
690 void
691 gnome_canvas_item_lower (GnomeCanvasItem *item,
692 gint positions)
693 {
694 GList *link, *before;
695 GnomeCanvasGroup *parent;
696
697 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
698 g_return_if_fail (positions >= 1);
699
700 if (!item->parent || positions == 0)
701 return;
702
703 parent = GNOME_CANVAS_GROUP (item->parent);
704 link = g_list_find (parent->item_list, item);
705 g_return_if_fail (link != NULL);
706
707 if (link->prev)
708 for (before = link->prev; positions && before; positions--)
709 before = before->prev;
710 else
711 before = NULL;
712
713 if (put_item_after (link, before)) {
714 redraw_if_visible (item);
715 item->canvas->need_repick = TRUE;
716 }
717 }
718
719 /**
720 * gnome_canvas_item_raise_to_top:
721 * @item: A canvas item.
722 *
723 * Raises an item to the top of its parent's stack.
724 **/
725 void
726 gnome_canvas_item_raise_to_top (GnomeCanvasItem *item)
727 {
728 GList *link;
729 GnomeCanvasGroup *parent;
730
731 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
732
733 if (!item->parent)
734 return;
735
736 parent = GNOME_CANVAS_GROUP (item->parent);
737 link = g_list_find (parent->item_list, item);
738 g_return_if_fail (link != NULL);
739
740 if (put_item_after (link, parent->item_list_end)) {
741 redraw_if_visible (item);
742 item->canvas->need_repick = TRUE;
743 }
744 }
745
746 /**
747 * gnome_canvas_item_lower_to_bottom:
748 * @item: A canvas item.
749 *
750 * Lowers an item to the bottom of its parent's stack.
751 **/
752 void
753 gnome_canvas_item_lower_to_bottom (GnomeCanvasItem *item)
754 {
755 GList *link;
756 GnomeCanvasGroup *parent;
757
758 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
759
760 if (!item->parent)
761 return;
762
763 parent = GNOME_CANVAS_GROUP (item->parent);
764 link = g_list_find (parent->item_list, item);
765 g_return_if_fail (link != NULL);
766
767 if (put_item_after (link, NULL)) {
768 redraw_if_visible (item);
769 item->canvas->need_repick = TRUE;
770 }
771 }
772
773 /**
774 * gnome_canvas_item_show:
775 * @item: A canvas item.
776 *
777 * Shows a canvas item. If the item was already shown, then no action is taken.
778 **/
779 void
780 gnome_canvas_item_show (GnomeCanvasItem *item)
781 {
782 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
783
784 if (!(item->flags & GNOME_CANVAS_ITEM_VISIBLE)) {
785 item->flags |= GNOME_CANVAS_ITEM_VISIBLE;
786 gnome_canvas_request_redraw (
787 item->canvas, item->x1, item->y1,
788 item->x2 + 1, item->y2 + 1);
789 item->canvas->need_repick = TRUE;
790 }
791 }
792
793 /**
794 * gnome_canvas_item_hide:
795 * @item: A canvas item.
796 *
797 * Hides a canvas item. If the item was already hidden, then no action is
798 * taken.
799 **/
800 void
801 gnome_canvas_item_hide (GnomeCanvasItem *item)
802 {
803 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
804
805 if (item->flags & GNOME_CANVAS_ITEM_VISIBLE) {
806 item->flags &= ~GNOME_CANVAS_ITEM_VISIBLE;
807 gnome_canvas_request_redraw (
808 item->canvas, item->x1, item->y1,
809 item->x2 + 1, item->y2 + 1);
810 item->canvas->need_repick = TRUE;
811 }
812 }
813
814 /**
815 * gnome_canvas_item_grab:
816 * @item: A canvas item.
817 * @event_mask: Mask of events that will be sent to this item.
818 * @cursor: If non-NULL, the cursor that will be used while the grab is active.
819 * @etime: The timestamp required for grabbing the mouse, or GDK_CURRENT_TIME.
820 *
821 * Specifies that all events that match the specified event mask should be sent
822 * to the specified item, and also grabs the mouse by calling
823 * gdk_pointer_grab(). The event mask is also used when grabbing the pointer.
824 * If @cursor is not NULL, then that cursor is used while the grab is active.
825 * The @etime parameter is the timestamp required for grabbing the mouse.
826 *
827 * Return value: If an item was already grabbed, it returns
828 * %GDK_GRAB_ALREADY_GRABBED. If the specified item was hidden by calling
829 * gnome_canvas_item_hide(), then it returns %GDK_GRAB_NOT_VIEWABLE. Else,
830 * it returns the result of calling gdk_pointer_grab().
831 **/
832 gint
833 gnome_canvas_item_grab (GnomeCanvasItem *item,
834 guint event_mask,
835 GdkCursor *cursor,
836 guint32 etime)
837 {
838 GtkLayout *layout;
839 GdkWindow *bin_window;
840 gint retval;
841
842 g_return_val_if_fail (
843 GNOME_IS_CANVAS_ITEM (item), GDK_GRAB_NOT_VIEWABLE);
844 g_return_val_if_fail (
845 gtk_widget_get_mapped (GTK_WIDGET (item->canvas)),
846 GDK_GRAB_NOT_VIEWABLE);
847
848 if (item->canvas->grabbed_item)
849 return GDK_GRAB_ALREADY_GRABBED;
850
851 if (!(item->flags & GNOME_CANVAS_ITEM_VISIBLE))
852 return GDK_GRAB_NOT_VIEWABLE;
853
854 layout = GTK_LAYOUT (item->canvas);
855 bin_window = gtk_layout_get_bin_window (layout);
856
857 retval = gdk_pointer_grab (
858 bin_window, FALSE, event_mask,
859 NULL, cursor, etime);
860
861 if (retval != GDK_GRAB_SUCCESS)
862 return retval;
863
864 item->canvas->grabbed_item = item;
865 item->canvas->grabbed_event_mask = event_mask;
866 item->canvas->current_item = item; /* So that events go to the grabbed item */
867
868 return retval;
869 }
870
871 /**
872 * gnome_canvas_item_ungrab:
873 * @item: A canvas item that holds a grab.
874 * @etime: The timestamp for ungrabbing the mouse.
875 *
876 * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
877 * mouse.
878 **/
879 void
880 gnome_canvas_item_ungrab (GnomeCanvasItem *item,
881 guint32 etime)
882 {
883 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
884
885 if (item->canvas->grabbed_item != item)
886 return;
887
888 item->canvas->grabbed_item = NULL;
889
890 gdk_pointer_ungrab (etime);
891 }
892
893 void
894 gnome_canvas_item_i2w_matrix (GnomeCanvasItem *item,
895 cairo_matrix_t *matrix)
896 {
897 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
898 g_return_if_fail (matrix != NULL);
899
900 cairo_matrix_init_identity (matrix);
901
902 while (item) {
903 cairo_matrix_multiply (matrix, matrix, &item->matrix);
904
905 item = item->parent;
906 }
907 }
908
909 void
910 gnome_canvas_item_w2i_matrix (GnomeCanvasItem *item,
911 cairo_matrix_t *matrix)
912 {
913 cairo_status_t status;
914
915 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
916 g_return_if_fail (matrix != NULL);
917
918 gnome_canvas_item_i2w_matrix (item, matrix);
919 status = cairo_matrix_invert (matrix);
920 g_return_if_fail (status == CAIRO_STATUS_SUCCESS);
921 }
922
923 /**
924 * gnome_canvas_item_w2i:
925 * @item: A canvas item.
926 * @x: X coordinate to convert (input/output value).
927 * @y: Y coordinate to convert (input/output value).
928 *
929 * Converts a coordinate pair from world coordinates to item-relative
930 * coordinates.
931 **/
932 void
933 gnome_canvas_item_w2i (GnomeCanvasItem *item,
934 gdouble *x,
935 gdouble *y)
936 {
937 cairo_matrix_t matrix;
938
939 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
940 g_return_if_fail (x != NULL);
941 g_return_if_fail (y != NULL);
942
943 gnome_canvas_item_w2i_matrix (item, &matrix);
944 cairo_matrix_transform_point (&matrix, x, y);
945 }
946
947 /**
948 * gnome_canvas_item_i2w:
949 * @item: A canvas item.
950 * @x: X coordinate to convert (input/output value).
951 * @y: Y coordinate to convert (input/output value).
952 *
953 * Converts a coordinate pair from item-relative coordinates to world
954 * coordinates.
955 **/
956 void
957 gnome_canvas_item_i2w (GnomeCanvasItem *item,
958 gdouble *x,
959 gdouble *y)
960 {
961 cairo_matrix_t matrix;
962
963 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
964 g_return_if_fail (x != NULL);
965 g_return_if_fail (y != NULL);
966
967 gnome_canvas_item_i2w_matrix (item, &matrix);
968 cairo_matrix_transform_point (&matrix, x, y);
969 }
970
971 /**
972 * gnome_canvas_item_i2c_matrix:
973 * @item: A canvas item.
974 * @matrix: Matrix to take the resulting transformation matrix (return value).
975 *
976 * Gets the affine transform that converts from item-relative coordinates to
977 * canvas pixel coordinates.
978 **/
979 void
980 gnome_canvas_item_i2c_matrix (GnomeCanvasItem *item,
981 cairo_matrix_t *matrix)
982 {
983 cairo_matrix_t i2w, w2c;
984
985 gnome_canvas_item_i2w_matrix (item, &i2w);
986 gnome_canvas_w2c_matrix (item->canvas, &w2c);
987 cairo_matrix_multiply (matrix, &i2w, &w2c);
988 }
989
990 /* Returns whether the item is an inferior of or is equal to the parent. */
991 static gint
992 is_descendant (GnomeCanvasItem *item,
993 GnomeCanvasItem *parent)
994 {
995 for (; item; item = item->parent)
996 if (item == parent)
997 return TRUE;
998
999 return FALSE;
1000 }
1001
1002 /**
1003 * gnome_canvas_item_reparent:
1004 * @item: A canvas item.
1005 * @new_group: A canvas group.
1006 *
1007 * Changes the parent of the specified item to be the new group. The item keeps
1008 * its group-relative coordinates as for its old parent, so the item may change
1009 * its absolute position within the canvas.
1010 **/
1011 void
1012 gnome_canvas_item_reparent (GnomeCanvasItem *item,
1013 GnomeCanvasGroup *new_group)
1014 {
1015 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
1016 g_return_if_fail (GNOME_IS_CANVAS_GROUP (new_group));
1017
1018 /* Both items need to be in the same canvas */
1019 g_return_if_fail (item->canvas == GNOME_CANVAS_ITEM (new_group)->canvas);
1020
1021 /* The group cannot be an inferior of the item or be the item itself --
1022 * this also takes care of the case where the item is the root item of
1023 * the canvas. */
1024 g_return_if_fail (!is_descendant (GNOME_CANVAS_ITEM (new_group), item));
1025
1026 /* Everything is ok, now actually reparent the item */
1027
1028 g_object_ref (item); /* protect it from the unref in group_remove */
1029
1030 redraw_if_visible (item);
1031
1032 group_remove (GNOME_CANVAS_GROUP (item->parent), item);
1033 item->parent = GNOME_CANVAS_ITEM (new_group);
1034 group_add (new_group, item);
1035
1036 /* Redraw and repick */
1037
1038 redraw_if_visible (item);
1039 item->canvas->need_repick = TRUE;
1040
1041 g_object_unref (item);
1042 }
1043
1044 /**
1045 * gnome_canvas_item_grab_focus:
1046 * @item: A canvas item.
1047 *
1048 * Makes the specified item take the keyboard focus, so all keyboard events will
1049 * be sent to it. If the canvas widget itself did not have the focus, it grabs
1050 * it as well.
1051 **/
1052 void
1053 gnome_canvas_item_grab_focus (GnomeCanvasItem *item)
1054 {
1055 GnomeCanvasItem *focused_item;
1056 GdkEvent ev;
1057
1058 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
1059 g_return_if_fail (gtk_widget_get_can_focus (GTK_WIDGET (item->canvas)));
1060
1061 focused_item = item->canvas->focused_item;
1062
1063 if (focused_item) {
1064 GtkLayout *layout;
1065 GdkWindow *bin_window;
1066
1067 layout = GTK_LAYOUT (item->canvas);
1068 bin_window = gtk_layout_get_bin_window (layout);
1069
1070 ev.focus_change.type = GDK_FOCUS_CHANGE;
1071 ev.focus_change.window = bin_window;
1072 ev.focus_change.send_event = FALSE;
1073 ev.focus_change.in = FALSE;
1074
1075 emit_event (item->canvas, &ev);
1076 }
1077
1078 item->canvas->focused_item = item;
1079 gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
1080
1081 if (focused_item) {
1082 GtkLayout *layout;
1083 GdkWindow *bin_window;
1084
1085 layout = GTK_LAYOUT (item->canvas);
1086 bin_window = gtk_layout_get_bin_window (layout);
1087
1088 ev.focus_change.type = GDK_FOCUS_CHANGE;
1089 ev.focus_change.window = bin_window;
1090 ev.focus_change.send_event = FALSE;
1091 ev.focus_change.in = TRUE;
1092
1093 emit_event (item->canvas, &ev);
1094 }
1095 }
1096
1097 /**
1098 * gnome_canvas_item_get_bounds:
1099 * @item: A canvas item.
1100 * @x1: Leftmost edge of the bounding box (return value).
1101 * @y1: Upper edge of the bounding box (return value).
1102 * @x2: Rightmost edge of the bounding box (return value).
1103 * @y2: Lower edge of the bounding box (return value).
1104 *
1105 * Queries the bounding box of a canvas item. The bounds are returned in the
1106 * coordinate system of the item's parent.
1107 **/
1108 void
1109 gnome_canvas_item_get_bounds (GnomeCanvasItem *item,
1110 gdouble *x1,
1111 gdouble *y1,
1112 gdouble *x2,
1113 gdouble *y2)
1114 {
1115 gdouble tx1, ty1, tx2, ty2;
1116
1117 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
1118
1119 tx1 = ty1 = tx2 = ty2 = 0.0;
1120
1121 /* Get the item's bounds in its coordinate system */
1122
1123 if (GNOME_CANVAS_ITEM_GET_CLASS (item)->bounds)
1124 GNOME_CANVAS_ITEM_GET_CLASS (item)->bounds (
1125 item, &tx1, &ty1, &tx2, &ty2);
1126
1127 /* Make the bounds relative to the item's parent coordinate system */
1128 gnome_canvas_matrix_transform_rect (&item->matrix, &tx1, &ty1, &tx2, &ty2);
1129
1130 /* Return the values */
1131
1132 if (x1)
1133 *x1 = tx1;
1134
1135 if (y1)
1136 *y1 = ty1;
1137
1138 if (x2)
1139 *x2 = tx2;
1140
1141 if (y2)
1142 *y2 = ty2;
1143 }
1144
1145 /**
1146 * gnome_canvas_item_request_update
1147 * @item: A canvas item.
1148 *
1149 * To be used only by item implementations. Requests that the canvas queue an
1150 * update for the specified item.
1151 **/
1152 void
1153 gnome_canvas_item_request_update (GnomeCanvasItem *item)
1154 {
1155 if (item->flags & GNOME_CANVAS_ITEM_NEED_UPDATE)
1156 return;
1157
1158 item->flags |= GNOME_CANVAS_ITEM_NEED_UPDATE;
1159
1160 if (item->parent != NULL) {
1161 /* Recurse up the tree */
1162 gnome_canvas_item_request_update (item->parent);
1163 } else {
1164 /* Have reached the top of the tree, make
1165 * sure the update call gets scheduled. */
1166 gnome_canvas_request_update (item->canvas);
1167 }
1168 }
1169
1170 /*** GnomeCanvasGroup ***/
1171
1172 enum {
1173 GROUP_PROP_0,
1174 GROUP_PROP_X,
1175 GROUP_PROP_Y
1176 };
1177
1178 static void gnome_canvas_group_set_property (GObject *object,
1179 guint property_id,
1180 const GValue *value,
1181 GParamSpec *pspec);
1182 static void gnome_canvas_group_get_property (GObject *object,
1183 guint property_id,
1184 GValue *value,
1185 GParamSpec *pspec);
1186
1187 static void gnome_canvas_group_dispose (GnomeCanvasItem *object);
1188
1189 static void gnome_canvas_group_update (GnomeCanvasItem *item,
1190 const cairo_matrix_t *matrix,
1191 gint flags);
1192 static void gnome_canvas_group_realize (GnomeCanvasItem *item);
1193 static void gnome_canvas_group_unrealize (GnomeCanvasItem *item);
1194 static void gnome_canvas_group_map (GnomeCanvasItem *item);
1195 static void gnome_canvas_group_unmap (GnomeCanvasItem *item);
1196 static void gnome_canvas_group_draw (GnomeCanvasItem *item,
1197 cairo_t *cr,
1198 gint x, gint y,
1199 gint width, gint height);
1200 static GnomeCanvasItem *gnome_canvas_group_point (GnomeCanvasItem *item,
1201 gdouble x, gdouble y,
1202 gint cx, gint cy);
1203 static void gnome_canvas_group_bounds (GnomeCanvasItem *item,
1204 gdouble *x1, gdouble *y1,
1205 gdouble *x2, gdouble *y2);
1206
1207 G_DEFINE_TYPE (
1208 GnomeCanvasGroup,
1209 gnome_canvas_group,
1210 GNOME_TYPE_CANVAS_ITEM)
1211
1212 /* Class initialization function for GnomeCanvasGroupClass */
1213 static void
1214 gnome_canvas_group_class_init (GnomeCanvasGroupClass *class)
1215 {
1216 GObjectClass *object_class;
1217 GnomeCanvasItemClass *item_class;
1218
1219 object_class = (GObjectClass *) class;
1220 item_class = (GnomeCanvasItemClass *) class;
1221
1222 object_class->set_property = gnome_canvas_group_set_property;
1223 object_class->get_property = gnome_canvas_group_get_property;
1224
1225 g_object_class_install_property (
1226 object_class,
1227 GROUP_PROP_X,
1228 g_param_spec_double (
1229 "x",
1230 "X",
1231 "X",
1232 -G_MAXDOUBLE,
1233 G_MAXDOUBLE,
1234 0.0,
1235 G_PARAM_READABLE |
1236 G_PARAM_WRITABLE));
1237
1238 g_object_class_install_property (
1239 object_class,
1240 GROUP_PROP_Y,
1241 g_param_spec_double (
1242 "y",
1243 "Y",
1244 "Y",
1245 -G_MAXDOUBLE,
1246 G_MAXDOUBLE,
1247 0.0,
1248 G_PARAM_READABLE |
1249 G_PARAM_WRITABLE));
1250
1251 item_class->dispose = gnome_canvas_group_dispose;
1252 item_class->update = gnome_canvas_group_update;
1253 item_class->realize = gnome_canvas_group_realize;
1254 item_class->unrealize = gnome_canvas_group_unrealize;
1255 item_class->map = gnome_canvas_group_map;
1256 item_class->unmap = gnome_canvas_group_unmap;
1257 item_class->draw = gnome_canvas_group_draw;
1258 item_class->point = gnome_canvas_group_point;
1259 item_class->bounds = gnome_canvas_group_bounds;
1260 }
1261
1262 /* Object initialization function for GnomeCanvasGroup */
1263 static void
1264 gnome_canvas_group_init (GnomeCanvasGroup *group)
1265 {
1266 }
1267
1268 /* Set_property handler for canvas groups */
1269 static void
1270 gnome_canvas_group_set_property (GObject *gobject,
1271 guint property_id,
1272 const GValue *value,
1273 GParamSpec *pspec)
1274 {
1275 GnomeCanvasItem *item;
1276
1277 g_return_if_fail (GNOME_IS_CANVAS_GROUP (gobject));
1278
1279 item = GNOME_CANVAS_ITEM (gobject);
1280
1281 switch (property_id) {
1282 case GROUP_PROP_X:
1283 item->matrix.x0 = g_value_get_double (value);
1284 break;
1285
1286 case GROUP_PROP_Y:
1287 item->matrix.y0 = g_value_get_double (value);
1288 break;
1289
1290 default:
1291 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
1292 break;
1293 }
1294 }
1295
1296 /* Get_property handler for canvas groups */
1297 static void
1298 gnome_canvas_group_get_property (GObject *gobject,
1299 guint property_id,
1300 GValue *value,
1301 GParamSpec *pspec)
1302 {
1303 GnomeCanvasItem *item;
1304
1305 g_return_if_fail (GNOME_IS_CANVAS_GROUP (gobject));
1306
1307 item = GNOME_CANVAS_ITEM (gobject);
1308
1309 switch (property_id) {
1310 case GROUP_PROP_X:
1311 g_value_set_double (value, item->matrix.x0);
1312 break;
1313
1314 case GROUP_PROP_Y:
1315 g_value_set_double (value, item->matrix.y0);
1316 break;
1317
1318 default:
1319 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
1320 break;
1321 }
1322 }
1323
1324 /* Dispose handler for canvas groups */
1325 static void
1326 gnome_canvas_group_dispose (GnomeCanvasItem *object)
1327 {
1328 GnomeCanvasGroup *group;
1329
1330 g_return_if_fail (GNOME_IS_CANVAS_GROUP (object));
1331
1332 group = GNOME_CANVAS_GROUP (object);
1333
1334 while (group->item_list) {
1335 /* child is unref'ed by the child's group_remove (). */
1336 g_object_run_dispose (G_OBJECT (group->item_list->data));
1337 }
1338
1339 GNOME_CANVAS_ITEM_CLASS (gnome_canvas_group_parent_class)->
1340 dispose (object);
1341 }
1342
1343 /* Update handler for canvas groups */
1344 static void
1345 gnome_canvas_group_update (GnomeCanvasItem *item,
1346 const cairo_matrix_t *i2c,
1347 gint flags)
1348 {
1349 GnomeCanvasGroup *group;
1350 GList *list;
1351 GnomeCanvasItem *i;
1352 gdouble x1, y1, x2, y2;
1353
1354 group = GNOME_CANVAS_GROUP (item);
1355
1356 GNOME_CANVAS_ITEM_CLASS (gnome_canvas_group_parent_class)->
1357 update (item, i2c, flags);
1358
1359 x1 = G_MAXDOUBLE;
1360 y1 = G_MAXDOUBLE;
1361 x2 = -G_MAXDOUBLE;
1362 y2 = -G_MAXDOUBLE;
1363
1364 for (list = group->item_list; list; list = list->next) {
1365 i = list->data;
1366
1367 gnome_canvas_item_invoke_update (i, i2c, flags);
1368
1369 x1 = MIN (x1, i->x1);
1370 x2 = MAX (x2, i->x2);
1371 y1 = MIN (y1, i->y1);
1372 y2 = MAX (y2, i->y2);
1373 }
1374 if (x1 >= x2 || y1 >= y2) {
1375 item->x1 = item->x2 = item->y1 = item->y2 = 0;
1376 } else {
1377 item->x1 = x1;
1378 item->y1 = y1;
1379 item->x2 = x2;
1380 item->y2 = y2;
1381 }
1382 }
1383
1384 /* Realize handler for canvas groups */
1385 static void
1386 gnome_canvas_group_realize (GnomeCanvasItem *item)
1387 {
1388 GnomeCanvasGroup *group;
1389 GList *list;
1390 GnomeCanvasItem *i;
1391
1392 group = GNOME_CANVAS_GROUP (item);
1393
1394 for (list = group->item_list; list; list = list->next) {
1395 i = list->data;
1396
1397 if (!(i->flags & GNOME_CANVAS_ITEM_REALIZED))
1398 (* GNOME_CANVAS_ITEM_GET_CLASS (i)->realize) (i);
1399 }
1400
1401 GNOME_CANVAS_ITEM_CLASS (gnome_canvas_group_parent_class)->
1402 realize (item);
1403 }
1404
1405 /* Unrealize handler for canvas groups */
1406 static void
1407 gnome_canvas_group_unrealize (GnomeCanvasItem *item)
1408 {
1409 GnomeCanvasGroup *group;
1410 GList *list;
1411 GnomeCanvasItem *i;
1412
1413 group = GNOME_CANVAS_GROUP (item);
1414
1415 for (list = group->item_list; list; list = list->next) {
1416 i = list->data;
1417
1418 if (i->flags & GNOME_CANVAS_ITEM_REALIZED)
1419 (* GNOME_CANVAS_ITEM_GET_CLASS (i)->unrealize) (i);
1420 }
1421
1422 GNOME_CANVAS_ITEM_CLASS (gnome_canvas_group_parent_class)->
1423 unrealize (item);
1424 }
1425
1426 /* Map handler for canvas groups */
1427 static void
1428 gnome_canvas_group_map (GnomeCanvasItem *item)
1429 {
1430 GnomeCanvasGroup *group;
1431 GList *list;
1432 GnomeCanvasItem *i;
1433
1434 group = GNOME_CANVAS_GROUP (item);
1435
1436 for (list = group->item_list; list; list = list->next) {
1437 i = list->data;
1438
1439 if (!(i->flags & GNOME_CANVAS_ITEM_MAPPED))
1440 (* GNOME_CANVAS_ITEM_GET_CLASS (i)->map) (i);
1441 }
1442
1443 GNOME_CANVAS_ITEM_CLASS (gnome_canvas_group_parent_class)->map (item);
1444 }
1445
1446 /* Unmap handler for canvas groups */
1447 static void
1448 gnome_canvas_group_unmap (GnomeCanvasItem *item)
1449 {
1450 GnomeCanvasGroup *group;
1451 GList *list;
1452 GnomeCanvasItem *i;
1453
1454 group = GNOME_CANVAS_GROUP (item);
1455
1456 for (list = group->item_list; list; list = list->next) {
1457 i = list->data;
1458
1459 if (i->flags & GNOME_CANVAS_ITEM_MAPPED)
1460 (* GNOME_CANVAS_ITEM_GET_CLASS (i)->unmap) (i);
1461 }
1462
1463 GNOME_CANVAS_ITEM_CLASS (gnome_canvas_group_parent_class)->unmap (item);
1464 }
1465
1466 /* Draw handler for canvas groups */
1467 static void
1468 gnome_canvas_group_draw (GnomeCanvasItem *item,
1469 cairo_t *cr,
1470 gint x,
1471 gint y,
1472 gint width,
1473 gint height)
1474 {
1475 GnomeCanvasGroup *group;
1476 GList *list;
1477 GnomeCanvasItem *child = NULL;
1478
1479 group = GNOME_CANVAS_GROUP (item);
1480
1481 for (list = group->item_list; list; list = list->next) {
1482 child = list->data;
1483
1484 if ((child->flags & GNOME_CANVAS_ITEM_VISIBLE)
1485 && ((child->x1 < (x + width))
1486 && (child->y1 < (y + height))
1487 && (child->x2 > x)
1488 && (child->y2 > y))) {
1489 cairo_save (cr);
1490
1491 GNOME_CANVAS_ITEM_GET_CLASS (child)->draw (
1492 child, cr, x, y, width, height);
1493
1494 cairo_restore (cr);
1495 }
1496 }
1497 }
1498
1499 /* Point handler for canvas groups */
1500 static GnomeCanvasItem *
1501 gnome_canvas_group_point (GnomeCanvasItem *item,
1502 gdouble x,
1503 gdouble y,
1504 gint cx,
1505 gint cy)
1506 {
1507 GnomeCanvasGroup *group;
1508 GList *list;
1509 GnomeCanvasItem *child, *point_item;
1510
1511 group = GNOME_CANVAS_GROUP (item);
1512
1513 for (list = g_list_last (group->item_list); list; list = list->prev) {
1514 child = list->data;
1515
1516 if ((child->x1 > cx) || (child->y1 > cy))
1517 continue;
1518
1519 if ((child->x2 < cx) || (child->y2 < cy))
1520 continue;
1521
1522 if (!(child->flags & GNOME_CANVAS_ITEM_VISIBLE))
1523 continue;
1524
1525 point_item = gnome_canvas_item_invoke_point (child, x, y, cx, cy);
1526 if (point_item)
1527 return point_item;
1528 }
1529
1530 return NULL;
1531 }
1532
1533 /* Bounds handler for canvas groups */
1534 static void
1535 gnome_canvas_group_bounds (GnomeCanvasItem *item,
1536 gdouble *x1,
1537 gdouble *y1,
1538 gdouble *x2,
1539 gdouble *y2)
1540 {
1541 GnomeCanvasGroup *group;
1542 GnomeCanvasItem *child;
1543 GList *list;
1544 gdouble tx1, ty1, tx2, ty2;
1545 gdouble minx, miny, maxx, maxy;
1546 gint set;
1547
1548 group = GNOME_CANVAS_GROUP (item);
1549
1550 /* Get the bounds of the first visible item */
1551
1552 child = NULL; /* Unnecessary but eliminates a warning. */
1553
1554 set = FALSE;
1555
1556 for (list = group->item_list; list; list = list->next) {
1557 child = list->data;
1558
1559 if (child->flags & GNOME_CANVAS_ITEM_VISIBLE) {
1560 set = TRUE;
1561 gnome_canvas_item_get_bounds (child, &minx, &miny, &maxx, &maxy);
1562 break;
1563 }
1564 }
1565
1566 /* If there were no visible items, return an empty bounding box */
1567
1568 if (!set) {
1569 *x1 = *y1 = *x2 = *y2 = 0.0;
1570 return;
1571 }
1572
1573 /* Now we can grow the bounds using the rest of the items */
1574
1575 list = list->next;
1576
1577 for (; list; list = list->next) {
1578 child = list->data;
1579
1580 if (!(child->flags & GNOME_CANVAS_ITEM_VISIBLE))
1581 continue;
1582
1583 gnome_canvas_item_get_bounds (child, &tx1, &ty1, &tx2, &ty2);
1584
1585 if (tx1 < minx)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
1586 minx = tx1;
1587
1588 if (ty1 < miny)
1589 miny = ty1;
1590
1591 if (tx2 > maxx)
1592 maxx = tx2;
1593
1594 if (ty2 > maxy)
1595 maxy = ty2;
1596 }
1597
1598 *x1 = minx;
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
1599 *y1 = miny;
1600 *x2 = maxx;
1601 *y2 = maxy;
1602 }
1603
1604 /* Adds an item to a group */
1605 static void
1606 group_add (GnomeCanvasGroup *group,
1607 GnomeCanvasItem *item)
1608 {
1609 g_object_ref_sink (item);
1610
1611 if (!group->item_list) {
1612 group->item_list = g_list_append (group->item_list, item);
1613 group->item_list_end = group->item_list;
1614 } else
1615 group->item_list_end = g_list_append (group->item_list_end, item)->next;
1616
1617 if (group->item.flags & GNOME_CANVAS_ITEM_REALIZED)
1618 (* GNOME_CANVAS_ITEM_GET_CLASS (item)->realize) (item);
1619
1620 if (group->item.flags & GNOME_CANVAS_ITEM_MAPPED)
1621 (* GNOME_CANVAS_ITEM_GET_CLASS (item)->map) (item);
1622
1623 g_object_notify (G_OBJECT (item), "parent");
1624 }
1625
1626 /* Removes an item from a group */
1627 static void
1628 group_remove (GnomeCanvasGroup *group,
1629 GnomeCanvasItem *item)
1630 {
1631 GList *children;
1632
1633 g_return_if_fail (GNOME_IS_CANVAS_GROUP (group));
1634 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
1635
1636 for (children = group->item_list; children; children = children->next)
1637 if (children->data == item) {
1638 if (item->flags & GNOME_CANVAS_ITEM_MAPPED)
1639 (* GNOME_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
1640
1641 if (item->flags & GNOME_CANVAS_ITEM_REALIZED)
1642 (* GNOME_CANVAS_ITEM_GET_CLASS (item)->unrealize) (item);
1643
1644 /* Unparent the child */
1645
1646 item->parent = NULL;
1647 g_object_unref (item);
1648
1649 /* Remove it from the list */
1650
1651 if (children == group->item_list_end)
1652 group->item_list_end = children->prev;
1653
1654 group->item_list = g_list_remove_link (group->item_list, children);
1655 g_list_free (children);
1656 break;
1657 }
1658 }
1659
1660 /*** GnomeCanvas ***/
1661
1662 enum {
1663 DRAW_BACKGROUND,
1664 LAST_SIGNAL
1665 };
1666
1667 static void gnome_canvas_dispose (GObject *object);
1668 static void gnome_canvas_map (GtkWidget *widget);
1669 static void gnome_canvas_unmap (GtkWidget *widget);
1670 static void gnome_canvas_realize (GtkWidget *widget);
1671 static void gnome_canvas_unrealize (GtkWidget *widget);
1672 static void gnome_canvas_size_allocate (GtkWidget *widget,
1673 GtkAllocation *allocation);
1674 static gint gnome_canvas_draw (GtkWidget *widget,
1675 cairo_t *cr);
1676 static gint gnome_canvas_button (GtkWidget *widget,
1677 GdkEventButton *event);
1678 static gint gnome_canvas_motion (GtkWidget *widget,
1679 GdkEventMotion *event);
1680 static gboolean gnome_canvas_key (GtkWidget *widget,
1681 GdkEventKey *event);
1682 static gint gnome_canvas_crossing (GtkWidget *widget,
1683 GdkEventCrossing *event);
1684 static gint gnome_canvas_focus_in (GtkWidget *widget,
1685 GdkEventFocus *event);
1686 static gint gnome_canvas_focus_out (GtkWidget *widget,
1687 GdkEventFocus *event);
1688 static void gnome_canvas_request_update_real (GnomeCanvas *canvas);
1689 static void gnome_canvas_draw_background (GnomeCanvas *canvas,
1690 cairo_t *cr,
1691 gint x,
1692 gint y,
1693 gint width,
1694 gint height);
1695
1696 static guint canvas_signals[LAST_SIGNAL];
1697
1698 enum {
1699 PROP_0,
1700 PROP_FOCUSED_ITEM,
1701 };
1702
1703 G_DEFINE_TYPE (
1704 GnomeCanvas,
1705 gnome_canvas,
1706 GTK_TYPE_LAYOUT)
1707
1708 static void
1709 gnome_canvas_paint_rect (GnomeCanvas *canvas,
1710 cairo_t *cr,
1711 gint x0,
1712 gint y0,
1713 gint x1,
1714 gint y1)
1715 {
1716 GtkWidget *widget;
1717 GtkAllocation allocation;
1718 GtkScrollable *scrollable;
1719 GtkAdjustment *hadjustment;
1720 GtkAdjustment *vadjustment;
1721 gint draw_x1, draw_y1;
1722 gint draw_x2, draw_y2;
1723 gint draw_width, draw_height;
1724 gdouble hadjustment_value;
1725 gdouble vadjustment_value;
1726
1727 g_return_if_fail (!canvas->need_update);
1728
1729 widget = GTK_WIDGET (canvas);
1730 gtk_widget_get_allocation (widget, &allocation);
1731
1732 scrollable = GTK_SCROLLABLE (canvas);
1733 hadjustment = gtk_scrollable_get_hadjustment (scrollable);
1734 vadjustment = gtk_scrollable_get_vadjustment (scrollable);
1735
1736 hadjustment_value = gtk_adjustment_get_value (hadjustment);
1737 vadjustment_value = gtk_adjustment_get_value (vadjustment);
1738
1739 draw_x1 = MAX (x0, hadjustment_value - canvas->zoom_xofs);
1740 draw_y1 = MAX (y0, vadjustment_value - canvas->zoom_yofs);
1741 draw_x2 = MIN (draw_x1 + allocation.width, x1);
1742 draw_y2 = MIN (draw_y1 + allocation.height, y1);
1743
1744 draw_width = draw_x2 - draw_x1;
1745 draw_height = draw_y2 - draw_y1;
1746
1747 if (draw_width < 1 || draw_height < 1)
1748 return;
1749
1750 canvas->draw_xofs = draw_x1;
1751 canvas->draw_yofs = draw_y1;
1752
1753 cairo_save (cr);
1754
1755 g_signal_emit (
1756 canvas, canvas_signals[DRAW_BACKGROUND], 0, cr,
1757 draw_x1, draw_y1, draw_width, draw_height);
1758
1759 cairo_restore (cr);
1760
1761 if (canvas->root->flags & GNOME_CANVAS_ITEM_VISIBLE) {
1762 cairo_save (cr);
1763
1764 (* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->draw) (
1765 canvas->root, cr,
1766 draw_x1, draw_y1,
1767 draw_width, draw_height);
1768
1769 cairo_restore (cr);
1770 }
1771 }
1772
1773 static void
1774 gnome_canvas_get_property (GObject *object,
1775 guint property_id,
1776 GValue *value,
1777 GParamSpec *pspec)
1778 {
1779 switch (property_id) {
1780 case PROP_FOCUSED_ITEM:
1781 g_value_set_object (value, GNOME_CANVAS (object)->focused_item);
1782 break;
1783 default:
1784 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1785 break;
1786 }
1787 }
1788
1789 static void
1790 gnome_canvas_set_property (GObject *object,
1791 guint property_id,
1792 const GValue *value,
1793 GParamSpec *pspec)
1794 {
1795 switch (property_id) {
1796 case PROP_FOCUSED_ITEM:
1797 GNOME_CANVAS (object)->focused_item = g_value_get_object (value);
1798 break;
1799 default:
1800 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1801 break;
1802 }
1803 }
1804
1805 /* Class initialization function for GnomeCanvasClass */
1806 static void
1807 gnome_canvas_class_init (GnomeCanvasClass *class)
1808 {
1809 GObjectClass *object_class;
1810 GtkWidgetClass *widget_class;
1811
1812 object_class = (GObjectClass *) class;
1813 widget_class = (GtkWidgetClass *) class;
1814
1815 object_class->set_property = gnome_canvas_set_property;
1816 object_class->get_property = gnome_canvas_get_property;
1817 object_class->dispose = gnome_canvas_dispose;
1818
1819 widget_class->map = gnome_canvas_map;
1820 widget_class->unmap = gnome_canvas_unmap;
1821 widget_class->realize = gnome_canvas_realize;
1822 widget_class->unrealize = gnome_canvas_unrealize;
1823 widget_class->size_allocate = gnome_canvas_size_allocate;
1824 widget_class->draw = gnome_canvas_draw;
1825 widget_class->button_press_event = gnome_canvas_button;
1826 widget_class->button_release_event = gnome_canvas_button;
1827 widget_class->motion_notify_event = gnome_canvas_motion;
1828 widget_class->key_press_event = gnome_canvas_key;
1829 widget_class->key_release_event = gnome_canvas_key;
1830 widget_class->enter_notify_event = gnome_canvas_crossing;
1831 widget_class->leave_notify_event = gnome_canvas_crossing;
1832 widget_class->focus_in_event = gnome_canvas_focus_in;
1833 widget_class->focus_out_event = gnome_canvas_focus_out;
1834
1835 class->draw_background = gnome_canvas_draw_background;
1836 class->request_update = gnome_canvas_request_update_real;
1837
1838 g_object_class_install_property (
1839 object_class,
1840 PROP_FOCUSED_ITEM,
1841 g_param_spec_object (
1842 "focused_item",
1843 NULL,
1844 NULL,
1845 GNOME_TYPE_CANVAS_ITEM,
1846 G_PARAM_READABLE |
1847 G_PARAM_WRITABLE));
1848
1849 canvas_signals[DRAW_BACKGROUND] = g_signal_new (
1850 "draw_background",
1851 G_TYPE_FROM_CLASS (object_class),
1852 G_SIGNAL_RUN_LAST,
1853 G_STRUCT_OFFSET (GnomeCanvasClass, draw_background),
1854 NULL, NULL, NULL,
1855 G_TYPE_NONE, 5,
1856 CAIRO_GOBJECT_TYPE_CONTEXT,
1857 G_TYPE_INT,
1858 G_TYPE_INT,
1859 G_TYPE_INT,
1860 G_TYPE_INT);
1861
1862 gail_canvas_init ();
1863 }
1864
1865 /* Callback used when the root item of a canvas is destroyed. The user should
1866 * never ever do this, so we panic if this happens.
1867 */
1868 G_GNUC_NORETURN static void
1869 panic_root_finalized (gpointer data,
1870 GObject *gone_object)
1871 {
1872 g_error ("Eeeek, root item %p of canvas %p was destroyed!", gone_object, data);
1873 }
1874
1875 /* Object initialization function for GnomeCanvas */
1876 static void
1877 gnome_canvas_init (GnomeCanvas *canvas)
1878 {
1879 GtkLayout *layout;
1880 guint layout_width, layout_height;
1881
1882 layout = GTK_LAYOUT (canvas);
1883 gtk_layout_get_size (layout, &layout_width, &layout_height);
1884
1885 gtk_widget_set_can_focus (GTK_WIDGET (canvas), TRUE);
1886
1887 canvas->need_update = FALSE;
1888 canvas->idle_id = 0;
1889
1890 canvas->scroll_x1 = 0.0;
1891 canvas->scroll_y1 = 0.0;
1892 canvas->scroll_x2 = layout_width;
1893 canvas->scroll_y2 = layout_height;
1894
1895 canvas->pick_event.type = GDK_LEAVE_NOTIFY;
1896 canvas->pick_event.crossing.x = 0;
1897 canvas->pick_event.crossing.y = 0;
1898
1899 gtk_scrollable_set_hadjustment (GTK_SCROLLABLE (canvas), NULL);
1900 gtk_scrollable_set_vadjustment (GTK_SCROLLABLE (canvas), NULL);
1901
1902 /* Create the root item as a special case */
1903
1904 canvas->root = GNOME_CANVAS_ITEM (
1905 g_object_new (gnome_canvas_group_get_type (), NULL));
1906 canvas->root->canvas = canvas;
1907
1908 g_object_ref_sink (canvas->root);
1909
1910 g_object_weak_ref (G_OBJECT (canvas->root), panic_root_finalized, canvas);
1911
1912 canvas->need_repick = TRUE;
1913 }
1914
1915 /* Convenience function to remove the idle handler of a canvas */
1916 static void
1917 remove_idle (GnomeCanvas *canvas)
1918 {
1919 if (canvas->idle_id == 0)
1920 return;
1921
1922 g_source_remove (canvas->idle_id);
1923 canvas->idle_id = 0;
1924 }
1925
1926 /* Removes the transient state of the canvas (idle handler, grabs). */
1927 static void
1928 shutdown_transients (GnomeCanvas *canvas)
1929 {
1930 if (canvas->grabbed_item) {
1931 canvas->grabbed_item = NULL;
1932 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1933 }
1934
1935 remove_idle (canvas);
1936 }
1937
1938 /* Dispose handler for GnomeCanvas */
1939 static void
1940 gnome_canvas_dispose (GObject *object)
1941 {
1942 GnomeCanvas *canvas;
1943
1944 g_return_if_fail (GNOME_IS_CANVAS (object));
1945
1946 /* remember, dispose can be run multiple times! */
1947
1948 canvas = GNOME_CANVAS (object);
1949
1950 if (canvas->root) {
1951 g_object_weak_unref (G_OBJECT (canvas->root), panic_root_finalized, canvas);
1952 g_object_unref (canvas->root);
1953 canvas->root = NULL;
1954 }
1955
1956 shutdown_transients (canvas);
1957
1958 /* Chain up to parent's dispose() method. */
1959 G_OBJECT_CLASS (gnome_canvas_parent_class)->dispose (object);
1960 }
1961
1962 /**
1963 * gnome_canvas_new:
1964 *
1965 * Creates a new empty canvas in non-antialiased mode.
1966 *
1967 * Return value: A newly-created canvas.
1968 **/
1969 GtkWidget *
1970 gnome_canvas_new (void)
1971 {
1972 return GTK_WIDGET (g_object_new (gnome_canvas_get_type (), NULL));
1973 }
1974
1975 /* Map handler for the canvas */
1976 static void
1977 gnome_canvas_map (GtkWidget *widget)
1978 {
1979 GnomeCanvas *canvas;
1980
1981 g_return_if_fail (GNOME_IS_CANVAS (widget));
1982
1983 /* Normal widget mapping stuff */
1984
1985 GTK_WIDGET_CLASS (gnome_canvas_parent_class)->map (widget);
1986
1987 canvas = GNOME_CANVAS (widget);
1988
1989 if (canvas->need_update)
1990 add_idle (canvas);
1991
1992 /* Map items */
1993
1994 if (GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->map)
1995 (* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->map) (canvas->root);
1996 }
1997
1998 /* Unmap handler for the canvas */
1999 static void
2000 gnome_canvas_unmap (GtkWidget *widget)
2001 {
2002 GnomeCanvas *canvas;
2003
2004 g_return_if_fail (GNOME_IS_CANVAS (widget));
2005
2006 canvas = GNOME_CANVAS (widget);
2007
2008 shutdown_transients (canvas);
2009
2010 /* Unmap items */
2011
2012 if (GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->unmap)
2013 (* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->unmap) (canvas->root);
2014
2015 /* Normal widget unmapping stuff */
2016
2017 GTK_WIDGET_CLASS (gnome_canvas_parent_class)->unmap (widget);
2018 }
2019
2020 /* Realize handler for the canvas */
2021 static void
2022 gnome_canvas_realize (GtkWidget *widget)
2023 {
2024 GnomeCanvas *canvas;
2025 GtkLayout *layout;
2026 GdkWindow *bin_window;
2027
2028 g_return_if_fail (GNOME_IS_CANVAS (widget));
2029
2030 /* Normal widget realization stuff */
2031
2032 GTK_WIDGET_CLASS (gnome_canvas_parent_class)->realize (widget);
2033
2034 canvas = GNOME_CANVAS (widget);
2035
2036 layout = GTK_LAYOUT (canvas);
2037 bin_window = gtk_layout_get_bin_window (layout);
2038
2039 gdk_window_set_events (
2040 bin_window,
2041 (gdk_window_get_events (bin_window)
2042 | GDK_EXPOSURE_MASK
2043 | GDK_SCROLL_MASK
2044 | GDK_BUTTON_PRESS_MASK
2045 | GDK_BUTTON_RELEASE_MASK
2046 | GDK_POINTER_MOTION_MASK
2047 | GDK_KEY_PRESS_MASK
2048 | GDK_KEY_RELEASE_MASK
2049 | GDK_ENTER_NOTIFY_MASK
2050 | GDK_LEAVE_NOTIFY_MASK
2051 | GDK_FOCUS_CHANGE_MASK));
2052
2053 /* Create our own temporary pixmap gc and realize all the items */
2054
2055 (* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->realize) (canvas->root);
2056 }
2057
2058 /* Unrealize handler for the canvas */
2059 static void
2060 gnome_canvas_unrealize (GtkWidget *widget)
2061 {
2062 GnomeCanvas *canvas;
2063
2064 g_return_if_fail (GNOME_IS_CANVAS (widget));
2065
2066 canvas = GNOME_CANVAS (widget);
2067
2068 shutdown_transients (canvas);
2069
2070 /* Unrealize items and parent widget */
2071
2072 (* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->unrealize) (canvas->root);
2073
2074 GTK_WIDGET_CLASS (gnome_canvas_parent_class)->unrealize (widget);
2075 }
2076
2077 /* Handles scrolling of the canvas. Adjusts the scrolling and zooming offset to
2078 * keep as much as possible of the canvas scrolling region in view.
2079 */
2080 static void
2081 scroll_to (GnomeCanvas *canvas,
2082 gint cx,
2083 gint cy)
2084 {
2085 GtkWidget *widget;
2086 GtkAllocation allocation;
2087 GtkScrollable *scrollable;
2088 GtkAdjustment *hadjustment;
2089 GtkAdjustment *vadjustment;
2090 guint layout_width, layout_height;
2091 gint scroll_width, scroll_height;
2092 gint right_limit, bottom_limit;
2093 gint old_zoom_xofs, old_zoom_yofs;
2094 gint canvas_width, canvas_height;
2095
2096 widget = GTK_WIDGET (canvas);
2097 gtk_widget_get_allocation (widget, &allocation);
2098
2099 scrollable = GTK_SCROLLABLE (canvas);
2100 hadjustment = gtk_scrollable_get_hadjustment (scrollable);
2101 vadjustment = gtk_scrollable_get_vadjustment (scrollable);
2102
2103 gtk_layout_get_size (GTK_LAYOUT (canvas), &layout_width, &layout_height);
2104
2105 canvas_width = allocation.width;
2106 canvas_height = allocation.height;
2107
2108 scroll_width =
2109 floor ((canvas->scroll_x2 - canvas->scroll_x1) + 0.5);
2110 scroll_height =
2111 floor ((canvas->scroll_y2 - canvas->scroll_y1) + 0.5);
2112
2113 right_limit = scroll_width - canvas_width;
2114 bottom_limit = scroll_height - canvas_height;
2115
2116 old_zoom_xofs = canvas->zoom_xofs;
2117 old_zoom_yofs = canvas->zoom_yofs;
2118
2119 if (right_limit < 0) {
2120 cx = 0;
2121 canvas->zoom_xofs = (canvas_width - scroll_width) / 2;
2122 scroll_width = canvas_width;
2123 } else if (cx < 0) {
2124 cx = 0;
2125 canvas->zoom_xofs = 0;
2126 } else if (cx > right_limit) {
2127 cx = right_limit;
2128 canvas->zoom_xofs = 0;
2129 } else
2130 canvas->zoom_xofs = 0;
2131
2132 if (bottom_limit < 0) {
2133 cy = 0;
2134 canvas->zoom_yofs = (canvas_height - scroll_height) / 2;
2135 scroll_height = canvas_height;
2136 } else if (cy < 0) {
2137 cy = 0;
2138 canvas->zoom_yofs = 0;
2139 } else if (cy > bottom_limit) {
2140 cy = bottom_limit;
2141 canvas->zoom_yofs = 0;
2142 } else
2143 canvas->zoom_yofs = 0;
2144
2145 if ((canvas->zoom_xofs != old_zoom_xofs) ||
2146 (canvas->zoom_yofs != old_zoom_yofs)) {
2147 /* This can only occur, if either canvas size or widget
2148 * size changes. So I think we can request full redraw
2149 * here. The reason is, that coverage UTA will be
2150 * invalidated by offset change. */
2151 /* FIXME Strictly this is not correct - we have to remove
2152 * our own idle (Lauris) */
2153 /* More stuff - we have to mark root as needing fresh affine
2154 * (Lauris) */
2155 if (!(canvas->root->flags & GNOME_CANVAS_ITEM_NEED_AFFINE)) {
2156 canvas->root->flags |= GNOME_CANVAS_ITEM_NEED_AFFINE;
2157 gnome_canvas_request_update (canvas);
2158 }
2159 gtk_widget_queue_draw (GTK_WIDGET (canvas));
2160 }
2161
2162 if (hadjustment)
2163 gtk_adjustment_set_value (hadjustment, cx);
2164
2165 if (vadjustment)
2166 gtk_adjustment_set_value (vadjustment, cy);
2167
2168 if ((scroll_width != (gint) layout_width)
2169 || (scroll_height != (gint) layout_height))
2170 gtk_layout_set_size (GTK_LAYOUT (canvas), scroll_width, scroll_height);
2171 }
2172
2173 /* Size allocation handler for the canvas */
2174 static void
2175 gnome_canvas_size_allocate (GtkWidget *widget,
2176 GtkAllocation *allocation)
2177 {
2178 GtkScrollable *scrollable;
2179 GtkAdjustment *hadjustment;
2180 GtkAdjustment *vadjustment;
2181
2182 g_return_if_fail (GNOME_IS_CANVAS (widget));
2183 g_return_if_fail (allocation != NULL);
2184
2185 GTK_WIDGET_CLASS (gnome_canvas_parent_class)->
2186 size_allocate (widget, allocation);
2187
2188 scrollable = GTK_SCROLLABLE (widget);
2189 hadjustment = gtk_scrollable_get_hadjustment (scrollable);
2190 vadjustment = gtk_scrollable_get_vadjustment (scrollable);
2191
2192 /* Recenter the view, if appropriate */
2193
2194 g_object_freeze_notify (G_OBJECT (hadjustment));
2195 g_object_freeze_notify (G_OBJECT (vadjustment));
2196
2197 gtk_adjustment_set_page_size (hadjustment, allocation->width);
2198 gtk_adjustment_set_page_increment (hadjustment, allocation->width / 2);
2199
2200 gtk_adjustment_set_page_size (vadjustment, allocation->height);
2201 gtk_adjustment_set_page_increment (vadjustment, allocation->height / 2);
2202
2203 scroll_to (
2204 GNOME_CANVAS (widget),
2205 gtk_adjustment_get_value (hadjustment),
2206 gtk_adjustment_get_value (vadjustment));
2207
2208 g_object_thaw_notify (G_OBJECT (hadjustment));
2209 g_object_thaw_notify (G_OBJECT (vadjustment));
2210 }
2211
2212 static gboolean
2213 gnome_canvas_draw (GtkWidget *widget,
2214 cairo_t *cr)
2215 {
2216 GnomeCanvas *canvas = GNOME_CANVAS (widget);
2217 cairo_rectangle_int_t rect;
2218 GtkLayout *layout;
2219 GtkAdjustment *hadjustment;
2220 GtkAdjustment *vadjustment;
2221 gdouble hadjustment_value;
2222 gdouble vadjustment_value;
2223
2224 layout = GTK_LAYOUT (canvas);
2225 hadjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (layout));
2226 vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (layout));
2227
2228 hadjustment_value = gtk_adjustment_get_value (hadjustment);
2229 vadjustment_value = gtk_adjustment_get_value (vadjustment);
2230
2231 gdk_cairo_get_clip_rectangle (cr, &rect);
2232
2233 if (canvas->need_update) {
2234 cairo_matrix_t w2c;
2235
2236 /* We start updating root with w2c matrix */
2237 gnome_canvas_w2c_matrix (canvas, &w2c);
2238
2239 gnome_canvas_item_invoke_update (canvas->root, &w2c, 0);
2240
2241 canvas->need_update = FALSE;
2242 }
2243
2244 cairo_save (cr);
2245 cairo_translate (
2246 cr,
2247 -canvas->zoom_xofs + rect.x,
2248 -canvas->zoom_yofs + rect.y);
2249
2250 rect.x += hadjustment_value;
2251 rect.y += vadjustment_value;
2252
2253 /* No pending updates, draw exposed area immediately */
2254 gnome_canvas_paint_rect (
2255 canvas, cr,
2256 rect.x, rect.y,
2257 rect.x + rect.width,
2258 rect.y + rect.height);
2259 cairo_restore (cr);
2260
2261 /* And call expose on parent container class */
2262 GTK_WIDGET_CLASS (gnome_canvas_parent_class)->draw (widget, cr);
2263
2264 return FALSE;
2265 }
2266
2267 /* Emits an event for an item in the canvas, be it the current item, grabbed
2268 * item, or focused item, as appropriate.
2269 */
2270
2271 static gint
2272 emit_event (GnomeCanvas *canvas,
2273 GdkEvent *event)
2274 {
2275 GdkEvent *ev;
2276 gint finished;
2277 GnomeCanvasItem *item;
2278 GnomeCanvasItem *parent;
2279 guint mask;
2280
2281 /* Perform checks for grabbed items */
2282
2283 if (canvas->grabbed_item &&
2284 !is_descendant (canvas->current_item, canvas->grabbed_item)) {
2285 /* I think this warning is annoying and I don't know what it's for
2286 * so I'll disable it for now.
2287 */
2288 /* g_warning ("emit_event() returning FALSE!\n");*/
2289 return FALSE;
2290 }
2291
2292 if (canvas->grabbed_item) {
2293 switch (event->type) {
2294 case GDK_ENTER_NOTIFY:
2295 mask = GDK_ENTER_NOTIFY_MASK;
2296 break;
2297
2298 case GDK_LEAVE_NOTIFY:
2299 mask = GDK_LEAVE_NOTIFY_MASK;
2300 break;
2301
2302 case GDK_MOTION_NOTIFY:
2303 mask = GDK_POINTER_MOTION_MASK;
2304 break;
2305
2306 case GDK_BUTTON_PRESS:
2307 case GDK_2BUTTON_PRESS:
2308 case GDK_3BUTTON_PRESS:
2309 mask = GDK_BUTTON_PRESS_MASK;
2310 break;
2311
2312 case GDK_BUTTON_RELEASE:
2313 mask = GDK_BUTTON_RELEASE_MASK;
2314 break;
2315
2316 case GDK_KEY_PRESS:
2317 mask = GDK_KEY_PRESS_MASK;
2318 break;
2319
2320 case GDK_KEY_RELEASE:
2321 mask = GDK_KEY_RELEASE_MASK;
2322 break;
2323
2324 case GDK_SCROLL:
2325 mask = GDK_SCROLL_MASK;
2326 break;
2327
2328 default:
2329 mask = 0;
2330 break;
2331 }
2332
2333 if (!(mask & canvas->grabbed_event_mask))
2334 return FALSE;
2335 }
2336
2337 /* Convert to world coordinates -- we have two cases because of diferent
2338 * offsets of the fields in the event structures.
2339 */
2340
2341 ev = gdk_event_copy (event);
2342
2343 switch (ev->type)
2344 {
2345 case GDK_ENTER_NOTIFY:
2346 case GDK_LEAVE_NOTIFY:
2347 gnome_canvas_window_to_world (
2348 canvas,
2349 ev->crossing.x, ev->crossing.y,
2350 &ev->crossing.x, &ev->crossing.y);
2351 break;
2352
2353 case GDK_MOTION_NOTIFY:
2354 case GDK_BUTTON_PRESS:
2355 case GDK_2BUTTON_PRESS:
2356 case GDK_3BUTTON_PRESS:
2357 case GDK_BUTTON_RELEASE:
2358 gnome_canvas_window_to_world (
2359 canvas,
2360 ev->motion.x, ev->motion.y,
2361 &ev->motion.x, &ev->motion.y);
2362 break;
2363
2364 default:
2365 break;
2366 }
2367
2368 /* Choose where we send the event */
2369
2370 item = canvas->current_item;
2371
2372 if (canvas->focused_item
2373 && ((event->type == GDK_KEY_PRESS) ||
2374 (event->type == GDK_KEY_RELEASE) ||
2375 (event->type == GDK_FOCUS_CHANGE)))
2376 item = canvas->focused_item;
2377
2378 /* The event is propagated up the hierarchy (for if someone connected to
2379 * a group instead of a leaf event), and emission is stopped if a
2380 * handler returns TRUE, just like for GtkWidget events.
2381 */
2382
2383 finished = FALSE;
2384
2385 while (item && !finished) {
2386 g_object_ref (item);
2387
2388 g_signal_emit (
2389 item, item_signals[ITEM_EVENT], 0,
2390 ev, &finished);
2391
2392 parent = item->parent;
2393 g_object_unref (item);
2394
2395 item = parent;
2396 }
2397
2398 gdk_event_free (ev);
2399
2400 return finished;
2401 }
2402
2403 /* Re-picks the current item in the canvas, based on the event's coordinates.
2404 * Also emits enter/leave events for items as appropriate.
2405 */
2406 static gint
2407 pick_current_item (GnomeCanvas *canvas,
2408 GdkEvent *event)
2409 {
2410 gint button_down;
2411 gdouble x, y;
2412 gint cx, cy;
2413 gint retval;
2414
2415 retval = FALSE;
2416
2417 /* If a button is down, we'll perform enter and leave events on the
2418 * current item, but not enter on any other item. This is more or less
2419 * like X pointer grabbing for canvas items.
2420 */
2421 button_down = canvas->state & (GDK_BUTTON1_MASK
2422 | GDK_BUTTON2_MASK
2423 | GDK_BUTTON3_MASK
2424 | GDK_BUTTON4_MASK
2425 | GDK_BUTTON5_MASK);
2426 if (!button_down)
2427 canvas->left_grabbed_item = FALSE;
2428
2429 /* Save the event in the canvas. This is used to synthesize enter and
2430 * leave events in case the current item changes. It is also used to
2431 * re-pick the current item if the current one gets deleted. Also,
2432 * synthesize an enter event.
2433 */
2434 if (event != &canvas->pick_event) {
2435 if ((event->type == GDK_MOTION_NOTIFY) ||
2436 (event->type == GDK_BUTTON_RELEASE)) {
2437 /* these fields have the same offsets in both types of events */
2438
2439 canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
2440 canvas->pick_event.crossing.window = event->motion.window;
2441 canvas->pick_event.crossing.send_event = event->motion.send_event;
2442 canvas->pick_event.crossing.subwindow = NULL;
2443 canvas->pick_event.crossing.x = event->motion.x;
2444 canvas->pick_event.crossing.y = event->motion.y;
2445 canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
2446 canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
2447 canvas->pick_event.crossing.focus = FALSE;
2448 canvas->pick_event.crossing.state = event->motion.state;
2449
2450 /* these fields don't have the same offsets in both types of events */
2451
2452 if (event->type == GDK_MOTION_NOTIFY) {
2453 canvas->pick_event.crossing.x_root = event->motion.x_root;
2454 canvas->pick_event.crossing.y_root = event->motion.y_root;
2455 } else {
2456 canvas->pick_event.crossing.x_root = event->button.x_root;
2457 canvas->pick_event.crossing.y_root = event->button.y_root;
2458 }
2459 } else
2460 canvas->pick_event = *event;
2461 }
2462
2463 /* Don't do anything else if this is a recursive call */
2464
2465 if (canvas->in_repick)
2466 return retval;
2467
2468 /* LeaveNotify means that there is no current item, so we don't look for one */
2469
2470 if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
2471 /* these fields don't have the same offsets in both types of events */
2472
2473 if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
2474 x = canvas->pick_event.crossing.x - canvas->zoom_xofs;
2475 y = canvas->pick_event.crossing.y - canvas->zoom_yofs;
2476 } else {
2477 x = canvas->pick_event.motion.x - canvas->zoom_xofs;
2478 y = canvas->pick_event.motion.y - canvas->zoom_yofs;
2479 }
2480
2481 /* canvas pixel coords */
2482
2483 cx = (gint) (x + 0.5);
2484 cy = (gint) (y + 0.5);
2485
2486 /* world coords */
2487
2488 x = canvas->scroll_x1 + x;
2489 y = canvas->scroll_y1 + y;
2490
2491 /* find the closest item */
2492
2493 if (canvas->root->flags & GNOME_CANVAS_ITEM_VISIBLE)
2494 canvas->new_current_item =
2495 gnome_canvas_item_invoke_point (
2496 canvas->root, x, y, cx, cy);
2497 else
2498 canvas->new_current_item = NULL;
2499 } else
2500 canvas->new_current_item = NULL;
2501
2502 if ((canvas->new_current_item == canvas->current_item)
2503 && !canvas->left_grabbed_item)
2504 return retval; /* current item did not change */
2505
2506 /* Synthesize events for old and new current items */
2507
2508 if ((canvas->new_current_item != canvas->current_item)
2509 && (canvas->current_item != NULL)
2510 && !canvas->left_grabbed_item) {
2511 GdkEvent new_event;
2512
2513 new_event = canvas->pick_event;
2514 new_event.type = GDK_LEAVE_NOTIFY;
2515
2516 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
2517 new_event.crossing.subwindow = NULL;
2518 canvas->in_repick = TRUE;
2519 retval = emit_event (canvas, &new_event);
2520 canvas->in_repick = FALSE;
2521 }
2522
2523 /* new_current_item may have been set to NULL during the
2524 * call to emit_event() above */
2525
2526 if ((canvas->new_current_item != canvas->current_item) && button_down) {
2527 canvas->left_grabbed_item = TRUE;
2528 return retval;
2529 }
2530
2531 /* Handle the rest of cases */
2532
2533 canvas->left_grabbed_item = FALSE;
2534 canvas->current_item = canvas->new_current_item;
2535
2536 if (canvas->current_item != NULL) {
2537 GdkEvent new_event;
2538
2539 new_event = canvas->pick_event;
2540 new_event.type = GDK_ENTER_NOTIFY;
2541 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
2542 new_event.crossing.subwindow = NULL;
2543 retval = emit_event (canvas, &new_event);
2544 }
2545
2546 return retval;
2547 }
2548
2549 /* Button event handler for the canvas */
2550 static gint
2551 gnome_canvas_button (GtkWidget *widget,
2552 GdkEventButton *event)
2553 {
2554 GnomeCanvas *canvas;
2555 GtkLayout *layout;
2556 GdkWindow *bin_window;
2557 gint mask;
2558 gint retval;
2559
2560 g_return_val_if_fail (GNOME_IS_CANVAS (widget), FALSE);
2561 g_return_val_if_fail (event != NULL, FALSE);
2562
2563 retval = FALSE;
2564
2565 canvas = GNOME_CANVAS (widget);
2566
2567 layout = GTK_LAYOUT (canvas);
2568 bin_window = gtk_layout_get_bin_window (layout);
2569
2570 /*
2571 * dispatch normally regardless of the event's window if an item has
2572 * has a pointer grab in effect
2573 */
2574 if (!canvas->grabbed_item && event->window != bin_window)
2575 return retval;
2576
2577 switch (event->button) {
2578 case 1:
2579 mask = GDK_BUTTON1_MASK;
2580 break;
2581 case 2:
2582 mask = GDK_BUTTON2_MASK;
2583 break;
2584 case 3:
2585 mask = GDK_BUTTON3_MASK;
2586 break;
2587 case 4:
2588 mask = GDK_BUTTON4_MASK;
2589 break;
2590 case 5:
2591 mask = GDK_BUTTON5_MASK;
2592 break;
2593 default:
2594 mask = 0;
2595 }
2596
2597 switch (event->type) {
2598 case GDK_BUTTON_PRESS:
2599 case GDK_2BUTTON_PRESS:
2600 case GDK_3BUTTON_PRESS:
2601 /* Pick the current item as if the button were not pressed, and
2602 * then process the event.
2603 */
2604 canvas->state = event->state;
2605 pick_current_item (canvas, (GdkEvent *) event);
2606 canvas->state ^= mask;
2607 retval = emit_event (canvas, (GdkEvent *) event);
2608 break;
2609
2610 case GDK_BUTTON_RELEASE:
2611 /* Process the event as if the button were pressed, then repick
2612 * after the button has been released
2613 */
2614 canvas->state = event->state;
2615 retval = emit_event (canvas, (GdkEvent *) event);
2616 event->state ^= mask;
2617 canvas->state = event->state;
2618 pick_current_item (canvas, (GdkEvent *) event);
2619 event->state ^= mask;
2620 break;
2621
2622 default:
2623 g_warn_if_reached ();
2624 }
2625
2626 return retval;
2627 }
2628
2629 /* Motion event handler for the canvas */
2630 static gint
2631 gnome_canvas_motion (GtkWidget *widget,
2632 GdkEventMotion *event)
2633 {
2634 GnomeCanvas *canvas;
2635 GtkLayout *layout;
2636 GdkWindow *bin_window;
2637
2638 g_return_val_if_fail (GNOME_IS_CANVAS (widget), FALSE);
2639 g_return_val_if_fail (event != NULL, FALSE);
2640
2641 canvas = GNOME_CANVAS (widget);
2642
2643 layout = GTK_LAYOUT (widget);
2644 bin_window = gtk_layout_get_bin_window (layout);
2645
2646 if (event->window != bin_window)
2647 return FALSE;
2648
2649 canvas->state = event->state;
2650 pick_current_item (canvas, (GdkEvent *) event);
2651 return emit_event (canvas, (GdkEvent *) event);
2652 }
2653
2654 /* Key event handler for the canvas */
2655 static gboolean
2656 gnome_canvas_key (GtkWidget *widget,
2657 GdkEventKey *event)
2658 {
2659 GnomeCanvas *canvas;
2660
2661 g_return_val_if_fail (GNOME_IS_CANVAS (widget), FALSE);
2662 g_return_val_if_fail (event != NULL, FALSE);
2663
2664 canvas = GNOME_CANVAS (widget);
2665
2666 if (!emit_event (canvas, (GdkEvent *) event)) {
2667 GtkWidgetClass *widget_class;
2668
2669 widget_class = GTK_WIDGET_CLASS (gnome_canvas_parent_class);
2670
2671 if (event->type == GDK_KEY_PRESS) {
2672 if (widget_class->key_press_event)
2673 return (* widget_class->key_press_event) (widget, event);
2674 } else if (event->type == GDK_KEY_RELEASE) {
2675 if (widget_class->key_release_event)
2676 return (* widget_class->key_release_event) (widget, event);
2677 } else
2678 g_warn_if_reached ();
2679
2680 return FALSE;
2681 } else
2682 return TRUE;
2683 }
2684
2685 /* Crossing event handler for the canvas */
2686 static gint
2687 gnome_canvas_crossing (GtkWidget *widget,
2688 GdkEventCrossing *event)
2689 {
2690 GnomeCanvas *canvas;
2691 GtkLayout *layout;
2692 GdkWindow *bin_window;
2693
2694 g_return_val_if_fail (GNOME_IS_CANVAS (widget), FALSE);
2695 g_return_val_if_fail (event != NULL, FALSE);
2696
2697 canvas = GNOME_CANVAS (widget);
2698
2699 layout = GTK_LAYOUT (canvas);
2700 bin_window = gtk_layout_get_bin_window (layout);
2701
2702 if (event->window != bin_window)
2703 return FALSE;
2704
2705 /* XXX Detect and disregard synthesized crossing events generated
2706 * by synth_crossing() in gtkwidget.c. The pointer coordinates
2707 * are invalid and pick_current_item() relies on them. */
2708 if (event->x == 0 && event->y == 0 &&
2709 event->x_root == 0 && event->y_root == 0)
2710 return FALSE;
2711
2712 canvas->state = event->state;
2713 return pick_current_item (canvas, (GdkEvent *) event);
2714 }
2715
2716 /* Focus in handler for the canvas */
2717 static gint
2718 gnome_canvas_focus_in (GtkWidget *widget,
2719 GdkEventFocus *event)
2720 {
2721 GnomeCanvas *canvas;
2722
2723 /* XXX Can't access flags directly anymore, but is it really needed?
2724 * If so, could we call gtk_widget_send_focus_change() instead? */
2725 #if 0
2726 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
2727 #endif
2728
2729 canvas = GNOME_CANVAS (widget);
2730
2731 if (canvas->focused_item)
2732 return emit_event (canvas, (GdkEvent *) event);
2733 else
2734 return FALSE;
2735 }
2736
2737 /* Focus out handler for the canvas */
2738 static gint
2739 gnome_canvas_focus_out (GtkWidget *widget,
2740 GdkEventFocus *event)
2741 {
2742 GnomeCanvas *canvas;
2743
2744 /* XXX Can't access flags directly anymore, but is it really needed?
2745 * If so, could we call gtk_widget_send_focus_change() instead? */
2746 #if 0
2747 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2748 #endif
2749
2750 canvas = GNOME_CANVAS (widget);
2751
2752 if (canvas->focused_item)
2753 return emit_event (canvas, (GdkEvent *) event);
2754 else
2755 return FALSE;
2756 }
2757
2758 static void
2759 gnome_canvas_draw_background (GnomeCanvas *canvas,
2760 cairo_t *cr,
2761 gint x,
2762 gint y,
2763 gint width,
2764 gint height)
2765 {
2766 GtkStyle *style;
2767
2768 style = gtk_widget_get_style (GTK_WIDGET (canvas));
2769
2770 cairo_save (cr);
2771 gdk_cairo_set_source_color (cr, &style->bg[GTK_STATE_NORMAL]);
2772 cairo_paint (cr);
2773 cairo_restore (cr);
2774 }
2775
2776 static void
2777 do_update (GnomeCanvas *canvas)
2778 {
2779 /* Cause the update if necessary */
2780
2781 update_again:
2782 if (canvas->need_update) {
2783 cairo_matrix_t w2c;
2784
2785 /* We start updating root with w2c matrix */
2786 gnome_canvas_w2c_matrix (canvas, &w2c);
2787
2788 gnome_canvas_item_invoke_update (canvas->root, &w2c, 0);
2789
2790 canvas->need_update = FALSE;
2791 }
2792
2793 /* Pick new current item */
2794
2795 while (canvas->need_repick) {
2796 canvas->need_repick = FALSE;
2797 pick_current_item (canvas, &canvas->pick_event);
2798 }
2799
2800 /* it is possible that during picking we emitted an event in which
2801 * the user then called some function which then requested update
2802 * of something. Without this we'd be left in a state where
2803 * need_update would have been left TRUE and the canvas would have
2804 * been left unpainted. */
2805 if (canvas->need_update) {
2806 goto update_again;
2807 }
2808 }
2809
2810 /* Idle handler for the canvas. It deals with pending updates and redraws. */
2811 static gboolean
2812 idle_handler (gpointer data)
2813 {
2814 GnomeCanvas *canvas;
2815
2816 canvas = GNOME_CANVAS (data);
2817
2818 do_update (canvas);
2819
2820 /* Reset idle id */
2821 canvas->idle_id = 0;
2822
2823 return FALSE;
2824 }
2825
2826 /* Convenience function to add an idle handler to a canvas */
2827 static void
2828 add_idle (GnomeCanvas *canvas)
2829 {
2830 g_return_if_fail (canvas->need_update);
2831
2832 if (!canvas->idle_id)
2833 canvas->idle_id = g_idle_add_full (
2834 CANVAS_IDLE_PRIORITY,
2835 idle_handler,
2836 canvas,
2837 NULL);
2838
2839 /* canvas->idle_id = gtk_idle_add (idle_handler, canvas); */
2840 }
2841
2842 /**
2843 * gnome_canvas_root:
2844 * @canvas: A canvas.
2845 *
2846 * Queries the root group of a canvas.
2847 *
2848 * Return value: The root group of the specified canvas.
2849 **/
2850 GnomeCanvasGroup *
2851 gnome_canvas_root (GnomeCanvas *canvas)
2852 {
2853 g_return_val_if_fail (GNOME_IS_CANVAS (canvas), NULL);
2854
2855 return GNOME_CANVAS_GROUP (canvas->root);
2856 }
2857
2858 /**
2859 * gnome_canvas_set_scroll_region:
2860 * @canvas: A canvas.
2861 * @x1: Leftmost limit of the scrolling region.
2862 * @y1: Upper limit of the scrolling region.
2863 * @x2: Rightmost limit of the scrolling region.
2864 * @y2: Lower limit of the scrolling region.
2865 *
2866 * Sets the scrolling region of a canvas to the specified rectangle. The canvas
2867 * will then be able to scroll only within this region. The view of the canvas
2868 * is adjusted as appropriate to display as much of the new region as possible.
2869 **/
2870 void
2871 gnome_canvas_set_scroll_region (GnomeCanvas *canvas,
2872 gdouble x1,
2873 gdouble y1,
2874 gdouble x2,
2875 gdouble y2)
2876 {
2877 GtkScrollable *scrollable;
2878 GtkAdjustment *hadjustment;
2879 GtkAdjustment *vadjustment;
2880 gdouble hadjustment_value;
2881 gdouble vadjustment_value;
2882 gdouble wxofs, wyofs;
2883 gint xofs, yofs;
2884
2885 g_return_if_fail (GNOME_IS_CANVAS (canvas));
2886
2887 scrollable = GTK_SCROLLABLE (canvas);
2888 hadjustment = gtk_scrollable_get_hadjustment (scrollable);
2889 vadjustment = gtk_scrollable_get_vadjustment (scrollable);
2890
2891 hadjustment_value = gtk_adjustment_get_value (hadjustment);
2892 vadjustment_value = gtk_adjustment_get_value (vadjustment);
2893
2894 /*
2895 * Set the new scrolling region. If possible, do not move the
2896 * visible contents of the canvas.
2897 */
2898
2899 gnome_canvas_c2w (
2900 canvas,
2901 hadjustment_value + canvas->zoom_xofs,
2902 vadjustment_value + canvas->zoom_yofs,
2903 &wxofs, &wyofs);
2904
2905 canvas->scroll_x1 = x1;
2906 canvas->scroll_y1 = y1;
2907 canvas->scroll_x2 = x2;
2908 canvas->scroll_y2 = y2;
2909
2910 gnome_canvas_w2c (canvas, wxofs, wyofs, &xofs, &yofs);
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
2911
2912 scroll_to (canvas, xofs, yofs);
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
2913
2914 canvas->need_repick = TRUE;
2915 #if 0
2916 /* todo: should be requesting update */
2917 (* GNOME_CANVAS_ITEM_CLASS (canvas->root->object.class)->update) (
2918 canvas->root, NULL, NULL, 0);
2919 #endif
2920 }
2921
2922 /**
2923 * gnome_canvas_get_scroll_region:
2924 * @canvas: A canvas.
2925 * @x1: Leftmost limit of the scrolling region (return value).
2926 * @y1: Upper limit of the scrolling region (return value).
2927 * @x2: Rightmost limit of the scrolling region (return value).
2928 * @y2: Lower limit of the scrolling region (return value).
2929 *
2930 * Queries the scrolling region of a canvas.
2931 **/
2932 void
2933 gnome_canvas_get_scroll_region (GnomeCanvas *canvas,
2934 gdouble *x1,
2935 gdouble *y1,
2936 gdouble *x2,
2937 gdouble *y2)
2938 {
2939 g_return_if_fail (GNOME_IS_CANVAS (canvas));
2940
2941 if (x1)
2942 *x1 = canvas->scroll_x1;
2943
2944 if (y1)
2945 *y1 = canvas->scroll_y1;
2946
2947 if (x2)
2948 *x2 = canvas->scroll_x2;
2949
2950 if (y2)
2951 *y2 = canvas->scroll_y2;
2952 }
2953
2954 /**
2955 * gnome_canvas_scroll_to:
2956 * @canvas: A canvas.
2957 * @cx: Horizontal scrolling offset in canvas pixel units.
2958 * @cy: Vertical scrolling offset in canvas pixel units.
2959 *
2960 * Makes a canvas scroll to the specified offsets, given in canvas pixel units.
2961 * The canvas will adjust the view so that it is not outside the scrolling
2962 * region. This function is typically not used, as it is better to hook
2963 * scrollbars to the canvas layout's scrolling adjusments.
2964 **/
2965 void
2966 gnome_canvas_scroll_to (GnomeCanvas *canvas,
2967 gint cx,
2968 gint cy)
2969 {
2970 g_return_if_fail (GNOME_IS_CANVAS (canvas));
2971
2972 scroll_to (canvas, cx, cy);
2973 }
2974
2975 /**
2976 * gnome_canvas_get_scroll_offsets:
2977 * @canvas: A canvas.
2978 * @cx: Horizontal scrolling offset (return value).
2979 * @cy: Vertical scrolling offset (return value).
2980 *
2981 * Queries the scrolling offsets of a canvas. The values are returned in canvas
2982 * pixel units.
2983 **/
2984 void
2985 gnome_canvas_get_scroll_offsets (GnomeCanvas *canvas,
2986 gint *cx,
2987 gint *cy)
2988 {
2989 GtkAdjustment *adjustment;
2990 GtkScrollable *scrollable;
2991
2992 g_return_if_fail (GNOME_IS_CANVAS (canvas));
2993
2994 scrollable = GTK_SCROLLABLE (canvas);
2995
2996 if (cx) {
2997 adjustment = gtk_scrollable_get_hadjustment (scrollable);
2998 *cx = (gint) gtk_adjustment_get_value (adjustment);
2999 }
3000
3001 if (cy) {
3002 adjustment = gtk_scrollable_get_vadjustment (scrollable);
3003 *cy = (gint) gtk_adjustment_get_value (adjustment);
3004 }
3005 }
3006
3007 /**
3008 * gnome_canvas_get_item_at:
3009 * @canvas: A canvas.
3010 * @x: X position in world coordinates.
3011 * @y: Y position in world coordinates.
3012 *
3013 * Looks for the item that is under the specified position, which must be
3014 * specified in world coordinates.
3015 *
3016 * Return value: The sought item, or NULL if no item is at the specified
3017 * coordinates.
3018 **/
3019 GnomeCanvasItem *
3020 gnome_canvas_get_item_at (GnomeCanvas *canvas,
3021 gdouble x,
3022 gdouble y)
3023 {
3024 gint cx, cy;
3025
3026 g_return_val_if_fail (GNOME_IS_CANVAS (canvas), NULL);
3027
3028 gnome_canvas_w2c (canvas, x, y, &cx, &cy);
3029
3030 return gnome_canvas_item_invoke_point (canvas->root, x, y, cx, cy);
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
3031 }
3032
3033 /* Queues an update of the canvas */
3034 static void
3035 gnome_canvas_request_update (GnomeCanvas *canvas)
3036 {
3037 GNOME_CANVAS_GET_CLASS (canvas)->request_update (canvas);
3038 }
3039
3040 static void
3041 gnome_canvas_request_update_real (GnomeCanvas *canvas)
3042 {
3043 if (canvas->need_update)
3044 return;
3045
3046 canvas->need_update = TRUE;
3047 if (gtk_widget_get_mapped ((GtkWidget *) canvas))
3048 add_idle (canvas);
3049 }
3050
3051 static inline void
3052 get_visible_rect (GnomeCanvas *canvas,
3053 GdkRectangle *visible)
3054 {
3055 GtkAllocation allocation;
3056 GtkScrollable *scrollable;
3057 GtkAdjustment *hadjustment;
3058 GtkAdjustment *vadjustment;
3059 gdouble hadjustment_value;
3060 gdouble vadjustment_value;
3061
3062 gtk_widget_get_allocation (GTK_WIDGET (canvas), &allocation);
3063
3064 scrollable = GTK_SCROLLABLE (canvas);
3065 hadjustment = gtk_scrollable_get_hadjustment (scrollable);
3066 vadjustment = gtk_scrollable_get_vadjustment (scrollable);
3067
3068 hadjustment_value = gtk_adjustment_get_value (hadjustment);
3069 vadjustment_value = gtk_adjustment_get_value (vadjustment);
3070
3071 visible->x = hadjustment_value - canvas->zoom_xofs;
3072 visible->y = vadjustment_value - canvas->zoom_yofs;
3073 visible->width = allocation.width;
3074 visible->height = allocation.height;
3075 }
3076
3077 /**
3078 * gnome_canvas_request_redraw:
3079 * @canvas: A canvas.
3080 * @x1: Leftmost coordinate of the rectangle to be redrawn.
3081 * @y1: Upper coordinate of the rectangle to be redrawn.
3082 * @x2: Rightmost coordinate of the rectangle to be redrawn, plus 1.
3083 * @y2: Lower coordinate of the rectangle to be redrawn, plus 1.
3084 *
3085 * Convenience function that informs a canvas that the specified rectangle needs
3086 * to be repainted. The rectangle includes @x1 and @y1, but not @x2 and @y2. To
3087 * be used only by item implementations.
3088 **/
3089 void
3090 gnome_canvas_request_redraw (GnomeCanvas *canvas,
3091 gint x1,
3092 gint y1,
3093 gint x2,
3094 gint y2)
3095 {
3096 GdkRectangle area, clip;
3097
3098 g_return_if_fail (GNOME_IS_CANVAS (canvas));
3099
3100 if (!gtk_widget_is_drawable (GTK_WIDGET (canvas)) || (x1 >= x2) || (y1 >= y2))
3101 return;
3102
3103 area.x = x1;
3104 area.y = y1;
3105 area.width = x2 - x1 + 1;
3106 area.height = y2 - y1 + 1;
3107
3108 get_visible_rect (canvas, &clip);
3109 if (!gdk_rectangle_intersect (&area, &clip, &area))
3110 return;
3111
3112 gdk_window_invalidate_rect (
3113 gtk_layout_get_bin_window (GTK_LAYOUT (canvas)),
3114 &area, FALSE);
3115 }
3116
3117 /**
3118 * gnome_canvas_w2c_matrix:
3119 * @canvas: A canvas.
3120 * @matrix: (out): matrix to initialize
3121 *
3122 * Gets the transformtion matrix that converts from world coordinates to canvas
3123 * pixel coordinates.
3124 **/
3125 void
3126 gnome_canvas_w2c_matrix (GnomeCanvas *canvas,
3127 cairo_matrix_t *matrix)
3128 {
3129 g_return_if_fail (GNOME_IS_CANVAS (canvas));
3130 g_return_if_fail (matrix != NULL);
3131
3132 cairo_matrix_init_translate (
3133 matrix, -canvas->scroll_x1, -canvas->scroll_y1);
3134 }
3135
3136 /**
3137 * gnome_canvas_c2w_matrix:
3138 * @canvas: A canvas.
3139 * @matrix: (out): matrix to initialize
3140 *
3141 * Gets the transformtion matrix that converts from canvas pixel coordinates to
3142 * world coordinates.
3143 **/
3144 void
3145 gnome_canvas_c2w_matrix (GnomeCanvas *canvas,
3146 cairo_matrix_t *matrix)
3147 {
3148 g_return_if_fail (GNOME_IS_CANVAS (canvas));
3149 g_return_if_fail (matrix != NULL);
3150
3151 cairo_matrix_init_translate (
3152 matrix, canvas->scroll_x1, canvas->scroll_y1);
3153 }
3154
3155 /**
3156 * gnome_canvas_w2c:
3157 * @canvas: A canvas.
3158 * @wx: World X coordinate.
3159 * @wy: World Y coordinate.
3160 * @cx: X pixel coordinate (return value).
3161 * @cy: Y pixel coordinate (return value).
3162 *
3163 * Converts world coordinates into canvas pixel coordinates.
3164 **/
3165 void
3166 gnome_canvas_w2c (GnomeCanvas *canvas,
3167 gdouble wx,
3168 gdouble wy,
3169 gint *cx,
3170 gint *cy)
3171 {
3172 cairo_matrix_t w2c;
3173
3174 g_return_if_fail (GNOME_IS_CANVAS (canvas));
3175
3176 gnome_canvas_w2c_matrix (canvas, &w2c);
3177 cairo_matrix_transform_point (&w2c, &wx, &wy);
3178
3179 if (cx)
3180 *cx = floor (wx + 0.5);
3181 if (cy)
3182 *cy = floor (wy + 0.5);
3183 }
3184
3185 /**
3186 * gnome_canvas_w2c_d:
3187 * @canvas: A canvas.
3188 * @wx: World X coordinate.
3189 * @wy: World Y coordinate.
3190 * @cx: X pixel coordinate (return value).
3191 * @cy: Y pixel coordinate (return value).
3192 *
3193 * Converts world coordinates into canvas pixel coordinates. This
3194 * version returns coordinates in floating point coordinates, for
3195 * greater precision.
3196 **/
3197 void
3198 gnome_canvas_w2c_d (GnomeCanvas *canvas,
3199 gdouble wx,
3200 gdouble wy,
3201 gdouble *cx,
3202 gdouble *cy)
3203 {
3204 cairo_matrix_t w2c;
3205
3206 g_return_if_fail (GNOME_IS_CANVAS (canvas));
3207
3208 gnome_canvas_w2c_matrix (canvas, &w2c);
3209 cairo_matrix_transform_point (&w2c, &wx, &wy);
3210
3211 if (cx)
3212 *cx = wx;
3213 if (cy)
3214 *cy = wy;
3215 }
3216
3217 /**
3218 * gnome_canvas_c2w:
3219 * @canvas: A canvas.
3220 * @cx: Canvas pixel X coordinate.
3221 * @cy: Canvas pixel Y coordinate.
3222 * @wx: X world coordinate (return value).
3223 * @wy: Y world coordinate (return value).
3224 *
3225 * Converts canvas pixel coordinates to world coordinates.
3226 **/
3227 void
3228 gnome_canvas_c2w (GnomeCanvas *canvas,
3229 gint cx,
3230 gint cy,
3231 gdouble *wx,
3232 gdouble *wy)
3233 {
3234 cairo_matrix_t c2w;
3235 gdouble x, y;
3236
3237 g_return_if_fail (GNOME_IS_CANVAS (canvas));
3238
3239 x = cx;
3240 y = cy;
3241 gnome_canvas_c2w_matrix (canvas, &c2w);
3242 cairo_matrix_transform_point (&c2w, &x, &y);
3243
3244 if (wx)
3245 *wx = x;
3246 if (wy)
3247 *wy = y;
3248 }
3249
3250 /**
3251 * gnome_canvas_window_to_world:
3252 * @canvas: A canvas.
3253 * @winx: Window-relative X coordinate.
3254 * @winy: Window-relative Y coordinate.
3255 * @worldx: X world coordinate (return value).
3256 * @worldy: Y world coordinate (return value).
3257 *
3258 * Converts window-relative coordinates into world coordinates. You can use
3259 * this when you need to convert mouse coordinates into world coordinates, for
3260 * example.
3261 **/
3262 void
3263 gnome_canvas_window_to_world (GnomeCanvas *canvas,
3264 gdouble winx,
3265 gdouble winy,
3266 gdouble *worldx,
3267 gdouble *worldy)
3268 {
3269 g_return_if_fail (GNOME_IS_CANVAS (canvas));
3270
3271 if (worldx)
3272 *worldx = canvas->scroll_x1 + (winx - canvas->zoom_xofs);
3273
3274 if (worldy)
3275 *worldy = canvas->scroll_y1 + (winy - canvas->zoom_yofs);
3276 }
3277
3278 /**
3279 * gnome_canvas_world_to_window:
3280 * @canvas: A canvas.
3281 * @worldx: World X coordinate.
3282 * @worldy: World Y coordinate.
3283 * @winx: X window-relative coordinate.
3284 * @winy: Y window-relative coordinate.
3285 *
3286 * Converts world coordinates into window-relative coordinates.
3287 **/
3288 void
3289 gnome_canvas_world_to_window (GnomeCanvas *canvas,
3290 gdouble worldx,
3291 gdouble worldy,
3292 gdouble *winx,
3293 gdouble *winy)
3294 {
3295 g_return_if_fail (GNOME_IS_CANVAS (canvas));
3296
3297 if (winx)
3298 *winx = (worldx - canvas->scroll_x1) + canvas->zoom_xofs;
3299
3300 if (winy)
3301 *winy = (worldy - canvas->scroll_y1) + canvas->zoom_yofs;
3302 }
3303
3304 static gboolean
3305 boolean_handled_accumulator (GSignalInvocationHint *ihint,
3306 GValue *return_accu,
3307 const GValue *handler_return,
3308 gpointer dummy)
3309 {
3310 gboolean continue_emission;
3311 gboolean signal_handled;
3312
3313 signal_handled = g_value_get_boolean (handler_return);
3314 g_value_set_boolean (return_accu, signal_handled);
3315 continue_emission = !signal_handled;
3316
3317 return continue_emission;
3318 }
3319
3320 /* Class initialization function for GnomeCanvasItemClass */
3321 static void
3322 gnome_canvas_item_class_init (GnomeCanvasItemClass *class)
3323 {
3324 GObjectClass *gobject_class;
3325
3326 gobject_class = (GObjectClass *) class;
3327
3328 gobject_class->set_property = gnome_canvas_item_set_property;
3329 gobject_class->get_property = gnome_canvas_item_get_property;
3330
3331 g_object_class_install_property (
3332 gobject_class,
3333 ITEM_PROP_PARENT,
3334 g_param_spec_object (
3335 "parent",
3336 NULL,
3337 NULL,
3338 GNOME_TYPE_CANVAS_ITEM,
3339 G_PARAM_READABLE |
3340 G_PARAM_WRITABLE));
3341
3342 item_signals[ITEM_EVENT] = g_signal_new (
3343 "event",
3344 G_TYPE_FROM_CLASS (class),
3345 G_SIGNAL_RUN_LAST,
3346 G_STRUCT_OFFSET (GnomeCanvasItemClass, event),
3347 boolean_handled_accumulator, NULL, NULL,
3348 G_TYPE_BOOLEAN, 1,
3349 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
3350
3351 gobject_class->dispose = gnome_canvas_item_dispose;
3352
3353 class->update = gnome_canvas_item_update;
3354 class->realize = gnome_canvas_item_realize;
3355 class->unrealize = gnome_canvas_item_unrealize;
3356 class->map = gnome_canvas_item_map;
3357 class->unmap = gnome_canvas_item_unmap;
3358 class->dispose = gnome_canvas_item_dispose_item;
3359 }