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