]> Pileus Git - ~andy/gtk/blob - gtk/a11y/gailcell.c
gail: No need to include modules/other in CFLAGS anymore
[~andy/gtk] / gtk / a11y / gailcell.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 <string.h>
23 #include <stdlib.h>
24 #include <gtk/gtk.h>
25 #include "gailcontainercell.h"
26 #include "gailcell.h"
27 #include "gailcellparent.h"
28
29 static void         gail_cell_class_init          (GailCellClass       *klass);
30 static void         gail_cell_destroyed           (GtkWidget           *widget,
31                                                    GailCell            *cell);
32
33 static void         gail_cell_init                (GailCell            *cell);
34 static void         gail_cell_object_finalize     (GObject             *cell);
35 static AtkStateSet* gail_cell_ref_state_set       (AtkObject           *obj);
36 static gint         gail_cell_get_index_in_parent (AtkObject           *obj);
37
38 /* AtkAction */
39
40 static void         atk_action_interface_init 
41                                                   (AtkActionIface      *iface);
42 static ActionInfo * _gail_cell_get_action_info    (GailCell            *cell,
43                                                    gint                index);
44 static void         _gail_cell_destroy_action_info 
45                                                   (gpointer            data,
46                                                    gpointer            user_data);
47
48 static gint         gail_cell_action_get_n_actions 
49                                                   (AtkAction           *action);
50 static const gchar*
51                     gail_cell_action_get_name     (AtkAction           *action,
52                                                    gint                index);
53 static const gchar *
54                     gail_cell_action_get_description 
55                                                   (AtkAction           *action,
56                                                    gint                index);
57 static gboolean     gail_cell_action_set_description 
58                                                   (AtkAction           *action,
59                                                    gint                index,
60                                                    const gchar         *desc);
61 static const gchar *
62                     gail_cell_action_get_keybinding 
63                                                   (AtkAction           *action,
64                                                    gint                index);
65 static gboolean     gail_cell_action_do_action    (AtkAction           *action,
66                                                    gint                index);
67 static gboolean     idle_do_action                (gpointer            data);
68
69 static void         atk_component_interface_init  (AtkComponentIface   *iface);
70 static void         gail_cell_get_extents         (AtkComponent        *component,
71                                                    gint                *x,
72                                                    gint                *y,
73                                                    gint                *width,
74                                                    gint                *height,
75                                                    AtkCoordType        coord_type);
76 static gboolean     gail_cell_grab_focus         (AtkComponent        *component);
77
78 G_DEFINE_TYPE_WITH_CODE (GailCell, gail_cell, ATK_TYPE_OBJECT,
79                          G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, atk_action_interface_init)
80                          G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT, atk_component_interface_init))
81
82 static void      
83 gail_cell_class_init (GailCellClass *klass)
84 {
85   AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
86   GObjectClass *g_object_class = G_OBJECT_CLASS (klass);
87
88   g_object_class->finalize = gail_cell_object_finalize;
89
90   class->get_index_in_parent = gail_cell_get_index_in_parent;
91   class->ref_state_set = gail_cell_ref_state_set;
92 }
93
94 void 
95 gail_cell_initialise (GailCell  *cell,
96                       GtkWidget *widget,
97                       AtkObject *parent,
98                       gint      index)
99 {
100   g_return_if_fail (GAIL_IS_CELL (cell));
101   g_return_if_fail (GTK_IS_WIDGET (widget));
102
103   cell->widget = widget;
104   atk_object_set_parent (ATK_OBJECT (cell), parent);
105   cell->index = index;
106
107   g_signal_connect_object (G_OBJECT (widget),
108                            "destroy",
109                            G_CALLBACK (gail_cell_destroyed ),
110                            cell, 0);
111 }
112
113 static void
114 gail_cell_destroyed (GtkWidget       *widget,
115                      GailCell        *cell)
116 {
117   /*
118    * This is the signal handler for the "destroy" signal for the 
119    * GtkWidget. We set the  pointer location to NULL;
120    */
121   cell->widget = NULL;
122 }
123
124 static void
125 gail_cell_init (GailCell *cell)
126 {
127   cell->state_set = atk_state_set_new ();
128   cell->widget = NULL;
129   cell->action_list = NULL;
130   cell->index = 0;
131   atk_state_set_add_state (cell->state_set, ATK_STATE_TRANSIENT);
132   atk_state_set_add_state (cell->state_set, ATK_STATE_ENABLED);
133   atk_state_set_add_state (cell->state_set, ATK_STATE_SENSITIVE);
134   atk_state_set_add_state (cell->state_set, ATK_STATE_SELECTABLE);
135   cell->refresh_index = NULL;
136 }
137
138 static void
139 gail_cell_object_finalize (GObject *obj)
140 {
141   GailCell *cell = GAIL_CELL (obj);
142   AtkRelationSet *relation_set;
143   AtkRelation *relation;
144   GPtrArray *target;
145   gpointer target_object;
146   gint i;
147
148   if (cell->state_set)
149     g_object_unref (cell->state_set);
150   if (cell->action_list)
151     {
152       g_list_foreach (cell->action_list, _gail_cell_destroy_action_info, NULL);
153       g_list_free (cell->action_list);
154     }
155   if (cell->action_idle_handler)
156     {
157       g_source_remove (cell->action_idle_handler);
158       cell->action_idle_handler = 0;
159     }
160   relation_set = atk_object_ref_relation_set (ATK_OBJECT (obj));
161   if (ATK_IS_RELATION_SET (relation_set))
162     {
163       relation = atk_relation_set_get_relation_by_type (relation_set, 
164                                                         ATK_RELATION_NODE_CHILD_OF);
165       if (relation)
166         {
167           target = atk_relation_get_target (relation);
168           for (i = 0; i < target->len; i++)
169             {
170               target_object = g_ptr_array_index (target, i);
171               if (GAIL_IS_CELL (target_object))
172                 {
173                   g_object_unref (target_object);
174                 }
175             }
176         }
177       g_object_unref (relation_set);
178     }
179   G_OBJECT_CLASS (gail_cell_parent_class)->finalize (obj);
180 }
181
182 static AtkStateSet *
183 gail_cell_ref_state_set (AtkObject *obj)
184 {
185   GailCell *cell = GAIL_CELL (obj);
186   g_assert (cell->state_set);
187
188   g_object_ref(cell->state_set);
189   return cell->state_set;
190 }
191
192 gboolean
193 gail_cell_add_state (GailCell     *cell, 
194                      AtkStateType state_type,
195                      gboolean     emit_signal)
196 {
197   if (!atk_state_set_contains_state (cell->state_set, state_type))
198     {
199       gboolean rc;
200       AtkObject *parent;
201     
202       rc = atk_state_set_add_state (cell->state_set, state_type);
203       /*
204        * The signal should only be generated if the value changed,
205        * not when the cell is set up.  So states that are set
206        * initially should pass FALSE as the emit_signal argument.
207        */
208
209       if (emit_signal)
210         {
211           atk_object_notify_state_change (ATK_OBJECT (cell), state_type, TRUE);
212           /* If state_type is ATK_STATE_VISIBLE, additional notification */
213           if (state_type == ATK_STATE_VISIBLE)
214             g_signal_emit_by_name (cell, "visible_data_changed");
215         }
216
217       /* 
218        * If the parent is a flyweight container cell, propagate the state 
219        * change to it also 
220        */
221
222       parent = atk_object_get_parent (ATK_OBJECT (cell));
223       if (GAIL_IS_CONTAINER_CELL (parent))
224         gail_cell_add_state (GAIL_CELL (parent), state_type, emit_signal);
225       return rc;
226     }
227   else
228     return FALSE;
229 }
230
231 gboolean
232 gail_cell_remove_state (GailCell     *cell, 
233                         AtkStateType state_type,
234                         gboolean     emit_signal)
235 {
236   if (atk_state_set_contains_state (cell->state_set, state_type))
237     {
238       gboolean rc;
239       AtkObject *parent;
240
241       parent = atk_object_get_parent (ATK_OBJECT (cell));
242
243       rc = atk_state_set_remove_state (cell->state_set, state_type);
244       /*
245        * The signal should only be generated if the value changed,
246        * not when the cell is set up.  So states that are set
247        * initially should pass FALSE as the emit_signal argument.
248        */
249
250       if (emit_signal)
251         {
252           atk_object_notify_state_change (ATK_OBJECT (cell), state_type, FALSE);
253           /* If state_type is ATK_STATE_VISIBLE, additional notification */
254           if (state_type == ATK_STATE_VISIBLE)
255             g_signal_emit_by_name (cell, "visible_data_changed");
256         }
257
258       /* 
259        * If the parent is a flyweight container cell, propagate the state 
260        * change to it also 
261        */
262
263       if (GAIL_IS_CONTAINER_CELL (parent))
264         gail_cell_remove_state (GAIL_CELL (parent), state_type, emit_signal);
265       return rc;
266     }
267   else
268     return FALSE;
269 }
270
271 static gint
272 gail_cell_get_index_in_parent (AtkObject *obj)
273 {
274   GailCell *cell;
275
276   g_assert (GAIL_IS_CELL (obj));
277
278   cell = GAIL_CELL (obj);
279   if (atk_state_set_contains_state (cell->state_set, ATK_STATE_STALE))
280     if (cell->refresh_index)
281       {
282         cell->refresh_index (cell);
283         atk_state_set_remove_state (cell->state_set, ATK_STATE_STALE);
284       }
285   return cell->index;
286 }
287
288 static void
289 atk_action_interface_init (AtkActionIface *iface)
290 {
291   iface->get_n_actions = gail_cell_action_get_n_actions;
292   iface->do_action = gail_cell_action_do_action;
293   iface->get_name = gail_cell_action_get_name;
294   iface->get_description = gail_cell_action_get_description;
295   iface->set_description = gail_cell_action_set_description;
296   iface->get_keybinding = gail_cell_action_get_keybinding;
297 }
298
299 gboolean
300 gail_cell_add_action (GailCell    *cell,
301                       const gchar *action_name,
302                       const gchar *action_description,
303                       const gchar *action_keybinding,
304                       ACTION_FUNC action_func)
305 {
306   ActionInfo *info;
307   g_return_val_if_fail (GAIL_IS_CELL (cell), FALSE);
308   info = g_new (ActionInfo, 1);
309
310   if (action_name != NULL)
311     info->name = g_strdup (action_name);
312   else
313     info->name = NULL;
314   if (action_description != NULL)
315     info->description = g_strdup (action_description);
316   else
317     info->description = NULL;
318   if (action_keybinding != NULL)
319     info->keybinding = g_strdup (action_keybinding);
320   else
321     info->keybinding = NULL;
322   info->do_action_func = action_func;
323
324   cell->action_list = g_list_append (cell->action_list, (gpointer) info);
325   return TRUE;
326 }
327
328 gboolean
329 gail_cell_remove_action (GailCell *cell,
330                          gint     action_index)
331 {
332   GList *list_node;
333
334   g_return_val_if_fail (GAIL_IS_CELL (cell), FALSE);
335   list_node = g_list_nth (cell->action_list, action_index);
336   if (!list_node)
337     return FALSE;
338   _gail_cell_destroy_action_info (list_node->data, NULL);
339   cell->action_list = g_list_remove_link (cell->action_list, list_node);
340   return TRUE;
341 }
342
343
344 gboolean
345 gail_cell_remove_action_by_name (GailCell    *cell,
346                                  const gchar *action_name)
347 {
348   GList *list_node;
349   gboolean action_found= FALSE;
350   
351   g_return_val_if_fail (GAIL_IS_CELL (cell), FALSE);
352   for (list_node = cell->action_list; list_node && !action_found; 
353                     list_node = list_node->next)
354     {
355       if (!strcmp (((ActionInfo *)(list_node->data))->name, action_name))
356         {
357           action_found = TRUE;
358           break;
359         }
360     }
361   if (!action_found)
362     return FALSE;
363   _gail_cell_destroy_action_info (list_node->data, NULL);
364   cell->action_list = g_list_remove_link (cell->action_list, list_node);
365   return TRUE;
366 }
367
368 static ActionInfo *
369 _gail_cell_get_action_info (GailCell *cell,
370                             gint     index)
371 {
372   GList *list_node;
373
374   g_return_val_if_fail (GAIL_IS_CELL (cell), NULL);
375   if (cell->action_list == NULL)
376     return NULL;
377   list_node = g_list_nth (cell->action_list, index);
378   if (!list_node)
379     return NULL;
380   return (ActionInfo *) (list_node->data);
381 }
382
383
384 static void
385 _gail_cell_destroy_action_info (gpointer action_info, 
386                                 gpointer user_data)
387 {
388   ActionInfo *info = (ActionInfo *)action_info;
389   g_assert (info != NULL);
390   g_free (info->name);
391   g_free (info->description);
392   g_free (info->keybinding);
393   g_free (info);
394 }
395 static gint
396 gail_cell_action_get_n_actions (AtkAction *action)
397 {
398   GailCell *cell = GAIL_CELL(action);
399   if (cell->action_list != NULL)
400     return g_list_length (cell->action_list);
401   else
402     return 0;
403 }
404
405 static const gchar *
406 gail_cell_action_get_name (AtkAction *action,
407                            gint      index)
408 {
409   GailCell *cell = GAIL_CELL(action);
410   ActionInfo *info = _gail_cell_get_action_info (cell, index);
411
412   if (info == NULL)
413     return NULL;
414   return info->name;
415 }
416
417 static const gchar *
418 gail_cell_action_get_description (AtkAction *action,
419                                   gint      index)
420 {
421   GailCell *cell = GAIL_CELL(action);
422   ActionInfo *info = _gail_cell_get_action_info (cell, index);
423
424   if (info == NULL)
425     return NULL;
426   return info->description;
427 }
428
429 static gboolean
430 gail_cell_action_set_description (AtkAction   *action,
431                                   gint        index,
432                                   const gchar *desc)
433 {
434   GailCell *cell = GAIL_CELL(action);
435   ActionInfo *info = _gail_cell_get_action_info (cell, index);
436
437   if (info == NULL)
438     return FALSE;
439   g_free (info->description);
440   info->description = g_strdup (desc);
441   return TRUE;
442 }
443
444 static const gchar *
445 gail_cell_action_get_keybinding (AtkAction *action,
446                                  gint      index)
447 {
448   GailCell *cell = GAIL_CELL(action);
449   ActionInfo *info = _gail_cell_get_action_info (cell, index);
450   if (info == NULL)
451     return NULL;
452   return info->keybinding;
453 }
454
455 static gboolean
456 gail_cell_action_do_action (AtkAction *action,
457                             gint      index)
458 {
459   GailCell *cell = GAIL_CELL(action);
460   ActionInfo *info = _gail_cell_get_action_info (cell, index);
461   if (info == NULL)
462     return FALSE;
463   if (info->do_action_func == NULL)
464     return FALSE;
465   if (cell->action_idle_handler)
466     return FALSE;
467   cell->action_func = info->do_action_func;
468   cell->action_idle_handler = gdk_threads_add_idle (idle_do_action, cell);
469   return TRUE;
470 }
471
472 static gboolean
473 idle_do_action (gpointer data)
474 {
475   GailCell *cell;
476
477   cell = GAIL_CELL (data);
478   cell->action_idle_handler = 0;
479   cell->action_func (cell);
480
481   return FALSE;
482 }
483
484 static void
485 atk_component_interface_init (AtkComponentIface *iface)
486 {
487   iface->get_extents = gail_cell_get_extents;
488   iface->grab_focus = gail_cell_grab_focus;
489 }
490
491 static void 
492 gail_cell_get_extents (AtkComponent *component,
493                        gint         *x,
494                        gint         *y,
495                        gint         *width,
496                        gint         *height,
497                        AtkCoordType coord_type)
498 {
499   GailCell *gailcell;
500   AtkObject *cell_parent;
501
502   g_assert (GAIL_IS_CELL (component));
503
504   gailcell = GAIL_CELL (component);
505
506   cell_parent = gtk_widget_get_accessible (gailcell->widget);
507
508   gail_cell_parent_get_cell_extents (GAIL_CELL_PARENT (cell_parent), 
509                                    gailcell, x, y, width, height, coord_type);
510 }
511
512 static gboolean 
513 gail_cell_grab_focus (AtkComponent *component)
514 {
515   GailCell *gailcell;
516   AtkObject *cell_parent;
517
518   g_assert (GAIL_IS_CELL (component));
519
520   gailcell = GAIL_CELL (component);
521
522   cell_parent = gtk_widget_get_accessible (gailcell->widget);
523
524   return gail_cell_parent_grab_focus (GAIL_CELL_PARENT (cell_parent), 
525                                       gailcell);
526 }