/* * SpinButton.c, Interleaf, 16aug93 2:37pm Version 1.1. */ /*********************************************************** 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 * * SpinButton.c (XmSpinButtonWidget): * * I wanted a margin around the widget (outside the shadow, like buttons), * so that the spin-button 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-button 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_button. 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 SpinButton functions common, so they can be shared * with ComboBox. * * Known bugs: * Changing margin_width or margin_height resources when the * spin_button has focus will probably result in display glitches. */ /* * 11/99: JRA - Made a few bug fixes and added a couple of minor utilites. * adair@iglou.com */ #include #include "SpinButtonP.h" static void ClassInitialize AA(()); static void Initialize AA((XmSpinButtonWidget request, XmSpinButtonWidget new, ArgList given_args, Cardinal *num_args)); static XmNavigability WidgetNavigable AA((XmSpinButtonWidget spin)); static void _SpinButtonFocusIn AA((XmSpinButtonWidget spin, XEvent *event, char **params, Cardinal *num_params)); static void _SpinButtonFocusOut AA((XmSpinButtonWidget spin, XEvent *event, char **params, Cardinal *num_params)); static void DrawHighlight AA((XmSpinButtonWidget spin, Boolean clear)); static void _SpinButtonUp AA((XmSpinButtonWidget spin, XEvent *event, char **params, Cardinal *num_params)); static void _SpinButtonDown AA((XmSpinButtonWidget spin, XEvent *event, char **params, Cardinal *num_params)); static void _SpinButtonLeft AA((XmSpinButtonWidget spin, XEvent *event, char **params, Cardinal *num_params)); static void _SpinButtonRight AA((XmSpinButtonWidget spin, XEvent *event, char **params, Cardinal *num_params)); static void _SpinButtonBeginLine AA((XmSpinButtonWidget spin, XEvent *event, char **params, Cardinal *num_params)); static void _SpinButtonEndLine AA((XmSpinButtonWidget spin, XEvent *event, char **params, Cardinal *num_params)); static void CheckResources AA((XmSpinButtonWidget spin)); static void Destroy AA((XmSpinButtonWidget spin)); static void Resize AA((XmSpinButtonWidget spin)); static void Redisplay AA((XmSpinButtonWidget w, XEvent *event, Region region)); static XtGeometryResult GeometryManager AA((Widget w, XtWidgetGeometry *request, XtWidgetGeometry *reply)); static void SetSpinButtonSize AA((XmSpinButtonWidget spin)); static void ForceChildSizes AA((XmSpinButtonWidget spin)); static void LayoutChildren AA((XmSpinButtonWidget spin)); static Boolean SetValues AA((XmSpinButtonWidget current, XmSpinButtonWidget request, XmSpinButtonWidget new)); static void ClearShadow AA((XmSpinButtonWidget w, Boolean all)); static void DrawShadow AA((XmSpinButtonWidget w)); static void StoreResourceInfo AA((XmSpinButtonPart *spin_p, XmSpinButtonPart *old_p, Boolean do_items)); static char* GetTextString AA((XmString xm_string)); static void SetTextFieldData AA((XmSpinButtonWidget spin)); static void SetMaximumLabelSize AA((XmSpinButtonPart *spin_p)); static void SetLabelData AA((XmSpinButtonWidget spin)); static void timer_dispatch AA((XtPointer client_data, XtIntervalId *id)); static void TextFieldActivate AA((XmSpinButtonPart *spin_p)); static Boolean SendCallback AA((XmSpinButtonWidget spin, XEvent *event, Boolean value_changed, int position, float current, Boolean crossed)); static void FinishUpDown AA((XmSpinButtonWidget spin, XtPointer arrow_call_data, int new_position, float new_current, Boolean crossed)); static void up_cb AA((Widget w, XtPointer client_data, XtPointer call_data)); static void down_cb AA((Widget w, XtPointer client_data, XtPointer call_data)); static void disarm_cb AA((Widget w, XtPointer client_data, XtPointer call_data)); static void grab_leave_cb AA((Widget w, XtPointer client_data, Event *event, Boolean *dispatch)); static void text_losing_focus_cb AA((Widget w, XtPointer client_data, XtPointer call_data)); static void text_activate_cb AA((Widget w, XtPointer client_data, XtPointer call_data)); static void text_focus_cb AA((Widget w, XtPointer client_data, XtPointer call_data)); static XmImportOperator _XmSetSyntheticResForChild AA((Widget widget, int offset, XtArgVal * value)); static XmString InitLabel = NULL; static XtTranslations child_trans; XtTranslations spin_trans; #define SPIN_SHADOW(w) w->manager.shadow_thickness #define SPIN_MARGIN_W(w) w->spin_button.margin_width #define SPIN_MARGIN_H(w) w->spin_button.margin_height /* * 11/99: JRA - Added horizontal and vertical spacing stuff */ #define SPIN_H_SPACING(w) w->spin_button.horizontal_spacing #define SPIN_V_SPACING(w) w->spin_button.vertical_spacing #define INVALID_DIMENSION (0xFFFF) #define MAXINT 2147483647 /* Taken from TextF.c */ /* Only need one timer for all widget instances */ static XtIntervalId timer; static char SpinButtonTranslationTable[] = "\ : SpinButtonFocusIn() \n\ : SpinButtonFocusOut() \n\ osfUp: SpinButtonUp() \n\ osfDown: SpinButtonDown() \n\ osfRight: SpinButtonRight() \n\ osfLeft: SpinButtonLeft() \n\ osfBeginLine: SpinButtonBeginLine() \n\ osfEndLine: SpinButtonEndLine() \n\ "; static char SpinButtonChildTranslationTable[] = "\ osfUp: SpinButtonUp(child) \n\ osfDown: SpinButtonDown(child) \n\ osfRight: SpinButtonRight(child) \n\ osfLeft: SpinButtonLeft(child) \n\ osfBeginLine: SpinButtonBeginLine(child) \n\ osfEndLine: SpinButtonEndLine(child) \n\ "; static XtActionsRec SpinButtonActionTable[] = { {"SpinButtonFocusIn", (XtActionProc)_SpinButtonFocusIn}, {"SpinButtonFocusOut", (XtActionProc)_SpinButtonFocusOut}, {"SpinButtonUp", (XtActionProc)_SpinButtonUp}, {"SpinButtonDown", (XtActionProc)_SpinButtonDown}, {"SpinButtonRight", (XtActionProc)_SpinButtonRight}, {"SpinButtonLeft", (XtActionProc)_SpinButtonLeft}, {"SpinButtonBeginLine", (XtActionProc)_SpinButtonBeginLine}, {"SpinButtonEndLine", (XtActionProc)_SpinButtonEndLine}, }; /* XmSpinButtonWidget resources */ #define offset(field) XtOffset(XmSpinButtonWidget, field) static XtResource resources[] = { {XmNshadowThickness, XmCShadowThickness, XmRHorizontalDimension, sizeof(Dimension), offset(manager.shadow_thickness), XmRImmediate, (XtPointer)TEXT_FIELD_SHADOW}, /* Common resources */ {XmNactivateCallback, XmCCallback, XmRCallback, sizeof(XtCallbackList), offset(spin_button.activate_callback), XmRCallback, (XtPointer)NULL}, {XmNalignment, XmCAlignment, XmRAlignment, sizeof(unsigned char), offset(spin_button.alignment), XmRImmediate, (XtPointer)XmALIGNMENT_BEGINNING}, {XmNarrowLayout, XmCArrowLayout, XmRArrowLayout, sizeof(unsigned char), offset(spin_button.arrow_layout), XmRImmediate, (XtPointer)XmARROWS_BEGINNING}, {XmNarrowSensitivity, XmCArrowSensitivity, XmRArrowSensitivity, sizeof(unsigned char), offset(spin_button.arrow_sensitivity), XmRImmediate, (XtPointer)XmARROWS_SENSITIVE}, {XmNchildType, XmCChildType, XmRChildType, sizeof(unsigned char), offset(spin_button.child_type), XmRImmediate, (XtPointer)XmSTRING}, {XmNcolumns, XmCColumns, XmRShort, sizeof(short), offset(spin_button.text_columns), XmRImmediate, (XtPointer)20}, {XmNdecimalPoints, XmCDecimalPoints, XmRInt, sizeof(unsigned int), offset(spin_button.decimal_points), XmRImmediate, (XtPointer)0}, {XmNeditable, XmCEditable, XmRBoolean, sizeof(Boolean), offset(spin_button.editable), XmRImmediate, (XtPointer)TRUE}, {XmNfocusCallback, XmCCallback, XmRCallback, sizeof(XtCallbackList), offset(spin_button.focus_callback), XmRCallback, (XtPointer)NULL}, /* * 11/99: JRA - Added horizontal and vertical spacing stuff */ {XmNhorizontalSpacing, XmCHorizontalSpacing, XmRHorizontalDimension, sizeof(Dimension), offset(spin_button.horizontal_spacing), XmRImmediate, (XtPointer)INVALID_DIMENSION}, {XmNincrement, XmCIncrement, XmRInt, sizeof(int), offset(spin_button.numeric_increment), XmRImmediate, (XtPointer)1}, {XmNinitialDelay, XmCInitialDelay, XmRInt, sizeof(unsigned int), offset(spin_button.initial_delay), XmRImmediate, (XtPointer)250}, {XmNitemCount, XmCItemCount, XmRInt, sizeof(unsigned int), offset(spin_button.item_count), XmRImmediate, (XtPointer)0}, {XmNitems, XmCItems, XmRXmStringTable, sizeof(XmStringTable), offset(spin_button.items), XmRImmediate, (XtPointer)NULL}, /* * 11/99: JRA - Added label font list */ {XmNspinButtonLabelFontList, XmCSpinButtonLabelFontList, XmRFontList, sizeof(XmFontList), offset(spin_button.label_font_list), XmRImmediate, (XtPointer)NULL}, {XmNlosingFocusCallback, XmCCallback, XmRCallback, sizeof(XtCallbackList), offset(spin_button.losing_focus_callback), XmRCallback, (XtPointer)NULL}, {XmNmarginHeight, XmCMarginHeight, XmRVerticalDimension, sizeof(Dimension), offset(spin_button.margin_height), XmRImmediate, (XtPointer)MARGIN}, {XmNmarginWidth, XmCMarginWidth, XmRHorizontalDimension, sizeof(Dimension), offset(spin_button.margin_width), XmRImmediate, (XtPointer)MARGIN}, {XmNmaximum, XmCMaximum, XmRInt, sizeof(int), offset(spin_button.maximum), XmRImmediate, (XtPointer)1}, {XmNmaxLength, XmCMaxLength, XmRInt, sizeof(int), offset(spin_button.text_max_length), XmRImmediate, (XtPointer)MAXINT}, {XmNminimum, XmCMinimum, XmRInt, sizeof(int), offset(spin_button.minimum), XmRImmediate, (XtPointer)0}, {XmNmodifyVerifyCallback, XmCCallback, XmRCallback, sizeof(XtCallbackList), offset(spin_button.modify_verify_callback), XmRCallback, (XtPointer)NULL}, {XmNposition, XmCPosition, XmRInt, sizeof(unsigned int), offset(spin_button.position), XmRImmediate, (XtPointer)0}, {XmNrecomputeSize, XmCRecomputeSize, XmRBoolean, sizeof(Boolean), offset(spin_button.recompute_size), XmRImmediate, (XtPointer)TRUE}, {XmNrepeatDelay, XmCRepeatDelay, XmRInt, sizeof(unsigned int), offset(spin_button.repeat_delay), XmRImmediate, (XtPointer)200}, {XmNtextField, XmCTextField, XmRWidget, sizeof(Widget), offset(spin_button.text), XmRImmediate, (XtPointer)NULL}, /* * 11/99: JRA - Added text font list */ {XmNspinButtonTextFontList, XmCSpinButtonTextFontList, XmRFontList, sizeof(XmFontList), offset(spin_button.text_font_list), XmRImmediate, (XtPointer)NULL}, {XmNvalueChangedCallback, XmCCallback, XmRCallback, sizeof(XtCallbackList), offset(spin_button.value_changed_callback), XmRCallback, (XtPointer)NULL}, /* * 11/99: JRA - Added horizontal and vertical spacing stuff */ {XmNverticalSpacing, XmCVerticalSpacing, XmRVerticalDimension, sizeof(Dimension), offset(spin_button.vertical_spacing), XmRImmediate, (XtPointer)INVALID_DIMENSION}, {XmNwrap, XmCWrap, XmRBoolean, sizeof(Boolean), offset(spin_button.wrap), XmRImmediate, (XtPointer)TRUE}, }; /* Synthetic resources. Only used for Motif API arrowSize right now */ static XmSyntheticResource syn_resources[] = { {XmNarrowSize, sizeof(Dimension), offset(spin_button.arrow_size), _XmSpinButtonGetArrowSize, _XmSetSyntheticResForChild}, }; #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 */ WidgetNavigable, /* widgetNavigable */ (XmFocusChangeProc)NULL, /* focusChange */ (XmWrapperData)NULL /* wrapperData */ }; /* * Define Class Record. */ XmSpinButtonClassRec xmSpinButtonClassRec = { { /* core_class fields */ (WidgetClass)&(xmManagerClassRec), /* superclass */ (String)"XmSpinButton", /* class_name */ (Cardinal)sizeof(XmSpinButtonRec), /* 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)SpinButtonActionTable, /* actions */ (Cardinal)XtNumber(SpinButtonActionTable), /* 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)XtVersion, /* 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_button_class fields */ (Boolean)0, } }; WidgetClass xmSpinButtonWidgetClass = (WidgetClass)&xmSpinButtonClassRec; /* * Must set up the record type for the class extensions to work. */ static void ClassInitialize() { baseClassExtRec.record_type = XmQmotif; child_trans = XtParseTranslationTable(SpinButtonChildTranslationTable); spin_trans = XtParseTranslationTable(SpinButtonTranslationTable); } /* * SpinButton 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(request, new, given_args, num_args) XmSpinButtonWidget request; XmSpinButtonWidget new; ArgList given_args; Cardinal *num_args; { XmSpinButtonPart *spin_p = (XmSpinButtonPart*)&(new->spin_button); /*Widget parent;*/ char *widget_name; Arg args[20]/*, u_args[10], d_args[10]*/; int n/*, up_n, down_n*/; /* Overwrite the manager's focusIn and focusOut translations */ XtOverrideTranslations((Widget)new, spin_trans); if (InitLabel == NULL) InitLabel = XmStringCreateSimple("SpinButton"); widget_name = XtMalloc(strlen(XtName(new)) + 10); spin_p->text = (Widget)NULL; spin_p->label = (Widget)NULL; spin_p->old_width = 0; spin_p->old_height = 0; spin_p->init_cb = TRUE; spin_p->grabbed = FALSE; CheckResources(new); /* * Create the text or label depending on editable resource. */ if (spin_p->editable) { sprintf(widget_name, "%s_TF", XtName(new)); n = 0; XtSetArg(args[n], XmNcolumns, spin_p->text_columns); n++; XtSetArg(args[n], XmNmaxLength, spin_p->text_max_length); n++; XtSetArg(args[n], XmNmarginWidth, 2); n++; XtSetArg(args[n], XmNmarginHeight, 2); n++; /* * 11/99: JRA - Added text font list */ if( spin_p->text_font_list ) { XtSetArg( args[ n ], XmNfontList, spin_p->text_font_list ); n++; } /* end if user specified a font list for the text field */ spin_p->text = XtCreateManagedWidget(widget_name, xmTextFieldWidgetClass, (Widget)new, args, n); XtAddCallback(spin_p->text, XmNlosingFocusCallback, text_losing_focus_cb, (XtPointer)new); XtAddCallback(spin_p->text, XmNactivateCallback, text_activate_cb, (XtPointer)new); XtAddCallback(spin_p->text, XmNfocusCallback, text_focus_cb, (XtPointer)new); XtOverrideTranslations((Widget)spin_p->text, child_trans); /* * 11/99: JRA - Added horizontal and vertical spacing stuff */ if( spin_p->horizontal_spacing == INVALID_DIMENSION ) spin_p->horizontal_spacing = 0; if( spin_p->vertical_spacing == INVALID_DIMENSION ) spin_p->vertical_spacing = 0; } else { sprintf(widget_name, "%s_Label", XtName(new)); SPIN_SHADOW(new) = LABEL_SHADOW; n = 0; XtSetArg(args[n], XmNalignment, spin_p->alignment); 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, 0); n++; XtSetArg(args[n], XmNmarginHeight, 0); n++; /* * 11/99: JRA - Added label font list */ if( spin_p->label_font_list ) { XtSetArg( args[ n ], XmNfontList, spin_p->label_font_list ); n++; } /* end if user specified a font list for the label */ spin_p->label = XtCreateManagedWidget(widget_name, xmLabelWidgetClass, (Widget)new, args, n); XtOverrideTranslations((Widget)spin_p->label, child_trans); /* * 11/99: JRA - Added horizontal and vertical spacing stuff */ if( spin_p->horizontal_spacing == INVALID_DIMENSION ) spin_p->horizontal_spacing = 1; if( spin_p->vertical_spacing == INVALID_DIMENSION ) spin_p->vertical_spacing = 2; } /* * Create the 2 ArrowWidgets. */ sprintf(widget_name, "%s_Up", XtName(new)); n = 0; if (spin_p->arrow_layout == XmARROWS_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, new->core.background_pixel); n++; spin_p->up_arrow = XtCreateManagedWidget(widget_name, xmArrowButtonWidgetClass, (Widget)new, args, n); XtOverrideTranslations((Widget)spin_p->up_arrow, child_trans); sprintf(widget_name, "%s_Down", XtName(new)); if (spin_p->arrow_layout == XmARROWS_SPLIT) { XtSetArg(args[n], XmNarrowDirection, XmARROW_LEFT); n++; } else { XtSetArg(args[n], XmNarrowDirection, XmARROW_DOWN); n++; } spin_p->down_arrow = XtCreateManagedWidget(widget_name, xmArrowButtonWidgetClass, (Widget)new, args, n); XtOverrideTranslations((Widget)spin_p->down_arrow, child_trans); /* Set sensitivity of arrows (up arrow is right arrow) */ if ((spin_p->arrow_sensitivity == XmARROWS_INSENSITIVE) || (spin_p->arrow_sensitivity == XmARROWS_LEFT_SENSITIVE)) XtSetSensitive(spin_p->up_arrow, FALSE); if ((spin_p->arrow_sensitivity == XmARROWS_INSENSITIVE) || (spin_p->arrow_sensitivity == XmARROWS_RIGHT_SENSITIVE)) XtSetSensitive(spin_p->down_arrow, FALSE); /* * Arm causes the value to change and the timer to start. * Disarm (leaveNotify from grab) causes the timer to stop. */ XtAddCallback(spin_p->up_arrow, XmNarmCallback, up_cb, (XtPointer)new); XtAddCallback(spin_p->up_arrow, XmNdisarmCallback, disarm_cb, (XtPointer)new); XtAddEventHandler(spin_p->up_arrow, LeaveWindowMask, FALSE, grab_leave_cb, (XtPointer)new); XtAddCallback(spin_p->down_arrow, XmNarmCallback, down_cb, (XtPointer)new); XtAddCallback(spin_p->down_arrow, XmNdisarmCallback, disarm_cb, (XtPointer)new); XtAddEventHandler(spin_p->down_arrow, 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 (spin_p->editable == FALSE) { SetLabelData(new); SetMaximumLabelSize(spin_p); } else SetTextFieldData(new); SetSpinButtonSize(new); LayoutChildren(new); XtFree(widget_name); } /* * 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(spin) XmSpinButtonWidget 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 (spin->spin_button.editable) return(XmDESCENDANTS_TAB_NAVIGABLE); else return(XmTAB_NAVIGABLE); } return(XmDESCENDANTS_NAVIGABLE); } return(XmNOT_NAVIGABLE); } /* * The spin-button gets focus. */ static void _SpinButtonFocusIn(spin, event, params, num_params) XmSpinButtonWidget spin; XEvent *event; char **params; Cardinal *num_params; { DrawHighlight(spin, FALSE); } /* * The spin-button loses focus. */ static void _SpinButtonFocusOut(spin, event, params, num_params) XmSpinButtonWidget 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(spin, clear) XmSpinButtonWidget spin; Boolean clear; { XRectangle rect[4] ; if (XtIsRealized(spin)) { if (clear) { rect[0].x = rect[1].x = rect[2].x = 0; rect[3].x = spin->spin_button.old_width - SPIN_MARGIN_W(spin); rect[0].y = rect[2].y = rect[3].y = 0 ; rect[1].y = spin->spin_button.old_height - SPIN_MARGIN_H(spin); rect[0].width = rect[1].width = spin->spin_button.old_width; 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 = spin->spin_button.old_height; 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 _SpinButtonUp(spin, event, params, num_params) XmSpinButtonWidget spin; XEvent *event; char **params; Cardinal *num_params; { if (*num_params != 0) /* params means label or arrows */ spin = (XmSpinButtonWidget)XtParent(spin); if (spin->spin_button.arrow_layout != XmARROWS_SPLIT) { up_cb((Widget)spin->spin_button.up_arrow, (XtPointer)spin, NULL); disarm_cb((Widget)spin->spin_button.up_arrow, (XtPointer)spin, NULL); } } /* * osfDown virtual key hit. Simulate hitting the down arrow. */ static void _SpinButtonDown(spin, event, params, num_params) XmSpinButtonWidget spin; XEvent *event; char **params; Cardinal *num_params; { if (*num_params != 0) /* params means label or arrows */ spin = (XmSpinButtonWidget)XtParent(spin); if (spin->spin_button.arrow_layout != XmARROWS_SPLIT) { down_cb((Widget)spin->spin_button.down_arrow, (XtPointer)spin, NULL); disarm_cb((Widget)spin->spin_button.down_arrow, (XtPointer)spin, NULL); } } /* * osfRight virtual key hit. Simulate hitting the up arrow. */ static void _SpinButtonRight(spin, event, params, num_params) XmSpinButtonWidget spin; XEvent *event; char **params; Cardinal *num_params; { if (*num_params != 0) /* params means label or arrows */ spin = (XmSpinButtonWidget)XtParent(spin); if (spin->spin_button.arrow_layout == XmARROWS_SPLIT) { up_cb((Widget)spin->spin_button.up_arrow, (XtPointer)spin, NULL); disarm_cb((Widget)spin->spin_button.up_arrow, (XtPointer)spin, NULL); } } /* * osfLeft virtual key hit. Simulate hitting the down arrow. */ static void _SpinButtonLeft(spin, event, params, num_params) XmSpinButtonWidget spin; XEvent *event; char **params; Cardinal *num_params; { if (*num_params != 0) /* params means label or arrows */ spin = (XmSpinButtonWidget)XtParent(spin); if (spin->spin_button.arrow_layout == XmARROWS_SPLIT) { down_cb((Widget)spin->spin_button.down_arrow, (XtPointer)spin, NULL); disarm_cb((Widget)spin->spin_button.down_arrow, (XtPointer)spin, NULL); } } /* * osfBeginLine virtual key hit. Go to first item. */ static void _SpinButtonBeginLine(spin, event, params, num_params) XmSpinButtonWidget spin; XEvent *event; char **params; Cardinal *num_params; { XmSpinButtonPart *spin_p; int new_position; float new_current; if (*num_params != 0) /* params means label or arrows */ spin = (XmSpinButtonWidget)XtParent(spin); spin_p = (XmSpinButtonPart*)&(spin->spin_button); if (spin_p->child_type == XmNUMERIC) { new_position = spin_p->minimum; new_current = spin_p->min; } else { new_position = 0; } if (SendCallback(spin, event, FALSE, new_position, new_current, FALSE) == TRUE) { /* User said yes, so set widget values */ spin_p->position = new_position; spin_p->current = new_current; if (spin_p->editable) SetTextFieldData(spin); else SetLabelData(spin); /* send value_changed callback */ (void)SendCallback(spin, event, TRUE, spin_p->position, spin_p->current, FALSE); } } /* * osfEndLine virtual key hit. Go to last item. */ static void _SpinButtonEndLine(spin, event, params, num_params) XmSpinButtonWidget spin; XEvent *event; char **params; Cardinal *num_params; { XmSpinButtonPart *spin_p; int new_position; float new_current; if (*num_params != 0) /* params means label or arrows */ spin = (XmSpinButtonWidget)XtParent(spin); spin_p = (XmSpinButtonPart*)&(spin->spin_button); if (spin_p->child_type == XmNUMERIC) { new_position = spin_p->maximum; new_current = spin_p->max; } else { new_position = spin_p->item_count - 1; } if (SendCallback(spin, event, FALSE, new_position, new_current, FALSE) == TRUE) { /* User said yes, so set widget values */ spin_p->position = new_position; spin_p->current = new_current; if (spin_p->editable) SetTextFieldData(spin); else SetLabelData(spin); /* send value_changed callback */ (void)SendCallback(spin, event, TRUE, spin_p->position, spin_p->current, FALSE); } } /* * This function goes through most of the resources and makes sure * they have legal values. */ static void CheckResources(spin) XmSpinButtonWidget spin; { XmSpinButtonPart *spin_p = (XmSpinButtonPart*)&(spin->spin_button); if ((spin_p->arrow_sensitivity != XmARROWS_SENSITIVE) && (spin_p->arrow_sensitivity != XmARROWS_LEFT_SENSITIVE) && (spin_p->arrow_sensitivity != XmARROWS_RIGHT_SENSITIVE) && (spin_p->arrow_sensitivity != XmARROWS_INSENSITIVE)) { XtWarning(SPIN_ARROW_SENSITIVE); spin_p->arrow_sensitivity = XmARROWS_SENSITIVE; } if ((spin_p->alignment != XmALIGNMENT_CENTER) && (spin_p->alignment != XmALIGNMENT_BEGINNING) && (spin_p->alignment != XmALIGNMENT_END)) { XtWarning(SPIN_ALIGNMENT); spin_p->alignment = XmALIGNMENT_CENTER; } if (spin_p->initial_delay <= 0) { XtWarning(SPIN_INIT_DELAY); spin_p->initial_delay = 250; } /* if (spin_p->margin_height < 0) { XtWarning(SPIN_MARGIN_HEIGHT); spin_p->margin_height = 2; } if (spin_p->margin_width < 0) { XtWarning(SPIN_MARGIN_WIDTH); spin_p->margin_width = 2; } */ /* * 11/99: JRA - Added horizontal and vertical spacing stuff */ /* if (spin_p->horizontal_spacing < 0) { XtWarning(SPIN_HORIZONTAL_SPACING); spin_p->horizontal_spacing = 0; } if (spin_p->vertical_spacing < 0) { XtWarning(SPIN_VERTICAL_SPACING); spin_p->vertical_spacing = 0; } */ if ((spin_p->arrow_layout != XmARROWS_FLAT_BEGINNING) && (spin_p->arrow_layout != XmARROWS_FLAT_END) && (spin_p->arrow_layout != XmARROWS_SPLIT) && (spin_p->arrow_layout != XmARROWS_BEGINNING) && (spin_p->arrow_layout != XmARROWS_END)) { XtWarning(SPIN_ARROW_LAYOUT); spin_p->arrow_layout = XmARROWS_BEGINNING; } if (spin_p->repeat_delay <= 0) { XtWarning(SPIN_REPEAT_DELAY); spin_p->repeat_delay = 200; } if (spin_p->item_count < 0) { XtWarning(SPIN_ITEM_COUNT); spin_p->item_count = 0; } if ((spin_p->child_type == XmSTRING) && ((spin_p->position < 0) || ((spin_p->position >= spin_p->item_count) && (spin_p->item_count > 0)))) { XtWarning(SPIN_POSITION_STRING); spin_p->position = 0; } if ((spin_p->decimal_points < 0) || (spin_p->decimal_points > MAX_FLOAT_DECIMALS)) { XtWarning(SPIN_DECIMAL_POINTS); spin_p->decimal_points = 0; } if (spin_p->minimum > spin_p->maximum) { XtWarning(SPIN_MIN_MAX); spin_p->minimum = spin_p->maximum; } if ((spin_p->child_type == XmNUMERIC) && ((spin_p->position < spin_p->minimum) || (spin_p->position > spin_p->maximum) || ((spin_p->position % spin_p->numeric_increment) != 0))) { XtWarning(SPIN_POSITION_NUMERIC); spin_p->position = spin_p->minimum; } } /* * Destroy procedure called by the toolkit. Remove all callbacks and * event-handlers. */ static void Destroy(spin) XmSpinButtonWidget spin; { XmSpinButtonPart *spin_p = (XmSpinButtonPart*)&(spin->spin_button); int i; if ((int)timer) XtRemoveTimeOut(timer); if (spin_p->items) { for (i = 0; i < spin_p->item_count; i++) { XmStringFree(spin_p->items[i]); } XtFree((char*)spin_p->items); } if (spin_p->text) { XtRemoveCallback(spin_p->text, XmNlosingFocusCallback, text_losing_focus_cb, (XtPointer)spin); XtRemoveCallback(spin_p->text, XmNactivateCallback, text_activate_cb, (XtPointer)spin); XtRemoveCallback(spin_p->text, XmNfocusCallback, text_focus_cb, (XtPointer)spin); } XtRemoveCallback(spin_p->up_arrow, XmNarmCallback, up_cb, (XtPointer)spin); XtRemoveCallback(spin_p->up_arrow, XmNdisarmCallback, disarm_cb, (XtPointer)spin); XtRemoveEventHandler(spin_p->up_arrow, LeaveWindowMask, FALSE, grab_leave_cb, (XtPointer)spin); XtRemoveCallback(spin_p->down_arrow, XmNarmCallback, down_cb, (XtPointer)spin); XtRemoveCallback(spin_p->down_arrow, XmNdisarmCallback, disarm_cb, (XtPointer)spin); XtRemoveEventHandler(spin_p->down_arrow, LeaveWindowMask, FALSE, grab_leave_cb, (XtPointer)spin); } /* * 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(spin) XmSpinButtonWidget spin; { XmSpinButtonPart *spin_p = (XmSpinButtonPart*)&(spin->spin_button); ClearShadow(spin, TRUE); LayoutChildren(spin); DrawShadow(spin); spin_p->old_width = spin->core.width; spin_p->old_height = spin->core.height; } /* * Redisplay function called by toolkit. The widget didn't change size, * so just redisplay the shadow. */ static void Redisplay(w, event, region) XmSpinButtonWidget w; XEvent *event; Region region; { DrawShadow(w, SPIN_MARGIN_W(w), SPIN_MARGIN_H(w), SPIN_SHADOW(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 SetSpinButtonSize() 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(w, request, reply) Widget w; /* child */ XtWidgetGeometry *request; XtWidgetGeometry *reply; { XmSpinButtonWidget spin = (XmSpinButtonWidget)(w->core.parent); /* Ignore everything but text-field */ if (w != spin->spin_button.text) return(XtGeometryNo); /* Only allow width/height changes */ if (!(request->request_mode & (CWWidth | CWHeight))) return(XtGeometryNo); /* Set the text-field to the requested size */ if (request->request_mode & CWWidth) w->core.width = request->width; if (request->request_mode & CWHeight) w->core.height = request->height; XtResizeWidget(w, w->core.width, w->core.height, w->core.border_width); ClearShadow(spin, TRUE); if (spin->spin_button.recompute_size) SetSpinButtonSize(spin); LayoutChildren(spin); DrawShadow(spin); return(XtGeometryYes); } /* * This function sets the size of the spin_button 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 SetSpinButtonSize(spin) XmSpinButtonWidget spin; { XmSpinButtonPart *spin_p = (XmSpinButtonPart*)&(spin->spin_button); Widget text_holder = spin_p->editable ? spin_p->text : spin_p->label; Dimension shadow = SPIN_SHADOW(spin) * 2; /* * 11/99: JRA - Added horizontal and vertical spacing stuff */ Dimension h_spacing = SPIN_H_SPACING(spin) * 2; Dimension v_spacing = SPIN_V_SPACING(spin) * 2; short arrow_width, arrow_height; short height = text_holder->core.height; /* * Find out how big the arrow can be (needed to get * available_width for text_holder). */ arrow_width = (Dimension)(text_holder->core.height * ARROW_MULT); arrow_width = (arrow_width < ARROW_MIN) ? ARROW_MIN : arrow_width; /* Get height based on arrow width */ arrow_height = arrow_width; if ((spin_p->arrow_layout == XmARROWS_BEGINNING) || (spin_p->arrow_layout == XmARROWS_END)) arrow_height += arrow_height; /* Make height bigger of 2 - arrows vs text_holder */ if (arrow_height > (short)text_holder->core.height) height = arrow_height; /* If not stacked add extra width for arrows */ if ((spin_p->arrow_layout != XmARROWS_BEGINNING) && (spin_p->arrow_layout != XmARROWS_END)) { arrow_width += arrow_width; } /* * 11/99: JRA - Added horizontal and vertical spacing stuff */ (void)XtMakeResizeRequest((Widget)spin, arrow_width + text_holder->core.width + shadow + h_spacing + (SPIN_MARGIN_W(spin) * 2), height + shadow +v_spacing + (SPIN_MARGIN_H(spin) * 2), NULL, NULL); spin_p->old_width = spin->core.width; spin_p->old_height = spin->core.height; } /* * This function makes the text_holder (label or text-field) smaller * if the spin_button 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_button, 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(spin) XmSpinButtonWidget spin; { XmSpinButtonPart *spin_p = (XmSpinButtonPart*)&(spin->spin_button); short available_height, available_width; short arrow_width; /* * 11/99: JRA - Added horizontal and vertical spacing stuff */ /* Calculate available height for children */ if ((available_height = spin->core.height - (SPIN_SHADOW(spin) * 2) - (SPIN_MARGIN_H(spin) * 2) - (SPIN_V_SPACING(spin) * 2)) <= 0) available_height = 1; /* * 11/99: JRA - Added horizontal and vertical spacing stuff */ /* Get initial available width for children */ available_width = (spin->core.width - (SPIN_SHADOW(spin) * 2) - (SPIN_MARGIN_W(spin) * 2) - (SPIN_H_SPACING(spin) * 2)); /* label only grows to maximum width needed */ if ((spin_p->editable == FALSE) && (available_height > (short)spin_p->label_max_height)) available_height = spin_p->label_max_height; else if (spin_p->editable) available_height = spin_p->text->core.height; /* * Find out how big the arrow can be (needed to get * available_width for text_holder). */ arrow_width = (Dimension)(available_height * ARROW_MULT); arrow_width = (arrow_width < ARROW_MIN) ? ARROW_MIN : arrow_width; /* Make sure width isn't too small or too big */ if ((available_width -= arrow_width) <= 0) available_width = 1; /* If not stacked subtract extra arrow */ if ((spin_p->arrow_layout != XmARROWS_BEGINNING) && (spin_p->arrow_layout != XmARROWS_END)) { available_width -= arrow_width; } if (spin_p->editable == FALSE) { /** label **/ if (available_width > (short)spin_p->label_max_length) available_width = spin_p->label_max_length; if ((available_width != spin_p->label->core.width) || (available_height != spin_p->label->core.height)) XtResizeWidget(spin_p->label, available_width, available_height, spin_p->label->core.border_width); } else if (spin_p->text->core.width != available_width) /** TextField **/ XtResizeWidget(spin_p->text, available_width, spin_p->text->core.height, spin_p->text->core.border_width); if ((arrow_width != spin_p->up_arrow->core.width) || (spin_p->up_arrow->core.height != arrow_width)) { available_height = (available_height < ARROW_MIN) ? ARROW_MIN : available_height; XtResizeWidget(spin_p->up_arrow, arrow_width, arrow_width, spin_p->up_arrow->core.border_width); } if ((arrow_width != spin_p->down_arrow->core.width) || (spin_p->down_arrow->core.height != arrow_width)) { available_height = (available_height < ARROW_MIN) ? ARROW_MIN : available_height; XtResizeWidget(spin_p->down_arrow, arrow_width, arrow_width, spin_p->down_arrow->core.border_width); } } /* * This function positions the children within the spin_button widget. * It calls ForceChildSizes() to make sure the children fit within the * spin_button widget, but it will not try to resize the spin_button widget. */ static void LayoutChildren(spin) XmSpinButtonWidget spin; { /* * 11/99: JRA - Added horizontal and vertical spacing stuff */ XmSpinButtonPart *spin_p = (XmSpinButtonPart*)&(spin->spin_button); Widget text_holder = spin_p->editable ? spin_p->text : spin_p->label; Position start_x = SPIN_SHADOW(spin) + SPIN_MARGIN_W(spin) + SPIN_H_SPACING(spin); Position start_y = SPIN_SHADOW(spin) + SPIN_MARGIN_H(spin) + SPIN_V_SPACING(spin); short available_height = spin->core.height - (start_y * 2); Position y, arrow_y; Dimension arrow_height; ForceChildSizes(spin); /* Center text_holder within spin_button */ y = available_height - text_holder->core.height; y = ((y < 0) ? 0 : y)/2 + start_y; arrow_height = spin_p->up_arrow->core.height; if ((spin_p->arrow_layout == XmARROWS_BEGINNING) || (spin_p->arrow_layout == XmARROWS_END)) arrow_height += arrow_height; arrow_y = available_height - arrow_height; arrow_y = ((arrow_y < 0) ? 0 : arrow_y)/2 + start_y; switch (spin_p->arrow_layout) { case XmARROWS_FLAT_BEGINNING: XtMoveWidget(spin_p->up_arrow, start_x, arrow_y); start_x += spin_p->up_arrow->core.width; XtMoveWidget(spin_p->down_arrow, start_x, arrow_y); start_x += spin_p->down_arrow->core.width; XtMoveWidget(text_holder, start_x, y); break; case XmARROWS_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_button, so * the user can use recompute_size more effectively. * start_x += text_holder->core.width; */ start_x = (spin->core.width - start_x - spin_p->up_arrow->core.width - spin_p->down_arrow->core.width); XtMoveWidget(spin_p->up_arrow, start_x, arrow_y); start_x += spin_p->up_arrow->core.width; XtMoveWidget(spin_p->down_arrow, start_x, arrow_y); break; case XmARROWS_SPLIT: XtMoveWidget(spin_p->down_arrow, start_x, arrow_y); start_x += spin_p->down_arrow->core.width; XtMoveWidget(text_holder, start_x, y); start_x += text_holder->core.width; XtMoveWidget(spin_p->up_arrow, start_x, arrow_y); break; case XmARROWS_BEGINNING: XtMoveWidget(spin_p->up_arrow, start_x, arrow_y); arrow_y += spin_p->up_arrow->core.height; XtMoveWidget(spin_p->down_arrow, start_x, arrow_y); start_x += spin_p->down_arrow->core.width; XtMoveWidget(text_holder, start_x, y); break; case XmARROWS_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_button, so * the user can use recompute_size more effectively. * start_x += text_holder->core.width; */ start_x = spin->core.width - start_x - spin_p->up_arrow->core.width; XtMoveWidget(spin_p->up_arrow, start_x, arrow_y); arrow_y += spin_p->up_arrow->core.width; XtMoveWidget(spin_p->down_arrow, start_x, arrow_y); break; } } /* * SetValues() routine for SpinButton 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-buttons 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(current, request, new) XmSpinButtonWidget current; XmSpinButtonWidget request; XmSpinButtonWidget new; { XmSpinButtonPart *new_p = (XmSpinButtonPart*)&(new->spin_button); XmSpinButtonPart *cur_p = (XmSpinButtonPart*)&(current->spin_button); Boolean store_info; char *widget_name; Boolean label_size_changed = FALSE; Arg args[20]; int n; CheckResources(new); /* * 11/99: JRA - Added text font list */ if( new_p->text_font_list != cur_p->text_font_list ) { XtSetArg( args[ n ], XmNfontList, new_p->text_font_list ); n++; } /* end if new font list != old font list for text field */ /* * 11/99: JRA - Added label font list */ if( new_p->label_font_list != cur_p->label_font_list ) { XtSetArg( args[ n ], XmNfontList, new_p->label_font_list ); n++; } /* end if new font list != old font list for label */ if (new_p->text != cur_p->text) { XtWarning(SPIN_TEXT); new_p->text = cur_p->text; } /* * Editable resource changed. If the widget (textField or Label) * doesn't exist, then create it. */ if (new_p->editable != cur_p->editable) { if (new_p->editable) { XtUnmanageChild(new_p->label); if (new_p->text == NULL) { widget_name = XtMalloc(strlen(XtName(new)) + 10); sprintf(widget_name, "%s_TF", XtName(new)); n = 0; XtSetArg(args[n], XmNcolumns, new_p->text_columns); n++; XtSetArg(args[n], XmNmaxLength, new_p->text_max_length); n++; XtSetArg(args[n], XmNmarginWidth, 2); n++; XtSetArg(args[n], XmNmarginHeight, 2); n++; /* * 11/99: JRA - Added text font list */ if( new_p->text_font_list ) { XtSetArg( args[ n ], XmNfontList, new_p->text_font_list ); n++; } /* end if user specified a font list for the text field */ new_p->text = XtCreateManagedWidget(widget_name, xmTextFieldWidgetClass, (Widget)new, args, n); XtAddCallback(new_p->text, XmNlosingFocusCallback, text_losing_focus_cb, (XtPointer)new); XtAddCallback(new_p->text, XmNactivateCallback, text_activate_cb, (XtPointer)new); XtAddCallback(new_p->text, XmNfocusCallback, text_focus_cb, (XtPointer)new); /* * 11/99: JRA - Added horizontal and vertical spacing stuff */ if( new_p->horizontal_spacing == cur_p->horizontal_spacing ) new_p->horizontal_spacing = 0; if( new_p->vertical_spacing == cur_p->vertical_spacing ) new_p->vertical_spacing = 0; XtFree(widget_name); } else XtManageChild(new_p->text); } else { XtUnmanageChild(new_p->text); if (new_p->label == NULL) { widget_name = XtMalloc(strlen(XtName(new)) + 10); sprintf(widget_name, "%s_Label", XtName(new)); n = 0; XtSetArg(args[n], XmNalignment, new_p->alignment); 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, 0); n++; XtSetArg(args[n], XmNmarginHeight, 0); n++; /* * 11/99: JRA - Added label font list */ if( new_p->label_font_list ) { XtSetArg( args[ n ], XmNfontList, new_p->label_font_list ); n++; } /* end if user specified a font list for the text field */ new_p->label = XtCreateManagedWidget(widget_name, xmLabelWidgetClass, (Widget)new, args, n); XtOverrideTranslations((Widget)new_p->label, child_trans); /* * 11/99: JRA - Added horizontal and vertical spacing stuff */ if( new_p->horizontal_spacing == cur_p->horizontal_spacing ) new_p->horizontal_spacing = 1; if( new_p->vertical_spacing == cur_p->vertical_spacing ) new_p->vertical_spacing = 2; XtFree(widget_name); } else XtManageChild(new_p->label); } /* * 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) = (new_p->editable) ? TEXT_FIELD_SHADOW : LABEL_SHADOW; } /* Check arrow sensitivity (up arrow is right arrow)*/ if (new_p->arrow_sensitivity != cur_p->arrow_sensitivity) { XtSetSensitive(new_p->up_arrow, ((new_p->arrow_sensitivity == XmARROWS_SENSITIVE) || (new_p->arrow_sensitivity == XmARROWS_RIGHT_SENSITIVE))); XtSetSensitive(new_p->down_arrow, ((new_p->arrow_sensitivity == XmARROWS_SENSITIVE) || (new_p->arrow_sensitivity == XmARROWS_LEFT_SENSITIVE))); } /* * Check arrow layout. Only need to change arrows if going to or * from XmARROWS_SPLIT. The LayoutChildren() routine actually * positions the arrows in the correct place. */ if (new_p->arrow_layout != cur_p->arrow_layout) { if (new_p->arrow_layout == XmARROWS_SPLIT) { XtSetArg(args[0], XmNarrowDirection, XmARROW_RIGHT); XtSetValues(new_p->up_arrow, args, 1); XtSetArg(args[0], XmNarrowDirection, XmARROW_LEFT); XtSetValues(new_p->down_arrow, args, 1); } else if (cur_p->arrow_layout == XmARROWS_SPLIT) { XtSetArg(args[0], XmNarrowDirection, XmARROW_UP); XtSetValues(new_p->up_arrow, args, 1); XtSetArg(args[0], XmNarrowDirection, XmARROW_DOWN); XtSetValues(new_p->down_arrow, args, 1); } } if (new_p->text && (new_p->text == cur_p->text)) { n = 0; if (new_p->text_columns != cur_p->text_columns) { XtSetArg(args[n], XmNcolumns, new_p->text_columns); n++; } if (new_p->text_max_length != cur_p->text_max_length) { XtSetArg(args[n], XmNmaxLength, new_p->text_max_length); n++; } if (n > 0) XtSetValues(new_p->text, args, n); } /* * LabelWidget alignment has changed. */ if (new_p->label && (new_p->alignment != cur_p->alignment)) { XtSetArg(args[0], XmNalignment, new_p->alignment); XtSetValues(new_p->label, args, 1); } store_info = ((new_p->items != cur_p->items) || (new_p->item_count != cur_p->item_count) || (new_p->decimal_points != cur_p->decimal_points) || (new_p->maximum != cur_p->maximum) || (new_p->minimum != cur_p->minimum) || (new_p->numeric_increment != cur_p->numeric_increment) || ((new_p->child_type == XmNUMERIC) && (new_p->position != cur_p->position))); if (store_info) StoreResourceInfo(new_p, cur_p, (new_p->items != cur_p->items)); if (new_p->label && (store_info || (new_p->label != cur_p->label) || (new_p->child_type != cur_p->child_type))) { SetMaximumLabelSize(new_p); label_size_changed = TRUE; } if (store_info || (new_p->alignment != cur_p->alignment) || (new_p->editable != cur_p->editable) || (new_p->position != cur_p->position) || (new_p->label != cur_p->label) || (new_p->child_type != cur_p->child_type)) { if (new_p->editable) SetTextFieldData(new); else SetLabelData(new); } /* * Must recalculate the spin_Button 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 SetSpinButtonSize() uses * the label's core sizes to figure what size to become. */ /* * 11/99: JRA - Added horizontal and vertical spacing stuff */ if ((new_p->editable != cur_p->editable) || (SPIN_MARGIN_W(new) != SPIN_MARGIN_W(current)) || (SPIN_MARGIN_H(new) != SPIN_MARGIN_H(current)) || (SPIN_H_SPACING( new ) != SPIN_H_SPACING( current ) ) || (SPIN_V_SPACING( new ) != SPIN_V_SPACING( current ) ) || (SPIN_SHADOW(new) != SPIN_SHADOW(current)) || (new_p->arrow_layout != cur_p->arrow_layout) || (!new_p->editable && label_size_changed)) { ClearShadow(current, TRUE); if (new_p->recompute_size) SetSpinButtonSize(new); LayoutChildren(new); DrawShadow(new); } 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(w, all) XmSpinButtonWidget 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(w)) { if (all) { XClearArea(XtDisplayOfObject((Widget)w), XtWindowOfObject((Widget)w), margin_w, margin_h, w->spin_button.old_width - (margin_w * 2), shadow, FALSE); XClearArea(XtDisplayOfObject((Widget)w), XtWindowOfObject((Widget)w), margin_w, margin_h, shadow, w->spin_button.old_height - (margin_h * 2), FALSE); } XClearArea(XtDisplayOfObject((Widget)w), XtWindowOfObject((Widget)w), margin_w, w->spin_button.old_height - margin_h - shadow, w->spin_button.old_width - (margin_w * 2), shadow, FALSE); XClearArea(XtDisplayOfObject((Widget)w), XtWindowOfObject((Widget)w), w->spin_button.old_width - margin_w - shadow, margin_h, shadow, w->spin_button.old_height - (margin_h * 2), FALSE); } DrawHighlight(w, TRUE); } /* * This functions draws the shadow around our spin-button. */ static void DrawShadow(w) XmSpinButtonWidget w; { Dimension shadow = SPIN_SHADOW(w); Dimension margin_w = SPIN_MARGIN_W(w); Dimension margin_h = SPIN_MARGIN_H(w); if ((shadow > 0) && XtIsRealized(w)) { _XmDrawShadows(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 SpinButton, as * well as variables needed for child_type. */ static void StoreResourceInfo(spin_p, old_p, do_items) XmSpinButtonPart *spin_p; XmSpinButtonPart *old_p; Boolean do_items; { XmStringTable table; /* XmFontList font_list; Dimension width, height; Dimension longest = 0; Dimension highest = 0; */ 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 = 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(xm_string) 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(spin) XmSpinButtonWidget spin; { XmSpinButtonPart *spin_p = (XmSpinButtonPart*)&(spin->spin_button); char string[NUMERIC_LENGTH]; XmString xm_string; char *text; Arg arg; if (spin_p->child_type == XmNUMERIC) { sprintf(string, spin_p->float_format, (float)spin_p->current); XtSetArg(arg, XmNvalue, string); XtSetValues(spin_p->text, &arg, 1); } else { if (spin_p->items == NULL){ XtSetArg(arg, XmNvalue, ""); XtSetValues(spin_p->text, &arg, 1); return; } else { xm_string = spin_p->items[spin_p->position]; if ((text = GetTextString(xm_string))!=NULL) { XtSetArg(arg, XmNvalue, text); XtSetValues(spin_p->text, &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(spin_p) XmSpinButtonPart *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; /* Get font info from the widget */ XtSetArg(args[0], XmNfontList, &font_list); XtGetValues(spin_p->label, args, 1); if (spin_p->child_type == XmNUMERIC) { /* Find out maximum size of the widget from min/max */ sprintf(string, spin_p->float_format, spin_p->min); xm_string = XmStringCreateSimple(string); XmStringExtent(font_list, xm_string, &longest, &highest); XmStringFree(xm_string); sprintf(string, spin_p->float_format, spin_p->max); xm_string = XmStringCreateSimple(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 = longest + (LABEL_PADDING * 2); spin_p->label_max_height = highest; XtResizeWidget(spin_p->label, spin_p->label_max_length, highest, spin_p->label->core.border_width); } /* * Put the current list item into the label. */ static void SetLabelData(spin) XmSpinButtonWidget spin; { XmSpinButtonPart *spin_p = (XmSpinButtonPart*)&(spin->spin_button); XmString xm_string; char string[NUMERIC_LENGTH]; int index = spin_p->position; Arg arg; if (spin_p->child_type == XmNUMERIC) { sprintf(string, spin_p->float_format, (float)spin_p->current); xm_string = XmStringCreateSimple(string); XtSetArg(arg, XmNlabelString, xm_string); XtSetValues(spin_p->label, &arg, 1); } else { /* * If the item is not empty, get the current item from the list, else * use InitLabel. */ xm_string = (spin_p->items) ? spin_p->items[index] : InitLabel; XtSetArg(arg, XmNlabelString, xm_string); XtSetValues(spin_p->label, &arg, 1); } } /* * Timout dispatch routine. This calls the appropriate callback function * to simulate up or down arrow activation. */ static void timer_dispatch(client_data, id) XtPointer client_data; XtIntervalId *id; { XmSpinButtonWidget spin_w = (XmSpinButtonWidget)client_data; XmSpinButtonPart *spin_p = (XmSpinButtonPart*)&(spin_w->spin_button); timer = (XtIntervalId)NULL; spin_p->init_cb = FALSE; if (spin_p->which_arrow == XmARROW_UP) { if (spin_p->grabbed) { XtRemoveGrab(spin_p->up_arrow); spin_p->grabbed = FALSE; } up_cb(NULL, client_data, NULL); } else { if (spin_p->grabbed) { XtRemoveGrab(spin_p->down_arrow); spin_p->grabbed = FALSE; } down_cb(NULL, client_data, NULL); } } static void TextFieldActivate(spin_p) XmSpinButtonPart *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 == XmNUMERIC) { 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; XtCallCallbackList((Widget)w, w->text.activate_callback, (XtPointer)&cb); if (text && free_me) XtFree(text); } } /* * This function calls the appropriate callback for the spin-button. * It gathers the correct arguments and fills in the callback structure. */ static Boolean SendCallback(spin, event, value_changed, position, current, crossed) XmSpinButtonWidget spin; XEvent *event; Boolean value_changed; int position; float current; /* Used for numeric */ Boolean crossed; { XmSpinButtonPart *spin_p = (XmSpinButtonPart*)&(spin->spin_button); XmSpinButtonCallbackStruct cb; XmString xm_string = NULL; char string[NUMERIC_LENGTH]; if (spin_p->child_type == XmNUMERIC) { sprintf(string, spin_p->float_format, (float)current); xm_string = XmStringCreateSimple(string); } else { xm_string = (spin_p->items) ? spin_p->items[position] : InitLabel; xm_string = XmStringCopy(xm_string); } if (event) cb.reason = XmCR_OK; else if (spin_p->which_arrow == XmARROW_UP) cb.reason = XmCR_SPIN_UP; else cb.reason = XmCR_SPIN_DOWN; 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, spin_p->value_changed_callback, (XtPointer)&cb); cb.doit = TRUE; } else { XtCallCallbackList((Widget)spin, spin_p->modify_verify_callback, (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(spin, arrow_call_data, new_position, new_current, crossed) XmSpinButtonWidget spin; XtPointer arrow_call_data; int new_position; float new_current; Boolean crossed; { XmSpinButtonPart *spin_p = (XmSpinButtonPart*)&(spin->spin_button); XmArrowButtonCallbackStruct *arrow_data; XEvent *last_event = NULL; int repeat_delay = spin_p->repeat_delay; if (spin_p->init_cb) repeat_delay = spin_p->initial_delay; timer = XtAppAddTimeOut(XtWidgetToApplicationContext((Widget)spin), repeat_delay, timer_dispatch, (XtPointer)spin); /* Try to get Xevent */ /* FIXME is it = or == ????? */ if ((arrow_data = (XmArrowButtonCallbackStruct*)arrow_call_data)!=NULL) 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); timer = (XtIntervalId)NULL; spin->spin_button.init_cb = TRUE; return; } /* User said yes, so set widget values */ spin_p->position = new_position; spin_p->current = new_current; if (spin_p->editable) SetTextFieldData(spin); else SetLabelData(spin); /* send value_changed callback */ (void)SendCallback(spin, last_event, TRUE, spin_p->position, spin_p->current, crossed); /* See notes at top of function on XtAddGrab usage */ spin_p->grabbed = TRUE; if (spin_p->which_arrow == XmARROW_UP) XtAddGrab(spin_p->up_arrow, FALSE, FALSE); else XtAddGrab(spin_p->down_arrow, FALSE, FALSE); } /* * Show the next value in the SpinButton. 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(w, client_data, call_data) Widget w; XtPointer client_data; XtPointer call_data; { XmSpinButtonWidget spin = (XmSpinButtonWidget)client_data; XmSpinButtonPart *spin_p = (XmSpinButtonPart*)&(spin->spin_button); Boolean crossed_boundary = FALSE; int new_position = spin_p->position; float new_current = spin_p->current; if (spin_p->editable) TextFieldActivate(spin_p); /* * If non-numeric and no items then ignore the user activate event. */ if ((spin_p->child_type == XmSTRING) && (spin_p->items == NULL)) return; if (spin_p->child_type == XmNUMERIC) { if ((new_current + spin_p->increment) > spin_p->max) { if (spin_p->wrap) { new_position = spin_p->minimum; new_current = spin_p->min; crossed_boundary = TRUE; } else XBell(XtDisplayOfObject((Widget)spin), 0); } else { new_position += spin_p->numeric_increment; new_current += spin_p->increment; } } else if (spin_p->items) { if (new_position == (spin_p->item_count - 1)) { if (spin_p->wrap) { new_position = 0; crossed_boundary = TRUE; } else XBell(XtDisplayOfObject((Widget)spin), 0); } else new_position++; } spin_p->which_arrow = XmARROW_UP; FinishUpDown(spin, call_data, new_position, new_current, crossed_boundary); } /* * Show the previous value in the SpinButton. 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(w, client_data, call_data) Widget w; XtPointer client_data; XtPointer call_data; { XmSpinButtonWidget spin = (XmSpinButtonWidget)client_data; XmSpinButtonPart *spin_p = (XmSpinButtonPart*)&(spin->spin_button); /* XmArrowButtonCallbackStruct *arrow_data; XEvent *last_event = NULL; int repeat_delay = spin_p->repeat_delay; */ Boolean crossed_boundary = FALSE; int new_position = spin_p->position; float new_current = spin_p->current; if (spin_p->editable) TextFieldActivate(spin_p); /* * If non-numeric and no items then ignore the user activate event. */ if ((spin_p->child_type == XmSTRING) && (spin_p->items == NULL)) return; if (spin_p->child_type == XmNUMERIC) { if ((new_current - spin_p->increment) < spin_p->min) { if (spin_p->wrap) { new_current = spin_p->max; new_position = spin_p->maximum; crossed_boundary = TRUE; } else XBell(XtDisplayOfObject((Widget)spin), 0); } else { new_current -= spin_p->increment; new_position -= spin_p->numeric_increment; } } else if (spin_p->items) { if (new_position == 0) { if (spin_p->wrap) { new_position = spin_p->item_count - 1; crossed_boundary = TRUE; } else XBell(XtDisplayOfObject((Widget)spin), 0); } else new_position--; } spin_p->which_arrow = XmARROW_DOWN; FinishUpDown(spin, call_data, new_position, new_current, crossed_boundary); } static void disarm_cb(w, client_data, call_data) Widget w; XtPointer client_data; XtPointer call_data; { XmSpinButtonWidget spin = (XmSpinButtonWidget)client_data; if ((int)timer) { spin->spin_button.init_cb = TRUE; XtRemoveTimeOut(timer); timer = (XtIntervalId)NULL; if (spin->spin_button.grabbed) { XtRemoveGrab(w); spin->spin_button.grabbed = FALSE; } } } static void grab_leave_cb(w, client_data, event, dispatch) Widget w; XtPointer client_data; XEvent *event; Boolean *dispatch; { XmSpinButtonWidget spin = (XmSpinButtonWidget)client_data; if ((int)timer && ((event->xcrossing.mode == NotifyGrab) || (event->xcrossing.mode == NotifyUngrab) || (!(event->xcrossing.state & Button1Mask)))) { spin->spin_button.init_cb = TRUE; XtRemoveTimeOut(timer); timer = (XtIntervalId)NULL; if (spin->spin_button.grabbed) { XtRemoveGrab(w); spin->spin_button.grabbed = 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(w, client_data, call_data) Widget w; XtPointer client_data; XtPointer call_data; { XmSpinButtonWidget spin = (XmSpinButtonWidget)client_data; if (spin->spin_button.losing_focus_callback) XtCallCallbackList((Widget)spin, spin->spin_button.losing_focus_callback, (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. * * 11/99: JRA - Modified to check for correct values and if the * user entered a correct value, set the current and * position variables to match it. */ static void text_activate_cb( w, client_data, call_data ) Widget w; XtPointer client_data; XtPointer call_data; { XmSpinButtonWidget spin = (XmSpinButtonWidget)client_data; XmSpinButtonPart *spin_p = (XmSpinButtonPart*)&(spin->spin_button); char *text; /*int i;*/ int intVal; float floatVal; XmString str; switch( spin_p->child_type ) { /* * 11/99: JRA - If it's a string spin button, try to add it via * AddItem (with the new uniqueness feature we've added). If we * can't do that, warn user via the bell. */ case XmSTRING: str = XmStringCreateLocalized( XmSpinButtonGetTextString( ( Widget ) spin ) ); if( !XmSpinButtonAddItem( spin, str, spin_p->position + 1, TRUE ) ) { XBell( XtDisplay( spin ), 0 ); XmStringFree( str ); return; } /* end if we weren't able to add item to the list */ XmStringFree( str ); break; /* * 11/99: JRA - If it's a numeric spin button, get the text, the * integer value of the text, (the flot value if it's a float type * button), and see if it's within the min and max. If not, warn user * via the bell. If so, set the private members accordingly. * We could potentially add error checking on the atoi/atof, but * I'll leave that for now. */ case XmNUMERIC: text = XmTextFieldGetString( spin_p->text ); intVal = atoi( text ); if( spin_p->decimal_points > 0 ) floatVal = atof( text ); else floatVal = ( float ) intVal; if( ( ( spin_p->decimal_points == 0 ) && ( intVal < spin_p->minimum || intVal > spin_p->maximum ) ) || ( ( spin_p->decimal_points > 0 ) && ( floatVal < spin_p->min || floatVal > spin_p->max ) ) ) { XBell( XtDisplay( spin ), 0 ); XtFree( text ); return; } /* end if value entered is not within the allowable range */ spin_p->current = floatVal; spin_p->position = intVal; XtFree( text ); break; } /* end switch on child type */ if( spin->spin_button.activate_callback ) XtCallCallbackList( ( Widget ) spin, spin->spin_button.activate_callback, ( XtPointer ) call_data ); } /* end text_activate_cb() */ /* * 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(w, client_data, call_data) Widget w; XtPointer client_data; XtPointer call_data; { XmSpinButtonWidget spin = (XmSpinButtonWidget)client_data; if (spin->spin_button.focus_callback) XtCallCallbackList((Widget)spin, spin->spin_button.focus_callback, (XtPointer)call_data); } /* * Synthetic resource get functions. */ static XmImportOperator _XmSetSyntheticResForChild(widget, offset, value) Widget widget; int offset; XtArgVal * value; { return(XmSYNTHETIC_LOAD); } void _XmSpinButtonGetArrowSize(w, resource_offset, value) Widget w; int resource_offset; XtArgVal *value; { XmSpinButtonWidget spin = (XmSpinButtonWidget)w; *value = (XtArgVal)spin->spin_button.up_arrow->core.height; } /* * Routines which manipulate the SpinButton list. These are external * for use by users of our widget. */ Widget XmCreateSpinButton(parent, name, arglist, num_args) Widget parent; char *name; Arg *arglist; int num_args; { return(XtCreateWidget(name, xmSpinButtonWidgetClass, parent, arglist, num_args)); } /* * 11/99: JRA - Modified to add uniqueness to string list items. */ Boolean XmSpinButtonAddItem( spin, item, pos, unique ) XmSpinButtonWidget spin; XmString item; int pos; Boolean unique; { XmSpinButtonPart *spin_p = (XmSpinButtonPart*)&(spin->spin_button); XmString old, new_str, tmp; int total_items, i; total_items = spin_p->item_count + 1; if( item && total_items ) { for( i=0 ; iitem_count ; i++ ) if( XmStringCompare( item, spin_p->items[ i ] ) ) break; if( ( i < spin_p->item_count ) && unique ) return( FALSE ); } /* end if there is an item and a list of items */ spin_p->items = (XmString *)XtRealloc((char*)spin_p->items, (sizeof(XmString) * total_items)); new_str = XmStringCopy(item); pos--; /* User gives pos starting at 1 (0 means end of list) */ if ((pos < 0) || (pos > spin_p->item_count)) pos = spin_p->item_count; if (pos >= spin_p->item_count) spin_p->items[pos] = new_str; else { old = spin_p->items[pos]; spin_p->items[pos] = new_str; for (pos++; pos < total_items; pos++) { tmp = spin_p->items[pos]; spin_p->items[pos] = old; old = tmp; } } spin_p->item_count = total_items; if (spin_p->label) { SetMaximumLabelSize(spin_p); if (spin_p->editable == FALSE) { ClearShadow(spin, TRUE); if (spin_p->recompute_size) SetSpinButtonSize(spin); LayoutChildren(spin); DrawShadow(spin); } } /* Update the text-field or label */ if (spin_p->editable) SetTextFieldData(spin); else SetLabelData(spin); return( TRUE ); } /* end XmSpinButtonAddItem() */ void XmSpinButtonDeletePos(spin, pos) XmSpinButtonWidget spin; int pos; { XmSpinButtonPart *spin_p = (XmSpinButtonPart*)&(spin->spin_button); int total_items; if (spin_p->item_count < 1) return; pos--; if ((pos < 0) || (pos > spin_p->item_count)) pos = spin_p->item_count - 1; total_items = spin_p->item_count - 1; XmStringFree(spin_p->items[pos]); if (pos < spin_p->item_count) { for (; pos < total_items; pos++) spin_p->items[pos] = spin_p->items[pos+1]; } spin_p->items = (XmString *)XtRealloc((char*)spin_p->items, (sizeof(XmString) * total_items)); spin_p->item_count = total_items; if (spin_p->label) { SetMaximumLabelSize(spin_p); if (spin_p->editable == FALSE) { ClearShadow(spin, TRUE); if (spin_p->recompute_size) SetSpinButtonSize(spin); LayoutChildren(spin); DrawShadow(spin); } } /* Update the text-field or label */ if (spin_p->editable) SetTextFieldData(spin); else SetLabelData(spin); } /* * Make the given item the currently visible item in the * text-field or label. */ void XmSpinButtonSetItem(spin, item) XmSpinButtonWidget spin; XmString item; { XmSpinButtonPart *spin_p = (XmSpinButtonPart*)&(spin->spin_button); int i; if (item && spin_p->items) { for (i = 0; i < spin_p->item_count; i++) if (XmStringCompare(item, spin_p->items[i])) break; if (i < spin_p->item_count) { spin_p->position = i; if (spin_p->editable) SetTextFieldData(spin); else SetLabelData(spin); } else XtWarning(SPIN_SET_ITEM); } else XtWarning(SPIN_SET_ITEM); } /* * 11/99: JRA - Added to get the text in the Text Field widget. */ char *XmSpinButtonGetTextString( Widget widget ) { XmSpinButtonWidget spin; XmSpinButtonPart *spin_p; if( !XmIsSpinButton( widget ) ) return( ( char * ) NULL ); spin = ( XmSpinButtonWidget ) widget; spin_p = ( XmSpinButtonPart * ) &( spin->spin_button ); if( spin_p->editable ) return( XmTextFieldGetString( spin_p->text ) ); else return( ( char * ) NULL ); } /* end XmSpinButtonGetTextString() */ /* * 11/99: JRA - Added to set the background color of the Text Field widget. */ void XmSpinButtonSetTextFieldBackground( widget, bg ) Widget widget; Pixel bg; { XmSpinButtonWidget spin = ( XmSpinButtonWidget ) widget; XmSpinButtonPart *spin_p = ( XmSpinButtonPart * ) &( spin->spin_button ); if( !spin_p->editable ) return; XtVaSetValues( spin_p->text, XmNbackground, bg, NULL ); } /* end XmSpinButtonSetTextFieldBackground() */ /* * 11/99: JRA - Added to get the background color of the Text Field widget. */ void XmSpinButtonGetTextFieldBackground( widget, bg ) Widget widget; Pixel *bg; { XmSpinButtonWidget spin = ( XmSpinButtonWidget ) widget; XmSpinButtonPart *spin_p = ( XmSpinButtonPart * ) &( spin->spin_button ); if( !spin_p->editable ) return; XtVaGetValues( spin_p->text, XmNbackground, bg, NULL ); } /* end XmSpinButtonGetTextFieldBackground() */