Tab context menu via TK Soh, from SF patch 891679
[nedit.git] / source / userCmds.c
blob7bcf7dc5af99cb63ff92d063e6cf463dc982a100
1 static const char CVSID[] = "$Id: userCmds.c,v 1.40 2004/01/27 18:02:32 tksoh 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);
1260 XtVaSetValues(menuPane, XmNuserData,
1261 (void *)window->languageMode, NULL);
1265 ** Find the widget corresponding to a hierarchical menu name (a>b>c...)
1267 static Widget findInMenuTree(menuTreeItem *menuTree, int nTreeEntries,
1268 const char *hierName)
1270 int i;
1272 for (i=0; i<nTreeEntries; i++)
1273 if (!strcmp(hierName, menuTree[i].name))
1274 return menuTree[i].menuPane;
1275 return NULL;
1278 static char *copySubstring(const char *string, int length)
1280 char *retStr = XtMalloc(length + 1);
1282 strncpy(retStr, string, length);
1283 retStr[length] = '\0';
1284 return retStr;
1288 ** Look for at signs (@) in the string menuItemName, and match them
1289 ** against the current language mode. If there are no @ signs, just
1290 ** return an allocated copy of menuItemName. If there are @ signs, match
1291 ** the following text against languageMode, and return NULL if none match,
1292 ** or an allocated copy of menuItemName stripped of @ parts. If the
1293 ** language name is "*", sets isDefaultLM to true.
1295 static char *findStripLanguageMode(const char *menuItemName, int languageMode,
1296 int *isDefaultLM)
1298 char *atPtr, *firstAtPtr, *endPtr;
1299 int lmNameLen;
1301 atPtr = firstAtPtr = strchr(menuItemName, '@');
1302 *isDefaultLM = False;
1303 if (atPtr == NULL)
1305 return XtNewString(menuItemName);
1307 if (!strcmp(atPtr+1, "*")) {
1308 /* only language is "*": this is for all but language specific macros */
1309 *isDefaultLM = True;
1310 return copySubstring(menuItemName, firstAtPtr-menuItemName);
1312 if (languageMode == PLAIN_LANGUAGE_MODE)
1313 return NULL;
1314 for (;;) {
1315 for(endPtr=atPtr+1; isalnum((unsigned char)*endPtr) || *endPtr=='_' ||
1316 *endPtr=='-' || *endPtr==' ' || *endPtr=='+' || *endPtr=='$' ||
1317 *endPtr=='#'; endPtr++);
1318 lmNameLen = endPtr-atPtr-1;
1319 if (!strncmp(LanguageModeName(languageMode), atPtr+1, lmNameLen) &&
1320 LanguageModeName(languageMode)[lmNameLen] == '\0')
1321 return copySubstring(menuItemName, firstAtPtr-menuItemName);
1322 atPtr = strchr(atPtr+1, '@');
1323 if (atPtr == NULL)
1324 return NULL;
1328 static Widget createUserMenuItem(Widget menuPane, char *name, menuItemRec *f,
1329 int index, XtCallbackProc cbRtn, XtPointer cbArg)
1331 XmString st1, st2;
1332 char accText[MAX_ACCEL_LEN], accKeys[MAX_ACCEL_LEN+5];
1333 Widget btn;
1335 generateAcceleratorString(accText, f->modifiers, f->keysym);
1336 genAccelEventName(accKeys, f->modifiers, f->keysym);
1337 st1=XmStringCreateSimple(name);
1338 st2=XmStringCreateSimple(accText);
1339 btn = XtVaCreateManagedWidget("cmd", xmPushButtonWidgetClass, menuPane,
1340 XmNlabelString, st1,
1341 XmNacceleratorText, st2,
1342 XmNaccelerator, accKeys,
1343 XmNmnemonic, f->mnemonic,
1344 XmNuserData, index+10, NULL);
1345 XtAddCallback(btn, XmNactivateCallback, cbRtn, cbArg);
1346 XmStringFree(st1);
1347 XmStringFree(st2);
1348 return btn;
1352 ** Add a user-defined sub-menu to an established pull-down menu, marking
1353 ** it's userData field with TEMPORARY_MENU_ITEM so it can be found and
1354 ** removed later if the menu is redefined. Returns the menu pane of the
1355 ** new sub menu.
1357 static Widget createUserSubMenu(Widget parent, char *label)
1359 Widget menu;
1360 XmString st1;
1361 static Arg args[1] = {{XmNuserData, (XtArgVal)TEMPORARY_MENU_ITEM}};
1363 menu = CreatePulldownMenu(parent, "userPulldown", args, 1);
1364 XtVaCreateManagedWidget("userCascade", xmCascadeButtonWidgetClass, parent,
1365 XmNlabelString, st1=XmStringCreateSimple(label),
1366 XmNsubMenuId, menu, XmNuserData, TEMPORARY_MENU_ITEM, NULL);
1367 XmStringFree(st1);
1368 return menu;
1371 static void removeMenuItems(Widget menuPane)
1373 WidgetList items, itemList;
1374 Widget subMenuID;
1375 XtPointer userData;
1376 int n;
1377 Cardinal nItems;
1379 /* Fetch the list of children from the menu pane, and make a copy
1380 (because the widget alters this list as you delete widgets) */
1381 XtVaGetValues(menuPane, XmNchildren, &itemList, XmNnumChildren, &nItems,
1382 NULL);
1383 items = (WidgetList)XtMalloc(sizeof(Widget) * nItems);
1384 memcpy(items, itemList, sizeof(Widget) * nItems);
1386 /* Delete all of the widgets not marked as PERMANENT_MENU_ITEM */
1387 for (n=0; n<(int)nItems; n++) {
1388 XtVaGetValues(items[n], XmNuserData, &userData, NULL);
1389 if (userData != (XtPointer)PERMANENT_MENU_ITEM) {
1390 if (XtClass(items[n]) == xmCascadeButtonWidgetClass) {
1391 XtVaGetValues(items[n], XmNsubMenuId, &subMenuID, NULL);
1392 removeMenuItems(subMenuID);
1393 #if XmVersion < 2000 /* Skipping this creates a memory and server resource
1394 leak (though both are reclaimed on window closing). In
1395 Motif 2.0 (and beyond?) there is a potential crash during
1396 phase 2 widget destruction in "SetCascadeField", and in
1397 Motif 1.2 there are free-memory reads. I would really like
1398 to be able to destroy this. */
1399 XtDestroyWidget(subMenuID);
1400 #endif
1401 } else /* remove accel. before destroy or lose it forever */
1402 XtVaSetValues(items[n], XmNaccelerator, NULL, NULL);
1403 /* unmanaging before destroying stops parent from displaying */
1404 XtUnmanageChild(items[n]);
1405 XtDestroyWidget(items[n]);
1408 XtFree((char *)items);
1411 static void dismissCB(Widget w, XtPointer clientData, XtPointer callData)
1413 userCmdDialog *ucd = (userCmdDialog *)clientData;
1415 /* Mark that there's no longer a (macro, bg, or shell) dialog up */
1416 if (ucd->dialogType == SHELL_CMDS)
1417 ShellCmdDialog = NULL;
1418 else if (ucd->dialogType == MACRO_CMDS)
1419 MacroCmdDialog = NULL;
1420 else
1421 BGMenuCmdDialog = NULL;
1423 /* pop down and destroy the dialog (memory for ucd is freed in the
1424 destroy callback) */
1425 XtDestroyWidget(ucd->dlogShell);
1428 static void okCB(Widget w, XtPointer clientData, XtPointer callData)
1430 userCmdDialog *ucd = (userCmdDialog *)clientData;
1432 /* Read the dialog fields, and update the menus */
1433 if (!applyDialogChanges(ucd))
1434 return;
1436 /* Mark that there's no longer a (macro, bg, or shell) dialog up */
1437 if (ucd->dialogType == SHELL_CMDS)
1438 ShellCmdDialog = NULL;
1439 else if (ucd->dialogType == MACRO_CMDS)
1440 MacroCmdDialog = NULL;
1441 else
1442 BGMenuCmdDialog = NULL;
1444 /* pop down and destroy the dialog (memory for ucd is freed in the
1445 destroy callback) */
1446 XtDestroyWidget(ucd->dlogShell);
1449 static void applyCB(Widget w, XtPointer clientData, XtPointer callData)
1451 applyDialogChanges((userCmdDialog *)clientData);
1454 static void checkCB(Widget w, XtPointer clientData, XtPointer callData)
1456 userCmdDialog *ucd = (userCmdDialog *)clientData;
1458 if (checkMacro(ucd))
1460 DialogF(DF_INF, ucd->dlogShell, 1, "Macro",
1461 "Macro compiled without error", "Dismiss");
1465 static int checkMacro(userCmdDialog *ucd)
1467 menuItemRec *f;
1469 f = readDialogFields(ucd, False);
1470 if (f == NULL)
1471 return False;
1472 if (!checkMacroText(f->cmd, ucd->dlogShell, ucd->cmdTextW)) {
1473 freeMenuItemRec(f);
1474 return False;
1476 return True;
1479 static int checkMacroText(char *macro, Widget errorParent, Widget errFocus)
1481 Program *prog;
1482 char *errMsg, *stoppedAt;
1484 prog = ParseMacro(macro, &errMsg, &stoppedAt);
1485 if (prog == NULL) {
1486 if (errorParent != NULL) {
1487 ParseError(errorParent, macro, stoppedAt, "macro", errMsg);
1488 XmTextSetInsertionPosition(errFocus, stoppedAt - macro);
1489 XmProcessTraversal(errFocus, XmTRAVERSE_CURRENT);
1491 return False;
1493 FreeProgram(prog);
1494 if (*stoppedAt != '\0') {
1495 if (errorParent != NULL) {
1496 ParseError(errorParent, macro, stoppedAt,"macro","syntax error");
1497 XmTextSetInsertionPosition(errFocus, stoppedAt - macro);
1498 XmProcessTraversal(errFocus, XmTRAVERSE_CURRENT);
1500 return False;
1502 return True;
1505 static int applyDialogChanges(userCmdDialog *ucd)
1507 int i;
1509 /* Get the current contents of the dialog fields */
1510 if (!UpdateManagedList(ucd->managedList, True))
1511 return False;
1513 /* Test compile the macro */
1514 if (ucd->dialogType == MACRO_CMDS)
1515 if (!checkMacro(ucd))
1516 return False;
1518 /* Update the menu information */
1519 if (ucd->dialogType == SHELL_CMDS) {
1520 for (i=0; i<NShellMenuItems; i++)
1521 freeMenuItemRec(ShellMenuItems[i]);
1522 for (i=0; i<ucd->nMenuItems; i++)
1523 ShellMenuItems[i] = copyMenuItemRec(ucd->menuItemsList[i]);
1524 NShellMenuItems = ucd->nMenuItems;
1525 } else if (ucd->dialogType == MACRO_CMDS) {
1526 for (i=0; i<NMacroMenuItems; i++)
1527 freeMenuItemRec(MacroMenuItems[i]);
1528 for (i=0; i<ucd->nMenuItems; i++)
1529 MacroMenuItems[i] = copyMenuItemRec(ucd->menuItemsList[i]);
1530 NMacroMenuItems = ucd->nMenuItems;
1531 } else { /* BG_MENU_CMDS */
1532 for (i=0; i<NBGMenuItems; i++)
1533 freeMenuItemRec(BGMenuItems[i]);
1534 for (i=0; i<ucd->nMenuItems; i++)
1535 BGMenuItems[i] = copyMenuItemRec(ucd->menuItemsList[i]);
1536 NBGMenuItems = ucd->nMenuItems;
1539 /* Update the menus themselves in all of the NEdit windows */
1540 updateMenus(ucd->dialogType);
1542 /* Note that preferences have been changed */
1543 MarkPrefsChanged();
1544 return True;
1547 static void pasteReplayCB(Widget w, XtPointer clientData, XtPointer callData)
1549 userCmdDialog *ucd = (userCmdDialog *)clientData;
1551 if (GetReplayMacro() == NULL)
1552 return;
1554 XmTextInsert(ucd->cmdTextW, XmTextGetInsertionPosition(ucd->cmdTextW),
1555 GetReplayMacro());
1558 static void destroyCB(Widget w, XtPointer clientData, XtPointer callData)
1560 userCmdDialog *ucd = (userCmdDialog *)clientData;
1561 int i;
1563 for (i=0; i<ucd->nMenuItems; i++)
1564 freeMenuItemRec(ucd->menuItemsList[i]);
1565 XtFree((char *)ucd->menuItemsList);
1566 XtFree((char *)ucd);
1569 static void accFocusCB(Widget w, XtPointer clientData, XtPointer callData)
1571 userCmdDialog *ucd = (userCmdDialog *)clientData;
1573 RemoveDialogMnemonicHandler(XtParent(ucd->accTextW));
1576 static void accLoseFocusCB(Widget w, XtPointer clientData, XtPointer callData)
1578 userCmdDialog *ucd = (userCmdDialog *)clientData;
1580 AddDialogMnemonicHandler(XtParent(ucd->accTextW), FALSE);
1583 static void accKeyCB(Widget w, XtPointer clientData, XKeyEvent *event)
1585 userCmdDialog *ucd = (userCmdDialog *)clientData;
1586 KeySym keysym = XLookupKeysym(event, 0);
1587 char outStr[MAX_ACCEL_LEN];
1589 /* Accept only real keys, not modifiers alone */
1590 if (IsModifierKey(keysym))
1591 return;
1593 /* Tab key means go to the next field, don't enter */
1594 if (keysym == XK_Tab)
1595 return;
1597 /* Beep and return if the modifiers are buttons or ones we don't support */
1598 if (event->state & ~(ShiftMask | LockMask | ControlMask | Mod1Mask |
1599 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask)) {
1600 XBell(TheDisplay, 0);
1601 return;
1604 /* Delete or backspace clears field */
1605 if (keysym == XK_Delete || keysym == XK_BackSpace) {
1606 XmTextSetString(ucd->accTextW, "");
1607 return;
1610 /* generate the string to use in the dialog field */
1611 generateAcceleratorString(outStr, event->state, keysym);
1613 /* Reject single character accelerators (a very simple way to eliminate
1614 un-modified letters and numbers) The goal is give users a clue that
1615 they're supposed to type the actual keys, not the name. This scheme
1616 is not rigorous and still allows accelerators like Comma. */
1617 if (strlen(outStr) == 1) {
1618 XBell(TheDisplay, 0);
1619 return;
1622 /* fill in the accelerator field in the dialog */
1623 XmTextSetString(ucd->accTextW, outStr);
1626 static void sameOutCB(Widget w, XtPointer clientData, XtPointer callData)
1628 XtSetSensitive(((userCmdDialog *)clientData)->repInpBtn,
1629 XmToggleButtonGetState(w));
1632 static void shellMenuCB(Widget w, WindowInfo *window, XtPointer callData)
1634 XtArgVal userData;
1635 int index;
1636 char *params[1];
1638 window = WidgetToWindow(MENU_WIDGET(w));
1640 /* get the index of the shell command and verify that it's in range */
1641 XtVaGetValues(w, XmNuserData, &userData, NULL);
1642 index = (int)userData - 10;
1643 if (index <0 || index >= NShellMenuItems)
1644 return;
1646 params[0] = ShellMenuItems[index]->name;
1647 XtCallActionProc(window->lastFocus, "shell_menu_command",
1648 ((XmAnyCallbackStruct *)callData)->event, params, 1);
1651 static void macroMenuCB(Widget w, WindowInfo *window, XtPointer callData)
1653 XtArgVal userData;
1654 int index;
1655 char *params[1];
1657 window = WidgetToWindow(MENU_WIDGET(w));
1659 /* Don't allow users to execute a macro command from the menu (or accel)
1660 if there's already a macro command executing. NEdit can't handle
1661 running multiple, independent uncoordinated, macros in the same
1662 window. Macros may invoke macro menu commands recursively via the
1663 macro_menu_command action proc, which is important for being able to
1664 repeat any operation, and to embed macros within eachother at any
1665 level, however, a call here with a macro running means that THE USER
1666 is explicitly invoking another macro via the menu or an accelerator. */
1667 if (window->macroCmdData != NULL) {
1668 XBell(TheDisplay, 0);
1669 return;
1672 /* get the index of the macro command and verify that it's in range */
1673 XtVaGetValues(w, XmNuserData, &userData, NULL);
1674 index = (int)userData - 10;
1675 if (index <0 || index >= NMacroMenuItems)
1676 return;
1678 params[0] = MacroMenuItems[index]->name;
1679 XtCallActionProc(window->lastFocus, "macro_menu_command",
1680 ((XmAnyCallbackStruct *)callData)->event, params, 1);
1683 static void bgMenuCB(Widget w, WindowInfo *window, XtPointer callData)
1685 XtArgVal userData;
1686 int index;
1687 char *params[1];
1689 window = WidgetToWindow(MENU_WIDGET(w));
1691 /* Same remark as for macro menu commands (see above). */
1692 if (window->macroCmdData != NULL) {
1693 XBell(TheDisplay, 0);
1694 return;
1697 /* get the index of the macro command and verify that it's in range */
1698 XtVaGetValues(w, XmNuserData, &userData, NULL);
1699 index = (int)userData - 10;
1700 if (index <0 || index >= NBGMenuItems)
1701 return;
1703 params[0] = BGMenuItems[index]->name;
1704 XtCallActionProc(window->lastFocus, "bg_menu_command",
1705 ((XmAnyCallbackStruct *)callData)->event, params, 1);
1709 ** Update the name, accelerator, mnemonic, and command fields in the shell
1710 ** command or macro dialog to agree with the currently selected item in the
1711 ** menu item list.
1713 static void updateDialogFields(menuItemRec *f, userCmdDialog *ucd)
1715 char mneString[2], accString[MAX_ACCEL_LEN];
1717 /* fill in the name, accelerator, mnemonic, and command fields of the
1718 dialog for the newly selected item, or blank them if "New" is selected */
1719 if (f == NULL) {
1720 XmTextSetString(ucd->nameTextW, "");
1721 XmTextSetString(ucd->cmdTextW, "");
1722 XmTextSetString(ucd->accTextW, "");
1723 XmTextSetString(ucd->mneTextW, "");
1724 if (ucd->dialogType == SHELL_CMDS) {
1725 RadioButtonChangeState(ucd->selInpBtn, True, True);
1726 RadioButtonChangeState(ucd->sameOutBtn, True, True);
1727 RadioButtonChangeState(ucd->repInpBtn, False, False);
1728 XtSetSensitive(ucd->repInpBtn, True);
1729 RadioButtonChangeState(ucd->saveFirstBtn, False, False);
1730 RadioButtonChangeState(ucd->loadAfterBtn, False, False);
1732 } else {
1733 mneString[0] = f->mnemonic;
1734 mneString[1] = '\0';
1735 generateAcceleratorString(accString, f->modifiers, f->keysym);
1736 XmTextSetString(ucd->nameTextW, f->name);
1737 XmTextSetString(ucd->cmdTextW, f->cmd);
1738 XmTextSetString(ucd->accTextW, accString);
1739 XmTextSetString(ucd->mneTextW, mneString);
1740 RadioButtonChangeState(ucd->selInpBtn, f->input==FROM_SELECTION, False);
1741 if (ucd->dialogType == SHELL_CMDS) {
1742 RadioButtonChangeState(ucd->winInpBtn, f->input == FROM_WINDOW,
1743 False);
1744 RadioButtonChangeState(ucd->eitherInpBtn, f->input == FROM_EITHER,
1745 False);
1746 RadioButtonChangeState(ucd->noInpBtn, f->input == FROM_NONE,
1747 False);
1748 RadioButtonChangeState(ucd->sameOutBtn, f->output==TO_SAME_WINDOW,
1749 False);
1750 RadioButtonChangeState(ucd->winOutBtn, f->output==TO_NEW_WINDOW,
1751 False);
1752 RadioButtonChangeState(ucd->dlogOutBtn, f->output==TO_DIALOG,
1753 False);
1754 RadioButtonChangeState(ucd->repInpBtn, f->repInput, False);
1755 XtSetSensitive(ucd->repInpBtn, f->output==TO_SAME_WINDOW);
1756 RadioButtonChangeState(ucd->saveFirstBtn, f->saveFirst, False);
1757 RadioButtonChangeState(ucd->loadAfterBtn, f->loadAfter, False);
1763 ** Read the name, accelerator, mnemonic, and command fields from the shell or
1764 ** macro commands dialog into a newly allocated menuItemRec. Returns a
1765 ** pointer to the new menuItemRec structure as the function value, or NULL on
1766 ** failure.
1768 static menuItemRec *readDialogFields(userCmdDialog *ucd, int silent)
1770 char *nameText, *cmdText, *mneText, *accText;
1771 menuItemRec *f;
1773 nameText = XmTextGetString(ucd->nameTextW);
1774 if (*nameText == '\0')
1776 if (!silent)
1778 DialogF(DF_WARN, ucd->dlogShell, 1, "Menu Entry",
1779 "Please specify a name\nfor the menu item", "Dismiss");
1780 XmProcessTraversal(ucd->nameTextW, XmTRAVERSE_CURRENT);
1782 XtFree(nameText);
1783 return NULL;
1786 if (strchr(nameText, ':'))
1788 if (!silent)
1790 DialogF(DF_WARN, ucd->dlogShell, 1, "Menu Entry",
1791 "Menu item names may not\ncontain colon (:) characters",
1792 "Dismiss");
1793 XmProcessTraversal(ucd->nameTextW, XmTRAVERSE_CURRENT);
1795 XtFree(nameText);
1796 return NULL;
1799 cmdText = XmTextGetString(ucd->cmdTextW);
1800 if (cmdText == NULL || *cmdText == '\0')
1802 if (!silent)
1804 DialogF(DF_WARN, ucd->dlogShell, 1, "Command to Execute",
1805 "Please specify %s to execute", "Dismiss",
1806 ucd->dialogType == SHELL_CMDS
1807 ? "shell command"
1808 : "macro command(s)");
1809 XmProcessTraversal(ucd->cmdTextW, XmTRAVERSE_CURRENT);
1811 XtFree(nameText);
1813 if (cmdText!=NULL)
1815 XtFree(cmdText);
1817 return NULL;
1820 if (ucd->dialogType == MACRO_CMDS || ucd->dialogType == BG_MENU_CMDS) {
1821 addTerminatingNewline(&cmdText);
1822 if (!checkMacroText(cmdText, silent ? NULL : ucd->dlogShell,
1823 ucd->cmdTextW)) {
1824 XtFree(nameText);
1825 XtFree(cmdText);
1826 return NULL;
1829 f = (menuItemRec *)XtMalloc(sizeof(menuItemRec));
1830 f->name = nameText;
1831 f->cmd = cmdText;
1832 if ((mneText = XmTextGetString(ucd->mneTextW)) != NULL) {
1833 f->mnemonic = mneText==NULL ? '\0' : mneText[0];
1834 XtFree(mneText);
1835 if (f->mnemonic == ':') /* colons mess up string parsing */
1836 f->mnemonic = '\0';
1838 if ((accText = XmTextGetString(ucd->accTextW)) != NULL) {
1839 parseAcceleratorString(accText, &f->modifiers, &f->keysym);
1840 XtFree(accText);
1842 if (ucd->dialogType == SHELL_CMDS) {
1843 if (XmToggleButtonGetState(ucd->selInpBtn))
1844 f->input = FROM_SELECTION;
1845 else if (XmToggleButtonGetState(ucd->winInpBtn))
1846 f->input = FROM_WINDOW;
1847 else if (XmToggleButtonGetState(ucd->eitherInpBtn))
1848 f->input = FROM_EITHER;
1849 else
1850 f->input = FROM_NONE;
1851 if (XmToggleButtonGetState(ucd->winOutBtn))
1852 f->output = TO_NEW_WINDOW;
1853 else if (XmToggleButtonGetState(ucd->dlogOutBtn))
1854 f->output = TO_DIALOG;
1855 else
1856 f->output = TO_SAME_WINDOW;
1857 f->repInput = XmToggleButtonGetState(ucd->repInpBtn);
1858 f->saveFirst = XmToggleButtonGetState(ucd->saveFirstBtn);
1859 f->loadAfter = XmToggleButtonGetState(ucd->loadAfterBtn);
1860 } else {
1861 f->input = XmToggleButtonGetState(ucd->selInpBtn) ? FROM_SELECTION :
1862 FROM_NONE;
1863 f->output = TO_SAME_WINDOW;
1864 f->repInput = False;
1865 f->saveFirst = False;
1866 f->loadAfter = False;
1868 return f;
1872 ** Copy a menu item record, and its associated memory
1874 static menuItemRec *copyMenuItemRec(menuItemRec *item)
1876 menuItemRec *newItem;
1878 newItem = (menuItemRec *)XtMalloc(sizeof(menuItemRec));
1879 *newItem = *item;
1880 newItem->name = XtMalloc(strlen(item->name)+1);
1881 strcpy(newItem->name, item->name);
1882 newItem->cmd = XtMalloc(strlen(item->cmd)+1);
1883 strcpy(newItem->cmd, item->cmd);
1884 return newItem;
1888 ** Free a menu item record, and its associated memory
1890 static void freeMenuItemRec(menuItemRec *item)
1892 XtFree(item->name);
1893 XtFree(item->cmd);
1894 XtFree((char *)item);
1898 ** Callbacks for managed-list operations
1900 static void *getDialogDataCB(void *oldItem, int explicitRequest, int *abort,
1901 void *cbArg)
1903 userCmdDialog *ucd = (userCmdDialog *)cbArg;
1904 menuItemRec *currentFields;
1906 /* If the dialog is currently displaying the "new" entry and the
1907 fields are empty, that's just fine */
1908 if (oldItem == NULL && dialogFieldsAreEmpty(ucd))
1909 return NULL;
1911 /* If there are no problems reading the data, just return it */
1912 currentFields = readDialogFields(ucd, True);
1913 if (currentFields != NULL)
1914 return (void *)currentFields;
1916 /* If user might not be expecting fields to be read, give more warning */
1917 if (!explicitRequest)
1919 if (DialogF(DF_WARN, ucd->dlogShell, 2, "Discard Entry",
1920 "Discard incomplete entry\nfor current menu item?", "Keep",
1921 "Discard") == 2)
1923 return oldItem == NULL
1924 ? NULL
1925 : (void *)copyMenuItemRec((menuItemRec *)oldItem);
1929 /* Do readDialogFields again without "silent" mode to display warning(s) */
1930 readDialogFields(ucd, False);
1931 *abort = True;
1932 return NULL;
1936 static void setDialogDataCB(void *item, void *cbArg)
1938 updateDialogFields((menuItemRec *)item, (userCmdDialog *)cbArg);
1941 static int dialogFieldsAreEmpty(userCmdDialog *ucd)
1943 return TextWidgetIsBlank(ucd->nameTextW) &&
1944 TextWidgetIsBlank(ucd->cmdTextW) &&
1945 TextWidgetIsBlank(ucd->accTextW) &&
1946 TextWidgetIsBlank(ucd->mneTextW) &&
1947 (ucd->dialogType != SHELL_CMDS || (
1948 XmToggleButtonGetState(ucd->selInpBtn) &&
1949 XmToggleButtonGetState(ucd->sameOutBtn) &&
1950 !XmToggleButtonGetState(ucd->repInpBtn) &&
1951 !XmToggleButtonGetState(ucd->saveFirstBtn) &&
1952 !XmToggleButtonGetState(ucd->loadAfterBtn)));
1955 static void freeItemCB(void *item)
1957 freeMenuItemRec((menuItemRec *)item);
1961 ** Gut a text widget of it's ability to process input
1963 static void disableTextW(Widget textW)
1965 static XtTranslations emptyTable = NULL;
1966 static char *emptyTranslations = "\
1967 <EnterWindow>: enter()\n\
1968 <Btn1Down>: grab-focus()\n\
1969 <Btn1Motion>: extend-adjust()\n\
1970 <Btn1Up>: extend-end()\n\
1971 Shift<Key>Tab: prev-tab-group()\n\
1972 Ctrl<Key>Tab: next-tab-group()\n\
1973 <Key>Tab: next-tab-group()\n\
1974 <LeaveWindow>: leave()\n\
1975 <FocusIn>: focusIn()\n\
1976 <FocusOut>: focusOut()\n\
1977 <Unmap>: unmap()\n";
1979 /* replace the translation table with the slimmed down one above */
1980 if (emptyTable == NULL)
1981 emptyTable = XtParseTranslationTable(emptyTranslations);
1982 XtVaSetValues(textW, XmNtranslations, emptyTable, NULL);
1985 static char *writeMenuItemString(menuItemRec **menuItems, int nItems,
1986 int listType)
1988 char *outStr, *outPtr, *c, accStr[MAX_ACCEL_LEN];
1989 menuItemRec *f;
1990 int i, length;
1992 /* determine the max. amount of memory needed for the returned string
1993 and allocate a buffer for composing the string */
1994 length = 0;
1995 for (i=0; i<nItems; i++) {
1996 f = menuItems[i];
1997 generateAcceleratorString(accStr, f->modifiers, f->keysym);
1998 length += strlen(f->name) * 2; /* allow for \n & \\ expansions */
1999 length += strlen(accStr);
2000 length += strlen(f->cmd) * 6; /* allow for \n & \\ expansions */
2001 length += 21; /* number of characters added below */
2003 length++; /* terminating null */
2004 outStr = XtMalloc(length);
2006 /* write the string */
2007 outPtr = outStr;
2008 *outPtr++ = '\\';
2009 *outPtr++ = '\n';
2010 for (i=0; i<nItems; i++) {
2011 f = menuItems[i];
2012 generateAcceleratorString(accStr, f->modifiers, f->keysym);
2013 *outPtr++ = '\t';
2014 for (c=f->name; *c!='\0'; ++c) { /* Copy the command name */
2015 if (*c == '\\') { /* changing backslashes to \\ */
2016 *outPtr++ = '\\';
2017 *outPtr++ = '\\';
2018 } else if (*c == '\n') { /* changing newlines to \n */
2019 *outPtr++ = '\\';
2020 *outPtr++ = 'n';
2021 } else {
2022 *outPtr++ = *c;
2025 *outPtr++ = ':';
2026 strcpy(outPtr, accStr);
2027 outPtr += strlen(accStr);
2028 *outPtr++ = ':';
2029 if (f->mnemonic != '\0')
2030 *outPtr++ = f->mnemonic;
2031 *outPtr++ = ':';
2032 if (listType == SHELL_CMDS) {
2033 if (f->input == FROM_SELECTION)
2034 *outPtr++ = 'I';
2035 else if (f->input == FROM_WINDOW)
2036 *outPtr++ = 'A';
2037 else if (f->input == FROM_EITHER)
2038 *outPtr++ = 'E';
2039 if (f->output == TO_DIALOG)
2040 *outPtr++ = 'D';
2041 else if (f->output == TO_NEW_WINDOW)
2042 *outPtr++ = 'W';
2043 if (f->repInput)
2044 *outPtr++ = 'X';
2045 if (f->saveFirst)
2046 *outPtr++ = 'S';
2047 if (f->loadAfter)
2048 *outPtr++ = 'L';
2049 *outPtr++ = ':';
2050 } else {
2051 if (f->input == FROM_SELECTION)
2052 *outPtr++ = 'R';
2053 *outPtr++ = ':';
2054 *outPtr++ = ' ';
2055 *outPtr++ = '{';
2057 *outPtr++ = '\\';
2058 *outPtr++ = 'n';
2059 *outPtr++ = '\\';
2060 *outPtr++ = '\n';
2061 *outPtr++ = '\t';
2062 *outPtr++ = '\t';
2063 for (c=f->cmd; *c!='\0'; c++) { /* Copy the command string, changing */
2064 if (*c == '\\') { /* backslashes to double backslashes */
2065 *outPtr++ = '\\'; /* and newlines to backslash-n's, */
2066 *outPtr++ = '\\'; /* followed by real newlines and tab */
2067 } else if (*c == '\n') {
2068 *outPtr++ = '\\';
2069 *outPtr++ = 'n';
2070 *outPtr++ = '\\';
2071 *outPtr++ = '\n';
2072 *outPtr++ = '\t';
2073 *outPtr++ = '\t';
2074 } else
2075 *outPtr++ = *c;
2077 if (listType == MACRO_CMDS || listType == BG_MENU_CMDS) {
2078 if (*(outPtr-1) == '\t') outPtr--;
2079 *outPtr++ = '}';
2081 *outPtr++ = '\\';
2082 *outPtr++ = 'n';
2083 *outPtr++ = '\\';
2084 *outPtr++ = '\n';
2086 --outPtr;
2087 *--outPtr = '\0';
2088 return outStr;
2091 static int loadMenuItemString(char *inString, menuItemRec **menuItems,
2092 int *nItems, int listType)
2094 menuItemRec *f;
2095 char *cmdStr;
2096 char *inPtr = inString;
2097 char *nameStr, accStr[MAX_ACCEL_LEN], mneChar;
2098 KeySym keysym;
2099 unsigned int modifiers;
2100 int i, input, output, saveFirst, loadAfter, repInput;
2101 int nameLen, accLen, mneLen, cmdLen;
2103 for (;;) {
2105 /* remove leading whitespace */
2106 while (*inPtr == ' ' || *inPtr == '\t')
2107 inPtr++;
2109 /* read name field */
2110 nameLen = strcspn(inPtr, ":");
2111 if (nameLen == 0)
2112 return parseError("no name field");
2113 nameStr = XtMalloc(nameLen+1);
2114 strncpy(nameStr, inPtr, nameLen);
2115 nameStr[nameLen] = '\0';
2116 inPtr += nameLen;
2117 if (*inPtr == '\0')
2118 return parseError("end not expected");
2119 inPtr++;
2121 /* read accelerator field */
2122 accLen = strcspn(inPtr, ":");
2123 if (accLen >= MAX_ACCEL_LEN)
2124 return parseError("accelerator field too long");
2125 strncpy(accStr, inPtr, accLen);
2126 accStr[accLen] = '\0';
2127 inPtr += accLen;
2128 if (*inPtr == '\0')
2129 return parseError("end not expected");
2130 inPtr++;
2132 /* read menemonic field */
2133 mneLen = strcspn(inPtr, ":");
2134 if (mneLen > 1)
2135 return parseError("mnemonic field too long");
2136 if (mneLen == 1)
2137 mneChar = *inPtr++;
2138 else
2139 mneChar = '\0';
2140 inPtr++;
2141 if (*inPtr == '\0')
2142 return parseError("end not expected");
2144 /* read flags field */
2145 input = FROM_NONE;
2146 output = TO_SAME_WINDOW;
2147 repInput = False;
2148 saveFirst = False;
2149 loadAfter = False;
2150 for (; *inPtr != ':'; inPtr++) {
2151 if (listType == SHELL_CMDS) {
2152 if (*inPtr == 'I')
2153 input = FROM_SELECTION;
2154 else if (*inPtr == 'A')
2155 input = FROM_WINDOW;
2156 else if (*inPtr == 'E')
2157 input = FROM_EITHER;
2158 else if (*inPtr == 'W')
2159 output = TO_NEW_WINDOW;
2160 else if (*inPtr == 'D')
2161 output = TO_DIALOG;
2162 else if (*inPtr == 'X')
2163 repInput = True;
2164 else if (*inPtr == 'S')
2165 saveFirst = True;
2166 else if (*inPtr == 'L')
2167 loadAfter = True;
2168 else
2169 return parseError("unreadable flag field");
2170 } else {
2171 if (*inPtr == 'R')
2172 input = FROM_SELECTION;
2173 else
2174 return parseError("unreadable flag field");
2177 inPtr++;
2179 /* read command field */
2180 if (listType == SHELL_CMDS) {
2181 if (*inPtr++ != '\n')
2182 return parseError("command must begin with newline");
2183 while (*inPtr == ' ' || *inPtr == '\t') /* leading whitespace */
2184 inPtr++;
2185 cmdLen = strcspn(inPtr, "\n");
2186 if (cmdLen == 0)
2187 return parseError("shell command field is empty");
2188 cmdStr = XtMalloc(cmdLen+1);
2189 strncpy(cmdStr, inPtr, cmdLen);
2190 cmdStr[cmdLen] = '\0';
2191 inPtr += cmdLen;
2192 } else {
2193 cmdStr = copyMacroToEnd(&inPtr);
2194 if (cmdStr == NULL)
2195 return False;
2197 while (*inPtr == ' ' || *inPtr == '\t' || *inPtr == '\n')
2198 inPtr++; /* skip trailing whitespace & newline */
2200 /* parse the accelerator field */
2201 if (!parseAcceleratorString(accStr, &modifiers, &keysym))
2202 return parseError("couldn't read accelerator field");
2204 /* create a menu item record */
2205 f = (menuItemRec *)XtMalloc(sizeof(menuItemRec));
2206 f->name = nameStr;
2207 f->cmd = cmdStr;
2208 f->mnemonic = mneChar;
2209 f->modifiers = modifiers;
2210 f->input = input;
2211 f->output = output;
2212 f->repInput = repInput;
2213 f->saveFirst = saveFirst;
2214 f->loadAfter = loadAfter;
2215 f->keysym = keysym;
2217 /* add/replace menu record in the list */
2218 for (i=0; i < *nItems; i++) {
2219 if (!strcmp(menuItems[i]->name, f->name)) {
2220 freeMenuItemRec(menuItems[i]);
2221 menuItems[i] = f;
2222 break;
2225 if (i == *nItems)
2226 menuItems[(*nItems)++] = f;
2228 /* end of string in proper place */
2229 if (*inPtr == '\0')
2230 return True;
2234 static int parseError(const char *message)
2236 fprintf(stderr, "NEdit: Parse error in user defined menu item, %s\n",
2237 message);
2238 return False;
2242 ** Create a text string representing an accelerator for the dialog,
2243 ** the shellCommands or macroCommands resource, and for the menu item.
2245 static void generateAcceleratorString(char *text, unsigned int modifiers,
2246 KeySym keysym)
2248 char *shiftStr = "", *ctrlStr = "", *altStr = "";
2249 char *mod2Str = "", *mod3Str = "", *mod4Str = "", *mod5Str = "";
2250 char keyName[20];
2251 Modifiers numLockMask = GetNumLockModMask(TheDisplay);
2253 /* if there's no accelerator, generate an empty string */
2254 if (keysym == NoSymbol) {
2255 *text = '\0';
2256 return;
2260 /* Translate the modifiers into strings.
2261 Lock and NumLock are always ignored (see util/misc.c),
2262 so we don't display them either. */
2263 if (modifiers & ShiftMask)
2264 shiftStr = "Shift+";
2265 if (modifiers & ControlMask)
2266 ctrlStr = "Ctrl+";
2267 if (modifiers & Mod1Mask)
2268 altStr = "Alt+";
2269 if ((modifiers & Mod2Mask) && (Mod2Mask != numLockMask))
2270 mod2Str = "Mod2+";
2271 if ((modifiers & Mod3Mask) && (Mod3Mask != numLockMask))
2272 mod3Str = "Mod3+";
2273 if ((modifiers & Mod4Mask) && (Mod4Mask != numLockMask))
2274 mod4Str = "Mod4+";
2275 if ((modifiers & Mod5Mask) && (Mod5Mask != numLockMask))
2276 mod5Str = "Mod5+";
2278 /* for a consistent look to the accelerator names in the menus,
2279 capitalize the first letter of the keysym */
2280 strcpy(keyName, XKeysymToString(keysym));
2281 *keyName = toupper(*keyName);
2283 /* concatenate the strings together */
2284 sprintf(text, "%s%s%s%s%s%s%s%s", shiftStr, ctrlStr, altStr,
2285 mod2Str, mod3Str, mod4Str, mod5Str, keyName);
2289 ** Create a translation table event description string for the menu
2290 ** XmNaccelerator resource.
2292 static void genAccelEventName(char *text, unsigned int modifiers,
2293 KeySym keysym)
2295 char *shiftStr = "", *lockStr = "", *ctrlStr = "", *altStr = "";
2296 char *mod2Str = "", *mod3Str = "", *mod4Str = "", *mod5Str = "";
2298 /* if there's no accelerator, generate an empty string */
2299 if (keysym == NoSymbol) {
2300 *text = '\0';
2301 return;
2304 /* translate the modifiers into strings */
2305 if (modifiers & ShiftMask)
2306 shiftStr = "Shift ";
2307 if (modifiers & LockMask)
2308 lockStr = "Lock ";
2309 if (modifiers & ControlMask)
2310 ctrlStr = "Ctrl ";
2311 if (modifiers & Mod1Mask)
2312 altStr = "Alt ";
2313 if (modifiers & Mod2Mask)
2314 mod2Str = "Mod2 ";
2315 if (modifiers & Mod3Mask)
2316 mod3Str = "Mod3 ";
2317 if (modifiers & Mod4Mask)
2318 mod4Str = "Mod4 ";
2319 if (modifiers & Mod5Mask)
2320 mod5Str = "Mod5 ";
2322 /* put the modifiers together with the key name */
2323 sprintf(text, "%s%s%s%s%s%s%s%s<Key>%s",
2324 shiftStr, lockStr, ctrlStr, altStr,
2325 mod2Str, mod3Str, mod4Str, mod5Str,
2326 XKeysymToString(keysym));
2330 ** Read an accelerator name and put it into the form of a modifier mask
2331 ** and a KeySym code. Returns false if string can't be read
2332 ** ... does not handle whitespace in string (look at scanf)
2334 static int parseAcceleratorString(const char *string, unsigned int *modifiers,
2335 KeySym *keysym)
2337 int i, nFields, inputLength = strlen(string);
2338 char fields[10][MAX_ACCEL_LEN];
2340 /* a blank field means no accelerator */
2341 if (inputLength == 0) {
2342 *modifiers = 0;
2343 *keysym = NoSymbol;
2344 return True;
2347 /* limit the string length so no field strings will overflow */
2348 if (inputLength > MAX_ACCEL_LEN)
2349 return False;
2351 /* divide the input into '+' separated fields */
2352 nFields = sscanf(string, "%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]",
2353 fields[0], fields[1], fields[2], fields[3], fields[4], fields[5],
2354 fields[6], fields[7], fields[8], fields[9]);
2355 if (nFields == 0)
2356 return False;
2358 /* get the key name from the last field and translate it to a keysym.
2359 If the name is capitalized, try it lowercase as well, since some
2360 of the keysyms are "prettied up" by generateAcceleratorString */
2361 *keysym = XStringToKeysym(fields[nFields-1]);
2362 if (*keysym == NoSymbol) {
2363 *fields[nFields-1] = tolower(*fields[nFields-1]);
2364 *keysym = XStringToKeysym(fields[nFields-1]);
2365 if (*keysym == NoSymbol)
2366 return False;
2369 /* parse the modifier names from the rest of the fields */
2370 *modifiers = 0;
2371 for (i=0; i<nFields-1; i++) {
2372 if (!strcmp(fields[i], "Shift"))
2373 *modifiers |= ShiftMask;
2374 else if (!strcmp(fields[i], "Lock"))
2375 *modifiers |= LockMask;
2376 else if (!strcmp(fields[i], "Ctrl"))
2377 *modifiers |= ControlMask;
2378 /* comparision with "Alt" for compatibility with old .nedit files*/
2379 else if (!strcmp(fields[i], "Alt"))
2380 *modifiers |= Mod1Mask;
2381 else if (!strcmp(fields[i], "Mod2"))
2382 *modifiers |= Mod2Mask;
2383 else if (!strcmp(fields[i], "Mod3"))
2384 *modifiers |= Mod3Mask;
2385 else if (!strcmp(fields[i], "Mod4"))
2386 *modifiers |= Mod4Mask;
2387 else if (!strcmp(fields[i], "Mod5"))
2388 *modifiers |= Mod5Mask;
2389 else
2390 return False;
2393 /* all fields successfully parsed */
2394 return True;
2398 ** Scan text from "*inPtr" to the end of macro input (matching brace),
2399 ** advancing inPtr, and return macro text as function return value.
2401 ** This is kind of wastefull in that it throws away the compiled macro,
2402 ** to be re-generated from the text as needed, but compile time is
2403 ** negligible for most macros.
2405 static char *copyMacroToEnd(char **inPtr)
2407 char *retStr, *errMsg, *stoppedAt, *p, *retPtr;
2408 Program *prog;
2410 /* Skip over whitespace to find make sure there's a beginning brace
2411 to anchor the parse (if not, it will take the whole file) */
2412 *inPtr += strspn(*inPtr, " \t\n");
2413 if (**inPtr != '{') {
2414 ParseError(NULL, *inPtr, *inPtr-1, "macro menu item", "expecting '{'");
2415 return NULL;
2418 /* Parse the input */
2419 prog = ParseMacro(*inPtr, &errMsg, &stoppedAt);
2420 if (prog == NULL) {
2421 ParseError(NULL, *inPtr, stoppedAt, "macro menu item", errMsg);
2422 return NULL;
2424 FreeProgram(prog);
2426 /* Copy and return the body of the macro, stripping outer braces and
2427 extra leading tabs added by the writer routine */
2428 (*inPtr)++;
2429 *inPtr += strspn(*inPtr, " \t");
2430 if (**inPtr == '\n') (*inPtr)++;
2431 if (**inPtr == '\t') (*inPtr)++;
2432 if (**inPtr == '\t') (*inPtr)++;
2433 retPtr = retStr = XtMalloc(stoppedAt - *inPtr + 1);
2434 for (p = *inPtr; p < stoppedAt - 1; p++) {
2435 if (!strncmp(p, "\n\t\t", 3)) {
2436 *retPtr++ = '\n';
2437 p += 2;
2438 } else
2439 *retPtr++ = *p;
2441 if (*(retPtr-1) == '\t') retPtr--;
2442 *retPtr = '\0';
2443 *inPtr = stoppedAt;
2444 return retStr;
2448 ** If "*string" is not terminated with a newline character, reallocate the
2449 ** string and add one. (The macro language requires newline terminators for
2450 ** statements, but the text widget doesn't force it like the NEdit text buffer
2451 ** does, so this might avoid some confusion.)
2453 static void addTerminatingNewline(char **string)
2455 char *newString;
2456 int length;
2458 length = strlen(*string);
2459 if ((*string)[length-1] != '\n') {
2460 newString = XtMalloc(length + 2);
2461 strcpy(newString, *string);
2462 newString[length] = '\n';
2463 newString[length+1] = '\0';
2464 XtFree(*string);
2465 *string = newString;