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