1
/* ATK -  Accessibility Toolkit
2
 * Copyright 2001 Sun Microsystems Inc.
3
 *
4
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
6
 * License as published by the Free Software Foundation; either
7
 * version 2 of the License, or (at your option) any later version.
8
 *
9
 * This library is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 * Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15
 * License along with this library; if not, write to the
16
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17
 * Boston, MA 02111-1307, USA.
18
 */
19

            
20
#include "config.h"
21

            
22
#include "atk.h"
23
#include <glib-object.h>
24
#include <string.h>
25

            
26
/**
27
 * AtkRelation:
28
 *
29
 * An object used to describe a relation between a
30
 *  object and one or more other objects.
31
 *
32
 * An AtkRelation describes a relation between an object and one or
33
 * more other objects. The actual relations that an object has with
34
 * other objects are defined as an AtkRelationSet, which is a set of
35
 * AtkRelations.
36
 */
37
enum
38
{
39
  PROP_0,
40

            
41
  PROP_RELATION_TYPE,
42
  PROP_TARGET,
43
  PROP_LAST
44
};
45

            
46
static GPtrArray *extra_names = NULL;
47

            
48
static gpointer parent_class = NULL;
49

            
50
static void atk_relation_class_init (AtkRelationClass *klass);
51
static void atk_relation_finalize (GObject *object);
52
static void atk_relation_set_property (GObject *object,
53
                                       guint prop_id,
54
                                       const GValue *value,
55
                                       GParamSpec *pspec);
56
static void atk_relation_get_property (GObject *object,
57
                                       guint prop_id,
58
                                       GValue *value,
59
                                       GParamSpec *pspec);
60

            
61
static GPtrArray *atk_relation_get_ptr_array_from_value_array (GValueArray *array);
62
static GValueArray *atk_relation_get_value_array_from_ptr_array (GPtrArray *array);
63

            
64
GType
65
364
atk_relation_get_type (void)
66
{
67
  static GType type = 0;
68

            
69
364
  if (!type)
70
    {
71
      static const GTypeInfo typeInfo = {
72
        sizeof (AtkRelationClass),
73
        (GBaseInitFunc) NULL,
74
        (GBaseFinalizeFunc) NULL,
75
        (GClassInitFunc) atk_relation_class_init,
76
        (GClassFinalizeFunc) NULL,
77
        NULL,
78
        sizeof (AtkRelation),
79
        0,
80
        (GInstanceInitFunc) NULL,
81
      };
82
51
      type = g_type_register_static (G_TYPE_OBJECT, "AtkRelation", &typeInfo, 0);
83
    }
84
364
  return type;
85
}
86

            
87
static void
88
51
atk_relation_class_init (AtkRelationClass *klass)
89
{
90
51
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
91

            
92
51
  parent_class = g_type_class_peek_parent (klass);
93

            
94
51
  gobject_class->finalize = atk_relation_finalize;
95
51
  gobject_class->set_property = atk_relation_set_property;
96
51
  gobject_class->get_property = atk_relation_get_property;
97

            
98
51
  g_object_class_install_property (gobject_class,
99
                                   PROP_RELATION_TYPE,
100
                                   g_param_spec_enum ("relation_type",
101
                                                      "Relation Type",
102
                                                      "The type of the relation",
103
                                                      ATK_TYPE_RELATION_TYPE,
104
                                                      ATK_RELATION_NULL,
105
                                                      G_PARAM_READWRITE));
106
51
  g_object_class_install_property (gobject_class,
107
                                   PROP_TARGET,
108
                                   g_param_spec_value_array ("target",
109
                                                             "Target",
110
                                                             "An array of the targets for the relation",
111
                                                             NULL,
112

            
113
                                                             G_PARAM_READWRITE));
114
51
}
115

            
116
/**
117
 * atk_relation_type_register:
118
 * @name: a name string
119
 *
120
 * Associate @name with a new #AtkRelationType
121

            
122
 * Returns: an #AtkRelationType associated with @name
123
 **/
