~ml-launchpad/ubuntu/natty/gcompris/fix-for-777349

« back to all changes in this revision

Viewing changes to src/goocanvas/src/goocanvasgroup.c

  • Committer: Bazaar Package Importer
  • Author(s): Marc Gariepy, Marc Gariepy, Stephane Graber
  • Date: 2010-01-04 17:42:49 UTC
  • mfrom: (1.1.14 upstream)
  • Revision ID: james.westby@ubuntu.com-20100104174249-7bupatd9dtxyhvs4
Tags: 9.0-0ubuntu1
[Marc Gariepy]
* New upstream release (9.0).
* Remove cache.c from POTFILES to avoid FTBFS
* Remove unneeded rm in debian/rules (file no longer exists upstream)

[Stephane Graber]
* Bump Debian standards to 3.8.3
* Add patch system (dpatch)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * GooCanvas. Copyright (C) 2005 Damon Chaplin.
 
3
 * Released under the GNU LGPL license. See COPYING for details.
 
4
 *
 
5
 * goocanvasgroup.c - group item.
 
6
 */
 
7
 
 
8
/**
 
9
 * SECTION:goocanvasgroup
 
10
 * @Title: GooCanvasGroup
 
11
 * @Short_Description: a group of items.
 
12
 *
 
13
 * #GooCanvasGroup represents a group of items. Groups can be nested to
 
14
 * any depth, to create a hierarchy of items. Items are ordered within each
 
15
 * group, with later items being displayed above earlier items.
 
16
 *
 
17
 * #GooCanvasGroup is a subclass of #GooCanvasItemSimple and so
 
18
 * inherits all of the style properties such as "stroke-color", "fill-color"
 
19
 * and "line-width". Setting a style property on a #GooCanvasGroup will affect
 
20
 * all children of the #GooCanvasGroup (unless the children override the
 
21
 * property setting).
 
22
 *
 
23
 * #GooCanvasGroup implements the #GooCanvasItem interface, so you can use
 
24
 * the #GooCanvasItem functions such as goo_canvas_item_raise() and
 
25
 * goo_canvas_item_rotate(), and the properties such as "visibility" and
 
26
 * "pointer-events".
 
27
 *
 
28
 * If the #GooCanvasGroup:width and #GooCanvasGroup:height properties are
 
29
 * set to positive values then the group is clipped to the given size.
 
30
 *
 
31
 * To create a #GooCanvasGroup use goo_canvas_group_new().
 
32
 *
 
33
 * To get or set the properties of an existing #GooCanvasGroup, use
 
34
 * g_object_get() and g_object_set().
 
35
 */
 
36
#include <config.h>
 
37
#include <glib/gi18n-lib.h>
 
38
#include <gtk/gtk.h>
 
39
#include "goocanvasprivate.h"
 
40
#include "goocanvasgroup.h"
 
41
#include "goocanvasitemmodel.h"
 
42
#include "goocanvas.h"
 
43
#include "goocanvasmarshal.h"
 
44
#include "goocanvasatk.h"
 
45
 
 
46
typedef struct _GooCanvasGroupPrivate GooCanvasGroupPrivate;
 
47
struct _GooCanvasGroupPrivate {
 
48
  gdouble x;
 
49
  gdouble y;
 
50
  gdouble width;
 
51
  gdouble height;
 
52
};
 
53
 
 
54
#define GOO_CANVAS_GROUP_GET_PRIVATE(group)  \
 
55
   (G_TYPE_INSTANCE_GET_PRIVATE ((group), GOO_TYPE_CANVAS_GROUP, GooCanvasGroupPrivate))
 
56
#define GOO_CANVAS_GROUP_MODEL_GET_PRIVATE(group)  \
 
57
   (G_TYPE_INSTANCE_GET_PRIVATE ((group), GOO_TYPE_CANVAS_GROUP_MODEL, GooCanvasGroupPrivate))
 
58
 
 
59
enum {
 
60
  PROP_0,
 
61
 
 
62
  PROP_X,
 
63
  PROP_Y,
 
64
  PROP_WIDTH,
 
65
  PROP_HEIGHT
 
66
};
 
67
 
 
68
static void goo_canvas_group_dispose      (GObject            *object);
 
69
static void goo_canvas_group_finalize     (GObject            *object);
 
70
static void goo_canvas_group_get_property (GObject            *object,
 
71
                                           guint               prop_id,
 
72
                                           GValue             *value,
 
73
                                           GParamSpec         *pspec);
 
74
static void goo_canvas_group_set_property (GObject            *object,
 
75
                                           guint               prop_id,
 
76
                                           const GValue       *value,
 
77
                                           GParamSpec         *pspec);
 
78
static void canvas_item_interface_init    (GooCanvasItemIface *iface);
 
79
 
 
80
G_DEFINE_TYPE_WITH_CODE (GooCanvasGroup, goo_canvas_group,
 
81
                         GOO_TYPE_CANVAS_ITEM_SIMPLE,
 
82
                         G_IMPLEMENT_INTERFACE (GOO_TYPE_CANVAS_ITEM,
 
83
                                                canvas_item_interface_init))
 
84
 
 
85
static void
 
86
goo_canvas_group_install_common_properties (GObjectClass *gobject_class)
 
