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