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