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