]> Pileus Git - ~andy/gtk/blob - modules/other/gail/gailcombobox.c
Use gdk_threads_add_idle. Bug #504571.
[~andy/gtk] / modules / other / gail / gailcombobox.c
1 /* GAIL - The GNOME Accessibility Implementation Library
2  * Copyright 2004 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 <gtk/gtk.h>
21 #include <gdk/gdkkeysyms.h>
22 #include "gailcombobox.h"
23
24 #if GTK_MINOR_VERSION > 4
25 #define GAIL_COMBOX_BOX_A11y_COMPLETE
26 #endif
27
28 static void         gail_combo_box_class_init              (GailComboBoxClass *klass);
29 static void         gail_combo_box_object_init             (GailComboBox      *combo_box);
30 static void         gail_combo_box_real_initialize         (AtkObject      *obj,
31                                                             gpointer       data);
32
33 static void         gail_combo_box_changed_gtk             (GtkWidget      *widget);
34
35 static G_CONST_RETURN gchar* gail_combo_box_get_name       (AtkObject      *obj);
36 static gint         gail_combo_box_get_n_children          (AtkObject      *obj);
37 static AtkObject*   gail_combo_box_ref_child               (AtkObject      *obj,
38                                                             gint           i);
39 static void         gail_combo_box_finalize                (GObject        *object);
40 #ifdef GAIL_COMBOX_BOX_A11y_COMPLETE
41 static void         atk_action_interface_init              (AtkActionIface *iface);
42
43 static gboolean     gail_combo_box_do_action               (AtkAction      *action,
44                                                             gint           i);
45 static gboolean     idle_do_action                         (gpointer       data);
46 static gint         gail_combo_box_get_n_actions           (AtkAction      *action)
47 ;
48 static G_CONST_RETURN gchar* gail_combo_box_get_description(AtkAction      *action,
49                                                             gint           i);
50 static G_CONST_RETURN gchar* gail_combo_box_get_keybinding   (AtkAction       *action,
51                                                              gint            i);
52 static G_CONST_RETURN gchar* gail_combo_box_action_get_name(AtkAction      *action,
53                                                             gint           i);
54 static gboolean              gail_combo_box_set_description(AtkAction      *action,
55                                                             gint           i,
56                                                             const gchar    *desc);
57 #endif
58
59 static void         atk_selection_interface_init           (AtkSelectionIface *iface);
60 static gboolean     gail_combo_box_add_selection           (AtkSelection   *selection,
61                                                             gint           i);
62 static gboolean     gail_combo_box_clear_selection         (AtkSelection   *selection);
63 static AtkObject*   gail_combo_box_ref_selection           (AtkSelection   *selection,
64                                                             gint           i);
65 static gint         gail_combo_box_get_selection_count     (AtkSelection   *selection);
66 static gboolean     gail_combo_box_is_child_selected       (AtkSelection   *selection,
67                                                             gint           i);
68 static gboolean     gail_combo_box_remove_selection        (AtkSelection   *selection,
69                                                             gint           i);
70
71 static gpointer parent_class = NULL;
72
73 GType
74 gail_combo_box_get_type (void)
75 {
76   static GType type = 0;
77
78   if (!type)
79     {
80       static const GTypeInfo tinfo =
81       {
82         sizeof (GailComboBoxClass),
83         (GBaseInitFunc) NULL, /* base init */
84         (GBaseFinalizeFunc) NULL, /* base finalize */
85         (GClassInitFunc) gail_combo_box_class_init, /* class init */
86         (GClassFinalizeFunc) NULL, /* class finalize */
87         NULL, /* class data */
88         sizeof (GailComboBox), /* instance size */
89         0, /* nb preallocs */
90         (GInstanceInitFunc) gail_combo_box_object_init, /* instance init */
91         NULL /* value table */
92       };
93
94 #ifdef GAIL_COMBOX_BOX_A11y_COMPLETE
95       static const GInterfaceInfo atk_action_info =
96       {
97         (GInterfaceInitFunc) atk_action_interface_init,
98         (GInterfaceFinalizeFunc) NULL,
99         NULL
100       };
101 #endif
102       static const GInterfaceInfo atk_selection_info =
103       {
104         (GInterfaceInitFunc) atk_selection_interface_init,
105         (GInterfaceFinalizeFunc) NULL,
106         NULL
107       };
108
109       type = g_type_register_static (GAIL_TYPE_CONTAINER,
110                                      "GailComboBox", &tinfo, 0);
111
112 #ifdef GAIL_COMBOX_BOX_A11y_COMPLETE
113       g_type_add_interface_static (type, ATK_TYPE_ACTION,
114                                    &atk_action_info);
115 #endif
116       g_type_add_interface_static (type, ATK_TYPE_SELECTION,
117                                    &atk_selection_info);
118     }
119
120   return type;
121 }
122
123 static void
124 gail_combo_box_class_init (GailComboBoxClass *klass)
125 {
126   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
127   AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
128
129   gobject_class->finalize = gail_combo_box_finalize;
130
131   parent_class = g_type_class_peek_parent (klass);
132
133   class->get_name = gail_combo_box_get_name;
134   class->get_n_children = gail_combo_box_get_n_children;
135   class->ref_child = gail_combo_box_ref_child;
136   class->initialize = gail_combo_box_real_initialize;
137 }
138
139 static void
140 gail_combo_box_object_init (GailComboBox      *combo_box)
141 {
142   combo_box->press_description = NULL;
143   combo_box->press_keybinding = NULL;
144   combo_box->old_selection = -1;
145   combo_box->name = NULL;
146   combo_box->popup_set = FALSE;
147 }
148
149 AtkObject* 
150 gail_combo_box_new (GtkWidget *widget)
151 {
152   GObject *object;
153   AtkObject *accessible;
154
155   g_return_val_if_fail (GTK_IS_COMBO_BOX (widget), NULL);
156
157   object = g_object_new (GAIL_TYPE_COMBO_BOX, NULL);
158
159   accessible = ATK_OBJECT (object);
160   atk_object_initialize (accessible, widget);
161
162   return accessible;
163 }
164
165 static void
166 gail_combo_box_real_initialize (AtkObject *obj,
167                                 gpointer  data)
168 {
169   GtkComboBox *combo_box;
170   GailComboBox *gail_combo_box;
171 #ifdef GAIL_COMBOX_BOX_A11y_COMPLETE
172   AtkObject *popup;
173 #endif
174
175   ATK_OBJECT_CLASS (parent_class)->initialize (obj, data);
176
177   combo_box = GTK_COMBO_BOX (data);
178
179   gail_combo_box = GAIL_COMBO_BOX (obj);
180
181   g_signal_connect (combo_box,
182                     "changed",
183                     G_CALLBACK (gail_combo_box_changed_gtk),
184                     NULL);
185   gail_combo_box->old_selection = gtk_combo_box_get_active (combo_box);
186
187 #ifdef GAIL_COMBOX_BOX_A11y_COMPLETE
188   popup = gtk_combo_box_get_popup_accessible (combo_box);
189   if (popup)
190     {
191       atk_object_set_parent (popup, obj);
192       gail_combo_box->popup_set = TRUE;
193     }
194   if (GTK_IS_COMBO_BOX_ENTRY (combo_box))
195     atk_object_set_parent (gtk_widget_get_accessible (gtk_bin_get_child (GTK_BIN (combo_box))), obj);
196 #endif
197
198   obj->role = ATK_ROLE_COMBO_BOX;
199 }
200
201 static void
202 gail_combo_box_changed_gtk (GtkWidget *widget)
203 {
204   GtkComboBox *combo_box;
205   AtkObject *obj;
206   GailComboBox *gail_combo_box;
207   gint index;
208
209   combo_box = GTK_COMBO_BOX (widget);
210
211   index = gtk_combo_box_get_active (combo_box);
212   obj = gtk_widget_get_accessible (widget);
213   gail_combo_box = GAIL_COMBO_BOX (obj);
214   if (gail_combo_box->old_selection != index)
215     {
216       gail_combo_box->old_selection = index;
217       g_signal_emit_by_name (obj, "selection_changed");
218     }
219 }
220
221 static G_CONST_RETURN gchar* 
222 gail_combo_box_get_name (AtkObject *obj)
223 {
224   GtkWidget *widget;
225   GtkComboBox *combo_box;
226   GailComboBox *gail_combo_box;
227   GtkTreeIter iter;
228   G_CONST_RETURN gchar *name;
229   GtkTreeModel *model;
230   gint n_columns;
231   gint i;
232
233   g_return_val_if_fail (GAIL_IS_COMBO_BOX (obj), NULL);
234
235   name = ATK_OBJECT_CLASS (parent_class)->get_name (obj);
236   if (name)
237     return name;
238
239   widget = GTK_ACCESSIBLE (obj)->widget;
240   if (widget == NULL)
241     /*
242      * State is defunct
243      */
244     return NULL;
245
246   combo_box = GTK_COMBO_BOX (widget);
247   gail_combo_box = GAIL_COMBO_BOX (obj);
248   if (gtk_combo_box_get_active_iter (combo_box, &iter))
249     {
250       model = gtk_combo_box_get_model (combo_box);
251       n_columns = gtk_tree_model_get_n_columns (model);
252       for (i = 0; i < n_columns; i++)
253         {
254           GValue value = { 0, };
255
256           gtk_tree_model_get_value (model, &iter, i, &value);
257           if (G_VALUE_HOLDS_STRING (&value))
258             {
259               if (gail_combo_box->name) g_free (gail_combo_box->name);
260               gail_combo_box->name =  g_strdup ((gchar *) 
261                                                 g_value_get_string (&value));
262               g_value_unset (&value);
263               break;
264             }
265           else
266             g_value_unset (&value);
267         }
268     }
269   return gail_combo_box->name;
270 }
271
272 /*
273  * The children of a GailComboBox are the list of items and the entry field
274  * if it is editable.
275  */
276 static gint
277 gail_combo_box_get_n_children (AtkObject* obj)
278 {
279   gint n_children = 0;
280   GtkWidget *widget;
281
282   g_return_val_if_fail (GAIL_IS_COMBO_BOX (obj), 0);
283
284   widget = GTK_ACCESSIBLE (obj)->widget;
285   if (widget == NULL)
286     /*
287      * State is defunct
288      */
289     return 0;
290
291 #ifdef GAIL_COMBOX_BOX_A11y_COMPLETE
292   n_children++;
293   if (GTK_IS_COMBO_BOX_ENTRY (widget))
294     n_children ++;
295 #endif
296
297   return n_children;
298 }
299
300 static AtkObject*
301 gail_combo_box_ref_child (AtkObject *obj,
302                           gint      i)
303 {
304   GtkWidget *widget;
305 #ifdef GAIL_COMBOX_BOX_A11y_COMPLETE
306   AtkObject *child;
307   GailComboBox *box;
308 #endif
309
310   g_return_val_if_fail (GAIL_IS_COMBO_BOX (obj), NULL);
311
312   widget = GTK_ACCESSIBLE (obj)->widget;
313
314   if (widget == NULL)
315     /*
316      * State is defunct
317      */
318     return NULL;
319
320 #ifdef GAIL_COMBOX_BOX_A11y_COMPLETE
321   if (i == 0)
322     {
323       child = gtk_combo_box_get_popup_accessible (GTK_COMBO_BOX (widget));
324       box = GAIL_COMBO_BOX (obj);
325       if (box->popup_set == FALSE)
326         {
327           atk_object_set_parent (child, obj);
328           box->popup_set = TRUE;
329         }
330     }
331   else if (i == 1 && GTK_IS_COMBO_BOX_ENTRY (widget))
332     {
333       child = gtk_widget_get_accessible (gtk_bin_get_child (GTK_BIN (widget)));
334     }
335   else
336     {
337       return NULL;
338     }
339   return g_object_ref (child);
340 #else
341   return NULL;
342 #endif
343 }
344
345 #ifdef GAIL_COMBOX_BOX_A11y_COMPLETE
346 static void
347 atk_action_interface_init (AtkActionIface *iface)
348 {
349   g_return_if_fail (iface != NULL);
350
351   iface->do_action = gail_combo_box_do_action;
352   iface->get_n_actions = gail_combo_box_get_n_actions;
353   iface->get_description = gail_combo_box_get_description;
354   iface->get_keybinding = gail_combo_box_get_keybinding;
355   iface->get_name = gail_combo_box_action_get_name;
356   iface->set_description = gail_combo_box_set_description;
357 }
358
359 static gboolean
360 gail_combo_box_do_action (AtkAction *action,
361                           gint      i)
362 {
363   GailComboBox *combo_box;
364   GtkWidget *widget;
365
366   widget = GTK_ACCESSIBLE (action)->widget;
367   if (widget == NULL)
368     /*
369      * State is defunct
370      */
371     return FALSE;
372
373   if (!GTK_WIDGET_SENSITIVE (widget) || !GTK_WIDGET_VISIBLE (widget))
374     return FALSE;
375
376   combo_box = GAIL_COMBO_BOX (action);
377   if (i == 0)
378     {
379       if (combo_box->action_idle_handler)
380         return FALSE;
381
382       combo_box->action_idle_handler = gdk_threads_add_idle (idle_do_action, combo_box);
383       return TRUE;
384     }
385   else
386     return FALSE;
387 }
388
389 static gboolean
390 idle_do_action (gpointer data)
391 {
392   GtkComboBox *combo_box;
393   GtkWidget *widget;
394   GailComboBox *gail_combo_box;
395   AtkObject *popup;
396   gboolean do_popup;
397
398   gail_combo_box = GAIL_COMBO_BOX (data);
399   gail_combo_box->action_idle_handler = 0;
400   widget = GTK_ACCESSIBLE (gail_combo_box)->widget;
401   if (widget == NULL || /* State is defunct */
402       !GTK_WIDGET_SENSITIVE (widget) || !GTK_WIDGET_VISIBLE (widget))
403     return FALSE;
404
405   combo_box = GTK_COMBO_BOX (widget);
406
407   popup = gtk_combo_box_get_popup_accessible (combo_box);
408   do_popup = !GTK_WIDGET_MAPPED (GTK_ACCESSIBLE (popup)->widget);
409   if (do_popup)
410       gtk_combo_box_popup (combo_box);
411   else
412       gtk_combo_box_popdown (combo_box);
413
414   return FALSE;
415 }
416
417 static gint
418 gail_combo_box_get_n_actions (AtkAction *action)
419 {
420   /*
421    * The default behavior of a combo_box box is to have one action -
422    */
423   return 1;
424 }
425
426 static G_CONST_RETURN gchar*
427 gail_combo_box_get_description (AtkAction *action,
428                            gint      i)
429 {
430   if (i == 0)
431     {
432       GailComboBox *combo_box;
433
434       combo_box = GAIL_COMBO_BOX (action);
435       return combo_box->press_description;
436     }
437   else
438     return NULL;
439 }
440
441 static G_CONST_RETURN gchar*
442 gail_combo_box_get_keybinding (AtkAction *action,
443                                     gint      i)
444 {
445   GailComboBox *combo_box;
446   gchar *return_value = NULL;
447   combo_box = GAIL_COMBO_BOX (action);
448   switch (i)
449   {
450      case 0:
451       {
452           GtkWidget *widget;                                                        
453           GtkWidget *label;
454           AtkRelationSet *set;
455           AtkRelation *relation;                                                             
456           GPtrArray *target;
457           gpointer target_object;
458           guint key_val;
459
460           combo_box = GAIL_COMBO_BOX (action);
461           widget = GTK_ACCESSIBLE (combo_box)->widget;
462           if (widget == NULL)
463              return NULL;
464           set = atk_object_ref_relation_set (ATK_OBJECT (action));                                                      
465           if (!set)
466              return NULL;
467           label = NULL;
468           relation = atk_relation_set_get_relation_by_type (set, ATK_RELATION_LABELLED_BY);
469           if (relation)
470           {
471              target = atk_relation_get_target (relation);
472              target_object = g_ptr_array_index (target, 0);
473              if (GTK_IS_ACCESSIBLE (target_object))
474              {
475                 label = GTK_ACCESSIBLE (target_object)->widget;
476              }
477           }  
478           g_object_unref (set);
479           if (GTK_IS_LABEL (label))
480           {
481              key_val = gtk_label_get_mnemonic_keyval (GTK_LABEL (label));
482              if (key_val != GDK_VoidSymbol)
483              return_value = gtk_accelerator_name (key_val, GDK_MOD1_MASK);
484           }
485            g_free (combo_box->press_keybinding);
486            combo_box->press_keybinding = return_value;
487            break;                                                                                    }                             
488     default:
489            break;
490   }
491   return return_value;
492 }
493
494
495 static G_CONST_RETURN gchar*
496 gail_combo_box_action_get_name (AtkAction *action,
497                                 gint      i)
498 {
499   if (i == 0)
500     return "press";
501   else
502     return NULL;
503 }
504
505 static gboolean
506 gail_combo_box_set_description (AtkAction   *action,
507                                 gint        i,
508                                 const gchar *desc)
509 {
510   if (i == 0)
511     {
512       GailComboBox *combo_box;
513
514       combo_box = GAIL_COMBO_BOX (action);
515       g_free (combo_box->press_description);
516       combo_box->press_description = g_strdup (desc);
517       return TRUE;
518     }
519   else
520     return FALSE;
521 }
522 #endif
523
524 static void
525 atk_selection_interface_init (AtkSelectionIface *iface)
526 {
527   g_return_if_fail (iface != NULL);
528
529   iface->add_selection = gail_combo_box_add_selection;
530   iface->clear_selection = gail_combo_box_clear_selection;
531   iface->ref_selection = gail_combo_box_ref_selection;
532   iface->get_selection_count = gail_combo_box_get_selection_count;
533   iface->is_child_selected = gail_combo_box_is_child_selected;
534   iface->remove_selection = gail_combo_box_remove_selection;
535   /*
536    * select_all_selection does not make sense for a combo_box
537    * so no implementation is provided.
538    */
539 }
540
541 static gboolean
542 gail_combo_box_add_selection (AtkSelection *selection,
543                               gint         i)
544 {
545   GtkComboBox *combo_box;
546   GtkWidget *widget;
547
548   widget = GTK_ACCESSIBLE (selection)->widget;
549   if (widget == NULL)
550     /*
551      * State is defunct
552      */
553     return FALSE;
554
555   combo_box = GTK_COMBO_BOX (widget);
556
557   gtk_combo_box_set_active (combo_box, i);
558   return TRUE;
559 }
560
561 static gboolean 
562 gail_combo_box_clear_selection (AtkSelection *selection)
563 {
564   GtkComboBox *combo_box;
565   GtkWidget *widget;
566
567   widget = GTK_ACCESSIBLE (selection)->widget;
568   if (widget == NULL)
569     /*
570      * State is defunct
571      */
572     return FALSE;
573
574   combo_box = GTK_COMBO_BOX (widget);
575
576   gtk_combo_box_set_active (combo_box, -1);
577   return TRUE;
578 }
579
580 static AtkObject*
581 gail_combo_box_ref_selection (AtkSelection *selection,
582                               gint         i)
583 {
584   GtkComboBox *combo_box;
585   GtkWidget *widget;
586 #ifdef GAIL_COMBOX_BOX_A11y_COMPLETE
587   AtkObject *obj;
588   gint index;
589 #endif
590
591   widget = GTK_ACCESSIBLE (selection)->widget;
592   if (widget == NULL)
593     /*
594      * State is defunct
595      */
596     return NULL;
597
598   combo_box = GTK_COMBO_BOX (widget);
599
600   /*
601    * A combo_box box can have only one selection.
602    */
603   if (i != 0)
604     return NULL;
605
606 #ifdef GAIL_COMBOX_BOX_A11y_COMPLETE
607   obj = gtk_combo_box_get_popup_accessible (combo_box);
608   index = gtk_combo_box_get_active (combo_box);
609   return atk_object_ref_accessible_child (obj, index);
610 #else
611   return NULL;
612 #endif
613 }
614
615 static gint
616 gail_combo_box_get_selection_count (AtkSelection *selection)
617 {
618   GtkComboBox *combo_box;
619   GtkWidget *widget;
620
621   widget = GTK_ACCESSIBLE (selection)->widget;
622   if (widget == NULL)
623     /*
624      * State is defunct
625      */
626     return 0;
627
628   combo_box = GTK_COMBO_BOX (widget);
629
630   return (gtk_combo_box_get_active (combo_box) == -1) ? 0 : 1;
631 }
632
633 static gboolean
634 gail_combo_box_is_child_selected (AtkSelection *selection,
635                                   gint         i)
636 {
637   GtkComboBox *combo_box;
638   gint j;
639   GtkWidget *widget;
640
641   widget = GTK_ACCESSIBLE (selection)->widget;
642   if (widget == NULL)
643     /*
644      * State is defunct
645      */
646     return FALSE;
647
648   combo_box = GTK_COMBO_BOX (widget);
649
650   j = gtk_combo_box_get_active (combo_box);
651
652   return (j == i);
653 }
654
655 static gboolean
656 gail_combo_box_remove_selection (AtkSelection *selection,
657                                  gint         i)
658 {
659   if (atk_selection_is_child_selected (selection, i))
660     atk_selection_clear_selection (selection);
661
662   return TRUE;
663 }
664
665 static void
666 gail_combo_box_finalize (GObject *object)
667 {
668   GailComboBox *combo_box = GAIL_COMBO_BOX (object);
669
670   g_free (combo_box->press_description);
671   g_free (combo_box->press_keybinding);
672   g_free (combo_box->name);
673   if (combo_box->action_idle_handler)
674     {
675       g_source_remove (combo_box->action_idle_handler);
676       combo_box->action_idle_handler = 0;
677     }
678   G_OBJECT_CLASS (parent_class)->finalize (object);
679 }