1 static const char CVSID
[] = "$Id: userCmds.c,v 1.40 2004/01/27 18:02:32 tksoh Exp $";
2 /*******************************************************************************
4 * userCmds.c -- Nirvana Editor shell and macro command dialogs *
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"
37 #include "preferences.h"
43 #include "interpret.h"
45 #include "../util/DialogF.h"
46 #include "../util/misc.h"
47 #include "../util/managedList.h"
54 #include "../util/VMSparam.h"
57 #include <sys/param.h>
62 #include <X11/keysym.h>
66 #include <Xm/LabelG.h>
68 #include <Xm/ToggleB.h>
69 #include <Xm/SelectioB.h>
70 #include <Xm/RowColumn.h>
71 #include <Xm/CascadeB.h>
72 #include <Xm/MenuShell.h>
79 #define MENU_WIDGET(w) (XmGetPostedFromWidget(XtParent(w)))
81 #define MENU_WIDGET(w) (w)
84 /* max number of user programmable menu commands allowed per each of the
85 macro, shell, and bacground menus */
86 #define MAX_ITEMS_PER_MENU 400
88 /* major divisions (in position units) in User Commands dialogs */
89 #define LEFT_MARGIN_POS 1
90 #define RIGHT_MARGIN_POS 99
92 #define SHELL_CMD_TOP 70
93 #define MACRO_CMD_TOP 40
95 /* types of current dialog and/or menu */
96 enum dialogTypes
{SHELL_CMDS
, MACRO_CMDS
, BG_MENU_CMDS
};
98 /* Structure representing a menu item for both the shell and macro menus */
101 unsigned int modifiers
;
112 /* Structure for widgets and flags associated with both shell command
113 and macro command editing dialogs */
117 Widget nameTextW
, accTextW
, mneTextW
, cmdTextW
, saveFirstBtn
;
118 Widget loadAfterBtn
, selInpBtn
, winInpBtn
, eitherInpBtn
, noInpBtn
;
119 Widget repInpBtn
, sameOutBtn
, dlogOutBtn
, winOutBtn
, dlogShell
;
121 menuItemRec
**menuItemsList
;
125 /* Structure for keeping track of hierarchical sub-menus durring user-menu
132 /* Descriptions of the current user programmed menu items for re-generating
133 menus and processing shell, macro, and background menu selections */
134 static menuItemRec
*ShellMenuItems
[MAX_ITEMS_PER_MENU
];
135 static int NShellMenuItems
= 0;
136 static menuItemRec
*MacroMenuItems
[MAX_ITEMS_PER_MENU
];
137 static int NMacroMenuItems
= 0;
138 static menuItemRec
*BGMenuItems
[MAX_ITEMS_PER_MENU
];
139 static int NBGMenuItems
= 0;
141 /* Top level shells of the user-defined menu editing dialogs */
142 static Widget ShellCmdDialog
= NULL
;
143 static Widget MacroCmdDialog
= NULL
;
144 static Widget BGMenuCmdDialog
= NULL
;
146 /* Paste learn/replay sequence buttons in user defined menu editing dialogs
147 (for dimming and undimming externally when replay macro is available) */
148 static Widget MacroPasteReplayBtn
= NULL
;
149 static Widget BGMenuPasteReplayBtn
= NULL
;
151 static void editMacroOrBGMenu(WindowInfo
*window
, int dialogType
);
152 static void dimSelDepItemsInMenu(Widget menuPane
, menuItemRec
**menuList
,
153 int nMenuItems
, int sensitive
);
154 static void updateMenus(int menuType
);
155 static Widget
findInMenuTree(menuTreeItem
*menuTree
, int nTreeEntries
,
156 const char *hierName
);
157 static char *copySubstring(const char *string
, int length
);
158 static char *findStripLanguageMode(const char *menuItemName
, int languageMode
,
160 static Widget
createUserMenuItem(Widget menuPane
, char *name
, menuItemRec
*f
,
161 int index
, XtCallbackProc cbRtn
, XtPointer cbArg
);
162 static Widget
createUserSubMenu(Widget parent
, char *label
);
163 static void removeMenuItems(Widget menuPane
);
164 static void updateMenu(WindowInfo
*window
, int menuType
);
165 static void okCB(Widget w
, XtPointer clientData
, XtPointer callData
);
166 static void applyCB(Widget w
, XtPointer clientData
, XtPointer callData
);
167 static void checkCB(Widget w
, XtPointer clientData
, XtPointer callData
);
168 static int checkMacro(userCmdDialog
*ucd
);
169 static int checkMacroText(char *macro
, Widget errorParent
, Widget errFocus
);
170 static int applyDialogChanges(userCmdDialog
*ucd
);
171 static void dismissCB(Widget w
, XtPointer clientData
, XtPointer callData
);
172 static void pasteReplayCB(Widget w
, XtPointer clientData
, XtPointer callData
);
173 static void destroyCB(Widget w
, XtPointer clientData
, XtPointer callData
);
174 static void accKeyCB(Widget w
, XtPointer clientData
, XKeyEvent
*event
);
175 static void sameOutCB(Widget w
, XtPointer clientData
, XtPointer callData
);
176 static void shellMenuCB(Widget w
, WindowInfo
*window
, XtPointer callData
);
177 static void macroMenuCB(Widget w
, WindowInfo
*window
, XtPointer callData
);
178 static void bgMenuCB(Widget w
, WindowInfo
*window
, XtPointer callData
) ;
179 static void accFocusCB(Widget w
, XtPointer clientData
, XtPointer callData
);
180 static void accLoseFocusCB(Widget w
, XtPointer clientData
,
182 static void updateDialogFields(menuItemRec
*f
, userCmdDialog
*ucd
);
183 static menuItemRec
*readDialogFields(userCmdDialog
*ucd
, int silent
);
184 static menuItemRec
*copyMenuItemRec(menuItemRec
*item
);
185 static void freeMenuItemRec(menuItemRec
*item
);
186 static void *getDialogDataCB(void *oldItem
, int explicitRequest
, int *abort
,
188 static void setDialogDataCB(void *item
, void *cbArg
);
189 static void freeItemCB(void *item
);
190 static int dialogFieldsAreEmpty(userCmdDialog
*ucd
);
191 static void disableTextW(Widget textW
);
192 static char *writeMenuItemString(menuItemRec
**menuItems
, int nItems
,
194 static int loadMenuItemString(char *inString
, menuItemRec
**menuItems
,
195 int *nItems
, int listType
);
196 static void generateAcceleratorString(char *text
, unsigned int modifiers
,
198 static void genAccelEventName(char *text
, unsigned int modifiers
,
200 static int parseAcceleratorString(const char *string
, unsigned int *modifiers
,
202 static int parseError(const char *message
);
203 static char *copyMacroToEnd(char **inPtr
);
204 static void addTerminatingNewline(char **string
);
207 ** Present a dialog for editing the user specified commands in the shell menu
209 void EditShellMenu(WindowInfo
*window
)
211 Widget form
, accLabel
, inpLabel
, inpBox
, outBox
, outLabel
;
212 Widget nameLabel
, cmdLabel
, okBtn
, applyBtn
, dismissBtn
;
218 /* if the dialog is already displayed, just pop it to the top and return */
219 if (ShellCmdDialog
!= NULL
) {
220 RaiseShellWindow(ShellCmdDialog
);
224 /* Create a structure for keeping track of dialog state */
225 ucd
= (userCmdDialog
*)XtMalloc(sizeof(userCmdDialog
));
226 ucd
->window
= window
;
228 /* Set the dialog to operate on the Shell menu */
229 ucd
->menuItemsList
= (menuItemRec
**)XtMalloc(sizeof(menuItemRec
*) *
231 for (i
=0; i
<NShellMenuItems
; i
++)
232 ucd
->menuItemsList
[i
] = copyMenuItemRec(ShellMenuItems
[i
]);
233 ucd
->nMenuItems
= NShellMenuItems
;
234 ucd
->dialogType
= SHELL_CMDS
;
237 XtSetArg(args
[ac
], XmNdeleteResponse
, XmDO_NOTHING
); ac
++;
238 XtSetArg(args
[ac
], XmNiconName
, "NEdit Shell Menu"); ac
++;
239 XtSetArg(args
[ac
], XmNtitle
, "Shell Menu"); ac
++;
240 ucd
->dlogShell
= CreateWidget(TheAppShell
, "shellCommands",
241 topLevelShellWidgetClass
, args
, ac
);
242 AddSmallIcon(ucd
->dlogShell
);
243 form
= XtVaCreateManagedWidget("editShellCommands", xmFormWidgetClass
,
244 ucd
->dlogShell
, XmNautoUnmanage
, False
,
245 XmNresizePolicy
, XmRESIZE_NONE
, NULL
);
246 ShellCmdDialog
= ucd
->dlogShell
;
247 XtAddCallback(form
, XmNdestroyCallback
, destroyCB
, ucd
);
248 AddMotifCloseCallback(ucd
->dlogShell
, dismissCB
, ucd
);
251 XtSetArg(args
[ac
], XmNtopAttachment
, XmATTACH_POSITION
); ac
++;
252 XtSetArg(args
[ac
], XmNtopPosition
, 2); ac
++;
253 XtSetArg(args
[ac
], XmNleftAttachment
, XmATTACH_POSITION
); ac
++;
254 XtSetArg(args
[ac
], XmNleftPosition
, LEFT_MARGIN_POS
); ac
++;
255 XtSetArg(args
[ac
], XmNrightAttachment
, XmATTACH_POSITION
); ac
++;
256 XtSetArg(args
[ac
], XmNrightPosition
, LIST_RIGHT
-1); ac
++;
257 XtSetArg(args
[ac
], XmNbottomAttachment
, XmATTACH_POSITION
); ac
++;
258 XtSetArg(args
[ac
], XmNbottomPosition
, SHELL_CMD_TOP
); ac
++;
259 ucd
->managedList
= CreateManagedList(form
, "list", args
, ac
,
260 (void **)ucd
->menuItemsList
, &ucd
->nMenuItems
, MAX_ITEMS_PER_MENU
,
261 20, getDialogDataCB
, ucd
, setDialogDataCB
, ucd
, freeItemCB
);
263 ucd
->loadAfterBtn
= XtVaCreateManagedWidget("loadAfterBtn",
264 xmToggleButtonWidgetClass
, form
,
265 XmNlabelString
, s1
=MKSTRING("Re-load file after executing command"),
267 XmNalignment
, XmALIGNMENT_BEGINNING
,
269 XmNleftAttachment
, XmATTACH_POSITION
,
270 XmNleftPosition
, LIST_RIGHT
,
271 XmNrightAttachment
, XmATTACH_POSITION
,
272 XmNrightPosition
, RIGHT_MARGIN_POS
,
273 XmNbottomAttachment
, XmATTACH_POSITION
,
274 XmNbottomPosition
, SHELL_CMD_TOP
, NULL
);
276 ucd
->saveFirstBtn
= XtVaCreateManagedWidget("saveFirstBtn",
277 xmToggleButtonWidgetClass
, form
,
278 XmNlabelString
, s1
=MKSTRING("Save file before executing command"),
280 XmNalignment
, XmALIGNMENT_BEGINNING
,
282 XmNleftAttachment
, XmATTACH_POSITION
,
283 XmNleftPosition
, LIST_RIGHT
,
284 XmNrightAttachment
, XmATTACH_POSITION
,
285 XmNrightPosition
, RIGHT_MARGIN_POS
,
286 XmNbottomAttachment
, XmATTACH_WIDGET
,
287 XmNbottomWidget
, ucd
->loadAfterBtn
, NULL
);
289 ucd
->repInpBtn
= XtVaCreateManagedWidget("repInpBtn",
290 xmToggleButtonWidgetClass
, form
,
291 XmNlabelString
, s1
=MKSTRING("Output replaces input"),
293 XmNalignment
, XmALIGNMENT_BEGINNING
,
295 XmNleftAttachment
, XmATTACH_POSITION
,
296 XmNleftPosition
, LIST_RIGHT
,
297 XmNrightAttachment
, XmATTACH_POSITION
,
298 XmNrightPosition
, RIGHT_MARGIN_POS
,
299 XmNbottomAttachment
, XmATTACH_WIDGET
,
300 XmNbottomWidget
, ucd
->saveFirstBtn
, NULL
);
302 outBox
= XtVaCreateManagedWidget("outBox", xmRowColumnWidgetClass
, form
,
303 XmNpacking
, XmPACK_TIGHT
,
304 XmNorientation
, XmHORIZONTAL
,
305 XmNradioBehavior
, True
,
306 XmNradioAlwaysOne
, True
,
307 XmNleftAttachment
, XmATTACH_POSITION
,
308 XmNleftPosition
, LIST_RIGHT
+ 2,
309 XmNrightAttachment
, XmATTACH_POSITION
,
310 XmNrightPosition
, RIGHT_MARGIN_POS
,
311 XmNbottomAttachment
, XmATTACH_WIDGET
,
312 XmNbottomWidget
, ucd
->repInpBtn
,
313 XmNbottomOffset
, 4, NULL
);
314 ucd
->sameOutBtn
= XtVaCreateManagedWidget("sameOutBtn",
315 xmToggleButtonWidgetClass
, outBox
,
316 XmNlabelString
, s1
=MKSTRING("same window"),
318 XmNalignment
, XmALIGNMENT_BEGINNING
,
322 XtAddCallback(ucd
->sameOutBtn
, XmNvalueChangedCallback
, sameOutCB
, ucd
);
323 ucd
->dlogOutBtn
= XtVaCreateManagedWidget("dlogOutBtn",
324 xmToggleButtonWidgetClass
, outBox
,
325 XmNlabelString
, s1
=MKSTRING("dialog"),
327 XmNalignment
, XmALIGNMENT_BEGINNING
,
329 XmNset
, False
, NULL
);
331 ucd
->winOutBtn
= XtVaCreateManagedWidget("winOutBtn", xmToggleButtonWidgetClass
,
333 XmNlabelString
, s1
=MKSTRING("new window"),
335 XmNalignment
, XmALIGNMENT_BEGINNING
,
337 XmNset
, False
, NULL
);
339 outLabel
= XtVaCreateManagedWidget("outLabel", xmLabelGadgetClass
, form
,
340 XmNlabelString
, s1
=MKSTRING("Command Output:"),
341 XmNalignment
, XmALIGNMENT_BEGINNING
,
343 XmNleftAttachment
, XmATTACH_POSITION
,
344 XmNleftPosition
, LIST_RIGHT
,
345 XmNrightAttachment
, XmATTACH_POSITION
,
346 XmNrightPosition
, RIGHT_MARGIN_POS
,
347 XmNbottomAttachment
, XmATTACH_WIDGET
,
348 XmNbottomWidget
, outBox
, NULL
);
351 inpBox
= XtVaCreateManagedWidget("inpBox", xmRowColumnWidgetClass
, form
,
352 XmNpacking
, XmPACK_TIGHT
,
353 XmNorientation
, XmHORIZONTAL
,
354 XmNradioBehavior
, True
,
355 XmNradioAlwaysOne
, True
,
356 XmNleftAttachment
, XmATTACH_POSITION
,
357 XmNleftPosition
, LIST_RIGHT
+ 2,
358 XmNrightAttachment
, XmATTACH_POSITION
,
359 XmNrightPosition
, RIGHT_MARGIN_POS
,
360 XmNbottomAttachment
, XmATTACH_WIDGET
,
361 XmNbottomWidget
, outLabel
, NULL
);
362 ucd
->selInpBtn
= XtVaCreateManagedWidget("selInpBtn", xmToggleButtonWidgetClass
,
364 XmNlabelString
, s1
=MKSTRING("selection"),
366 XmNalignment
, XmALIGNMENT_BEGINNING
,
370 ucd
->winInpBtn
= XtVaCreateManagedWidget("winInpBtn",
371 xmToggleButtonWidgetClass
, inpBox
,
372 XmNlabelString
, s1
=MKSTRING("window"),
374 XmNalignment
, XmALIGNMENT_BEGINNING
,
376 XmNset
, False
, NULL
);
378 ucd
->eitherInpBtn
= XtVaCreateManagedWidget("eitherInpBtn",
379 xmToggleButtonWidgetClass
, inpBox
,
380 XmNlabelString
, s1
=MKSTRING("either"),
382 XmNalignment
, XmALIGNMENT_BEGINNING
,
384 XmNset
, False
, NULL
);
386 ucd
->noInpBtn
= XtVaCreateManagedWidget("noInpBtn",
387 xmToggleButtonWidgetClass
, inpBox
,
388 XmNlabelString
, s1
=MKSTRING("none"),
390 XmNalignment
, XmALIGNMENT_BEGINNING
,
392 XmNset
, False
, NULL
);
394 inpLabel
= XtVaCreateManagedWidget("inpLabel", xmLabelGadgetClass
, form
,
395 XmNlabelString
, s1
=MKSTRING("Command Input:"),
396 XmNalignment
, XmALIGNMENT_BEGINNING
,
398 XmNleftAttachment
, XmATTACH_POSITION
,
399 XmNleftPosition
, LIST_RIGHT
,
400 XmNrightAttachment
, XmATTACH_POSITION
,
401 XmNrightPosition
, RIGHT_MARGIN_POS
,
402 XmNbottomAttachment
, XmATTACH_WIDGET
,
403 XmNbottomWidget
, inpBox
, NULL
);
406 ucd
->mneTextW
= XtVaCreateManagedWidget("mne", xmTextWidgetClass
, form
,
409 XmNleftAttachment
, XmATTACH_POSITION
,
410 XmNleftPosition
, RIGHT_MARGIN_POS
-10,
411 XmNrightAttachment
, XmATTACH_POSITION
,
412 XmNrightPosition
, RIGHT_MARGIN_POS
,
413 XmNbottomAttachment
, XmATTACH_WIDGET
,
414 XmNbottomWidget
, inpLabel
, NULL
);
415 RemapDeleteKey(ucd
->mneTextW
);
417 ucd
->accTextW
= XtVaCreateManagedWidget("acc", xmTextWidgetClass
, form
,
419 XmNmaxLength
, MAX_ACCEL_LEN
-1,
420 XmNcursorPositionVisible
, False
,
421 XmNleftAttachment
, XmATTACH_POSITION
,
422 XmNleftPosition
, LIST_RIGHT
,
423 XmNrightAttachment
, XmATTACH_POSITION
,
424 XmNrightPosition
, RIGHT_MARGIN_POS
-15,
425 XmNbottomAttachment
, XmATTACH_WIDGET
,
426 XmNbottomWidget
, inpLabel
, NULL
);
427 XtAddEventHandler(ucd
->accTextW
, KeyPressMask
, False
,
428 (XtEventHandler
)accKeyCB
, ucd
);
429 XtAddCallback(ucd
->accTextW
, XmNfocusCallback
, accFocusCB
, ucd
);
430 XtAddCallback(ucd
->accTextW
, XmNlosingFocusCallback
, accLoseFocusCB
, ucd
);
431 accLabel
= XtVaCreateManagedWidget("accLabel", xmLabelGadgetClass
, form
,
432 XmNlabelString
, s1
=MKSTRING("Accelerator"),
434 XmNuserData
, ucd
->accTextW
,
435 XmNalignment
, XmALIGNMENT_BEGINNING
,
437 XmNleftAttachment
, XmATTACH_POSITION
,
438 XmNleftPosition
, LIST_RIGHT
,
439 XmNrightAttachment
, XmATTACH_POSITION
,
440 XmNrightPosition
, LIST_RIGHT
+ 24,
441 XmNbottomAttachment
, XmATTACH_WIDGET
,
442 XmNbottomWidget
, ucd
->mneTextW
, NULL
);
445 XtVaCreateManagedWidget("mneLabel", xmLabelGadgetClass
, form
,
446 XmNlabelString
, s1
=MKSTRING("Mnemonic"),
448 XmNuserData
, ucd
->mneTextW
,
449 XmNalignment
, XmALIGNMENT_END
,
451 XmNleftAttachment
, XmATTACH_POSITION
,
452 XmNleftPosition
, LIST_RIGHT
+ 24,
453 XmNrightAttachment
, XmATTACH_POSITION
,
454 XmNrightPosition
, RIGHT_MARGIN_POS
,
455 XmNbottomAttachment
, XmATTACH_WIDGET
,
456 XmNbottomWidget
, ucd
->mneTextW
, NULL
);
459 ucd
->nameTextW
= XtVaCreateManagedWidget("name", xmTextWidgetClass
, form
,
460 XmNleftAttachment
, XmATTACH_POSITION
,
461 XmNleftPosition
, LIST_RIGHT
,
462 XmNrightAttachment
, XmATTACH_POSITION
,
463 XmNrightPosition
, RIGHT_MARGIN_POS
,
464 XmNbottomAttachment
, XmATTACH_WIDGET
,
465 XmNbottomWidget
, accLabel
, NULL
);
466 RemapDeleteKey(ucd
->nameTextW
);
468 nameLabel
= XtVaCreateManagedWidget("nameLabel", xmLabelGadgetClass
, form
,
469 XmNlabelString
, s1
=MKSTRING("Menu Entry"),
471 XmNuserData
, ucd
->nameTextW
,
472 XmNalignment
, XmALIGNMENT_BEGINNING
,
474 XmNleftAttachment
, XmATTACH_POSITION
,
475 XmNleftPosition
, LIST_RIGHT
,
476 XmNbottomAttachment
, XmATTACH_WIDGET
,
477 XmNbottomWidget
, ucd
->nameTextW
, NULL
);
480 XtVaCreateManagedWidget("nameNotes", xmLabelGadgetClass
, form
,
481 XmNlabelString
, s1
=MKSTRING("(> for sub-menu, @ language mode)"),
482 XmNalignment
, XmALIGNMENT_END
,
484 XmNleftAttachment
, XmATTACH_WIDGET
,
485 XmNleftWidget
, nameLabel
,
486 XmNrightAttachment
, XmATTACH_POSITION
,
487 XmNrightPosition
, RIGHT_MARGIN_POS
,
488 XmNbottomAttachment
, XmATTACH_WIDGET
,
489 XmNbottomWidget
, ucd
->nameTextW
, NULL
);
492 XtVaCreateManagedWidget("topLabel", xmLabelGadgetClass
, form
,
493 XmNlabelString
, s1
=MKSTRING(
494 "Select a shell menu item from the list at left.\n\
495 Select \"New\" to add a new command to the menu."),
496 XmNtopAttachment
, XmATTACH_POSITION
,
498 XmNleftAttachment
, XmATTACH_POSITION
,
499 XmNleftPosition
, LIST_RIGHT
,
500 XmNrightAttachment
, XmATTACH_POSITION
,
501 XmNrightPosition
, RIGHT_MARGIN_POS
,
502 XmNbottomAttachment
, XmATTACH_WIDGET
,
503 XmNbottomWidget
, nameLabel
, NULL
);
506 cmdLabel
= XtVaCreateManagedWidget("cmdLabel", xmLabelGadgetClass
, form
,
507 XmNlabelString
, s1
=MKSTRING("Shell Command to Execute"),
509 XmNalignment
, XmALIGNMENT_BEGINNING
,
511 XmNtopAttachment
, XmATTACH_POSITION
,
512 XmNtopPosition
, SHELL_CMD_TOP
,
513 XmNleftAttachment
, XmATTACH_POSITION
,
514 XmNleftPosition
, LEFT_MARGIN_POS
, NULL
);
516 XtVaCreateManagedWidget("cmdLabel", xmLabelGadgetClass
, form
,
517 XmNlabelString
, s1
=MKSTRING("(% expands to current filename, # to line number)"),
518 XmNalignment
, XmALIGNMENT_END
,
520 XmNtopAttachment
, XmATTACH_POSITION
,
521 XmNtopPosition
, SHELL_CMD_TOP
,
522 XmNleftAttachment
, XmATTACH_WIDGET
,
523 XmNleftWidget
, cmdLabel
,
524 XmNrightAttachment
, XmATTACH_POSITION
,
525 XmNrightPosition
, RIGHT_MARGIN_POS
, NULL
);
528 okBtn
= XtVaCreateManagedWidget("ok",xmPushButtonWidgetClass
,form
,
529 XmNlabelString
, s1
=MKSTRING("OK"),
530 XmNleftAttachment
, XmATTACH_POSITION
,
532 XmNrightAttachment
, XmATTACH_POSITION
,
533 XmNrightPosition
, 29,
534 XmNbottomAttachment
, XmATTACH_POSITION
,
535 XmNbottomPosition
, 99, NULL
);
536 XtAddCallback(okBtn
, XmNactivateCallback
, okCB
, ucd
);
539 applyBtn
= XtVaCreateManagedWidget("apply",xmPushButtonWidgetClass
,form
,
540 XmNlabelString
, s1
=MKSTRING("Apply"),
542 XmNleftAttachment
, XmATTACH_POSITION
,
544 XmNrightAttachment
, XmATTACH_POSITION
,
545 XmNrightPosition
, 58,
546 XmNbottomAttachment
, XmATTACH_POSITION
,
547 XmNbottomPosition
, 99, NULL
);
548 XtAddCallback(applyBtn
, XmNactivateCallback
, applyCB
, ucd
);
551 dismissBtn
= XtVaCreateManagedWidget("dismiss",xmPushButtonWidgetClass
,form
,
552 XmNlabelString
, s1
=MKSTRING("Dismiss"),
553 XmNleftAttachment
, XmATTACH_POSITION
,
555 XmNrightAttachment
, XmATTACH_POSITION
,
556 XmNrightPosition
, 87,
557 XmNbottomAttachment
, XmATTACH_POSITION
,
558 XmNbottomPosition
, 99, NULL
);
559 XtAddCallback(dismissBtn
, XmNactivateCallback
, dismissCB
, ucd
);
563 XtSetArg(args
[ac
], XmNeditMode
, XmMULTI_LINE_EDIT
); ac
++;
564 XtSetArg(args
[ac
], XmNscrollHorizontal
, False
); ac
++;
565 XtSetArg(args
[ac
], XmNwordWrap
, True
); ac
++;
566 XtSetArg(args
[ac
], XmNtopAttachment
, XmATTACH_WIDGET
); ac
++;
567 XtSetArg(args
[ac
], XmNtopWidget
, cmdLabel
); ac
++;
568 XtSetArg(args
[ac
], XmNleftAttachment
, XmATTACH_POSITION
); ac
++;
569 XtSetArg(args
[ac
], XmNleftPosition
, LEFT_MARGIN_POS
); ac
++;
570 XtSetArg(args
[ac
], XmNrightAttachment
, XmATTACH_POSITION
); ac
++;
571 XtSetArg(args
[ac
], XmNrightPosition
, RIGHT_MARGIN_POS
); ac
++;
572 XtSetArg(args
[ac
], XmNbottomAttachment
, XmATTACH_WIDGET
); ac
++;
573 XtSetArg(args
[ac
], XmNbottomWidget
, okBtn
); ac
++;
574 XtSetArg(args
[ac
], XmNbottomOffset
, 5); ac
++;
575 ucd
->cmdTextW
= XmCreateScrolledText(form
, "name", args
, ac
);
576 AddMouseWheelSupport(ucd
->cmdTextW
);
577 XtManageChild(ucd
->cmdTextW
);
578 MakeSingleLineTextW(ucd
->cmdTextW
);
579 RemapDeleteKey(ucd
->cmdTextW
);
580 XtVaSetValues(cmdLabel
, XmNuserData
, ucd
->cmdTextW
, NULL
); /* for mnemonic */
582 /* Disable text input for the accelerator key widget, let the
583 event handler manage it instead */
584 disableTextW(ucd
->accTextW
);
586 /* initializs the dialog fields to match "New" list item */
587 updateDialogFields(NULL
, ucd
);
589 /* Set initial default button */
590 XtVaSetValues(form
, XmNdefaultButton
, okBtn
, NULL
);
591 XtVaSetValues(form
, XmNcancelButton
, dismissBtn
, NULL
);
593 /* Handle mnemonic selection of buttons and focus to dialog */
594 AddDialogMnemonicHandler(form
, FALSE
);
596 /* realize all of the widgets in the new window */
597 RealizeWithoutForcingPosition(ucd
->dlogShell
);
601 ** Present a dialogs for editing the user specified commands in the Macro
602 ** and background menus
604 void EditMacroMenu(WindowInfo
*window
)
606 editMacroOrBGMenu(window
, MACRO_CMDS
);
608 void EditBGMenu(WindowInfo
*window
)
610 editMacroOrBGMenu(window
, BG_MENU_CMDS
);
613 static void editMacroOrBGMenu(WindowInfo
*window
, int dialogType
)
615 Widget form
, accLabel
, pasteReplayBtn
;
616 Widget nameLabel
, cmdLabel
, okBtn
, applyBtn
, dismissBtn
;
623 /* if the dialog is already displayed, just pop it to the top and return */
624 if (dialogType
== MACRO_CMDS
&& MacroCmdDialog
!= NULL
) {
625 RaiseShellWindow(MacroCmdDialog
);
628 if (dialogType
== BG_MENU_CMDS
&& BGMenuCmdDialog
!= NULL
) {
629 RaiseShellWindow(BGMenuCmdDialog
);
633 /* Create a structure for keeping track of dialog state */
634 ucd
= (userCmdDialog
*)XtMalloc(sizeof(userCmdDialog
));
635 ucd
->window
= window
;
637 /* Set the dialog to operate on the Macro menu */
638 ucd
->menuItemsList
= (menuItemRec
**)XtMalloc(sizeof(menuItemRec
**) *
640 if (dialogType
== MACRO_CMDS
) {
641 for (i
=0; i
<NMacroMenuItems
; i
++)
642 ucd
->menuItemsList
[i
] = copyMenuItemRec(MacroMenuItems
[i
]);
643 ucd
->nMenuItems
= NMacroMenuItems
;
644 } else { /* BG_MENU_CMDS */
645 for (i
=0; i
<NBGMenuItems
; i
++)
646 ucd
->menuItemsList
[i
] = copyMenuItemRec(BGMenuItems
[i
]);
647 ucd
->nMenuItems
= NBGMenuItems
;
649 ucd
->dialogType
= dialogType
;
651 title
= dialogType
== MACRO_CMDS
? "Macro Commands" :
652 "Window Background Menu";
654 XtSetArg(args
[ac
], XmNdeleteResponse
, XmDO_NOTHING
); ac
++;
655 XtSetArg(args
[ac
], XmNiconName
, title
); ac
++;
656 XtSetArg(args
[ac
], XmNtitle
, title
); ac
++;
657 ucd
->dlogShell
= CreateWidget(TheAppShell
, "macros",
658 topLevelShellWidgetClass
, args
, ac
);
659 AddSmallIcon(ucd
->dlogShell
);
660 form
= XtVaCreateManagedWidget("editMacroCommands", xmFormWidgetClass
,
661 ucd
->dlogShell
, XmNautoUnmanage
, False
,
662 XmNresizePolicy
, XmRESIZE_NONE
, NULL
);
663 XtAddCallback(form
, XmNdestroyCallback
, destroyCB
, ucd
);
664 AddMotifCloseCallback(ucd
->dlogShell
, dismissCB
, ucd
);
667 XtSetArg(args
[ac
], XmNtopAttachment
, XmATTACH_POSITION
); ac
++;
668 XtSetArg(args
[ac
], XmNtopPosition
, 2); ac
++;
669 XtSetArg(args
[ac
], XmNleftAttachment
, XmATTACH_POSITION
); ac
++;
670 XtSetArg(args
[ac
], XmNleftPosition
, LEFT_MARGIN_POS
); ac
++;
671 XtSetArg(args
[ac
], XmNrightAttachment
, XmATTACH_POSITION
); ac
++;
672 XtSetArg(args
[ac
], XmNrightPosition
, LIST_RIGHT
-1); ac
++;
673 XtSetArg(args
[ac
], XmNbottomAttachment
, XmATTACH_POSITION
); ac
++;
674 XtSetArg(args
[ac
], XmNbottomPosition
, MACRO_CMD_TOP
); ac
++;
675 ucd
->managedList
= CreateManagedList(form
, "list", args
, ac
,
676 (void **)ucd
->menuItemsList
, &ucd
->nMenuItems
, MAX_ITEMS_PER_MENU
, 20,
677 getDialogDataCB
, ucd
, setDialogDataCB
, ucd
, freeItemCB
);
679 ucd
->selInpBtn
= XtVaCreateManagedWidget("selInpBtn",
680 xmToggleButtonWidgetClass
, form
,
681 XmNlabelString
, s1
=MKSTRING("Requires Selection"),
683 XmNalignment
, XmALIGNMENT_BEGINNING
,
686 XmNleftAttachment
, XmATTACH_POSITION
,
687 XmNleftPosition
, LIST_RIGHT
,
688 XmNbottomAttachment
, XmATTACH_POSITION
,
689 XmNbottomPosition
, MACRO_CMD_TOP
, NULL
);
692 ucd
->mneTextW
= XtVaCreateManagedWidget("mne", xmTextWidgetClass
, form
,
695 XmNleftAttachment
, XmATTACH_POSITION
,
696 XmNleftPosition
, RIGHT_MARGIN_POS
-21-5,
697 XmNrightAttachment
, XmATTACH_POSITION
,
698 XmNrightPosition
, RIGHT_MARGIN_POS
-21,
699 XmNbottomAttachment
, XmATTACH_WIDGET
,
700 XmNbottomWidget
, ucd
->selInpBtn
,
701 XmNbottomOffset
, 5, NULL
);
702 RemapDeleteKey(ucd
->mneTextW
);
704 ucd
->accTextW
= XtVaCreateManagedWidget("acc", xmTextWidgetClass
, form
,
706 XmNmaxLength
, MAX_ACCEL_LEN
-1,
707 XmNcursorPositionVisible
, False
,
708 XmNleftAttachment
, XmATTACH_POSITION
,
709 XmNleftPosition
, LIST_RIGHT
,
710 XmNrightAttachment
, XmATTACH_POSITION
,
711 XmNrightPosition
, RIGHT_MARGIN_POS
-20-10,
712 XmNbottomAttachment
, XmATTACH_WIDGET
,
713 XmNbottomWidget
, ucd
->selInpBtn
,
714 XmNbottomOffset
, 5, NULL
);
715 XtAddEventHandler(ucd
->accTextW
, KeyPressMask
, False
,
716 (XtEventHandler
)accKeyCB
, ucd
);
717 XtAddCallback(ucd
->accTextW
, XmNfocusCallback
, accFocusCB
, ucd
);
718 XtAddCallback(ucd
->accTextW
, XmNlosingFocusCallback
, accLoseFocusCB
, ucd
);
720 accLabel
= XtVaCreateManagedWidget("accLabel", xmLabelGadgetClass
, form
,
721 XmNlabelString
, s1
=MKSTRING("Accelerator"),
723 XmNuserData
, ucd
->accTextW
,
724 XmNalignment
, XmALIGNMENT_BEGINNING
,
726 XmNleftAttachment
, XmATTACH_POSITION
,
727 XmNleftPosition
, LIST_RIGHT
,
728 XmNrightAttachment
, XmATTACH_POSITION
,
729 XmNrightPosition
, LIST_RIGHT
+ 22,
730 XmNbottomAttachment
, XmATTACH_WIDGET
,
731 XmNbottomWidget
, ucd
->mneTextW
, NULL
);
734 XtVaCreateManagedWidget("mneLabel", xmLabelGadgetClass
, form
,
735 XmNlabelString
, s1
=MKSTRING("Mnemonic"),
737 XmNuserData
, ucd
->mneTextW
,
738 XmNalignment
, XmALIGNMENT_END
,
740 XmNleftAttachment
, XmATTACH_POSITION
,
741 XmNleftPosition
, LIST_RIGHT
+ 22,
742 XmNrightAttachment
, XmATTACH_POSITION
,
743 XmNrightPosition
, RIGHT_MARGIN_POS
-21,
744 XmNbottomAttachment
, XmATTACH_WIDGET
,
745 XmNbottomWidget
, ucd
->mneTextW
, NULL
);
748 pasteReplayBtn
= XtVaCreateManagedWidget("pasteReplay",
749 xmPushButtonWidgetClass
, form
,
750 XmNlabelString
, s1
=MKSTRING("Paste Learn/\nReplay Macro"),
752 XmNsensitive
, GetReplayMacro() != NULL
,
753 XmNleftAttachment
, XmATTACH_POSITION
,
754 XmNleftPosition
, RIGHT_MARGIN_POS
-20,
755 XmNrightAttachment
, XmATTACH_POSITION
,
756 XmNrightPosition
, RIGHT_MARGIN_POS
,
757 XmNbottomAttachment
, XmATTACH_POSITION
,
758 XmNbottomPosition
, MACRO_CMD_TOP
, NULL
);
759 XtAddCallback(pasteReplayBtn
, XmNactivateCallback
,
763 ucd
->nameTextW
= XtVaCreateManagedWidget("name", xmTextWidgetClass
, form
,
764 XmNleftAttachment
, XmATTACH_POSITION
,
765 XmNleftPosition
, LIST_RIGHT
,
766 XmNrightAttachment
, XmATTACH_POSITION
,
767 XmNrightPosition
, RIGHT_MARGIN_POS
,
768 XmNbottomAttachment
, XmATTACH_WIDGET
,
769 XmNbottomWidget
, accLabel
, NULL
);
770 RemapDeleteKey(ucd
->nameTextW
);
772 nameLabel
= XtVaCreateManagedWidget("nameLabel", xmLabelGadgetClass
, form
,
773 XmNlabelString
, s1
=MKSTRING("Menu Entry"),
775 XmNuserData
, ucd
->nameTextW
,
776 XmNalignment
, XmALIGNMENT_BEGINNING
,
778 XmNleftAttachment
, XmATTACH_POSITION
,
779 XmNleftPosition
, LIST_RIGHT
,
780 XmNbottomAttachment
, XmATTACH_WIDGET
,
781 XmNbottomWidget
, ucd
->nameTextW
, NULL
);
784 XtVaCreateManagedWidget("nameNotes", xmLabelGadgetClass
, form
,
785 XmNlabelString
, s1
=MKSTRING("(> for sub-menu, @ language mode)"),
786 XmNalignment
, XmALIGNMENT_END
,
788 XmNleftAttachment
, XmATTACH_WIDGET
,
789 XmNleftWidget
, nameLabel
,
790 XmNrightAttachment
, XmATTACH_POSITION
,
791 XmNrightPosition
, RIGHT_MARGIN_POS
,
792 XmNbottomAttachment
, XmATTACH_WIDGET
,
793 XmNbottomWidget
, ucd
->nameTextW
, NULL
);
796 XtVaCreateManagedWidget("topLabel", xmLabelGadgetClass
, form
,
797 XmNlabelString
, s1
=MKSTRING(
798 "Select a macro menu item from the list at left.\n\
799 Select \"New\" to add a new command to the menu."),
800 XmNtopAttachment
, XmATTACH_POSITION
,
802 XmNleftAttachment
, XmATTACH_POSITION
,
803 XmNleftPosition
, LIST_RIGHT
,
804 XmNrightAttachment
, XmATTACH_POSITION
,
805 XmNrightPosition
, RIGHT_MARGIN_POS
,
806 XmNbottomAttachment
, XmATTACH_WIDGET
,
807 XmNbottomWidget
, nameLabel
, NULL
);
810 cmdLabel
= XtVaCreateManagedWidget("cmdLabel", xmLabelGadgetClass
, form
,
811 XmNlabelString
, s1
=MKSTRING("Macro Command to Execute"),
813 XmNalignment
, XmALIGNMENT_BEGINNING
,
815 XmNtopAttachment
, XmATTACH_POSITION
,
816 XmNtopPosition
, MACRO_CMD_TOP
,
817 XmNleftAttachment
, XmATTACH_POSITION
,
818 XmNleftPosition
, LEFT_MARGIN_POS
, NULL
);
821 okBtn
= XtVaCreateManagedWidget("ok",xmPushButtonWidgetClass
,form
,
822 XmNlabelString
, s1
=MKSTRING("OK"),
823 XmNleftAttachment
, XmATTACH_POSITION
,
825 XmNrightAttachment
, XmATTACH_POSITION
,
826 XmNrightPosition
, 23,
827 XmNbottomAttachment
, XmATTACH_POSITION
,
828 XmNbottomPosition
, 99, NULL
);
829 XtAddCallback(okBtn
, XmNactivateCallback
, okCB
, ucd
);
832 applyBtn
= XtVaCreateManagedWidget("apply",xmPushButtonWidgetClass
,form
,
833 XmNlabelString
, s1
=MKSTRING("Apply"),
835 XmNleftAttachment
, XmATTACH_POSITION
,
837 XmNrightAttachment
, XmATTACH_POSITION
,
838 XmNrightPosition
, 46,
839 XmNbottomAttachment
, XmATTACH_POSITION
,
840 XmNbottomPosition
, 99, NULL
);
841 XtAddCallback(applyBtn
, XmNactivateCallback
, applyCB
, ucd
);
844 applyBtn
= XtVaCreateManagedWidget("check",xmPushButtonWidgetClass
,form
,
845 XmNlabelString
, s1
=MKSTRING("Check"),
847 XmNleftAttachment
, XmATTACH_POSITION
,
849 XmNrightAttachment
, XmATTACH_POSITION
,
850 XmNrightPosition
, 69,
851 XmNbottomAttachment
, XmATTACH_POSITION
,
852 XmNbottomPosition
, 99, NULL
);
853 XtAddCallback(applyBtn
, XmNactivateCallback
, checkCB
, ucd
);
856 dismissBtn
= XtVaCreateManagedWidget("dismiss",xmPushButtonWidgetClass
,form
,
857 XmNlabelString
, s1
=MKSTRING("Dismiss"),
858 XmNleftAttachment
, XmATTACH_POSITION
,
860 XmNrightAttachment
, XmATTACH_POSITION
,
861 XmNrightPosition
, 92,
862 XmNbottomAttachment
, XmATTACH_POSITION
,
863 XmNbottomPosition
, 99, NULL
);
864 XtAddCallback(dismissBtn
, XmNactivateCallback
, dismissCB
, ucd
);
868 XtSetArg(args
[ac
], XmNeditMode
, XmMULTI_LINE_EDIT
); ac
++;
869 XtSetArg(args
[ac
], XmNtopAttachment
, XmATTACH_WIDGET
); ac
++;
870 XtSetArg(args
[ac
], XmNtopWidget
, cmdLabel
); ac
++;
871 XtSetArg(args
[ac
], XmNleftAttachment
, XmATTACH_POSITION
); ac
++;
872 XtSetArg(args
[ac
], XmNleftPosition
, LEFT_MARGIN_POS
); ac
++;
873 XtSetArg(args
[ac
], XmNrightAttachment
, XmATTACH_POSITION
); ac
++;
874 XtSetArg(args
[ac
], XmNrightPosition
, RIGHT_MARGIN_POS
); ac
++;
875 XtSetArg(args
[ac
], XmNbottomAttachment
, XmATTACH_WIDGET
); ac
++;
876 XtSetArg(args
[ac
], XmNbottomWidget
, okBtn
); ac
++;
877 XtSetArg(args
[ac
], XmNbottomOffset
, 5); ac
++;
878 ucd
->cmdTextW
= XmCreateScrolledText(form
, "name", args
, ac
);
879 AddMouseWheelSupport(ucd
->cmdTextW
);
880 XtManageChild(ucd
->cmdTextW
);
881 RemapDeleteKey(ucd
->cmdTextW
);
882 XtVaSetValues(cmdLabel
, XmNuserData
, ucd
->cmdTextW
, NULL
); /* for mnemonic */
884 /* Disable text input for the accelerator key widget, let the
885 event handler manage it instead */
886 disableTextW(ucd
->accTextW
);
888 /* initializs the dialog fields to match "New" list item */
889 updateDialogFields(NULL
, ucd
);
891 /* Set initial default button */
892 XtVaSetValues(form
, XmNdefaultButton
, okBtn
, NULL
);
893 XtVaSetValues(form
, XmNcancelButton
, dismissBtn
, NULL
);
895 /* Handle mnemonic selection of buttons and focus to dialog */
896 AddDialogMnemonicHandler(form
, FALSE
);
898 /* Make widgets for top level shell and paste-replay buttons available
899 to other functions */
900 if (dialogType
== MACRO_CMDS
) {
901 MacroCmdDialog
= ucd
->dlogShell
;
902 MacroPasteReplayBtn
= pasteReplayBtn
;
904 BGMenuCmdDialog
= ucd
->dlogShell
;
905 BGMenuPasteReplayBtn
= pasteReplayBtn
;
908 /* Realize all of the widgets in the new dialog */
909 RealizeWithoutForcingPosition(ucd
->dlogShell
);
913 ** Update the Shell, Macro, and window background menus menu of window "window"
914 ** from the currently loaded command descriptions.
916 void UpdateShellMenu(WindowInfo
*window
)
918 updateMenu(window
, SHELL_CMDS
);
920 void UpdateMacroMenu(WindowInfo
*window
)
922 updateMenu(window
, MACRO_CMDS
);
924 void UpdateBGMenu(WindowInfo
*window
)
926 updateMenu(window
, BG_MENU_CMDS
);
930 ** Dim/undim buttons for pasting replay macros into macro and bg menu dialogs
932 void DimPasteReplayBtns(int sensitive
)
934 if (MacroCmdDialog
!= NULL
)
935 XtSetSensitive(MacroPasteReplayBtn
, sensitive
);
936 if (BGMenuCmdDialog
!= NULL
)
937 XtSetSensitive(BGMenuPasteReplayBtn
, sensitive
);
941 ** Dim/undim user programmable menu items which depend on there being
942 ** a selection in their associated window.
944 void DimSelectionDepUserMenuItems(WindowInfo
*window
, int sensitive
)
947 dimSelDepItemsInMenu(window
->shellMenuPane
, ShellMenuItems
,
948 NShellMenuItems
, sensitive
);
950 dimSelDepItemsInMenu(window
->macroMenuPane
, MacroMenuItems
,
951 NMacroMenuItems
, sensitive
);
952 dimSelDepItemsInMenu(window
->bgMenuPane
, BGMenuItems
,
953 NBGMenuItems
, sensitive
);
956 static void dimSelDepItemsInMenu(Widget menuPane
, menuItemRec
**menuList
,
957 int nMenuItems
, int sensitive
)
965 XtVaGetValues(menuPane
, XmNchildren
, &items
, XmNnumChildren
, &nItems
, NULL
);
966 for (n
=0; n
<(int)nItems
; n
++) {
967 XtVaGetValues(items
[n
], XmNuserData
, &userData
, NULL
);
968 if (userData
!= (XtPointer
)PERMANENT_MENU_ITEM
) {
969 if (XtClass(items
[n
]) == xmCascadeButtonWidgetClass
) {
970 XtVaGetValues(items
[n
], XmNsubMenuId
, &subMenu
, NULL
);
971 dimSelDepItemsInMenu(subMenu
, menuList
, nMenuItems
, sensitive
);
973 index
= (int)userData
- 10;
974 if (index
<0 || index
>= nMenuItems
)
976 if (menuList
[index
]->input
== FROM_SELECTION
)
977 XtSetSensitive(items
[n
], sensitive
);
984 ** Harmless kludge for making undo/redo menu items in background menu properly
985 ** sensitive (even though they're programmable) along with real undo item
988 void SetBGMenuUndoSensitivity(WindowInfo
*window
, int sensitive
)
990 if (window
->bgMenuUndoItem
!= NULL
)
991 XtSetSensitive(window
->bgMenuUndoItem
, sensitive
);
993 void SetBGMenuRedoSensitivity(WindowInfo
*window
, int sensitive
)
995 if (window
->bgMenuRedoItem
!= NULL
)
996 XtSetSensitive(window
->bgMenuRedoItem
, sensitive
);
1000 ** Generate a text string for the preferences file describing the contents
1001 ** of the shell cmd list. This string is not exactly of the form that it
1002 ** can be read by LoadShellCmdsString, rather, it is what needs to be written
1003 ** to a resource file such that it will read back in that form.
1005 char *WriteShellCmdsString(void)
1007 return writeMenuItemString(ShellMenuItems
, NShellMenuItems
,
1012 ** Generate a text string for the preferences file describing the contents of
1013 ** the macro menu and background menu commands lists. These strings are not
1014 ** exactly of the form that it can be read by LoadMacroCmdsString, rather, it
1015 ** is what needs to be written to a resource file such that it will read back
1018 char *WriteMacroCmdsString(void)
1020 return writeMenuItemString(MacroMenuItems
, NMacroMenuItems
, MACRO_CMDS
);
1023 char *WriteBGMenuCmdsString(void)
1025 return writeMenuItemString(BGMenuItems
, NBGMenuItems
, BG_MENU_CMDS
);
1029 ** Read a string representing shell command menu items and add them to the
1030 ** internal list used for constructing shell menus
1032 int LoadShellCmdsString(char *inString
)
1034 return loadMenuItemString(inString
, ShellMenuItems
, &NShellMenuItems
,
1039 ** Read strings representing macro menu or background menu command menu items
1040 ** and add them to the internal lists used for constructing menus
1042 int LoadMacroCmdsString(char *inString
)
1044 return loadMenuItemString(inString
, MacroMenuItems
, &NMacroMenuItems
,
1048 int LoadBGMenuCmdsString(char *inString
)
1050 return loadMenuItemString(inString
, BGMenuItems
, &NBGMenuItems
,
1055 ** Search through the shell menu and execute the first command with menu item
1056 ** name "itemName". Returns True on successs and False on failure.
1059 int DoNamedShellMenuCmd(WindowInfo
*window
, const char *itemName
, int fromMacro
)
1063 for (i
=0; i
<NShellMenuItems
; i
++) {
1064 if (!strcmp(ShellMenuItems
[i
]->name
, itemName
)) {
1065 if (ShellMenuItems
[i
]->output
== TO_SAME_WINDOW
&&
1066 CheckReadOnly(window
))
1068 DoShellMenuCmd(window
, ShellMenuItems
[i
]->cmd
,
1069 ShellMenuItems
[i
]->input
, ShellMenuItems
[i
]->output
,
1070 ShellMenuItems
[i
]->repInput
, ShellMenuItems
[i
]->saveFirst
,
1071 ShellMenuItems
[i
]->loadAfter
, fromMacro
);
1080 ** Search through the Macro or background menu and execute the first command
1081 ** with menu item name "itemName". Returns True on successs and False on
1084 int DoNamedMacroMenuCmd(WindowInfo
*window
, const char *itemName
)
1088 for (i
=0; i
<NMacroMenuItems
; i
++) {
1089 if (!strcmp(MacroMenuItems
[i
]->name
, itemName
)) {
1090 DoMacro(window
, MacroMenuItems
[i
]->cmd
, "macro menu command");
1097 int DoNamedBGMenuCmd(WindowInfo
*window
, const char *itemName
)
1101 for (i
=0; i
<NBGMenuItems
; i
++) {
1102 if (!strcmp(BGMenuItems
[i
]->name
, itemName
)) {
1103 DoMacro(window
, BGMenuItems
[i
]->cmd
, "background menu macro");
1111 ** Update all of the Shell or Macro menus of all editor windows.
1113 static void updateMenus(int menuType
)
1117 for (w
=WindowList
; w
!=NULL
; w
=w
->next
)
1118 updateMenu(w
, menuType
);
1122 ** Updates either the Shell menu or the Macro menu of "window", depending on
1123 ** value of "menuType"
1125 static void updateMenu(WindowInfo
*window
, int menuType
)
1127 Widget btn
, menuPane
, subPane
, newSubPane
;
1129 menuItemRec
*f
, **itemList
;
1130 menuTreeItem
*menuTree
;
1131 int i
, nTreeEntries
, isDefaultLM
;
1132 char *hierName
, *namePtr
, *subMenuName
, *subSep
, *strippedName
, *name
;
1134 if (!IsTopDocument(window
))
1137 /* Fetch the appropriate menu pane and item list for this menu type */
1138 if (menuType
== SHELL_CMDS
) {
1139 menuPane
= window
->shellMenuPane
;
1140 itemList
= ShellMenuItems
;
1141 nListItems
= NShellMenuItems
;
1142 } else if (menuType
== MACRO_CMDS
) {
1143 menuPane
= window
->macroMenuPane
;
1144 itemList
= MacroMenuItems
;
1145 nListItems
= NMacroMenuItems
;
1146 } else { /* BG_MENU_CMDS */
1147 menuPane
= window
->bgMenuPane
;
1148 itemList
= BGMenuItems
;
1149 nListItems
= NBGMenuItems
;
1152 /* if the menu is torn off, unmanage the menu pane
1153 before updating it to prevent the tear-off menu
1154 from shrinking and expanding as the menu entries
1156 if (!XmIsMenuShell(XtParent(menuPane
)))
1157 XtUnmanageChild(menuPane
);
1159 /* Remove all of the existing user commands from the menu */
1160 removeMenuItems(menuPane
);
1162 /* Allocate storage for structures to help find sub-menus */
1163 menuTree
= (menuTreeItem
*)XtMalloc(sizeof(menuTreeItem
) * nListItems
);
1166 /* Harmless kludge: undo and redo items are marked specially if found
1167 in the background menu, and used to dim/undim with edit menu */
1168 window
->bgMenuUndoItem
= NULL
;
1169 window
->bgMenuRedoItem
= NULL
;
1172 ** Add items to the list, creating hierarchical sub-menus as necessary,
1173 ** and skipping items not intended for this language mode
1175 for (n
=0; n
<nListItems
; n
++) {
1178 /* Eliminate items meant for other language modes, strip @ sign parts.
1179 If the language mode is "*", scan the list for an item with the
1180 same name and a language mode specified. If one is found, skip
1181 the item in favor of the exact match. */
1182 strippedName
= findStripLanguageMode(f
->name
, window
->languageMode
,
1184 if (strippedName
== NULL
)
1185 continue; /* not a valid entry for the language */
1187 for (i
=0; i
<nListItems
; i
++) {
1188 name
= findStripLanguageMode(itemList
[i
]->name
,
1189 window
->languageMode
, &isDefaultLM
);
1190 if (name
!=NULL
&& !isDefaultLM
&& !strcmp(name
, strippedName
)) {
1191 XtFree(name
); /* item with matching language overrides */
1196 if (i
!= nListItems
) {
1197 XtFree(strippedName
);
1202 /* create/find sub-menus, stripping off '>' until item name is
1203 reached, then create the menu item */
1204 namePtr
= strippedName
;
1207 subSep
= strchr(namePtr
, '>');
1208 if (subSep
== NULL
) {
1209 btn
= createUserMenuItem(subPane
, namePtr
, f
, n
,
1210 (XtCallbackProc
)(menuType
== SHELL_CMDS
? shellMenuCB
:
1211 (menuType
== MACRO_CMDS
? macroMenuCB
: bgMenuCB
)),
1213 if (menuType
== BG_MENU_CMDS
&& !strcmp(f
->cmd
, "undo()\n"))
1214 window
->bgMenuUndoItem
= btn
;
1215 else if (menuType
== BG_MENU_CMDS
&& !strcmp(f
->cmd
,"redo()\n"))
1216 window
->bgMenuRedoItem
= btn
;
1217 UpdateAccelLockPatch(window
->splitPane
, btn
);
1220 hierName
= copySubstring(strippedName
, subSep
- strippedName
);
1221 newSubPane
= findInMenuTree(menuTree
, nTreeEntries
, hierName
);
1222 if (newSubPane
== NULL
) {
1223 subMenuName
= copySubstring(namePtr
, subSep
- namePtr
);
1224 newSubPane
= createUserSubMenu(subPane
, subMenuName
);
1225 XtFree(subMenuName
);
1226 menuTree
[nTreeEntries
].name
= hierName
;
1227 menuTree
[nTreeEntries
++].menuPane
= newSubPane
;
1230 subPane
= newSubPane
;
1231 namePtr
= subSep
+ 1;
1233 XtFree(strippedName
);
1236 /* Free the structure used to keep track of sub-menus durring creation */
1237 for (i
=0; i
<nTreeEntries
; i
++)
1238 XtFree(menuTree
[i
].name
);
1239 XtFree((char *)menuTree
);
1241 /* Set the proper sensitivity of items which may be dimmed */
1242 SetBGMenuUndoSensitivity(window
, XtIsSensitive(window
->undoItem
));
1243 SetBGMenuRedoSensitivity(window
, XtIsSensitive(window
->redoItem
));
1244 DimSelectionDepUserMenuItems(window
, window
->buffer
->primary
.selected
);
1246 /* if the menu is torn off, we need to manually adjust the
1247 dimension of the menuShell _before_ re-managing the menu
1248 pane, to either expose the hidden menu entries or remove
1250 if (!XmIsMenuShell(XtParent(menuPane
))) {
1251 Dimension width
, height
;
1253 XtVaGetValues(menuPane
, XmNwidth
, &width
,
1254 XmNheight
, &height
, NULL
);
1255 XtVaSetValues(XtParent(menuPane
), XmNwidth
, width
,
1256 XmNheight
, height
, NULL
);
1257 XtManageChild(menuPane
);
1260 XtVaSetValues(menuPane
, XmNuserData
,
1261 (void *)window
->languageMode
, NULL
);
1265 ** Find the widget corresponding to a hierarchical menu name (a>b>c...)
1267 static Widget
findInMenuTree(menuTreeItem
*menuTree
, int nTreeEntries
,
1268 const char *hierName
)
1272 for (i
=0; i
<nTreeEntries
; i
++)
1273 if (!strcmp(hierName
, menuTree
[i
].name
))
1274 return menuTree
[i
].menuPane
;
1278 static char *copySubstring(const char *string
, int length
)
1280 char *retStr
= XtMalloc(length
+ 1);
1282 strncpy(retStr
, string
, length
);
1283 retStr
[length
] = '\0';
1288 ** Look for at signs (@) in the string menuItemName, and match them
1289 ** against the current language mode. If there are no @ signs, just
1290 ** return an allocated copy of menuItemName. If there are @ signs, match
1291 ** the following text against languageMode, and return NULL if none match,
1292 ** or an allocated copy of menuItemName stripped of @ parts. If the
1293 ** language name is "*", sets isDefaultLM to true.
1295 static char *findStripLanguageMode(const char *menuItemName
, int languageMode
,
1298 char *atPtr
, *firstAtPtr
, *endPtr
;
1301 atPtr
= firstAtPtr
= strchr(menuItemName
, '@');
1302 *isDefaultLM
= False
;
1305 return XtNewString(menuItemName
);
1307 if (!strcmp(atPtr
+1, "*")) {
1308 /* only language is "*": this is for all but language specific macros */
1309 *isDefaultLM
= True
;
1310 return copySubstring(menuItemName
, firstAtPtr
-menuItemName
);
1312 if (languageMode
== PLAIN_LANGUAGE_MODE
)
1315 for(endPtr
=atPtr
+1; isalnum((unsigned char)*endPtr
) || *endPtr
=='_' ||
1316 *endPtr
=='-' || *endPtr
==' ' || *endPtr
=='+' || *endPtr
=='$' ||
1317 *endPtr
=='#'; endPtr
++);
1318 lmNameLen
= endPtr
-atPtr
-1;
1319 if (!strncmp(LanguageModeName(languageMode
), atPtr
+1, lmNameLen
) &&
1320 LanguageModeName(languageMode
)[lmNameLen
] == '\0')
1321 return copySubstring(menuItemName
, firstAtPtr
-menuItemName
);
1322 atPtr
= strchr(atPtr
+1, '@');
1328 static Widget
createUserMenuItem(Widget menuPane
, char *name
, menuItemRec
*f
,
1329 int index
, XtCallbackProc cbRtn
, XtPointer cbArg
)
1332 char accText
[MAX_ACCEL_LEN
], accKeys
[MAX_ACCEL_LEN
+5];
1335 generateAcceleratorString(accText
, f
->modifiers
, f
->keysym
);
1336 genAccelEventName(accKeys
, f
->modifiers
, f
->keysym
);
1337 st1
=XmStringCreateSimple(name
);
1338 st2
=XmStringCreateSimple(accText
);
1339 btn
= XtVaCreateManagedWidget("cmd", xmPushButtonWidgetClass
, menuPane
,
1340 XmNlabelString
, st1
,
1341 XmNacceleratorText
, st2
,
1342 XmNaccelerator
, accKeys
,
1343 XmNmnemonic
, f
->mnemonic
,
1344 XmNuserData
, index
+10, NULL
);
1345 XtAddCallback(btn
, XmNactivateCallback
, cbRtn
, cbArg
);
1352 ** Add a user-defined sub-menu to an established pull-down menu, marking
1353 ** it's userData field with TEMPORARY_MENU_ITEM so it can be found and
1354 ** removed later if the menu is redefined. Returns the menu pane of the
1357 static Widget
createUserSubMenu(Widget parent
, char *label
)
1361 static Arg args
[1] = {{XmNuserData
, (XtArgVal
)TEMPORARY_MENU_ITEM
}};
1363 menu
= CreatePulldownMenu(parent
, "userPulldown", args
, 1);
1364 XtVaCreateManagedWidget("userCascade", xmCascadeButtonWidgetClass
, parent
,
1365 XmNlabelString
, st1
=XmStringCreateSimple(label
),
1366 XmNsubMenuId
, menu
, XmNuserData
, TEMPORARY_MENU_ITEM
, NULL
);
1371 static void removeMenuItems(Widget menuPane
)
1373 WidgetList items
, itemList
;
1379 /* Fetch the list of children from the menu pane, and make a copy
1380 (because the widget alters this list as you delete widgets) */
1381 XtVaGetValues(menuPane
, XmNchildren
, &itemList
, XmNnumChildren
, &nItems
,
1383 items
= (WidgetList
)XtMalloc(sizeof(Widget
) * nItems
);
1384 memcpy(items
, itemList
, sizeof(Widget
) * nItems
);
1386 /* Delete all of the widgets not marked as PERMANENT_MENU_ITEM */
1387 for (n
=0; n
<(int)nItems
; n
++) {
1388 XtVaGetValues(items
[n
], XmNuserData
, &userData
, NULL
);
1389 if (userData
!= (XtPointer
)PERMANENT_MENU_ITEM
) {
1390 if (XtClass(items
[n
]) == xmCascadeButtonWidgetClass
) {
1391 XtVaGetValues(items
[n
], XmNsubMenuId
, &subMenuID
, NULL
);
1392 removeMenuItems(subMenuID
);
1393 #if XmVersion < 2000 /* Skipping this creates a memory and server resource
1394 leak (though both are reclaimed on window closing). In
1395 Motif 2.0 (and beyond?) there is a potential crash during
1396 phase 2 widget destruction in "SetCascadeField", and in
1397 Motif 1.2 there are free-memory reads. I would really like
1398 to be able to destroy this. */
1399 XtDestroyWidget(subMenuID
);
1401 } else /* remove accel. before destroy or lose it forever */
1402 XtVaSetValues(items
[n
], XmNaccelerator
, NULL
, NULL
);
1403 /* unmanaging before destroying stops parent from displaying */
1404 XtUnmanageChild(items
[n
]);
1405 XtDestroyWidget(items
[n
]);
1408 XtFree((char *)items
);
1411 static void dismissCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1413 userCmdDialog
*ucd
= (userCmdDialog
*)clientData
;
1415 /* Mark that there's no longer a (macro, bg, or shell) dialog up */
1416 if (ucd
->dialogType
== SHELL_CMDS
)
1417 ShellCmdDialog
= NULL
;
1418 else if (ucd
->dialogType
== MACRO_CMDS
)
1419 MacroCmdDialog
= NULL
;
1421 BGMenuCmdDialog
= NULL
;
1423 /* pop down and destroy the dialog (memory for ucd is freed in the
1424 destroy callback) */
1425 XtDestroyWidget(ucd
->dlogShell
);
1428 static void okCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1430 userCmdDialog
*ucd
= (userCmdDialog
*)clientData
;
1432 /* Read the dialog fields, and update the menus */
1433 if (!applyDialogChanges(ucd
))
1436 /* Mark that there's no longer a (macro, bg, or shell) dialog up */
1437 if (ucd
->dialogType
== SHELL_CMDS
)
1438 ShellCmdDialog
= NULL
;
1439 else if (ucd
->dialogType
== MACRO_CMDS
)
1440 MacroCmdDialog
= NULL
;
1442 BGMenuCmdDialog
= NULL
;
1444 /* pop down and destroy the dialog (memory for ucd is freed in the
1445 destroy callback) */
1446 XtDestroyWidget(ucd
->dlogShell
);
1449 static void applyCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1451 applyDialogChanges((userCmdDialog
*)clientData
);
1454 static void checkCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1456 userCmdDialog
*ucd
= (userCmdDialog
*)clientData
;
1458 if (checkMacro(ucd
))
1460 DialogF(DF_INF
, ucd
->dlogShell
, 1, "Macro",
1461 "Macro compiled without error", "Dismiss");
1465 static int checkMacro(userCmdDialog
*ucd
)
1469 f
= readDialogFields(ucd
, False
);
1472 if (!checkMacroText(f
->cmd
, ucd
->dlogShell
, ucd
->cmdTextW
)) {
1479 static int checkMacroText(char *macro
, Widget errorParent
, Widget errFocus
)
1482 char *errMsg
, *stoppedAt
;
1484 prog
= ParseMacro(macro
, &errMsg
, &stoppedAt
);
1486 if (errorParent
!= NULL
) {
1487 ParseError(errorParent
, macro
, stoppedAt
, "macro", errMsg
);
1488 XmTextSetInsertionPosition(errFocus
, stoppedAt
- macro
);
1489 XmProcessTraversal(errFocus
, XmTRAVERSE_CURRENT
);
1494 if (*stoppedAt
!= '\0') {
1495 if (errorParent
!= NULL
) {
1496 ParseError(errorParent
, macro
, stoppedAt
,"macro","syntax error");
1497 XmTextSetInsertionPosition(errFocus
, stoppedAt
- macro
);
1498 XmProcessTraversal(errFocus
, XmTRAVERSE_CURRENT
);
1505 static int applyDialogChanges(userCmdDialog
*ucd
)
1509 /* Get the current contents of the dialog fields */
1510 if (!UpdateManagedList(ucd
->managedList
, True
))
1513 /* Test compile the macro */
1514 if (ucd
->dialogType
== MACRO_CMDS
)
1515 if (!checkMacro(ucd
))
1518 /* Update the menu information */
1519 if (ucd
->dialogType
== SHELL_CMDS
) {
1520 for (i
=0; i
<NShellMenuItems
; i
++)
1521 freeMenuItemRec(ShellMenuItems
[i
]);
1522 for (i
=0; i
<ucd
->nMenuItems
; i
++)
1523 ShellMenuItems
[i
] = copyMenuItemRec(ucd
->menuItemsList
[i
]);
1524 NShellMenuItems
= ucd
->nMenuItems
;
1525 } else if (ucd
->dialogType
== MACRO_CMDS
) {
1526 for (i
=0; i
<NMacroMenuItems
; i
++)
1527 freeMenuItemRec(MacroMenuItems
[i
]);
1528 for (i
=0; i
<ucd
->nMenuItems
; i
++)
1529 MacroMenuItems
[i
] = copyMenuItemRec(ucd
->menuItemsList
[i
]);
1530 NMacroMenuItems
= ucd
->nMenuItems
;
1531 } else { /* BG_MENU_CMDS */
1532 for (i
=0; i
<NBGMenuItems
; i
++)
1533 freeMenuItemRec(BGMenuItems
[i
]);
1534 for (i
=0; i
<ucd
->nMenuItems
; i
++)
1535 BGMenuItems
[i
] = copyMenuItemRec(ucd
->menuItemsList
[i
]);
1536 NBGMenuItems
= ucd
->nMenuItems
;
1539 /* Update the menus themselves in all of the NEdit windows */
1540 updateMenus(ucd
->dialogType
);
1542 /* Note that preferences have been changed */
1547 static void pasteReplayCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1549 userCmdDialog
*ucd
= (userCmdDialog
*)clientData
;
1551 if (GetReplayMacro() == NULL
)
1554 XmTextInsert(ucd
->cmdTextW
, XmTextGetInsertionPosition(ucd
->cmdTextW
),
1558 static void destroyCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1560 userCmdDialog
*ucd
= (userCmdDialog
*)clientData
;
1563 for (i
=0; i
<ucd
->nMenuItems
; i
++)
1564 freeMenuItemRec(ucd
->menuItemsList
[i
]);
1565 XtFree((char *)ucd
->menuItemsList
);
1566 XtFree((char *)ucd
);
1569 static void accFocusCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1571 userCmdDialog
*ucd
= (userCmdDialog
*)clientData
;
1573 RemoveDialogMnemonicHandler(XtParent(ucd
->accTextW
));
1576 static void accLoseFocusCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1578 userCmdDialog
*ucd
= (userCmdDialog
*)clientData
;
1580 AddDialogMnemonicHandler(XtParent(ucd
->accTextW
), FALSE
);
1583 static void accKeyCB(Widget w
, XtPointer clientData
, XKeyEvent
*event
)
1585 userCmdDialog
*ucd
= (userCmdDialog
*)clientData
;
1586 KeySym keysym
= XLookupKeysym(event
, 0);
1587 char outStr
[MAX_ACCEL_LEN
];
1589 /* Accept only real keys, not modifiers alone */
1590 if (IsModifierKey(keysym
))
1593 /* Tab key means go to the next field, don't enter */
1594 if (keysym
== XK_Tab
)
1597 /* Beep and return if the modifiers are buttons or ones we don't support */
1598 if (event
->state
& ~(ShiftMask
| LockMask
| ControlMask
| Mod1Mask
|
1599 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
)) {
1600 XBell(TheDisplay
, 0);
1604 /* Delete or backspace clears field */
1605 if (keysym
== XK_Delete
|| keysym
== XK_BackSpace
) {
1606 XmTextSetString(ucd
->accTextW
, "");
1610 /* generate the string to use in the dialog field */
1611 generateAcceleratorString(outStr
, event
->state
, keysym
);
1613 /* Reject single character accelerators (a very simple way to eliminate
1614 un-modified letters and numbers) The goal is give users a clue that
1615 they're supposed to type the actual keys, not the name. This scheme
1616 is not rigorous and still allows accelerators like Comma. */
1617 if (strlen(outStr
) == 1) {
1618 XBell(TheDisplay
, 0);
1622 /* fill in the accelerator field in the dialog */
1623 XmTextSetString(ucd
->accTextW
, outStr
);
1626 static void sameOutCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1628 XtSetSensitive(((userCmdDialog
*)clientData
)->repInpBtn
,
1629 XmToggleButtonGetState(w
));
1632 static void shellMenuCB(Widget w
, WindowInfo
*window
, XtPointer callData
)
1638 window
= WidgetToWindow(MENU_WIDGET(w
));
1640 /* get the index of the shell command and verify that it's in range */
1641 XtVaGetValues(w
, XmNuserData
, &userData
, NULL
);
1642 index
= (int)userData
- 10;
1643 if (index
<0 || index
>= NShellMenuItems
)
1646 params
[0] = ShellMenuItems
[index
]->name
;
1647 XtCallActionProc(window
->lastFocus
, "shell_menu_command",
1648 ((XmAnyCallbackStruct
*)callData
)->event
, params
, 1);
1651 static void macroMenuCB(Widget w
, WindowInfo
*window
, XtPointer callData
)
1657 window
= WidgetToWindow(MENU_WIDGET(w
));
1659 /* Don't allow users to execute a macro command from the menu (or accel)
1660 if there's already a macro command executing. NEdit can't handle
1661 running multiple, independent uncoordinated, macros in the same
1662 window. Macros may invoke macro menu commands recursively via the
1663 macro_menu_command action proc, which is important for being able to
1664 repeat any operation, and to embed macros within eachother at any
1665 level, however, a call here with a macro running means that THE USER
1666 is explicitly invoking another macro via the menu or an accelerator. */
1667 if (window
->macroCmdData
!= NULL
) {
1668 XBell(TheDisplay
, 0);
1672 /* get the index of the macro command and verify that it's in range */
1673 XtVaGetValues(w
, XmNuserData
, &userData
, NULL
);
1674 index
= (int)userData
- 10;
1675 if (index
<0 || index
>= NMacroMenuItems
)
1678 params
[0] = MacroMenuItems
[index
]->name
;
1679 XtCallActionProc(window
->lastFocus
, "macro_menu_command",
1680 ((XmAnyCallbackStruct
*)callData
)->event
, params
, 1);
1683 static void bgMenuCB(Widget w
, WindowInfo
*window
, XtPointer callData
)
1689 window
= WidgetToWindow(MENU_WIDGET(w
));
1691 /* Same remark as for macro menu commands (see above). */
1692 if (window
->macroCmdData
!= NULL
) {
1693 XBell(TheDisplay
, 0);
1697 /* get the index of the macro command and verify that it's in range */
1698 XtVaGetValues(w
, XmNuserData
, &userData
, NULL
);
1699 index
= (int)userData
- 10;
1700 if (index
<0 || index
>= NBGMenuItems
)
1703 params
[0] = BGMenuItems
[index
]->name
;
1704 XtCallActionProc(window
->lastFocus
, "bg_menu_command",
1705 ((XmAnyCallbackStruct
*)callData
)->event
, params
, 1);
1709 ** Update the name, accelerator, mnemonic, and command fields in the shell
1710 ** command or macro dialog to agree with the currently selected item in the
1713 static void updateDialogFields(menuItemRec
*f
, userCmdDialog
*ucd
)
1715 char mneString
[2], accString
[MAX_ACCEL_LEN
];
1717 /* fill in the name, accelerator, mnemonic, and command fields of the
1718 dialog for the newly selected item, or blank them if "New" is selected */
1720 XmTextSetString(ucd
->nameTextW
, "");
1721 XmTextSetString(ucd
->cmdTextW
, "");
1722 XmTextSetString(ucd
->accTextW
, "");
1723 XmTextSetString(ucd
->mneTextW
, "");
1724 if (ucd
->dialogType
== SHELL_CMDS
) {
1725 RadioButtonChangeState(ucd
->selInpBtn
, True
, True
);
1726 RadioButtonChangeState(ucd
->sameOutBtn
, True
, True
);
1727 RadioButtonChangeState(ucd
->repInpBtn
, False
, False
);
1728 XtSetSensitive(ucd
->repInpBtn
, True
);
1729 RadioButtonChangeState(ucd
->saveFirstBtn
, False
, False
);
1730 RadioButtonChangeState(ucd
->loadAfterBtn
, False
, False
);
1733 mneString
[0] = f
->mnemonic
;
1734 mneString
[1] = '\0';
1735 generateAcceleratorString(accString
, f
->modifiers
, f
->keysym
);
1736 XmTextSetString(ucd
->nameTextW
, f
->name
);
1737 XmTextSetString(ucd
->cmdTextW
, f
->cmd
);
1738 XmTextSetString(ucd
->accTextW
, accString
);
1739 XmTextSetString(ucd
->mneTextW
, mneString
);
1740 RadioButtonChangeState(ucd
->selInpBtn
, f
->input
==FROM_SELECTION
, False
);
1741 if (ucd
->dialogType
== SHELL_CMDS
) {
1742 RadioButtonChangeState(ucd
->winInpBtn
, f
->input
== FROM_WINDOW
,
1744 RadioButtonChangeState(ucd
->eitherInpBtn
, f
->input
== FROM_EITHER
,
1746 RadioButtonChangeState(ucd
->noInpBtn
, f
->input
== FROM_NONE
,
1748 RadioButtonChangeState(ucd
->sameOutBtn
, f
->output
==TO_SAME_WINDOW
,
1750 RadioButtonChangeState(ucd
->winOutBtn
, f
->output
==TO_NEW_WINDOW
,
1752 RadioButtonChangeState(ucd
->dlogOutBtn
, f
->output
==TO_DIALOG
,
1754 RadioButtonChangeState(ucd
->repInpBtn
, f
->repInput
, False
);
1755 XtSetSensitive(ucd
->repInpBtn
, f
->output
==TO_SAME_WINDOW
);
1756 RadioButtonChangeState(ucd
->saveFirstBtn
, f
->saveFirst
, False
);
1757 RadioButtonChangeState(ucd
->loadAfterBtn
, f
->loadAfter
, False
);
1763 ** Read the name, accelerator, mnemonic, and command fields from the shell or
1764 ** macro commands dialog into a newly allocated menuItemRec. Returns a
1765 ** pointer to the new menuItemRec structure as the function value, or NULL on
1768 static menuItemRec
*readDialogFields(userCmdDialog
*ucd
, int silent
)
1770 char *nameText
, *cmdText
, *mneText
, *accText
;
1773 nameText
= XmTextGetString(ucd
->nameTextW
);
1774 if (*nameText
== '\0')
1778 DialogF(DF_WARN
, ucd
->dlogShell
, 1, "Menu Entry",
1779 "Please specify a name\nfor the menu item", "Dismiss");
1780 XmProcessTraversal(ucd
->nameTextW
, XmTRAVERSE_CURRENT
);
1786 if (strchr(nameText
, ':'))
1790 DialogF(DF_WARN
, ucd
->dlogShell
, 1, "Menu Entry",
1791 "Menu item names may not\ncontain colon (:) characters",
1793 XmProcessTraversal(ucd
->nameTextW
, XmTRAVERSE_CURRENT
);
1799 cmdText
= XmTextGetString(ucd
->cmdTextW
);
1800 if (cmdText
== NULL
|| *cmdText
== '\0')
1804 DialogF(DF_WARN
, ucd
->dlogShell
, 1, "Command to Execute",
1805 "Please specify %s to execute", "Dismiss",
1806 ucd
->dialogType
== SHELL_CMDS
1808 : "macro command(s)");
1809 XmProcessTraversal(ucd
->cmdTextW
, XmTRAVERSE_CURRENT
);
1820 if (ucd
->dialogType
== MACRO_CMDS
|| ucd
->dialogType
== BG_MENU_CMDS
) {
1821 addTerminatingNewline(&cmdText
);
1822 if (!checkMacroText(cmdText
, silent
? NULL
: ucd
->dlogShell
,
1829 f
= (menuItemRec
*)XtMalloc(sizeof(menuItemRec
));
1832 if ((mneText
= XmTextGetString(ucd
->mneTextW
)) != NULL
) {
1833 f
->mnemonic
= mneText
==NULL
? '\0' : mneText
[0];
1835 if (f
->mnemonic
== ':') /* colons mess up string parsing */
1838 if ((accText
= XmTextGetString(ucd
->accTextW
)) != NULL
) {
1839 parseAcceleratorString(accText
, &f
->modifiers
, &f
->keysym
);
1842 if (ucd
->dialogType
== SHELL_CMDS
) {
1843 if (XmToggleButtonGetState(ucd
->selInpBtn
))
1844 f
->input
= FROM_SELECTION
;
1845 else if (XmToggleButtonGetState(ucd
->winInpBtn
))
1846 f
->input
= FROM_WINDOW
;
1847 else if (XmToggleButtonGetState(ucd
->eitherInpBtn
))
1848 f
->input
= FROM_EITHER
;
1850 f
->input
= FROM_NONE
;
1851 if (XmToggleButtonGetState(ucd
->winOutBtn
))
1852 f
->output
= TO_NEW_WINDOW
;
1853 else if (XmToggleButtonGetState(ucd
->dlogOutBtn
))
1854 f
->output
= TO_DIALOG
;
1856 f
->output
= TO_SAME_WINDOW
;
1857 f
->repInput
= XmToggleButtonGetState(ucd
->repInpBtn
);
1858 f
->saveFirst
= XmToggleButtonGetState(ucd
->saveFirstBtn
);
1859 f
->loadAfter
= XmToggleButtonGetState(ucd
->loadAfterBtn
);
1861 f
->input
= XmToggleButtonGetState(ucd
->selInpBtn
) ? FROM_SELECTION
:
1863 f
->output
= TO_SAME_WINDOW
;
1864 f
->repInput
= False
;
1865 f
->saveFirst
= False
;
1866 f
->loadAfter
= False
;
1872 ** Copy a menu item record, and its associated memory
1874 static menuItemRec
*copyMenuItemRec(menuItemRec
*item
)
1876 menuItemRec
*newItem
;
1878 newItem
= (menuItemRec
*)XtMalloc(sizeof(menuItemRec
));
1880 newItem
->name
= XtMalloc(strlen(item
->name
)+1);
1881 strcpy(newItem
->name
, item
->name
);
1882 newItem
->cmd
= XtMalloc(strlen(item
->cmd
)+1);
1883 strcpy(newItem
->cmd
, item
->cmd
);
1888 ** Free a menu item record, and its associated memory
1890 static void freeMenuItemRec(menuItemRec
*item
)
1894 XtFree((char *)item
);
1898 ** Callbacks for managed-list operations
1900 static void *getDialogDataCB(void *oldItem
, int explicitRequest
, int *abort
,
1903 userCmdDialog
*ucd
= (userCmdDialog
*)cbArg
;
1904 menuItemRec
*currentFields
;
1906 /* If the dialog is currently displaying the "new" entry and the
1907 fields are empty, that's just fine */
1908 if (oldItem
== NULL
&& dialogFieldsAreEmpty(ucd
))
1911 /* If there are no problems reading the data, just return it */
1912 currentFields
= readDialogFields(ucd
, True
);
1913 if (currentFields
!= NULL
)
1914 return (void *)currentFields
;
1916 /* If user might not be expecting fields to be read, give more warning */
1917 if (!explicitRequest
)
1919 if (DialogF(DF_WARN
, ucd
->dlogShell
, 2, "Discard Entry",
1920 "Discard incomplete entry\nfor current menu item?", "Keep",
1923 return oldItem
== NULL
1925 : (void *)copyMenuItemRec((menuItemRec
*)oldItem
);
1929 /* Do readDialogFields again without "silent" mode to display warning(s) */
1930 readDialogFields(ucd
, False
);
1936 static void setDialogDataCB(void *item
, void *cbArg
)
1938 updateDialogFields((menuItemRec
*)item
, (userCmdDialog
*)cbArg
);
1941 static int dialogFieldsAreEmpty(userCmdDialog
*ucd
)
1943 return TextWidgetIsBlank(ucd
->nameTextW
) &&
1944 TextWidgetIsBlank(ucd
->cmdTextW
) &&
1945 TextWidgetIsBlank(ucd
->accTextW
) &&
1946 TextWidgetIsBlank(ucd
->mneTextW
) &&
1947 (ucd
->dialogType
!= SHELL_CMDS
|| (
1948 XmToggleButtonGetState(ucd
->selInpBtn
) &&
1949 XmToggleButtonGetState(ucd
->sameOutBtn
) &&
1950 !XmToggleButtonGetState(ucd
->repInpBtn
) &&
1951 !XmToggleButtonGetState(ucd
->saveFirstBtn
) &&
1952 !XmToggleButtonGetState(ucd
->loadAfterBtn
)));
1955 static void freeItemCB(void *item
)
1957 freeMenuItemRec((menuItemRec
*)item
);
1961 ** Gut a text widget of it's ability to process input
1963 static void disableTextW(Widget textW
)
1965 static XtTranslations emptyTable
= NULL
;
1966 static char *emptyTranslations
= "\
1967 <EnterWindow>: enter()\n\
1968 <Btn1Down>: grab-focus()\n\
1969 <Btn1Motion>: extend-adjust()\n\
1970 <Btn1Up>: extend-end()\n\
1971 Shift<Key>Tab: prev-tab-group()\n\
1972 Ctrl<Key>Tab: next-tab-group()\n\
1973 <Key>Tab: next-tab-group()\n\
1974 <LeaveWindow>: leave()\n\
1975 <FocusIn>: focusIn()\n\
1976 <FocusOut>: focusOut()\n\
1977 <Unmap>: unmap()\n";
1979 /* replace the translation table with the slimmed down one above */
1980 if (emptyTable
== NULL
)
1981 emptyTable
= XtParseTranslationTable(emptyTranslations
);
1982 XtVaSetValues(textW
, XmNtranslations
, emptyTable
, NULL
);
1985 static char *writeMenuItemString(menuItemRec
**menuItems
, int nItems
,
1988 char *outStr
, *outPtr
, *c
, accStr
[MAX_ACCEL_LEN
];
1992 /* determine the max. amount of memory needed for the returned string
1993 and allocate a buffer for composing the string */
1995 for (i
=0; i
<nItems
; i
++) {
1997 generateAcceleratorString(accStr
, f
->modifiers
, f
->keysym
);
1998 length
+= strlen(f
->name
) * 2; /* allow for \n & \\ expansions */
1999 length
+= strlen(accStr
);
2000 length
+= strlen(f
->cmd
) * 6; /* allow for \n & \\ expansions */
2001 length
+= 21; /* number of characters added below */
2003 length
++; /* terminating null */
2004 outStr
= XtMalloc(length
);
2006 /* write the string */
2010 for (i
=0; i
<nItems
; i
++) {
2012 generateAcceleratorString(accStr
, f
->modifiers
, f
->keysym
);
2014 for (c
=f
->name
; *c
!='\0'; ++c
) { /* Copy the command name */
2015 if (*c
== '\\') { /* changing backslashes to \\ */
2018 } else if (*c
== '\n') { /* changing newlines to \n */
2026 strcpy(outPtr
, accStr
);
2027 outPtr
+= strlen(accStr
);
2029 if (f
->mnemonic
!= '\0')
2030 *outPtr
++ = f
->mnemonic
;
2032 if (listType
== SHELL_CMDS
) {
2033 if (f
->input
== FROM_SELECTION
)
2035 else if (f
->input
== FROM_WINDOW
)
2037 else if (f
->input
== FROM_EITHER
)
2039 if (f
->output
== TO_DIALOG
)
2041 else if (f
->output
== TO_NEW_WINDOW
)
2051 if (f
->input
== FROM_SELECTION
)
2063 for (c
=f
->cmd
; *c
!='\0'; c
++) { /* Copy the command string, changing */
2064 if (*c
== '\\') { /* backslashes to double backslashes */
2065 *outPtr
++ = '\\'; /* and newlines to backslash-n's, */
2066 *outPtr
++ = '\\'; /* followed by real newlines and tab */
2067 } else if (*c
== '\n') {
2077 if (listType
== MACRO_CMDS
|| listType
== BG_MENU_CMDS
) {
2078 if (*(outPtr
-1) == '\t') outPtr
--;
2091 static int loadMenuItemString(char *inString
, menuItemRec
**menuItems
,
2092 int *nItems
, int listType
)
2096 char *inPtr
= inString
;
2097 char *nameStr
, accStr
[MAX_ACCEL_LEN
], mneChar
;
2099 unsigned int modifiers
;
2100 int i
, input
, output
, saveFirst
, loadAfter
, repInput
;
2101 int nameLen
, accLen
, mneLen
, cmdLen
;
2105 /* remove leading whitespace */
2106 while (*inPtr
== ' ' || *inPtr
== '\t')
2109 /* read name field */
2110 nameLen
= strcspn(inPtr
, ":");
2112 return parseError("no name field");
2113 nameStr
= XtMalloc(nameLen
+1);
2114 strncpy(nameStr
, inPtr
, nameLen
);
2115 nameStr
[nameLen
] = '\0';
2118 return parseError("end not expected");
2121 /* read accelerator field */
2122 accLen
= strcspn(inPtr
, ":");
2123 if (accLen
>= MAX_ACCEL_LEN
)
2124 return parseError("accelerator field too long");
2125 strncpy(accStr
, inPtr
, accLen
);
2126 accStr
[accLen
] = '\0';
2129 return parseError("end not expected");
2132 /* read menemonic field */
2133 mneLen
= strcspn(inPtr
, ":");
2135 return parseError("mnemonic field too long");
2142 return parseError("end not expected");
2144 /* read flags field */
2146 output
= TO_SAME_WINDOW
;
2150 for (; *inPtr
!= ':'; inPtr
++) {
2151 if (listType
== SHELL_CMDS
) {
2153 input
= FROM_SELECTION
;
2154 else if (*inPtr
== 'A')
2155 input
= FROM_WINDOW
;
2156 else if (*inPtr
== 'E')
2157 input
= FROM_EITHER
;
2158 else if (*inPtr
== 'W')
2159 output
= TO_NEW_WINDOW
;
2160 else if (*inPtr
== 'D')
2162 else if (*inPtr
== 'X')
2164 else if (*inPtr
== 'S')
2166 else if (*inPtr
== 'L')
2169 return parseError("unreadable flag field");
2172 input
= FROM_SELECTION
;
2174 return parseError("unreadable flag field");
2179 /* read command field */
2180 if (listType
== SHELL_CMDS
) {
2181 if (*inPtr
++ != '\n')
2182 return parseError("command must begin with newline");
2183 while (*inPtr
== ' ' || *inPtr
== '\t') /* leading whitespace */
2185 cmdLen
= strcspn(inPtr
, "\n");
2187 return parseError("shell command field is empty");
2188 cmdStr
= XtMalloc(cmdLen
+1);
2189 strncpy(cmdStr
, inPtr
, cmdLen
);
2190 cmdStr
[cmdLen
] = '\0';
2193 cmdStr
= copyMacroToEnd(&inPtr
);
2197 while (*inPtr
== ' ' || *inPtr
== '\t' || *inPtr
== '\n')
2198 inPtr
++; /* skip trailing whitespace & newline */
2200 /* parse the accelerator field */
2201 if (!parseAcceleratorString(accStr
, &modifiers
, &keysym
))
2202 return parseError("couldn't read accelerator field");
2204 /* create a menu item record */
2205 f
= (menuItemRec
*)XtMalloc(sizeof(menuItemRec
));
2208 f
->mnemonic
= mneChar
;
2209 f
->modifiers
= modifiers
;
2212 f
->repInput
= repInput
;
2213 f
->saveFirst
= saveFirst
;
2214 f
->loadAfter
= loadAfter
;
2217 /* add/replace menu record in the list */
2218 for (i
=0; i
< *nItems
; i
++) {
2219 if (!strcmp(menuItems
[i
]->name
, f
->name
)) {
2220 freeMenuItemRec(menuItems
[i
]);
2226 menuItems
[(*nItems
)++] = f
;
2228 /* end of string in proper place */
2234 static int parseError(const char *message
)
2236 fprintf(stderr
, "NEdit: Parse error in user defined menu item, %s\n",
2242 ** Create a text string representing an accelerator for the dialog,
2243 ** the shellCommands or macroCommands resource, and for the menu item.
2245 static void generateAcceleratorString(char *text
, unsigned int modifiers
,
2248 char *shiftStr
= "", *ctrlStr
= "", *altStr
= "";
2249 char *mod2Str
= "", *mod3Str
= "", *mod4Str
= "", *mod5Str
= "";
2251 Modifiers numLockMask
= GetNumLockModMask(TheDisplay
);
2253 /* if there's no accelerator, generate an empty string */
2254 if (keysym
== NoSymbol
) {
2260 /* Translate the modifiers into strings.
2261 Lock and NumLock are always ignored (see util/misc.c),
2262 so we don't display them either. */
2263 if (modifiers
& ShiftMask
)
2264 shiftStr
= "Shift+";
2265 if (modifiers
& ControlMask
)
2267 if (modifiers
& Mod1Mask
)
2269 if ((modifiers
& Mod2Mask
) && (Mod2Mask
!= numLockMask
))
2271 if ((modifiers
& Mod3Mask
) && (Mod3Mask
!= numLockMask
))
2273 if ((modifiers
& Mod4Mask
) && (Mod4Mask
!= numLockMask
))
2275 if ((modifiers
& Mod5Mask
) && (Mod5Mask
!= numLockMask
))
2278 /* for a consistent look to the accelerator names in the menus,
2279 capitalize the first letter of the keysym */
2280 strcpy(keyName
, XKeysymToString(keysym
));
2281 *keyName
= toupper(*keyName
);
2283 /* concatenate the strings together */
2284 sprintf(text
, "%s%s%s%s%s%s%s%s", shiftStr
, ctrlStr
, altStr
,
2285 mod2Str
, mod3Str
, mod4Str
, mod5Str
, keyName
);
2289 ** Create a translation table event description string for the menu
2290 ** XmNaccelerator resource.
2292 static void genAccelEventName(char *text
, unsigned int modifiers
,
2295 char *shiftStr
= "", *lockStr
= "", *ctrlStr
= "", *altStr
= "";
2296 char *mod2Str
= "", *mod3Str
= "", *mod4Str
= "", *mod5Str
= "";
2298 /* if there's no accelerator, generate an empty string */
2299 if (keysym
== NoSymbol
) {
2304 /* translate the modifiers into strings */
2305 if (modifiers
& ShiftMask
)
2306 shiftStr
= "Shift ";
2307 if (modifiers
& LockMask
)
2309 if (modifiers
& ControlMask
)
2311 if (modifiers
& Mod1Mask
)
2313 if (modifiers
& Mod2Mask
)
2315 if (modifiers
& Mod3Mask
)
2317 if (modifiers
& Mod4Mask
)
2319 if (modifiers
& Mod5Mask
)
2322 /* put the modifiers together with the key name */
2323 sprintf(text
, "%s%s%s%s%s%s%s%s<Key>%s",
2324 shiftStr
, lockStr
, ctrlStr
, altStr
,
2325 mod2Str
, mod3Str
, mod4Str
, mod5Str
,
2326 XKeysymToString(keysym
));
2330 ** Read an accelerator name and put it into the form of a modifier mask
2331 ** and a KeySym code. Returns false if string can't be read
2332 ** ... does not handle whitespace in string (look at scanf)
2334 static int parseAcceleratorString(const char *string
, unsigned int *modifiers
,
2337 int i
, nFields
, inputLength
= strlen(string
);
2338 char fields
[10][MAX_ACCEL_LEN
];
2340 /* a blank field means no accelerator */
2341 if (inputLength
== 0) {
2347 /* limit the string length so no field strings will overflow */
2348 if (inputLength
> MAX_ACCEL_LEN
)
2351 /* divide the input into '+' separated fields */
2352 nFields
= sscanf(string
, "%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]",
2353 fields
[0], fields
[1], fields
[2], fields
[3], fields
[4], fields
[5],
2354 fields
[6], fields
[7], fields
[8], fields
[9]);
2358 /* get the key name from the last field and translate it to a keysym.
2359 If the name is capitalized, try it lowercase as well, since some
2360 of the keysyms are "prettied up" by generateAcceleratorString */
2361 *keysym
= XStringToKeysym(fields
[nFields
-1]);
2362 if (*keysym
== NoSymbol
) {
2363 *fields
[nFields
-1] = tolower(*fields
[nFields
-1]);
2364 *keysym
= XStringToKeysym(fields
[nFields
-1]);
2365 if (*keysym
== NoSymbol
)
2369 /* parse the modifier names from the rest of the fields */
2371 for (i
=0; i
<nFields
-1; i
++) {
2372 if (!strcmp(fields
[i
], "Shift"))
2373 *modifiers
|= ShiftMask
;
2374 else if (!strcmp(fields
[i
], "Lock"))
2375 *modifiers
|= LockMask
;
2376 else if (!strcmp(fields
[i
], "Ctrl"))
2377 *modifiers
|= ControlMask
;
2378 /* comparision with "Alt" for compatibility with old .nedit files*/
2379 else if (!strcmp(fields
[i
], "Alt"))
2380 *modifiers
|= Mod1Mask
;
2381 else if (!strcmp(fields
[i
], "Mod2"))
2382 *modifiers
|= Mod2Mask
;
2383 else if (!strcmp(fields
[i
], "Mod3"))
2384 *modifiers
|= Mod3Mask
;
2385 else if (!strcmp(fields
[i
], "Mod4"))
2386 *modifiers
|= Mod4Mask
;
2387 else if (!strcmp(fields
[i
], "Mod5"))
2388 *modifiers
|= Mod5Mask
;
2393 /* all fields successfully parsed */
2398 ** Scan text from "*inPtr" to the end of macro input (matching brace),
2399 ** advancing inPtr, and return macro text as function return value.
2401 ** This is kind of wastefull in that it throws away the compiled macro,
2402 ** to be re-generated from the text as needed, but compile time is
2403 ** negligible for most macros.
2405 static char *copyMacroToEnd(char **inPtr
)
2407 char *retStr
, *errMsg
, *stoppedAt
, *p
, *retPtr
;
2410 /* Skip over whitespace to find make sure there's a beginning brace
2411 to anchor the parse (if not, it will take the whole file) */
2412 *inPtr
+= strspn(*inPtr
, " \t\n");
2413 if (**inPtr
!= '{') {
2414 ParseError(NULL
, *inPtr
, *inPtr
-1, "macro menu item", "expecting '{'");
2418 /* Parse the input */
2419 prog
= ParseMacro(*inPtr
, &errMsg
, &stoppedAt
);
2421 ParseError(NULL
, *inPtr
, stoppedAt
, "macro menu item", errMsg
);
2426 /* Copy and return the body of the macro, stripping outer braces and
2427 extra leading tabs added by the writer routine */
2429 *inPtr
+= strspn(*inPtr
, " \t");
2430 if (**inPtr
== '\n') (*inPtr
)++;
2431 if (**inPtr
== '\t') (*inPtr
)++;
2432 if (**inPtr
== '\t') (*inPtr
)++;
2433 retPtr
= retStr
= XtMalloc(stoppedAt
- *inPtr
+ 1);
2434 for (p
= *inPtr
; p
< stoppedAt
- 1; p
++) {
2435 if (!strncmp(p
, "\n\t\t", 3)) {
2441 if (*(retPtr
-1) == '\t') retPtr
--;
2448 ** If "*string" is not terminated with a newline character, reallocate the
2449 ** string and add one. (The macro language requires newline terminators for
2450 ** statements, but the text widget doesn't force it like the NEdit text buffer
2451 ** does, so this might avoid some confusion.)
2453 static void addTerminatingNewline(char **string
)
2458 length
= strlen(*string
);
2459 if ((*string
)[length
-1] != '\n') {
2460 newString
= XtMalloc(length
+ 2);
2461 strcpy(newString
, *string
);
2462 newString
[length
] = '\n';
2463 newString
[length
+1] = '\0';
2465 *string
= newString
;