87
{
 
88
  g_object_class_install_property (gobject_class, PROP_X,
 
89
                                  g_param_spec_double ("x",
 
90
                                                       "X",
 
91
                                                       _("The x coordinate of the group"),
 
92
                                                       -G_MAXDOUBLE,
 
93
                                                       G_MAXDOUBLE, 0.0,
 
94
                                                       G_PARAM_READWRITE));
 
95
 
 
96
  g_object_class_install_property (gobject_class, PROP_Y,
 
97
                                  g_param_spec_double ("y",
 
98
                                                       "Y",
 
99
                                                       _("The y coordinate of the group"),
 
100
                                                       -G_MAXDOUBLE,
 
101
                                                       G_MAXDOUBLE, 0.0,
 
102
                                                       G_PARAM_READWRITE));
 
103
 
 
104
  g_object_class_install_property (gobject_class, PROP_WIDTH,
 
105
                                  g_param_spec_double ("width",
 
106
                                                       _("Width"),
 
107
                                                       _("The width of the group, or -1 to use the default width"),
 
108
                                                       -G_MAXDOUBLE,
 
109
                                                       G_MAXDOUBLE, -1.0,
 
110
                                                       G_PARAM_READWRITE));
 
111
 
 
112
  g_object_class_install_property (gobject_class, PROP_HEIGHT,
 
113
                                  g_param_spec_double ("height",
 
114
                                                       _("Height"),
 
115
                                                       _("The height of the group, or -1 to use the default height"),
 
116
                                                       -G_MAXDOUBLE,
 
117
                                                       G_MAXDOUBLE, -1.0,
 
118
                                                       G_PARAM_READWRITE));
 
119
}
 
120
 
 
121
static void
 
122
goo_canvas_group_class_init (GooCanvasGroupClass *klass)
 
123
{
 
124
  GObjectClass *gobject_class = (GObjectClass*) klass;
 
125
 
 
126
  g_type_class_add_private (gobject_class, sizeof (GooCanvasGroupPrivate));
 
127
 
 
128
  gobject_class->dispose  = goo_canvas_group_dispose;
 
129
  gobject_class->finalize = goo_canvas_group_finalize;
 
130
  gobject_class->get_property = goo_canvas_group_get_property;
 
131
  gobject_class->set_property = goo_canvas_group_set_property;
 
132
 
 
133
  /* Register our accessible factory, but only if accessibility is enabled. */
 
134
  if (!ATK_IS_NO_OP_OBJECT_FACTORY (atk_registry_get_factory (atk_get_default_registry (), GTK_TYPE_WIDGET)))
 
135
    {
 
136
      atk_registry_set_factory_type (atk_get_default_registry (),
 
137
                                     GOO_TYPE_CANVAS_GROUP,
 
138
                                     goo_canvas_item_accessible_factory_get_type ());
 
139
    }
 
140
 
 
141
  goo_canvas_group_install_common_properties (gobject_class);
 
142
}
 
143
 
 
144
 
 
145
static void
 
146
goo_canvas_group_init (GooCanvasGroup *group)
 
147
{
 
148
  GooCanvasGroupPrivate* priv = GOO_CANVAS_GROUP_GET_PRIVATE (group);
 
149
 
 
150
  group->items = g_ptr_array_sized_new (8);
 
151
 
 
152
  priv->x = 0.0;
 
153
  priv->y = 0.0;
 
154
  priv->width = -1.0;
 
155
  priv->height = -1.0;
 
156
}
 
157
 
 
158
 
 
159
/**
 
160
 * goo_canvas_group_new:
 
161
 * @parent: the parent item, or %NULL. If a parent is specified, it will assume
 
162
 *  ownership of the item, and the item will automatically be freed when it is
 
163
 *  removed from the parent. Otherwise call g_object_unref() to free it.
 
164
 * @...: optional pairs of property names and values, and a terminating %NULL.
 
165
 * 
 
166
 * Creates a new group item.
 
167
 * 
 
168
 * Return value: a new group item.
 
169
 **/
 
170
GooCanvasItem*
 
171
goo_canvas_group_new (GooCanvasItem *parent,
 
172
                      ...)
 
173
{
 
174
  GooCanvasItem *item;
 
175
  GooCanvasGroup *group;
 
176
  va_list var_args;
 
177
  const char *first_property;
 
178
 
 
179
  item = g_object_new (GOO_TYPE_CANVAS_GROUP, NULL);
 
180
  group = (GooCanvasGroup*) item;
 
181
 
 
182
  va_start (var_args, parent);
 
183
  first_property = va_arg (var_args, char*);
 
184
  if (first_property)
 
185
    g_object_set_valist (G_OBJECT (item), first_property, var_args);
 
186
  va_end (var_args);
 
187
 
 
188
  if (parent)
 
189
    {
 
190
      goo_canvas_item_add_child (parent, item, -1);
 
191
      g_object_unref (item);
 
192
    }
 
193
 
 
194
  return item;
 
195
}
 
196
 
 
197
 
 
198
static void
 
199
goo_canvas_group_dispose (GObject *object)
 
200
{
 
201
  GooCanvasGroup *group = (GooCanvasGroup*) object;
 
202
  gint i;
 
203
 
 
204
  /* Unref all the items in the group. */
 
205
  for (i = 0; i < group->items->len; i++)
 
206
    {
 
207
      GooCanvasItem *item = group->items->pdata[i];
 
208
      goo_canvas_item_set_parent (item, NULL);
 
209
      g_object_unref (item);
 
210
    }
 
211
 
 
212
  g_ptr_array_set_size (group->items, 0);
 
213
 
 
214
  G_OBJECT_CLASS (goo_canvas_group_parent_class)->dispose (object);
 
215
}
 
216
 
 
217
 
 
218
static void
 
219
goo_canvas_group_finalize (GObject *object)
 
220
{
 
221
  GooCanvasGroup *group = (GooCanvasGroup*) object;
 
222
 
 
223
  g_ptr_array_free (group->items, TRUE);
 
224
 
 
225
  G_OBJECT_CLASS (goo_canvas_group_parent_class)->finalize (object);
 
226
}
 
227
 
 
228
 
 
229
/* Gets the private data to use, from the model or from the item itself. */
 
230
static GooCanvasGroupPrivate*
 
231
goo_canvas_group_get_private (GooCanvasGroup *group)
 
232
{
 
233
  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) group;
 
234
 
 
235
  if (simple->model)
 
236
    return GOO_CANVAS_GROUP_MODEL_GET_PRIVATE (simple->model);
 
237
  else
 
238
    return GOO_CANVAS_GROUP_GET_PRIVATE (group);
 
239
}
 
240
 
 
241
 
 
242
static void
 
