]> Pileus Git - ~andy/gtk/blob - gtk/gtkspinner.c
306eeb8f9eacb93bd0b16fcb840ec65b654d0c4b
[~andy/gtk] / gtk / gtkspinner.c
1 /* GTK - The GIMP Toolkit
2  *
3  * Copyright (C) 2007 John Stowers, Neil Jagdish Patel.
4  * Copyright (C) 2009 Bastien Nocera, David Zeuthen
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA  02111-1307, USA.
20  *
21  * Code adapted from egg-spinner
22  * by Christian Hergert <christian.hergert@gmail.com>
23  */
24
25 /*
26  * Modified by the GTK+ Team and others 2007.  See the AUTHORS
27  * file for a list of people on the GTK+ Team.  See the ChangeLog
28  * files for a list of changes.  These files are distributed with
29  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
30  */
31
32 #include "config.h"
33
34 #include "gtkintl.h"
35 #include "gtkaccessible.h"
36 #include "gtkimage.h"
37 #include "gtkspinner.h"
38
39
40 /**
41  * SECTION:gtkspinner
42  * @Short_description: Show a spinner animation
43  * @Title: GtkSpinner
44  * @See_also: #GtkCellRendererSpinner, #GtkProgressBar
45  *
46  * A GtkSpinner widget displays an icon-size spinning animation.
47  * It is often used as an alternative to a #GtkProgressBar for
48  * displaying indefinite activity, instead of actual progress.
49  *
50  * To start the animation, use gtk_spinner_start(), to stop it
51  * use gtk_spinner_stop().
52  */
53
54
55 #define SPINNER_SIZE 12
56
57 enum {
58   PROP_0,
59   PROP_ACTIVE
60 };
61
62 struct _GtkSpinnerPrivate
63 {
64   gboolean active;
65 };
66
67 static gboolean gtk_spinner_draw       (GtkWidget       *widget,
68                                         cairo_t         *cr);
69 static void gtk_spinner_get_property   (GObject         *object,
70                                         guint            param_id,
71                                         GValue          *value,
72                                         GParamSpec      *pspec);
73 static void gtk_spinner_set_property   (GObject         *object,
74                                         guint            param_id,
75                                         const GValue    *value,
76                                         GParamSpec      *pspec);
77 static void gtk_spinner_set_active     (GtkSpinner      *spinner,
78                                         gboolean         active);
79 static void gtk_spinner_get_preferred_width (GtkWidget  *widget,
80                                         gint            *minimum_size,
81                                         gint            *natural_size);
82 static void gtk_spinner_get_preferred_height (GtkWidget *widget,
83                                         gint            *minimum_size,
84                                         gint            *natural_size);
85
86 static AtkObject *gtk_spinner_get_accessible      (GtkWidget *widget);
87 static GType      gtk_spinner_accessible_get_type (void);
88
89 G_DEFINE_TYPE (GtkSpinner, gtk_spinner, GTK_TYPE_WIDGET)
90
91 static void
92 gtk_spinner_class_init (GtkSpinnerClass *klass)
93 {
94   GObjectClass *gobject_class;
95   GtkWidgetClass *widget_class;
96
97   gobject_class = G_OBJECT_CLASS(klass);
98   g_type_class_add_private (gobject_class, sizeof (GtkSpinnerPrivate));
99   gobject_class->get_property = gtk_spinner_get_property;
100   gobject_class->set_property = gtk_spinner_set_property;
101
102   widget_class = GTK_WIDGET_CLASS(klass);
103   widget_class->draw = gtk_spinner_draw;
104   widget_class->get_accessible = gtk_spinner_get_accessible;
105   widget_class->get_preferred_width = gtk_spinner_get_preferred_width;
106   widget_class->get_preferred_height = gtk_spinner_get_preferred_height;
107
108   /* GtkSpinner:active:
109    *
110    * Whether the spinner is active
111    *
112    * Since: 2.20
113    */
114   g_object_class_install_property (gobject_class,
115                                    PROP_ACTIVE,
116                                    g_param_spec_boolean ("active",
117                                                          P_("Active"),
118                                                          P_("Whether the spinner is active"),
119                                                          FALSE,
120                                                          G_PARAM_READWRITE));
121 }
122
123 static void
124 gtk_spinner_get_property (GObject    *object,
125                           guint       param_id,
126                           GValue     *value,
127                           GParamSpec *pspec)
128 {
129   GtkSpinnerPrivate *priv;
130
131   priv = GTK_SPINNER (object)->priv;
132
133   switch (param_id)
134     {
135       case PROP_ACTIVE:
136         g_value_set_boolean (value, priv->active);
137         break;
138       default:
139         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
140     }
141 }
142
143 static void
144 gtk_spinner_set_property (GObject      *object,
145                           guint         param_id,
146                           const GValue *value,
147                           GParamSpec   *pspec)
148 {
149   switch (param_id)
150     {
151       case PROP_ACTIVE:
152         gtk_spinner_set_active (GTK_SPINNER (object), g_value_get_boolean (value));
153         break;
154       default:
155         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
156     }
157 }
158
159 static void
160 gtk_spinner_init (GtkSpinner *spinner)
161 {
162   GtkSpinnerPrivate *priv;
163   GtkStyleContext *context;
164
165   priv = G_TYPE_INSTANCE_GET_PRIVATE (spinner,
166                                       GTK_TYPE_SPINNER,
167                                       GtkSpinnerPrivate);
168   spinner->priv = priv;
169
170   gtk_widget_set_has_window (GTK_WIDGET (spinner), FALSE);
171
172   context = gtk_widget_get_style_context (GTK_WIDGET (spinner));
173   gtk_style_context_add_class (context, GTK_STYLE_CLASS_SPINNER);
174 }
175
176 static void
177 gtk_spinner_get_preferred_width (GtkWidget *widget,
178                                  gint      *minimum_size,
179                                  gint      *natural_size)
180 {
181   if (minimum_size)
182     *minimum_size = SPINNER_SIZE;
183
184   if (natural_size)
185     *natural_size = SPINNER_SIZE;
186 }
187
188 static void
189 gtk_spinner_get_preferred_height (GtkWidget *widget,
190                                   gint      *minimum_size,
191                                   gint      *natural_size)
192 {
193   if (minimum_size)
194     *minimum_size = SPINNER_SIZE;
195
196   if (natural_size)
197     *natural_size = SPINNER_SIZE;
198 }
199
200 static gboolean
201 gtk_spinner_draw (GtkWidget *widget,
202                   cairo_t   *cr)
203 {
204   GtkStyleContext *context;
205   GtkStateFlags state;
206
207   context = gtk_widget_get_style_context (widget);
208   state = gtk_widget_get_state_flags (widget);
209
210   gtk_style_context_set_state (context, state);
211   gtk_render_activity (context, cr, 0, 0,
212                        gtk_widget_get_allocated_width (widget),
213                        gtk_widget_get_allocated_height (widget));
214
215   return FALSE;
216 }
217
218 static void
219 gtk_spinner_set_active (GtkSpinner *spinner,
220                         gboolean    active)
221 {
222   GtkSpinnerPrivate *priv = spinner->priv;
223
224   active = !!active;
225
226   if (priv->active != active)
227     {
228       priv->active = active;
229
230       g_object_notify (G_OBJECT (spinner), "active");
231
232       if (active)
233         gtk_widget_set_state_flags (GTK_WIDGET (spinner),
234                                     GTK_STATE_FLAG_ACTIVE, FALSE);
235       else
236         gtk_widget_unset_state_flags (GTK_WIDGET (spinner),
237                                       GTK_STATE_FLAG_ACTIVE);
238     }
239 }
240
241 /* accessible implementation */
242
243 static void
244 gtk_spinner_accessible_image_get_size (AtkImage *image,
245                                        gint     *width,
246                                        gint     *height)
247 {
248   GtkAllocation allocation;
249   GtkWidget *widget;
250
251   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (image));
252   if (widget == NULL)
253     {
254       *width = *height = 0;
255     }
256   else
257     {
258       gtk_widget_get_allocation (widget, &allocation);
259       *width = allocation.width;
260       *height = allocation.height;
261     }
262 }
263
264 static void
265 gtk_spinner_accessible_image_iface_init (AtkImageIface *iface)
266 {
267   iface->get_image_size = gtk_spinner_accessible_image_get_size;
268 }
269
270 /* dummy typedef */
271 typedef struct _GtkSpinnerAccessible            GtkSpinnerAccessible;
272 typedef struct _GtkSpinnerAccessibleClass       GtkSpinnerAccessibleClass;
273
274 ATK_DEFINE_TYPE_WITH_CODE (GtkSpinnerAccessible,
275                            gtk_spinner_accessible,
276                            GTK_TYPE_IMAGE,
277                            G_IMPLEMENT_INTERFACE (ATK_TYPE_IMAGE,
278                                                   gtk_spinner_accessible_image_iface_init));
279
280 static void
281 gtk_spinner_accessible_initialize (AtkObject *accessible,
282                                    gpointer   widget)
283 {
284   ATK_OBJECT_CLASS (gtk_spinner_accessible_parent_class)->initialize (accessible, widget);
285
286   atk_object_set_name (accessible, C_("throbbing progress animation widget", "Spinner"));
287   atk_object_set_description (accessible, _("Provides visual indication of progress"));
288 }
289
290 static void
291 gtk_spinner_accessible_class_init (GtkSpinnerAccessibleClass *klass)
292 {
293   AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass);
294
295   atk_class->initialize = gtk_spinner_accessible_initialize;
296 }
297
298 static void
299 gtk_spinner_accessible_init (GtkSpinnerAccessible *self)
300 {
301 }
302
303 /* factory */
304 typedef AtkObjectFactory        GtkSpinnerAccessibleFactory;
305 typedef AtkObjectFactoryClass   GtkSpinnerAccessibleFactoryClass;
306
307 G_DEFINE_TYPE (GtkSpinnerAccessibleFactory,
308                _gtk_spinner_accessible_factory,
309                ATK_TYPE_OBJECT_FACTORY);
310
311 static GType
312 gtk_spinner_accessible_factory_get_accessible_type (void)
313 {
314   return gtk_spinner_accessible_get_type ();
315 }
316
317 static AtkObject *
318 gtk_spinner_accessible_factory_create_accessible (GObject *obj)
319 {
320   AtkObject *accessible;
321
322   accessible = g_object_new (gtk_spinner_accessible_get_type (), NULL);
323   atk_object_initialize (accessible, obj);
324
325   return accessible;
326 }
327
328 static void
329 _gtk_spinner_accessible_factory_class_init (AtkObjectFactoryClass *klass)
330 {
331   klass->create_accessible = gtk_spinner_accessible_factory_create_accessible;
332   klass->get_accessible_type = gtk_spinner_accessible_factory_get_accessible_type;
333 }
334
335 static void
336 _gtk_spinner_accessible_factory_init (AtkObjectFactory *factory)
337 {
338 }
339
340 static AtkObject *
341 gtk_spinner_get_accessible (GtkWidget *widget)
342 {
343   static gboolean first_time = TRUE;
344
345   if (first_time)
346     {
347       AtkObjectFactory *factory;
348       AtkRegistry *registry;
349       GType derived_type;
350       GType derived_atk_type;
351
352       /*
353        * Figure out whether accessibility is enabled by looking at the
354        * type of the accessible object which would be created for
355        * the parent type of GtkSpinner.
356        */
357       derived_type = g_type_parent (GTK_TYPE_SPINNER);
358
359       registry = atk_get_default_registry ();
360       factory = atk_registry_get_factory (registry, derived_type);
361       derived_atk_type = atk_object_factory_get_accessible_type (factory);
362       if (g_type_is_a (derived_atk_type, GTK_TYPE_ACCESSIBLE))
363         atk_registry_set_factory_type (registry,
364                                        GTK_TYPE_SPINNER,
365                                        _gtk_spinner_accessible_factory_get_type ());
366       first_time = FALSE;
367     }
368
369   return GTK_WIDGET_CLASS (gtk_spinner_parent_class)->get_accessible (widget);
370 }
371
372 /**
373  * gtk_spinner_new:
374  *
375  * Returns a new spinner widget. Not yet started.
376  *
377  * Return value: a new #GtkSpinner
378  *
379  * Since: 2.20
380  */
381 GtkWidget *
382 gtk_spinner_new (void)
383 {
384   return g_object_new (GTK_TYPE_SPINNER, NULL);
385 }
386
387 /**
388  * gtk_spinner_start:
389  * @spinner: a #GtkSpinner
390  *
391  * Starts the animation of the spinner.
392  *
393  * Since: 2.20
394  */
395 void
396 gtk_spinner_start (GtkSpinner *spinner)
397 {
398   g_return_if_fail (GTK_IS_SPINNER (spinner));
399
400   gtk_spinner_set_active (spinner, TRUE);
401 }
402
403 /**
404  * gtk_spinner_stop:
405  * @spinner: a #GtkSpinner
406  *
407  * Stops the animation of the spinner.
408  *
409  * Since: 2.20
410  */
411 void
412 gtk_spinner_stop (GtkSpinner *spinner)
413 {
414   g_return_if_fail (GTK_IS_SPINNER (spinner));
415
416   gtk_spinner_set_active (spinner, FALSE);
417 }