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