]> Pileus Git - ~andy/gtk/blob - gtk/gtkcellrendererspin.c
Move gtkstyle and gtkrc to deprecated/
[~andy/gtk] / gtk / gtkcellrendererspin.c
1 /* GtkCellRendererSpin
2  * Copyright (C) 2004 Lorenzo Gil Sanchez
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  *
19  * Authors: Lorenzo Gil Sanchez    <lgs@sicem.biz>
20  *          Carlos Garnacho Parro  <carlosg@gnome.org>
21  */
22
23 #include "config.h"
24
25 #include "gtkcellrendererspin.h"
26
27 #include "gtkintl.h"
28 #include "gtkprivate.h"
29 #include "gtkspinbutton.h"
30
31
32 /**
33  * SECTION:gtkcellrendererspin
34  * @Short_description: Renders a spin button in a cell
35  * @Title: GtkCellRendererSpin
36  * @See_also: #GtkCellRendererText, #GtkSpinButton
37  *
38  * #GtkCellRendererSpin renders text in a cell like #GtkCellRendererText from
39  * which it is derived. But while #GtkCellRendererText offers a simple entry to
40  * edit the text, #GtkCellRendererSpin offers a #GtkSpinButton widget. Of course,
41  * that means that the text has to be parseable as a floating point number.
42  *
43  * The range of the spinbutton is taken from the adjustment property of the
44  * cell renderer, which can be set explicitly or mapped to a column in the
45  * tree model, like all properties of cell renders. #GtkCellRendererSpin
46  * also has properties for the #GtkCellRendererSpin:climb-rate and the number
47  * of #GtkCellRendererSpin:digits to display. Other #GtkSpinButton properties
48  * can be set in a handler for the #GtkCellRenderer::editing-started signal.
49  *
50  * The #GtkCellRendererSpin cell renderer was added in GTK+ 2.10.
51  */
52
53
54 struct _GtkCellRendererSpinPrivate
55 {
56   GtkAdjustment *adjustment;
57   gdouble climb_rate;
58   guint   digits;
59 };
60
61 static void gtk_cell_renderer_spin_finalize   (GObject                  *object);
62
63 static void gtk_cell_renderer_spin_get_property (GObject      *object,
64                                                  guint         prop_id,
65                                                  GValue       *value,
66                                                  GParamSpec   *spec);
67 static void gtk_cell_renderer_spin_set_property (GObject      *object,
68                                                  guint         prop_id,
69                                                  const GValue *value,
70                                                  GParamSpec   *spec);
71
72 static GtkCellEditable * gtk_cell_renderer_spin_start_editing (GtkCellRenderer     *cell,
73                                                                GdkEvent            *event,
74                                                                GtkWidget           *widget,
75                                                                const gchar         *path,
76                                                                const GdkRectangle  *background_area,
77                                                                const GdkRectangle  *cell_area,
78                                                                GtkCellRendererState flags);
79 enum {
80   PROP_0,
81   PROP_ADJUSTMENT,
82   PROP_CLIMB_RATE,
83   PROP_DIGITS
84 };
85
86 #define GTK_CELL_RENDERER_SPIN_PATH "gtk-cell-renderer-spin-path"
87
88 G_DEFINE_TYPE (GtkCellRendererSpin, gtk_cell_renderer_spin, GTK_TYPE_CELL_RENDERER_TEXT)
89
90
91 static void
92 gtk_cell_renderer_spin_class_init (GtkCellRendererSpinClass *klass)
93 {
94   GObjectClass *object_class = G_OBJECT_CLASS (klass);
95   GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);
96
97   object_class->finalize     = gtk_cell_renderer_spin_finalize;
98   object_class->get_property = gtk_cell_renderer_spin_get_property;
99   object_class->set_property = gtk_cell_renderer_spin_set_property;
100
101   cell_class->start_editing  = gtk_cell_renderer_spin_start_editing;
102
103   /**
104    * GtkCellRendererSpin:adjustment:
105    *
106    * The adjustment that holds the value of the spinbutton. 
107    * This must be non-%NULL for the cell renderer to be editable.
108    *
109    * Since: 2.10
110    */
111   g_object_class_install_property (object_class,
112                                    PROP_ADJUSTMENT,
113                                    g_param_spec_object ("adjustment",
114                                                         P_("Adjustment"),
115                                                         P_("The adjustment that holds the value of the spin button"),
116                                                         GTK_TYPE_ADJUSTMENT,
117                                                         GTK_PARAM_READWRITE));
118
119
120   /**
121    * GtkCellRendererSpin:climb-rate:
122    *
123    * The acceleration rate when you hold down a button.
124    *
125    * Since: 2.10
126    */
127   g_object_class_install_property (object_class,
128                                    PROP_CLIMB_RATE,
129                                    g_param_spec_double ("climb-rate",
130                                                         P_("Climb rate"),
131                                                         P_("The acceleration rate when you hold down a button"),
132                                                         0.0, G_MAXDOUBLE, 0.0,
133                                                         GTK_PARAM_READWRITE));  
134   /**
135    * GtkCellRendererSpin:digits:
136    *
137    * The number of decimal places to display.
138    *
139    * Since: 2.10
140    */
141   g_object_class_install_property (object_class,
142                                    PROP_DIGITS,
143                                    g_param_spec_uint ("digits",
144                                                       P_("Digits"),
145                                                       P_("The number of decimal places to display"),
146                                                       0, 20, 0,
147                                                       GTK_PARAM_READWRITE));  
148
149   g_type_class_add_private (object_class, sizeof (GtkCellRendererSpinPrivate));
150 }
151
152 static void
153 gtk_cell_renderer_spin_init (GtkCellRendererSpin *self)
154 {
155   GtkCellRendererSpinPrivate *priv;
156
157   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
158                                             GTK_TYPE_CELL_RENDERER_SPIN,
159                                             GtkCellRendererSpinPrivate);
160   priv = self->priv;
161
162   priv->adjustment = NULL;
163   priv->climb_rate = 0.0;
164   priv->digits = 0;
165 }
166
167 static void
168 gtk_cell_renderer_spin_finalize (GObject *object)
169 {
170   GtkCellRendererSpinPrivate *priv;
171
172   priv = GTK_CELL_RENDERER_SPIN (object)->priv;
173
174   if (priv && priv->adjustment)
175     g_object_unref (priv->adjustment);
176
177   G_OBJECT_CLASS (gtk_cell_renderer_spin_parent_class)->finalize (object);
178 }
179
180 static void
181 gtk_cell_renderer_spin_get_property (GObject      *object,
182                                      guint         prop_id,
183                                      GValue       *value,
184                                      GParamSpec   *pspec)
185 {
186   GtkCellRendererSpin *renderer;
187   GtkCellRendererSpinPrivate *priv;
188
189   renderer = GTK_CELL_RENDERER_SPIN (object);
190   priv = renderer->priv;
191
192   switch (prop_id)
193     {
194     case PROP_ADJUSTMENT:
195       g_value_set_object (value, priv->adjustment);
196       break;
197     case PROP_CLIMB_RATE:
198       g_value_set_double (value, priv->climb_rate);
199       break;
200     case PROP_DIGITS:
201       g_value_set_uint (value, priv->digits);
202       break;
203     default:
204       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
205       break;
206     }
207 }
208
209 static void
210 gtk_cell_renderer_spin_set_property (GObject      *object,
211                                      guint         prop_id,
212                                      const GValue *value,
213                                      GParamSpec   *pspec)
214 {
215   GtkCellRendererSpin *renderer;
216   GtkCellRendererSpinPrivate *priv;
217   GObject *obj;
218
219   renderer = GTK_CELL_RENDERER_SPIN (object);
220   priv = renderer->priv;
221
222   switch (prop_id)
223     {
224     case PROP_ADJUSTMENT:
225       obj = g_value_get_object (value);
226
227       if (priv->adjustment)
228         {
229           g_object_unref (priv->adjustment);
230           priv->adjustment = NULL;
231         }
232
233       if (obj)
234         priv->adjustment = g_object_ref_sink (obj);
235       break;
236     case PROP_CLIMB_RATE:
237       priv->climb_rate = g_value_get_double (value);
238       break;
239     case PROP_DIGITS:
240       priv->digits = g_value_get_uint (value);
241       break;
242     default:
243       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
244       break;
245     }
246 }
247
248 static gboolean
249 gtk_cell_renderer_spin_focus_out_event (GtkWidget *widget,
250                                         GdkEvent  *event,
251                                         gpointer   data)
252 {
253   const gchar *path;
254   const gchar *new_text;
255   gboolean canceled;
256
257   g_object_get (widget,
258                 "editing-canceled", &canceled,
259                 NULL);
260
261   g_signal_handlers_disconnect_by_func (widget,
262                                         gtk_cell_renderer_spin_focus_out_event,
263                                         data);
264
265   gtk_cell_renderer_stop_editing (GTK_CELL_RENDERER (data), canceled);
266
267   if (!canceled)
268     {
269       path = g_object_get_data (G_OBJECT (widget), GTK_CELL_RENDERER_SPIN_PATH);
270
271       new_text = gtk_entry_get_text (GTK_ENTRY (widget));
272       g_signal_emit_by_name (data, "edited", path, new_text);
273     }
274   
275   return FALSE;
276 }
277
278 static gboolean
279 gtk_cell_renderer_spin_key_press_event (GtkWidget   *widget,
280                                         GdkEventKey *event,
281                                         gpointer     data)
282 {
283   if (event->state == 0)
284     {
285       if (event->keyval == GDK_KEY_Up)
286         {
287           gtk_spin_button_spin (GTK_SPIN_BUTTON (widget), GTK_SPIN_STEP_FORWARD, 1);
288           return TRUE;
289         }
290       else if (event->keyval == GDK_KEY_Down)
291         {
292           gtk_spin_button_spin (GTK_SPIN_BUTTON (widget), GTK_SPIN_STEP_BACKWARD, 1);
293           return TRUE;
294         }
295     }
296
297   return FALSE;
298 }
299
300 static gboolean
301 gtk_cell_renderer_spin_button_press_event (GtkWidget      *widget,
302                                            GdkEventButton *event,
303                                            gpointer        user_data)
304 {
305   /* Block 2BUTTON and 3BUTTON here, so that they won't be eaten
306    * by tree view.
307    */
308   if (event->type == GDK_2BUTTON_PRESS
309       || event->type == GDK_3BUTTON_PRESS)
310     return TRUE;
311
312   return FALSE;
313 }
314
315 static GtkCellEditable *
316 gtk_cell_renderer_spin_start_editing (GtkCellRenderer      *cell,
317                                       GdkEvent             *event,
318                                       GtkWidget            *widget,
319                                       const gchar          *path,
320                                       const GdkRectangle   *background_area,
321                                       const GdkRectangle   *cell_area,
322                                       GtkCellRendererState  flags)
323 {
324   GtkCellRendererSpinPrivate *priv;
325   GtkCellRendererText *cell_text;
326   GtkWidget *spin;
327   gboolean editable;
328   gchar *text;
329
330   cell_text = GTK_CELL_RENDERER_TEXT (cell);
331   priv = GTK_CELL_RENDERER_SPIN (cell)->priv;
332
333   g_object_get (cell_text, "editable", &editable, NULL);
334   if (!editable)
335     return NULL;
336
337   if (!priv->adjustment)
338     return NULL;
339
340   spin = gtk_spin_button_new (priv->adjustment,
341                               priv->climb_rate, priv->digits);
342
343   g_signal_connect (spin, "button-press-event",
344                     G_CALLBACK (gtk_cell_renderer_spin_button_press_event),
345                     NULL);
346
347   g_object_get (cell_text, "text", &text, NULL);
348   if (text)
349     {
350       gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin),
351                                  g_strtod (text, NULL));
352       g_free (text);
353     }
354
355   g_object_set_data_full (G_OBJECT (spin), GTK_CELL_RENDERER_SPIN_PATH,
356                           g_strdup (path), g_free);
357
358   g_signal_connect (G_OBJECT (spin), "focus-out-event",
359                     G_CALLBACK (gtk_cell_renderer_spin_focus_out_event),
360                     cell);
361   g_signal_connect (G_OBJECT (spin), "key-press-event",
362                     G_CALLBACK (gtk_cell_renderer_spin_key_press_event),
363                     cell);
364
365   gtk_widget_show (spin);
366
367   return GTK_CELL_EDITABLE (spin);
368 }
369
370 /**
371  * gtk_cell_renderer_spin_new:
372  *
373  * Creates a new #GtkCellRendererSpin. 
374  *
375  * Returns: a new #GtkCellRendererSpin
376  *
377  * Since: 2.10
378  */
379 GtkCellRenderer *
380 gtk_cell_renderer_spin_new (void)
381 {
382   return g_object_new (GTK_TYPE_CELL_RENDERER_SPIN, NULL);
383 }