Acquire default colors and fonts from the environment if using the default
[nedit.git] / util / misc.c
blob1b516adac8a0538a7129bd2ab46cf54f4ca6e233
1 static const char CVSID[] = "$Id: misc.c,v 1.43 2002/09/05 17:48:43 tringali Exp $";
2 /*******************************************************************************
3 * *
4 * misc.c -- Miscelaneous Motif convenience functions *
5 * *
6 * Copyright (C) 1999 Mark Edel *
7 * *
8 * This is free software; you can redistribute it and/or modify it under the *
9 * terms of the GNU General Public License as published by the Free Software *
10 * Foundation; either version 2 of the License, or (at your option) any later *
11 * version. *
12 * *
13 * This software is distributed in the hope that it will be useful, but WITHOUT *
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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>
43 #ifdef __unix__
44 #include <sys/time.h>
45 #endif
47 #ifdef VMS
48 #include <types.h>
49 #include <unixio.h>
50 #include <file.h>
51 #endif /*VMS*/
53 #include <X11/Intrinsic.h>
54 #include <X11/Xatom.h>
55 #include <X11/keysym.h>
56 #include <X11/keysymdef.h>
57 #include <Xm/Xm.h>
58 #include <Xm/Label.h>
59 #include <Xm/LabelG.h>
60 #include <Xm/ToggleB.h>
61 #include <Xm/PushB.h>
62 #include <Xm/Separator.h>
63 #include <Xm/RowColumn.h>
64 #include <Xm/CascadeB.h>
65 #include <Xm/AtomMgr.h>
66 #include <Xm/Protocols.h>
67 #include <Xm/Text.h>
68 #include <Xm/MessageB.h>
69 #include <Xm/DialogS.h>
70 #include <Xm/SelectioB.h>
71 #include <Xm/Form.h>
72 #include <Xm/FileSB.h>
74 #ifdef HAVE_DEBUG_H
75 #include "../debug.h"
76 #endif
78 /* structure for passing history-recall data to callbacks */
79 typedef struct {
80 char ***list;
81 int *nItems;
82 int index;
83 } histInfo;
85 typedef Widget (*MotifDialogCreationCall)(Widget, String, ArgList, Cardinal);
87 /* Maximum size of a history-recall list. Typically never invoked, since
88 user must first make this many entries in the text field, limited for
89 safety, to the maximum reasonable number of times user can hit up-arrow
90 before carpal tunnel syndrome sets in */
91 #define HISTORY_LIST_TRIM_TO 1000
92 #define HISTORY_LIST_MAX 2000
94 /* flags to enable/disable delete key remapping and pointer centered dialogs */
95 static int RemapDeleteEnabled = True;
96 static int PointerCenteredDialogsEnabled = False;
98 /* bitmap and mask for waiting (wrist-watch) cursor */
99 #define watch_x_hot 7
100 #define watch_y_hot 7
101 #define watch_width 16
102 #define watch_height 16
103 static unsigned char watch_bits[] = {
104 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07, 0x10, 0x08, 0x08, 0x11,
105 0x04, 0x21, 0x04, 0x21, 0xe4, 0x21, 0x04, 0x20, 0x08, 0x10, 0x10, 0x08,
106 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07
108 #define watch_mask_width 16
109 #define watch_mask_height 16
110 static unsigned char watch_mask_bits[] = {
111 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf8, 0x1f, 0xfc, 0x3f,
112 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfc, 0x3f, 0xf8, 0x1f,
113 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f
116 static void addMnemonicGrabs(Widget addTo, Widget w, int unmodified);
117 static void mnemonicCB(Widget w, XtPointer callData, XKeyEvent *event);
118 static void findAndActivateMnemonic(Widget w, unsigned int keycode);
119 static void addAccelGrabs(Widget topWidget, Widget w);
120 static void addAccelGrab(Widget topWidget, Widget w);
121 static int parseAccelString(Display *display, const char *string, KeySym *keysym,
122 unsigned int *modifiers);
123 static void lockCB(Widget w, XtPointer callData, XEvent *event,
124 Boolean *continueDispatch);
125 static int findAndActivateAccel(Widget w, unsigned int keyCode,
126 unsigned int modifiers, XEvent *event);
127 static void removeWhiteSpace(char *string);
128 static int stripCaseCmp(const char *str1, const char *str2);
129 static void warnHandlerCB(String message);
130 static void passwdCB(Widget w, char * passTxt, XmTextVerifyCallbackStruct
131 *txtVerStr);
132 static void histDestroyCB(Widget w, XtPointer clientData, XtPointer callData);
133 static void histArrowKeyEH(Widget w, XtPointer callData, XEvent *event,
134 Boolean *continueDispatch);
135 static Widget addParentVisArgsAndCall(MotifDialogCreationCall callRoutine,
136 Widget parent, char *name, ArgList arglist, Cardinal argcount);
139 ** Set up closeCB to be called when the user selects close from the
140 ** window menu. The close menu item usually activates f.kill which
141 ** sends a WM_DELETE_WINDOW protocol request for the window.
143 void AddMotifCloseCallback(Widget shell, XtCallbackProc closeCB, void *arg)
145 static Atom wmpAtom, dwAtom = 0;
146 Display *display = XtDisplay(shell);
148 /* deactivate the built in delete response of killing the application */
149 XtVaSetValues(shell, XmNdeleteResponse, XmDO_NOTHING, NULL);
151 /* add a delete window protocol callback instead */
152 if (dwAtom == 0) {
153 wmpAtom = XmInternAtom(display, "WM_PROTOCOLS", FALSE);
154 dwAtom = XmInternAtom(display, "WM_DELETE_WINDOW", FALSE);
156 XmAddProtocolCallback(shell, wmpAtom, dwAtom, closeCB, arg);
160 ** Motif still generates spurious passive grab warnings on both IBM and SGI
161 ** This routine suppresses them.
162 ** (amai, 20011121:)
163 ** And triggers an annoying message on DEC systems on alpha ->
164 ** See Xt sources, xc/lib/Xt/Error.c:DefaultMsg()):
165 ** actually for some obscure reasons they check for XtError/Warning
166 ** handlers being installed when running as a root process!
167 ** Since this handler doesn't help on non-effected systems we should only
168 ** use it if necessary.
170 void SuppressPassiveGrabWarnings(void)
172 #if !defined(__alpha) && !defined(__EMX__)
173 XtSetWarningHandler(warnHandlerCB);
174 #endif
178 ** This routine kludges around the problem of backspace not being mapped
179 ** correctly when Motif is used between a server with a delete key in
180 ** the traditional typewriter backspace position and a client that
181 ** expects a backspace key in that position. Though there are three
182 ** distinct levels of key re-mapping in effect when a user is running
183 ** a Motif application, none of these is really appropriate or effective
184 ** for eliminating the delete v.s. backspace problem. Our solution is,
185 ** sadly, to eliminate the forward delete functionality of the delete key
186 ** in favor of backwards delete for both keys. So as not to prevent the
187 ** user or the application from applying other translation table re-mapping,
188 ** we apply re-map the key as a post-processing step, applied after widget
189 ** creation. As a result, the re-mapping necessarily becomes embedded
190 ** throughout an application (wherever text widgets are created), and
191 ** within library routines, including the Nirvana utility library. To
192 ** make this remapping optional, the SetDeleteRemap function provides a
193 ** way for an application to turn this functionality on and off. It is
194 ** recommended that applications that use this routine provide an
195 ** application resource called remapDeleteKey so savvy users can get
196 ** their forward delete functionality back.
198 void RemapDeleteKey(Widget w)
200 static XtTranslations table = NULL;
201 static char *translations =
202 "~Shift~Ctrl~Meta~Alt<Key>osfDelete: delete-previous-character()\n";
204 if (RemapDeleteEnabled) {
205 if (table == NULL)
206 table = XtParseTranslationTable(translations);
207 XtOverrideTranslations(w, table);
211 void SetDeleteRemap(int state)
213 RemapDeleteEnabled = state;
217 ** This routine resolves a window manager protocol incompatibility between
218 ** the X toolkit and several popular window managers. Using this in place
219 ** of XtRealizeWidget will realize the window in a way which allows the
220 ** affected window managers to apply their own placement strategy to the
221 ** window, as opposed to forcing the window to a specific location.
223 ** One of the hints in the WM_NORMAL_HINTS protocol, PPlacement, gets set by
224 ** the X toolkit (probably part of the Core or Shell widget) when a shell
225 ** widget is realized to the value stored in the XmNx and XmNy resources of the
226 ** Core widget. While callers can set these values, there is no "unset" value
227 ** for these resources. On systems which are more Motif aware, a PPosition
228 ** hint of 0,0, which is the default for XmNx and XmNy, is interpreted as,
229 ** "place this as if no hints were specified". Unfortunately the fvwm family
230 ** of window managers, which are now some of the most popular, interpret this
231 ** as "place this window at (0,0)". This routine intervenes between the
232 ** realizing and the mapping of the window to remove the inappropriate
233 ** PPlacement hint.
235 void RealizeWithoutForcingPosition(Widget shell)
237 XSizeHints *hints = XAllocSizeHints();
238 long suppliedHints;
239 Boolean mappedWhenManaged;
241 /* Temporarily set value of XmNmappedWhenManaged
242 to stop the window from popping up right away */
243 XtVaGetValues(shell, XmNmappedWhenManaged, &mappedWhenManaged, NULL);
244 XtVaSetValues(shell, XmNmappedWhenManaged, False, NULL);
246 /* Realize the widget in unmapped state */
247 XtRealizeWidget(shell);
249 /* Get rid of the incorrect WMNormal hint */
250 if (XGetWMNormalHints(XtDisplay(shell), XtWindow(shell), hints,
251 &suppliedHints)) {
252 hints->flags &= ~PPosition;
253 XSetWMNormalHints(XtDisplay(shell), XtWindow(shell), hints);
255 XFree(hints);
257 /* Map the widget */
258 XtMapWidget(shell);
260 /* Restore the value of XmNmappedWhenManaged */
261 XtVaSetValues(shell, XmNmappedWhenManaged, mappedWhenManaged, NULL);
265 ** Older X applications and X servers were mostly designed to operate with
266 ** visual class PseudoColor, because older displays were at most 8 bits
267 ** deep. Modern X servers, however, usually support 24 bit depth and other
268 ** color models. Sun (and others?) still sets their default visual to
269 ** 8-bit PseudoColor, because some of their X applications don't work
270 ** properly with the other color models. The problem with PseudoColor, of
271 ** course, is that users run out of colors in the default colormap, and if
272 ** they install additional colormaps for individual applications, colors
273 ** flash and change weirdly when you change your focus from one application
274 ** to another.
276 ** In addition to the poor choice of default, a design flaw in Xt makes it
277 ** impossible even for savvy users to specify the XtNvisual resource to
278 ** switch to a deeper visual. The problem is that the colormap resource is
279 ** processed independently of the visual resource, and usually results in a
280 ** colormap for the default visual rather than for the user-selected one.
282 ** This routine should be called before creating a shell widget, to
283 ** pre-process the visual, depth, and colormap resources, and return the
284 ** proper values for these three resources to be passed to XtAppCreateShell.
285 ** Applications which actually require a particular color model (i.e. for
286 ** doing color table animation or dynamic color assignment) should not use
287 ** this routine.
289 ** Note that a consequence of using the "best" as opposed to the default
290 ** visual is that some color resources are still converted with the default
291 ** visual (particularly *background), and these must be avoided by widgets
292 ** which are allowed to handle any visual.
294 ** Returns True if the best visual is the default, False otherwise.
296 Boolean FindBestVisual(Display *display, const char *appName, const char *appClass,
297 Visual **visual, int *depth, Colormap *colormap)
299 char rsrcName[256], rsrcClass[256], *valueString, *type, *endPtr;
300 XrmValue value;
301 int screen = DefaultScreen(display);
302 int reqDepth = -1;
303 long reqID = -1; /* should hold a 'VisualID' and a '-1' ... */
304 int reqClass = -1;
305 int installColormap = FALSE;
306 int maxDepth, bestClass, bestVisual, nVis, i, j;
307 XVisualInfo visTemplate, *visList = NULL;
308 static Visual *cachedVisual = NULL;
309 static Colormap cachedColormap;
310 static int cachedDepth = 0;
311 int bestClasses[] = {StaticGray, GrayScale, StaticColor, PseudoColor,
312 DirectColor, TrueColor};
314 /* If results have already been computed, just return them */
315 if (cachedVisual != NULL) {
316 *visual = cachedVisual;
317 *depth = cachedDepth;
318 *colormap = cachedColormap;
319 return (*visual == DefaultVisual(display, screen));
322 /* Read the visualID and installColormap resources for the application.
323 visualID can be specified either as a number (the visual id as
324 shown by xdpyinfo), as a visual class name, or as Best or Default. */
325 sprintf(rsrcName,"%s.%s", appName, "visualID");
326 sprintf(rsrcClass, "%s.%s", appClass, "VisualID");
327 if (XrmGetResource(XtDatabase(display), rsrcName, rsrcClass, &type,
328 &value)) {
329 valueString = value.addr;
330 reqID = (int)strtol(valueString, &endPtr, 0);
331 if (endPtr == valueString) {
332 reqID = -1;
333 if (stripCaseCmp(valueString, "Default"))
334 reqID = DefaultVisual(display, screen)->visualid;
335 else if (stripCaseCmp(valueString, "StaticGray"))
336 reqClass = StaticGray;
337 else if (stripCaseCmp(valueString, "StaticColor"))
338 reqClass = StaticColor;
339 else if (stripCaseCmp(valueString, "TrueColor"))
340 reqClass = TrueColor;
341 else if (stripCaseCmp(valueString, "GrayScale"))
342 reqClass = GrayScale;
343 else if (stripCaseCmp(valueString, "PseudoColor"))
344 reqClass = PseudoColor;
345 else if (stripCaseCmp(valueString, "DirectColor"))
346 reqClass = DirectColor;
347 else if (!stripCaseCmp(valueString, "Best"))
348 fprintf(stderr, "Invalid visualID resource value\n");
351 sprintf(rsrcName,"%s.%s", appName, "installColormap");
352 sprintf(rsrcClass, "%s.%s", appClass, "InstallColormap");
353 if (XrmGetResource(XtDatabase(display), rsrcName, rsrcClass, &type,
354 &value)) {
355 if (stripCaseCmp(value.addr, "Yes") || stripCaseCmp(value.addr, "True"))
356 installColormap = TRUE;
359 visTemplate.screen = screen;
361 /* Generate a list of visuals to consider. (Note, vestigial code for
362 user-requested visual depth is left in, just in case that function
363 might be needed again, but it does nothing) */
364 if (reqID != -1) {
365 visTemplate.visualid = reqID;
366 visList = XGetVisualInfo(display, VisualScreenMask|VisualIDMask,
367 &visTemplate, &nVis);
368 if (visList == NULL)
369 fprintf(stderr, "VisualID resource value not valid\n");
371 if (visList == NULL && reqClass != -1 && reqDepth != -1) {
372 visTemplate.class = reqClass;
373 visTemplate.depth = reqDepth;
374 visList = XGetVisualInfo(display,
375 VisualScreenMask| VisualClassMask | VisualDepthMask,
376 &visTemplate, &nVis);
377 if (visList == NULL)
378 fprintf(stderr, "Visual class/depth combination not available\n");
380 if (visList == NULL && reqClass != -1) {
381 visTemplate.class = reqClass;
382 visList = XGetVisualInfo(display, VisualScreenMask|VisualClassMask,
383 &visTemplate, &nVis);
384 if (visList == NULL)
385 fprintf(stderr,
386 "Visual Class from resource \"visualID\" not available\n");
388 if (visList == NULL && reqDepth != -1) {
389 visTemplate.depth = reqDepth;
390 visList = XGetVisualInfo(display, VisualScreenMask|VisualDepthMask,
391 &visTemplate, &nVis);
392 if (visList == NULL)
393 fprintf(stderr, "Requested visual depth not available\n");
395 if (visList == NULL) {
396 visList = XGetVisualInfo(display, VisualScreenMask, &visTemplate, &nVis);
397 if (visList == NULL) {
398 fprintf(stderr, "Internal Error: no visuals available?\n");
399 *visual = DefaultVisual(display, screen);
400 *depth = DefaultDepth(display, screen);
401 *colormap = DefaultColormap(display, screen);
402 return True;
406 /* Choose among the visuals in the candidate list. Prefer maximum
407 depth first then matching default, then largest value of bestClass
408 (I'm not sure whether we actually care about class) */
409 maxDepth = 0;
410 bestClass = 0;
411 bestVisual = 0;
412 for (i=0; i < nVis; i++) {
413 if (visList[i].depth > maxDepth) {
414 maxDepth = visList[i].depth;
415 bestClass = 0;
416 bestVisual = i;
418 if (visList[i].depth == maxDepth) {
419 if (visList[i].visual == DefaultVisual(display, screen))
420 bestVisual = i;
421 if (visList[bestVisual].visual != DefaultVisual(display, screen)) {
422 for (j = 0; j < (int)XtNumber(bestClasses); j++) {
423 if (visList[i].class == bestClasses[j] && j > bestClass) {
424 bestClass = j;
425 bestVisual = i;
431 *visual = cachedVisual = visList[bestVisual].visual;
432 *depth = cachedDepth = visList[bestVisual].depth;
434 /* If the chosen visual is not the default, it needs a colormap allocated */
435 if (*visual == DefaultVisual(display, screen) && !installColormap)
436 *colormap = cachedColormap = DefaultColormap(display, screen);
437 else {
438 *colormap = cachedColormap = XCreateColormap(display,
439 RootWindow(display, screen), cachedVisual, AllocNone);
440 XInstallColormap(display, cachedColormap);
442 /* printf("Chose visual with depth %d, class %d, colormap %ld, id 0x%x\n",
443 visList[bestVisual].depth, visList[bestVisual].class,
444 *colormap, cachedVisual->visualid); */
445 /* Fix memory leak */
446 if (visList != NULL) {
447 XFree(visList);
450 return (*visual == DefaultVisual(display, screen));
454 ** If you want to use a non-default visual with Motif, shells all have to be
455 ** created with that visual, depth, and colormap, even if the parent has them
456 ** set up properly. Substituting these routines, will append visual args copied
457 ** from the parent widget (CreatePopupMenu and CreatePulldownMenu), or from the
458 ** best visual, obtained via FindBestVisual above (CreateShellWithBestVis).
460 Widget CreateDialogShell(Widget parent, char *name,
461 ArgList arglist, Cardinal argcount)
463 return addParentVisArgsAndCall(XmCreateDialogShell, parent, name, arglist,
464 argcount);
468 Widget CreatePopupMenu(Widget parent, char *name, ArgList arglist,
469 Cardinal argcount)
471 return addParentVisArgsAndCall(XmCreatePopupMenu, parent, name,
472 arglist, argcount);
476 Widget CreatePulldownMenu(Widget parent, char *name,
477 ArgList arglist, Cardinal argcount)
479 return addParentVisArgsAndCall(XmCreatePulldownMenu, parent, name, arglist,
480 argcount);
484 Widget CreatePromptDialog(Widget parent, char *name,
485 ArgList arglist, Cardinal argcount)
487 return addParentVisArgsAndCall(XmCreatePromptDialog, parent, name, arglist,
488 argcount);
492 Widget CreateSelectionDialog(Widget parent, char *name,
493 ArgList arglist, Cardinal argcount)
495 return addParentVisArgsAndCall(XmCreateSelectionDialog, parent, name,
496 arglist, argcount);
500 Widget CreateFormDialog(Widget parent, char *name,
501 ArgList arglist, Cardinal argcount)
503 return addParentVisArgsAndCall(XmCreateFormDialog, parent, name, arglist,
504 argcount);
508 Widget CreateFileSelectionDialog(Widget parent, char *name,
509 ArgList arglist, Cardinal argcount)
511 return addParentVisArgsAndCall(XmCreateFileSelectionDialog, parent, name,
512 arglist, argcount);
516 Widget CreateQuestionDialog(Widget parent, char *name,
517 ArgList arglist, Cardinal argcount)
519 return addParentVisArgsAndCall(XmCreateQuestionDialog, parent, name,
520 arglist, argcount);
524 Widget CreateMessageDialog(Widget parent, char *name,
525 ArgList arglist, Cardinal argcount)
527 return addParentVisArgsAndCall(XmCreateMessageDialog, parent, name,
528 arglist, argcount);
532 Widget CreateErrorDialog(Widget parent, char *name,
533 ArgList arglist, Cardinal argcount)
535 return addParentVisArgsAndCall(XmCreateErrorDialog, parent, name, arglist,
536 argcount);
540 Widget CreateShellWithBestVis(String appName, String appClass,
541 WidgetClass class, Display *display, ArgList args, Cardinal nArgs)
543 Visual *visual;
544 int depth;
545 Colormap colormap;
546 ArgList al;
547 Cardinal ac = nArgs;
548 Widget result;
550 FindBestVisual(display, appName, appClass, &visual, &depth, &colormap);
551 al = (ArgList)XtMalloc(sizeof(Arg) * (nArgs + 3));
552 if (nArgs != 0)
553 memcpy(al, args, sizeof(Arg) * nArgs);
554 XtSetArg(al[ac], XtNvisual, visual); ac++;
555 XtSetArg(al[ac], XtNdepth, depth); ac++;
556 XtSetArg(al[ac], XtNcolormap, colormap); ac++;
557 result = XtAppCreateShell(appName, appClass, class, display, al, ac);
558 XtFree((char *)al);
559 return result;
563 ** Calls one of the Motif widget creation routines, splicing in additional
564 ** arguments for visual, colormap, and depth.
566 static Widget addParentVisArgsAndCall(MotifDialogCreationCall createRoutine,
567 Widget parent, char *name, ArgList arglist, Cardinal argcount)
569 Visual *visual;
570 int depth;
571 Colormap colormap;
572 ArgList al;
573 Cardinal ac = argcount;
574 Widget result;
575 Widget parentShell = parent;
577 /* Find the application/dialog/menu shell at the top of the widget
578 hierarchy, which has the visual resource being used */
579 while (True) {
580 if (XtIsShell(parentShell))
581 break;
582 if (parentShell == NULL) {
583 fprintf(stderr, "failed to find shell\n");
584 exit(EXIT_FAILURE);
586 parentShell = XtParent(parentShell);
589 /* Add the visual, depth, and colormap resources to the argument list */
590 XtVaGetValues(parentShell, XtNvisual, &visual, XtNdepth, &depth,
591 XtNcolormap, &colormap, NULL);
592 al = (ArgList)XtMalloc(sizeof(Arg) * (argcount + 3));
593 if (argcount != 0)
594 memcpy(al, arglist, sizeof(Arg) * argcount);
595 XtSetArg(al[ac], XtNvisual, visual); ac++;
596 XtSetArg(al[ac], XtNdepth, depth); ac++;
597 XtSetArg(al[ac], XtNcolormap, colormap); ac++;
598 result = (*createRoutine)(parent, name, al, ac);
599 XtFree((char *)al);
600 return result;
604 ** ManageDialogCenteredOnPointer is used in place of XtManageChild for
605 ** popping up a dialog to enable the dialog to be centered under the
606 ** mouse pointer. Whether it pops up the dialog centered under the pointer
607 ** or in its default position centered over the parent widget, depends on
608 ** the value set in the SetPointerCenteredDialogs call.
610 void ManageDialogCenteredOnPointer(Widget dialogChild)
612 Widget shell = XtParent(dialogChild);
613 Window root, child;
614 unsigned int mask;
615 unsigned int width, height, borderWidth, depth;
616 int x, y, winX, winY, maxX, maxY;
617 Boolean mappedWhenManaged;
619 /* If this feature is not enabled, just manage the dialog */
620 if (!PointerCenteredDialogsEnabled) {
621 XtManageChild(dialogChild);
622 return;
625 /* Temporarily set value of XmNmappedWhenManaged
626 to stop the dialog from popping up right away */
627 XtVaGetValues(shell, XmNmappedWhenManaged, &mappedWhenManaged, NULL);
628 XtVaSetValues(shell, XmNmappedWhenManaged, False, NULL);
630 /* Manage the dialog */
631 XtManageChild(dialogChild);
633 /* Get the pointer position (x, y) */
634 XQueryPointer(XtDisplay(shell), XtWindow(shell), &root, &child,
635 &x, &y, &winX, &winY, &mask);
637 /* Translate the pointer position (x, y) into a position for the new
638 window that will place the pointer at its center */
639 XGetGeometry(XtDisplay(shell), XtWindow(shell), &root, &winX, &winY,
640 &width, &height, &borderWidth, &depth);
641 width += 2 * borderWidth;
642 height += 2 * borderWidth;
643 x -= width/2;
644 y -= height/2;
646 /* Ensure that the dialog remains on screen */
647 maxX = XtScreen(shell)->width - width;
648 maxY = XtScreen(shell)->height - height;
649 if (x < 0) x = 0;
650 if (x > maxX) x = maxX;
651 if (y < 0) y = 0;
652 if (y > maxY) y = maxY;
654 /* Set desired window position in the DialogShell */
655 XtVaSetValues(shell, XmNx, x, XmNy, y, NULL);
657 /* Map the widget */
658 XtMapWidget(shell);
660 /* Restore the value of XmNmappedWhenManaged */
661 XtVaSetValues(shell, XmNmappedWhenManaged, mappedWhenManaged, NULL);
665 ** Cause dialogs created by libNUtil.a routines (such as DialogF and
666 ** GetNewFilename), and dialogs which use ManageDialogCenteredOnPointer
667 ** to pop up over the pointer (state = True), or pop up in their default
668 ** positions (state = False)
670 void SetPointerCenteredDialogs(int state)
672 PointerCenteredDialogsEnabled = state;
677 ** Raise a window to the top and give it the input focus. Setting input focus
678 ** is important on systems which use explict (rather than pointer) focus.
680 ** The X alternatives XMapRaised, and XSetInputFocus both have problems.
681 ** XMapRaised only gives the window the focus if it was initially not visible,
682 ** and XSetInputFocus sets the input focus, but crashes if the window is not
683 ** visible.
685 ** This routine should also be used in the case where a dialog is popped up and
686 ** subsequent calls to the dialog function use a flag, or the XtIsManaged, to
687 ** decide whether to create a new instance of the dialog, because on slower
688 ** systems, events can intervene while a dialog is still on its way up and its
689 ** window is still invisible, causing a subtle crash potential if
690 ** XSetInputFocus is used.
692 void RaiseShellWindow(Widget shell)
694 RaiseWindow(XtDisplay(shell), XtWindow(shell));
698 void RaiseWindow(Display *display, Window w)
700 XWindowAttributes winAttr;
702 XGetWindowAttributes(display, w, &winAttr);
703 if (winAttr.map_state == IsViewable)
704 XSetInputFocus(display, w, RevertToParent, CurrentTime);
705 XMapRaised(display, w);
709 ** Add a handler for mnemonics in a dialog (Motif currently only handles
710 ** mnemonics in menus) following the example of M.S. Windows. To add
711 ** mnemonics to a dialog, set the XmNmnemonic resource, as you would in
712 ** a menu, on push buttons or toggle buttons, and call this function
713 ** when the dialog is fully constructed. Mnemonics added or changed
714 ** after this call will not be noticed. To add a mnemonic to a text field
715 ** or list, set the XmNmnemonic resource on the appropriate label and set
716 ** the XmNuserData resource of the label to the widget to get the focus
717 ** when the mnemonic is typed.
719 void AddDialogMnemonicHandler(Widget dialog, int unmodifiedToo)
721 XtAddEventHandler(dialog, KeyPressMask, False,
722 (XtEventHandler)mnemonicCB, (XtPointer)0);
723 addMnemonicGrabs(dialog, dialog, unmodifiedToo);
727 ** Removes the event handler and key-grabs added by AddDialogMnemonicHandler
729 void RemoveDialogMnemonicHandler(Widget dialog)
731 XtUngrabKey(dialog, AnyKey, Mod1Mask);
732 XtRemoveEventHandler(dialog, KeyPressMask, False,
733 (XtEventHandler)mnemonicCB, (XtPointer)0);
737 ** Patch around Motif's poor handling of menu accelerator keys. Motif
738 ** does not process menu accelerators when the caps lock or num lock
739 ** keys are engaged. To enable accelerators in these cases, call this
740 ** routine with the completed menu bar widget as "topMenuContainer", and
741 ** the top level shell widget as "topWidget". It will add key grabs for
742 ** all of the accelerators it finds in the topMenuContainer menu tree, and
743 ** an event handler which can process dropped accelerator events by (again)
744 ** traversing the menu tree looking for matching accelerators, and invoking
745 ** the appropriate button actions. Any dynamic additions to the menus
746 ** require a call to UpdateAccelLockPatch to add the additional grabs.
747 ** Unfortunately, these grabs can not be removed.
749 void AccelLockBugPatch(Widget topWidget, Widget topMenuContainer)
751 XtAddEventHandler(topWidget, KeyPressMask, False, lockCB, topMenuContainer);
752 addAccelGrabs(topWidget, topMenuContainer);
756 ** Add additional key grabs for new menu items added to the menus, for
757 ** patching around the Motif Caps/Num Lock problem. "topWidget" must be
758 ** the same widget passed in the original call to AccelLockBugPatch.
760 void UpdateAccelLockPatch(Widget topWidget, Widget newButton)
762 addAccelGrab(topWidget, newButton);
766 ** PopDownBugPatch
768 ** Under some circumstances, popping down a dialog and its parent in
769 ** rapid succession causes a crash. This routine delays and
770 ** processs events until receiving a ReparentNotify event.
771 ** (I have no idea why a ReparentNotify event occurs at all, but it does
772 ** mark the point where it is safe to destroy or pop down the parent, and
773 ** it might have something to do with the bug.) There is a failsafe in
774 ** the form of a ~1.5 second timeout in case no ReparentNotify arrives.
775 ** Use this sparingly, only when real crashes are observed, and periodically
776 ** check to make sure that it is still necessary.
778 void PopDownBugPatch(Widget w)
780 time_t stopTime;
782 stopTime = time(NULL) + 1;
783 while (time(NULL) <= stopTime) {
784 XEvent event;
785 XtAppContext context = XtWidgetToApplicationContext(w);
786 XtAppPeekEvent(context, &event);
787 if (event.xany.type == ReparentNotify)
788 return;
789 XtAppProcessEvent(context, XtIMAll);
794 ** Convert a compound string to a C style null terminated string.
795 ** Returned string must be freed by the caller.
797 char *GetXmStringText(XmString fromString)
799 XmStringContext context;
800 char *text, *toPtr, *toString, *fromPtr;
801 XmStringCharSet charset;
802 XmStringDirection direction;
803 Boolean separator;
805 /* Malloc a buffer large enough to hold the string. XmStringLength
806 should always be slightly longer than necessary, but won't be
807 shorter than the equivalent null-terminated string */
808 toString = XtMalloc(XmStringLength(fromString));
810 /* loop over all of the segments in the string, copying each segment
811 into the output string and converting separators into newlines */
812 XmStringInitContext(&context, fromString);
813 toPtr = toString;
814 while (XmStringGetNextSegment(context, &text,
815 &charset, &direction, &separator)) {
816 for (fromPtr=text; *fromPtr!='\0'; fromPtr++)
817 *toPtr++ = *fromPtr;
818 if (separator)
819 *toPtr++ = '\n';
822 /* terminate the string, free the context, and return the string */
823 *toPtr++ = '\0';
824 XmStringFreeContext(context);
825 return toString;
829 ** Get the XFontStruct that corresponds to the default (first) font in
830 ** a Motif font list. Since Motif stores this, it saves us from storing
831 ** it or querying it from the X server.
833 XFontStruct *GetDefaultFontStruct(XmFontList font)
835 XFontStruct *fs;
836 XmFontContext context;
837 XmStringCharSet charset;
839 XmFontListInitFontContext(&context, font);
840 XmFontListGetNextFont(context, &charset, &fs);
841 XmFontListFreeFontContext(context);
842 XtFree(charset);
843 return fs;
847 ** Create a string table suitable for passing to XmList widgets
849 XmString* StringTable(int count, ... )
851 va_list ap;
852 XmString *array;
853 int i;
854 char *str;
856 va_start(ap, count);
857 array = (XmString*)XtMalloc((count+1) * sizeof(XmString));
858 for(i = 0; i < count; i++ ) {
859 str = va_arg(ap, char *);
860 array[i] = XmStringCreateSimple(str);
862 array[i] = (XmString)0;
863 va_end(ap);
864 return(array);
867 void FreeStringTable(XmString *table)
869 int i;
871 for(i = 0; table[i] != 0; i++)
872 XmStringFree(table[i]);
873 XtFree((char *)table);
877 ** Simulate a button press. The purpose of this routine is show users what
878 ** is happening when they take an action with a non-obvious side effect,
879 ** such as when a user double clicks on a list item. The argument is an
880 ** XmPushButton widget to "press"
882 void SimulateButtonPress(Widget widget)
884 XEvent keyEvent;
886 memset((char *)&keyEvent, 0, sizeof(XKeyPressedEvent));
887 keyEvent.type = KeyPress;
888 keyEvent.xkey.serial = 1;
889 keyEvent.xkey.send_event = True;
891 if (XtIsSubclass(widget, xmGadgetClass))
893 /* On some Motif implementations, asking a gadget for its
894 window will crash, rather than return the window of its
895 parent. */
896 Widget parent = XtParent(widget);
897 keyEvent.xkey.display = XtDisplay(parent);
898 keyEvent.xkey.window = XtWindow(parent);
900 XtCallActionProc(parent, "ManagerGadgetSelect",
901 &keyEvent, NULL, 0);
903 else
905 keyEvent.xkey.display = XtDisplay(widget);
906 keyEvent.xkey.window = XtWindow(widget);
908 XtCallActionProc(widget, "ArmAndActivate", &keyEvent, NULL, 0);
913 ** Add an item to an already established pull-down or pop-up menu, including
914 ** mnemonics, accelerators and callbacks.
916 Widget AddMenuItem(Widget parent, char *name, char *label,
917 char mnemonic, char *acc, char *accText,
918 XtCallbackProc callback, void *cbArg)
920 Widget button;
921 XmString st1, st2;
923 button = XtVaCreateManagedWidget(name, xmPushButtonWidgetClass, parent,
924 XmNlabelString, st1=XmStringCreateSimple(label),
925 XmNmnemonic, mnemonic,
926 XmNacceleratorText, st2=XmStringCreateSimple(accText),
927 XmNaccelerator, acc, NULL);
928 XtAddCallback(button, XmNactivateCallback, callback, cbArg);
929 XmStringFree(st1);
930 XmStringFree(st2);
931 return button;
935 ** Add a toggle button item to an already established pull-down or pop-up
936 ** menu, including mnemonics, accelerators and callbacks.
938 Widget AddMenuToggle(Widget parent, char *name, char *label,
939 char mnemonic, char *acc, char *accText,
940 XtCallbackProc callback, void *cbArg, int set)
942 Widget button;
943 XmString st1, st2;
945 button = XtVaCreateManagedWidget(name, xmToggleButtonWidgetClass, parent,
946 XmNlabelString, st1=XmStringCreateSimple(label),
947 XmNmnemonic, mnemonic,
948 XmNacceleratorText, st2=XmStringCreateSimple(accText),
949 XmNaccelerator, acc,
950 XmNset, set, NULL);
951 XtAddCallback(button, XmNvalueChangedCallback, callback, cbArg);
952 XmStringFree(st1);
953 XmStringFree(st2);
954 return button;
958 ** Add a separator line to a menu
960 Widget AddMenuSeparator(Widget parent, char *name)
962 Widget button;
964 button = XmCreateSeparator(parent, name, NULL, 0);
965 XtManageChild(button);
966 return button;
970 ** Add a sub-menu to an established pull-down or pop-up menu, including
971 ** mnemonics, accelerators and callbacks. Returns the menu pane of the
972 ** new sub menu.
974 Widget AddSubMenu(Widget parent, char *name, char *label, char mnemonic)
976 Widget menu;
977 XmString st1;
979 menu = CreatePulldownMenu(parent, name, NULL, 0);
980 XtVaCreateManagedWidget(name, xmCascadeButtonWidgetClass, parent,
981 XmNlabelString, st1=XmStringCreateSimple(label),
982 XmNmnemonic, mnemonic,
983 XmNsubMenuId, menu, NULL);
984 XmStringFree(st1);
985 return menu;
989 ** SetIntLabel, SetFloatLabel, SetIntText, SetFloatText
991 ** Set the text of a motif label or text widget to show an integer or
992 ** floating number.
994 void SetIntLabel(Widget label, int value)
996 char labelString[20];
997 XmString s1;
999 sprintf(labelString, "%d", value);
1000 s1=XmStringCreateSimple(labelString);
1001 XtVaSetValues(label, XmNlabelString, s1, NULL);
1002 XmStringFree(s1);
1006 void SetFloatLabel(Widget label, double value)
1008 char labelString[20];
1009 XmString s1;
1011 sprintf(labelString, "%g", value);
1012 s1=XmStringCreateSimple(labelString);
1013 XtVaSetValues(label, XmNlabelString, s1, NULL);
1014 XmStringFree(s1);
1018 void SetIntText(Widget text, int value)
1020 char labelString[20];
1022 sprintf(labelString, "%d", value);
1023 XmTextSetString(text, labelString);
1027 void SetFloatText(Widget text, double value)
1029 char labelString[20];
1031 sprintf(labelString, "%g", value);
1032 XmTextSetString(text, labelString);
1036 ** GetIntText, GetFloatText, GetIntTextWarn, GetFloatTextWarn
1038 ** Get the text of a motif text widget as an integer or floating point number.
1039 ** The functions will return TEXT_READ_OK of the value was read correctly.
1040 ** If not, they will return either TEXT_IS_BLANK, or TEXT_NOT_NUMBER. The
1041 ** GetIntTextWarn and GetFloatTextWarn will display a dialog warning the
1042 ** user that the value could not be read. The argument fieldName is used
1043 ** in the dialog to help the user identify where the problem is. Set
1044 ** warnBlank to true if a blank field is also considered an error.
1046 int GetFloatText(Widget text, double *value)
1048 char *strValue, *endPtr;
1049 int retVal;
1051 strValue = XmTextGetString(text); /* Get Value */
1052 removeWhiteSpace(strValue); /* Remove blanks and tabs */
1053 *value = strtod(strValue, &endPtr); /* Convert string to double */
1054 if (strlen(strValue) == 0) /* String is empty */
1055 retVal = TEXT_IS_BLANK;
1056 else if (*endPtr != '\0') /* Whole string not parsed */
1057 retVal = TEXT_NOT_NUMBER;
1058 else
1059 retVal = TEXT_READ_OK;
1060 XtFree(strValue);
1061 return retVal;
1064 int GetIntText(Widget text, int *value)
1066 char *strValue, *endPtr;
1067 int retVal;
1069 strValue = XmTextGetString(text); /* Get Value */
1070 removeWhiteSpace(strValue); /* Remove blanks and tabs */
1071 *value = strtol(strValue, &endPtr, 10); /* Convert string to long */
1072 if (strlen(strValue) == 0) /* String is empty */
1073 retVal = TEXT_IS_BLANK;
1074 else if (*endPtr != '\0') /* Whole string not parsed */
1075 retVal = TEXT_NOT_NUMBER;
1076 else
1077 retVal = TEXT_READ_OK;
1078 XtFree(strValue);
1079 return retVal;
1082 int GetFloatTextWarn(Widget text, double *value, const char *fieldName,
1083 int warnBlank)
1085 int result;
1086 char *valueStr;
1088 result = GetFloatText(text, value);
1089 if (result == TEXT_READ_OK || (result == TEXT_IS_BLANK && !warnBlank))
1090 return result;
1091 valueStr = XmTextGetString(text);
1092 if (result == TEXT_IS_BLANK)
1093 DialogF (DF_ERR, text, 1, "Please supply %s value",
1094 "Dismiss", fieldName);
1095 else /* TEXT_NOT_NUMBER */
1096 DialogF (DF_ERR, text, 1,
1097 "Can't read %s value: \"%s\"",
1098 "Dismiss", fieldName, valueStr);
1099 XtFree(valueStr);
1100 return result;
1103 int GetIntTextWarn(Widget text, int *value, const char *fieldName, int warnBlank)
1105 int result;
1106 char *valueStr;
1108 result = GetIntText(text, value);
1109 if (result == TEXT_READ_OK || (result == TEXT_IS_BLANK && !warnBlank))
1110 return result;
1111 valueStr = XmTextGetString(text);
1112 if (result == TEXT_IS_BLANK)
1113 DialogF (DF_ERR, text, 1, "Please supply a value for %s",
1114 "Dismiss", fieldName);
1115 else /* TEXT_NOT_NUMBER */
1116 DialogF (DF_ERR, text, 1,
1117 "Can't read integer value \"%s\" in %s",
1118 "Dismiss", valueStr, fieldName);
1119 XtFree(valueStr);
1120 return result;
1123 int TextWidgetIsBlank(Widget textW)
1125 char *str;
1126 int retVal;
1128 str = XmTextGetString(textW);
1129 removeWhiteSpace(str);
1130 retVal = *str == '\0';
1131 XtFree(str);
1132 return retVal;
1136 ** Turn a multi-line editing text widget into a fake single line text area
1137 ** by disabling the translation for Return. This is a way to give users
1138 ** extra space, by allowing wrapping, but still prohibiting newlines.
1139 ** (SINGLE_LINE_EDIT mode can't be used, in this case, because it forces
1140 ** the widget to be one line high).
1142 void MakeSingleLineTextW(Widget textW)
1144 static XtTranslations noReturnTable = NULL;
1145 static char *noReturnTranslations = "<Key>Return: activate()\n";
1147 if (noReturnTable == NULL)
1148 noReturnTable = XtParseTranslationTable(noReturnTranslations);
1149 XtOverrideTranslations(textW, noReturnTable);
1153 ** Add up-arrow/down-arrow recall to a single line text field. When user
1154 ** presses up-arrow, string is cleared and recent entries are presented,
1155 ** moving to older ones as each successive up-arrow is pressed. Down-arrow
1156 ** moves to more recent ones, final down-arrow clears the field. Associated
1157 ** routine, AddToHistoryList, makes maintaining a history list easier.
1159 ** Arguments are the widget, and pointers to the history list and number of
1160 ** items, which are expected to change periodically.
1162 void AddHistoryToTextWidget(Widget textW, char ***historyList, int *nItems)
1164 histInfo *histData;
1166 /* create a data structure for passing history info to the callbacks */
1167 histData = (histInfo *)XtMalloc(sizeof(histInfo));
1168 histData->list = historyList;
1169 histData->nItems = nItems;
1170 histData->index = -1;
1172 /* Add an event handler for handling up/down arrow events */
1173 XtAddEventHandler(textW, KeyPressMask, False,
1174 (XtEventHandler)histArrowKeyEH, histData);
1176 /* Add a destroy callback for freeing history data structure */
1177 XtAddCallback(textW, XmNdestroyCallback, histDestroyCB, histData);
1180 static void histDestroyCB(Widget w, XtPointer clientData, XtPointer callData)
1182 XtFree((char *)clientData);
1185 static void histArrowKeyEH(Widget w, XtPointer callData, XEvent *event,
1186 Boolean *continueDispatch)
1188 histInfo *histData = (histInfo *)callData;
1189 KeySym keysym = XLookupKeysym((XKeyEvent *)event, 0);
1191 /* only process up and down arrow keys */
1192 if (keysym != XK_Up && keysym != XK_Down)
1193 return;
1195 /* increment or decrement the index depending on which arrow was pressed */
1196 histData->index += (keysym == XK_Up) ? 1 : -1;
1198 /* if the index is out of range, beep, fix it up, and return */
1199 if (histData->index < -1) {
1200 histData->index = -1;
1201 XBell(XtDisplay(w), 0);
1202 return;
1204 if (histData->index >= *histData->nItems) {
1205 histData->index = *histData->nItems - 1;
1206 XBell(XtDisplay(w), 0);
1207 return;
1210 /* Change the text field contents */
1211 XmTextSetString(w, histData->index == -1 ? "" :
1212 (*histData->list)[histData->index]);
1216 ** Copies a string on to the end of history list, which may be reallocated
1217 ** to make room. If historyList grows beyond its internally set boundary
1218 ** for size (HISTORY_LIST_MAX), it is trimmed back to a smaller size
1219 ** (HISTORY_LIST_TRIM_TO). Before adding to the list, checks if the item
1220 ** is a duplicate of the last item. If so, it is not added.
1222 void AddToHistoryList(char *newItem, char ***historyList, int *nItems)
1224 char **newList;
1225 int i;
1227 if (*nItems != 0 && !strcmp(newItem, **historyList))
1228 return;
1229 if (*nItems == HISTORY_LIST_MAX) {
1230 for (i=HISTORY_LIST_TRIM_TO; i<HISTORY_LIST_MAX; i++)
1231 XtFree((*historyList)[i]);
1232 *nItems = HISTORY_LIST_TRIM_TO;
1234 newList = (char **)XtMalloc(sizeof(char *) * (*nItems + 1));
1235 for (i=0; i < *nItems; i++)
1236 newList[i+1] = (*historyList)[i];
1237 if (*nItems != 0 && *historyList != NULL)
1238 XtFree((char *)*historyList);
1239 (*nItems)++;
1240 newList[0] = XtNewString(newItem);
1241 *historyList = newList;
1245 * PasswordText - routine to add a callback to any text widget so that all
1246 * text typed by the user is echoed with asterisks, allowing
1247 * a password to be typed in without being seen.
1249 * parameters: w - text widget to add the callback to
1250 * passTxt - pointer to a string created by caller of this routine.
1251 * **NOTE** The length of this string should be one
1252 * greater than the maximum specified by XmNmaxLength.
1253 * This string is set to empty just before the callback
1254 * is added.
1257 void PasswordText(Widget w, char *passTxt)
1259 passTxt[0] = '\0';
1260 XtAddCallback(w, XmNmodifyVerifyCallback, (XtCallbackProc)passwdCB,passTxt);
1264 ** BeginWait/EndWait
1266 ** Display/Remove a watch cursor over topCursorWidget and its descendents
1268 void BeginWait(Widget topCursorWidget)
1270 Display *display = XtDisplay(topCursorWidget);
1271 Pixmap pixmap;
1272 Pixmap maskPixmap;
1273 XColor xcolors[2];
1274 static Cursor waitCursor = 0;
1276 /* if the watch cursor hasn't been created yet, create it */
1277 if (!waitCursor) {
1278 pixmap = XCreateBitmapFromData(display, DefaultRootWindow(display),
1279 (char *)watch_bits, watch_width, watch_height);
1281 maskPixmap = XCreateBitmapFromData(display, DefaultRootWindow(display),
1282 (char *)watch_mask_bits, watch_width, watch_height);
1284 xcolors[0].pixel = BlackPixelOfScreen(DefaultScreenOfDisplay(display));
1285 xcolors[1].pixel = WhitePixelOfScreen(DefaultScreenOfDisplay(display));
1287 XQueryColors(display, DefaultColormapOfScreen(
1288 DefaultScreenOfDisplay(display)), xcolors, 2);
1289 waitCursor = XCreatePixmapCursor(display, pixmap, maskPixmap,
1290 &xcolors[0], &xcolors[1], watch_x_hot, watch_y_hot);
1291 XFreePixmap(display, pixmap);
1292 XFreePixmap(display, maskPixmap);
1295 /* display the cursor */
1296 XDefineCursor(display, XtWindow(topCursorWidget), waitCursor);
1299 void BusyWait(Widget widget)
1301 #ifdef __unix__
1302 static const int timeout = 100000; /* 1/10 sec = 100 ms = 100,000 us */
1303 static struct timeval last = { 0 };
1304 struct timeval current;
1305 gettimeofday(&current, NULL);
1307 if ((current.tv_sec != last.tv_sec) ||
1308 (current.tv_usec - last.tv_usec > timeout))
1310 XmUpdateDisplay(widget);
1311 last = current;
1313 #else
1314 static time_t last = 0;
1315 time_t current;
1316 time(&current);
1318 if (difftime(current, last) > 0)
1320 XmUpdateDisplay(widget);
1321 last = current;
1323 #endif
1326 void EndWait(Widget topCursorWidget)
1328 XUndefineCursor(XtDisplay(topCursorWidget), XtWindow(topCursorWidget));
1332 ** Create an X window geometry string from width, height, x, and y values.
1333 ** This is a complement to the X routine XParseGeometry, and uses the same
1334 ** bitmask values (XValue, YValue, WidthValue, HeightValue, XNegative, and
1335 ** YNegative) as defined in <X11/Xutil.h> and documented under XParseGeometry.
1336 ** It expects a string of at least MAX_GEOMETRY_STRING_LEN in which to write
1337 ** result. Note that in a geometry string, it is not possible to supply a y
1338 ** position without an x position. Also note that the X/YNegative flags
1339 ** mean "add a '-' and negate the value" which is kind of odd.
1341 void CreateGeometryString(char *string, int x, int y,
1342 int width, int height, int bitmask)
1344 char *ptr = string;
1345 int nChars;
1347 if (bitmask & WidthValue) {
1348 sprintf(ptr, "%d%n", width, &nChars);
1349 ptr += nChars;
1351 if (bitmask & HeightValue) {
1352 sprintf(ptr, "x%d%n", height, &nChars);
1353 ptr += nChars;
1355 if (bitmask & XValue) {
1356 if (bitmask & XNegative)
1357 sprintf(ptr, "-%d%n", -x, &nChars);
1358 else
1359 sprintf(ptr, "+%d%n", x, &nChars);
1360 ptr += nChars;
1362 if (bitmask & YValue) {
1363 if (bitmask & YNegative)
1364 sprintf(ptr, "-%d%n", -y, &nChars);
1365 else
1366 sprintf(ptr, "+%d%n", y, &nChars);
1367 ptr += nChars;
1369 *ptr = '\0';
1372 /* */
1373 /* passwdCB: callback routine added by PasswordText routine. This routine */
1374 /* echoes each character typed as an asterisk (*) and a few other */
1375 /* necessary things so that the password typed in is not visible */
1376 /* */
1377 static void passwdCB(Widget w, char * passTxt, XmTextVerifyCallbackStruct
1378 *txtVerStr)
1380 /* XmTextVerifyCallbackStruct: */
1381 /* int reason; should be XmCR_MODIFYING_TEXT_VALUE */
1382 /* XEvent *event; points to XEvent that triggered the callback */
1383 /* Boolean doit; indicates whether action should be performed; setting */
1384 /* this to false negates the action */
1385 /* long currInsert, current position of insert cursor */
1386 /* newInsert; position user attempts to position the insert cursor */
1387 /* long startPos, starting position of the text to modify */
1388 /* endPos; ending position of the text to modify */
1389 /* XmTextBlock text; */
1391 /* XmTextBlock (used to pass text around): */
1392 /* char *ptr; points to text to be inserted */
1393 /* int length; Number of bytes (length) */
1394 /* XmTextFormat format; Representations format */
1396 /* XmTextFormat: either FMT8BIT or FMT16BIT */
1399 int numCharsTyped, i, j, pos;
1401 /* ensure text format is 8-bit characters */
1402 if (txtVerStr->text->format != FMT8BIT)
1403 return;
1405 /* verify assumptions */
1406 /* if (txtVerStr->endPos < txtVerStr->startPos)
1407 fprintf(stderr, "User password callback error: endPos < startPos\n");
1408 if (strlen(passTxt) == 0 && txtVerStr->endPos != 0)
1409 fprintf(stderr, "User password callback error: no txt, but end != 0\n");
1411 printf("passTxt = %s, startPos = %d, endPos = %d, txtBlkAddr = %d\n",
1412 passTxt, txtVerStr->startPos, txtVerStr->endPos, txtVerStr->text);
1413 if (txtVerStr->text != NULL && txtVerStr->text->ptr != NULL)
1414 printf(" string typed = %s, length = %d\n", txtVerStr->text->ptr,
1415 txtVerStr->text->length);
1417 /* If necessary, expand/compress passTxt and insert any new text */
1418 if (txtVerStr->text != NULL && txtVerStr->text->ptr != NULL)
1419 numCharsTyped = txtVerStr->text->length;
1420 else
1421 numCharsTyped = 0;
1422 /* numCharsTyped = # chars to insert (that user typed) */
1423 /* j = # chars to expand (+) or compress (-) the password string */
1424 j = numCharsTyped - (txtVerStr->endPos - txtVerStr->startPos);
1425 if (j > 0) /* expand case: start at ending null */
1426 for (pos = strlen(passTxt) + 1; pos >= txtVerStr->endPos; --pos)
1427 passTxt[pos+j] = passTxt[pos];
1428 if (j < 0) /* compress case */
1429 for (pos = txtVerStr->startPos + numCharsTyped;
1430 pos <= (int)strlen(passTxt)+1; ++pos)
1431 passTxt[pos] = passTxt[pos-j];
1432 /* then copy text to be inserted into passTxt */
1433 for (pos = txtVerStr->startPos, i = 0; i < numCharsTyped; ++i) {
1434 passTxt[pos+i] = *(txtVerStr->text->ptr + i);
1435 /* Replace text typed by user with asterisks (*) */
1436 *(txtVerStr->text->ptr + i) = '*';
1438 /* printf(" Password string now = %s\n", passTxt); */
1442 ** Remove the white space (blanks and tabs) from a string
1444 static void removeWhiteSpace(char *string)
1446 char *outPtr = string;
1448 while (TRUE) {
1449 if (*string == 0) {
1450 *outPtr = 0;
1451 return;
1452 } else if (*string != ' ' && *string != '\t')
1453 *(outPtr++) = *(string++);
1454 else
1455 string++;
1460 ** Compares two strings and return TRUE if the two strings
1461 ** are the same, ignoring whitespace and case differences.
1463 static int stripCaseCmp(const char *str1, const char *str2)
1465 const char *c1, *c2;
1467 for (c1=str1, c2=str2; *c1!='\0' && *c2!='\0'; c1++, c2++) {
1468 while (*c1 == ' ' || *c1 == '\t')
1469 c1++;
1470 while (*c2 == ' ' || *c2 == '\t')
1471 c2++;
1472 if (toupper((unsigned char)*c1) != toupper((unsigned char)*c2))
1473 return FALSE;
1475 return *c1 == '\0' && *c2 == '\0';
1478 static void warnHandlerCB(String message)
1480 if (strstr(message, "XtRemoveGrab"))
1481 return;
1482 if (strstr(message, "Attempt to remove non-existant passive grab"))
1483 return;
1484 fputs(message, stderr);
1485 fputc('\n', stderr);
1488 static XModifierKeymap *getKeyboardMapping(Display *display) {
1489 static XModifierKeymap *keyboardMap = NULL;
1491 if (keyboardMap == NULL) {
1492 keyboardMap = XGetModifierMapping(display);
1494 return(keyboardMap);
1498 ** get mask for a modifier
1502 static Modifiers findModifierMapping(Display *display, KeyCode keyCode) {
1503 int i, j;
1504 KeyCode *mapentry;
1505 XModifierKeymap *modMap = getKeyboardMapping(display);
1507 if (modMap == NULL || keyCode == 0) {
1508 return(0);
1511 mapentry = modMap->modifiermap;
1512 for (i = 0; i < 8; ++i) {
1513 for (j = 0; j < (modMap->max_keypermod); ++j) {
1514 if (keyCode == *mapentry) {
1515 return(1 << ((mapentry - modMap->modifiermap) / modMap->max_keypermod));
1517 ++mapentry;
1520 return(0);
1523 static Modifiers getNumLockModMask(Display *display) {
1524 static int numLockMask = -1;
1526 if (numLockMask == -1) {
1527 numLockMask = findModifierMapping(display, XKeysymToKeycode(display, XK_Num_Lock));
1529 return(numLockMask);
1533 ** Grab a key regardless of caps-lock and other silly latching keys.
1537 static void reallyGrabAKey(Widget dialog, int keyCode, Modifiers mask) {
1538 Modifiers numLockMask = getNumLockModMask(XtDisplay(dialog));
1540 if (keyCode == 0) /* No anykey grabs, sorry */
1541 return;
1543 XtGrabKey(dialog, keyCode, mask, True, GrabModeAsync, GrabModeAsync);
1544 XtGrabKey(dialog, keyCode, mask|LockMask, True, GrabModeAsync, GrabModeAsync);
1545 if (numLockMask && numLockMask != LockMask) {
1546 XtGrabKey(dialog, keyCode, mask|numLockMask, True, GrabModeAsync, GrabModeAsync);
1547 XtGrabKey(dialog, keyCode, mask|LockMask|numLockMask, True, GrabModeAsync, GrabModeAsync);
1552 ** Part of dialog mnemonic processing. Search the widget tree under w
1553 ** for widgets with mnemonics. When found, add a passive grab to the
1554 ** dialog widget for the mnemonic character, thus directing mnemonic
1555 ** events to the dialog widget.
1557 static void addMnemonicGrabs(Widget dialog, Widget w, int unmodifiedToo)
1559 char mneString[2];
1560 WidgetList children;
1561 Cardinal numChildren;
1562 int i, isMenu;
1563 KeySym mnemonic = '\0';
1564 unsigned char rowColType;
1565 unsigned int keyCode;
1567 if (XtIsComposite(w)) {
1568 if (XtClass(w) == xmRowColumnWidgetClass) {
1569 XtVaGetValues(w, XmNrowColumnType, &rowColType, NULL);
1570 isMenu = rowColType != XmWORK_AREA;
1571 } else
1572 isMenu = False;
1573 if (!isMenu) {
1574 XtVaGetValues(w, XmNchildren, &children, XmNnumChildren,
1575 &numChildren, NULL);
1576 for (i=0; i<(int)numChildren; i++)
1577 addMnemonicGrabs(dialog, children[i], unmodifiedToo);
1579 } else {
1580 XtVaGetValues(w, XmNmnemonic, &mnemonic, NULL);
1581 if (mnemonic != XK_VoidSymbol && mnemonic != '\0') {
1582 mneString[0] = mnemonic; mneString[1] = '\0';
1583 keyCode = XKeysymToKeycode(XtDisplay(dialog),
1584 XStringToKeysym(mneString));
1585 reallyGrabAKey(dialog, keyCode, Mod1Mask);
1586 if (unmodifiedToo)
1587 reallyGrabAKey(dialog, keyCode, 0);
1593 ** Callback routine for dialog mnemonic processing.
1595 static void mnemonicCB(Widget w, XtPointer callData, XKeyEvent *event)
1597 findAndActivateMnemonic(w, event->keycode);
1601 ** Look for a widget in the widget tree w, with a mnemonic matching
1602 ** keycode. When one is found, simulate a button press on that widget
1603 ** and give it the keyboard focus. If the mnemonic is on a label,
1604 ** look in the userData field of the label to see if it points to
1605 ** another widget, and give that the focus. This routine is just
1606 ** sufficient for NEdit, no doubt it will need to be extended for
1607 ** mnemonics on widgets other than just buttons and text fields.
1609 static void findAndActivateMnemonic(Widget w, unsigned int keycode)
1611 WidgetList children;
1612 Cardinal numChildren;
1613 int i, isMenu;
1614 KeySym mnemonic = '\0';
1615 char mneString[2];
1616 Widget userData;
1617 unsigned char rowColType;
1619 if (XtIsComposite(w)) {
1620 if (XtClass(w) == xmRowColumnWidgetClass) {
1621 XtVaGetValues(w, XmNrowColumnType, &rowColType, NULL);
1622 isMenu = rowColType != XmWORK_AREA;
1623 } else
1624 isMenu = False;
1625 if (!isMenu) {
1626 XtVaGetValues(w, XmNchildren, &children, XmNnumChildren,
1627 &numChildren, NULL);
1628 for (i=0; i<(int)numChildren; i++)
1629 findAndActivateMnemonic(children[i], keycode);
1631 } else {
1632 XtVaGetValues(w, XmNmnemonic, &mnemonic, NULL);
1633 if (mnemonic != '\0') {
1634 mneString[0] = mnemonic; mneString[1] = '\0';
1635 if (XKeysymToKeycode(XtDisplay(XtParent(w)),
1636 XStringToKeysym(mneString)) == keycode) {
1637 if (XtClass(w) == xmLabelWidgetClass ||
1638 XtClass(w) == xmLabelGadgetClass) {
1639 XtVaGetValues(w, XmNuserData, &userData, NULL);
1640 if (userData!=NULL && XtIsWidget(userData) &&
1641 XmIsTraversable(userData))
1642 XmProcessTraversal(userData, XmTRAVERSE_CURRENT);
1643 } else if (XmIsTraversable(w)) {
1644 XmProcessTraversal(w, XmTRAVERSE_CURRENT);
1645 SimulateButtonPress(w);
1653 ** Part of workaround for Motif Caps/Num Lock bug. Search the widget tree
1654 ** under w for widgets with accelerators. When found, add three passive
1655 ** grabs to topWidget, one for the accelerator keysym + modifiers + Caps
1656 ** Lock, one for Num Lock, and one for both, thus directing lock +
1657 ** accelerator events to topWidget.
1659 static void addAccelGrabs(Widget topWidget, Widget w)
1661 WidgetList children;
1662 Widget menu;
1663 Cardinal numChildren;
1664 int i;
1666 if (XtIsComposite(w)) {
1667 XtVaGetValues(w, XmNchildren, &children, XmNnumChildren,
1668 &numChildren, NULL);
1669 for (i=0; i<(int)numChildren; i++)
1670 addAccelGrabs(topWidget, children[i]);
1671 } else if (XtClass(w) == xmCascadeButtonWidgetClass) {
1672 XtVaGetValues(w, XmNsubMenuId, &menu, NULL);
1673 if (menu != NULL)
1674 addAccelGrabs(topWidget, menu);
1675 } else
1676 addAccelGrab(topWidget, w);
1680 ** Grabs the key + modifier defined in the widget's accelerator resource,
1681 ** in combination with the Caps Lock and Num Lock accelerators.
1683 static void addAccelGrab(Widget topWidget, Widget w)
1685 char *accelString = NULL;
1686 KeySym keysym;
1687 unsigned int modifiers;
1688 KeyCode code;
1689 Modifiers numLockMask = getNumLockModMask(XtDisplay(topWidget));
1691 XtVaGetValues(w, XmNaccelerator, &accelString, NULL);
1692 if (accelString == NULL || *accelString == '\0') {
1693 if (accelString != NULL) XtFree(accelString);
1694 return;
1697 if (!parseAccelString(XtDisplay(topWidget), accelString, &keysym, &modifiers)) {
1698 XtFree(accelString);
1699 return;
1701 XtFree(accelString);
1703 /* Check to see if this server has this key mapped. Some cruddy PC X
1704 servers (Xoftware) have terrible default keymaps. If not,
1705 XKeysymToKeycode will return 0. However, it's bad news to pass
1706 that to XtGrabKey because 0 is really "AnyKey" which is definitely
1707 not what we want!! */
1709 code = XKeysymToKeycode(XtDisplay(topWidget), keysym);
1710 if (code == 0)
1711 return;
1713 XtGrabKey(topWidget, code,
1714 modifiers | LockMask, True, GrabModeAsync, GrabModeAsync);
1715 if (numLockMask && numLockMask != LockMask) {
1716 XtGrabKey(topWidget, code,
1717 modifiers | numLockMask, True, GrabModeAsync, GrabModeAsync);
1718 XtGrabKey(topWidget, code,
1719 modifiers | LockMask | numLockMask, True, GrabModeAsync, GrabModeAsync);
1724 ** Read a Motif accelerator string and translate it into a keysym + modifiers.
1725 ** Returns TRUE if the parse was successful, FALSE, if not.
1727 static int parseAccelString(Display *display, const char *string, KeySym *keySym,
1728 unsigned int *modifiers)
1730 #define N_MODIFIERS 12
1731 /*... Is NumLock always Mod3? */
1732 static char *modifierNames[N_MODIFIERS] = {"Ctrl", "Shift", "Alt", "Mod2",
1733 "Mod3", "Mod4", "Mod5", "Button1", "Button2", "Button3", "Button4",
1734 "Button5"};
1735 static unsigned int modifierMasks[N_MODIFIERS] = {ControlMask, ShiftMask,
1736 Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask, Button1Mask, Button2Mask,
1737 Button3Mask, Button4Mask, Button5Mask};
1738 Modifiers numLockMask = getNumLockModMask(display);
1739 char modStr[MAX_ACCEL_LEN];
1740 char evtStr[MAX_ACCEL_LEN];
1741 char keyStr[MAX_ACCEL_LEN];
1742 const char *c, *evtStart, *keyStart;
1743 int i;
1745 if (strlen(string) >= MAX_ACCEL_LEN)
1746 return FALSE;
1748 /* Get the modifier part */
1749 for (c = string; *c != '<'; c++)
1750 if (*c == '\0')
1751 return FALSE;
1752 strncpy(modStr, string, c - string);
1753 modStr[c - string] = '\0';
1755 /* Verify the <key> or <keypress> part */
1756 evtStart = c;
1757 for ( ; *c != '>'; c++)
1758 if (*c == '\0')
1759 return FALSE;
1760 c++;
1761 strncpy(evtStr, evtStart, c - evtStart);
1762 evtStr[c - evtStart] = '\0';
1763 if (!stripCaseCmp(evtStr, "<key>") && !stripCaseCmp(evtStr, "<keypress>"))
1764 return FALSE;
1766 /* Get the keysym part */
1767 keyStart = c;
1768 for ( ; *c != '\0'; c++);
1769 strncpy(keyStr, keyStart, c - keyStart);
1770 keyStr[c - keyStart] = '\0';
1771 *keySym = XStringToKeysym(keyStr);
1773 /* Parse the modifier part */
1774 *modifiers = 0;
1775 c = modStr;
1776 while (*c != '\0') {
1777 while (*c == ' ' || *c == '\t')
1778 c++;
1779 if (*c == '\0')
1780 break;
1781 for (i = 0; i < N_MODIFIERS; i++) {
1782 if (!strncmp(c, modifierNames[i], strlen(modifierNames[i]))) {
1783 c += strlen(modifierNames[i]);
1784 if (modifierMasks[i] != numLockMask) {
1785 *modifiers |= modifierMasks[i];
1787 break;
1790 if (i == N_MODIFIERS)
1791 return FALSE;
1794 return TRUE;
1798 ** Event handler for patching around Motif's lock + accelerator problem.
1799 ** Looks for a menu item in the patched menu hierarchy and invokes its
1800 ** ArmAndActivate action.
1802 static void lockCB(Widget w, XtPointer callData, XEvent *event,
1803 Boolean *continueDispatch)
1805 Modifiers numLockMask = getNumLockModMask(XtDisplay(w));
1806 Widget topMenuWidget = (Widget)callData;
1807 *continueDispatch = TRUE;
1808 if (!(((XKeyEvent *)event)->state & (LockMask | numLockMask)))
1809 return;
1811 if (!findAndActivateAccel(topMenuWidget, ((XKeyEvent *)event)->keycode,
1812 ((XKeyEvent *)event)->state & ~(LockMask | numLockMask), event))
1813 *continueDispatch = FALSE;
1817 ** Search through menu hierarchy under w and look for a button with
1818 ** accelerator matching keyCode + modifiers, and do its action
1820 static int findAndActivateAccel(Widget w, unsigned int keyCode,
1821 unsigned int modifiers, XEvent *event)
1824 WidgetList children;
1825 Widget menu;
1826 Cardinal numChildren;
1827 int i;
1828 char *accelString = NULL;
1829 KeySym keysym;
1830 unsigned int mods;
1832 if (XtIsComposite(w)) {
1833 XtVaGetValues(w, XmNchildren, &children, XmNnumChildren,
1834 &numChildren, NULL);
1835 for (i=0; i<(int)numChildren; i++)
1836 if (findAndActivateAccel(children[i], keyCode, modifiers, event))
1837 return TRUE;
1838 } else if (XtClass(w) == xmCascadeButtonWidgetClass) {
1839 XtVaGetValues(w, XmNsubMenuId, &menu, NULL);
1840 if (menu != NULL)
1841 if (findAndActivateAccel(menu, keyCode, modifiers, event))
1842 return TRUE;
1843 } else {
1844 XtVaGetValues(w, XmNaccelerator, &accelString, NULL);
1845 if (accelString != NULL && *accelString != '\0') {
1846 if (!parseAccelString(XtDisplay(w), accelString, &keysym, &mods))
1847 return FALSE;
1848 if (keyCode == XKeysymToKeycode(XtDisplay(w), keysym) &&
1849 modifiers == mods) {
1850 if (XtIsSensitive(w)) {
1851 XtCallActionProc(w, "ArmAndActivate", event, NULL, 0);
1852 return TRUE;
1857 return FALSE;