]> Pileus Git - ~andy/gtk/blob - gtk/a11y/gtkcellaccessible.c
Plug a few memory leaks
[~andy/gtk] / gtk / a11y / gtkcellaccessible.c
1 /* GTK+ - accessibility implementations
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, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include "config.h"
19
20 #include <glib/gi18n-lib.h>
21 #include <gtk/gtk.h>
22 #include "gtkcontainercellaccessible.h"
23 #include "gtkcellaccessibleprivate.h"
24 #include "gtkcellaccessibleparent.h"
25
26 static const struct {
27   AtkState atk_state;
28   GtkCellRendererState renderer_state;
29   gboolean invert;
30 } state_map[] = {
31   { ATK_STATE_SENSITIVE, GTK_CELL_RENDERER_INSENSITIVE, TRUE },
32   { ATK_STATE_ENABLED,   GTK_CELL_RENDERER_INSENSITIVE, TRUE },
33   { ATK_STATE_SELECTED,  GTK_CELL_RENDERER_SELECTED,    FALSE },
34   /* XXX: why do we map ACTIVE here? */
35   { ATK_STATE_ACTIVE,    GTK_CELL_RENDERER_FOCUSED,     FALSE },
36   { ATK_STATE_FOCUSED,   GTK_CELL_RENDERER_FOCUSED,     FALSE },
37   { ATK_STATE_EXPANDABLE,GTK_CELL_RENDERER_EXPANDABLE,  FALSE },
38   { ATK_STATE_EXPANDED,  GTK_CELL_RENDERER_EXPANDED,    FALSE },
39 };
40
41 static GtkCellRendererState gtk_cell_accessible_get_state (GtkCellAccessible *cell);
42 static void atk_action_interface_init    (AtkActionIface    *iface);
43 static void atk_component_interface_init (AtkComponentIface *iface);
44
45 G_DEFINE_TYPE_WITH_CODE (GtkCellAccessible, gtk_cell_accessible, GTK_TYPE_ACCESSIBLE,
46                          G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, atk_action_interface_init)
47                          G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT, atk_component_interface_init))
48
49 static void
50 gtk_cell_accessible_object_finalize (GObject *obj)
51 {
52   AtkRelationSet *relation_set;
53   AtkRelation *relation;
54   GPtrArray *target;
55   gpointer target_object;
56   gint i;
57
58   relation_set = atk_object_ref_relation_set (ATK_OBJECT (obj));
59   if (ATK_IS_RELATION_SET (relation_set))
60     {
61       relation = atk_relation_set_get_relation_by_type (relation_set,
62                                                         ATK_RELATION_NODE_CHILD_OF);
63       if (relation)
64         {
65           target = atk_relation_get_target (relation);
66           for (i = 0; i < target->len; i++)
67             {
68               target_object = g_ptr_array_index (target, i);
69               if (GTK_IS_CELL_ACCESSIBLE (target_object))
70                 g_object_unref (target_object);
71             }
72         }
73       g_object_unref (relation_set);
74     }
75   G_OBJECT_CLASS (gtk_cell_accessible_parent_class)->finalize (obj);
76 }
77
78 static gint
79 gtk_cell_accessible_get_index_in_parent (AtkObject *obj)
80 {
81   GtkCellAccessible *cell;
82   AtkObject *parent;
83
84   cell = GTK_CELL_ACCESSIBLE (obj);
85
86   parent = atk_object_get_parent (obj);
87   if (GTK_IS_CONTAINER_CELL_ACCESSIBLE (parent))
88     return g_list_index (gtk_container_cell_accessible_get_children (GTK_CONTAINER_CELL_ACCESSIBLE (parent)), obj);
89
90   parent = gtk_widget_get_accessible (gtk_accessible_get_widget (GTK_ACCESSIBLE (cell)));
91   if (parent == NULL)
92     return -1;
93
94   return gtk_cell_accessible_parent_get_child_index (GTK_CELL_ACCESSIBLE_PARENT (parent), cell);
95 }
96
97 static AtkStateSet *
98 gtk_cell_accessible_ref_state_set (AtkObject *accessible)
99 {
100   GtkCellAccessible *cell_accessible;
101   AtkStateSet *state_set;
102   GtkCellRendererState flags;
103   guint i;
104
105   cell_accessible = GTK_CELL_ACCESSIBLE (accessible);
106
107   state_set = atk_state_set_new ();
108
109   if (gtk_accessible_get_widget (GTK_ACCESSIBLE (cell_accessible)) == NULL)
110     {
111       atk_state_set_add_state (state_set, ATK_STATE_DEFUNCT);
112       return state_set;
113     }
114
115   flags = gtk_cell_accessible_get_state (cell_accessible);
116
117   atk_state_set_add_state (state_set, ATK_STATE_FOCUSABLE);
118   atk_state_set_add_state (state_set, ATK_STATE_SELECTABLE);
119   atk_state_set_add_state (state_set, ATK_STATE_TRANSIENT);
120   atk_state_set_add_state (state_set, ATK_STATE_VISIBLE);
121
122   for (i = 0; i < G_N_ELEMENTS (state_map); i++)
123     {
124       if (flags & state_map[i].renderer_state)
125         {
126           if (!state_map[i].invert)
127             atk_state_set_add_state (state_set, state_map[i].atk_state);
128         }
129       else
130         {
131           if (state_map[i].invert)
132             atk_state_set_add_state (state_set, state_map[i].atk_state);
133         }
134     }
135
136   if (gtk_widget_get_mapped (gtk_accessible_get_widget (GTK_ACCESSIBLE (cell_accessible))))
137     atk_state_set_add_state (state_set, ATK_STATE_SHOWING);
138
139   return state_set;
140 }
141
142
143 static void
144 gtk_cell_accessible_class_init (GtkCellAccessibleClass *klass)
145 {
146   AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
147   GObjectClass *g_object_class = G_OBJECT_CLASS (klass);
148
149   g_object_class->finalize = gtk_cell_accessible_object_finalize;
150
151   class->get_index_in_parent = gtk_cell_accessible_get_index_in_parent;
152   class->ref_state_set = gtk_cell_accessible_ref_state_set;
153 }
154
155 static void
156 gtk_cell_accessible_init (GtkCellAccessible *cell)
157 {
158 }
159
160 void
161 _gtk_cell_accessible_initialize (GtkCellAccessible *cell,
162                                  GtkWidget         *widget,
163                                  AtkObject         *parent)
164 {
165   gtk_accessible_set_widget (GTK_ACCESSIBLE (cell), widget);
166   atk_object_set_parent (ATK_OBJECT (cell), parent);
167 }
168
169 gboolean
170 _gtk_cell_accessible_add_state (GtkCellAccessible *cell,
171                                 AtkStateType       state_type,
172                                 gboolean           emit_signal)
173 {
174   AtkObject *parent;
175
176   /* The signal should only be generated if the value changed,
177    * not when the cell is set up. So states that are set
178    * initially should pass FALSE as the emit_signal argument.
179    */
180   if (emit_signal)
181     {
182       atk_object_notify_state_change (ATK_OBJECT (cell), state_type, TRUE);
183       /* If state_type is ATK_STATE_VISIBLE, additional notification */
184       if (state_type == ATK_STATE_VISIBLE)
185         g_signal_emit_by_name (cell, "visible-data-changed");
186     }
187
188   /* If the parent is a flyweight container cell, propagate the state
189    * change to it also
190    */
191   parent = atk_object_get_parent (ATK_OBJECT (cell));
192   if (GTK_IS_CONTAINER_CELL_ACCESSIBLE (parent))
193     _gtk_cell_accessible_add_state (GTK_CELL_ACCESSIBLE (parent), state_type, emit_signal);
194
195   return TRUE;
196 }
197
198 gboolean
199 _gtk_cell_accessible_remove_state (GtkCellAccessible *cell,
200                                    AtkStateType       state_type,
201                                    gboolean           emit_signal)
202 {
203   AtkObject *parent;
204
205   parent = atk_object_get_parent (ATK_OBJECT (cell));
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, FALSE);
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   if (GTK_IS_CONTAINER_CELL_ACCESSIBLE (parent))
223     _gtk_cell_accessible_remove_state (GTK_CELL_ACCESSIBLE (parent), state_type, emit_signal);
224
225   return TRUE;
226 }
227
228 static gint
229 gtk_cell_accessible_action_get_n_actions (AtkAction *action)
230 {
231   return 3;
232 }
233
234 static const gchar *
235 gtk_cell_accessible_action_get_name (AtkAction *action,
236                                      gint       index)
237 {
238   switch (index)
239     {
240     case 0:
241       return "expand or contract";
242     case 1:
243       return "edit";
244     case 2:
245       return "activate";
246     default:
247       return NULL;
248     }
249 }
250
251 static const gchar *
252 gtk_cell_accessible_action_get_localized_name (AtkAction *action,
253                                                gint       index)
254 {
255   switch (index)
256     {
257     case 0:
258       return C_("Action name", "Expand or contract");
259     case 1:
260       return C_("Action name", "Edit");
261     case 2:
262       return C_("Action name", "Activate");
263     default:
264       return NULL;
265     }
266 }
267
268 static const gchar *
269 gtk_cell_accessible_action_get_description (AtkAction *action,
270                                             gint       index)
271 {
272   switch (index)
273     {
274     case 0:
275       return C_("Action description", "Expands or contracts the row in the tree view containing this cell");
276     case 1:
277       return C_("Action description", "Creates a widget in which the contents of the cell can be edited");
278     case 2:
279       return C_("Action description", "Activates the cell");
280     default:
281       return NULL;
282     }
283 }
284
285 static const gchar *
286 gtk_cell_accessible_action_get_keybinding (AtkAction *action,
287                                            gint       index)
288 {
289   return NULL;
290 }
291
292 static gboolean
293 gtk_cell_accessible_action_do_action (AtkAction *action,
294                                       gint       index)
295 {
296   GtkCellAccessible *cell = GTK_CELL_ACCESSIBLE (action);
297   GtkCellAccessibleParent *parent;
298
299   cell = GTK_CELL_ACCESSIBLE (action);
300   if (gtk_accessible_get_widget (GTK_ACCESSIBLE (cell)) == NULL)
301     return FALSE;
302
303   parent = GTK_CELL_ACCESSIBLE_PARENT (gtk_widget_get_accessible (gtk_accessible_get_widget (GTK_ACCESSIBLE (cell))));
304
305   switch (index)
306     {
307     case 0:
308       gtk_cell_accessible_parent_expand_collapse (parent, cell);
309       break;
310     case 1:
311       gtk_cell_accessible_parent_edit (parent, cell);
312       break;
313     case 2:
314       gtk_cell_accessible_parent_activate (parent, cell);
315       break;
316     default:
317       return FALSE;
318     }
319
320   return TRUE;
321 }
322
323 static void
324 atk_action_interface_init (AtkActionIface *iface)
325 {
326   iface->get_n_actions = gtk_cell_accessible_action_get_n_actions;
327   iface->do_action = gtk_cell_accessible_action_do_action;
328   iface->get_name = gtk_cell_accessible_action_get_name;
329   iface->get_localized_name = gtk_cell_accessible_action_get_localized_name;
330   iface->get_description = gtk_cell_accessible_action_get_description;
331   iface->get_keybinding = gtk_cell_accessible_action_get_keybinding;
332 }
333
334 static void
335 gtk_cell_accessible_get_extents (AtkComponent *component,
336                                  gint         *x,
337                                  gint         *y,
338                                  gint         *width,
339                                  gint         *height,
340                                  AtkCoordType  coord_type)
341 {
342   GtkCellAccessible *cell;
343   AtkObject *parent;
344
345   cell = GTK_CELL_ACCESSIBLE (component);
346   parent = gtk_widget_get_accessible (gtk_accessible_get_widget (GTK_ACCESSIBLE (cell)));
347
348   gtk_cell_accessible_parent_get_cell_extents (GTK_CELL_ACCESSIBLE_PARENT (parent),
349                                                 cell,
350                                                 x, y, width, height, coord_type);
351 }
352
353 static gboolean
354 gtk_cell_accessible_grab_focus (AtkComponent *component)
355 {
356   GtkCellAccessible *cell;
357   AtkObject *parent;
358
359   cell = GTK_CELL_ACCESSIBLE (component);
360   parent = gtk_widget_get_accessible (gtk_accessible_get_widget (GTK_ACCESSIBLE (cell)));
361
362   return gtk_cell_accessible_parent_grab_focus (GTK_CELL_ACCESSIBLE_PARENT (parent), cell);
363 }
364
365 static void
366 atk_component_interface_init (AtkComponentIface *iface)
367 {
368   iface->get_extents = gtk_cell_accessible_get_extents;
369   iface->grab_focus = gtk_cell_accessible_grab_focus;
370 }
371
372 static GtkCellRendererState
373 gtk_cell_accessible_get_state (GtkCellAccessible *cell)
374 {
375   AtkObject *parent;
376
377   g_return_val_if_fail (GTK_IS_CELL_ACCESSIBLE (cell), 0);
378
379   parent = gtk_widget_get_accessible (gtk_accessible_get_widget (GTK_ACCESSIBLE (cell)));
380   if (parent == NULL)
381     return 0;
382
383   return gtk_cell_accessible_parent_get_renderer_state (GTK_CELL_ACCESSIBLE_PARENT (parent), cell);
384 }
385
386 /*
387  * gtk_cell_accessible_state_changed:
388  * @cell: a #GtkCellAccessible
389  * @added: the flags that were added from @cell
390  * @removed: the flags that were removed from @cell
391  *
392  * Notifies @cell of state changes. Multiple states may be added
393  * or removed at the same time. A state that is @added may not be
394  * @removed at the same time.
395  **/
396 void
397 _gtk_cell_accessible_state_changed (GtkCellAccessible    *cell,
398                                     GtkCellRendererState  added,
399                                     GtkCellRendererState  removed)
400 {
401   AtkObject *object;
402   guint i;
403
404   g_return_if_fail (GTK_IS_CELL_ACCESSIBLE (cell));
405   g_return_if_fail ((added & removed) == 0);
406
407   object = ATK_OBJECT (cell);
408
409   for (i = 0; i < G_N_ELEMENTS (state_map); i++)
410     {
411       if (added & state_map[i].renderer_state)
412         atk_object_notify_state_change (object,
413                                         state_map[i].atk_state,
414                                         !state_map[i].invert);
415       if (added & state_map[i].renderer_state)
416         atk_object_notify_state_change (object,
417                                         state_map[i].atk_state,
418                                         state_map[i].invert);
419     }
420 }
421
422 /*
423  * gtk_cell_accessible_update_cache:
424  * @cell: the cell that is changed
425  *
426  * Notifies the cell that the values in the data in the row that
427  * is used to feed the cell renderer with has changed. The
428  * cell_changed function of @cell is called to send update
429  * notifications for the properties it takes from its cell
430  * renderer.
431  *
432  * Note that there is no higher granularity available about which
433  * properties changed, so you will need to make do with this
434  * function.
435  **/
436 void
437 _gtk_cell_accessible_update_cache (GtkCellAccessible *cell)
438 {
439   GtkCellAccessibleClass *klass;
440
441   g_return_if_fail (GTK_CELL_ACCESSIBLE (cell));
442
443   klass = GTK_CELL_ACCESSIBLE_GET_CLASS (cell);
444
445   if (klass->update_cache)
446     klass->update_cache (cell);
447 }