]> Pileus Git - ~andy/gtk/blob - modules/other/gail/gailcombo.c
1f10ecbec81dd400a776ff05044b827dfaa97c04
[~andy/gtk] / modules / other / gail / gailcombo.c
1 /* GAIL - The GNOME Accessibility Implementation Library
2  * Copyright 2001, 2002, 2003 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 #undef GTK_DISABLE_DEPRECATED
23
24 #include <gtk/gtk.h>
25 #include "gailcombo.h"
26
27 static void         gail_combo_class_init              (GailComboClass *klass);
28 static void         gail_combo_init                    (GailCombo      *combo);
29 static void         gail_combo_real_initialize         (AtkObject      *obj,
30                                                         gpointer       data);
31
32 static void         gail_combo_selection_changed_gtk   (GtkWidget      *widget,
33                                                         gpointer       data);
34
35 static gint         gail_combo_get_n_children          (AtkObject      *obj);
36 static AtkObject*   gail_combo_ref_child               (AtkObject      *obj,
37                                                         gint           i);
38 static void         gail_combo_finalize                (GObject        *object);
39 static void         atk_action_interface_init          (AtkActionIface *iface);
40
41 static gboolean     gail_combo_do_action               (AtkAction      *action,
42                                                         gint           i);
43 static gboolean     idle_do_action                     (gpointer       data);
44 static gint         gail_combo_get_n_actions           (AtkAction      *action)
45 ;
46 static G_CONST_RETURN gchar* gail_combo_get_description(AtkAction      *action,
47                                                         gint           i);
48 static G_CONST_RETURN gchar* gail_combo_get_name       (AtkAction      *action,
49                                                         gint           i);
50 static gboolean              gail_combo_set_description(AtkAction      *action,
51                                                         gint           i,
52                                                         const gchar    *desc);
53
54 static void         atk_selection_interface_init       (AtkSelectionIface *iface);
55 static gboolean     gail_combo_add_selection           (AtkSelection   *selection,
56                                                         gint           i);
57 static gboolean     gail_combo_clear_selection         (AtkSelection   *selection);
58 static AtkObject*   gail_combo_ref_selection           (AtkSelection   *selection,
59                                                         gint           i);
60 static gint         gail_combo_get_selection_count     (AtkSelection   *selection);
61 static gboolean     gail_combo_is_child_selected       (AtkSelection   *selection,
62                                                         gint           i);
63 static gboolean     gail_combo_remove_selection        (AtkSelection   *selection,
64                                                         gint           i);
65
66 static gint         _gail_combo_button_release         (gpointer       data);
67 static gint         _gail_combo_popup_release          (gpointer       data);
68
69
70 G_DEFINE_TYPE_WITH_CODE (GailCombo, gail_combo, GAIL_TYPE_CONTAINER,
71                          G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, atk_action_interface_init)
72                          G_IMPLEMENT_INTERFACE (ATK_TYPE_SELECTION, atk_selection_interface_init))
73
74 static void
75 gail_combo_class_init (GailComboClass *klass)
76 {
77   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
78   AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
79
80   gobject_class->finalize = gail_combo_finalize;
81
82   class->get_n_children = gail_combo_get_n_children;
83   class->ref_child = gail_combo_ref_child;
84   class->initialize = gail_combo_real_initialize;
85 }
86
87 static void
88 gail_combo_init (GailCombo      *combo)
89 {
90   combo->press_description = NULL;
91   combo->old_selection = NULL;
92   combo->deselect_idle_handler = 0;
93   combo->select_idle_handler = 0;
94 }
95
96 static void
97 gail_combo_real_initialize (AtkObject *obj,
98                             gpointer  data)
99 {
100   GtkCombo *combo;
101   GtkList *list;
102   GList *slist; 
103   GailCombo *gail_combo;
104
105   ATK_OBJECT_CLASS (gail_combo_parent_class)->initialize (obj, data);
106
107   combo = GTK_COMBO (data);
108
109   list = GTK_LIST (combo->list);
110   slist = list->selection;
111
112   gail_combo = GAIL_COMBO (obj);
113   if (slist && slist->data)
114     {
115       gail_combo->old_selection = slist->data;
116     }
117   g_signal_connect (combo->list,
118                     "selection_changed",
119                     G_CALLBACK (gail_combo_selection_changed_gtk),
120                     data);
121   atk_object_set_parent (gtk_widget_get_accessible (combo->entry), obj);
122   atk_object_set_parent (gtk_widget_get_accessible (combo->popup), obj);
123
124   obj->role = ATK_ROLE_COMBO_BOX;
125 }
126
127 static gboolean
128 notify_deselect (gpointer data)
129 {
130   GailCombo *combo;
131
132   combo = GAIL_COMBO (data);
133
134   combo->old_selection = NULL;
135   combo->deselect_idle_handler = 0;
136   g_signal_emit_by_name (data, "selection_changed");
137
138   return FALSE;
139 }
140
141 static gboolean
142 notify_select (gpointer data)
143 {
144   GailCombo *combo;
145
146   combo = GAIL_COMBO (data);
147
148   combo->select_idle_handler = 0;
149   g_signal_emit_by_name (data, "selection_changed");
150
151   return FALSE;
152 }
153
154 static void
155 gail_combo_selection_changed_gtk (GtkWidget      *widget,
156                                   gpointer       data)
157 {
158   GtkCombo *combo;
159   GtkList *list;
160   GList *slist; 
161   AtkObject *obj;
162   GailCombo *gail_combo;
163
164   combo = GTK_COMBO (data);
165   list = GTK_LIST (combo->list);
166   
167   slist = list->selection;
168
169   obj = gtk_widget_get_accessible (GTK_WIDGET (data));
170   gail_combo = GAIL_COMBO (obj);
171   if (slist && slist->data)
172     {
173       if (slist->data != gail_combo->old_selection)
174         {
175           gail_combo->old_selection = slist->data;
176           if (gail_combo->select_idle_handler == 0)
177             gail_combo->select_idle_handler = gdk_threads_add_idle (notify_select, gail_combo);
178         }
179       if (gail_combo->deselect_idle_handler)
180         {
181           g_source_remove (gail_combo->deselect_idle_handler);
182           gail_combo->deselect_idle_handler = 0;       
183         }
184     }
185   else
186     {
187       if (gail_combo->deselect_idle_handler == 0)
188         gail_combo->deselect_idle_handler = gdk_threads_add_idle (notify_deselect, gail_combo);
189       if (gail_combo->select_idle_handler)
190         {
191           g_source_remove (gail_combo->select_idle_handler);
192           gail_combo->select_idle_handler = 0;       
193         }
194     }
195 }
196
197 /*
198  * The children of a GailCombo are the list of items and the entry field
199  */
200 static gint
201 gail_combo_get_n_children (AtkObject* obj)
202 {
203   gint n_children = 2;
204   GtkWidget *widget;
205
206   g_return_val_if_fail (GAIL_IS_COMBO (obj), 0);
207
208   widget = GTK_ACCESSIBLE (obj)->widget;
209   if (widget == NULL)
210     /*
211      * State is defunct
212      */
213     return 0;
214
215   return n_children;
216 }
217
218 static AtkObject*
219 gail_combo_ref_child (AtkObject *obj,
220                       gint      i)
221 {
222   AtkObject *accessible;
223   GtkWidget *widget;
224
225   g_return_val_if_fail (GAIL_IS_COMBO (obj), NULL);
226
227   if (i < 0 || i > 1)
228     return NULL;
229
230   widget = GTK_ACCESSIBLE (obj)->widget;
231   if (widget == NULL)
232     /*
233      * State is defunct
234      */
235     return NULL;
236
237   if (i == 0)
238     accessible = gtk_widget_get_accessible (GTK_COMBO (widget)->popup);
239   else 
240     accessible = gtk_widget_get_accessible (GTK_COMBO (widget)->entry);
241
242   g_object_ref (accessible);
243   return accessible;
244 }
245
246 static void
247 atk_action_interface_init (AtkActionIface *iface)
248 {
249   iface->do_action = gail_combo_do_action;
250   iface->get_n_actions = gail_combo_get_n_actions;
251   iface->get_description = gail_combo_get_description;
252   iface->get_name = gail_combo_get_name;
253   iface->set_description = gail_combo_set_description;
254 }
255
256 static gboolean
257 gail_combo_do_action (AtkAction *action,
258                        gint      i)
259 {
260   GailCombo *combo;
261   GtkWidget *widget;
262
263   widget = GTK_ACCESSIBLE (action)->widget;
264   if (widget == NULL)
265     /*
266      * State is defunct
267      */
268     return FALSE;
269
270   if (!gtk_widget_get_sensitive (widget) || !gtk_widget_get_visible (widget))
271     return FALSE;
272
273   combo = GAIL_COMBO (action);
274   if (i == 0)
275     {
276       if (combo->action_idle_handler)
277         return FALSE;
278
279       combo->action_idle_handler = gdk_threads_add_idle (idle_do_action, combo);
280       return TRUE;
281     }
282   else
283     return FALSE;
284 }
285
286 /*
287  * This action is the pressing of the button on the combo box.
288  * The behavior is different depending on whether the list is being
289  * displayed or removed.
290  *
291  * A button press event is simulated on the appropriate widget and 
292  * a button release event is simulated in an idle function.
293  */
294 static gboolean
295 idle_do_action (gpointer data)
296 {
297   GtkCombo *combo;
298   GtkWidget *action_widget;
299   GtkWidget *widget;
300   GailCombo *gail_combo;
301   gboolean do_popup;
302   GdkEvent tmp_event;
303
304   gail_combo = GAIL_COMBO (data);
305   gail_combo->action_idle_handler = 0;
306   widget = GTK_ACCESSIBLE (gail_combo)->widget;
307   if (widget == NULL /* State is defunct */ ||
308       !gtk_widget_get_sensitive (widget) || !gtk_widget_get_visible (widget))
309     return FALSE;
310
311   combo = GTK_COMBO (widget);
312
313   do_popup = !GTK_WIDGET_MAPPED (combo->popwin);
314
315   tmp_event.button.type = GDK_BUTTON_PRESS; 
316   tmp_event.button.window = widget->window;
317   tmp_event.button.button = 1; 
318   tmp_event.button.send_event = TRUE;
319   tmp_event.button.time = GDK_CURRENT_TIME;
320   tmp_event.button.axes = NULL;
321
322   if (do_popup)
323     {
324       /* Pop up list */
325       action_widget = combo->button;
326
327       gtk_widget_event (action_widget, &tmp_event);
328
329       /* FIXME !*/
330       g_idle_add (_gail_combo_button_release, combo);
331     }
332     else
333     {
334       /* Pop down list */
335       tmp_event.button.window = combo->list->window;
336       gdk_window_set_user_data (combo->list->window, combo->button);
337       action_widget = combo->popwin;
338     
339       gtk_widget_event (action_widget, &tmp_event);
340       /* FIXME !*/
341       g_idle_add (_gail_combo_popup_release, combo);
342     }
343
344   return FALSE;
345 }
346
347 static gint
348 gail_combo_get_n_actions (AtkAction *action)
349 {
350   /*
351    * The default behavior of a combo box is to have one action -
352    */
353   return 1;
354 }
355
356 static G_CONST_RETURN gchar*
357 gail_combo_get_description (AtkAction *action,
358                            gint      i)
359 {
360   if (i == 0)
361     {
362       GailCombo *combo;
363
364       combo = GAIL_COMBO (action);
365       return combo->press_description;
366     }
367   else
368     return NULL;
369 }
370
371 static G_CONST_RETURN gchar*
372 gail_combo_get_name (AtkAction *action,
373                      gint      i)
374 {
375   if (i == 0)
376     return "press";
377   else
378     return NULL;
379 }
380
381 static void
382 atk_selection_interface_init (AtkSelectionIface *iface)
383 {
384   iface->add_selection = gail_combo_add_selection;
385   iface->clear_selection = gail_combo_clear_selection;
386   iface->ref_selection = gail_combo_ref_selection;
387   iface->get_selection_count = gail_combo_get_selection_count;
388   iface->is_child_selected = gail_combo_is_child_selected;
389   iface->remove_selection = gail_combo_remove_selection;
390   /*
391    * select_all_selection does not make sense for a combo box
392    * so no implementation is provided.
393    */
394 }
395
396 static gboolean
397 gail_combo_add_selection (AtkSelection   *selection,
398                           gint           i)
399 {
400   GtkCombo *combo;
401   GtkWidget *widget;
402
403   widget = GTK_ACCESSIBLE (selection)->widget;
404   if (widget == NULL)
405     /*
406      * State is defunct
407      */
408     return FALSE;
409
410   combo = GTK_COMBO (widget);
411
412   gtk_list_select_item (GTK_LIST (combo->list), i);
413   return TRUE;
414 }
415
416 static gboolean 
417 gail_combo_clear_selection (AtkSelection   *selection)
418 {
419   GtkCombo *combo;
420   GtkWidget *widget;
421
422   widget = GTK_ACCESSIBLE (selection)->widget;
423   if (widget == NULL)
424     /*
425      * State is defunct
426      */
427     return FALSE;
428
429   combo = GTK_COMBO (widget);
430
431   gtk_list_unselect_all (GTK_LIST (combo->list));
432   return TRUE;
433 }
434
435 static AtkObject*
436 gail_combo_ref_selection (AtkSelection   *selection,
437                           gint           i)
438 {
439   GtkCombo *combo;
440   GList * list;
441   GtkWidget *item;
442   AtkObject *obj;
443   GtkWidget *widget;
444
445   widget = GTK_ACCESSIBLE (selection)->widget;
446   if (widget == NULL)
447     /*
448      * State is defunct
449      */
450     return NULL;
451
452   combo = GTK_COMBO (widget);
453
454   /*
455    * A combo box can have only one selection.
456    */
457   if (i != 0)
458     return NULL;
459
460   list = GTK_LIST (combo->list)->selection;
461
462   if (list == NULL)
463     return NULL;
464
465   item = GTK_WIDGET (list->data);
466
467   obj = gtk_widget_get_accessible (item);
468   g_object_ref (obj);
469   return obj;
470 }
471
472 static gint
473 gail_combo_get_selection_count (AtkSelection   *selection)
474 {
475   GtkCombo *combo;
476   GList * list;
477   GtkWidget *widget;
478
479   widget = GTK_ACCESSIBLE (selection)->widget;
480   if (widget == NULL)
481     /*
482      * State is defunct
483      */
484     return 0;
485
486   combo = GTK_COMBO (widget);
487
488   /*
489    * The number of children currently selected is either 1 or 0 so we
490    * do not bother to count the elements of the selected list.
491    */
492   list = GTK_LIST (combo->list)->selection;
493
494   if (list == NULL)
495     return 0;
496   else
497     return 1;
498 }
499
500 static gboolean
501 gail_combo_is_child_selected (AtkSelection   *selection,
502                               gint           i)
503 {
504   GtkCombo *combo;
505   GList * list;
506   GtkWidget *item;
507   gint j;
508   GtkWidget *widget;
509
510   widget = GTK_ACCESSIBLE (selection)->widget;
511   if (widget == NULL)
512     /*
513      * State is defunct
514      */
515     return FALSE;
516
517   combo = GTK_COMBO (widget);
518
519   list = GTK_LIST (combo->list)->selection;
520
521   if (list == NULL)
522     return FALSE;
523
524   item = GTK_WIDGET (list->data);
525
526   j = g_list_index (GTK_LIST (combo->list)->children, item);
527
528   return (j == i);
529 }
530
531 static gboolean
532 gail_combo_remove_selection (AtkSelection   *selection,
533                              gint           i)
534 {
535   if (atk_selection_is_child_selected (selection, i))
536     atk_selection_clear_selection (selection);
537
538   return TRUE;
539 }
540
541 static gint 
542 _gail_combo_popup_release (gpointer data)
543 {
544   GtkCombo *combo;
545   GtkWidget *action_widget;
546   GdkEvent tmp_event;
547
548   GDK_THREADS_ENTER ();
549
550   combo = GTK_COMBO (data);
551   if (combo->current_button == 0)
552     {
553       GDK_THREADS_LEAVE ();
554       return FALSE;
555     }
556
557   tmp_event.button.type = GDK_BUTTON_RELEASE; 
558   tmp_event.button.button = 1; 
559   tmp_event.button.time = GDK_CURRENT_TIME;
560   action_widget = combo->button;
561
562   gtk_widget_event (action_widget, &tmp_event);
563
564   GDK_THREADS_LEAVE ();
565
566   return FALSE;
567 }
568
569 static gint 
570 _gail_combo_button_release (gpointer data)
571 {
572   GtkCombo *combo;
573   GtkWidget *action_widget;
574   GdkEvent tmp_event;
575
576   GDK_THREADS_ENTER ();
577
578   combo = GTK_COMBO (data);
579   if (combo->current_button == 0)
580     {
581       GDK_THREADS_LEAVE ();
582       return FALSE;
583     }
584
585   tmp_event.button.type = GDK_BUTTON_RELEASE; 
586   tmp_event.button.button = 1; 
587   tmp_event.button.window = combo->list->window;
588   tmp_event.button.time = GDK_CURRENT_TIME;
589   gdk_window_set_user_data (combo->list->window, combo->button);
590   action_widget = combo->list;
591
592   gtk_widget_event (action_widget, &tmp_event);
593
594   GDK_THREADS_LEAVE ();
595
596   return FALSE;
597 }
598
599 static gboolean
600 gail_combo_set_description (AtkAction      *action,
601                             gint           i,
602                             const gchar    *desc)
603 {
604   if (i == 0)
605     {
606       GailCombo *combo;
607
608       combo = GAIL_COMBO (action);
609       g_free (combo->press_description);
610       combo->press_description = g_strdup (desc);
611       return TRUE;
612     }
613   else
614     return FALSE;
615 }
616
617 static void
618 gail_combo_finalize (GObject            *object)
619 {
620   GailCombo *combo = GAIL_COMBO (object);
621
622   g_free (combo->press_description);
623   if (combo->action_idle_handler)
624     {
625       g_source_remove (combo->action_idle_handler);
626       combo->action_idle_handler = 0;
627     }
628   if (combo->deselect_idle_handler)
629     {
630       g_source_remove (combo->deselect_idle_handler);
631       combo->deselect_idle_handler = 0;       
632     }
633   if (combo->select_idle_handler)
634     {
635       g_source_remove (combo->select_idle_handler);
636       combo->select_idle_handler = 0;       
637     }
638   G_OBJECT_CLASS (gail_combo_parent_class)->finalize (object);
639 }