124
AtkRelationType
125
1
atk_relation_type_register (const gchar *name)
126
{
127
1
  g_return_val_if_fail (name, ATK_RELATION_NULL);
128

            
129
1
  if (!extra_names)
130
1
    extra_names = g_ptr_array_new ();
131

            
132
1
  g_ptr_array_add (extra_names, g_strdup (name));
133
1
  return extra_names->len + ATK_RELATION_LAST_DEFINED;
134
}
135

            
136
/**
137
 * atk_relation_type_get_name:
138
 * @type: The #AtkRelationType whose name is required
139
 *
140
 * Gets the description string describing the #AtkRelationType @type.
141
 *
142
 * Returns: the string describing the AtkRelationType
143
 */
144
const gchar *
145
5
atk_relation_type_get_name (AtkRelationType type)
146
{
147
  GTypeClass *type_class;
148
  GEnumValue *value;
149
5
  const gchar *name = NULL;
150

            
151
5
  type_class = g_type_class_ref (ATK_TYPE_RELATION_TYPE);
152
5
  g_return_val_if_fail (G_IS_ENUM_CLASS (type_class), NULL);
153

            
154
5
  value = g_enum_get_value (G_ENUM_CLASS (type_class), type);
155

            
156
5
  if (value)
157
    {
158
3
      name = value->value_nick;
159
    }
160
  else
161
    {
162
2
      if (extra_names)
163
        {
164
2
          gint n = type;
165

            
166
2
          n -= ATK_RELATION_LAST_DEFINED + 1;
167

            
168
2
          if (n < extra_names->len)
169
1
            name = g_ptr_array_index (extra_names, n);
170
        }
171
    }
172
5
  g_type_class_unref (type_class);
173
5
  return name;
174
}
175

            
176
/**
177
 * atk_relation_type_for_name:
178
 * @name: a string which is the (non-localized) name of an ATK relation type.
179
 *
180
 * Get the #AtkRelationType type corresponding to a relation name.
181
 *
182
 * Returns: the #AtkRelationType enumerated type corresponding to the specified name,
183
 *          or #ATK_RELATION_NULL if no matching relation type is found.
184
 **/
185
AtkRelationType
186
4
atk_relation_type_for_name (const gchar *name)
187
{
188
  GTypeClass *type_class;
189
  GEnumValue *value;
190
4
  AtkRelationType type = ATK_RELATION_NULL;
191

            
192
4
  g_return_val_if_fail (name, ATK_RELATION_NULL);
193

            
194
4
  type_class = g_type_class_ref (ATK_TYPE_RELATION_TYPE);
195
4
  g_return_val_if_fail (G_IS_ENUM_CLASS (type_class), ATK_RELATION_NULL);
196

            
197
4
  value = g_enum_get_value_by_nick (G_ENUM_CLASS (type_class), name);
198

            
199
4
  if (value)
200
    {
201
2
      type = value->value;
202
    }
203
  else
204
    {
205
      gint i;
206

            
207
2
      if (extra_names)
208
        {
209
3
          for (i = 0; i < extra_names->len; i++)
210
            {
211
2
              gchar *extra_name = (gchar *) g_ptr_array_index (extra_names, i);
212

            
213
2
              g_return_val_if_fail (extra_name, ATK_RELATION_NULL);
214

            
215
2
              if (strcmp (name, extra_name) == 0)
216
                {
217
1
                  type = i + 1 + ATK_RELATION_LAST_DEFINED;
218
1
                  break;
219
                }
220
            }
221
        }
222
    }
223
4
  g_type_class_unref (type_class);
224

            
225
4
  return type;
226
}
227

            
228
/**
229
 * atk_relation_new:
230
 * @targets: (array length=n_targets): an array of pointers to
231
 *  #AtkObjects
232
 * @n_targets: number of #AtkObjects pointed to by @targets
233
 * @relationship: an #AtkRelationType with which to create the new
234
 *  #AtkRelation
235
 *
236
 * Create a new relation for the specified key and the specified list
237
 * of targets.  See also atk_object_add_relationship().
238
 *
239
 * Returns: a pointer to a new #AtkRelation
240
 **/
241
AtkRelation *
242
101
atk_relation_new (AtkObject **targets,
243
                  gint n_targets,
244
                  AtkRelationType relationship)
