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