Applied the "OpenMotif sticky radio button" workaround to all radio buttons.
[nedit.git] / source / userCmds.c
blob8fcae2fe4276ff0b8d20d65dafcfe3305dc574ea
1 static const char CVSID[] = "$Id: userCmds.c,v 1.39 2004/01/16 09:18:28 edg Exp $";
2 /*******************************************************************************
3 * *
4 * userCmds.c -- Nirvana Editor shell and macro command dialogs *
5 * *
6 * Copyright (C) 1999 Mark Edel *
7 * *
8 * This is free software; you can redistribute it and/or modify it under the *
9 * terms of the GNU General Public License as published by the Free Software *
10 * Foundation; either version 2 of the License, or (at your option) any later *
11 * version. *
12 * *
13 * This software is distributed in the hope that it will be useful, but WITHOUT *
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
16 * for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License along with *
19 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
20 * Place, Suite 330, Boston, MA 02111-1307 USA *
21 * *
22 * Nirvana Text Editor *
23 * April, 1997 *
24 * *
25 * Written by Mark Edel *
26 * *
27 *******************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 #include "../config.h"
31 #endif
33 #include "userCmds.h"
34 #include "textBuf.h"
35 #include "text.h"
36 #include "nedit.h"
37 #include "preferences.h"
38 #include "window.h"
39 #include "menu.h"
40 #include "shell.h"
41 #include "macro.h"
42 #include "file.h"
43 #include "interpret.h"
44 #include "parse.h"
45 #include "../util/DialogF.h"
46 #include "../util/misc.h"
47 #include "../util/managedList.h"
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <ctype.h>
53 #ifdef VMS
54 #include "../util/VMSparam.h"
55 #else
56 #ifndef __MVS__
57 #include <sys/param.h>
58 #endif
59 #endif /*VMS*/
61 #include <Xm/Xm.h>
62 #include <X11/keysym.h>
63 #include <Xm/Text.h>
64 #include <Xm/Form.h>
65 #include <Xm/List.h>
66 #include <Xm/LabelG.h>
67 #include <Xm/PushB.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>
74 #ifdef HAVE_DEBUG_H
75 #include "../debug.h"
76 #endif
78 #if XmVersion >= 1002
79 #define MENU_WIDGET(w) (XmGetPostedFromWidget(XtParent(w)))
80 #else
81 #define MENU_WIDGET(w) (w)
82 #endif
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
91 #define LIST_RIGHT 45
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 */
99 typedef struct {
100 char *name;
101 unsigned int modifiers;
102 KeySym keysym;
103 char mnemonic;
104 char input;
105 char output;
106 char repInput;
107 char saveFirst;
108 char loadAfter;
109 char *cmd;
110 } menuItemRec;
112 /* Structure for widgets and flags associated with both shell command
113 and macro command editing dialogs */
114 typedef struct {
115 int dialogType;
116 WindowInfo *window;
117 Widget nameTextW, accTextW, mneTextW, cmdTextW, saveFirstBtn;
118 Widget loadAfterBtn, selInpBtn, winInpBtn, eitherInpBtn, noInpBtn;
119 Widget repInpBtn, sameOutBtn, dlogOutBtn, winOutBtn, dlogShell;
120 Widget managedList;
121 menuItemRec **menuItemsList;
122 int nMenuItems;
123 } userCmdDialog;
125 /* Structure for keeping track of hierarchical sub-menus durring user-menu
126 creation */
127 typedef struct {
128 char *name;
129 Widget menuPane;
130 } menuTreeItem;
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,
159 int *isDefaultLM);
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,
181 XtPointer callData);
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,
187 void *cbArg);
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,
193 int listType);
194 static int loadMenuItemString(char *inString, menuItemRec **menuItems,
195 int *nItems, int listType);
196 static void generateAcceleratorString(char *text, unsigned int modifiers,
197 KeySym keysym);
198 static void genAccelEventName(char *text, unsigned int modifiers,
199 KeySym keysym);
200 static int parseAcceleratorString(const char *string, unsigned int *modifiers,
201 KeySym *keysym);
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;
213 userCmdDialog *ucd;
214 XmString s1;
215 int ac, i;
216 Arg args[20];
218 /* if the dialog is already displayed, just pop it to the top and return */
219 if (ShellCmdDialog != NULL) {
220 RaiseShellWindow(ShellCmdDialog);
221 return;
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 *) *
230 MAX_ITEMS_PER_MENU);
231 for (i=0; i<NShellMenuItems; i++)
232 ucd->menuItemsList[i] = copyMenuItemRec(ShellMenuItems[i]);
233 ucd->nMenuItems = NShellMenuItems;
234 ucd->dialogType = SHELL_CMDS;
236 ac = 0;
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);
250 ac = 0;
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"),
266 XmNmnemonic, 'R',
267 XmNalignment, XmALIGNMENT_BEGINNING,
268 XmNset, False,
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);
275 XmStringFree(s1);
276 ucd->saveFirstBtn = XtVaCreateManagedWidget("saveFirstBtn",
277 xmToggleButtonWidgetClass, form,
278 XmNlabelString, s1=MKSTRING("Save file before executing command"),
279 XmNmnemonic, 'f',
280 XmNalignment, XmALIGNMENT_BEGINNING,
281 XmNset, False,
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);
288 XmStringFree(s1);
289 ucd->repInpBtn = XtVaCreateManagedWidget("repInpBtn",
290 xmToggleButtonWidgetClass, form,
291 XmNlabelString, s1=MKSTRING("Output replaces input"),
292 XmNmnemonic, 'f',
293 XmNalignment, XmALIGNMENT_BEGINNING,
294 XmNset, False,
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);
301 XmStringFree(s1);
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"),
317 XmNmnemonic, 'm',
318 XmNalignment, XmALIGNMENT_BEGINNING,
319 XmNmarginHeight, 0,
320 XmNset, True, NULL);
321 XmStringFree(s1);
322 XtAddCallback(ucd->sameOutBtn, XmNvalueChangedCallback, sameOutCB, ucd);
323 ucd->dlogOutBtn = XtVaCreateManagedWidget("dlogOutBtn",
324 xmToggleButtonWidgetClass, outBox,
325 XmNlabelString, s1=MKSTRING("dialog"),
326 XmNmnemonic, 'g',
327 XmNalignment, XmALIGNMENT_BEGINNING,
328 XmNmarginHeight, 0,
329 XmNset, False, NULL);
330 XmStringFree(s1);
331 ucd->winOutBtn = XtVaCreateManagedWidget("winOutBtn", xmToggleButtonWidgetClass,
332 outBox,
333 XmNlabelString, s1=MKSTRING("new window"),
334 XmNmnemonic, 'n',
335 XmNalignment, XmALIGNMENT_BEGINNING,
336 XmNmarginHeight, 0,
337 XmNset, False, NULL);
338 XmStringFree(s1);
339 outLabel = XtVaCreateManagedWidget("outLabel", xmLabelGadgetClass, form,
340 XmNlabelString, s1=MKSTRING("Command Output:"),
341 XmNalignment, XmALIGNMENT_BEGINNING,
342 XmNmarginTop, 5,
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);
349 XmStringFree(s1);
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,
363 inpBox,
364 XmNlabelString, s1=MKSTRING("selection"),
365 XmNmnemonic, 's',
366 XmNalignment, XmALIGNMENT_BEGINNING,
367 XmNmarginHeight, 0,
368 XmNset, True, NULL);
369 XmStringFree(s1);
370 ucd->winInpBtn = XtVaCreateManagedWidget("winInpBtn",
371 xmToggleButtonWidgetClass, inpBox,
372 XmNlabelString, s1=MKSTRING("window"),
373 XmNmnemonic, 'w',
374 XmNalignment, XmALIGNMENT_BEGINNING,
375 XmNmarginHeight, 0,
376 XmNset, False, NULL);
377 XmStringFree(s1);
378 ucd->eitherInpBtn = XtVaCreateManagedWidget("eitherInpBtn",
379 xmToggleButtonWidgetClass, inpBox,
380 XmNlabelString, s1=MKSTRING("either"),
381 XmNmnemonic, 't',
382 XmNalignment, XmALIGNMENT_BEGINNING,
383 XmNmarginHeight, 0,
384 XmNset, False, NULL);
385 XmStringFree(s1);
386 ucd->noInpBtn = XtVaCreateManagedWidget("noInpBtn",
387 xmToggleButtonWidgetClass, inpBox,
388 XmNlabelString, s1=MKSTRING("none"),
389 XmNmnemonic, 'o',
390 XmNalignment, XmALIGNMENT_BEGINNING,
391 XmNmarginHeight, 0,
392 XmNset, False, NULL);
393 XmStringFree(s1);
394 inpLabel = XtVaCreateManagedWidget("inpLabel", xmLabelGadgetClass, form,
395 XmNlabelString, s1=MKSTRING("Command Input:"),
396 XmNalignment, XmALIGNMENT_BEGINNING,
397 XmNmarginTop, 5,
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);
404 XmStringFree(s1);
406 ucd->mneTextW = XtVaCreateManagedWidget("mne", xmTextWidgetClass, form,
407 XmNcolumns, 1,
408 XmNmaxLength, 1,
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,
418 XmNcolumns, 12,
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"),
433 XmNmnemonic, 'l',
434 XmNuserData, ucd->accTextW,
435 XmNalignment, XmALIGNMENT_BEGINNING,
436 XmNmarginTop, 5,
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);
443 XmStringFree(s1);
445 XtVaCreateManagedWidget("mneLabel", xmLabelGadgetClass, form,
446 XmNlabelString, s1=MKSTRING("Mnemonic"),
447 XmNmnemonic, 'i',
448 XmNuserData, ucd->mneTextW,
449 XmNalignment, XmALIGNMENT_END,
450 XmNmarginTop, 5,
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);
457 XmStringFree(s1);
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"),
470 XmNmnemonic, 'y',
471 XmNuserData, ucd->nameTextW,
472 XmNalignment, XmALIGNMENT_BEGINNING,
473 XmNmarginTop, 5,
474 XmNleftAttachment, XmATTACH_POSITION,
475 XmNleftPosition, LIST_RIGHT,
476 XmNbottomAttachment, XmATTACH_WIDGET,
477 XmNbottomWidget, ucd->nameTextW, NULL);
478 XmStringFree(s1);
480 XtVaCreateManagedWidget("nameNotes", xmLabelGadgetClass, form,
481 XmNlabelString, s1=MKSTRING("(> for sub-menu, @ language mode)"),
482 XmNalignment, XmALIGNMENT_END,
483 XmNmarginTop, 5,
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);
490 XmStringFree(s1);
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,
497 XmNtopPosition, 2,
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);
504 XmStringFree(s1);
506 cmdLabel = XtVaCreateManagedWidget("cmdLabel", xmLabelGadgetClass, form,
507 XmNlabelString, s1=MKSTRING("Shell Command to Execute"),
508 XmNmnemonic, 'x',
509 XmNalignment, XmALIGNMENT_BEGINNING,
510 XmNmarginTop, 5,
511 XmNtopAttachment, XmATTACH_POSITION,
512 XmNtopPosition, SHELL_CMD_TOP,
513 XmNleftAttachment, XmATTACH_POSITION,
514 XmNleftPosition, LEFT_MARGIN_POS, NULL);
515 XmStringFree(s1);
516 XtVaCreateManagedWidget("cmdLabel", xmLabelGadgetClass, form,
517 XmNlabelString, s1=MKSTRING("(% expands to current filename, # to line number)"),
518 XmNalignment, XmALIGNMENT_END,
519 XmNmarginTop, 5,
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);
526 XmStringFree(s1);
528 okBtn = XtVaCreateManagedWidget("ok",xmPushButtonWidgetClass,form,
529 XmNlabelString, s1=MKSTRING("OK"),
530 XmNleftAttachment, XmATTACH_POSITION,
531 XmNleftPosition, 13,
532 XmNrightAttachment, XmATTACH_POSITION,
533 XmNrightPosition, 29,
534 XmNbottomAttachment, XmATTACH_POSITION,
535 XmNbottomPosition, 99, NULL);
536 XtAddCallback(okBtn, XmNactivateCallback, okCB, ucd);
537 XmStringFree(s1);
539 applyBtn = XtVaCreateManagedWidget("apply",xmPushButtonWidgetClass,form,
540 XmNlabelString, s1=MKSTRING("Apply"),
541 XmNmnemonic, 'A',
542 XmNleftAttachment, XmATTACH_POSITION,
543 XmNleftPosition, 42,
544 XmNrightAttachment, XmATTACH_POSITION,
545 XmNrightPosition, 58,
546 XmNbottomAttachment, XmATTACH_POSITION,
547 XmNbottomPosition, 99, NULL);
548 XtAddCallback(applyBtn, XmNactivateCallback, applyCB, ucd);
549 XmStringFree(s1);
551 dismissBtn = XtVaCreateManagedWidget("dismiss",xmPushButtonWidgetClass,form,
552 XmNlabelString, s1=MKSTRING("Dismiss"),
553 XmNleftAttachment, XmATTACH_POSITION,
554 XmNleftPosition, 71,
555 XmNrightAttachment, XmATTACH_POSITION,
556 XmNrightPosition, 87,
557 XmNbottomAttachment, XmATTACH_POSITION,
558 XmNbottomPosition, 99, NULL);
559 XtAddCallback(dismissBtn, XmNactivateCallback, dismissCB, ucd);
560 XmStringFree(s1);
562 ac = 0;
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;
617 userCmdDialog *ucd;
618 char *title;
619 XmString s1;
620 int ac, i;
621 Arg args[20];
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);
626 return;
628 if (dialogType == BG_MENU_CMDS && BGMenuCmdDialog != NULL) {
629 RaiseShellWindow(BGMenuCmdDialog);
630 return;
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 **) *
639 MAX_ITEMS_PER_MENU);
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";
653 ac = 0;
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);
666 ac = 0;
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"),
682 XmNmnemonic, 'R',
683 XmNalignment, XmALIGNMENT_BEGINNING,
684 XmNmarginHeight, 0,
685 XmNset, False,
686 XmNleftAttachment, XmATTACH_POSITION,
687 XmNleftPosition, LIST_RIGHT,
688 XmNbottomAttachment, XmATTACH_POSITION,
689 XmNbottomPosition, MACRO_CMD_TOP, NULL);
690 XmStringFree(s1);
692 ucd->mneTextW = XtVaCreateManagedWidget("mne", xmTextWidgetClass, form,
693 XmNcolumns, 1,
694 XmNmaxLength, 1,
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,
705 XmNcolumns, 12,
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"),
722 XmNmnemonic, 'l',
723 XmNuserData, ucd->accTextW,
724 XmNalignment, XmALIGNMENT_BEGINNING,
725 XmNmarginTop, 5,
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);
732 XmStringFree(s1);
734 XtVaCreateManagedWidget("mneLabel", xmLabelGadgetClass, form,
735 XmNlabelString, s1=MKSTRING("Mnemonic"),
736 XmNmnemonic, 'i',
737 XmNuserData, ucd->mneTextW,
738 XmNalignment, XmALIGNMENT_END,
739 XmNmarginTop, 5,
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);
746 XmStringFree(s1);
748 pasteReplayBtn = XtVaCreateManagedWidget("pasteReplay",
749 xmPushButtonWidgetClass, form,
750 XmNlabelString, s1=MKSTRING("Paste Learn/\nReplay Macro"),
751 XmNmnemonic, 'P',
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,
760 pasteReplayCB, ucd);
761 XmStringFree(s1);
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"),
774 XmNmnemonic, 'y',
775 XmNuserData, ucd->nameTextW,
776 XmNalignment, XmALIGNMENT_BEGINNING,
777 XmNmarginTop, 5,
778 XmNleftAttachment, XmATTACH_POSITION,
779 XmNleftPosition, LIST_RIGHT,
780 XmNbottomAttachment, XmATTACH_WIDGET,
781 XmNbottomWidget, ucd->nameTextW, NULL);
782 XmStringFree(s1);
784 XtVaCreateManagedWidget("nameNotes", xmLabelGadgetClass, form,
785 XmNlabelString, s1=MKSTRING("(> for sub-menu, @ language mode)"),
786 XmNalignment, XmALIGNMENT_END,
787 XmNmarginTop, 5,
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);
794 XmStringFree(s1);
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,
801 XmNtopPosition, 2,
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);
808 XmStringFree(s1);
810 cmdLabel = XtVaCreateManagedWidget("cmdLabel", xmLabelGadgetClass, form,
811 XmNlabelString, s1=MKSTRING("Macro Command to Execute"),
812 XmNmnemonic, 'x',
813 XmNalignment, XmALIGNMENT_BEGINNING,
814 XmNmarginTop, 5,
815 XmNtopAttachment, XmATTACH_POSITION,
816 XmNtopPosition, MACRO_CMD_TOP,
817 XmNleftAttachment, XmATTACH_POSITION,
818 XmNleftPosition, LEFT_MARGIN_POS, NULL);
819 XmStringFree(s1);
821 okBtn = XtVaCreateManagedWidget("ok",xmPushButtonWidgetClass,form,
822 XmNlabelString, s1=MKSTRING("OK"),
823 XmNleftAttachment, XmATTACH_POSITION,
824 XmNleftPosition, 8,
825 XmNrightAttachment, XmATTACH_POSITION,
826 XmNrightPosition, 23,
827 XmNbottomAttachment, XmATTACH_POSITION,
828 XmNbottomPosition, 99, NULL);
829 XtAddCallback(okBtn, XmNactivateCallback, okCB, ucd);
830 XmStringFree(s1);
832 applyBtn = XtVaCreateManagedWidget("apply",xmPushButtonWidgetClass,form,
833 XmNlabelString, s1=MKSTRING("Apply"),
834 XmNmnemonic, 'A',
835 XmNleftAttachment, XmATTACH_POSITION,
836 XmNleftPosition, 31,
837 XmNrightAttachment, XmATTACH_POSITION,
838 XmNrightPosition, 46,
839 XmNbottomAttachment, XmATTACH_POSITION,
840 XmNbottomPosition, 99, NULL);
841 XtAddCallback(applyBtn, XmNactivateCallback, applyCB, ucd);
842 XmStringFree(s1);
844 applyBtn = XtVaCreateManagedWidget("check",xmPushButtonWidgetClass,form,
845 XmNlabelString, s1=MKSTRING("Check"),
846 XmNmnemonic, 'C',
847 XmNleftAttachment, XmATTACH_POSITION,
848 XmNleftPosition, 54,
849 XmNrightAttachment, XmATTACH_POSITION,
850 XmNrightPosition, 69,
851 XmNbottomAttachment, XmATTACH_POSITION,
852 XmNbottomPosition, 99, NULL);
853 XtAddCallback(applyBtn, XmNactivateCallback, checkCB, ucd);
854 XmStringFree(s1);
856 dismissBtn = XtVaCreateManagedWidget("dismiss",xmPushButtonWidgetClass,form,
857 XmNlabelString, s1=MKSTRING("Dismiss"),
858 XmNleftAttachment, XmATTACH_POSITION,
859 XmNleftPosition, 77,
860 XmNrightAttachment, XmATTACH_POSITION,
861 XmNrightPosition, 92,
862 XmNbottomAttachment, XmATTACH_POSITION,
863 XmNbottomPosition, 99, NULL);
864 XtAddCallback(dismissBtn, XmNactivateCallback, dismissCB, ucd);
865 XmStringFree(s1);
867 ac = 0;
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;
903 } else {
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)
946 #ifndef VMS
947 dimSelDepItemsInMenu(window->shellMenuPane, ShellMenuItems,
948 NShellMenuItems, sensitive);
949 #endif /*VMS*/
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)
959 WidgetList items;
960 Widget subMenu;
961 XtPointer userData;
962 int n, index;
963 Cardinal nItems;
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);
972 } else {
973 index = (int)userData - 10;
974 if (index <0 || index >= nMenuItems)
975 return;
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
986 ** in the Edit menu
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,
1008 SHELL_CMDS);
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
1016 ** in that form.
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,
1035 SHELL_CMDS);
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,
1045 MACRO_CMDS);
1048 int LoadBGMenuCmdsString(char *inString)
1050 return loadMenuItemString(inString, BGMenuItems, &NBGMenuItems,
1051 BG_MENU_CMDS);
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.
1058 #ifndef VMS
1059 int DoNamedShellMenuCmd(WindowInfo *window, const char *itemName, int fromMacro)
1061 int i;
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))
1067 return False;
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);
1072 return True;
1075 return False;
1077 #endif /*VMS*/
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
1082 ** failure.
1084 int DoNamedMacroMenuCmd(WindowInfo *window, const char *itemName)
1086 int i;
1088 for (i=0; i<NMacroMenuItems; i++) {
1089 if (!strcmp(MacroMenuItems[i]->name, itemName)) {
1090 DoMacro(window, MacroMenuItems[i]->cmd, "macro menu command");
1091 return True;
1094 return False;
1097 int DoNamedBGMenuCmd(WindowInfo *window, const char *itemName)
1099 int i;
1101 for (i=0; i<NBGMenuItems; i++) {
1102 if (!strcmp(BGMenuItems[i]->name, itemName)) {
1103 DoMacro(window, BGMenuItems[i]->cmd, "background menu macro");
1104 return True;
1107 return False;
1111 ** Update all of the Shell or Macro menus of all editor windows.
1113 static void updateMenus(int menuType)
1115 WindowInfo *w;
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;
1128 int nListItems, n;
1129 menuItemRec *f, **itemList;
1130 menuTreeItem *menuTree;
1131 int i, nTreeEntries, isDefaultLM;
1132 char *hierName, *namePtr, *subMenuName, *subSep, *strippedName, *name;
1134 if (!IsTopDocument(window))
1135 return;
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
1155 are added */
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);
1164 nTreeEntries = 0;
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++) {
1176 f = itemList[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,
1183 &isDefaultLM);
1184 if (strippedName == NULL)
1185 continue; /* not a valid entry for the language */
1186 if (isDefaultLM) {
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 */
1192 break;
1194 XtFree(name);
1196 if (i != nListItems) {
1197 XtFree(strippedName);
1198 continue;
1202 /* create/find sub-menus, stripping off '>' until item name is
1203 reached, then create the menu item */
1204 namePtr = strippedName;
1205 subPane = menuPane;
1206 for (;;) {
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)),
1212 (XtPointer)window);
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);
1218 break;
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;
1228 } else
1229 XtFree(hierName);
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
1249 the empty space */
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)
1267 int i;
1269 for (i=0; i<nTreeEntries; i++)
1270 if (!strcmp(hierName, menuTree[i].name))
1271 return menuTree[i].menuPane;
1272 return NULL;
1275 static char *copySubstring(const char *string, int length)
1277 char *retStr = XtMalloc(length + 1);
1279 strncpy(retStr, string, length);
1280 retStr[length] = '\0';
1281 return retStr;
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,
1293 int *isDefaultLM)
1295 char *atPtr, *firstAtPtr, *endPtr;
1296 int lmNameLen;
1298 atPtr = firstAtPtr = strchr(menuItemName, '@');
1299 *isDefaultLM = False;
1300 if (atPtr == NULL)
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)
1310 return NULL;
1311 for (;;) {
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, '@');
1320 if (atPtr == NULL)
1321 return NULL;
1325 static Widget createUserMenuItem(Widget menuPane, char *name, menuItemRec *f,
1326 int index, XtCallbackProc cbRtn, XtPointer cbArg)
1328 XmString st1, st2;
1329 char accText[MAX_ACCEL_LEN], accKeys[MAX_ACCEL_LEN+5];
1330 Widget btn;
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);
1343 XmStringFree(st1);
1344 XmStringFree(st2);
1345 return btn;
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
1352 ** new sub menu.
1354 static Widget createUserSubMenu(Widget parent, char *label)
1356 Widget menu;
1357 XmString st1;
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);
1364 XmStringFree(st1);
1365 return menu;
1368 static void removeMenuItems(Widget menuPane)
1370 WidgetList items, itemList;
1371 Widget subMenuID;
1372 XtPointer userData;
1373 int n;
1374 Cardinal nItems;
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,
1379 NULL);
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);
1397 #endif
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;
1417 else
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))
1431 return;
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;
1438 else
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)
1464 menuItemRec *f;
1466 f = readDialogFields(ucd, False);
1467 if (f == NULL)
1468 return False;
1469 if (!checkMacroText(f->cmd, ucd->dlogShell, ucd->cmdTextW)) {
1470 freeMenuItemRec(f);
1471 return False;
1473 return True;
1476 static int checkMacroText(char *macro, Widget errorParent, Widget errFocus)
1478 Program *prog;
1479 char *errMsg, *stoppedAt;
1481 prog = ParseMacro(macro, &errMsg, &stoppedAt);
1482 if (prog == NULL) {
1483 if (errorParent != NULL) {
1484 ParseError(errorParent, macro, stoppedAt, "macro", errMsg);
1485 XmTextSetInsertionPosition(errFocus, stoppedAt - macro);
1486 XmProcessTraversal(errFocus, XmTRAVERSE_CURRENT);
1488 return False;
1490 FreeProgram(prog);
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);
1497 return False;
1499 return True;
1502 static int applyDialogChanges(userCmdDialog *ucd)
1504 int i;
1506 /* Get the current contents of the dialog fields */
1507 if (!UpdateManagedList(ucd->managedList, True))
1508 return False;
1510 /* Test compile the macro */
1511 if (ucd->dialogType == MACRO_CMDS)
1512 if (!checkMacro(ucd))
1513 return False;
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 */
1540 MarkPrefsChanged();
1541 return True;
1544 static void pasteReplayCB(Widget w, XtPointer clientData, XtPointer callData)
1546 userCmdDialog *ucd = (userCmdDialog *)clientData;
1548 if (GetReplayMacro() == NULL)
1549 return;
1551 XmTextInsert(ucd->cmdTextW, XmTextGetInsertionPosition(ucd->cmdTextW),
1552 GetReplayMacro());
1555 static void destroyCB(Widget w, XtPointer clientData, XtPointer callData)
1557 userCmdDialog *ucd = (userCmdDialog *)clientData;
1558 int i;
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))
1588 return;
1590 /* Tab key means go to the next field, don't enter */
1591 if (keysym == XK_Tab)
1592 return;
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);
1598 return;
1601 /* Delete or backspace clears field */
1602 if (keysym == XK_Delete || keysym == XK_BackSpace) {
1603 XmTextSetString(ucd->accTextW, "");
1604 return;
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);
1616 return;
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)
1631 XtArgVal userData;
1632 int index;
1633 char *params[1];
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)
1641 return;
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)
1650 XtArgVal userData;
1651 int index;
1652 char *params[1];
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);
1666 return;
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)
1673 return;
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)
1682 XtArgVal userData;
1683 int index;
1684 char *params[1];
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);
1691 return;
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)
1698 return;
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
1708 ** menu item list.
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 */
1716 if (f == NULL) {
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);
1729 } else {
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,
1740 False);
1741 RadioButtonChangeState(ucd->eitherInpBtn, f->input == FROM_EITHER,
1742 False);
1743 RadioButtonChangeState(ucd->noInpBtn, f->input == FROM_NONE,
1744 False);
1745 RadioButtonChangeState(ucd->sameOutBtn, f->output==TO_SAME_WINDOW,
1746 False);
1747 RadioButtonChangeState(ucd->winOutBtn, f->output==TO_NEW_WINDOW,
1748 False);
1749 RadioButtonChangeState(ucd->dlogOutBtn, f->output==TO_DIALOG,
1750 False);
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
1763 ** failure.
1765 static menuItemRec *readDialogFields(userCmdDialog *ucd, int silent)
1767 char *nameText, *cmdText, *mneText, *accText;
1768 menuItemRec *f;
1770 nameText = XmTextGetString(ucd->nameTextW);
1771 if (*nameText == '\0')
1773 if (!silent)
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);
1779 XtFree(nameText);
1780 return NULL;
1783 if (strchr(nameText, ':'))
1785 if (!silent)
1787 DialogF(DF_WARN, ucd->dlogShell, 1, "Menu Entry",
1788 "Menu item names may not\ncontain colon (:) characters",
1789 "Dismiss");
1790 XmProcessTraversal(ucd->nameTextW, XmTRAVERSE_CURRENT);
1792 XtFree(nameText);
1793 return NULL;
1796 cmdText = XmTextGetString(ucd->cmdTextW);
1797 if (cmdText == NULL || *cmdText == '\0')
1799 if (!silent)
1801 DialogF(DF_WARN, ucd->dlogShell, 1, "Command to Execute",
1802 "Please specify %s to execute", "Dismiss",
1803 ucd->dialogType == SHELL_CMDS
1804 ? "shell command"
1805 : "macro command(s)");
1806 XmProcessTraversal(ucd->cmdTextW, XmTRAVERSE_CURRENT);
1808 XtFree(nameText);
1810 if (cmdText!=NULL)
1812 XtFree(cmdText);
1814 return NULL;
1817 if (ucd->dialogType == MACRO_CMDS || ucd->dialogType == BG_MENU_CMDS) {
1818 addTerminatingNewline(&cmdText);
1819 if (!checkMacroText(cmdText, silent ? NULL : ucd->dlogShell,
1820 ucd->cmdTextW)) {
1821 XtFree(nameText);
1822 XtFree(cmdText);
1823 return NULL;
1826 f = (menuItemRec *)XtMalloc(sizeof(menuItemRec));
1827 f->name = nameText;
1828 f->cmd = cmdText;
1829 if ((mneText = XmTextGetString(ucd->mneTextW)) != NULL) {
1830 f->mnemonic = mneText==NULL ? '\0' : mneText[0];
1831 XtFree(mneText);
1832 if (f->mnemonic == ':') /* colons mess up string parsing */
1833 f->mnemonic = '\0';
1835 if ((accText = XmTextGetString(ucd->accTextW)) != NULL) {
1836 parseAcceleratorString(accText, &f->modifiers, &f->keysym);
1837 XtFree(accText);
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;
1846 else
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;
1852 else
1853 f->output = TO_SAME_WINDOW;
1854 f->repInput = XmToggleButtonGetState(ucd->repInpBtn);
1855 f->saveFirst = XmToggleButtonGetState(ucd->saveFirstBtn);
1856 f->loadAfter = XmToggleButtonGetState(ucd->loadAfterBtn);
1857 } else {
1858 f->input = XmToggleButtonGetState(ucd->selInpBtn) ? FROM_SELECTION :
1859 FROM_NONE;
1860 f->output = TO_SAME_WINDOW;
1861 f->repInput = False;
1862 f->saveFirst = False;
1863 f->loadAfter = False;
1865 return f;
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));
1876 *newItem = *item;
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);
1881 return newItem;
1885 ** Free a menu item record, and its associated memory
1887 static void freeMenuItemRec(menuItemRec *item)
1889 XtFree(item->name);
1890 XtFree(item->cmd);
1891 XtFree((char *)item);
1895 ** Callbacks for managed-list operations
1897 static void *getDialogDataCB(void *oldItem, int explicitRequest, int *abort,
1898 void *cbArg)
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))
1906 return NULL;
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",
1918 "Discard") == 2)
1920 return oldItem == NULL
1921 ? NULL
1922 : (void *)copyMenuItemRec((menuItemRec *)oldItem);
1926 /* Do readDialogFields again without "silent" mode to display warning(s) */
1927 readDialogFields(ucd, False);
1928 *abort = True;
1929 return NULL;
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,
1983 int listType)
1985 char *outStr, *outPtr, *c, accStr[MAX_ACCEL_LEN];
1986 menuItemRec *f;
1987 int i, length;
1989 /* determine the max. amount of memory needed for the returned string
1990 and allocate a buffer for composing the string */
1991 length = 0;
1992 for (i=0; i<nItems; i++) {
1993 f = menuItems[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 */
2004 outPtr = outStr;
2005 *outPtr++ = '\\';
2006 *outPtr++ = '\n';
2007 for (i=0; i<nItems; i++) {
2008 f = menuItems[i];
2009 generateAcceleratorString(accStr, f->modifiers, f->keysym);
2010 *outPtr++ = '\t';
2011 for (c=f->name; *c!='\0'; ++c) { /* Copy the command name */
2012 if (*c == '\\') { /* changing backslashes to \\ */
2013 *outPtr++ = '\\';
2014 *outPtr++ = '\\';
2015 } else if (*c == '\n') { /* changing newlines to \n */
2016 *outPtr++ = '\\';
2017 *outPtr++ = 'n';
2018 } else {
2019 *outPtr++ = *c;
2022 *outPtr++ = ':';
2023 strcpy(outPtr, accStr);
2024 outPtr += strlen(accStr);
2025 *outPtr++ = ':';
2026 if (f->mnemonic != '\0')
2027 *outPtr++ = f->mnemonic;
2028 *outPtr++ = ':';
2029 if (listType == SHELL_CMDS) {
2030 if (f->input == FROM_SELECTION)
2031 *outPtr++ = 'I';
2032 else if (f->input == FROM_WINDOW)
2033 *outPtr++ = 'A';
2034 else if (f->input == FROM_EITHER)
2035 *outPtr++ = 'E';
2036 if (f->output == TO_DIALOG)
2037 *outPtr++ = 'D';
2038 else if (f->output == TO_NEW_WINDOW)
2039 *outPtr++ = 'W';
2040 if (f->repInput)
2041 *outPtr++ = 'X';
2042 if (f->saveFirst)
2043 *outPtr++ = 'S';
2044 if (f->loadAfter)
2045 *outPtr++ = 'L';
2046 *outPtr++ = ':';
2047 } else {
2048 if (f->input == FROM_SELECTION)
2049 *outPtr++ = 'R';
2050 *outPtr++ = ':';
2051 *outPtr++ = ' ';
2052 *outPtr++ = '{';
2054 *outPtr++ = '\\';
2055 *outPtr++ = 'n';
2056 *outPtr++ = '\\';
2057 *outPtr++ = '\n';
2058 *outPtr++ = '\t';
2059 *outPtr++ = '\t';
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') {
2065 *outPtr++ = '\\';
2066 *outPtr++ = 'n';
2067 *outPtr++ = '\\';
2068 *outPtr++ = '\n';
2069 *outPtr++ = '\t';
2070 *outPtr++ = '\t';
2071 } else
2072 *outPtr++ = *c;
2074 if (listType == MACRO_CMDS || listType == BG_MENU_CMDS) {
2075 if (*(outPtr-1) == '\t') outPtr--;
2076 *outPtr++ = '}';
2078 *outPtr++ = '\\';
2079 *outPtr++ = 'n';
2080 *outPtr++ = '\\';
2081 *outPtr++ = '\n';
2083 --outPtr;
2084 *--outPtr = '\0';
2085 return outStr;
2088 static int loadMenuItemString(char *inString, menuItemRec **menuItems,
2089 int *nItems, int listType)
2091 menuItemRec *f;
2092 char *cmdStr;
2093 char *inPtr = inString;
2094 char *nameStr, accStr[MAX_ACCEL_LEN], mneChar;
2095 KeySym keysym;
2096 unsigned int modifiers;
2097 int i, input, output, saveFirst, loadAfter, repInput;
2098 int nameLen, accLen, mneLen, cmdLen;
2100 for (;;) {
2102 /* remove leading whitespace */
2103 while (*inPtr == ' ' || *inPtr == '\t')
2104 inPtr++;
2106 /* read name field */
2107 nameLen = strcspn(inPtr, ":");
2108 if (nameLen == 0)
2109 return parseError("no name field");
2110 nameStr = XtMalloc(nameLen+1);
2111 strncpy(nameStr, inPtr, nameLen);
2112 nameStr[nameLen] = '\0';
2113 inPtr += nameLen;
2114 if (*inPtr == '\0')
2115 return parseError("end not expected");
2116 inPtr++;
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';
2124 inPtr += accLen;
2125 if (*inPtr == '\0')
2126 return parseError("end not expected");
2127 inPtr++;
2129 /* read menemonic field */
2130 mneLen = strcspn(inPtr, ":");
2131 if (mneLen > 1)
2132 return parseError("mnemonic field too long");
2133 if (mneLen == 1)
2134 mneChar = *inPtr++;
2135 else
2136 mneChar = '\0';
2137 inPtr++;
2138 if (*inPtr == '\0')
2139 return parseError("end not expected");
2141 /* read flags field */
2142 input = FROM_NONE;
2143 output = TO_SAME_WINDOW;
2144 repInput = False;
2145 saveFirst = False;
2146 loadAfter = False;
2147 for (; *inPtr != ':'; inPtr++) {
2148 if (listType == SHELL_CMDS) {
2149 if (*inPtr == 'I')
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')
2158 output = TO_DIALOG;
2159 else if (*inPtr == 'X')
2160 repInput = True;
2161 else if (*inPtr == 'S')
2162 saveFirst = True;
2163 else if (*inPtr == 'L')
2164 loadAfter = True;
2165 else
2166 return parseError("unreadable flag field");
2167 } else {
2168 if (*inPtr == 'R')
2169 input = FROM_SELECTION;
2170 else
2171 return parseError("unreadable flag field");
2174 inPtr++;
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 */
2181 inPtr++;
2182 cmdLen = strcspn(inPtr, "\n");
2183 if (cmdLen == 0)
2184 return parseError("shell command field is empty");
2185 cmdStr = XtMalloc(cmdLen+1);
2186 strncpy(cmdStr, inPtr, cmdLen);
2187 cmdStr[cmdLen] = '\0';
2188 inPtr += cmdLen;
2189 } else {
2190 cmdStr = copyMacroToEnd(&inPtr);
2191 if (cmdStr == NULL)
2192 return False;
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));
2203 f->name = nameStr;
2204 f->cmd = cmdStr;
2205 f->mnemonic = mneChar;
2206 f->modifiers = modifiers;
2207 f->input = input;
2208 f->output = output;
2209 f->repInput = repInput;
2210 f->saveFirst = saveFirst;
2211 f->loadAfter = loadAfter;
2212 f->keysym = keysym;
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]);
2218 menuItems[i] = f;
2219 break;
2222 if (i == *nItems)
2223 menuItems[(*nItems)++] = f;
2225 /* end of string in proper place */
2226 if (*inPtr == '\0')
2227 return True;
2231 static int parseError(const char *message)
2233 fprintf(stderr, "NEdit: Parse error in user defined menu item, %s\n",
2234 message);
2235 return False;
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,
2243 KeySym keysym)
2245 char *shiftStr = "", *ctrlStr = "", *altStr = "";
2246 char *mod2Str = "", *mod3Str = "", *mod4Str = "", *mod5Str = "";
2247 char keyName[20];
2248 Modifiers numLockMask = GetNumLockModMask(TheDisplay);
2250 /* if there's no accelerator, generate an empty string */
2251 if (keysym == NoSymbol) {
2252 *text = '\0';
2253 return;
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)
2263 ctrlStr = "Ctrl+";
2264 if (modifiers & Mod1Mask)
2265 altStr = "Alt+";
2266 if ((modifiers & Mod2Mask) && (Mod2Mask != numLockMask))
2267 mod2Str = "Mod2+";
2268 if ((modifiers & Mod3Mask) && (Mod3Mask != numLockMask))
2269 mod3Str = "Mod3+";
2270 if ((modifiers & Mod4Mask) && (Mod4Mask != numLockMask))
2271 mod4Str = "Mod4+";
2272 if ((modifiers & Mod5Mask) && (Mod5Mask != numLockMask))
2273 mod5Str = "Mod5+";
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,
2290 KeySym keysym)
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) {
2297 *text = '\0';
2298 return;
2301 /* translate the modifiers into strings */
2302 if (modifiers & ShiftMask)
2303 shiftStr = "Shift ";
2304 if (modifiers & LockMask)
2305 lockStr = "Lock ";
2306 if (modifiers & ControlMask)
2307 ctrlStr = "Ctrl ";
2308 if (modifiers & Mod1Mask)
2309 altStr = "Alt ";
2310 if (modifiers & Mod2Mask)
2311 mod2Str = "Mod2 ";
2312 if (modifiers & Mod3Mask)
2313 mod3Str = "Mod3 ";
2314 if (modifiers & Mod4Mask)
2315 mod4Str = "Mod4 ";
2316 if (modifiers & Mod5Mask)
2317 mod5Str = "Mod5 ";
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,
2332 KeySym *keysym)
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) {
2339 *modifiers = 0;
2340 *keysym = NoSymbol;
2341 return True;
2344 /* limit the string length so no field strings will overflow */
2345 if (inputLength > MAX_ACCEL_LEN)
2346 return False;
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]);
2352 if (nFields == 0)
2353 return False;
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)
2363 return False;
2366 /* parse the modifier names from the rest of the fields */
2367 *modifiers = 0;
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;
2386 else
2387 return False;
2390 /* all fields successfully parsed */
2391 return True;
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;
2405 Program *prog;
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 '{'");
2412 return NULL;
2415 /* Parse the input */
2416 prog = ParseMacro(*inPtr, &errMsg, &stoppedAt);
2417 if (prog == NULL) {
2418 ParseError(NULL, *inPtr, stoppedAt, "macro menu item", errMsg);
2419 return NULL;
2421 FreeProgram(prog);
2423 /* Copy and return the body of the macro, stripping outer braces and
2424 extra leading tabs added by the writer routine */
2425 (*inPtr)++;
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)) {
2433 *retPtr++ = '\n';
2434 p += 2;
2435 } else
2436 *retPtr++ = *p;
2438 if (*(retPtr-1) == '\t') retPtr--;
2439 *retPtr = '\0';
2440 *inPtr = stoppedAt;
2441 return retStr;
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)
2452 char *newString;
2453 int length;
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';
2461 XtFree(*string);
2462 *string = newString;