source: trunk/third/gtk/gtk/gtkspinbutton.c @ 15781

Revision 15781, 43.9 KB checked in by ghudson, 24 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r15780, which included commits to RCS files with non-trunk default branches.
Line 
1/* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3 *
4 * GtkSpinButton widget for GTK+
5 * Copyright (C) 1998 Lars Hamann and Stefan Jeske
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library 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 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
21 */
22
23/*
24 * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
25 * file for a list of people on the GTK+ Team.  See the ChangeLog
26 * files for a list of changes.  These files are distributed with
27 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
28 */
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <math.h>
33#include <string.h>
34#include <locale.h>
35#include "gdk/gdkkeysyms.h"
36#include "gtkspinbutton.h"
37#include "gtkmain.h"
38#include "gtksignal.h"
39
40
41#define MIN_SPIN_BUTTON_WIDTH              30
42#define ARROW_SIZE                         11
43#define SPIN_BUTTON_INITIAL_TIMER_DELAY    200
44#define SPIN_BUTTON_TIMER_DELAY            20
45#define MAX_TEXT_LENGTH                    256
46#define MAX_TIMER_CALLS                    5
47#define EPSILON                            1e-5
48
49enum {
50  ARG_0,
51  ARG_ADJUSTMENT,
52  ARG_CLIMB_RATE,
53  ARG_DIGITS,
54  ARG_SNAP_TO_TICKS,
55  ARG_NUMERIC,
56  ARG_WRAP,
57  ARG_UPDATE_POLICY,
58  ARG_SHADOW_TYPE,
59  ARG_VALUE
60};
61
62
63static void gtk_spin_button_class_init     (GtkSpinButtonClass *klass);
64static void gtk_spin_button_init           (GtkSpinButton      *spin_button);
65static void gtk_spin_button_finalize       (GtkObject          *object);
66static void gtk_spin_button_set_arg        (GtkObject          *object,
67                                            GtkArg             *arg,
68                                            guint               arg_id);
69static void gtk_spin_button_get_arg        (GtkObject          *object,
70                                            GtkArg             *arg,
71                                            guint               arg_id);
72static void gtk_spin_button_map            (GtkWidget          *widget);
73static void gtk_spin_button_unmap          (GtkWidget          *widget);
74static void gtk_spin_button_realize        (GtkWidget          *widget);
75static void gtk_spin_button_unrealize      (GtkWidget          *widget);
76static void gtk_spin_button_size_request   (GtkWidget          *widget,
77                                            GtkRequisition     *requisition);
78static void gtk_spin_button_size_allocate  (GtkWidget          *widget,
79                                            GtkAllocation      *allocation);
80static void gtk_spin_button_paint          (GtkWidget          *widget,
81                                            GdkRectangle       *area);
82static void gtk_spin_button_draw           (GtkWidget          *widget,
83                                            GdkRectangle       *area);
84static gint gtk_spin_button_expose         (GtkWidget          *widget,
85                                            GdkEventExpose     *event);
86static gint gtk_spin_button_button_press   (GtkWidget          *widget,
87                                            GdkEventButton     *event);
88static gint gtk_spin_button_button_release (GtkWidget          *widget,
89                                            GdkEventButton     *event);
90static gint gtk_spin_button_motion_notify  (GtkWidget          *widget,
91                                            GdkEventMotion     *event);
92static gint gtk_spin_button_enter_notify   (GtkWidget          *widget,
93                                            GdkEventCrossing   *event);
94static gint gtk_spin_button_leave_notify   (GtkWidget          *widget,
95                                            GdkEventCrossing   *event);
96static gint gtk_spin_button_focus_out      (GtkWidget          *widget,
97                                            GdkEventFocus      *event);
98static void gtk_spin_button_draw_arrow     (GtkSpinButton      *spin_button,
99                                            guint               arrow);
100static gint gtk_spin_button_timer          (GtkSpinButton      *spin_button);
101static void gtk_spin_button_value_changed  (GtkAdjustment      *adjustment,
102                                            GtkSpinButton      *spin_button);
103static gint gtk_spin_button_key_press      (GtkWidget          *widget,
104                                            GdkEventKey        *event);
105static gint gtk_spin_button_key_release    (GtkWidget          *widget,
106                                            GdkEventKey        *event);
107static void gtk_spin_button_activate       (GtkEditable        *editable);
108static void gtk_spin_button_snap           (GtkSpinButton      *spin_button,
109                                            gfloat              val);
110static void gtk_spin_button_insert_text    (GtkEditable        *editable,
111                                            const gchar        *new_text,
112                                            gint                new_text_length,
113                                            gint               *position);
114static void gtk_spin_button_real_spin      (GtkSpinButton      *spin_button,
115                                            gfloat              step);
116
117
118static GtkEntryClass *parent_class = NULL;
119
120
121GtkType
122gtk_spin_button_get_type (void)
123{
124  static guint spin_button_type = 0;
125
126  if (!spin_button_type)
127    {
128      static const GtkTypeInfo spin_button_info =
129      {
130        "GtkSpinButton",
131        sizeof (GtkSpinButton),
132        sizeof (GtkSpinButtonClass),
133        (GtkClassInitFunc) gtk_spin_button_class_init,
134        (GtkObjectInitFunc) gtk_spin_button_init,
135        /* reserved_1 */ NULL,
136        /* reserved_2 */ NULL,
137        (GtkClassInitFunc) NULL,
138      };
139
140      spin_button_type = gtk_type_unique (GTK_TYPE_ENTRY, &spin_button_info);
141    }
142  return spin_button_type;
143}
144
145static void
146gtk_spin_button_class_init (GtkSpinButtonClass *class)
147{
148  GtkObjectClass   *object_class;
149  GtkWidgetClass   *widget_class;
150  GtkEditableClass *editable_class;
151
152  object_class   = (GtkObjectClass*)   class;
153  widget_class   = (GtkWidgetClass*)   class;
154  editable_class = (GtkEditableClass*) class;
155
156  parent_class = gtk_type_class (GTK_TYPE_ENTRY);
157
158  gtk_object_add_arg_type ("GtkSpinButton::adjustment",
159                           GTK_TYPE_ADJUSTMENT,
160                           GTK_ARG_READWRITE,
161                           ARG_ADJUSTMENT);
162  gtk_object_add_arg_type ("GtkSpinButton::climb_rate",
163                           GTK_TYPE_FLOAT,
164                           GTK_ARG_READWRITE,
165                           ARG_CLIMB_RATE);
166  gtk_object_add_arg_type ("GtkSpinButton::digits",
167                           GTK_TYPE_UINT,
168                           GTK_ARG_READWRITE,
169                           ARG_DIGITS);
170  gtk_object_add_arg_type ("GtkSpinButton::snap_to_ticks",
171                           GTK_TYPE_BOOL,
172                           GTK_ARG_READWRITE,
173                           ARG_SNAP_TO_TICKS);
174  gtk_object_add_arg_type ("GtkSpinButton::numeric",
175                           GTK_TYPE_BOOL,
176                           GTK_ARG_READWRITE,
177                           ARG_NUMERIC);
178  gtk_object_add_arg_type ("GtkSpinButton::wrap",
179                           GTK_TYPE_BOOL,
180                           GTK_ARG_READWRITE,
181                           ARG_WRAP);
182  gtk_object_add_arg_type ("GtkSpinButton::update_policy",
183                           GTK_TYPE_SPIN_BUTTON_UPDATE_POLICY,
184                           GTK_ARG_READWRITE,
185                           ARG_UPDATE_POLICY);
186  gtk_object_add_arg_type ("GtkSpinButton::shadow_type",
187                           GTK_TYPE_SHADOW_TYPE,
188                           GTK_ARG_READWRITE,
189                           ARG_SHADOW_TYPE);
190  gtk_object_add_arg_type ("GtkSpinButton::value",
191                           GTK_TYPE_FLOAT,
192                           GTK_ARG_READWRITE,
193                           ARG_VALUE);
194 
195
196  object_class->set_arg = gtk_spin_button_set_arg;
197  object_class->get_arg = gtk_spin_button_get_arg;
198  object_class->finalize = gtk_spin_button_finalize;
199
200  widget_class->map = gtk_spin_button_map;
201  widget_class->unmap = gtk_spin_button_unmap;
202  widget_class->realize = gtk_spin_button_realize;
203  widget_class->unrealize = gtk_spin_button_unrealize;
204  widget_class->size_request = gtk_spin_button_size_request;
205  widget_class->size_allocate = gtk_spin_button_size_allocate;
206  widget_class->draw = gtk_spin_button_draw;
207  widget_class->expose_event = gtk_spin_button_expose;
208  widget_class->button_press_event = gtk_spin_button_button_press;
209  widget_class->button_release_event = gtk_spin_button_button_release;
210  widget_class->motion_notify_event = gtk_spin_button_motion_notify;
211  widget_class->key_press_event = gtk_spin_button_key_press;
212  widget_class->key_release_event = gtk_spin_button_key_release;
213  widget_class->enter_notify_event = gtk_spin_button_enter_notify;
214  widget_class->leave_notify_event = gtk_spin_button_leave_notify;
215  widget_class->focus_out_event = gtk_spin_button_focus_out;
216
217  editable_class->insert_text = gtk_spin_button_insert_text;
218  editable_class->activate = gtk_spin_button_activate;
219}
220
221static void
222gtk_spin_button_set_arg (GtkObject        *object,
223                         GtkArg           *arg,
224                         guint             arg_id)
225{
226  GtkSpinButton *spin_button;
227
228  spin_button = GTK_SPIN_BUTTON (object);
229 
230  switch (arg_id)
231    {
232      GtkAdjustment *adjustment;
233
234    case ARG_ADJUSTMENT:
235      adjustment = GTK_VALUE_POINTER (*arg);
236      if (!adjustment)
237        adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
238      gtk_spin_button_set_adjustment (spin_button, adjustment);
239      break;
240    case ARG_CLIMB_RATE:
241      gtk_spin_button_configure (spin_button,
242                                 spin_button->adjustment,
243                                 GTK_VALUE_FLOAT (*arg),
244                                 spin_button->digits);
245      break;
246    case ARG_DIGITS:
247      gtk_spin_button_configure (spin_button,
248                                 spin_button->adjustment,
249                                 spin_button->climb_rate,
250                                 GTK_VALUE_UINT (*arg));
251      break;
252    case ARG_SNAP_TO_TICKS:
253      gtk_spin_button_set_snap_to_ticks (spin_button, GTK_VALUE_BOOL (*arg));
254      break;
255    case ARG_NUMERIC:
256      gtk_spin_button_set_numeric (spin_button, GTK_VALUE_BOOL (*arg));
257      break;
258    case ARG_WRAP:
259      gtk_spin_button_set_wrap (spin_button, GTK_VALUE_BOOL (*arg));
260      break;
261    case ARG_UPDATE_POLICY:
262      gtk_spin_button_set_update_policy (spin_button, GTK_VALUE_ENUM (*arg));
263      break;
264    case ARG_SHADOW_TYPE:
265      gtk_spin_button_set_shadow_type (spin_button, GTK_VALUE_ENUM (*arg));
266      break;
267    case ARG_VALUE:
268      gtk_spin_button_set_value (spin_button, GTK_VALUE_FLOAT (*arg));
269      break;
270    default:
271      break;
272    }
273}
274
275static void
276gtk_spin_button_get_arg (GtkObject        *object,
277                         GtkArg           *arg,
278                         guint             arg_id)
279{
280  GtkSpinButton *spin_button;
281
282  spin_button = GTK_SPIN_BUTTON (object);
283 
284  switch (arg_id)
285    {
286    case ARG_ADJUSTMENT:
287      GTK_VALUE_POINTER (*arg) = spin_button->adjustment;
288      break;
289    case ARG_CLIMB_RATE:
290      GTK_VALUE_FLOAT (*arg) = spin_button->climb_rate;
291      break;
292    case ARG_DIGITS:
293      GTK_VALUE_UINT (*arg) = spin_button->digits;
294      break;
295    case ARG_SNAP_TO_TICKS:
296      GTK_VALUE_BOOL (*arg) = spin_button->snap_to_ticks;
297      break;
298    case ARG_NUMERIC:
299      GTK_VALUE_BOOL (*arg) = spin_button->numeric;
300      break;
301    case ARG_WRAP:
302      GTK_VALUE_BOOL (*arg) = spin_button->wrap;
303      break;
304    case ARG_UPDATE_POLICY:
305      GTK_VALUE_ENUM (*arg) = spin_button->update_policy;
306      break;
307    case ARG_SHADOW_TYPE:
308      GTK_VALUE_ENUM (*arg) = spin_button->shadow_type;
309      break;
310    case ARG_VALUE:
311      GTK_VALUE_FLOAT (*arg) = spin_button->adjustment->value;
312      break;
313    default:
314      arg->type = GTK_TYPE_INVALID;
315      break;
316    }
317}
318
319static void
320gtk_spin_button_init (GtkSpinButton *spin_button)
321{
322  spin_button->adjustment = NULL;
323  spin_button->panel = NULL;
324  spin_button->shadow_type = GTK_SHADOW_NONE;
325  spin_button->timer = 0;
326  spin_button->ev_time = 0;
327  spin_button->climb_rate = 0.0;
328  spin_button->timer_step = 0.0;
329  spin_button->update_policy = GTK_UPDATE_ALWAYS;
330  spin_button->in_child = 2;
331  spin_button->click_child = 2;
332  spin_button->button = 0;
333  spin_button->need_timer = FALSE;
334  spin_button->timer_calls = 0;
335  spin_button->digits = 0;
336  spin_button->numeric = FALSE;
337  spin_button->wrap = FALSE;
338  spin_button->snap_to_ticks = FALSE;
339
340  gtk_spin_button_set_adjustment (spin_button,
341                                  (GtkAdjustment*) gtk_adjustment_new (0, 0, 0, 0, 0, 0));
342}
343
344static void
345gtk_spin_button_finalize (GtkObject *object)
346{
347  g_return_if_fail (object != NULL);
348  g_return_if_fail (GTK_IS_SPIN_BUTTON (object));
349
350  gtk_object_unref (GTK_OBJECT (GTK_SPIN_BUTTON (object)->adjustment));
351 
352  GTK_OBJECT_CLASS (parent_class)->finalize (object);
353}
354
355static void
356gtk_spin_button_map (GtkWidget *widget)
357{
358  g_return_if_fail (widget != NULL);
359  g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
360
361  if (GTK_WIDGET_REALIZED (widget) && !GTK_WIDGET_MAPPED (widget))
362    {
363      GTK_WIDGET_CLASS (parent_class)->map (widget);
364      gdk_window_show (GTK_SPIN_BUTTON (widget)->panel);
365    }
366}
367
368static void
369gtk_spin_button_unmap (GtkWidget *widget)
370{
371  g_return_if_fail (widget != NULL);
372  g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
373
374  if (GTK_WIDGET_MAPPED (widget))
375    {
376      gdk_window_hide (GTK_SPIN_BUTTON (widget)->panel);
377      GTK_WIDGET_CLASS (parent_class)->unmap (widget);
378    }
379}
380
381static void
382gtk_spin_button_realize (GtkWidget *widget)
383{
384  GtkSpinButton *spin;
385  GdkWindowAttr attributes;
386  gint attributes_mask;
387  guint real_width;
388
389  g_return_if_fail (widget != NULL);
390  g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
391 
392  spin = GTK_SPIN_BUTTON (widget);
393
394  real_width = widget->allocation.width;
395  widget->allocation.width -= ARROW_SIZE + 2 * widget->style->klass->xthickness;
396  gtk_widget_set_events (widget, gtk_widget_get_events (widget) |
397                         GDK_KEY_RELEASE_MASK);
398  GTK_WIDGET_CLASS (parent_class)->realize (widget);
399
400  widget->allocation.width = real_width;
401 
402  attributes.window_type = GDK_WINDOW_CHILD;
403  attributes.wclass = GDK_INPUT_OUTPUT;
404  attributes.visual = gtk_widget_get_visual (widget);
405  attributes.colormap = gtk_widget_get_colormap (widget);
406  attributes.event_mask = gtk_widget_get_events (widget);
407  attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK
408    | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK
409    | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK;
410
411  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
412
413  attributes.x = (widget->allocation.x + widget->allocation.width - ARROW_SIZE -
414                  2 * widget->style->klass->xthickness);
415  attributes.y = widget->allocation.y + (widget->allocation.height -
416                                         widget->requisition.height) / 2;
417  attributes.width = ARROW_SIZE + 2 * widget->style->klass->xthickness;
418  attributes.height = widget->requisition.height;
419 
420  spin->panel = gdk_window_new (gtk_widget_get_parent_window (widget),
421                                &attributes, attributes_mask);
422  gdk_window_set_user_data (spin->panel, widget);
423
424  gtk_style_set_background (widget->style, spin->panel, GTK_STATE_NORMAL);
425}
426
427static void
428gtk_spin_button_unrealize (GtkWidget *widget)
429{
430  GtkSpinButton *spin;
431
432  g_return_if_fail (widget != NULL);
433  g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
434
435  spin = GTK_SPIN_BUTTON (widget);
436
437  GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
438
439  if (spin->panel)
440    {
441      gdk_window_set_user_data (spin->panel, NULL);
442      gdk_window_destroy (spin->panel);
443      spin->panel = NULL;
444    }
445}
446
447static void
448gtk_spin_button_size_request (GtkWidget      *widget,
449                              GtkRequisition *requisition)
450{
451  g_return_if_fail (widget != NULL);
452  g_return_if_fail (requisition != NULL);
453  g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
454
455  GTK_WIDGET_CLASS (parent_class)->size_request (widget, requisition);
456 
457  requisition->width = MIN_SPIN_BUTTON_WIDTH + ARROW_SIZE
458    + 2 * widget->style->klass->xthickness;
459}
460
461static void
462gtk_spin_button_size_allocate (GtkWidget     *widget,
463                               GtkAllocation *allocation)
464{
465  GtkAllocation child_allocation;
466
467  g_return_if_fail (widget != NULL);
468  g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
469  g_return_if_fail (allocation != NULL);
470
471  child_allocation = *allocation;
472  if (child_allocation.width > ARROW_SIZE + 2 * widget->style->klass->xthickness)
473    child_allocation.width -= ARROW_SIZE + 2 * widget->style->klass->xthickness;
474
475  GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, &child_allocation);
476
477  widget->allocation = *allocation;
478
479  if (GTK_WIDGET_REALIZED (widget))
480    {
481      child_allocation.width = ARROW_SIZE + 2 * widget->style->klass->xthickness;
482      child_allocation.height = widget->requisition.height; 
483      child_allocation.x = (allocation->x + allocation->width - ARROW_SIZE -
484                            2 * widget->style->klass->xthickness);
485      child_allocation.y = allocation->y + (allocation->height - widget->requisition.height) / 2;
486
487      gdk_window_move_resize (GTK_SPIN_BUTTON (widget)->panel,
488                              child_allocation.x,
489                              child_allocation.y,
490                              child_allocation.width,
491                              child_allocation.height);
492    }
493}
494
495static GtkShadowType
496gtk_spin_button_get_shadow_type (GtkSpinButton *spin_button)
497{
498  GtkWidget *widget = GTK_WIDGET (spin_button);
499 
500  GtkShadowType shadow_type =
501    gtk_style_get_prop_experimental (widget->style,
502                                     "GtkSpinButton::shadow_type", -1);
503
504  if (shadow_type != (GtkShadowType)-1)
505    return shadow_type;
506  else
507    return spin_button->shadow_type;
508}
509
510static void
511gtk_spin_button_paint (GtkWidget    *widget,
512                       GdkRectangle *area)
513{
514  GtkSpinButton *spin;
515  GtkShadowType shadow_type;
516
517  g_return_if_fail (widget != NULL);
518  g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
519
520  spin = GTK_SPIN_BUTTON (widget);
521  shadow_type = gtk_spin_button_get_shadow_type (spin);
522
523  if (GTK_WIDGET_DRAWABLE (widget))
524    {
525      if (shadow_type != GTK_SHADOW_NONE)
526        gtk_paint_box (widget->style, spin->panel,
527                       GTK_STATE_NORMAL, shadow_type,
528                       area, widget, "spinbutton",
529                       0, 0,
530                       ARROW_SIZE + 2 * widget->style->klass->xthickness,
531                       widget->requisition.height);
532      else
533        {
534          gdk_window_set_back_pixmap (spin->panel, NULL, TRUE);
535          gdk_window_clear_area (spin->panel, area->x, area->y, area->width, area->height);
536        }
537      gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
538      gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
539     
540      GTK_WIDGET_CLASS (parent_class)->draw (widget, area);
541    }
542}
543
544static void
545gtk_spin_button_draw (GtkWidget    *widget,
546                      GdkRectangle *area)
547{
548  g_return_if_fail (widget != NULL);
549  g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
550  g_return_if_fail (area != NULL);
551
552  if (GTK_WIDGET_DRAWABLE (widget))
553    gtk_spin_button_paint (widget, area);
554}
555
556static gint
557gtk_spin_button_expose (GtkWidget      *widget,
558                        GdkEventExpose *event)
559{
560  g_return_val_if_fail (widget != NULL, FALSE);
561  g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
562  g_return_val_if_fail (event != NULL, FALSE);
563
564  if (GTK_WIDGET_DRAWABLE (widget))
565    gtk_spin_button_paint (widget, &event->area);
566
567  return FALSE;
568}
569
570static void
571gtk_spin_button_draw_arrow (GtkSpinButton *spin_button,
572                            guint          arrow)
573{
574  GtkStateType state_type;
575  GtkShadowType shadow_type;
576  GtkShadowType spin_shadow_type;
577  GtkWidget *widget;
578  gint x;
579  gint y;
580
581  g_return_if_fail (spin_button != NULL);
582  g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
583 
584  widget = GTK_WIDGET (spin_button);
585
586  spin_shadow_type = gtk_spin_button_get_shadow_type (spin_button);
587
588  if (GTK_WIDGET_DRAWABLE (spin_button))
589    {
590      if (!spin_button->wrap &&
591          (((arrow == GTK_ARROW_UP &&
592          (spin_button->adjustment->upper - spin_button->adjustment->value
593           <= EPSILON))) ||
594          ((arrow == GTK_ARROW_DOWN &&
595          (spin_button->adjustment->value - spin_button->adjustment->lower
596           <= EPSILON)))))
597        {
598          shadow_type = GTK_SHADOW_ETCHED_IN;
599          state_type = GTK_STATE_NORMAL;
600        }
601      else
602        {
603          if (spin_button->in_child == arrow)
604            {
605              if (spin_button->click_child == arrow)
606                state_type = GTK_STATE_ACTIVE;
607              else
608                state_type = GTK_STATE_PRELIGHT;
609            }
610          else
611            state_type = GTK_STATE_NORMAL;
612         
613          if (spin_button->click_child == arrow)
614            shadow_type = GTK_SHADOW_IN;
615          else
616            shadow_type = GTK_SHADOW_OUT;
617        }
618      if (arrow == GTK_ARROW_UP)
619        {
620          if (spin_shadow_type != GTK_SHADOW_NONE)
621            {
622              x = widget->style->klass->xthickness;
623              y = widget->style->klass->ythickness;
624            }
625          else
626            {
627              x = widget->style->klass->xthickness - 1;
628              y = widget->style->klass->ythickness - 1;
629            }
630          gtk_paint_arrow (widget->style, spin_button->panel,
631                           state_type, shadow_type,
632                           NULL, widget, "spinbutton",
633                           arrow, TRUE,
634                           x, y, ARROW_SIZE, widget->requisition.height / 2
635                           - widget->style->klass->ythickness);
636        }
637      else
638        {
639          if (spin_shadow_type != GTK_SHADOW_NONE)
640            {
641              x = widget->style->klass->xthickness;
642              y = widget->requisition.height / 2;
643            }
644          else
645            {
646              x = widget->style->klass->xthickness - 1;
647              y = widget->requisition.height / 2 + 1;
648            }
649          gtk_paint_arrow (widget->style, spin_button->panel,
650                           state_type, shadow_type,
651                           NULL, widget, "spinbutton",
652                           arrow, TRUE,
653                           x, y, ARROW_SIZE, widget->requisition.height / 2
654                           - widget->style->klass->ythickness);
655        }
656    }
657}
658
659static gint
660gtk_spin_button_enter_notify (GtkWidget        *widget,
661                              GdkEventCrossing *event)
662{
663  GtkSpinButton *spin;
664
665  g_return_val_if_fail (widget != NULL, FALSE);
666  g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
667  g_return_val_if_fail (event != NULL, FALSE);
668
669  spin = GTK_SPIN_BUTTON (widget);
670
671  if (event->window == spin->panel)
672    {
673      gint x;
674      gint y;
675
676      gdk_window_get_pointer (spin->panel, &x, &y, NULL);
677
678      if (y <= widget->requisition.height / 2)
679        {
680          spin->in_child = GTK_ARROW_UP;
681          if (spin->click_child == 2)
682            gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
683        }
684      else
685        {
686          spin->in_child = GTK_ARROW_DOWN;
687          if (spin->click_child == 2)
688            gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
689        }
690    }
691  return FALSE;
692}
693
694static gint
695gtk_spin_button_leave_notify (GtkWidget        *widget,
696                              GdkEventCrossing *event)
697{
698  GtkSpinButton *spin;
699
700  g_return_val_if_fail (widget != NULL, FALSE);
701  g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
702  g_return_val_if_fail (event != NULL, FALSE);
703
704  spin = GTK_SPIN_BUTTON (widget);
705
706  if (event->window == spin->panel && spin->click_child == 2)
707    {
708      if (spin->in_child == GTK_ARROW_UP)
709        {
710          spin->in_child = 2;
711          gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
712        }
713      else
714        {
715          spin->in_child = 2;
716          gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
717        }
718    }
719  return FALSE;
720}
721
722static gint
723gtk_spin_button_focus_out (GtkWidget     *widget,
724                           GdkEventFocus *event)
725{
726  g_return_val_if_fail (widget != NULL, FALSE);
727  g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
728  g_return_val_if_fail (event != NULL, FALSE);
729
730  if (GTK_EDITABLE (widget)->editable)
731    gtk_spin_button_update (GTK_SPIN_BUTTON (widget));
732
733  return GTK_WIDGET_CLASS (parent_class)->focus_out_event (widget, event);
734}
735
736static gint
737gtk_spin_button_button_press (GtkWidget      *widget,
738                              GdkEventButton *event)
739{
740  GtkSpinButton *spin;
741
742  g_return_val_if_fail (widget != NULL, FALSE);
743  g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
744  g_return_val_if_fail (event != NULL, FALSE);
745
746  spin = GTK_SPIN_BUTTON (widget);
747
748  if (!spin->button)
749    {
750      if (event->window == spin->panel)
751        {
752          if (!GTK_WIDGET_HAS_FOCUS (widget))
753            gtk_widget_grab_focus (widget);
754          gtk_grab_add (widget);
755          spin->button = event->button;
756         
757          if (GTK_EDITABLE (widget)->editable)
758            gtk_spin_button_update (spin);
759         
760          if (event->y <= widget->requisition.height / 2)
761            {
762              spin->click_child = GTK_ARROW_UP;
763              if (event->button == 1)
764                {
765                 gtk_spin_button_real_spin (spin,
766                                            spin->adjustment->step_increment);
767                  if (!spin->timer)
768                    {
769                      spin->timer_step = spin->adjustment->step_increment;
770                      spin->need_timer = TRUE;
771                      spin->timer = gtk_timeout_add
772                        (SPIN_BUTTON_INITIAL_TIMER_DELAY,
773                         (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
774                    }
775                }
776              else if (event->button == 2)
777                {
778                 gtk_spin_button_real_spin (spin,
779                                            spin->adjustment->page_increment);
780                  if (!spin->timer)
781                    {
782                      spin->timer_step = spin->adjustment->page_increment;
783                      spin->need_timer = TRUE;
784                      spin->timer = gtk_timeout_add
785                        (SPIN_BUTTON_INITIAL_TIMER_DELAY,
786                         (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
787                    }
788                }
789              gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
790            }
791          else
792            {
793              spin->click_child = GTK_ARROW_DOWN;
794              if (event->button == 1)
795                {
796                  gtk_spin_button_real_spin (spin,
797                                             -spin->adjustment->step_increment);
798                  if (!spin->timer)
799                    {
800                      spin->timer_step = spin->adjustment->step_increment;
801                      spin->need_timer = TRUE;
802                      spin->timer = gtk_timeout_add
803                        (SPIN_BUTTON_INITIAL_TIMER_DELAY,
804                         (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
805                    }
806                }     
807              else if (event->button == 2)
808                {
809                  gtk_spin_button_real_spin (spin,
810                                             -spin->adjustment->page_increment);
811                  if (!spin->timer)
812                    {
813                      spin->timer_step = spin->adjustment->page_increment;
814                      spin->need_timer = TRUE;
815                      spin->timer = gtk_timeout_add
816                        (SPIN_BUTTON_INITIAL_TIMER_DELAY,
817                         (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
818                    }
819                }
820              gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
821            }
822        }
823      else
824        GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event);
825    }
826  return FALSE;
827}
828
829static gint
830gtk_spin_button_button_release (GtkWidget      *widget,
831                                GdkEventButton *event)
832{
833  GtkSpinButton *spin;
834
835  g_return_val_if_fail (widget != NULL, FALSE);
836  g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
837  g_return_val_if_fail (event != NULL, FALSE);
838
839  spin = GTK_SPIN_BUTTON (widget);
840
841  if (event->button == spin->button)
842    {
843      guint click_child;
844
845      if (spin->timer)
846        {
847          gtk_timeout_remove (spin->timer);
848          spin->timer = 0;
849          spin->timer_calls = 0;
850          spin->need_timer = FALSE;
851        }
852
853      if (event->button == 3)
854        {
855          if (event->y >= 0 && event->x >= 0 &&
856              event->y <= widget->requisition.height &&
857              event->x <= ARROW_SIZE + 2 * widget->style->klass->xthickness)
858            {
859              if (spin->click_child == GTK_ARROW_UP &&
860                  event->y <= widget->requisition.height / 2)
861                {
862                  gfloat diff;
863
864                  diff = spin->adjustment->upper - spin->adjustment->value;
865                  if (diff > EPSILON)
866                    gtk_spin_button_real_spin (spin, diff);
867                }
868              else if (spin->click_child == GTK_ARROW_DOWN &&
869                       event->y > widget->requisition.height / 2)
870                {
871                  gfloat diff;
872
873                  diff = spin->adjustment->value - spin->adjustment->lower;
874                  if (diff > EPSILON)
875                    gtk_spin_button_real_spin (spin, -diff);
876                }
877            }
878        }                 
879      gtk_grab_remove (widget);
880      click_child = spin->click_child;
881      spin->click_child = 2;
882      spin->button = 0;
883      gtk_spin_button_draw_arrow (spin, click_child);
884    }
885  else
886    GTK_WIDGET_CLASS (parent_class)->button_release_event (widget, event);
887
888  return FALSE;
889}
890
891static gint
892gtk_spin_button_motion_notify (GtkWidget      *widget,
893                               GdkEventMotion *event)
894{
895  GtkSpinButton *spin;
896
897  g_return_val_if_fail (widget != NULL, FALSE);
898  g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
899  g_return_val_if_fail (event != NULL, FALSE);
900
901  spin = GTK_SPIN_BUTTON (widget);
902 
903  if (spin->button)
904    return FALSE;
905
906  if (event->window == spin->panel)
907    {
908      gint y;
909
910      y = event->y;
911      if (event->is_hint)
912        gdk_window_get_pointer (spin->panel, NULL, &y, NULL);
913
914      if (y <= widget->requisition.height / 2 &&
915          spin->in_child == GTK_ARROW_DOWN)
916        {
917          spin->in_child = GTK_ARROW_UP;
918          gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
919          gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
920        }
921      else if (y > widget->requisition.height / 2 &&
922          spin->in_child == GTK_ARROW_UP)
923        {
924          spin->in_child = GTK_ARROW_DOWN;
925          gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
926          gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
927        }
928      return FALSE;
929    }
930         
931  return GTK_WIDGET_CLASS (parent_class)->motion_notify_event (widget, event);
932}
933
934static gint
935gtk_spin_button_timer (GtkSpinButton *spin_button)
936{
937  gboolean retval = FALSE;
938 
939  GDK_THREADS_ENTER ();
940
941  if (spin_button->timer)
942    {
943      if (spin_button->click_child == GTK_ARROW_UP)
944        gtk_spin_button_real_spin (spin_button, spin_button->timer_step);
945      else
946        gtk_spin_button_real_spin (spin_button, -spin_button->timer_step);
947
948      if (spin_button->need_timer)
949        {
950          spin_button->need_timer = FALSE;
951          spin_button->timer = gtk_timeout_add
952            (SPIN_BUTTON_TIMER_DELAY, (GtkFunction) gtk_spin_button_timer,
953             (gpointer) spin_button);
954        }
955      else
956        {
957          if (spin_button->climb_rate > 0.0 && spin_button->timer_step
958              < spin_button->adjustment->page_increment)
959            {
960              if (spin_button->timer_calls < MAX_TIMER_CALLS)
961                spin_button->timer_calls++;
962              else
963                {
964                  spin_button->timer_calls = 0;
965                  spin_button->timer_step += spin_button->climb_rate;
966                }
967            }
968          retval = TRUE;
969        }
970    }
971
972  GDK_THREADS_LEAVE ();
973
974  return retval;
975}
976
977static void
978gtk_spin_button_value_changed (GtkAdjustment *adjustment,
979                               GtkSpinButton *spin_button)
980{
981  char buf[MAX_TEXT_LENGTH];
982
983  g_return_if_fail (adjustment != NULL);
984  g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
985
986  sprintf (buf, "%0.*f", spin_button->digits, adjustment->value);
987  gtk_entry_set_text (GTK_ENTRY (spin_button), buf);
988
989  gtk_spin_button_draw_arrow (spin_button, GTK_ARROW_UP);
990  gtk_spin_button_draw_arrow (spin_button, GTK_ARROW_DOWN);
991}
992
993static gint
994gtk_spin_button_key_press (GtkWidget     *widget,
995                           GdkEventKey   *event)
996{
997  GtkSpinButton *spin;
998  gint key;
999  gboolean key_repeat = FALSE;
1000
1001  g_return_val_if_fail (widget != NULL, FALSE);
1002  g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
1003  g_return_val_if_fail (event != NULL, FALSE);
1004 
1005  spin = GTK_SPIN_BUTTON (widget);
1006  key = event->keyval;
1007
1008  key_repeat = (event->time == spin->ev_time);
1009
1010  if (GTK_EDITABLE (widget)->editable &&
1011      (key == GDK_Up || key == GDK_Down ||
1012       key == GDK_Page_Up || key == GDK_Page_Down))
1013    gtk_spin_button_update (spin);
1014
1015  switch (key)
1016    {
1017    case GDK_Up:
1018
1019      if (GTK_WIDGET_HAS_FOCUS (widget))
1020        {
1021          gtk_signal_emit_stop_by_name (GTK_OBJECT (widget),
1022                                        "key_press_event");
1023          if (!key_repeat)
1024            spin->timer_step = spin->adjustment->step_increment;
1025
1026         gtk_spin_button_real_spin (spin, spin->timer_step);
1027
1028          if (key_repeat)
1029            {
1030              if (spin->climb_rate > 0.0 && spin->timer_step
1031                  < spin->adjustment->page_increment)
1032                {
1033                  if (spin->timer_calls < MAX_TIMER_CALLS)
1034                    spin->timer_calls++;
1035                  else
1036                    {
1037                      spin->timer_calls = 0;
1038                      spin->timer_step += spin->climb_rate;
1039                    }
1040                }
1041            }
1042          return TRUE;
1043        }
1044      return FALSE;
1045
1046    case GDK_Down:
1047
1048      if (GTK_WIDGET_HAS_FOCUS (widget))
1049        {
1050          gtk_signal_emit_stop_by_name (GTK_OBJECT (widget),
1051                                        "key_press_event");
1052          if (!key_repeat)
1053            spin->timer_step = spin->adjustment->step_increment;
1054
1055         gtk_spin_button_real_spin (spin, -spin->timer_step);
1056
1057          if (key_repeat)
1058            {
1059              if (spin->climb_rate > 0.0 && spin->timer_step
1060                  < spin->adjustment->page_increment)
1061                {
1062                  if (spin->timer_calls < MAX_TIMER_CALLS)
1063                    spin->timer_calls++;
1064                  else
1065                    {
1066                      spin->timer_calls = 0;
1067                      spin->timer_step += spin->climb_rate;
1068                    }
1069                }
1070            }
1071          return TRUE;
1072        }
1073      return FALSE;
1074
1075    case GDK_Page_Up:
1076
1077      if (event->state & GDK_CONTROL_MASK)
1078        {
1079          gfloat diff = spin->adjustment->upper - spin->adjustment->value;
1080          if (diff > EPSILON)
1081            gtk_spin_button_real_spin (spin, diff);
1082        }
1083      else
1084        gtk_spin_button_real_spin (spin, spin->adjustment->page_increment);
1085      return TRUE;
1086
1087    case GDK_Page_Down:
1088
1089      if (event->state & GDK_CONTROL_MASK)
1090        {
1091          gfloat diff = spin->adjustment->value - spin->adjustment->lower;
1092          if (diff > EPSILON)
1093            gtk_spin_button_real_spin (spin, -diff);
1094        }
1095      else
1096        gtk_spin_button_real_spin (spin, -spin->adjustment->page_increment);
1097      return TRUE;
1098
1099    default:
1100      break;
1101    }
1102
1103  return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);
1104}
1105
1106static gint
1107gtk_spin_button_key_release (GtkWidget   *widget,
1108                             GdkEventKey *event)
1109{
1110  GtkSpinButton *spin;
1111
1112  g_return_val_if_fail (widget != NULL, FALSE);
1113  g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
1114 
1115  spin = GTK_SPIN_BUTTON (widget);
1116 
1117  spin->ev_time = event->time;
1118  return TRUE;
1119}
1120
1121static void
1122gtk_spin_button_snap (GtkSpinButton *spin_button,
1123                      gfloat         val)
1124{
1125  gfloat inc;
1126  gfloat tmp;
1127 
1128  inc = spin_button->adjustment->step_increment;
1129  tmp = (val - spin_button->adjustment->lower) / inc;
1130  if (tmp - floor (tmp) < ceil (tmp) - tmp)
1131    val = spin_button->adjustment->lower + floor (tmp) * inc;
1132  else
1133    val = spin_button->adjustment->lower + ceil (tmp) * inc;
1134
1135  if (fabs (val - spin_button->adjustment->value) > EPSILON)
1136    gtk_adjustment_set_value (spin_button->adjustment, val);
1137  else
1138    {
1139      char buf[MAX_TEXT_LENGTH];
1140
1141      sprintf (buf, "%0.*f", spin_button->digits,
1142               spin_button->adjustment->value);
1143      if (strcmp (buf, gtk_entry_get_text (GTK_ENTRY (spin_button))))
1144        gtk_entry_set_text (GTK_ENTRY (spin_button), buf);
1145    }
1146}
1147
1148void
1149gtk_spin_button_update (GtkSpinButton *spin_button)
1150{
1151  gfloat val;
1152  gchar *error = NULL;
1153
1154  g_return_if_fail (spin_button != NULL);
1155  g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1156
1157  val = strtod (gtk_entry_get_text (GTK_ENTRY (spin_button)), &error);
1158
1159  if (spin_button->update_policy == GTK_UPDATE_ALWAYS)
1160    {
1161      if (val < spin_button->adjustment->lower)
1162        val = spin_button->adjustment->lower;
1163      else if (val > spin_button->adjustment->upper)
1164        val = spin_button->adjustment->upper;
1165    }
1166  else if ((spin_button->update_policy == GTK_UPDATE_IF_VALID) &&
1167           (*error ||
1168           val < spin_button->adjustment->lower ||
1169           val > spin_button->adjustment->upper))
1170    {
1171      gtk_spin_button_value_changed (spin_button->adjustment, spin_button);
1172      return;
1173    }
1174
1175  if (spin_button->snap_to_ticks)
1176    gtk_spin_button_snap (spin_button, val);
1177  else
1178    {
1179      if (fabs (val - spin_button->adjustment->value) > EPSILON)
1180        gtk_adjustment_set_value (spin_button->adjustment, val);
1181      else
1182        {
1183          char buf[MAX_TEXT_LENGTH];
1184         
1185          sprintf (buf, "%0.*f", spin_button->digits,
1186                   spin_button->adjustment->value);
1187          if (strcmp (buf, gtk_entry_get_text (GTK_ENTRY (spin_button))))
1188            gtk_entry_set_text (GTK_ENTRY (spin_button), buf);
1189        }
1190    }
1191}
1192
1193static void
1194gtk_spin_button_activate (GtkEditable *editable)
1195{
1196  g_return_if_fail (editable != NULL);
1197  g_return_if_fail (GTK_IS_SPIN_BUTTON (editable));
1198
1199  if (editable->editable)
1200    gtk_spin_button_update (GTK_SPIN_BUTTON (editable));
1201}
1202
1203static void
1204gtk_spin_button_insert_text (GtkEditable *editable,
1205                             const gchar *new_text,
1206                             gint         new_text_length,
1207                             gint        *position)
1208{
1209  GtkEntry *entry;
1210  GtkSpinButton *spin;
1211 
1212  g_return_if_fail (editable != NULL);
1213  g_return_if_fail (GTK_IS_SPIN_BUTTON (editable));
1214
1215  entry = GTK_ENTRY (editable);
1216  spin  = GTK_SPIN_BUTTON (editable);
1217
1218  if (spin->numeric)
1219    {
1220      struct lconv *lc;
1221      gboolean sign;
1222      gint dotpos = -1;
1223      gint i;
1224      GdkWChar pos_sign;
1225      GdkWChar neg_sign;
1226      gint entry_length;
1227
1228      entry_length = entry->text_length;
1229
1230      lc = localeconv ();
1231
1232      if (*(lc->negative_sign))
1233        neg_sign = *(lc->negative_sign);
1234      else
1235        neg_sign = '-';
1236
1237      if (*(lc->positive_sign))
1238        pos_sign = *(lc->positive_sign);
1239      else
1240        pos_sign = '+';
1241
1242      for (sign=0, i=0; i<entry_length; i++)
1243        if ((entry->text[i] == neg_sign) ||
1244            (entry->text[i] == pos_sign))
1245          {
1246            sign = 1;
1247            break;
1248          }
1249
1250      if (sign && !(*position))
1251        return;
1252
1253      for (dotpos=-1, i=0; i<entry_length; i++)
1254        if (entry->text[i] == *(lc->decimal_point))
1255          {
1256            dotpos = i;
1257            break;
1258          }
1259
1260      if (dotpos > -1 && *position > dotpos &&
1261          (gint)spin->digits - entry_length
1262            + dotpos - new_text_length + 1 < 0)
1263        return;
1264
1265      for (i = 0; i < new_text_length; i++)
1266        {
1267          if (new_text[i] == neg_sign || new_text[i] == pos_sign)
1268            {
1269              if (sign || (*position) || i)
1270                return;
1271              sign = TRUE;
1272            }
1273          else if (new_text[i] == *(lc->decimal_point))
1274            {
1275              if (!spin->digits || dotpos > -1 ||
1276                  (new_text_length - 1 - i + entry_length
1277                    - *position > (gint)spin->digits))
1278                return;
1279              dotpos = *position + i;
1280            }
1281          else if (new_text[i] < 0x30 || new_text[i] > 0x39)
1282            return;
1283        }
1284    }
1285
1286  GTK_EDITABLE_CLASS (parent_class)->insert_text (editable, new_text,
1287                                                  new_text_length, position);
1288}
1289
1290static void
1291gtk_spin_button_real_spin (GtkSpinButton *spin_button,
1292                           gfloat         increment)
1293{
1294  GtkAdjustment *adj;
1295  gfloat new_value = 0.0;
1296
1297  g_return_if_fail (spin_button != NULL);
1298  g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1299 
1300  adj = spin_button->adjustment;
1301
1302  new_value = adj->value + increment;
1303
1304  if (increment > 0)
1305    {
1306      if (spin_button->wrap)
1307        {
1308          if (fabs (adj->value - adj->upper) < EPSILON)
1309            new_value = adj->lower;
1310          else if (new_value > adj->upper)
1311            new_value = adj->upper;
1312        }
1313      else
1314        new_value = MIN (new_value, adj->upper);
1315    }
1316  else if (increment < 0)
1317    {
1318      if (spin_button->wrap)
1319        {
1320          if (fabs (adj->value - adj->lower) < EPSILON)
1321            new_value = adj->upper;
1322          else if (new_value < adj->lower)
1323            new_value = adj->lower;
1324        }
1325      else
1326        new_value = MAX (new_value, adj->lower);
1327    }
1328
1329  if (fabs (new_value - adj->value) > EPSILON)
1330    gtk_adjustment_set_value (adj, new_value);
1331}
1332
1333
1334/***********************************************************
1335 ***********************************************************
1336 ***                  Public interface                   ***
1337 ***********************************************************
1338 ***********************************************************/
1339
1340
1341void
1342gtk_spin_button_configure (GtkSpinButton  *spin_button,
1343                           GtkAdjustment  *adjustment,
1344                           gfloat          climb_rate,
1345                           guint           digits)
1346{
1347  g_return_if_fail (spin_button != NULL);
1348  g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1349  g_return_if_fail (digits < 6);
1350
1351  if (adjustment)
1352    gtk_spin_button_set_adjustment (spin_button, adjustment);
1353  else
1354    adjustment = spin_button->adjustment;
1355
1356  spin_button->digits = digits;
1357  spin_button->climb_rate = climb_rate;
1358  gtk_adjustment_value_changed (adjustment);
1359}
1360
1361GtkWidget *
1362gtk_spin_button_new (GtkAdjustment *adjustment,
1363                     gfloat         climb_rate,
1364                     guint          digits)
1365{
1366  GtkSpinButton *spin;
1367
1368  if (adjustment)
1369    g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), NULL);
1370  g_return_val_if_fail (digits < 6, NULL);
1371
1372  spin = gtk_type_new (GTK_TYPE_SPIN_BUTTON);
1373
1374  gtk_spin_button_configure (spin, adjustment, climb_rate, digits);
1375
1376  return GTK_WIDGET (spin);
1377}
1378
1379/* Callback used when the spin button's adjustment changes.  We need to redraw
1380 * the arrows when the adjustment's range changes.
1381 */
1382static void
1383adjustment_changed_cb (GtkAdjustment *adjustment, gpointer data)
1384{
1385  GtkSpinButton *spin_button;
1386
1387  spin_button = GTK_SPIN_BUTTON (data);
1388
1389  gtk_spin_button_draw_arrow (spin_button, GTK_ARROW_UP);
1390  gtk_spin_button_draw_arrow (spin_button, GTK_ARROW_DOWN);
1391}
1392
1393void
1394gtk_spin_button_set_adjustment (GtkSpinButton *spin_button,
1395                                GtkAdjustment *adjustment)
1396{
1397  g_return_if_fail (spin_button != NULL);
1398  g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1399
1400  if (spin_button->adjustment != adjustment)
1401    {
1402      if (spin_button->adjustment)
1403        {
1404          gtk_signal_disconnect_by_data (GTK_OBJECT (spin_button->adjustment),
1405                                         (gpointer) spin_button);
1406          gtk_object_unref (GTK_OBJECT (spin_button->adjustment));
1407        }
1408      spin_button->adjustment = adjustment;
1409      if (adjustment)
1410        {
1411          gtk_object_ref (GTK_OBJECT (adjustment));
1412          gtk_object_sink (GTK_OBJECT (adjustment));
1413          gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
1414                              (GtkSignalFunc) gtk_spin_button_value_changed,
1415                              (gpointer) spin_button);
1416          gtk_signal_connect (GTK_OBJECT (adjustment), "changed",
1417                              (GtkSignalFunc) adjustment_changed_cb,
1418                              (gpointer) spin_button);
1419        }
1420    }
1421}
1422
1423GtkAdjustment *
1424gtk_spin_button_get_adjustment (GtkSpinButton *spin_button)
1425{
1426  g_return_val_if_fail (spin_button != NULL, NULL);
1427  g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), NULL);
1428
1429  return spin_button->adjustment;
1430}
1431
1432void
1433gtk_spin_button_set_digits (GtkSpinButton *spin_button,
1434                            guint          digits)
1435{
1436  g_return_if_fail (spin_button != NULL);
1437  g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1438  g_return_if_fail (digits < 6);
1439
1440  if (spin_button->digits != digits)
1441    {
1442      spin_button->digits = digits;
1443      gtk_spin_button_value_changed (spin_button->adjustment, spin_button);
1444    }
1445}
1446
1447gfloat
1448gtk_spin_button_get_value_as_float (GtkSpinButton *spin_button)
1449{
1450  g_return_val_if_fail (spin_button != NULL, 0.0);
1451  g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), 0.0);
1452
1453  return spin_button->adjustment->value;
1454}
1455
1456gint
1457gtk_spin_button_get_value_as_int (GtkSpinButton *spin_button)
1458{
1459  gfloat val;
1460
1461  g_return_val_if_fail (spin_button != NULL, 0);
1462  g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), 0);
1463
1464  val = spin_button->adjustment->value;
1465  if (val - floor (val) < ceil (val) - val)
1466    return floor (val);
1467  else
1468    return ceil (val);
1469}
1470
1471void
1472gtk_spin_button_set_value (GtkSpinButton *spin_button,
1473                           gfloat         value)
1474{
1475  g_return_if_fail (spin_button != NULL);
1476  g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1477
1478  if (fabs (value - spin_button->adjustment->value) > EPSILON)
1479    gtk_adjustment_set_value (spin_button->adjustment, value);
1480  else
1481    {
1482      char buf[MAX_TEXT_LENGTH];
1483
1484      sprintf (buf, "%0.*f", spin_button->digits,
1485               spin_button->adjustment->value);
1486      if (strcmp (buf, gtk_entry_get_text (GTK_ENTRY (spin_button))))
1487        gtk_entry_set_text (GTK_ENTRY (spin_button), buf);
1488    }
1489}
1490
1491void
1492gtk_spin_button_set_update_policy (GtkSpinButton             *spin_button,
1493                                   GtkSpinButtonUpdatePolicy  policy)
1494{
1495  g_return_if_fail (spin_button != NULL);
1496  g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1497
1498  spin_button->update_policy = policy;
1499}
1500
1501void
1502gtk_spin_button_set_numeric (GtkSpinButton  *spin_button,
1503                             gboolean        numeric)
1504{
1505  g_return_if_fail (spin_button != NULL);
1506  g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1507
1508  spin_button->numeric = (numeric != 0);
1509}
1510
1511void
1512gtk_spin_button_set_wrap (GtkSpinButton  *spin_button,
1513                          gboolean        wrap)
1514{
1515  g_return_if_fail (spin_button != NULL);
1516  g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1517
1518  spin_button->wrap = (wrap != 0);
1519}
1520
1521void
1522gtk_spin_button_set_shadow_type (GtkSpinButton *spin_button,
1523                                 GtkShadowType  shadow_type)
1524{
1525  g_return_if_fail (spin_button != NULL);
1526  g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1527
1528  if (shadow_type != spin_button->shadow_type)
1529    {
1530      spin_button->shadow_type = shadow_type;
1531      if (GTK_WIDGET_DRAWABLE (spin_button))
1532        gtk_widget_queue_draw (GTK_WIDGET (spin_button));
1533    }
1534}
1535
1536void
1537gtk_spin_button_set_snap_to_ticks (GtkSpinButton *spin_button,
1538                                   gboolean       snap_to_ticks)
1539{
1540  guint new_val;
1541
1542  g_return_if_fail (spin_button != NULL);
1543  g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1544
1545  new_val = (snap_to_ticks != 0);
1546
1547  if (new_val != spin_button->snap_to_ticks)
1548    {
1549      spin_button->snap_to_ticks = new_val;
1550      if (new_val)
1551        {
1552          gchar *error = NULL;
1553          gfloat val;
1554
1555          val = strtod (gtk_entry_get_text (GTK_ENTRY (spin_button)), &error);
1556          gtk_spin_button_snap (spin_button, val);
1557        }
1558    }
1559}
1560
1561void
1562gtk_spin_button_spin (GtkSpinButton *spin_button,
1563                      GtkSpinType    direction,
1564                      gfloat         increment)
1565{
1566  GtkAdjustment *adj;
1567  gfloat diff;
1568
1569  g_return_if_fail (spin_button != NULL);
1570  g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1571 
1572  adj = spin_button->adjustment;
1573
1574  /* for compatibility with the 1.0.x version of this function */
1575  if (increment != 0 && increment != adj->step_increment &&
1576      (direction == GTK_SPIN_STEP_FORWARD ||
1577       direction == GTK_SPIN_STEP_BACKWARD))
1578    {
1579      if (direction == GTK_SPIN_STEP_BACKWARD && increment > 0)
1580        increment = -increment;
1581      direction = GTK_SPIN_USER_DEFINED;
1582    }
1583
1584  switch (direction)
1585    {
1586    case GTK_SPIN_STEP_FORWARD:
1587
1588      gtk_spin_button_real_spin (spin_button, adj->step_increment);
1589      break;
1590
1591    case GTK_SPIN_STEP_BACKWARD:
1592
1593      gtk_spin_button_real_spin (spin_button, -adj->step_increment);
1594      break;
1595
1596    case GTK_SPIN_PAGE_FORWARD:
1597
1598      gtk_spin_button_real_spin (spin_button, adj->page_increment);
1599      break;
1600
1601    case GTK_SPIN_PAGE_BACKWARD:
1602
1603      gtk_spin_button_real_spin (spin_button, -adj->page_increment);
1604      break;
1605
1606    case GTK_SPIN_HOME:
1607
1608      diff = adj->value - adj->lower;
1609      if (diff > EPSILON)
1610        gtk_spin_button_real_spin (spin_button, -diff);
1611      break;
1612
1613    case GTK_SPIN_END:
1614
1615      diff = adj->upper - adj->value;
1616      if (diff > EPSILON)
1617        gtk_spin_button_real_spin (spin_button, diff);
1618      break;
1619
1620    case GTK_SPIN_USER_DEFINED:
1621
1622      if (increment != 0)
1623        gtk_spin_button_real_spin (spin_button, increment);
1624      break;
1625
1626    default:
1627      break;
1628    }
1629}
Note: See TracBrowser for help on using the repository browser.