243
goo_canvas_group_get_common_property (GObject               *object,
 
244
                                      GooCanvasGroupPrivate *priv,
 
245
                                      guint                  prop_id,
 
246
                                      GValue                *value,
 
247
                                      GParamSpec            *pspec)
 
248
{
 
249
  switch (prop_id)
 
250
    {
 
251
    case PROP_X:
 
252
      g_value_set_double (value, priv->x);
 
253
      break;
 
254
    case PROP_Y:
 
255
      g_value_set_double (value, priv->y);
 
256
      break;
 
257
    case PROP_WIDTH:
 
258
      g_value_set_double (value, priv->width);
 
259
      break;
 
260
    case PROP_HEIGHT:
 
261
      g_value_set_double (value, priv->height);
 
262
      break;
 
263
    default:
 
264
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
265
      break;
 
266
    }
 
267
}
 
268
 
 
269
static void
 
270
goo_canvas_group_get_property (GObject               *object,
 
271
                               guint                  prop_id,
 
272
                               GValue                *value,
 
273
                               GParamSpec            *pspec)
 
274
{
 
275
  GooCanvasGroup *group = (GooCanvasGroup*) object;
 
276
  GooCanvasGroupPrivate *priv = goo_canvas_group_get_private (group);
 
277
 
 
278
  goo_canvas_group_get_common_property (object, priv, prop_id, value, pspec);
 
279
}
 
280
 
 
281
static void
 
282
goo_canvas_group_set_common_property (GObject               *object,
 
283
                                      GooCanvasGroupPrivate *priv,
 
284
                                      guint                  prop_id,
 
285
                                      const GValue          *value,
 
286
                                      GParamSpec            *pspec)
 
287
{
 
288
  switch (prop_id)
 
289
    {
 
290
    case PROP_X:
 
291
      priv->x = g_value_get_double (value);
 
292
      break;
 
293
    case PROP_Y:
 
294
      priv->y = g_value_get_double (value);
 
295
      break;
 
296
    case PROP_WIDTH:
 
297
      priv->width = g_value_get_double (value);
 
298
      break;
 
299
    case PROP_HEIGHT:
 
300
      priv->height = g_value_get_double (value);
 
301
      break;
 
302
    default:
 
303
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
304
      break;
 
305
    }
 
306
}
 
307
 
 
308
static void
 
309
goo_canvas_group_set_property (GObject                  *object,
 
310
                               guint                     prop_id,
 
311
                               const GValue             *value,
 
312
                               GParamSpec               *pspec)
 
313
{
 
314
  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) object;
 
315
  GooCanvasGroup *group = (GooCanvasGroup*) object;
 
316
  GooCanvasGroupPrivate *priv = goo_canvas_group_get_private (group);
 
317
 
 
318
  if (simple->model)
 
319
    {
 
320
      g_warning ("Can't set property of a canvas item with a model - set the model property instead");
 
321
      return;
 
322
    }
 
323
 
 
324
  goo_canvas_group_set_common_property (object, priv, prop_id, value, pspec);
 
325
  goo_canvas_item_simple_changed (simple, TRUE);
 
326
}
 
327
 
 
328
static void
 
329
goo_canvas_group_add_child     (GooCanvasItem  *item,
 
330
                                GooCanvasItem  *child,
 
331
                                gint            position)
 
332
{
 
333
  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item;
 
334
  GooCanvasGroup *group = (GooCanvasGroup*) item;
 
335
  AtkObject *atk_obj, *child_atk_obj;
 
336
 
 
337
  g_object_ref (child);
 
338
 
 
339
  if (position >= 0)
 
340
    {
 
341
      goo_canvas_util_ptr_array_insert (group->items, child, position);
 
342
    }
 
343
  else
 
344
    {
 
345
      position = group->items->len;
 
346
      g_ptr_array_add (group->items, child);
 
347
    }
 
348
 
 
349
  goo_canvas_item_set_parent (child, item);
 
350
  goo_canvas_item_set_is_static (child, simple->simple_data->is_static);
 
351
 
 
352
  /* Emit the "children_changed" ATK signal, if ATK is enabled. */
 
353
  atk_obj = atk_gobject_accessible_for_object (G_OBJECT (item));
 
354
  if (!ATK_IS_NO_OP_OBJECT (atk_obj))
 
355
    {
 
356
      child_atk_obj = atk_gobject_accessible_for_object (G_OBJECT (child));
 
357
      g_signal_emit_by_name (atk_obj, "children_changed::add",
 
358
                             position, child_atk_obj);
 
359
    }
 
360
 
 
361
  goo_canvas_item_request_update (item);
 
362
}
 
363
 
 
364
 
 
365
static void
 
366
goo_canvas_group_move_child    (GooCanvasItem  *item,
 
367
                                gint            old_position,
 
368
                                gint            new_position)
 
369
{
 
370
  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item;
 
371
  GooCanvasGroup *group = (GooCanvasGroup*) item;
 
372
  GooCanvasItem *child;
 
373
  GooCanvasBounds bounds;
 
374
 
 
375
  /* Request a redraw of the item's bounds. */
 
376
  child = group->items->pdata[old_position];
 
377
  if (simple->canvas)
 
378
    {
 
379
      goo_canvas_item_get_bounds (child, &bounds);
 
380
      goo_canvas_request_item_redraw (simple->canvas, &bounds,
 
381
                                      simple->simple_data->is_static);
 
382
    }
 
383
 
 
384
  goo_canvas_util_ptr_array_move (group->items, old_position, new_position);
 
385
 
 
386
  goo_canvas_item_request_update (item);
 
387
}
 
388
 
 
389
 
 
390
static void
 
391
goo_canvas_group_remove_child  (GooCanvasItem  *item,
 
392
                                gint            child_num)
 
