1 static const char CVSID
[] = "$Id: userCmds.c,v 1.39 2004/01/16 09:18:28 edg 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
);
1262 ** Find the widget corresponding to a hierarchical menu name (a>b>c...)
1264 static Widget
findInMenuTree(menuTreeItem
*menuTree
, int nTreeEntries
,
1265 const char *hierName
)
1269 for (i
=0; i
<nTreeEntries
; i
++)
1270 if (!strcmp(hierName
, menuTree
[i
].name
))
1271 return menuTree
[i
].menuPane
;
1275 static char *copySubstring(const char *string
, int length
)
1277 char *retStr
= XtMalloc(length
+ 1);
1279 strncpy(retStr
, string
, length
);
1280 retStr
[length
] = '\0';
1285 ** Look for at signs (@) in the string menuItemName, and match them
1286 ** against the current language mode. If there are no @ signs, just
1287 ** return an allocated copy of menuItemName. If there are @ signs, match
1288 ** the following text against languageMode, and return NULL if none match,
1289 ** or an allocated copy of menuItemName stripped of @ parts. If the
1290 ** language name is "*", sets isDefaultLM to true.
1292 static char *findStripLanguageMode(const char *menuItemName
, int languageMode
,
1295 char *atPtr
, *firstAtPtr
, *endPtr
;
1298 atPtr
= firstAtPtr
= strchr(menuItemName
, '@');
1299 *isDefaultLM
= False
;
1302 return XtNewString(menuItemName
);
1304 if (!strcmp(atPtr
+1, "*")) {
1305 /* only language is "*": this is for all but language specific macros */
1306 *isDefaultLM
= True
;
1307 return copySubstring(menuItemName
, firstAtPtr
-menuItemName
);
1309 if (languageMode
== PLAIN_LANGUAGE_MODE
)
1312 for(endPtr
=atPtr
+1; isalnum((unsigned char)*endPtr
) || *endPtr
=='_' ||
1313 *endPtr
=='-' || *endPtr
==' ' || *endPtr
=='+' || *endPtr
=='$' ||
1314 *endPtr
=='#'; endPtr
++);
1315 lmNameLen
= endPtr
-atPtr
-1;
1316 if (!strncmp(LanguageModeName(languageMode
), atPtr
+1, lmNameLen
) &&
1317 LanguageModeName(languageMode
)[lmNameLen
] == '\0')
1318 return copySubstring(menuItemName
, firstAtPtr
-menuItemName
);
1319 atPtr
= strchr(atPtr
+1, '@');
1325 static Widget
createUserMenuItem(Widget menuPane
, char *name
, menuItemRec
*f
,
1326 int index
, XtCallbackProc cbRtn
, XtPointer cbArg
)
1329 char accText
[MAX_ACCEL_LEN
], accKeys
[MAX_ACCEL_LEN
+5];
1332 generateAcceleratorString(accText
, f
->modifiers
, f
->keysym
);
1333 genAccelEventName(accKeys
, f
->modifiers
, f
->keysym
);
1334 st1
=XmStringCreateSimple(name
);
1335 st2
=XmStringCreateSimple(accText
);
1336 btn
= XtVaCreateManagedWidget("cmd", xmPushButtonWidgetClass
, menuPane
,
1337 XmNlabelString
, st1
,
1338 XmNacceleratorText
, st2
,
1339 XmNaccelerator
, accKeys
,
1340 XmNmnemonic
, f
->mnemonic
,
1341 XmNuserData
, index
+10, NULL
);
1342 XtAddCallback(btn
, XmNactivateCallback
, cbRtn
, cbArg
);
1349 ** Add a user-defined sub-menu to an established pull-down menu, marking
1350 ** it's userData field with TEMPORARY_MENU_ITEM so it can be found and
1351 ** removed later if the menu is redefined. Returns the menu pane of the
1354 static Widget
createUserSubMenu(Widget parent
, char *label
)
1358 static Arg args
[1] = {{XmNuserData
, (XtArgVal
)TEMPORARY_MENU_ITEM
}};
1360 menu
= CreatePulldownMenu(parent
, "userPulldown", args
, 1);
1361 XtVaCreateManagedWidget("userCascade", xmCascadeButtonWidgetClass
, parent
,
1362 XmNlabelString
, st1
=XmStringCreateSimple(label
),
1363 XmNsubMenuId
, menu
, XmNuserData
, TEMPORARY_MENU_ITEM
, NULL
);
1368 static void removeMenuItems(Widget menuPane
)
1370 WidgetList items
, itemList
;
1376 /* Fetch the list of children from the menu pane, and make a copy
1377 (because the widget alters this list as you delete widgets) */
1378 XtVaGetValues(menuPane
, XmNchildren
, &itemList
, XmNnumChildren
, &nItems
,
1380 items
= (WidgetList
)XtMalloc(sizeof(Widget
) * nItems
);
1381 memcpy(items
, itemList
, sizeof(Widget
) * nItems
);
1383 /* Delete all of the widgets not marked as PERMANENT_MENU_ITEM */
1384 for (n
=0; n
<(int)nItems
; n
++) {
1385 XtVaGetValues(items
[n
], XmNuserData
, &userData
, NULL
);
1386 if (userData
!= (XtPointer
)PERMANENT_MENU_ITEM
) {
1387 if (XtClass(items
[n
]) == xmCascadeButtonWidgetClass
) {
1388 XtVaGetValues(items
[n
], XmNsubMenuId
, &subMenuID
, NULL
);
1389 removeMenuItems(subMenuID
);
1390 #if XmVersion < 2000 /* Skipping this creates a memory and server resource
1391 leak (though both are reclaimed on window closing). In
1392 Motif 2.0 (and beyond?) there is a potential crash during
1393 phase 2 widget destruction in "SetCascadeField", and in
1394 Motif 1.2 there are free-memory reads. I would really like
1395 to be able to destroy this. */
1396 XtDestroyWidget(subMenuID
);
1398 } else /* remove accel. before destroy or lose it forever */
1399 XtVaSetValues(items
[n
], XmNaccelerator
, NULL
, NULL
);
1400 /* unmanaging before destroying stops parent from displaying */
1401 XtUnmanageChild(items
[n
]);
1402 XtDestroyWidget(items
[n
]);
1405 XtFree((char *)items
);
1408 static void dismissCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1410 userCmdDialog
*ucd
= (userCmdDialog
*)clientData
;
1412 /* Mark that there's no longer a (macro, bg, or shell) dialog up */
1413 if (ucd
->dialogType
== SHELL_CMDS
)
1414 ShellCmdDialog
= NULL
;
1415 else if (ucd
->dialogType
== MACRO_CMDS
)
1416 MacroCmdDialog
= NULL
;
1418 BGMenuCmdDialog
= NULL
;
1420 /* pop down and destroy the dialog (memory for ucd is freed in the
1421 destroy callback) */
1422 XtDestroyWidget(ucd
->dlogShell
);
1425 static void okCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1427 userCmdDialog
*ucd
= (userCmdDialog
*)clientData
;
1429 /* Read the dialog fields, and update the menus */
1430 if (!applyDialogChanges(ucd
))
1433 /* Mark that there's no longer a (macro, bg, or shell) dialog up */
1434 if (ucd
->dialogType
== SHELL_CMDS
)
1435 ShellCmdDialog
= NULL
;
1436 else if (ucd
->dialogType
== MACRO_CMDS
)
1437 MacroCmdDialog
= NULL
;
1439 BGMenuCmdDialog
= NULL
;
1441 /* pop down and destroy the dialog (memory for ucd is freed in the
1442 destroy callback) */
1443 XtDestroyWidget(ucd
->dlogShell
);
1446 static void applyCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1448 applyDialogChanges((userCmdDialog
*)clientData
);
1451 static void checkCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1453 userCmdDialog
*ucd
= (userCmdDialog
*)clientData
;
1455 if (checkMacro(ucd
))
1457 DialogF(DF_INF
, ucd
->dlogShell
, 1, "Macro",
1458 "Macro compiled without error", "Dismiss");
1462 static int checkMacro(userCmdDialog
*ucd
)
1466 f
= readDialogFields(ucd
, False
);
1469 if (!checkMacroText(f
->cmd
, ucd
->dlogShell
, ucd
->cmdTextW
)) {
1476 static int checkMacroText(char *macro
, Widget errorParent
, Widget errFocus
)
1479 char *errMsg
, *stoppedAt
;
1481 prog
= ParseMacro(macro
, &errMsg
, &stoppedAt
);
1483 if (errorParent
!= NULL
) {
1484 ParseError(errorParent
, macro
, stoppedAt
, "macro", errMsg
);
1485 XmTextSetInsertionPosition(errFocus
, stoppedAt
- macro
);
1486 XmProcessTraversal(errFocus
, XmTRAVERSE_CURRENT
);
1491 if (*stoppedAt
!= '\0') {
1492 if (errorParent
!= NULL
) {
1493 ParseError(errorParent
, macro
, stoppedAt
,"macro","syntax error");
1494 XmTextSetInsertionPosition(errFocus
, stoppedAt
- macro
);
1495 XmProcessTraversal(errFocus
, XmTRAVERSE_CURRENT
);
1502 static int applyDialogChanges(userCmdDialog
*ucd
)
1506 /* Get the current contents of the dialog fields */
1507 if (!UpdateManagedList(ucd
->managedList
, True
))
1510 /* Test compile the macro */
1511 if (ucd
->dialogType
== MACRO_CMDS
)
1512 if (!checkMacro(ucd
))
1515 /* Update the menu information */
1516 if (ucd
->dialogType
== SHELL_CMDS
) {
1517 for (i
=0; i
<NShellMenuItems
; i
++)
1518 freeMenuItemRec(ShellMenuItems
[i
]);
1519 for (i
=0; i
<ucd
->nMenuItems
; i
++)
1520 ShellMenuItems
[i
] = copyMenuItemRec(ucd
->menuItemsList
[i
]);
1521 NShellMenuItems
= ucd
->nMenuItems
;
1522 } else if (ucd
->dialogType
== MACRO_CMDS
) {
1523 for (i
=0; i
<NMacroMenuItems
; i
++)
1524 freeMenuItemRec(MacroMenuItems
[i
]);
1525 for (i
=0; i
<ucd
->nMenuItems
; i
++)
1526 MacroMenuItems
[i
] = copyMenuItemRec(ucd
->menuItemsList
[i
]);
1527 NMacroMenuItems
= ucd
->nMenuItems
;
1528 } else { /* BG_MENU_CMDS */
1529 for (i
=0; i
<NBGMenuItems
; i
++)
1530 freeMenuItemRec(BGMenuItems
[i
]);
1531 for (i
=0; i
<ucd
->nMenuItems
; i
++)
1532 BGMenuItems
[i
] = copyMenuItemRec(ucd
->menuItemsList
[i
]);
1533 NBGMenuItems
= ucd
->nMenuItems
;
1536 /* Update the menus themselves in all of the NEdit windows */
1537 updateMenus(ucd
->dialogType
);
1539 /* Note that preferences have been changed */
1544 static void pasteReplayCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1546 userCmdDialog
*ucd
= (userCmdDialog
*)clientData
;
1548 if (GetReplayMacro() == NULL
)
1551 XmTextInsert(ucd
->cmdTextW
, XmTextGetInsertionPosition(ucd
->cmdTextW
),
1555 static void destroyCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1557 userCmdDialog
*ucd
= (userCmdDialog
*)clientData
;
1560 for (i
=0; i
<ucd
->nMenuItems
; i
++)
1561 freeMenuItemRec(ucd
->menuItemsList
[i
]);
1562 XtFree((char *)ucd
->menuItemsList
);
1563 XtFree((char *)ucd
);
1566 static void accFocusCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1568 userCmdDialog
*ucd
= (userCmdDialog
*)clientData
;
1570 RemoveDialogMnemonicHandler(XtParent(ucd
->accTextW
));
1573 static void accLoseFocusCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1575 userCmdDialog
*ucd
= (userCmdDialog
*)clientData
;
1577 AddDialogMnemonicHandler(XtParent(ucd
->accTextW
), FALSE
);
1580 static void accKeyCB(Widget w
, XtPointer clientData
, XKeyEvent
*event
)
1582 userCmdDialog
*ucd
= (userCmdDialog
*)clientData
;
1583 KeySym keysym
= XLookupKeysym(event
, 0);
1584 char outStr
[MAX_ACCEL_LEN
];
1586 /* Accept only real keys, not modifiers alone */
1587 if (IsModifierKey(keysym
))
1590 /* Tab key means go to the next field, don't enter */
1591 if (keysym
== XK_Tab
)
1594 /* Beep and return if the modifiers are buttons or ones we don't support */
1595 if (event
->state
& ~(ShiftMask
| LockMask
| ControlMask
| Mod1Mask
|
1596 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
)) {
1597 XBell(TheDisplay
, 0);
1601 /* Delete or backspace clears field */
1602 if (keysym
== XK_Delete
|| keysym
== XK_BackSpace
) {
1603 XmTextSetString(ucd
->accTextW
, "");
1607 /* generate the string to use in the dialog field */
1608 generateAcceleratorString(outStr
, event
->state
, keysym
);
1610 /* Reject single character accelerators (a very simple way to eliminate
1611 un-modified letters and numbers) The goal is give users a clue that
1612 they're supposed to type the actual keys, not the name. This scheme
1613 is not rigorous and still allows accelerators like Comma. */
1614 if (strlen(outStr
) == 1) {
1615 XBell(TheDisplay
, 0);
1619 /* fill in the accelerator field in the dialog */
1620 XmTextSetString(ucd
->accTextW
, outStr
);
1623 static void sameOutCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1625 XtSetSensitive(((userCmdDialog
*)clientData
)->repInpBtn
,
1626 XmToggleButtonGetState(w
));
1629 static void shellMenuCB(Widget w
, WindowInfo
*window
, XtPointer callData
)
1635 window
= WidgetToWindow(MENU_WIDGET(w
));
1637 /* get the index of the shell command and verify that it's in range */
1638 XtVaGetValues(w
, XmNuserData
, &userData
, NULL
);
1639 index
= (int)userData
- 10;
1640 if (index
<0 || index
>= NShellMenuItems
)
1643 params
[0] = ShellMenuItems
[index
]->name
;
1644 XtCallActionProc(window
->lastFocus
, "shell_menu_command",
1645 ((XmAnyCallbackStruct
*)callData
)->event
, params
, 1);
1648 static void macroMenuCB(Widget w
, WindowInfo
*window
, XtPointer callData
)
1654 window
= WidgetToWindow(MENU_WIDGET(w
));
1656 /* Don't allow users to execute a macro command from the menu (or accel)
1657 if there's already a macro command executing. NEdit can't handle
1658 running multiple, independent uncoordinated, macros in the same
1659 window. Macros may invoke macro menu commands recursively via the
1660 macro_menu_command action proc, which is important for being able to
1661 repeat any operation, and to embed macros within eachother at any
1662 level, however, a call here with a macro running means that THE USER
1663 is explicitly invoking another macro via the menu or an accelerator. */
1664 if (window
->macroCmdData
!= NULL
) {
1665 XBell(TheDisplay
, 0);
1669 /* get the index of the macro command and verify that it's in range */
1670 XtVaGetValues(w
, XmNuserData
, &userData
, NULL
);
1671 index
= (int)userData
- 10;
1672 if (index
<0 || index
>= NMacroMenuItems
)
1675 params
[0] = MacroMenuItems
[index
]->name
;
1676 XtCallActionProc(window
->lastFocus
, "macro_menu_command",
1677 ((XmAnyCallbackStruct
*)callData
)->event
, params
, 1);
1680 static void bgMenuCB(Widget w
, WindowInfo
*window
, XtPointer callData
)
1686 window
= WidgetToWindow(MENU_WIDGET(w
));
1688 /* Same remark as for macro menu commands (see above). */
1689 if (window
->macroCmdData
!= NULL
) {
1690 XBell(TheDisplay
, 0);
1694 /* get the index of the macro command and verify that it's in range */
1695 XtVaGetValues(w
, XmNuserData
, &userData
, NULL
);
1696 index
= (int)userData
- 10;
1697 if (index
<0 || index
>= NBGMenuItems
)
1700 params
[0] = BGMenuItems
[index
]->name
;
1701 XtCallActionProc(window
->lastFocus
, "bg_menu_command",
1702 ((XmAnyCallbackStruct
*)callData
)->event
, params
, 1);
1706 ** Update the name, accelerator, mnemonic, and command fields in the shell
1707 ** command or macro dialog to agree with the currently selected item in the
1710 static void updateDialogFields(menuItemRec
*f
, userCmdDialog
*ucd
)
1712 char mneString
[2], accString
[MAX_ACCEL_LEN
];
1714 /* fill in the name, accelerator, mnemonic, and command fields of the
1715 dialog for the newly selected item, or blank them if "New" is selected */
1717 XmTextSetString(ucd
->nameTextW
, "");
1718 XmTextSetString(ucd
->cmdTextW
, "");
1719 XmTextSetString(ucd
->accTextW
, "");
1720 XmTextSetString(ucd
->mneTextW
, "");
1721 if (ucd
->dialogType
== SHELL_CMDS
) {
1722 RadioButtonChangeState(ucd
->selInpBtn
, True
, True
);
1723 RadioButtonChangeState(ucd
->sameOutBtn
, True
, True
);
1724 RadioButtonChangeState(ucd
->repInpBtn
, False
, False
);
1725 XtSetSensitive(ucd
->repInpBtn
, True
);
1726 RadioButtonChangeState(ucd
->saveFirstBtn
, False
, False
);
1727 RadioButtonChangeState(ucd
->loadAfterBtn
, False
, False
);
1730 mneString
[0] = f
->mnemonic
;
1731 mneString
[1] = '\0';
1732 generateAcceleratorString(accString
, f
->modifiers
, f
->keysym
);
1733 XmTextSetString(ucd
->nameTextW
, f
->name
);
1734 XmTextSetString(ucd
->cmdTextW
, f
->cmd
);
1735 XmTextSetString(ucd
->accTextW
, accString
);
1736 XmTextSetString(ucd
->mneTextW
, mneString
);
1737 RadioButtonChangeState(ucd
->selInpBtn
, f
->input
==FROM_SELECTION
, False
);
1738 if (ucd
->dialogType
== SHELL_CMDS
) {
1739 RadioButtonChangeState(ucd
->winInpBtn
, f
->input
== FROM_WINDOW
,
1741 RadioButtonChangeState(ucd
->eitherInpBtn
, f
->input
== FROM_EITHER
,
1743 RadioButtonChangeState(ucd
->noInpBtn
, f
->input
== FROM_NONE
,
1745 RadioButtonChangeState(ucd
->sameOutBtn
, f
->output
==TO_SAME_WINDOW
,
1747 RadioButtonChangeState(ucd
->winOutBtn
, f
->output
==TO_NEW_WINDOW
,
1749 RadioButtonChangeState(ucd
->dlogOutBtn
, f
->output
==TO_DIALOG
,
1751 RadioButtonChangeState(ucd
->repInpBtn
, f
->repInput
, False
);
1752 XtSetSensitive(ucd
->repInpBtn
, f
->output
==TO_SAME_WINDOW
);
1753 RadioButtonChangeState(ucd
->saveFirstBtn
, f
->saveFirst
, False
);
1754 RadioButtonChangeState(ucd
->loadAfterBtn
, f
->loadAfter
, False
);
1760 ** Read the name, accelerator, mnemonic, and command fields from the shell or
1761 ** macro commands dialog into a newly allocated menuItemRec. Returns a
1762 ** pointer to the new menuItemRec structure as the function value, or NULL on
1765 static menuItemRec
*readDialogFields(userCmdDialog
*ucd
, int silent
)
1767 char *nameText
, *cmdText
, *mneText
, *accText
;
1770 nameText
= XmTextGetString(ucd
->nameTextW
);
1771 if (*nameText
== '\0')
1775 DialogF(DF_WARN
, ucd
->dlogShell
, 1, "Menu Entry",
1776 "Please specify a name\nfor the menu item", "Dismiss");
1777 XmProcessTraversal(ucd
->nameTextW
, XmTRAVERSE_CURRENT
);
1783 if (strchr(nameText
, ':'))
1787 DialogF(DF_WARN
, ucd
->dlogShell
, 1, "Menu Entry",
1788 "Menu item names may not\ncontain colon (:) characters",
1790 XmProcessTraversal(ucd
->nameTextW
, XmTRAVERSE_CURRENT
);
1796 cmdText
= XmTextGetString(ucd
->cmdTextW
);
1797 if (cmdText
== NULL
|| *cmdText
== '\0')
1801 DialogF(DF_WARN
, ucd
->dlogShell
, 1, "Command to Execute",
1802 "Please specify %s to execute", "Dismiss",
1803 ucd
->dialogType
== SHELL_CMDS
1805 : "macro command(s)");
1806 XmProcessTraversal(ucd
->cmdTextW
, XmTRAVERSE_CURRENT
);
1817 if (ucd
->dialogType
== MACRO_CMDS
|| ucd
->dialogType
== BG_MENU_CMDS
) {
1818 addTerminatingNewline(&cmdText
);
1819 if (!checkMacroText(cmdText
, silent
? NULL
: ucd
->dlogShell
,
1826 f
= (menuItemRec
*)XtMalloc(sizeof(menuItemRec
));
1829 if ((mneText
= XmTextGetString(ucd
->mneTextW
)) != NULL
) {
1830 f
->mnemonic
= mneText
==NULL
? '\0' : mneText
[0];
1832 if (f
->mnemonic
== ':') /* colons mess up string parsing */
1835 if ((accText
= XmTextGetString(ucd
->accTextW
)) != NULL
) {
1836 parseAcceleratorString(accText
, &f
->modifiers
, &f
->keysym
);
1839 if (ucd
->dialogType
== SHELL_CMDS
) {
1840 if (XmToggleButtonGetState(ucd
->selInpBtn
))
1841 f
->input
= FROM_SELECTION
;
1842 else if (XmToggleButtonGetState(ucd
->winInpBtn
))
1843 f
->input
= FROM_WINDOW
;
1844 else if (XmToggleButtonGetState(ucd
->eitherInpBtn
))
1845 f
->input
= FROM_EITHER
;
1847 f
->input
= FROM_NONE
;
1848 if (XmToggleButtonGetState(ucd
->winOutBtn
))
1849 f
->output
= TO_NEW_WINDOW
;
1850 else if (XmToggleButtonGetState(ucd
->dlogOutBtn
))
1851 f
->output
= TO_DIALOG
;
1853 f
->output
= TO_SAME_WINDOW
;
1854 f
->repInput
= XmToggleButtonGetState(ucd
->repInpBtn
);
1855 f
->saveFirst
= XmToggleButtonGetState(ucd
->saveFirstBtn
);
1856 f
->loadAfter
= XmToggleButtonGetState(ucd
->loadAfterBtn
);
1858 f
->input
= XmToggleButtonGetState(ucd
->selInpBtn
) ? FROM_SELECTION
:
1860 f
->output
= TO_SAME_WINDOW
;
1861 f
->repInput
= False
;
1862 f
->saveFirst
= False
;
1863 f
->loadAfter
= False
;
1869 ** Copy a menu item record, and its associated memory
1871 static menuItemRec
*copyMenuItemRec(menuItemRec
*item
)
1873 menuItemRec
*newItem
;
1875 newItem
= (menuItemRec
*)XtMalloc(sizeof(menuItemRec
));
1877 newItem
->name
= XtMalloc(strlen(item
->name
)+1);
1878 strcpy(newItem
->name
, item
->name
);
1879 newItem
->cmd
= XtMalloc(strlen(item
->cmd
)+1);
1880 strcpy(newItem
->cmd
, item
->cmd
);
1885 ** Free a menu item record, and its associated memory
1887 static void freeMenuItemRec(menuItemRec
*item
)
1891 XtFree((char *)item
);
1895 ** Callbacks for managed-list operations
1897 static void *getDialogDataCB(void *oldItem
, int explicitRequest
, int *abort
,
1900 userCmdDialog
*ucd
= (userCmdDialog
*)cbArg
;
1901 menuItemRec
*currentFields
;
1903 /* If the dialog is currently displaying the "new" entry and the
1904 fields are empty, that's just fine */
1905 if (oldItem
== NULL
&& dialogFieldsAreEmpty(ucd
))
1908 /* If there are no problems reading the data, just return it */
1909 currentFields
= readDialogFields(ucd
, True
);
1910 if (currentFields
!= NULL
)
1911 return (void *)currentFields
;
1913 /* If user might not be expecting fields to be read, give more warning */
1914 if (!explicitRequest
)
1916 if (DialogF(DF_WARN
, ucd
->dlogShell
, 2, "Discard Entry",
1917 "Discard incomplete entry\nfor current menu item?", "Keep",
1920 return oldItem
== NULL
1922 : (void *)copyMenuItemRec((menuItemRec
*)oldItem
);
1926 /* Do readDialogFields again without "silent" mode to display warning(s) */
1927 readDialogFields(ucd
, False
);
1933 static void setDialogDataCB(void *item
, void *cbArg
)
1935 updateDialogFields((menuItemRec
*)item
, (userCmdDialog
*)cbArg
);
1938 static int dialogFieldsAreEmpty(userCmdDialog
*ucd
)
1940 return TextWidgetIsBlank(ucd
->nameTextW
) &&
1941 TextWidgetIsBlank(ucd
->cmdTextW
) &&
1942 TextWidgetIsBlank(ucd
->accTextW
) &&
1943 TextWidgetIsBlank(ucd
->mneTextW
) &&
1944 (ucd
->dialogType
!= SHELL_CMDS
|| (
1945 XmToggleButtonGetState(ucd
->selInpBtn
) &&
1946 XmToggleButtonGetState(ucd
->sameOutBtn
) &&
1947 !XmToggleButtonGetState(ucd
->repInpBtn
) &&
1948 !XmToggleButtonGetState(ucd
->saveFirstBtn
) &&
1949 !XmToggleButtonGetState(ucd
->loadAfterBtn
)));
1952 static void freeItemCB(void *item
)
1954 freeMenuItemRec((menuItemRec
*)item
);
1958 ** Gut a text widget of it's ability to process input
1960 static void disableTextW(Widget textW
)
1962 static XtTranslations emptyTable
= NULL
;
1963 static char *emptyTranslations
= "\
1964 <EnterWindow>: enter()\n\
1965 <Btn1Down>: grab-focus()\n\
1966 <Btn1Motion>: extend-adjust()\n\
1967 <Btn1Up>: extend-end()\n\
1968 Shift<Key>Tab: prev-tab-group()\n\
1969 Ctrl<Key>Tab: next-tab-group()\n\
1970 <Key>Tab: next-tab-group()\n\
1971 <LeaveWindow>: leave()\n\
1972 <FocusIn>: focusIn()\n\
1973 <FocusOut>: focusOut()\n\
1974 <Unmap>: unmap()\n";
1976 /* replace the translation table with the slimmed down one above */
1977 if (emptyTable
== NULL
)
1978 emptyTable
= XtParseTranslationTable(emptyTranslations
);
1979 XtVaSetValues(textW
, XmNtranslations
, emptyTable
, NULL
);
1982 static char *writeMenuItemString(menuItemRec
**menuItems
, int nItems
,
1985 char *outStr
, *outPtr
, *c
, accStr
[MAX_ACCEL_LEN
];
1989 /* determine the max. amount of memory needed for the returned string
1990 and allocate a buffer for composing the string */
1992 for (i
=0; i
<nItems
; i
++) {
1994 generateAcceleratorString(accStr
, f
->modifiers
, f
->keysym
);
1995 length
+= strlen(f
->name
) * 2; /* allow for \n & \\ expansions */
1996 length
+= strlen(accStr
);
1997 length
+= strlen(f
->cmd
) * 6; /* allow for \n & \\ expansions */
1998 length
+= 21; /* number of characters added below */
2000 length
++; /* terminating null */
2001 outStr
= XtMalloc(length
);
2003 /* write the string */
2007 for (i
=0; i
<nItems
; i
++) {
2009 generateAcceleratorString(accStr
, f
->modifiers
, f
->keysym
);
2011 for (c
=f
->name
; *c
!='\0'; ++c
) { /* Copy the command name */
2012 if (*c
== '\\') { /* changing backslashes to \\ */
2015 } else if (*c
== '\n') { /* changing newlines to \n */
2023 strcpy(outPtr
, accStr
);
2024 outPtr
+= strlen(accStr
);
2026 if (f
->mnemonic
!= '\0')
2027 *outPtr
++ = f
->mnemonic
;
2029 if (listType
== SHELL_CMDS
) {
2030 if (f
->input
== FROM_SELECTION
)
2032 else if (f
->input
== FROM_WINDOW
)
2034 else if (f
->input
== FROM_EITHER
)
2036 if (f
->output
== TO_DIALOG
)
2038 else if (f
->output
== TO_NEW_WINDOW
)
2048 if (f
->input
== FROM_SELECTION
)
2060 for (c
=f
->cmd
; *c
!='\0'; c
++) { /* Copy the command string, changing */
2061 if (*c
== '\\') { /* backslashes to double backslashes */
2062 *outPtr
++ = '\\'; /* and newlines to backslash-n's, */
2063 *outPtr
++ = '\\'; /* followed by real newlines and tab */
2064 } else if (*c
== '\n') {
2074 if (listType
== MACRO_CMDS
|| listType
== BG_MENU_CMDS
) {
2075 if (*(outPtr
-1) == '\t') outPtr
--;
2088 static int loadMenuItemString(char *inString
, menuItemRec
**menuItems
,
2089 int *nItems
, int listType
)
2093 char *inPtr
= inString
;
2094 char *nameStr
, accStr
[MAX_ACCEL_LEN
], mneChar
;
2096 unsigned int modifiers
;
2097 int i
, input
, output
, saveFirst
, loadAfter
, repInput
;
2098 int nameLen
, accLen
, mneLen
, cmdLen
;
2102 /* remove leading whitespace */
2103 while (*inPtr
== ' ' || *inPtr
== '\t')
2106 /* read name field */
2107 nameLen
= strcspn(inPtr
, ":");
2109 return parseError("no name field");
2110 nameStr
= XtMalloc(nameLen
+1);
2111 strncpy(nameStr
, inPtr
, nameLen
);
2112 nameStr
[nameLen
] = '\0';
2115 return parseError("end not expected");
2118 /* read accelerator field */
2119 accLen
= strcspn(inPtr
, ":");
2120 if (accLen
>= MAX_ACCEL_LEN
)
2121 return parseError("accelerator field too long");
2122 strncpy(accStr
, inPtr
, accLen
);
2123 accStr
[accLen
] = '\0';
2126 return parseError("end not expected");
2129 /* read menemonic field */
2130 mneLen
= strcspn(inPtr
, ":");
2132 return parseError("mnemonic field too long");
2139 return parseError("end not expected");
2141 /* read flags field */
2143 output
= TO_SAME_WINDOW
;
2147 for (; *inPtr
!= ':'; inPtr
++) {
2148 if (listType
== SHELL_CMDS
) {
2150 input
= FROM_SELECTION
;
2151 else if (*inPtr
== 'A')
2152 input
= FROM_WINDOW
;
2153 else if (*inPtr
== 'E')
2154 input
= FROM_EITHER
;
2155 else if (*inPtr
== 'W')
2156 output
= TO_NEW_WINDOW
;
2157 else if (*inPtr
== 'D')
2159 else if (*inPtr
== 'X')
2161 else if (*inPtr
== 'S')
2163 else if (*inPtr
== 'L')
2166 return parseError("unreadable flag field");
2169 input
= FROM_SELECTION
;
2171 return parseError("unreadable flag field");
2176 /* read command field */
2177 if (listType
== SHELL_CMDS
) {
2178 if (*inPtr
++ != '\n')
2179 return parseError("command must begin with newline");
2180 while (*inPtr
== ' ' || *inPtr
== '\t') /* leading whitespace */
2182 cmdLen
= strcspn(inPtr
, "\n");
2184 return parseError("shell command field is empty");
2185 cmdStr
= XtMalloc(cmdLen
+1);
2186 strncpy(cmdStr
, inPtr
, cmdLen
);
2187 cmdStr
[cmdLen
] = '\0';
2190 cmdStr
= copyMacroToEnd(&inPtr
);
2194 while (*inPtr
== ' ' || *inPtr
== '\t' || *inPtr
== '\n')
2195 inPtr
++; /* skip trailing whitespace & newline */
2197 /* parse the accelerator field */
2198 if (!parseAcceleratorString(accStr
, &modifiers
, &keysym
))
2199 return parseError("couldn't read accelerator field");
2201 /* create a menu item record */
2202 f
= (menuItemRec
*)XtMalloc(sizeof(menuItemRec
));
2205 f
->mnemonic
= mneChar
;
2206 f
->modifiers
= modifiers
;
2209 f
->repInput
= repInput
;
2210 f
->saveFirst
= saveFirst
;
2211 f
->loadAfter
= loadAfter
;
2214 /* add/replace menu record in the list */
2215 for (i
=0; i
< *nItems
; i
++) {
2216 if (!strcmp(menuItems
[i
]->name
, f
->name
)) {
2217 freeMenuItemRec(menuItems
[i
]);
2223 menuItems
[(*nItems
)++] = f
;
2225 /* end of string in proper place */
2231 static int parseError(const char *message
)
2233 fprintf(stderr
, "NEdit: Parse error in user defined menu item, %s\n",
2239 ** Create a text string representing an accelerator for the dialog,
2240 ** the shellCommands or macroCommands resource, and for the menu item.
2242 static void generateAcceleratorString(char *text
, unsigned int modifiers
,
2245 char *shiftStr
= "", *ctrlStr
= "", *altStr
= "";
2246 char *mod2Str
= "", *mod3Str
= "", *mod4Str
= "", *mod5Str
= "";
2248 Modifiers numLockMask
= GetNumLockModMask(TheDisplay
);
2250 /* if there's no accelerator, generate an empty string */
2251 if (keysym
== NoSymbol
) {
2257 /* Translate the modifiers into strings.
2258 Lock and NumLock are always ignored (see util/misc.c),
2259 so we don't display them either. */
2260 if (modifiers
& ShiftMask
)
2261 shiftStr
= "Shift+";
2262 if (modifiers
& ControlMask
)
2264 if (modifiers
& Mod1Mask
)
2266 if ((modifiers
& Mod2Mask
) && (Mod2Mask
!= numLockMask
))
2268 if ((modifiers
& Mod3Mask
) && (Mod3Mask
!= numLockMask
))
2270 if ((modifiers
& Mod4Mask
) && (Mod4Mask
!= numLockMask
))
2272 if ((modifiers
& Mod5Mask
) && (Mod5Mask
!= numLockMask
))
2275 /* for a consistent look to the accelerator names in the menus,
2276 capitalize the first letter of the keysym */
2277 strcpy(keyName
, XKeysymToString(keysym
));
2278 *keyName
= toupper(*keyName
);
2280 /* concatenate the strings together */
2281 sprintf(text
, "%s%s%s%s%s%s%s%s", shiftStr
, ctrlStr
, altStr
,
2282 mod2Str
, mod3Str
, mod4Str
, mod5Str
, keyName
);
2286 ** Create a translation table event description string for the menu
2287 ** XmNaccelerator resource.
2289 static void genAccelEventName(char *text
, unsigned int modifiers
,
2292 char *shiftStr
= "", *lockStr
= "", *ctrlStr
= "", *altStr
= "";
2293 char *mod2Str
= "", *mod3Str
= "", *mod4Str
= "", *mod5Str
= "";
2295 /* if there's no accelerator, generate an empty string */
2296 if (keysym
== NoSymbol
) {
2301 /* translate the modifiers into strings */
2302 if (modifiers
& ShiftMask
)
2303 shiftStr
= "Shift ";
2304 if (modifiers
& LockMask
)
2306 if (modifiers
& ControlMask
)
2308 if (modifiers
& Mod1Mask
)
2310 if (modifiers
& Mod2Mask
)
2312 if (modifiers
& Mod3Mask
)
2314 if (modifiers
& Mod4Mask
)
2316 if (modifiers
& Mod5Mask
)
2319 /* put the modifiers together with the key name */
2320 sprintf(text
, "%s%s%s%s%s%s%s%s<Key>%s",
2321 shiftStr
, lockStr
, ctrlStr
, altStr
,
2322 mod2Str
, mod3Str
, mod4Str
, mod5Str
,
2323 XKeysymToString(keysym
));
2327 ** Read an accelerator name and put it into the form of a modifier mask
2328 ** and a KeySym code. Returns false if string can't be read
2329 ** ... does not handle whitespace in string (look at scanf)
2331 static int parseAcceleratorString(const char *string
, unsigned int *modifiers
,
2334 int i
, nFields
, inputLength
= strlen(string
);
2335 char fields
[10][MAX_ACCEL_LEN
];
2337 /* a blank field means no accelerator */
2338 if (inputLength
== 0) {
2344 /* limit the string length so no field strings will overflow */
2345 if (inputLength
> MAX_ACCEL_LEN
)
2348 /* divide the input into '+' separated fields */
2349 nFields
= sscanf(string
, "%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]",
2350 fields
[0], fields
[1], fields
[2], fields
[3], fields
[4], fields
[5],
2351 fields
[6], fields
[7], fields
[8], fields
[9]);
2355 /* get the key name from the last field and translate it to a keysym.
2356 If the name is capitalized, try it lowercase as well, since some
2357 of the keysyms are "prettied up" by generateAcceleratorString */
2358 *keysym
= XStringToKeysym(fields
[nFields
-1]);
2359 if (*keysym
== NoSymbol
) {
2360 *fields
[nFields
-1] = tolower(*fields
[nFields
-1]);
2361 *keysym
= XStringToKeysym(fields
[nFields
-1]);
2362 if (*keysym
== NoSymbol
)
2366 /* parse the modifier names from the rest of the fields */
2368 for (i
=0; i
<nFields
-1; i
++) {
2369 if (!strcmp(fields
[i
], "Shift"))
2370 *modifiers
|= ShiftMask
;
2371 else if (!strcmp(fields
[i
], "Lock"))
2372 *modifiers
|= LockMask
;
2373 else if (!strcmp(fields
[i
], "Ctrl"))
2374 *modifiers
|= ControlMask
;
2375 /* comparision with "Alt" for compatibility with old .nedit files*/
2376 else if (!strcmp(fields
[i
], "Alt"))
2377 *modifiers
|= Mod1Mask
;
2378 else if (!strcmp(fields
[i
], "Mod2"))
2379 *modifiers
|= Mod2Mask
;
2380 else if (!strcmp(fields
[i
], "Mod3"))
2381 *modifiers
|= Mod3Mask
;
2382 else if (!strcmp(fields
[i
], "Mod4"))
2383 *modifiers
|= Mod4Mask
;
2384 else if (!strcmp(fields
[i
], "Mod5"))
2385 *modifiers
|= Mod5Mask
;
2390 /* all fields successfully parsed */
2395 ** Scan text from "*inPtr" to the end of macro input (matching brace),
2396 ** advancing inPtr, and return macro text as function return value.
2398 ** This is kind of wastefull in that it throws away the compiled macro,
2399 ** to be re-generated from the text as needed, but compile time is
2400 ** negligible for most macros.
2402 static char *copyMacroToEnd(char **inPtr
)
2404 char *retStr
, *errMsg
, *stoppedAt
, *p
, *retPtr
;
2407 /* Skip over whitespace to find make sure there's a beginning brace
2408 to anchor the parse (if not, it will take the whole file) */
2409 *inPtr
+= strspn(*inPtr
, " \t\n");
2410 if (**inPtr
!= '{') {
2411 ParseError(NULL
, *inPtr
, *inPtr
-1, "macro menu item", "expecting '{'");
2415 /* Parse the input */
2416 prog
= ParseMacro(*inPtr
, &errMsg
, &stoppedAt
);
2418 ParseError(NULL
, *inPtr
, stoppedAt
, "macro menu item", errMsg
);
2423 /* Copy and return the body of the macro, stripping outer braces and
2424 extra leading tabs added by the writer routine */
2426 *inPtr
+= strspn(*inPtr
, " \t");
2427 if (**inPtr
== '\n') (*inPtr
)++;
2428 if (**inPtr
== '\t') (*inPtr
)++;
2429 if (**inPtr
== '\t') (*inPtr
)++;
2430 retPtr
= retStr
= XtMalloc(stoppedAt
- *inPtr
+ 1);
2431 for (p
= *inPtr
; p
< stoppedAt
- 1; p
++) {
2432 if (!strncmp(p
, "\n\t\t", 3)) {
2438 if (*(retPtr
-1) == '\t') retPtr
--;
2445 ** If "*string" is not terminated with a newline character, reallocate the
2446 ** string and add one. (The macro language requires newline terminators for
2447 ** statements, but the text widget doesn't force it like the NEdit text buffer
2448 ** does, so this might avoid some confusion.)
2450 static void addTerminatingNewline(char **string
)
2455 length
= strlen(*string
);
2456 if ((*string
)[length
-1] != '\n') {
2457 newString
= XtMalloc(length
+ 2);
2458 strcpy(newString
, *string
);
2459 newString
[length
] = '\n';
2460 newString
[length
+1] = '\0';
2462 *string
= newString
;