UNIXworkcode

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