]> Pileus Git - ~andy/gtk/blob - gtk/gtkcellrendererspinner.c
Bug 319607 – Add a throbber (activity widget) to GTK+
[~andy/gtk] / gtk / gtkcellrendererspinner.c
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, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA  02111-1307, USA.
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 "gtkintl.h"
33 #include <gtk/gtk.h>
34 #include "gtkcellrendererspinner.h"
35 #include "gtkalias.h"
36
37 enum {
38   PROP_0,
39   PROP_ACTIVE,
40   PROP_PULSE,
41   PROP_SIZE
42 };
43
44 struct _GtkCellRendererSpinnerPrivate
45 {
46   gboolean active;
47   guint pulse;
48   GtkIconSize icon_size, old_icon_size;
49   gint size;
50 };
51
52 #define GTK_CELL_RENDERER_SPINNER_GET_PRIVATE(object)        \
53                 (G_TYPE_INSTANCE_GET_PRIVATE ((object),        \
54                         GTK_TYPE_CELL_RENDERER_SPINNER, \
55                         GtkCellRendererSpinnerPrivate))
56
57 static void gtk_cell_renderer_spinner_get_property        (GObject                *object,
58                                                          guint                         param_id,
59                                                          GValue                        *value,
60                                                          GParamSpec                *pspec);
61 static void gtk_cell_renderer_spinner_set_property        (GObject                *object,
62                                                          guint                         param_id,
63                                                          const GValue                *value,
64                                                          GParamSpec                *pspec);
65 static void gtk_cell_renderer_spinner_get_size                (GtkCellRenderer        *cell,
66                                                          GtkWidget                *widget,
67                                                          GdkRectangle                *cell_area,
68                                                          gint                        *x_offset,
69                                                          gint                        *y_offset,
70                                                          gint                        *width,
71                                                          gint                        *height);
72 static void gtk_cell_renderer_spinner_render                (GtkCellRenderer        *cell,
73                                                          GdkWindow                *window,
74                                                          GtkWidget                *widget,
75                                                          GdkRectangle                *background_area,
76                                                          GdkRectangle                *cell_area,
77                                                          GdkRectangle                *expose_area,
78                                                          guint                         flags);
79
80 G_DEFINE_TYPE (GtkCellRendererSpinner, gtk_cell_renderer_spinner, GTK_TYPE_CELL_RENDERER)
81
82 static void
83 gtk_cell_renderer_spinner_class_init (GtkCellRendererSpinnerClass *klass)
84 {
85   GObjectClass *object_class = G_OBJECT_CLASS (klass);
86   GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);
87
88   object_class->get_property = gtk_cell_renderer_spinner_get_property;
89   object_class->set_property = gtk_cell_renderer_spinner_set_property;
90
91   cell_class->get_size = gtk_cell_renderer_spinner_get_size;
92   cell_class->render = gtk_cell_renderer_spinner_render;
93
94   /* GtkCellRendererSpinner::active:
95    *
96    * Whether the spinner is active (ie. shown) in the cell
97    *
98    * Since 2.20
99    */
100   g_object_class_install_property (object_class,
101                                    PROP_ACTIVE,
102                                    g_param_spec_boolean ("active",
103                                                          P_("Active"),
104                                                          P_("Whether the spinner is active (ie. shown) in the cell"),
105                                                          FALSE,
106                                                          G_PARAM_READWRITE));
107   /* GtkCellRendererSpinner::pulse:
108    *
109    * Pulse of the spinner. Increment this value to draw the next frame of the spinner animation.
110    * Usually, you would update this value in a timeout, every 80 milliseconds to show a full
111    * animation within one second.
112    *
113    * Since 2.20
114    */
115   g_object_class_install_property (object_class,
116                                    PROP_PULSE,
117                                    g_param_spec_uint ("pulse",
118                                                       P_("Pulse"),
119                                                       P_("Pulse of the spinner"),
120                                                       0, G_MAXUINT, 0,
121                                                       G_PARAM_READWRITE));
122   /* GtkCellRendererSpinner::size:
123    *
124    * The #GtkIconSize value that specifies the size of the rendered spinner.
125    *
126    * Since 2.20
127    */
128   g_object_class_install_property (object_class,
129                                    PROP_SIZE,
130                                    g_param_spec_enum ("size",
131                                                       P_("Size"),
132                                                       P_("The #GtkIconSize value that specifies the size of the rendered spinner"),
133                                                       GTK_TYPE_ICON_SIZE, GTK_ICON_SIZE_MENU,
134                                                       G_PARAM_READWRITE));
135
136
137   g_type_class_add_private (object_class, sizeof (GtkCellRendererSpinnerPrivate));
138 }
139
140 static void
141 gtk_cell_renderer_spinner_init (GtkCellRendererSpinner *cell)
142 {
143   cell->priv = GTK_CELL_RENDERER_SPINNER_GET_PRIVATE (cell);
144   cell->priv->pulse = 0;
145   cell->priv->old_icon_size = GTK_ICON_SIZE_INVALID;
146   cell->priv->icon_size = GTK_ICON_SIZE_MENU;
147 }
148
149 /**
150  * gtk_cell_renderer_spinner_new
151  *
152  * Returns a new cell renderer which will show a spinner to indicate
153  * activity.
154  *
155  * Return value: a new #GtkCellRenderer
156  *
157  * Since: 2.20
158  */
159 GtkCellRenderer *
160 gtk_cell_renderer_spinner_new (void)
161 {
162   return g_object_new (GTK_TYPE_CELL_RENDERER_SPINNER, NULL);
163 }
164
165 static void
166 gtk_cell_renderer_spinner_update_size (GtkCellRendererSpinner *cell,
167                                        GtkWidget *widget)
168 {
169   GtkCellRendererSpinnerPrivate *priv = cell->priv;
170   GdkScreen *screen;
171   GtkIconTheme *icon_theme;
172   GtkSettings *settings;
173
174   if (cell->priv->old_icon_size == cell->priv->icon_size)
175     return;
176
177   screen = gtk_widget_get_screen (GTK_WIDGET (widget));
178   icon_theme = gtk_icon_theme_get_for_screen (screen);
179   settings = gtk_settings_get_for_screen (screen);
180
181   if (!gtk_icon_size_lookup_for_settings (settings, priv->icon_size, &priv->size, NULL))
182     {
183       g_warning ("Invalid icon size %u\n", priv->icon_size);
184       priv->size = 24;
185     }
186 }
187
188 static void
189 gtk_cell_renderer_spinner_get_property (GObject    *object,
190                                         guint       param_id,
191                                         GValue     *value,
192                                         GParamSpec *pspec)
193 {
194   GtkCellRendererSpinner *cell = GTK_CELL_RENDERER_SPINNER (object);
195   GtkCellRendererSpinnerPrivate *priv = cell->priv;
196
197   switch (param_id)
198     {
199       case PROP_ACTIVE:
200         g_value_set_boolean (value, priv->active);
201         break;
202       case PROP_PULSE:
203         g_value_set_uint (value, priv->pulse);
204         break;
205       case PROP_SIZE:
206         g_value_set_enum (value, priv->icon_size);
207         break;
208       default:
209         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
210     }
211 }
212
213 static void
214 gtk_cell_renderer_spinner_set_property (GObject      *object,
215                                         guint         param_id,
216                                         const GValue *value,
217                                         GParamSpec   *pspec)
218 {
219   GtkCellRendererSpinner *cell = GTK_CELL_RENDERER_SPINNER (object);
220   GtkCellRendererSpinnerPrivate *priv = cell->priv;
221
222   switch (param_id)
223     {
224       case PROP_ACTIVE:
225         priv->active = g_value_get_boolean (value);
226         break;
227       case PROP_PULSE:
228         priv->pulse = g_value_get_uint (value);
229         break;
230       case PROP_SIZE:
231         priv->old_icon_size = priv->icon_size;
232         priv->icon_size = g_value_get_enum (value);
233         break;
234       default:
235         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
236     }
237 }
238
239 static void
240 gtk_cell_renderer_spinner_get_size (GtkCellRenderer *cellr,
241                                     GtkWidget *widget,
242                                     GdkRectangle *cell_area,
243                                     gint *x_offset,
244                                     gint *y_offset,
245                                     gint *width,
246                                     gint *height)
247 {
248   GtkCellRendererSpinner *cell = GTK_CELL_RENDERER_SPINNER (cellr);
249   GtkCellRendererSpinnerPrivate *priv = cell->priv;
250   gdouble align;
251   gint w, h;
252   gint xpad, ypad;
253   gfloat xalign, yalign;
254   gboolean rtl;
255
256   rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
257
258   gtk_cell_renderer_spinner_update_size (cell, widget);
259
260   g_object_get (cellr,
261                 "xpad", &xpad,
262                 "ypad", &ypad,
263                 "xalign", &xalign,
264                 "yalign", &yalign,
265                 NULL);
266   w = h = priv->size;
267
268   if (cell_area)
269     {
270       if (x_offset)
271         {
272           align = rtl ? 1.0 - xalign : xalign;
273           *x_offset = align * (cell_area->width - w);
274           *x_offset = MAX (*x_offset, 0);
275         }
276       if (y_offset)
277         {
278           align = rtl ? 1.0 - yalign : yalign;
279           *y_offset = align * (cell_area->height - h);
280           *y_offset = MAX (*y_offset, 0);
281         }
282     }
283   else
284     {
285       if (x_offset)
286         *x_offset = 0;
287       if (y_offset)
288         *y_offset = 0;
289     }
290
291   if (width)
292     *width = w;
293   if (height)
294     *height = h;
295 }
296
297 static void
298 gtk_cell_renderer_spinner_render (GtkCellRenderer *cellr,
299                                   GdkWindow *window,
300                                   GtkWidget *widget,
301                                   GdkRectangle *background_area,
302                                   GdkRectangle *cell_area,
303                                   GdkRectangle *expose_area,
304                                   guint flags)
305 {
306   GtkCellRendererSpinner *cell = GTK_CELL_RENDERER_SPINNER (cellr);
307   GtkCellRendererSpinnerPrivate *priv = cell->priv;
308   GtkStateType state;
309   GdkRectangle pix_rect;
310   GdkRectangle draw_rect;
311   gint xpad, ypad;
312
313   if (!priv->active)
314     return;
315
316   gtk_cell_renderer_spinner_get_size (cellr, widget, cell_area,
317                                       &pix_rect.x, &pix_rect.y,
318                                       &pix_rect.width, &pix_rect.height);
319
320   g_object_get (cellr,
321                 "xpad", &xpad,
322                 "ypad", &ypad,
323                 NULL);
324   pix_rect.x += cell_area->x + xpad;
325   pix_rect.y += cell_area->y + ypad;
326   pix_rect.width -= xpad * 2;
327   pix_rect.height -= ypad * 2;
328
329   if (!gdk_rectangle_intersect (cell_area, &pix_rect, &draw_rect) ||
330       !gdk_rectangle_intersect (expose_area, &pix_rect, &draw_rect))
331     {
332       return;
333     }
334
335   state = GTK_STATE_NORMAL;
336   if (GTK_WIDGET_STATE (widget) == GTK_STATE_INSENSITIVE || !cellr->sensitive)
337     {
338       state = GTK_STATE_INSENSITIVE;
339     }
340   else
341     {
342       if ((flags & GTK_CELL_RENDERER_SELECTED) != 0)
343         {
344           if (GTK_WIDGET_HAS_FOCUS (widget))
345             state = GTK_STATE_SELECTED;
346           else
347             state = GTK_STATE_ACTIVE;
348         }
349       else
350         state = GTK_STATE_PRELIGHT;
351     }
352
353   gtk_paint_spinner (widget->style,
354                      window,
355                      state,
356                      priv->pulse,
357                      draw_rect.x, draw_rect.y,
358                      draw_rect.width, draw_rect.height);
359 }
360
361 #define __GTK_CELL_RENDERER_SPINNER_C__
362 #include "gtkaliasdef.c"