393
{
 
394
  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item;
 
395
  GooCanvasGroup *group = (GooCanvasGroup*) item;
 
396
  GooCanvasItem *child;
 
397
  GooCanvasBounds bounds;
 
398
  AtkObject *atk_obj, *child_atk_obj;
 
399
 
 
400
  g_return_if_fail (child_num < group->items->len);
 
401
 
 
402
  /* Request a redraw of the item's bounds. */
 
403
  child = group->items->pdata[child_num];
 
404
  if (simple->canvas)
 
405
    {
 
406
      goo_canvas_item_get_bounds (child, &bounds);
 
407
      goo_canvas_request_item_redraw (simple->canvas, &bounds,
 
408
                                      simple->simple_data->is_static);
 
409
    }
 
410
 
 
411
  /* Emit the "children_changed" ATK signal, if ATK is enabled. */
 
412
  atk_obj = atk_gobject_accessible_for_object (G_OBJECT (item));
 
413
  if (!ATK_IS_NO_OP_OBJECT (atk_obj))
 
414
    {
 
415
      child_atk_obj = atk_gobject_accessible_for_object (G_OBJECT (child));
 
416
      g_signal_emit_by_name (atk_obj, "children_changed::remove",
 
417
                             child_num, child_atk_obj);
 
418
    }
 
419
 
 
420
  g_ptr_array_remove_index (group->items, child_num);
 
421
 
 
422
  goo_canvas_item_set_parent (child, NULL);
 
423
  g_object_unref (child);
 
424
 
 
425
  goo_canvas_item_request_update (item);
 
426
}
 
427
 
 
428
 
 
429
static gint
 
430
goo_canvas_group_get_n_children (GooCanvasItem  *item)
 
431
{
 
432
  GooCanvasGroup *group = (GooCanvasGroup*) item;
 
433
 
 
434
  return group->items->len;
 
435
}
 
436
 
 
437
 
 
438
static GooCanvasItem*
 
439
goo_canvas_group_get_child   (GooCanvasItem       *item,
 
440
                              gint                 child_num)
 
441
{
 
442
  GooCanvasGroup *group = (GooCanvasGroup*) item;
 
443
 
 
444
  if (child_num < group->items->len)
 
445
    return group->items->pdata[child_num];
 
446
  return NULL;
 
447
}
 
448
 
 
449
 
 
450
/* This is only used to set the canvas of the root group. It isn't normally
 
451
   needed by apps. */
 
452
static void
 
453
goo_canvas_group_set_canvas  (GooCanvasItem *item,
 
454
                              GooCanvas     *canvas)
 
455
{
 
456
  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item;
 
457
  GooCanvasGroup *group = (GooCanvasGroup*) item;
 
458
  gint i;
 
459
 
 
460
  if (simple->canvas == canvas)
 
461
    return;
 
462
 
 
463
  simple->canvas = canvas;
 
464
 
 
465
  /* Recursively set the canvas of all child items. */
 
466
  for (i = 0; i < group->items->len; i++)
 
467
    {
 
468
      GooCanvasItem *item = group->items->pdata[i];
 
469
      goo_canvas_item_set_canvas (item, canvas);
 
470
    }
 
471
}
 
472
 
 
473
 
 
474
static void
 
475
goo_canvas_group_set_is_static  (GooCanvasItem *item,
 
476
                                 gboolean       is_static)
 
477
{
 
478
  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item;
 
479
  GooCanvasItemSimpleData *simple_data = simple->simple_data;
 
480
  GooCanvasGroup *group = (GooCanvasGroup*) item;
 
481
  gint i;
 
482
 
 
483
  if (simple_data->is_static == is_static)
 
484
    return;
 
485
 
 
486
  simple_data->is_static = is_static;
 
487
 
 
488
  /* Recursively set the canvas of all child items. */
 
489
  for (i = 0; i < group->items->len; i++)
 
490
    {
 
491
      GooCanvasItem *item = group->items->pdata[i];
 
492
      goo_canvas_item_set_is_static (item, is_static);
 
493
    }
 
494
}
 
495
 
 
496
 
 
497
static void
 
498
on_model_child_added (GooCanvasGroupModel *model,
 
499
                      gint                 position,
 
500
                      GooCanvasGroup      *group)
 
501
{
 
502
  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) group;
 
503
  GooCanvasItem *item = (GooCanvasItem*) group;
 
504
  GooCanvasItemModel *child_model;
 
505
  GooCanvasItem *child;
 
506
 
 
507
  /* Create a canvas item for the model. */
 
508
  child_model = goo_canvas_item_model_get_child ((GooCanvasItemModel*) model,
 
509
                                                 position);
 
510
  child = goo_canvas_create_item (simple->canvas, child_model);
 
511
  goo_canvas_item_add_child (item, child, position);
 
512
  g_object_unref (child);
 
513
}
 
514
 
 
515
 
 
516
static void
 
517
on_model_child_moved (GooCanvasGroupModel *model,
 
518
                      gint                 old_position,
 
519
                      gint                 new_position,
 
520
                      GooCanvasGroup      *group)
 
521
{
 
522
  goo_canvas_item_move_child ((GooCanvasItem*) group, old_position,
 
523
                               new_position);
 
524
}
 
525
 
 
526
 
 
527
static void
 
528
on_model_child_removed (GooCanvasGroupModel *model,
 
529
                        gint                 child_num,
 
530
                        GooCanvasGroup      *group)
 
531
{
 
532
  goo_canvas_item_remove_child ((GooCanvasItem*) group, child_num);
 
533
}
 
534
 
 
535
 
 
536
static void
 
537
goo_canvas_group_set_model (GooCanvasItem       *item,
 
538
                            GooCanvasItemModel  *model)
 
539
{
 
540
  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item;
 
541
  GooCanvasGroup *group = (GooCanvasGroup*) item;
 
542
  gint n_children, i;
 
543
 
 
544
  /* Do the default GooCanvasItemSimple code first. */
 
545
  goo_canvas_item_simple_set_model (simple, model);
 
546
 
 
547
  /* Now add our own handlers. */
 
548
  g_signal_connect (model, "child-added",
 
549
                    G_CALLBACK (on_model_child_added), group);
 
550
  g_signal_connect (model, "child-moved",
 
551
                    G_CALLBACK (on_model_child_moved), group);
 
552
  g_signal_connect (model, "child-removed",
 
553
                    G_CALLBACK (on_model_child_removed), group);
 
554
 
 
555
  /* Recursively create child items for any children. */
 
556
  n_children = goo_canvas_item_model_get_n_children (model);
 
557
  for (i = 0; i < n_children; i++)
 
558
    on_model_child_added ((GooCanvasGroupModel*) simple->model, i, group);
 
559
}
 
