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 * EelCanvas widget - Tk-like canvas widget for Gnome
28 *
29 * EelCanvas is basically a port of the Tk toolkit's most excellent canvas widget. Tk is
30 * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
31 *
32 *
33 * Authors: Federico Mena <federico@nuclecu.unam.mx>
34 * Raph Levien <raph@gimp.org>
35 */
36
37 /*
38 * TO-DO list for the canvas:
39 *
40 * - Allow to specify whether EelCanvasImage sizes are in units or pixels (scale or don't scale).
41 *
42 * - Implement a flag for eel_canvas_item_reparent() that tells the function to keep the item
43 * visually in the same place, that is, to keep it in the same place with respect to the canvas
44 * origin.
45 *
46 * - GC put functions for items.
47 *
48 * - Widget item (finish it).
49 *
50 * - GList *eel_canvas_gimme_all_items_contained_in_this_area (EelCanvas *canvas, Rectangle area);
51 *
52 * - Retrofit all the primitive items with microtile support.
53 *
54 * - Curve support for line item.
55 *
56 * - Arc item (Havoc has it; to be integrated in EelCanvasEllipse).
57 *
58 * - Sane font handling API.
59 *
60 * - Get_arg methods for items:
61 * - How to fetch the outline width and know whether it is in pixels or units?
62 */
63
64 #include <config.h>
65
66 #include <math.h>
67 #include <string.h>
68 #include <stdio.h>
69 #include <gdk/gdkprivate.h>
70 #include <gtk/gtk.h>
71 #include <glib/gi18n-lib.h>
72 #include <cairo-gobject.h>
73 #include "eel-canvas.h"
74
75 static void eel_canvas_request_update (EelCanvas *canvas);
76 static void group_add (EelCanvasGroup *group,
77 EelCanvasItem *item);
78 static void group_remove (EelCanvasGroup *group,
79 EelCanvasItem *item);
80 static void redraw_and_repick_if_mapped (EelCanvasItem *item);
81
82 /*** EelCanvasItem ***/
83
84 /* Some convenience stuff */
85 #define GCI_UPDATE_MASK (EEL_CANVAS_UPDATE_REQUESTED | EEL_CANVAS_UPDATE_DEEP)
86 #define GCI_EPSILON 1e-18
87
88 enum {
89 ITEM_PROP_0,
90 ITEM_PROP_PARENT,
91 ITEM_PROP_VISIBLE
92 };
93
94 enum {
95 ITEM_DESTROY,
96 ITEM_EVENT,
97 ITEM_LAST_SIGNAL
98 };
99
100 static void eel_canvas_item_class_init (EelCanvasItemClass *klass);
101 static void eel_canvas_item_init (EelCanvasItem *item);
102 static int emit_event (EelCanvas *canvas, GdkEvent *event);
103
104 static guint item_signals[ITEM_LAST_SIGNAL];
105
106 static GObjectClass *item_parent_class;
107
108 static gpointer accessible_item_parent_class;
109 static gpointer accessible_parent_class;
110
111
112 /**
113 * eel_canvas_item_get_type:
114 *
115 * Registers the &EelCanvasItem class if necessary, and returns the type ID
116 * associated to it.
117 *
118 * Return value: The type ID of the &EelCanvasItem class.
119 **/
120 GType
121 eel_canvas_item_get_type (void)
122 {
123 static GType canvas_item_type = 0;
124
125 if (!canvas_item_type) {
126 static const GTypeInfo canvas_item_info = {
127 sizeof (EelCanvasItemClass),
128 (GBaseInitFunc) NULL,
129 (GBaseFinalizeFunc) NULL,
130 (GClassInitFunc) eel_canvas_item_class_init,
131 NULL, /* class_finalize */
132 NULL, /* class_data */
133 sizeof (EelCanvasItem),
134 0, /* n_preallocs */
135 (GInstanceInitFunc) eel_canvas_item_init
136 };
137
138 canvas_item_type = g_type_register_static (G_TYPE_INITIALLY_UNOWNED,
139 "EelCanvasItem",
140 &canvas_item_info,
141 0);
142 }
143
144 return canvas_item_type;
145 }
146
147 /* Object initialization function for EelCanvasItem */
148 static void
149 eel_canvas_item_init (EelCanvasItem *item)
150 {
151 item->flags |= EEL_CANVAS_ITEM_VISIBLE;
152 }
153
154 /**
155 * eel_canvas_item_new:
156 * @parent: The parent group for the new item.
157 * @type: The object type of the item.
158 * @first_arg_name: A list of object argument name/value pairs, NULL-terminated,
159 * used to configure the item. For example, "fill_color", "black",
160 * "width_units", 5.0, NULL.
161 * @Varargs:
162 *
163 * Creates a new canvas item with @parent as its parent group. The item is
164 * created at the top of its parent's stack, and starts up as visible. The item
165 * is of the specified @type, for example, it can be
166 * eel_canvas_rect_get_type(). The list of object arguments/value pairs is
167 * used to configure the item.
168 *
169 * Return value: The newly-created item.
170 **/
171 EelCanvasItem *
172 eel_canvas_item_new (EelCanvasGroup *parent, GType type, const gchar *first_arg_name, ...)
173 {
174 EelCanvasItem *item;
175 va_list args;
176
177 g_return_val_if_fail (EEL_IS_CANVAS_GROUP (parent), NULL);
178 g_return_val_if_fail (g_type_is_a (type, eel_canvas_item_get_type ()), NULL);
179
180 item = EEL_CANVAS_ITEM (g_object_new (type, NULL));
181
182 va_start (args, first_arg_name);
183 eel_canvas_item_construct (item, parent, first_arg_name, args);
184 va_end (args);
185
186 return item;
187 }
188
189
190 /* Performs post-creation operations on a canvas item (adding it to its parent
191 * group, etc.)
192 */
193 static void
194 item_post_create_setup (EelCanvasItem *item)
195 {
196 group_add (EEL_CANVAS_GROUP (item->parent), item);
197
198 redraw_and_repick_if_mapped (item);
199 }
200
201 /* Set_property handler for canvas items */
202 static void
203 eel_canvas_item_set_property (GObject *gobject, guint param_id,
204 const GValue *value, GParamSpec *pspec)
205 {
206 EelCanvasItem *item;
207
208 g_return_if_fail (EEL_IS_CANVAS_ITEM (gobject));
209
210 item = EEL_CANVAS_ITEM (gobject);
211
212 switch (param_id) {
213 case ITEM_PROP_PARENT:
214 if (item->parent != NULL) {
215 g_warning ("Cannot set `parent' argument after item has "
216 "already been constructed.");
217 } else if (g_value_get_object (value)) {
218 item->parent = EEL_CANVAS_ITEM (g_value_get_object (value));
219 item->canvas = item->parent->canvas;
220 item_post_create_setup (item);
221 }
222 break;
223 case ITEM_PROP_VISIBLE:
224 if (g_value_get_boolean (value)) {
225 eel_canvas_item_show (item);
226 } else {
227 eel_canvas_item_hide (item);
228 }
229 break;
230 default:
231 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
232 break;
233 }
234 }
235
236 /* Get_property handler for canvas items */
237 static void
238 eel_canvas_item_get_property (GObject *gobject, guint param_id,
239 GValue *value, GParamSpec *pspec)
240 {
241 EelCanvasItem *item;
242
243 g_return_if_fail (EEL_IS_CANVAS_ITEM (gobject));
244
245 item = EEL_CANVAS_ITEM (gobject);
246
247 switch (param_id) {
248 case ITEM_PROP_VISIBLE:
249 g_value_set_boolean (value, item->flags & EEL_CANVAS_ITEM_VISIBLE);
250 break;
251 default:
252 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
253 break;
254 }
255 }
256
257 /**
258 * eel_canvas_item_construct:
259 * @item: An unconstructed canvas item.
260 * @parent: The parent group for the item.
261 * @first_arg_name: The name of the first argument for configuring the item.
262 * @args: The list of arguments used to configure the item.
263 *
264 * Constructs a canvas item; meant for use only by item implementations.
265 **/
266 void
267 eel_canvas_item_construct (EelCanvasItem *item, EelCanvasGroup *parent,
268 const gchar *first_arg_name, va_list args)
269 {
270 g_return_if_fail (EEL_IS_CANVAS_GROUP (parent));
271 g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
272
273 item->parent = EEL_CANVAS_ITEM (parent);
274 item->canvas = item->parent->canvas;
275
276 g_object_set_valist (G_OBJECT (item), first_arg_name, args);
277
278 item_post_create_setup (item);
279 }
280
281
282 static void
283 redraw_and_repick_if_mapped (EelCanvasItem *item)
284 {
285 if (item->flags & EEL_CANVAS_ITEM_MAPPED) {
286 eel_canvas_item_request_redraw (item);
287 item->canvas->need_repick = TRUE;
288 }
289 }
290
291 /* Dispose handler for canvas items */
292 static void
293 eel_canvas_item_dispose (GObject *object)
294 {
295 EelCanvasItem *item;
296
297 g_return_if_fail (EEL_IS_CANVAS_ITEM (object));
298
299 item = EEL_CANVAS_ITEM (object);
300
301 if (item->canvas) {
302 eel_canvas_item_request_redraw (item);
303
304 /* Make the canvas forget about us */
305
306 if (item == item->canvas->current_item) {
307 item->canvas->current_item = NULL;
308 item->canvas->need_repick = TRUE;
309 }
310
311 if (item == item->canvas->new_current_item) {
312 item->canvas->new_current_item = NULL;
313 item->canvas->need_repick = TRUE;
314 }
315
316 eel_canvas_item_ungrab (item, GDK_CURRENT_TIME);
317
318 if (item == item->canvas->focused_item)
319 item->canvas->focused_item = NULL;
320
321 /* Normal destroy stuff */
322
323 if (item->flags & EEL_CANVAS_ITEM_MAPPED)
324 (* EEL_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
325
326 if (item->flags & EEL_CANVAS_ITEM_REALIZED)
327 (* EEL_CANVAS_ITEM_GET_CLASS (item)->unrealize) (item);
328
329 if (item->parent)
330 group_remove (EEL_CANVAS_GROUP (item->parent), item);
331
332 item->canvas = NULL;
333 }
334
335 g_object_set_data (object, "in-destruction", GINT_TO_POINTER (1));
336 g_signal_emit (object, item_signals[ITEM_DESTROY], 0);
337
338 g_object_set_data (object, "in-destruction", NULL);
339
340 G_OBJECT_CLASS (item_parent_class)->dispose (object);
341 }
342
343 void
344 eel_canvas_item_destroy (EelCanvasItem *item)
345 {
346 if (g_object_get_data (G_OBJECT (item), "in-destruction") == NULL) {
347 g_object_run_dispose (G_OBJECT (item));
348 }
349 }
350
351 /* Realize handler for canvas items */
352 static void
353 eel_canvas_item_realize (EelCanvasItem *item)
354 {
355 if (item->parent && !(item->parent->flags & EEL_CANVAS_ITEM_REALIZED))
356 (* EEL_CANVAS_ITEM_GET_CLASS (item->parent)->realize) (item->parent);
357
358 if (item->parent == NULL && !gtk_widget_get_realized (GTK_WIDGET (item->canvas)))
359 gtk_widget_realize (GTK_WIDGET (item->canvas));
360
361 item->flags |= EEL_CANVAS_ITEM_REALIZED;
362
363 eel_canvas_item_request_update (item);
364 }
365
366 /* Unrealize handler for canvas items */
367 static void
368 eel_canvas_item_unrealize (EelCanvasItem *item)
369 {
370 if (item->flags & EEL_CANVAS_ITEM_MAPPED)
371 (* EEL_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
372
373 item->flags &= ~(EEL_CANVAS_ITEM_REALIZED);
374 }
375
376 /* Map handler for canvas items */
377 static void
378 eel_canvas_item_map (EelCanvasItem *item)
379 {
380 item->flags |= EEL_CANVAS_ITEM_MAPPED;
381 }
382
383 /* Unmap handler for canvas items */
384 static void
385 eel_canvas_item_unmap (EelCanvasItem *item)
386 {
387 item->flags &= ~(EEL_CANVAS_ITEM_MAPPED);
388 }
389
390 /* Update handler for canvas items */
391 static void
392 eel_canvas_item_update (EelCanvasItem *item, double i2w_dx, double i2w_dy, int flags)
393 {
394 item->flags &= ~(EEL_CANVAS_ITEM_NEED_UPDATE);
395 item->flags &= ~(EEL_CANVAS_ITEM_NEED_DEEP_UPDATE);
396 }
397
398 /*
399 * This routine invokes the update method of the item
400 * Please notice, that we take parent to canvas pixel matrix as argument
401 * unlike virtual method ::update, whose argument is item 2 canvas pixel
402 * matrix
403 *
404 * I will try to force somewhat meaningful naming for affines (Lauris)
405 * General naming rule is FROM2TO, where FROM and TO are abbreviations
406 * So p2cpx is Parent2CanvasPixel and i2cpx is Item2CanvasPixel
407 * I hope that this helps to keep track of what really happens
408 *
409 */
410
411 static void
412 eel_canvas_item_invoke_update (EelCanvasItem *item,
413 double i2w_dx,
414 double i2w_dy,
415 int flags)
416 {
417 int child_flags;
418
419 child_flags = flags;
420
421 /* apply object flags to child flags */
422 child_flags &= ~EEL_CANVAS_UPDATE_REQUESTED;
423
424 if (item->flags & EEL_CANVAS_ITEM_NEED_UPDATE)
425 child_flags |= EEL_CANVAS_UPDATE_REQUESTED;
426
427 if (item->flags & EEL_CANVAS_ITEM_NEED_DEEP_UPDATE)
428 child_flags |= EEL_CANVAS_UPDATE_DEEP;
429
430 if (child_flags & GCI_UPDATE_MASK) {
431 if (EEL_CANVAS_ITEM_GET_CLASS (item)->update)
432 EEL_CANVAS_ITEM_GET_CLASS (item)->update (item, i2w_dx, i2w_dy, child_flags);
433 }
434
435 /* If this fail you probably forgot to chain up to
436 * EelCanvasItem::update from a derived class */
437 g_return_if_fail (!(item->flags & EEL_CANVAS_ITEM_NEED_UPDATE));
438 }
439
440 /*
441 * This routine invokes the point method of the item.
442 * The arguments x, y should be in the parent item local coordinates.
443 */
444
445 static double
446 eel_canvas_item_invoke_point (EelCanvasItem *item, double x, double y, int cx, int cy, EelCanvasItem **actual_item)
447 {
448 /* Calculate x & y in item local coordinates */
449
450 if (EEL_CANVAS_ITEM_GET_CLASS (item)->point)
451 return EEL_CANVAS_ITEM_GET_CLASS (item)->point (item, x, y, cx, cy, actual_item);
452
453 return 1e18;
454 }
455
456 /**
457 * eel_canvas_item_set:
458 * @item: A canvas item.
459 * @first_arg_name: The list of object argument name/value pairs used to configure the item.
460 * @Varargs:
461 *
462 * Configures a canvas item. The arguments in the item are set to the specified
463 * values, and the item is repainted as appropriate.
464 **/
465 void
466 eel_canvas_item_set (EelCanvasItem *item, const gchar *first_arg_name, ...)
467 {
468 va_list args;
469
470 va_start (args, first_arg_name);
471 eel_canvas_item_set_valist (item, first_arg_name, args);
472 va_end (args);
473 }
474
475
476 /**
477 * eel_canvas_item_set_valist:
478 * @item: A canvas item.
479 * @first_arg_name: The name of the first argument used to configure the item.
480 * @args: The list of object argument name/value pairs used to configure the item.
481 *
482 * Configures a canvas item. The arguments in the item are set to the specified
483 * values, and the item is repainted as appropriate.
484 **/
485 void
486 eel_canvas_item_set_valist (EelCanvasItem *item, const gchar *first_arg_name, va_list args)
487 {
488 g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
489
490 g_object_set_valist (G_OBJECT (item), first_arg_name, args);
491
492 item->canvas->need_repick = TRUE;
493 }
494
495
496 /**
497 * eel_canvas_item_move:
498 * @item: A canvas item.
499 * @dx: Horizontal offset.
500 * @dy: Vertical offset.
501 *
502 * Moves a canvas item by creating an affine transformation matrix for
503 * translation by using the specified values. This happens in item
504 * local coordinate system, so if you have nontrivial transform, it
505 * most probably does not do, what you want.
506 **/
507 void
508 eel_canvas_item_move (EelCanvasItem *item, double dx, double dy)
509 {
510 g_return_if_fail (item != NULL);
511 g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
512
513 if (!EEL_CANVAS_ITEM_GET_CLASS (item)->translate) {
514 g_warning ("Item type %s does not implement translate method.\n",
515 g_type_name (G_OBJECT_TYPE (item)));
516 return;
517 }
518
519 (* EEL_CANVAS_ITEM_GET_CLASS (item)->translate) (item, dx, dy);
520
521 if (item->flags & EEL_CANVAS_ITEM_MAPPED)
522 item->canvas->need_repick = TRUE;
523
524 if (!(item->flags & EEL_CANVAS_ITEM_NEED_DEEP_UPDATE)) {
525 item->flags |= EEL_CANVAS_ITEM_NEED_DEEP_UPDATE;
526 if (item->parent != NULL)
527 eel_canvas_item_request_update (item->parent);
528 else
529 eel_canvas_request_update (item->canvas);
530 }
531
532 }
533
534 static void
535 eel_canvas_queue_resize (EelCanvas *canvas)
536 {
537 if (gtk_widget_is_drawable (GTK_WIDGET (canvas)))
538 gtk_widget_queue_resize (GTK_WIDGET (canvas));
539 }
540
541 /* Convenience function to reorder items in a group's child list. This puts the
542 * specified link after the "before" link. Returns TRUE if the list was changed.
543 */
544 static gboolean
545 put_item_after (GList *link, GList *before)
546 {
547 EelCanvasGroup *parent;
548
549 if (link == before)
550 return FALSE;
551
552 parent = EEL_CANVAS_GROUP (EEL_CANVAS_ITEM (link->data)->parent);
553
554 if (before == NULL) {
555 if (link == parent->item_list)
556 return FALSE;
557
558 link->prev->next = link->next;
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
559
560 if (link->next)
561 link->next->prev = link->prev;
562 else
563 parent->item_list_end = link->prev;
564
565 link->prev = before;
566 link->next = parent->item_list;
567 link->next->prev = link;
568 parent->item_list = link;
569 } else {
570 if ((link == parent->item_list_end) && (before == parent->item_list_end->prev))
571 return FALSE;
572
573 if (link->next)
574 link->next->prev = link->prev;
575
576 if (link->prev)
577 link->prev->next = link->next;
578 else {
579 parent->item_list = link->next;
580 parent->item_list->prev = NULL;
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
581 }
582
583 link->prev = before;
584 link->next = before->next;
585
586 link->prev->next = link;
587
588 if (link->next)
589 link->next->prev = link;
590 else
591 parent->item_list_end = link;
592 }
593 return TRUE;
594 }
595
596
597 /**
598 * eel_canvas_item_raise:
599 * @item: A canvas item.
600 * @positions: Number of steps to raise the item.
601 *
602 * Raises the item in its parent's stack by the specified number of positions.
603 * If the number of positions is greater than the distance to the top of the
604 * stack, then the item is put at the top.
605 **/
606 void
607 eel_canvas_item_raise (EelCanvasItem *item, int positions)
608 {
609 GList *link, *before;
610 EelCanvasGroup *parent;
611
612 g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
613 g_return_if_fail (positions >= 0);
614
615 if (!item->parent || positions == 0)
616 return;
617
618 parent = EEL_CANVAS_GROUP (item->parent);
619 link = g_list_find (parent->item_list, item);
620 g_assert (link != NULL);
621
622 for (before = link; positions && before; positions--)
623 before = before->next;
624
625 if (!before)
626 before = parent->item_list_end;
627
628 if (put_item_after (link, before)) {
629 redraw_and_repick_if_mapped (item);
630 }
631 }
632
633
634 /**
635 * eel_canvas_item_lower:
636 * @item: A canvas item.
637 * @positions: Number of steps to lower the item.
638 *
639 * Lowers the item in its parent's stack by the specified number of positions.
640 * If the number of positions is greater than the distance to the bottom of the
641 * stack, then the item is put at the bottom.
642 **/
643 void
644 eel_canvas_item_lower (EelCanvasItem *item, int positions)
645 {
646 GList *link, *before;
647 EelCanvasGroup *parent;
648
649 g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
650 g_return_if_fail (positions >= 1);
651
652 if (!item->parent || positions == 0)
653 return;
654
655 parent = EEL_CANVAS_GROUP (item->parent);
656 link = g_list_find (parent->item_list, item);
657 g_assert (link != NULL);
658
659 if (link->prev)
660 for (before = link->prev; positions && before; positions--)
661 before = before->prev;
662 else
663 before = NULL;
664
665 if (put_item_after (link, before)) {
666 redraw_and_repick_if_mapped (item);
667 }
668 }
669
670
671 /**
672 * eel_canvas_item_raise_to_top:
673 * @item: A canvas item.
674 *
675 * Raises an item to the top of its parent's stack.
676 **/
677 void
678 eel_canvas_item_raise_to_top (EelCanvasItem *item)
679 {
680 GList *link;
681 EelCanvasGroup *parent;
682
683 g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
684
685 if (!item->parent)
686 return;
687
688 parent = EEL_CANVAS_GROUP (item->parent);
689 link = g_list_find (parent->item_list, item);
690 g_assert (link != NULL);
691
692 if (put_item_after (link, parent->item_list_end)) {
693 redraw_and_repick_if_mapped (item);
694 }
695 }
696
697
698 /**
699 * eel_canvas_item_lower_to_bottom:
700 * @item: A canvas item.
701 *
702 * Lowers an item to the bottom of its parent's stack.
703 **/
704 void
705 eel_canvas_item_lower_to_bottom (EelCanvasItem *item)
706 {
707 GList *link;
708 EelCanvasGroup *parent;
709
710 g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
711
712 if (!item->parent)
713 return;
714
715 parent = EEL_CANVAS_GROUP (item->parent);
716 link = g_list_find (parent->item_list, item);
717 g_assert (link != NULL);
718
719 if (put_item_after (link, NULL)) {
720 redraw_and_repick_if_mapped (item);
721 }
722 }
723
724 /**
725 * eel_canvas_item_send_behind:
726 * @item: A canvas item.
727 * @behind_item: The canvas item to put item behind, or NULL
728 *
729 * Moves item to a in the position in the stacking order so that
730 * it is placed immediately below behind_item, or at the top if
731 * behind_item is NULL.
732 **/
733 void
734 eel_canvas_item_send_behind (EelCanvasItem *item,
735 EelCanvasItem *behind_item)
736 {
737 GList *item_list;
738 int item_position, behind_position;
739
740 g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
741
742 if (behind_item == NULL) {
743 eel_canvas_item_raise_to_top (item);
744 return;
745 }
746
747 g_return_if_fail (EEL_IS_CANVAS_ITEM (behind_item));
748 g_return_if_fail (item->parent == behind_item->parent);
749
750 item_list = EEL_CANVAS_GROUP (item->parent)->item_list;
751
752 item_position = g_list_index (item_list, item);
753 g_assert (item_position != -1);
754 behind_position = g_list_index (item_list, behind_item);
755 g_assert (behind_position != -1);
756 g_assert (item_position != behind_position);
757
758 if (item_position == behind_position - 1) {
759 return;
760 }
761
762 if (item_position < behind_position) {
763 eel_canvas_item_raise (item, (behind_position - 1) - item_position);
764 } else {
765 eel_canvas_item_lower (item, item_position - behind_position);
766 }
767 }
768
769 /**
770 * eel_canvas_item_show:
771 * @item: A canvas item.
772 *
773 * Shows a canvas item. If the item was already shown, then no action is taken.
774 **/
775 void
776 eel_canvas_item_show (EelCanvasItem *item)
777 {
778 g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
779
780 if (!(item->flags & EEL_CANVAS_ITEM_VISIBLE)) {
781 item->flags |= EEL_CANVAS_ITEM_VISIBLE;
782
783 if (!(item->flags & EEL_CANVAS_ITEM_REALIZED))
784 (* EEL_CANVAS_ITEM_GET_CLASS (item)->realize) (item);
785
786 if (item->parent != NULL) {
787 if (!(item->flags & EEL_CANVAS_ITEM_MAPPED) &&
788 item->parent->flags & EEL_CANVAS_ITEM_MAPPED)
789 (* EEL_CANVAS_ITEM_GET_CLASS (item)->map) (item);
790 } else {
791 if (!(item->flags & EEL_CANVAS_ITEM_MAPPED) &&
792 gtk_widget_get_mapped (GTK_WIDGET (item->canvas)))
793 (* EEL_CANVAS_ITEM_GET_CLASS (item)->map) (item);
794 }
795
796 redraw_and_repick_if_mapped (item);
797 eel_canvas_queue_resize (item->canvas);
798 }
799 }
800
801
802 /**
803 * eel_canvas_item_hide:
804 * @item: A canvas item.
805 *
806 * Hides a canvas item. If the item was already hidden, then no action is
807 * taken.
808 **/
809 void
810 eel_canvas_item_hide (EelCanvasItem *item)
811 {
812 g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
813
814 if (item->flags & EEL_CANVAS_ITEM_VISIBLE) {
815 item->flags &= ~EEL_CANVAS_ITEM_VISIBLE;
816
817 redraw_and_repick_if_mapped (item);
818
819 if (item->flags & EEL_CANVAS_ITEM_MAPPED)
820 (* EEL_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
821
822 eel_canvas_queue_resize (item->canvas);
823
824 /* No need to unrealize when we just want to hide */
825 }
826 }
827
828
829 /**
830 * eel_canvas_item_grab:
831 * @item: A canvas item.
832 * @event_mask: Mask of events that will be sent to this item.
833 * @cursor: If non-NULL, the cursor that will be used while the grab is active.
834 * @etime: The timestamp required for grabbing the mouse, or GDK_CURRENT_TIME.
835 *
836 * Specifies that all events that match the specified event mask should be sent
837 * to the specified item, and also grabs the mouse by calling
838 * gdk_pointer_grab(). The event mask is also used when grabbing the pointer.
839 * If @cursor is not NULL, then that cursor is used while the grab is active.
840 * The @etime parameter is the timestamp required for grabbing the mouse.
841 *
842 * Return value: If an item was already grabbed, it returns %GDK_GRAB_ALREADY_GRABBED. If
843 * the specified item was hidden by calling eel_canvas_item_hide(), then it
844 * returns %GDK_GRAB_NOT_VIEWABLE. Else, it returns the result of calling
845 * gdk_pointer_grab().
846 **/
847 GdkGrabStatus
848 eel_canvas_item_grab (EelCanvasItem *item,
849 GdkEventMask event_mask,
850 GdkCursor *cursor,
851 guint32 timestamp)
852 {
853 GdkGrabStatus retval;
854 GdkDisplay *display;
855 GdkDeviceManager *manager;
856 GdkDevice *device;
857
858 g_return_val_if_fail (EEL_IS_CANVAS_ITEM (item), GDK_GRAB_NOT_VIEWABLE);
859 g_return_val_if_fail (gtk_widget_get_mapped (GTK_WIDGET (item->canvas)),
860 GDK_GRAB_NOT_VIEWABLE);
861
862 if (item->canvas->grabbed_item)
863 return GDK_GRAB_ALREADY_GRABBED;
864
865 if (!(item->flags & EEL_CANVAS_ITEM_MAPPED))
866 return GDK_GRAB_NOT_VIEWABLE;
867
868 display = gtk_widget_get_display (GTK_WIDGET (item->canvas));
869 manager = gdk_display_get_device_manager (display);
870 device = gdk_device_manager_get_client_pointer (manager);
871
872 retval = gdk_device_grab (device,
873 gtk_layout_get_bin_window (GTK_LAYOUT (item->canvas)),
874 GDK_OWNERSHIP_NONE,
875 FALSE,
876 event_mask,
877 cursor,
878 timestamp);
879
880 if (retval != GDK_GRAB_SUCCESS)
881 return retval;
882
883 item->canvas->grabbed_item = item;
884 item->canvas->grabbed_event_mask = event_mask;
885 item->canvas->current_item = item; /* So that events go to the grabbed item */
886
887 return retval;
888 }
889
890
891 /**
892 * eel_canvas_item_ungrab:
893 * @item: A canvas item that holds a grab.
894 * @etime: The timestamp for ungrabbing the mouse.
895 *
896 * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
897 * mouse.
898 **/
899 void
900 eel_canvas_item_ungrab (EelCanvasItem *item, guint32 etime)
901 {
902 GdkDisplay *display;
903 GdkDeviceManager *manager;
904 GdkDevice *device;
905
906 g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
907
908 if (item->canvas->grabbed_item != item)
909 return;
910
911 display = gtk_widget_get_display (GTK_WIDGET (item->canvas));
912 manager = gdk_display_get_device_manager (display);
913 device = gdk_device_manager_get_client_pointer (manager);
914
915 item->canvas->grabbed_item = NULL;
916 gdk_device_ungrab (device, etime);
917 }
918
919 /**
920 * eel_canvas_item_w2i:
921 * @item: A canvas item.
922 * @x: X coordinate to convert (input/output value).
923 * @y: Y coordinate to convert (input/output value).
924 *
925 * Converts a coordinate pair from world coordinates to item-relative
926 * coordinates.
927 **/
928 void
929 eel_canvas_item_w2i (EelCanvasItem *item, double *x, double *y)
930 {
931 g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
932 g_return_if_fail (x != NULL);
933 g_return_if_fail (y != NULL);
934
935 item = item->parent;
936 while (item) {
937 if (EEL_IS_CANVAS_GROUP (item)) {
938 *x -= EEL_CANVAS_GROUP (item)->xpos;
939 *y -= EEL_CANVAS_GROUP (item)->ypos;
940 }
941
942 item = item->parent;
943 }
944 }
945
946
947 /**
948 * eel_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 eel_canvas_item_i2w (EelCanvasItem *item, double *x, double *y)
958 {
959 g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
960 g_return_if_fail (x != NULL);
961 g_return_if_fail (y != NULL);
962
963 item = item->parent;
964 while (item) {
965 if (EEL_IS_CANVAS_GROUP (item)) {
966 *x += EEL_CANVAS_GROUP (item)->xpos;
967 *y += EEL_CANVAS_GROUP (item)->ypos;
968 }
969
970 item = item->parent;
971 }
972 }
973
974 /* Returns whether the item is an inferior of or is equal to the parent. */
975 static int
976 is_descendant (EelCanvasItem *item, EelCanvasItem *parent)
977 {
978 for (; item; item = item->parent)
979 if (item == parent)
980 return TRUE;
981
982 return FALSE;
983 }
984
985 /**
986 * eel_canvas_item_reparent:
987 * @item: A canvas item.
988 * @new_group: A canvas group.
989 *
990 * Changes the parent of the specified item to be the new group. The item keeps
991 * its group-relative coordinates as for its old parent, so the item may change
992 * its absolute position within the canvas.
993 **/
994 void
995 eel_canvas_item_reparent (EelCanvasItem *item, EelCanvasGroup *new_group)
996 {
997 g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
998 g_return_if_fail (EEL_IS_CANVAS_GROUP (new_group));
999
1000 /* Both items need to be in the same canvas */
1001 g_return_if_fail (item->canvas == EEL_CANVAS_ITEM (new_group)->canvas);
1002
1003 /* The group cannot be an inferior of the item or be the item itself --
1004 * this also takes care of the case where the item is the root item of
1005 * the canvas. */
1006 g_return_if_fail (!is_descendant (EEL_CANVAS_ITEM (new_group), item));
1007
1008 /* Everything is ok, now actually reparent the item */
1009
1010 g_object_ref (G_OBJECT (item)); /* protect it from the unref in group_remove */
1011
1012 eel_canvas_item_request_redraw (item);
1013
1014 group_remove (EEL_CANVAS_GROUP (item->parent), item);
1015 item->parent = EEL_CANVAS_ITEM (new_group);
1016 /* item->canvas is unchanged. */
1017 group_add (new_group, item);
1018
1019 /* Redraw and repick */
1020
1021 redraw_and_repick_if_mapped (item);
1022
1023 g_object_unref (G_OBJECT (item));
1024 }
1025
1026 /**
1027 * eel_canvas_item_grab_focus:
1028 * @item: A canvas item.
1029 *
1030 * Makes the specified item take the keyboard focus, so all keyboard events will
1031 * be sent to it. If the canvas widget itself did not have the focus, it grabs
1032 * it as well.
1033 **/
1034 void
1035 eel_canvas_item_grab_focus (EelCanvasItem *item)
1036 {
1037 EelCanvasItem *focused_item;
1038 GdkEvent ev;
1039
1040 g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
1041 g_return_if_fail (gtk_widget_get_can_focus (GTK_WIDGET (item->canvas)));
1042
1043 focused_item = item->canvas->focused_item;
1044
1045 if (focused_item) {
1046 ev.focus_change.type = GDK_FOCUS_CHANGE;
1047 ev.focus_change.window = gtk_layout_get_bin_window (GTK_LAYOUT (item->canvas));
1048 ev.focus_change.send_event = FALSE;
1049 ev.focus_change.in = FALSE;
1050
1051 emit_event (item->canvas, &ev);
1052 }
1053
1054 item->canvas->focused_item = item;
1055 gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
1056
1057 if (focused_item) {
1058 ev.focus_change.type = GDK_FOCUS_CHANGE;
1059 ev.focus_change.window = gtk_layout_get_bin_window (GTK_LAYOUT (item->canvas));
1060 ev.focus_change.send_event = FALSE;
1061 ev.focus_change.in = TRUE;
1062
1063 emit_event (item->canvas, &ev);
1064 }
1065 }
1066
1067
1068 /**
1069 * eel_canvas_item_get_bounds:
1070 * @item: A canvas item.
1071 * @x1: Leftmost edge of the bounding box (return value).
1072 * @y1: Upper edge of the bounding box (return value).
1073 * @x2: Rightmost edge of the bounding box (return value).
1074 * @y2: Lower edge of the bounding box (return value).
1075 *
1076 * Queries the bounding box of a canvas item. The bounds are returned in the
1077 * coordinate system of the item's parent.
1078 **/
1079 void
1080 eel_canvas_item_get_bounds (EelCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
1081 {
1082 double tx1, ty1, tx2, ty2;
1083
1084 g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
1085
1086 tx1 = ty1 = tx2 = ty2 = 0.0;
1087
1088 /* Get the item's bounds in its coordinate system */
1089
1090 if (EEL_CANVAS_ITEM_GET_CLASS (item)->bounds)
1091 (* EEL_CANVAS_ITEM_GET_CLASS (item)->bounds) (item, &tx1, &ty1, &tx2, &ty2);
1092
1093 /* Return the values */
1094
1095 if (x1)
1096 *x1 = tx1;
1097
1098 if (y1)
1099 *y1 = ty1;
1100
1101 if (x2)
1102 *x2 = tx2;
1103
1104 if (y2)
1105 *y2 = ty2;
1106 }
1107
1108
1109 /**
1110 * eel_canvas_item_request_update
1111 * @item: A canvas item.
1112 *
1113 * To be used only by item implementations. Requests that the canvas queue an
1114 * update for the specified item.
1115 **/
1116 void
1117 eel_canvas_item_request_update (EelCanvasItem *item)
1118 {
1119 if (NULL == item->canvas)
1120 return;
1121
1122 g_return_if_fail (!item->canvas->doing_update);
1123
1124 if (item->flags & EEL_CANVAS_ITEM_NEED_UPDATE)
1125 return;
1126
1127 item->flags |= EEL_CANVAS_ITEM_NEED_UPDATE;
1128
1129 if (item->parent != NULL) {
1130 /* Recurse up the tree */
1131 eel_canvas_item_request_update (item->parent);
1132 } else {
1133 /* Have reached the top of the tree, make sure the update call gets scheduled. */
1134 eel_canvas_request_update (item->canvas);
1135 }
1136 }
1137
1138 /**
1139 * eel_canvas_item_request_update
1140 * @item: A canvas item.
1141 *
1142 * Convenience function that informs a canvas that the specified item needs
1143 * to be repainted. To be used by item implementations
1144 **/
1145 void
1146 eel_canvas_item_request_redraw (EelCanvasItem *item)
1147 {
1148 if (item->flags & EEL_CANVAS_ITEM_MAPPED)
1149 eel_canvas_request_redraw (item->canvas,
1150 item->x1, item->y1,
1151 item->x2 + 1, item->y2 + 1);
1152 }
1153
1154
1155
1156 /*** EelCanvasGroup ***/
1157
1158
1159 enum {
1160 GROUP_PROP_0,
1161 GROUP_PROP_X,
1162 GROUP_PROP_Y
1163 };
1164
1165
1166 static void eel_canvas_group_class_init (EelCanvasGroupClass *klass);
1167 static void eel_canvas_group_init (EelCanvasGroup *group);
1168 static void eel_canvas_group_set_property(GObject *object,
1169 guint param_id,
1170 const GValue *value,
1171 GParamSpec *pspec);
1172 static void eel_canvas_group_get_property(GObject *object,
1173 guint param_id,
1174 GValue *value,
1175 GParamSpec *pspec);
1176
1177 static void eel_canvas_group_destroy (EelCanvasItem *object);
1178
1179 static void eel_canvas_group_update (EelCanvasItem *item,
1180 double i2w_dx,
1181 double i2w_dy,
1182 int flags);
1183 static void eel_canvas_group_unrealize (EelCanvasItem *item);
1184 static void eel_canvas_group_map (EelCanvasItem *item);
1185 static void eel_canvas_group_unmap (EelCanvasItem *item);
1186 static void eel_canvas_group_draw (EelCanvasItem *item,
1187 cairo_t *cr,
1188 cairo_region_t *region);
1189 static double eel_canvas_group_point (EelCanvasItem *item, double x, double y,
1190 int cx, int cy,
1191 EelCanvasItem **actual_item);
1192 static void eel_canvas_group_translate (EelCanvasItem *item, double dx, double dy);
1193 static void eel_canvas_group_bounds (EelCanvasItem *item, double *x1, double *y1,
1194 double *x2, double *y2);
1195
1196
1197 static EelCanvasItemClass *group_parent_class;
1198
1199
1200 /**
1201 * eel_canvas_group_get_type:
1202 *
1203 * Registers the &EelCanvasGroup class if necessary, and returns the type ID
1204 * associated to it.
1205 *
1206 * Return value: The type ID of the &EelCanvasGroup class.
1207 **/
1208 GType
1209 eel_canvas_group_get_type (void)
1210 {
1211 static GType group_type = 0;
1212
1213 if (!group_type) {
1214 static const GTypeInfo group_info = {
1215 sizeof (EelCanvasGroupClass),
1216 (GBaseInitFunc) NULL,
1217 (GBaseFinalizeFunc) NULL,
1218 (GClassInitFunc) eel_canvas_group_class_init,
1219 NULL, /* class_finalize */
1220 NULL, /* class_data */
1221 sizeof (EelCanvasGroup),
1222 0, /* n_preallocs */
1223 (GInstanceInitFunc) eel_canvas_group_init
1224
1225
1226 };
1227
1228 group_type = g_type_register_static (eel_canvas_item_get_type (),
1229 "EelCanvasGroup",
1230 &group_info,
1231 0);
1232 }
1233
1234 return group_type;
1235 }
1236
1237 /* Class initialization function for EelCanvasGroupClass */
1238 static void
1239 eel_canvas_group_class_init (EelCanvasGroupClass *klass)
1240 {
1241 GObjectClass *gobject_class;
1242 EelCanvasItemClass *item_class;
1243
1244 gobject_class = (GObjectClass *) klass;
1245 item_class = (EelCanvasItemClass *) klass;
1246
1247 group_parent_class = g_type_class_peek_parent (klass);
1248
1249 gobject_class->set_property = eel_canvas_group_set_property;
1250 gobject_class->get_property = eel_canvas_group_get_property;
1251
1252 g_object_class_install_property
1253 (gobject_class, GROUP_PROP_X,
1254 g_param_spec_double ("x",
1255 _("X"),
1256 _("X"),
1257 -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
1258 G_PARAM_READWRITE));
1259 g_object_class_install_property
1260 (gobject_class, GROUP_PROP_Y,
1261 g_param_spec_double ("y",
1262 _("Y"),
1263 _("Y"),
1264 -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
1265 G_PARAM_READWRITE));
1266
1267 item_class->destroy = eel_canvas_group_destroy;
1268 item_class->update = eel_canvas_group_update;
1269 item_class->unrealize = eel_canvas_group_unrealize;
1270 item_class->map = eel_canvas_group_map;
1271 item_class->unmap = eel_canvas_group_unmap;
1272 item_class->draw = eel_canvas_group_draw;
1273 item_class->point = eel_canvas_group_point;
1274 item_class->translate = eel_canvas_group_translate;
1275 item_class->bounds = eel_canvas_group_bounds;
1276 }
1277
1278 /* Object initialization function for EelCanvasGroup */
1279 static void
1280 eel_canvas_group_init (EelCanvasGroup *group)
1281 {
1282 group->xpos = 0.0;
1283 group->ypos = 0.0;
1284 }
1285
1286 /* Set_property handler for canvas groups */
1287 static void
1288 eel_canvas_group_set_property (GObject *gobject, guint param_id,
1289 const GValue *value, GParamSpec *pspec)
1290 {
1291 EelCanvasItem *item;
1292 EelCanvasGroup *group;
1293 double old;
1294 gboolean moved;
1295
1296 g_return_if_fail (EEL_IS_CANVAS_GROUP (gobject));
1297
1298 item = EEL_CANVAS_ITEM (gobject);
1299 group = EEL_CANVAS_GROUP (gobject);
1300
1301 moved = FALSE;
1302 switch (param_id) {
1303 case GROUP_PROP_X:
1304 old = group->xpos;
1305 group->xpos = g_value_get_double (value);
1306 if (old != group->xpos)
1307 moved = TRUE;
1308 break;
1309
1310 case GROUP_PROP_Y:
1311 old = group->ypos;
1312 group->ypos = g_value_get_double (value);
1313 if (old != group->ypos)
1314 moved = TRUE;
1315 break;
1316
1317 default:
1318 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
1319 break;
1320 }
1321
1322 if (moved) {
1323 item->flags |= EEL_CANVAS_ITEM_NEED_DEEP_UPDATE;
1324 if (item->parent != NULL)
1325 eel_canvas_item_request_update (item->parent);
1326 else
1327 eel_canvas_request_update (item->canvas);
1328 }
1329 }
1330
1331 /* Get_property handler for canvas groups */
1332 static void
1333 eel_canvas_group_get_property (GObject *gobject, guint param_id,
1334 GValue *value, GParamSpec *pspec)
1335 {
1336 EelCanvasGroup *group;
1337
1338 g_return_if_fail (EEL_IS_CANVAS_GROUP (gobject));
1339
1340 group = EEL_CANVAS_GROUP (gobject);
1341
1342 switch (param_id) {
1343 case GROUP_PROP_X:
1344 g_value_set_double (value, group->xpos);
1345 break;
1346
1347 case GROUP_PROP_Y:
1348 g_value_set_double (value, group->ypos);
1349 break;
1350
1351 default:
1352 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
1353 break;
1354 }
1355 }
1356
1357 /* Destroy handler for canvas groups */
1358 static void
1359 eel_canvas_group_destroy (EelCanvasItem *object)
1360 {
1361 EelCanvasGroup *group;
1362 EelCanvasItem *child;
1363 GList *list;
1364
1365 g_return_if_fail (EEL_IS_CANVAS_GROUP (object));
1366
1367 group = EEL_CANVAS_GROUP (object);
1368
1369 list = group->item_list;
1370 while (list) {
1371 child = list->data;
1372 list = list->next;
1373
1374 eel_canvas_item_destroy (child);
1375 }
1376
1377 if (EEL_CANVAS_ITEM_CLASS (group_parent_class)->destroy)
1378 (* EEL_CANVAS_ITEM_CLASS (group_parent_class)->destroy) (object);
1379 }
1380
1381 /* Update handler for canvas groups */
1382 static void
1383 eel_canvas_group_update (EelCanvasItem *item, double i2w_dx, double i2w_dy, int flags)
1384 {
1385 EelCanvasGroup *group;
1386 GList *list;
1387 EelCanvasItem *i;
1388 double bbox_x0, bbox_y0, bbox_x1, bbox_y1;
1389 gboolean first = TRUE;
1390
1391 group = EEL_CANVAS_GROUP (item);
1392
1393 (* group_parent_class->update) (item, i2w_dx, i2w_dy, flags);
1394
1395 bbox_x0 = 0;
1396 bbox_y0 = 0;
1397 bbox_x1 = 0;
1398 bbox_y1 = 0;
1399
1400 for (list = group->item_list; list; list = list->next) {
1401 i = list->data;
1402
1403 eel_canvas_item_invoke_update (i, i2w_dx + group->xpos, i2w_dy + group->ypos, flags);
1404
1405 if (first) {
1406 first = FALSE;
1407 bbox_x0 = i->x1;
1408 bbox_y0 = i->y1;
1409 bbox_x1 = i->x2;
1410 bbox_y1 = i->y2;
1411 } else {
1412 bbox_x0 = MIN (bbox_x0, i->x1);
1413 bbox_y0 = MIN (bbox_y0, i->y1);
1414 bbox_x1 = MAX (bbox_x1, i->x2);
1415 bbox_y1 = MAX (bbox_y1, i->y2);
1416 }
1417 }
1418 item->x1 = bbox_x0;
1419 item->y1 = bbox_y0;
1420 item->x2 = bbox_x1;
1421 item->y2 = bbox_y1;
1422 }
1423
1424 /* Unrealize handler for canvas groups */
1425 static void
1426 eel_canvas_group_unrealize (EelCanvasItem *item)
1427 {
1428 EelCanvasGroup *group;
1429 GList *list;
1430 EelCanvasItem *i;
1431
1432 group = EEL_CANVAS_GROUP (item);
1433
1434 /* Unmap group before children to avoid flash */
1435 if (item->flags & EEL_CANVAS_ITEM_MAPPED)
1436 (* EEL_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
1437
1438 for (list = group->item_list; list; list = list->next) {
1439 i = list->data;
1440
1441 if (i->flags & EEL_CANVAS_ITEM_REALIZED)
1442 (* EEL_CANVAS_ITEM_GET_CLASS (i)->unrealize) (i);
1443 }
1444
1445 (* group_parent_class->unrealize) (item);
1446 }
1447
1448 /* Map handler for canvas groups */
1449 static void
1450 eel_canvas_group_map (EelCanvasItem *item)
1451 {
1452 EelCanvasGroup *group;
1453 GList *list;
1454 EelCanvasItem *i;
1455
1456 group = EEL_CANVAS_GROUP (item);
1457
1458 for (list = group->item_list; list; list = list->next) {
1459 i = list->data;
1460
1461 if (i->flags & EEL_CANVAS_ITEM_VISIBLE &&
1462 !(i->flags & EEL_CANVAS_ITEM_MAPPED)) {
1463 if (!(i->flags & EEL_CANVAS_ITEM_REALIZED))
1464 (* EEL_CANVAS_ITEM_GET_CLASS (i)->realize) (i);
1465
1466 (* EEL_CANVAS_ITEM_GET_CLASS (i)->map) (i);
1467 }
1468 }
1469
1470 (* group_parent_class->map) (item);
1471 }
1472
1473 /* Unmap handler for canvas groups */
1474 static void
1475 eel_canvas_group_unmap (EelCanvasItem *item)
1476 {
1477 EelCanvasGroup *group;
1478 GList *list;
1479 EelCanvasItem *i;
1480
1481 group = EEL_CANVAS_GROUP (item);
1482
1483 for (list = group->item_list; list; list = list->next) {
1484 i = list->data;
1485
1486 if (i->flags & EEL_CANVAS_ITEM_MAPPED)
1487 (* EEL_CANVAS_ITEM_GET_CLASS (i)->unmap) (i);
1488 }
1489
1490 (* group_parent_class->unmap) (item);
1491 }
1492
1493 /* Draw handler for canvas groups */
1494 static void
1495 eel_canvas_group_draw (EelCanvasItem *item,
1496 cairo_t *cr,
1497 cairo_region_t *region)
1498 {
1499 EelCanvasGroup *group;
1500 GList *list;
1501 EelCanvasItem *child = NULL;
1502
1503 group = EEL_CANVAS_GROUP (item);
1504
1505 for (list = group->item_list; list; list = list->next) {
1506 child = list->data;
1507
1508 if ((child->flags & EEL_CANVAS_ITEM_MAPPED) &&
1509 (EEL_CANVAS_ITEM_GET_CLASS (child)->draw)) {
1510 GdkRectangle child_rect;
1511
1512 child_rect.x = child->x1;
1513 child_rect.y = child->y1;
1514 child_rect.width = child->x2 - child->x1 + 1;
1515 child_rect.height = child->y2 - child->y1 + 1;
1516
1517 if (cairo_region_contains_rectangle (region, &child_rect) != CAIRO_REGION_OVERLAP_OUT)
1518 EEL_CANVAS_ITEM_GET_CLASS (child)->draw (child, cr, region);
1519 }
1520 }
1521 }
1522
1523 /* Point handler for canvas groups */
1524 static double
1525 eel_canvas_group_point (EelCanvasItem *item, double x, double y, int cx, int cy,
1526 EelCanvasItem **actual_item)
1527 {
1528 EelCanvasGroup *group;
1529 GList *list;
1530 EelCanvasItem *child, *point_item;
1531 int x1, y1, x2, y2;
1532 double gx, gy;
1533 double dist, best;
1534 int has_point;
1535
1536 group = EEL_CANVAS_GROUP (item);
1537
1538 x1 = cx - item->canvas->close_enough;
1539 y1 = cy - item->canvas->close_enough;
1540 x2 = cx + item->canvas->close_enough;
1541 y2 = cy + item->canvas->close_enough;
1542
1543 best = 0.0;
1544 *actual_item = NULL;
1545
1546 gx = x - group->xpos;
1547 gy = y - group->ypos;
1548
1549 dist = 0.0; /* keep gcc happy */
1550
1551 for (list = group->item_list; list; list = list->next) {
1552 child = list->data;
1553
1554 if ((child->x1 > x2) || (child->y1 > y2) || (child->x2 < x1) || (child->y2 < y1))
1555 continue;
1556
1557 point_item = NULL; /* cater for incomplete item implementations */
1558
1559 if ((child->flags & EEL_CANVAS_ITEM_MAPPED)
1560 && EEL_CANVAS_ITEM_GET_CLASS (child)->point) {
1561 dist = eel_canvas_item_invoke_point (child, gx, gy, cx, cy, &point_item);
1562 has_point = TRUE;
1563 } else
1564 has_point = FALSE;
1565
1566 if (has_point
1567 && point_item
1568 && ((int) (dist * item->canvas->pixels_per_unit + 0.5)
1569 <= item->canvas->close_enough)) {
1570 best = dist;
1571 *actual_item = point_item;
1572 }
1573 }
1574
1575 return best;
1576 }
1577
1578 void
1579 eel_canvas_group_translate (EelCanvasItem *item, double dx, double dy)
1580 {
1581 EelCanvasGroup *group;
1582
1583 group = EEL_CANVAS_GROUP (item);
1584
1585 group->xpos += dx;
1586 group->ypos += dy;
1587 }
1588
1589 /* Bounds handler for canvas groups */
1590 static void
1591 eel_canvas_group_bounds (EelCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
1592 {
1593 EelCanvasGroup *group;
1594 EelCanvasItem *child;
1595 GList *list;
1596 double tx1, ty1, tx2, ty2;
1597 double minx, miny, maxx, maxy;
1598 int set;
1599
1600 group = EEL_CANVAS_GROUP (item);
1601
1602 /* Get the bounds of the first visible item */
1603
1604 child = NULL; /* Unnecessary but eliminates a warning. */
1605
1606 set = FALSE;
1607
1608 for (list = group->item_list; list; list = list->next) {
1609 child = list->data;
1610
1611 if (child->flags & EEL_CANVAS_ITEM_MAPPED) {
1612 set = TRUE;
1613 eel_canvas_item_get_bounds (child, &minx, &miny, &maxx, &maxy);
1614 break;
1615 }
1616 }
1617
1618 /* If there were no visible items, return an empty bounding box */
1619
1620 if (!set) {
1621 *x1 = *y1 = *x2 = *y2 = 0.0;
1622 return;
1623 }
1624
1625 /* Now we can grow the bounds using the rest of the items */
1626
1627 list = list->next;
1628
1629 for (; list; list = list->next) {
1630 child = list->data;
1631
1632 if (!(child->flags & EEL_CANVAS_ITEM_MAPPED))
1633 continue;
1634
1635 eel_canvas_item_get_bounds (child, &tx1, &ty1, &tx2, &ty2);
1636
1637 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)
1638 minx = tx1;
1639
1640 if (ty1 < miny)
1641 miny = ty1;
1642
1643 if (tx2 > maxx)
1644 maxx = tx2;
1645
1646 if (ty2 > maxy)
1647 maxy = ty2;
1648 }
1649
1650 /* Make the bounds be relative to our parent's coordinate system */
1651
1652 if (item->parent) {
1653 minx += group->xpos;
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
1654 miny += group->ypos;
1655 maxx += group->xpos;
1656 maxy += group->ypos;
1657 }
1658
1659 *x1 = minx;
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
1660 *y1 = miny;
1661 *x2 = maxx;
1662 *y2 = maxy;
1663 }
1664
1665 /* Adds an item to a group */
1666 static void
1667 group_add (EelCanvasGroup *group, EelCanvasItem *item)
1668 {
1669 g_object_ref_sink (item);
1670
1671 if (!group->item_list) {
1672 group->item_list = g_list_append (group->item_list, item);
1673 group->item_list_end = group->item_list;
1674 } else
1675 group->item_list_end = g_list_append (group->item_list_end, item)->next;
1676
1677 if (item->flags & EEL_CANVAS_ITEM_VISIBLE &&
1678 group->item.flags & EEL_CANVAS_ITEM_MAPPED) {
1679 if (!(item->flags & EEL_CANVAS_ITEM_REALIZED))
1680 (* EEL_CANVAS_ITEM_GET_CLASS (item)->realize) (item);
1681
1682 if (!(item->flags & EEL_CANVAS_ITEM_MAPPED))
1683 (* EEL_CANVAS_ITEM_GET_CLASS (item)->map) (item);
1684 }
1685
1686 if (item->flags & EEL_CANVAS_ITEM_VISIBLE)
1687 eel_canvas_queue_resize (EEL_CANVAS_ITEM (group)->canvas);
1688 }
1689
1690 /* Removes an item from a group */
1691 static void
1692 group_remove (EelCanvasGroup *group, EelCanvasItem *item)
1693 {
1694 GList *children;
1695
1696 g_return_if_fail (EEL_IS_CANVAS_GROUP (group));
1697 g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
1698
1699 for (children = group->item_list; children; children = children->next)
1700 if (children->data == item) {
1701 if (item->flags & EEL_CANVAS_ITEM_MAPPED) {
1702 (* EEL_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
1703 }
1704
1705 if (item->flags & EEL_CANVAS_ITEM_REALIZED)
1706 (* EEL_CANVAS_ITEM_GET_CLASS (item)->unrealize) (item);
1707
1708 if (item->flags & EEL_CANVAS_ITEM_VISIBLE)
1709 eel_canvas_queue_resize (item->canvas);
1710
1711 /* Unparent the child */
1712
1713 item->parent = NULL;
1714 /* item->canvas = NULL; */
1715 g_object_unref (G_OBJECT (item));
1716
1717 /* Remove it from the list */
1718
1719 if (children == group->item_list_end)
1720 group->item_list_end = children->prev;
1721
1722 group->item_list = g_list_remove_link (group->item_list, children);
1723 g_list_free (children);
1724 break;
1725 }
1726 }
1727
1728
1729 /*** EelCanvas ***/
1730
1731
1732 enum {
1733 DRAW_BACKGROUND,
1734 LAST_SIGNAL
1735 };
1736
1737 static void eel_canvas_class_init (EelCanvasClass *klass);
1738 static void eel_canvas_init (EelCanvas *canvas);
1739 static void eel_canvas_destroy (GtkWidget *object);
1740 static void eel_canvas_map (GtkWidget *widget);
1741 static void eel_canvas_unmap (GtkWidget *widget);
1742 static void eel_canvas_realize (GtkWidget *widget);
1743 static void eel_canvas_unrealize (GtkWidget *widget);
1744 static void eel_canvas_size_allocate (GtkWidget *widget,
1745 GtkAllocation *allocation);
1746 static gint eel_canvas_button (GtkWidget *widget,
1747 GdkEventButton *event);
1748 static gint eel_canvas_motion (GtkWidget *widget,
1749 GdkEventMotion *event);
1750 static gint eel_canvas_draw (GtkWidget *widget,
1751 cairo_t *cr);
1752 static gint eel_canvas_key (GtkWidget *widget,
1753 GdkEventKey *event);
1754 static gint eel_canvas_crossing (GtkWidget *widget,
1755 GdkEventCrossing *event);
1756 static gint eel_canvas_focus_in (GtkWidget *widget,
1757 GdkEventFocus *event);
1758 static gint eel_canvas_focus_out (GtkWidget *widget,
1759 GdkEventFocus *event);
1760 static void eel_canvas_request_update_real (EelCanvas *canvas);
1761 static void eel_canvas_draw_background (EelCanvas *canvas,
1762 cairo_t *cr);
1763 static AtkObject *eel_canvas_get_accessible (GtkWidget *widget);
1764
1765
1766 static GtkLayoutClass *canvas_parent_class;
1767
1768 static guint canvas_signals[LAST_SIGNAL];
1769
1770 /**
1771 * eel_canvas_get_type:
1772 *
1773 * Registers the &EelCanvas class if necessary, and returns the type ID
1774 * associated to it.
1775 *
1776 * Return value: The type ID of the &EelCanvas class.
1777 **/
1778 GType
1779 eel_canvas_get_type (void)
1780 {
1781 static GType canvas_type = 0;
1782
1783 if (!canvas_type) {
1784 static const GTypeInfo canvas_info = {
1785 sizeof (EelCanvasClass),
1786 (GBaseInitFunc) NULL,
1787 (GBaseFinalizeFunc) NULL,
1788 (GClassInitFunc) eel_canvas_class_init,
1789 NULL, /* class_finalize */
1790 NULL, /* class_data */
1791 sizeof (EelCanvas),
1792 0, /* n_preallocs */
1793 (GInstanceInitFunc) eel_canvas_init
1794 };
1795
1796 canvas_type = g_type_register_static (gtk_layout_get_type (),
1797 "EelCanvas",
1798 &canvas_info,
1799 0);
1800 }
1801
1802 return canvas_type;
1803 }
1804
1805 static void
1806 eel_canvas_get_property (GObject *object,
1807 guint prop_id,
1808 GValue *value,
1809 GParamSpec *pspec)
1810 {
1811 switch (prop_id) {
1812 default:
1813 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1814 break;
1815 }
1816 }
1817
1818 static void
1819 eel_canvas_set_property (GObject *object,
1820 guint prop_id,
1821 const GValue *value,
1822 GParamSpec *pspec)
1823 {
1824 switch (prop_id) {
1825 default:
1826 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1827 break;
1828 }
1829 }
1830
1831 static void
1832 eel_canvas_accessible_adjustment_changed (GtkAdjustment *adjustment,
1833 gpointer data)
1834 {
1835 AtkObject *atk_obj;
1836
1837 /* The scrollbars have changed */
1838 atk_obj = ATK_OBJECT (data);
1839
1840 g_signal_emit_by_name (atk_obj, "visible_data_changed");
1841 }
1842
1843 static void
1844 accessible_destroy_cb (GtkWidget *widget,
1845 GtkAccessible *accessible)
1846 {
1847 gtk_accessible_set_widget (accessible, NULL);
1848 atk_object_notify_state_change (ATK_OBJECT (accessible), ATK_STATE_DEFUNCT, TRUE);
1849 }
1850
1851 static gboolean
1852 accessible_focus_cb (GtkWidget *widget,
1853 GdkEventFocus *event)
1854 {
1855 AtkObject* accessible = gtk_widget_get_accessible (widget);
1856 atk_object_notify_state_change (accessible, ATK_STATE_FOCUSED, event->in);
1857
1858 return FALSE;
1859 }
1860
1861 static void
1862 accessible_notify_cb (GObject *obj,
1863 GParamSpec *pspec)
1864 {
1865 GtkWidget* widget = GTK_WIDGET (obj);
1866 AtkObject* atk_obj = gtk_widget_get_accessible (widget);
1867 AtkState state;
1868 gboolean value;
1869
1870 if (strcmp (pspec->name, "visible") == 0) {
1871 state = ATK_STATE_VISIBLE;
1872 value = gtk_widget_get_visible (widget);
1873 } else if (strcmp (pspec->name, "sensitive") == 0) {
1874 state = ATK_STATE_SENSITIVE;
1875 value = gtk_widget_get_sensitive (widget);
1876
1877 atk_object_notify_state_change (atk_obj, ATK_STATE_ENABLED, value);
1878 } else {
1879 g_assert_not_reached ();
1880 }
1881
1882 atk_object_notify_state_change (atk_obj, state, value);
1883 }
1884
1885 /* Translate GtkWidget::size-allocate to AtkComponent::bounds-changed */
1886 static void
1887 accessible_size_allocate_cb (GtkWidget *widget,
1888 GtkAllocation *allocation)
1889 {
1890 AtkObject* accessible = gtk_widget_get_accessible (widget);
1891 AtkRectangle rect;
1892
1893 rect.x = allocation->x;
1894 rect.y = allocation->y;
1895 rect.width = allocation->width;
1896 rect.height = allocation->height;
1897
1898 g_signal_emit_by_name (accessible, "bounds_changed", &rect);
1899 }
1900
1901 /* Translate GtkWidget mapped state into AtkObject showing */
1902 static void
1903 accessible_map_cb (GtkWidget *widget)
1904 {
1905 AtkObject *accessible = gtk_widget_get_accessible (widget);
1906 atk_object_notify_state_change (accessible, ATK_STATE_SHOWING,
1907 gtk_widget_get_mapped (widget));
1908 }
1909
1910 static void
1911 eel_canvas_accessible_initialize (AtkObject *obj,
1912 gpointer data)
1913 {
1914 EelCanvas *canvas = data;
1915
1916 if (ATK_OBJECT_CLASS (accessible_parent_class)->initialize != NULL) {
1917 ATK_OBJECT_CLASS (accessible_parent_class)->initialize (obj, data);
1918 }
1919
1920 gtk_accessible_set_widget (GTK_ACCESSIBLE (obj), GTK_WIDGET (data));
1921 g_signal_connect (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (canvas)),
1922 "value_changed",
1923 G_CALLBACK (eel_canvas_accessible_adjustment_changed),
1924 obj);
1925 g_signal_connect (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (canvas)),
1926 "value_changed",
1927 G_CALLBACK (eel_canvas_accessible_adjustment_changed),
1928 obj);
1929
1930 obj->role = ATK_ROLE_LAYERED_PANE;
1931
1932 /* below adapted from gtkwidgetaccessible.c */
1933
1934 g_signal_connect_after (canvas, "destroy",
1935 G_CALLBACK (accessible_destroy_cb), obj);
1936 g_signal_connect_after (canvas, "focus-in-event",
1937 G_CALLBACK (accessible_focus_cb), NULL);
1938 g_signal_connect_after (canvas, "focus-out-event",
1939 G_CALLBACK (accessible_focus_cb), NULL);
1940 g_signal_connect (canvas, "notify::visible",
1941 G_CALLBACK (accessible_notify_cb), NULL);
1942 g_signal_connect (canvas, "notify::sensitive",
1943 G_CALLBACK (accessible_notify_cb), NULL);
1944 g_signal_connect (canvas, "size-allocate",
1945 G_CALLBACK (accessible_size_allocate_cb), NULL);
1946 g_signal_connect (canvas, "map",
1947 G_CALLBACK (accessible_map_cb), NULL);
1948 g_signal_connect (canvas, "unmap",
1949 G_CALLBACK (accessible_map_cb), NULL);
1950 }
1951
1952 static gint
1953 eel_canvas_accessible_get_n_children (AtkObject* obj)
1954 {
1955 GtkAccessible *accessible;
1956 GtkWidget *widget;
1957 EelCanvas *canvas;
1958 EelCanvasGroup *root_group;
1959
1960 accessible = GTK_ACCESSIBLE (obj);
1961 widget = gtk_accessible_get_widget (accessible);
1962
1963 if (widget == NULL) {
1964 return 0;
1965 }
1966
1967 g_return_val_if_fail (EEL_IS_CANVAS (widget), 0);
1968
1969 canvas = EEL_CANVAS (widget);
1970 root_group = eel_canvas_root (canvas);
1971 g_return_val_if_fail (root_group, 0);
1972
1973 return 1;
1974 }
1975
1976 static AtkObject*
1977 eel_canvas_accessible_ref_child (AtkObject *obj,
1978 gint i)
1979 {
1980 GtkAccessible *accessible;
1981 GtkWidget *widget;
1982 EelCanvas *canvas;
1983 EelCanvasGroup *root_group;
1984 AtkObject *atk_object;
1985
1986 /* Canvas only has one child, so return NULL if index is non zero */
1987 if (i != 0) {
1988 return NULL;
1989 }
1990
1991 accessible = GTK_ACCESSIBLE (obj);
1992 widget = gtk_accessible_get_widget (accessible);
1993
1994 if (widget == NULL) {
1995 return NULL;
1996 }
1997
1998 canvas = EEL_CANVAS (widget);
1999 root_group = eel_canvas_root (canvas);
2000 g_return_val_if_fail (root_group, NULL);
2001
2002 atk_object = atk_gobject_accessible_for_object (G_OBJECT (root_group));
2003
2004 return g_object_ref (atk_object);
2005 }
2006
2007 static gboolean
2008 eel_canvas_accessible_all_parents_visible (GtkWidget *widget)
2009 {
2010 GtkWidget *iter_parent = NULL;
2011 gboolean result = TRUE;
2012
2013 for (iter_parent = gtk_widget_get_parent (widget); iter_parent != NULL;
2014 iter_parent = gtk_widget_get_parent (iter_parent)) {
2015 if (!gtk_widget_get_visible (iter_parent)) {
2016 result = FALSE;
2017 break;
2018 }
2019 }
2020
2021 return result;
2022 }
2023
2024 static gboolean
2025 eel_canvas_accessible_on_screen (GtkWidget *widget)
2026 {
2027 GtkAllocation allocation;
2028 GtkWidget *viewport;
2029 gboolean return_value = TRUE;
2030
2031 gtk_widget_get_allocation (widget, &allocation);
2032
2033 viewport = gtk_widget_get_ancestor (widget, GTK_TYPE_VIEWPORT);
2034
2035 if (viewport) {
2036 GtkAllocation viewport_allocation;
2037 GtkAdjustment *adjustment;
2038 GdkRectangle visible_rect;
2039
2040 gtk_widget_get_allocation (viewport, &viewport_allocation);
2041
2042 adjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (viewport));
2043 visible_rect.y = gtk_adjustment_get_value (adjustment);
2044 adjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (viewport));
2045 visible_rect.x = gtk_adjustment_get_value (adjustment);
2046 visible_rect.width = viewport_allocation.width;
2047 visible_rect.height = viewport_allocation.height;
2048
2049 if (((allocation.x + allocation.width) < visible_rect.x) ||
2050 ((allocation.y + allocation.height) < visible_rect.y) ||
2051 (allocation.x > (visible_rect.x + visible_rect.width)) ||
2052 (allocation.y > (visible_rect.y + visible_rect.height))) {
2053 return_value = FALSE;
2054 }
2055 } else {
2056 /* Check whether the widget has been placed off the screen.
2057 * The widget may be MAPPED as when toolbar items do not
2058 * fit on the toolbar.
2059 */
2060 if (allocation.x + allocation.width <= 0 &&
2061 allocation.y + allocation.height <= 0) {
2062 return_value = FALSE;
2063 }
2064 }
2065
2066 return return_value;
2067 }
2068
2069 static AtkStateSet *
2070 eel_canvas_accessible_ref_state_set (AtkObject *accessible)
2071 {
2072 GtkWidget *widget;
2073 AtkStateSet *state_set;
2074
2075 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
2076 state_set = ATK_OBJECT_CLASS (accessible_parent_class)->ref_state_set (accessible);
2077
2078 if (widget == NULL) {
2079 atk_state_set_add_state (state_set, ATK_STATE_DEFUNCT);
2080 } else {
2081 if (gtk_widget_is_sensitive (widget)) {
2082 atk_state_set_add_state (state_set, ATK_STATE_SENSITIVE);
2083 atk_state_set_add_state (state_set, ATK_STATE_ENABLED);
2084 }
2085
2086 if (gtk_widget_get_can_focus (widget)) {
2087 atk_state_set_add_state (state_set, ATK_STATE_FOCUSABLE);
2088 }
2089 /*
2090 * We do not currently generate notifications when an ATK object
2091 * corresponding to a GtkWidget changes visibility by being scrolled
2092 * on or off the screen. The testcase for this is the main window
2093 * of the testgtk application in which a set of buttons in a GtkVBox
2094 * is in a scrolled window with a viewport.
2095 *
2096 * To generate the notifications we would need to do the following:
2097 * 1) Find the GtkViewport among the ancestors of the objects
2098 * 2) Create an accessible for the viewport
2099 * 3) Connect to the value-changed signal on the viewport
2100 * 4) When the signal is received we need to traverse the children
2101 * of the viewport and check whether the children are visible or not
2102 * visible; we may want to restrict this to the widgets for which
2103 * accessible objects have been created.
2104 * 5) We probably need to store a variable on_screen in the
2105 * GtkWidgetAccessible data structure so we can determine whether
2106 * the value has changed.
2107 */
2108 if (gtk_widget_get_visible (widget)) {
2109 atk_state_set_add_state (state_set, ATK_STATE_VISIBLE);
2110
2111 if (eel_canvas_accessible_on_screen (widget) &&
2112 gtk_widget_get_mapped (widget) &&
2113 eel_canvas_accessible_all_parents_visible (widget)) {
2114 atk_state_set_add_state (state_set, ATK_STATE_SHOWING);
2115 }
2116 }
2117
2118 if (gtk_widget_has_focus (widget)) {
2119 AtkObject *focus_obj;
2120
2121 focus_obj = g_object_get_data (G_OBJECT (accessible), "gail-focus-object");
2122 if (focus_obj == NULL) {
2123 atk_state_set_add_state (state_set, ATK_STATE_FOCUSED);
2124 }
2125 }
2126
2127 if (gtk_widget_has_default (widget)) {
2128 atk_state_set_add_state (state_set, ATK_STATE_DEFAULT);
2129 }
2130 }
2131 return state_set;
2132 }
2133
2134 static void
2135 eel_canvas_accessible_class_init (EelCanvasAccessibleClass *klass)
2136 {
2137 AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass);
2138
2139 accessible_parent_class = g_type_class_peek_parent (klass);
2140
2141 atk_class->initialize = eel_canvas_accessible_initialize;
2142 atk_class->get_n_children = eel_canvas_accessible_get_n_children;
2143 atk_class->ref_child = eel_canvas_accessible_ref_child;
2144 /* below adapted from gtkwidgetaccessible.c */
2145 atk_class->ref_state_set = eel_canvas_accessible_ref_state_set;
2146 }
2147
2148 static void
2149 eel_canvas_accessible_get_extents (AtkComponent *component,
2150 gint *x,
2151 gint *y,
2152 gint *width,
2153 gint *height,
2154 AtkCoordType coord_type)
2155 {
2156 GdkWindow *window;
2157 gint x_window, y_window;
2158 gint x_toplevel, y_toplevel;
2159 GtkWidget *widget;
2160 GtkAllocation allocation;
2161
2162 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
2163
2164 if (widget == NULL) {
2165 return;
2166 }
2167
2168 gtk_widget_get_allocation (widget, &allocation);
2169 *width = allocation.width;
2170 *height = allocation.height;
2171
2172 if (!eel_canvas_accessible_on_screen (widget) ||
2173 !gtk_widget_is_drawable (widget)) {
2174 *x = G_MININT;
2175 *y = G_MININT;
2176
2177 return;
2178 }
2179
2180 if (gtk_widget_get_parent (widget)) {
2181 *x = allocation.x;
2182 *y = allocation.y;
2183 window = gtk_widget_get_parent_window (widget);
2184 } else {
2185 *x = 0;
2186 *y = 0;
2187 window = gtk_widget_get_window (widget);
2188 }
2189
2190 gdk_window_get_origin (window, &x_window, &y_window);
2191 *x += x_window;
2192 *y += y_window;
2193
2194 if (coord_type == ATK_XY_WINDOW) {
2195 window = gdk_window_get_toplevel (gtk_widget_get_window (widget));
2196 gdk_window_get_origin (window, &x_toplevel, &y_toplevel);
2197
2198 *x -= x_toplevel;
2199 *y -= y_toplevel;
2200 }
2201 }
2202
2203 static void
2204 eel_canvas_accessible_get_size (AtkComponent *component,
2205 gint *width,
2206 gint *height)
2207 {
2208 GtkWidget *widget;
2209
2210 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
2211
2212 if (widget == NULL) {
2213 return;
2214 }
2215
2216 *width = gtk_widget_get_allocated_width (widget);
2217 *height = gtk_widget_get_allocated_height (widget);
2218 }
2219
2220 static void
2221 eel_canvas_accessible_component_init(gpointer iface, gpointer data)
2222 {
2223 AtkComponentIface *component;
2224
2225 g_assert (G_TYPE_FROM_INTERFACE(iface) == ATK_TYPE_COMPONENT);
2226
2227 component = iface;
2228 component->get_extents = eel_canvas_accessible_get_extents;
2229 component->get_size = eel_canvas_accessible_get_size;
2230 }
2231
2232 static void
2233 eel_canvas_accessible_init (EelCanvasAccessible *accessible)
2234 {
2235 }
2236
2237 G_DEFINE_TYPE_WITH_CODE (EelCanvasAccessible, eel_canvas_accessible, GTK_TYPE_ACCESSIBLE,
2238 G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT, eel_canvas_accessible_component_init))
2239
2240 static AtkObject *
2241 eel_canvas_accessible_create (GObject *for_object)
2242 {
2243 GType type;
2244 AtkObject *accessible;
2245 EelCanvas *canvas;
2246
2247 canvas = EEL_CANVAS (for_object);
2248 g_return_val_if_fail (canvas != NULL, NULL);
2249
2250 type = eel_canvas_accessible_get_type ();
2251
2252 if (type == G_TYPE_INVALID) {
2253 return atk_no_op_object_new (for_object);
2254 }
2255
2256 accessible = g_object_new (type, NULL);
2257 atk_object_initialize (accessible, for_object);
2258 return accessible;
2259 }
2260
2261 static GType
2262 eel_canvas_accessible_factory_get_accessible_type (void)
2263 {
2264 return eel_canvas_accessible_get_type ();
2265 }
2266
2267 static AtkObject*
2268 eel_canvas_accessible_factory_create_accessible (GObject *obj)
2269 {
2270 AtkObject *accessible;
2271
2272 g_return_val_if_fail (G_IS_OBJECT (obj), NULL);
2273
2274 accessible = eel_canvas_accessible_create (obj);
2275
2276 return accessible;
2277 }
2278
2279 static void
2280 eel_canvas_accessible_factory_class_init (AtkObjectFactoryClass *klass)
2281 {
2282 klass->create_accessible = eel_canvas_accessible_factory_create_accessible;
2283 klass->get_accessible_type = eel_canvas_accessible_factory_get_accessible_type;
2284 }
2285
2286 static GType
2287 eel_canvas_accessible_factory_get_type (void)
2288 {
2289 static GType type = 0;
2290
2291 if (!type) {
2292 static const GTypeInfo tinfo = {
2293 sizeof (AtkObjectFactoryClass),
2294 (GBaseInitFunc) NULL,
2295 (GBaseFinalizeFunc) NULL,
2296 (GClassInitFunc) eel_canvas_accessible_factory_class_init,
2297 NULL, /* class_finalize */
2298 NULL, /* class_data */
2299 sizeof (AtkObjectFactory),
2300 0, /* n_preallocs */
2301 NULL
2302 };
2303 type = g_type_register_static (ATK_TYPE_OBJECT_FACTORY,
2304 "EelCanvasAccessibilityFactory",
2305 &tinfo, 0);
2306 }
2307
2308 return type;
2309 }
2310
2311
2312 /* Class initialization function for EelCanvasClass */
2313 static void
2314 eel_canvas_class_init (EelCanvasClass *klass)
2315 {
2316 GObjectClass *gobject_class;
2317 GtkWidgetClass *widget_class;
2318
2319 gobject_class = (GObjectClass *)klass;
2320 widget_class = (GtkWidgetClass *) klass;
2321
2322 canvas_parent_class = g_type_class_peek_parent (klass);
2323
2324 gobject_class->set_property = eel_canvas_set_property;
2325 gobject_class->get_property = eel_canvas_get_property;
2326
2327 widget_class->destroy = eel_canvas_destroy;
2328 widget_class->map = eel_canvas_map;
2329 widget_class->unmap = eel_canvas_unmap;
2330 widget_class->realize = eel_canvas_realize;
2331 widget_class->unrealize = eel_canvas_unrealize;
2332 widget_class->size_allocate = eel_canvas_size_allocate;
2333 widget_class->button_press_event = eel_canvas_button;
2334 widget_class->button_release_event = eel_canvas_button;
2335 widget_class->motion_notify_event = eel_canvas_motion;
2336 widget_class->draw = eel_canvas_draw;
2337 widget_class->key_press_event = eel_canvas_key;
2338 widget_class->key_release_event = eel_canvas_key;
2339 widget_class->enter_notify_event = eel_canvas_crossing;
2340 widget_class->leave_notify_event = eel_canvas_crossing;
2341 widget_class->focus_in_event = eel_canvas_focus_in;
2342 widget_class->focus_out_event = eel_canvas_focus_out;
2343 widget_class->get_accessible = eel_canvas_get_accessible;
2344
2345 klass->draw_background = eel_canvas_draw_background;
2346 klass->request_update = eel_canvas_request_update_real;
2347
2348 canvas_signals[DRAW_BACKGROUND] =
2349 g_signal_new ("draw_background",
2350 G_TYPE_FROM_CLASS (klass),
2351 G_SIGNAL_RUN_LAST,
2352 G_STRUCT_OFFSET (EelCanvasClass, draw_background),
2353 NULL, NULL,
2354 g_cclosure_marshal_VOID__BOXED,
2355 G_TYPE_NONE, 1,
2356 CAIRO_GOBJECT_TYPE_CONTEXT);
2357
2358 atk_registry_set_factory_type (atk_get_default_registry (),
2359 EEL_TYPE_CANVAS,
2360 eel_canvas_accessible_factory_get_type ());
2361 }
2362
2363 /* Callback used when the root item of a canvas is destroyed. The user should
2364 * never ever do this, so we panic if this happens.
2365 */
2366 static void
2367 panic_root_destroyed (GtkWidget *object, gpointer data)
2368 {
2369 g_error ("Eeeek, root item %p of canvas %p was destroyed!", object, data);
2370 }
2371
2372 /* Object initialization function for EelCanvas */
2373 static void
2374 eel_canvas_init (EelCanvas *canvas)
2375 {
2376 guint width, height;
2377 gtk_widget_set_can_focus (GTK_WIDGET (canvas), TRUE);
2378
2379 gtk_widget_set_redraw_on_allocate (GTK_WIDGET (canvas), FALSE);
2380
2381 canvas->scroll_x1 = 0.0;
2382 canvas->scroll_y1 = 0.0;
2383 gtk_layout_get_size (GTK_LAYOUT (canvas),
2384 &width, &height);
2385 canvas->scroll_x2 = width;
2386 canvas->scroll_y2 = height;
2387
2388 canvas->pixels_per_unit = 1.0;
2389
2390 canvas->pick_event.type = GDK_LEAVE_NOTIFY;
2391 canvas->pick_event.crossing.x = 0;
2392 canvas->pick_event.crossing.y = 0;
2393
2394 gtk_scrollable_set_hadjustment (GTK_SCROLLABLE (canvas), NULL);
2395 gtk_scrollable_set_vadjustment (GTK_SCROLLABLE (canvas), NULL);
2396
2397 /* Create the root item as a special case */
2398
2399 canvas->root = EEL_CANVAS_ITEM (g_object_new (eel_canvas_group_get_type (), NULL));
2400 canvas->root->canvas = canvas;
2401
2402 g_object_ref_sink (canvas->root);
2403
2404 canvas->root_destroy_id = g_signal_connect (G_OBJECT (canvas->root),
2405 "destroy", G_CALLBACK (panic_root_destroyed), canvas);
2406
2407 canvas->need_repick = TRUE;
2408 canvas->doing_update = FALSE;
2409 }
2410
2411 /* Convenience function to remove the idle handler of a canvas */
2412 static void
2413 remove_idle (EelCanvas *canvas)
2414 {
2415 if (canvas->idle_id == 0)
2416 return;
2417
2418 g_source_remove (canvas->idle_id);
2419 canvas->idle_id = 0;
2420 }
2421
2422 /* Removes the transient state of the canvas (idle handler, grabs). */
2423 static void
2424 shutdown_transients (EelCanvas *canvas)
2425 {
2426 /* We turn off the need_redraw flag, since if the canvas is mapped again
2427 * it will request a redraw anyways. We do not turn off the need_update
2428 * flag, though, because updates are not queued when the canvas remaps
2429 * itself.
2430 */
2431 if (canvas->need_redraw) {
2432 canvas->need_redraw = FALSE;
2433 }
2434
2435 if (canvas->grabbed_item) {
2436 eel_canvas_item_ungrab (canvas->grabbed_item, GDK_CURRENT_TIME);
2437 }
2438
2439 remove_idle (canvas);
2440 }
2441
2442 /* Destroy handler for EelCanvas */
2443 static void
2444 eel_canvas_destroy (GtkWidget *object)
2445 {
2446 EelCanvas *canvas;
2447
2448 g_return_if_fail (EEL_IS_CANVAS (object));
2449
2450 /* remember, destroy can be run multiple times! */
2451
2452 canvas = EEL_CANVAS (object);
2453
2454 if (canvas->root_destroy_id) {
2455 g_signal_handler_disconnect (G_OBJECT (canvas->root), canvas->root_destroy_id);
2456 canvas->root_destroy_id = 0;
2457 }
2458 if (canvas->root) {
2459 EelCanvasItem *root = canvas->root;
2460 canvas->root = NULL;
2461 eel_canvas_item_destroy (root);
2462 g_object_unref (root);
2463 }
2464
2465 shutdown_transients (canvas);
2466
2467 if (GTK_WIDGET_CLASS (canvas_parent_class)->destroy)
2468 (* GTK_WIDGET_CLASS (canvas_parent_class)->destroy) (object);
2469 }
2470
2471 /**
2472 * eel_canvas_new:
2473 * @void:
2474 *
2475 * Creates a new empty canvas. If you wish to use the
2476 * &EelCanvasImage item inside this canvas, then you must push the gdk_imlib
2477 * visual and colormap before calling this function, and they can be popped
2478 * afterwards.
2479 *
2480 * Return value: A newly-created canvas.
2481 **/
2482 GtkWidget *
2483 eel_canvas_new (void)
2484 {
2485 return GTK_WIDGET (g_object_new (eel_canvas_get_type (), NULL));
2486 }
2487
2488 /* Map handler for the canvas */
2489 static void
2490 eel_canvas_map (GtkWidget *widget)
2491 {
2492 EelCanvas *canvas;
2493
2494 g_return_if_fail (EEL_IS_CANVAS (widget));
2495
2496 /* Normal widget mapping stuff */
2497
2498 if (GTK_WIDGET_CLASS (canvas_parent_class)->map)
2499 (* GTK_WIDGET_CLASS (canvas_parent_class)->map) (widget);
2500
2501 canvas = EEL_CANVAS (widget);
2502
2503 /* Map items */
2504
2505 if (canvas->root->flags & EEL_CANVAS_ITEM_VISIBLE &&
2506 !(canvas->root->flags & EEL_CANVAS_ITEM_MAPPED) &&
2507 EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->map)
2508 (* EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->map) (canvas->root);
2509 }
2510
2511 /* Unmap handler for the canvas */
2512 static void
2513 eel_canvas_unmap (GtkWidget *widget)
2514 {
2515 EelCanvas *canvas;
2516
2517 g_return_if_fail (EEL_IS_CANVAS (widget));
2518
2519 canvas = EEL_CANVAS (widget);
2520
2521 shutdown_transients (canvas);
2522
2523 /* Unmap items */
2524
2525 if (EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->unmap)
2526 (* EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->unmap) (canvas->root);
2527
2528 /* Normal widget unmapping stuff */
2529
2530 if (GTK_WIDGET_CLASS (canvas_parent_class)->unmap)
2531 (* GTK_WIDGET_CLASS (canvas_parent_class)->unmap) (widget);
2532 }
2533
2534 /* Realize handler for the canvas */
2535 static void
2536 eel_canvas_realize (GtkWidget *widget)
2537 {
2538 EelCanvas *canvas;
2539
2540 g_return_if_fail (EEL_IS_CANVAS (widget));
2541
2542 /* Normal widget realization stuff */
2543
2544 if (GTK_WIDGET_CLASS (canvas_parent_class)->realize)
2545 (* GTK_WIDGET_CLASS (canvas_parent_class)->realize) (widget);
2546
2547 canvas = EEL_CANVAS (widget);
2548
2549 gdk_window_set_events (gtk_layout_get_bin_window (GTK_LAYOUT (canvas)),
2550 (gdk_window_get_events (gtk_layout_get_bin_window (GTK_LAYOUT (canvas)))
2551 | GDK_EXPOSURE_MASK
2552 | GDK_BUTTON_PRESS_MASK
2553 | GDK_BUTTON_RELEASE_MASK
2554 | GDK_POINTER_MOTION_MASK
2555 | GDK_KEY_PRESS_MASK
2556 | GDK_KEY_RELEASE_MASK
2557 | GDK_ENTER_NOTIFY_MASK
2558 | GDK_LEAVE_NOTIFY_MASK
2559 | GDK_FOCUS_CHANGE_MASK));
2560
2561 /* Create our own temporary pixmap gc and realize all the items */
2562
2563 (* EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->realize) (canvas->root);
2564 }
2565
2566 /* Unrealize handler for the canvas */
2567 static void
2568 eel_canvas_unrealize (GtkWidget *widget)
2569 {
2570 EelCanvas *canvas;
2571
2572 g_return_if_fail (EEL_IS_CANVAS (widget));
2573
2574 canvas = EEL_CANVAS (widget);
2575
2576 shutdown_transients (canvas);
2577
2578 /* Unrealize items and parent widget */
2579
2580 (* EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->unrealize) (canvas->root);
2581
2582 if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
2583 (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
2584 }
2585
2586 /* Handles scrolling of the canvas. Adjusts the scrolling and zooming offset to
2587 * keep as much as possible of the canvas scrolling region in view.
2588 */
2589 static void
2590 scroll_to (EelCanvas *canvas, int cx, int cy)
2591 {
2592 int scroll_width, scroll_height;
2593 int right_limit, bottom_limit;
2594 int old_zoom_xofs, old_zoom_yofs;
2595 int changed_x = FALSE, changed_y = FALSE;
2596 int canvas_width, canvas_height;
2597 GtkAllocation allocation;
2598 GtkAdjustment *vadjustment, *hadjustment;
2599 guint width, height;
2600
2601 gtk_widget_get_allocation (GTK_WIDGET (canvas), &allocation);
2602 canvas_width = allocation.width;
2603 canvas_height = allocation.height;
2604
2605 scroll_width = floor ((canvas->scroll_x2 - canvas->scroll_x1) * canvas->pixels_per_unit + 0.5);
2606 scroll_height = floor ((canvas->scroll_y2 - canvas->scroll_y1) * canvas->pixels_per_unit + 0.5);
2607
2608 right_limit = scroll_width - canvas_width;
2609 bottom_limit = scroll_height - canvas_height;
2610
2611 old_zoom_xofs = canvas->zoom_xofs;
2612 old_zoom_yofs = canvas->zoom_yofs;
2613
2614 if (right_limit < 0) {
2615 cx = 0;
2616 if (canvas->center_scroll_region) {
2617 canvas->zoom_xofs = (canvas_width - scroll_width) / 2;
2618 scroll_width = canvas_width;
2619 } else {
2620 canvas->zoom_xofs = 0;
2621 }
2622 } else if (cx < 0) {
2623 cx = 0;
2624 canvas->zoom_xofs = 0;
2625 } else if (cx > right_limit) {
2626 cx = right_limit;
2627 canvas->zoom_xofs = 0;
2628 } else
2629 canvas->zoom_xofs = 0;
2630
2631 if (bottom_limit < 0) {
2632 cy = 0;
2633 if (canvas->center_scroll_region) {
2634 canvas->zoom_yofs = (canvas_height - scroll_height) / 2;
2635 scroll_height = canvas_height;
2636 } else {
2637 canvas->zoom_yofs = 0;
2638 }
2639 } else if (cy < 0) {
2640 cy = 0;
2641 canvas->zoom_yofs = 0;
2642 } else if (cy > bottom_limit) {
2643 cy = bottom_limit;
2644 canvas->zoom_yofs = 0;
2645 } else
2646 canvas->zoom_yofs = 0;
2647
2648 if ((canvas->zoom_xofs != old_zoom_xofs) || (canvas->zoom_yofs != old_zoom_yofs)) {
2649 /* This can only occur, if either canvas size or widget size changes */
2650 /* So I think we can request full redraw here */
2651 /* More stuff - we have to mark root as needing fresh affine (Lauris) */
2652 if (!(canvas->root->flags & EEL_CANVAS_ITEM_NEED_DEEP_UPDATE)) {
2653 canvas->root->flags |= EEL_CANVAS_ITEM_NEED_DEEP_UPDATE;
2654 eel_canvas_request_update (canvas);
2655 }
2656 gtk_widget_queue_draw (GTK_WIDGET (canvas));
2657 }
2658
2659 hadjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (canvas));
2660 vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (canvas));
2661
2662 if (((int) gtk_adjustment_get_value (hadjustment)) != cx) {
2663 gtk_adjustment_set_value (hadjustment, cx);
2664 changed_x = TRUE;
2665 }
2666
2667 if (((int) gtk_adjustment_get_value (vadjustment)) != cy) {
2668 gtk_adjustment_set_value (vadjustment, cy);
2669 changed_y = TRUE;
2670 }
2671
2672 gtk_layout_get_size (&canvas->layout, &width, &height);
2673 if ((scroll_width != (int) width )|| (scroll_height != (int) height)) {
2674 gtk_layout_set_size (GTK_LAYOUT (canvas), scroll_width, scroll_height);
2675 }
2676
2677 /* Signal GtkLayout that it should do a redraw. */
2678 if (changed_x)
2679 g_signal_emit_by_name (hadjustment, "value_changed");
2680 if (changed_y)
2681 g_signal_emit_by_name (vadjustment, "value_changed");
2682 }
2683
2684 /* Size allocation handler for the canvas */
2685 static void
2686 eel_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
2687 {
2688 EelCanvas *canvas;
2689 GtkAdjustment *vadjustment, *hadjustment;
2690
2691 g_return_if_fail (EEL_IS_CANVAS (widget));
2692 g_return_if_fail (allocation != NULL);
2693
2694 if (GTK_WIDGET_CLASS (canvas_parent_class)->size_allocate)
2695 (* GTK_WIDGET_CLASS (canvas_parent_class)->size_allocate) (widget, allocation);
2696
2697 canvas = EEL_CANVAS (widget);
2698
2699 /* Recenter the view, if appropriate */
2700
2701 hadjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (canvas));
2702 vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (canvas));
2703
2704 gtk_adjustment_set_page_size (hadjustment, allocation->width);
2705 gtk_adjustment_set_page_increment (hadjustment, allocation->width / 2);
2706
2707 gtk_adjustment_set_page_size (vadjustment, allocation->height);
2708 gtk_adjustment_set_page_increment (vadjustment, allocation->height / 2);
2709
2710 scroll_to (canvas,
2711 gtk_adjustment_get_value (hadjustment),
2712 gtk_adjustment_get_value (vadjustment));
2713
2714 g_signal_emit_by_name (hadjustment, "changed");
2715 g_signal_emit_by_name (vadjustment, "changed");
2716 }
2717
2718 /* Emits an event for an item in the canvas, be it the current item, grabbed
2719 * item, or focused item, as appropriate.
2720 */
2721
2722 static int
2723 emit_event (EelCanvas *canvas, GdkEvent *event)
2724 {
2725 GdkEvent ev;
2726 gint finished;
2727 EelCanvasItem *item;
2728 EelCanvasItem *parent;
2729 guint mask;
2730
2731 /* Could be an old pick event */
2732 if (!gtk_widget_get_realized (GTK_WIDGET (canvas))) {
2733 return FALSE;
2734 }
2735
2736 /* Perform checks for grabbed items */
2737
2738 if (canvas->grabbed_item &&
2739 !is_descendant (canvas->current_item, canvas->grabbed_item)) {
2740 return FALSE;
2741 }
2742
2743 if (canvas->grabbed_item) {
2744 switch (event->type) {
2745 case GDK_ENTER_NOTIFY:
2746 mask = GDK_ENTER_NOTIFY_MASK;
2747 break;
2748
2749 case GDK_LEAVE_NOTIFY:
2750 mask = GDK_LEAVE_NOTIFY_MASK;
2751 break;
2752
2753 case GDK_MOTION_NOTIFY:
2754 mask = GDK_POINTER_MOTION_MASK;
2755 break;
2756
2757 case GDK_BUTTON_PRESS:
2758 case GDK_2BUTTON_PRESS:
2759 case GDK_3BUTTON_PRESS:
2760 mask = GDK_BUTTON_PRESS_MASK;
2761 break;
2762
2763 case GDK_BUTTON_RELEASE:
2764 mask = GDK_BUTTON_RELEASE_MASK;
2765 break;
2766
2767 case GDK_KEY_PRESS:
2768 mask = GDK_KEY_PRESS_MASK;
2769 break;
2770
2771 case GDK_KEY_RELEASE:
2772 mask = GDK_KEY_RELEASE_MASK;
2773 break;
2774
2775 default:
2776 mask = 0;
2777 break;
2778 }
2779
2780 if (!(mask & canvas->grabbed_event_mask))
2781 return FALSE;
2782 }
2783
2784 /* Convert to world coordinates -- we have two cases because of diferent
2785 * offsets of the fields in the event structures.
2786 */
2787
2788 ev = *event;
2789
2790 switch (ev.type)
2791 {
2792 case GDK_ENTER_NOTIFY:
2793 case GDK_LEAVE_NOTIFY:
2794 eel_canvas_window_to_world (canvas,
2795 ev.crossing.x, ev.crossing.y,
2796 &ev.crossing.x, &ev.crossing.y);
2797 break;
2798
2799 case GDK_MOTION_NOTIFY:
2800 eel_canvas_window_to_world (canvas,
2801 ev.motion.x, ev.motion.y,
2802 &ev.motion.x, &ev.motion.y);
2803 break;
2804
2805 case GDK_BUTTON_PRESS:
2806 case GDK_2BUTTON_PRESS:
2807 case GDK_3BUTTON_PRESS:
2808 eel_canvas_window_to_world (canvas,
2809 ev.motion.x, ev.motion.y,
2810 &ev.motion.x, &ev.motion.y);
2811 break;
2812
2813 case GDK_BUTTON_RELEASE:
2814 eel_canvas_window_to_world (canvas,
2815 ev.motion.x, ev.motion.y,
2816 &ev.motion.x, &ev.motion.y);
2817 break;
2818
2819 default:
2820 break;
2821 }
2822
2823 /* Choose where we send the event */
2824
2825 item = canvas->current_item;
2826
2827 if (canvas->focused_item
2828 && ((event->type == GDK_KEY_PRESS) ||
2829 (event->type == GDK_KEY_RELEASE) ||
2830 (event->type == GDK_FOCUS_CHANGE)))
2831 item = canvas->focused_item;
2832
2833 /* The event is propagated up the hierarchy (for if someone connected to
2834 * a group instead of a leaf event), and emission is stopped if a
2835 * handler returns TRUE, just like for GtkWidget events.
2836 */
2837
2838 finished = FALSE;
2839
2840 while (item && !finished) {
2841 g_object_ref (item);
2842
2843 g_signal_emit (
2844 G_OBJECT (item), item_signals[ITEM_EVENT], 0,
2845 &ev, &finished);
2846
2847 parent = item->parent;
2848 g_object_unref (item);
2849
2850 item = parent;
2851 }
2852
2853 return finished;
2854 }
2855
2856 /* Re-picks the current item in the canvas, based on the event's coordinates.
2857 * Also emits enter/leave events for items as appropriate.
2858 */
2859 static int
2860 pick_current_item (EelCanvas *canvas, GdkEvent *event)
2861 {
2862 int button_down;
2863 double x, y;
2864 int cx, cy;
2865 int retval;
2866
2867 retval = FALSE;
2868
2869 /* If a button is down, we'll perform enter and leave events on the
2870 * current item, but not enter on any other item. This is more or less
2871 * like X pointer grabbing for canvas items.
2872 */
2873 button_down = canvas->state & (GDK_BUTTON1_MASK
2874 | GDK_BUTTON2_MASK
2875 | GDK_BUTTON3_MASK
2876 | GDK_BUTTON4_MASK
2877 | GDK_BUTTON5_MASK);
2878 if (!button_down)
2879 canvas->left_grabbed_item = FALSE;
2880
2881 /* Save the event in the canvas. This is used to synthesize enter and
2882 * leave events in case the current item changes. It is also used to
2883 * re-pick the current item if the current one gets deleted. Also,
2884 * synthesize an enter event.
2885 */
2886 if (event != &canvas->pick_event) {
2887 if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
2888 /* these fields have the same offsets in both types of events */
2889
2890 canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
2891 canvas->pick_event.crossing.window = event->motion.window;
2892 canvas->pick_event.crossing.send_event = event->motion.send_event;
2893 canvas->pick_event.crossing.subwindow = NULL;
2894 canvas->pick_event.crossing.x = event->motion.x;
2895 canvas->pick_event.crossing.y = event->motion.y;
2896 canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
2897 canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
2898 canvas->pick_event.crossing.focus = FALSE;
2899 canvas->pick_event.crossing.state = event->motion.state;
2900
2901 /* these fields don't have the same offsets in both types of events */
2902
2903 if (event->type == GDK_MOTION_NOTIFY) {
2904 canvas->pick_event.crossing.x_root = event->motion.x_root;
2905 canvas->pick_event.crossing.y_root = event->motion.y_root;
2906 } else {
2907 canvas->pick_event.crossing.x_root = event->button.x_root;
2908 canvas->pick_event.crossing.y_root = event->button.y_root;
2909 }
2910 } else
2911 canvas->pick_event = *event;
2912 }
2913
2914 /* Don't do anything else if this is a recursive call */
2915
2916 if (canvas->in_repick)
2917 return retval;
2918
2919 /* LeaveNotify means that there is no current item, so we don't look for one */
2920
2921 if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
2922 /* these fields don't have the same offsets in both types of events */
2923
2924 if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
2925 x = canvas->pick_event.crossing.x;
2926 y = canvas->pick_event.crossing.y;
2927 } else {
2928 x = canvas->pick_event.motion.x;
2929 y = canvas->pick_event.motion.y;
2930 }
2931
2932 /* canvas pixel coords */
2933
2934 cx = (int) (x + 0.5);
2935 cy = (int) (y + 0.5);
2936
2937 /* world coords */
2938 eel_canvas_c2w (canvas, cx, cy, &x, &y);
2939
2940 /* find the closest item */
2941 if (canvas->root->flags & EEL_CANVAS_ITEM_MAPPED)
2942 eel_canvas_item_invoke_point (canvas->root, x, y, cx, cy,
2943 &canvas->new_current_item);
2944 else
2945 canvas->new_current_item = NULL;
2946 } else
2947 canvas->new_current_item = NULL;
2948
2949 if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item)
2950 return retval; /* current item did not change */
2951
2952 /* Synthesize events for old and new current items */
2953
2954 if ((canvas->new_current_item != canvas->current_item)
2955 && (canvas->current_item != NULL)
2956 && !canvas->left_grabbed_item) {
2957 GdkEvent new_event;
2958
2959 new_event = canvas->pick_event;
2960 new_event.type = GDK_LEAVE_NOTIFY;
2961
2962 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
2963 new_event.crossing.subwindow = NULL;
2964 canvas->in_repick = TRUE;
2965 retval = emit_event (canvas, &new_event);
2966 canvas->in_repick = FALSE;
2967 }
2968
2969 /* new_current_item may have been set to NULL during the call to emit_event() above */
2970
2971 if ((canvas->new_current_item != canvas->current_item) && button_down) {
2972 canvas->left_grabbed_item = TRUE;
2973 return retval;
2974 }
2975
2976 /* Handle the rest of cases */
2977
2978 canvas->left_grabbed_item = FALSE;
2979 canvas->current_item = canvas->new_current_item;
2980
2981 if (canvas->current_item != NULL) {
2982 GdkEvent new_event;
2983
2984 new_event = canvas->pick_event;
2985 new_event.type = GDK_ENTER_NOTIFY;
2986 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
2987 new_event.crossing.subwindow = NULL;
2988 retval = emit_event (canvas, &new_event);
2989 }
2990
2991 return retval;
2992 }
2993
2994 /* Button event handler for the canvas */
2995 static gint
2996 eel_canvas_button (GtkWidget *widget, GdkEventButton *event)
2997 {
2998 EelCanvas *canvas;
2999 int mask;
3000 int retval;
3001
3002 g_return_val_if_fail (EEL_IS_CANVAS (widget), FALSE);
3003 g_return_val_if_fail (event != NULL, FALSE);
3004
3005 retval = FALSE;
3006
3007 canvas = EEL_CANVAS (widget);
3008
3009 /* Don't handle extra mouse button events */
3010 if (event->button > 5)
3011 return FALSE;
3012
3013 /*
3014 * dispatch normally regardless of the event's window if an item has
3015 * has a pointer grab in effect
3016 */
3017 if (!canvas->grabbed_item && event->window != gtk_layout_get_bin_window (GTK_LAYOUT (canvas)))
3018 return retval;
3019
3020 switch (event->button) {
3021 case 1:
3022 mask = GDK_BUTTON1_MASK;
3023 break;
3024 case 2:
3025 mask = GDK_BUTTON2_MASK;
3026 break;
3027 case 3:
3028 mask = GDK_BUTTON3_MASK;
3029 break;
3030 case 4:
3031 mask = GDK_BUTTON4_MASK;
3032 break;
3033 case 5:
3034 mask = GDK_BUTTON5_MASK;
3035 break;
3036 default:
3037 mask = 0;
3038 }
3039
3040 switch (event->type) {
3041 case GDK_BUTTON_PRESS:
3042 case GDK_2BUTTON_PRESS:
3043 case GDK_3BUTTON_PRESS:
3044 /* Pick the current item as if the button were not pressed, and
3045 * then process the event.
3046 */
3047 canvas->state = event->state;
3048 pick_current_item (canvas, (GdkEvent *) event);
3049 canvas->state ^= mask;
3050 retval = emit_event (canvas, (GdkEvent *) event);
3051 break;
3052
3053 case GDK_BUTTON_RELEASE:
3054 /* Process the event as if the button were pressed, then repick
3055 * after the button has been released
3056 */
3057 canvas->state = event->state;
3058 retval = emit_event (canvas, (GdkEvent *) event);
3059 event->state ^= mask;
3060 canvas->state = event->state;
3061 pick_current_item (canvas, (GdkEvent *) event);
3062 event->state ^= mask;
3063 break;
3064
3065 default:
3066 g_assert_not_reached ();
3067 }
3068
3069 return retval;
3070 }
3071
3072 /* Motion event handler for the canvas */
3073 static gint
3074 eel_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
3075 {
3076 EelCanvas *canvas;
3077
3078 g_return_val_if_fail (EEL_IS_CANVAS (widget), FALSE);
3079 g_return_val_if_fail (event != NULL, FALSE);
3080
3081 canvas = EEL_CANVAS (widget);
3082
3083 if (event->window != gtk_layout_get_bin_window (GTK_LAYOUT (canvas)))
3084 return FALSE;
3085
3086 canvas->state = event->state;
3087 pick_current_item (canvas, (GdkEvent *) event);
3088 return emit_event (canvas, (GdkEvent *) event);
3089 }
3090
3091 /* Key event handler for the canvas */
3092 static gint
3093 eel_canvas_key (GtkWidget *widget, GdkEventKey *event)
3094 {
3095 EelCanvas *canvas;
3096
3097 g_return_val_if_fail (EEL_IS_CANVAS (widget), FALSE);
3098 g_return_val_if_fail (event != NULL, FALSE);
3099
3100 canvas = EEL_CANVAS (widget);
3101
3102 if (emit_event (canvas, (GdkEvent *) event))
3103 return TRUE;
3104 if (event->type == GDK_KEY_RELEASE)
3105 return GTK_WIDGET_CLASS (canvas_parent_class)->key_release_event (widget, event);
3106 else
3107 return GTK_WIDGET_CLASS (canvas_parent_class)->key_press_event (widget, event);
3108 }
3109
3110
3111 /* Crossing event handler for the canvas */
3112 static gint
3113 eel_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
3114 {
3115 EelCanvas *canvas;
3116
3117 g_return_val_if_fail (EEL_IS_CANVAS (widget), FALSE);
3118 g_return_val_if_fail (event != NULL, FALSE);
3119
3120 canvas = EEL_CANVAS (widget);
3121
3122 if (event->window != gtk_layout_get_bin_window (GTK_LAYOUT (canvas)))
3123 return FALSE;
3124
3125 canvas->state = event->state;
3126 return pick_current_item (canvas, (GdkEvent *) event);
3127 }
3128
3129 /* Focus in handler for the canvas */
3130 static gint
3131 eel_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
3132 {
3133 EelCanvas *canvas;
3134
3135 canvas = EEL_CANVAS (widget);
3136
3137 if (canvas->focused_item)
3138 return emit_event (canvas, (GdkEvent *) event);
3139 else
3140 return FALSE;
3141 }
3142
3143 static AtkObject *
3144 eel_canvas_get_accessible (GtkWidget *widget)
3145 {
3146 return atk_gobject_accessible_for_object (G_OBJECT (widget));
3147 }
3148
3149 /* Focus out handler for the canvas */
3150 static gint
3151 eel_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
3152 {
3153 EelCanvas *canvas;
3154
3155 canvas = EEL_CANVAS (widget);
3156
3157 if (canvas->focused_item)
3158 return emit_event (canvas, (GdkEvent *) event);
3159 else
3160 return FALSE;
3161 }
3162
3163
3164 static cairo_region_t *
3165 eel_cairo_get_clip_region (cairo_t *cr)
3166 {
3167 cairo_rectangle_list_t *list;
3168 cairo_region_t *region;
3169 int i;
3170
3171 list = cairo_copy_clip_rectangle_list (cr);
3172 if (list->status == CAIRO_STATUS_CLIP_NOT_REPRESENTABLE) {
3173 cairo_rectangle_int_t clip_rect;
3174
3175 cairo_rectangle_list_destroy (list);
3176
3177 if (!gdk_cairo_get_clip_rectangle (cr, &clip_rect))
3178 return NULL;
3179 return cairo_region_create_rectangle (&clip_rect);
3180 }
3181
3182
3183 region = cairo_region_create ();
3184 for (i = list->num_rectangles - 1; i >= 0; --i) {
3185 cairo_rectangle_t *rect = &list->rectangles[i];
3186 cairo_rectangle_int_t clip_rect;
3187
3188 clip_rect.x = floor (rect->x);
3189 clip_rect.y = floor (rect->y);
3190 clip_rect.width = ceil (rect->x + rect->width) - clip_rect.x;
3191 clip_rect.height = ceil (rect->y + rect->height) - clip_rect.y;
3192
3193 if (cairo_region_union_rectangle (region, &clip_rect) != CAIRO_STATUS_SUCCESS) {
3194 cairo_region_destroy (region);
3195 region = NULL;
3196 break;
3197 }
3198 }
3199
3200 cairo_rectangle_list_destroy (list);
3201 return region;
3202 }
3203
3204 /* Expose handler for the canvas */
3205 static gboolean
3206 eel_canvas_draw (GtkWidget *widget, cairo_t *cr)
3207 {
3208 EelCanvas *canvas = EEL_CANVAS (widget);
3209 GdkWindow *bin_window;
3210 cairo_region_t *region;
3211
3212 if (!gdk_cairo_get_clip_rectangle (cr, NULL))
3213 return FALSE;
3214
3215 bin_window = gtk_layout_get_bin_window (GTK_LAYOUT (widget));
3216 gtk_cairo_transform_to_window (cr, widget, bin_window);
3217
3218 region = eel_cairo_get_clip_region (cr);
3219 if (region == NULL)
3220 return FALSE;
3221
3222 #ifdef VERBOSE
3223 g_print ("Draw\n");
3224 #endif
3225 /* If there are any outstanding items that need updating, do them now */
3226 if (canvas->idle_id) {
3227 g_source_remove (canvas->idle_id);
3228 canvas->idle_id = 0;
3229 }
3230 if (canvas->need_update) {
3231 g_return_val_if_fail (!canvas->doing_update, FALSE);
3232
3233 canvas->doing_update = TRUE;
3234 eel_canvas_item_invoke_update (canvas->root, 0, 0, 0);
3235
3236 g_return_val_if_fail (canvas->doing_update, FALSE);
3237
3238 canvas->doing_update = FALSE;
3239
3240 canvas->need_update = FALSE;
3241 }
3242
3243 /* Hmmm. Would like to queue antiexposes if the update marked
3244 anything that is gonna get redrawn as invalid */
3245
3246 g_signal_emit (G_OBJECT (canvas), canvas_signals[DRAW_BACKGROUND], 0,
3247 cr);
3248
3249 if (canvas->root->flags & EEL_CANVAS_ITEM_MAPPED)
3250 EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->draw (canvas->root, cr, region);
3251
3252 /* Chain up to get exposes on child widgets */
3253 if (GTK_WIDGET_CLASS (canvas_parent_class)->draw)
3254 GTK_WIDGET_CLASS (canvas_parent_class)->draw (widget, cr);
3255
3256 cairo_region_destroy (region);
3257 return FALSE;
3258 }
3259
3260 static void
3261 eel_canvas_draw_background (EelCanvas *canvas,
3262 cairo_t *cr)
3263 {
3264 cairo_rectangle_int_t rect;
3265 GtkStyleContext *style_context;
3266 GdkRGBA color;
3267
3268 if (!gdk_cairo_get_clip_rectangle (cr, &rect))
3269 return;
3270
3271 cairo_save (cr);
3272 /* By default, we use the style background. */
3273 style_context = gtk_widget_get_style_context (GTK_WIDGET (canvas));
3274 gtk_style_context_get_background_color (style_context, GTK_STATE_FLAG_NORMAL, &color);
3275 gdk_cairo_set_source_rgba (cr, &color);
3276 gdk_cairo_rectangle (cr, &rect);
3277 cairo_fill (cr);
3278 cairo_restore (cr);
3279 }
3280
3281 static void
3282 do_update (EelCanvas *canvas)
3283 {
3284 /* Cause the update if necessary */
3285
3286 update_again:
3287 if (canvas->need_update) {
3288 g_return_if_fail (!canvas->doing_update);
3289
3290 canvas->doing_update = TRUE;
3291 eel_canvas_item_invoke_update (canvas->root, 0, 0, 0);
3292
3293 g_return_if_fail (canvas->doing_update);
3294
3295 canvas->doing_update = FALSE;
3296
3297 canvas->need_update = FALSE;
3298 }
3299
3300 /* Pick new current item */
3301
3302 while (canvas->need_repick) {
3303 canvas->need_repick = FALSE;
3304 pick_current_item (canvas, &canvas->pick_event);
3305 }
3306
3307 /* it is possible that during picking we emitted an event in which
3308 the user then called some function which then requested update
3309 of something. Without this we'd be left in a state where
3310 need_update would have been left TRUE and the canvas would have
3311 been left unpainted. */
3312 if (canvas->need_update) {
3313 goto update_again;
3314 }
3315 }
3316
3317 /* Idle handler for the canvas. It deals with pending updates and redraws. */
3318 static gint
3319 idle_handler (gpointer data)
3320 {
3321 EelCanvas *canvas;
3322
3323 canvas = EEL_CANVAS (data);
3324 do_update (canvas);
3325
3326 /* Reset idle id */
3327 canvas->idle_id = 0;
3328
3329 return FALSE;
3330 }
3331
3332 /* Convenience function to add an idle handler to a canvas */
3333 static void
3334 add_idle (EelCanvas *canvas)
3335 {
3336 if (!canvas->idle_id) {
3337 /* We let the update idle handler have higher priority
3338 * than the redraw idle handler so the canvas state
3339 * will be updated during the expose event. canvas in
3340 * expose_event.
3341 */
3342 canvas->idle_id = g_idle_add_full (GDK_PRIORITY_REDRAW - 20,
3343 idle_handler, canvas, NULL);
3344 }
3345 }
3346
3347 /**
3348 * eel_canvas_root:
3349 * @canvas: A canvas.
3350 *
3351 * Queries the root group of a canvas.
3352 *
3353 * Return value: The root group of the specified canvas.
3354 **/
3355 EelCanvasGroup *
3356 eel_canvas_root (EelCanvas *canvas)
3357 {
3358 g_return_val_if_fail (EEL_IS_CANVAS (canvas), NULL);
3359
3360 return EEL_CANVAS_GROUP (canvas->root);
3361 }
3362
3363
3364 /**
3365 * eel_canvas_set_scroll_region:
3366 * @canvas: A canvas.
3367 * @x1: Leftmost limit of the scrolling region.
3368 * @y1: Upper limit of the scrolling region.
3369 * @x2: Rightmost limit of the scrolling region.
3370 * @y2: Lower limit of the scrolling region.
3371 *
3372 * Sets the scrolling region of a canvas to the specified rectangle. The canvas
3373 * will then be able to scroll only within this region. The view of the canvas
3374 * is adjusted as appropriate to display as much of the new region as possible.
3375 **/
3376 void
3377 eel_canvas_set_scroll_region (EelCanvas *canvas, double x1, double y1, double x2, double y2)
3378 {
3379 double wxofs, wyofs;
3380 int xofs, yofs;
3381 GtkAdjustment *vadjustment, *hadjustment;
3382
3383 g_return_if_fail (EEL_IS_CANVAS (canvas));
3384
3385 if ((canvas->scroll_x1 == x1) && (canvas->scroll_y1 == y1) &&
3386 (canvas->scroll_x2 == x2) && (canvas->scroll_y2 == y2)) {
3387 return;
3388 }
3389
3390 /*
3391 * Set the new scrolling region. If possible, do not move the visible contents of the
3392 * canvas.
3393 */
3394 hadjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (canvas));
3395 vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (canvas));
3396
3397 eel_canvas_c2w (canvas,
3398 gtk_adjustment_get_value (hadjustment) + canvas->zoom_xofs,
3399 gtk_adjustment_get_value (vadjustment) + canvas->zoom_yofs,
3400 /*canvas->zoom_xofs,
3401 canvas->zoom_yofs,*/
3402 &wxofs, &wyofs);
3403
3404 canvas->scroll_x1 = x1;
3405 canvas->scroll_y1 = y1;
3406 canvas->scroll_x2 = x2;
3407 canvas->scroll_y2 = y2;
3408
3409 eel_canvas_w2c (canvas, wxofs, wyofs, &xofs, &yofs);
3410
3411 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)
3412
3413 canvas->need_repick = TRUE;
3414
3415 if (!(canvas->root->flags & EEL_CANVAS_ITEM_NEED_DEEP_UPDATE)) {
3416 canvas->root->flags |= EEL_CANVAS_ITEM_NEED_DEEP_UPDATE;
3417 eel_canvas_request_update (canvas);
3418 }
3419 }
3420
3421
3422 /**
3423 * eel_canvas_get_scroll_region:
3424 * @canvas: A canvas.
3425 * @x1: Leftmost limit of the scrolling region (return value).
3426 * @y1: Upper limit of the scrolling region (return value).
3427 * @x2: Rightmost limit of the scrolling region (return value).
3428 * @y2: Lower limit of the scrolling region (return value).
3429 *
3430 * Queries the scrolling region of a canvas.
3431 **/
3432 void
3433 eel_canvas_get_scroll_region (EelCanvas *canvas, double *x1, double *y1, double *x2, double *y2)
3434 {
3435 g_return_if_fail (EEL_IS_CANVAS (canvas));
3436
3437 if (x1)
3438 *x1 = canvas->scroll_x1;
3439
3440 if (y1)
3441 *y1 = canvas->scroll_y1;
3442
3443 if (x2)
3444 *x2 = canvas->scroll_x2;
3445
3446 if (y2)
3447 *y2 = canvas->scroll_y2;
3448 }
3449
3450 void
3451 eel_canvas_set_center_scroll_region (EelCanvas *canvas,
3452 gboolean center_scroll_region)
3453 {
3454 GtkAdjustment *vadjustment, *hadjustment;
3455
3456 g_return_if_fail (EEL_IS_CANVAS (canvas));
3457
3458 canvas->center_scroll_region = center_scroll_region != 0;
3459
3460 hadjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (&canvas->layout));
3461 vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (&canvas->layout));
3462
3463 scroll_to (canvas,
3464 gtk_adjustment_get_value (hadjustment),
3465 gtk_adjustment_get_value (vadjustment));
3466 }
3467
3468
3469 /**
3470 * eel_canvas_set_pixels_per_unit:
3471 * @canvas: A canvas.
3472 * @n: The number of pixels that correspond to one canvas unit.
3473 *
3474 * Sets the zooming factor of a canvas by specifying the number of pixels that
3475 * correspond to one canvas unit.
3476 **/
3477 void
3478 eel_canvas_set_pixels_per_unit (EelCanvas *canvas, double n)
3479 {
3480 GtkWidget *widget;
3481 double cx, cy;
3482 int x1, y1;
3483 int center_x, center_y;
3484 GdkWindow *window;
3485 GdkWindowAttr attributes;
3486 gint attributes_mask;
3487 GtkAllocation allocation;
3488 GtkAdjustment *vadjustment, *hadjustment;
3489
3490 g_return_if_fail (EEL_IS_CANVAS (canvas));
3491 g_return_if_fail (n > EEL_CANVAS_EPSILON);
3492
3493 widget = GTK_WIDGET (canvas);
3494
3495 gtk_widget_get_allocation (widget, &allocation);
3496 center_x = allocation.width / 2;
3497 center_y = allocation.height / 2;
3498
3499 /* Find the coordinates of the screen center in units. */
3500 hadjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (canvas));
3501 vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (canvas));
3502 cx = (gtk_adjustment_get_value (hadjustment) + center_x) / canvas->pixels_per_unit + canvas->scroll_x1 + canvas->zoom_xofs;
3503 cy = (gtk_adjustment_get_value (vadjustment) + center_y) / canvas->pixels_per_unit + canvas->scroll_y1 + canvas->zoom_yofs;
3504
3505 /* Now calculate the new offset of the upper left corner. (round not truncate) */
3506 x1 = ((cx - canvas->scroll_x1) * n) - center_x + .5;
3507 y1 = ((cy - canvas->scroll_y1) * n) - center_y + .5;
3508
3509 canvas->pixels_per_unit = n;
3510
3511 if (!(canvas->root->flags & EEL_CANVAS_ITEM_NEED_DEEP_UPDATE)) {
3512 canvas->root->flags |= EEL_CANVAS_ITEM_NEED_DEEP_UPDATE;
3513 eel_canvas_request_update (canvas);
3514 }
3515
3516 /* Map a background None window over the bin_window to avoid
3517 * scrolling the window scroll causing exposes.
3518 */
3519 window = NULL;
3520 if (gtk_widget_get_mapped (widget)) {
3521 GtkAllocation allocation;
3522 attributes.window_type = GDK_WINDOW_CHILD;
3523 gtk_widget_get_allocation (widget, &allocation);
3524 attributes.x = allocation.x;
3525 attributes.y = allocation.y;
3526 attributes.width = allocation.width;
3527 attributes.height = allocation.height;
3528 attributes.wclass = GDK_INPUT_OUTPUT;
3529 attributes.visual = gtk_widget_get_visual (widget);
3530 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
3531
3532 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
3533
3534 window = gdk_window_new (gtk_widget_get_parent_window (widget),
3535 &attributes, attributes_mask);
3536 gdk_window_set_user_data (window, widget);
3537
3538 gdk_window_show (window);
3539 }
3540
3541 scroll_to (canvas, x1, y1);
3542
3543 /* If we created a an overlapping background None window, remove it how.
3544 *
3545 * TODO: We would like to temporarily set the bin_window background to
3546 * None to avoid clearing the bin_window to the background, but gdk doesn't
3547 * expose enought to let us do this, so we get a flash-effect here. At least
3548 * it looks better than scroll + expose.
3549 */
3550 if (window != NULL) {
3551 gdk_window_hide (window);
3552 gdk_window_set_user_data (window, NULL);
3553 gdk_window_destroy (window);
3554 }
3555
3556 canvas->need_repick = TRUE;
3557 }
3558
3559 /**
3560 * eel_canvas_scroll_to:
3561 * @canvas: A canvas.
3562 * @cx: Horizontal scrolling offset in canvas pixel units.
3563 * @cy: Vertical scrolling offset in canvas pixel units.
3564 *
3565 * Makes a canvas scroll to the specified offsets, given in canvas pixel units.
3566 * The canvas will adjust the view so that it is not outside the scrolling
3567 * region. This function is typically not used, as it is better to hook
3568 * scrollbars to the canvas layout's scrolling adjusments.
3569 **/
3570 void
3571 eel_canvas_scroll_to (EelCanvas *canvas, int cx, int cy)
3572 {
3573 g_return_if_fail (EEL_IS_CANVAS (canvas));
3574
3575 scroll_to (canvas, cx, cy);
3576 }
3577
3578 /**
3579 * eel_canvas_get_scroll_offsets:
3580 * @canvas: A canvas.
3581 * @cx: Horizontal scrolling offset (return value).
3582 * @cy: Vertical scrolling offset (return value).
3583 *
3584 * Queries the scrolling offsets of a canvas. The values are returned in canvas
3585 * pixel units.
3586 **/
3587 void
3588 eel_canvas_get_scroll_offsets (EelCanvas *canvas, int *cx, int *cy)
3589 {
3590 GtkAdjustment *vadjustment, *hadjustment;
3591
3592 g_return_if_fail (EEL_IS_CANVAS (canvas));
3593
3594 hadjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (canvas));
3595 vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (canvas));
3596
3597 if (cx)
3598 *cx = gtk_adjustment_get_value (hadjustment);
3599
3600 if (cy)
3601 *cy = gtk_adjustment_get_value (vadjustment);
3602 }
3603
3604 /**
3605 * eel_canvas_update_now:
3606 * @canvas: A canvas.
3607 *
3608 * Forces an immediate update and redraw of a canvas. If the canvas does not
3609 * have any pending update or redraw requests, then no action is taken. This is
3610 * typically only used by applications that need explicit control of when the
3611 * display is updated, like games. It is not needed by normal applications.
3612 */
3613 void
3614 eel_canvas_update_now (EelCanvas *canvas)
3615 {
3616 g_return_if_fail (EEL_IS_CANVAS (canvas));
3617
3618 if (!(canvas->need_update || canvas->need_redraw))
3619 return;
3620 remove_idle (canvas);
3621 do_update (canvas);
3622 }
3623
3624 /**
3625 * eel_canvas_get_item_at:
3626 * @canvas: A canvas.
3627 * @x: X position in world coordinates.
3628 * @y: Y position in world coordinates.
3629 *
3630 * Looks for the item that is under the specified position, which must be
3631 * specified in world coordinates.
3632 *
3633 * Return value: The sought item, or NULL if no item is at the specified
3634 * coordinates.
3635 **/
3636 EelCanvasItem *
3637 eel_canvas_get_item_at (EelCanvas *canvas, double x, double y)
3638 {
3639 EelCanvasItem *item;
3640 double dist;
3641 int cx, cy;
3642
3643 g_return_val_if_fail (EEL_IS_CANVAS (canvas), NULL);
3644
3645 eel_canvas_w2c (canvas, x, y, &cx, &cy);
3646
3647 dist = eel_canvas_item_invoke_point (canvas->root, x, y, cx, cy, &item);
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
3648 if ((int) (dist * canvas->pixels_per_unit + 0.5) <= canvas->close_enough)
3649 return item;
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
3650 else
3651 return NULL;
3652 }
3653
3654 /* Queues an update of the canvas */
3655 static void
3656 eel_canvas_request_update (EelCanvas *canvas)
3657 {
3658 EEL_CANVAS_GET_CLASS (canvas)->request_update (canvas);
3659 }
3660
3661 static void
3662 eel_canvas_request_update_real (EelCanvas *canvas)
3663 {
3664 canvas->need_update = TRUE;
3665 add_idle (canvas);
3666 }
3667
3668 /**
3669 * eel_canvas_request_redraw:
3670 * @canvas: A canvas.
3671 * @x1: Leftmost coordinate of the rectangle to be redrawn.
3672 * @y1: Upper coordinate of the rectangle to be redrawn.
3673 * @x2: Rightmost coordinate of the rectangle to be redrawn, plus 1.
3674 * @y2: Lower coordinate of the rectangle to be redrawn, plus 1.
3675 *
3676 * Convenience function that informs a canvas that the specified rectangle needs
3677 * to be repainted. The rectangle includes @x1 and @y1, but not @x2 and @y2.
3678 * To be used only by item implementations.
3679 **/
3680 void
3681 eel_canvas_request_redraw (EelCanvas *canvas, int x1, int y1, int x2, int y2)
3682 {
3683 GdkRectangle bbox;
3684
3685 g_return_if_fail (EEL_IS_CANVAS (canvas));
3686
3687 if (!gtk_widget_is_drawable (GTK_WIDGET (canvas))
3688 || (x1 >= x2) || (y1 >= y2)) return;
3689
3690 bbox.x = x1;
3691 bbox.y = y1;
3692 bbox.width = x2 - x1;
3693 bbox.height = y2 - y1;
3694
3695 gdk_window_invalidate_rect (gtk_layout_get_bin_window (GTK_LAYOUT (canvas)),
3696 &bbox, FALSE);
3697 }
3698
3699 /**
3700 * eel_canvas_w2c:
3701 * @canvas: A canvas.
3702 * @wx: World X coordinate.
3703 * @wy: World Y coordinate.
3704 * @cx: X pixel coordinate (return value).
3705 * @cy: Y pixel coordinate (return value).
3706 *
3707 * Converts world coordinates into canvas pixel coordinates.
3708 **/
3709 void
3710 eel_canvas_w2c (EelCanvas *canvas, double wx, double wy, int *cx, int *cy)
3711 {
3712 double zoom;
3713
3714 g_return_if_fail (EEL_IS_CANVAS (canvas));
3715
3716 zoom = canvas->pixels_per_unit;
3717
3718 if (cx)
3719 *cx = floor ((wx - canvas->scroll_x1)*zoom + canvas->zoom_xofs + 0.5);
3720 if (cy)
3721 *cy = floor ((wy - canvas->scroll_y1)*zoom + canvas->zoom_yofs + 0.5);
3722 }
3723
3724 /**
3725 * eel_canvas_w2c:
3726 * @canvas: A canvas.
3727 * @world: rectangle in world coordinates.
3728 * @canvas: rectangle in canvase coordinates.
3729 *
3730 * Converts rectangles in world coordinates into canvas pixel coordinates.
3731 **/
3732 void
3733 eel_canvas_w2c_rect_d (EelCanvas *canvas,
3734 double *x1, double *y1,
3735 double *x2, double *y2)
3736 {
3737 eel_canvas_w2c_d (canvas,
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
3738 *x1, *y1,
3739 x1, y1);
3740 eel_canvas_w2c_d (canvas,
3741 *x2, *y2,
3742 x2, y2);
3743 }
3744
3745
3746
3747 /**
3748 * eel_canvas_w2c_d:
3749 * @canvas: A canvas.
3750 * @wx: World X coordinate.
3751 * @wy: World Y coordinate.
3752 * @cx: X pixel coordinate (return value).
3753 * @cy: Y pixel coordinate (return value).
3754 *
3755 * Converts world coordinates into canvas pixel coordinates. This version
3756 * produces coordinates in floating point coordinates, for greater precision.
3757 **/
3758 void
3759 eel_canvas_w2c_d (EelCanvas *canvas, double wx, double wy, double *cx, double *cy)
3760 {
3761 double zoom;
3762
3763 g_return_if_fail (EEL_IS_CANVAS (canvas));
3764
3765 zoom = canvas->pixels_per_unit;
3766
3767 if (cx)
3768 *cx = (wx - canvas->scroll_x1)*zoom + canvas->zoom_xofs;
3769 if (cy)
3770 *cy = (wy - canvas->scroll_y1)*zoom + canvas->zoom_yofs;
3771 }
3772
3773
3774 /**
3775 * eel_canvas_c2w:
3776 * @canvas: A canvas.
3777 * @cx: Canvas pixel X coordinate.
3778 * @cy: Canvas pixel Y coordinate.
3779 * @wx: X world coordinate (return value).
3780 * @wy: Y world coordinate (return value).
3781 *
3782 * Converts canvas pixel coordinates to world coordinates.
3783 **/
3784 void
3785 eel_canvas_c2w (EelCanvas *canvas, int cx, int cy, double *wx, double *wy)
3786 {
3787 double zoom;
3788
3789 g_return_if_fail (EEL_IS_CANVAS (canvas));
3790
3791 zoom = canvas->pixels_per_unit;
3792
3793 if (wx)
3794 *wx = (cx - canvas->zoom_xofs)/zoom + canvas->scroll_x1;
3795 if (wy)
3796 *wy = (cy - canvas->zoom_yofs)/zoom + canvas->scroll_y1;
3797 }
3798
3799
3800 /**
3801 * eel_canvas_window_to_world:
3802 * @canvas: A canvas.
3803 * @winx: Window-relative X coordinate.
3804 * @winy: Window-relative Y coordinate.
3805 * @worldx: X world coordinate (return value).
3806 * @worldy: Y world coordinate (return value).
3807 *
3808 * Converts window-relative coordinates into world coordinates. You can use
3809 * this when you need to convert mouse coordinates into world coordinates, for
3810 * example.
3811 * Window coordinates are really the same as canvas coordinates now, but this
3812 * function is here for backwards compatibility reasons.
3813 **/
3814 void
3815 eel_canvas_window_to_world (EelCanvas *canvas, double winx, double winy,
3816 double *worldx, double *worldy)
3817 {
3818 g_return_if_fail (EEL_IS_CANVAS (canvas));
3819
3820 if (worldx)
3821 *worldx = canvas->scroll_x1 + ((winx - canvas->zoom_xofs)
3822 / canvas->pixels_per_unit);
3823
3824 if (worldy)
3825 *worldy = canvas->scroll_y1 + ((winy - canvas->zoom_yofs)
3826 / canvas->pixels_per_unit);
3827 }
3828
3829
3830 /**
3831 * eel_canvas_world_to_window:
3832 * @canvas: A canvas.
3833 * @worldx: World X coordinate.
3834 * @worldy: World Y coordinate.
3835 * @winx: X window-relative coordinate.
3836 * @winy: Y window-relative coordinate.
3837 *
3838 * Converts world coordinates into window-relative coordinates.
3839 * Window coordinates are really the same as canvas coordinates now, but this
3840 * function is here for backwards compatibility reasons.
3841 **/
3842 void
3843 eel_canvas_world_to_window (EelCanvas *canvas, double worldx, double worldy,
3844 double *winx, double *winy)
3845 {
3846 g_return_if_fail (EEL_IS_CANVAS (canvas));
3847
3848 if (winx)
3849 *winx = (canvas->pixels_per_unit)*(worldx - canvas->scroll_x1) + canvas->zoom_xofs;
3850
3851 if (winy)
3852 *winy = (canvas->pixels_per_unit)*(worldy - canvas->scroll_y1) + canvas->zoom_yofs;
3853 }
3854
3855 static gboolean
3856 boolean_handled_accumulator (GSignalInvocationHint *ihint,
3857 GValue *return_accu,
3858 const GValue *handler_return,
3859 gpointer dummy)
3860 {
3861 gboolean continue_emission;
3862 gboolean signal_handled;
3863
3864 signal_handled = g_value_get_boolean (handler_return);
3865 g_value_set_boolean (return_accu, signal_handled);
3866 continue_emission = !signal_handled;
3867
3868 return continue_emission;
3869 }
3870
3871 static guint
3872 eel_canvas_item_accessible_add_focus_handler (AtkComponent *component,
3873 AtkFocusHandler handler)
3874 {
3875 GSignalMatchType match_type;
3876 guint signal_id;
3877
3878 match_type = G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC;
3879 signal_id = g_signal_lookup ("focus-event", ATK_TYPE_OBJECT);
3880
3881 if (!g_signal_handler_find (component, match_type, signal_id, 0, NULL,
3882 (gpointer) handler, NULL)) {
3883 return g_signal_connect_closure_by_id (component,
3884 signal_id, 0,
3885 g_cclosure_new (
3886 G_CALLBACK (handler), NULL,
3887 (GClosureNotify) NULL),
3888 FALSE);
3889 }
3890 return 0;
3891 }
3892
3893 static void
3894 eel_canvas_item_accessible_get_item_extents (EelCanvasItem *item,
3895 GdkRectangle *rect)
3896 {
3897 double bx1, bx2, by1, by2;
3898 gint scroll_x, scroll_y;
3899 gint x1, x2, y1, y2;
3900
3901 eel_canvas_item_get_bounds (item, &bx1, &by1, &bx2, &by2);
3902 eel_canvas_w2c_rect_d (item->canvas, &bx1, &by1, &bx2, &by2);
3903 eel_canvas_get_scroll_offsets (item->canvas, &scroll_x, &scroll_y);
3904 x1 = floor (bx1 + .5);
3905 y1 = floor (by1 + .5);
3906 x2 = floor (bx2 + .5);
3907 y2 = floor (by2 + .5);
3908 rect->x = x1 - scroll_x;
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
3909 rect->y = y1 - scroll_y;
3910 rect->width = x2 - x1;
3911 rect->height = y2 - y1;
3912 }
3913
3914 static gboolean
3915 eel_canvas_item_accessible_is_item_in_window (EelCanvasItem *item,
3916 GdkRectangle *rect)
3917 {
3918 GtkWidget *widget;
3919 gboolean retval;
3920
3921 widget = GTK_WIDGET (item->canvas);
3922 if (gtk_widget_get_window (widget)) {
3923 int window_width, window_height;
3924
3925 gdk_window_get_geometry (gtk_widget_get_window (widget), NULL, NULL,
3926 &window_width, &window_height);
3927 /*
3928 * Check whether rectangles intersect
3929 */
3930 if (rect->x + rect->width < 0 ||
3931 rect->y + rect->height < 0 ||
3932 rect->x > window_width ||
3933 rect->y > window_height) {
3934 retval = FALSE;
3935 } else {
3936 retval = TRUE;
3937 }
3938 } else {
3939 retval = FALSE;
3940 }
3941 return retval;
3942 }
3943
3944
3945 static void
3946 eel_canvas_item_accessible_get_extents (AtkComponent *component,
3947 gint *x,
3948 gint *y,
3949 gint *width,
3950 gint *height,
3951 AtkCoordType coord_type)
3952 {
3953 AtkGObjectAccessible *atk_gobj;
3954 GObject *obj;
3955 EelCanvasItem *item;
3956 gint window_x, window_y;
3957 gint toplevel_x, toplevel_y;
3958 GdkRectangle rect;
3959 GdkWindow *window;
3960 GtkWidget *canvas;
3961
3962 atk_gobj = ATK_GOBJECT_ACCESSIBLE (component);
3963 obj = atk_gobject_accessible_get_object (atk_gobj);
3964
3965 if (obj == NULL) {
3966 /* item is defunct */
3967 return;
3968 }
3969
3970 /* Get the CanvasItem */
3971 item = EEL_CANVAS_ITEM (obj);
3972
3973 /* If this item has no parent canvas, something's broken */
3974 g_return_if_fail (GTK_IS_WIDGET (item->canvas));
3975
3976 eel_canvas_item_accessible_get_item_extents (item, &rect);
3977 *width = rect.width;
3978 *height = rect.height;
3979 if (!eel_canvas_item_accessible_is_item_in_window (item, &rect)) {
3980 *x = G_MININT;
3981 *y = G_MININT;
3982 return;
3983 }
3984
3985 canvas = GTK_WIDGET (item->canvas);
3986 window = gtk_widget_get_parent_window (canvas);
3987 gdk_window_get_origin (window, &window_x, &window_y);
3988 *x = rect.x + window_x;
3989 *y = rect.y + window_y;
3990 if (coord_type == ATK_XY_WINDOW) {
3991 window = gdk_window_get_toplevel (gtk_widget_get_window (canvas));
3992 gdk_window_get_origin (window, &toplevel_x, &toplevel_y);
3993 *x -= toplevel_x;
3994 *y -= toplevel_y;
3995 }
3996 return;
3997 }
3998
3999 static gint
4000 eel_canvas_item_accessible_get_mdi_zorder (AtkComponent *component)
4001 {
4002 AtkGObjectAccessible *atk_gobj;
4003 GObject *g_obj;
4004 EelCanvasItem *item;
4005
4006 atk_gobj = ATK_GOBJECT_ACCESSIBLE (component);
4007 g_obj = atk_gobject_accessible_get_object (atk_gobj);
4008 if (g_obj == NULL) {
4009 /* Object is defunct */
4010 return -1;
4011 }
4012
4013 item = EEL_CANVAS_ITEM (g_obj);
4014 if (item->parent) {
4015 return g_list_index (EEL_CANVAS_GROUP (item->parent)->item_list, item);
4016 } else {
4017 g_return_val_if_fail (item->canvas->root == item, -1);
4018 return 0;
4019 }
4020 }
4021
4022 static gboolean
4023 eel_canvas_item_accessible_grab_focus (AtkComponent *component)
4024 {
4025 AtkGObjectAccessible *atk_gobj;
4026 GObject *obj;
4027 EelCanvasItem *item;
4028 GtkWidget *toplevel;
4029
4030 atk_gobj = ATK_GOBJECT_ACCESSIBLE (component);
4031 obj = atk_gobject_accessible_get_object (atk_gobj);
4032
4033 item = EEL_CANVAS_ITEM (obj);
4034 if (item == NULL) {
4035 /* item is defunct */
4036 return FALSE;
4037 }
4038
4039 eel_canvas_item_grab_focus (item);
4040 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (item->canvas));
4041 if (gtk_widget_is_toplevel (toplevel)) {
4042 gtk_window_present (GTK_WINDOW (toplevel));
4043 }
4044
4045 return TRUE;
4046 }
4047
4048 static void
4049 eel_canvas_item_accessible_remove_focus_handler (AtkComponent *component,
4050 guint handler_id)
4051 {
4052 g_signal_handler_disconnect (component, handler_id);
4053 }
4054
4055 static void
4056 eel_canvas_item_accessible_component_interface_init (AtkComponentIface *iface)
4057 {
4058 g_return_if_fail (iface != NULL);
4059
4060 iface->add_focus_handler = eel_canvas_item_accessible_add_focus_handler;
4061 iface->get_extents = eel_canvas_item_accessible_get_extents;
4062 iface->get_mdi_zorder = eel_canvas_item_accessible_get_mdi_zorder;
4063 iface->grab_focus = eel_canvas_item_accessible_grab_focus;
4064 iface->remove_focus_handler = eel_canvas_item_accessible_remove_focus_handler;
4065 }
4066
4067 static gboolean
4068 eel_canvas_item_accessible_is_item_on_screen (EelCanvasItem *item)
4069 {
4070 GdkRectangle rect;
4071
4072 eel_canvas_item_accessible_get_item_extents (item, &rect);
4073 return eel_canvas_item_accessible_is_item_in_window (item, &rect);
4074 }
4075
4076 static void
4077 eel_canvas_item_accessible_initialize (AtkObject *obj, gpointer data)
4078 {
4079 if (ATK_OBJECT_CLASS (accessible_item_parent_class)->initialize != NULL)
4080 ATK_OBJECT_CLASS (accessible_item_parent_class)->initialize (obj, data);
4081 g_object_set_data (G_OBJECT (obj), "atk-component-layer",
4082 GINT_TO_POINTER (ATK_LAYER_MDI));
4083 }
4084
4085 static AtkStateSet*
4086 eel_canvas_item_accessible_ref_state_set (AtkObject *accessible)
4087 {
4088 AtkGObjectAccessible *atk_gobj;
4089 GObject *obj;
4090 EelCanvasItem *item;
4091 AtkStateSet *state_set;
4092
4093 state_set = ATK_OBJECT_CLASS (accessible_item_parent_class)->ref_state_set (accessible);
4094 atk_gobj = ATK_GOBJECT_ACCESSIBLE (accessible);
4095 obj = atk_gobject_accessible_get_object (atk_gobj);
4096
4097 item = EEL_CANVAS_ITEM (obj);
4098 if (item == NULL) {
4099 atk_state_set_add_state (state_set, ATK_STATE_DEFUNCT);
4100 } else {
4101 if (item->flags & EEL_CANVAS_ITEM_VISIBLE) {
4102 atk_state_set_add_state (state_set, ATK_STATE_VISIBLE);
4103
4104 if (eel_canvas_item_accessible_is_item_on_screen (item)) {
4105 atk_state_set_add_state (state_set, ATK_STATE_SHOWING);
4106 }
4107 }
4108 if (gtk_widget_get_can_focus (GTK_WIDGET (item->canvas))) {
4109 atk_state_set_add_state (state_set, ATK_STATE_FOCUSABLE);
4110
4111 if (item->canvas->focused_item == item) {
4112 atk_state_set_add_state (state_set, ATK_STATE_FOCUSED);
4113 }
4114 }
4115 }
4116
4117 return state_set;
4118 }
4119
4120 static void
4121 eel_canvas_item_accessible_class_init (EelCanvasItemAccessibleClass *klass)
4122 {
4123 AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass);
4124
4125 accessible_item_parent_class = g_type_class_peek_parent (klass);
4126
4127 atk_class->initialize = eel_canvas_item_accessible_initialize;
4128 atk_class->ref_state_set = eel_canvas_item_accessible_ref_state_set;
4129 }
4130
4131 static void
4132 eel_canvas_item_accessible_init (EelCanvasItemAccessible *self)
4133 {
4134
4135 }
4136
4137 G_DEFINE_TYPE_WITH_CODE (EelCanvasItemAccessible,
4138 eel_canvas_item_accessible,
4139 ATK_TYPE_GOBJECT_ACCESSIBLE,
4140 G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT,
4141 eel_canvas_item_accessible_component_interface_init));
4142
4143 static GType eel_canvas_item_accessible_factory_get_type (void);
4144
4145 typedef AtkObjectFactory EelCanvasItemAccessibleFactory;
4146 typedef AtkObjectFactoryClass EelCanvasItemAccessibleFactoryClass;
4147 G_DEFINE_TYPE (EelCanvasItemAccessibleFactory, eel_canvas_item_accessible_factory,
4148 ATK_TYPE_OBJECT_FACTORY)
4149
4150 static GType
4151 eel_canvas_item_accessible_factory_get_accessible_type (void)
4152 {
4153 return eel_canvas_item_accessible_get_type ();
4154 }
4155
4156 static AtkObject*
4157 eel_canvas_item_accessible_factory_create_accessible (GObject *for_object)
4158 {
4159 AtkObject *accessible;
4160
4161 accessible = g_object_new (eel_canvas_item_accessible_get_type (), NULL);
4162 atk_object_initialize (accessible, for_object);
4163 return accessible;
4164 }
4165
4166 static void
4167 eel_canvas_item_accessible_factory_init (EelCanvasItemAccessibleFactory *self)
4168 {
4169
4170 }
4171
4172 static void
4173 eel_canvas_item_accessible_factory_class_init (AtkObjectFactoryClass *klass)
4174 {
4175 klass->create_accessible = eel_canvas_item_accessible_factory_create_accessible;
4176 klass->get_accessible_type = eel_canvas_item_accessible_factory_get_accessible_type;
4177 }
4178
4179 /* Class initialization function for EelCanvasItemClass */
4180 static void
4181 eel_canvas_item_class_init (EelCanvasItemClass *klass)
4182 {
4183 GObjectClass *gobject_class = (GObjectClass *) klass;
4184
4185 item_parent_class = g_type_class_peek_parent (klass);
4186
4187 gobject_class->set_property = eel_canvas_item_set_property;
4188 gobject_class->get_property = eel_canvas_item_get_property;
4189 gobject_class->dispose = eel_canvas_item_dispose;
4190
4191 g_object_class_install_property
4192 (gobject_class, ITEM_PROP_PARENT,
4193 g_param_spec_object ("parent", NULL, NULL,
4194 EEL_TYPE_CANVAS_ITEM,
4195 G_PARAM_READWRITE));
4196
4197 g_object_class_install_property
4198 (gobject_class, ITEM_PROP_VISIBLE,
4199 g_param_spec_boolean ("visible", NULL, NULL,
4200 TRUE,
4201 G_PARAM_READWRITE));
4202
4203 item_signals[ITEM_EVENT] =
4204 g_signal_new ("event",
4205 G_TYPE_FROM_CLASS (klass),
4206 G_SIGNAL_RUN_LAST,
4207 G_STRUCT_OFFSET (EelCanvasItemClass, event),
4208 boolean_handled_accumulator, NULL,
4209 g_cclosure_marshal_generic,
4210 G_TYPE_BOOLEAN, 1,
4211 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
4212
4213 item_signals[ITEM_DESTROY] =
4214 g_signal_new ("destroy",
4215 G_TYPE_FROM_CLASS (klass),
4216 G_SIGNAL_RUN_CLEANUP | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
4217 G_STRUCT_OFFSET (EelCanvasItemClass, destroy),
4218 NULL, NULL,
4219 g_cclosure_marshal_VOID__VOID,
4220 G_TYPE_NONE, 0);
4221
4222 klass->realize = eel_canvas_item_realize;
4223 klass->unrealize = eel_canvas_item_unrealize;
4224 klass->map = eel_canvas_item_map;
4225 klass->unmap = eel_canvas_item_unmap;
4226 klass->update = eel_canvas_item_update;
4227
4228 atk_registry_set_factory_type (atk_get_default_registry (),
4229 EEL_TYPE_CANVAS_ITEM,
4230 eel_canvas_item_accessible_factory_get_type ());
4231 }