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