245
{
246
  AtkRelation *relation;
247
  int i;
248
  GValueArray *array;
249
  GValue *value;
250

            
251
101
  g_return_val_if_fail (targets != NULL, NULL);
252

            
253
101
  array = g_value_array_new (n_targets);
254
202
  for (i = 0; i < n_targets; i++)
255
    {
256
101
      value = g_new0 (GValue, 1);
257
101
      g_value_init (value, ATK_TYPE_OBJECT);
258
101
      g_value_set_object (value, targets[i]);
259
101
      array = g_value_array_append (array, value);
260
101
      g_value_unset (value);
261
101
      g_free (value);
262
    }
263

            
264
101
  relation = g_object_new (ATK_TYPE_RELATION,
265
                           "relation_type", relationship,
266
                           "target", array,
267
                           NULL);
268

            
269
101
  g_value_array_free (array);
270

            
271
101
  return relation;
272
}
273

            
274
/**
275
 * atk_relation_get_relation_type:
276
 * @relation: an #AtkRelation
277
 *
278
 * Gets the type of @relation
279
 *
280
 * Returns: the type of @relation
281
 **/
282
AtkRelationType
283
54
atk_relation_get_relation_type (AtkRelation *relation)
284
{
285
54
  g_return_val_if_fail (ATK_IS_RELATION (relation), 0);
286

            
287
54
  return relation->relationship;
288
}
289

            
290
/**
291
 * atk_relation_get_target:
292
 * @relation: an #AtkRelation
293
 *
294
 * Gets the target list of @relation
295
 *
296
 * Returns: (transfer none) (element-type Atk.Object): the target list of @relation
297
 **/
298
GPtrArray *
299
5
atk_relation_get_target (AtkRelation *relation)
300
{
301
5
  g_return_val_if_fail (ATK_IS_RELATION (relation), NULL);
302

            
303
5
  return relation->target;
304
}
305

            
306
static void
307
delete_object_while_in_relation (gpointer callback_data,
308
                                 GObject *where_the_object_was)
309
{
310
  GPtrArray *array;
311

            
312
  g_assert (callback_data != NULL);
313

            
314
  array = callback_data;
315
  g_ptr_array_remove (array, where_the_object_was);
316
}
317

            
318
/**
319
 * atk_relation_add_target:
320
 * @relation: an #AtkRelation
321
 * @target: an #AtkObject
322
 *
323
 * Adds the specified AtkObject to the target for the relation, if it is
324
 * not already present.  See also atk_object_add_relationship().
325
 *
326
 *
327
 * Since: 1.9
328
 **/
329
void
330
atk_relation_add_target (AtkRelation *relation,
331
                         AtkObject *target)
332
{
333
  guint i;
334

            
335
  g_return_if_fail (ATK_IS_RELATION (relation));
336
  g_return_if_fail (ATK_IS_OBJECT (target));
337

            
338
  /* first check if target occurs in array ... */
339
  for (i = 0; i < relation->target->len; i++)
340
    if (g_ptr_array_index (relation->target, i) == target)
341
      return;
342

            
343
  g_ptr_array_add (relation->target, target);
344
  g_object_weak_ref (G_OBJECT (target), (GWeakNotify) delete_object_while_in_relation, relation->target);
345
}
346

            
347
/**
348
 * atk_relation_remove_target:
349
 * @relation: an #AtkRelation
350
 * @target: an #AtkObject
351
 *
352
 * Remove the specified AtkObject from the target for the relation.
353
 *
354
 * Returns: TRUE if the removal is successful.
355
 **/
356

            
357
gboolean
358
1
atk_relation_remove_target (AtkRelation *relation,
359
                            AtkObject *target)
