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