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