Don't allow traversal to insensitive widgets.
[nedit.git] / util / misc.c
blob14db19f60616d7ed1a21a3f828c6261a3644d7e1
1 static const char CVSID[] = "$Id: misc.c,v 1.15 2001/04/03 01:42:10 tringali Exp $";
2 /*******************************************************************************
3 * *
4 * misc.c -- Miscelaneous Motif convenience functions *
5 * *
6 * Copyright (C) 1999 Mark Edel *
7 * *
8 * This is free software; you can redistribute it and/or modify it under the *
9 * terms of the GNU General Public License as published by the Free Software *
10 * Foundation; either version 2 of the License, or (at your option) any later *
11 * version. *
12 * *
13 * This software is distributed in the hope that it will be useful, but WITHOUT *
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License *
16 * for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License along with *
19 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
20 * Place, Suite 330, Boston, MA 02111-1307 USA *
21 * *
22 * Nirvana Text Editor *
23 * July 28, 1992 *
24 * *
25 * Written by Mark Edel *
26 * *
27 *******************************************************************************/
28 #include <stdlib.h>
29 #include <stdarg.h>
30 #include <ctype.h>
31 #include <math.h>
32 #include <stdio.h>
33 #include <time.h>
34 #ifdef VMS
35 #include <types.h>
36 #include <unixio.h>
37 #include <file.h>
38 #endif /*VMS*/
39 #include <X11/Intrinsic.h>
40 #include <X11/Xatom.h>
41 #include <X11/keysym.h>
42 #include <Xm/Xm.h>
43 #include <Xm/Label.h>
44 #include <Xm/LabelG.h>
45 #include <Xm/ToggleB.h>
46 #include <Xm/PushB.h>
47 #include <Xm/Separator.h>
48 #include <Xm/RowColumn.h>
49 #include <Xm/CascadeB.h>
50 #include <Xm/AtomMgr.h>
51 #include <Xm/Protocols.h>
52 #include <Xm/Text.h>
53 #include <Xm/MessageB.h>
54 #include <Xm/DialogS.h>
55 #include <Xm/SelectioB.h>
56 #include <Xm/Form.h>
57 #include <Xm/FileSB.h>
58 #include "DialogF.h"
59 #include "misc.h"
61 /* math.h on Sun mysteriously excludes strtod and other functions when
62 POSIX compliance is turned on */
63 extern double strtod();
65 /* structure for passing history-recall data to callbacks */
66 typedef struct {
67 char ***list;
68 int *nItems;
69 int index;
70 } histInfo;
72 typedef Widget (*MotifDialogCreationCall)(Widget, String, ArgList, Cardinal);
74 /* Maximum size of a history-recall list. Typically never invoked, since
75 user must first make this many entries in the text field, limited for
76 safety, to the maximum reasonable number of times user can hit up-arrow
77 before carpal tunnel syndrome sets in */
78 #define HISTORY_LIST_TRIM_TO 1000
79 #define HISTORY_LIST_MAX 2000
81 /* Maximum length for a menu accelerator string which can be parsed by
82 parseAccelString (how many modifier keys can you hold down at once?) */
83 #define MAX_ACCEL_LEN 100
85 /* flags to enable/disable delete key remapping and pointer centered dialogs */
86 static int RemapDeleteEnabled = True;
87 static int PointerCenteredDialogsEnabled = False;
89 /* bitmap and mask for waiting (wrist-watch) cursor */
90 #define watch_x_hot 7
91 #define watch_y_hot 7
92 #define watch_width 16
93 #define watch_height 16
94 static unsigned char watch_bits[] = {
95 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07, 0x10, 0x08, 0x08, 0x11,
96 0x04, 0x21, 0x04, 0x21, 0xe4, 0x21, 0x04, 0x20, 0x08, 0x10, 0x10, 0x08,
97 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07
99 #define watch_mask_width 16
100 #define watch_mask_height 16
101 static unsigned char watch_mask_bits[] = {
102 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf8, 0x1f, 0xfc, 0x3f,
103 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfc, 0x3f, 0xf8, 0x1f,
104 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f
107 static void addMnemonicGrabs(Widget addTo, Widget w);
108 static void mnemonicCB(Widget w, XtPointer callData, XKeyEvent *event);
109 static void findAndActivateMnemonic(Widget w, unsigned int keycode);
110 static void addAccelGrabs(Widget topWidget, Widget w);
111 static void addAccelGrab(Widget topWidget, Widget w);
112 static int parseAccelString(char *string, KeySym *keysym,
113 unsigned int *modifiers);
114 static void lockCB(Widget w, XtPointer callData, XEvent *event,
115 Boolean *continueDispatch);
116 static int findAndActivateAccel(Widget w, unsigned int keyCode,
117 unsigned int modifiers, XEvent *event);
118 static void removeWhiteSpace(char *string);
119 static int stripCaseCmp(const char *str1, const char *str2);
120 static void warnHandlerCB(String message);
121 static void passwdCB(Widget w, char * passTxt, XmTextVerifyCallbackStruct
122 *txtVerStr);
123 static void histDestroyCB(Widget w, XtPointer clientData, XtPointer callData);
124 static void histArrowKeyEH(Widget w, XtPointer callData, XEvent *event,
125 Boolean *continueDispatch);
126 static Widget addParentVisArgsAndCall(MotifDialogCreationCall callRoutine,
127 Widget parent, char *name, ArgList arglist, Cardinal argcount);
130 ** Set up closeCB to be called when the user selects close from the
131 ** window menu. The close menu item usually activates f.kill which
132 ** sends a WM_DELETE_WINDOW protocol request for the window.
134 void AddMotifCloseCallback(Widget shell, XtCallbackProc closeCB, void *arg)
136 static Atom wmpAtom, dwAtom = 0;
137 Display *display = XtDisplay(shell);
139 /* deactivate the built in delete response of killing the application */
140 XtVaSetValues(shell, XmNdeleteResponse, XmDO_NOTHING, NULL);
142 /* add a delete window protocol callback instead */
143 if (dwAtom == 0) {
144 wmpAtom = XmInternAtom(display, "WM_PROTOCOLS", TRUE);
145 dwAtom = XmInternAtom(display, "WM_DELETE_WINDOW", TRUE);
147 XmAddProtocolCallback(shell, wmpAtom, dwAtom, closeCB, arg);
151 ** Motif still generates spurrious passive grab warnings on both IBM and SGI
152 ** This routine suppresses them
154 void SuppressPassiveGrabWarnings(void)
156 XtSetWarningHandler(warnHandlerCB);
160 ** This routine kludges around the problem of backspace not being mapped
161 ** correctly when Motif is used between a server with a delete key in
162 ** the traditional typewriter backspace position and a client that
163 ** expects a backspace key in that position. Though there are three
164 ** distinct levels of key re-mapping in effect when a user is running
165 ** a Motif application, none of these is really appropriate or effective
166 ** for eliminating the delete v.s. backspace problem. Our solution is,
167 ** sadly, to eliminate the forward delete functionality of the delete key
168 ** in favor of backwards delete for both keys. So as not to prevent the
169 ** user or the application from applying other translation table re-mapping,
170 ** we apply re-map the key as a post-processing step, applied after widget
171 ** creation. As a result, the re-mapping necessarily becomes embedded
172 ** throughout an application (wherever text widgets are created), and
173 ** within library routines, including the Nirvana utility library. To
174 ** make this remapping optional, the SetDeleteRemap function provides a
175 ** way for an application to turn this functionality on and off. It is
176 ** recommended that applications that use this routine provide an
177 ** application resource called remapDeleteKey so savvy users can get
178 ** their forward delete functionality back.
180 void RemapDeleteKey(Widget w)
182 static XtTranslations table = NULL;
183 static char *translations =
184 "~Shift~Ctrl~Meta~Alt<Key>osfDelete: delete-previous-character()\n";
186 if (RemapDeleteEnabled) {
187 if (table == NULL)
188 table = XtParseTranslationTable(translations);
189 XtOverrideTranslations(w, table);
193 void SetDeleteRemap(int state)
195 RemapDeleteEnabled = state;
199 ** This routine resolves a window manager protocol incompatibility between
200 ** the X toolkit and several popular window managers. Using this in place
201 ** of XtRealizeWidget will realize the window in a way which allows the
202 ** affected window managers to apply their own placement strategy to the
203 ** window, as opposed to forcing the window to a specific location.
205 ** One of the hints in the WM_NORMAL_HINTS protocol, PPlacement, gets set by
206 ** the X toolkit (probably part of the Core or Shell widget) when a shell
207 ** widget is realized to the value stored in the XmNx and XmNy resources of the
208 ** Core widget. While callers can set these values, there is no "unset" value
209 ** for these resources. On systems which are more Motif aware, a PPosition
210 ** hint of 0,0, which is the default for XmNx and XmNy, is interpreted as,
211 ** "place this as if no hints were specified". Unfortunately the fvwm family
212 ** of window managers, which are now some of the most popular, interpret this
213 ** as "place this window at (0,0)". This routine intervenes between the
214 ** realizing and the mapping of the window to remove the inappropriate
215 ** PPlacement hint.
217 void RealizeWithoutForcingPosition(Widget shell)
219 XSizeHints *hints = XAllocSizeHints();
220 long suppliedHints;
221 Boolean mappedWhenManaged;
223 /* Temporarily set value of XmNmappedWhenManaged
224 to stop the window from popping up right away */
225 XtVaGetValues(shell, XmNmappedWhenManaged, &mappedWhenManaged, NULL);
226 XtVaSetValues(shell, XmNmappedWhenManaged, False, NULL);
228 /* Realize the widget in unmapped state */
229 XtRealizeWidget(shell);
231 /* Get rid of the incorrect WMNormal hint */
232 if (XGetWMNormalHints(XtDisplay(shell), XtWindow(shell), hints,
233 &suppliedHints)) {
234 hints->flags &= ~PPosition;
235 XSetWMNormalHints(XtDisplay(shell), XtWindow(shell), hints);
237 XFree(hints);
239 /* Map the widget */
240 XtMapWidget(shell);
242 /* Restore the value of XmNmappedWhenManaged */
243 XtVaSetValues(shell, XmNmappedWhenManaged, mappedWhenManaged, NULL);
247 ** Older X applications and X servers were mostly designed to operate with
248 ** visual class PseudoColor, because older displays were at most 8 bits
249 ** deep. Modern X servers, however, usually support 24 bit depth and other
250 ** color models. Sun (and others?) still sets their default visual to
251 ** 8-bit PseudoColor, because some of their X applications don't work
252 ** properly with the other color models. The problem with PseudoColor, of
253 ** course, is that users run out of colors in the default colormap, and if
254 ** they install additional colormaps for individual applications, colors
255 ** flash and change weirdly when you change your focus from one application
256 ** to another.
258 ** In addition to the poor choice of default, a design flaw in Xt makes it
259 ** impossible even for savvy users to specify the XtNvisual resource to
260 ** switch to a deeper visual. The problem is that the colormap resource is
261 ** processed independently of the visual resource, and usually results in a
262 ** colormap for the default visual rather than for the user-selected one.
264 ** This routine should be called before creating a shell widget, to
265 ** pre-process the visual, depth, and colormap resources, and return the
266 ** proper values for these three resources to be passed to XtAppCreateShell.
267 ** Applications which actually require a particular color model (i.e. for
268 ** doing color table animation or dynamic color assignment) should not use
269 ** this routine.
271 ** Note that a consequence of using the "best" as opposed to the default
272 ** visual is that some color resources are still converted with the default
273 ** visual (particularly *background), and these must be avoided by widgets
274 ** which are allowed to handle any visual.
276 void FindBestVisual(Display *display, char *appName, char *appClass,
277 Visual **visual, int *depth, Colormap *colormap)
279 char rsrcName[256], rsrcClass[256], *valueString, *type, *endPtr;
280 XrmValue value;
281 int screen = DefaultScreen(display);
282 int reqDepth = -1;
283 int reqID = -1;
284 int reqClass = -1;
285 int installColormap = FALSE;
286 int maxDepth, bestClass, bestVisual, nVis, i, j;
287 XVisualInfo visTemplate, *visList = NULL;
288 static Visual *cachedVisual = NULL;
289 static Colormap cachedColormap;
290 static int cachedDepth = 0;
291 int bestClasses[] = {StaticGray, GrayScale, StaticColor, PseudoColor,
292 DirectColor, TrueColor};
294 /* If results have already been computed, just return them */
295 if (cachedVisual != NULL) {
296 *visual = cachedVisual;
297 *depth = cachedDepth;
298 *colormap = cachedColormap;
299 return;
302 /* Read the visualID and installColormap resources for the application.
303 visualID can be specified either as a number (the visual id as
304 shown by xdpyinfo), as a visual class name, or as Best or Default. */
305 sprintf(rsrcName,"%s.%s", appName, "visualID");
306 sprintf(rsrcClass, "%s.%s", appClass, "VisualID");
307 if (XrmGetResource(XtDatabase(display), rsrcName, rsrcClass, &type,
308 &value)) {
309 valueString = value.addr;
310 reqID = (int)strtol(valueString, &endPtr, 0);
311 if (endPtr == valueString) {
312 reqID = -1;
313 if (stripCaseCmp(valueString, "Default"))
314 reqID = DefaultVisual(display, screen)->visualid;
315 else if (stripCaseCmp(valueString, "StaticGray"))
316 reqClass = StaticGray;
317 else if (stripCaseCmp(valueString, "StaticColor"))
318 reqClass = StaticColor;
319 else if (stripCaseCmp(valueString, "TrueColor"))
320 reqClass = TrueColor;
321 else if (stripCaseCmp(valueString, "GrayScale"))
322 reqClass = GrayScale;
323 else if (stripCaseCmp(valueString, "PseudoColor"))
324 reqClass = PseudoColor;
325 else if (stripCaseCmp(valueString, "DirectColor"))
326 reqClass = DirectColor;
327 else if (!stripCaseCmp(valueString, "Best"))
328 fprintf(stderr, "Invalid visualID resource value\n");
331 sprintf(rsrcName,"%s.%s", appName, "installColormap");
332 sprintf(rsrcClass, "%s.%s", appClass, "InstallColormap");
333 if (XrmGetResource(XtDatabase(display), rsrcName, rsrcClass, &type,
334 &value)) {
335 if (stripCaseCmp(value.addr, "Yes") || stripCaseCmp(value.addr, "True"))
336 installColormap = TRUE;
339 /* Generate a list of visuals to consider. (Note, vestigial code for
340 user-requested visual depth is left in, just in case that function
341 might be needed again, but it does nothing) */
342 if (reqID != -1) {
343 visTemplate.visualid = reqID;
344 visList = XGetVisualInfo(display, VisualIDMask, &visTemplate, &nVis);
345 if (visList == NULL)
346 fprintf(stderr, "VisualID resource value not valid\n");
348 if (visList == NULL && reqClass != -1 && reqDepth != -1) {
349 visTemplate.class = reqClass;
350 visTemplate.depth = reqDepth;
351 visList = XGetVisualInfo(display,
352 VisualClassMask | VisualDepthMask, &visTemplate, &nVis);
353 if (visList == NULL)
354 fprintf(stderr, "Visual class/depth combination not available\n");
356 if (visList == NULL && reqClass != -1) {
357 visTemplate.class = reqClass;
358 visList = XGetVisualInfo(display, VisualClassMask, &visTemplate, &nVis);
359 if (visList == NULL)
360 fprintf(stderr,
361 "Visual Class from resource \"visualID\" not available\n");
363 if (visList == NULL && reqDepth != -1) {
364 visTemplate.depth = reqDepth;
365 visTemplate.depth = reqDepth;
366 visList = XGetVisualInfo(display, VisualDepthMask, &visTemplate, &nVis);
367 if (visList == NULL)
368 fprintf(stderr, "Requested visual depth not available\n");
370 if (visList == NULL) {
371 visList = XGetVisualInfo(display, VisualNoMask, &visTemplate, &nVis);
372 if (visList == NULL) {
373 fprintf(stderr, "Internal Error: no visuals available?\n");
374 *visual = DefaultVisual(display, screen);
375 *depth = DefaultDepth(display, screen);
376 *colormap = DefaultColormap(display, screen);
377 return;
381 /* Choose among the visuals in the candidate list. Prefer maximum
382 depth first then matching default, then largest value of bestClass
383 (I'm not sure whether we actually care about class) */
384 maxDepth = 0;
385 bestClass = 0;
386 bestVisual = 0;
387 for (i=0; i < nVis; i++) {
388 if (visList[i].depth > maxDepth) {
389 maxDepth = visList[i].depth;
390 bestClass = 0;
391 bestVisual = i;
393 if (visList[i].depth == maxDepth) {
394 if (visList[i].visual == DefaultVisual(display, screen))
395 bestVisual = i;
396 if (visList[bestVisual].visual != DefaultVisual(display, screen)) {
397 for (j = 0; j < XtNumber(bestClasses); j++) {
398 if (visList[i].class == bestClasses[j] && j > bestClass) {
399 bestClass = j;
400 bestVisual = i;
406 *visual = cachedVisual = visList[bestVisual].visual;
407 *depth = cachedDepth = visList[bestVisual].depth;
409 /* If the chosen visual is not the default, it needs a colormap allocated */
410 if (*visual == DefaultVisual(display, screen) && !installColormap)
411 *colormap = cachedColormap = DefaultColormap(display, screen);
412 else {
413 *colormap = cachedColormap = XCreateColormap(display,
414 RootWindow(display, screen), cachedVisual, AllocNone);
415 XInstallColormap(display, cachedColormap);
417 /* printf("Chose visual with depth %d, class %d, colormap %ld, id 0x%x\n",
418 visList[bestVisual].depth, visList[bestVisual].class,
419 *colormap, cachedVisual->visualid); */
423 ** If you want to use a non-default visual with Motif, shells all have to be
424 ** created with that visual, depth, and colormap, even if the parent has them
425 ** set up properly. Substituting these routines, will append visual args copied
426 ** from the parent widget (CreatePopupMenu and CreatePulldownMenu), or from the
427 ** best visual, obtained via FindBestVisual above (CreateShellWithBestVis).
429 Widget CreateDialogShell(Widget parent, char *name,
430 ArgList arglist, Cardinal argcount)
432 return addParentVisArgsAndCall(XmCreateDialogShell, parent, name, arglist,
433 argcount);
435 Widget CreatePopupMenu(Widget parent, char *name, ArgList arglist,
436 Cardinal argcount)
438 return addParentVisArgsAndCall(XmCreatePopupMenu, parent, name,
439 arglist, argcount);
441 Widget CreatePulldownMenu(Widget parent, char *name,
442 ArgList arglist, Cardinal argcount)
444 return addParentVisArgsAndCall(XmCreatePulldownMenu, parent, name, arglist,
445 argcount);
447 Widget CreatePromptDialog(Widget parent, char *name,
448 ArgList arglist, Cardinal argcount)
450 return addParentVisArgsAndCall(XmCreatePromptDialog, parent, name, arglist,
451 argcount);
453 Widget CreateSelectionDialog(Widget parent, char *name,
454 ArgList arglist, Cardinal argcount)
456 return addParentVisArgsAndCall(XmCreateSelectionDialog, parent, name,
457 arglist, argcount);
459 Widget CreateFormDialog(Widget parent, char *name,
460 ArgList arglist, Cardinal argcount)
462 return addParentVisArgsAndCall(XmCreateFormDialog, parent, name, arglist,
463 argcount);
465 Widget CreateFileSelectionDialog(Widget parent, char *name,
466 ArgList arglist, Cardinal argcount)
468 return addParentVisArgsAndCall(XmCreateFileSelectionDialog, parent, name,
469 arglist, argcount);
471 Widget CreateQuestionDialog(Widget parent, char *name,
472 ArgList arglist, Cardinal argcount)
474 return addParentVisArgsAndCall(XmCreateQuestionDialog, parent, name,
475 arglist, argcount);
477 Widget CreateMessageDialog(Widget parent, char *name,
478 ArgList arglist, Cardinal argcount)
480 return addParentVisArgsAndCall(XmCreateMessageDialog, parent, name,
481 arglist, argcount);
483 Widget CreateErrorDialog(Widget parent, char *name,
484 ArgList arglist, Cardinal argcount)
486 return addParentVisArgsAndCall(XmCreateErrorDialog, parent, name, arglist,
487 argcount);
489 Widget CreateShellWithBestVis(String appName, String appClass,
490 WidgetClass class, Display *display, ArgList args, Cardinal nArgs)
492 Visual *visual;
493 int depth;
494 Colormap colormap;
495 ArgList al;
496 Cardinal ac = nArgs;
497 Widget result;
499 FindBestVisual(display, appName, appClass, &visual, &depth, &colormap);
500 al = (ArgList)XtMalloc(sizeof(Arg) * (nArgs + 3));
501 if (nArgs != 0)
502 memcpy(al, args, sizeof(Arg) * nArgs);
503 XtSetArg(al[ac], XtNvisual, visual); ac++;
504 XtSetArg(al[ac], XtNdepth, depth); ac++;
505 XtSetArg(al[ac], XtNcolormap, colormap); ac++;
506 result = XtAppCreateShell(appName, appClass, class, display, al, ac);
507 XtFree((char *)al);
508 return result;
512 ** Calls one of the Motif widget creation routines, splicing in additional
513 ** arguments for visual, colormap, and depth.
515 static Widget addParentVisArgsAndCall(MotifDialogCreationCall createRoutine,
516 Widget parent, char *name, ArgList arglist, Cardinal argcount)
518 Visual *visual;
519 int depth;
520 Colormap colormap;
521 ArgList al;
522 Cardinal ac = argcount;
523 Widget result;
524 Widget parentShell = parent;
526 /* Find the application/dialog/menu shell at the top of the widget
527 hierarchy, which has the visual resource being used */
528 while (True) {
529 if (XtIsShell(parentShell))
530 break;
531 if (parentShell == NULL) {
532 fprintf(stderr, "failed to find shell\n");
533 exit(1);
535 parentShell = XtParent(parentShell);
538 /* Add the visual, depth, and colormap resources to the argument list */
539 XtVaGetValues(parentShell, XtNvisual, &visual, XtNdepth, &depth,
540 XtNcolormap, &colormap, NULL);
541 al = (ArgList)XtMalloc(sizeof(Arg) * (argcount + 3));
542 if (argcount != 0)
543 memcpy(al, arglist, sizeof(Arg) * argcount);
544 XtSetArg(al[ac], XtNvisual, visual); ac++;
545 XtSetArg(al[ac], XtNdepth, depth); ac++;
546 XtSetArg(al[ac], XtNcolormap, colormap); ac++;
547 result = (*createRoutine)(parent, name, al, ac);
548 XtFree((char *)al);
549 return result;
553 ** ManageDialogCenteredOnPointer is used in place of XtManageChild for
554 ** popping up a dialog to enable the dialog to be centered under the
555 ** mouse pointer. Whether it pops up the dialog centered under the pointer
556 ** or in its default position centered over the parent widget, depends on
557 ** the value set in the SetPointerCenteredDialogs call.
559 void ManageDialogCenteredOnPointer(Widget dialogChild)
561 Widget shell = XtParent(dialogChild);
562 Window root, child;
563 unsigned int mask;
564 unsigned int width, height, borderWidth, depth;
565 int x, y, winX, winY, maxX, maxY;
566 Boolean mappedWhenManaged;
568 /* If this feature is not enabled, just manage the dialog */
569 if (!PointerCenteredDialogsEnabled) {
570 XtManageChild(dialogChild);
571 return;
574 /* Temporarily set value of XmNmappedWhenManaged
575 to stop the dialog from popping up right away */
576 XtVaGetValues(shell, XmNmappedWhenManaged, &mappedWhenManaged, NULL);
577 XtVaSetValues(shell, XmNmappedWhenManaged, False, NULL);
579 /* Manage the dialog */
580 XtManageChild(dialogChild);
582 /* Get the pointer position (x, y) */
583 XQueryPointer(XtDisplay(shell), XtWindow(shell), &root, &child,
584 &x, &y, &winX, &winY, &mask);
586 /* Translate the pointer position (x, y) into a position for the new
587 window that will place the pointer at its center */
588 XGetGeometry(XtDisplay(shell), XtWindow(shell), &root, &winX, &winY,
589 &width, &height, &borderWidth, &depth);
590 width += 2 * borderWidth;
591 height += 2 * borderWidth;
592 x -= width/2;
593 y -= height/2;
595 /* Ensure that the dialog remains on screen */
596 maxX = XtScreen(shell)->width - width;
597 maxY = XtScreen(shell)->height - height;
598 if (x < 0) x = 0;
599 if (x > maxX) x = maxX;
600 if (y < 0) y = 0;
601 if (y > maxY) y = maxY;
603 /* Set desired window position in the DialogShell */
604 XtVaSetValues(shell, XmNx, x, XmNy, y, NULL);
606 /* Map the widget */
607 XtMapWidget(shell);
609 /* Restore the value of XmNmappedWhenManaged */
610 XtVaSetValues(shell, XmNmappedWhenManaged, mappedWhenManaged, NULL);
614 ** Cause dialogs created by libNUtil.a routines (such as DialogF and
615 ** GetNewFilename), and dialogs which use ManageDialogCenteredOnPointer
616 ** to pop up over the pointer (state = True), or pop up in their default
617 ** positions (state = False)
619 void SetPointerCenteredDialogs(int state)
621 PointerCenteredDialogsEnabled = state;
626 ** Raise a window to the top and give it the input focus. Setting input focus
627 ** is important on systems which use explict (rather than pointer) focus.
629 ** The X alternatives XMapRaised, and XSetInputFocus both have problems.
630 ** XMapRaised only gives the window the focus if it was initially not visible,
631 ** and XSetInputFocus sets the input focus, but crashes if the window is not
632 ** visible.
634 ** This routine should also be used in the case where a dialog is popped up and
635 ** subsequent calls to the dialog function use a flag, or the XtIsManaged, to
636 ** decide whether to create a new instance of the dialog, because on slower
637 ** systems, events can intervene while a dialog is still on its way up and its
638 ** window is still invisible, causing a subtle crash potential if
639 ** XSetInputFocus is used.
641 void RaiseShellWindow(Widget shell)
643 RaiseWindow(XtDisplay(shell), XtWindow(shell));
645 void RaiseWindow(Display *display, Window w)
647 XWindowAttributes winAttr;
649 XGetWindowAttributes(display, w, &winAttr);
650 if (winAttr.map_state == IsViewable)
651 XSetInputFocus(display, w, RevertToParent, CurrentTime);
652 XMapRaised(display, w);
656 ** Add a handler for mnemonics in a dialog (Motif currently only handles
657 ** mnemonics in menus) following the example of M.S. Windows. To add
658 ** mnemonics to a dialog, set the XmNmnemonic resource, as you would in
659 ** a menu, on push buttons or toggle buttons, and call this function
660 ** when the dialog is fully constructed. Mnemonics added or changed
661 ** after this call will not be noticed. To add a mnemonic to a text field
662 ** or list, set the XmNmnemonic resource on the appropriate label and set
663 ** the XmNuserData resource of the label to the widget to get the focus
664 ** when the mnemonic is typed.
666 void AddDialogMnemonicHandler(Widget dialog)
668 XtAddEventHandler(dialog, KeyPressMask, False,
669 (XtEventHandler)mnemonicCB, (XtPointer)0);
670 addMnemonicGrabs(dialog, dialog);
674 ** Removes the event handler and key-grabs added by AddDialogMnemonicHandler
676 void RemoveDialogMnemonicHandler(Widget dialog)
678 XtUngrabKey(dialog, AnyKey, Mod1Mask);
679 XtRemoveEventHandler(dialog, KeyPressMask, False,
680 (XtEventHandler)mnemonicCB, (XtPointer)0);
684 ** Patch around Motif's poor handling of menu accelerator keys. Motif
685 ** does not process menu accelerators when the caps lock or num lock
686 ** keys are engaged. To enable accelerators in these cases, call this
687 ** routine with the completed menu bar widget as "topMenuContainer", and
688 ** the top level shell widget as "topWidget". It will add key grabs for
689 ** all of the accelerators it finds in the topMenuContainer menu tree, and
690 ** an event handler which can process dropped accelerator events by (again)
691 ** traversing the menu tree looking for matching accelerators, and invoking
692 ** the appropriate button actions. Any dynamic additions to the menus
693 ** require a call to UpdateAccelLockPatch to add the additional grabs.
694 ** Unfortunately, these grabs can not be removed.
696 void AccelLockBugPatch(Widget topWidget, Widget topMenuContainer)
698 XtAddEventHandler(topWidget, KeyPressMask, False, lockCB, topMenuContainer);
699 addAccelGrabs(topWidget, topMenuContainer);
703 ** Add additional key grabs for new menu items added to the menus, for
704 ** patching around the Motif Caps/Num Lock problem. "topWidget" must be
705 ** the same widget passed in the original call to AccelLockBugPatch.
707 void UpdateAccelLockPatch(Widget topWidget, Widget newButton)
709 addAccelGrab(topWidget, newButton);
713 ** PopDownBugPatch
715 ** Under some circumstances, popping down a dialog and its parent in
716 ** rapid succession causes a crash. This routine delays and
717 ** processs events until receiving a ReparentNotify event.
718 ** (I have no idea why a ReparentNotify event occurs at all, but it does
719 ** mark the point where it is safe to destroy or pop down the parent, and
720 ** it might have something to do with the bug.) There is a failsafe in
721 ** the form of a ~1.5 second timeout in case no ReparentNotify arrives.
722 ** Use this sparingly, only when real crashes are observed, and periodically
723 ** check to make sure that it is still necessary.
725 void PopDownBugPatch(Widget w)
727 time_t stopTime;
729 stopTime = time(NULL) + 1;
730 while (time(NULL) <= stopTime) {
731 XEvent event;
732 XtAppContext context = XtWidgetToApplicationContext(w);
733 XtAppPeekEvent(context, &event);
734 if (event.xany.type == ReparentNotify)
735 return;
736 XtAppProcessEvent(context, XtIMAll);
741 ** Convert a compound string to a C style null terminated string.
742 ** Returned string must be freed by the caller.
744 char *GetXmStringText(XmString fromString)
746 XmStringContext context;
747 char *text, *toPtr, *toString, *fromPtr;
748 XmStringCharSet charset;
749 XmStringDirection direction;
750 Boolean separator;
752 /* Malloc a buffer large enough to hold the string. XmStringLength
753 should always be slightly longer than necessary, but won't be
754 shorter than the equivalent null-terminated string */
755 toString = XtMalloc(XmStringLength(fromString));
757 /* loop over all of the segments in the string, copying each segment
758 into the output string and converting separators into newlines */
759 XmStringInitContext(&context, fromString);
760 toPtr = toString;
761 while (XmStringGetNextSegment(context, &text,
762 &charset, &direction, &separator)) {
763 for (fromPtr=text; *fromPtr!='\0'; fromPtr++)
764 *toPtr++ = *fromPtr;
765 if (separator)
766 *toPtr++ = '\n';
769 /* terminate the string, free the context, and return the string */
770 *toPtr++ = '\0';
771 XmStringFreeContext(context);
772 return toString;
776 ** Get the XFontStruct that corresponds to the default (first) font in
777 ** a Motif font list. Since Motif stores this, it saves us from storing
778 ** it or querying it from the X server.
780 XFontStruct *GetDefaultFontStruct(XmFontList font)
782 XFontStruct *fs;
783 XmFontContext context;
784 XmStringCharSet charset;
786 XmFontListInitFontContext(&context, font);
787 XmFontListGetNextFont(context, &charset, &fs);
788 XmFontListFreeFontContext(context);
789 XtFree(charset);
790 return fs;
794 ** Create a string table suitable for passing to XmList widgets
796 XmString* StringTable(int count, ... )
798 va_list ap;
799 XmString *array;
800 int i;
801 char *str;
803 va_start(ap, count);
804 array = (XmString*)XtMalloc((count+1) * sizeof(XmString));
805 for(i = 0; i < count; i++ ) {
806 str = va_arg(ap, char *);
807 array[i] = XmStringCreateSimple(str);
809 array[i] = (XmString)0;
810 va_end(ap);
811 return(array);
814 void FreeStringTable(XmString *table)
816 int i;
818 for(i = 0; table[i] != 0; i++)
819 XmStringFree(table[i]);
820 XtFree((char *)table);
824 ** Simulate a button press. The purpose of this routine is show users what
825 ** is happening when they take an action with a non-obvious side effect,
826 ** such as when a user double clicks on a list item. The argument is an
827 ** XmPushButton widget to "press"
829 void SimulateButtonPress(Widget widget)
831 XEvent keyEvent;
833 memset((char *)&keyEvent, 0, sizeof(XKeyPressedEvent));
834 keyEvent.type = KeyPress;
835 keyEvent.xkey.serial = 1;
836 keyEvent.xkey.send_event = True;
837 keyEvent.xkey.display = XtDisplay(widget);
838 keyEvent.xkey.window = XtWindow(widget);
840 if (XtIsSubclass(widget, xmGadgetClass))
842 XtCallActionProc(XtParent(widget), "ManagerGadgetSelect",
843 &keyEvent, NULL, 0);
845 else
847 XtCallActionProc(widget, "ArmAndActivate", &keyEvent, NULL, 0);
852 ** Add an item to an already established pull-down or pop-up menu, including
853 ** mnemonics, accelerators and callbacks.
855 Widget AddMenuItem(Widget parent, char *name, char *label,
856 char mnemonic, char *acc, char *accText,
857 XtCallbackProc callback, void *cbArg)
859 Widget button;
860 XmString st1, st2;
862 button = XtVaCreateManagedWidget(name, xmPushButtonWidgetClass, parent,
863 XmNlabelString, st1=XmStringCreateSimple(label),
864 XmNmnemonic, mnemonic,
865 XmNacceleratorText, st2=XmStringCreateSimple(accText),
866 XmNaccelerator, acc, NULL);
867 XtAddCallback(button, XmNactivateCallback, callback, cbArg);
868 XmStringFree(st1);
869 XmStringFree(st2);
870 return button;
874 ** Add a toggle button item to an already established pull-down or pop-up
875 ** menu, including mnemonics, accelerators and callbacks.
877 Widget AddMenuToggle(Widget parent, char *name, char *label,
878 char mnemonic, char *acc, char *accText,
879 XtCallbackProc callback, void *cbArg, int set)
881 Widget button;
882 XmString st1, st2;
884 button = XtVaCreateManagedWidget(name, xmToggleButtonWidgetClass, parent,
885 XmNlabelString, st1=XmStringCreateSimple(label),
886 XmNmnemonic, mnemonic,
887 XmNacceleratorText, st2=XmStringCreateSimple(accText),
888 XmNaccelerator, acc,
889 XmNset, set, NULL);
890 XtAddCallback(button, XmNvalueChangedCallback, callback, cbArg);
891 XmStringFree(st1);
892 XmStringFree(st2);
893 return button;
897 ** Add a separator line to a menu
899 Widget AddMenuSeparator(Widget parent, char *name)
901 Widget button;
903 button = XmCreateSeparator(parent, name, NULL, 0);
904 XtManageChild(button);
905 return button;
909 ** Add a sub-menu to an established pull-down or pop-up menu, including
910 ** mnemonics, accelerators and callbacks. Returns the menu pane of the
911 ** new sub menu.
913 Widget AddSubMenu(Widget parent, char *name, char *label, char mnemonic)
915 Widget menu;
916 XmString st1;
918 menu = CreatePulldownMenu(parent, name, NULL, 0);
919 XtVaCreateManagedWidget(name, xmCascadeButtonWidgetClass, parent,
920 XmNlabelString, st1=XmStringCreateSimple(label),
921 XmNmnemonic, mnemonic,
922 XmNsubMenuId, menu, NULL);
923 XmStringFree(st1);
924 return menu;
928 ** SetIntLabel, SetFloatLabel, SetIntText, SetFloatText
930 ** Set the text of a motif label or text widget to show an integer or
931 ** floating number.
933 void SetIntLabel(Widget label, int value)
935 char labelString[20];
936 XmString s1;
938 sprintf(labelString, "%d", value);
939 s1=XmStringCreateSimple(labelString);
940 XtVaSetValues(label, XmNlabelString, s1, NULL);
941 XmStringFree(s1);
943 void SetFloatLabel(Widget label, double value)
945 char labelString[20];
946 XmString s1;
948 sprintf(labelString, "%g", value);
949 s1=XmStringCreateSimple(labelString);
950 XtVaSetValues(label, XmNlabelString, s1, NULL);
951 XmStringFree(s1);
953 void SetIntText(Widget text, int value)
955 char labelString[20];
957 sprintf(labelString, "%d", value);
958 XmTextSetString(text, labelString);
960 void SetFloatText(Widget text, double value)
962 char labelString[20];
964 sprintf(labelString, "%g", value);
965 XmTextSetString(text, labelString);
969 ** GetIntText, GetFloatText, GetIntTextWarn, GetFloatTextWarn
971 ** Get the text of a motif text widget as an integer or floating point number.
972 ** The functions will return TEXT_READ_OK of the value was read correctly.
973 ** If not, they will return either TEXT_IS_BLANK, or TEXT_NOT_NUMBER. The
974 ** GetIntTextWarn and GetFloatTextWarn will display a dialog warning the
975 ** user that the value could not be read. The argument fieldName is used
976 ** in the dialog to help the user identify where the problem is. Set
977 ** warnBlank to true if a blank field is also considered an error.
979 int GetFloatText(Widget text, double *value)
981 char *strValue, *endPtr;
982 int retVal;
984 strValue = XmTextGetString(text); /* Get Value */
985 removeWhiteSpace(strValue); /* Remove blanks and tabs */
986 *value = strtod(strValue, &endPtr); /* Convert string to double */
987 if (strlen(strValue) == 0) /* String is empty */
988 retVal = TEXT_IS_BLANK;
989 else if (*endPtr != '\0') /* Whole string not parsed */
990 retVal = TEXT_NOT_NUMBER;
991 else
992 retVal = TEXT_READ_OK;
993 XtFree(strValue);
994 return retVal;
997 int GetIntText(Widget text, int *value)
999 char *strValue, *endPtr;
1000 int retVal;
1002 strValue = XmTextGetString(text); /* Get Value */
1003 removeWhiteSpace(strValue); /* Remove blanks and tabs */
1004 *value = strtol(strValue, &endPtr, 10); /* Convert string to long */
1005 if (strlen(strValue) == 0) /* String is empty */
1006 retVal = TEXT_IS_BLANK;
1007 else if (*endPtr != '\0') /* Whole string not parsed */
1008 retVal = TEXT_NOT_NUMBER;
1009 else
1010 retVal = TEXT_READ_OK;
1011 XtFree(strValue);
1012 return retVal;
1015 int GetFloatTextWarn(Widget text, double *value, char *fieldName, int warnBlank)
1017 int result;
1018 char *valueStr;
1020 result = GetFloatText(text, value);
1021 if (result == TEXT_READ_OK || (result == TEXT_IS_BLANK && !warnBlank))
1022 return result;
1023 valueStr = XmTextGetString(text);
1024 if (result == TEXT_IS_BLANK)
1025 DialogF (DF_ERR, text, 1, "Please supply %s value",
1026 "Dismiss", fieldName);
1027 else /* TEXT_NOT_NUMBER */
1028 DialogF (DF_ERR, text, 1,
1029 "Can't read %s value: \"%s\"",
1030 "Dismiss", fieldName, valueStr);
1031 XtFree(valueStr);
1032 return result;
1035 int GetIntTextWarn(Widget text, int *value, char *fieldName, int warnBlank)
1037 int result;
1038 char *valueStr;
1040 result = GetIntText(text, value);
1041 if (result == TEXT_READ_OK || (result == TEXT_IS_BLANK && !warnBlank))
1042 return result;
1043 valueStr = XmTextGetString(text);
1044 if (result == TEXT_IS_BLANK)
1045 DialogF (DF_ERR, text, 1, "Please supply a value for %s",
1046 "Dismiss", fieldName);
1047 else /* TEXT_NOT_NUMBER */
1048 DialogF (DF_ERR, text, 1,
1049 "Can't read integer value \"%s\" in %s",
1050 "Dismiss", valueStr, fieldName);
1051 XtFree(valueStr);
1052 return result;
1055 int TextWidgetIsBlank(Widget textW)
1057 char *str;
1058 int retVal;
1060 str = XmTextGetString(textW);
1061 removeWhiteSpace(str);
1062 retVal = *str == '\0';
1063 XtFree(str);
1064 return retVal;
1068 ** Turn a multi-line editing text widget into a fake single line text area
1069 ** by disabling the translation for Return. This is a way to give users
1070 ** extra space, by allowing wrapping, but still prohibiting newlines.
1071 ** (SINGLE_LINE_EDIT mode can't be used, in this case, because it forces
1072 ** the widget to be one line high).
1074 void MakeSingleLineTextW(Widget textW)
1076 static XtTranslations noReturnTable = NULL;
1077 static char *noReturnTranslations = "<Key>Return: activate()\n";
1079 if (noReturnTable == NULL)
1080 noReturnTable = XtParseTranslationTable(noReturnTranslations);
1081 XtOverrideTranslations(textW, noReturnTable);
1085 ** Add up-arrow/down-arrow recall to a single line text field. When user
1086 ** presses up-arrow, string is cleared and recent entries are presented,
1087 ** moving to older ones as each successive up-arrow is pressed. Down-arrow
1088 ** moves to more recent ones, final down-arrow clears the field. Associated
1089 ** routine, AddToHistoryList, makes maintaining a history list easier.
1091 ** Arguments are the widget, and pointers to the history list and number of
1092 ** items, which are expected to change periodically.
1094 void AddHistoryToTextWidget(Widget textW, char ***historyList, int *nItems)
1096 histInfo *histData;
1098 /* create a data structure for passing history info to the callbacks */
1099 histData = (histInfo *)XtMalloc(sizeof(histInfo));
1100 histData->list = historyList;
1101 histData->nItems = nItems;
1102 histData->index = -1;
1104 /* Add an event handler for handling up/down arrow events */
1105 XtAddEventHandler(textW, KeyPressMask, False,
1106 (XtEventHandler)histArrowKeyEH, histData);
1108 /* Add a destroy callback for freeing history data structure */
1109 XtAddCallback(textW, XmNdestroyCallback, histDestroyCB, histData);
1112 static void histDestroyCB(Widget w, XtPointer clientData, XtPointer callData)
1114 XtFree((char *)clientData);
1117 static void histArrowKeyEH(Widget w, XtPointer callData, XEvent *event,
1118 Boolean *continueDispatch)
1120 histInfo *histData = (histInfo *)callData;
1121 KeySym keysym = XLookupKeysym((XKeyEvent *)event, 0);
1123 /* only process up and down arrow keys */
1124 if (keysym != XK_Up && keysym != XK_Down)
1125 return;
1127 /* increment or decrement the index depending on which arrow was pressed */
1128 histData->index += (keysym == XK_Up) ? 1 : -1;
1130 /* if the index is out of range, beep, fix it up, and return */
1131 if (histData->index < -1) {
1132 histData->index = -1;
1133 XBell(XtDisplay(w), 0);
1134 return;
1136 if (histData->index >= *histData->nItems) {
1137 histData->index = *histData->nItems - 1;
1138 XBell(XtDisplay(w), 0);
1139 return;
1142 /* Change the text field contents */
1143 XmTextSetString(w, histData->index == -1 ? "" :
1144 (*histData->list)[histData->index]);
1148 ** Copies a string on to the end of history list, which may be reallocated
1149 ** to make room. If historyList grows beyond its internally set boundary
1150 ** for size (HISTORY_LIST_MAX), it is trimmed back to a smaller size
1151 ** (HISTORY_LIST_TRIM_TO). Before adding to the list, checks if the item
1152 ** is a duplicate of the last item. If so, it is not added.
1154 void AddToHistoryList(char *newItem, char ***historyList, int *nItems)
1156 char **newList;
1157 int i;
1159 if (*nItems != 0 && !strcmp(newItem, **historyList))
1160 return;
1161 if (*nItems == HISTORY_LIST_MAX) {
1162 for (i=HISTORY_LIST_TRIM_TO; i<HISTORY_LIST_MAX; i++)
1163 XtFree((*historyList)[i]);
1164 *nItems = HISTORY_LIST_TRIM_TO;
1166 newList = (char **)XtMalloc(sizeof(char *) * (*nItems + 1));
1167 for (i=0; i < *nItems; i++)
1168 newList[i+1] = (*historyList)[i];
1169 if (*nItems != 0 && *historyList != NULL)
1170 XtFree((char *)*historyList);
1171 (*nItems)++;
1172 newList[0] = XtNewString(newItem);
1173 *historyList = newList;
1177 * PasswordText - routine to add a callback to any text widget so that all
1178 * text typed by the user is echoed with asterisks, allowing
1179 * a password to be typed in without being seen.
1181 * parameters: w - text widget to add the callback to
1182 * passTxt - pointer to a string created by caller of this routine.
1183 * **NOTE** The length of this string should be one
1184 * greater than the maximum specified by XmNmaxLength.
1185 * This string is set to empty just before the callback
1186 * is added.
1189 void PasswordText(Widget w, char *passTxt)
1191 passTxt[0] = '\0';
1192 XtAddCallback(w, XmNmodifyVerifyCallback, (XtCallbackProc)passwdCB,passTxt);
1196 ** BeginWait/EndWait
1198 ** Display/Remove a watch cursor over topCursorWidget and its descendents
1200 void BeginWait(Widget topCursorWidget)
1202 Display *display = XtDisplay(topCursorWidget);
1203 Pixmap pixmap;
1204 Pixmap maskPixmap;
1205 XColor xcolors[2];
1206 static Cursor waitCursor = 0;
1208 /* if the watch cursor hasn't been created yet, create it */
1209 if (!waitCursor) {
1210 pixmap = XCreateBitmapFromData(display, DefaultRootWindow(display),
1211 (char *)watch_bits, watch_width, watch_height);
1213 maskPixmap = XCreateBitmapFromData(display, DefaultRootWindow(display),
1214 (char *)watch_mask_bits, watch_width, watch_height);
1216 xcolors[0].pixel = BlackPixelOfScreen(DefaultScreenOfDisplay(display));
1217 xcolors[1].pixel = WhitePixelOfScreen(DefaultScreenOfDisplay(display));
1219 XQueryColors(display, DefaultColormapOfScreen(
1220 DefaultScreenOfDisplay(display)), xcolors, 2);
1221 waitCursor = XCreatePixmapCursor(display, pixmap, maskPixmap,
1222 &xcolors[0], &xcolors[1], watch_x_hot, watch_y_hot);
1223 XFreePixmap(display, pixmap);
1224 XFreePixmap(display, maskPixmap);
1227 /* display the cursor */
1228 XDefineCursor(display, XtWindow(topCursorWidget), waitCursor);
1231 void EndWait(Widget topCursorWidget)
1233 XUndefineCursor(XtDisplay(topCursorWidget), XtWindow(topCursorWidget));
1237 ** Create an X window geometry string from width, height, x, and y values.
1238 ** This is a complement to the X routine XParseGeometry, and uses the same
1239 ** bitmask values (XValue, YValue, WidthValue, HeightValue, XNegative, and
1240 ** YNegative) as defined in <X11/Xutil.h> and documented under XParseGeometry.
1241 ** It expects a string of at least MAX_GEOMETRY_STRING_LEN in which to write
1242 ** result. Note that in a geometry string, it is not possible to supply a y
1243 ** position without an x position. Also note that the X/YNegative flags
1244 ** mean "add a '-' and negate the value" which is kind of odd.
1246 void CreateGeometryString(char *string, short x, short y,
1247 short width, short height, int bitmask)
1249 char *ptr = string;
1250 int nChars;
1252 if (bitmask & WidthValue) {
1253 sprintf(ptr, "%d%n", width, &nChars);
1254 ptr += nChars;
1256 if (bitmask & HeightValue) {
1257 sprintf(ptr, "x%d%n", height, &nChars);
1258 ptr += nChars;
1260 if (bitmask & XValue) {
1261 if (bitmask & XNegative)
1262 sprintf(ptr, "-%d%n", -x, &nChars);
1263 else
1264 sprintf(ptr, "+%d%n", x, &nChars);
1265 ptr += nChars;
1267 if (bitmask & YValue) {
1268 if (bitmask & YNegative)
1269 sprintf(ptr, "-%d%n", -y, &nChars);
1270 else
1271 sprintf(ptr, "+%d%n", y, &nChars);
1272 ptr += nChars;
1274 *ptr = '\0';
1277 /* */
1278 /* passwdCB: callback routine added by PasswordText routine. This routine */
1279 /* echoes each character typed as an asterisk (*) and a few other */
1280 /* necessary things so that the password typed in is not visible */
1281 /* */
1282 static void passwdCB(Widget w, char * passTxt, XmTextVerifyCallbackStruct
1283 *txtVerStr)
1285 /* XmTextVerifyCallbackStruct: */
1286 /* int reason; should be XmCR_MODIFYING_TEXT_VALUE */
1287 /* XEvent *event; points to XEvent that triggered the callback */
1288 /* Boolean doit; indicates whether action should be performed; setting */
1289 /* this to false negates the action */
1290 /* long currInsert, current position of insert cursor */
1291 /* newInsert; position user attempts to position the insert cursor */
1292 /* long startPos, starting position of the text to modify */
1293 /* endPos; ending position of the text to modify */
1294 /* XmTextBlock text; */
1296 /* XmTextBlock (used to pass text around): */
1297 /* char *ptr; points to text to be inserted */
1298 /* int length; Number of bytes (length) */
1299 /* XmTextFormat format; Representations format */
1301 /* XmTextFormat: either FMT8BIT or FMT16BIT */
1304 int numCharsTyped, i, j, pos;
1306 /* ensure text format is 8-bit characters */
1307 if (txtVerStr->text->format != FMT8BIT)
1308 return;
1310 /* verify assumptions */
1311 /* if (txtVerStr->endPos < txtVerStr->startPos)
1312 fprintf(stderr, "User password callback error: endPos < startPos\n");
1313 if (strlen(passTxt) == 0 && txtVerStr->endPos != 0)
1314 fprintf(stderr, "User password callback error: no txt, but end != 0\n");
1316 printf("passTxt = %s, startPos = %d, endPos = %d, txtBlkAddr = %d\n",
1317 passTxt, txtVerStr->startPos, txtVerStr->endPos, txtVerStr->text);
1318 if (txtVerStr->text != NULL && txtVerStr->text->ptr != NULL)
1319 printf(" string typed = %s, length = %d\n", txtVerStr->text->ptr,
1320 txtVerStr->text->length);
1322 /* If necessary, expand/compress passTxt and insert any new text */
1323 if (txtVerStr->text != NULL && txtVerStr->text->ptr != NULL)
1324 numCharsTyped = txtVerStr->text->length;
1325 else
1326 numCharsTyped = 0;
1327 /* numCharsTyped = # chars to insert (that user typed) */
1328 /* j = # chars to expand (+) or compress (-) the password string */
1329 j = numCharsTyped - (txtVerStr->endPos - txtVerStr->startPos);
1330 if (j > 0) /* expand case: start at ending null */
1331 for (pos = strlen(passTxt) + 1; pos >= txtVerStr->endPos; --pos)
1332 passTxt[pos+j] = passTxt[pos];
1333 if (j < 0) /* compress case */
1334 for (pos = txtVerStr->startPos + numCharsTyped;
1335 pos <= strlen(passTxt)+1; ++pos)
1336 passTxt[pos] = passTxt[pos-j];
1337 /* then copy text to be inserted into passTxt */
1338 for (pos = txtVerStr->startPos, i = 0; i < numCharsTyped; ++i) {
1339 passTxt[pos+i] = *(txtVerStr->text->ptr + i);
1340 /* Replace text typed by user with asterisks (*) */
1341 *(txtVerStr->text->ptr + i) = '*';
1343 /* printf(" Password string now = %s\n", passTxt); */
1347 ** Remove the white space (blanks and tabs) from a string
1349 static void removeWhiteSpace(char *string)
1351 char *outPtr = string;
1353 while (TRUE) {
1354 if (*string == 0) {
1355 *outPtr = 0;
1356 return;
1357 } else if (*string != ' ' && *string != '\t')
1358 *(outPtr++) = *(string++);
1359 else
1360 string++;
1365 ** Compares two strings and return TRUE if the two strings
1366 ** are the same, ignoring whitespace and case differences.
1368 static int stripCaseCmp(const char *str1, const char *str2)
1370 const char *c1, *c2;
1372 for (c1=str1, c2=str2; *c1!='\0' && *c2!='\0'; c1++, c2++) {
1373 while (*c1 == ' ' || *c1 == '\t')
1374 c1++;
1375 while (*c2 == ' ' || *c2 == '\t')
1376 c2++;
1377 if (toupper((unsigned char)*c1) != toupper((unsigned char)*c2))
1378 return FALSE;
1380 return *c1 == '\0' && *c2 == '\0';
1383 static void warnHandlerCB(String message)
1385 if (strstr(message, "XtRemoveGrab"))
1386 return;
1387 if (strstr(message, "Attempt to remove non-existant passive grab"))
1388 return;
1389 fputs(message, stderr);
1390 fputc('\n', stderr);
1394 ** Part of dialog mnemonic processing. Search the widget tree under w
1395 ** for widgets with mnemonics. When found, add a passive grab to the
1396 ** dialog widget for the mnemonic character, thus directing mnemonic
1397 ** events to the dialog widget.
1399 static void addMnemonicGrabs(Widget dialog, Widget w)
1401 char mneString[2];
1402 WidgetList children;
1403 Cardinal numChildren;
1404 int i, isMenu;
1405 KeySym mnemonic = '\0';
1406 unsigned char rowColType;
1407 unsigned int keyCode;
1409 if (XtIsComposite(w)) {
1410 if (XtClass(w) == xmRowColumnWidgetClass) {
1411 XtVaGetValues(w, XmNrowColumnType, &rowColType, NULL);
1412 isMenu = rowColType != XmWORK_AREA;
1413 } else
1414 isMenu = False;
1415 if (!isMenu) {
1416 XtVaGetValues(w, XmNchildren, &children, XmNnumChildren,
1417 &numChildren, NULL);
1418 for (i=0; i<numChildren; i++)
1419 addMnemonicGrabs(dialog, children[i]);
1421 } else {
1422 XtVaGetValues(w, XmNmnemonic, &mnemonic, NULL);
1423 if (mnemonic != '\0') {
1424 mneString[0] = mnemonic; mneString[1] = '\0';
1425 keyCode = XKeysymToKeycode(XtDisplay(dialog),
1426 XStringToKeysym(mneString));
1427 XtGrabKey(dialog, keyCode, Mod1Mask, True,
1428 GrabModeAsync, GrabModeAsync);
1429 XtGrabKey(dialog, keyCode, Mod1Mask | LockMask, True,
1430 GrabModeAsync, GrabModeAsync);
1431 XtGrabKey(dialog, keyCode, Mod1Mask | Mod3Mask, True,
1432 GrabModeAsync, GrabModeAsync);
1433 XtGrabKey(dialog, keyCode, Mod1Mask | LockMask | Mod3Mask, True,
1434 GrabModeAsync, GrabModeAsync);
1440 ** Callback routine for dialog mnemonic processing.
1442 static void mnemonicCB(Widget w, XtPointer callData, XKeyEvent *event)
1444 findAndActivateMnemonic(w, event->keycode);
1448 ** Look for a widget in the widget tree w, with a mnemonic matching
1449 ** keycode. When one is found, simulate a button press on that widget
1450 ** and give it the keyboard focus. If the mnemonic is on a label,
1451 ** look in the userData field of the label to see if it points to
1452 ** another widget, and give that the focus. This routine is just
1453 ** sufficient for NEdit, no doubt it will need to be extended for
1454 ** mnemonics on widgets other than just buttons and text fields.
1456 static void findAndActivateMnemonic(Widget w, unsigned int keycode)
1458 WidgetList children;
1459 Cardinal numChildren;
1460 int i, isMenu;
1461 KeySym mnemonic = '\0';
1462 char mneString[2];
1463 Widget userData;
1464 unsigned char rowColType;
1466 if (XtIsComposite(w)) {
1467 if (XtClass(w) == xmRowColumnWidgetClass) {
1468 XtVaGetValues(w, XmNrowColumnType, &rowColType, NULL);
1469 isMenu = rowColType != XmWORK_AREA;
1470 } else
1471 isMenu = False;
1472 if (!isMenu) {
1473 XtVaGetValues(w, XmNchildren, &children, XmNnumChildren,
1474 &numChildren, NULL);
1475 for (i=0; i<numChildren; i++)
1476 findAndActivateMnemonic(children[i], keycode);
1478 } else {
1479 XtVaGetValues(w, XmNmnemonic, &mnemonic, NULL);
1480 if (mnemonic != '\0') {
1481 mneString[0] = mnemonic; mneString[1] = '\0';
1482 if (XKeysymToKeycode(XtDisplay(XtParent(w)),
1483 XStringToKeysym(mneString)) == keycode) {
1484 if (XtClass(w) == xmLabelWidgetClass ||
1485 XtClass(w) == xmLabelGadgetClass) {
1486 XtVaGetValues(w, XmNuserData, &userData, NULL);
1487 if (userData!=NULL && XtIsWidget(userData) &&
1488 XmIsTraversable(w))
1489 XmProcessTraversal(userData, XmTRAVERSE_CURRENT);
1490 } else if (XmIsTraversable(w)) {
1491 XmProcessTraversal(w, XmTRAVERSE_CURRENT);
1492 SimulateButtonPress(w);
1500 ** Part of workaround for Motif Caps/Num Lock bug. Search the widget tree
1501 ** under w for widgets with accelerators. When found, add three passive
1502 ** grabs to topWidget, one for the accelerator keysym + modifiers + Caps
1503 ** Lock, one for Num Lock, and one for both, thus directing lock +
1504 ** accelerator events to topWidget.
1506 static void addAccelGrabs(Widget topWidget, Widget w)
1508 WidgetList children;
1509 Widget menu;
1510 Cardinal numChildren;
1511 int i;
1513 if (XtIsComposite(w)) {
1514 XtVaGetValues(w, XmNchildren, &children, XmNnumChildren,
1515 &numChildren, NULL);
1516 for (i=0; i<numChildren; i++)
1517 addAccelGrabs(topWidget, children[i]);
1518 } else if (XtClass(w) == xmCascadeButtonWidgetClass) {
1519 XtVaGetValues(w, XmNsubMenuId, &menu, NULL);
1520 if (menu != NULL)
1521 addAccelGrabs(topWidget, menu);
1522 } else
1523 addAccelGrab(topWidget, w);
1527 ** Grabs the key + modifier defined in the widget's accelerator resource,
1528 ** in combination with the Caps Lock and Num Lock accelerators.
1530 static void addAccelGrab(Widget topWidget, Widget w)
1532 char *accelString = NULL;
1533 KeySym keysym;
1534 unsigned int modifiers;
1536 XtVaGetValues(w, XmNaccelerator, &accelString, NULL);
1537 if (accelString == NULL || *accelString == '\0')
1538 return;
1540 if (!parseAccelString(accelString, &keysym, &modifiers))
1541 return;
1542 XtGrabKey(topWidget, XKeysymToKeycode(XtDisplay(topWidget), keysym),
1543 modifiers | LockMask, True, GrabModeAsync, GrabModeAsync);
1544 XtGrabKey(topWidget, XKeysymToKeycode(XtDisplay(topWidget), keysym),
1545 modifiers | Mod3Mask, True, GrabModeAsync, GrabModeAsync);
1546 XtGrabKey(topWidget, XKeysymToKeycode(XtDisplay(topWidget), keysym),
1547 modifiers | LockMask | Mod3Mask, True, GrabModeAsync, GrabModeAsync);
1551 ** Read a Motif accelerator string and translate it into a keysym + modifiers.
1552 ** Returns TRUE if the parse was successful, FALSE, if not.
1554 static int parseAccelString(char *string, KeySym *keySym,
1555 unsigned int *modifiers)
1557 #define N_MODIFIERS 12
1558 /*... Is NumLock always Mod3? */
1559 static char *modifierNames[N_MODIFIERS] = {"Ctrl", "Shift", "Alt", "Mod2",
1560 "Mod4", "Mod5", "Button1", "Button2", "Button3", "Button4",
1561 "Button5"};
1562 static unsigned int modifierMasks[N_MODIFIERS] = {ControlMask, ShiftMask,
1563 Mod1Mask, Mod2Mask, Mod4Mask, Mod5Mask, Button1Mask, Button2Mask,
1564 Button3Mask, Button4Mask, Button5Mask};
1565 char modStr[MAX_ACCEL_LEN];
1566 char evtStr[MAX_ACCEL_LEN];
1567 char keyStr[MAX_ACCEL_LEN];
1568 char *c, *evtStart, *keyStart;
1569 int i;
1571 if (strlen(string) >= MAX_ACCEL_LEN)
1572 return FALSE;
1574 /* Get the modifier part */
1575 for (c = string; *c != '<'; c++)
1576 if (*c == '\0')
1577 return FALSE;
1578 strncpy(modStr, string, c - string);
1579 modStr[c - string] = '\0';
1581 /* Verify the <key> or <keypress> part */
1582 evtStart = c;
1583 for ( ; *c != '>'; c++)
1584 if (*c == '\0')
1585 return FALSE;
1586 c++;
1587 strncpy(evtStr, evtStart, c - evtStart);
1588 evtStr[c - evtStart] = '\0';
1589 if (!stripCaseCmp(evtStr, "<key>") && !stripCaseCmp(evtStr, "<keypress>"))
1590 return FALSE;
1592 /* Get the keysym part */
1593 keyStart = c;
1594 for ( ; *c != '\0'; c++);
1595 strncpy(keyStr, keyStart, c - keyStart);
1596 keyStr[c - keyStart] = '\0';
1597 *keySym = XStringToKeysym(keyStr);
1599 /* Parse the modifier part */
1600 *modifiers = 0;
1601 c = modStr;
1602 while (*c != '\0') {
1603 while (*c == ' ' || *c == '\t')
1604 c++;
1605 if (*c == '\0')
1606 break;
1607 for (i = 0; i < N_MODIFIERS; i++) {
1608 if (!strncmp(c, modifierNames[i], strlen(modifierNames[i]))) {
1609 *modifiers |= modifierMasks[i];
1610 c += strlen(modifierNames[i]);
1611 break;
1614 if (i == N_MODIFIERS)
1615 return FALSE;
1618 return TRUE;
1622 ** Event handler for patching around Motif's lock + accelerator problem.
1623 ** Looks for a menu item in the patched menu hierarchy and invokes its
1624 ** ArmAndActivate action.
1626 static void lockCB(Widget w, XtPointer callData, XEvent *event,
1627 Boolean *continueDispatch)
1629 Widget topMenuWidget = (Widget)callData;
1630 *continueDispatch = TRUE;
1631 if (!(((XKeyEvent *)event)->state & (LockMask | Mod3Mask)))
1632 return;
1634 if (!findAndActivateAccel(topMenuWidget, ((XKeyEvent *)event)->keycode,
1635 ((XKeyEvent *)event)->state & ~(LockMask | Mod3Mask), event))
1636 *continueDispatch = FALSE;
1640 ** Search through menu hierarchy under w and look for a button with
1641 ** accelerator matching keyCode + modifiers, and do its action
1643 static int findAndActivateAccel(Widget w, unsigned int keyCode,
1644 unsigned int modifiers, XEvent *event)
1647 WidgetList children;
1648 Widget menu;
1649 Cardinal numChildren;
1650 int i;
1651 char *accelString = NULL;
1652 KeySym keysym;
1653 unsigned int mods;
1655 if (XtIsComposite(w)) {
1656 XtVaGetValues(w, XmNchildren, &children, XmNnumChildren,
1657 &numChildren, NULL);
1658 for (i=0; i<numChildren; i++)
1659 if (findAndActivateAccel(children[i], keyCode, modifiers, event))
1660 return TRUE;
1661 } else if (XtClass(w) == xmCascadeButtonWidgetClass) {
1662 XtVaGetValues(w, XmNsubMenuId, &menu, NULL);
1663 if (menu != NULL)
1664 if (findAndActivateAccel(menu, keyCode, modifiers, event))
1665 return TRUE;
1666 } else {
1667 XtVaGetValues(w, XmNaccelerator, &accelString, NULL);
1668 if (accelString != NULL && *accelString != '\0') {
1669 if (!parseAccelString(accelString, &keysym, &mods))
1670 return FALSE;
1671 if (keyCode == XKeysymToKeycode(XtDisplay(w), keysym) &&
1672 modifiers == mods) {
1673 if (XtIsSensitive(w)) {
1674 XtCallActionProc(w, "ArmAndActivate", event, NULL, 0);
1675 return TRUE;
1680 return FALSE;