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