Remove sprintf call - this won't compile on many platforms.
[nedit.git] / source / userCmds.c
bloba556ba20c1c61113b45422ed65cb57da0912095d
1 static const char CVSID[] = "$Id: userCmds.c,v 1.51 2004/10/15 18:04:22 arnef Exp $";
2 /*******************************************************************************
3 * *
4 * userCmds.c -- Nirvana Editor shell and macro command dialogs *
5 * *
6 * Copyright (C) 1999 Mark Edel *
7 * *
8 * This is free software; you can redistribute it and/or modify it under the *
9 * terms of the GNU General Public License as published by the Free Software *
10 * Foundation; either version 2 of the License, or (at your option) any later *
11 * version. In addition, you may distribute version of this program linked to *
12 * Motif or Open Motif. See README for details. *
13 * *
14 * This software is distributed in the hope that it will be useful, but WITHOUT *
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
17 * for more details. *
18 * *
19 * You should have received a copy of the GNU General Public License along with *
20 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
21 * Place, Suite 330, Boston, MA 02111-1307 USA *
22 * *
23 * Nirvana Text Editor *
24 * April, 1997 *
25 * *
26 * Written by Mark Edel *
27 * *
28 *******************************************************************************/
30 #ifdef HAVE_CONFIG_H
31 #include "../config.h"
32 #endif
34 #include "userCmds.h"
35 #include "textBuf.h"
36 #include "text.h"
37 #include "nedit.h"
38 #include "preferences.h"
39 #include "window.h"
40 #include "menu.h"
41 #include "shell.h"
42 #include "macro.h"
43 #include "file.h"
44 #include "interpret.h"
45 #include "parse.h"
46 #include "../util/DialogF.h"
47 #include "../util/misc.h"
48 #include "../util/managedList.h"
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <ctype.h>
54 #ifdef VMS
55 #include "../util/VMSparam.h"
56 #else
57 #ifndef __MVS__
58 #include <sys/param.h>
59 #endif
60 #endif /*VMS*/
62 #include <Xm/Xm.h>
63 #include <X11/keysym.h>
64 #include <X11/IntrinsicP.h>
65 #include <Xm/Text.h>
66 #include <Xm/Form.h>
67 #include <Xm/List.h>
68 #include <Xm/LabelG.h>
69 #include <Xm/PushB.h>
70 #include <Xm/ToggleB.h>
71 #include <Xm/SelectioB.h>
72 #include <Xm/RowColumn.h>
73 #include <Xm/CascadeB.h>
74 #include <Xm/MenuShell.h>
76 #ifdef HAVE_DEBUG_H
77 #include "../debug.h"
78 #endif
80 #if XmVersion >= 1002
81 #define MENU_WIDGET(w) (XmGetPostedFromWidget(XtParent(w)))
82 #else
83 #define MENU_WIDGET(w) (w)
84 #endif
86 extern void _XmDismissTearOff(Widget w, XtPointer call, XtPointer x);
88 /* max. number of user programmable menu commands allowed per each of the
89 macro, shell, and background menus */
90 #define MAX_ITEMS_PER_MENU 400
92 /* indicates, that an unknown (i.e. not existing) language mode
93 is bound to an user menu item */
94 #define UNKNOWN_LANGUAGE_MODE -2
96 /* major divisions (in position units) in User Commands dialogs */
97 #define LEFT_MARGIN_POS 1
98 #define RIGHT_MARGIN_POS 99
99 #define LIST_RIGHT 45
100 #define SHELL_CMD_TOP 70
101 #define MACRO_CMD_TOP 40
103 /* types of current dialog and/or menu */
104 enum dialogTypes {SHELL_CMDS, MACRO_CMDS, BG_MENU_CMDS};
106 /* Structure representing a menu item for shell, macro and BG menus*/
107 typedef struct {
108 char *name;
109 unsigned int modifiers;
110 KeySym keysym;
111 char mnemonic;
112 char input;
113 char output;
114 char repInput;
115 char saveFirst;
116 char loadAfter;
117 char *cmd;
118 } menuItemRec;
120 /* Structure for widgets and flags associated with shell command,
121 macro command and BG command editing dialogs */
122 typedef struct {
123 int dialogType;
124 WindowInfo *window;
125 Widget nameTextW, accTextW, mneTextW, cmdTextW, saveFirstBtn;
126 Widget loadAfterBtn, selInpBtn, winInpBtn, eitherInpBtn, noInpBtn;
127 Widget repInpBtn, sameOutBtn, dlogOutBtn, winOutBtn, dlogShell;
128 Widget managedList;
129 menuItemRec **menuItemsList;
130 int nMenuItems;
131 } userCmdDialog;
133 /* Structure for keeping track of hierarchical sub-menus during user-menu
134 creation */
135 typedef struct {
136 char *name;
137 Widget menuPane;
138 } menuTreeItem;
140 /* Structure holding hierarchical info about one sub-menu.
142 Suppose following user menu items:
143 a.) "menuItem1"
144 b.) "subMenuA>menuItemA1"
145 c.) "subMenuA>menuItemA2"
146 d.) "subMenuA>subMenuB>menuItemB1"
147 e.) "subMenuA>subMenuB>menuItemB2"
149 Structure of this user menu is:
151 Main Menu Name Sub-Menu A Name Sub-Menu B Name
152 element nbr. element nbr. element nbr.
153 0 menuItem1
154 1 subMenuA --+-> 0 menuItemA1
155 +-> 1 menuItemA2
156 +-> 2 subMenuB --+-> 0 menuItemB1
157 +-> 1 menuItemB2
159 Above example holds 2 sub-menus:
160 1.) "subMenuA" (hierarchical ID = {1} means: element nbr. "1" of main menu)
161 2.) "subMenuA>subMenuB" (hierarchical ID = {1, 2} means: el. nbr. "2" of
162 "subMenuA", which itself is el. nbr. "0" of main menu) */
163 typedef struct {
164 char *usmiName; /* hierarchical name of sub-menu */
165 int *usmiId; /* hierarchical ID of sub-menu */
166 int usmiIdLen; /* length of hierarchical ID */
167 } userSubMenuInfo;
169 /* Holds info about sub-menu structure of an user menu */
170 typedef struct {
171 int usmcNbrOfMainMenuItems; /* number of main menu items */
172 int usmcNbrOfSubMenus; /* number of sub-menus */
173 userSubMenuInfo *usmcInfo; /* list of sub-menu info */
174 } userSubMenuCache;
176 /* Structure holding info about a single menu item.
177 According to above example there exist 5 user menu items:
178 a.) "menuItem1" (hierarchical ID = {0} means: element nbr. "0" of main menu)
179 b.) "menuItemA1" (hierarchical ID = {1, 0} means: el. nbr. "0" of
180 "subMenuA", which itself is el. nbr. "1" of main menu)
181 c.) "menuItemA2" (hierarchical ID = {1, 1})
182 d.) "menuItemB1" (hierarchical ID = {1, 2, 0})
183 e.) "menuItemB2" (hierarchical ID = {1, 2, 1})
185 typedef struct {
186 char *umiName; /* hierarchical name of menu item
187 (w.o. language mode info) */
188 int *umiId; /* hierarchical ID of menu item */
189 int umiIdLen; /* length of hierarchical ID */
190 Boolean umiIsDefault; /* menu item is default one ("@*") */
191 int umiNbrOfLanguageModes; /* number of language modes
192 applicable for this menu item */
193 int *umiLanguageMode; /* list of applicable lang. modes */
194 int umiDefaultIndex; /* array index of menu item to be
195 used as default, if no lang. mode
196 matches */
197 Boolean umiToBeManaged; /* indicates, that menu item needs
198 to be managed */
199 } userMenuInfo;
201 /* Structure holding info about a selected user menu (shell, macro or
202 background) */
203 typedef struct {
204 int sumType; /* type of menu (shell, macro or
205 background */
206 Widget sumMenuPane; /* pane of main menu */
207 int sumNbrOfListItems; /* number of menu items */
208 menuItemRec **sumItemList; /* list of menu items */
209 userMenuInfo **sumInfoList; /* list of infos about menu items */
210 userSubMenuCache *sumSubMenus; /* info about sub-menu structure */
211 UserMenuList *sumMainMenuList; /* cached info about main menu */
212 Boolean *sumMenuCreated; /* pointer to "menu created"
213 indicator */
214 } selectedUserMenu;
216 /* Descriptions of the current user programmed menu items for re-generating
217 menus and processing shell, macro, and background menu selections */
218 static menuItemRec *ShellMenuItems[MAX_ITEMS_PER_MENU];
219 static userMenuInfo *ShellMenuInfo[MAX_ITEMS_PER_MENU];
220 static userSubMenuCache ShellSubMenus;
221 static int NShellMenuItems = 0;
222 static menuItemRec *MacroMenuItems[MAX_ITEMS_PER_MENU];
223 static userMenuInfo *MacroMenuInfo[MAX_ITEMS_PER_MENU];
224 static userSubMenuCache MacroSubMenus;
225 static int NMacroMenuItems = 0;
226 static menuItemRec *BGMenuItems[MAX_ITEMS_PER_MENU];
227 static userMenuInfo *BGMenuInfo[MAX_ITEMS_PER_MENU];
228 static userSubMenuCache BGSubMenus;
229 static int NBGMenuItems = 0;
231 /* Top level shells of the user-defined menu editing dialogs */
232 static Widget ShellCmdDialog = NULL;
233 static Widget MacroCmdDialog = NULL;
234 static Widget BGMenuCmdDialog = NULL;
236 /* Paste learn/replay sequence buttons in user defined menu editing dialogs
237 (for dimming and undimming externally when replay macro is available) */
238 static Widget MacroPasteReplayBtn = NULL;
239 static Widget BGMenuPasteReplayBtn = NULL;
241 static void editMacroOrBGMenu(WindowInfo *window, int dialogType);
242 static void dimSelDepItemsInMenu(Widget menuPane, menuItemRec **menuList,
243 int nMenuItems, int sensitive);
244 static void rebuildMenuOfAllWindows(int menuType);
245 static void rebuildMenu(WindowInfo *window, int menuType);
246 static Widget findInMenuTree(menuTreeItem *menuTree, int nTreeEntries,
247 const char *hierName);
248 static char *copySubstring(const char *string, int length);
249 static Widget createUserMenuItem(Widget menuPane, char *name, menuItemRec *f,
250 int index, XtCallbackProc cbRtn, XtPointer cbArg);
251 static Widget createUserSubMenu(Widget parent, char *label, Widget *menuItem);
252 static void deleteMenuItems(Widget menuPane);
253 static void selectUserMenu(WindowInfo *window, int menuType, selectedUserMenu *menu);
254 static void updateMenu(WindowInfo *window, int menuType);
255 static void manageTearOffMenu(Widget menuPane);
256 static void resetManageMode(UserMenuList *list);
257 static void manageAllSubMenuWidgets(UserMenuListElement *subMenu);
258 static void unmanageAllSubMenuWidgets(UserMenuListElement *subMenu);
259 static void manageMenuWidgets(UserMenuList *list);
260 static void removeAccelFromMenuWidgets(UserMenuList *menuList);
261 static void assignAccelToMenuWidgets(UserMenuList *menuList, WindowInfo *window);
262 static void manageUserMenu(selectedUserMenu *menu, WindowInfo *window);
263 static void createMenuItems(WindowInfo *window, selectedUserMenu *menu);
264 static void okCB(Widget w, XtPointer clientData, XtPointer callData);
265 static void applyCB(Widget w, XtPointer clientData, XtPointer callData);
266 static void checkCB(Widget w, XtPointer clientData, XtPointer callData);
267 static int checkMacro(userCmdDialog *ucd);
268 static int checkMacroText(char *macro, Widget errorParent, Widget errFocus);
269 static int applyDialogChanges(userCmdDialog *ucd);
270 static void closeCB(Widget w, XtPointer clientData, XtPointer callData);
271 static void pasteReplayCB(Widget w, XtPointer clientData, XtPointer callData);
272 static void destroyCB(Widget w, XtPointer clientData, XtPointer callData);
273 static void accKeyCB(Widget w, XtPointer clientData, XKeyEvent *event);
274 static void sameOutCB(Widget w, XtPointer clientData, XtPointer callData);
275 static void shellMenuCB(Widget w, WindowInfo *window, XtPointer callData);
276 static void macroMenuCB(Widget w, WindowInfo *window, XtPointer callData);
277 static void bgMenuCB(Widget w, WindowInfo *window, XtPointer callData) ;
278 static void accFocusCB(Widget w, XtPointer clientData, XtPointer callData);
279 static void accLoseFocusCB(Widget w, XtPointer clientData,
280 XtPointer callData);
281 static void updateDialogFields(menuItemRec *f, userCmdDialog *ucd);
282 static menuItemRec *readDialogFields(userCmdDialog *ucd, int silent);
283 static menuItemRec *copyMenuItemRec(menuItemRec *item);
284 static void freeMenuItemRec(menuItemRec *item);
285 static void *getDialogDataCB(void *oldItem, int explicitRequest, int *abort,
286 void *cbArg);
287 static void setDialogDataCB(void *item, void *cbArg);
288 static void freeItemCB(void *item);
289 static int dialogFieldsAreEmpty(userCmdDialog *ucd);
290 static void disableTextW(Widget textW);
291 static char *writeMenuItemString(menuItemRec **menuItems, int nItems,
292 int listType);
293 static int loadMenuItemString(char *inString, menuItemRec **menuItems,
294 int *nItems, int listType);
295 static void generateAcceleratorString(char *text, unsigned int modifiers,
296 KeySym keysym);
297 static void genAccelEventName(char *text, unsigned int modifiers,
298 KeySym keysym);
299 static int parseAcceleratorString(const char *string, unsigned int *modifiers,
300 KeySym *keysym);
301 static int parseError(const char *message);
302 static char *copyMacroToEnd(char **inPtr);
303 static void addTerminatingNewline(char **string);
304 static void parseMenuItemList(menuItemRec **itemList, int nbrOfItems,
305 userMenuInfo **infoList, userSubMenuCache *subMenus);
306 static int getSubMenuDepth(const char *menuName);
307 static userMenuInfo *parseMenuItemRec(menuItemRec *item);
308 static void parseMenuItemName(char *menuItemName, userMenuInfo *info);
309 static void generateUserMenuId(userMenuInfo *info, userSubMenuCache *subMenus);
310 static userSubMenuInfo *findSubMenuInfo(userSubMenuCache *subMenus,
311 const char *hierName);
312 static char *stripLanguageMode(const char *menuItemName);
313 static void setDefaultIndex(userMenuInfo **infoList, int nbrOfItems,
314 int defaultIdx);
315 static void applyLangModeToUserMenuInfo(userMenuInfo **infoList, int nbrOfItems,
316 int languageMode);
317 static int doesLanguageModeMatch(userMenuInfo *info, int languageMode);
318 static void freeUserMenuInfoList(userMenuInfo **infoList, int nbrOfItems);
319 static void freeUserMenuInfo(userMenuInfo *info);
320 static void allocSubMenuCache(userSubMenuCache *subMenus, int nbrOfItems);
321 static void freeSubMenuCache(userSubMenuCache *subMenus);
322 static void allocUserMenuList(UserMenuList *list, int nbrOfItems);
323 static void freeUserMenuList(UserMenuList *list);
324 static UserMenuListElement *allocUserMenuListElement(Widget menuItem, char *accKeys);
325 static void freeUserMenuListElement(UserMenuListElement *element);
326 static UserMenuList *allocUserSubMenuList(int nbrOfItems);
327 static void freeUserSubMenuList(UserMenuList *list);
330 ** Present a dialog for editing the user specified commands in the shell menu
332 void EditShellMenu(WindowInfo *window)
334 Widget form, accLabel, inpLabel, inpBox, outBox, outLabel;
335 Widget nameLabel, cmdLabel, okBtn, applyBtn, closeBtn;
336 userCmdDialog *ucd;
337 XmString s1;
338 int ac, i;
339 Arg args[20];
341 /* if the dialog is already displayed, just pop it to the top and return */
342 if (ShellCmdDialog != NULL) {
343 RaiseShellWindow(ShellCmdDialog);
344 return;
347 /* Create a structure for keeping track of dialog state */
348 ucd = (userCmdDialog *)XtMalloc(sizeof(userCmdDialog));
349 ucd->window = window;
351 /* Set the dialog to operate on the Shell menu */
352 ucd->menuItemsList = (menuItemRec **)XtMalloc(sizeof(menuItemRec *) *
353 MAX_ITEMS_PER_MENU);
354 for (i=0; i<NShellMenuItems; i++)
355 ucd->menuItemsList[i] = copyMenuItemRec(ShellMenuItems[i]);
356 ucd->nMenuItems = NShellMenuItems;
357 ucd->dialogType = SHELL_CMDS;
359 ac = 0;
360 XtSetArg(args[ac], XmNdeleteResponse, XmDO_NOTHING); ac++;
361 XtSetArg(args[ac], XmNiconName, "NEdit Shell Menu"); ac++;
362 XtSetArg(args[ac], XmNtitle, "Shell Menu"); ac++;
363 ucd->dlogShell = CreateWidget(TheAppShell, "shellCommands",
364 topLevelShellWidgetClass, args, ac);
365 AddSmallIcon(ucd->dlogShell);
366 form = XtVaCreateManagedWidget("editShellCommands", xmFormWidgetClass,
367 ucd->dlogShell, XmNautoUnmanage, False,
368 XmNresizePolicy, XmRESIZE_NONE, NULL);
369 ShellCmdDialog = ucd->dlogShell;
370 XtAddCallback(form, XmNdestroyCallback, destroyCB, ucd);
371 AddMotifCloseCallback(ucd->dlogShell, closeCB, ucd);
373 ac = 0;
374 XtSetArg(args[ac], XmNtopAttachment, XmATTACH_POSITION); ac++;
375 XtSetArg(args[ac], XmNtopPosition, 2); ac++;
376 XtSetArg(args[ac], XmNleftAttachment, XmATTACH_POSITION); ac++;
377 XtSetArg(args[ac], XmNleftPosition, LEFT_MARGIN_POS); ac++;
378 XtSetArg(args[ac], XmNrightAttachment, XmATTACH_POSITION); ac++;
379 XtSetArg(args[ac], XmNrightPosition, LIST_RIGHT-1); ac++;
380 XtSetArg(args[ac], XmNbottomAttachment, XmATTACH_POSITION); ac++;
381 XtSetArg(args[ac], XmNbottomPosition, SHELL_CMD_TOP); ac++;
382 ucd->managedList = CreateManagedList(form, "list", args, ac,
383 (void **)ucd->menuItemsList, &ucd->nMenuItems, MAX_ITEMS_PER_MENU,
384 20, getDialogDataCB, ucd, setDialogDataCB, ucd, freeItemCB);
386 ucd->loadAfterBtn = XtVaCreateManagedWidget("loadAfterBtn",
387 xmToggleButtonWidgetClass, form,
388 XmNlabelString, s1=MKSTRING("Re-load file after executing command"),
389 XmNmnemonic, 'R',
390 XmNalignment, XmALIGNMENT_BEGINNING,
391 XmNset, False,
392 XmNleftAttachment, XmATTACH_POSITION,
393 XmNleftPosition, LIST_RIGHT,
394 XmNrightAttachment, XmATTACH_POSITION,
395 XmNrightPosition, RIGHT_MARGIN_POS,
396 XmNbottomAttachment, XmATTACH_POSITION,
397 XmNbottomPosition, SHELL_CMD_TOP, NULL);
398 XmStringFree(s1);
399 ucd->saveFirstBtn = XtVaCreateManagedWidget("saveFirstBtn",
400 xmToggleButtonWidgetClass, form,
401 XmNlabelString, s1=MKSTRING("Save file before executing command"),
402 XmNmnemonic, 'f',
403 XmNalignment, XmALIGNMENT_BEGINNING,
404 XmNset, False,
405 XmNleftAttachment, XmATTACH_POSITION,
406 XmNleftPosition, LIST_RIGHT,
407 XmNrightAttachment, XmATTACH_POSITION,
408 XmNrightPosition, RIGHT_MARGIN_POS,
409 XmNbottomAttachment, XmATTACH_WIDGET,
410 XmNbottomWidget, ucd->loadAfterBtn, NULL);
411 XmStringFree(s1);
412 ucd->repInpBtn = XtVaCreateManagedWidget("repInpBtn",
413 xmToggleButtonWidgetClass, form,
414 XmNlabelString, s1=MKSTRING("Output replaces input"),
415 XmNmnemonic, 'f',
416 XmNalignment, XmALIGNMENT_BEGINNING,
417 XmNset, False,
418 XmNleftAttachment, XmATTACH_POSITION,
419 XmNleftPosition, LIST_RIGHT,
420 XmNrightAttachment, XmATTACH_POSITION,
421 XmNrightPosition, RIGHT_MARGIN_POS,
422 XmNbottomAttachment, XmATTACH_WIDGET,
423 XmNbottomWidget, ucd->saveFirstBtn, NULL);
424 XmStringFree(s1);
425 outBox = XtVaCreateManagedWidget("outBox", xmRowColumnWidgetClass, form,
426 XmNpacking, XmPACK_TIGHT,
427 XmNorientation, XmHORIZONTAL,
428 XmNradioBehavior, True,
429 XmNradioAlwaysOne, True,
430 XmNleftAttachment, XmATTACH_POSITION,
431 XmNleftPosition, LIST_RIGHT + 2,
432 XmNrightAttachment, XmATTACH_POSITION,
433 XmNrightPosition, RIGHT_MARGIN_POS,
434 XmNbottomAttachment, XmATTACH_WIDGET,
435 XmNbottomWidget, ucd->repInpBtn,
436 XmNbottomOffset, 4, NULL);
437 ucd->sameOutBtn = XtVaCreateManagedWidget("sameOutBtn",
438 xmToggleButtonWidgetClass, outBox,
439 XmNlabelString, s1=MKSTRING("same document"),
440 XmNmnemonic, 'm',
441 XmNalignment, XmALIGNMENT_BEGINNING,
442 XmNmarginHeight, 0,
443 XmNset, True, NULL);
444 XmStringFree(s1);
445 XtAddCallback(ucd->sameOutBtn, XmNvalueChangedCallback, sameOutCB, ucd);
446 ucd->dlogOutBtn = XtVaCreateManagedWidget("dlogOutBtn",
447 xmToggleButtonWidgetClass, outBox,
448 XmNlabelString, s1=MKSTRING("dialog"),
449 XmNmnemonic, 'g',
450 XmNalignment, XmALIGNMENT_BEGINNING,
451 XmNmarginHeight, 0,
452 XmNset, False, NULL);
453 XmStringFree(s1);
454 ucd->winOutBtn = XtVaCreateManagedWidget("winOutBtn", xmToggleButtonWidgetClass,
455 outBox,
456 XmNlabelString, s1=MKSTRING("new document"),
457 XmNmnemonic, 'n',
458 XmNalignment, XmALIGNMENT_BEGINNING,
459 XmNmarginHeight, 0,
460 XmNset, False, NULL);
461 XmStringFree(s1);
462 outLabel = XtVaCreateManagedWidget("outLabel", xmLabelGadgetClass, form,
463 XmNlabelString, s1=MKSTRING("Command Output:"),
464 XmNalignment, XmALIGNMENT_BEGINNING,
465 XmNmarginTop, 5,
466 XmNleftAttachment, XmATTACH_POSITION,
467 XmNleftPosition, LIST_RIGHT,
468 XmNrightAttachment, XmATTACH_POSITION,
469 XmNrightPosition, RIGHT_MARGIN_POS,
470 XmNbottomAttachment, XmATTACH_WIDGET,
471 XmNbottomWidget, outBox, NULL);
472 XmStringFree(s1);
474 inpBox = XtVaCreateManagedWidget("inpBox", xmRowColumnWidgetClass, form,
475 XmNpacking, XmPACK_TIGHT,
476 XmNorientation, XmHORIZONTAL,
477 XmNradioBehavior, True,
478 XmNradioAlwaysOne, True,
479 XmNleftAttachment, XmATTACH_POSITION,
480 XmNleftPosition, LIST_RIGHT + 2,
481 XmNrightAttachment, XmATTACH_POSITION,
482 XmNrightPosition, RIGHT_MARGIN_POS,
483 XmNbottomAttachment, XmATTACH_WIDGET,
484 XmNbottomWidget, outLabel, NULL);
485 ucd->selInpBtn = XtVaCreateManagedWidget("selInpBtn", xmToggleButtonWidgetClass,
486 inpBox,
487 XmNlabelString, s1=MKSTRING("selection"),
488 XmNmnemonic, 's',
489 XmNalignment, XmALIGNMENT_BEGINNING,
490 XmNmarginHeight, 0,
491 XmNset, True, NULL);
492 XmStringFree(s1);
493 ucd->winInpBtn = XtVaCreateManagedWidget("winInpBtn",
494 xmToggleButtonWidgetClass, inpBox,
495 XmNlabelString, s1=MKSTRING("document"),
496 XmNmnemonic, 'w',
497 XmNalignment, XmALIGNMENT_BEGINNING,
498 XmNmarginHeight, 0,
499 XmNset, False, NULL);
500 XmStringFree(s1);
501 ucd->eitherInpBtn = XtVaCreateManagedWidget("eitherInpBtn",
502 xmToggleButtonWidgetClass, inpBox,
503 XmNlabelString, s1=MKSTRING("either"),
504 XmNmnemonic, 't',
505 XmNalignment, XmALIGNMENT_BEGINNING,
506 XmNmarginHeight, 0,
507 XmNset, False, NULL);
508 XmStringFree(s1);
509 ucd->noInpBtn = XtVaCreateManagedWidget("noInpBtn",
510 xmToggleButtonWidgetClass, inpBox,
511 XmNlabelString, s1=MKSTRING("none"),
512 XmNmnemonic, 'o',
513 XmNalignment, XmALIGNMENT_BEGINNING,
514 XmNmarginHeight, 0,
515 XmNset, False, NULL);
516 XmStringFree(s1);
517 inpLabel = XtVaCreateManagedWidget("inpLabel", xmLabelGadgetClass, form,
518 XmNlabelString, s1=MKSTRING("Command Input:"),
519 XmNalignment, XmALIGNMENT_BEGINNING,
520 XmNmarginTop, 5,
521 XmNleftAttachment, XmATTACH_POSITION,
522 XmNleftPosition, LIST_RIGHT,
523 XmNrightAttachment, XmATTACH_POSITION,
524 XmNrightPosition, RIGHT_MARGIN_POS,
525 XmNbottomAttachment, XmATTACH_WIDGET,
526 XmNbottomWidget, inpBox, NULL);
527 XmStringFree(s1);
529 ucd->mneTextW = XtVaCreateManagedWidget("mne", xmTextWidgetClass, form,
530 XmNcolumns, 1,
531 XmNmaxLength, 1,
532 XmNleftAttachment, XmATTACH_POSITION,
533 XmNleftPosition, RIGHT_MARGIN_POS-10,
534 XmNrightAttachment, XmATTACH_POSITION,
535 XmNrightPosition, RIGHT_MARGIN_POS,
536 XmNbottomAttachment, XmATTACH_WIDGET,
537 XmNbottomWidget, inpLabel, NULL);
538 RemapDeleteKey(ucd->mneTextW);
540 ucd->accTextW = XtVaCreateManagedWidget("acc", xmTextWidgetClass, form,
541 XmNcolumns, 12,
542 XmNmaxLength, MAX_ACCEL_LEN-1,
543 XmNcursorPositionVisible, False,
544 XmNleftAttachment, XmATTACH_POSITION,
545 XmNleftPosition, LIST_RIGHT,
546 XmNrightAttachment, XmATTACH_POSITION,
547 XmNrightPosition, RIGHT_MARGIN_POS-15,
548 XmNbottomAttachment, XmATTACH_WIDGET,
549 XmNbottomWidget, inpLabel, NULL);
550 XtAddEventHandler(ucd->accTextW, KeyPressMask, False,
551 (XtEventHandler)accKeyCB, ucd);
552 XtAddCallback(ucd->accTextW, XmNfocusCallback, accFocusCB, ucd);
553 XtAddCallback(ucd->accTextW, XmNlosingFocusCallback, accLoseFocusCB, ucd);
554 accLabel = XtVaCreateManagedWidget("accLabel", xmLabelGadgetClass, form,
555 XmNlabelString, s1=MKSTRING("Accelerator"),
556 XmNmnemonic, 'l',
557 XmNuserData, ucd->accTextW,
558 XmNalignment, XmALIGNMENT_BEGINNING,
559 XmNmarginTop, 5,
560 XmNleftAttachment, XmATTACH_POSITION,
561 XmNleftPosition, LIST_RIGHT,
562 XmNrightAttachment, XmATTACH_POSITION,
563 XmNrightPosition, LIST_RIGHT + 24,
564 XmNbottomAttachment, XmATTACH_WIDGET,
565 XmNbottomWidget, ucd->mneTextW, NULL);
566 XmStringFree(s1);
568 XtVaCreateManagedWidget("mneLabel", xmLabelGadgetClass, form,
569 XmNlabelString, s1=MKSTRING("Mnemonic"),
570 XmNmnemonic, 'i',
571 XmNuserData, ucd->mneTextW,
572 XmNalignment, XmALIGNMENT_END,
573 XmNmarginTop, 5,
574 XmNleftAttachment, XmATTACH_POSITION,
575 XmNleftPosition, LIST_RIGHT + 24,
576 XmNrightAttachment, XmATTACH_POSITION,
577 XmNrightPosition, RIGHT_MARGIN_POS,
578 XmNbottomAttachment, XmATTACH_WIDGET,
579 XmNbottomWidget, ucd->mneTextW, NULL);
580 XmStringFree(s1);
582 ucd->nameTextW = XtVaCreateManagedWidget("name", xmTextWidgetClass, form,
583 XmNleftAttachment, XmATTACH_POSITION,
584 XmNleftPosition, LIST_RIGHT,
585 XmNrightAttachment, XmATTACH_POSITION,
586 XmNrightPosition, RIGHT_MARGIN_POS,
587 XmNbottomAttachment, XmATTACH_WIDGET,
588 XmNbottomWidget, accLabel, NULL);
589 RemapDeleteKey(ucd->nameTextW);
591 nameLabel = XtVaCreateManagedWidget("nameLabel", xmLabelGadgetClass, form,
592 XmNlabelString, s1=MKSTRING("Menu Entry"),
593 XmNmnemonic, 'y',
594 XmNuserData, ucd->nameTextW,
595 XmNalignment, XmALIGNMENT_BEGINNING,
596 XmNmarginTop, 5,
597 XmNleftAttachment, XmATTACH_POSITION,
598 XmNleftPosition, LIST_RIGHT,
599 XmNbottomAttachment, XmATTACH_WIDGET,
600 XmNbottomWidget, ucd->nameTextW, NULL);
601 XmStringFree(s1);
603 XtVaCreateManagedWidget("nameNotes", xmLabelGadgetClass, form,
604 XmNlabelString, s1=MKSTRING("(> for sub-menu, @ language mode)"),
605 XmNalignment, XmALIGNMENT_END,
606 XmNmarginTop, 5,
607 XmNleftAttachment, XmATTACH_WIDGET,
608 XmNleftWidget, nameLabel,
609 XmNrightAttachment, XmATTACH_POSITION,
610 XmNrightPosition, RIGHT_MARGIN_POS,
611 XmNbottomAttachment, XmATTACH_WIDGET,
612 XmNbottomWidget, ucd->nameTextW, NULL);
613 XmStringFree(s1);
615 XtVaCreateManagedWidget("topLabel", xmLabelGadgetClass, form,
616 XmNlabelString, s1=MKSTRING(
617 "Select a shell menu item from the list at left.\n\
618 Select \"New\" to add a new command to the menu."),
619 XmNtopAttachment, XmATTACH_POSITION,
620 XmNtopPosition, 2,
621 XmNleftAttachment, XmATTACH_POSITION,
622 XmNleftPosition, LIST_RIGHT,
623 XmNrightAttachment, XmATTACH_POSITION,
624 XmNrightPosition, RIGHT_MARGIN_POS,
625 XmNbottomAttachment, XmATTACH_WIDGET,
626 XmNbottomWidget, nameLabel, NULL);
627 XmStringFree(s1);
629 cmdLabel = XtVaCreateManagedWidget("cmdLabel", xmLabelGadgetClass, form,
630 XmNlabelString, s1=MKSTRING("Shell Command to Execute"),
631 XmNmnemonic, 'x',
632 XmNalignment, XmALIGNMENT_BEGINNING,
633 XmNmarginTop, 5,
634 XmNtopAttachment, XmATTACH_POSITION,
635 XmNtopPosition, SHELL_CMD_TOP,
636 XmNleftAttachment, XmATTACH_POSITION,
637 XmNleftPosition, LEFT_MARGIN_POS, NULL);
638 XmStringFree(s1);
639 XtVaCreateManagedWidget("cmdLabel", xmLabelGadgetClass, form,
640 XmNlabelString, s1=MKSTRING("(% expands to current filename, # to line number)"),
641 XmNalignment, XmALIGNMENT_END,
642 XmNmarginTop, 5,
643 XmNtopAttachment, XmATTACH_POSITION,
644 XmNtopPosition, SHELL_CMD_TOP,
645 XmNleftAttachment, XmATTACH_WIDGET,
646 XmNleftWidget, cmdLabel,
647 XmNrightAttachment, XmATTACH_POSITION,
648 XmNrightPosition, RIGHT_MARGIN_POS, NULL);
649 XmStringFree(s1);
651 okBtn = XtVaCreateManagedWidget("ok",xmPushButtonWidgetClass,form,
652 XmNlabelString, s1=MKSTRING("OK"),
653 XmNmarginWidth, BUTTON_WIDTH_MARGIN,
654 XmNleftAttachment, XmATTACH_POSITION,
655 XmNleftPosition, 13,
656 XmNrightAttachment, XmATTACH_POSITION,
657 XmNrightPosition, 29,
658 XmNbottomAttachment, XmATTACH_POSITION,
659 XmNbottomPosition, 99, NULL);
660 XtAddCallback(okBtn, XmNactivateCallback, okCB, ucd);
661 XmStringFree(s1);
663 applyBtn = XtVaCreateManagedWidget("apply",xmPushButtonWidgetClass,form,
664 XmNlabelString, s1=MKSTRING("Apply"),
665 XmNmnemonic, 'A',
666 XmNleftAttachment, XmATTACH_POSITION,
667 XmNleftPosition, 42,
668 XmNrightAttachment, XmATTACH_POSITION,
669 XmNrightPosition, 58,
670 XmNbottomAttachment, XmATTACH_POSITION,
671 XmNbottomPosition, 99, NULL);
672 XtAddCallback(applyBtn, XmNactivateCallback, applyCB, ucd);
673 XmStringFree(s1);
675 closeBtn = XtVaCreateManagedWidget("close",
676 xmPushButtonWidgetClass, form,
677 XmNlabelString, s1=MKSTRING("Close"),
678 XmNleftAttachment, XmATTACH_POSITION,
679 XmNleftPosition, 71,
680 XmNrightAttachment, XmATTACH_POSITION,
681 XmNrightPosition, 87,
682 XmNbottomAttachment, XmATTACH_POSITION,
683 XmNbottomPosition, 99,
684 NULL);
685 XtAddCallback(closeBtn, XmNactivateCallback, closeCB, ucd);
686 XmStringFree(s1);
688 ac = 0;
689 XtSetArg(args[ac], XmNeditMode, XmMULTI_LINE_EDIT); ac++;
690 XtSetArg(args[ac], XmNscrollHorizontal, False); ac++;
691 XtSetArg(args[ac], XmNwordWrap, True); ac++;
692 XtSetArg(args[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++;
693 XtSetArg(args[ac], XmNtopWidget, cmdLabel); ac++;
694 XtSetArg(args[ac], XmNleftAttachment, XmATTACH_POSITION); ac++;
695 XtSetArg(args[ac], XmNleftPosition, LEFT_MARGIN_POS); ac++;
696 XtSetArg(args[ac], XmNrightAttachment, XmATTACH_POSITION); ac++;
697 XtSetArg(args[ac], XmNrightPosition, RIGHT_MARGIN_POS); ac++;
698 XtSetArg(args[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
699 XtSetArg(args[ac], XmNbottomWidget, okBtn); ac++;
700 XtSetArg(args[ac], XmNbottomOffset, 5); ac++;
701 ucd->cmdTextW = XmCreateScrolledText(form, "name", args, ac);
702 AddMouseWheelSupport(ucd->cmdTextW);
703 XtManageChild(ucd->cmdTextW);
704 MakeSingleLineTextW(ucd->cmdTextW);
705 RemapDeleteKey(ucd->cmdTextW);
706 XtVaSetValues(cmdLabel, XmNuserData, ucd->cmdTextW, NULL); /* for mnemonic */
708 /* Disable text input for the accelerator key widget, let the
709 event handler manage it instead */
710 disableTextW(ucd->accTextW);
712 /* initializs the dialog fields to match "New" list item */
713 updateDialogFields(NULL, ucd);
715 /* Set initial default button */
716 XtVaSetValues(form, XmNdefaultButton, okBtn, NULL);
717 XtVaSetValues(form, XmNcancelButton, closeBtn, NULL);
719 /* Handle mnemonic selection of buttons and focus to dialog */
720 AddDialogMnemonicHandler(form, FALSE);
722 /* realize all of the widgets in the new window */
723 RealizeWithoutForcingPosition(ucd->dlogShell);
727 ** Present a dialogs for editing the user specified commands in the Macro
728 ** and background menus
730 void EditMacroMenu(WindowInfo *window)
732 editMacroOrBGMenu(window, MACRO_CMDS);
734 void EditBGMenu(WindowInfo *window)
736 editMacroOrBGMenu(window, BG_MENU_CMDS);
739 static void editMacroOrBGMenu(WindowInfo *window, int dialogType)
741 Widget form, accLabel, pasteReplayBtn;
742 Widget nameLabel, cmdLabel, okBtn, applyBtn, closeBtn;
743 userCmdDialog *ucd;
744 char *title;
745 XmString s1;
746 int ac, i;
747 Arg args[20];
749 /* if the dialog is already displayed, just pop it to the top and return */
750 if (dialogType == MACRO_CMDS && MacroCmdDialog != NULL) {
751 RaiseShellWindow(MacroCmdDialog);
752 return;
754 if (dialogType == BG_MENU_CMDS && BGMenuCmdDialog != NULL) {
755 RaiseShellWindow(BGMenuCmdDialog);
756 return;
759 /* Create a structure for keeping track of dialog state */
760 ucd = (userCmdDialog *)XtMalloc(sizeof(userCmdDialog));
761 ucd->window = window;
763 /* Set the dialog to operate on the Macro menu */
764 ucd->menuItemsList = (menuItemRec **)XtMalloc(sizeof(menuItemRec **) *
765 MAX_ITEMS_PER_MENU);
766 if (dialogType == MACRO_CMDS) {
767 for (i=0; i<NMacroMenuItems; i++)
768 ucd->menuItemsList[i] = copyMenuItemRec(MacroMenuItems[i]);
769 ucd->nMenuItems = NMacroMenuItems;
770 } else { /* BG_MENU_CMDS */
771 for (i=0; i<NBGMenuItems; i++)
772 ucd->menuItemsList[i] = copyMenuItemRec(BGMenuItems[i]);
773 ucd->nMenuItems = NBGMenuItems;
775 ucd->dialogType = dialogType;
777 title = dialogType == MACRO_CMDS ? "Macro Commands" :
778 "Window Background Menu";
779 ac = 0;
780 XtSetArg(args[ac], XmNdeleteResponse, XmDO_NOTHING); ac++;
781 XtSetArg(args[ac], XmNiconName, title); ac++;
782 XtSetArg(args[ac], XmNtitle, title); ac++;
783 ucd->dlogShell = CreateWidget(TheAppShell, "macros",
784 topLevelShellWidgetClass, args, ac);
785 AddSmallIcon(ucd->dlogShell);
786 form = XtVaCreateManagedWidget("editMacroCommands", xmFormWidgetClass,
787 ucd->dlogShell, XmNautoUnmanage, False,
788 XmNresizePolicy, XmRESIZE_NONE, NULL);
789 XtAddCallback(form, XmNdestroyCallback, destroyCB, ucd);
790 AddMotifCloseCallback(ucd->dlogShell, closeCB, ucd);
792 ac = 0;
793 XtSetArg(args[ac], XmNtopAttachment, XmATTACH_POSITION); ac++;
794 XtSetArg(args[ac], XmNtopPosition, 2); ac++;
795 XtSetArg(args[ac], XmNleftAttachment, XmATTACH_POSITION); ac++;
796 XtSetArg(args[ac], XmNleftPosition, LEFT_MARGIN_POS); ac++;
797 XtSetArg(args[ac], XmNrightAttachment, XmATTACH_POSITION); ac++;
798 XtSetArg(args[ac], XmNrightPosition, LIST_RIGHT-1); ac++;
799 XtSetArg(args[ac], XmNbottomAttachment, XmATTACH_POSITION); ac++;
800 XtSetArg(args[ac], XmNbottomPosition, MACRO_CMD_TOP); ac++;
801 ucd->managedList = CreateManagedList(form, "list", args, ac,
802 (void **)ucd->menuItemsList, &ucd->nMenuItems, MAX_ITEMS_PER_MENU, 20,
803 getDialogDataCB, ucd, setDialogDataCB, ucd, freeItemCB);
805 ucd->selInpBtn = XtVaCreateManagedWidget("selInpBtn",
806 xmToggleButtonWidgetClass, form,
807 XmNlabelString, s1=MKSTRING("Requires Selection"),
808 XmNmnemonic, 'R',
809 XmNalignment, XmALIGNMENT_BEGINNING,
810 XmNmarginHeight, 0,
811 XmNset, False,
812 XmNleftAttachment, XmATTACH_POSITION,
813 XmNleftPosition, LIST_RIGHT,
814 XmNbottomAttachment, XmATTACH_POSITION,
815 XmNbottomPosition, MACRO_CMD_TOP, NULL);
816 XmStringFree(s1);
818 ucd->mneTextW = XtVaCreateManagedWidget("mne", xmTextWidgetClass, form,
819 XmNcolumns, 1,
820 XmNmaxLength, 1,
821 XmNleftAttachment, XmATTACH_POSITION,
822 XmNleftPosition, RIGHT_MARGIN_POS-21-5,
823 XmNrightAttachment, XmATTACH_POSITION,
824 XmNrightPosition, RIGHT_MARGIN_POS-21,
825 XmNbottomAttachment, XmATTACH_WIDGET,
826 XmNbottomWidget, ucd->selInpBtn,
827 XmNbottomOffset, 5, NULL);
828 RemapDeleteKey(ucd->mneTextW);
830 ucd->accTextW = XtVaCreateManagedWidget("acc", xmTextWidgetClass, form,
831 XmNcolumns, 12,
832 XmNmaxLength, MAX_ACCEL_LEN-1,
833 XmNcursorPositionVisible, False,
834 XmNleftAttachment, XmATTACH_POSITION,
835 XmNleftPosition, LIST_RIGHT,
836 XmNrightAttachment, XmATTACH_POSITION,
837 XmNrightPosition, RIGHT_MARGIN_POS-20-10,
838 XmNbottomAttachment, XmATTACH_WIDGET,
839 XmNbottomWidget, ucd->selInpBtn,
840 XmNbottomOffset, 5, NULL);
841 XtAddEventHandler(ucd->accTextW, KeyPressMask, False,
842 (XtEventHandler)accKeyCB, ucd);
843 XtAddCallback(ucd->accTextW, XmNfocusCallback, accFocusCB, ucd);
844 XtAddCallback(ucd->accTextW, XmNlosingFocusCallback, accLoseFocusCB, ucd);
846 accLabel = XtVaCreateManagedWidget("accLabel", xmLabelGadgetClass, form,
847 XmNlabelString, s1=MKSTRING("Accelerator"),
848 XmNmnemonic, 'l',
849 XmNuserData, ucd->accTextW,
850 XmNalignment, XmALIGNMENT_BEGINNING,
851 XmNmarginTop, 5,
852 XmNleftAttachment, XmATTACH_POSITION,
853 XmNleftPosition, LIST_RIGHT,
854 XmNrightAttachment, XmATTACH_POSITION,
855 XmNrightPosition, LIST_RIGHT + 22,
856 XmNbottomAttachment, XmATTACH_WIDGET,
857 XmNbottomWidget, ucd->mneTextW, NULL);
858 XmStringFree(s1);
860 XtVaCreateManagedWidget("mneLabel", xmLabelGadgetClass, form,
861 XmNlabelString, s1=MKSTRING("Mnemonic"),
862 XmNmnemonic, 'i',
863 XmNuserData, ucd->mneTextW,
864 XmNalignment, XmALIGNMENT_END,
865 XmNmarginTop, 5,
866 XmNleftAttachment, XmATTACH_POSITION,
867 XmNleftPosition, LIST_RIGHT + 22,
868 XmNrightAttachment, XmATTACH_POSITION,
869 XmNrightPosition, RIGHT_MARGIN_POS-21,
870 XmNbottomAttachment, XmATTACH_WIDGET,
871 XmNbottomWidget, ucd->mneTextW, NULL);
872 XmStringFree(s1);
874 pasteReplayBtn = XtVaCreateManagedWidget("pasteReplay",
875 xmPushButtonWidgetClass, form,
876 XmNlabelString, s1=MKSTRING("Paste Learn/\nReplay Macro"),
877 XmNmnemonic, 'P',
878 XmNsensitive, GetReplayMacro() != NULL,
879 XmNleftAttachment, XmATTACH_POSITION,
880 XmNleftPosition, RIGHT_MARGIN_POS-20,
881 XmNrightAttachment, XmATTACH_POSITION,
882 XmNrightPosition, RIGHT_MARGIN_POS,
883 XmNbottomAttachment, XmATTACH_POSITION,
884 XmNbottomPosition, MACRO_CMD_TOP, NULL);
885 XtAddCallback(pasteReplayBtn, XmNactivateCallback,
886 pasteReplayCB, ucd);
887 XmStringFree(s1);
889 ucd->nameTextW = XtVaCreateManagedWidget("name", xmTextWidgetClass, form,
890 XmNleftAttachment, XmATTACH_POSITION,
891 XmNleftPosition, LIST_RIGHT,
892 XmNrightAttachment, XmATTACH_POSITION,
893 XmNrightPosition, RIGHT_MARGIN_POS,
894 XmNbottomAttachment, XmATTACH_WIDGET,
895 XmNbottomWidget, accLabel, NULL);
896 RemapDeleteKey(ucd->nameTextW);
898 nameLabel = XtVaCreateManagedWidget("nameLabel", xmLabelGadgetClass, form,
899 XmNlabelString, s1=MKSTRING("Menu Entry"),
900 XmNmnemonic, 'y',
901 XmNuserData, ucd->nameTextW,
902 XmNalignment, XmALIGNMENT_BEGINNING,
903 XmNmarginTop, 5,
904 XmNleftAttachment, XmATTACH_POSITION,
905 XmNleftPosition, LIST_RIGHT,
906 XmNbottomAttachment, XmATTACH_WIDGET,
907 XmNbottomWidget, ucd->nameTextW, NULL);
908 XmStringFree(s1);
910 XtVaCreateManagedWidget("nameNotes", xmLabelGadgetClass, form,
911 XmNlabelString, s1=MKSTRING("(> for sub-menu, @ language mode)"),
912 XmNalignment, XmALIGNMENT_END,
913 XmNmarginTop, 5,
914 XmNleftAttachment, XmATTACH_WIDGET,
915 XmNleftWidget, nameLabel,
916 XmNrightAttachment, XmATTACH_POSITION,
917 XmNrightPosition, RIGHT_MARGIN_POS,
918 XmNbottomAttachment, XmATTACH_WIDGET,
919 XmNbottomWidget, ucd->nameTextW, NULL);
920 XmStringFree(s1);
922 XtVaCreateManagedWidget("topLabel", xmLabelGadgetClass, form,
923 XmNlabelString, s1=MKSTRING(
924 "Select a macro menu item from the list at left.\n\
925 Select \"New\" to add a new command to the menu."),
926 XmNtopAttachment, XmATTACH_POSITION,
927 XmNtopPosition, 2,
928 XmNleftAttachment, XmATTACH_POSITION,
929 XmNleftPosition, LIST_RIGHT,
930 XmNrightAttachment, XmATTACH_POSITION,
931 XmNrightPosition, RIGHT_MARGIN_POS,
932 XmNbottomAttachment, XmATTACH_WIDGET,
933 XmNbottomWidget, nameLabel, NULL);
934 XmStringFree(s1);
936 cmdLabel = XtVaCreateManagedWidget("cmdLabel", xmLabelGadgetClass, form,
937 XmNlabelString, s1=MKSTRING("Macro Command to Execute"),
938 XmNmnemonic, 'x',
939 XmNalignment, XmALIGNMENT_BEGINNING,
940 XmNmarginTop, 5,
941 XmNtopAttachment, XmATTACH_POSITION,
942 XmNtopPosition, MACRO_CMD_TOP,
943 XmNleftAttachment, XmATTACH_POSITION,
944 XmNleftPosition, LEFT_MARGIN_POS, NULL);
945 XmStringFree(s1);
947 okBtn = XtVaCreateManagedWidget("ok",xmPushButtonWidgetClass,form,
948 XmNlabelString, s1=MKSTRING("OK"),
949 XmNmarginWidth, BUTTON_WIDTH_MARGIN,
950 XmNleftAttachment, XmATTACH_POSITION,
951 XmNleftPosition, 8,
952 XmNrightAttachment, XmATTACH_POSITION,
953 XmNrightPosition, 23,
954 XmNbottomAttachment, XmATTACH_POSITION,
955 XmNbottomPosition, 99, NULL);
956 XtAddCallback(okBtn, XmNactivateCallback, okCB, ucd);
957 XmStringFree(s1);
959 applyBtn = XtVaCreateManagedWidget("apply",xmPushButtonWidgetClass,form,
960 XmNlabelString, s1=MKSTRING("Apply"),
961 XmNmnemonic, 'A',
962 XmNleftAttachment, XmATTACH_POSITION,
963 XmNleftPosition, 31,
964 XmNrightAttachment, XmATTACH_POSITION,
965 XmNrightPosition, 46,
966 XmNbottomAttachment, XmATTACH_POSITION,
967 XmNbottomPosition, 99, NULL);
968 XtAddCallback(applyBtn, XmNactivateCallback, applyCB, ucd);
969 XmStringFree(s1);
971 applyBtn = XtVaCreateManagedWidget("check",xmPushButtonWidgetClass,form,
972 XmNlabelString, s1=MKSTRING("Check"),
973 XmNmnemonic, 'C',
974 XmNleftAttachment, XmATTACH_POSITION,
975 XmNleftPosition, 54,
976 XmNrightAttachment, XmATTACH_POSITION,
977 XmNrightPosition, 69,
978 XmNbottomAttachment, XmATTACH_POSITION,
979 XmNbottomPosition, 99, NULL);
980 XtAddCallback(applyBtn, XmNactivateCallback, checkCB, ucd);
981 XmStringFree(s1);
983 closeBtn = XtVaCreateManagedWidget("close",
984 xmPushButtonWidgetClass, form,
985 XmNlabelString, s1=MKSTRING("Close"),
986 XmNleftAttachment, XmATTACH_POSITION,
987 XmNleftPosition, 77,
988 XmNrightAttachment, XmATTACH_POSITION,
989 XmNrightPosition, 92,
990 XmNbottomAttachment, XmATTACH_POSITION,
991 XmNbottomPosition, 99,
992 NULL);
993 XtAddCallback(closeBtn, XmNactivateCallback, closeCB, ucd);
994 XmStringFree(s1);
996 ac = 0;
997 XtSetArg(args[ac], XmNeditMode, XmMULTI_LINE_EDIT); ac++;
998 XtSetArg(args[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++;
999 XtSetArg(args[ac], XmNtopWidget, cmdLabel); ac++;
1000 XtSetArg(args[ac], XmNleftAttachment, XmATTACH_POSITION); ac++;
1001 XtSetArg(args[ac], XmNleftPosition, LEFT_MARGIN_POS); ac++;
1002 XtSetArg(args[ac], XmNrightAttachment, XmATTACH_POSITION); ac++;
1003 XtSetArg(args[ac], XmNrightPosition, RIGHT_MARGIN_POS); ac++;
1004 XtSetArg(args[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1005 XtSetArg(args[ac], XmNbottomWidget, okBtn); ac++;
1006 XtSetArg(args[ac], XmNbottomOffset, 5); ac++;
1007 ucd->cmdTextW = XmCreateScrolledText(form, "name", args, ac);
1008 AddMouseWheelSupport(ucd->cmdTextW);
1009 XtManageChild(ucd->cmdTextW);
1010 RemapDeleteKey(ucd->cmdTextW);
1011 XtVaSetValues(cmdLabel, XmNuserData, ucd->cmdTextW, NULL); /* for mnemonic */
1013 /* Disable text input for the accelerator key widget, let the
1014 event handler manage it instead */
1015 disableTextW(ucd->accTextW);
1017 /* initializs the dialog fields to match "New" list item */
1018 updateDialogFields(NULL, ucd);
1020 /* Set initial default button */
1021 XtVaSetValues(form, XmNdefaultButton, okBtn, NULL);
1022 XtVaSetValues(form, XmNcancelButton, closeBtn, NULL);
1024 /* Handle mnemonic selection of buttons and focus to dialog */
1025 AddDialogMnemonicHandler(form, FALSE);
1027 /* Make widgets for top level shell and paste-replay buttons available
1028 to other functions */
1029 if (dialogType == MACRO_CMDS) {
1030 MacroCmdDialog = ucd->dlogShell;
1031 MacroPasteReplayBtn = pasteReplayBtn;
1032 } else {
1033 BGMenuCmdDialog = ucd->dlogShell;
1034 BGMenuPasteReplayBtn = pasteReplayBtn;
1037 /* Realize all of the widgets in the new dialog */
1038 RealizeWithoutForcingPosition(ucd->dlogShell);
1042 ** Update the Shell, Macro, and Window Background menus of window
1043 ** "window" from the currently loaded command descriptions.
1045 void UpdateUserMenus(WindowInfo *window)
1047 if (!IsTopDocument(window))
1048 return;
1050 /* update user menus, which are shared over all documents, only
1051 if language mode was changed */
1052 if (window->userMenuCache->umcLanguageMode != window->languageMode) {
1053 #ifndef VMS
1054 updateMenu(window, SHELL_CMDS);
1055 #endif
1056 updateMenu(window, MACRO_CMDS);
1058 /* remember language mode assigned to shared user menus */
1059 window->userMenuCache->umcLanguageMode = window->languageMode;
1062 /* update background menu, which is owned by a single document, only
1063 if language mode was changed */
1064 if (window->userBGMenuCache.ubmcLanguageMode != window->languageMode) {
1065 updateMenu(window, BG_MENU_CMDS);
1067 /* remember language mode assigned to background menu */
1068 window->userBGMenuCache.ubmcLanguageMode = window->languageMode;
1073 ** Dim/undim buttons for pasting replay macros into macro and bg menu dialogs
1075 void DimPasteReplayBtns(int sensitive)
1077 if (MacroCmdDialog != NULL)
1078 XtSetSensitive(MacroPasteReplayBtn, sensitive);
1079 if (BGMenuCmdDialog != NULL)
1080 XtSetSensitive(BGMenuPasteReplayBtn, sensitive);
1084 ** Dim/undim user programmable menu items which depend on there being
1085 ** a selection in their associated window.
1087 void DimSelectionDepUserMenuItems(WindowInfo *window, int sensitive)
1089 if (!IsTopDocument(window))
1090 return;
1092 #ifndef VMS
1093 dimSelDepItemsInMenu(window->shellMenuPane, ShellMenuItems,
1094 NShellMenuItems, sensitive);
1095 #endif /*VMS*/
1096 dimSelDepItemsInMenu(window->macroMenuPane, MacroMenuItems,
1097 NMacroMenuItems, sensitive);
1098 dimSelDepItemsInMenu(window->bgMenuPane, BGMenuItems,
1099 NBGMenuItems, sensitive);
1102 static void dimSelDepItemsInMenu(Widget menuPane, menuItemRec **menuList,
1103 int nMenuItems, int sensitive)
1105 WidgetList items;
1106 Widget subMenu;
1107 XtPointer userData;
1108 int n, index;
1109 Cardinal nItems;
1111 XtVaGetValues(menuPane, XmNchildren, &items, XmNnumChildren, &nItems, NULL);
1112 for (n=0; n<(int)nItems; n++) {
1113 XtVaGetValues(items[n], XmNuserData, &userData, NULL);
1114 if (userData != (XtPointer)PERMANENT_MENU_ITEM) {
1115 if (XtClass(items[n]) == xmCascadeButtonWidgetClass) {
1116 XtVaGetValues(items[n], XmNsubMenuId, &subMenu, NULL);
1117 dimSelDepItemsInMenu(subMenu, menuList, nMenuItems, sensitive);
1118 } else {
1119 index = (int)userData - 10;
1120 if (index <0 || index >= nMenuItems)
1121 return;
1122 if (menuList[index]->input == FROM_SELECTION)
1123 XtSetSensitive(items[n], sensitive);
1130 ** Harmless kludge for making undo/redo menu items in background menu properly
1131 ** sensitive (even though they're programmable) along with real undo item
1132 ** in the Edit menu
1134 void SetBGMenuUndoSensitivity(WindowInfo *window, int sensitive)
1136 if (window->bgMenuUndoItem != NULL)
1137 SetSensitive(window, window->bgMenuUndoItem, sensitive);
1139 void SetBGMenuRedoSensitivity(WindowInfo *window, int sensitive)
1141 if (window->bgMenuRedoItem != NULL)
1142 SetSensitive(window, window->bgMenuRedoItem, sensitive);
1146 ** Generate a text string for the preferences file describing the contents
1147 ** of the shell cmd list. This string is not exactly of the form that it
1148 ** can be read by LoadShellCmdsString, rather, it is what needs to be written
1149 ** to a resource file such that it will read back in that form.
1151 char *WriteShellCmdsString(void)
1153 return writeMenuItemString(ShellMenuItems, NShellMenuItems,
1154 SHELL_CMDS);
1158 ** Generate a text string for the preferences file describing the contents of
1159 ** the macro menu and background menu commands lists. These strings are not
1160 ** exactly of the form that it can be read by LoadMacroCmdsString, rather, it
1161 ** is what needs to be written to a resource file such that it will read back
1162 ** in that form.
1164 char *WriteMacroCmdsString(void)
1166 return writeMenuItemString(MacroMenuItems, NMacroMenuItems, MACRO_CMDS);
1169 char *WriteBGMenuCmdsString(void)
1171 return writeMenuItemString(BGMenuItems, NBGMenuItems, BG_MENU_CMDS);
1175 ** Read a string representing shell command menu items and add them to the
1176 ** internal list used for constructing shell menus
1178 int LoadShellCmdsString(char *inString)
1180 return loadMenuItemString(inString, ShellMenuItems, &NShellMenuItems,
1181 SHELL_CMDS);
1185 ** Read strings representing macro menu or background menu command menu items
1186 ** and add them to the internal lists used for constructing menus
1188 int LoadMacroCmdsString(char *inString)
1190 return loadMenuItemString(inString, MacroMenuItems, &NMacroMenuItems,
1191 MACRO_CMDS);
1194 int LoadBGMenuCmdsString(char *inString)
1196 return loadMenuItemString(inString, BGMenuItems, &NBGMenuItems,
1197 BG_MENU_CMDS);
1201 ** Cache user menus:
1202 ** Setup user menu info after read of macro, shell and background menu
1203 ** string (reason: language mode info from preference string is read *after*
1204 ** user menu preference string was read).
1206 void SetupUserMenuInfo(void)
1208 parseMenuItemList(ShellMenuItems, NShellMenuItems, ShellMenuInfo, &ShellSubMenus);
1209 parseMenuItemList(MacroMenuItems, NMacroMenuItems, MacroMenuInfo, &MacroSubMenus);
1210 parseMenuItemList(BGMenuItems , NBGMenuItems , BGMenuInfo , &BGSubMenus);
1214 ** Search through the shell menu and execute the first command with menu item
1215 ** name "itemName". Returns True on successs and False on failure.
1217 #ifndef VMS
1218 int DoNamedShellMenuCmd(WindowInfo *window, const char *itemName, int fromMacro)
1220 int i;
1222 for (i=0; i<NShellMenuItems; i++) {
1223 if (!strcmp(ShellMenuItems[i]->name, itemName)) {
1224 if (ShellMenuItems[i]->output == TO_SAME_WINDOW &&
1225 CheckReadOnly(window))
1226 return False;
1227 DoShellMenuCmd(window, ShellMenuItems[i]->cmd,
1228 ShellMenuItems[i]->input, ShellMenuItems[i]->output,
1229 ShellMenuItems[i]->repInput, ShellMenuItems[i]->saveFirst,
1230 ShellMenuItems[i]->loadAfter, fromMacro);
1231 return True;
1234 return False;
1236 #endif /*VMS*/
1239 ** Search through the Macro or background menu and execute the first command
1240 ** with menu item name "itemName". Returns True on successs and False on
1241 ** failure.
1243 int DoNamedMacroMenuCmd(WindowInfo *window, const char *itemName)
1245 int i;
1247 for (i=0; i<NMacroMenuItems; i++) {
1248 if (!strcmp(MacroMenuItems[i]->name, itemName)) {
1249 DoMacro(window, MacroMenuItems[i]->cmd, "macro menu command");
1250 return True;
1253 return False;
1256 int DoNamedBGMenuCmd(WindowInfo *window, const char *itemName)
1258 int i;
1260 for (i=0; i<NBGMenuItems; i++) {
1261 if (!strcmp(BGMenuItems[i]->name, itemName)) {
1262 DoMacro(window, BGMenuItems[i]->cmd, "background menu macro");
1263 return True;
1266 return False;
1270 ** Cache user menus:
1271 ** Rebuild all of the Shell, Macro, Background menus of given editor window.
1273 void RebuildAllMenus(WindowInfo *window)
1275 rebuildMenu(window, SHELL_CMDS);
1276 rebuildMenu(window, MACRO_CMDS);
1277 rebuildMenu(window, BG_MENU_CMDS);
1281 ** Cache user menus:
1282 ** Rebuild either Shell, Macro or Background menus of all editor windows.
1284 static void rebuildMenuOfAllWindows(int menuType)
1286 WindowInfo *w;
1288 for (w=WindowList; w!=NULL; w=w->next)
1289 rebuildMenu(w, menuType);
1293 ** Rebuild either the Shell, Macro or Background menu of "window", depending
1294 ** on value of "menuType". Rebuild is realized by following main steps:
1295 ** - dismiss user (sub) menu tearoff.
1296 ** - delete all user defined menu widgets.
1297 ** - update user menu including (re)creation of menu widgets.
1299 static void rebuildMenu(WindowInfo *window, int menuType)
1301 selectedUserMenu menu;
1303 /* Background menu is always rebuild (exists once per document).
1304 Shell, macro (user) menu cache is rebuild only, if given window is
1305 currently displayed on top. */
1306 if (menuType != BG_MENU_CMDS && !IsTopDocument(window))
1307 return;
1309 /* Fetch the appropriate menu data */
1310 selectUserMenu(window, menuType, &menu);
1312 /* dismiss user menu tearoff, to workaround the quick
1313 but noticeable shrink-expand bug, most probably
1314 triggered by the rebuild of the user menus. In any
1315 case, the submenu tearoffs will later be dismissed
1316 too in order to prevent dangling tearoffs, so doing
1317 this also for the main user menu tearoffs shouldn't
1318 be so bad */
1319 if (!XmIsMenuShell(XtParent(menu.sumMenuPane)))
1320 _XmDismissTearOff(XtParent(menu.sumMenuPane), NULL, NULL);
1322 /* destroy all widgets related to menu pane */
1323 deleteMenuItems(menu.sumMenuPane);
1325 /* remove cached user menu info */
1326 freeUserMenuList(menu.sumMainMenuList);
1327 *menu.sumMenuCreated = False;
1329 /* re-create & cache user menu items */
1330 updateMenu(window, menuType);
1334 ** Fetch the appropriate menu info for given menu type
1336 static void selectUserMenu(WindowInfo *window, int menuType, selectedUserMenu *menu)
1338 if (menuType == SHELL_CMDS) {
1339 menu->sumMenuPane = window->shellMenuPane;
1340 menu->sumNbrOfListItems = NShellMenuItems;
1341 menu->sumItemList = ShellMenuItems;
1342 menu->sumInfoList = ShellMenuInfo;
1343 menu->sumSubMenus = &ShellSubMenus;
1344 menu->sumMainMenuList = &window->userMenuCache->umcShellMenuList;
1345 menu->sumMenuCreated = &window->userMenuCache->umcShellMenuCreated;
1346 } else if (menuType == MACRO_CMDS) {
1347 menu->sumMenuPane = window->macroMenuPane;
1348 menu->sumNbrOfListItems = NMacroMenuItems;
1349 menu->sumItemList = MacroMenuItems;
1350 menu->sumInfoList = MacroMenuInfo;
1351 menu->sumSubMenus = &MacroSubMenus;
1352 menu->sumMainMenuList = &window->userMenuCache->umcMacroMenuList;
1353 menu->sumMenuCreated = &window->userMenuCache->umcMacroMenuCreated;
1354 } else { /* BG_MENU_CMDS */
1355 menu->sumMenuPane = window->bgMenuPane;
1356 menu->sumNbrOfListItems = NBGMenuItems;
1357 menu->sumItemList = BGMenuItems;
1358 menu->sumInfoList = BGMenuInfo;
1359 menu->sumSubMenus = &BGSubMenus;
1360 menu->sumMainMenuList = &window->userBGMenuCache.ubmcMenuList;
1361 menu->sumMenuCreated = &window->userBGMenuCache.ubmcMenuCreated;
1363 menu->sumType = menuType;
1367 ** Updates either the Shell, Macro or Background menu of "window", depending
1368 ** on value of "menuType". Update is realized by following main steps:
1369 ** - set / reset "to be managed" flag of user menu info list items
1370 ** according to current selected language mode.
1371 ** - create *all* user menu items (widgets etc). related to given
1372 ** window & menu type, if not done before.
1373 ** - manage / unmanage user menu widgets according to "to be managed"
1374 ** indication of user menu info list items.
1376 static void updateMenu(WindowInfo *window, int menuType)
1378 selectedUserMenu menu;
1380 /* Fetch the appropriate menu data */
1381 selectUserMenu(window, menuType, &menu);
1383 /* Set / reset "to be managed" flag of all info list items */
1384 applyLangModeToUserMenuInfo(menu.sumInfoList, menu.sumNbrOfListItems,
1385 window->languageMode);
1387 /* create user menu items, if not done before */
1388 if (!*menu.sumMenuCreated)
1389 createMenuItems(window, &menu);
1391 /* manage user menu items depending on current language mode */
1392 manageUserMenu(&menu, window);
1394 if (menuType == BG_MENU_CMDS) {
1395 /* Set the proper sensitivity of items which may be dimmed */
1396 SetBGMenuUndoSensitivity(window, XtIsSensitive(window->undoItem));
1397 SetBGMenuRedoSensitivity(window, XtIsSensitive(window->redoItem));
1400 DimSelectionDepUserMenuItems(window, window->buffer->primary.selected);
1404 ** Manually adjust the dimension of the menuShell _before_
1405 ** re-managing the menu pane, to either expose hidden menu
1406 ** entries or remove empty space.
1408 static void manageTearOffMenu(Widget menuPane)
1410 Dimension width, height, border;
1412 /* somehow OM went into a long CPU cycling when we
1413 attempt to change the shell window dimension by
1414 setting the XmNwidth & XmNheight directly. Using
1415 XtResizeWidget() seem to fix it */
1416 XtVaGetValues(XtParent(menuPane), XmNborderWidth, &border, NULL);
1417 XtVaGetValues(menuPane, XmNwidth, &width, XmNheight, &height, NULL);
1418 XtResizeWidget(XtParent(menuPane), width, height, border);
1420 XtManageChild(menuPane);
1424 ** Cache user menus:
1425 ** Reset manage mode of user menu items in window cache.
1427 static void resetManageMode(UserMenuList *list)
1429 int i;
1430 UserMenuListElement *element;
1432 for (i=0; i<list->umlNbrItems; i ++) {
1433 element = list->umlItems[i];
1435 /* remember current manage mode before reset it to
1436 "unmanaged" */
1437 element->umlePrevManageMode = element->umleManageMode;
1438 element->umleManageMode = UMMM_UNMANAGE;
1440 /* recursively reset manage mode of sub-menus */
1441 if (element->umleSubMenuList != NULL)
1442 resetManageMode(element->umleSubMenuList);
1447 ** Cache user menus:
1448 ** Manage all menu widgets of given user sub-menu list.
1450 static void manageAllSubMenuWidgets(UserMenuListElement *subMenu)
1452 int i;
1453 UserMenuList *subMenuList;
1454 UserMenuListElement *element;
1455 WidgetList widgetList;
1456 Cardinal nWidgetListItems;
1458 /* if the sub-menu is torn off, unmanage the menu pane
1459 before updating it to prevent the tear-off menu
1460 from shrinking and expanding as the menu entries
1461 are (un)managed */
1462 if (!XmIsMenuShell(XtParent(subMenu->umleSubMenuPane))) {
1463 XtUnmanageChild(subMenu->umleSubMenuPane);
1466 /* manage all children of sub-menu pane */
1467 XtVaGetValues(subMenu->umleSubMenuPane,
1468 XmNchildren, &widgetList,
1469 XmNnumChildren, &nWidgetListItems,
1470 NULL);
1471 XtManageChildren(widgetList, nWidgetListItems);
1473 /* scan, if an menu item of given sub-menu holds a nested
1474 sub-menu */
1475 subMenuList = subMenu->umleSubMenuList;
1477 for (i=0; i<subMenuList->umlNbrItems; i ++) {
1478 element = subMenuList->umlItems[i];
1480 if (element->umleSubMenuList != NULL) {
1481 /* if element is a sub-menu, then continue managing
1482 all items of that sub-menu recursively */
1483 manageAllSubMenuWidgets(element);
1487 /* manage sub-menu pane widget itself */
1488 XtManageChild(subMenu->umleMenuItem);
1490 /* if the sub-menu is torn off, then adjust & manage the menu */
1491 if (!XmIsMenuShell(XtParent(subMenu->umleSubMenuPane))) {
1492 manageTearOffMenu(subMenu->umleSubMenuPane);
1495 /* redisplay sub-menu tearoff window, if the sub-menu
1496 was torn off before */
1497 ShowHiddenTearOff(subMenu->umleSubMenuPane);
1501 ** Cache user menus:
1502 ** Unmanage all menu widgets of given user sub-menu list.
1504 static void unmanageAllSubMenuWidgets(UserMenuListElement *subMenu)
1506 int i;
1507 Widget shell;
1508 UserMenuList *subMenuList;
1509 UserMenuListElement *element;
1510 WidgetList widgetList;
1511 Cardinal nWidgetListItems;
1513 /* if sub-menu is torn-off, then unmap its shell
1514 (so tearoff window isn't displayed anymore) */
1515 shell = XtParent(subMenu->umleSubMenuPane);
1516 if (!XmIsMenuShell(shell)) {
1517 XtUnmapWidget(shell);
1520 /* unmanage all children of sub-menu pane */
1521 XtVaGetValues(subMenu->umleSubMenuPane,
1522 XmNchildren, &widgetList,
1523 XmNnumChildren, &nWidgetListItems,
1524 NULL);
1525 XtUnmanageChildren(widgetList, nWidgetListItems);
1527 /* scan, if an menu item of given sub-menu holds a nested
1528 sub-menu */
1529 subMenuList = subMenu->umleSubMenuList;
1531 for (i=0; i<subMenuList->umlNbrItems; i ++) {
1532 element = subMenuList->umlItems[i];
1534 if (element->umleSubMenuList != NULL) {
1535 /* if element is a sub-menu, then continue unmanaging
1536 all items of that sub-menu recursively */
1537 unmanageAllSubMenuWidgets(element);
1541 /* unmanage sub-menu pane widget itself */
1542 XtUnmanageChild(subMenu->umleMenuItem);
1546 ** Cache user menus:
1547 ** Manage / unmanage menu widgets according to given user menu list.
1549 static void manageMenuWidgets(UserMenuList *list)
1551 int i;
1552 UserMenuListElement *element;
1554 /* (un)manage all elements of given user menu list */
1555 for (i=0; i<list->umlNbrItems; i ++) {
1556 element = list->umlItems[i];
1558 if (element->umlePrevManageMode != element->umleManageMode ||
1559 element->umleManageMode == UMMM_MANAGE) {
1560 /* previous and current manage mode differ OR
1561 current manage mode indicates: element needs to be
1562 (un)managed individually */
1563 if (element->umleManageMode == UMMM_MANAGE_ALL) {
1564 /* menu item represented by "element" is a sub-menu and
1565 needs to be completely managed */
1566 manageAllSubMenuWidgets(element);
1567 } else if (element->umleManageMode == UMMM_MANAGE) {
1568 if (element->umlePrevManageMode == UMMM_UNMANAGE ||
1569 element->umlePrevManageMode == UMMM_UNMANAGE_ALL) {
1570 /* menu item represented by "element" was unmanaged
1571 before and needs to be managed now */
1572 XtManageChild(element->umleMenuItem);
1575 /* if element is a sub-menu, then continue (un)managing
1576 single elements of that sub-menu one by one */
1577 if (element->umleSubMenuList != NULL) {
1578 /* if the sub-menu is torn off, unmanage the menu pane
1579 before updating it to prevent the tear-off menu
1580 from shrinking and expanding as the menu entries
1581 are (un)managed */
1582 if (!XmIsMenuShell(XtParent(element->umleSubMenuPane))) {
1583 XtUnmanageChild(element->umleSubMenuPane);
1586 /* (un)manage menu entries of sub-menu */
1587 manageMenuWidgets(element->umleSubMenuList);
1589 /* if the sub-menu is torn off, then adjust & manage the menu */
1590 if (!XmIsMenuShell(XtParent(element->umleSubMenuPane))) {
1591 manageTearOffMenu(element->umleSubMenuPane);
1594 /* if the sub-menu was torn off then redisplay it */
1595 ShowHiddenTearOff(element->umleSubMenuPane);
1597 } else if (element->umleManageMode == UMMM_UNMANAGE_ALL){
1598 /* menu item represented by "element" is a sub-menu and
1599 needs to be completely unmanaged */
1600 unmanageAllSubMenuWidgets(element);
1601 } else {
1602 /* current mode is UMMM_UNMANAGE -> menu item represented
1603 by "element" is a single menu item and needs to be
1604 unmanaged */
1605 XtUnmanageChild(element->umleMenuItem);
1612 ** Cache user menus:
1613 ** Remove accelerators from all items of given user (sub-)menu list.
1615 static void removeAccelFromMenuWidgets(UserMenuList *menuList)
1617 int i;
1618 UserMenuListElement *element;
1620 /* scan all elements of this (sub-)menu */
1621 for (i=0; i<menuList->umlNbrItems; i ++) {
1622 element = menuList->umlItems[i];
1624 if (element->umleSubMenuList != NULL) {
1625 /* if element is a sub-menu, then continue removing accelerators
1626 from all items of that sub-menu recursively */
1627 removeAccelFromMenuWidgets(element->umleSubMenuList);
1628 } else if (element->umleAccKeys != NULL &&
1629 element->umleManageMode == UMMM_UNMANAGE &&
1630 element->umlePrevManageMode == UMMM_MANAGE) {
1631 /* remove accelerator if one was bound */
1632 XtVaSetValues(element->umleMenuItem, XmNaccelerator, NULL, NULL);
1638 ** Cache user menus:
1639 ** Assign accelerators to all managed items of given user (sub-)menu list.
1641 static void assignAccelToMenuWidgets(UserMenuList *menuList, WindowInfo *window)
1643 int i;
1644 UserMenuListElement *element;
1646 /* scan all elements of this (sub-)menu */
1647 for (i=0; i<menuList->umlNbrItems; i ++) {
1648 element = menuList->umlItems[i];
1650 if (element->umleSubMenuList != NULL) {
1651 /* if element is a sub-menu, then continue assigning accelerators
1652 to all managed items of that sub-menu recursively */
1653 assignAccelToMenuWidgets(element->umleSubMenuList, window);
1654 } else if (element->umleAccKeys != NULL &&
1655 element->umleManageMode == UMMM_MANAGE &&
1656 element->umlePrevManageMode == UMMM_UNMANAGE) {
1657 /* assign accelerator if applicable */
1658 XtVaSetValues(element->umleMenuItem, XmNaccelerator,
1659 element->umleAccKeys, NULL);
1660 if (!element->umleAccLockPatchApplied) {
1661 UpdateAccelLockPatch(window->splitPane, element->umleMenuItem);
1662 element->umleAccLockPatchApplied = True;
1669 ** Cache user menus:
1670 ** (Un)Manage all items of selected user menu.
1672 static void manageUserMenu(selectedUserMenu *menu, WindowInfo *window)
1674 int n, i;
1675 int *id;
1676 Boolean currentLEisSubMenu;
1677 userMenuInfo *info;
1678 UserMenuList *menuList;
1679 UserMenuListElement *currentLE;
1680 UserMenuManageMode *mode;
1682 /* reset manage mode of all items of selected user menu in window cache */
1683 resetManageMode(menu->sumMainMenuList);
1685 /* set manage mode of all items of selected user menu in window cache
1686 according to the "to be managed" indication of the info list */
1687 for (n=0; n<menu->sumNbrOfListItems; n++) {
1688 info = menu->sumInfoList[n];
1690 menuList = menu->sumMainMenuList;
1691 id = info->umiId;
1693 /* select all menu list items belonging to menu record "info" using
1694 hierarchical ID of current menu info (e.g. id = {3} means:
1695 4th element of main menu; {0} = 1st element etc.)*/
1696 for (i=0; i<info->umiIdLen; i ++) {
1697 currentLE = menuList->umlItems[*id];
1698 mode = &currentLE->umleManageMode;
1699 currentLEisSubMenu = (currentLE->umleSubMenuList != NULL);
1701 if (info->umiToBeManaged) {
1702 /* menu record needs to be managed: */
1703 if (*mode == UMMM_UNMANAGE) {
1704 /* "mode" was not touched after reset ("init. state"):
1705 if current list element represents a sub-menu, then
1706 probably the complete sub-menu needs to be managed
1707 too. If current list element indicates single menu
1708 item, then just this item needs to be managed */
1709 if (currentLEisSubMenu) {
1710 *mode = UMMM_MANAGE_ALL;
1711 } else {
1712 *mode = UMMM_MANAGE;
1714 } else if (*mode == UMMM_UNMANAGE_ALL) {
1715 /* "mode" was touched after reset:
1716 current list element represents a sub-menu and min.
1717 one element of the sub-menu needs to be unmanaged ->
1718 the sub-menu needs to be (un)managed element by
1719 element */
1720 *mode = UMMM_MANAGE;
1722 } else {
1723 /* menu record needs to be unmanaged: */
1724 if (*mode == UMMM_UNMANAGE) {
1725 /* "mode" was not touched after reset ("init. state"):
1726 if current list element represents a sub-menu, then
1727 probably the complete sub-menu needs to be unmanaged
1728 too. */
1729 if (currentLEisSubMenu) {
1730 *mode = UMMM_UNMANAGE_ALL;
1732 } else if (*mode == UMMM_MANAGE_ALL) {
1733 /* "mode" was touched after reset:
1734 current list element represents a sub-menu and min.
1735 one element of the sub-menu needs to be managed ->
1736 the sub-menu needs to be (un)managed element by
1737 element */
1738 *mode = UMMM_MANAGE;
1742 menuList = currentLE->umleSubMenuList;
1744 id ++;
1748 /* if the menu is torn off, unmanage the menu pane
1749 before updating it to prevent the tear-off menu
1750 from shrinking and expanding as the menu entries
1751 are managed */
1752 if (!XmIsMenuShell(XtParent(menu->sumMenuPane)))
1753 XtUnmanageChild(menu->sumMenuPane);
1755 /* manage menu widgets according to current / previous manage mode of
1756 user menu window cache */
1757 manageMenuWidgets(menu->sumMainMenuList);
1759 /* Note: before new accelerator is assigned it seems to be necessary
1760 to remove old accelerator from user menu widgets. Removing same
1761 accelerator *after* it was assigned to another user menu widget
1762 doesn't work */
1763 removeAccelFromMenuWidgets(menu->sumMainMenuList);
1765 assignAccelToMenuWidgets(menu->sumMainMenuList, window);
1767 /* if the menu is torn off, then adjust & manage the menu */
1768 if (!XmIsMenuShell(XtParent(menu->sumMenuPane)))
1769 manageTearOffMenu(menu->sumMenuPane);
1773 ** Create either the variable Shell menu, Macro menu or Background menu
1774 ** items of "window" (driven by value of "menuType")
1776 static void createMenuItems(WindowInfo *window, selectedUserMenu *menu)
1778 Widget btn, subPane, newSubPane;
1779 int n;
1780 menuItemRec *item;
1781 menuTreeItem *menuTree;
1782 int i, nTreeEntries, size;
1783 char *hierName, *namePtr, *subMenuName, *subSep, *fullName;
1784 int menuType = menu->sumType;
1785 userMenuInfo *info;
1786 userSubMenuCache *subMenus = menu->sumSubMenus;
1787 userSubMenuInfo *subMenuInfo;
1788 UserMenuList *menuList;
1789 UserMenuListElement *currentLE;
1790 int subMenuDepth;
1791 char accKeysBuf[MAX_ACCEL_LEN+5];
1792 char *accKeys;
1794 /* Allocate storage for structures to help find panes of sub-menus */
1795 size = sizeof(menuTreeItem) * menu->sumNbrOfListItems;
1796 menuTree = (menuTreeItem *)XtMalloc(size);
1797 nTreeEntries = 0;
1799 /* Harmless kludge: undo and redo items are marked specially if found
1800 in the background menu, and used to dim/undim with edit menu */
1801 window->bgMenuUndoItem = NULL;
1802 window->bgMenuRedoItem = NULL;
1805 ** Add items to the menu pane, creating hierarchical sub-menus as
1806 ** necessary
1808 allocUserMenuList(menu->sumMainMenuList, subMenus->usmcNbrOfMainMenuItems);
1809 for (n=0; n<menu->sumNbrOfListItems; n++) {
1810 item = menu->sumItemList[n];
1811 info = menu->sumInfoList[n];
1812 menuList = menu->sumMainMenuList;
1813 subMenuDepth = 0;
1815 fullName = info->umiName;
1817 /* create/find sub-menus, stripping off '>' until item name is
1818 reached, then create the menu item */
1819 namePtr = fullName;
1820 subPane = menu->sumMenuPane;
1821 for (;;) {
1822 subSep = strchr(namePtr, '>');
1823 if (subSep == NULL) {
1824 btn = createUserMenuItem(subPane, namePtr, item, n,
1825 (XtCallbackProc)(menuType == SHELL_CMDS ? shellMenuCB :
1826 (menuType == MACRO_CMDS ? macroMenuCB : bgMenuCB)),
1827 (XtPointer)window);
1828 if (menuType == BG_MENU_CMDS && !strcmp(item->cmd, "undo()\n"))
1829 window->bgMenuUndoItem = btn;
1830 else if (menuType == BG_MENU_CMDS && !strcmp(item->cmd,"redo()\n"))
1831 window->bgMenuRedoItem = btn;
1832 /* generate accelerator keys */
1833 genAccelEventName(accKeysBuf, item->modifiers, item->keysym);
1834 accKeys = item->keysym == NoSymbol ? NULL : XtNewString(accKeysBuf);
1835 /* create corresponding menu list item */
1836 menuList->umlItems[menuList->umlNbrItems ++] =
1837 allocUserMenuListElement(btn, accKeys);
1838 break;
1840 hierName = copySubstring(fullName, subSep - fullName);
1841 subMenuInfo = findSubMenuInfo(subMenus, hierName);
1842 newSubPane = findInMenuTree(menuTree, nTreeEntries, hierName);
1843 if (newSubPane == NULL) {
1844 subMenuName = copySubstring(namePtr, subSep - namePtr);
1845 newSubPane = createUserSubMenu(subPane, subMenuName, &btn);
1846 XtFree(subMenuName);
1847 menuTree[nTreeEntries].name = hierName;
1848 menuTree[nTreeEntries++].menuPane = newSubPane;
1850 currentLE = allocUserMenuListElement(btn, NULL);
1851 menuList->umlItems[menuList->umlNbrItems ++] = currentLE;
1852 currentLE->umleSubMenuPane = newSubPane;
1853 currentLE->umleSubMenuList =
1854 allocUserSubMenuList(subMenuInfo->usmiId[subMenuInfo->usmiIdLen]);
1855 } else {
1856 currentLE = menuList->umlItems[subMenuInfo->usmiId[subMenuDepth]];
1857 XtFree(hierName);
1859 subPane = newSubPane;
1860 menuList = currentLE->umleSubMenuList;
1861 subMenuDepth ++;
1862 namePtr = subSep + 1;
1866 *menu->sumMenuCreated = True;
1868 /* Free the structure used to keep track of sub-menus durring creation */
1869 for (i=0; i<nTreeEntries; i++)
1870 XtFree(menuTree[i].name);
1871 XtFree((char *)menuTree);
1875 ** Find the widget corresponding to a hierarchical menu name (a>b>c...)
1877 static Widget findInMenuTree(menuTreeItem *menuTree, int nTreeEntries,
1878 const char *hierName)
1880 int i;
1882 for (i=0; i<nTreeEntries; i++)
1883 if (!strcmp(hierName, menuTree[i].name))
1884 return menuTree[i].menuPane;
1885 return NULL;
1888 static char *copySubstring(const char *string, int length)
1890 char *retStr = XtMalloc(length + 1);
1892 strncpy(retStr, string, length);
1893 retStr[length] = '\0';
1894 return retStr;
1897 static Widget createUserMenuItem(Widget menuPane, char *name, menuItemRec *f,
1898 int index, XtCallbackProc cbRtn, XtPointer cbArg)
1900 XmString st1, st2;
1901 char accText[MAX_ACCEL_LEN];
1902 Widget btn;
1904 generateAcceleratorString(accText, f->modifiers, f->keysym);
1905 st1=XmStringCreateSimple(name);
1906 st2=XmStringCreateSimple(accText);
1907 btn = XtVaCreateWidget("cmd", xmPushButtonWidgetClass, menuPane,
1908 XmNlabelString, st1,
1909 XmNacceleratorText, st2,
1910 XmNmnemonic, f->mnemonic,
1911 XmNuserData, index+10, NULL);
1912 XtAddCallback(btn, XmNactivateCallback, cbRtn, cbArg);
1913 XmStringFree(st1);
1914 XmStringFree(st2);
1915 return btn;
1919 ** Add a user-defined sub-menu to an established pull-down menu, marking
1920 ** it's userData field with TEMPORARY_MENU_ITEM so it can be found and
1921 ** removed later if the menu is redefined. Returns the menu pane of the
1922 ** new sub-menu.
1924 static Widget createUserSubMenu(Widget parent, char *label, Widget *menuItem)
1926 Widget menuPane;
1927 XmString st1;
1928 static Arg args[1] = {{XmNuserData, (XtArgVal)TEMPORARY_MENU_ITEM}};
1930 menuPane = CreatePulldownMenu(parent, "userPulldown", args, 1);
1931 *menuItem = XtVaCreateWidget("userCascade", xmCascadeButtonWidgetClass, parent,
1932 XmNlabelString, st1=XmStringCreateSimple(label),
1933 XmNsubMenuId, menuPane, XmNuserData, TEMPORARY_MENU_ITEM,
1934 NULL);
1935 XmStringFree(st1);
1936 return menuPane;
1940 ** Cache user menus:
1941 ** Delete all variable menu items of given menu pane
1943 static void deleteMenuItems(Widget menuPane)
1945 WidgetList itemList, items;
1946 Cardinal nItems;
1947 Widget subMenuID;
1948 XtPointer userData;
1949 int n;
1951 /* Fetch the list of children from the menu pane to delete */
1952 XtVaGetValues(menuPane, XmNchildren, &itemList,
1953 XmNnumChildren, &nItems, NULL);
1955 /* make a copy because the widget alters the list as you delete widgets */
1956 items = (WidgetList)XtMalloc(sizeof(Widget) * nItems);
1957 memcpy(items, itemList, sizeof(Widget) * nItems);
1959 /* delete all of the widgets not marked as PERMANENT_MENU_ITEM */
1960 for (n=0; n<(int)nItems; n++) {
1961 XtVaGetValues(items[n], XmNuserData, &userData, NULL);
1962 if (userData != (XtPointer)PERMANENT_MENU_ITEM) {
1963 if (XtClass(items[n]) == xmCascadeButtonWidgetClass) {
1964 XtVaGetValues(items[n], XmNsubMenuId, &subMenuID, NULL);
1966 /* prevent dangling submenu tearoffs */
1967 if (!XmIsMenuShell(XtParent(subMenuID)))
1968 _XmDismissTearOff(XtParent(subMenuID), NULL, NULL);
1970 deleteMenuItems(subMenuID);
1971 #if XmVersion < 2000
1972 /* Skipping this creates a memory and server resource
1973 leak (though both are reclaimed on window closing). In
1974 Motif 2.0 (and beyond?) there is a potential crash during
1975 phase 2 widget destruction in "SetCascadeField", and in
1976 Motif 1.2 there are free-memory reads. I would really like
1977 to be able to destroy this. */
1978 XtDestroyWidget(subMenuID);
1979 #endif
1980 } else {
1981 /* remove accel. before destroy or lose it forever */
1982 XtVaSetValues(items[n], XmNaccelerator, NULL, NULL);
1984 XtDestroyWidget(items[n]);
1987 XtFree((char *)items);
1990 static void closeCB(Widget w, XtPointer clientData, XtPointer callData)
1992 userCmdDialog *ucd = (userCmdDialog *)clientData;
1994 /* Mark that there's no longer a (macro, bg, or shell) dialog up */
1995 if (ucd->dialogType == SHELL_CMDS)
1996 ShellCmdDialog = NULL;
1997 else if (ucd->dialogType == MACRO_CMDS)
1998 MacroCmdDialog = NULL;
1999 else
2000 BGMenuCmdDialog = NULL;
2002 /* pop down and destroy the dialog (memory for ucd is freed in the
2003 destroy callback) */
2004 XtDestroyWidget(ucd->dlogShell);
2007 static void okCB(Widget w, XtPointer clientData, XtPointer callData)
2009 userCmdDialog *ucd = (userCmdDialog *)clientData;
2011 /* Read the dialog fields, and update the menus */
2012 if (!applyDialogChanges(ucd))
2013 return;
2015 /* Mark that there's no longer a (macro, bg, or shell) dialog up */
2016 if (ucd->dialogType == SHELL_CMDS)
2017 ShellCmdDialog = NULL;
2018 else if (ucd->dialogType == MACRO_CMDS)
2019 MacroCmdDialog = NULL;
2020 else
2021 BGMenuCmdDialog = NULL;
2023 /* pop down and destroy the dialog (memory for ucd is freed in the
2024 destroy callback) */
2025 XtDestroyWidget(ucd->dlogShell);
2028 static void applyCB(Widget w, XtPointer clientData, XtPointer callData)
2030 applyDialogChanges((userCmdDialog *)clientData);
2033 static void checkCB(Widget w, XtPointer clientData, XtPointer callData)
2035 userCmdDialog *ucd = (userCmdDialog *)clientData;
2037 if (checkMacro(ucd))
2039 DialogF(DF_INF, ucd->dlogShell, 1, "Macro",
2040 "Macro compiled without error", "OK");
2044 static int checkMacro(userCmdDialog *ucd)
2046 menuItemRec *f;
2048 f = readDialogFields(ucd, False);
2049 if (f == NULL)
2050 return False;
2051 if (!checkMacroText(f->cmd, ucd->dlogShell, ucd->cmdTextW)) {
2052 freeMenuItemRec(f);
2053 return False;
2055 return True;
2058 static int checkMacroText(char *macro, Widget errorParent, Widget errFocus)
2060 Program *prog;
2061 char *errMsg, *stoppedAt;
2063 prog = ParseMacro(macro, &errMsg, &stoppedAt);
2064 if (prog == NULL) {
2065 if (errorParent != NULL) {
2066 ParseError(errorParent, macro, stoppedAt, "macro", errMsg);
2067 XmTextSetInsertionPosition(errFocus, stoppedAt - macro);
2068 XmProcessTraversal(errFocus, XmTRAVERSE_CURRENT);
2070 return False;
2072 FreeProgram(prog);
2073 if (*stoppedAt != '\0') {
2074 if (errorParent != NULL) {
2075 ParseError(errorParent, macro, stoppedAt,"macro","syntax error");
2076 XmTextSetInsertionPosition(errFocus, stoppedAt - macro);
2077 XmProcessTraversal(errFocus, XmTRAVERSE_CURRENT);
2079 return False;
2081 return True;
2084 static int applyDialogChanges(userCmdDialog *ucd)
2086 int i;
2088 /* Get the current contents of the dialog fields */
2089 if (!UpdateManagedList(ucd->managedList, True))
2090 return False;
2092 /* Test compile the macro */
2093 if (ucd->dialogType == MACRO_CMDS)
2094 if (!checkMacro(ucd))
2095 return False;
2097 /* Update the menu information */
2098 if (ucd->dialogType == SHELL_CMDS) {
2099 for (i=0; i<NShellMenuItems; i++)
2100 freeMenuItemRec(ShellMenuItems[i]);
2101 freeUserMenuInfoList(ShellMenuInfo, NShellMenuItems);
2102 freeSubMenuCache(&ShellSubMenus);
2103 for (i=0; i<ucd->nMenuItems; i++)
2104 ShellMenuItems[i] = copyMenuItemRec(ucd->menuItemsList[i]);
2105 NShellMenuItems = ucd->nMenuItems;
2106 parseMenuItemList(ShellMenuItems, NShellMenuItems, ShellMenuInfo, &ShellSubMenus);
2107 } else if (ucd->dialogType == MACRO_CMDS) {
2108 for (i=0; i<NMacroMenuItems; i++)
2109 freeMenuItemRec(MacroMenuItems[i]);
2110 freeUserMenuInfoList(MacroMenuInfo, NMacroMenuItems);
2111 freeSubMenuCache(&MacroSubMenus);
2112 for (i=0; i<ucd->nMenuItems; i++)
2113 MacroMenuItems[i] = copyMenuItemRec(ucd->menuItemsList[i]);
2114 NMacroMenuItems = ucd->nMenuItems;
2115 parseMenuItemList(MacroMenuItems, NMacroMenuItems, MacroMenuInfo, &MacroSubMenus);
2116 } else { /* BG_MENU_CMDS */
2117 for (i=0; i<NBGMenuItems; i++)
2118 freeMenuItemRec(BGMenuItems[i]);
2119 freeUserMenuInfoList(BGMenuInfo, NBGMenuItems);
2120 freeSubMenuCache(&BGSubMenus);
2121 for (i=0; i<ucd->nMenuItems; i++)
2122 BGMenuItems[i] = copyMenuItemRec(ucd->menuItemsList[i]);
2123 NBGMenuItems = ucd->nMenuItems;
2124 parseMenuItemList(BGMenuItems, NBGMenuItems, BGMenuInfo, &BGSubMenus);
2127 /* Update the menus themselves in all of the NEdit windows */
2128 rebuildMenuOfAllWindows(ucd->dialogType);
2130 /* Note that preferences have been changed */
2131 MarkPrefsChanged();
2132 return True;
2135 static void pasteReplayCB(Widget w, XtPointer clientData, XtPointer callData)
2137 userCmdDialog *ucd = (userCmdDialog *)clientData;
2139 if (GetReplayMacro() == NULL)
2140 return;
2142 XmTextInsert(ucd->cmdTextW, XmTextGetInsertionPosition(ucd->cmdTextW),
2143 GetReplayMacro());
2146 static void destroyCB(Widget w, XtPointer clientData, XtPointer callData)
2148 userCmdDialog *ucd = (userCmdDialog *)clientData;
2149 int i;
2151 for (i=0; i<ucd->nMenuItems; i++)
2152 freeMenuItemRec(ucd->menuItemsList[i]);
2153 XtFree((char *)ucd->menuItemsList);
2154 XtFree((char *)ucd);
2157 static void accFocusCB(Widget w, XtPointer clientData, XtPointer callData)
2159 userCmdDialog *ucd = (userCmdDialog *)clientData;
2161 RemoveDialogMnemonicHandler(XtParent(ucd->accTextW));
2164 static void accLoseFocusCB(Widget w, XtPointer clientData, XtPointer callData)
2166 userCmdDialog *ucd = (userCmdDialog *)clientData;
2168 AddDialogMnemonicHandler(XtParent(ucd->accTextW), FALSE);
2171 static void accKeyCB(Widget w, XtPointer clientData, XKeyEvent *event)
2173 userCmdDialog *ucd = (userCmdDialog *)clientData;
2174 KeySym keysym = XLookupKeysym(event, 0);
2175 char outStr[MAX_ACCEL_LEN];
2177 /* Accept only real keys, not modifiers alone */
2178 if (IsModifierKey(keysym))
2179 return;
2181 /* Tab key means go to the next field, don't enter */
2182 if (keysym == XK_Tab)
2183 return;
2185 /* Beep and return if the modifiers are buttons or ones we don't support */
2186 if (event->state & ~(ShiftMask | LockMask | ControlMask | Mod1Mask |
2187 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask)) {
2188 XBell(TheDisplay, 0);
2189 return;
2192 /* Delete or backspace clears field */
2193 if (keysym == XK_Delete || keysym == XK_BackSpace) {
2194 XmTextSetString(ucd->accTextW, "");
2195 return;
2198 /* generate the string to use in the dialog field */
2199 generateAcceleratorString(outStr, event->state, keysym);
2201 /* Reject single character accelerators (a very simple way to eliminate
2202 un-modified letters and numbers) The goal is give users a clue that
2203 they're supposed to type the actual keys, not the name. This scheme
2204 is not rigorous and still allows accelerators like Comma. */
2205 if (strlen(outStr) == 1) {
2206 XBell(TheDisplay, 0);
2207 return;
2210 /* fill in the accelerator field in the dialog */
2211 XmTextSetString(ucd->accTextW, outStr);
2214 static void sameOutCB(Widget w, XtPointer clientData, XtPointer callData)
2216 XtSetSensitive(((userCmdDialog *)clientData)->repInpBtn,
2217 XmToggleButtonGetState(w));
2220 static void shellMenuCB(Widget w, WindowInfo *window, XtPointer callData)
2222 XtArgVal userData;
2223 int index;
2224 char *params[1];
2226 window = WidgetToWindow(MENU_WIDGET(w));
2228 /* get the index of the shell command and verify that it's in range */
2229 XtVaGetValues(w, XmNuserData, &userData, NULL);
2230 index = (int)userData - 10;
2231 if (index <0 || index >= NShellMenuItems)
2232 return;
2234 params[0] = ShellMenuItems[index]->name;
2235 XtCallActionProc(window->lastFocus, "shell_menu_command",
2236 ((XmAnyCallbackStruct *)callData)->event, params, 1);
2239 static void macroMenuCB(Widget w, WindowInfo *window, XtPointer callData)
2241 XtArgVal userData;
2242 int index;
2243 char *params[1];
2245 window = WidgetToWindow(MENU_WIDGET(w));
2247 /* Don't allow users to execute a macro command from the menu (or accel)
2248 if there's already a macro command executing. NEdit can't handle
2249 running multiple, independent uncoordinated, macros in the same
2250 window. Macros may invoke macro menu commands recursively via the
2251 macro_menu_command action proc, which is important for being able to
2252 repeat any operation, and to embed macros within eachother at any
2253 level, however, a call here with a macro running means that THE USER
2254 is explicitly invoking another macro via the menu or an accelerator. */
2255 if (window->macroCmdData != NULL) {
2256 XBell(TheDisplay, 0);
2257 return;
2260 /* get the index of the macro command and verify that it's in range */
2261 XtVaGetValues(w, XmNuserData, &userData, NULL);
2262 index = (int)userData - 10;
2263 if (index <0 || index >= NMacroMenuItems)
2264 return;
2266 params[0] = MacroMenuItems[index]->name;
2267 XtCallActionProc(window->lastFocus, "macro_menu_command",
2268 ((XmAnyCallbackStruct *)callData)->event, params, 1);
2271 static void bgMenuCB(Widget w, WindowInfo *window, XtPointer callData)
2273 XtArgVal userData;
2274 int index;
2275 char *params[1];
2277 /* Same remark as for macro menu commands (see above). */
2278 if (window->macroCmdData != NULL) {
2279 XBell(TheDisplay, 0);
2280 return;
2283 /* get the index of the macro command and verify that it's in range */
2284 XtVaGetValues(w, XmNuserData, &userData, NULL);
2285 index = (int)userData - 10;
2286 if (index <0 || index >= NBGMenuItems)
2287 return;
2289 params[0] = BGMenuItems[index]->name;
2290 XtCallActionProc(window->lastFocus, "bg_menu_command",
2291 ((XmAnyCallbackStruct *)callData)->event, params, 1);
2295 ** Update the name, accelerator, mnemonic, and command fields in the shell
2296 ** command or macro dialog to agree with the currently selected item in the
2297 ** menu item list.
2299 static void updateDialogFields(menuItemRec *f, userCmdDialog *ucd)
2301 char mneString[2], accString[MAX_ACCEL_LEN];
2303 /* fill in the name, accelerator, mnemonic, and command fields of the
2304 dialog for the newly selected item, or blank them if "New" is selected */
2305 if (f == NULL) {
2306 XmTextSetString(ucd->nameTextW, "");
2307 XmTextSetString(ucd->cmdTextW, "");
2308 XmTextSetString(ucd->accTextW, "");
2309 XmTextSetString(ucd->mneTextW, "");
2310 if (ucd->dialogType == SHELL_CMDS) {
2311 RadioButtonChangeState(ucd->selInpBtn, True, True);
2312 RadioButtonChangeState(ucd->sameOutBtn, True, True);
2313 RadioButtonChangeState(ucd->repInpBtn, False, False);
2314 XtSetSensitive(ucd->repInpBtn, True);
2315 RadioButtonChangeState(ucd->saveFirstBtn, False, False);
2316 RadioButtonChangeState(ucd->loadAfterBtn, False, False);
2318 } else {
2319 mneString[0] = f->mnemonic;
2320 mneString[1] = '\0';
2321 generateAcceleratorString(accString, f->modifiers, f->keysym);
2322 XmTextSetString(ucd->nameTextW, f->name);
2323 XmTextSetString(ucd->cmdTextW, f->cmd);
2324 XmTextSetString(ucd->accTextW, accString);
2325 XmTextSetString(ucd->mneTextW, mneString);
2326 RadioButtonChangeState(ucd->selInpBtn, f->input==FROM_SELECTION, False);
2327 if (ucd->dialogType == SHELL_CMDS) {
2328 RadioButtonChangeState(ucd->winInpBtn, f->input == FROM_WINDOW,
2329 False);
2330 RadioButtonChangeState(ucd->eitherInpBtn, f->input == FROM_EITHER,
2331 False);
2332 RadioButtonChangeState(ucd->noInpBtn, f->input == FROM_NONE,
2333 False);
2334 RadioButtonChangeState(ucd->sameOutBtn, f->output==TO_SAME_WINDOW,
2335 False);
2336 RadioButtonChangeState(ucd->winOutBtn, f->output==TO_NEW_WINDOW,
2337 False);
2338 RadioButtonChangeState(ucd->dlogOutBtn, f->output==TO_DIALOG,
2339 False);
2340 RadioButtonChangeState(ucd->repInpBtn, f->repInput, False);
2341 XtSetSensitive(ucd->repInpBtn, f->output==TO_SAME_WINDOW);
2342 RadioButtonChangeState(ucd->saveFirstBtn, f->saveFirst, False);
2343 RadioButtonChangeState(ucd->loadAfterBtn, f->loadAfter, False);
2349 ** Read the name, accelerator, mnemonic, and command fields from the shell or
2350 ** macro commands dialog into a newly allocated menuItemRec. Returns a
2351 ** pointer to the new menuItemRec structure as the function value, or NULL on
2352 ** failure.
2354 static menuItemRec *readDialogFields(userCmdDialog *ucd, int silent)
2356 char *nameText, *cmdText, *mneText, *accText;
2357 menuItemRec *f;
2359 nameText = XmTextGetString(ucd->nameTextW);
2360 if (*nameText == '\0')
2362 if (!silent)
2364 DialogF(DF_WARN, ucd->dlogShell, 1, "Menu Entry",
2365 "Please specify a name\nfor the menu item", "OK");
2366 XmProcessTraversal(ucd->nameTextW, XmTRAVERSE_CURRENT);
2368 XtFree(nameText);
2369 return NULL;
2372 if (strchr(nameText, ':'))
2374 if (!silent)
2376 DialogF(DF_WARN, ucd->dlogShell, 1, "Menu Entry",
2377 "Menu item names may not\ncontain colon (:) characters",
2378 "OK");
2379 XmProcessTraversal(ucd->nameTextW, XmTRAVERSE_CURRENT);
2381 XtFree(nameText);
2382 return NULL;
2385 cmdText = XmTextGetString(ucd->cmdTextW);
2386 if (cmdText == NULL || *cmdText == '\0')
2388 if (!silent)
2390 DialogF(DF_WARN, ucd->dlogShell, 1, "Command to Execute",
2391 "Please specify %s to execute", "OK",
2392 ucd->dialogType == SHELL_CMDS
2393 ? "shell command"
2394 : "macro command(s)");
2395 XmProcessTraversal(ucd->cmdTextW, XmTRAVERSE_CURRENT);
2397 XtFree(nameText);
2399 if (cmdText!=NULL)
2401 XtFree(cmdText);
2403 return NULL;
2406 if (ucd->dialogType == MACRO_CMDS || ucd->dialogType == BG_MENU_CMDS) {
2407 addTerminatingNewline(&cmdText);
2408 if (!checkMacroText(cmdText, silent ? NULL : ucd->dlogShell,
2409 ucd->cmdTextW)) {
2410 XtFree(nameText);
2411 XtFree(cmdText);
2412 return NULL;
2415 f = (menuItemRec *)XtMalloc(sizeof(menuItemRec));
2416 f->name = nameText;
2417 f->cmd = cmdText;
2418 if ((mneText = XmTextGetString(ucd->mneTextW)) != NULL) {
2419 f->mnemonic = mneText==NULL ? '\0' : mneText[0];
2420 XtFree(mneText);
2421 if (f->mnemonic == ':') /* colons mess up string parsing */
2422 f->mnemonic = '\0';
2424 if ((accText = XmTextGetString(ucd->accTextW)) != NULL) {
2425 parseAcceleratorString(accText, &f->modifiers, &f->keysym);
2426 XtFree(accText);
2428 if (ucd->dialogType == SHELL_CMDS) {
2429 if (XmToggleButtonGetState(ucd->selInpBtn))
2430 f->input = FROM_SELECTION;
2431 else if (XmToggleButtonGetState(ucd->winInpBtn))
2432 f->input = FROM_WINDOW;
2433 else if (XmToggleButtonGetState(ucd->eitherInpBtn))
2434 f->input = FROM_EITHER;
2435 else
2436 f->input = FROM_NONE;
2437 if (XmToggleButtonGetState(ucd->winOutBtn))
2438 f->output = TO_NEW_WINDOW;
2439 else if (XmToggleButtonGetState(ucd->dlogOutBtn))
2440 f->output = TO_DIALOG;
2441 else
2442 f->output = TO_SAME_WINDOW;
2443 f->repInput = XmToggleButtonGetState(ucd->repInpBtn);
2444 f->saveFirst = XmToggleButtonGetState(ucd->saveFirstBtn);
2445 f->loadAfter = XmToggleButtonGetState(ucd->loadAfterBtn);
2446 } else {
2447 f->input = XmToggleButtonGetState(ucd->selInpBtn) ? FROM_SELECTION :
2448 FROM_NONE;
2449 f->output = TO_SAME_WINDOW;
2450 f->repInput = False;
2451 f->saveFirst = False;
2452 f->loadAfter = False;
2454 return f;
2458 ** Copy a menu item record, and its associated memory
2460 static menuItemRec *copyMenuItemRec(menuItemRec *item)
2462 menuItemRec *newItem;
2464 newItem = (menuItemRec *)XtMalloc(sizeof(menuItemRec));
2465 *newItem = *item;
2466 newItem->name = XtMalloc(strlen(item->name)+1);
2467 strcpy(newItem->name, item->name);
2468 newItem->cmd = XtMalloc(strlen(item->cmd)+1);
2469 strcpy(newItem->cmd, item->cmd);
2470 return newItem;
2474 ** Free a menu item record, and its associated memory
2476 static void freeMenuItemRec(menuItemRec *item)
2478 XtFree(item->name);
2479 XtFree(item->cmd);
2480 XtFree((char *)item);
2484 ** Callbacks for managed-list operations
2486 static void *getDialogDataCB(void *oldItem, int explicitRequest, int *abort,
2487 void *cbArg)
2489 userCmdDialog *ucd = (userCmdDialog *)cbArg;
2490 menuItemRec *currentFields;
2492 /* If the dialog is currently displaying the "new" entry and the
2493 fields are empty, that's just fine */
2494 if (oldItem == NULL && dialogFieldsAreEmpty(ucd))
2495 return NULL;
2497 /* If there are no problems reading the data, just return it */
2498 currentFields = readDialogFields(ucd, True);
2499 if (currentFields != NULL)
2500 return (void *)currentFields;
2502 /* If user might not be expecting fields to be read, give more warning */
2503 if (!explicitRequest)
2505 if (DialogF(DF_WARN, ucd->dlogShell, 2, "Discard Entry",
2506 "Discard incomplete entry\nfor current menu item?", "Keep",
2507 "Discard") == 2)
2509 return oldItem == NULL
2510 ? NULL
2511 : (void *)copyMenuItemRec((menuItemRec *)oldItem);
2515 /* Do readDialogFields again without "silent" mode to display warning(s) */
2516 readDialogFields(ucd, False);
2517 *abort = True;
2518 return NULL;
2522 static void setDialogDataCB(void *item, void *cbArg)
2524 updateDialogFields((menuItemRec *)item, (userCmdDialog *)cbArg);
2527 static int dialogFieldsAreEmpty(userCmdDialog *ucd)
2529 return TextWidgetIsBlank(ucd->nameTextW) &&
2530 TextWidgetIsBlank(ucd->cmdTextW) &&
2531 TextWidgetIsBlank(ucd->accTextW) &&
2532 TextWidgetIsBlank(ucd->mneTextW) &&
2533 (ucd->dialogType != SHELL_CMDS || (
2534 XmToggleButtonGetState(ucd->selInpBtn) &&
2535 XmToggleButtonGetState(ucd->sameOutBtn) &&
2536 !XmToggleButtonGetState(ucd->repInpBtn) &&
2537 !XmToggleButtonGetState(ucd->saveFirstBtn) &&
2538 !XmToggleButtonGetState(ucd->loadAfterBtn)));
2541 static void freeItemCB(void *item)
2543 freeMenuItemRec((menuItemRec *)item);
2547 ** Gut a text widget of it's ability to process input
2549 static void disableTextW(Widget textW)
2551 static XtTranslations emptyTable = NULL;
2552 static char *emptyTranslations = "\
2553 <EnterWindow>: enter()\n\
2554 <Btn1Down>: grab-focus()\n\
2555 <Btn1Motion>: extend-adjust()\n\
2556 <Btn1Up>: extend-end()\n\
2557 Shift<Key>Tab: prev-tab-group()\n\
2558 Ctrl<Key>Tab: next-tab-group()\n\
2559 <Key>Tab: next-tab-group()\n\
2560 <LeaveWindow>: leave()\n\
2561 <FocusIn>: focusIn()\n\
2562 <FocusOut>: focusOut()\n\
2563 <Unmap>: unmap()\n";
2565 /* replace the translation table with the slimmed down one above */
2566 if (emptyTable == NULL)
2567 emptyTable = XtParseTranslationTable(emptyTranslations);
2568 XtVaSetValues(textW, XmNtranslations, emptyTable, NULL);
2571 static char *writeMenuItemString(menuItemRec **menuItems, int nItems,
2572 int listType)
2574 char *outStr, *outPtr, *c, accStr[MAX_ACCEL_LEN];
2575 menuItemRec *f;
2576 int i, length;
2578 /* determine the max. amount of memory needed for the returned string
2579 and allocate a buffer for composing the string */
2580 length = 0;
2581 for (i=0; i<nItems; i++) {
2582 f = menuItems[i];
2583 generateAcceleratorString(accStr, f->modifiers, f->keysym);
2584 length += strlen(f->name) * 2; /* allow for \n & \\ expansions */
2585 length += strlen(accStr);
2586 length += strlen(f->cmd) * 6; /* allow for \n & \\ expansions */
2587 length += 21; /* number of characters added below */
2589 length++; /* terminating null */
2590 outStr = XtMalloc(length);
2592 /* write the string */
2593 outPtr = outStr;
2594 *outPtr++ = '\\';
2595 *outPtr++ = '\n';
2596 for (i=0; i<nItems; i++) {
2597 f = menuItems[i];
2598 generateAcceleratorString(accStr, f->modifiers, f->keysym);
2599 *outPtr++ = '\t';
2600 for (c=f->name; *c!='\0'; ++c) { /* Copy the command name */
2601 if (*c == '\\') { /* changing backslashes to \\ */
2602 *outPtr++ = '\\';
2603 *outPtr++ = '\\';
2604 } else if (*c == '\n') { /* changing newlines to \n */
2605 *outPtr++ = '\\';
2606 *outPtr++ = 'n';
2607 } else {
2608 *outPtr++ = *c;
2611 *outPtr++ = ':';
2612 strcpy(outPtr, accStr);
2613 outPtr += strlen(accStr);
2614 *outPtr++ = ':';
2615 if (f->mnemonic != '\0')
2616 *outPtr++ = f->mnemonic;
2617 *outPtr++ = ':';
2618 if (listType == SHELL_CMDS) {
2619 if (f->input == FROM_SELECTION)
2620 *outPtr++ = 'I';
2621 else if (f->input == FROM_WINDOW)
2622 *outPtr++ = 'A';
2623 else if (f->input == FROM_EITHER)
2624 *outPtr++ = 'E';
2625 if (f->output == TO_DIALOG)
2626 *outPtr++ = 'D';
2627 else if (f->output == TO_NEW_WINDOW)
2628 *outPtr++ = 'W';
2629 if (f->repInput)
2630 *outPtr++ = 'X';
2631 if (f->saveFirst)
2632 *outPtr++ = 'S';
2633 if (f->loadAfter)
2634 *outPtr++ = 'L';
2635 *outPtr++ = ':';
2636 } else {
2637 if (f->input == FROM_SELECTION)
2638 *outPtr++ = 'R';
2639 *outPtr++ = ':';
2640 *outPtr++ = ' ';
2641 *outPtr++ = '{';
2643 *outPtr++ = '\\';
2644 *outPtr++ = 'n';
2645 *outPtr++ = '\\';
2646 *outPtr++ = '\n';
2647 *outPtr++ = '\t';
2648 *outPtr++ = '\t';
2649 for (c=f->cmd; *c!='\0'; c++) { /* Copy the command string, changing */
2650 if (*c == '\\') { /* backslashes to double backslashes */
2651 *outPtr++ = '\\'; /* and newlines to backslash-n's, */
2652 *outPtr++ = '\\'; /* followed by real newlines and tab */
2653 } else if (*c == '\n') {
2654 *outPtr++ = '\\';
2655 *outPtr++ = 'n';
2656 *outPtr++ = '\\';
2657 *outPtr++ = '\n';
2658 *outPtr++ = '\t';
2659 *outPtr++ = '\t';
2660 } else
2661 *outPtr++ = *c;
2663 if (listType == MACRO_CMDS || listType == BG_MENU_CMDS) {
2664 if (*(outPtr-1) == '\t') outPtr--;
2665 *outPtr++ = '}';
2667 *outPtr++ = '\\';
2668 *outPtr++ = 'n';
2669 *outPtr++ = '\\';
2670 *outPtr++ = '\n';
2672 --outPtr;
2673 *--outPtr = '\0';
2674 return outStr;
2677 static int loadMenuItemString(char *inString, menuItemRec **menuItems,
2678 int *nItems, int listType)
2680 menuItemRec *f;
2681 char *cmdStr;
2682 char *inPtr = inString;
2683 char *nameStr, accStr[MAX_ACCEL_LEN], mneChar;
2684 KeySym keysym;
2685 unsigned int modifiers;
2686 int i, input, output, saveFirst, loadAfter, repInput;
2687 int nameLen, accLen, mneLen, cmdLen;
2689 for (;;) {
2691 /* remove leading whitespace */
2692 while (*inPtr == ' ' || *inPtr == '\t')
2693 inPtr++;
2695 /* read name field */
2696 nameLen = strcspn(inPtr, ":");
2697 if (nameLen == 0)
2698 return parseError("no name field");
2699 nameStr = XtMalloc(nameLen+1);
2700 strncpy(nameStr, inPtr, nameLen);
2701 nameStr[nameLen] = '\0';
2702 inPtr += nameLen;
2703 if (*inPtr == '\0')
2704 return parseError("end not expected");
2705 inPtr++;
2707 /* read accelerator field */
2708 accLen = strcspn(inPtr, ":");
2709 if (accLen >= MAX_ACCEL_LEN)
2710 return parseError("accelerator field too long");
2711 strncpy(accStr, inPtr, accLen);
2712 accStr[accLen] = '\0';
2713 inPtr += accLen;
2714 if (*inPtr == '\0')
2715 return parseError("end not expected");
2716 inPtr++;
2718 /* read menemonic field */
2719 mneLen = strcspn(inPtr, ":");
2720 if (mneLen > 1)
2721 return parseError("mnemonic field too long");
2722 if (mneLen == 1)
2723 mneChar = *inPtr++;
2724 else
2725 mneChar = '\0';
2726 inPtr++;
2727 if (*inPtr == '\0')
2728 return parseError("end not expected");
2730 /* read flags field */
2731 input = FROM_NONE;
2732 output = TO_SAME_WINDOW;
2733 repInput = False;
2734 saveFirst = False;
2735 loadAfter = False;
2736 for (; *inPtr != ':'; inPtr++) {
2737 if (listType == SHELL_CMDS) {
2738 if (*inPtr == 'I')
2739 input = FROM_SELECTION;
2740 else if (*inPtr == 'A')
2741 input = FROM_WINDOW;
2742 else if (*inPtr == 'E')
2743 input = FROM_EITHER;
2744 else if (*inPtr == 'W')
2745 output = TO_NEW_WINDOW;
2746 else if (*inPtr == 'D')
2747 output = TO_DIALOG;
2748 else if (*inPtr == 'X')
2749 repInput = True;
2750 else if (*inPtr == 'S')
2751 saveFirst = True;
2752 else if (*inPtr == 'L')
2753 loadAfter = True;
2754 else
2755 return parseError("unreadable flag field");
2756 } else {
2757 if (*inPtr == 'R')
2758 input = FROM_SELECTION;
2759 else
2760 return parseError("unreadable flag field");
2763 inPtr++;
2765 /* read command field */
2766 if (listType == SHELL_CMDS) {
2767 if (*inPtr++ != '\n')
2768 return parseError("command must begin with newline");
2769 while (*inPtr == ' ' || *inPtr == '\t') /* leading whitespace */
2770 inPtr++;
2771 cmdLen = strcspn(inPtr, "\n");
2772 if (cmdLen == 0)
2773 return parseError("shell command field is empty");
2774 cmdStr = XtMalloc(cmdLen+1);
2775 strncpy(cmdStr, inPtr, cmdLen);
2776 cmdStr[cmdLen] = '\0';
2777 inPtr += cmdLen;
2778 } else {
2779 cmdStr = copyMacroToEnd(&inPtr);
2780 if (cmdStr == NULL)
2781 return False;
2783 while (*inPtr == ' ' || *inPtr == '\t' || *inPtr == '\n')
2784 inPtr++; /* skip trailing whitespace & newline */
2786 /* parse the accelerator field */
2787 if (!parseAcceleratorString(accStr, &modifiers, &keysym))
2788 return parseError("couldn't read accelerator field");
2790 /* create a menu item record */
2791 f = (menuItemRec *)XtMalloc(sizeof(menuItemRec));
2792 f->name = nameStr;
2793 f->cmd = cmdStr;
2794 f->mnemonic = mneChar;
2795 f->modifiers = modifiers;
2796 f->input = input;
2797 f->output = output;
2798 f->repInput = repInput;
2799 f->saveFirst = saveFirst;
2800 f->loadAfter = loadAfter;
2801 f->keysym = keysym;
2803 /* add/replace menu record in the list */
2804 for (i=0; i < *nItems; i++) {
2805 if (!strcmp(menuItems[i]->name, f->name)) {
2806 freeMenuItemRec(menuItems[i]);
2807 menuItems[i] = f;
2808 break;
2811 if (i == *nItems)
2812 menuItems[(*nItems)++] = f;
2814 /* end of string in proper place */
2815 if (*inPtr == '\0')
2816 return True;
2820 static int parseError(const char *message)
2822 fprintf(stderr, "NEdit: Parse error in user defined menu item, %s\n",
2823 message);
2824 return False;
2828 ** Create a text string representing an accelerator for the dialog,
2829 ** the shellCommands or macroCommands resource, and for the menu item.
2831 static void generateAcceleratorString(char *text, unsigned int modifiers,
2832 KeySym keysym)
2834 char *shiftStr = "", *ctrlStr = "", *altStr = "";
2835 char *mod2Str = "", *mod3Str = "", *mod4Str = "", *mod5Str = "";
2836 char keyName[20];
2837 Modifiers numLockMask = GetNumLockModMask(TheDisplay);
2839 /* if there's no accelerator, generate an empty string */
2840 if (keysym == NoSymbol) {
2841 *text = '\0';
2842 return;
2846 /* Translate the modifiers into strings.
2847 Lock and NumLock are always ignored (see util/misc.c),
2848 so we don't display them either. */
2849 if (modifiers & ShiftMask)
2850 shiftStr = "Shift+";
2851 if (modifiers & ControlMask)
2852 ctrlStr = "Ctrl+";
2853 if (modifiers & Mod1Mask)
2854 altStr = "Alt+";
2855 if ((modifiers & Mod2Mask) && (Mod2Mask != numLockMask))
2856 mod2Str = "Mod2+";
2857 if ((modifiers & Mod3Mask) && (Mod3Mask != numLockMask))
2858 mod3Str = "Mod3+";
2859 if ((modifiers & Mod4Mask) && (Mod4Mask != numLockMask))
2860 mod4Str = "Mod4+";
2861 if ((modifiers & Mod5Mask) && (Mod5Mask != numLockMask))
2862 mod5Str = "Mod5+";
2864 /* for a consistent look to the accelerator names in the menus,
2865 capitalize the first letter of the keysym */
2866 strcpy(keyName, XKeysymToString(keysym));
2867 *keyName = toupper(*keyName);
2869 /* concatenate the strings together */
2870 sprintf(text, "%s%s%s%s%s%s%s%s", shiftStr, ctrlStr, altStr,
2871 mod2Str, mod3Str, mod4Str, mod5Str, keyName);
2875 ** Create a translation table event description string for the menu
2876 ** XmNaccelerator resource.
2878 static void genAccelEventName(char *text, unsigned int modifiers,
2879 KeySym keysym)
2881 char *shiftStr = "", *lockStr = "", *ctrlStr = "", *altStr = "";
2882 char *mod2Str = "", *mod3Str = "", *mod4Str = "", *mod5Str = "";
2884 /* if there's no accelerator, generate an empty string */
2885 if (keysym == NoSymbol) {
2886 *text = '\0';
2887 return;
2890 /* translate the modifiers into strings */
2891 if (modifiers & ShiftMask)
2892 shiftStr = "Shift ";
2893 if (modifiers & LockMask)
2894 lockStr = "Lock ";
2895 if (modifiers & ControlMask)
2896 ctrlStr = "Ctrl ";
2897 if (modifiers & Mod1Mask)
2898 altStr = "Alt ";
2899 if (modifiers & Mod2Mask)
2900 mod2Str = "Mod2 ";
2901 if (modifiers & Mod3Mask)
2902 mod3Str = "Mod3 ";
2903 if (modifiers & Mod4Mask)
2904 mod4Str = "Mod4 ";
2905 if (modifiers & Mod5Mask)
2906 mod5Str = "Mod5 ";
2908 /* put the modifiers together with the key name */
2909 sprintf(text, "%s%s%s%s%s%s%s%s<Key>%s",
2910 shiftStr, lockStr, ctrlStr, altStr,
2911 mod2Str, mod3Str, mod4Str, mod5Str,
2912 XKeysymToString(keysym));
2916 ** Read an accelerator name and put it into the form of a modifier mask
2917 ** and a KeySym code. Returns false if string can't be read
2918 ** ... does not handle whitespace in string (look at scanf)
2920 static int parseAcceleratorString(const char *string, unsigned int *modifiers,
2921 KeySym *keysym)
2923 int i, nFields, inputLength = strlen(string);
2924 char fields[10][MAX_ACCEL_LEN];
2926 /* a blank field means no accelerator */
2927 if (inputLength == 0) {
2928 *modifiers = 0;
2929 *keysym = NoSymbol;
2930 return True;
2933 /* limit the string length so no field strings will overflow */
2934 if (inputLength > MAX_ACCEL_LEN)
2935 return False;
2937 /* divide the input into '+' separated fields */
2938 nFields = sscanf(string, "%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]",
2939 fields[0], fields[1], fields[2], fields[3], fields[4], fields[5],
2940 fields[6], fields[7], fields[8], fields[9]);
2941 if (nFields == 0)
2942 return False;
2944 /* get the key name from the last field and translate it to a keysym.
2945 If the name is capitalized, try it lowercase as well, since some
2946 of the keysyms are "prettied up" by generateAcceleratorString */
2947 *keysym = XStringToKeysym(fields[nFields-1]);
2948 if (*keysym == NoSymbol) {
2949 *fields[nFields-1] = tolower(*fields[nFields-1]);
2950 *keysym = XStringToKeysym(fields[nFields-1]);
2951 if (*keysym == NoSymbol)
2952 return False;
2955 /* parse the modifier names from the rest of the fields */
2956 *modifiers = 0;
2957 for (i=0; i<nFields-1; i++) {
2958 if (!strcmp(fields[i], "Shift"))
2959 *modifiers |= ShiftMask;
2960 else if (!strcmp(fields[i], "Lock"))
2961 *modifiers |= LockMask;
2962 else if (!strcmp(fields[i], "Ctrl"))
2963 *modifiers |= ControlMask;
2964 /* comparision with "Alt" for compatibility with old .nedit files*/
2965 else if (!strcmp(fields[i], "Alt"))
2966 *modifiers |= Mod1Mask;
2967 else if (!strcmp(fields[i], "Mod2"))
2968 *modifiers |= Mod2Mask;
2969 else if (!strcmp(fields[i], "Mod3"))
2970 *modifiers |= Mod3Mask;
2971 else if (!strcmp(fields[i], "Mod4"))
2972 *modifiers |= Mod4Mask;
2973 else if (!strcmp(fields[i], "Mod5"))
2974 *modifiers |= Mod5Mask;
2975 else
2976 return False;
2979 /* all fields successfully parsed */
2980 return True;
2984 ** Scan text from "*inPtr" to the end of macro input (matching brace),
2985 ** advancing inPtr, and return macro text as function return value.
2987 ** This is kind of wastefull in that it throws away the compiled macro,
2988 ** to be re-generated from the text as needed, but compile time is
2989 ** negligible for most macros.
2991 static char *copyMacroToEnd(char **inPtr)
2993 char *retStr, *errMsg, *stoppedAt, *p, *retPtr;
2994 Program *prog;
2996 /* Skip over whitespace to find make sure there's a beginning brace
2997 to anchor the parse (if not, it will take the whole file) */
2998 *inPtr += strspn(*inPtr, " \t\n");
2999 if (**inPtr != '{') {
3000 ParseError(NULL, *inPtr, *inPtr-1, "macro menu item", "expecting '{'");
3001 return NULL;
3004 /* Parse the input */
3005 prog = ParseMacro(*inPtr, &errMsg, &stoppedAt);
3006 if (prog == NULL) {
3007 ParseError(NULL, *inPtr, stoppedAt, "macro menu item", errMsg);
3008 return NULL;
3010 FreeProgram(prog);
3012 /* Copy and return the body of the macro, stripping outer braces and
3013 extra leading tabs added by the writer routine */
3014 (*inPtr)++;
3015 *inPtr += strspn(*inPtr, " \t");
3016 if (**inPtr == '\n') (*inPtr)++;
3017 if (**inPtr == '\t') (*inPtr)++;
3018 if (**inPtr == '\t') (*inPtr)++;
3019 retPtr = retStr = XtMalloc(stoppedAt - *inPtr + 1);
3020 for (p = *inPtr; p < stoppedAt - 1; p++) {
3021 if (!strncmp(p, "\n\t\t", 3)) {
3022 *retPtr++ = '\n';
3023 p += 2;
3024 } else
3025 *retPtr++ = *p;
3027 if (*(retPtr-1) == '\t') retPtr--;
3028 *retPtr = '\0';
3029 *inPtr = stoppedAt;
3030 return retStr;
3034 ** If "*string" is not terminated with a newline character, reallocate the
3035 ** string and add one. (The macro language requires newline terminators for
3036 ** statements, but the text widget doesn't force it like the NEdit text buffer
3037 ** does, so this might avoid some confusion.)
3039 static void addTerminatingNewline(char **string)
3041 char *newString;
3042 int length;
3044 length = strlen(*string);
3045 if ((*string)[length-1] != '\n') {
3046 newString = XtMalloc(length + 2);
3047 strcpy(newString, *string);
3048 newString[length] = '\n';
3049 newString[length+1] = '\0';
3050 XtFree(*string);
3051 *string = newString;
3056 ** Cache user menus:
3057 ** allocate an empty user (shell, macro) menu cache structure
3059 UserMenuCache *CreateUserMenuCache(void)
3061 /* allocate some memory for the new data structure */
3062 UserMenuCache *cache = (UserMenuCache *)XtMalloc(sizeof(UserMenuCache));
3064 cache->umcLanguageMode = -2;
3065 cache->umcShellMenuCreated = False;
3066 cache->umcMacroMenuCreated = False;
3067 cache->umcShellMenuList.umlNbrItems = 0;
3068 cache->umcShellMenuList.umlItems = NULL;
3069 cache->umcMacroMenuList.umlNbrItems = 0;
3070 cache->umcMacroMenuList.umlItems = NULL;
3072 return cache;
3075 void FreeUserMenuCache(UserMenuCache *cache)
3077 freeUserMenuList(&cache->umcShellMenuList);
3078 freeUserMenuList(&cache->umcMacroMenuList);
3080 XtFree((char *)cache);
3084 ** Cache user menus:
3085 ** init. a user background menu cache structure
3087 void InitUserBGMenuCache(UserBGMenuCache *cache)
3089 cache->ubmcLanguageMode = -2;
3090 cache->ubmcMenuCreated = False;
3091 cache->ubmcMenuList.umlNbrItems = 0;
3092 cache->ubmcMenuList.umlItems = NULL;
3095 void FreeUserBGMenuCache(UserBGMenuCache *cache)
3097 freeUserMenuList(&cache->ubmcMenuList);
3101 ** Cache user menus:
3102 ** Parse given menu item list and setup a user menu info list for
3103 ** management of user menu.
3105 static void parseMenuItemList(menuItemRec **itemList, int nbrOfItems,
3106 userMenuInfo **infoList, userSubMenuCache *subMenus)
3108 int i;
3109 userMenuInfo *info;
3111 /* Allocate storage for structures to keep track of sub-menus */
3112 allocSubMenuCache(subMenus, nbrOfItems);
3114 /* 1st pass: setup user menu info: extract language modes, menu name &
3115 default indication; build user menu ID */
3116 for (i=0; i<nbrOfItems; i++) {
3117 infoList[i] = parseMenuItemRec(itemList[i]);
3118 generateUserMenuId(infoList[i], subMenus);
3121 /* 2nd pass: solve "default" dependencies */
3122 for (i=0; i<nbrOfItems; i++) {
3123 info = infoList[i];
3125 /* If the user menu item is a default one, then scan the list for
3126 items with the same name and a language mode specified.
3127 If one is found, then set the default index to the index of the
3128 current default item. */
3129 if (info->umiIsDefault) {
3130 setDefaultIndex(infoList, nbrOfItems, i);
3136 ** Returns the sub-menu depth (i.e. nesting level) of given
3137 ** menu name.
3139 static int getSubMenuDepth(const char *menuName)
3141 const char *subSep;
3142 int depth = 0;
3144 /* determine sub-menu depth by counting '>' of given "menuName" */
3145 subSep = menuName;
3146 while ((subSep = strchr(subSep, '>')) != NULL ) {
3147 depth ++;
3148 subSep ++;
3151 return depth;
3155 ** Cache user menus:
3156 ** Parse a singe menu item. Allocate & setup a user menu info element
3157 ** holding extracted info.
3159 static userMenuInfo *parseMenuItemRec(menuItemRec *item)
3161 userMenuInfo *newInfo;
3162 int subMenuDepth;
3163 int idSize;
3165 /* allocate a new user menu info element */
3166 newInfo = (userMenuInfo *)XtMalloc(sizeof(userMenuInfo));
3168 /* determine sub-menu depth and allocate some memory
3169 for hierarchical ID; init. ID with {0,.., 0} */
3170 newInfo->umiName = stripLanguageMode(item->name);
3172 subMenuDepth = getSubMenuDepth(newInfo->umiName);
3173 idSize = sizeof(int)*(subMenuDepth+1);
3175 newInfo->umiId = (int *)XtMalloc(idSize);
3176 memset(newInfo->umiId,0,idSize);
3178 /* init. remaining parts of user menu info element */
3179 newInfo->umiIdLen = 0;
3180 newInfo->umiIsDefault = False;
3181 newInfo->umiNbrOfLanguageModes = 0;
3182 newInfo->umiLanguageMode = NULL;
3183 newInfo->umiDefaultIndex = -1;
3184 newInfo->umiToBeManaged = False;
3186 /* assign language mode info to new user menu info element */
3187 parseMenuItemName(item->name, newInfo);
3189 return newInfo;
3193 ** Cache user menus:
3194 ** Extract language mode related info out of given menu item name string.
3195 ** Store this info in given user menu info structure.
3197 static void parseMenuItemName(char *menuItemName, userMenuInfo *info)
3199 char *atPtr, *firstAtPtr, *endPtr;
3200 char c;
3201 int languageMode;
3202 int langModes[MAX_LANGUAGE_MODES];
3203 int nbrLM = 0;
3204 int size;
3206 atPtr = firstAtPtr = strchr(menuItemName, '@');
3207 if (atPtr != NULL) {
3208 if (!strcmp(atPtr+1, "*")) {
3209 /* only language is "*": this is for all but language specific
3210 macros */
3211 info->umiIsDefault = True;
3212 return;
3215 /* setup a list of all language modes related to given menu item */
3216 while (atPtr != NULL) {
3217 /* extract language mode name after "@" sign */
3218 for(endPtr=atPtr+1; isalnum((unsigned char)*endPtr) || *endPtr=='_' ||
3219 *endPtr=='-' || *endPtr==' ' || *endPtr=='+' || *endPtr=='$' ||
3220 *endPtr=='#'; endPtr++);
3222 /* lookup corresponding language mode index; if PLAIN is
3223 returned then this means, that language mode name after
3224 "@" is unknown (i.e. not defined) */
3225 c = *endPtr;
3226 *endPtr = '\0';
3227 languageMode = FindLanguageMode(atPtr+1);
3228 if (languageMode == PLAIN_LANGUAGE_MODE) {
3229 langModes[nbrLM] = UNKNOWN_LANGUAGE_MODE;
3230 } else {
3231 langModes[nbrLM] = languageMode;
3233 nbrLM ++;
3234 *endPtr = c;
3236 /* look for next "@" */
3237 atPtr = strchr(endPtr, '@');
3240 if (nbrLM != 0) {
3241 info->umiNbrOfLanguageModes = nbrLM;
3242 size = sizeof(int)*nbrLM;
3243 info->umiLanguageMode = (int *)XtMalloc(size);
3244 memcpy(info->umiLanguageMode, langModes, size);
3250 ** Cache user menus:
3251 ** generates an ID (= array of integers) of given user menu info, which
3252 ** allows to find the user menu item within the menu tree later on: 1st
3253 ** integer of ID indicates position within main menu; 2nd integer indicates
3254 ** position within 1st sub-menu etc.
3256 static void generateUserMenuId(userMenuInfo *info, userSubMenuCache *subMenus)
3258 int idSize;
3259 char *hierName, *subSep;
3260 int subMenuDepth = 0;
3261 int *menuIdx = &subMenus->usmcNbrOfMainMenuItems;
3262 userSubMenuInfo *curSubMenu;
3264 /* find sub-menus, stripping off '>' until item name is
3265 reached */
3266 subSep = info->umiName;
3267 while ((subSep = strchr(subSep, '>')) != NULL) {
3268 hierName = copySubstring(info->umiName, subSep - info->umiName);
3269 curSubMenu = findSubMenuInfo(subMenus, hierName);
3270 if (curSubMenu == NULL) {
3271 /* sub-menu info not stored before: new sub-menu;
3272 remember its hierarchical position */
3273 info->umiId[subMenuDepth] = *menuIdx;
3274 (*menuIdx) ++;
3276 /* store sub-menu info in list of subMenus; allocate
3277 some memory for hierarchical ID of sub-menu & take over
3278 current hierarchical ID of current user menu info */
3279 curSubMenu = &subMenus->usmcInfo[subMenus->usmcNbrOfSubMenus];
3280 subMenus->usmcNbrOfSubMenus ++;
3281 curSubMenu->usmiName = hierName;
3282 idSize = sizeof(int)*(subMenuDepth+2);
3283 curSubMenu->usmiId = (int *)XtMalloc(idSize);
3284 memcpy(curSubMenu->usmiId, info->umiId, idSize);
3285 curSubMenu->usmiIdLen = subMenuDepth+1;
3286 } else {
3287 /* sub-menu info already stored before: takeover its
3288 hierarchical position */
3289 XtFree(hierName);
3290 info->umiId[subMenuDepth] = curSubMenu->usmiId[subMenuDepth];
3293 subMenuDepth ++;
3294 menuIdx = &curSubMenu->usmiId[subMenuDepth];
3296 subSep ++;
3299 /* remember position of menu item within final (sub) menu */
3300 info->umiId[subMenuDepth] = *menuIdx;
3301 info->umiIdLen = subMenuDepth + 1;
3302 (*menuIdx) ++;
3306 ** Cache user menus:
3307 ** Find info corresponding to a hierarchical menu name (a>b>c...)
3309 static userSubMenuInfo *findSubMenuInfo(userSubMenuCache *subMenus,
3310 const char *hierName)
3312 int i;
3314 for (i=0; i<subMenus->usmcNbrOfSubMenus; i++)
3315 if (!strcmp(hierName, subMenus->usmcInfo[i].usmiName))
3316 return &subMenus->usmcInfo[i];
3317 return NULL;
3321 ** Cache user menus:
3322 ** Returns an allocated copy of menuItemName stripped of language mode
3323 ** parts (i.e. parts starting with "@").
3325 static char *stripLanguageMode(const char *menuItemName)
3327 char *firstAtPtr;
3329 firstAtPtr = strchr(menuItemName, '@');
3330 if (firstAtPtr == NULL)
3331 return XtNewString(menuItemName);
3332 else
3333 return copySubstring(menuItemName, firstAtPtr-menuItemName);
3336 static void setDefaultIndex(userMenuInfo **infoList, int nbrOfItems,
3337 int defaultIdx)
3339 char *defaultMenuName = infoList[defaultIdx]->umiName;
3340 int i;
3341 userMenuInfo *info;
3343 /* Scan the list for items with the same name and a language mode
3344 specified. If one is found, then set the default index to the
3345 index of the current default item. */
3346 for (i=0; i<nbrOfItems; i++) {
3347 info = infoList[i];
3349 if (!info->umiIsDefault && strcmp(info->umiName, defaultMenuName)==0) {
3350 info->umiDefaultIndex = defaultIdx;
3356 ** Determine the info list menu items, which need to be managed
3357 ** for given language mode. Set / reset "to be managed" indication
3358 ** of info list items accordingly.
3360 static void applyLangModeToUserMenuInfo(userMenuInfo **infoList, int nbrOfItems,
3361 int languageMode)
3363 int i;
3364 userMenuInfo *info;
3366 /* 1st pass: mark all items as "to be managed", which are applicable
3367 for all language modes or which are indicated as "default" items */
3368 for (i=0; i<nbrOfItems; i++) {
3369 info = infoList[i];
3371 info->umiToBeManaged =
3372 (info->umiNbrOfLanguageModes == 0 || info->umiIsDefault);
3375 /* 2nd pass: mark language mode specific items matching given language
3376 mode as "to be managed". Reset "to be managed" indications of
3377 "default" items, if applicable */
3378 for (i=0; i<nbrOfItems; i++) {
3379 info = infoList[i];
3381 if (info->umiNbrOfLanguageModes != 0) {
3382 if (doesLanguageModeMatch(info, languageMode)) {
3383 info->umiToBeManaged = True;
3385 if (info->umiDefaultIndex != -1)
3386 infoList[info->umiDefaultIndex]->umiToBeManaged = False;
3393 ** Returns true, if given user menu info is applicable for given language mode
3395 static int doesLanguageModeMatch(userMenuInfo *info, int languageMode)
3397 int i;
3399 for (i=0; i<info->umiNbrOfLanguageModes; i++) {
3400 if (info->umiLanguageMode[i] == languageMode)
3401 return True;
3404 return False;
3407 static void freeUserMenuInfoList(userMenuInfo **infoList, int nbrOfItems)
3409 int i;
3411 for (i=0; i<nbrOfItems; i++) {
3412 freeUserMenuInfo(infoList[i]);
3416 static void freeUserMenuInfo(userMenuInfo *info)
3418 XtFree(info->umiName);
3420 XtFree((char *)info->umiId);
3422 if (info->umiNbrOfLanguageModes != 0)
3423 XtFree((char *)info->umiLanguageMode);
3425 XtFree((char *)info);
3429 ** Cache user menus:
3430 ** Allocate & init. storage for structures to manage sub-menus
3432 static void allocSubMenuCache(userSubMenuCache *subMenus, int nbrOfItems)
3434 int size = sizeof(userSubMenuInfo) * nbrOfItems;
3436 subMenus->usmcNbrOfMainMenuItems = 0;
3437 subMenus->usmcNbrOfSubMenus = 0;
3438 subMenus->usmcInfo = (userSubMenuInfo *)XtMalloc(size);
3441 static void freeSubMenuCache(userSubMenuCache *subMenus)
3443 int i;
3445 for (i=0; i<subMenus->usmcNbrOfSubMenus; i++) {
3446 XtFree(subMenus->usmcInfo[i].usmiName);
3447 XtFree((char *)subMenus->usmcInfo[i].usmiId);
3450 XtFree((char *)subMenus->usmcInfo);
3453 static void allocUserMenuList(UserMenuList *list, int nbrOfItems)
3455 int size = sizeof(UserMenuListElement *) * nbrOfItems;
3457 list->umlNbrItems = 0;
3458 list->umlItems = (UserMenuListElement **)XtMalloc(size);
3461 static void freeUserMenuList(UserMenuList *list)
3463 int i;
3465 for (i=0; i<list->umlNbrItems; i++)
3466 freeUserMenuListElement(list->umlItems[i]);
3468 list->umlNbrItems = 0;
3470 if (list->umlItems != NULL) {
3471 XtFree((char *)list->umlItems);
3472 list->umlItems = NULL;
3476 static UserMenuListElement *allocUserMenuListElement(Widget menuItem, char *accKeys)
3478 UserMenuListElement *element;
3480 element = (UserMenuListElement *)XtMalloc(sizeof(UserMenuListElement));
3482 element->umleManageMode = UMMM_UNMANAGE;
3483 element->umlePrevManageMode = UMMM_UNMANAGE;
3484 element->umleAccKeys = accKeys;
3485 element->umleAccLockPatchApplied = False;
3486 element->umleMenuItem = menuItem;
3487 element->umleSubMenuPane = NULL;
3488 element->umleSubMenuList = NULL;
3490 return element;
3493 static void freeUserMenuListElement(UserMenuListElement *element)
3495 if (element->umleSubMenuList != NULL)
3496 freeUserSubMenuList(element->umleSubMenuList);
3498 if (element->umleAccKeys != NULL)
3499 XtFree(element->umleAccKeys);
3501 XtFree((char *)element);
3504 static UserMenuList *allocUserSubMenuList(int nbrOfItems)
3506 UserMenuList *list;
3508 list = (UserMenuList *)XtMalloc(sizeof(UserMenuList));
3510 allocUserMenuList(list, nbrOfItems);
3512 return list;
3515 static void freeUserSubMenuList(UserMenuList *list)
3517 freeUserMenuList(list);
3519 XtFree((char *)list);