1 static const char CVSID
[] = "$Id: misc.c,v 1.54 2003/05/03 09:15:00 edg Exp $";
2 /*******************************************************************************
4 * misc.c -- Miscelaneous Motif convenience functions *
6 * Copyright (C) 1999 Mark Edel *
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 *
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 *
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 *
22 * Nirvana Text Editor *
25 * Written by Mark Edel *
27 *******************************************************************************/
30 #include "../config.h"
53 #include <X11/Intrinsic.h>
54 #include <X11/Xatom.h>
55 #include <X11/keysym.h>
56 #include <X11/keysymdef.h>
59 #include <Xm/LabelG.h>
60 #include <Xm/ToggleB.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>
68 #include <Xm/MessageB.h>
69 #include <Xm/DialogS.h>
70 #include <Xm/SelectioB.h>
72 #include <Xm/FileSB.h>
73 #include <Xm/ScrolledW.h>
79 /* structure for passing history-recall data to callbacks */
86 typedef Widget (*MotifDialogCreationCall
)(Widget
, String
, ArgList
, Cardinal
);
88 /* Maximum size of a history-recall list. Typically never invoked, since
89 user must first make this many entries in the text field, limited for
90 safety, to the maximum reasonable number of times user can hit up-arrow
91 before carpal tunnel syndrome sets in */
92 #define HISTORY_LIST_TRIM_TO 1000
93 #define HISTORY_LIST_MAX 2000
95 /* flags to enable/disable delete key remapping and pointer centered dialogs */
96 static int RemapDeleteEnabled
= True
;
97 static int PointerCenteredDialogsEnabled
= False
;
99 /* bitmap and mask for waiting (wrist-watch) cursor */
100 #define watch_x_hot 7
101 #define watch_y_hot 7
102 #define watch_width 16
103 #define watch_height 16
104 static unsigned char watch_bits
[] = {
105 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07, 0x10, 0x08, 0x08, 0x11,
106 0x04, 0x21, 0x04, 0x21, 0xe4, 0x21, 0x04, 0x20, 0x08, 0x10, 0x10, 0x08,
107 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07
109 #define watch_mask_width 16
110 #define watch_mask_height 16
111 static unsigned char watch_mask_bits
[] = {
112 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf8, 0x1f, 0xfc, 0x3f,
113 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfc, 0x3f, 0xf8, 0x1f,
114 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f
117 static void addMnemonicGrabs(Widget addTo
, Widget w
, int unmodified
);
118 static void mnemonicCB(Widget w
, XtPointer callData
, XKeyEvent
*event
);
119 static void findAndActivateMnemonic(Widget w
, unsigned int keycode
);
120 static void addAccelGrabs(Widget topWidget
, Widget w
);
121 static void addAccelGrab(Widget topWidget
, Widget w
);
122 static int parseAccelString(Display
*display
, const char *string
, KeySym
*keysym
,
123 unsigned int *modifiers
);
124 static void lockCB(Widget w
, XtPointer callData
, XEvent
*event
,
125 Boolean
*continueDispatch
);
126 static int findAndActivateAccel(Widget w
, unsigned int keyCode
,
127 unsigned int modifiers
, XEvent
*event
);
128 static void removeWhiteSpace(char *string
);
129 static int stripCaseCmp(const char *str1
, const char *str2
);
130 static void warnHandlerCB(String message
);
131 static void passwdCB(Widget w
, char * passTxt
, XmTextVerifyCallbackStruct
133 static void histDestroyCB(Widget w
, XtPointer clientData
, XtPointer callData
);
134 static void histArrowKeyEH(Widget w
, XtPointer callData
, XEvent
*event
,
135 Boolean
*continueDispatch
);
136 static ArgList
addParentVisArgs(Widget parent
, ArgList arglist
,
138 static Widget
addParentVisArgsAndCall(MotifDialogCreationCall callRoutine
,
139 Widget parent
, char *name
, ArgList arglist
, Cardinal argcount
);
140 static void scrollDownAP(Widget w
, XEvent
*event
, String
*args
,
142 static void scrollUpAP(Widget w
, XEvent
*event
, String
*args
,
144 static void pageDownAP(Widget w
, XEvent
*event
, String
*args
,
146 static void pageUpAP(Widget w
, XEvent
*event
, String
*args
,
150 ** Set up closeCB to be called when the user selects close from the
151 ** window menu. The close menu item usually activates f.kill which
152 ** sends a WM_DELETE_WINDOW protocol request for the window.
154 void AddMotifCloseCallback(Widget shell
, XtCallbackProc closeCB
, void *arg
)
156 static Atom wmpAtom
, dwAtom
= 0;
157 Display
*display
= XtDisplay(shell
);
159 /* deactivate the built in delete response of killing the application */
160 XtVaSetValues(shell
, XmNdeleteResponse
, XmDO_NOTHING
, NULL
);
162 /* add a delete window protocol callback instead */
164 wmpAtom
= XmInternAtom(display
, "WM_PROTOCOLS", FALSE
);
165 dwAtom
= XmInternAtom(display
, "WM_DELETE_WINDOW", FALSE
);
167 XmAddProtocolCallback(shell
, wmpAtom
, dwAtom
, closeCB
, arg
);
171 ** Motif still generates spurious passive grab warnings on both IBM and SGI
172 ** This routine suppresses them.
174 ** And triggers an annoying message on DEC systems on alpha ->
175 ** See Xt sources, xc/lib/Xt/Error.c:DefaultMsg()):
176 ** actually for some obscure reasons they check for XtError/Warning
177 ** handlers being installed when running as a root process!
178 ** Since this handler doesn't help on non-effected systems we should only
179 ** use it if necessary.
181 void SuppressPassiveGrabWarnings(void)
183 #if !defined(__alpha) && !defined(__EMX__)
184 XtSetWarningHandler(warnHandlerCB
);
189 ** This routine kludges around the problem of backspace not being mapped
190 ** correctly when Motif is used between a server with a delete key in
191 ** the traditional typewriter backspace position and a client that
192 ** expects a backspace key in that position. Though there are three
193 ** distinct levels of key re-mapping in effect when a user is running
194 ** a Motif application, none of these is really appropriate or effective
195 ** for eliminating the delete v.s. backspace problem. Our solution is,
196 ** sadly, to eliminate the forward delete functionality of the delete key
197 ** in favor of backwards delete for both keys. So as not to prevent the
198 ** user or the application from applying other translation table re-mapping,
199 ** we apply re-map the key as a post-processing step, applied after widget
200 ** creation. As a result, the re-mapping necessarily becomes embedded
201 ** throughout an application (wherever text widgets are created), and
202 ** within library routines, including the Nirvana utility library. To
203 ** make this remapping optional, the SetDeleteRemap function provides a
204 ** way for an application to turn this functionality on and off. It is
205 ** recommended that applications that use this routine provide an
206 ** application resource called remapDeleteKey so savvy users can get
207 ** their forward delete functionality back.
209 void RemapDeleteKey(Widget w
)
211 static XtTranslations table
= NULL
;
212 static char *translations
=
213 "~Shift~Ctrl~Meta~Alt<Key>osfDelete: delete-previous-character()\n";
215 if (RemapDeleteEnabled
) {
217 table
= XtParseTranslationTable(translations
);
218 XtOverrideTranslations(w
, table
);
222 void SetDeleteRemap(int state
)
224 RemapDeleteEnabled
= state
;
229 ** The routine adds the passed in top-level Widget's window to our
230 ** window group. On the first call a dummy unmapped window will
231 ** be created to be our leader. This must not be called before the
232 ** Widget has be realized and should be called before the window is
235 void SetWindowGroup(Widget shell
) {
236 static int firstTime
= True
;
237 static Window groupLeader
;
238 Display
*display
= XtDisplay(shell
);
242 /* Create a dummy window to be the group leader for our windows */
244 XClassHint
*classHint
;
246 groupLeader
= XCreateSimpleWindow(display
,
247 RootWindow(display
, DefaultScreen(display
)),
248 1, 1, 1, 1, 0, 0, 0);
250 /* Set it's class hint so it will be identified correctly by the
252 XtGetApplicationNameAndClass(display
, &name
, &class);
253 classHint
= XAllocClassHint();
254 classHint
->res_name
= name
;
255 classHint
->res_class
= class;
256 XSetClassHint(display
, groupLeader
, classHint
);
261 /* Set the window group hint for this shell's window */
262 wmHints
= XAllocWMHints();
263 wmHints
->window_group
= groupLeader
;
264 wmHints
->flags
= WindowGroupHint
;
265 XSetWMHints(display
, XtWindow(shell
), wmHints
);
269 ** This routine resolves a window manager protocol incompatibility between
270 ** the X toolkit and several popular window managers. Using this in place
271 ** of XtRealizeWidget will realize the window in a way which allows the
272 ** affected window managers to apply their own placement strategy to the
273 ** window, as opposed to forcing the window to a specific location.
275 ** One of the hints in the WM_NORMAL_HINTS protocol, PPlacement, gets set by
276 ** the X toolkit (probably part of the Core or Shell widget) when a shell
277 ** widget is realized to the value stored in the XmNx and XmNy resources of the
278 ** Core widget. While callers can set these values, there is no "unset" value
279 ** for these resources. On systems which are more Motif aware, a PPosition
280 ** hint of 0,0, which is the default for XmNx and XmNy, is interpreted as,
281 ** "place this as if no hints were specified". Unfortunately the fvwm family
282 ** of window managers, which are now some of the most popular, interpret this
283 ** as "place this window at (0,0)". This routine intervenes between the
284 ** realizing and the mapping of the window to remove the inappropriate
287 void RealizeWithoutForcingPosition(Widget shell
)
289 XSizeHints
*hints
= XAllocSizeHints();
291 Boolean mappedWhenManaged
;
293 /* Temporarily set value of XmNmappedWhenManaged
294 to stop the window from popping up right away */
295 XtVaGetValues(shell
, XmNmappedWhenManaged
, &mappedWhenManaged
, NULL
);
296 XtVaSetValues(shell
, XmNmappedWhenManaged
, False
, NULL
);
298 /* Realize the widget in unmapped state */
299 XtRealizeWidget(shell
);
301 /* Get rid of the incorrect WMNormal hint */
302 if (XGetWMNormalHints(XtDisplay(shell
), XtWindow(shell
), hints
,
304 hints
->flags
&= ~PPosition
;
305 XSetWMNormalHints(XtDisplay(shell
), XtWindow(shell
), hints
);
309 /* Set WindowGroupHint so the NEdit icons can be grouped; this
310 seems to be necessary starting with Gnome 2.0 */
311 SetWindowGroup(shell
);
316 /* Restore the value of XmNmappedWhenManaged */
317 XtVaSetValues(shell
, XmNmappedWhenManaged
, mappedWhenManaged
, NULL
);
321 ** Older X applications and X servers were mostly designed to operate with
322 ** visual class PseudoColor, because older displays were at most 8 bits
323 ** deep. Modern X servers, however, usually support 24 bit depth and other
324 ** color models. Sun (and others?) still sets their default visual to
325 ** 8-bit PseudoColor, because some of their X applications don't work
326 ** properly with the other color models. The problem with PseudoColor, of
327 ** course, is that users run out of colors in the default colormap, and if
328 ** they install additional colormaps for individual applications, colors
329 ** flash and change weirdly when you change your focus from one application
332 ** In addition to the poor choice of default, a design flaw in Xt makes it
333 ** impossible even for savvy users to specify the XtNvisual resource to
334 ** switch to a deeper visual. The problem is that the colormap resource is
335 ** processed independently of the visual resource, and usually results in a
336 ** colormap for the default visual rather than for the user-selected one.
338 ** This routine should be called before creating a shell widget, to
339 ** pre-process the visual, depth, and colormap resources, and return the
340 ** proper values for these three resources to be passed to XtAppCreateShell.
341 ** Applications which actually require a particular color model (i.e. for
342 ** doing color table animation or dynamic color assignment) should not use
345 ** Note that a consequence of using the "best" as opposed to the default
346 ** visual is that some color resources are still converted with the default
347 ** visual (particularly *background), and these must be avoided by widgets
348 ** which are allowed to handle any visual.
350 ** Returns True if the best visual is the default, False otherwise.
352 Boolean
FindBestVisual(Display
*display
, const char *appName
, const char *appClass
,
353 Visual
**visual
, int *depth
, Colormap
*colormap
)
355 char rsrcName
[256], rsrcClass
[256], *valueString
, *type
, *endPtr
;
357 int screen
= DefaultScreen(display
);
359 long reqID
= -1; /* should hold a 'VisualID' and a '-1' ... */
361 int installColormap
= FALSE
;
362 int maxDepth
, bestClass
, bestVisual
, nVis
, i
, j
;
363 XVisualInfo visTemplate
, *visList
= NULL
;
364 static Visual
*cachedVisual
= NULL
;
365 static Colormap cachedColormap
;
366 static int cachedDepth
= 0;
367 int bestClasses
[] = {StaticGray
, GrayScale
, StaticColor
, PseudoColor
,
368 DirectColor
, TrueColor
};
370 /* If results have already been computed, just return them */
371 if (cachedVisual
!= NULL
) {
372 *visual
= cachedVisual
;
373 *depth
= cachedDepth
;
374 *colormap
= cachedColormap
;
375 return (*visual
== DefaultVisual(display
, screen
));
378 /* Read the visualID and installColormap resources for the application.
379 visualID can be specified either as a number (the visual id as
380 shown by xdpyinfo), as a visual class name, or as Best or Default. */
381 sprintf(rsrcName
,"%s.%s", appName
, "visualID");
382 sprintf(rsrcClass
, "%s.%s", appClass
, "VisualID");
383 if (XrmGetResource(XtDatabase(display
), rsrcName
, rsrcClass
, &type
,
385 valueString
= value
.addr
;
386 reqID
= (int)strtol(valueString
, &endPtr
, 0);
387 if (endPtr
== valueString
) {
389 if (stripCaseCmp(valueString
, "Default"))
390 reqID
= DefaultVisual(display
, screen
)->visualid
;
391 else if (stripCaseCmp(valueString
, "StaticGray"))
392 reqClass
= StaticGray
;
393 else if (stripCaseCmp(valueString
, "StaticColor"))
394 reqClass
= StaticColor
;
395 else if (stripCaseCmp(valueString
, "TrueColor"))
396 reqClass
= TrueColor
;
397 else if (stripCaseCmp(valueString
, "GrayScale"))
398 reqClass
= GrayScale
;
399 else if (stripCaseCmp(valueString
, "PseudoColor"))
400 reqClass
= PseudoColor
;
401 else if (stripCaseCmp(valueString
, "DirectColor"))
402 reqClass
= DirectColor
;
403 else if (!stripCaseCmp(valueString
, "Best"))
404 fprintf(stderr
, "Invalid visualID resource value\n");
407 sprintf(rsrcName
,"%s.%s", appName
, "installColormap");
408 sprintf(rsrcClass
, "%s.%s", appClass
, "InstallColormap");
409 if (XrmGetResource(XtDatabase(display
), rsrcName
, rsrcClass
, &type
,
411 if (stripCaseCmp(value
.addr
, "Yes") || stripCaseCmp(value
.addr
, "True"))
412 installColormap
= TRUE
;
415 visTemplate
.screen
= screen
;
417 /* Generate a list of visuals to consider. (Note, vestigial code for
418 user-requested visual depth is left in, just in case that function
419 might be needed again, but it does nothing) */
421 visTemplate
.visualid
= reqID
;
422 visList
= XGetVisualInfo(display
, VisualScreenMask
|VisualIDMask
,
423 &visTemplate
, &nVis
);
425 fprintf(stderr
, "VisualID resource value not valid\n");
427 if (visList
== NULL
&& reqClass
!= -1 && reqDepth
!= -1) {
428 visTemplate
.class = reqClass
;
429 visTemplate
.depth
= reqDepth
;
430 visList
= XGetVisualInfo(display
,
431 VisualScreenMask
| VisualClassMask
| VisualDepthMask
,
432 &visTemplate
, &nVis
);
434 fprintf(stderr
, "Visual class/depth combination not available\n");
436 if (visList
== NULL
&& reqClass
!= -1) {
437 visTemplate
.class = reqClass
;
438 visList
= XGetVisualInfo(display
, VisualScreenMask
|VisualClassMask
,
439 &visTemplate
, &nVis
);
442 "Visual Class from resource \"visualID\" not available\n");
444 if (visList
== NULL
&& reqDepth
!= -1) {
445 visTemplate
.depth
= reqDepth
;
446 visList
= XGetVisualInfo(display
, VisualScreenMask
|VisualDepthMask
,
447 &visTemplate
, &nVis
);
449 fprintf(stderr
, "Requested visual depth not available\n");
451 if (visList
== NULL
) {
452 visList
= XGetVisualInfo(display
, VisualScreenMask
, &visTemplate
, &nVis
);
453 if (visList
== NULL
) {
454 fprintf(stderr
, "Internal Error: no visuals available?\n");
455 *visual
= DefaultVisual(display
, screen
);
456 *depth
= DefaultDepth(display
, screen
);
457 *colormap
= DefaultColormap(display
, screen
);
462 /* Choose among the visuals in the candidate list. Prefer maximum
463 depth first then matching default, then largest value of bestClass
464 (I'm not sure whether we actually care about class) */
468 for (i
=0; i
< nVis
; i
++) {
469 if (visList
[i
].depth
> maxDepth
) {
470 maxDepth
= visList
[i
].depth
;
474 if (visList
[i
].depth
== maxDepth
) {
475 if (visList
[i
].visual
== DefaultVisual(display
, screen
))
477 if (visList
[bestVisual
].visual
!= DefaultVisual(display
, screen
)) {
478 for (j
= 0; j
< (int)XtNumber(bestClasses
); j
++) {
479 if (visList
[i
].class == bestClasses
[j
] && j
> bestClass
) {
487 *visual
= cachedVisual
= visList
[bestVisual
].visual
;
488 *depth
= cachedDepth
= visList
[bestVisual
].depth
;
490 /* If the chosen visual is not the default, it needs a colormap allocated */
491 if (*visual
== DefaultVisual(display
, screen
) && !installColormap
)
492 *colormap
= cachedColormap
= DefaultColormap(display
, screen
);
494 *colormap
= cachedColormap
= XCreateColormap(display
,
495 RootWindow(display
, screen
), cachedVisual
, AllocNone
);
496 XInstallColormap(display
, cachedColormap
);
498 /* printf("Chose visual with depth %d, class %d, colormap %ld, id 0x%x\n",
499 visList[bestVisual].depth, visList[bestVisual].class,
500 *colormap, cachedVisual->visualid); */
501 /* Fix memory leak */
502 if (visList
!= NULL
) {
506 return (*visual
== DefaultVisual(display
, screen
));
510 ** If you want to use a non-default visual with Motif, shells all have to be
511 ** created with that visual, depth, and colormap, even if the parent has them
512 ** set up properly. Substituting these routines, will append visual args copied
513 ** from the parent widget (CreatePopupMenu and CreatePulldownMenu), or from the
514 ** best visual, obtained via FindBestVisual above (CreateShellWithBestVis).
516 Widget
CreateDialogShell(Widget parent
, char *name
,
517 ArgList arglist
, Cardinal argcount
)
519 return addParentVisArgsAndCall(XmCreateDialogShell
, parent
, name
, arglist
,
524 Widget
CreatePopupMenu(Widget parent
, char *name
, ArgList arglist
,
527 return addParentVisArgsAndCall(XmCreatePopupMenu
, parent
, name
,
532 Widget
CreatePulldownMenu(Widget parent
, char *name
,
533 ArgList arglist
, Cardinal argcount
)
535 return addParentVisArgsAndCall(XmCreatePulldownMenu
, parent
, name
, arglist
,
540 Widget
CreatePromptDialog(Widget parent
, char *name
,
541 ArgList arglist
, Cardinal argcount
)
543 return addParentVisArgsAndCall(XmCreatePromptDialog
, parent
, name
, arglist
,
548 Widget
CreateSelectionDialog(Widget parent
, char *name
,
549 ArgList arglist
, Cardinal argcount
)
551 Widget dialog
= addParentVisArgsAndCall(XmCreateSelectionDialog
, parent
, name
,
553 AddMouseWheelSupport(XmSelectionBoxGetChild(dialog
, XmDIALOG_LIST
));
558 Widget
CreateFormDialog(Widget parent
, char *name
,
559 ArgList arglist
, Cardinal argcount
)
561 return addParentVisArgsAndCall(XmCreateFormDialog
, parent
, name
, arglist
,
566 Widget
CreateFileSelectionDialog(Widget parent
, char *name
,
567 ArgList arglist
, Cardinal argcount
)
569 Widget dialog
= addParentVisArgsAndCall(XmCreateFileSelectionDialog
, parent
,
570 name
, arglist
, argcount
);
571 AddMouseWheelSupport(XmFileSelectionBoxGetChild(dialog
, XmDIALOG_LIST
));
572 AddMouseWheelSupport(XmFileSelectionBoxGetChild(dialog
, XmDIALOG_DIR_LIST
));
577 Widget
CreateQuestionDialog(Widget parent
, char *name
,
578 ArgList arglist
, Cardinal argcount
)
580 return addParentVisArgsAndCall(XmCreateQuestionDialog
, parent
, name
,
585 Widget
CreateMessageDialog(Widget parent
, char *name
,
586 ArgList arglist
, Cardinal argcount
)
588 return addParentVisArgsAndCall(XmCreateMessageDialog
, parent
, name
,
593 Widget
CreateErrorDialog(Widget parent
, char *name
,
594 ArgList arglist
, Cardinal argcount
)
596 return addParentVisArgsAndCall(XmCreateErrorDialog
, parent
, name
, arglist
,
601 Widget
CreateShellWithBestVis(String appName
, String appClass
,
602 WidgetClass
class, Display
*display
, ArgList args
, Cardinal nArgs
)
611 FindBestVisual(display
, appName
, appClass
, &visual
, &depth
, &colormap
);
612 al
= (ArgList
)XtMalloc(sizeof(Arg
) * (nArgs
+ 3));
614 memcpy(al
, args
, sizeof(Arg
) * nArgs
);
615 XtSetArg(al
[ac
], XtNvisual
, visual
); ac
++;
616 XtSetArg(al
[ac
], XtNdepth
, depth
); ac
++;
617 XtSetArg(al
[ac
], XtNcolormap
, colormap
); ac
++;
618 result
= XtAppCreateShell(appName
, appClass
, class, display
, al
, ac
);
624 Widget
CreatePopupShellWithBestVis(String shellName
, WidgetClass
class,
625 Widget parent
, ArgList arglist
, Cardinal argcount
)
628 ArgList al
= addParentVisArgs(parent
, arglist
, &argcount
);
629 result
= XtCreatePopupShell(shellName
, class, parent
, al
, argcount
);
635 ** Extends an argument list for widget creation with additional arguments
636 ** for visual, colormap, and depth. The original argument list is not altered
637 ** and it's the caller's responsability to free the returned list.
639 static ArgList
addParentVisArgs(Widget parent
, ArgList arglist
,
646 Widget parentShell
= parent
;
648 /* Find the application/dialog/menu shell at the top of the widget
649 hierarchy, which has the visual resource being used */
651 if (XtIsShell(parentShell
))
653 if (parentShell
== NULL
) {
654 fprintf(stderr
, "failed to find shell\n");
657 parentShell
= XtParent(parentShell
);
660 /* Add the visual, depth, and colormap resources to the argument list */
661 XtVaGetValues(parentShell
, XtNvisual
, &visual
, XtNdepth
, &depth
,
662 XtNcolormap
, &colormap
, NULL
);
663 al
= (ArgList
)XtMalloc(sizeof(Arg
) * ((*argcount
) + 3));
664 if ((*argcount
) != 0)
665 memcpy(al
, arglist
, sizeof(Arg
) * (*argcount
));
666 XtSetArg(al
[*argcount
], XtNvisual
, visual
); (*argcount
)++;
667 XtSetArg(al
[*argcount
], XtNdepth
, depth
); (*argcount
)++;
668 XtSetArg(al
[*argcount
], XtNcolormap
, colormap
); (*argcount
)++;
674 ** Calls one of the Motif widget creation routines, splicing in additional
675 ** arguments for visual, colormap, and depth.
677 static Widget
addParentVisArgsAndCall(MotifDialogCreationCall createRoutine
,
678 Widget parent
, char *name
, ArgList arglist
, Cardinal argcount
)
681 ArgList al
= addParentVisArgs(parent
, arglist
, &argcount
);
682 result
= (*createRoutine
)(parent
, name
, al
, argcount
);
688 ** ManageDialogCenteredOnPointer is used in place of XtManageChild for
689 ** popping up a dialog to enable the dialog to be centered under the
690 ** mouse pointer. Whether it pops up the dialog centered under the pointer
691 ** or in its default position centered over the parent widget, depends on
692 ** the value set in the SetPointerCenteredDialogs call.
693 ** Additionally, this function constrains the size of the dialog to the
694 ** screen's size, to avoid insanely wide dialogs with obscured buttons.
696 void ManageDialogCenteredOnPointer(Widget dialogChild
)
698 Widget shell
= XtParent(dialogChild
);
701 unsigned int width
, height
, borderWidth
, depth
;
702 int x
, y
, winX
, winY
, maxX
, maxY
, maxWidth
, maxHeight
;
703 Dimension xtWidth
, xtHeight
;
704 Boolean mappedWhenManaged
;
705 static const int slop
= 25;
707 /* Temporarily set value of XmNmappedWhenManaged
708 to stop the dialog from popping up right away */
709 XtVaGetValues(shell
, XmNmappedWhenManaged
, &mappedWhenManaged
, NULL
);
710 XtVaSetValues(shell
, XmNmappedWhenManaged
, False
, NULL
);
712 /* Ensure that the dialog doesn't get wider/taller than the screen.
713 We use a hard-coded "slop" size because we don't know the border
714 width until it's on screen, and by then it's too late. Putting
715 this before managing the widgets allows it to get the geometry
716 right on the first pass and also prevents the user from
717 accidentally resizing too wide. */
718 maxWidth
= XtScreen(shell
)->width
- slop
;
719 maxHeight
= XtScreen(shell
)->height
- slop
;
722 XmNmaxWidth
, maxWidth
,
723 XmNmaxHeight
, maxHeight
,
726 /* Manage the dialog */
727 XtManageChild(dialogChild
);
729 /* Check to see if the window manager doesn't respect XmNmaxWidth
730 and XmNmaxHeight on the first geometry pass (sawfish, twm, fvwm).
731 For this to work XmNresizePolicy must be XmRESIZE_NONE, otherwise
732 the dialog will try to expand anyway. */
733 XtVaGetValues(shell
, XmNwidth
, &xtWidth
, XmNheight
, &xtHeight
, NULL
);
734 if (xtWidth
> maxWidth
)
735 XtVaSetValues(shell
, XmNwidth
, (Dimension
) maxWidth
, NULL
);
736 if (xtHeight
> maxHeight
)
737 XtVaSetValues(shell
, XmNheight
, (Dimension
) maxHeight
, NULL
);
739 /* Only set the x/y position if the centering option is enabled.
740 Avoid getting the coordinates if not so, to save a few round-trips
742 if (PointerCenteredDialogsEnabled
) {
743 /* Get the pointer position (x, y) */
744 XQueryPointer(XtDisplay(shell
), XtWindow(shell
), &root
, &child
,
745 &x
, &y
, &winX
, &winY
, &mask
);
747 /* Translate the pointer position (x, y) into a position for the new
748 window that will place the pointer at its center */
749 XGetGeometry(XtDisplay(shell
), XtWindow(shell
), &root
, &winX
, &winY
,
750 &width
, &height
, &borderWidth
, &depth
);
751 width
+= 2 * borderWidth
;
752 height
+= 2 * borderWidth
;
757 /* Ensure that the dialog remains on screen */
758 maxX
= maxWidth
- width
;
759 maxY
= maxHeight
- height
;
760 if (x
> maxX
) x
= maxX
;
762 if (y
> maxY
) y
= maxY
;
765 /* Some window managers (Sawfish) don't appear to respond
766 to the geometry set call in synchronous mode. This causes
767 the window to delay XmNwmTimeout (default 5 seconds) before
768 posting, and it is very annoying. See "man VendorShell" for
770 XtVaSetValues(shell
, XmNuseAsyncGeometry
, True
, NULL
);
772 /* Set desired window position in the DialogShell */
773 XtVaSetValues(shell
, XmNx
, x
, XmNy
, y
, NULL
);
779 /* Restore the value of XmNmappedWhenManaged */
780 XtVaSetValues(shell
, XmNmappedWhenManaged
, mappedWhenManaged
, NULL
);
784 ** Cause dialogs created by libNUtil.a routines (such as DialogF),
785 ** and dialogs which use ManageDialogCenteredOnPointer to pop up
786 ** over the pointer (state = True), or pop up in their default
787 ** positions (state = False)
789 void SetPointerCenteredDialogs(int state
)
791 PointerCenteredDialogsEnabled
= state
;
796 ** Raise a window to the top and give it the input focus. Setting input focus
797 ** is important on systems which use explict (rather than pointer) focus.
799 ** The X alternatives XMapRaised, and XSetInputFocus both have problems.
800 ** XMapRaised only gives the window the focus if it was initially not visible,
801 ** and XSetInputFocus sets the input focus, but crashes if the window is not
804 ** This routine should also be used in the case where a dialog is popped up and
805 ** subsequent calls to the dialog function use a flag, or the XtIsManaged, to
806 ** decide whether to create a new instance of the dialog, because on slower
807 ** systems, events can intervene while a dialog is still on its way up and its
808 ** window is still invisible, causing a subtle crash potential if
809 ** XSetInputFocus is used.
811 void RaiseShellWindow(Widget shell
)
813 RaiseWindow(XtDisplay(shell
), XtWindow(shell
));
817 void RaiseWindow(Display
*display
, Window w
)
819 XWindowAttributes winAttr
;
821 XGetWindowAttributes(display
, w
, &winAttr
);
822 if (winAttr
.map_state
== IsViewable
)
823 XSetInputFocus(display
, w
, RevertToParent
, CurrentTime
);
824 XMapRaised(display
, w
);
828 ** Add a handler for mnemonics in a dialog (Motif currently only handles
829 ** mnemonics in menus) following the example of M.S. Windows. To add
830 ** mnemonics to a dialog, set the XmNmnemonic resource, as you would in
831 ** a menu, on push buttons or toggle buttons, and call this function
832 ** when the dialog is fully constructed. Mnemonics added or changed
833 ** after this call will not be noticed. To add a mnemonic to a text field
834 ** or list, set the XmNmnemonic resource on the appropriate label and set
835 ** the XmNuserData resource of the label to the widget to get the focus
836 ** when the mnemonic is typed.
838 void AddDialogMnemonicHandler(Widget dialog
, int unmodifiedToo
)
840 XtAddEventHandler(dialog
, KeyPressMask
, False
,
841 (XtEventHandler
)mnemonicCB
, (XtPointer
)0);
842 addMnemonicGrabs(dialog
, dialog
, unmodifiedToo
);
846 ** Removes the event handler and key-grabs added by AddDialogMnemonicHandler
848 void RemoveDialogMnemonicHandler(Widget dialog
)
850 XtUngrabKey(dialog
, AnyKey
, Mod1Mask
);
851 XtRemoveEventHandler(dialog
, KeyPressMask
, False
,
852 (XtEventHandler
)mnemonicCB
, (XtPointer
)0);
856 ** Patch around Motif's poor handling of menu accelerator keys. Motif
857 ** does not process menu accelerators when the caps lock or num lock
858 ** keys are engaged. To enable accelerators in these cases, call this
859 ** routine with the completed menu bar widget as "topMenuContainer", and
860 ** the top level shell widget as "topWidget". It will add key grabs for
861 ** all of the accelerators it finds in the topMenuContainer menu tree, and
862 ** an event handler which can process dropped accelerator events by (again)
863 ** traversing the menu tree looking for matching accelerators, and invoking
864 ** the appropriate button actions. Any dynamic additions to the menus
865 ** require a call to UpdateAccelLockPatch to add the additional grabs.
866 ** Unfortunately, these grabs can not be removed.
868 void AccelLockBugPatch(Widget topWidget
, Widget topMenuContainer
)
870 XtAddEventHandler(topWidget
, KeyPressMask
, False
, lockCB
, topMenuContainer
);
871 addAccelGrabs(topWidget
, topMenuContainer
);
875 ** Add additional key grabs for new menu items added to the menus, for
876 ** patching around the Motif Caps/Num Lock problem. "topWidget" must be
877 ** the same widget passed in the original call to AccelLockBugPatch.
879 void UpdateAccelLockPatch(Widget topWidget
, Widget newButton
)
881 addAccelGrab(topWidget
, newButton
);
887 ** Under some circumstances, popping down a dialog and its parent in
888 ** rapid succession causes a crash. This routine delays and
889 ** processs events until receiving a ReparentNotify event.
890 ** (I have no idea why a ReparentNotify event occurs at all, but it does
891 ** mark the point where it is safe to destroy or pop down the parent, and
892 ** it might have something to do with the bug.) There is a failsafe in
893 ** the form of a ~1.5 second timeout in case no ReparentNotify arrives.
894 ** Use this sparingly, only when real crashes are observed, and periodically
895 ** check to make sure that it is still necessary.
897 void PopDownBugPatch(Widget w
)
901 stopTime
= time(NULL
) + 1;
902 while (time(NULL
) <= stopTime
) {
904 XtAppContext context
= XtWidgetToApplicationContext(w
);
905 XtAppPeekEvent(context
, &event
);
906 if (event
.xany
.type
== ReparentNotify
)
908 XtAppProcessEvent(context
, XtIMAll
);
913 ** Convert a compound string to a C style null terminated string.
914 ** Returned string must be freed by the caller.
916 char *GetXmStringText(XmString fromString
)
918 XmStringContext context
;
919 char *text
, *toPtr
, *toString
, *fromPtr
;
920 XmStringCharSet charset
;
921 XmStringDirection direction
;
924 /* Malloc a buffer large enough to hold the string. XmStringLength
925 should always be slightly longer than necessary, but won't be
926 shorter than the equivalent null-terminated string */
927 toString
= XtMalloc(XmStringLength(fromString
));
929 /* loop over all of the segments in the string, copying each segment
930 into the output string and converting separators into newlines */
931 XmStringInitContext(&context
, fromString
);
933 while (XmStringGetNextSegment(context
, &text
,
934 &charset
, &direction
, &separator
)) {
935 for (fromPtr
=text
; *fromPtr
!='\0'; fromPtr
++)
941 /* terminate the string, free the context, and return the string */
943 XmStringFreeContext(context
);
948 ** Get the XFontStruct that corresponds to the default (first) font in
949 ** a Motif font list. Since Motif stores this, it saves us from storing
950 ** it or querying it from the X server.
952 XFontStruct
*GetDefaultFontStruct(XmFontList font
)
955 XmFontContext context
;
956 XmStringCharSet charset
;
958 XmFontListInitFontContext(&context
, font
);
959 XmFontListGetNextFont(context
, &charset
, &fs
);
960 XmFontListFreeFontContext(context
);
966 ** Create a string table suitable for passing to XmList widgets
968 XmString
* StringTable(int count
, ... )
976 array
= (XmString
*)XtMalloc((count
+1) * sizeof(XmString
));
977 for(i
= 0; i
< count
; i
++ ) {
978 str
= va_arg(ap
, char *);
979 array
[i
] = XmStringCreateSimple(str
);
981 array
[i
] = (XmString
)0;
986 void FreeStringTable(XmString
*table
)
990 for(i
= 0; table
[i
] != 0; i
++)
991 XmStringFree(table
[i
]);
992 XtFree((char *)table
);
996 ** Simulate a button press. The purpose of this routine is show users what
997 ** is happening when they take an action with a non-obvious side effect,
998 ** such as when a user double clicks on a list item. The argument is an
999 ** XmPushButton widget to "press"
1001 void SimulateButtonPress(Widget widget
)
1005 memset((char *)&keyEvent
, 0, sizeof(XKeyPressedEvent
));
1006 keyEvent
.type
= KeyPress
;
1007 keyEvent
.xkey
.serial
= 1;
1008 keyEvent
.xkey
.send_event
= True
;
1010 if (XtIsSubclass(widget
, xmGadgetClass
))
1012 /* On some Motif implementations, asking a gadget for its
1013 window will crash, rather than return the window of its
1015 Widget parent
= XtParent(widget
);
1016 keyEvent
.xkey
.display
= XtDisplay(parent
);
1017 keyEvent
.xkey
.window
= XtWindow(parent
);
1019 XtCallActionProc(parent
, "ManagerGadgetSelect",
1020 &keyEvent
, NULL
, 0);
1024 keyEvent
.xkey
.display
= XtDisplay(widget
);
1025 keyEvent
.xkey
.window
= XtWindow(widget
);
1027 XtCallActionProc(widget
, "ArmAndActivate", &keyEvent
, NULL
, 0);
1032 ** Add an item to an already established pull-down or pop-up menu, including
1033 ** mnemonics, accelerators and callbacks.
1035 Widget
AddMenuItem(Widget parent
, char *name
, char *label
,
1036 char mnemonic
, char *acc
, char *accText
,
1037 XtCallbackProc callback
, void *cbArg
)
1042 button
= XtVaCreateManagedWidget(name
, xmPushButtonWidgetClass
, parent
,
1043 XmNlabelString
, st1
=XmStringCreateSimple(label
),
1044 XmNmnemonic
, mnemonic
,
1045 XmNacceleratorText
, st2
=XmStringCreateSimple(accText
),
1046 XmNaccelerator
, acc
, NULL
);
1047 XtAddCallback(button
, XmNactivateCallback
, callback
, cbArg
);
1054 ** Add a toggle button item to an already established pull-down or pop-up
1055 ** menu, including mnemonics, accelerators and callbacks.
1057 Widget
AddMenuToggle(Widget parent
, char *name
, char *label
,
1058 char mnemonic
, char *acc
, char *accText
,
1059 XtCallbackProc callback
, void *cbArg
, int set
)
1064 button
= XtVaCreateManagedWidget(name
, xmToggleButtonWidgetClass
, parent
,
1065 XmNlabelString
, st1
=XmStringCreateSimple(label
),
1066 XmNmnemonic
, mnemonic
,
1067 XmNacceleratorText
, st2
=XmStringCreateSimple(accText
),
1068 XmNaccelerator
, acc
,
1070 XtAddCallback(button
, XmNvalueChangedCallback
, callback
, cbArg
);
1077 ** Add a separator line to a menu
1079 Widget
AddMenuSeparator(Widget parent
, char *name
)
1083 button
= XmCreateSeparator(parent
, name
, NULL
, 0);
1084 XtManageChild(button
);
1089 ** Add a sub-menu to an established pull-down or pop-up menu, including
1090 ** mnemonics, accelerators and callbacks. Returns the menu pane of the
1093 Widget
AddSubMenu(Widget parent
, char *name
, char *label
, char mnemonic
)
1098 menu
= CreatePulldownMenu(parent
, name
, NULL
, 0);
1099 XtVaCreateManagedWidget(name
, xmCascadeButtonWidgetClass
, parent
,
1100 XmNlabelString
, st1
=XmStringCreateSimple(label
),
1101 XmNmnemonic
, mnemonic
,
1102 XmNsubMenuId
, menu
, NULL
);
1108 ** SetIntLabel, SetFloatLabel, SetIntText, SetFloatText
1110 ** Set the text of a motif label or text widget to show an integer or
1113 void SetIntLabel(Widget label
, int value
)
1115 char labelString
[20];
1118 sprintf(labelString
, "%d", value
);
1119 s1
=XmStringCreateSimple(labelString
);
1120 XtVaSetValues(label
, XmNlabelString
, s1
, NULL
);
1125 void SetFloatLabel(Widget label
, double value
)
1127 char labelString
[20];
1130 sprintf(labelString
, "%g", value
);
1131 s1
=XmStringCreateSimple(labelString
);
1132 XtVaSetValues(label
, XmNlabelString
, s1
, NULL
);
1137 void SetIntText(Widget text
, int value
)
1139 char labelString
[20];
1141 sprintf(labelString
, "%d", value
);
1142 XmTextSetString(text
, labelString
);
1146 void SetFloatText(Widget text
, double value
)
1148 char labelString
[20];
1150 sprintf(labelString
, "%g", value
);
1151 XmTextSetString(text
, labelString
);
1155 ** GetIntText, GetFloatText, GetIntTextWarn, GetFloatTextWarn
1157 ** Get the text of a motif text widget as an integer or floating point number.
1158 ** The functions will return TEXT_READ_OK of the value was read correctly.
1159 ** If not, they will return either TEXT_IS_BLANK, or TEXT_NOT_NUMBER. The
1160 ** GetIntTextWarn and GetFloatTextWarn will display a dialog warning the
1161 ** user that the value could not be read. The argument fieldName is used
1162 ** in the dialog to help the user identify where the problem is. Set
1163 ** warnBlank to true if a blank field is also considered an error.
1165 int GetFloatText(Widget text
, double *value
)
1167 char *strValue
, *endPtr
;
1170 strValue
= XmTextGetString(text
); /* Get Value */
1171 removeWhiteSpace(strValue
); /* Remove blanks and tabs */
1172 *value
= strtod(strValue
, &endPtr
); /* Convert string to double */
1173 if (strlen(strValue
) == 0) /* String is empty */
1174 retVal
= TEXT_IS_BLANK
;
1175 else if (*endPtr
!= '\0') /* Whole string not parsed */
1176 retVal
= TEXT_NOT_NUMBER
;
1178 retVal
= TEXT_READ_OK
;
1183 int GetIntText(Widget text
, int *value
)
1185 char *strValue
, *endPtr
;
1188 strValue
= XmTextGetString(text
); /* Get Value */
1189 removeWhiteSpace(strValue
); /* Remove blanks and tabs */
1190 *value
= strtol(strValue
, &endPtr
, 10); /* Convert string to long */
1191 if (strlen(strValue
) == 0) /* String is empty */
1192 retVal
= TEXT_IS_BLANK
;
1193 else if (*endPtr
!= '\0') /* Whole string not parsed */
1194 retVal
= TEXT_NOT_NUMBER
;
1196 retVal
= TEXT_READ_OK
;
1201 int GetFloatTextWarn(Widget text
, double *value
, const char *fieldName
,
1207 result
= GetFloatText(text
, value
);
1208 if (result
== TEXT_READ_OK
|| (result
== TEXT_IS_BLANK
&& !warnBlank
))
1210 valueStr
= XmTextGetString(text
);
1212 if (result
== TEXT_IS_BLANK
)
1214 DialogF(DF_ERR
, text
, 1, "Warning", "Please supply %s value", "Dismiss",
1216 } else /* TEXT_NOT_NUMBER */
1218 DialogF (DF_ERR
, text
, 1, "Warning", "Can't read %s value: \"%s\"",
1219 "Dismiss", fieldName
, valueStr
);
1226 int GetIntTextWarn(Widget text
, int *value
, const char *fieldName
, int warnBlank
)
1231 result
= GetIntText(text
, value
);
1232 if (result
== TEXT_READ_OK
|| (result
== TEXT_IS_BLANK
&& !warnBlank
))
1234 valueStr
= XmTextGetString(text
);
1236 if (result
== TEXT_IS_BLANK
)
1238 DialogF (DF_ERR
, text
, 1, "Warning", "Please supply a value for %s",
1239 "Dismiss", fieldName
);
1240 } else /* TEXT_NOT_NUMBER */
1242 DialogF (DF_ERR
, text
, 1, "Warning",
1243 "Can't read integer value \"%s\" in %s", "Dismiss", valueStr
,
1251 int TextWidgetIsBlank(Widget textW
)
1256 str
= XmTextGetString(textW
);
1257 removeWhiteSpace(str
);
1258 retVal
= *str
== '\0';
1264 ** Turn a multi-line editing text widget into a fake single line text area
1265 ** by disabling the translation for Return. This is a way to give users
1266 ** extra space, by allowing wrapping, but still prohibiting newlines.
1267 ** (SINGLE_LINE_EDIT mode can't be used, in this case, because it forces
1268 ** the widget to be one line high).
1270 void MakeSingleLineTextW(Widget textW
)
1272 static XtTranslations noReturnTable
= NULL
;
1273 static char *noReturnTranslations
= "<Key>Return: activate()\n";
1275 if (noReturnTable
== NULL
)
1276 noReturnTable
= XtParseTranslationTable(noReturnTranslations
);
1277 XtOverrideTranslations(textW
, noReturnTable
);
1281 ** Add up-arrow/down-arrow recall to a single line text field. When user
1282 ** presses up-arrow, string is cleared and recent entries are presented,
1283 ** moving to older ones as each successive up-arrow is pressed. Down-arrow
1284 ** moves to more recent ones, final down-arrow clears the field. Associated
1285 ** routine, AddToHistoryList, makes maintaining a history list easier.
1287 ** Arguments are the widget, and pointers to the history list and number of
1288 ** items, which are expected to change periodically.
1290 void AddHistoryToTextWidget(Widget textW
, char ***historyList
, int *nItems
)
1294 /* create a data structure for passing history info to the callbacks */
1295 histData
= (histInfo
*)XtMalloc(sizeof(histInfo
));
1296 histData
->list
= historyList
;
1297 histData
->nItems
= nItems
;
1298 histData
->index
= -1;
1300 /* Add an event handler for handling up/down arrow events */
1301 XtAddEventHandler(textW
, KeyPressMask
, False
,
1302 (XtEventHandler
)histArrowKeyEH
, histData
);
1304 /* Add a destroy callback for freeing history data structure */
1305 XtAddCallback(textW
, XmNdestroyCallback
, histDestroyCB
, histData
);
1308 static void histDestroyCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1310 XtFree((char *)clientData
);
1313 static void histArrowKeyEH(Widget w
, XtPointer callData
, XEvent
*event
,
1314 Boolean
*continueDispatch
)
1316 histInfo
*histData
= (histInfo
*)callData
;
1317 KeySym keysym
= XLookupKeysym((XKeyEvent
*)event
, 0);
1319 /* only process up and down arrow keys */
1320 if (keysym
!= XK_Up
&& keysym
!= XK_Down
)
1323 /* increment or decrement the index depending on which arrow was pressed */
1324 histData
->index
+= (keysym
== XK_Up
) ? 1 : -1;
1326 /* if the index is out of range, beep, fix it up, and return */
1327 if (histData
->index
< -1) {
1328 histData
->index
= -1;
1329 XBell(XtDisplay(w
), 0);
1332 if (histData
->index
>= *histData
->nItems
) {
1333 histData
->index
= *histData
->nItems
- 1;
1334 XBell(XtDisplay(w
), 0);
1338 /* Change the text field contents */
1339 XmTextSetString(w
, histData
->index
== -1 ? "" :
1340 (*histData
->list
)[histData
->index
]);
1344 ** Copies a string on to the end of history list, which may be reallocated
1345 ** to make room. If historyList grows beyond its internally set boundary
1346 ** for size (HISTORY_LIST_MAX), it is trimmed back to a smaller size
1347 ** (HISTORY_LIST_TRIM_TO). Before adding to the list, checks if the item
1348 ** is a duplicate of the last item. If so, it is not added.
1350 void AddToHistoryList(char *newItem
, char ***historyList
, int *nItems
)
1355 if (*nItems
!= 0 && !strcmp(newItem
, **historyList
))
1357 if (*nItems
== HISTORY_LIST_MAX
) {
1358 for (i
=HISTORY_LIST_TRIM_TO
; i
<HISTORY_LIST_MAX
; i
++)
1359 XtFree((*historyList
)[i
]);
1360 *nItems
= HISTORY_LIST_TRIM_TO
;
1362 newList
= (char **)XtMalloc(sizeof(char *) * (*nItems
+ 1));
1363 for (i
=0; i
< *nItems
; i
++)
1364 newList
[i
+1] = (*historyList
)[i
];
1365 if (*nItems
!= 0 && *historyList
!= NULL
)
1366 XtFree((char *)*historyList
);
1368 newList
[0] = XtNewString(newItem
);
1369 *historyList
= newList
;
1373 * PasswordText - routine to add a callback to any text widget so that all
1374 * text typed by the user is echoed with asterisks, allowing
1375 * a password to be typed in without being seen.
1377 * parameters: w - text widget to add the callback to
1378 * passTxt - pointer to a string created by caller of this routine.
1379 * **NOTE** The length of this string should be one
1380 * greater than the maximum specified by XmNmaxLength.
1381 * This string is set to empty just before the callback
1385 void PasswordText(Widget w
, char *passTxt
)
1388 XtAddCallback(w
, XmNmodifyVerifyCallback
, (XtCallbackProc
)passwdCB
,passTxt
);
1392 ** BeginWait/EndWait
1394 ** Display/Remove a watch cursor over topCursorWidget and its descendents
1396 void BeginWait(Widget topCursorWidget
)
1398 Display
*display
= XtDisplay(topCursorWidget
);
1402 static Cursor waitCursor
= 0;
1404 /* if the watch cursor hasn't been created yet, create it */
1406 pixmap
= XCreateBitmapFromData(display
, DefaultRootWindow(display
),
1407 (char *)watch_bits
, watch_width
, watch_height
);
1409 maskPixmap
= XCreateBitmapFromData(display
, DefaultRootWindow(display
),
1410 (char *)watch_mask_bits
, watch_width
, watch_height
);
1412 xcolors
[0].pixel
= BlackPixelOfScreen(DefaultScreenOfDisplay(display
));
1413 xcolors
[1].pixel
= WhitePixelOfScreen(DefaultScreenOfDisplay(display
));
1415 XQueryColors(display
, DefaultColormapOfScreen(
1416 DefaultScreenOfDisplay(display
)), xcolors
, 2);
1417 waitCursor
= XCreatePixmapCursor(display
, pixmap
, maskPixmap
,
1418 &xcolors
[0], &xcolors
[1], watch_x_hot
, watch_y_hot
);
1419 XFreePixmap(display
, pixmap
);
1420 XFreePixmap(display
, maskPixmap
);
1423 /* display the cursor */
1424 XDefineCursor(display
, XtWindow(topCursorWidget
), waitCursor
);
1427 void BusyWait(Widget widget
)
1430 static const int timeout
= 100000; /* 1/10 sec = 100 ms = 100,000 us */
1431 static struct timeval last
= { 0, 0 };
1432 struct timeval current
;
1433 gettimeofday(¤t
, NULL
);
1435 if ((current
.tv_sec
!= last
.tv_sec
) ||
1436 (current
.tv_usec
- last
.tv_usec
> timeout
))
1438 XmUpdateDisplay(widget
);
1442 static time_t last
= 0;
1446 if (difftime(current
, last
) > 0)
1448 XmUpdateDisplay(widget
);
1454 void EndWait(Widget topCursorWidget
)
1456 XUndefineCursor(XtDisplay(topCursorWidget
), XtWindow(topCursorWidget
));
1460 ** Create an X window geometry string from width, height, x, and y values.
1461 ** This is a complement to the X routine XParseGeometry, and uses the same
1462 ** bitmask values (XValue, YValue, WidthValue, HeightValue, XNegative, and
1463 ** YNegative) as defined in <X11/Xutil.h> and documented under XParseGeometry.
1464 ** It expects a string of at least MAX_GEOMETRY_STRING_LEN in which to write
1465 ** result. Note that in a geometry string, it is not possible to supply a y
1466 ** position without an x position. Also note that the X/YNegative flags
1467 ** mean "add a '-' and negate the value" which is kind of odd.
1469 void CreateGeometryString(char *string
, int x
, int y
,
1470 int width
, int height
, int bitmask
)
1475 if (bitmask
& WidthValue
) {
1476 sprintf(ptr
, "%d%n", width
, &nChars
);
1479 if (bitmask
& HeightValue
) {
1480 sprintf(ptr
, "x%d%n", height
, &nChars
);
1483 if (bitmask
& XValue
) {
1484 if (bitmask
& XNegative
)
1485 sprintf(ptr
, "-%d%n", -x
, &nChars
);
1487 sprintf(ptr
, "+%d%n", x
, &nChars
);
1490 if (bitmask
& YValue
) {
1491 if (bitmask
& YNegative
)
1492 sprintf(ptr
, "-%d%n", -y
, &nChars
);
1494 sprintf(ptr
, "+%d%n", y
, &nChars
);
1501 /* passwdCB: callback routine added by PasswordText routine. This routine */
1502 /* echoes each character typed as an asterisk (*) and a few other */
1503 /* necessary things so that the password typed in is not visible */
1505 static void passwdCB(Widget w
, char * passTxt
, XmTextVerifyCallbackStruct
1508 /* XmTextVerifyCallbackStruct: */
1509 /* int reason; should be XmCR_MODIFYING_TEXT_VALUE */
1510 /* XEvent *event; points to XEvent that triggered the callback */
1511 /* Boolean doit; indicates whether action should be performed; setting */
1512 /* this to false negates the action */
1513 /* long currInsert, current position of insert cursor */
1514 /* newInsert; position user attempts to position the insert cursor */
1515 /* long startPos, starting position of the text to modify */
1516 /* endPos; ending position of the text to modify */
1517 /* XmTextBlock text; */
1519 /* XmTextBlock (used to pass text around): */
1520 /* char *ptr; points to text to be inserted */
1521 /* int length; Number of bytes (length) */
1522 /* XmTextFormat format; Representations format */
1524 /* XmTextFormat: either FMT8BIT or FMT16BIT */
1527 int numCharsTyped
, i
, j
, pos
;
1529 /* ensure text format is 8-bit characters */
1530 if (txtVerStr
->text
->format
!= FMT8BIT
)
1533 /* verify assumptions */
1534 /* if (txtVerStr->endPos < txtVerStr->startPos)
1535 fprintf(stderr, "User password callback error: endPos < startPos\n");
1536 if (strlen(passTxt) == 0 && txtVerStr->endPos != 0)
1537 fprintf(stderr, "User password callback error: no txt, but end != 0\n");
1539 printf("passTxt = %s, startPos = %d, endPos = %d, txtBlkAddr = %d\n",
1540 passTxt, txtVerStr->startPos, txtVerStr->endPos, txtVerStr->text);
1541 if (txtVerStr->text != NULL && txtVerStr->text->ptr != NULL)
1542 printf(" string typed = %s, length = %d\n", txtVerStr->text->ptr,
1543 txtVerStr->text->length);
1545 /* If necessary, expand/compress passTxt and insert any new text */
1546 if (txtVerStr
->text
!= NULL
&& txtVerStr
->text
->ptr
!= NULL
)
1547 numCharsTyped
= txtVerStr
->text
->length
;
1550 /* numCharsTyped = # chars to insert (that user typed) */
1551 /* j = # chars to expand (+) or compress (-) the password string */
1552 j
= numCharsTyped
- (txtVerStr
->endPos
- txtVerStr
->startPos
);
1553 if (j
> 0) /* expand case: start at ending null */
1554 for (pos
= strlen(passTxt
) + 1; pos
>= txtVerStr
->endPos
; --pos
)
1555 passTxt
[pos
+j
] = passTxt
[pos
];
1556 if (j
< 0) /* compress case */
1557 for (pos
= txtVerStr
->startPos
+ numCharsTyped
;
1558 pos
<= (int)strlen(passTxt
)+1; ++pos
)
1559 passTxt
[pos
] = passTxt
[pos
-j
];
1560 /* then copy text to be inserted into passTxt */
1561 for (pos
= txtVerStr
->startPos
, i
= 0; i
< numCharsTyped
; ++i
) {
1562 passTxt
[pos
+i
] = *(txtVerStr
->text
->ptr
+ i
);
1563 /* Replace text typed by user with asterisks (*) */
1564 *(txtVerStr
->text
->ptr
+ i
) = '*';
1566 /* printf(" Password string now = %s\n", passTxt); */
1570 ** Remove the white space (blanks and tabs) from a string
1572 static void removeWhiteSpace(char *string
)
1574 char *outPtr
= string
;
1580 } else if (*string
!= ' ' && *string
!= '\t')
1581 *(outPtr
++) = *(string
++);
1588 ** Compares two strings and return TRUE if the two strings
1589 ** are the same, ignoring whitespace and case differences.
1591 static int stripCaseCmp(const char *str1
, const char *str2
)
1593 const char *c1
, *c2
;
1595 for (c1
=str1
, c2
=str2
; *c1
!='\0' && *c2
!='\0'; c1
++, c2
++) {
1596 while (*c1
== ' ' || *c1
== '\t')
1598 while (*c2
== ' ' || *c2
== '\t')
1600 if (toupper((unsigned char)*c1
) != toupper((unsigned char)*c2
))
1603 return *c1
== '\0' && *c2
== '\0';
1606 static void warnHandlerCB(String message
)
1608 if (strstr(message
, "XtRemoveGrab"))
1610 if (strstr(message
, "Attempt to remove non-existant passive grab"))
1612 fputs(message
, stderr
);
1613 fputc('\n', stderr
);
1616 static XModifierKeymap
*getKeyboardMapping(Display
*display
) {
1617 static XModifierKeymap
*keyboardMap
= NULL
;
1619 if (keyboardMap
== NULL
) {
1620 keyboardMap
= XGetModifierMapping(display
);
1622 return(keyboardMap
);
1626 ** get mask for a modifier
1630 static Modifiers
findModifierMapping(Display
*display
, KeyCode keyCode
) {
1633 XModifierKeymap
*modMap
= getKeyboardMapping(display
);
1635 if (modMap
== NULL
|| keyCode
== 0) {
1639 mapentry
= modMap
->modifiermap
;
1640 for (i
= 0; i
< 8; ++i
) {
1641 for (j
= 0; j
< (modMap
->max_keypermod
); ++j
) {
1642 if (keyCode
== *mapentry
) {
1643 return(1 << ((mapentry
- modMap
->modifiermap
) / modMap
->max_keypermod
));
1651 Modifiers
GetNumLockModMask(Display
*display
) {
1652 static int numLockMask
= -1;
1654 if (numLockMask
== -1) {
1655 numLockMask
= findModifierMapping(display
, XKeysymToKeycode(display
, XK_Num_Lock
));
1657 return(numLockMask
);
1661 ** Grab a key regardless of caps-lock and other silly latching keys.
1665 static void reallyGrabAKey(Widget dialog
, int keyCode
, Modifiers mask
) {
1666 Modifiers numLockMask
= GetNumLockModMask(XtDisplay(dialog
));
1668 if (keyCode
== 0) /* No anykey grabs, sorry */
1671 XtGrabKey(dialog
, keyCode
, mask
, True
, GrabModeAsync
, GrabModeAsync
);
1672 XtGrabKey(dialog
, keyCode
, mask
|LockMask
, True
, GrabModeAsync
, GrabModeAsync
);
1673 if (numLockMask
&& numLockMask
!= LockMask
) {
1674 XtGrabKey(dialog
, keyCode
, mask
|numLockMask
, True
, GrabModeAsync
, GrabModeAsync
);
1675 XtGrabKey(dialog
, keyCode
, mask
|LockMask
|numLockMask
, True
, GrabModeAsync
, GrabModeAsync
);
1680 ** Part of dialog mnemonic processing. Search the widget tree under w
1681 ** for widgets with mnemonics. When found, add a passive grab to the
1682 ** dialog widget for the mnemonic character, thus directing mnemonic
1683 ** events to the dialog widget.
1685 static void addMnemonicGrabs(Widget dialog
, Widget w
, int unmodifiedToo
)
1688 WidgetList children
;
1689 Cardinal numChildren
;
1691 KeySym mnemonic
= '\0';
1692 unsigned char rowColType
;
1693 unsigned int keyCode
;
1695 if (XtIsComposite(w
)) {
1696 if (XtClass(w
) == xmRowColumnWidgetClass
) {
1697 XtVaGetValues(w
, XmNrowColumnType
, &rowColType
, NULL
);
1698 isMenu
= rowColType
!= XmWORK_AREA
;
1702 XtVaGetValues(w
, XmNchildren
, &children
, XmNnumChildren
,
1703 &numChildren
, NULL
);
1704 for (i
=0; i
<(int)numChildren
; i
++)
1705 addMnemonicGrabs(dialog
, children
[i
], unmodifiedToo
);
1708 XtVaGetValues(w
, XmNmnemonic
, &mnemonic
, NULL
);
1709 if (mnemonic
!= XK_VoidSymbol
&& mnemonic
!= '\0') {
1710 mneString
[0] = mnemonic
; mneString
[1] = '\0';
1711 keyCode
= XKeysymToKeycode(XtDisplay(dialog
),
1712 XStringToKeysym(mneString
));
1713 reallyGrabAKey(dialog
, keyCode
, Mod1Mask
);
1715 reallyGrabAKey(dialog
, keyCode
, 0);
1721 ** Callback routine for dialog mnemonic processing.
1723 static void mnemonicCB(Widget w
, XtPointer callData
, XKeyEvent
*event
)
1725 findAndActivateMnemonic(w
, event
->keycode
);
1729 ** Look for a widget in the widget tree w, with a mnemonic matching
1730 ** keycode. When one is found, simulate a button press on that widget
1731 ** and give it the keyboard focus. If the mnemonic is on a label,
1732 ** look in the userData field of the label to see if it points to
1733 ** another widget, and give that the focus. This routine is just
1734 ** sufficient for NEdit, no doubt it will need to be extended for
1735 ** mnemonics on widgets other than just buttons and text fields.
1737 static void findAndActivateMnemonic(Widget w
, unsigned int keycode
)
1739 WidgetList children
;
1740 Cardinal numChildren
;
1742 KeySym mnemonic
= '\0';
1745 unsigned char rowColType
;
1747 if (XtIsComposite(w
)) {
1748 if (XtClass(w
) == xmRowColumnWidgetClass
) {
1749 XtVaGetValues(w
, XmNrowColumnType
, &rowColType
, NULL
);
1750 isMenu
= rowColType
!= XmWORK_AREA
;
1754 XtVaGetValues(w
, XmNchildren
, &children
, XmNnumChildren
,
1755 &numChildren
, NULL
);
1756 for (i
=0; i
<(int)numChildren
; i
++)
1757 findAndActivateMnemonic(children
[i
], keycode
);
1760 XtVaGetValues(w
, XmNmnemonic
, &mnemonic
, NULL
);
1761 if (mnemonic
!= '\0') {
1762 mneString
[0] = mnemonic
; mneString
[1] = '\0';
1763 if (XKeysymToKeycode(XtDisplay(XtParent(w
)),
1764 XStringToKeysym(mneString
)) == keycode
) {
1765 if (XtClass(w
) == xmLabelWidgetClass
||
1766 XtClass(w
) == xmLabelGadgetClass
) {
1767 XtVaGetValues(w
, XmNuserData
, &userData
, NULL
);
1768 if (userData
!=NULL
&& XtIsWidget(userData
) &&
1769 XmIsTraversable(userData
))
1770 XmProcessTraversal(userData
, XmTRAVERSE_CURRENT
);
1771 } else if (XmIsTraversable(w
)) {
1772 XmProcessTraversal(w
, XmTRAVERSE_CURRENT
);
1773 SimulateButtonPress(w
);
1781 ** Part of workaround for Motif Caps/Num Lock bug. Search the widget tree
1782 ** under w for widgets with accelerators. When found, add three passive
1783 ** grabs to topWidget, one for the accelerator keysym + modifiers + Caps
1784 ** Lock, one for Num Lock, and one for both, thus directing lock +
1785 ** accelerator events to topWidget.
1787 static void addAccelGrabs(Widget topWidget
, Widget w
)
1789 WidgetList children
;
1791 Cardinal numChildren
;
1794 if (XtIsComposite(w
)) {
1795 XtVaGetValues(w
, XmNchildren
, &children
, XmNnumChildren
,
1796 &numChildren
, NULL
);
1797 for (i
=0; i
<(int)numChildren
; i
++)
1798 addAccelGrabs(topWidget
, children
[i
]);
1799 } else if (XtClass(w
) == xmCascadeButtonWidgetClass
) {
1800 XtVaGetValues(w
, XmNsubMenuId
, &menu
, NULL
);
1802 addAccelGrabs(topWidget
, menu
);
1804 addAccelGrab(topWidget
, w
);
1808 ** Grabs the key + modifier defined in the widget's accelerator resource,
1809 ** in combination with the Caps Lock and Num Lock accelerators.
1811 static void addAccelGrab(Widget topWidget
, Widget w
)
1813 char *accelString
= NULL
;
1815 unsigned int modifiers
;
1817 Modifiers numLockMask
= GetNumLockModMask(XtDisplay(topWidget
));
1819 XtVaGetValues(w
, XmNaccelerator
, &accelString
, NULL
);
1820 if (accelString
== NULL
|| *accelString
== '\0') {
1821 if (accelString
!= NULL
) XtFree(accelString
);
1825 if (!parseAccelString(XtDisplay(topWidget
), accelString
, &keysym
, &modifiers
)) {
1826 XtFree(accelString
);
1829 XtFree(accelString
);
1831 /* Check to see if this server has this key mapped. Some cruddy PC X
1832 servers (Xoftware) have terrible default keymaps. If not,
1833 XKeysymToKeycode will return 0. However, it's bad news to pass
1834 that to XtGrabKey because 0 is really "AnyKey" which is definitely
1835 not what we want!! */
1837 code
= XKeysymToKeycode(XtDisplay(topWidget
), keysym
);
1841 XtGrabKey(topWidget
, code
,
1842 modifiers
| LockMask
, True
, GrabModeAsync
, GrabModeAsync
);
1843 if (numLockMask
&& numLockMask
!= LockMask
) {
1844 XtGrabKey(topWidget
, code
,
1845 modifiers
| numLockMask
, True
, GrabModeAsync
, GrabModeAsync
);
1846 XtGrabKey(topWidget
, code
,
1847 modifiers
| LockMask
| numLockMask
, True
, GrabModeAsync
, GrabModeAsync
);
1852 ** Read a Motif accelerator string and translate it into a keysym + modifiers.
1853 ** Returns TRUE if the parse was successful, FALSE, if not.
1855 static int parseAccelString(Display
*display
, const char *string
, KeySym
*keySym
,
1856 unsigned int *modifiers
)
1858 #define N_MODIFIERS 12
1859 /*... Is NumLock always Mod3? */
1860 static char *modifierNames
[N_MODIFIERS
] = {"Ctrl", "Shift", "Alt", "Mod2",
1861 "Mod3", "Mod4", "Mod5", "Button1", "Button2", "Button3", "Button4",
1863 static unsigned int modifierMasks
[N_MODIFIERS
] = {ControlMask
, ShiftMask
,
1864 Mod1Mask
, Mod2Mask
, Mod3Mask
, Mod4Mask
, Mod5Mask
, Button1Mask
, Button2Mask
,
1865 Button3Mask
, Button4Mask
, Button5Mask
};
1866 Modifiers numLockMask
= GetNumLockModMask(display
);
1867 char modStr
[MAX_ACCEL_LEN
];
1868 char evtStr
[MAX_ACCEL_LEN
];
1869 char keyStr
[MAX_ACCEL_LEN
];
1870 const char *c
, *evtStart
, *keyStart
;
1873 if (strlen(string
) >= MAX_ACCEL_LEN
)
1876 /* Get the modifier part */
1877 for (c
= string
; *c
!= '<'; c
++)
1880 strncpy(modStr
, string
, c
- string
);
1881 modStr
[c
- string
] = '\0';
1883 /* Verify the <key> or <keypress> part */
1885 for ( ; *c
!= '>'; c
++)
1889 strncpy(evtStr
, evtStart
, c
- evtStart
);
1890 evtStr
[c
- evtStart
] = '\0';
1891 if (!stripCaseCmp(evtStr
, "<key>") && !stripCaseCmp(evtStr
, "<keypress>"))
1894 /* Get the keysym part */
1896 for ( ; *c
!= '\0'; c
++);
1897 strncpy(keyStr
, keyStart
, c
- keyStart
);
1898 keyStr
[c
- keyStart
] = '\0';
1899 *keySym
= XStringToKeysym(keyStr
);
1901 /* Parse the modifier part */
1904 while (*c
!= '\0') {
1905 while (*c
== ' ' || *c
== '\t')
1909 for (i
= 0; i
< N_MODIFIERS
; i
++) {
1910 if (!strncmp(c
, modifierNames
[i
], strlen(modifierNames
[i
]))) {
1911 c
+= strlen(modifierNames
[i
]);
1912 if (modifierMasks
[i
] != numLockMask
) {
1913 *modifiers
|= modifierMasks
[i
];
1918 if (i
== N_MODIFIERS
)
1926 ** Event handler for patching around Motif's lock + accelerator problem.
1927 ** Looks for a menu item in the patched menu hierarchy and invokes its
1928 ** ArmAndActivate action.
1930 static void lockCB(Widget w
, XtPointer callData
, XEvent
*event
,
1931 Boolean
*continueDispatch
)
1933 Modifiers numLockMask
= GetNumLockModMask(XtDisplay(w
));
1934 Widget topMenuWidget
= (Widget
)callData
;
1935 *continueDispatch
= TRUE
;
1936 if (!(((XKeyEvent
*)event
)->state
& (LockMask
| numLockMask
)))
1939 if (!findAndActivateAccel(topMenuWidget
, ((XKeyEvent
*)event
)->keycode
,
1940 ((XKeyEvent
*)event
)->state
& ~(LockMask
| numLockMask
), event
))
1941 *continueDispatch
= FALSE
;
1945 ** Search through menu hierarchy under w and look for a button with
1946 ** accelerator matching keyCode + modifiers, and do its action
1948 static int findAndActivateAccel(Widget w
, unsigned int keyCode
,
1949 unsigned int modifiers
, XEvent
*event
)
1952 WidgetList children
;
1954 Cardinal numChildren
;
1956 char *accelString
= NULL
;
1960 if (XtIsComposite(w
)) {
1961 XtVaGetValues(w
, XmNchildren
, &children
, XmNnumChildren
,
1962 &numChildren
, NULL
);
1963 for (i
=0; i
<(int)numChildren
; i
++)
1964 if (findAndActivateAccel(children
[i
], keyCode
, modifiers
, event
))
1966 } else if (XtClass(w
) == xmCascadeButtonWidgetClass
) {
1967 XtVaGetValues(w
, XmNsubMenuId
, &menu
, NULL
);
1969 if (findAndActivateAccel(menu
, keyCode
, modifiers
, event
))
1972 XtVaGetValues(w
, XmNaccelerator
, &accelString
, NULL
);
1973 if (accelString
!= NULL
&& *accelString
!= '\0') {
1974 if (!parseAccelString(XtDisplay(w
), accelString
, &keysym
, &mods
))
1976 if (keyCode
== XKeysymToKeycode(XtDisplay(w
), keysym
) &&
1977 modifiers
== mods
) {
1978 if (XtIsSensitive(w
)) {
1979 XtCallActionProc(w
, "ArmAndActivate", event
, NULL
, 0);
1989 ** Global installation of mouse wheel actions for scrolled windows.
1991 void InstallMouseWheelActions(XtAppContext context
)
1993 static XtActionsRec Actions
[] = {
1994 {"scrolled-window-scroll-up", scrollUpAP
},
1995 {"scrolled-window-page-up", pageUpAP
},
1996 {"scrolled-window-scroll-down", scrollDownAP
},
1997 {"scrolled-window-page-down", pageDownAP
}
2000 XtAppAddActions(context
, Actions
, XtNumber(Actions
));
2004 ** Add mouse wheel support to a specific widget, which must be the scrollable
2005 ** widget of a ScrolledWindow.
2007 void AddMouseWheelSupport(Widget w
)
2009 if (XmIsScrolledWindow(XtParent(w
)))
2011 static char scrollTranslations
[] =
2012 "Shift<Btn4Down>,<Btn4Up>: scrolled-window-scroll-up(1)\n"
2013 "Shift<Btn5Down>,<Btn5Up>: scrolled-window-scroll-down(1)\n"
2014 "Ctrl<Btn4Down>,<Btn4Up>: scrolled-window-page-up()\n"
2015 "Ctrl<Btn5Down>,<Btn5Up>: scrolled-window-page-down()\n"
2016 "<Btn4Down>,<Btn4Up>: scrolled-window-scroll-up(3)\n"
2017 "<Btn5Down>,<Btn5Up>: scrolled-window-scroll-down(3)\n";
2018 static XtTranslations trans_table
= NULL
;
2020 if (trans_table
== NULL
)
2022 trans_table
= XtParseTranslationTable(scrollTranslations
);
2024 XtOverrideTranslations(w
, trans_table
);
2028 static void pageUpAP(Widget w
, XEvent
*event
, String
*args
, Cardinal
*nArgs
)
2030 Widget scrolledWindow
, scrollBar
;
2034 scrolledWindow
= XtParent(w
);
2035 scrollBar
= XtNameToWidget (scrolledWindow
, "VertScrollBar");
2037 XtCallActionProc(scrollBar
, "PageUpOrLeft", event
, al
, 1) ;
2041 static void pageDownAP(Widget w
, XEvent
*event
, String
*args
, Cardinal
*nArgs
)
2043 Widget scrolledWindow
, scrollBar
;
2047 scrolledWindow
= XtParent(w
);
2048 scrollBar
= XtNameToWidget (scrolledWindow
, "VertScrollBar");
2050 XtCallActionProc(scrollBar
, "PageDownOrRight", event
, al
, 1) ;
2054 static void scrollUpAP(Widget w
, XEvent
*event
, String
*args
, Cardinal
*nArgs
)
2056 Widget scrolledWindow
, scrollBar
;
2060 if (*nArgs
== 0 || sscanf(args
[0], "%d", &nLines
) != 1)
2063 scrolledWindow
= XtParent(w
);
2064 scrollBar
= XtNameToWidget (scrolledWindow
, "VertScrollBar");
2066 for (i
=0; i
<nLines
; i
++)
2067 XtCallActionProc(scrollBar
, "IncrementUpOrLeft", event
, al
, 1) ;
2071 static void scrollDownAP(Widget w
, XEvent
*event
, String
*args
, Cardinal
*nArgs
)
2073 Widget scrolledWindow
, scrollBar
;
2077 if (*nArgs
== 0 || sscanf(args
[0], "%d", &nLines
) != 1)
2080 scrolledWindow
= XtParent(w
);
2081 scrollBar
= XtNameToWidget (scrolledWindow
, "VertScrollBar");
2083 for (i
=0; i
<nLines
; i
++)
2084 XtCallActionProc(scrollBar
, "IncrementDownOrRight", event
, al
, 1) ;