1 /*
2  * GooCanvas. Copyright (C) 2005 Damon Chaplin.
3  * Released under the GNU LGPL license. See COPYING for details.
4  *
5  * goocanvasitem.c - interface for canvas items & groups.
6  */
7 
8 /**
9  * SECTION:goocanvasitem
10  * @Title: GooCanvasItem
11  * @Short_Description: the interface for canvas items.
12  *
13  * #GooCanvasItem defines the interface that canvas items must implement,
14  * and contains methods for operating on canvas items.
15  */
16 #include <config.h>
17 #include <math.h>
18 #include <glib/gi18n-lib.h>
19 #include <gobject/gobjectnotifyqueue.c>
20 #include <gobject/gvaluecollector.h>
21 #include <gtk/gtk.h>
22 #include "goocanvasprivate.h"
23 #include "goocanvasitem.h"
24 #include "goocanvas.h"
25 #include "goocanvasutils.h"
26 #include "goocanvasmarshal.h"
27 
28 
29 static GParamSpecPool       *_goo_canvas_item_child_property_pool = NULL;
30 static GObjectNotifyContext *_goo_canvas_item_child_property_notify_context = NULL;
31 static const char *animation_key = "GooCanvasItemAnimation";
32 
33 enum {
34   /* Mouse events. */
35   ENTER_NOTIFY_EVENT,
36   LEAVE_NOTIFY_EVENT,
37   MOTION_NOTIFY_EVENT,
38   BUTTON_PRESS_EVENT,
39   BUTTON_RELEASE_EVENT,
40 
41   /* Keyboard events. */
42   FOCUS_IN_EVENT,
43   FOCUS_OUT_EVENT,
44   KEY_PRESS_EVENT,
45   KEY_RELEASE_EVENT,
46 
47   /* Miscellaneous signals. */
48   GRAB_BROKEN_EVENT,
49   CHILD_NOTIFY,
50   ANIMATION_FINISHED,
51   SCROLL_EVENT,
52   QUERY_TOOLTIP,
53 
54   LAST_SIGNAL
55 };
56 
57 static guint canvas_item_signals[LAST_SIGNAL] = { 0 };
58 
59 static void goo_canvas_item_base_init (gpointer g_class);
60 extern void _goo_canvas_style_init (void);
61 
62 
63 GType
goo_canvas_item_get_type(void)64 goo_canvas_item_get_type (void)
65 {
66   static GType canvas_item_type = 0;
67 
68   if (!canvas_item_type)
69     {
70       static const GTypeInfo canvas_item_info =
71       {
72         sizeof (GooCanvasItemIface), /* class_size */
73 	goo_canvas_item_base_init,   /* base_init */
74 	NULL,			     /* base_finalize */
75       };
76 
77       canvas_item_type = g_type_register_static (G_TYPE_INTERFACE,
78 						 "GooCanvasItem",
79 						 &canvas_item_info, 0);
80 
81       g_type_interface_add_prerequisite (canvas_item_type, G_TYPE_OBJECT);
82     }
83 
84   return canvas_item_type;
85 }
86 
87 
88 static void
child_property_notify_dispatcher(GObject * object,guint n_pspecs,GParamSpec ** pspecs)89 child_property_notify_dispatcher (GObject     *object,
90 				  guint        n_pspecs,
91 				  GParamSpec **pspecs)
92 {
93   guint i;
94 
95   for (i = 0; i < n_pspecs; i++)
96     g_signal_emit (object, canvas_item_signals[CHILD_NOTIFY],
97 		   g_quark_from_string (pspecs[i]->name), pspecs[i]);
98 }
99 
100 
101 static void
goo_canvas_item_base_init(gpointer g_iface)102 goo_canvas_item_base_init (gpointer g_iface)
103 {
104   static GObjectNotifyContext cpn_context = { 0, NULL, NULL };
105   static gboolean initialized = FALSE;
106 
107   if (!initialized)
108     {
109       GType iface_type = G_TYPE_FROM_INTERFACE (g_iface);
110 
111       _goo_canvas_item_child_property_pool = g_param_spec_pool_new (TRUE);
112 
113       cpn_context.quark_notify_queue = g_quark_from_static_string ("GooCanvasItem-child-property-notify-queue");
114       cpn_context.dispatcher = child_property_notify_dispatcher;
115       _goo_canvas_item_child_property_notify_context = &cpn_context;
116 
117       /* Mouse events. */
118 
119       /**
120        * GooCanvasItem::enter-notify-event
121        * @item: the item that received the signal.
122        * @target_item: the target of the event.
123        * @event: the event data. The x & y fields contain the mouse position
124        *  in the item's coordinate space. The x_root & y_root fields contain
125        *  the same coordinates converted to the canvas coordinate space.
126        *
127        * Emitted when the mouse enters an item.
128        *
129        * Returns: %TRUE to stop the signal emission, or %FALSE to let it
130        *  continue.
131        */
132       canvas_item_signals[ENTER_NOTIFY_EVENT] =
133 	g_signal_new ("enter_notify_event",
134 		      iface_type,
135 		      G_SIGNAL_RUN_LAST,
136 		      G_STRUCT_OFFSET (GooCanvasItemIface,
137 				       enter_notify_event),
138 		      goo_canvas_boolean_handled_accumulator, NULL,
139 		      goo_canvas_marshal_BOOLEAN__OBJECT_BOXED,
140 		      G_TYPE_BOOLEAN, 2,
141 		      GOO_TYPE_CANVAS_ITEM,
142 		      GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
143 
144       /**
145        * GooCanvasItem::leave-notify-event
146        * @item: the item that received the signal.
147        * @target_item: the target of the event.
148        * @event: the event data. The x & y fields contain the mouse position
149        *  in the item's coordinate space. The x_root & y_root fields contain
150        *  the same coordinates converted to the canvas coordinate space.
151        *
152        * Emitted when the mouse leaves an item.
153        *
154        * Returns: %TRUE to stop the signal emission, or %FALSE to let it
155        *  continue.
156        */
157       canvas_item_signals[LEAVE_NOTIFY_EVENT] =
158 	g_signal_new ("leave_notify_event",
159 		      iface_type,
160 		      G_SIGNAL_RUN_LAST,
161 		      G_STRUCT_OFFSET (GooCanvasItemIface,
162 				       leave_notify_event),
163 		      goo_canvas_boolean_handled_accumulator, NULL,
164 		      goo_canvas_marshal_BOOLEAN__OBJECT_BOXED,
165 		      G_TYPE_BOOLEAN, 2,
166 		      GOO_TYPE_CANVAS_ITEM,
167 		      GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
168 
169       /**
170        * GooCanvasItem::motion-notify-event
171        * @item: the item that received the signal.
172        * @target_item: the target of the event.
173        * @event: the event data. The x & y fields contain the mouse position
174        *  in the item's coordinate space. The x_root & y_root fields contain
175        *  the same coordinates converted to the canvas coordinate space.
176        *
177        * Emitted when the mouse moves within an item.
178        *
179        * Returns: %TRUE to stop the signal emission, or %FALSE to let it
180        *  continue.
181        */
182       canvas_item_signals[MOTION_NOTIFY_EVENT] =
183 	g_signal_new ("motion_notify_event",
184 		      iface_type,
185 		      G_SIGNAL_RUN_LAST,
186 		      G_STRUCT_OFFSET (GooCanvasItemIface,
187 				       motion_notify_event),
188 		      goo_canvas_boolean_handled_accumulator, NULL,
189 		      goo_canvas_marshal_BOOLEAN__OBJECT_BOXED,
190 		      G_TYPE_BOOLEAN, 2,
191 		      GOO_TYPE_CANVAS_ITEM,
192 		      GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
193 
194       /**
195        * GooCanvasItem::button-press-event
196        * @item: the item that received the signal.
197        * @target_item: the target of the event.
198        * @event: the event data. The x & y fields contain the mouse position
199        *  in the item's coordinate space. The x_root & y_root fields contain
200        *  the same coordinates converted to the canvas coordinate space.
201        *
202        * Emitted when a mouse button is pressed in an item.
203        *
204        * Returns: %TRUE to stop the signal emission, or %FALSE to let it
205        *  continue.
206        */
207       canvas_item_signals[BUTTON_PRESS_EVENT] =
208 	g_signal_new ("button_press_event",
209 		      iface_type,
210 		      G_SIGNAL_RUN_LAST,
211 		      G_STRUCT_OFFSET (GooCanvasItemIface,
212 				       button_press_event),
213 		      goo_canvas_boolean_handled_accumulator, NULL,
214 		      goo_canvas_marshal_BOOLEAN__OBJECT_BOXED,
215 		      G_TYPE_BOOLEAN, 2,
216 		      GOO_TYPE_CANVAS_ITEM,
217 		      GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
218 
219       /**
220        * GooCanvasItem::button-release-event
221        * @item: the item that received the signal.
222        * @target_item: the target of the event.
223        * @event: the event data. The x & y fields contain the mouse position
224        *  in the item's coordinate space. The x_root & y_root fields contain
225        *  the same coordinates converted to the canvas coordinate space.
226        *
227        * Emitted when a mouse button is released in an item.
228        *
229        * Returns: %TRUE to stop the signal emission, or %FALSE to let it
230        *  continue.
231        */
232       canvas_item_signals[BUTTON_RELEASE_EVENT] =
233 	g_signal_new ("button_release_event",
234 		      iface_type,
235 		      G_SIGNAL_RUN_LAST,
236 		      G_STRUCT_OFFSET (GooCanvasItemIface,
237 				       button_release_event),
238 		      goo_canvas_boolean_handled_accumulator, NULL,
239 		      goo_canvas_marshal_BOOLEAN__OBJECT_BOXED,
240 		      G_TYPE_BOOLEAN, 2,
241 		      GOO_TYPE_CANVAS_ITEM,
242 		      GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
243 
244 
245       /* Keyboard events. */
246 
247       /**
248        * GooCanvasItem::focus-in-event
249        * @item: the item that received the signal.
250        * @target_item: the target of the event.
251        * @event: the event data.
252        *
253        * Emitted when the item receives the keyboard focus.
254        *
255        * Returns: %TRUE to stop the signal emission, or %FALSE to let it
256        *  continue.
257        */
258       canvas_item_signals[FOCUS_IN_EVENT] =
259 	g_signal_new ("focus_in_event",
260 		      iface_type,
261 		      G_SIGNAL_RUN_LAST,
262 		      G_STRUCT_OFFSET (GooCanvasItemIface,
263 				       focus_in_event),
264 		      goo_canvas_boolean_handled_accumulator, NULL,
265 		      goo_canvas_marshal_BOOLEAN__OBJECT_BOXED,
266 		      G_TYPE_BOOLEAN, 2,
267 		      GOO_TYPE_CANVAS_ITEM,
268 		      GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
269 
270       /**
271        * GooCanvasItem::focus-out-event
272        * @item: the item that received the signal.
273        * @target_item: the target of the event.
274        * @event: the event data.
275        *
276        * Emitted when the item loses the keyboard focus.
277        *
278        * Returns: %TRUE to stop the signal emission, or %FALSE to let it
279        *  continue.
280        */
281       canvas_item_signals[FOCUS_OUT_EVENT] =
282 	g_signal_new ("focus_out_event",
283 		      iface_type,
284 		      G_SIGNAL_RUN_LAST,
285 		      G_STRUCT_OFFSET (GooCanvasItemIface,
286 				       focus_out_event),
287 		      goo_canvas_boolean_handled_accumulator, NULL,
288 		      goo_canvas_marshal_BOOLEAN__OBJECT_BOXED,
289 		      G_TYPE_BOOLEAN, 2,
290 		      GOO_TYPE_CANVAS_ITEM,
291 		      GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
292 
293       /**
294        * GooCanvasItem::key-press-event
295        * @item: the item that received the signal.
296        * @target_item: the target of the event.
297        * @event: the event data.
298        *
299        * Emitted when a key is pressed and the item has the keyboard
300        * focus.
301        *
302        * Returns: %TRUE to stop the signal emission, or %FALSE to let it
303        *  continue.
304        */
305       canvas_item_signals[KEY_PRESS_EVENT] =
306 	g_signal_new ("key_press_event",
307 		      iface_type,
308 		      G_SIGNAL_RUN_LAST,
309 		      G_STRUCT_OFFSET (GooCanvasItemIface,
310 				       key_press_event),
311 		      goo_canvas_boolean_handled_accumulator, NULL,
312 		      goo_canvas_marshal_BOOLEAN__OBJECT_BOXED,
313 		      G_TYPE_BOOLEAN, 2,
314 		      GOO_TYPE_CANVAS_ITEM,
315 		      GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
316 
317       /**
318        * GooCanvasItem::key-release-event
319        * @item: the item that received the signal.
320        * @target_item: the target of the event.
321        * @event: the event data.
322        *
323        * Emitted when a key is released and the item has the keyboard
324        * focus.
325        *
326        * Returns: %TRUE to stop the signal emission, or %FALSE to let it
327        *  continue.
328        */
329       canvas_item_signals[KEY_RELEASE_EVENT] =
330 	g_signal_new ("key_release_event",
331 		      iface_type,
332 		      G_SIGNAL_RUN_LAST,
333 		      G_STRUCT_OFFSET (GooCanvasItemIface,
334 				       key_release_event),
335 		      goo_canvas_boolean_handled_accumulator, NULL,
336 		      goo_canvas_marshal_BOOLEAN__OBJECT_BOXED,
337 		      G_TYPE_BOOLEAN, 2,
338 		      GOO_TYPE_CANVAS_ITEM,
339 		      GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
340 
341       /**
342        * GooCanvasItem::query-tooltip:
343        * @item: the item which received the signal.
344        * @x: the x coordinate of the mouse.
345        * @y: the y coordinate of the mouse.
346        * @keyboard_mode: %TRUE if the tooltip was triggered using the keyboard.
347        * @tooltip: a #GtkTooltip.
348        *
349        * Emitted when the mouse has paused over the item for a certain amount
350        * of time, or the tooltip was requested via the keyboard.
351        *
352        * Note that if @keyboard_mode is %TRUE, the values of @x and @y are
353        * undefined and should not be used.
354        *
355        * If the item wants to display a tooltip it should update @tooltip
356        * and return %TRUE.
357        *
358        * Returns: %TRUE if the item has set a tooltip to show.
359        */
360       canvas_item_signals[QUERY_TOOLTIP] =
361 	g_signal_new ("query-tooltip",
362 		      iface_type,
363 		      G_SIGNAL_RUN_LAST,
364 		      G_STRUCT_OFFSET (GooCanvasItemIface, query_tooltip),
365 		      goo_canvas_boolean_handled_accumulator, NULL,
366 		      goo_canvas_marshal_BOOLEAN__DOUBLE_DOUBLE_BOOLEAN_OBJECT,
367 		      G_TYPE_BOOLEAN, 4,
368 		      G_TYPE_DOUBLE,
369 		      G_TYPE_DOUBLE,
370 		      G_TYPE_BOOLEAN,
371 		      GTK_TYPE_TOOLTIP);
372 
373       /**
374        * GooCanvasItem::grab-broken-event
375        * @item: the item that received the signal.
376        * @target_item: the target of the event.
377        * @event: the event data.
378        *
379        * Emitted when the item's keyboard or pointer grab was lost
380        * unexpectedly.
381        *
382        * Returns: %TRUE to stop the signal emission, or %FALSE to let it
383        *  continue.
384        */
385       canvas_item_signals[GRAB_BROKEN_EVENT] =
386 	g_signal_new ("grab_broken_event",
387 		      iface_type,
388 		      G_SIGNAL_RUN_LAST,
389 		      G_STRUCT_OFFSET (GooCanvasItemIface,
390 				       grab_broken_event),
391 		      goo_canvas_boolean_handled_accumulator, NULL,
392 		      goo_canvas_marshal_BOOLEAN__OBJECT_BOXED,
393 		      G_TYPE_BOOLEAN, 2,
394 		      GOO_TYPE_CANVAS_ITEM,
395 		      GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
396 
397       /**
398        * GooCanvasItem::child-notify
399        * @item: the item that received the signal.
400        * @pspec: the #GParamSpec of the changed child property.
401        *
402        * Emitted for each child property that has changed.
403        * The signal's detail holds the property name.
404        */
405       canvas_item_signals[CHILD_NOTIFY] =
406 	g_signal_new ("child_notify",
407 		      iface_type,
408 		      G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_DETAILED | G_SIGNAL_NO_HOOKS,
409 		      G_STRUCT_OFFSET (GooCanvasItemIface, child_notify),
410 		      NULL, NULL,
411 		      g_cclosure_marshal_VOID__PARAM,
412 		      G_TYPE_NONE, 1,
413 		      G_TYPE_PARAM);
414 
415       /**
416        * GooCanvasItem::animation-finished
417        * @item: the item that received the signal.
418        * @stopped: if the animation was explicitly stopped.
419        *
420        * Emitted when the item animation has finished.
421        */
422       canvas_item_signals[ANIMATION_FINISHED] =
423 	g_signal_new ("animation-finished",
424 		      iface_type,
425 		      G_SIGNAL_RUN_LAST,
426 		      G_STRUCT_OFFSET (GooCanvasItemIface, animation_finished),
427 		      NULL, NULL,
428 		      g_cclosure_marshal_VOID__BOOLEAN,
429 		      G_TYPE_NONE, 1,
430 		      G_TYPE_BOOLEAN);
431 
432       /**
433        * GooCanvasItem::scroll-event
434        * @item: the item that received the signal.
435        * @target_item: the target of the event.
436        * @event: the event data. The x & y fields contain the mouse position
437        *  in the item's coordinate space. The x_root & y_root fields contain
438        *  the same coordinates converted to the canvas coordinate space.
439        *
440        * Emitted when a button in the 4 to 7 range is pressed. Wheel mice are
441        * usually configured to generate button press events for buttons 4 and 5
442        * when the wheel is turned in an item.
443        *
444        * Returns: %TRUE to stop the signal emission, or %FALSE to let it
445        *  continue.
446        */
447       canvas_item_signals[SCROLL_EVENT] =
448 	g_signal_new ("scroll_event",
449 		      iface_type,
450 		      G_SIGNAL_RUN_LAST,
451 		      G_STRUCT_OFFSET (GooCanvasItemIface,
452 				       scroll_event),
453 		      goo_canvas_boolean_handled_accumulator, NULL,
454 		      goo_canvas_marshal_BOOLEAN__OBJECT_BOXED,
455 		      G_TYPE_BOOLEAN, 2,
456 		      GOO_TYPE_CANVAS_ITEM,
457 		      GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
458 
459       g_object_interface_install_property (g_iface,
460 					   g_param_spec_object ("parent",
461 								_("Parent"),
462 								_("The parent item"),
463 								GOO_TYPE_CANVAS_ITEM,
464 								G_PARAM_READWRITE));
465 
466       g_object_interface_install_property (g_iface,
467 					   g_param_spec_enum ("visibility",
468 							      _("Visibility"),
469 							      _("When the canvas item is visible"),
470 							      GOO_TYPE_CANVAS_ITEM_VISIBILITY,
471 							      GOO_CANVAS_ITEM_VISIBLE,
472 							      G_PARAM_READWRITE));
473 
474       g_object_interface_install_property (g_iface,
475 					   g_param_spec_double ("visibility-threshold",
476 								_("Visibility Threshold"),
477 								_("The scale threshold at which the item becomes visible"),
478 								0.0,
479 								G_MAXDOUBLE,
480 								0.0,
481 								G_PARAM_READWRITE));
482 
483       g_object_interface_install_property (g_iface,
484 					   g_param_spec_boxed ("transform",
485 							       _("Transform"),
486 							       _("The transformation matrix of the item"),
487 							       GOO_TYPE_CAIRO_MATRIX,
488 							       G_PARAM_READWRITE));
489 
490       g_object_interface_install_property (g_iface,
491 					   g_param_spec_flags ("pointer-events",
492 							       _("Pointer Events"),
493 							       _("Specifies when the item receives pointer events"),
494 							       GOO_TYPE_CANVAS_POINTER_EVENTS,
495 							       GOO_CANVAS_EVENTS_VISIBLE_PAINTED,
496 							       G_PARAM_READWRITE));
497 
498       g_object_interface_install_property (g_iface,
499 					   g_param_spec_string ("title",
500 								_("Title"),
501 								_("A short context-rich description of the item for use by assistive technologies"),
502 								NULL,
503 								G_PARAM_READWRITE));
504 
505       g_object_interface_install_property (g_iface,
506 					   g_param_spec_string ("description",
507 								_("Description"),
508 								_("A description of the item for use by assistive technologies"),
509 								NULL,
510 								G_PARAM_READWRITE));
511 
512       g_object_interface_install_property (g_iface,
513 					   g_param_spec_boolean ("can-focus",
514 								 _("Can Focus"),
515 								 _("If the item can take the keyboard focus"),
516 								 FALSE,
517 								 G_PARAM_READWRITE));
518 
519       g_object_interface_install_property (g_iface,
520 					   g_param_spec_string ("tooltip",
521 								_("Tooltip"),
522 								_("The tooltip to display for the item"),
523 								NULL,
524 								G_PARAM_READWRITE));
525 
526       _goo_canvas_style_init ();
527 
528       initialized = TRUE;
529     }
530 }
531 
532 
533 /**
534  * goo_canvas_item_get_canvas:
535  * @item: a #GooCanvasItem.
536  *
537  * Returns the #GooCanvas containing the given #GooCanvasItem.
538  *
539  * Returns: the #GooCanvas.
540  **/
541 GooCanvas*
goo_canvas_item_get_canvas(GooCanvasItem * item)542 goo_canvas_item_get_canvas (GooCanvasItem *item)
543 {
544   GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
545 
546   if (iface->get_canvas)
547     {
548       return iface->get_canvas (item);
549     }
550   else
551     {
552       GooCanvasItem *parent = iface->get_parent (item);
553 
554       if (parent)
555 	return goo_canvas_item_get_canvas (parent);
556       return NULL;
557     }
558 }
559 
560 
561 /**
562  * goo_canvas_item_set_canvas:
563  * @item: a #GooCanvasItem.
564  * @canvas: a #GooCanvas
565  *
566  * This function is only intended to be used when implementing new canvas
567  * items, specifically container items such as #GooCanvasGroup.
568  *
569  * It sets the canvas of the item.
570  **/
571 void
goo_canvas_item_set_canvas(GooCanvasItem * item,GooCanvas * canvas)572 goo_canvas_item_set_canvas     (GooCanvasItem   *item,
573 				GooCanvas       *canvas)
574 {
575   GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
576 
577   if (iface->set_canvas)
578     iface->set_canvas (item, canvas);
579 }
580 
581 
582 /**
583  * goo_canvas_item_add_child:
584  * @item: the container to add the item to.
585  * @child: the item to add.
586  * @position: the position of the item, or -1 to place it last (at the top of
587  *  the stacking order).
588  *
589  * Adds a child item to a container item at the given stack position.
590  **/
591 void
goo_canvas_item_add_child(GooCanvasItem * item,GooCanvasItem * child,gint position)592 goo_canvas_item_add_child      (GooCanvasItem       *item,
593 				GooCanvasItem       *child,
594 				gint                 position)
595 {
596   GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
597 
598   g_return_if_fail (iface->add_child != NULL);
599   g_return_if_fail (item != child);
600 
601   iface->add_child (item, child, position);
602 }
603 
604 
605 /**
606  * goo_canvas_item_move_child:
607  * @item: a container item.
608  * @old_position: the current position of the child item.
609  * @new_position: the new position of the child item.
610  *
611  * Moves a child item to a new stack position within the container.
612  **/
613 void
goo_canvas_item_move_child(GooCanvasItem * item,gint old_position,gint new_position)614 goo_canvas_item_move_child     (GooCanvasItem       *item,
615 				gint                 old_position,
616 				gint                 new_position)
617 {
618   GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
619 
620   g_return_if_fail (iface->move_child != NULL);
621 
622   iface->move_child (item, old_position, new_position);
623 }
624 
625 
626 /**
627  * goo_canvas_item_remove_child:
628  * @item: a container item.
629  * @child_num: the position of the child item to remove.
630  *
631  * Removes the child item at the given position.
632  **/
633 void
goo_canvas_item_remove_child(GooCanvasItem * item,gint child_num)634 goo_canvas_item_remove_child   (GooCanvasItem       *item,
635 				gint                 child_num)
636 {
637   GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
638 
639   g_return_if_fail (iface->remove_child != NULL);
640 
641   iface->remove_child (item, child_num);
642 }
643 
644 
645 /**
646  * goo_canvas_item_find_child:
647  * @item: a container item.
648  * @child: the child item to find.
649  *
650  * Attempts to find the given child item with the container's stack.
651  *
652  * Returns: the position of the given @child item, or -1 if it isn't found.
653  **/
654 gint
goo_canvas_item_find_child(GooCanvasItem * item,GooCanvasItem * child)655 goo_canvas_item_find_child     (GooCanvasItem *item,
656 				GooCanvasItem *child)
657 {
658   GooCanvasItem *tmp;
659   int n_children, i;
660 
661   /* Find the current position of item and above. */
662   n_children = goo_canvas_item_get_n_children (item);
663   for (i = 0; i < n_children; i++)
664     {
665       tmp = goo_canvas_item_get_child (item, i);
666       if (child == tmp)
667 	return i;
668     }
669   return -1;
670 }
671 
672 
673 /**
674  * goo_canvas_item_is_container:
675  * @item: an item.
676  *
677  * Tests to see if the given item is a container.
678  *
679  * Returns: %TRUE if the item is a container.
680  **/
681 gboolean
goo_canvas_item_is_container(GooCanvasItem * item)682 goo_canvas_item_is_container (GooCanvasItem       *item)
683 {
684   GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
685 
686   return iface->get_n_children ? TRUE : FALSE;
687 }
688 
689 
690 /**
691  * goo_canvas_item_get_n_children:
692  * @item: a container item.
693  *
694  * Gets the number of children of the container.
695  *
696  * Returns: the number of children.
697  **/
698 gint
goo_canvas_item_get_n_children(GooCanvasItem * item)699 goo_canvas_item_get_n_children (GooCanvasItem       *item)
700 {
701   GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
702 
703   return iface->get_n_children ? iface->get_n_children (item) : 0;
704 }
705 
706 
707 /**
708  * goo_canvas_item_get_child:
709  * @item: a container item.
710  * @child_num: the position of a child in the container's stack.
711  *
712  * Gets the child item at the given stack position.
713  *
714  * Returns: the child item at the given stack position, or %NULL if @child_num
715  * is out of range.
716  **/
717 GooCanvasItem*
goo_canvas_item_get_child(GooCanvasItem * item,gint child_num)718 goo_canvas_item_get_child (GooCanvasItem       *item,
719 			   gint                 child_num)
720 {
721   GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
722 
723   return iface->get_child ? iface->get_child (item, child_num) : NULL;
724 }
725 
726 
727 /**
728  * goo_canvas_item_get_parent:
729  * @item: an item.
730  *
731  * Gets the parent of the given item.
732  *
733  * Returns: the parent item, or %NULL if the item has no parent.
734  **/
735 GooCanvasItem*
goo_canvas_item_get_parent(GooCanvasItem * item)736 goo_canvas_item_get_parent  (GooCanvasItem *item)
737 {
738   g_return_val_if_fail (GOO_IS_CANVAS_ITEM (item), NULL);
739 
740   return GOO_CANVAS_ITEM_GET_IFACE (item)->get_parent (item);
741 }
742 
743 
744 /**
745  * goo_canvas_item_set_parent:
746  * @item: an item.
747  * @parent: the new parent item.
748  *
749  * This function is only intended to be used when implementing new canvas
750  * items (specifically container items such as #GooCanvasGroup).
751  * It sets the parent of the child item.
752  * <!--PARAMETERS-->
753  * <note><para>
754  * This function cannot be used to add an item to a group
755  * or to change the parent of an item.
756  * To do that use the #GooCanvasItem:parent property.
757  * </para></note>
758  **/
759 void
goo_canvas_item_set_parent(GooCanvasItem * item,GooCanvasItem * parent)760 goo_canvas_item_set_parent (GooCanvasItem *item,
761 			    GooCanvasItem *parent)
762 {
763   GOO_CANVAS_ITEM_GET_IFACE (item)->set_parent (item, parent);
764 }
765 
766 
767 /**
768  * goo_canvas_item_get_is_static:
769  * @item: an item.
770  *
771  * Returns %TRUE if the item is static. Static items do not move or change
772  * size when the canvas is scrolled or the scale changes.
773  *
774  * Returns: %TRUE if the item is static.
775  **/
776 gboolean
goo_canvas_item_get_is_static(GooCanvasItem * item)777 goo_canvas_item_get_is_static	(GooCanvasItem		*item)
778 {
779   GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
780 
781   if (iface->get_is_static)
782     return iface->get_is_static (item);
783   return FALSE;
784 }
785 
786 
787 /**
788  * goo_canvas_item_set_is_static:
789  * @item: an item.
790  * @is_static: if the item is static.
791  *
792  * Notifies the item that it is static. Static items do not move or change
793  * size when the canvas is scrolled or the scale changes.
794  *
795  * Container items such as #GooCanvasGroup should call this function when
796  * children are added, to notify children whether they are static or not.
797  * Containers should also pass on any changes in their own status to children.
798  **/
799 void
goo_canvas_item_set_is_static(GooCanvasItem * item,gboolean is_static)800 goo_canvas_item_set_is_static	(GooCanvasItem		*item,
801 				 gboolean		 is_static)
802 {
803   GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
804 
805   if (iface->set_is_static)
806     iface->set_is_static (item, is_static);
807 }
808 
809 
810 /**
811  * goo_canvas_item_remove:
812  * @item: an item.
813  *
814  * Removes an item from its parent. If the item is in a canvas it will be
815  * removed.
816  *
817  * This would normally also result in the item being freed.
818  **/
819 void
goo_canvas_item_remove(GooCanvasItem * item)820 goo_canvas_item_remove (GooCanvasItem *item)
821 {
822   GooCanvasItem *parent;
823   gint child_num;
824 
825   parent = goo_canvas_item_get_parent (item);
826   if (!parent)
827     return;
828 
829   child_num = goo_canvas_item_find_child (parent, item);
830   if (child_num == -1)
831     return;
832 
833   goo_canvas_item_remove_child (parent, child_num);
834 }
835 
836 
837 /**
838  * goo_canvas_item_raise:
839  * @item: an item.
840  * @above: the item to raise @item above, or %NULL to raise @item to the top
841  *  of the stack.
842  *
843  * Raises an item in the stacking order.
844  **/
845 void
goo_canvas_item_raise(GooCanvasItem * item,GooCanvasItem * above)846 goo_canvas_item_raise          (GooCanvasItem *item,
847 				GooCanvasItem *above)
848 {
849   GooCanvasItem *parent, *child;
850   int n_children, i, item_pos = -1, above_pos = -1;
851 
852   parent = goo_canvas_item_get_parent (item);
853   if (!parent || item == above)
854     return;
855 
856   /* Find the current position of item and above. */
857   n_children = goo_canvas_item_get_n_children (parent);
858   for (i = 0; i < n_children; i++)
859     {
860       child = goo_canvas_item_get_child (parent, i);
861       if (child == item)
862 	item_pos = i;
863       if (child == above)
864 	above_pos = i;
865     }
866 
867   /* If above is NULL we raise the item to the top of the stack. */
868   if (!above)
869     above_pos = n_children - 1;
870 
871   g_return_if_fail (item_pos != -1);
872   g_return_if_fail (above_pos != -1);
873 
874   /* Only move the item if the new position is higher in the stack. */
875   if (above_pos > item_pos)
876     goo_canvas_item_move_child (parent, item_pos, above_pos);
877 }
878 
879 
880 /**
881  * goo_canvas_item_lower:
882  * @item: an item.
883  * @below: the item to lower @item below, or %NULL to lower @item to the
884  *  bottom of the stack.
885  *
886  * Lowers an item in the stacking order.
887  **/
888 void
goo_canvas_item_lower(GooCanvasItem * item,GooCanvasItem * below)889 goo_canvas_item_lower          (GooCanvasItem *item,
890 				GooCanvasItem *below)
891 {
892   GooCanvasItem *parent, *child;
893   int n_children, i, item_pos = -1, below_pos = -1;
894 
895   parent = goo_canvas_item_get_parent (item);
896   if (!parent || item == below)
897     return;
898 
899   /* Find the current position of item and below. */
900   n_children = goo_canvas_item_get_n_children (parent);
901   for (i = 0; i < n_children; i++)
902     {
903       child = goo_canvas_item_get_child (parent, i);
904       if (child == item)
905 	item_pos = i;
906       if (child == below)
907 	below_pos = i;
908     }
909 
910   /* If below is NULL we lower the item to the bottom of the stack. */
911   if (!below)
912     below_pos = 0;
913 
914   g_return_if_fail (item_pos != -1);
915   g_return_if_fail (below_pos != -1);
916 
917   /* Only move the item if the new position is lower in the stack. */
918   if (below_pos < item_pos)
919     goo_canvas_item_move_child (parent, item_pos, below_pos);
920 }
921 
922 
923 /**
924  * goo_canvas_item_get_transform:
925  * @item: an item.
926  * @transform: the place to store the transform.
927  *
928  * Gets the transformation matrix of an item.
929  *
930  * Returns: %TRUE if a transform is set.
931  **/
932 gboolean
goo_canvas_item_get_transform(GooCanvasItem * item,cairo_matrix_t * transform)933 goo_canvas_item_get_transform  (GooCanvasItem   *item,
934 				cairo_matrix_t  *transform)
935 {
936   GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
937 
938   return iface->get_transform ? iface->get_transform (item, transform) : FALSE;
939 }
940 
941 
942 /**
943  * goo_canvas_item_get_transform_for_child:
944  * @item: an item.
945  * @child: a child of @item.
946  * @transform: the place to store the transform.
947  *
948  * Gets the transformation matrix of an item combined with any special
949  * transform needed for the given child. These special transforms are used
950  * by layout items such as #GooCanvasTable.
951  *
952  * Returns: %TRUE if a transform is set.
953  **/
954 gboolean
goo_canvas_item_get_transform_for_child(GooCanvasItem * item,GooCanvasItem * child,cairo_matrix_t * transform)955 goo_canvas_item_get_transform_for_child  (GooCanvasItem  *item,
956 					  GooCanvasItem  *child,
957 					  cairo_matrix_t *transform)
958 {
959   GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
960 
961   if (child && iface->get_transform_for_child)
962     return iface->get_transform_for_child (item, child, transform);
963 
964   /* We fallback to the standard get_transform method. */
965   if (iface->get_transform)
966     return iface->get_transform (item, transform);
967 
968   return FALSE;
969 }
970 
971 
972 /**
973  * goo_canvas_item_set_transform:
974  * @item: an item.
975  * @transform: the new transformation matrix, or %NULL to reset the
976  *  transformation to the identity matrix.
977  *
978  * Sets the transformation matrix of an item.
979  **/
980 void
goo_canvas_item_set_transform(GooCanvasItem * item,const cairo_matrix_t * transform)981 goo_canvas_item_set_transform  (GooCanvasItem        *item,
982 				const cairo_matrix_t *transform)
983 {
984   GOO_CANVAS_ITEM_GET_IFACE (item)->set_transform (item, transform);
985 }
986 
987 
988 /**
989  * goo_canvas_item_get_simple_transform:
990  * @item: an item.
991  * @x: returns the x coordinate of the origin of the item's coordinate space.
992  * @y: returns the y coordinate of the origin of the item's coordinate space.
993  * @scale: returns the scale of the item.
994  * @rotation: returns the clockwise rotation of the item, in degrees (0-360).
995  *
996  * This function can be used to get the position, scale and rotation of an
997  * item, providing that the item has a simple transformation matrix
998  * (e.g. set with goo_canvas_item_set_simple_transform(), or using a
999  * combination of simple translate, scale and rotate operations). If the item
1000  * has a complex transformation matrix the results will be incorrect.
1001  *
1002  * Returns: %TRUE if a transform is set.
1003  **/
1004 gboolean
goo_canvas_item_get_simple_transform(GooCanvasItem * item,gdouble * x,gdouble * y,gdouble * scale,gdouble * rotation)1005 goo_canvas_item_get_simple_transform (GooCanvasItem   *item,
1006 				      gdouble         *x,
1007 				      gdouble         *y,
1008 				      gdouble         *scale,
1009 				      gdouble         *rotation)
1010 {
1011   GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
1012   cairo_matrix_t matrix = { 1, 0, 0, 1, 0, 0 };
1013   double x1 = 1.0, y1 = 0.0, radians;
1014   gboolean has_transform = FALSE;
1015 
1016   if (iface->get_transform)
1017     has_transform = iface->get_transform (item, &matrix);
1018 
1019   if (!has_transform)
1020     {
1021       *x = *y = *rotation = 0.0;
1022       *scale = 1.0;
1023       return FALSE;
1024     }
1025 
1026   *x = matrix.x0;
1027   *y = matrix.y0;
1028 
1029   matrix.x0 = 0.0;
1030   matrix.y0 = 0.0;
1031 
1032   cairo_matrix_transform_point (&matrix, &x1, &y1);
1033   *scale = sqrt (x1 * x1 + y1 * y1);
1034   radians = atan2 (y1, x1);
1035   *rotation = radians * (180 / M_PI);
1036   if (*rotation < 0)
1037     *rotation += 360;
1038 
1039   return TRUE;
1040 }
1041 
1042 
1043 /**
1044  * goo_canvas_item_set_simple_transform:
1045  * @item: an item.
1046  * @x: the x coordinate of the origin of the item's coordinate space.
1047  * @y: the y coordinate of the origin of the item's coordinate space.
1048  * @scale: the scale of the item.
1049  * @rotation: the clockwise rotation of the item, in degrees.
1050  *
1051  * A convenience function to set the item's transformation matrix.
1052  **/
1053 void
goo_canvas_item_set_simple_transform(GooCanvasItem * item,gdouble x,gdouble y,gdouble scale,gdouble rotation)1054 goo_canvas_item_set_simple_transform (GooCanvasItem   *item,
1055 				      gdouble          x,
1056 				      gdouble          y,
1057 				      gdouble          scale,
1058 				      gdouble          rotation)
1059 {
1060   GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
1061   cairo_matrix_t new_matrix = { 1, 0, 0, 1, 0, 0 };
1062 
1063   cairo_matrix_translate (&new_matrix, x, y);
1064   cairo_matrix_scale (&new_matrix, scale, scale);
1065   cairo_matrix_rotate (&new_matrix, rotation * (M_PI  / 180));
1066   iface->set_transform (item, &new_matrix);
1067 }
1068 
1069 
1070 /**
1071  * goo_canvas_item_translate:
1072  * @item: an item.
1073  * @tx: the amount to move the origin in the horizontal direction.
1074  * @ty: the amount to move the origin in the vertical direction.
1075  *
1076  * Translates the origin of the item's coordinate system by the given amounts.
1077  **/
1078 void
goo_canvas_item_translate(GooCanvasItem * item,gdouble tx,gdouble ty)1079 goo_canvas_item_translate      (GooCanvasItem *item,
1080 				gdouble        tx,
1081 				gdouble        ty)
1082 {
1083   GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
1084   cairo_matrix_t new_matrix = { 1, 0, 0, 1, 0, 0 };
1085 
1086   iface->get_transform (item, &new_matrix);
1087   cairo_matrix_translate (&new_matrix, tx, ty);
1088   iface->set_transform (item, &new_matrix);
1089 }
1090 
1091 
1092 /**
1093  * goo_canvas_item_scale:
1094  * @item: an item.
1095  * @sx: the amount to scale the horizontal axis.
1096  * @sy: the amount to scale the vertical axis.
1097  *
1098  * Scales the item's coordinate system by the given amounts.
1099  **/
1100 void
goo_canvas_item_scale(GooCanvasItem * item,gdouble sx,gdouble sy)1101 goo_canvas_item_scale          (GooCanvasItem *item,
1102 				gdouble        sx,
1103 				gdouble        sy)
1104 {
1105   GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
1106   cairo_matrix_t new_matrix = { 1, 0, 0, 1, 0, 0 };
1107 
1108   iface->get_transform (item, &new_matrix);
1109   cairo_matrix_scale (&new_matrix, sx, sy);
1110   iface->set_transform (item, &new_matrix);
1111 }
1112 
1113 
1114 /**
1115  * goo_canvas_item_rotate:
1116  * @item: an item.
1117  * @degrees: the clockwise angle of rotation.
1118  * @cx: the x coordinate of the origin of the rotation.
1119  * @cy: the y coordinate of the origin of the rotation.
1120  *
1121  * Rotates the item's coordinate system by the given amount, about the given
1122  * origin.
1123  **/
1124 void
goo_canvas_item_rotate(GooCanvasItem * item,gdouble degrees,gdouble cx,gdouble cy)1125 goo_canvas_item_rotate         (GooCanvasItem *item,
1126 				gdouble        degrees,
1127 				gdouble        cx,
1128 				gdouble        cy)
1129 {
1130   GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
1131   cairo_matrix_t new_matrix = { 1, 0, 0, 1, 0, 0 };
1132   double radians = degrees * (M_PI / 180);
1133 
1134   iface->get_transform (item, &new_matrix);
1135   cairo_matrix_translate (&new_matrix, cx, cy);
1136   cairo_matrix_rotate (&new_matrix, radians);
1137   cairo_matrix_translate (&new_matrix, -cx, -cy);
1138   iface->set_transform (item, &new_matrix);
1139 }
1140 
1141 
1142 /**
1143  * goo_canvas_item_skew_x:
1144  * @item: an item.
1145  * @degrees: the skew angle.
1146  * @cx: the x coordinate of the origin of the skew transform.
1147  * @cy: the y coordinate of the origin of the skew transform.
1148  *
1149  * Skews the item's coordinate system along the x axis by the given amount,
1150  * about the given origin.
1151  **/
1152 void
goo_canvas_item_skew_x(GooCanvasItem * item,gdouble degrees,gdouble cx,gdouble cy)1153 goo_canvas_item_skew_x         (GooCanvasItem *item,
1154 				gdouble        degrees,
1155 				gdouble        cx,
1156 				gdouble        cy)
1157 {
1158   GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
1159   cairo_matrix_t tmp, new_matrix = { 1, 0, 0, 1, 0, 0 };
1160   double radians = degrees * (M_PI / 180);
1161 
1162   iface->get_transform (item, &new_matrix);
1163   cairo_matrix_translate (&new_matrix, cx, cy);
1164   cairo_matrix_init (&tmp, 1, 0, tan (radians), 1, 0, 0);
1165   cairo_matrix_multiply (&new_matrix, &tmp, &new_matrix);
1166   cairo_matrix_translate (&new_matrix, -cx, -cy);
1167   iface->set_transform (item, &new_matrix);
1168 }
1169 
1170 
1171 /**
1172  * goo_canvas_item_skew_y:
1173  * @item: an item.
1174  * @degrees: the skew angle.
1175  * @cx: the x coordinate of the origin of the skew transform.
1176  * @cy: the y coordinate of the origin of the skew transform.
1177  *
1178  * Skews the item's coordinate system along the y axis by the given amount,
1179  * about the given origin.
1180  **/
1181 void
goo_canvas_item_skew_y(GooCanvasItem * item,gdouble degrees,gdouble cx,gdouble cy)1182 goo_canvas_item_skew_y         (GooCanvasItem *item,
1183 				gdouble        degrees,
1184 				gdouble        cx,
1185 				gdouble        cy)
1186 {
1187   GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
1188   cairo_matrix_t tmp, new_matrix = { 1, 0, 0, 1, 0, 0 };
1189   double radians = degrees * (M_PI / 180);
1190 
1191   iface->get_transform (item, &new_matrix);
1192   cairo_matrix_translate (&new_matrix, cx, cy);
1193   cairo_matrix_init (&tmp, 1, tan (radians), 0, 1, 0, 0);
1194   cairo_matrix_multiply (&new_matrix, &tmp, &new_matrix);
1195   cairo_matrix_translate (&new_matrix, -cx, -cy);
1196   iface->set_transform (item, &new_matrix);
1197 }
1198 
1199 
1200 /**
1201  * goo_canvas_item_get_style:
1202  * @item: an item.
1203  *
1204  * Gets the item's style. If the item doesn't have its own style it will return
1205  * its parent's style.
1206  *
1207  * Returns: the item's style.
1208  **/
1209 GooCanvasStyle*
goo_canvas_item_get_style(GooCanvasItem * item)1210 goo_canvas_item_get_style      (GooCanvasItem   *item)
1211 {
1212   GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
1213 
1214   return iface->get_style ? iface->get_style (item) : NULL;
1215 }
1216 
1217 
1218 /**
1219  * goo_canvas_item_set_style:
1220  * @item: an item.
1221  * @style: a style.
1222  *
1223  * Sets the item's style, by copying the properties from the given style.
1224  **/
1225 void
goo_canvas_item_set_style(GooCanvasItem * item,GooCanvasStyle * style)1226 goo_canvas_item_set_style      (GooCanvasItem   *item,
1227 				GooCanvasStyle  *style)
1228 {
1229   GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
1230 
1231   if (iface->set_style)
1232     iface->set_style (item, style);
1233 }
1234 
1235 
1236 typedef struct _GooCanvasItemAnimation  GooCanvasItemAnimation;
1237 struct _GooCanvasItemAnimation
1238 {
1239   GooCanvasAnimateType type;
1240   GooCanvasItem *item;
1241   GooCanvasItemModel *model;
1242   int step, total_steps;
1243   cairo_matrix_t start;
1244   gdouble x_start, y_start, scale_start, radians_start;
1245   gdouble x_step, y_step, scale_step, radians_step;
1246   gboolean absolute;
1247   gboolean forward;
1248   guint timeout_id;
1249 };
1250 
1251 
1252 static void
goo_canvas_item_free_animation(GooCanvasItemAnimation * anim)1253 goo_canvas_item_free_animation (GooCanvasItemAnimation *anim)
1254 {
1255   if (anim->timeout_id)
1256     {
1257       g_source_remove (anim->timeout_id);
1258       anim->timeout_id = 0;
1259     }
1260 
1261   g_free (anim);
1262 }
1263 
1264 
1265 static gboolean
goo_canvas_item_animate_cb(GooCanvasItemAnimation * anim)1266 goo_canvas_item_animate_cb (GooCanvasItemAnimation *anim)
1267 {
1268   GooCanvasItem *item = anim->item;
1269   GooCanvasItemModel *model = anim->model;
1270   GooCanvasAnimateType type = anim->type;
1271   GooCanvasItemIface *iface = NULL;
1272   GooCanvasItemModelIface *model_iface = NULL;
1273   cairo_matrix_t new_matrix;
1274   gboolean keep_source = TRUE;
1275   gdouble scale;
1276   gint step;
1277 
1278   GDK_THREADS_ENTER ();
1279 
1280   if (model)
1281     model_iface = GOO_CANVAS_ITEM_MODEL_GET_IFACE (model);
1282   else
1283     iface = GOO_CANVAS_ITEM_GET_IFACE (item);
1284 
1285   if (++anim->step > anim->total_steps)
1286     {
1287       switch (type)
1288 	{
1289 	case GOO_CANVAS_ANIMATE_RESET:
1290 	  /* Reset the transform to the initial value. */
1291 	  if (model)
1292 	    model_iface->set_transform (model, &anim->start);
1293 	  else
1294 	    iface->set_transform (item, &anim->start);
1295 
1296 	  /* Fall through.. */
1297 	case GOO_CANVAS_ANIMATE_FREEZE:
1298 	  keep_source = FALSE;
1299 	  anim->timeout_id = 0;
1300 	  /* This will result in a call to goo_canvas_item_free_animation()
1301 	     above. We've set the timeout_id to 0 so it isn't removed twice. */
1302 	  if (model)
1303 	    {
1304 	      g_object_set_data (G_OBJECT (model), animation_key, NULL);
1305 	      g_signal_emit_by_name (model, "animation-finished", FALSE);
1306 	    }
1307 	  else
1308 	    {
1309 	      g_object_set_data (G_OBJECT (item), animation_key, NULL);
1310 	      g_signal_emit_by_name (item, "animation-finished", FALSE);
1311 	    }
1312 	  break;
1313 
1314 	case GOO_CANVAS_ANIMATE_RESTART:
1315 	  anim->step = 0;
1316 	  break;
1317 
1318 	case GOO_CANVAS_ANIMATE_BOUNCE:
1319 	  anim->forward = !anim->forward;
1320 	  anim->step = 1;
1321 	  break;
1322 	}
1323     }
1324 
1325   if (keep_source)
1326     {
1327       step = anim->forward ? anim->step : anim->total_steps - anim->step;
1328 
1329       if (anim->absolute)
1330 	{
1331 	  cairo_matrix_init_identity (&new_matrix);
1332 	  scale = anim->scale_start + anim->scale_step * step;
1333 	  cairo_matrix_translate (&new_matrix,
1334 				  anim->x_start + anim->x_step * step,
1335 				  anim->y_start + anim->y_step * step);
1336 	  cairo_matrix_scale (&new_matrix, scale, scale);
1337 	  cairo_matrix_rotate (&new_matrix,
1338 			       anim->radians_start + anim->radians_step * step);
1339 	}
1340       else
1341 	{
1342 	  new_matrix = anim->start;
1343 	  scale = 1 + anim->scale_step * step;
1344 	  cairo_matrix_translate (&new_matrix, anim->x_step * step,
1345 				  anim->y_step * step);
1346 	  cairo_matrix_scale (&new_matrix, scale, scale);
1347 	  cairo_matrix_rotate (&new_matrix, anim->radians_step * step);
1348 	}
1349 
1350       if (model)
1351 	model_iface->set_transform (model, &new_matrix);
1352       else
1353 	iface->set_transform (item, &new_matrix);
1354     }
1355 
1356   GDK_THREADS_LEAVE ();
1357 
1358   /* Return FALSE to remove the timeout handler when we are finished. */
1359   return keep_source;
1360 }
1361 
1362 
1363 void
_goo_canvas_item_animate_internal(GooCanvasItem * item,GooCanvasItemModel * model,gdouble x,gdouble y,gdouble scale,gdouble degrees,gboolean absolute,gint duration,gint step_time,GooCanvasAnimateType type)1364 _goo_canvas_item_animate_internal (GooCanvasItem       *item,
1365 				   GooCanvasItemModel  *model,
1366 				   gdouble              x,
1367 				   gdouble              y,
1368 				   gdouble              scale,
1369 				   gdouble              degrees,
1370 				   gboolean             absolute,
1371 				   gint                 duration,
1372 				   gint                 step_time,
1373 				   GooCanvasAnimateType type)
1374 {
1375   GObject *object;
1376   cairo_matrix_t matrix = { 1, 0, 0, 1, 0, 0 };
1377   GooCanvasItemAnimation *anim;
1378 
1379   if (item)
1380     {
1381       GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
1382       iface->get_transform (item, &matrix);
1383       object = (GObject*) item;
1384     }
1385   else
1386     {
1387       GooCanvasItemModelIface *iface = GOO_CANVAS_ITEM_MODEL_GET_IFACE (model);
1388       iface->get_transform (model, &matrix);
1389       object = (GObject*) model;
1390     }
1391 
1392   anim = g_new (GooCanvasItemAnimation, 1);
1393   anim->type = type;
1394   anim->item = item;
1395   anim->model = model;
1396   anim->step = 0;
1397   anim->total_steps = duration / step_time;
1398   anim->start = matrix;
1399   anim->absolute = absolute;
1400   anim->forward = TRUE;
1401 
1402   /* For absolute animation we have to try to calculate the current position,
1403      scale and rotation. */
1404   if (absolute)
1405     {
1406       cairo_matrix_t tmp_matrix = anim->start;
1407       double x1 = 1.0, y1 = 0.0;
1408 
1409       anim->x_start = tmp_matrix.x0;
1410       anim->y_start = tmp_matrix.y0;
1411 
1412       tmp_matrix.x0 = 0.0;
1413       tmp_matrix.y0 = 0.0;
1414 
1415       cairo_matrix_transform_point (&tmp_matrix, &x1, &y1);
1416       anim->scale_start = sqrt (x1 * x1 + y1 * y1);
1417       anim->radians_start = atan2 (y1, x1);
1418 
1419       anim->x_step = (x - anim->x_start) / anim->total_steps;
1420       anim->y_step = (y - anim->y_start) / anim->total_steps;
1421       anim->scale_step = (scale - anim->scale_start) / anim->total_steps;
1422       anim->radians_step = (degrees * (M_PI / 180) - anim->radians_start) / anim->total_steps;
1423     }
1424   else
1425     {
1426       anim->x_step = x / anim->total_steps;
1427       anim->y_step = y / anim->total_steps;
1428       anim->scale_step = (scale - 1.0) / anim->total_steps;
1429       anim->radians_step = (degrees * (M_PI / 180)) / anim->total_steps;
1430     }
1431 
1432 
1433   /* Store a pointer to the new animation in the item. This will automatically
1434      stop any current animation and free it. */
1435   g_object_set_data_full (object, animation_key, anim,
1436 			  (GDestroyNotify) goo_canvas_item_free_animation);
1437 
1438   anim->timeout_id = g_timeout_add (step_time,
1439 				    (GSourceFunc) goo_canvas_item_animate_cb,
1440 				    anim);
1441 }
1442 
1443 
1444 /**
1445  * goo_canvas_item_animate:
1446  * @item: an item.
1447  * @x: the final x coordinate.
1448  * @y: the final y coordinate.
1449  * @scale: the final scale.
1450  * @degrees: the final rotation. This can be negative to rotate anticlockwise,
1451  *  and can also be greater than 360 to rotate a number of times.
1452  * @absolute: if the @x, @y, @scale and @degrees values are absolute, or
1453  *  relative to the current transform. Note that absolute animations only work
1454  *  if the item currently has a simple transform. If the item has a shear or
1455  *  some other complicated transform it may result in strange animations.
1456  * @duration: the duration of the animation, in milliseconds (1/1000ths of a
1457  *  second).
1458  * @step_time: the time between each animation step, in milliseconds.
1459  * @type: specifies what happens when the animation finishes.
1460  *
1461  * Animates an item from its current position to the given offsets, scale
1462  * and rotation.
1463  **/
1464 void
goo_canvas_item_animate(GooCanvasItem * item,gdouble x,gdouble y,gdouble scale,gdouble degrees,gboolean absolute,gint duration,gint step_time,GooCanvasAnimateType type)1465 goo_canvas_item_animate        (GooCanvasItem *item,
1466 				gdouble        x,
1467 				gdouble        y,
1468 				gdouble        scale,
1469 				gdouble        degrees,
1470 				gboolean       absolute,
1471 				gint           duration,
1472 				gint           step_time,
1473 				GooCanvasAnimateType type)
1474 {
1475   _goo_canvas_item_animate_internal (item, NULL, x, y, scale, degrees,
1476 				     absolute, duration, step_time, type);
1477 }
1478 
1479 
1480 /**
1481  * goo_canvas_item_stop_animation:
1482  * @item: an item.
1483  *
1484  * Stops any current animation for the given item, leaving it at its current
1485  * position.
1486  **/
1487 void
goo_canvas_item_stop_animation(GooCanvasItem * item)1488 goo_canvas_item_stop_animation (GooCanvasItem *item)
1489 {
1490   /* This will result in a call to goo_canvas_item_free_animation() above. */
1491   g_object_set_data (G_OBJECT (item), animation_key, NULL);
1492 
1493   g_signal_emit_by_name (item, "animation-finished", TRUE);
1494 }
1495 
1496 
1497 
1498 
1499 /**
1500  * goo_canvas_item_request_update:
1501  * @item: a #GooCanvasItem.
1502  *
1503  * This function is only intended to be used when implementing new canvas
1504  * items.
1505  *
1506  * It requests that an update of the item is scheduled. It will be performed
1507  * as soon as the application is idle, and before the canvas is redrawn.
1508  **/
1509 void
goo_canvas_item_request_update(GooCanvasItem * item)1510 goo_canvas_item_request_update  (GooCanvasItem *item)
1511 {
1512   GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
1513 
1514   if (iface->request_update)
1515     iface->request_update (item);
1516   else
1517     goo_canvas_item_request_update (iface->get_parent (item));
1518 }
1519 
1520 
1521 /**
1522  * goo_canvas_item_get_bounds:
1523  * @item: a #GooCanvasItem.
1524  * @bounds: a #GooCanvasBounds to return the bounds in.
1525  *
1526  * Gets the bounds of the item.
1527  *
1528  * Note that the bounds includes the entire fill and stroke extents of the
1529  * item, whether they are painted or not.
1530  **/
1531 void
goo_canvas_item_get_bounds(GooCanvasItem * item,GooCanvasBounds * bounds)1532 goo_canvas_item_get_bounds  (GooCanvasItem   *item,
1533 			     GooCanvasBounds *bounds)
1534 {
1535   GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
1536 
1537   iface->get_bounds (item, bounds);
1538 }
1539 
1540 
1541 /**
1542  * goo_canvas_item_get_items_at:
1543  * @item: a #GooCanvasItem.
1544  * @x: the x coordinate of the point.
1545  * @y: the y coordinate of the point.
1546  * @cr: a cairo contect.
1547  * @is_pointer_event: %TRUE if the "pointer-events" properties of items should
1548  *  be used to determine which parts of the item are tested.
1549  * @parent_is_visible: %TRUE if the parent item is visible (which
1550  *  implies that all ancestors are also visible).
1551  * @found_items: the list of items found so far.
1552  *
1553  * This function is only intended to be used when implementing new canvas
1554  * items, specifically container items such as #GooCanvasGroup.
1555  *
1556  * It gets the items at the given point.
1557  *
1558  * Returns: the @found_items list, with any more found items added onto
1559  *  the start of the list, leaving the top item first.
1560  **/
1561 GList*
goo_canvas_item_get_items_at(GooCanvasItem * item,gdouble x,gdouble y,cairo_t * cr,gboolean is_pointer_event,gboolean parent_is_visible,GList * found_items)1562 goo_canvas_item_get_items_at (GooCanvasItem  *item,
1563 			      gdouble         x,
1564 			      gdouble         y,
1565 			      cairo_t        *cr,
1566 			      gboolean        is_pointer_event,
1567 			      gboolean        parent_is_visible,
1568 			      GList          *found_items)
1569 {
1570   GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
1571 
1572   if (iface->get_items_at)
1573     return iface->get_items_at (item, x, y, cr, is_pointer_event,
1574 				parent_is_visible, found_items);
1575   else
1576     return found_items;
1577 }
1578 
1579 
1580 /**
1581  * goo_canvas_item_is_visible:
1582  * @item: a #GooCanvasItem.
1583  *
1584  * Checks if the item is visible.
1585  *
1586  * This entails checking the item's own visibility setting, as well as those
1587  * of its ancestors.
1588  *
1589  * Note that the item may be scrolled off the screen and so may not
1590  * be actually visible to the user.
1591  *
1592  * Returns: %TRUE if the item is visible.
1593  **/
1594 gboolean
goo_canvas_item_is_visible(GooCanvasItem * item)1595 goo_canvas_item_is_visible  (GooCanvasItem   *item)
1596 {
1597   GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
1598   GooCanvasItem *parent;
1599 
1600   if (iface->is_visible)
1601     return iface->is_visible (item);
1602 
1603   /* If the item doesn't implement the is_visible method we assume it is
1604      visible and check its ancestors. */
1605   parent = goo_canvas_item_get_parent (item);
1606   if (parent)
1607     return goo_canvas_item_is_visible (parent);
1608 
1609   return TRUE;
1610 }
1611 
1612 
1613 /**
1614  * goo_canvas_item_get_model:
1615  * @item: a #GooCanvasItem.
1616  *
1617  * Gets the model of the given canvas item.
1618  *
1619  * Returns: the item's model, or %NULL if it has no model.
1620  **/
1621 GooCanvasItemModel*
goo_canvas_item_get_model(GooCanvasItem * item)1622 goo_canvas_item_get_model	  (GooCanvasItem   *item)
1623 {
1624   GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
1625 
1626   return iface->get_model ? iface->get_model (item) : NULL;
1627 }
1628 
1629 
1630 /**
1631  * goo_canvas_item_set_model:
1632  * @item: a #GooCanvasItem.
1633  * @model: a #GooCanvasItemModel.
1634  *
1635  * Sets the model of the given canvas item.
1636  **/
1637 void
goo_canvas_item_set_model(GooCanvasItem * item,GooCanvasItemModel * model)1638 goo_canvas_item_set_model	  (GooCanvasItem      *item,
1639 				   GooCanvasItemModel *model)
1640 {
1641   GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
1642 
1643   if (iface->set_model)
1644     iface->set_model (item, model);
1645 }
1646 
1647 
1648 /**
1649  * goo_canvas_item_ensure_updated:
1650  * @item: a #GooCanvasItem.
1651  *
1652  * This function is only intended to be used when implementing new canvas
1653  * items.
1654  *
1655  * It updates the canvas immediately, if an update is scheduled.
1656  * This ensures that all item bounds are up-to-date.
1657  **/
1658 void
goo_canvas_item_ensure_updated(GooCanvasItem * item)1659 goo_canvas_item_ensure_updated (GooCanvasItem *item)
1660 {
1661   GooCanvas *canvas;
1662 
1663   canvas = goo_canvas_item_get_canvas (item);
1664   if (canvas)
1665     goo_canvas_update (canvas);
1666 }
1667 
1668 
1669 /**
1670  * goo_canvas_item_update:
1671  * @item: a #GooCanvasItem.
1672  * @entire_tree: if the entire subtree should be updated.
1673  * @cr: a cairo context.
1674  * @bounds: a #GooCanvasBounds to return the new bounds in.
1675  *
1676  * This function is only intended to be used when implementing new canvas
1677  * items, specifically container items such as #GooCanvasGroup.
1678  *
1679  * Updates the item, if needed, and any children.
1680  **/
1681 void
goo_canvas_item_update(GooCanvasItem * item,gboolean entire_tree,cairo_t * cr,GooCanvasBounds * bounds)1682 goo_canvas_item_update      (GooCanvasItem   *item,
1683 			     gboolean         entire_tree,
1684 			     cairo_t         *cr,
1685 			     GooCanvasBounds *bounds)
1686 {
1687   GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
1688 
1689   iface->update (item, entire_tree, cr, bounds);
1690 }
1691 
1692 
1693 /**
1694  * goo_canvas_item_paint:
1695  * @item: a #GooCanvasItem.
1696  * @cr: a cairo context.
1697  * @bounds: the bounds that need to be repainted, in device space.
1698  * @scale: the scale to use to determine whether an item should be painted.
1699  *  See #GooCanvasItem:visibility-threshold.
1700  *
1701  * This function is only intended to be used when implementing new canvas
1702  * items, specifically container items such as #GooCanvasGroup.
1703  *
1704  * It paints the item and all children if they intersect the given bounds.
1705  *
1706  * Note that the @scale argument may be different to the current scale in the
1707  * #GooCanvasItem, e.g. when the canvas is being printed.
1708  **/
1709 void
goo_canvas_item_paint(GooCanvasItem * item,cairo_t * cr,const GooCanvasBounds * bounds,gdouble scale)1710 goo_canvas_item_paint (GooCanvasItem         *item,
1711 		       cairo_t               *cr,
1712 		       const GooCanvasBounds *bounds,
1713 		       gdouble                scale)
1714 {
1715   GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
1716 
1717   iface->paint (item, cr, bounds, scale);
1718 }
1719 
1720 
1721 /**
1722  * goo_canvas_item_get_requested_area:
1723  * @item: a #GooCanvasItem.
1724  * @cr: a cairo context.
1725  * @requested_area: a #GooCanvasBounds to return the requested area in, in the
1726  *  parent's coordinate space.
1727  *
1728  * This function is only intended to be used when implementing new canvas
1729  * items, specifically layout items such as #GooCanvasTable.
1730  *
1731  * It gets the requested area of a child item.
1732  *
1733  * Returns: %TRUE if the item should be allocated space.
1734  **/
1735 gboolean
goo_canvas_item_get_requested_area(GooCanvasItem * item,cairo_t * cr,GooCanvasBounds * requested_area)1736 goo_canvas_item_get_requested_area (GooCanvasItem    *item,
1737 				    cairo_t          *cr,
1738 				    GooCanvasBounds  *requested_area)
1739 {
1740   GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
1741 
1742   return iface->get_requested_area (item, cr, requested_area);
1743 }
1744 
1745 
1746 /**
1747  * goo_canvas_item_get_requested_height:
1748  * @item: a #GooCanvasItem.
1749  * @cr: a cairo context.
1750  * @width: the width that the item may be allocated.
1751  *
1752  * This function is only intended to be used when implementing new canvas
1753  * items, specifically layout items such as #GooCanvasTable.
1754  *
1755  * It gets the requested height of a child item, assuming it is allocated the
1756  * given width. This is useful for text items whose requested height may change
1757  * depending on the allocated width.
1758  *
1759  * Returns: the requested height of the item, given the allocated width,
1760  *  or %-1 if the item doesn't support this method or its height doesn't
1761  *  change when allocated different widths.
1762  **/
1763 gdouble
goo_canvas_item_get_requested_height(GooCanvasItem * item,cairo_t * cr,gdouble width)1764 goo_canvas_item_get_requested_height (GooCanvasItem       *item,
1765 				      cairo_t		  *cr,
1766 				      gdouble              width)
1767 {
1768   GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
1769 
1770   if (iface->get_requested_height)
1771     return iface->get_requested_height (item, cr, width);
1772   else
1773     return -1;
1774 }
1775 
1776 
1777 /**
1778  * goo_canvas_item_allocate_area:
1779  * @item: a #GooCanvasItem.
1780  * @cr: a cairo context.
1781  * @requested_area: the area that the item originally requested, in the
1782  *  parent's coordinate space.
1783  * @allocated_area: the area that the item has been allocated, in the parent's
1784  *  coordinate space.
1785  * @x_offset: the x offset of the allocated area from the requested area in
1786  *  the device coordinate space.
1787  * @y_offset: the y offset of the allocated area from the requested area in
1788  *  the device coordinate space.
1789  *
1790  * This function is only intended to be used when implementing new canvas
1791  * items, specifically layout items such as #GooCanvasTable.
1792  *
1793  * It allocates an area to a child #GooCanvasItem.
1794  *
1795  * Note that the parent layout item will use a transform to move each of its
1796  * children for the layout, so there is no need for the child item to
1797  * reposition itself. It only needs to recalculate its device bounds.
1798  *
1799  * To help recalculate the item's device bounds, the @x_offset and @y_offset
1800  * of the child item's allocated position from its requested position are
1801  * provided. Simple items can just add these to their bounds.
1802  **/
1803 void
goo_canvas_item_allocate_area(GooCanvasItem * item,cairo_t * cr,const GooCanvasBounds * requested_area,const GooCanvasBounds * allocated_area,gdouble x_offset,gdouble y_offset)1804 goo_canvas_item_allocate_area      (GooCanvasItem         *item,
1805 				    cairo_t               *cr,
1806 				    const GooCanvasBounds *requested_area,
1807 				    const GooCanvasBounds *allocated_area,
1808 				    gdouble                x_offset,
1809 				    gdouble                y_offset)
1810 {
1811   GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item);
1812 
1813   iface->allocate_area (item, cr, requested_area, allocated_area,
1814 			x_offset, y_offset);
1815 }
1816 
1817 
1818 /*
1819  * Child Properties.
1820  */
1821 static inline void
item_get_child_property(GObject * object,GObject * child,GParamSpec * pspec,GValue * value,gboolean is_model)1822 item_get_child_property (GObject      *object,
1823 			 GObject      *child,
1824 			 GParamSpec   *pspec,
1825 			 GValue       *value,
1826 			 gboolean      is_model)
1827 {
1828   GObjectClass *class;
1829 
1830   class = g_type_class_peek (pspec->owner_type);
1831 
1832   if (is_model)
1833     {
1834       GooCanvasItemModelIface *iface;
1835 
1836       iface = g_type_interface_peek (class, GOO_TYPE_CANVAS_ITEM_MODEL);
1837       iface->get_child_property ((GooCanvasItemModel*) object,
1838 				 (GooCanvasItemModel*) child,
1839 				 pspec->param_id, value, pspec);
1840     }
1841   else
1842     {
1843       GooCanvasItemIface *iface;
1844 
1845       iface = g_type_interface_peek (class, GOO_TYPE_CANVAS_ITEM);
1846       iface->get_child_property ((GooCanvasItem*) object,
1847 				 (GooCanvasItem*) child,
1848 				 pspec->param_id, value, pspec);
1849     }
1850 }
1851 
1852 
1853 void
_goo_canvas_item_get_child_property_internal(GObject * object,GObject * child,const gchar * property_name,GValue * value,GParamSpecPool * property_pool,gboolean is_model)1854 _goo_canvas_item_get_child_property_internal (GObject              *object,
1855 					      GObject              *child,
1856 					      const gchar          *property_name,
1857 					      GValue               *value,
1858 					      GParamSpecPool       *property_pool,
1859 					      gboolean              is_model)
1860 {
1861   GParamSpec *pspec;
1862 
1863   g_object_ref (object);
1864   g_object_ref (child);
1865   pspec = g_param_spec_pool_lookup (property_pool, property_name,
1866 				    G_OBJECT_TYPE (object), TRUE);
1867   if (!pspec)
1868     g_warning ("%s: class `%s' has no child property named `%s'",
1869 	       G_STRLOC,
1870 	       G_OBJECT_TYPE_NAME (object),
1871 	       property_name);
1872   else if (!(pspec->flags & G_PARAM_READABLE))
1873     g_warning ("%s: child property `%s' of class `%s' is not readable",
1874 	       G_STRLOC,
1875 	       pspec->name,
1876 	       G_OBJECT_TYPE_NAME (object));
1877   else
1878     {
1879       GValue *prop_value, tmp_value = { 0, };
1880 
1881       /* auto-conversion of the callers value type
1882        */
1883       if (G_VALUE_TYPE (value) == G_PARAM_SPEC_VALUE_TYPE (pspec))
1884 	{
1885 	  g_value_reset (value);
1886 	  prop_value = value;
1887 	}
1888       else if (!g_value_type_transformable (G_PARAM_SPEC_VALUE_TYPE (pspec), G_VALUE_TYPE (value)))
1889 	{
1890 	  g_warning ("can't retrieve child property `%s' of type `%s' as value of type `%s'",
1891 		     pspec->name,
1892 		     g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
1893 		     G_VALUE_TYPE_NAME (value));
1894 	  g_object_unref (child);
1895 	  g_object_unref (object);
1896 	  return;
1897 	}
1898       else
1899 	{
1900 	  g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1901 	  prop_value = &tmp_value;
1902 	}
1903       item_get_child_property (object, child, pspec, prop_value, is_model);
1904       if (prop_value != value)
1905 	{
1906 	  g_value_transform (prop_value, value);
1907 	  g_value_unset (&tmp_value);
1908 	}
1909     }
1910   g_object_unref (child);
1911   g_object_unref (object);
1912 }
1913 
1914 
1915 void
_goo_canvas_item_get_child_properties_internal(GObject * object,GObject * child,va_list var_args,GParamSpecPool * property_pool,GObjectNotifyContext * notify_context,gboolean is_model)1916 _goo_canvas_item_get_child_properties_internal (GObject              *object,
1917 						GObject              *child,
1918 						va_list	              var_args,
1919 						GParamSpecPool       *property_pool,
1920 						GObjectNotifyContext *notify_context,
1921 						gboolean              is_model)
1922 {
1923   g_object_ref (object);
1924   g_object_ref (child);
1925 
1926   for (;;)
1927     {
1928       GValue value = { 0, };
1929       GParamSpec *pspec;
1930       gchar *name, *error = NULL;
1931 
1932       name = va_arg (var_args, gchar*);
1933       if (!name)
1934 	break;
1935 
1936       pspec = g_param_spec_pool_lookup (property_pool, name,
1937 					G_OBJECT_TYPE (object), TRUE);
1938       if (!pspec)
1939 	{
1940 	  g_warning ("%s: class `%s' has no child property named `%s'",
1941 		     G_STRLOC, G_OBJECT_TYPE_NAME (object), name);
1942 	  break;
1943 	}
1944       if (!(pspec->flags & G_PARAM_READABLE))
1945 	{
1946 	  g_warning ("%s: child property `%s' of class `%s' is not readable",
1947 		     G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (object));
1948 	  break;
1949 	}
1950       g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1951       item_get_child_property (object, child, pspec, &value, is_model);
1952       G_VALUE_LCOPY (&value, var_args, 0, &error);
1953       if (error)
1954 	{
1955 	  g_warning ("%s: %s", G_STRLOC, error);
1956 	  g_free (error);
1957 	  g_value_unset (&value);
1958 	  break;
1959 	}
1960       g_value_unset (&value);
1961     }
1962 
1963   g_object_unref (child);
1964   g_object_unref (object);
1965 }
1966 
1967 
1968 static inline void
canvas_item_set_child_property(GObject * object,GObject * child,GParamSpec * pspec,const GValue * value,GObjectNotifyQueue * nqueue,gboolean is_model)1969 canvas_item_set_child_property (GObject            *object,
1970 				GObject            *child,
1971 				GParamSpec         *pspec,
1972 				const GValue       *value,
1973 				GObjectNotifyQueue *nqueue,
1974 				gboolean            is_model)
1975 {
1976   GValue tmp_value = { 0, };
1977 
1978   /* provide a copy to work from, convert (if necessary) and validate */
1979   g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1980   if (!g_value_transform (value, &tmp_value))
1981     g_warning ("unable to set child property `%s' of type `%s' from value of type `%s'",
1982 	       pspec->name,
1983 	       g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
1984 	       G_VALUE_TYPE_NAME (value));
1985   else if (g_param_value_validate (pspec, &tmp_value) && !(pspec->flags & G_PARAM_LAX_VALIDATION))
1986     {
1987       gchar *contents = g_strdup_value_contents (value);
1988 
1989       g_warning ("value \"%s\" of type `%s' is invalid for property `%s' of type `%s'",
1990 		 contents,
1991 		 G_VALUE_TYPE_NAME (value),
1992 		 pspec->name,
1993 		 g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)));
1994       g_free (contents);
1995     }
1996   else
1997     {
1998       GObjectClass *class = g_type_class_peek (pspec->owner_type);
1999 
2000       if (is_model)
2001 	{
2002 	  GooCanvasItemModelIface *iface;
2003 
2004 	  iface = g_type_interface_peek (class, GOO_TYPE_CANVAS_ITEM_MODEL);
2005 	  iface->set_child_property ((GooCanvasItemModel*) object,
2006 				     (GooCanvasItemModel*) child,
2007 				     pspec->param_id, &tmp_value, pspec);
2008 	}
2009       else
2010 	{
2011 	  GooCanvasItemIface *iface;
2012 
2013 	  iface = g_type_interface_peek (class, GOO_TYPE_CANVAS_ITEM);
2014 	  iface->set_child_property ((GooCanvasItem*) object,
2015 				     (GooCanvasItem*) child,
2016 				     pspec->param_id, &tmp_value, pspec);
2017 	}
2018 
2019       g_object_notify_queue_add (G_OBJECT (child), nqueue, pspec);
2020     }
2021   g_value_unset (&tmp_value);
2022 }
2023 
2024 
2025 void
_goo_canvas_item_set_child_property_internal(GObject * object,GObject * child,const gchar * property_name,const GValue * value,GParamSpecPool * property_pool,GObjectNotifyContext * notify_context,gboolean is_model)2026 _goo_canvas_item_set_child_property_internal (GObject              *object,
2027 					      GObject              *child,
2028 					      const gchar          *property_name,
2029 					      const GValue         *value,
2030 					      GParamSpecPool       *property_pool,
2031 					      GObjectNotifyContext *notify_context,
2032 					      gboolean              is_model)
2033 {
2034   GObjectNotifyQueue *nqueue;
2035   GParamSpec *pspec;
2036 
2037   g_object_ref (object);
2038   g_object_ref (child);
2039 
2040   nqueue = g_object_notify_queue_freeze (child, notify_context);
2041   pspec = g_param_spec_pool_lookup (property_pool, property_name,
2042 				    G_OBJECT_TYPE (object), TRUE);
2043   if (!pspec)
2044     g_warning ("%s: class `%s' has no child property named `%s'",
2045 	       G_STRLOC,
2046 	       G_OBJECT_TYPE_NAME (object),
2047 	       property_name);
2048   else if (!(pspec->flags & G_PARAM_WRITABLE))
2049     g_warning ("%s: child property `%s' of class `%s' is not writable",
2050 	       G_STRLOC,
2051 	       pspec->name,
2052 	       G_OBJECT_TYPE_NAME (object));
2053   else
2054     {
2055       canvas_item_set_child_property (object, child, pspec,
2056 				      value, nqueue, is_model);
2057     }
2058   g_object_notify_queue_thaw (child, nqueue);
2059   g_object_unref (object);
2060   g_object_unref (child);
2061 }
2062 
2063 
2064 void
_goo_canvas_item_set_child_properties_internal(GObject * object,GObject * child,va_list var_args,GParamSpecPool * property_pool,GObjectNotifyContext * notify_context,gboolean is_model)2065 _goo_canvas_item_set_child_properties_internal (GObject              *object,
2066 						GObject              *child,
2067 						va_list	              var_args,
2068 						GParamSpecPool       *property_pool,
2069 						GObjectNotifyContext *notify_context,
2070 						gboolean              is_model)
2071 {
2072   GObjectNotifyQueue *nqueue;
2073 
2074   g_object_ref (object);
2075   g_object_ref (child);
2076 
2077   nqueue = g_object_notify_queue_freeze (child, notify_context);
2078 
2079   for (;;)
2080     {
2081       GValue value = { 0, };
2082       GParamSpec *pspec;
2083       gchar *name, *error = NULL;
2084 
2085       name = va_arg (var_args, gchar*);
2086       if (!name)
2087 	break;
2088 
2089       pspec = g_param_spec_pool_lookup (property_pool, name,
2090 					G_OBJECT_TYPE (object), TRUE);
2091       if (!pspec)
2092 	{
2093 	  g_warning ("%s: class `%s' has no child property named `%s'",
2094 		     G_STRLOC, G_OBJECT_TYPE_NAME (object), name);
2095 	  break;
2096 	}
2097       if (!(pspec->flags & G_PARAM_WRITABLE))
2098 	{
2099 	  g_warning ("%s: child property `%s' of class `%s' is not writable",
2100 		     G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (object));
2101 	  break;
2102 	}
2103       g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
2104       G_VALUE_COLLECT (&value, var_args, 0, &error);
2105       if (error)
2106 	{
2107 	  g_warning ("%s: %s", G_STRLOC, error);
2108 	  g_free (error);
2109 
2110 	  /* we purposely leak the value here, it might not be
2111 	   * in a sane state if an error condition occoured
2112 	   */
2113 	  break;
2114 	}
2115       canvas_item_set_child_property (object, child, pspec, &value, nqueue,
2116 				      is_model);
2117       g_value_unset (&value);
2118     }
2119   g_object_notify_queue_thaw (G_OBJECT (child), nqueue);
2120 
2121   g_object_unref (object);
2122   g_object_unref (child);
2123 }
2124 
2125 
2126 /**
2127  * goo_canvas_item_get_child_property:
2128  * @item: a #GooCanvasItem.
2129  * @child: a child #GooCanvasItem.
2130  * @property_name: the name of the child property to get.
2131  * @value: a location to return the value.
2132  *
2133  * Gets a child property of @child.
2134  **/
2135 void
goo_canvas_item_get_child_property(GooCanvasItem * item,GooCanvasItem * child,const gchar * property_name,GValue * value)2136 goo_canvas_item_get_child_property (GooCanvasItem *item,
2137 				    GooCanvasItem *child,
2138 				    const gchar   *property_name,
2139 				    GValue        *value)
2140 {
2141   g_return_if_fail (GOO_IS_CANVAS_ITEM (item));
2142   g_return_if_fail (GOO_IS_CANVAS_ITEM (child));
2143   g_return_if_fail (property_name != NULL);
2144   g_return_if_fail (G_IS_VALUE (value));
2145 
2146   _goo_canvas_item_get_child_property_internal ((GObject*) item, (GObject*) child, property_name, value, _goo_canvas_item_child_property_pool, FALSE);
2147 }
2148 
2149 
2150 /**
2151  * goo_canvas_item_set_child_property:
2152  * @item: a #GooCanvasItem.
2153  * @child: a child #GooCanvasItem.
2154  * @property_name: the name of the child property to set.
2155  * @value: the value to set the property to.
2156  *
2157  * Sets a child property of @child.
2158  **/
2159 void
goo_canvas_item_set_child_property(GooCanvasItem * item,GooCanvasItem * child,const gchar * property_name,const GValue * value)2160 goo_canvas_item_set_child_property (GooCanvasItem   *item,
2161 				    GooCanvasItem   *child,
2162 				    const gchar     *property_name,
2163 				    const GValue    *value)
2164 {
2165   g_return_if_fail (GOO_IS_CANVAS_ITEM (item));
2166   g_return_if_fail (GOO_IS_CANVAS_ITEM (child));
2167   g_return_if_fail (property_name != NULL);
2168   g_return_if_fail (G_IS_VALUE (value));
2169 
2170   _goo_canvas_item_set_child_property_internal ((GObject*) item, (GObject*) child, property_name, value, _goo_canvas_item_child_property_pool, _goo_canvas_item_child_property_notify_context, FALSE);
2171 }
2172 
2173 
2174 /**
2175  * goo_canvas_item_get_child_properties_valist:
2176  * @item: a #GooCanvasItem.
2177  * @child: a child #GooCanvasItem.
2178  * @var_args: pairs of property names and value pointers, and a terminating
2179  *  %NULL.
2180  *
2181  * Gets the values of one or more child properties of @child.
2182  **/
2183 void
goo_canvas_item_get_child_properties_valist(GooCanvasItem * item,GooCanvasItem * child,va_list var_args)2184 goo_canvas_item_get_child_properties_valist (GooCanvasItem   *item,
2185 					     GooCanvasItem   *child,
2186 					     va_list	      var_args)
2187 {
2188   g_return_if_fail (GOO_IS_CANVAS_ITEM (item));
2189   g_return_if_fail (GOO_IS_CANVAS_ITEM (child));
2190 
2191   _goo_canvas_item_get_child_properties_internal ((GObject*) item, (GObject*) child, var_args, _goo_canvas_item_child_property_pool, _goo_canvas_item_child_property_notify_context, FALSE);
2192 }
2193 
2194 
2195 /**
2196  * goo_canvas_item_set_child_properties_valist:
2197  * @item: a #GooCanvasItem.
2198  * @child: a child #GooCanvasItem.
2199  * @var_args: pairs of property names and values, and a terminating %NULL.
2200  *
2201  * Sets the values of one or more child properties of @child.
2202  **/
2203 void
goo_canvas_item_set_child_properties_valist(GooCanvasItem * item,GooCanvasItem * child,va_list var_args)2204 goo_canvas_item_set_child_properties_valist (GooCanvasItem   *item,
2205 					     GooCanvasItem   *child,
2206 					     va_list	      var_args)
2207 {
2208   g_return_if_fail (GOO_IS_CANVAS_ITEM (item));
2209   g_return_if_fail (GOO_IS_CANVAS_ITEM (child));
2210 
2211   _goo_canvas_item_set_child_properties_internal ((GObject*) item, (GObject*) child, var_args, _goo_canvas_item_child_property_pool, _goo_canvas_item_child_property_notify_context, FALSE);
2212 }
2213 
2214 
2215 /**
2216  * goo_canvas_item_get_child_properties:
2217  * @item: a #GooCanvasItem.
2218  * @child: a child #GooCanvasItem.
2219  * @...: pairs of property names and value pointers, and a terminating %NULL.
2220  *
2221  * Gets the values of one or more child properties of @child.
2222  **/
2223 void
goo_canvas_item_get_child_properties(GooCanvasItem * item,GooCanvasItem * child,...)2224 goo_canvas_item_get_child_properties        (GooCanvasItem   *item,
2225 					     GooCanvasItem   *child,
2226 					     ...)
2227 {
2228   va_list var_args;
2229 
2230   va_start (var_args, child);
2231   goo_canvas_item_get_child_properties_valist (item, child, var_args);
2232   va_end (var_args);
2233 }
2234 
2235 
2236 /**
2237  * goo_canvas_item_set_child_properties:
2238  * @item: a #GooCanvasItem.
2239  * @child: a child #GooCanvasItem.
2240  * @...: pairs of property names and values, and a terminating %NULL.
2241  *
2242  * Sets the values of one or more child properties of @child.
2243  **/
2244 void
goo_canvas_item_set_child_properties(GooCanvasItem * item,GooCanvasItem * child,...)2245 goo_canvas_item_set_child_properties        (GooCanvasItem   *item,
2246 					     GooCanvasItem   *child,
2247 					     ...)
2248 {
2249   va_list var_args;
2250 
2251   va_start (var_args, child);
2252   goo_canvas_item_set_child_properties_valist (item, child, var_args);
2253   va_end (var_args);
2254 }
2255 
2256 
2257 
2258 /**
2259  * goo_canvas_item_class_install_child_property:
2260  * @iclass: a #GObjectClass
2261  * @property_id: the id for the property
2262  * @pspec: the #GParamSpec for the property
2263  *
2264  * This function is only intended to be used when implementing new canvas
2265  * items, specifically layout container items such as #GooCanvasTable.
2266  *
2267  * It installs a child property on a canvas item class.
2268  **/
2269 void
goo_canvas_item_class_install_child_property(GObjectClass * iclass,guint property_id,GParamSpec * pspec)2270 goo_canvas_item_class_install_child_property (GObjectClass *iclass,
2271 					      guint         property_id,
2272 					      GParamSpec   *pspec)
2273 {
2274   g_return_if_fail (G_IS_OBJECT_CLASS (iclass));
2275   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
2276   g_return_if_fail (property_id > 0);
2277 
2278   if (g_param_spec_pool_lookup (_goo_canvas_item_child_property_pool,
2279 				pspec->name, G_OBJECT_CLASS_TYPE (iclass),
2280 				FALSE))
2281     {
2282       g_warning (G_STRLOC ": class `%s' already contains a child property named `%s'",
2283 		 G_OBJECT_CLASS_NAME (iclass), pspec->name);
2284       return;
2285     }
2286   g_param_spec_ref (pspec);
2287   g_param_spec_sink (pspec);
2288   pspec->param_id = property_id;
2289   g_param_spec_pool_insert (_goo_canvas_item_child_property_pool, pspec,
2290 			    G_OBJECT_CLASS_TYPE (iclass));
2291 }
2292 
2293 /**
2294  * goo_canvas_item_class_find_child_property:
2295  * @iclass: a #GObjectClass
2296  * @property_name: the name of the child property to find
2297  * @returns: the #GParamSpec of the child property or %NULL if @class has no
2298  *   child property with that name.
2299  *
2300  * This function is only intended to be used when implementing new canvas
2301  * items, specifically layout container items such as #GooCanvasTable.
2302  *
2303  * It finds a child property of a canvas item class by name.
2304  */
2305 GParamSpec*
goo_canvas_item_class_find_child_property(GObjectClass * iclass,const gchar * property_name)2306 goo_canvas_item_class_find_child_property (GObjectClass *iclass,
2307 					   const gchar  *property_name)
2308 {
2309   g_return_val_if_fail (G_IS_OBJECT_CLASS (iclass), NULL);
2310   g_return_val_if_fail (property_name != NULL, NULL);
2311 
2312   return g_param_spec_pool_lookup (_goo_canvas_item_child_property_pool,
2313 				   property_name, G_OBJECT_CLASS_TYPE (iclass),
2314 				   TRUE);
2315 }
2316 
2317 /**
2318  * goo_canvas_item_class_list_child_properties:
2319  * @iclass: a #GObjectClass
2320  * @n_properties: location to return the number of child properties found
2321  * @returns: a newly allocated array of #GParamSpec*. The array must be
2322  *           freed with g_free().
2323  *
2324  * This function is only intended to be used when implementing new canvas
2325  * items, specifically layout container items such as #GooCanvasTable.
2326  *
2327  * It returns all child properties of a canvas item class.
2328  */
2329 GParamSpec**
goo_canvas_item_class_list_child_properties(GObjectClass * iclass,guint * n_properties)2330 goo_canvas_item_class_list_child_properties (GObjectClass *iclass,
2331 					     guint        *n_properties)
2332 {
2333   GParamSpec **pspecs;
2334   guint n;
2335 
2336   g_return_val_if_fail (G_IS_OBJECT_CLASS (iclass), NULL);
2337 
2338   pspecs = g_param_spec_pool_list (_goo_canvas_item_child_property_pool,
2339 				   G_OBJECT_CLASS_TYPE (iclass), &n);
2340   if (n_properties)
2341     *n_properties = n;
2342 
2343   return pspecs;
2344 }
2345