/* check_passwd() −− handle the input of a password. */
check_passwd(text_w, client_data, call_data)
Widget text_w;
XtPointer client_data;
XtPointer call_data;
char *new;
int len;
XmTextVerifyCallbackStruct *cbs =
(XmTextVerifyCallbackStruct *) call_data;
if (cbs−>reason == XmCR_ACTIVATE) {
printf ("Password: %s0, passwd);
if (cbs−>startPos < cbs−>currInsert) { /* backspace */
cbs−>endPos = strlen (passwd); /* delete from here to end */
passwd[cbs−>startPos] = 0; /* backspace−−terminate */
if (cbs−>text−>length > 1) {
cbs−>doit = False; /* don't allow "paste" operations */
return; /* make the user *type* the password! */
new = XtMalloc (cbs−>endPos + 2); /* new char + NULL terminator */
if (passwd) {
strcpy (new, passwd);
XtFree (passwd);
} else
new[0] = NULL;
passwd = new;
strncat (passwd, cbs−>text−>ptr, cbs−>text−>length);
passwd[cbs−>endPos + cbs−>text−>length] = 0;
for (len = 0; len < cbs−>text−>length; len++)
cbs−>text−>ptr[len] = '*';
As you can see in the figure, the Text widget only displays asterisks, no matter what the user has typed.
Output of password.c
We use the check_passwd() function for both the XmNactivateCallback and the
XmNmodifyVerifyCallback callbacks. When the user presses RETURN, the routine prints what has been typed
to stdout. If the user is not backspacing through the text, we know we can add the new text to passwd, which is
15 Text Widgets 15.5.2 Text Modification Callbacks
the internal variable we use to store the text. Once the new text has been copied, we convert it into asterisks, so that
the user cannot see what has been typed.
We need to handle two different cases for deletion. If the insertion cursor is at the end of the typed string and the user
backspaces, we simply allow the action. If the user clicks somewhere in the middle of the string and then backspaces,
we delete all of the characters from that point in the string to the end, since the user cannot see the characters that he is
To handle the different forms of text deletion, we test to see if startPos is less than currInsert. Since
startPos and endPos specify the range of text that is being deleted, we can change these values and effectively
delete more text than the user originally intended. By setting endPos to the string length of the internal variable
passwd, we handle both of the cases that we just described. If we had wanted to, we could also have set startPos
to 0 and deleted all of the text. We can expand on the ZIP code example that we used for filtering non−digits from
typed input by providing an input field for an area code and phone number. The format for a US phone number is as
We want to filter out all non−digits for a phone number, but we also want to add the dash character () automatically
as it is needed. For example, after the user enters three digits, the Text widget should automatically insert a dash, so
that the next character expected from the user is still a digit. Similarly, when the user backspaces and deletes a dash
character, the widget should delete the preceding digit as well. shows how the interaction should work.
tab(@), linesize(2); l | l n | n. User Types@Text Widget Displays
4@4 1@41 5@415− 4@415−4 T{ BACKSPACE T}@415− T{ BACKSPACE T}@41
_ We can continue to use the same type of algorithm that we used in check_zip() to filter digits, and we can use
some of the code from check_passwd() to handle backspacing. The only remaining problem is adding the
necessary dash characters. Since we are using US phone numbers, we know that the dashes should occur after the
third and seventh digits. Therefore, when currInsert is either 2 or 6, the new digit should be added first, followed
by the dash. the source code shows the program that implements this functionality. XtSetLanguageProc() is
only available in X11R5; there is no corresponding function in X11R4.
/* prompt_phone.c −− a complex problem for XmNmodifyVerifyCallback.
* Prompt for a phone number by filtering digits only from input.
* Don't allow paste operations and handle backspacing.
#include <Xm/Text.h>
#include <Xm/LabelG.h>
#include <Xm/RowColumn.h>
#include <ctype.h>
void check_phone();
main(argc, argv)
int argc;
char *argv[];
Widget toplevel, text_w, rowcol;
XtAppContext app;
XtSetLanguageProc (NULL, NULL, NULL);
toplevel = XtVaAppInitialize (&app, "Demos",
NULL, 0, &argc, argv, NULL, NULL);
15 Text Widgets 15.5.2 Text Modification Callbacks
rowcol = XtVaCreateWidget ("rowcol",
xmRowColumnWidgetClass, toplevel,
XmNorientation, XmHORIZONTAL,
XtVaCreateManagedWidget ("Phone Number:",
xmLabelGadgetClass, rowcol, NULL);
text_w = XtVaCreateManagedWidget ("text_w",
xmTextWidgetClass, rowcol, NULL);
XtAddCallback (text_w, XmNmodifyVerifyCallback, check_phone, NULL);
XtManageChild (rowcol);
XtRealizeWidget (toplevel);
XtAppMainLoop (app);
/* check_phone() −− handle phone number input. */
check_phone(text_w, client_data, call_data)
Widget text_w;
XtPointer client_data;
XtPointer call_data;
char c;
int len = XmTextGetLastPosition(text_w);
XmTextVerifyCallbackStruct *cbs =
(XmTextVerifyCallbackStruct *) call_data;
/* no backspacing or typing in the middle of string */
if (cbs−>currInsert < len) {
cbs−>doit = False;
if (cbs−>text−>length == 0) { /* backspace */
if (cbs−>startPos == 3 || cbs−>startPos == 7)
cbs−>startPos−−; /* delete the hyphen too */
if (cbs−>text−>length > 1) { /* don't allow clipboard copies */
cbs−>doit = False;
/* don't allow non−digits or let the input exceed 12 chars */
if (!isdigit (c = cbs−>text−>ptr[0]) || len >= 12)
cbs−>doit = False;
else if (len == 2 || len == 6) {
cbs−>text−>ptr = XtRealloc (cbs−>text−>ptr, 2);
cbs−>text−>length = 2;
cbs−>text−>ptr[0] = c;
cbs−>text−>ptr[1] = '−';
There are a couple of ways that you could think to add the dashes. One way would be to use the
XmNvalueChangedCallback to keep track of the phone number after it has been entered and then use
15 Text Widgets 15.5.2 Text Modification Callbacks

Get Volume 6A: Motif Programming Manual now with the O’Reilly learning platform.

O’Reilly members experience live online training, plus books, videos, and digital content from nearly 200 publishers.