/* * Motif Tools Library, Version 3.1 * $Id: InputField.c,v 1.3 2001/07/20 23:07:49 grmcdorman Exp $ * * Written by David Flanagan. * Copyright (c) 1992-2001 by David Flanagan. * All Rights Reserved. See the file COPYRIGHT for details. * This is open source software. See the file LICENSE for details. * There is no warranty for this software. See NO_WARRANTY for details. * * $Log: InputField.c,v $ * Revision 1.3 2001/07/20 23:07:49 grmcdorman * This change provides two optional Motif/NuTCracker bug workarounds. * Defines in Xmt.tmpl (normally commented out) turn them on. * * Revision 1.2 2001/04/15 15:29:36 grmcdorman * * XmtCallCallback has two new arguments types: XmtRCallbackUserData, which * is the XmNuserData value, and XmtRCallbackImmediate, which is a value * embedded in the list. * * * Two new functions, XmtCreateQueryListChildren and XmtCreateQueryListChild, * are provided. They are like the XmtCreate* functions except that they * return a list of the widgets created (quark and pointer to the widget). * * * Major enhancements to XmtInputField: * - The input field now has a cancel() action. This reverts the field's * value to that prior to the start of editing (i.e. the last commit). * No default translation is bound to this action, however. * - There is a callback for the cancel action (XmtNcancelCallback). * - The input field can be set to cancel, commit, or do nothing on focus * out. (Previously, it always did a commit). The default is commit. * (XmtNfocusOutAction) * * * XmtLayout: * - It now has a losingFocusCallback (XmNloosingFocusCallback). * - It, and the layout widgets (XmtLayoutBox/XmtLayoutString), now * support background pixmaps. * - It now supports XmNresizePolicy. * - Strings in the layout parser are converted to XmStrings by * XtConvertAndStore, so any custom XmString conversion is used * (as, for example, the Xmt convert, which supports the @[] * embedded items) * * * Other: * - Pixmaps are convered using XtConvertAndStore, again to allow * custom converters. * * Bug Fixes: (for Solaris 2.5.1/Motif 1.2/Sparc system) * * XmtInputField did not work properly if the widget had traversalOn * false. * * XmtLayout's child geometry manager was called by Xt when * a shell child was added to the layout; it did not handle * this case. * * XmtLayout had an XmtWarningMsg call with a missing argument (line 2029). * * Widgets automatically created by XmtLayout parsing have been given * non-blank names. [You may wish to tweak this by adding, for example, * a leading underscore.] * * Under some circumstances, XmtNameToWidget could be called during * widget creation, with 'w->core.num_popups' uninitialized. The * workaround applied is to ignore the popup list if the 'num_popups' * value is more than 50 or negative. * * Revision 1.1.1.1 2001/02/10 13:44:32 motiftools * Initial import of Xmt310 to CVS * * */ #include #include #include #include #include #include #include /* for XmtNlayoutSensitive resource */ #include #ifdef DISABLE_INPUTFIELD_DRAGDROP #include #endif #ifndef X_NOT_STDC_ENV #include #else extern int atoi(); extern double atof(); #endif /* * I don't know how MAXINT is getting defined on most platforms, (I * don't know where is included), but it isn't defined in * Linux. It might be better to include and switch to * INT_MAX, since this is the POSIX way to do it. But then I'd have * to take care of non-POSIX platforms as a special case. So this fix * is easier. Platforms with 64-bit integers that don't define MAXINT * will just have to live with this 32-bit definition. This won't be * a problem for its use in this file. */ #ifndef MAXINT #define MAXINT 0x7FFFFFFF #endif #define offset(field) XtOffsetOf(XmtInputFieldRec, input_field.field) static XtResource resources[] = { {XmtNinput, XmtCInput, XtRString, sizeof(String), offset(input), XtRString, (XtPointer)NULL}, {XmtNpattern, XmtCPattern, XtRString, sizeof(String), offset(pattern), XtRString, (XtPointer)NULL}, {XmtNmatchAll, XmtCMatchAll, XtRBoolean, sizeof(Boolean), offset(match_all), XtRImmediate, (XtPointer)True}, {XmtNoverstrike, XmtCOverstrike, XtRBoolean, sizeof(Boolean), offset(overstrike), XtRBoolean, (XtPointer) False}, {XmtNbufferSymbolName, XmtCBufferSymbolName, XtRString, sizeof(String), offset(buffer_symbol_name), XtRString, (XtPointer)NULL}, {XmtNtargetSymbolName, XmtCTargetSymbolName, XtRString, sizeof(String), offset(target_symbol_name), XtRString, (XtPointer)NULL}, {XmtNautoInsert, XmtCAutoInsert, XtRBoolean, sizeof(Boolean), offset(auto_insert), XtRImmediate, (XtPointer)True}, {XmtNautoDelete, XmtCAutoDelete, XtRBoolean, sizeof(Boolean), offset(auto_delete), XtRImmediate, (XtPointer)True}, {XmtNbeepOnError, XmtCBeepOnError, XtRBoolean, sizeof(Boolean), offset(beep_on_error), XtRImmediate, (XtPointer) True}, {XmtNreplaceOnError, XmtCReplaceOnError, XtRBoolean, sizeof(Boolean), offset(replace_on_error), XtRImmediate, (XtPointer) True}, {XmtNhighlightOnError, XmtCHighlightOnError, XtRBoolean, sizeof(Boolean), offset(highlight_on_error), XtRImmediate, (XtPointer) False}, {XmtNerrorString, XmtCErrorString, XtRString, sizeof(String), offset(error_string), XtRString, (XtPointer) NULL}, {XmtNerrorForeground, XmtCErrorForeground, XtRPixel, sizeof(String), offset(error_foreground), XtRImmediate, (XtPointer) -1}, /* XXX could be a valid Pixel value */ {XmtNerrorBackground, XmtCErrorBackground, XtRPixel, sizeof(String), offset(error_background), XtRImmediate, (XtPointer) -1}, /* XXX could be a valid Pixel value */ {XmtNinputCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList), offset(input_callback), XtRCallback, (XtPointer) NULL}, {XmtNverifyCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList), offset(verify_callback), XtRCallback, (XtPointer) NULL}, {XmtNerrorCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList), offset(error_callback), XtRCallback, (XtPointer) NULL}, {XmtNcancelCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList), offset(cancel_callback), XtRCallback, (XtPointer) NULL}, {XmtNfocusOutAction, XmtCFocusOutAction, XmtRXmtInputFieldActions, sizeof(int), offset(focus_out_action), XtRImmediate, (XtPointer) XmtINPUT_COMMIT}, /* * override navigationType here so arrow-key traversal will work */ {XmNnavigationType, XmCNavigationType, XmRNavigationType, sizeof(unsigned char), XtOffsetOf(XmtInputFieldRec, primitive.navigation_type), XtRImmediate, (XtPointer) XmNONE}, }; #undef offset /* widget methods */ #if NeedFunctionPrototypes static void ClassInitialize(void); static void Initialize(Widget, Widget, ArgList, Cardinal *); static void Destroy(Widget); static Boolean SetValues(Widget, Widget, Widget, ArgList, Cardinal *); #else static void ClassInitialize(); static void Initialize(); static void Destroy(); static Boolean SetValues(); #endif /* action procedure */ #if NeedFunctionPrototypes static void Overstrike(Widget, XEvent *, String *, Cardinal *); static void Cancel(Widget, XEvent *, String *, Cardinal *); static void GrabFocus(Widget, XEvent *, String *, Cardinal *); #else static void Overstrike(); static void Cancel(); static void GrabFocus(); #endif static XtActionsRec actions[] = { {"overstrike", Overstrike}, {"cancel", Cancel}, {"grab-focus", GrabFocus}, /* same as XmText action */ }; #if XmVersion > 1001 static XmPrimitiveClassExtRec primClassExtRec = { NULL, NULLQUARK, XmPrimitiveClassExtVersion, sizeof(XmPrimitiveClassExtRec), XmInheritBaselineProc, /* widget_baseline */ XmInheritDisplayRectProc, /* widget_display_rect */ /* (XmWidgetMarginsProc) _XtInherit */ _XmTextMarginsProc /* this isn't inherited properly */ }; #endif #define Superclass (&xmTextClassRec) externaldef(xmtinputfieldclassrec) XmtInputFieldClassRec xmtInputFieldClassRec = { { /* core_class fields */ /* superclass */ (WidgetClass) Superclass, /* class_name */ "XmtInputField", /* widget_size */ sizeof(XmtInputFieldRec), /* class_initialize */ ClassInitialize, /* class_part_init */ NULL, /* class_inited */ FALSE, /* initialize */ Initialize, /* initialize_hook */ NULL, /* realize */ XtInheritRealize, /* actions */ actions, /* num_actions */ XtNumber(actions), /* resources */ resources, /* num_resources */ XtNumber(resources), /* xrm_class */ NULLQUARK, /* compress_motion */ TRUE, /* compress_exposure */ XtExposeCompressMaximal, /* compress_enterleave*/ TRUE, /* visible_interest */ FALSE, /* destroy */ Destroy, /* resize */ XtInheritResize, /* expose */ XtInheritExpose, /* set_values */ SetValues, /* set_values_hook */ NULL, /* set_values_almost */ XtInheritSetValuesAlmost, /* get_values_hook */ NULL, /* accept_focus */ XtInheritAcceptFocus, /* version */ XtVersion, /* callback_private */ NULL, /* tm_table */ XtInheritTranslations, /* query_geometry */ XtInheritQueryGeometry, /* display_accelerator*/ XtInheritDisplayAccelerator, /* extension */ NULL, }, { /* primitive_class fields */ XmInheritBorderHighlight, /* Primitive border_highlight */ XmInheritBorderUnhighlight,/* Primitive border_unhighlight */ NULL, /* translations */ NULL, /* arm_and_activate */ NULL, /* get resources */ 0, /* num get_resources */ #if XmVersion <= 1001 NULL, /* extension */ #else (XtPointer)&primClassExtRec,/* extension */ #endif }, { /* text class fields */ NULL, /* extension */ }, { /* XmtInputField */ /* superclassGrabFocus*/ NULL, /* extension */ NULL } }; externaldef(xmtinputfieldwidgetclass) WidgetClass xmtInputFieldWidgetClass = (WidgetClass)&xmtInputFieldClassRec; #if XmVersion != 1002 #define SetString XmTextSetString #else #if NeedFunctionPrototypes static void SetString(Widget w, String s) #else static void SetString(w, s) Widget w; String s; #endif { Boolean hack; XmtInputFieldWidget iw = (XmtInputFieldWidget) w; if (s && s[0]) XmTextSetString(w,s); else { hack = iw->text.auto_show_cursor_position; iw->text.auto_show_cursor_position = False; XmTextSetString(w,s); iw->text.auto_show_cursor_position = hack; } } #endif /* ARGSUSED */ #if NeedFunctionPrototypes static void HandleOverstrike(Widget text, XtPointer tag, XtPointer call_data) #else static void HandleOverstrike(text, tag, call_data) Widget text; XtPointer tag; XtPointer call_data; #endif { XmtInputFieldWidget iw = (XmtInputFieldWidget) text; XmTextVerifyCallbackStruct *data = (XmTextVerifyCallbackStruct*) call_data; /* if this is not a user keystroke, don't check it */ if (!data->event) return; /* if this is a deletion, do nothing */ /* Different versions of Motif indicate deletions in different ways, * so check several fields */ if ((data->text->length == 0) || (data->text->ptr == NULL) || (data->text->ptr[0] == '\0')) return; /* * turn an insertion into a replace. * HandlePattern() also does this for us, in case it is called * after this callback and changes increase the insertion length */ data->endPos = data->startPos+data->text->length; /* * We can't have overstrike on and the text XmNmaxLength set, because the * text won't allow us to type a character when we're at maxLength, because * it assumes we'll be inserting it. So when overstrike is on, we * don't set maxLength, and instead check the length here. */ if (data->endPos > iw->input_field.max_length) data->doit = False; } /* ARGSUSED */ #if NeedFunctionPrototype static void MoveCursorTimerProc(XtPointer data, XtIntervalId *id) #else static void MoveCursorTimerProc(data, id) XtPointer data; XtIntervalId *id; #endif { XmtInputFieldWidget iw = (XmtInputFieldWidget)data; if (iw->input_field.pattern_skip != 0) { XmTextSetInsertionPosition((Widget)iw, XmTextGetInsertionPosition((Widget)iw) + iw->input_field.pattern_skip); iw->input_field.pattern_skip = 0; } } /* ARGSUSED */ #if NeedFunctionPrototypes static int isoctal(int c) #else static int isoctal(c) int c; #endif { return (int)(strchr ("01234567", c)); } /* ARGSUSED */ #if NeedFunctionPrototypes static int ishex(int c) #else static int ishex(c) int c; #endif { return (int)(strchr ("0123456789abcdefABCDEF", c)); } /* ARGSUSED */ #if NeedFunctionPrototypes static void HandlePattern(Widget text, XtPointer tag, XtPointer call_data) #else static void HandlePattern(text, tag, call_data) Widget text; XtPointer tag; XtPointer call_data; #endif { XmtInputFieldWidget iw = (XmtInputFieldWidget) text; XmTextVerifyCallbackStruct *data = (XmTextVerifyCallbackStruct*) call_data; int i, j, pos, last; char *c; /* if this is not a user keystroke, don't check it */ if (!data->event) return; last = XmTextGetLastPosition(text); /* if it is a deletion, only allow it at the end. * Also, if autoDelete adjust startPos to point to a non-constant char */ /* Different versions of Motif indicate deletions in different ways, * so check several fields */ if ((data->text->length == 0) || (data->text->ptr == NULL) || (data->text->ptr[0] == '\0')) { if (data->endPos != last) { data->doit = False; #ifdef BACKSPACEBUG data->text->ptr = NULL; #endif return; } if (iw->input_field.auto_delete) for(c = &iw->input_field.pattern[data->startPos]; data->startPos > 0; data->startPos--, c--) { if (((*c >= 'a')&&(*c <= 'd')) || ((*c >= 'A')&&(*c <= 'C')) || (*c == 'h') || (*c == 'o')) break; } return; } /* otherwise it is an insertion, or if in overstrike mode, a replace. * if not in overstrike mode, only allow it at the end. * Also, if it would put us past the end of the pattern, reject */ if ((!iw->input_field.overstrike) && (data->startPos != last)) { data->doit = False; return; } if (data->startPos + data->text->length > iw->input_field.pattern_length) { data->doit = False; return; } iw->input_field.pattern_skip = 0; /* # of const. chars inserted */ /* test each character of the insertion */ for(i=0, pos=data->startPos; i < data->text->length; i++, pos++) { c = &data->text->ptr[i]; switch(iw->input_field.pattern[pos]) { case 'A': *c = toupper(*c); case 'a': if (!isalpha(*c)) data->doit = False; break; case 'B': *c = toupper(*c); case 'b': if (!isalpha(*c) && !isdigit(*c)) data->doit = False; break; case 'C': *c = toupper(*c); case 'c': break; case 'd': if (!isdigit(*c)) data->doit = False; break; case 'o': if (!isoctal(*c)) data->doit = False; break; case 'h': if (!ishex(*c)) data->doit = False; break; default: /* * it is a constant character. If *c matches, accept it. * Otherwise, if autoInsert, insert it, otherwise reject. */ if (*c == iw->input_field.pattern[pos]) break; if (!iw->input_field.auto_insert) { data->doit = False; break; } data->text->length++; data->text->ptr = XtRealloc(data->text->ptr, data->text->length); for(j=data->text->length-1; j > i; j--) data->text->ptr[j] = data->text->ptr[j-1]; data->text->ptr[i] = iw->input_field.pattern[pos]; iw->input_field.pattern_skip++; break; } if (data->doit == False) return; } if (iw->input_field.pattern_skip) XtAppAddTimeOut(XtWidgetToApplicationContext(text), 0, MoveCursorTimerProc, (XtPointer)iw); /* if we are overstriking and this is a replace, we've got to * increase the deleted characters by the # of inserted constant chars * The right thing will happen whether HandleOverstrike is called * first or this procedure is called first. */ if (iw->input_field.overstrike) data->endPos = data->startPos + data->text->length; } /* ARGSUSED */ #if NeedFunctionPrototypes static void ValueChanged(Widget text, XtPointer tag, XtPointer data) #else static void ValueChanged(text, tag, data) Widget text; XtPointer tag; XtPointer data; #endif { XmtInputFieldWidget iw = (XmtInputFieldWidget) text; /* remember that the value has changed */ iw->input_field.input_changed = True; /* and remove ourselves for efficiency */ XtRemoveCallback(text, XmNvalueChangedCallback, ValueChanged, NULL); } #if NeedFunctionPrototypes static void RestoreOldValue(XmtInputFieldWidget iw) #else static void RestoreOldValue(iw) XmtInputFieldWidget iw; #endif { SetString((Widget)iw, iw->input_field.input); XmTextSetInsertionPosition((Widget)iw, XmTextGetLastPosition((Widget)iw)); iw->input_field.input_changed = False; XtAddCallback((Widget)iw, XmNvalueChangedCallback, ValueChanged, NULL); } /* ARGSUSED */ #if NeedFunctionPrototypes static void UndoError(Widget text, XtPointer tag, XtPointer data) #else static void UndoError(text, tag, data) Widget text; XtPointer tag; XtPointer data; #endif { XmtInputFieldWidget iw = (XmtInputFieldWidget) text; /* * if not handling an error, don't do anything. * This should never happen. */ if (!iw->input_field.error_state) return; /* remove this callback everywhere it was registered */ XtRemoveCallback(text, XmNvalueChangedCallback, UndoError, NULL); XtRemoveCallback(text, XmNfocusCallback, UndoError, NULL); XtRemoveCallback(text, XmNactivateCallback, UndoError, NULL); /* if we are currently displaying an error string, change it, either * to the last value, or back to the bad value */ if (iw->input_field.error_string) { if (iw->input_field.replace_on_error) { RestoreOldValue(iw); } else { SetString(text, iw->input_field.error_value); XmTextSetInsertionPosition(text, XmTextGetLastPosition(text)); XtFree(iw->input_field.error_value); } } else if (iw->input_field.highlight_on_error) { /* if a color specified, and color display, restore old color */ if (((iw->input_field.error_background != (Pixel)-1) || (iw->input_field.error_foreground != (Pixel)-1)) && (iw->core.depth > 1)) { Arg al[2]; int ac = 0; if (iw->input_field.error_background != (Pixel)-1) { XtSetArg(al[ac],XmNbackground,iw->input_field.background); ac++; } if (iw->input_field.error_foreground != (Pixel)-1) { XtSetArg(al[ac],XmNforeground,iw->input_field.foreground); ac++; } XtSetValues(text, al, ac); } else { /* remove the underlining */ XmTextSetHighlight(text, 0,XmTextGetLastPosition(text), XmHIGHLIGHT_NORMAL); } /* if replaceOnError, restore old string */ if (iw->input_field.replace_on_error) RestoreOldValue(iw); } iw->input_field.error_state = False; } #if NeedFunctionPrototypes static void HandleError(XmtInputFieldWidget iw, String value) #else static void HandleError(iw, value) XmtInputFieldWidget iw; String value; #endif { XmtInputFieldCallbackStruct error; /* * if we're already handling an error, don't do anything. */ if (iw->input_field.error_state) return; /* * call the error callback to give app programmer the chance to handle * the error. If requested, abort further error handling */ error.input = value; error.okay = False; XtCallCallbackList((Widget)iw, iw->input_field.error_callback, (XtPointer) &error); if (error.okay == True) return; /* beep if requested */ if (iw->input_field.beep_on_error) XBell(XtDisplay(iw), 0); if (iw->input_field.error_string) { /* * We'll display the error string, but first, we've got to save a * copy of the current bad string, unless we're going to replace that * string with the current value resource. * See UndoError() above. */ if (!iw->input_field.replace_on_error) iw->input_field.error_value = XmTextGetString((Widget)iw); SetString((Widget)iw, iw->input_field.error_string); XmTextSetInsertionPosition((Widget)iw, XmTextGetLastPosition((Widget)iw)); } else if (iw->input_field.highlight_on_error) { /* use color if a color is specified, and if a color display */ if (((iw->input_field.error_background != (Pixel)-1) || (iw->input_field.error_foreground != (Pixel)-1)) && (iw->core.depth > 1)) { Arg al[2]; int ac = 0; if (iw->input_field.error_background != (Pixel)-1) { iw->input_field.background = iw->core.background_pixel; XtSetArg(al[ac],XmNbackground, iw->input_field.error_background); ac++; } if (iw->input_field.error_foreground != (Pixel)-1) { iw->input_field.foreground = iw->primitive.foreground; XtSetArg(al[ac],XmNforeground, iw->input_field.error_foreground); ac++; } XtSetValues((Widget)iw, al, ac); } else { /* just underline the text */ XmTextSetHighlight((Widget)iw, 0,XmTextGetLastPosition((Widget)iw), XmHIGHLIGHT_SECONDARY_SELECTED); } } else if (iw->input_field.replace_on_error) { /* * if no error string or highlighting, restore old value now. * otherwise, we restore it in UndoError() above. */ RestoreOldValue(iw); } /* * add callbacks to undo the effects of this error handling * on focus in or any keypress. Note that if the error handling is * just replace_on_error, there is nothing to undo. * Set the error_state flag. */ if (iw->input_field.error_string || iw->input_field.highlight_on_error) { XtAddCallback((Widget)iw, XmNvalueChangedCallback, UndoError, NULL); XtAddCallback((Widget)iw, XmNfocusCallback, UndoError, NULL); XtAddCallback((Widget)iw, XmNactivateCallback, UndoError, NULL); iw->input_field.error_state = True; } } #if NeedFunctionPrototypes static Boolean SetValueOnSymbols(XmtInputFieldWidget iw, StringConst value, Boolean check) #else static Boolean SetValueOnSymbols(iw, value, check) XmtInputFieldWidget iw; StringConst value; Boolean check; #endif { Boolean status = True; /* * set the buffer and target symbols from the value resource. * This will cause the buffer symbol callback to be invoked, so we * set a flag which will make that callback a no-op. * If check is true, then we don't set the buffer symbol if * the target symbol conversion failed. */ if (value == NULL) value = ""; if (iw->input_field.target_symbol) status = XmtSymbolSetTypedValue((Widget) iw, iw->input_field.target_symbol, XtRString, (XtArgVal) value, strlen(value)+1); if ((!check || status) && iw->input_field.buffer_symbol) { iw->input_field.ignore_symbol_notify = True; XmtSymbolSetValue(iw->input_field.buffer_symbol, (XtArgVal)value); iw->input_field.ignore_symbol_notify = False; } return status; } #if NeedFunctionPrototypes static void SetValueFromSymbol(XmtInputFieldWidget iw) #else static void SetValueFromSymbol(iw) XmtInputFieldWidget iw; #endif { String symbol_value; /* * set the value resource from the buffer_symbol */ if (iw->input_field.buffer_symbol) { XmtSymbolGetValue(iw->input_field.buffer_symbol, (XtArgVal *) &symbol_value); if (iw->input_field.input) XtFree(iw->input_field.input); iw->input_field.input = XtNewString(symbol_value); } } /* ARGSUSED */ #if NeedFunctionPrototypes static void SymbolValueChanged(XmtSymbol s, XtPointer tag, XtArgVal value) #else static void SymbolValueChanged(s, tag, value) XmtSymbol s; XtPointer tag; XtArgVal value; #endif { XmtInputFieldWidget iw = (XmtInputFieldWidget)tag; /* * the notify callback invoked when the value in the buffer symbol * changes underneath us. Update the value resource, the display, * and the target symbol. If this procedure is called as a side * effect fo SetValueOnSymbols(), then we do nothing. */ if (iw->input_field.ignore_symbol_notify) return; /* set the value resource, and the text widget displayed value */ SetValueFromSymbol(iw); SetString((Widget)iw, iw->input_field.input); /* set the target symbol value */ if (iw->input_field.target_symbol) XmtSymbolSetTypedValue((Widget) iw, iw->input_field.target_symbol, XtRString, (XtArgVal) iw->input_field.input, strlen(iw->input_field.input)+1); } /* ARGSUSED */ #if NeedFunctionPrototypes static void Activate(Widget text, XtPointer tag, XtPointer data) #else static void Activate(text, tag, data) Widget text; XtPointer tag; XtPointer data; #endif { XmtInputFieldWidget iw = (XmtInputFieldWidget) text; XmtInputFieldCallbackStruct verify; String input; int input_length; Boolean status; /* if we're handling an error, don't activate */ if (iw->input_field.error_state) return; /* if nothing has changed since the last time, don't do anything */ if (!iw->input_field.input_changed) return; /* get the current input */ input = XmTextGetString(text); input_length = strlen(input); /* check that input matches all of the pattern error if not */ if (iw->input_field.pattern && iw->input_field.match_all && (input_length != iw->input_field.pattern_length)) goto error; /* call the verify callback; error if it fails. * The programmer may replace input, but should not free or realloc it. * Programmer owns any string used to replace the input. */ verify.input = input; verify.okay = True; XtCallCallbackList((Widget)iw, iw->input_field.verify_callback, (XtPointer)&verify); if (verify.okay == False) goto error; if (verify.input != input) { XtFree(input); input = XtNewString(verify.input); input_length = strlen(input); SetString(text, input); XmTextSetInsertionPosition(text, XmTextGetLastPosition(text)); } /* put the new input into buffer, and target, if specified */ status = SetValueOnSymbols(iw, input, True); if (status == False) goto error; /* re-register the value changed callback */ iw->input_field.input_changed = False; XtAddCallback(text, XmNvalueChangedCallback, ValueChanged, NULL); /* update the input resource */ XtFree(iw->input_field.input); iw->input_field.input = input; /* invoke the input field input callback */ XtCallCallbackList((Widget)iw, iw->input_field.input_callback, input); return; error: HandleError(iw, input); XtFree(input); } /* ARGSUSED */ #if NeedFunctionPrototypes static void handleFocusOut(Widget text, XtPointer tag, XtPointer data) #else static void handleFocusOut(text, tag, data) Widget text; XtPointer tag; XtPointer data; #endif { XmtInputFieldWidget iw = (XmtInputFieldWidget) text; /* * Turn off traversal if applicable. */ if (iw->input_field.notTraversable) { XtVaSetValues(text, XmNtraversalOn, False, NULL); } switch (iw->input_field.focus_out_action) { default: /* default is Activate */ case XmtINPUT_COMMIT: Activate(text, tag, data); break; case XmtINPUT_CANCEL: Cancel(text, NULL, NULL, 0); break; case XmtINPUT_NONE: break; } } #if NeedFunctionPrototypes static void BindBufferSymbol(XmtInputFieldWidget iw) #else static void BindBufferSymbol(iw) XmtInputFieldWidget iw; #endif { if (iw->input_field.buffer_symbol_name) { iw->input_field.buffer_symbol = XmtLookupSymbol(iw->input_field.buffer_symbol_name); if (iw->input_field.buffer_symbol) { if (strcmp(XmtSymbolType(iw->input_field.buffer_symbol), XmtRBuffer) != 0) { XmtWarningMsg("XmtInputField", "symbolType", "%s: buffer symbol '%s' has wrong type;\n\tshould be XmtRBuffer.", XtName((Widget)iw), iw->input_field.buffer_symbol_name); iw->input_field.buffer_symbol = NULL; } else { /* a defined symbol of the right type */ XmtSymbolAddCallback(iw->input_field.buffer_symbol, SymbolValueChanged, (XtPointer)iw); } } else { XmtWarningMsg("XmtInputField", "badBSymbol", "%s: buffer symbol '%s' is undefined.", XtName((Widget)iw), iw->input_field.buffer_symbol_name); } } else iw->input_field.buffer_symbol = NULL; } #if NeedFunctionPrototypes static void ReleaseBufferSymbol(XmtInputFieldWidget iw) #else static void ReleaseBufferSymbol(iw) XmtInputFieldWidget iw; #endif { if (iw->input_field.buffer_symbol) { XmtSymbolRemoveCallback(iw->input_field.buffer_symbol, SymbolValueChanged, (XtPointer)iw); iw->input_field.buffer_symbol = NULL; } } #if NeedFunctionPrototypes static void BindTargetSymbol(XmtInputFieldWidget iw) #else static void BindTargetSymbol(iw) XmtInputFieldWidget iw; #endif { if (iw->input_field.target_symbol_name) { iw->input_field.target_symbol = XmtLookupSymbol(iw->input_field.target_symbol_name); if (!iw->input_field.target_symbol) XmtWarningMsg("XmtInputField", "badTSymbol", "%s: target symbol '%s' is undefined.", XtName((Widget)iw), iw->input_field.target_symbol_name); } else iw->input_field.target_symbol = NULL; } #if NeedFunctionPrototypes static void ReleaseTargetSymbol(XmtInputFieldWidget iw) #else static void ReleaseTargetSymbol(iw) XmtInputFieldWidget iw; #endif { if (iw->input_field.target_symbol) iw->input_field.target_symbol = NULL; } /* ARGSUSED */ #if NeedFunctionPrototypes static void Overstrike(Widget w, XEvent * e, String *args, Cardinal *num) #else static void Overstrike(w, e, args, num) Widget w; XEvent *e; String *args; Cardinal *num; #endif { XmtInputFieldWidget iw = (XmtInputFieldWidget)w; Boolean overstrike; if (*num == 1) { if (args[0][1] == 'n') /* "On" */ overstrike = True; else overstrike = False; } else overstrike = !iw->input_field.overstrike; XtVaSetValues(w, XmtNoverstrike, overstrike, NULL); } /* * About to do a grab-focus(). If the text widget is not traversable but is * editable, turn on traversal (until focus is lost). */ /* ARGSUSED */ #if NeedFunctionPrototypes static void GrabFocus(Widget text, XEvent * e, String *args, Cardinal *num) #else static void GrabFocus(text, e, args, num) Widget text; XEvent *e; String *args; Cardinal *num; #endif { XmtInputFieldWidget iw = (XmtInputFieldWidget) text; /* * If the widget is not traversable, make it so for the duration * (until focus out). */ if (iw->input_field.notTraversable && iw->text.editable) { XtVaSetValues(text, XmNtraversalOn, True, NULL); iw->input_field.notTraversable = 1; /* changed by SetValues */ } /* * Invoke our superclass' grab-focus. */ if (xmtInputFieldClassRec.input_field_class.superclassGrabFocus != NULL) xmtInputFieldClassRec.input_field_class.superclassGrabFocus(text, e, args, num); } /* ARGSUSED */ #if NeedFunctionPrototypes static void Cancel(Widget w, XEvent * e, String *args, Cardinal *num) #else static void Cancel(w, e, args, num) Widget w; XEvent *e; String *args; Cardinal *num; #endif { XmtInputFieldWidget iw = (XmtInputFieldWidget)w; XmtInputFieldCallbackStruct cancel; char * input; /* if we're handling an error, don't activate */ if (iw->input_field.error_state) return; /* if nothing has changed since the last time, don't do anything */ if (!iw->input_field.input_changed) return; input = XmTextGetString(w); cancel.input = input; cancel.okay = False; XtCallCallbackList((Widget)iw, iw->input_field.cancel_callback, (XtPointer) &cancel); XtFree(input); if (cancel.okay == True) return; RestoreOldValue(iw); } #if NeedFunctionPrototypes static void ClassInitialize(void) #else static void ClassInitialize() #endif { static String names[] = { "CANCEL", "COMMIT", "Cancel", "Commit", "NONE", "None", "cancel", "commit", "none" }; static int values[] = { XmtINPUT_CANCEL, XmtINPUT_COMMIT, XmtINPUT_CANCEL, XmtINPUT_COMMIT, XmtINPUT_NONE, XmtINPUT_NONE, XmtINPUT_CANCEL, XmtINPUT_COMMIT, XmtINPUT_NONE }; static String prefixes[] = { "Xmt", "INPUT_", "Input_", "input_", NULL }; XtActionList list; Cardinal count; int i; XmtRegisterStringListConverter(); XmtRegisterEnumConverter(XmtRXmtInputFieldActions, names, values, XtNumber(names), prefixes); /* * Find GrabFocus in parent. */ XtGetActionList((WidgetClass) Superclass, &list, &count); for (i = 0; i < count; i++) { if (strcmp(list[i].string, "grab-focus") == 0) { xmtInputFieldClassRec.input_field_class.superclassGrabFocus = list[i].proc; } } } /* ARGSUSED */ #if NeedFunctionPrototypes static void Initialize(Widget request, Widget init, ArgList arglist, Cardinal *num_args) #else static void Initialize(request, init, arglist, num_args) Widget request; Widget init; ArgList arglist; Cardinal *num_args; #endif { XmtInputFieldWidget iw = (XmtInputFieldWidget) init; #ifdef DISABLE_INPUTFIELD_DRAGDROP Arg args[1]; int n; #endif /* * copy the symbol names, and bind the symbols, if defined */ if (iw->input_field.buffer_symbol_name) iw->input_field.buffer_symbol_name = XtNewString(iw->input_field.buffer_symbol_name); BindBufferSymbol(iw); if (iw->input_field.target_symbol_name) iw->input_field.target_symbol_name = XtNewString(iw->input_field.target_symbol_name); BindTargetSymbol(iw); /* * figure out the initial value. * if value is specified, copy it. * also, store it in buffer and target if those are specified. * otherwise, buffer contains the initial value, and we make * a copy of it for the value resource. In this case target is not set. */ if (iw->input_field.input) { iw->input_field.input = XtNewString(iw->input_field.input); (void)SetValueOnSymbols(iw, iw->input_field.input, False); } else if (iw->input_field.buffer_symbol) SetValueFromSymbol(iw); /* if there is a pattern, get its length */ if (iw->input_field.pattern) iw->input_field.pattern_length = strlen(iw->input_field.pattern); else iw->input_field.pattern_length = 0; /* * figure out the maximum length of the input string. This value can * come from 3 sources: * the XmNmaxLength resource * the length of the pattern resource * the size of the buffer. */ if (iw->text.max_length != MAXINT) iw->input_field.max_length = iw->text.max_length; else if (iw->input_field.pattern) iw->input_field.max_length = iw->input_field.pattern_length; else if (iw->input_field.buffer_symbol) iw->input_field.max_length = XmtSymbolSize(iw->input_field.buffer_symbol); else iw->input_field.max_length = MAXINT; XmTextSetMaxLength(init, iw->input_field.overstrike?MAXINT: iw->input_field.max_length); /* * if we're insensitve, set layoutSensitive, in case we're a child * of a layout and have a caption */ if (!iw->core.sensitive || !iw->core.ancestor_sensitive) XtVaSetValues(init, XmtNlayoutSensitive, False, NULL); /* set initial value, if any */ if (iw->input_field.input) SetString(init, iw->input_field.input); XtAddCallback(init, XmNactivateCallback, Activate, NULL); XtAddCallback(init, XmNlosingFocusCallback, handleFocusOut, NULL); XtAddCallback(init, XmNvalueChangedCallback, ValueChanged, NULL); /* if we're in overstrike mode, add the callback */ if (iw->input_field.overstrike) XtAddCallback(init, XmNmodifyVerifyCallback, HandleOverstrike, NULL); /* if there's a pattern, add the callbacks to handle it */ if (iw->input_field.pattern) { XtAddCallback(init, XmNmodifyVerifyCallback, HandlePattern, NULL); } /* * initialize private fields that need it */ iw->input_field.input_changed = False; iw->input_field.pattern_skip = 0; iw->input_field.error_state = False; iw->input_field.ignore_symbol_notify = False; iw->input_field.notTraversable = !iw->primitive.traversal_on; #ifdef NUTCRACKER_BUG_WORKAROUND iw->input_field.sensitive_background = iw->core.background_pixel; #endif #ifdef DISABLE_INPUTFIELD_DRAGDROP n = 0; XtSetArg(args[n], XmNdropSiteActivity, XmDROP_SITE_INACTIVE); n++; XmDropSiteUpdate((Widget) iw, args, n); #endif } #if NeedFunctionPrototypes static void Destroy(Widget w) #else static void Destroy(w) Widget w; #endif { XmtInputFieldWidget iw = (XmtInputFieldWidget) w; XtFree(iw->input_field.input); XtFree(iw->input_field.pattern); /* release the symbols, if bound, and free the symbol names */ if (iw->input_field.buffer_symbol_name) { ReleaseBufferSymbol(iw); XtFree(iw->input_field.buffer_symbol_name); } if (iw->input_field.target_symbol_name) { ReleaseTargetSymbol(iw); XtFree(iw->input_field.target_symbol_name); } } /* ARGSUSED */ #if NeedFunctionPrototypes static Boolean SetValues(Widget current, Widget request, Widget set, ArgList arglist, Cardinal *num_args) #else static Boolean SetValues(current, request, set, arglist, num_args) Widget current; Widget request; Widget set; ArgList arglist; Cardinal *num_args; #endif { XmtInputFieldWidget cw = (XmtInputFieldWidget) current; XmtInputFieldWidget rw = (XmtInputFieldWidget) request; XmtInputFieldWidget sw = (XmtInputFieldWidget) set; #define Current(field) (cw->input_field.field) #define Request(field) (rw->input_field.field) #define Set(field) (sw->input_field.field) #define Changed(field) (rw->input_field.field != cw->input_field.field) /* * handle changes to the buffer or target symbol names */ if (Changed(buffer_symbol_name)) { /* XXX do something with the new value in the symbol? */ if (Current(buffer_symbol_name)) { XtFree(Current(buffer_symbol_name)); ReleaseBufferSymbol(cw); } if (Set(buffer_symbol_name)) { Set(buffer_symbol_name) = XtNewString(Set(buffer_symbol_name)); BindBufferSymbol(sw); } } if (Changed(target_symbol_name)) { if (Current(target_symbol_name)) { XtFree(Current(target_symbol_name)); ReleaseTargetSymbol(cw); } if (Set(target_symbol_name)) { Set(target_symbol_name) = XtNewString(Set(target_symbol_name)); BindTargetSymbol(sw); } } /* * if the value resource changes, copy the new value, set it on the * text widget, and copy it to the buffer, and target, if any. Note * that we do not call the verifyCallback, nor do we handle errors. * Note that changing value updates the buffer, but changing the buffer * does not change the value. */ if (Changed(input)) { XtFree(Current(input)); if (Set(input)) Set(input) = XtNewString(Set(input)); SetString(set, Set(input)); (void)SetValueOnSymbols(sw, Set(input), False); } if (Changed(overstrike)) { if (Set(overstrike)) XtAddCallback(set, XmNmodifyVerifyCallback, HandleOverstrike, NULL); else XtRemoveCallback(set, XmNmodifyVerifyCallback, HandleOverstrike, NULL); } /* if pattern changed, free old, copy new, and adjust callbacks */ if (Changed(pattern)) { /* if the old pattern was NULL, add the callbacks, else free */ if (Current(pattern) == NULL) { XtAddCallback(set, XmNmodifyVerifyCallback, HandlePattern, NULL); } else XtFree(Current(pattern)); /* if the new pattern is NULL, remove the callbacks, else copy */ if (Set(pattern) == NULL) { XtRemoveCallback(set, XmNmodifyVerifyCallback,HandlePattern, NULL); } else Set(pattern) = XtNewString(Set(pattern)); /* get the new size of the pattern */ if (Set(pattern)) Set(pattern_length) = strlen(Set(pattern)); else Set(pattern_length) = 0; } /* * update max_length from pattern or buffer size */ if (sw->text.max_length != Current(max_length)) Set(max_length) = sw->text.max_length; else if (Changed(pattern)) Set(max_length) = Set(pattern_length); else if (Changed(buffer_symbol) && Set(buffer_symbol)) Set(max_length) = XmtSymbolSize(Set(buffer_symbol)); /* * if max_length or overstrike changed, * update the internal widget */ if (Changed(max_length) || Changed(overstrike)) XmTextSetMaxLength(set, Set(overstrike)?MAXINT:Set(max_length)); #ifdef NUTCRACKER_BUG_WORKAROUND /* If the background changed, save it - but only if we're not already in * SetValues. */ if (sw->core.background_pixel != cw->core.background_pixel && !sw->text.in_setvalues) { sw->input_field.sensitive_background = sw->core.background_pixel; } #endif /* * if sensitivity changes, set the layoutSensitive resource, in case * we're a child of XmtLayout, and have a caption. */ if ((sw->core.sensitive != cw->core.sensitive) || (sw->core.ancestor_sensitive != cw->core.ancestor_sensitive)) { XtVaSetValues(set, XmtNlayoutSensitive, sw->core.sensitive && sw->core.ancestor_sensitive, NULL); } /* * Get new traversalOn state. */ Set(notTraversable) = !sw->primitive.traversal_on; #ifdef NUTCRACKER_BUG_WORKAROUND if (sw->core.sensitive && sw->core.ancestor_sensitive) { if (sw->core.background_pixel != sw->input_field.sensitive_background) { XtVaSetValues((Widget) sw, XmNbackground, sw->input_field.sensitive_background, NULL); } } else { /* Save the *old* background pixel, not the new. WINTIF munches that too. */ sw->input_field.sensitive_background = cw->core.background_pixel; } #endif /* never need to redraw */ return False; #undef Current #undef Request #undef Set #undef Changed } #if NeedFunctionPrototypes Widget XmtCreateInputField(Widget parent, StringConst name, ArgList args, Cardinal n) #else Widget XmtCreateInputField(parent, name, args, n) Widget parent; StringConst name; ArgList args; Cardinal n; #endif { return XtCreateWidget((String)name, xmtInputFieldWidgetClass, parent, args, n); } /* * returns a string that must not be modified or freed by the caller */ #if NeedFunctionPrototypes String XmtInputFieldGetString(Widget w) #else String XmtInputFieldGetString(w) Widget w; #endif { XmtInputFieldWidget iw = (XmtInputFieldWidget) w; XmtAssertWidgetClass(w, xmtInputFieldWidgetClass,"XmtInputFieldGetString"); return iw->input_field.input; } #if NeedFunctionPrototypes void XmtInputFieldSetString(Widget w, StringConst s) #else void XmtInputFieldSetString(w, s) Widget w; StringConst s; #endif { XmtInputFieldWidget iw = (XmtInputFieldWidget) w; XmtAssertWidgetClass(w, xmtInputFieldWidgetClass,"XmtInputFieldSetString"); if (iw->input_field.input) XtFree(iw->input_field.input); if (s) iw->input_field.input = XtNewString(s); else iw->input_field.input = NULL; SetString((Widget)iw, (String)s); (void) SetValueOnSymbols(iw, s, False); } /* ARGSUSED */ #if NeedFunctionPrototypes static void setvalue(Widget w, XtPointer address, XrmQuark type, Cardinal size) #else static void setvalue(w, address, type, size) Widget w; XtPointer address; XrmQuark type; Cardinal size; #endif { char buf[100]; if (type == XmtQString) XmtInputFieldSetString(w, *(String *)address); else if (type == XmtQBuffer) XmtInputFieldSetString(w, (char *)address); else if ((type == XmtQShort) || (type == XmtQPosition)) { (void)sprintf(buf, "%d", *(short *)address); XmtInputFieldSetString(w, buf); } else if (type == XmtQDimension) { (void)sprintf(buf, "%u", *(unsigned short *)address); XmtInputFieldSetString(w, buf); } else if (type == XmtQInt) { (void)sprintf(buf, "%d", *(int *)address); XmtInputFieldSetString(w, buf); } else if (type == XmtQCardinal) { (void)sprintf(buf, "%u", *(unsigned int *)address); XmtInputFieldSetString(w, buf); } else if (type == XmtQFloat) { (void)sprintf(buf, "%g", *(float *)address); XmtInputFieldSetString(w, buf); } else if (type == XmtQDouble) { (void)sprintf(buf, "%g", *(double *)address); XmtInputFieldSetString(w, buf); } else XmtWarningMsg("XmtInputField", "setvalue", "Type mismatch:\n\tCan't set value from resource of type '%s'. String or Buffer expected.", XrmQuarkToString(type)); } #if NeedFunctionPrototypes static void getvalue(Widget w, XtPointer address, XrmQuark type, Cardinal size) #else static void getvalue(w, address, type, size) Widget w; XtPointer address; XrmQuark type; Cardinal size; #endif { String s = XmtInputFieldGetString(w); if (type == XmtQString) *(String *)address = s; else if (type == XmtQBuffer) { int len = strlen(s); strncpy(address, s, size-1); ((char *)address)[size-1] = '\0'; if (len >= size) XmtWarningMsg("XmtInputField", "trunc", "The input value is %d characters long\n\tand does not fit into a buffer %d characters long.\n\tThe trailing characters have been truncated.", len+1, size); } /* XXX * unsigned values should be handled better here. * the XmtInputField should also probably have resources to * set that will automatically verify that the value is valid. */ else if ((type == XmtQShort) || (type == XmtQPosition) || (type == XmtQDimension)) *(short *)address = atoi(s); else if ((type == XmtQInt) || (type == XmtQCardinal)) *(int *)address = atoi(s); else if (type == XmtQFloat) *(float *)address = atof(s); else if (type == XmtQDouble) *(double *)address = atof(s); else XmtWarningMsg("XmtInputField", "getvalue", "Type mismatch:\n\tCan't set input value on a resource of type '%s'. String or Buffer expected.", XrmQuarkToString(type)); } static XmtWidgetType inputfield_widget = { "XmtInputField", (WidgetClass) &xmtInputFieldClassRec, NULL, setvalue, getvalue, }; #if NeedFunctionPrototypes void XmtRegisterInputField(void) #else void XmtRegisterInputField() #endif { _XmtInitQuarks(); XmtRegisterWidgetTypes(&inputfield_widget, 1); }