560
 
 
561
 
 
562
static void
 
563
goo_canvas_group_request_update  (GooCanvasItem *item)
 
564
{
 
565
  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item;
 
566
 
 
567
  if (!simple->need_update)
 
568
    {
 
569
      simple->need_update = TRUE;
 
570
 
 
571
      if (simple->parent)
 
572
        goo_canvas_item_request_update (simple->parent);
 
573
      else if (simple->canvas)
 
574
        goo_canvas_request_update (simple->canvas);
 
575
    }
 
576
}
 
577
 
 
578
 
 
579
static void
 
580
goo_canvas_group_update  (GooCanvasItem   *item,
 
581
                          gboolean         entire_tree,
 
582
                          cairo_t         *cr,
 
583
                          GooCanvasBounds *bounds)
 
584
{
 
585
  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item;
 
586
  GooCanvasGroup *group = (GooCanvasGroup*) item;
 
587
  GooCanvasGroupPrivate *priv = goo_canvas_group_get_private (group);
 
588
  GooCanvasBounds child_bounds;
 
589
  gboolean initial_bounds = TRUE;
 
590
  gint i;
 
591
 
 
592
  if (entire_tree || simple->need_update)
 
593
    {
 
594
      if (simple->need_entire_subtree_update)
 
595
        entire_tree = TRUE;
 
596
 
 
597
      simple->need_update = FALSE;
 
598
      simple->need_entire_subtree_update = FALSE;
 
599
 
 
600
      goo_canvas_item_simple_check_style (simple);
 
601
 
 
602
      simple->bounds.x1 = simple->bounds.y1 = 0.0;
 
603
      simple->bounds.x2 = simple->bounds.y2 = 0.0;
 
604
 
 
605
      cairo_save (cr);
 
606
      if (simple->simple_data->transform)
 
607
        cairo_transform (cr, simple->simple_data->transform);
 
608
 
 
609
      cairo_translate (cr, priv->x, priv->y);
 
610
 
 
611
      for (i = 0; i < group->items->len; i++)
 
612
        {
 
613
          GooCanvasItem *child = group->items->pdata[i];
 
614
 
 
615
          goo_canvas_item_update (child, entire_tree, cr, &child_bounds);
 
616
          
 
617
          /* If the child has non-empty bounds, compute the union. */
 
618
          if (child_bounds.x1 < child_bounds.x2
 
619
              && child_bounds.y1 < child_bounds.y2)
 
620
            {
 
621
              if (initial_bounds)
 
622
                {
 
623
                  simple->bounds.x1 = child_bounds.x1;
 
624
                  simple->bounds.y1 = child_bounds.y1;
 
625
                  simple->bounds.x2 = child_bounds.x2;
 
626
                  simple->bounds.y2 = child_bounds.y2;
 
627
                  initial_bounds = FALSE;
 
628
                }
 
629
              else
 
630
                {
 
631
                  simple->bounds.x1 = MIN (simple->bounds.x1, child_bounds.x1);
 
632
                  simple->bounds.y1 = MIN (simple->bounds.y1, child_bounds.y1);
 
633
                  simple->bounds.x2 = MAX (simple->bounds.x2, child_bounds.x2);
 
634
                  simple->bounds.y2 = MAX (simple->bounds.y2, child_bounds.y2);
 
635
                }
 
636
            }
 
637
        }
 
638
 
 
639
      cairo_restore (cr);
 
640
    }
 
641
 
 
642
  *bounds = simple->bounds;
 
643
}
 
644
 
 
645
 
 
646
static GList*
 
647
goo_canvas_group_get_items_at (GooCanvasItem  *item,
 
648
                               gdouble         x,
 
649
                               gdouble         y,
 
650
                               cairo_t        *cr,
 
651
                               gboolean        is_pointer_event,
 
652
                               gboolean        parent_visible,
 
653
                               GList          *found_items)
 
654
{
 
655
  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item;
 
656
  GooCanvasItemSimpleData *simple_data = simple->simple_data;
 
657
  GooCanvasGroup *group = (GooCanvasGroup*) item;
 
658
  GooCanvasGroupPrivate *priv = goo_canvas_group_get_private (group);
 
659
  gboolean visible = parent_visible;
 
660
  int i;
 
661
 
 
662
  if (simple->need_update)
 
663
    goo_canvas_item_ensure_updated (item);
 
664
 
 
665
  /* Skip the item if the point isn't in the item's bounds. */
 
666
  if (simple->bounds.x1 > x || simple->bounds.x2 < x
 
667
      || simple->bounds.y1 > y || simple->bounds.y2 < y)
 
668
    return found_items;
 
669
 
 
670
  if (simple_data->visibility <= GOO_CANVAS_ITEM_INVISIBLE
 
671
      || (simple_data->visibility == GOO_CANVAS_ITEM_VISIBLE_ABOVE_THRESHOLD
 
672
          && simple->canvas->scale < simple_data->visibility_threshold))
 
673
    visible = FALSE;
 
674
 
 
675
  /* Check if the group should receive events. */
 
676
  if (is_pointer_event
 
677
      && (simple_data->pointer_events == GOO_CANVAS_EVENTS_NONE
 
678
          || ((simple_data->pointer_events & GOO_CANVAS_EVENTS_VISIBLE_MASK)
 
679
              && !visible)))
 
680
    return found_items;
 
681
 
 
682
  cairo_save (cr);
 
683
  if (simple_data->transform)
 
684
    cairo_transform (cr, simple_data->transform);
 
685
 
 
686
  cairo_translate (cr, priv->x, priv->y);
 
687
 
 
688
  /* If the group has a clip path, check if the point is inside it. */
 
689
  if (simple_data->clip_path_commands)
 
690
    {
 
691
      double user_x = x, user_y = y;
 
692
 
 
693
      cairo_device_to_user (cr, &user_x, &user_y);
 
694
      goo_canvas_create_path (simple_data->clip_path_commands, cr);
 
695
      cairo_set_fill_rule (cr, simple_data->clip_fill_rule);
 
696
      if (!cairo_in_fill (cr, user_x, user_y))
 
697
        {
 
698
          cairo_restore (cr);
 
699
          return found_items;
 
700
        }
 
701
    }
 
702
 
 
703
  if (priv->width > 0.0 && priv->height > 0.0)
 
704
    {
 
705
      double user_x = x, user_y = y;
 
706
 
 
707
      cairo_device_to_user (cr, &user_x, &user_y);
 
708
      if (user_x < 0.0 || user_x >= priv->width
 
709
          || user_y < 0.0 || user_y >= priv->height)
 
710
        {
 
711
          cairo_restore (cr);
 
712
          return found_items;
 
713
        }
 
714
    }
 
715
 
 
716
  /* Step up from the bottom of the children to the top, adding any items
 
717
     found to the start of the list. */
 
718
  for (i = 0; i < group->items->len; i++)
 
719
    {
 
720
      GooCanvasItem *child = group->items->pdata[i];
 
721
 
 
722
      found_items = goo_canvas_item_get_items_at (child, x, y, cr,
 
723
                                                  is_pointer_event, visible,
 
724
                                                  found_items);
 
725
    }
 
726
  cairo_restore (cr);
 
727
 
 
728
  return found_items;
 
729
}
 
