Propogate colors to new windows on opening (744294)
[nedit.git] / source / userCmds.c
blobfd5171af266d604ccb5595bdb833decce7a85096
1 static const char CVSID[] = "$Id: userCmds.c,v 1.32 2003/05/02 19:19:02 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>
73 #ifdef HAVE_DEBUG_H
74 #include "../debug.h"
75 #endif
78 /* max number of user programmable menu commands allowed per each of the
79 macro, shell, and bacground menus */
80 #define MAX_ITEMS_PER_MENU 400
82 /* major divisions (in position units) in User Commands dialogs */
83 #define LEFT_MARGIN_POS 1
84 #define RIGHT_MARGIN_POS 99
85 #define LIST_RIGHT 45
86 #define SHELL_CMD_TOP 70
87 #define MACRO_CMD_TOP 40
89 /* types of current dialog and/or menu */
90 enum dialogTypes {SHELL_CMDS, MACRO_CMDS, BG_MENU_CMDS};
92 /* Structure representing a menu item for both the shell and macro menus */
93 typedef struct {
94 char *name;
95 unsigned int modifiers;
96 KeySym keysym;
97 char mnemonic;
98 char input;
99 char output;
100 char repInput;
101 char saveFirst;
102 char loadAfter;
103 char *cmd;
104 } menuItemRec;
106 /* Structure for widgets and flags associated with both shell command
107 and macro command editing dialogs */
108 typedef struct {
109 int dialogType;
110 WindowInfo *window;
111 Widget nameTextW, accTextW, mneTextW, cmdTextW, saveFirstBtn;
112 Widget loadAfterBtn, selInpBtn, winInpBtn, eitherInpBtn, noInpBtn;
113 Widget repInpBtn, sameOutBtn, dlogOutBtn, winOutBtn, dlogShell;
114 Widget managedList;
115 menuItemRec **menuItemsList;
116 int nMenuItems;
117 } userCmdDialog;
119 /* Structure for keeping track of hierarchical sub-menus durring user-menu
120 creation */
121 typedef struct {
122 char *name;
123 Widget menuPane;
124 } menuTreeItem;
126 /* Descriptions of the current user programmed menu items for re-generating
127 menus and processing shell, macro, and background menu selections */
128 static menuItemRec *ShellMenuItems[MAX_ITEMS_PER_MENU];
129 static int NShellMenuItems = 0;
130 static menuItemRec *MacroMenuItems[MAX_ITEMS_PER_MENU];
131 static int NMacroMenuItems = 0;
132 static menuItemRec *BGMenuItems[MAX_ITEMS_PER_MENU];
133 static int NBGMenuItems = 0;
135 /* Top level shells of the user-defined menu editing dialogs */
136 static Widget ShellCmdDialog = NULL;
137 static Widget MacroCmdDialog = NULL;
138 static Widget BGMenuCmdDialog = NULL;
140 /* Paste learn/replay sequence buttons in user defined menu editing dialogs
141 (for dimming and undimming externally when replay macro is available) */
142 static Widget MacroPasteReplayBtn = NULL;
143 static Widget BGMenuPasteReplayBtn = NULL;
145 static void editMacroOrBGMenu(WindowInfo *window, int dialogType);
146 static void dimSelDepItemsInMenu(Widget menuPane, menuItemRec **menuList,
147 int nMenuItems, int sensitive);
148 static void updateMenus(int menuType);
149 static Widget findInMenuTree(menuTreeItem *menuTree, int nTreeEntries,
150 const char *hierName);
151 static char *copySubstring(const char *string, int length);
152 static char *findStripLanguageMode(const char *menuItemName, int languageMode,
153 int *isDefaultLM);
154 static Widget createUserMenuItem(Widget menuPane, char *name, menuItemRec *f,
155 int index, XtCallbackProc cbRtn, XtPointer cbArg);
156 static Widget createUserSubMenu(Widget parent, char *label);
157 static void removeMenuItems(Widget menuPane);
158 static void updateMenu(WindowInfo *window, int menuType);
159 static void okCB(Widget w, XtPointer clientData, XtPointer callData);
160 static void applyCB(Widget w, XtPointer clientData, XtPointer callData);
161 static void checkCB(Widget w, XtPointer clientData, XtPointer callData);
162 static int checkMacro(userCmdDialog *ucd);
163 static int checkMacroText(char *macro, Widget errorParent, Widget errFocus);
164 static int applyDialogChanges(userCmdDialog *ucd);
165 static void dismissCB(Widget w, XtPointer clientData, XtPointer callData);
166 static void pasteReplayCB(Widget w, XtPointer clientData, XtPointer callData);
167 static void destroyCB(Widget w, XtPointer clientData, XtPointer callData);
168 static void accKeyCB(Widget w, XtPointer clientData, XKeyEvent *event);
169 static void sameOutCB(Widget w, XtPointer clientData, XtPointer callData);
170 static void shellMenuCB(Widget w, WindowInfo *window, XtPointer callData);
171 static void macroMenuCB(Widget w, WindowInfo *window, XtPointer callData);
172 static void bgMenuCB(Widget w, WindowInfo *window, XtPointer callData) ;
173 static void accFocusCB(Widget w, XtPointer clientData, XtPointer callData);
174 static void accLoseFocusCB(Widget w, XtPointer clientData,
175 XtPointer callData);
176 static void updateDialogFields(menuItemRec *f, userCmdDialog *ucd);
177 static menuItemRec *readDialogFields(userCmdDialog *ucd, int silent);
178 static menuItemRec *copyMenuItemRec(menuItemRec *item);
179 static void freeMenuItemRec(menuItemRec *item);
180 static void *getDialogDataCB(void *oldItem, int explicitRequest, int *abort,
181 void *cbArg);
182 static void setDialogDataCB(void *item, void *cbArg);
183 static void freeItemCB(void *item);
184 static int dialogFieldsAreEmpty(userCmdDialog *ucd);
185 static void disableTextW(Widget textW);
186 static char *writeMenuItemString(menuItemRec **menuItems, int nItems,
187 int listType);
188 static int loadMenuItemString(char *inString, menuItemRec **menuItems,
189 int *nItems, int listType);
190 static void generateAcceleratorString(char *text, unsigned int modifiers,
191 KeySym keysym);
192 static void genAccelEventName(char *text, unsigned int modifiers,
193 KeySym keysym);
194 static int parseAcceleratorString(const char *string, unsigned int *modifiers,
195 KeySym *keysym);
196 static int parseError(const char *message);
197 static char *copyMacroToEnd(char **inPtr);
198 static void addTerminatingNewline(char **string);
201 ** Present a dialog for editing the user specified commands in the shell menu
203 void EditShellMenu(WindowInfo *window)
205 Widget form, accLabel, inpLabel, inpBox, outBox, outLabel;
206 Widget nameLabel, cmdLabel, okBtn, applyBtn, dismissBtn;
207 userCmdDialog *ucd;
208 XmString s1;
209 int ac, i;
210 Arg args[20];
212 /* if the dialog is already displayed, just pop it to the top and return */
213 if (ShellCmdDialog != NULL) {
214 RaiseShellWindow(ShellCmdDialog);
215 return;
218 /* Create a structure for keeping track of dialog state */
219 ucd = (userCmdDialog *)XtMalloc(sizeof(userCmdDialog));
220 ucd->window = window;
222 /* Set the dialog to operate on the Shell menu */
223 ucd->menuItemsList = (menuItemRec **)XtMalloc(sizeof(menuItemRec *) *
224 MAX_ITEMS_PER_MENU);
225 for (i=0; i<NShellMenuItems; i++)
226 ucd->menuItemsList[i] = copyMenuItemRec(ShellMenuItems[i]);
227 ucd->nMenuItems = NShellMenuItems;
228 ucd->dialogType = SHELL_CMDS;
230 ac = 0;
231 XtSetArg(args[ac], XmNdeleteResponse, XmDO_NOTHING); ac++;
232 XtSetArg(args[ac], XmNiconName, "NEdit Shell Menu"); ac++;
233 XtSetArg(args[ac], XmNtitle, "Shell Menu"); ac++;
234 ucd->dlogShell = CreateShellWithBestVis(APP_NAME, APP_CLASS,
235 applicationShellWidgetClass, TheDisplay, args, ac);
236 AddSmallIcon(ucd->dlogShell);
237 form = XtVaCreateManagedWidget("editShellCommands", xmFormWidgetClass,
238 ucd->dlogShell, XmNautoUnmanage, False,
239 XmNresizePolicy, XmRESIZE_NONE, NULL);
240 ShellCmdDialog = ucd->dlogShell;
241 XtAddCallback(form, XmNdestroyCallback, destroyCB, ucd);
242 AddMotifCloseCallback(ucd->dlogShell, dismissCB, ucd);
244 ac = 0;
245 XtSetArg(args[ac], XmNtopAttachment, XmATTACH_POSITION); ac++;
246 XtSetArg(args[ac], XmNtopPosition, 2); ac++;
247 XtSetArg(args[ac], XmNleftAttachment, XmATTACH_POSITION); ac++;
248 XtSetArg(args[ac], XmNleftPosition, LEFT_MARGIN_POS); ac++;
249 XtSetArg(args[ac], XmNrightAttachment, XmATTACH_POSITION); ac++;
250 XtSetArg(args[ac], XmNrightPosition, LIST_RIGHT-1); ac++;
251 XtSetArg(args[ac], XmNbottomAttachment, XmATTACH_POSITION); ac++;
252 XtSetArg(args[ac], XmNbottomPosition, SHELL_CMD_TOP); ac++;
253 ucd->managedList = CreateManagedList(form, "list", args, ac,
254 (void **)ucd->menuItemsList, &ucd->nMenuItems, MAX_ITEMS_PER_MENU,
255 20, getDialogDataCB, ucd, setDialogDataCB, ucd, freeItemCB);
257 ucd->loadAfterBtn = XtVaCreateManagedWidget("loadAfterBtn",
258 xmToggleButtonWidgetClass, form,
259 XmNlabelString, s1=MKSTRING("Re-load file after executing command"),
260 XmNmnemonic, 'R',
261 XmNalignment, XmALIGNMENT_BEGINNING,
262 XmNset, False,
263 XmNleftAttachment, XmATTACH_POSITION,
264 XmNleftPosition, LIST_RIGHT,
265 XmNrightAttachment, XmATTACH_POSITION,
266 XmNrightPosition, RIGHT_MARGIN_POS,
267 XmNbottomAttachment, XmATTACH_POSITION,
268 XmNbottomPosition, SHELL_CMD_TOP, NULL);
269 XmStringFree(s1);
270 ucd->saveFirstBtn = XtVaCreateManagedWidget("saveFirstBtn",
271 xmToggleButtonWidgetClass, form,
272 XmNlabelString, s1=MKSTRING("Save file before executing command"),
273 XmNmnemonic, 'f',
274 XmNalignment, XmALIGNMENT_BEGINNING,
275 XmNset, False,
276 XmNleftAttachment, XmATTACH_POSITION,
277 XmNleftPosition, LIST_RIGHT,
278 XmNrightAttachment, XmATTACH_POSITION,
279 XmNrightPosition, RIGHT_MARGIN_POS,
280 XmNbottomAttachment, XmATTACH_WIDGET,
281 XmNbottomWidget, ucd->loadAfterBtn, NULL);
282 XmStringFree(s1);
283 ucd->repInpBtn = XtVaCreateManagedWidget("repInpBtn",
284 xmToggleButtonWidgetClass, form,
285 XmNlabelString, s1=MKSTRING("Output replaces input"),
286 XmNmnemonic, 'f',
287 XmNalignment, XmALIGNMENT_BEGINNING,
288 XmNset, False,
289 XmNleftAttachment, XmATTACH_POSITION,
290 XmNleftPosition, LIST_RIGHT,
291 XmNrightAttachment, XmATTACH_POSITION,
292 XmNrightPosition, RIGHT_MARGIN_POS,
293 XmNbottomAttachment, XmATTACH_WIDGET,
294 XmNbottomWidget, ucd->saveFirstBtn, NULL);
295 XmStringFree(s1);
296 outBox = XtVaCreateManagedWidget("outBox", xmRowColumnWidgetClass, form,
297 XmNpacking, XmPACK_TIGHT,
298 XmNorientation, XmHORIZONTAL,
299 XmNradioBehavior, True,
300 XmNradioAlwaysOne, True,
301 XmNleftAttachment, XmATTACH_POSITION,
302 XmNleftPosition, LIST_RIGHT + 2,
303 XmNrightAttachment, XmATTACH_POSITION,
304 XmNrightPosition, RIGHT_MARGIN_POS,
305 XmNbottomAttachment, XmATTACH_WIDGET,
306 XmNbottomWidget, ucd->repInpBtn,
307 XmNbottomOffset, 4, NULL);
308 ucd->sameOutBtn = XtVaCreateManagedWidget("sameOutBtn",
309 xmToggleButtonWidgetClass, outBox,
310 XmNlabelString, s1=MKSTRING("same window"),
311 XmNmnemonic, 'm',
312 XmNalignment, XmALIGNMENT_BEGINNING,
313 XmNmarginHeight, 0,
314 XmNset, True, NULL);
315 XmStringFree(s1);
316 XtAddCallback(ucd->sameOutBtn, XmNvalueChangedCallback, sameOutCB, ucd);
317 ucd->dlogOutBtn = XtVaCreateManagedWidget("dlogOutBtn",
318 xmToggleButtonWidgetClass, outBox,
319 XmNlabelString, s1=MKSTRING("dialog"),
320 XmNmnemonic, 'g',
321 XmNalignment, XmALIGNMENT_BEGINNING,
322 XmNmarginHeight, 0,
323 XmNset, False, NULL);
324 XmStringFree(s1);
325 ucd->winOutBtn = XtVaCreateManagedWidget("winOutBtn", xmToggleButtonWidgetClass,
326 outBox,
327 XmNlabelString, s1=MKSTRING("new window"),
328 XmNmnemonic, 'n',
329 XmNalignment, XmALIGNMENT_BEGINNING,
330 XmNmarginHeight, 0,
331 XmNset, False, NULL);
332 XmStringFree(s1);
333 outLabel = XtVaCreateManagedWidget("outLabel", xmLabelGadgetClass, form,
334 XmNlabelString, s1=MKSTRING("Command Output:"),
335 XmNalignment, XmALIGNMENT_BEGINNING,
336 XmNmarginTop, 5,
337 XmNleftAttachment, XmATTACH_POSITION,
338 XmNleftPosition, LIST_RIGHT,
339 XmNrightAttachment, XmATTACH_POSITION,
340 XmNrightPosition, RIGHT_MARGIN_POS,
341 XmNbottomAttachment, XmATTACH_WIDGET,
342 XmNbottomWidget, outBox, NULL);
343 XmStringFree(s1);
345 inpBox = XtVaCreateManagedWidget("inpBox", xmRowColumnWidgetClass, form,
346 XmNpacking, XmPACK_TIGHT,
347 XmNorientation, XmHORIZONTAL,
348 XmNradioBehavior, True,
349 XmNradioAlwaysOne, True,
350 XmNleftAttachment, XmATTACH_POSITION,
351 XmNleftPosition, LIST_RIGHT + 2,
352 XmNrightAttachment, XmATTACH_POSITION,
353 XmNrightPosition, RIGHT_MARGIN_POS,
354 XmNbottomAttachment, XmATTACH_WIDGET,
355 XmNbottomWidget, outLabel, NULL);
356 ucd->selInpBtn = XtVaCreateManagedWidget("selInpBtn", xmToggleButtonWidgetClass,
357 inpBox,
358 XmNlabelString, s1=MKSTRING("selection"),
359 XmNmnemonic, 's',
360 XmNalignment, XmALIGNMENT_BEGINNING,
361 XmNmarginHeight, 0,
362 XmNset, True, NULL);
363 XmStringFree(s1);
364 ucd->winInpBtn = XtVaCreateManagedWidget("winInpBtn",
365 xmToggleButtonWidgetClass, inpBox,
366 XmNlabelString, s1=MKSTRING("window"),
367 XmNmnemonic, 'w',
368 XmNalignment, XmALIGNMENT_BEGINNING,
369 XmNmarginHeight, 0,
370 XmNset, False, NULL);
371 XmStringFree(s1);
372 ucd->eitherInpBtn = XtVaCreateManagedWidget("eitherInpBtn",
373 xmToggleButtonWidgetClass, inpBox,
374 XmNlabelString, s1=MKSTRING("either"),
375 XmNmnemonic, 't',
376 XmNalignment, XmALIGNMENT_BEGINNING,
377 XmNmarginHeight, 0,
378 XmNset, False, NULL);
379 XmStringFree(s1);
380 ucd->noInpBtn = XtVaCreateManagedWidget("noInpBtn",
381 xmToggleButtonWidgetClass, inpBox,
382 XmNlabelString, s1=MKSTRING("none"),
383 XmNmnemonic, 'o',
384 XmNalignment, XmALIGNMENT_BEGINNING,
385 XmNmarginHeight, 0,
386 XmNset, False, NULL);
387 XmStringFree(s1);
388 inpLabel = XtVaCreateManagedWidget("inpLabel", xmLabelGadgetClass, form,
389 XmNlabelString, s1=MKSTRING("Command Input:"),
390 XmNalignment, XmALIGNMENT_BEGINNING,
391 XmNmarginTop, 5,
392 XmNleftAttachment, XmATTACH_POSITION,
393 XmNleftPosition, LIST_RIGHT,
394 XmNrightAttachment, XmATTACH_POSITION,
395 XmNrightPosition, RIGHT_MARGIN_POS,
396 XmNbottomAttachment, XmATTACH_WIDGET,
397 XmNbottomWidget, inpBox, NULL);
398 XmStringFree(s1);
400 ucd->mneTextW = XtVaCreateManagedWidget("mne", xmTextWidgetClass, form,
401 XmNcolumns, 1,
402 XmNmaxLength, 1,
403 XmNleftAttachment, XmATTACH_POSITION,
404 XmNleftPosition, RIGHT_MARGIN_POS-10,
405 XmNrightAttachment, XmATTACH_POSITION,
406 XmNrightPosition, RIGHT_MARGIN_POS,
407 XmNbottomAttachment, XmATTACH_WIDGET,
408 XmNbottomWidget, inpLabel, NULL);
409 RemapDeleteKey(ucd->mneTextW);
411 ucd->accTextW = XtVaCreateManagedWidget("acc", xmTextWidgetClass, form,
412 XmNcolumns, 12,
413 XmNmaxLength, MAX_ACCEL_LEN-1,
414 XmNcursorPositionVisible, False,
415 XmNleftAttachment, XmATTACH_POSITION,
416 XmNleftPosition, LIST_RIGHT,
417 XmNrightAttachment, XmATTACH_POSITION,
418 XmNrightPosition, RIGHT_MARGIN_POS-15,
419 XmNbottomAttachment, XmATTACH_WIDGET,
420 XmNbottomWidget, inpLabel, NULL);
421 XtAddEventHandler(ucd->accTextW, KeyPressMask, False,
422 (XtEventHandler)accKeyCB, ucd);
423 XtAddCallback(ucd->accTextW, XmNfocusCallback, accFocusCB, ucd);
424 XtAddCallback(ucd->accTextW, XmNlosingFocusCallback, accLoseFocusCB, ucd);
425 accLabel = XtVaCreateManagedWidget("accLabel", xmLabelGadgetClass, form,
426 XmNlabelString, s1=MKSTRING("Accelerator"),
427 XmNmnemonic, 'l',
428 XmNuserData, ucd->accTextW,
429 XmNalignment, XmALIGNMENT_BEGINNING,
430 XmNmarginTop, 5,
431 XmNleftAttachment, XmATTACH_POSITION,
432 XmNleftPosition, LIST_RIGHT,
433 XmNrightAttachment, XmATTACH_POSITION,
434 XmNrightPosition, LIST_RIGHT + 24,
435 XmNbottomAttachment, XmATTACH_WIDGET,
436 XmNbottomWidget, ucd->mneTextW, NULL);
437 XmStringFree(s1);
439 XtVaCreateManagedWidget("mneLabel", xmLabelGadgetClass, form,
440 XmNlabelString, s1=MKSTRING("Mnemonic"),
441 XmNmnemonic, 'i',
442 XmNuserData, ucd->mneTextW,
443 XmNalignment, XmALIGNMENT_END,
444 XmNmarginTop, 5,
445 XmNleftAttachment, XmATTACH_POSITION,
446 XmNleftPosition, LIST_RIGHT + 24,
447 XmNrightAttachment, XmATTACH_POSITION,
448 XmNrightPosition, RIGHT_MARGIN_POS,
449 XmNbottomAttachment, XmATTACH_WIDGET,
450 XmNbottomWidget, ucd->mneTextW, NULL);
451 XmStringFree(s1);
453 ucd->nameTextW = XtVaCreateManagedWidget("name", xmTextWidgetClass, form,
454 XmNleftAttachment, XmATTACH_POSITION,
455 XmNleftPosition, LIST_RIGHT,
456 XmNrightAttachment, XmATTACH_POSITION,
457 XmNrightPosition, RIGHT_MARGIN_POS,
458 XmNbottomAttachment, XmATTACH_WIDGET,
459 XmNbottomWidget, accLabel, NULL);
460 RemapDeleteKey(ucd->nameTextW);
462 nameLabel = XtVaCreateManagedWidget("nameLabel", xmLabelGadgetClass, form,
463 XmNlabelString, s1=MKSTRING("Menu Entry"),
464 XmNmnemonic, 'y',
465 XmNuserData, ucd->nameTextW,
466 XmNalignment, XmALIGNMENT_BEGINNING,
467 XmNmarginTop, 5,
468 XmNleftAttachment, XmATTACH_POSITION,
469 XmNleftPosition, LIST_RIGHT,
470 XmNbottomAttachment, XmATTACH_WIDGET,
471 XmNbottomWidget, ucd->nameTextW, NULL);
472 XmStringFree(s1);
474 XtVaCreateManagedWidget("nameNotes", xmLabelGadgetClass, form,
475 XmNlabelString, s1=MKSTRING("(> for sub-menu, @ language mode)"),
476 XmNalignment, XmALIGNMENT_END,
477 XmNmarginTop, 5,
478 XmNleftAttachment, XmATTACH_WIDGET,
479 XmNleftWidget, nameLabel,
480 XmNrightAttachment, XmATTACH_POSITION,
481 XmNrightPosition, RIGHT_MARGIN_POS,
482 XmNbottomAttachment, XmATTACH_WIDGET,
483 XmNbottomWidget, ucd->nameTextW, NULL);
484 XmStringFree(s1);
486 XtVaCreateManagedWidget("topLabel", xmLabelGadgetClass, form,
487 XmNlabelString, s1=MKSTRING(
488 "Select a shell menu item from the list at left.\n\
489 Select \"New\" to add a new command to the menu."),
490 XmNtopAttachment, XmATTACH_POSITION,
491 XmNtopPosition, 2,
492 XmNleftAttachment, XmATTACH_POSITION,
493 XmNleftPosition, LIST_RIGHT,
494 XmNrightAttachment, XmATTACH_POSITION,
495 XmNrightPosition, RIGHT_MARGIN_POS,
496 XmNbottomAttachment, XmATTACH_WIDGET,
497 XmNbottomWidget, nameLabel, NULL);
498 XmStringFree(s1);
500 cmdLabel = XtVaCreateManagedWidget("cmdLabel", xmLabelGadgetClass, form,
501 XmNlabelString, s1=MKSTRING("Shell Command to Execute"),
502 XmNmnemonic, 'x',
503 XmNalignment, XmALIGNMENT_BEGINNING,
504 XmNmarginTop, 5,
505 XmNtopAttachment, XmATTACH_POSITION,
506 XmNtopPosition, SHELL_CMD_TOP,
507 XmNleftAttachment, XmATTACH_POSITION,
508 XmNleftPosition, LEFT_MARGIN_POS, NULL);
509 XmStringFree(s1);
510 XtVaCreateManagedWidget("cmdLabel", xmLabelGadgetClass, form,
511 XmNlabelString, s1=MKSTRING("(% expands to current filename, # to line number)"),
512 XmNalignment, XmALIGNMENT_END,
513 XmNmarginTop, 5,
514 XmNtopAttachment, XmATTACH_POSITION,
515 XmNtopPosition, SHELL_CMD_TOP,
516 XmNleftAttachment, XmATTACH_WIDGET,
517 XmNleftWidget, cmdLabel,
518 XmNrightAttachment, XmATTACH_POSITION,
519 XmNrightPosition, RIGHT_MARGIN_POS, NULL);
520 XmStringFree(s1);
522 okBtn = XtVaCreateManagedWidget("ok",xmPushButtonWidgetClass,form,
523 XmNlabelString, s1=MKSTRING("OK"),
524 XmNleftAttachment, XmATTACH_POSITION,
525 XmNleftPosition, 13,
526 XmNrightAttachment, XmATTACH_POSITION,
527 XmNrightPosition, 29,
528 XmNbottomAttachment, XmATTACH_POSITION,
529 XmNbottomPosition, 99, NULL);
530 XtAddCallback(okBtn, XmNactivateCallback, okCB, ucd);
531 XmStringFree(s1);
533 applyBtn = XtVaCreateManagedWidget("apply",xmPushButtonWidgetClass,form,
534 XmNlabelString, s1=MKSTRING("Apply"),
535 XmNmnemonic, 'A',
536 XmNleftAttachment, XmATTACH_POSITION,
537 XmNleftPosition, 42,
538 XmNrightAttachment, XmATTACH_POSITION,
539 XmNrightPosition, 58,
540 XmNbottomAttachment, XmATTACH_POSITION,
541 XmNbottomPosition, 99, NULL);
542 XtAddCallback(applyBtn, XmNactivateCallback, applyCB, ucd);
543 XmStringFree(s1);
545 dismissBtn = XtVaCreateManagedWidget("dismiss",xmPushButtonWidgetClass,form,
546 XmNlabelString, s1=MKSTRING("Dismiss"),
547 XmNleftAttachment, XmATTACH_POSITION,
548 XmNleftPosition, 71,
549 XmNrightAttachment, XmATTACH_POSITION,
550 XmNrightPosition, 87,
551 XmNbottomAttachment, XmATTACH_POSITION,
552 XmNbottomPosition, 99, NULL);
553 XtAddCallback(dismissBtn, XmNactivateCallback, dismissCB, ucd);
554 XmStringFree(s1);
556 ac = 0;
557 XtSetArg(args[ac], XmNeditMode, XmMULTI_LINE_EDIT); ac++;
558 XtSetArg(args[ac], XmNscrollHorizontal, False); ac++;
559 XtSetArg(args[ac], XmNwordWrap, True); ac++;
560 XtSetArg(args[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++;
561 XtSetArg(args[ac], XmNtopWidget, cmdLabel); ac++;
562 XtSetArg(args[ac], XmNleftAttachment, XmATTACH_POSITION); ac++;
563 XtSetArg(args[ac], XmNleftPosition, LEFT_MARGIN_POS); ac++;
564 XtSetArg(args[ac], XmNrightAttachment, XmATTACH_POSITION); ac++;
565 XtSetArg(args[ac], XmNrightPosition, RIGHT_MARGIN_POS); ac++;
566 XtSetArg(args[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
567 XtSetArg(args[ac], XmNbottomWidget, okBtn); ac++;
568 XtSetArg(args[ac], XmNbottomOffset, 5); ac++;
569 ucd->cmdTextW = XmCreateScrolledText(form, "name", args, ac);
570 AddMouseWheelSupport(ucd->cmdTextW);
571 XtManageChild(ucd->cmdTextW);
572 MakeSingleLineTextW(ucd->cmdTextW);
573 RemapDeleteKey(ucd->cmdTextW);
574 XtVaSetValues(cmdLabel, XmNuserData, ucd->cmdTextW, NULL); /* for mnemonic */
576 /* Disable text input for the accelerator key widget, let the
577 event handler manage it instead */
578 disableTextW(ucd->accTextW);
580 /* initializs the dialog fields to match "New" list item */
581 updateDialogFields(NULL, ucd);
583 /* Set initial default button */
584 XtVaSetValues(form, XmNdefaultButton, okBtn, NULL);
585 XtVaSetValues(form, XmNcancelButton, dismissBtn, NULL);
587 /* Handle mnemonic selection of buttons and focus to dialog */
588 AddDialogMnemonicHandler(form, FALSE);
590 /* realize all of the widgets in the new window */
591 RealizeWithoutForcingPosition(ucd->dlogShell);
595 ** Present a dialogs for editing the user specified commands in the Macro
596 ** and background menus
598 void EditMacroMenu(WindowInfo *window)
600 editMacroOrBGMenu(window, MACRO_CMDS);
602 void EditBGMenu(WindowInfo *window)
604 editMacroOrBGMenu(window, BG_MENU_CMDS);
607 static void editMacroOrBGMenu(WindowInfo *window, int dialogType)
609 Widget form, accLabel, pasteReplayBtn;
610 Widget nameLabel, cmdLabel, okBtn, applyBtn, dismissBtn;
611 userCmdDialog *ucd;
612 char *title;
613 XmString s1;
614 int ac, i;
615 Arg args[20];
617 /* if the dialog is already displayed, just pop it to the top and return */
618 if (dialogType == MACRO_CMDS && MacroCmdDialog != NULL) {
619 RaiseShellWindow(MacroCmdDialog);
620 return;
622 if (dialogType == BG_MENU_CMDS && BGMenuCmdDialog != NULL) {
623 RaiseShellWindow(BGMenuCmdDialog);
624 return;
627 /* Create a structure for keeping track of dialog state */
628 ucd = (userCmdDialog *)XtMalloc(sizeof(userCmdDialog));
629 ucd->window = window;
631 /* Set the dialog to operate on the Macro menu */
632 ucd->menuItemsList = (menuItemRec **)XtMalloc(sizeof(menuItemRec **) *
633 MAX_ITEMS_PER_MENU);
634 if (dialogType == MACRO_CMDS) {
635 for (i=0; i<NMacroMenuItems; i++)
636 ucd->menuItemsList[i] = copyMenuItemRec(MacroMenuItems[i]);
637 ucd->nMenuItems = NMacroMenuItems;
638 } else { /* BG_MENU_CMDS */
639 for (i=0; i<NBGMenuItems; i++)
640 ucd->menuItemsList[i] = copyMenuItemRec(BGMenuItems[i]);
641 ucd->nMenuItems = NBGMenuItems;
643 ucd->dialogType = dialogType;
645 title = dialogType == MACRO_CMDS ? "Macro Commands" :
646 "Window Background Menu";
647 ac = 0;
648 XtSetArg(args[ac], XmNdeleteResponse, XmDO_NOTHING); ac++;
649 XtSetArg(args[ac], XmNiconName, title); ac++;
650 XtSetArg(args[ac], XmNtitle, title); ac++;
651 ucd->dlogShell = CreateShellWithBestVis(APP_NAME, APP_CLASS,
652 applicationShellWidgetClass, TheDisplay, args, ac);
653 AddSmallIcon(ucd->dlogShell);
654 form = XtVaCreateManagedWidget("editMacroCommands", xmFormWidgetClass,
655 ucd->dlogShell, XmNautoUnmanage, False,
656 XmNresizePolicy, XmRESIZE_NONE, NULL);
657 XtAddCallback(form, XmNdestroyCallback, destroyCB, ucd);
658 AddMotifCloseCallback(ucd->dlogShell, dismissCB, ucd);
660 ac = 0;
661 XtSetArg(args[ac], XmNtopAttachment, XmATTACH_POSITION); ac++;
662 XtSetArg(args[ac], XmNtopPosition, 2); ac++;
663 XtSetArg(args[ac], XmNleftAttachment, XmATTACH_POSITION); ac++;
664 XtSetArg(args[ac], XmNleftPosition, LEFT_MARGIN_POS); ac++;
665 XtSetArg(args[ac], XmNrightAttachment, XmATTACH_POSITION); ac++;
666 XtSetArg(args[ac], XmNrightPosition, LIST_RIGHT-1); ac++;
667 XtSetArg(args[ac], XmNbottomAttachment, XmATTACH_POSITION); ac++;
668 XtSetArg(args[ac], XmNbottomPosition, MACRO_CMD_TOP); ac++;
669 ucd->managedList = CreateManagedList(form, "list", args, ac,
670 (void **)ucd->menuItemsList, &ucd->nMenuItems, MAX_ITEMS_PER_MENU, 20,
671 getDialogDataCB, ucd, setDialogDataCB, ucd, freeItemCB);
673 ucd->selInpBtn = XtVaCreateManagedWidget("selInpBtn",
674 xmToggleButtonWidgetClass, form,
675 XmNlabelString, s1=MKSTRING("Requires Selection"),
676 XmNmnemonic, 'R',
677 XmNalignment, XmALIGNMENT_BEGINNING,
678 XmNmarginHeight, 0,
679 XmNset, False,
680 XmNleftAttachment, XmATTACH_POSITION,
681 XmNleftPosition, LIST_RIGHT,
682 XmNbottomAttachment, XmATTACH_POSITION,
683 XmNbottomPosition, MACRO_CMD_TOP, NULL);
684 XmStringFree(s1);
686 ucd->mneTextW = XtVaCreateManagedWidget("mne", xmTextWidgetClass, form,
687 XmNcolumns, 1,
688 XmNmaxLength, 1,
689 XmNleftAttachment, XmATTACH_POSITION,
690 XmNleftPosition, RIGHT_MARGIN_POS-21-5,
691 XmNrightAttachment, XmATTACH_POSITION,
692 XmNrightPosition, RIGHT_MARGIN_POS-21,
693 XmNbottomAttachment, XmATTACH_WIDGET,
694 XmNbottomWidget, ucd->selInpBtn,
695 XmNbottomOffset, 5, NULL);
696 RemapDeleteKey(ucd->mneTextW);
698 ucd->accTextW = XtVaCreateManagedWidget("acc", xmTextWidgetClass, form,
699 XmNcolumns, 12,
700 XmNmaxLength, MAX_ACCEL_LEN-1,
701 XmNcursorPositionVisible, False,
702 XmNleftAttachment, XmATTACH_POSITION,
703 XmNleftPosition, LIST_RIGHT,
704 XmNrightAttachment, XmATTACH_POSITION,
705 XmNrightPosition, RIGHT_MARGIN_POS-20-10,
706 XmNbottomAttachment, XmATTACH_WIDGET,
707 XmNbottomWidget, ucd->selInpBtn,
708 XmNbottomOffset, 5, NULL);
709 XtAddEventHandler(ucd->accTextW, KeyPressMask, False,
710 (XtEventHandler)accKeyCB, ucd);
711 XtAddCallback(ucd->accTextW, XmNfocusCallback, accFocusCB, ucd);
712 XtAddCallback(ucd->accTextW, XmNlosingFocusCallback, accLoseFocusCB, ucd);
714 accLabel = XtVaCreateManagedWidget("accLabel", xmLabelGadgetClass, form,
715 XmNlabelString, s1=MKSTRING("Accelerator"),
716 XmNmnemonic, 'l',
717 XmNuserData, ucd->accTextW,
718 XmNalignment, XmALIGNMENT_BEGINNING,
719 XmNmarginTop, 5,
720 XmNleftAttachment, XmATTACH_POSITION,
721 XmNleftPosition, LIST_RIGHT,
722 XmNrightAttachment, XmATTACH_POSITION,
723 XmNrightPosition, LIST_RIGHT + 22,
724 XmNbottomAttachment, XmATTACH_WIDGET,
725 XmNbottomWidget, ucd->mneTextW, NULL);
726 XmStringFree(s1);
728 XtVaCreateManagedWidget("mneLabel", xmLabelGadgetClass, form,
729 XmNlabelString, s1=MKSTRING("Mnemonic"),
730 XmNmnemonic, 'i',
731 XmNuserData, ucd->mneTextW,
732 XmNalignment, XmALIGNMENT_END,
733 XmNmarginTop, 5,
734 XmNleftAttachment, XmATTACH_POSITION,
735 XmNleftPosition, LIST_RIGHT + 22,
736 XmNrightAttachment, XmATTACH_POSITION,
737 XmNrightPosition, RIGHT_MARGIN_POS-21,
738 XmNbottomAttachment, XmATTACH_WIDGET,
739 XmNbottomWidget, ucd->mneTextW, NULL);
740 XmStringFree(s1);
742 pasteReplayBtn = XtVaCreateManagedWidget("pasteReplay",
743 xmPushButtonWidgetClass, form,
744 XmNlabelString, s1=MKSTRING("Paste Learn/\nReplay Macro"),
745 XmNmnemonic, 'P',
746 XmNsensitive, GetReplayMacro() != NULL,
747 XmNleftAttachment, XmATTACH_POSITION,
748 XmNleftPosition, RIGHT_MARGIN_POS-20,
749 XmNrightAttachment, XmATTACH_POSITION,
750 XmNrightPosition, RIGHT_MARGIN_POS,
751 XmNbottomAttachment, XmATTACH_POSITION,
752 XmNbottomPosition, MACRO_CMD_TOP, NULL);
753 XtAddCallback(pasteReplayBtn, XmNactivateCallback,
754 pasteReplayCB, ucd);
755 XmStringFree(s1);
757 ucd->nameTextW = XtVaCreateManagedWidget("name", xmTextWidgetClass, form,
758 XmNleftAttachment, XmATTACH_POSITION,
759 XmNleftPosition, LIST_RIGHT,
760 XmNrightAttachment, XmATTACH_POSITION,
761 XmNrightPosition, RIGHT_MARGIN_POS,
762 XmNbottomAttachment, XmATTACH_WIDGET,
763 XmNbottomWidget, accLabel, NULL);
764 RemapDeleteKey(ucd->nameTextW);
766 nameLabel = XtVaCreateManagedWidget("nameLabel", xmLabelGadgetClass, form,
767 XmNlabelString, s1=MKSTRING("Menu Entry"),
768 XmNmnemonic, 'y',
769 XmNuserData, ucd->nameTextW,
770 XmNalignment, XmALIGNMENT_BEGINNING,
771 XmNmarginTop, 5,
772 XmNleftAttachment, XmATTACH_POSITION,
773 XmNleftPosition, LIST_RIGHT,
774 XmNbottomAttachment, XmATTACH_WIDGET,
775 XmNbottomWidget, ucd->nameTextW, NULL);
776 XmStringFree(s1);
778 XtVaCreateManagedWidget("nameNotes", xmLabelGadgetClass, form,
779 XmNlabelString, s1=MKSTRING("(> for sub-menu, @ language mode)"),
780 XmNalignment, XmALIGNMENT_END,
781 XmNmarginTop, 5,
782 XmNleftAttachment, XmATTACH_WIDGET,
783 XmNleftWidget, nameLabel,
784 XmNrightAttachment, XmATTACH_POSITION,
785 XmNrightPosition, RIGHT_MARGIN_POS,
786 XmNbottomAttachment, XmATTACH_WIDGET,
787 XmNbottomWidget, ucd->nameTextW, NULL);
788 XmStringFree(s1);
790 XtVaCreateManagedWidget("topLabel", xmLabelGadgetClass, form,
791 XmNlabelString, s1=MKSTRING(
792 "Select a macro menu item from the list at left.\n\
793 Select \"New\" to add a new command to the menu."),
794 XmNtopAttachment, XmATTACH_POSITION,
795 XmNtopPosition, 2,
796 XmNleftAttachment, XmATTACH_POSITION,
797 XmNleftPosition, LIST_RIGHT,
798 XmNrightAttachment, XmATTACH_POSITION,
799 XmNrightPosition, RIGHT_MARGIN_POS,
800 XmNbottomAttachment, XmATTACH_WIDGET,
801 XmNbottomWidget, nameLabel, NULL);
802 XmStringFree(s1);
804 cmdLabel = XtVaCreateManagedWidget("cmdLabel", xmLabelGadgetClass, form,
805 XmNlabelString, s1=MKSTRING("Macro Command to Execute"),
806 XmNmnemonic, 'x',
807 XmNalignment, XmALIGNMENT_BEGINNING,
808 XmNmarginTop, 5,
809 XmNtopAttachment, XmATTACH_POSITION,
810 XmNtopPosition, MACRO_CMD_TOP,
811 XmNleftAttachment, XmATTACH_POSITION,
812 XmNleftPosition, LEFT_MARGIN_POS, NULL);
813 XmStringFree(s1);
815 okBtn = XtVaCreateManagedWidget("ok",xmPushButtonWidgetClass,form,
816 XmNlabelString, s1=MKSTRING("OK"),
817 XmNleftAttachment, XmATTACH_POSITION,
818 XmNleftPosition, 8,
819 XmNrightAttachment, XmATTACH_POSITION,
820 XmNrightPosition, 23,
821 XmNbottomAttachment, XmATTACH_POSITION,
822 XmNbottomPosition, 99, NULL);
823 XtAddCallback(okBtn, XmNactivateCallback, okCB, ucd);
824 XmStringFree(s1);
826 applyBtn = XtVaCreateManagedWidget("apply",xmPushButtonWidgetClass,form,
827 XmNlabelString, s1=MKSTRING("Apply"),
828 XmNmnemonic, 'A',
829 XmNleftAttachment, XmATTACH_POSITION,
830 XmNleftPosition, 31,
831 XmNrightAttachment, XmATTACH_POSITION,
832 XmNrightPosition, 46,
833 XmNbottomAttachment, XmATTACH_POSITION,
834 XmNbottomPosition, 99, NULL);
835 XtAddCallback(applyBtn, XmNactivateCallback, applyCB, ucd);
836 XmStringFree(s1);
838 applyBtn = XtVaCreateManagedWidget("check",xmPushButtonWidgetClass,form,
839 XmNlabelString, s1=MKSTRING("Check"),
840 XmNmnemonic, 'C',
841 XmNleftAttachment, XmATTACH_POSITION,
842 XmNleftPosition, 54,
843 XmNrightAttachment, XmATTACH_POSITION,
844 XmNrightPosition, 69,
845 XmNbottomAttachment, XmATTACH_POSITION,
846 XmNbottomPosition, 99, NULL);
847 XtAddCallback(applyBtn, XmNactivateCallback, checkCB, ucd);
848 XmStringFree(s1);
850 dismissBtn = XtVaCreateManagedWidget("dismiss",xmPushButtonWidgetClass,form,
851 XmNlabelString, s1=MKSTRING("Dismiss"),
852 XmNleftAttachment, XmATTACH_POSITION,
853 XmNleftPosition, 77,
854 XmNrightAttachment, XmATTACH_POSITION,
855 XmNrightPosition, 92,
856 XmNbottomAttachment, XmATTACH_POSITION,
857 XmNbottomPosition, 99, NULL);
858 XtAddCallback(dismissBtn, XmNactivateCallback, dismissCB, ucd);
859 XmStringFree(s1);
861 ac = 0;
862 XtSetArg(args[ac], XmNeditMode, XmMULTI_LINE_EDIT); ac++;
863 XtSetArg(args[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++;
864 XtSetArg(args[ac], XmNtopWidget, cmdLabel); ac++;
865 XtSetArg(args[ac], XmNleftAttachment, XmATTACH_POSITION); ac++;
866 XtSetArg(args[ac], XmNleftPosition, LEFT_MARGIN_POS); ac++;
867 XtSetArg(args[ac], XmNrightAttachment, XmATTACH_POSITION); ac++;
868 XtSetArg(args[ac], XmNrightPosition, RIGHT_MARGIN_POS); ac++;
869 XtSetArg(args[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
870 XtSetArg(args[ac], XmNbottomWidget, okBtn); ac++;
871 XtSetArg(args[ac], XmNbottomOffset, 5); ac++;
872 ucd->cmdTextW = XmCreateScrolledText(form, "name", args, ac);
873 AddMouseWheelSupport(ucd->cmdTextW);
874 XtManageChild(ucd->cmdTextW);
875 RemapDeleteKey(ucd->cmdTextW);
876 XtVaSetValues(cmdLabel, XmNuserData, ucd->cmdTextW, NULL); /* for mnemonic */
878 /* Disable text input for the accelerator key widget, let the
879 event handler manage it instead */
880 disableTextW(ucd->accTextW);
882 /* initializs the dialog fields to match "New" list item */
883 updateDialogFields(NULL, ucd);
885 /* Set initial default button */
886 XtVaSetValues(form, XmNdefaultButton, okBtn, NULL);
887 XtVaSetValues(form, XmNcancelButton, dismissBtn, NULL);
889 /* Handle mnemonic selection of buttons and focus to dialog */
890 AddDialogMnemonicHandler(form, FALSE);
892 /* Make widgets for top level shell and paste-replay buttons available
893 to other functions */
894 if (dialogType == MACRO_CMDS) {
895 MacroCmdDialog = ucd->dlogShell;
896 MacroPasteReplayBtn = pasteReplayBtn;
897 } else {
898 BGMenuCmdDialog = ucd->dlogShell;
899 BGMenuPasteReplayBtn = pasteReplayBtn;
902 /* Realize all of the widgets in the new dialog */
903 RealizeWithoutForcingPosition(ucd->dlogShell);
907 ** Update the Shell, Macro, and window background menus menu of window "window"
908 ** from the currently loaded command descriptions.
910 void UpdateShellMenu(WindowInfo *window)
912 updateMenu(window, SHELL_CMDS);
914 void UpdateMacroMenu(WindowInfo *window)
916 updateMenu(window, MACRO_CMDS);
918 void UpdateBGMenu(WindowInfo *window)
920 updateMenu(window, BG_MENU_CMDS);
924 ** Dim/undim buttons for pasting replay macros into macro and bg menu dialogs
926 void DimPasteReplayBtns(int sensitive)
928 if (MacroCmdDialog != NULL)
929 XtSetSensitive(MacroPasteReplayBtn, sensitive);
930 if (BGMenuCmdDialog != NULL)
931 XtSetSensitive(BGMenuPasteReplayBtn, sensitive);
935 ** Dim/undim user programmable menu items which depend on there being
936 ** a selection in their associated window.
938 void DimSelectionDepUserMenuItems(WindowInfo *window, int sensitive)
940 #ifndef VMS
941 dimSelDepItemsInMenu(window->shellMenuPane, ShellMenuItems,
942 NShellMenuItems, sensitive);
943 #endif /*VMS*/
944 dimSelDepItemsInMenu(window->macroMenuPane, MacroMenuItems,
945 NMacroMenuItems, sensitive);
946 dimSelDepItemsInMenu(window->bgMenuPane, BGMenuItems,
947 NBGMenuItems, sensitive);
950 static void dimSelDepItemsInMenu(Widget menuPane, menuItemRec **menuList,
951 int nMenuItems, int sensitive)
953 WidgetList items;
954 Widget subMenu;
955 XtPointer userData;
956 int n, index;
957 Cardinal nItems;
959 XtVaGetValues(menuPane, XmNchildren, &items, XmNnumChildren, &nItems, NULL);
960 for (n=0; n<(int)nItems; n++) {
961 XtVaGetValues(items[n], XmNuserData, &userData, NULL);
962 if (userData != (XtPointer)PERMANENT_MENU_ITEM) {
963 if (XtClass(items[n]) == xmCascadeButtonWidgetClass) {
964 XtVaGetValues(items[n], XmNsubMenuId, &subMenu, NULL);
965 dimSelDepItemsInMenu(subMenu, menuList, nMenuItems, sensitive);
966 } else {
967 index = (int)userData - 10;
968 if (index <0 || index >= nMenuItems)
969 return;
970 if (menuList[index]->input == FROM_SELECTION)
971 XtSetSensitive(items[n], sensitive);
978 ** Harmless kludge for making undo/redo menu items in background menu properly
979 ** sensitive (even though they're programmable) along with real undo item
980 ** in the Edit menu
982 void SetBGMenuUndoSensitivity(WindowInfo *window, int sensitive)
984 if (window->bgMenuUndoItem != NULL)
985 XtSetSensitive(window->bgMenuUndoItem, sensitive);
987 void SetBGMenuRedoSensitivity(WindowInfo *window, int sensitive)
989 if (window->bgMenuRedoItem != NULL)
990 XtSetSensitive(window->bgMenuRedoItem, sensitive);
994 ** Generate a text string for the preferences file describing the contents
995 ** of the shell cmd list. This string is not exactly of the form that it
996 ** can be read by LoadShellCmdsString, rather, it is what needs to be written
997 ** to a resource file such that it will read back in that form.
999 char *WriteShellCmdsString(void)
1001 return writeMenuItemString(ShellMenuItems, NShellMenuItems,
1002 SHELL_CMDS);
1006 ** Generate a text string for the preferences file describing the contents of
1007 ** the macro menu and background menu commands lists. These strings are not
1008 ** exactly of the form that it can be read by LoadMacroCmdsString, rather, it
1009 ** is what needs to be written to a resource file such that it will read back
1010 ** in that form.
1012 char *WriteMacroCmdsString(void)
1014 return writeMenuItemString(MacroMenuItems, NMacroMenuItems, MACRO_CMDS);
1017 char *WriteBGMenuCmdsString(void)
1019 return writeMenuItemString(BGMenuItems, NBGMenuItems, BG_MENU_CMDS);
1023 ** Read a string representing shell command menu items and add them to the
1024 ** internal list used for constructing shell menus
1026 int LoadShellCmdsString(char *inString)
1028 return loadMenuItemString(inString, ShellMenuItems, &NShellMenuItems,
1029 SHELL_CMDS);
1033 ** Read strings representing macro menu or background menu command menu items
1034 ** and add them to the internal lists used for constructing menus
1036 int LoadMacroCmdsString(char *inString)
1038 return loadMenuItemString(inString, MacroMenuItems, &NMacroMenuItems,
1039 MACRO_CMDS);
1042 int LoadBGMenuCmdsString(char *inString)
1044 return loadMenuItemString(inString, BGMenuItems, &NBGMenuItems,
1045 BG_MENU_CMDS);
1049 ** Search through the shell menu and execute the first command with menu item
1050 ** name "itemName". Returns True on successs and False on failure.
1052 #ifndef VMS
1053 int DoNamedShellMenuCmd(WindowInfo *window, const char *itemName, int fromMacro)
1055 int i;
1057 for (i=0; i<NShellMenuItems; i++) {
1058 if (!strcmp(ShellMenuItems[i]->name, itemName)) {
1059 if (ShellMenuItems[i]->output == TO_SAME_WINDOW &&
1060 CheckReadOnly(window))
1061 return False;
1062 DoShellMenuCmd(window, ShellMenuItems[i]->cmd,
1063 ShellMenuItems[i]->input, ShellMenuItems[i]->output,
1064 ShellMenuItems[i]->repInput, ShellMenuItems[i]->saveFirst,
1065 ShellMenuItems[i]->loadAfter, fromMacro);
1066 return True;
1069 return False;
1071 #endif /*VMS*/
1074 ** Search through the Macro or background menu and execute the first command
1075 ** with menu item name "itemName". Returns True on successs and False on
1076 ** failure.
1078 int DoNamedMacroMenuCmd(WindowInfo *window, const char *itemName)
1080 int i;
1082 for (i=0; i<NMacroMenuItems; i++) {
1083 if (!strcmp(MacroMenuItems[i]->name, itemName)) {
1084 DoMacro(window, MacroMenuItems[i]->cmd, "macro menu command");
1085 return True;
1088 return False;
1091 int DoNamedBGMenuCmd(WindowInfo *window, const char *itemName)
1093 int i;
1095 for (i=0; i<NBGMenuItems; i++) {
1096 if (!strcmp(BGMenuItems[i]->name, itemName)) {
1097 DoMacro(window, BGMenuItems[i]->cmd, "background menu macro");
1098 return True;
1101 return False;
1105 ** Update all of the Shell or Macro menus of all editor windows.
1107 static void updateMenus(int menuType)
1109 WindowInfo *w;
1111 for (w=WindowList; w!=NULL; w=w->next)
1112 updateMenu(w, menuType);
1116 ** Updates either the Shell menu or the Macro menu of "window", depending on
1117 ** value of "menuType"
1119 static void updateMenu(WindowInfo *window, int menuType)
1121 Widget btn, menuPane, subPane, newSubPane;
1122 int nListItems, n;
1123 menuItemRec *f, **itemList;
1124 menuTreeItem *menuTree;
1125 int i, nTreeEntries, isDefaultLM;
1126 char *hierName, *namePtr, *subMenuName, *subSep, *strippedName, *name;
1128 /* Fetch the appropriate menu pane and item list for this menu type */
1129 if (menuType == SHELL_CMDS) {
1130 menuPane = window->shellMenuPane;
1131 itemList = ShellMenuItems;
1132 nListItems = NShellMenuItems;
1133 } else if (menuType == MACRO_CMDS) {
1134 menuPane = window->macroMenuPane;
1135 itemList = MacroMenuItems;
1136 nListItems = NMacroMenuItems;
1137 } else { /* BG_MENU_CMDS */
1138 menuPane = window->bgMenuPane;
1139 itemList = BGMenuItems;
1140 nListItems = NBGMenuItems;
1143 /* Remove all of the existing user commands from the menu */
1144 removeMenuItems(menuPane);
1146 /* Allocate storage for structures to help find sub-menus */
1147 menuTree = (menuTreeItem *)XtMalloc(sizeof(menuTreeItem) * nListItems);
1148 nTreeEntries = 0;
1150 /* Harmless kludge: undo and redo items are marked specially if found
1151 in the background menu, and used to dim/undim with edit menu */
1152 window->bgMenuUndoItem = NULL;
1153 window->bgMenuRedoItem = NULL;
1156 ** Add items to the list, creating hierarchical sub-menus as necessary,
1157 ** and skipping items not intended for this language mode
1159 for (n=0; n<nListItems; n++) {
1160 f = itemList[n];
1162 /* Eliminate items meant for other language modes, strip @ sign parts.
1163 If the language mode is "*", scan the list for an item with the
1164 same name and a language mode specified. If one is found, skip
1165 the item in favor of the exact match. */
1166 strippedName = findStripLanguageMode(f->name, window->languageMode,
1167 &isDefaultLM);
1168 if (strippedName == NULL)
1169 continue; /* not a valid entry for the language */
1170 if (isDefaultLM) {
1171 for (i=0; i<nListItems; i++) {
1172 name = findStripLanguageMode(itemList[i]->name,
1173 window->languageMode, &isDefaultLM);
1174 if (name!=NULL && !isDefaultLM && !strcmp(name, strippedName)) {
1175 XtFree(name); /* item with matching language overrides */
1176 break;
1178 XtFree(name);
1180 if (i != nListItems) {
1181 XtFree(strippedName);
1182 continue;
1186 /* create/find sub-menus, stripping off '>' until item name is
1187 reached, then create the menu item */
1188 namePtr = strippedName;
1189 subPane = menuPane;
1190 for (;;) {
1191 subSep = strchr(namePtr, '>');
1192 if (subSep == NULL) {
1193 btn = createUserMenuItem(subPane, namePtr, f, n,
1194 (XtCallbackProc)(menuType == SHELL_CMDS ? shellMenuCB :
1195 (menuType == MACRO_CMDS ? macroMenuCB : bgMenuCB)),
1196 (XtPointer)window);
1197 if (menuType == BG_MENU_CMDS && !strcmp(f->cmd, "undo()\n"))
1198 window->bgMenuUndoItem = btn;
1199 else if (menuType == BG_MENU_CMDS && !strcmp(f->cmd,"redo()\n"))
1200 window->bgMenuRedoItem = btn;
1201 UpdateAccelLockPatch(window->splitPane, btn);
1202 break;
1204 hierName = copySubstring(strippedName, subSep - strippedName);
1205 newSubPane = findInMenuTree(menuTree, nTreeEntries, hierName);
1206 if (newSubPane == NULL) {
1207 subMenuName = copySubstring(namePtr, subSep - namePtr);
1208 newSubPane = createUserSubMenu(subPane, subMenuName);
1209 XtFree(subMenuName);
1210 menuTree[nTreeEntries].name = hierName;
1211 menuTree[nTreeEntries++].menuPane = newSubPane;
1212 } else
1213 XtFree(hierName);
1214 subPane = newSubPane;
1215 namePtr = subSep + 1;
1217 XtFree(strippedName);
1220 /* Free the structure used to keep track of sub-menus durring creation */
1221 for (i=0; i<nTreeEntries; i++)
1222 XtFree(menuTree[i].name);
1223 XtFree((char *)menuTree);
1225 /* Set the proper sensitivity of items which may be dimmed */
1226 SetBGMenuUndoSensitivity(window, XtIsSensitive(window->undoItem));
1227 SetBGMenuRedoSensitivity(window, XtIsSensitive(window->redoItem));
1228 DimSelectionDepUserMenuItems(window, window->buffer->primary.selected);
1232 ** Find the widget corresponding to a hierarchical menu name (a>b>c...)
1234 static Widget findInMenuTree(menuTreeItem *menuTree, int nTreeEntries,
1235 const char *hierName)
1237 int i;
1239 for (i=0; i<nTreeEntries; i++)
1240 if (!strcmp(hierName, menuTree[i].name))
1241 return menuTree[i].menuPane;
1242 return NULL;
1245 static char *copySubstring(const char *string, int length)
1247 char *retStr = XtMalloc(length + 1);
1249 strncpy(retStr, string, length);
1250 retStr[length] = '\0';
1251 return retStr;
1255 ** Look for at signs (@) in the string menuItemName, and match them
1256 ** against the current language mode. If there are no @ signs, just
1257 ** return an allocated copy of menuItemName. If there are @ signs, match
1258 ** the following text against languageMode, and return NULL if none match,
1259 ** or an allocated copy of menuItemName stripped of @ parts. If the
1260 ** language name is "*", sets isDefaultLM to true.
1262 static char *findStripLanguageMode(const char *menuItemName, int languageMode,
1263 int *isDefaultLM)
1265 char *atPtr, *firstAtPtr, *endPtr;
1266 int lmNameLen;
1268 atPtr = firstAtPtr = strchr(menuItemName, '@');
1269 *isDefaultLM = False;
1270 if (atPtr == NULL)
1272 return XtNewString(menuItemName);
1274 if (!strcmp(atPtr+1, "*")) {
1275 /* only language is "*": this is for all but language specific macros */
1276 *isDefaultLM = True;
1277 return copySubstring(menuItemName, firstAtPtr-menuItemName);
1279 if (languageMode == PLAIN_LANGUAGE_MODE)
1280 return NULL;
1281 for (;;) {
1282 for(endPtr=atPtr+1; isalnum((unsigned char)*endPtr) || *endPtr=='_' ||
1283 *endPtr=='-' || *endPtr==' ' || *endPtr=='+' || *endPtr=='$' ||
1284 *endPtr=='#'; endPtr++);
1285 lmNameLen = endPtr-atPtr-1;
1286 if (!strncmp(LanguageModeName(languageMode), atPtr+1, lmNameLen) &&
1287 LanguageModeName(languageMode)[lmNameLen] == '\0')
1288 return copySubstring(menuItemName, firstAtPtr-menuItemName);
1289 atPtr = strchr(atPtr+1, '@');
1290 if (atPtr == NULL)
1291 return NULL;
1295 static Widget createUserMenuItem(Widget menuPane, char *name, menuItemRec *f,
1296 int index, XtCallbackProc cbRtn, XtPointer cbArg)
1298 XmString st1, st2;
1299 char accText[MAX_ACCEL_LEN], accKeys[MAX_ACCEL_LEN+5];
1300 Widget btn;
1302 generateAcceleratorString(accText, f->modifiers, f->keysym);
1303 genAccelEventName(accKeys, f->modifiers, f->keysym);
1304 st1=XmStringCreateSimple(name);
1305 st2=XmStringCreateSimple(accText);
1306 btn = XtVaCreateManagedWidget("cmd", xmPushButtonWidgetClass, menuPane,
1307 XmNlabelString, st1,
1308 XmNacceleratorText, st2,
1309 XmNaccelerator, accKeys,
1310 XmNmnemonic, f->mnemonic,
1311 XmNuserData, index+10, NULL);
1312 XtAddCallback(btn, XmNactivateCallback, cbRtn, cbArg);
1313 XmStringFree(st1);
1314 XmStringFree(st2);
1315 return btn;
1319 ** Add a user-defined sub-menu to an established pull-down menu, marking
1320 ** it's userData field with TEMPORARY_MENU_ITEM so it can be found and
1321 ** removed later if the menu is redefined. Returns the menu pane of the
1322 ** new sub menu.
1324 static Widget createUserSubMenu(Widget parent, char *label)
1326 Widget menu;
1327 XmString st1;
1328 static Arg args[1] = {{XmNuserData, (XtArgVal)TEMPORARY_MENU_ITEM}};
1330 menu = CreatePulldownMenu(parent, "userPulldown", args, 1);
1331 XtVaCreateManagedWidget("userCascade", xmCascadeButtonWidgetClass, parent,
1332 XmNlabelString, st1=XmStringCreateSimple(label),
1333 XmNsubMenuId, menu, XmNuserData, TEMPORARY_MENU_ITEM, NULL);
1334 XmStringFree(st1);
1335 return menu;
1338 static void removeMenuItems(Widget menuPane)
1340 WidgetList items, itemList;
1341 Widget subMenuID;
1342 XtPointer userData;
1343 int n;
1344 Cardinal nItems;
1346 /* Fetch the list of children from the menu pane, and make a copy
1347 (because the widget alters this list as you delete widgets) */
1348 XtVaGetValues(menuPane, XmNchildren, &itemList, XmNnumChildren, &nItems,
1349 NULL);
1350 items = (WidgetList)XtMalloc(sizeof(Widget) * nItems);
1351 memcpy(items, itemList, sizeof(Widget) * nItems);
1353 /* Delete all of the widgets not marked as PERMANENT_MENU_ITEM */
1354 for (n=0; n<(int)nItems; n++) {
1355 XtVaGetValues(items[n], XmNuserData, &userData, NULL);
1356 if (userData != (XtPointer)PERMANENT_MENU_ITEM) {
1357 if (XtClass(items[n]) == xmCascadeButtonWidgetClass) {
1358 XtVaGetValues(items[n], XmNsubMenuId, &subMenuID, NULL);
1359 removeMenuItems(subMenuID);
1360 #if XmVersion < 2000 /* Skipping this creates a memory and server resource
1361 leak (though both are reclaimed on window closing). In
1362 Motif 2.0 (and beyond?) there is a potential crash during
1363 phase 2 widget destruction in "SetCascadeField", and in
1364 Motif 1.2 there are free-memory reads. I would really like
1365 to be able to destroy this. */
1366 XtDestroyWidget(subMenuID);
1367 #endif
1368 } else /* remove accel. before destroy or lose it forever */
1369 XtVaSetValues(items[n], XmNaccelerator, NULL, NULL);
1370 /* unmanaging before destroying stops parent from displaying */
1371 XtUnmanageChild(items[n]);
1372 XtDestroyWidget(items[n]);
1375 XtFree((char *)items);
1378 static void dismissCB(Widget w, XtPointer clientData, XtPointer callData)
1380 userCmdDialog *ucd = (userCmdDialog *)clientData;
1382 /* Mark that there's no longer a (macro, bg, or shell) dialog up */
1383 if (ucd->dialogType == SHELL_CMDS)
1384 ShellCmdDialog = NULL;
1385 else if (ucd->dialogType == MACRO_CMDS)
1386 MacroCmdDialog = NULL;
1387 else
1388 BGMenuCmdDialog = NULL;
1390 /* pop down and destroy the dialog (memory for ucd is freed in the
1391 destroy callback) */
1392 XtDestroyWidget(ucd->dlogShell);
1395 static void okCB(Widget w, XtPointer clientData, XtPointer callData)
1397 userCmdDialog *ucd = (userCmdDialog *)clientData;
1399 /* Read the dialog fields, and update the menus */
1400 if (!applyDialogChanges(ucd))
1401 return;
1403 /* Mark that there's no longer a (macro, bg, or shell) dialog up */
1404 if (ucd->dialogType == SHELL_CMDS)
1405 ShellCmdDialog = NULL;
1406 else if (ucd->dialogType == MACRO_CMDS)
1407 MacroCmdDialog = NULL;
1408 else
1409 BGMenuCmdDialog = NULL;
1411 /* pop down and destroy the dialog (memory for ucd is freed in the
1412 destroy callback) */
1413 XtDestroyWidget(ucd->dlogShell);
1416 static void applyCB(Widget w, XtPointer clientData, XtPointer callData)
1418 applyDialogChanges((userCmdDialog *)clientData);
1421 static void checkCB(Widget w, XtPointer clientData, XtPointer callData)
1423 userCmdDialog *ucd = (userCmdDialog *)clientData;
1425 if (checkMacro(ucd))
1427 DialogF(DF_INF, ucd->dlogShell, 1, "Macro",
1428 "Macro compiled without error", "Dismiss");
1432 static int checkMacro(userCmdDialog *ucd)
1434 menuItemRec *f;
1436 f = readDialogFields(ucd, False);
1437 if (f == NULL)
1438 return False;
1439 if (!checkMacroText(f->cmd, ucd->dlogShell, ucd->cmdTextW)) {
1440 freeMenuItemRec(f);
1441 return False;
1443 return True;
1446 static int checkMacroText(char *macro, Widget errorParent, Widget errFocus)
1448 Program *prog;
1449 char *errMsg, *stoppedAt;
1451 prog = ParseMacro(macro, &errMsg, &stoppedAt);
1452 if (prog == NULL) {
1453 if (errorParent != NULL) {
1454 ParseError(errorParent, macro, stoppedAt, "macro", errMsg);
1455 XmTextSetInsertionPosition(errFocus, stoppedAt - macro);
1456 XmProcessTraversal(errFocus, XmTRAVERSE_CURRENT);
1458 return False;
1460 FreeProgram(prog);
1461 if (*stoppedAt != '\0') {
1462 if (errorParent != NULL) {
1463 ParseError(errorParent, macro, stoppedAt,"macro","syntax error");
1464 XmTextSetInsertionPosition(errFocus, stoppedAt - macro);
1465 XmProcessTraversal(errFocus, XmTRAVERSE_CURRENT);
1467 return False;
1469 return True;
1472 static int applyDialogChanges(userCmdDialog *ucd)
1474 int i;
1476 /* Get the current contents of the dialog fields */
1477 if (!UpdateManagedList(ucd->managedList, True))
1478 return False;
1480 /* Test compile the macro */
1481 if (ucd->dialogType == MACRO_CMDS)
1482 if (!checkMacro(ucd))
1483 return False;
1485 /* Update the menu information */
1486 if (ucd->dialogType == SHELL_CMDS) {
1487 for (i=0; i<NShellMenuItems; i++)
1488 freeMenuItemRec(ShellMenuItems[i]);
1489 for (i=0; i<ucd->nMenuItems; i++)
1490 ShellMenuItems[i] = copyMenuItemRec(ucd->menuItemsList[i]);
1491 NShellMenuItems = ucd->nMenuItems;
1492 } else if (ucd->dialogType == MACRO_CMDS) {
1493 for (i=0; i<NMacroMenuItems; i++)
1494 freeMenuItemRec(MacroMenuItems[i]);
1495 for (i=0; i<ucd->nMenuItems; i++)
1496 MacroMenuItems[i] = copyMenuItemRec(ucd->menuItemsList[i]);
1497 NMacroMenuItems = ucd->nMenuItems;
1498 } else { /* BG_MENU_CMDS */
1499 for (i=0; i<NBGMenuItems; i++)
1500 freeMenuItemRec(BGMenuItems[i]);
1501 for (i=0; i<ucd->nMenuItems; i++)
1502 BGMenuItems[i] = copyMenuItemRec(ucd->menuItemsList[i]);
1503 NBGMenuItems = ucd->nMenuItems;
1506 /* Update the menus themselves in all of the NEdit windows */
1507 updateMenus(ucd->dialogType);
1509 /* Note that preferences have been changed */
1510 MarkPrefsChanged();
1511 return True;
1514 static void pasteReplayCB(Widget w, XtPointer clientData, XtPointer callData)
1516 userCmdDialog *ucd = (userCmdDialog *)clientData;
1518 if (GetReplayMacro() == NULL)
1519 return;
1521 XmTextInsert(ucd->cmdTextW, XmTextGetInsertionPosition(ucd->cmdTextW),
1522 GetReplayMacro());
1525 static void destroyCB(Widget w, XtPointer clientData, XtPointer callData)
1527 userCmdDialog *ucd = (userCmdDialog *)clientData;
1528 int i;
1530 for (i=0; i<ucd->nMenuItems; i++)
1531 freeMenuItemRec(ucd->menuItemsList[i]);
1532 XtFree((char *)ucd->menuItemsList);
1533 XtFree((char *)ucd);
1536 static void accFocusCB(Widget w, XtPointer clientData, XtPointer callData)
1538 userCmdDialog *ucd = (userCmdDialog *)clientData;
1540 RemoveDialogMnemonicHandler(XtParent(ucd->accTextW));
1543 static void accLoseFocusCB(Widget w, XtPointer clientData, XtPointer callData)
1545 userCmdDialog *ucd = (userCmdDialog *)clientData;
1547 AddDialogMnemonicHandler(XtParent(ucd->accTextW), FALSE);
1550 static void accKeyCB(Widget w, XtPointer clientData, XKeyEvent *event)
1552 userCmdDialog *ucd = (userCmdDialog *)clientData;
1553 KeySym keysym = XLookupKeysym(event, 0);
1554 char outStr[MAX_ACCEL_LEN];
1556 /* Accept only real keys, not modifiers alone */
1557 if (IsModifierKey(keysym))
1558 return;
1560 /* Tab key means go to the next field, don't enter */
1561 if (keysym == XK_Tab)
1562 return;
1564 /* Beep and return if the modifiers are buttons or ones we don't support */
1565 if (event->state & ~(ShiftMask | LockMask | ControlMask | Mod1Mask |
1566 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask)) {
1567 XBell(TheDisplay, 0);
1568 return;
1571 /* Delete or backspace clears field */
1572 if (keysym == XK_Delete || keysym == XK_BackSpace) {
1573 XmTextSetString(ucd->accTextW, "");
1574 return;
1577 /* generate the string to use in the dialog field */
1578 generateAcceleratorString(outStr, event->state, keysym);
1580 /* Reject single character accelerators (a very simple way to eliminate
1581 un-modified letters and numbers) The goal is give users a clue that
1582 they're supposed to type the actual keys, not the name. This scheme
1583 is not rigorous and still allows accelerators like Comma. */
1584 if (strlen(outStr) == 1) {
1585 XBell(TheDisplay, 0);
1586 return;
1589 /* fill in the accelerator field in the dialog */
1590 XmTextSetString(ucd->accTextW, outStr);
1593 static void sameOutCB(Widget w, XtPointer clientData, XtPointer callData)
1595 XtSetSensitive(((userCmdDialog *)clientData)->repInpBtn,
1596 XmToggleButtonGetState(w));
1599 static void shellMenuCB(Widget w, WindowInfo *window, XtPointer callData)
1601 XtArgVal userData;
1602 int index;
1603 char *params[1];
1605 /* get the index of the shell command and verify that it's in range */
1606 XtVaGetValues(w, XmNuserData, &userData, NULL);
1607 index = (int)userData - 10;
1608 if (index <0 || index >= NShellMenuItems)
1609 return;
1611 params[0] = ShellMenuItems[index]->name;
1612 XtCallActionProc(window->lastFocus, "shell_menu_command",
1613 ((XmAnyCallbackStruct *)callData)->event, params, 1);
1616 static void macroMenuCB(Widget w, WindowInfo *window, XtPointer callData)
1618 XtArgVal userData;
1619 int index;
1620 char *params[1];
1622 /* Don't allow users to execute a macro command from the menu (or accel)
1623 if there's already a macro command executing. NEdit can't handle
1624 running multiple, independent uncoordinated, macros in the same
1625 window. Macros may invoke macro menu commands recursively via the
1626 macro_menu_command action proc, which is important for being able to
1627 repeat any operation, and to embed macros within eachother at any
1628 level, however, a call here with a macro running means that THE USER
1629 is explicitly invoking another macro via the menu or an accelerator. */
1630 if (window->macroCmdData != NULL) {
1631 XBell(TheDisplay, 0);
1632 return;
1635 /* get the index of the macro command and verify that it's in range */
1636 XtVaGetValues(w, XmNuserData, &userData, NULL);
1637 index = (int)userData - 10;
1638 if (index <0 || index >= NMacroMenuItems)
1639 return;
1641 params[0] = MacroMenuItems[index]->name;
1642 XtCallActionProc(window->lastFocus, "macro_menu_command",
1643 ((XmAnyCallbackStruct *)callData)->event, params, 1);
1646 static void bgMenuCB(Widget w, WindowInfo *window, XtPointer callData)
1648 XtArgVal userData;
1649 int index;
1650 char *params[1];
1652 /* Same remark as for macro menu commands (see above). */
1653 if (window->macroCmdData != NULL) {
1654 XBell(TheDisplay, 0);
1655 return;
1658 /* get the index of the macro command and verify that it's in range */
1659 XtVaGetValues(w, XmNuserData, &userData, NULL);
1660 index = (int)userData - 10;
1661 if (index <0 || index >= NBGMenuItems)
1662 return;
1664 params[0] = BGMenuItems[index]->name;
1665 XtCallActionProc(window->lastFocus, "bg_menu_command",
1666 ((XmAnyCallbackStruct *)callData)->event, params, 1);
1670 ** Update the name, accelerator, mnemonic, and command fields in the shell
1671 ** command or macro dialog to agree with the currently selected item in the
1672 ** menu item list.
1674 static void updateDialogFields(menuItemRec *f, userCmdDialog *ucd)
1676 char mneString[2], accString[MAX_ACCEL_LEN];
1678 /* fill in the name, accelerator, mnemonic, and command fields of the
1679 dialog for the newly selected item, or blank them if "New" is selected */
1680 if (f == NULL) {
1681 XmTextSetString(ucd->nameTextW, "");
1682 XmTextSetString(ucd->cmdTextW, "");
1683 XmTextSetString(ucd->accTextW, "");
1684 XmTextSetString(ucd->mneTextW, "");
1685 if (ucd->dialogType == SHELL_CMDS) {
1686 XmToggleButtonSetState(ucd->selInpBtn, True, True);
1687 XmToggleButtonSetState(ucd->sameOutBtn, True, True);
1688 XmToggleButtonSetState(ucd->repInpBtn, False, False);
1689 XtSetSensitive(ucd->repInpBtn, True);
1690 XmToggleButtonSetState(ucd->saveFirstBtn, False, False);
1691 XmToggleButtonSetState(ucd->loadAfterBtn, False, False);
1693 } else {
1694 mneString[0] = f->mnemonic;
1695 mneString[1] = '\0';
1696 generateAcceleratorString(accString, f->modifiers, f->keysym);
1697 XmTextSetString(ucd->nameTextW, f->name);
1698 XmTextSetString(ucd->cmdTextW, f->cmd);
1699 XmTextSetString(ucd->accTextW, accString);
1700 XmTextSetString(ucd->mneTextW, mneString);
1701 XmToggleButtonSetState(ucd->selInpBtn, f->input==FROM_SELECTION, False);
1702 if (ucd->dialogType == SHELL_CMDS) {
1703 XmToggleButtonSetState(ucd->winInpBtn, f->input == FROM_WINDOW,
1704 False);
1705 XmToggleButtonSetState(ucd->eitherInpBtn, f->input == FROM_EITHER,
1706 False);
1707 XmToggleButtonSetState(ucd->noInpBtn, f->input == FROM_NONE,
1708 False);
1709 XmToggleButtonSetState(ucd->sameOutBtn, f->output==TO_SAME_WINDOW,
1710 False);
1711 XmToggleButtonSetState(ucd->winOutBtn, f->output==TO_NEW_WINDOW,
1712 False);
1713 XmToggleButtonSetState(ucd->dlogOutBtn, f->output==TO_DIALOG,
1714 False);
1715 XmToggleButtonSetState(ucd->repInpBtn, f->repInput, False);
1716 XtSetSensitive(ucd->repInpBtn, f->output==TO_SAME_WINDOW);
1717 XmToggleButtonSetState(ucd->saveFirstBtn, f->saveFirst, False);
1718 XmToggleButtonSetState(ucd->loadAfterBtn, f->loadAfter, False);
1724 ** Read the name, accelerator, mnemonic, and command fields from the shell or
1725 ** macro commands dialog into a newly allocated menuItemRec. Returns a
1726 ** pointer to the new menuItemRec structure as the function value, or NULL on
1727 ** failure.
1729 static menuItemRec *readDialogFields(userCmdDialog *ucd, int silent)
1731 char *nameText, *cmdText, *mneText, *accText;
1732 menuItemRec *f;
1734 nameText = XmTextGetString(ucd->nameTextW);
1735 if (*nameText == '\0')
1737 if (!silent)
1739 DialogF(DF_WARN, ucd->dlogShell, 1, "Menu Entry",
1740 "Please specify a name\nfor the menu item", "Dismiss");
1741 XmProcessTraversal(ucd->nameTextW, XmTRAVERSE_CURRENT);
1743 XtFree(nameText);
1744 return NULL;
1747 if (strchr(nameText, ':'))
1749 if (!silent)
1751 DialogF(DF_WARN, ucd->dlogShell, 1, "Menu Entry",
1752 "Menu item names may not\ncontain colon (:) characters",
1753 "Dismiss");
1754 XmProcessTraversal(ucd->nameTextW, XmTRAVERSE_CURRENT);
1756 XtFree(nameText);
1757 return NULL;
1760 cmdText = XmTextGetString(ucd->cmdTextW);
1761 if (cmdText == NULL || *cmdText == '\0')
1763 if (!silent)
1765 DialogF(DF_WARN, ucd->dlogShell, 1, "Command to Execute",
1766 "Please specify %s to execute", "Dismiss",
1767 ucd->dialogType == SHELL_CMDS
1768 ? "shell command"
1769 : "macro command(s)");
1770 XmProcessTraversal(ucd->cmdTextW, XmTRAVERSE_CURRENT);
1772 XtFree(nameText);
1774 if (cmdText!=NULL)
1776 XtFree(cmdText);
1778 return NULL;
1781 if (ucd->dialogType == MACRO_CMDS || ucd->dialogType == BG_MENU_CMDS) {
1782 addTerminatingNewline(&cmdText);
1783 if (!checkMacroText(cmdText, silent ? NULL : ucd->dlogShell,
1784 ucd->cmdTextW)) {
1785 XtFree(nameText);
1786 XtFree(cmdText);
1787 return NULL;
1790 f = (menuItemRec *)XtMalloc(sizeof(menuItemRec));
1791 f->name = nameText;
1792 f->cmd = cmdText;
1793 if ((mneText = XmTextGetString(ucd->mneTextW)) != NULL) {
1794 f->mnemonic = mneText==NULL ? '\0' : mneText[0];
1795 XtFree(mneText);
1796 if (f->mnemonic == ':') /* colons mess up string parsing */
1797 f->mnemonic = '\0';
1799 if ((accText = XmTextGetString(ucd->accTextW)) != NULL) {
1800 parseAcceleratorString(accText, &f->modifiers, &f->keysym);
1801 XtFree(accText);
1803 if (ucd->dialogType == SHELL_CMDS) {
1804 if (XmToggleButtonGetState(ucd->selInpBtn))
1805 f->input = FROM_SELECTION;
1806 else if (XmToggleButtonGetState(ucd->winInpBtn))
1807 f->input = FROM_WINDOW;
1808 else if (XmToggleButtonGetState(ucd->eitherInpBtn))
1809 f->input = FROM_EITHER;
1810 else
1811 f->input = FROM_NONE;
1812 if (XmToggleButtonGetState(ucd->winOutBtn))
1813 f->output = TO_NEW_WINDOW;
1814 else if (XmToggleButtonGetState(ucd->dlogOutBtn))
1815 f->output = TO_DIALOG;
1816 else
1817 f->output = TO_SAME_WINDOW;
1818 f->repInput = XmToggleButtonGetState(ucd->repInpBtn);
1819 f->saveFirst = XmToggleButtonGetState(ucd->saveFirstBtn);
1820 f->loadAfter = XmToggleButtonGetState(ucd->loadAfterBtn);
1821 } else {
1822 f->input = XmToggleButtonGetState(ucd->selInpBtn) ? FROM_SELECTION :
1823 FROM_NONE;
1824 f->output = TO_SAME_WINDOW;
1825 f->repInput = False;
1826 f->saveFirst = False;
1827 f->loadAfter = False;
1829 return f;
1833 ** Copy a menu item record, and its associated memory
1835 static menuItemRec *copyMenuItemRec(menuItemRec *item)
1837 menuItemRec *newItem;
1839 newItem = (menuItemRec *)XtMalloc(sizeof(menuItemRec));
1840 *newItem = *item;
1841 newItem->name = XtMalloc(strlen(item->name)+1);
1842 strcpy(newItem->name, item->name);
1843 newItem->cmd = XtMalloc(strlen(item->cmd)+1);
1844 strcpy(newItem->cmd, item->cmd);
1845 return newItem;
1849 ** Free a menu item record, and its associated memory
1851 static void freeMenuItemRec(menuItemRec *item)
1853 XtFree(item->name);
1854 XtFree(item->cmd);
1855 XtFree((char *)item);
1859 ** Callbacks for managed-list operations
1861 static void *getDialogDataCB(void *oldItem, int explicitRequest, int *abort,
1862 void *cbArg)
1864 userCmdDialog *ucd = (userCmdDialog *)cbArg;
1865 menuItemRec *currentFields;
1867 /* If the dialog is currently displaying the "new" entry and the
1868 fields are empty, that's just fine */
1869 if (oldItem == NULL && dialogFieldsAreEmpty(ucd))
1870 return NULL;
1872 /* If there are no problems reading the data, just return it */
1873 currentFields = readDialogFields(ucd, True);
1874 if (currentFields != NULL)
1875 return (void *)currentFields;
1877 /* If user might not be expecting fields to be read, give more warning */
1878 if (!explicitRequest)
1880 if (DialogF(DF_WARN, ucd->dlogShell, 2, "Discard Entry",
1881 "Discard incomplete entry\nfor current menu item?", "Keep",
1882 "Discard") == 2)
1884 return oldItem == NULL
1885 ? NULL
1886 : (void *)copyMenuItemRec((menuItemRec *)oldItem);
1890 /* Do readDialogFields again without "silent" mode to display warning(s) */
1891 readDialogFields(ucd, False);
1892 *abort = True;
1893 return NULL;
1897 static void setDialogDataCB(void *item, void *cbArg)
1899 updateDialogFields((menuItemRec *)item, (userCmdDialog *)cbArg);
1902 static int dialogFieldsAreEmpty(userCmdDialog *ucd)
1904 return TextWidgetIsBlank(ucd->nameTextW) &&
1905 TextWidgetIsBlank(ucd->cmdTextW) &&
1906 TextWidgetIsBlank(ucd->accTextW) &&
1907 TextWidgetIsBlank(ucd->mneTextW) &&
1908 (ucd->dialogType != SHELL_CMDS || (
1909 XmToggleButtonGetState(ucd->selInpBtn) &&
1910 XmToggleButtonGetState(ucd->sameOutBtn) &&
1911 !XmToggleButtonGetState(ucd->repInpBtn) &&
1912 !XmToggleButtonGetState(ucd->saveFirstBtn) &&
1913 !XmToggleButtonGetState(ucd->loadAfterBtn)));
1916 static void freeItemCB(void *item)
1918 freeMenuItemRec((menuItemRec *)item);
1922 ** Gut a text widget of it's ability to process input
1924 static void disableTextW(Widget textW)
1926 static XtTranslations emptyTable = NULL;
1927 static char *emptyTranslations = "\
1928 <EnterWindow>: enter()\n\
1929 <Btn1Down>: grab-focus()\n\
1930 <Btn1Motion>: extend-adjust()\n\
1931 <Btn1Up>: extend-end()\n\
1932 Shift<Key>Tab: prev-tab-group()\n\
1933 Ctrl<Key>Tab: next-tab-group()\n\
1934 <Key>Tab: next-tab-group()\n\
1935 <LeaveWindow>: leave()\n\
1936 <FocusIn>: focusIn()\n\
1937 <FocusOut>: focusOut()\n\
1938 <Unmap>: unmap()\n";
1940 /* replace the translation table with the slimmed down one above */
1941 if (emptyTable == NULL)
1942 emptyTable = XtParseTranslationTable(emptyTranslations);
1943 XtVaSetValues(textW, XmNtranslations, emptyTable, NULL);
1946 static char *writeMenuItemString(menuItemRec **menuItems, int nItems,
1947 int listType)
1949 char *outStr, *outPtr, *c, accStr[MAX_ACCEL_LEN];
1950 menuItemRec *f;
1951 int i, length;
1953 /* determine the max. amount of memory needed for the returned string
1954 and allocate a buffer for composing the string */
1955 length = 0;
1956 for (i=0; i<nItems; i++) {
1957 f = menuItems[i];
1958 generateAcceleratorString(accStr, f->modifiers, f->keysym);
1959 length += strlen(f->name) * 2; /* allow for \n & \\ expansions */
1960 length += strlen(accStr);
1961 length += strlen(f->cmd) * 6; /* allow for \n & \\ expansions */
1962 length += 21; /* number of characters added below */
1964 length++; /* terminating null */
1965 outStr = XtMalloc(length);
1967 /* write the string */
1968 outPtr = outStr;
1969 *outPtr++ = '\\';
1970 *outPtr++ = '\n';
1971 for (i=0; i<nItems; i++) {
1972 f = menuItems[i];
1973 generateAcceleratorString(accStr, f->modifiers, f->keysym);
1974 *outPtr++ = '\t';
1975 for (c=f->name; *c!='\0'; ++c) { /* Copy the command name */
1976 if (*c == '\\') { /* changing backslashes to \\ */
1977 *outPtr++ = '\\';
1978 *outPtr++ = '\\';
1979 } else if (*c == '\n') { /* changing newlines to \n */
1980 *outPtr++ = '\\';
1981 *outPtr++ = 'n';
1982 } else {
1983 *outPtr++ = *c;
1986 *outPtr++ = ':';
1987 strcpy(outPtr, accStr);
1988 outPtr += strlen(accStr);
1989 *outPtr++ = ':';
1990 if (f->mnemonic != '\0')
1991 *outPtr++ = f->mnemonic;
1992 *outPtr++ = ':';
1993 if (listType == SHELL_CMDS) {
1994 if (f->input == FROM_SELECTION)
1995 *outPtr++ = 'I';
1996 else if (f->input == FROM_WINDOW)
1997 *outPtr++ = 'A';
1998 else if (f->input == FROM_EITHER)
1999 *outPtr++ = 'E';
2000 if (f->output == TO_DIALOG)
2001 *outPtr++ = 'D';
2002 else if (f->output == TO_NEW_WINDOW)
2003 *outPtr++ = 'W';
2004 if (f->repInput)
2005 *outPtr++ = 'X';
2006 if (f->saveFirst)
2007 *outPtr++ = 'S';
2008 if (f->loadAfter)
2009 *outPtr++ = 'L';
2010 *outPtr++ = ':';
2011 } else {
2012 if (f->input == FROM_SELECTION)
2013 *outPtr++ = 'R';
2014 *outPtr++ = ':';
2015 *outPtr++ = ' ';
2016 *outPtr++ = '{';
2018 *outPtr++ = '\\';
2019 *outPtr++ = 'n';
2020 *outPtr++ = '\\';
2021 *outPtr++ = '\n';
2022 *outPtr++ = '\t';
2023 *outPtr++ = '\t';
2024 for (c=f->cmd; *c!='\0'; c++) { /* Copy the command string, changing */
2025 if (*c == '\\') { /* backslashes to double backslashes */
2026 *outPtr++ = '\\'; /* and newlines to backslash-n's, */
2027 *outPtr++ = '\\'; /* followed by real newlines and tab */
2028 } else if (*c == '\n') {
2029 *outPtr++ = '\\';
2030 *outPtr++ = 'n';
2031 *outPtr++ = '\\';
2032 *outPtr++ = '\n';
2033 *outPtr++ = '\t';
2034 *outPtr++ = '\t';
2035 } else
2036 *outPtr++ = *c;
2038 if (listType == MACRO_CMDS || listType == BG_MENU_CMDS) {
2039 if (*(outPtr-1) == '\t') outPtr--;
2040 *outPtr++ = '}';
2042 *outPtr++ = '\\';
2043 *outPtr++ = 'n';
2044 *outPtr++ = '\\';
2045 *outPtr++ = '\n';
2047 --outPtr;
2048 *--outPtr = '\0';
2049 return outStr;
2052 static int loadMenuItemString(char *inString, menuItemRec **menuItems,
2053 int *nItems, int listType)
2055 menuItemRec *f;
2056 char *cmdStr;
2057 char *inPtr = inString;
2058 char *nameStr, accStr[MAX_ACCEL_LEN], mneChar;
2059 KeySym keysym;
2060 unsigned int modifiers;
2061 int i, input, output, saveFirst, loadAfter, repInput;
2062 int nameLen, accLen, mneLen, cmdLen;
2064 for (;;) {
2066 /* remove leading whitespace */
2067 while (*inPtr == ' ' || *inPtr == '\t')
2068 inPtr++;
2070 /* read name field */
2071 nameLen = strcspn(inPtr, ":");
2072 if (nameLen == 0)
2073 return parseError("no name field");
2074 nameStr = XtMalloc(nameLen+1);
2075 strncpy(nameStr, inPtr, nameLen);
2076 nameStr[nameLen] = '\0';
2077 inPtr += nameLen;
2078 if (*inPtr == '\0')
2079 return parseError("end not expected");
2080 inPtr++;
2082 /* read accelerator field */
2083 accLen = strcspn(inPtr, ":");
2084 if (accLen >= MAX_ACCEL_LEN)
2085 return parseError("accelerator field too long");
2086 strncpy(accStr, inPtr, accLen);
2087 accStr[accLen] = '\0';
2088 inPtr += accLen;
2089 if (*inPtr == '\0')
2090 return parseError("end not expected");
2091 inPtr++;
2093 /* read menemonic field */
2094 mneLen = strcspn(inPtr, ":");
2095 if (mneLen > 1)
2096 return parseError("mnemonic field too long");
2097 if (mneLen == 1)
2098 mneChar = *inPtr++;
2099 else
2100 mneChar = '\0';
2101 inPtr++;
2102 if (*inPtr == '\0')
2103 return parseError("end not expected");
2105 /* read flags field */
2106 input = FROM_NONE;
2107 output = TO_SAME_WINDOW;
2108 repInput = False;
2109 saveFirst = False;
2110 loadAfter = False;
2111 for (; *inPtr != ':'; inPtr++) {
2112 if (listType == SHELL_CMDS) {
2113 if (*inPtr == 'I')
2114 input = FROM_SELECTION;
2115 else if (*inPtr == 'A')
2116 input = FROM_WINDOW;
2117 else if (*inPtr == 'E')
2118 input = FROM_EITHER;
2119 else if (*inPtr == 'W')
2120 output = TO_NEW_WINDOW;
2121 else if (*inPtr == 'D')
2122 output = TO_DIALOG;
2123 else if (*inPtr == 'X')
2124 repInput = True;
2125 else if (*inPtr == 'S')
2126 saveFirst = True;
2127 else if (*inPtr == 'L')
2128 loadAfter = True;
2129 else
2130 return parseError("unreadable flag field");
2131 } else {
2132 if (*inPtr == 'R')
2133 input = FROM_SELECTION;
2134 else
2135 return parseError("unreadable flag field");
2138 inPtr++;
2140 /* read command field */
2141 if (listType == SHELL_CMDS) {
2142 if (*inPtr++ != '\n')
2143 return parseError("command must begin with newline");
2144 while (*inPtr == ' ' || *inPtr == '\t') /* leading whitespace */
2145 inPtr++;
2146 cmdLen = strcspn(inPtr, "\n");
2147 if (cmdLen == 0)
2148 return parseError("shell command field is empty");
2149 cmdStr = XtMalloc(cmdLen+1);
2150 strncpy(cmdStr, inPtr, cmdLen);
2151 cmdStr[cmdLen] = '\0';
2152 inPtr += cmdLen;
2153 } else {
2154 cmdStr = copyMacroToEnd(&inPtr);
2155 if (cmdStr == NULL)
2156 return False;
2158 while (*inPtr == ' ' || *inPtr == '\t' || *inPtr == '\n')
2159 inPtr++; /* skip trailing whitespace & newline */
2161 /* parse the accelerator field */
2162 if (!parseAcceleratorString(accStr, &modifiers, &keysym))
2163 return parseError("couldn't read accelerator field");
2165 /* create a menu item record */
2166 f = (menuItemRec *)XtMalloc(sizeof(menuItemRec));
2167 f->name = nameStr;
2168 f->cmd = cmdStr;
2169 f->mnemonic = mneChar;
2170 f->modifiers = modifiers;
2171 f->input = input;
2172 f->output = output;
2173 f->repInput = repInput;
2174 f->saveFirst = saveFirst;
2175 f->loadAfter = loadAfter;
2176 f->keysym = keysym;
2178 /* add/replace menu record in the list */
2179 for (i=0; i < *nItems; i++) {
2180 if (!strcmp(menuItems[i]->name, f->name)) {
2181 freeMenuItemRec(menuItems[i]);
2182 menuItems[i] = f;
2183 break;
2186 if (i == *nItems)
2187 menuItems[(*nItems)++] = f;
2189 /* end of string in proper place */
2190 if (*inPtr == '\0')
2191 return True;
2195 static int parseError(const char *message)
2197 fprintf(stderr, "NEdit: Parse error in user defined menu item, %s\n",
2198 message);
2199 return False;
2203 ** Create a text string representing an accelerator for the dialog,
2204 ** the shellCommands or macroCommands resource, and for the menu item.
2206 static void generateAcceleratorString(char *text, unsigned int modifiers,
2207 KeySym keysym)
2209 char *shiftStr = "", *ctrlStr = "", *altStr = "";
2210 char *mod2Str = "", *mod3Str = "", *mod4Str = "", *mod5Str = "";
2211 char keyName[20];
2212 Modifiers numLockMask = GetNumLockModMask(TheDisplay);
2214 /* if there's no accelerator, generate an empty string */
2215 if (keysym == NoSymbol) {
2216 *text = '\0';
2217 return;
2221 /* Translate the modifiers into strings.
2222 Lock and NumLock are always ignored (see util/misc.c),
2223 so we don't display them either. */
2224 if (modifiers & ShiftMask)
2225 shiftStr = "Shift+";
2226 if (modifiers & ControlMask)
2227 ctrlStr = "Ctrl+";
2228 if (modifiers & Mod1Mask)
2229 altStr = "Alt+";
2230 if ((modifiers & Mod2Mask) && (Mod2Mask != numLockMask))
2231 mod2Str = "Mod2+";
2232 if ((modifiers & Mod3Mask) && (Mod3Mask != numLockMask))
2233 mod3Str = "Mod3+";
2234 if ((modifiers & Mod4Mask) && (Mod4Mask != numLockMask))
2235 mod4Str = "Mod4+";
2236 if ((modifiers & Mod5Mask) && (Mod5Mask != numLockMask))
2237 mod5Str = "Mod5+";
2239 /* for a consistent look to the accelerator names in the menus,
2240 capitalize the first letter of the keysym */
2241 strcpy(keyName, XKeysymToString(keysym));
2242 *keyName = toupper(*keyName);
2244 /* concatenate the strings together */
2245 sprintf(text, "%s%s%s%s%s%s%s%s", shiftStr, ctrlStr, altStr,
2246 mod2Str, mod3Str, mod4Str, mod5Str, keyName);
2250 ** Create a translation table event description string for the menu
2251 ** XmNaccelerator resource.
2253 static void genAccelEventName(char *text, unsigned int modifiers,
2254 KeySym keysym)
2256 char *shiftStr = "", *lockStr = "", *ctrlStr = "", *altStr = "";
2257 char *mod2Str = "", *mod3Str = "", *mod4Str = "", *mod5Str = "";
2259 /* if there's no accelerator, generate an empty string */
2260 if (keysym == NoSymbol) {
2261 *text = '\0';
2262 return;
2265 /* translate the modifiers into strings */
2266 if (modifiers & ShiftMask)
2267 shiftStr = "Shift ";
2268 if (modifiers & LockMask)
2269 lockStr = "Lock ";
2270 if (modifiers & ControlMask)
2271 ctrlStr = "Ctrl ";
2272 if (modifiers & Mod1Mask)
2273 altStr = "Alt ";
2274 if (modifiers & Mod2Mask)
2275 mod2Str = "Mod2 ";
2276 if (modifiers & Mod3Mask)
2277 mod3Str = "Mod3 ";
2278 if (modifiers & Mod4Mask)
2279 mod4Str = "Mod4 ";
2280 if (modifiers & Mod5Mask)
2281 mod5Str = "Mod5 ";
2283 /* put the modifiers together with the key name */
2284 sprintf(text, "%s%s%s%s%s%s%s%s<Key>%s",
2285 shiftStr, lockStr, ctrlStr, altStr,
2286 mod2Str, mod3Str, mod4Str, mod5Str,
2287 XKeysymToString(keysym));
2291 ** Read an accelerator name and put it into the form of a modifier mask
2292 ** and a KeySym code. Returns false if string can't be read
2293 ** ... does not handle whitespace in string (look at scanf)
2295 static int parseAcceleratorString(const char *string, unsigned int *modifiers,
2296 KeySym *keysym)
2298 int i, nFields, inputLength = strlen(string);
2299 char fields[10][MAX_ACCEL_LEN];
2301 /* a blank field means no accelerator */
2302 if (inputLength == 0) {
2303 *modifiers = 0;
2304 *keysym = NoSymbol;
2305 return True;
2308 /* limit the string length so no field strings will overflow */
2309 if (inputLength > MAX_ACCEL_LEN)
2310 return False;
2312 /* divide the input into '+' separated fields */
2313 nFields = sscanf(string, "%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]",
2314 fields[0], fields[1], fields[2], fields[3], fields[4], fields[5],
2315 fields[6], fields[7], fields[8], fields[9]);
2316 if (nFields == 0)
2317 return False;
2319 /* get the key name from the last field and translate it to a keysym.
2320 If the name is capitalized, try it lowercase as well, since some
2321 of the keysyms are "prettied up" by generateAcceleratorString */
2322 *keysym = XStringToKeysym(fields[nFields-1]);
2323 if (*keysym == NoSymbol) {
2324 *fields[nFields-1] = tolower(*fields[nFields-1]);
2325 *keysym = XStringToKeysym(fields[nFields-1]);
2326 if (*keysym == NoSymbol)
2327 return False;
2330 /* parse the modifier names from the rest of the fields */
2331 *modifiers = 0;
2332 for (i=0; i<nFields-1; i++) {
2333 if (!strcmp(fields[i], "Shift"))
2334 *modifiers |= ShiftMask;
2335 else if (!strcmp(fields[i], "Lock"))
2336 *modifiers |= LockMask;
2337 else if (!strcmp(fields[i], "Ctrl"))
2338 *modifiers |= ControlMask;
2339 /* comparision with "Alt" for compatibility with old .nedit files*/
2340 else if (!strcmp(fields[i], "Alt"))
2341 *modifiers |= Mod1Mask;
2342 else if (!strcmp(fields[i], "Mod2"))
2343 *modifiers |= Mod2Mask;
2344 else if (!strcmp(fields[i], "Mod3"))
2345 *modifiers |= Mod3Mask;
2346 else if (!strcmp(fields[i], "Mod4"))
2347 *modifiers |= Mod4Mask;
2348 else if (!strcmp(fields[i], "Mod5"))
2349 *modifiers |= Mod5Mask;
2350 else
2351 return False;
2354 /* all fields successfully parsed */
2355 return True;
2359 ** Scan text from "*inPtr" to the end of macro input (matching brace),
2360 ** advancing inPtr, and return macro text as function return value.
2362 ** This is kind of wastefull in that it throws away the compiled macro,
2363 ** to be re-generated from the text as needed, but compile time is
2364 ** negligible for most macros.
2366 static char *copyMacroToEnd(char **inPtr)
2368 char *retStr, *errMsg, *stoppedAt, *p, *retPtr;
2369 Program *prog;
2371 /* Skip over whitespace to find make sure there's a beginning brace
2372 to anchor the parse (if not, it will take the whole file) */
2373 *inPtr += strspn(*inPtr, " \t\n");
2374 if (**inPtr != '{') {
2375 ParseError(NULL, *inPtr, *inPtr-1, "macro menu item", "expecting '{'");
2376 return NULL;
2379 /* Parse the input */
2380 prog = ParseMacro(*inPtr, &errMsg, &stoppedAt);
2381 if (prog == NULL) {
2382 ParseError(NULL, *inPtr, stoppedAt, "macro menu item", errMsg);
2383 return NULL;
2385 FreeProgram(prog);
2387 /* Copy and return the body of the macro, stripping outer braces and
2388 extra leading tabs added by the writer routine */
2389 (*inPtr)++;
2390 *inPtr += strspn(*inPtr, " \t");
2391 if (**inPtr == '\n') (*inPtr)++;
2392 if (**inPtr == '\t') (*inPtr)++;
2393 if (**inPtr == '\t') (*inPtr)++;
2394 retPtr = retStr = XtMalloc(stoppedAt - *inPtr + 1);
2395 for (p = *inPtr; p < stoppedAt - 1; p++) {
2396 if (!strncmp(p, "\n\t\t", 3)) {
2397 *retPtr++ = '\n';
2398 p += 2;
2399 } else
2400 *retPtr++ = *p;
2402 if (*(retPtr-1) == '\t') retPtr--;
2403 *retPtr = '\0';
2404 *inPtr = stoppedAt;
2405 return retStr;
2409 ** If "*string" is not terminated with a newline character, reallocate the
2410 ** string and add one. (The macro language requires newline terminators for
2411 ** statements, but the text widget doesn't force it like the NEdit text buffer
2412 ** does, so this might avoid some confusion.)
2414 static void addTerminatingNewline(char **string)
2416 char *newString;
2417 int length;
2419 length = strlen(*string);
2420 if ((*string)[length-1] != '\n') {
2421 newString = XtMalloc(length + 2);
2422 strcpy(newString, *string);
2423 newString[length] = '\n';
2424 newString[length+1] = '\0';
2425 XtFree(*string);
2426 *string = newString;