360
{
361
1
  gboolean ret = FALSE;
362
  GPtrArray *array;
363

            
364
1
  array = atk_relation_get_target (relation);
365

            
366
1
  if (array && g_ptr_array_remove (array, target))
367
    {
368
1
      g_object_weak_unref (G_OBJECT (target),
369
                           (GWeakNotify) delete_object_while_in_relation,
370
1
                           relation->target);
371
1
      ret = TRUE;
372
    }
373
1
  return ret;
374
}
375

            
376
static void
377
1
atk_relation_finalize (GObject *object)
378
{
379
  AtkRelation *relation;
380

            
381
1
  g_return_if_fail (ATK_IS_RELATION (object));
382

            
383
1
  relation = ATK_RELATION (object);
384

            
385
1
  if (relation->target)
386
    {
387
      gint i;
388

            
389
1
      for (i = 0; i < relation->target->len; i++)
390
        {
391
          g_object_weak_unref (G_OBJECT (g_ptr_array_index (relation->target, i)),
392
                               (GWeakNotify) delete_object_while_in_relation,
393
                               relation->target);
394
        }
395
1
      g_ptr_array_free (relation->target, TRUE);
396
    }
397

            
398
1
  G_OBJECT_CLASS (parent_class)->finalize (object);
399
}
400

            
401
static void
402
202
atk_relation_set_property (GObject *object,
403
                           guint prop_id,
404
                           const GValue *value,
405
                           GParamSpec *pspec)
406
{
407
  AtkRelation *relation;
408
  gpointer boxed;
409

            
410
202
  relation = ATK_RELATION (object);
411

            
412
202
  switch (prop_id)
413
    {
414
101
    case PROP_RELATION_TYPE:
415
101
      relation->relationship = g_value_get_enum (value);
416
101
      break;
417
101
    case PROP_TARGET:
418
101
      if (relation->target)
419
        {
420
          gint i;
421

            
422
          for (i = 0; i < relation->target->len; i++)
423
            {
424
              g_object_weak_unref (G_OBJECT (g_ptr_array_index (relation->target, i)),
425
                                   (GWeakNotify) delete_object_while_in_relation,
426
                                   relation->target);
427
            }
428
          g_ptr_array_free (relation->target, TRUE);
429
        }
430
101
      boxed = g_value_get_boxed (value);
431
101
      relation->target = atk_relation_get_ptr_array_from_value_array ((GValueArray *) boxed);
432
101
      break;
433
    default:
434
      break;
435
    }
436
202
}
437

            
438
static void
439
atk_relation_get_property (GObject *object,
440
                           guint prop_id,
441
                           GValue *value,
442
                           GParamSpec *pspec)
443
{
444
  AtkRelation *relation;
445
  GValueArray *array;
446

            
447
  relation = ATK_RELATION (object);
448

            
449
  switch (prop_id)
450
    {
451
    case PROP_RELATION_TYPE:
452
      g_value_set_enum (value, relation->relationship);
453
      break;
454
    case PROP_TARGET:
455
      array = atk_relation_get_value_array_from_ptr_array (relation->target);
456
      g_value_set_boxed (value, array);
457
      break;
458
    default:
459
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
460
      break;
461
    }
462
}
463

            
464
static GPtrArray *
465
101
atk_relation_get_ptr_array_from_value_array (GValueArray *array)
466
{
467
  gint i;
468
  GPtrArray *return_array;
469
  GValue *value;
470
  GObject *obj;
471

            
472
101
  return_array = g_ptr_array_sized_new (array->n_values);
473
202
  for (i = 0; i < array->n_values; i++)
474
    {
475
101
      value = g_value_array_get_nth (array, i);
476
101
      obj = g_value_get_object (value);
477
101
      g_ptr_array_add (return_array, obj);
478
101
      g_object_weak_ref (obj, (GWeakNotify) delete_object_while_in_relation, return_array);
479
    }
480

            
481
101
  return return_array;
482
}
483

            
484
static GValueArray *
485
atk_relation_get_value_array_from_ptr_array (GPtrArray *array)
486
{
487
  int i;
488
  GValueArray *return_array;
489
  GValue *value;
490

            
491
  return_array = g_value_array_new (array->len);
492
  for (i = 0; i < array->len; i++)
493
    {
494
      value = g_new0 (GValue, 1);
495
      g_value_init (value, ATK_TYPE_OBJECT);
496
      g_value_set_object (value, g_ptr_array_index (array, i));
497
      return_array = g_value_array_append (return_array, value);
498
    }
499
  return return_array;
500
}