nautilus-3.6.3/eel/eel-canvas.c

Location Tool Test ID Function Issue
eel-canvas.c:558:20 clang-analyzer Access to field 'next' results in a dereference of a null pointer (loaded from field 'prev')
eel-canvas.c:580:28 clang-analyzer Access to field 'prev' results in a dereference of a null pointer (loaded from field 'item_list')
eel-canvas.c:1637:11 clang-analyzer The right operand of '<' is a garbage value
eel-canvas.c:1637:11 clang-analyzer The left operand of '<' is a garbage value
eel-canvas.c:1653:8 clang-analyzer The left expression of the compound assignment is an uninitialized value. The computed value will also be garbage
eel-canvas.c:1659:6 clang-analyzer Assigned value is garbage or undefined
eel-canvas.c:3411:2 clang-analyzer Function call argument is an uninitialized value
eel-canvas.c:3647:9 clang-analyzer Function call argument is an uninitialized value
eel-canvas.c:3649:3 clang-analyzer Undefined or garbage value returned to caller
eel-canvas.c:3737:2 clang-analyzer Function call argument is an uninitialized value
eel-canvas.c:3908:15 clang-analyzer The right operand of '-' is a garbage value
   1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: 8; c-basic-offset: 8 -*- */
   2 /*
   3  * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
   4  * All rights reserved.
   5  *
   6  * This file is part of the Gnome Library.
   7  *
   8  * The Gnome Library is free software; you can redistribute it and/or
   9  * modify it under the terms of the GNU Library General Public License as
  10  * published by the Free Software Foundation; either version 2 of the
  11  * License, or (at your option) any later version.
  12  *
  13  * The Gnome Library is distributed in the hope that it will be useful,
  14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16  * Library General Public License for more details.
  17  *
  18  * You should have received a copy of the GNU Library General Public
  19  * License along with the Gnome Library; see the file COPYING.LIB.  If not,
  20  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  21  * Boston, MA 02111-1307, USA.
  22  */
  23 /*
  24   @NOTATION@
  25  */
  26 /*
  27  * EelCanvas widget - Tk-like canvas widget for Gnome
  28  *
  29  * EelCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
  30  * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
  31  *
  32  *
  33  * Authors: Federico Mena <federico@nuclecu.unam.mx>
  34  *          Raph Levien <raph@gimp.org>
  35  */
  36 
  37 /*
  38  * TO-DO list for the canvas:
  39  *
  40  * - Allow to specify whether EelCanvasImage sizes are in units or pixels (scale or don't scale).
  41  *
  42  * - Implement a flag for eel_canvas_item_reparent() that tells the function to keep the item
  43  *   visually in the same place, that is, to keep it in the same place with respect to the canvas
  44  *   origin.
  45  *
  46  * - GC put functions for items.
  47  *
  48  * - Widget item (finish it).
  49  *
  50  * - GList *eel_canvas_gimme_all_items_contained_in_this_area (EelCanvas *canvas, Rectangle area);
  51  *
  52  * - Retrofit all the primitive items with microtile support.
  53  *
  54  * - Curve support for line item.
  55  *
  56  * - Arc item (Havoc has it; to be integrated in EelCanvasEllipse).
  57  *
  58  * - Sane font handling API.
  59  *
  60  * - Get_arg methods for items:
  61  *   - How to fetch the outline width and know whether it is in pixels or units?
  62  */
  63 
  64 #include <config.h>
  65 
  66 #include <math.h>
  67 #include <string.h>
  68 #include <stdio.h>
  69 #include <gdk/gdkprivate.h>
  70 #include <gtk/gtk.h>
  71 #include <glib/gi18n-lib.h>
  72 #include <cairo-gobject.h>
  73 #include "eel-canvas.h"
  74 
  75 static void eel_canvas_request_update (EelCanvas      *canvas);
  76 static void group_add                   (EelCanvasGroup *group,
  77 					 EelCanvasItem  *item);
  78 static void group_remove                (EelCanvasGroup *group,
  79 					 EelCanvasItem  *item);
  80 static void redraw_and_repick_if_mapped (EelCanvasItem *item);
  81 
  82 /*** EelCanvasItem ***/
  83 
  84 /* Some convenience stuff */
  85 #define GCI_UPDATE_MASK (EEL_CANVAS_UPDATE_REQUESTED | EEL_CANVAS_UPDATE_DEEP)
  86 #define GCI_EPSILON 1e-18
  87 
  88 enum {
  89 	ITEM_PROP_0,
  90 	ITEM_PROP_PARENT,
  91 	ITEM_PROP_VISIBLE
  92 };
  93 
  94 enum {
  95 	ITEM_DESTROY,
  96 	ITEM_EVENT,
  97 	ITEM_LAST_SIGNAL
  98 };
  99 
 100 static void eel_canvas_item_class_init     (EelCanvasItemClass *klass);
 101 static void eel_canvas_item_init           (EelCanvasItem      *item);
 102 static int  emit_event                       (EelCanvas *canvas, GdkEvent *event);
 103 
 104 static guint item_signals[ITEM_LAST_SIGNAL];
 105 
 106 static GObjectClass *item_parent_class;
 107 
 108 static gpointer accessible_item_parent_class;
 109 static gpointer accessible_parent_class;
 110 
 111 
 112 /**
 113  * eel_canvas_item_get_type:
 114  *
 115  * Registers the &EelCanvasItem class if necessary, and returns the type ID
 116  * associated to it.
 117  *
 118  * Return value:  The type ID of the &EelCanvasItem class.
 119  **/
 120 GType
 121 eel_canvas_item_get_type (void)
 122 {
 123 	static GType canvas_item_type = 0;
 124 
 125 	if (!canvas_item_type) {
 126 		static const GTypeInfo canvas_item_info = {
 127 			sizeof (EelCanvasItemClass),
 128 			(GBaseInitFunc) NULL,
 129 			(GBaseFinalizeFunc) NULL,
 130 			(GClassInitFunc) eel_canvas_item_class_init,
 131 			NULL,           /* class_finalize */
 132 			NULL,           /* class_data */
 133 			sizeof (EelCanvasItem),
 134 			0,              /* n_preallocs */
 135 			(GInstanceInitFunc) eel_canvas_item_init
 136 		};
 137 
 138 		canvas_item_type = g_type_register_static (G_TYPE_INITIALLY_UNOWNED,
 139 							   "EelCanvasItem",
 140 							   &canvas_item_info,
 141 							   0);
 142 	}
 143 
 144 	return canvas_item_type;
 145 }
 146 
 147 /* Object initialization function for EelCanvasItem */
 148 static void
 149 eel_canvas_item_init (EelCanvasItem *item)
 150 {
 151 	item->flags |= EEL_CANVAS_ITEM_VISIBLE;
 152 }
 153 
 154 /**
 155  * eel_canvas_item_new:
 156  * @parent: The parent group for the new item.
 157  * @type: The object type of the item.
 158  * @first_arg_name: A list of object argument name/value pairs, NULL-terminated,
 159  * used to configure the item.  For example, "fill_color", "black",
 160  * "width_units", 5.0, NULL.
 161  * @Varargs:
 162  *
 163  * Creates a new canvas item with @parent as its parent group.  The item is
 164  * created at the top of its parent's stack, and starts up as visible.  The item
 165  * is of the specified @type, for example, it can be
 166  * eel_canvas_rect_get_type().  The list of object arguments/value pairs is
 167  * used to configure the item.
 168  *
 169  * Return value: The newly-created item.
 170  **/
 171 EelCanvasItem *
 172 eel_canvas_item_new (EelCanvasGroup *parent, GType type, const gchar *first_arg_name, ...)
 173 {
 174 	EelCanvasItem *item;
 175 	va_list args;
 176 
 177 	g_return_val_if_fail (EEL_IS_CANVAS_GROUP (parent), NULL);
 178 	g_return_val_if_fail (g_type_is_a (type, eel_canvas_item_get_type ()), NULL);
 179 
 180 	item = EEL_CANVAS_ITEM (g_object_new (type, NULL));
 181 
 182 	va_start (args, first_arg_name);
 183 	eel_canvas_item_construct (item, parent, first_arg_name, args);
 184 	va_end (args);
 185 
 186 	return item;
 187 }
 188 
 189 
 190 /* Performs post-creation operations on a canvas item (adding it to its parent
 191  * group, etc.)
 192  */
 193 static void
 194 item_post_create_setup (EelCanvasItem *item)
 195 {
 196 	group_add (EEL_CANVAS_GROUP (item->parent), item);
 197 
 198 	redraw_and_repick_if_mapped (item);
 199 }
 200 
 201 /* Set_property handler for canvas items */
 202 static void
 203 eel_canvas_item_set_property (GObject *gobject, guint param_id,
 204 			      const GValue *value, GParamSpec *pspec)
 205 {
 206 	EelCanvasItem *item;
 207 
 208 	g_return_if_fail (EEL_IS_CANVAS_ITEM (gobject));
 209 
 210 	item = EEL_CANVAS_ITEM (gobject);
 211 
 212 	switch (param_id) {
 213 	case ITEM_PROP_PARENT:
 214 		if (item->parent != NULL) {
 215 		    g_warning ("Cannot set `parent' argument after item has "
 216 			       "already been constructed.");
 217 		} else if (g_value_get_object (value)) {
 218 			item->parent = EEL_CANVAS_ITEM (g_value_get_object (value));
 219 			item->canvas = item->parent->canvas;
 220 			item_post_create_setup (item);
 221 		}
 222 		break;
 223 	case ITEM_PROP_VISIBLE:
 224 		if (g_value_get_boolean (value)) {
 225 			eel_canvas_item_show (item);
 226 		} else {
 227 			eel_canvas_item_hide (item);
 228 		}
 229 		break;
 230 	default:
 231 		G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
 232 		break;
 233 	}
 234 }
 235 
 236 /* Get_property handler for canvas items */
 237 static void
 238 eel_canvas_item_get_property (GObject *gobject, guint param_id,
 239 			      GValue *value, GParamSpec *pspec)
 240 {
 241 	EelCanvasItem *item;
 242 
 243 	g_return_if_fail (EEL_IS_CANVAS_ITEM (gobject));
 244 
 245 	item = EEL_CANVAS_ITEM (gobject);
 246 
 247 	switch (param_id) {
 248 	case ITEM_PROP_VISIBLE:
 249 		g_value_set_boolean (value, item->flags & EEL_CANVAS_ITEM_VISIBLE);
 250 		break;
 251 	default:
 252 		G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
 253 		break;
 254 	}
 255 }
 256 
 257 /**
 258  * eel_canvas_item_construct:
 259  * @item: An unconstructed canvas item.
 260  * @parent: The parent group for the item.
 261  * @first_arg_name: The name of the first argument for configuring the item.
 262  * @args: The list of arguments used to configure the item.
 263  *
 264  * Constructs a canvas item; meant for use only by item implementations.
 265  **/
 266 void
 267 eel_canvas_item_construct (EelCanvasItem *item, EelCanvasGroup *parent,
 268 			   const gchar *first_arg_name, va_list args)
 269 {
 270 	g_return_if_fail (EEL_IS_CANVAS_GROUP (parent));
 271 	g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
 272 
 273 	item->parent = EEL_CANVAS_ITEM (parent);
 274 	item->canvas = item->parent->canvas;
 275 
 276 	g_object_set_valist (G_OBJECT (item), first_arg_name, args);
 277 
 278 	item_post_create_setup (item);
 279 }
 280 
 281 
 282 static void
 283 redraw_and_repick_if_mapped (EelCanvasItem *item)
 284 {
 285 	if (item->flags & EEL_CANVAS_ITEM_MAPPED) {
 286 		eel_canvas_item_request_redraw (item);
 287 		item->canvas->need_repick = TRUE;
 288 	}
 289 }
 290 
 291 /* Dispose handler for canvas items */
 292 static void
 293 eel_canvas_item_dispose (GObject *object)
 294 {
 295 	EelCanvasItem *item;
 296 
 297 	g_return_if_fail (EEL_IS_CANVAS_ITEM (object));
 298 
 299 	item = EEL_CANVAS_ITEM (object);
 300 
 301 	if (item->canvas) {
 302 		eel_canvas_item_request_redraw (item);
 303 
 304 		/* Make the canvas forget about us */
 305 
 306 		if (item == item->canvas->current_item) {
 307 			item->canvas->current_item = NULL;
 308 			item->canvas->need_repick = TRUE;
 309 		}
 310 
 311 		if (item == item->canvas->new_current_item) {
 312 			item->canvas->new_current_item = NULL;
 313 			item->canvas->need_repick = TRUE;
 314 		}
 315 
 316 		eel_canvas_item_ungrab (item, GDK_CURRENT_TIME);
 317 
 318 		if (item == item->canvas->focused_item)
 319 			item->canvas->focused_item = NULL;
 320 
 321 		/* Normal destroy stuff */
 322 
 323 		if (item->flags & EEL_CANVAS_ITEM_MAPPED)
 324 			(* EEL_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
 325 
 326 		if (item->flags & EEL_CANVAS_ITEM_REALIZED)
 327 			(* EEL_CANVAS_ITEM_GET_CLASS (item)->unrealize) (item);
 328 
 329 		if (item->parent)
 330 			group_remove (EEL_CANVAS_GROUP (item->parent), item);
 331 
 332 		item->canvas = NULL;
 333 	}
 334 
 335 	g_object_set_data (object, "in-destruction", GINT_TO_POINTER (1));
 336 	g_signal_emit (object, item_signals[ITEM_DESTROY], 0);
 337 
 338 	g_object_set_data (object, "in-destruction", NULL);
 339 
 340 	G_OBJECT_CLASS (item_parent_class)->dispose (object);
 341 }
 342 
 343 void
 344 eel_canvas_item_destroy (EelCanvasItem *item)
 345 {
 346 	if (g_object_get_data (G_OBJECT (item), "in-destruction") == NULL) {
 347 		g_object_run_dispose (G_OBJECT (item));
 348 	}
 349 }
 350 
 351 /* Realize handler for canvas items */
 352 static void
 353 eel_canvas_item_realize (EelCanvasItem *item)
 354 {
 355 	if (item->parent && !(item->parent->flags & EEL_CANVAS_ITEM_REALIZED))
 356 		(* EEL_CANVAS_ITEM_GET_CLASS (item->parent)->realize) (item->parent);
 357 	
 358 	if (item->parent == NULL && !gtk_widget_get_realized (GTK_WIDGET (item->canvas)))
 359 		gtk_widget_realize (GTK_WIDGET (item->canvas));
 360 
 361 	item->flags |= EEL_CANVAS_ITEM_REALIZED;
 362 
 363 	eel_canvas_item_request_update (item);
 364 }
 365 
 366 /* Unrealize handler for canvas items */
 367 static void
 368 eel_canvas_item_unrealize (EelCanvasItem *item)
 369 {
 370 	if (item->flags & EEL_CANVAS_ITEM_MAPPED)
 371 		(* EEL_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
 372 
 373 	item->flags &= ~(EEL_CANVAS_ITEM_REALIZED);
 374 }
 375 
 376 /* Map handler for canvas items */
 377 static void
 378 eel_canvas_item_map (EelCanvasItem *item)
 379 {
 380 	item->flags |= EEL_CANVAS_ITEM_MAPPED;
 381 }
 382 
 383 /* Unmap handler for canvas items */
 384 static void
 385 eel_canvas_item_unmap (EelCanvasItem *item)
 386 {
 387 	item->flags &= ~(EEL_CANVAS_ITEM_MAPPED);
 388 }
 389 
 390 /* Update handler for canvas items */
 391 static void
 392 eel_canvas_item_update (EelCanvasItem *item, double i2w_dx, double i2w_dy, int flags)
 393 {
 394 	item->flags &= ~(EEL_CANVAS_ITEM_NEED_UPDATE);
 395 	item->flags &= ~(EEL_CANVAS_ITEM_NEED_DEEP_UPDATE);
 396 }
 397 
 398 /*
 399  * This routine invokes the update method of the item
 400  * Please notice, that we take parent to canvas pixel matrix as argument
 401  * unlike virtual method ::update, whose argument is item 2 canvas pixel
 402  * matrix
 403  *
 404  * I will try to force somewhat meaningful naming for affines (Lauris)
 405  * General naming rule is FROM2TO, where FROM and TO are abbreviations
 406  * So p2cpx is Parent2CanvasPixel and i2cpx is Item2CanvasPixel
 407  * I hope that this helps to keep track of what really happens
 408  *
 409  */
 410 
 411 static void
 412 eel_canvas_item_invoke_update (EelCanvasItem *item,
 413 			       double i2w_dx,
 414 			       double i2w_dy,
 415 			       int flags)
 416 {
 417 	int child_flags;
 418 
 419 	child_flags = flags;
 420 
 421 	/* apply object flags to child flags */
 422 	child_flags &= ~EEL_CANVAS_UPDATE_REQUESTED;
 423 
 424 	if (item->flags & EEL_CANVAS_ITEM_NEED_UPDATE)
 425 		child_flags |= EEL_CANVAS_UPDATE_REQUESTED;
 426 
 427 	if (item->flags & EEL_CANVAS_ITEM_NEED_DEEP_UPDATE)
 428 		child_flags |= EEL_CANVAS_UPDATE_DEEP;
 429 
 430 	if (child_flags & GCI_UPDATE_MASK) {
 431 		if (EEL_CANVAS_ITEM_GET_CLASS (item)->update)
 432 			EEL_CANVAS_ITEM_GET_CLASS (item)->update (item, i2w_dx, i2w_dy, child_flags);
 433 	}
 434  
 435 	/* If this fail you probably forgot to chain up to
 436 	 * EelCanvasItem::update from a derived class */
 437  	g_return_if_fail (!(item->flags & EEL_CANVAS_ITEM_NEED_UPDATE));
 438 }
 439 
 440 /*
 441  * This routine invokes the point method of the item.
 442  * The arguments x, y should be in the parent item local coordinates.
 443  */
 444 
 445 static double
 446 eel_canvas_item_invoke_point (EelCanvasItem *item, double x, double y, int cx, int cy, EelCanvasItem **actual_item)
 447 {
 448 	/* Calculate x & y in item local coordinates */
 449 
 450 	if (EEL_CANVAS_ITEM_GET_CLASS (item)->point)
 451 		return EEL_CANVAS_ITEM_GET_CLASS (item)->point (item, x, y, cx, cy, actual_item);
 452 
 453 	return 1e18;
 454 }
 455 
 456 /**
 457  * eel_canvas_item_set:
 458  * @item: A canvas item.
 459  * @first_arg_name: The list of object argument name/value pairs used to configure the item.
 460  * @Varargs:
 461  *
 462  * Configures a canvas item.  The arguments in the item are set to the specified
 463  * values, and the item is repainted as appropriate.
 464  **/
 465 void
 466 eel_canvas_item_set (EelCanvasItem *item, const gchar *first_arg_name, ...)
 467 {
 468 	va_list args;
 469 
 470 	va_start (args, first_arg_name);
 471 	eel_canvas_item_set_valist (item, first_arg_name, args);
 472 	va_end (args);
 473 }
 474 
 475 
 476 /**
 477  * eel_canvas_item_set_valist:
 478  * @item: A canvas item.
 479  * @first_arg_name: The name of the first argument used to configure the item.
 480  * @args: The list of object argument name/value pairs used to configure the item.
 481  *
 482  * Configures a canvas item.  The arguments in the item are set to the specified
 483  * values, and the item is repainted as appropriate.
 484  **/
 485 void
 486 eel_canvas_item_set_valist (EelCanvasItem *item, const gchar *first_arg_name, va_list args)
 487 {
 488 	g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
 489 
 490 	g_object_set_valist (G_OBJECT (item), first_arg_name, args);
 491 
 492 	item->canvas->need_repick = TRUE;
 493 }
 494 
 495 
 496 /**
 497  * eel_canvas_item_move:
 498  * @item: A canvas item.
 499  * @dx: Horizontal offset.
 500  * @dy: Vertical offset.
 501  *
 502  * Moves a canvas item by creating an affine transformation matrix for
 503  * translation by using the specified values. This happens in item
 504  * local coordinate system, so if you have nontrivial transform, it
 505  * most probably does not do, what you want.
 506  **/
 507 void
 508 eel_canvas_item_move (EelCanvasItem *item, double dx, double dy)
 509 {
 510         g_return_if_fail (item != NULL);
 511         g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
 512 
 513         if (!EEL_CANVAS_ITEM_GET_CLASS (item)->translate) {
 514                 g_warning ("Item type %s does not implement translate method.\n",
 515                            g_type_name (G_OBJECT_TYPE (item)));
 516                 return;
 517         }
 518 
 519         (* EEL_CANVAS_ITEM_GET_CLASS (item)->translate) (item, dx, dy);
 520 
 521 	if (item->flags & EEL_CANVAS_ITEM_MAPPED) 
 522 		item->canvas->need_repick = TRUE;
 523 
 524 	if (!(item->flags & EEL_CANVAS_ITEM_NEED_DEEP_UPDATE)) {
 525 		item->flags |= EEL_CANVAS_ITEM_NEED_DEEP_UPDATE;
 526 		if (item->parent != NULL)
 527 			eel_canvas_item_request_update (item->parent);
 528 		else
 529 			eel_canvas_request_update (item->canvas);
 530 	}
 531 
 532 }
 533 
 534 static void
 535 eel_canvas_queue_resize (EelCanvas *canvas)
 536 {
 537 	if (gtk_widget_is_drawable (GTK_WIDGET (canvas)))
 538 		gtk_widget_queue_resize (GTK_WIDGET (canvas));
 539 }
 540 
 541 /* Convenience function to reorder items in a group's child list.  This puts the
 542  * specified link after the "before" link. Returns TRUE if the list was changed.
 543  */
 544 static gboolean
 545 put_item_after (GList *link, GList *before)
 546 {
 547 	EelCanvasGroup *parent;
 548 
 549 	if (link == before)
 550 		return FALSE;
 551 
 552 	parent = EEL_CANVAS_GROUP (EEL_CANVAS_ITEM (link->data)->parent);
 553 
 554 	if (before == NULL) {
 555 		if (link == parent->item_list)
 556 			return FALSE;
 557 
 558 		link->prev->next = link->next;
Access to field 'next' results in a dereference of a null pointer (loaded from field 'prev')
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

559 560 if (link->next) 561 link->next->prev = link->prev; 562 else 563 parent->item_list_end = link->prev; 564 565 link->prev = before; 566 link->next = parent->item_list; 567 link->next->prev = link; 568 parent->item_list = link; 569 } else { 570 if ((link == parent->item_list_end) && (before == parent->item_list_end->prev)) 571 return FALSE; 572 573 if (link->next) 574 link->next->prev = link->prev; 575 576 if (link->prev) 577 link->prev->next = link->next; 578 else { 579 parent->item_list = link->next; 580 parent->item_list->prev = NULL;
Access to field 'prev' results in a dereference of a null pointer (loaded from field 'item_list')
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

581 } 582 583 link->prev = before; 584 link->next = before->next; 585 586 link->prev->next = link; 587 588 if (link->next) 589 link->next->prev = link; 590 else 591 parent->item_list_end = link; 592 } 593 return TRUE; 594 } 595 596 597 /** 598 * eel_canvas_item_raise: 599 * @item: A canvas item. 600 * @positions: Number of steps to raise the item. 601 * 602 * Raises the item in its parent's stack by the specified number of positions. 603 * If the number of positions is greater than the distance to the top of the 604 * stack, then the item is put at the top. 605 **/ 606 void 607 eel_canvas_item_raise (EelCanvasItem *item, int positions) 608 { 609 GList *link, *before; 610 EelCanvasGroup *parent; 611 612 g_return_if_fail (EEL_IS_CANVAS_ITEM (item)); 613 g_return_if_fail (positions >= 0); 614 615 if (!item->parent || positions == 0) 616 return; 617 618 parent = EEL_CANVAS_GROUP (item->parent); 619 link = g_list_find (parent->item_list, item); 620 g_assert (link != NULL); 621 622 for (before = link; positions && before; positions--) 623 before = before->next; 624 625 if (!before) 626 before = parent->item_list_end; 627 628 if (put_item_after (link, before)) { 629 redraw_and_repick_if_mapped (item); 630 } 631 } 632 633 634 /** 635 * eel_canvas_item_lower: 636 * @item: A canvas item. 637 * @positions: Number of steps to lower the item. 638 * 639 * Lowers the item in its parent's stack by the specified number of positions. 640 * If the number of positions is greater than the distance to the bottom of the 641 * stack, then the item is put at the bottom. 642 **/ 643 void 644 eel_canvas_item_lower (EelCanvasItem *item, int positions) 645 { 646 GList *link, *before; 647 EelCanvasGroup *parent; 648 649 g_return_if_fail (EEL_IS_CANVAS_ITEM (item)); 650 g_return_if_fail (positions >= 1); 651 652 if (!item->parent || positions == 0) 653 return; 654 655 parent = EEL_CANVAS_GROUP (item->parent); 656 link = g_list_find (parent->item_list, item); 657 g_assert (link != NULL); 658 659 if (link->prev) 660 for (before = link->prev; positions && before; positions--) 661 before = before->prev; 662 else 663 before = NULL; 664 665 if (put_item_after (link, before)) { 666 redraw_and_repick_if_mapped (item); 667 } 668 } 669 670 671 /** 672 * eel_canvas_item_raise_to_top: 673 * @item: A canvas item. 674 * 675 * Raises an item to the top of its parent's stack. 676 **/ 677 void 678 eel_canvas_item_raise_to_top (EelCanvasItem *item) 679 { 680 GList *link; 681 EelCanvasGroup *parent; 682 683 g_return_if_fail (EEL_IS_CANVAS_ITEM (item)); 684 685 if (!item->parent) 686 return; 687 688 parent = EEL_CANVAS_GROUP (item->parent); 689 link = g_list_find (parent->item_list, item); 690 g_assert (link != NULL); 691 692 if (put_item_after (link, parent->item_list_end)) { 693 redraw_and_repick_if_mapped (item); 694 } 695 } 696 697 698 /** 699 * eel_canvas_item_lower_to_bottom: 700 * @item: A canvas item. 701 * 702 * Lowers an item to the bottom of its parent's stack. 703 **/ 704 void 705 eel_canvas_item_lower_to_bottom (EelCanvasItem *item) 706 { 707 GList *link; 708 EelCanvasGroup *parent; 709 710 g_return_if_fail (EEL_IS_CANVAS_ITEM (item)); 711 712 if (!item->parent) 713 return; 714 715 parent = EEL_CANVAS_GROUP (item->parent); 716 link = g_list_find (parent->item_list, item); 717 g_assert (link != NULL); 718 719 if (put_item_after (link, NULL)) { 720 redraw_and_repick_if_mapped (item); 721 } 722 } 723 724 /** 725 * eel_canvas_item_send_behind: 726 * @item: A canvas item. 727 * @behind_item: The canvas item to put item behind, or NULL 728 * 729 * Moves item to a in the position in the stacking order so that 730 * it is placed immediately below behind_item, or at the top if 731 * behind_item is NULL. 732 **/ 733 void 734 eel_canvas_item_send_behind (EelCanvasItem *item, 735 EelCanvasItem *behind_item) 736 { 737 GList *item_list; 738 int item_position, behind_position; 739 740 g_return_if_fail (EEL_IS_CANVAS_ITEM (item)); 741 742 if (behind_item == NULL) { 743 eel_canvas_item_raise_to_top (item); 744 return; 745 } 746 747 g_return_if_fail (EEL_IS_CANVAS_ITEM (behind_item)); 748 g_return_if_fail (item->parent == behind_item->parent); 749 750 item_list = EEL_CANVAS_GROUP (item->parent)->item_list; 751 752 item_position = g_list_index (item_list, item); 753 g_assert (item_position != -1); 754 behind_position = g_list_index (item_list, behind_item); 755 g_assert (behind_position != -1); 756 g_assert (item_position != behind_position); 757 758 if (item_position == behind_position - 1) { 759 return; 760 } 761 762 if (item_position < behind_position) { 763 eel_canvas_item_raise (item, (behind_position - 1) - item_position); 764 } else { 765 eel_canvas_item_lower (item, item_position - behind_position); 766 } 767 } 768 769 /** 770 * eel_canvas_item_show: 771 * @item: A canvas item. 772 * 773 * Shows a canvas item. If the item was already shown, then no action is taken. 774 **/ 775 void 776 eel_canvas_item_show (EelCanvasItem *item) 777 { 778 g_return_if_fail (EEL_IS_CANVAS_ITEM (item)); 779 780 if (!(item->flags & EEL_CANVAS_ITEM_VISIBLE)) { 781 item->flags |= EEL_CANVAS_ITEM_VISIBLE; 782 783 if (!(item->flags & EEL_CANVAS_ITEM_REALIZED)) 784 (* EEL_CANVAS_ITEM_GET_CLASS (item)->realize) (item); 785 786 if (item->parent != NULL) { 787 if (!(item->flags & EEL_CANVAS_ITEM_MAPPED) && 788 item->parent->flags & EEL_CANVAS_ITEM_MAPPED) 789 (* EEL_CANVAS_ITEM_GET_CLASS (item)->map) (item); 790 } else { 791 if (!(item->flags & EEL_CANVAS_ITEM_MAPPED) && 792 gtk_widget_get_mapped (GTK_WIDGET (item->canvas))) 793 (* EEL_CANVAS_ITEM_GET_CLASS (item)->map) (item); 794 } 795 796 redraw_and_repick_if_mapped (item); 797 eel_canvas_queue_resize (item->canvas); 798 } 799 } 800 801 802 /** 803 * eel_canvas_item_hide: 804 * @item: A canvas item. 805 * 806 * Hides a canvas item. If the item was already hidden, then no action is 807 * taken. 808 **/ 809 void 810 eel_canvas_item_hide (EelCanvasItem *item) 811 { 812 g_return_if_fail (EEL_IS_CANVAS_ITEM (item)); 813 814 if (item->flags & EEL_CANVAS_ITEM_VISIBLE) { 815 item->flags &= ~EEL_CANVAS_ITEM_VISIBLE; 816 817 redraw_and_repick_if_mapped (item); 818 819 if (item->flags & EEL_CANVAS_ITEM_MAPPED) 820 (* EEL_CANVAS_ITEM_GET_CLASS (item)->unmap) (item); 821 822 eel_canvas_queue_resize (item->canvas); 823 824 /* No need to unrealize when we just want to hide */ 825 } 826 } 827 828 829 /** 830 * eel_canvas_item_grab: 831 * @item: A canvas item. 832 * @event_mask: Mask of events that will be sent to this item. 833 * @cursor: If non-NULL, the cursor that will be used while the grab is active. 834 * @etime: The timestamp required for grabbing the mouse, or GDK_CURRENT_TIME. 835 * 836 * Specifies that all events that match the specified event mask should be sent 837 * to the specified item, and also grabs the mouse by calling 838 * gdk_pointer_grab(). The event mask is also used when grabbing the pointer. 839 * If @cursor is not NULL, then that cursor is used while the grab is active. 840 * The @etime parameter is the timestamp required for grabbing the mouse. 841 * 842 * Return value: If an item was already grabbed, it returns %GDK_GRAB_ALREADY_GRABBED. If 843 * the specified item was hidden by calling eel_canvas_item_hide(), then it 844 * returns %GDK_GRAB_NOT_VIEWABLE. Else, it returns the result of calling 845 * gdk_pointer_grab(). 846 **/ 847 GdkGrabStatus 848 eel_canvas_item_grab (EelCanvasItem *item, 849 GdkEventMask event_mask, 850 GdkCursor *cursor, 851 guint32 timestamp) 852 { 853 GdkGrabStatus retval; 854 GdkDisplay *display; 855 GdkDeviceManager *manager; 856 GdkDevice *device; 857 858 g_return_val_if_fail (EEL_IS_CANVAS_ITEM (item), GDK_GRAB_NOT_VIEWABLE); 859 g_return_val_if_fail (gtk_widget_get_mapped (GTK_WIDGET (item->canvas)), 860 GDK_GRAB_NOT_VIEWABLE); 861 862 if (item->canvas->grabbed_item) 863 return GDK_GRAB_ALREADY_GRABBED; 864 865 if (!(item->flags & EEL_CANVAS_ITEM_MAPPED)) 866 return GDK_GRAB_NOT_VIEWABLE; 867 868 display = gtk_widget_get_display (GTK_WIDGET (item->canvas)); 869 manager = gdk_display_get_device_manager (display); 870 device = gdk_device_manager_get_client_pointer (manager); 871 872 retval = gdk_device_grab (device, 873 gtk_layout_get_bin_window (GTK_LAYOUT (item->canvas)), 874 GDK_OWNERSHIP_NONE, 875 FALSE, 876 event_mask, 877 cursor, 878 timestamp); 879 880 if (retval != GDK_GRAB_SUCCESS) 881 return retval; 882 883 item->canvas->grabbed_item = item; 884 item->canvas->grabbed_event_mask = event_mask; 885 item->canvas->current_item = item; /* So that events go to the grabbed item */ 886 887 return retval; 888 } 889 890 891 /** 892 * eel_canvas_item_ungrab: 893 * @item: A canvas item that holds a grab. 894 * @etime: The timestamp for ungrabbing the mouse. 895 * 896 * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the 897 * mouse. 898 **/ 899 void 900 eel_canvas_item_ungrab (EelCanvasItem *item, guint32 etime) 901 { 902 GdkDisplay *display; 903 GdkDeviceManager *manager; 904 GdkDevice *device; 905 906 g_return_if_fail (EEL_IS_CANVAS_ITEM (item)); 907 908 if (item->canvas->grabbed_item != item) 909 return; 910 911 display = gtk_widget_get_display (GTK_WIDGET (item->canvas)); 912 manager = gdk_display_get_device_manager (display); 913 device = gdk_device_manager_get_client_pointer (manager); 914 915 item->canvas->grabbed_item = NULL; 916 gdk_device_ungrab (device, etime); 917 } 918 919 /** 920 * eel_canvas_item_w2i: 921 * @item: A canvas item. 922 * @x: X coordinate to convert (input/output value). 923 * @y: Y coordinate to convert (input/output value). 924 * 925 * Converts a coordinate pair from world coordinates to item-relative 926 * coordinates. 927 **/ 928 void 929 eel_canvas_item_w2i (EelCanvasItem *item, double *x, double *y) 930 { 931 g_return_if_fail (EEL_IS_CANVAS_ITEM (item)); 932 g_return_if_fail (x != NULL); 933 g_return_if_fail (y != NULL); 934 935 item = item->parent; 936 while (item) { 937 if (EEL_IS_CANVAS_GROUP (item)) { 938 *x -= EEL_CANVAS_GROUP (item)->xpos; 939 *y -= EEL_CANVAS_GROUP (item)->ypos; 940 } 941 942 item = item->parent; 943 } 944 } 945 946 947 /** 948 * eel_canvas_item_i2w: 949 * @item: A canvas item. 950 * @x: X coordinate to convert (input/output value). 951 * @y: Y coordinate to convert (input/output value). 952 * 953 * Converts a coordinate pair from item-relative coordinates to world 954 * coordinates. 955 **/ 956 void 957 eel_canvas_item_i2w (EelCanvasItem *item, double *x, double *y) 958 { 959 g_return_if_fail (EEL_IS_CANVAS_ITEM (item)); 960 g_return_if_fail (x != NULL); 961 g_return_if_fail (y != NULL); 962 963 item = item->parent; 964 while (item) { 965 if (EEL_IS_CANVAS_GROUP (item)) { 966 *x += EEL_CANVAS_GROUP (item)->xpos; 967 *y += EEL_CANVAS_GROUP (item)->ypos; 968 } 969 970 item = item->parent; 971 } 972 } 973 974 /* Returns whether the item is an inferior of or is equal to the parent. */ 975 static int 976 is_descendant (EelCanvasItem *item, EelCanvasItem *parent) 977 { 978 for (; item; item = item->parent) 979 if (item == parent) 980 return TRUE; 981 982 return FALSE; 983 } 984 985 /** 986 * eel_canvas_item_reparent: 987 * @item: A canvas item. 988 * @new_group: A canvas group. 989 * 990 * Changes the parent of the specified item to be the new group. The item keeps 991 * its group-relative coordinates as for its old parent, so the item may change 992 * its absolute position within the canvas. 993 **/ 994 void 995 eel_canvas_item_reparent (EelCanvasItem *item, EelCanvasGroup *new_group) 996 { 997 g_return_if_fail (EEL_IS_CANVAS_ITEM (item)); 998 g_return_if_fail (EEL_IS_CANVAS_GROUP (new_group)); 999 1000 /* Both items need to be in the same canvas */ 1001 g_return_if_fail (item->canvas == EEL_CANVAS_ITEM (new_group)->canvas); 1002 1003 /* The group cannot be an inferior of the item or be the item itself -- 1004 * this also takes care of the case where the item is the root item of 1005 * the canvas. */ 1006 g_return_if_fail (!is_descendant (EEL_CANVAS_ITEM (new_group), item)); 1007 1008 /* Everything is ok, now actually reparent the item */ 1009 1010 g_object_ref (G_OBJECT (item)); /* protect it from the unref in group_remove */ 1011 1012 eel_canvas_item_request_redraw (item); 1013 1014 group_remove (EEL_CANVAS_GROUP (item->parent), item); 1015 item->parent = EEL_CANVAS_ITEM (new_group); 1016 /* item->canvas is unchanged. */ 1017 group_add (new_group, item); 1018 1019 /* Redraw and repick */ 1020 1021 redraw_and_repick_if_mapped (item); 1022 1023 g_object_unref (G_OBJECT (item)); 1024 } 1025 1026 /** 1027 * eel_canvas_item_grab_focus: 1028 * @item: A canvas item. 1029 * 1030 * Makes the specified item take the keyboard focus, so all keyboard events will 1031 * be sent to it. If the canvas widget itself did not have the focus, it grabs 1032 * it as well. 1033 **/ 1034 void 1035 eel_canvas_item_grab_focus (EelCanvasItem *item) 1036 { 1037 EelCanvasItem *focused_item; 1038 GdkEvent ev; 1039 1040 g_return_if_fail (EEL_IS_CANVAS_ITEM (item)); 1041 g_return_if_fail (gtk_widget_get_can_focus (GTK_WIDGET (item->canvas))); 1042 1043 focused_item = item->canvas->focused_item; 1044 1045 if (focused_item) { 1046 ev.focus_change.type = GDK_FOCUS_CHANGE; 1047 ev.focus_change.window = gtk_layout_get_bin_window (GTK_LAYOUT (item->canvas)); 1048 ev.focus_change.send_event = FALSE; 1049 ev.focus_change.in = FALSE; 1050 1051 emit_event (item->canvas, &ev); 1052 } 1053 1054 item->canvas->focused_item = item; 1055 gtk_widget_grab_focus (GTK_WIDGET (item->canvas)); 1056 1057 if (focused_item) { 1058 ev.focus_change.type = GDK_FOCUS_CHANGE; 1059 ev.focus_change.window = gtk_layout_get_bin_window (GTK_LAYOUT (item->canvas)); 1060 ev.focus_change.send_event = FALSE; 1061 ev.focus_change.in = TRUE; 1062 1063 emit_event (item->canvas, &ev); 1064 } 1065 } 1066 1067 1068 /** 1069 * eel_canvas_item_get_bounds: 1070 * @item: A canvas item. 1071 * @x1: Leftmost edge of the bounding box (return value). 1072 * @y1: Upper edge of the bounding box (return value). 1073 * @x2: Rightmost edge of the bounding box (return value). 1074 * @y2: Lower edge of the bounding box (return value). 1075 * 1076 * Queries the bounding box of a canvas item. The bounds are returned in the 1077 * coordinate system of the item's parent. 1078 **/ 1079 void 1080 eel_canvas_item_get_bounds (EelCanvasItem *item, double *x1, double *y1, double *x2, double *y2) 1081 { 1082 double tx1, ty1, tx2, ty2; 1083 1084 g_return_if_fail (EEL_IS_CANVAS_ITEM (item)); 1085 1086 tx1 = ty1 = tx2 = ty2 = 0.0; 1087 1088 /* Get the item's bounds in its coordinate system */ 1089 1090 if (EEL_CANVAS_ITEM_GET_CLASS (item)->bounds) 1091 (* EEL_CANVAS_ITEM_GET_CLASS (item)->bounds) (item, &tx1, &ty1, &tx2, &ty2); 1092 1093 /* Return the values */ 1094 1095 if (x1) 1096 *x1 = tx1; 1097 1098 if (y1) 1099 *y1 = ty1; 1100 1101 if (x2) 1102 *x2 = tx2; 1103 1104 if (y2) 1105 *y2 = ty2; 1106 } 1107 1108 1109 /** 1110 * eel_canvas_item_request_update 1111 * @item: A canvas item. 1112 * 1113 * To be used only by item implementations. Requests that the canvas queue an 1114 * update for the specified item. 1115 **/ 1116 void 1117 eel_canvas_item_request_update (EelCanvasItem *item) 1118 { 1119 if (NULL == item->canvas) 1120 return; 1121 1122 g_return_if_fail (!item->canvas->doing_update); 1123 1124 if (item->flags & EEL_CANVAS_ITEM_NEED_UPDATE) 1125 return; 1126 1127 item->flags |= EEL_CANVAS_ITEM_NEED_UPDATE; 1128 1129 if (item->parent != NULL) { 1130 /* Recurse up the tree */ 1131 eel_canvas_item_request_update (item->parent); 1132 } else { 1133 /* Have reached the top of the tree, make sure the update call gets scheduled. */ 1134 eel_canvas_request_update (item->canvas); 1135 } 1136 } 1137 1138 /** 1139 * eel_canvas_item_request_update 1140 * @item: A canvas item. 1141 * 1142 * Convenience function that informs a canvas that the specified item needs 1143 * to be repainted. To be used by item implementations 1144 **/ 1145 void 1146 eel_canvas_item_request_redraw (EelCanvasItem *item) 1147 { 1148 if (item->flags & EEL_CANVAS_ITEM_MAPPED) 1149 eel_canvas_request_redraw (item->canvas, 1150 item->x1, item->y1, 1151 item->x2 + 1, item->y2 + 1); 1152 } 1153 1154 1155 1156 /*** EelCanvasGroup ***/ 1157 1158 1159 enum { 1160 GROUP_PROP_0, 1161 GROUP_PROP_X, 1162 GROUP_PROP_Y 1163 }; 1164 1165 1166 static void eel_canvas_group_class_init (EelCanvasGroupClass *klass); 1167 static void eel_canvas_group_init (EelCanvasGroup *group); 1168 static void eel_canvas_group_set_property(GObject *object, 1169 guint param_id, 1170 const GValue *value, 1171 GParamSpec *pspec); 1172 static void eel_canvas_group_get_property(GObject *object, 1173 guint param_id, 1174 GValue *value, 1175 GParamSpec *pspec); 1176 1177 static void eel_canvas_group_destroy (EelCanvasItem *object); 1178 1179 static void eel_canvas_group_update (EelCanvasItem *item, 1180 double i2w_dx, 1181 double i2w_dy, 1182 int flags); 1183 static void eel_canvas_group_unrealize (EelCanvasItem *item); 1184 static void eel_canvas_group_map (EelCanvasItem *item); 1185 static void eel_canvas_group_unmap (EelCanvasItem *item); 1186 static void eel_canvas_group_draw (EelCanvasItem *item, 1187 cairo_t *cr, 1188 cairo_region_t *region); 1189 static double eel_canvas_group_point (EelCanvasItem *item, double x, double y, 1190 int cx, int cy, 1191 EelCanvasItem **actual_item); 1192 static void eel_canvas_group_translate (EelCanvasItem *item, double dx, double dy); 1193 static void eel_canvas_group_bounds (EelCanvasItem *item, double *x1, double *y1, 1194 double *x2, double *y2); 1195 1196 1197 static EelCanvasItemClass *group_parent_class; 1198 1199 1200 /** 1201 * eel_canvas_group_get_type: 1202 * 1203 * Registers the &EelCanvasGroup class if necessary, and returns the type ID 1204 * associated to it. 1205 * 1206 * Return value: The type ID of the &EelCanvasGroup class. 1207 **/ 1208 GType 1209 eel_canvas_group_get_type (void) 1210 { 1211 static GType group_type = 0; 1212 1213 if (!group_type) { 1214 static const GTypeInfo group_info = { 1215 sizeof (EelCanvasGroupClass), 1216 (GBaseInitFunc) NULL, 1217 (GBaseFinalizeFunc) NULL, 1218 (GClassInitFunc) eel_canvas_group_class_init, 1219 NULL, /* class_finalize */ 1220 NULL, /* class_data */ 1221 sizeof (EelCanvasGroup), 1222 0, /* n_preallocs */ 1223 (GInstanceInitFunc) eel_canvas_group_init 1224 1225 1226 }; 1227 1228 group_type = g_type_register_static (eel_canvas_item_get_type (), 1229 "EelCanvasGroup", 1230 &group_info, 1231 0); 1232 } 1233 1234 return group_type; 1235 } 1236 1237 /* Class initialization function for EelCanvasGroupClass */ 1238 static void 1239 eel_canvas_group_class_init (EelCanvasGroupClass *klass) 1240 { 1241 GObjectClass *gobject_class; 1242 EelCanvasItemClass *item_class; 1243 1244 gobject_class = (GObjectClass *) klass; 1245 item_class = (EelCanvasItemClass *) klass; 1246 1247 group_parent_class = g_type_class_peek_parent (klass); 1248 1249 gobject_class->set_property = eel_canvas_group_set_property; 1250 gobject_class->get_property = eel_canvas_group_get_property; 1251 1252 g_object_class_install_property 1253 (gobject_class, GROUP_PROP_X, 1254 g_param_spec_double ("x", 1255 _("X"), 1256 _("X"), 1257 -G_MAXDOUBLE, G_MAXDOUBLE, 0.0, 1258 G_PARAM_READWRITE)); 1259 g_object_class_install_property 1260 (gobject_class, GROUP_PROP_Y, 1261 g_param_spec_double ("y", 1262 _("Y"), 1263 _("Y"), 1264 -G_MAXDOUBLE, G_MAXDOUBLE, 0.0, 1265 G_PARAM_READWRITE)); 1266 1267 item_class->destroy = eel_canvas_group_destroy; 1268 item_class->update = eel_canvas_group_update; 1269 item_class->unrealize = eel_canvas_group_unrealize; 1270 item_class->map = eel_canvas_group_map; 1271 item_class->unmap = eel_canvas_group_unmap; 1272 item_class->draw = eel_canvas_group_draw; 1273 item_class->point = eel_canvas_group_point; 1274 item_class->translate = eel_canvas_group_translate; 1275 item_class->bounds = eel_canvas_group_bounds; 1276 } 1277 1278 /* Object initialization function for EelCanvasGroup */ 1279 static void 1280 eel_canvas_group_init (EelCanvasGroup *group) 1281 { 1282 group->xpos = 0.0; 1283 group->ypos = 0.0; 1284 } 1285 1286 /* Set_property handler for canvas groups */ 1287 static void 1288 eel_canvas_group_set_property (GObject *gobject, guint param_id, 1289 const GValue *value, GParamSpec *pspec) 1290 { 1291 EelCanvasItem *item; 1292 EelCanvasGroup *group; 1293 double old; 1294 gboolean moved; 1295 1296 g_return_if_fail (EEL_IS_CANVAS_GROUP (gobject)); 1297 1298 item = EEL_CANVAS_ITEM (gobject); 1299 group = EEL_CANVAS_GROUP (gobject); 1300 1301 moved = FALSE; 1302 switch (param_id) { 1303 case GROUP_PROP_X: 1304 old = group->xpos; 1305 group->xpos = g_value_get_double (value); 1306 if (old != group->xpos) 1307 moved = TRUE; 1308 break; 1309 1310 case GROUP_PROP_Y: 1311 old = group->ypos; 1312 group->ypos = g_value_get_double (value); 1313 if (old != group->ypos) 1314 moved = TRUE; 1315 break; 1316 1317 default: 1318 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec); 1319 break; 1320 } 1321 1322 if (moved) { 1323 item->flags |= EEL_CANVAS_ITEM_NEED_DEEP_UPDATE; 1324 if (item->parent != NULL) 1325 eel_canvas_item_request_update (item->parent); 1326 else 1327 eel_canvas_request_update (item->canvas); 1328 } 1329 } 1330 1331 /* Get_property handler for canvas groups */ 1332 static void 1333 eel_canvas_group_get_property (GObject *gobject, guint param_id, 1334 GValue *value, GParamSpec *pspec) 1335 { 1336 EelCanvasGroup *group; 1337 1338 g_return_if_fail (EEL_IS_CANVAS_GROUP (gobject)); 1339 1340 group = EEL_CANVAS_GROUP (gobject); 1341 1342 switch (param_id) { 1343 case GROUP_PROP_X: 1344 g_value_set_double (value, group->xpos); 1345 break; 1346 1347 case GROUP_PROP_Y: 1348 g_value_set_double (value, group->ypos); 1349 break; 1350 1351 default: 1352 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec); 1353 break; 1354 } 1355 } 1356 1357 /* Destroy handler for canvas groups */ 1358 static void 1359 eel_canvas_group_destroy (EelCanvasItem *object) 1360 { 1361 EelCanvasGroup *group; 1362 EelCanvasItem *child; 1363 GList *list; 1364 1365 g_return_if_fail (EEL_IS_CANVAS_GROUP (object)); 1366 1367 group = EEL_CANVAS_GROUP (object); 1368 1369 list = group->item_list; 1370 while (list) { 1371 child = list->data; 1372 list = list->next; 1373 1374 eel_canvas_item_destroy (child); 1375 } 1376 1377 if (EEL_CANVAS_ITEM_CLASS (group_parent_class)->destroy) 1378 (* EEL_CANVAS_ITEM_CLASS (group_parent_class)->destroy) (object); 1379 } 1380 1381 /* Update handler for canvas groups */ 1382 static void 1383 eel_canvas_group_update (EelCanvasItem *item, double i2w_dx, double i2w_dy, int flags) 1384 { 1385 EelCanvasGroup *group; 1386 GList *list; 1387 EelCanvasItem *i; 1388 double bbox_x0, bbox_y0, bbox_x1, bbox_y1; 1389 gboolean first = TRUE; 1390 1391 group = EEL_CANVAS_GROUP (item); 1392 1393 (* group_parent_class->update) (item, i2w_dx, i2w_dy, flags); 1394 1395 bbox_x0 = 0; 1396 bbox_y0 = 0; 1397 bbox_x1 = 0; 1398 bbox_y1 = 0; 1399 1400 for (list = group->item_list; list; list = list->next) { 1401 i = list->data; 1402 1403 eel_canvas_item_invoke_update (i, i2w_dx + group->xpos, i2w_dy + group->ypos, flags); 1404 1405 if (first) { 1406 first = FALSE; 1407 bbox_x0 = i->x1; 1408 bbox_y0 = i->y1; 1409 bbox_x1 = i->x2; 1410 bbox_y1 = i->y2; 1411 } else { 1412 bbox_x0 = MIN (bbox_x0, i->x1); 1413 bbox_y0 = MIN (bbox_y0, i->y1); 1414 bbox_x1 = MAX (bbox_x1, i->x2); 1415 bbox_y1 = MAX (bbox_y1, i->y2); 1416 } 1417 } 1418 item->x1 = bbox_x0; 1419 item->y1 = bbox_y0; 1420 item->x2 = bbox_x1; 1421 item->y2 = bbox_y1; 1422 } 1423 1424 /* Unrealize handler for canvas groups */ 1425 static void 1426 eel_canvas_group_unrealize (EelCanvasItem *item) 1427 { 1428 EelCanvasGroup *group; 1429 GList *list; 1430 EelCanvasItem *i; 1431 1432 group = EEL_CANVAS_GROUP (item); 1433 1434 /* Unmap group before children to avoid flash */ 1435 if (item->flags & EEL_CANVAS_ITEM_MAPPED) 1436 (* EEL_CANVAS_ITEM_GET_CLASS (item)->unmap) (item); 1437 1438 for (list = group->item_list; list; list = list->next) { 1439 i = list->data; 1440 1441 if (i->flags & EEL_CANVAS_ITEM_REALIZED) 1442 (* EEL_CANVAS_ITEM_GET_CLASS (i)->unrealize) (i); 1443 } 1444 1445 (* group_parent_class->unrealize) (item); 1446 } 1447 1448 /* Map handler for canvas groups */ 1449 static void 1450 eel_canvas_group_map (EelCanvasItem *item) 1451 { 1452 EelCanvasGroup *group; 1453 GList *list; 1454 EelCanvasItem *i; 1455 1456 group = EEL_CANVAS_GROUP (item); 1457 1458 for (list = group->item_list; list; list = list->next) { 1459 i = list->data; 1460 1461 if (i->flags & EEL_CANVAS_ITEM_VISIBLE && 1462 !(i->flags & EEL_CANVAS_ITEM_MAPPED)) { 1463 if (!(i->flags & EEL_CANVAS_ITEM_REALIZED)) 1464 (* EEL_CANVAS_ITEM_GET_CLASS (i)->realize) (i); 1465 1466 (* EEL_CANVAS_ITEM_GET_CLASS (i)->map) (i); 1467 } 1468 } 1469 1470 (* group_parent_class->map) (item); 1471 } 1472 1473 /* Unmap handler for canvas groups */ 1474 static void 1475 eel_canvas_group_unmap (EelCanvasItem *item) 1476 { 1477 EelCanvasGroup *group; 1478 GList *list; 1479 EelCanvasItem *i; 1480 1481 group = EEL_CANVAS_GROUP (item); 1482 1483 for (list = group->item_list; list; list = list->next) { 1484 i = list->data; 1485 1486 if (i->flags & EEL_CANVAS_ITEM_MAPPED) 1487 (* EEL_CANVAS_ITEM_GET_CLASS (i)->unmap) (i); 1488 } 1489 1490 (* group_parent_class->unmap) (item); 1491 } 1492 1493 /* Draw handler for canvas groups */ 1494 static void 1495 eel_canvas_group_draw (EelCanvasItem *item, 1496 cairo_t *cr, 1497 cairo_region_t *region) 1498 { 1499 EelCanvasGroup *group; 1500 GList *list; 1501 EelCanvasItem *child = NULL; 1502 1503 group = EEL_CANVAS_GROUP (item); 1504 1505 for (list = group->item_list; list; list = list->next) { 1506 child = list->data; 1507 1508 if ((child->flags & EEL_CANVAS_ITEM_MAPPED) && 1509 (EEL_CANVAS_ITEM_GET_CLASS (child)->draw)) { 1510 GdkRectangle child_rect; 1511 1512 child_rect.x = child->x1; 1513 child_rect.y = child->y1; 1514 child_rect.width = child->x2 - child->x1 + 1; 1515 child_rect.height = child->y2 - child->y1 + 1; 1516 1517 if (cairo_region_contains_rectangle (region, &child_rect) != CAIRO_REGION_OVERLAP_OUT) 1518 EEL_CANVAS_ITEM_GET_CLASS (child)->draw (child, cr, region); 1519 } 1520 } 1521 } 1522 1523 /* Point handler for canvas groups */ 1524 static double 1525 eel_canvas_group_point (EelCanvasItem *item, double x, double y, int cx, int cy, 1526 EelCanvasItem **actual_item) 1527 { 1528 EelCanvasGroup *group; 1529 GList *list; 1530 EelCanvasItem *child, *point_item; 1531 int x1, y1, x2, y2; 1532 double gx, gy; 1533 double dist, best; 1534 int has_point; 1535 1536 group = EEL_CANVAS_GROUP (item); 1537 1538 x1 = cx - item->canvas->close_enough; 1539 y1 = cy - item->canvas->close_enough; 1540 x2 = cx + item->canvas->close_enough; 1541 y2 = cy + item->canvas->close_enough; 1542 1543 best = 0.0; 1544 *actual_item = NULL; 1545 1546 gx = x - group->xpos; 1547 gy = y - group->ypos; 1548 1549 dist = 0.0; /* keep gcc happy */ 1550 1551 for (list = group->item_list; list; list = list->next) { 1552 child = list->data; 1553 1554 if ((child->x1 > x2) || (child->y1 > y2) || (child->x2 < x1) || (child->y2 < y1)) 1555 continue; 1556 1557 point_item = NULL; /* cater for incomplete item implementations */ 1558 1559 if ((child->flags & EEL_CANVAS_ITEM_MAPPED) 1560 && EEL_CANVAS_ITEM_GET_CLASS (child)->point) { 1561 dist = eel_canvas_item_invoke_point (child, gx, gy, cx, cy, &point_item); 1562 has_point = TRUE; 1563 } else 1564 has_point = FALSE; 1565 1566 if (has_point 1567 && point_item 1568 && ((int) (dist * item->canvas->pixels_per_unit + 0.5) 1569 <= item->canvas->close_enough)) { 1570 best = dist; 1571 *actual_item = point_item; 1572 } 1573 } 1574 1575 return best; 1576 } 1577 1578 void 1579 eel_canvas_group_translate (EelCanvasItem *item, double dx, double dy) 1580 { 1581 EelCanvasGroup *group; 1582 1583 group = EEL_CANVAS_GROUP (item); 1584 1585 group->xpos += dx; 1586 group->ypos += dy; 1587 } 1588 1589 /* Bounds handler for canvas groups */ 1590 static void 1591 eel_canvas_group_bounds (EelCanvasItem *item, double *x1, double *y1, double *x2, double *y2) 1592 { 1593 EelCanvasGroup *group; 1594 EelCanvasItem *child; 1595 GList *list; 1596 double tx1, ty1, tx2, ty2; 1597 double minx, miny, maxx, maxy; 1598 int set; 1599 1600 group = EEL_CANVAS_GROUP (item); 1601 1602 /* Get the bounds of the first visible item */ 1603 1604 child = NULL; /* Unnecessary but eliminates a warning. */ 1605 1606 set = FALSE; 1607 1608 for (list = group->item_list; list; list = list->next) { 1609 child = list->data; 1610 1611 if (child->flags & EEL_CANVAS_ITEM_MAPPED) { 1612 set = TRUE; 1613 eel_canvas_item_get_bounds (child, &minx, &miny, &maxx, &maxy); 1614 break; 1615 } 1616 } 1617 1618 /* If there were no visible items, return an empty bounding box */ 1619 1620 if (!set) { 1621 *x1 = *y1 = *x2 = *y2 = 0.0; 1622 return; 1623 } 1624 1625 /* Now we can grow the bounds using the rest of the items */ 1626 1627 list = list->next; 1628 1629 for (; list; list = list->next) { 1630 child = list->data; 1631 1632 if (!(child->flags & EEL_CANVAS_ITEM_MAPPED)) 1633 continue; 1634 1635 eel_canvas_item_get_bounds (child, &tx1, &ty1, &tx2, &ty2); 1636 1637 if (tx1 < minx)
The right operand of '<' is a garbage value
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

The left operand of '<' is a garbage value
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

1638 minx = tx1; 1639 1640 if (ty1 < miny) 1641 miny = ty1; 1642 1643 if (tx2 > maxx) 1644 maxx = tx2; 1645 1646 if (ty2 > maxy) 1647 maxy = ty2; 1648 } 1649 1650 /* Make the bounds be relative to our parent's coordinate system */ 1651 1652 if (item->parent) { 1653 minx += group->xpos;
The left expression of the compound assignment is an uninitialized value. The computed value will also be garbage
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

1654 miny += group->ypos; 1655 maxx += group->xpos; 1656 maxy += group->ypos; 1657 } 1658 1659 *x1 = minx;
Assigned value is garbage or undefined
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

1660 *y1 = miny; 1661 *x2 = maxx; 1662 *y2 = maxy; 1663 } 1664 1665 /* Adds an item to a group */ 1666 static void 1667 group_add (EelCanvasGroup *group, EelCanvasItem *item) 1668 { 1669 g_object_ref_sink (item); 1670 1671 if (!group->item_list) { 1672 group->item_list = g_list_append (group->item_list, item); 1673 group->item_list_end = group->item_list; 1674 } else 1675 group->item_list_end = g_list_append (group->item_list_end, item)->next; 1676 1677 if (item->flags & EEL_CANVAS_ITEM_VISIBLE && 1678 group->item.flags & EEL_CANVAS_ITEM_MAPPED) { 1679 if (!(item->flags & EEL_CANVAS_ITEM_REALIZED)) 1680 (* EEL_CANVAS_ITEM_GET_CLASS (item)->realize) (item); 1681 1682 if (!(item->flags & EEL_CANVAS_ITEM_MAPPED)) 1683 (* EEL_CANVAS_ITEM_GET_CLASS (item)->map) (item); 1684 } 1685 1686 if (item->flags & EEL_CANVAS_ITEM_VISIBLE) 1687 eel_canvas_queue_resize (EEL_CANVAS_ITEM (group)->canvas); 1688 } 1689 1690 /* Removes an item from a group */ 1691 static void 1692 group_remove (EelCanvasGroup *group, EelCanvasItem *item) 1693 { 1694 GList *children; 1695 1696 g_return_if_fail (EEL_IS_CANVAS_GROUP (group)); 1697 g_return_if_fail (EEL_IS_CANVAS_ITEM (item)); 1698 1699 for (children = group->item_list; children; children = children->next) 1700 if (children->data == item) { 1701 if (item->flags & EEL_CANVAS_ITEM_MAPPED) { 1702 (* EEL_CANVAS_ITEM_GET_CLASS (item)->unmap) (item); 1703 } 1704 1705 if (item->flags & EEL_CANVAS_ITEM_REALIZED) 1706 (* EEL_CANVAS_ITEM_GET_CLASS (item)->unrealize) (item); 1707 1708 if (item->flags & EEL_CANVAS_ITEM_VISIBLE) 1709 eel_canvas_queue_resize (item->canvas); 1710 1711 /* Unparent the child */ 1712 1713 item->parent = NULL; 1714 /* item->canvas = NULL; */ 1715 g_object_unref (G_OBJECT (item)); 1716 1717 /* Remove it from the list */ 1718 1719 if (children == group->item_list_end) 1720 group->item_list_end = children->prev; 1721 1722 group->item_list = g_list_remove_link (group->item_list, children); 1723 g_list_free (children); 1724 break; 1725 } 1726 } 1727 1728 1729 /*** EelCanvas ***/ 1730 1731 1732 enum { 1733 DRAW_BACKGROUND, 1734 LAST_SIGNAL 1735 }; 1736 1737 static void eel_canvas_class_init (EelCanvasClass *klass); 1738 static void eel_canvas_init (EelCanvas *canvas); 1739 static void eel_canvas_destroy (GtkWidget *object); 1740 static void eel_canvas_map (GtkWidget *widget); 1741 static void eel_canvas_unmap (GtkWidget *widget); 1742 static void eel_canvas_realize (GtkWidget *widget); 1743 static void eel_canvas_unrealize (GtkWidget *widget); 1744 static void eel_canvas_size_allocate (GtkWidget *widget, 1745 GtkAllocation *allocation); 1746 static gint eel_canvas_button (GtkWidget *widget, 1747 GdkEventButton *event); 1748 static gint eel_canvas_motion (GtkWidget *widget, 1749 GdkEventMotion *event); 1750 static gint eel_canvas_draw (GtkWidget *widget, 1751 cairo_t *cr); 1752 static gint eel_canvas_key (GtkWidget *widget, 1753 GdkEventKey *event); 1754 static gint eel_canvas_crossing (GtkWidget *widget, 1755 GdkEventCrossing *event); 1756 static gint eel_canvas_focus_in (GtkWidget *widget, 1757 GdkEventFocus *event); 1758 static gint eel_canvas_focus_out (GtkWidget *widget, 1759 GdkEventFocus *event); 1760 static void eel_canvas_request_update_real (EelCanvas *canvas); 1761 static void eel_canvas_draw_background (EelCanvas *canvas, 1762 cairo_t *cr); 1763 static AtkObject *eel_canvas_get_accessible (GtkWidget *widget); 1764 1765 1766 static GtkLayoutClass *canvas_parent_class; 1767 1768 static guint canvas_signals[LAST_SIGNAL]; 1769 1770 /** 1771 * eel_canvas_get_type: 1772 * 1773 * Registers the &EelCanvas class if necessary, and returns the type ID 1774 * associated to it. 1775 * 1776 * Return value: The type ID of the &EelCanvas class. 1777 **/ 1778 GType 1779 eel_canvas_get_type (void) 1780 { 1781 static GType canvas_type = 0; 1782 1783 if (!canvas_type) { 1784 static const GTypeInfo canvas_info = { 1785 sizeof (EelCanvasClass), 1786 (GBaseInitFunc) NULL, 1787 (GBaseFinalizeFunc) NULL, 1788 (GClassInitFunc) eel_canvas_class_init, 1789 NULL, /* class_finalize */ 1790 NULL, /* class_data */ 1791 sizeof (EelCanvas), 1792 0, /* n_preallocs */ 1793 (GInstanceInitFunc) eel_canvas_init 1794 }; 1795 1796 canvas_type = g_type_register_static (gtk_layout_get_type (), 1797 "EelCanvas", 1798 &canvas_info, 1799 0); 1800 } 1801 1802 return canvas_type; 1803 } 1804 1805 static void 1806 eel_canvas_get_property (GObject *object, 1807 guint prop_id, 1808 GValue *value, 1809 GParamSpec *pspec) 1810 { 1811 switch (prop_id) { 1812 default: 1813 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); 1814 break; 1815 } 1816 } 1817 1818 static void 1819 eel_canvas_set_property (GObject *object, 1820 guint prop_id, 1821 const GValue *value, 1822 GParamSpec *pspec) 1823 { 1824 switch (prop_id) { 1825 default: 1826 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); 1827 break; 1828 } 1829 } 1830 1831 static void 1832 eel_canvas_accessible_adjustment_changed (GtkAdjustment *adjustment, 1833 gpointer data) 1834 { 1835 AtkObject *atk_obj; 1836 1837 /* The scrollbars have changed */ 1838 atk_obj = ATK_OBJECT (data); 1839 1840 g_signal_emit_by_name (atk_obj, "visible_data_changed"); 1841 } 1842 1843 static void 1844 accessible_destroy_cb (GtkWidget *widget, 1845 GtkAccessible *accessible) 1846 { 1847 gtk_accessible_set_widget (accessible, NULL); 1848 atk_object_notify_state_change (ATK_OBJECT (accessible), ATK_STATE_DEFUNCT, TRUE); 1849 } 1850 1851 static gboolean 1852 accessible_focus_cb (GtkWidget *widget, 1853 GdkEventFocus *event) 1854 { 1855 AtkObject* accessible = gtk_widget_get_accessible (widget); 1856 atk_object_notify_state_change (accessible, ATK_STATE_FOCUSED, event->in); 1857 1858 return FALSE; 1859 } 1860 1861 static void 1862 accessible_notify_cb (GObject *obj, 1863 GParamSpec *pspec) 1864 { 1865 GtkWidget* widget = GTK_WIDGET (obj); 1866 AtkObject* atk_obj = gtk_widget_get_accessible (widget); 1867 AtkState state; 1868 gboolean value; 1869 1870 if (strcmp (pspec->name, "visible") == 0) { 1871 state = ATK_STATE_VISIBLE; 1872 value = gtk_widget_get_visible (widget); 1873 } else if (strcmp (pspec->name, "sensitive") == 0) { 1874 state = ATK_STATE_SENSITIVE; 1875 value = gtk_widget_get_sensitive (widget); 1876 1877 atk_object_notify_state_change (atk_obj, ATK_STATE_ENABLED, value); 1878 } else { 1879 g_assert_not_reached (); 1880 } 1881 1882 atk_object_notify_state_change (atk_obj, state, value); 1883 } 1884 1885 /* Translate GtkWidget::size-allocate to AtkComponent::bounds-changed */ 1886 static void 1887 accessible_size_allocate_cb (GtkWidget *widget, 1888 GtkAllocation *allocation) 1889 { 1890 AtkObject* accessible = gtk_widget_get_accessible (widget); 1891 AtkRectangle rect; 1892 1893 rect.x = allocation->x; 1894 rect.y = allocation->y; 1895 rect.width = allocation->width; 1896 rect.height = allocation->height; 1897 1898 g_signal_emit_by_name (accessible, "bounds_changed", &rect); 1899 } 1900 1901 /* Translate GtkWidget mapped state into AtkObject showing */ 1902 static void 1903 accessible_map_cb (GtkWidget *widget) 1904 { 1905 AtkObject *accessible = gtk_widget_get_accessible (widget); 1906 atk_object_notify_state_change (accessible, ATK_STATE_SHOWING, 1907 gtk_widget_get_mapped (widget)); 1908 } 1909 1910 static void 1911 eel_canvas_accessible_initialize (AtkObject *obj, 1912 gpointer data) 1913 { 1914 EelCanvas *canvas = data; 1915 1916 if (ATK_OBJECT_CLASS (accessible_parent_class)->initialize != NULL) { 1917 ATK_OBJECT_CLASS (accessible_parent_class)->initialize (obj, data); 1918 } 1919 1920 gtk_accessible_set_widget (GTK_ACCESSIBLE (obj), GTK_WIDGET (data)); 1921 g_signal_connect (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (canvas)), 1922 "value_changed", 1923 G_CALLBACK (eel_canvas_accessible_adjustment_changed), 1924 obj); 1925 g_signal_connect (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (canvas)), 1926 "value_changed", 1927 G_CALLBACK (eel_canvas_accessible_adjustment_changed), 1928 obj); 1929 1930 obj->role = ATK_ROLE_LAYERED_PANE; 1931 1932 /* below adapted from gtkwidgetaccessible.c */ 1933 1934 g_signal_connect_after (canvas, "destroy", 1935 G_CALLBACK (accessible_destroy_cb), obj); 1936 g_signal_connect_after (canvas, "focus-in-event", 1937 G_CALLBACK (accessible_focus_cb), NULL); 1938 g_signal_connect_after (canvas, "focus-out-event", 1939 G_CALLBACK (accessible_focus_cb), NULL); 1940 g_signal_connect (canvas, "notify::visible", 1941 G_CALLBACK (accessible_notify_cb), NULL); 1942 g_signal_connect (canvas, "notify::sensitive", 1943 G_CALLBACK (accessible_notify_cb), NULL); 1944 g_signal_connect (canvas, "size-allocate", 1945 G_CALLBACK (accessible_size_allocate_cb), NULL); 1946 g_signal_connect (canvas, "map", 1947 G_CALLBACK (accessible_map_cb), NULL); 1948 g_signal_connect (canvas, "unmap", 1949 G_CALLBACK (accessible_map_cb), NULL); 1950 } 1951 1952 static gint 1953 eel_canvas_accessible_get_n_children (AtkObject* obj) 1954 { 1955 GtkAccessible *accessible; 1956 GtkWidget *widget; 1957 EelCanvas *canvas; 1958 EelCanvasGroup *root_group; 1959 1960 accessible = GTK_ACCESSIBLE (obj); 1961 widget = gtk_accessible_get_widget (accessible); 1962 1963 if (widget == NULL) { 1964 return 0; 1965 } 1966 1967 g_return_val_if_fail (EEL_IS_CANVAS (widget), 0); 1968 1969 canvas = EEL_CANVAS (widget); 1970 root_group = eel_canvas_root (canvas); 1971 g_return_val_if_fail (root_group, 0); 1972 1973 return 1; 1974 } 1975 1976 static AtkObject* 1977 eel_canvas_accessible_ref_child (AtkObject *obj, 1978 gint i) 1979 { 1980 GtkAccessible *accessible; 1981 GtkWidget *widget; 1982 EelCanvas *canvas; 1983 EelCanvasGroup *root_group; 1984 AtkObject *atk_object; 1985 1986 /* Canvas only has one child, so return NULL if index is non zero */ 1987 if (i != 0) { 1988 return NULL; 1989 } 1990 1991 accessible = GTK_ACCESSIBLE (obj); 1992 widget = gtk_accessible_get_widget (accessible); 1993 1994 if (widget == NULL) { 1995 return NULL; 1996 } 1997 1998 canvas = EEL_CANVAS (widget); 1999 root_group = eel_canvas_root (canvas); 2000 g_return_val_if_fail (root_group, NULL); 2001 2002 atk_object = atk_gobject_accessible_for_object (G_OBJECT (root_group)); 2003 2004 return g_object_ref (atk_object); 2005 } 2006 2007 static gboolean 2008 eel_canvas_accessible_all_parents_visible (GtkWidget *widget) 2009 { 2010 GtkWidget *iter_parent = NULL; 2011 gboolean result = TRUE; 2012 2013 for (iter_parent = gtk_widget_get_parent (widget); iter_parent != NULL; 2014 iter_parent = gtk_widget_get_parent (iter_parent)) { 2015 if (!gtk_widget_get_visible (iter_parent)) { 2016 result = FALSE; 2017 break; 2018 } 2019 } 2020 2021 return result; 2022 } 2023 2024 static gboolean 2025 eel_canvas_accessible_on_screen (GtkWidget *widget) 2026 { 2027 GtkAllocation allocation; 2028 GtkWidget *viewport; 2029 gboolean return_value = TRUE; 2030 2031 gtk_widget_get_allocation (widget, &allocation); 2032 2033 viewport = gtk_widget_get_ancestor (widget, GTK_TYPE_VIEWPORT); 2034 2035 if (viewport) { 2036 GtkAllocation viewport_allocation; 2037 GtkAdjustment *adjustment; 2038 GdkRectangle visible_rect; 2039 2040 gtk_widget_get_allocation (viewport, &viewport_allocation); 2041 2042 adjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (viewport)); 2043 visible_rect.y = gtk_adjustment_get_value (adjustment); 2044 adjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (viewport)); 2045 visible_rect.x = gtk_adjustment_get_value (adjustment); 2046 visible_rect.width = viewport_allocation.width; 2047 visible_rect.height = viewport_allocation.height; 2048 2049 if (((allocation.x + allocation.width) < visible_rect.x) || 2050 ((allocation.y + allocation.height) < visible_rect.y) || 2051 (allocation.x > (visible_rect.x + visible_rect.width)) || 2052 (allocation.y > (visible_rect.y + visible_rect.height))) { 2053 return_value = FALSE; 2054 } 2055 } else { 2056 /* Check whether the widget has been placed off the screen. 2057 * The widget may be MAPPED as when toolbar items do not 2058 * fit on the toolbar. 2059 */ 2060 if (allocation.x + allocation.width <= 0 && 2061 allocation.y + allocation.height <= 0) { 2062 return_value = FALSE; 2063 } 2064 } 2065 2066 return return_value; 2067 } 2068 2069 static AtkStateSet * 2070 eel_canvas_accessible_ref_state_set (AtkObject *accessible) 2071 { 2072 GtkWidget *widget; 2073 AtkStateSet *state_set; 2074 2075 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible)); 2076 state_set = ATK_OBJECT_CLASS (accessible_parent_class)->ref_state_set (accessible); 2077 2078 if (widget == NULL) { 2079 atk_state_set_add_state (state_set, ATK_STATE_DEFUNCT); 2080 } else { 2081 if (gtk_widget_is_sensitive (widget)) { 2082 atk_state_set_add_state (state_set, ATK_STATE_SENSITIVE); 2083 atk_state_set_add_state (state_set, ATK_STATE_ENABLED); 2084 } 2085 2086 if (gtk_widget_get_can_focus (widget)) { 2087 atk_state_set_add_state (state_set, ATK_STATE_FOCUSABLE); 2088 } 2089 /* 2090 * We do not currently generate notifications when an ATK object 2091 * corresponding to a GtkWidget changes visibility by being scrolled 2092 * on or off the screen. The testcase for this is the main window 2093 * of the testgtk application in which a set of buttons in a GtkVBox 2094 * is in a scrolled window with a viewport. 2095 * 2096 * To generate the notifications we would need to do the following: 2097 * 1) Find the GtkViewport among the ancestors of the objects 2098 * 2) Create an accessible for the viewport 2099 * 3) Connect to the value-changed signal on the viewport 2100 * 4) When the signal is received we need to traverse the children 2101 * of the viewport and check whether the children are visible or not 2102 * visible; we may want to restrict this to the widgets for which 2103 * accessible objects have been created. 2104 * 5) We probably need to store a variable on_screen in the 2105 * GtkWidgetAccessible data structure so we can determine whether 2106 * the value has changed. 2107 */ 2108 if (gtk_widget_get_visible (widget)) { 2109 atk_state_set_add_state (state_set, ATK_STATE_VISIBLE); 2110 2111 if (eel_canvas_accessible_on_screen (widget) && 2112 gtk_widget_get_mapped (widget) && 2113 eel_canvas_accessible_all_parents_visible (widget)) { 2114 atk_state_set_add_state (state_set, ATK_STATE_SHOWING); 2115 } 2116 } 2117 2118 if (gtk_widget_has_focus (widget)) { 2119 AtkObject *focus_obj; 2120 2121 focus_obj = g_object_get_data (G_OBJECT (accessible), "gail-focus-object"); 2122 if (focus_obj == NULL) { 2123 atk_state_set_add_state (state_set, ATK_STATE_FOCUSED); 2124 } 2125 } 2126 2127 if (gtk_widget_has_default (widget)) { 2128 atk_state_set_add_state (state_set, ATK_STATE_DEFAULT); 2129 } 2130 } 2131 return state_set; 2132 } 2133 2134 static void 2135 eel_canvas_accessible_class_init (EelCanvasAccessibleClass *klass) 2136 { 2137 AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass); 2138 2139 accessible_parent_class = g_type_class_peek_parent (klass); 2140 2141 atk_class->initialize = eel_canvas_accessible_initialize; 2142 atk_class->get_n_children = eel_canvas_accessible_get_n_children; 2143 atk_class->ref_child = eel_canvas_accessible_ref_child; 2144 /* below adapted from gtkwidgetaccessible.c */ 2145 atk_class->ref_state_set = eel_canvas_accessible_ref_state_set; 2146 } 2147 2148 static void 2149 eel_canvas_accessible_get_extents (AtkComponent *component, 2150 gint *x, 2151 gint *y, 2152 gint *width, 2153 gint *height, 2154 AtkCoordType coord_type) 2155 { 2156 GdkWindow *window; 2157 gint x_window, y_window; 2158 gint x_toplevel, y_toplevel; 2159 GtkWidget *widget; 2160 GtkAllocation allocation; 2161 2162 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component)); 2163 2164 if (widget == NULL) { 2165 return; 2166 } 2167 2168 gtk_widget_get_allocation (widget, &allocation); 2169 *width = allocation.width; 2170 *height = allocation.height; 2171 2172 if (!eel_canvas_accessible_on_screen (widget) || 2173 !gtk_widget_is_drawable (widget)) { 2174 *x = G_MININT; 2175 *y = G_MININT; 2176 2177 return; 2178 } 2179 2180 if (gtk_widget_get_parent (widget)) { 2181 *x = allocation.x; 2182 *y = allocation.y; 2183 window = gtk_widget_get_parent_window (widget); 2184 } else { 2185 *x = 0; 2186 *y = 0; 2187 window = gtk_widget_get_window (widget); 2188 } 2189 2190 gdk_window_get_origin (window, &x_window, &y_window); 2191 *x += x_window; 2192 *y += y_window; 2193 2194 if (coord_type == ATK_XY_WINDOW) { 2195 window = gdk_window_get_toplevel (gtk_widget_get_window (widget)); 2196 gdk_window_get_origin (window, &x_toplevel, &y_toplevel); 2197 2198 *x -= x_toplevel; 2199 *y -= y_toplevel; 2200 } 2201 } 2202 2203 static void 2204 eel_canvas_accessible_get_size (AtkComponent *component, 2205 gint *width, 2206 gint *height) 2207 { 2208 GtkWidget *widget; 2209 2210 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component)); 2211 2212 if (widget == NULL) { 2213 return; 2214 } 2215 2216 *width = gtk_widget_get_allocated_width (widget); 2217 *height = gtk_widget_get_allocated_height (widget); 2218 } 2219 2220 static void 2221 eel_canvas_accessible_component_init(gpointer iface, gpointer data) 2222 { 2223 AtkComponentIface *component; 2224 2225 g_assert (G_TYPE_FROM_INTERFACE(iface) == ATK_TYPE_COMPONENT); 2226 2227 component = iface; 2228 component->get_extents = eel_canvas_accessible_get_extents; 2229 component->get_size = eel_canvas_accessible_get_size; 2230 } 2231 2232 static void 2233 eel_canvas_accessible_init (EelCanvasAccessible *accessible) 2234 { 2235 } 2236 2237 G_DEFINE_TYPE_WITH_CODE (EelCanvasAccessible, eel_canvas_accessible, GTK_TYPE_ACCESSIBLE, 2238 G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT, eel_canvas_accessible_component_init)) 2239 2240 static AtkObject * 2241 eel_canvas_accessible_create (GObject *for_object) 2242 { 2243 GType type; 2244 AtkObject *accessible; 2245 EelCanvas *canvas; 2246 2247 canvas = EEL_CANVAS (for_object); 2248 g_return_val_if_fail (canvas != NULL, NULL); 2249 2250 type = eel_canvas_accessible_get_type (); 2251 2252 if (type == G_TYPE_INVALID) { 2253 return atk_no_op_object_new (for_object); 2254 } 2255 2256 accessible = g_object_new (type, NULL); 2257 atk_object_initialize (accessible, for_object); 2258 return accessible; 2259 } 2260 2261 static GType 2262 eel_canvas_accessible_factory_get_accessible_type (void) 2263 { 2264 return eel_canvas_accessible_get_type (); 2265 } 2266 2267 static AtkObject* 2268 eel_canvas_accessible_factory_create_accessible (GObject *obj) 2269 { 2270 AtkObject *accessible; 2271 2272 g_return_val_if_fail (G_IS_OBJECT (obj), NULL); 2273 2274 accessible = eel_canvas_accessible_create (obj); 2275 2276 return accessible; 2277 } 2278 2279 static void 2280 eel_canvas_accessible_factory_class_init (AtkObjectFactoryClass *klass) 2281 { 2282 klass->create_accessible = eel_canvas_accessible_factory_create_accessible; 2283 klass->get_accessible_type = eel_canvas_accessible_factory_get_accessible_type; 2284 } 2285 2286 static GType 2287 eel_canvas_accessible_factory_get_type (void) 2288 { 2289 static GType type = 0; 2290 2291 if (!type) { 2292 static const GTypeInfo tinfo = { 2293 sizeof (AtkObjectFactoryClass), 2294 (GBaseInitFunc) NULL, 2295 (GBaseFinalizeFunc) NULL, 2296 (GClassInitFunc) eel_canvas_accessible_factory_class_init, 2297 NULL, /* class_finalize */ 2298 NULL, /* class_data */ 2299 sizeof (AtkObjectFactory), 2300 0, /* n_preallocs */ 2301 NULL 2302 }; 2303 type = g_type_register_static (ATK_TYPE_OBJECT_FACTORY, 2304 "EelCanvasAccessibilityFactory", 2305 &tinfo, 0); 2306 } 2307 2308 return type; 2309 } 2310 2311 2312 /* Class initialization function for EelCanvasClass */ 2313 static void 2314 eel_canvas_class_init (EelCanvasClass *klass) 2315 { 2316 GObjectClass *gobject_class; 2317 GtkWidgetClass *widget_class; 2318 2319 gobject_class = (GObjectClass *)klass; 2320 widget_class = (GtkWidgetClass *) klass; 2321 2322 canvas_parent_class = g_type_class_peek_parent (klass); 2323 2324 gobject_class->set_property = eel_canvas_set_property; 2325 gobject_class->get_property = eel_canvas_get_property; 2326 2327 widget_class->destroy = eel_canvas_destroy; 2328 widget_class->map = eel_canvas_map; 2329 widget_class->unmap = eel_canvas_unmap; 2330 widget_class->realize = eel_canvas_realize; 2331 widget_class->unrealize = eel_canvas_unrealize; 2332 widget_class->size_allocate = eel_canvas_size_allocate; 2333 widget_class->button_press_event = eel_canvas_button; 2334 widget_class->button_release_event = eel_canvas_button; 2335 widget_class->motion_notify_event = eel_canvas_motion; 2336 widget_class->draw = eel_canvas_draw; 2337 widget_class->key_press_event = eel_canvas_key; 2338 widget_class->key_release_event = eel_canvas_key; 2339 widget_class->enter_notify_event = eel_canvas_crossing; 2340 widget_class->leave_notify_event = eel_canvas_crossing; 2341 widget_class->focus_in_event = eel_canvas_focus_in; 2342 widget_class->focus_out_event = eel_canvas_focus_out; 2343 widget_class->get_accessible = eel_canvas_get_accessible; 2344 2345 klass->draw_background = eel_canvas_draw_background; 2346 klass->request_update = eel_canvas_request_update_real; 2347 2348 canvas_signals[DRAW_BACKGROUND] = 2349 g_signal_new ("draw_background", 2350 G_TYPE_FROM_CLASS (klass), 2351 G_SIGNAL_RUN_LAST, 2352 G_STRUCT_OFFSET (EelCanvasClass, draw_background), 2353 NULL, NULL, 2354 g_cclosure_marshal_VOID__BOXED, 2355 G_TYPE_NONE, 1, 2356 CAIRO_GOBJECT_TYPE_CONTEXT); 2357 2358 atk_registry_set_factory_type (atk_get_default_registry (), 2359 EEL_TYPE_CANVAS, 2360 eel_canvas_accessible_factory_get_type ()); 2361 } 2362 2363 /* Callback used when the root item of a canvas is destroyed. The user should 2364 * never ever do this, so we panic if this happens. 2365 */ 2366 static void 2367 panic_root_destroyed (GtkWidget *object, gpointer data) 2368 { 2369 g_error ("Eeeek, root item %p of canvas %p was destroyed!", object, data); 2370 } 2371 2372 /* Object initialization function for EelCanvas */ 2373 static void 2374 eel_canvas_init (EelCanvas *canvas) 2375 { 2376 guint width, height; 2377 gtk_widget_set_can_focus (GTK_WIDGET (canvas), TRUE); 2378 2379 gtk_widget_set_redraw_on_allocate (GTK_WIDGET (canvas), FALSE); 2380 2381 canvas->scroll_x1 = 0.0; 2382 canvas->scroll_y1 = 0.0; 2383 gtk_layout_get_size (GTK_LAYOUT (canvas), 2384 &width, &height); 2385 canvas->scroll_x2 = width; 2386 canvas->scroll_y2 = height; 2387 2388 canvas->pixels_per_unit = 1.0; 2389 2390 canvas->pick_event.type = GDK_LEAVE_NOTIFY; 2391 canvas->pick_event.crossing.x = 0; 2392 canvas->pick_event.crossing.y = 0; 2393 2394 gtk_scrollable_set_hadjustment (GTK_SCROLLABLE (canvas), NULL); 2395 gtk_scrollable_set_vadjustment (GTK_SCROLLABLE (canvas), NULL); 2396 2397 /* Create the root item as a special case */ 2398 2399 canvas->root = EEL_CANVAS_ITEM (g_object_new (eel_canvas_group_get_type (), NULL)); 2400 canvas->root->canvas = canvas; 2401 2402 g_object_ref_sink (canvas->root); 2403 2404 canvas->root_destroy_id = g_signal_connect (G_OBJECT (canvas->root), 2405 "destroy", G_CALLBACK (panic_root_destroyed), canvas); 2406 2407 canvas->need_repick = TRUE; 2408 canvas->doing_update = FALSE; 2409 } 2410 2411 /* Convenience function to remove the idle handler of a canvas */ 2412 static void 2413 remove_idle (EelCanvas *canvas) 2414 { 2415 if (canvas->idle_id == 0) 2416 return; 2417 2418 g_source_remove (canvas->idle_id); 2419 canvas->idle_id = 0; 2420 } 2421 2422 /* Removes the transient state of the canvas (idle handler, grabs). */ 2423 static void 2424 shutdown_transients (EelCanvas *canvas) 2425 { 2426 /* We turn off the need_redraw flag, since if the canvas is mapped again 2427 * it will request a redraw anyways. We do not turn off the need_update 2428 * flag, though, because updates are not queued when the canvas remaps 2429 * itself. 2430 */ 2431 if (canvas->need_redraw) { 2432 canvas->need_redraw = FALSE; 2433 } 2434 2435 if (canvas->grabbed_item) { 2436 eel_canvas_item_ungrab (canvas->grabbed_item, GDK_CURRENT_TIME); 2437 } 2438 2439 remove_idle (canvas); 2440 } 2441 2442 /* Destroy handler for EelCanvas */ 2443 static void 2444 eel_canvas_destroy (GtkWidget *object) 2445 { 2446 EelCanvas *canvas; 2447 2448 g_return_if_fail (EEL_IS_CANVAS (object)); 2449 2450 /* remember, destroy can be run multiple times! */ 2451 2452 canvas = EEL_CANVAS (object); 2453 2454 if (canvas->root_destroy_id) { 2455 g_signal_handler_disconnect (G_OBJECT (canvas->root), canvas->root_destroy_id); 2456 canvas->root_destroy_id = 0; 2457 } 2458 if (canvas->root) { 2459 EelCanvasItem *root = canvas->root; 2460 canvas->root = NULL; 2461 eel_canvas_item_destroy (root); 2462 g_object_unref (root); 2463 } 2464 2465 shutdown_transients (canvas); 2466 2467 if (GTK_WIDGET_CLASS (canvas_parent_class)->destroy) 2468 (* GTK_WIDGET_CLASS (canvas_parent_class)->destroy) (object); 2469 } 2470 2471 /** 2472 * eel_canvas_new: 2473 * @void: 2474 * 2475 * Creates a new empty canvas. If you wish to use the 2476 * &EelCanvasImage item inside this canvas, then you must push the gdk_imlib 2477 * visual and colormap before calling this function, and they can be popped 2478 * afterwards. 2479 * 2480 * Return value: A newly-created canvas. 2481 **/ 2482 GtkWidget * 2483 eel_canvas_new (void) 2484 { 2485 return GTK_WIDGET (g_object_new (eel_canvas_get_type (), NULL)); 2486 } 2487 2488 /* Map handler for the canvas */ 2489 static void 2490 eel_canvas_map (GtkWidget *widget) 2491 { 2492 EelCanvas *canvas; 2493 2494 g_return_if_fail (EEL_IS_CANVAS (widget)); 2495 2496 /* Normal widget mapping stuff */ 2497 2498 if (GTK_WIDGET_CLASS (canvas_parent_class)->map) 2499 (* GTK_WIDGET_CLASS (canvas_parent_class)->map) (widget); 2500 2501 canvas = EEL_CANVAS (widget); 2502 2503 /* Map items */ 2504 2505 if (canvas->root->flags & EEL_CANVAS_ITEM_VISIBLE && 2506 !(canvas->root->flags & EEL_CANVAS_ITEM_MAPPED) && 2507 EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->map) 2508 (* EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->map) (canvas->root); 2509 } 2510 2511 /* Unmap handler for the canvas */ 2512 static void 2513 eel_canvas_unmap (GtkWidget *widget) 2514 { 2515 EelCanvas *canvas; 2516 2517 g_return_if_fail (EEL_IS_CANVAS (widget)); 2518 2519 canvas = EEL_CANVAS (widget); 2520 2521 shutdown_transients (canvas); 2522 2523 /* Unmap items */ 2524 2525 if (EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->unmap) 2526 (* EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->unmap) (canvas->root); 2527 2528 /* Normal widget unmapping stuff */ 2529 2530 if (GTK_WIDGET_CLASS (canvas_parent_class)->unmap) 2531 (* GTK_WIDGET_CLASS (canvas_parent_class)->unmap) (widget); 2532 } 2533 2534 /* Realize handler for the canvas */ 2535 static void 2536 eel_canvas_realize (GtkWidget *widget) 2537 { 2538 EelCanvas *canvas; 2539 2540 g_return_if_fail (EEL_IS_CANVAS (widget)); 2541 2542 /* Normal widget realization stuff */ 2543 2544 if (GTK_WIDGET_CLASS (canvas_parent_class)->realize) 2545 (* GTK_WIDGET_CLASS (canvas_parent_class)->realize) (widget); 2546 2547 canvas = EEL_CANVAS (widget); 2548 2549 gdk_window_set_events (gtk_layout_get_bin_window (GTK_LAYOUT (canvas)), 2550 (gdk_window_get_events (gtk_layout_get_bin_window (GTK_LAYOUT (canvas))) 2551 | GDK_EXPOSURE_MASK 2552 | GDK_BUTTON_PRESS_MASK 2553 | GDK_BUTTON_RELEASE_MASK 2554 | GDK_POINTER_MOTION_MASK 2555 | GDK_KEY_PRESS_MASK 2556 | GDK_KEY_RELEASE_MASK 2557 | GDK_ENTER_NOTIFY_MASK 2558 | GDK_LEAVE_NOTIFY_MASK 2559 | GDK_FOCUS_CHANGE_MASK)); 2560 2561 /* Create our own temporary pixmap gc and realize all the items */ 2562 2563 (* EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->realize) (canvas->root); 2564 } 2565 2566 /* Unrealize handler for the canvas */ 2567 static void 2568 eel_canvas_unrealize (GtkWidget *widget) 2569 { 2570 EelCanvas *canvas; 2571 2572 g_return_if_fail (EEL_IS_CANVAS (widget)); 2573 2574 canvas = EEL_CANVAS (widget); 2575 2576 shutdown_transients (canvas); 2577 2578 /* Unrealize items and parent widget */ 2579 2580 (* EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->unrealize) (canvas->root); 2581 2582 if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) 2583 (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget); 2584 } 2585 2586 /* Handles scrolling of the canvas. Adjusts the scrolling and zooming offset to 2587 * keep as much as possible of the canvas scrolling region in view. 2588 */ 2589 static void 2590 scroll_to (EelCanvas *canvas, int cx, int cy) 2591 { 2592 int scroll_width, scroll_height; 2593 int right_limit, bottom_limit; 2594 int old_zoom_xofs, old_zoom_yofs; 2595 int changed_x = FALSE, changed_y = FALSE; 2596 int canvas_width, canvas_height; 2597 GtkAllocation allocation; 2598 GtkAdjustment *vadjustment, *hadjustment; 2599 guint width, height; 2600 2601 gtk_widget_get_allocation (GTK_WIDGET (canvas), &allocation); 2602 canvas_width = allocation.width; 2603 canvas_height = allocation.height; 2604 2605 scroll_width = floor ((canvas->scroll_x2 - canvas->scroll_x1) * canvas->pixels_per_unit + 0.5); 2606 scroll_height = floor ((canvas->scroll_y2 - canvas->scroll_y1) * canvas->pixels_per_unit + 0.5); 2607 2608 right_limit = scroll_width - canvas_width; 2609 bottom_limit = scroll_height - canvas_height; 2610 2611 old_zoom_xofs = canvas->zoom_xofs; 2612 old_zoom_yofs = canvas->zoom_yofs; 2613 2614 if (right_limit < 0) { 2615 cx = 0; 2616 if (canvas->center_scroll_region) { 2617 canvas->zoom_xofs = (canvas_width - scroll_width) / 2; 2618 scroll_width = canvas_width; 2619 } else { 2620 canvas->zoom_xofs = 0; 2621 } 2622 } else if (cx < 0) { 2623 cx = 0; 2624 canvas->zoom_xofs = 0; 2625 } else if (cx > right_limit) { 2626 cx = right_limit; 2627 canvas->zoom_xofs = 0; 2628 } else 2629 canvas->zoom_xofs = 0; 2630 2631 if (bottom_limit < 0) { 2632 cy = 0; 2633 if (canvas->center_scroll_region) { 2634 canvas->zoom_yofs = (canvas_height - scroll_height) / 2; 2635 scroll_height = canvas_height; 2636 } else { 2637 canvas->zoom_yofs = 0; 2638 } 2639 } else if (cy < 0) { 2640 cy = 0; 2641 canvas->zoom_yofs = 0; 2642 } else if (cy > bottom_limit) { 2643 cy = bottom_limit; 2644 canvas->zoom_yofs = 0; 2645 } else 2646 canvas->zoom_yofs = 0; 2647 2648 if ((canvas->zoom_xofs != old_zoom_xofs) || (canvas->zoom_yofs != old_zoom_yofs)) { 2649 /* This can only occur, if either canvas size or widget size changes */ 2650 /* So I think we can request full redraw here */ 2651 /* More stuff - we have to mark root as needing fresh affine (Lauris) */ 2652 if (!(canvas->root->flags & EEL_CANVAS_ITEM_NEED_DEEP_UPDATE)) { 2653 canvas->root->flags |= EEL_CANVAS_ITEM_NEED_DEEP_UPDATE; 2654 eel_canvas_request_update (canvas); 2655 } 2656 gtk_widget_queue_draw (GTK_WIDGET (canvas)); 2657 } 2658 2659 hadjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (canvas)); 2660 vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (canvas)); 2661 2662 if (((int) gtk_adjustment_get_value (hadjustment)) != cx) { 2663 gtk_adjustment_set_value (hadjustment, cx); 2664 changed_x = TRUE; 2665 } 2666 2667 if (((int) gtk_adjustment_get_value (vadjustment)) != cy) { 2668 gtk_adjustment_set_value (vadjustment, cy); 2669 changed_y = TRUE; 2670 } 2671 2672 gtk_layout_get_size (&canvas->layout, &width, &height); 2673 if ((scroll_width != (int) width )|| (scroll_height != (int) height)) { 2674 gtk_layout_set_size (GTK_LAYOUT (canvas), scroll_width, scroll_height); 2675 } 2676 2677 /* Signal GtkLayout that it should do a redraw. */ 2678 if (changed_x) 2679 g_signal_emit_by_name (hadjustment, "value_changed"); 2680 if (changed_y) 2681 g_signal_emit_by_name (vadjustment, "value_changed"); 2682 } 2683 2684 /* Size allocation handler for the canvas */ 2685 static void 2686 eel_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation) 2687 { 2688 EelCanvas *canvas; 2689 GtkAdjustment *vadjustment, *hadjustment; 2690 2691 g_return_if_fail (EEL_IS_CANVAS (widget)); 2692 g_return_if_fail (allocation != NULL); 2693 2694 if (GTK_WIDGET_CLASS (canvas_parent_class)->size_allocate) 2695 (* GTK_WIDGET_CLASS (canvas_parent_class)->size_allocate) (widget, allocation); 2696 2697 canvas = EEL_CANVAS (widget); 2698 2699 /* Recenter the view, if appropriate */ 2700 2701 hadjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (canvas)); 2702 vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (canvas)); 2703 2704 gtk_adjustment_set_page_size (hadjustment, allocation->width); 2705 gtk_adjustment_set_page_increment (hadjustment, allocation->width / 2); 2706 2707 gtk_adjustment_set_page_size (vadjustment, allocation->height); 2708 gtk_adjustment_set_page_increment (vadjustment, allocation->height / 2); 2709 2710 scroll_to (canvas, 2711 gtk_adjustment_get_value (hadjustment), 2712 gtk_adjustment_get_value (vadjustment)); 2713 2714 g_signal_emit_by_name (hadjustment, "changed"); 2715 g_signal_emit_by_name (vadjustment, "changed"); 2716 } 2717 2718 /* Emits an event for an item in the canvas, be it the current item, grabbed 2719 * item, or focused item, as appropriate. 2720 */ 2721 2722 static int 2723 emit_event (EelCanvas *canvas, GdkEvent *event) 2724 { 2725 GdkEvent ev; 2726 gint finished; 2727 EelCanvasItem *item; 2728 EelCanvasItem *parent; 2729 guint mask; 2730 2731 /* Could be an old pick event */ 2732 if (!gtk_widget_get_realized (GTK_WIDGET (canvas))) { 2733 return FALSE; 2734 } 2735 2736 /* Perform checks for grabbed items */ 2737 2738 if (canvas->grabbed_item && 2739 !is_descendant (canvas->current_item, canvas->grabbed_item)) { 2740 return FALSE; 2741 } 2742 2743 if (canvas->grabbed_item) { 2744 switch (event->type) { 2745 case GDK_ENTER_NOTIFY: 2746 mask = GDK_ENTER_NOTIFY_MASK; 2747 break; 2748 2749 case GDK_LEAVE_NOTIFY: 2750 mask = GDK_LEAVE_NOTIFY_MASK; 2751 break; 2752 2753 case GDK_MOTION_NOTIFY: 2754 mask = GDK_POINTER_MOTION_MASK; 2755 break; 2756 2757 case GDK_BUTTON_PRESS: 2758 case GDK_2BUTTON_PRESS: 2759 case GDK_3BUTTON_PRESS: 2760 mask = GDK_BUTTON_PRESS_MASK; 2761 break; 2762 2763 case GDK_BUTTON_RELEASE: 2764 mask = GDK_BUTTON_RELEASE_MASK; 2765 break; 2766 2767 case GDK_KEY_PRESS: 2768 mask = GDK_KEY_PRESS_MASK; 2769 break; 2770 2771 case GDK_KEY_RELEASE: 2772 mask = GDK_KEY_RELEASE_MASK; 2773 break; 2774 2775 default: 2776 mask = 0; 2777 break; 2778 } 2779 2780 if (!(mask & canvas->grabbed_event_mask)) 2781 return FALSE; 2782 } 2783 2784 /* Convert to world coordinates -- we have two cases because of diferent 2785 * offsets of the fields in the event structures. 2786 */ 2787 2788 ev = *event; 2789 2790 switch (ev.type) 2791 { 2792 case GDK_ENTER_NOTIFY: 2793 case GDK_LEAVE_NOTIFY: 2794 eel_canvas_window_to_world (canvas, 2795 ev.crossing.x, ev.crossing.y, 2796 &ev.crossing.x, &ev.crossing.y); 2797 break; 2798 2799 case GDK_MOTION_NOTIFY: 2800 eel_canvas_window_to_world (canvas, 2801 ev.motion.x, ev.motion.y, 2802 &ev.motion.x, &ev.motion.y); 2803 break; 2804 2805 case GDK_BUTTON_PRESS: 2806 case GDK_2BUTTON_PRESS: 2807 case GDK_3BUTTON_PRESS: 2808 eel_canvas_window_to_world (canvas, 2809 ev.motion.x, ev.motion.y, 2810 &ev.motion.x, &ev.motion.y); 2811 break; 2812 2813 case GDK_BUTTON_RELEASE: 2814 eel_canvas_window_to_world (canvas, 2815 ev.motion.x, ev.motion.y, 2816 &ev.motion.x, &ev.motion.y); 2817 break; 2818 2819 default: 2820 break; 2821 } 2822 2823 /* Choose where we send the event */ 2824 2825 item = canvas->current_item; 2826 2827 if (canvas->focused_item 2828 && ((event->type == GDK_KEY_PRESS) || 2829 (event->type == GDK_KEY_RELEASE) || 2830 (event->type == GDK_FOCUS_CHANGE))) 2831 item = canvas->focused_item; 2832 2833 /* The event is propagated up the hierarchy (for if someone connected to 2834 * a group instead of a leaf event), and emission is stopped if a 2835 * handler returns TRUE, just like for GtkWidget events. 2836 */ 2837 2838 finished = FALSE; 2839 2840 while (item && !finished) { 2841 g_object_ref (item); 2842 2843 g_signal_emit ( 2844 G_OBJECT (item), item_signals[ITEM_EVENT], 0, 2845 &ev, &finished); 2846 2847 parent = item->parent; 2848 g_object_unref (item); 2849 2850 item = parent; 2851 } 2852 2853 return finished; 2854 } 2855 2856 /* Re-picks the current item in the canvas, based on the event's coordinates. 2857 * Also emits enter/leave events for items as appropriate. 2858 */ 2859 static int 2860 pick_current_item (EelCanvas *canvas, GdkEvent *event) 2861 { 2862 int button_down; 2863 double x, y; 2864 int cx, cy; 2865 int retval; 2866 2867 retval = FALSE; 2868 2869 /* If a button is down, we'll perform enter and leave events on the 2870 * current item, but not enter on any other item. This is more or less 2871 * like X pointer grabbing for canvas items. 2872 */ 2873 button_down = canvas->state & (GDK_BUTTON1_MASK 2874 | GDK_BUTTON2_MASK 2875 | GDK_BUTTON3_MASK 2876 | GDK_BUTTON4_MASK 2877 | GDK_BUTTON5_MASK); 2878 if (!button_down) 2879 canvas->left_grabbed_item = FALSE; 2880 2881 /* Save the event in the canvas. This is used to synthesize enter and 2882 * leave events in case the current item changes. It is also used to 2883 * re-pick the current item if the current one gets deleted. Also, 2884 * synthesize an enter event. 2885 */ 2886 if (event != &canvas->pick_event) { 2887 if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) { 2888 /* these fields have the same offsets in both types of events */ 2889 2890 canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY; 2891 canvas->pick_event.crossing.window = event->motion.window; 2892 canvas->pick_event.crossing.send_event = event->motion.send_event; 2893 canvas->pick_event.crossing.subwindow = NULL; 2894 canvas->pick_event.crossing.x = event->motion.x; 2895 canvas->pick_event.crossing.y = event->motion.y; 2896 canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL; 2897 canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR; 2898 canvas->pick_event.crossing.focus = FALSE; 2899 canvas->pick_event.crossing.state = event->motion.state; 2900 2901 /* these fields don't have the same offsets in both types of events */ 2902 2903 if (event->type == GDK_MOTION_NOTIFY) { 2904 canvas->pick_event.crossing.x_root = event->motion.x_root; 2905 canvas->pick_event.crossing.y_root = event->motion.y_root; 2906 } else { 2907 canvas->pick_event.crossing.x_root = event->button.x_root; 2908 canvas->pick_event.crossing.y_root = event->button.y_root; 2909 } 2910 } else 2911 canvas->pick_event = *event; 2912 } 2913 2914 /* Don't do anything else if this is a recursive call */ 2915 2916 if (canvas->in_repick) 2917 return retval; 2918 2919 /* LeaveNotify means that there is no current item, so we don't look for one */ 2920 2921 if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) { 2922 /* these fields don't have the same offsets in both types of events */ 2923 2924 if (canvas->pick_event.type == GDK_ENTER_NOTIFY) { 2925 x = canvas->pick_event.crossing.x; 2926 y = canvas->pick_event.crossing.y; 2927 } else { 2928 x = canvas->pick_event.motion.x; 2929 y = canvas->pick_event.motion.y; 2930 } 2931 2932 /* canvas pixel coords */ 2933 2934 cx = (int) (x + 0.5); 2935 cy = (int) (y + 0.5); 2936 2937 /* world coords */ 2938 eel_canvas_c2w (canvas, cx, cy, &x, &y); 2939 2940 /* find the closest item */ 2941 if (canvas->root->flags & EEL_CANVAS_ITEM_MAPPED) 2942 eel_canvas_item_invoke_point (canvas->root, x, y, cx, cy, 2943 &canvas->new_current_item); 2944 else 2945 canvas->new_current_item = NULL; 2946 } else 2947 canvas->new_current_item = NULL; 2948 2949 if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item) 2950 return retval; /* current item did not change */ 2951 2952 /* Synthesize events for old and new current items */ 2953 2954 if ((canvas->new_current_item != canvas->current_item) 2955 && (canvas->current_item != NULL) 2956 && !canvas->left_grabbed_item) { 2957 GdkEvent new_event; 2958 2959 new_event = canvas->pick_event; 2960 new_event.type = GDK_LEAVE_NOTIFY; 2961 2962 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR; 2963 new_event.crossing.subwindow = NULL; 2964 canvas->in_repick = TRUE; 2965 retval = emit_event (canvas, &new_event); 2966 canvas->in_repick = FALSE; 2967 } 2968 2969 /* new_current_item may have been set to NULL during the call to emit_event() above */ 2970 2971 if ((canvas->new_current_item != canvas->current_item) && button_down) { 2972 canvas->left_grabbed_item = TRUE; 2973 return retval; 2974 } 2975 2976 /* Handle the rest of cases */ 2977 2978 canvas->left_grabbed_item = FALSE; 2979 canvas->current_item = canvas->new_current_item; 2980 2981 if (canvas->current_item != NULL) { 2982 GdkEvent new_event; 2983 2984 new_event = canvas->pick_event; 2985 new_event.type = GDK_ENTER_NOTIFY; 2986 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR; 2987 new_event.crossing.subwindow = NULL; 2988 retval = emit_event (canvas, &new_event); 2989 } 2990 2991 return retval; 2992 } 2993 2994 /* Button event handler for the canvas */ 2995 static gint 2996 eel_canvas_button (GtkWidget *widget, GdkEventButton *event) 2997 { 2998 EelCanvas *canvas; 2999 int mask; 3000 int retval; 3001 3002 g_return_val_if_fail (EEL_IS_CANVAS (widget), FALSE); 3003 g_return_val_if_fail (event != NULL, FALSE); 3004 3005 retval = FALSE; 3006 3007 canvas = EEL_CANVAS (widget); 3008 3009 /* Don't handle extra mouse button events */ 3010 if (event->button > 5) 3011 return FALSE; 3012 3013 /* 3014 * dispatch normally regardless of the event's window if an item has 3015 * has a pointer grab in effect 3016 */ 3017 if (!canvas->grabbed_item && event->window != gtk_layout_get_bin_window (GTK_LAYOUT (canvas))) 3018 return retval; 3019 3020 switch (event->button) { 3021 case 1: 3022 mask = GDK_BUTTON1_MASK; 3023 break; 3024 case 2: 3025 mask = GDK_BUTTON2_MASK; 3026 break; 3027 case 3: 3028 mask = GDK_BUTTON3_MASK; 3029 break; 3030 case 4: 3031 mask = GDK_BUTTON4_MASK; 3032 break; 3033 case 5: 3034 mask = GDK_BUTTON5_MASK; 3035 break; 3036 default: 3037 mask = 0; 3038 } 3039 3040 switch (event->type) { 3041 case GDK_BUTTON_PRESS: 3042 case GDK_2BUTTON_PRESS: 3043 case GDK_3BUTTON_PRESS: 3044 /* Pick the current item as if the button were not pressed, and 3045 * then process the event. 3046 */ 3047 canvas->state = event->state; 3048 pick_current_item (canvas, (GdkEvent *) event); 3049 canvas->state ^= mask; 3050 retval = emit_event (canvas, (GdkEvent *) event); 3051 break; 3052 3053 case GDK_BUTTON_RELEASE: 3054 /* Process the event as if the button were pressed, then repick 3055 * after the button has been released 3056 */ 3057 canvas->state = event->state; 3058 retval = emit_event (canvas, (GdkEvent *) event); 3059 event->state ^= mask; 3060 canvas->state = event->state; 3061 pick_current_item (canvas, (GdkEvent *) event); 3062 event->state ^= mask; 3063 break; 3064 3065 default: 3066 g_assert_not_reached (); 3067 } 3068 3069 return retval; 3070 } 3071 3072 /* Motion event handler for the canvas */ 3073 static gint 3074 eel_canvas_motion (GtkWidget *widget, GdkEventMotion *event) 3075 { 3076 EelCanvas *canvas; 3077 3078 g_return_val_if_fail (EEL_IS_CANVAS (widget), FALSE); 3079 g_return_val_if_fail (event != NULL, FALSE); 3080 3081 canvas = EEL_CANVAS (widget); 3082 3083 if (event->window != gtk_layout_get_bin_window (GTK_LAYOUT (canvas))) 3084 return FALSE; 3085 3086 canvas->state = event->state; 3087 pick_current_item (canvas, (GdkEvent *) event); 3088 return emit_event (canvas, (GdkEvent *) event); 3089 } 3090 3091 /* Key event handler for the canvas */ 3092 static gint 3093 eel_canvas_key (GtkWidget *widget, GdkEventKey *event) 3094 { 3095 EelCanvas *canvas; 3096 3097 g_return_val_if_fail (EEL_IS_CANVAS (widget), FALSE); 3098 g_return_val_if_fail (event != NULL, FALSE); 3099 3100 canvas = EEL_CANVAS (widget); 3101 3102 if (emit_event (canvas, (GdkEvent *) event)) 3103 return TRUE; 3104 if (event->type == GDK_KEY_RELEASE) 3105 return GTK_WIDGET_CLASS (canvas_parent_class)->key_release_event (widget, event); 3106 else 3107 return GTK_WIDGET_CLASS (canvas_parent_class)->key_press_event (widget, event); 3108 } 3109 3110 3111 /* Crossing event handler for the canvas */ 3112 static gint 3113 eel_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event) 3114 { 3115 EelCanvas *canvas; 3116 3117 g_return_val_if_fail (EEL_IS_CANVAS (widget), FALSE); 3118 g_return_val_if_fail (event != NULL, FALSE); 3119 3120 canvas = EEL_CANVAS (widget); 3121 3122 if (event->window != gtk_layout_get_bin_window (GTK_LAYOUT (canvas))) 3123 return FALSE; 3124 3125 canvas->state = event->state; 3126 return pick_current_item (canvas, (GdkEvent *) event); 3127 } 3128 3129 /* Focus in handler for the canvas */ 3130 static gint 3131 eel_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event) 3132 { 3133 EelCanvas *canvas; 3134 3135 canvas = EEL_CANVAS (widget); 3136 3137 if (canvas->focused_item) 3138 return emit_event (canvas, (GdkEvent *) event); 3139 else 3140 return FALSE; 3141 } 3142 3143 static AtkObject * 3144 eel_canvas_get_accessible (GtkWidget *widget) 3145 { 3146 return atk_gobject_accessible_for_object (G_OBJECT (widget)); 3147 } 3148 3149 /* Focus out handler for the canvas */ 3150 static gint 3151 eel_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event) 3152 { 3153 EelCanvas *canvas; 3154 3155 canvas = EEL_CANVAS (widget); 3156 3157 if (canvas->focused_item) 3158 return emit_event (canvas, (GdkEvent *) event); 3159 else 3160 return FALSE; 3161 } 3162 3163 3164 static cairo_region_t * 3165 eel_cairo_get_clip_region (cairo_t *cr) 3166 { 3167 cairo_rectangle_list_t *list; 3168 cairo_region_t *region; 3169 int i; 3170 3171 list = cairo_copy_clip_rectangle_list (cr); 3172 if (list->status == CAIRO_STATUS_CLIP_NOT_REPRESENTABLE) { 3173 cairo_rectangle_int_t clip_rect; 3174 3175 cairo_rectangle_list_destroy (list); 3176 3177 if (!gdk_cairo_get_clip_rectangle (cr, &clip_rect)) 3178 return NULL; 3179 return cairo_region_create_rectangle (&clip_rect); 3180 } 3181 3182 3183 region = cairo_region_create (); 3184 for (i = list->num_rectangles - 1; i >= 0; --i) { 3185 cairo_rectangle_t *rect = &list->rectangles[i]; 3186 cairo_rectangle_int_t clip_rect; 3187 3188 clip_rect.x = floor (rect->x); 3189 clip_rect.y = floor (rect->y); 3190 clip_rect.width = ceil (rect->x + rect->width) - clip_rect.x; 3191 clip_rect.height = ceil (rect->y + rect->height) - clip_rect.y; 3192 3193 if (cairo_region_union_rectangle (region, &clip_rect) != CAIRO_STATUS_SUCCESS) { 3194 cairo_region_destroy (region); 3195 region = NULL; 3196 break; 3197 } 3198 } 3199 3200 cairo_rectangle_list_destroy (list); 3201 return region; 3202 } 3203 3204 /* Expose handler for the canvas */ 3205 static gboolean 3206 eel_canvas_draw (GtkWidget *widget, cairo_t *cr) 3207 { 3208 EelCanvas *canvas = EEL_CANVAS (widget); 3209 GdkWindow *bin_window; 3210 cairo_region_t *region; 3211 3212 if (!gdk_cairo_get_clip_rectangle (cr, NULL)) 3213 return FALSE; 3214 3215 bin_window = gtk_layout_get_bin_window (GTK_LAYOUT (widget)); 3216 gtk_cairo_transform_to_window (cr, widget, bin_window); 3217 3218 region = eel_cairo_get_clip_region (cr); 3219 if (region == NULL) 3220 return FALSE; 3221 3222 #ifdef VERBOSE 3223 g_print ("Draw\n"); 3224 #endif 3225 /* If there are any outstanding items that need updating, do them now */ 3226 if (canvas->idle_id) { 3227 g_source_remove (canvas->idle_id); 3228 canvas->idle_id = 0; 3229 } 3230 if (canvas->need_update) { 3231 g_return_val_if_fail (!canvas->doing_update, FALSE); 3232 3233 canvas->doing_update = TRUE; 3234 eel_canvas_item_invoke_update (canvas->root, 0, 0, 0); 3235 3236 g_return_val_if_fail (canvas->doing_update, FALSE); 3237 3238 canvas->doing_update = FALSE; 3239 3240 canvas->need_update = FALSE; 3241 } 3242 3243 /* Hmmm. Would like to queue antiexposes if the update marked 3244 anything that is gonna get redrawn as invalid */ 3245 3246 g_signal_emit (G_OBJECT (canvas), canvas_signals[DRAW_BACKGROUND], 0, 3247 cr); 3248 3249 if (canvas->root->flags & EEL_CANVAS_ITEM_MAPPED) 3250 EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->draw (canvas->root, cr, region); 3251 3252 /* Chain up to get exposes on child widgets */ 3253 if (GTK_WIDGET_CLASS (canvas_parent_class)->draw) 3254 GTK_WIDGET_CLASS (canvas_parent_class)->draw (widget, cr); 3255 3256 cairo_region_destroy (region); 3257 return FALSE; 3258 } 3259 3260 static void 3261 eel_canvas_draw_background (EelCanvas *canvas, 3262 cairo_t *cr) 3263 { 3264 cairo_rectangle_int_t rect; 3265 GtkStyleContext *style_context; 3266 GdkRGBA color; 3267 3268 if (!gdk_cairo_get_clip_rectangle (cr, &rect)) 3269 return; 3270 3271 cairo_save (cr); 3272 /* By default, we use the style background. */ 3273 style_context = gtk_widget_get_style_context (GTK_WIDGET (canvas)); 3274 gtk_style_context_get_background_color (style_context, GTK_STATE_FLAG_NORMAL, &color); 3275 gdk_cairo_set_source_rgba (cr, &color); 3276 gdk_cairo_rectangle (cr, &rect); 3277 cairo_fill (cr); 3278 cairo_restore (cr); 3279 } 3280 3281 static void 3282 do_update (EelCanvas *canvas) 3283 { 3284 /* Cause the update if necessary */ 3285 3286 update_again: 3287 if (canvas->need_update) { 3288 g_return_if_fail (!canvas->doing_update); 3289 3290 canvas->doing_update = TRUE; 3291 eel_canvas_item_invoke_update (canvas->root, 0, 0, 0); 3292 3293 g_return_if_fail (canvas->doing_update); 3294 3295 canvas->doing_update = FALSE; 3296 3297 canvas->need_update = FALSE; 3298 } 3299 3300 /* Pick new current item */ 3301 3302 while (canvas->need_repick) { 3303 canvas->need_repick = FALSE; 3304 pick_current_item (canvas, &canvas->pick_event); 3305 } 3306 3307 /* it is possible that during picking we emitted an event in which 3308 the user then called some function which then requested update 3309 of something. Without this we'd be left in a state where 3310 need_update would have been left TRUE and the canvas would have 3311 been left unpainted. */ 3312 if (canvas->need_update) { 3313 goto update_again; 3314 } 3315 } 3316 3317 /* Idle handler for the canvas. It deals with pending updates and redraws. */ 3318 static gint 3319 idle_handler (gpointer data) 3320 { 3321 EelCanvas *canvas; 3322 3323 canvas = EEL_CANVAS (data); 3324 do_update (canvas); 3325 3326 /* Reset idle id */ 3327 canvas->idle_id = 0; 3328 3329 return FALSE; 3330 } 3331 3332 /* Convenience function to add an idle handler to a canvas */ 3333 static void 3334 add_idle (EelCanvas *canvas) 3335 { 3336 if (!canvas->idle_id) { 3337 /* We let the update idle handler have higher priority 3338 * than the redraw idle handler so the canvas state 3339 * will be updated during the expose event. canvas in 3340 * expose_event. 3341 */ 3342 canvas->idle_id = g_idle_add_full (GDK_PRIORITY_REDRAW - 20, 3343 idle_handler, canvas, NULL); 3344 } 3345 } 3346 3347 /** 3348 * eel_canvas_root: 3349 * @canvas: A canvas. 3350 * 3351 * Queries the root group of a canvas. 3352 * 3353 * Return value: The root group of the specified canvas. 3354 **/ 3355 EelCanvasGroup * 3356 eel_canvas_root (EelCanvas *canvas) 3357 { 3358 g_return_val_if_fail (EEL_IS_CANVAS (canvas), NULL); 3359 3360 return EEL_CANVAS_GROUP (canvas->root); 3361 } 3362 3363 3364 /** 3365 * eel_canvas_set_scroll_region: 3366 * @canvas: A canvas. 3367 * @x1: Leftmost limit of the scrolling region. 3368 * @y1: Upper limit of the scrolling region. 3369 * @x2: Rightmost limit of the scrolling region. 3370 * @y2: Lower limit of the scrolling region. 3371 * 3372 * Sets the scrolling region of a canvas to the specified rectangle. The canvas 3373 * will then be able to scroll only within this region. The view of the canvas 3374 * is adjusted as appropriate to display as much of the new region as possible. 3375 **/ 3376 void 3377 eel_canvas_set_scroll_region (EelCanvas *canvas, double x1, double y1, double x2, double y2) 3378 { 3379 double wxofs, wyofs; 3380 int xofs, yofs; 3381 GtkAdjustment *vadjustment, *hadjustment; 3382 3383 g_return_if_fail (EEL_IS_CANVAS (canvas)); 3384 3385 if ((canvas->scroll_x1 == x1) && (canvas->scroll_y1 == y1) && 3386 (canvas->scroll_x2 == x2) && (canvas->scroll_y2 == y2)) { 3387 return; 3388 } 3389 3390 /* 3391 * Set the new scrolling region. If possible, do not move the visible contents of the 3392 * canvas. 3393 */ 3394 hadjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (canvas)); 3395 vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (canvas)); 3396 3397 eel_canvas_c2w (canvas, 3398 gtk_adjustment_get_value (hadjustment) + canvas->zoom_xofs, 3399 gtk_adjustment_get_value (vadjustment) + canvas->zoom_yofs, 3400 /*canvas->zoom_xofs, 3401 canvas->zoom_yofs,*/ 3402 &wxofs, &wyofs); 3403 3404 canvas->scroll_x1 = x1; 3405 canvas->scroll_y1 = y1; 3406 canvas->scroll_x2 = x2; 3407 canvas->scroll_y2 = y2; 3408 3409 eel_canvas_w2c (canvas, wxofs, wyofs, &xofs, &yofs); 3410 3411 scroll_to (canvas, xofs, yofs);
Function call argument is an uninitialized value
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

3412 3413 canvas->need_repick = TRUE; 3414 3415 if (!(canvas->root->flags & EEL_CANVAS_ITEM_NEED_DEEP_UPDATE)) { 3416 canvas->root->flags |= EEL_CANVAS_ITEM_NEED_DEEP_UPDATE; 3417 eel_canvas_request_update (canvas); 3418 } 3419 } 3420 3421 3422 /** 3423 * eel_canvas_get_scroll_region: 3424 * @canvas: A canvas. 3425 * @x1: Leftmost limit of the scrolling region (return value). 3426 * @y1: Upper limit of the scrolling region (return value). 3427 * @x2: Rightmost limit of the scrolling region (return value). 3428 * @y2: Lower limit of the scrolling region (return value). 3429 * 3430 * Queries the scrolling region of a canvas. 3431 **/ 3432 void 3433 eel_canvas_get_scroll_region (EelCanvas *canvas, double *x1, double *y1, double *x2, double *y2) 3434 { 3435 g_return_if_fail (EEL_IS_CANVAS (canvas)); 3436 3437 if (x1) 3438 *x1 = canvas->scroll_x1; 3439 3440 if (y1) 3441 *y1 = canvas->scroll_y1; 3442 3443 if (x2) 3444 *x2 = canvas->scroll_x2; 3445 3446 if (y2) 3447 *y2 = canvas->scroll_y2; 3448 } 3449 3450 void 3451 eel_canvas_set_center_scroll_region (EelCanvas *canvas, 3452 gboolean center_scroll_region) 3453 { 3454 GtkAdjustment *vadjustment, *hadjustment; 3455 3456 g_return_if_fail (EEL_IS_CANVAS (canvas)); 3457 3458 canvas->center_scroll_region = center_scroll_region != 0; 3459 3460 hadjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (&canvas->layout)); 3461 vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (&canvas->layout)); 3462 3463 scroll_to (canvas, 3464 gtk_adjustment_get_value (hadjustment), 3465 gtk_adjustment_get_value (vadjustment)); 3466 } 3467 3468 3469 /** 3470 * eel_canvas_set_pixels_per_unit: 3471 * @canvas: A canvas. 3472 * @n: The number of pixels that correspond to one canvas unit. 3473 * 3474 * Sets the zooming factor of a canvas by specifying the number of pixels that 3475 * correspond to one canvas unit. 3476 **/ 3477 void 3478 eel_canvas_set_pixels_per_unit (EelCanvas *canvas, double n) 3479 { 3480 GtkWidget *widget; 3481 double cx, cy; 3482 int x1, y1; 3483 int center_x, center_y; 3484 GdkWindow *window; 3485 GdkWindowAttr attributes; 3486 gint attributes_mask; 3487 GtkAllocation allocation; 3488 GtkAdjustment *vadjustment, *hadjustment; 3489 3490 g_return_if_fail (EEL_IS_CANVAS (canvas)); 3491 g_return_if_fail (n > EEL_CANVAS_EPSILON); 3492 3493 widget = GTK_WIDGET (canvas); 3494 3495 gtk_widget_get_allocation (widget, &allocation); 3496 center_x = allocation.width / 2; 3497 center_y = allocation.height / 2; 3498 3499 /* Find the coordinates of the screen center in units. */ 3500 hadjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (canvas)); 3501 vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (canvas)); 3502 cx = (gtk_adjustment_get_value (hadjustment) + center_x) / canvas->pixels_per_unit + canvas->scroll_x1 + canvas->zoom_xofs; 3503 cy = (gtk_adjustment_get_value (vadjustment) + center_y) / canvas->pixels_per_unit + canvas->scroll_y1 + canvas->zoom_yofs; 3504 3505 /* Now calculate the new offset of the upper left corner. (round not truncate) */ 3506 x1 = ((cx - canvas->scroll_x1) * n) - center_x + .5; 3507 y1 = ((cy - canvas->scroll_y1) * n) - center_y + .5; 3508 3509 canvas->pixels_per_unit = n; 3510 3511 if (!(canvas->root->flags & EEL_CANVAS_ITEM_NEED_DEEP_UPDATE)) { 3512 canvas->root->flags |= EEL_CANVAS_ITEM_NEED_DEEP_UPDATE; 3513 eel_canvas_request_update (canvas); 3514 } 3515 3516 /* Map a background None window over the bin_window to avoid 3517 * scrolling the window scroll causing exposes. 3518 */ 3519 window = NULL; 3520 if (gtk_widget_get_mapped (widget)) { 3521 GtkAllocation allocation; 3522 attributes.window_type = GDK_WINDOW_CHILD; 3523 gtk_widget_get_allocation (widget, &allocation); 3524 attributes.x = allocation.x; 3525 attributes.y = allocation.y; 3526 attributes.width = allocation.width; 3527 attributes.height = allocation.height; 3528 attributes.wclass = GDK_INPUT_OUTPUT; 3529 attributes.visual = gtk_widget_get_visual (widget); 3530 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK; 3531 3532 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL; 3533 3534 window = gdk_window_new (gtk_widget_get_parent_window (widget), 3535 &attributes, attributes_mask); 3536 gdk_window_set_user_data (window, widget); 3537 3538 gdk_window_show (window); 3539 } 3540 3541 scroll_to (canvas, x1, y1); 3542 3543 /* If we created a an overlapping background None window, remove it how. 3544 * 3545 * TODO: We would like to temporarily set the bin_window background to 3546 * None to avoid clearing the bin_window to the background, but gdk doesn't 3547 * expose enought to let us do this, so we get a flash-effect here. At least 3548 * it looks better than scroll + expose. 3549 */ 3550 if (window != NULL) { 3551 gdk_window_hide (window); 3552 gdk_window_set_user_data (window, NULL); 3553 gdk_window_destroy (window); 3554 } 3555 3556 canvas->need_repick = TRUE; 3557 } 3558 3559 /** 3560 * eel_canvas_scroll_to: 3561 * @canvas: A canvas. 3562 * @cx: Horizontal scrolling offset in canvas pixel units. 3563 * @cy: Vertical scrolling offset in canvas pixel units. 3564 * 3565 * Makes a canvas scroll to the specified offsets, given in canvas pixel units. 3566 * The canvas will adjust the view so that it is not outside the scrolling 3567 * region. This function is typically not used, as it is better to hook 3568 * scrollbars to the canvas layout's scrolling adjusments. 3569 **/ 3570 void 3571 eel_canvas_scroll_to (EelCanvas *canvas, int cx, int cy) 3572 { 3573 g_return_if_fail (EEL_IS_CANVAS (canvas)); 3574 3575 scroll_to (canvas, cx, cy); 3576 } 3577 3578 /** 3579 * eel_canvas_get_scroll_offsets: 3580 * @canvas: A canvas. 3581 * @cx: Horizontal scrolling offset (return value). 3582 * @cy: Vertical scrolling offset (return value). 3583 * 3584 * Queries the scrolling offsets of a canvas. The values are returned in canvas 3585 * pixel units. 3586 **/ 3587 void 3588 eel_canvas_get_scroll_offsets (EelCanvas *canvas, int *cx, int *cy) 3589 { 3590 GtkAdjustment *vadjustment, *hadjustment; 3591 3592 g_return_if_fail (EEL_IS_CANVAS (canvas)); 3593 3594 hadjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (canvas)); 3595 vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (canvas)); 3596 3597 if (cx) 3598 *cx = gtk_adjustment_get_value (hadjustment); 3599 3600 if (cy) 3601 *cy = gtk_adjustment_get_value (vadjustment); 3602 } 3603 3604 /** 3605 * eel_canvas_update_now: 3606 * @canvas: A canvas. 3607 * 3608 * Forces an immediate update and redraw of a canvas. If the canvas does not 3609 * have any pending update or redraw requests, then no action is taken. This is 3610 * typically only used by applications that need explicit control of when the 3611 * display is updated, like games. It is not needed by normal applications. 3612 */ 3613 void 3614 eel_canvas_update_now (EelCanvas *canvas) 3615 { 3616 g_return_if_fail (EEL_IS_CANVAS (canvas)); 3617 3618 if (!(canvas->need_update || canvas->need_redraw)) 3619 return; 3620 remove_idle (canvas); 3621 do_update (canvas); 3622 } 3623 3624 /** 3625 * eel_canvas_get_item_at: 3626 * @canvas: A canvas. 3627 * @x: X position in world coordinates. 3628 * @y: Y position in world coordinates. 3629 * 3630 * Looks for the item that is under the specified position, which must be 3631 * specified in world coordinates. 3632 * 3633 * Return value: The sought item, or NULL if no item is at the specified 3634 * coordinates. 3635 **/ 3636 EelCanvasItem * 3637 eel_canvas_get_item_at (EelCanvas *canvas, double x, double y) 3638 { 3639 EelCanvasItem *item; 3640 double dist; 3641 int cx, cy; 3642 3643 g_return_val_if_fail (EEL_IS_CANVAS (canvas), NULL); 3644 3645 eel_canvas_w2c (canvas, x, y, &cx, &cy); 3646 3647 dist = eel_canvas_item_invoke_point (canvas->root, x, y, cx, cy, &item);
Function call argument is an uninitialized value
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

3648 if ((int) (dist * canvas->pixels_per_unit + 0.5) <= canvas->close_enough) 3649 return item;
Undefined or garbage value returned to caller
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

3650 else 3651 return NULL; 3652 } 3653 3654 /* Queues an update of the canvas */ 3655 static void 3656 eel_canvas_request_update (EelCanvas *canvas) 3657 { 3658 EEL_CANVAS_GET_CLASS (canvas)->request_update (canvas); 3659 } 3660 3661 static void 3662 eel_canvas_request_update_real (EelCanvas *canvas) 3663 { 3664 canvas->need_update = TRUE; 3665 add_idle (canvas); 3666 } 3667 3668 /** 3669 * eel_canvas_request_redraw: 3670 * @canvas: A canvas. 3671 * @x1: Leftmost coordinate of the rectangle to be redrawn. 3672 * @y1: Upper coordinate of the rectangle to be redrawn. 3673 * @x2: Rightmost coordinate of the rectangle to be redrawn, plus 1. 3674 * @y2: Lower coordinate of the rectangle to be redrawn, plus 1. 3675 * 3676 * Convenience function that informs a canvas that the specified rectangle needs 3677 * to be repainted. The rectangle includes @x1 and @y1, but not @x2 and @y2. 3678 * To be used only by item implementations. 3679 **/ 3680 void 3681 eel_canvas_request_redraw (EelCanvas *canvas, int x1, int y1, int x2, int y2) 3682 { 3683 GdkRectangle bbox; 3684 3685 g_return_if_fail (EEL_IS_CANVAS (canvas)); 3686 3687 if (!gtk_widget_is_drawable (GTK_WIDGET (canvas)) 3688 || (x1 >= x2) || (y1 >= y2)) return; 3689 3690 bbox.x = x1; 3691 bbox.y = y1; 3692 bbox.width = x2 - x1; 3693 bbox.height = y2 - y1; 3694 3695 gdk_window_invalidate_rect (gtk_layout_get_bin_window (GTK_LAYOUT (canvas)), 3696 &bbox, FALSE); 3697 } 3698 3699 /** 3700 * eel_canvas_w2c: 3701 * @canvas: A canvas. 3702 * @wx: World X coordinate. 3703 * @wy: World Y coordinate. 3704 * @cx: X pixel coordinate (return value). 3705 * @cy: Y pixel coordinate (return value). 3706 * 3707 * Converts world coordinates into canvas pixel coordinates. 3708 **/ 3709 void 3710 eel_canvas_w2c (EelCanvas *canvas, double wx, double wy, int *cx, int *cy) 3711 { 3712 double zoom; 3713 3714 g_return_if_fail (EEL_IS_CANVAS (canvas)); 3715 3716 zoom = canvas->pixels_per_unit; 3717 3718 if (cx) 3719 *cx = floor ((wx - canvas->scroll_x1)*zoom + canvas->zoom_xofs + 0.5); 3720 if (cy) 3721 *cy = floor ((wy - canvas->scroll_y1)*zoom + canvas->zoom_yofs + 0.5); 3722 } 3723 3724 /** 3725 * eel_canvas_w2c: 3726 * @canvas: A canvas. 3727 * @world: rectangle in world coordinates. 3728 * @canvas: rectangle in canvase coordinates. 3729 * 3730 * Converts rectangles in world coordinates into canvas pixel coordinates. 3731 **/ 3732 void 3733 eel_canvas_w2c_rect_d (EelCanvas *canvas, 3734 double *x1, double *y1, 3735 double *x2, double *y2) 3736 { 3737 eel_canvas_w2c_d (canvas,
Function call argument is an uninitialized value
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

3738 *x1, *y1, 3739 x1, y1); 3740 eel_canvas_w2c_d (canvas, 3741 *x2, *y2, 3742 x2, y2); 3743 } 3744 3745 3746 3747 /** 3748 * eel_canvas_w2c_d: 3749 * @canvas: A canvas. 3750 * @wx: World X coordinate. 3751 * @wy: World Y coordinate. 3752 * @cx: X pixel coordinate (return value). 3753 * @cy: Y pixel coordinate (return value). 3754 * 3755 * Converts world coordinates into canvas pixel coordinates. This version 3756 * produces coordinates in floating point coordinates, for greater precision. 3757 **/ 3758 void 3759 eel_canvas_w2c_d (EelCanvas *canvas, double wx, double wy, double *cx, double *cy) 3760 { 3761 double zoom; 3762 3763 g_return_if_fail (EEL_IS_CANVAS (canvas)); 3764 3765 zoom = canvas->pixels_per_unit; 3766 3767 if (cx) 3768 *cx = (wx - canvas->scroll_x1)*zoom + canvas->zoom_xofs; 3769 if (cy) 3770 *cy = (wy - canvas->scroll_y1)*zoom + canvas->zoom_yofs; 3771 } 3772 3773 3774 /** 3775 * eel_canvas_c2w: 3776 * @canvas: A canvas. 3777 * @cx: Canvas pixel X coordinate. 3778 * @cy: Canvas pixel Y coordinate. 3779 * @wx: X world coordinate (return value). 3780 * @wy: Y world coordinate (return value). 3781 * 3782 * Converts canvas pixel coordinates to world coordinates. 3783 **/ 3784 void 3785 eel_canvas_c2w (EelCanvas *canvas, int cx, int cy, double *wx, double *wy) 3786 { 3787 double zoom; 3788 3789 g_return_if_fail (EEL_IS_CANVAS (canvas)); 3790 3791 zoom = canvas->pixels_per_unit; 3792 3793 if (wx) 3794 *wx = (cx - canvas->zoom_xofs)/zoom + canvas->scroll_x1; 3795 if (wy) 3796 *wy = (cy - canvas->zoom_yofs)/zoom + canvas->scroll_y1; 3797 } 3798 3799 3800 /** 3801 * eel_canvas_window_to_world: 3802 * @canvas: A canvas. 3803 * @winx: Window-relative X coordinate. 3804 * @winy: Window-relative Y coordinate. 3805 * @worldx: X world coordinate (return value). 3806 * @worldy: Y world coordinate (return value). 3807 * 3808 * Converts window-relative coordinates into world coordinates. You can use 3809 * this when you need to convert mouse coordinates into world coordinates, for 3810 * example. 3811 * Window coordinates are really the same as canvas coordinates now, but this 3812 * function is here for backwards compatibility reasons. 3813 **/ 3814 void 3815 eel_canvas_window_to_world (EelCanvas *canvas, double winx, double winy, 3816 double *worldx, double *worldy) 3817 { 3818 g_return_if_fail (EEL_IS_CANVAS (canvas)); 3819 3820 if (worldx) 3821 *worldx = canvas->scroll_x1 + ((winx - canvas->zoom_xofs) 3822 / canvas->pixels_per_unit); 3823 3824 if (worldy) 3825 *worldy = canvas->scroll_y1 + ((winy - canvas->zoom_yofs) 3826 / canvas->pixels_per_unit); 3827 } 3828 3829 3830 /** 3831 * eel_canvas_world_to_window: 3832 * @canvas: A canvas. 3833 * @worldx: World X coordinate. 3834 * @worldy: World Y coordinate. 3835 * @winx: X window-relative coordinate. 3836 * @winy: Y window-relative coordinate. 3837 * 3838 * Converts world coordinates into window-relative coordinates. 3839 * Window coordinates are really the same as canvas coordinates now, but this 3840 * function is here for backwards compatibility reasons. 3841 **/ 3842 void 3843 eel_canvas_world_to_window (EelCanvas *canvas, double worldx, double worldy, 3844 double *winx, double *winy) 3845 { 3846 g_return_if_fail (EEL_IS_CANVAS (canvas)); 3847 3848 if (winx) 3849 *winx = (canvas->pixels_per_unit)*(worldx - canvas->scroll_x1) + canvas->zoom_xofs; 3850 3851 if (winy) 3852 *winy = (canvas->pixels_per_unit)*(worldy - canvas->scroll_y1) + canvas->zoom_yofs; 3853 } 3854 3855 static gboolean 3856 boolean_handled_accumulator (GSignalInvocationHint *ihint, 3857 GValue *return_accu, 3858 const GValue *handler_return, 3859 gpointer dummy) 3860 { 3861 gboolean continue_emission; 3862 gboolean signal_handled; 3863 3864 signal_handled = g_value_get_boolean (handler_return); 3865 g_value_set_boolean (return_accu, signal_handled); 3866 continue_emission = !signal_handled; 3867 3868 return continue_emission; 3869 } 3870 3871 static guint 3872 eel_canvas_item_accessible_add_focus_handler (AtkComponent *component, 3873 AtkFocusHandler handler) 3874 { 3875 GSignalMatchType match_type; 3876 guint signal_id; 3877 3878 match_type = G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC; 3879 signal_id = g_signal_lookup ("focus-event", ATK_TYPE_OBJECT); 3880 3881 if (!g_signal_handler_find (component, match_type, signal_id, 0, NULL, 3882 (gpointer) handler, NULL)) { 3883 return g_signal_connect_closure_by_id (component, 3884 signal_id, 0, 3885 g_cclosure_new ( 3886 G_CALLBACK (handler), NULL, 3887 (GClosureNotify) NULL), 3888 FALSE); 3889 } 3890 return 0; 3891 } 3892 3893 static void 3894 eel_canvas_item_accessible_get_item_extents (EelCanvasItem *item, 3895 GdkRectangle *rect) 3896 { 3897 double bx1, bx2, by1, by2; 3898 gint scroll_x, scroll_y; 3899 gint x1, x2, y1, y2; 3900 3901 eel_canvas_item_get_bounds (item, &bx1, &by1, &bx2, &by2); 3902 eel_canvas_w2c_rect_d (item->canvas, &bx1, &by1, &bx2, &by2); 3903 eel_canvas_get_scroll_offsets (item->canvas, &scroll_x, &scroll_y); 3904 x1 = floor (bx1 + .5); 3905 y1 = floor (by1 + .5); 3906 x2 = floor (bx2 + .5); 3907 y2 = floor (by2 + .5); 3908 rect->x = x1 - scroll_x;
The right operand of '-' is a garbage value
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

3909 rect->y = y1 - scroll_y; 3910 rect->width = x2 - x1; 3911 rect->height = y2 - y1; 3912 } 3913 3914 static gboolean 3915 eel_canvas_item_accessible_is_item_in_window (EelCanvasItem *item, 3916 GdkRectangle *rect) 3917 { 3918 GtkWidget *widget; 3919 gboolean retval; 3920 3921 widget = GTK_WIDGET (item->canvas); 3922 if (gtk_widget_get_window (widget)) { 3923 int window_width, window_height; 3924 3925 gdk_window_get_geometry (gtk_widget_get_window (widget), NULL, NULL, 3926 &window_width, &window_height); 3927 /* 3928 * Check whether rectangles intersect 3929 */ 3930 if (rect->x + rect->width < 0 || 3931 rect->y + rect->height < 0 || 3932 rect->x > window_width || 3933 rect->y > window_height) { 3934 retval = FALSE; 3935 } else { 3936 retval = TRUE; 3937 } 3938 } else { 3939 retval = FALSE; 3940 } 3941 return retval; 3942 } 3943 3944 3945 static void 3946 eel_canvas_item_accessible_get_extents (AtkComponent *component, 3947 gint *x, 3948 gint *y, 3949 gint *width, 3950 gint *height, 3951 AtkCoordType coord_type) 3952 { 3953 AtkGObjectAccessible *atk_gobj; 3954 GObject *obj; 3955 EelCanvasItem *item; 3956 gint window_x, window_y; 3957 gint toplevel_x, toplevel_y; 3958 GdkRectangle rect; 3959 GdkWindow *window; 3960 GtkWidget *canvas; 3961 3962 atk_gobj = ATK_GOBJECT_ACCESSIBLE (component); 3963 obj = atk_gobject_accessible_get_object (atk_gobj); 3964 3965 if (obj == NULL) { 3966 /* item is defunct */ 3967 return; 3968 } 3969 3970 /* Get the CanvasItem */ 3971 item = EEL_CANVAS_ITEM (obj); 3972 3973 /* If this item has no parent canvas, something's broken */ 3974 g_return_if_fail (GTK_IS_WIDGET (item->canvas)); 3975 3976 eel_canvas_item_accessible_get_item_extents (item, &rect); 3977 *width = rect.width; 3978 *height = rect.height; 3979 if (!eel_canvas_item_accessible_is_item_in_window (item, &rect)) { 3980 *x = G_MININT; 3981 *y = G_MININT; 3982 return; 3983 } 3984 3985 canvas = GTK_WIDGET (item->canvas); 3986 window = gtk_widget_get_parent_window (canvas); 3987 gdk_window_get_origin (window, &window_x, &window_y); 3988 *x = rect.x + window_x; 3989 *y = rect.y + window_y; 3990 if (coord_type == ATK_XY_WINDOW) { 3991 window = gdk_window_get_toplevel (gtk_widget_get_window (canvas)); 3992 gdk_window_get_origin (window, &toplevel_x, &toplevel_y); 3993 *x -= toplevel_x; 3994 *y -= toplevel_y; 3995 } 3996 return; 3997 } 3998 3999 static gint 4000 eel_canvas_item_accessible_get_mdi_zorder (AtkComponent *component) 4001 { 4002 AtkGObjectAccessible *atk_gobj; 4003 GObject *g_obj; 4004 EelCanvasItem *item; 4005 4006 atk_gobj = ATK_GOBJECT_ACCESSIBLE (component); 4007 g_obj = atk_gobject_accessible_get_object (atk_gobj); 4008 if (g_obj == NULL) { 4009 /* Object is defunct */ 4010 return -1; 4011 } 4012 4013 item = EEL_CANVAS_ITEM (g_obj); 4014 if (item->parent) { 4015 return g_list_index (EEL_CANVAS_GROUP (item->parent)->item_list, item); 4016 } else { 4017 g_return_val_if_fail (item->canvas->root == item, -1); 4018 return 0; 4019 } 4020 } 4021 4022 static gboolean 4023 eel_canvas_item_accessible_grab_focus (AtkComponent *component) 4024 { 4025 AtkGObjectAccessible *atk_gobj; 4026 GObject *obj; 4027 EelCanvasItem *item; 4028 GtkWidget *toplevel; 4029 4030 atk_gobj = ATK_GOBJECT_ACCESSIBLE (component); 4031 obj = atk_gobject_accessible_get_object (atk_gobj); 4032 4033 item = EEL_CANVAS_ITEM (obj); 4034 if (item == NULL) { 4035 /* item is defunct */ 4036 return FALSE; 4037 } 4038 4039 eel_canvas_item_grab_focus (item); 4040 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (item->canvas)); 4041 if (gtk_widget_is_toplevel (toplevel)) { 4042 gtk_window_present (GTK_WINDOW (toplevel)); 4043 } 4044 4045 return TRUE; 4046 } 4047 4048 static void 4049 eel_canvas_item_accessible_remove_focus_handler (AtkComponent *component, 4050 guint handler_id) 4051 { 4052 g_signal_handler_disconnect (component, handler_id); 4053 } 4054 4055 static void 4056 eel_canvas_item_accessible_component_interface_init (AtkComponentIface *iface) 4057 { 4058 g_return_if_fail (iface != NULL); 4059 4060 iface->add_focus_handler = eel_canvas_item_accessible_add_focus_handler; 4061 iface->get_extents = eel_canvas_item_accessible_get_extents; 4062 iface->get_mdi_zorder = eel_canvas_item_accessible_get_mdi_zorder; 4063 iface->grab_focus = eel_canvas_item_accessible_grab_focus; 4064 iface->remove_focus_handler = eel_canvas_item_accessible_remove_focus_handler; 4065 } 4066 4067 static gboolean 4068 eel_canvas_item_accessible_is_item_on_screen (EelCanvasItem *item) 4069 { 4070 GdkRectangle rect; 4071 4072 eel_canvas_item_accessible_get_item_extents (item, &rect); 4073 return eel_canvas_item_accessible_is_item_in_window (item, &rect); 4074 } 4075 4076 static void 4077 eel_canvas_item_accessible_initialize (AtkObject *obj, gpointer data) 4078 { 4079 if (ATK_OBJECT_CLASS (accessible_item_parent_class)->initialize != NULL) 4080 ATK_OBJECT_CLASS (accessible_item_parent_class)->initialize (obj, data); 4081 g_object_set_data (G_OBJECT (obj), "atk-component-layer", 4082 GINT_TO_POINTER (ATK_LAYER_MDI)); 4083 } 4084 4085 static AtkStateSet* 4086 eel_canvas_item_accessible_ref_state_set (AtkObject *accessible) 4087 { 4088 AtkGObjectAccessible *atk_gobj; 4089 GObject *obj; 4090 EelCanvasItem *item; 4091 AtkStateSet *state_set; 4092 4093 state_set = ATK_OBJECT_CLASS (accessible_item_parent_class)->ref_state_set (accessible); 4094 atk_gobj = ATK_GOBJECT_ACCESSIBLE (accessible); 4095 obj = atk_gobject_accessible_get_object (atk_gobj); 4096 4097 item = EEL_CANVAS_ITEM (obj); 4098 if (item == NULL) { 4099 atk_state_set_add_state (state_set, ATK_STATE_DEFUNCT); 4100 } else { 4101 if (item->flags & EEL_CANVAS_ITEM_VISIBLE) { 4102 atk_state_set_add_state (state_set, ATK_STATE_VISIBLE); 4103 4104 if (eel_canvas_item_accessible_is_item_on_screen (item)) { 4105 atk_state_set_add_state (state_set, ATK_STATE_SHOWING); 4106 } 4107 } 4108 if (gtk_widget_get_can_focus (GTK_WIDGET (item->canvas))) { 4109 atk_state_set_add_state (state_set, ATK_STATE_FOCUSABLE); 4110 4111 if (item->canvas->focused_item == item) { 4112 atk_state_set_add_state (state_set, ATK_STATE_FOCUSED); 4113 } 4114 } 4115 } 4116 4117 return state_set; 4118 } 4119 4120 static void 4121 eel_canvas_item_accessible_class_init (EelCanvasItemAccessibleClass *klass) 4122 { 4123 AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass); 4124 4125 accessible_item_parent_class = g_type_class_peek_parent (klass); 4126 4127 atk_class->initialize = eel_canvas_item_accessible_initialize; 4128 atk_class->ref_state_set = eel_canvas_item_accessible_ref_state_set; 4129 } 4130 4131 static void 4132 eel_canvas_item_accessible_init (EelCanvasItemAccessible *self) 4133 { 4134 4135 } 4136 4137 G_DEFINE_TYPE_WITH_CODE (EelCanvasItemAccessible, 4138 eel_canvas_item_accessible, 4139 ATK_TYPE_GOBJECT_ACCESSIBLE, 4140 G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT, 4141 eel_canvas_item_accessible_component_interface_init)); 4142 4143 static GType eel_canvas_item_accessible_factory_get_type (void); 4144 4145 typedef AtkObjectFactory EelCanvasItemAccessibleFactory; 4146 typedef AtkObjectFactoryClass EelCanvasItemAccessibleFactoryClass; 4147 G_DEFINE_TYPE (EelCanvasItemAccessibleFactory, eel_canvas_item_accessible_factory, 4148 ATK_TYPE_OBJECT_FACTORY) 4149 4150 static GType 4151 eel_canvas_item_accessible_factory_get_accessible_type (void) 4152 { 4153 return eel_canvas_item_accessible_get_type (); 4154 } 4155 4156 static AtkObject* 4157 eel_canvas_item_accessible_factory_create_accessible (GObject *for_object) 4158 { 4159 AtkObject *accessible; 4160 4161 accessible = g_object_new (eel_canvas_item_accessible_get_type (), NULL); 4162 atk_object_initialize (accessible, for_object); 4163 return accessible; 4164 } 4165 4166 static void 4167 eel_canvas_item_accessible_factory_init (EelCanvasItemAccessibleFactory *self) 4168 { 4169 4170 } 4171 4172 static void 4173 eel_canvas_item_accessible_factory_class_init (AtkObjectFactoryClass *klass) 4174 { 4175 klass->create_accessible = eel_canvas_item_accessible_factory_create_accessible; 4176 klass->get_accessible_type = eel_canvas_item_accessible_factory_get_accessible_type; 4177 } 4178 4179 /* Class initialization function for EelCanvasItemClass */ 4180 static void 4181 eel_canvas_item_class_init (EelCanvasItemClass *klass) 4182 { 4183 GObjectClass *gobject_class = (GObjectClass *) klass; 4184 4185 item_parent_class = g_type_class_peek_parent (klass); 4186 4187 gobject_class->set_property = eel_canvas_item_set_property; 4188 gobject_class->get_property = eel_canvas_item_get_property; 4189 gobject_class->dispose = eel_canvas_item_dispose; 4190 4191 g_object_class_install_property 4192 (gobject_class, ITEM_PROP_PARENT, 4193 g_param_spec_object ("parent", NULL, NULL, 4194 EEL_TYPE_CANVAS_ITEM, 4195 G_PARAM_READWRITE)); 4196 4197 g_object_class_install_property 4198 (gobject_class, ITEM_PROP_VISIBLE, 4199 g_param_spec_boolean ("visible", NULL, NULL, 4200 TRUE, 4201 G_PARAM_READWRITE)); 4202 4203 item_signals[ITEM_EVENT] = 4204 g_signal_new ("event", 4205 G_TYPE_FROM_CLASS (klass), 4206 G_SIGNAL_RUN_LAST, 4207 G_STRUCT_OFFSET (EelCanvasItemClass, event), 4208 boolean_handled_accumulator, NULL, 4209 g_cclosure_marshal_generic, 4210 G_TYPE_BOOLEAN, 1, 4211 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); 4212 4213 item_signals[ITEM_DESTROY] = 4214 g_signal_new ("destroy", 4215 G_TYPE_FROM_CLASS (klass), 4216 G_SIGNAL_RUN_CLEANUP | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 4217 G_STRUCT_OFFSET (EelCanvasItemClass, destroy), 4218 NULL, NULL, 4219 g_cclosure_marshal_VOID__VOID, 4220 G_TYPE_NONE, 0); 4221 4222 klass->realize = eel_canvas_item_realize; 4223 klass->unrealize = eel_canvas_item_unrealize; 4224 klass->map = eel_canvas_item_map; 4225 klass->unmap = eel_canvas_item_unmap; 4226 klass->update = eel_canvas_item_update; 4227 4228 atk_registry_set_factory_type (atk_get_default_registry (), 4229 EEL_TYPE_CANVAS_ITEM, 4230 eel_canvas_item_accessible_factory_get_type ()); 4231 }