Fix our use of $em_tab_dist after it was changed to 0 for 'turned of'.
[nedit.git] / source / userCmds.c
blobf09533c5e05b1496c16b4a1d9a813927c92d7960
1 static const char CVSID[] = "$Id: userCmds.c,v 1.57 2009/06/23 21:03:13 lebert 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. In addition, you may distribute version of this program linked to *
12 * Motif or Open Motif. See README for details. *
13 * *
14 * This software is distributed in the hope that it will be useful, but WITHOUT *
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
17 * for more details. *
18 * *
19 * You should have received a copy of the GNU General Public License along with *
20 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
21 * Place, Suite 330, Boston, MA 02111-1307 USA *
22 * *
23 * Nirvana Text Editor *
24 * April, 1997 *
25 * *
26 * Written by Mark Edel *
27 * *
28 *******************************************************************************/
30 #ifdef HAVE_CONFIG_H
31 #include "../config.h"
32 #endif
34 #include "userCmds.h"
35 #include "textBuf.h"
36 #include "text.h"
37 #include "nedit.h"
38 #include "preferences.h"
39 #include "window.h"
40 #include "menu.h"
41 #include "shell.h"
42 #include "macro.h"
43 #include "file.h"
44 #include "interpret.h"
45 #include "parse.h"
46 #include "../util/DialogF.h"
47 #include "../util/misc.h"
48 #include "../util/managedList.h"
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <ctype.h>
54 #ifdef VMS
55 #include "../util/VMSparam.h"
56 #else
57 #ifndef __MVS__
58 #include <sys/param.h>
59 #endif
60 #endif /*VMS*/
62 #include <Xm/Xm.h>
63 #include <X11/keysym.h>
64 #include <X11/IntrinsicP.h>
65 #include <Xm/Text.h>
66 #include <Xm/Form.h>
67 #include <Xm/List.h>
68 #include <Xm/LabelG.h>
69 #include <Xm/PushB.h>
70 #include <Xm/ToggleB.h>
71 #include <Xm/SelectioB.h>
72 #include <Xm/RowColumn.h>
73 #include <Xm/CascadeB.h>
74 #include <Xm/MenuShell.h>
76 #ifdef HAVE_DEBUG_H
77 #include "../debug.h"
78 #endif
80 #if XmVersion >= 1002
81 #define MENU_WIDGET(w) (XmGetPostedFromWidget(XtParent(w)))
82 #else
83 #define MENU_WIDGET(w) (w)
84 #endif
86 extern void _XmDismissTearOff(Widget w, XtPointer call, XtPointer x);
88 /* max. number of user programmable menu commands allowed per each of the
89 macro, shell, and background menus */
90 #define MAX_ITEMS_PER_MENU 400
92 /* indicates, that an unknown (i.e. not existing) language mode
93 is bound to an user menu item */
94 #define UNKNOWN_LANGUAGE_MODE -2
96 /* major divisions (in position units) in User Commands dialogs */
97 #define LEFT_MARGIN_POS 1
98 #define RIGHT_MARGIN_POS 99
99 #define LIST_RIGHT 45
100 #define SHELL_CMD_TOP 70
101 #define MACRO_CMD_TOP 40
103 /* types of current dialog and/or menu */
104 enum dialogTypes {SHELL_CMDS, MACRO_CMDS, BG_MENU_CMDS};
106 /* Structure representing a menu item for shell, macro and BG menus*/
107 typedef struct {
108 char *name;
109 unsigned int modifiers;
110 KeySym keysym;
111 char mnemonic;
112 char input;
113 char output;
114 char repInput;
115 char saveFirst;
116 char loadAfter;
117 char *cmd;
118 } menuItemRec;
120 /* Structure for widgets and flags associated with shell command,
121 macro command and BG command editing dialogs */
122 typedef struct {
123 int dialogType;
124 WindowInfo *window;
125 Widget nameTextW, accTextW, mneTextW, cmdTextW, saveFirstBtn;
126 Widget loadAfterBtn, selInpBtn, winInpBtn, eitherInpBtn, noInpBtn;
127 Widget repInpBtn, sameOutBtn, dlogOutBtn, winOutBtn, dlogShell;
128 Widget managedList;
129 menuItemRec **menuItemsList;
130 int nMenuItems;
131 } userCmdDialog;
133 /* Structure for keeping track of hierarchical sub-menus during user-menu
134 creation */
135 typedef struct {
136 char *name;
137 Widget menuPane;
138 } menuTreeItem;
140 /* Structure holding hierarchical info about one sub-menu.
142 Suppose following user menu items:
143 a.) "menuItem1"
144 b.) "subMenuA>menuItemA1"
145 c.) "subMenuA>menuItemA2"
146 d.) "subMenuA>subMenuB>menuItemB1"
147 e.) "subMenuA>subMenuB>menuItemB2"
149 Structure of this user menu is:
151 Main Menu Name Sub-Menu A Name Sub-Menu B Name
152 element nbr. element nbr. element nbr.
153 0 menuItem1
154 1 subMenuA --+-> 0 menuItemA1
155 +-> 1 menuItemA2
156 +-> 2 subMenuB --+-> 0 menuItemB1
157 +-> 1 menuItemB2
159 Above example holds 2 sub-menus:
160 1.) "subMenuA" (hierarchical ID = {1} means: element nbr. "1" of main menu)
161 2.) "subMenuA>subMenuB" (hierarchical ID = {1, 2} means: el. nbr. "2" of
162 "subMenuA", which itself is el. nbr. "0" of main menu) */
163 typedef struct {
164 char *usmiName; /* hierarchical name of sub-menu */
165 int *usmiId; /* hierarchical ID of sub-menu */
166 int usmiIdLen; /* length of hierarchical ID */
167 } userSubMenuInfo;
169 /* Holds info about sub-menu structure of an user menu */
170 typedef struct {
171 int usmcNbrOfMainMenuItems; /* number of main menu items */
172 int usmcNbrOfSubMenus; /* number of sub-menus */
173 userSubMenuInfo *usmcInfo; /* list of sub-menu info */
174 } userSubMenuCache;
176 /* Structure holding info about a single menu item.
177 According to above example there exist 5 user menu items:
178 a.) "menuItem1" (hierarchical ID = {0} means: element nbr. "0" of main menu)
179 b.) "menuItemA1" (hierarchical ID = {1, 0} means: el. nbr. "0" of
180 "subMenuA", which itself is el. nbr. "1" of main menu)
181 c.) "menuItemA2" (hierarchical ID = {1, 1})
182 d.) "menuItemB1" (hierarchical ID = {1, 2, 0})
183 e.) "menuItemB2" (hierarchical ID = {1, 2, 1})
185 typedef struct {
186 char *umiName; /* hierarchical name of menu item
187 (w.o. language mode info) */
188 int *umiId; /* hierarchical ID of menu item */
189 int umiIdLen; /* length of hierarchical ID */
190 Boolean umiIsDefault; /* menu item is default one ("@*") */
191 int umiNbrOfLanguageModes; /* number of language modes
192 applicable for this menu item */
193 int *umiLanguageMode; /* list of applicable lang. modes */
194 int umiDefaultIndex; /* array index of menu item to be
195 used as default, if no lang. mode
196 matches */
197 Boolean umiToBeManaged; /* indicates, that menu item needs
198 to be managed */
199 } userMenuInfo;
201 /* Structure holding info about a selected user menu (shell, macro or
202 background) */
203 typedef struct {
204 int sumType; /* type of menu (shell, macro or
205 background */
206 Widget sumMenuPane; /* pane of main menu */
207 int sumNbrOfListItems; /* number of menu items */
208 menuItemRec **sumItemList; /* list of menu items */
209 userMenuInfo **sumInfoList; /* list of infos about menu items */
210 userSubMenuCache *sumSubMenus; /* info about sub-menu structure */
211 UserMenuList *sumMainMenuList; /* cached info about main menu */
212 Boolean *sumMenuCreated; /* pointer to "menu created"
213 indicator */
214 } selectedUserMenu;
216 /* Descriptions of the current user programmed menu items for re-generating
217 menus and processing shell, macro, and background menu selections */
218 static menuItemRec *ShellMenuItems[MAX_ITEMS_PER_MENU];
219 static userMenuInfo *ShellMenuInfo[MAX_ITEMS_PER_MENU];
220 static userSubMenuCache ShellSubMenus;
221 static int NShellMenuItems = 0;
222 static menuItemRec *MacroMenuItems[MAX_ITEMS_PER_MENU];
223 static userMenuInfo *MacroMenuInfo[MAX_ITEMS_PER_MENU];
224 static userSubMenuCache MacroSubMenus;
225 static int NMacroMenuItems = 0;
226 static menuItemRec *BGMenuItems[MAX_ITEMS_PER_MENU];
227 static userMenuInfo *BGMenuInfo[MAX_ITEMS_PER_MENU];
228 static userSubMenuCache BGSubMenus;
229 static int NBGMenuItems = 0;
231 /* Top level shells of the user-defined menu editing dialogs */
232 static Widget ShellCmdDialog = NULL;
233 static Widget MacroCmdDialog = NULL;
234 static Widget BGMenuCmdDialog = NULL;
236 /* Paste learn/replay sequence buttons in user defined menu editing dialogs
237 (for dimming and undimming externally when replay macro is available) */
238 static Widget MacroPasteReplayBtn = NULL;
239 static Widget BGMenuPasteReplayBtn = NULL;
241 static void editMacroOrBGMenu(WindowInfo *window, int dialogType);
242 static void dimSelDepItemsInMenu(Widget menuPane, menuItemRec **menuList,
243 int nMenuItems, int sensitive);
244 static void rebuildMenuOfAllWindows(int menuType);
245 static void rebuildMenu(WindowInfo *window, int menuType);
246 static Widget findInMenuTree(menuTreeItem *menuTree, int nTreeEntries,
247 const char *hierName);
248 static char *copySubstring(const char *string, int length);
249 static Widget createUserMenuItem(Widget menuPane, char *name, menuItemRec *f,
250 int index, XtCallbackProc cbRtn, XtPointer cbArg);
251 static Widget createUserSubMenu(Widget parent, char *label, Widget *menuItem);
252 static void deleteMenuItems(Widget menuPane);
253 static void selectUserMenu(WindowInfo *window, int menuType, selectedUserMenu *menu);
254 static void updateMenu(WindowInfo *window, int menuType);
255 static void manageTearOffMenu(Widget menuPane);
256 static void resetManageMode(UserMenuList *list);
257 static void manageAllSubMenuWidgets(UserMenuListElement *subMenu);
258 static void unmanageAllSubMenuWidgets(UserMenuListElement *subMenu);
259 static void manageMenuWidgets(UserMenuList *list);
260 static void removeAccelFromMenuWidgets(UserMenuList *menuList);
261 static void assignAccelToMenuWidgets(UserMenuList *menuList, WindowInfo *window);
262 static void manageUserMenu(selectedUserMenu *menu, WindowInfo *window);
263 static void createMenuItems(WindowInfo *window, selectedUserMenu *menu);
264 static void okCB(Widget w, XtPointer clientData, XtPointer callData);
265 static void applyCB(Widget w, XtPointer clientData, XtPointer callData);
266 static void checkCB(Widget w, XtPointer clientData, XtPointer callData);
267 static int checkMacro(userCmdDialog *ucd);
268 static int checkMacroText(char *macro, Widget errorParent, Widget errFocus);
269 static int applyDialogChanges(userCmdDialog *ucd);
270 static void closeCB(Widget w, XtPointer clientData, XtPointer callData);
271 static void pasteReplayCB(Widget w, XtPointer clientData, XtPointer callData);
272 static void destroyCB(Widget w, XtPointer clientData, XtPointer callData);
273 static void accKeyCB(Widget w, XtPointer clientData, XKeyEvent *event);
274 static void sameOutCB(Widget w, XtPointer clientData, XtPointer callData);
275 static void shellMenuCB(Widget w, WindowInfo *window, XtPointer callData);
276 static void macroMenuCB(Widget w, WindowInfo *window, XtPointer callData);
277 static void bgMenuCB(Widget w, WindowInfo *window, XtPointer callData) ;
278 static void accFocusCB(Widget w, XtPointer clientData, XtPointer callData);
279 static void accLoseFocusCB(Widget w, XtPointer clientData,
280 XtPointer callData);
281 static void updateDialogFields(menuItemRec *f, userCmdDialog *ucd);
282 static menuItemRec *readDialogFields(userCmdDialog *ucd, int silent);
283 static menuItemRec *copyMenuItemRec(menuItemRec *item);
284 static void freeMenuItemRec(menuItemRec *item);
285 static void *getDialogDataCB(void *oldItem, int explicitRequest, int *abort,
286 void *cbArg);
287 static void setDialogDataCB(void *item, void *cbArg);
288 static void freeItemCB(void *item);
289 static int dialogFieldsAreEmpty(userCmdDialog *ucd);
290 static void disableTextW(Widget textW);
291 static char *writeMenuItemString(menuItemRec **menuItems, int nItems,
292 int listType);
293 static int loadMenuItemString(char *inString, menuItemRec **menuItems,
294 int *nItems, int listType);
295 static void generateAcceleratorString(char *text, unsigned int modifiers,
296 KeySym keysym);
297 static void genAccelEventName(char *text, unsigned int modifiers,
298 KeySym keysym);
299 static int parseAcceleratorString(const char *string, unsigned int *modifiers,
300 KeySym *keysym);
301 static int parseError(const char *message);
302 static char *copyMacroToEnd(char **inPtr);
303 static void addTerminatingNewline(char **string);
304 static void parseMenuItemList(menuItemRec **itemList, int nbrOfItems,
305 userMenuInfo **infoList, userSubMenuCache *subMenus);
306 static int getSubMenuDepth(const char *menuName);
307 static userMenuInfo *parseMenuItemRec(menuItemRec *item);
308 static void parseMenuItemName(char *menuItemName, userMenuInfo *info);
309 static void generateUserMenuId(userMenuInfo *info, userSubMenuCache *subMenus);
310 static userSubMenuInfo *findSubMenuInfo(userSubMenuCache *subMenus,
311 const char *hierName);
312 static char *stripLanguageMode(const char *menuItemName);
313 static void setDefaultIndex(userMenuInfo **infoList, int nbrOfItems,
314 int defaultIdx);
315 static void applyLangModeToUserMenuInfo(userMenuInfo **infoList, int nbrOfItems,
316 int languageMode);
317 static int doesLanguageModeMatch(userMenuInfo *info, int languageMode);
318 static void freeUserMenuInfoList(userMenuInfo **infoList, int nbrOfItems);
319 static void freeUserMenuInfo(userMenuInfo *info);
320 static void allocSubMenuCache(userSubMenuCache *subMenus, int nbrOfItems);
321 static void freeSubMenuCache(userSubMenuCache *subMenus);
322 static void allocUserMenuList(UserMenuList *list, int nbrOfItems);
323 static void freeUserMenuList(UserMenuList *list);
324 static UserMenuListElement *allocUserMenuListElement(Widget menuItem, char *accKeys);
325 static void freeUserMenuListElement(UserMenuListElement *element);
326 static UserMenuList *allocUserSubMenuList(int nbrOfItems);
327 static void freeUserSubMenuList(UserMenuList *list);
330 ** Present a dialog for editing the user specified commands in the shell menu
332 void EditShellMenu(WindowInfo *window)
334 Widget form, accLabel, inpLabel, inpBox, outBox, outLabel;
335 Widget nameLabel, cmdLabel, okBtn, applyBtn, closeBtn;
336 userCmdDialog *ucd;
337 XmString s1;
338 int ac, i;
339 Arg args[20];
341 /* if the dialog is already displayed, just pop it to the top and return */
342 if (ShellCmdDialog != NULL) {
343 RaiseDialogWindow(ShellCmdDialog);
344 return;
347 /* Create a structure for keeping track of dialog state */
348 ucd = (userCmdDialog *)XtMalloc(sizeof(userCmdDialog));
349 ucd->window = window;
351 /* Set the dialog to operate on the Shell menu */
352 ucd->menuItemsList = (menuItemRec **)XtMalloc(sizeof(menuItemRec *) *
353 MAX_ITEMS_PER_MENU);
354 for (i=0; i<NShellMenuItems; i++)
355 ucd->menuItemsList[i] = copyMenuItemRec(ShellMenuItems[i]);
356 ucd->nMenuItems = NShellMenuItems;
357 ucd->dialogType = SHELL_CMDS;
359 ac = 0;
360 XtSetArg(args[ac], XmNdeleteResponse, XmDO_NOTHING); ac++;
361 XtSetArg(args[ac], XmNiconName, "NEdit Shell Menu"); ac++;
362 XtSetArg(args[ac], XmNtitle, "Shell Menu"); ac++;
363 ucd->dlogShell = CreateWidget(TheAppShell, "shellCommands",
364 topLevelShellWidgetClass, args, ac);
365 AddSmallIcon(ucd->dlogShell);
366 form = XtVaCreateManagedWidget("editShellCommands", xmFormWidgetClass,
367 ucd->dlogShell, XmNautoUnmanage, False,
368 XmNresizePolicy, XmRESIZE_NONE, NULL);
369 ShellCmdDialog = ucd->dlogShell;
370 XtAddCallback(form, XmNdestroyCallback, destroyCB, ucd);
371 AddMotifCloseCallback(ucd->dlogShell, closeCB, ucd);
373 ac = 0;
374 XtSetArg(args[ac], XmNtopAttachment, XmATTACH_POSITION); ac++;
375 XtSetArg(args[ac], XmNtopPosition, 2); ac++;
376 XtSetArg(args[ac], XmNleftAttachment, XmATTACH_POSITION); ac++;
377 XtSetArg(args[ac], XmNleftPosition, LEFT_MARGIN_POS); ac++;
378 XtSetArg(args[ac], XmNrightAttachment, XmATTACH_POSITION); ac++;
379 XtSetArg(args[ac], XmNrightPosition, LIST_RIGHT-1); ac++;
380 XtSetArg(args[ac], XmNbottomAttachment, XmATTACH_POSITION); ac++;
381 XtSetArg(args[ac], XmNbottomPosition, SHELL_CMD_TOP); ac++;
382 ucd->managedList = CreateManagedList(form, "list", args, ac,
383 (void **)ucd->menuItemsList, &ucd->nMenuItems, MAX_ITEMS_PER_MENU,
384 20, getDialogDataCB, ucd, setDialogDataCB, ucd, freeItemCB);
386 ucd->loadAfterBtn = XtVaCreateManagedWidget("loadAfterBtn",
387 xmToggleButtonWidgetClass, form,
388 XmNlabelString, s1=MKSTRING("Re-load file after executing command"),
389 XmNmnemonic, 'R',
390 XmNalignment, XmALIGNMENT_BEGINNING,
391 XmNset, False,
392 XmNleftAttachment, XmATTACH_POSITION,
393 XmNleftPosition, LIST_RIGHT,
394 XmNrightAttachment, XmATTACH_POSITION,
395 XmNrightPosition, RIGHT_MARGIN_POS,
396 XmNbottomAttachment, XmATTACH_POSITION,
397 XmNbottomPosition, SHELL_CMD_TOP, NULL);
398 XmStringFree(s1);
399 ucd->saveFirstBtn = XtVaCreateManagedWidget("saveFirstBtn",
400 xmToggleButtonWidgetClass, form,
401 XmNlabelString, s1=MKSTRING("Save file before executing command"),
402 XmNmnemonic, 'f',
403 XmNalignment, XmALIGNMENT_BEGINNING,
404 XmNset, False,
405 XmNleftAttachment, XmATTACH_POSITION,
406 XmNleftPosition, LIST_RIGHT,
407 XmNrightAttachment, XmATTACH_POSITION,
408 XmNrightPosition, RIGHT_MARGIN_POS,
409 XmNbottomAttachment, XmATTACH_WIDGET,
410 XmNbottomWidget, ucd->loadAfterBtn, NULL);
411 XmStringFree(s1);
412 ucd->repInpBtn = XtVaCreateManagedWidget("repInpBtn",
413 xmToggleButtonWidgetClass, form,
414 XmNlabelString, s1=MKSTRING("Output replaces input"),
415 XmNmnemonic, 'f',
416 XmNalignment, XmALIGNMENT_BEGINNING,
417 XmNset, False,
418 XmNleftAttachment, XmATTACH_POSITION,
419 XmNleftPosition, LIST_RIGHT,
420 XmNrightAttachment, XmATTACH_POSITION,
421 XmNrightPosition, RIGHT_MARGIN_POS,
422 XmNbottomAttachment, XmATTACH_WIDGET,
423 XmNbottomWidget, ucd->saveFirstBtn, NULL);
424 XmStringFree(s1);
425 outBox = XtVaCreateManagedWidget("outBox", xmRowColumnWidgetClass, form,
426 XmNpacking, XmPACK_TIGHT,
427 XmNorientation, XmHORIZONTAL,
428 XmNradioBehavior, True,
429 XmNradioAlwaysOne, True,
430 XmNleftAttachment, XmATTACH_POSITION,
431 XmNleftPosition, LIST_RIGHT + 2,
432 XmNrightAttachment, XmATTACH_POSITION,
433 XmNrightPosition, RIGHT_MARGIN_POS,
434 XmNbottomAttachment, XmATTACH_WIDGET,
435 XmNbottomWidget, ucd->repInpBtn,
436 XmNbottomOffset, 4, NULL);
437 ucd->sameOutBtn = XtVaCreateManagedWidget("sameOutBtn",
438 xmToggleButtonWidgetClass, outBox,
439 XmNlabelString, s1=MKSTRING("same document"),
440 XmNmnemonic, 'm',
441 XmNalignment, XmALIGNMENT_BEGINNING,
442 XmNmarginHeight, 0,
443 XmNset, True, NULL);
444 XmStringFree(s1);
445 XtAddCallback(ucd->sameOutBtn, XmNvalueChangedCallback, sameOutCB, ucd);
446 ucd->dlogOutBtn = XtVaCreateManagedWidget("dlogOutBtn",
447 xmToggleButtonWidgetClass, outBox,
448 XmNlabelString, s1=MKSTRING("dialog"),
449 XmNmnemonic, 'g',
450 XmNalignment, XmALIGNMENT_BEGINNING,
451 XmNmarginHeight, 0,
452 XmNset, False, NULL);
453 XmStringFree(s1);
454 ucd->winOutBtn = XtVaCreateManagedWidget("winOutBtn", xmToggleButtonWidgetClass,
455 outBox,
456 XmNlabelString, s1=MKSTRING("new document"),
457 XmNmnemonic, 'n',
458 XmNalignment, XmALIGNMENT_BEGINNING,
459 XmNmarginHeight, 0,
460 XmNset, False, NULL);
461 XmStringFree(s1);
462 outLabel = XtVaCreateManagedWidget("outLabel", xmLabelGadgetClass, form,
463 XmNlabelString, s1=MKSTRING("Command Output (stdout/stderr):"),
464 XmNalignment, XmALIGNMENT_BEGINNING,
465 XmNmarginTop, 5,
466 XmNleftAttachment, XmATTACH_POSITION,
467 XmNleftPosition, LIST_RIGHT,
468 XmNrightAttachment, XmATTACH_POSITION,
469 XmNrightPosition, RIGHT_MARGIN_POS,
470 XmNbottomAttachment, XmATTACH_WIDGET,
471 XmNbottomWidget, outBox, NULL);
472 XmStringFree(s1);
474 inpBox = XtVaCreateManagedWidget("inpBox", xmRowColumnWidgetClass, form,
475 XmNpacking, XmPACK_TIGHT,
476 XmNorientation, XmHORIZONTAL,
477 XmNradioBehavior, True,
478 XmNradioAlwaysOne, True,
479 XmNleftAttachment, XmATTACH_POSITION,
480 XmNleftPosition, LIST_RIGHT + 2,
481 XmNrightAttachment, XmATTACH_POSITION,
482 XmNrightPosition, RIGHT_MARGIN_POS,
483 XmNbottomAttachment, XmATTACH_WIDGET,
484 XmNbottomWidget, outLabel, NULL);
485 ucd->selInpBtn = XtVaCreateManagedWidget("selInpBtn", xmToggleButtonWidgetClass,
486 inpBox,
487 XmNlabelString, s1=MKSTRING("selection"),
488 XmNmnemonic, 's',
489 XmNalignment, XmALIGNMENT_BEGINNING,
490 XmNmarginHeight, 0,
491 XmNset, True, NULL);
492 XmStringFree(s1);
493 ucd->winInpBtn = XtVaCreateManagedWidget("winInpBtn",
494 xmToggleButtonWidgetClass, inpBox,
495 XmNlabelString, s1=MKSTRING("document"),
496 XmNmnemonic, 'w',
497 XmNalignment, XmALIGNMENT_BEGINNING,
498 XmNmarginHeight, 0,
499 XmNset, False, NULL);
500 XmStringFree(s1);
501 ucd->eitherInpBtn = XtVaCreateManagedWidget("eitherInpBtn",
502 xmToggleButtonWidgetClass, inpBox,
503 XmNlabelString, s1=MKSTRING("either"),
504 XmNmnemonic, 't',
505 XmNalignment, XmALIGNMENT_BEGINNING,
506 XmNmarginHeight, 0,
507 XmNset, False, NULL);
508 XmStringFree(s1);
509 ucd->noInpBtn = XtVaCreateManagedWidget("noInpBtn",
510 xmToggleButtonWidgetClass, inpBox,
511 XmNlabelString, s1=MKSTRING("none"),
512 XmNmnemonic, 'o',
513 XmNalignment, XmALIGNMENT_BEGINNING,
514 XmNmarginHeight, 0,
515 XmNset, False, NULL);
516 XmStringFree(s1);
517 inpLabel = XtVaCreateManagedWidget("inpLabel", xmLabelGadgetClass, form,
518 XmNlabelString, s1=MKSTRING("Command Input (stdin):"),
519 XmNalignment, XmALIGNMENT_BEGINNING,
520 XmNmarginTop, 5,
521 XmNleftAttachment, XmATTACH_POSITION,
522 XmNleftPosition, LIST_RIGHT,
523 XmNrightAttachment, XmATTACH_POSITION,
524 XmNrightPosition, RIGHT_MARGIN_POS,
525 XmNbottomAttachment, XmATTACH_WIDGET,
526 XmNbottomWidget, inpBox, NULL);
527 XmStringFree(s1);
529 ucd->mneTextW = XtVaCreateManagedWidget("mne", xmTextWidgetClass, form,
530 XmNcolumns, 1,
531 XmNmaxLength, 1,
532 XmNleftAttachment, XmATTACH_POSITION,
533 XmNleftPosition, RIGHT_MARGIN_POS-10,
534 XmNrightAttachment, XmATTACH_POSITION,
535 XmNrightPosition, RIGHT_MARGIN_POS,
536 XmNbottomAttachment, XmATTACH_WIDGET,
537 XmNbottomWidget, inpLabel, NULL);
538 RemapDeleteKey(ucd->mneTextW);
540 ucd->accTextW = XtVaCreateManagedWidget("acc", xmTextWidgetClass, form,
541 XmNcolumns, 12,
542 XmNmaxLength, MAX_ACCEL_LEN-1,
543 XmNcursorPositionVisible, False,
544 XmNleftAttachment, XmATTACH_POSITION,
545 XmNleftPosition, LIST_RIGHT,
546 XmNrightAttachment, XmATTACH_POSITION,
547 XmNrightPosition, RIGHT_MARGIN_POS-15,
548 XmNbottomAttachment, XmATTACH_WIDGET,
549 XmNbottomWidget, inpLabel, NULL);
550 XtAddEventHandler(ucd->accTextW, KeyPressMask, False,
551 (XtEventHandler)accKeyCB, ucd);
552 XtAddCallback(ucd->accTextW, XmNfocusCallback, accFocusCB, ucd);
553 XtAddCallback(ucd->accTextW, XmNlosingFocusCallback, accLoseFocusCB, ucd);
554 accLabel = XtVaCreateManagedWidget("accLabel", xmLabelGadgetClass, form,
555 XmNlabelString, s1=MKSTRING("Accelerator"),
556 XmNmnemonic, 'l',
557 XmNuserData, ucd->accTextW,
558 XmNalignment, XmALIGNMENT_BEGINNING,
559 XmNmarginTop, 5,
560 XmNleftAttachment, XmATTACH_POSITION,
561 XmNleftPosition, LIST_RIGHT,
562 XmNrightAttachment, XmATTACH_POSITION,
563 XmNrightPosition, LIST_RIGHT + 24,
564 XmNbottomAttachment, XmATTACH_WIDGET,
565 XmNbottomWidget, ucd->mneTextW, NULL);
566 XmStringFree(s1);
568 XtVaCreateManagedWidget("mneLabel", xmLabelGadgetClass, form,
569 XmNlabelString, s1=MKSTRING("Mnemonic"),
570 XmNmnemonic, 'i',
571 XmNuserData, ucd->mneTextW,
572 XmNalignment, XmALIGNMENT_END,
573 XmNmarginTop, 5,
574 XmNleftAttachment, XmATTACH_POSITION,
575 XmNleftPosition, LIST_RIGHT + 24,
576 XmNrightAttachment, XmATTACH_POSITION,
577 XmNrightPosition, RIGHT_MARGIN_POS,
578 XmNbottomAttachment, XmATTACH_WIDGET,
579 XmNbottomWidget, ucd->mneTextW, NULL);
580 XmStringFree(s1);
582 ucd->nameTextW = XtVaCreateManagedWidget("name", xmTextWidgetClass, form,
583 XmNleftAttachment, XmATTACH_POSITION,
584 XmNleftPosition, LIST_RIGHT,
585 XmNrightAttachment, XmATTACH_POSITION,
586 XmNrightPosition, RIGHT_MARGIN_POS,
587 XmNbottomAttachment, XmATTACH_WIDGET,
588 XmNbottomWidget, accLabel, NULL);
589 RemapDeleteKey(ucd->nameTextW);
591 nameLabel = XtVaCreateManagedWidget("nameLabel", xmLabelGadgetClass, form,
592 XmNlabelString, s1=MKSTRING("Menu Entry"),
593 XmNmnemonic, 'y',
594 XmNuserData, ucd->nameTextW,
595 XmNalignment, XmALIGNMENT_BEGINNING,
596 XmNmarginTop, 5,
597 XmNleftAttachment, XmATTACH_POSITION,
598 XmNleftPosition, LIST_RIGHT,
599 XmNbottomAttachment, XmATTACH_WIDGET,
600 XmNbottomWidget, ucd->nameTextW, NULL);
601 XmStringFree(s1);
603 XtVaCreateManagedWidget("nameNotes", xmLabelGadgetClass, form,
604 XmNlabelString, s1=MKSTRING("(> for sub-menu, @ language mode)"),
605 XmNalignment, XmALIGNMENT_END,
606 XmNmarginTop, 5,
607 XmNleftAttachment, XmATTACH_WIDGET,
608 XmNleftWidget, nameLabel,
609 XmNrightAttachment, XmATTACH_POSITION,
610 XmNrightPosition, RIGHT_MARGIN_POS,
611 XmNbottomAttachment, XmATTACH_WIDGET,
612 XmNbottomWidget, ucd->nameTextW, NULL);
613 XmStringFree(s1);
615 XtVaCreateManagedWidget("topLabel", xmLabelGadgetClass, form,
616 XmNlabelString, s1=MKSTRING(
617 "Select a shell menu item from the list at left.\n\
618 Select \"New\" to add a new command to the menu."),
619 XmNtopAttachment, XmATTACH_POSITION,
620 XmNtopPosition, 2,
621 XmNleftAttachment, XmATTACH_POSITION,
622 XmNleftPosition, LIST_RIGHT,
623 XmNrightAttachment, XmATTACH_POSITION,
624 XmNrightPosition, RIGHT_MARGIN_POS,
625 XmNbottomAttachment, XmATTACH_WIDGET,
626 XmNbottomWidget, nameLabel, NULL);
627 XmStringFree(s1);
629 cmdLabel = XtVaCreateManagedWidget("cmdLabel", xmLabelGadgetClass, form,
630 XmNlabelString, s1=MKSTRING("Shell Command to Execute"),
631 XmNmnemonic, 'x',
632 XmNalignment, XmALIGNMENT_BEGINNING,
633 XmNmarginTop, 5,
634 XmNtopAttachment, XmATTACH_POSITION,
635 XmNtopPosition, SHELL_CMD_TOP,
636 XmNleftAttachment, XmATTACH_POSITION,
637 XmNleftPosition, LEFT_MARGIN_POS, NULL);
638 XmStringFree(s1);
639 XtVaCreateManagedWidget("cmdLabel", xmLabelGadgetClass, form,
640 XmNlabelString, s1=MKSTRING("(% expands to current filename, # to line number)"),
641 XmNalignment, XmALIGNMENT_END,
642 XmNmarginTop, 5,
643 XmNtopAttachment, XmATTACH_POSITION,
644 XmNtopPosition, SHELL_CMD_TOP,
645 XmNleftAttachment, XmATTACH_WIDGET,
646 XmNleftWidget, cmdLabel,
647 XmNrightAttachment, XmATTACH_POSITION,
648 XmNrightPosition, RIGHT_MARGIN_POS, NULL);
649 XmStringFree(s1);
651 okBtn = XtVaCreateManagedWidget("ok",xmPushButtonWidgetClass,form,
652 XmNlabelString, s1=MKSTRING("OK"),
653 XmNmarginWidth, BUTTON_WIDTH_MARGIN,
654 XmNleftAttachment, XmATTACH_POSITION,
655 XmNleftPosition, 13,
656 XmNrightAttachment, XmATTACH_POSITION,
657 XmNrightPosition, 29,
658 XmNbottomAttachment, XmATTACH_POSITION,
659 XmNbottomPosition, 99, NULL);
660 XtAddCallback(okBtn, XmNactivateCallback, okCB, ucd);
661 XmStringFree(s1);
663 applyBtn = XtVaCreateManagedWidget("apply",xmPushButtonWidgetClass,form,
664 XmNlabelString, s1=MKSTRING("Apply"),
665 XmNmnemonic, 'A',
666 XmNleftAttachment, XmATTACH_POSITION,
667 XmNleftPosition, 42,
668 XmNrightAttachment, XmATTACH_POSITION,
669 XmNrightPosition, 58,
670 XmNbottomAttachment, XmATTACH_POSITION,
671 XmNbottomPosition, 99, NULL);
672 XtAddCallback(applyBtn, XmNactivateCallback, applyCB, ucd);
673 XmStringFree(s1);
675 closeBtn = XtVaCreateManagedWidget("close",
676 xmPushButtonWidgetClass, form,
677 XmNlabelString, s1=MKSTRING("Close"),
678 XmNleftAttachment, XmATTACH_POSITION,
679 XmNleftPosition, 71,
680 XmNrightAttachment, XmATTACH_POSITION,
681 XmNrightPosition, 87,
682 XmNbottomAttachment, XmATTACH_POSITION,
683 XmNbottomPosition, 99,
684 NULL);
685 XtAddCallback(closeBtn, XmNactivateCallback, closeCB, ucd);
686 XmStringFree(s1);
688 ac = 0;
689 XtSetArg(args[ac], XmNeditMode, XmMULTI_LINE_EDIT); ac++;
690 XtSetArg(args[ac], XmNscrollHorizontal, False); ac++;
691 XtSetArg(args[ac], XmNwordWrap, True); ac++;
692 XtSetArg(args[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++;
693 XtSetArg(args[ac], XmNtopWidget, cmdLabel); ac++;
694 XtSetArg(args[ac], XmNleftAttachment, XmATTACH_POSITION); ac++;
695 XtSetArg(args[ac], XmNleftPosition, LEFT_MARGIN_POS); ac++;
696 XtSetArg(args[ac], XmNrightAttachment, XmATTACH_POSITION); ac++;
697 XtSetArg(args[ac], XmNrightPosition, RIGHT_MARGIN_POS); ac++;
698 XtSetArg(args[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
699 XtSetArg(args[ac], XmNbottomWidget, okBtn); ac++;
700 XtSetArg(args[ac], XmNbottomOffset, 5); ac++;
701 ucd->cmdTextW = XmCreateScrolledText(form, "name", args, ac);
702 AddMouseWheelSupport(ucd->cmdTextW);
703 XtManageChild(ucd->cmdTextW);
704 MakeSingleLineTextW(ucd->cmdTextW);
705 RemapDeleteKey(ucd->cmdTextW);
706 XtVaSetValues(cmdLabel, XmNuserData, ucd->cmdTextW, NULL); /* for mnemonic */
708 /* Disable text input for the accelerator key widget, let the
709 event handler manage it instead */
710 disableTextW(ucd->accTextW);
712 /* initializs the dialog fields to match "New" list item */
713 updateDialogFields(NULL, ucd);
715 /* Set initial default button */
716 XtVaSetValues(form, XmNdefaultButton, okBtn, NULL);
717 XtVaSetValues(form, XmNcancelButton, closeBtn, NULL);
719 /* Handle mnemonic selection of buttons and focus to dialog */
720 AddDialogMnemonicHandler(form, FALSE);
722 /* realize all of the widgets in the new window */
723 RealizeWithoutForcingPosition(ucd->dlogShell);
727 ** Present a dialogs for editing the user specified commands in the Macro
728 ** and background menus
730 void EditMacroMenu(WindowInfo *window)
732 editMacroOrBGMenu(window, MACRO_CMDS);
734 void EditBGMenu(WindowInfo *window)
736 editMacroOrBGMenu(window, BG_MENU_CMDS);
739 static void editMacroOrBGMenu(WindowInfo *window, int dialogType)
741 Widget form, accLabel, pasteReplayBtn;
742 Widget nameLabel, cmdLabel, okBtn, applyBtn, closeBtn;
743 userCmdDialog *ucd;
744 char *title;
745 XmString s1;
746 int ac, i;
747 Arg args[20];
749 /* if the dialog is already displayed, just pop it to the top and return */
750 if (dialogType == MACRO_CMDS && MacroCmdDialog != NULL) {
751 RaiseDialogWindow(MacroCmdDialog);
752 return;
754 if (dialogType == BG_MENU_CMDS && BGMenuCmdDialog != NULL) {
755 RaiseDialogWindow(BGMenuCmdDialog);
756 return;
759 /* Create a structure for keeping track of dialog state */
760 ucd = (userCmdDialog *)XtMalloc(sizeof(userCmdDialog));
761 ucd->window = window;
763 /* Set the dialog to operate on the Macro menu */
764 ucd->menuItemsList = (menuItemRec **)XtMalloc(sizeof(menuItemRec **) *
765 MAX_ITEMS_PER_MENU);
766 if (dialogType == MACRO_CMDS) {
767 for (i=0; i<NMacroMenuItems; i++)
768 ucd->menuItemsList[i] = copyMenuItemRec(MacroMenuItems[i]);
769 ucd->nMenuItems = NMacroMenuItems;
770 } else { /* BG_MENU_CMDS */
771 for (i=0; i<NBGMenuItems; i++)
772 ucd->menuItemsList[i] = copyMenuItemRec(BGMenuItems[i]);
773 ucd->nMenuItems = NBGMenuItems;
775 ucd->dialogType = dialogType;
777 title = dialogType == MACRO_CMDS ? "Macro Commands" :
778 "Window Background Menu";
779 ac = 0;
780 XtSetArg(args[ac], XmNdeleteResponse, XmDO_NOTHING); ac++;
781 XtSetArg(args[ac], XmNiconName, title); ac++;
782 XtSetArg(args[ac], XmNtitle, title); ac++;
783 ucd->dlogShell = CreateWidget(TheAppShell, "macros",
784 topLevelShellWidgetClass, args, ac);
785 AddSmallIcon(ucd->dlogShell);
786 form = XtVaCreateManagedWidget("editMacroCommands", xmFormWidgetClass,
787 ucd->dlogShell, XmNautoUnmanage, False,
788 XmNresizePolicy, XmRESIZE_NONE, NULL);
789 XtAddCallback(form, XmNdestroyCallback, destroyCB, ucd);
790 AddMotifCloseCallback(ucd->dlogShell, closeCB, ucd);
792 ac = 0;
793 XtSetArg(args[ac], XmNtopAttachment, XmATTACH_POSITION); ac++;
794 XtSetArg(args[ac], XmNtopPosition, 2); ac++;
795 XtSetArg(args[ac], XmNleftAttachment, XmATTACH_POSITION); ac++;
796 XtSetArg(args[ac], XmNleftPosition, LEFT_MARGIN_POS); ac++;
797 XtSetArg(args[ac], XmNrightAttachment, XmATTACH_POSITION); ac++;
798 XtSetArg(args[ac], XmNrightPosition, LIST_RIGHT-1); ac++;
799 XtSetArg(args[ac], XmNbottomAttachment, XmATTACH_POSITION); ac++;
800 XtSetArg(args[ac], XmNbottomPosition, MACRO_CMD_TOP); ac++;
801 ucd->managedList = CreateManagedList(form, "list", args, ac,
802 (void **)ucd->menuItemsList, &ucd->nMenuItems, MAX_ITEMS_PER_MENU, 20,
803 getDialogDataCB, ucd, setDialogDataCB, ucd, freeItemCB);
805 ucd->selInpBtn = XtVaCreateManagedWidget("selInpBtn",
806 xmToggleButtonWidgetClass, form,
807 XmNlabelString, s1=MKSTRING("Requires Selection"),
808 XmNmnemonic, 'R',
809 XmNalignment, XmALIGNMENT_BEGINNING,
810 XmNmarginHeight, 0,
811 XmNset, False,
812 XmNleftAttachment, XmATTACH_POSITION,
813 XmNleftPosition, LIST_RIGHT,
814 XmNbottomAttachment, XmATTACH_POSITION,
815 XmNbottomPosition, MACRO_CMD_TOP, NULL);
816 XmStringFree(s1);
818 ucd->mneTextW = XtVaCreateManagedWidget("mne", xmTextWidgetClass, form,
819 XmNcolumns, 1,
820 XmNmaxLength, 1,
821 XmNleftAttachment, XmATTACH_POSITION,
822 XmNleftPosition, RIGHT_MARGIN_POS-21-5,
823 XmNrightAttachment, XmATTACH_POSITION,
824 XmNrightPosition, RIGHT_MARGIN_POS-21,
825 XmNbottomAttachment, XmATTACH_WIDGET,
826 XmNbottomWidget, ucd->selInpBtn,
827 XmNbottomOffset, 5, NULL);
828 RemapDeleteKey(ucd->mneTextW);
830 ucd->accTextW = XtVaCreateManagedWidget("acc", xmTextWidgetClass, form,
831 XmNcolumns, 12,
832 XmNmaxLength, MAX_ACCEL_LEN-1,
833 XmNcursorPositionVisible, False,
834 XmNleftAttachment, XmATTACH_POSITION,
835 XmNleftPosition, LIST_RIGHT,
836 XmNrightAttachment, XmATTACH_POSITION,
837 XmNrightPosition, RIGHT_MARGIN_POS-20-10,
838 XmNbottomAttachment, XmATTACH_WIDGET,
839 XmNbottomWidget, ucd->selInpBtn,
840 XmNbottomOffset, 5, NULL);
841 XtAddEventHandler(ucd->accTextW, KeyPressMask, False,
842 (XtEventHandler)accKeyCB, ucd);
843 XtAddCallback(ucd->accTextW, XmNfocusCallback, accFocusCB, ucd);
844 XtAddCallback(ucd->accTextW, XmNlosingFocusCallback, accLoseFocusCB, ucd);
846 accLabel = XtVaCreateManagedWidget("accLabel", xmLabelGadgetClass, form,
847 XmNlabelString, s1=MKSTRING("Accelerator"),
848 XmNmnemonic, 'l',
849 XmNuserData, ucd->accTextW,
850 XmNalignment, XmALIGNMENT_BEGINNING,
851 XmNmarginTop, 5,
852 XmNleftAttachment, XmATTACH_POSITION,
853 XmNleftPosition, LIST_RIGHT,
854 XmNrightAttachment, XmATTACH_POSITION,
855 XmNrightPosition, LIST_RIGHT + 22,
856 XmNbottomAttachment, XmATTACH_WIDGET,
857 XmNbottomWidget, ucd->mneTextW, NULL);
858 XmStringFree(s1);
860 XtVaCreateManagedWidget("mneLabel", xmLabelGadgetClass, form,
861 XmNlabelString, s1=MKSTRING("Mnemonic"),
862 XmNmnemonic, 'i',
863 XmNuserData, ucd->mneTextW,
864 XmNalignment, XmALIGNMENT_END,
865 XmNmarginTop, 5,
866 XmNleftAttachment, XmATTACH_POSITION,
867 XmNleftPosition, LIST_RIGHT + 22,
868 XmNrightAttachment, XmATTACH_POSITION,
869 XmNrightPosition, RIGHT_MARGIN_POS-21,
870 XmNbottomAttachment, XmATTACH_WIDGET,
871 XmNbottomWidget, ucd->mneTextW, NULL);
872 XmStringFree(s1);
874 pasteReplayBtn = XtVaCreateManagedWidget("pasteReplay",
875 xmPushButtonWidgetClass, form,
876 XmNlabelString, s1=MKSTRING("Paste Learn/\nReplay Macro"),
877 XmNmnemonic, 'P',
878 XmNsensitive, GetReplayMacro() != NULL,
879 XmNleftAttachment, XmATTACH_POSITION,
880 XmNleftPosition, RIGHT_MARGIN_POS-20,
881 XmNrightAttachment, XmATTACH_POSITION,
882 XmNrightPosition, RIGHT_MARGIN_POS,
883 XmNbottomAttachment, XmATTACH_POSITION,
884 XmNbottomPosition, MACRO_CMD_TOP, NULL);
885 XtAddCallback(pasteReplayBtn, XmNactivateCallback,
886 pasteReplayCB, ucd);
887 XmStringFree(s1);
889 ucd->nameTextW = XtVaCreateManagedWidget("name", xmTextWidgetClass, form,
890 XmNleftAttachment, XmATTACH_POSITION,
891 XmNleftPosition, LIST_RIGHT,
892 XmNrightAttachment, XmATTACH_POSITION,
893 XmNrightPosition, RIGHT_MARGIN_POS,
894 XmNbottomAttachment, XmATTACH_WIDGET,
895 XmNbottomWidget, accLabel, NULL);
896 RemapDeleteKey(ucd->nameTextW);
898 nameLabel = XtVaCreateManagedWidget("nameLabel", xmLabelGadgetClass, form,
899 XmNlabelString, s1=MKSTRING("Menu Entry"),
900 XmNmnemonic, 'y',
901 XmNuserData, ucd->nameTextW,
902 XmNalignment, XmALIGNMENT_BEGINNING,
903 XmNmarginTop, 5,
904 XmNleftAttachment, XmATTACH_POSITION,
905 XmNleftPosition, LIST_RIGHT,
906 XmNbottomAttachment, XmATTACH_WIDGET,
907 XmNbottomWidget, ucd->nameTextW, NULL);
908 XmStringFree(s1);
910 XtVaCreateManagedWidget("nameNotes", xmLabelGadgetClass, form,
911 XmNlabelString, s1=MKSTRING("(> for sub-menu, @ language mode)"),
912 XmNalignment, XmALIGNMENT_END,
913 XmNmarginTop, 5,
914 XmNleftAttachment, XmATTACH_WIDGET,
915 XmNleftWidget, nameLabel,
916 XmNrightAttachment, XmATTACH_POSITION,
917 XmNrightPosition, RIGHT_MARGIN_POS,
918 XmNbottomAttachment, XmATTACH_WIDGET,
919 XmNbottomWidget, ucd->nameTextW, NULL);
920 XmStringFree(s1);
922 XtVaCreateManagedWidget("topLabel", xmLabelGadgetClass, form,
923 XmNlabelString, s1=MKSTRING(
924 "Select a macro menu item from the list at left.\n\
925 Select \"New\" to add a new command to the menu."),
926 XmNtopAttachment, XmATTACH_POSITION,
927 XmNtopPosition, 2,
928 XmNleftAttachment, XmATTACH_POSITION,
929 XmNleftPosition, LIST_RIGHT,
930 XmNrightAttachment, XmATTACH_POSITION,
931 XmNrightPosition, RIGHT_MARGIN_POS,
932 XmNbottomAttachment, XmATTACH_WIDGET,
933 XmNbottomWidget, nameLabel, NULL);
934 XmStringFree(s1);
936 cmdLabel = XtVaCreateManagedWidget("cmdLabel", xmLabelGadgetClass, form,
937 XmNlabelString, s1=MKSTRING("Macro Command to Execute"),
938 XmNmnemonic, 'x',
939 XmNalignment, XmALIGNMENT_BEGINNING,
940 XmNmarginTop, 5,
941 XmNtopAttachment, XmATTACH_POSITION,
942 XmNtopPosition, MACRO_CMD_TOP,
943 XmNleftAttachment, XmATTACH_POSITION,
944 XmNleftPosition, LEFT_MARGIN_POS, NULL);
945 XmStringFree(s1);
947 okBtn = XtVaCreateManagedWidget("ok",xmPushButtonWidgetClass,form,
948 XmNlabelString, s1=MKSTRING("OK"),
949 XmNmarginWidth, BUTTON_WIDTH_MARGIN,
950 XmNleftAttachment, XmATTACH_POSITION,
951 XmNleftPosition, 8,
952 XmNrightAttachment, XmATTACH_POSITION,
953 XmNrightPosition, 23,
954 XmNbottomAttachment, XmATTACH_POSITION,
955 XmNbottomPosition, 99, NULL);
956 XtAddCallback(okBtn, XmNactivateCallback, okCB, ucd);
957 XmStringFree(s1);
959 applyBtn = XtVaCreateManagedWidget("apply",xmPushButtonWidgetClass,form,
960 XmNlabelString, s1=MKSTRING("Apply"),
961 XmNmnemonic, 'A',
962 XmNleftAttachment, XmATTACH_POSITION,
963 XmNleftPosition, 31,
964 XmNrightAttachment, XmATTACH_POSITION,
965 XmNrightPosition, 46,
966 XmNbottomAttachment, XmATTACH_POSITION,
967 XmNbottomPosition, 99, NULL);
968 XtAddCallback(applyBtn, XmNactivateCallback, applyCB, ucd);
969 XmStringFree(s1);
971 applyBtn = XtVaCreateManagedWidget("check",xmPushButtonWidgetClass,form,
972 XmNlabelString, s1=MKSTRING("Check"),
973 XmNmnemonic, 'C',
974 XmNleftAttachment, XmATTACH_POSITION,
975 XmNleftPosition, 54,
976 XmNrightAttachment, XmATTACH_POSITION,
977 XmNrightPosition, 69,
978 XmNbottomAttachment, XmATTACH_POSITION,
979 XmNbottomPosition, 99, NULL);
980 XtAddCallback(applyBtn, XmNactivateCallback, checkCB, ucd);
981 XmStringFree(s1);
983 closeBtn = XtVaCreateManagedWidget("close",
984 xmPushButtonWidgetClass, form,
985 XmNlabelString, s1=MKSTRING("Close"),
986 XmNleftAttachment, XmATTACH_POSITION,
987 XmNleftPosition, 77,
988 XmNrightAttachment, XmATTACH_POSITION,
989 XmNrightPosition, 92,
990 XmNbottomAttachment, XmATTACH_POSITION,
991 XmNbottomPosition, 99,
992 NULL);
993 XtAddCallback(closeBtn, XmNactivateCallback, closeCB, ucd);
994 XmStringFree(s1);
996 ac = 0;
997 XtSetArg(args[ac], XmNeditMode, XmMULTI_LINE_EDIT); ac++;
998 XtSetArg(args[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++;
999 XtSetArg(args[ac], XmNtopWidget, cmdLabel); ac++;
1000 XtSetArg(args[ac], XmNleftAttachment, XmATTACH_POSITION); ac++;
1001 XtSetArg(args[ac], XmNleftPosition, LEFT_MARGIN_POS); ac++;
1002 XtSetArg(args[ac], XmNrightAttachment, XmATTACH_POSITION); ac++;
1003 XtSetArg(args[ac], XmNrightPosition, RIGHT_MARGIN_POS); ac++;
1004 XtSetArg(args[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1005 XtSetArg(args[ac], XmNbottomWidget, okBtn); ac++;
1006 XtSetArg(args[ac], XmNbottomOffset, 5); ac++;
1007 ucd->cmdTextW = XmCreateScrolledText(form, "name", args, ac);
1008 AddMouseWheelSupport(ucd->cmdTextW);
1009 XtManageChild(ucd->cmdTextW);
1010 RemapDeleteKey(ucd->cmdTextW);
1011 XtVaSetValues(cmdLabel, XmNuserData, ucd->cmdTextW, NULL); /* for mnemonic */
1013 /* Disable text input for the accelerator key widget, let the
1014 event handler manage it instead */
1015 disableTextW(ucd->accTextW);
1017 /* initializs the dialog fields to match "New" list item */
1018 updateDialogFields(NULL, ucd);
1020 /* Set initial default button */
1021 XtVaSetValues(form, XmNdefaultButton, okBtn, NULL);
1022 XtVaSetValues(form, XmNcancelButton, closeBtn, NULL);
1024 /* Handle mnemonic selection of buttons and focus to dialog */
1025 AddDialogMnemonicHandler(form, FALSE);
1027 /* Make widgets for top level shell and paste-replay buttons available
1028 to other functions */
1029 if (dialogType == MACRO_CMDS) {
1030 MacroCmdDialog = ucd->dlogShell;
1031 MacroPasteReplayBtn = pasteReplayBtn;
1032 } else {
1033 BGMenuCmdDialog = ucd->dlogShell;
1034 BGMenuPasteReplayBtn = pasteReplayBtn;
1037 /* Realize all of the widgets in the new dialog */
1038 RealizeWithoutForcingPosition(ucd->dlogShell);
1042 ** Update the Shell, Macro, and Window Background menus of window
1043 ** "window" from the currently loaded command descriptions.
1045 void UpdateUserMenus(WindowInfo *window)
1047 if (!IsTopDocument(window))
1048 return;
1050 /* update user menus, which are shared over all documents, only
1051 if language mode was changed */
1052 if (window->userMenuCache->umcLanguageMode != window->languageMode) {
1053 #ifndef VMS
1054 updateMenu(window, SHELL_CMDS);
1055 #endif
1056 updateMenu(window, MACRO_CMDS);
1058 /* remember language mode assigned to shared user menus */
1059 window->userMenuCache->umcLanguageMode = window->languageMode;
1062 /* update background menu, which is owned by a single document, only
1063 if language mode was changed */
1064 if (window->userBGMenuCache.ubmcLanguageMode != window->languageMode) {
1065 updateMenu(window, BG_MENU_CMDS);
1067 /* remember language mode assigned to background menu */
1068 window->userBGMenuCache.ubmcLanguageMode = window->languageMode;
1073 ** Dim/undim buttons for pasting replay macros into macro and bg menu dialogs
1075 void DimPasteReplayBtns(int sensitive)
1077 if (MacroCmdDialog != NULL)
1078 XtSetSensitive(MacroPasteReplayBtn, sensitive);
1079 if (BGMenuCmdDialog != NULL)
1080 XtSetSensitive(BGMenuPasteReplayBtn, sensitive);
1084 ** Dim/undim user programmable menu items which depend on there being
1085 ** a selection in their associated window.
1087 void DimSelectionDepUserMenuItems(WindowInfo *window, int sensitive)
1089 if (!IsTopDocument(window))
1090 return;
1092 #ifndef VMS
1093 dimSelDepItemsInMenu(window->shellMenuPane, ShellMenuItems,
1094 NShellMenuItems, sensitive);
1095 #endif /*VMS*/
1096 dimSelDepItemsInMenu(window->macroMenuPane, MacroMenuItems,
1097 NMacroMenuItems, sensitive);
1098 dimSelDepItemsInMenu(window->bgMenuPane, BGMenuItems,
1099 NBGMenuItems, sensitive);
1102 static void dimSelDepItemsInMenu(Widget menuPane, menuItemRec **menuList,
1103 int nMenuItems, int sensitive)
1105 WidgetList items;
1106 Widget subMenu;
1107 XtPointer userData;
1108 int n, index;
1109 Cardinal nItems;
1111 XtVaGetValues(menuPane, XmNchildren, &items, XmNnumChildren, &nItems, NULL);
1112 for (n=0; n<(int)nItems; n++) {
1113 XtVaGetValues(items[n], XmNuserData, &userData, NULL);
1114 if (userData != (XtPointer)PERMANENT_MENU_ITEM) {
1115 if (XtClass(items[n]) == xmCascadeButtonWidgetClass) {
1116 XtVaGetValues(items[n], XmNsubMenuId, &subMenu, NULL);
1117 dimSelDepItemsInMenu(subMenu, menuList, nMenuItems, sensitive);
1118 } else {
1119 index = (int)userData - 10;
1120 if (index <0 || index >= nMenuItems)
1121 return;
1122 if (menuList[index]->input == FROM_SELECTION)
1123 XtSetSensitive(items[n], sensitive);
1130 ** Harmless kludge for making undo/redo menu items in background menu properly
1131 ** sensitive (even though they're programmable) along with real undo item
1132 ** in the Edit menu
1134 void SetBGMenuUndoSensitivity(WindowInfo *window, int sensitive)
1136 if (window->bgMenuUndoItem != NULL)
1137 SetSensitive(window, window->bgMenuUndoItem, sensitive);
1139 void SetBGMenuRedoSensitivity(WindowInfo *window, int sensitive)
1141 if (window->bgMenuRedoItem != NULL)
1142 SetSensitive(window, window->bgMenuRedoItem, sensitive);
1146 ** Generate a text string for the preferences file describing the contents
1147 ** of the shell cmd list. This string is not exactly of the form that it
1148 ** can be read by LoadShellCmdsString, rather, it is what needs to be written
1149 ** to a resource file such that it will read back in that form.
1151 char *WriteShellCmdsString(void)
1153 return writeMenuItemString(ShellMenuItems, NShellMenuItems,
1154 SHELL_CMDS);
1158 ** Generate a text string for the preferences file describing the contents of
1159 ** the macro menu and background menu commands lists. These strings are not
1160 ** exactly of the form that it can be read by LoadMacroCmdsString, rather, it
1161 ** is what needs to be written to a resource file such that it will read back
1162 ** in that form.
1164 char *WriteMacroCmdsString(void)
1166 return writeMenuItemString(MacroMenuItems, NMacroMenuItems, MACRO_CMDS);
1169 char *WriteBGMenuCmdsString(void)
1171 return writeMenuItemString(BGMenuItems, NBGMenuItems, BG_MENU_CMDS);
1175 ** Read a string representing shell command menu items and add them to the
1176 ** internal list used for constructing shell menus
1178 int LoadShellCmdsString(char *inString)
1180 return loadMenuItemString(inString, ShellMenuItems, &NShellMenuItems,
1181 SHELL_CMDS);
1185 ** Read strings representing macro menu or background menu command menu items
1186 ** and add them to the internal lists used for constructing menus
1188 int LoadMacroCmdsString(char *inString)
1190 return loadMenuItemString(inString, MacroMenuItems, &NMacroMenuItems,
1191 MACRO_CMDS);
1194 int LoadBGMenuCmdsString(char *inString)
1196 return loadMenuItemString(inString, BGMenuItems, &NBGMenuItems,
1197 BG_MENU_CMDS);
1201 ** Cache user menus:
1202 ** Setup user menu info after read of macro, shell and background menu
1203 ** string (reason: language mode info from preference string is read *after*
1204 ** user menu preference string was read).
1206 void SetupUserMenuInfo(void)
1208 parseMenuItemList(ShellMenuItems, NShellMenuItems, ShellMenuInfo, &ShellSubMenus);
1209 parseMenuItemList(MacroMenuItems, NMacroMenuItems, MacroMenuInfo, &MacroSubMenus);
1210 parseMenuItemList(BGMenuItems , NBGMenuItems , BGMenuInfo , &BGSubMenus);
1214 ** Cache user menus:
1215 ** Update user menu info to take into account e.g. change of language modes
1216 ** (i.e. add / move / delete of language modes etc).
1218 void UpdateUserMenuInfo(void)
1220 freeUserMenuInfoList(ShellMenuInfo, NShellMenuItems);
1221 freeSubMenuCache(&ShellSubMenus);
1222 parseMenuItemList(ShellMenuItems, NShellMenuItems, ShellMenuInfo, &ShellSubMenus);
1224 freeUserMenuInfoList(MacroMenuInfo, NMacroMenuItems);
1225 freeSubMenuCache(&MacroSubMenus);
1226 parseMenuItemList(MacroMenuItems, NMacroMenuItems, MacroMenuInfo, &MacroSubMenus);
1228 freeUserMenuInfoList(BGMenuInfo, NBGMenuItems);
1229 freeSubMenuCache(&BGSubMenus);
1230 parseMenuItemList(BGMenuItems, NBGMenuItems, BGMenuInfo, &BGSubMenus);
1234 ** Search through the shell menu and execute the first command with menu item
1235 ** name "itemName". Returns True on successs and False on failure.
1237 #ifndef VMS
1238 int DoNamedShellMenuCmd(WindowInfo *window, const char *itemName, int fromMacro)
1240 int i;
1242 for (i=0; i<NShellMenuItems; i++) {
1243 if (!strcmp(ShellMenuItems[i]->name, itemName)) {
1244 if (ShellMenuItems[i]->output == TO_SAME_WINDOW &&
1245 CheckReadOnly(window))
1246 return False;
1247 DoShellMenuCmd(window, ShellMenuItems[i]->cmd,
1248 ShellMenuItems[i]->input, ShellMenuItems[i]->output,
1249 ShellMenuItems[i]->repInput, ShellMenuItems[i]->saveFirst,
1250 ShellMenuItems[i]->loadAfter, fromMacro);
1251 return True;
1254 return False;
1256 #endif /*VMS*/
1259 ** Search through the Macro or background menu and execute the first command
1260 ** with menu item name "itemName". Returns True on successs and False on
1261 ** failure.
1263 int DoNamedMacroMenuCmd(WindowInfo *window, const char *itemName)
1265 int i;
1267 for (i=0; i<NMacroMenuItems; i++) {
1268 if (!strcmp(MacroMenuItems[i]->name, itemName)) {
1269 DoMacro(window, MacroMenuItems[i]->cmd, "macro menu command");
1270 return True;
1273 return False;
1276 int DoNamedBGMenuCmd(WindowInfo *window, const char *itemName)
1278 int i;
1280 for (i=0; i<NBGMenuItems; i++) {
1281 if (!strcmp(BGMenuItems[i]->name, itemName)) {
1282 DoMacro(window, BGMenuItems[i]->cmd, "background menu macro");
1283 return True;
1286 return False;
1290 ** Cache user menus:
1291 ** Rebuild all of the Shell, Macro, Background menus of given editor window.
1293 void RebuildAllMenus(WindowInfo *window)
1295 rebuildMenu(window, SHELL_CMDS);
1296 rebuildMenu(window, MACRO_CMDS);
1297 rebuildMenu(window, BG_MENU_CMDS);
1301 ** Cache user menus:
1302 ** Rebuild either Shell, Macro or Background menus of all editor windows.
1304 static void rebuildMenuOfAllWindows(int menuType)
1306 WindowInfo *w;
1308 for (w=WindowList; w!=NULL; w=w->next)
1309 rebuildMenu(w, menuType);
1313 ** Rebuild either the Shell, Macro or Background menu of "window", depending
1314 ** on value of "menuType". Rebuild is realized by following main steps:
1315 ** - dismiss user (sub) menu tearoff.
1316 ** - delete all user defined menu widgets.
1317 ** - update user menu including (re)creation of menu widgets.
1319 static void rebuildMenu(WindowInfo *window, int menuType)
1321 selectedUserMenu menu;
1323 /* Background menu is always rebuild (exists once per document).
1324 Shell, macro (user) menu cache is rebuild only, if given window is
1325 currently displayed on top. */
1326 if (menuType != BG_MENU_CMDS && !IsTopDocument(window))
1327 return;
1329 /* Fetch the appropriate menu data */
1330 selectUserMenu(window, menuType, &menu);
1332 /* dismiss user menu tearoff, to workaround the quick
1333 but noticeable shrink-expand bug, most probably
1334 triggered by the rebuild of the user menus. In any
1335 case, the submenu tearoffs will later be dismissed
1336 too in order to prevent dangling tearoffs, so doing
1337 this also for the main user menu tearoffs shouldn't
1338 be so bad */
1339 if (!XmIsMenuShell(XtParent(menu.sumMenuPane)))
1340 _XmDismissTearOff(XtParent(menu.sumMenuPane), NULL, NULL);
1342 /* destroy all widgets related to menu pane */
1343 deleteMenuItems(menu.sumMenuPane);
1345 /* remove cached user menu info */
1346 freeUserMenuList(menu.sumMainMenuList);
1347 *menu.sumMenuCreated = False;
1349 /* re-create & cache user menu items */
1350 updateMenu(window, menuType);
1354 ** Fetch the appropriate menu info for given menu type
1356 static void selectUserMenu(WindowInfo *window, int menuType, selectedUserMenu *menu)
1358 if (menuType == SHELL_CMDS) {
1359 menu->sumMenuPane = window->shellMenuPane;
1360 menu->sumNbrOfListItems = NShellMenuItems;
1361 menu->sumItemList = ShellMenuItems;
1362 menu->sumInfoList = ShellMenuInfo;
1363 menu->sumSubMenus = &ShellSubMenus;
1364 menu->sumMainMenuList = &window->userMenuCache->umcShellMenuList;
1365 menu->sumMenuCreated = &window->userMenuCache->umcShellMenuCreated;
1366 } else if (menuType == MACRO_CMDS) {
1367 menu->sumMenuPane = window->macroMenuPane;
1368 menu->sumNbrOfListItems = NMacroMenuItems;
1369 menu->sumItemList = MacroMenuItems;
1370 menu->sumInfoList = MacroMenuInfo;
1371 menu->sumSubMenus = &MacroSubMenus;
1372 menu->sumMainMenuList = &window->userMenuCache->umcMacroMenuList;
1373 menu->sumMenuCreated = &window->userMenuCache->umcMacroMenuCreated;
1374 } else { /* BG_MENU_CMDS */
1375 menu->sumMenuPane = window->bgMenuPane;
1376 menu->sumNbrOfListItems = NBGMenuItems;
1377 menu->sumItemList = BGMenuItems;
1378 menu->sumInfoList = BGMenuInfo;
1379 menu->sumSubMenus = &BGSubMenus;
1380 menu->sumMainMenuList = &window->userBGMenuCache.ubmcMenuList;
1381 menu->sumMenuCreated = &window->userBGMenuCache.ubmcMenuCreated;
1383 menu->sumType = menuType;
1387 ** Updates either the Shell, Macro or Background menu of "window", depending
1388 ** on value of "menuType". Update is realized by following main steps:
1389 ** - set / reset "to be managed" flag of user menu info list items
1390 ** according to current selected language mode.
1391 ** - create *all* user menu items (widgets etc). related to given
1392 ** window & menu type, if not done before.
1393 ** - manage / unmanage user menu widgets according to "to be managed"
1394 ** indication of user menu info list items.
1396 static void updateMenu(WindowInfo *window, int menuType)
1398 selectedUserMenu menu;
1400 /* Fetch the appropriate menu data */
1401 selectUserMenu(window, menuType, &menu);
1403 /* Set / reset "to be managed" flag of all info list items */
1404 applyLangModeToUserMenuInfo(menu.sumInfoList, menu.sumNbrOfListItems,
1405 window->languageMode);
1407 /* create user menu items, if not done before */
1408 if (!*menu.sumMenuCreated)
1409 createMenuItems(window, &menu);
1411 /* manage user menu items depending on current language mode */
1412 manageUserMenu(&menu, window);
1414 if (menuType == BG_MENU_CMDS) {
1415 /* Set the proper sensitivity of items which may be dimmed */
1416 SetBGMenuUndoSensitivity(window, XtIsSensitive(window->undoItem));
1417 SetBGMenuRedoSensitivity(window, XtIsSensitive(window->redoItem));
1420 DimSelectionDepUserMenuItems(window, window->buffer->primary.selected);
1424 ** Manually adjust the dimension of the menuShell _before_
1425 ** re-managing the menu pane, to either expose hidden menu
1426 ** entries or remove empty space.
1428 static void manageTearOffMenu(Widget menuPane)
1430 Dimension width, height, border;
1432 /* somehow OM went into a long CPU cycling when we
1433 attempt to change the shell window dimension by
1434 setting the XmNwidth & XmNheight directly. Using
1435 XtResizeWidget() seem to fix it */
1436 XtVaGetValues(XtParent(menuPane), XmNborderWidth, &border, NULL);
1437 XtVaGetValues(menuPane, XmNwidth, &width, XmNheight, &height, NULL);
1438 XtResizeWidget(XtParent(menuPane), width, height, border);
1440 XtManageChild(menuPane);
1444 ** Cache user menus:
1445 ** Reset manage mode of user menu items in window cache.
1447 static void resetManageMode(UserMenuList *list)
1449 int i;
1450 UserMenuListElement *element;
1452 for (i=0; i<list->umlNbrItems; i ++) {
1453 element = list->umlItems[i];
1455 /* remember current manage mode before reset it to
1456 "unmanaged" */
1457 element->umlePrevManageMode = element->umleManageMode;
1458 element->umleManageMode = UMMM_UNMANAGE;
1460 /* recursively reset manage mode of sub-menus */
1461 if (element->umleSubMenuList != NULL)
1462 resetManageMode(element->umleSubMenuList);
1467 ** Cache user menus:
1468 ** Manage all menu widgets of given user sub-menu list.
1470 static void manageAllSubMenuWidgets(UserMenuListElement *subMenu)
1472 int i;
1473 UserMenuList *subMenuList;
1474 UserMenuListElement *element;
1475 WidgetList widgetList;
1476 Cardinal nWidgetListItems;
1478 /* if the sub-menu is torn off, unmanage the menu pane
1479 before updating it to prevent the tear-off menu
1480 from shrinking and expanding as the menu entries
1481 are (un)managed */
1482 if (!XmIsMenuShell(XtParent(subMenu->umleSubMenuPane))) {
1483 XtUnmanageChild(subMenu->umleSubMenuPane);
1486 /* manage all children of sub-menu pane */
1487 XtVaGetValues(subMenu->umleSubMenuPane,
1488 XmNchildren, &widgetList,
1489 XmNnumChildren, &nWidgetListItems,
1490 NULL);
1491 XtManageChildren(widgetList, nWidgetListItems);
1493 /* scan, if an menu item of given sub-menu holds a nested
1494 sub-menu */
1495 subMenuList = subMenu->umleSubMenuList;
1497 for (i=0; i<subMenuList->umlNbrItems; i ++) {
1498 element = subMenuList->umlItems[i];
1500 if (element->umleSubMenuList != NULL) {
1501 /* if element is a sub-menu, then continue managing
1502 all items of that sub-menu recursively */
1503 manageAllSubMenuWidgets(element);
1507 /* manage sub-menu pane widget itself */
1508 XtManageChild(subMenu->umleMenuItem);
1510 /* if the sub-menu is torn off, then adjust & manage the menu */
1511 if (!XmIsMenuShell(XtParent(subMenu->umleSubMenuPane))) {
1512 manageTearOffMenu(subMenu->umleSubMenuPane);
1515 /* redisplay sub-menu tearoff window, if the sub-menu
1516 was torn off before */
1517 ShowHiddenTearOff(subMenu->umleSubMenuPane);
1521 ** Cache user menus:
1522 ** Unmanage all menu widgets of given user sub-menu list.
1524 static void unmanageAllSubMenuWidgets(UserMenuListElement *subMenu)
1526 int i;
1527 Widget shell;
1528 UserMenuList *subMenuList;
1529 UserMenuListElement *element;
1530 WidgetList widgetList;
1531 Cardinal nWidgetListItems;
1533 /* if sub-menu is torn-off, then unmap its shell
1534 (so tearoff window isn't displayed anymore) */
1535 shell = XtParent(subMenu->umleSubMenuPane);
1536 if (!XmIsMenuShell(shell)) {
1537 XtUnmapWidget(shell);
1540 /* unmanage all children of sub-menu pane */
1541 XtVaGetValues(subMenu->umleSubMenuPane,
1542 XmNchildren, &widgetList,
1543 XmNnumChildren, &nWidgetListItems,
1544 NULL);
1545 XtUnmanageChildren(widgetList, nWidgetListItems);
1547 /* scan, if an menu item of given sub-menu holds a nested
1548 sub-menu */
1549 subMenuList = subMenu->umleSubMenuList;
1551 for (i=0; i<subMenuList->umlNbrItems; i ++) {
1552 element = subMenuList->umlItems[i];
1554 if (element->umleSubMenuList != NULL) {
1555 /* if element is a sub-menu, then continue unmanaging
1556 all items of that sub-menu recursively */
1557 unmanageAllSubMenuWidgets(element);
1561 /* unmanage sub-menu pane widget itself */
1562 XtUnmanageChild(subMenu->umleMenuItem);
1566 ** Cache user menus:
1567 ** Manage / unmanage menu widgets according to given user menu list.
1569 static void manageMenuWidgets(UserMenuList *list)
1571 int i;
1572 UserMenuListElement *element;
1574 /* (un)manage all elements of given user menu list */
1575 for (i=0; i<list->umlNbrItems; i ++) {
1576 element = list->umlItems[i];
1578 if (element->umlePrevManageMode != element->umleManageMode ||
1579 element->umleManageMode == UMMM_MANAGE) {
1580 /* previous and current manage mode differ OR
1581 current manage mode indicates: element needs to be
1582 (un)managed individually */
1583 if (element->umleManageMode == UMMM_MANAGE_ALL) {
1584 /* menu item represented by "element" is a sub-menu and
1585 needs to be completely managed */
1586 manageAllSubMenuWidgets(element);
1587 } else if (element->umleManageMode == UMMM_MANAGE) {
1588 if (element->umlePrevManageMode == UMMM_UNMANAGE ||
1589 element->umlePrevManageMode == UMMM_UNMANAGE_ALL) {
1590 /* menu item represented by "element" was unmanaged
1591 before and needs to be managed now */
1592 XtManageChild(element->umleMenuItem);
1595 /* if element is a sub-menu, then continue (un)managing
1596 single elements of that sub-menu one by one */
1597 if (element->umleSubMenuList != NULL) {
1598 /* if the sub-menu is torn off, unmanage the menu pane
1599 before updating it to prevent the tear-off menu
1600 from shrinking and expanding as the menu entries
1601 are (un)managed */
1602 if (!XmIsMenuShell(XtParent(element->umleSubMenuPane))) {
1603 XtUnmanageChild(element->umleSubMenuPane);
1606 /* (un)manage menu entries of sub-menu */
1607 manageMenuWidgets(element->umleSubMenuList);
1609 /* if the sub-menu is torn off, then adjust & manage the menu */
1610 if (!XmIsMenuShell(XtParent(element->umleSubMenuPane))) {
1611 manageTearOffMenu(element->umleSubMenuPane);
1614 /* if the sub-menu was torn off then redisplay it */
1615 ShowHiddenTearOff(element->umleSubMenuPane);
1617 } else if (element->umleManageMode == UMMM_UNMANAGE_ALL){
1618 /* menu item represented by "element" is a sub-menu and
1619 needs to be completely unmanaged */
1620 unmanageAllSubMenuWidgets(element);
1621 } else {
1622 /* current mode is UMMM_UNMANAGE -> menu item represented
1623 by "element" is a single menu item and needs to be
1624 unmanaged */
1625 XtUnmanageChild(element->umleMenuItem);
1632 ** Cache user menus:
1633 ** Remove accelerators from all items of given user (sub-)menu list.
1635 static void removeAccelFromMenuWidgets(UserMenuList *menuList)
1637 int i;
1638 UserMenuListElement *element;
1640 /* scan all elements of this (sub-)menu */
1641 for (i=0; i<menuList->umlNbrItems; i ++) {
1642 element = menuList->umlItems[i];
1644 if (element->umleSubMenuList != NULL) {
1645 /* if element is a sub-menu, then continue removing accelerators
1646 from all items of that sub-menu recursively */
1647 removeAccelFromMenuWidgets(element->umleSubMenuList);
1648 } else if (element->umleAccKeys != NULL &&
1649 element->umleManageMode == UMMM_UNMANAGE &&
1650 element->umlePrevManageMode == UMMM_MANAGE) {
1651 /* remove accelerator if one was bound */
1652 XtVaSetValues(element->umleMenuItem, XmNaccelerator, NULL, NULL);
1658 ** Cache user menus:
1659 ** Assign accelerators to all managed items of given user (sub-)menu list.
1661 static void assignAccelToMenuWidgets(UserMenuList *menuList, WindowInfo *window)
1663 int i;
1664 UserMenuListElement *element;
1666 /* scan all elements of this (sub-)menu */
1667 for (i=0; i<menuList->umlNbrItems; i ++) {
1668 element = menuList->umlItems[i];
1670 if (element->umleSubMenuList != NULL) {
1671 /* if element is a sub-menu, then continue assigning accelerators
1672 to all managed items of that sub-menu recursively */
1673 assignAccelToMenuWidgets(element->umleSubMenuList, window);
1674 } else if (element->umleAccKeys != NULL &&
1675 element->umleManageMode == UMMM_MANAGE &&
1676 element->umlePrevManageMode == UMMM_UNMANAGE) {
1677 /* assign accelerator if applicable */
1678 XtVaSetValues(element->umleMenuItem, XmNaccelerator,
1679 element->umleAccKeys, NULL);
1680 if (!element->umleAccLockPatchApplied) {
1681 UpdateAccelLockPatch(window->splitPane, element->umleMenuItem);
1682 element->umleAccLockPatchApplied = True;
1689 ** Cache user menus:
1690 ** (Un)Manage all items of selected user menu.
1692 static void manageUserMenu(selectedUserMenu *menu, WindowInfo *window)
1694 int n, i;
1695 int *id;
1696 Boolean currentLEisSubMenu;
1697 userMenuInfo *info;
1698 UserMenuList *menuList;
1699 UserMenuListElement *currentLE;
1700 UserMenuManageMode *mode;
1702 /* reset manage mode of all items of selected user menu in window cache */
1703 resetManageMode(menu->sumMainMenuList);
1705 /* set manage mode of all items of selected user menu in window cache
1706 according to the "to be managed" indication of the info list */
1707 for (n=0; n<menu->sumNbrOfListItems; n++) {
1708 info = menu->sumInfoList[n];
1710 menuList = menu->sumMainMenuList;
1711 id = info->umiId;
1713 /* select all menu list items belonging to menu record "info" using
1714 hierarchical ID of current menu info (e.g. id = {3} means:
1715 4th element of main menu; {0} = 1st element etc.)*/
1716 for (i=0; i<info->umiIdLen; i ++) {
1717 currentLE = menuList->umlItems[*id];
1718 mode = &currentLE->umleManageMode;
1719 currentLEisSubMenu = (currentLE->umleSubMenuList != NULL);
1721 if (info->umiToBeManaged) {
1722 /* menu record needs to be managed: */
1723 if (*mode == UMMM_UNMANAGE) {
1724 /* "mode" was not touched after reset ("init. state"):
1725 if current list element represents a sub-menu, then
1726 probably the complete sub-menu needs to be managed
1727 too. If current list element indicates single menu
1728 item, then just this item needs to be managed */
1729 if (currentLEisSubMenu) {
1730 *mode = UMMM_MANAGE_ALL;
1731 } else {
1732 *mode = UMMM_MANAGE;
1734 } else if (*mode == UMMM_UNMANAGE_ALL) {
1735 /* "mode" was touched after reset:
1736 current list element represents a sub-menu and min.
1737 one element of the sub-menu needs to be unmanaged ->
1738 the sub-menu needs to be (un)managed element by
1739 element */
1740 *mode = UMMM_MANAGE;
1742 } else {
1743 /* menu record needs to be unmanaged: */
1744 if (*mode == UMMM_UNMANAGE) {
1745 /* "mode" was not touched after reset ("init. state"):
1746 if current list element represents a sub-menu, then
1747 probably the complete sub-menu needs to be unmanaged
1748 too. */
1749 if (currentLEisSubMenu) {
1750 *mode = UMMM_UNMANAGE_ALL;
1752 } else if (*mode == UMMM_MANAGE_ALL) {
1753 /* "mode" was touched after reset:
1754 current list element represents a sub-menu and min.
1755 one element of the sub-menu needs to be managed ->
1756 the sub-menu needs to be (un)managed element by
1757 element */
1758 *mode = UMMM_MANAGE;
1762 menuList = currentLE->umleSubMenuList;
1764 id ++;
1768 /* if the menu is torn off, unmanage the menu pane
1769 before updating it to prevent the tear-off menu
1770 from shrinking and expanding as the menu entries
1771 are managed */
1772 if (!XmIsMenuShell(XtParent(menu->sumMenuPane)))
1773 XtUnmanageChild(menu->sumMenuPane);
1775 /* manage menu widgets according to current / previous manage mode of
1776 user menu window cache */
1777 manageMenuWidgets(menu->sumMainMenuList);
1779 /* Note: before new accelerator is assigned it seems to be necessary
1780 to remove old accelerator from user menu widgets. Removing same
1781 accelerator *after* it was assigned to another user menu widget
1782 doesn't work */
1783 removeAccelFromMenuWidgets(menu->sumMainMenuList);
1785 assignAccelToMenuWidgets(menu->sumMainMenuList, window);
1787 /* if the menu is torn off, then adjust & manage the menu */
1788 if (!XmIsMenuShell(XtParent(menu->sumMenuPane)))
1789 manageTearOffMenu(menu->sumMenuPane);
1793 ** Create either the variable Shell menu, Macro menu or Background menu
1794 ** items of "window" (driven by value of "menuType")
1796 static void createMenuItems(WindowInfo *window, selectedUserMenu *menu)
1798 Widget btn, subPane, newSubPane;
1799 int n;
1800 menuItemRec *item;
1801 menuTreeItem *menuTree;
1802 int i, nTreeEntries, size;
1803 char *hierName, *namePtr, *subMenuName, *subSep, *fullName;
1804 int menuType = menu->sumType;
1805 userMenuInfo *info;
1806 userSubMenuCache *subMenus = menu->sumSubMenus;
1807 userSubMenuInfo *subMenuInfo;
1808 UserMenuList *menuList;
1809 UserMenuListElement *currentLE;
1810 int subMenuDepth;
1811 char accKeysBuf[MAX_ACCEL_LEN+5];
1812 char *accKeys;
1814 /* Allocate storage for structures to help find panes of sub-menus */
1815 size = sizeof(menuTreeItem) * menu->sumNbrOfListItems;
1816 menuTree = (menuTreeItem *)XtMalloc(size);
1817 nTreeEntries = 0;
1819 /* Harmless kludge: undo and redo items are marked specially if found
1820 in the background menu, and used to dim/undim with edit menu */
1821 window->bgMenuUndoItem = NULL;
1822 window->bgMenuRedoItem = NULL;
1825 ** Add items to the menu pane, creating hierarchical sub-menus as
1826 ** necessary
1828 allocUserMenuList(menu->sumMainMenuList, subMenus->usmcNbrOfMainMenuItems);
1829 for (n=0; n<menu->sumNbrOfListItems; n++) {
1830 item = menu->sumItemList[n];
1831 info = menu->sumInfoList[n];
1832 menuList = menu->sumMainMenuList;
1833 subMenuDepth = 0;
1835 fullName = info->umiName;
1837 /* create/find sub-menus, stripping off '>' until item name is
1838 reached, then create the menu item */
1839 namePtr = fullName;
1840 subPane = menu->sumMenuPane;
1841 for (;;) {
1842 subSep = strchr(namePtr, '>');
1843 if (subSep == NULL) {
1844 btn = createUserMenuItem(subPane, namePtr, item, n,
1845 (XtCallbackProc)(menuType == SHELL_CMDS ? shellMenuCB :
1846 (menuType == MACRO_CMDS ? macroMenuCB : bgMenuCB)),
1847 (XtPointer)window);
1848 if (menuType == BG_MENU_CMDS && !strcmp(item->cmd, "undo()\n"))
1849 window->bgMenuUndoItem = btn;
1850 else if (menuType == BG_MENU_CMDS && !strcmp(item->cmd,"redo()\n"))
1851 window->bgMenuRedoItem = btn;
1852 /* generate accelerator keys */
1853 genAccelEventName(accKeysBuf, item->modifiers, item->keysym);
1854 accKeys = item->keysym == NoSymbol ? NULL : XtNewString(accKeysBuf);
1855 /* create corresponding menu list item */
1856 menuList->umlItems[menuList->umlNbrItems ++] =
1857 allocUserMenuListElement(btn, accKeys);
1858 break;
1860 hierName = copySubstring(fullName, subSep - fullName);
1861 subMenuInfo = findSubMenuInfo(subMenus, hierName);
1862 newSubPane = findInMenuTree(menuTree, nTreeEntries, hierName);
1863 if (newSubPane == NULL) {
1864 subMenuName = copySubstring(namePtr, subSep - namePtr);
1865 newSubPane = createUserSubMenu(subPane, subMenuName, &btn);
1866 XtFree(subMenuName);
1867 menuTree[nTreeEntries].name = hierName;
1868 menuTree[nTreeEntries++].menuPane = newSubPane;
1870 currentLE = allocUserMenuListElement(btn, NULL);
1871 menuList->umlItems[menuList->umlNbrItems ++] = currentLE;
1872 currentLE->umleSubMenuPane = newSubPane;
1873 currentLE->umleSubMenuList =
1874 allocUserSubMenuList(subMenuInfo->usmiId[subMenuInfo->usmiIdLen]);
1875 } else {
1876 currentLE = menuList->umlItems[subMenuInfo->usmiId[subMenuDepth]];
1877 XtFree(hierName);
1879 subPane = newSubPane;
1880 menuList = currentLE->umleSubMenuList;
1881 subMenuDepth ++;
1882 namePtr = subSep + 1;
1886 *menu->sumMenuCreated = True;
1888 /* Free the structure used to keep track of sub-menus durring creation */
1889 for (i=0; i<nTreeEntries; i++)
1890 XtFree(menuTree[i].name);
1891 XtFree((char *)menuTree);
1895 ** Find the widget corresponding to a hierarchical menu name (a>b>c...)
1897 static Widget findInMenuTree(menuTreeItem *menuTree, int nTreeEntries,
1898 const char *hierName)
1900 int i;
1902 for (i=0; i<nTreeEntries; i++)
1903 if (!strcmp(hierName, menuTree[i].name))
1904 return menuTree[i].menuPane;
1905 return NULL;
1908 static char *copySubstring(const char *string, int length)
1910 char *retStr = XtMalloc(length + 1);
1912 strncpy(retStr, string, length);
1913 retStr[length] = '\0';
1914 return retStr;
1917 static Widget createUserMenuItem(Widget menuPane, char *name, menuItemRec *f,
1918 int index, XtCallbackProc cbRtn, XtPointer cbArg)
1920 XmString st1, st2;
1921 char accText[MAX_ACCEL_LEN];
1922 Widget btn;
1924 generateAcceleratorString(accText, f->modifiers, f->keysym);
1925 st1=XmStringCreateSimple(name);
1926 st2=XmStringCreateSimple(accText);
1927 btn = XtVaCreateWidget("cmd", xmPushButtonWidgetClass, menuPane,
1928 XmNlabelString, st1,
1929 XmNacceleratorText, st2,
1930 XmNmnemonic, f->mnemonic,
1931 XmNuserData, (XtPointer)(index+10), NULL);
1932 XtAddCallback(btn, XmNactivateCallback, cbRtn, cbArg);
1933 XmStringFree(st1);
1934 XmStringFree(st2);
1935 return btn;
1939 ** Add a user-defined sub-menu to an established pull-down menu, marking
1940 ** it's userData field with TEMPORARY_MENU_ITEM so it can be found and
1941 ** removed later if the menu is redefined. Returns the menu pane of the
1942 ** new sub-menu.
1944 static Widget createUserSubMenu(Widget parent, char *label, Widget *menuItem)
1946 Widget menuPane;
1947 XmString st1;
1948 static Arg args[1] = {{XmNuserData, (XtArgVal)TEMPORARY_MENU_ITEM}};
1950 menuPane = CreatePulldownMenu(parent, "userPulldown", args, 1);
1951 *menuItem = XtVaCreateWidget("userCascade", xmCascadeButtonWidgetClass, parent,
1952 XmNlabelString, st1=XmStringCreateSimple(label),
1953 XmNsubMenuId, menuPane, XmNuserData, TEMPORARY_MENU_ITEM,
1954 NULL);
1955 XmStringFree(st1);
1956 return menuPane;
1960 ** Cache user menus:
1961 ** Delete all variable menu items of given menu pane
1963 static void deleteMenuItems(Widget menuPane)
1965 WidgetList itemList, items;
1966 Cardinal nItems;
1967 Widget subMenuID;
1968 XtPointer userData;
1969 int n;
1971 /* Fetch the list of children from the menu pane to delete */
1972 XtVaGetValues(menuPane, XmNchildren, &itemList,
1973 XmNnumChildren, &nItems, NULL);
1975 /* make a copy because the widget alters the list as you delete widgets */
1976 items = (WidgetList)XtMalloc(sizeof(Widget) * nItems);
1977 memcpy(items, itemList, sizeof(Widget) * nItems);
1979 /* delete all of the widgets not marked as PERMANENT_MENU_ITEM */
1980 for (n=0; n<(int)nItems; n++) {
1981 XtVaGetValues(items[n], XmNuserData, &userData, NULL);
1982 if (userData != (XtPointer)PERMANENT_MENU_ITEM) {
1983 if (XtClass(items[n]) == xmCascadeButtonWidgetClass) {
1984 XtVaGetValues(items[n], XmNsubMenuId, &subMenuID, NULL);
1986 /* prevent dangling submenu tearoffs */
1987 if (!XmIsMenuShell(XtParent(subMenuID)))
1988 _XmDismissTearOff(XtParent(subMenuID), NULL, NULL);
1990 deleteMenuItems(subMenuID);
1991 #if XmVersion < 2000
1992 /* Skipping this creates a memory and server resource
1993 leak (though both are reclaimed on window closing). In
1994 Motif 2.0 (and beyond?) there is a potential crash during
1995 phase 2 widget destruction in "SetCascadeField", and in
1996 Motif 1.2 there are free-memory reads. I would really like
1997 to be able to destroy this. */
1998 XtDestroyWidget(subMenuID);
1999 #endif
2000 } else {
2001 /* remove accel. before destroy or lose it forever */
2002 XtVaSetValues(items[n], XmNaccelerator, NULL, NULL);
2004 XtDestroyWidget(items[n]);
2007 XtFree((char *)items);
2010 static void closeCB(Widget w, XtPointer clientData, XtPointer callData)
2012 userCmdDialog *ucd = (userCmdDialog *)clientData;
2014 /* Mark that there's no longer a (macro, bg, or shell) dialog up */
2015 if (ucd->dialogType == SHELL_CMDS)
2016 ShellCmdDialog = NULL;
2017 else if (ucd->dialogType == MACRO_CMDS)
2018 MacroCmdDialog = NULL;
2019 else
2020 BGMenuCmdDialog = NULL;
2022 /* pop down and destroy the dialog (memory for ucd is freed in the
2023 destroy callback) */
2024 XtDestroyWidget(ucd->dlogShell);
2027 static void okCB(Widget w, XtPointer clientData, XtPointer callData)
2029 userCmdDialog *ucd = (userCmdDialog *)clientData;
2031 /* Read the dialog fields, and update the menus */
2032 if (!applyDialogChanges(ucd))
2033 return;
2035 /* Mark that there's no longer a (macro, bg, or shell) dialog up */
2036 if (ucd->dialogType == SHELL_CMDS)
2037 ShellCmdDialog = NULL;
2038 else if (ucd->dialogType == MACRO_CMDS)
2039 MacroCmdDialog = NULL;
2040 else
2041 BGMenuCmdDialog = NULL;
2043 /* pop down and destroy the dialog (memory for ucd is freed in the
2044 destroy callback) */
2045 XtDestroyWidget(ucd->dlogShell);
2048 static void applyCB(Widget w, XtPointer clientData, XtPointer callData)
2050 applyDialogChanges((userCmdDialog *)clientData);
2053 static void checkCB(Widget w, XtPointer clientData, XtPointer callData)
2055 userCmdDialog *ucd = (userCmdDialog *)clientData;
2057 if (checkMacro(ucd))
2059 DialogF(DF_INF, ucd->dlogShell, 1, "Macro",
2060 "Macro compiled without error", "OK");
2064 static int checkMacro(userCmdDialog *ucd)
2066 menuItemRec *f;
2068 f = readDialogFields(ucd, False);
2069 if (f == NULL)
2070 return False;
2071 if (!checkMacroText(f->cmd, ucd->dlogShell, ucd->cmdTextW)) {
2072 freeMenuItemRec(f);
2073 return False;
2075 return True;
2078 static int checkMacroText(char *macro, Widget errorParent, Widget errFocus)
2080 Program *prog;
2081 char *errMsg, *stoppedAt;
2083 prog = ParseMacro(macro, &errMsg, &stoppedAt);
2084 if (prog == NULL) {
2085 if (errorParent != NULL) {
2086 ParseError(errorParent, macro, stoppedAt, "macro", errMsg);
2087 XmTextSetInsertionPosition(errFocus, stoppedAt - macro);
2088 XmProcessTraversal(errFocus, XmTRAVERSE_CURRENT);
2090 return False;
2092 FreeProgram(prog);
2093 if (*stoppedAt != '\0') {
2094 if (errorParent != NULL) {
2095 ParseError(errorParent, macro, stoppedAt,"macro","syntax error");
2096 XmTextSetInsertionPosition(errFocus, stoppedAt - macro);
2097 XmProcessTraversal(errFocus, XmTRAVERSE_CURRENT);
2099 return False;
2101 return True;
2104 static int applyDialogChanges(userCmdDialog *ucd)
2106 int i;
2108 /* Get the current contents of the dialog fields */
2109 if (!UpdateManagedList(ucd->managedList, True))
2110 return False;
2112 /* Test compile the macro */
2113 if (ucd->dialogType == MACRO_CMDS)
2114 if (!checkMacro(ucd))
2115 return False;
2117 /* Update the menu information */
2118 if (ucd->dialogType == SHELL_CMDS) {
2119 for (i=0; i<NShellMenuItems; i++)
2120 freeMenuItemRec(ShellMenuItems[i]);
2121 freeUserMenuInfoList(ShellMenuInfo, NShellMenuItems);
2122 freeSubMenuCache(&ShellSubMenus);
2123 for (i=0; i<ucd->nMenuItems; i++)
2124 ShellMenuItems[i] = copyMenuItemRec(ucd->menuItemsList[i]);
2125 NShellMenuItems = ucd->nMenuItems;
2126 parseMenuItemList(ShellMenuItems, NShellMenuItems, ShellMenuInfo, &ShellSubMenus);
2127 } else if (ucd->dialogType == MACRO_CMDS) {
2128 for (i=0; i<NMacroMenuItems; i++)
2129 freeMenuItemRec(MacroMenuItems[i]);
2130 freeUserMenuInfoList(MacroMenuInfo, NMacroMenuItems);
2131 freeSubMenuCache(&MacroSubMenus);
2132 for (i=0; i<ucd->nMenuItems; i++)
2133 MacroMenuItems[i] = copyMenuItemRec(ucd->menuItemsList[i]);
2134 NMacroMenuItems = ucd->nMenuItems;
2135 parseMenuItemList(MacroMenuItems, NMacroMenuItems, MacroMenuInfo, &MacroSubMenus);
2136 } else { /* BG_MENU_CMDS */
2137 for (i=0; i<NBGMenuItems; i++)
2138 freeMenuItemRec(BGMenuItems[i]);
2139 freeUserMenuInfoList(BGMenuInfo, NBGMenuItems);
2140 freeSubMenuCache(&BGSubMenus);
2141 for (i=0; i<ucd->nMenuItems; i++)
2142 BGMenuItems[i] = copyMenuItemRec(ucd->menuItemsList[i]);
2143 NBGMenuItems = ucd->nMenuItems;
2144 parseMenuItemList(BGMenuItems, NBGMenuItems, BGMenuInfo, &BGSubMenus);
2147 /* Update the menus themselves in all of the NEdit windows */
2148 rebuildMenuOfAllWindows(ucd->dialogType);
2150 /* Note that preferences have been changed */
2151 MarkPrefsChanged();
2152 return True;
2155 static void pasteReplayCB(Widget w, XtPointer clientData, XtPointer callData)
2157 userCmdDialog *ucd = (userCmdDialog *)clientData;
2159 if (GetReplayMacro() == NULL)
2160 return;
2162 XmTextInsert(ucd->cmdTextW, XmTextGetInsertionPosition(ucd->cmdTextW),
2163 GetReplayMacro());
2166 static void destroyCB(Widget w, XtPointer clientData, XtPointer callData)
2168 userCmdDialog *ucd = (userCmdDialog *)clientData;
2169 int i;
2171 for (i=0; i<ucd->nMenuItems; i++)
2172 freeMenuItemRec(ucd->menuItemsList[i]);
2173 XtFree((char *)ucd->menuItemsList);
2174 XtFree((char *)ucd);
2177 static void accFocusCB(Widget w, XtPointer clientData, XtPointer callData)
2179 userCmdDialog *ucd = (userCmdDialog *)clientData;
2181 RemoveDialogMnemonicHandler(XtParent(ucd->accTextW));
2184 static void accLoseFocusCB(Widget w, XtPointer clientData, XtPointer callData)
2186 userCmdDialog *ucd = (userCmdDialog *)clientData;
2188 AddDialogMnemonicHandler(XtParent(ucd->accTextW), FALSE);
2191 static void accKeyCB(Widget w, XtPointer clientData, XKeyEvent *event)
2193 userCmdDialog *ucd = (userCmdDialog *)clientData;
2194 KeySym keysym = XLookupKeysym(event, 0);
2195 char outStr[MAX_ACCEL_LEN];
2197 /* Accept only real keys, not modifiers alone */
2198 if (IsModifierKey(keysym))
2199 return;
2201 /* Tab key means go to the next field, don't enter */
2202 if (keysym == XK_Tab)
2203 return;
2205 /* Beep and return if the modifiers are buttons or ones we don't support */
2206 if (event->state & ~(ShiftMask | LockMask | ControlMask | Mod1Mask |
2207 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask)) {
2208 XBell(TheDisplay, 0);
2209 return;
2212 /* Delete or backspace clears field */
2213 if (keysym == XK_Delete || keysym == XK_BackSpace) {
2214 XmTextSetString(ucd->accTextW, "");
2215 return;
2218 /* generate the string to use in the dialog field */
2219 generateAcceleratorString(outStr, event->state, keysym);
2221 /* Reject single character accelerators (a very simple way to eliminate
2222 un-modified letters and numbers) The goal is give users a clue that
2223 they're supposed to type the actual keys, not the name. This scheme
2224 is not rigorous and still allows accelerators like Comma. */
2225 if (strlen(outStr) == 1) {
2226 XBell(TheDisplay, 0);
2227 return;
2230 /* fill in the accelerator field in the dialog */
2231 XmTextSetString(ucd->accTextW, outStr);
2234 static void sameOutCB(Widget w, XtPointer clientData, XtPointer callData)
2236 XtSetSensitive(((userCmdDialog *)clientData)->repInpBtn,
2237 XmToggleButtonGetState(w));
2240 static void shellMenuCB(Widget w, WindowInfo *window, XtPointer callData)
2242 XtArgVal userData;
2243 int index;
2244 char *params[1];
2246 window = WidgetToWindow(MENU_WIDGET(w));
2248 /* get the index of the shell command and verify that it's in range */
2249 XtVaGetValues(w, XmNuserData, &userData, NULL);
2250 index = (int)userData - 10;
2251 if (index <0 || index >= NShellMenuItems)
2252 return;
2254 params[0] = ShellMenuItems[index]->name;
2255 XtCallActionProc(window->lastFocus, "shell_menu_command",
2256 ((XmAnyCallbackStruct *)callData)->event, params, 1);
2259 static void macroMenuCB(Widget w, WindowInfo *window, XtPointer callData)
2261 XtArgVal userData;
2262 int index;
2263 char *params[1];
2265 window = WidgetToWindow(MENU_WIDGET(w));
2267 /* Don't allow users to execute a macro command from the menu (or accel)
2268 if there's already a macro command executing. NEdit can't handle
2269 running multiple, independent uncoordinated, macros in the same
2270 window. Macros may invoke macro menu commands recursively via the
2271 macro_menu_command action proc, which is important for being able to
2272 repeat any operation, and to embed macros within eachother at any
2273 level, however, a call here with a macro running means that THE USER
2274 is explicitly invoking another macro via the menu or an accelerator. */
2275 if (window->macroCmdData != NULL) {
2276 XBell(TheDisplay, 0);
2277 return;
2280 /* get the index of the macro command and verify that it's in range */
2281 XtVaGetValues(w, XmNuserData, &userData, NULL);
2282 index = (int)userData - 10;
2283 if (index <0 || index >= NMacroMenuItems)
2284 return;
2286 params[0] = MacroMenuItems[index]->name;
2287 XtCallActionProc(window->lastFocus, "macro_menu_command",
2288 ((XmAnyCallbackStruct *)callData)->event, params, 1);
2291 static void bgMenuCB(Widget w, WindowInfo *window, XtPointer callData)
2293 XtArgVal userData;
2294 int index;
2295 char *params[1];
2297 /* Same remark as for macro menu commands (see above). */
2298 if (window->macroCmdData != NULL) {
2299 XBell(TheDisplay, 0);
2300 return;
2303 /* get the index of the macro command and verify that it's in range */
2304 XtVaGetValues(w, XmNuserData, &userData, NULL);
2305 index = (int)userData - 10;
2306 if (index <0 || index >= NBGMenuItems)
2307 return;
2309 params[0] = BGMenuItems[index]->name;
2310 XtCallActionProc(window->lastFocus, "bg_menu_command",
2311 ((XmAnyCallbackStruct *)callData)->event, params, 1);
2315 ** Update the name, accelerator, mnemonic, and command fields in the shell
2316 ** command or macro dialog to agree with the currently selected item in the
2317 ** menu item list.
2319 static void updateDialogFields(menuItemRec *f, userCmdDialog *ucd)
2321 char mneString[2], accString[MAX_ACCEL_LEN];
2323 /* fill in the name, accelerator, mnemonic, and command fields of the
2324 dialog for the newly selected item, or blank them if "New" is selected */
2325 if (f == NULL) {
2326 XmTextSetString(ucd->nameTextW, "");
2327 XmTextSetString(ucd->cmdTextW, "");
2328 XmTextSetString(ucd->accTextW, "");
2329 XmTextSetString(ucd->mneTextW, "");
2330 if (ucd->dialogType == SHELL_CMDS) {
2331 RadioButtonChangeState(ucd->selInpBtn, True, True);
2332 RadioButtonChangeState(ucd->sameOutBtn, True, True);
2333 RadioButtonChangeState(ucd->repInpBtn, False, False);
2334 XtSetSensitive(ucd->repInpBtn, True);
2335 RadioButtonChangeState(ucd->saveFirstBtn, False, False);
2336 RadioButtonChangeState(ucd->loadAfterBtn, False, False);
2338 } else {
2339 mneString[0] = f->mnemonic;
2340 mneString[1] = '\0';
2341 generateAcceleratorString(accString, f->modifiers, f->keysym);
2342 XmTextSetString(ucd->nameTextW, f->name);
2343 XmTextSetString(ucd->cmdTextW, f->cmd);
2344 XmTextSetString(ucd->accTextW, accString);
2345 XmTextSetString(ucd->mneTextW, mneString);
2346 RadioButtonChangeState(ucd->selInpBtn, f->input==FROM_SELECTION, False);
2347 if (ucd->dialogType == SHELL_CMDS) {
2348 RadioButtonChangeState(ucd->winInpBtn, f->input == FROM_WINDOW,
2349 False);
2350 RadioButtonChangeState(ucd->eitherInpBtn, f->input == FROM_EITHER,
2351 False);
2352 RadioButtonChangeState(ucd->noInpBtn, f->input == FROM_NONE,
2353 False);
2354 RadioButtonChangeState(ucd->sameOutBtn, f->output==TO_SAME_WINDOW,
2355 False);
2356 RadioButtonChangeState(ucd->winOutBtn, f->output==TO_NEW_WINDOW,
2357 False);
2358 RadioButtonChangeState(ucd->dlogOutBtn, f->output==TO_DIALOG,
2359 False);
2360 RadioButtonChangeState(ucd->repInpBtn, f->repInput, False);
2361 XtSetSensitive(ucd->repInpBtn, f->output==TO_SAME_WINDOW);
2362 RadioButtonChangeState(ucd->saveFirstBtn, f->saveFirst, False);
2363 RadioButtonChangeState(ucd->loadAfterBtn, f->loadAfter, False);
2369 ** Read the name, accelerator, mnemonic, and command fields from the shell or
2370 ** macro commands dialog into a newly allocated menuItemRec. Returns a
2371 ** pointer to the new menuItemRec structure as the function value, or NULL on
2372 ** failure.
2374 static menuItemRec *readDialogFields(userCmdDialog *ucd, int silent)
2376 char *nameText, *cmdText, *mneText, *accText;
2377 menuItemRec *f;
2379 nameText = XmTextGetString(ucd->nameTextW);
2380 if (*nameText == '\0')
2382 if (!silent)
2384 DialogF(DF_WARN, ucd->dlogShell, 1, "Menu Entry",
2385 "Please specify a name\nfor the menu item", "OK");
2386 XmProcessTraversal(ucd->nameTextW, XmTRAVERSE_CURRENT);
2388 XtFree(nameText);
2389 return NULL;
2392 if (strchr(nameText, ':'))
2394 if (!silent)
2396 DialogF(DF_WARN, ucd->dlogShell, 1, "Menu Entry",
2397 "Menu item names may not\ncontain colon (:) characters",
2398 "OK");
2399 XmProcessTraversal(ucd->nameTextW, XmTRAVERSE_CURRENT);
2401 XtFree(nameText);
2402 return NULL;
2405 cmdText = XmTextGetString(ucd->cmdTextW);
2406 if (cmdText == NULL || *cmdText == '\0')
2408 if (!silent)
2410 DialogF(DF_WARN, ucd->dlogShell, 1, "Command to Execute",
2411 "Please specify %s to execute", "OK",
2412 ucd->dialogType == SHELL_CMDS
2413 ? "shell command"
2414 : "macro command(s)");
2415 XmProcessTraversal(ucd->cmdTextW, XmTRAVERSE_CURRENT);
2417 XtFree(nameText);
2418 XtFree(cmdText);
2420 return NULL;
2423 if (ucd->dialogType == MACRO_CMDS || ucd->dialogType == BG_MENU_CMDS) {
2424 addTerminatingNewline(&cmdText);
2425 if (!checkMacroText(cmdText, silent ? NULL : ucd->dlogShell,
2426 ucd->cmdTextW)) {
2427 XtFree(nameText);
2428 XtFree(cmdText);
2429 return NULL;
2432 f = (menuItemRec *)XtMalloc(sizeof(menuItemRec));
2433 f->name = nameText;
2434 f->cmd = cmdText;
2435 if ((mneText = XmTextGetString(ucd->mneTextW)) != NULL) {
2436 f->mnemonic = mneText==NULL ? '\0' : mneText[0];
2437 XtFree(mneText);
2438 if (f->mnemonic == ':') /* colons mess up string parsing */
2439 f->mnemonic = '\0';
2441 if ((accText = XmTextGetString(ucd->accTextW)) != NULL) {
2442 parseAcceleratorString(accText, &f->modifiers, &f->keysym);
2443 XtFree(accText);
2445 if (ucd->dialogType == SHELL_CMDS) {
2446 if (XmToggleButtonGetState(ucd->selInpBtn))
2447 f->input = FROM_SELECTION;
2448 else if (XmToggleButtonGetState(ucd->winInpBtn))
2449 f->input = FROM_WINDOW;
2450 else if (XmToggleButtonGetState(ucd->eitherInpBtn))
2451 f->input = FROM_EITHER;
2452 else
2453 f->input = FROM_NONE;
2454 if (XmToggleButtonGetState(ucd->winOutBtn))
2455 f->output = TO_NEW_WINDOW;
2456 else if (XmToggleButtonGetState(ucd->dlogOutBtn))
2457 f->output = TO_DIALOG;
2458 else
2459 f->output = TO_SAME_WINDOW;
2460 f->repInput = XmToggleButtonGetState(ucd->repInpBtn);
2461 f->saveFirst = XmToggleButtonGetState(ucd->saveFirstBtn);
2462 f->loadAfter = XmToggleButtonGetState(ucd->loadAfterBtn);
2463 } else {
2464 f->input = XmToggleButtonGetState(ucd->selInpBtn) ? FROM_SELECTION :
2465 FROM_NONE;
2466 f->output = TO_SAME_WINDOW;
2467 f->repInput = False;
2468 f->saveFirst = False;
2469 f->loadAfter = False;
2471 return f;
2475 ** Copy a menu item record, and its associated memory
2477 static menuItemRec *copyMenuItemRec(menuItemRec *item)
2479 menuItemRec *newItem;
2481 newItem = (menuItemRec *)XtMalloc(sizeof(menuItemRec));
2482 *newItem = *item;
2483 newItem->name = XtMalloc(strlen(item->name)+1);
2484 strcpy(newItem->name, item->name);
2485 newItem->cmd = XtMalloc(strlen(item->cmd)+1);
2486 strcpy(newItem->cmd, item->cmd);
2487 return newItem;
2491 ** Free a menu item record, and its associated memory
2493 static void freeMenuItemRec(menuItemRec *item)
2495 XtFree(item->name);
2496 XtFree(item->cmd);
2497 XtFree((char *)item);
2501 ** Callbacks for managed-list operations
2503 static void *getDialogDataCB(void *oldItem, int explicitRequest, int *abort,
2504 void *cbArg)
2506 userCmdDialog *ucd = (userCmdDialog *)cbArg;
2507 menuItemRec *currentFields;
2509 /* If the dialog is currently displaying the "new" entry and the
2510 fields are empty, that's just fine */
2511 if (oldItem == NULL && dialogFieldsAreEmpty(ucd))
2512 return NULL;
2514 /* If there are no problems reading the data, just return it */
2515 currentFields = readDialogFields(ucd, True);
2516 if (currentFields != NULL)
2517 return (void *)currentFields;
2519 /* If user might not be expecting fields to be read, give more warning */
2520 if (!explicitRequest)
2522 if (DialogF(DF_WARN, ucd->dlogShell, 2, "Discard Entry",
2523 "Discard incomplete entry\nfor current menu item?", "Keep",
2524 "Discard") == 2)
2526 return oldItem == NULL
2527 ? NULL
2528 : (void *)copyMenuItemRec((menuItemRec *)oldItem);
2532 /* Do readDialogFields again without "silent" mode to display warning(s) */
2533 readDialogFields(ucd, False);
2534 *abort = True;
2535 return NULL;
2539 static void setDialogDataCB(void *item, void *cbArg)
2541 updateDialogFields((menuItemRec *)item, (userCmdDialog *)cbArg);
2544 static int dialogFieldsAreEmpty(userCmdDialog *ucd)
2546 return TextWidgetIsBlank(ucd->nameTextW) &&
2547 TextWidgetIsBlank(ucd->cmdTextW) &&
2548 TextWidgetIsBlank(ucd->accTextW) &&
2549 TextWidgetIsBlank(ucd->mneTextW) &&
2550 (ucd->dialogType != SHELL_CMDS || (
2551 XmToggleButtonGetState(ucd->selInpBtn) &&
2552 XmToggleButtonGetState(ucd->sameOutBtn) &&
2553 !XmToggleButtonGetState(ucd->repInpBtn) &&
2554 !XmToggleButtonGetState(ucd->saveFirstBtn) &&
2555 !XmToggleButtonGetState(ucd->loadAfterBtn)));
2558 static void freeItemCB(void *item)
2560 freeMenuItemRec((menuItemRec *)item);
2564 ** Gut a text widget of it's ability to process input
2566 static void disableTextW(Widget textW)
2568 static XtTranslations emptyTable = NULL;
2569 static char *emptyTranslations = "\
2570 <EnterWindow>: enter()\n\
2571 <Btn1Down>: grab-focus()\n\
2572 <Btn1Motion>: extend-adjust()\n\
2573 <Btn1Up>: extend-end()\n\
2574 Shift<Key>Tab: prev-tab-group()\n\
2575 Ctrl<Key>Tab: next-tab-group()\n\
2576 <Key>Tab: next-tab-group()\n\
2577 <LeaveWindow>: leave()\n\
2578 <FocusIn>: focusIn()\n\
2579 <FocusOut>: focusOut()\n\
2580 <Unmap>: unmap()\n";
2582 /* replace the translation table with the slimmed down one above */
2583 if (emptyTable == NULL)
2584 emptyTable = XtParseTranslationTable(emptyTranslations);
2585 XtVaSetValues(textW, XmNtranslations, emptyTable, NULL);
2588 static char *writeMenuItemString(menuItemRec **menuItems, int nItems,
2589 int listType)
2591 char *outStr, *outPtr, *c, accStr[MAX_ACCEL_LEN];
2592 menuItemRec *f;
2593 int i, length;
2595 /* determine the max. amount of memory needed for the returned string
2596 and allocate a buffer for composing the string */
2597 length = 0;
2598 for (i=0; i<nItems; i++) {
2599 f = menuItems[i];
2600 generateAcceleratorString(accStr, f->modifiers, f->keysym);
2601 length += strlen(f->name) * 2; /* allow for \n & \\ expansions */
2602 length += strlen(accStr);
2603 length += strlen(f->cmd) * 6; /* allow for \n & \\ expansions */
2604 length += 21; /* number of characters added below */
2606 length++; /* terminating null */
2607 outStr = XtMalloc(length);
2609 /* write the string */
2610 outPtr = outStr;
2611 *outPtr++ = '\\';
2612 *outPtr++ = '\n';
2613 for (i=0; i<nItems; i++) {
2614 f = menuItems[i];
2615 generateAcceleratorString(accStr, f->modifiers, f->keysym);
2616 *outPtr++ = '\t';
2617 for (c=f->name; *c!='\0'; ++c) { /* Copy the command name */
2618 if (*c == '\\') { /* changing backslashes to \\ */
2619 *outPtr++ = '\\';
2620 *outPtr++ = '\\';
2621 } else if (*c == '\n') { /* changing newlines to \n */
2622 *outPtr++ = '\\';
2623 *outPtr++ = 'n';
2624 } else {
2625 *outPtr++ = *c;
2628 *outPtr++ = ':';
2629 strcpy(outPtr, accStr);
2630 outPtr += strlen(accStr);
2631 *outPtr++ = ':';
2632 if (f->mnemonic != '\0')
2633 *outPtr++ = f->mnemonic;
2634 *outPtr++ = ':';
2635 if (listType == SHELL_CMDS) {
2636 if (f->input == FROM_SELECTION)
2637 *outPtr++ = 'I';
2638 else if (f->input == FROM_WINDOW)
2639 *outPtr++ = 'A';
2640 else if (f->input == FROM_EITHER)
2641 *outPtr++ = 'E';
2642 if (f->output == TO_DIALOG)
2643 *outPtr++ = 'D';
2644 else if (f->output == TO_NEW_WINDOW)
2645 *outPtr++ = 'W';
2646 if (f->repInput)
2647 *outPtr++ = 'X';
2648 if (f->saveFirst)
2649 *outPtr++ = 'S';
2650 if (f->loadAfter)
2651 *outPtr++ = 'L';
2652 *outPtr++ = ':';
2653 } else {
2654 if (f->input == FROM_SELECTION)
2655 *outPtr++ = 'R';
2656 *outPtr++ = ':';
2657 *outPtr++ = ' ';
2658 *outPtr++ = '{';
2660 *outPtr++ = '\\';
2661 *outPtr++ = 'n';
2662 *outPtr++ = '\\';
2663 *outPtr++ = '\n';
2664 *outPtr++ = '\t';
2665 *outPtr++ = '\t';
2666 for (c=f->cmd; *c!='\0'; c++) { /* Copy the command string, changing */
2667 if (*c == '\\') { /* backslashes to double backslashes */
2668 *outPtr++ = '\\'; /* and newlines to backslash-n's, */
2669 *outPtr++ = '\\'; /* followed by real newlines and tab */
2670 } else if (*c == '\n') {
2671 *outPtr++ = '\\';
2672 *outPtr++ = 'n';
2673 *outPtr++ = '\\';
2674 *outPtr++ = '\n';
2675 *outPtr++ = '\t';
2676 *outPtr++ = '\t';
2677 } else
2678 *outPtr++ = *c;
2680 if (listType == MACRO_CMDS || listType == BG_MENU_CMDS) {
2681 if (*(outPtr-1) == '\t') outPtr--;
2682 *outPtr++ = '}';
2684 *outPtr++ = '\\';
2685 *outPtr++ = 'n';
2686 *outPtr++ = '\\';
2687 *outPtr++ = '\n';
2689 --outPtr;
2690 *--outPtr = '\0';
2691 return outStr;
2694 static int loadMenuItemString(char *inString, menuItemRec **menuItems,
2695 int *nItems, int listType)
2697 menuItemRec *f;
2698 char *cmdStr;
2699 char *inPtr = inString;
2700 char *nameStr, accStr[MAX_ACCEL_LEN], mneChar;
2701 KeySym keysym;
2702 unsigned int modifiers;
2703 int i, input, output, saveFirst, loadAfter, repInput;
2704 int nameLen, accLen, mneLen, cmdLen;
2706 for (;;) {
2708 /* remove leading whitespace */
2709 while (*inPtr == ' ' || *inPtr == '\t')
2710 inPtr++;
2712 /* end of string in proper place */
2713 if (*inPtr == '\0') {
2714 return True;
2717 /* read name field */
2718 nameLen = strcspn(inPtr, ":");
2719 if (nameLen == 0)
2720 return parseError("no name field");
2721 nameStr = XtMalloc(nameLen+1);
2722 strncpy(nameStr, inPtr, nameLen);
2723 nameStr[nameLen] = '\0';
2724 inPtr += nameLen;
2725 if (*inPtr == '\0')
2726 return parseError("end not expected");
2727 inPtr++;
2729 /* read accelerator field */
2730 accLen = strcspn(inPtr, ":");
2731 if (accLen >= MAX_ACCEL_LEN)
2732 return parseError("accelerator field too long");
2733 strncpy(accStr, inPtr, accLen);
2734 accStr[accLen] = '\0';
2735 inPtr += accLen;
2736 if (*inPtr == '\0')
2737 return parseError("end not expected");
2738 inPtr++;
2740 /* read menemonic field */
2741 mneLen = strcspn(inPtr, ":");
2742 if (mneLen > 1)
2743 return parseError("mnemonic field too long");
2744 if (mneLen == 1)
2745 mneChar = *inPtr++;
2746 else
2747 mneChar = '\0';
2748 inPtr++;
2749 if (*inPtr == '\0')
2750 return parseError("end not expected");
2752 /* read flags field */
2753 input = FROM_NONE;
2754 output = TO_SAME_WINDOW;
2755 repInput = False;
2756 saveFirst = False;
2757 loadAfter = False;
2758 for (; *inPtr != ':'; inPtr++) {
2759 if (listType == SHELL_CMDS) {
2760 if (*inPtr == 'I')
2761 input = FROM_SELECTION;
2762 else if (*inPtr == 'A')
2763 input = FROM_WINDOW;
2764 else if (*inPtr == 'E')
2765 input = FROM_EITHER;
2766 else if (*inPtr == 'W')
2767 output = TO_NEW_WINDOW;
2768 else if (*inPtr == 'D')
2769 output = TO_DIALOG;
2770 else if (*inPtr == 'X')
2771 repInput = True;
2772 else if (*inPtr == 'S')
2773 saveFirst = True;
2774 else if (*inPtr == 'L')
2775 loadAfter = True;
2776 else
2777 return parseError("unreadable flag field");
2778 } else {
2779 if (*inPtr == 'R')
2780 input = FROM_SELECTION;
2781 else
2782 return parseError("unreadable flag field");
2785 inPtr++;
2787 /* read command field */
2788 if (listType == SHELL_CMDS) {
2789 if (*inPtr++ != '\n')
2790 return parseError("command must begin with newline");
2791 while (*inPtr == ' ' || *inPtr == '\t') /* leading whitespace */
2792 inPtr++;
2793 cmdLen = strcspn(inPtr, "\n");
2794 if (cmdLen == 0)
2795 return parseError("shell command field is empty");
2796 cmdStr = XtMalloc(cmdLen+1);
2797 strncpy(cmdStr, inPtr, cmdLen);
2798 cmdStr[cmdLen] = '\0';
2799 inPtr += cmdLen;
2800 } else {
2801 cmdStr = copyMacroToEnd(&inPtr);
2802 if (cmdStr == NULL)
2803 return False;
2805 while (*inPtr == ' ' || *inPtr == '\t' || *inPtr == '\n')
2806 inPtr++; /* skip trailing whitespace & newline */
2808 /* parse the accelerator field */
2809 if (!parseAcceleratorString(accStr, &modifiers, &keysym))
2810 return parseError("couldn't read accelerator field");
2812 /* create a menu item record */
2813 f = (menuItemRec *)XtMalloc(sizeof(menuItemRec));
2814 f->name = nameStr;
2815 f->cmd = cmdStr;
2816 f->mnemonic = mneChar;
2817 f->modifiers = modifiers;
2818 f->input = input;
2819 f->output = output;
2820 f->repInput = repInput;
2821 f->saveFirst = saveFirst;
2822 f->loadAfter = loadAfter;
2823 f->keysym = keysym;
2825 /* add/replace menu record in the list */
2826 for (i=0; i < *nItems; i++) {
2827 if (!strcmp(menuItems[i]->name, f->name)) {
2828 freeMenuItemRec(menuItems[i]);
2829 menuItems[i] = f;
2830 break;
2833 if (i == *nItems)
2834 menuItems[(*nItems)++] = f;
2839 static int parseError(const char *message)
2841 fprintf(stderr, "NEdit: Parse error in user defined menu item, %s\n",
2842 message);
2843 return False;
2847 ** Create a text string representing an accelerator for the dialog,
2848 ** the shellCommands or macroCommands resource, and for the menu item.
2850 static void generateAcceleratorString(char *text, unsigned int modifiers,
2851 KeySym keysym)
2853 char *shiftStr = "", *ctrlStr = "", *altStr = "";
2854 char *mod2Str = "", *mod3Str = "", *mod4Str = "", *mod5Str = "";
2855 char keyName[20];
2856 Modifiers numLockMask = GetNumLockModMask(TheDisplay);
2858 /* if there's no accelerator, generate an empty string */
2859 if (keysym == NoSymbol) {
2860 *text = '\0';
2861 return;
2865 /* Translate the modifiers into strings.
2866 Lock and NumLock are always ignored (see util/misc.c),
2867 so we don't display them either. */
2868 if (modifiers & ShiftMask)
2869 shiftStr = "Shift+";
2870 if (modifiers & ControlMask)
2871 ctrlStr = "Ctrl+";
2872 if (modifiers & Mod1Mask)
2873 altStr = "Alt+";
2874 if ((modifiers & Mod2Mask) && (Mod2Mask != numLockMask))
2875 mod2Str = "Mod2+";
2876 if ((modifiers & Mod3Mask) && (Mod3Mask != numLockMask))
2877 mod3Str = "Mod3+";
2878 if ((modifiers & Mod4Mask) && (Mod4Mask != numLockMask))
2879 mod4Str = "Mod4+";
2880 if ((modifiers & Mod5Mask) && (Mod5Mask != numLockMask))
2881 mod5Str = "Mod5+";
2883 /* for a consistent look to the accelerator names in the menus,
2884 capitalize the first letter of the keysym */
2885 strcpy(keyName, XKeysymToString(keysym));
2886 *keyName = toupper(*keyName);
2888 /* concatenate the strings together */
2889 sprintf(text, "%s%s%s%s%s%s%s%s", shiftStr, ctrlStr, altStr,
2890 mod2Str, mod3Str, mod4Str, mod5Str, keyName);
2894 ** Create a translation table event description string for the menu
2895 ** XmNaccelerator resource.
2897 static void genAccelEventName(char *text, unsigned int modifiers,
2898 KeySym keysym)
2900 char *shiftStr = "", *lockStr = "", *ctrlStr = "", *altStr = "";
2901 char *mod2Str = "", *mod3Str = "", *mod4Str = "", *mod5Str = "";
2903 /* if there's no accelerator, generate an empty string */
2904 if (keysym == NoSymbol) {
2905 *text = '\0';
2906 return;
2909 /* translate the modifiers into strings */
2910 if (modifiers & ShiftMask)
2911 shiftStr = "Shift ";
2912 if (modifiers & LockMask)
2913 lockStr = "Lock ";
2914 if (modifiers & ControlMask)
2915 ctrlStr = "Ctrl ";
2916 if (modifiers & Mod1Mask)
2917 altStr = "Alt ";
2918 if (modifiers & Mod2Mask)
2919 mod2Str = "Mod2 ";
2920 if (modifiers & Mod3Mask)
2921 mod3Str = "Mod3 ";
2922 if (modifiers & Mod4Mask)
2923 mod4Str = "Mod4 ";
2924 if (modifiers & Mod5Mask)
2925 mod5Str = "Mod5 ";
2927 /* put the modifiers together with the key name */
2928 sprintf(text, "%s%s%s%s%s%s%s%s<Key>%s",
2929 shiftStr, lockStr, ctrlStr, altStr,
2930 mod2Str, mod3Str, mod4Str, mod5Str,
2931 XKeysymToString(keysym));
2935 ** Read an accelerator name and put it into the form of a modifier mask
2936 ** and a KeySym code. Returns false if string can't be read
2937 ** ... does not handle whitespace in string (look at scanf)
2939 static int parseAcceleratorString(const char *string, unsigned int *modifiers,
2940 KeySym *keysym)
2942 int i, nFields, inputLength = strlen(string);
2943 char fields[10][MAX_ACCEL_LEN];
2945 /* a blank field means no accelerator */
2946 if (inputLength == 0) {
2947 *modifiers = 0;
2948 *keysym = NoSymbol;
2949 return True;
2952 /* limit the string length so no field strings will overflow */
2953 if (inputLength > MAX_ACCEL_LEN)
2954 return False;
2956 /* divide the input into '+' separated fields */
2957 nFields = sscanf(string, "%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]",
2958 fields[0], fields[1], fields[2], fields[3], fields[4], fields[5],
2959 fields[6], fields[7], fields[8], fields[9]);
2960 if (nFields == 0)
2961 return False;
2963 /* get the key name from the last field and translate it to a keysym.
2964 If the name is capitalized, try it lowercase as well, since some
2965 of the keysyms are "prettied up" by generateAcceleratorString */
2966 *keysym = XStringToKeysym(fields[nFields-1]);
2967 if (*keysym == NoSymbol) {
2968 *fields[nFields-1] = tolower(*fields[nFields-1]);
2969 *keysym = XStringToKeysym(fields[nFields-1]);
2970 if (*keysym == NoSymbol)
2971 return False;
2974 /* parse the modifier names from the rest of the fields */
2975 *modifiers = 0;
2976 for (i=0; i<nFields-1; i++) {
2977 if (!strcmp(fields[i], "Shift"))
2978 *modifiers |= ShiftMask;
2979 else if (!strcmp(fields[i], "Lock"))
2980 *modifiers |= LockMask;
2981 else if (!strcmp(fields[i], "Ctrl"))
2982 *modifiers |= ControlMask;
2983 /* comparision with "Alt" for compatibility with old .nedit files*/
2984 else if (!strcmp(fields[i], "Alt"))
2985 *modifiers |= Mod1Mask;
2986 else if (!strcmp(fields[i], "Mod2"))
2987 *modifiers |= Mod2Mask;
2988 else if (!strcmp(fields[i], "Mod3"))
2989 *modifiers |= Mod3Mask;
2990 else if (!strcmp(fields[i], "Mod4"))
2991 *modifiers |= Mod4Mask;
2992 else if (!strcmp(fields[i], "Mod5"))
2993 *modifiers |= Mod5Mask;
2994 else
2995 return False;
2998 /* all fields successfully parsed */
2999 return True;
3003 ** Scan text from "*inPtr" to the end of macro input (matching brace),
3004 ** advancing inPtr, and return macro text as function return value.
3006 ** This is kind of wastefull in that it throws away the compiled macro,
3007 ** to be re-generated from the text as needed, but compile time is
3008 ** negligible for most macros.
3010 static char *copyMacroToEnd(char **inPtr)
3012 char *retStr, *errMsg, *stoppedAt, *p, *retPtr;
3013 Program *prog;
3015 /* Skip over whitespace to find make sure there's a beginning brace
3016 to anchor the parse (if not, it will take the whole file) */
3017 *inPtr += strspn(*inPtr, " \t\n");
3018 if (**inPtr != '{') {
3019 ParseError(NULL, *inPtr, *inPtr-1, "macro menu item", "expecting '{'");
3020 return NULL;
3023 /* Parse the input */
3024 prog = ParseMacro(*inPtr, &errMsg, &stoppedAt);
3025 if (prog == NULL) {
3026 ParseError(NULL, *inPtr, stoppedAt, "macro menu item", errMsg);
3027 return NULL;
3029 FreeProgram(prog);
3031 /* Copy and return the body of the macro, stripping outer braces and
3032 extra leading tabs added by the writer routine */
3033 (*inPtr)++;
3034 *inPtr += strspn(*inPtr, " \t");
3035 if (**inPtr == '\n') (*inPtr)++;
3036 if (**inPtr == '\t') (*inPtr)++;
3037 if (**inPtr == '\t') (*inPtr)++;
3038 retPtr = retStr = XtMalloc(stoppedAt - *inPtr + 1);
3039 for (p = *inPtr; p < stoppedAt - 1; p++) {
3040 if (!strncmp(p, "\n\t\t", 3)) {
3041 *retPtr++ = '\n';
3042 p += 2;
3043 } else
3044 *retPtr++ = *p;
3046 if (*(retPtr-1) == '\t') retPtr--;
3047 *retPtr = '\0';
3048 *inPtr = stoppedAt;
3049 return retStr;
3053 ** If "*string" is not terminated with a newline character, reallocate the
3054 ** string and add one. (The macro language requires newline terminators for
3055 ** statements, but the text widget doesn't force it like the NEdit text buffer
3056 ** does, so this might avoid some confusion.)
3058 static void addTerminatingNewline(char **string)
3060 char *newString;
3061 int length;
3063 length = strlen(*string);
3064 if ((*string)[length-1] != '\n') {
3065 newString = XtMalloc(length + 2);
3066 strcpy(newString, *string);
3067 newString[length] = '\n';
3068 newString[length+1] = '\0';
3069 XtFree(*string);
3070 *string = newString;
3075 ** Cache user menus:
3076 ** allocate an empty user (shell, macro) menu cache structure
3078 UserMenuCache *CreateUserMenuCache(void)
3080 /* allocate some memory for the new data structure */
3081 UserMenuCache *cache = (UserMenuCache *)XtMalloc(sizeof(UserMenuCache));
3083 cache->umcLanguageMode = -2;
3084 cache->umcShellMenuCreated = False;
3085 cache->umcMacroMenuCreated = False;
3086 cache->umcShellMenuList.umlNbrItems = 0;
3087 cache->umcShellMenuList.umlItems = NULL;
3088 cache->umcMacroMenuList.umlNbrItems = 0;
3089 cache->umcMacroMenuList.umlItems = NULL;
3091 return cache;
3094 void FreeUserMenuCache(UserMenuCache *cache)
3096 freeUserMenuList(&cache->umcShellMenuList);
3097 freeUserMenuList(&cache->umcMacroMenuList);
3099 XtFree((char *)cache);
3103 ** Cache user menus:
3104 ** init. a user background menu cache structure
3106 void InitUserBGMenuCache(UserBGMenuCache *cache)
3108 cache->ubmcLanguageMode = -2;
3109 cache->ubmcMenuCreated = False;
3110 cache->ubmcMenuList.umlNbrItems = 0;
3111 cache->ubmcMenuList.umlItems = NULL;
3114 void FreeUserBGMenuCache(UserBGMenuCache *cache)
3116 freeUserMenuList(&cache->ubmcMenuList);
3120 ** Cache user menus:
3121 ** Parse given menu item list and setup a user menu info list for
3122 ** management of user menu.
3124 static void parseMenuItemList(menuItemRec **itemList, int nbrOfItems,
3125 userMenuInfo **infoList, userSubMenuCache *subMenus)
3127 int i;
3128 userMenuInfo *info;
3130 /* Allocate storage for structures to keep track of sub-menus */
3131 allocSubMenuCache(subMenus, nbrOfItems);
3133 /* 1st pass: setup user menu info: extract language modes, menu name &
3134 default indication; build user menu ID */
3135 for (i=0; i<nbrOfItems; i++) {
3136 infoList[i] = parseMenuItemRec(itemList[i]);
3137 generateUserMenuId(infoList[i], subMenus);
3140 /* 2nd pass: solve "default" dependencies */
3141 for (i=0; i<nbrOfItems; i++) {
3142 info = infoList[i];
3144 /* If the user menu item is a default one, then scan the list for
3145 items with the same name and a language mode specified.
3146 If one is found, then set the default index to the index of the
3147 current default item. */
3148 if (info->umiIsDefault) {
3149 setDefaultIndex(infoList, nbrOfItems, i);
3155 ** Returns the sub-menu depth (i.e. nesting level) of given
3156 ** menu name.
3158 static int getSubMenuDepth(const char *menuName)
3160 const char *subSep;
3161 int depth = 0;
3163 /* determine sub-menu depth by counting '>' of given "menuName" */
3164 subSep = menuName;
3165 while ((subSep = strchr(subSep, '>')) != NULL ) {
3166 depth ++;
3167 subSep ++;
3170 return depth;
3174 ** Cache user menus:
3175 ** Parse a singe menu item. Allocate & setup a user menu info element
3176 ** holding extracted info.
3178 static userMenuInfo *parseMenuItemRec(menuItemRec *item)
3180 userMenuInfo *newInfo;
3181 int subMenuDepth;
3182 int idSize;
3184 /* allocate a new user menu info element */
3185 newInfo = (userMenuInfo *)XtMalloc(sizeof(userMenuInfo));
3187 /* determine sub-menu depth and allocate some memory
3188 for hierarchical ID; init. ID with {0,.., 0} */
3189 newInfo->umiName = stripLanguageMode(item->name);
3191 subMenuDepth = getSubMenuDepth(newInfo->umiName);
3192 idSize = sizeof(int)*(subMenuDepth+1);
3194 newInfo->umiId = (int *)XtMalloc(idSize);
3195 memset(newInfo->umiId,0,idSize);
3197 /* init. remaining parts of user menu info element */
3198 newInfo->umiIdLen = 0;
3199 newInfo->umiIsDefault = False;
3200 newInfo->umiNbrOfLanguageModes = 0;
3201 newInfo->umiLanguageMode = NULL;
3202 newInfo->umiDefaultIndex = -1;
3203 newInfo->umiToBeManaged = False;
3205 /* assign language mode info to new user menu info element */
3206 parseMenuItemName(item->name, newInfo);
3208 return newInfo;
3212 ** Cache user menus:
3213 ** Extract language mode related info out of given menu item name string.
3214 ** Store this info in given user menu info structure.
3216 static void parseMenuItemName(char *menuItemName, userMenuInfo *info)
3218 char *atPtr, *firstAtPtr, *endPtr;
3219 char c;
3220 int languageMode;
3221 int langModes[MAX_LANGUAGE_MODES];
3222 int nbrLM = 0;
3223 int size;
3225 atPtr = firstAtPtr = strchr(menuItemName, '@');
3226 if (atPtr != NULL) {
3227 if (!strcmp(atPtr+1, "*")) {
3228 /* only language is "*": this is for all but language specific
3229 macros */
3230 info->umiIsDefault = True;
3231 return;
3234 /* setup a list of all language modes related to given menu item */
3235 while (atPtr != NULL) {
3236 /* extract language mode name after "@" sign */
3237 for(endPtr=atPtr+1; isalnum((unsigned char)*endPtr) || *endPtr=='_' ||
3238 *endPtr=='-' || *endPtr==' ' || *endPtr=='+' || *endPtr=='$' ||
3239 *endPtr=='#'; endPtr++);
3241 /* lookup corresponding language mode index; if PLAIN is
3242 returned then this means, that language mode name after
3243 "@" is unknown (i.e. not defined) */
3244 c = *endPtr;
3245 *endPtr = '\0';
3246 languageMode = FindLanguageMode(atPtr+1);
3247 if (languageMode == PLAIN_LANGUAGE_MODE) {
3248 langModes[nbrLM] = UNKNOWN_LANGUAGE_MODE;
3249 } else {
3250 langModes[nbrLM] = languageMode;
3252 nbrLM ++;
3253 *endPtr = c;
3255 /* look for next "@" */
3256 atPtr = strchr(endPtr, '@');
3259 if (nbrLM != 0) {
3260 info->umiNbrOfLanguageModes = nbrLM;
3261 size = sizeof(int)*nbrLM;
3262 info->umiLanguageMode = (int *)XtMalloc(size);
3263 memcpy(info->umiLanguageMode, langModes, size);
3269 ** Cache user menus:
3270 ** generates an ID (= array of integers) of given user menu info, which
3271 ** allows to find the user menu item within the menu tree later on: 1st
3272 ** integer of ID indicates position within main menu; 2nd integer indicates
3273 ** position within 1st sub-menu etc.
3275 static void generateUserMenuId(userMenuInfo *info, userSubMenuCache *subMenus)
3277 int idSize;
3278 char *hierName, *subSep;
3279 int subMenuDepth = 0;
3280 int *menuIdx = &subMenus->usmcNbrOfMainMenuItems;
3281 userSubMenuInfo *curSubMenu;
3283 /* find sub-menus, stripping off '>' until item name is
3284 reached */
3285 subSep = info->umiName;
3286 while ((subSep = strchr(subSep, '>')) != NULL) {
3287 hierName = copySubstring(info->umiName, subSep - info->umiName);
3288 curSubMenu = findSubMenuInfo(subMenus, hierName);
3289 if (curSubMenu == NULL) {
3290 /* sub-menu info not stored before: new sub-menu;
3291 remember its hierarchical position */
3292 info->umiId[subMenuDepth] = *menuIdx;
3293 (*menuIdx) ++;
3295 /* store sub-menu info in list of subMenus; allocate
3296 some memory for hierarchical ID of sub-menu & take over
3297 current hierarchical ID of current user menu info */
3298 curSubMenu = &subMenus->usmcInfo[subMenus->usmcNbrOfSubMenus];
3299 subMenus->usmcNbrOfSubMenus ++;
3300 curSubMenu->usmiName = hierName;
3301 idSize = sizeof(int)*(subMenuDepth+2);
3302 curSubMenu->usmiId = (int *)XtMalloc(idSize);
3303 memcpy(curSubMenu->usmiId, info->umiId, idSize);
3304 curSubMenu->usmiIdLen = subMenuDepth+1;
3305 } else {
3306 /* sub-menu info already stored before: takeover its
3307 hierarchical position */
3308 XtFree(hierName);
3309 info->umiId[subMenuDepth] = curSubMenu->usmiId[subMenuDepth];
3312 subMenuDepth ++;
3313 menuIdx = &curSubMenu->usmiId[subMenuDepth];
3315 subSep ++;
3318 /* remember position of menu item within final (sub) menu */
3319 info->umiId[subMenuDepth] = *menuIdx;
3320 info->umiIdLen = subMenuDepth + 1;
3321 (*menuIdx) ++;
3325 ** Cache user menus:
3326 ** Find info corresponding to a hierarchical menu name (a>b>c...)
3328 static userSubMenuInfo *findSubMenuInfo(userSubMenuCache *subMenus,
3329 const char *hierName)
3331 int i;
3333 for (i=0; i<subMenus->usmcNbrOfSubMenus; i++)
3334 if (!strcmp(hierName, subMenus->usmcInfo[i].usmiName))
3335 return &subMenus->usmcInfo[i];
3336 return NULL;
3340 ** Cache user menus:
3341 ** Returns an allocated copy of menuItemName stripped of language mode
3342 ** parts (i.e. parts starting with "@").
3344 static char *stripLanguageMode(const char *menuItemName)
3346 char *firstAtPtr;
3348 firstAtPtr = strchr(menuItemName, '@');
3349 if (firstAtPtr == NULL)
3350 return XtNewString(menuItemName);
3351 else
3352 return copySubstring(menuItemName, firstAtPtr-menuItemName);
3355 static void setDefaultIndex(userMenuInfo **infoList, int nbrOfItems,
3356 int defaultIdx)
3358 char *defaultMenuName = infoList[defaultIdx]->umiName;
3359 int i;
3360 userMenuInfo *info;
3362 /* Scan the list for items with the same name and a language mode
3363 specified. If one is found, then set the default index to the
3364 index of the current default item. */
3365 for (i=0; i<nbrOfItems; i++) {
3366 info = infoList[i];
3368 if (!info->umiIsDefault && strcmp(info->umiName, defaultMenuName)==0) {
3369 info->umiDefaultIndex = defaultIdx;
3375 ** Determine the info list menu items, which need to be managed
3376 ** for given language mode. Set / reset "to be managed" indication
3377 ** of info list items accordingly.
3379 static void applyLangModeToUserMenuInfo(userMenuInfo **infoList, int nbrOfItems,
3380 int languageMode)
3382 int i;
3383 userMenuInfo *info;
3385 /* 1st pass: mark all items as "to be managed", which are applicable
3386 for all language modes or which are indicated as "default" items */
3387 for (i=0; i<nbrOfItems; i++) {
3388 info = infoList[i];
3390 info->umiToBeManaged =
3391 (info->umiNbrOfLanguageModes == 0 || info->umiIsDefault);
3394 /* 2nd pass: mark language mode specific items matching given language
3395 mode as "to be managed". Reset "to be managed" indications of
3396 "default" items, if applicable */
3397 for (i=0; i<nbrOfItems; i++) {
3398 info = infoList[i];
3400 if (info->umiNbrOfLanguageModes != 0) {
3401 if (doesLanguageModeMatch(info, languageMode)) {
3402 info->umiToBeManaged = True;
3404 if (info->umiDefaultIndex != -1)
3405 infoList[info->umiDefaultIndex]->umiToBeManaged = False;
3412 ** Returns true, if given user menu info is applicable for given language mode
3414 static int doesLanguageModeMatch(userMenuInfo *info, int languageMode)
3416 int i;
3418 for (i=0; i<info->umiNbrOfLanguageModes; i++) {
3419 if (info->umiLanguageMode[i] == languageMode)
3420 return True;
3423 return False;
3426 static void freeUserMenuInfoList(userMenuInfo **infoList, int nbrOfItems)
3428 int i;
3430 for (i=0; i<nbrOfItems; i++) {
3431 freeUserMenuInfo(infoList[i]);
3435 static void freeUserMenuInfo(userMenuInfo *info)
3437 XtFree(info->umiName);
3439 XtFree((char *)info->umiId);
3441 if (info->umiNbrOfLanguageModes != 0)
3442 XtFree((char *)info->umiLanguageMode);
3444 XtFree((char *)info);
3448 ** Cache user menus:
3449 ** Allocate & init. storage for structures to manage sub-menus
3451 static void allocSubMenuCache(userSubMenuCache *subMenus, int nbrOfItems)
3453 int size = sizeof(userSubMenuInfo) * nbrOfItems;
3455 subMenus->usmcNbrOfMainMenuItems = 0;
3456 subMenus->usmcNbrOfSubMenus = 0;
3457 subMenus->usmcInfo = (userSubMenuInfo *)XtMalloc(size);
3460 static void freeSubMenuCache(userSubMenuCache *subMenus)
3462 int i;
3464 for (i=0; i<subMenus->usmcNbrOfSubMenus; i++) {
3465 XtFree(subMenus->usmcInfo[i].usmiName);
3466 XtFree((char *)subMenus->usmcInfo[i].usmiId);
3469 XtFree((char *)subMenus->usmcInfo);
3472 static void allocUserMenuList(UserMenuList *list, int nbrOfItems)
3474 int size = sizeof(UserMenuListElement *) * nbrOfItems;
3476 list->umlNbrItems = 0;
3477 list->umlItems = (UserMenuListElement **)XtMalloc(size);
3480 static void freeUserMenuList(UserMenuList *list)
3482 int i;
3484 for (i=0; i<list->umlNbrItems; i++)
3485 freeUserMenuListElement(list->umlItems[i]);
3487 list->umlNbrItems = 0;
3489 XtFree((char*) list->umlItems);
3490 list->umlItems = NULL;
3493 static UserMenuListElement *allocUserMenuListElement(Widget menuItem, char *accKeys)
3495 UserMenuListElement *element;
3497 element = (UserMenuListElement *)XtMalloc(sizeof(UserMenuListElement));
3499 element->umleManageMode = UMMM_UNMANAGE;
3500 element->umlePrevManageMode = UMMM_UNMANAGE;
3501 element->umleAccKeys = accKeys;
3502 element->umleAccLockPatchApplied = False;
3503 element->umleMenuItem = menuItem;
3504 element->umleSubMenuPane = NULL;
3505 element->umleSubMenuList = NULL;
3507 return element;
3510 static void freeUserMenuListElement(UserMenuListElement *element)
3512 if (element->umleSubMenuList != NULL)
3513 freeUserSubMenuList(element->umleSubMenuList);
3515 XtFree(element->umleAccKeys);
3516 XtFree((char *)element);
3519 static UserMenuList *allocUserSubMenuList(int nbrOfItems)
3521 UserMenuList *list;
3523 list = (UserMenuList *)XtMalloc(sizeof(UserMenuList));
3525 allocUserMenuList(list, nbrOfItems);
3527 return list;
3530 static void freeUserSubMenuList(UserMenuList *list)
3532 freeUserMenuList(list);
3534 XtFree((char *)list);