Updated FSF's address
[platform/upstream/atk.git] / atk / atkrelation.c
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, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include <string.h>
19 #include <glib-object.h>
20 #include "atk.h"
21
22 /**
23  * SECTION:atkrelation
24  * @Short_description: An object used to describe a relation between a
25  *  object and one or more other objects.
26  * @Title:AtkRelation
27  *
28  * An AtkRelation describes a relation between an object and one or
29  * more other objects. The actual relations that an object has with
30  * other objects are defined as an AtkRelationSet, which is a set of
31  * AtkRelations.
32  */
33 enum {
34   PROP_0,
35
36   PROP_RELATION_TYPE,
37   PROP_TARGET,
38   PROP_LAST
39 };
40
41 static GPtrArray *extra_names = NULL;
42
43 static gpointer parent_class = NULL;
44   
45 static void atk_relation_class_init   (AtkRelationClass *klass);
46 static void atk_relation_finalize     (GObject          *object);
47 static void atk_relation_set_property (GObject          *object,
48                                        guint            prop_id,
49                                        const GValue     *value,
50                                        GParamSpec       *pspec);
51 static void atk_relation_get_property (GObject          *object,
52                                        guint            prop_id,
53                                        GValue           *value,
54                                        GParamSpec       *pspec);
55
56 static GPtrArray* atk_relation_get_ptr_array_from_value_array (GValueArray *array);
57 static GValueArray* atk_relation_get_value_array_from_ptr_array (GPtrArray *array);
58
59 GType
60 atk_relation_get_type (void)
61 {
62   static GType type = 0;
63
64   if (!type)
65     {
66       static const GTypeInfo typeInfo =
67       {
68         sizeof (AtkRelationClass),
69         (GBaseInitFunc) NULL,
70         (GBaseFinalizeFunc) NULL,
71         (GClassInitFunc) atk_relation_class_init,
72         (GClassFinalizeFunc) NULL,
73         NULL,
74         sizeof (AtkRelation),
75         0,
76         (GInstanceInitFunc) NULL,
77       } ;
78       type = g_type_register_static (G_TYPE_OBJECT, "AtkRelation", &typeInfo, 0) ;
79     }
80   return type;
81 }
82
83 static void
84 atk_relation_class_init (AtkRelationClass *klass)
85 {
86   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
87
88   parent_class = g_type_class_peek_parent (klass);
89   
90   gobject_class->finalize = atk_relation_finalize;
91   gobject_class->set_property = atk_relation_set_property;
92   gobject_class->get_property = atk_relation_get_property;
93
94   g_object_class_install_property (gobject_class,
95                                    PROP_RELATION_TYPE,
96                                    g_param_spec_enum ("relation_type",
97                                                       "Relation Type",
98                                                       "The type of the relation",
99                                                       ATK_TYPE_RELATION_TYPE,
100                                                       ATK_RELATION_NULL,
101                                                       G_PARAM_READWRITE));
102   g_object_class_install_property (gobject_class,
103                                    PROP_TARGET,
104                                    g_param_spec_value_array ("target",
105                                                              "Target",
106                                                              "An array of the targets for the relation",
107                                                              NULL,
108
109                                                              G_PARAM_READWRITE));
110 }
111
112 /**
113  * atk_relation_type_register:
114  * @name: a name string
115  *
116  * Associate @name with a new #AtkRelationType
117  
118  * Returns: an #AtkRelationType associated with @name
119  **/
120 AtkRelationType
121 atk_relation_type_register (const gchar *name)
122 {
123   g_return_val_if_fail (name, ATK_RELATION_NULL);
124
125   if (!extra_names)
126     extra_names = g_ptr_array_new ();
127
128   g_ptr_array_add (extra_names, g_strdup (name));
129   return extra_names->len + ATK_RELATION_LAST_DEFINED;
130 }
131
132 /**
133  * atk_relation_type_get_name:
134  * @type: The #AtkRelationType whose name is required
135  *
136  * Gets the description string describing the #AtkRelationType @type.
137  *
138  * Returns: the string describing the AtkRelationType
139  */
140 const gchar*
141 atk_relation_type_get_name (AtkRelationType type)
142 {
143   GTypeClass *type_class;
144   GEnumValue *value;
145   const gchar *name = NULL;
146
147   type_class = g_type_class_ref (ATK_TYPE_RELATION_TYPE);
148   g_return_val_if_fail (G_IS_ENUM_CLASS (type_class), NULL);
149
150   value = g_enum_get_value (G_ENUM_CLASS (type_class), type);
151
152   if (value)
153     {
154       name = value->value_nick;
155     }
156   else
157     {
158       if (extra_names)
159         {
160           gint n = type;
161
162           n -= ATK_RELATION_LAST_DEFINED + 1;
163
164           if (n < extra_names->len)
165             name = g_ptr_array_index (extra_names, n);
166         }
167     }
168   g_type_class_unref (type_class);
169   return name;
170 }
171
172 /**
173  * atk_relation_type_for_name:
174  * @name: a string which is the (non-localized) name of an ATK relation type.
175  *
176  * Get the #AtkRelationType type corresponding to a relation name.
177  *
178  * Returns: the #AtkRelationType enumerated type corresponding to the specified name,
179  *          or #ATK_RELATION_NULL if no matching relation type is found.
180  **/
181 AtkRelationType
182 atk_relation_type_for_name (const gchar *name)
183 {
184   GTypeClass *type_class;
185   GEnumValue *value;
186   AtkRelationType type = ATK_RELATION_NULL;
187
188   g_return_val_if_fail (name, ATK_RELATION_NULL);
189
190   type_class = g_type_class_ref (ATK_TYPE_RELATION_TYPE);
191   g_return_val_if_fail (G_IS_ENUM_CLASS (type_class), ATK_RELATION_NULL);
192
193   value = g_enum_get_value_by_nick (G_ENUM_CLASS (type_class), name);
194
195   if (value)
196     {
197       type = value->value;
198     }
199   else
200     {
201       gint i;
202
203       if (extra_names)
204         {
205           for (i = 0; i < extra_names->len; i++)
206             {
207               gchar *extra_name = (gchar *)g_ptr_array_index (extra_names, i);
208
209               g_return_val_if_fail (extra_name, ATK_RELATION_NULL);
210          
211               if (strcmp (name, extra_name) == 0)
212                 {
213                   type = i + 1 + ATK_RELATION_LAST_DEFINED;
214                   break;
215                 }
216             }
217         }
218     }
219   g_type_class_unref (type_class);
220  
221   return type;
222 }
223
224
225 /**
226  * atk_relation_new:
227  * @targets: (array length=n_targets): an array of pointers to
228  *  #AtkObjects
229  * @n_targets: number of #AtkObjects pointed to by @targets
230  * @relationship: an #AtkRelationType with which to create the new
231  *  #AtkRelation
232  *
233  * Create a new relation for the specified key and the specified list
234  * of targets.  See also atk_object_add_relationship().
235  *
236  * Returns: a pointer to a new #AtkRelation
237  **/
238 AtkRelation*
239 atk_relation_new (AtkObject       **targets,
240                   gint            n_targets,
241                   AtkRelationType relationship)
242 {
243   AtkRelation *relation;
244   int         i;
245   GValueArray *array;
246   GValue      *value;
247
248   g_return_val_if_fail (targets != NULL, NULL);
249
250   array = g_value_array_new (n_targets);
251   for (i = 0; i < n_targets; i++)
252   {
253     value = g_new0 (GValue, 1);
254     g_value_init (value, ATK_TYPE_OBJECT);
255     g_value_set_object (value, targets[i]);
256     array = g_value_array_append (array, value);
257     g_value_unset (value);
258     g_free (value);
259   }
260   
261   relation =  g_object_new (ATK_TYPE_RELATION, 
262                             "relation_type", relationship,
263                             "target", array,
264                             NULL);
265
266   g_value_array_free (array);
267
268   return relation;
269 }
270
271 /**
272  * atk_relation_get_relation_type:
273  * @relation: an #AtkRelation 
274  *
275  * Gets the type of @relation
276  *
277  * Returns: the type of @relation
278  **/
279 AtkRelationType
280 atk_relation_get_relation_type (AtkRelation *relation)
281 {
282   g_return_val_if_fail (ATK_IS_RELATION (relation), 0);
283   
284   return relation->relationship;
285 }
286
287 /**
288  * atk_relation_get_target:
289  * @relation: an #AtkRelation
290  *
291  * Gets the target list of @relation
292  *
293  * Returns: (transfer none) (element-type Atk.Object): the target list of @relation
294  **/
295 GPtrArray*
296 atk_relation_get_target (AtkRelation *relation)
297 {
298   g_return_val_if_fail (ATK_IS_RELATION (relation), NULL);
299
300   return relation->target;
301 }
302
303 static void
304 delete_object_while_in_relation (gpointer callback_data,
305                                  GObject *where_the_object_was)
306 {
307   GPtrArray *array;
308
309   g_assert (callback_data != NULL);
310
311   array = callback_data;
312   g_ptr_array_remove (array, where_the_object_was);
313 }
314
315 /**
316  * atk_relation_add_target:
317  * @relation: an #AtkRelation
318  * @target: an #AtkObject
319  *
320  * Adds the specified AtkObject to the target for the relation, if it is
321  * not already present.  See also atk_object_add_relationship().
322  *
323  *
324  * Since: 1.9
325  **/
326 void
327 atk_relation_add_target (AtkRelation *relation,
328                          AtkObject   *target)
329 {
330   guint i;
331
332   g_return_if_fail (ATK_IS_RELATION (relation));
333   g_return_if_fail (ATK_IS_OBJECT (target));
334
335   /* first check if target occurs in array ... */
336   for (i = 0; i < relation->target->len; i++)
337     if (g_ptr_array_index(relation->target, i) == target)
338       return;
339
340   g_ptr_array_add (relation->target, target);
341   g_object_weak_ref (G_OBJECT (target), (GWeakNotify) delete_object_while_in_relation, relation->target);
342 }
343
344 /**
345  * atk_relation_remove_target:
346  * @relation: an #AtkRelation
347  * @target: an #AtkObject
348  *
349  * Remove the specified AtkObject from the target for the relation.
350  *
351  * Returns: TRUE if the removal is successful.
352  **/
353
354 gboolean
355 atk_relation_remove_target (AtkRelation *relation,
356                             AtkObject *target)
357 {
358   gboolean ret = FALSE;
359   GPtrArray *array;
360
361   array = atk_relation_get_target (relation);
362
363   if (array && g_ptr_array_remove (array, target))
364     {
365       g_object_weak_unref (G_OBJECT (target),
366                            (GWeakNotify) delete_object_while_in_relation,
367                            relation->target);
368       ret = TRUE;
369     }
370   return ret;
371 }
372
373 static void
374 atk_relation_finalize (GObject *object)
375 {
376   AtkRelation        *relation;
377
378   g_return_if_fail (ATK_IS_RELATION (object));
379
380   relation = ATK_RELATION (object);
381
382   if (relation->target)
383   {
384     gint i;
385
386     for (i = 0; i < relation->target->len; i++)
387     {
388       g_object_weak_unref (G_OBJECT (g_ptr_array_index (relation->target, i)),
389                            (GWeakNotify) delete_object_while_in_relation, 
390                            relation->target);
391     }
392     g_ptr_array_free (relation->target, TRUE);
393   } 
394
395   G_OBJECT_CLASS (parent_class)->finalize (object);
396 }
397
398 static void 
399 atk_relation_set_property (GObject       *object,
400                            guint         prop_id,
401                            const GValue  *value,
402                            GParamSpec    *pspec)
403 {
404   AtkRelation *relation;
405   gpointer boxed;
406
407   relation = ATK_RELATION (object);
408
409   switch (prop_id)
410     {
411     case PROP_RELATION_TYPE:
412       relation->relationship = g_value_get_enum (value);
413       break; 
414     case PROP_TARGET:
415       if (relation->target)
416       {
417         gint i;
418
419         for (i = 0; i < relation->target->len; i++)
420         {
421           g_object_weak_unref (G_OBJECT (g_ptr_array_index (relation->target, i)),
422                                (GWeakNotify) delete_object_while_in_relation,
423                                relation->target);
424         }
425         g_ptr_array_free (relation->target, TRUE);
426       }
427       boxed = g_value_get_boxed (value);
428       relation->target = atk_relation_get_ptr_array_from_value_array ( (GValueArray *) boxed);
429       break; 
430     default:
431       break;
432     }  
433 }
434
435 static void
436 atk_relation_get_property (GObject    *object,
437                            guint      prop_id,
438                            GValue     *value,
439                            GParamSpec *pspec)
440 {
441   AtkRelation *relation;
442   GValueArray *array;
443
444   relation = ATK_RELATION (object);
445
446   switch (prop_id)
447     {
448     case PROP_RELATION_TYPE:
449       g_value_set_enum (value, relation->relationship);
450       break;
451     case PROP_TARGET:
452       array = atk_relation_get_value_array_from_ptr_array (relation->target);
453       g_value_set_boxed (value, array);
454       break;
455     default:
456       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
457       break;
458     }  
459 }
460
461 static GPtrArray*
462 atk_relation_get_ptr_array_from_value_array (GValueArray *array)
463 {
464   gint i;
465   GPtrArray *return_array;
466   GValue *value;
467   GObject *obj;
468
469   return_array = g_ptr_array_sized_new (array->n_values);
470   for (i = 0; i < array->n_values; i++)
471     {
472       value = g_value_array_get_nth (array, i);
473       obj = g_value_get_object (value);
474       g_ptr_array_add (return_array, obj);
475       g_object_weak_ref (obj, (GWeakNotify) delete_object_while_in_relation, return_array);
476     }
477       
478   return return_array;
479 }
480
481 static GValueArray*
482 atk_relation_get_value_array_from_ptr_array (GPtrArray *array)
483 {
484   int         i;
485   GValueArray *return_array;
486   GValue      *value;
487
488   return_array = g_value_array_new (array->len);
489   for (i = 0; i < array->len; i++)
490     {
491       value = g_new0 (GValue, 1);
492       g_value_init (value, ATK_TYPE_OBJECT);
493       g_value_set_object (value, g_ptr_array_index (array, i));
494       return_array = g_value_array_append (return_array, value);
495     }
496   return return_array;
497 }