2672 lines
85 KiB
C
2672 lines
85 KiB
C
/*
|
|
* CDE - Common Desktop Environment
|
|
*
|
|
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
|
|
*
|
|
* These libraries and programs are free software; you can
|
|
* redistribute them and/or modify them under the terms of the GNU
|
|
* Lesser General Public License as published by the Free Software
|
|
* Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* These libraries and programs are distributed in the hope that
|
|
* they will be useful, but WITHOUT ANY WARRANTY; without even the
|
|
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
* PURPOSE. See the GNU Lesser General Public License for more
|
|
* details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with these libraries and programs; if not, write
|
|
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
|
|
* Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
/* $XConsortium: SpinBox.c /main/9 1996/10/29 12:49:58 cde-hp $
|
|
*
|
|
* (c) Copyright 1996 Digital Equipment Corporation.
|
|
* (c) Copyright 1993,1994,1996 Hewlett-Packard Company.
|
|
* (c) Copyright 1993,1994,1996 International Business Machines Corp.
|
|
* (c) Copyright 1993,1994,1996 Sun Microsystems, Inc.
|
|
* (c) Copyright 1993,1994,1996 Novell, Inc.
|
|
* (c) Copyright 1996 FUJITSU LIMITED.
|
|
* (c) Copyright 1996 Hitachi.
|
|
*/
|
|
|
|
/***********************************************************
|
|
Copyright 1993 Interleaf, Inc.
|
|
|
|
Permission to use, copy, modify, and distribute this software
|
|
and its documentation for any purpose without fee is granted,
|
|
provided that the above copyright notice appear in all copies
|
|
and that both copyright notice and this permission notice appear
|
|
in supporting documentation, and that the name of Interleaf not
|
|
be used in advertising or publicly pertaining to distribution of
|
|
the software without specific written prior permission.
|
|
|
|
Interleaf makes no representation about the suitability of this
|
|
software for any purpose. It is provided "AS IS" without any
|
|
express or implied warranty.
|
|
******************************************************************/
|
|
|
|
/*
|
|
* (C) Copyright 1991,1992, 1993
|
|
* Interleaf, Inc.
|
|
* 9 Hillside Avenue,
|
|
* Waltham, MA 02154
|
|
*
|
|
* SpinBox.c (DtSpinBoxWidget):
|
|
*
|
|
* I wanted a margin around the widget (outside the shadow, like buttons),
|
|
* so that the spin_box could be made the smae size as a
|
|
* push-button, etc. The bulletin-board widget always puts the shadow at
|
|
* the outside edge of the widget, so spin_box is a sublcass of
|
|
* manager, and we do everything ourselves.
|
|
*
|
|
* One must be carefull when using Dimension (for core width and height).
|
|
* Dimension is an unsigned short. This causes problems when subtracting
|
|
* and ending up with what should be a negative number (but it doesn't).
|
|
* All child widget positioning is done by the spin_box. We don't
|
|
* use any heavy-weight forms, etc. to help us out.
|
|
*
|
|
* There is no padding when editable. If using a label, give it a
|
|
* small margin, so it doesn't run up against the side of our
|
|
* shadow or the arrow.
|
|
*
|
|
* Make some of the SpinBox functions common, so they can be shared
|
|
* with ComboBox.
|
|
*
|
|
* Known bugs:
|
|
* Changing margin_width or margin_height resources when the
|
|
* spin_box has focus will probably result in display glitches.
|
|
*/
|
|
/*
|
|
* The DtSpinBox widget is rigged with the Motif widget binary compatibilit
|
|
* mechanism. All Motif-specific changes for this mechanism are preceded
|
|
* by a comment including the string "MotifBc".
|
|
*
|
|
* For a description of the Motif widget binary compatibility mechanism
|
|
* see the reference manual entry on XmResolveAllPartOffsets().
|
|
*
|
|
*/
|
|
|
|
/*Support for a legacy operating system has resulted in some leftover cruft
|
|
* code which wasn't ifdefed out, this needs to be cleaned, such code is
|
|
* marked Legacy: - 05/19/18 - C
|
|
*/
|
|
|
|
#include <Dt/DtMsgsP.h>
|
|
#include <Xm/DrawP.h>
|
|
#include <Xm/XmP.h>
|
|
#include <Xm/RepType.h>
|
|
#include "SpinBoxP.h"
|
|
#include "DtWidgetI.h"
|
|
|
|
#include <Xm/XmPrivate.h> /* _XmShellIsExclusive */
|
|
|
|
#ifdef I18N_MSG
|
|
#include <langinfo.h>
|
|
#endif
|
|
|
|
|
|
/*
|
|
* MotifBc
|
|
*/
|
|
#define DtSpinBoxIndex (XmManagerIndex + 1)
|
|
static XmOffsetPtr ipot; /* Instance part offset table */
|
|
static XmOffsetPtr cpot; /* Constraint part offset table */
|
|
|
|
static void ClassInitialize ();
|
|
static void Initialize (DtSpinBoxWidget request,
|
|
DtSpinBoxWidget new, ArgList given_args,
|
|
Cardinal *num_args);
|
|
static XmNavigability WidgetNavigable (DtSpinBoxWidget spin);
|
|
static void _SpinBoxFocusIn (DtSpinBoxWidget spin, XEvent *event,
|
|
char **params, Cardinal *num_params);
|
|
static void _SpinBoxFocusOut (DtSpinBoxWidget spin, XEvent *event,
|
|
char **params, Cardinal *num_params);
|
|
static void DrawHighlight (DtSpinBoxWidget spin, Boolean clear);
|
|
static void _SpinBoxUp (DtSpinBoxWidget spin,
|
|
XEvent *event, char **params,
|
|
Cardinal *num_params);
|
|
static void _SpinBoxDown (DtSpinBoxWidget spin,
|
|
XEvent *event, char **params,
|
|
Cardinal *num_params);
|
|
static void _SpinBoxLeft (DtSpinBoxWidget spin,
|
|
XEvent *event, char **params,
|
|
Cardinal *num_params);
|
|
static void _SpinBoxRight (DtSpinBoxWidget spin,
|
|
XEvent *event, char **params,
|
|
Cardinal *num_params);
|
|
static void _SpinBoxBeginLine (DtSpinBoxWidget spin,
|
|
XEvent *event, char **params,
|
|
Cardinal *num_params);
|
|
static void _SpinBoxEndLine (DtSpinBoxWidget spin,
|
|
XEvent *event, char **params,
|
|
Cardinal *num_params);
|
|
static void _SpinBoxGetFocus (DtSpinBoxWidget spin,
|
|
XEvent *event, char **params,
|
|
Cardinal *num_params);
|
|
static void _SpinBoxPrevTabGroup (DtSpinBoxWidget spin,
|
|
XEvent *event, char **params,
|
|
Cardinal *num_params);
|
|
static void _SpinBoxNextTabGroup (DtSpinBoxWidget spin,
|
|
XEvent *event, char **params,
|
|
Cardinal *num_params);
|
|
|
|
static void CheckResources (DtSpinBoxWidget spin);
|
|
static void Destroy (DtSpinBoxWidget spin);
|
|
static void Resize (DtSpinBoxWidget spin);
|
|
static void Redisplay (DtSpinBoxWidget w, XEvent *event,
|
|
Region region);
|
|
static XtGeometryResult GeometryManager (Widget w,
|
|
XtWidgetGeometry *request,
|
|
XtWidgetGeometry *reply);
|
|
static void SetSpinBoxSize (DtSpinBoxWidget spin);
|
|
static void ForceChildSizes (DtSpinBoxWidget spin);
|
|
static void CalculateSizes (DtSpinBoxWidget spin,
|
|
Dimension *pwidth,
|
|
Dimension *pheight,
|
|
Dimension *parrow_width);
|
|
static void LayoutChildren (DtSpinBoxWidget spin);
|
|
static Boolean SetValues (DtSpinBoxWidget current,
|
|
DtSpinBoxWidget request,
|
|
DtSpinBoxWidget new);
|
|
static void ClearShadow (DtSpinBoxWidget w, Boolean all);
|
|
static void DrawShadow (DtSpinBoxWidget w);
|
|
static void StoreResourceInfo (DtSpinBoxPart *spin_p,
|
|
DtSpinBoxPart *old_p,
|
|
Boolean do_items);
|
|
static char* GetTextString (XmString xm_string);
|
|
static void SetTextFieldData (DtSpinBoxWidget spin);
|
|
static void SetMaximumLabelSize (DtSpinBoxPart *spin_p);
|
|
static void SetLabelData (DtSpinBoxWidget spin);
|
|
static void timer_dispatch (XtPointer client_data, XtIntervalId *id);
|
|
static void TextFieldActivate (DtSpinBoxPart *spin_p);
|
|
static Boolean SendCallback (DtSpinBoxWidget spin, XEvent *event,
|
|
Boolean value_changed, int position,
|
|
float current, Boolean crossed);
|
|
static void FinishUpDown (DtSpinBoxWidget spin,
|
|
XtPointer arrow_call_data, int new_position,
|
|
float new_current, Boolean crossed);
|
|
static void up_cb (Widget w, XtPointer client_data,
|
|
XtPointer call_data);
|
|
static void down_cb (Widget w, XtPointer client_data,
|
|
XtPointer call_data);
|
|
static void disarm_cb (Widget w, XtPointer client_data,
|
|
XtPointer call_data);
|
|
static void grab_leave_cb (Widget w, XtPointer client_data,
|
|
XEvent *event, Boolean *dispatch);
|
|
static void text_losing_focus_cb (Widget w, XtPointer client_data,
|
|
XtPointer call_data);
|
|
static void text_activate_cb (Widget w, XtPointer client_data,
|
|
XtPointer call_data);
|
|
static void text_focus_cb (Widget w, XtPointer client_data,
|
|
XtPointer call_data);
|
|
static XmImportOperator _XmSetSyntheticResForChild (Widget widget,
|
|
int offset,
|
|
XtArgVal * value);
|
|
|
|
static XmString InitLabel = NULL;
|
|
|
|
/*
|
|
* MotifBc
|
|
*/
|
|
#define Label(w) XmField(w,ipot,DtSpinBox,label,Widget)
|
|
#define UpArrow(w) XmField(w,ipot,DtSpinBox,up_arrow,Widget)
|
|
#define DownArrow(w) XmField(w,ipot,DtSpinBox,down_arrow,Widget)
|
|
#define WhichArrow(w) XmField(w,ipot,DtSpinBox,which_arrow,unsigned char)
|
|
#define InitCb(w) XmField(w,ipot,DtSpinBox,init_cb,Boolean)
|
|
#define Grabbed(w) XmField(w,ipot,DtSpinBox,grabbed,Boolean)
|
|
#define Base(w) XmField(w,ipot,DtSpinBox,base,int)
|
|
#define Min(w) XmField(w,ipot,DtSpinBox,min,float)
|
|
#define Max(w) XmField(w,ipot,DtSpinBox,max,float)
|
|
#define Increment(w) XmField(w,ipot,DtSpinBox,increment,float)
|
|
#define Current(w) XmField(w,ipot,DtSpinBox,current,float)
|
|
#define FloatFormat(w) (String) &(XmField(w,ipot,DtSpinBox,float_format,char *))
|
|
#define OldWidth(w) XmField(w,ipot,DtSpinBox,old_width,Dimension)
|
|
#define OldHeight(w) XmField(w,ipot,DtSpinBox,old_height,Dimension)
|
|
#define LabelMaxLength(w) XmField(w,ipot,DtSpinBox,label_max_length,Dimension)
|
|
#define LabelMaxHeight(w) XmField(w,ipot,DtSpinBox,label_max_height,Dimension)
|
|
|
|
#define ArrowSensitivity(w) XmField(w,ipot,DtSpinBox,arrow_sensitivity,unsigned char)
|
|
#define DecimalPoints(w) XmField(w,ipot,DtSpinBox,decimal_points,short)
|
|
#define NumericIncrement(w) XmField(w,ipot,DtSpinBox,numeric_increment,int)
|
|
#define Maximum(w) XmField(w,ipot,DtSpinBox,maximum,int)
|
|
#define Minimum(w) XmField(w,ipot,DtSpinBox,minimum,int)
|
|
#define ItemCount(w) XmField(w,ipot,DtSpinBox,item_count,int)
|
|
#define Position(w) XmField(w,ipot,DtSpinBox,position,int)
|
|
#define ChildType(w) XmField(w,ipot,DtSpinBox,child_type,unsigned char)
|
|
#define Items(w) XmField(w,ipot,DtSpinBox,items,XmStringTable)
|
|
#define ActivateCallback(w) XmField(w,ipot,DtSpinBox,activate_callback,XtCallbackList)
|
|
#define Alignment(w) XmField(w,ipot,DtSpinBox,alignment,unsigned char)
|
|
#define ArrowLayout(w) XmField(w,ipot,DtSpinBox,arrow_layout,unsigned char)
|
|
#define ArrowSize(w) XmField(w,ipot,DtSpinBox,arrow_size,Dimension)
|
|
#define TextColumns(w) XmField(w,ipot,DtSpinBox,text_columns,short)
|
|
#define Editable(w) XmField(w,ipot,DtSpinBox,editable,Boolean)
|
|
#define FocusCallback(w) XmField(w,ipot,DtSpinBox,focus_callback,XtCallbackList)
|
|
#define InitialDelay(w) XmField(w,ipot,DtSpinBox,initial_delay,unsigned int)
|
|
#define LosingFocusCallback(w) XmField(w,ipot,DtSpinBox,losing_focus_callback,XtCallbackList)
|
|
#define MarginHeight(w) XmField(w,ipot,DtSpinBox,margin_height,Dimension)
|
|
#define MarginWidth(w) XmField(w,ipot,DtSpinBox,margin_width,Dimension)
|
|
#define TextMaxLength(w) XmField(w,ipot,DtSpinBox,text_max_length,int)
|
|
#define ModifyVerifyCallback(w) XmField(w,ipot,DtSpinBox,modify_verify_callback,XtCallbackList)
|
|
#define RecomputeSize(w) XmField(w,ipot,DtSpinBox,recompute_size,Boolean)
|
|
#define RepeatDelay(w) XmField(w,ipot,DtSpinBox,repeat_delay,unsigned int)
|
|
#define Text(w) XmField(w,ipot,DtSpinBox,text,Widget)
|
|
#define ValueChangedCallback(w) XmField(w,ipot,DtSpinBox,value_changed_callback,XtCallbackList)
|
|
#define Wrap(w) XmField(w,ipot,DtSpinBox,wrap,Boolean)
|
|
#define Timer(w) XmField(w,ipot,DtSpinBox,timer,XtIntervalId)
|
|
|
|
/* SpinBox and Superclass resource macros*/
|
|
#define PUnitType(w) w->primitive.unit_type
|
|
#define MUnitType(w) w->manager.unit_type
|
|
#define MFgPixel(w) w->manager.foreground
|
|
#define SPIN_SHADOW(w) w->manager.shadow_thickness
|
|
#define CBgPixel(w) w->core.background_pixel
|
|
#define CBgPixmap(w) w->core.background_pixmap
|
|
#define Width(w) w->core.width
|
|
#define Height(w) w->core.height
|
|
#define SPIN_MARGIN_W(w) MarginWidth(w)
|
|
#define SPIN_MARGIN_H(w) MarginHeight(w)
|
|
#define MAXINT 2147483647 /* Taken from TextF.c */
|
|
#define DEFAULT_COL 20
|
|
|
|
/* Legacy: Label get Focus */
|
|
static XtTranslations child_trans_label;
|
|
/* Legacy: Keyboard only for Text */
|
|
static XtTranslations child_trans_text;
|
|
|
|
static XtTranslations child_trans_arrow;
|
|
|
|
static XtTranslations child_trans;
|
|
static XtTranslations spin_trans;
|
|
|
|
|
|
static char const SpinBoxTranslationTable[] = "\
|
|
<FocusIn>: SpinBoxFocusIn() \n\
|
|
<FocusOut>: SpinBoxFocusOut() \n\
|
|
<Key>osfUp: SpinBoxUp() \n\
|
|
<Key>osfDown: SpinBoxDown() \n\
|
|
<Key>osfRight: SpinBoxRight() \n\
|
|
<Key>osfLeft: SpinBoxLeft() \n\
|
|
<Key>osfBeginLine: SpinBoxBeginLine() \n\
|
|
<Key>osfEndLine: SpinBoxEndLine() \n\
|
|
";
|
|
|
|
static char const SpinBoxChildTranslationTable[] = "\
|
|
<Key>osfUp: SpinBoxUp(child) \n\
|
|
<Key>osfDown: SpinBoxDown(child) \n\
|
|
<Key>osfRight: SpinBoxRight(child) \n\
|
|
<Key>osfLeft: SpinBoxLeft(child) \n\
|
|
<Key>osfBeginLine: SpinBoxBeginLine(child) \n\
|
|
<Key>osfEndLine: SpinBoxEndLine(child) \n\
|
|
";
|
|
|
|
/* #5: Label get focus */
|
|
static char const SpinBoxLabelTranslationTable[] = "\
|
|
<Key>osfUp: SpinBoxUp(child) \n\
|
|
<Key>osfDown: SpinBoxDown(child) \n\
|
|
<Key>osfRight: SpinBoxRight(child) \n\
|
|
<Key>osfLeft: SpinBoxLeft(child) \n\
|
|
<Key>osfBeginLine: SpinBoxBeginLine(child) \n\
|
|
<Key>osfEndLine: SpinBoxEndLine(child) \n\
|
|
<Key>osfDown: SpinBoxGetFocus() \n\
|
|
<Btn1Down>,<Btn1Up>: SpinBoxGetFocus() \n\
|
|
<Key>osfSelect: SpinBoxGetFocus() \n\
|
|
~s ~m ~a <Key>space: SpinBoxGetFocus() \n\
|
|
";
|
|
|
|
/* Legacy: Keyboard Only Traversing During Editable-Mode */
|
|
static char const SpinBoxTextTranslationTable[] = "\
|
|
<Key>osfUp: SpinBoxUp(child) SpinBoxRight(child)\n\
|
|
<Key>osfDown: SpinBoxDown(child) SpinBoxLeft(child)\n\
|
|
";
|
|
static char const SpinBoxArrowTranslationTable[] = "\
|
|
<Key>osfUp: SpinBoxUp(child) \n\
|
|
<Key>osfDown: SpinBoxDown(child) \n\
|
|
<Key>osfRight: SpinBoxRight(child) \n\
|
|
<Key>osfLeft: SpinBoxLeft(child) \n\
|
|
<Key>osfBeginLine: SpinBoxBeginLine(child) \n\
|
|
<Key>osfEndLine: SpinBoxEndLine(child) \n\
|
|
s ~m ~a <Key>Tab: SpinBoxPrevTabGroup()\n\
|
|
~m ~a <Key>Tab: SpinBoxNextTabGroup()\n\
|
|
";
|
|
|
|
|
|
static XtActionsRec SpinBoxActionTable[] = {
|
|
{"SpinBoxFocusIn", (XtActionProc)_SpinBoxFocusIn},
|
|
{"SpinBoxFocusOut", (XtActionProc)_SpinBoxFocusOut},
|
|
{"SpinBoxUp", (XtActionProc)_SpinBoxUp},
|
|
{"SpinBoxDown", (XtActionProc)_SpinBoxDown},
|
|
{"SpinBoxRight", (XtActionProc)_SpinBoxRight},
|
|
{"SpinBoxLeft", (XtActionProc)_SpinBoxLeft},
|
|
{"SpinBoxBeginLine", (XtActionProc)_SpinBoxBeginLine},
|
|
{"SpinBoxEndLine", (XtActionProc)_SpinBoxEndLine},
|
|
{"SpinBoxGetFocus", (XtActionProc)_SpinBoxGetFocus},
|
|
{"SpinBoxPrevTabGroup", (XtActionProc)_SpinBoxPrevTabGroup},
|
|
{"SpinBoxNextTabGroup", (XtActionProc)_SpinBoxNextTabGroup},
|
|
};
|
|
|
|
/* DtSpinBoxWidget resources */
|
|
/* MotifBc */
|
|
#define offset(field) XtOffset(DtSpinBoxWidget, field)
|
|
#define DtOffset(field) XmPartOffset(DtSpinBox,field)
|
|
static XmPartResource resources[] = {
|
|
{XmNshadowThickness, XmCShadowThickness, XmRHorizontalDimension,
|
|
sizeof(Dimension), offset(manager.shadow_thickness),
|
|
XmRImmediate, (XtPointer)TEXT_FIELD_SHADOW},
|
|
|
|
/* Common resources */
|
|
{DtNactivateCallback, DtCCallback, XmRCallback, sizeof(XtCallbackList),
|
|
DtOffset(activate_callback), XmRCallback,
|
|
(XtPointer)NULL},
|
|
{DtNalignment, DtCAlignment, XmRAlignment, sizeof(unsigned char),
|
|
DtOffset(alignment), XmRImmediate,
|
|
(XtPointer)DtALIGNMENT_BEGINNING},
|
|
{DtNarrowLayout, DtCArrowLayout, DtRArrowLayout, sizeof(unsigned char),
|
|
DtOffset(arrow_layout), XmRImmediate,
|
|
(XtPointer)DtARROWS_END},
|
|
{DtNarrowSensitivity, DtCArrowSensitivity, DtRArrowSensitivity,
|
|
sizeof(unsigned char), DtOffset(arrow_sensitivity),
|
|
XmRImmediate, (XtPointer)DtARROWS_SENSITIVE},
|
|
{DtNspinBoxChildType, DtCSpinBoxChildType, DtRSpinBoxChildType,
|
|
sizeof(unsigned char),
|
|
DtOffset(child_type), XmRImmediate, (XtPointer)DtSTRING},
|
|
{DtNcolumns, DtCColumns, XmRShort, sizeof(short),
|
|
DtOffset(text_columns), XmRImmediate, (XtPointer)DEFAULT_COL},
|
|
{DtNdecimalPoints, DtCDecimalPoints, XmRShort, sizeof( short ),
|
|
DtOffset(decimal_points), XmRImmediate, (XtPointer)0},
|
|
{DtNeditable, DtCEditable, XmRBoolean, sizeof(Boolean),
|
|
DtOffset(editable), XmRImmediate, (XtPointer)TRUE},
|
|
{DtNfocusCallback, DtCCallback, XmRCallback, sizeof(XtCallbackList),
|
|
DtOffset(focus_callback), XmRCallback,
|
|
(XtPointer)NULL},
|
|
{DtNincrementValue, DtCIncrementValue, XmRInt, sizeof(int),
|
|
DtOffset(numeric_increment), XmRImmediate, (XtPointer)1},
|
|
{DtNinitialDelay, DtCInitialDelay, XmRInt, sizeof(unsigned int),
|
|
DtOffset(initial_delay), XmRImmediate, (XtPointer)250},
|
|
{DtNnumValues, DtCNumValues, DtRNumValues, sizeof(int),
|
|
DtOffset(item_count), XmRImmediate, (XtPointer)0},
|
|
{DtNvalues, DtCItems, XmRXmStringTable, sizeof(XmStringTable),
|
|
DtOffset(items), XmRImmediate, (XtPointer)NULL},
|
|
{DtNlosingFocusCallback, DtCCallback, XmRCallback, sizeof(XtCallbackList),
|
|
DtOffset(losing_focus_callback), XmRCallback,
|
|
(XtPointer)NULL},
|
|
{DtNmarginHeight, DtCMarginHeight, XmRVerticalDimension, sizeof(Dimension),
|
|
DtOffset(margin_height), XmRImmediate, (XtPointer)MARGIN},
|
|
{DtNmarginWidth, DtCMarginWidth, XmRHorizontalDimension, sizeof(Dimension),
|
|
DtOffset(margin_width), XmRImmediate, (XtPointer)MARGIN},
|
|
{DtNmaximumValue, DtCMaximumValue, XmRInt, sizeof(int),
|
|
DtOffset(maximum), XmRImmediate, (XtPointer)10},
|
|
{DtNmaxLength, DtCMaxLength, XmRInt, sizeof(int),
|
|
DtOffset(text_max_length), XmRImmediate, (XtPointer)MAXINT},
|
|
{DtNminimumValue, DtCMinimumValue, XmRInt, sizeof(int),
|
|
DtOffset(minimum), XmRImmediate, (XtPointer)0},
|
|
{DtNmodifyVerifyCallback, DtCCallback, XmRCallback,
|
|
sizeof(XtCallbackList), DtOffset(modify_verify_callback),
|
|
XmRCallback, (XtPointer)NULL},
|
|
{DtNposition, DtCPosition, XmRInt, sizeof(int),
|
|
DtOffset(position), XmRImmediate, (XtPointer)0},
|
|
{DtNrecomputeSize, DtCRecomputeSize, XmRBoolean, sizeof(Boolean),
|
|
DtOffset(recompute_size), XmRImmediate, (XtPointer)TRUE},
|
|
{DtNrepeatDelay, DtCRepeatDelay, XmRInt, sizeof(unsigned int),
|
|
DtOffset(repeat_delay), XmRImmediate, (XtPointer)200},
|
|
{DtNtextField, DtCTextField, XmRWidget, sizeof(Widget),
|
|
DtOffset(text), XmRImmediate, (XtPointer)NULL},
|
|
{DtNvalueChangedCallback, DtCCallback, XmRCallback,
|
|
sizeof(XtCallbackList), DtOffset(value_changed_callback),
|
|
XmRCallback, (XtPointer)NULL},
|
|
{DtNwrap, DtCWrap, XmRBoolean, sizeof(Boolean),
|
|
DtOffset(wrap), XmRImmediate, (XtPointer)TRUE},
|
|
};
|
|
|
|
/* Synthetic resources. Only used for Motif API arrowSize right now */
|
|
static XmSyntheticResource syn_resources[] = {
|
|
{DtNarrowSize, sizeof(Dimension), DtOffset(arrow_size),
|
|
_DtSpinBoxGetArrowSize, _XmSetSyntheticResForChild},
|
|
{DtNmarginHeight, sizeof(Dimension), DtOffset(margin_height),
|
|
XmeFromVerticalPixels, XmeToVerticalPixels},
|
|
{DtNmarginWidth, sizeof(Dimension), DtOffset(margin_width),
|
|
XmeFromHorizontalPixels, XmeToHorizontalPixels},
|
|
};
|
|
#undef DtOffset
|
|
#undef offset
|
|
|
|
/* Need Class Extension for widget navigation */
|
|
static XmBaseClassExtRec baseClassExtRec = {
|
|
NULL,
|
|
NULLQUARK,
|
|
XmBaseClassExtVersion,
|
|
sizeof(XmBaseClassExtRec),
|
|
(XtInitProc)NULL, /* InitializePrehook */
|
|
(XtSetValuesFunc)NULL, /* SetValuesPrehook */
|
|
(XtInitProc)NULL, /* InitializePosthook */
|
|
(XtSetValuesFunc)NULL, /* SetValuesPosthook */
|
|
NULL, /* secondaryObjectClass */
|
|
(XtInitProc)NULL, /* secondaryCreate */
|
|
(XmGetSecResDataFunc)NULL, /* getSecRes data */
|
|
{ 0 }, /* fastSubclass flags */
|
|
(XtArgsProc)NULL, /* getValuesPrehook */
|
|
(XtArgsProc)NULL, /* getValuesPosthook */
|
|
(XtWidgetClassProc)NULL, /* classPartInitPrehook */
|
|
(XtWidgetClassProc)NULL, /* classPartInitPosthook*/
|
|
NULL, /* ext_resources */
|
|
NULL, /* compiled_ext_resources*/
|
|
0, /* num_ext_resources */
|
|
FALSE, /* use_sub_resources */
|
|
(XmWidgetNavigableProc)WidgetNavigable,
|
|
/* widgetNavigable */
|
|
(XmFocusChangeProc)NULL, /* focusChange */
|
|
(XmWrapperData)NULL /* wrapperData */
|
|
};
|
|
|
|
/*
|
|
* Define Class Record.
|
|
*/
|
|
externaldef(dtspinboxclassrec) DtSpinBoxClassRec dtSpinBoxClassRec =
|
|
{
|
|
{ /* core_class fields */
|
|
(WidgetClass)&(xmManagerClassRec), /* superclass */
|
|
(String)"DtSpinBox", /* class_name */
|
|
(Cardinal)sizeof(DtSpinBoxPart), /* widget_size */
|
|
(XtProc)ClassInitialize, /* class_initialize */
|
|
(XtWidgetClassProc)NULL, /* class_part_init */
|
|
(XtEnum)FALSE, /* class_inited */
|
|
(XtInitProc)Initialize, /* initialize */
|
|
(XtArgsProc)NULL, /* initialize_hook */
|
|
(XtRealizeProc)XtInheritRealize, /* realize */
|
|
(XtActionList)SpinBoxActionTable, /* actions */
|
|
(Cardinal)XtNumber(SpinBoxActionTable), /* num_actions */
|
|
(XtResourceList)resources, /* resources */
|
|
(Cardinal)XtNumber(resources), /* num_resources */
|
|
(XrmClass)NULLQUARK, /* xrm_class */
|
|
(Boolean)TRUE, /* compress_motion */
|
|
(XtEnum)XtExposeCompressMaximal, /* compress_exposure */
|
|
(Boolean)TRUE, /* compress_enterleave*/
|
|
(Boolean)FALSE, /* visible_interest */
|
|
(XtWidgetProc)Destroy, /* destroy */
|
|
(XtWidgetProc)Resize, /* resize */
|
|
(XtExposeProc)Redisplay, /* expose */
|
|
(XtSetValuesFunc)SetValues, /* set_values */
|
|
(XtArgsFunc)NULL, /* set values hook */
|
|
(XtAlmostProc)XtInheritSetValuesAlmost, /* set values almost */
|
|
(XtArgsProc)NULL, /* get values hook */
|
|
(XtAcceptFocusProc)NULL, /* accept_focus */
|
|
(XtVersionType)XtVersionDontCheck, /* Version */
|
|
(XtPointer)NULL, /* PRIVATE cb list */
|
|
(String)XtInheritTranslations, /* tm_table */
|
|
(XtGeometryHandler)XtInheritQueryGeometry, /* query_geom */
|
|
(XtStringProc)XtInheritDisplayAccelerator, /* display_accelerator*/
|
|
(XtPointer)&baseClassExtRec /* extension */
|
|
},
|
|
{ /* composite_class fields */
|
|
(XtGeometryHandler)GeometryManager, /* geometry_manager */
|
|
(XtWidgetProc)XtInheritChangeManaged, /* change_managed */
|
|
(XtWidgetProc)XtInheritInsertChild, /* insert_child */
|
|
(XtWidgetProc)XtInheritDeleteChild, /* delete_child */
|
|
(XtPointer)NULL /* extension */
|
|
},
|
|
{ /* constraint_class fields */
|
|
(XtResourceList)NULL, /* resources */
|
|
(Cardinal)0, /* num_resources */
|
|
(Cardinal)0, /* constraint_size */
|
|
(XtInitProc)NULL, /* initialize */
|
|
(XtWidgetProc)NULL, /* destroy */
|
|
(XtSetValuesFunc)NULL, /* set_values */
|
|
(XtPointer)NULL /* extension */
|
|
},
|
|
{ /* manager class */
|
|
(String)XtInheritTranslations, /* translations */
|
|
(XmSyntheticResource*)syn_resources, /* syn resources */
|
|
(int)XtNumber(syn_resources), /* num syn_resources */
|
|
(XmSyntheticResource*)NULL, /* get_cont_resources */
|
|
(int)0, /* num_get_cont_resources */
|
|
(XmParentProcessProc)XmInheritParentProcess,/* parent_process */
|
|
(XtPointer)NULL /* extension */
|
|
},
|
|
{ /* spin_box_class fields */
|
|
(Boolean)0,
|
|
}
|
|
};
|
|
|
|
externaldef(dtspinboxwidgetclass) WidgetClass dtSpinBoxWidgetClass =
|
|
(WidgetClass)&dtSpinBoxClassRec;
|
|
|
|
static XmRepTypeId _DtRID_SB_ARROW_SENSITIVITY_TYPE;
|
|
static String _DtArrowSensitivityNames[] = {
|
|
"arrows_insensitive",
|
|
"arrows_increment_sensitive",
|
|
"arrows_decrement_sensitive",
|
|
"arrows_sensitive"
|
|
};
|
|
|
|
/*
|
|
* Must set up the record type for the class extensions to work.
|
|
*/
|
|
static void
|
|
ClassInitialize(void)
|
|
{
|
|
baseClassExtRec.record_type = XmQmotif;
|
|
/*
|
|
* MotifBc
|
|
*/
|
|
XmResolveAllPartOffsets(dtSpinBoxWidgetClass, &ipot, &cpot);
|
|
|
|
child_trans = XtParseTranslationTable(SpinBoxChildTranslationTable);
|
|
spin_trans = XtParseTranslationTable(SpinBoxTranslationTable);
|
|
|
|
child_trans_label=XtParseTranslationTable(SpinBoxLabelTranslationTable);
|
|
child_trans_text=XtParseTranslationTable(SpinBoxTextTranslationTable);
|
|
child_trans_arrow=XtParseTranslationTable(SpinBoxArrowTranslationTable);
|
|
|
|
_DtRID_SB_ARROW_SENSITIVITY_TYPE =
|
|
XmRepTypeRegister(DtRArrowSensitivity, _DtArrowSensitivityNames,
|
|
NULL, XtNumber(_DtArrowSensitivityNames));
|
|
|
|
InitLabel = XmStringCreateLocalized(SB_LABEL);
|
|
}
|
|
|
|
/*
|
|
* SpinBox initialization function. This builds the widgets inside
|
|
* our widget, to get the correct layout. If the editable resource
|
|
* is TRUE, we create a textField; if FALSE, we create a label. If the
|
|
* user changes this resource later, we will create the other widget
|
|
* (textField or Label). We don't want to carry backage from both
|
|
* widgets if the user never changes the editable resource.
|
|
*/
|
|
static void
|
|
Initialize( DtSpinBoxWidget request,
|
|
DtSpinBoxWidget new,
|
|
ArgList given_args,
|
|
Cardinal *num_args)
|
|
{ /* MotifBc */
|
|
DtSpinBoxPart *spin_p = (DtSpinBoxPart*)
|
|
&(XmField(new,ipot,DtSpinBox,label,Widget));
|
|
char *widget_name;
|
|
Arg args[20];
|
|
int n;
|
|
/* Resolution Independent */
|
|
unsigned char unit_type = MUnitType(new);
|
|
|
|
/* Overwrite the manager's focusIn and focusOut translations */
|
|
XtOverrideTranslations((Widget)new, spin_trans);
|
|
|
|
widget_name = XtMalloc(strlen(XtName((Widget)new)) + 10);
|
|
|
|
Text(new) = (Widget)NULL;
|
|
Label(new) = (Widget)NULL;
|
|
OldWidth(new) = 0;
|
|
OldHeight(new) = 0;
|
|
InitCb(new) = TRUE;
|
|
Grabbed(new) = FALSE;
|
|
|
|
CheckResources(new);
|
|
|
|
/*
|
|
* Create the text or label depending on editable resource.
|
|
*/
|
|
if (Editable(new)) {
|
|
sprintf(widget_name, "%s_TF", XtName((Widget)new));
|
|
n = 0;
|
|
XtSetArg(args[n], XmNmaxLength, TextMaxLength(new)); n++;
|
|
XtSetArg(args[n], XmNmarginWidth, TEXT_CONTEXT_MARGIN); n++;
|
|
XtSetArg(args[n], XmNmarginHeight, 2); n++;
|
|
/* Resolution Independent */
|
|
if (unit_type != XmPIXELS) {
|
|
XtSetArg(args[n], XmNunitType, XmPIXELS); n++;
|
|
}
|
|
Text(new) = XtCreateManagedWidget(widget_name,
|
|
xmTextFieldWidgetClass,
|
|
(Widget)new, args, n);
|
|
XtAddCallback(Text(new), XmNlosingFocusCallback,
|
|
text_losing_focus_cb, (XtPointer)new);
|
|
XtAddCallback(Text(new), XmNactivateCallback,
|
|
text_activate_cb, (XtPointer)new);
|
|
XtAddCallback(Text(new), XmNfocusCallback,
|
|
text_focus_cb, (XtPointer)new);
|
|
|
|
if (TextColumns(request) == DEFAULT_COL && Width(request)) {
|
|
Dimension width;
|
|
CalculateSizes(new, &width, NULL, NULL);
|
|
XtSetArg(args[n], XmNwidth, width); n++;
|
|
}
|
|
else {
|
|
XtSetArg(args[n], XmNcolumns, TextColumns(new)); n++;
|
|
}
|
|
if (unit_type != XmPIXELS) {
|
|
XtSetArg(args[n], XmNunitType, unit_type); n++;
|
|
}
|
|
XtSetValues(Text(new), args, n);
|
|
}
|
|
else {
|
|
sprintf(widget_name, "%s_Label", XtName((Widget)new));
|
|
SPIN_SHADOW(new) = LABEL_SHADOW;
|
|
n = 0;
|
|
XtSetArg(args[n], XmNalignment, Alignment(new)); n++;
|
|
XtSetArg(args[n], XmNrecomputeSize, FALSE); n++;
|
|
XtSetArg(args[n], XmNlabelString, InitLabel); n++;
|
|
XtSetArg(args[n], XmNmarginLeft, LABEL_PADDING); n++;
|
|
XtSetArg(args[n], XmNmarginRight, LABEL_PADDING); n++;
|
|
XtSetArg(args[n], XmNmarginWidth, TEXT_CONTEXT_MARGIN); n++;
|
|
XtSetArg(args[n], XmNmarginHeight, 2); n++;
|
|
if (unit_type != XmPIXELS) {
|
|
XtSetArg(args[n], XmNunitType, XmPIXELS); n++;
|
|
}
|
|
Label(new) = XtCreateManagedWidget(widget_name, xmLabelWidgetClass,
|
|
(Widget)new, args, n);
|
|
|
|
if (unit_type != XmPIXELS) {
|
|
XtSetArg(args[n], XmNunitType, unit_type); n++;
|
|
}
|
|
if (Width(new)) {
|
|
Dimension width;
|
|
CalculateSizes(new, &width, NULL, NULL);
|
|
XtSetArg(args[n], XmNwidth, width); n++;
|
|
}
|
|
if (n>0)
|
|
XtSetValues(Label(new), args, n);
|
|
}
|
|
|
|
/*
|
|
* Create the 2 ArrowWidgets.
|
|
*/
|
|
sprintf(widget_name, "%s_Up", XtName((Widget)new));
|
|
n = 0;
|
|
if (ArrowLayout(new) == DtARROWS_SPLIT) {
|
|
XtSetArg(args[n], XmNarrowDirection, XmARROW_RIGHT); n++;
|
|
}
|
|
XtSetArg(args[n], XmNhighlightThickness, 0); n++;
|
|
XtSetArg(args[n], XmNshadowThickness, 0); n++;
|
|
XtSetArg(args[n], XmNtraversalOn, FALSE); n++;
|
|
XtSetArg(args[n], XmNforeground, CBgPixel(new)); n++;
|
|
UpArrow(new) = XtCreateManagedWidget(widget_name,
|
|
xmArrowButtonWidgetClass,
|
|
(Widget)new, args, n);
|
|
XtOverrideTranslations((Widget)UpArrow(new), child_trans_arrow);
|
|
|
|
sprintf(widget_name, "%s_Down", XtName((Widget)new));
|
|
if (ArrowLayout(new) == DtARROWS_SPLIT) {
|
|
XtSetArg(args[n], XmNarrowDirection, XmARROW_LEFT); n++;
|
|
}
|
|
else {
|
|
XtSetArg(args[n], XmNarrowDirection, XmARROW_DOWN); n++;
|
|
}
|
|
DownArrow(new) = XtCreateManagedWidget(widget_name,
|
|
xmArrowButtonWidgetClass,
|
|
(Widget)new, args, n);
|
|
XtOverrideTranslations((Widget)DownArrow(new), child_trans_arrow);
|
|
|
|
/* Set sensitivity of arrows (up arrow is right arrow) */
|
|
if ((ArrowSensitivity(new) == DtARROWS_INSENSITIVE) ||
|
|
(ArrowSensitivity(new) == DtARROWS_DECREMENT_SENSITIVE))
|
|
XtSetSensitive(UpArrow(new), FALSE);
|
|
if ((ArrowSensitivity(new) == DtARROWS_INSENSITIVE) ||
|
|
(ArrowSensitivity(new) == DtARROWS_INCREMENT_SENSITIVE))
|
|
XtSetSensitive(DownArrow(new), FALSE);
|
|
|
|
/*
|
|
* Arm causes the value to change and the timer to start.
|
|
* Disarm (leaveNotify from grab) causes the timer to stop.
|
|
*/
|
|
XtAddCallback(UpArrow(new), XmNarmCallback, up_cb, (XtPointer)new);
|
|
XtAddCallback(UpArrow(new), XmNdisarmCallback, disarm_cb,
|
|
(XtPointer)new);
|
|
XtAddEventHandler(UpArrow(new), LeaveWindowMask, FALSE, grab_leave_cb,
|
|
(XtPointer)new);
|
|
XtAddCallback(DownArrow(new), XmNarmCallback, down_cb, (XtPointer)new);
|
|
XtAddCallback(DownArrow(new), XmNdisarmCallback, disarm_cb,
|
|
(XtPointer)new);
|
|
XtAddEventHandler(DownArrow(new), LeaveWindowMask, FALSE,
|
|
grab_leave_cb, (XtPointer)new);
|
|
|
|
/* Initialize everything based on what the resource values are */
|
|
StoreResourceInfo(spin_p, NULL, TRUE);
|
|
|
|
/*
|
|
* Set initial value in text or label if items was specified
|
|
*/
|
|
if (Editable(new) == FALSE) {
|
|
SetLabelData(new);
|
|
SetMaximumLabelSize(spin_p);
|
|
}
|
|
else
|
|
{
|
|
SetTextFieldData(new);
|
|
}
|
|
SetSpinBoxSize(new);
|
|
LayoutChildren(new);
|
|
XtFree(widget_name);
|
|
|
|
/* Store Converter for DtNmaximumValue, DtNminimumValue, DtNincrementValue,
|
|
*/
|
|
XtSetTypeConverter(XmRString, DtRMaximumValue, XtCvtStringToInt, NULL, 0,
|
|
XtCacheAll, NULL);
|
|
XtSetTypeConverter(XmRString, DtRMinimumValue, XtCvtStringToInt, NULL, 0,
|
|
XtCacheAll, NULL);
|
|
XtSetTypeConverter(XmRString, DtRIncrementValue, XtCvtStringToInt, NULL, 0,
|
|
XtCacheAll, NULL);
|
|
XtSetTypeConverter(XmRString, DtRNumValues, XtCvtStringToInt, NULL, 0,
|
|
XtCacheAll, NULL);
|
|
/*
|
|
* this is so these resources can be set uniformly through XtCreateWidget
|
|
* or through defaults file as *spin.backgound
|
|
*/
|
|
|
|
n=0;
|
|
XtSetArg(args[n],XmNbackground,CBgPixel(new));n++;
|
|
XtSetArg(args[n],XmNbackgroundPixmap,CBgPixmap(new));n++;
|
|
XtSetArg(args[n],XmNforeground,MFgPixel(new));n++;
|
|
if(Text(new))
|
|
XtSetValues (Text(new),args,n);
|
|
if(Label(new))
|
|
XtSetValues (Label(new),args,n);
|
|
if(UpArrow(new))
|
|
XtSetValues (UpArrow(new),args,n);
|
|
if(DownArrow(new))
|
|
XtSetValues (DownArrow(new),args,n);
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* Allow the manager to gain focus if not editable. If editable (using
|
|
* text-field), then let the toolkit give focus to the text-field.
|
|
*/
|
|
static XmNavigability
|
|
WidgetNavigable(DtSpinBoxWidget spin)
|
|
{
|
|
XmNavigationType nav_type = ((XmManagerWidget)spin)->manager.navigation_type;
|
|
|
|
if (spin->core.sensitive && spin->core.ancestor_sensitive &&
|
|
((XmManagerWidget)spin)->manager.traversal_on) {
|
|
if ((nav_type == XmSTICKY_TAB_GROUP) ||
|
|
(nav_type == XmEXCLUSIVE_TAB_GROUP) ||
|
|
((nav_type == XmTAB_GROUP) &&
|
|
!_XmShellIsExclusive((Widget)spin))) {
|
|
if (Editable(spin))
|
|
return(XmDESCENDANTS_TAB_NAVIGABLE);
|
|
else
|
|
return(XmTAB_NAVIGABLE);
|
|
}
|
|
return(XmDESCENDANTS_NAVIGABLE);
|
|
}
|
|
return(XmNOT_NAVIGABLE);
|
|
}
|
|
|
|
/*
|
|
* The spin_box gets focus.
|
|
*/
|
|
static void
|
|
_SpinBoxFocusIn( DtSpinBoxWidget spin,
|
|
XEvent *event,
|
|
char **params,
|
|
Cardinal *num_params)
|
|
{
|
|
DrawHighlight(spin, FALSE);
|
|
}
|
|
|
|
/*
|
|
* The spin_box loses focus.
|
|
*/
|
|
static void
|
|
_SpinBoxFocusOut( DtSpinBoxWidget spin,
|
|
XEvent *event,
|
|
char **params,
|
|
Cardinal *num_params)
|
|
{
|
|
DrawHighlight(spin, TRUE);
|
|
}
|
|
|
|
/*
|
|
* This function gets called whenever we draw or clear the shadow (to
|
|
* redraw highlight during resize, etc), as well as during focus_in
|
|
* and focus_out events.
|
|
*/
|
|
static void
|
|
DrawHighlight( DtSpinBoxWidget spin,
|
|
Boolean clear)
|
|
{
|
|
XRectangle rect[4] ;
|
|
|
|
if (XtIsRealized((Widget)spin)) {
|
|
if (clear) {
|
|
rect[0].x = rect[1].x = rect[2].x = 0;
|
|
rect[3].x = OldWidth(spin) - SPIN_MARGIN_W(spin);
|
|
rect[0].y = rect[2].y = rect[3].y = 0 ;
|
|
rect[1].y = OldHeight(spin) - SPIN_MARGIN_H(spin);
|
|
rect[0].width = rect[1].width = OldWidth(spin);
|
|
rect[2].width = rect[3].width = SPIN_MARGIN_W(spin);
|
|
rect[0].height = rect[1].height = SPIN_MARGIN_H(spin);
|
|
rect[2].height = rect[3].height = OldHeight(spin);
|
|
XFillRectangles(XtDisplayOfObject((Widget)spin),
|
|
XtWindowOfObject((Widget)spin),
|
|
spin->manager.background_GC, rect, 4);
|
|
}
|
|
else if (XmGetFocusWidget((Widget)spin) == (Widget)spin) {
|
|
rect[0].x = rect[1].x = rect[2].x = 0;
|
|
rect[3].x = XtWidth(spin) - SPIN_MARGIN_W(spin);
|
|
rect[0].y = rect[2].y = rect[3].y = 0 ;
|
|
rect[1].y = XtHeight(spin) - SPIN_MARGIN_H(spin);
|
|
rect[0].width = rect[1].width = XtWidth(spin);
|
|
rect[2].width = rect[3].width = SPIN_MARGIN_W(spin);
|
|
rect[0].height = rect[1].height = SPIN_MARGIN_H(spin);
|
|
rect[2].height = rect[3].height = XtHeight(spin);
|
|
XFillRectangles(XtDisplayOfObject((Widget)spin),
|
|
XtWindowOfObject((Widget)spin),
|
|
spin->manager.highlight_GC, rect, 4);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* osfUp virtual key hit. Simulate hitting the up arrow.
|
|
*/
|
|
static void
|
|
_SpinBoxUp( DtSpinBoxWidget spin,
|
|
XEvent *event,
|
|
char **params,
|
|
Cardinal *num_params)
|
|
{
|
|
if (*num_params != 0) /* params means label or arrows */
|
|
spin = (DtSpinBoxWidget)XtParent(spin);
|
|
|
|
if (ArrowLayout(spin) != DtARROWS_SPLIT) {
|
|
up_cb((Widget)UpArrow(spin), (XtPointer)spin, NULL);
|
|
disarm_cb((Widget)UpArrow(spin), (XtPointer)spin, NULL);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* osfDown virtual key hit. Simulate hitting the down arrow.
|
|
*/
|
|
static void
|
|
_SpinBoxDown(DtSpinBoxWidget spin,
|
|
XEvent *event,
|
|
char **params,
|
|
Cardinal *num_params)
|
|
{
|
|
if (*num_params != 0) /* params means label or arrows */
|
|
spin = (DtSpinBoxWidget)XtParent(spin);
|
|
|
|
if (ArrowLayout(spin) != DtARROWS_SPLIT) {
|
|
down_cb((Widget)DownArrow(spin), (XtPointer)spin, NULL);
|
|
disarm_cb((Widget)DownArrow(spin), (XtPointer)spin, NULL);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* osfRight virtual key hit. Simulate hitting the up arrow.
|
|
*/
|
|
static void
|
|
_SpinBoxRight(DtSpinBoxWidget spin,
|
|
XEvent *event,
|
|
char **params,
|
|
Cardinal *num_params)
|
|
{
|
|
if (*num_params != 0) /* params means label or arrows */
|
|
spin = (DtSpinBoxWidget)XtParent(spin);
|
|
|
|
if (ArrowLayout(spin) == DtARROWS_SPLIT) {
|
|
up_cb((Widget)UpArrow(spin), (XtPointer)spin, NULL);
|
|
disarm_cb((Widget)UpArrow(spin), (XtPointer)spin, NULL);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* osfLeft virtual key hit. Simulate hitting the down arrow.
|
|
*/
|
|
static void
|
|
_SpinBoxLeft(DtSpinBoxWidget spin,
|
|
XEvent *event,
|
|
char **params,
|
|
Cardinal *num_params)
|
|
{
|
|
if (*num_params != 0) /* params means label or arrows */
|
|
spin = (DtSpinBoxWidget)XtParent(spin);
|
|
|
|
if (ArrowLayout(spin) == DtARROWS_SPLIT) {
|
|
down_cb((Widget)DownArrow(spin), (XtPointer)spin, NULL);
|
|
disarm_cb((Widget)DownArrow(spin), (XtPointer)spin, NULL);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* osfBeginLine virtual key hit. Go to first item.
|
|
*/
|
|
static void
|
|
_SpinBoxBeginLine( DtSpinBoxWidget spin,
|
|
XEvent *event,
|
|
char **params,
|
|
Cardinal *num_params)
|
|
{
|
|
int new_position;
|
|
float new_current = 0.0;
|
|
|
|
if (*num_params != 0) /* params means label or arrows */
|
|
spin = (DtSpinBoxWidget)XtParent(spin);
|
|
|
|
if (ChildType(spin) == DtNUMERIC) {
|
|
new_position = Minimum(spin);
|
|
new_current = Min(spin);
|
|
}
|
|
else {
|
|
new_position = 0;
|
|
}
|
|
if (SendCallback(spin, event, FALSE, new_position,
|
|
new_current, FALSE) == TRUE) {
|
|
/* User said yes, so set widget values */
|
|
Position(spin) = new_position;
|
|
Current(spin) = new_current;
|
|
if (Editable(spin))
|
|
SetTextFieldData(spin);
|
|
else
|
|
SetLabelData(spin);
|
|
|
|
/* send value_changed callback */
|
|
(void)SendCallback(spin, event, TRUE, Position(spin),
|
|
Current(spin), FALSE);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* osfEndLine virtual key hit. Go to last item.
|
|
*/
|
|
static void
|
|
_SpinBoxEndLine( DtSpinBoxWidget spin,
|
|
XEvent *event,
|
|
char **params,
|
|
Cardinal *num_params)
|
|
{
|
|
int new_position;
|
|
float new_current = 0.0;
|
|
|
|
if (*num_params != 0) /* params means label or arrows */
|
|
spin = (DtSpinBoxWidget)XtParent(spin);
|
|
|
|
if (ChildType(spin) == DtNUMERIC) {
|
|
new_position = Maximum(spin);
|
|
new_current = Max(spin);
|
|
}
|
|
else {
|
|
new_position = ItemCount(spin) - 1;
|
|
}
|
|
if (SendCallback(spin, event, FALSE, new_position,
|
|
new_current, FALSE) == TRUE) {
|
|
/* User said yes, so set widget values */
|
|
Position(spin) = new_position;
|
|
Current(spin) = new_current;
|
|
if (Editable(spin))
|
|
SetTextFieldData(spin);
|
|
else
|
|
SetLabelData(spin);
|
|
|
|
/* send value_changed callback */
|
|
(void)SendCallback(spin, event, TRUE, Position(spin),
|
|
Current(spin), FALSE);
|
|
}
|
|
}
|
|
/*
|
|
* Legacy: Get Focus for SpinBox when hit its label part
|
|
*/
|
|
static void
|
|
_SpinBoxGetFocus( DtSpinBoxWidget spin,
|
|
XEvent *event,
|
|
char **params,
|
|
Cardinal *num_params)
|
|
{
|
|
XmProcessTraversal((Widget)XtParent(spin),
|
|
(XmTraversalDirection) XmTRAVERSE_CURRENT);
|
|
}
|
|
|
|
/*
|
|
* Legacy: Process Focus Traversal for SpinBox when cursor is in its arrow part
|
|
*/
|
|
static void
|
|
_SpinBoxPrevTabGroup(DtSpinBoxWidget spin,
|
|
XEvent *event,
|
|
char **params,
|
|
Cardinal *num_params)
|
|
{
|
|
XmProcessTraversal((Widget)XtParent(spin),
|
|
(XmTraversalDirection) XmTRAVERSE_PREV_TAB_GROUP);
|
|
}
|
|
|
|
static void
|
|
_SpinBoxNextTabGroup(DtSpinBoxWidget spin,
|
|
XEvent *event,
|
|
char **params,
|
|
Cardinal *num_params)
|
|
{
|
|
XmProcessTraversal((Widget)XtParent(spin),
|
|
(XmTraversalDirection) XmTRAVERSE_NEXT_TAB_GROUP);
|
|
}
|
|
|
|
/*
|
|
* This function goes through most of the resources and makes sure
|
|
* they have legal values.
|
|
*/
|
|
static void
|
|
CheckResources(DtSpinBoxWidget spin)
|
|
{
|
|
if (!XmRepTypeValidValue(_DtRID_SB_ARROW_SENSITIVITY_TYPE,
|
|
ArrowSensitivity(spin), (Widget)spin)) {
|
|
XtWarning(SB_ARROW_SENSITIVE);
|
|
ArrowSensitivity(spin) = DtARROWS_SENSITIVE;
|
|
}
|
|
if ((Alignment(spin) != DtALIGNMENT_CENTER) &&
|
|
(Alignment(spin) != DtALIGNMENT_BEGINNING) &&
|
|
(Alignment(spin) != DtALIGNMENT_END)) {
|
|
XtWarning(SB_ALIGNMENT);
|
|
Alignment(spin) = DtALIGNMENT_CENTER;
|
|
}
|
|
if (InitialDelay(spin) == 0) {
|
|
XtWarning(SB_INIT_DELAY);
|
|
InitialDelay(spin) = 250;
|
|
}
|
|
if ((ArrowLayout(spin) != DtARROWS_FLAT_BEGINNING) &&
|
|
(ArrowLayout(spin) != DtARROWS_FLAT_END) &&
|
|
(ArrowLayout(spin) != DtARROWS_SPLIT) &&
|
|
(ArrowLayout(spin) != DtARROWS_BEGINNING) &&
|
|
(ArrowLayout(spin) != DtARROWS_END)) {
|
|
XtWarning(SB_ARROW_LAYOUT);
|
|
ArrowLayout(spin) = DtARROWS_BEGINNING;
|
|
}
|
|
if (RepeatDelay(spin) == 0) {
|
|
XtWarning(SB_REPEAT_DELAY);
|
|
RepeatDelay(spin) = 200;
|
|
}
|
|
if (ItemCount(spin) < 0) {
|
|
XtWarning(SB_ITEM_COUNT);
|
|
ItemCount(spin) = 0;
|
|
}
|
|
if ((ChildType(spin) == DtSTRING) &&
|
|
((Position(spin) < 0) ||
|
|
((Position(spin) >= ItemCount(spin)) &&
|
|
(ItemCount(spin) > 0)))) {
|
|
XtWarning(SB_POSITION_STRING);
|
|
Position(spin) = 0;
|
|
}
|
|
if ((DecimalPoints(spin) < 0) ||
|
|
(DecimalPoints(spin) > MAX_FLOAT_DECIMALS)) {
|
|
XtWarning(SB_DECIMAL_POINTS);
|
|
DecimalPoints(spin) = 0;
|
|
}
|
|
if (Minimum(spin) > Maximum(spin)) {
|
|
XtWarning(SB_MIN_MAX);
|
|
Minimum(spin) = Maximum(spin);
|
|
}
|
|
if ((ChildType(spin) == DtNUMERIC) &&
|
|
((Position(spin) < Minimum(spin)) ||
|
|
(Position(spin) > Maximum(spin)) ||
|
|
((Position(spin) % NumericIncrement(spin)) != 0))) {
|
|
XtWarning(SB_POSITION_NUMERIC);
|
|
Position(spin) = Minimum(spin);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Destroy procedure called by the toolkit.
|
|
*/
|
|
static void
|
|
Destroy(DtSpinBoxWidget spin)
|
|
{
|
|
int i;
|
|
|
|
if ((int)Timer(spin))
|
|
{
|
|
XtRemoveTimeOut(Timer(spin));
|
|
Timer(spin) = (XtIntervalId)NULL;
|
|
}
|
|
|
|
if (ItemCount(spin)>0) {
|
|
for (i = 0; i < ItemCount(spin); i++) {
|
|
XmStringFree((Items(spin))[i]);
|
|
}
|
|
XtFree((char*)(Items(spin)));
|
|
}
|
|
|
|
/*
|
|
* Don't remove callbacks and event handlers set on the children,
|
|
* this has already been done by Xt (children are cleaned up before
|
|
* the parent).
|
|
*/
|
|
}
|
|
|
|
|
|
/*
|
|
* Resize function called by toolkit. The size of our spin-box
|
|
* has already been changed. That is why we must store
|
|
* old_width and old_height.
|
|
*/
|
|
static void
|
|
Resize(DtSpinBoxWidget spin)
|
|
{
|
|
ClearShadow(spin, TRUE);
|
|
LayoutChildren(spin);
|
|
DrawShadow(spin);
|
|
OldWidth(spin) = spin->core.width;
|
|
OldHeight(spin) = spin->core.height;
|
|
}
|
|
|
|
|
|
/*
|
|
* Redisplay function called by toolkit. The widget didn't change size,
|
|
* so just redisplay the shadow.
|
|
*/
|
|
static void
|
|
Redisplay( DtSpinBoxWidget w,
|
|
XEvent *event,
|
|
Region region)
|
|
{
|
|
DrawShadow(w);
|
|
}
|
|
|
|
|
|
/*
|
|
* GeometryManager function called by toolkit when a child resizes/moves.
|
|
* We are not allowing any changes but width/height of the text-field.
|
|
* this is because the user can retrieve the text-field and make changes
|
|
* that we want to honor. If they mess around with the label or arrow,
|
|
* then we won't honor the request.
|
|
* If the text-field requests a change, then make the change, and allow
|
|
* our SetSpinBoxSize() and LayoutChildren() figure out what size will
|
|
* be allowed.
|
|
* Returning GeometryDone was suppose to tell the toolkit
|
|
* that we resized the child ourselves, but the text-field had trouble
|
|
* with this (its' geometry_manager wasn't called or working right?), so
|
|
* we return GeometryYes.
|
|
*/
|
|
static XtGeometryResult
|
|
GeometryManager(Widget w, /* child */
|
|
XtWidgetGeometry *request,
|
|
XtWidgetGeometry *reply)
|
|
{
|
|
/* MotifBc */
|
|
DtSpinBoxWidget spin = (DtSpinBoxWidget)XtParent(w);
|
|
/* Resolution Independent */
|
|
Arg args[4];
|
|
Dimension width, height, border_width;
|
|
int n;
|
|
unsigned char unit_type;
|
|
|
|
/* Ignore everything but text-field */
|
|
if (w != Text(spin) ) {
|
|
return(XtGeometryNo);
|
|
}
|
|
|
|
/* Only allow width/height changes */
|
|
if (!(request->request_mode & (CWWidth | CWHeight)))
|
|
return(XtGeometryNo);
|
|
|
|
/* Resolution Independent */
|
|
XtSetArg(args[0], XmNunitType, &unit_type);
|
|
XtGetValues(w, args, 1);
|
|
if ( unit_type != XmPIXELS) {
|
|
XtSetArg(args[0], XmNunitType, XmPIXELS);
|
|
XtSetValues(w, args, 1);
|
|
}
|
|
n = 0;
|
|
/* Set the text-field to the requested size */
|
|
if (request->request_mode & CWWidth) {
|
|
/* MotifBc */
|
|
XtSetArg(args[n], XmNwidth, &width); n++;
|
|
}
|
|
if (request->request_mode & CWHeight) {
|
|
/* MotifBc */
|
|
XtSetArg(args[n], XmNheight, &height); n++;
|
|
}
|
|
/* MotifBc */
|
|
XtSetArg(args[n], XmNborderWidth, &border_width); n++;
|
|
XtGetValues(w, args, n);
|
|
XtResizeWidget(w, width, height, border_width);
|
|
if ( unit_type != XmPIXELS) {
|
|
XtSetArg(args[0], XmNunitType, unit_type);
|
|
XtSetValues(w, args, 1);
|
|
}
|
|
|
|
ClearShadow(spin, TRUE);
|
|
if (RecomputeSize(spin))
|
|
SetSpinBoxSize(spin);
|
|
LayoutChildren(spin);
|
|
DrawShadow(spin);
|
|
return(XtGeometryDone);
|
|
}
|
|
|
|
/*
|
|
* This function sets the size of the spin_box widget based on the
|
|
* current size of the children. Don't worry if it doesn't work, the
|
|
* children will be squeezed in later.
|
|
*/
|
|
static void
|
|
SetSpinBoxSize(DtSpinBoxWidget spin)
|
|
{
|
|
Widget text_holder = Editable(spin) ? Text(spin) : Label(spin);
|
|
Dimension shadow = SPIN_SHADOW(spin) * 2;
|
|
Dimension arrow_width, arrow_height;
|
|
/* MotifBc & Resolution Independent */
|
|
Dimension height, width;
|
|
Arg args[2];
|
|
unsigned char unit_type = MUnitType(spin);
|
|
|
|
|
|
/* Resolution Independent */
|
|
if (unit_type != XmPIXELS) {
|
|
XtSetArg(args[0], XmNunitType, XmPIXELS);
|
|
XtSetValues((Widget)spin, args, 1);
|
|
XtSetValues(text_holder, args, 1);
|
|
}
|
|
/* MotifBc */
|
|
XtSetArg(args[0], XmNheight, &height);
|
|
XtSetArg(args[1], XmNwidth, &width);
|
|
XtGetValues(text_holder, args, 2);
|
|
/*
|
|
* Find out how big the arrow can be (needed to get
|
|
* width for text_holder).
|
|
*/
|
|
arrow_width = (Dimension)((float)height * ARROW_MULT);
|
|
arrow_width = (arrow_width < ARROW_MIN) ? ARROW_MIN : arrow_width;
|
|
|
|
/* Get height based on arrow width */
|
|
arrow_height = arrow_width;
|
|
if ((ArrowLayout(spin) == DtARROWS_BEGINNING) ||
|
|
(ArrowLayout(spin) == DtARROWS_END))
|
|
arrow_height += arrow_height;
|
|
|
|
/* Make height bigger of 2 - arrows vs text_holder */
|
|
if (arrow_height > (Dimension)height)
|
|
height = arrow_height;
|
|
|
|
/* If not stacked add extra width for arrows */
|
|
if ((ArrowLayout(spin) != DtARROWS_BEGINNING) &&
|
|
(ArrowLayout(spin) != DtARROWS_END)) {
|
|
arrow_width += arrow_width;
|
|
}
|
|
|
|
(void)XtMakeResizeRequest((Widget)spin, arrow_width +
|
|
width + shadow +
|
|
(SPIN_MARGIN_W(spin) * 2),
|
|
height + shadow + (SPIN_MARGIN_H(spin) * 2),
|
|
NULL, NULL);
|
|
OldWidth(spin) = Width(spin);
|
|
OldHeight(spin) = Height(spin);
|
|
|
|
/* Resolution Independent */
|
|
if (unit_type != XmPIXELS) {
|
|
XtSetArg(args[0], XmNunitType, unit_type);
|
|
XtSetValues((Widget)spin, args, 1);
|
|
XtSetValues(text_holder, args, 1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This function makes the text_holder (label or text-field) smaller
|
|
* if the spin_box couldn't grow to the needed full size. It will
|
|
* also make the text_holder grow if there is space. The textfield will
|
|
* grow with the spin_box, but the label will only grow to its'
|
|
* maximum size. The label will also shrink down to nothing, but the
|
|
* text-field will always keep its' core height.
|
|
*/
|
|
static void
|
|
ForceChildSizes(DtSpinBoxWidget spin)
|
|
{
|
|
Dimension available_height, available_width, arrow_width;
|
|
/* MotifBc & Resolution Independent */
|
|
Arg args[3];
|
|
unsigned char unit_type = XmPIXELS;
|
|
|
|
/* Resolution Independent */
|
|
if (MUnitType(spin) != XmPIXELS) {
|
|
unit_type = MUnitType(spin);
|
|
XtSetArg(args[0], XmNunitType, XmPIXELS);
|
|
XtSetValues(UpArrow(spin), args, 1);
|
|
XtSetValues(DownArrow(spin), args, 1);
|
|
if (Editable(spin) == False)
|
|
XtSetValues(Label(spin), args, 1);
|
|
else
|
|
XtSetValues(Text(spin), args, 1);
|
|
}
|
|
|
|
CalculateSizes(spin, &available_width, &available_height, &arrow_width);
|
|
|
|
if (Editable(spin) == FALSE) { /** label **/
|
|
if (available_width > LabelMaxLength(spin))
|
|
available_width = LabelMaxLength(spin);
|
|
|
|
if ((available_width != (Label(spin))->core.width) ||
|
|
(available_height != (Label(spin))->core.height))
|
|
XtResizeWidget(Label(spin), available_width, available_height,
|
|
(Label(spin))->core.border_width);
|
|
}
|
|
else if ((Text(spin))->core.width != available_width) /** TextField **/
|
|
XtResizeWidget(Text(spin), available_width,
|
|
(Text(spin))->core.height,
|
|
(Text(spin))->core.border_width);
|
|
if ((arrow_width != UpArrow(spin)->core.width) ||
|
|
((UpArrow(spin))->core.height != arrow_width)) {
|
|
available_height = (available_height < ARROW_MIN) ? ARROW_MIN :
|
|
available_height;
|
|
XtResizeWidget(UpArrow(spin), arrow_width, arrow_width,
|
|
(UpArrow(spin))->core.border_width);
|
|
}
|
|
if ((arrow_width != (DownArrow(spin))->core.width) ||
|
|
((DownArrow(spin))->core.height != arrow_width)) {
|
|
available_height = (available_height < ARROW_MIN) ? ARROW_MIN :
|
|
available_height;
|
|
XtResizeWidget(DownArrow(spin), arrow_width, arrow_width,
|
|
DownArrow(spin)->core.border_width);
|
|
}
|
|
/* Resolution Independent */
|
|
if (MUnitType(spin) != XmPIXELS) {
|
|
XtSetArg(args[0], XmNunitType, unit_type);
|
|
XtSetValues(UpArrow(spin), args, 1);
|
|
XtSetValues(DownArrow(spin), args, 1);
|
|
if (Editable(spin) == False)
|
|
XtSetValues(Label(spin), args, 1);
|
|
else
|
|
XtSetValues(Text(spin), args, 1);
|
|
}
|
|
}
|
|
|
|
static void
|
|
CalculateSizes (DtSpinBoxWidget spin,
|
|
Dimension *pwidth,
|
|
Dimension *pheight,
|
|
Dimension *parrow_width)
|
|
{
|
|
Dimension width, height, arrow_width;
|
|
|
|
/* Calculate available height for children */
|
|
if ((height = spin->core.height - (SPIN_SHADOW(spin) * 2) -
|
|
(SPIN_MARGIN_H(spin) * 2)) <= 0)
|
|
height = 1;
|
|
|
|
/* Get initial available width for children */
|
|
width = (spin->core.width - (SPIN_SHADOW(spin) * 2) -
|
|
(SPIN_MARGIN_W(spin) * 2));
|
|
|
|
/* label only grows to maximum width needed */
|
|
if ((Editable(spin) == FALSE) &&
|
|
(height > LabelMaxHeight(spin)))
|
|
height = LabelMaxHeight(spin);
|
|
else if (Editable(spin))
|
|
height = (Text(spin))->core.height;
|
|
|
|
/*
|
|
* Find out how big the arrow can be (needed to get
|
|
* width for text_holder).
|
|
*/
|
|
arrow_width = (Dimension)(height * ARROW_MULT);
|
|
arrow_width = (arrow_width < ARROW_MIN) ? ARROW_MIN : arrow_width;
|
|
|
|
/* Make sure width isn't too small or too big */
|
|
if ((width -= arrow_width) <= 0)
|
|
width = 1;
|
|
|
|
/* If not stacked subtract extra arrow */
|
|
if ((ArrowLayout(spin) != DtARROWS_BEGINNING) &&
|
|
(ArrowLayout(spin) != DtARROWS_END)) {
|
|
width -= arrow_width;
|
|
}
|
|
|
|
if (pwidth) *pwidth = width;
|
|
if (pheight) *pheight = height;
|
|
if (parrow_width) *parrow_width = arrow_width;
|
|
}
|
|
|
|
/*
|
|
* This function positions the children within the spin_box widget.
|
|
* It calls ForceChildSizes() to make sure the children fit within the
|
|
* spin_box widget, but it will not try to resize the spin_box widget.
|
|
*/
|
|
static void
|
|
LayoutChildren(DtSpinBoxWidget spin)
|
|
{
|
|
Widget text_holder = Editable(spin) ? Text(spin) : Label(spin);
|
|
Position start_x = SPIN_SHADOW(spin) + SPIN_MARGIN_W(spin);
|
|
Position start_y = SPIN_SHADOW(spin) + SPIN_MARGIN_H(spin);
|
|
Dimension available_height = spin->core.height - (start_y * 2);
|
|
Position y, arrow_y;
|
|
Dimension arrow_height;
|
|
/* Resolution Independent */
|
|
Arg args[4];
|
|
unsigned char unit_type = XmPIXELS;
|
|
|
|
ForceChildSizes(spin);
|
|
|
|
/* Resolution Independent */
|
|
if (MUnitType(spin) != XmPIXELS) {
|
|
unit_type = MUnitType(spin);
|
|
XtSetArg(args[0], XmNunitType, XmPIXELS);
|
|
XtSetValues(UpArrow(spin), args, 1);
|
|
XtSetValues(DownArrow(spin), args, 1);
|
|
XtSetValues(text_holder, args, 1);
|
|
}
|
|
|
|
/* Center text_holder within spin_box */
|
|
y = available_height - text_holder->core.height;
|
|
y = ((y < 0) ? 0 : y)/2 + start_y;
|
|
|
|
arrow_height = (UpArrow(spin))->core.height;
|
|
if ((ArrowLayout(spin) == DtARROWS_BEGINNING) ||
|
|
(ArrowLayout(spin) == DtARROWS_END))
|
|
arrow_height += arrow_height;
|
|
arrow_y = available_height - arrow_height;
|
|
arrow_y = ((arrow_y < 0) ? 0 : arrow_y)/2 + start_y;
|
|
|
|
switch (ArrowLayout(spin)) {
|
|
case DtARROWS_FLAT_BEGINNING:
|
|
XtMoveWidget(UpArrow(spin), start_x, arrow_y);
|
|
start_x += (UpArrow(spin))->core.width;
|
|
XtMoveWidget(DownArrow(spin), start_x, arrow_y);
|
|
start_x += (DownArrow(spin))->core.width;
|
|
XtMoveWidget(text_holder, start_x, y);
|
|
break;
|
|
case DtARROWS_FLAT_END:
|
|
XtMoveWidget(text_holder, start_x, y);
|
|
/*
|
|
* This start_x places arrow right after text_holder. With
|
|
* labels we want the arrow at the end of the spin_box, so
|
|
* the user can use recompute_size more effectively.
|
|
* start_x += text_holder->core.width;
|
|
*/
|
|
start_x = (spin->core.width - start_x -
|
|
(UpArrow(spin))->core.width -
|
|
(DownArrow(spin))->core.width);
|
|
XtMoveWidget(UpArrow(spin), start_x, arrow_y);
|
|
start_x += (UpArrow(spin))->core.width;
|
|
XtMoveWidget(DownArrow(spin), start_x, arrow_y);
|
|
break;
|
|
case DtARROWS_SPLIT:
|
|
XtMoveWidget(DownArrow(spin), start_x, arrow_y);
|
|
start_x += (DownArrow(spin))->core.width;
|
|
XtMoveWidget(text_holder, start_x, y);
|
|
start_x += text_holder->core.width;
|
|
XtMoveWidget(UpArrow(spin), start_x, arrow_y);
|
|
break;
|
|
case DtARROWS_BEGINNING:
|
|
XtMoveWidget(UpArrow(spin), start_x, arrow_y);
|
|
arrow_y += (UpArrow(spin))->core.height;
|
|
XtMoveWidget(DownArrow(spin), start_x, arrow_y);
|
|
start_x += (DownArrow(spin))->core.width;
|
|
XtMoveWidget(text_holder, start_x, y);
|
|
break;
|
|
case DtARROWS_END:
|
|
XtMoveWidget(text_holder, start_x, y);
|
|
/*
|
|
* This start_x places arrow right after text_holder. With
|
|
* labels we want the arrow at the end of the spin_box, so
|
|
* the user can use recompute_size more effectively.
|
|
* start_x += text_holder->core.width;
|
|
*/
|
|
start_x = spin->core.width - start_x - (UpArrow(spin))->core.width;
|
|
XtMoveWidget(UpArrow(spin), start_x, arrow_y);
|
|
arrow_y += (UpArrow(spin))->core.width;
|
|
XtMoveWidget(DownArrow(spin), start_x, arrow_y);
|
|
break;
|
|
}
|
|
/* Resolution Independent */
|
|
if (MUnitType(spin) != XmPIXELS) {
|
|
XtSetArg(args[0], XmNunitType, unit_type);
|
|
XtSetValues(UpArrow(spin), args, 1);
|
|
XtSetValues(DownArrow(spin), args, 1);
|
|
XtSetValues(text_holder, args, 1);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* SetValues() routine for SpinBox widget. Most of the real work
|
|
* is done in SetItems() and SetNumeric(). If items is set we store
|
|
* our own XmStrings.
|
|
* This function was built with static spin_boxs in mind, meaning that
|
|
* is-numeric or editable resources won't be changed constantly. These
|
|
* resources can be changed, but this function doesn't handle them in
|
|
* a super-efficient manor. for example, changing child-type will cause
|
|
* the label to be resize even if it isn't managed.
|
|
*/
|
|
static Boolean
|
|
SetValues( DtSpinBoxWidget current,
|
|
DtSpinBoxWidget request,
|
|
DtSpinBoxWidget new)
|
|
{
|
|
DtSpinBoxPart *new_p = (DtSpinBoxPart*)
|
|
&(XmField(new,ipot,DtSpinBox,label,Widget));
|
|
DtSpinBoxPart *cur_p = (DtSpinBoxPart*)
|
|
&(XmField(current,ipot,DtSpinBox,label,Widget));
|
|
Boolean store_info;
|
|
char *widget_name;
|
|
Boolean label_size_changed = FALSE;
|
|
Arg args[20];
|
|
int n;
|
|
/* Resolution Independent */
|
|
unsigned char unit_type = MUnitType(new);
|
|
|
|
CheckResources(new);
|
|
|
|
if (Text(new) != Text(current)) {
|
|
XtWarning(SB_TEXT);
|
|
Text(new) = Text(current);
|
|
}
|
|
|
|
/*
|
|
* Editable resource changed. If the widget (textField or Label)
|
|
* doesn't exist, then create it.
|
|
*/
|
|
if (Editable(new) != Editable(current)) {
|
|
if (Editable(new)) {
|
|
XtUnmanageChild(Label(new));
|
|
if (Text(new) == NULL) {
|
|
widget_name = XtMalloc(strlen(XtName((Widget)new)) + 10);
|
|
sprintf(widget_name, "%s_TF", XtName((Widget)new));
|
|
n = 0;
|
|
XtSetArg(args[n], XmNcolumns, TextColumns(new)); n++;
|
|
XtSetArg(args[n], XmNmaxLength, TextMaxLength(new)); n++;
|
|
XtSetArg(args[n], XmNmarginWidth, 2); n++;
|
|
XtSetArg(args[n], XmNmarginHeight, 2); n++;
|
|
if (unit_type != XmPIXELS) {
|
|
XtSetArg(args[n], XmNunitType, XmPIXELS); n++;
|
|
}
|
|
Text(new) = XtCreateManagedWidget(widget_name,
|
|
xmTextFieldWidgetClass,
|
|
(Widget)new, args, n);
|
|
XtAddCallback(Text(new), XmNlosingFocusCallback,
|
|
text_losing_focus_cb, (XtPointer)new);
|
|
XtAddCallback(Text(new), XmNactivateCallback,
|
|
text_activate_cb, (XtPointer)new);
|
|
XtAddCallback(Text(new), XmNfocusCallback,
|
|
text_focus_cb, (XtPointer)new);
|
|
XtFree(widget_name);
|
|
if (unit_type != XmPIXELS) {
|
|
XtSetArg(args[n], XmNunitType, unit_type); n++;
|
|
XtSetValues(Text(new), args, 1);
|
|
}
|
|
}
|
|
else
|
|
XtManageChild(Text(new));
|
|
}
|
|
else {
|
|
XtUnmanageChild(Text(new));
|
|
if (Label(new) == NULL) {
|
|
widget_name = XtMalloc(strlen(XtName((Widget)new)) + 10);
|
|
sprintf(widget_name, "%s_Label", XtName((Widget)new));
|
|
n = 0;
|
|
XtSetArg(args[n], XmNalignment, Alignment(new)); n++;
|
|
XtSetArg(args[n], XmNrecomputeSize, FALSE); n++;
|
|
XtSetArg(args[n], XmNlabelString, InitLabel); n++;
|
|
XtSetArg(args[n], XmNmarginLeft, LABEL_PADDING); n++;
|
|
XtSetArg(args[n], XmNmarginRight, LABEL_PADDING); n++;
|
|
XtSetArg(args[n], XmNmarginWidth, TEXT_CONTEXT_MARGIN); n++;
|
|
XtSetArg(args[n], XmNmarginHeight, 0); n++;
|
|
if (unit_type != XmPIXELS) {
|
|
XtSetArg(args[n], XmNunitType, XmPIXELS); n++;
|
|
}
|
|
Label(new) = XtCreateManagedWidget(widget_name,
|
|
xmLabelWidgetClass,
|
|
(Widget)new, args, n);
|
|
XtOverrideTranslations((Widget)Label(new), child_trans);
|
|
XtFree(widget_name);
|
|
if (unit_type != XmPIXELS) {
|
|
XtSetArg(args[n], XmNunitType, unit_type); n++;
|
|
XtSetValues(Label(new), args, 1);
|
|
}
|
|
}
|
|
else
|
|
XtManageChild(Label(new));
|
|
}
|
|
/*
|
|
* Text-fields and labels have different shadows. Only
|
|
* change if user didn't change the shadow resource.
|
|
*/
|
|
if (SPIN_SHADOW(new) == SPIN_SHADOW(current))
|
|
SPIN_SHADOW(new) = (Editable(new)) ? TEXT_FIELD_SHADOW :
|
|
LABEL_SHADOW;
|
|
}
|
|
|
|
/* Check arrow sensitivity (up arrow is right arrow)*/
|
|
if (ArrowSensitivity(new) != ArrowSensitivity(current)) {
|
|
XtSetSensitive(UpArrow(new),
|
|
((ArrowSensitivity(new) == DtARROWS_SENSITIVE) ||
|
|
(ArrowSensitivity(new) == DtARROWS_INCREMENT_SENSITIVE)));
|
|
XtSetSensitive(DownArrow(new),
|
|
((ArrowSensitivity(new) == DtARROWS_SENSITIVE) ||
|
|
(ArrowSensitivity(new) == DtARROWS_DECREMENT_SENSITIVE)));
|
|
}
|
|
|
|
/*
|
|
* Check arrow layout. Only need to change arrows if going to or
|
|
* from DtARROWS_SPLIT. The LayoutChildren() routine actually
|
|
* positions the arrows in the correct place.
|
|
*/
|
|
if (ArrowLayout(new) != ArrowLayout(current)) {
|
|
if (ArrowLayout(new) == DtARROWS_SPLIT) {
|
|
XtSetArg(args[0], XmNarrowDirection, XmARROW_RIGHT);
|
|
XtSetValues(UpArrow(new), args, 1);
|
|
XtSetArg(args[0], XmNarrowDirection, XmARROW_LEFT);
|
|
XtSetValues(DownArrow(new), args, 1);
|
|
}
|
|
else {
|
|
XtSetArg(args[0], XmNarrowDirection, XmARROW_UP);
|
|
XtSetValues(UpArrow(new), args, 1);
|
|
XtSetArg(args[0], XmNarrowDirection, XmARROW_DOWN);
|
|
XtSetValues(DownArrow(new), args, 1);
|
|
}
|
|
}
|
|
|
|
if (Text(new) && (Text(new) == Text(current))) {
|
|
n = 0;
|
|
if (TextColumns(new) != TextColumns(current)) {
|
|
XtSetArg(args[n], XmNcolumns, TextColumns(new)); n++;
|
|
}
|
|
if (TextMaxLength(new) != TextMaxLength(current)) {
|
|
XtSetArg(args[n], XmNmaxLength, TextMaxLength(new)); n++;
|
|
}
|
|
if (n > 0)
|
|
XtSetValues(Text(new), args, n);
|
|
}
|
|
|
|
/*
|
|
* LabelWidget alignment has changed.
|
|
*/
|
|
if (Label(new) && (Alignment(new) != Alignment(current))) {
|
|
XtSetArg(args[0], XmNalignment, Alignment(new));
|
|
XtSetValues(Label(new), args, 1);
|
|
}
|
|
|
|
store_info = ((Items(new) != Items(current)) ||
|
|
(ItemCount(new) != ItemCount(current)) ||
|
|
(DecimalPoints(new) != DecimalPoints(current)) ||
|
|
(Maximum(new) != Maximum(current)) ||
|
|
(Minimum(new) != Minimum(current)) ||
|
|
(NumericIncrement(new) != NumericIncrement(current)) ||
|
|
((ChildType(new) == DtNUMERIC) &&
|
|
(Position(new) != Position(current))));
|
|
if (store_info)
|
|
StoreResourceInfo(new_p, cur_p, (Items(new) != Items(current)));
|
|
|
|
if (Label(new) && (store_info ||
|
|
(Label(new) != Label(current)) ||
|
|
(ChildType(new) != ChildType(current)))) {
|
|
SetMaximumLabelSize(new_p);
|
|
label_size_changed = TRUE;
|
|
}
|
|
|
|
if (store_info ||
|
|
(Alignment(new) != Alignment(current)) ||
|
|
(Editable(new) != Editable(current)) ||
|
|
(Position(new) != Position(current)) ||
|
|
(Label(new) != Label(current)) ||
|
|
(ChildType(new) != ChildType(current))) {
|
|
if (Editable(new))
|
|
SetTextFieldData(new);
|
|
else
|
|
SetLabelData(new);
|
|
}
|
|
|
|
/*
|
|
* Must recalculate the spin_box and re-layout the children.
|
|
* If this is not editable, then set the label to its' maximum
|
|
* size; it will get chopped if it is too big. This is needed
|
|
* because we shrink the label down, and SetSpinBoxSize() uses
|
|
* the label's core sizes to figure what size to become.
|
|
*/
|
|
if ((Editable(new) != Editable(current)) ||
|
|
(SPIN_MARGIN_W(new) != SPIN_MARGIN_W(current)) ||
|
|
(SPIN_MARGIN_H(new) != SPIN_MARGIN_H(current)) ||
|
|
(SPIN_SHADOW(new) != SPIN_SHADOW(current)) ||
|
|
(ArrowLayout(new) != ArrowLayout(current)) ||
|
|
(!(Editable(new)) && label_size_changed)) {
|
|
ClearShadow(current, TRUE);
|
|
if (RecomputeSize(new))
|
|
SetSpinBoxSize(new);
|
|
LayoutChildren(new);
|
|
DrawShadow(new);
|
|
}
|
|
n=0;
|
|
if(CBgPixel(new) != CBgPixel(current))
|
|
{
|
|
XtSetArg(args[n],XmNbackground,CBgPixel(new));n++;
|
|
}
|
|
if(CBgPixmap(new) != CBgPixmap(current))
|
|
{
|
|
XtSetArg(args[n],XmNbackgroundPixmap,CBgPixmap(new));n++;
|
|
}
|
|
if(MFgPixel(new) != MFgPixel(current))
|
|
{
|
|
XtSetArg(args[n],XmNforeground,MFgPixel(new));n++;
|
|
}
|
|
if(Text(new))
|
|
XtSetValues (Text(new),args,n);
|
|
if(Label(new))
|
|
XtSetValues (Label(new),args,n);
|
|
if(UpArrow(new))
|
|
XtSetValues (UpArrow(new),args,n);
|
|
if(DownArrow(new))
|
|
XtSetValues (DownArrow(new),args,n);
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
/*
|
|
* This function clears the shadow around our widget. If all is TRUE,
|
|
* then clear all 4 sides; otherwise, only clear the right and bottom
|
|
* sides (during resize).
|
|
*/
|
|
static void
|
|
ClearShadow( DtSpinBoxWidget w,
|
|
Boolean all)
|
|
{
|
|
Dimension shadow = SPIN_SHADOW(w);
|
|
Dimension margin_w = SPIN_MARGIN_W(w);
|
|
Dimension margin_h = SPIN_MARGIN_H(w);
|
|
|
|
if ((shadow > 0) && XtIsRealized((Widget)w)) {
|
|
if (all) {
|
|
XClearArea(XtDisplayOfObject((Widget)w),
|
|
XtWindowOfObject((Widget)w),
|
|
margin_w, margin_h,
|
|
OldWidth(w) - (margin_w * 2),
|
|
shadow, FALSE);
|
|
XClearArea(XtDisplayOfObject((Widget)w),
|
|
XtWindowOfObject((Widget)w),
|
|
margin_w, margin_h, shadow,
|
|
OldHeight(w) - (margin_h * 2), FALSE);
|
|
}
|
|
XClearArea(XtDisplayOfObject((Widget)w), XtWindowOfObject((Widget)w),
|
|
margin_w, OldHeight(w) - margin_h - shadow,
|
|
OldWidth(w) - (margin_w * 2), shadow, FALSE);
|
|
XClearArea(XtDisplayOfObject((Widget)w), XtWindowOfObject((Widget)w),
|
|
OldWidth(w) - margin_w - shadow,
|
|
margin_h, shadow,
|
|
OldHeight(w) - (margin_h * 2), FALSE);
|
|
}
|
|
DrawHighlight(w, TRUE);
|
|
}
|
|
|
|
/*
|
|
* This functions draws the shadow around our spin_box.
|
|
*/
|
|
static void
|
|
DrawShadow(DtSpinBoxWidget w)
|
|
{
|
|
Dimension shadow = SPIN_SHADOW(w);
|
|
Dimension margin_w = SPIN_MARGIN_W(w);
|
|
Dimension margin_h = SPIN_MARGIN_H(w);
|
|
|
|
if ((shadow > 0) && XtIsRealized((Widget)w)) {
|
|
XmeDrawShadows(XtDisplayOfObject((Widget)w),
|
|
XtWindowOfObject((Widget)w),
|
|
w->manager.top_shadow_GC,
|
|
w->manager.bottom_shadow_GC,
|
|
margin_w, margin_h,
|
|
w->core.width - (margin_w * 2),
|
|
w->core.height - (margin_h * 2),
|
|
shadow, XmSHADOW_OUT);
|
|
}
|
|
DrawHighlight(w, FALSE);
|
|
}
|
|
|
|
/*
|
|
* This function sets up the items information for the SpinBox, as
|
|
* well as variables needed for child_type.
|
|
*/
|
|
static void
|
|
StoreResourceInfo( DtSpinBoxPart *spin_p,
|
|
DtSpinBoxPart *old_p,
|
|
Boolean do_items)
|
|
{
|
|
XmStringTable table;
|
|
int i, base = 1;
|
|
|
|
if (do_items && spin_p->items) {
|
|
/* Free up current items if needed */
|
|
if (old_p && old_p->items) {
|
|
for (i = 0; i < old_p->item_count; i++) {
|
|
XmStringFree(old_p->items[i]);
|
|
}
|
|
XtFree((char*)old_p->items);
|
|
}
|
|
|
|
/*
|
|
* Loop through all the items, copy them into our space.
|
|
*/
|
|
table = (XmStringTable)XtMalloc(sizeof(XmString) * spin_p->item_count);
|
|
for (i = 0; i < spin_p->item_count; i++) {
|
|
table[i] = XmStringCopy(spin_p->items[i]);
|
|
}
|
|
spin_p->items = table;
|
|
for (i = 0; i < spin_p->item_count; i++)
|
|
spin_p->items[i] = table[i];
|
|
}
|
|
|
|
/*
|
|
* Store the numeric information
|
|
*/
|
|
|
|
/* get base number for convert ints to floats */
|
|
for (i = 0; i < spin_p->decimal_points; i++)
|
|
base *= 10;
|
|
|
|
/* Set new initial values */
|
|
spin_p->min = (float)spin_p->minimum/base;
|
|
spin_p->max = (float)spin_p->maximum/base;
|
|
spin_p->increment = (float)spin_p->numeric_increment/base;
|
|
|
|
spin_p->current = (float)spin_p->position/base;
|
|
|
|
/* Create format string used to build correct XmString value */
|
|
spin_p->float_format[0] = '%';
|
|
sprintf((char*)(spin_p->float_format+1), ".%df", spin_p->decimal_points);
|
|
}
|
|
|
|
|
|
/* Caller must free string */
|
|
static char*
|
|
GetTextString(XmString xm_string)
|
|
{
|
|
XmStringContext context;
|
|
XmStringComponentType type;
|
|
XmStringCharSet charset;
|
|
XmStringDirection direction;
|
|
XmStringComponentType unknown_tag;
|
|
unsigned short ul;
|
|
unsigned char *uv;
|
|
char *text = NULL;
|
|
Boolean done = FALSE;
|
|
|
|
XmStringInitContext(&context, xm_string);
|
|
|
|
/* Loop until 1st char* found */
|
|
while (!done) {
|
|
type = XmStringGetNextComponent(context, &text, &charset,
|
|
&direction, &unknown_tag,
|
|
&ul, &uv);
|
|
switch (type) {
|
|
case XmSTRING_COMPONENT_END:
|
|
done = TRUE;
|
|
break;
|
|
case XmSTRING_COMPONENT_TEXT:
|
|
case XmSTRING_COMPONENT_LOCALE_TEXT:
|
|
done = TRUE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
XmStringFreeContext(context);
|
|
return(text);
|
|
}
|
|
|
|
/*
|
|
* Take the string out of the list and put it into the text-field.
|
|
* text-fields don't handle xm-strings, so we must get the char*
|
|
* out of it (only getting the first segment). This is slower than
|
|
* storing the text-strings (char*) ourselves, but that would take
|
|
* up a lot of memory. Since this setting happens during a user
|
|
* action, speed isn't a problem.
|
|
*/
|
|
static void
|
|
SetTextFieldData(DtSpinBoxWidget spin)
|
|
{
|
|
char string[NUMERIC_LENGTH];
|
|
XmString xm_string;
|
|
char *text;
|
|
Arg arg;
|
|
|
|
|
|
if (ChildType(spin) == DtNUMERIC) {
|
|
sprintf(string, FloatFormat(spin), (float)(Current(spin)));
|
|
XtSetArg(arg, XmNvalue, string);
|
|
XtSetValues(Text(spin), &arg, 1);
|
|
}
|
|
else {
|
|
if (ItemCount(spin) == 0){
|
|
XtSetArg(arg, XmNvalue, "");
|
|
XtSetValues(Text(spin), &arg, 1);
|
|
return;
|
|
}
|
|
else {
|
|
xm_string = (Items(spin))[Position(spin)];
|
|
if ((text = GetTextString(xm_string))) {
|
|
XtSetArg(arg, XmNvalue, text);
|
|
XtSetValues(Text(spin), &arg, 1);
|
|
XtFree(text);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set the maximum size of the label, depending on the
|
|
* characteristics of the list of items. Not very efficient
|
|
* if switching from numeric to non-numeric.
|
|
*/
|
|
static void
|
|
SetMaximumLabelSize(DtSpinBoxPart *spin_p)
|
|
{
|
|
XmString xm_string;
|
|
XmFontList font_list;
|
|
char string[NUMERIC_LENGTH];
|
|
Dimension width, height;
|
|
Dimension longest = 0;
|
|
Dimension highest = 0;
|
|
Arg args[5];
|
|
int i;
|
|
/* Resolution Independent */
|
|
unsigned char unit_type;
|
|
|
|
/* Get font info from the widget */
|
|
XtSetArg(args[0], XmNfontList, &font_list);
|
|
XtSetArg(args[1], XmNunitType, &unit_type); /* resolution Independent */
|
|
XtGetValues(spin_p->label, args, 2);
|
|
if ( unit_type != XmPIXELS) {
|
|
XtSetArg(args[0], XmNunitType, XmPIXELS);
|
|
XtSetValues(spin_p->label, args, 1);
|
|
}
|
|
|
|
if (spin_p->child_type == DtNUMERIC) {
|
|
/* Find out maximum size of the widget from min/max */
|
|
sprintf(string, spin_p->float_format, spin_p->min);
|
|
xm_string = XmStringCreateLocalized(string);
|
|
XmStringExtent(font_list, xm_string, &longest, &highest);
|
|
XmStringFree(xm_string);
|
|
sprintf(string, spin_p->float_format, spin_p->max);
|
|
xm_string = XmStringCreateLocalized(string);
|
|
XmStringExtent(font_list, xm_string, &width, &height);
|
|
XmStringFree(xm_string);
|
|
|
|
longest = (width > longest) ? width : longest;
|
|
highest = (height > highest) ? height : highest;
|
|
}
|
|
else if (spin_p->items) {
|
|
/*
|
|
* Loop through all the items to find the biggest dimensions
|
|
*/
|
|
for (i = 0; i < spin_p->item_count; i++) {
|
|
XmStringExtent(font_list, spin_p->items[i], &width, &height);
|
|
longest = (width > longest) ? width : longest;
|
|
highest = (height > highest) ? height : highest;
|
|
}
|
|
}
|
|
else {
|
|
XmStringExtent(font_list, InitLabel, &longest, &highest);
|
|
}
|
|
|
|
spin_p->label_max_length =
|
|
( (Dimension)(longest + ( (LABEL_PADDING + TEXT_CONTEXT_MARGIN) *2) ) >
|
|
(Dimension)Width(spin_p->label) ) ?
|
|
(longest + ((LABEL_PADDING+TEXT_CONTEXT_MARGIN) * 2)) :
|
|
Width(spin_p->label);
|
|
|
|
spin_p->label_max_height = highest > Height(spin_p->label) ?
|
|
highest : Height(spin_p->label);
|
|
XtResizeWidget(spin_p->label, spin_p->label_max_length,
|
|
spin_p->label_max_height,
|
|
spin_p->label->core.border_width);
|
|
|
|
/* Resolution Independent */
|
|
if ( unit_type != XmPIXELS) {
|
|
XtSetArg(args[0], XmNunitType, unit_type);
|
|
XtSetValues(spin_p->label, args, 1);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Put the current list item into the label.
|
|
*/
|
|
static void
|
|
SetLabelData(DtSpinBoxWidget spin)
|
|
{
|
|
XmString xm_string;
|
|
char string[NUMERIC_LENGTH];
|
|
int index = Position(spin);
|
|
Arg arg;
|
|
|
|
if (ChildType(spin) == DtNUMERIC) {
|
|
sprintf(string, FloatFormat(spin), (float)Current(spin));
|
|
xm_string = XmStringCreateLocalized(string);
|
|
XtSetArg(arg, XmNlabelString, xm_string);
|
|
XtSetValues(Label(spin), &arg, 1);
|
|
}
|
|
else {
|
|
/*
|
|
* If the item is not empty, get the current item from the list, else
|
|
* use InitLabel.
|
|
*/
|
|
xm_string = (ItemCount(spin)>0) ? (Items(spin))[index] : InitLabel;
|
|
XtSetArg(arg, XmNlabelString, xm_string);
|
|
XtSetValues(Label(spin), &arg, 1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Timout dispatch routine. This calls the appropriate callback function
|
|
* to simulate up or down arrow activation.
|
|
*/
|
|
static void
|
|
timer_dispatch( XtPointer client_data,
|
|
XtIntervalId *id)
|
|
{
|
|
DtSpinBoxWidget spin_w = (DtSpinBoxWidget)client_data;
|
|
|
|
Timer(spin_w) = (XtIntervalId)NULL;
|
|
InitCb(spin_w) = FALSE;
|
|
if (WhichArrow(spin_w) == XmARROW_UP) {
|
|
if (Grabbed(spin_w)) {
|
|
XtRemoveGrab(UpArrow(spin_w));
|
|
Grabbed(spin_w) = FALSE;
|
|
}
|
|
up_cb(NULL, client_data, NULL);
|
|
}
|
|
else {
|
|
if (Grabbed(spin_w)) {
|
|
XtRemoveGrab(DownArrow(spin_w));
|
|
Grabbed(spin_w) = FALSE;
|
|
}
|
|
down_cb(NULL, client_data, NULL);
|
|
}
|
|
}
|
|
|
|
static void
|
|
TextFieldActivate(DtSpinBoxPart *spin_p)
|
|
{
|
|
XmTextFieldWidget w = (XmTextFieldWidget)(spin_p->text);
|
|
XmAnyCallbackStruct cb;
|
|
char string[NUMERIC_LENGTH];
|
|
char *data = NULL;
|
|
char *text = NULL;
|
|
Arg arg;
|
|
Boolean free_me = TRUE;
|
|
|
|
XtSetArg(arg, XmNvalue, &data);
|
|
XtGetValues((Widget)w, &arg, 1);
|
|
|
|
if (spin_p->child_type == DtNUMERIC) {
|
|
sprintf(string, spin_p->float_format, (float)spin_p->current);
|
|
text = string;
|
|
free_me = FALSE;
|
|
}
|
|
else if (spin_p->items)
|
|
text = GetTextString(spin_p->items[spin_p->position]);
|
|
|
|
if (text && data && (strcmp(text, data) == 0)) {
|
|
if (free_me)
|
|
XtFree(text);
|
|
return;
|
|
}
|
|
/* Only send callback if both are not NULL */
|
|
else if (!((text == NULL) && (data == NULL))) {
|
|
cb.reason = XmCR_ACTIVATE;
|
|
cb.event = NULL;
|
|
/* MotifBc */
|
|
if (XtHasCallbacks((Widget)w, XmNactivateCallback)==XtCallbackHasSome)
|
|
XtCallCallbacks((Widget)w, XmNactivateCallback,
|
|
(XtPointer) &cb);
|
|
if (text && free_me)
|
|
XtFree(text);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This function calls the appropriate callback for the spin_box.
|
|
* It gathers the correct arguments and fills in the callback structure.
|
|
*/
|
|
static Boolean
|
|
SendCallback( DtSpinBoxWidget spin,
|
|
XEvent *event,
|
|
Boolean value_changed,
|
|
int position,
|
|
float current, /* Used for numeric */
|
|
Boolean crossed)
|
|
{
|
|
DtSpinBoxCallbackStruct cb;
|
|
XmString xm_string = NULL;
|
|
char string[NUMERIC_LENGTH];
|
|
|
|
if (ChildType(spin) == DtNUMERIC) {
|
|
sprintf(string, FloatFormat(spin), (float)current);
|
|
xm_string = XmStringCreateLocalized(string);
|
|
}
|
|
else {
|
|
xm_string = (ItemCount(spin)>0) ? (Items(spin))[position] : InitLabel;
|
|
xm_string = XmStringCopy(xm_string);
|
|
}
|
|
|
|
if (event)
|
|
cb.reason = DtCR_OK;
|
|
else if (WhichArrow(spin) == XmARROW_UP)
|
|
cb.reason = DtCR_SPIN_NEXT;
|
|
else
|
|
cb.reason = DtCR_SPIN_PRIOR;
|
|
cb.doit = TRUE;
|
|
cb.event = event;
|
|
cb.widget = (Widget)spin;
|
|
cb.position = position;
|
|
cb.value = xm_string;
|
|
cb.crossed_boundary = crossed;
|
|
if (value_changed) {
|
|
XtCallCallbackList((Widget)spin, ValueChangedCallback(spin),
|
|
(XtPointer)&cb);
|
|
cb.doit = TRUE;
|
|
}
|
|
else {
|
|
XtCallCallbackList((Widget)spin, ModifyVerifyCallback(spin),
|
|
(XtPointer)&cb);
|
|
}
|
|
XmStringFree(xm_string);
|
|
|
|
return(cb.doit);
|
|
}
|
|
|
|
|
|
/*
|
|
* This function gets called by the up/down arm callback functions.
|
|
* We set up the timer and send the modify-verify and value-changed
|
|
* callbacks.
|
|
* There are potential problems if the user does some weird stuff
|
|
* in the callbacks. I have added protection against the case where
|
|
* a user does a grab (with XtAddGrab/XtPopup/etc.) in the callbacks.
|
|
* Grabbing in the callback would cause us to lose the button-release
|
|
* (disarm), which would make the timer go on forever. A grab is
|
|
* done after the callbacks just to make sure we will receive the
|
|
* button-release. The button-release will call disarm_cb() which will
|
|
* un-grab and disable the timer. I have also added a leave callback
|
|
* which helps to protect against these kinds of problems.
|
|
* If the callback goes into another event-loop (which I hope would
|
|
* never happen), we would spin continuously (since our XtAddGrab never
|
|
* get called), until the user left the window (which would call
|
|
* grab_leave_cb). The grabbed flag gets set if we do the grab, so that
|
|
* we know if we can remove the grab. Our XtAddGrab() might not get called
|
|
* if the callback enters another event-loop.
|
|
*
|
|
* The event sent in the callback will be NULL during continuous spinning.
|
|
*/
|
|
static void
|
|
FinishUpDown( DtSpinBoxWidget spin,
|
|
XtPointer arrow_call_data,
|
|
int new_position,
|
|
float new_current,
|
|
Boolean crossed)
|
|
{
|
|
XmArrowButtonCallbackStruct *arrow_data;
|
|
XEvent *last_event = NULL;
|
|
int repeat_delay = RepeatDelay(spin);
|
|
|
|
if (InitCb(spin))
|
|
repeat_delay = InitialDelay(spin);
|
|
Timer(spin) = XtAppAddTimeOut(XtWidgetToApplicationContext((Widget)spin),
|
|
repeat_delay, timer_dispatch, (XtPointer)spin);
|
|
|
|
/* Try to get Xevent */
|
|
if ((arrow_data = (XmArrowButtonCallbackStruct*)arrow_call_data))
|
|
last_event = arrow_data->event;
|
|
|
|
/*
|
|
* Send modify_verify callback. If user says no, then
|
|
* clear the timer and reset the state before returning.
|
|
*/
|
|
if (SendCallback(spin, last_event, FALSE, new_position,
|
|
new_current, crossed) == FALSE) {
|
|
XtRemoveTimeOut(Timer(spin));
|
|
Timer(spin) = (XtIntervalId)NULL;
|
|
InitCb(spin) = TRUE;
|
|
return;
|
|
}
|
|
|
|
/* User said yes, so set widget values */
|
|
Position(spin) = new_position;
|
|
Current(spin) = new_current;
|
|
if (Editable(spin))
|
|
SetTextFieldData(spin);
|
|
else
|
|
SetLabelData(spin);
|
|
|
|
/* send value_changed callback */
|
|
(void)SendCallback(spin, last_event, TRUE, Position(spin),
|
|
Current(spin), crossed);
|
|
|
|
/* See notes at top of function on XtAddGrab usage */
|
|
Grabbed(spin) = TRUE;
|
|
if (WhichArrow(spin) == XmARROW_UP)
|
|
XtAddGrab(UpArrow(spin), FALSE, FALSE);
|
|
else
|
|
XtAddGrab(DownArrow(spin), FALSE, FALSE);
|
|
}
|
|
|
|
/*
|
|
* Show the next value in the SpinBox. If numeric, just add the
|
|
* increment value. If using string-table, get the next one in the
|
|
* table. This function takes care of wrap around. Set the arrow
|
|
* type. This is needed for the timer_dispatch function. up_cb
|
|
* gets called the first time the button is pressed, and each time the
|
|
* timer goes off.
|
|
* All widget internals are expected to be correct here; they
|
|
* get verified when set by the user.
|
|
*/
|
|
static void
|
|
up_cb( Widget w,
|
|
XtPointer client_data,
|
|
XtPointer call_data)
|
|
{
|
|
DtSpinBoxWidget spin = (DtSpinBoxWidget)client_data;
|
|
DtSpinBoxPart *spin_p = (DtSpinBoxPart*)
|
|
&(XmField(spin,ipot,DtSpinBox,label,Widget));
|
|
Boolean crossed_boundary = FALSE;
|
|
int new_position = Position(spin);
|
|
float new_current = Current(spin);
|
|
|
|
/* Getting Focus */
|
|
if ( !_XmFocusIsHere( (Widget)spin) )
|
|
XmProcessTraversal((Widget)spin,
|
|
(XmTraversalDirection) XmTRAVERSE_CURRENT);
|
|
|
|
if (Editable(spin))
|
|
TextFieldActivate(spin_p);
|
|
|
|
/*
|
|
* If non-numeric and no items then ignore the user activate event.
|
|
*/
|
|
if ((ChildType(spin) == DtSTRING) && (ItemCount(spin) == 0))
|
|
return;
|
|
|
|
if (ChildType(spin) == DtNUMERIC) {
|
|
if ((new_current + Increment(spin)) > Max(spin)) {
|
|
if (Wrap(spin)) {
|
|
new_position = Minimum(spin);
|
|
new_current = Min(spin);
|
|
crossed_boundary = TRUE;
|
|
}
|
|
else
|
|
XBell(XtDisplayOfObject((Widget)spin), 0);
|
|
}
|
|
else {
|
|
new_position += NumericIncrement(spin);
|
|
new_current += Increment(spin);
|
|
}
|
|
}
|
|
else if (ItemCount(spin) >0) {
|
|
if (new_position == (ItemCount(spin) - 1)) {
|
|
if (Wrap(spin)) {
|
|
new_position = 0;
|
|
crossed_boundary = TRUE;
|
|
}
|
|
else
|
|
XBell(XtDisplayOfObject((Widget)spin), 0);
|
|
}
|
|
else
|
|
new_position++;
|
|
}
|
|
|
|
WhichArrow(spin) = XmARROW_UP;
|
|
FinishUpDown(spin, call_data, new_position, new_current, crossed_boundary);
|
|
}
|
|
|
|
|
|
/*
|
|
* Show the previous value in the SpinBox. If numeric, just decrement
|
|
* the increment value. If using string-table, get the previous one in the
|
|
* table. This function takes care of wrap around. Set the arrow
|
|
* type. This is needed for the timer_dispatch function. down_cb
|
|
* gets called the first time the button is pressed, and each time the
|
|
* timer goes off.
|
|
* All widget internals are expected to be correct here; they
|
|
* get verified when set by the user.
|
|
*/
|
|
static void
|
|
down_cb(Widget w,
|
|
XtPointer client_data,
|
|
XtPointer call_data)
|
|
{
|
|
DtSpinBoxWidget spin = (DtSpinBoxWidget)client_data;
|
|
DtSpinBoxPart *spin_p = (DtSpinBoxPart*)
|
|
&(XmField(spin,ipot,DtSpinBox,label,Widget));
|
|
Boolean crossed_boundary = FALSE;
|
|
int new_position = Position(spin);
|
|
float new_current = Current(spin);
|
|
|
|
/* #5: Getting Focus */
|
|
if ( !_XmFocusIsHere( (Widget)spin) )
|
|
XmProcessTraversal((Widget)spin,
|
|
(XmTraversalDirection) XmTRAVERSE_CURRENT);
|
|
|
|
if (Editable(spin))
|
|
TextFieldActivate(spin_p);
|
|
|
|
/*
|
|
* If non-numeric and no items then ignore the user activate event.
|
|
*/
|
|
if ((ChildType(spin) == DtSTRING) && (ItemCount(spin) == 0))
|
|
return;
|
|
|
|
if (ChildType(spin) == DtNUMERIC) {
|
|
if ((new_current - Increment(spin)) < Min(spin)) {
|
|
if (Wrap(spin)) {
|
|
new_current = Max(spin);
|
|
new_position = Maximum(spin);
|
|
crossed_boundary = TRUE;
|
|
}
|
|
else
|
|
XBell(XtDisplayOfObject((Widget)spin), 0);
|
|
}
|
|
else {
|
|
new_current -= Increment(spin);
|
|
new_position -= NumericIncrement(spin);
|
|
}
|
|
}
|
|
else if (ItemCount(spin)>0) {
|
|
if (new_position == 0) {
|
|
if (Wrap(spin)) {
|
|
new_position = ItemCount(spin) - 1;
|
|
crossed_boundary = TRUE;
|
|
}
|
|
else
|
|
XBell(XtDisplayOfObject((Widget)spin), 0);
|
|
}
|
|
else
|
|
new_position--;
|
|
}
|
|
|
|
WhichArrow(spin) = XmARROW_DOWN;
|
|
FinishUpDown(spin, call_data, new_position, new_current, crossed_boundary);
|
|
}
|
|
|
|
static void
|
|
disarm_cb( Widget w,
|
|
XtPointer client_data,
|
|
XtPointer call_data)
|
|
{
|
|
DtSpinBoxWidget spin = (DtSpinBoxWidget)client_data;
|
|
|
|
if ((int)Timer(spin)) {
|
|
InitCb(spin) = TRUE;
|
|
XtRemoveTimeOut(Timer(spin));
|
|
Timer(spin) = (XtIntervalId)NULL;
|
|
if (Grabbed(spin)) {
|
|
XtRemoveGrab(w);
|
|
Grabbed(spin) = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
grab_leave_cb( Widget w,
|
|
XtPointer client_data,
|
|
XEvent *event,
|
|
Boolean *dispatch)
|
|
{
|
|
DtSpinBoxWidget spin = (DtSpinBoxWidget)client_data;
|
|
|
|
if ((int)Timer(spin) &&
|
|
((event->xcrossing.mode == NotifyGrab) ||
|
|
(event->xcrossing.mode == NotifyUngrab) ||
|
|
(!(event->xcrossing.state & Button1Mask)))) {
|
|
InitCb(spin) = TRUE;
|
|
XtRemoveTimeOut(Timer(spin));
|
|
Timer(spin) = (XtIntervalId)NULL;
|
|
if (Grabbed(spin)) {
|
|
XtRemoveGrab(w);
|
|
Grabbed(spin) = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* We get the text-field losing-focus callback, so pass it on to
|
|
* the user if they requested it. Our losing-focus callback
|
|
* is just a convenience callback, so that the user doesn't
|
|
* have to get the text-field first. This make our integration
|
|
* with XDesigner a little easier.
|
|
*/
|
|
static void
|
|
text_losing_focus_cb( Widget w,
|
|
XtPointer client_data,
|
|
XtPointer call_data)
|
|
{
|
|
DtSpinBoxWidget spin = (DtSpinBoxWidget)client_data;
|
|
|
|
if (LosingFocusCallback(spin))
|
|
XtCallCallbackList((Widget)spin,
|
|
LosingFocusCallback(spin),
|
|
(XtPointer)call_data);
|
|
}
|
|
|
|
/*
|
|
* We get the text-field activate callback, so pass it on to
|
|
* the user if they requested it. Our activate callback
|
|
* is just a convenience callback, so that the user doesn't
|
|
* have to get the text-field first. This make our integration
|
|
* with XDesigner a little easier.
|
|
*/
|
|
static void
|
|
text_activate_cb(Widget w,
|
|
XtPointer client_data,
|
|
XtPointer call_data)
|
|
{
|
|
DtSpinBoxWidget spin = (DtSpinBoxWidget)client_data;
|
|
|
|
if (ActivateCallback(spin))
|
|
XtCallCallbackList((Widget)spin,
|
|
ActivateCallback(spin),
|
|
(XtPointer)call_data);
|
|
}
|
|
|
|
/*
|
|
* We get the text-field focus callback, so pass it on to
|
|
* the user if they requested it. Our focus callback
|
|
* is just a convenience callback, so that the user doesn't
|
|
* have to get the text-field first. This make our integration
|
|
* with XDesigner a little easier.
|
|
*/
|
|
static void
|
|
text_focus_cb( Widget w,
|
|
XtPointer client_data,
|
|
XtPointer call_data)
|
|
{
|
|
DtSpinBoxWidget spin = (DtSpinBoxWidget)client_data;
|
|
|
|
if (FocusCallback(spin))
|
|
XtCallCallbackList((Widget)spin,
|
|
FocusCallback(spin),
|
|
(XtPointer)call_data);
|
|
}
|
|
|
|
|
|
/*
|
|
* Synthetic resource get functions.
|
|
*/
|
|
|
|
static XmImportOperator
|
|
_XmSetSyntheticResForChild( Widget widget,
|
|
int offset,
|
|
XtArgVal * value)
|
|
{
|
|
return(XmSYNTHETIC_LOAD);
|
|
}
|
|
|
|
void
|
|
_DtSpinBoxGetArrowSize( Widget w,
|
|
int resource_offset,
|
|
XtArgVal *value)
|
|
{
|
|
DtSpinBoxWidget spin = (DtSpinBoxWidget)w;
|
|
Dimension data;
|
|
Arg arg;
|
|
|
|
/* MotifBc */
|
|
XtSetArg(arg, XmNheight, &data);
|
|
XtGetValues(UpArrow(spin), &arg, 1);
|
|
*value = (XtArgVal)data;
|
|
}
|
|
|
|
/*
|
|
* Routines which manipulate the SpinBox list. These are external
|
|
* for use by users of our widget.
|
|
*/
|
|
|
|
Widget
|
|
DtCreateSpinBox( Widget parent,
|
|
char *name,
|
|
Arg *arglist,
|
|
Cardinal num_args)
|
|
{
|
|
return(XtCreateWidget(name, dtSpinBoxWidgetClass, parent,
|
|
arglist, num_args));
|
|
}
|
|
|
|
void
|
|
DtSpinBoxAddItem( Widget spinw,
|
|
XmString item,
|
|
int pos)
|
|
{
|
|
DtSpinBoxWidget spin = (DtSpinBoxWidget)spinw;
|
|
DtSpinBoxPart *spin_p;
|
|
XmString old, new_str, tmp;
|
|
int total_items;
|
|
_DtWidgetToAppContext(spinw);
|
|
_DtAppLock(app);
|
|
|
|
spin_p = (DtSpinBoxPart*) &(XmField(spin,ipot,DtSpinBox,label,Widget));
|
|
|
|
total_items = ItemCount(spin) + 1;
|
|
Items(spin) = (XmString *)XtRealloc((char*)Items(spin),
|
|
(sizeof(XmString) * total_items));
|
|
new_str = XmStringCopy(item);
|
|
|
|
pos--; /* User gives pos starting at 1 (0 means end of list) */
|
|
|
|
if ((pos < 0) || (pos > ItemCount(spin)))
|
|
pos = ItemCount(spin);
|
|
|
|
if (pos >= ItemCount(spin))
|
|
(Items(spin))[pos] = new_str;
|
|
else {
|
|
old = (Items(spin))[pos];
|
|
(Items(spin))[pos] = new_str;
|
|
for (pos++; pos < total_items; pos++) {
|
|
tmp = (Items(spin))[pos];
|
|
(Items(spin))[pos] = old;
|
|
old = tmp;
|
|
}
|
|
}
|
|
ItemCount(spin) = total_items;
|
|
|
|
if (Label(spin)) {
|
|
SetMaximumLabelSize(spin_p);
|
|
if (Editable(spin) == FALSE) {
|
|
ClearShadow(spin, TRUE);
|
|
if (RecomputeSize(spin))
|
|
SetSpinBoxSize(spin);
|
|
LayoutChildren(spin);
|
|
DrawShadow(spin);
|
|
}
|
|
}
|
|
|
|
/* Update the text-field or label */
|
|
if (Editable(spin))
|
|
SetTextFieldData(spin);
|
|
else
|
|
SetLabelData(spin);
|
|
|
|
_DtAppUnlock(app);
|
|
}
|
|
|
|
|
|
|
|
void
|
|
DtSpinBoxDeletePos( Widget spinw,
|
|
int pos)
|
|
{
|
|
DtSpinBoxWidget spin = (DtSpinBoxWidget)spinw;
|
|
DtSpinBoxPart *spin_p;
|
|
int total_items;
|
|
_DtWidgetToAppContext(spinw);
|
|
_DtAppLock(app);
|
|
|
|
if (ItemCount(spin) < 1)
|
|
{
|
|
_DtAppUnlock(app);
|
|
return;
|
|
}
|
|
|
|
spin_p = (DtSpinBoxPart*) &(XmField(spin,ipot,DtSpinBox,label,Widget));
|
|
|
|
pos--;
|
|
if ((pos < 0) || (pos > ItemCount(spin)))
|
|
pos = ItemCount(spin) - 1;
|
|
|
|
total_items = ItemCount(spin) - 1;
|
|
XmStringFree((Items(spin))[pos]);
|
|
|
|
/* To keep Position of SpinBox up to date */
|
|
if (Position(spin) > 0 &&
|
|
((Position(spin) >= total_items) || pos < Position(spin)) )
|
|
Position(spin) = Position(spin) - 1;
|
|
|
|
if (pos < ItemCount(spin)) {
|
|
for (; pos < total_items; pos++)
|
|
(Items(spin))[pos] = (Items(spin))[pos+1];
|
|
}
|
|
if (total_items > 0)
|
|
Items(spin) = (XmString *)XtRealloc((char*)Items(spin),
|
|
(sizeof(XmString) * total_items));
|
|
else {
|
|
XtFree((char *)Items(spin));
|
|
Items(spin) = (XmString *)NULL;
|
|
}
|
|
ItemCount(spin) = total_items;
|
|
|
|
if (Label(spin)) {
|
|
SetMaximumLabelSize(spin_p);
|
|
if (Editable(spin) == FALSE) {
|
|
ClearShadow(spin, TRUE);
|
|
if (RecomputeSize(spin))
|
|
SetSpinBoxSize(spin);
|
|
LayoutChildren(spin);
|
|
DrawShadow(spin);
|
|
}
|
|
}
|
|
|
|
/* Update the text-field or label */
|
|
if (Editable(spin))
|
|
SetTextFieldData(spin);
|
|
else
|
|
SetLabelData(spin);
|
|
|
|
_DtAppUnlock(app);
|
|
}
|
|
|
|
/*
|
|
* Make the given item the currently visible item in the
|
|
* text-field or label.
|
|
*/
|
|
void
|
|
DtSpinBoxSetItem( Widget spinw,
|
|
XmString item)
|
|
{
|
|
DtSpinBoxWidget spin = (DtSpinBoxWidget)spinw;
|
|
int i;
|
|
_DtWidgetToAppContext(spinw);
|
|
_DtAppLock(app);
|
|
|
|
if (item && ItemCount(spin)>0) {
|
|
for (i = 0; i < ItemCount(spin); i++)
|
|
if (XmStringCompare(item, (Items(spin))[i]))
|
|
break;
|
|
if (i < ItemCount(spin)) {
|
|
Position(spin) = i;
|
|
if (Editable(spin))
|
|
SetTextFieldData(spin);
|
|
else
|
|
SetLabelData(spin);
|
|
}
|
|
else
|
|
XtWarning(SB_SET_ITEM);
|
|
}
|
|
else
|
|
XtWarning(SB_SET_ITEM);
|
|
|
|
_DtAppUnlock(app);
|
|
}
|