gtkspinbuttonsci.c

Go to the documentation of this file.
00001 /* GTK - The GIMP Toolkit
00002  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
00003  *
00004  * GtkSpinbuttonsci widget for GTK+
00005  * Copyright (C) 1998 Lars Hamann and Stefan Jeske
00006  *
00007  * This library is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public
00009  * License as published by the Free Software Foundation; either
00010  * version 2 of the License, or (at your option) any later version.
00011  *
00012  * This library is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Lesser General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with this library; if not, write to the
00019  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00020  * Boston, MA 02111-1307, USA.
00021  */
00022 
00023 /*
00024  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
00025  * file for a list of people on the GTK+ Team.  See the ChangeLog
00026  * files for a list of changes.  These files are distributed with
00027  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
00028  */
00029 
00030 #include <config.h>
00031 #include <stdio.h>
00032 #include <stdlib.h>
00033 #include <math.h>
00034 #include <string.h>
00035 #include <locale.h>
00036 
00037 #include <glib.h>
00038 #include <gtk/gtk.h>
00039 #include <gdk/gdk.h>
00040 #include "gdk/gdkkeysyms.h"
00041 #include "gtk/gtkbindings.h"
00042 #include "gtk/gtkmain.h"
00043 #include "gtk/gtksettings.h"
00044 
00045 #include "gtkmarshalers.h"
00046 #include "gtkintl.h"
00047 
00048 #include "gtkspinbuttonsci.h"
00049 
00050 #define MIN_SPIN_BUTTON_SCI_WIDTH              30
00051 #define SPIN_BUTTON_SCI_INITIAL_TIMER_DELAY    200
00052 #define SPIN_BUTTON_SCI_TIMER_DELAY            20
00053 #define MAX_TIMER_CALLS                    5
00054 #define EPSILON                            1e-10
00055 #define MAX_DIGITS                         20
00056 #define MIN_ARROW_WIDTH                    6
00057 
00058 enum {
00059   PROP_0,
00060   PROP_ADJUSTMENT,
00061   PROP_CLIMB_RATE,
00062   PROP_DIGITS,
00063   PROP_SNAP_TO_TICKS,
00064   PROP_NUMERIC,
00065   PROP_WRAP,
00066   PROP_UPDATE_POLICY,
00067   PROP_VALUE
00068 };
00069 
00070 /* Signals */
00071 enum
00072 {
00073   INPUT,
00074   OUTPUT,
00075   VALUE_CHANGED,
00076   CHANGE_VALUE,
00077   LAST_SIGNAL
00078 };
00079 
00080 static void gtk_spin_button_sci_class_init     (GtkSpinbuttonsciClass *klass);
00081 static void gtk_spin_button_sci_editable_init  (GtkEditableClass   *iface);
00082 static void gtk_spin_button_sci_init           (GtkSpinbuttonsci      *spin_button_sci);
00083 static void gtk_spin_button_sci_finalize       (GObject            *object);
00084 static void gtk_spin_button_sci_destroy        (GtkObject          *object);
00085 static void gtk_spin_button_sci_set_property   (GObject         *object,
00086                                             guint            prop_id,
00087                                             const GValue    *value,
00088                                             GParamSpec      *pspec);
00089 static void gtk_spin_button_sci_get_property   (GObject         *object,
00090                                             guint            prop_id,
00091                                             GValue          *value,
00092                                             GParamSpec      *pspec);
00093 static void gtk_spin_button_sci_map            (GtkWidget          *widget);
00094 static void gtk_spin_button_sci_unmap          (GtkWidget          *widget);
00095 static void gtk_spin_button_sci_realize        (GtkWidget          *widget);
00096 static void gtk_spin_button_sci_unrealize      (GtkWidget          *widget);
00097 static void gtk_spin_button_sci_size_request   (GtkWidget          *widget,
00098                                             GtkRequisition     *requisition);
00099 static void gtk_spin_button_sci_size_allocate  (GtkWidget          *widget,
00100                                             GtkAllocation      *allocation);
00101 static gint gtk_spin_button_sci_expose         (GtkWidget          *widget,
00102                                             GdkEventExpose     *event);
00103 static gint gtk_spin_button_sci_button_press   (GtkWidget          *widget,
00104                                             GdkEventButton     *event);
00105 static gint gtk_spin_button_sci_button_release (GtkWidget          *widget,
00106                                             GdkEventButton     *event);
00107 static gint gtk_spin_button_sci_motion_notify  (GtkWidget          *widget,
00108                                             GdkEventMotion     *event);
00109 static gint gtk_spin_button_sci_enter_notify   (GtkWidget          *widget,
00110                                             GdkEventCrossing   *event);
00111 static gint gtk_spin_button_sci_leave_notify   (GtkWidget          *widget,
00112                                             GdkEventCrossing   *event);
00113 static gint gtk_spin_button_sci_focus_out      (GtkWidget          *widget,
00114                                             GdkEventFocus      *event);
00115 static void gtk_spin_button_sci_grab_notify    (GtkWidget          *widget,
00116                                             gboolean            was_grabbed);
00117 static void gtk_spin_button_sci_state_changed  (GtkWidget          *widget,
00118                                             GtkStateType        previous_state);
00119 static void gtk_spin_button_sci_draw_arrow     (GtkSpinbuttonsci      *spin_button_sci, 
00120                                             GtkArrowType        arrow_type);
00121 static gint gtk_spin_button_sci_timer          (GtkSpinbuttonsci      *spin_button_sci);
00122 static void gtk_spin_button_sci_stop_spinning  (GtkSpinbuttonsci      *spin);
00123 static void gtk_spin_button_sci_value_changed  (GtkAdjustment      *adjustment,
00124                                             GtkSpinbuttonsci      *spin_button_sci); 
00125 static gint gtk_spin_button_sci_key_release    (GtkWidget          *widget,
00126                                             GdkEventKey        *event);
00127 static gint gtk_spin_button_sci_scroll         (GtkWidget          *widget,
00128                                             GdkEventScroll     *event);
00129 static void gtk_spin_button_sci_activate       (GtkEntry           *entry);
00130 static void gtk_spin_button_sci_snap           (GtkSpinbuttonsci      *spin_button_sci,
00131                                             gdouble             val);
00132 static void gtk_spin_button_sci_insert_text    (GtkEditable        *editable,
00133                                             const gchar        *new_text,
00134                                             gint                new_text_length,
00135                                             gint               *position);
00136 static void gtk_spin_button_sci_real_spin      (GtkSpinbuttonsci      *spin_button_sci,
00137                                             gdouble             step);
00138 static void gtk_spin_button_sci_real_change_value (GtkSpinbuttonsci   *spin,
00139                                                GtkScrollType    scroll);
00140 
00141 static gint gtk_spin_button_sci_default_input  (GtkSpinbuttonsci      *spin_button_sci,
00142                                             gdouble            *new_val);
00143 static gint gtk_spin_button_sci_default_output (GtkSpinbuttonsci      *spin_button_sci);
00144 
00145 static gint spin_button_sci_get_arrow_size     (GtkSpinbuttonsci      *spin_button_sci);
00146 static gint spin_button_sci_get_shadow_type    (GtkSpinbuttonsci      *spin_button_sci);
00147 static void spin_button_sci_redraw             (GtkSpinbuttonsci      *spin_button_sci);
00148 
00149 
00150 static GtkEntryClass *parent_class = NULL;
00151 static guint spinbuttonsci_signals[LAST_SIGNAL] = {0};
00152 
00153 #define NO_ARROW 2
00154 
00155 GType
00156 gtk_spin_button_sci_get_type (void)
00157 {
00158   static GType spin_button_sci_type = 0;
00159 
00160   if (!spin_button_sci_type)
00161     {
00162       static const GTypeInfo spin_button_sci_info =
00163       {
00164         sizeof (GtkSpinbuttonsciClass),
00165         NULL,           /* base_init */
00166         NULL,           /* base_finalize */
00167         (GClassInitFunc) gtk_spin_button_sci_class_init,
00168         NULL,           /* class_finalize */
00169         NULL,           /* class_data */
00170         sizeof (GtkSpinbuttonsci),
00171         0,              /* n_preallocs */
00172         (GInstanceInitFunc) gtk_spin_button_sci_init,
00173       };
00174 
00175       static const GInterfaceInfo editable_info =
00176       {
00177         (GInterfaceInitFunc) gtk_spin_button_sci_editable_init, /* interface_init */
00178         NULL, /* interface_finalize */
00179         NULL  /* interface_data */
00180       };
00181 
00182       spin_button_sci_type =
00183         g_type_register_static (GTK_TYPE_ENTRY, "GtkSpinbuttonsci",
00184                                 &spin_button_sci_info, 0);
00185 
00186       g_type_add_interface_static (spin_button_sci_type,
00187                                    GTK_TYPE_EDITABLE,
00188                                    &editable_info);
00189     }
00190   return spin_button_sci_type;
00191 }
00192 
00193 #define add_spin_binding(binding_set, keyval, mask, scroll)            \
00194   gtk_binding_entry_add_signal (binding_set, keyval, mask,             \
00195                                 "change_value", 1,                     \
00196                                 GTK_TYPE_SCROLL_TYPE, scroll)
00197 
00198 static void
00199 gtk_spin_button_sci_class_init (GtkSpinbuttonsciClass *class)
00200 {
00201   GObjectClass     *gobject_class = G_OBJECT_CLASS (class);
00202   GtkObjectClass   *object_class;
00203   GtkWidgetClass   *widget_class;
00204   GtkEntryClass    *entry_class;
00205   GtkBindingSet    *binding_set;
00206 
00207   object_class   = (GtkObjectClass*)   class;
00208   widget_class   = (GtkWidgetClass*)   class;
00209   entry_class    = (GtkEntryClass*)    class;
00210 
00211   parent_class = g_type_class_peek_parent (class);
00212 
00213   gobject_class->finalize = gtk_spin_button_sci_finalize;
00214 
00215   gobject_class->set_property = gtk_spin_button_sci_set_property;
00216   gobject_class->get_property = gtk_spin_button_sci_get_property;
00217 
00218   object_class->destroy = gtk_spin_button_sci_destroy;
00219 
00220   widget_class->map = gtk_spin_button_sci_map;
00221   widget_class->unmap = gtk_spin_button_sci_unmap;
00222   widget_class->realize = gtk_spin_button_sci_realize;
00223   widget_class->unrealize = gtk_spin_button_sci_unrealize;
00224   widget_class->size_request = gtk_spin_button_sci_size_request;
00225   widget_class->size_allocate = gtk_spin_button_sci_size_allocate;
00226   widget_class->expose_event = gtk_spin_button_sci_expose;
00227   widget_class->scroll_event = gtk_spin_button_sci_scroll;
00228   widget_class->button_press_event = gtk_spin_button_sci_button_press;
00229   widget_class->button_release_event = gtk_spin_button_sci_button_release;
00230   widget_class->motion_notify_event = gtk_spin_button_sci_motion_notify;
00231   widget_class->key_release_event = gtk_spin_button_sci_key_release;
00232   widget_class->enter_notify_event = gtk_spin_button_sci_enter_notify;
00233   widget_class->leave_notify_event = gtk_spin_button_sci_leave_notify;
00234   widget_class->focus_out_event = gtk_spin_button_sci_focus_out;
00235   widget_class->grab_notify = gtk_spin_button_sci_grab_notify;
00236   widget_class->state_changed = gtk_spin_button_sci_state_changed;
00237 
00238   entry_class->activate = gtk_spin_button_sci_activate;
00239 
00240   class->input = NULL;
00241   class->output = NULL;
00242   class->change_value = gtk_spin_button_sci_real_change_value;
00243 
00244   g_object_class_install_property (gobject_class,
00245                                    PROP_ADJUSTMENT,
00246                                    g_param_spec_object ("adjustment",
00247                                                         P_("Adjustment"),
00248                                                         P_("The adjustment that holds the value of the spinbuttonsci"),
00249                                                         GTK_TYPE_ADJUSTMENT,
00250                                                         G_PARAM_READWRITE));
00251   
00252   g_object_class_install_property (gobject_class,
00253                                    PROP_CLIMB_RATE,
00254                                    g_param_spec_double ("climb_rate",
00255                                                         P_("Climb Rate"),
00256                                                         P_("The acceleration rate when you hold down a button"),
00257                                                         0.0,
00258                                                         G_MAXDOUBLE,
00259                                                         0.0,
00260                                                         G_PARAM_READWRITE));  
00261   
00262   g_object_class_install_property (gobject_class,
00263                                    PROP_DIGITS,
00264                                    g_param_spec_uint ("digits",
00265                                                       P_("Digits"),
00266                                                       P_("The number of decimal places to display"),
00267                                                       0,
00268                                                       MAX_DIGITS,
00269                                                       0,
00270                                                       G_PARAM_READWRITE));
00271   
00272   g_object_class_install_property (gobject_class,
00273                                    PROP_SNAP_TO_TICKS,
00274                                    g_param_spec_boolean ("snap_to_ticks",
00275                                                          P_("Snap to Ticks"),
00276                                                          P_("Whether erroneous values are automatically changed to a spin button's nearest step increment"),
00277                                                          FALSE,
00278                                                          G_PARAM_READWRITE));
00279   
00280   g_object_class_install_property (gobject_class,
00281                                    PROP_NUMERIC,
00282                                    g_param_spec_boolean ("numeric",
00283                                                          P_("Numeric"),
00284                                                          P_("Whether non-numeric characters should be ignored"),
00285                                                          FALSE,
00286                                                          G_PARAM_READWRITE));
00287   
00288   g_object_class_install_property (gobject_class,
00289                                    PROP_WRAP,
00290                                    g_param_spec_boolean ("wrap",
00291                                                          P_("Wrap"),
00292                                                          P_("Whether a spin button should wrap upon reaching its limits"),
00293                                                          FALSE,
00294                                                          G_PARAM_READWRITE));
00295   
00296   g_object_class_install_property (gobject_class,
00297                                    PROP_UPDATE_POLICY,
00298                                    g_param_spec_enum ("update_policy",
00299                                                       P_("Update Policy"),
00300                                                       P_("Whether the spin button should update always, or only when the value is legal"),
00301                                                       GTK_TYPE_SPIN_BUTTON_UPDATE_POLICY,
00302                                                       GTK_UPDATE_ALWAYS,
00303                                                       G_PARAM_READWRITE));
00304   
00305   g_object_class_install_property (gobject_class,
00306                                    PROP_VALUE,
00307                                    g_param_spec_double ("value",
00308                                                         P_("Value"),
00309                                                         P_("Reads the current value, or sets a new value"),
00310                                                         -G_MAXDOUBLE,
00311                                                         G_MAXDOUBLE,
00312                                                         0.0,
00313                                                         G_PARAM_READWRITE));  
00314   
00315   gtk_widget_class_install_style_property_parser (widget_class,
00316                                                   g_param_spec_enum ("shadow_type", 
00317                                                                      "Shadow Type", 
00318                                                                      P_("Style of bevel around the spin button"),
00319                                                                      GTK_TYPE_SHADOW_TYPE,
00320                                                                      GTK_SHADOW_IN,
00321                                                                      G_PARAM_READABLE),
00322                                                   gtk_rc_property_parse_enum);
00323   spinbuttonsci_signals[INPUT] =
00324     g_signal_new ("input",
00325                   G_TYPE_FROM_CLASS (gobject_class),
00326                   G_SIGNAL_RUN_LAST,
00327                   G_STRUCT_OFFSET (GtkSpinbuttonsciClass, input),
00328                   NULL, NULL,
00329                   _gtk_marshal_INT__POINTER,
00330                   G_TYPE_INT, 1,
00331                   G_TYPE_POINTER);
00332 
00333   spinbuttonsci_signals[OUTPUT] =
00334     g_signal_new ("output",
00335                   G_TYPE_FROM_CLASS (gobject_class),
00336                   G_SIGNAL_RUN_LAST,
00337                   G_STRUCT_OFFSET (GtkSpinbuttonsciClass, output),
00338                   NULL, NULL,
00339 //                _gtk_boolean_handled_accumulator, NULL, /* Fedora Problem */
00340                   _gtk_marshal_BOOLEAN__VOID,
00341                   G_TYPE_BOOLEAN, 0);
00342 
00343   spinbuttonsci_signals[VALUE_CHANGED] =
00344     g_signal_new ("value_changed",
00345                   G_TYPE_FROM_CLASS (gobject_class),
00346                   G_SIGNAL_RUN_LAST,
00347                   G_STRUCT_OFFSET (GtkSpinbuttonsciClass, value_changed),
00348                   NULL, NULL,
00349                   _gtk_marshal_VOID__VOID,
00350                   G_TYPE_NONE, 0);
00351 
00352   /* Action signals */
00353   spinbuttonsci_signals[CHANGE_VALUE] =
00354     g_signal_new ("change_value",
00355                   G_TYPE_FROM_CLASS (gobject_class),
00356                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
00357                   G_STRUCT_OFFSET (GtkSpinbuttonsciClass, change_value),
00358                   NULL, NULL,
00359                   _gtk_marshal_VOID__ENUM,
00360                   G_TYPE_NONE, 1,
00361                   GTK_TYPE_SCROLL_TYPE);
00362   
00363   binding_set = gtk_binding_set_by_class (class);
00364   
00365   add_spin_binding (binding_set, GDK_Up, 0, GTK_SCROLL_STEP_UP);
00366   add_spin_binding (binding_set, GDK_KP_Up, 0, GTK_SCROLL_STEP_UP);
00367   add_spin_binding (binding_set, GDK_Down, 0, GTK_SCROLL_STEP_DOWN);
00368   add_spin_binding (binding_set, GDK_KP_Down, 0, GTK_SCROLL_STEP_DOWN);
00369   add_spin_binding (binding_set, GDK_Page_Up, 0, GTK_SCROLL_PAGE_UP);
00370   add_spin_binding (binding_set, GDK_Page_Down, 0, GTK_SCROLL_PAGE_DOWN);
00371   add_spin_binding (binding_set, GDK_Page_Up, GDK_CONTROL_MASK, GTK_SCROLL_END);
00372   add_spin_binding (binding_set, GDK_Page_Down, GDK_CONTROL_MASK, GTK_SCROLL_START);
00373 }
00374 
00375 static void
00376 gtk_spin_button_sci_editable_init (GtkEditableClass *iface)
00377 {
00378   iface->insert_text = gtk_spin_button_sci_insert_text;
00379 }
00380 
00381 static void
00382 gtk_spin_button_sci_set_property (GObject      *object,
00383                               guint         prop_id,
00384                               const GValue *value,
00385                               GParamSpec   *pspec)
00386 {
00387   GtkSpinbuttonsci *spin_button_sci;
00388 
00389   spin_button_sci = GTK_SPIN_BUTTON_SCI (object);
00390   
00391   switch (prop_id)
00392     {
00393       GtkAdjustment *adjustment;
00394 
00395     case PROP_ADJUSTMENT:
00396       adjustment = GTK_ADJUSTMENT (g_value_get_object (value));
00397       if (!adjustment)
00398         adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
00399       gtk_spin_button_sci_set_adjustment (spin_button_sci, adjustment);
00400       break;
00401     case PROP_CLIMB_RATE:
00402       gtk_spin_button_sci_configure (spin_button_sci,
00403                                  spin_button_sci->adjustment,
00404                                  g_value_get_double (value),
00405                                  spin_button_sci->digits);
00406       break;
00407     case PROP_DIGITS:
00408       gtk_spin_button_sci_configure (spin_button_sci,
00409                                  spin_button_sci->adjustment,
00410                                  spin_button_sci->climb_rate,
00411                                  g_value_get_uint (value));
00412       break;
00413     case PROP_SNAP_TO_TICKS:
00414       gtk_spin_button_sci_set_snap_to_ticks (spin_button_sci, g_value_get_boolean (value));
00415       break;
00416     case PROP_NUMERIC:
00417       gtk_spin_button_sci_set_numeric (spin_button_sci, g_value_get_boolean (value));
00418       break;
00419     case PROP_WRAP:
00420       gtk_spin_button_sci_set_wrap (spin_button_sci, g_value_get_boolean (value));
00421       break;
00422     case PROP_UPDATE_POLICY:
00423       gtk_spin_button_sci_set_update_policy (spin_button_sci, g_value_get_enum (value));
00424       break;
00425     case PROP_VALUE:
00426       gtk_spin_button_sci_set_value (spin_button_sci, g_value_get_double (value));
00427       break;
00428     default:
00429       break;
00430     }
00431 }
00432 
00433 static void
00434 gtk_spin_button_sci_get_property (GObject      *object,
00435                               guint         prop_id,
00436                               GValue       *value,
00437                               GParamSpec   *pspec)
00438 {
00439   GtkSpinbuttonsci *spin_button_sci;
00440 
00441   spin_button_sci = GTK_SPIN_BUTTON_SCI (object);
00442   
00443   switch (prop_id)
00444     {
00445     case PROP_ADJUSTMENT:
00446       g_value_set_object (value, spin_button_sci->adjustment);
00447       break;
00448     case PROP_CLIMB_RATE:
00449       g_value_set_double (value, spin_button_sci->climb_rate);
00450       break;
00451     case PROP_DIGITS:
00452       g_value_set_uint (value, spin_button_sci->digits);
00453       break;
00454     case PROP_SNAP_TO_TICKS:
00455       g_value_set_boolean (value, spin_button_sci->snap_to_ticks);
00456       break;
00457     case PROP_NUMERIC:
00458       g_value_set_boolean (value, spin_button_sci->numeric);
00459       break;
00460     case PROP_WRAP:
00461       g_value_set_boolean (value, spin_button_sci->wrap);
00462       break;
00463     case PROP_UPDATE_POLICY:
00464       g_value_set_enum (value, spin_button_sci->update_policy);
00465       break;
00466      case PROP_VALUE:
00467        g_value_set_double (value, spin_button_sci->adjustment->value);
00468       break;
00469     default:
00470       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
00471       break;
00472     }
00473 }
00474 
00475 static void
00476 gtk_spin_button_sci_init (GtkSpinbuttonsci *spin_button_sci)
00477 {
00478   spin_button_sci->adjustment = NULL;
00479   spin_button_sci->adjustment_safety = NULL;
00480   spin_button_sci->panel = NULL;
00481   spin_button_sci->timer = 0;
00482   spin_button_sci->climb_rate = 0.0;
00483   spin_button_sci->timer_step = 0.0;
00484   spin_button_sci->update_policy = GTK_UPDATE_ALWAYS;
00485   spin_button_sci->in_child = NO_ARROW;
00486   spin_button_sci->click_child = NO_ARROW;
00487   spin_button_sci->button = 0;
00488   spin_button_sci->need_timer = FALSE;
00489   spin_button_sci->timer_calls = 0;
00490   spin_button_sci->digits = 0;
00491   spin_button_sci->numeric = FALSE;
00492   spin_button_sci->wrap = FALSE;
00493   spin_button_sci->snap_to_ticks = FALSE;
00494   spin_button_sci->jump_to_limits = FALSE;
00495   spin_button_sci->sci_fixed_point_mantisse = FALSE;
00496   spin_button_sci->sci_max_left_digits = 8;
00497   spin_button_sci->sci_extension_magnitude_arrows = TRUE;
00498   spin_button_sci->sci_suffix = NULL;
00499 
00500   gtk_spin_button_sci_set_adjustment (spin_button_sci,
00501           (GtkAdjustment*) gtk_adjustment_new (0, 0, 0, 0, 0, 0));
00502 }
00503 
00504 static void
00505 gtk_spin_button_sci_finalize (GObject *object)
00506 {
00507   gtk_spin_button_sci_set_adjustment (GTK_SPIN_BUTTON_SCI (object), NULL);
00508   
00509   G_OBJECT_CLASS (parent_class)->finalize (object);
00510 }
00511 
00512 static void
00513 gtk_spin_button_sci_destroy (GtkObject *object)
00514 {
00515   gtk_spin_button_sci_stop_spinning (GTK_SPIN_BUTTON_SCI (object));
00516   gtk_spin_button_sci_set_suffix (GTK_SPIN_BUTTON_SCI (object), NULL);
00517   
00518   GTK_OBJECT_CLASS (parent_class)->destroy (object);
00519 }
00520 
00521 static void
00522 gtk_spin_button_sci_map (GtkWidget *widget)
00523 {
00524   if (GTK_WIDGET_REALIZED (widget) && !GTK_WIDGET_MAPPED (widget))
00525     {
00526       GTK_WIDGET_CLASS (parent_class)->map (widget);
00527       gdk_window_show (GTK_SPIN_BUTTON_SCI (widget)->panel);
00528     }
00529 }
00530 
00531 static void
00532 gtk_spin_button_sci_unmap (GtkWidget *widget)
00533 {
00534   if (GTK_WIDGET_MAPPED (widget))
00535     {
00536       gdk_window_hide (GTK_SPIN_BUTTON_SCI (widget)->panel);
00537       GTK_WIDGET_CLASS (parent_class)->unmap (widget);
00538     }
00539 }
00540 
00541 static void
00542 gtk_spin_button_sci_realize (GtkWidget *widget)
00543 {
00544   GtkSpinbuttonsci *spin_button_sci;
00545   GdkWindowAttr attributes;
00546   gint attributes_mask;
00547   guint real_width;
00548   gboolean return_val;
00549   gint arrow_size;
00550 
00551   spin_button_sci = GTK_SPIN_BUTTON_SCI (widget);
00552   arrow_size = spin_button_sci_get_arrow_size (spin_button_sci);
00553 
00554   real_width = widget->allocation.width;
00555   widget->allocation.width -= arrow_size + 2 * widget->style->xthickness;
00556   gtk_widget_set_events (widget, gtk_widget_get_events (widget) |
00557                          GDK_KEY_RELEASE_MASK);
00558   GTK_WIDGET_CLASS (parent_class)->realize (widget);
00559 
00560   widget->allocation.width = real_width;
00561   
00562   attributes.window_type = GDK_WINDOW_CHILD;
00563   attributes.wclass = GDK_INPUT_OUTPUT;
00564   attributes.visual = gtk_widget_get_visual (widget);
00565   attributes.colormap = gtk_widget_get_colormap (widget);
00566   attributes.event_mask = gtk_widget_get_events (widget);
00567   attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK 
00568     | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK 
00569     | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK;
00570 
00571   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
00572 
00573   attributes.x = (widget->allocation.x +
00574                   widget->allocation.width - arrow_size -
00575                   2 * widget->style->xthickness);
00576   attributes.y = widget->allocation.y + (widget->allocation.height -
00577                                          widget->requisition.height) / 2;
00578   attributes.width = arrow_size + 2 * widget->style->xthickness;
00579   attributes.height = widget->requisition.height;
00580   
00581   spin_button_sci->panel = gdk_window_new (gtk_widget_get_parent_window (widget), 
00582                                        &attributes, attributes_mask);
00583   gdk_window_set_user_data (spin_button_sci->panel, widget);
00584 
00585   gtk_style_set_background (widget->style, spin_button_sci->panel, GTK_STATE_NORMAL);
00586 
00587   return_val = FALSE;
00588   g_signal_emit (spin_button_sci, spinbuttonsci_signals[OUTPUT], 0, &return_val);
00589   if (return_val == FALSE)
00590     gtk_spin_button_sci_default_output (spin_button_sci);
00591 
00592   gtk_widget_queue_resize (GTK_WIDGET (spin_button_sci));
00593 }
00594 
00595 static void
00596 gtk_spin_button_sci_unrealize (GtkWidget *widget)
00597 {
00598   GtkSpinbuttonsci *spin = GTK_SPIN_BUTTON_SCI (widget);
00599 
00600   GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
00601 
00602   if (spin->panel)
00603     {
00604       gdk_window_set_user_data (spin->panel, NULL);
00605       gdk_window_destroy (spin->panel);
00606       spin->panel = NULL;
00607     }
00608 }
00609 
00610 static int
00611 compute_double_length (double val, int digits)
00612 {
00613   int a;
00614   int extra;
00615 
00616   a = 1;
00617   if (fabs (val) > 1.0)
00618     a = floor (log10 (fabs (val))) + 1;  
00619 
00620   extra = 0;
00621   
00622   /* The dot: */
00623   if (digits > 0)
00624     extra++;
00625 
00626   /* The sign: */
00627   if (val < 0)
00628     extra++;
00629 
00630   return a + digits + extra;
00631 }
00632 
00633 /* Keep in sync with gtkentry.c !
00634  */
00635 static void
00636 get_borders (GtkEntry *entry,
00637              gint     *xborder,
00638              gint     *yborder)
00639 {
00640   GtkWidget *widget = GTK_WIDGET (entry);
00641   gint focus_width;
00642   gboolean interior_focus;
00643 
00644   gtk_widget_style_get (widget,
00645                         "interior-focus", &interior_focus,
00646                         "focus-line-width", &focus_width,
00647                         NULL);
00648 
00649   if (entry->has_frame)
00650     {
00651       *xborder = widget->style->xthickness;
00652       *yborder = widget->style->ythickness;
00653     }
00654   else
00655     {
00656       *xborder = 0;
00657       *yborder = 0;
00658     }
00659 
00660   if (!interior_focus)
00661     {
00662       *xborder += focus_width;
00663       *yborder += focus_width;
00664     }
00665 }
00666 
00667 static void
00668 gtk_spin_button_sci_size_request (GtkWidget      *widget,
00669                               GtkRequisition *requisition)
00670 {
00671   GtkEntry *entry;
00672   GtkSpinbuttonsci *spin_button_sci;
00673   gint arrow_size;
00674 
00675   entry = GTK_ENTRY (widget);
00676   spin_button_sci = GTK_SPIN_BUTTON_SCI (widget);
00677   arrow_size = spin_button_sci_get_arrow_size (spin_button_sci);
00678   
00679   GTK_WIDGET_CLASS (parent_class)->size_request (widget, requisition);
00680 
00681   if (entry->width_chars < 0)
00682     {
00683       PangoContext *context;
00684       PangoFontMetrics *metrics;
00685       gint width;
00686       gint w;
00687       gint string_len;
00688       gint max_string_len;
00689       gint digit_width;
00690       gboolean interior_focus;
00691       gint focus_width;
00692       gint xborder, yborder;
00693 
00694       gtk_widget_style_get (widget,
00695                             "interior-focus", &interior_focus,
00696                             "focus-line-width", &focus_width,
00697                             NULL);
00698 
00699       context = gtk_widget_get_pango_context (widget);
00700       metrics = pango_context_get_metrics (context,
00701                                            widget->style->font_desc,
00702                                            pango_context_get_language (context));
00703 
00704       digit_width = pango_font_metrics_get_approximate_digit_width (metrics);
00705       digit_width = PANGO_SCALE *
00706         ((digit_width + PANGO_SCALE - 1) / PANGO_SCALE);
00707 
00708       pango_font_metrics_unref (metrics);
00709       
00710       /* Get max of MIN_SPIN_BUTTON_SCI_WIDTH, size of upper, size of lower */
00711       
00712       width = MIN_SPIN_BUTTON_SCI_WIDTH;
00713       max_string_len = MAX (10, compute_double_length (1e9 * spin_button_sci->adjustment->step_increment,
00714                                                        spin_button_sci->digits));
00715 
00716       string_len = compute_double_length (spin_button_sci->adjustment->upper,
00717                                           spin_button_sci->digits);
00718       w = PANGO_PIXELS (MIN (string_len, max_string_len) * digit_width);
00719       width = MAX (width, w);
00720       string_len = compute_double_length (spin_button_sci->adjustment->lower,
00721                                           spin_button_sci->digits);
00722       w = PANGO_PIXELS (MIN (string_len, max_string_len) * digit_width);
00723       width = MAX (width, w);
00724       
00725       get_borders (entry, &xborder, &yborder);
00726       
00727       xborder += 2; /* INNER_BORDER */
00728 
00729       requisition->width = width + xborder * 2;
00730     }
00731 
00732   requisition->width += arrow_size + 2 * widget->style->xthickness;
00733 }
00734 
00735 static void
00736 gtk_spin_button_sci_size_allocate (GtkWidget     *widget,
00737                                GtkAllocation *allocation)
00738 {
00739   GtkSpinbuttonsci *spin;
00740   GtkAllocation entry_allocation;
00741   GtkAllocation panel_allocation;
00742   gint arrow_size;
00743   gint panel_width;
00744 
00745   g_return_if_fail (GTK_IS_SPIN_BUTTON_SCI (widget));
00746   g_return_if_fail (allocation != NULL);
00747 
00748   spin = GTK_SPIN_BUTTON_SCI (widget);
00749   arrow_size = spin_button_sci_get_arrow_size (spin);
00750   panel_width = arrow_size + 2 * widget->style->xthickness;
00751   
00752   widget->allocation = *allocation;
00753   
00754   entry_allocation = *allocation;
00755   entry_allocation.width -= panel_width;
00756 
00757   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
00758     {
00759       entry_allocation.x += panel_width;
00760       panel_allocation.x = allocation->x;
00761     }
00762   else
00763     {
00764       panel_allocation.x = allocation->x + allocation->width - panel_width;
00765     }
00766 
00767   panel_allocation.width = panel_width;
00768   panel_allocation.height = MIN (widget->requisition.height, allocation->height);
00769 
00770   panel_allocation.y = allocation->y + (allocation->height -
00771                                        panel_allocation.height) / 2;
00772 
00773   GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, &entry_allocation);
00774 
00775   if (GTK_WIDGET_REALIZED (widget))
00776     {
00777       gdk_window_move_resize (GTK_SPIN_BUTTON_SCI (widget)->panel, 
00778                               panel_allocation.x,
00779                               panel_allocation.y,
00780                               panel_allocation.width,
00781                               panel_allocation.height); 
00782     }
00783 
00784   spin_button_sci_redraw (spin);
00785 }
00786 
00787 static gint
00788 gtk_spin_button_sci_expose (GtkWidget      *widget,
00789                         GdkEventExpose *event)
00790 {
00791   GtkSpinbuttonsci *spin;
00792 
00793   g_return_val_if_fail (GTK_IS_SPIN_BUTTON_SCI (widget), FALSE);
00794   g_return_val_if_fail (event != NULL, FALSE);
00795 
00796   spin = GTK_SPIN_BUTTON_SCI (widget);
00797 
00798   if (GTK_WIDGET_DRAWABLE (widget))
00799     {
00800       GtkShadowType shadow_type;
00801       GdkRectangle rect;
00802 
00803       if (event->window != spin->panel)
00804         GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
00805 
00806       /* we redraw the panel even if it wasn't exposed. This is
00807        * because spin->panel is not a child window of widget->window,
00808        * so it will not be invalidated by eg. gtk_widget_queue_draw().
00809        */
00810       rect.x = 0;
00811       rect.y = 0;
00812 
00813       gdk_drawable_get_size (spin->panel, &rect.width, &rect.height);
00814 
00815       shadow_type = spin_button_sci_get_shadow_type (spin);
00816       
00817       gdk_window_begin_paint_rect (spin->panel, &rect);      
00818 
00819       if (shadow_type != GTK_SHADOW_NONE)
00820         {
00821           gtk_paint_box (widget->style, spin->panel,
00822                          GTK_STATE_NORMAL, shadow_type,
00823                          NULL, widget, "spinbuttonsci",
00824                          rect.x, rect.y, rect.width, rect.height);
00825         }
00826 
00827       gtk_spin_button_sci_draw_arrow (spin, GTK_ARROW_UP);
00828       gtk_spin_button_sci_draw_arrow (spin, GTK_ARROW_DOWN);
00829 
00830       gdk_window_end_paint (spin->panel);
00831     }
00832   
00833   return FALSE;
00834 }
00835 
00836 static gboolean
00837 spin_button_sci_at_limit (GtkSpinbuttonsci *spin_button_sci,
00838                      GtkArrowType   arrow)
00839 {
00840   GtkArrowType effective_arrow;
00841 
00842   if (spin_button_sci->wrap)
00843     return FALSE;
00844 
00845   if (spin_button_sci->adjustment->step_increment > 0)
00846     effective_arrow = arrow;
00847   else
00848     effective_arrow = arrow == GTK_ARROW_UP ? GTK_ARROW_DOWN : GTK_ARROW_UP; 
00849   
00850   if (effective_arrow == GTK_ARROW_UP &&
00851       (spin_button_sci->adjustment->upper - spin_button_sci->adjustment->value <= EPSILON))
00852     return TRUE;
00853   
00854   if (effective_arrow == GTK_ARROW_DOWN &&
00855       (spin_button_sci->adjustment->value - spin_button_sci->adjustment->lower <= EPSILON))
00856     return TRUE;
00857   
00858   return FALSE;
00859 }
00860 
00861 static void
00862 gtk_spin_button_sci_draw_arrow (GtkSpinbuttonsci *spin_button_sci, 
00863                             GtkArrowType   arrow_type)
00864 {
00865   GtkStateType state_type;
00866   GtkShadowType shadow_type;
00867   GtkWidget *widget;
00868   gint x;
00869   gint y;
00870   gint height;
00871   gint width;
00872   gint h, w;
00873 
00874   g_return_if_fail (GTK_IS_SPIN_BUTTON_SCI (spin_button_sci));
00875   g_return_if_fail (arrow_type == GTK_ARROW_UP || arrow_type == GTK_ARROW_DOWN);
00876   
00877   widget = GTK_WIDGET (spin_button_sci);
00878 
00879   if (GTK_WIDGET_DRAWABLE (widget))
00880     {
00881       width = spin_button_sci_get_arrow_size (spin_button_sci) + 2 * widget->style->xthickness;
00882 
00883       if (arrow_type == GTK_ARROW_UP)
00884         {
00885           x = 0;
00886           y = 0;
00887 
00888           height = widget->requisition.height / 2;
00889         }
00890       else
00891         {
00892           x = 0;
00893           y = widget->requisition.height / 2;
00894 
00895           height = (widget->requisition.height + 1) / 2;
00896         }
00897 
00898       if (spin_button_sci_at_limit (spin_button_sci, arrow_type))
00899         {
00900           shadow_type = GTK_SHADOW_OUT;
00901           state_type = GTK_STATE_INSENSITIVE;
00902         }
00903       else
00904         {
00905           if (spin_button_sci->click_child == arrow_type)
00906             {
00907               state_type = GTK_STATE_ACTIVE;
00908               shadow_type = GTK_SHADOW_IN;
00909             }
00910           else
00911             {
00912               if (spin_button_sci->in_child == arrow_type &&
00913                   spin_button_sci->click_child == NO_ARROW)
00914                 {
00915                   state_type = GTK_STATE_PRELIGHT;
00916                 }
00917               else
00918                 {
00919                   state_type = GTK_WIDGET_STATE (widget);
00920                 }
00921               
00922               shadow_type = GTK_SHADOW_OUT;
00923             }
00924         }
00925       
00926       gtk_paint_box (widget->style, spin_button_sci->panel,
00927                      state_type, shadow_type,
00928                      NULL, widget,
00929                      (arrow_type == GTK_ARROW_UP)? "spinbuttonsci_up" : "spinbuttonsci_down",
00930                      x, y, width, height);
00931 
00932       height = widget->requisition.height;
00933 
00934       if (arrow_type == GTK_ARROW_DOWN)
00935         {
00936           y = height / 2;
00937           height = height - y - 2;
00938         }
00939       else
00940         {
00941           y = 2;
00942           height = height / 2 - 2;
00943         }
00944 
00945       width -= 3;
00946 
00947       if (widget && gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
00948         x = 2;
00949       else
00950         x = 1;
00951 
00952       w = width / 2;
00953       w -= w % 2 - 1; /* force odd */
00954       h = (w + 1) / 2;
00955       
00956       x += (width - w) / 2;
00957       y += (height - h) / 2;
00958       
00959       height = h;
00960       width = w;
00961 
00962       gtk_paint_arrow (widget->style, spin_button_sci->panel,
00963                        state_type, shadow_type, 
00964                        NULL, widget, "spinbuttonsci",
00965                        arrow_type, TRUE, 
00966                        x, y, width, height);
00967     }
00968 }
00969 
00970 static gint
00971 gtk_spin_button_sci_enter_notify (GtkWidget        *widget,
00972                               GdkEventCrossing *event)
00973 {
00974   GtkSpinbuttonsci *spin = GTK_SPIN_BUTTON_SCI (widget);
00975 
00976   if (event->window == spin->panel)
00977     {
00978       gint x;
00979       gint y;
00980 
00981       gdk_window_get_pointer (spin->panel, &x, &y, NULL);
00982 
00983       if (y <= widget->requisition.height / 2)
00984         spin->in_child = GTK_ARROW_UP;
00985       else
00986         spin->in_child = GTK_ARROW_DOWN;
00987 
00988       spin_button_sci_redraw (spin);
00989     }
00990   
00991   return FALSE;
00992 }
00993 
00994 static gint
00995 gtk_spin_button_sci_leave_notify (GtkWidget        *widget,
00996                               GdkEventCrossing *event)
00997 {
00998   GtkSpinbuttonsci *spin = GTK_SPIN_BUTTON_SCI (widget);
00999 
01000   spin->in_child = NO_ARROW;
01001   spin_button_sci_redraw (spin);
01002   
01003   return FALSE;
01004 }
01005 
01006 static gint
01007 gtk_spin_button_sci_focus_out (GtkWidget     *widget,
01008                            GdkEventFocus *event)
01009 {
01010   if (GTK_ENTRY (widget)->editable)
01011     gtk_spin_button_sci_update (GTK_SPIN_BUTTON_SCI (widget));
01012 
01013   return GTK_WIDGET_CLASS (parent_class)->focus_out_event (widget, event);
01014 }
01015 
01016 static void
01017 gtk_spin_button_sci_grab_notify (GtkWidget *widget,
01018                              gboolean   was_grabbed)
01019 {
01020   GtkSpinbuttonsci *spin = GTK_SPIN_BUTTON_SCI (widget);
01021 
01022   if (!was_grabbed)
01023     {
01024       gtk_spin_button_sci_stop_spinning (spin);
01025       spin_button_sci_redraw (spin);
01026     }
01027 }
01028 
01029 static void
01030 gtk_spin_button_sci_state_changed (GtkWidget    *widget,
01031                                GtkStateType  previous_state)
01032 {
01033   GtkSpinbuttonsci *spin = GTK_SPIN_BUTTON_SCI (widget);
01034 
01035   if (!GTK_WIDGET_IS_SENSITIVE (widget))
01036     {
01037       gtk_spin_button_sci_stop_spinning (spin);    
01038       spin_button_sci_redraw (spin);
01039     }
01040 }
01041 
01042 static gint
01043 gtk_spin_button_sci_scroll (GtkWidget      *widget,
01044                         GdkEventScroll *event)
01045 {
01046   GtkSpinbuttonsci *spin = GTK_SPIN_BUTTON_SCI (widget);
01047 
01048   if (event->direction == GDK_SCROLL_UP)
01049     {
01050       if (!GTK_WIDGET_HAS_FOCUS (widget))
01051         gtk_widget_grab_focus (widget);
01052       gtk_spin_button_sci_real_spin (spin, spin->adjustment->step_increment);
01053     }
01054   else if (event->direction == GDK_SCROLL_DOWN)
01055     {
01056       if (!GTK_WIDGET_HAS_FOCUS (widget))
01057         gtk_widget_grab_focus (widget);
01058       gtk_spin_button_sci_real_spin (spin, -spin->adjustment->step_increment); 
01059     }
01060   else
01061     return FALSE;
01062 
01063   return TRUE;
01064 }
01065 
01066 static void
01067 gtk_spin_button_sci_stop_spinning (GtkSpinbuttonsci *spin)
01068 {
01069   if (spin->timer)
01070     {
01071       g_source_remove (spin->timer);
01072       spin->timer = 0;
01073       spin->timer_calls = 0;
01074       spin->need_timer = FALSE;
01075     }
01076 
01077   spin->button = 0;
01078   spin->timer = 0;
01079   spin->timer_step = spin->adjustment->step_increment;
01080   spin->timer_calls = 0;
01081 
01082   spin->click_child = NO_ARROW;
01083   spin->button = 0;
01084 }
01085 
01086 static void
01087 start_spinning (GtkSpinbuttonsci *spin,
01088                 GtkArrowType   click_child,
01089                 gdouble        step)
01090 {
01091   g_return_if_fail (click_child == GTK_ARROW_UP || click_child == GTK_ARROW_DOWN);
01092   
01093   spin->click_child = click_child;
01094   gtk_spin_button_sci_real_spin (spin, click_child == GTK_ARROW_UP ? step : -step);
01095   
01096   if (!spin->timer)
01097     {
01098       spin->timer_step = step;
01099       spin->need_timer = TRUE;
01100       spin->timer = g_timeout_add (SPIN_BUTTON_SCI_INITIAL_TIMER_DELAY, 
01101                                    (GSourceFunc) gtk_spin_button_sci_timer, 
01102                                    (gpointer) spin);
01103     }
01104 
01105   spin_button_sci_redraw (spin);
01106 }
01107 
01108 static gint
01109 gtk_spin_button_sci_button_press (GtkWidget      *widget,
01110                               GdkEventButton *event)
01111 {
01112   GtkSpinbuttonsci *spin = GTK_SPIN_BUTTON_SCI (widget);
01113 
01114   if (!spin->button)
01115     {
01116       if (event->window == spin->panel)
01117         {
01118           if (!GTK_WIDGET_HAS_FOCUS (widget))
01119             gtk_widget_grab_focus (widget);
01120           spin->button = event->button;
01121           
01122           if (GTK_ENTRY (widget)->editable)
01123             gtk_spin_button_sci_update (spin);
01124           
01125           if (event->y <= widget->requisition.height / 2)
01126             {
01127               if (event->button == 1)
01128                 start_spinning (spin, GTK_ARROW_UP, spin->adjustment->step_increment);
01129               else if (event->button == 2)
01130                 start_spinning (spin, GTK_ARROW_UP, spin->adjustment->page_increment);
01131               else if (event->button == 3 && !spin->jump_to_limits)
01132                 start_spinning (spin, GTK_ARROW_UP, 10.*spin->adjustment->page_increment);
01133               else
01134                 spin->click_child = GTK_ARROW_UP;
01135             }
01136           else 
01137             {
01138               if (event->button == 1)
01139                 start_spinning (spin, GTK_ARROW_DOWN, spin->adjustment->step_increment);
01140               else if (event->button == 2)
01141                 start_spinning (spin, GTK_ARROW_DOWN, spin->adjustment->page_increment);
01142               else if (event->button == 3 && !spin->jump_to_limits)
01143                 start_spinning (spin, GTK_ARROW_DOWN, 10.*spin->adjustment->page_increment);
01144               else
01145                 spin->click_child = GTK_ARROW_DOWN;
01146             }
01147           return TRUE;
01148         }
01149       else
01150         return GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event);
01151     }
01152   return FALSE;
01153 }
01154 
01155 static gint
01156 gtk_spin_button_sci_button_release (GtkWidget      *widget,
01157                                 GdkEventButton *event)
01158 {
01159   GtkSpinbuttonsci *spin = GTK_SPIN_BUTTON_SCI (widget);
01160   gint arrow_size;
01161 
01162   arrow_size = spin_button_sci_get_arrow_size (spin);
01163 
01164   if (event->button == spin->button)
01165     {
01166       int click_child = spin->click_child;
01167 
01168       gtk_spin_button_sci_stop_spinning (spin);
01169 
01170       if (event->button == 3 && spin->jump_to_limits)
01171         {
01172           if (event->y >= 0 && event->x >= 0 && 
01173               event->y <= widget->requisition.height &&
01174               event->x <= arrow_size + 2 * widget->style->xthickness)
01175             {
01176               if (click_child == GTK_ARROW_UP &&
01177                   event->y <= widget->requisition.height / 2)
01178                 {
01179                   gdouble diff;
01180 
01181                   diff = spin->adjustment->upper - spin->adjustment->value;
01182                   if (diff > EPSILON)
01183                     gtk_spin_button_sci_real_spin (spin, diff);
01184                 }
01185               else if (click_child == GTK_ARROW_DOWN &&
01186                        event->y > widget->requisition.height / 2)
01187                 {
01188                   gdouble diff;
01189 
01190                   diff = spin->adjustment->value - spin->adjustment->lower;
01191                   if (diff > EPSILON)
01192                     gtk_spin_button_sci_real_spin (spin, -diff);
01193                 }
01194             }
01195         }                 
01196       spin_button_sci_redraw (spin);
01197 
01198       return TRUE;
01199     }
01200   else
01201     return GTK_WIDGET_CLASS (parent_class)->button_release_event (widget, event);
01202 }
01203 
01204 static gint
01205 gtk_spin_button_sci_motion_notify (GtkWidget      *widget,
01206                                GdkEventMotion *event)
01207 {
01208   GtkSpinbuttonsci *spin = GTK_SPIN_BUTTON_SCI (widget);
01209 
01210   if (spin->button)
01211     return FALSE;
01212 
01213   if (event->window == spin->panel)
01214     {
01215       gint y;
01216       
01217       gdk_window_get_pointer (spin->panel, NULL, &y, NULL);
01218   
01219       if (y <= widget->requisition.height / 2 && 
01220           spin->in_child == GTK_ARROW_DOWN)
01221         {
01222           spin->in_child = GTK_ARROW_UP;
01223           spin_button_sci_redraw (spin);
01224         }
01225       else if (y > widget->requisition.height / 2 && 
01226           spin->in_child == GTK_ARROW_UP)
01227         {
01228           spin->in_child = GTK_ARROW_DOWN;
01229           spin_button_sci_redraw (spin);
01230         }
01231       
01232       return FALSE;
01233     }
01234           
01235   return GTK_WIDGET_CLASS (parent_class)->motion_notify_event (widget, event);
01236 }
01237 
01238 static gint
01239 gtk_spin_button_sci_timer (GtkSpinbuttonsci *spin_button_sci)
01240 {
01241   gboolean retval = FALSE;
01242   
01243   GDK_THREADS_ENTER ();
01244 
01245   if (spin_button_sci->timer)
01246     {
01247       if (spin_button_sci->click_child == GTK_ARROW_UP)
01248         gtk_spin_button_sci_real_spin (spin_button_sci, spin_button_sci->timer_step);
01249       else
01250         gtk_spin_button_sci_real_spin (spin_button_sci, -spin_button_sci->timer_step);
01251 
01252       if (spin_button_sci->need_timer)
01253         {
01254           spin_button_sci->need_timer = FALSE;
01255           spin_button_sci->timer = g_timeout_add (SPIN_BUTTON_SCI_TIMER_DELAY, 
01256                                               (GSourceFunc) gtk_spin_button_sci_timer, 
01257                                               (gpointer) spin_button_sci);
01258         }
01259       else 
01260         {
01261           if (spin_button_sci->climb_rate > 0.0 && spin_button_sci->timer_step 
01262               < spin_button_sci->adjustment->page_increment)
01263             {
01264               if (spin_button_sci->timer_calls < MAX_TIMER_CALLS)
01265                 spin_button_sci->timer_calls++;
01266               else 
01267                 {
01268                   spin_button_sci->timer_calls = 0;
01269                   spin_button_sci->timer_step += spin_button_sci->climb_rate;
01270                 }
01271             }
01272           retval = TRUE;
01273         }
01274     }
01275 
01276   GDK_THREADS_LEAVE ();
01277 
01278   return retval;
01279 }
01280 
01281 static void
01282 gtk_spin_button_sci_value_changed (GtkAdjustment *adjustment,
01283                                GtkSpinbuttonsci *spin_button_sci)
01284 {
01285   gboolean return_val;
01286 
01287   g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
01288 
01289   return_val = FALSE;
01290   g_signal_emit (spin_button_sci, spinbuttonsci_signals[OUTPUT], 0, &return_val);
01291   if (return_val == FALSE)
01292     gtk_spin_button_sci_default_output (spin_button_sci);
01293 
01294   g_signal_emit (spin_button_sci, spinbuttonsci_signals[VALUE_CHANGED], 0);
01295 
01296   spin_button_sci_redraw (spin_button_sci);
01297   
01298   g_object_notify (G_OBJECT (spin_button_sci), "value");
01299 }
01300 
01301 static void
01302 gtk_spin_button_sci_real_change_value (GtkSpinbuttonsci *spin,
01303                                    GtkScrollType  scroll)
01304 {
01305   /* We don't test whether the entry is editable, since
01306    * this key binding conceptually corresponds to changing
01307    * the value with the buttons using the mouse, which
01308    * we allow for non-editable spin buttons.
01309    */
01310   switch (scroll)
01311     {
01312     case GTK_SCROLL_STEP_BACKWARD:
01313     case GTK_SCROLL_STEP_DOWN:
01314     case GTK_SCROLL_STEP_LEFT:
01315       gtk_spin_button_sci_real_spin (spin, -spin->timer_step);
01316       
01317       if (spin->climb_rate > 0.0 && spin->timer_step
01318           < spin->adjustment->page_increment)
01319         {
01320           if (spin->timer_calls < MAX_TIMER_CALLS)
01321             spin->timer_calls++;
01322           else 
01323             {
01324               spin->timer_calls = 0;
01325               spin->timer_step += spin->climb_rate;
01326             }
01327         }
01328       break;
01329       
01330     case GTK_SCROLL_STEP_FORWARD:
01331     case GTK_SCROLL_STEP_UP:
01332     case GTK_SCROLL_STEP_RIGHT:
01333       gtk_spin_button_sci_real_spin (spin, spin->timer_step);
01334       
01335       if (spin->climb_rate > 0.0 && spin->timer_step
01336           < spin->adjustment->page_increment)
01337         {
01338           if (spin->timer_calls < MAX_TIMER_CALLS)
01339             spin->timer_calls++;
01340           else 
01341             {
01342               spin->timer_calls = 0;
01343               spin->timer_step += spin->climb_rate;
01344             }
01345         }
01346       break;
01347       
01348     case GTK_SCROLL_PAGE_BACKWARD:
01349     case GTK_SCROLL_PAGE_DOWN:
01350     case GTK_SCROLL_PAGE_LEFT:
01351       gtk_spin_button_sci_real_spin (spin, -spin->adjustment->page_increment);
01352       break;
01353       
01354     case GTK_SCROLL_PAGE_FORWARD:
01355     case GTK_SCROLL_PAGE_UP:
01356     case GTK_SCROLL_PAGE_RIGHT:
01357       gtk_spin_button_sci_real_spin (spin, spin->adjustment->page_increment);
01358       break;
01359       
01360     case GTK_SCROLL_START:
01361       if (spin->jump_to_limits)
01362       {
01363         gdouble diff = spin->adjustment->value - spin->adjustment->lower;
01364         if (diff > EPSILON)
01365           gtk_spin_button_sci_real_spin (spin, -diff);
01366       }
01367       break;
01368       
01369     case GTK_SCROLL_END:
01370       if (spin->jump_to_limits)
01371       {
01372         gdouble diff = spin->adjustment->upper - spin->adjustment->value;
01373         if (diff > EPSILON)
01374           gtk_spin_button_sci_real_spin (spin, diff);
01375       }
01376       break;
01377       
01378     default:
01379       g_warning ("Invalid scroll type %d for GtkSpinbuttonsci::change-value", scroll);
01380       break;
01381     }
01382   
01383   gtk_spin_button_sci_update (spin);
01384 }
01385 
01386 static gint
01387 gtk_spin_button_sci_key_release (GtkWidget   *widget,
01388                              GdkEventKey *event)
01389 {
01390   GtkSpinbuttonsci *spin = GTK_SPIN_BUTTON_SCI (widget);
01391 
01392   /* We only get a release at the end of a key repeat run, so reset the timer_step */
01393   spin->timer_step = spin->adjustment->step_increment;
01394   spin->timer_calls = 0;
01395   
01396   return TRUE;
01397 }
01398 
01399 static void
01400 gtk_spin_button_sci_snap (GtkSpinbuttonsci *spin_button_sci,
01401                       gdouble        val)
01402 {
01403   gdouble inc;
01404   gdouble tmp;
01405 
01406   inc = spin_button_sci->adjustment->step_increment;
01407   if (inc == 0)
01408     return;
01409   
01410   tmp = (val - spin_button_sci->adjustment->lower) / inc;
01411   if (tmp - floor (tmp) < ceil (tmp) - tmp)
01412     val = spin_button_sci->adjustment->lower + floor (tmp) * inc;
01413   else
01414     val = spin_button_sci->adjustment->lower + ceil (tmp) * inc;
01415 
01416   if (fabs (val - spin_button_sci->adjustment->value) > EPSILON)
01417     gtk_adjustment_set_value (spin_button_sci->adjustment, val);
01418   else
01419     {
01420       gint return_val = FALSE;
01421       g_signal_emit (spin_button_sci, spinbuttonsci_signals[OUTPUT], 0, &return_val);
01422       if (return_val == FALSE)
01423         gtk_spin_button_sci_default_output (spin_button_sci);
01424     }
01425 }
01426 
01427 static void
01428 gtk_spin_button_sci_activate (GtkEntry *entry)
01429 {
01430   if (entry->editable)
01431     gtk_spin_button_sci_update (GTK_SPIN_BUTTON_SCI (entry));
01432 
01433   /* Chain up so that entry->activates_default is honored */
01434   parent_class->activate (entry);
01435 }
01436 
01437 static void
01438 gtk_spin_button_sci_insert_text (GtkEditable *editable,
01439                              const gchar *new_text,
01440                              gint         new_text_length,
01441                              gint        *position)
01442 {
01443   GtkEntry *entry = GTK_ENTRY (editable);
01444   GtkSpinbuttonsci *spin = GTK_SPIN_BUTTON_SCI (editable);
01445   GtkEditableClass *parent_editable_iface = g_type_interface_peek (parent_class, GTK_TYPE_EDITABLE);
01446  
01447   if (spin->numeric)
01448     {
01449       struct lconv *lc;
01450       gboolean sign;
01451       gint dotpos = -1;
01452       gint i;
01453       GdkWChar pos_sign;
01454       GdkWChar neg_sign;
01455       gint entry_length;
01456 
01457       entry_length = entry->text_length;
01458 
01459       lc = localeconv ();
01460 
01461       if (*(lc->negative_sign))
01462         neg_sign = *(lc->negative_sign);
01463       else 
01464         neg_sign = '-';
01465 
01466       if (*(lc->positive_sign))
01467         pos_sign = *(lc->positive_sign);
01468       else 
01469         pos_sign = '+';
01470 
01471       for (sign=0, i=0; i<entry_length; i++)
01472         if ((entry->text[i] == neg_sign) ||
01473             (entry->text[i] == pos_sign))
01474           {
01475             sign = 1;
01476             break;
01477           }
01478 
01479       if (sign && !(*position))
01480         return;
01481 
01482       for (dotpos=-1, i=0; i<entry_length; i++)
01483         if (entry->text[i] == *(lc->decimal_point))
01484           {
01485             dotpos = i;
01486             break;
01487           }
01488 
01489       if (dotpos > -1 && *position > dotpos &&
01490           (gint)spin->digits - entry_length
01491             + dotpos - new_text_length + 1 < 0)
01492         return;
01493 
01494       for (i = 0; i < new_text_length; i++)
01495         {
01496           if (new_text[i] == neg_sign || new_text[i] == pos_sign)
01497             {
01498               if (sign || (*position) || i)
01499                 return;
01500               sign = TRUE;
01501             }
01502           else if (new_text[i] == *(lc->decimal_point))
01503             {
01504               if (!spin->digits || dotpos > -1 || 
01505                   (new_text_length - 1 - i + entry_length
01506                     - *position > (gint)spin->digits)) 
01507                 return;
01508               dotpos = *position + i;
01509             }
01510           else if (new_text[i] < 0x30 || new_text[i] > 0x39)
01511             return;
01512         }
01513     }
01514 
01515   parent_editable_iface->insert_text (editable, new_text,
01516                                       new_text_length, position);
01517 }
01518 
01519 static void
01520 gtk_spin_button_sci_real_spin (GtkSpinbuttonsci *spin_button_sci,
01521                            gdouble        increment)
01522 {
01523   GtkAdjustment *adj;
01524   gdouble new_value = 0.0;
01525   
01526   adj = spin_button_sci->adjustment;
01527 
01528   new_value = adj->value + increment;
01529 
01530   if (increment > 0)
01531     {
01532       if (spin_button_sci->wrap)
01533         {
01534           if (fabs (adj->value - adj->upper) < EPSILON)
01535             new_value = adj->lower;
01536           else if (new_value > adj->upper)
01537             new_value = adj->upper;
01538         }
01539       else
01540         new_value = MIN (new_value, adj->upper);
01541     }
01542   else if (increment < 0) 
01543     {
01544       if (spin_button_sci->wrap)
01545         {
01546           if (fabs (adj->value - adj->lower) < EPSILON)
01547             new_value = adj->upper;
01548           else if (new_value < adj->lower)
01549             new_value = adj->lower;
01550         }
01551       else
01552         new_value = MAX (new_value, adj->lower);
01553     }
01554 
01555   if (fabs (new_value - adj->value) > EPSILON)
01556     gtk_adjustment_set_value (adj, new_value);
01557 
01558   spin_button_sci_redraw (spin_button_sci);
01559 }
01560 
01561 static gint
01562 gtk_spin_button_sci_default_input (GtkSpinbuttonsci *spin_button_sci,
01563                                gdouble       *new_val)
01564 {
01565   gchar *err = NULL;
01566 
01567   *new_val = g_strtod (gtk_entry_get_text (GTK_ENTRY (spin_button_sci)), &err);
01568   if (*err)
01569     return GTK_INPUT_ERROR;
01570   else
01571     return FALSE;
01572 }
01573 
01574 static gint
01575 gtk_spin_button_sci_default_output (GtkSpinbuttonsci *spin_button_sci)
01576 {
01577    gchar *buf = NULL;
01578   
01579   if (spin_button_sci->sci_suffix)
01580           buf = g_strdup_printf ("%0.*f %s *", 
01581                                  spin_button_sci->digits, 
01582                                  spin_button_sci->adjustment->value, 
01583                                  spin_button_sci->sci_suffix);
01584   else
01585           buf = g_strdup_printf ("%0.*f", 
01586                                  spin_button_sci->digits, 
01587                                  spin_button_sci->adjustment->value);
01588   
01589   if (strcmp (buf, gtk_entry_get_text (GTK_ENTRY (spin_button_sci))))
01590     gtk_entry_set_text (GTK_ENTRY (spin_button_sci), buf);
01591   g_free (buf);
01592   return FALSE;
01593 }
01594 
01595 
01596 /***********************************************************
01597  ***********************************************************
01598  ***                  Public interface                   ***
01599  ***********************************************************
01600  ***********************************************************/
01601 
01602 
01603 void
01604 gtk_spin_button_sci_configure (GtkSpinbuttonsci  *spin_button_sci,
01605                            GtkAdjustment  *adjustment,
01606                            gdouble         climb_rate,
01607                            guint           digits)
01608 {
01609   g_return_if_fail (GTK_IS_SPIN_BUTTON_SCI (spin_button_sci));
01610 
01611   if (adjustment)
01612     gtk_spin_button_sci_set_adjustment (spin_button_sci, adjustment);
01613   else
01614     adjustment = spin_button_sci->adjustment;
01615 
01616   g_object_freeze_notify (G_OBJECT (spin_button_sci));
01617   if (spin_button_sci->digits != digits) 
01618     {
01619       spin_button_sci->digits = digits;
01620       g_object_notify (G_OBJECT (spin_button_sci), "digits");
01621     }
01622 
01623   if (spin_button_sci->climb_rate != climb_rate)
01624     {
01625       spin_button_sci->climb_rate = climb_rate;
01626       g_object_notify (G_OBJECT (spin_button_sci), "climb_rate");
01627     }
01628   g_object_thaw_notify (G_OBJECT (spin_button_sci));
01629 
01630   gtk_adjustment_value_changed (adjustment);
01631 }
01632 
01633 GtkWidget *
01634 gtk_spin_button_sci_new (GtkAdjustment *adjustment,
01635                      gdouble        climb_rate,
01636                      guint          digits)
01637 {
01638   GtkSpinbuttonsci *spin;
01639 
01640   if (adjustment)
01641     g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), NULL);
01642 
01643   spin = g_object_new (GTK_TYPE_SPIN_BUTTON_SCI, NULL);
01644 
01645   gtk_spin_button_sci_configure (spin, adjustment, climb_rate, digits);
01646 
01647   return GTK_WIDGET (spin);
01648 }
01649 
01664 GtkWidget *
01665 gtk_spin_button_sci_new_with_range (gdouble min,
01666                                 gdouble max,
01667                                 gdouble step)
01668 {
01669   GtkObject *adj;
01670   GtkSpinbuttonsci *spin;
01671   gint digits;
01672 
01673   g_return_val_if_fail (min < max, NULL);
01674   g_return_val_if_fail (step != 0.0, NULL);
01675 
01676   spin = g_object_new (GTK_TYPE_SPIN_BUTTON_SCI, NULL);
01677 
01678   adj = gtk_adjustment_new (min, min, max, step, 10 * step, 0);
01679 
01680   if (fabs (step) >= 1.0 || step == 0.0)
01681     digits = 0;
01682   else {
01683     digits = abs ((gint) floor (log10 (fabs (step))));
01684     if (digits > MAX_DIGITS)
01685       digits = MAX_DIGITS;
01686   }
01687 
01688   gtk_spin_button_sci_configure (spin, GTK_ADJUSTMENT (adj), step, digits);
01689 
01690   gtk_spin_button_sci_set_numeric (spin, TRUE);
01691 
01692   return GTK_WIDGET (spin);
01693 }
01694 
01695 /* Callback used when the spin button's adjustment changes.  We need to redraw
01696  * the arrows when the adjustment's range changes, and reevaluate our size request.
01697  */
01698 static void
01699 adjustment_changed_cb (GtkAdjustment *adjustment, gpointer data)
01700 {
01701   GtkSpinbuttonsci *spin_button_sci;
01702 
01703   spin_button_sci = GTK_SPIN_BUTTON_SCI (data);
01704 
01705   spin_button_sci->timer_step = spin_button_sci->adjustment->step_increment;
01706   gtk_widget_queue_resize (GTK_WIDGET (spin_button_sci));
01707 }
01708 
01716 void
01717 gtk_spin_button_sci_set_adjustment (GtkSpinbuttonsci *spin_button_sci,
01718                                 GtkAdjustment *adjustment)
01719 {
01720   g_return_if_fail (GTK_IS_SPIN_BUTTON_SCI (spin_button_sci));
01721 
01722   if (spin_button_sci->adjustment != adjustment)
01723     {
01724       if (spin_button_sci->adjustment)
01725         {
01726           g_signal_handlers_disconnect_by_func (spin_button_sci->adjustment,
01727                                                 gtk_spin_button_sci_value_changed,
01728                                                 spin_button_sci);
01729           g_signal_handlers_disconnect_by_func (spin_button_sci->adjustment,
01730                                                 adjustment_changed_cb,
01731                                                 spin_button_sci);
01732           g_object_unref (spin_button_sci->adjustment);
01733         }
01734       spin_button_sci->adjustment = adjustment;
01735       if (adjustment)
01736         {
01737           g_object_ref (adjustment);
01738           gtk_object_sink (GTK_OBJECT (adjustment));
01739           g_signal_connect (adjustment, "value_changed",
01740                             G_CALLBACK (gtk_spin_button_sci_value_changed),
01741                             spin_button_sci);
01742           g_signal_connect (adjustment, "changed",
01743                             G_CALLBACK (adjustment_changed_cb),
01744                             spin_button_sci);
01745           spin_button_sci->timer_step = spin_button_sci->adjustment->step_increment;
01746         }
01747 
01748       gtk_widget_queue_resize (GTK_WIDGET (spin_button_sci));
01749     }
01750 
01751   g_object_notify (G_OBJECT (spin_button_sci), "adjustment");
01752 }
01753 
01762 GtkAdjustment *
01763 gtk_spin_button_sci_get_adjustment (GtkSpinbuttonsci *spin_button_sci)
01764 {
01765   g_return_val_if_fail (GTK_IS_SPIN_BUTTON_SCI (spin_button_sci), NULL);
01766 
01767   return spin_button_sci->adjustment;
01768 }
01769 
01778 void
01779 gtk_spin_button_sci_set_digits (GtkSpinbuttonsci *spin_button_sci,
01780                             guint          digits)
01781 {
01782   g_return_if_fail (GTK_IS_SPIN_BUTTON_SCI (spin_button_sci));
01783 
01784   if (spin_button_sci->digits != digits)
01785     {
01786       spin_button_sci->digits = digits;
01787       gtk_spin_button_sci_value_changed (spin_button_sci->adjustment, spin_button_sci);
01788       g_object_notify (G_OBJECT (spin_button_sci), "digits");
01789       
01790       /* since lower/upper may have changed */
01791       gtk_widget_queue_resize (GTK_WIDGET (spin_button_sci));
01792     }
01793 }
01794 
01803 guint
01804 gtk_spin_button_sci_get_digits (GtkSpinbuttonsci *spin_button_sci)
01805 {
01806   g_return_val_if_fail (GTK_IS_SPIN_BUTTON_SCI (spin_button_sci), 0);
01807 
01808   return spin_button_sci->digits;
01809 }
01810 
01820 void
01821 gtk_spin_button_sci_set_increments (GtkSpinbuttonsci *spin_button_sci,
01822                                 gdouble        step,
01823                                 gdouble        page)
01824 {
01825   g_return_if_fail (GTK_IS_SPIN_BUTTON_SCI (spin_button_sci));
01826 
01827   spin_button_sci->adjustment->step_increment = step;
01828   spin_button_sci->adjustment->page_increment = page;
01829 }
01830 
01840 void
01841 gtk_spin_button_sci_get_increments (GtkSpinbuttonsci *spin_button_sci,
01842                                 gdouble       *step,
01843                                 gdouble       *page)
01844 {
01845   g_return_if_fail (GTK_IS_SPIN_BUTTON_SCI (spin_button_sci));
01846 
01847   if (step)
01848     *step = spin_button_sci->adjustment->step_increment;
01849   if (page)
01850     *page = spin_button_sci->adjustment->page_increment;
01851 }
01852 
01861 void
01862 gtk_spin_button_sci_set_range (GtkSpinbuttonsci *spin_button_sci,
01863                            gdouble        min,
01864                            gdouble        max)
01865 {
01866   gdouble value;
01867   
01868   g_return_if_fail (GTK_IS_SPIN_BUTTON_SCI (spin_button_sci));
01869 
01870   spin_button_sci->adjustment->lower = min;
01871   spin_button_sci->adjustment->upper = max;
01872 
01873   value = CLAMP (spin_button_sci->adjustment->value,
01874                  spin_button_sci->adjustment->lower,
01875                  (spin_button_sci->adjustment->upper - spin_button_sci->adjustment->page_size));
01876 
01877   if (value != spin_button_sci->adjustment->value)
01878     gtk_spin_button_sci_set_value (spin_button_sci, value);
01879 
01880   gtk_adjustment_changed (spin_button_sci->adjustment);
01881 }
01882 
01892 void
01893 gtk_spin_button_sci_get_range (GtkSpinbuttonsci *spin_button_sci,
01894                            gdouble       *min,
01895                            gdouble       *max)
01896 {
01897   g_return_if_fail (GTK_IS_SPIN_BUTTON_SCI (spin_button_sci));
01898 
01899   if (min)
01900     *min = spin_button_sci->adjustment->lower;
01901   if (max)
01902     *max = spin_button_sci->adjustment->upper;
01903 }
01904 
01912 void
01913 gtk_spin_button_sci_set_suffix     (GtkSpinbuttonsci  *spin_button_sci,
01914                                     gchar       *suffix)
01915 {
01916         if (spin_button_sci->sci_suffix)
01917                 g_free (spin_button_sci->sci_suffix);
01918         spin_button_sci->sci_suffix = NULL;
01919         if (suffix)
01920                 spin_button_sci->sci_suffix = g_strdup (suffix);
01921 }
01922 
01923 
01932 gdouble
01933 gtk_spin_button_sci_get_value (GtkSpinbuttonsci *spin_button_sci)
01934 {
01935   g_return_val_if_fail (GTK_IS_SPIN_BUTTON_SCI (spin_button_sci), 0.0);
01936 
01937   return spin_button_sci->adjustment->value;
01938 }
01939 
01948 gint
01949 gtk_spin_button_sci_get_value_as_int (GtkSpinbuttonsci *spin_button_sci)
01950 {
01951   gdouble val;
01952 
01953   g_return_val_if_fail (GTK_IS_SPIN_BUTTON_SCI (spin_button_sci), 0);
01954 
01955   val = spin_button_sci->adjustment->value;
01956   if (val - floor (val) < ceil (val) - val)
01957     return floor (val);
01958   else
01959     return ceil (val);
01960 }
01961 
01969 void 
01970 gtk_spin_button_sci_set_value (GtkSpinbuttonsci *spin_button_sci, 
01971                            gdouble        value)
01972 {
01973   g_return_if_fail (GTK_IS_SPIN_BUTTON_SCI (spin_button_sci));
01974 
01975   if (fabs (value - spin_button_sci->adjustment->value) > EPSILON)
01976     gtk_adjustment_set_value (spin_button_sci->adjustment, value);
01977   else
01978     {
01979       gint return_val = FALSE;
01980       g_signal_emit (spin_button_sci, spinbuttonsci_signals[OUTPUT], 0, &return_val);
01981       if (return_val == FALSE)
01982         gtk_spin_button_sci_default_output (spin_button_sci);
01983     }
01984 }
01985 
01994 void
01995 gtk_spin_button_sci_set_update_policy (GtkSpinbuttonsci             *spin_button_sci,
01996                                    GtkSpinbuttonsciUpdatePolicy  policy)
01997 {
01998   g_return_if_fail (GTK_IS_SPIN_BUTTON_SCI (spin_button_sci));
01999 
02000   if (spin_button_sci->update_policy != policy)
02001     {
02002       spin_button_sci->update_policy = policy;
02003       g_object_notify (G_OBJECT (spin_button_sci), "update_policy");
02004     }
02005 }
02006 
02016 GtkSpinbuttonsciUpdatePolicy
02017 gtk_spin_button_sci_get_update_policy (GtkSpinbuttonsci *spin_button_sci)
02018 {
02019   g_return_val_if_fail (GTK_IS_SPIN_BUTTON_SCI (spin_button_sci), GTK_SPIN_SCI_UPDATE_ALWAYS);
02020 
02021   return spin_button_sci->update_policy;
02022 }
02023 
02032 void
02033 gtk_spin_button_sci_set_numeric (GtkSpinbuttonsci  *spin_button_sci,
02034                              gboolean        numeric)
02035 {
02036   g_return_if_fail (GTK_IS_SPIN_BUTTON_SCI (spin_button_sci));
02037 
02038   numeric = numeric != FALSE;
02039 
02040   if (spin_button_sci->numeric != numeric)
02041     {
02042        spin_button_sci->numeric = numeric;
02043        g_object_notify (G_OBJECT (spin_button_sci), "numeric");
02044     }
02045 }
02046 
02056 gboolean
02057 gtk_spin_button_sci_get_numeric (GtkSpinbuttonsci *spin_button_sci)
02058 {
02059   g_return_val_if_fail (GTK_IS_SPIN_BUTTON_SCI (spin_button_sci), FALSE);
02060 
02061   return spin_button_sci->numeric;
02062 }
02063 
02072 void
02073 gtk_spin_button_sci_set_wrap (GtkSpinbuttonsci  *spin_button_sci,
02074                           gboolean        wrap)
02075 {
02076   g_return_if_fail (GTK_IS_SPIN_BUTTON_SCI (spin_button_sci));
02077 
02078   wrap = wrap != FALSE; 
02079 
02080   if (spin_button_sci->wrap != wrap)
02081     {
02082        spin_button_sci->wrap = (wrap != 0);
02083   
02084        g_object_notify (G_OBJECT (spin_button_sci), "wrap");
02085     }
02086 }
02087 
02098 gboolean
02099 gtk_spin_button_sci_get_wrap (GtkSpinbuttonsci *spin_button_sci)
02100 {
02101   g_return_val_if_fail (GTK_IS_SPIN_BUTTON_SCI (spin_button_sci), FALSE);
02102 
02103   return spin_button_sci->wrap;
02104 }
02105 
02106 static gint
02107 spin_button_sci_get_arrow_size (GtkSpinbuttonsci *spin_button_sci)
02108 {
02109   gint size = pango_font_description_get_size (GTK_WIDGET (spin_button_sci)->style->font_desc);
02110   gint arrow_size;
02111 
02112   arrow_size = MAX (PANGO_PIXELS (size), MIN_ARROW_WIDTH);
02113 
02114   return arrow_size - arrow_size % 2; /* force even */
02115 }
02116 
02126 static gint
02127 spin_button_sci_get_shadow_type (GtkSpinbuttonsci *spin_button_sci)
02128 {
02129   GtkShadowType rc_shadow_type;
02130 
02131   gtk_widget_style_get (GTK_WIDGET (spin_button_sci), "shadow_type", &rc_shadow_type, NULL);
02132 
02133   return rc_shadow_type;
02134 }
02135 
02144 void
02145 gtk_spin_button_sci_set_snap_to_ticks (GtkSpinbuttonsci *spin_button_sci,
02146                                    gboolean       snap_to_ticks)
02147 {
02148   guint new_val;
02149 
02150   g_return_if_fail (GTK_IS_SPIN_BUTTON_SCI (spin_button_sci));
02151 
02152   new_val = (snap_to_ticks != 0);
02153 
02154   if (new_val != spin_button_sci->snap_to_ticks)
02155     {
02156       spin_button_sci->snap_to_ticks = new_val;
02157       if (new_val && GTK_ENTRY (spin_button_sci)->editable)
02158         gtk_spin_button_sci_update (spin_button_sci);
02159       
02160       g_object_notify (G_OBJECT (spin_button_sci), "snap_to_ticks");
02161     }
02162 }
02163 
02173 gboolean
02174 gtk_spin_button_sci_get_snap_to_ticks (GtkSpinbuttonsci *spin_button_sci)
02175 {
02176   g_return_val_if_fail (GTK_IS_SPIN_BUTTON_SCI (spin_button_sci), FALSE);
02177 
02178   return spin_button_sci->snap_to_ticks;
02179 }
02180 
02190 void
02191 gtk_spin_button_sci_spin (GtkSpinbuttonsci *spin_button_sci,
02192                       GtkSpinsciType    direction,
02193                       gdouble        increment)
02194 {
02195   GtkAdjustment *adj;
02196   gdouble diff;
02197 
02198   g_return_if_fail (GTK_IS_SPIN_BUTTON_SCI (spin_button_sci));
02199   
02200   adj = spin_button_sci->adjustment;
02201 
02202   /* for compatibility with the 1.0.x version of this function */
02203   if (increment != 0 && increment != adj->step_increment &&
02204       (direction == GTK_SPIN_SCI_STEP_FORWARD ||
02205        direction == GTK_SPIN_SCI_STEP_BACKWARD))
02206     {
02207       if (direction == GTK_SPIN_SCI_STEP_BACKWARD && increment > 0)
02208         increment = -increment;
02209       direction = GTK_SPIN_SCI_USER_DEFINED;
02210     }
02211 
02212   switch (direction)
02213     {
02214     case GTK_SPIN_SCI_STEP_FORWARD:
02215 
02216       gtk_spin_button_sci_real_spin (spin_button_sci, adj->step_increment);
02217       break;
02218 
02219     case GTK_SPIN_SCI_STEP_BACKWARD:
02220 
02221       gtk_spin_button_sci_real_spin (spin_button_sci, -adj->step_increment);
02222       break;
02223 
02224     case GTK_SPIN_SCI_PAGE_FORWARD:
02225 
02226       gtk_spin_button_sci_real_spin (spin_button_sci, adj->page_increment);
02227       break;
02228 
02229     case GTK_SPIN_SCI_PAGE_BACKWARD:
02230 
02231       gtk_spin_button_sci_real_spin (spin_button_sci, -adj->page_increment);
02232       break;
02233 
02234     case GTK_SPIN_SCI_HOME:
02235 
02236       if (spin_button_sci->jump_to_limits)
02237       {
02238               diff = adj->value - adj->lower;
02239               if (diff > EPSILON)
02240                       gtk_spin_button_sci_real_spin (spin_button_sci, -diff);
02241       }
02242       break;
02243 
02244     case GTK_SPIN_SCI_END:
02245 
02246       if (spin_button_sci->jump_to_limits)
02247       {
02248               diff = adj->upper - adj->value;
02249               if (diff > EPSILON)
02250                       gtk_spin_button_sci_real_spin (spin_button_sci, diff);
02251       }
02252       break;
02253 
02254     case GTK_SPIN_SCI_USER_DEFINED:
02255 
02256       if (increment != 0)
02257         gtk_spin_button_sci_real_spin (spin_button_sci, increment);
02258       break;
02259 
02260     default:
02261       break;
02262     }
02263 }
02264 
02271 void 
02272 gtk_spin_button_sci_update (GtkSpinbuttonsci *spin_button_sci)
02273 {
02274   gdouble val;
02275   gint error = 0;
02276   gint return_val;
02277 
02278   g_return_if_fail (GTK_IS_SPIN_BUTTON_SCI (spin_button_sci));
02279 
02280   return_val = FALSE;
02281   g_signal_emit (spin_button_sci, spinbuttonsci_signals[INPUT], 0, &val, &return_val);
02282   if (return_val == FALSE)
02283     {
02284       return_val = gtk_spin_button_sci_default_input (spin_button_sci, &val);
02285       error = (return_val == GTK_INPUT_ERROR);
02286     }
02287   else if (return_val == GTK_INPUT_ERROR)
02288     error = 1;
02289 
02290   spin_button_sci_redraw (spin_button_sci);
02291 
02292   if (spin_button_sci->update_policy == GTK_SPIN_SCI_UPDATE_ALWAYS)
02293     {
02294       if (val < spin_button_sci->adjustment->lower)
02295         val = spin_button_sci->adjustment->lower;
02296       else if (val > spin_button_sci->adjustment->upper)
02297         val = spin_button_sci->adjustment->upper;
02298     }
02299   else if ((spin_button_sci->update_policy == GTK_SPIN_SCI_UPDATE_IF_VALID) && 
02300            (error ||
02301            val < spin_button_sci->adjustment->lower ||
02302            val > spin_button_sci->adjustment->upper))
02303     {
02304       gtk_spin_button_sci_value_changed (spin_button_sci->adjustment, spin_button_sci);
02305       return;
02306     }
02307 
02308   if (spin_button_sci->snap_to_ticks)
02309     gtk_spin_button_sci_snap (spin_button_sci, val);
02310   else
02311     {
02312       if (fabs (val - spin_button_sci->adjustment->value) > EPSILON)
02313         gtk_adjustment_set_value (spin_button_sci->adjustment, val);
02314       else
02315         {
02316           return_val = FALSE;
02317           g_signal_emit (spin_button_sci, spinbuttonsci_signals[OUTPUT], 0,
02318                          &return_val);
02319           if (return_val == FALSE)
02320             gtk_spin_button_sci_default_output (spin_button_sci);
02321         }
02322     }
02323 }
02324 
02325 static void
02326 spin_button_sci_redraw (GtkSpinbuttonsci *spin_button_sci)
02327 {
02328   GtkWidget *widget;
02329 
02330   widget = GTK_WIDGET (spin_button_sci);
02331 
02332   if (GTK_WIDGET_DRAWABLE (widget))
02333     {
02334       gtk_widget_queue_draw (widget);
02335 
02336       /* We must invalidate the panel window ourselves, because it
02337        * is not a child of widget->window
02338        */
02339       gdk_window_invalidate_rect (spin_button_sci->panel, NULL, TRUE);
02340     }
02341 }        

Generated on Sat Apr 1 09:03:57 2006 for GXSM by  doxygen 1.4.6