]> Pileus Git - ~andy/gtk/blob - gtk/a11y/gtkcomboboxaccessible.c
Merge branch 'bgo687196-filesystemmodel-crash'
[~andy/gtk] / gtk / a11y / gtkcomboboxaccessible.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, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include "config.h"
19
20 #include <gtk/gtk.h>
21 #include "gtkcomboboxaccessible.h"
22
23 struct _GtkComboBoxAccessiblePrivate
24 {
25   gchar         *name;
26   gint           old_selection;
27   gboolean       popup_set;
28 };
29
30 static void atk_action_interface_init    (AtkActionIface    *iface);
31 static void atk_selection_interface_init (AtkSelectionIface *iface);
32
33 G_DEFINE_TYPE_WITH_CODE (GtkComboBoxAccessible, _gtk_combo_box_accessible, GTK_TYPE_CONTAINER_ACCESSIBLE,
34                          G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, atk_action_interface_init)
35                          G_IMPLEMENT_INTERFACE (ATK_TYPE_SELECTION, atk_selection_interface_init))
36
37 static void
38 changed_cb (GtkWidget *widget)
39 {
40   GtkComboBox *combo_box;
41   AtkObject *obj;
42   GtkComboBoxAccessible *accessible;
43   gint index;
44
45   combo_box = GTK_COMBO_BOX (widget);
46
47   index = gtk_combo_box_get_active (combo_box);
48   obj = gtk_widget_get_accessible (widget);
49   accessible = GTK_COMBO_BOX_ACCESSIBLE (obj);
50   if (accessible->priv->old_selection != index)
51     {
52       accessible->priv->old_selection = index;
53       g_object_notify (G_OBJECT (obj), "accessible-name");
54       g_signal_emit_by_name (obj, "selection-changed");
55     }
56 }
57
58 static void
59 gtk_combo_box_accessible_initialize (AtkObject *obj,
60                                      gpointer   data)
61 {
62   GtkComboBox *combo_box;
63   GtkComboBoxAccessible *accessible;
64   AtkObject *popup;
65
66   ATK_OBJECT_CLASS (_gtk_combo_box_accessible_parent_class)->initialize (obj, data);
67
68   combo_box = GTK_COMBO_BOX (data);
69   accessible = GTK_COMBO_BOX_ACCESSIBLE (obj);
70
71   g_signal_connect (combo_box, "changed", G_CALLBACK (changed_cb), NULL);
72   accessible->priv->old_selection = gtk_combo_box_get_active (combo_box);
73
74   popup = gtk_combo_box_get_popup_accessible (combo_box);
75   if (popup)
76     {
77       atk_object_set_parent (popup, obj);
78       accessible->priv->popup_set = TRUE;
79     }
80   if (gtk_combo_box_get_has_entry (combo_box))
81     atk_object_set_parent (gtk_widget_get_accessible (gtk_bin_get_child (GTK_BIN (combo_box))), obj);
82
83   obj->role = ATK_ROLE_COMBO_BOX;
84 }
85
86 static void
87 gtk_combo_box_accessible_finalize (GObject *object)
88 {
89   GtkComboBoxAccessible *combo_box = GTK_COMBO_BOX_ACCESSIBLE (object);
90
91   g_free (combo_box->priv->name);
92
93   G_OBJECT_CLASS (_gtk_combo_box_accessible_parent_class)->finalize (object);
94 }
95
96 static const gchar *
97 gtk_combo_box_accessible_get_name (AtkObject *obj)
98 {
99   GtkWidget *widget;
100   GtkComboBox *combo_box;
101   GtkComboBoxAccessible *accessible;
102   GtkTreeIter iter;
103   const gchar *name;
104   GtkTreeModel *model;
105   gint n_columns;
106   gint i;
107
108   name = ATK_OBJECT_CLASS (_gtk_combo_box_accessible_parent_class)->get_name (obj);
109   if (name)
110     return name;
111
112   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
113   if (widget == NULL)
114     return NULL;
115
116   combo_box = GTK_COMBO_BOX (widget);
117   accessible = GTK_COMBO_BOX_ACCESSIBLE (obj);
118   if (gtk_combo_box_get_active_iter (combo_box, &iter))
119     {
120       model = gtk_combo_box_get_model (combo_box);
121       n_columns = gtk_tree_model_get_n_columns (model);
122       for (i = 0; i < n_columns; i++)
123         {
124           GValue value = G_VALUE_INIT;
125
126           gtk_tree_model_get_value (model, &iter, i, &value);
127           if (G_VALUE_HOLDS_STRING (&value))
128             {
129               g_free (accessible->priv->name);
130               accessible->priv->name =  g_strdup (g_value_get_string (&value));
131               g_value_unset (&value);
132               break;
133             }
134           else
135             g_value_unset (&value);
136         }
137     }
138   return accessible->priv->name;
139 }
140
141 static gint
142 gtk_combo_box_accessible_get_n_children (AtkObject* obj)
143 {
144   gint n_children = 0;
145   GtkWidget *widget;
146
147   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
148   if (widget == NULL)
149     return 0;
150
151   n_children++;
152   if (gtk_combo_box_get_has_entry (GTK_COMBO_BOX (widget)))
153     n_children++;
154
155   return n_children;
156 }
157
158 static AtkObject *
159 gtk_combo_box_accessible_ref_child (AtkObject *obj,
160                                     gint       i)
161 {
162   GtkWidget *widget;
163   AtkObject *child;
164   GtkComboBoxAccessible *box;
165
166   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
167   if (widget == NULL)
168     return NULL;
169
170   if (i == 0)
171     {
172       child = gtk_combo_box_get_popup_accessible (GTK_COMBO_BOX (widget));
173       box = GTK_COMBO_BOX_ACCESSIBLE (obj);
174       if (!box->priv->popup_set)
175         {
176           atk_object_set_parent (child, obj);
177           box->priv->popup_set = TRUE;
178         }
179     }
180   else if (i == 1 && gtk_combo_box_get_has_entry (GTK_COMBO_BOX (widget)))
181     {
182       child = gtk_widget_get_accessible (gtk_bin_get_child (GTK_BIN (widget)));
183     }
184   else
185     {
186       return NULL;
187     }
188
189   return g_object_ref (child);
190 }
191
192 static void
193 _gtk_combo_box_accessible_class_init (GtkComboBoxAccessibleClass *klass)
194 {
195   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
196   AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
197
198   gobject_class->finalize = gtk_combo_box_accessible_finalize;
199
200   class->get_name = gtk_combo_box_accessible_get_name;
201   class->get_n_children = gtk_combo_box_accessible_get_n_children;
202   class->ref_child = gtk_combo_box_accessible_ref_child;
203   class->initialize = gtk_combo_box_accessible_initialize;
204
205   g_type_class_add_private (klass, sizeof (GtkComboBoxAccessiblePrivate));
206 }
207
208 static void
209 _gtk_combo_box_accessible_init (GtkComboBoxAccessible *combo_box)
210 {
211   combo_box->priv = G_TYPE_INSTANCE_GET_PRIVATE (combo_box,
212                                                  GTK_TYPE_COMBO_BOX_ACCESSIBLE,
213                                                  GtkComboBoxAccessiblePrivate);
214   combo_box->priv->old_selection = -1;
215   combo_box->priv->name = NULL;
216   combo_box->priv->popup_set = FALSE;
217 }
218
219 static gboolean
220 gtk_combo_box_accessible_do_action (AtkAction *action,
221                                     gint       i)
222 {
223   GtkComboBox *combo_box;
224   GtkWidget *widget;
225   gboolean popup_shown;
226
227   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (action));
228   if (widget == NULL)
229     return FALSE;
230
231   if (!gtk_widget_get_sensitive (widget) || !gtk_widget_get_visible (widget))
232     return FALSE;
233
234   if (i != 0)
235     return FALSE;
236
237   combo_box = GTK_COMBO_BOX (widget);
238   g_object_get (combo_box, "popup-shown", &popup_shown, NULL);
239   if (popup_shown)
240     gtk_combo_box_popdown (combo_box);
241   else
242     gtk_combo_box_popup (combo_box);
243
244   return TRUE;
245 }
246
247 static gint
248 gtk_combo_box_accessible_get_n_actions (AtkAction *action)
249 {
250   return 1;
251 }
252
253 static const gchar *
254 gtk_combo_box_accessible_get_keybinding (AtkAction *action,
255                                          gint       i)
256 {
257   GtkComboBoxAccessible *combo_box;
258   GtkWidget *widget;
259   GtkWidget *label;
260   AtkRelationSet *set;
261   AtkRelation *relation;
262   GPtrArray *target;
263   gpointer target_object;
264   guint key_val;
265   gchar *return_value = NULL;
266
267   if (i != 0)
268     return NULL;
269
270   combo_box = GTK_COMBO_BOX_ACCESSIBLE (action);
271   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (combo_box));
272   if (widget == NULL)
273     return NULL;
274
275   set = atk_object_ref_relation_set (ATK_OBJECT (action));
276   if (set == NULL)
277     return NULL;
278
279   label = NULL;
280   relation = atk_relation_set_get_relation_by_type (set, ATK_RELATION_LABELLED_BY);
281   if (relation)
282     {
283       target = atk_relation_get_target (relation);
284       target_object = g_ptr_array_index (target, 0);
285       label = gtk_accessible_get_widget (GTK_ACCESSIBLE (target_object));
286     }
287   g_object_unref (set);
288   if (GTK_IS_LABEL (label))
289     {
290       key_val = gtk_label_get_mnemonic_keyval (GTK_LABEL (label));
291       if (key_val != GDK_KEY_VoidSymbol)
292         return_value = gtk_accelerator_name (key_val, GDK_MOD1_MASK);
293     }
294
295   return return_value;
296 }
297
298 static const gchar *
299 gtk_combo_box_accessible_action_get_name (AtkAction *action,
300                                           gint       i)
301 {
302   if (i != 0)
303     return NULL;
304
305   return "press";
306 }
307
308 static void
309 atk_action_interface_init (AtkActionIface *iface)
310 {
311   iface->do_action = gtk_combo_box_accessible_do_action;
312   iface->get_n_actions = gtk_combo_box_accessible_get_n_actions;
313   iface->get_keybinding = gtk_combo_box_accessible_get_keybinding;
314   iface->get_name = gtk_combo_box_accessible_action_get_name;
315 }
316
317 static gboolean
318 gtk_combo_box_accessible_add_selection (AtkSelection *selection,
319                                         gint          i)
320 {
321   GtkWidget *widget;
322
323   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
324   if (widget == NULL)
325     return FALSE;
326
327   gtk_combo_box_set_active (GTK_COMBO_BOX (widget), i);
328
329   return TRUE;
330 }
331
332 static gboolean
333 gtk_combo_box_accessible_clear_selection (AtkSelection *selection)
334 {
335   GtkWidget *widget;
336
337   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
338   if (widget == NULL)
339     return FALSE;
340
341   gtk_combo_box_set_active (GTK_COMBO_BOX (widget), -1);
342
343   return TRUE;
344 }
345
346 static AtkObject *
347 gtk_combo_box_accessible_ref_selection (AtkSelection *selection,
348                                         gint          i)
349 {
350   GtkComboBox *combo_box;
351   GtkWidget *widget;
352   AtkObject *obj;
353   gint index;
354
355   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
356   if (widget == NULL)
357     return NULL;
358
359   if (i != 0)
360     return NULL;
361
362   combo_box = GTK_COMBO_BOX (widget);
363
364   obj = gtk_combo_box_get_popup_accessible (combo_box);
365   index = gtk_combo_box_get_active (combo_box);
366
367   return atk_object_ref_accessible_child (obj, index);
368 }
369
370 static gint
371 gtk_combo_box_accessible_get_selection_count (AtkSelection *selection)
372 {
373   GtkWidget *widget;
374
375   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
376   if (widget == NULL)
377     return 0;
378
379   return (gtk_combo_box_get_active (GTK_COMBO_BOX (widget)) == -1) ? 0 : 1;
380 }
381
382 static gboolean
383 gtk_combo_box_accessible_is_child_selected (AtkSelection *selection,
384                                             gint          i)
385 {
386   GtkWidget *widget;
387   gint j;
388
389   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
390
391   if (widget == NULL)
392     return FALSE;
393
394   j = gtk_combo_box_get_active (GTK_COMBO_BOX (widget));
395
396   return (j == i);
397 }
398
399 static gboolean
400 gtk_combo_box_accessible_remove_selection (AtkSelection *selection,
401                                            gint          i)
402 {
403   if (atk_selection_is_child_selected (selection, i))
404     atk_selection_clear_selection (selection);
405
406   return TRUE;
407 }
408
409 static void
410 atk_selection_interface_init (AtkSelectionIface *iface)
411 {
412   iface->add_selection = gtk_combo_box_accessible_add_selection;
413   iface->clear_selection = gtk_combo_box_accessible_clear_selection;
414   iface->ref_selection = gtk_combo_box_accessible_ref_selection;
415   iface->get_selection_count = gtk_combo_box_accessible_get_selection_count;
416   iface->is_child_selected = gtk_combo_box_accessible_is_child_selected;
417   iface->remove_selection = gtk_combo_box_accessible_remove_selection;
418 }