1 /* GTK - The GIMP Toolkit
2 *
3 * Copyright (C) 2009 Matthias Clasen <mclasen@redhat.com>
4 * Copyright (C) 2008 Richard Hughes <richard@hughsie.com>
5 * Copyright (C) 2009 Bastien Nocera <hadess@hadess.net>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 /*
22 * Modified by the GTK+ Team and others 2007. See the AUTHORS
23 * file for a list of people on the GTK+ Team. See the ChangeLog
24 * files for a list of changes. These files are distributed with
25 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
26 */
27
28 #include "config.h"
29
30 #include "gtkcellrendererspinner.h"
31 #include "deprecated/gtkiconfactory.h"
32 #include "gtkicontheme.h"
33 #include "gtkintl.h"
34 #include "gtksettings.h"
35 #include "gtktypebuiltins.h"
36
37 #undef GDK_DEPRECATED
38 #undef GDK_DEPRECATED_FOR
39 #define GDK_DEPRECATED
40 #define GDK_DEPRECATED_FOR(f)
41
42 #include "deprecated/gtkstyle.h"
43
44
45 /**
46 * SECTION:gtkcellrendererspinner
47 * @Short_description: Renders a spinning animation in a cell
48 * @Title: GtkCellRendererSpinner
49 * @See_also: #GtkSpinner, #GtkCellRendererProgress
50 *
51 * GtkCellRendererSpinner renders a spinning animation in a cell, very
52 * similar to #GtkSpinner. It can often be used as an alternative
53 * to a #GtkCellRendererProgress for displaying indefinite activity,
54 * instead of actual progress.
55 *
56 * To start the animation in a cell, set the #GtkCellRendererSpinner:active
57 * property to %TRUE and increment the #GtkCellRendererSpinner:pulse property
58 * at regular intervals. The usual way to set the cell renderer properties
59 * for each cell is to bind them to columns in your tree model using e.g.
60 * gtk_tree_view_column_add_attribute().
61 */
62
63
64 enum {
65 PROP_0,
66 PROP_ACTIVE,
67 PROP_PULSE,
68 PROP_SIZE
69 };
70
71 struct _GtkCellRendererSpinnerPrivate
72 {
73 gboolean active;
74 guint pulse;
75 GtkIconSize icon_size, old_icon_size;
76 gint size;
77 };
78
79
80 static void gtk_cell_renderer_spinner_get_property (GObject *object,
81 guint param_id,
82 GValue *value,
83 GParamSpec *pspec);
84 static void gtk_cell_renderer_spinner_set_property (GObject *object,
85 guint param_id,
86 const GValue *value,
87 GParamSpec *pspec);
88 static void gtk_cell_renderer_spinner_get_size (GtkCellRenderer *cell,
89 GtkWidget *widget,
90 const GdkRectangle *cell_area,
91 gint *x_offset,
92 gint *y_offset,
93 gint *width,
94 gint *height);
95 static void gtk_cell_renderer_spinner_render (GtkCellRenderer *cell,
96 cairo_t *cr,
97 GtkWidget *widget,
98 const GdkRectangle *background_area,
99 const GdkRectangle *cell_area,
100 GtkCellRendererState flags);
101
G_DEFINE_TYPE_WITH_PRIVATE(GtkCellRendererSpinner,gtk_cell_renderer_spinner,GTK_TYPE_CELL_RENDERER)102 G_DEFINE_TYPE_WITH_PRIVATE (GtkCellRendererSpinner, gtk_cell_renderer_spinner, GTK_TYPE_CELL_RENDERER)
103
104 static void
105 gtk_cell_renderer_spinner_class_init (GtkCellRendererSpinnerClass *klass)
106 {
107 GObjectClass *object_class = G_OBJECT_CLASS (klass);
108 GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);
109
110 object_class->get_property = gtk_cell_renderer_spinner_get_property;
111 object_class->set_property = gtk_cell_renderer_spinner_set_property;
112
113 cell_class->get_size = gtk_cell_renderer_spinner_get_size;
114 cell_class->render = gtk_cell_renderer_spinner_render;
115
116 /* GtkCellRendererSpinner:active:
117 *
118 * Whether the spinner is active (ie. shown) in the cell
119 *
120 * Since: 2.20
121 */
122 g_object_class_install_property (object_class,
123 PROP_ACTIVE,
124 g_param_spec_boolean ("active",
125 P_("Active"),
126 P_("Whether the spinner is active (ie. shown) in the cell"),
127 FALSE,
128 G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
129
130 /**
131 * GtkCellRendererSpinner:pulse:
132 *
133 * Pulse of the spinner. Increment this value to draw the next frame of the
134 * spinner animation. Usually, you would update this value in a timeout.
135 *
136 * By default, the #GtkSpinner widget draws one full cycle of the animation,
137 * consisting of 12 frames, in 750 milliseconds.
138 *
139 * Since: 2.20
140 */
141 g_object_class_install_property (object_class,
142 PROP_PULSE,
143 g_param_spec_uint ("pulse",
144 P_("Pulse"),
145 P_("Pulse of the spinner"),
146 0, G_MAXUINT, 0,
147 G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
148
149 /**
150 * GtkCellRendererSpinner:size:
151 *
152 * The #GtkIconSize value that specifies the size of the rendered spinner.
153 *
154 * Since: 2.20
155 */
156 g_object_class_install_property (object_class,
157 PROP_SIZE,
158 g_param_spec_enum ("size",
159 P_("Size"),
160 P_("The GtkIconSize value that specifies the size of the rendered spinner"),
161 GTK_TYPE_ICON_SIZE, GTK_ICON_SIZE_MENU,
162 G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
163
164 }
165
166 static void
gtk_cell_renderer_spinner_init(GtkCellRendererSpinner * cell)167 gtk_cell_renderer_spinner_init (GtkCellRendererSpinner *cell)
168 {
169 cell->priv = gtk_cell_renderer_spinner_get_instance_private (cell);
170 cell->priv->pulse = 0;
171 cell->priv->old_icon_size = GTK_ICON_SIZE_INVALID;
172 cell->priv->icon_size = GTK_ICON_SIZE_MENU;
173 }
174
175 /**
176 * gtk_cell_renderer_spinner_new:
177 *
178 * Returns a new cell renderer which will show a spinner to indicate
179 * activity.
180 *
181 * Returns: a new #GtkCellRenderer
182 *
183 * Since: 2.20
184 */
185 GtkCellRenderer *
gtk_cell_renderer_spinner_new(void)186 gtk_cell_renderer_spinner_new (void)
187 {
188 return g_object_new (GTK_TYPE_CELL_RENDERER_SPINNER, NULL);
189 }
190
191 static void
gtk_cell_renderer_spinner_update_size(GtkCellRendererSpinner * cell,GtkWidget * widget)192 gtk_cell_renderer_spinner_update_size (GtkCellRendererSpinner *cell,
193 GtkWidget *widget)
194 {
195 GtkCellRendererSpinnerPrivate *priv = cell->priv;
196
197 if (priv->old_icon_size == priv->icon_size)
198 return;
199
200 if (!gtk_icon_size_lookup (priv->icon_size, &priv->size, NULL))
201 {
202 g_warning ("Invalid icon size %u", priv->icon_size);
203 priv->size = 24;
204 }
205 }
206
207 static void
gtk_cell_renderer_spinner_get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)208 gtk_cell_renderer_spinner_get_property (GObject *object,
209 guint param_id,
210 GValue *value,
211 GParamSpec *pspec)
212 {
213 GtkCellRendererSpinner *cell = GTK_CELL_RENDERER_SPINNER (object);
214 GtkCellRendererSpinnerPrivate *priv = cell->priv;
215
216 switch (param_id)
217 {
218 case PROP_ACTIVE:
219 g_value_set_boolean (value, priv->active);
220 break;
221 case PROP_PULSE:
222 g_value_set_uint (value, priv->pulse);
223 break;
224 case PROP_SIZE:
225 g_value_set_enum (value, priv->icon_size);
226 break;
227 default:
228 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
229 }
230 }
231
232 static void
gtk_cell_renderer_spinner_set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)233 gtk_cell_renderer_spinner_set_property (GObject *object,
234 guint param_id,
235 const GValue *value,
236 GParamSpec *pspec)
237 {
238 GtkCellRendererSpinner *cell = GTK_CELL_RENDERER_SPINNER (object);
239 GtkCellRendererSpinnerPrivate *priv = cell->priv;
240
241 switch (param_id)
242 {
243 case PROP_ACTIVE:
244 if (priv->active != g_value_get_boolean (value))
245 {
246 priv->active = g_value_get_boolean (value);
247 g_object_notify (object, "active");
248 }
249 break;
250 case PROP_PULSE:
251 if (priv->pulse != g_value_get_uint (value))
252 {
253 priv->pulse = g_value_get_uint (value);
254 g_object_notify (object, "pulse");
255 }
256 break;
257 case PROP_SIZE:
258 if (priv->icon_size != g_value_get_enum (value))
259 {
260 priv->old_icon_size = priv->icon_size;
261 priv->icon_size = g_value_get_enum (value);
262 g_object_notify (object, "size");
263 }
264 break;
265 default:
266 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
267 }
268 }
269
270 static void
gtk_cell_renderer_spinner_get_size(GtkCellRenderer * cellr,GtkWidget * widget,const GdkRectangle * cell_area,gint * x_offset,gint * y_offset,gint * width,gint * height)271 gtk_cell_renderer_spinner_get_size (GtkCellRenderer *cellr,
272 GtkWidget *widget,
273 const GdkRectangle *cell_area,
274 gint *x_offset,
275 gint *y_offset,
276 gint *width,
277 gint *height)
278 {
279 GtkCellRendererSpinner *cell = GTK_CELL_RENDERER_SPINNER (cellr);
280 GtkCellRendererSpinnerPrivate *priv = cell->priv;
281 gdouble align;
282 gint w, h;
283 gint xpad, ypad;
284 gfloat xalign, yalign;
285 gboolean rtl;
286
287 rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
288
289 gtk_cell_renderer_spinner_update_size (cell, widget);
290
291 g_object_get (cellr,
292 "xpad", &xpad,
293 "ypad", &ypad,
294 "xalign", &xalign,
295 "yalign", &yalign,
296 NULL);
297 w = h = priv->size;
298
299 if (cell_area)
300 {
301 if (x_offset)
302 {
303 align = rtl ? 1.0 - xalign : xalign;
304 *x_offset = align * (cell_area->width - w);
305 *x_offset = MAX (*x_offset, 0);
306 }
307 if (y_offset)
308 {
309 align = rtl ? 1.0 - yalign : yalign;
310 *y_offset = align * (cell_area->height - h);
311 *y_offset = MAX (*y_offset, 0);
312 }
313 }
314 else
315 {
316 if (x_offset)
317 *x_offset = 0;
318 if (y_offset)
319 *y_offset = 0;
320 }
321
322 if (width)
323 *width = w;
324 if (height)
325 *height = h;
326 }
327
328 static void
gtk_cell_renderer_spinner_render(GtkCellRenderer * cellr,cairo_t * cr,GtkWidget * widget,const GdkRectangle * background_area,const GdkRectangle * cell_area,GtkCellRendererState flags)329 gtk_cell_renderer_spinner_render (GtkCellRenderer *cellr,
330 cairo_t *cr,
331 GtkWidget *widget,
332 const GdkRectangle *background_area,
333 const GdkRectangle *cell_area,
334 GtkCellRendererState flags)
335 {
336 GtkCellRendererSpinner *cell = GTK_CELL_RENDERER_SPINNER (cellr);
337 GtkCellRendererSpinnerPrivate *priv = cell->priv;
338 GtkStateType state;
339 GdkRectangle pix_rect;
340 GdkRectangle draw_rect;
341 gint xpad, ypad;
342
343 if (!priv->active)
344 return;
345
346 gtk_cell_renderer_spinner_get_size (cellr, widget, (GdkRectangle *) cell_area,
347 &pix_rect.x, &pix_rect.y,
348 &pix_rect.width, &pix_rect.height);
349
350 g_object_get (cellr,
351 "xpad", &xpad,
352 "ypad", &ypad,
353 NULL);
354 pix_rect.x += cell_area->x + xpad;
355 pix_rect.y += cell_area->y + ypad;
356 pix_rect.width -= xpad * 2;
357 pix_rect.height -= ypad * 2;
358
359 if (!gdk_rectangle_intersect (cell_area, &pix_rect, &draw_rect))
360 return;
361
362 state = GTK_STATE_NORMAL;
363 if ((gtk_widget_get_state_flags (widget) & GTK_STATE_FLAG_INSENSITIVE) ||
364 !gtk_cell_renderer_get_sensitive (cellr))
365 {
366 state = GTK_STATE_INSENSITIVE;
367 }
368 else
369 {
370 if ((flags & GTK_CELL_RENDERER_SELECTED) != 0)
371 {
372 if (gtk_widget_has_focus (widget))
373 state = GTK_STATE_SELECTED;
374 else
375 state = GTK_STATE_ACTIVE;
376 }
377 else
378 state = GTK_STATE_PRELIGHT;
379 }
380
381 cairo_save (cr);
382
383 gdk_cairo_rectangle (cr, cell_area);
384 cairo_clip (cr);
385
386 gtk_paint_spinner (gtk_widget_get_style (widget),
387 cr,
388 state,
389 widget,
390 "cell",
391 priv->pulse,
392 draw_rect.x, draw_rect.y,
393 draw_rect.width, draw_rect.height);
394
395 cairo_restore (cr);
396 }
397