730
 
 
731
 
 
732
static void
 
733
goo_canvas_group_paint (GooCanvasItem         *item,
 
734
                        cairo_t               *cr,
 
735
                        const GooCanvasBounds *bounds,
 
736
                        gdouble                scale)
 
737
{
 
738
  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item;
 
739
  GooCanvasItemSimpleData *simple_data = simple->simple_data;
 
740
  GooCanvasGroup *group = (GooCanvasGroup*) item;
 
741
  GooCanvasGroupPrivate *priv = goo_canvas_group_get_private (group);
 
742
  gint i;
 
743
 
 
744
  /* Skip the item if the bounds don't intersect the expose rectangle. */
 
745
  if (simple->bounds.x1 > bounds->x2 || simple->bounds.x2 < bounds->x1
 
746
      || simple->bounds.y1 > bounds->y2 || simple->bounds.y2 < bounds->y1)
 
747
    return;
 
748
 
 
749
  /* Check if the item should be visible. */
 
750
  if (simple_data->visibility <= GOO_CANVAS_ITEM_INVISIBLE
 
751
      || (simple_data->visibility == GOO_CANVAS_ITEM_VISIBLE_ABOVE_THRESHOLD
 
752
          && simple->canvas->scale < simple_data->visibility_threshold))
 
753
    return;
 
754
 
 
755
  /* Paint all the items in the group. */
 
756
  cairo_save (cr);
 
757
  if (simple_data->transform)
 
758
    cairo_transform (cr, simple_data->transform);
 
759
 
 
760
  cairo_translate (cr, priv->x, priv->y);
 
761
 
 
762
  /* Clip with the group's clip path, if it is set. */
 
763
  if (simple_data->clip_path_commands)
 
764
    {
 
765
      goo_canvas_create_path (simple_data->clip_path_commands, cr);
 
766
      cairo_set_fill_rule (cr, simple_data->clip_fill_rule);
 
767
      cairo_clip (cr);
 
768
    }
 
769
 
 
770
  if (priv->width > 0.0 && priv->height > 0.0)
 
771
    {
 
772
      cairo_rectangle (cr, 0.0, 0.0, priv->width, priv->height);
 
773
      cairo_clip (cr);
 
774
    }
 
775
 
 
776
  for (i = 0; i < group->items->len; i++)
 
777
    {
 
778
      GooCanvasItem *child = group->items->pdata[i];
 
779
      goo_canvas_item_paint (child, cr, bounds, scale);
 
780
    }
 
781
  cairo_restore (cr);
 
782
}
 
783
 
 
784
 
 
785
static void
 
786
canvas_item_interface_init (GooCanvasItemIface *iface)
 
787
{
 
788
  iface->set_canvas     = goo_canvas_group_set_canvas;
 
789
  iface->get_n_children = goo_canvas_group_get_n_children;
 
790
  iface->get_child      = goo_canvas_group_get_child;
 
791
  iface->request_update = goo_canvas_group_request_update;
 
792
 
 
793
  iface->add_child      = goo_canvas_group_add_child;
 
794
  iface->move_child     = goo_canvas_group_move_child;
 
795
  iface->remove_child   = goo_canvas_group_remove_child;
 
796
 
 
797
  iface->get_items_at   = goo_canvas_group_get_items_at;
 
798
  iface->update         = goo_canvas_group_update;
 
799
  iface->paint          = goo_canvas_group_paint;
 
800
 
 
801
  iface->set_model      = goo_canvas_group_set_model;
 
802
  iface->set_is_static  = goo_canvas_group_set_is_static;
 
803
}
 
