]> Pileus Git - ~andy/gtk/blob - gtk/gtkcellrendererspin.c
Use g_strtod when converting strings for GtkCellRendererSpin
[~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 #include <gdk/gdkkeysyms.h>
25 #include "gtkintl.h"
26 #include "gtkprivate.h"
27 #include "gtkspinbutton.h"
28 #include "gtkcellrendererspin.h"
29
30
31 struct _GtkCellRendererSpinPrivate
32 {
33   GtkAdjustment *adjustment;
34   gdouble climb_rate;
35   guint   digits;
36 };
37
38 static void gtk_cell_renderer_spin_finalize   (GObject                  *object);
39
40 static void gtk_cell_renderer_spin_get_property (GObject      *object,
41                                                  guint         prop_id,
42                                                  GValue       *value,
43                                                  GParamSpec   *spec);
44 static void gtk_cell_renderer_spin_set_property (GObject      *object,
45                                                  guint         prop_id,
46                                                  const GValue *value,
47                                                  GParamSpec   *spec);
48
49 static GtkCellEditable * gtk_cell_renderer_spin_start_editing (GtkCellRenderer     *cell,
50                                                                GdkEvent            *event,
51                                                                GtkWidget           *widget,
52                                                                const gchar         *path,
53                                                                const GdkRectangle  *background_area,
54                                                                const GdkRectangle  *cell_area,
55                                                                GtkCellRendererState flags);
56 enum {
57   PROP_0,
58   PROP_ADJUSTMENT,
59   PROP_CLIMB_RATE,
60   PROP_DIGITS
61 };
62
63 #define GTK_CELL_RENDERER_SPIN_PATH "gtk-cell-renderer-spin-path"
64
65 G_DEFINE_TYPE (GtkCellRendererSpin, gtk_cell_renderer_spin, GTK_TYPE_CELL_RENDERER_TEXT)
66
67
68 static void
69 gtk_cell_renderer_spin_class_init (GtkCellRendererSpinClass *klass)
70 {
71   GObjectClass *object_class = G_OBJECT_CLASS (klass);
72   GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);
73
74   object_class->finalize     = gtk_cell_renderer_spin_finalize;
75   object_class->get_property = gtk_cell_renderer_spin_get_property;
76   object_class->set_property = gtk_cell_renderer_spin_set_property;
77
78   cell_class->start_editing  = gtk_cell_renderer_spin_start_editing;
79
80   /**
81    * GtkCellRendererSpin:adjustment:
82    *
83    * The adjustment that holds the value of the spinbutton. 
84    * This must be non-%NULL for the cell renderer to be editable.
85    *
86    * Since: 2.10
87    */
88   g_object_class_install_property (object_class,
89                                    PROP_ADJUSTMENT,
90                                    g_param_spec_object ("adjustment",
91                                                         P_("Adjustment"),
92                                                         P_("The adjustment that holds the value of the spin button"),
93                                                         GTK_TYPE_ADJUSTMENT,
94                                                         GTK_PARAM_READWRITE));
95
96
97   /**
98    * GtkCellRendererSpin:climb-rate:
99    *
100    * The acceleration rate when you hold down a button.
101    *
102    * Since: 2.10
103    */
104   g_object_class_install_property (object_class,
105                                    PROP_CLIMB_RATE,
106                                    g_param_spec_double ("climb-rate",
107                                                         P_("Climb rate"),
108                                                         P_("The acceleration rate when you hold down a button"),
109                                                         0.0, G_MAXDOUBLE, 0.0,
110                                                         GTK_PARAM_READWRITE));  
111   /**
112    * GtkCellRendererSpin:digits:
113    *
114    * The number of decimal places to display.
115    *
116    * Since: 2.10
117    */
118   g_object_class_install_property (object_class,
119                                    PROP_DIGITS,
120                                    g_param_spec_uint ("digits",
121                                                       P_("Digits"),
122                                                       P_("The number of decimal places to display"),
123                                                       0, 20, 0,
124                                                       GTK_PARAM_READWRITE));  
125
126   g_type_class_add_private (object_class, sizeof (GtkCellRendererSpinPrivate));
127 }
128
129 static void
130 gtk_cell_renderer_spin_init (GtkCellRendererSpin *self)
131 {
132   GtkCellRendererSpinPrivate *priv;
133
134   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
135                                             GTK_TYPE_CELL_RENDERER_SPIN,
136                                             GtkCellRendererSpinPrivate);
137   priv = self->priv;
138
139   priv->adjustment = NULL;
140   priv->climb_rate = 0.0;
141   priv->digits = 0;
142 }
143
144 static void
145 gtk_cell_renderer_spin_finalize (GObject *object)
146 {
147   GtkCellRendererSpinPrivate *priv;
148
149   priv = GTK_CELL_RENDERER_SPIN (object)->priv;
150
151   if (priv && priv->adjustment)
152     g_object_unref (priv->adjustment);
153
154   G_OBJECT_CLASS (gtk_cell_renderer_spin_parent_class)->finalize (object);
155 }
156
157 static void
158 gtk_cell_renderer_spin_get_property (GObject      *object,
159                                      guint         prop_id,
160                                      GValue       *value,
161                                      GParamSpec   *pspec)
162 {
163   GtkCellRendererSpin *renderer;
164   GtkCellRendererSpinPrivate *priv;
165
166   renderer = GTK_CELL_RENDERER_SPIN (object);
167   priv = renderer->priv;
168
169   switch (prop_id)
170     {
171     case PROP_ADJUSTMENT:
172       g_value_set_object (value, priv->adjustment);
173       break;
174     case PROP_CLIMB_RATE:
175       g_value_set_double (value, priv->climb_rate);
176       break;
177     case PROP_DIGITS:
178       g_value_set_uint (value, priv->digits);
179       break;
180     default:
181       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
182       break;
183     }
184 }
185
186 static void
187 gtk_cell_renderer_spin_set_property (GObject      *object,
188                                      guint         prop_id,
189                                      const GValue *value,
190                                      GParamSpec   *pspec)
191 {
192   GtkCellRendererSpin *renderer;
193   GtkCellRendererSpinPrivate *priv;
194   GObject *obj;
195
196   renderer = GTK_CELL_RENDERER_SPIN (object);
197   priv = renderer->priv;
198
199   switch (prop_id)
200     {
201     case PROP_ADJUSTMENT:
202       obj = g_value_get_object (value);
203
204       if (priv->adjustment)
205         {
206           g_object_unref (priv->adjustment);
207           priv->adjustment = NULL;
208         }
209
210       if (obj)
211         priv->adjustment = g_object_ref_sink (obj);
212       break;
213     case PROP_CLIMB_RATE:
214       priv->climb_rate = g_value_get_double (value);
215       break;
216     case PROP_DIGITS:
217       priv->digits = g_value_get_uint (value);
218       break;
219     default:
220       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
221       break;
222     }
223 }
224
225 static gboolean
226 gtk_cell_renderer_spin_focus_out_event (GtkWidget *widget,
227                                         GdkEvent  *event,
228                                         gpointer   data)
229 {
230   const gchar *path;
231   const gchar *new_text;
232   gboolean canceled;
233
234   g_object_get (widget,
235                 "editing-canceled", &canceled,
236                 NULL);
237
238   g_signal_handlers_disconnect_by_func (widget,
239                                         gtk_cell_renderer_spin_focus_out_event,
240                                         data);
241
242   gtk_cell_renderer_stop_editing (GTK_CELL_RENDERER (data), canceled);
243
244   if (!canceled)
245     {
246       path = g_object_get_data (G_OBJECT (widget), GTK_CELL_RENDERER_SPIN_PATH);
247
248       new_text = gtk_entry_get_text (GTK_ENTRY (widget));
249       g_signal_emit_by_name (data, "edited", path, new_text);
250     }
251   
252   return FALSE;
253 }
254
255 static gboolean
256 gtk_cell_renderer_spin_key_press_event (GtkWidget   *widget,
257                                         GdkEventKey *event,
258                                         gpointer     data)
259 {
260   if (event->state == 0)
261     {
262       if (event->keyval == GDK_KEY_Up)
263         {
264           gtk_spin_button_spin (GTK_SPIN_BUTTON (widget), GTK_SPIN_STEP_FORWARD, 1);
265           return TRUE;
266         }
267       else if (event->keyval == GDK_KEY_Down)
268         {
269           gtk_spin_button_spin (GTK_SPIN_BUTTON (widget), GTK_SPIN_STEP_BACKWARD, 1);
270           return TRUE;
271         }
272     }
273
274   return FALSE;
275 }
276
277 static gboolean
278 gtk_cell_renderer_spin_button_press_event (GtkWidget      *widget,
279                                            GdkEventButton *event,
280                                            gpointer        user_data)
281 {
282   /* Block 2BUTTON and 3BUTTON here, so that they won't be eaten
283    * by tree view.
284    */
285   if (event->type == GDK_2BUTTON_PRESS
286       || event->type == GDK_3BUTTON_PRESS)
287     return TRUE;
288
289   return FALSE;
290 }
291
292 static GtkCellEditable *
293 gtk_cell_renderer_spin_start_editing (GtkCellRenderer      *cell,
294                                       GdkEvent             *event,
295                                       GtkWidget            *widget,
296                                       const gchar          *path,
297                                       const GdkRectangle   *background_area,
298                                       const GdkRectangle   *cell_area,
299                                       GtkCellRendererState  flags)
300 {
301   GtkCellRendererSpinPrivate *priv;
302   GtkCellRendererText *cell_text;
303   GtkWidget *spin;
304   gboolean editable;
305   gchar *text;
306
307   cell_text = GTK_CELL_RENDERER_TEXT (cell);
308   priv = GTK_CELL_RENDERER_SPIN (cell)->priv;
309
310   g_object_get (cell_text, "editable", &editable, NULL);
311   if (!editable)
312     return NULL;
313
314   if (!priv->adjustment)
315     return NULL;
316
317   spin = gtk_spin_button_new (priv->adjustment,
318                               priv->climb_rate, priv->digits);
319
320   g_signal_connect (spin, "button-press-event",
321                     G_CALLBACK (gtk_cell_renderer_spin_button_press_event),
322                     NULL);
323
324   g_object_get (cell_text, "text", &text, NULL);
325   if (text)
326     {
327       gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin),
328                                  g_strtod (text, NULL));
329       g_free (text);
330     }
331
332   g_object_set_data_full (G_OBJECT (spin), GTK_CELL_RENDERER_SPIN_PATH,
333                           g_strdup (path), g_free);
334
335   g_signal_connect (G_OBJECT (spin), "focus-out-event",
336                     G_CALLBACK (gtk_cell_renderer_spin_focus_out_event),
337                     cell);
338   g_signal_connect (G_OBJECT (spin), "key-press-event",
339                     G_CALLBACK (gtk_cell_renderer_spin_key_press_event),
340                     cell);
341
342   gtk_widget_show (spin);
343
344   return GTK_CELL_EDITABLE (spin);
345 }
346
347 /**
348  * gtk_cell_renderer_spin_new:
349  *
350  * Creates a new #GtkCellRendererSpin. 
351  *
352  * Returns: a new #GtkCellRendererSpin
353  *
354  * Since: 2.10
355  */
356 GtkCellRenderer *
357 gtk_cell_renderer_spin_new (void)
358 {
359   return g_object_new (GTK_TYPE_CELL_RENDERER_SPIN, NULL);
360 }