1 /* The routines in this file provide menu-related functions under the
2 Microsoft Windows environment on an IBM-PC or compatible computer.
3
4 This module also contains the code for the About and Modes dialog
5 boxes.
6
7 Must be compiled with Borland C++ 2.0 or MSC 6.0 or later versions.
8
9 It should not be compiled if the WINDOW_MSWIN symbol is not set */
10
11 #include "estruct.h"
12 #include "elang.h"
13 #include <stdio.h>
14 #include "eproto.h"
15 #include "edef.h"
16
17 #include "mswin.h"
18 #include "mswmenu.h"
19 #include "mswhelp.h"
20
21 #define MAXMENUTITLE 50
22
23 /* codes for the MenuType parameter from AddMenuEntry () */
24 #define MT_DUMMY 1
25 #define MT_MENUBAR 2
26
27 typedef struct {
28 WORD m_word;
29 ETYPE EPOINTER m_ptr;
30 } MENUTAB;
31
32 #define MB_FNC 0x4000
33 #define MB_BUF 0x8000 /* static IDs should not look like they carry
34 this flag */
35
36 #define MAXDYNMENU 256
37
38 static MENUTAB MenuDynBind [MAXDYNMENU] = {0, NULL};
39
40 static MENUTAB MenuStaticBind [] = {
41 {IDM_NULLPROC, nullproc},
42 {IDM_FILEFIND, filefind},
43 {IDM_VIEWFILE, viewfile},
44 {IDM_INSFILE, insfile},
45 {IDM_FILEREAD, fileread},
46 {IDM_FILENAME, filename},
47 {IDM_FILESAVE, filesave},
48 {IDM_FILEWRITE, filewrite},
49 {IDM_FILEAPP, fileapp},
50 {IDM_SETEKEY, setekey},
51 {IDM_NEXTBUFFER, nextbuffer},
52 {IDM_USEBUFFER, usebuffer},
53 {IDM_UNMARK, unmark},
54 {IDM_NAMEBUFFER, namebuffer},
55 {IDM_KILLBUFFER, killbuffer},
56 {IDM_NARROW, narrow},
57 {IDM_WIDEN, widen},
58 {IDM_LISTBUFFERS, listbuffers},
59 {IDM_SPLITWIND, splitwind},
60 {IDM_DELWIND, delwind},
61 {IDM_ONLYWIND, onlywind},
62 {IDM_NEXTWIND, nextwind},
63 {IDM_PREVWIND, prevwind},
64 {IDM_MVUPWIND, mvupwind},
65 {IDM_MVDNWIND, mvdnwind},
66 {IDM_NEXTUP, nextup},
67 {IDM_NEXTDOWN, nextdown},
68 {IDM_ENLARGEWIND, enlargewind},
69 {IDM_SHRINKWIND, shrinkwind},
70 {IDM_RESIZE, resize},
71 {IDM_QUICKEXIT, quickexit},
72 {IDM_QUIT, quit},
73 /* here just to compute the key binding: the actual IDM_QUIT
74 selection is processed as a special */
75 {IDM_CUTREGION, cutregion},
76 {IDM_CLIPREGION, clipregion},
77 {IDM_INSERTCLIP, insertclip},
78 {IDM_SETMARK, setmark},
79 {IDM_REMMARK, remmark},
80 {IDM_SWAPMARK, swapmark},
81 {IDM_YANK, yank},
82 {IDM_KILLREGION, killregion},
83 {IDM_COPYREGION, copyregion},
84 {IDM_UPPERREGION, upperregion},
85 {IDM_LOWERREGION, lowerregion},
86 {IDM_ENTAB, entab},
87 {IDM_DETAB, detab},
88 {IDM_TRIM, trim},
89 {IDM_INDENT_REGION, indent_region},
90 {IDM_UNDENT_REGION, undent_region},
91 {IDM_WORDCOUNT, wordcount},
92 {IDM_FILLPARA, fillpara},
93 {IDM_KILLPARA, killpara},
94 {IDM_KILLTEXT, killtext},
95 {IDM_OPENLINE, openline},
96 {IDM_DELFWORD, delfword},
97 {IDM_DELBWORD, delbword},
98 {IDM_CAPWORD, capword},
99 {IDM_LOWERWORD, lowerword},
100 {IDM_UPPERWORD, upperword},
101 {IDM_DEBLANK, deblank},
102 {IDM_TWIDDLE, twiddle},
103 {IDM_TAB, tab},
104 {IDM_QUOTE, quote},
105 {IDM_FORWSEARCH, forwsearch},
106 {IDM_BACKSEARCH, backsearch},
107 {IDM_FORWHUNT, forwhunt},
108 {IDM_BACKHUNT, backhunt},
109 {IDM_FISEARCH, fisearch},
110 {IDM_RISEARCH, risearch},
111 {IDM_SREPLACE, sreplace},
112 {IDM_QREPLACE, qreplace},
113 {IDM_GOTOMARK, gotomark},
114 {IDM_GOTOLINE, gotoline},
115 {IDM_GETFENCE, getfence},
116 {IDM_GOTOBOB, gotobob},
117 {IDM_GOTOEOB, gotoeob},
118 {IDM_FORWPAGE, forwpage},
119 {IDM_BACKPAGE, backpage},
120 {IDM_GOTOEOP, gotoeop},
121 {IDM_GOTOBOP, gotobop},
122 {IDM_FORWLINE, forwline},
123 {IDM_BACKLINE, backline},
124 {IDM_GOTOBOL, gotobol},
125 {IDM_GOTOEOL, gotoeol},
126 {IDM_FORWWORD, forwword},
127 {IDM_BACKWORD, backword},
128 {IDM_ENDWORD, endword},
129 {IDM_EXECPRG, execprg},
130 {IDM_SPAWNCLI, spawncli},
131 {IDM_SPAWN, spawn},
132 {IDM_PIPECMD, pipecmd},
133 {IDM_FILTER, filter},
134 {IDM_CTLXE, ctlxe},
135 {IDM_CTLXLP, ctlxlp},
136 {IDM_CTLXRP, ctlxrp},
137 {IDM_NAMEDCMD, namedcmd},
138 {IDM_EXECCMD, execcmd},
139 {IDM_EXECPROC, execproc},
140 {IDM_EXECBUF, execbuf},
141 {IDM_EXECFILE, execfile},
142 {IDM_BINDTOKEY, bindtokey},
143 {IDM_MACROTOKEY, macrotokey},
144 {IDM_BINDTOMENU, bindtomenu},
145 {IDM_MACROTOMENU, macrotomenu},
146 {IDM_UNBINDKEY, unbindkey},
147 {IDM_UNBINDMENU, unbindmenu},
148 {IDM_DESKEY, deskey},
149 {IDM_SETVAR, setvar},
150 {IDM_DISPVAR, dispvar},
151 {IDM_DESVARS, desvars},
152 {IDM_SHOWCPOS, showcpos},
153 {IDM_CTRLG, ctrlg},
154 {IDM_FIND_SCREEN, find_screen},
155 {IDM_RENAME_SCREEN, rename_screen},
156 {IDM_NEWSIZE, newsize},
157 {IDM_NEWWIDTH, newwidth},
158 {IDM_DESBIND, desbind},
159 {IDM_DESFUNC, desfunc},
160 {IDM_APRO, apro},
161 {0, NULL}
162 };
163
164 #define MAXMENUPARENT 7
165
166 typedef struct { /* current menu description */
167 int cm_pos; /* entry position */
168 int cm_x; /* index of the last parent handle, -1 if entry
169 located in main menu bar */
170 HMENU cm_parent[MAXMENUPARENT];
171 /* parent handle, main popup menu at index 0 */
172 } CURMENU;
173
174 static CURMENU CurrentMenu = {-1, -1, {NULL}};
175
176 static unsigned int ctrlx_key = 0;
177 static unsigned int meta_key = 0; /* for GetKeyText */
178
179 /* execmenu: execute a function bound to a menu */
180 /* ======== */
181
182 /* This function is called by the edit loop in main.c, when a MENU
183 extended character is detected. */
184
execmenu(int f,int n)185 int PASCAL execmenu (int f, int n)
186 /* f, n: arguments to target function */
187 {
188 register UINT ID;
189 register MENUTAB *MTp;
190
191 ID = (xpos << 8) + ypos; /* get_key sees MENU sequences as MOUS */
192 if (ID >= IDM_DYNAMIC) {
193 MTp = &MenuDynBind[ID - IDM_DYNAMIC];
194 if (MTp->m_word & MB_FNC) return (*(MTp->m_ptr.fp)) (f, n);
195 else if (MTp->m_word & MB_BUF) {
196 BOOL status;
197
198 if (f == FALSE) n = 1;
199 while (n--) {
200 status = dobuf (MTp->m_ptr.buf);
201 if (status != TRUE) return status;
202 }
203 }
204 else return FAILD;
205 return TRUE;
206 }
207 else for (MTp = &MenuStaticBind[0]; MTp->m_word != 0; MTp++) {
208 if (ID == MTp->m_word) return (*(MTp->m_ptr.fp)) (f, n);
209 }
210 return FAILD; /* not found !!! */
211 } /* execmenu */
212
213 /* GenerateMenuSeq: send a menu sequence into the input stream */
214 /* =============== */
215
GenerateMenuSeq(UINT ID)216 void FAR PASCAL GenerateMenuSeq (UINT ID)
217 {
218 if (!in_room (5)) return;
219 in_put (0); /* escape indicator */
220 in_put (MENU >> 8); /* MENU prefix */
221 in_put (ID >> 8); /* menu ID high byte (--> xpos) */
222 in_put (ID & 0xFF); /* menu ID low byte (--> ypos) */
223 in_put ('m'); /* dummy event */
224 } /* GenerateMenuSeq */
225
226 /* AboutDlgProc: About box dialog function */
227 /* ============ */
AboutDlgProc(HWND hDlg,UINT wMsg,UINT wParam,LONG lParam)228 int EXPORT FAR PASCAL AboutDlgProc (HWND hDlg, UINT wMsg, UINT wParam,
229 LONG lParam)
230 {
231 char s [50];
232 static RECT FullBox;
233
234 switch (wMsg) {
235
236 case WM_INITDIALOG:
237 strcpy (s, PROGNAME);
238 strcat (s, " ");
239 strcat (s, VERSION);
240 SetDlgItemText (hDlg, ID_PROGVER, s);
241 {
242 RECT Bar;
243
244 GetWindowRect (hDlg, &FullBox);
245 GetWindowRect (GetDlgItem (hDlg, ID_ABOUTBAR), &Bar);
246 MoveWindow (hDlg, FullBox.left, FullBox.top,
247 Bar.left + GetSystemMetrics (SM_CXDLGFRAME)
248 - FullBox.left,
249 FullBox.bottom - FullBox.top,
250 FALSE); /* shrink dialog box, do not repaint */
251 }
252 return TRUE;
253
254 case WM_COMMAND:
255 if (NOTIFICATION_CODE != BN_CLICKED) break;
256 if (LOWORD(wParam) == 1) {
257 EndDialog (hDlg, 0);
258 return TRUE;
259 }
260 else if (LOWORD(wParam) == ID_MOREABOUT) {
261 EnableWindow ((HWND)lParam, FALSE); /* disable "More >>" */
262 SetFocus (GetDlgItem (hDlg, 1)); /* make "OK" default */
263 MoveWindow (hDlg, FullBox.left, FullBox.top,
264 FullBox.right - FullBox.left,
265 FullBox.bottom - FullBox.top,
266 TRUE); /* enlarge dialog box and repaint */
267 return TRUE;
268 }
269 break;
270 default:
271 return FALSE;
272 }
273 return FALSE;
274 } /* AboutDlgProc */
275
276 /* SetCheck: puts a check mark in a check box */
277 /* ======== */
278
SetCheck(HWND hDlg,int BoxID)279 void PASCAL SetCheck (HWND hDlg, int BoxID)
280 {
281 SendMessage (GetDlgItem (hDlg, BoxID), BM_SETCHECK, 1, 0L);
282 } /* SetCheck */
283
284 /* GetCheck: TRUE is the check box is checked */
285 /* ======== */
286
GetCheck(HWND hDlg,int BoxID)287 BOOL PASCAL GetCheck (HWND hDlg, int BoxID)
288 {
289 return (SendMessage (GetDlgItem (hDlg, BoxID), BM_GETCHECK, 0, 0L) != 0);
290 } /* GetCheck */
291
292 /* ModeDlgProc: Modes dialog box function */
293 /* =========== */
294
295 /* must be invoked through DialogBoxParam, with LOWORD(dwInitParam) set
296 to TRUE for global modes and FALSE for current buffer modes */
297
ModeDlgProc(HWND hDlg,UINT wMsg,UINT wParam,LONG lParam)298 int EXPORT FAR PASCAL ModeDlgProc (HWND hDlg, UINT wMsg, UINT wParam,
299 LONG lParam)
300 {
301 char s[40+NBUFN];
302 static int *modep; /* mode flags pointer */
303
304 switch (wMsg) {
305
306 case WM_INITDIALOG:
307 if (LOWORD(lParam)) {
308 strcpy (s, TEXT331); /* "Global modes" */
309 modep = &gmode;
310 }
311 else {
312 strcpy (s, TEXT332); /* "Modes for buffer: " */
313 strcat (s, curbp->b_bname);
314 modep = &(curbp->b_mode);
315 }
316 SetWindowText (hDlg, s);
317 if (*modep & MDWRAP) SetCheck (hDlg, ID_WRAP);
318 if (*modep & MDCMOD) SetCheck (hDlg, ID_CMODE);
319 if (*modep & MDEXACT) SetCheck (hDlg, ID_EXACT);
320 if (*modep & MDVIEW) SetCheck (hDlg, ID_VIEW);
321 if (*modep & MDOVER) SetCheck (hDlg, ID_OVER);
322 if (*modep & MDREPL) SetCheck (hDlg, ID_REP);
323 if (*modep & MDMAGIC) SetCheck (hDlg, ID_MAGIC);
324 if (*modep & MDCRYPT) SetCheck (hDlg, ID_CRYPT);
325 if (*modep & MDASAVE) SetCheck (hDlg, ID_ASAVE);
326 return TRUE;
327
328 case WM_COMMAND:
329 if (NOTIFICATION_CODE != BN_CLICKED) break;
330 switch (LOWORD(wParam)) {
331
332 case 1: /* OK */
333 *modep = 0;
334 if (GetCheck (hDlg, ID_WRAP)) *modep |= MDWRAP;
335 if (GetCheck (hDlg, ID_CMODE)) *modep |= MDCMOD;
336 if (GetCheck (hDlg, ID_EXACT)) *modep |= MDEXACT;
337 if (GetCheck (hDlg, ID_VIEW)) *modep |= MDVIEW;
338 if (GetCheck (hDlg, ID_OVER)) *modep |= MDOVER;
339 if (GetCheck (hDlg, ID_REP)) *modep |= MDREPL;
340 if (GetCheck (hDlg, ID_MAGIC)) *modep |= MDMAGIC;
341 if (GetCheck (hDlg, ID_CRYPT)) *modep |= MDCRYPT;
342 if (GetCheck (hDlg, ID_ASAVE)) *modep |= MDASAVE;
343 EndDialog (hDlg, 0);
344 break;
345
346 case 2: /* Cancel */
347 EndDialog (hDlg, -1);
348 break;
349
350 case ID_OVER:
351 /* if OVER set, clear the REP mode check-box */
352 if (GetCheck (hDlg, ID_OVER)) {
353 SendMessage (GetDlgItem (hDlg, ID_REP), BM_SETCHECK, 0, 0L);
354 }
355 break;
356
357 case ID_REP:
358 /* if REP set, clear the OVER mode check-box */
359 if (GetCheck (hDlg, ID_REP)) {
360 SendMessage (GetDlgItem (hDlg, ID_OVER), BM_SETCHECK, 0, 0L);
361 }
362 break;
363
364 }
365 break;
366
367 default:
368 return FALSE;
369 }
370 return FALSE;
371 } /* ModeDlgProc */
372
373 /* IsMenuSeparator: TRUE if the item is a separator */
374 /* =============== */
IsMenuSeparator(HMENU hMenu,int Position)375 static BOOL PASCAL IsMenuSeparator (HMENU hMenu, int Position)
376 {
377 DWORD state;
378
379 if (GetSubMenu (hMenu, Position) != 0) return FALSE;
380 if ((state = GetMenuState (hMenu, Position, MF_BYPOSITION)) != -1) {
381 if (state & MF_SEPARATOR) return TRUE;
382 }
383 return FALSE;
384 } /* IsMenuSeparator */
385
386 /* GetMenuEntryID: give back ID of MenuItem or popup */
387 /* ============== */
388
389 /* if the entry is a menu item (not a popup), its ID is returned. If the
390 entry is a popup, the ID of the first entry in this popup is
391 returned, added to n*IDM_POPUP where n is the number of popup levels.
392
393 If the entry is neither an item nor a popup, an item id of 0 is
394 returned */
395
GetMenuEntryID(HMENU hMenu,int Position)396 UINT PASCAL GetMenuEntryID (HMENU hMenu, int Position)
397 {
398 UINT id;
399 HMENU hSubMenu;
400
401 if (IsMenuSeparator (hMenu, Position)) {
402 id = 0;
403 }
404 else if ((hSubMenu = GetSubMenu (hMenu, Position)) != 0) {
405 id = IDM_POPUP + GetMenuEntryID (hSubMenu, 0);
406 }
407 else id = GetMenuItemID (hMenu, Position);
408 return id;
409 } /* GetMenuEntryID */
410
411 /* FindKeyBinding: scan the key binding table, return first match */
412 /* ============== */
413
414 /* If there is no match, the returned value point to the BINDNUL entry
415 */
416
FindKeyBinding(void * Func)417 KEYTAB * FAR PASCAL FindKeyBinding (void *Func)
418 {
419 register KEYTAB *KTp;
420
421 for (KTp = &keytab[0]; KTp->k_type != BINDNUL; ++KTp) {
422 if (((KTp->k_type == BINDFNC) && (KTp->k_ptr.fp == Func)) ||
423 ((KTp->k_type == BINDBUF) && (KTp->k_ptr.buf == Func)))
424 if (!(KTp->k_code & MOUS)) break; /* found it! (we skip
425 mouse bindings) */
426 }
427 return KTp;
428
429 } /* FindKeyBinding */
430
431 /* GetKeyText: translates a key code into a CUA-type description */
432 /* ========== */
433
434 /* Returns the length (excluding the terminating NULL) of the text if it
435 fits in the supplied buffer, 0 otherwise (in which case the buffer
436 contents are unuseable). */
437
438 /* This function uses the ctrlx_key and meta_key static variables to
439 avoid scanning the key binding table too often. If these are NULL,
440 however, it looks them up. Therefore, these variables can be zeroed
441 if there is a doubt about their validity */
442
GetKeyText(int Key,char * Text,int TextLength)443 int PASCAL GetKeyText (int Key, char *Text, int TextLength)
444 {
445 int i; /* index in Text */
446 char c;
447
448 if (Key & (CTLX | META)) {
449 unsigned *prefix_key_ptr;
450 int n;
451
452 prefix_key_ptr = (Key & CTLX) ? &ctrlx_key : &meta_key;
453 if (*prefix_key_ptr == 0) {
454 KEYTAB *KTp;
455
456 KTp = FindKeyBinding ((Key & CTLX) ? cex : meta);
457 if (KTp->k_type == BINDNUL) return 0;
458 *prefix_key_ptr = KTp->k_code;
459 }
460 #define NBSPACES 3 /* # of spaces after prefix */
461 i = GetKeyText (*prefix_key_ptr, Text, TextLength - NBSPACES);
462 if (i == 0) return 0;
463 for (n = 0; n < NBSPACES; ++n) Text[i++] = ' ';
464 }
465 else {
466 i = 0;
467 }
468
469 #define APPENDTEXT(s) {if (TextLength - i < sizeof(s)) return 0;\
470 strcpy (&Text[i], s); i += sizeof(s) - 1;}
471
472 c = (char)Key;
473
474 if (Key & ALTD) APPENDTEXT(TEXT310) /* "Alt+" */
475
476 if (Key & SHFT) APPENDTEXT(TEXT311) /* "Shift+" */
477
478 if (Key & CTRL) {
479 switch (c) {
480 case 'H':
481 APPENDTEXT(TEXT312) /* "BkSp" */
482 goto all_done;
483 case 'I':
484 APPENDTEXT(TEXT313) /* "Tab" */
485 goto all_done;
486 case 'M':
487 APPENDTEXT(TEXT314) /* "Enter" */
488 goto all_done;
489 case '[':
490 APPENDTEXT(TEXT315) /* "Esc" */
491 goto all_done;
492 default:
493 APPENDTEXT(TEXT316) /* "Ctrl+" */
494 break;
495 }
496 }
497
498 if (Key & SPEC) {
499 switch (c) {
500 case '<':
501 APPENDTEXT(TEXT317) /* "Home" */
502 break;
503 case 'N':
504 APPENDTEXT(TEXT318) /* "DownArrow" */
505 break;
506 case 'P':
507 APPENDTEXT(TEXT319) /* "UpArrow" */
508 break;
509 case 'B':
510 APPENDTEXT(TEXT320) /* "LeftArrow" */
511 break;
512 case 'F':
513 APPENDTEXT(TEXT321) /* "RightArrow" */
514 break;
515 case '>':
516 APPENDTEXT(TEXT322) /* "End" */
517 break;
518 case 'Z':
519 APPENDTEXT(TEXT323) /* "PageUp" */
520 break;
521 case 'V':
522 APPENDTEXT(TEXT324) /* "PageDown" */
523 break;
524 case 'C':
525 APPENDTEXT(TEXT325) /* "Ins" */
526 break;
527 case 'D':
528 APPENDTEXT(TEXT326) /* "Del" */
529 break;
530 case '0':
531 APPENDTEXT(TEXT327) /* "F10" */
532 break;
533 default: /* single digit function key */
534 if ((c < '1') || (c > '9')) return 0; /* unknown! */
535 if (TextLength - i < 3) return 0;
536 Text[i++] = CHAR328; /* 'F' */
537 Text[i++] = c;
538 break;
539 }
540 }
541 else if (c == ' ') APPENDTEXT(TEXT329) /* "SpaceBar" */
542 else {
543 /* normal char */
544 if (TextLength - i < 2) return 0;
545 Text[i++] = c;
546 }
547 all_done:
548 Text[i] = '\0';
549 return i;
550 } /* GetKeyText */
551
552 /* UpdateMenuItemText: sets the key binding info in a menu item */
553 /* ================== */
554
UpdateMenuItemText(HMENU hMenu,int Position,MENUTAB * MTp)555 void PASCAL UpdateMenuItemText (HMENU hMenu, int Position,
556 MENUTAB *MTp)
557 {
558 KEYTAB *KTp;
559 char NewText[MAXMENUTITLE];
560 char OldText[MAXMENUTITLE];
561 register int i;
562
563 GetMenuString (hMenu, Position, OldText, MAXMENUTITLE, MF_BYPOSITION);
564 strcpy (NewText, OldText);
565 for (i = 0; (NewText[i] != '\0') && (NewText[i] != '\t'); ++i) ;
566 /* find the first tab char or the string's end */
567
568 KTp = FindKeyBinding (MTp->m_word & MB_BUF ? (void*)MTp->m_ptr.buf :
569 (void*)MTp->m_ptr.fp);
570 if (KTp->k_type != BINDNUL) {
571 NewText[i] = '\t';
572 if (!GetKeyText (KTp->k_code, &NewText[i+1], MAXMENUTITLE + 1 - i))
573 goto no_binding; /* if out of room, no binding text */
574 }
575 else {
576 no_binding:
577 /* let's erase the binding text */
578 NewText[i] = '\0';
579 }
580 if (strcmp (NewText, OldText) == 0) return;
581 ModifyMenu (hMenu, Position, MF_BYPOSITION | MF_STRING,
582 GetMenuItemID (hMenu, Position), NewText);
583 } /* UpdateMenuItemText */
584
585 /* InitMenuPopup: perform popup menu init before display (WM_INITMENUPOPUP) */
586 /* ============= */
587
588 /* here, we may gray menu entries that are invalid. We also try to
589 display a key binding after each menu item */
590
InitMenuPopup(HMENU hMenu,LONG lParam)591 void FAR PASCAL InitMenuPopup (HMENU hMenu, LONG lParam)
592 {
593 int Position;
594 int ItemCount;
595 BOOL Enable;
596 UINT EntryID;
597 register MENUTAB *MTp; /* points the appropriate entry in MenuStaticBind */
598 MENUTAB *PrevMTp; /* to control MenuStaticBind scanning */
599
600 if (HIWORD(lParam)) return; /* do not process the system menu */
601
602 ctrlx_key = meta_key = 0; /* these may have changed since the
603 last call to GetKeyText */
604
605 /*-Scan the menu's items */
606 ItemCount = GetMenuItemCount (hMenu);
607 PrevMTp = &MenuStaticBind[0];
608 for (Position = 0; Position < ItemCount; Position++) {
609 EntryID = GetMenuEntryID (hMenu, Position);
610
611 if (EntryID == 0) continue; /* skip separators */
612
613 /*-lookup the item in the menu binding table */
614 /* since we assume the items' order follows the binding table,
615 we try to optimize the lookup by starting from the last
616 found entry */
617 if (EntryID >= IDM_POPUP) {
618 /* for popup menus MTp yields the nullproc entry */
619 MTp = &MenuStaticBind[0];
620 }
621 else if (EntryID >= IDM_DYNAMIC) {
622 /* for dynamic items, MTp is directly set to the proper
623 entry in the MenuDynBind */
624 MTp = &MenuDynBind[EntryID - IDM_DYNAMIC];
625 }
626 else {
627 MTp = PrevMTp + 1;
628 for (;;) {
629 if (MTp->m_word == EntryID) {
630 /* found it! */
631 PrevMTp = MTp; /* prepare for next item */
632 break;
633 }
634 if (MTp == PrevMTp) {
635 /* scan completed, item not found! */
636 MTp = &MenuStaticBind[0]; /* use default */
637 break;
638 }
639 if (MTp->m_word == 0) {
640 MTp = &MenuStaticBind[0]; /* cycle through */
641 }
642 else ++MTp;
643 }
644 }
645
646 /*-for each item found in the binding table, display a key binding */
647 if (MTp != &MenuStaticBind[0]) UpdateMenuItemText (hMenu, Position, MTp);
648
649 /*-if not quiescent, gray most items */
650 if (notquiescent) {
651 switch (EntryID) {
652 #ifdef IDM_DEBUG
653 case IDM_DEBUG:
654 #endif
655 case IDM_QUIT:
656 case IDM_CTRLG:
657 case IDM_ABOUT:
658 case IDM_WHELPINDEX:
659 case IDM_WHELPKEYBOARD:
660 case IDM_WHELPCOMMANDS:
661 case IDM_WHELPPROCEDURES:
662 Enable = TRUE;
663 break;
664 default:
665 Enable = FALSE;
666 break;
667 }
668 }
669 else {
670 /*-if quiescent, gray the invalid items, depending on
671 appropriate conditions */
672 Enable = TRUE;
673 switch (EntryID) {
674 case IDM_FILESAVE:
675 case IDM_UNMARK:
676 /* grayed if current buffer is unmarked */
677 if (!(curbp->b_flag & BFCHG)) Enable = FALSE;
678 break;
679 case IDM_NEXTBUFFER:
680 case IDM_USEBUFFER:
681 case IDM_KILLBUFFER:
682 /* grayed if sole visible buffer */
683 if (getdefb () == NULL) Enable = FALSE;
684 break;
685 case IDM_NARROW:
686 /* grayed if already narrowed */
687 if (curbp->b_flag & BFNAROW) Enable = FALSE;
688 /* and if no mark... */
689 case IDM_CUTREGION:
690 case IDM_CLIPREGION:
691 case IDM_EDIT_REGION_POPUP:
692 /* grayed if no mark0 in current window */
693 if (curwp->w_markp[0] == NULL) Enable = FALSE;
694 break;
695 case IDM_REMMARK:
696 case IDM_SWAPMARK:
697 case IDM_GOTOMARK:
698 /* grayed if no mark at all in current window */
699 {
700 int i;
701 Enable = FALSE;
702 for (i = 0; i <= 9; i++) if (curwp->w_markp[i]) {
703 Enable = TRUE;
704 break;
705 }
706 }
707 break;
708 case IDM_WIDEN:
709 /* grayed if not narrowed */
710 if (!(curbp->b_flag & BFNAROW)) Enable = FALSE;
711 break;
712 case IDM_DELWIND:
713 case IDM_ONLYWIND:
714 case IDM_NEXTWIND:
715 case IDM_PREVWIND:
716 case IDM_NEXTUP:
717 case IDM_NEXTDOWN:
718 case IDM_FILE_WINDOW_SIZE_POPUP:
719 /* grayed if only one window in current screen */
720 if (wheadp->w_wndp == NULL) Enable = FALSE;
721 break;
722 case IDM_EDIT_CLIPBOARD_POPUP:
723 /* grayed if clipboard empty and no mark set */
724 if ((curwp->w_markp[0] == NULL) &&
725 !IsClipboardFormatAvailable (CF_TEXT)) Enable = FALSE;
726 break;
727 case IDM_INSERTCLIP:
728 /* grayed if clipboard empty */
729 if (!IsClipboardFormatAvailable (CF_TEXT)) Enable = FALSE;
730 break;
731 case IDM_YANK:
732 /* grayed if no kill buffer */
733 if (kbufh[kill_index] == NULL) Enable = FALSE;
734 break;
735 case IDM_FORWHUNT:
736 case IDM_BACKHUNT:
737 /* grayed if no search string */
738 if (pat[0] == '\0') Enable = FALSE;
739 break;
740 case IDM_CTLXE:
741 /* grayed if no keyboard macro */
742 break;
743 case IDM_TILE_VERTICALLY:
744 if (!Win31API) Enable = FALSE;
745 break;
746 }
747
748 /*-If view mode, gray the items flagged IDM_NOTVIEW */
749 if (curbp->b_mode & MDVIEW) {
750 if (!(EntryID & (IDM_DYNAMIC | IDM_POPUP)) &&
751 (MTp->m_word & IDM_NOTVIEW)) Enable = FALSE;
752 }
753 }
754
755 EnableMenuItem (hMenu, Position,
756 MF_BYPOSITION | (Enable ? MF_ENABLED : MF_GRAYED));
757 }
758 } /* InitMenuPopup */
759
760 /* SimulateExtendedKey: feed an extended key into the input stream */
761 /* =================== */
762
SimulateExtendedKey(int ec)763 static void PASCAL SimulateExtendedKey (int ec)
764 {
765 char prefix;
766
767 if (in_room(5)) {
768 prefix = ec >> 8;
769 if (prefix != 0) {
770 in_put (0);
771 in_put (prefix);
772 if (prefix & (MOUS >> 8)) {
773 /* in case the key is a mouse action, supply
774 dummy mouse position info */
775 in_put (1);
776 in_put (1);
777 }
778 }
779 in_put (ec & 0xFF);
780 }
781 } /* SimulateExtendedKey */
782
783 /* MenuCommand: WM_COMMAND message handling */
784 /* =========== */
785
786 /* returns TRUE if the command has been recognized and FALSE otherwise
787 */
788
MenuCommand(UINT wParam,LONG lParam)789 BOOL FAR PASCAL MenuCommand (UINT wParam, LONG lParam)
790 {
791 FARPROC ProcInstance;
792 DWORD HelpContext;
793
794 switch (LOWORD(wParam)) {
795 /* the menu choices from here down to the default statement are
796 valid even in the not-quiescent case */
797
798 case IDM_ABOUT:
799 ProcInstance = MakeProcInstance ((FARPROC)AboutDlgProc,
800 hEmacsInstance);
801 DialogBox (hEmacsInstance, "ABOUT", hFrameWnd, ProcInstance);
802 FreeProcInstance (ProcInstance);
803 break;
804
805 #ifdef IDM_DEBUG
806 case IDM_DEBUG: /* places a call to the debugger */
807 DebugBreak ();
808 break;
809 #endif
810
811 case IDM_QUIT:
812 PostMessage (hFrameWnd, WM_CLOSE, 0, 0L);
813 break;
814
815 case IDM_CTRLG:
816 /* this is a special case: we feed the abort key into the input
817 stream */
818 SimulateExtendedKey (abortc);
819 break;
820
821 case IDM_WHELPINDEX:
822 HelpContext = 0;
823 goto InvokeHelp;
824 case IDM_WHELPKEYBOARD:
825 HelpContext = HELPID_KEYBOARD;
826 goto InvokeHelp;
827 case IDM_WHELPCOMMANDS:
828 HelpContext = HELPID_COMMANDS;
829 goto InvokeHelp;
830 case IDM_WHELPPROCEDURES:
831 HelpContext = HELPID_PROCEDURES;
832 InvokeHelp:
833 WinHelp (hFrameWnd, MainHelpFile,
834 HelpContext ? HELP_CONTEXT : HELP_INDEX, HelpContext);
835 MainHelpUsed = TRUE;
836 break;
837
838 default:
839 if (notquiescent) return TRUE; /* abort processing */
840
841 /* the following IDs are processed only if emacs is quiescent */
842 switch (LOWORD(wParam)) {
843
844 case IDM_GLOBMODE:
845 case IDM_MODE:
846 ProcInstance = MakeProcInstance ((FARPROC)ModeDlgProc,
847 hEmacsInstance);
848 DialogBoxParam (hEmacsInstance, "MODES",
849 hFrameWnd, ProcInstance,
850 (DWORD)(wParam == IDM_GLOBMODE));
851 FreeProcInstance (ProcInstance);
852 if (wParam = IDM_MODE) {
853 upmode ();
854 update (FALSE);
855 }
856 break;
857
858 case IDM_FONT:
859 PickEmacsFont ();
860 break;
861
862 case IDM_CASCADE:
863 SendMessage (hMDIClientWnd, WM_MDICASCADE, 0, 0L);
864 break;
865 case IDM_TILE_HORIZONTALLY:
866 SendMessage (hMDIClientWnd, WM_MDITILE, MDITILE_VERTICAL, 0L);
867 break;
868 case IDM_TILE_VERTICALLY:
869 SendMessage (hMDIClientWnd, WM_MDITILE, MDITILE_HORIZONTAL, 0L);
870 break;
871 case IDM_ARRANGEICONS:
872 SendMessage (hMDIClientWnd, WM_MDIICONARRANGE, 0, 0L);
873 break;
874
875 case IDM_NORMALIZE:
876 {
877 SCREEN *sp;
878
879 sp = (SCREEN*)GetWindowLong ((HWND)(SendMessage (hMDIClientWnd,
880 WM_MDIGETACTIVE, 0, 0L)),
881 GWL_SCRPTR);
882 newsize (TRUE, sp->s_nrow);
883 newwidth (TRUE, sp->s_ncol);
884 update (FALSE);
885 }
886 break;
887
888 default:
889 if (wParam >= IDM_FIRSTCHILD) return FALSE;
890 GenerateMenuSeq (wParam);
891 return TRUE;
892 }
893 }
894
895 /* we have processed an internal menu command */
896 if (!notquiescent) GenerateMenuSeq (IDM_NULLPROC);
897 /* this flushes a possible numeric argument */
898 return TRUE;
899 } /* MenuCommand */
900
901 /* GetScreenMenuHandle: returns the handle to the 'Screen' menu (for MDI mgt) */
902 /* =================== */
903
GetScreenMenuHandle(void)904 HMENU FAR PASCAL GetScreenMenuHandle (void)
905 {
906 HMENU hBaseMenu;
907 int Pos;
908
909 hBaseMenu = GetMenu (hFrameWnd);
910 for (Pos = GetMenuItemCount (hBaseMenu) - 1; Pos >= 0; Pos--) {
911 if (GetMenuEntryID (hBaseMenu, Pos) == IDM_SCREEN_POPUP) {
912 return GetSubMenu (hBaseMenu, Pos);
913 }
914 }
915 return NULL;
916 } /* GetScreenMenuHandle */
917
918 /* MenuEntryCount: count of menu entries, excluding the MDI buttons */
919 /* ============== */
920
MenuEntryCount(HMENU hMenu)921 static int PASCAL MenuEntryCount (HMENU hMenu)
922 {
923 int Count;
924
925 Count = GetMenuItemCount (hMenu);
926 if (hMenu == GetMenu (hFrameWnd)) {
927 #if WINDOW_MSWIN32
928 if (GetWindowLong((HWND)SendMessage (hMDIClientWnd, WM_MDIGETACTIVE,
929 0, 0L), GWL_STYLE) &
930 WS_MAXIMIZE) {
931 #else
932 if (HIWORD(SendMessage (hMDIClientWnd, WM_MDIGETACTIVE, 0, 0L))) {
933 #endif
934 /* an MDI child is maximized ==> we have MDI buttons in the
935 menu bar */
936 Count -= 2;
937 }
938 }
939 return Count;
940 } /* MenuEntryCount */
941
942 /* MenuEntryOffset: position of the first non-MDI button menu entry */
943 /* =============== */
944
945 static int PASCAL MenuEntryOffset (HMENU hMenu)
946 {
947 if (hMenu == GetMenu (hFrameWnd)) {
948 #if WINDOW_MSWIN32
949 if (GetWindowLong((HWND)SendMessage (hMDIClientWnd, WM_MDIGETACTIVE,
950 0, 0L), GWL_STYLE) &
951 WS_MAXIMIZE) {
952 #else
953 if (HIWORD(SendMessage (hMDIClientWnd, WM_MDIGETACTIVE, 0, 0L))) {
954 #endif
955 /* an MDI child is maximized ==> we have MDI buttons in the
956 menu bar */
957 return 1;
958 }
959 }
960 return 0;
961
962 } /* MenuEntryOffset */
963
964 /* ParseMenu: parse a piece of menu path */
965 /* ========= */
966
967 static BOOL PASCAL ParseMenu (char *Name, char *Title, int *Posp)
968
969 /* Puts the text part of the menu name in Title (at most MAXMENUTITLE
970 characters including the \0) and the position number in *Posp. If no
971 position is specified, *Posp is set to -1. The returned BOOL is TRUE
972 if no error occured and FAILD if a syntax problem was diagnosed */
973 {
974 register int i;
975
976 *Posp = -1;
977 for (i = 0; Name[i] != '\0'; i++) {
978 if (i >= MAXMENUTITLE - 1) {
979 mlwrite (TEXT300); /* "[Incorrect menu]" */
980 return FAILD;
981 }
982 if (Name[i] == '>') break;
983 if (Name[i] == '@') { /* must parse a position index */
984 unsigned char *s;
985
986 *Posp = 0;
987 for (s = &Name[i+1]; (*s != '>') && (*s != '\0'); s++) {
988 if ((*s >= '0') && (*s <= '9')) {
989 *Posp = (*Posp * 10) + (*s - '0');
990 }
991 else { /* non numerical character! */
992 mlwrite (TEXT300); /* "[Incorrect menu]" */
993 return FAILD;
994 }
995 }
996 break;
997 }
998 Title[i] = Name[i];
999 }
1000 Title[i] = '\0';
1001 return TRUE;
1002 } /* ParseMenu */
1003
1004 /* LocateMenu: locate a menu entry matching a piece of menu path */
1005 /* ========== */
1006
1007 static BOOL PASCAL LocateMenu (char *Name, CURMENU *CM)
1008
1009 /* The returned BOOL is TRUE if a matching menu entry was found, FALSE
1010 if no such entry was found and FAILD if a syntax problem was
1011 diagnosed. The CURMENU structure is updated if the entry was found.
1012 */
1013 {
1014 HMENU hMenu;
1015 int ItemCount;
1016 int Pos, StartPos, PosOffset;
1017 BOOL Result, DoScan;
1018 char Target[MAXMENUTITLE];
1019 char EntryName[MAXMENUTITLE];
1020
1021 while (*Name == '<') {
1022 ++Name;
1023 --CM->cm_x;
1024 CM->cm_pos = -1;
1025 }
1026 if ((Result = ParseMenu (Name, Target, &Pos)) != TRUE) {
1027 return Result;
1028 }
1029 if (CM->cm_x < 0) {
1030 hMenu = GetMenu (hFrameWnd);
1031 }
1032 else hMenu = CM->cm_parent[CM->cm_x];
1033 PosOffset = MenuEntryOffset (hMenu);
1034 ItemCount = GetMenuItemCount (hMenu);
1035 if (DoScan = (Pos < 0)) Pos = CM->cm_pos;
1036 if (Pos < 0) Pos = 0;
1037 Pos += PosOffset;
1038 StartPos = Pos;
1039 do {
1040 char *s;
1041
1042 if (IsMenuSeparator (hMenu, Pos)) {
1043 EntryName[0] = '\0';
1044 }
1045 else {
1046 GetMenuString (hMenu, Pos, (LPSTR)&EntryName[0],
1047 MAXMENUTITLE, MF_BYPOSITION);
1048 if ((s = strchr (EntryName, '\t')) != NULL) *s = '\0';
1049 /* discard the accelerator/key-binding info */
1050 }
1051 if (strcmp (EntryName, Target) == 0) goto MenuLocated;
1052 if (!DoScan) {
1053 if (*Target == '\0') goto MenuLocated;
1054 else break; /* no matching target at indicated position */
1055 }
1056 if (++Pos >= ItemCount) Pos = 0;
1057 } while (Pos != StartPos);
1058 return FALSE; /* not found */
1059 MenuLocated:
1060 CM->cm_pos = Pos - PosOffset;
1061 return TRUE;
1062 } /* LocateMenu */
1063
1064 /* AddMenuEntry: add a menu entry (or, recursively, a cascade of entries) */
1065 /* ============ */
1066
1067 static BOOL PASCAL AddMenuEntry (char *Name, UINT ID, CURMENU *CM,
1068 WORD *MenuType)
1069 /* the MenuType is a set of flags set returned by this function:
1070 MT_DUMMY indicates that the menu item is a mere separator, MT_MENUBAR
1071 indicates that the menu bar has been modified */
1072 {
1073 HMENU hMenu, hPopup;
1074 int Pos;
1075 BOOL Result;
1076 char EntryName[MAXMENUTITLE];
1077 WORD MenuFlags;
1078
1079 if ((Result = ParseMenu (Name, EntryName, &Pos)) != TRUE) {
1080 return Result;
1081 }
1082 if (CM->cm_x < 0) {
1083 hMenu = GetMenu (hFrameWnd);
1084 *MenuType |= MT_MENUBAR;
1085 }
1086 else hMenu = CM->cm_parent[CM->cm_x];
1087 Name = strchr(Name, '>');
1088 if (Name != NULL) { /* our task is to set up a popup menu */
1089 if (CM->cm_x >= MAXMENUPARENT - 1) {
1090 mlwrite (TEXT301); /* "[Too many nested popup menus]" */
1091 return FALSE;
1092 }
1093 if ((hPopup = CreatePopupMenu ()) == NULL) {
1094 mlwrite (TEXT302); /* "[lack of resources]" */
1095 return ABORT;
1096 }
1097 MenuFlags = MF_POPUP | MF_STRING;
1098 }
1099 else { /* this is a menu entry, not a popup */
1100 if (strcmp (EntryName, "-") == 0) { /* only a separator */
1101 MenuFlags = MF_SEPARATOR;
1102 *MenuType |= MT_DUMMY;
1103 ID = 0; /* contrary to SDK documentation, InsertMenu
1104 does not ignore the ID for a separator in 3.0! */
1105 }
1106 else {
1107 MenuFlags = MF_STRING;
1108 }
1109 }
1110 if (Pos < 0) {
1111 Pos = CM->cm_pos + 1; /* to insert after current entry */
1112 if (Pos <= 0) Pos = MenuEntryCount (hMenu); /* to append at end */
1113 }
1114 Result = InsertMenu (hMenu, Pos + MenuEntryOffset (hMenu),
1115 MenuFlags | MF_BYPOSITION,
1116 Name ? (UINT)hPopup : ID,
1117 (LPSTR)&EntryName[0]);
1118 if (!Result) {
1119 mlwrite (TEXT302); /* "[lack of resources]" */
1120 Result = FALSE;
1121 }
1122 else Result = TRUE;
1123 if (Name == NULL) {
1124 CM->cm_pos = Pos;
1125 }
1126 else {
1127 if (Result != TRUE) {
1128 DestroyMenu (hPopup);
1129 }
1130 else {
1131 CM->cm_parent[++CM->cm_x] = hPopup;
1132 CM->cm_pos = -1;
1133 ++Name; /* skip the '>' */
1134 if ((Result = AddMenuEntry (Name, ID, CM, MenuType)) != TRUE) {
1135 /* Menu creation FAILD at some point, we must undo our own */
1136 DeleteMenu (hMenu, Pos + MenuEntryOffset (hMenu),
1137 MF_BYPOSITION);
1138 }
1139 }
1140 }
1141 return Result;
1142 } /* AddMenuEntry */
1143
1144 /* AddMenuBinding: bind a menu item to an EPOINTER */
1145 /* ============== */
1146
1147 static BOOL PASCAL AddMenuBinding (ETYPE EPOINTER eptr, WORD type)
1148
1149 /* called by bindtomenu and macrotomenu, once the function or macro name
1150 has been parsed */
1151 {
1152 BOOL Result;
1153 WORD MenuType = 0; /* info returned by AddMenuEntry */
1154 char FullName[NSTRING];
1155 char *Name;
1156 UINT DynIndex;
1157 CURMENU CM;
1158
1159 /*-Let's make sure we have a free entry in the binding table */
1160 for (DynIndex = 0; MenuDynBind[DynIndex].m_word != 0; DynIndex++) {
1161 if (DynIndex >= MAXDYNMENU) {
1162 mlwrite (TEXT17);
1163 /* "Binding table FULL!" */
1164 return FALSE;
1165 }
1166 }
1167
1168 /*-Parse the menu Name */
1169 Name = &FullName[0];
1170 if ((Result = mlreply (TEXT306, Name, NSTRING)) != TRUE) {
1171 /* "Menu: " */
1172 return Result;
1173 }
1174 if (*Name == '>') {
1175 CM.cm_pos = -1;
1176 CM.cm_x = -1;
1177 ++Name;
1178 }
1179 else CM = CurrentMenu;
1180 while ((Result = LocateMenu (Name, &CM)) == TRUE) {
1181 /* ...looping until we hit a piece of the menu path that does
1182 not yet exist */
1183 HMENU hMenu;
1184
1185 if (CM.cm_x < 0) hMenu = GetMenu (hFrameWnd);
1186 else hMenu = CM.cm_parent[CM.cm_x];
1187 hMenu = GetSubMenu (hMenu, CM.cm_pos + MenuEntryOffset (hMenu));
1188 if (hMenu == NULL) {
1189 /* this is not a popup menu, we loose! */
1190 mlwrite (TEXT300); /* "[Incorrect menu]" */
1191 return FALSE;
1192 }
1193 if (CM.cm_x >= MAXMENUPARENT - 1) {
1194 mlwrite (TEXT301); /* "[Too many nested popup menus]" */
1195 return FALSE;
1196 }
1197 CM.cm_parent[++CM.cm_x] = hMenu;
1198 CM.cm_pos = -1;
1199 Name = strchr (Name, '>');
1200 if (Name == NULL) {
1201 /* premature end of menu path! */
1202 mlwrite (TEXT300); /* "[Incorrect menu]" */
1203 return FALSE;
1204 }
1205 ++Name; /* skip the '>' */
1206 }
1207 if (Result != FALSE) return Result; /* syntax error, no cigar! */
1208
1209 /*-Create the menu entry (or cascade of entries) */
1210 if ((Result = AddMenuEntry (Name, DynIndex + IDM_DYNAMIC, &CM,
1211 &MenuType)) != TRUE) {
1212 return Result;
1213 }
1214 CurrentMenu = CM;
1215
1216 if (!(MenuType & MT_DUMMY)) {
1217 /*-complete the binding */
1218 MenuDynBind[DynIndex].m_word = type;
1219 MenuDynBind[DynIndex].m_ptr = eptr;
1220 }
1221
1222 if (MenuType & MT_MENUBAR) DrawMenuBar (hFrameWnd);
1223 return TRUE;
1224 } /* AddMenuBinding */
1225
1226 /* bindtomenu: bind a menu item to an emacs function */
1227 /* ========== */
1228
1229 PASCAL bindtomenu (int f, int n)
1230 /* command arguments IGNORED */
1231 {
1232 ETYPE EPOINTER e;
1233
1234 e.fp = getname (TEXT304); /* "Function: " */
1235 if (e.fp == NULL) {
1236 mlwrite (TEXT16); /* "[No such function]" */
1237 return FALSE;
1238 }
1239 return AddMenuBinding (e, MB_FNC);
1240 } /* bindtomenu */
1241
1242 /* macrotomenu: bind a menu item to a macro (i.e. a buffer) */
1243 /* =========== */
1244
1245 PASCAL macrotomenu (int f, int n)
1246 /* command arguments IGNORED */
1247 {
1248 ETYPE EPOINTER e;
1249 char Name[NBUFN];
1250 BOOL Result;
1251
1252 if ((Result = mlreply(TEXT305, &Name[1], NBUFN-2)) != TRUE) {
1253 /* "Macro: " */
1254 return Result;
1255 }
1256 Name[0] = '[';
1257 strcat (Name, "]");
1258 if ((e.buf = bfind (Name, FALSE, 0)) == NULL) {
1259 mlwrite (TEXT130);
1260 /* "Macro not defined" */
1261 return FALSE;
1262 }
1263 return AddMenuBinding (e, MB_BUF);
1264 } /* macrotomenu */
1265
1266 /* DeleteMenuBinding: remove a menu entry and its binding or sub-entries */
1267 /* ================= */
1268
1269 static BOOL PASCAL DeleteMenuBinding (HMENU hMenu, int Pos)
1270 /* returns TRUE except when an attempt is made to delete the 'Screen'
1271 menu */
1272 {
1273 BOOL Result;
1274 UINT ID;
1275 HMENU hPopup;
1276
1277 Pos += MenuEntryOffset (hMenu);
1278 if (!IsMenuSeparator (hMenu, Pos)) {
1279 if ((ID = GetMenuItemID (hMenu, Pos)) != -1) {
1280 /* this is a pure menu item */
1281 if (ID >= IDM_DYNAMIC) {
1282 /* let's free the dynamic binding table entry */
1283 MenuDynBind[ID - IDM_DYNAMIC].m_word = 0;
1284 }
1285 }
1286 else if ((hPopup = GetSubMenu (hMenu, Pos)) != NULL) {
1287 /* this is a popup, we must delete all the contained entries */
1288 int i;
1289
1290 if (ID == IDM_SCREEN_POPUP) {
1291 /* this menu is used by the system's MDI manager to list the
1292 MDI childs. We cannot remove it */
1293 return FALSE;
1294 }
1295 for (i = MenuEntryCount (hPopup); i > 0;) {
1296 DeleteMenuBinding (hPopup, --i);
1297 }
1298 }
1299 }
1300 DeleteMenu (hMenu, Pos, MF_BYPOSITION);
1301 if (hMenu == GetMenu (hFrameWnd)) DrawMenuBar (hFrameWnd);
1302 return TRUE;
1303 } /* DeleteMenuBinding */
1304
1305 /* unbindmenu: remove a menu entry */
1306 /* ========== */
1307
1308 PASCAL unbindmenu (int f, int n)
1309 /* command arguments IGNORED */
1310 {
1311 BOOL Result;
1312 char FullName[NSTRING];
1313 char *Name;
1314 CURMENU CM;
1315
1316 Name = &FullName[0];
1317 if ((Result = mlreply (TEXT303, Name, NSTRING)) != TRUE) {
1318 /* "Menu: " */
1319 return Result;
1320 }
1321 if (*Name == '>') {
1322 CM.cm_pos = -1;
1323 CM.cm_x = -1;
1324 ++Name;
1325 }
1326 else CM = CurrentMenu;
1327 while ((Result = LocateMenu (Name, &CM)) == TRUE) {
1328 HMENU hMenu;
1329
1330 if (CM.cm_x < 0) hMenu = GetMenu (hFrameWnd);
1331 else hMenu = CM.cm_parent[CM.cm_x];
1332 Name = strchr (Name, '>');
1333 if (Name != NULL) { /* the menu path continues beyond this... */
1334 hMenu = GetSubMenu (hMenu, CM.cm_pos + MenuEntryOffset (hMenu));
1335 if (hMenu == NULL) {
1336 /* this is not a popup menu, we loose! */
1337 Result = FALSE;
1338 break;
1339 }
1340 if (CM.cm_x >= MAXMENUPARENT - 1) {
1341 mlwrite (TEXT301); /* "[Too many nested popup menus]" */
1342 return FALSE;
1343 }
1344 CM.cm_parent[++CM.cm_x] = hMenu;
1345 CM.cm_pos = 0;
1346 ++Name; /* skip the '>' */
1347 /* and loop once again... */
1348 }
1349 else { /* this is it: the end of the menu path */
1350 int Pos;
1351
1352 if ((Result = DeleteMenuBinding (hMenu, CM.cm_pos)) != TRUE) {
1353 return FALSE;
1354 }
1355 if ((Pos = MenuEntryCount (hMenu) - 1) < CM.cm_pos) {
1356 /* we just deleted the item at the menu's end. This
1357 position is no longer valid */
1358 CM.cm_pos = Pos;
1359 }
1360 CurrentMenu = CM;
1361 break; /* we are done */
1362 }
1363 }
1364 /* we arrive here if the menu was not found or a syntax error occured */
1365 if (Result == FALSE) mlwrite (TEXT300); /* "[Incorrect menu]" */
1366 return Result;
1367 } /* unbindmenu */
1368