]> Pileus Git - ~andy/gtk/blob - modules/other/gail/gailrange.c
bf6eaa4522da8c3dfc47ba62c028d4c5752d53a4
[~andy/gtk] / modules / other / gail / gailrange.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 <string.h>
21 #include <gtk/gtk.h>
22 #include <gdk/gdkkeysyms.h>
23 #include "gailrange.h"
24 #include "gailadjustment.h"
25 #include "gail-private-macros.h"
26
27 static void         gail_range_class_init        (GailRangeClass *klass);
28
29 static void         gail_range_real_initialize   (AtkObject      *obj,
30                                                   gpointer      data);
31
32 static void         gail_range_finalize          (GObject        *object);
33
34 static AtkStateSet* gail_range_ref_state_set     (AtkObject      *obj);
35
36
37 static void         gail_range_real_notify_gtk   (GObject        *obj,
38                                                   GParamSpec     *pspec);
39
40 static void         atk_value_interface_init     (AtkValueIface  *iface);
41 static void         gail_range_get_current_value (AtkValue       *obj,
42                                                   GValue         *value);
43 static void         gail_range_get_maximum_value (AtkValue       *obj,
44                                                   GValue         *value);
45 static void         gail_range_get_minimum_value (AtkValue       *obj,
46                                                   GValue         *value);
47 static gboolean     gail_range_set_current_value (AtkValue       *obj,
48                                                   const GValue   *value);
49 static void         gail_range_value_changed     (GtkAdjustment  *adjustment,
50                                                   gpointer       data);
51
52 static void         atk_action_interface_init    (AtkActionIface *iface);
53 static gboolean     gail_range_do_action        (AtkAction       *action,
54                                                 gint            i);
55 static gboolean     idle_do_action              (gpointer        data);
56 static gint         gail_range_get_n_actions    (AtkAction       *action);
57 static G_CONST_RETURN gchar* gail_range_get_description  (AtkAction    *action, 
58                                                          gint          i);
59 static G_CONST_RETURN gchar* gail_range_get_keybinding   (AtkAction     *action,
60                                                          gint            i);
61 static G_CONST_RETURN gchar* gail_range_action_get_name  (AtkAction    *action,
62                                                         gint            i);
63 static gboolean   gail_range_set_description  (AtkAction       *action,
64                                               gint            i,
65                                               const gchar     *desc);
66 static GailWidgetClass *parent_class = NULL;
67
68 GType
69 gail_range_get_type (void)
70 {
71   static GType type = 0;
72
73   if (!type)
74     {
75       static const GTypeInfo tinfo =
76       {
77         sizeof (GailRangeClass),
78         (GBaseInitFunc) NULL, /* base init */
79         (GBaseFinalizeFunc) NULL, /* base finalize */
80         (GClassInitFunc) gail_range_class_init, /* class init */
81         (GClassFinalizeFunc) NULL, /* class finalize */
82         NULL, /* class data */
83         sizeof (GailRange), /* instance size */
84         0, /* nb preallocs */
85         (GInstanceInitFunc) NULL, /* instance init */
86         NULL /* value table */
87       };
88
89       static const GInterfaceInfo atk_action_info =
90       {
91        (GInterfaceInitFunc) atk_action_interface_init,
92        (GInterfaceFinalizeFunc) NULL,
93        NULL
94       };
95
96
97       static const GInterfaceInfo atk_value_info =
98       {
99         (GInterfaceInitFunc) atk_value_interface_init,
100         (GInterfaceFinalizeFunc) NULL,
101         NULL
102       };
103
104       type = g_type_register_static (GAIL_TYPE_WIDGET,
105                                      "GailRange", &tinfo, 0);
106
107       g_type_add_interface_static (type, ATK_TYPE_ACTION,
108                                        &atk_action_info);
109
110       g_type_add_interface_static (type, ATK_TYPE_VALUE,
111                                    &atk_value_info);
112     }
113   return type;
114 }
115
116 static void      
117 gail_range_class_init           (GailRangeClass *klass)
118 {
119   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
120   AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
121   GailWidgetClass *widget_class;
122
123   widget_class = (GailWidgetClass*)klass;
124
125   widget_class->notify_gtk = gail_range_real_notify_gtk;
126
127   class->ref_state_set = gail_range_ref_state_set;
128   class->initialize = gail_range_real_initialize;
129
130   gobject_class->finalize = gail_range_finalize;
131
132   parent_class = g_type_class_peek_parent (klass);
133 }
134
135 AtkObject* 
136 gail_range_new (GtkWidget *widget)
137 {
138   GObject *object;
139   AtkObject *accessible;
140
141   g_return_val_if_fail (GTK_IS_RANGE (widget), NULL);
142
143   object = g_object_new (GAIL_TYPE_RANGE, NULL);
144
145   accessible = ATK_OBJECT (object);
146   atk_object_initialize (accessible, widget);
147
148   return accessible;
149 }
150
151 static void
152 gail_range_real_initialize (AtkObject *obj,
153                             gpointer  data)
154 {
155   GailRange *range = GAIL_RANGE (obj);
156   GtkRange *gtk_range;
157
158   ATK_OBJECT_CLASS (parent_class)->initialize (obj, data);
159
160   gtk_range = GTK_RANGE (data);
161   /*
162    * If a GtkAdjustment already exists for the GtkRange,
163    * create the GailAdjustment
164    */
165   if (gtk_range->adjustment)
166     {
167       range->adjustment = gail_adjustment_new (gtk_range->adjustment);
168       g_signal_connect (gtk_range->adjustment,
169                         "value-changed",
170                         G_CALLBACK (gail_range_value_changed),
171                         range);
172     }
173   else
174     range->adjustment = NULL;
175   range->activate_keybinding=NULL;
176   range->activate_description=NULL;
177   /*
178    * Assumed to GtkScale (either GtkHScale or GtkVScale)
179    */
180   obj->role = ATK_ROLE_SLIDER;
181 }
182
183 static AtkStateSet*
184 gail_range_ref_state_set (AtkObject *obj)
185 {
186   AtkStateSet *state_set;
187   GtkWidget *widget;
188   GtkRange *range;
189
190   state_set = ATK_OBJECT_CLASS (parent_class)->ref_state_set (obj);
191   widget = GTK_ACCESSIBLE (obj)->widget;
192
193   if (widget == NULL)
194     return state_set;
195
196   range = GTK_RANGE (widget);
197
198   /*
199    * We do not generate property change for orientation change as there
200    * is no interface to change the orientation which emits a notification
201    */
202   if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
203     atk_state_set_add_state (state_set, ATK_STATE_HORIZONTAL);
204   else
205     atk_state_set_add_state (state_set, ATK_STATE_VERTICAL);
206
207   return state_set;
208 }
209
210 static void      
211 atk_value_interface_init (AtkValueIface *iface)
212 {
213   g_return_if_fail (iface != NULL);
214
215   iface->get_current_value = gail_range_get_current_value;
216   iface->get_maximum_value = gail_range_get_maximum_value;
217   iface->get_minimum_value = gail_range_get_minimum_value;
218   iface->set_current_value = gail_range_set_current_value;
219
220 }
221
222 static void      
223 gail_range_get_current_value (AtkValue          *obj,
224                               GValue            *value)
225 {
226   GailRange *range;
227
228   g_return_if_fail (GAIL_IS_RANGE (obj));
229
230   range = GAIL_RANGE (obj);
231   if (range->adjustment == NULL)
232     /*
233      * Adjustment has not been specified
234      */
235     return;
236
237   atk_value_get_current_value (ATK_VALUE (range->adjustment), value);
238 }
239
240 static void      
241 gail_range_get_maximum_value (AtkValue          *obj,
242                               GValue            *value)
243 {
244   GailRange *range;
245
246   g_return_if_fail (GAIL_IS_RANGE (obj));
247
248   range = GAIL_RANGE (obj);
249   if (range->adjustment == NULL)
250     /*
251      * Adjustment has not been specified
252      */
253     return;
254
255   atk_value_get_maximum_value (ATK_VALUE (range->adjustment), value);
256 }
257
258 static void      
259 gail_range_get_minimum_value (AtkValue          *obj,
260                               GValue            *value)
261 {
262   GailRange *range;
263
264   g_return_if_fail (GAIL_IS_RANGE (obj));
265
266   range = GAIL_RANGE (obj);
267   if (range->adjustment == NULL)
268     /*
269      * Adjustment has not been specified
270      */
271     return;
272
273   atk_value_get_minimum_value (ATK_VALUE (range->adjustment), value);
274 }
275
276 static gboolean  gail_range_set_current_value (AtkValue         *obj,
277                                                const GValue     *value)
278 {
279   GtkWidget *widget;
280
281   g_return_val_if_fail (GAIL_IS_RANGE (obj), FALSE);
282
283   widget = GTK_ACCESSIBLE (obj)->widget;
284   if (widget == NULL)
285     return FALSE;
286
287   if (G_VALUE_HOLDS_DOUBLE (value))
288     {
289       GtkRange *range = GTK_RANGE (widget);
290       gdouble new_value;
291
292       new_value = g_value_get_double (value);
293       gtk_range_set_value (range, new_value);
294       return TRUE;
295     }
296   else
297     {
298       return FALSE;
299     }
300 }
301
302 static void
303 gail_range_finalize (GObject            *object)
304 {
305   GailRange *range = GAIL_RANGE (object);
306
307   if (range->adjustment)
308     {
309       /*
310        * The GtkAdjustment may live on so we need to dicsonnect the
311        * signal handler
312        */
313       if (GAIL_ADJUSTMENT (range->adjustment)->adjustment)
314         {
315           g_signal_handlers_disconnect_by_func (GAIL_ADJUSTMENT (range->adjustment)->adjustment,
316                                                 (void *)gail_range_value_changed,
317                                                 range);
318         }
319       g_object_unref (range->adjustment);
320       range->adjustment = NULL;
321     }
322   range->activate_keybinding=NULL;
323   range->activate_description=NULL;
324   if (range->action_idle_handler)
325    {
326     g_source_remove (range->action_idle_handler);
327     range->action_idle_handler = 0;
328    }
329
330   G_OBJECT_CLASS (parent_class)->finalize (object);
331 }
332
333
334 static void
335 gail_range_real_notify_gtk (GObject           *obj,
336                             GParamSpec        *pspec)
337 {
338   GtkWidget *widget = GTK_WIDGET (obj);
339   GailRange *range = GAIL_RANGE (gtk_widget_get_accessible (widget));
340
341   if (strcmp (pspec->name, "adjustment") == 0)
342     {
343       /*
344        * Get rid of the GailAdjustment for the GtkAdjustment
345        * which was associated with the range.
346        */
347       if (range->adjustment)
348         {
349           g_object_unref (range->adjustment);
350           range->adjustment = NULL;
351         }
352       /*
353        * Create the GailAdjustment when notify for "adjustment" property
354        * is received
355        */
356       range->adjustment = gail_adjustment_new (GTK_RANGE (widget)->adjustment);
357       g_signal_connect (GTK_RANGE (widget)->adjustment,
358                         "value-changed",
359                         G_CALLBACK (gail_range_value_changed),
360                         range);
361     }
362   else
363     parent_class->notify_gtk (obj, pspec);
364 }
365
366 static void
367 gail_range_value_changed (GtkAdjustment    *adjustment,
368                           gpointer         data)
369 {
370   GailRange *range;
371
372   g_return_if_fail (adjustment != NULL);
373   gail_return_if_fail (data != NULL);
374
375   range = GAIL_RANGE (data);
376
377   g_object_notify (G_OBJECT (range), "accessible-value");
378 }
379
380 static void
381 atk_action_interface_init (AtkActionIface *iface)
382 {
383   g_return_if_fail (iface != NULL);
384  
385   iface->do_action = gail_range_do_action;
386   iface->get_n_actions = gail_range_get_n_actions;
387   iface->get_description = gail_range_get_description;
388   iface->get_keybinding = gail_range_get_keybinding;
389   iface->get_name = gail_range_action_get_name;
390   iface->set_description = gail_range_set_description;
391 }
392
393 static gboolean
394 gail_range_do_action (AtkAction *action,
395                      gint      i)
396 {
397   GailRange *range;
398   GtkWidget *widget;
399   gboolean return_value = TRUE;
400
401   range = GAIL_RANGE (action);
402   widget = GTK_ACCESSIBLE (action)->widget;
403   if (widget == NULL)
404     /*
405      * State is defunct
406      */
407     return FALSE;
408   if (!GTK_WIDGET_SENSITIVE (widget) || !GTK_WIDGET_VISIBLE (widget))
409     return FALSE;
410   if(i==0)
411    {
412     if (range->action_idle_handler)
413       return_value = FALSE;
414     else
415       range->action_idle_handler = gdk_threads_add_idle (idle_do_action, range);
416    }
417   else
418      return_value = FALSE;
419   return return_value;
420 }
421
422 static gboolean
423 idle_do_action (gpointer data)
424 {
425   GailRange *range;
426   GtkWidget *widget;
427
428   range = GAIL_RANGE (data);
429   range->action_idle_handler = 0;
430   widget = GTK_ACCESSIBLE (range)->widget;
431   if (widget == NULL /* State is defunct */ ||
432      !GTK_WIDGET_SENSITIVE (widget) || !GTK_WIDGET_VISIBLE (widget))
433     return FALSE;
434
435    gtk_widget_activate (widget);
436
437    return FALSE;
438 }
439
440 static gint
441 gail_range_get_n_actions (AtkAction *action)
442 {
443     return 1;
444 }
445
446 static G_CONST_RETURN gchar*
447 gail_range_get_description (AtkAction *action,
448                               gint      i)
449 {
450   GailRange *range;
451   G_CONST_RETURN gchar *return_value;
452
453   range = GAIL_RANGE (action);
454   if (i==0)
455    return_value = range->activate_description;
456   else
457    return_value = NULL;
458   return return_value;
459 }
460
461 static G_CONST_RETURN gchar*
462 gail_range_get_keybinding (AtkAction *action,
463                               gint      i)
464 {
465   GailRange *range;
466   gchar *return_value = NULL;
467   range = GAIL_RANGE (action);
468   if(i==0)
469    {
470     GtkWidget *widget;
471     GtkWidget *label;
472     AtkRelationSet *set;
473     AtkRelation *relation;
474     GPtrArray *target;
475     gpointer target_object;
476     guint key_val;
477
478     range = GAIL_RANGE (action);
479     widget = GTK_ACCESSIBLE (range)->widget;
480     if (widget == NULL)
481        return NULL;
482     set = atk_object_ref_relation_set (ATK_OBJECT (action));
483
484     if (!set)
485       return NULL;
486     label = NULL;
487     relation = atk_relation_set_get_relation_by_type (set, ATK_RELATION_LABELLED_BY);    
488     if (relation)
489      {
490       target = atk_relation_get_target (relation);
491       target_object = g_ptr_array_index (target, 0);
492       if (GTK_IS_ACCESSIBLE (target_object))
493          label = GTK_ACCESSIBLE (target_object)->widget;
494      }
495     g_object_unref (set);
496     if (GTK_IS_LABEL (label))
497      {
498       key_val = gtk_label_get_mnemonic_keyval (GTK_LABEL (label));
499       if (key_val != GDK_VoidSymbol)
500          return_value = gtk_accelerator_name (key_val, GDK_MOD1_MASK);
501       }
502     g_free (range->activate_keybinding);
503     range->activate_keybinding = return_value;
504    }
505   return return_value;
506 }
507
508 static G_CONST_RETURN gchar*
509 gail_range_action_get_name (AtkAction *action,
510                            gint      i)
511 {
512   G_CONST_RETURN gchar *return_value;
513   
514   if (i==0)
515    return_value = "activate";
516   else
517    return_value = NULL;
518
519   return return_value;
520 }
521
522 static gboolean
523 gail_range_set_description (AtkAction      *action,
524                            gint           i,
525                            const gchar    *desc)
526 {
527   GailRange *range;
528   gchar **value;
529
530   range = GAIL_RANGE (action);
531   
532   if (i==0)
533    value = &range->activate_description;
534   else
535    value = NULL;
536
537   if (value)
538    {
539     g_free (*value);
540     *value = g_strdup (desc);
541     return TRUE;
542    }
543   else
544    return FALSE;
545 }
546
547