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