804
 
 
805
 
 
806
/**
 
807
 * SECTION:goocanvasgroupmodel
 
808
 * @Title: GooCanvasGroupModel
 
809
 * @Short_Description: a model for a group of items.
 
810
 *
 
811
 * #GooCanvasGroupModel represents a group of items. Groups can be nested to
 
812
 * any depth, to create a hierarchy of items. Items are ordered within each
 
813
 * group, with later items being displayed above earlier items.
 
814
 *
 
815
 * #GooCanvasGroupModel is a subclass of #GooCanvasItemModelSimple and so
 
816
 * inherits all of the style properties such as "stroke-color", "fill-color"
 
817
 * and "line-width". Setting a style property on a #GooCanvasGroupModel will
 
818
 * affect all children of the #GooCanvasGroupModel (unless the children
 
819
 * override the property setting).
 
820
 *
 
821
 * #GooCanvasGroupModel implements the #GooCanvasItemModel interface, so you
 
822
 * can use the #GooCanvasItemModel functions such as
 
823
 * goo_canvas_item_model_raise() and goo_canvas_item_model_rotate(), and the
 
824
 * properties such as "visibility" and "pointer-events".
 
825
 *
 
826
 * To create a #GooCanvasGroupModel use goo_canvas_group_model_new().
 
827
 *
 
828
 * To get or set the properties of an existing #GooCanvasGroupModel, use
 
829
 * g_object_get() and g_object_set().
 
830
 *
 
831
 * To respond to events such as mouse clicks on the group you must connect
 
832
 * to the signal handlers of the corresponding #GooCanvasGroup objects.
 
833
 * (See goo_canvas_get_item() and #GooCanvas::item-created.)
 
834
 */
 
835
static void item_model_interface_init (GooCanvasItemModelIface *iface);
 
836
static void goo_canvas_group_model_dispose      (GObject            *object);
 
837
static void goo_canvas_group_model_finalize     (GObject            *object);
 
838
static void goo_canvas_group_model_get_property (GObject            *object,
 
839
                                                 guint               prop_id,
 
840
                                                 GValue             *value,
 
841
                                                 GParamSpec         *pspec);
 
842
static void goo_canvas_group_model_set_property (GObject            *object,
 
843
                                                 guint               prop_id,
 
844
                                                 const GValue       *value,
 
845
                                                 GParamSpec         *pspec);
 
846
 
 
847
G_DEFINE_TYPE_WITH_CODE (GooCanvasGroupModel, goo_canvas_group_model,
 
848
                         GOO_TYPE_CANVAS_ITEM_MODEL_SIMPLE,
 
849
                         G_IMPLEMENT_INTERFACE (GOO_TYPE_CANVAS_ITEM_MODEL,
 
850
                                                item_model_interface_init))
 
851
 
 
852
 
 
853
static void
 
854
goo_canvas_group_model_class_init (GooCanvasGroupModelClass *klass)
 
855
{
 
856
  GObjectClass *gobject_class = (GObjectClass*) klass;
 
857
  g_type_class_add_private (gobject_class, sizeof (GooCanvasGroupPrivate));
 
858
 
 
859
  gobject_class->dispose  = goo_canvas_group_model_dispose;
 
860
  gobject_class->finalize = goo_canvas_group_model_finalize;
 
861
  gobject_class->get_property  = goo_canvas_group_model_get_property;
 
862
  gobject_class->set_property = goo_canvas_group_model_set_property;
 
863
 
 
864
  goo_canvas_group_install_common_properties (gobject_class);
 
865
}
 
866
 
 
867
 
 
868
static void
 
869
goo_canvas_group_model_init (GooCanvasGroupModel *gmodel)
 
870
{
 
871
  GooCanvasGroupPrivate *priv = GOO_CANVAS_GROUP_MODEL_GET_PRIVATE (gmodel);
 
872
  gmodel->children = g_ptr_array_sized_new (8);
 
873
 
 
874
  priv->x = 0.0;
 
875
  priv->y = 0.0;
 
876
  priv->width = -1.0;
 
877
  priv->height = -1.0;
 
878
}
 
879
 
 
880
 
 
881
/**
 
882
 * goo_canvas_group_model_new:
 
883
 * @parent: the parent model, or %NULL. If a parent is specified, it will
 
884
 *  assume ownership of the item, and the item will automatically be freed when
 
885
 *  it is removed from the parent. Otherwise call g_object_unref() to free it.
 
886
 * @...: optional pairs of property names and values, and a terminating %NULL.
 
887
 * 
 
888
 * Creates a new group item.
 
889
 * 
 
890
 * Return value: a new group model.
 
891
 **/
 
892
GooCanvasItemModel*
 
893
goo_canvas_group_model_new (GooCanvasItemModel *parent,
 
894
                            ...)
 
895
{
 
896
  GooCanvasItemModel *model;
 
897
  GooCanvasGroupModel *gmodel;
 
898
  va_list var_args;
 
899
  const char *first_property;
 
900
 
 
901
  model = g_object_new (GOO_TYPE_CANVAS_GROUP_MODEL, NULL);
 
902
  gmodel = (GooCanvasGroupModel*) model;
 
903
 
 
904
  va_start (var_args, parent);
 
905
  first_property = va_arg (var_args, char*);
 
906
  if (first_property)
 
907
    g_object_set_valist (G_OBJECT (model), first_property, var_args);
 
908
  va_end (var_args);
 
909
 
 
910
  if (parent)
 
911
    {
 
912
      goo_canvas_item_model_add_child (parent, model, -1);
 
913
      g_object_unref (model);
 
914
    }
 
915
 
 
916
  return model;
 
917
}
 
918
 
 
919
 
 
920
static void
 
921
goo_canvas_group_model_dispose (GObject *object)
 
922
{
 
923
  GooCanvasGroupModel *gmodel = (GooCanvasGroupModel*) object;
 
924
  gint i;
 
925
 
 
926
  /* Unref all the items in the group. */
 
927
  for (i = 0; i < gmodel->children->len; i++)
 
928
    {
 
929
      GooCanvasItemModel *child = gmodel->children->pdata[i];
 
930
      goo_canvas_item_model_set_parent (child, NULL);
 
931
      g_object_unref (child);
 
932
    }
 
933
 
 
934
  g_ptr_array_set_size (gmodel->children, 0);
 
935
 
 
936
  G_OBJECT_CLASS (goo_canvas_group_model_parent_class)->dispose (object);
 
937
}
 
938
 
 
939
 
 
940
static void
 
941
goo_canvas_group_model_finalize (GObject *object)
 
942
{
 
943
  GooCanvasGroupModel *gmodel = (GooCanvasGroupModel*) object;
 
944
 
 
945
  g_ptr_array_free (gmodel->children, TRUE);
 
946
 
 
947
  G_OBJECT_CLASS (goo_canvas_group_model_parent_class)->finalize (object);
 
948
}
 
