]> Pileus Git - ~andy/gtk/blob - gtk/a11y/gtkcellaccessible.c
a11y: Don't duplicate information anymore
[~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->action_list)
65     g_list_free_full (cell->action_list, destroy_action_info);
66
67   relation_set = atk_object_ref_relation_set (ATK_OBJECT (obj));
68   if (ATK_IS_RELATION_SET (relation_set))
69     {
70       relation = atk_relation_set_get_relation_by_type (relation_set,
71                                                         ATK_RELATION_NODE_CHILD_OF);
72       if (relation)
73         {
74           target = atk_relation_get_target (relation);
75           for (i = 0; i < target->len; i++)
76             {
77               target_object = g_ptr_array_index (target, i);
78               if (GTK_IS_CELL_ACCESSIBLE (target_object))
79                 g_object_unref (target_object);
80             }
81         }
82       g_object_unref (relation_set);
83     }
84   G_OBJECT_CLASS (_gtk_cell_accessible_parent_class)->finalize (obj);
85 }
86
87 static gint
88 gtk_cell_accessible_get_index_in_parent (AtkObject *obj)
89 {
90   GtkCellAccessible *cell;
91   AtkObject *parent;
92
93   cell = GTK_CELL_ACCESSIBLE (obj);
94
95   parent = atk_object_get_parent (obj);
96   if (GTK_IS_CONTAINER_CELL_ACCESSIBLE (parent))
97     return g_list_index (GTK_CONTAINER_CELL_ACCESSIBLE (parent)->children, obj);
98
99   parent = gtk_widget_get_accessible (cell->widget);
100   if (parent == NULL)
101     return -1;
102
103   return _gtk_cell_accessible_parent_get_child_index (GTK_CELL_ACCESSIBLE_PARENT (parent), cell);
104 }
105
106 static AtkStateSet *
107 gtk_cell_accessible_ref_state_set (AtkObject *accessible)
108 {
109   GtkCellAccessible *cell_accessible;
110   AtkStateSet *state_set;
111   GtkCellRendererState flags;
112
113   cell_accessible = GTK_CELL_ACCESSIBLE (accessible);
114
115   state_set = atk_state_set_new ();
116
117   if (cell_accessible->widget == NULL)
118     {
119       atk_state_set_add_state (state_set, ATK_STATE_DEFUNCT);
120       return state_set;
121     }
122
123   flags = _gtk_cell_accessible_get_state (cell_accessible);
124
125   atk_state_set_add_state (state_set, ATK_STATE_TRANSIENT);
126
127   if (!(flags & GTK_CELL_RENDERER_INSENSITIVE))
128     {
129       atk_state_set_add_state (state_set, ATK_STATE_SENSITIVE);
130       atk_state_set_add_state (state_set, ATK_STATE_ENABLED);
131     }
132
133   atk_state_set_add_state (state_set, ATK_STATE_SELECTABLE);
134   if (flags & GTK_CELL_RENDERER_SELECTED)
135     atk_state_set_add_state (state_set, ATK_STATE_SELECTED);
136
137   atk_state_set_add_state (state_set, ATK_STATE_VISIBLE);
138   if (gtk_widget_get_mapped (cell_accessible->widget))
139     atk_state_set_add_state (state_set, ATK_STATE_SHOWING);
140
141   /* This is not completely right. We should be tracking the
142    * focussed cell renderer, but that involves diving into
143    * cell areas...
144    */
145   atk_state_set_add_state (state_set, ATK_STATE_FOCUSABLE);
146   if (flags & GTK_CELL_RENDERER_FOCUSED)
147     {
148       /* XXX: Why do we set ACTIVE here? */
149       atk_state_set_add_state (state_set, ATK_STATE_ACTIVE);
150       atk_state_set_add_state (state_set, ATK_STATE_FOCUSED);
151     }
152
153   if (flags & GTK_CELL_RENDERER_EXPANDABLE)
154     atk_state_set_add_state (state_set, ATK_STATE_EXPANDABLE);
155   if (flags & GTK_CELL_RENDERER_EXPANDED)
156     atk_state_set_add_state (state_set, ATK_STATE_EXPANDED);
157   
158   return state_set;
159 }
160
161
162 static void
163 _gtk_cell_accessible_class_init (GtkCellAccessibleClass *klass)
164 {
165   AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
166   GObjectClass *g_object_class = G_OBJECT_CLASS (klass);
167
168   g_object_class->finalize = gtk_cell_accessible_object_finalize;
169
170   class->get_index_in_parent = gtk_cell_accessible_get_index_in_parent;
171   class->ref_state_set = gtk_cell_accessible_ref_state_set;
172 }
173
174 static void
175 _gtk_cell_accessible_init (GtkCellAccessible *cell)
176 {
177   cell->widget = NULL;
178   cell->action_list = NULL;
179 }
180
181 static void
182 widget_destroyed (GtkWidget         *widget,
183                   GtkCellAccessible *cell)
184 {
185   cell->widget = NULL;
186 }
187
188 void
189 _gtk_cell_accessible_initialise (GtkCellAccessible *cell,
190                                  GtkWidget         *widget,
191                                  AtkObject         *parent)
192 {
193   cell->widget = widget;
194   atk_object_set_parent (ATK_OBJECT (cell), parent);
195
196   g_signal_connect_object (G_OBJECT (widget), "destroy",
197                            G_CALLBACK (widget_destroyed), cell, 0);
198 }
199
200 gboolean
201 _gtk_cell_accessible_add_state (GtkCellAccessible *cell,
202                                 AtkStateType       state_type,
203                                 gboolean           emit_signal)
204 {
205   AtkObject *parent;
206
207   /* The signal should only be generated if the value changed,
208    * not when the cell is set up. So states that are set
209    * initially should pass FALSE as the emit_signal argument.
210    */
211   if (emit_signal)
212     {
213       atk_object_notify_state_change (ATK_OBJECT (cell), state_type, TRUE);
214       /* If state_type is ATK_STATE_VISIBLE, additional notification */
215       if (state_type == ATK_STATE_VISIBLE)
216         g_signal_emit_by_name (cell, "visible-data-changed");
217     }
218
219   /* If the parent is a flyweight container cell, propagate the state
220    * change to it also
221    */
222   parent = atk_object_get_parent (ATK_OBJECT (cell));
223   if (GTK_IS_CONTAINER_CELL_ACCESSIBLE (parent))
224     _gtk_cell_accessible_add_state (GTK_CELL_ACCESSIBLE (parent), state_type, emit_signal);
225
226   return TRUE;
227 }
228
229 gboolean
230 _gtk_cell_accessible_remove_state (GtkCellAccessible *cell,
231                                    AtkStateType       state_type,
232                                    gboolean           emit_signal)
233 {
234   AtkObject *parent;
235
236   parent = atk_object_get_parent (ATK_OBJECT (cell));
237
238   /* The signal should only be generated if the value changed,
239    * not when the cell is set up.  So states that are set
240    * initially should pass FALSE as the emit_signal argument.
241    */
242   if (emit_signal)
243     {
244       atk_object_notify_state_change (ATK_OBJECT (cell), state_type, FALSE);
245       /* If state_type is ATK_STATE_VISIBLE, additional notification */
246       if (state_type == ATK_STATE_VISIBLE)
247         g_signal_emit_by_name (cell, "visible-data-changed");
248     }
249
250   /* If the parent is a flyweight container cell, propagate the state
251    * change to it also
252    */
253   if (GTK_IS_CONTAINER_CELL_ACCESSIBLE (parent))
254     _gtk_cell_accessible_remove_state (GTK_CELL_ACCESSIBLE (parent), state_type, emit_signal);
255
256   return TRUE;
257 }
258
259 gboolean
260 _gtk_cell_accessible_add_action (GtkCellAccessible *cell,
261                                  const gchar       *name,
262                                  const gchar       *description,
263                                  const gchar       *keybinding,
264                                  void (*func) (GtkCellAccessible *))
265 {
266   ActionInfo *info;
267
268   info = g_new (ActionInfo, 1);
269   info->name = g_strdup (name);
270   info->description = g_strdup (description);
271   info->keybinding = g_strdup (keybinding);
272   info->do_action_func = func;
273
274   cell->action_list = g_list_append (cell->action_list, info);
275
276   return TRUE;
277 }
278
279 gboolean
280 _gtk_cell_accessible_remove_action (GtkCellAccessible *cell,
281                                     gint               index)
282 {
283   GList *l;
284
285   l = g_list_nth (cell->action_list, index);
286   if (l == NULL)
287     return FALSE;
288
289   destroy_action_info (l->data);
290   cell->action_list = g_list_remove_link (cell->action_list, l);
291
292   return TRUE;
293 }
294
295
296 gboolean
297 _gtk_cell_accessible_remove_action_by_name (GtkCellAccessible *cell,
298                                             const gchar       *name)
299 {
300   GList *l;
301
302   for (l = cell->action_list; l; l = l->next)
303     {
304       ActionInfo *info = l->data;
305
306       if (g_strcmp0 (info->name, name) == 0)
307         break;
308     }
309
310   if (l == NULL)
311     return FALSE;
312
313   destroy_action_info (l->data);
314   cell->action_list = g_list_remove_link (cell->action_list, l);
315
316   return TRUE;
317 }
318
319 static ActionInfo *
320 get_action_info (GtkCellAccessible *cell,
321                  gint               index)
322 {
323   GList *l;
324
325   l = g_list_nth (cell->action_list, index);
326   if (l == NULL)
327     return NULL;
328
329   return (ActionInfo *) (l->data);
330 }
331
332 static gint
333 gtk_cell_accessible_action_get_n_actions (AtkAction *action)
334 {
335   GtkCellAccessible *cell = GTK_CELL_ACCESSIBLE(action);
336   if (cell->action_list != NULL)
337     return g_list_length (cell->action_list);
338   else
339     return 0;
340 }
341
342 static const gchar *
343 gtk_cell_accessible_action_get_name (AtkAction *action,
344                                      gint       index)
345 {
346   GtkCellAccessible *cell = GTK_CELL_ACCESSIBLE (action);
347   ActionInfo *info;
348
349   info = get_action_info (cell, index);
350   if (info == NULL)
351     return NULL;
352
353   return info->name;
354 }
355
356 static const gchar *
357 gtk_cell_accessible_action_get_description (AtkAction *action,
358                                             gint       index)
359 {
360   GtkCellAccessible *cell = GTK_CELL_ACCESSIBLE (action);
361   ActionInfo *info;
362
363   info = get_action_info (cell, index);
364   if (info == NULL)
365     return NULL;
366
367   return info->description;
368 }
369
370 static const gchar *
371 gtk_cell_accessible_action_get_keybinding (AtkAction *action,
372                                            gint       index)
373 {
374   GtkCellAccessible *cell = GTK_CELL_ACCESSIBLE (action);
375   ActionInfo *info;
376
377   info = get_action_info (cell, index);
378   if (info == NULL)
379     return NULL;
380
381   return info->keybinding;
382 }
383
384 static gboolean
385 gtk_cell_accessible_action_do_action (AtkAction *action,
386                                       gint       index)
387 {
388   GtkCellAccessible *cell = GTK_CELL_ACCESSIBLE (action);
389   ActionInfo *info;
390
391   info = get_action_info (cell, index);
392   if (info == NULL)
393     return FALSE;
394
395   if (info->do_action_func == NULL)
396     return FALSE;
397
398   info->do_action_func (cell);
399
400   return TRUE;
401 }
402
403 static void
404 atk_action_interface_init (AtkActionIface *iface)
405 {
406   iface->get_n_actions = gtk_cell_accessible_action_get_n_actions;
407   iface->do_action = gtk_cell_accessible_action_do_action;
408   iface->get_name = gtk_cell_accessible_action_get_name;
409   iface->get_description = gtk_cell_accessible_action_get_description;
410   iface->get_keybinding = gtk_cell_accessible_action_get_keybinding;
411 }
412
413 static void
414 gtk_cell_accessible_get_extents (AtkComponent *component,
415                                  gint         *x,
416                                  gint         *y,
417                                  gint         *width,
418                                  gint         *height,
419                                  AtkCoordType  coord_type)
420 {
421   GtkCellAccessible *cell;
422   AtkObject *parent;
423
424   cell = GTK_CELL_ACCESSIBLE (component);
425   parent = gtk_widget_get_accessible (cell->widget);
426
427   _gtk_cell_accessible_parent_get_cell_extents (GTK_CELL_ACCESSIBLE_PARENT (parent),
428                                                 cell,
429                                                 x, y, width, height, coord_type);
430 }
431
432 static gboolean
433 gtk_cell_accessible_grab_focus (AtkComponent *component)
434 {
435   GtkCellAccessible *cell;
436   AtkObject *parent;
437
438   cell = GTK_CELL_ACCESSIBLE (component);
439   parent = gtk_widget_get_accessible (cell->widget);
440
441   return _gtk_cell_accessible_parent_grab_focus (GTK_CELL_ACCESSIBLE_PARENT (parent), cell);
442 }
443
444 static void
445 atk_component_interface_init (AtkComponentIface *iface)
446 {
447   iface->get_extents = gtk_cell_accessible_get_extents;
448   iface->grab_focus = gtk_cell_accessible_grab_focus;
449 }
450
451 /**
452  * _gtk_cell_accessible_set_cell_data:
453  * @cell: a #GtkCellAccessible
454  *
455  * Sets the cell data to the row used by @cell. This is useful in
456  * particular if you want to work with cell renderers.
457  *
458  * Note that this function is potentially slow, so be careful.
459  **/
460 void
461 _gtk_cell_accessible_set_cell_data (GtkCellAccessible *cell)
462 {
463   AtkObject *parent;
464
465   g_return_if_fail (GTK_IS_CELL_ACCESSIBLE (cell));
466
467   parent = gtk_widget_get_accessible (cell->widget);
468   if (parent == NULL)
469     return;
470
471   _gtk_cell_accessible_parent_set_cell_data (GTK_CELL_ACCESSIBLE_PARENT (parent), cell);
472 }
473
474 /**
475  * _gtk_cell_accessible_get_state:
476  * @cell: a #GtkCellAccessible
477  *
478  * Gets the state that would be used to render the area referenced by @cell.
479  *
480  * Returns: the #GtkCellRendererState for cell
481  **/
482 GtkCellRendererState
483 _gtk_cell_accessible_get_state (GtkCellAccessible *cell)
484 {
485   AtkObject *parent;
486
487   g_return_val_if_fail (GTK_IS_CELL_ACCESSIBLE (cell), 0);
488
489   parent = gtk_widget_get_accessible (cell->widget);
490   if (parent == NULL)
491     return 0;
492
493   return _gtk_cell_accessible_parent_get_renderer_state (GTK_CELL_ACCESSIBLE_PARENT (parent), cell);
494 }