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 
G_DEFINE_TYPE_WITH_CODE(GailCell,gail_cell,ATK_TYPE_OBJECT,G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION,atk_action_interface_init)G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT,atk_component_interface_init))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
gail_cell_initialise(GailCell * cell,GtkWidget * widget,AtkObject * parent,gint index)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
gail_cell_destroyed(GtkWidget * widget,GailCell * cell)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
gail_cell_init(GailCell * cell)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
gail_cell_object_finalize(GObject * obj)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 *
gail_cell_ref_state_set(AtkObject * obj)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
gail_cell_add_state(GailCell * cell,AtkStateType state_type,gboolean emit_signal)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
gail_cell_remove_state(GailCell * cell,AtkStateType state_type,gboolean emit_signal)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
gail_cell_get_index_in_parent(AtkObject * obj)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
atk_action_interface_init(AtkActionIface * iface)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 /*
300  * Deprecated: 2.22: The action interface is added for all cells now.
301  */
302 void
gail_cell_type_add_action_interface(GType type)303 gail_cell_type_add_action_interface (GType type)
304 {
305 }
306 
307 gboolean
gail_cell_add_action(GailCell * cell,const gchar * action_name,const gchar * action_description,const gchar * action_keybinding,ACTION_FUNC action_func)308 gail_cell_add_action (GailCell    *cell,
309 		      const gchar *action_name,
310 		      const gchar *action_description,
311 		      const gchar *action_keybinding,
312 		      ACTION_FUNC action_func)
313 {
314   ActionInfo *info;
315   g_return_val_if_fail (GAIL_IS_CELL (cell), FALSE);
316   info = g_new (ActionInfo, 1);
317 
318   if (action_name != NULL)
319     info->name = g_strdup (action_name);
320   else
321     info->name = NULL;
322   if (action_description != NULL)
323     info->description = g_strdup (action_description);
324   else
325     info->description = NULL;
326   if (action_keybinding != NULL)
327     info->keybinding = g_strdup (action_keybinding);
328   else
329     info->keybinding = NULL;
330   info->do_action_func = action_func;
331 
332   cell->action_list = g_list_append (cell->action_list, (gpointer) info);
333   return TRUE;
334 }
335 
336 gboolean
gail_cell_remove_action(GailCell * cell,gint action_index)337 gail_cell_remove_action (GailCell *cell,
338 			 gint     action_index)
339 {
340   GList *list_node;
341 
342   g_return_val_if_fail (GAIL_IS_CELL (cell), FALSE);
343   list_node = g_list_nth (cell->action_list, action_index);
344   if (!list_node)
345     return FALSE;
346   _gail_cell_destroy_action_info (list_node->data, NULL);
347   cell->action_list = g_list_remove_link (cell->action_list, list_node);
348   return TRUE;
349 }
350 
351 
352 gboolean
gail_cell_remove_action_by_name(GailCell * cell,const gchar * action_name)353 gail_cell_remove_action_by_name (GailCell    *cell,
354 				 const gchar *action_name)
355 {
356   GList *list_node;
357   gboolean action_found= FALSE;
358 
359   g_return_val_if_fail (GAIL_IS_CELL (cell), FALSE);
360   for (list_node = cell->action_list; list_node && !action_found;
361                     list_node = list_node->next)
362     {
363       if (!strcmp (((ActionInfo *)(list_node->data))->name, action_name))
364 	{
365 	  action_found = TRUE;
366 	  break;
367 	}
368     }
369   if (!action_found)
370     return FALSE;
371   _gail_cell_destroy_action_info (list_node->data, NULL);
372   cell->action_list = g_list_remove_link (cell->action_list, list_node);
373   return TRUE;
374 }
375 
376 static ActionInfo *
_gail_cell_get_action_info(GailCell * cell,gint index)377 _gail_cell_get_action_info (GailCell *cell,
378 			    gint     index)
379 {
380   GList *list_node;
381 
382   g_return_val_if_fail (GAIL_IS_CELL (cell), NULL);
383   if (cell->action_list == NULL)
384     return NULL;
385   list_node = g_list_nth (cell->action_list, index);
386   if (!list_node)
387     return NULL;
388   return (ActionInfo *) (list_node->data);
389 }
390 
391 
392 static void
_gail_cell_destroy_action_info(gpointer action_info,gpointer user_data)393 _gail_cell_destroy_action_info (gpointer action_info,
394                                 gpointer user_data)
395 {
396   ActionInfo *info = (ActionInfo *)action_info;
397   g_assert (info != NULL);
398   g_free (info->name);
399   g_free (info->description);
400   g_free (info->keybinding);
401   g_free (info);
402 }
403 static gint
gail_cell_action_get_n_actions(AtkAction * action)404 gail_cell_action_get_n_actions (AtkAction *action)
405 {
406   GailCell *cell = GAIL_CELL(action);
407   if (cell->action_list != NULL)
408     return g_list_length (cell->action_list);
409   else
410     return 0;
411 }
412 
413 static const gchar *
gail_cell_action_get_name(AtkAction * action,gint index)414 gail_cell_action_get_name (AtkAction *action,
415 			   gint      index)
416 {
417   GailCell *cell = GAIL_CELL(action);
418   ActionInfo *info = _gail_cell_get_action_info (cell, index);
419 
420   if (info == NULL)
421     return NULL;
422   return info->name;
423 }
424 
425 static const gchar *
gail_cell_action_get_description(AtkAction * action,gint index)426 gail_cell_action_get_description (AtkAction *action,
427 				  gint      index)
428 {
429   GailCell *cell = GAIL_CELL(action);
430   ActionInfo *info = _gail_cell_get_action_info (cell, index);
431 
432   if (info == NULL)
433     return NULL;
434   return info->description;
435 }
436 
437 static gboolean
gail_cell_action_set_description(AtkAction * action,gint index,const gchar * desc)438 gail_cell_action_set_description (AtkAction   *action,
439 				  gint        index,
440 				  const gchar *desc)
441 {
442   GailCell *cell = GAIL_CELL(action);
443   ActionInfo *info = _gail_cell_get_action_info (cell, index);
444 
445   if (info == NULL)
446     return FALSE;
447   g_free (info->description);
448   info->description = g_strdup (desc);
449   return TRUE;
450 }
451 
452 static const gchar *
gail_cell_action_get_keybinding(AtkAction * action,gint index)453 gail_cell_action_get_keybinding (AtkAction *action,
454 				 gint      index)
455 {
456   GailCell *cell = GAIL_CELL(action);
457   ActionInfo *info = _gail_cell_get_action_info (cell, index);
458   if (info == NULL)
459     return NULL;
460   return info->keybinding;
461 }
462 
463 static gboolean
gail_cell_action_do_action(AtkAction * action,gint index)464 gail_cell_action_do_action (AtkAction *action,
465 			    gint      index)
466 {
467   GailCell *cell = GAIL_CELL(action);
468   ActionInfo *info = _gail_cell_get_action_info (cell, index);
469   if (info == NULL)
470     return FALSE;
471   if (info->do_action_func == NULL)
472     return FALSE;
473   if (cell->action_idle_handler)
474     return FALSE;
475   cell->action_func = info->do_action_func;
476   cell->action_idle_handler = gdk_threads_add_idle (idle_do_action, cell);
477   return TRUE;
478 }
479 
480 static gboolean
idle_do_action(gpointer data)481 idle_do_action (gpointer data)
482 {
483   GailCell *cell;
484 
485   cell = GAIL_CELL (data);
486   cell->action_idle_handler = 0;
487   cell->action_func (cell);
488 
489   return FALSE;
490 }
491 
492 static void
atk_component_interface_init(AtkComponentIface * iface)493 atk_component_interface_init (AtkComponentIface *iface)
494 {
495   iface->get_extents = gail_cell_get_extents;
496   iface->grab_focus = gail_cell_grab_focus;
497 }
498 
499 static void
gail_cell_get_extents(AtkComponent * component,gint * x,gint * y,gint * width,gint * height,AtkCoordType coord_type)500 gail_cell_get_extents (AtkComponent *component,
501                        gint         *x,
502                        gint         *y,
503                        gint         *width,
504                        gint         *height,
505                        AtkCoordType coord_type)
506 {
507   GailCell *gailcell;
508   AtkObject *cell_parent;
509 
510   g_assert (GAIL_IS_CELL (component));
511 
512   gailcell = GAIL_CELL (component);
513 
514   cell_parent = gtk_widget_get_accessible (gailcell->widget);
515 
516   gail_cell_parent_get_cell_extents (GAIL_CELL_PARENT (cell_parent),
517                                    gailcell, x, y, width, height, coord_type);
518 }
519 
520 static gboolean
gail_cell_grab_focus(AtkComponent * component)521 gail_cell_grab_focus (AtkComponent *component)
522 {
523   GailCell *gailcell;
524   AtkObject *cell_parent;
525 
526   g_assert (GAIL_IS_CELL (component));
527 
528   gailcell = GAIL_CELL (component);
529 
530   cell_parent = gtk_widget_get_accessible (gailcell->widget);
531 
532   return gail_cell_parent_grab_focus (GAIL_CELL_PARENT (cell_parent),
533                                       gailcell);
534 }
535