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, see <http://www.gnu.org/licenses/>.
18 *
19 * Code adapted from egg-spinner
20 * by Christian Hergert <christian.hergert@gmail.com>
21 */
22
23 /*
24 * Modified by the GTK+ Team and others 2007. See the AUTHORS
25 * file for a list of people on the GTK+ Team. See the ChangeLog
26 * files for a list of changes. These files are distributed with
27 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
28 */
29
30 #include "config.h"
31
32 #include "gtkspinner.h"
33
34 #include "gtkimage.h"
35 #include "gtkintl.h"
36 #include "gtkprivate.h"
37 #include "gtkstylecontext.h"
38 #include "gtkstylecontextprivate.h"
39 #include "gtkwidgetprivate.h"
40 #include "a11y/gtkspinneraccessible.h"
41 #include "gtkbuiltiniconprivate.h"
42
43
44 /**
45 * SECTION:gtkspinner
46 * @Short_description: Show a spinner animation
47 * @Title: GtkSpinner
48 * @See_also: #GtkCellRendererSpinner, #GtkProgressBar
49 *
50 * A GtkSpinner widget displays an icon-size spinning animation.
51 * It is often used as an alternative to a #GtkProgressBar for
52 * displaying indefinite activity, instead of actual progress.
53 *
54 * To start the animation, use gtk_spinner_start(), to stop it
55 * use gtk_spinner_stop().
56 *
57 * # CSS nodes
58 *
59 * GtkSpinner has a single CSS node with the name spinner. When the animation is
60 * active, the :checked pseudoclass is added to this node.
61 */
62
63
64 enum {
65 PROP_0,
66 PROP_ACTIVE
67 };
68
69 struct _GtkSpinnerPrivate
70 {
71 GtkCssGadget *gadget;
72 gboolean active;
73 };
74
G_DEFINE_TYPE_WITH_PRIVATE(GtkSpinner,gtk_spinner,GTK_TYPE_WIDGET)75 G_DEFINE_TYPE_WITH_PRIVATE (GtkSpinner, gtk_spinner, GTK_TYPE_WIDGET)
76
77 static void
78 gtk_spinner_finalize (GObject *object)
79 {
80 GtkSpinner *spinner = GTK_SPINNER (object);
81
82 g_clear_object (&spinner->priv->gadget);
83
84 G_OBJECT_CLASS (gtk_spinner_parent_class)->finalize (object);
85 }
86
87 static void
gtk_spinner_get_preferred_width(GtkWidget * widget,gint * minimum,gint * natural)88 gtk_spinner_get_preferred_width (GtkWidget *widget,
89 gint *minimum,
90 gint *natural)
91 {
92 gtk_css_gadget_get_preferred_size (GTK_SPINNER (widget)->priv->gadget,
93 GTK_ORIENTATION_HORIZONTAL,
94 -1,
95 minimum, natural,
96 NULL, NULL);
97 }
98
99 static void
gtk_spinner_get_preferred_height(GtkWidget * widget,gint * minimum,gint * natural)100 gtk_spinner_get_preferred_height (GtkWidget *widget,
101 gint *minimum,
102 gint *natural)
103 {
104 gtk_css_gadget_get_preferred_size (GTK_SPINNER (widget)->priv->gadget,
105 GTK_ORIENTATION_VERTICAL,
106 -1,
107 minimum, natural,
108 NULL, NULL);
109 }
110
111 static void
gtk_spinner_size_allocate(GtkWidget * widget,GtkAllocation * allocation)112 gtk_spinner_size_allocate (GtkWidget *widget,
113 GtkAllocation *allocation)
114 {
115 GtkAllocation clip;
116
117 gtk_widget_set_allocation (widget, allocation);
118
119 gtk_css_gadget_allocate (GTK_SPINNER (widget)->priv->gadget,
120 allocation,
121 gtk_widget_get_allocated_baseline (widget),
122 &clip);
123
124 gtk_widget_set_clip (widget, &clip);
125 }
126
127 static gboolean
gtk_spinner_draw(GtkWidget * widget,cairo_t * cr)128 gtk_spinner_draw (GtkWidget *widget,
129 cairo_t *cr)
130 {
131 gtk_css_gadget_draw (GTK_SPINNER (widget)->priv->gadget, cr);
132
133 return FALSE;
134 }
135
136 static void
gtk_spinner_set_active(GtkSpinner * spinner,gboolean active)137 gtk_spinner_set_active (GtkSpinner *spinner,
138 gboolean active)
139 {
140 GtkSpinnerPrivate *priv = spinner->priv;
141
142 active = !!active;
143
144 if (priv->active != active)
145 {
146 priv->active = active;
147
148 g_object_notify (G_OBJECT (spinner), "active");
149
150 if (active)
151 gtk_widget_set_state_flags (GTK_WIDGET (spinner),
152 GTK_STATE_FLAG_CHECKED, FALSE);
153 else
154 gtk_widget_unset_state_flags (GTK_WIDGET (spinner),
155 GTK_STATE_FLAG_CHECKED);
156 }
157 }
158
159 static void
gtk_spinner_get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)160 gtk_spinner_get_property (GObject *object,
161 guint param_id,
162 GValue *value,
163 GParamSpec *pspec)
164 {
165 GtkSpinnerPrivate *priv;
166
167 priv = GTK_SPINNER (object)->priv;
168
169 switch (param_id)
170 {
171 case PROP_ACTIVE:
172 g_value_set_boolean (value, priv->active);
173 break;
174 default:
175 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
176 }
177 }
178
179 static void
gtk_spinner_set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)180 gtk_spinner_set_property (GObject *object,
181 guint param_id,
182 const GValue *value,
183 GParamSpec *pspec)
184 {
185 switch (param_id)
186 {
187 case PROP_ACTIVE:
188 gtk_spinner_set_active (GTK_SPINNER (object), g_value_get_boolean (value));
189 break;
190 default:
191 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
192 }
193 }
194
195 static void
gtk_spinner_class_init(GtkSpinnerClass * klass)196 gtk_spinner_class_init (GtkSpinnerClass *klass)
197 {
198 GObjectClass *gobject_class;
199 GtkWidgetClass *widget_class;
200
201 gobject_class = G_OBJECT_CLASS(klass);
202 gobject_class->finalize = gtk_spinner_finalize;
203 gobject_class->get_property = gtk_spinner_get_property;
204 gobject_class->set_property = gtk_spinner_set_property;
205
206 widget_class = GTK_WIDGET_CLASS(klass);
207 widget_class->size_allocate = gtk_spinner_size_allocate;
208 widget_class->draw = gtk_spinner_draw;
209 widget_class->get_preferred_width = gtk_spinner_get_preferred_width;
210 widget_class->get_preferred_height = gtk_spinner_get_preferred_height;
211
212 /* GtkSpinner:active:
213 *
214 * Whether the spinner is active
215 *
216 * Since: 2.20
217 */
218 g_object_class_install_property (gobject_class,
219 PROP_ACTIVE,
220 g_param_spec_boolean ("active",
221 P_("Active"),
222 P_("Whether the spinner is active"),
223 FALSE,
224 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
225
226 gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_SPINNER_ACCESSIBLE);
227 gtk_widget_class_set_css_name (widget_class, "spinner");
228 }
229
230 static void
gtk_spinner_init(GtkSpinner * spinner)231 gtk_spinner_init (GtkSpinner *spinner)
232 {
233 GtkCssNode *widget_node;
234
235 spinner->priv = gtk_spinner_get_instance_private (spinner);
236
237 gtk_widget_set_has_window (GTK_WIDGET (spinner), FALSE);
238
239 widget_node = gtk_widget_get_css_node (GTK_WIDGET (spinner));
240 spinner->priv->gadget = gtk_builtin_icon_new_for_node (widget_node, GTK_WIDGET (spinner));
241 gtk_builtin_icon_set_image (GTK_BUILTIN_ICON (spinner->priv->gadget), GTK_CSS_IMAGE_BUILTIN_SPINNER);
242 gtk_builtin_icon_set_default_size (GTK_BUILTIN_ICON (spinner->priv->gadget), 16);
243 }
244
245 /**
246 * gtk_spinner_new:
247 *
248 * Returns a new spinner widget. Not yet started.
249 *
250 * Returns: a new #GtkSpinner
251 *
252 * Since: 2.20
253 */
254 GtkWidget *
gtk_spinner_new(void)255 gtk_spinner_new (void)
256 {
257 return g_object_new (GTK_TYPE_SPINNER, NULL);
258 }
259
260 /**
261 * gtk_spinner_start:
262 * @spinner: a #GtkSpinner
263 *
264 * Starts the animation of the spinner.
265 *
266 * Since: 2.20
267 */
268 void
gtk_spinner_start(GtkSpinner * spinner)269 gtk_spinner_start (GtkSpinner *spinner)
270 {
271 g_return_if_fail (GTK_IS_SPINNER (spinner));
272
273 gtk_spinner_set_active (spinner, TRUE);
274 }
275
276 /**
277 * gtk_spinner_stop:
278 * @spinner: a #GtkSpinner
279 *
280 * Stops the animation of the spinner.
281 *
282 * Since: 2.20
283 */
284 void
gtk_spinner_stop(GtkSpinner * spinner)285 gtk_spinner_stop (GtkSpinner *spinner)
286 {
287 g_return_if_fail (GTK_IS_SPINNER (spinner));
288
289 gtk_spinner_set_active (spinner, FALSE);
290 }
291