949
 
 
950
static void goo_canvas_group_model_get_property (GObject            *object,
 
951
                                                 guint               prop_id,
 
952
                                                 GValue             *value,
 
953
                                                 GParamSpec         *pspec)
 
954
{
 
955
  GooCanvasGroupModel *model = (GooCanvasGroupModel*) object;
 
956
  GooCanvasGroupPrivate *priv = GOO_CANVAS_GROUP_MODEL_GET_PRIVATE (model);
 
957
 
 
958
  goo_canvas_group_get_common_property (object, priv, prop_id, value, pspec);
 
959
}
 
960
 
 
961
static void goo_canvas_group_model_set_property (GObject            *object,
 
962
                                                 guint               prop_id,
 
963
                                                 const GValue       *value,
 
964
                                                 GParamSpec         *pspec)
 
965
{
 
966
  GooCanvasGroupModel *model = (GooCanvasGroupModel*) object;
 
967
  GooCanvasGroupPrivate *priv = GOO_CANVAS_GROUP_MODEL_GET_PRIVATE (model);
 
968
 
 
969
  goo_canvas_group_set_common_property (object, priv, prop_id, value, pspec);
 
970
  g_signal_emit_by_name (model, "changed", TRUE);
 
971
}
 
972
 
 
973
extern void _goo_canvas_item_model_emit_child_added (GooCanvasItemModel *model,
 
974
                                                     gint                position);
 
975
 
 
976
static void
 
977
goo_canvas_group_model_add_child     (GooCanvasItemModel *model,
 
978
                                      GooCanvasItemModel *child,
 
979
                                      gint                position)
 
980
{
 
981
  GooCanvasGroupModel *gmodel = (GooCanvasGroupModel*) model;
 
982
 
 
983
  g_object_ref (child);
 
984
 
 
985
  if (position >= 0)
 
986
    {
 
987
      goo_canvas_util_ptr_array_insert (gmodel->children, child, position);
 
988
    }
 
989
  else
 
990
    {
 
991
      position = gmodel->children->len;
 
992
      g_ptr_array_add (gmodel->children, child);
 
993
    }
 
994
 
 
995
  goo_canvas_item_model_set_parent (child, model);
 
996
 
 
997
  _goo_canvas_item_model_emit_child_added (model, position);
 
998
}
 
999
 
 
1000
 
 
1001
static void
 
1002
goo_canvas_group_model_move_child    (GooCanvasItemModel *model,
 
1003
                                      gint                old_position,
 
1004
                                      gint                new_position)
 
1005
{
 
1006
  GooCanvasGroupModel *gmodel = (GooCanvasGroupModel*) model;
 
1007
 
 
1008
  goo_canvas_util_ptr_array_move (gmodel->children, old_position,
 
1009
                                  new_position);
 
1010
 
 
1011
  g_signal_emit_by_name (gmodel, "child-moved", old_position, new_position);
 
1012
}
 
1013
 
 
1014
 
 
1015
static void
 
1016
goo_canvas_group_model_remove_child  (GooCanvasItemModel *model,
 
1017
                                      gint                child_num)
 
1018
{
 
1019
  GooCanvasGroupModel *gmodel = (GooCanvasGroupModel*) model;
 
1020
  GooCanvasItemModel *child;
 
1021
 
 
1022
  child = gmodel->children->pdata[child_num];
 
1023
  goo_canvas_item_model_set_parent (child, NULL);
 
1024
 
 
1025
  g_ptr_array_remove_index (gmodel->children, child_num);
 
1026
 
 
1027
  g_signal_emit_by_name (gmodel, "child-removed", child_num);
 
1028
 
 
1029
  g_object_unref (child);
 
1030
}
 
1031
 
 
1032
 
 
1033
static gint
 
1034
goo_canvas_group_model_get_n_children (GooCanvasItemModel  *model)
 
1035
{
 
1036
  GooCanvasGroupModel *gmodel = (GooCanvasGroupModel*) model;
 
1037
 
 
1038
  return gmodel->children->len;
 
1039
}
 
1040
 
 
1041
 
 
1042
static GooCanvasItemModel*
 
1043
goo_canvas_group_model_get_child   (GooCanvasItemModel  *model,
 
1044
                                    gint                 child_num)
 
1045
{
 
1046
  GooCanvasGroupModel *gmodel = (GooCanvasGroupModel*) model;
 
1047
 
 
1048
  if (child_num < gmodel->children->len)
 
1049
    return gmodel->children->pdata[child_num];
 
1050
  return NULL;
 
1051
}
 
1052
 
 
1053
 
 
1054
static GooCanvasItem*
 
1055
goo_canvas_group_model_create_item (GooCanvasItemModel *model,
 
1056
                                    GooCanvas          *canvas)
 
1057
{
 
1058
  GooCanvasItem *item;
 
1059
 
 
1060
  item = goo_canvas_group_new (NULL, NULL);
 
1061
  /* Note that we set the canvas before the model, since we may need the
 
1062
     canvas to create any child items. */
 
1063
  goo_canvas_item_set_canvas (item, canvas);
 
1064
  goo_canvas_item_set_model (item, model);
 
1065
 
 
1066
  return item;
 
1067
}
 
1068
 
 
1069
 
 
1070
static void
 
1071
item_model_interface_init (GooCanvasItemModelIface *iface)
 
1072
{
 
1073
  iface->add_child      = goo_canvas_group_model_add_child;
 
1074
  iface->move_child     = goo_canvas_group_model_move_child;
 
1075
  iface->remove_child   = goo_canvas_group_model_remove_child;
 
1076
  iface->get_n_children = goo_canvas_group_model_get_n_children;
 
1077
  iface->get_child      = goo_canvas_group_model_get_child;
 
1078
 
 
1079
  iface->create_item    = goo_canvas_group_model_create_item;
 
1080
}
 
1081
 
 
1082