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