Support for Lynx, MacOS; better x86 reporting
[nedit.git] / util / misc.c
blob3d0e33dd919509692137331e0e3581fd69b9174b
1 static const char CVSID[] = "$Id: misc.c,v 1.41 2002/07/12 11:44:01 edg 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 *******************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 #include "../config.h"
31 #endif
33 #include "misc.h"
34 #include "DialogF.h"
36 #include <stdlib.h>
37 #include <string.h>
38 #include <stdarg.h>
39 #include <ctype.h>
40 #include <stdio.h>
41 #include <time.h>
42 #ifdef VMS
43 #include <types.h>
44 #include <unixio.h>
45 #include <file.h>
46 #endif /*VMS*/
48 #include <X11/Intrinsic.h>
49 #include <X11/Xatom.h>
50 #include <X11/keysym.h>
51 #include <X11/keysymdef.h>
52 #include <Xm/Xm.h>
53 #include <Xm/Label.h>
54 #include <Xm/LabelG.h>
55 #include <Xm/ToggleB.h>
56 #include <Xm/PushB.h>
57 #include <Xm/Separator.h>
58 #include <Xm/RowColumn.h>
59 #include <Xm/CascadeB.h>
60 #include <Xm/AtomMgr.h>
61 #include <Xm/Protocols.h>
62 #include <Xm/Text.h>
63 #include <Xm/MessageB.h>
64 #include <Xm/DialogS.h>
65 #include <Xm/SelectioB.h>
66 #include <Xm/Form.h>
67 #include <Xm/FileSB.h>
69 #ifdef HAVE_DEBUG_H
70 #include "../debug.h"
71 #endif
73 /* structure for passing history-recall data to callbacks */
74 typedef struct {
75 char ***list;
76 int *nItems;
77 int index;
78 } histInfo;
80 typedef Widget (*MotifDialogCreationCall)(Widget, String, ArgList, Cardinal);
82 /* Maximum size of a history-recall list. Typically never invoked, since
83 user must first make this many entries in the text field, limited for
84 safety, to the maximum reasonable number of times user can hit up-arrow
85 before carpal tunnel syndrome sets in */
86 #define HISTORY_LIST_TRIM_TO 1000
87 #define HISTORY_LIST_MAX 2000
89 /* flags to enable/disable delete key remapping and pointer centered dialogs */
90 static int RemapDeleteEnabled = True;
91 static int PointerCenteredDialogsEnabled = False;
93 /* bitmap and mask for waiting (wrist-watch) cursor */
94 #define watch_x_hot 7
95 #define watch_y_hot 7
96 #define watch_width 16
97 #define watch_height 16
98 static unsigned char watch_bits[] = {
99 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07, 0x10, 0x08, 0x08, 0x11,
100 0x04, 0x21, 0x04, 0x21, 0xe4, 0x21, 0x04, 0x20, 0x08, 0x10, 0x10, 0x08,
101 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07
103 #define watch_mask_width 16
104 #define watch_mask_height 16
105 static unsigned char watch_mask_bits[] = {
106 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf8, 0x1f, 0xfc, 0x3f,
107 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfc, 0x3f, 0xf8, 0x1f,
108 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f
111 static void addMnemonicGrabs(Widget addTo, Widget w, int unmodified);
112 static void mnemonicCB(Widget w, XtPointer callData, XKeyEvent *event);
113 static void findAndActivateMnemonic(Widget w, unsigned int keycode);
114 static void addAccelGrabs(Widget topWidget, Widget w);
115 static void addAccelGrab(Widget topWidget, Widget w);
116 static int parseAccelString(Display *display, const char *string, KeySym *keysym,
117 unsigned int *modifiers);
118 static void lockCB(Widget w, XtPointer callData, XEvent *event,
119 Boolean *continueDispatch);
120 static int findAndActivateAccel(Widget w, unsigned int keyCode,
121 unsigned int modifiers, XEvent *event);
122 static void removeWhiteSpace(char *string);
123 static int stripCaseCmp(const char *str1, const char *str2);
124 static void warnHandlerCB(String message);
125 static void passwdCB(Widget w, char * passTxt, XmTextVerifyCallbackStruct
126 *txtVerStr);
127 static void histDestroyCB(Widget w, XtPointer clientData, XtPointer callData);
128 static void histArrowKeyEH(Widget w, XtPointer callData, XEvent *event,
129 Boolean *continueDispatch);
130 static Widget addParentVisArgsAndCall(MotifDialogCreationCall callRoutine,
131 Widget parent, char *name, ArgList arglist, Cardinal argcount);
134 ** Set up closeCB to be called when the user selects close from the
135 ** window menu. The close menu item usually activates f.kill which
136 ** sends a WM_DELETE_WINDOW protocol request for the window.
138 void AddMotifCloseCallback(Widget shell, XtCallbackProc closeCB, void *arg)
140 static Atom wmpAtom, dwAtom = 0;
141 Display *display = XtDisplay(shell);
143 /* deactivate the built in delete response of killing the application */
144 XtVaSetValues(shell, XmNdeleteResponse, XmDO_NOTHING, NULL);
146 /* add a delete window protocol callback instead */
147 if (dwAtom == 0) {
148 wmpAtom = XmInternAtom(display, "WM_PROTOCOLS", FALSE);
149 dwAtom = XmInternAtom(display, "WM_DELETE_WINDOW", FALSE);
151 XmAddProtocolCallback(shell, wmpAtom, dwAtom, closeCB, arg);
155 ** Motif still generates spurious passive grab warnings on both IBM and SGI
156 ** This routine suppresses them.
157 ** (amai, 20011121:)
158 ** And triggers an annoying message on DEC systems on alpha ->
159 ** See Xt sources, xc/lib/Xt/Error.c:DefaultMsg()):
160 ** actually for some obscure reasons they check for XtError/Warning
161 ** handlers being installed when running as a root process!
162 ** Since this handler doesn't help on non-effected systems we should only
163 ** use it if necessary.
165 void SuppressPassiveGrabWarnings(void)
167 #if !defined(__alpha) && !defined(__EMX__)
168 XtSetWarningHandler(warnHandlerCB);
169 #endif
173 ** This routine kludges around the problem of backspace not being mapped
174 ** correctly when Motif is used between a server with a delete key in
175 ** the traditional typewriter backspace position and a client that
176 ** expects a backspace key in that position. Though there are three
177 ** distinct levels of key re-mapping in effect when a user is running
178 ** a Motif application, none of these is really appropriate or effective
179 ** for eliminating the delete v.s. backspace problem. Our solution is,
180 ** sadly, to eliminate the forward delete functionality of the delete key
181 ** in favor of backwards delete for both keys. So as not to prevent the
182 ** user or the application from applying other translation table re-mapping,
183 ** we apply re-map the key as a post-processing step, applied after widget
184 ** creation. As a result, the re-mapping necessarily becomes embedded
185 ** throughout an application (wherever text widgets are created), and
186 ** within library routines, including the Nirvana utility library. To
187 ** make this remapping optional, the SetDeleteRemap function provides a
188 ** way for an application to turn this functionality on and off. It is
189 ** recommended that applications that use this routine provide an
190 ** application resource called remapDeleteKey so savvy users can get
191 ** their forward delete functionality back.
193 void RemapDeleteKey(Widget w)
195 static XtTranslations table = NULL;
196 static char *translations =
197 "~Shift~Ctrl~Meta~Alt<Key>osfDelete: delete-previous-character()\n";
199 if (RemapDeleteEnabled) {
200 if (table == NULL)
201 table = XtParseTranslationTable(translations);
202 XtOverrideTranslations(w, table);
206 void SetDeleteRemap(int state)
208 RemapDeleteEnabled = state;
212 ** This routine resolves a window manager protocol incompatibility between
213 ** the X toolkit and several popular window managers. Using this in place
214 ** of XtRealizeWidget will realize the window in a way which allows the
215 ** affected window managers to apply their own placement strategy to the
216 ** window, as opposed to forcing the window to a specific location.
218 ** One of the hints in the WM_NORMAL_HINTS protocol, PPlacement, gets set by
219 ** the X toolkit (probably part of the Core or Shell widget) when a shell
220 ** widget is realized to the value stored in the XmNx and XmNy resources of the
221 ** Core widget. While callers can set these values, there is no "unset" value
222 ** for these resources. On systems which are more Motif aware, a PPosition
223 ** hint of 0,0, which is the default for XmNx and XmNy, is interpreted as,
224 ** "place this as if no hints were specified". Unfortunately the fvwm family
225 ** of window managers, which are now some of the most popular, interpret this
226 ** as "place this window at (0,0)". This routine intervenes between the
227 ** realizing and the mapping of the window to remove the inappropriate
228 ** PPlacement hint.
230 void RealizeWithoutForcingPosition(Widget shell)
232 XSizeHints *hints = XAllocSizeHints();
233 long suppliedHints;
234 Boolean mappedWhenManaged;
236 /* Temporarily set value of XmNmappedWhenManaged
237 to stop the window from popping up right away */
238 XtVaGetValues(shell, XmNmappedWhenManaged, &mappedWhenManaged, NULL);
239 XtVaSetValues(shell, XmNmappedWhenManaged, False, NULL);
241 /* Realize the widget in unmapped state */
242 XtRealizeWidget(shell);
244 /* Get rid of the incorrect WMNormal hint */
245 if (XGetWMNormalHints(XtDisplay(shell), XtWindow(shell), hints,
246 &suppliedHints)) {
247 hints->flags &= ~PPosition;
248 XSetWMNormalHints(XtDisplay(shell), XtWindow(shell), hints);
250 XFree(hints);
252 /* Map the widget */
253 XtMapWidget(shell);
255 /* Restore the value of XmNmappedWhenManaged */
256 XtVaSetValues(shell, XmNmappedWhenManaged, mappedWhenManaged, NULL);
260 ** Older X applications and X servers were mostly designed to operate with
261 ** visual class PseudoColor, because older displays were at most 8 bits
262 ** deep. Modern X servers, however, usually support 24 bit depth and other
263 ** color models. Sun (and others?) still sets their default visual to
264 ** 8-bit PseudoColor, because some of their X applications don't work
265 ** properly with the other color models. The problem with PseudoColor, of
266 ** course, is that users run out of colors in the default colormap, and if
267 ** they install additional colormaps for individual applications, colors
268 ** flash and change weirdly when you change your focus from one application
269 ** to another.
271 ** In addition to the poor choice of default, a design flaw in Xt makes it
272 ** impossible even for savvy users to specify the XtNvisual resource to
273 ** switch to a deeper visual. The problem is that the colormap resource is
274 ** processed independently of the visual resource, and usually results in a
275 ** colormap for the default visual rather than for the user-selected one.
277 ** This routine should be called before creating a shell widget, to
278 ** pre-process the visual, depth, and colormap resources, and return the
279 ** proper values for these three resources to be passed to XtAppCreateShell.
280 ** Applications which actually require a particular color model (i.e. for
281 ** doing color table animation or dynamic color assignment) should not use
282 ** this routine.
284 ** Note that a consequence of using the "best" as opposed to the default
285 ** visual is that some color resources are still converted with the default
286 ** visual (particularly *background), and these must be avoided by widgets
287 ** which are allowed to handle any visual.
289 void FindBestVisual(Display *display, const char *appName, const char *appClass,
290 Visual **visual, int *depth, Colormap *colormap)
292 char rsrcName[256], rsrcClass[256], *valueString, *type, *endPtr;
293 XrmValue value;
294 int screen = DefaultScreen(display);
295 int reqDepth = -1;
296 long reqID = -1; /* should hold a 'VisualID' and a '-1' ... */
297 int reqClass = -1;
298 int installColormap = FALSE;
299 int maxDepth, bestClass, bestVisual, nVis, i, j;
300 XVisualInfo visTemplate, *visList = NULL;
301 static Visual *cachedVisual = NULL;
302 static Colormap cachedColormap;
303 static int cachedDepth = 0;
304 int bestClasses[] = {StaticGray, GrayScale, StaticColor, PseudoColor,
305 DirectColor, TrueColor};
307 /* If results have already been computed, just return them */
308 if (cachedVisual != NULL) {
309 *visual = cachedVisual;
310 *depth = cachedDepth;
311 *colormap = cachedColormap;
312 return;
315 /* Read the visualID and installColormap resources for the application.
316 visualID can be specified either as a number (the visual id as
317 shown by xdpyinfo), as a visual class name, or as Best or Default. */
318 sprintf(rsrcName,"%s.%s", appName, "visualID");
319 sprintf(rsrcClass, "%s.%s", appClass, "VisualID");
320 if (XrmGetResource(XtDatabase(display), rsrcName, rsrcClass, &type,
321 &value)) {
322 valueString = value.addr;
323 reqID = (int)strtol(valueString, &endPtr, 0);
324 if (endPtr == valueString) {
325 reqID = -1;
326 if (stripCaseCmp(valueString, "Default"))
327 reqID = DefaultVisual(display, screen)->visualid;
328 else if (stripCaseCmp(valueString, "StaticGray"))
329 reqClass = StaticGray;
330 else if (stripCaseCmp(valueString, "StaticColor"))
331 reqClass = StaticColor;
332 else if (stripCaseCmp(valueString, "TrueColor"))
333 reqClass = TrueColor;
334 else if (stripCaseCmp(valueString, "GrayScale"))
335 reqClass = GrayScale;
336 else if (stripCaseCmp(valueString, "PseudoColor"))
337 reqClass = PseudoColor;
338 else if (stripCaseCmp(valueString, "DirectColor"))
339 reqClass = DirectColor;
340 else if (!stripCaseCmp(valueString, "Best"))
341 fprintf(stderr, "Invalid visualID resource value\n");
344 sprintf(rsrcName,"%s.%s", appName, "installColormap");
345 sprintf(rsrcClass, "%s.%s", appClass, "InstallColormap");
346 if (XrmGetResource(XtDatabase(display), rsrcName, rsrcClass, &type,
347 &value)) {
348 if (stripCaseCmp(value.addr, "Yes") || stripCaseCmp(value.addr, "True"))
349 installColormap = TRUE;
352 visTemplate.screen = screen;
354 /* Generate a list of visuals to consider. (Note, vestigial code for
355 user-requested visual depth is left in, just in case that function
356 might be needed again, but it does nothing) */
357 if (reqID != -1) {
358 visTemplate.visualid = reqID;
359 visList = XGetVisualInfo(display, VisualScreenMask|VisualIDMask,
360 &visTemplate, &nVis);
361 if (visList == NULL)
362 fprintf(stderr, "VisualID resource value not valid\n");
364 if (visList == NULL && reqClass != -1 && reqDepth != -1) {
365 visTemplate.class = reqClass;
366 visTemplate.depth = reqDepth;
367 visList = XGetVisualInfo(display,
368 VisualScreenMask| VisualClassMask | VisualDepthMask,
369 &visTemplate, &nVis);
370 if (visList == NULL)
371 fprintf(stderr, "Visual class/depth combination not available\n");
373 if (visList == NULL && reqClass != -1) {
374 visTemplate.class = reqClass;
375 visList = XGetVisualInfo(display, VisualScreenMask|VisualClassMask,
376 &visTemplate, &nVis);
377 if (visList == NULL)
378 fprintf(stderr,
379 "Visual Class from resource \"visualID\" not available\n");
381 if (visList == NULL && reqDepth != -1) {
382 visTemplate.depth = reqDepth;
383 visList = XGetVisualInfo(display, VisualScreenMask|VisualDepthMask,
384 &visTemplate, &nVis);
385 if (visList == NULL)
386 fprintf(stderr, "Requested visual depth not available\n");
388 if (visList == NULL) {
389 visList = XGetVisualInfo(display, VisualScreenMask, &visTemplate, &nVis);
390 if (visList == NULL) {
391 fprintf(stderr, "Internal Error: no visuals available?\n");
392 *visual = DefaultVisual(display, screen);
393 *depth = DefaultDepth(display, screen);
394 *colormap = DefaultColormap(display, screen);
395 return;
399 /* Choose among the visuals in the candidate list. Prefer maximum
400 depth first then matching default, then largest value of bestClass
401 (I'm not sure whether we actually care about class) */
402 maxDepth = 0;
403 bestClass = 0;
404 bestVisual = 0;
405 for (i=0; i < nVis; i++) {
406 if (visList[i].depth > maxDepth) {
407 maxDepth = visList[i].depth;
408 bestClass = 0;
409 bestVisual = i;
411 if (visList[i].depth == maxDepth) {
412 if (visList[i].visual == DefaultVisual(display, screen))
413 bestVisual = i;
414 if (visList[bestVisual].visual != DefaultVisual(display, screen)) {
415 for (j = 0; j < (int)XtNumber(bestClasses); j++) {
416 if (visList[i].class == bestClasses[j] && j > bestClass) {
417 bestClass = j;
418 bestVisual = i;
424 *visual = cachedVisual = visList[bestVisual].visual;
425 *depth = cachedDepth = visList[bestVisual].depth;
427 /* If the chosen visual is not the default, it needs a colormap allocated */
428 if (*visual == DefaultVisual(display, screen) && !installColormap)
429 *colormap = cachedColormap = DefaultColormap(display, screen);
430 else {
431 *colormap = cachedColormap = XCreateColormap(display,
432 RootWindow(display, screen), cachedVisual, AllocNone);
433 XInstallColormap(display, cachedColormap);
435 /* printf("Chose visual with depth %d, class %d, colormap %ld, id 0x%x\n",
436 visList[bestVisual].depth, visList[bestVisual].class,
437 *colormap, cachedVisual->visualid); */
438 /* Fix memory leak */
439 if (visList != NULL) {
440 XFree(visList);
445 ** If you want to use a non-default visual with Motif, shells all have to be
446 ** created with that visual, depth, and colormap, even if the parent has them
447 ** set up properly. Substituting these routines, will append visual args copied
448 ** from the parent widget (CreatePopupMenu and CreatePulldownMenu), or from the
449 ** best visual, obtained via FindBestVisual above (CreateShellWithBestVis).
451 Widget CreateDialogShell(Widget parent, char *name,
452 ArgList arglist, Cardinal argcount)
454 return addParentVisArgsAndCall(XmCreateDialogShell, parent, name, arglist,
455 argcount);
459 Widget CreatePopupMenu(Widget parent, char *name, ArgList arglist,
460 Cardinal argcount)
462 return addParentVisArgsAndCall(XmCreatePopupMenu, parent, name,
463 arglist, argcount);
467 Widget CreatePulldownMenu(Widget parent, char *name,
468 ArgList arglist, Cardinal argcount)
470 return addParentVisArgsAndCall(XmCreatePulldownMenu, parent, name, arglist,
471 argcount);
475 Widget CreatePromptDialog(Widget parent, char *name,
476 ArgList arglist, Cardinal argcount)
478 return addParentVisArgsAndCall(XmCreatePromptDialog, parent, name, arglist,
479 argcount);
483 Widget CreateSelectionDialog(Widget parent, char *name,
484 ArgList arglist, Cardinal argcount)
486 return addParentVisArgsAndCall(XmCreateSelectionDialog, parent, name,
487 arglist, argcount);
491 Widget CreateFormDialog(Widget parent, char *name,
492 ArgList arglist, Cardinal argcount)
494 return addParentVisArgsAndCall(XmCreateFormDialog, parent, name, arglist,
495 argcount);
499 Widget CreateFileSelectionDialog(Widget parent, char *name,
500 ArgList arglist, Cardinal argcount)
502 return addParentVisArgsAndCall(XmCreateFileSelectionDialog, parent, name,
503 arglist, argcount);
507 Widget CreateQuestionDialog(Widget parent, char *name,
508 ArgList arglist, Cardinal argcount)
510 return addParentVisArgsAndCall(XmCreateQuestionDialog, parent, name,
511 arglist, argcount);
515 Widget CreateMessageDialog(Widget parent, char *name,
516 ArgList arglist, Cardinal argcount)
518 return addParentVisArgsAndCall(XmCreateMessageDialog, parent, name,
519 arglist, argcount);
523 Widget CreateErrorDialog(Widget parent, char *name,
524 ArgList arglist, Cardinal argcount)
526 return addParentVisArgsAndCall(XmCreateErrorDialog, parent, name, arglist,
527 argcount);
531 Widget CreateShellWithBestVis(String appName, String appClass,
532 WidgetClass class, Display *display, ArgList args, Cardinal nArgs)
534 Visual *visual;
535 int depth;
536 Colormap colormap;
537 ArgList al;
538 Cardinal ac = nArgs;
539 Widget result;
541 FindBestVisual(display, appName, appClass, &visual, &depth, &colormap);
542 al = (ArgList)XtMalloc(sizeof(Arg) * (nArgs + 3));
543 if (nArgs != 0)
544 memcpy(al, args, sizeof(Arg) * nArgs);
545 XtSetArg(al[ac], XtNvisual, visual); ac++;
546 XtSetArg(al[ac], XtNdepth, depth); ac++;
547 XtSetArg(al[ac], XtNcolormap, colormap); ac++;
548 result = XtAppCreateShell(appName, appClass, class, display, al, ac);
549 XtFree((char *)al);
550 return result;
554 ** Calls one of the Motif widget creation routines, splicing in additional
555 ** arguments for visual, colormap, and depth.
557 static Widget addParentVisArgsAndCall(MotifDialogCreationCall createRoutine,
558 Widget parent, char *name, ArgList arglist, Cardinal argcount)
560 Visual *visual;
561 int depth;
562 Colormap colormap;
563 ArgList al;
564 Cardinal ac = argcount;
565 Widget result;
566 Widget parentShell = parent;
568 /* Find the application/dialog/menu shell at the top of the widget
569 hierarchy, which has the visual resource being used */
570 while (True) {
571 if (XtIsShell(parentShell))
572 break;
573 if (parentShell == NULL) {
574 fprintf(stderr, "failed to find shell\n");
575 exit(EXIT_FAILURE);
577 parentShell = XtParent(parentShell);
580 /* Add the visual, depth, and colormap resources to the argument list */
581 XtVaGetValues(parentShell, XtNvisual, &visual, XtNdepth, &depth,
582 XtNcolormap, &colormap, NULL);
583 al = (ArgList)XtMalloc(sizeof(Arg) * (argcount + 3));
584 if (argcount != 0)
585 memcpy(al, arglist, sizeof(Arg) * argcount);
586 XtSetArg(al[ac], XtNvisual, visual); ac++;
587 XtSetArg(al[ac], XtNdepth, depth); ac++;
588 XtSetArg(al[ac], XtNcolormap, colormap); ac++;
589 result = (*createRoutine)(parent, name, al, ac);
590 XtFree((char *)al);
591 return result;
595 ** ManageDialogCenteredOnPointer is used in place of XtManageChild for
596 ** popping up a dialog to enable the dialog to be centered under the
597 ** mouse pointer. Whether it pops up the dialog centered under the pointer
598 ** or in its default position centered over the parent widget, depends on
599 ** the value set in the SetPointerCenteredDialogs call.
601 void ManageDialogCenteredOnPointer(Widget dialogChild)
603 Widget shell = XtParent(dialogChild);
604 Window root, child;
605 unsigned int mask;
606 unsigned int width, height, borderWidth, depth;
607 int x, y, winX, winY, maxX, maxY;
608 Boolean mappedWhenManaged;
610 /* If this feature is not enabled, just manage the dialog */
611 if (!PointerCenteredDialogsEnabled) {
612 XtManageChild(dialogChild);
613 return;
616 /* Temporarily set value of XmNmappedWhenManaged
617 to stop the dialog from popping up right away */
618 XtVaGetValues(shell, XmNmappedWhenManaged, &mappedWhenManaged, NULL);
619 XtVaSetValues(shell, XmNmappedWhenManaged, False, NULL);
621 /* Manage the dialog */
622 XtManageChild(dialogChild);
624 /* Get the pointer position (x, y) */
625 XQueryPointer(XtDisplay(shell), XtWindow(shell), &root, &child,
626 &x, &y, &winX, &winY, &mask);
628 /* Translate the pointer position (x, y) into a position for the new
629 window that will place the pointer at its center */
630 XGetGeometry(XtDisplay(shell), XtWindow(shell), &root, &winX, &winY,
631 &width, &height, &borderWidth, &depth);
632 width += 2 * borderWidth;
633 height += 2 * borderWidth;
634 x -= width/2;
635 y -= height/2;
637 /* Ensure that the dialog remains on screen */
638 maxX = XtScreen(shell)->width - width;
639 maxY = XtScreen(shell)->height - height;
640 if (x < 0) x = 0;
641 if (x > maxX) x = maxX;
642 if (y < 0) y = 0;
643 if (y > maxY) y = maxY;
645 /* Set desired window position in the DialogShell */
646 XtVaSetValues(shell, XmNx, x, XmNy, y, NULL);
648 /* Map the widget */
649 XtMapWidget(shell);
651 /* Restore the value of XmNmappedWhenManaged */
652 XtVaSetValues(shell, XmNmappedWhenManaged, mappedWhenManaged, NULL);
656 ** Cause dialogs created by libNUtil.a routines (such as DialogF and
657 ** GetNewFilename), and dialogs which use ManageDialogCenteredOnPointer
658 ** to pop up over the pointer (state = True), or pop up in their default
659 ** positions (state = False)
661 void SetPointerCenteredDialogs(int state)
663 PointerCenteredDialogsEnabled = state;
668 ** Raise a window to the top and give it the input focus. Setting input focus
669 ** is important on systems which use explict (rather than pointer) focus.
671 ** The X alternatives XMapRaised, and XSetInputFocus both have problems.
672 ** XMapRaised only gives the window the focus if it was initially not visible,
673 ** and XSetInputFocus sets the input focus, but crashes if the window is not
674 ** visible.
676 ** This routine should also be used in the case where a dialog is popped up and
677 ** subsequent calls to the dialog function use a flag, or the XtIsManaged, to
678 ** decide whether to create a new instance of the dialog, because on slower
679 ** systems, events can intervene while a dialog is still on its way up and its
680 ** window is still invisible, causing a subtle crash potential if
681 ** XSetInputFocus is used.
683 void RaiseShellWindow(Widget shell)
685 RaiseWindow(XtDisplay(shell), XtWindow(shell));
689 void RaiseWindow(Display *display, Window w)
691 XWindowAttributes winAttr;
693 XGetWindowAttributes(display, w, &winAttr);
694 if (winAttr.map_state == IsViewable)
695 XSetInputFocus(display, w, RevertToParent, CurrentTime);
696 XMapRaised(display, w);
700 ** Add a handler for mnemonics in a dialog (Motif currently only handles
701 ** mnemonics in menus) following the example of M.S. Windows. To add
702 ** mnemonics to a dialog, set the XmNmnemonic resource, as you would in
703 ** a menu, on push buttons or toggle buttons, and call this function
704 ** when the dialog is fully constructed. Mnemonics added or changed
705 ** after this call will not be noticed. To add a mnemonic to a text field
706 ** or list, set the XmNmnemonic resource on the appropriate label and set
707 ** the XmNuserData resource of the label to the widget to get the focus
708 ** when the mnemonic is typed.
710 void AddDialogMnemonicHandler(Widget dialog, int unmodifiedToo)
712 XtAddEventHandler(dialog, KeyPressMask, False,
713 (XtEventHandler)mnemonicCB, (XtPointer)0);
714 addMnemonicGrabs(dialog, dialog, unmodifiedToo);
718 ** Removes the event handler and key-grabs added by AddDialogMnemonicHandler
720 void RemoveDialogMnemonicHandler(Widget dialog)
722 XtUngrabKey(dialog, AnyKey, Mod1Mask);
723 XtRemoveEventHandler(dialog, KeyPressMask, False,
724 (XtEventHandler)mnemonicCB, (XtPointer)0);
728 ** Patch around Motif's poor handling of menu accelerator keys. Motif
729 ** does not process menu accelerators when the caps lock or num lock
730 ** keys are engaged. To enable accelerators in these cases, call this
731 ** routine with the completed menu bar widget as "topMenuContainer", and
732 ** the top level shell widget as "topWidget". It will add key grabs for
733 ** all of the accelerators it finds in the topMenuContainer menu tree, and
734 ** an event handler which can process dropped accelerator events by (again)
735 ** traversing the menu tree looking for matching accelerators, and invoking
736 ** the appropriate button actions. Any dynamic additions to the menus
737 ** require a call to UpdateAccelLockPatch to add the additional grabs.
738 ** Unfortunately, these grabs can not be removed.
740 void AccelLockBugPatch(Widget topWidget, Widget topMenuContainer)
742 XtAddEventHandler(topWidget, KeyPressMask, False, lockCB, topMenuContainer);
743 addAccelGrabs(topWidget, topMenuContainer);
747 ** Add additional key grabs for new menu items added to the menus, for
748 ** patching around the Motif Caps/Num Lock problem. "topWidget" must be
749 ** the same widget passed in the original call to AccelLockBugPatch.
751 void UpdateAccelLockPatch(Widget topWidget, Widget newButton)
753 addAccelGrab(topWidget, newButton);
757 ** PopDownBugPatch
759 ** Under some circumstances, popping down a dialog and its parent in
760 ** rapid succession causes a crash. This routine delays and
761 ** processs events until receiving a ReparentNotify event.
762 ** (I have no idea why a ReparentNotify event occurs at all, but it does
763 ** mark the point where it is safe to destroy or pop down the parent, and
764 ** it might have something to do with the bug.) There is a failsafe in
765 ** the form of a ~1.5 second timeout in case no ReparentNotify arrives.
766 ** Use this sparingly, only when real crashes are observed, and periodically
767 ** check to make sure that it is still necessary.
769 void PopDownBugPatch(Widget w)
771 time_t stopTime;
773 stopTime = time(NULL) + 1;
774 while (time(NULL) <= stopTime) {
775 XEvent event;
776 XtAppContext context = XtWidgetToApplicationContext(w);
777 XtAppPeekEvent(context, &event);
778 if (event.xany.type == ReparentNotify)
779 return;
780 XtAppProcessEvent(context, XtIMAll);
785 ** Convert a compound string to a C style null terminated string.
786 ** Returned string must be freed by the caller.
788 char *GetXmStringText(XmString fromString)
790 XmStringContext context;
791 char *text, *toPtr, *toString, *fromPtr;
792 XmStringCharSet charset;
793 XmStringDirection direction;
794 Boolean separator;
796 /* Malloc a buffer large enough to hold the string. XmStringLength
797 should always be slightly longer than necessary, but won't be
798 shorter than the equivalent null-terminated string */
799 toString = XtMalloc(XmStringLength(fromString));
801 /* loop over all of the segments in the string, copying each segment
802 into the output string and converting separators into newlines */
803 XmStringInitContext(&context, fromString);
804 toPtr = toString;
805 while (XmStringGetNextSegment(context, &text,
806 &charset, &direction, &separator)) {
807 for (fromPtr=text; *fromPtr!='\0'; fromPtr++)
808 *toPtr++ = *fromPtr;
809 if (separator)
810 *toPtr++ = '\n';
813 /* terminate the string, free the context, and return the string */
814 *toPtr++ = '\0';
815 XmStringFreeContext(context);
816 return toString;
820 ** Get the XFontStruct that corresponds to the default (first) font in
821 ** a Motif font list. Since Motif stores this, it saves us from storing
822 ** it or querying it from the X server.
824 XFontStruct *GetDefaultFontStruct(XmFontList font)
826 XFontStruct *fs;
827 XmFontContext context;
828 XmStringCharSet charset;
830 XmFontListInitFontContext(&context, font);
831 XmFontListGetNextFont(context, &charset, &fs);
832 XmFontListFreeFontContext(context);
833 XtFree(charset);
834 return fs;
838 ** Create a string table suitable for passing to XmList widgets
840 XmString* StringTable(int count, ... )
842 va_list ap;
843 XmString *array;
844 int i;
845 char *str;
847 va_start(ap, count);
848 array = (XmString*)XtMalloc((count+1) * sizeof(XmString));
849 for(i = 0; i < count; i++ ) {
850 str = va_arg(ap, char *);
851 array[i] = XmStringCreateSimple(str);
853 array[i] = (XmString)0;
854 va_end(ap);
855 return(array);
858 void FreeStringTable(XmString *table)
860 int i;
862 for(i = 0; table[i] != 0; i++)
863 XmStringFree(table[i]);
864 XtFree((char *)table);
868 ** Simulate a button press. The purpose of this routine is show users what
869 ** is happening when they take an action with a non-obvious side effect,
870 ** such as when a user double clicks on a list item. The argument is an
871 ** XmPushButton widget to "press"
873 void SimulateButtonPress(Widget widget)
875 XEvent keyEvent;
877 memset((char *)&keyEvent, 0, sizeof(XKeyPressedEvent));
878 keyEvent.type = KeyPress;
879 keyEvent.xkey.serial = 1;
880 keyEvent.xkey.send_event = True;
882 if (XtIsSubclass(widget, xmGadgetClass))
884 /* On some Motif implementations, asking a gadget for its
885 window will crash, rather than return the window of its
886 parent. */
887 Widget parent = XtParent(widget);
888 keyEvent.xkey.display = XtDisplay(parent);
889 keyEvent.xkey.window = XtWindow(parent);
891 XtCallActionProc(parent, "ManagerGadgetSelect",
892 &keyEvent, NULL, 0);
894 else
896 keyEvent.xkey.display = XtDisplay(widget);
897 keyEvent.xkey.window = XtWindow(widget);
899 XtCallActionProc(widget, "ArmAndActivate", &keyEvent, NULL, 0);
904 ** Add an item to an already established pull-down or pop-up menu, including
905 ** mnemonics, accelerators and callbacks.
907 Widget AddMenuItem(Widget parent, char *name, char *label,
908 char mnemonic, char *acc, char *accText,
909 XtCallbackProc callback, void *cbArg)
911 Widget button;
912 XmString st1, st2;
914 button = XtVaCreateManagedWidget(name, xmPushButtonWidgetClass, parent,
915 XmNlabelString, st1=XmStringCreateSimple(label),
916 XmNmnemonic, mnemonic,
917 XmNacceleratorText, st2=XmStringCreateSimple(accText),
918 XmNaccelerator, acc, NULL);
919 XtAddCallback(button, XmNactivateCallback, callback, cbArg);
920 XmStringFree(st1);
921 XmStringFree(st2);
922 return button;
926 ** Add a toggle button item to an already established pull-down or pop-up
927 ** menu, including mnemonics, accelerators and callbacks.
929 Widget AddMenuToggle(Widget parent, char *name, char *label,
930 char mnemonic, char *acc, char *accText,
931 XtCallbackProc callback, void *cbArg, int set)
933 Widget button;
934 XmString st1, st2;
936 button = XtVaCreateManagedWidget(name, xmToggleButtonWidgetClass, parent,
937 XmNlabelString, st1=XmStringCreateSimple(label),
938 XmNmnemonic, mnemonic,
939 XmNacceleratorText, st2=XmStringCreateSimple(accText),
940 XmNaccelerator, acc,
941 XmNset, set, NULL);
942 XtAddCallback(button, XmNvalueChangedCallback, callback, cbArg);
943 XmStringFree(st1);
944 XmStringFree(st2);
945 return button;
949 ** Add a separator line to a menu
951 Widget AddMenuSeparator(Widget parent, char *name)
953 Widget button;
955 button = XmCreateSeparator(parent, name, NULL, 0);
956 XtManageChild(button);
957 return button;
961 ** Add a sub-menu to an established pull-down or pop-up menu, including
962 ** mnemonics, accelerators and callbacks. Returns the menu pane of the
963 ** new sub menu.
965 Widget AddSubMenu(Widget parent, char *name, char *label, char mnemonic)
967 Widget menu;
968 XmString st1;
970 menu = CreatePulldownMenu(parent, name, NULL, 0);
971 XtVaCreateManagedWidget(name, xmCascadeButtonWidgetClass, parent,
972 XmNlabelString, st1=XmStringCreateSimple(label),
973 XmNmnemonic, mnemonic,
974 XmNsubMenuId, menu, NULL);
975 XmStringFree(st1);
976 return menu;
980 ** SetIntLabel, SetFloatLabel, SetIntText, SetFloatText
982 ** Set the text of a motif label or text widget to show an integer or
983 ** floating number.
985 void SetIntLabel(Widget label, int value)
987 char labelString[20];
988 XmString s1;
990 sprintf(labelString, "%d", value);
991 s1=XmStringCreateSimple(labelString);
992 XtVaSetValues(label, XmNlabelString, s1, NULL);
993 XmStringFree(s1);
997 void SetFloatLabel(Widget label, double value)
999 char labelString[20];
1000 XmString s1;
1002 sprintf(labelString, "%g", value);
1003 s1=XmStringCreateSimple(labelString);
1004 XtVaSetValues(label, XmNlabelString, s1, NULL);
1005 XmStringFree(s1);
1009 void SetIntText(Widget text, int value)
1011 char labelString[20];
1013 sprintf(labelString, "%d", value);
1014 XmTextSetString(text, labelString);
1018 void SetFloatText(Widget text, double value)
1020 char labelString[20];
1022 sprintf(labelString, "%g", value);
1023 XmTextSetString(text, labelString);
1027 ** GetIntText, GetFloatText, GetIntTextWarn, GetFloatTextWarn
1029 ** Get the text of a motif text widget as an integer or floating point number.
1030 ** The functions will return TEXT_READ_OK of the value was read correctly.
1031 ** If not, they will return either TEXT_IS_BLANK, or TEXT_NOT_NUMBER. The
1032 ** GetIntTextWarn and GetFloatTextWarn will display a dialog warning the
1033 ** user that the value could not be read. The argument fieldName is used
1034 ** in the dialog to help the user identify where the problem is. Set
1035 ** warnBlank to true if a blank field is also considered an error.
1037 int GetFloatText(Widget text, double *value)
1039 char *strValue, *endPtr;
1040 int retVal;
1042 strValue = XmTextGetString(text); /* Get Value */
1043 removeWhiteSpace(strValue); /* Remove blanks and tabs */
1044 *value = strtod(strValue, &endPtr); /* Convert string to double */
1045 if (strlen(strValue) == 0) /* String is empty */
1046 retVal = TEXT_IS_BLANK;
1047 else if (*endPtr != '\0') /* Whole string not parsed */
1048 retVal = TEXT_NOT_NUMBER;
1049 else
1050 retVal = TEXT_READ_OK;
1051 XtFree(strValue);
1052 return retVal;
1055 int GetIntText(Widget text, int *value)
1057 char *strValue, *endPtr;
1058 int retVal;
1060 strValue = XmTextGetString(text); /* Get Value */
1061 removeWhiteSpace(strValue); /* Remove blanks and tabs */
1062 *value = strtol(strValue, &endPtr, 10); /* Convert string to long */
1063 if (strlen(strValue) == 0) /* String is empty */
1064 retVal = TEXT_IS_BLANK;
1065 else if (*endPtr != '\0') /* Whole string not parsed */
1066 retVal = TEXT_NOT_NUMBER;
1067 else
1068 retVal = TEXT_READ_OK;
1069 XtFree(strValue);
1070 return retVal;
1073 int GetFloatTextWarn(Widget text, double *value, const char *fieldName,
1074 int warnBlank)
1076 int result;
1077 char *valueStr;
1079 result = GetFloatText(text, value);
1080 if (result == TEXT_READ_OK || (result == TEXT_IS_BLANK && !warnBlank))
1081 return result;
1082 valueStr = XmTextGetString(text);
1083 if (result == TEXT_IS_BLANK)
1084 DialogF (DF_ERR, text, 1, "Please supply %s value",
1085 "Dismiss", fieldName);
1086 else /* TEXT_NOT_NUMBER */
1087 DialogF (DF_ERR, text, 1,
1088 "Can't read %s value: \"%s\"",
1089 "Dismiss", fieldName, valueStr);
1090 XtFree(valueStr);
1091 return result;
1094 int GetIntTextWarn(Widget text, int *value, const char *fieldName, int warnBlank)
1096 int result;
1097 char *valueStr;
1099 result = GetIntText(text, value);
1100 if (result == TEXT_READ_OK || (result == TEXT_IS_BLANK && !warnBlank))
1101 return result;
1102 valueStr = XmTextGetString(text);
1103 if (result == TEXT_IS_BLANK)
1104 DialogF (DF_ERR, text, 1, "Please supply a value for %s",
1105 "Dismiss", fieldName);
1106 else /* TEXT_NOT_NUMBER */
1107 DialogF (DF_ERR, text, 1,
1108 "Can't read integer value \"%s\" in %s",
1109 "Dismiss", valueStr, fieldName);
1110 XtFree(valueStr);
1111 return result;
1114 int TextWidgetIsBlank(Widget textW)
1116 char *str;
1117 int retVal;
1119 str = XmTextGetString(textW);
1120 removeWhiteSpace(str);
1121 retVal = *str == '\0';
1122 XtFree(str);
1123 return retVal;
1127 ** Turn a multi-line editing text widget into a fake single line text area
1128 ** by disabling the translation for Return. This is a way to give users
1129 ** extra space, by allowing wrapping, but still prohibiting newlines.
1130 ** (SINGLE_LINE_EDIT mode can't be used, in this case, because it forces
1131 ** the widget to be one line high).
1133 void MakeSingleLineTextW(Widget textW)
1135 static XtTranslations noReturnTable = NULL;
1136 static char *noReturnTranslations = "<Key>Return: activate()\n";
1138 if (noReturnTable == NULL)
1139 noReturnTable = XtParseTranslationTable(noReturnTranslations);
1140 XtOverrideTranslations(textW, noReturnTable);
1144 ** Add up-arrow/down-arrow recall to a single line text field. When user
1145 ** presses up-arrow, string is cleared and recent entries are presented,
1146 ** moving to older ones as each successive up-arrow is pressed. Down-arrow
1147 ** moves to more recent ones, final down-arrow clears the field. Associated
1148 ** routine, AddToHistoryList, makes maintaining a history list easier.
1150 ** Arguments are the widget, and pointers to the history list and number of
1151 ** items, which are expected to change periodically.
1153 void AddHistoryToTextWidget(Widget textW, char ***historyList, int *nItems)
1155 histInfo *histData;
1157 /* create a data structure for passing history info to the callbacks */
1158 histData = (histInfo *)XtMalloc(sizeof(histInfo));
1159 histData->list = historyList;
1160 histData->nItems = nItems;
1161 histData->index = -1;
1163 /* Add an event handler for handling up/down arrow events */
1164 XtAddEventHandler(textW, KeyPressMask, False,
1165 (XtEventHandler)histArrowKeyEH, histData);
1167 /* Add a destroy callback for freeing history data structure */
1168 XtAddCallback(textW, XmNdestroyCallback, histDestroyCB, histData);
1171 static void histDestroyCB(Widget w, XtPointer clientData, XtPointer callData)
1173 XtFree((char *)clientData);
1176 static void histArrowKeyEH(Widget w, XtPointer callData, XEvent *event,
1177 Boolean *continueDispatch)
1179 histInfo *histData = (histInfo *)callData;
1180 KeySym keysym = XLookupKeysym((XKeyEvent *)event, 0);
1182 /* only process up and down arrow keys */
1183 if (keysym != XK_Up && keysym != XK_Down)
1184 return;
1186 /* increment or decrement the index depending on which arrow was pressed */
1187 histData->index += (keysym == XK_Up) ? 1 : -1;
1189 /* if the index is out of range, beep, fix it up, and return */
1190 if (histData->index < -1) {
1191 histData->index = -1;
1192 XBell(XtDisplay(w), 0);
1193 return;
1195 if (histData->index >= *histData->nItems) {
1196 histData->index = *histData->nItems - 1;
1197 XBell(XtDisplay(w), 0);
1198 return;
1201 /* Change the text field contents */
1202 XmTextSetString(w, histData->index == -1 ? "" :
1203 (*histData->list)[histData->index]);
1207 ** Copies a string on to the end of history list, which may be reallocated
1208 ** to make room. If historyList grows beyond its internally set boundary
1209 ** for size (HISTORY_LIST_MAX), it is trimmed back to a smaller size
1210 ** (HISTORY_LIST_TRIM_TO). Before adding to the list, checks if the item
1211 ** is a duplicate of the last item. If so, it is not added.
1213 void AddToHistoryList(char *newItem, char ***historyList, int *nItems)
1215 char **newList;
1216 int i;
1218 if (*nItems != 0 && !strcmp(newItem, **historyList))
1219 return;
1220 if (*nItems == HISTORY_LIST_MAX) {
1221 for (i=HISTORY_LIST_TRIM_TO; i<HISTORY_LIST_MAX; i++)
1222 XtFree((*historyList)[i]);
1223 *nItems = HISTORY_LIST_TRIM_TO;
1225 newList = (char **)XtMalloc(sizeof(char *) * (*nItems + 1));
1226 for (i=0; i < *nItems; i++)
1227 newList[i+1] = (*historyList)[i];
1228 if (*nItems != 0 && *historyList != NULL)
1229 XtFree((char *)*historyList);
1230 (*nItems)++;
1231 newList[0] = XtNewString(newItem);
1232 *historyList = newList;
1236 * PasswordText - routine to add a callback to any text widget so that all
1237 * text typed by the user is echoed with asterisks, allowing
1238 * a password to be typed in without being seen.
1240 * parameters: w - text widget to add the callback to
1241 * passTxt - pointer to a string created by caller of this routine.
1242 * **NOTE** The length of this string should be one
1243 * greater than the maximum specified by XmNmaxLength.
1244 * This string is set to empty just before the callback
1245 * is added.
1248 void PasswordText(Widget w, char *passTxt)
1250 passTxt[0] = '\0';
1251 XtAddCallback(w, XmNmodifyVerifyCallback, (XtCallbackProc)passwdCB,passTxt);
1255 ** BeginWait/EndWait
1257 ** Display/Remove a watch cursor over topCursorWidget and its descendents
1259 void BeginWait(Widget topCursorWidget)
1261 Display *display = XtDisplay(topCursorWidget);
1262 Pixmap pixmap;
1263 Pixmap maskPixmap;
1264 XColor xcolors[2];
1265 static Cursor waitCursor = 0;
1267 /* if the watch cursor hasn't been created yet, create it */
1268 if (!waitCursor) {
1269 pixmap = XCreateBitmapFromData(display, DefaultRootWindow(display),
1270 (char *)watch_bits, watch_width, watch_height);
1272 maskPixmap = XCreateBitmapFromData(display, DefaultRootWindow(display),
1273 (char *)watch_mask_bits, watch_width, watch_height);
1275 xcolors[0].pixel = BlackPixelOfScreen(DefaultScreenOfDisplay(display));
1276 xcolors[1].pixel = WhitePixelOfScreen(DefaultScreenOfDisplay(display));
1278 XQueryColors(display, DefaultColormapOfScreen(
1279 DefaultScreenOfDisplay(display)), xcolors, 2);
1280 waitCursor = XCreatePixmapCursor(display, pixmap, maskPixmap,
1281 &xcolors[0], &xcolors[1], watch_x_hot, watch_y_hot);
1282 XFreePixmap(display, pixmap);
1283 XFreePixmap(display, maskPixmap);
1286 /* display the cursor */
1287 XDefineCursor(display, XtWindow(topCursorWidget), waitCursor);
1290 void EndWait(Widget topCursorWidget)
1292 XUndefineCursor(XtDisplay(topCursorWidget), XtWindow(topCursorWidget));
1296 ** Create an X window geometry string from width, height, x, and y values.
1297 ** This is a complement to the X routine XParseGeometry, and uses the same
1298 ** bitmask values (XValue, YValue, WidthValue, HeightValue, XNegative, and
1299 ** YNegative) as defined in <X11/Xutil.h> and documented under XParseGeometry.
1300 ** It expects a string of at least MAX_GEOMETRY_STRING_LEN in which to write
1301 ** result. Note that in a geometry string, it is not possible to supply a y
1302 ** position without an x position. Also note that the X/YNegative flags
1303 ** mean "add a '-' and negate the value" which is kind of odd.
1305 void CreateGeometryString(char *string, int x, int y,
1306 int width, int height, int bitmask)
1308 char *ptr = string;
1309 int nChars;
1311 if (bitmask & WidthValue) {
1312 sprintf(ptr, "%d%n", width, &nChars);
1313 ptr += nChars;
1315 if (bitmask & HeightValue) {
1316 sprintf(ptr, "x%d%n", height, &nChars);
1317 ptr += nChars;
1319 if (bitmask & XValue) {
1320 if (bitmask & XNegative)
1321 sprintf(ptr, "-%d%n", -x, &nChars);
1322 else
1323 sprintf(ptr, "+%d%n", x, &nChars);
1324 ptr += nChars;
1326 if (bitmask & YValue) {
1327 if (bitmask & YNegative)
1328 sprintf(ptr, "-%d%n", -y, &nChars);
1329 else
1330 sprintf(ptr, "+%d%n", y, &nChars);
1331 ptr += nChars;
1333 *ptr = '\0';
1336 /* */
1337 /* passwdCB: callback routine added by PasswordText routine. This routine */
1338 /* echoes each character typed as an asterisk (*) and a few other */
1339 /* necessary things so that the password typed in is not visible */
1340 /* */
1341 static void passwdCB(Widget w, char * passTxt, XmTextVerifyCallbackStruct
1342 *txtVerStr)
1344 /* XmTextVerifyCallbackStruct: */
1345 /* int reason; should be XmCR_MODIFYING_TEXT_VALUE */
1346 /* XEvent *event; points to XEvent that triggered the callback */
1347 /* Boolean doit; indicates whether action should be performed; setting */
1348 /* this to false negates the action */
1349 /* long currInsert, current position of insert cursor */
1350 /* newInsert; position user attempts to position the insert cursor */
1351 /* long startPos, starting position of the text to modify */
1352 /* endPos; ending position of the text to modify */
1353 /* XmTextBlock text; */
1355 /* XmTextBlock (used to pass text around): */
1356 /* char *ptr; points to text to be inserted */
1357 /* int length; Number of bytes (length) */
1358 /* XmTextFormat format; Representations format */
1360 /* XmTextFormat: either FMT8BIT or FMT16BIT */
1363 int numCharsTyped, i, j, pos;
1365 /* ensure text format is 8-bit characters */
1366 if (txtVerStr->text->format != FMT8BIT)
1367 return;
1369 /* verify assumptions */
1370 /* if (txtVerStr->endPos < txtVerStr->startPos)
1371 fprintf(stderr, "User password callback error: endPos < startPos\n");
1372 if (strlen(passTxt) == 0 && txtVerStr->endPos != 0)
1373 fprintf(stderr, "User password callback error: no txt, but end != 0\n");
1375 printf("passTxt = %s, startPos = %d, endPos = %d, txtBlkAddr = %d\n",
1376 passTxt, txtVerStr->startPos, txtVerStr->endPos, txtVerStr->text);
1377 if (txtVerStr->text != NULL && txtVerStr->text->ptr != NULL)
1378 printf(" string typed = %s, length = %d\n", txtVerStr->text->ptr,
1379 txtVerStr->text->length);
1381 /* If necessary, expand/compress passTxt and insert any new text */
1382 if (txtVerStr->text != NULL && txtVerStr->text->ptr != NULL)
1383 numCharsTyped = txtVerStr->text->length;
1384 else
1385 numCharsTyped = 0;
1386 /* numCharsTyped = # chars to insert (that user typed) */
1387 /* j = # chars to expand (+) or compress (-) the password string */
1388 j = numCharsTyped - (txtVerStr->endPos - txtVerStr->startPos);
1389 if (j > 0) /* expand case: start at ending null */
1390 for (pos = strlen(passTxt) + 1; pos >= txtVerStr->endPos; --pos)
1391 passTxt[pos+j] = passTxt[pos];
1392 if (j < 0) /* compress case */
1393 for (pos = txtVerStr->startPos + numCharsTyped;
1394 pos <= (int)strlen(passTxt)+1; ++pos)
1395 passTxt[pos] = passTxt[pos-j];
1396 /* then copy text to be inserted into passTxt */
1397 for (pos = txtVerStr->startPos, i = 0; i < numCharsTyped; ++i) {
1398 passTxt[pos+i] = *(txtVerStr->text->ptr + i);
1399 /* Replace text typed by user with asterisks (*) */
1400 *(txtVerStr->text->ptr + i) = '*';
1402 /* printf(" Password string now = %s\n", passTxt); */
1406 ** Remove the white space (blanks and tabs) from a string
1408 static void removeWhiteSpace(char *string)
1410 char *outPtr = string;
1412 while (TRUE) {
1413 if (*string == 0) {
1414 *outPtr = 0;
1415 return;
1416 } else if (*string != ' ' && *string != '\t')
1417 *(outPtr++) = *(string++);
1418 else
1419 string++;
1424 ** Compares two strings and return TRUE if the two strings
1425 ** are the same, ignoring whitespace and case differences.
1427 static int stripCaseCmp(const char *str1, const char *str2)
1429 const char *c1, *c2;
1431 for (c1=str1, c2=str2; *c1!='\0' && *c2!='\0'; c1++, c2++) {
1432 while (*c1 == ' ' || *c1 == '\t')
1433 c1++;
1434 while (*c2 == ' ' || *c2 == '\t')
1435 c2++;
1436 if (toupper((unsigned char)*c1) != toupper((unsigned char)*c2))
1437 return FALSE;
1439 return *c1 == '\0' && *c2 == '\0';
1442 static void warnHandlerCB(String message)
1444 if (strstr(message, "XtRemoveGrab"))
1445 return;
1446 if (strstr(message, "Attempt to remove non-existant passive grab"))
1447 return;
1448 fputs(message, stderr);
1449 fputc('\n', stderr);
1452 static XModifierKeymap *getKeyboardMapping(Display *display) {
1453 static XModifierKeymap *keyboardMap = NULL;
1455 if (keyboardMap == NULL) {
1456 keyboardMap = XGetModifierMapping(display);
1458 return(keyboardMap);
1462 ** get mask for a modifier
1466 static Modifiers findModifierMapping(Display *display, KeyCode keyCode) {
1467 int i, j;
1468 KeyCode *mapentry;
1469 XModifierKeymap *modMap = getKeyboardMapping(display);
1471 if (modMap == NULL || keyCode == 0) {
1472 return(0);
1475 mapentry = modMap->modifiermap;
1476 for (i = 0; i < 8; ++i) {
1477 for (j = 0; j < (modMap->max_keypermod); ++j) {
1478 if (keyCode == *mapentry) {
1479 return(1 << ((mapentry - modMap->modifiermap) / modMap->max_keypermod));
1481 ++mapentry;
1484 return(0);
1487 static Modifiers getNumLockModMask(Display *display) {
1488 static int numLockMask = -1;
1490 if (numLockMask == -1) {
1491 numLockMask = findModifierMapping(display, XKeysymToKeycode(display, XK_Num_Lock));
1493 return(numLockMask);
1497 ** Grab a key regardless of caps-lock and other silly latching keys.
1501 static void reallyGrabAKey(Widget dialog, int keyCode, Modifiers mask) {
1502 Modifiers numLockMask = getNumLockModMask(XtDisplay(dialog));
1504 if (keyCode == 0) /* No anykey grabs, sorry */
1505 return;
1507 XtGrabKey(dialog, keyCode, mask, True, GrabModeAsync, GrabModeAsync);
1508 XtGrabKey(dialog, keyCode, mask|LockMask, True, GrabModeAsync, GrabModeAsync);
1509 if (numLockMask && numLockMask != LockMask) {
1510 XtGrabKey(dialog, keyCode, mask|numLockMask, True, GrabModeAsync, GrabModeAsync);
1511 XtGrabKey(dialog, keyCode, mask|LockMask|numLockMask, True, GrabModeAsync, GrabModeAsync);
1516 ** Part of dialog mnemonic processing. Search the widget tree under w
1517 ** for widgets with mnemonics. When found, add a passive grab to the
1518 ** dialog widget for the mnemonic character, thus directing mnemonic
1519 ** events to the dialog widget.
1521 static void addMnemonicGrabs(Widget dialog, Widget w, int unmodifiedToo)
1523 char mneString[2];
1524 WidgetList children;
1525 Cardinal numChildren;
1526 int i, isMenu;
1527 KeySym mnemonic = '\0';
1528 unsigned char rowColType;
1529 unsigned int keyCode;
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<(int)numChildren; i++)
1541 addMnemonicGrabs(dialog, children[i], unmodifiedToo);
1543 } else {
1544 XtVaGetValues(w, XmNmnemonic, &mnemonic, NULL);
1545 if (mnemonic != XK_VoidSymbol && mnemonic != '\0') {
1546 mneString[0] = mnemonic; mneString[1] = '\0';
1547 keyCode = XKeysymToKeycode(XtDisplay(dialog),
1548 XStringToKeysym(mneString));
1549 reallyGrabAKey(dialog, keyCode, Mod1Mask);
1550 if (unmodifiedToo)
1551 reallyGrabAKey(dialog, keyCode, 0);
1557 ** Callback routine for dialog mnemonic processing.
1559 static void mnemonicCB(Widget w, XtPointer callData, XKeyEvent *event)
1561 findAndActivateMnemonic(w, event->keycode);
1565 ** Look for a widget in the widget tree w, with a mnemonic matching
1566 ** keycode. When one is found, simulate a button press on that widget
1567 ** and give it the keyboard focus. If the mnemonic is on a label,
1568 ** look in the userData field of the label to see if it points to
1569 ** another widget, and give that the focus. This routine is just
1570 ** sufficient for NEdit, no doubt it will need to be extended for
1571 ** mnemonics on widgets other than just buttons and text fields.
1573 static void findAndActivateMnemonic(Widget w, unsigned int keycode)
1575 WidgetList children;
1576 Cardinal numChildren;
1577 int i, isMenu;
1578 KeySym mnemonic = '\0';
1579 char mneString[2];
1580 Widget userData;
1581 unsigned char rowColType;
1583 if (XtIsComposite(w)) {
1584 if (XtClass(w) == xmRowColumnWidgetClass) {
1585 XtVaGetValues(w, XmNrowColumnType, &rowColType, NULL);
1586 isMenu = rowColType != XmWORK_AREA;
1587 } else
1588 isMenu = False;
1589 if (!isMenu) {
1590 XtVaGetValues(w, XmNchildren, &children, XmNnumChildren,
1591 &numChildren, NULL);
1592 for (i=0; i<(int)numChildren; i++)
1593 findAndActivateMnemonic(children[i], keycode);
1595 } else {
1596 XtVaGetValues(w, XmNmnemonic, &mnemonic, NULL);
1597 if (mnemonic != '\0') {
1598 mneString[0] = mnemonic; mneString[1] = '\0';
1599 if (XKeysymToKeycode(XtDisplay(XtParent(w)),
1600 XStringToKeysym(mneString)) == keycode) {
1601 if (XtClass(w) == xmLabelWidgetClass ||
1602 XtClass(w) == xmLabelGadgetClass) {
1603 XtVaGetValues(w, XmNuserData, &userData, NULL);
1604 if (userData!=NULL && XtIsWidget(userData) &&
1605 XmIsTraversable(userData))
1606 XmProcessTraversal(userData, XmTRAVERSE_CURRENT);
1607 } else if (XmIsTraversable(w)) {
1608 XmProcessTraversal(w, XmTRAVERSE_CURRENT);
1609 SimulateButtonPress(w);
1617 ** Part of workaround for Motif Caps/Num Lock bug. Search the widget tree
1618 ** under w for widgets with accelerators. When found, add three passive
1619 ** grabs to topWidget, one for the accelerator keysym + modifiers + Caps
1620 ** Lock, one for Num Lock, and one for both, thus directing lock +
1621 ** accelerator events to topWidget.
1623 static void addAccelGrabs(Widget topWidget, Widget w)
1625 WidgetList children;
1626 Widget menu;
1627 Cardinal numChildren;
1628 int i;
1630 if (XtIsComposite(w)) {
1631 XtVaGetValues(w, XmNchildren, &children, XmNnumChildren,
1632 &numChildren, NULL);
1633 for (i=0; i<(int)numChildren; i++)
1634 addAccelGrabs(topWidget, children[i]);
1635 } else if (XtClass(w) == xmCascadeButtonWidgetClass) {
1636 XtVaGetValues(w, XmNsubMenuId, &menu, NULL);
1637 if (menu != NULL)
1638 addAccelGrabs(topWidget, menu);
1639 } else
1640 addAccelGrab(topWidget, w);
1644 ** Grabs the key + modifier defined in the widget's accelerator resource,
1645 ** in combination with the Caps Lock and Num Lock accelerators.
1647 static void addAccelGrab(Widget topWidget, Widget w)
1649 char *accelString = NULL;
1650 KeySym keysym;
1651 unsigned int modifiers;
1652 KeyCode code;
1653 Modifiers numLockMask = getNumLockModMask(XtDisplay(topWidget));
1655 XtVaGetValues(w, XmNaccelerator, &accelString, NULL);
1656 if (accelString == NULL || *accelString == '\0') {
1657 if (accelString != NULL) XtFree(accelString);
1658 return;
1661 if (!parseAccelString(XtDisplay(topWidget), accelString, &keysym, &modifiers)) {
1662 XtFree(accelString);
1663 return;
1665 XtFree(accelString);
1667 /* Check to see if this server has this key mapped. Some cruddy PC X
1668 servers (Xoftware) have terrible default keymaps. If not,
1669 XKeysymToKeycode will return 0. However, it's bad news to pass
1670 that to XtGrabKey because 0 is really "AnyKey" which is definitely
1671 not what we want!! */
1673 code = XKeysymToKeycode(XtDisplay(topWidget), keysym);
1674 if (code == 0)
1675 return;
1677 XtGrabKey(topWidget, code,
1678 modifiers | LockMask, True, GrabModeAsync, GrabModeAsync);
1679 if (numLockMask && numLockMask != LockMask) {
1680 XtGrabKey(topWidget, code,
1681 modifiers | numLockMask, True, GrabModeAsync, GrabModeAsync);
1682 XtGrabKey(topWidget, code,
1683 modifiers | LockMask | numLockMask, True, GrabModeAsync, GrabModeAsync);
1688 ** Read a Motif accelerator string and translate it into a keysym + modifiers.
1689 ** Returns TRUE if the parse was successful, FALSE, if not.
1691 static int parseAccelString(Display *display, const char *string, KeySym *keySym,
1692 unsigned int *modifiers)
1694 #define N_MODIFIERS 12
1695 /*... Is NumLock always Mod3? */
1696 static char *modifierNames[N_MODIFIERS] = {"Ctrl", "Shift", "Alt", "Mod2",
1697 "Mod3", "Mod4", "Mod5", "Button1", "Button2", "Button3", "Button4",
1698 "Button5"};
1699 static unsigned int modifierMasks[N_MODIFIERS] = {ControlMask, ShiftMask,
1700 Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask, Button1Mask, Button2Mask,
1701 Button3Mask, Button4Mask, Button5Mask};
1702 Modifiers numLockMask = getNumLockModMask(display);
1703 char modStr[MAX_ACCEL_LEN];
1704 char evtStr[MAX_ACCEL_LEN];
1705 char keyStr[MAX_ACCEL_LEN];
1706 const char *c, *evtStart, *keyStart;
1707 int i;
1709 if (strlen(string) >= MAX_ACCEL_LEN)
1710 return FALSE;
1712 /* Get the modifier part */
1713 for (c = string; *c != '<'; c++)
1714 if (*c == '\0')
1715 return FALSE;
1716 strncpy(modStr, string, c - string);
1717 modStr[c - string] = '\0';
1719 /* Verify the <key> or <keypress> part */
1720 evtStart = c;
1721 for ( ; *c != '>'; c++)
1722 if (*c == '\0')
1723 return FALSE;
1724 c++;
1725 strncpy(evtStr, evtStart, c - evtStart);
1726 evtStr[c - evtStart] = '\0';
1727 if (!stripCaseCmp(evtStr, "<key>") && !stripCaseCmp(evtStr, "<keypress>"))
1728 return FALSE;
1730 /* Get the keysym part */
1731 keyStart = c;
1732 for ( ; *c != '\0'; c++);
1733 strncpy(keyStr, keyStart, c - keyStart);
1734 keyStr[c - keyStart] = '\0';
1735 *keySym = XStringToKeysym(keyStr);
1737 /* Parse the modifier part */
1738 *modifiers = 0;
1739 c = modStr;
1740 while (*c != '\0') {
1741 while (*c == ' ' || *c == '\t')
1742 c++;
1743 if (*c == '\0')
1744 break;
1745 for (i = 0; i < N_MODIFIERS; i++) {
1746 if (!strncmp(c, modifierNames[i], strlen(modifierNames[i]))) {
1747 c += strlen(modifierNames[i]);
1748 if (modifierMasks[i] != numLockMask) {
1749 *modifiers |= modifierMasks[i];
1751 break;
1754 if (i == N_MODIFIERS)
1755 return FALSE;
1758 return TRUE;
1762 ** Event handler for patching around Motif's lock + accelerator problem.
1763 ** Looks for a menu item in the patched menu hierarchy and invokes its
1764 ** ArmAndActivate action.
1766 static void lockCB(Widget w, XtPointer callData, XEvent *event,
1767 Boolean *continueDispatch)
1769 Modifiers numLockMask = getNumLockModMask(XtDisplay(w));
1770 Widget topMenuWidget = (Widget)callData;
1771 *continueDispatch = TRUE;
1772 if (!(((XKeyEvent *)event)->state & (LockMask | numLockMask)))
1773 return;
1775 if (!findAndActivateAccel(topMenuWidget, ((XKeyEvent *)event)->keycode,
1776 ((XKeyEvent *)event)->state & ~(LockMask | numLockMask), event))
1777 *continueDispatch = FALSE;
1781 ** Search through menu hierarchy under w and look for a button with
1782 ** accelerator matching keyCode + modifiers, and do its action
1784 static int findAndActivateAccel(Widget w, unsigned int keyCode,
1785 unsigned int modifiers, XEvent *event)
1788 WidgetList children;
1789 Widget menu;
1790 Cardinal numChildren;
1791 int i;
1792 char *accelString = NULL;
1793 KeySym keysym;
1794 unsigned int mods;
1796 if (XtIsComposite(w)) {
1797 XtVaGetValues(w, XmNchildren, &children, XmNnumChildren,
1798 &numChildren, NULL);
1799 for (i=0; i<(int)numChildren; i++)
1800 if (findAndActivateAccel(children[i], keyCode, modifiers, event))
1801 return TRUE;
1802 } else if (XtClass(w) == xmCascadeButtonWidgetClass) {
1803 XtVaGetValues(w, XmNsubMenuId, &menu, NULL);
1804 if (menu != NULL)
1805 if (findAndActivateAccel(menu, keyCode, modifiers, event))
1806 return TRUE;
1807 } else {
1808 XtVaGetValues(w, XmNaccelerator, &accelString, NULL);
1809 if (accelString != NULL && *accelString != '\0') {
1810 if (!parseAccelString(XtDisplay(w), accelString, &keysym, &mods))
1811 return FALSE;
1812 if (keyCode == XKeysymToKeycode(XtDisplay(w), keysym) &&
1813 modifiers == mods) {
1814 if (XtIsSensitive(w)) {
1815 XtCallActionProc(w, "ArmAndActivate", event, NULL, 0);
1816 return TRUE;
1821 return FALSE;