1 static const char CVSID
[] = "$Id: userCmds.c,v 1.32 2003/05/02 19:19:02 edg Exp $";
2 /*******************************************************************************
4 * userCmds.c -- Nirvana Editor shell and macro command dialogs *
6 * Copyright (C) 1999 Mark Edel *
8 * This is free software; you can redistribute it and/or modify it under the *
9 * terms of the GNU General Public License as published by the Free Software *
10 * Foundation; either version 2 of the License, or (at your option) any later *
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 *
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 *
22 * Nirvana Text Editor *
25 * Written by Mark Edel *
27 *******************************************************************************/
30 #include "../config.h"
37 #include "preferences.h"
43 #include "interpret.h"
45 #include "../util/DialogF.h"
46 #include "../util/misc.h"
47 #include "../util/managedList.h"
54 #include "../util/VMSparam.h"
57 #include <sys/param.h>
62 #include <X11/keysym.h>
66 #include <Xm/LabelG.h>
68 #include <Xm/ToggleB.h>
69 #include <Xm/SelectioB.h>
70 #include <Xm/RowColumn.h>
71 #include <Xm/CascadeB.h>
78 /* max number of user programmable menu commands allowed per each of the
79 macro, shell, and bacground menus */
80 #define MAX_ITEMS_PER_MENU 400
82 /* major divisions (in position units) in User Commands dialogs */
83 #define LEFT_MARGIN_POS 1
84 #define RIGHT_MARGIN_POS 99
86 #define SHELL_CMD_TOP 70
87 #define MACRO_CMD_TOP 40
89 /* types of current dialog and/or menu */
90 enum dialogTypes
{SHELL_CMDS
, MACRO_CMDS
, BG_MENU_CMDS
};
92 /* Structure representing a menu item for both the shell and macro menus */
95 unsigned int modifiers
;
106 /* Structure for widgets and flags associated with both shell command
107 and macro command editing dialogs */
111 Widget nameTextW
, accTextW
, mneTextW
, cmdTextW
, saveFirstBtn
;
112 Widget loadAfterBtn
, selInpBtn
, winInpBtn
, eitherInpBtn
, noInpBtn
;
113 Widget repInpBtn
, sameOutBtn
, dlogOutBtn
, winOutBtn
, dlogShell
;
115 menuItemRec
**menuItemsList
;
119 /* Structure for keeping track of hierarchical sub-menus durring user-menu
126 /* Descriptions of the current user programmed menu items for re-generating
127 menus and processing shell, macro, and background menu selections */
128 static menuItemRec
*ShellMenuItems
[MAX_ITEMS_PER_MENU
];
129 static int NShellMenuItems
= 0;
130 static menuItemRec
*MacroMenuItems
[MAX_ITEMS_PER_MENU
];
131 static int NMacroMenuItems
= 0;
132 static menuItemRec
*BGMenuItems
[MAX_ITEMS_PER_MENU
];
133 static int NBGMenuItems
= 0;
135 /* Top level shells of the user-defined menu editing dialogs */
136 static Widget ShellCmdDialog
= NULL
;
137 static Widget MacroCmdDialog
= NULL
;
138 static Widget BGMenuCmdDialog
= NULL
;
140 /* Paste learn/replay sequence buttons in user defined menu editing dialogs
141 (for dimming and undimming externally when replay macro is available) */
142 static Widget MacroPasteReplayBtn
= NULL
;
143 static Widget BGMenuPasteReplayBtn
= NULL
;
145 static void editMacroOrBGMenu(WindowInfo
*window
, int dialogType
);
146 static void dimSelDepItemsInMenu(Widget menuPane
, menuItemRec
**menuList
,
147 int nMenuItems
, int sensitive
);
148 static void updateMenus(int menuType
);
149 static Widget
findInMenuTree(menuTreeItem
*menuTree
, int nTreeEntries
,
150 const char *hierName
);
151 static char *copySubstring(const char *string
, int length
);
152 static char *findStripLanguageMode(const char *menuItemName
, int languageMode
,
154 static Widget
createUserMenuItem(Widget menuPane
, char *name
, menuItemRec
*f
,
155 int index
, XtCallbackProc cbRtn
, XtPointer cbArg
);
156 static Widget
createUserSubMenu(Widget parent
, char *label
);
157 static void removeMenuItems(Widget menuPane
);
158 static void updateMenu(WindowInfo
*window
, int menuType
);
159 static void okCB(Widget w
, XtPointer clientData
, XtPointer callData
);
160 static void applyCB(Widget w
, XtPointer clientData
, XtPointer callData
);
161 static void checkCB(Widget w
, XtPointer clientData
, XtPointer callData
);
162 static int checkMacro(userCmdDialog
*ucd
);
163 static int checkMacroText(char *macro
, Widget errorParent
, Widget errFocus
);
164 static int applyDialogChanges(userCmdDialog
*ucd
);
165 static void dismissCB(Widget w
, XtPointer clientData
, XtPointer callData
);
166 static void pasteReplayCB(Widget w
, XtPointer clientData
, XtPointer callData
);
167 static void destroyCB(Widget w
, XtPointer clientData
, XtPointer callData
);
168 static void accKeyCB(Widget w
, XtPointer clientData
, XKeyEvent
*event
);
169 static void sameOutCB(Widget w
, XtPointer clientData
, XtPointer callData
);
170 static void shellMenuCB(Widget w
, WindowInfo
*window
, XtPointer callData
);
171 static void macroMenuCB(Widget w
, WindowInfo
*window
, XtPointer callData
);
172 static void bgMenuCB(Widget w
, WindowInfo
*window
, XtPointer callData
) ;
173 static void accFocusCB(Widget w
, XtPointer clientData
, XtPointer callData
);
174 static void accLoseFocusCB(Widget w
, XtPointer clientData
,
176 static void updateDialogFields(menuItemRec
*f
, userCmdDialog
*ucd
);
177 static menuItemRec
*readDialogFields(userCmdDialog
*ucd
, int silent
);
178 static menuItemRec
*copyMenuItemRec(menuItemRec
*item
);
179 static void freeMenuItemRec(menuItemRec
*item
);
180 static void *getDialogDataCB(void *oldItem
, int explicitRequest
, int *abort
,
182 static void setDialogDataCB(void *item
, void *cbArg
);
183 static void freeItemCB(void *item
);
184 static int dialogFieldsAreEmpty(userCmdDialog
*ucd
);
185 static void disableTextW(Widget textW
);
186 static char *writeMenuItemString(menuItemRec
**menuItems
, int nItems
,
188 static int loadMenuItemString(char *inString
, menuItemRec
**menuItems
,
189 int *nItems
, int listType
);
190 static void generateAcceleratorString(char *text
, unsigned int modifiers
,
192 static void genAccelEventName(char *text
, unsigned int modifiers
,
194 static int parseAcceleratorString(const char *string
, unsigned int *modifiers
,
196 static int parseError(const char *message
);
197 static char *copyMacroToEnd(char **inPtr
);
198 static void addTerminatingNewline(char **string
);
201 ** Present a dialog for editing the user specified commands in the shell menu
203 void EditShellMenu(WindowInfo
*window
)
205 Widget form
, accLabel
, inpLabel
, inpBox
, outBox
, outLabel
;
206 Widget nameLabel
, cmdLabel
, okBtn
, applyBtn
, dismissBtn
;
212 /* if the dialog is already displayed, just pop it to the top and return */
213 if (ShellCmdDialog
!= NULL
) {
214 RaiseShellWindow(ShellCmdDialog
);
218 /* Create a structure for keeping track of dialog state */
219 ucd
= (userCmdDialog
*)XtMalloc(sizeof(userCmdDialog
));
220 ucd
->window
= window
;
222 /* Set the dialog to operate on the Shell menu */
223 ucd
->menuItemsList
= (menuItemRec
**)XtMalloc(sizeof(menuItemRec
*) *
225 for (i
=0; i
<NShellMenuItems
; i
++)
226 ucd
->menuItemsList
[i
] = copyMenuItemRec(ShellMenuItems
[i
]);
227 ucd
->nMenuItems
= NShellMenuItems
;
228 ucd
->dialogType
= SHELL_CMDS
;
231 XtSetArg(args
[ac
], XmNdeleteResponse
, XmDO_NOTHING
); ac
++;
232 XtSetArg(args
[ac
], XmNiconName
, "NEdit Shell Menu"); ac
++;
233 XtSetArg(args
[ac
], XmNtitle
, "Shell Menu"); ac
++;
234 ucd
->dlogShell
= CreateShellWithBestVis(APP_NAME
, APP_CLASS
,
235 applicationShellWidgetClass
, TheDisplay
, args
, ac
);
236 AddSmallIcon(ucd
->dlogShell
);
237 form
= XtVaCreateManagedWidget("editShellCommands", xmFormWidgetClass
,
238 ucd
->dlogShell
, XmNautoUnmanage
, False
,
239 XmNresizePolicy
, XmRESIZE_NONE
, NULL
);
240 ShellCmdDialog
= ucd
->dlogShell
;
241 XtAddCallback(form
, XmNdestroyCallback
, destroyCB
, ucd
);
242 AddMotifCloseCallback(ucd
->dlogShell
, dismissCB
, ucd
);
245 XtSetArg(args
[ac
], XmNtopAttachment
, XmATTACH_POSITION
); ac
++;
246 XtSetArg(args
[ac
], XmNtopPosition
, 2); ac
++;
247 XtSetArg(args
[ac
], XmNleftAttachment
, XmATTACH_POSITION
); ac
++;
248 XtSetArg(args
[ac
], XmNleftPosition
, LEFT_MARGIN_POS
); ac
++;
249 XtSetArg(args
[ac
], XmNrightAttachment
, XmATTACH_POSITION
); ac
++;
250 XtSetArg(args
[ac
], XmNrightPosition
, LIST_RIGHT
-1); ac
++;
251 XtSetArg(args
[ac
], XmNbottomAttachment
, XmATTACH_POSITION
); ac
++;
252 XtSetArg(args
[ac
], XmNbottomPosition
, SHELL_CMD_TOP
); ac
++;
253 ucd
->managedList
= CreateManagedList(form
, "list", args
, ac
,
254 (void **)ucd
->menuItemsList
, &ucd
->nMenuItems
, MAX_ITEMS_PER_MENU
,
255 20, getDialogDataCB
, ucd
, setDialogDataCB
, ucd
, freeItemCB
);
257 ucd
->loadAfterBtn
= XtVaCreateManagedWidget("loadAfterBtn",
258 xmToggleButtonWidgetClass
, form
,
259 XmNlabelString
, s1
=MKSTRING("Re-load file after executing command"),
261 XmNalignment
, XmALIGNMENT_BEGINNING
,
263 XmNleftAttachment
, XmATTACH_POSITION
,
264 XmNleftPosition
, LIST_RIGHT
,
265 XmNrightAttachment
, XmATTACH_POSITION
,
266 XmNrightPosition
, RIGHT_MARGIN_POS
,
267 XmNbottomAttachment
, XmATTACH_POSITION
,
268 XmNbottomPosition
, SHELL_CMD_TOP
, NULL
);
270 ucd
->saveFirstBtn
= XtVaCreateManagedWidget("saveFirstBtn",
271 xmToggleButtonWidgetClass
, form
,
272 XmNlabelString
, s1
=MKSTRING("Save file before executing command"),
274 XmNalignment
, XmALIGNMENT_BEGINNING
,
276 XmNleftAttachment
, XmATTACH_POSITION
,
277 XmNleftPosition
, LIST_RIGHT
,
278 XmNrightAttachment
, XmATTACH_POSITION
,
279 XmNrightPosition
, RIGHT_MARGIN_POS
,
280 XmNbottomAttachment
, XmATTACH_WIDGET
,
281 XmNbottomWidget
, ucd
->loadAfterBtn
, NULL
);
283 ucd
->repInpBtn
= XtVaCreateManagedWidget("repInpBtn",
284 xmToggleButtonWidgetClass
, form
,
285 XmNlabelString
, s1
=MKSTRING("Output replaces input"),
287 XmNalignment
, XmALIGNMENT_BEGINNING
,
289 XmNleftAttachment
, XmATTACH_POSITION
,
290 XmNleftPosition
, LIST_RIGHT
,
291 XmNrightAttachment
, XmATTACH_POSITION
,
292 XmNrightPosition
, RIGHT_MARGIN_POS
,
293 XmNbottomAttachment
, XmATTACH_WIDGET
,
294 XmNbottomWidget
, ucd
->saveFirstBtn
, NULL
);
296 outBox
= XtVaCreateManagedWidget("outBox", xmRowColumnWidgetClass
, form
,
297 XmNpacking
, XmPACK_TIGHT
,
298 XmNorientation
, XmHORIZONTAL
,
299 XmNradioBehavior
, True
,
300 XmNradioAlwaysOne
, True
,
301 XmNleftAttachment
, XmATTACH_POSITION
,
302 XmNleftPosition
, LIST_RIGHT
+ 2,
303 XmNrightAttachment
, XmATTACH_POSITION
,
304 XmNrightPosition
, RIGHT_MARGIN_POS
,
305 XmNbottomAttachment
, XmATTACH_WIDGET
,
306 XmNbottomWidget
, ucd
->repInpBtn
,
307 XmNbottomOffset
, 4, NULL
);
308 ucd
->sameOutBtn
= XtVaCreateManagedWidget("sameOutBtn",
309 xmToggleButtonWidgetClass
, outBox
,
310 XmNlabelString
, s1
=MKSTRING("same window"),
312 XmNalignment
, XmALIGNMENT_BEGINNING
,
316 XtAddCallback(ucd
->sameOutBtn
, XmNvalueChangedCallback
, sameOutCB
, ucd
);
317 ucd
->dlogOutBtn
= XtVaCreateManagedWidget("dlogOutBtn",
318 xmToggleButtonWidgetClass
, outBox
,
319 XmNlabelString
, s1
=MKSTRING("dialog"),
321 XmNalignment
, XmALIGNMENT_BEGINNING
,
323 XmNset
, False
, NULL
);
325 ucd
->winOutBtn
= XtVaCreateManagedWidget("winOutBtn", xmToggleButtonWidgetClass
,
327 XmNlabelString
, s1
=MKSTRING("new window"),
329 XmNalignment
, XmALIGNMENT_BEGINNING
,
331 XmNset
, False
, NULL
);
333 outLabel
= XtVaCreateManagedWidget("outLabel", xmLabelGadgetClass
, form
,
334 XmNlabelString
, s1
=MKSTRING("Command Output:"),
335 XmNalignment
, XmALIGNMENT_BEGINNING
,
337 XmNleftAttachment
, XmATTACH_POSITION
,
338 XmNleftPosition
, LIST_RIGHT
,
339 XmNrightAttachment
, XmATTACH_POSITION
,
340 XmNrightPosition
, RIGHT_MARGIN_POS
,
341 XmNbottomAttachment
, XmATTACH_WIDGET
,
342 XmNbottomWidget
, outBox
, NULL
);
345 inpBox
= XtVaCreateManagedWidget("inpBox", xmRowColumnWidgetClass
, form
,
346 XmNpacking
, XmPACK_TIGHT
,
347 XmNorientation
, XmHORIZONTAL
,
348 XmNradioBehavior
, True
,
349 XmNradioAlwaysOne
, True
,
350 XmNleftAttachment
, XmATTACH_POSITION
,
351 XmNleftPosition
, LIST_RIGHT
+ 2,
352 XmNrightAttachment
, XmATTACH_POSITION
,
353 XmNrightPosition
, RIGHT_MARGIN_POS
,
354 XmNbottomAttachment
, XmATTACH_WIDGET
,
355 XmNbottomWidget
, outLabel
, NULL
);
356 ucd
->selInpBtn
= XtVaCreateManagedWidget("selInpBtn", xmToggleButtonWidgetClass
,
358 XmNlabelString
, s1
=MKSTRING("selection"),
360 XmNalignment
, XmALIGNMENT_BEGINNING
,
364 ucd
->winInpBtn
= XtVaCreateManagedWidget("winInpBtn",
365 xmToggleButtonWidgetClass
, inpBox
,
366 XmNlabelString
, s1
=MKSTRING("window"),
368 XmNalignment
, XmALIGNMENT_BEGINNING
,
370 XmNset
, False
, NULL
);
372 ucd
->eitherInpBtn
= XtVaCreateManagedWidget("eitherInpBtn",
373 xmToggleButtonWidgetClass
, inpBox
,
374 XmNlabelString
, s1
=MKSTRING("either"),
376 XmNalignment
, XmALIGNMENT_BEGINNING
,
378 XmNset
, False
, NULL
);
380 ucd
->noInpBtn
= XtVaCreateManagedWidget("noInpBtn",
381 xmToggleButtonWidgetClass
, inpBox
,
382 XmNlabelString
, s1
=MKSTRING("none"),
384 XmNalignment
, XmALIGNMENT_BEGINNING
,
386 XmNset
, False
, NULL
);
388 inpLabel
= XtVaCreateManagedWidget("inpLabel", xmLabelGadgetClass
, form
,
389 XmNlabelString
, s1
=MKSTRING("Command Input:"),
390 XmNalignment
, XmALIGNMENT_BEGINNING
,
392 XmNleftAttachment
, XmATTACH_POSITION
,
393 XmNleftPosition
, LIST_RIGHT
,
394 XmNrightAttachment
, XmATTACH_POSITION
,
395 XmNrightPosition
, RIGHT_MARGIN_POS
,
396 XmNbottomAttachment
, XmATTACH_WIDGET
,
397 XmNbottomWidget
, inpBox
, NULL
);
400 ucd
->mneTextW
= XtVaCreateManagedWidget("mne", xmTextWidgetClass
, form
,
403 XmNleftAttachment
, XmATTACH_POSITION
,
404 XmNleftPosition
, RIGHT_MARGIN_POS
-10,
405 XmNrightAttachment
, XmATTACH_POSITION
,
406 XmNrightPosition
, RIGHT_MARGIN_POS
,
407 XmNbottomAttachment
, XmATTACH_WIDGET
,
408 XmNbottomWidget
, inpLabel
, NULL
);
409 RemapDeleteKey(ucd
->mneTextW
);
411 ucd
->accTextW
= XtVaCreateManagedWidget("acc", xmTextWidgetClass
, form
,
413 XmNmaxLength
, MAX_ACCEL_LEN
-1,
414 XmNcursorPositionVisible
, False
,
415 XmNleftAttachment
, XmATTACH_POSITION
,
416 XmNleftPosition
, LIST_RIGHT
,
417 XmNrightAttachment
, XmATTACH_POSITION
,
418 XmNrightPosition
, RIGHT_MARGIN_POS
-15,
419 XmNbottomAttachment
, XmATTACH_WIDGET
,
420 XmNbottomWidget
, inpLabel
, NULL
);
421 XtAddEventHandler(ucd
->accTextW
, KeyPressMask
, False
,
422 (XtEventHandler
)accKeyCB
, ucd
);
423 XtAddCallback(ucd
->accTextW
, XmNfocusCallback
, accFocusCB
, ucd
);
424 XtAddCallback(ucd
->accTextW
, XmNlosingFocusCallback
, accLoseFocusCB
, ucd
);
425 accLabel
= XtVaCreateManagedWidget("accLabel", xmLabelGadgetClass
, form
,
426 XmNlabelString
, s1
=MKSTRING("Accelerator"),
428 XmNuserData
, ucd
->accTextW
,
429 XmNalignment
, XmALIGNMENT_BEGINNING
,
431 XmNleftAttachment
, XmATTACH_POSITION
,
432 XmNleftPosition
, LIST_RIGHT
,
433 XmNrightAttachment
, XmATTACH_POSITION
,
434 XmNrightPosition
, LIST_RIGHT
+ 24,
435 XmNbottomAttachment
, XmATTACH_WIDGET
,
436 XmNbottomWidget
, ucd
->mneTextW
, NULL
);
439 XtVaCreateManagedWidget("mneLabel", xmLabelGadgetClass
, form
,
440 XmNlabelString
, s1
=MKSTRING("Mnemonic"),
442 XmNuserData
, ucd
->mneTextW
,
443 XmNalignment
, XmALIGNMENT_END
,
445 XmNleftAttachment
, XmATTACH_POSITION
,
446 XmNleftPosition
, LIST_RIGHT
+ 24,
447 XmNrightAttachment
, XmATTACH_POSITION
,
448 XmNrightPosition
, RIGHT_MARGIN_POS
,
449 XmNbottomAttachment
, XmATTACH_WIDGET
,
450 XmNbottomWidget
, ucd
->mneTextW
, NULL
);
453 ucd
->nameTextW
= XtVaCreateManagedWidget("name", xmTextWidgetClass
, form
,
454 XmNleftAttachment
, XmATTACH_POSITION
,
455 XmNleftPosition
, LIST_RIGHT
,
456 XmNrightAttachment
, XmATTACH_POSITION
,
457 XmNrightPosition
, RIGHT_MARGIN_POS
,
458 XmNbottomAttachment
, XmATTACH_WIDGET
,
459 XmNbottomWidget
, accLabel
, NULL
);
460 RemapDeleteKey(ucd
->nameTextW
);
462 nameLabel
= XtVaCreateManagedWidget("nameLabel", xmLabelGadgetClass
, form
,
463 XmNlabelString
, s1
=MKSTRING("Menu Entry"),
465 XmNuserData
, ucd
->nameTextW
,
466 XmNalignment
, XmALIGNMENT_BEGINNING
,
468 XmNleftAttachment
, XmATTACH_POSITION
,
469 XmNleftPosition
, LIST_RIGHT
,
470 XmNbottomAttachment
, XmATTACH_WIDGET
,
471 XmNbottomWidget
, ucd
->nameTextW
, NULL
);
474 XtVaCreateManagedWidget("nameNotes", xmLabelGadgetClass
, form
,
475 XmNlabelString
, s1
=MKSTRING("(> for sub-menu, @ language mode)"),
476 XmNalignment
, XmALIGNMENT_END
,
478 XmNleftAttachment
, XmATTACH_WIDGET
,
479 XmNleftWidget
, nameLabel
,
480 XmNrightAttachment
, XmATTACH_POSITION
,
481 XmNrightPosition
, RIGHT_MARGIN_POS
,
482 XmNbottomAttachment
, XmATTACH_WIDGET
,
483 XmNbottomWidget
, ucd
->nameTextW
, NULL
);
486 XtVaCreateManagedWidget("topLabel", xmLabelGadgetClass
, form
,
487 XmNlabelString
, s1
=MKSTRING(
488 "Select a shell menu item from the list at left.\n\
489 Select \"New\" to add a new command to the menu."),
490 XmNtopAttachment
, XmATTACH_POSITION
,
492 XmNleftAttachment
, XmATTACH_POSITION
,
493 XmNleftPosition
, LIST_RIGHT
,
494 XmNrightAttachment
, XmATTACH_POSITION
,
495 XmNrightPosition
, RIGHT_MARGIN_POS
,
496 XmNbottomAttachment
, XmATTACH_WIDGET
,
497 XmNbottomWidget
, nameLabel
, NULL
);
500 cmdLabel
= XtVaCreateManagedWidget("cmdLabel", xmLabelGadgetClass
, form
,
501 XmNlabelString
, s1
=MKSTRING("Shell Command to Execute"),
503 XmNalignment
, XmALIGNMENT_BEGINNING
,
505 XmNtopAttachment
, XmATTACH_POSITION
,
506 XmNtopPosition
, SHELL_CMD_TOP
,
507 XmNleftAttachment
, XmATTACH_POSITION
,
508 XmNleftPosition
, LEFT_MARGIN_POS
, NULL
);
510 XtVaCreateManagedWidget("cmdLabel", xmLabelGadgetClass
, form
,
511 XmNlabelString
, s1
=MKSTRING("(% expands to current filename, # to line number)"),
512 XmNalignment
, XmALIGNMENT_END
,
514 XmNtopAttachment
, XmATTACH_POSITION
,
515 XmNtopPosition
, SHELL_CMD_TOP
,
516 XmNleftAttachment
, XmATTACH_WIDGET
,
517 XmNleftWidget
, cmdLabel
,
518 XmNrightAttachment
, XmATTACH_POSITION
,
519 XmNrightPosition
, RIGHT_MARGIN_POS
, NULL
);
522 okBtn
= XtVaCreateManagedWidget("ok",xmPushButtonWidgetClass
,form
,
523 XmNlabelString
, s1
=MKSTRING("OK"),
524 XmNleftAttachment
, XmATTACH_POSITION
,
526 XmNrightAttachment
, XmATTACH_POSITION
,
527 XmNrightPosition
, 29,
528 XmNbottomAttachment
, XmATTACH_POSITION
,
529 XmNbottomPosition
, 99, NULL
);
530 XtAddCallback(okBtn
, XmNactivateCallback
, okCB
, ucd
);
533 applyBtn
= XtVaCreateManagedWidget("apply",xmPushButtonWidgetClass
,form
,
534 XmNlabelString
, s1
=MKSTRING("Apply"),
536 XmNleftAttachment
, XmATTACH_POSITION
,
538 XmNrightAttachment
, XmATTACH_POSITION
,
539 XmNrightPosition
, 58,
540 XmNbottomAttachment
, XmATTACH_POSITION
,
541 XmNbottomPosition
, 99, NULL
);
542 XtAddCallback(applyBtn
, XmNactivateCallback
, applyCB
, ucd
);
545 dismissBtn
= XtVaCreateManagedWidget("dismiss",xmPushButtonWidgetClass
,form
,
546 XmNlabelString
, s1
=MKSTRING("Dismiss"),
547 XmNleftAttachment
, XmATTACH_POSITION
,
549 XmNrightAttachment
, XmATTACH_POSITION
,
550 XmNrightPosition
, 87,
551 XmNbottomAttachment
, XmATTACH_POSITION
,
552 XmNbottomPosition
, 99, NULL
);
553 XtAddCallback(dismissBtn
, XmNactivateCallback
, dismissCB
, ucd
);
557 XtSetArg(args
[ac
], XmNeditMode
, XmMULTI_LINE_EDIT
); ac
++;
558 XtSetArg(args
[ac
], XmNscrollHorizontal
, False
); ac
++;
559 XtSetArg(args
[ac
], XmNwordWrap
, True
); ac
++;
560 XtSetArg(args
[ac
], XmNtopAttachment
, XmATTACH_WIDGET
); ac
++;
561 XtSetArg(args
[ac
], XmNtopWidget
, cmdLabel
); ac
++;
562 XtSetArg(args
[ac
], XmNleftAttachment
, XmATTACH_POSITION
); ac
++;
563 XtSetArg(args
[ac
], XmNleftPosition
, LEFT_MARGIN_POS
); ac
++;
564 XtSetArg(args
[ac
], XmNrightAttachment
, XmATTACH_POSITION
); ac
++;
565 XtSetArg(args
[ac
], XmNrightPosition
, RIGHT_MARGIN_POS
); ac
++;
566 XtSetArg(args
[ac
], XmNbottomAttachment
, XmATTACH_WIDGET
); ac
++;
567 XtSetArg(args
[ac
], XmNbottomWidget
, okBtn
); ac
++;
568 XtSetArg(args
[ac
], XmNbottomOffset
, 5); ac
++;
569 ucd
->cmdTextW
= XmCreateScrolledText(form
, "name", args
, ac
);
570 AddMouseWheelSupport(ucd
->cmdTextW
);
571 XtManageChild(ucd
->cmdTextW
);
572 MakeSingleLineTextW(ucd
->cmdTextW
);
573 RemapDeleteKey(ucd
->cmdTextW
);
574 XtVaSetValues(cmdLabel
, XmNuserData
, ucd
->cmdTextW
, NULL
); /* for mnemonic */
576 /* Disable text input for the accelerator key widget, let the
577 event handler manage it instead */
578 disableTextW(ucd
->accTextW
);
580 /* initializs the dialog fields to match "New" list item */
581 updateDialogFields(NULL
, ucd
);
583 /* Set initial default button */
584 XtVaSetValues(form
, XmNdefaultButton
, okBtn
, NULL
);
585 XtVaSetValues(form
, XmNcancelButton
, dismissBtn
, NULL
);
587 /* Handle mnemonic selection of buttons and focus to dialog */
588 AddDialogMnemonicHandler(form
, FALSE
);
590 /* realize all of the widgets in the new window */
591 RealizeWithoutForcingPosition(ucd
->dlogShell
);
595 ** Present a dialogs for editing the user specified commands in the Macro
596 ** and background menus
598 void EditMacroMenu(WindowInfo
*window
)
600 editMacroOrBGMenu(window
, MACRO_CMDS
);
602 void EditBGMenu(WindowInfo
*window
)
604 editMacroOrBGMenu(window
, BG_MENU_CMDS
);
607 static void editMacroOrBGMenu(WindowInfo
*window
, int dialogType
)
609 Widget form
, accLabel
, pasteReplayBtn
;
610 Widget nameLabel
, cmdLabel
, okBtn
, applyBtn
, dismissBtn
;
617 /* if the dialog is already displayed, just pop it to the top and return */
618 if (dialogType
== MACRO_CMDS
&& MacroCmdDialog
!= NULL
) {
619 RaiseShellWindow(MacroCmdDialog
);
622 if (dialogType
== BG_MENU_CMDS
&& BGMenuCmdDialog
!= NULL
) {
623 RaiseShellWindow(BGMenuCmdDialog
);
627 /* Create a structure for keeping track of dialog state */
628 ucd
= (userCmdDialog
*)XtMalloc(sizeof(userCmdDialog
));
629 ucd
->window
= window
;
631 /* Set the dialog to operate on the Macro menu */
632 ucd
->menuItemsList
= (menuItemRec
**)XtMalloc(sizeof(menuItemRec
**) *
634 if (dialogType
== MACRO_CMDS
) {
635 for (i
=0; i
<NMacroMenuItems
; i
++)
636 ucd
->menuItemsList
[i
] = copyMenuItemRec(MacroMenuItems
[i
]);
637 ucd
->nMenuItems
= NMacroMenuItems
;
638 } else { /* BG_MENU_CMDS */
639 for (i
=0; i
<NBGMenuItems
; i
++)
640 ucd
->menuItemsList
[i
] = copyMenuItemRec(BGMenuItems
[i
]);
641 ucd
->nMenuItems
= NBGMenuItems
;
643 ucd
->dialogType
= dialogType
;
645 title
= dialogType
== MACRO_CMDS
? "Macro Commands" :
646 "Window Background Menu";
648 XtSetArg(args
[ac
], XmNdeleteResponse
, XmDO_NOTHING
); ac
++;
649 XtSetArg(args
[ac
], XmNiconName
, title
); ac
++;
650 XtSetArg(args
[ac
], XmNtitle
, title
); ac
++;
651 ucd
->dlogShell
= CreateShellWithBestVis(APP_NAME
, APP_CLASS
,
652 applicationShellWidgetClass
, TheDisplay
, args
, ac
);
653 AddSmallIcon(ucd
->dlogShell
);
654 form
= XtVaCreateManagedWidget("editMacroCommands", xmFormWidgetClass
,
655 ucd
->dlogShell
, XmNautoUnmanage
, False
,
656 XmNresizePolicy
, XmRESIZE_NONE
, NULL
);
657 XtAddCallback(form
, XmNdestroyCallback
, destroyCB
, ucd
);
658 AddMotifCloseCallback(ucd
->dlogShell
, dismissCB
, ucd
);
661 XtSetArg(args
[ac
], XmNtopAttachment
, XmATTACH_POSITION
); ac
++;
662 XtSetArg(args
[ac
], XmNtopPosition
, 2); ac
++;
663 XtSetArg(args
[ac
], XmNleftAttachment
, XmATTACH_POSITION
); ac
++;
664 XtSetArg(args
[ac
], XmNleftPosition
, LEFT_MARGIN_POS
); ac
++;
665 XtSetArg(args
[ac
], XmNrightAttachment
, XmATTACH_POSITION
); ac
++;
666 XtSetArg(args
[ac
], XmNrightPosition
, LIST_RIGHT
-1); ac
++;
667 XtSetArg(args
[ac
], XmNbottomAttachment
, XmATTACH_POSITION
); ac
++;
668 XtSetArg(args
[ac
], XmNbottomPosition
, MACRO_CMD_TOP
); ac
++;
669 ucd
->managedList
= CreateManagedList(form
, "list", args
, ac
,
670 (void **)ucd
->menuItemsList
, &ucd
->nMenuItems
, MAX_ITEMS_PER_MENU
, 20,
671 getDialogDataCB
, ucd
, setDialogDataCB
, ucd
, freeItemCB
);
673 ucd
->selInpBtn
= XtVaCreateManagedWidget("selInpBtn",
674 xmToggleButtonWidgetClass
, form
,
675 XmNlabelString
, s1
=MKSTRING("Requires Selection"),
677 XmNalignment
, XmALIGNMENT_BEGINNING
,
680 XmNleftAttachment
, XmATTACH_POSITION
,
681 XmNleftPosition
, LIST_RIGHT
,
682 XmNbottomAttachment
, XmATTACH_POSITION
,
683 XmNbottomPosition
, MACRO_CMD_TOP
, NULL
);
686 ucd
->mneTextW
= XtVaCreateManagedWidget("mne", xmTextWidgetClass
, form
,
689 XmNleftAttachment
, XmATTACH_POSITION
,
690 XmNleftPosition
, RIGHT_MARGIN_POS
-21-5,
691 XmNrightAttachment
, XmATTACH_POSITION
,
692 XmNrightPosition
, RIGHT_MARGIN_POS
-21,
693 XmNbottomAttachment
, XmATTACH_WIDGET
,
694 XmNbottomWidget
, ucd
->selInpBtn
,
695 XmNbottomOffset
, 5, NULL
);
696 RemapDeleteKey(ucd
->mneTextW
);
698 ucd
->accTextW
= XtVaCreateManagedWidget("acc", xmTextWidgetClass
, form
,
700 XmNmaxLength
, MAX_ACCEL_LEN
-1,
701 XmNcursorPositionVisible
, False
,
702 XmNleftAttachment
, XmATTACH_POSITION
,
703 XmNleftPosition
, LIST_RIGHT
,
704 XmNrightAttachment
, XmATTACH_POSITION
,
705 XmNrightPosition
, RIGHT_MARGIN_POS
-20-10,
706 XmNbottomAttachment
, XmATTACH_WIDGET
,
707 XmNbottomWidget
, ucd
->selInpBtn
,
708 XmNbottomOffset
, 5, NULL
);
709 XtAddEventHandler(ucd
->accTextW
, KeyPressMask
, False
,
710 (XtEventHandler
)accKeyCB
, ucd
);
711 XtAddCallback(ucd
->accTextW
, XmNfocusCallback
, accFocusCB
, ucd
);
712 XtAddCallback(ucd
->accTextW
, XmNlosingFocusCallback
, accLoseFocusCB
, ucd
);
714 accLabel
= XtVaCreateManagedWidget("accLabel", xmLabelGadgetClass
, form
,
715 XmNlabelString
, s1
=MKSTRING("Accelerator"),
717 XmNuserData
, ucd
->accTextW
,
718 XmNalignment
, XmALIGNMENT_BEGINNING
,
720 XmNleftAttachment
, XmATTACH_POSITION
,
721 XmNleftPosition
, LIST_RIGHT
,
722 XmNrightAttachment
, XmATTACH_POSITION
,
723 XmNrightPosition
, LIST_RIGHT
+ 22,
724 XmNbottomAttachment
, XmATTACH_WIDGET
,
725 XmNbottomWidget
, ucd
->mneTextW
, NULL
);
728 XtVaCreateManagedWidget("mneLabel", xmLabelGadgetClass
, form
,
729 XmNlabelString
, s1
=MKSTRING("Mnemonic"),
731 XmNuserData
, ucd
->mneTextW
,
732 XmNalignment
, XmALIGNMENT_END
,
734 XmNleftAttachment
, XmATTACH_POSITION
,
735 XmNleftPosition
, LIST_RIGHT
+ 22,
736 XmNrightAttachment
, XmATTACH_POSITION
,
737 XmNrightPosition
, RIGHT_MARGIN_POS
-21,
738 XmNbottomAttachment
, XmATTACH_WIDGET
,
739 XmNbottomWidget
, ucd
->mneTextW
, NULL
);
742 pasteReplayBtn
= XtVaCreateManagedWidget("pasteReplay",
743 xmPushButtonWidgetClass
, form
,
744 XmNlabelString
, s1
=MKSTRING("Paste Learn/\nReplay Macro"),
746 XmNsensitive
, GetReplayMacro() != NULL
,
747 XmNleftAttachment
, XmATTACH_POSITION
,
748 XmNleftPosition
, RIGHT_MARGIN_POS
-20,
749 XmNrightAttachment
, XmATTACH_POSITION
,
750 XmNrightPosition
, RIGHT_MARGIN_POS
,
751 XmNbottomAttachment
, XmATTACH_POSITION
,
752 XmNbottomPosition
, MACRO_CMD_TOP
, NULL
);
753 XtAddCallback(pasteReplayBtn
, XmNactivateCallback
,
757 ucd
->nameTextW
= XtVaCreateManagedWidget("name", xmTextWidgetClass
, form
,
758 XmNleftAttachment
, XmATTACH_POSITION
,
759 XmNleftPosition
, LIST_RIGHT
,
760 XmNrightAttachment
, XmATTACH_POSITION
,
761 XmNrightPosition
, RIGHT_MARGIN_POS
,
762 XmNbottomAttachment
, XmATTACH_WIDGET
,
763 XmNbottomWidget
, accLabel
, NULL
);
764 RemapDeleteKey(ucd
->nameTextW
);
766 nameLabel
= XtVaCreateManagedWidget("nameLabel", xmLabelGadgetClass
, form
,
767 XmNlabelString
, s1
=MKSTRING("Menu Entry"),
769 XmNuserData
, ucd
->nameTextW
,
770 XmNalignment
, XmALIGNMENT_BEGINNING
,
772 XmNleftAttachment
, XmATTACH_POSITION
,
773 XmNleftPosition
, LIST_RIGHT
,
774 XmNbottomAttachment
, XmATTACH_WIDGET
,
775 XmNbottomWidget
, ucd
->nameTextW
, NULL
);
778 XtVaCreateManagedWidget("nameNotes", xmLabelGadgetClass
, form
,
779 XmNlabelString
, s1
=MKSTRING("(> for sub-menu, @ language mode)"),
780 XmNalignment
, XmALIGNMENT_END
,
782 XmNleftAttachment
, XmATTACH_WIDGET
,
783 XmNleftWidget
, nameLabel
,
784 XmNrightAttachment
, XmATTACH_POSITION
,
785 XmNrightPosition
, RIGHT_MARGIN_POS
,
786 XmNbottomAttachment
, XmATTACH_WIDGET
,
787 XmNbottomWidget
, ucd
->nameTextW
, NULL
);
790 XtVaCreateManagedWidget("topLabel", xmLabelGadgetClass
, form
,
791 XmNlabelString
, s1
=MKSTRING(
792 "Select a macro menu item from the list at left.\n\
793 Select \"New\" to add a new command to the menu."),
794 XmNtopAttachment
, XmATTACH_POSITION
,
796 XmNleftAttachment
, XmATTACH_POSITION
,
797 XmNleftPosition
, LIST_RIGHT
,
798 XmNrightAttachment
, XmATTACH_POSITION
,
799 XmNrightPosition
, RIGHT_MARGIN_POS
,
800 XmNbottomAttachment
, XmATTACH_WIDGET
,
801 XmNbottomWidget
, nameLabel
, NULL
);
804 cmdLabel
= XtVaCreateManagedWidget("cmdLabel", xmLabelGadgetClass
, form
,
805 XmNlabelString
, s1
=MKSTRING("Macro Command to Execute"),
807 XmNalignment
, XmALIGNMENT_BEGINNING
,
809 XmNtopAttachment
, XmATTACH_POSITION
,
810 XmNtopPosition
, MACRO_CMD_TOP
,
811 XmNleftAttachment
, XmATTACH_POSITION
,
812 XmNleftPosition
, LEFT_MARGIN_POS
, NULL
);
815 okBtn
= XtVaCreateManagedWidget("ok",xmPushButtonWidgetClass
,form
,
816 XmNlabelString
, s1
=MKSTRING("OK"),
817 XmNleftAttachment
, XmATTACH_POSITION
,
819 XmNrightAttachment
, XmATTACH_POSITION
,
820 XmNrightPosition
, 23,
821 XmNbottomAttachment
, XmATTACH_POSITION
,
822 XmNbottomPosition
, 99, NULL
);
823 XtAddCallback(okBtn
, XmNactivateCallback
, okCB
, ucd
);
826 applyBtn
= XtVaCreateManagedWidget("apply",xmPushButtonWidgetClass
,form
,
827 XmNlabelString
, s1
=MKSTRING("Apply"),
829 XmNleftAttachment
, XmATTACH_POSITION
,
831 XmNrightAttachment
, XmATTACH_POSITION
,
832 XmNrightPosition
, 46,
833 XmNbottomAttachment
, XmATTACH_POSITION
,
834 XmNbottomPosition
, 99, NULL
);
835 XtAddCallback(applyBtn
, XmNactivateCallback
, applyCB
, ucd
);
838 applyBtn
= XtVaCreateManagedWidget("check",xmPushButtonWidgetClass
,form
,
839 XmNlabelString
, s1
=MKSTRING("Check"),
841 XmNleftAttachment
, XmATTACH_POSITION
,
843 XmNrightAttachment
, XmATTACH_POSITION
,
844 XmNrightPosition
, 69,
845 XmNbottomAttachment
, XmATTACH_POSITION
,
846 XmNbottomPosition
, 99, NULL
);
847 XtAddCallback(applyBtn
, XmNactivateCallback
, checkCB
, ucd
);
850 dismissBtn
= XtVaCreateManagedWidget("dismiss",xmPushButtonWidgetClass
,form
,
851 XmNlabelString
, s1
=MKSTRING("Dismiss"),
852 XmNleftAttachment
, XmATTACH_POSITION
,
854 XmNrightAttachment
, XmATTACH_POSITION
,
855 XmNrightPosition
, 92,
856 XmNbottomAttachment
, XmATTACH_POSITION
,
857 XmNbottomPosition
, 99, NULL
);
858 XtAddCallback(dismissBtn
, XmNactivateCallback
, dismissCB
, ucd
);
862 XtSetArg(args
[ac
], XmNeditMode
, XmMULTI_LINE_EDIT
); ac
++;
863 XtSetArg(args
[ac
], XmNtopAttachment
, XmATTACH_WIDGET
); ac
++;
864 XtSetArg(args
[ac
], XmNtopWidget
, cmdLabel
); ac
++;
865 XtSetArg(args
[ac
], XmNleftAttachment
, XmATTACH_POSITION
); ac
++;
866 XtSetArg(args
[ac
], XmNleftPosition
, LEFT_MARGIN_POS
); ac
++;
867 XtSetArg(args
[ac
], XmNrightAttachment
, XmATTACH_POSITION
); ac
++;
868 XtSetArg(args
[ac
], XmNrightPosition
, RIGHT_MARGIN_POS
); ac
++;
869 XtSetArg(args
[ac
], XmNbottomAttachment
, XmATTACH_WIDGET
); ac
++;
870 XtSetArg(args
[ac
], XmNbottomWidget
, okBtn
); ac
++;
871 XtSetArg(args
[ac
], XmNbottomOffset
, 5); ac
++;
872 ucd
->cmdTextW
= XmCreateScrolledText(form
, "name", args
, ac
);
873 AddMouseWheelSupport(ucd
->cmdTextW
);
874 XtManageChild(ucd
->cmdTextW
);
875 RemapDeleteKey(ucd
->cmdTextW
);
876 XtVaSetValues(cmdLabel
, XmNuserData
, ucd
->cmdTextW
, NULL
); /* for mnemonic */
878 /* Disable text input for the accelerator key widget, let the
879 event handler manage it instead */
880 disableTextW(ucd
->accTextW
);
882 /* initializs the dialog fields to match "New" list item */
883 updateDialogFields(NULL
, ucd
);
885 /* Set initial default button */
886 XtVaSetValues(form
, XmNdefaultButton
, okBtn
, NULL
);
887 XtVaSetValues(form
, XmNcancelButton
, dismissBtn
, NULL
);
889 /* Handle mnemonic selection of buttons and focus to dialog */
890 AddDialogMnemonicHandler(form
, FALSE
);
892 /* Make widgets for top level shell and paste-replay buttons available
893 to other functions */
894 if (dialogType
== MACRO_CMDS
) {
895 MacroCmdDialog
= ucd
->dlogShell
;
896 MacroPasteReplayBtn
= pasteReplayBtn
;
898 BGMenuCmdDialog
= ucd
->dlogShell
;
899 BGMenuPasteReplayBtn
= pasteReplayBtn
;
902 /* Realize all of the widgets in the new dialog */
903 RealizeWithoutForcingPosition(ucd
->dlogShell
);
907 ** Update the Shell, Macro, and window background menus menu of window "window"
908 ** from the currently loaded command descriptions.
910 void UpdateShellMenu(WindowInfo
*window
)
912 updateMenu(window
, SHELL_CMDS
);
914 void UpdateMacroMenu(WindowInfo
*window
)
916 updateMenu(window
, MACRO_CMDS
);
918 void UpdateBGMenu(WindowInfo
*window
)
920 updateMenu(window
, BG_MENU_CMDS
);
924 ** Dim/undim buttons for pasting replay macros into macro and bg menu dialogs
926 void DimPasteReplayBtns(int sensitive
)
928 if (MacroCmdDialog
!= NULL
)
929 XtSetSensitive(MacroPasteReplayBtn
, sensitive
);
930 if (BGMenuCmdDialog
!= NULL
)
931 XtSetSensitive(BGMenuPasteReplayBtn
, sensitive
);
935 ** Dim/undim user programmable menu items which depend on there being
936 ** a selection in their associated window.
938 void DimSelectionDepUserMenuItems(WindowInfo
*window
, int sensitive
)
941 dimSelDepItemsInMenu(window
->shellMenuPane
, ShellMenuItems
,
942 NShellMenuItems
, sensitive
);
944 dimSelDepItemsInMenu(window
->macroMenuPane
, MacroMenuItems
,
945 NMacroMenuItems
, sensitive
);
946 dimSelDepItemsInMenu(window
->bgMenuPane
, BGMenuItems
,
947 NBGMenuItems
, sensitive
);
950 static void dimSelDepItemsInMenu(Widget menuPane
, menuItemRec
**menuList
,
951 int nMenuItems
, int sensitive
)
959 XtVaGetValues(menuPane
, XmNchildren
, &items
, XmNnumChildren
, &nItems
, NULL
);
960 for (n
=0; n
<(int)nItems
; n
++) {
961 XtVaGetValues(items
[n
], XmNuserData
, &userData
, NULL
);
962 if (userData
!= (XtPointer
)PERMANENT_MENU_ITEM
) {
963 if (XtClass(items
[n
]) == xmCascadeButtonWidgetClass
) {
964 XtVaGetValues(items
[n
], XmNsubMenuId
, &subMenu
, NULL
);
965 dimSelDepItemsInMenu(subMenu
, menuList
, nMenuItems
, sensitive
);
967 index
= (int)userData
- 10;
968 if (index
<0 || index
>= nMenuItems
)
970 if (menuList
[index
]->input
== FROM_SELECTION
)
971 XtSetSensitive(items
[n
], sensitive
);
978 ** Harmless kludge for making undo/redo menu items in background menu properly
979 ** sensitive (even though they're programmable) along with real undo item
982 void SetBGMenuUndoSensitivity(WindowInfo
*window
, int sensitive
)
984 if (window
->bgMenuUndoItem
!= NULL
)
985 XtSetSensitive(window
->bgMenuUndoItem
, sensitive
);
987 void SetBGMenuRedoSensitivity(WindowInfo
*window
, int sensitive
)
989 if (window
->bgMenuRedoItem
!= NULL
)
990 XtSetSensitive(window
->bgMenuRedoItem
, sensitive
);
994 ** Generate a text string for the preferences file describing the contents
995 ** of the shell cmd list. This string is not exactly of the form that it
996 ** can be read by LoadShellCmdsString, rather, it is what needs to be written
997 ** to a resource file such that it will read back in that form.
999 char *WriteShellCmdsString(void)
1001 return writeMenuItemString(ShellMenuItems
, NShellMenuItems
,
1006 ** Generate a text string for the preferences file describing the contents of
1007 ** the macro menu and background menu commands lists. These strings are not
1008 ** exactly of the form that it can be read by LoadMacroCmdsString, rather, it
1009 ** is what needs to be written to a resource file such that it will read back
1012 char *WriteMacroCmdsString(void)
1014 return writeMenuItemString(MacroMenuItems
, NMacroMenuItems
, MACRO_CMDS
);
1017 char *WriteBGMenuCmdsString(void)
1019 return writeMenuItemString(BGMenuItems
, NBGMenuItems
, BG_MENU_CMDS
);
1023 ** Read a string representing shell command menu items and add them to the
1024 ** internal list used for constructing shell menus
1026 int LoadShellCmdsString(char *inString
)
1028 return loadMenuItemString(inString
, ShellMenuItems
, &NShellMenuItems
,
1033 ** Read strings representing macro menu or background menu command menu items
1034 ** and add them to the internal lists used for constructing menus
1036 int LoadMacroCmdsString(char *inString
)
1038 return loadMenuItemString(inString
, MacroMenuItems
, &NMacroMenuItems
,
1042 int LoadBGMenuCmdsString(char *inString
)
1044 return loadMenuItemString(inString
, BGMenuItems
, &NBGMenuItems
,
1049 ** Search through the shell menu and execute the first command with menu item
1050 ** name "itemName". Returns True on successs and False on failure.
1053 int DoNamedShellMenuCmd(WindowInfo
*window
, const char *itemName
, int fromMacro
)
1057 for (i
=0; i
<NShellMenuItems
; i
++) {
1058 if (!strcmp(ShellMenuItems
[i
]->name
, itemName
)) {
1059 if (ShellMenuItems
[i
]->output
== TO_SAME_WINDOW
&&
1060 CheckReadOnly(window
))
1062 DoShellMenuCmd(window
, ShellMenuItems
[i
]->cmd
,
1063 ShellMenuItems
[i
]->input
, ShellMenuItems
[i
]->output
,
1064 ShellMenuItems
[i
]->repInput
, ShellMenuItems
[i
]->saveFirst
,
1065 ShellMenuItems
[i
]->loadAfter
, fromMacro
);
1074 ** Search through the Macro or background menu and execute the first command
1075 ** with menu item name "itemName". Returns True on successs and False on
1078 int DoNamedMacroMenuCmd(WindowInfo
*window
, const char *itemName
)
1082 for (i
=0; i
<NMacroMenuItems
; i
++) {
1083 if (!strcmp(MacroMenuItems
[i
]->name
, itemName
)) {
1084 DoMacro(window
, MacroMenuItems
[i
]->cmd
, "macro menu command");
1091 int DoNamedBGMenuCmd(WindowInfo
*window
, const char *itemName
)
1095 for (i
=0; i
<NBGMenuItems
; i
++) {
1096 if (!strcmp(BGMenuItems
[i
]->name
, itemName
)) {
1097 DoMacro(window
, BGMenuItems
[i
]->cmd
, "background menu macro");
1105 ** Update all of the Shell or Macro menus of all editor windows.
1107 static void updateMenus(int menuType
)
1111 for (w
=WindowList
; w
!=NULL
; w
=w
->next
)
1112 updateMenu(w
, menuType
);
1116 ** Updates either the Shell menu or the Macro menu of "window", depending on
1117 ** value of "menuType"
1119 static void updateMenu(WindowInfo
*window
, int menuType
)
1121 Widget btn
, menuPane
, subPane
, newSubPane
;
1123 menuItemRec
*f
, **itemList
;
1124 menuTreeItem
*menuTree
;
1125 int i
, nTreeEntries
, isDefaultLM
;
1126 char *hierName
, *namePtr
, *subMenuName
, *subSep
, *strippedName
, *name
;
1128 /* Fetch the appropriate menu pane and item list for this menu type */
1129 if (menuType
== SHELL_CMDS
) {
1130 menuPane
= window
->shellMenuPane
;
1131 itemList
= ShellMenuItems
;
1132 nListItems
= NShellMenuItems
;
1133 } else if (menuType
== MACRO_CMDS
) {
1134 menuPane
= window
->macroMenuPane
;
1135 itemList
= MacroMenuItems
;
1136 nListItems
= NMacroMenuItems
;
1137 } else { /* BG_MENU_CMDS */
1138 menuPane
= window
->bgMenuPane
;
1139 itemList
= BGMenuItems
;
1140 nListItems
= NBGMenuItems
;
1143 /* Remove all of the existing user commands from the menu */
1144 removeMenuItems(menuPane
);
1146 /* Allocate storage for structures to help find sub-menus */
1147 menuTree
= (menuTreeItem
*)XtMalloc(sizeof(menuTreeItem
) * nListItems
);
1150 /* Harmless kludge: undo and redo items are marked specially if found
1151 in the background menu, and used to dim/undim with edit menu */
1152 window
->bgMenuUndoItem
= NULL
;
1153 window
->bgMenuRedoItem
= NULL
;
1156 ** Add items to the list, creating hierarchical sub-menus as necessary,
1157 ** and skipping items not intended for this language mode
1159 for (n
=0; n
<nListItems
; n
++) {
1162 /* Eliminate items meant for other language modes, strip @ sign parts.
1163 If the language mode is "*", scan the list for an item with the
1164 same name and a language mode specified. If one is found, skip
1165 the item in favor of the exact match. */
1166 strippedName
= findStripLanguageMode(f
->name
, window
->languageMode
,
1168 if (strippedName
== NULL
)
1169 continue; /* not a valid entry for the language */
1171 for (i
=0; i
<nListItems
; i
++) {
1172 name
= findStripLanguageMode(itemList
[i
]->name
,
1173 window
->languageMode
, &isDefaultLM
);
1174 if (name
!=NULL
&& !isDefaultLM
&& !strcmp(name
, strippedName
)) {
1175 XtFree(name
); /* item with matching language overrides */
1180 if (i
!= nListItems
) {
1181 XtFree(strippedName
);
1186 /* create/find sub-menus, stripping off '>' until item name is
1187 reached, then create the menu item */
1188 namePtr
= strippedName
;
1191 subSep
= strchr(namePtr
, '>');
1192 if (subSep
== NULL
) {
1193 btn
= createUserMenuItem(subPane
, namePtr
, f
, n
,
1194 (XtCallbackProc
)(menuType
== SHELL_CMDS
? shellMenuCB
:
1195 (menuType
== MACRO_CMDS
? macroMenuCB
: bgMenuCB
)),
1197 if (menuType
== BG_MENU_CMDS
&& !strcmp(f
->cmd
, "undo()\n"))
1198 window
->bgMenuUndoItem
= btn
;
1199 else if (menuType
== BG_MENU_CMDS
&& !strcmp(f
->cmd
,"redo()\n"))
1200 window
->bgMenuRedoItem
= btn
;
1201 UpdateAccelLockPatch(window
->splitPane
, btn
);
1204 hierName
= copySubstring(strippedName
, subSep
- strippedName
);
1205 newSubPane
= findInMenuTree(menuTree
, nTreeEntries
, hierName
);
1206 if (newSubPane
== NULL
) {
1207 subMenuName
= copySubstring(namePtr
, subSep
- namePtr
);
1208 newSubPane
= createUserSubMenu(subPane
, subMenuName
);
1209 XtFree(subMenuName
);
1210 menuTree
[nTreeEntries
].name
= hierName
;
1211 menuTree
[nTreeEntries
++].menuPane
= newSubPane
;
1214 subPane
= newSubPane
;
1215 namePtr
= subSep
+ 1;
1217 XtFree(strippedName
);
1220 /* Free the structure used to keep track of sub-menus durring creation */
1221 for (i
=0; i
<nTreeEntries
; i
++)
1222 XtFree(menuTree
[i
].name
);
1223 XtFree((char *)menuTree
);
1225 /* Set the proper sensitivity of items which may be dimmed */
1226 SetBGMenuUndoSensitivity(window
, XtIsSensitive(window
->undoItem
));
1227 SetBGMenuRedoSensitivity(window
, XtIsSensitive(window
->redoItem
));
1228 DimSelectionDepUserMenuItems(window
, window
->buffer
->primary
.selected
);
1232 ** Find the widget corresponding to a hierarchical menu name (a>b>c...)
1234 static Widget
findInMenuTree(menuTreeItem
*menuTree
, int nTreeEntries
,
1235 const char *hierName
)
1239 for (i
=0; i
<nTreeEntries
; i
++)
1240 if (!strcmp(hierName
, menuTree
[i
].name
))
1241 return menuTree
[i
].menuPane
;
1245 static char *copySubstring(const char *string
, int length
)
1247 char *retStr
= XtMalloc(length
+ 1);
1249 strncpy(retStr
, string
, length
);
1250 retStr
[length
] = '\0';
1255 ** Look for at signs (@) in the string menuItemName, and match them
1256 ** against the current language mode. If there are no @ signs, just
1257 ** return an allocated copy of menuItemName. If there are @ signs, match
1258 ** the following text against languageMode, and return NULL if none match,
1259 ** or an allocated copy of menuItemName stripped of @ parts. If the
1260 ** language name is "*", sets isDefaultLM to true.
1262 static char *findStripLanguageMode(const char *menuItemName
, int languageMode
,
1265 char *atPtr
, *firstAtPtr
, *endPtr
;
1268 atPtr
= firstAtPtr
= strchr(menuItemName
, '@');
1269 *isDefaultLM
= False
;
1272 return XtNewString(menuItemName
);
1274 if (!strcmp(atPtr
+1, "*")) {
1275 /* only language is "*": this is for all but language specific macros */
1276 *isDefaultLM
= True
;
1277 return copySubstring(menuItemName
, firstAtPtr
-menuItemName
);
1279 if (languageMode
== PLAIN_LANGUAGE_MODE
)
1282 for(endPtr
=atPtr
+1; isalnum((unsigned char)*endPtr
) || *endPtr
=='_' ||
1283 *endPtr
=='-' || *endPtr
==' ' || *endPtr
=='+' || *endPtr
=='$' ||
1284 *endPtr
=='#'; endPtr
++);
1285 lmNameLen
= endPtr
-atPtr
-1;
1286 if (!strncmp(LanguageModeName(languageMode
), atPtr
+1, lmNameLen
) &&
1287 LanguageModeName(languageMode
)[lmNameLen
] == '\0')
1288 return copySubstring(menuItemName
, firstAtPtr
-menuItemName
);
1289 atPtr
= strchr(atPtr
+1, '@');
1295 static Widget
createUserMenuItem(Widget menuPane
, char *name
, menuItemRec
*f
,
1296 int index
, XtCallbackProc cbRtn
, XtPointer cbArg
)
1299 char accText
[MAX_ACCEL_LEN
], accKeys
[MAX_ACCEL_LEN
+5];
1302 generateAcceleratorString(accText
, f
->modifiers
, f
->keysym
);
1303 genAccelEventName(accKeys
, f
->modifiers
, f
->keysym
);
1304 st1
=XmStringCreateSimple(name
);
1305 st2
=XmStringCreateSimple(accText
);
1306 btn
= XtVaCreateManagedWidget("cmd", xmPushButtonWidgetClass
, menuPane
,
1307 XmNlabelString
, st1
,
1308 XmNacceleratorText
, st2
,
1309 XmNaccelerator
, accKeys
,
1310 XmNmnemonic
, f
->mnemonic
,
1311 XmNuserData
, index
+10, NULL
);
1312 XtAddCallback(btn
, XmNactivateCallback
, cbRtn
, cbArg
);
1319 ** Add a user-defined sub-menu to an established pull-down menu, marking
1320 ** it's userData field with TEMPORARY_MENU_ITEM so it can be found and
1321 ** removed later if the menu is redefined. Returns the menu pane of the
1324 static Widget
createUserSubMenu(Widget parent
, char *label
)
1328 static Arg args
[1] = {{XmNuserData
, (XtArgVal
)TEMPORARY_MENU_ITEM
}};
1330 menu
= CreatePulldownMenu(parent
, "userPulldown", args
, 1);
1331 XtVaCreateManagedWidget("userCascade", xmCascadeButtonWidgetClass
, parent
,
1332 XmNlabelString
, st1
=XmStringCreateSimple(label
),
1333 XmNsubMenuId
, menu
, XmNuserData
, TEMPORARY_MENU_ITEM
, NULL
);
1338 static void removeMenuItems(Widget menuPane
)
1340 WidgetList items
, itemList
;
1346 /* Fetch the list of children from the menu pane, and make a copy
1347 (because the widget alters this list as you delete widgets) */
1348 XtVaGetValues(menuPane
, XmNchildren
, &itemList
, XmNnumChildren
, &nItems
,
1350 items
= (WidgetList
)XtMalloc(sizeof(Widget
) * nItems
);
1351 memcpy(items
, itemList
, sizeof(Widget
) * nItems
);
1353 /* Delete all of the widgets not marked as PERMANENT_MENU_ITEM */
1354 for (n
=0; n
<(int)nItems
; n
++) {
1355 XtVaGetValues(items
[n
], XmNuserData
, &userData
, NULL
);
1356 if (userData
!= (XtPointer
)PERMANENT_MENU_ITEM
) {
1357 if (XtClass(items
[n
]) == xmCascadeButtonWidgetClass
) {
1358 XtVaGetValues(items
[n
], XmNsubMenuId
, &subMenuID
, NULL
);
1359 removeMenuItems(subMenuID
);
1360 #if XmVersion < 2000 /* Skipping this creates a memory and server resource
1361 leak (though both are reclaimed on window closing). In
1362 Motif 2.0 (and beyond?) there is a potential crash during
1363 phase 2 widget destruction in "SetCascadeField", and in
1364 Motif 1.2 there are free-memory reads. I would really like
1365 to be able to destroy this. */
1366 XtDestroyWidget(subMenuID
);
1368 } else /* remove accel. before destroy or lose it forever */
1369 XtVaSetValues(items
[n
], XmNaccelerator
, NULL
, NULL
);
1370 /* unmanaging before destroying stops parent from displaying */
1371 XtUnmanageChild(items
[n
]);
1372 XtDestroyWidget(items
[n
]);
1375 XtFree((char *)items
);
1378 static void dismissCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1380 userCmdDialog
*ucd
= (userCmdDialog
*)clientData
;
1382 /* Mark that there's no longer a (macro, bg, or shell) dialog up */
1383 if (ucd
->dialogType
== SHELL_CMDS
)
1384 ShellCmdDialog
= NULL
;
1385 else if (ucd
->dialogType
== MACRO_CMDS
)
1386 MacroCmdDialog
= NULL
;
1388 BGMenuCmdDialog
= NULL
;
1390 /* pop down and destroy the dialog (memory for ucd is freed in the
1391 destroy callback) */
1392 XtDestroyWidget(ucd
->dlogShell
);
1395 static void okCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1397 userCmdDialog
*ucd
= (userCmdDialog
*)clientData
;
1399 /* Read the dialog fields, and update the menus */
1400 if (!applyDialogChanges(ucd
))
1403 /* Mark that there's no longer a (macro, bg, or shell) dialog up */
1404 if (ucd
->dialogType
== SHELL_CMDS
)
1405 ShellCmdDialog
= NULL
;
1406 else if (ucd
->dialogType
== MACRO_CMDS
)
1407 MacroCmdDialog
= NULL
;
1409 BGMenuCmdDialog
= NULL
;
1411 /* pop down and destroy the dialog (memory for ucd is freed in the
1412 destroy callback) */
1413 XtDestroyWidget(ucd
->dlogShell
);
1416 static void applyCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1418 applyDialogChanges((userCmdDialog
*)clientData
);
1421 static void checkCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1423 userCmdDialog
*ucd
= (userCmdDialog
*)clientData
;
1425 if (checkMacro(ucd
))
1427 DialogF(DF_INF
, ucd
->dlogShell
, 1, "Macro",
1428 "Macro compiled without error", "Dismiss");
1432 static int checkMacro(userCmdDialog
*ucd
)
1436 f
= readDialogFields(ucd
, False
);
1439 if (!checkMacroText(f
->cmd
, ucd
->dlogShell
, ucd
->cmdTextW
)) {
1446 static int checkMacroText(char *macro
, Widget errorParent
, Widget errFocus
)
1449 char *errMsg
, *stoppedAt
;
1451 prog
= ParseMacro(macro
, &errMsg
, &stoppedAt
);
1453 if (errorParent
!= NULL
) {
1454 ParseError(errorParent
, macro
, stoppedAt
, "macro", errMsg
);
1455 XmTextSetInsertionPosition(errFocus
, stoppedAt
- macro
);
1456 XmProcessTraversal(errFocus
, XmTRAVERSE_CURRENT
);
1461 if (*stoppedAt
!= '\0') {
1462 if (errorParent
!= NULL
) {
1463 ParseError(errorParent
, macro
, stoppedAt
,"macro","syntax error");
1464 XmTextSetInsertionPosition(errFocus
, stoppedAt
- macro
);
1465 XmProcessTraversal(errFocus
, XmTRAVERSE_CURRENT
);
1472 static int applyDialogChanges(userCmdDialog
*ucd
)
1476 /* Get the current contents of the dialog fields */
1477 if (!UpdateManagedList(ucd
->managedList
, True
))
1480 /* Test compile the macro */
1481 if (ucd
->dialogType
== MACRO_CMDS
)
1482 if (!checkMacro(ucd
))
1485 /* Update the menu information */
1486 if (ucd
->dialogType
== SHELL_CMDS
) {
1487 for (i
=0; i
<NShellMenuItems
; i
++)
1488 freeMenuItemRec(ShellMenuItems
[i
]);
1489 for (i
=0; i
<ucd
->nMenuItems
; i
++)
1490 ShellMenuItems
[i
] = copyMenuItemRec(ucd
->menuItemsList
[i
]);
1491 NShellMenuItems
= ucd
->nMenuItems
;
1492 } else if (ucd
->dialogType
== MACRO_CMDS
) {
1493 for (i
=0; i
<NMacroMenuItems
; i
++)
1494 freeMenuItemRec(MacroMenuItems
[i
]);
1495 for (i
=0; i
<ucd
->nMenuItems
; i
++)
1496 MacroMenuItems
[i
] = copyMenuItemRec(ucd
->menuItemsList
[i
]);
1497 NMacroMenuItems
= ucd
->nMenuItems
;
1498 } else { /* BG_MENU_CMDS */
1499 for (i
=0; i
<NBGMenuItems
; i
++)
1500 freeMenuItemRec(BGMenuItems
[i
]);
1501 for (i
=0; i
<ucd
->nMenuItems
; i
++)
1502 BGMenuItems
[i
] = copyMenuItemRec(ucd
->menuItemsList
[i
]);
1503 NBGMenuItems
= ucd
->nMenuItems
;
1506 /* Update the menus themselves in all of the NEdit windows */
1507 updateMenus(ucd
->dialogType
);
1509 /* Note that preferences have been changed */
1514 static void pasteReplayCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1516 userCmdDialog
*ucd
= (userCmdDialog
*)clientData
;
1518 if (GetReplayMacro() == NULL
)
1521 XmTextInsert(ucd
->cmdTextW
, XmTextGetInsertionPosition(ucd
->cmdTextW
),
1525 static void destroyCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1527 userCmdDialog
*ucd
= (userCmdDialog
*)clientData
;
1530 for (i
=0; i
<ucd
->nMenuItems
; i
++)
1531 freeMenuItemRec(ucd
->menuItemsList
[i
]);
1532 XtFree((char *)ucd
->menuItemsList
);
1533 XtFree((char *)ucd
);
1536 static void accFocusCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1538 userCmdDialog
*ucd
= (userCmdDialog
*)clientData
;
1540 RemoveDialogMnemonicHandler(XtParent(ucd
->accTextW
));
1543 static void accLoseFocusCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1545 userCmdDialog
*ucd
= (userCmdDialog
*)clientData
;
1547 AddDialogMnemonicHandler(XtParent(ucd
->accTextW
), FALSE
);
1550 static void accKeyCB(Widget w
, XtPointer clientData
, XKeyEvent
*event
)
1552 userCmdDialog
*ucd
= (userCmdDialog
*)clientData
;
1553 KeySym keysym
= XLookupKeysym(event
, 0);
1554 char outStr
[MAX_ACCEL_LEN
];
1556 /* Accept only real keys, not modifiers alone */
1557 if (IsModifierKey(keysym
))
1560 /* Tab key means go to the next field, don't enter */
1561 if (keysym
== XK_Tab
)
1564 /* Beep and return if the modifiers are buttons or ones we don't support */
1565 if (event
->state
& ~(ShiftMask
| LockMask
| ControlMask
| Mod1Mask
|
1566 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
)) {
1567 XBell(TheDisplay
, 0);
1571 /* Delete or backspace clears field */
1572 if (keysym
== XK_Delete
|| keysym
== XK_BackSpace
) {
1573 XmTextSetString(ucd
->accTextW
, "");
1577 /* generate the string to use in the dialog field */
1578 generateAcceleratorString(outStr
, event
->state
, keysym
);
1580 /* Reject single character accelerators (a very simple way to eliminate
1581 un-modified letters and numbers) The goal is give users a clue that
1582 they're supposed to type the actual keys, not the name. This scheme
1583 is not rigorous and still allows accelerators like Comma. */
1584 if (strlen(outStr
) == 1) {
1585 XBell(TheDisplay
, 0);
1589 /* fill in the accelerator field in the dialog */
1590 XmTextSetString(ucd
->accTextW
, outStr
);
1593 static void sameOutCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1595 XtSetSensitive(((userCmdDialog
*)clientData
)->repInpBtn
,
1596 XmToggleButtonGetState(w
));
1599 static void shellMenuCB(Widget w
, WindowInfo
*window
, XtPointer callData
)
1605 /* get the index of the shell command and verify that it's in range */
1606 XtVaGetValues(w
, XmNuserData
, &userData
, NULL
);
1607 index
= (int)userData
- 10;
1608 if (index
<0 || index
>= NShellMenuItems
)
1611 params
[0] = ShellMenuItems
[index
]->name
;
1612 XtCallActionProc(window
->lastFocus
, "shell_menu_command",
1613 ((XmAnyCallbackStruct
*)callData
)->event
, params
, 1);
1616 static void macroMenuCB(Widget w
, WindowInfo
*window
, XtPointer callData
)
1622 /* Don't allow users to execute a macro command from the menu (or accel)
1623 if there's already a macro command executing. NEdit can't handle
1624 running multiple, independent uncoordinated, macros in the same
1625 window. Macros may invoke macro menu commands recursively via the
1626 macro_menu_command action proc, which is important for being able to
1627 repeat any operation, and to embed macros within eachother at any
1628 level, however, a call here with a macro running means that THE USER
1629 is explicitly invoking another macro via the menu or an accelerator. */
1630 if (window
->macroCmdData
!= NULL
) {
1631 XBell(TheDisplay
, 0);
1635 /* get the index of the macro command and verify that it's in range */
1636 XtVaGetValues(w
, XmNuserData
, &userData
, NULL
);
1637 index
= (int)userData
- 10;
1638 if (index
<0 || index
>= NMacroMenuItems
)
1641 params
[0] = MacroMenuItems
[index
]->name
;
1642 XtCallActionProc(window
->lastFocus
, "macro_menu_command",
1643 ((XmAnyCallbackStruct
*)callData
)->event
, params
, 1);
1646 static void bgMenuCB(Widget w
, WindowInfo
*window
, XtPointer callData
)
1652 /* Same remark as for macro menu commands (see above). */
1653 if (window
->macroCmdData
!= NULL
) {
1654 XBell(TheDisplay
, 0);
1658 /* get the index of the macro command and verify that it's in range */
1659 XtVaGetValues(w
, XmNuserData
, &userData
, NULL
);
1660 index
= (int)userData
- 10;
1661 if (index
<0 || index
>= NBGMenuItems
)
1664 params
[0] = BGMenuItems
[index
]->name
;
1665 XtCallActionProc(window
->lastFocus
, "bg_menu_command",
1666 ((XmAnyCallbackStruct
*)callData
)->event
, params
, 1);
1670 ** Update the name, accelerator, mnemonic, and command fields in the shell
1671 ** command or macro dialog to agree with the currently selected item in the
1674 static void updateDialogFields(menuItemRec
*f
, userCmdDialog
*ucd
)
1676 char mneString
[2], accString
[MAX_ACCEL_LEN
];
1678 /* fill in the name, accelerator, mnemonic, and command fields of the
1679 dialog for the newly selected item, or blank them if "New" is selected */
1681 XmTextSetString(ucd
->nameTextW
, "");
1682 XmTextSetString(ucd
->cmdTextW
, "");
1683 XmTextSetString(ucd
->accTextW
, "");
1684 XmTextSetString(ucd
->mneTextW
, "");
1685 if (ucd
->dialogType
== SHELL_CMDS
) {
1686 XmToggleButtonSetState(ucd
->selInpBtn
, True
, True
);
1687 XmToggleButtonSetState(ucd
->sameOutBtn
, True
, True
);
1688 XmToggleButtonSetState(ucd
->repInpBtn
, False
, False
);
1689 XtSetSensitive(ucd
->repInpBtn
, True
);
1690 XmToggleButtonSetState(ucd
->saveFirstBtn
, False
, False
);
1691 XmToggleButtonSetState(ucd
->loadAfterBtn
, False
, False
);
1694 mneString
[0] = f
->mnemonic
;
1695 mneString
[1] = '\0';
1696 generateAcceleratorString(accString
, f
->modifiers
, f
->keysym
);
1697 XmTextSetString(ucd
->nameTextW
, f
->name
);
1698 XmTextSetString(ucd
->cmdTextW
, f
->cmd
);
1699 XmTextSetString(ucd
->accTextW
, accString
);
1700 XmTextSetString(ucd
->mneTextW
, mneString
);
1701 XmToggleButtonSetState(ucd
->selInpBtn
, f
->input
==FROM_SELECTION
, False
);
1702 if (ucd
->dialogType
== SHELL_CMDS
) {
1703 XmToggleButtonSetState(ucd
->winInpBtn
, f
->input
== FROM_WINDOW
,
1705 XmToggleButtonSetState(ucd
->eitherInpBtn
, f
->input
== FROM_EITHER
,
1707 XmToggleButtonSetState(ucd
->noInpBtn
, f
->input
== FROM_NONE
,
1709 XmToggleButtonSetState(ucd
->sameOutBtn
, f
->output
==TO_SAME_WINDOW
,
1711 XmToggleButtonSetState(ucd
->winOutBtn
, f
->output
==TO_NEW_WINDOW
,
1713 XmToggleButtonSetState(ucd
->dlogOutBtn
, f
->output
==TO_DIALOG
,
1715 XmToggleButtonSetState(ucd
->repInpBtn
, f
->repInput
, False
);
1716 XtSetSensitive(ucd
->repInpBtn
, f
->output
==TO_SAME_WINDOW
);
1717 XmToggleButtonSetState(ucd
->saveFirstBtn
, f
->saveFirst
, False
);
1718 XmToggleButtonSetState(ucd
->loadAfterBtn
, f
->loadAfter
, False
);
1724 ** Read the name, accelerator, mnemonic, and command fields from the shell or
1725 ** macro commands dialog into a newly allocated menuItemRec. Returns a
1726 ** pointer to the new menuItemRec structure as the function value, or NULL on
1729 static menuItemRec
*readDialogFields(userCmdDialog
*ucd
, int silent
)
1731 char *nameText
, *cmdText
, *mneText
, *accText
;
1734 nameText
= XmTextGetString(ucd
->nameTextW
);
1735 if (*nameText
== '\0')
1739 DialogF(DF_WARN
, ucd
->dlogShell
, 1, "Menu Entry",
1740 "Please specify a name\nfor the menu item", "Dismiss");
1741 XmProcessTraversal(ucd
->nameTextW
, XmTRAVERSE_CURRENT
);
1747 if (strchr(nameText
, ':'))
1751 DialogF(DF_WARN
, ucd
->dlogShell
, 1, "Menu Entry",
1752 "Menu item names may not\ncontain colon (:) characters",
1754 XmProcessTraversal(ucd
->nameTextW
, XmTRAVERSE_CURRENT
);
1760 cmdText
= XmTextGetString(ucd
->cmdTextW
);
1761 if (cmdText
== NULL
|| *cmdText
== '\0')
1765 DialogF(DF_WARN
, ucd
->dlogShell
, 1, "Command to Execute",
1766 "Please specify %s to execute", "Dismiss",
1767 ucd
->dialogType
== SHELL_CMDS
1769 : "macro command(s)");
1770 XmProcessTraversal(ucd
->cmdTextW
, XmTRAVERSE_CURRENT
);
1781 if (ucd
->dialogType
== MACRO_CMDS
|| ucd
->dialogType
== BG_MENU_CMDS
) {
1782 addTerminatingNewline(&cmdText
);
1783 if (!checkMacroText(cmdText
, silent
? NULL
: ucd
->dlogShell
,
1790 f
= (menuItemRec
*)XtMalloc(sizeof(menuItemRec
));
1793 if ((mneText
= XmTextGetString(ucd
->mneTextW
)) != NULL
) {
1794 f
->mnemonic
= mneText
==NULL
? '\0' : mneText
[0];
1796 if (f
->mnemonic
== ':') /* colons mess up string parsing */
1799 if ((accText
= XmTextGetString(ucd
->accTextW
)) != NULL
) {
1800 parseAcceleratorString(accText
, &f
->modifiers
, &f
->keysym
);
1803 if (ucd
->dialogType
== SHELL_CMDS
) {
1804 if (XmToggleButtonGetState(ucd
->selInpBtn
))
1805 f
->input
= FROM_SELECTION
;
1806 else if (XmToggleButtonGetState(ucd
->winInpBtn
))
1807 f
->input
= FROM_WINDOW
;
1808 else if (XmToggleButtonGetState(ucd
->eitherInpBtn
))
1809 f
->input
= FROM_EITHER
;
1811 f
->input
= FROM_NONE
;
1812 if (XmToggleButtonGetState(ucd
->winOutBtn
))
1813 f
->output
= TO_NEW_WINDOW
;
1814 else if (XmToggleButtonGetState(ucd
->dlogOutBtn
))
1815 f
->output
= TO_DIALOG
;
1817 f
->output
= TO_SAME_WINDOW
;
1818 f
->repInput
= XmToggleButtonGetState(ucd
->repInpBtn
);
1819 f
->saveFirst
= XmToggleButtonGetState(ucd
->saveFirstBtn
);
1820 f
->loadAfter
= XmToggleButtonGetState(ucd
->loadAfterBtn
);
1822 f
->input
= XmToggleButtonGetState(ucd
->selInpBtn
) ? FROM_SELECTION
:
1824 f
->output
= TO_SAME_WINDOW
;
1825 f
->repInput
= False
;
1826 f
->saveFirst
= False
;
1827 f
->loadAfter
= False
;
1833 ** Copy a menu item record, and its associated memory
1835 static menuItemRec
*copyMenuItemRec(menuItemRec
*item
)
1837 menuItemRec
*newItem
;
1839 newItem
= (menuItemRec
*)XtMalloc(sizeof(menuItemRec
));
1841 newItem
->name
= XtMalloc(strlen(item
->name
)+1);
1842 strcpy(newItem
->name
, item
->name
);
1843 newItem
->cmd
= XtMalloc(strlen(item
->cmd
)+1);
1844 strcpy(newItem
->cmd
, item
->cmd
);
1849 ** Free a menu item record, and its associated memory
1851 static void freeMenuItemRec(menuItemRec
*item
)
1855 XtFree((char *)item
);
1859 ** Callbacks for managed-list operations
1861 static void *getDialogDataCB(void *oldItem
, int explicitRequest
, int *abort
,
1864 userCmdDialog
*ucd
= (userCmdDialog
*)cbArg
;
1865 menuItemRec
*currentFields
;
1867 /* If the dialog is currently displaying the "new" entry and the
1868 fields are empty, that's just fine */
1869 if (oldItem
== NULL
&& dialogFieldsAreEmpty(ucd
))
1872 /* If there are no problems reading the data, just return it */
1873 currentFields
= readDialogFields(ucd
, True
);
1874 if (currentFields
!= NULL
)
1875 return (void *)currentFields
;
1877 /* If user might not be expecting fields to be read, give more warning */
1878 if (!explicitRequest
)
1880 if (DialogF(DF_WARN
, ucd
->dlogShell
, 2, "Discard Entry",
1881 "Discard incomplete entry\nfor current menu item?", "Keep",
1884 return oldItem
== NULL
1886 : (void *)copyMenuItemRec((menuItemRec
*)oldItem
);
1890 /* Do readDialogFields again without "silent" mode to display warning(s) */
1891 readDialogFields(ucd
, False
);
1897 static void setDialogDataCB(void *item
, void *cbArg
)
1899 updateDialogFields((menuItemRec
*)item
, (userCmdDialog
*)cbArg
);
1902 static int dialogFieldsAreEmpty(userCmdDialog
*ucd
)
1904 return TextWidgetIsBlank(ucd
->nameTextW
) &&
1905 TextWidgetIsBlank(ucd
->cmdTextW
) &&
1906 TextWidgetIsBlank(ucd
->accTextW
) &&
1907 TextWidgetIsBlank(ucd
->mneTextW
) &&
1908 (ucd
->dialogType
!= SHELL_CMDS
|| (
1909 XmToggleButtonGetState(ucd
->selInpBtn
) &&
1910 XmToggleButtonGetState(ucd
->sameOutBtn
) &&
1911 !XmToggleButtonGetState(ucd
->repInpBtn
) &&
1912 !XmToggleButtonGetState(ucd
->saveFirstBtn
) &&
1913 !XmToggleButtonGetState(ucd
->loadAfterBtn
)));
1916 static void freeItemCB(void *item
)
1918 freeMenuItemRec((menuItemRec
*)item
);
1922 ** Gut a text widget of it's ability to process input
1924 static void disableTextW(Widget textW
)
1926 static XtTranslations emptyTable
= NULL
;
1927 static char *emptyTranslations
= "\
1928 <EnterWindow>: enter()\n\
1929 <Btn1Down>: grab-focus()\n\
1930 <Btn1Motion>: extend-adjust()\n\
1931 <Btn1Up>: extend-end()\n\
1932 Shift<Key>Tab: prev-tab-group()\n\
1933 Ctrl<Key>Tab: next-tab-group()\n\
1934 <Key>Tab: next-tab-group()\n\
1935 <LeaveWindow>: leave()\n\
1936 <FocusIn>: focusIn()\n\
1937 <FocusOut>: focusOut()\n\
1938 <Unmap>: unmap()\n";
1940 /* replace the translation table with the slimmed down one above */
1941 if (emptyTable
== NULL
)
1942 emptyTable
= XtParseTranslationTable(emptyTranslations
);
1943 XtVaSetValues(textW
, XmNtranslations
, emptyTable
, NULL
);
1946 static char *writeMenuItemString(menuItemRec
**menuItems
, int nItems
,
1949 char *outStr
, *outPtr
, *c
, accStr
[MAX_ACCEL_LEN
];
1953 /* determine the max. amount of memory needed for the returned string
1954 and allocate a buffer for composing the string */
1956 for (i
=0; i
<nItems
; i
++) {
1958 generateAcceleratorString(accStr
, f
->modifiers
, f
->keysym
);
1959 length
+= strlen(f
->name
) * 2; /* allow for \n & \\ expansions */
1960 length
+= strlen(accStr
);
1961 length
+= strlen(f
->cmd
) * 6; /* allow for \n & \\ expansions */
1962 length
+= 21; /* number of characters added below */
1964 length
++; /* terminating null */
1965 outStr
= XtMalloc(length
);
1967 /* write the string */
1971 for (i
=0; i
<nItems
; i
++) {
1973 generateAcceleratorString(accStr
, f
->modifiers
, f
->keysym
);
1975 for (c
=f
->name
; *c
!='\0'; ++c
) { /* Copy the command name */
1976 if (*c
== '\\') { /* changing backslashes to \\ */
1979 } else if (*c
== '\n') { /* changing newlines to \n */
1987 strcpy(outPtr
, accStr
);
1988 outPtr
+= strlen(accStr
);
1990 if (f
->mnemonic
!= '\0')
1991 *outPtr
++ = f
->mnemonic
;
1993 if (listType
== SHELL_CMDS
) {
1994 if (f
->input
== FROM_SELECTION
)
1996 else if (f
->input
== FROM_WINDOW
)
1998 else if (f
->input
== FROM_EITHER
)
2000 if (f
->output
== TO_DIALOG
)
2002 else if (f
->output
== TO_NEW_WINDOW
)
2012 if (f
->input
== FROM_SELECTION
)
2024 for (c
=f
->cmd
; *c
!='\0'; c
++) { /* Copy the command string, changing */
2025 if (*c
== '\\') { /* backslashes to double backslashes */
2026 *outPtr
++ = '\\'; /* and newlines to backslash-n's, */
2027 *outPtr
++ = '\\'; /* followed by real newlines and tab */
2028 } else if (*c
== '\n') {
2038 if (listType
== MACRO_CMDS
|| listType
== BG_MENU_CMDS
) {
2039 if (*(outPtr
-1) == '\t') outPtr
--;
2052 static int loadMenuItemString(char *inString
, menuItemRec
**menuItems
,
2053 int *nItems
, int listType
)
2057 char *inPtr
= inString
;
2058 char *nameStr
, accStr
[MAX_ACCEL_LEN
], mneChar
;
2060 unsigned int modifiers
;
2061 int i
, input
, output
, saveFirst
, loadAfter
, repInput
;
2062 int nameLen
, accLen
, mneLen
, cmdLen
;
2066 /* remove leading whitespace */
2067 while (*inPtr
== ' ' || *inPtr
== '\t')
2070 /* read name field */
2071 nameLen
= strcspn(inPtr
, ":");
2073 return parseError("no name field");
2074 nameStr
= XtMalloc(nameLen
+1);
2075 strncpy(nameStr
, inPtr
, nameLen
);
2076 nameStr
[nameLen
] = '\0';
2079 return parseError("end not expected");
2082 /* read accelerator field */
2083 accLen
= strcspn(inPtr
, ":");
2084 if (accLen
>= MAX_ACCEL_LEN
)
2085 return parseError("accelerator field too long");
2086 strncpy(accStr
, inPtr
, accLen
);
2087 accStr
[accLen
] = '\0';
2090 return parseError("end not expected");
2093 /* read menemonic field */
2094 mneLen
= strcspn(inPtr
, ":");
2096 return parseError("mnemonic field too long");
2103 return parseError("end not expected");
2105 /* read flags field */
2107 output
= TO_SAME_WINDOW
;
2111 for (; *inPtr
!= ':'; inPtr
++) {
2112 if (listType
== SHELL_CMDS
) {
2114 input
= FROM_SELECTION
;
2115 else if (*inPtr
== 'A')
2116 input
= FROM_WINDOW
;
2117 else if (*inPtr
== 'E')
2118 input
= FROM_EITHER
;
2119 else if (*inPtr
== 'W')
2120 output
= TO_NEW_WINDOW
;
2121 else if (*inPtr
== 'D')
2123 else if (*inPtr
== 'X')
2125 else if (*inPtr
== 'S')
2127 else if (*inPtr
== 'L')
2130 return parseError("unreadable flag field");
2133 input
= FROM_SELECTION
;
2135 return parseError("unreadable flag field");
2140 /* read command field */
2141 if (listType
== SHELL_CMDS
) {
2142 if (*inPtr
++ != '\n')
2143 return parseError("command must begin with newline");
2144 while (*inPtr
== ' ' || *inPtr
== '\t') /* leading whitespace */
2146 cmdLen
= strcspn(inPtr
, "\n");
2148 return parseError("shell command field is empty");
2149 cmdStr
= XtMalloc(cmdLen
+1);
2150 strncpy(cmdStr
, inPtr
, cmdLen
);
2151 cmdStr
[cmdLen
] = '\0';
2154 cmdStr
= copyMacroToEnd(&inPtr
);
2158 while (*inPtr
== ' ' || *inPtr
== '\t' || *inPtr
== '\n')
2159 inPtr
++; /* skip trailing whitespace & newline */
2161 /* parse the accelerator field */
2162 if (!parseAcceleratorString(accStr
, &modifiers
, &keysym
))
2163 return parseError("couldn't read accelerator field");
2165 /* create a menu item record */
2166 f
= (menuItemRec
*)XtMalloc(sizeof(menuItemRec
));
2169 f
->mnemonic
= mneChar
;
2170 f
->modifiers
= modifiers
;
2173 f
->repInput
= repInput
;
2174 f
->saveFirst
= saveFirst
;
2175 f
->loadAfter
= loadAfter
;
2178 /* add/replace menu record in the list */
2179 for (i
=0; i
< *nItems
; i
++) {
2180 if (!strcmp(menuItems
[i
]->name
, f
->name
)) {
2181 freeMenuItemRec(menuItems
[i
]);
2187 menuItems
[(*nItems
)++] = f
;
2189 /* end of string in proper place */
2195 static int parseError(const char *message
)
2197 fprintf(stderr
, "NEdit: Parse error in user defined menu item, %s\n",
2203 ** Create a text string representing an accelerator for the dialog,
2204 ** the shellCommands or macroCommands resource, and for the menu item.
2206 static void generateAcceleratorString(char *text
, unsigned int modifiers
,
2209 char *shiftStr
= "", *ctrlStr
= "", *altStr
= "";
2210 char *mod2Str
= "", *mod3Str
= "", *mod4Str
= "", *mod5Str
= "";
2212 Modifiers numLockMask
= GetNumLockModMask(TheDisplay
);
2214 /* if there's no accelerator, generate an empty string */
2215 if (keysym
== NoSymbol
) {
2221 /* Translate the modifiers into strings.
2222 Lock and NumLock are always ignored (see util/misc.c),
2223 so we don't display them either. */
2224 if (modifiers
& ShiftMask
)
2225 shiftStr
= "Shift+";
2226 if (modifiers
& ControlMask
)
2228 if (modifiers
& Mod1Mask
)
2230 if ((modifiers
& Mod2Mask
) && (Mod2Mask
!= numLockMask
))
2232 if ((modifiers
& Mod3Mask
) && (Mod3Mask
!= numLockMask
))
2234 if ((modifiers
& Mod4Mask
) && (Mod4Mask
!= numLockMask
))
2236 if ((modifiers
& Mod5Mask
) && (Mod5Mask
!= numLockMask
))
2239 /* for a consistent look to the accelerator names in the menus,
2240 capitalize the first letter of the keysym */
2241 strcpy(keyName
, XKeysymToString(keysym
));
2242 *keyName
= toupper(*keyName
);
2244 /* concatenate the strings together */
2245 sprintf(text
, "%s%s%s%s%s%s%s%s", shiftStr
, ctrlStr
, altStr
,
2246 mod2Str
, mod3Str
, mod4Str
, mod5Str
, keyName
);
2250 ** Create a translation table event description string for the menu
2251 ** XmNaccelerator resource.
2253 static void genAccelEventName(char *text
, unsigned int modifiers
,
2256 char *shiftStr
= "", *lockStr
= "", *ctrlStr
= "", *altStr
= "";
2257 char *mod2Str
= "", *mod3Str
= "", *mod4Str
= "", *mod5Str
= "";
2259 /* if there's no accelerator, generate an empty string */
2260 if (keysym
== NoSymbol
) {
2265 /* translate the modifiers into strings */
2266 if (modifiers
& ShiftMask
)
2267 shiftStr
= "Shift ";
2268 if (modifiers
& LockMask
)
2270 if (modifiers
& ControlMask
)
2272 if (modifiers
& Mod1Mask
)
2274 if (modifiers
& Mod2Mask
)
2276 if (modifiers
& Mod3Mask
)
2278 if (modifiers
& Mod4Mask
)
2280 if (modifiers
& Mod5Mask
)
2283 /* put the modifiers together with the key name */
2284 sprintf(text
, "%s%s%s%s%s%s%s%s<Key>%s",
2285 shiftStr
, lockStr
, ctrlStr
, altStr
,
2286 mod2Str
, mod3Str
, mod4Str
, mod5Str
,
2287 XKeysymToString(keysym
));
2291 ** Read an accelerator name and put it into the form of a modifier mask
2292 ** and a KeySym code. Returns false if string can't be read
2293 ** ... does not handle whitespace in string (look at scanf)
2295 static int parseAcceleratorString(const char *string
, unsigned int *modifiers
,
2298 int i
, nFields
, inputLength
= strlen(string
);
2299 char fields
[10][MAX_ACCEL_LEN
];
2301 /* a blank field means no accelerator */
2302 if (inputLength
== 0) {
2308 /* limit the string length so no field strings will overflow */
2309 if (inputLength
> MAX_ACCEL_LEN
)
2312 /* divide the input into '+' separated fields */
2313 nFields
= sscanf(string
, "%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]",
2314 fields
[0], fields
[1], fields
[2], fields
[3], fields
[4], fields
[5],
2315 fields
[6], fields
[7], fields
[8], fields
[9]);
2319 /* get the key name from the last field and translate it to a keysym.
2320 If the name is capitalized, try it lowercase as well, since some
2321 of the keysyms are "prettied up" by generateAcceleratorString */
2322 *keysym
= XStringToKeysym(fields
[nFields
-1]);
2323 if (*keysym
== NoSymbol
) {
2324 *fields
[nFields
-1] = tolower(*fields
[nFields
-1]);
2325 *keysym
= XStringToKeysym(fields
[nFields
-1]);
2326 if (*keysym
== NoSymbol
)
2330 /* parse the modifier names from the rest of the fields */
2332 for (i
=0; i
<nFields
-1; i
++) {
2333 if (!strcmp(fields
[i
], "Shift"))
2334 *modifiers
|= ShiftMask
;
2335 else if (!strcmp(fields
[i
], "Lock"))
2336 *modifiers
|= LockMask
;
2337 else if (!strcmp(fields
[i
], "Ctrl"))
2338 *modifiers
|= ControlMask
;
2339 /* comparision with "Alt" for compatibility with old .nedit files*/
2340 else if (!strcmp(fields
[i
], "Alt"))
2341 *modifiers
|= Mod1Mask
;
2342 else if (!strcmp(fields
[i
], "Mod2"))
2343 *modifiers
|= Mod2Mask
;
2344 else if (!strcmp(fields
[i
], "Mod3"))
2345 *modifiers
|= Mod3Mask
;
2346 else if (!strcmp(fields
[i
], "Mod4"))
2347 *modifiers
|= Mod4Mask
;
2348 else if (!strcmp(fields
[i
], "Mod5"))
2349 *modifiers
|= Mod5Mask
;
2354 /* all fields successfully parsed */
2359 ** Scan text from "*inPtr" to the end of macro input (matching brace),
2360 ** advancing inPtr, and return macro text as function return value.
2362 ** This is kind of wastefull in that it throws away the compiled macro,
2363 ** to be re-generated from the text as needed, but compile time is
2364 ** negligible for most macros.
2366 static char *copyMacroToEnd(char **inPtr
)
2368 char *retStr
, *errMsg
, *stoppedAt
, *p
, *retPtr
;
2371 /* Skip over whitespace to find make sure there's a beginning brace
2372 to anchor the parse (if not, it will take the whole file) */
2373 *inPtr
+= strspn(*inPtr
, " \t\n");
2374 if (**inPtr
!= '{') {
2375 ParseError(NULL
, *inPtr
, *inPtr
-1, "macro menu item", "expecting '{'");
2379 /* Parse the input */
2380 prog
= ParseMacro(*inPtr
, &errMsg
, &stoppedAt
);
2382 ParseError(NULL
, *inPtr
, stoppedAt
, "macro menu item", errMsg
);
2387 /* Copy and return the body of the macro, stripping outer braces and
2388 extra leading tabs added by the writer routine */
2390 *inPtr
+= strspn(*inPtr
, " \t");
2391 if (**inPtr
== '\n') (*inPtr
)++;
2392 if (**inPtr
== '\t') (*inPtr
)++;
2393 if (**inPtr
== '\t') (*inPtr
)++;
2394 retPtr
= retStr
= XtMalloc(stoppedAt
- *inPtr
+ 1);
2395 for (p
= *inPtr
; p
< stoppedAt
- 1; p
++) {
2396 if (!strncmp(p
, "\n\t\t", 3)) {
2402 if (*(retPtr
-1) == '\t') retPtr
--;
2409 ** If "*string" is not terminated with a newline character, reallocate the
2410 ** string and add one. (The macro language requires newline terminators for
2411 ** statements, but the text widget doesn't force it like the NEdit text buffer
2412 ** does, so this might avoid some confusion.)
2414 static void addTerminatingNewline(char **string
)
2419 length
= strlen(*string
);
2420 if ((*string
)[length
-1] != '\n') {
2421 newString
= XtMalloc(length
+ 2);
2422 strcpy(newString
, *string
);
2423 newString
[length
] = '\n';
2424 newString
[length
+1] = '\0';
2426 *string
= newString
;