Crash fix: bgMenuUndo/RedoItem were memcopied when opening multiple tabs in
[nedit.git] / source / userCmds.c
blob439cc5538c85ed1cdb46acddc1a381578db9f8e2
1 static const char CVSID[] = "$Id: userCmds.c,v 1.46 2004/06/09 17:52:58 edg Exp $";
2 /*******************************************************************************
3 * *
4 * userCmds.c -- Nirvana Editor shell and macro command dialogs *
5 * *
6 * Copyright (C) 1999 Mark Edel *
7 * *
8 * This is free software; you can redistribute it and/or modify it under the *
9 * terms of the GNU General Public License as published by the Free Software *
10 * Foundation; either version 2 of the License, or (at your option) any later *
11 * version. *
12 * *
13 * This software is distributed in the hope that it will be useful, but WITHOUT *
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
16 * for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License along with *
19 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
20 * Place, Suite 330, Boston, MA 02111-1307 USA *
21 * *
22 * Nirvana Text Editor *
23 * April, 1997 *
24 * *
25 * Written by Mark Edel *
26 * *
27 *******************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 #include "../config.h"
31 #endif
33 #include "userCmds.h"
34 #include "textBuf.h"
35 #include "text.h"
36 #include "nedit.h"
37 #include "preferences.h"
38 #include "window.h"
39 #include "menu.h"
40 #include "shell.h"
41 #include "macro.h"
42 #include "file.h"
43 #include "interpret.h"
44 #include "parse.h"
45 #include "../util/DialogF.h"
46 #include "../util/misc.h"
47 #include "../util/managedList.h"
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <ctype.h>
53 #ifdef VMS
54 #include "../util/VMSparam.h"
55 #else
56 #ifndef __MVS__
57 #include <sys/param.h>
58 #endif
59 #endif /*VMS*/
61 #include <Xm/Xm.h>
62 #include <X11/keysym.h>
63 #include <X11/IntrinsicP.h>
64 #include <Xm/Text.h>
65 #include <Xm/Form.h>
66 #include <Xm/List.h>
67 #include <Xm/LabelG.h>
68 #include <Xm/PushB.h>
69 #include <Xm/ToggleB.h>
70 #include <Xm/SelectioB.h>
71 #include <Xm/RowColumn.h>
72 #include <Xm/CascadeB.h>
73 #include <Xm/MenuShell.h>
75 #ifdef HAVE_DEBUG_H
76 #include "../debug.h"
77 #endif
79 #if XmVersion >= 1002
80 #define MENU_WIDGET(w) (XmGetPostedFromWidget(XtParent(w)))
81 #else
82 #define MENU_WIDGET(w) (w)
83 #endif
85 extern void _XmDismissTearOff(Widget w, XtPointer call, XtPointer x);
87 /* max. number of user programmable menu commands allowed per each of the
88 macro, shell, and background menus */
89 #define MAX_ITEMS_PER_MENU 400
91 /* indicates, that an unknown (i.e. not existing) language mode
92 is bound to an user menu item */
93 #define UNKNOWN_LANGUAGE_MODE -2
95 /* major divisions (in position units) in User Commands dialogs */
96 #define LEFT_MARGIN_POS 1
97 #define RIGHT_MARGIN_POS 99
98 #define LIST_RIGHT 45
99 #define SHELL_CMD_TOP 70
100 #define MACRO_CMD_TOP 40
102 /* types of current dialog and/or menu */
103 enum dialogTypes {SHELL_CMDS, MACRO_CMDS, BG_MENU_CMDS};
105 /* Structure representing a menu item for shell, macro and BG menus*/
106 typedef struct {
107 char *name;
108 unsigned int modifiers;
109 KeySym keysym;
110 char mnemonic;
111 char input;
112 char output;
113 char repInput;
114 char saveFirst;
115 char loadAfter;
116 char *cmd;
117 } menuItemRec;
119 /* Structure for widgets and flags associated with shell command,
120 macro command and BG command editing dialogs */
121 typedef struct {
122 int dialogType;
123 WindowInfo *window;
124 Widget nameTextW, accTextW, mneTextW, cmdTextW, saveFirstBtn;
125 Widget loadAfterBtn, selInpBtn, winInpBtn, eitherInpBtn, noInpBtn;
126 Widget repInpBtn, sameOutBtn, dlogOutBtn, winOutBtn, dlogShell;
127 Widget managedList;
128 menuItemRec **menuItemsList;
129 int nMenuItems;
130 } userCmdDialog;
132 /* Structure for keeping track of hierarchical sub-menus during user-menu
133 creation */
134 typedef struct {
135 char *name;
136 Widget menuPane;
137 } menuTreeItem;
139 /* Structure holding hierarchical info about one sub-menu.
141 Suppose following user menu items:
142 a.) "menuItem1"
143 b.) "subMenuA>menuItemA1"
144 c.) "subMenuA>menuItemA2"
145 d.) "subMenuA>subMenuB>menuItemB1"
146 e.) "subMenuA>subMenuB>menuItemB2"
148 Structure of this user menu is:
150 Main Menu Name Sub-Menu A Name Sub-Menu B Name
151 element nbr. element nbr. element nbr.
152 0 menuItem1
153 1 subMenuA --+-> 0 menuItemA1
154 +-> 1 menuItemA2
155 +-> 2 subMenuB --+-> 0 menuItemB1
156 +-> 1 menuItemB2
158 Above example holds 2 sub-menus:
159 1.) "subMenuA" (hierarchical ID = {1} means: element nbr. "1" of main menu)
160 2.) "subMenuA>subMenuB" (hierarchical ID = {1, 2} means: el. nbr. "2" of
161 "subMenuA", which itself is el. nbr. "0" of main menu) */
162 typedef struct {
163 char *usmiName; /* hierarchical name of sub-menu */
164 int *usmiId; /* hierarchical ID of sub-menu */
165 int usmiIdLen; /* length of hierarchical ID */
166 } userSubMenuInfo;
168 /* Holds info about sub-menu structure of an user menu */
169 typedef struct {
170 int usmcNbrOfMainMenuItems; /* number of main menu items */
171 int usmcNbrOfSubMenus; /* number of sub-menus */
172 userSubMenuInfo *usmcInfo; /* list of sub-menu info */
173 } userSubMenuCache;
175 /* Structure holding info about a single menu item.
176 According to above example there exist 5 user menu items:
177 a.) "menuItem1" (hierarchical ID = {0} means: element nbr. "0" of main menu)
178 b.) "menuItemA1" (hierarchical ID = {1, 0} means: el. nbr. "0" of
179 "subMenuA", which itself is el. nbr. "1" of main menu)
180 c.) "menuItemA2" (hierarchical ID = {1, 1})
181 d.) "menuItemB1" (hierarchical ID = {1, 2, 0})
182 e.) "menuItemB2" (hierarchical ID = {1, 2, 1})
184 typedef struct {
185 char *umiName; /* hierarchical name of menu item
186 (w.o. language mode info) */
187 int *umiId; /* hierarchical ID of menu item */
188 int umiIdLen; /* length of hierarchical ID */
189 Boolean umiIsDefault; /* menu item is default one ("@*") */
190 int umiNbrOfLanguageModes; /* number of language modes
191 applicable for this menu item */
192 int *umiLanguageMode; /* list of applicable lang. modes */
193 int umiDefaultIndex; /* array index of menu item to be
194 used as default, if no lang. mode
195 matches */
196 Boolean umiToBeManaged; /* indicates, that menu item needs
197 to be managed */
198 } userMenuInfo;
200 /* Structure holding info about a selected user menu (shell, macro or
201 background) */
202 typedef struct {
203 int sumType; /* type of menu (shell, macro or
204 background */
205 Widget sumMenuPane; /* pane of main menu */
206 int sumNbrOfListItems; /* number of menu items */
207 menuItemRec **sumItemList; /* list of menu items */
208 userMenuInfo **sumInfoList; /* list of infos about menu items */
209 userSubMenuCache *sumSubMenus; /* info about sub-menu structure */
210 UserMenuList *sumMainMenuList; /* cached info about main menu */
211 Boolean *sumMenuCreated; /* pointer to "menu created"
212 indicator */
213 } selectedUserMenu;
215 /* Descriptions of the current user programmed menu items for re-generating
216 menus and processing shell, macro, and background menu selections */
217 static menuItemRec *ShellMenuItems[MAX_ITEMS_PER_MENU];
218 static userMenuInfo *ShellMenuInfo[MAX_ITEMS_PER_MENU];
219 static userSubMenuCache ShellSubMenus;
220 static int NShellMenuItems = 0;
221 static menuItemRec *MacroMenuItems[MAX_ITEMS_PER_MENU];
222 static userMenuInfo *MacroMenuInfo[MAX_ITEMS_PER_MENU];
223 static userSubMenuCache MacroSubMenus;
224 static int NMacroMenuItems = 0;
225 static menuItemRec *BGMenuItems[MAX_ITEMS_PER_MENU];
226 static userMenuInfo *BGMenuInfo[MAX_ITEMS_PER_MENU];
227 static userSubMenuCache BGSubMenus;
228 static int NBGMenuItems = 0;
230 /* Top level shells of the user-defined menu editing dialogs */
231 static Widget ShellCmdDialog = NULL;
232 static Widget MacroCmdDialog = NULL;
233 static Widget BGMenuCmdDialog = NULL;
235 /* Paste learn/replay sequence buttons in user defined menu editing dialogs
236 (for dimming and undimming externally when replay macro is available) */
237 static Widget MacroPasteReplayBtn = NULL;
238 static Widget BGMenuPasteReplayBtn = NULL;
240 static void editMacroOrBGMenu(WindowInfo *window, int dialogType);
241 static void dimSelDepItemsInMenu(Widget menuPane, menuItemRec **menuList,
242 int nMenuItems, int sensitive);
243 static void rebuildMenuOfAllWindows(int menuType);
244 static void rebuildMenu(WindowInfo *window, int menuType);
245 static Widget findInMenuTree(menuTreeItem *menuTree, int nTreeEntries,
246 const char *hierName);
247 static char *copySubstring(const char *string, int length);
248 static Widget createUserMenuItem(Widget menuPane, char *name, menuItemRec *f,
249 int index, XtCallbackProc cbRtn, XtPointer cbArg);
250 static Widget createUserSubMenu(Widget parent, char *label, Widget *menuItem);
251 static void deleteMenuItems(Widget menuPane);
252 static void selectUserMenu(WindowInfo *window, int menuType, selectedUserMenu *menu);
253 static void updateMenu(WindowInfo *window, int menuType);
254 static void manageTearOffMenu(Widget menuPane);
255 static void resetManageMode(UserMenuList *list);
256 static void manageAllSubMenuWidgets(UserMenuListElement *subMenu);
257 static void unmanageAllSubMenuWidgets(UserMenuListElement *subMenu);
258 static void manageMenuWidgets(UserMenuList *list);
259 static void removeAccelFromMenuWidgets(UserMenuList *menuList);
260 static void assignAccelToMenuWidgets(UserMenuList *menuList, WindowInfo *window);
261 static void manageUserMenu(selectedUserMenu *menu, WindowInfo *window);
262 static void createMenuItems(WindowInfo *window, selectedUserMenu *menu);
263 static void okCB(Widget w, XtPointer clientData, XtPointer callData);
264 static void applyCB(Widget w, XtPointer clientData, XtPointer callData);
265 static void checkCB(Widget w, XtPointer clientData, XtPointer callData);
266 static int checkMacro(userCmdDialog *ucd);
267 static int checkMacroText(char *macro, Widget errorParent, Widget errFocus);
268 static int applyDialogChanges(userCmdDialog *ucd);
269 static void dismissCB(Widget w, XtPointer clientData, XtPointer callData);
270 static void pasteReplayCB(Widget w, XtPointer clientData, XtPointer callData);
271 static void destroyCB(Widget w, XtPointer clientData, XtPointer callData);
272 static void accKeyCB(Widget w, XtPointer clientData, XKeyEvent *event);
273 static void sameOutCB(Widget w, XtPointer clientData, XtPointer callData);
274 static void shellMenuCB(Widget w, WindowInfo *window, XtPointer callData);
275 static void macroMenuCB(Widget w, WindowInfo *window, XtPointer callData);
276 static void bgMenuCB(Widget w, WindowInfo *window, XtPointer callData) ;
277 static void accFocusCB(Widget w, XtPointer clientData, XtPointer callData);
278 static void accLoseFocusCB(Widget w, XtPointer clientData,
279 XtPointer callData);
280 static void updateDialogFields(menuItemRec *f, userCmdDialog *ucd);
281 static menuItemRec *readDialogFields(userCmdDialog *ucd, int silent);
282 static menuItemRec *copyMenuItemRec(menuItemRec *item);
283 static void freeMenuItemRec(menuItemRec *item);
284 static void *getDialogDataCB(void *oldItem, int explicitRequest, int *abort,
285 void *cbArg);
286 static void setDialogDataCB(void *item, void *cbArg);
287 static void freeItemCB(void *item);
288 static int dialogFieldsAreEmpty(userCmdDialog *ucd);
289 static void disableTextW(Widget textW);
290 static char *writeMenuItemString(menuItemRec **menuItems, int nItems,
291 int listType);
292 static int loadMenuItemString(char *inString, menuItemRec **menuItems,
293 int *nItems, int listType);
294 static void generateAcceleratorString(char *text, unsigned int modifiers,
295 KeySym keysym);
296 static void genAccelEventName(char *text, unsigned int modifiers,
297 KeySym keysym);
298 static int parseAcceleratorString(const char *string, unsigned int *modifiers,
299 KeySym *keysym);
300 static int parseError(const char *message);
301 static char *copyMacroToEnd(char **inPtr);
302 static void addTerminatingNewline(char **string);
303 static void parseMenuItemList(menuItemRec **itemList, int nbrOfItems,
304 userMenuInfo **infoList, userSubMenuCache *subMenus);
305 static int getSubMenuDepth(const char *menuName);
306 static userMenuInfo *parseMenuItemRec(menuItemRec *item);
307 static void parseMenuItemName(char *menuItemName, userMenuInfo *info);
308 static void generateUserMenuId(userMenuInfo *info, userSubMenuCache *subMenus);
309 static userSubMenuInfo *findSubMenuInfo(userSubMenuCache *subMenus,
310 const char *hierName);
311 static char *stripLanguageMode(const char *menuItemName);
312 static void setDefaultIndex(userMenuInfo **infoList, int nbrOfItems,
313 int defaultIdx);
314 static void applyLangModeToUserMenuInfo(userMenuInfo **infoList, int nbrOfItems,
315 int languageMode);
316 static int doesLanguageModeMatch(userMenuInfo *info, int languageMode);
317 static void freeUserMenuInfoList(userMenuInfo **infoList, int nbrOfItems);
318 static void freeUserMenuInfo(userMenuInfo *info);
319 static void allocSubMenuCache(userSubMenuCache *subMenus, int nbrOfItems);
320 static void freeSubMenuCache(userSubMenuCache *subMenus);
321 static void allocUserMenuList(UserMenuList *list, int nbrOfItems);
322 static void freeUserMenuList(UserMenuList *list);
323 static UserMenuListElement *allocUserMenuListElement(Widget menuItem, char *accKeys);
324 static void freeUserMenuListElement(UserMenuListElement *element);
325 static UserMenuList *allocUserSubMenuList(int nbrOfItems);
326 static void freeUserSubMenuList(UserMenuList *list);
329 ** Present a dialog for editing the user specified commands in the shell menu
331 void EditShellMenu(WindowInfo *window)
333 Widget form, accLabel, inpLabel, inpBox, outBox, outLabel;
334 Widget nameLabel, cmdLabel, okBtn, applyBtn, dismissBtn;
335 userCmdDialog *ucd;
336 XmString s1;
337 int ac, i;
338 Arg args[20];
340 /* if the dialog is already displayed, just pop it to the top and return */
341 if (ShellCmdDialog != NULL) {
342 RaiseShellWindow(ShellCmdDialog);
343 return;
346 /* Create a structure for keeping track of dialog state */
347 ucd = (userCmdDialog *)XtMalloc(sizeof(userCmdDialog));
348 ucd->window = window;
350 /* Set the dialog to operate on the Shell menu */
351 ucd->menuItemsList = (menuItemRec **)XtMalloc(sizeof(menuItemRec *) *
352 MAX_ITEMS_PER_MENU);
353 for (i=0; i<NShellMenuItems; i++)
354 ucd->menuItemsList[i] = copyMenuItemRec(ShellMenuItems[i]);
355 ucd->nMenuItems = NShellMenuItems;
356 ucd->dialogType = SHELL_CMDS;
358 ac = 0;
359 XtSetArg(args[ac], XmNdeleteResponse, XmDO_NOTHING); ac++;
360 XtSetArg(args[ac], XmNiconName, "NEdit Shell Menu"); ac++;
361 XtSetArg(args[ac], XmNtitle, "Shell Menu"); ac++;
362 ucd->dlogShell = CreateWidget(TheAppShell, "shellCommands",
363 topLevelShellWidgetClass, args, ac);
364 AddSmallIcon(ucd->dlogShell);
365 form = XtVaCreateManagedWidget("editShellCommands", xmFormWidgetClass,
366 ucd->dlogShell, XmNautoUnmanage, False,
367 XmNresizePolicy, XmRESIZE_NONE, NULL);
368 ShellCmdDialog = ucd->dlogShell;
369 XtAddCallback(form, XmNdestroyCallback, destroyCB, ucd);
370 AddMotifCloseCallback(ucd->dlogShell, dismissCB, ucd);
372 ac = 0;
373 XtSetArg(args[ac], XmNtopAttachment, XmATTACH_POSITION); ac++;
374 XtSetArg(args[ac], XmNtopPosition, 2); ac++;
375 XtSetArg(args[ac], XmNleftAttachment, XmATTACH_POSITION); ac++;
376 XtSetArg(args[ac], XmNleftPosition, LEFT_MARGIN_POS); ac++;
377 XtSetArg(args[ac], XmNrightAttachment, XmATTACH_POSITION); ac++;
378 XtSetArg(args[ac], XmNrightPosition, LIST_RIGHT-1); ac++;
379 XtSetArg(args[ac], XmNbottomAttachment, XmATTACH_POSITION); ac++;
380 XtSetArg(args[ac], XmNbottomPosition, SHELL_CMD_TOP); ac++;
381 ucd->managedList = CreateManagedList(form, "list", args, ac,
382 (void **)ucd->menuItemsList, &ucd->nMenuItems, MAX_ITEMS_PER_MENU,
383 20, getDialogDataCB, ucd, setDialogDataCB, ucd, freeItemCB);
385 ucd->loadAfterBtn = XtVaCreateManagedWidget("loadAfterBtn",
386 xmToggleButtonWidgetClass, form,
387 XmNlabelString, s1=MKSTRING("Re-load file after executing command"),
388 XmNmnemonic, 'R',
389 XmNalignment, XmALIGNMENT_BEGINNING,
390 XmNset, False,
391 XmNleftAttachment, XmATTACH_POSITION,
392 XmNleftPosition, LIST_RIGHT,
393 XmNrightAttachment, XmATTACH_POSITION,
394 XmNrightPosition, RIGHT_MARGIN_POS,
395 XmNbottomAttachment, XmATTACH_POSITION,
396 XmNbottomPosition, SHELL_CMD_TOP, NULL);
397 XmStringFree(s1);
398 ucd->saveFirstBtn = XtVaCreateManagedWidget("saveFirstBtn",
399 xmToggleButtonWidgetClass, form,
400 XmNlabelString, s1=MKSTRING("Save file before executing command"),
401 XmNmnemonic, 'f',
402 XmNalignment, XmALIGNMENT_BEGINNING,
403 XmNset, False,
404 XmNleftAttachment, XmATTACH_POSITION,
405 XmNleftPosition, LIST_RIGHT,
406 XmNrightAttachment, XmATTACH_POSITION,
407 XmNrightPosition, RIGHT_MARGIN_POS,
408 XmNbottomAttachment, XmATTACH_WIDGET,
409 XmNbottomWidget, ucd->loadAfterBtn, NULL);
410 XmStringFree(s1);
411 ucd->repInpBtn = XtVaCreateManagedWidget("repInpBtn",
412 xmToggleButtonWidgetClass, form,
413 XmNlabelString, s1=MKSTRING("Output replaces input"),
414 XmNmnemonic, 'f',
415 XmNalignment, XmALIGNMENT_BEGINNING,
416 XmNset, False,
417 XmNleftAttachment, XmATTACH_POSITION,
418 XmNleftPosition, LIST_RIGHT,
419 XmNrightAttachment, XmATTACH_POSITION,
420 XmNrightPosition, RIGHT_MARGIN_POS,
421 XmNbottomAttachment, XmATTACH_WIDGET,
422 XmNbottomWidget, ucd->saveFirstBtn, NULL);
423 XmStringFree(s1);
424 outBox = XtVaCreateManagedWidget("outBox", xmRowColumnWidgetClass, form,
425 XmNpacking, XmPACK_TIGHT,
426 XmNorientation, XmHORIZONTAL,
427 XmNradioBehavior, True,
428 XmNradioAlwaysOne, True,
429 XmNleftAttachment, XmATTACH_POSITION,
430 XmNleftPosition, LIST_RIGHT + 2,
431 XmNrightAttachment, XmATTACH_POSITION,
432 XmNrightPosition, RIGHT_MARGIN_POS,
433 XmNbottomAttachment, XmATTACH_WIDGET,
434 XmNbottomWidget, ucd->repInpBtn,
435 XmNbottomOffset, 4, NULL);
436 ucd->sameOutBtn = XtVaCreateManagedWidget("sameOutBtn",
437 xmToggleButtonWidgetClass, outBox,
438 XmNlabelString, s1=MKSTRING("same window"),
439 XmNmnemonic, 'm',
440 XmNalignment, XmALIGNMENT_BEGINNING,
441 XmNmarginHeight, 0,
442 XmNset, True, NULL);
443 XmStringFree(s1);
444 XtAddCallback(ucd->sameOutBtn, XmNvalueChangedCallback, sameOutCB, ucd);
445 ucd->dlogOutBtn = XtVaCreateManagedWidget("dlogOutBtn",
446 xmToggleButtonWidgetClass, outBox,
447 XmNlabelString, s1=MKSTRING("dialog"),
448 XmNmnemonic, 'g',
449 XmNalignment, XmALIGNMENT_BEGINNING,
450 XmNmarginHeight, 0,
451 XmNset, False, NULL);
452 XmStringFree(s1);
453 ucd->winOutBtn = XtVaCreateManagedWidget("winOutBtn", xmToggleButtonWidgetClass,
454 outBox,
455 XmNlabelString, s1=MKSTRING("new window"),
456 XmNmnemonic, 'n',
457 XmNalignment, XmALIGNMENT_BEGINNING,
458 XmNmarginHeight, 0,
459 XmNset, False, NULL);
460 XmStringFree(s1);
461 outLabel = XtVaCreateManagedWidget("outLabel", xmLabelGadgetClass, form,
462 XmNlabelString, s1=MKSTRING("Command Output:"),
463 XmNalignment, XmALIGNMENT_BEGINNING,
464 XmNmarginTop, 5,
465 XmNleftAttachment, XmATTACH_POSITION,
466 XmNleftPosition, LIST_RIGHT,
467 XmNrightAttachment, XmATTACH_POSITION,
468 XmNrightPosition, RIGHT_MARGIN_POS,
469 XmNbottomAttachment, XmATTACH_WIDGET,
470 XmNbottomWidget, outBox, NULL);
471 XmStringFree(s1);
473 inpBox = XtVaCreateManagedWidget("inpBox", xmRowColumnWidgetClass, form,
474 XmNpacking, XmPACK_TIGHT,
475 XmNorientation, XmHORIZONTAL,
476 XmNradioBehavior, True,
477 XmNradioAlwaysOne, True,
478 XmNleftAttachment, XmATTACH_POSITION,
479 XmNleftPosition, LIST_RIGHT + 2,
480 XmNrightAttachment, XmATTACH_POSITION,
481 XmNrightPosition, RIGHT_MARGIN_POS,
482 XmNbottomAttachment, XmATTACH_WIDGET,
483 XmNbottomWidget, outLabel, NULL);
484 ucd->selInpBtn = XtVaCreateManagedWidget("selInpBtn", xmToggleButtonWidgetClass,
485 inpBox,
486 XmNlabelString, s1=MKSTRING("selection"),
487 XmNmnemonic, 's',
488 XmNalignment, XmALIGNMENT_BEGINNING,
489 XmNmarginHeight, 0,
490 XmNset, True, NULL);
491 XmStringFree(s1);
492 ucd->winInpBtn = XtVaCreateManagedWidget("winInpBtn",
493 xmToggleButtonWidgetClass, inpBox,
494 XmNlabelString, s1=MKSTRING("window"),
495 XmNmnemonic, 'w',
496 XmNalignment, XmALIGNMENT_BEGINNING,
497 XmNmarginHeight, 0,
498 XmNset, False, NULL);
499 XmStringFree(s1);
500 ucd->eitherInpBtn = XtVaCreateManagedWidget("eitherInpBtn",
501 xmToggleButtonWidgetClass, inpBox,
502 XmNlabelString, s1=MKSTRING("either"),
503 XmNmnemonic, 't',
504 XmNalignment, XmALIGNMENT_BEGINNING,
505 XmNmarginHeight, 0,
506 XmNset, False, NULL);
507 XmStringFree(s1);
508 ucd->noInpBtn = XtVaCreateManagedWidget("noInpBtn",
509 xmToggleButtonWidgetClass, inpBox,
510 XmNlabelString, s1=MKSTRING("none"),
511 XmNmnemonic, 'o',
512 XmNalignment, XmALIGNMENT_BEGINNING,
513 XmNmarginHeight, 0,
514 XmNset, False, NULL);
515 XmStringFree(s1);
516 inpLabel = XtVaCreateManagedWidget("inpLabel", xmLabelGadgetClass, form,
517 XmNlabelString, s1=MKSTRING("Command Input:"),
518 XmNalignment, XmALIGNMENT_BEGINNING,
519 XmNmarginTop, 5,
520 XmNleftAttachment, XmATTACH_POSITION,
521 XmNleftPosition, LIST_RIGHT,
522 XmNrightAttachment, XmATTACH_POSITION,
523 XmNrightPosition, RIGHT_MARGIN_POS,
524 XmNbottomAttachment, XmATTACH_WIDGET,
525 XmNbottomWidget, inpBox, NULL);
526 XmStringFree(s1);
528 ucd->mneTextW = XtVaCreateManagedWidget("mne", xmTextWidgetClass, form,
529 XmNcolumns, 1,
530 XmNmaxLength, 1,
531 XmNleftAttachment, XmATTACH_POSITION,
532 XmNleftPosition, RIGHT_MARGIN_POS-10,
533 XmNrightAttachment, XmATTACH_POSITION,
534 XmNrightPosition, RIGHT_MARGIN_POS,
535 XmNbottomAttachment, XmATTACH_WIDGET,
536 XmNbottomWidget, inpLabel, NULL);
537 RemapDeleteKey(ucd->mneTextW);
539 ucd->accTextW = XtVaCreateManagedWidget("acc", xmTextWidgetClass, form,
540 XmNcolumns, 12,
541 XmNmaxLength, MAX_ACCEL_LEN-1,
542 XmNcursorPositionVisible, False,
543 XmNleftAttachment, XmATTACH_POSITION,
544 XmNleftPosition, LIST_RIGHT,
545 XmNrightAttachment, XmATTACH_POSITION,
546 XmNrightPosition, RIGHT_MARGIN_POS-15,
547 XmNbottomAttachment, XmATTACH_WIDGET,
548 XmNbottomWidget, inpLabel, NULL);
549 XtAddEventHandler(ucd->accTextW, KeyPressMask, False,
550 (XtEventHandler)accKeyCB, ucd);
551 XtAddCallback(ucd->accTextW, XmNfocusCallback, accFocusCB, ucd);
552 XtAddCallback(ucd->accTextW, XmNlosingFocusCallback, accLoseFocusCB, ucd);
553 accLabel = XtVaCreateManagedWidget("accLabel", xmLabelGadgetClass, form,
554 XmNlabelString, s1=MKSTRING("Accelerator"),
555 XmNmnemonic, 'l',
556 XmNuserData, ucd->accTextW,
557 XmNalignment, XmALIGNMENT_BEGINNING,
558 XmNmarginTop, 5,
559 XmNleftAttachment, XmATTACH_POSITION,
560 XmNleftPosition, LIST_RIGHT,
561 XmNrightAttachment, XmATTACH_POSITION,
562 XmNrightPosition, LIST_RIGHT + 24,
563 XmNbottomAttachment, XmATTACH_WIDGET,
564 XmNbottomWidget, ucd->mneTextW, NULL);
565 XmStringFree(s1);
567 XtVaCreateManagedWidget("mneLabel", xmLabelGadgetClass, form,
568 XmNlabelString, s1=MKSTRING("Mnemonic"),
569 XmNmnemonic, 'i',
570 XmNuserData, ucd->mneTextW,
571 XmNalignment, XmALIGNMENT_END,
572 XmNmarginTop, 5,
573 XmNleftAttachment, XmATTACH_POSITION,
574 XmNleftPosition, LIST_RIGHT + 24,
575 XmNrightAttachment, XmATTACH_POSITION,
576 XmNrightPosition, RIGHT_MARGIN_POS,
577 XmNbottomAttachment, XmATTACH_WIDGET,
578 XmNbottomWidget, ucd->mneTextW, NULL);
579 XmStringFree(s1);
581 ucd->nameTextW = XtVaCreateManagedWidget("name", xmTextWidgetClass, form,
582 XmNleftAttachment, XmATTACH_POSITION,
583 XmNleftPosition, LIST_RIGHT,
584 XmNrightAttachment, XmATTACH_POSITION,
585 XmNrightPosition, RIGHT_MARGIN_POS,
586 XmNbottomAttachment, XmATTACH_WIDGET,
587 XmNbottomWidget, accLabel, NULL);
588 RemapDeleteKey(ucd->nameTextW);
590 nameLabel = XtVaCreateManagedWidget("nameLabel", xmLabelGadgetClass, form,
591 XmNlabelString, s1=MKSTRING("Menu Entry"),
592 XmNmnemonic, 'y',
593 XmNuserData, ucd->nameTextW,
594 XmNalignment, XmALIGNMENT_BEGINNING,
595 XmNmarginTop, 5,
596 XmNleftAttachment, XmATTACH_POSITION,
597 XmNleftPosition, LIST_RIGHT,
598 XmNbottomAttachment, XmATTACH_WIDGET,
599 XmNbottomWidget, ucd->nameTextW, NULL);
600 XmStringFree(s1);
602 XtVaCreateManagedWidget("nameNotes", xmLabelGadgetClass, form,
603 XmNlabelString, s1=MKSTRING("(> for sub-menu, @ language mode)"),
604 XmNalignment, XmALIGNMENT_END,
605 XmNmarginTop, 5,
606 XmNleftAttachment, XmATTACH_WIDGET,
607 XmNleftWidget, nameLabel,
608 XmNrightAttachment, XmATTACH_POSITION,
609 XmNrightPosition, RIGHT_MARGIN_POS,
610 XmNbottomAttachment, XmATTACH_WIDGET,
611 XmNbottomWidget, ucd->nameTextW, NULL);
612 XmStringFree(s1);
614 XtVaCreateManagedWidget("topLabel", xmLabelGadgetClass, form,
615 XmNlabelString, s1=MKSTRING(
616 "Select a shell menu item from the list at left.\n\
617 Select \"New\" to add a new command to the menu."),
618 XmNtopAttachment, XmATTACH_POSITION,
619 XmNtopPosition, 2,
620 XmNleftAttachment, XmATTACH_POSITION,
621 XmNleftPosition, LIST_RIGHT,
622 XmNrightAttachment, XmATTACH_POSITION,
623 XmNrightPosition, RIGHT_MARGIN_POS,
624 XmNbottomAttachment, XmATTACH_WIDGET,
625 XmNbottomWidget, nameLabel, NULL);
626 XmStringFree(s1);
628 cmdLabel = XtVaCreateManagedWidget("cmdLabel", xmLabelGadgetClass, form,
629 XmNlabelString, s1=MKSTRING("Shell Command to Execute"),
630 XmNmnemonic, 'x',
631 XmNalignment, XmALIGNMENT_BEGINNING,
632 XmNmarginTop, 5,
633 XmNtopAttachment, XmATTACH_POSITION,
634 XmNtopPosition, SHELL_CMD_TOP,
635 XmNleftAttachment, XmATTACH_POSITION,
636 XmNleftPosition, LEFT_MARGIN_POS, NULL);
637 XmStringFree(s1);
638 XtVaCreateManagedWidget("cmdLabel", xmLabelGadgetClass, form,
639 XmNlabelString, s1=MKSTRING("(% expands to current filename, # to line number)"),
640 XmNalignment, XmALIGNMENT_END,
641 XmNmarginTop, 5,
642 XmNtopAttachment, XmATTACH_POSITION,
643 XmNtopPosition, SHELL_CMD_TOP,
644 XmNleftAttachment, XmATTACH_WIDGET,
645 XmNleftWidget, cmdLabel,
646 XmNrightAttachment, XmATTACH_POSITION,
647 XmNrightPosition, RIGHT_MARGIN_POS, NULL);
648 XmStringFree(s1);
650 okBtn = XtVaCreateManagedWidget("ok",xmPushButtonWidgetClass,form,
651 XmNlabelString, s1=MKSTRING("OK"),
652 XmNleftAttachment, XmATTACH_POSITION,
653 XmNleftPosition, 13,
654 XmNrightAttachment, XmATTACH_POSITION,
655 XmNrightPosition, 29,
656 XmNbottomAttachment, XmATTACH_POSITION,
657 XmNbottomPosition, 99, NULL);
658 XtAddCallback(okBtn, XmNactivateCallback, okCB, ucd);
659 XmStringFree(s1);
661 applyBtn = XtVaCreateManagedWidget("apply",xmPushButtonWidgetClass,form,
662 XmNlabelString, s1=MKSTRING("Apply"),
663 XmNmnemonic, 'A',
664 XmNleftAttachment, XmATTACH_POSITION,
665 XmNleftPosition, 42,
666 XmNrightAttachment, XmATTACH_POSITION,
667 XmNrightPosition, 58,
668 XmNbottomAttachment, XmATTACH_POSITION,
669 XmNbottomPosition, 99, NULL);
670 XtAddCallback(applyBtn, XmNactivateCallback, applyCB, ucd);
671 XmStringFree(s1);
673 dismissBtn = XtVaCreateManagedWidget("dismiss",xmPushButtonWidgetClass,form,
674 XmNlabelString, s1=MKSTRING("Dismiss"),
675 XmNleftAttachment, XmATTACH_POSITION,
676 XmNleftPosition, 71,
677 XmNrightAttachment, XmATTACH_POSITION,
678 XmNrightPosition, 87,
679 XmNbottomAttachment, XmATTACH_POSITION,
680 XmNbottomPosition, 99, NULL);
681 XtAddCallback(dismissBtn, XmNactivateCallback, dismissCB, ucd);
682 XmStringFree(s1);
684 ac = 0;
685 XtSetArg(args[ac], XmNeditMode, XmMULTI_LINE_EDIT); ac++;
686 XtSetArg(args[ac], XmNscrollHorizontal, False); ac++;
687 XtSetArg(args[ac], XmNwordWrap, True); ac++;
688 XtSetArg(args[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++;
689 XtSetArg(args[ac], XmNtopWidget, cmdLabel); ac++;
690 XtSetArg(args[ac], XmNleftAttachment, XmATTACH_POSITION); ac++;
691 XtSetArg(args[ac], XmNleftPosition, LEFT_MARGIN_POS); ac++;
692 XtSetArg(args[ac], XmNrightAttachment, XmATTACH_POSITION); ac++;
693 XtSetArg(args[ac], XmNrightPosition, RIGHT_MARGIN_POS); ac++;
694 XtSetArg(args[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
695 XtSetArg(args[ac], XmNbottomWidget, okBtn); ac++;
696 XtSetArg(args[ac], XmNbottomOffset, 5); ac++;
697 ucd->cmdTextW = XmCreateScrolledText(form, "name", args, ac);
698 AddMouseWheelSupport(ucd->cmdTextW);
699 XtManageChild(ucd->cmdTextW);
700 MakeSingleLineTextW(ucd->cmdTextW);
701 RemapDeleteKey(ucd->cmdTextW);
702 XtVaSetValues(cmdLabel, XmNuserData, ucd->cmdTextW, NULL); /* for mnemonic */
704 /* Disable text input for the accelerator key widget, let the
705 event handler manage it instead */
706 disableTextW(ucd->accTextW);
708 /* initializs the dialog fields to match "New" list item */
709 updateDialogFields(NULL, ucd);
711 /* Set initial default button */
712 XtVaSetValues(form, XmNdefaultButton, okBtn, NULL);
713 XtVaSetValues(form, XmNcancelButton, dismissBtn, NULL);
715 /* Handle mnemonic selection of buttons and focus to dialog */
716 AddDialogMnemonicHandler(form, FALSE);
718 /* realize all of the widgets in the new window */
719 RealizeWithoutForcingPosition(ucd->dlogShell);
723 ** Present a dialogs for editing the user specified commands in the Macro
724 ** and background menus
726 void EditMacroMenu(WindowInfo *window)
728 editMacroOrBGMenu(window, MACRO_CMDS);
730 void EditBGMenu(WindowInfo *window)
732 editMacroOrBGMenu(window, BG_MENU_CMDS);
735 static void editMacroOrBGMenu(WindowInfo *window, int dialogType)
737 Widget form, accLabel, pasteReplayBtn;
738 Widget nameLabel, cmdLabel, okBtn, applyBtn, dismissBtn;
739 userCmdDialog *ucd;
740 char *title;
741 XmString s1;
742 int ac, i;
743 Arg args[20];
745 /* if the dialog is already displayed, just pop it to the top and return */
746 if (dialogType == MACRO_CMDS && MacroCmdDialog != NULL) {
747 RaiseShellWindow(MacroCmdDialog);
748 return;
750 if (dialogType == BG_MENU_CMDS && BGMenuCmdDialog != NULL) {
751 RaiseShellWindow(BGMenuCmdDialog);
752 return;
755 /* Create a structure for keeping track of dialog state */
756 ucd = (userCmdDialog *)XtMalloc(sizeof(userCmdDialog));
757 ucd->window = window;
759 /* Set the dialog to operate on the Macro menu */
760 ucd->menuItemsList = (menuItemRec **)XtMalloc(sizeof(menuItemRec **) *
761 MAX_ITEMS_PER_MENU);
762 if (dialogType == MACRO_CMDS) {
763 for (i=0; i<NMacroMenuItems; i++)
764 ucd->menuItemsList[i] = copyMenuItemRec(MacroMenuItems[i]);
765 ucd->nMenuItems = NMacroMenuItems;
766 } else { /* BG_MENU_CMDS */
767 for (i=0; i<NBGMenuItems; i++)
768 ucd->menuItemsList[i] = copyMenuItemRec(BGMenuItems[i]);
769 ucd->nMenuItems = NBGMenuItems;
771 ucd->dialogType = dialogType;
773 title = dialogType == MACRO_CMDS ? "Macro Commands" :
774 "Window Background Menu";
775 ac = 0;
776 XtSetArg(args[ac], XmNdeleteResponse, XmDO_NOTHING); ac++;
777 XtSetArg(args[ac], XmNiconName, title); ac++;
778 XtSetArg(args[ac], XmNtitle, title); ac++;
779 ucd->dlogShell = CreateWidget(TheAppShell, "macros",
780 topLevelShellWidgetClass, args, ac);
781 AddSmallIcon(ucd->dlogShell);
782 form = XtVaCreateManagedWidget("editMacroCommands", xmFormWidgetClass,
783 ucd->dlogShell, XmNautoUnmanage, False,
784 XmNresizePolicy, XmRESIZE_NONE, NULL);
785 XtAddCallback(form, XmNdestroyCallback, destroyCB, ucd);
786 AddMotifCloseCallback(ucd->dlogShell, dismissCB, ucd);
788 ac = 0;
789 XtSetArg(args[ac], XmNtopAttachment, XmATTACH_POSITION); ac++;
790 XtSetArg(args[ac], XmNtopPosition, 2); ac++;
791 XtSetArg(args[ac], XmNleftAttachment, XmATTACH_POSITION); ac++;
792 XtSetArg(args[ac], XmNleftPosition, LEFT_MARGIN_POS); ac++;
793 XtSetArg(args[ac], XmNrightAttachment, XmATTACH_POSITION); ac++;
794 XtSetArg(args[ac], XmNrightPosition, LIST_RIGHT-1); ac++;
795 XtSetArg(args[ac], XmNbottomAttachment, XmATTACH_POSITION); ac++;
796 XtSetArg(args[ac], XmNbottomPosition, MACRO_CMD_TOP); ac++;
797 ucd->managedList = CreateManagedList(form, "list", args, ac,
798 (void **)ucd->menuItemsList, &ucd->nMenuItems, MAX_ITEMS_PER_MENU, 20,
799 getDialogDataCB, ucd, setDialogDataCB, ucd, freeItemCB);
801 ucd->selInpBtn = XtVaCreateManagedWidget("selInpBtn",
802 xmToggleButtonWidgetClass, form,
803 XmNlabelString, s1=MKSTRING("Requires Selection"),
804 XmNmnemonic, 'R',
805 XmNalignment, XmALIGNMENT_BEGINNING,
806 XmNmarginHeight, 0,
807 XmNset, False,
808 XmNleftAttachment, XmATTACH_POSITION,
809 XmNleftPosition, LIST_RIGHT,
810 XmNbottomAttachment, XmATTACH_POSITION,
811 XmNbottomPosition, MACRO_CMD_TOP, NULL);
812 XmStringFree(s1);
814 ucd->mneTextW = XtVaCreateManagedWidget("mne", xmTextWidgetClass, form,
815 XmNcolumns, 1,
816 XmNmaxLength, 1,
817 XmNleftAttachment, XmATTACH_POSITION,
818 XmNleftPosition, RIGHT_MARGIN_POS-21-5,
819 XmNrightAttachment, XmATTACH_POSITION,
820 XmNrightPosition, RIGHT_MARGIN_POS-21,
821 XmNbottomAttachment, XmATTACH_WIDGET,
822 XmNbottomWidget, ucd->selInpBtn,
823 XmNbottomOffset, 5, NULL);
824 RemapDeleteKey(ucd->mneTextW);
826 ucd->accTextW = XtVaCreateManagedWidget("acc", xmTextWidgetClass, form,
827 XmNcolumns, 12,
828 XmNmaxLength, MAX_ACCEL_LEN-1,
829 XmNcursorPositionVisible, False,
830 XmNleftAttachment, XmATTACH_POSITION,
831 XmNleftPosition, LIST_RIGHT,
832 XmNrightAttachment, XmATTACH_POSITION,
833 XmNrightPosition, RIGHT_MARGIN_POS-20-10,
834 XmNbottomAttachment, XmATTACH_WIDGET,
835 XmNbottomWidget, ucd->selInpBtn,
836 XmNbottomOffset, 5, NULL);
837 XtAddEventHandler(ucd->accTextW, KeyPressMask, False,
838 (XtEventHandler)accKeyCB, ucd);
839 XtAddCallback(ucd->accTextW, XmNfocusCallback, accFocusCB, ucd);
840 XtAddCallback(ucd->accTextW, XmNlosingFocusCallback, accLoseFocusCB, ucd);
842 accLabel = XtVaCreateManagedWidget("accLabel", xmLabelGadgetClass, form,
843 XmNlabelString, s1=MKSTRING("Accelerator"),
844 XmNmnemonic, 'l',
845 XmNuserData, ucd->accTextW,
846 XmNalignment, XmALIGNMENT_BEGINNING,
847 XmNmarginTop, 5,
848 XmNleftAttachment, XmATTACH_POSITION,
849 XmNleftPosition, LIST_RIGHT,
850 XmNrightAttachment, XmATTACH_POSITION,
851 XmNrightPosition, LIST_RIGHT + 22,
852 XmNbottomAttachment, XmATTACH_WIDGET,
853 XmNbottomWidget, ucd->mneTextW, NULL);
854 XmStringFree(s1);
856 XtVaCreateManagedWidget("mneLabel", xmLabelGadgetClass, form,
857 XmNlabelString, s1=MKSTRING("Mnemonic"),
858 XmNmnemonic, 'i',
859 XmNuserData, ucd->mneTextW,
860 XmNalignment, XmALIGNMENT_END,
861 XmNmarginTop, 5,
862 XmNleftAttachment, XmATTACH_POSITION,
863 XmNleftPosition, LIST_RIGHT + 22,
864 XmNrightAttachment, XmATTACH_POSITION,
865 XmNrightPosition, RIGHT_MARGIN_POS-21,
866 XmNbottomAttachment, XmATTACH_WIDGET,
867 XmNbottomWidget, ucd->mneTextW, NULL);
868 XmStringFree(s1);
870 pasteReplayBtn = XtVaCreateManagedWidget("pasteReplay",
871 xmPushButtonWidgetClass, form,
872 XmNlabelString, s1=MKSTRING("Paste Learn/\nReplay Macro"),
873 XmNmnemonic, 'P',
874 XmNsensitive, GetReplayMacro() != NULL,
875 XmNleftAttachment, XmATTACH_POSITION,
876 XmNleftPosition, RIGHT_MARGIN_POS-20,
877 XmNrightAttachment, XmATTACH_POSITION,
878 XmNrightPosition, RIGHT_MARGIN_POS,
879 XmNbottomAttachment, XmATTACH_POSITION,
880 XmNbottomPosition, MACRO_CMD_TOP, NULL);
881 XtAddCallback(pasteReplayBtn, XmNactivateCallback,
882 pasteReplayCB, ucd);
883 XmStringFree(s1);
885 ucd->nameTextW = XtVaCreateManagedWidget("name", xmTextWidgetClass, form,
886 XmNleftAttachment, XmATTACH_POSITION,
887 XmNleftPosition, LIST_RIGHT,
888 XmNrightAttachment, XmATTACH_POSITION,
889 XmNrightPosition, RIGHT_MARGIN_POS,
890 XmNbottomAttachment, XmATTACH_WIDGET,
891 XmNbottomWidget, accLabel, NULL);
892 RemapDeleteKey(ucd->nameTextW);
894 nameLabel = XtVaCreateManagedWidget("nameLabel", xmLabelGadgetClass, form,
895 XmNlabelString, s1=MKSTRING("Menu Entry"),
896 XmNmnemonic, 'y',
897 XmNuserData, ucd->nameTextW,
898 XmNalignment, XmALIGNMENT_BEGINNING,
899 XmNmarginTop, 5,
900 XmNleftAttachment, XmATTACH_POSITION,
901 XmNleftPosition, LIST_RIGHT,
902 XmNbottomAttachment, XmATTACH_WIDGET,
903 XmNbottomWidget, ucd->nameTextW, NULL);
904 XmStringFree(s1);
906 XtVaCreateManagedWidget("nameNotes", xmLabelGadgetClass, form,
907 XmNlabelString, s1=MKSTRING("(> for sub-menu, @ language mode)"),
908 XmNalignment, XmALIGNMENT_END,
909 XmNmarginTop, 5,
910 XmNleftAttachment, XmATTACH_WIDGET,
911 XmNleftWidget, nameLabel,
912 XmNrightAttachment, XmATTACH_POSITION,
913 XmNrightPosition, RIGHT_MARGIN_POS,
914 XmNbottomAttachment, XmATTACH_WIDGET,
915 XmNbottomWidget, ucd->nameTextW, NULL);
916 XmStringFree(s1);
918 XtVaCreateManagedWidget("topLabel", xmLabelGadgetClass, form,
919 XmNlabelString, s1=MKSTRING(
920 "Select a macro menu item from the list at left.\n\
921 Select \"New\" to add a new command to the menu."),
922 XmNtopAttachment, XmATTACH_POSITION,
923 XmNtopPosition, 2,
924 XmNleftAttachment, XmATTACH_POSITION,
925 XmNleftPosition, LIST_RIGHT,
926 XmNrightAttachment, XmATTACH_POSITION,
927 XmNrightPosition, RIGHT_MARGIN_POS,
928 XmNbottomAttachment, XmATTACH_WIDGET,
929 XmNbottomWidget, nameLabel, NULL);
930 XmStringFree(s1);
932 cmdLabel = XtVaCreateManagedWidget("cmdLabel", xmLabelGadgetClass, form,
933 XmNlabelString, s1=MKSTRING("Macro Command to Execute"),
934 XmNmnemonic, 'x',
935 XmNalignment, XmALIGNMENT_BEGINNING,
936 XmNmarginTop, 5,
937 XmNtopAttachment, XmATTACH_POSITION,
938 XmNtopPosition, MACRO_CMD_TOP,
939 XmNleftAttachment, XmATTACH_POSITION,
940 XmNleftPosition, LEFT_MARGIN_POS, NULL);
941 XmStringFree(s1);
943 okBtn = XtVaCreateManagedWidget("ok",xmPushButtonWidgetClass,form,
944 XmNlabelString, s1=MKSTRING("OK"),
945 XmNleftAttachment, XmATTACH_POSITION,
946 XmNleftPosition, 8,
947 XmNrightAttachment, XmATTACH_POSITION,
948 XmNrightPosition, 23,
949 XmNbottomAttachment, XmATTACH_POSITION,
950 XmNbottomPosition, 99, NULL);
951 XtAddCallback(okBtn, XmNactivateCallback, okCB, ucd);
952 XmStringFree(s1);
954 applyBtn = XtVaCreateManagedWidget("apply",xmPushButtonWidgetClass,form,
955 XmNlabelString, s1=MKSTRING("Apply"),
956 XmNmnemonic, 'A',
957 XmNleftAttachment, XmATTACH_POSITION,
958 XmNleftPosition, 31,
959 XmNrightAttachment, XmATTACH_POSITION,
960 XmNrightPosition, 46,
961 XmNbottomAttachment, XmATTACH_POSITION,
962 XmNbottomPosition, 99, NULL);
963 XtAddCallback(applyBtn, XmNactivateCallback, applyCB, ucd);
964 XmStringFree(s1);
966 applyBtn = XtVaCreateManagedWidget("check",xmPushButtonWidgetClass,form,
967 XmNlabelString, s1=MKSTRING("Check"),
968 XmNmnemonic, 'C',
969 XmNleftAttachment, XmATTACH_POSITION,
970 XmNleftPosition, 54,
971 XmNrightAttachment, XmATTACH_POSITION,
972 XmNrightPosition, 69,
973 XmNbottomAttachment, XmATTACH_POSITION,
974 XmNbottomPosition, 99, NULL);
975 XtAddCallback(applyBtn, XmNactivateCallback, checkCB, ucd);
976 XmStringFree(s1);
978 dismissBtn = XtVaCreateManagedWidget("dismiss",xmPushButtonWidgetClass,form,
979 XmNlabelString, s1=MKSTRING("Dismiss"),
980 XmNleftAttachment, XmATTACH_POSITION,
981 XmNleftPosition, 77,
982 XmNrightAttachment, XmATTACH_POSITION,
983 XmNrightPosition, 92,
984 XmNbottomAttachment, XmATTACH_POSITION,
985 XmNbottomPosition, 99, NULL);
986 XtAddCallback(dismissBtn, XmNactivateCallback, dismissCB, ucd);
987 XmStringFree(s1);
989 ac = 0;
990 XtSetArg(args[ac], XmNeditMode, XmMULTI_LINE_EDIT); ac++;
991 XtSetArg(args[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++;
992 XtSetArg(args[ac], XmNtopWidget, cmdLabel); ac++;
993 XtSetArg(args[ac], XmNleftAttachment, XmATTACH_POSITION); ac++;
994 XtSetArg(args[ac], XmNleftPosition, LEFT_MARGIN_POS); ac++;
995 XtSetArg(args[ac], XmNrightAttachment, XmATTACH_POSITION); ac++;
996 XtSetArg(args[ac], XmNrightPosition, RIGHT_MARGIN_POS); ac++;
997 XtSetArg(args[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
998 XtSetArg(args[ac], XmNbottomWidget, okBtn); ac++;
999 XtSetArg(args[ac], XmNbottomOffset, 5); ac++;
1000 ucd->cmdTextW = XmCreateScrolledText(form, "name", args, ac);
1001 AddMouseWheelSupport(ucd->cmdTextW);
1002 XtManageChild(ucd->cmdTextW);
1003 RemapDeleteKey(ucd->cmdTextW);
1004 XtVaSetValues(cmdLabel, XmNuserData, ucd->cmdTextW, NULL); /* for mnemonic */
1006 /* Disable text input for the accelerator key widget, let the
1007 event handler manage it instead */
1008 disableTextW(ucd->accTextW);
1010 /* initializs the dialog fields to match "New" list item */
1011 updateDialogFields(NULL, ucd);
1013 /* Set initial default button */
1014 XtVaSetValues(form, XmNdefaultButton, okBtn, NULL);
1015 XtVaSetValues(form, XmNcancelButton, dismissBtn, NULL);
1017 /* Handle mnemonic selection of buttons and focus to dialog */
1018 AddDialogMnemonicHandler(form, FALSE);
1020 /* Make widgets for top level shell and paste-replay buttons available
1021 to other functions */
1022 if (dialogType == MACRO_CMDS) {
1023 MacroCmdDialog = ucd->dlogShell;
1024 MacroPasteReplayBtn = pasteReplayBtn;
1025 } else {
1026 BGMenuCmdDialog = ucd->dlogShell;
1027 BGMenuPasteReplayBtn = pasteReplayBtn;
1030 /* Realize all of the widgets in the new dialog */
1031 RealizeWithoutForcingPosition(ucd->dlogShell);
1035 ** Update the Shell, Macro, and Window Background menus of window
1036 ** "window" from the currently loaded command descriptions.
1038 void UpdateUserMenus(WindowInfo *window)
1040 if (!IsTopDocument(window))
1041 return;
1043 /* update user menus, which are shared over all documents, only
1044 if language mode was changed */
1045 if (window->userMenuCache->umcLanguageMode != window->languageMode) {
1046 #ifndef VMS
1047 updateMenu(window, SHELL_CMDS);
1048 #endif
1049 updateMenu(window, MACRO_CMDS);
1051 /* remember language mode assigned to shared user menus */
1052 window->userMenuCache->umcLanguageMode = window->languageMode;
1055 /* update background menu, which is owned by a single document, only
1056 if language mode was changed */
1057 if (window->userBGMenuCache.ubmcLanguageMode != window->languageMode) {
1058 updateMenu(window, BG_MENU_CMDS);
1060 /* remember language mode assigned to background menu */
1061 window->userBGMenuCache.ubmcLanguageMode = window->languageMode;
1066 ** Dim/undim buttons for pasting replay macros into macro and bg menu dialogs
1068 void DimPasteReplayBtns(int sensitive)
1070 if (MacroCmdDialog != NULL)
1071 XtSetSensitive(MacroPasteReplayBtn, sensitive);
1072 if (BGMenuCmdDialog != NULL)
1073 XtSetSensitive(BGMenuPasteReplayBtn, sensitive);
1077 ** Dim/undim user programmable menu items which depend on there being
1078 ** a selection in their associated window.
1080 void DimSelectionDepUserMenuItems(WindowInfo *window, int sensitive)
1082 if (!IsTopDocument(window))
1083 return;
1085 #ifndef VMS
1086 dimSelDepItemsInMenu(window->shellMenuPane, ShellMenuItems,
1087 NShellMenuItems, sensitive);
1088 #endif /*VMS*/
1089 dimSelDepItemsInMenu(window->macroMenuPane, MacroMenuItems,
1090 NMacroMenuItems, sensitive);
1091 dimSelDepItemsInMenu(window->bgMenuPane, BGMenuItems,
1092 NBGMenuItems, sensitive);
1095 static void dimSelDepItemsInMenu(Widget menuPane, menuItemRec **menuList,
1096 int nMenuItems, int sensitive)
1098 WidgetList items;
1099 Widget subMenu;
1100 XtPointer userData;
1101 int n, index;
1102 Cardinal nItems;
1104 XtVaGetValues(menuPane, XmNchildren, &items, XmNnumChildren, &nItems, NULL);
1105 for (n=0; n<(int)nItems; n++) {
1106 XtVaGetValues(items[n], XmNuserData, &userData, NULL);
1107 if (userData != (XtPointer)PERMANENT_MENU_ITEM) {
1108 if (XtClass(items[n]) == xmCascadeButtonWidgetClass) {
1109 XtVaGetValues(items[n], XmNsubMenuId, &subMenu, NULL);
1110 dimSelDepItemsInMenu(subMenu, menuList, nMenuItems, sensitive);
1111 } else {
1112 index = (int)userData - 10;
1113 if (index <0 || index >= nMenuItems)
1114 return;
1115 if (menuList[index]->input == FROM_SELECTION)
1116 XtSetSensitive(items[n], sensitive);
1123 ** Harmless kludge for making undo/redo menu items in background menu properly
1124 ** sensitive (even though they're programmable) along with real undo item
1125 ** in the Edit menu
1127 void SetBGMenuUndoSensitivity(WindowInfo *window, int sensitive)
1129 if (window->bgMenuUndoItem != NULL)
1130 SetSensitive(window, window->bgMenuUndoItem, sensitive);
1132 void SetBGMenuRedoSensitivity(WindowInfo *window, int sensitive)
1134 if (window->bgMenuRedoItem != NULL)
1135 SetSensitive(window, window->bgMenuRedoItem, sensitive);
1139 ** Generate a text string for the preferences file describing the contents
1140 ** of the shell cmd list. This string is not exactly of the form that it
1141 ** can be read by LoadShellCmdsString, rather, it is what needs to be written
1142 ** to a resource file such that it will read back in that form.
1144 char *WriteShellCmdsString(void)
1146 return writeMenuItemString(ShellMenuItems, NShellMenuItems,
1147 SHELL_CMDS);
1151 ** Generate a text string for the preferences file describing the contents of
1152 ** the macro menu and background menu commands lists. These strings are not
1153 ** exactly of the form that it can be read by LoadMacroCmdsString, rather, it
1154 ** is what needs to be written to a resource file such that it will read back
1155 ** in that form.
1157 char *WriteMacroCmdsString(void)
1159 return writeMenuItemString(MacroMenuItems, NMacroMenuItems, MACRO_CMDS);
1162 char *WriteBGMenuCmdsString(void)
1164 return writeMenuItemString(BGMenuItems, NBGMenuItems, BG_MENU_CMDS);
1168 ** Read a string representing shell command menu items and add them to the
1169 ** internal list used for constructing shell menus
1171 int LoadShellCmdsString(char *inString)
1173 return loadMenuItemString(inString, ShellMenuItems, &NShellMenuItems,
1174 SHELL_CMDS);
1178 ** Read strings representing macro menu or background menu command menu items
1179 ** and add them to the internal lists used for constructing menus
1181 int LoadMacroCmdsString(char *inString)
1183 return loadMenuItemString(inString, MacroMenuItems, &NMacroMenuItems,
1184 MACRO_CMDS);
1187 int LoadBGMenuCmdsString(char *inString)
1189 return loadMenuItemString(inString, BGMenuItems, &NBGMenuItems,
1190 BG_MENU_CMDS);
1194 ** Cache user menus:
1195 ** Setup user menu info after read of macro, shell and background menu
1196 ** string (reason: language mode info from preference string is read *after*
1197 ** user menu preference string was read).
1199 void SetupUserMenuInfo()
1201 parseMenuItemList(ShellMenuItems, NShellMenuItems, ShellMenuInfo, &ShellSubMenus);
1202 parseMenuItemList(MacroMenuItems, NMacroMenuItems, MacroMenuInfo, &MacroSubMenus);
1203 parseMenuItemList(BGMenuItems , NBGMenuItems , BGMenuInfo , &BGSubMenus);
1207 ** Search through the shell menu and execute the first command with menu item
1208 ** name "itemName". Returns True on successs and False on failure.
1210 #ifndef VMS
1211 int DoNamedShellMenuCmd(WindowInfo *window, const char *itemName, int fromMacro)
1213 int i;
1215 for (i=0; i<NShellMenuItems; i++) {
1216 if (!strcmp(ShellMenuItems[i]->name, itemName)) {
1217 if (ShellMenuItems[i]->output == TO_SAME_WINDOW &&
1218 CheckReadOnly(window))
1219 return False;
1220 DoShellMenuCmd(window, ShellMenuItems[i]->cmd,
1221 ShellMenuItems[i]->input, ShellMenuItems[i]->output,
1222 ShellMenuItems[i]->repInput, ShellMenuItems[i]->saveFirst,
1223 ShellMenuItems[i]->loadAfter, fromMacro);
1224 return True;
1227 return False;
1229 #endif /*VMS*/
1232 ** Search through the Macro or background menu and execute the first command
1233 ** with menu item name "itemName". Returns True on successs and False on
1234 ** failure.
1236 int DoNamedMacroMenuCmd(WindowInfo *window, const char *itemName)
1238 int i;
1240 for (i=0; i<NMacroMenuItems; i++) {
1241 if (!strcmp(MacroMenuItems[i]->name, itemName)) {
1242 DoMacro(window, MacroMenuItems[i]->cmd, "macro menu command");
1243 return True;
1246 return False;
1249 int DoNamedBGMenuCmd(WindowInfo *window, const char *itemName)
1251 int i;
1253 for (i=0; i<NBGMenuItems; i++) {
1254 if (!strcmp(BGMenuItems[i]->name, itemName)) {
1255 DoMacro(window, BGMenuItems[i]->cmd, "background menu macro");
1256 return True;
1259 return False;
1263 ** Cache user menus:
1264 ** Rebuild all of the Shell, Macro, Background menus of given editor window.
1266 void RebuildAllMenus(WindowInfo *window)
1268 rebuildMenu(window, SHELL_CMDS);
1269 rebuildMenu(window, MACRO_CMDS);
1270 rebuildMenu(window, BG_MENU_CMDS);
1274 ** Cache user menus:
1275 ** Rebuild either Shell, Macro or Background menus of all editor windows.
1277 static void rebuildMenuOfAllWindows(int menuType)
1279 WindowInfo *w;
1281 for (w=WindowList; w!=NULL; w=w->next)
1282 rebuildMenu(w, menuType);
1286 ** Rebuild either the Shell, Macro or Background menu of "window", depending
1287 ** on value of "menuType". Rebuild is realized by following main steps:
1288 ** - dismiss user (sub) menu tearoff.
1289 ** - delete all user defined menu widgets.
1290 ** - update user menu including (re)creation of menu widgets.
1292 static void rebuildMenu(WindowInfo *window, int menuType)
1294 selectedUserMenu menu;
1296 /* Background menu is always rebuild (exists once per document).
1297 Shell, macro (user) menu cache is rebuild only, if given window is
1298 currently displayed on top. */
1299 if (menuType != BG_MENU_CMDS && !IsTopDocument(window))
1300 return;
1302 /* Fetch the appropriate menu data */
1303 selectUserMenu(window, menuType, &menu);
1305 /* dismiss user menu tearoff, to workaround the quick
1306 but noticeable shrink-expand bug, most probably
1307 triggered by the rebuild of the user menus. In any
1308 case, the submenu tearoffs will later be dismissed
1309 too in order to prevent dangling tearoffs, so doing
1310 this also for the main user menu tearoffs shouldn't
1311 be so bad */
1312 if (!XmIsMenuShell(XtParent(menu.sumMenuPane)))
1313 _XmDismissTearOff(XtParent(menu.sumMenuPane), NULL, NULL);
1315 /* destroy all widgets related to menu pane */
1316 deleteMenuItems(menu.sumMenuPane);
1318 /* remove cached user menu info */
1319 freeUserMenuList(menu.sumMainMenuList);
1320 *menu.sumMenuCreated = False;
1322 /* re-create & cache user menu items */
1323 updateMenu(window, menuType);
1327 ** Fetch the appropriate menu info for given menu type
1329 static void selectUserMenu(WindowInfo *window, int menuType, selectedUserMenu *menu)
1331 if (menuType == SHELL_CMDS) {
1332 menu->sumMenuPane = window->shellMenuPane;
1333 menu->sumNbrOfListItems = NShellMenuItems;
1334 menu->sumItemList = ShellMenuItems;
1335 menu->sumInfoList = ShellMenuInfo;
1336 menu->sumSubMenus = &ShellSubMenus;
1337 menu->sumMainMenuList = &window->userMenuCache->umcShellMenuList;
1338 menu->sumMenuCreated = &window->userMenuCache->umcShellMenuCreated;
1339 } else if (menuType == MACRO_CMDS) {
1340 menu->sumMenuPane = window->macroMenuPane;
1341 menu->sumNbrOfListItems = NMacroMenuItems;
1342 menu->sumItemList = MacroMenuItems;
1343 menu->sumInfoList = MacroMenuInfo;
1344 menu->sumSubMenus = &MacroSubMenus;
1345 menu->sumMainMenuList = &window->userMenuCache->umcMacroMenuList;
1346 menu->sumMenuCreated = &window->userMenuCache->umcMacroMenuCreated;
1347 } else { /* BG_MENU_CMDS */
1348 menu->sumMenuPane = window->bgMenuPane;
1349 menu->sumNbrOfListItems = NBGMenuItems;
1350 menu->sumItemList = BGMenuItems;
1351 menu->sumInfoList = BGMenuInfo;
1352 menu->sumSubMenus = &BGSubMenus;
1353 menu->sumMainMenuList = &window->userBGMenuCache.ubmcMenuList;
1354 menu->sumMenuCreated = &window->userBGMenuCache.ubmcMenuCreated;
1356 menu->sumType = menuType;
1360 ** Updates either the Shell, Macro or Background menu of "window", depending
1361 ** on value of "menuType". Update is realized by following main steps:
1362 ** - set / reset "to be managed" flag of user menu info list items
1363 ** according to current selected language mode.
1364 ** - create *all* user menu items (widgets etc). related to given
1365 ** window & menu type, if not done before.
1366 ** - manage / unmanage user menu widgets according to "to be managed"
1367 ** indication of user menu info list items.
1369 static void updateMenu(WindowInfo *window, int menuType)
1371 selectedUserMenu menu;
1373 /* Fetch the appropriate menu data */
1374 selectUserMenu(window, menuType, &menu);
1376 /* Set / reset "to be managed" flag of all info list items */
1377 applyLangModeToUserMenuInfo(menu.sumInfoList, menu.sumNbrOfListItems,
1378 window->languageMode);
1380 /* create user menu items, if not done before */
1381 if (!*menu.sumMenuCreated)
1382 createMenuItems(window, &menu);
1384 /* manage user menu items depending on current language mode */
1385 manageUserMenu(&menu, window);
1387 if (menuType == BG_MENU_CMDS) {
1388 /* Set the proper sensitivity of items which may be dimmed */
1389 SetBGMenuUndoSensitivity(window, XtIsSensitive(window->undoItem));
1390 SetBGMenuRedoSensitivity(window, XtIsSensitive(window->redoItem));
1393 DimSelectionDepUserMenuItems(window, window->buffer->primary.selected);
1397 ** Manually adjust the dimension of the menuShell _before_
1398 ** re-managing the menu pane, to either expose hidden menu
1399 ** entries or remove empty space.
1401 static void manageTearOffMenu(Widget menuPane)
1403 Dimension width, height, border;
1405 /* somehow OM went into a long CPU cycling when we
1406 attempt to change the shell window dimension by
1407 setting the XmNwidth & XmNheight directly. Using
1408 XtResizeWidget() seem to fix it */
1409 XtVaGetValues(XtParent(menuPane), XmNborderWidth, &border, NULL);
1410 XtVaGetValues(menuPane, XmNwidth, &width, XmNheight, &height, NULL);
1411 XtResizeWidget(XtParent(menuPane), width, height, border);
1413 XtManageChild(menuPane);
1417 ** Cache user menus:
1418 ** Reset manage mode of user menu items in window cache.
1420 static void resetManageMode(UserMenuList *list)
1422 int i;
1423 UserMenuListElement *element;
1425 for (i=0; i<list->umlNbrItems; i ++) {
1426 element = list->umlItems[i];
1428 /* remember current manage mode before reset it to
1429 "unmanaged" */
1430 element->umlePrevManageMode = element->umleManageMode;
1431 element->umleManageMode = UMMM_UNMANAGE;
1433 /* recursively reset manage mode of sub-menus */
1434 if (element->umleSubMenuList != NULL)
1435 resetManageMode(element->umleSubMenuList);
1440 ** Cache user menus:
1441 ** Manage all menu widgets of given user sub-menu list.
1443 static void manageAllSubMenuWidgets(UserMenuListElement *subMenu)
1445 int i;
1446 UserMenuList *subMenuList;
1447 UserMenuListElement *element;
1448 WidgetList widgetList;
1449 Cardinal nWidgetListItems;
1451 /* if the sub-menu is torn off, unmanage the menu pane
1452 before updating it to prevent the tear-off menu
1453 from shrinking and expanding as the menu entries
1454 are (un)managed */
1455 if (!XmIsMenuShell(XtParent(subMenu->umleSubMenuPane))) {
1456 XtUnmanageChild(subMenu->umleSubMenuPane);
1459 /* manage all children of sub-menu pane */
1460 XtVaGetValues(subMenu->umleSubMenuPane,
1461 XmNchildren, &widgetList,
1462 XmNnumChildren, &nWidgetListItems,
1463 NULL);
1464 XtManageChildren(widgetList, nWidgetListItems);
1466 /* scan, if an menu item of given sub-menu holds a nested
1467 sub-menu */
1468 subMenuList = subMenu->umleSubMenuList;
1470 for (i=0; i<subMenuList->umlNbrItems; i ++) {
1471 element = subMenuList->umlItems[i];
1473 if (element->umleSubMenuList != NULL) {
1474 /* if element is a sub-menu, then continue managing
1475 all items of that sub-menu recursively */
1476 manageAllSubMenuWidgets(element);
1480 /* manage sub-menu pane widget itself */
1481 XtManageChild(subMenu->umleMenuItem);
1483 /* if the sub-menu is torn off, then adjust & manage the menu */
1484 if (!XmIsMenuShell(XtParent(subMenu->umleSubMenuPane))) {
1485 manageTearOffMenu(subMenu->umleSubMenuPane);
1488 /* redisplay sub-menu tearoff window, if the sub-menu
1489 was torn off before */
1490 ShowHiddenTearOff(subMenu->umleSubMenuPane);
1494 ** Cache user menus:
1495 ** Unmanage all menu widgets of given user sub-menu list.
1497 static void unmanageAllSubMenuWidgets(UserMenuListElement *subMenu)
1499 int i;
1500 Widget shell;
1501 UserMenuList *subMenuList;
1502 UserMenuListElement *element;
1503 WidgetList widgetList;
1504 Cardinal nWidgetListItems;
1506 /* if sub-menu is torn-off, then unmap its shell
1507 (so tearoff window isn't displayed anymore) */
1508 shell = XtParent(subMenu->umleSubMenuPane);
1509 if (!XmIsMenuShell(shell)) {
1510 XtUnmapWidget(shell);
1513 /* unmanage all children of sub-menu pane */
1514 XtVaGetValues(subMenu->umleSubMenuPane,
1515 XmNchildren, &widgetList,
1516 XmNnumChildren, &nWidgetListItems,
1517 NULL);
1518 XtUnmanageChildren(widgetList, nWidgetListItems);
1520 /* scan, if an menu item of given sub-menu holds a nested
1521 sub-menu */
1522 subMenuList = subMenu->umleSubMenuList;
1524 for (i=0; i<subMenuList->umlNbrItems; i ++) {
1525 element = subMenuList->umlItems[i];
1527 if (element->umleSubMenuList != NULL) {
1528 /* if element is a sub-menu, then continue unmanaging
1529 all items of that sub-menu recursively */
1530 unmanageAllSubMenuWidgets(element);
1534 /* unmanage sub-menu pane widget itself */
1535 XtUnmanageChild(subMenu->umleMenuItem);
1539 ** Cache user menus:
1540 ** Manage / unmanage menu widgets according to given user menu list.
1542 static void manageMenuWidgets(UserMenuList *list)
1544 int i;
1545 UserMenuListElement *element;
1547 /* (un)manage all elements of given user menu list */
1548 for (i=0; i<list->umlNbrItems; i ++) {
1549 element = list->umlItems[i];
1551 if (element->umlePrevManageMode != element->umleManageMode ||
1552 element->umleManageMode == UMMM_MANAGE) {
1553 /* previous and current manage mode differ OR
1554 current manage mode indicates: element needs to be
1555 (un)managed individually */
1556 if (element->umleManageMode == UMMM_MANAGE_ALL) {
1557 /* menu item represented by "element" is a sub-menu and
1558 needs to be completely managed */
1559 manageAllSubMenuWidgets(element);
1560 } else if (element->umleManageMode == UMMM_MANAGE) {
1561 if (element->umlePrevManageMode == UMMM_UNMANAGE ||
1562 element->umlePrevManageMode == UMMM_UNMANAGE_ALL) {
1563 /* menu item represented by "element" was unmanaged
1564 before and needs to be managed now */
1565 XtManageChild(element->umleMenuItem);
1568 /* if element is a sub-menu, then continue (un)managing
1569 single elements of that sub-menu one by one */
1570 if (element->umleSubMenuList != NULL) {
1571 /* if the sub-menu is torn off, unmanage the menu pane
1572 before updating it to prevent the tear-off menu
1573 from shrinking and expanding as the menu entries
1574 are (un)managed */
1575 if (!XmIsMenuShell(XtParent(element->umleSubMenuPane))) {
1576 XtUnmanageChild(element->umleSubMenuPane);
1579 /* (un)manage menu entries of sub-menu */
1580 manageMenuWidgets(element->umleSubMenuList);
1582 /* if the sub-menu is torn off, then adjust & manage the menu */
1583 if (!XmIsMenuShell(XtParent(element->umleSubMenuPane))) {
1584 manageTearOffMenu(element->umleSubMenuPane);
1587 /* if the sub-menu was torn off then redisplay it */
1588 ShowHiddenTearOff(element->umleSubMenuPane);
1590 } else if (element->umleManageMode == UMMM_UNMANAGE_ALL){
1591 /* menu item represented by "element" is a sub-menu and
1592 needs to be completely unmanaged */
1593 unmanageAllSubMenuWidgets(element);
1594 } else {
1595 /* current mode is UMMM_UNMANAGE -> menu item represented
1596 by "element" is a single menu item and needs to be
1597 unmanaged */
1598 XtUnmanageChild(element->umleMenuItem);
1605 ** Cache user menus:
1606 ** Remove accelerators from all items of given user (sub-)menu list.
1608 static void removeAccelFromMenuWidgets(UserMenuList *menuList)
1610 int i;
1611 UserMenuListElement *element;
1613 /* scan all elements of this (sub-)menu */
1614 for (i=0; i<menuList->umlNbrItems; i ++) {
1615 element = menuList->umlItems[i];
1617 if (element->umleSubMenuList != NULL) {
1618 /* if element is a sub-menu, then continue removing accelerators
1619 from all items of that sub-menu recursively */
1620 removeAccelFromMenuWidgets(element->umleSubMenuList);
1621 } else if (element->umleAccKeys != NULL &&
1622 element->umleManageMode == UMMM_UNMANAGE &&
1623 element->umlePrevManageMode == UMMM_MANAGE) {
1624 /* remove accelerator if one was bound */
1625 XtVaSetValues(element->umleMenuItem, XmNaccelerator, NULL, NULL);
1631 ** Cache user menus:
1632 ** Assign accelerators to all managed items of given user (sub-)menu list.
1634 static void assignAccelToMenuWidgets(UserMenuList *menuList, WindowInfo *window)
1636 int i;
1637 UserMenuListElement *element;
1639 /* scan all elements of this (sub-)menu */
1640 for (i=0; i<menuList->umlNbrItems; i ++) {
1641 element = menuList->umlItems[i];
1643 if (element->umleSubMenuList != NULL) {
1644 /* if element is a sub-menu, then continue assigning accelerators
1645 to all managed items of that sub-menu recursively */
1646 assignAccelToMenuWidgets(element->umleSubMenuList, window);
1647 } else if (element->umleAccKeys != NULL &&
1648 element->umleManageMode == UMMM_MANAGE &&
1649 element->umlePrevManageMode == UMMM_UNMANAGE) {
1650 /* assign accelerator if applicable */
1651 XtVaSetValues(element->umleMenuItem, XmNaccelerator,
1652 element->umleAccKeys, NULL);
1653 if (!element->umleAccLockPatchApplied) {
1654 UpdateAccelLockPatch(window->splitPane, element->umleMenuItem);
1655 element->umleAccLockPatchApplied = True;
1662 ** Cache user menus:
1663 ** (Un)Manage all items of selected user menu.
1665 static void manageUserMenu(selectedUserMenu *menu, WindowInfo *window)
1667 int n, i;
1668 int *id;
1669 Boolean currentLEisSubMenu;
1670 userMenuInfo *info;
1671 UserMenuList *menuList;
1672 UserMenuListElement *currentLE;
1673 UserMenuManageMode *mode;
1675 /* reset manage mode of all items of selected user menu in window cache */
1676 resetManageMode(menu->sumMainMenuList);
1678 /* set manage mode of all items of selected user menu in window cache
1679 according to the "to be managed" indication of the info list */
1680 for (n=0; n<menu->sumNbrOfListItems; n++) {
1681 info = menu->sumInfoList[n];
1683 menuList = menu->sumMainMenuList;
1684 id = info->umiId;
1686 /* select all menu list items belonging to menu record "info" using
1687 hierarchical ID of current menu info (e.g. id = {3} means:
1688 4th element of main menu; {0} = 1st element etc.)*/
1689 for (i=0; i<info->umiIdLen; i ++) {
1690 currentLE = menuList->umlItems[*id];
1691 mode = &currentLE->umleManageMode;
1692 currentLEisSubMenu = (currentLE->umleSubMenuList != NULL);
1694 if (info->umiToBeManaged) {
1695 /* menu record needs to be managed: */
1696 if (*mode == UMMM_UNMANAGE) {
1697 /* "mode" was not touched after reset ("init. state"):
1698 if current list element represents a sub-menu, then
1699 probably the complete sub-menu needs to be managed
1700 too. If current list element indicates single menu
1701 item, then just this item needs to be managed */
1702 if (currentLEisSubMenu) {
1703 *mode = UMMM_MANAGE_ALL;
1704 } else {
1705 *mode = UMMM_MANAGE;
1707 } else if (*mode == UMMM_UNMANAGE_ALL) {
1708 /* "mode" was touched after reset:
1709 current list element represents a sub-menu and min.
1710 one element of the sub-menu needs to be unmanaged ->
1711 the sub-menu needs to be (un)managed element by
1712 element */
1713 *mode = UMMM_MANAGE;
1715 } else {
1716 /* menu record needs to be unmanaged: */
1717 if (*mode == UMMM_UNMANAGE) {
1718 /* "mode" was not touched after reset ("init. state"):
1719 if current list element represents a sub-menu, then
1720 probably the complete sub-menu needs to be unmanaged
1721 too. */
1722 if (currentLEisSubMenu) {
1723 *mode = UMMM_UNMANAGE_ALL;
1725 } else if (*mode == UMMM_MANAGE_ALL) {
1726 /* "mode" was touched after reset:
1727 current list element represents a sub-menu and min.
1728 one element of the sub-menu needs to be managed ->
1729 the sub-menu needs to be (un)managed element by
1730 element */
1731 *mode = UMMM_MANAGE;
1735 menuList = currentLE->umleSubMenuList;
1737 id ++;
1741 /* if the menu is torn off, unmanage the menu pane
1742 before updating it to prevent the tear-off menu
1743 from shrinking and expanding as the menu entries
1744 are managed */
1745 if (!XmIsMenuShell(XtParent(menu->sumMenuPane)))
1746 XtUnmanageChild(menu->sumMenuPane);
1748 /* manage menu widgets according to current / previous manage mode of
1749 user menu window cache */
1750 manageMenuWidgets(menu->sumMainMenuList);
1752 /* Note: before new accelerator is assigned it seems to be necessary
1753 to remove old accelerator from user menu widgets. Removing same
1754 accelerator *after* it was assigned to another user menu widget
1755 doesn't work */
1756 removeAccelFromMenuWidgets(menu->sumMainMenuList);
1758 assignAccelToMenuWidgets(menu->sumMainMenuList, window);
1760 /* if the menu is torn off, then adjust & manage the menu */
1761 if (!XmIsMenuShell(XtParent(menu->sumMenuPane)))
1762 manageTearOffMenu(menu->sumMenuPane);
1766 ** Create either the variable Shell menu, Macro menu or Background menu
1767 ** items of "window" (driven by value of "menuType")
1769 static void createMenuItems(WindowInfo *window, selectedUserMenu *menu)
1771 Widget btn, subPane, newSubPane;
1772 int n;
1773 menuItemRec *item;
1774 menuTreeItem *menuTree;
1775 int i, nTreeEntries, size;
1776 char *hierName, *namePtr, *subMenuName, *subSep, *fullName;
1777 int menuType = menu->sumType;
1778 userMenuInfo *info;
1779 userSubMenuCache *subMenus = menu->sumSubMenus;
1780 userSubMenuInfo *subMenuInfo;
1781 UserMenuList *menuList;
1782 UserMenuListElement *currentLE;
1783 int subMenuDepth;
1784 char accKeysBuf[MAX_ACCEL_LEN+5];
1785 char *accKeys;
1787 /* Allocate storage for structures to help find panes of sub-menus */
1788 size = sizeof(menuTreeItem) * menu->sumNbrOfListItems;
1789 menuTree = (menuTreeItem *)XtMalloc(size);
1790 nTreeEntries = 0;
1792 /* Harmless kludge: undo and redo items are marked specially if found
1793 in the background menu, and used to dim/undim with edit menu */
1794 window->bgMenuUndoItem = NULL;
1795 window->bgMenuRedoItem = NULL;
1798 ** Add items to the menu pane, creating hierarchical sub-menus as
1799 ** necessary
1801 allocUserMenuList(menu->sumMainMenuList, subMenus->usmcNbrOfMainMenuItems);
1802 for (n=0; n<menu->sumNbrOfListItems; n++) {
1803 item = menu->sumItemList[n];
1804 info = menu->sumInfoList[n];
1805 menuList = menu->sumMainMenuList;
1806 subMenuDepth = 0;
1808 fullName = info->umiName;
1810 /* create/find sub-menus, stripping off '>' until item name is
1811 reached, then create the menu item */
1812 namePtr = fullName;
1813 subPane = menu->sumMenuPane;
1814 for (;;) {
1815 subSep = strchr(namePtr, '>');
1816 if (subSep == NULL) {
1817 btn = createUserMenuItem(subPane, namePtr, item, n,
1818 (XtCallbackProc)(menuType == SHELL_CMDS ? shellMenuCB :
1819 (menuType == MACRO_CMDS ? macroMenuCB : bgMenuCB)),
1820 (XtPointer)window);
1821 if (menuType == BG_MENU_CMDS && !strcmp(item->cmd, "undo()\n"))
1822 window->bgMenuUndoItem = btn;
1823 else if (menuType == BG_MENU_CMDS && !strcmp(item->cmd,"redo()\n"))
1824 window->bgMenuRedoItem = btn;
1825 /* generate accelerator keys */
1826 genAccelEventName(accKeysBuf, item->modifiers, item->keysym);
1827 accKeys = item->keysym == NoSymbol ? NULL : XtNewString(accKeysBuf);
1828 /* create corresponding menu list item */
1829 menuList->umlItems[menuList->umlNbrItems ++] =
1830 allocUserMenuListElement(btn, accKeys);
1831 break;
1833 hierName = copySubstring(fullName, subSep - fullName);
1834 subMenuInfo = findSubMenuInfo(subMenus, hierName);
1835 newSubPane = findInMenuTree(menuTree, nTreeEntries, hierName);
1836 if (newSubPane == NULL) {
1837 subMenuName = copySubstring(namePtr, subSep - namePtr);
1838 newSubPane = createUserSubMenu(subPane, subMenuName, &btn);
1839 XtFree(subMenuName);
1840 menuTree[nTreeEntries].name = hierName;
1841 menuTree[nTreeEntries++].menuPane = newSubPane;
1843 currentLE = allocUserMenuListElement(btn, NULL);
1844 menuList->umlItems[menuList->umlNbrItems ++] = currentLE;
1845 currentLE->umleSubMenuPane = newSubPane;
1846 currentLE->umleSubMenuList =
1847 allocUserSubMenuList(subMenuInfo->usmiId[subMenuInfo->usmiIdLen]);
1848 } else {
1849 currentLE = menuList->umlItems[subMenuInfo->usmiId[subMenuDepth]];
1850 XtFree(hierName);
1852 subPane = newSubPane;
1853 menuList = currentLE->umleSubMenuList;
1854 subMenuDepth ++;
1855 namePtr = subSep + 1;
1859 *menu->sumMenuCreated = True;
1861 /* Free the structure used to keep track of sub-menus durring creation */
1862 for (i=0; i<nTreeEntries; i++)
1863 XtFree(menuTree[i].name);
1864 XtFree((char *)menuTree);
1868 ** Find the widget corresponding to a hierarchical menu name (a>b>c...)
1870 static Widget findInMenuTree(menuTreeItem *menuTree, int nTreeEntries,
1871 const char *hierName)
1873 int i;
1875 for (i=0; i<nTreeEntries; i++)
1876 if (!strcmp(hierName, menuTree[i].name))
1877 return menuTree[i].menuPane;
1878 return NULL;
1881 static char *copySubstring(const char *string, int length)
1883 char *retStr = XtMalloc(length + 1);
1885 strncpy(retStr, string, length);
1886 retStr[length] = '\0';
1887 return retStr;
1890 static Widget createUserMenuItem(Widget menuPane, char *name, menuItemRec *f,
1891 int index, XtCallbackProc cbRtn, XtPointer cbArg)
1893 XmString st1, st2;
1894 char accText[MAX_ACCEL_LEN];
1895 Widget btn;
1897 generateAcceleratorString(accText, f->modifiers, f->keysym);
1898 st1=XmStringCreateSimple(name);
1899 st2=XmStringCreateSimple(accText);
1900 btn = XtVaCreateWidget("cmd", xmPushButtonWidgetClass, menuPane,
1901 XmNlabelString, st1,
1902 XmNacceleratorText, st2,
1903 XmNmnemonic, f->mnemonic,
1904 XmNuserData, index+10, NULL);
1905 XtAddCallback(btn, XmNactivateCallback, cbRtn, cbArg);
1906 XmStringFree(st1);
1907 XmStringFree(st2);
1908 return btn;
1912 ** Add a user-defined sub-menu to an established pull-down menu, marking
1913 ** it's userData field with TEMPORARY_MENU_ITEM so it can be found and
1914 ** removed later if the menu is redefined. Returns the menu pane of the
1915 ** new sub-menu.
1917 static Widget createUserSubMenu(Widget parent, char *label, Widget *menuItem)
1919 Widget menuPane;
1920 XmString st1;
1921 static Arg args[1] = {{XmNuserData, (XtArgVal)TEMPORARY_MENU_ITEM}};
1923 menuPane = CreatePulldownMenu(parent, "userPulldown", args, 1);
1924 *menuItem = XtVaCreateWidget("userCascade", xmCascadeButtonWidgetClass, parent,
1925 XmNlabelString, st1=XmStringCreateSimple(label),
1926 XmNsubMenuId, menuPane, XmNuserData, TEMPORARY_MENU_ITEM,
1927 NULL);
1928 XmStringFree(st1);
1929 return menuPane;
1933 ** Cache user menus:
1934 ** Delete all variable menu items of given menu pane
1936 static void deleteMenuItems(Widget menuPane)
1938 WidgetList itemList, items;
1939 Cardinal nItems;
1940 Widget subMenuID;
1941 XtPointer userData;
1942 int n;
1944 /* Fetch the list of children from the menu pane to delete */
1945 XtVaGetValues(menuPane, XmNchildren, &itemList,
1946 XmNnumChildren, &nItems, NULL);
1948 /* make a copy because the widget alters the list as you delete widgets */
1949 items = (WidgetList)XtMalloc(sizeof(Widget) * nItems);
1950 memcpy(items, itemList, sizeof(Widget) * nItems);
1952 /* delete all of the widgets not marked as PERMANENT_MENU_ITEM */
1953 for (n=0; n<(int)nItems; n++) {
1954 XtVaGetValues(items[n], XmNuserData, &userData, NULL);
1955 if (userData != (XtPointer)PERMANENT_MENU_ITEM) {
1956 if (XtClass(items[n]) == xmCascadeButtonWidgetClass) {
1957 XtVaGetValues(items[n], XmNsubMenuId, &subMenuID, NULL);
1959 /* prevent dangling submenu tearoffs */
1960 if (!XmIsMenuShell(XtParent(subMenuID)))
1961 _XmDismissTearOff(XtParent(subMenuID), NULL, NULL);
1963 deleteMenuItems(subMenuID);
1964 #if XmVersion < 2000
1965 /* Skipping this creates a memory and server resource
1966 leak (though both are reclaimed on window closing). In
1967 Motif 2.0 (and beyond?) there is a potential crash during
1968 phase 2 widget destruction in "SetCascadeField", and in
1969 Motif 1.2 there are free-memory reads. I would really like
1970 to be able to destroy this. */
1971 XtDestroyWidget(subMenuID);
1972 #endif
1973 } else {
1974 /* remove accel. before destroy or lose it forever */
1975 XtVaSetValues(items[n], XmNaccelerator, NULL, NULL);
1977 XtDestroyWidget(items[n]);
1980 XtFree((char *)items);
1983 static void dismissCB(Widget w, XtPointer clientData, XtPointer callData)
1985 userCmdDialog *ucd = (userCmdDialog *)clientData;
1987 /* Mark that there's no longer a (macro, bg, or shell) dialog up */
1988 if (ucd->dialogType == SHELL_CMDS)
1989 ShellCmdDialog = NULL;
1990 else if (ucd->dialogType == MACRO_CMDS)
1991 MacroCmdDialog = NULL;
1992 else
1993 BGMenuCmdDialog = NULL;
1995 /* pop down and destroy the dialog (memory for ucd is freed in the
1996 destroy callback) */
1997 XtDestroyWidget(ucd->dlogShell);
2000 static void okCB(Widget w, XtPointer clientData, XtPointer callData)
2002 userCmdDialog *ucd = (userCmdDialog *)clientData;
2004 /* Read the dialog fields, and update the menus */
2005 if (!applyDialogChanges(ucd))
2006 return;
2008 /* Mark that there's no longer a (macro, bg, or shell) dialog up */
2009 if (ucd->dialogType == SHELL_CMDS)
2010 ShellCmdDialog = NULL;
2011 else if (ucd->dialogType == MACRO_CMDS)
2012 MacroCmdDialog = NULL;
2013 else
2014 BGMenuCmdDialog = NULL;
2016 /* pop down and destroy the dialog (memory for ucd is freed in the
2017 destroy callback) */
2018 XtDestroyWidget(ucd->dlogShell);
2021 static void applyCB(Widget w, XtPointer clientData, XtPointer callData)
2023 applyDialogChanges((userCmdDialog *)clientData);
2026 static void checkCB(Widget w, XtPointer clientData, XtPointer callData)
2028 userCmdDialog *ucd = (userCmdDialog *)clientData;
2030 if (checkMacro(ucd))
2032 DialogF(DF_INF, ucd->dlogShell, 1, "Macro",
2033 "Macro compiled without error", "Dismiss");
2037 static int checkMacro(userCmdDialog *ucd)
2039 menuItemRec *f;
2041 f = readDialogFields(ucd, False);
2042 if (f == NULL)
2043 return False;
2044 if (!checkMacroText(f->cmd, ucd->dlogShell, ucd->cmdTextW)) {
2045 freeMenuItemRec(f);
2046 return False;
2048 return True;
2051 static int checkMacroText(char *macro, Widget errorParent, Widget errFocus)
2053 Program *prog;
2054 char *errMsg, *stoppedAt;
2056 prog = ParseMacro(macro, &errMsg, &stoppedAt);
2057 if (prog == NULL) {
2058 if (errorParent != NULL) {
2059 ParseError(errorParent, macro, stoppedAt, "macro", errMsg);
2060 XmTextSetInsertionPosition(errFocus, stoppedAt - macro);
2061 XmProcessTraversal(errFocus, XmTRAVERSE_CURRENT);
2063 return False;
2065 FreeProgram(prog);
2066 if (*stoppedAt != '\0') {
2067 if (errorParent != NULL) {
2068 ParseError(errorParent, macro, stoppedAt,"macro","syntax error");
2069 XmTextSetInsertionPosition(errFocus, stoppedAt - macro);
2070 XmProcessTraversal(errFocus, XmTRAVERSE_CURRENT);
2072 return False;
2074 return True;
2077 static int applyDialogChanges(userCmdDialog *ucd)
2079 int i;
2081 /* Get the current contents of the dialog fields */
2082 if (!UpdateManagedList(ucd->managedList, True))
2083 return False;
2085 /* Test compile the macro */
2086 if (ucd->dialogType == MACRO_CMDS)
2087 if (!checkMacro(ucd))
2088 return False;
2090 /* Update the menu information */
2091 if (ucd->dialogType == SHELL_CMDS) {
2092 for (i=0; i<NShellMenuItems; i++)
2093 freeMenuItemRec(ShellMenuItems[i]);
2094 freeUserMenuInfoList(ShellMenuInfo, NShellMenuItems);
2095 freeSubMenuCache(&ShellSubMenus);
2096 for (i=0; i<ucd->nMenuItems; i++)
2097 ShellMenuItems[i] = copyMenuItemRec(ucd->menuItemsList[i]);
2098 NShellMenuItems = ucd->nMenuItems;
2099 parseMenuItemList(ShellMenuItems, NShellMenuItems, ShellMenuInfo, &ShellSubMenus);
2100 } else if (ucd->dialogType == MACRO_CMDS) {
2101 for (i=0; i<NMacroMenuItems; i++)
2102 freeMenuItemRec(MacroMenuItems[i]);
2103 freeUserMenuInfoList(MacroMenuInfo, NMacroMenuItems);
2104 freeSubMenuCache(&MacroSubMenus);
2105 for (i=0; i<ucd->nMenuItems; i++)
2106 MacroMenuItems[i] = copyMenuItemRec(ucd->menuItemsList[i]);
2107 NMacroMenuItems = ucd->nMenuItems;
2108 parseMenuItemList(MacroMenuItems, NMacroMenuItems, MacroMenuInfo, &MacroSubMenus);
2109 } else { /* BG_MENU_CMDS */
2110 for (i=0; i<NBGMenuItems; i++)
2111 freeMenuItemRec(BGMenuItems[i]);
2112 freeUserMenuInfoList(BGMenuInfo, NBGMenuItems);
2113 freeSubMenuCache(&BGSubMenus);
2114 for (i=0; i<ucd->nMenuItems; i++)
2115 BGMenuItems[i] = copyMenuItemRec(ucd->menuItemsList[i]);
2116 NBGMenuItems = ucd->nMenuItems;
2117 parseMenuItemList(BGMenuItems, NBGMenuItems, BGMenuInfo, &BGSubMenus);
2120 /* Update the menus themselves in all of the NEdit windows */
2121 rebuildMenuOfAllWindows(ucd->dialogType);
2123 /* Note that preferences have been changed */
2124 MarkPrefsChanged();
2125 return True;
2128 static void pasteReplayCB(Widget w, XtPointer clientData, XtPointer callData)
2130 userCmdDialog *ucd = (userCmdDialog *)clientData;
2132 if (GetReplayMacro() == NULL)
2133 return;
2135 XmTextInsert(ucd->cmdTextW, XmTextGetInsertionPosition(ucd->cmdTextW),
2136 GetReplayMacro());
2139 static void destroyCB(Widget w, XtPointer clientData, XtPointer callData)
2141 userCmdDialog *ucd = (userCmdDialog *)clientData;
2142 int i;
2144 for (i=0; i<ucd->nMenuItems; i++)
2145 freeMenuItemRec(ucd->menuItemsList[i]);
2146 XtFree((char *)ucd->menuItemsList);
2147 XtFree((char *)ucd);
2150 static void accFocusCB(Widget w, XtPointer clientData, XtPointer callData)
2152 userCmdDialog *ucd = (userCmdDialog *)clientData;
2154 RemoveDialogMnemonicHandler(XtParent(ucd->accTextW));
2157 static void accLoseFocusCB(Widget w, XtPointer clientData, XtPointer callData)
2159 userCmdDialog *ucd = (userCmdDialog *)clientData;
2161 AddDialogMnemonicHandler(XtParent(ucd->accTextW), FALSE);
2164 static void accKeyCB(Widget w, XtPointer clientData, XKeyEvent *event)
2166 userCmdDialog *ucd = (userCmdDialog *)clientData;
2167 KeySym keysym = XLookupKeysym(event, 0);
2168 char outStr[MAX_ACCEL_LEN];
2170 /* Accept only real keys, not modifiers alone */
2171 if (IsModifierKey(keysym))
2172 return;
2174 /* Tab key means go to the next field, don't enter */
2175 if (keysym == XK_Tab)
2176 return;
2178 /* Beep and return if the modifiers are buttons or ones we don't support */
2179 if (event->state & ~(ShiftMask | LockMask | ControlMask | Mod1Mask |
2180 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask)) {
2181 XBell(TheDisplay, 0);
2182 return;
2185 /* Delete or backspace clears field */
2186 if (keysym == XK_Delete || keysym == XK_BackSpace) {
2187 XmTextSetString(ucd->accTextW, "");
2188 return;
2191 /* generate the string to use in the dialog field */
2192 generateAcceleratorString(outStr, event->state, keysym);
2194 /* Reject single character accelerators (a very simple way to eliminate
2195 un-modified letters and numbers) The goal is give users a clue that
2196 they're supposed to type the actual keys, not the name. This scheme
2197 is not rigorous and still allows accelerators like Comma. */
2198 if (strlen(outStr) == 1) {
2199 XBell(TheDisplay, 0);
2200 return;
2203 /* fill in the accelerator field in the dialog */
2204 XmTextSetString(ucd->accTextW, outStr);
2207 static void sameOutCB(Widget w, XtPointer clientData, XtPointer callData)
2209 XtSetSensitive(((userCmdDialog *)clientData)->repInpBtn,
2210 XmToggleButtonGetState(w));
2213 static void shellMenuCB(Widget w, WindowInfo *window, XtPointer callData)
2215 XtArgVal userData;
2216 int index;
2217 char *params[1];
2219 window = WidgetToWindow(MENU_WIDGET(w));
2221 /* get the index of the shell command and verify that it's in range */
2222 XtVaGetValues(w, XmNuserData, &userData, NULL);
2223 index = (int)userData - 10;
2224 if (index <0 || index >= NShellMenuItems)
2225 return;
2227 params[0] = ShellMenuItems[index]->name;
2228 XtCallActionProc(window->lastFocus, "shell_menu_command",
2229 ((XmAnyCallbackStruct *)callData)->event, params, 1);
2232 static void macroMenuCB(Widget w, WindowInfo *window, XtPointer callData)
2234 XtArgVal userData;
2235 int index;
2236 char *params[1];
2238 window = WidgetToWindow(MENU_WIDGET(w));
2240 /* Don't allow users to execute a macro command from the menu (or accel)
2241 if there's already a macro command executing. NEdit can't handle
2242 running multiple, independent uncoordinated, macros in the same
2243 window. Macros may invoke macro menu commands recursively via the
2244 macro_menu_command action proc, which is important for being able to
2245 repeat any operation, and to embed macros within eachother at any
2246 level, however, a call here with a macro running means that THE USER
2247 is explicitly invoking another macro via the menu or an accelerator. */
2248 if (window->macroCmdData != NULL) {
2249 XBell(TheDisplay, 0);
2250 return;
2253 /* get the index of the macro command and verify that it's in range */
2254 XtVaGetValues(w, XmNuserData, &userData, NULL);
2255 index = (int)userData - 10;
2256 if (index <0 || index >= NMacroMenuItems)
2257 return;
2259 params[0] = MacroMenuItems[index]->name;
2260 XtCallActionProc(window->lastFocus, "macro_menu_command",
2261 ((XmAnyCallbackStruct *)callData)->event, params, 1);
2264 static void bgMenuCB(Widget w, WindowInfo *window, XtPointer callData)
2266 XtArgVal userData;
2267 int index;
2268 char *params[1];
2270 /* Same remark as for macro menu commands (see above). */
2271 if (window->macroCmdData != NULL) {
2272 XBell(TheDisplay, 0);
2273 return;
2276 /* get the index of the macro command and verify that it's in range */
2277 XtVaGetValues(w, XmNuserData, &userData, NULL);
2278 index = (int)userData - 10;
2279 if (index <0 || index >= NBGMenuItems)
2280 return;
2282 params[0] = BGMenuItems[index]->name;
2283 XtCallActionProc(window->lastFocus, "bg_menu_command",
2284 ((XmAnyCallbackStruct *)callData)->event, params, 1);
2288 ** Update the name, accelerator, mnemonic, and command fields in the shell
2289 ** command or macro dialog to agree with the currently selected item in the
2290 ** menu item list.
2292 static void updateDialogFields(menuItemRec *f, userCmdDialog *ucd)
2294 char mneString[2], accString[MAX_ACCEL_LEN];
2296 /* fill in the name, accelerator, mnemonic, and command fields of the
2297 dialog for the newly selected item, or blank them if "New" is selected */
2298 if (f == NULL) {
2299 XmTextSetString(ucd->nameTextW, "");
2300 XmTextSetString(ucd->cmdTextW, "");
2301 XmTextSetString(ucd->accTextW, "");
2302 XmTextSetString(ucd->mneTextW, "");
2303 if (ucd->dialogType == SHELL_CMDS) {
2304 RadioButtonChangeState(ucd->selInpBtn, True, True);
2305 RadioButtonChangeState(ucd->sameOutBtn, True, True);
2306 RadioButtonChangeState(ucd->repInpBtn, False, False);
2307 XtSetSensitive(ucd->repInpBtn, True);
2308 RadioButtonChangeState(ucd->saveFirstBtn, False, False);
2309 RadioButtonChangeState(ucd->loadAfterBtn, False, False);
2311 } else {
2312 mneString[0] = f->mnemonic;
2313 mneString[1] = '\0';
2314 generateAcceleratorString(accString, f->modifiers, f->keysym);
2315 XmTextSetString(ucd->nameTextW, f->name);
2316 XmTextSetString(ucd->cmdTextW, f->cmd);
2317 XmTextSetString(ucd->accTextW, accString);
2318 XmTextSetString(ucd->mneTextW, mneString);
2319 RadioButtonChangeState(ucd->selInpBtn, f->input==FROM_SELECTION, False);
2320 if (ucd->dialogType == SHELL_CMDS) {
2321 RadioButtonChangeState(ucd->winInpBtn, f->input == FROM_WINDOW,
2322 False);
2323 RadioButtonChangeState(ucd->eitherInpBtn, f->input == FROM_EITHER,
2324 False);
2325 RadioButtonChangeState(ucd->noInpBtn, f->input == FROM_NONE,
2326 False);
2327 RadioButtonChangeState(ucd->sameOutBtn, f->output==TO_SAME_WINDOW,
2328 False);
2329 RadioButtonChangeState(ucd->winOutBtn, f->output==TO_NEW_WINDOW,
2330 False);
2331 RadioButtonChangeState(ucd->dlogOutBtn, f->output==TO_DIALOG,
2332 False);
2333 RadioButtonChangeState(ucd->repInpBtn, f->repInput, False);
2334 XtSetSensitive(ucd->repInpBtn, f->output==TO_SAME_WINDOW);
2335 RadioButtonChangeState(ucd->saveFirstBtn, f->saveFirst, False);
2336 RadioButtonChangeState(ucd->loadAfterBtn, f->loadAfter, False);
2342 ** Read the name, accelerator, mnemonic, and command fields from the shell or
2343 ** macro commands dialog into a newly allocated menuItemRec. Returns a
2344 ** pointer to the new menuItemRec structure as the function value, or NULL on
2345 ** failure.
2347 static menuItemRec *readDialogFields(userCmdDialog *ucd, int silent)
2349 char *nameText, *cmdText, *mneText, *accText;
2350 menuItemRec *f;
2352 nameText = XmTextGetString(ucd->nameTextW);
2353 if (*nameText == '\0')
2355 if (!silent)
2357 DialogF(DF_WARN, ucd->dlogShell, 1, "Menu Entry",
2358 "Please specify a name\nfor the menu item", "Dismiss");
2359 XmProcessTraversal(ucd->nameTextW, XmTRAVERSE_CURRENT);
2361 XtFree(nameText);
2362 return NULL;
2365 if (strchr(nameText, ':'))
2367 if (!silent)
2369 DialogF(DF_WARN, ucd->dlogShell, 1, "Menu Entry",
2370 "Menu item names may not\ncontain colon (:) characters",
2371 "Dismiss");
2372 XmProcessTraversal(ucd->nameTextW, XmTRAVERSE_CURRENT);
2374 XtFree(nameText);
2375 return NULL;
2378 cmdText = XmTextGetString(ucd->cmdTextW);
2379 if (cmdText == NULL || *cmdText == '\0')
2381 if (!silent)
2383 DialogF(DF_WARN, ucd->dlogShell, 1, "Command to Execute",
2384 "Please specify %s to execute", "Dismiss",
2385 ucd->dialogType == SHELL_CMDS
2386 ? "shell command"
2387 : "macro command(s)");
2388 XmProcessTraversal(ucd->cmdTextW, XmTRAVERSE_CURRENT);
2390 XtFree(nameText);
2392 if (cmdText!=NULL)
2394 XtFree(cmdText);
2396 return NULL;
2399 if (ucd->dialogType == MACRO_CMDS || ucd->dialogType == BG_MENU_CMDS) {
2400 addTerminatingNewline(&cmdText);
2401 if (!checkMacroText(cmdText, silent ? NULL : ucd->dlogShell,
2402 ucd->cmdTextW)) {
2403 XtFree(nameText);
2404 XtFree(cmdText);
2405 return NULL;
2408 f = (menuItemRec *)XtMalloc(sizeof(menuItemRec));
2409 f->name = nameText;
2410 f->cmd = cmdText;
2411 if ((mneText = XmTextGetString(ucd->mneTextW)) != NULL) {
2412 f->mnemonic = mneText==NULL ? '\0' : mneText[0];
2413 XtFree(mneText);
2414 if (f->mnemonic == ':') /* colons mess up string parsing */
2415 f->mnemonic = '\0';
2417 if ((accText = XmTextGetString(ucd->accTextW)) != NULL) {
2418 parseAcceleratorString(accText, &f->modifiers, &f->keysym);
2419 XtFree(accText);
2421 if (ucd->dialogType == SHELL_CMDS) {
2422 if (XmToggleButtonGetState(ucd->selInpBtn))
2423 f->input = FROM_SELECTION;
2424 else if (XmToggleButtonGetState(ucd->winInpBtn))
2425 f->input = FROM_WINDOW;
2426 else if (XmToggleButtonGetState(ucd->eitherInpBtn))
2427 f->input = FROM_EITHER;
2428 else
2429 f->input = FROM_NONE;
2430 if (XmToggleButtonGetState(ucd->winOutBtn))
2431 f->output = TO_NEW_WINDOW;
2432 else if (XmToggleButtonGetState(ucd->dlogOutBtn))
2433 f->output = TO_DIALOG;
2434 else
2435 f->output = TO_SAME_WINDOW;
2436 f->repInput = XmToggleButtonGetState(ucd->repInpBtn);
2437 f->saveFirst = XmToggleButtonGetState(ucd->saveFirstBtn);
2438 f->loadAfter = XmToggleButtonGetState(ucd->loadAfterBtn);
2439 } else {
2440 f->input = XmToggleButtonGetState(ucd->selInpBtn) ? FROM_SELECTION :
2441 FROM_NONE;
2442 f->output = TO_SAME_WINDOW;
2443 f->repInput = False;
2444 f->saveFirst = False;
2445 f->loadAfter = False;
2447 return f;
2451 ** Copy a menu item record, and its associated memory
2453 static menuItemRec *copyMenuItemRec(menuItemRec *item)
2455 menuItemRec *newItem;
2457 newItem = (menuItemRec *)XtMalloc(sizeof(menuItemRec));
2458 *newItem = *item;
2459 newItem->name = XtMalloc(strlen(item->name)+1);
2460 strcpy(newItem->name, item->name);
2461 newItem->cmd = XtMalloc(strlen(item->cmd)+1);
2462 strcpy(newItem->cmd, item->cmd);
2463 return newItem;
2467 ** Free a menu item record, and its associated memory
2469 static void freeMenuItemRec(menuItemRec *item)
2471 XtFree(item->name);
2472 XtFree(item->cmd);
2473 XtFree((char *)item);
2477 ** Callbacks for managed-list operations
2479 static void *getDialogDataCB(void *oldItem, int explicitRequest, int *abort,
2480 void *cbArg)
2482 userCmdDialog *ucd = (userCmdDialog *)cbArg;
2483 menuItemRec *currentFields;
2485 /* If the dialog is currently displaying the "new" entry and the
2486 fields are empty, that's just fine */
2487 if (oldItem == NULL && dialogFieldsAreEmpty(ucd))
2488 return NULL;
2490 /* If there are no problems reading the data, just return it */
2491 currentFields = readDialogFields(ucd, True);
2492 if (currentFields != NULL)
2493 return (void *)currentFields;
2495 /* If user might not be expecting fields to be read, give more warning */
2496 if (!explicitRequest)
2498 if (DialogF(DF_WARN, ucd->dlogShell, 2, "Discard Entry",
2499 "Discard incomplete entry\nfor current menu item?", "Keep",
2500 "Discard") == 2)
2502 return oldItem == NULL
2503 ? NULL
2504 : (void *)copyMenuItemRec((menuItemRec *)oldItem);
2508 /* Do readDialogFields again without "silent" mode to display warning(s) */
2509 readDialogFields(ucd, False);
2510 *abort = True;
2511 return NULL;
2515 static void setDialogDataCB(void *item, void *cbArg)
2517 updateDialogFields((menuItemRec *)item, (userCmdDialog *)cbArg);
2520 static int dialogFieldsAreEmpty(userCmdDialog *ucd)
2522 return TextWidgetIsBlank(ucd->nameTextW) &&
2523 TextWidgetIsBlank(ucd->cmdTextW) &&
2524 TextWidgetIsBlank(ucd->accTextW) &&
2525 TextWidgetIsBlank(ucd->mneTextW) &&
2526 (ucd->dialogType != SHELL_CMDS || (
2527 XmToggleButtonGetState(ucd->selInpBtn) &&
2528 XmToggleButtonGetState(ucd->sameOutBtn) &&
2529 !XmToggleButtonGetState(ucd->repInpBtn) &&
2530 !XmToggleButtonGetState(ucd->saveFirstBtn) &&
2531 !XmToggleButtonGetState(ucd->loadAfterBtn)));
2534 static void freeItemCB(void *item)
2536 freeMenuItemRec((menuItemRec *)item);
2540 ** Gut a text widget of it's ability to process input
2542 static void disableTextW(Widget textW)
2544 static XtTranslations emptyTable = NULL;
2545 static char *emptyTranslations = "\
2546 <EnterWindow>: enter()\n\
2547 <Btn1Down>: grab-focus()\n\
2548 <Btn1Motion>: extend-adjust()\n\
2549 <Btn1Up>: extend-end()\n\
2550 Shift<Key>Tab: prev-tab-group()\n\
2551 Ctrl<Key>Tab: next-tab-group()\n\
2552 <Key>Tab: next-tab-group()\n\
2553 <LeaveWindow>: leave()\n\
2554 <FocusIn>: focusIn()\n\
2555 <FocusOut>: focusOut()\n\
2556 <Unmap>: unmap()\n";
2558 /* replace the translation table with the slimmed down one above */
2559 if (emptyTable == NULL)
2560 emptyTable = XtParseTranslationTable(emptyTranslations);
2561 XtVaSetValues(textW, XmNtranslations, emptyTable, NULL);
2564 static char *writeMenuItemString(menuItemRec **menuItems, int nItems,
2565 int listType)
2567 char *outStr, *outPtr, *c, accStr[MAX_ACCEL_LEN];
2568 menuItemRec *f;
2569 int i, length;
2571 /* determine the max. amount of memory needed for the returned string
2572 and allocate a buffer for composing the string */
2573 length = 0;
2574 for (i=0; i<nItems; i++) {
2575 f = menuItems[i];
2576 generateAcceleratorString(accStr, f->modifiers, f->keysym);
2577 length += strlen(f->name) * 2; /* allow for \n & \\ expansions */
2578 length += strlen(accStr);
2579 length += strlen(f->cmd) * 6; /* allow for \n & \\ expansions */
2580 length += 21; /* number of characters added below */
2582 length++; /* terminating null */
2583 outStr = XtMalloc(length);
2585 /* write the string */
2586 outPtr = outStr;
2587 *outPtr++ = '\\';
2588 *outPtr++ = '\n';
2589 for (i=0; i<nItems; i++) {
2590 f = menuItems[i];
2591 generateAcceleratorString(accStr, f->modifiers, f->keysym);
2592 *outPtr++ = '\t';
2593 for (c=f->name; *c!='\0'; ++c) { /* Copy the command name */
2594 if (*c == '\\') { /* changing backslashes to \\ */
2595 *outPtr++ = '\\';
2596 *outPtr++ = '\\';
2597 } else if (*c == '\n') { /* changing newlines to \n */
2598 *outPtr++ = '\\';
2599 *outPtr++ = 'n';
2600 } else {
2601 *outPtr++ = *c;
2604 *outPtr++ = ':';
2605 strcpy(outPtr, accStr);
2606 outPtr += strlen(accStr);
2607 *outPtr++ = ':';
2608 if (f->mnemonic != '\0')
2609 *outPtr++ = f->mnemonic;
2610 *outPtr++ = ':';
2611 if (listType == SHELL_CMDS) {
2612 if (f->input == FROM_SELECTION)
2613 *outPtr++ = 'I';
2614 else if (f->input == FROM_WINDOW)
2615 *outPtr++ = 'A';
2616 else if (f->input == FROM_EITHER)
2617 *outPtr++ = 'E';
2618 if (f->output == TO_DIALOG)
2619 *outPtr++ = 'D';
2620 else if (f->output == TO_NEW_WINDOW)
2621 *outPtr++ = 'W';
2622 if (f->repInput)
2623 *outPtr++ = 'X';
2624 if (f->saveFirst)
2625 *outPtr++ = 'S';
2626 if (f->loadAfter)
2627 *outPtr++ = 'L';
2628 *outPtr++ = ':';
2629 } else {
2630 if (f->input == FROM_SELECTION)
2631 *outPtr++ = 'R';
2632 *outPtr++ = ':';
2633 *outPtr++ = ' ';
2634 *outPtr++ = '{';
2636 *outPtr++ = '\\';
2637 *outPtr++ = 'n';
2638 *outPtr++ = '\\';
2639 *outPtr++ = '\n';
2640 *outPtr++ = '\t';
2641 *outPtr++ = '\t';
2642 for (c=f->cmd; *c!='\0'; c++) { /* Copy the command string, changing */
2643 if (*c == '\\') { /* backslashes to double backslashes */
2644 *outPtr++ = '\\'; /* and newlines to backslash-n's, */
2645 *outPtr++ = '\\'; /* followed by real newlines and tab */
2646 } else if (*c == '\n') {
2647 *outPtr++ = '\\';
2648 *outPtr++ = 'n';
2649 *outPtr++ = '\\';
2650 *outPtr++ = '\n';
2651 *outPtr++ = '\t';
2652 *outPtr++ = '\t';
2653 } else
2654 *outPtr++ = *c;
2656 if (listType == MACRO_CMDS || listType == BG_MENU_CMDS) {
2657 if (*(outPtr-1) == '\t') outPtr--;
2658 *outPtr++ = '}';
2660 *outPtr++ = '\\';
2661 *outPtr++ = 'n';
2662 *outPtr++ = '\\';
2663 *outPtr++ = '\n';
2665 --outPtr;
2666 *--outPtr = '\0';
2667 return outStr;
2670 static int loadMenuItemString(char *inString, menuItemRec **menuItems,
2671 int *nItems, int listType)
2673 menuItemRec *f;
2674 char *cmdStr;
2675 char *inPtr = inString;
2676 char *nameStr, accStr[MAX_ACCEL_LEN], mneChar;
2677 KeySym keysym;
2678 unsigned int modifiers;
2679 int i, input, output, saveFirst, loadAfter, repInput;
2680 int nameLen, accLen, mneLen, cmdLen;
2682 for (;;) {
2684 /* remove leading whitespace */
2685 while (*inPtr == ' ' || *inPtr == '\t')
2686 inPtr++;
2688 /* read name field */
2689 nameLen = strcspn(inPtr, ":");
2690 if (nameLen == 0)
2691 return parseError("no name field");
2692 nameStr = XtMalloc(nameLen+1);
2693 strncpy(nameStr, inPtr, nameLen);
2694 nameStr[nameLen] = '\0';
2695 inPtr += nameLen;
2696 if (*inPtr == '\0')
2697 return parseError("end not expected");
2698 inPtr++;
2700 /* read accelerator field */
2701 accLen = strcspn(inPtr, ":");
2702 if (accLen >= MAX_ACCEL_LEN)
2703 return parseError("accelerator field too long");
2704 strncpy(accStr, inPtr, accLen);
2705 accStr[accLen] = '\0';
2706 inPtr += accLen;
2707 if (*inPtr == '\0')
2708 return parseError("end not expected");
2709 inPtr++;
2711 /* read menemonic field */
2712 mneLen = strcspn(inPtr, ":");
2713 if (mneLen > 1)
2714 return parseError("mnemonic field too long");
2715 if (mneLen == 1)
2716 mneChar = *inPtr++;
2717 else
2718 mneChar = '\0';
2719 inPtr++;
2720 if (*inPtr == '\0')
2721 return parseError("end not expected");
2723 /* read flags field */
2724 input = FROM_NONE;
2725 output = TO_SAME_WINDOW;
2726 repInput = False;
2727 saveFirst = False;
2728 loadAfter = False;
2729 for (; *inPtr != ':'; inPtr++) {
2730 if (listType == SHELL_CMDS) {
2731 if (*inPtr == 'I')
2732 input = FROM_SELECTION;
2733 else if (*inPtr == 'A')
2734 input = FROM_WINDOW;
2735 else if (*inPtr == 'E')
2736 input = FROM_EITHER;
2737 else if (*inPtr == 'W')
2738 output = TO_NEW_WINDOW;
2739 else if (*inPtr == 'D')
2740 output = TO_DIALOG;
2741 else if (*inPtr == 'X')
2742 repInput = True;
2743 else if (*inPtr == 'S')
2744 saveFirst = True;
2745 else if (*inPtr == 'L')
2746 loadAfter = True;
2747 else
2748 return parseError("unreadable flag field");
2749 } else {
2750 if (*inPtr == 'R')
2751 input = FROM_SELECTION;
2752 else
2753 return parseError("unreadable flag field");
2756 inPtr++;
2758 /* read command field */
2759 if (listType == SHELL_CMDS) {
2760 if (*inPtr++ != '\n')
2761 return parseError("command must begin with newline");
2762 while (*inPtr == ' ' || *inPtr == '\t') /* leading whitespace */
2763 inPtr++;
2764 cmdLen = strcspn(inPtr, "\n");
2765 if (cmdLen == 0)
2766 return parseError("shell command field is empty");
2767 cmdStr = XtMalloc(cmdLen+1);
2768 strncpy(cmdStr, inPtr, cmdLen);
2769 cmdStr[cmdLen] = '\0';
2770 inPtr += cmdLen;
2771 } else {
2772 cmdStr = copyMacroToEnd(&inPtr);
2773 if (cmdStr == NULL)
2774 return False;
2776 while (*inPtr == ' ' || *inPtr == '\t' || *inPtr == '\n')
2777 inPtr++; /* skip trailing whitespace & newline */
2779 /* parse the accelerator field */
2780 if (!parseAcceleratorString(accStr, &modifiers, &keysym))
2781 return parseError("couldn't read accelerator field");
2783 /* create a menu item record */
2784 f = (menuItemRec *)XtMalloc(sizeof(menuItemRec));
2785 f->name = nameStr;
2786 f->cmd = cmdStr;
2787 f->mnemonic = mneChar;
2788 f->modifiers = modifiers;
2789 f->input = input;
2790 f->output = output;
2791 f->repInput = repInput;
2792 f->saveFirst = saveFirst;
2793 f->loadAfter = loadAfter;
2794 f->keysym = keysym;
2796 /* add/replace menu record in the list */
2797 for (i=0; i < *nItems; i++) {
2798 if (!strcmp(menuItems[i]->name, f->name)) {
2799 freeMenuItemRec(menuItems[i]);
2800 menuItems[i] = f;
2801 break;
2804 if (i == *nItems)
2805 menuItems[(*nItems)++] = f;
2807 /* end of string in proper place */
2808 if (*inPtr == '\0')
2809 return True;
2813 static int parseError(const char *message)
2815 fprintf(stderr, "NEdit: Parse error in user defined menu item, %s\n",
2816 message);
2817 return False;
2821 ** Create a text string representing an accelerator for the dialog,
2822 ** the shellCommands or macroCommands resource, and for the menu item.
2824 static void generateAcceleratorString(char *text, unsigned int modifiers,
2825 KeySym keysym)
2827 char *shiftStr = "", *ctrlStr = "", *altStr = "";
2828 char *mod2Str = "", *mod3Str = "", *mod4Str = "", *mod5Str = "";
2829 char keyName[20];
2830 Modifiers numLockMask = GetNumLockModMask(TheDisplay);
2832 /* if there's no accelerator, generate an empty string */
2833 if (keysym == NoSymbol) {
2834 *text = '\0';
2835 return;
2839 /* Translate the modifiers into strings.
2840 Lock and NumLock are always ignored (see util/misc.c),
2841 so we don't display them either. */
2842 if (modifiers & ShiftMask)
2843 shiftStr = "Shift+";
2844 if (modifiers & ControlMask)
2845 ctrlStr = "Ctrl+";
2846 if (modifiers & Mod1Mask)
2847 altStr = "Alt+";
2848 if ((modifiers & Mod2Mask) && (Mod2Mask != numLockMask))
2849 mod2Str = "Mod2+";
2850 if ((modifiers & Mod3Mask) && (Mod3Mask != numLockMask))
2851 mod3Str = "Mod3+";
2852 if ((modifiers & Mod4Mask) && (Mod4Mask != numLockMask))
2853 mod4Str = "Mod4+";
2854 if ((modifiers & Mod5Mask) && (Mod5Mask != numLockMask))
2855 mod5Str = "Mod5+";
2857 /* for a consistent look to the accelerator names in the menus,
2858 capitalize the first letter of the keysym */
2859 strcpy(keyName, XKeysymToString(keysym));
2860 *keyName = toupper(*keyName);
2862 /* concatenate the strings together */
2863 sprintf(text, "%s%s%s%s%s%s%s%s", shiftStr, ctrlStr, altStr,
2864 mod2Str, mod3Str, mod4Str, mod5Str, keyName);
2868 ** Create a translation table event description string for the menu
2869 ** XmNaccelerator resource.
2871 static void genAccelEventName(char *text, unsigned int modifiers,
2872 KeySym keysym)
2874 char *shiftStr = "", *lockStr = "", *ctrlStr = "", *altStr = "";
2875 char *mod2Str = "", *mod3Str = "", *mod4Str = "", *mod5Str = "";
2877 /* if there's no accelerator, generate an empty string */
2878 if (keysym == NoSymbol) {
2879 *text = '\0';
2880 return;
2883 /* translate the modifiers into strings */
2884 if (modifiers & ShiftMask)
2885 shiftStr = "Shift ";
2886 if (modifiers & LockMask)
2887 lockStr = "Lock ";
2888 if (modifiers & ControlMask)
2889 ctrlStr = "Ctrl ";
2890 if (modifiers & Mod1Mask)
2891 altStr = "Alt ";
2892 if (modifiers & Mod2Mask)
2893 mod2Str = "Mod2 ";
2894 if (modifiers & Mod3Mask)
2895 mod3Str = "Mod3 ";
2896 if (modifiers & Mod4Mask)
2897 mod4Str = "Mod4 ";
2898 if (modifiers & Mod5Mask)
2899 mod5Str = "Mod5 ";
2901 /* put the modifiers together with the key name */
2902 sprintf(text, "%s%s%s%s%s%s%s%s<Key>%s",
2903 shiftStr, lockStr, ctrlStr, altStr,
2904 mod2Str, mod3Str, mod4Str, mod5Str,
2905 XKeysymToString(keysym));
2909 ** Read an accelerator name and put it into the form of a modifier mask
2910 ** and a KeySym code. Returns false if string can't be read
2911 ** ... does not handle whitespace in string (look at scanf)
2913 static int parseAcceleratorString(const char *string, unsigned int *modifiers,
2914 KeySym *keysym)
2916 int i, nFields, inputLength = strlen(string);
2917 char fields[10][MAX_ACCEL_LEN];
2919 /* a blank field means no accelerator */
2920 if (inputLength == 0) {
2921 *modifiers = 0;
2922 *keysym = NoSymbol;
2923 return True;
2926 /* limit the string length so no field strings will overflow */
2927 if (inputLength > MAX_ACCEL_LEN)
2928 return False;
2930 /* divide the input into '+' separated fields */
2931 nFields = sscanf(string, "%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]",
2932 fields[0], fields[1], fields[2], fields[3], fields[4], fields[5],
2933 fields[6], fields[7], fields[8], fields[9]);
2934 if (nFields == 0)
2935 return False;
2937 /* get the key name from the last field and translate it to a keysym.
2938 If the name is capitalized, try it lowercase as well, since some
2939 of the keysyms are "prettied up" by generateAcceleratorString */
2940 *keysym = XStringToKeysym(fields[nFields-1]);
2941 if (*keysym == NoSymbol) {
2942 *fields[nFields-1] = tolower(*fields[nFields-1]);
2943 *keysym = XStringToKeysym(fields[nFields-1]);
2944 if (*keysym == NoSymbol)
2945 return False;
2948 /* parse the modifier names from the rest of the fields */
2949 *modifiers = 0;
2950 for (i=0; i<nFields-1; i++) {
2951 if (!strcmp(fields[i], "Shift"))
2952 *modifiers |= ShiftMask;
2953 else if (!strcmp(fields[i], "Lock"))
2954 *modifiers |= LockMask;
2955 else if (!strcmp(fields[i], "Ctrl"))
2956 *modifiers |= ControlMask;
2957 /* comparision with "Alt" for compatibility with old .nedit files*/
2958 else if (!strcmp(fields[i], "Alt"))
2959 *modifiers |= Mod1Mask;
2960 else if (!strcmp(fields[i], "Mod2"))
2961 *modifiers |= Mod2Mask;
2962 else if (!strcmp(fields[i], "Mod3"))
2963 *modifiers |= Mod3Mask;
2964 else if (!strcmp(fields[i], "Mod4"))
2965 *modifiers |= Mod4Mask;
2966 else if (!strcmp(fields[i], "Mod5"))
2967 *modifiers |= Mod5Mask;
2968 else
2969 return False;
2972 /* all fields successfully parsed */
2973 return True;
2977 ** Scan text from "*inPtr" to the end of macro input (matching brace),
2978 ** advancing inPtr, and return macro text as function return value.
2980 ** This is kind of wastefull in that it throws away the compiled macro,
2981 ** to be re-generated from the text as needed, but compile time is
2982 ** negligible for most macros.
2984 static char *copyMacroToEnd(char **inPtr)
2986 char *retStr, *errMsg, *stoppedAt, *p, *retPtr;
2987 Program *prog;
2989 /* Skip over whitespace to find make sure there's a beginning brace
2990 to anchor the parse (if not, it will take the whole file) */
2991 *inPtr += strspn(*inPtr, " \t\n");
2992 if (**inPtr != '{') {
2993 ParseError(NULL, *inPtr, *inPtr-1, "macro menu item", "expecting '{'");
2994 return NULL;
2997 /* Parse the input */
2998 prog = ParseMacro(*inPtr, &errMsg, &stoppedAt);
2999 if (prog == NULL) {
3000 ParseError(NULL, *inPtr, stoppedAt, "macro menu item", errMsg);
3001 return NULL;
3003 FreeProgram(prog);
3005 /* Copy and return the body of the macro, stripping outer braces and
3006 extra leading tabs added by the writer routine */
3007 (*inPtr)++;
3008 *inPtr += strspn(*inPtr, " \t");
3009 if (**inPtr == '\n') (*inPtr)++;
3010 if (**inPtr == '\t') (*inPtr)++;
3011 if (**inPtr == '\t') (*inPtr)++;
3012 retPtr = retStr = XtMalloc(stoppedAt - *inPtr + 1);
3013 for (p = *inPtr; p < stoppedAt - 1; p++) {
3014 if (!strncmp(p, "\n\t\t", 3)) {
3015 *retPtr++ = '\n';
3016 p += 2;
3017 } else
3018 *retPtr++ = *p;
3020 if (*(retPtr-1) == '\t') retPtr--;
3021 *retPtr = '\0';
3022 *inPtr = stoppedAt;
3023 return retStr;
3027 ** If "*string" is not terminated with a newline character, reallocate the
3028 ** string and add one. (The macro language requires newline terminators for
3029 ** statements, but the text widget doesn't force it like the NEdit text buffer
3030 ** does, so this might avoid some confusion.)
3032 static void addTerminatingNewline(char **string)
3034 char *newString;
3035 int length;
3037 length = strlen(*string);
3038 if ((*string)[length-1] != '\n') {
3039 newString = XtMalloc(length + 2);
3040 strcpy(newString, *string);
3041 newString[length] = '\n';
3042 newString[length+1] = '\0';
3043 XtFree(*string);
3044 *string = newString;
3049 ** Cache user menus:
3050 ** allocate an empty user (shell, macro) menu cache structure
3052 UserMenuCache *CreateUserMenuCache()
3054 /* allocate some memory for the new data structure */
3055 UserMenuCache *cache = (UserMenuCache *)XtMalloc(sizeof(UserMenuCache));
3057 cache->umcLanguageMode = -2;
3058 cache->umcShellMenuCreated = False;
3059 cache->umcMacroMenuCreated = False;
3060 cache->umcShellMenuList.umlNbrItems = 0;
3061 cache->umcShellMenuList.umlItems = NULL;
3062 cache->umcMacroMenuList.umlNbrItems = 0;
3063 cache->umcMacroMenuList.umlItems = NULL;
3065 return cache;
3068 void FreeUserMenuCache(UserMenuCache *cache)
3070 freeUserMenuList(&cache->umcShellMenuList);
3071 freeUserMenuList(&cache->umcMacroMenuList);
3073 XtFree((char *)cache);
3077 ** Cache user menus:
3078 ** init. a user background menu cache structure
3080 void InitUserBGMenuCache(UserBGMenuCache *cache)
3082 cache->ubmcLanguageMode = -2;
3083 cache->ubmcMenuCreated = False;
3084 cache->ubmcMenuList.umlNbrItems = 0;
3085 cache->ubmcMenuList.umlItems = NULL;
3088 void FreeUserBGMenuCache(UserBGMenuCache *cache)
3090 freeUserMenuList(&cache->ubmcMenuList);
3094 ** Cache user menus:
3095 ** Parse given menu item list and setup a user menu info list for
3096 ** management of user menu.
3098 static void parseMenuItemList(menuItemRec **itemList, int nbrOfItems,
3099 userMenuInfo **infoList, userSubMenuCache *subMenus)
3101 int i;
3102 userMenuInfo *info;
3104 /* Allocate storage for structures to keep track of sub-menus */
3105 allocSubMenuCache(subMenus, nbrOfItems);
3107 /* 1st pass: setup user menu info: extract language modes, menu name &
3108 default indication; build user menu ID */
3109 for (i=0; i<nbrOfItems; i++) {
3110 infoList[i] = parseMenuItemRec(itemList[i]);
3111 generateUserMenuId(infoList[i], subMenus);
3114 /* 2nd pass: solve "default" dependencies */
3115 for (i=0; i<nbrOfItems; i++) {
3116 info = infoList[i];
3118 /* If the user menu item is a default one, then scan the list for
3119 items with the same name and a language mode specified.
3120 If one is found, then set the default index to the index of the
3121 current default item. */
3122 if (info->umiIsDefault) {
3123 setDefaultIndex(infoList, nbrOfItems, i);
3129 ** Returns the sub-menu depth (i.e. nesting level) of given
3130 ** menu name.
3132 static int getSubMenuDepth(const char *menuName)
3134 const char *subSep;
3135 int depth = 0;
3137 /* determine sub-menu depth by counting '>' of given "menuName" */
3138 subSep = menuName;
3139 while ((subSep = strchr(subSep, '>')) != NULL ) {
3140 depth ++;
3141 subSep ++;
3144 return depth;
3148 ** Cache user menus:
3149 ** Parse a singe menu item. Allocate & setup a user menu info element
3150 ** holding extracted info.
3152 static userMenuInfo *parseMenuItemRec(menuItemRec *item)
3154 userMenuInfo *newInfo;
3155 int subMenuDepth;
3156 int idSize;
3158 /* allocate a new user menu info element */
3159 newInfo = (userMenuInfo *)XtMalloc(sizeof(userMenuInfo));
3161 /* determine sub-menu depth and allocate some memory
3162 for hierarchical ID; init. ID with {0,.., 0} */
3163 newInfo->umiName = stripLanguageMode(item->name);
3165 subMenuDepth = getSubMenuDepth(newInfo->umiName);
3166 idSize = sizeof(int)*(subMenuDepth+1);
3168 newInfo->umiId = (int *)XtMalloc(idSize);
3169 memset(newInfo->umiId,0,idSize);
3171 /* init. remaining parts of user menu info element */
3172 newInfo->umiIdLen = 0;
3173 newInfo->umiIsDefault = False;
3174 newInfo->umiNbrOfLanguageModes = 0;
3175 newInfo->umiLanguageMode = NULL;
3176 newInfo->umiDefaultIndex = -1;
3177 newInfo->umiToBeManaged = False;
3179 /* assign language mode info to new user menu info element */
3180 parseMenuItemName(item->name, newInfo);
3182 return newInfo;
3186 ** Cache user menus:
3187 ** Extract language mode related info out of given menu item name string.
3188 ** Store this info in given user menu info structure.
3190 static void parseMenuItemName(char *menuItemName, userMenuInfo *info)
3192 char *atPtr, *firstAtPtr, *endPtr;
3193 char c;
3194 int languageMode;
3195 int langModes[MAX_LANGUAGE_MODES];
3196 int nbrLM = 0;
3197 int size;
3199 atPtr = firstAtPtr = strchr(menuItemName, '@');
3200 if (atPtr != NULL) {
3201 if (!strcmp(atPtr+1, "*")) {
3202 /* only language is "*": this is for all but language specific
3203 macros */
3204 info->umiIsDefault = True;
3205 return;
3208 /* setup a list of all language modes related to given menu item */
3209 while (atPtr != NULL) {
3210 /* extract language mode name after "@" sign */
3211 for(endPtr=atPtr+1; isalnum((unsigned char)*endPtr) || *endPtr=='_' ||
3212 *endPtr=='-' || *endPtr==' ' || *endPtr=='+' || *endPtr=='$' ||
3213 *endPtr=='#'; endPtr++);
3215 /* lookup corresponding language mode index; if PLAIN is
3216 returned then this means, that language mode name after
3217 "@" is unknown (i.e. not defined) */
3218 c = *endPtr;
3219 *endPtr = '\0';
3220 languageMode = FindLanguageMode(atPtr+1);
3221 if (languageMode == PLAIN_LANGUAGE_MODE) {
3222 langModes[nbrLM] = UNKNOWN_LANGUAGE_MODE;
3223 } else {
3224 langModes[nbrLM] = languageMode;
3226 nbrLM ++;
3227 *endPtr = c;
3229 /* look for next "@" */
3230 atPtr = strchr(endPtr, '@');
3233 if (nbrLM != 0) {
3234 info->umiNbrOfLanguageModes = nbrLM;
3235 size = sizeof(int)*nbrLM;
3236 info->umiLanguageMode = (int *)XtMalloc(size);
3237 memcpy(info->umiLanguageMode, langModes, size);
3243 ** Cache user menus:
3244 ** generates an ID (= array of integers) of given user menu info, which
3245 ** allows to find the user menu item within the menu tree later on: 1st
3246 ** integer of ID indicates position within main menu; 2nd integer indicates
3247 ** position within 1st sub-menu etc.
3249 static void generateUserMenuId(userMenuInfo *info, userSubMenuCache *subMenus)
3251 int idSize;
3252 char *hierName, *subSep;
3253 int subMenuDepth = 0;
3254 int *menuIdx = &subMenus->usmcNbrOfMainMenuItems;
3255 userSubMenuInfo *curSubMenu;
3257 /* find sub-menus, stripping off '>' until item name is
3258 reached */
3259 subSep = info->umiName;
3260 while ((subSep = strchr(subSep, '>')) != NULL) {
3261 hierName = copySubstring(info->umiName, subSep - info->umiName);
3262 curSubMenu = findSubMenuInfo(subMenus, hierName);
3263 if (curSubMenu == NULL) {
3264 /* sub-menu info not stored before: new sub-menu;
3265 remember its hierarchical position */
3266 info->umiId[subMenuDepth] = *menuIdx;
3267 (*menuIdx) ++;
3269 /* store sub-menu info in list of subMenus; allocate
3270 some memory for hierarchical ID of sub-menu & take over
3271 current hierarchical ID of current user menu info */
3272 curSubMenu = &subMenus->usmcInfo[subMenus->usmcNbrOfSubMenus];
3273 subMenus->usmcNbrOfSubMenus ++;
3274 curSubMenu->usmiName = hierName;
3275 idSize = sizeof(int)*(subMenuDepth+2);
3276 curSubMenu->usmiId = (int *)XtMalloc(idSize);
3277 memcpy(curSubMenu->usmiId, info->umiId, idSize);
3278 curSubMenu->usmiIdLen = subMenuDepth+1;
3279 } else {
3280 /* sub-menu info already stored before: takeover its
3281 hierarchical position */
3282 XtFree(hierName);
3283 info->umiId[subMenuDepth] = curSubMenu->usmiId[subMenuDepth];
3286 subMenuDepth ++;
3287 menuIdx = &curSubMenu->usmiId[subMenuDepth];
3289 subSep ++;
3292 /* remember position of menu item within final (sub) menu */
3293 info->umiId[subMenuDepth] = *menuIdx;
3294 info->umiIdLen = subMenuDepth + 1;
3295 (*menuIdx) ++;
3299 ** Cache user menus:
3300 ** Find info corresponding to a hierarchical menu name (a>b>c...)
3302 static userSubMenuInfo *findSubMenuInfo(userSubMenuCache *subMenus,
3303 const char *hierName)
3305 int i;
3307 for (i=0; i<subMenus->usmcNbrOfSubMenus; i++)
3308 if (!strcmp(hierName, subMenus->usmcInfo[i].usmiName))
3309 return &subMenus->usmcInfo[i];
3310 return NULL;
3314 ** Cache user menus:
3315 ** Returns an allocated copy of menuItemName stripped of language mode
3316 ** parts (i.e. parts starting with "@").
3318 static char *stripLanguageMode(const char *menuItemName)
3320 char *firstAtPtr;
3322 firstAtPtr = strchr(menuItemName, '@');
3323 if (firstAtPtr == NULL)
3324 return XtNewString(menuItemName);
3325 else
3326 return copySubstring(menuItemName, firstAtPtr-menuItemName);
3329 static void setDefaultIndex(userMenuInfo **infoList, int nbrOfItems,
3330 int defaultIdx)
3332 char *defaultMenuName = infoList[defaultIdx]->umiName;
3333 int i;
3334 userMenuInfo *info;
3336 /* Scan the list for items with the same name and a language mode
3337 specified. If one is found, then set the default index to the
3338 index of the current default item. */
3339 for (i=0; i<nbrOfItems; i++) {
3340 info = infoList[i];
3342 if (!info->umiIsDefault && strcmp(info->umiName, defaultMenuName)==0) {
3343 info->umiDefaultIndex = defaultIdx;
3349 ** Determine the info list menu items, which need to be managed
3350 ** for given language mode. Set / reset "to be managed" indication
3351 ** of info list items accordingly.
3353 static void applyLangModeToUserMenuInfo(userMenuInfo **infoList, int nbrOfItems,
3354 int languageMode)
3356 int i;
3357 userMenuInfo *info;
3359 /* 1st pass: mark all items as "to be managed", which are applicable
3360 for all language modes or which are indicated as "default" items */
3361 for (i=0; i<nbrOfItems; i++) {
3362 info = infoList[i];
3364 info->umiToBeManaged =
3365 (info->umiNbrOfLanguageModes == 0 || info->umiIsDefault);
3368 /* 2nd pass: mark language mode specific items matching given language
3369 mode as "to be managed". Reset "to be managed" indications of
3370 "default" items, if applicable */
3371 for (i=0; i<nbrOfItems; i++) {
3372 info = infoList[i];
3374 if (info->umiNbrOfLanguageModes != 0) {
3375 if (doesLanguageModeMatch(info, languageMode)) {
3376 info->umiToBeManaged = True;
3378 if (info->umiDefaultIndex != -1)
3379 infoList[info->umiDefaultIndex]->umiToBeManaged = False;
3386 ** Returns true, if given user menu info is applicable for given language mode
3388 static int doesLanguageModeMatch(userMenuInfo *info, int languageMode)
3390 int i;
3392 for (i=0; i<info->umiNbrOfLanguageModes; i++) {
3393 if (info->umiLanguageMode[i] == languageMode)
3394 return True;
3397 return False;
3400 static void freeUserMenuInfoList(userMenuInfo **infoList, int nbrOfItems)
3402 int i;
3404 for (i=0; i<nbrOfItems; i++) {
3405 freeUserMenuInfo(infoList[i]);
3409 static void freeUserMenuInfo(userMenuInfo *info)
3411 XtFree(info->umiName);
3413 XtFree((char *)info->umiId);
3415 if (info->umiNbrOfLanguageModes != 0)
3416 XtFree((char *)info->umiLanguageMode);
3418 XtFree((char *)info);
3422 ** Cache user menus:
3423 ** Allocate & init. storage for structures to manage sub-menus
3425 static void allocSubMenuCache(userSubMenuCache *subMenus, int nbrOfItems)
3427 int size = sizeof(userSubMenuInfo) * nbrOfItems;
3429 subMenus->usmcNbrOfMainMenuItems = 0;
3430 subMenus->usmcNbrOfSubMenus = 0;
3431 subMenus->usmcInfo = (userSubMenuInfo *)XtMalloc(size);
3434 static void freeSubMenuCache(userSubMenuCache *subMenus)
3436 int i;
3438 for (i=0; i<subMenus->usmcNbrOfSubMenus; i++) {
3439 XtFree(subMenus->usmcInfo[i].usmiName);
3440 XtFree((char *)subMenus->usmcInfo[i].usmiId);
3443 XtFree((char *)subMenus->usmcInfo);
3446 static void allocUserMenuList(UserMenuList *list, int nbrOfItems)
3448 int size = sizeof(UserMenuListElement *) * nbrOfItems;
3450 list->umlNbrItems = 0;
3451 list->umlItems = (UserMenuListElement **)XtMalloc(size);
3454 static void freeUserMenuList(UserMenuList *list)
3456 int i;
3458 for (i=0; i<list->umlNbrItems; i++)
3459 freeUserMenuListElement(list->umlItems[i]);
3461 list->umlNbrItems = 0;
3463 if (list->umlItems != NULL) {
3464 XtFree((char *)list->umlItems);
3465 list->umlItems = NULL;
3469 static UserMenuListElement *allocUserMenuListElement(Widget menuItem, char *accKeys)
3471 UserMenuListElement *element;
3473 element = (UserMenuListElement *)XtMalloc(sizeof(UserMenuListElement));
3475 element->umleManageMode = UMMM_UNMANAGE;
3476 element->umlePrevManageMode = UMMM_UNMANAGE;
3477 element->umleAccKeys = accKeys;
3478 element->umleAccLockPatchApplied = False;
3479 element->umleMenuItem = menuItem;
3480 element->umleSubMenuPane = NULL;
3481 element->umleSubMenuList = NULL;
3483 return element;
3486 static void freeUserMenuListElement(UserMenuListElement *element)
3488 if (element->umleSubMenuList != NULL)
3489 freeUserSubMenuList(element->umleSubMenuList);
3491 if (element->umleAccKeys != NULL)
3492 XtFree(element->umleAccKeys);
3494 XtFree((char *)element);
3497 static UserMenuList *allocUserSubMenuList(int nbrOfItems)
3499 UserMenuList *list;
3501 list = (UserMenuList *)XtMalloc(sizeof(UserMenuList));
3503 allocUserMenuList(list, nbrOfItems);
3505 return list;
3508 static void freeUserSubMenuList(UserMenuList *list)
3510 freeUserMenuList(list);
3512 XtFree((char *)list);