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