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