]> Pileus Git - ~andy/gtk/blob - gtk/a11y/gtkcellaccessible.c
a11y: Add _gtk_cell_accessible_parent_get_child_index()
[~andy/gtk] / gtk / a11y / gtkcellaccessible.c
1 /* GAIL - The GNOME Accessibility Implementation Library
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 <gtk/gtk.h>
23 #include "gtkcontainercellaccessible.h"
24 #include "gtkcellaccessible.h"
25 #include "gtkcellaccessibleparent.h"
26
27 typedef struct _ActionInfo ActionInfo;
28 struct _ActionInfo {
29   gchar *name;
30   gchar *description;
31   gchar *keybinding;
32   void (*do_action_func) (GtkCellAccessible *cell);
33 };
34
35
36 static void atk_action_interface_init    (AtkActionIface    *iface);
37 static void atk_component_interface_init (AtkComponentIface *iface);
38
39 G_DEFINE_TYPE_WITH_CODE (GtkCellAccessible, _gtk_cell_accessible, ATK_TYPE_OBJECT,
40                          G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, atk_action_interface_init)
41                          G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT, atk_component_interface_init))
42
43 static void
44 destroy_action_info (gpointer action_info)
45 {
46   ActionInfo *info = (ActionInfo *)action_info;
47
48   g_free (info->name);
49   g_free (info->description);
50   g_free (info->keybinding);
51   g_free (info);
52 }
53
54 static void
55 gtk_cell_accessible_object_finalize (GObject *obj)
56 {
57   GtkCellAccessible *cell = GTK_CELL_ACCESSIBLE (obj);
58   AtkRelationSet *relation_set;
59   AtkRelation *relation;
60   GPtrArray *target;
61   gpointer target_object;
62   gint i;
63
64   if (cell->state_set)
65     g_object_unref (cell->state_set);
66
67   if (cell->action_list)
68     g_list_free_full (cell->action_list, destroy_action_info);
69
70   relation_set = atk_object_ref_relation_set (ATK_OBJECT (obj));
71   if (ATK_IS_RELATION_SET (relation_set))
72     {
73       relation = atk_relation_set_get_relation_by_type (relation_set,
74                                                         ATK_RELATION_NODE_CHILD_OF);
75       if (relation)
76         {
77           target = atk_relation_get_target (relation);
78           for (i = 0; i < target->len; i++)
79             {
80               target_object = g_ptr_array_index (target, i);
81               if (GTK_IS_CELL_ACCESSIBLE (target_object))
82                 g_object_unref (target_object);
83             }
84         }
85       g_object_unref (relation_set);
86     }
87   G_OBJECT_CLASS (_gtk_cell_accessible_parent_class)->finalize (obj);
88 }
89
90 static gint
91 gtk_cell_accessible_get_index_in_parent (AtkObject *obj)
92 {
93   GtkCellAccessible *cell;
94   AtkObject *parent;
95   int index;
96
97   cell = GTK_CELL_ACCESSIBLE (obj);
98   parent = gtk_widget_get_accessible (cell->widget);
99   if (parent == NULL)
100     return -1;
101
102   index = _gtk_cell_accessible_parent_get_child_index (GTK_CELL_ACCESSIBLE_PARENT (parent), cell);
103   if (index >= 0)
104     return index;
105
106   if (atk_state_set_contains_state (cell->state_set, ATK_STATE_STALE) &&
107       cell->refresh_index != NULL)
108     {
109       cell->refresh_index (cell);
110       atk_state_set_remove_state (cell->state_set, ATK_STATE_STALE);
111     }
112
113   return cell->index;
114 }
115
116 static AtkStateSet *
117 gtk_cell_accessible_ref_state_set (AtkObject *obj)
118 {
119   GtkCellAccessible *cell = GTK_CELL_ACCESSIBLE (obj);
120
121   g_object_ref (cell->state_set);
122
123   return cell->state_set;
124 }
125
126 static void
127 _gtk_cell_accessible_class_init (GtkCellAccessibleClass *klass)
128 {
129   AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
130   GObjectClass *g_object_class = G_OBJECT_CLASS (klass);
131
132   g_object_class->finalize = gtk_cell_accessible_object_finalize;
133
134   class->get_index_in_parent = gtk_cell_accessible_get_index_in_parent;
135   class->ref_state_set = gtk_cell_accessible_ref_state_set;
136 }
137
138 static void
139 _gtk_cell_accessible_init (GtkCellAccessible *cell)
140 {
141   cell->widget = NULL;
142   cell->action_list = NULL;
143   cell->index = 0;
144   cell->refresh_index = NULL;
145   cell->state_set = atk_state_set_new ();
146   atk_state_set_add_state (cell->state_set, ATK_STATE_TRANSIENT);
147   atk_state_set_add_state (cell->state_set, ATK_STATE_ENABLED);
148   atk_state_set_add_state (cell->state_set, ATK_STATE_SENSITIVE);
149   atk_state_set_add_state (cell->state_set, ATK_STATE_SELECTABLE);
150 }
151
152 static void
153 widget_destroyed (GtkWidget         *widget,
154                   GtkCellAccessible *cell)
155 {
156   cell->widget = NULL;
157 }
158
159 void
160 _gtk_cell_accessible_initialise (GtkCellAccessible *cell,
161                                  GtkWidget         *widget,
162                                  AtkObject         *parent,
163                                  gint               index)
164 {
165   cell->widget = widget;
166   atk_object_set_parent (ATK_OBJECT (cell), parent);
167   cell->index = index;
168
169   g_signal_connect_object (G_OBJECT (widget), "destroy",
170                            G_CALLBACK (widget_destroyed), cell, 0);
171 }
172
173 gboolean
174 _gtk_cell_accessible_add_state (GtkCellAccessible *cell,
175                                 AtkStateType       state_type,
176                                 gboolean           emit_signal)
177 {
178   gboolean rc;
179   AtkObject *parent;
180
181   if (atk_state_set_contains_state (cell->state_set, state_type))
182     return FALSE;
183
184   rc = atk_state_set_add_state (cell->state_set, state_type);
185
186   /* The signal should only be generated if the value changed,
187    * not when the cell is set up. So states that are set
188    * initially should pass FALSE as the emit_signal argument.
189    */
190   if (emit_signal)
191     {
192       atk_object_notify_state_change (ATK_OBJECT (cell), state_type, TRUE);
193       /* If state_type is ATK_STATE_VISIBLE, additional notification */
194       if (state_type == ATK_STATE_VISIBLE)
195         g_signal_emit_by_name (cell, "visible-data-changed");
196     }
197
198   /* If the parent is a flyweight container cell, propagate the state
199    * change to it also
200    */
201   parent = atk_object_get_parent (ATK_OBJECT (cell));
202   if (GTK_IS_CONTAINER_CELL_ACCESSIBLE (parent))
203     _gtk_cell_accessible_add_state (GTK_CELL_ACCESSIBLE (parent), state_type, emit_signal);
204
205   return rc;
206 }
207
208 gboolean
209 _gtk_cell_accessible_remove_state (GtkCellAccessible *cell,
210                                    AtkStateType       state_type,
211                                    gboolean           emit_signal)
212 {
213   gboolean rc;
214   AtkObject *parent;
215
216   if (!atk_state_set_contains_state (cell->state_set, state_type))
217     return FALSE;
218
219   parent = atk_object_get_parent (ATK_OBJECT (cell));
220
221   rc = atk_state_set_remove_state (cell->state_set, state_type);
222
223   /* The signal should only be generated if the value changed,
224    * not when the cell is set up.  So states that are set
225    * initially should pass FALSE as the emit_signal argument.
226    */
227   if (emit_signal)
228     {
229       atk_object_notify_state_change (ATK_OBJECT (cell), state_type, FALSE);
230       /* If state_type is ATK_STATE_VISIBLE, additional notification */
231       if (state_type == ATK_STATE_VISIBLE)
232         g_signal_emit_by_name (cell, "visible-data-changed");
233     }
234
235   /* If the parent is a flyweight container cell, propagate the state
236    * change to it also
237    */
238   if (GTK_IS_CONTAINER_CELL_ACCESSIBLE (parent))
239     _gtk_cell_accessible_remove_state (GTK_CELL_ACCESSIBLE (parent), state_type, emit_signal);
240
241   return rc;
242 }
243
244 gboolean
245 _gtk_cell_accessible_add_action (GtkCellAccessible *cell,
246                                  const gchar       *name,
247                                  const gchar       *description,
248                                  const gchar       *keybinding,
249                                  void (*func) (GtkCellAccessible *))
250 {
251   ActionInfo *info;
252
253   info = g_new (ActionInfo, 1);
254   info->name = g_strdup (name);
255   info->description = g_strdup (description);
256   info->keybinding = g_strdup (keybinding);
257   info->do_action_func = func;
258
259   cell->action_list = g_list_append (cell->action_list, info);
260
261   return TRUE;
262 }
263
264 gboolean
265 _gtk_cell_accessible_remove_action (GtkCellAccessible *cell,
266                                     gint               index)
267 {
268   GList *l;
269
270   l = g_list_nth (cell->action_list, index);
271   if (l == NULL)
272     return FALSE;
273
274   destroy_action_info (l->data);
275   cell->action_list = g_list_remove_link (cell->action_list, l);
276
277   return TRUE;
278 }
279
280
281 gboolean
282 _gtk_cell_accessible_remove_action_by_name (GtkCellAccessible *cell,
283                                             const gchar       *name)
284 {
285   GList *l;
286
287   for (l = cell->action_list; l; l = l->next)
288     {
289       ActionInfo *info = l->data;
290
291       if (g_strcmp0 (info->name, name) == 0)
292         break;
293     }
294
295   if (l == NULL)
296     return FALSE;
297
298   destroy_action_info (l->data);
299   cell->action_list = g_list_remove_link (cell->action_list, l);
300
301   return TRUE;
302 }
303
304 static ActionInfo *
305 get_action_info (GtkCellAccessible *cell,
306                  gint               index)
307 {
308   GList *l;
309
310   l = g_list_nth (cell->action_list, index);
311   if (l == NULL)
312     return NULL;
313
314   return (ActionInfo *) (l->data);
315 }
316
317 static gint
318 gtk_cell_accessible_action_get_n_actions (AtkAction *action)
319 {
320   GtkCellAccessible *cell = GTK_CELL_ACCESSIBLE(action);
321   if (cell->action_list != NULL)
322     return g_list_length (cell->action_list);
323   else
324     return 0;
325 }
326
327 static const gchar *
328 gtk_cell_accessible_action_get_name (AtkAction *action,
329                                      gint       index)
330 {
331   GtkCellAccessible *cell = GTK_CELL_ACCESSIBLE (action);
332   ActionInfo *info;
333
334   info = get_action_info (cell, index);
335   if (info == NULL)
336     return NULL;
337
338   return info->name;
339 }
340
341 static const gchar *
342 gtk_cell_accessible_action_get_description (AtkAction *action,
343                                             gint       index)
344 {
345   GtkCellAccessible *cell = GTK_CELL_ACCESSIBLE (action);
346   ActionInfo *info;
347
348   info = get_action_info (cell, index);
349   if (info == NULL)
350     return NULL;
351
352   return info->description;
353 }
354
355 static const gchar *
356 gtk_cell_accessible_action_get_keybinding (AtkAction *action,
357                                            gint       index)
358 {
359   GtkCellAccessible *cell = GTK_CELL_ACCESSIBLE (action);
360   ActionInfo *info;
361
362   info = get_action_info (cell, index);
363   if (info == NULL)
364     return NULL;
365
366   return info->keybinding;
367 }
368
369 static gboolean
370 gtk_cell_accessible_action_do_action (AtkAction *action,
371                                       gint       index)
372 {
373   GtkCellAccessible *cell = GTK_CELL_ACCESSIBLE (action);
374   ActionInfo *info;
375
376   info = get_action_info (cell, index);
377   if (info == NULL)
378     return FALSE;
379
380   if (info->do_action_func == NULL)
381     return FALSE;
382
383   info->do_action_func (cell);
384
385   return TRUE;
386 }
387
388 static void
389 atk_action_interface_init (AtkActionIface *iface)
390 {
391   iface->get_n_actions = gtk_cell_accessible_action_get_n_actions;
392   iface->do_action = gtk_cell_accessible_action_do_action;
393   iface->get_name = gtk_cell_accessible_action_get_name;
394   iface->get_description = gtk_cell_accessible_action_get_description;
395   iface->get_keybinding = gtk_cell_accessible_action_get_keybinding;
396 }
397
398 static void
399 gtk_cell_accessible_get_extents (AtkComponent *component,
400                                  gint         *x,
401                                  gint         *y,
402                                  gint         *width,
403                                  gint         *height,
404                                  AtkCoordType  coord_type)
405 {
406   GtkCellAccessible *cell;
407   AtkObject *parent;
408
409   cell = GTK_CELL_ACCESSIBLE (component);
410   parent = gtk_widget_get_accessible (cell->widget);
411
412   _gtk_cell_accessible_parent_get_cell_extents (GTK_CELL_ACCESSIBLE_PARENT (parent),
413                                                 cell,
414                                                 x, y, width, height, coord_type);
415 }
416
417 static gboolean
418 gtk_cell_accessible_grab_focus (AtkComponent *component)
419 {
420   GtkCellAccessible *cell;
421   AtkObject *parent;
422
423   cell = GTK_CELL_ACCESSIBLE (component);
424   parent = gtk_widget_get_accessible (cell->widget);
425
426   return _gtk_cell_accessible_parent_grab_focus (GTK_CELL_ACCESSIBLE_PARENT (parent), cell);
427 }
428
429 static void
430 atk_component_interface_init (AtkComponentIface *iface)
431 {
432   iface->get_extents = gtk_cell_accessible_get_extents;
433   iface->grab_focus = gtk_cell_accessible_grab_focus;
434 }