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., 51 Franklin Street - Suite 500,
21 * Boston, MA 02110-1335, 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] = { 0 };
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
eel_canvas_item_get_type(void)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
eel_canvas_item_init(EelCanvasItem * item)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 *
eel_canvas_item_new(EelCanvasGroup * parent,GType type,const gchar * first_arg_name,...)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
item_post_create_setup(EelCanvasItem * item)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
eel_canvas_item_set_property(GObject * gobject,guint param_id,const GValue * value,GParamSpec * pspec)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
eel_canvas_item_get_property(GObject * gobject,guint param_id,GValue * value,GParamSpec * pspec)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
eel_canvas_item_construct(EelCanvasItem * item,EelCanvasGroup * parent,const gchar * first_arg_name,va_list args)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
redraw_and_repick_if_mapped(EelCanvasItem * item)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
eel_canvas_item_dispose(GObject * object)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
eel_canvas_item_destroy(EelCanvasItem * item)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
eel_canvas_item_realize(EelCanvasItem * item)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
eel_canvas_item_unrealize(EelCanvasItem * item)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
eel_canvas_item_map(EelCanvasItem * item)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
eel_canvas_item_unmap(EelCanvasItem * item)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
eel_canvas_item_update(EelCanvasItem * item,double i2w_dx,double i2w_dy,int flags)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
eel_canvas_item_invoke_update(EelCanvasItem * item,double i2w_dx,double i2w_dy,int flags)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
eel_canvas_item_invoke_point(EelCanvasItem * item,double x,double y,int cx,int cy,EelCanvasItem ** actual_item)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
eel_canvas_item_set(EelCanvasItem * item,const gchar * first_arg_name,...)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
eel_canvas_item_set_valist(EelCanvasItem * item,const gchar * first_arg_name,va_list args)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
eel_canvas_item_move(EelCanvasItem * item,double dx,double dy)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
eel_canvas_queue_resize(EelCanvas * canvas)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
put_item_after(GList * link,GList * before)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;
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;
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
eel_canvas_item_raise(EelCanvasItem * item,int positions)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
eel_canvas_item_lower(EelCanvasItem * item,int positions)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
eel_canvas_item_raise_to_top(EelCanvasItem * item)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
eel_canvas_item_lower_to_bottom(EelCanvasItem * item)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
eel_canvas_item_send_behind(EelCanvasItem * item,EelCanvasItem * behind_item)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
eel_canvas_item_show(EelCanvasItem * item)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
eel_canvas_item_hide(EelCanvasItem * item)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
eel_canvas_item_grab(EelCanvasItem * item,GdkEventMask event_mask,GdkCursor * cursor,guint32 timestamp)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
eel_canvas_item_ungrab(EelCanvasItem * item,guint32 etime)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
eel_canvas_item_w2i(EelCanvasItem * item,double * x,double * y)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
eel_canvas_item_i2w(EelCanvasItem * item,double * x,double * y)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
is_descendant(EelCanvasItem * item,EelCanvasItem * parent)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
eel_canvas_item_reparent(EelCanvasItem * item,EelCanvasGroup * new_group)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
eel_canvas_item_grab_focus(EelCanvasItem * item)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
eel_canvas_item_get_bounds(EelCanvasItem * item,double * x1,double * y1,double * x2,double * y2)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
eel_canvas_item_request_update(EelCanvasItem * item)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
eel_canvas_item_request_redraw(EelCanvasItem * item)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 + 3);
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
eel_canvas_group_get_type(void)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
eel_canvas_group_class_init(EelCanvasGroupClass * klass)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
eel_canvas_group_init(EelCanvasGroup * group)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
eel_canvas_group_set_property(GObject * gobject,guint param_id,const GValue * value,GParamSpec * pspec)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
eel_canvas_group_get_property(GObject * gobject,guint param_id,GValue * value,GParamSpec * pspec)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
eel_canvas_group_destroy(EelCanvasItem * object)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
eel_canvas_group_update(EelCanvasItem * item,double i2w_dx,double i2w_dy,int flags)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
eel_canvas_group_unrealize(EelCanvasItem * item)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
eel_canvas_group_map(EelCanvasItem * item)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
eel_canvas_group_unmap(EelCanvasItem * item)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
eel_canvas_group_draw(EelCanvasItem * item,cairo_t * cr,cairo_region_t * region)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
eel_canvas_group_point(EelCanvasItem * item,double x,double y,int cx,int cy,EelCanvasItem ** actual_item)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
eel_canvas_group_translate(EelCanvasItem * item,double dx,double dy)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
eel_canvas_group_bounds(EelCanvasItem * item,double * x1,double * y1,double * x2,double * y2)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)
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;
1654 miny += group->ypos;
1655 maxx += group->xpos;
1656 maxy += group->ypos;
1657 }
1658
1659 *x1 = minx;
1660 *y1 = miny;
1661 *x2 = maxx;
1662 *y2 = maxy;
1663 }
1664
1665 /* Adds an item to a group */
1666 static void
group_add(EelCanvasGroup * group,EelCanvasItem * item)1667 group_add (EelCanvasGroup *group, EelCanvasItem *item)
1668 {
1669 g_object_ref_sink (item);
1670
1671 /* FIXME: relatively inefficient way to add to the list, appending to the list causes it to be read from the top each time.
1672 prepend, followed by a reverse if necessary is the recommended approach. However as this list append is a number of
1673 levels of call down, correcting it is not as easy as it could be */
1674
1675 if (!group->item_list) {
1676 group->item_list = g_list_append (group->item_list, item);
1677 group->item_list_end = group->item_list;
1678 } else
1679 group->item_list_end = g_list_append (group->item_list_end, item)->next;
1680
1681 if ((item->flags & EEL_CANVAS_ITEM_VISIBLE) &&
1682 (group->item.flags & EEL_CANVAS_ITEM_MAPPED)) {
1683 if (!(item->flags & EEL_CANVAS_ITEM_REALIZED))
1684 (* EEL_CANVAS_ITEM_GET_CLASS (item)->realize) (item);
1685
1686 if (!(item->flags & EEL_CANVAS_ITEM_MAPPED))
1687 (* EEL_CANVAS_ITEM_GET_CLASS (item)->map) (item);
1688 }
1689
1690 if (item->flags & EEL_CANVAS_ITEM_VISIBLE)
1691 eel_canvas_queue_resize (EEL_CANVAS_ITEM (group)->canvas);
1692 }
1693
1694 /* Removes an item from a group */
1695 static void
group_remove(EelCanvasGroup * group,EelCanvasItem * item)1696 group_remove (EelCanvasGroup *group, EelCanvasItem *item)
1697 {
1698 GList *children;
1699
1700 g_return_if_fail (EEL_IS_CANVAS_GROUP (group));
1701 g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
1702
1703 for (children = group->item_list; children; children = children->next)
1704 if (children->data == item) {
1705 if (item->flags & EEL_CANVAS_ITEM_MAPPED) {
1706 (* EEL_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
1707 }
1708
1709 if (item->flags & EEL_CANVAS_ITEM_REALIZED)
1710 (* EEL_CANVAS_ITEM_GET_CLASS (item)->unrealize) (item);
1711
1712 if (item->flags & EEL_CANVAS_ITEM_VISIBLE)
1713 eel_canvas_queue_resize (item->canvas);
1714
1715 /* Unparent the child */
1716
1717 item->parent = NULL;
1718 /* item->canvas = NULL; */
1719 g_object_unref (G_OBJECT (item));
1720
1721 /* Remove it from the list */
1722
1723 if (children == group->item_list_end)
1724 group->item_list_end = children->prev;
1725
1726 group->item_list = g_list_remove_link (group->item_list, children);
1727 g_list_free (children);
1728 break;
1729 }
1730 }
1731
1732
1733 /*** EelCanvas ***/
1734
1735
1736 enum {
1737 DRAW_BACKGROUND,
1738 LAST_SIGNAL
1739 };
1740
1741 static void eel_canvas_class_init (EelCanvasClass *klass);
1742 static void eel_canvas_init (EelCanvas *canvas);
1743 static void eel_canvas_destroy (GtkWidget *object);
1744 static void eel_canvas_map (GtkWidget *widget);
1745 static void eel_canvas_unmap (GtkWidget *widget);
1746 static void eel_canvas_realize (GtkWidget *widget);
1747 static void eel_canvas_unrealize (GtkWidget *widget);
1748 static void eel_canvas_size_allocate (GtkWidget *widget,
1749 GtkAllocation *allocation);
1750 static gint eel_canvas_button (GtkWidget *widget,
1751 GdkEventButton *event);
1752 static gint eel_canvas_motion (GtkWidget *widget,
1753 GdkEventMotion *event);
1754 static gint eel_canvas_draw (GtkWidget *widget,
1755 cairo_t *cr);
1756 static gint eel_canvas_key (GtkWidget *widget,
1757 GdkEventKey *event);
1758 static gint eel_canvas_crossing (GtkWidget *widget,
1759 GdkEventCrossing *event);
1760 static gint eel_canvas_focus_in (GtkWidget *widget,
1761 GdkEventFocus *event);
1762 static gint eel_canvas_focus_out (GtkWidget *widget,
1763 GdkEventFocus *event);
1764 static void eel_canvas_request_update_real (EelCanvas *canvas);
1765 static void eel_canvas_draw_background (EelCanvas *canvas,
1766 cairo_t *cr);
1767 static AtkObject *eel_canvas_get_accessible (GtkWidget *widget);
1768
1769
1770 static GtkLayoutClass *canvas_parent_class;
1771
1772 static guint canvas_signals[LAST_SIGNAL] = { 0 };
1773
1774 /**
1775 * eel_canvas_get_type:
1776 *
1777 * Registers the &EelCanvas class if necessary, and returns the type ID
1778 * associated to it.
1779 *
1780 * Return value: The type ID of the &EelCanvas class.
1781 **/
1782 GType
eel_canvas_get_type(void)1783 eel_canvas_get_type (void)
1784 {
1785 static GType canvas_type = 0;
1786
1787 if (!canvas_type) {
1788 static const GTypeInfo canvas_info = {
1789 sizeof (EelCanvasClass),
1790 (GBaseInitFunc) NULL,
1791 (GBaseFinalizeFunc) NULL,
1792 (GClassInitFunc) eel_canvas_class_init,
1793 NULL, /* class_finalize */
1794 NULL, /* class_data */
1795 sizeof (EelCanvas),
1796 0, /* n_preallocs */
1797 (GInstanceInitFunc) eel_canvas_init
1798 };
1799
1800 canvas_type = g_type_register_static (gtk_layout_get_type (),
1801 "EelCanvas",
1802 &canvas_info,
1803 0);
1804 }
1805
1806 return canvas_type;
1807 }
1808
1809 static void
eel_canvas_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1810 eel_canvas_get_property (GObject *object,
1811 guint prop_id,
1812 GValue *value,
1813 GParamSpec *pspec)
1814 {
1815 switch (prop_id) {
1816 default:
1817 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1818 break;
1819 }
1820 }
1821
1822 static void
eel_canvas_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1823 eel_canvas_set_property (GObject *object,
1824 guint prop_id,
1825 const GValue *value,
1826 GParamSpec *pspec)
1827 {
1828 switch (prop_id) {
1829 default:
1830 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1831 break;
1832 }
1833 }
1834
1835 static void
eel_canvas_accessible_adjustment_changed(GtkAdjustment * adjustment,AtkObject * obj)1836 eel_canvas_accessible_adjustment_changed (GtkAdjustment *adjustment,
1837 AtkObject *obj)
1838 {
1839 /* The scrollbars have changed */
1840
1841 g_signal_emit_by_name (obj, "visible_data_changed");
1842 }
1843
1844 static void
accessible_destroy_cb(GtkWidget * widget,AtkObject * obj)1845 accessible_destroy_cb (GtkWidget *widget,
1846 AtkObject *obj)
1847 {
1848 gtk_accessible_set_widget (GTK_ACCESSIBLE(obj), NULL);
1849 atk_object_notify_state_change (obj, ATK_STATE_DEFUNCT, TRUE);
1850 }
1851
1852 static gboolean
accessible_focus_cb(GtkWidget * widget,GdkEventFocus * event)1853 accessible_focus_cb (GtkWidget *widget,
1854 GdkEventFocus *event)
1855 {
1856 atk_object_notify_state_change ((AtkObject *)gtk_widget_get_accessible (widget), ATK_STATE_FOCUSED, event->in);
1857
1858 return FALSE;
1859 }
1860
1861 static void
accessible_notify_cb(GObject * obj,GParamSpec * pspec)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
accessible_size_allocate_cb(GtkWidget * widget,GtkAllocation * allocation)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
accessible_map_cb(GtkWidget * widget)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
eel_canvas_accessible_initialize(AtkObject * obj,gpointer data)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
eel_canvas_accessible_get_n_children(AtkObject * obj)1953 eel_canvas_accessible_get_n_children (AtkObject* obj)
1954 {
1955 GtkWidget *widget;
1956 EelCanvas *canvas;
1957 EelCanvasGroup *root_group;
1958
1959 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
1960
1961 if (widget == NULL) {
1962 return 0;
1963 }
1964
1965 g_return_val_if_fail (EEL_IS_CANVAS (widget), 0);
1966
1967 canvas = EEL_CANVAS (widget);
1968 root_group = eel_canvas_root (canvas);
1969 g_return_val_if_fail (root_group, 0);
1970
1971 return 1;
1972 }
1973
1974 static AtkObject*
eel_canvas_accessible_ref_child(AtkObject * obj,gint i)1975 eel_canvas_accessible_ref_child (AtkObject *obj,
1976 gint i)
1977 {
1978 GtkWidget *widget;
1979 EelCanvas *canvas;
1980 EelCanvasGroup *root_group;
1981 AtkObject *atk_object;
1982
1983 /* Canvas only has one child, so return NULL if index is non zero */
1984 if (i != 0) {
1985 return NULL;
1986 }
1987
1988 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
1989
1990 if (widget == NULL) {
1991 return NULL;
1992 }
1993
1994 canvas = EEL_CANVAS (widget);
1995 root_group = eel_canvas_root (canvas);
1996 g_return_val_if_fail (root_group, NULL);
1997
1998 atk_object = atk_gobject_accessible_for_object (G_OBJECT (root_group));
1999
2000 return g_object_ref (atk_object);
2001 }
2002
2003 static gboolean
eel_canvas_accessible_all_parents_visible(GtkWidget * widget)2004 eel_canvas_accessible_all_parents_visible (GtkWidget *widget)
2005 {
2006 GtkWidget *iter_parent = NULL;
2007 gboolean result = TRUE;
2008
2009 for (iter_parent = gtk_widget_get_parent (widget); iter_parent != NULL;
2010 iter_parent = gtk_widget_get_parent (iter_parent)) {
2011 if (!gtk_widget_get_visible (iter_parent)) {
2012 result = FALSE;
2013 break;
2014 }
2015 }
2016
2017 return result;
2018 }
2019
2020 static gboolean
eel_canvas_accessible_on_screen(GtkWidget * widget)2021 eel_canvas_accessible_on_screen (GtkWidget *widget)
2022 {
2023 GtkAllocation allocation;
2024 GtkWidget *viewport;
2025 gboolean return_value = TRUE;
2026
2027 gtk_widget_get_allocation (widget, &allocation);
2028
2029 viewport = gtk_widget_get_ancestor (widget, GTK_TYPE_VIEWPORT);
2030
2031 if (viewport) {
2032 GtkAllocation viewport_allocation;
2033 GtkAdjustment *adjustment;
2034 GdkRectangle visible_rect;
2035
2036 gtk_widget_get_allocation (viewport, &viewport_allocation);
2037
2038 adjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (viewport));
2039 visible_rect.y = gtk_adjustment_get_value (adjustment);
2040 adjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (viewport));
2041 visible_rect.x = gtk_adjustment_get_value (adjustment);
2042 visible_rect.width = viewport_allocation.width;
2043 visible_rect.height = viewport_allocation.height;
2044
2045 if (((allocation.x + allocation.width) < visible_rect.x) ||
2046 ((allocation.y + allocation.height) < visible_rect.y) ||
2047 (allocation.x > (visible_rect.x + visible_rect.width)) ||
2048 (allocation.y > (visible_rect.y + visible_rect.height))) {
2049 return_value = FALSE;
2050 }
2051 } else {
2052 /* Check whether the widget has been placed off the screen.
2053 * The widget may be MAPPED as when toolbar items do not
2054 * fit on the toolbar.
2055 */
2056 if (allocation.x + allocation.width <= 0 &&
2057 allocation.y + allocation.height <= 0) {
2058 return_value = FALSE;
2059 }
2060 }
2061
2062 return return_value;
2063 }
2064
2065 static AtkStateSet *
eel_canvas_accessible_ref_state_set(AtkObject * accessible)2066 eel_canvas_accessible_ref_state_set (AtkObject *accessible)
2067 {
2068 GtkWidget *widget;
2069 AtkStateSet *state_set;
2070
2071 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
2072 state_set = ATK_OBJECT_CLASS (accessible_parent_class)->ref_state_set (accessible);
2073
2074 if (widget == NULL) {
2075 atk_state_set_add_state (state_set, ATK_STATE_DEFUNCT);
2076 } else {
2077 if (gtk_widget_is_sensitive (widget)) {
2078 atk_state_set_add_state (state_set, ATK_STATE_SENSITIVE);
2079 atk_state_set_add_state (state_set, ATK_STATE_ENABLED);
2080 }
2081
2082 if (gtk_widget_get_can_focus (widget)) {
2083 atk_state_set_add_state (state_set, ATK_STATE_FOCUSABLE);
2084 }
2085 /*
2086 * We do not currently generate notifications when an ATK object
2087 * corresponding to a GtkWidget changes visibility by being scrolled
2088 * on or off the screen. The testcase for this is the main window
2089 * of the testgtk application in which a set of buttons in a GtkVBox
2090 * is in a scrolled window with a viewport.
2091 *
2092 * To generate the notifications we would need to do the following:
2093 * 1) Find the GtkViewport among the ancestors of the objects
2094 * 2) Create an accessible for the viewport
2095 * 3) Connect to the value-changed signal on the viewport
2096 * 4) When the signal is received we need to traverse the children
2097 * of the viewport and check whether the children are visible or not
2098 * visible; we may want to restrict this to the widgets for which
2099 * accessible objects have been created.
2100 * 5) We probably need to store a variable on_screen in the
2101 * GtkWidgetAccessible data structure so we can determine whether
2102 * the value has changed.
2103 */
2104 if (gtk_widget_get_visible (widget)) {
2105 atk_state_set_add_state (state_set, ATK_STATE_VISIBLE);
2106
2107 if (eel_canvas_accessible_on_screen (widget) &&
2108 gtk_widget_get_mapped (widget) &&
2109 eel_canvas_accessible_all_parents_visible (widget)) {
2110 atk_state_set_add_state (state_set, ATK_STATE_SHOWING);
2111 }
2112 }
2113
2114 if (gtk_widget_has_focus (widget)) {
2115 if (g_object_get_data (G_OBJECT (accessible), "gail-focus-object") == NULL) {
2116 atk_state_set_add_state (state_set, ATK_STATE_FOCUSED);
2117 }
2118 }
2119
2120 if (gtk_widget_has_default (widget)) {
2121 atk_state_set_add_state (state_set, ATK_STATE_DEFAULT);
2122 }
2123 }
2124 return state_set;
2125 }
2126
2127 static void
eel_canvas_accessible_get_extents(AtkComponent * component,gint * x,gint * y,gint * width,gint * height,AtkCoordType coord_type)2128 eel_canvas_accessible_get_extents (AtkComponent *component,
2129 gint *x,
2130 gint *y,
2131 gint *width,
2132 gint *height,
2133 AtkCoordType coord_type)
2134 {
2135 GdkWindow *window;
2136 gint x_window, y_window;
2137 gint x_toplevel, y_toplevel;
2138 GtkWidget *widget;
2139 GtkAllocation allocation;
2140
2141 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
2142
2143 if (widget == NULL) {
2144 return;
2145 }
2146
2147 gtk_widget_get_allocation (widget, &allocation);
2148 *width = allocation.width;
2149 *height = allocation.height;
2150
2151 if (!eel_canvas_accessible_on_screen (widget) ||
2152 !gtk_widget_is_drawable (widget)) {
2153 *x = G_MININT;
2154 *y = G_MININT;
2155
2156 return;
2157 }
2158
2159 if (gtk_widget_get_parent (widget)) {
2160 *x = allocation.x;
2161 *y = allocation.y;
2162 window = gtk_widget_get_parent_window (widget);
2163 } else {
2164 *x = 0;
2165 *y = 0;
2166 window = gtk_widget_get_window (widget);
2167 }
2168
2169 gdk_window_get_origin (window, &x_window, &y_window);
2170 *x += x_window;
2171 *y += y_window;
2172
2173 if (coord_type == ATK_XY_WINDOW) {
2174 window = gdk_window_get_toplevel (gtk_widget_get_window (widget));
2175 gdk_window_get_origin (window, &x_toplevel, &y_toplevel);
2176
2177 *x -= x_toplevel;
2178 *y -= y_toplevel;
2179 }
2180 }
2181
2182 static void
eel_canvas_accessible_get_size(AtkComponent * component,gint * width,gint * height)2183 eel_canvas_accessible_get_size (AtkComponent *component,
2184 gint *width,
2185 gint *height)
2186 {
2187 GtkWidget *widget;
2188
2189 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
2190
2191 if (widget == NULL) {
2192 return;
2193 }
2194
2195 *width = gtk_widget_get_allocated_width (widget);
2196 *height = gtk_widget_get_allocated_height (widget);
2197 }
2198
2199 static void
eel_canvas_accessible_component_init(gpointer iface,gpointer data)2200 eel_canvas_accessible_component_init (gpointer iface, gpointer data)
2201 {
2202 AtkComponentIface *component;
2203
2204 g_assert (G_TYPE_FROM_INTERFACE(iface) == ATK_TYPE_COMPONENT);
2205
2206 component = iface;
2207 component->get_extents = eel_canvas_accessible_get_extents;
2208 component->get_size = eel_canvas_accessible_get_size;
2209 }
2210
G_DEFINE_TYPE_WITH_CODE(EelCanvasAccessible,eel_canvas_accessible,GTK_TYPE_ACCESSIBLE,G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT,eel_canvas_accessible_component_init))2211 G_DEFINE_TYPE_WITH_CODE (EelCanvasAccessible, eel_canvas_accessible, GTK_TYPE_ACCESSIBLE,
2212 G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT, eel_canvas_accessible_component_init))
2213
2214 static void
2215 eel_canvas_accessible_class_init (EelCanvasAccessibleClass *klass)
2216 {
2217 AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass);
2218
2219 accessible_parent_class = g_type_class_peek_parent (klass);
2220
2221 atk_class->initialize = eel_canvas_accessible_initialize;
2222 atk_class->get_n_children = eel_canvas_accessible_get_n_children;
2223 atk_class->ref_child = eel_canvas_accessible_ref_child;
2224 /* below adapted from gtkwidgetaccessible.c */
2225 atk_class->ref_state_set = eel_canvas_accessible_ref_state_set;
2226 }
2227
2228 static void
eel_canvas_accessible_init(EelCanvasAccessible * accessible)2229 eel_canvas_accessible_init (EelCanvasAccessible *accessible)
2230 {
2231 }
2232
2233 static AtkObject *
eel_canvas_accessible_create(GObject * for_object)2234 eel_canvas_accessible_create (GObject *for_object)
2235 {
2236 GType type;
2237 AtkObject *accessible;
2238 EelCanvas *canvas;
2239
2240 canvas = EEL_CANVAS (for_object);
2241 g_return_val_if_fail (canvas != NULL, NULL);
2242
2243 type = eel_canvas_accessible_get_type ();
2244
2245 if (type == G_TYPE_INVALID) {
2246 return atk_no_op_object_new (for_object);
2247 }
2248
2249 accessible = g_object_new (type, NULL);
2250 atk_object_initialize (accessible, for_object);
2251 return accessible;
2252 }
2253
2254 static GType
eel_canvas_accessible_factory_get_accessible_type(void)2255 eel_canvas_accessible_factory_get_accessible_type (void)
2256 {
2257 return eel_canvas_accessible_get_type ();
2258 }
2259
2260 static AtkObject*
eel_canvas_accessible_factory_create_accessible(GObject * obj)2261 eel_canvas_accessible_factory_create_accessible (GObject *obj)
2262 {
2263 g_return_val_if_fail (G_IS_OBJECT (obj), NULL);
2264
2265 return eel_canvas_accessible_create (obj);
2266 }
2267
2268 static void
eel_canvas_accessible_factory_class_init(AtkObjectFactoryClass * klass)2269 eel_canvas_accessible_factory_class_init (AtkObjectFactoryClass *klass)
2270 {
2271 klass->create_accessible = eel_canvas_accessible_factory_create_accessible;
2272 klass->get_accessible_type = eel_canvas_accessible_factory_get_accessible_type;
2273 }
2274
2275 static GType
eel_canvas_accessible_factory_get_type(void)2276 eel_canvas_accessible_factory_get_type (void)
2277 {
2278 static GType type = 0;
2279
2280 if (!type) {
2281 static const GTypeInfo tinfo = {
2282 sizeof (AtkObjectFactoryClass),
2283 (GBaseInitFunc) NULL,
2284 (GBaseFinalizeFunc) NULL,
2285 (GClassInitFunc) eel_canvas_accessible_factory_class_init,
2286 NULL, /* class_finalize */
2287 NULL, /* class_data */
2288 sizeof (AtkObjectFactory),
2289 0, /* n_preallocs */
2290 NULL
2291 };
2292 type = g_type_register_static (ATK_TYPE_OBJECT_FACTORY,
2293 "EelCanvasAccessibilityFactory",
2294 &tinfo, 0);
2295 }
2296 return type;
2297 }
2298
2299
2300 /* Class initialization function for EelCanvasClass */
2301 static void
eel_canvas_class_init(EelCanvasClass * klass)2302 eel_canvas_class_init (EelCanvasClass *klass)
2303 {
2304 GObjectClass *gobject_class;
2305 GtkWidgetClass *widget_class;
2306
2307 gobject_class = (GObjectClass *)klass;
2308 widget_class = (GtkWidgetClass *) klass;
2309
2310 canvas_parent_class = g_type_class_peek_parent (klass);
2311
2312 gobject_class->set_property = eel_canvas_set_property;
2313 gobject_class->get_property = eel_canvas_get_property;
2314
2315 widget_class->destroy = eel_canvas_destroy;
2316 widget_class->map = eel_canvas_map;
2317 widget_class->unmap = eel_canvas_unmap;
2318 widget_class->realize = eel_canvas_realize;
2319 widget_class->unrealize = eel_canvas_unrealize;
2320 widget_class->size_allocate = eel_canvas_size_allocate;
2321 widget_class->button_press_event = eel_canvas_button;
2322 widget_class->button_release_event = eel_canvas_button;
2323 widget_class->motion_notify_event = eel_canvas_motion;
2324 widget_class->draw = eel_canvas_draw;
2325 widget_class->key_press_event = eel_canvas_key;
2326 widget_class->key_release_event = eel_canvas_key;
2327 widget_class->enter_notify_event = eel_canvas_crossing;
2328 widget_class->leave_notify_event = eel_canvas_crossing;
2329 widget_class->focus_in_event = eel_canvas_focus_in;
2330 widget_class->focus_out_event = eel_canvas_focus_out;
2331 widget_class->get_accessible = eel_canvas_get_accessible;
2332
2333 klass->draw_background = eel_canvas_draw_background;
2334 klass->request_update = eel_canvas_request_update_real;
2335
2336 canvas_signals[DRAW_BACKGROUND] =
2337 g_signal_new ("draw_background",
2338 G_TYPE_FROM_CLASS (klass),
2339 G_SIGNAL_RUN_LAST,
2340 G_STRUCT_OFFSET (EelCanvasClass, draw_background),
2341 NULL, NULL,
2342 g_cclosure_marshal_VOID__BOXED,
2343 G_TYPE_NONE, 1,
2344 CAIRO_GOBJECT_TYPE_CONTEXT);
2345
2346 atk_registry_set_factory_type (atk_get_default_registry (),
2347 EEL_TYPE_CANVAS,
2348 eel_canvas_accessible_factory_get_type ());
2349 }
2350
2351 /* Callback used when the root item of a canvas is destroyed. The user should
2352 * never ever do this, so we panic if this happens.
2353 */
2354 static void
panic_root_destroyed(GtkWidget * object,gpointer data)2355 panic_root_destroyed (GtkWidget *object, gpointer data)
2356 {
2357 g_error ("Eeeek, root item %p of canvas %p was destroyed!", object, data);
2358 }
2359
2360 /* Object initialization function for EelCanvas */
2361 static void
eel_canvas_init(EelCanvas * canvas)2362 eel_canvas_init (EelCanvas *canvas)
2363 {
2364 guint width, height;
2365 gtk_widget_set_can_focus (GTK_WIDGET (canvas), TRUE);
2366
2367 gtk_widget_set_redraw_on_allocate (GTK_WIDGET (canvas), FALSE);
2368
2369 canvas->scroll_x1 = 0.0;
2370 canvas->scroll_y1 = 0.0;
2371 gtk_layout_get_size (GTK_LAYOUT (canvas),
2372 &width, &height);
2373 canvas->scroll_x2 = width;
2374 canvas->scroll_y2 = height;
2375
2376 canvas->pixels_per_unit = 1.0;
2377
2378 canvas->pick_event.type = GDK_LEAVE_NOTIFY;
2379 canvas->pick_event.crossing.x = 0;
2380 canvas->pick_event.crossing.y = 0;
2381
2382 gtk_scrollable_set_hadjustment (GTK_SCROLLABLE (canvas), NULL);
2383 gtk_scrollable_set_vadjustment (GTK_SCROLLABLE (canvas), NULL);
2384
2385 /* Create the root item as a special case */
2386
2387 canvas->root = EEL_CANVAS_ITEM (g_object_new (eel_canvas_group_get_type (), NULL));
2388 canvas->root->canvas = canvas;
2389
2390 g_object_ref_sink (canvas->root);
2391
2392 canvas->root_destroy_id = g_signal_connect (G_OBJECT (canvas->root),
2393 "destroy", G_CALLBACK (panic_root_destroyed), canvas);
2394
2395 canvas->need_repick = TRUE;
2396 canvas->doing_update = FALSE;
2397 }
2398
2399 /* Convenience function to remove the idle handler of a canvas */
2400 static void
remove_idle(EelCanvas * canvas)2401 remove_idle (EelCanvas *canvas)
2402 {
2403 if (canvas->idle_id == 0)
2404 return;
2405
2406 g_source_remove (canvas->idle_id);
2407 canvas->idle_id = 0;
2408 }
2409
2410 /* Removes the transient state of the canvas (idle handler, grabs). */
2411 static void
shutdown_transients(EelCanvas * canvas)2412 shutdown_transients (EelCanvas *canvas)
2413 {
2414 /* We turn off the need_redraw flag, since if the canvas is mapped again
2415 * it will request a redraw anyways. We do not turn off the need_update
2416 * flag, though, because updates are not queued when the canvas remaps
2417 * itself.
2418 */
2419 if (canvas->need_redraw) {
2420 canvas->need_redraw = FALSE;
2421 }
2422
2423 if (canvas->grabbed_item) {
2424 eel_canvas_item_ungrab (canvas->grabbed_item, GDK_CURRENT_TIME);
2425 }
2426
2427 remove_idle (canvas);
2428 }
2429
2430 /* Destroy handler for EelCanvas */
2431 static void
eel_canvas_destroy(GtkWidget * object)2432 eel_canvas_destroy (GtkWidget *object)
2433 {
2434 EelCanvas *canvas;
2435
2436 g_return_if_fail (EEL_IS_CANVAS (object));
2437
2438 /* remember, destroy can be run multiple times! */
2439
2440 canvas = EEL_CANVAS (object);
2441
2442 if (canvas->root_destroy_id) {
2443 g_signal_handler_disconnect (G_OBJECT (canvas->root), canvas->root_destroy_id);
2444 canvas->root_destroy_id = 0;
2445 }
2446 if (canvas->root) {
2447 EelCanvasItem *root = canvas->root;
2448 canvas->root = NULL;
2449 eel_canvas_item_destroy (root);
2450 g_object_unref (root);
2451 }
2452
2453 shutdown_transients (canvas);
2454
2455 if (GTK_WIDGET_CLASS (canvas_parent_class)->destroy)
2456 (* GTK_WIDGET_CLASS (canvas_parent_class)->destroy) (object);
2457 }
2458
2459 /**
2460 * eel_canvas_new:
2461 * @void:
2462 *
2463 * Creates a new empty canvas. If you wish to use the
2464 * &EelCanvasImage item inside this canvas, then you must push the gdk_imlib
2465 * visual and colormap before calling this function, and they can be popped
2466 * afterwards.
2467 *
2468 * Return value: A newly-created canvas.
2469 **/
2470 GtkWidget *
eel_canvas_new(void)2471 eel_canvas_new (void)
2472 {
2473 return GTK_WIDGET (g_object_new (eel_canvas_get_type (), NULL));
2474 }
2475
2476 /* Map handler for the canvas */
2477 static void
eel_canvas_map(GtkWidget * widget)2478 eel_canvas_map (GtkWidget *widget)
2479 {
2480 EelCanvas *canvas;
2481
2482 g_return_if_fail (EEL_IS_CANVAS (widget));
2483
2484 /* Normal widget mapping stuff */
2485
2486 if (GTK_WIDGET_CLASS (canvas_parent_class)->map)
2487 (* GTK_WIDGET_CLASS (canvas_parent_class)->map) (widget);
2488
2489 canvas = EEL_CANVAS (widget);
2490
2491 /* Map items */
2492
2493 if ((canvas->root->flags & EEL_CANVAS_ITEM_VISIBLE) &&
2494 !(canvas->root->flags & EEL_CANVAS_ITEM_MAPPED) &&
2495 EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->map)
2496 (* EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->map) (canvas->root);
2497 }
2498
2499 /* Unmap handler for the canvas */
2500 static void
eel_canvas_unmap(GtkWidget * widget)2501 eel_canvas_unmap (GtkWidget *widget)
2502 {
2503 EelCanvas *canvas;
2504
2505 g_return_if_fail (EEL_IS_CANVAS (widget));
2506
2507 canvas = EEL_CANVAS (widget);
2508
2509 shutdown_transients (canvas);
2510
2511 /* Unmap items */
2512
2513 if (EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->unmap)
2514 (* EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->unmap) (canvas->root);
2515
2516 /* Normal widget unmapping stuff */
2517
2518 if (GTK_WIDGET_CLASS (canvas_parent_class)->unmap)
2519 (* GTK_WIDGET_CLASS (canvas_parent_class)->unmap) (widget);
2520 }
2521
2522 /* Realize handler for the canvas */
2523 static void
eel_canvas_realize(GtkWidget * widget)2524 eel_canvas_realize (GtkWidget *widget)
2525 {
2526 EelCanvas *canvas;
2527
2528 g_return_if_fail (EEL_IS_CANVAS (widget));
2529
2530 /* Normal widget realization stuff */
2531
2532 if (GTK_WIDGET_CLASS (canvas_parent_class)->realize)
2533 (* GTK_WIDGET_CLASS (canvas_parent_class)->realize) (widget);
2534
2535 canvas = EEL_CANVAS (widget);
2536
2537 gdk_window_set_events (gtk_layout_get_bin_window (GTK_LAYOUT (canvas)),
2538 (gdk_window_get_events (gtk_layout_get_bin_window (GTK_LAYOUT (canvas)))
2539 | GDK_EXPOSURE_MASK
2540 | GDK_BUTTON_PRESS_MASK
2541 | GDK_BUTTON_RELEASE_MASK
2542 | GDK_POINTER_MOTION_MASK
2543 | GDK_KEY_PRESS_MASK
2544 | GDK_KEY_RELEASE_MASK
2545 | GDK_ENTER_NOTIFY_MASK
2546 | GDK_LEAVE_NOTIFY_MASK
2547 | GDK_FOCUS_CHANGE_MASK));
2548
2549 /* Create our own temporary pixmap gc and realize all the items */
2550
2551 (* EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->realize) (canvas->root);
2552 }
2553
2554 /* Unrealize handler for the canvas */
2555 static void
eel_canvas_unrealize(GtkWidget * widget)2556 eel_canvas_unrealize (GtkWidget *widget)
2557 {
2558 EelCanvas *canvas;
2559
2560 g_return_if_fail (EEL_IS_CANVAS (widget));
2561
2562 canvas = EEL_CANVAS (widget);
2563
2564 shutdown_transients (canvas);
2565
2566 /* Unrealize items and parent widget */
2567
2568 (* EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->unrealize) (canvas->root);
2569
2570 if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
2571 (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
2572 }
2573
2574 /* Handles scrolling of the canvas. Adjusts the scrolling and zooming offset to
2575 * keep as much as possible of the canvas scrolling region in view.
2576 */
2577 static void
scroll_to(EelCanvas * canvas,int cx,int cy)2578 scroll_to (EelCanvas *canvas, int cx, int cy)
2579 {
2580 int scroll_width, scroll_height;
2581 int right_limit, bottom_limit;
2582 int old_zoom_xofs, old_zoom_yofs;
2583 int changed_x = FALSE, changed_y = FALSE;
2584 int canvas_width, canvas_height;
2585 GtkAllocation allocation;
2586 GtkAdjustment *vadjustment, *hadjustment;
2587 guint width, height;
2588
2589 gtk_widget_get_allocation (GTK_WIDGET (canvas), &allocation);
2590 canvas_width = allocation.width;
2591 canvas_height = allocation.height;
2592
2593 scroll_width = floor ((canvas->scroll_x2 - canvas->scroll_x1) * canvas->pixels_per_unit + 0.5);
2594 scroll_height = floor ((canvas->scroll_y2 - canvas->scroll_y1) * canvas->pixels_per_unit + 0.5);
2595
2596 right_limit = scroll_width - canvas_width;
2597 bottom_limit = scroll_height - canvas_height;
2598
2599 old_zoom_xofs = canvas->zoom_xofs;
2600 old_zoom_yofs = canvas->zoom_yofs;
2601
2602 if (right_limit < 0) {
2603 cx = 0;
2604 if (canvas->center_scroll_region) {
2605 canvas->zoom_xofs = (canvas_width - scroll_width) / 2;
2606 scroll_width = canvas_width;
2607 } else {
2608 canvas->zoom_xofs = 0;
2609 }
2610 } else if (cx < 0) {
2611 cx = 0;
2612 canvas->zoom_xofs = 0;
2613 } else if (cx > right_limit) {
2614 cx = right_limit;
2615 canvas->zoom_xofs = 0;
2616 } else
2617 canvas->zoom_xofs = 0;
2618
2619 if (bottom_limit < 0) {
2620 cy = 0;
2621 if (canvas->center_scroll_region) {
2622 canvas->zoom_yofs = (canvas_height - scroll_height) / 2;
2623 scroll_height = canvas_height;
2624 } else {
2625 canvas->zoom_yofs = 0;
2626 }
2627 } else if (cy < 0) {
2628 cy = 0;
2629 canvas->zoom_yofs = 0;
2630 } else if (cy > bottom_limit) {
2631 cy = bottom_limit;
2632 canvas->zoom_yofs = 0;
2633 } else
2634 canvas->zoom_yofs = 0;
2635
2636 if ((canvas->zoom_xofs != old_zoom_xofs) || (canvas->zoom_yofs != old_zoom_yofs)) {
2637 /* This can only occur, if either canvas size or widget size changes */
2638 /* So I think we can request full redraw here */
2639 /* More stuff - we have to mark root as needing fresh affine (Lauris) */
2640 if (!(canvas->root->flags & EEL_CANVAS_ITEM_NEED_DEEP_UPDATE)) {
2641 canvas->root->flags |= EEL_CANVAS_ITEM_NEED_DEEP_UPDATE;
2642 eel_canvas_request_update (canvas);
2643 }
2644 gtk_widget_queue_draw (GTK_WIDGET (canvas));
2645 }
2646
2647 hadjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (canvas));
2648 vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (canvas));
2649
2650 if (((int) gtk_adjustment_get_value (hadjustment)) != cx) {
2651 gtk_adjustment_set_value (hadjustment, cx);
2652 changed_x = TRUE;
2653 }
2654
2655 if (((int) gtk_adjustment_get_value (vadjustment)) != cy) {
2656 gtk_adjustment_set_value (vadjustment, cy);
2657 changed_y = TRUE;
2658 }
2659
2660 gtk_layout_get_size (&canvas->layout, &width, &height);
2661 if ((scroll_width != (int) width )|| (scroll_height != (int) height)) {
2662 gtk_layout_set_size (GTK_LAYOUT (canvas), scroll_width, scroll_height);
2663 }
2664
2665 /* Signal GtkLayout that it should do a redraw. */
2666 if (changed_x)
2667 g_signal_emit_by_name (hadjustment, "value_changed");
2668 if (changed_y)
2669 g_signal_emit_by_name (vadjustment, "value_changed");
2670 }
2671
2672 /* Size allocation handler for the canvas */
2673 static void
eel_canvas_size_allocate(GtkWidget * widget,GtkAllocation * allocation)2674 eel_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
2675 {
2676 EelCanvas *canvas;
2677 GtkAdjustment *vadjustment, *hadjustment;
2678
2679 g_return_if_fail (EEL_IS_CANVAS (widget));
2680 g_return_if_fail (allocation != NULL);
2681
2682 if (GTK_WIDGET_CLASS (canvas_parent_class)->size_allocate)
2683 (* GTK_WIDGET_CLASS (canvas_parent_class)->size_allocate) (widget, allocation);
2684
2685 canvas = EEL_CANVAS (widget);
2686
2687 /* Recenter the view, if appropriate */
2688
2689 hadjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (canvas));
2690 vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (canvas));
2691
2692 gtk_adjustment_set_page_size (hadjustment, allocation->width);
2693 gtk_adjustment_set_page_increment (hadjustment, allocation->width / 2);
2694
2695 gtk_adjustment_set_page_size (vadjustment, allocation->height);
2696 gtk_adjustment_set_page_increment (vadjustment, allocation->height / 2);
2697
2698 scroll_to (canvas,
2699 gtk_adjustment_get_value (hadjustment),
2700 gtk_adjustment_get_value (vadjustment));
2701
2702 g_signal_emit_by_name (hadjustment, "changed");
2703 g_signal_emit_by_name (vadjustment, "changed");
2704 }
2705
2706 /* Emits an event for an item in the canvas, be it the current item, grabbed
2707 * item, or focused item, as appropriate.
2708 */
2709
2710 static int
emit_event(EelCanvas * canvas,GdkEvent * event)2711 emit_event (EelCanvas *canvas, GdkEvent *event)
2712 {
2713 GdkEvent ev;
2714 gint finished;
2715 EelCanvasItem *item;
2716 EelCanvasItem *parent;
2717 guint mask;
2718
2719 /* Could be an old pick event */
2720 if (!gtk_widget_get_realized (GTK_WIDGET (canvas))) {
2721 return FALSE;
2722 }
2723
2724 /* Perform checks for grabbed items */
2725
2726 if (canvas->grabbed_item &&
2727 !is_descendant (canvas->current_item, canvas->grabbed_item)) {
2728 return FALSE;
2729 }
2730
2731 if (canvas->grabbed_item) {
2732 switch ((int) event->type) {
2733 case GDK_ENTER_NOTIFY:
2734 mask = GDK_ENTER_NOTIFY_MASK;
2735 break;
2736
2737 case GDK_LEAVE_NOTIFY:
2738 mask = GDK_LEAVE_NOTIFY_MASK;
2739 break;
2740
2741 case GDK_MOTION_NOTIFY:
2742 mask = GDK_POINTER_MOTION_MASK;
2743 break;
2744
2745 case GDK_BUTTON_PRESS:
2746 case GDK_2BUTTON_PRESS:
2747 case GDK_3BUTTON_PRESS:
2748 mask = GDK_BUTTON_PRESS_MASK;
2749 break;
2750
2751 case GDK_BUTTON_RELEASE:
2752 mask = GDK_BUTTON_RELEASE_MASK;
2753 break;
2754
2755 case GDK_KEY_PRESS:
2756 mask = GDK_KEY_PRESS_MASK;
2757 break;
2758
2759 case GDK_KEY_RELEASE:
2760 mask = GDK_KEY_RELEASE_MASK;
2761 break;
2762
2763 default:
2764 mask = 0;
2765 break;
2766 }
2767
2768 if (!(mask & canvas->grabbed_event_mask))
2769 return FALSE;
2770 }
2771
2772 /* Convert to world coordinates -- we have two cases because of diferent
2773 * offsets of the fields in the event structures.
2774 */
2775
2776 ev = *event;
2777
2778 switch ((int) ev.type)
2779 {
2780 case GDK_ENTER_NOTIFY:
2781 case GDK_LEAVE_NOTIFY:
2782 eel_canvas_window_to_world (canvas,
2783 ev.crossing.x, ev.crossing.y,
2784 &ev.crossing.x, &ev.crossing.y);
2785 break;
2786
2787 case GDK_MOTION_NOTIFY:
2788 eel_canvas_window_to_world (canvas,
2789 ev.motion.x, ev.motion.y,
2790 &ev.motion.x, &ev.motion.y);
2791 break;
2792
2793 case GDK_BUTTON_PRESS:
2794 case GDK_2BUTTON_PRESS:
2795 case GDK_3BUTTON_PRESS:
2796 eel_canvas_window_to_world (canvas,
2797 ev.motion.x, ev.motion.y,
2798 &ev.motion.x, &ev.motion.y);
2799 break;
2800
2801 case GDK_BUTTON_RELEASE:
2802 eel_canvas_window_to_world (canvas,
2803 ev.motion.x, ev.motion.y,
2804 &ev.motion.x, &ev.motion.y);
2805 break;
2806
2807 default:
2808 break;
2809 }
2810
2811 /* Choose where we send the event */
2812
2813 item = canvas->current_item;
2814
2815 if (canvas->focused_item
2816 && ((event->type == GDK_KEY_PRESS) ||
2817 (event->type == GDK_KEY_RELEASE) ||
2818 (event->type == GDK_FOCUS_CHANGE)))
2819 item = canvas->focused_item;
2820
2821 /* The event is propagated up the hierarchy (for if someone connected to
2822 * a group instead of a leaf event), and emission is stopped if a
2823 * handler returns TRUE, just like for GtkWidget events.
2824 */
2825
2826 finished = FALSE;
2827
2828 while (item && !finished) {
2829 g_object_ref (item);
2830
2831 g_signal_emit (
2832 G_OBJECT (item), item_signals[ITEM_EVENT], 0,
2833 &ev, &finished);
2834
2835 parent = item->parent;
2836 g_object_unref (item);
2837
2838 item = parent;
2839 }
2840
2841 return finished;
2842 }
2843
2844 /* Re-picks the current item in the canvas, based on the event's coordinates.
2845 * Also emits enter/leave events for items as appropriate.
2846 */
2847 static int
pick_current_item(EelCanvas * canvas,GdkEvent * event)2848 pick_current_item (EelCanvas *canvas, GdkEvent *event)
2849 {
2850 int button_down;
2851 double x, y;
2852 int cx, cy;
2853 int retval;
2854
2855 retval = FALSE;
2856
2857 /* If a button is down, we'll perform enter and leave events on the
2858 * current item, but not enter on any other item. This is more or less
2859 * like X pointer grabbing for canvas items.
2860 */
2861 button_down = canvas->state & (GDK_BUTTON1_MASK
2862 | GDK_BUTTON2_MASK
2863 | GDK_BUTTON3_MASK
2864 | GDK_BUTTON4_MASK
2865 | GDK_BUTTON5_MASK);
2866 if (!button_down)
2867 canvas->left_grabbed_item = FALSE;
2868
2869 /* Save the event in the canvas. This is used to synthesize enter and
2870 * leave events in case the current item changes. It is also used to
2871 * re-pick the current item if the current one gets deleted. Also,
2872 * synthesize an enter event.
2873 */
2874 if (event != &canvas->pick_event) {
2875 if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
2876 /* these fields have the same offsets in both types of events */
2877
2878 canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
2879 canvas->pick_event.crossing.window = event->motion.window;
2880 canvas->pick_event.crossing.send_event = event->motion.send_event;
2881 canvas->pick_event.crossing.subwindow = NULL;
2882 canvas->pick_event.crossing.x = event->motion.x;
2883 canvas->pick_event.crossing.y = event->motion.y;
2884 canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
2885 canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
2886 canvas->pick_event.crossing.focus = FALSE;
2887 canvas->pick_event.crossing.state = event->motion.state;
2888
2889 /* these fields don't have the same offsets in both types of events */
2890
2891 if (event->type == GDK_MOTION_NOTIFY) {
2892 canvas->pick_event.crossing.x_root = event->motion.x_root;
2893 canvas->pick_event.crossing.y_root = event->motion.y_root;
2894 } else {
2895 canvas->pick_event.crossing.x_root = event->button.x_root;
2896 canvas->pick_event.crossing.y_root = event->button.y_root;
2897 }
2898 } else
2899 canvas->pick_event = *event;
2900 }
2901
2902 /* Don't do anything else if this is a recursive call */
2903
2904 if (canvas->in_repick)
2905 return retval;
2906
2907 /* LeaveNotify means that there is no current item, so we don't look for one */
2908
2909 if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
2910 /* these fields don't have the same offsets in both types of events */
2911
2912 if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
2913 x = canvas->pick_event.crossing.x;
2914 y = canvas->pick_event.crossing.y;
2915 } else {
2916 x = canvas->pick_event.motion.x;
2917 y = canvas->pick_event.motion.y;
2918 }
2919
2920 /* canvas pixel coords */
2921
2922 cx = (int) (x + 0.5);
2923 cy = (int) (y + 0.5);
2924
2925 /* world coords */
2926 eel_canvas_c2w (canvas, cx, cy, &x, &y);
2927
2928 /* find the closest item */
2929 if (canvas->root->flags & EEL_CANVAS_ITEM_MAPPED)
2930 eel_canvas_item_invoke_point (canvas->root, x, y, cx, cy,
2931 &canvas->new_current_item);
2932 else
2933 canvas->new_current_item = NULL;
2934 } else
2935 canvas->new_current_item = NULL;
2936
2937 if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item)
2938 return retval; /* current item did not change */
2939
2940 /* Synthesize events for old and new current items */
2941
2942 if ((canvas->new_current_item != canvas->current_item)
2943 && (canvas->current_item != NULL)
2944 && !canvas->left_grabbed_item) {
2945 GdkEvent new_event;
2946
2947 new_event = canvas->pick_event;
2948 new_event.type = GDK_LEAVE_NOTIFY;
2949
2950 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
2951 new_event.crossing.subwindow = NULL;
2952 canvas->in_repick = TRUE;
2953 retval = emit_event (canvas, &new_event);
2954 canvas->in_repick = FALSE;
2955 }
2956
2957 /* new_current_item may have been set to NULL during the call to emit_event() above */
2958
2959 if ((canvas->new_current_item != canvas->current_item) && button_down) {
2960 canvas->left_grabbed_item = TRUE;
2961 return retval;
2962 }
2963
2964 /* Handle the rest of cases */
2965
2966 canvas->left_grabbed_item = FALSE;
2967 canvas->current_item = canvas->new_current_item;
2968
2969 if (canvas->current_item != NULL) {
2970 GdkEvent new_event;
2971
2972 new_event = canvas->pick_event;
2973 new_event.type = GDK_ENTER_NOTIFY;
2974 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
2975 new_event.crossing.subwindow = NULL;
2976 retval = emit_event (canvas, &new_event);
2977 }
2978
2979 return retval;
2980 }
2981
2982 /* Button event handler for the canvas */
2983 static gint
eel_canvas_button(GtkWidget * widget,GdkEventButton * event)2984 eel_canvas_button (GtkWidget *widget, GdkEventButton *event)
2985 {
2986 EelCanvas *canvas;
2987 int mask;
2988 int retval;
2989
2990 g_return_val_if_fail (EEL_IS_CANVAS (widget), FALSE);
2991 g_return_val_if_fail (event != NULL, FALSE);
2992
2993 /* Don't handle extra mouse button events */
2994 if (event->button > 5)
2995 return FALSE;
2996
2997 retval = FALSE;
2998
2999 canvas = EEL_CANVAS (widget);
3000
3001 /*
3002 * dispatch normally regardless of the event's window if an item has
3003 * has a pointer grab in effect
3004 */
3005 if (!canvas->grabbed_item && event->window != gtk_layout_get_bin_window (GTK_LAYOUT (canvas)))
3006 return retval;
3007
3008 switch (event->button) {
3009 case 1:
3010 mask = GDK_BUTTON1_MASK;
3011 break;
3012 case 2:
3013 mask = GDK_BUTTON2_MASK;
3014 break;
3015 case 3:
3016 mask = GDK_BUTTON3_MASK;
3017 break;
3018 case 4:
3019 mask = GDK_BUTTON4_MASK;
3020 break;
3021 case 5:
3022 mask = GDK_BUTTON5_MASK;
3023 break;
3024 default:
3025 mask = 0;
3026 }
3027
3028 switch ((int) event->type) {
3029 case GDK_BUTTON_PRESS:
3030 case GDK_2BUTTON_PRESS:
3031 case GDK_3BUTTON_PRESS:
3032 /* Pick the current item as if the button were not pressed, and
3033 * then process the event.
3034 */
3035 event->state ^= mask;
3036 canvas->state = event->state;
3037 pick_current_item (canvas, (GdkEvent *) event);
3038 event->state ^= mask;
3039 canvas->state = event->state;
3040 retval = emit_event (canvas, (GdkEvent *) event);
3041 break;
3042
3043 case GDK_BUTTON_RELEASE:
3044 /* Process the event as if the button were pressed, then repick
3045 * after the button has been released
3046 */
3047 canvas->state = event->state;
3048 retval = emit_event (canvas, (GdkEvent *) event);
3049 event->state ^= mask;
3050 canvas->state = event->state;
3051 pick_current_item (canvas, (GdkEvent *) event);
3052 event->state ^= mask;
3053 break;
3054
3055 default:
3056 g_assert_not_reached ();
3057 }
3058
3059 return retval;
3060 }
3061
3062 /* Motion event handler for the canvas */
3063 static gint
eel_canvas_motion(GtkWidget * widget,GdkEventMotion * event)3064 eel_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
3065 {
3066 EelCanvas *canvas;
3067
3068 g_return_val_if_fail (EEL_IS_CANVAS (widget), FALSE);
3069 g_return_val_if_fail (event != NULL, FALSE);
3070
3071 canvas = EEL_CANVAS (widget);
3072
3073 if (event->window != gtk_layout_get_bin_window (GTK_LAYOUT (canvas)))
3074 return FALSE;
3075
3076 canvas->state = event->state;
3077 pick_current_item (canvas, (GdkEvent *) event);
3078 return emit_event (canvas, (GdkEvent *) event);
3079 }
3080
3081 /* Key event handler for the canvas */
3082 static gint
eel_canvas_key(GtkWidget * widget,GdkEventKey * event)3083 eel_canvas_key (GtkWidget *widget, GdkEventKey *event)
3084 {
3085 EelCanvas *canvas;
3086
3087 g_return_val_if_fail (EEL_IS_CANVAS (widget), FALSE);
3088 g_return_val_if_fail (event != NULL, FALSE);
3089
3090 canvas = EEL_CANVAS (widget);
3091
3092 if (emit_event (canvas, (GdkEvent *) event))
3093 return TRUE;
3094 if (event->type == GDK_KEY_RELEASE)
3095 return GTK_WIDGET_CLASS (canvas_parent_class)->key_release_event (widget, event);
3096 else
3097 return GTK_WIDGET_CLASS (canvas_parent_class)->key_press_event (widget, event);
3098 }
3099
3100
3101 /* Crossing event handler for the canvas */
3102 static gint
eel_canvas_crossing(GtkWidget * widget,GdkEventCrossing * event)3103 eel_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
3104 {
3105 EelCanvas *canvas;
3106
3107 g_return_val_if_fail (EEL_IS_CANVAS (widget), FALSE);
3108 g_return_val_if_fail (event != NULL, FALSE);
3109
3110 canvas = EEL_CANVAS (widget);
3111
3112 if (event->window != gtk_layout_get_bin_window (GTK_LAYOUT (canvas)))
3113 return FALSE;
3114
3115 canvas->state = event->state;
3116 return pick_current_item (canvas, (GdkEvent *) event);
3117 }
3118
3119 /* Focus in handler for the canvas */
3120 static gint
eel_canvas_focus_in(GtkWidget * widget,GdkEventFocus * event)3121 eel_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
3122 {
3123 EelCanvas *canvas;
3124
3125 canvas = EEL_CANVAS (widget);
3126
3127 if (canvas->focused_item)
3128 return emit_event (canvas, (GdkEvent *) event);
3129 else
3130 return FALSE;
3131 }
3132
3133 static AtkObject *
eel_canvas_get_accessible(GtkWidget * widget)3134 eel_canvas_get_accessible (GtkWidget *widget)
3135 {
3136 return atk_gobject_accessible_for_object (G_OBJECT (widget));
3137 }
3138
3139 /* Focus out handler for the canvas */
3140 static gint
eel_canvas_focus_out(GtkWidget * widget,GdkEventFocus * event)3141 eel_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
3142 {
3143 EelCanvas *canvas;
3144
3145 canvas = EEL_CANVAS (widget);
3146
3147 if (canvas->focused_item)
3148 return emit_event (canvas, (GdkEvent *) event);
3149 else
3150 return FALSE;
3151 }
3152
3153
3154 static cairo_region_t *
eel_cairo_get_clip_region(cairo_t * cr)3155 eel_cairo_get_clip_region (cairo_t *cr)
3156 {
3157 cairo_rectangle_list_t *list;
3158 cairo_region_t *region;
3159 int i;
3160
3161 list = cairo_copy_clip_rectangle_list (cr);
3162 if (list->status == CAIRO_STATUS_CLIP_NOT_REPRESENTABLE) {
3163 cairo_rectangle_int_t clip_rect;
3164
3165 cairo_rectangle_list_destroy (list);
3166
3167 if (!gdk_cairo_get_clip_rectangle (cr, &clip_rect))
3168 return NULL;
3169 return cairo_region_create_rectangle (&clip_rect);
3170 }
3171
3172
3173 region = cairo_region_create ();
3174 for (i = list->num_rectangles - 1; i >= 0; --i) {
3175 cairo_rectangle_t *rect = &list->rectangles[i];
3176 cairo_rectangle_int_t clip_rect;
3177
3178 clip_rect.x = floor (rect->x);
3179 clip_rect.y = floor (rect->y);
3180 clip_rect.width = ceil (rect->x + rect->width) - clip_rect.x;
3181 clip_rect.height = ceil (rect->y + rect->height) - clip_rect.y;
3182
3183 if (cairo_region_union_rectangle (region, &clip_rect) != CAIRO_STATUS_SUCCESS) {
3184 cairo_region_destroy (region);
3185 region = NULL;
3186 break;
3187 }
3188 }
3189
3190 cairo_rectangle_list_destroy (list);
3191 return region;
3192 }
3193
3194 /* Expose handler for the canvas */
3195 static gboolean
eel_canvas_draw(GtkWidget * widget,cairo_t * cr)3196 eel_canvas_draw (GtkWidget *widget, cairo_t *cr)
3197 {
3198 EelCanvas *canvas;
3199 GdkWindow *bin_window;
3200 cairo_region_t *region;
3201
3202 if (!gdk_cairo_get_clip_rectangle (cr, NULL))
3203 return FALSE;
3204
3205 bin_window = gtk_layout_get_bin_window (GTK_LAYOUT (widget));
3206
3207 if (!gtk_cairo_should_draw_window (cr, bin_window))
3208 return FALSE;
3209
3210 cairo_save (cr);
3211
3212 gtk_cairo_transform_to_window (cr, widget, bin_window);
3213
3214 region = eel_cairo_get_clip_region (cr);
3215
3216 if (region == NULL) {
3217 cairo_restore (cr);
3218 return FALSE;
3219 }
3220
3221 #ifdef VERBOSE
3222 g_print ("Draw\n");
3223 #endif
3224 /* If there are any outstanding items that need updating, do them now */
3225 canvas = EEL_CANVAS (widget);
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 cairo_restore (cr);
3253
3254 /* Chain up to get exposes on child widgets */
3255 if (GTK_WIDGET_CLASS (canvas_parent_class)->draw)
3256 GTK_WIDGET_CLASS (canvas_parent_class)->draw (widget, cr);
3257
3258 cairo_region_destroy (region);
3259 return FALSE;
3260 }
3261
3262 static void
eel_canvas_draw_background(EelCanvas * canvas,cairo_t * cr)3263 eel_canvas_draw_background (EelCanvas *canvas,
3264 cairo_t *cr)
3265 {
3266 cairo_rectangle_int_t rect;
3267 GtkStyleContext *style_context;
3268 GdkRGBA color;
3269
3270 if (!gdk_cairo_get_clip_rectangle (cr, &rect))
3271 return;
3272
3273 cairo_save (cr);
3274 /* By default, we use the style background. */
3275 style_context = gtk_widget_get_style_context (GTK_WIDGET (canvas));
3276 gtk_style_context_get_background_color (style_context, GTK_STATE_FLAG_NORMAL, &color);
3277 gdk_cairo_set_source_rgba (cr, &color);
3278 gdk_cairo_rectangle (cr, &rect);
3279 cairo_fill (cr);
3280 cairo_restore (cr);
3281 }
3282
3283 static void
do_update(EelCanvas * canvas)3284 do_update (EelCanvas *canvas)
3285 {
3286 /* Cause the update if necessary */
3287
3288 update_again:
3289 if (canvas->need_update) {
3290 g_return_if_fail (!canvas->doing_update);
3291
3292 canvas->doing_update = TRUE;
3293 eel_canvas_item_invoke_update (canvas->root, 0, 0, 0);
3294
3295 g_return_if_fail (canvas->doing_update);
3296
3297 canvas->doing_update = FALSE;
3298
3299 canvas->need_update = FALSE;
3300 }
3301
3302 /* Pick new current item */
3303
3304 while (canvas->need_repick) {
3305 canvas->need_repick = FALSE;
3306 pick_current_item (canvas, &canvas->pick_event);
3307 }
3308
3309 /* it is possible that during picking we emitted an event in which
3310 the user then called some function which then requested update
3311 of something. Without this we'd be left in a state where
3312 need_update would have been left TRUE and the canvas would have
3313 been left unpainted. */
3314 if (canvas->need_update) {
3315 goto update_again;
3316 }
3317 }
3318
3319 /* Idle handler for the canvas. It deals with pending updates and redraws. */
3320 static gint
idle_handler(gpointer data)3321 idle_handler (gpointer data)
3322 {
3323 EelCanvas *canvas;
3324
3325 canvas = EEL_CANVAS (data);
3326 do_update (canvas);
3327
3328 /* Reset idle id */
3329 canvas->idle_id = 0;
3330
3331 return FALSE;
3332 }
3333
3334 /* Convenience function to add an idle handler to a canvas */
3335 static void
add_idle(EelCanvas * canvas)3336 add_idle (EelCanvas *canvas)
3337 {
3338 if (!canvas->idle_id) {
3339 /* We let the update idle handler have higher priority
3340 * than the redraw idle handler so the canvas state
3341 * will be updated during the expose event. canvas in
3342 * expose_event.
3343 */
3344 canvas->idle_id = g_idle_add_full (GDK_PRIORITY_REDRAW - 20,
3345 idle_handler, canvas, NULL);
3346 }
3347 }
3348
3349 /**
3350 * eel_canvas_root:
3351 * @canvas: A canvas.
3352 *
3353 * Queries the root group of a canvas.
3354 *
3355 * Return value: The root group of the specified canvas.
3356 **/
3357 EelCanvasGroup *
eel_canvas_root(EelCanvas * canvas)3358 eel_canvas_root (EelCanvas *canvas)
3359 {
3360 g_return_val_if_fail (EEL_IS_CANVAS (canvas), NULL);
3361
3362 return EEL_CANVAS_GROUP (canvas->root);
3363 }
3364
3365
3366 /**
3367 * eel_canvas_set_scroll_region:
3368 * @canvas: A canvas.
3369 * @x1: Leftmost limit of the scrolling region.
3370 * @y1: Upper limit of the scrolling region.
3371 * @x2: Rightmost limit of the scrolling region.
3372 * @y2: Lower limit of the scrolling region.
3373 *
3374 * Sets the scrolling region of a canvas to the specified rectangle. The canvas
3375 * will then be able to scroll only within this region. The view of the canvas
3376 * is adjusted as appropriate to display as much of the new region as possible.
3377 **/
3378 void
eel_canvas_set_scroll_region(EelCanvas * canvas,double x1,double y1,double x2,double y2)3379 eel_canvas_set_scroll_region (EelCanvas *canvas, double x1, double y1, double x2, double y2)
3380 {
3381 double wxofs, wyofs;
3382 int xofs, yofs;
3383
3384 g_return_if_fail (EEL_IS_CANVAS (canvas));
3385
3386 if ((canvas->scroll_x1 == x1) && (canvas->scroll_y1 == y1) &&
3387 (canvas->scroll_x2 == x2) && (canvas->scroll_y2 == y2)) {
3388 return;
3389 }
3390
3391 /*
3392 * Set the new scrolling region. If possible, do not move the visible contents of the
3393 * canvas.
3394 */
3395
3396 eel_canvas_c2w (canvas,
3397 gtk_adjustment_get_value (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (canvas))) + canvas->zoom_xofs,
3398 gtk_adjustment_get_value (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (canvas))) + canvas->zoom_yofs,
3399 /*canvas->zoom_xofs,
3400 canvas->zoom_yofs,*/
3401 &wxofs, &wyofs);
3402
3403 canvas->scroll_x1 = x1;
3404 canvas->scroll_y1 = y1;
3405 canvas->scroll_x2 = x2;
3406 canvas->scroll_y2 = y2;
3407
3408 eel_canvas_w2c (canvas, wxofs, wyofs, &xofs, &yofs);
3409
3410 scroll_to (canvas, xofs, yofs);
3411
3412 canvas->need_repick = TRUE;
3413
3414 if (!(canvas->root->flags & EEL_CANVAS_ITEM_NEED_DEEP_UPDATE)) {
3415 canvas->root->flags |= EEL_CANVAS_ITEM_NEED_DEEP_UPDATE;
3416 eel_canvas_request_update (canvas);
3417 }
3418 }
3419
3420
3421 /**
3422 * eel_canvas_get_scroll_region:
3423 * @canvas: A canvas.
3424 * @x1: Leftmost limit of the scrolling region (return value).
3425 * @y1: Upper limit of the scrolling region (return value).
3426 * @x2: Rightmost limit of the scrolling region (return value).
3427 * @y2: Lower limit of the scrolling region (return value).
3428 *
3429 * Queries the scrolling region of a canvas.
3430 **/
3431 void
eel_canvas_get_scroll_region(EelCanvas * canvas,double * x1,double * y1,double * x2,double * y2)3432 eel_canvas_get_scroll_region (EelCanvas *canvas, double *x1, double *y1, double *x2, double *y2)
3433 {
3434 g_return_if_fail (EEL_IS_CANVAS (canvas));
3435
3436 if (x1)
3437 *x1 = canvas->scroll_x1;
3438
3439 if (y1)
3440 *y1 = canvas->scroll_y1;
3441
3442 if (x2)
3443 *x2 = canvas->scroll_x2;
3444
3445 if (y2)
3446 *y2 = canvas->scroll_y2;
3447 }
3448
3449 void
eel_canvas_set_center_scroll_region(EelCanvas * canvas,gboolean center_scroll_region)3450 eel_canvas_set_center_scroll_region (EelCanvas *canvas,
3451 gboolean center_scroll_region)
3452 {
3453 g_return_if_fail (EEL_IS_CANVAS (canvas));
3454
3455 canvas->center_scroll_region = center_scroll_region != 0;
3456
3457 scroll_to (canvas,
3458 gtk_adjustment_get_value (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (&canvas->layout))),
3459 gtk_adjustment_get_value (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (&canvas->layout))));
3460 }
3461
3462
3463 /**
3464 * eel_canvas_set_pixels_per_unit:
3465 * @canvas: A canvas.
3466 * @n: The number of pixels that correspond to one canvas unit.
3467 *
3468 * Sets the zooming factor of a canvas by specifying the number of pixels that
3469 * correspond to one canvas unit.
3470 **/
3471 void
eel_canvas_set_pixels_per_unit(EelCanvas * canvas,double n)3472 eel_canvas_set_pixels_per_unit (EelCanvas *canvas, double n)
3473 {
3474 GtkWidget *widget;
3475 double cx, cy;
3476 int x1, y1;
3477 int center_x, center_y;
3478 GdkWindow *window;
3479 GdkWindowAttr attributes;
3480 gint attributes_mask;
3481 GtkAllocation allocation;
3482 GtkAdjustment *vadjustment, *hadjustment;
3483
3484 g_return_if_fail (EEL_IS_CANVAS (canvas));
3485 g_return_if_fail (n > EEL_CANVAS_EPSILON);
3486
3487 widget = GTK_WIDGET (canvas);
3488
3489 gtk_widget_get_allocation (widget, &allocation);
3490 center_x = allocation.width / 2;
3491 center_y = allocation.height / 2;
3492
3493 /* Find the coordinates of the screen center in units. */
3494 hadjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (canvas));
3495 vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (canvas));
3496 cx = (gtk_adjustment_get_value (hadjustment) + center_x) / canvas->pixels_per_unit + canvas->scroll_x1 + canvas->zoom_xofs;
3497 cy = (gtk_adjustment_get_value (vadjustment) + center_y) / canvas->pixels_per_unit + canvas->scroll_y1 + canvas->zoom_yofs;
3498
3499 /* Now calculate the new offset of the upper left corner. (round not truncate) */
3500 x1 = ((cx - canvas->scroll_x1) * n) - center_x + .5;
3501 y1 = ((cy - canvas->scroll_y1) * n) - center_y + .5;
3502
3503 canvas->pixels_per_unit = n;
3504
3505 if (!(canvas->root->flags & EEL_CANVAS_ITEM_NEED_DEEP_UPDATE)) {
3506 canvas->root->flags |= EEL_CANVAS_ITEM_NEED_DEEP_UPDATE;
3507 eel_canvas_request_update (canvas);
3508 }
3509
3510 /* Map a background None window over the bin_window to avoid
3511 * scrolling the window scroll causing exposes.
3512 */
3513 window = NULL;
3514 if (gtk_widget_get_mapped (widget)) {
3515 attributes.window_type = GDK_WINDOW_CHILD;
3516 gtk_widget_get_allocation (widget, &allocation);
3517 attributes.x = allocation.x;
3518 attributes.y = allocation.y;
3519 attributes.width = allocation.width;
3520 attributes.height = allocation.height;
3521 attributes.wclass = GDK_INPUT_OUTPUT;
3522 attributes.visual = gtk_widget_get_visual (widget);
3523 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
3524
3525 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
3526
3527 window = gdk_window_new (gtk_widget_get_parent_window (widget),
3528 &attributes, attributes_mask);
3529 gdk_window_set_user_data (window, widget);
3530
3531 gdk_window_show (window);
3532 }
3533
3534 scroll_to (canvas, x1, y1);
3535
3536 /* If we created a an overlapping background None window, remove it how.
3537 *
3538 * TODO: We would like to temporarily set the bin_window background to
3539 * None to avoid clearing the bin_window to the background, but gdk doesn't
3540 * expose enought to let us do this, so we get a flash-effect here. At least
3541 * it looks better than scroll + expose.
3542 */
3543 if (window != NULL) {
3544 gdk_window_hide (window);
3545 gdk_window_set_user_data (window, NULL);
3546 gdk_window_destroy (window);
3547 }
3548
3549 canvas->need_repick = TRUE;
3550 }
3551
3552 /**
3553 * eel_canvas_scroll_to:
3554 * @canvas: A canvas.
3555 * @cx: Horizontal scrolling offset in canvas pixel units.
3556 * @cy: Vertical scrolling offset in canvas pixel units.
3557 *
3558 * Makes a canvas scroll to the specified offsets, given in canvas pixel units.
3559 * The canvas will adjust the view so that it is not outside the scrolling
3560 * region. This function is typically not used, as it is better to hook
3561 * scrollbars to the canvas layout's scrolling adjusments.
3562 **/
3563 void
eel_canvas_scroll_to(EelCanvas * canvas,int cx,int cy)3564 eel_canvas_scroll_to (EelCanvas *canvas, int cx, int cy)
3565 {
3566 g_return_if_fail (EEL_IS_CANVAS (canvas));
3567
3568 scroll_to (canvas, cx, cy);
3569 }
3570
3571 /**
3572 * eel_canvas_get_scroll_offsets:
3573 * @canvas: A canvas.
3574 * @cx: Horizontal scrolling offset (return value).
3575 * @cy: Vertical scrolling offset (return value).
3576 *
3577 * Queries the scrolling offsets of a canvas. The values are returned in canvas
3578 * pixel units.
3579 **/
3580 void
eel_canvas_get_scroll_offsets(EelCanvas * canvas,int * cx,int * cy)3581 eel_canvas_get_scroll_offsets (EelCanvas *canvas, int *cx, int *cy)
3582 {
3583 g_return_if_fail (EEL_IS_CANVAS (canvas));
3584
3585 if (cx)
3586 *cx = gtk_adjustment_get_value (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (canvas)));
3587
3588 if (cy)
3589 *cy = gtk_adjustment_get_value (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (canvas)));
3590 }
3591
3592 /**
3593 * eel_canvas_update_now:
3594 * @canvas: A canvas.
3595 *
3596 * Forces an immediate update and redraw of a canvas. If the canvas does not
3597 * have any pending update or redraw requests, then no action is taken. This is
3598 * typically only used by applications that need explicit control of when the
3599 * display is updated, like games. It is not needed by normal applications.
3600 */
3601 void
eel_canvas_update_now(EelCanvas * canvas)3602 eel_canvas_update_now (EelCanvas *canvas)
3603 {
3604 g_return_if_fail (EEL_IS_CANVAS (canvas));
3605
3606 if (!(canvas->need_update || canvas->need_redraw))
3607 return;
3608 remove_idle (canvas);
3609 do_update (canvas);
3610 }
3611
3612 /**
3613 * eel_canvas_get_item_at:
3614 * @canvas: A canvas.
3615 * @x: X position in world coordinates.
3616 * @y: Y position in world coordinates.
3617 *
3618 * Looks for the item that is under the specified position, which must be
3619 * specified in world coordinates.
3620 *
3621 * Return value: The sought item, or NULL if no item is at the specified
3622 * coordinates.
3623 **/
3624 EelCanvasItem *
eel_canvas_get_item_at(EelCanvas * canvas,double x,double y)3625 eel_canvas_get_item_at (EelCanvas *canvas, double x, double y)
3626 {
3627 EelCanvasItem *item;
3628 double dist;
3629 int cx, cy;
3630
3631 g_return_val_if_fail (EEL_IS_CANVAS (canvas), NULL);
3632
3633 eel_canvas_w2c (canvas, x, y, &cx, &cy);
3634
3635 dist = eel_canvas_item_invoke_point (canvas->root, x, y, cx, cy, &item);
3636 if ((int) (dist * canvas->pixels_per_unit + 0.5) <= canvas->close_enough)
3637 return item;
3638 else
3639 return NULL;
3640 }
3641
3642 /* Queues an update of the canvas */
3643 static void
eel_canvas_request_update(EelCanvas * canvas)3644 eel_canvas_request_update (EelCanvas *canvas)
3645 {
3646 EEL_CANVAS_GET_CLASS (canvas)->request_update (canvas);
3647 }
3648
3649 static void
eel_canvas_request_update_real(EelCanvas * canvas)3650 eel_canvas_request_update_real (EelCanvas *canvas)
3651 {
3652 canvas->need_update = TRUE;
3653 add_idle (canvas);
3654 }
3655
3656 /**
3657 * eel_canvas_request_redraw:
3658 * @canvas: A canvas.
3659 * @x1: Leftmost coordinate of the rectangle to be redrawn.
3660 * @y1: Upper coordinate of the rectangle to be redrawn.
3661 * @x2: Rightmost coordinate of the rectangle to be redrawn, plus 1.
3662 * @y2: Lower coordinate of the rectangle to be redrawn, plus 1.
3663 *
3664 * Convenience function that informs a canvas that the specified rectangle needs
3665 * to be repainted. The rectangle includes @x1 and @y1, but not @x2 and @y2.
3666 * To be used only by item implementations.
3667 **/
3668 void
eel_canvas_request_redraw(EelCanvas * canvas,int x1,int y1,int x2,int y2)3669 eel_canvas_request_redraw (EelCanvas *canvas, int x1, int y1, int x2, int y2)
3670 {
3671 GdkRectangle bbox;
3672
3673 g_return_if_fail (EEL_IS_CANVAS (canvas));
3674
3675 if (!gtk_widget_is_drawable (GTK_WIDGET (canvas))
3676 || (x1 >= x2) || (y1 >= y2)) return;
3677
3678 bbox.x = x1;
3679 bbox.y = y1;
3680 bbox.width = x2 - x1;
3681 bbox.height = y2 - y1;
3682
3683 gdk_window_invalidate_rect (gtk_layout_get_bin_window (GTK_LAYOUT (canvas)),
3684 &bbox, FALSE);
3685 }
3686
3687 /**
3688 * eel_canvas_w2c:
3689 * @canvas: A canvas.
3690 * @wx: World X coordinate.
3691 * @wy: World Y coordinate.
3692 * @cx: X pixel coordinate (return value).
3693 * @cy: Y pixel coordinate (return value).
3694 *
3695 * Converts world coordinates into canvas pixel coordinates.
3696 **/
3697 void
eel_canvas_w2c(EelCanvas * canvas,double wx,double wy,int * cx,int * cy)3698 eel_canvas_w2c (EelCanvas *canvas, double wx, double wy, int *cx, int *cy)
3699 {
3700 double zoom;
3701
3702 g_return_if_fail (EEL_IS_CANVAS (canvas));
3703
3704 zoom = canvas->pixels_per_unit;
3705
3706 if (cx)
3707 *cx = floor ((wx - canvas->scroll_x1)*zoom + canvas->zoom_xofs + 0.5);
3708 if (cy)
3709 *cy = floor ((wy - canvas->scroll_y1)*zoom + canvas->zoom_yofs + 0.5);
3710 }
3711
3712 /**
3713 * eel_canvas_w2c:
3714 * @canvas: A canvas.
3715 * @world: rectangle in world coordinates.
3716 * @canvas: rectangle in canvase coordinates.
3717 *
3718 * Converts rectangles in world coordinates into canvas pixel coordinates.
3719 **/
3720 void
eel_canvas_w2c_rect_d(EelCanvas * canvas,double * x1,double * y1,double * x2,double * y2)3721 eel_canvas_w2c_rect_d (EelCanvas *canvas,
3722 double *x1, double *y1,
3723 double *x2, double *y2)
3724 {
3725 eel_canvas_w2c_d (canvas,
3726 *x1, *y1,
3727 x1, y1);
3728 eel_canvas_w2c_d (canvas,
3729 *x2, *y2,
3730 x2, y2);
3731 }
3732
3733
3734
3735 /**
3736 * eel_canvas_w2c_d:
3737 * @canvas: A canvas.
3738 * @wx: World X coordinate.
3739 * @wy: World Y coordinate.
3740 * @cx: X pixel coordinate (return value).
3741 * @cy: Y pixel coordinate (return value).
3742 *
3743 * Converts world coordinates into canvas pixel coordinates. This version
3744 * produces coordinates in floating point coordinates, for greater precision.
3745 **/
3746 void
eel_canvas_w2c_d(EelCanvas * canvas,double wx,double wy,double * cx,double * cy)3747 eel_canvas_w2c_d (EelCanvas *canvas, double wx, double wy, double *cx, double *cy)
3748 {
3749 double zoom;
3750
3751 g_return_if_fail (EEL_IS_CANVAS (canvas));
3752
3753 zoom = canvas->pixels_per_unit;
3754
3755 if (cx)
3756 *cx = (wx - canvas->scroll_x1)*zoom + canvas->zoom_xofs;
3757 if (cy)
3758 *cy = (wy - canvas->scroll_y1)*zoom + canvas->zoom_yofs;
3759 }
3760
3761
3762 /**
3763 * eel_canvas_c2w:
3764 * @canvas: A canvas.
3765 * @cx: Canvas pixel X coordinate.
3766 * @cy: Canvas pixel Y coordinate.
3767 * @wx: X world coordinate (return value).
3768 * @wy: Y world coordinate (return value).
3769 *
3770 * Converts canvas pixel coordinates to world coordinates.
3771 **/
3772 void
eel_canvas_c2w(EelCanvas * canvas,int cx,int cy,double * wx,double * wy)3773 eel_canvas_c2w (EelCanvas *canvas, int cx, int cy, double *wx, double *wy)
3774 {
3775 double zoom;
3776
3777 g_return_if_fail (EEL_IS_CANVAS (canvas));
3778
3779 zoom = canvas->pixels_per_unit;
3780
3781 if (wx)
3782 *wx = (cx - canvas->zoom_xofs)/zoom + canvas->scroll_x1;
3783 if (wy)
3784 *wy = (cy - canvas->zoom_yofs)/zoom + canvas->scroll_y1;
3785 }
3786
3787
3788 /**
3789 * eel_canvas_window_to_world:
3790 * @canvas: A canvas.
3791 * @winx: Window-relative X coordinate.
3792 * @winy: Window-relative Y coordinate.
3793 * @worldx: X world coordinate (return value).
3794 * @worldy: Y world coordinate (return value).
3795 *
3796 * Converts window-relative coordinates into world coordinates. You can use
3797 * this when you need to convert mouse coordinates into world coordinates, for
3798 * example.
3799 * Window coordinates are really the same as canvas coordinates now, but this
3800 * function is here for backwards compatibility reasons.
3801 **/
3802 void
eel_canvas_window_to_world(EelCanvas * canvas,double winx,double winy,double * worldx,double * worldy)3803 eel_canvas_window_to_world (EelCanvas *canvas, double winx, double winy,
3804 double *worldx, double *worldy)
3805 {
3806 g_return_if_fail (EEL_IS_CANVAS (canvas));
3807
3808 if (worldx)
3809 *worldx = canvas->scroll_x1 + ((winx - canvas->zoom_xofs)
3810 / canvas->pixels_per_unit);
3811
3812 if (worldy)
3813 *worldy = canvas->scroll_y1 + ((winy - canvas->zoom_yofs)
3814 / canvas->pixels_per_unit);
3815 }
3816
3817
3818 /**
3819 * eel_canvas_world_to_window:
3820 * @canvas: A canvas.
3821 * @worldx: World X coordinate.
3822 * @worldy: World Y coordinate.
3823 * @winx: X window-relative coordinate.
3824 * @winy: Y window-relative coordinate.
3825 *
3826 * Converts world coordinates into window-relative coordinates.
3827 * Window coordinates are really the same as canvas coordinates now, but this
3828 * function is here for backwards compatibility reasons.
3829 **/
3830 void
eel_canvas_world_to_window(EelCanvas * canvas,double worldx,double worldy,double * winx,double * winy)3831 eel_canvas_world_to_window (EelCanvas *canvas, double worldx, double worldy,
3832 double *winx, double *winy)
3833 {
3834 g_return_if_fail (EEL_IS_CANVAS (canvas));
3835
3836 if (winx)
3837 *winx = (canvas->pixels_per_unit)*(worldx - canvas->scroll_x1) + canvas->zoom_xofs;
3838
3839 if (winy)
3840 *winy = (canvas->pixels_per_unit)*(worldy - canvas->scroll_y1) + canvas->zoom_yofs;
3841 }
3842
3843 static gboolean
boolean_handled_accumulator(GSignalInvocationHint * ihint,GValue * return_accu,const GValue * handler_return,gpointer data)3844 boolean_handled_accumulator (GSignalInvocationHint *ihint,
3845 GValue *return_accu,
3846 const GValue *handler_return,
3847 gpointer data)
3848 {
3849 gboolean signal_handled;
3850
3851 signal_handled = g_value_get_boolean (handler_return);
3852 g_value_set_boolean (return_accu, signal_handled);
3853
3854 return !signal_handled;
3855 }
3856
3857 static guint
eel_canvas_item_accessible_add_focus_handler(AtkComponent * component,AtkFocusHandler handler)3858 eel_canvas_item_accessible_add_focus_handler (AtkComponent *component,
3859 AtkFocusHandler handler)
3860 {
3861 GSignalMatchType match_type;
3862 guint signal_id;
3863
3864 match_type = G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC;
3865 signal_id = g_signal_lookup ("focus-event", ATK_TYPE_OBJECT);
3866
3867 if (!g_signal_handler_find (component, match_type, signal_id, 0, NULL,
3868 (gpointer) handler, NULL)) {
3869 return g_signal_connect_closure_by_id (component,
3870 signal_id, 0,
3871 g_cclosure_new (
3872 G_CALLBACK (handler), NULL,
3873 (GClosureNotify) NULL),
3874 FALSE);
3875 }
3876 return 0;
3877 }
3878
3879 static void
eel_canvas_item_accessible_get_item_extents(EelCanvasItem * item,GdkRectangle * rect)3880 eel_canvas_item_accessible_get_item_extents (EelCanvasItem *item,
3881 GdkRectangle *rect)
3882 {
3883 double bx1, bx2, by1, by2;
3884 gint scroll_x, scroll_y;
3885 gint x1, x2, y1, y2;
3886
3887 eel_canvas_item_get_bounds (item, &bx1, &by1, &bx2, &by2);
3888 eel_canvas_w2c_rect_d (item->canvas, &bx1, &by1, &bx2, &by2);
3889 eel_canvas_get_scroll_offsets (item->canvas, &scroll_x, &scroll_y);
3890 x1 = floor (bx1 + .5);
3891 y1 = floor (by1 + .5);
3892 x2 = floor (bx2 + .5);
3893 y2 = floor (by2 + .5);
3894 rect->x = x1 - scroll_x;
3895 rect->y = y1 - scroll_y;
3896 rect->width = x2 - x1;
3897 rect->height = y2 - y1;
3898 }
3899
3900 static gboolean
eel_canvas_item_accessible_is_item_in_window(EelCanvasItem * item,GdkRectangle * rect)3901 eel_canvas_item_accessible_is_item_in_window (EelCanvasItem *item,
3902 GdkRectangle *rect)
3903 {
3904 GtkWidget *widget;
3905 gboolean retval;
3906
3907 widget = GTK_WIDGET (item->canvas);
3908 if (gtk_widget_get_window (widget)) {
3909 int window_width, window_height;
3910
3911 gdk_window_get_geometry (gtk_widget_get_window (widget), NULL, NULL,
3912 &window_width, &window_height);
3913 /*
3914 * Check whether rectangles intersect
3915 */
3916 if (rect->x + rect->width < 0 ||
3917 rect->y + rect->height < 0 ||
3918 rect->x > window_width ||
3919 rect->y > window_height) {
3920 retval = FALSE;
3921 } else {
3922 retval = TRUE;
3923 }
3924 } else {
3925 retval = FALSE;
3926 }
3927 return retval;
3928 }
3929
3930
3931 static void
eel_canvas_item_accessible_get_extents(AtkComponent * component,gint * x,gint * y,gint * width,gint * height,AtkCoordType coord_type)3932 eel_canvas_item_accessible_get_extents (AtkComponent *component,
3933 gint *x,
3934 gint *y,
3935 gint *width,
3936 gint *height,
3937 AtkCoordType coord_type)
3938 {
3939 GObject *obj;
3940 EelCanvasItem *item;
3941 gint window_x, window_y;
3942 gint toplevel_x, toplevel_y;
3943 GdkRectangle rect;
3944 GdkWindow *window;
3945 GtkWidget *canvas;
3946
3947 obj = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (component));
3948
3949 if (obj == NULL) {
3950 /* item is defunct */
3951 return;
3952 }
3953
3954 /* Get the CanvasItem */
3955 item = EEL_CANVAS_ITEM (obj);
3956
3957 /* If this item has no parent canvas, something's broken */
3958 g_return_if_fail (GTK_IS_WIDGET (item->canvas));
3959
3960 eel_canvas_item_accessible_get_item_extents (item, &rect);
3961 *width = rect.width;
3962 *height = rect.height;
3963 if (!eel_canvas_item_accessible_is_item_in_window (item, &rect)) {
3964 *x = G_MININT;
3965 *y = G_MININT;
3966 return;
3967 }
3968
3969 canvas = GTK_WIDGET (item->canvas);
3970 window = gtk_widget_get_parent_window (canvas);
3971 gdk_window_get_origin (window, &window_x, &window_y);
3972 *x = rect.x + window_x;
3973 *y = rect.y + window_y;
3974 if (coord_type == ATK_XY_WINDOW) {
3975 window = gdk_window_get_toplevel (gtk_widget_get_window (canvas));
3976 gdk_window_get_origin (window, &toplevel_x, &toplevel_y);
3977 *x -= toplevel_x;
3978 *y -= toplevel_y;
3979 }
3980 return;
3981 }
3982
3983 static gint
eel_canvas_item_accessible_get_mdi_zorder(AtkComponent * component)3984 eel_canvas_item_accessible_get_mdi_zorder (AtkComponent *component)
3985 {
3986 GObject *g_obj;
3987 EelCanvasItem *item;
3988
3989 g_obj = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (component));
3990 if (g_obj == NULL) {
3991 /* Object is defunct */
3992 return -1;
3993 }
3994
3995 item = EEL_CANVAS_ITEM (g_obj);
3996 if (item->parent) {
3997 return g_list_index (EEL_CANVAS_GROUP (item->parent)->item_list, item);
3998 } else {
3999 g_return_val_if_fail (item->canvas->root == item, -1);
4000 return 0;
4001 }
4002 }
4003
4004 static gboolean
eel_canvas_item_accessible_grab_focus(AtkComponent * component)4005 eel_canvas_item_accessible_grab_focus (AtkComponent *component)
4006 {
4007 GObject *obj;
4008 EelCanvasItem *item;
4009 GtkWidget *toplevel;
4010
4011 obj = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (component));
4012
4013 item = EEL_CANVAS_ITEM (obj);
4014 if (item == NULL) {
4015 /* item is defunct */
4016 return FALSE;
4017 }
4018
4019 eel_canvas_item_grab_focus (item);
4020 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (item->canvas));
4021 if (gtk_widget_is_toplevel (toplevel)) {
4022 gtk_window_present (GTK_WINDOW (toplevel));
4023 }
4024
4025 return TRUE;
4026 }
4027
4028 static void
eel_canvas_item_accessible_remove_focus_handler(AtkComponent * component,guint handler_id)4029 eel_canvas_item_accessible_remove_focus_handler (AtkComponent *component,
4030 guint handler_id)
4031 {
4032 g_signal_handler_disconnect (component, handler_id);
4033 }
4034
4035 static void
eel_canvas_item_accessible_component_interface_init(AtkComponentIface * iface)4036 eel_canvas_item_accessible_component_interface_init (AtkComponentIface *iface)
4037 {
4038 g_return_if_fail (iface != NULL);
4039
4040 iface->add_focus_handler = eel_canvas_item_accessible_add_focus_handler;
4041 iface->get_extents = eel_canvas_item_accessible_get_extents;
4042 iface->get_mdi_zorder = eel_canvas_item_accessible_get_mdi_zorder;
4043 iface->grab_focus = eel_canvas_item_accessible_grab_focus;
4044 iface->remove_focus_handler = eel_canvas_item_accessible_remove_focus_handler;
4045 }
4046
4047 static gboolean
eel_canvas_item_accessible_is_item_on_screen(EelCanvasItem * item)4048 eel_canvas_item_accessible_is_item_on_screen (EelCanvasItem *item)
4049 {
4050 GdkRectangle rect;
4051
4052 eel_canvas_item_accessible_get_item_extents (item, &rect);
4053 return eel_canvas_item_accessible_is_item_in_window (item, &rect);
4054 }
4055
4056 static void
eel_canvas_item_accessible_initialize(AtkObject * obj,gpointer data)4057 eel_canvas_item_accessible_initialize (AtkObject *obj, gpointer data)
4058 {
4059 if (ATK_OBJECT_CLASS (accessible_item_parent_class)->initialize != NULL)
4060 ATK_OBJECT_CLASS (accessible_item_parent_class)->initialize (obj, data);
4061 g_object_set_data (G_OBJECT (obj), "atk-component-layer",
4062 GINT_TO_POINTER (ATK_LAYER_MDI));
4063 }
4064
4065 static AtkStateSet*
eel_canvas_item_accessible_ref_state_set(AtkObject * accessible)4066 eel_canvas_item_accessible_ref_state_set (AtkObject *accessible)
4067 {
4068 GObject *obj;
4069 EelCanvasItem *item;
4070 AtkStateSet *state_set;
4071
4072 state_set = ATK_OBJECT_CLASS (accessible_item_parent_class)->ref_state_set (accessible);
4073 obj = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (accessible));
4074
4075 item = EEL_CANVAS_ITEM (obj);
4076 if (item == NULL) {
4077 atk_state_set_add_state (state_set, ATK_STATE_DEFUNCT);
4078 } else {
4079 if (item->flags & EEL_CANVAS_ITEM_VISIBLE) {
4080 atk_state_set_add_state (state_set, ATK_STATE_VISIBLE);
4081
4082 if (eel_canvas_item_accessible_is_item_on_screen (item)) {
4083 atk_state_set_add_state (state_set, ATK_STATE_SHOWING);
4084 }
4085 }
4086 if (gtk_widget_get_can_focus (GTK_WIDGET (item->canvas))) {
4087 atk_state_set_add_state (state_set, ATK_STATE_FOCUSABLE);
4088
4089 if (item->canvas->focused_item == item) {
4090 atk_state_set_add_state (state_set, ATK_STATE_FOCUSED);
4091 }
4092 }
4093 }
4094
4095 return state_set;
4096 }
4097
4098 static void
eel_canvas_item_accessible_class_init(AtkObjectClass * klass)4099 eel_canvas_item_accessible_class_init (AtkObjectClass *klass)
4100 {
4101 accessible_item_parent_class = g_type_class_peek_parent (klass);
4102
4103 klass->initialize = eel_canvas_item_accessible_initialize;
4104 klass->ref_state_set = eel_canvas_item_accessible_ref_state_set;
4105 }
4106
4107 static GType
eel_canvas_item_accessible_get_type(void)4108 eel_canvas_item_accessible_get_type (void)
4109 {
4110 static GType type = 0;
4111
4112 if (!type) {
4113 static const GInterfaceInfo atk_component_info = {
4114 (GInterfaceInitFunc) eel_canvas_item_accessible_component_interface_init,
4115 (GInterfaceFinalizeFunc) NULL,
4116 NULL
4117 };
4118 AtkObjectFactory *factory;
4119 GType parent_atk_type;
4120 GTypeQuery query;
4121 GTypeInfo tinfo = { 0 };
4122
4123 factory = atk_registry_get_factory (atk_get_default_registry(),
4124 G_TYPE_INITIALLY_UNOWNED);
4125 if (!factory) {
4126 return G_TYPE_INVALID;
4127 }
4128 parent_atk_type = atk_object_factory_get_accessible_type (factory);
4129 if (!parent_atk_type) {
4130 return G_TYPE_INVALID;
4131 }
4132 g_type_query (parent_atk_type, &query);
4133 tinfo.class_init = (GClassInitFunc) eel_canvas_item_accessible_class_init;
4134 tinfo.class_size = query.class_size;
4135 tinfo.instance_size = query.instance_size;
4136 type = g_type_register_static (parent_atk_type,
4137 "EelCanvasItemAccessibility",
4138 &tinfo, 0);
4139
4140 g_type_add_interface_static (type, ATK_TYPE_COMPONENT,
4141 &atk_component_info);
4142
4143 }
4144 return type;
4145 }
4146
4147 static AtkObject *
eel_canvas_item_accessible_create(GObject * for_object)4148 eel_canvas_item_accessible_create (GObject *for_object)
4149 {
4150 GType type;
4151 AtkObject *accessible;
4152 EelCanvasItem *item;
4153
4154 item = EEL_CANVAS_ITEM (for_object);
4155 g_return_val_if_fail (item != NULL, NULL);
4156
4157 type = eel_canvas_item_accessible_get_type ();
4158 if (type == G_TYPE_INVALID) {
4159 return atk_no_op_object_new (for_object);
4160 }
4161
4162 accessible = g_object_new (type, NULL);
4163 atk_object_initialize (accessible, for_object);
4164 return accessible;
4165 }
4166
4167 static GType
eel_canvas_item_accessible_factory_get_accessible_type(void)4168 eel_canvas_item_accessible_factory_get_accessible_type (void)
4169 {
4170 return eel_canvas_item_accessible_get_type ();
4171 }
4172
4173 static AtkObject*
eel_canvas_item_accessible_factory_create_accessible(GObject * obj)4174 eel_canvas_item_accessible_factory_create_accessible (GObject *obj)
4175 {
4176 g_return_val_if_fail (G_IS_OBJECT (obj), NULL);
4177
4178 return eel_canvas_item_accessible_create (obj);
4179 }
4180
4181 static void
eel_canvas_item_accessible_factory_class_init(AtkObjectFactoryClass * klass)4182 eel_canvas_item_accessible_factory_class_init (AtkObjectFactoryClass *klass)
4183 {
4184 klass->create_accessible = eel_canvas_item_accessible_factory_create_accessible;
4185 klass->get_accessible_type = eel_canvas_item_accessible_factory_get_accessible_type;
4186 }
4187
4188 static GType
eel_canvas_item_accessible_factory_get_type(void)4189 eel_canvas_item_accessible_factory_get_type (void)
4190 {
4191 static GType type = 0;
4192
4193 if (!type) {
4194 static const GTypeInfo tinfo = {
4195 sizeof (AtkObjectFactoryClass),
4196 (GBaseInitFunc) NULL,
4197 (GBaseFinalizeFunc) NULL,
4198 (GClassInitFunc) eel_canvas_item_accessible_factory_class_init,
4199 NULL, /* class_finalize */
4200 NULL, /* class_data */
4201 sizeof (AtkObjectFactory),
4202 0, /* n_preallocs */
4203 NULL
4204 };
4205 type = g_type_register_static (ATK_TYPE_OBJECT_FACTORY,
4206 "EelCanvasItemAccessibilityFactory",
4207 &tinfo, 0);
4208 }
4209 return type;
4210 }
4211
4212 /* Class initialization function for EelCanvasItemClass */
4213 static void
eel_canvas_item_class_init(EelCanvasItemClass * klass)4214 eel_canvas_item_class_init (EelCanvasItemClass *klass)
4215 {
4216 GObjectClass *gobject_class = (GObjectClass *) klass;
4217
4218 item_parent_class = g_type_class_peek_parent (klass);
4219
4220 gobject_class->set_property = eel_canvas_item_set_property;
4221 gobject_class->get_property = eel_canvas_item_get_property;
4222 gobject_class->dispose = eel_canvas_item_dispose;
4223
4224 g_object_class_install_property
4225 (gobject_class, ITEM_PROP_PARENT,
4226 g_param_spec_object ("parent", NULL, NULL,
4227 EEL_TYPE_CANVAS_ITEM,
4228 G_PARAM_READWRITE));
4229
4230 g_object_class_install_property
4231 (gobject_class, ITEM_PROP_VISIBLE,
4232 g_param_spec_boolean ("visible", NULL, NULL,
4233 TRUE,
4234 G_PARAM_READWRITE));
4235
4236 item_signals[ITEM_EVENT] =
4237 g_signal_new ("event",
4238 G_TYPE_FROM_CLASS (klass),
4239 G_SIGNAL_RUN_LAST,
4240 G_STRUCT_OFFSET (EelCanvasItemClass, event),
4241 boolean_handled_accumulator, NULL,
4242 g_cclosure_marshal_generic,
4243 G_TYPE_BOOLEAN, 1,
4244 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
4245
4246 item_signals[ITEM_DESTROY] =
4247 g_signal_new ("destroy",
4248 G_TYPE_FROM_CLASS (klass),
4249 G_SIGNAL_RUN_CLEANUP | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
4250 G_STRUCT_OFFSET (EelCanvasItemClass, destroy),
4251 NULL, NULL,
4252 g_cclosure_marshal_VOID__VOID,
4253 G_TYPE_NONE, 0);
4254
4255 klass->realize = eel_canvas_item_realize;
4256 klass->unrealize = eel_canvas_item_unrealize;
4257 klass->map = eel_canvas_item_map;
4258 klass->unmap = eel_canvas_item_unmap;
4259 klass->update = eel_canvas_item_update;
4260
4261 atk_registry_set_factory_type (atk_get_default_registry (),
4262 EEL_TYPE_CANVAS_ITEM,
4263 eel_canvas_item_accessible_factory_get_type ());
4264 }
4265