dtcalc: change from obsoleted MAXFLOAT to FLT_MAX from std C
[oweals/cde.git] / cde / lib / DtWidget / SpinBox.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $XConsortium: SpinBox.c /main/9 1996/10/29 12:49:58 cde-hp $
24  *
25  * (c) Copyright 1996 Digital Equipment Corporation.
26  * (c) Copyright 1993,1994,1996 Hewlett-Packard Company.
27  * (c) Copyright 1993,1994,1996 International Business Machines Corp.
28  * (c) Copyright 1993,1994,1996 Sun Microsystems, Inc.
29  * (c) Copyright 1993,1994,1996 Novell, Inc. 
30  * (c) Copyright 1996 FUJITSU LIMITED.
31  * (c) Copyright 1996 Hitachi.
32  */
33
34 /***********************************************************
35 Copyright 1993 Interleaf, Inc.
36
37 Permission to use, copy, modify, and distribute this software
38 and its documentation for any purpose without fee is granted,
39 provided that the above copyright notice appear in all copies
40 and that both copyright notice and this permission notice appear
41 in supporting documentation, and that the name of Interleaf not
42 be used in advertising or publicly pertaining to distribution of
43 the software without specific written prior permission.
44
45 Interleaf makes no representation about the suitability of this
46 software for any purpose. It is provided "AS IS" without any
47 express or implied warranty. 
48 ******************************************************************/
49
50 /*
51  * (C) Copyright 1991,1992, 1993
52  * Interleaf, Inc.
53  * 9 Hillside Avenue, 
54  * Waltham, MA  02154
55  *
56  * SpinBox.c (DtSpinBoxWidget):
57  *
58  * I wanted a margin around the widget (outside the shadow, like buttons), 
59  * so that the spin_box could be made the smae size as a 
60  * push-button, etc.  The bulletin-board widget always puts the shadow at 
61  * the outside edge of the widget, so spin_box is a sublcass of
62  * manager, and we do everything ourselves.
63  * 
64  * One must be carefull when using Dimension (for core width and height).
65  * Dimension is an unsigned short.  This causes problems when subtracting
66  * and ending up with what should be a negative number (but it doesn't).
67  * All child widget positioning is done by the spin_box.  We don't
68  * use any heavy-weight forms, etc. to help us out.
69  * 
70  * There is no padding when editable.  If using a label, give it a
71  * small margin, so it doesn't run up against the side of our
72  * shadow or the arrow.
73  *
74  * Make some of the SpinBox functions common, so they can be shared
75  * with ComboBox.
76  *
77  * Known bugs:
78  *          Changing margin_width or margin_height resources when the
79  *          spin_box has focus will probably result in display glitches.
80  */
81 /*
82  * The DtSpinBox widget is rigged with the Motif widget binary compatibilit
83  * mechanism. All Motif-specific changes for this mechanism are preceded
84  * by a comment including the string "MotifBc".
85  *
86  * For a description of the Motif widget binary compatibility mechanism
87  * see the reference manual entry on XmResolveAllPartOffsets().
88  *
89  */
90
91 #include <Dt/DtMsgsP.h>
92 #include <Xm/DrawP.h>
93 #include <Xm/XmP.h>
94 #include <Xm/RepType.h>
95 #include "SpinBoxP.h"
96 #include "DtWidgetI.h"
97
98 #include <Xm/XmPrivate.h>    /* _XmShellIsExclusive */
99
100 #ifdef I18N_MSG
101 #include <langinfo.h>
102 #endif 
103
104
105 /*
106  * MotifBc
107  */
108 #define DtSpinBoxIndex (XmManagerIndex + 1)
109 static XmOffsetPtr ipot; /* Instance part offset table */
110 static XmOffsetPtr cpot; /* Constraint part offset table */
111
112 static void     ClassInitialize ();
113 static void     Initialize (DtSpinBoxWidget request, 
114                                DtSpinBoxWidget new, ArgList given_args, 
115                                Cardinal *num_args);
116 static XmNavigability WidgetNavigable (DtSpinBoxWidget spin);
117 static void     _SpinBoxFocusIn (DtSpinBoxWidget spin, XEvent *event, 
118                                        char **params, Cardinal *num_params);
119 static void     _SpinBoxFocusOut (DtSpinBoxWidget spin, XEvent *event,
120                                         char **params, Cardinal *num_params);
121 static void     DrawHighlight (DtSpinBoxWidget spin, Boolean clear);
122 static void     _SpinBoxUp (DtSpinBoxWidget spin, 
123                                   XEvent *event, char **params, 
124                                   Cardinal *num_params);
125 static void     _SpinBoxDown (DtSpinBoxWidget spin, 
126                                     XEvent *event, char **params, 
127                                     Cardinal *num_params);
128 static void     _SpinBoxLeft (DtSpinBoxWidget spin, 
129                                     XEvent *event, char **params, 
130                                     Cardinal *num_params);
131 static void     _SpinBoxRight (DtSpinBoxWidget spin, 
132                                      XEvent *event, char **params, 
133                                      Cardinal *num_params);
134 static void     _SpinBoxBeginLine (DtSpinBoxWidget spin, 
135                                          XEvent *event, char **params, 
136                                          Cardinal *num_params);
137 static void     _SpinBoxEndLine (DtSpinBoxWidget spin, 
138                                        XEvent *event, char **params, 
139                                        Cardinal *num_params);
140 static void     _SpinBoxGetFocus (DtSpinBoxWidget spin,
141                                        XEvent *event, char **params,
142                                        Cardinal *num_params);
143 static void     _SpinBoxPrevTabGroup (DtSpinBoxWidget spin,
144                                        XEvent *event, char **params,
145                                        Cardinal *num_params);
146 static void     _SpinBoxNextTabGroup (DtSpinBoxWidget spin,
147                                        XEvent *event, char **params,
148                                        Cardinal *num_params);
149
150 static void     CheckResources (DtSpinBoxWidget spin);
151 static void     Destroy (DtSpinBoxWidget spin);
152 static void     Resize (DtSpinBoxWidget spin);
153 static void     Redisplay (DtSpinBoxWidget w, XEvent *event, 
154                               Region region);
155 static XtGeometryResult GeometryManager (Widget w, 
156                                             XtWidgetGeometry *request, 
157                                             XtWidgetGeometry *reply);
158 static void     SetSpinBoxSize (DtSpinBoxWidget spin);
159 static void     ForceChildSizes (DtSpinBoxWidget spin);
160 static void     CalculateSizes (DtSpinBoxWidget spin,
161                                 Dimension *pwidth,
162                                 Dimension *pheight,
163                                 Dimension *parrow_width);
164 static void     LayoutChildren (DtSpinBoxWidget spin);
165 static Boolean  SetValues (DtSpinBoxWidget current, 
166                               DtSpinBoxWidget request, 
167                               DtSpinBoxWidget new);
168 static void     ClearShadow (DtSpinBoxWidget w, Boolean all);
169 static void     DrawShadow (DtSpinBoxWidget w);
170 static void     StoreResourceInfo (DtSpinBoxPart *spin_p,
171                                       DtSpinBoxPart *old_p,
172                                       Boolean do_items);
173 static char*    GetTextString (XmString xm_string);
174 static void     SetTextFieldData (DtSpinBoxWidget spin);
175 static void     SetMaximumLabelSize (DtSpinBoxPart *spin_p);
176 static void     SetLabelData (DtSpinBoxWidget spin);
177 static void     timer_dispatch (XtPointer client_data, XtIntervalId *id);
178 static void     TextFieldActivate (DtSpinBoxPart *spin_p);
179 static Boolean  SendCallback (DtSpinBoxWidget spin, XEvent *event,
180                                  Boolean value_changed, int position,
181                                  float current, Boolean crossed);
182 static void     FinishUpDown (DtSpinBoxWidget spin, 
183                                  XtPointer arrow_call_data, int new_position,
184                                  float new_current, Boolean crossed);
185 static void     up_cb (Widget w, XtPointer client_data, 
186                           XtPointer call_data);
187 static void     down_cb (Widget w, XtPointer client_data, 
188                             XtPointer call_data);
189 static void     disarm_cb (Widget w, XtPointer client_data, 
190                               XtPointer call_data);
191 static void     grab_leave_cb (Widget w, XtPointer client_data, 
192                                   XEvent *event, Boolean *dispatch);
193 static void     text_losing_focus_cb (Widget w, XtPointer client_data,
194                                          XtPointer call_data);
195 static void     text_activate_cb (Widget w, XtPointer client_data,
196                                      XtPointer call_data);
197 static void     text_focus_cb (Widget w, XtPointer client_data,
198                                   XtPointer call_data);
199 static XmImportOperator _XmSetSyntheticResForChild (Widget widget,
200                                                        int offset, 
201                                                        XtArgVal * value);
202
203 static XmString InitLabel = NULL;
204
205 /*
206  * MotifBc
207  */
208 #define Label(w) XmField(w,ipot,DtSpinBox,label,Widget)
209 #define UpArrow(w) XmField(w,ipot,DtSpinBox,up_arrow,Widget)
210 #define DownArrow(w) XmField(w,ipot,DtSpinBox,down_arrow,Widget)
211 #define WhichArrow(w) XmField(w,ipot,DtSpinBox,which_arrow,unsigned char)
212 #define InitCb(w) XmField(w,ipot,DtSpinBox,init_cb,Boolean)
213 #define Grabbed(w) XmField(w,ipot,DtSpinBox,grabbed,Boolean)
214 #define Base(w) XmField(w,ipot,DtSpinBox,base,int)
215 #define Min(w) XmField(w,ipot,DtSpinBox,min,float)
216 #define Max(w) XmField(w,ipot,DtSpinBox,max,float)
217 #define Increment(w) XmField(w,ipot,DtSpinBox,increment,float)
218 #define Current(w) XmField(w,ipot,DtSpinBox,current,float)
219 #define FloatFormat(w) (String) &(XmField(w,ipot,DtSpinBox,float_format,char *))
220 #define OldWidth(w) XmField(w,ipot,DtSpinBox,old_width,Dimension)
221 #define OldHeight(w) XmField(w,ipot,DtSpinBox,old_height,Dimension)
222 #define LabelMaxLength(w) XmField(w,ipot,DtSpinBox,label_max_length,Dimension)
223 #define LabelMaxHeight(w) XmField(w,ipot,DtSpinBox,label_max_height,Dimension)
224
225 #define ArrowSensitivity(w) XmField(w,ipot,DtSpinBox,arrow_sensitivity,unsigned char)
226 #define DecimalPoints(w) XmField(w,ipot,DtSpinBox,decimal_points,short)
227 #define NumericIncrement(w) XmField(w,ipot,DtSpinBox,numeric_increment,int)
228 #define Maximum(w) XmField(w,ipot,DtSpinBox,maximum,int)
229 #define Minimum(w) XmField(w,ipot,DtSpinBox,minimum,int)
230 #define ItemCount(w) XmField(w,ipot,DtSpinBox,item_count,int)
231 #define Position(w) XmField(w,ipot,DtSpinBox,position,int)
232 #define ChildType(w) XmField(w,ipot,DtSpinBox,child_type,unsigned char)
233 #define Items(w) XmField(w,ipot,DtSpinBox,items,XmStringTable)
234 #define ActivateCallback(w) XmField(w,ipot,DtSpinBox,activate_callback,XtCallbackList)
235 #define Alignment(w) XmField(w,ipot,DtSpinBox,alignment,unsigned char)
236 #define ArrowLayout(w) XmField(w,ipot,DtSpinBox,arrow_layout,unsigned char)
237 #define ArrowSize(w) XmField(w,ipot,DtSpinBox,arrow_size,Dimension)
238 #define TextColumns(w) XmField(w,ipot,DtSpinBox,text_columns,short)
239 #define Editable(w) XmField(w,ipot,DtSpinBox,editable,Boolean)
240 #define FocusCallback(w) XmField(w,ipot,DtSpinBox,focus_callback,XtCallbackList)
241 #define InitialDelay(w) XmField(w,ipot,DtSpinBox,initial_delay,unsigned int)
242 #define LosingFocusCallback(w) XmField(w,ipot,DtSpinBox,losing_focus_callback,XtCallbackList)
243 #define MarginHeight(w) XmField(w,ipot,DtSpinBox,margin_height,Dimension)
244 #define MarginWidth(w) XmField(w,ipot,DtSpinBox,margin_width,Dimension)
245 #define TextMaxLength(w) XmField(w,ipot,DtSpinBox,text_max_length,int)
246 #define ModifyVerifyCallback(w) XmField(w,ipot,DtSpinBox,modify_verify_callback,XtCallbackList)
247 #define RecomputeSize(w) XmField(w,ipot,DtSpinBox,recompute_size,Boolean)
248 #define RepeatDelay(w) XmField(w,ipot,DtSpinBox,repeat_delay,unsigned int)
249 #define Text(w) XmField(w,ipot,DtSpinBox,text,Widget)
250 #define ValueChangedCallback(w) XmField(w,ipot,DtSpinBox,value_changed_callback,XtCallbackList)
251 #define Wrap(w) XmField(w,ipot,DtSpinBox,wrap,Boolean)
252 #define Timer(w) XmField(w,ipot,DtSpinBox,timer,XtIntervalId)
253
254 /* SpinBox and Superclass resource macros*/
255 #define PUnitType(w)        w->primitive.unit_type
256 #define MUnitType(w)        w->manager.unit_type
257 #define MFgPixel(w)         w->manager.foreground
258 #define SPIN_SHADOW(w)      w->manager.shadow_thickness
259 #define CBgPixel(w)         w->core.background_pixel
260 #define CBgPixmap(w)        w->core.background_pixmap
261 #define Width(w)            w->core.width
262 #define Height(w)           w->core.height
263 #define SPIN_MARGIN_W(w)    MarginWidth(w)
264 #define SPIN_MARGIN_H(w)    MarginHeight(w)
265 #define MAXINT 2147483647  /* Taken from TextF.c */
266 #define DEFAULT_COL 20 
267
268 /* USL: Label get Focus */
269 static XtTranslations child_trans_label;
270 /* USL: Keyboard only for Text */
271 static XtTranslations child_trans_text;
272 static XtTranslations child_trans_arrow;
273
274 static XtTranslations child_trans;
275 static XtTranslations spin_trans;
276
277
278 static char const SpinBoxTranslationTable[] = "\
279         <FocusIn>:              SpinBoxFocusIn() \n\
280         <FocusOut>:             SpinBoxFocusOut() \n\
281         <Key>osfUp:             SpinBoxUp() \n\
282         <Key>osfDown:           SpinBoxDown() \n\
283         <Key>osfRight:          SpinBoxRight() \n\
284         <Key>osfLeft:           SpinBoxLeft() \n\
285         <Key>osfBeginLine:      SpinBoxBeginLine() \n\
286         <Key>osfEndLine:        SpinBoxEndLine() \n\
287 ";
288
289 static char const SpinBoxChildTranslationTable[] = "\
290         <Key>osfUp:             SpinBoxUp(child) \n\
291         <Key>osfDown:           SpinBoxDown(child) \n\
292         <Key>osfRight:          SpinBoxRight(child) \n\
293         <Key>osfLeft:           SpinBoxLeft(child) \n\
294         <Key>osfBeginLine:      SpinBoxBeginLine(child) \n\
295         <Key>osfEndLine:        SpinBoxEndLine(child) \n\
296 ";
297
298 /* #5: Label get focus */
299 static char const SpinBoxLabelTranslationTable[] = "\
300         <Key>osfUp:             SpinBoxUp(child) \n\
301         <Key>osfDown:           SpinBoxDown(child) \n\
302         <Key>osfRight:          SpinBoxRight(child) \n\
303         <Key>osfLeft:           SpinBoxLeft(child) \n\
304         <Key>osfBeginLine:      SpinBoxBeginLine(child) \n\
305         <Key>osfEndLine:        SpinBoxEndLine(child) \n\
306         <Key>osfDown:        SpinBoxGetFocus() \n\
307         <Btn1Down>,<Btn1Up>:  SpinBoxGetFocus() \n\
308         <Key>osfSelect:       SpinBoxGetFocus() \n\
309         ~s ~m ~a <Key>space:  SpinBoxGetFocus() \n\
310 ";
311
312 /* USL: Keyboard Only Traversing During Editable-Mode */
313 static char const SpinBoxTextTranslationTable[] = "\
314         <Key>osfUp:             SpinBoxUp(child) SpinBoxRight(child)\n\
315         <Key>osfDown:           SpinBoxDown(child) SpinBoxLeft(child)\n\
316 ";
317 static char const SpinBoxArrowTranslationTable[] = "\
318         <Key>osfUp:             SpinBoxUp(child) \n\
319         <Key>osfDown:           SpinBoxDown(child) \n\
320         <Key>osfRight:          SpinBoxRight(child) \n\
321         <Key>osfLeft:           SpinBoxLeft(child) \n\
322         <Key>osfBeginLine:      SpinBoxBeginLine(child) \n\
323         <Key>osfEndLine:        SpinBoxEndLine(child) \n\
324         s ~m ~a <Key>Tab:    SpinBoxPrevTabGroup()\n\
325         ~m ~a <Key>Tab:      SpinBoxNextTabGroup()\n\
326 ";
327
328
329 static XtActionsRec SpinBoxActionTable[] = {
330        {"SpinBoxFocusIn",       (XtActionProc)_SpinBoxFocusIn},
331        {"SpinBoxFocusOut",      (XtActionProc)_SpinBoxFocusOut},
332        {"SpinBoxUp",            (XtActionProc)_SpinBoxUp},
333        {"SpinBoxDown",  (XtActionProc)_SpinBoxDown},
334        {"SpinBoxRight", (XtActionProc)_SpinBoxRight},
335        {"SpinBoxLeft",  (XtActionProc)_SpinBoxLeft},
336        {"SpinBoxBeginLine",     (XtActionProc)_SpinBoxBeginLine},
337        {"SpinBoxEndLine",       (XtActionProc)_SpinBoxEndLine},
338        {"SpinBoxGetFocus",      (XtActionProc)_SpinBoxGetFocus},
339        {"SpinBoxPrevTabGroup",   (XtActionProc)_SpinBoxPrevTabGroup},
340        {"SpinBoxNextTabGroup",   (XtActionProc)_SpinBoxNextTabGroup},
341 };
342
343 /* DtSpinBoxWidget resources */
344 /* MotifBc */
345 #define offset(field) XtOffset(DtSpinBoxWidget, field)
346 #define DtOffset(field) XmPartOffset(DtSpinBox,field)
347 static XmPartResource resources[] = {
348     {XmNshadowThickness, XmCShadowThickness, XmRHorizontalDimension, 
349          sizeof(Dimension), offset(manager.shadow_thickness),
350          XmRImmediate, (XtPointer)TEXT_FIELD_SHADOW},
351
352     /* Common resources */
353     {DtNactivateCallback, DtCCallback, XmRCallback, sizeof(XtCallbackList),
354          DtOffset(activate_callback), XmRCallback, 
355          (XtPointer)NULL},
356     {DtNalignment, DtCAlignment, XmRAlignment, sizeof(unsigned char),
357          DtOffset(alignment), XmRImmediate, 
358          (XtPointer)DtALIGNMENT_BEGINNING},
359     {DtNarrowLayout, DtCArrowLayout, DtRArrowLayout, sizeof(unsigned char),
360          DtOffset(arrow_layout), XmRImmediate, 
361          (XtPointer)DtARROWS_END},
362     {DtNarrowSensitivity, DtCArrowSensitivity, DtRArrowSensitivity,
363          sizeof(unsigned char), DtOffset(arrow_sensitivity),
364          XmRImmediate, (XtPointer)DtARROWS_SENSITIVE},
365     {DtNspinBoxChildType, DtCSpinBoxChildType, DtRSpinBoxChildType, 
366          sizeof(unsigned char),
367          DtOffset(child_type), XmRImmediate, (XtPointer)DtSTRING},
368     {DtNcolumns, DtCColumns, XmRShort, sizeof(short),
369          DtOffset(text_columns), XmRImmediate, (XtPointer)DEFAULT_COL},
370     {DtNdecimalPoints, DtCDecimalPoints, XmRShort, sizeof( short ),
371          DtOffset(decimal_points), XmRImmediate, (XtPointer)0},
372     {DtNeditable, DtCEditable, XmRBoolean, sizeof(Boolean),
373          DtOffset(editable), XmRImmediate, (XtPointer)TRUE},
374     {DtNfocusCallback, DtCCallback, XmRCallback, sizeof(XtCallbackList),
375          DtOffset(focus_callback), XmRCallback, 
376          (XtPointer)NULL},
377     {DtNincrementValue, DtCIncrementValue, XmRInt, sizeof(int),
378          DtOffset(numeric_increment), XmRImmediate, (XtPointer)1},
379     {DtNinitialDelay, DtCInitialDelay, XmRInt, sizeof(unsigned int),
380          DtOffset(initial_delay), XmRImmediate, (XtPointer)250},
381     {DtNnumValues, DtCNumValues, DtRNumValues, sizeof(int),
382          DtOffset(item_count), XmRImmediate, (XtPointer)0},
383     {DtNvalues, DtCItems, XmRXmStringTable, sizeof(XmStringTable),
384          DtOffset(items), XmRImmediate, (XtPointer)NULL},
385     {DtNlosingFocusCallback, DtCCallback, XmRCallback, sizeof(XtCallbackList),
386          DtOffset(losing_focus_callback), XmRCallback, 
387          (XtPointer)NULL},
388     {DtNmarginHeight, DtCMarginHeight, XmRVerticalDimension, sizeof(Dimension),
389          DtOffset(margin_height), XmRImmediate, (XtPointer)MARGIN},
390     {DtNmarginWidth, DtCMarginWidth, XmRHorizontalDimension, sizeof(Dimension),
391          DtOffset(margin_width), XmRImmediate, (XtPointer)MARGIN},
392     {DtNmaximumValue, DtCMaximumValue, XmRInt, sizeof(int), 
393          DtOffset(maximum), XmRImmediate, (XtPointer)10},
394     {DtNmaxLength, DtCMaxLength, XmRInt, sizeof(int),
395          DtOffset(text_max_length), XmRImmediate, (XtPointer)MAXINT},
396     {DtNminimumValue, DtCMinimumValue, XmRInt, sizeof(int), 
397          DtOffset(minimum), XmRImmediate, (XtPointer)0},
398     {DtNmodifyVerifyCallback, DtCCallback, XmRCallback, 
399          sizeof(XtCallbackList), DtOffset(modify_verify_callback),
400          XmRCallback, (XtPointer)NULL},
401     {DtNposition, DtCPosition, XmRInt, sizeof(int),
402          DtOffset(position), XmRImmediate, (XtPointer)0},
403     {DtNrecomputeSize, DtCRecomputeSize, XmRBoolean, sizeof(Boolean),
404          DtOffset(recompute_size), XmRImmediate, (XtPointer)TRUE},
405     {DtNrepeatDelay, DtCRepeatDelay, XmRInt, sizeof(unsigned int),
406          DtOffset(repeat_delay), XmRImmediate, (XtPointer)200},
407     {DtNtextField, DtCTextField, XmRWidget, sizeof(Widget),
408          DtOffset(text), XmRImmediate, (XtPointer)NULL},
409     {DtNvalueChangedCallback, DtCCallback, XmRCallback, 
410          sizeof(XtCallbackList), DtOffset(value_changed_callback),
411          XmRCallback, (XtPointer)NULL},
412     {DtNwrap, DtCWrap, XmRBoolean, sizeof(Boolean),
413          DtOffset(wrap), XmRImmediate, (XtPointer)TRUE},
414 };
415
416 /* Synthetic resources.  Only used for Motif API arrowSize right now */
417 static XmSyntheticResource syn_resources[] = {
418     {DtNarrowSize, sizeof(Dimension), DtOffset(arrow_size), 
419          _DtSpinBoxGetArrowSize, _XmSetSyntheticResForChild},
420     {DtNmarginHeight, sizeof(Dimension), DtOffset(margin_height),
421         XmeFromVerticalPixels, XmeToVerticalPixels},
422     {DtNmarginWidth, sizeof(Dimension), DtOffset(margin_width),
423         XmeFromHorizontalPixels, XmeToHorizontalPixels},
424 };
425 #undef DtOffset
426 #undef offset
427
428 /* Need Class Extension for widget navigation */
429 static XmBaseClassExtRec baseClassExtRec = {
430     NULL,
431     NULLQUARK,
432     XmBaseClassExtVersion,
433     sizeof(XmBaseClassExtRec),
434     (XtInitProc)NULL,                   /* InitializePrehook    */
435     (XtSetValuesFunc)NULL,              /* SetValuesPrehook     */
436     (XtInitProc)NULL,                   /* InitializePosthook   */
437     (XtSetValuesFunc)NULL,              /* SetValuesPosthook    */
438     NULL,                               /* secondaryObjectClass */
439     (XtInitProc)NULL,                   /* secondaryCreate      */
440     (XmGetSecResDataFunc)NULL,          /* getSecRes data       */
441     { 0 },                              /* fastSubclass flags   */
442     (XtArgsProc)NULL,                   /* getValuesPrehook     */
443     (XtArgsProc)NULL,                   /* getValuesPosthook    */
444     (XtWidgetClassProc)NULL,            /* classPartInitPrehook */
445     (XtWidgetClassProc)NULL,            /* classPartInitPosthook*/
446     NULL,                               /* ext_resources        */
447     NULL,                               /* compiled_ext_resources*/
448     0,                                  /* num_ext_resources    */
449     FALSE,                              /* use_sub_resources    */
450     (XmWidgetNavigableProc)WidgetNavigable,
451                                         /* widgetNavigable      */
452     (XmFocusChangeProc)NULL,            /* focusChange          */
453     (XmWrapperData)NULL                 /* wrapperData          */
454 };
455
456 /*
457  * Define Class Record.
458  */
459 externaldef(dtspinboxclassrec) DtSpinBoxClassRec dtSpinBoxClassRec =
460 {
461     {           /* core_class fields      */
462     (WidgetClass)&(xmManagerClassRec),          /* superclass         */    
463     (String)"DtSpinBox",                        /* class_name         */    
464     (Cardinal)sizeof(DtSpinBoxPart),            /* widget_size        */    
465     (XtProc)ClassInitialize,                    /* class_initialize   */    
466     (XtWidgetClassProc)NULL,                    /* class_part_init    */    
467     (XtEnum)FALSE,                              /* class_inited       */    
468     (XtInitProc)Initialize,                     /* initialize         */    
469     (XtArgsProc)NULL,                           /* initialize_hook    */    
470     (XtRealizeProc)XtInheritRealize,            /* realize            */    
471     (XtActionList)SpinBoxActionTable,           /* actions            */    
472     (Cardinal)XtNumber(SpinBoxActionTable),     /* num_actions        */    
473     (XtResourceList)resources,                  /* resources          */    
474     (Cardinal)XtNumber(resources),              /* num_resources      */    
475     (XrmClass)NULLQUARK,                        /* xrm_class          */    
476     (Boolean)TRUE,                              /* compress_motion    */    
477     (XtEnum)XtExposeCompressMaximal,            /* compress_exposure  */    
478     (Boolean)TRUE,                              /* compress_enterleave*/    
479     (Boolean)FALSE,                             /* visible_interest   */    
480     (XtWidgetProc)Destroy,                      /* destroy            */    
481     (XtWidgetProc)Resize,                       /* resize             */    
482     (XtExposeProc)Redisplay,                    /* expose             */    
483     (XtSetValuesFunc)SetValues,                 /* set_values         */    
484     (XtArgsFunc)NULL,                           /* set values hook    */    
485     (XtAlmostProc)XtInheritSetValuesAlmost,     /* set values almost  */    
486     (XtArgsProc)NULL,                           /* get values hook    */    
487     (XtAcceptFocusProc)NULL,                    /* accept_focus       */    
488     (XtVersionType)XtVersionDontCheck,          /* Version            */    
489     (XtPointer)NULL,                            /* PRIVATE cb list    */
490     (String)XtInheritTranslations,              /* tm_table           */
491     (XtGeometryHandler)XtInheritQueryGeometry,  /* query_geom         */
492     (XtStringProc)XtInheritDisplayAccelerator,  /* display_accelerator*/
493     (XtPointer)&baseClassExtRec                 /* extension          */
494     },
495     {           /* composite_class fields */
496     (XtGeometryHandler)GeometryManager,         /* geometry_manager   */     
497     (XtWidgetProc)XtInheritChangeManaged,       /* change_managed     */     
498     (XtWidgetProc)XtInheritInsertChild,         /* insert_child       */     
499     (XtWidgetProc)XtInheritDeleteChild,         /* delete_child       */     
500     (XtPointer)NULL                             /* extension          */     
501     },
502     {           /* constraint_class fields */
503     (XtResourceList)NULL,                       /* resources          */     
504     (Cardinal)0,                                /* num_resources      */     
505     (Cardinal)0,                                /* constraint_size    */     
506     (XtInitProc)NULL,                           /* initialize         */     
507     (XtWidgetProc)NULL,                         /* destroy            */     
508     (XtSetValuesFunc)NULL,                      /* set_values         */     
509     (XtPointer)NULL                             /* extension          */     
510     },
511     {           /* manager class     */
512     (String)XtInheritTranslations,              /* translations       */     
513     (XmSyntheticResource*)syn_resources,        /* syn resources      */     
514     (int)XtNumber(syn_resources),               /* num syn_resources  */     
515     (XmSyntheticResource*)NULL,                 /* get_cont_resources */     
516     (int)0,                                     /* num_get_cont_resources */ 
517     (XmParentProcessProc)XmInheritParentProcess,/* parent_process     */     
518     (XtPointer)NULL                             /* extension          */     
519     },
520     {           /* spin_box_class fields */     
521     (Boolean)0,
522     }
523 };
524
525 externaldef(dtspinboxwidgetclass) WidgetClass dtSpinBoxWidgetClass =
526                                                 (WidgetClass)&dtSpinBoxClassRec;
527
528 static XmRepTypeId _DtRID_SB_ARROW_SENSITIVITY_TYPE;
529 static String _DtArrowSensitivityNames[] = {
530         "arrows_insensitive",
531         "arrows_increment_sensitive",
532         "arrows_decrement_sensitive",
533         "arrows_sensitive"
534 };
535
536 /* 
537  * Must set up the record type for the class extensions to work.
538  */
539 static void
540 ClassInitialize(void)
541 {
542     baseClassExtRec.record_type = XmQmotif;
543 /*
544  * MotifBc
545  */
546     XmResolveAllPartOffsets(dtSpinBoxWidgetClass, &ipot, &cpot);
547
548     child_trans = XtParseTranslationTable(SpinBoxChildTranslationTable);
549     spin_trans = XtParseTranslationTable(SpinBoxTranslationTable);
550
551     child_trans_label=XtParseTranslationTable(SpinBoxLabelTranslationTable);
552     child_trans_text=XtParseTranslationTable(SpinBoxTextTranslationTable);
553     child_trans_arrow=XtParseTranslationTable(SpinBoxArrowTranslationTable);
554
555     _DtRID_SB_ARROW_SENSITIVITY_TYPE =
556       XmRepTypeRegister(DtRArrowSensitivity, _DtArrowSensitivityNames,
557                         NULL, XtNumber(_DtArrowSensitivityNames));
558
559     InitLabel = XmStringCreateLocalized(SB_LABEL);
560 }
561
562 /*
563  * SpinBox initialization function.  This builds the widgets inside
564  * our widget, to get the correct layout.  If the editable resource
565  * is TRUE, we create a textField; if FALSE, we create a label.  If the
566  * user changes this resource later, we will create the other widget
567  * (textField or Label).  We don't want to carry backage from both
568  * widgets if the user never changes the editable resource.
569  */
570 static void
571 Initialize(     DtSpinBoxWidget request,
572                 DtSpinBoxWidget new,
573                 ArgList given_args,
574                 Cardinal *num_args)
575 {   /* MotifBc */
576     DtSpinBoxPart *spin_p = (DtSpinBoxPart*)
577         &(XmField(new,ipot,DtSpinBox,label,Widget));
578     char *widget_name;
579     Arg args[20];
580     int n;
581     /* Resolution Independent */
582     unsigned char unit_type = MUnitType(new);
583
584     /* Overwrite the manager's focusIn and focusOut translations */
585     XtOverrideTranslations((Widget)new, spin_trans);
586
587     widget_name = XtMalloc(strlen(XtName((Widget)new)) + 10);
588
589     Text(new) = (Widget)NULL;
590     Label(new) = (Widget)NULL;
591     OldWidth(new) = 0;
592     OldHeight(new) = 0;
593     InitCb(new) = TRUE;
594     Grabbed(new) = FALSE;
595
596     CheckResources(new);
597
598     /*
599      * Create the text or label depending on editable resource.
600      */
601     if (Editable(new)) {
602         sprintf(widget_name, "%s_TF", XtName((Widget)new));
603         n = 0;
604         XtSetArg(args[n], XmNmaxLength, TextMaxLength(new)); n++;
605         XtSetArg(args[n], XmNmarginWidth, TEXT_CONTEXT_MARGIN); n++;
606         XtSetArg(args[n], XmNmarginHeight, 2); n++;
607         /* Resolution Independent */
608         if (unit_type != XmPIXELS) {
609                 XtSetArg(args[n], XmNunitType, XmPIXELS); n++;
610         }
611         Text(new) = XtCreateManagedWidget(widget_name,
612                                              xmTextFieldWidgetClass,
613                                              (Widget)new, args, n);
614         XtAddCallback(Text(new), XmNlosingFocusCallback, 
615                       text_losing_focus_cb, (XtPointer)new);
616         XtAddCallback(Text(new), XmNactivateCallback, 
617                       text_activate_cb, (XtPointer)new);
618         XtAddCallback(Text(new), XmNfocusCallback, 
619                       text_focus_cb, (XtPointer)new);
620         /* USL */
621         XtOverrideTranslations((Widget)Text(new), child_trans_text);
622         n = 0;
623         if (TextColumns(request) == DEFAULT_COL && Width(request)) {
624                 Dimension width;
625                 CalculateSizes(new, &width, NULL, NULL);
626                 XtSetArg(args[n], XmNwidth, width); n++;
627         }
628         else {
629                 XtSetArg(args[n], XmNcolumns, TextColumns(new)); n++;
630         }
631         if (unit_type != XmPIXELS) {
632                 XtSetArg(args[n], XmNunitType, unit_type); n++;
633         }                
634         XtSetValues(Text(new), args, n);
635     }
636     else {
637         sprintf(widget_name, "%s_Label", XtName((Widget)new));
638         SPIN_SHADOW(new) = LABEL_SHADOW;
639         n = 0;
640         XtSetArg(args[n], XmNalignment, Alignment(new)); n++;
641         XtSetArg(args[n], XmNrecomputeSize, FALSE); n++;
642         XtSetArg(args[n], XmNlabelString, InitLabel); n++;
643         XtSetArg(args[n], XmNmarginLeft, LABEL_PADDING); n++;
644         XtSetArg(args[n], XmNmarginRight, LABEL_PADDING); n++;
645         XtSetArg(args[n], XmNmarginWidth, TEXT_CONTEXT_MARGIN); n++;
646         XtSetArg(args[n], XmNmarginHeight, 2); n++;
647         if (unit_type != XmPIXELS) {
648                 XtSetArg(args[n], XmNunitType, XmPIXELS); n++;
649         }
650         Label(new) = XtCreateManagedWidget(widget_name, xmLabelWidgetClass,
651                                               (Widget)new, args, n);
652         /* USL */
653         XtOverrideTranslations((Widget)Label(new), child_trans_label);
654         n = 0;
655         if (unit_type != XmPIXELS) {
656                 XtSetArg(args[n], XmNunitType, unit_type); n++;
657         }                
658         if (Width(new)) {
659                 Dimension width;
660                 CalculateSizes(new, &width, NULL, NULL);
661                 XtSetArg(args[n], XmNwidth,  width); n++;
662         }
663         if (n>0)
664                 XtSetValues(Label(new), args, n);
665     }
666
667     /*
668      * Create the 2 ArrowWidgets.
669      */
670     sprintf(widget_name, "%s_Up", XtName((Widget)new));
671     n = 0;
672     if (ArrowLayout(new) == DtARROWS_SPLIT) {
673         XtSetArg(args[n], XmNarrowDirection, XmARROW_RIGHT); n++;
674     }
675     XtSetArg(args[n], XmNhighlightThickness, 0); n++;
676     XtSetArg(args[n], XmNshadowThickness, 0); n++;
677     XtSetArg(args[n], XmNtraversalOn, FALSE); n++;
678     XtSetArg(args[n], XmNforeground, CBgPixel(new)); n++;
679     UpArrow(new) = XtCreateManagedWidget(widget_name, 
680                                              xmArrowButtonWidgetClass,
681                                              (Widget)new, args, n);
682     XtOverrideTranslations((Widget)UpArrow(new), child_trans_arrow);
683
684     sprintf(widget_name, "%s_Down", XtName((Widget)new));
685     if (ArrowLayout(new) == DtARROWS_SPLIT) {
686         XtSetArg(args[n], XmNarrowDirection, XmARROW_LEFT); n++;
687     }
688     else {
689         XtSetArg(args[n], XmNarrowDirection, XmARROW_DOWN); n++;
690     }
691     DownArrow(new) = XtCreateManagedWidget(widget_name, 
692                                                xmArrowButtonWidgetClass,
693                                                (Widget)new, args, n);
694     XtOverrideTranslations((Widget)DownArrow(new), child_trans_arrow);
695
696     /* Set sensitivity of arrows (up arrow is right arrow) */
697     if ((ArrowSensitivity(new) == DtARROWS_INSENSITIVE) ||
698         (ArrowSensitivity(new) == DtARROWS_DECREMENT_SENSITIVE)) 
699         XtSetSensitive(UpArrow(new), FALSE);
700     if ((ArrowSensitivity(new) == DtARROWS_INSENSITIVE) ||
701         (ArrowSensitivity(new) == DtARROWS_INCREMENT_SENSITIVE)) 
702         XtSetSensitive(DownArrow(new), FALSE);
703
704     /* 
705      * Arm causes the value to change and the timer to start.
706      * Disarm (leaveNotify from grab) causes the timer to stop.
707      */
708     XtAddCallback(UpArrow(new), XmNarmCallback, up_cb, (XtPointer)new);
709     XtAddCallback(UpArrow(new), XmNdisarmCallback, disarm_cb, 
710                   (XtPointer)new);
711     XtAddEventHandler(UpArrow(new), LeaveWindowMask, FALSE, grab_leave_cb, 
712                       (XtPointer)new);
713     XtAddCallback(DownArrow(new), XmNarmCallback, down_cb, (XtPointer)new);
714     XtAddCallback(DownArrow(new), XmNdisarmCallback, disarm_cb, 
715                   (XtPointer)new);
716     XtAddEventHandler(DownArrow(new), LeaveWindowMask, FALSE, 
717                       grab_leave_cb, (XtPointer)new);
718
719     /* Initialize everything based on what the resource values are */
720     StoreResourceInfo(spin_p, NULL, TRUE);
721
722     /*
723      * Set initial value in text or label if items was specified
724      */
725     if (Editable(new) == FALSE) {
726         SetLabelData(new);
727         SetMaximumLabelSize(spin_p);
728     }
729     else
730     {
731         SetTextFieldData(new);
732     }
733     SetSpinBoxSize(new);
734     LayoutChildren(new);
735     XtFree(widget_name);
736
737     /* Store Converter for DtNmaximumValue, DtNminimumValue, DtNincrementValue,
738      */
739     XtSetTypeConverter(XmRString, DtRMaximumValue, XtCvtStringToInt, NULL, 0, 
740             XtCacheAll, NULL);
741     XtSetTypeConverter(XmRString, DtRMinimumValue, XtCvtStringToInt, NULL, 0, 
742             XtCacheAll, NULL);
743     XtSetTypeConverter(XmRString, DtRIncrementValue, XtCvtStringToInt, NULL, 0, 
744             XtCacheAll, NULL);
745     XtSetTypeConverter(XmRString, DtRNumValues, XtCvtStringToInt, NULL, 0, 
746             XtCacheAll, NULL);
747     /*
748      * this is so these resources can be set uniformly through XtCreateWidget
749      * or through defaults file as *spin.backgound
750      */
751
752     n=0;
753     XtSetArg(args[n],XmNbackground,CBgPixel(new));n++;
754     XtSetArg(args[n],XmNbackgroundPixmap,CBgPixmap(new));n++;
755     XtSetArg(args[n],XmNforeground,MFgPixel(new));n++;
756     if(Text(new))
757         XtSetValues (Text(new),args,n);
758     if(Label(new))
759         XtSetValues (Label(new),args,n);
760     if(UpArrow(new))
761         XtSetValues (UpArrow(new),args,n);
762     if(DownArrow(new))
763         XtSetValues (DownArrow(new),args,n);
764
765 }
766
767
768 /*
769  * Allow the manager to gain focus if not editable.  If editable (using
770  * text-field), then let the toolkit give focus to the text-field.
771  */
772 static XmNavigability
773 WidgetNavigable(DtSpinBoxWidget spin)
774 {   
775     XmNavigationType nav_type = ((XmManagerWidget)spin)->manager.navigation_type;
776
777     if (spin->core.sensitive &&  spin->core.ancestor_sensitive &&
778         ((XmManagerWidget)spin)->manager.traversal_on) {
779         if ((nav_type == XmSTICKY_TAB_GROUP) ||
780             (nav_type == XmEXCLUSIVE_TAB_GROUP) ||
781             ((nav_type == XmTAB_GROUP) &&
782              !_XmShellIsExclusive((Widget)spin))) {
783             if (Editable(spin))
784                 return(XmDESCENDANTS_TAB_NAVIGABLE);
785             else
786                 return(XmTAB_NAVIGABLE);
787         }
788         return(XmDESCENDANTS_NAVIGABLE);
789     }
790     return(XmNOT_NAVIGABLE);
791 }
792
793 /* 
794  * The spin_box gets focus.
795  */
796 static void 
797 _SpinBoxFocusIn(        DtSpinBoxWidget spin,
798                         XEvent *event,
799                         char **params,
800                         Cardinal *num_params)
801 {
802     DrawHighlight(spin, FALSE);
803 }
804
805 /* 
806  * The spin_box loses focus.
807  */
808 static void 
809 _SpinBoxFocusOut(       DtSpinBoxWidget spin,
810                         XEvent *event,
811                         char **params,
812                         Cardinal *num_params)
813 {
814     DrawHighlight(spin, TRUE);
815 }
816
817 /*
818  * This function gets called whenever we draw or clear the shadow (to
819  * redraw highlight during resize, etc), as well as during focus_in
820  * and focus_out events.
821  */
822 static void
823 DrawHighlight(  DtSpinBoxWidget spin,
824                 Boolean clear)
825 {
826     XRectangle rect[4] ;
827
828     if (XtIsRealized((Widget)spin)) {
829         if (clear) {
830             rect[0].x = rect[1].x = rect[2].x = 0;
831             rect[3].x = OldWidth(spin) - SPIN_MARGIN_W(spin);
832             rect[0].y = rect[2].y = rect[3].y = 0 ;
833             rect[1].y = OldHeight(spin) - SPIN_MARGIN_H(spin);
834             rect[0].width = rect[1].width = OldWidth(spin);
835             rect[2].width = rect[3].width = SPIN_MARGIN_W(spin);
836             rect[0].height = rect[1].height = SPIN_MARGIN_H(spin);
837             rect[2].height = rect[3].height = OldHeight(spin);
838             XFillRectangles(XtDisplayOfObject((Widget)spin),
839                             XtWindowOfObject((Widget)spin), 
840                             spin->manager.background_GC, rect, 4);
841         }
842         else if (XmGetFocusWidget((Widget)spin) == (Widget)spin) {
843             rect[0].x = rect[1].x = rect[2].x = 0;
844             rect[3].x = XtWidth(spin) - SPIN_MARGIN_W(spin);
845             rect[0].y = rect[2].y = rect[3].y = 0 ;
846             rect[1].y = XtHeight(spin) - SPIN_MARGIN_H(spin);
847             rect[0].width = rect[1].width = XtWidth(spin);
848             rect[2].width = rect[3].width = SPIN_MARGIN_W(spin);
849             rect[0].height = rect[1].height = SPIN_MARGIN_H(spin);
850             rect[2].height = rect[3].height = XtHeight(spin);
851             XFillRectangles(XtDisplayOfObject((Widget)spin),
852                             XtWindowOfObject((Widget)spin), 
853                             spin->manager.highlight_GC, rect, 4);
854         }
855     }
856 }
857
858
859 /*
860  * osfUp virtual key hit.  Simulate hitting the up arrow.
861  */
862 static void 
863 _SpinBoxUp(     DtSpinBoxWidget spin,
864                 XEvent *event,
865                 char **params,
866                 Cardinal *num_params)
867 {
868     if (*num_params != 0) /* params means label or arrows */
869         spin = (DtSpinBoxWidget)XtParent(spin);
870
871     if (ArrowLayout(spin) != DtARROWS_SPLIT) {
872         up_cb((Widget)UpArrow(spin), (XtPointer)spin, NULL);
873         disarm_cb((Widget)UpArrow(spin), (XtPointer)spin, NULL);
874     }
875 }
876
877 /*
878  * osfDown virtual key hit.  Simulate hitting the down arrow.
879  */
880 static void 
881 _SpinBoxDown(DtSpinBoxWidget spin,
882                 XEvent *event,
883                 char **params,
884                 Cardinal *num_params)
885 {
886     if (*num_params != 0) /* params means label or arrows */
887         spin = (DtSpinBoxWidget)XtParent(spin);
888
889     if (ArrowLayout(spin) != DtARROWS_SPLIT) {
890         down_cb((Widget)DownArrow(spin), (XtPointer)spin, NULL);
891         disarm_cb((Widget)DownArrow(spin), (XtPointer)spin, NULL);
892     }
893 }
894
895 /*
896  * osfRight virtual key hit.  Simulate hitting the up arrow.
897  */
898 static void 
899 _SpinBoxRight(DtSpinBoxWidget spin,
900                 XEvent *event,
901                 char **params,
902                 Cardinal *num_params)
903 {
904     if (*num_params != 0) /* params means label or arrows */
905         spin = (DtSpinBoxWidget)XtParent(spin);
906
907     if (ArrowLayout(spin) == DtARROWS_SPLIT) {
908         up_cb((Widget)UpArrow(spin), (XtPointer)spin, NULL);
909         disarm_cb((Widget)UpArrow(spin), (XtPointer)spin, NULL);
910     }
911 }
912
913 /*
914  * osfLeft virtual key hit.  Simulate hitting the down arrow.
915  */
916 static void 
917 _SpinBoxLeft(DtSpinBoxWidget spin,
918                 XEvent *event,
919                 char **params,
920                 Cardinal *num_params)
921 {
922     if (*num_params != 0) /* params means label or arrows */
923         spin = (DtSpinBoxWidget)XtParent(spin);
924
925     if (ArrowLayout(spin) == DtARROWS_SPLIT) {
926         down_cb((Widget)DownArrow(spin), (XtPointer)spin, NULL);
927         disarm_cb((Widget)DownArrow(spin), (XtPointer)spin, NULL);
928     }
929 }
930
931 /*
932  * osfBeginLine virtual key hit.  Go to first item.
933  */
934 static void 
935 _SpinBoxBeginLine(      DtSpinBoxWidget spin,
936                         XEvent *event,
937                         char **params,
938                         Cardinal *num_params)
939 {
940     int new_position;
941     float new_current;
942     
943     if (*num_params != 0) /* params means label or arrows */
944         spin = (DtSpinBoxWidget)XtParent(spin);
945
946     if (ChildType(spin) == DtNUMERIC) {
947         new_position = Minimum(spin);
948         new_current = Min(spin);
949     }
950     else {
951         new_position = 0;
952     }
953     if (SendCallback(spin, event, FALSE, new_position,
954                      new_current, FALSE) == TRUE) {
955         /* User said yes, so set widget values */
956         Position(spin) = new_position;
957         Current(spin) = new_current;
958         if (Editable(spin))
959             SetTextFieldData(spin);
960         else
961             SetLabelData(spin);
962         
963         /* send value_changed callback */
964         (void)SendCallback(spin, event, TRUE, Position(spin),
965                            Current(spin), FALSE);
966     }
967 }
968
969 /*
970  * osfEndLine virtual key hit.  Go to last item.
971  */
972 static void 
973 _SpinBoxEndLine(        DtSpinBoxWidget spin,
974                         XEvent *event,
975                         char **params,
976                         Cardinal *num_params)
977 {
978     int new_position;
979     float new_current;
980     
981     if (*num_params != 0) /* params means label or arrows */
982         spin = (DtSpinBoxWidget)XtParent(spin);
983
984     if (ChildType(spin) == DtNUMERIC) {
985         new_position = Maximum(spin);
986         new_current = Max(spin);
987     }
988     else {
989         new_position = ItemCount(spin) - 1;
990     }
991     if (SendCallback(spin, event, FALSE, new_position,
992                      new_current, FALSE) == TRUE) {
993         /* User said yes, so set widget values */
994         Position(spin) = new_position;
995         Current(spin) = new_current;
996         if (Editable(spin))
997             SetTextFieldData(spin);
998         else
999             SetLabelData(spin);
1000         
1001         /* send value_changed callback */
1002         (void)SendCallback(spin, event, TRUE, Position(spin),
1003                            Current(spin), FALSE);
1004     }
1005 }
1006
1007 /*
1008  * USL: Get Focus for SpinBox when hit its label part
1009  */
1010 static void
1011 _SpinBoxGetFocus(     DtSpinBoxWidget spin,
1012                         XEvent *event,
1013                         char **params,
1014                         Cardinal *num_params)
1015 {
1016     XmProcessTraversal((Widget)XtParent(spin),
1017                         (XmTraversalDirection) XmTRAVERSE_CURRENT);
1018 }
1019
1020 /*
1021  * USL: Process Focus Traversal for SpinBox when cursor is in its arrow part
1022  */
1023 static void
1024 _SpinBoxPrevTabGroup(DtSpinBoxWidget spin,
1025                         XEvent *event,
1026                         char **params,
1027                         Cardinal *num_params)
1028 {
1029     XmProcessTraversal((Widget)XtParent(spin),
1030                         (XmTraversalDirection) XmTRAVERSE_PREV_TAB_GROUP);
1031 }
1032
1033 static void
1034 _SpinBoxNextTabGroup(DtSpinBoxWidget spin,
1035                         XEvent *event,
1036                         char **params,
1037                         Cardinal *num_params)
1038 {
1039     XmProcessTraversal((Widget)XtParent(spin),
1040                         (XmTraversalDirection) XmTRAVERSE_NEXT_TAB_GROUP);
1041 }
1042
1043 /*
1044  * This function goes through most of the resources and makes sure 
1045  * they have legal values.
1046  */
1047 static void
1048 CheckResources(DtSpinBoxWidget spin)
1049 {
1050     if (!XmRepTypeValidValue(_DtRID_SB_ARROW_SENSITIVITY_TYPE,
1051                              ArrowSensitivity(spin), (Widget)spin)) {
1052         XtWarning(SB_ARROW_SENSITIVE);
1053         ArrowSensitivity(spin) = DtARROWS_SENSITIVE;
1054     }
1055     if ((Alignment(spin) != DtALIGNMENT_CENTER) && 
1056         (Alignment(spin) != DtALIGNMENT_BEGINNING) &&
1057         (Alignment(spin) != DtALIGNMENT_END)) {
1058         XtWarning(SB_ALIGNMENT);
1059         Alignment(spin) = DtALIGNMENT_CENTER;
1060     }
1061     if (InitialDelay(spin) == 0) {
1062         XtWarning(SB_INIT_DELAY);
1063         InitialDelay(spin) = 250;
1064     }
1065     if ((ArrowLayout(spin) != DtARROWS_FLAT_BEGINNING) && 
1066         (ArrowLayout(spin) != DtARROWS_FLAT_END) &&
1067         (ArrowLayout(spin) != DtARROWS_SPLIT) &&
1068         (ArrowLayout(spin) != DtARROWS_BEGINNING) &&
1069         (ArrowLayout(spin) != DtARROWS_END)) {
1070         XtWarning(SB_ARROW_LAYOUT);
1071         ArrowLayout(spin) = DtARROWS_BEGINNING;
1072     }
1073     if (RepeatDelay(spin) == 0) {
1074         XtWarning(SB_REPEAT_DELAY);
1075         RepeatDelay(spin) = 200;
1076     }
1077     if (ItemCount(spin) < 0) {
1078         XtWarning(SB_ITEM_COUNT);
1079         ItemCount(spin) = 0;
1080     }
1081     if ((ChildType(spin) == DtSTRING) &&
1082         ((Position(spin) < 0) ||
1083          ((Position(spin) >= ItemCount(spin)) &&
1084           (ItemCount(spin) > 0)))) {
1085         XtWarning(SB_POSITION_STRING);
1086         Position(spin) = 0;
1087     }
1088     if ((DecimalPoints(spin) < 0) ||
1089         (DecimalPoints(spin) > MAX_FLOAT_DECIMALS)) {
1090         XtWarning(SB_DECIMAL_POINTS);
1091         DecimalPoints(spin) = 0;
1092     }
1093     if (Minimum(spin) > Maximum(spin)) {
1094         XtWarning(SB_MIN_MAX);
1095         Minimum(spin) = Maximum(spin);
1096     }
1097     if ((ChildType(spin) == DtNUMERIC) &&
1098         ((Position(spin) < Minimum(spin)) ||
1099          (Position(spin) > Maximum(spin)) ||
1100          ((Position(spin) % NumericIncrement(spin)) != 0))) {
1101         XtWarning(SB_POSITION_NUMERIC);
1102         Position(spin) = Minimum(spin);
1103     }
1104 }
1105
1106
1107 /*
1108  * Destroy procedure called by the toolkit.
1109  */
1110 static void 
1111 Destroy(DtSpinBoxWidget spin)
1112 {
1113     int i;
1114
1115     if ((int)Timer(spin))
1116       {
1117         XtRemoveTimeOut(Timer(spin));
1118         Timer(spin) = (XtIntervalId)NULL;
1119       }
1120
1121     if (ItemCount(spin)>0) {
1122         for (i = 0; i < ItemCount(spin); i++) {
1123             XmStringFree((Items(spin))[i]);
1124             }
1125         XtFree((char*)(Items(spin)));
1126     }
1127
1128     /*
1129      * Don't remove callbacks and event handlers set on the children,
1130      * this has already been done by Xt (children are cleaned up before
1131      * the parent).
1132      */
1133 }
1134
1135
1136 /*
1137  * Resize function called by toolkit.  The size of our spin-box
1138  * has already been changed.  That is why we must store 
1139  * old_width and old_height.
1140  */
1141 static void
1142 Resize(DtSpinBoxWidget spin)
1143 {
1144     ClearShadow(spin, TRUE);
1145     LayoutChildren(spin);
1146     DrawShadow(spin);
1147     OldWidth(spin) = spin->core.width;
1148     OldHeight(spin) = spin->core.height;
1149 }
1150
1151
1152 /*
1153  * Redisplay function called by toolkit. The widget didn't change size, 
1154  * so just redisplay the shadow.
1155  */
1156 static void
1157 Redisplay(      DtSpinBoxWidget w,
1158                 XEvent *event,
1159                 Region region)
1160 {
1161     DrawShadow(w);
1162 }
1163
1164
1165 /*
1166  * GeometryManager function called by toolkit when a child resizes/moves.
1167  * We are not allowing any changes but width/height of the text-field.
1168  * this is because the user can retrieve the text-field and make changes
1169  * that we want to honor.  If they mess around with the label or arrow,
1170  * then we won't honor the request.
1171  * If the text-field requests a change, then make the change, and allow
1172  * our SetSpinBoxSize() and LayoutChildren() figure out what size will
1173  * be allowed.
1174  * Returning GeometryDone was suppose to tell the toolkit
1175  * that we resized the child ourselves, but the text-field had trouble
1176  * with this (its' geometry_manager wasn't called or working right?), so
1177  * we return GeometryYes.
1178  */
1179 static XtGeometryResult
1180 GeometryManager(Widget w, /* child */
1181                 XtWidgetGeometry *request,
1182                 XtWidgetGeometry *reply)
1183 {
1184     /* MotifBc */
1185     DtSpinBoxWidget spin = (DtSpinBoxWidget)XtParent(w);
1186     /* Resolution Independent */
1187     Arg args[4];
1188     Dimension width, height, border_width;
1189     int n;
1190     unsigned char unit_type;
1191
1192     /* Ignore everything but text-field */
1193     if (w != Text(spin) ) {
1194         return(XtGeometryNo);
1195     }
1196
1197     /* Only allow width/height changes */
1198     if (!(request->request_mode & (CWWidth | CWHeight)))
1199         return(XtGeometryNo);
1200     
1201     /* Resolution Independent */
1202     XtSetArg(args[0], XmNunitType, &unit_type);
1203     XtGetValues(w, args, 1);
1204     if ( unit_type != XmPIXELS) {
1205         XtSetArg(args[0], XmNunitType, XmPIXELS);
1206         XtSetValues(w, args, 1);
1207     }
1208     n = 0;
1209     /* Set the text-field to the requested size */
1210     if (request->request_mode & CWWidth) {
1211         /* MotifBc */
1212         XtSetArg(args[n], XmNwidth, &width); n++;
1213     }
1214     if (request->request_mode & CWHeight) {
1215         /* MotifBc */
1216         XtSetArg(args[n], XmNheight, &height); n++;
1217     }
1218     /* MotifBc */
1219     XtSetArg(args[n], XmNborderWidth, &border_width); n++;
1220     XtGetValues(w, args, n);
1221     XtResizeWidget(w, width, height, border_width);
1222     if ( unit_type != XmPIXELS) {
1223         XtSetArg(args[0], XmNunitType, unit_type);
1224         XtSetValues(w, args, 1);
1225     }
1226     
1227     ClearShadow(spin, TRUE);
1228     if (RecomputeSize(spin))
1229         SetSpinBoxSize(spin);
1230     LayoutChildren(spin);
1231     DrawShadow(spin);
1232     return(XtGeometryDone);
1233 }
1234
1235 /* 
1236  * This function sets the size of the spin_box widget based on the
1237  * current size of the children.  Don't worry if it doesn't work, the
1238  * children will be squeezed in later.
1239  */
1240 static void
1241 SetSpinBoxSize(DtSpinBoxWidget spin)
1242 {
1243     Widget text_holder = Editable(spin) ? Text(spin) : Label(spin);
1244     Dimension shadow = SPIN_SHADOW(spin) * 2;
1245     Dimension arrow_width, arrow_height;
1246     /* MotifBc & Resolution Independent */
1247     Dimension height, width;
1248     Arg args[2];
1249     unsigned char unit_type = MUnitType(spin);
1250
1251
1252     /* Resolution Independent */
1253     if (unit_type != XmPIXELS) {
1254         XtSetArg(args[0], XmNunitType, XmPIXELS);
1255         XtSetValues((Widget)spin, args, 1);
1256         XtSetValues(text_holder, args, 1);
1257     }
1258     /* MotifBc */
1259     XtSetArg(args[0], XmNheight, &height);
1260     XtSetArg(args[1], XmNwidth, &width);
1261     XtGetValues(text_holder, args, 2);
1262     /* 
1263      * Find out how big the arrow can be (needed to get 
1264      * width for text_holder).
1265      */
1266     arrow_width = (Dimension)((float)height * ARROW_MULT);
1267     arrow_width = (arrow_width < ARROW_MIN) ? ARROW_MIN : arrow_width;
1268
1269     /* Get height based on arrow width */
1270     arrow_height = arrow_width;
1271     if ((ArrowLayout(spin) == DtARROWS_BEGINNING) ||
1272         (ArrowLayout(spin) == DtARROWS_END))
1273         arrow_height += arrow_height;
1274
1275     /* Make height bigger of 2 - arrows vs text_holder */
1276     if (arrow_height > (Dimension)height)
1277         height = arrow_height;
1278
1279     /* If not stacked add extra width for arrows */
1280     if ((ArrowLayout(spin) != DtARROWS_BEGINNING) &&
1281         (ArrowLayout(spin) != DtARROWS_END)) {
1282         arrow_width += arrow_width;
1283     }
1284
1285     (void)XtMakeResizeRequest((Widget)spin, arrow_width +
1286                               width + shadow +
1287                               (SPIN_MARGIN_W(spin) * 2), 
1288                               height + shadow + (SPIN_MARGIN_H(spin) * 2), 
1289                               NULL, NULL);
1290     OldWidth(spin) = Width(spin);
1291     OldHeight(spin) = Height(spin);
1292
1293     /* Resolution Independent */
1294     if (unit_type != XmPIXELS) { 
1295         XtSetArg(args[0], XmNunitType, unit_type); 
1296         XtSetValues((Widget)spin, args, 1);
1297         XtSetValues(text_holder, args, 1);
1298     } 
1299 }
1300
1301 /*
1302  * This function makes the text_holder (label or text-field) smaller
1303  * if the spin_box couldn't grow to the needed full size.  It will
1304  * also make the text_holder grow if there is space.  The textfield will
1305  * grow with the spin_box, but the label will only grow to its' 
1306  * maximum size.  The label will also shrink down to nothing, but the
1307  * text-field will always keep its' core height.
1308  */
1309 static void
1310 ForceChildSizes(DtSpinBoxWidget spin)
1311 {
1312     Dimension available_height, available_width, arrow_width;
1313     /* MotifBc & Resolution Independent */
1314     Arg args[3];
1315     unsigned char unit_type = XmPIXELS;
1316
1317     /* Resolution Independent */
1318     if (MUnitType(spin) != XmPIXELS) {
1319         unit_type = MUnitType(spin);
1320         XtSetArg(args[0], XmNunitType, XmPIXELS);
1321         XtSetValues(UpArrow(spin), args, 1);
1322         XtSetValues(DownArrow(spin), args, 1);
1323         if (Editable(spin) == False) 
1324                 XtSetValues(Label(spin), args, 1);
1325         else
1326                 XtSetValues(Text(spin), args, 1);
1327     }
1328
1329     CalculateSizes(spin, &available_width, &available_height, &arrow_width);
1330
1331     if (Editable(spin) == FALSE) {  /** label **/
1332         if (available_width > LabelMaxLength(spin))
1333             available_width = LabelMaxLength(spin);
1334
1335         if ((available_width != (Label(spin))->core.width) ||
1336             (available_height != (Label(spin))->core.height))
1337             XtResizeWidget(Label(spin), available_width, available_height,
1338                            (Label(spin))->core.border_width);
1339     }
1340     else if ((Text(spin))->core.width != available_width)  /** TextField **/
1341         XtResizeWidget(Text(spin), available_width,
1342                        (Text(spin))->core.height,
1343                        (Text(spin))->core.border_width);
1344     if ((arrow_width != UpArrow(spin)->core.width) ||
1345         ((UpArrow(spin))->core.height != arrow_width)) {
1346         available_height = (available_height < ARROW_MIN) ? ARROW_MIN : 
1347                             available_height;
1348         XtResizeWidget(UpArrow(spin), arrow_width, arrow_width,
1349                        (UpArrow(spin))->core.border_width);
1350     }
1351     if ((arrow_width != (DownArrow(spin))->core.width) ||
1352         ((DownArrow(spin))->core.height != arrow_width)) {
1353         available_height = (available_height < ARROW_MIN) ? ARROW_MIN : 
1354                             available_height;
1355         XtResizeWidget(DownArrow(spin), arrow_width, arrow_width,
1356                        DownArrow(spin)->core.border_width);
1357     }
1358     /* Resolution Independent */
1359     if (MUnitType(spin) != XmPIXELS) {
1360         XtSetArg(args[0], XmNunitType, unit_type);
1361         XtSetValues(UpArrow(spin), args, 1);
1362         XtSetValues(DownArrow(spin), args, 1);
1363         if (Editable(spin) == False) 
1364                 XtSetValues(Label(spin), args, 1);
1365         else
1366                 XtSetValues(Text(spin), args, 1);
1367     }
1368 }
1369
1370 static void     
1371 CalculateSizes (DtSpinBoxWidget spin,
1372                 Dimension *pwidth,
1373                 Dimension *pheight,
1374                 Dimension *parrow_width)
1375 {
1376     Dimension width, height, arrow_width;
1377
1378     /* Calculate available height for children */
1379     if ((height = spin->core.height - (SPIN_SHADOW(spin) * 2) - 
1380          (SPIN_MARGIN_H(spin) * 2)) <= 0)
1381         height = 1;
1382
1383     /* Get initial available width for children */
1384     width = (spin->core.width - (SPIN_SHADOW(spin) * 2) - 
1385                        (SPIN_MARGIN_W(spin) * 2));
1386
1387     /* label only grows to maximum width needed */
1388     if ((Editable(spin) == FALSE) && 
1389         (height > LabelMaxHeight(spin)))
1390         height = LabelMaxHeight(spin);
1391     else if (Editable(spin)) 
1392         height = (Text(spin))->core.height;
1393     
1394     /* 
1395      * Find out how big the arrow can be (needed to get 
1396      * width for text_holder).
1397      */
1398     arrow_width = (Dimension)(height * ARROW_MULT);
1399     arrow_width = (arrow_width < ARROW_MIN) ? ARROW_MIN : arrow_width;
1400         
1401     /* Make sure width isn't too small or too big */
1402     if ((width -= arrow_width) <= 0)
1403         width = 1;
1404
1405     /* If not stacked subtract extra arrow */
1406     if ((ArrowLayout(spin) != DtARROWS_BEGINNING) &&
1407         (ArrowLayout(spin) != DtARROWS_END)) {
1408         width -= arrow_width;
1409     }
1410
1411     if (pwidth) *pwidth = width;
1412     if (pheight) *pheight = height;
1413     if (parrow_width) *parrow_width = arrow_width;
1414 }
1415
1416 /*
1417  * This function positions the children within the spin_box widget.
1418  * It calls ForceChildSizes() to make sure the children fit within the
1419  * spin_box widget, but it will not try to resize the spin_box widget.
1420  */
1421 static void
1422 LayoutChildren(DtSpinBoxWidget spin)
1423 {
1424     Widget text_holder = Editable(spin) ? Text(spin) : Label(spin);
1425     Position start_x = SPIN_SHADOW(spin) + SPIN_MARGIN_W(spin);
1426     Position start_y = SPIN_SHADOW(spin) + SPIN_MARGIN_H(spin);
1427     Dimension available_height = spin->core.height - (start_y * 2);
1428     Position y, arrow_y;
1429     Dimension arrow_height;
1430     /* Resolution Independent */
1431     Arg args[4];
1432     unsigned char unit_type = XmPIXELS;
1433     
1434     ForceChildSizes(spin);
1435
1436     /* Resolution Independent */
1437     if (MUnitType(spin) != XmPIXELS) {
1438         unit_type = MUnitType(spin);
1439         XtSetArg(args[0], XmNunitType, XmPIXELS);
1440         XtSetValues(UpArrow(spin), args, 1);
1441         XtSetValues(DownArrow(spin), args, 1);
1442         XtSetValues(text_holder, args, 1);
1443     }
1444
1445     /* Center text_holder within spin_box */
1446     y = available_height - text_holder->core.height;
1447     y = ((y < 0) ? 0 : y)/2 + start_y;
1448
1449     arrow_height = (UpArrow(spin))->core.height;
1450     if ((ArrowLayout(spin) == DtARROWS_BEGINNING) ||
1451         (ArrowLayout(spin) == DtARROWS_END))
1452         arrow_height += arrow_height;
1453     arrow_y = available_height - arrow_height;
1454     arrow_y = ((arrow_y < 0) ? 0 : arrow_y)/2 + start_y;
1455
1456     switch (ArrowLayout(spin)) {
1457     case DtARROWS_FLAT_BEGINNING:
1458         XtMoveWidget(UpArrow(spin), start_x, arrow_y);
1459         start_x += (UpArrow(spin))->core.width;
1460         XtMoveWidget(DownArrow(spin), start_x, arrow_y);
1461         start_x += (DownArrow(spin))->core.width;
1462         XtMoveWidget(text_holder, start_x, y);
1463         break;
1464     case DtARROWS_FLAT_END:
1465         XtMoveWidget(text_holder, start_x, y);
1466         /*  
1467          * This start_x places arrow right after text_holder.  With
1468          * labels we want the arrow at the end of the spin_box, so
1469          * the user can use recompute_size more effectively.
1470          * start_x += text_holder->core.width;
1471          */
1472         start_x = (spin->core.width - start_x - 
1473                    (UpArrow(spin))->core.width -
1474                    (DownArrow(spin))->core.width);
1475         XtMoveWidget(UpArrow(spin), start_x, arrow_y);
1476         start_x += (UpArrow(spin))->core.width;
1477         XtMoveWidget(DownArrow(spin), start_x, arrow_y);
1478         break;
1479     case DtARROWS_SPLIT:
1480         XtMoveWidget(DownArrow(spin), start_x, arrow_y);
1481         start_x += (DownArrow(spin))->core.width;
1482         XtMoveWidget(text_holder, start_x, y);
1483         start_x += text_holder->core.width;
1484         XtMoveWidget(UpArrow(spin), start_x, arrow_y);
1485         break;
1486     case DtARROWS_BEGINNING:
1487         XtMoveWidget(UpArrow(spin), start_x, arrow_y);
1488         arrow_y += (UpArrow(spin))->core.height;
1489         XtMoveWidget(DownArrow(spin), start_x, arrow_y);
1490         start_x += (DownArrow(spin))->core.width;
1491         XtMoveWidget(text_holder, start_x, y);
1492         break;
1493     case DtARROWS_END:
1494         XtMoveWidget(text_holder, start_x, y);
1495         /*  
1496          * This start_x places arrow right after text_holder.  With
1497          * labels we want the arrow at the end of the spin_box, so
1498          * the user can use recompute_size more effectively.
1499          * start_x += text_holder->core.width;
1500          */
1501         start_x = spin->core.width - start_x - (UpArrow(spin))->core.width;
1502         XtMoveWidget(UpArrow(spin), start_x, arrow_y);
1503         arrow_y += (UpArrow(spin))->core.width;
1504         XtMoveWidget(DownArrow(spin), start_x, arrow_y);
1505         break;
1506     }
1507     /* Resolution Independent */
1508     if (MUnitType(spin) != XmPIXELS) {
1509         XtSetArg(args[0], XmNunitType, unit_type);
1510         XtSetValues(UpArrow(spin), args, 1);
1511         XtSetValues(DownArrow(spin), args, 1);
1512         XtSetValues(text_holder, args, 1);
1513     }
1514 }
1515
1516
1517 /*
1518  * SetValues() routine for SpinBox widget. Most of the real work
1519  * is done in SetItems() and SetNumeric().  If items is set we store
1520  * our own XmStrings. 
1521  * This function was built with static spin_boxs in mind, meaning that
1522  * is-numeric or editable resources won't be changed constantly.  These
1523  * resources can be changed, but this function doesn't handle them in
1524  * a super-efficient manor.  for example, changing child-type will cause
1525  * the label to be resize even if it isn't managed.
1526  */
1527 static Boolean
1528 SetValues(      DtSpinBoxWidget current,
1529                 DtSpinBoxWidget request,
1530                 DtSpinBoxWidget new)
1531 {
1532     DtSpinBoxPart *new_p = (DtSpinBoxPart*)
1533         &(XmField(new,ipot,DtSpinBox,label,Widget));
1534     DtSpinBoxPart *cur_p = (DtSpinBoxPart*)
1535         &(XmField(current,ipot,DtSpinBox,label,Widget));
1536     Boolean store_info;
1537     char *widget_name;
1538     Boolean label_size_changed = FALSE;
1539     Arg args[20];
1540     int n;
1541     /* Resolution Independent */
1542     unsigned char unit_type = MUnitType(new);
1543
1544     CheckResources(new);
1545
1546     if (Text(new) != Text(current)) {
1547         XtWarning(SB_TEXT);
1548         Text(new) = Text(current);
1549     }
1550
1551     /*
1552      * Editable resource changed.  If the widget (textField or Label)
1553      * doesn't exist, then create it.
1554      */
1555     if (Editable(new) != Editable(current)) {
1556         if (Editable(new)) {
1557             XtUnmanageChild(Label(new));
1558             if (Text(new) == NULL) {
1559                 widget_name = XtMalloc(strlen(XtName((Widget)new)) + 10);
1560                 sprintf(widget_name, "%s_TF", XtName((Widget)new));
1561                 n = 0;
1562                 XtSetArg(args[n], XmNcolumns, TextColumns(new)); n++;
1563                 XtSetArg(args[n], XmNmaxLength, TextMaxLength(new)); n++;
1564                 XtSetArg(args[n], XmNmarginWidth, 2); n++;
1565                 XtSetArg(args[n], XmNmarginHeight, 2); n++;
1566                 if (unit_type != XmPIXELS) {
1567                         XtSetArg(args[n], XmNunitType, XmPIXELS); n++;
1568                 }
1569                 Text(new) = XtCreateManagedWidget(widget_name,
1570                                                     xmTextFieldWidgetClass,
1571                                                     (Widget)new, args, n);
1572                 XtAddCallback(Text(new), XmNlosingFocusCallback, 
1573                               text_losing_focus_cb, (XtPointer)new);
1574                 XtAddCallback(Text(new), XmNactivateCallback, 
1575                               text_activate_cb, (XtPointer)new);
1576                 XtAddCallback(Text(new), XmNfocusCallback, 
1577                               text_focus_cb, (XtPointer)new);
1578                 XtFree(widget_name);
1579                 if (unit_type != XmPIXELS) {
1580                         XtSetArg(args[n], XmNunitType, unit_type); n++;
1581                         XtSetValues(Text(new), args, 1);
1582                 }
1583             }
1584             else
1585                 XtManageChild(Text(new));
1586         }
1587         else {
1588             XtUnmanageChild(Text(new));
1589             if (Label(new) == NULL) {
1590                 widget_name = XtMalloc(strlen(XtName((Widget)new)) + 10);
1591                 sprintf(widget_name, "%s_Label", XtName((Widget)new));
1592                 n = 0;
1593                 XtSetArg(args[n], XmNalignment, Alignment(new)); n++;
1594                 XtSetArg(args[n], XmNrecomputeSize, FALSE); n++;
1595                 XtSetArg(args[n], XmNlabelString, InitLabel); n++;
1596                 XtSetArg(args[n], XmNmarginLeft, LABEL_PADDING); n++;
1597                 XtSetArg(args[n], XmNmarginRight, LABEL_PADDING); n++;
1598                 XtSetArg(args[n], XmNmarginWidth, TEXT_CONTEXT_MARGIN); n++;
1599                 XtSetArg(args[n], XmNmarginHeight, 0); n++;
1600                 if (unit_type != XmPIXELS) {
1601                         XtSetArg(args[n], XmNunitType, XmPIXELS); n++;
1602                 }
1603                 Label(new) = XtCreateManagedWidget(widget_name,
1604                                                      xmLabelWidgetClass,
1605                                                      (Widget)new, args, n);
1606                 XtOverrideTranslations((Widget)Label(new), child_trans);
1607                 XtFree(widget_name);
1608                 if (unit_type != XmPIXELS) { 
1609                         XtSetArg(args[n], XmNunitType, unit_type); n++; 
1610                         XtSetValues(Label(new), args, 1); 
1611                 } 
1612             }
1613             else
1614                 XtManageChild(Label(new));
1615         }
1616         /* 
1617          * Text-fields and labels have different shadows.  Only
1618          * change if user didn't change the shadow resource.
1619          */
1620         if (SPIN_SHADOW(new) == SPIN_SHADOW(current))
1621             SPIN_SHADOW(new) = (Editable(new)) ? TEXT_FIELD_SHADOW :
1622                                                    LABEL_SHADOW;
1623     }
1624
1625     /* Check arrow sensitivity (up arrow is right arrow)*/
1626     if (ArrowSensitivity(new) != ArrowSensitivity(current)) {
1627         XtSetSensitive(UpArrow(new), 
1628                ((ArrowSensitivity(new) == DtARROWS_SENSITIVE) ||
1629                 (ArrowSensitivity(new) == DtARROWS_INCREMENT_SENSITIVE)));
1630         XtSetSensitive(DownArrow(new), 
1631                ((ArrowSensitivity(new) == DtARROWS_SENSITIVE) ||
1632                 (ArrowSensitivity(new) == DtARROWS_DECREMENT_SENSITIVE)));
1633     }
1634
1635     /*
1636      * Check arrow layout.  Only need to change arrows if going to or
1637      * from DtARROWS_SPLIT.  The LayoutChildren() routine actually
1638      * positions the arrows in the correct place.
1639      */
1640     if (ArrowLayout(new) != ArrowLayout(current)) {
1641         if (ArrowLayout(new) == DtARROWS_SPLIT) {
1642             XtSetArg(args[0], XmNarrowDirection, XmARROW_RIGHT);
1643             XtSetValues(UpArrow(new), args, 1);
1644             XtSetArg(args[0], XmNarrowDirection, XmARROW_LEFT);
1645             XtSetValues(DownArrow(new), args, 1);
1646         }
1647         else {
1648             XtSetArg(args[0], XmNarrowDirection, XmARROW_UP);
1649             XtSetValues(UpArrow(new), args, 1);
1650             XtSetArg(args[0], XmNarrowDirection, XmARROW_DOWN);
1651             XtSetValues(DownArrow(new), args, 1);
1652         }
1653     }
1654
1655     if (Text(new) && (Text(new) == Text(current))) {
1656         n = 0;
1657         if (TextColumns(new) != TextColumns(current)) {
1658             XtSetArg(args[n], XmNcolumns, TextColumns(new)); n++;
1659         }
1660         if (TextMaxLength(new) != TextMaxLength(current)) {
1661             XtSetArg(args[n], XmNmaxLength, TextMaxLength(new)); n++;
1662         }
1663         if (n > 0) 
1664             XtSetValues(Text(new), args, n);
1665     }
1666     
1667     /*
1668      * LabelWidget alignment has changed.
1669      */
1670     if (Label(new) && (Alignment(new) != Alignment(current))) {
1671         XtSetArg(args[0], XmNalignment, Alignment(new));
1672         XtSetValues(Label(new), args, 1);
1673     }
1674
1675     store_info = ((Items(new) != Items(current)) ||
1676                   (ItemCount(new) != ItemCount(current)) ||
1677                   (DecimalPoints(new) != DecimalPoints(current)) ||
1678                   (Maximum(new) != Maximum(current)) ||
1679                   (Minimum(new) != Minimum(current)) ||
1680                   (NumericIncrement(new) != NumericIncrement(current)) ||
1681                   ((ChildType(new) == DtNUMERIC) &&
1682                    (Position(new) != Position(current))));
1683     if (store_info)
1684         StoreResourceInfo(new_p, cur_p, (Items(new) != Items(current)));
1685     
1686     if (Label(new) && (store_info || 
1687                          (Label(new) != Label(current)) ||
1688                          (ChildType(new) != ChildType(current)))) {
1689         SetMaximumLabelSize(new_p);
1690         label_size_changed = TRUE;
1691     }
1692     
1693     if (store_info ||
1694         (Alignment(new) != Alignment(current)) ||
1695         (Editable(new) != Editable(current)) ||
1696         (Position(new) != Position(current)) ||
1697         (Label(new) != Label(current)) ||
1698         (ChildType(new) != ChildType(current))) {
1699         if (Editable(new))
1700             SetTextFieldData(new);
1701         else
1702             SetLabelData(new);
1703     }
1704
1705     /*
1706      * Must recalculate the spin_box and re-layout the children.
1707      * If this is not editable, then set the label to its' maximum
1708      * size; it will get chopped if it is too big.  This is needed 
1709      * because we shrink the label down, and SetSpinBoxSize() uses
1710      * the label's core sizes to figure what size to become.
1711      */
1712     if ((Editable(new) != Editable(current)) ||
1713         (SPIN_MARGIN_W(new) != SPIN_MARGIN_W(current)) ||
1714         (SPIN_MARGIN_H(new) != SPIN_MARGIN_H(current)) ||
1715         (SPIN_SHADOW(new) != SPIN_SHADOW(current)) ||
1716         (ArrowLayout(new) != ArrowLayout(current)) ||
1717         (!(Editable(new)) && label_size_changed)) {
1718         ClearShadow(current, TRUE);
1719         if (RecomputeSize(new))
1720             SetSpinBoxSize(new);
1721         LayoutChildren(new);
1722         DrawShadow(new);
1723     }
1724     n=0;
1725     if(CBgPixel(new) != CBgPixel(current))
1726     {
1727         XtSetArg(args[n],XmNbackground,CBgPixel(new));n++;
1728     }
1729     if(CBgPixmap(new) != CBgPixmap(current))
1730     {
1731         XtSetArg(args[n],XmNbackgroundPixmap,CBgPixmap(new));n++;
1732     }
1733     if(MFgPixel(new) != MFgPixel(current))
1734     {
1735         XtSetArg(args[n],XmNforeground,MFgPixel(new));n++;
1736     }
1737     if(Text(new))
1738         XtSetValues (Text(new),args,n);
1739     if(Label(new))
1740         XtSetValues (Label(new),args,n);
1741     if(UpArrow(new))
1742         XtSetValues (UpArrow(new),args,n);
1743     if(DownArrow(new))
1744         XtSetValues (DownArrow(new),args,n);
1745     
1746     return(FALSE);
1747 }
1748
1749
1750 /*
1751  * This function clears the shadow around our widget.  If all is TRUE,
1752  * then clear all 4 sides; otherwise, only clear the right and bottom
1753  * sides (during resize). 
1754  */ 
1755 static void
1756 ClearShadow(    DtSpinBoxWidget w,
1757                 Boolean all)
1758 {
1759     Dimension shadow = SPIN_SHADOW(w);
1760     Dimension margin_w = SPIN_MARGIN_W(w);
1761     Dimension margin_h = SPIN_MARGIN_H(w);
1762
1763     if ((shadow > 0) && XtIsRealized((Widget)w)) {
1764         if (all) {
1765             XClearArea(XtDisplayOfObject((Widget)w),
1766                        XtWindowOfObject((Widget)w), 
1767                        margin_w, margin_h,
1768                        OldWidth(w) - (margin_w * 2),
1769                        shadow, FALSE);
1770             XClearArea(XtDisplayOfObject((Widget)w),
1771                        XtWindowOfObject((Widget)w), 
1772                        margin_w, margin_h, shadow, 
1773                        OldHeight(w) - (margin_h * 2), FALSE);
1774         }
1775         XClearArea(XtDisplayOfObject((Widget)w), XtWindowOfObject((Widget)w),
1776                    margin_w, OldHeight(w) - margin_h - shadow,
1777                    OldWidth(w) - (margin_w * 2), shadow, FALSE);
1778         XClearArea(XtDisplayOfObject((Widget)w), XtWindowOfObject((Widget)w),
1779                    OldWidth(w) - margin_w - shadow,
1780                    margin_h, shadow, 
1781                    OldHeight(w) - (margin_h * 2), FALSE);
1782     }
1783     DrawHighlight(w, TRUE);
1784 }
1785
1786 /* 
1787  * This functions draws the shadow around our spin_box.
1788  */
1789 static void
1790 DrawShadow(DtSpinBoxWidget w)
1791 {
1792     Dimension shadow = SPIN_SHADOW(w);
1793     Dimension margin_w = SPIN_MARGIN_W(w);
1794     Dimension margin_h = SPIN_MARGIN_H(w);
1795     
1796     if ((shadow > 0) && XtIsRealized((Widget)w)) {
1797         XmeDrawShadows(XtDisplayOfObject((Widget)w),
1798                        XtWindowOfObject((Widget)w),
1799                        w->manager.top_shadow_GC,
1800                        w->manager.bottom_shadow_GC, 
1801                        margin_w, margin_h,
1802                        w->core.width - (margin_w * 2),
1803                        w->core.height - (margin_h * 2),
1804                        shadow, XmSHADOW_OUT);
1805     }
1806     DrawHighlight(w, FALSE);
1807 }
1808
1809 /*
1810  * This function sets up the items information for the SpinBox, as
1811  * well as variables needed for child_type.
1812  */
1813 static void
1814 StoreResourceInfo(      DtSpinBoxPart *spin_p,
1815                         DtSpinBoxPart *old_p,
1816                         Boolean do_items)
1817 {
1818     XmStringTable table;
1819     int i, base = 1;
1820
1821     if (do_items && spin_p->items) {
1822         /* Free up current items if needed */
1823         if (old_p && old_p->items) {
1824             for (i = 0; i < old_p->item_count; i++) {
1825                 XmStringFree(old_p->items[i]);
1826             }
1827             XtFree((char*)old_p->items);
1828         }
1829             
1830         /*
1831          * Loop through all the items, copy them into our space.
1832          */
1833         table = (XmStringTable)XtMalloc(sizeof(XmString) * spin_p->item_count);
1834         for (i = 0; i < spin_p->item_count; i++) {
1835             table[i] = XmStringCopy(spin_p->items[i]);
1836         }
1837         spin_p->items = table;
1838         for (i = 0; i < spin_p->item_count; i++)
1839             spin_p->items[i] = table[i];
1840     }
1841
1842     /*
1843      * Store the numeric information
1844      */
1845
1846     /* get base number for convert ints to floats */
1847     for (i = 0; i < spin_p->decimal_points; i++)
1848         base *= 10;
1849
1850     /* Set new initial values */
1851     spin_p->min = (float)spin_p->minimum/base;
1852     spin_p->max = (float)spin_p->maximum/base;
1853     spin_p->increment = (float)spin_p->numeric_increment/base;
1854
1855     spin_p->current = (float)spin_p->position/base;
1856
1857     /* Create format string used to build correct XmString value */
1858     spin_p->float_format[0] = '%';
1859     sprintf((char*)(spin_p->float_format+1), ".%df", spin_p->decimal_points);
1860 }
1861
1862
1863 /* Caller must free string */
1864 static char*
1865 GetTextString(XmString xm_string)
1866 {
1867     XmStringContext context;
1868     XmStringComponentType type;
1869     XmStringCharSet charset;
1870     XmStringDirection direction;
1871     XmStringComponentType unknown_tag;
1872     unsigned short ul;
1873     unsigned char *uv;
1874     char *text = NULL;
1875     Boolean done = FALSE;
1876
1877     XmStringInitContext(&context, xm_string);
1878     
1879     /* Loop until 1st char* found */
1880     while (!done) {
1881         type = XmStringGetNextComponent(context, &text, &charset,
1882                                         &direction, &unknown_tag, 
1883                                         &ul, &uv);
1884         switch (type) {
1885         case XmSTRING_COMPONENT_END:
1886             done = TRUE;
1887             break;
1888         case XmSTRING_COMPONENT_TEXT:
1889         case XmSTRING_COMPONENT_LOCALE_TEXT:
1890             done = TRUE;
1891             break;
1892         default:
1893             break;
1894         }
1895     }
1896     XmStringFreeContext(context);
1897     return(text);
1898 }
1899
1900 /*
1901  * Take the string out of the list and put it into the text-field.
1902  * text-fields don't handle xm-strings, so we must get the char*
1903  * out of it (only getting the first segment).  This is slower than
1904  * storing the text-strings (char*) ourselves, but that would take
1905  * up a lot of memory.  Since this setting happens during a user
1906  * action, speed isn't a problem.
1907  */
1908 static void
1909 SetTextFieldData(DtSpinBoxWidget spin)
1910 {
1911     char string[NUMERIC_LENGTH];
1912     XmString xm_string;
1913     char *text;
1914     Arg arg;
1915     
1916     
1917     if (ChildType(spin) == DtNUMERIC) {
1918         sprintf(string, FloatFormat(spin), (float)(Current(spin)));
1919         XtSetArg(arg, XmNvalue, string);
1920         XtSetValues(Text(spin), &arg, 1);
1921     }
1922     else {
1923         if (ItemCount(spin) == 0){
1924             XtSetArg(arg, XmNvalue, "");
1925             XtSetValues(Text(spin), &arg, 1);
1926             return;
1927         }
1928         else {
1929             xm_string = (Items(spin))[Position(spin)];
1930             if ((text = GetTextString(xm_string))) {
1931                 XtSetArg(arg, XmNvalue, text);
1932                 XtSetValues(Text(spin), &arg, 1);
1933                 XtFree(text);
1934             }
1935         }
1936     }
1937 }
1938
1939 /*
1940  * Set the maximum size of the label, depending on the
1941  * characteristics of the list of items.  Not very efficient
1942  * if switching from numeric to non-numeric.
1943  */
1944 static void
1945 SetMaximumLabelSize(DtSpinBoxPart *spin_p)
1946 {
1947     XmString xm_string;
1948     XmFontList font_list;
1949     char string[NUMERIC_LENGTH];
1950     Dimension width, height;
1951     Dimension longest = 0;
1952     Dimension highest = 0;
1953     Arg args[5];
1954     int i;
1955     /* Resolution Independent */
1956     unsigned char unit_type;
1957     
1958     /* Get font info from the widget */
1959     XtSetArg(args[0], XmNfontList, &font_list);
1960     XtSetArg(args[1], XmNunitType, &unit_type); /* resolution Independent */
1961     XtGetValues(spin_p->label, args, 2);
1962     if ( unit_type != XmPIXELS) {
1963         XtSetArg(args[0], XmNunitType, XmPIXELS);
1964         XtSetValues(spin_p->label, args, 1);
1965     }
1966
1967     if (spin_p->child_type == DtNUMERIC) {
1968         /* Find out maximum size of the widget from min/max */
1969         sprintf(string, spin_p->float_format, spin_p->min);
1970         xm_string = XmStringCreateLocalized(string);
1971         XmStringExtent(font_list, xm_string, &longest, &highest);
1972         XmStringFree(xm_string);
1973         sprintf(string, spin_p->float_format, spin_p->max);
1974         xm_string = XmStringCreateLocalized(string);
1975         XmStringExtent(font_list, xm_string, &width, &height);
1976         XmStringFree(xm_string);
1977         
1978         longest = (width > longest) ? width : longest;
1979         highest = (height > highest) ? height : highest;
1980     }
1981     else if (spin_p->items) {
1982         /*
1983          * Loop through all the items to find the biggest dimensions
1984          */
1985         for (i = 0; i < spin_p->item_count; i++) {
1986             XmStringExtent(font_list, spin_p->items[i], &width, &height);
1987             longest = (width > longest) ? width : longest;
1988             highest = (height > highest) ? height : highest;
1989         }
1990     }
1991     else {
1992         XmStringExtent(font_list, InitLabel, &longest, &highest);
1993     }
1994
1995     spin_p->label_max_length = 
1996         ( (Dimension)(longest + ( (LABEL_PADDING + TEXT_CONTEXT_MARGIN) *2) ) >
1997           (Dimension)Width(spin_p->label) ) ? 
1998         (longest + ((LABEL_PADDING+TEXT_CONTEXT_MARGIN) * 2)) :
1999         Width(spin_p->label);
2000
2001     spin_p->label_max_height = highest > Height(spin_p->label) ?
2002         highest : Height(spin_p->label);
2003     XtResizeWidget(spin_p->label, spin_p->label_max_length, 
2004                    spin_p->label_max_height,
2005                    spin_p->label->core.border_width);
2006
2007     /* Resolution Independent */
2008     if ( unit_type != XmPIXELS) {
2009         XtSetArg(args[0], XmNunitType, unit_type);
2010         XtSetValues(spin_p->label, args, 1);
2011     }
2012 }
2013
2014
2015 /*
2016  * Put the current list item into the label.
2017  */
2018 static void
2019 SetLabelData(DtSpinBoxWidget spin)
2020 {
2021     XmString xm_string;
2022     char string[NUMERIC_LENGTH];
2023     int index = Position(spin);
2024     Arg arg;
2025
2026     if (ChildType(spin) == DtNUMERIC) {
2027         sprintf(string, FloatFormat(spin), (float)Current(spin));
2028         xm_string = XmStringCreateLocalized(string);
2029         XtSetArg(arg, XmNlabelString, xm_string);
2030         XtSetValues(Label(spin), &arg, 1);
2031     }
2032     else {
2033         /*
2034          * If the item is not empty, get the current item from the list, else
2035          * use InitLabel.
2036          */
2037         xm_string = (ItemCount(spin)>0) ? (Items(spin))[index] : InitLabel;
2038         XtSetArg(arg, XmNlabelString, xm_string);
2039         XtSetValues(Label(spin), &arg, 1);
2040     }
2041 }
2042
2043 /*
2044  * Timout dispatch routine.  This calls the appropriate callback function
2045  * to simulate up or down arrow activation.
2046  */
2047 static void
2048 timer_dispatch( XtPointer client_data,
2049                 XtIntervalId *id)
2050 {
2051     DtSpinBoxWidget spin_w = (DtSpinBoxWidget)client_data;
2052
2053     Timer(spin_w) = (XtIntervalId)NULL;
2054     InitCb(spin_w) = FALSE;
2055     if (WhichArrow(spin_w) == XmARROW_UP) {
2056         if (Grabbed(spin_w)) {
2057             XtRemoveGrab(UpArrow(spin_w));
2058             Grabbed(spin_w) = FALSE;
2059         }
2060         up_cb(NULL, client_data, NULL);
2061     }
2062     else {
2063         if (Grabbed(spin_w)) {
2064             XtRemoveGrab(DownArrow(spin_w));
2065             Grabbed(spin_w) = FALSE;
2066         }
2067         down_cb(NULL, client_data, NULL);
2068     }
2069 }
2070
2071 static void
2072 TextFieldActivate(DtSpinBoxPart *spin_p)
2073 {
2074     XmTextFieldWidget w = (XmTextFieldWidget)(spin_p->text);
2075     XmAnyCallbackStruct cb;
2076     char string[NUMERIC_LENGTH];
2077     char *data = NULL;
2078     char *text = NULL;
2079     Arg arg;
2080     Boolean free_me = TRUE;
2081     
2082     XtSetArg(arg, XmNvalue, &data);
2083     XtGetValues((Widget)w, &arg, 1);
2084
2085     if (spin_p->child_type == DtNUMERIC) {
2086         sprintf(string, spin_p->float_format, (float)spin_p->current);
2087         text = string;
2088         free_me = FALSE;
2089     }
2090     else if (spin_p->items)
2091         text = GetTextString(spin_p->items[spin_p->position]);
2092
2093     if (text && data && (strcmp(text, data) == 0)) {
2094         if (free_me)
2095             XtFree(text);
2096         return;
2097     }
2098     /* Only send callback if both are not NULL */
2099     else if (!((text == NULL) && (data == NULL))) {
2100         cb.reason = XmCR_ACTIVATE;
2101         cb.event  = NULL;
2102         /* MotifBc */
2103         if (XtHasCallbacks((Widget)w, XmNactivateCallback)==XtCallbackHasSome)
2104                XtCallCallbacks((Widget)w, XmNactivateCallback, 
2105                                (XtPointer) &cb);
2106         if (text && free_me)
2107             XtFree(text);
2108     }
2109 }
2110
2111 /*
2112  * This function calls the appropriate callback for the spin_box.
2113  * It gathers the correct arguments and fills in the callback structure.
2114  */
2115 static Boolean
2116 SendCallback(   DtSpinBoxWidget spin,
2117                 XEvent *event,
2118                 Boolean value_changed,
2119                 int position,
2120                 float current, /* Used for numeric */
2121                 Boolean crossed)
2122 {
2123     DtSpinBoxCallbackStruct cb;
2124     XmString xm_string = NULL;
2125     char string[NUMERIC_LENGTH];
2126
2127     if (ChildType(spin) == DtNUMERIC) {
2128         sprintf(string, FloatFormat(spin), (float)current);
2129         xm_string = XmStringCreateLocalized(string);
2130     }
2131     else {
2132         xm_string = (ItemCount(spin)>0) ? (Items(spin))[position] : InitLabel;
2133         xm_string = XmStringCopy(xm_string);
2134     }
2135
2136     if (event)
2137         cb.reason = DtCR_OK;
2138     else if (WhichArrow(spin) == XmARROW_UP)
2139         cb.reason = DtCR_SPIN_NEXT;
2140     else
2141         cb.reason = DtCR_SPIN_PRIOR;
2142     cb.doit = TRUE;
2143     cb.event = event;
2144     cb.widget = (Widget)spin;
2145     cb.position = position;
2146     cb.value = xm_string;
2147     cb.crossed_boundary = crossed;
2148     if (value_changed) {
2149         XtCallCallbackList((Widget)spin, ValueChangedCallback(spin), 
2150                            (XtPointer)&cb);
2151         cb.doit = TRUE;
2152     }
2153     else {
2154         XtCallCallbackList((Widget)spin, ModifyVerifyCallback(spin), 
2155                            (XtPointer)&cb);
2156     }
2157     XmStringFree(xm_string);
2158
2159     return(cb.doit);
2160 }
2161
2162
2163 /*
2164  * This function gets called by the up/down arm callback functions.
2165  * We set up the timer and send the modify-verify and value-changed
2166  * callbacks. 
2167  * There are potential problems if the user does some weird stuff 
2168  * in the callbacks.  I have added protection against the case where
2169  * a user does a grab (with XtAddGrab/XtPopup/etc.) in the callbacks.
2170  * Grabbing in the callback would cause us to lose the button-release
2171  * (disarm), which would make the timer go on forever.  A grab is
2172  * done after the callbacks just to make sure we will receive the
2173  * button-release.  The button-release will call disarm_cb() which will
2174  * un-grab and disable the timer.  I have also added a leave callback
2175  * which helps to protect against these kinds of problems.  
2176  * If the callback goes into another event-loop (which I hope would
2177  * never happen), we would spin continuously (since our XtAddGrab never
2178  * get called), until the user left the window (which would call 
2179  * grab_leave_cb).  The grabbed flag gets set if we do the grab, so that
2180  * we know if we can remove the grab.  Our XtAddGrab() might not get called
2181  * if the callback enters another event-loop.
2182  *
2183  * The event sent in the callback will be NULL during continuous spinning.
2184  */
2185 static void
2186 FinishUpDown(   DtSpinBoxWidget spin,
2187                 XtPointer arrow_call_data,
2188                 int new_position,
2189                 float new_current,
2190                 Boolean crossed)
2191 {
2192     XmArrowButtonCallbackStruct *arrow_data;
2193     XEvent *last_event = NULL;
2194     int repeat_delay = RepeatDelay(spin);
2195
2196     if (InitCb(spin))    
2197         repeat_delay = InitialDelay(spin);
2198     Timer(spin) = XtAppAddTimeOut(XtWidgetToApplicationContext((Widget)spin),
2199                             repeat_delay, timer_dispatch, (XtPointer)spin);
2200
2201     /* Try to get Xevent */
2202     if ((arrow_data = (XmArrowButtonCallbackStruct*)arrow_call_data))
2203         last_event = arrow_data->event;
2204
2205     /* 
2206      * Send modify_verify callback.  If user says no, then
2207      * clear the timer and reset the state before returning.
2208      */
2209     if (SendCallback(spin, last_event, FALSE, new_position,
2210                      new_current, crossed) == FALSE) {
2211         XtRemoveTimeOut(Timer(spin));
2212         Timer(spin) = (XtIntervalId)NULL;
2213         InitCb(spin) = TRUE;
2214         return;
2215     }
2216
2217     /* User said yes, so set widget values */
2218     Position(spin) = new_position;
2219     Current(spin) = new_current;
2220     if (Editable(spin))
2221         SetTextFieldData(spin);
2222     else
2223         SetLabelData(spin);
2224
2225     /* send value_changed callback */
2226     (void)SendCallback(spin, last_event, TRUE, Position(spin),
2227                        Current(spin), crossed);
2228
2229     /* See notes at top of function on XtAddGrab usage */
2230     Grabbed(spin) = TRUE;
2231     if (WhichArrow(spin) == XmARROW_UP)
2232         XtAddGrab(UpArrow(spin), FALSE, FALSE);
2233     else
2234         XtAddGrab(DownArrow(spin), FALSE, FALSE);
2235 }
2236
2237 /*
2238  * Show the next value in the SpinBox.  If numeric, just add the
2239  * increment value.  If using string-table, get the next one in the
2240  * table.  This function takes care of wrap around.  Set the arrow
2241  * type.  This is needed for the timer_dispatch function.  up_cb
2242  * gets called the first time the button is pressed, and each time the
2243  * timer goes off.
2244  * All widget internals are expected to be correct here; they
2245  * get verified when set by the user.
2246  */
2247 static void
2248 up_cb(  Widget w,
2249         XtPointer client_data,
2250         XtPointer call_data)
2251 {
2252     DtSpinBoxWidget spin = (DtSpinBoxWidget)client_data;
2253     DtSpinBoxPart *spin_p = (DtSpinBoxPart*)
2254         &(XmField(spin,ipot,DtSpinBox,label,Widget));
2255     Boolean crossed_boundary = FALSE;
2256     int new_position = Position(spin);
2257     float new_current = Current(spin);
2258
2259    /* Getting Focus */
2260     if ( !_XmFocusIsHere( (Widget)spin) )
2261         XmProcessTraversal((Widget)spin,
2262                           (XmTraversalDirection) XmTRAVERSE_CURRENT);
2263
2264     if (Editable(spin))
2265         TextFieldActivate(spin_p);
2266
2267     /*
2268      * If non-numeric and no items then ignore the user activate event.
2269      */
2270     if ((ChildType(spin) == DtSTRING) && (ItemCount(spin) == 0))
2271         return;
2272     
2273     if (ChildType(spin) == DtNUMERIC) {
2274         if ((new_current + Increment(spin)) > Max(spin)) {
2275             if (Wrap(spin)) {
2276                 new_position = Minimum(spin);
2277                 new_current = Min(spin);
2278                 crossed_boundary = TRUE;
2279             }
2280             else
2281                 XBell(XtDisplayOfObject((Widget)spin), 0);
2282         }
2283         else {
2284             new_position += NumericIncrement(spin);
2285             new_current += Increment(spin);
2286         }
2287     }
2288     else if (ItemCount(spin) >0) {
2289         if (new_position == (ItemCount(spin) - 1)) {
2290             if (Wrap(spin)) {
2291                 new_position = 0;
2292                 crossed_boundary = TRUE;
2293             }
2294             else
2295                 XBell(XtDisplayOfObject((Widget)spin), 0);
2296         }
2297         else
2298             new_position++;
2299     }
2300
2301     WhichArrow(spin) = XmARROW_UP;
2302     FinishUpDown(spin, call_data, new_position, new_current, crossed_boundary);
2303 }
2304
2305
2306 /*
2307  * Show the previous value in the SpinBox.  If numeric, just decrement
2308  * the increment value.  If using string-table, get the previous one in the
2309  * table.  This function takes care of wrap around. Set the arrow
2310  * type.  This is needed for the timer_dispatch function.  down_cb
2311  * gets called the first time the button is pressed, and each time the
2312  * timer goes off.
2313  * All widget internals are expected to be correct here; they
2314  * get verified when set by the user.
2315  */
2316 static void
2317 down_cb(Widget w,
2318         XtPointer client_data,
2319         XtPointer call_data)
2320 {
2321     DtSpinBoxWidget spin = (DtSpinBoxWidget)client_data;
2322     DtSpinBoxPart *spin_p = (DtSpinBoxPart*)
2323         &(XmField(spin,ipot,DtSpinBox,label,Widget));
2324     Boolean crossed_boundary = FALSE;
2325     int new_position = Position(spin);
2326     float new_current = Current(spin);
2327     
2328    /* #5: Getting Focus */
2329     if ( !_XmFocusIsHere( (Widget)spin) )
2330       XmProcessTraversal((Widget)spin,
2331                          (XmTraversalDirection) XmTRAVERSE_CURRENT);
2332
2333     if (Editable(spin))
2334         TextFieldActivate(spin_p);
2335
2336     /*
2337      * If non-numeric and no items then ignore the user activate event.
2338      */
2339     if ((ChildType(spin) == DtSTRING) && (ItemCount(spin) == 0))
2340         return;
2341
2342     if (ChildType(spin) == DtNUMERIC) {
2343         if ((new_current - Increment(spin)) < Min(spin)) {
2344             if (Wrap(spin)) {
2345                 new_current = Max(spin);
2346                 new_position = Maximum(spin);
2347                 crossed_boundary = TRUE;
2348             }
2349             else
2350                 XBell(XtDisplayOfObject((Widget)spin), 0);
2351         }
2352         else {
2353             new_current -= Increment(spin);
2354             new_position -= NumericIncrement(spin);
2355         }
2356     }
2357     else if (ItemCount(spin)>0) {
2358         if (new_position == 0) {
2359             if (Wrap(spin)) {
2360                 new_position = ItemCount(spin) - 1;
2361                 crossed_boundary = TRUE;
2362             }
2363             else
2364                 XBell(XtDisplayOfObject((Widget)spin), 0);
2365         }
2366         else
2367             new_position--;
2368     }
2369
2370     WhichArrow(spin) = XmARROW_DOWN;
2371     FinishUpDown(spin, call_data, new_position, new_current, crossed_boundary);
2372 }
2373
2374 static void
2375 disarm_cb(      Widget w,
2376                 XtPointer client_data,
2377                 XtPointer call_data)
2378 {
2379     DtSpinBoxWidget spin = (DtSpinBoxWidget)client_data;
2380
2381     if ((int)Timer(spin)) {
2382         InitCb(spin) = TRUE;
2383         XtRemoveTimeOut(Timer(spin));
2384         Timer(spin) = (XtIntervalId)NULL;
2385         if (Grabbed(spin)) {
2386             XtRemoveGrab(w);
2387             Grabbed(spin) = FALSE;
2388         }
2389     }
2390 }
2391
2392 static void
2393 grab_leave_cb(  Widget w,
2394                 XtPointer client_data,
2395                 XEvent *event,
2396                 Boolean *dispatch)
2397 {
2398     DtSpinBoxWidget spin = (DtSpinBoxWidget)client_data;
2399
2400     if ((int)Timer(spin) && 
2401         ((event->xcrossing.mode == NotifyGrab) ||
2402          (event->xcrossing.mode == NotifyUngrab) ||
2403          (!(event->xcrossing.state & Button1Mask)))) {
2404         InitCb(spin) = TRUE;
2405         XtRemoveTimeOut(Timer(spin));
2406         Timer(spin) = (XtIntervalId)NULL;
2407         if (Grabbed(spin)) {
2408             XtRemoveGrab(w);
2409             Grabbed(spin) = FALSE;
2410         }
2411     }
2412 }
2413
2414 /*
2415  * We get the text-field losing-focus callback, so pass it on to
2416  * the user if they requested it.  Our losing-focus callback 
2417  * is just a convenience callback, so that the user doesn't
2418  * have to get the text-field first.  This make our integration
2419  * with XDesigner a little easier.
2420  */
2421 static void
2422 text_losing_focus_cb(   Widget w,
2423                         XtPointer client_data,
2424                         XtPointer call_data)
2425 {
2426     DtSpinBoxWidget spin = (DtSpinBoxWidget)client_data;
2427
2428     if (LosingFocusCallback(spin))
2429         XtCallCallbackList((Widget)spin, 
2430                            LosingFocusCallback(spin), 
2431                            (XtPointer)call_data);
2432 }
2433
2434 /*
2435  * We get the text-field activate callback, so pass it on to
2436  * the user if they requested it.  Our activate callback 
2437  * is just a convenience callback, so that the user doesn't
2438  * have to get the text-field first.  This make our integration
2439  * with XDesigner a little easier.
2440  */
2441 static void
2442 text_activate_cb(Widget w,
2443                 XtPointer client_data,
2444                 XtPointer call_data)
2445 {
2446     DtSpinBoxWidget spin = (DtSpinBoxWidget)client_data;
2447     
2448     if (ActivateCallback(spin))
2449         XtCallCallbackList((Widget)spin, 
2450                            ActivateCallback(spin), 
2451                            (XtPointer)call_data);
2452 }
2453
2454 /*
2455  * We get the text-field focus callback, so pass it on to
2456  * the user if they requested it.  Our focus callback 
2457  * is just a convenience callback, so that the user doesn't
2458  * have to get the text-field first.  This make our integration
2459  * with XDesigner a little easier.
2460  */
2461 static void
2462 text_focus_cb(  Widget w,
2463                 XtPointer client_data,
2464                 XtPointer call_data)
2465 {
2466     DtSpinBoxWidget spin = (DtSpinBoxWidget)client_data;
2467
2468     if (FocusCallback(spin))
2469         XtCallCallbackList((Widget)spin, 
2470                            FocusCallback(spin), 
2471                            (XtPointer)call_data);
2472 }
2473
2474
2475 /*
2476  * Synthetic resource get functions.
2477  */
2478
2479 static XmImportOperator
2480 _XmSetSyntheticResForChild(     Widget widget,
2481                                 int offset,
2482                                 XtArgVal * value)
2483
2484     return(XmSYNTHETIC_LOAD);
2485 }
2486
2487 void
2488 _DtSpinBoxGetArrowSize( Widget w,
2489                                 int resource_offset,
2490                                 XtArgVal *value)
2491 {
2492     DtSpinBoxWidget spin = (DtSpinBoxWidget)w;
2493     Dimension data;
2494     Arg arg;
2495
2496     /* MotifBc */
2497     XtSetArg(arg, XmNheight, &data);
2498     XtGetValues(UpArrow(spin), &arg, 1);
2499     *value = (XtArgVal)data;
2500 }
2501
2502 /*
2503  * Routines which manipulate the SpinBox list.  These are external
2504  * for use by users of our widget.
2505  */
2506
2507 Widget 
2508 DtCreateSpinBox(        Widget parent,
2509                         char *name,
2510                         Arg *arglist,
2511                         Cardinal num_args)
2512 {
2513     return(XtCreateWidget(name, dtSpinBoxWidgetClass, parent,
2514                           arglist, num_args));
2515 }
2516
2517 void
2518 DtSpinBoxAddItem(       Widget spinw,
2519                         XmString item,
2520                         int pos)
2521 {
2522     DtSpinBoxWidget spin = (DtSpinBoxWidget)spinw;
2523     DtSpinBoxPart *spin_p;
2524     XmString old, new_str, tmp;
2525     int total_items;
2526     _DtWidgetToAppContext(spinw);
2527     _DtAppLock(app);
2528
2529     spin_p = (DtSpinBoxPart*) &(XmField(spin,ipot,DtSpinBox,label,Widget));
2530
2531     total_items = ItemCount(spin) + 1;
2532     Items(spin) = (XmString *)XtRealloc((char*)Items(spin), 
2533                                           (sizeof(XmString) * total_items));
2534     new_str = XmStringCopy(item);
2535
2536     pos--;  /* User gives pos starting at 1 (0 means end of list) */
2537
2538     if ((pos < 0) || (pos > ItemCount(spin)))
2539         pos = ItemCount(spin);
2540
2541     if (pos >= ItemCount(spin))
2542         (Items(spin))[pos] = new_str;
2543     else {
2544         old = (Items(spin))[pos];
2545         (Items(spin))[pos] = new_str;
2546         for (pos++; pos < total_items; pos++) {
2547             tmp = (Items(spin))[pos];
2548             (Items(spin))[pos] = old;
2549             old = tmp;
2550         }
2551     }
2552     ItemCount(spin) = total_items;
2553
2554     if (Label(spin)) {
2555         SetMaximumLabelSize(spin_p);
2556         if (Editable(spin) == FALSE) {
2557             ClearShadow(spin, TRUE);
2558             if (RecomputeSize(spin))
2559                 SetSpinBoxSize(spin);
2560             LayoutChildren(spin);
2561             DrawShadow(spin);
2562         }
2563     }
2564
2565     /* Update the text-field or label */
2566     if (Editable(spin))
2567         SetTextFieldData(spin);
2568     else
2569         SetLabelData(spin);
2570
2571     _DtAppUnlock(app);
2572 }
2573
2574
2575
2576 void
2577 DtSpinBoxDeletePos(     Widget spinw,
2578                         int pos)
2579 {
2580     DtSpinBoxWidget spin = (DtSpinBoxWidget)spinw;
2581     DtSpinBoxPart *spin_p;
2582     int total_items;
2583     _DtWidgetToAppContext(spinw);
2584     _DtAppLock(app);
2585
2586     if (ItemCount(spin) < 1)
2587       {
2588         _DtAppUnlock(app);
2589         return;
2590       }
2591
2592     spin_p = (DtSpinBoxPart*) &(XmField(spin,ipot,DtSpinBox,label,Widget));
2593
2594     pos--;
2595     if ((pos < 0) || (pos > ItemCount(spin)))
2596         pos = ItemCount(spin) - 1;
2597
2598     total_items = ItemCount(spin) - 1;
2599     XmStringFree((Items(spin))[pos]);
2600
2601     /* To keep Position of SpinBox up to date */
2602     if (Position(spin) > 0 && 
2603         ((Position(spin) >= total_items) || pos < Position(spin)) )
2604         Position(spin) = Position(spin) - 1;
2605
2606     if (pos < ItemCount(spin)) {
2607         for (; pos < total_items; pos++)
2608             (Items(spin))[pos] = (Items(spin))[pos+1];
2609     }
2610     if (total_items > 0)
2611         Items(spin) = (XmString *)XtRealloc((char*)Items(spin),
2612                                           (sizeof(XmString) * total_items));
2613     else {
2614         XtFree((char *)Items(spin));
2615         Items(spin) = (XmString *)NULL;
2616     }
2617     ItemCount(spin) = total_items;
2618
2619     if (Label(spin)) {
2620         SetMaximumLabelSize(spin_p);
2621         if (Editable(spin) == FALSE) {
2622             ClearShadow(spin, TRUE);
2623             if (RecomputeSize(spin))
2624                 SetSpinBoxSize(spin);
2625             LayoutChildren(spin);
2626             DrawShadow(spin);
2627         }
2628     }
2629
2630     /* Update the text-field or label */
2631     if (Editable(spin))
2632         SetTextFieldData(spin);
2633     else
2634         SetLabelData(spin);
2635
2636     _DtAppUnlock(app);
2637 }
2638
2639 /*
2640  * Make the given item the currently visible item in the
2641  * text-field or label.
2642  */
2643 void
2644 DtSpinBoxSetItem(       Widget spinw,
2645                         XmString item)
2646 {
2647     DtSpinBoxWidget spin = (DtSpinBoxWidget)spinw;
2648     int i;
2649     _DtWidgetToAppContext(spinw);
2650     _DtAppLock(app);
2651
2652     if (item && ItemCount(spin)>0) {
2653         for (i = 0; i < ItemCount(spin); i++)
2654             if (XmStringCompare(item, (Items(spin))[i]))
2655                 break;
2656         if (i < ItemCount(spin)) {
2657             Position(spin) = i;
2658             if (Editable(spin))
2659                 SetTextFieldData(spin);
2660             else
2661                 SetLabelData(spin);
2662         }
2663         else
2664             XtWarning(SB_SET_ITEM);
2665     }
2666     else
2667         XtWarning(SB_SET_ITEM);
2668
2669     _DtAppUnlock(app);
2670 }