|
|
1.1 root 1: /*
2: * $Source: /u1/X11/clients/xterm/RCS/menu.c,v $
3: * $Header: menu.c,v 1.28 87/09/11 22:31:28 rws Exp $
4: */
5:
6: #include <X11/copyright.h>
7:
8: /*
9: * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
10: *
11: * All Rights Reserved
12: *
13: * Permission to use, copy, modify, and distribute this software and its
14: * documentation for any purpose and without fee is hereby granted,
15: * provided that the above copyright notice appear in all copies and that
16: * both that copyright notice and this permission notice appear in
17: * supporting documentation, and that the name of Digital Equipment
18: * Corporation not be used in advertising or publicity pertaining to
19: * distribution of the software without specific, written prior permission.
20: *
21: *
22: * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
23: * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
24: * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
25: * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
26: * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
27: * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
28: * SOFTWARE.
29: */
30:
31: #include <stdio.h>
32: #ifdef MODEMENU
33: #include <X11/Xlib.h>
34: #include <X11/Intrinsic.h>
35: #include <X11/Xutil.h>
36: #include <X11/Xatom.h>
37: #include <X11/cursorfont.h>
38: #include <X11/Xtlib.h>
39: #include "menu.h"
40: #include <setjmp.h>
41: #include "ptyx.h"
42: #include "data.h"
43:
44: #ifndef lint
45: static char rcs_id[] = "$Header: menu.c,v 1.28 87/09/11 22:31:28 rws Exp $";
46: #endif lint
47:
48: #define DEFMENUBORDER 2
49: #define DEFMENUPAD 3
50:
51: #define XtNxterm "xterm"
52: #define XtCApp "App"
53:
54: #define XOR(a,b) ((a&(~b)) | ((~a)&b))
55:
56: #define SetStateFlags(item) item->itemFlags = (item->itemFlags &\
57: ~(itemStateMask | itemChanged)) |\
58: ((item->itemFlags & itemSetMask) >>\
59: itemSetMaskShift)
60:
61:
62: static char Check_MarkBits[] = {
63: 0x00, 0x01, 0x80, 0x01, 0xc0, 0x00, 0x60, 0x00,
64: 0x31, 0x00, 0x1b, 0x00, 0x0e, 0x00, 0x04, 0x00
65: };
66: static char Default_CursorBits[] = {
67: 0x00, 0x00, 0x02, 0x00, 0x06, 0x00, 0x0e, 0x00,
68: 0x1e, 0x00, 0x3e, 0x00, 0x7e, 0x00, 0xfe, 0x00,
69: 0xfe, 0x01, 0x3e, 0x00, 0x36, 0x00, 0x62, 0x00,
70: 0x60, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0x00, 0x00
71: };
72: static char Default_MaskBits[] = {
73: 0x03, 0x00, 0x07, 0x00, 0x0f, 0x00, 0x1f, 0x00,
74: 0x3f, 0x00, 0x7f, 0x00, 0xff, 0x00, 0xff, 0x01,
75: 0xff, 0x03, 0xff, 0x07, 0x7f, 0x00, 0xf7, 0x00,
76: 0xf3, 0x00, 0xe1, 0x01, 0xe0, 0x01, 0xc0, 0x01
77: };
78:
79: static GC MenuGC, MenuInverseGC, MenuInvertGC, MenuGrayGC;
80: static int gotGCs = FALSE;
81:
82: Pixmap Gray_Tile, Check_Tile;
83: Menu Menu_Default;
84: Cursor Menu_DefaultCursor;
85: char *Menu_DefaultFont;
86:
87: extern XrmNameList nameList;
88: extern XrmClassList classList;
89: extern EventDoNothing();
90:
91: static int default_menuBorder = DEFMENUBORDER;
92: static int default_menuPad = DEFMENUPAD;
93: static char *defaultNULL = NULL;
94:
95: static Resource resourceList[] = {
96: {"menuBorder", "MenuBorder", XrmRInt, sizeof(int),
97: (caddr_t) &Menu_Default.menuBorderWidth, (caddr_t) &default_menuBorder},
98: {"menuFont", XtCFont, XrmRString, sizeof(char *),
99: (caddr_t) &Menu_DefaultFont, (caddr_t) &defaultNULL},
100: {"menuPad", "MenuPad", XrmRInt, sizeof(int),
101: (caddr_t) &Menu_Default.menuItemPad, (caddr_t) &default_menuPad}
102: };
103:
104: /*
105: * AddMenuItem() adds a menu item to an existing menu, at the end of the
106: * list, which are number sequentially from zero. The menuitem index is
107: * return, or -1 if failed.
108: */
109:
110: AddMenuItem(menu, text)
111: register Menu *menu;
112: register char *text;
113: {
114: register MenuItem *menuitem, **next;
115: register int i;
116: extern char *malloc();
117:
118: if(!menu || !text || (menuitem = (MenuItem *)malloc(sizeof(MenuItem)))
119: == (MenuItem *)0)
120: return(-1);
121: bzero((char *)menuitem, sizeof(MenuItem));
122: menuitem->itemText = text;
123: menuitem->itemTextLength = strlen(text);
124: for(i = 0, next = &menu->menuItems ; *next ; i++)
125: next = &(*next)->nextItem;
126: *next = menuitem;
127: menu->menuFlags |= menuChanged;
128: return(i);
129: }
130:
131: /*
132: * DisposeItem() releases the memory allocated for the given indexed
133: * menuitem. Nonzero is returned if an item was actual disposed of.
134: */
135: DisposeItem(menu, i)
136: register Menu *menu;
137: register int i;
138: {
139: register MenuItem **next, **last, *menuitem;
140:
141: if(!menu || i < 0)
142: return(0);
143: next = &menu->menuItems;
144: do {
145: if(!*next)
146: return(0);
147: last = next;
148: next = &(*next)->nextItem;
149: } while(i-- > 0);
150: menuitem = *last;
151: *last = *next;
152: free(menuitem);
153: return(1);
154: }
155:
156: /*
157: * DisposeMenu() releases the memory allocated for the given menu.
158: */
159: DisposeMenu(menu)
160: register Menu *menu;
161: {
162: register TScreen *screen = &term.screen;
163: static Unmap_Menu();
164:
165: if(!menu)
166: return;
167: if(menu->menuFlags & menuMapped)
168: Unmap_Menu(menu);
169: while(DisposeItem(menu, 0));
170: if(menu->menuWindow)
171: XDestroyWindow(screen->display, menu->menuWindow);
172: if(menu->menuSaved)
173: XFreePixmap(screen->display, menu->menuSaved);
174: free(menu);
175: }
176:
177: InitMenu(name)
178: register char *name;
179: {
180: register TScreen *screen = &term.screen;
181: register char *cp;
182: extern Pixmap make_gray();
183: Display *dpy = screen->display;
184:
185: /*
186: * If the gray tile hasn't been set up, do it now.
187: */
188: if(!Gray_Tile)
189: Gray_Tile = make_gray(WhitePixel(dpy, DefaultScreen(dpy)),
190: BlackPixel(dpy, DefaultScreen(dpy)), 1);
191: if (!Check_Tile) {
192: Check_Tile = Make_tile(checkMarkWidth, checkMarkHeight,
193: Check_MarkBits, BlackPixel(dpy, DefaultScreen(dpy)),
194: WhitePixel(dpy, DefaultScreen(dpy)),
195: DefaultDepth(dpy, DefaultScreen(dpy)));
196: }
197: Menu_Default.menuFlags = menuChanged;
198: /* if((cp = XGetDefault(dpy, name, "MenuFreeze")) && XStrCmp(cp, "on") == 0)
199: Menu_Default.menuFlags |= menuFreeze;
200: if((cp = XGetDefault(dpy, name, "MenuSave")) && XStrCmp(cp, "on") == 0)
201: Menu_Default.menuFlags |= menuSaveMenu;
202: */
203: Menu_Default.menuInitialItem = -1;
204: XtGetResources( dpy, resourceList, XtNumber(resourceList),
205: (ArgList) NULL, 0, DefaultRootWindow(dpy),
206: XtNxterm, XtCApp, &nameList, &classList );
207:
208: /* would be nice if (Xrm)CvtStringToFontStruct didn't complain
209: * when the font didn't exist! */
210: if (Menu_DefaultFont != NULL) {
211: Menu_Default.menuFontInfo = XLoadQueryFont(dpy, Menu_DefaultFont);
212: if (!Menu_Default.menuFontInfo)
213: Menu_DefaultFont = NULL;
214: }
215:
216: /* actually, we need to know whether the fid is valid anyway */
217: if (Menu_DefaultFont == NULL) {
218: Menu_Default.menuFontInfo = screen->fnt_norm;
219: MenugcFontMask = VTgcFontMask;
220: }
221: };
222:
223: /*
224: * ItemFlags returns the state of item "n" of the menu.
225: */
226: ItemFlags(menu, n)
227: register Menu *menu;
228: register int n;
229: {
230: register MenuItem *item;
231:
232: if(!menu || !menu->menuItems || n < 0)
233: return(-1);
234: for(item = menu->menuItems ; n > 0 ; n--)
235: if(!(item = item->nextItem))
236: return(0);
237: return((item->itemFlags & itemSetMask) >> itemSetMaskShift);
238: }
239:
240: /*
241: * ItemText changes the text of item "n" of the menu.
242: */
243: ItemText(menu, n, text)
244: register Menu *menu;
245: register int n;
246: char *text;
247: {
248: register MenuItem *item;
249:
250: if(!menu || !menu->menuItems || n < 0 || !text)
251: return(0);
252: for(item = menu->menuItems ; n > 0 ; n--)
253: if(!(item = item->nextItem))
254: return(0);
255: item->itemText = text;
256: menu->menuFlags |= menuChanged;
257: return(1);
258: }
259:
260: /*
261: * NewMenu() returns a pointer to an initialized new Menu structure, or NULL
262: * if failed.
263: *
264: * The Menu structure _menuDefault contains the default menu settings.
265: */
266: Menu *NewMenu(name, reverse)
267: char *name;
268: int reverse;
269: {
270: register Menu *menu;
271: register int fg, bg;
272: register TScreen *screen = &term.screen;
273: XGCValues xgc;
274: extern char *malloc();
275: extern XFontStruct *XLoadQueryFont();
276: register Display *dpy = screen->display;
277:
278: /*
279: * If the GrayTile hasn't been defined, InitMenu() was never
280: * run, so exit.
281: */
282: if(!Gray_Tile)
283: return((Menu *)0);
284: /*
285: * Allocate the memory for the menu structure.
286: */
287: if((menu = (Menu *)malloc(sizeof(Menu))) == (Menu *)0)
288: return((Menu *)0);
289: /*
290: * Initialize to default values.
291: */
292: *menu = Menu_Default;
293: /*
294: * If the menu cursor hasn't been given, make a default one.
295: */
296: if(!menu->menuCursor) {
297: if(!Menu_DefaultCursor) {
298: if(reverse) {
299: fg = WhitePixel(dpy,
300: DefaultScreen(dpy));
301: bg = BlackPixel(dpy,
302: DefaultScreen(dpy));
303: } else {
304: fg = BlackPixel(dpy,
305: DefaultScreen(dpy));
306: bg = WhitePixel(dpy,
307: DefaultScreen(dpy));
308: }
309: if(!(Menu_DefaultCursor =
310: XCreateFontCursor(dpy, XC_left_ptr)))
311: return((Menu *)0);
312: }
313: menu->menuCursor = Menu_DefaultCursor;
314: }
315: /*
316: * Initialze the default background and border pixmaps and foreground
317: * and background colors (black and white).
318: */
319: if(reverse) {
320: menu->menuFgColor = WhitePixel(dpy,
321: DefaultScreen(dpy));
322: menu->menuBgColor = BlackPixel(dpy,
323: DefaultScreen(dpy));
324: } else {
325: menu->menuFgColor = BlackPixel(dpy,
326: DefaultScreen(dpy));
327: menu->menuBgColor = WhitePixel(dpy,
328: DefaultScreen(dpy));
329: }
330: if(!gotGCs) {
331: xgc.foreground = menu->menuFgColor;
332: xgc.function = GXinvert;
333: xgc.plane_mask = XOR(menu->menuFgColor, menu->menuBgColor);
334: MenuInvertGC = XCreateGC(dpy, DefaultRootWindow(dpy),
335: GCForeground+GCFunction+GCPlaneMask, &xgc);
336: xgc.foreground = menu->menuFgColor;
337: xgc.background = menu->menuBgColor;
338: xgc.font = menu->menuFontInfo->fid;
339: xgc.function = GXcopy;
340: xgc.fill_style = FillSolid;
341: MenuGC = XCreateGC(dpy, DefaultRootWindow(dpy),
342: MenugcFontMask+GCForeground+GCBackground+GCFunction+GCFillStyle,
343: &xgc);
344: xgc.foreground = menu->menuBgColor;
345: xgc.background = menu->menuFgColor;
346: xgc.font = menu->menuFontInfo->fid;
347: xgc.function = GXcopy;
348: xgc.fill_style = FillSolid;
349: MenuInverseGC = XCreateGC(dpy, DefaultRootWindow(dpy),
350: MenugcFontMask+GCForeground+GCBackground+GCFunction+GCFillStyle,
351: &xgc);
352: xgc.foreground = menu->menuFgColor;
353: xgc.background = menu->menuBgColor;
354: xgc.function = menu->menuFgColor ? GXor : GXand;
355: /* xgc.function = GXcopy;*/
356: xgc.stipple = Gray_Tile;
357: xgc.fill_style = FillStippled;
358: MenuGrayGC = XCreateGC(dpy, DefaultRootWindow(dpy),
359: GCStipple+GCFillStyle+MenugcFontMask
360: +GCForeground+GCBackground+GCFunction,
361: &xgc);
362: gotGCs = TRUE;
363: }
364: /*
365: * Set the menu title. If name is NULL or is an empty string, no
366: * title will be displayed.
367: */
368: if(name && *name) {
369: menu->menuTitleLength = strlen(menu->menuTitle = name);
370: menu->menuTitleWidth = XTextWidth(menu->menuFontInfo, name,
371: menu->menuTitleLength);
372: menu->menuItemTop = menu->menuFontInfo->ascent +
373: menu->menuFontInfo->descent + 2 * menu->menuItemPad + 1;
374: } else
375: menu->menuTitleLength = menu->menuTitleWidth =
376: menu->menuItemTop = 0;
377: return(menu);
378: }
379:
380: /*
381: * SetItemCheck sets the check state of item "n" of the menu to "state".
382: */
383: SetItemCheck(menu, n, state)
384: register Menu *menu;
385: register int n;
386: int state;
387: {
388: register MenuItem *item;
389:
390: if(!menu || !menu->menuItems || n < 0)
391: return(0);
392: for(item = menu->menuItems ; n > 0 ; n--)
393: if(!(item = item->nextItem))
394: return(0);
395: if(state)
396: item->itemFlags |= itemSetChecked;
397: else
398: item->itemFlags &= ~itemSetChecked;
399: if(((item->itemFlags & itemSetMask) >> itemSetMaskShift) !=
400: (item->itemFlags & itemStateMask)) {
401: item->itemFlags |= itemChanged;
402: menu->menuFlags |= menuItemChanged;
403: } else
404: item->itemFlags &= ~itemChanged;
405: return(1);
406: }
407:
408: /*
409: * SetItemDisable sets the disable state of item "n" of the menu to "state".
410: */
411: SetItemDisable(menu, n, state)
412: register Menu *menu;
413: register int n;
414: int state;
415: {
416: register MenuItem *item;
417:
418: if(!menu || !menu->menuItems || n < 0)
419: return(0);
420: for(item = menu->menuItems ; n > 0 ; n--)
421: if(!(item = item->nextItem))
422: return(0);
423: if(state)
424: item->itemFlags |= itemSetDisabled;
425: else
426: item->itemFlags &= ~itemSetDisabled;
427: if(((item->itemFlags & itemSetMask) >> itemSetMaskShift) !=
428: (item->itemFlags & itemStateMask)) {
429: item->itemFlags |= itemChanged;
430: menu->menuFlags |= menuItemChanged;
431: } else
432: item->itemFlags &= ~itemChanged;
433: return(1);
434: }
435:
436: static Menu *menu;
437: static MenuItem *item;
438: static int i;
439: static MenuItem *hilited_item;
440: static int drawn;
441: static int changed;
442: int y, n, hilited_y, hilited_n, in_window;
443: static MenuItem *Mouse_InItem(), *Y_InItem();
444: static Unmap_Menu();
445:
446: XtEventReturnCode MenuExposeWindow(event, eventdata)
447: register XEvent *event;
448: caddr_t eventdata;
449: {
450: register TScreen *screen = &term.screen;
451: /*
452: * If we have a saved pixmap, display it. Otherwise
453: * redraw the menu and save it away.
454: */
455: if (event->type == NoExpose) return;
456: if(menu->menuSaved) {
457: XCopyArea(screen->display, menu->menuSaved, menu->menuWindow,
458: MenuGC, 0, 0,
459: menu->menuWidth, menu->menuHeight, 0, 0);
460: /*
461: * If the menuItemChanged flag is still set,
462: * then we need to redraw certain menu items.
463: * ("i" is the vertical position of the top
464: * of the current item.)
465: */
466: if(changed & menuItemChanged) {
467: i = menu->menuItemTop;
468: for(item = menu->menuItems ; item ;
469: item = item->nextItem) {
470: if(item->itemFlags & itemChanged)
471: Modify_Item(menu, item, i);
472: i += item->itemHeight;
473: }
474: }
475: } else
476: Draw_Menu(menu);
477: /*
478: * If the menu has changed in any way and we want to
479: * save the menu, throw away any existing save menu
480: * image and make a new one.
481: */
482: XFlush(screen->display);
483: if(changed && (menu->menuFlags & menuSaveMenu)) {
484: if(menu->menuSaved)
485: XFreePixmap(screen->display, menu->menuSaved);
486: /* menu->menuSaved = XPixmapSave(screen->display,
487: menu->menuWindow, 0, 0, menu->menuWidth, menu->menuHeight);
488: */
489: }
490: /*
491: * See which item the cursor may currently be in. If
492: * it is in a non-disabled item, hilite it.
493: */
494: if(hilited_item = Mouse_InItem(menu, &hilited_y, &hilited_n))
495: XFillRectangle(screen->display, menu->menuWindow,
496: MenuInvertGC, 0, hilited_y,
497: menu->menuWidth, hilited_item->itemHeight);
498: drawn++;
499: return (XteventHandled);
500: }
501:
502: XtEventReturnCode MenuMouseMoved(event, eventdata)
503: XButtonEvent *event;
504: caddr_t eventdata;
505: {
506: register TScreen *screen = &term.screen;
507: if(!drawn || !in_window)
508: return;
509: /*
510: * See which item the cursor may currently be in. If
511: * the item has changed, unhilite the old one and
512: * then hilited the new one.
513: */
514: y = event->y;
515: if((item = Y_InItem(menu, &y, &n)) != hilited_item) {
516: if(hilited_item)
517: XFillRectangle(screen->display, menu->menuWindow,
518: MenuInvertGC, 0, hilited_y,
519: menu->menuWidth, hilited_item->itemHeight);
520: if(hilited_item = item) {
521: XFillRectangle(screen->display, menu->menuWindow,
522: MenuInvertGC, 0,
523: hilited_y = y, menu->menuWidth, item->itemHeight);
524: hilited_n = n;
525: }
526: }
527: return (XteventHandled);
528: }
529:
530: XtEventReturnCode MenuEnterWindow(event, eventdata)
531: XEvent *event;
532: caddr_t eventdata;
533: {
534: in_window = TRUE;
535: return (MenuMouseMoved(event, eventdata));
536: }
537:
538: XtEventReturnCode MenuLeaveWindow(event, eventdata)
539: XEvent *event;
540: caddr_t eventdata;
541: {
542: register TScreen *screen = &term.screen;
543: if(!drawn)
544: return (XteventHandled);
545: /*
546: * Unhilite any window that is currently hilited.
547: */
548: if(hilited_item) {
549: XFillRectangle(screen->display, menu->menuWindow,
550: MenuInvertGC, 0, hilited_y,
551: menu->menuWidth, hilited_item->itemHeight);
552: hilited_item = (MenuItem *)0;
553: }
554: in_window = FALSE;
555: return (XteventHandled);
556: }
557:
558: XtEventReturnCode MenuButtonReleased(event, eventdata)
559: XButtonEvent *event;
560: caddr_t eventdata;
561: {
562: register TScreen *screen = &term.screen;
563: extern FinishModeMenu();
564:
565: /*
566: * return the index number of any selected menu
567: * item.
568: */
569:
570: if (! AllButtonsUp(event->state, event->button))
571: return(XteventHandled);
572:
573: XUngrabPointer(screen->display, CurrentTime);
574:
575: if(in_window) {
576: y = event->y;
577: if((item = Y_InItem(menu, &y, &n)) != hilited_item) {
578: if(hilited_item)
579: XFillRectangle(screen->display, menu->menuWindow,
580: MenuInvertGC, 0,
581: hilited_y, menu->menuWidth,
582: hilited_item->itemHeight);
583: if(hilited_item = item) {
584: XFillRectangle(screen->display, menu->menuWindow,
585: MenuInvertGC, 0,
586: hilited_y = y, menu->menuWidth,
587: hilited_item->itemHeight);
588: hilited_n = n;
589: }
590: }
591: }
592: XFlush(screen->display);
593: menu->menuFlags &= ~(menuChanged | menuItemChanged);
594: Unmap_Menu(menu);
595: drawn = 0;
596: if(hilited_item)
597: FinishModeMenu(menu->menuInitialItem = hilited_n);
598: else
599: FinishModeMenu(-1);
600: return (XteventHandled);
601: }
602:
603: /*
604: * TrackMenu does most of the work of displaying the menu and tracking the
605: * mouse.
606: */
607: TrackMenu(lmenu, event)
608: register Menu *lmenu;
609: register XButtonPressedEvent *event;
610: {
611: register TScreen *screen = &term.screen;
612: XButtonReleasedEvent ev;
613: XSetWindowAttributes attr;
614:
615: menu = lmenu;
616: hilited_item = (MenuItem *)0;
617: /*
618: * Check that things are reasonable.
619: */
620: if(!menu || !event || !menu->menuItems || event->type != ButtonPress)
621: return(-1);
622: /*
623: * Set the changed flag and clear the menu changed flags.
624: */
625: changed = menu->menuFlags & (menuChanged | menuItemChanged);
626: /*
627: * If the entire menu has changed, throw away any saved pixmap and
628: * then call RecalcMenu().
629: */
630: if(changed & menuChanged) {
631: if(menu->menuSaved)
632: XFreePixmap(screen->display, menu->menuSaved);
633: menu->menuSaved = (Pixmap)0;
634: if(!Recalc_Menu(menu))
635: return(-1);
636: changed &= ~menuItemChanged;
637: }
638: /*
639: * Now if the window was never created, go ahead and make it. Otherwise
640: * if the menu has changed, resize the window.
641: */
642: if(!menu->menuWindow) {
643: attr.override_redirect = TRUE;
644: attr.border_pixmap = make_gray(
645: WhitePixel(screen->display, DefaultScreen(screen->display)),
646: BlackPixel(screen->display, DefaultScreen(screen->display)),
647: DefaultDepth(screen->display, DefaultScreen(screen->display)));
648: attr.background_pixel = menu->menuBgColor;
649: attr.cursor = menu->menuCursor;
650: if((menu->menuWindow = XCreateWindow(screen->display,
651: DefaultRootWindow(screen->display), 0, 0,
652: menu->menuWidth, menu->menuHeight, menu->menuBorderWidth,
653: 0, CopyFromParent, CopyFromParent,
654: CWBorderPixmap+CWBackPixel+CWOverrideRedirect+CWCursor,
655: &attr)) == (Window)0)
656: return(-1);
657:
658:
659: XtSetEventHandler(screen->display, menu->menuWindow,
660: (XtEventHandler) MenuExposeWindow, ExposureMask,
661: (caddr_t)NULL);
662: XtSetEventHandler(screen->display, menu->menuWindow,
663: (XtEventHandler) MenuEnterWindow, EnterWindowMask,
664: (caddr_t)NULL);
665: XtSetEventHandler(screen->display, menu->menuWindow,
666: (XtEventHandler) MenuLeaveWindow, LeaveWindowMask,
667: (caddr_t)NULL);
668: XtSetEventHandler(screen->display, menu->menuWindow,
669: (XtEventHandler) MenuMouseMoved, PointerMotionMask,
670: (caddr_t)NULL);
671: XtSetEventHandler(screen->display, menu->menuWindow,
672: (XtEventHandler) MenuButtonReleased, ButtonReleaseMask,
673: (caddr_t)NULL);
674: XtSetEventHandler(screen->display, menu->menuWindow,
675: (XtEventHandler) EventDoNothing, ButtonPressMask,
676: (caddr_t)NULL);
677: } else if(changed & menuChanged)
678: XResizeWindow(screen->display, menu->menuWindow,
679: menu->menuWidth, menu->menuHeight);
680: /*
681: * Figure out where the menu is supposed to go, from the initial button
682: * press, and move the window there. Then map the menu.
683: */
684: if(!Move_Menu(menu, event) || !Map_Menu(menu))
685: return(-1);
686:
687: menuWindow = menu->menuWindow;
688: in_window = TRUE;
689: XGrabPointer(screen->display, menu->menuWindow, FALSE,
690: ExposureMask | EnterWindowMask | LeaveWindowMask | PointerMotionMask
691: | ButtonReleaseMask | ButtonPressMask,
692: GrabModeAsync, GrabModeAsync, None, menu->menuCursor, CurrentTime
693: );
694: }
695:
696: /*
697: * Recalculate all of the various menu and item variables.
698: */
699: static Recalc_Menu(menu)
700: register Menu *menu;
701: {
702: register MenuItem *item;
703: register int max, i, height, fontheight;
704:
705: /*
706: * We must have already gotten the menu font.
707: */
708: if(!menu->menuFontInfo)
709: return(0);
710: /*
711: * Initialize the various max width variables.
712: */
713: fontheight = menu->menuFontInfo->ascent + menu->menuFontInfo->descent;
714: height = menu->menuItemTop;
715: menu->menuMaxTextWidth = menu->menuTitleWidth;
716: /*
717: * The item height is the maximum of the font height and the
718: * checkbox height.
719: */
720: max = fontheight;
721: if(checkMarkHeight > max)
722: max = checkMarkHeight;
723: /*
724: * Go through the menu item list.
725: */
726: for(item = menu->menuItems ; item ; item = item->nextItem) {
727: /*
728: * If the item text is a single dash, we assume this is
729: * a line separator and treat it special.
730: */
731: if(XStrCmp(item->itemText, "-") == 0)
732: height += (item->itemHeight = lineSeparatorHeight);
733: else {
734: height += (item->itemHeight = max);
735: /*
736: * Check the text width with the max value stored in
737: * menu.
738: */
739: if((item->itemTextWidth = XTextWidth(
740: menu->menuFontInfo, item->itemText,
741: strlen(item->itemText))) > menu->menuMaxTextWidth)
742: menu->menuMaxTextWidth = item->itemTextWidth;
743: }
744: /*
745: * If the itemChanged flag is set, set the state bits.
746: */
747: if(item->itemFlags & itemChanged) {
748: item->itemFlags = (item->itemFlags & ~itemStateMask) |
749: ((item->itemFlags & itemSetMask) >> itemSetMaskShift);
750: item->itemFlags &= ~itemChanged;
751: }
752: }
753: /*
754: * Set the menu height and then set the menu width.
755: */
756: menu->menuHeight = height;
757: menu->menuWidth = 3 * menu->menuItemPad + menu->menuMaxTextWidth +
758: checkMarkWidth;
759: return(1);
760: }
761:
762: /*
763: * Figure out where to popup the menu, relative to the where the button was
764: * pressed.
765: */
766: static Move_Menu(menu, ev)
767: register Menu *menu;
768: XButtonPressedEvent *ev;
769: {
770: register MenuItem *item;
771: register int n, x, y;
772: register TScreen *screen = &term.screen;
773: int total_width;
774: Window subw;
775: /*
776: * Try to popup the menu so that the cursor is centered within the
777: * width of the menu, but compensate if that would run it outside
778: * the display area.
779: */
780: total_width = menu->menuWidth + 2 * menu->menuBorderWidth;
781: if((x = ev->x_root - total_width / 2) < 0)
782: x = 0;
783: else if(x + total_width > DisplayWidth(screen->display,
784: DefaultScreen(screen->display)))
785: x = DisplayWidth(screen->display,
786: DefaultScreen(screen->display)) - total_width;
787: /*
788: * If the menu extends above outside of the display, warp
789: * the mouse vertically so the menu will all show up.
790: */
791: if((y = ev->y_root) < 0) {
792: /* don't bother warping pointer
793: XWarpPointer(screen->display, None,
794: DefaultRootWindow(screen->display), 0, 0, 0, 0, ev->x_root,
795: 0);
796: */
797: y = 0;
798: } else if((n = y + menu->menuHeight + 2 * menu->menuBorderWidth -
799: DisplayHeight(screen->display, DefaultScreen(screen->display))) > 0) {
800: /* don't bother warping pointer
801: XWarpPointer(screen->display, None,
802: DefaultRootWindow(screen->display), 0, 0, 0, 0, ev->x_root,
803: ev->y_root - n);
804: */
805: y -= n;
806: }
807: XMoveWindow(screen->display, menu->menuWindow, x, y);
808: /*
809: * If we are in freeze mode, save what will be the coordinates of
810: * the save image.
811: */
812: if(menu->menuFlags & menuFreeze) {
813: menu->menuSavedImageX = x;
814: menu->menuSavedImageY = y;
815: }
816: return(1);
817: }
818:
819: /*
820: * Map the menu window.
821: */
822: static Map_Menu(menu)
823: register Menu *menu;
824: {
825: register int i;
826: register TScreen *screen = &term.screen;
827:
828: /*
829: * If we are in freeze mode, save the pixmap underneath where the menu
830: * will be (including the border).
831: */
832: if(menu->menuFlags & menuFreeze) {
833: XGrabServer(screen->display);
834: i = 2 * menu->menuBorderWidth;
835: /* if((menu->menuSavedImage = XPixmapSave(screen->display,
836: DefaultRootWindow(screen->display),
837: menu->menuSavedImageX, menu->menuSavedImageY, menu->menuWidth
838: + i, menu->menuHeight + i)) == (Pixmap)0)
839: */
840: return(0);
841: }
842: /*
843: * Actually map the window.
844: */
845: XMapRaised(screen->display, menu->menuWindow);
846: menu->menuFlags |= menuMapped;
847: return(1);
848: }
849:
850: /*
851: * Draw the entire menu in the blank window.
852: */
853: static Draw_Menu(menu)
854: register Menu *menu;
855: {
856: register MenuItem *item;
857: register int top = menu->menuItemTop;
858: register int x = menu->menuItemPad;
859: register int y, dim;
860: register TScreen *screen = &term.screen;
861:
862: /*
863: * If we have a menu title, draw it first, centered and hilited.
864: */
865: if(menu->menuTitleLength) {
866: XFillRectangle(screen->display, menu->menuWindow,
867: MenuGC, 0, 0, menu->menuWidth, top - 1);
868: XDrawImageString(screen->display, menu->menuWindow,
869: MenuInverseGC, (menu->menuWidth -
870: menu->menuTitleWidth) / 2,
871: menu->menuItemPad+menu->menuFontInfo->ascent,
872: menu->menuTitle, menu->menuTitleLength);
873: }
874: /*
875: * For each item in the list, first draw any check mark and then
876: * draw the rest of it.
877: */
878: for(item = menu->menuItems ; item ; item = item->nextItem) {
879: SetStateFlags(item);
880: dim = (item->itemFlags & itemDisabled);
881: /*
882: * Draw the check mark, possibly dimmed, wherever is necessary.
883: */
884: if(item->itemFlags & itemChecked) {
885: XCopyArea(screen->display,
886: Check_Tile, menu->menuWindow,
887: dim ? MenuGrayGC : MenuGC,
888: 0, 0, checkMarkWidth, checkMarkHeight, x,
889: y = top + (item->itemHeight - checkMarkHeight) / 2);
890: }
891: /*
892: * Draw the item, possibly dimmed.
893: */
894: Draw_Item(menu, item, top, dim);
895: top += item->itemHeight;
896: }
897: }
898:
899: /*
900: * Modify the item at vertical position y. This routine is table driven and
901: * the state and set bits are each 2 bits long, contiguous, the least
902: * significant bits in the flag word and with the state bits in bits 0 & 1.
903: */
904:
905: #define drawCheck 0x10
906: #define removeCheck 0x08
907: #define dimCheck 0x04
908: #define drawItem 0x02
909: #define dimItem 0x01
910:
911: static char Modify_Table[] = {
912: 0x00, 0x02, 0x08, 0x0a, 0x01, 0x00, 0x09, 0x08,
913: 0x10, 0x12, 0x00, 0x12, 0x15, 0x14, 0x05, 0x00
914: };
915:
916: static Modify_Item(menu, item, top)
917: register Menu *menu;
918: register MenuItem *item;
919: int top;
920: {
921: register int x = menu->menuItemPad;
922: register int y;
923: register int center = top + item->itemHeight / 2;
924: register int func = Modify_Table[item->itemFlags &
925: (itemStateMask | itemSetMask)];
926: register TScreen *screen = &term.screen;
927:
928: /*
929: * If we really won't be making a change, return.
930: */
931: if(func == 0)
932: return;
933: /*
934: * Draw the check mark if needed, possibly dimmed.
935: */
936: y = center - (checkMarkHeight / 2);
937: if(func & (drawCheck | dimCheck))
938: XCopyArea(screen->display,
939: Check_Tile, menu->menuWindow,
940: (func & dimCheck) ? MenuGrayGC : MenuGC,
941: 0, 0, checkMarkWidth, checkMarkHeight, x,
942: y = top + (item->itemHeight - checkMarkHeight) / 2);
943: /*
944: * Remove the check mark if needed.
945: */
946: if(func & removeCheck)
947: XClearArea(screen->display, menu->menuWindow,
948: x, y, checkMarkWidth, checkMarkHeight);
949: /*
950: * Call Draw_Item if we need to draw or dim the item.
951: */
952: if((x = func & dimItem) || (func & drawItem))
953: Draw_Item(menu, item, top, x);
954: /*
955: * Update state flags.
956: */
957: SetStateFlags(item);
958: }
959:
960: /*
961: * Draw the item (less check mark) at vertical position y.
962: * Dim the item if "dim" is set.
963: */
964: static Draw_Item(menu, item, y, dim)
965: register Menu *menu;
966: register MenuItem *item;
967: register int y;
968: int dim;
969: {
970: register int x = 2 * menu->menuItemPad + checkMarkWidth;
971: register int center = y + item->itemHeight / 2;
972: register TScreen *screen = &term.screen;
973:
974: /*
975: * If the item text is a single dash, draw a separating line.
976: */
977: if(XStrCmp(item->itemText, "-") == 0) {
978: XDrawLine(screen->display, menu->menuWindow, MenuGC,
979: 0, center, menu->menuWidth, center);
980: return;
981: }
982: /*
983: * Draw and/or dim the text, centered vertically.
984: */
985: y = center -
986: ((menu->menuFontInfo->ascent + menu->menuFontInfo->descent)/ 2);
987: if(dim) {
988: XDrawString(screen->display, menu->menuWindow, MenuGrayGC,
989: x, y+menu->menuFontInfo->ascent,
990: item->itemText, item->itemTextLength);
991: } else
992: XDrawImageString(screen->display, menu->menuWindow,
993: MenuGC, x, y+menu->menuFontInfo->ascent,
994: item->itemText, item->itemTextLength);
995: }
996:
997: /*
998: * Determine which enabled menu item the mouse is currently in. Return the
999: * top position of this item and its item number. Set inwindow to whether
1000: * we are or not.
1001: */
1002: static MenuItem *Mouse_InItem(menu, top, n)
1003: register Menu *menu;
1004: int *top, *n;
1005: {
1006: int x, y, rootx, rooty, mask;
1007: Window subw, root;
1008: static MenuItem *Y_InItem();
1009: register TScreen *screen = &term.screen;
1010:
1011: /*
1012: * Find out where the mouse is. If its not in the menu window,
1013: * return NULL.
1014: */
1015: XQueryPointer(screen->display, menu->menuWindow,
1016: &root, &subw, &rootx, &rooty, &x, &y, &mask);
1017: if((x <0) || (y < 0) ||
1018: (x > menu->menuWidth) || (y > menu->menuHeight)) {
1019: return((MenuItem *)0);
1020: }
1021: /*
1022: * Call Y_InItem().
1023: */
1024: *top = y;
1025: return(Y_InItem(menu, top, n));
1026: }
1027:
1028: /*
1029: * Return which enabled item the locator is in. Also return the
1030: * top position of this item and its item number. Initial y passed
1031: * in top.
1032: */
1033: static MenuItem *Y_InItem(menu, top, n)
1034: register Menu *menu;
1035: int *top, *n;
1036: {
1037: register MenuItem *item;
1038: register int t, i;
1039: register int y = *top;
1040: Window subw;
1041:
1042: /*
1043: * Go through the item list. "t" is the vertical position of the
1044: * current item and "i" is its item number.
1045: */
1046: t = menu->menuItemTop;
1047: /*
1048: * If the mouse is before the first item, return.
1049: */
1050: if(y < t)
1051: return((MenuItem *)0);
1052: for(i = 0, item = menu->menuItems ; item ; i++, item = item->nextItem) {
1053: /*
1054: * If the y coordinate is within this menu item, then return.
1055: * But don't return disable items.
1056: */
1057: if(t + item->itemHeight > y) {
1058: if(item->itemFlags & itemDisabled)
1059: return((MenuItem *)0);
1060: *top = t;
1061: *n = i;
1062: return(item);
1063: }
1064: t += item->itemHeight;
1065: }
1066: /*
1067: * Should never get here.
1068: */
1069: return((MenuItem *)0);
1070: }
1071:
1072: /*
1073: * Unmap_Menu() unmaps a menu, if it is currently mapped.
1074: */
1075: static Unmap_Menu(menu)
1076: register Menu *menu;
1077: {
1078: register int i;
1079: register TScreen *screen = &term.screen;
1080:
1081: if(!menu || !(menu->menuFlags & menuMapped))
1082: return;
1083: if(menu->menuFlags & menuFreeze) {
1084: XUnmapWindow(screen->display, menu->menuWindow);
1085: i = 2 * menu->menuBorderWidth;
1086: XCopyArea(screen->display,
1087: menu->menuSavedImage, DefaultRootWindow(screen->display),
1088: MenuGC, 0, 0, menu->menuWidth + i,
1089: menu->menuHeight + i, menu->menuSavedImageX,
1090: menu->menuSavedImageY);
1091: XFreePixmap(screen->display, menu->menuSavedImage);
1092: XUngrabServer(screen->display);
1093: } else
1094: XUnmapWindow(screen->display, menu->menuWindow);
1095: menu->menuFlags &= ~menuMapped;
1096: }
1097: #endif MODEMENU
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.