Fix for crash in shared tag allocation on Solaris (compiler bug?)
[nedit.git] / util / misc.c
blob0d95b28c872a0d64de13cf5dde7789a17f044f24
1 static const char CVSID[] = "$Id: misc.c,v 1.27 2001/08/15 09:00:21 amai 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 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 <string.h>
30 #include <stdarg.h>
31 #include <ctype.h>
32 #include <math.h>
33 #include <stdio.h>
34 #include <time.h>
35 #ifdef VMS
36 #include <types.h>
37 #include <unixio.h>
38 #include <file.h>
39 #endif /*VMS*/
40 #include <X11/Intrinsic.h>
41 #include <X11/Xatom.h>
42 #include <X11/keysym.h>
43 #include <X11/keysymdef.h>
44 #include <Xm/Xm.h>
45 #include <Xm/Label.h>
46 #include <Xm/LabelG.h>
47 #include <Xm/ToggleB.h>
48 #include <Xm/PushB.h>
49 #include <Xm/Separator.h>
50 #include <Xm/RowColumn.h>
51 #include <Xm/CascadeB.h>
52 #include <Xm/AtomMgr.h>
53 #include <Xm/Protocols.h>
54 #include <Xm/Text.h>
55 #include <Xm/MessageB.h>
56 #include <Xm/DialogS.h>
57 #include <Xm/SelectioB.h>
58 #include <Xm/Form.h>
59 #include <Xm/FileSB.h>
60 #include "DialogF.h"
61 #include "misc.h"
63 /* math.h on Sun mysteriously excludes strtod and other functions when
64 POSIX compliance is turned on */
65 extern double strtod();
67 /* structure for passing history-recall data to callbacks */
68 typedef struct {
69 char ***list;
70 int *nItems;
71 int index;
72 } histInfo;
74 typedef Widget (*MotifDialogCreationCall)(Widget, String, ArgList, Cardinal);
76 /* Maximum size of a history-recall list. Typically never invoked, since
77 user must first make this many entries in the text field, limited for
78 safety, to the maximum reasonable number of times user can hit up-arrow
79 before carpal tunnel syndrome sets in */
80 #define HISTORY_LIST_TRIM_TO 1000
81 #define HISTORY_LIST_MAX 2000
83 /* flags to enable/disable delete key remapping and pointer centered dialogs */
84 static int RemapDeleteEnabled = True;
85 static int PointerCenteredDialogsEnabled = False;
87 /* bitmap and mask for waiting (wrist-watch) cursor */
88 #define watch_x_hot 7
89 #define watch_y_hot 7
90 #define watch_width 16
91 #define watch_height 16
92 static unsigned char watch_bits[] = {
93 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07, 0x10, 0x08, 0x08, 0x11,
94 0x04, 0x21, 0x04, 0x21, 0xe4, 0x21, 0x04, 0x20, 0x08, 0x10, 0x10, 0x08,
95 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07
97 #define watch_mask_width 16
98 #define watch_mask_height 16
99 static unsigned char watch_mask_bits[] = {
100 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf8, 0x1f, 0xfc, 0x3f,
101 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfc, 0x3f, 0xf8, 0x1f,
102 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f
105 static void addMnemonicGrabs(Widget addTo, Widget w, int unmodified);
106 static void mnemonicCB(Widget w, XtPointer callData, XKeyEvent *event);
107 static void findAndActivateMnemonic(Widget w, unsigned int keycode);
108 static void addAccelGrabs(Widget topWidget, Widget w);
109 static void addAccelGrab(Widget topWidget, Widget w);
110 static int parseAccelString(Display *display, const char *string, KeySym *keysym,
111 unsigned int *modifiers);
112 static void lockCB(Widget w, XtPointer callData, XEvent *event,
113 Boolean *continueDispatch);
114 static int findAndActivateAccel(Widget w, unsigned int keyCode,
115 unsigned int modifiers, XEvent *event);
116 static void removeWhiteSpace(char *string);
117 static int stripCaseCmp(const char *str1, const char *str2);
118 static void warnHandlerCB(String message);
119 static void passwdCB(Widget w, char * passTxt, XmTextVerifyCallbackStruct
120 *txtVerStr);
121 static void histDestroyCB(Widget w, XtPointer clientData, XtPointer callData);
122 static void histArrowKeyEH(Widget w, XtPointer callData, XEvent *event,
123 Boolean *continueDispatch);
124 static Widget addParentVisArgsAndCall(MotifDialogCreationCall callRoutine,
125 Widget parent, char *name, ArgList arglist, Cardinal argcount);
128 ** Set up closeCB to be called when the user selects close from the
129 ** window menu. The close menu item usually activates f.kill which
130 ** sends a WM_DELETE_WINDOW protocol request for the window.
132 void AddMotifCloseCallback(Widget shell, XtCallbackProc closeCB, void *arg)
134 static Atom wmpAtom, dwAtom = 0;
135 Display *display = XtDisplay(shell);
137 /* deactivate the built in delete response of killing the application */
138 XtVaSetValues(shell, XmNdeleteResponse, XmDO_NOTHING, NULL);
140 /* add a delete window protocol callback instead */
141 if (dwAtom == 0) {
142 wmpAtom = XmInternAtom(display, "WM_PROTOCOLS", TRUE);
143 dwAtom = XmInternAtom(display, "WM_DELETE_WINDOW", TRUE);
145 XmAddProtocolCallback(shell, wmpAtom, dwAtom, closeCB, arg);
149 ** Motif still generates spurrious passive grab warnings on both IBM and SGI
150 ** This routine suppresses them
152 void SuppressPassiveGrabWarnings(void)
154 XtSetWarningHandler(warnHandlerCB);
158 ** This routine kludges around the problem of backspace not being mapped
159 ** correctly when Motif is used between a server with a delete key in
160 ** the traditional typewriter backspace position and a client that
161 ** expects a backspace key in that position. Though there are three
162 ** distinct levels of key re-mapping in effect when a user is running
163 ** a Motif application, none of these is really appropriate or effective
164 ** for eliminating the delete v.s. backspace problem. Our solution is,
165 ** sadly, to eliminate the forward delete functionality of the delete key
166 ** in favor of backwards delete for both keys. So as not to prevent the
167 ** user or the application from applying other translation table re-mapping,
168 ** we apply re-map the key as a post-processing step, applied after widget
169 ** creation. As a result, the re-mapping necessarily becomes embedded
170 ** throughout an application (wherever text widgets are created), and
171 ** within library routines, including the Nirvana utility library. To
172 ** make this remapping optional, the SetDeleteRemap function provides a
173 ** way for an application to turn this functionality on and off. It is
174 ** recommended that applications that use this routine provide an
175 ** application resource called remapDeleteKey so savvy users can get
176 ** their forward delete functionality back.
178 void RemapDeleteKey(Widget w)
180 static XtTranslations table = NULL;
181 static char *translations =
182 "~Shift~Ctrl~Meta~Alt<Key>osfDelete: delete-previous-character()\n";
184 if (RemapDeleteEnabled) {
185 if (table == NULL)
186 table = XtParseTranslationTable(translations);
187 XtOverrideTranslations(w, table);
191 void SetDeleteRemap(int state)
193 RemapDeleteEnabled = state;
197 ** This routine resolves a window manager protocol incompatibility between
198 ** the X toolkit and several popular window managers. Using this in place
199 ** of XtRealizeWidget will realize the window in a way which allows the
200 ** affected window managers to apply their own placement strategy to the
201 ** window, as opposed to forcing the window to a specific location.
203 ** One of the hints in the WM_NORMAL_HINTS protocol, PPlacement, gets set by
204 ** the X toolkit (probably part of the Core or Shell widget) when a shell
205 ** widget is realized to the value stored in the XmNx and XmNy resources of the
206 ** Core widget. While callers can set these values, there is no "unset" value
207 ** for these resources. On systems which are more Motif aware, a PPosition
208 ** hint of 0,0, which is the default for XmNx and XmNy, is interpreted as,
209 ** "place this as if no hints were specified". Unfortunately the fvwm family
210 ** of window managers, which are now some of the most popular, interpret this
211 ** as "place this window at (0,0)". This routine intervenes between the
212 ** realizing and the mapping of the window to remove the inappropriate
213 ** PPlacement hint.
215 void RealizeWithoutForcingPosition(Widget shell)
217 XSizeHints *hints = XAllocSizeHints();
218 long suppliedHints;
219 Boolean mappedWhenManaged;
221 /* Temporarily set value of XmNmappedWhenManaged
222 to stop the window from popping up right away */
223 XtVaGetValues(shell, XmNmappedWhenManaged, &mappedWhenManaged, NULL);
224 XtVaSetValues(shell, XmNmappedWhenManaged, False, NULL);
226 /* Realize the widget in unmapped state */
227 XtRealizeWidget(shell);
229 /* Get rid of the incorrect WMNormal hint */
230 if (XGetWMNormalHints(XtDisplay(shell), XtWindow(shell), hints,
231 &suppliedHints)) {
232 hints->flags &= ~PPosition;
233 XSetWMNormalHints(XtDisplay(shell), XtWindow(shell), hints);
235 XFree(hints);
237 /* Map the widget */
238 XtMapWidget(shell);
240 /* Restore the value of XmNmappedWhenManaged */
241 XtVaSetValues(shell, XmNmappedWhenManaged, mappedWhenManaged, NULL);
245 ** Older X applications and X servers were mostly designed to operate with
246 ** visual class PseudoColor, because older displays were at most 8 bits
247 ** deep. Modern X servers, however, usually support 24 bit depth and other
248 ** color models. Sun (and others?) still sets their default visual to
249 ** 8-bit PseudoColor, because some of their X applications don't work
250 ** properly with the other color models. The problem with PseudoColor, of
251 ** course, is that users run out of colors in the default colormap, and if
252 ** they install additional colormaps for individual applications, colors
253 ** flash and change weirdly when you change your focus from one application
254 ** to another.
256 ** In addition to the poor choice of default, a design flaw in Xt makes it
257 ** impossible even for savvy users to specify the XtNvisual resource to
258 ** switch to a deeper visual. The problem is that the colormap resource is
259 ** processed independently of the visual resource, and usually results in a
260 ** colormap for the default visual rather than for the user-selected one.
262 ** This routine should be called before creating a shell widget, to
263 ** pre-process the visual, depth, and colormap resources, and return the
264 ** proper values for these three resources to be passed to XtAppCreateShell.
265 ** Applications which actually require a particular color model (i.e. for
266 ** doing color table animation or dynamic color assignment) should not use
267 ** this routine.
269 ** Note that a consequence of using the "best" as opposed to the default
270 ** visual is that some color resources are still converted with the default
271 ** visual (particularly *background), and these must be avoided by widgets
272 ** which are allowed to handle any visual.
274 void FindBestVisual(Display *display, const char *appName, char *appClass,
275 Visual **visual, int *depth, Colormap *colormap)
277 char rsrcName[256], rsrcClass[256], *valueString, *type, *endPtr;
278 XrmValue value;
279 int screen = DefaultScreen(display);
280 int reqDepth = -1;
281 int reqID = -1;
282 int reqClass = -1;
283 int installColormap = FALSE;
284 int maxDepth, bestClass, bestVisual, nVis, i, j;
285 XVisualInfo visTemplate, *visList = NULL;
286 static Visual *cachedVisual = NULL;
287 static Colormap cachedColormap;
288 static int cachedDepth = 0;
289 int bestClasses[] = {StaticGray, GrayScale, StaticColor, PseudoColor,
290 DirectColor, TrueColor};
292 /* If results have already been computed, just return them */
293 if (cachedVisual != NULL) {
294 *visual = cachedVisual;
295 *depth = cachedDepth;
296 *colormap = cachedColormap;
297 return;
300 /* Read the visualID and installColormap resources for the application.
301 visualID can be specified either as a number (the visual id as
302 shown by xdpyinfo), as a visual class name, or as Best or Default. */
303 sprintf(rsrcName,"%s.%s", appName, "visualID");
304 sprintf(rsrcClass, "%s.%s", appClass, "VisualID");
305 if (XrmGetResource(XtDatabase(display), rsrcName, rsrcClass, &type,
306 &value)) {
307 valueString = value.addr;
308 reqID = (int)strtol(valueString, &endPtr, 0);
309 if (endPtr == valueString) {
310 reqID = -1;
311 if (stripCaseCmp(valueString, "Default"))
312 reqID = DefaultVisual(display, screen)->visualid;
313 else if (stripCaseCmp(valueString, "StaticGray"))
314 reqClass = StaticGray;
315 else if (stripCaseCmp(valueString, "StaticColor"))
316 reqClass = StaticColor;
317 else if (stripCaseCmp(valueString, "TrueColor"))
318 reqClass = TrueColor;
319 else if (stripCaseCmp(valueString, "GrayScale"))
320 reqClass = GrayScale;
321 else if (stripCaseCmp(valueString, "PseudoColor"))
322 reqClass = PseudoColor;
323 else if (stripCaseCmp(valueString, "DirectColor"))
324 reqClass = DirectColor;
325 else if (!stripCaseCmp(valueString, "Best"))
326 fprintf(stderr, "Invalid visualID resource value\n");
329 sprintf(rsrcName,"%s.%s", appName, "installColormap");
330 sprintf(rsrcClass, "%s.%s", appClass, "InstallColormap");
331 if (XrmGetResource(XtDatabase(display), rsrcName, rsrcClass, &type,
332 &value)) {
333 if (stripCaseCmp(value.addr, "Yes") || stripCaseCmp(value.addr, "True"))
334 installColormap = TRUE;
337 /* Generate a list of visuals to consider. (Note, vestigial code for
338 user-requested visual depth is left in, just in case that function
339 might be needed again, but it does nothing) */
340 if (reqID != -1) {
341 visTemplate.visualid = reqID;
342 visList = XGetVisualInfo(display, VisualIDMask, &visTemplate, &nVis);
343 if (visList == NULL)
344 fprintf(stderr, "VisualID resource value not valid\n");
346 if (visList == NULL && reqClass != -1 && reqDepth != -1) {
347 visTemplate.class = reqClass;
348 visTemplate.depth = reqDepth;
349 visList = XGetVisualInfo(display,
350 VisualClassMask | VisualDepthMask, &visTemplate, &nVis);
351 if (visList == NULL)
352 fprintf(stderr, "Visual class/depth combination not available\n");
354 if (visList == NULL && reqClass != -1) {
355 visTemplate.class = reqClass;
356 visList = XGetVisualInfo(display, VisualClassMask, &visTemplate, &nVis);
357 if (visList == NULL)
358 fprintf(stderr,
359 "Visual Class from resource \"visualID\" not available\n");
361 if (visList == NULL && reqDepth != -1) {
362 visTemplate.depth = reqDepth;
363 visTemplate.depth = reqDepth;
364 visList = XGetVisualInfo(display, VisualDepthMask, &visTemplate, &nVis);
365 if (visList == NULL)
366 fprintf(stderr, "Requested visual depth not available\n");
368 if (visList == NULL) {
369 visList = XGetVisualInfo(display, VisualNoMask, &visTemplate, &nVis);
370 if (visList == NULL) {
371 fprintf(stderr, "Internal Error: no visuals available?\n");
372 *visual = DefaultVisual(display, screen);
373 *depth = DefaultDepth(display, screen);
374 *colormap = DefaultColormap(display, screen);
375 return;
379 /* Choose among the visuals in the candidate list. Prefer maximum
380 depth first then matching default, then largest value of bestClass
381 (I'm not sure whether we actually care about class) */
382 maxDepth = 0;
383 bestClass = 0;
384 bestVisual = 0;
385 for (i=0; i < nVis; i++) {
386 if (visList[i].depth > maxDepth) {
387 maxDepth = visList[i].depth;
388 bestClass = 0;
389 bestVisual = i;
391 if (visList[i].depth == maxDepth) {
392 if (visList[i].visual == DefaultVisual(display, screen))
393 bestVisual = i;
394 if (visList[bestVisual].visual != DefaultVisual(display, screen)) {
395 for (j = 0; j < XtNumber(bestClasses); j++) {
396 if (visList[i].class == bestClasses[j] && j > bestClass) {
397 bestClass = j;
398 bestVisual = i;
404 *visual = cachedVisual = visList[bestVisual].visual;
405 *depth = cachedDepth = visList[bestVisual].depth;
407 /* If the chosen visual is not the default, it needs a colormap allocated */
408 if (*visual == DefaultVisual(display, screen) && !installColormap)
409 *colormap = cachedColormap = DefaultColormap(display, screen);
410 else {
411 *colormap = cachedColormap = XCreateColormap(display,
412 RootWindow(display, screen), cachedVisual, AllocNone);
413 XInstallColormap(display, cachedColormap);
415 /* printf("Chose visual with depth %d, class %d, colormap %ld, id 0x%x\n",
416 visList[bestVisual].depth, visList[bestVisual].class,
417 *colormap, cachedVisual->visualid); */
418 /* Fix memory leak */
419 if (visList != NULL) {
420 XFree(visList);
425 ** If you want to use a non-default visual with Motif, shells all have to be
426 ** created with that visual, depth, and colormap, even if the parent has them
427 ** set up properly. Substituting these routines, will append visual args copied
428 ** from the parent widget (CreatePopupMenu and CreatePulldownMenu), or from the
429 ** best visual, obtained via FindBestVisual above (CreateShellWithBestVis).
431 Widget CreateDialogShell(Widget parent, char *name,
432 ArgList arglist, Cardinal argcount)
434 return addParentVisArgsAndCall(XmCreateDialogShell, parent, name, arglist,
435 argcount);
437 Widget CreatePopupMenu(Widget parent, char *name, ArgList arglist,
438 Cardinal argcount)
440 return addParentVisArgsAndCall(XmCreatePopupMenu, parent, name,
441 arglist, argcount);
443 Widget CreatePulldownMenu(Widget parent, char *name,
444 ArgList arglist, Cardinal argcount)
446 return addParentVisArgsAndCall(XmCreatePulldownMenu, parent, name, arglist,
447 argcount);
449 Widget CreatePromptDialog(Widget parent, char *name,
450 ArgList arglist, Cardinal argcount)
452 return addParentVisArgsAndCall(XmCreatePromptDialog, parent, name, arglist,
453 argcount);
455 Widget CreateSelectionDialog(Widget parent, char *name,
456 ArgList arglist, Cardinal argcount)
458 return addParentVisArgsAndCall(XmCreateSelectionDialog, parent, name,
459 arglist, argcount);
461 Widget CreateFormDialog(Widget parent, char *name,
462 ArgList arglist, Cardinal argcount)
464 return addParentVisArgsAndCall(XmCreateFormDialog, parent, name, arglist,
465 argcount);
467 Widget CreateFileSelectionDialog(Widget parent, char *name,
468 ArgList arglist, Cardinal argcount)
470 return addParentVisArgsAndCall(XmCreateFileSelectionDialog, parent, name,
471 arglist, argcount);
473 Widget CreateQuestionDialog(Widget parent, char *name,
474 ArgList arglist, Cardinal argcount)
476 return addParentVisArgsAndCall(XmCreateQuestionDialog, parent, name,
477 arglist, argcount);
479 Widget CreateMessageDialog(Widget parent, char *name,
480 ArgList arglist, Cardinal argcount)
482 return addParentVisArgsAndCall(XmCreateMessageDialog, parent, name,
483 arglist, argcount);
485 Widget CreateErrorDialog(Widget parent, char *name,
486 ArgList arglist, Cardinal argcount)
488 return addParentVisArgsAndCall(XmCreateErrorDialog, parent, name, arglist,
489 argcount);
491 Widget CreateShellWithBestVis(String appName, String appClass,
492 WidgetClass class, Display *display, ArgList args, Cardinal nArgs)
494 Visual *visual;
495 int depth;
496 Colormap colormap;
497 ArgList al;
498 Cardinal ac = nArgs;
499 Widget result;
501 FindBestVisual(display, appName, appClass, &visual, &depth, &colormap);
502 al = (ArgList)XtMalloc(sizeof(Arg) * (nArgs + 3));
503 if (nArgs != 0)
504 memcpy(al, args, sizeof(Arg) * nArgs);
505 XtSetArg(al[ac], XtNvisual, visual); ac++;
506 XtSetArg(al[ac], XtNdepth, depth); ac++;
507 XtSetArg(al[ac], XtNcolormap, colormap); ac++;
508 result = XtAppCreateShell(appName, appClass, class, display, al, ac);
509 XtFree((char *)al);
510 return result;
514 ** Calls one of the Motif widget creation routines, splicing in additional
515 ** arguments for visual, colormap, and depth.
517 static Widget addParentVisArgsAndCall(MotifDialogCreationCall createRoutine,
518 Widget parent, char *name, ArgList arglist, Cardinal argcount)
520 Visual *visual;
521 int depth;
522 Colormap colormap;
523 ArgList al;
524 Cardinal ac = argcount;
525 Widget result;
526 Widget parentShell = parent;
528 /* Find the application/dialog/menu shell at the top of the widget
529 hierarchy, which has the visual resource being used */
530 while (True) {
531 if (XtIsShell(parentShell))
532 break;
533 if (parentShell == NULL) {
534 fprintf(stderr, "failed to find shell\n");
535 exit(EXIT_FAILURE);
537 parentShell = XtParent(parentShell);
540 /* Add the visual, depth, and colormap resources to the argument list */
541 XtVaGetValues(parentShell, XtNvisual, &visual, XtNdepth, &depth,
542 XtNcolormap, &colormap, NULL);
543 al = (ArgList)XtMalloc(sizeof(Arg) * (argcount + 3));
544 if (argcount != 0)
545 memcpy(al, arglist, sizeof(Arg) * argcount);
546 XtSetArg(al[ac], XtNvisual, visual); ac++;
547 XtSetArg(al[ac], XtNdepth, depth); ac++;
548 XtSetArg(al[ac], XtNcolormap, colormap); ac++;
549 result = (*createRoutine)(parent, name, al, ac);
550 XtFree((char *)al);
551 return result;
555 ** ManageDialogCenteredOnPointer is used in place of XtManageChild for
556 ** popping up a dialog to enable the dialog to be centered under the
557 ** mouse pointer. Whether it pops up the dialog centered under the pointer
558 ** or in its default position centered over the parent widget, depends on
559 ** the value set in the SetPointerCenteredDialogs call.
561 void ManageDialogCenteredOnPointer(Widget dialogChild)
563 Widget shell = XtParent(dialogChild);
564 Window root, child;
565 unsigned int mask;
566 unsigned int width, height, borderWidth, depth;
567 int x, y, winX, winY, maxX, maxY;
568 Boolean mappedWhenManaged;
570 /* If this feature is not enabled, just manage the dialog */
571 if (!PointerCenteredDialogsEnabled) {
572 XtManageChild(dialogChild);
573 return;
576 /* Temporarily set value of XmNmappedWhenManaged
577 to stop the dialog from popping up right away */
578 XtVaGetValues(shell, XmNmappedWhenManaged, &mappedWhenManaged, NULL);
579 XtVaSetValues(shell, XmNmappedWhenManaged, False, NULL);
581 /* Manage the dialog */
582 XtManageChild(dialogChild);
584 /* Get the pointer position (x, y) */
585 XQueryPointer(XtDisplay(shell), XtWindow(shell), &root, &child,
586 &x, &y, &winX, &winY, &mask);
588 /* Translate the pointer position (x, y) into a position for the new
589 window that will place the pointer at its center */
590 XGetGeometry(XtDisplay(shell), XtWindow(shell), &root, &winX, &winY,
591 &width, &height, &borderWidth, &depth);
592 width += 2 * borderWidth;
593 height += 2 * borderWidth;
594 x -= width/2;
595 y -= height/2;
597 /* Ensure that the dialog remains on screen */
598 maxX = XtScreen(shell)->width - width;
599 maxY = XtScreen(shell)->height - height;
600 if (x < 0) x = 0;
601 if (x > maxX) x = maxX;
602 if (y < 0) y = 0;
603 if (y > maxY) y = maxY;
605 /* Set desired window position in the DialogShell */
606 XtVaSetValues(shell, XmNx, x, XmNy, y, NULL);
608 /* Map the widget */
609 XtMapWidget(shell);
611 /* Restore the value of XmNmappedWhenManaged */
612 XtVaSetValues(shell, XmNmappedWhenManaged, mappedWhenManaged, NULL);
616 ** Cause dialogs created by libNUtil.a routines (such as DialogF and
617 ** GetNewFilename), and dialogs which use ManageDialogCenteredOnPointer
618 ** to pop up over the pointer (state = True), or pop up in their default
619 ** positions (state = False)
621 void SetPointerCenteredDialogs(int state)
623 PointerCenteredDialogsEnabled = state;
628 ** Raise a window to the top and give it the input focus. Setting input focus
629 ** is important on systems which use explict (rather than pointer) focus.
631 ** The X alternatives XMapRaised, and XSetInputFocus both have problems.
632 ** XMapRaised only gives the window the focus if it was initially not visible,
633 ** and XSetInputFocus sets the input focus, but crashes if the window is not
634 ** visible.
636 ** This routine should also be used in the case where a dialog is popped up and
637 ** subsequent calls to the dialog function use a flag, or the XtIsManaged, to
638 ** decide whether to create a new instance of the dialog, because on slower
639 ** systems, events can intervene while a dialog is still on its way up and its
640 ** window is still invisible, causing a subtle crash potential if
641 ** XSetInputFocus is used.
643 void RaiseShellWindow(Widget shell)
645 RaiseWindow(XtDisplay(shell), XtWindow(shell));
647 void RaiseWindow(Display *display, Window w)
649 XWindowAttributes winAttr;
651 XGetWindowAttributes(display, w, &winAttr);
652 if (winAttr.map_state == IsViewable)
653 XSetInputFocus(display, w, RevertToParent, CurrentTime);
654 XMapRaised(display, w);
658 ** Add a handler for mnemonics in a dialog (Motif currently only handles
659 ** mnemonics in menus) following the example of M.S. Windows. To add
660 ** mnemonics to a dialog, set the XmNmnemonic resource, as you would in
661 ** a menu, on push buttons or toggle buttons, and call this function
662 ** when the dialog is fully constructed. Mnemonics added or changed
663 ** after this call will not be noticed. To add a mnemonic to a text field
664 ** or list, set the XmNmnemonic resource on the appropriate label and set
665 ** the XmNuserData resource of the label to the widget to get the focus
666 ** when the mnemonic is typed.
668 void AddDialogMnemonicHandler(Widget dialog, int unmodifiedToo)
670 XtAddEventHandler(dialog, KeyPressMask, False,
671 (XtEventHandler)mnemonicCB, (XtPointer)0);
672 addMnemonicGrabs(dialog, dialog, unmodifiedToo);
676 ** Removes the event handler and key-grabs added by AddDialogMnemonicHandler
678 void RemoveDialogMnemonicHandler(Widget dialog)
680 XtUngrabKey(dialog, AnyKey, Mod1Mask);
681 XtRemoveEventHandler(dialog, KeyPressMask, False,
682 (XtEventHandler)mnemonicCB, (XtPointer)0);
686 ** Patch around Motif's poor handling of menu accelerator keys. Motif
687 ** does not process menu accelerators when the caps lock or num lock
688 ** keys are engaged. To enable accelerators in these cases, call this
689 ** routine with the completed menu bar widget as "topMenuContainer", and
690 ** the top level shell widget as "topWidget". It will add key grabs for
691 ** all of the accelerators it finds in the topMenuContainer menu tree, and
692 ** an event handler which can process dropped accelerator events by (again)
693 ** traversing the menu tree looking for matching accelerators, and invoking
694 ** the appropriate button actions. Any dynamic additions to the menus
695 ** require a call to UpdateAccelLockPatch to add the additional grabs.
696 ** Unfortunately, these grabs can not be removed.
698 void AccelLockBugPatch(Widget topWidget, Widget topMenuContainer)
700 XtAddEventHandler(topWidget, KeyPressMask, False, lockCB, topMenuContainer);
701 addAccelGrabs(topWidget, topMenuContainer);
705 ** Add additional key grabs for new menu items added to the menus, for
706 ** patching around the Motif Caps/Num Lock problem. "topWidget" must be
707 ** the same widget passed in the original call to AccelLockBugPatch.
709 void UpdateAccelLockPatch(Widget topWidget, Widget newButton)
711 addAccelGrab(topWidget, newButton);
715 ** PopDownBugPatch
717 ** Under some circumstances, popping down a dialog and its parent in
718 ** rapid succession causes a crash. This routine delays and
719 ** processs events until receiving a ReparentNotify event.
720 ** (I have no idea why a ReparentNotify event occurs at all, but it does
721 ** mark the point where it is safe to destroy or pop down the parent, and
722 ** it might have something to do with the bug.) There is a failsafe in
723 ** the form of a ~1.5 second timeout in case no ReparentNotify arrives.
724 ** Use this sparingly, only when real crashes are observed, and periodically
725 ** check to make sure that it is still necessary.
727 void PopDownBugPatch(Widget w)
729 time_t stopTime;
731 stopTime = time(NULL) + 1;
732 while (time(NULL) <= stopTime) {
733 XEvent event;
734 XtAppContext context = XtWidgetToApplicationContext(w);
735 XtAppPeekEvent(context, &event);
736 if (event.xany.type == ReparentNotify)
737 return;
738 XtAppProcessEvent(context, XtIMAll);
743 ** Convert a compound string to a C style null terminated string.
744 ** Returned string must be freed by the caller.
746 char *GetXmStringText(XmString fromString)
748 XmStringContext context;
749 char *text, *toPtr, *toString, *fromPtr;
750 XmStringCharSet charset;
751 XmStringDirection direction;
752 Boolean separator;
754 /* Malloc a buffer large enough to hold the string. XmStringLength
755 should always be slightly longer than necessary, but won't be
756 shorter than the equivalent null-terminated string */
757 toString = XtMalloc(XmStringLength(fromString));
759 /* loop over all of the segments in the string, copying each segment
760 into the output string and converting separators into newlines */
761 XmStringInitContext(&context, fromString);
762 toPtr = toString;
763 while (XmStringGetNextSegment(context, &text,
764 &charset, &direction, &separator)) {
765 for (fromPtr=text; *fromPtr!='\0'; fromPtr++)
766 *toPtr++ = *fromPtr;
767 if (separator)
768 *toPtr++ = '\n';
771 /* terminate the string, free the context, and return the string */
772 *toPtr++ = '\0';
773 XmStringFreeContext(context);
774 return toString;
778 ** Get the XFontStruct that corresponds to the default (first) font in
779 ** a Motif font list. Since Motif stores this, it saves us from storing
780 ** it or querying it from the X server.
782 XFontStruct *GetDefaultFontStruct(XmFontList font)
784 XFontStruct *fs;
785 XmFontContext context;
786 XmStringCharSet charset;
788 XmFontListInitFontContext(&context, font);
789 XmFontListGetNextFont(context, &charset, &fs);
790 XmFontListFreeFontContext(context);
791 XtFree(charset);
792 return fs;
796 ** Create a string table suitable for passing to XmList widgets
798 XmString* StringTable(int count, ... )
800 va_list ap;
801 XmString *array;
802 int i;
803 char *str;
805 va_start(ap, count);
806 array = (XmString*)XtMalloc((count+1) * sizeof(XmString));
807 for(i = 0; i < count; i++ ) {
808 str = va_arg(ap, char *);
809 array[i] = XmStringCreateSimple(str);
811 array[i] = (XmString)0;
812 va_end(ap);
813 return(array);
816 void FreeStringTable(XmString *table)
818 int i;
820 for(i = 0; table[i] != 0; i++)
821 XmStringFree(table[i]);
822 XtFree((char *)table);
826 ** Simulate a button press. The purpose of this routine is show users what
827 ** is happening when they take an action with a non-obvious side effect,
828 ** such as when a user double clicks on a list item. The argument is an
829 ** XmPushButton widget to "press"
831 void SimulateButtonPress(Widget widget)
833 XEvent keyEvent;
835 memset((char *)&keyEvent, 0, sizeof(XKeyPressedEvent));
836 keyEvent.type = KeyPress;
837 keyEvent.xkey.serial = 1;
838 keyEvent.xkey.send_event = True;
840 if (XtIsSubclass(widget, xmGadgetClass))
842 /* On some Motif implementations, asking a gadget for its
843 window will crash, rather than return the window of its
844 parent. */
845 Widget parent = XtParent(widget);
846 keyEvent.xkey.display = XtDisplay(parent);
847 keyEvent.xkey.window = XtWindow(parent);
849 XtCallActionProc(parent, "ManagerGadgetSelect",
850 &keyEvent, NULL, 0);
852 else
854 keyEvent.xkey.display = XtDisplay(widget);
855 keyEvent.xkey.window = XtWindow(widget);
857 XtCallActionProc(widget, "ArmAndActivate", &keyEvent, NULL, 0);
862 ** Add an item to an already established pull-down or pop-up menu, including
863 ** mnemonics, accelerators and callbacks.
865 Widget AddMenuItem(Widget parent, char *name, char *label,
866 char mnemonic, char *acc, char *accText,
867 XtCallbackProc callback, void *cbArg)
869 Widget button;
870 XmString st1, st2;
872 button = XtVaCreateManagedWidget(name, xmPushButtonWidgetClass, parent,
873 XmNlabelString, st1=XmStringCreateSimple(label),
874 XmNmnemonic, mnemonic,
875 XmNacceleratorText, st2=XmStringCreateSimple(accText),
876 XmNaccelerator, acc, NULL);
877 XtAddCallback(button, XmNactivateCallback, callback, cbArg);
878 XmStringFree(st1);
879 XmStringFree(st2);
880 return button;
884 ** Add a toggle button item to an already established pull-down or pop-up
885 ** menu, including mnemonics, accelerators and callbacks.
887 Widget AddMenuToggle(Widget parent, char *name, char *label,
888 char mnemonic, char *acc, char *accText,
889 XtCallbackProc callback, void *cbArg, int set)
891 Widget button;
892 XmString st1, st2;
894 button = XtVaCreateManagedWidget(name, xmToggleButtonWidgetClass, parent,
895 XmNlabelString, st1=XmStringCreateSimple(label),
896 XmNmnemonic, mnemonic,
897 XmNacceleratorText, st2=XmStringCreateSimple(accText),
898 XmNaccelerator, acc,
899 XmNset, set, NULL);
900 XtAddCallback(button, XmNvalueChangedCallback, callback, cbArg);
901 XmStringFree(st1);
902 XmStringFree(st2);
903 return button;
907 ** Add a separator line to a menu
909 Widget AddMenuSeparator(Widget parent, char *name)
911 Widget button;
913 button = XmCreateSeparator(parent, name, NULL, 0);
914 XtManageChild(button);
915 return button;
919 ** Add a sub-menu to an established pull-down or pop-up menu, including
920 ** mnemonics, accelerators and callbacks. Returns the menu pane of the
921 ** new sub menu.
923 Widget AddSubMenu(Widget parent, char *name, char *label, char mnemonic)
925 Widget menu;
926 XmString st1;
928 menu = CreatePulldownMenu(parent, name, NULL, 0);
929 XtVaCreateManagedWidget(name, xmCascadeButtonWidgetClass, parent,
930 XmNlabelString, st1=XmStringCreateSimple(label),
931 XmNmnemonic, mnemonic,
932 XmNsubMenuId, menu, NULL);
933 XmStringFree(st1);
934 return menu;
938 ** SetIntLabel, SetFloatLabel, SetIntText, SetFloatText
940 ** Set the text of a motif label or text widget to show an integer or
941 ** floating number.
943 void SetIntLabel(Widget label, int value)
945 char labelString[20];
946 XmString s1;
948 sprintf(labelString, "%d", value);
949 s1=XmStringCreateSimple(labelString);
950 XtVaSetValues(label, XmNlabelString, s1, NULL);
951 XmStringFree(s1);
953 void SetFloatLabel(Widget label, double value)
955 char labelString[20];
956 XmString s1;
958 sprintf(labelString, "%g", value);
959 s1=XmStringCreateSimple(labelString);
960 XtVaSetValues(label, XmNlabelString, s1, NULL);
961 XmStringFree(s1);
963 void SetIntText(Widget text, int value)
965 char labelString[20];
967 sprintf(labelString, "%d", value);
968 XmTextSetString(text, labelString);
970 void SetFloatText(Widget text, double value)
972 char labelString[20];
974 sprintf(labelString, "%g", value);
975 XmTextSetString(text, labelString);
979 ** GetIntText, GetFloatText, GetIntTextWarn, GetFloatTextWarn
981 ** Get the text of a motif text widget as an integer or floating point number.
982 ** The functions will return TEXT_READ_OK of the value was read correctly.
983 ** If not, they will return either TEXT_IS_BLANK, or TEXT_NOT_NUMBER. The
984 ** GetIntTextWarn and GetFloatTextWarn will display a dialog warning the
985 ** user that the value could not be read. The argument fieldName is used
986 ** in the dialog to help the user identify where the problem is. Set
987 ** warnBlank to true if a blank field is also considered an error.
989 int GetFloatText(Widget text, double *value)
991 char *strValue, *endPtr;
992 int retVal;
994 strValue = XmTextGetString(text); /* Get Value */
995 removeWhiteSpace(strValue); /* Remove blanks and tabs */
996 *value = strtod(strValue, &endPtr); /* Convert string to double */
997 if (strlen(strValue) == 0) /* String is empty */
998 retVal = TEXT_IS_BLANK;
999 else if (*endPtr != '\0') /* Whole string not parsed */
1000 retVal = TEXT_NOT_NUMBER;
1001 else
1002 retVal = TEXT_READ_OK;
1003 XtFree(strValue);
1004 return retVal;
1007 int GetIntText(Widget text, int *value)
1009 char *strValue, *endPtr;
1010 int retVal;
1012 strValue = XmTextGetString(text); /* Get Value */
1013 removeWhiteSpace(strValue); /* Remove blanks and tabs */
1014 *value = strtol(strValue, &endPtr, 10); /* Convert string to long */
1015 if (strlen(strValue) == 0) /* String is empty */
1016 retVal = TEXT_IS_BLANK;
1017 else if (*endPtr != '\0') /* Whole string not parsed */
1018 retVal = TEXT_NOT_NUMBER;
1019 else
1020 retVal = TEXT_READ_OK;
1021 XtFree(strValue);
1022 return retVal;
1025 int GetFloatTextWarn(Widget text, double *value, char *fieldName, int warnBlank)
1027 int result;
1028 char *valueStr;
1030 result = GetFloatText(text, value);
1031 if (result == TEXT_READ_OK || (result == TEXT_IS_BLANK && !warnBlank))
1032 return result;
1033 valueStr = XmTextGetString(text);
1034 if (result == TEXT_IS_BLANK)
1035 DialogF (DF_ERR, text, 1, "Please supply %s value",
1036 "Dismiss", fieldName);
1037 else /* TEXT_NOT_NUMBER */
1038 DialogF (DF_ERR, text, 1,
1039 "Can't read %s value: \"%s\"",
1040 "Dismiss", fieldName, valueStr);
1041 XtFree(valueStr);
1042 return result;
1045 int GetIntTextWarn(Widget text, int *value, char *fieldName, int warnBlank)
1047 int result;
1048 char *valueStr;
1050 result = GetIntText(text, value);
1051 if (result == TEXT_READ_OK || (result == TEXT_IS_BLANK && !warnBlank))
1052 return result;
1053 valueStr = XmTextGetString(text);
1054 if (result == TEXT_IS_BLANK)
1055 DialogF (DF_ERR, text, 1, "Please supply a value for %s",
1056 "Dismiss", fieldName);
1057 else /* TEXT_NOT_NUMBER */
1058 DialogF (DF_ERR, text, 1,
1059 "Can't read integer value \"%s\" in %s",
1060 "Dismiss", valueStr, fieldName);
1061 XtFree(valueStr);
1062 return result;
1065 int TextWidgetIsBlank(Widget textW)
1067 char *str;
1068 int retVal;
1070 str = XmTextGetString(textW);
1071 removeWhiteSpace(str);
1072 retVal = *str == '\0';
1073 XtFree(str);
1074 return retVal;
1078 ** Turn a multi-line editing text widget into a fake single line text area
1079 ** by disabling the translation for Return. This is a way to give users
1080 ** extra space, by allowing wrapping, but still prohibiting newlines.
1081 ** (SINGLE_LINE_EDIT mode can't be used, in this case, because it forces
1082 ** the widget to be one line high).
1084 void MakeSingleLineTextW(Widget textW)
1086 static XtTranslations noReturnTable = NULL;
1087 static char *noReturnTranslations = "<Key>Return: activate()\n";
1089 if (noReturnTable == NULL)
1090 noReturnTable = XtParseTranslationTable(noReturnTranslations);
1091 XtOverrideTranslations(textW, noReturnTable);
1095 ** Add up-arrow/down-arrow recall to a single line text field. When user
1096 ** presses up-arrow, string is cleared and recent entries are presented,
1097 ** moving to older ones as each successive up-arrow is pressed. Down-arrow
1098 ** moves to more recent ones, final down-arrow clears the field. Associated
1099 ** routine, AddToHistoryList, makes maintaining a history list easier.
1101 ** Arguments are the widget, and pointers to the history list and number of
1102 ** items, which are expected to change periodically.
1104 void AddHistoryToTextWidget(Widget textW, char ***historyList, int *nItems)
1106 histInfo *histData;
1108 /* create a data structure for passing history info to the callbacks */
1109 histData = (histInfo *)XtMalloc(sizeof(histInfo));
1110 histData->list = historyList;
1111 histData->nItems = nItems;
1112 histData->index = -1;
1114 /* Add an event handler for handling up/down arrow events */
1115 XtAddEventHandler(textW, KeyPressMask, False,
1116 (XtEventHandler)histArrowKeyEH, histData);
1118 /* Add a destroy callback for freeing history data structure */
1119 XtAddCallback(textW, XmNdestroyCallback, histDestroyCB, histData);
1122 static void histDestroyCB(Widget w, XtPointer clientData, XtPointer callData)
1124 XtFree((char *)clientData);
1127 static void histArrowKeyEH(Widget w, XtPointer callData, XEvent *event,
1128 Boolean *continueDispatch)
1130 histInfo *histData = (histInfo *)callData;
1131 KeySym keysym = XLookupKeysym((XKeyEvent *)event, 0);
1133 /* only process up and down arrow keys */
1134 if (keysym != XK_Up && keysym != XK_Down)
1135 return;
1137 /* increment or decrement the index depending on which arrow was pressed */
1138 histData->index += (keysym == XK_Up) ? 1 : -1;
1140 /* if the index is out of range, beep, fix it up, and return */
1141 if (histData->index < -1) {
1142 histData->index = -1;
1143 XBell(XtDisplay(w), 0);
1144 return;
1146 if (histData->index >= *histData->nItems) {
1147 histData->index = *histData->nItems - 1;
1148 XBell(XtDisplay(w), 0);
1149 return;
1152 /* Change the text field contents */
1153 XmTextSetString(w, histData->index == -1 ? "" :
1154 (*histData->list)[histData->index]);
1158 ** Copies a string on to the end of history list, which may be reallocated
1159 ** to make room. If historyList grows beyond its internally set boundary
1160 ** for size (HISTORY_LIST_MAX), it is trimmed back to a smaller size
1161 ** (HISTORY_LIST_TRIM_TO). Before adding to the list, checks if the item
1162 ** is a duplicate of the last item. If so, it is not added.
1164 void AddToHistoryList(char *newItem, char ***historyList, int *nItems)
1166 char **newList;
1167 int i;
1169 if (*nItems != 0 && !strcmp(newItem, **historyList))
1170 return;
1171 if (*nItems == HISTORY_LIST_MAX) {
1172 for (i=HISTORY_LIST_TRIM_TO; i<HISTORY_LIST_MAX; i++)
1173 XtFree((*historyList)[i]);
1174 *nItems = HISTORY_LIST_TRIM_TO;
1176 newList = (char **)XtMalloc(sizeof(char *) * (*nItems + 1));
1177 for (i=0; i < *nItems; i++)
1178 newList[i+1] = (*historyList)[i];
1179 if (*nItems != 0 && *historyList != NULL)
1180 XtFree((char *)*historyList);
1181 (*nItems)++;
1182 newList[0] = XtNewString(newItem);
1183 *historyList = newList;
1187 * PasswordText - routine to add a callback to any text widget so that all
1188 * text typed by the user is echoed with asterisks, allowing
1189 * a password to be typed in without being seen.
1191 * parameters: w - text widget to add the callback to
1192 * passTxt - pointer to a string created by caller of this routine.
1193 * **NOTE** The length of this string should be one
1194 * greater than the maximum specified by XmNmaxLength.
1195 * This string is set to empty just before the callback
1196 * is added.
1199 void PasswordText(Widget w, char *passTxt)
1201 passTxt[0] = '\0';
1202 XtAddCallback(w, XmNmodifyVerifyCallback, (XtCallbackProc)passwdCB,passTxt);
1206 ** BeginWait/EndWait
1208 ** Display/Remove a watch cursor over topCursorWidget and its descendents
1210 void BeginWait(Widget topCursorWidget)
1212 Display *display = XtDisplay(topCursorWidget);
1213 Pixmap pixmap;
1214 Pixmap maskPixmap;
1215 XColor xcolors[2];
1216 static Cursor waitCursor = 0;
1218 /* if the watch cursor hasn't been created yet, create it */
1219 if (!waitCursor) {
1220 pixmap = XCreateBitmapFromData(display, DefaultRootWindow(display),
1221 (char *)watch_bits, watch_width, watch_height);
1223 maskPixmap = XCreateBitmapFromData(display, DefaultRootWindow(display),
1224 (char *)watch_mask_bits, watch_width, watch_height);
1226 xcolors[0].pixel = BlackPixelOfScreen(DefaultScreenOfDisplay(display));
1227 xcolors[1].pixel = WhitePixelOfScreen(DefaultScreenOfDisplay(display));
1229 XQueryColors(display, DefaultColormapOfScreen(
1230 DefaultScreenOfDisplay(display)), xcolors, 2);
1231 waitCursor = XCreatePixmapCursor(display, pixmap, maskPixmap,
1232 &xcolors[0], &xcolors[1], watch_x_hot, watch_y_hot);
1233 XFreePixmap(display, pixmap);
1234 XFreePixmap(display, maskPixmap);
1237 /* display the cursor */
1238 XDefineCursor(display, XtWindow(topCursorWidget), waitCursor);
1241 void EndWait(Widget topCursorWidget)
1243 XUndefineCursor(XtDisplay(topCursorWidget), XtWindow(topCursorWidget));
1247 ** Create an X window geometry string from width, height, x, and y values.
1248 ** This is a complement to the X routine XParseGeometry, and uses the same
1249 ** bitmask values (XValue, YValue, WidthValue, HeightValue, XNegative, and
1250 ** YNegative) as defined in <X11/Xutil.h> and documented under XParseGeometry.
1251 ** It expects a string of at least MAX_GEOMETRY_STRING_LEN in which to write
1252 ** result. Note that in a geometry string, it is not possible to supply a y
1253 ** position without an x position. Also note that the X/YNegative flags
1254 ** mean "add a '-' and negate the value" which is kind of odd.
1256 void CreateGeometryString(char *string, short x, short y,
1257 short width, short height, int bitmask)
1259 char *ptr = string;
1260 int nChars;
1262 if (bitmask & WidthValue) {
1263 sprintf(ptr, "%d%n", width, &nChars);
1264 ptr += nChars;
1266 if (bitmask & HeightValue) {
1267 sprintf(ptr, "x%d%n", height, &nChars);
1268 ptr += nChars;
1270 if (bitmask & XValue) {
1271 if (bitmask & XNegative)
1272 sprintf(ptr, "-%d%n", -x, &nChars);
1273 else
1274 sprintf(ptr, "+%d%n", x, &nChars);
1275 ptr += nChars;
1277 if (bitmask & YValue) {
1278 if (bitmask & YNegative)
1279 sprintf(ptr, "-%d%n", -y, &nChars);
1280 else
1281 sprintf(ptr, "+%d%n", y, &nChars);
1282 ptr += nChars;
1284 *ptr = '\0';
1287 /* */
1288 /* passwdCB: callback routine added by PasswordText routine. This routine */
1289 /* echoes each character typed as an asterisk (*) and a few other */
1290 /* necessary things so that the password typed in is not visible */
1291 /* */
1292 static void passwdCB(Widget w, char * passTxt, XmTextVerifyCallbackStruct
1293 *txtVerStr)
1295 /* XmTextVerifyCallbackStruct: */
1296 /* int reason; should be XmCR_MODIFYING_TEXT_VALUE */
1297 /* XEvent *event; points to XEvent that triggered the callback */
1298 /* Boolean doit; indicates whether action should be performed; setting */
1299 /* this to false negates the action */
1300 /* long currInsert, current position of insert cursor */
1301 /* newInsert; position user attempts to position the insert cursor */
1302 /* long startPos, starting position of the text to modify */
1303 /* endPos; ending position of the text to modify */
1304 /* XmTextBlock text; */
1306 /* XmTextBlock (used to pass text around): */
1307 /* char *ptr; points to text to be inserted */
1308 /* int length; Number of bytes (length) */
1309 /* XmTextFormat format; Representations format */
1311 /* XmTextFormat: either FMT8BIT or FMT16BIT */
1314 int numCharsTyped, i, j, pos;
1316 /* ensure text format is 8-bit characters */
1317 if (txtVerStr->text->format != FMT8BIT)
1318 return;
1320 /* verify assumptions */
1321 /* if (txtVerStr->endPos < txtVerStr->startPos)
1322 fprintf(stderr, "User password callback error: endPos < startPos\n");
1323 if (strlen(passTxt) == 0 && txtVerStr->endPos != 0)
1324 fprintf(stderr, "User password callback error: no txt, but end != 0\n");
1326 printf("passTxt = %s, startPos = %d, endPos = %d, txtBlkAddr = %d\n",
1327 passTxt, txtVerStr->startPos, txtVerStr->endPos, txtVerStr->text);
1328 if (txtVerStr->text != NULL && txtVerStr->text->ptr != NULL)
1329 printf(" string typed = %s, length = %d\n", txtVerStr->text->ptr,
1330 txtVerStr->text->length);
1332 /* If necessary, expand/compress passTxt and insert any new text */
1333 if (txtVerStr->text != NULL && txtVerStr->text->ptr != NULL)
1334 numCharsTyped = txtVerStr->text->length;
1335 else
1336 numCharsTyped = 0;
1337 /* numCharsTyped = # chars to insert (that user typed) */
1338 /* j = # chars to expand (+) or compress (-) the password string */
1339 j = numCharsTyped - (txtVerStr->endPos - txtVerStr->startPos);
1340 if (j > 0) /* expand case: start at ending null */
1341 for (pos = strlen(passTxt) + 1; pos >= txtVerStr->endPos; --pos)
1342 passTxt[pos+j] = passTxt[pos];
1343 if (j < 0) /* compress case */
1344 for (pos = txtVerStr->startPos + numCharsTyped;
1345 pos <= strlen(passTxt)+1; ++pos)
1346 passTxt[pos] = passTxt[pos-j];
1347 /* then copy text to be inserted into passTxt */
1348 for (pos = txtVerStr->startPos, i = 0; i < numCharsTyped; ++i) {
1349 passTxt[pos+i] = *(txtVerStr->text->ptr + i);
1350 /* Replace text typed by user with asterisks (*) */
1351 *(txtVerStr->text->ptr + i) = '*';
1353 /* printf(" Password string now = %s\n", passTxt); */
1357 ** Remove the white space (blanks and tabs) from a string
1359 static void removeWhiteSpace(char *string)
1361 char *outPtr = string;
1363 while (TRUE) {
1364 if (*string == 0) {
1365 *outPtr = 0;
1366 return;
1367 } else if (*string != ' ' && *string != '\t')
1368 *(outPtr++) = *(string++);
1369 else
1370 string++;
1375 ** Compares two strings and return TRUE if the two strings
1376 ** are the same, ignoring whitespace and case differences.
1378 static int stripCaseCmp(const char *str1, const char *str2)
1380 const char *c1, *c2;
1382 for (c1=str1, c2=str2; *c1!='\0' && *c2!='\0'; c1++, c2++) {
1383 while (*c1 == ' ' || *c1 == '\t')
1384 c1++;
1385 while (*c2 == ' ' || *c2 == '\t')
1386 c2++;
1387 if (toupper((unsigned char)*c1) != toupper((unsigned char)*c2))
1388 return FALSE;
1390 return *c1 == '\0' && *c2 == '\0';
1393 static void warnHandlerCB(String message)
1395 if (strstr(message, "XtRemoveGrab"))
1396 return;
1397 if (strstr(message, "Attempt to remove non-existant passive grab"))
1398 return;
1399 fputs(message, stderr);
1400 fputc('\n', stderr);
1403 static XModifierKeymap *getKeyboardMapping(Display *display) {
1404 static XModifierKeymap *keyboardMap = NULL;
1406 if (keyboardMap == NULL) {
1407 keyboardMap = XGetModifierMapping(display);
1409 return(keyboardMap);
1413 ** get mask for a modifier
1417 static Modifiers findModifierMapping(Display *display, KeyCode keyCode) {
1418 int i, j;
1419 KeyCode *mapentry;
1420 XModifierKeymap *modMap = getKeyboardMapping(display);
1422 if (modMap == NULL || keyCode == 0) {
1423 return(0);
1426 mapentry = modMap->modifiermap;
1427 for (i = 0; i < 8; ++i) {
1428 for (j = 0; j < (modMap->max_keypermod); ++j) {
1429 if (keyCode == *mapentry) {
1430 return(1 << ((mapentry - modMap->modifiermap) / modMap->max_keypermod));
1432 ++mapentry;
1435 return(0);
1438 static Modifiers getNumLockModMask(Display *display) {
1439 static int numLockMask = -1;
1441 if (numLockMask == -1) {
1442 numLockMask = findModifierMapping(display, XKeysymToKeycode(display, XK_Num_Lock));
1444 return(numLockMask);
1448 ** Grab a key regardless of caps-lock and other silly latching keys.
1452 static void reallyGrabAKey(Widget dialog, int keyCode, Modifiers mask) {
1453 Modifiers numLockMask = getNumLockModMask(XtDisplay(dialog));
1455 XtGrabKey(dialog, keyCode, mask, True, GrabModeAsync, GrabModeAsync);
1456 XtGrabKey(dialog, keyCode, mask|LockMask, True, GrabModeAsync, GrabModeAsync);
1457 if (numLockMask && numLockMask != LockMask) {
1458 XtGrabKey(dialog, keyCode, mask|numLockMask, True, GrabModeAsync, GrabModeAsync);
1459 XtGrabKey(dialog, keyCode, mask|LockMask|numLockMask, True, GrabModeAsync, GrabModeAsync);
1464 ** Part of dialog mnemonic processing. Search the widget tree under w
1465 ** for widgets with mnemonics. When found, add a passive grab to the
1466 ** dialog widget for the mnemonic character, thus directing mnemonic
1467 ** events to the dialog widget.
1469 static void addMnemonicGrabs(Widget dialog, Widget w, int unmodifiedToo)
1471 char mneString[2];
1472 WidgetList children;
1473 Cardinal numChildren;
1474 int i, isMenu;
1475 KeySym mnemonic = '\0';
1476 unsigned char rowColType;
1477 unsigned int keyCode;
1479 if (XtIsComposite(w)) {
1480 if (XtClass(w) == xmRowColumnWidgetClass) {
1481 XtVaGetValues(w, XmNrowColumnType, &rowColType, NULL);
1482 isMenu = rowColType != XmWORK_AREA;
1483 } else
1484 isMenu = False;
1485 if (!isMenu) {
1486 XtVaGetValues(w, XmNchildren, &children, XmNnumChildren,
1487 &numChildren, NULL);
1488 for (i=0; i<numChildren; i++)
1489 addMnemonicGrabs(dialog, children[i], unmodifiedToo);
1491 } else {
1492 XtVaGetValues(w, XmNmnemonic, &mnemonic, NULL);
1493 if (mnemonic != XK_VoidSymbol && mnemonic != '\0') {
1494 mneString[0] = mnemonic; mneString[1] = '\0';
1495 keyCode = XKeysymToKeycode(XtDisplay(dialog),
1496 XStringToKeysym(mneString));
1497 reallyGrabAKey(dialog, keyCode, Mod1Mask);
1498 if (unmodifiedToo)
1499 reallyGrabAKey(dialog, keyCode, 0);
1505 ** Callback routine for dialog mnemonic processing.
1507 static void mnemonicCB(Widget w, XtPointer callData, XKeyEvent *event)
1509 findAndActivateMnemonic(w, event->keycode);
1513 ** Look for a widget in the widget tree w, with a mnemonic matching
1514 ** keycode. When one is found, simulate a button press on that widget
1515 ** and give it the keyboard focus. If the mnemonic is on a label,
1516 ** look in the userData field of the label to see if it points to
1517 ** another widget, and give that the focus. This routine is just
1518 ** sufficient for NEdit, no doubt it will need to be extended for
1519 ** mnemonics on widgets other than just buttons and text fields.
1521 static void findAndActivateMnemonic(Widget w, unsigned int keycode)
1523 WidgetList children;
1524 Cardinal numChildren;
1525 int i, isMenu;
1526 KeySym mnemonic = '\0';
1527 char mneString[2];
1528 Widget userData;
1529 unsigned char rowColType;
1531 if (XtIsComposite(w)) {
1532 if (XtClass(w) == xmRowColumnWidgetClass) {
1533 XtVaGetValues(w, XmNrowColumnType, &rowColType, NULL);
1534 isMenu = rowColType != XmWORK_AREA;
1535 } else
1536 isMenu = False;
1537 if (!isMenu) {
1538 XtVaGetValues(w, XmNchildren, &children, XmNnumChildren,
1539 &numChildren, NULL);
1540 for (i=0; i<numChildren; i++)
1541 findAndActivateMnemonic(children[i], keycode);
1543 } else {
1544 XtVaGetValues(w, XmNmnemonic, &mnemonic, NULL);
1545 if (mnemonic != '\0') {
1546 mneString[0] = mnemonic; mneString[1] = '\0';
1547 if (XKeysymToKeycode(XtDisplay(XtParent(w)),
1548 XStringToKeysym(mneString)) == keycode) {
1549 if (XtClass(w) == xmLabelWidgetClass ||
1550 XtClass(w) == xmLabelGadgetClass) {
1551 XtVaGetValues(w, XmNuserData, &userData, NULL);
1552 if (userData!=NULL && XtIsWidget(userData) &&
1553 XmIsTraversable(userData))
1554 XmProcessTraversal(userData, XmTRAVERSE_CURRENT);
1555 } else if (XmIsTraversable(w)) {
1556 XmProcessTraversal(w, XmTRAVERSE_CURRENT);
1557 SimulateButtonPress(w);
1565 ** Part of workaround for Motif Caps/Num Lock bug. Search the widget tree
1566 ** under w for widgets with accelerators. When found, add three passive
1567 ** grabs to topWidget, one for the accelerator keysym + modifiers + Caps
1568 ** Lock, one for Num Lock, and one for both, thus directing lock +
1569 ** accelerator events to topWidget.
1571 static void addAccelGrabs(Widget topWidget, Widget w)
1573 WidgetList children;
1574 Widget menu;
1575 Cardinal numChildren;
1576 int i;
1578 if (XtIsComposite(w)) {
1579 XtVaGetValues(w, XmNchildren, &children, XmNnumChildren,
1580 &numChildren, NULL);
1581 for (i=0; i<numChildren; i++)
1582 addAccelGrabs(topWidget, children[i]);
1583 } else if (XtClass(w) == xmCascadeButtonWidgetClass) {
1584 XtVaGetValues(w, XmNsubMenuId, &menu, NULL);
1585 if (menu != NULL)
1586 addAccelGrabs(topWidget, menu);
1587 } else
1588 addAccelGrab(topWidget, w);
1592 ** Grabs the key + modifier defined in the widget's accelerator resource,
1593 ** in combination with the Caps Lock and Num Lock accelerators.
1595 static void addAccelGrab(Widget topWidget, Widget w)
1597 char *accelString = NULL;
1598 KeySym keysym;
1599 unsigned int modifiers;
1600 Modifiers numLockMask = getNumLockModMask(XtDisplay(topWidget));
1602 XtVaGetValues(w, XmNaccelerator, &accelString, NULL);
1603 if (accelString == NULL || *accelString == '\0')
1604 return;
1606 if (!parseAccelString(XtDisplay(topWidget), accelString, &keysym, &modifiers))
1607 return;
1608 XtGrabKey(topWidget, XKeysymToKeycode(XtDisplay(topWidget), keysym),
1609 modifiers | LockMask, True, GrabModeAsync, GrabModeAsync);
1610 if (numLockMask && numLockMask != LockMask) {
1611 XtGrabKey(topWidget, XKeysymToKeycode(XtDisplay(topWidget), keysym),
1612 modifiers | numLockMask, True, GrabModeAsync, GrabModeAsync);
1613 XtGrabKey(topWidget, XKeysymToKeycode(XtDisplay(topWidget), keysym),
1614 modifiers | LockMask | numLockMask, True, GrabModeAsync, GrabModeAsync);
1619 ** Read a Motif accelerator string and translate it into a keysym + modifiers.
1620 ** Returns TRUE if the parse was successful, FALSE, if not.
1622 static int parseAccelString(Display *display, const char *string, KeySym *keySym,
1623 unsigned int *modifiers)
1625 #define N_MODIFIERS 12
1626 /*... Is NumLock always Mod3? */
1627 static char *modifierNames[N_MODIFIERS] = {"Ctrl", "Shift", "Alt", "Mod2",
1628 "Mod3", "Mod4", "Mod5", "Button1", "Button2", "Button3", "Button4",
1629 "Button5"};
1630 static unsigned int modifierMasks[N_MODIFIERS] = {ControlMask, ShiftMask,
1631 Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask, Button1Mask, Button2Mask,
1632 Button3Mask, Button4Mask, Button5Mask};
1633 Modifiers numLockMask = getNumLockModMask(display);
1634 char modStr[MAX_ACCEL_LEN];
1635 char evtStr[MAX_ACCEL_LEN];
1636 char keyStr[MAX_ACCEL_LEN];
1637 const char *c, *evtStart, *keyStart;
1638 int i;
1640 if (strlen(string) >= MAX_ACCEL_LEN)
1641 return FALSE;
1643 /* Get the modifier part */
1644 for (c = string; *c != '<'; c++)
1645 if (*c == '\0')
1646 return FALSE;
1647 strncpy(modStr, string, c - string);
1648 modStr[c - string] = '\0';
1650 /* Verify the <key> or <keypress> part */
1651 evtStart = c;
1652 for ( ; *c != '>'; c++)
1653 if (*c == '\0')
1654 return FALSE;
1655 c++;
1656 strncpy(evtStr, evtStart, c - evtStart);
1657 evtStr[c - evtStart] = '\0';
1658 if (!stripCaseCmp(evtStr, "<key>") && !stripCaseCmp(evtStr, "<keypress>"))
1659 return FALSE;
1661 /* Get the keysym part */
1662 keyStart = c;
1663 for ( ; *c != '\0'; c++);
1664 strncpy(keyStr, keyStart, c - keyStart);
1665 keyStr[c - keyStart] = '\0';
1666 *keySym = XStringToKeysym(keyStr);
1668 /* Parse the modifier part */
1669 *modifiers = 0;
1670 c = modStr;
1671 while (*c != '\0') {
1672 while (*c == ' ' || *c == '\t')
1673 c++;
1674 if (*c == '\0')
1675 break;
1676 for (i = 0; i < N_MODIFIERS; i++) {
1677 if (!strncmp(c, modifierNames[i], strlen(modifierNames[i]))) {
1678 c += strlen(modifierNames[i]);
1679 if (modifierMasks[i] != numLockMask) {
1680 *modifiers |= modifierMasks[i];
1682 break;
1685 if (i == N_MODIFIERS)
1686 return FALSE;
1689 return TRUE;
1693 ** Event handler for patching around Motif's lock + accelerator problem.
1694 ** Looks for a menu item in the patched menu hierarchy and invokes its
1695 ** ArmAndActivate action.
1697 static void lockCB(Widget w, XtPointer callData, XEvent *event,
1698 Boolean *continueDispatch)
1700 Modifiers numLockMask = getNumLockModMask(XtDisplay(w));
1701 Widget topMenuWidget = (Widget)callData;
1702 *continueDispatch = TRUE;
1703 if (!(((XKeyEvent *)event)->state & (LockMask | numLockMask)))
1704 return;
1706 if (!findAndActivateAccel(topMenuWidget, ((XKeyEvent *)event)->keycode,
1707 ((XKeyEvent *)event)->state & ~(LockMask | numLockMask), event))
1708 *continueDispatch = FALSE;
1712 ** Search through menu hierarchy under w and look for a button with
1713 ** accelerator matching keyCode + modifiers, and do its action
1715 static int findAndActivateAccel(Widget w, unsigned int keyCode,
1716 unsigned int modifiers, XEvent *event)
1719 WidgetList children;
1720 Widget menu;
1721 Cardinal numChildren;
1722 int i;
1723 char *accelString = NULL;
1724 KeySym keysym;
1725 unsigned int mods;
1727 if (XtIsComposite(w)) {
1728 XtVaGetValues(w, XmNchildren, &children, XmNnumChildren,
1729 &numChildren, NULL);
1730 for (i=0; i<numChildren; i++)
1731 if (findAndActivateAccel(children[i], keyCode, modifiers, event))
1732 return TRUE;
1733 } else if (XtClass(w) == xmCascadeButtonWidgetClass) {
1734 XtVaGetValues(w, XmNsubMenuId, &menu, NULL);
1735 if (menu != NULL)
1736 if (findAndActivateAccel(menu, keyCode, modifiers, event))
1737 return TRUE;
1738 } else {
1739 XtVaGetValues(w, XmNaccelerator, &accelString, NULL);
1740 if (accelString != NULL && *accelString != '\0') {
1741 if (!parseAccelString(XtDisplay(w), accelString, &keysym, &mods))
1742 return FALSE;
1743 if (keyCode == XKeysymToKeycode(XtDisplay(w), keysym) &&
1744 modifiers == mods) {
1745 if (XtIsSensitive(w)) {
1746 XtCallActionProc(w, "ArmAndActivate", event, NULL, 0);
1747 return TRUE;
1752 return FALSE;