|
|
1.1 root 1: /*
2: * $Source: /u1/X/xterm/RCS/menu.c,v $
3: * $Header: menu.c,v 10.101 86/12/01 17:52:43 swick Rel $
4: */
5:
6: #ifdef MODEMENU
7: #include "X/Xlib.h"
8: #include "menu.h"
9:
10: #ifndef lint
11: static char sccs_id[] = "@(#)menu.c\tX10/6.6B\t12/26/86";
12: #endif lint
13:
14: #define FALSE 0
15: #define TRUE 1
16: #define InvertPlane 1
17: #define SetStateFlags(item) item->itemFlags = (item->itemFlags &\
18: ~(itemStateMask | itemChanged)) |\
19: ((item->itemFlags & itemSetMask) >>\
20: itemSetMaskShift)
21:
22:
23: static short Check_MarkBits[] = {
24: 0x0100, 0x0180, 0x00c0, 0x0060,
25: 0x0031, 0x001b, 0x000e, 0x0004
26: };
27: static short Check_GrayBits[] = {
28: 0x0100, 0x0080, 0x0040, 0x0020,
29: 0x0011, 0x000a, 0x0004, 0x0000
30: };
31: static short Default_CursorBits[] = {
32: 0x0000, 0x0002, 0x0006, 0x000e,
33: 0x001e, 0x003e, 0x007e, 0x00fe,
34: 0x01fe, 0x003e, 0x0036, 0x0062,
35: 0x0060, 0x00c0, 0x00c0, 0x0000
36: };
37: static short Default_GrayBits[] = {
38: 0xaaaa, 0x5555, 0xaaaa, 0x5555,
39: 0xaaaa, 0x5555, 0xaaaa, 0x5555,
40: 0xaaaa, 0x5555, 0xaaaa, 0x5555,
41: 0xaaaa, 0x5555, 0xaaaa, 0x5555,
42: };
43: static short Default_MaskBits[] = {
44: 0x0003, 0x0007, 0x000f, 0x001f,
45: 0x003f, 0x007f, 0x00ff, 0x01ff,
46: 0x03ff, 0x07ff, 0x007f, 0x00f7,
47: 0x00f3, 0x01e1, 0x01e0, 0x01c0
48: };
49: static char def_menu_font[] = "vtsingle";
50:
51: Pixmap Gray_Tile;
52: Menu Menu_Default;
53: Cursor Menu_DefaultCursor;
54: char *Menu_DefaultFont;
55: FontInfo *Menu_DefaultFontInfo;
56:
57: /*
58: * AddMenuItem() adds a menu item to an existing menu, at the end of the
59: * list, which are number sequentially from zero. The menuitem index is
60: * return, or -1 if failed.
61: */
62:
63: AddMenuItem(menu, text)
64: register Menu *menu;
65: register char *text;
66: {
67: register MenuItem *menuitem, **next;
68: register int i;
69: extern char *malloc();
70:
71: if(!menu || !text || (menuitem = (MenuItem *)malloc(sizeof(MenuItem)))
72: == (MenuItem *)0)
73: return(-1);
74: bzero((char *)menuitem, sizeof(MenuItem));
75: menuitem->itemText = text;
76: menuitem->itemTextLength = strlen(text);
77: for(i = 0, next = &menu->menuItems ; *next ; i++)
78: next = &(*next)->nextItem;
79: *next = menuitem;
80: menu->menuFlags |= menuChanged;
81: return(i);
82: }
83:
84: /*
85: * DisposeItem() releases the memory allocated for the given indexed
86: * menuitem. Nonzero is returned if an item was actual disposed of.
87: */
88: DisposeItem(menu, i)
89: register Menu *menu;
90: register int i;
91: {
92: register MenuItem **next, **last, *menuitem;
93:
94: if(!menu || i < 0)
95: return(0);
96: next = &menu->menuItems;
97: do {
98: if(!*next)
99: return(0);
100: last = next;
101: next = &(*next)->nextItem;
102: } while(i-- > 0);
103: menuitem = *last;
104: *last = *next;
105: free(menuitem);
106: return(1);
107: }
108:
109: /*
110: * DisposeMenu() releases the memory allocated for the given menu.
111: */
112: DisposeMenu(menu)
113: register Menu *menu;
114: {
115: static Unmap_Menu();
116:
117: if(!menu)
118: return;
119: if(menu->menuFlags & menuMapped)
120: Unmap_Menu(menu);
121: while(DisposeItem(menu, 0));
122: if(menu->menuWindow)
123: XDestroyWindow(menu->menuWindow);
124: if(menu->menuSaved)
125: XFreePixmap(menu->menuSaved);
126: free(menu);
127: }
128:
129: InitMenu(name)
130: register char *name;
131: {
132: register char *cp;
133: register Bitmap bit;
134:
135: /*
136: * If the gray tile hasn't been set up, do it now.
137: */
138: if(!Gray_Tile) {
139: if(!(bit = XStoreBitmap(grayWidth, grayHeight,
140: Default_GrayBits)))
141: return;
142: Gray_Tile = XMakePixmap(bit, WhitePixel, BlackPixel);
143: XFreeBitmap(bit);
144: }
145: Menu_Default.menuFlags = menuChanged;
146: if((cp = XGetDefault(name, "MenuFreeze")) && strcmp(cp, "on") == 0)
147: Menu_Default.menuFlags |= menuFreeze;
148: if((cp = XGetDefault(name, "MenuSave")) && strcmp(cp, "on") == 0)
149: Menu_Default.menuFlags |= menuSaveMenu;
150: Menu_Default.menuInitialItem = -1;
151: Menu_Default.menuBorderWidth = (cp = XGetDefault(name, "MenuBorder")) ?
152: atoi(cp) : 2;
153: Menu_Default.menuItemPad = (cp = XGetDefault(name, "MenuPad")) ?
154: atoi(cp) : 3;
155: Menu_DefaultFont = (cp = XGetDefault(name, "MenuFont")) ? cp :
156: def_menu_font;
157: };
158:
159: /*
160: * ItemFlags returns the state of item "n" of the menu.
161: */
162: ItemFlags(menu, n)
163: register Menu *menu;
164: register int n;
165: {
166: register MenuItem *item;
167:
168: if(!menu || !menu->menuItems || n < 0)
169: return(-1);
170: for(item = menu->menuItems ; n > 0 ; n--)
171: if(!(item = item->nextItem))
172: return(0);
173: return((item->itemFlags & itemSetMask) >> itemSetMaskShift);
174: }
175:
176: /*
177: * ItemText changes the text of item "n" of the menu.
178: */
179: ItemText(menu, n, text)
180: register Menu *menu;
181: register int n;
182: char *text;
183: {
184: register MenuItem *item;
185:
186: if(!menu || !menu->menuItems || n < 0 || !text)
187: return(0);
188: for(item = menu->menuItems ; n > 0 ; n--)
189: if(!(item = item->nextItem))
190: return(0);
191: item->itemText = text;
192: menu->menuFlags |= menuChanged;
193: return(1);
194: }
195:
196: /*
197: * NewMenu() returns a pointer to an initialized new Menu structure, or NULL
198: * if failed.
199: *
200: * The Menu structure _menuDefault contains the default menu settings.
201: */
202: Menu *NewMenu(name, reverse)
203: char *name;
204: int reverse;
205: {
206: register Menu *menu;
207: register int fg, bg;
208: extern char *malloc();
209:
210: /*
211: * If the GrayTile hasn't been defined, InitMenu() was never
212: * run, so exit.
213: */
214: if(!Gray_Tile)
215: return((Menu *)0);
216: /*
217: * Allocate the memory for the menu structure.
218: */
219: if((menu = (Menu *)malloc(sizeof(Menu))) == (Menu *)0)
220: return((Menu *)0);
221: /*
222: * Initialize to default values.
223: */
224: *menu = Menu_Default;
225: /*
226: * If the menu font hasn't yet been gotten, go get it.
227: */
228: if(!menu->menuFontInfo) {
229: if(!Menu_DefaultFontInfo && !(Menu_DefaultFontInfo =
230: XOpenFont(Menu_DefaultFont)))
231: return((Menu *)0);
232: menu->menuFontInfo = Menu_DefaultFontInfo;
233: }
234: /*
235: * If the menu cursor hasn't been given, make a default one.
236: */
237: if(!menu->menuCursor) {
238: if(!Menu_DefaultCursor) {
239: if(reverse) {
240: fg = WhitePixel;
241: bg = BlackPixel;
242: } else {
243: fg = BlackPixel;
244: bg = WhitePixel;
245: }
246: if(!(Menu_DefaultCursor =
247: XCreateCursor(defaultCursorWidth, defaultCursorHeight,
248: Default_CursorBits, Default_MaskBits, defaultCursorX,
249: defaultCursorY, fg, bg, GXcopy)))
250: return((Menu *)0);
251: }
252: menu->menuCursor = Menu_DefaultCursor;
253: }
254: /*
255: * Initialze the default background and border pixmaps and foreground
256: * and background colors (black and white).
257: */
258: if(reverse) {
259: menu->menuBgTile = BlackPixmap;
260: menu->menuFgColor = WhitePixel;
261: menu->menuBgColor = BlackPixel;
262: } else {
263: menu->menuBgTile = WhitePixmap;
264: menu->menuFgColor = BlackPixel;
265: menu->menuBgColor = WhitePixel;
266: }
267: /*
268: * Set the menu title. If name is NULL or is an empty string, no
269: * title will be displayed.
270: */
271: if(name && *name) {
272: menu->menuTitleLength = strlen(menu->menuTitle = name);
273: menu->menuTitleWidth = XStringWidth(name, menu->menuFontInfo,
274: 0, 0);
275: menu->menuItemTop = menu->menuFontInfo->height + 2 *
276: menu->menuItemPad + 1;
277: } else
278: menu->menuTitleLength = menu->menuTitleWidth =
279: menu->menuItemTop = 0;
280: return(menu);
281: }
282:
283: /*
284: * SetItemCheck sets the check state of item "n" of the menu to "state".
285: */
286: SetItemCheck(menu, n, state)
287: register Menu *menu;
288: register int n;
289: int state;
290: {
291: register MenuItem *item;
292:
293: if(!menu || !menu->menuItems || n < 0)
294: return(0);
295: for(item = menu->menuItems ; n > 0 ; n--)
296: if(!(item = item->nextItem))
297: return(0);
298: if(state)
299: item->itemFlags |= itemSetChecked;
300: else
301: item->itemFlags &= ~itemSetChecked;
302: if(((item->itemFlags & itemSetMask) >> itemSetMaskShift) !=
303: (item->itemFlags & itemStateMask)) {
304: item->itemFlags |= itemChanged;
305: menu->menuFlags |= menuItemChanged;
306: } else
307: item->itemFlags &= ~itemChanged;
308: return(1);
309: }
310:
311: /*
312: * SetItemDisable sets the disable state of item "n" of the menu to "state".
313: */
314: SetItemDisable(menu, n, state)
315: register Menu *menu;
316: register int n;
317: int state;
318: {
319: register MenuItem *item;
320:
321: if(!menu || !menu->menuItems || n < 0)
322: return(0);
323: for(item = menu->menuItems ; n > 0 ; n--)
324: if(!(item = item->nextItem))
325: return(0);
326: if(state)
327: item->itemFlags |= itemSetDisabled;
328: else
329: item->itemFlags &= ~itemSetDisabled;
330: if(((item->itemFlags & itemSetMask) >> itemSetMaskShift) !=
331: (item->itemFlags & itemStateMask)) {
332: item->itemFlags |= itemChanged;
333: menu->menuFlags |= menuItemChanged;
334: } else
335: item->itemFlags &= ~itemChanged;
336: return(1);
337: }
338:
339: /*
340: * TrackMenu does most of the work of displaying the menu and tracking the
341: * mouse.
342: */
343: TrackMenu(menu, event)
344: register Menu *menu;
345: register XButtonPressedEvent *event;
346: {
347: register MenuItem *item;
348: register int i, button;
349: register MenuItem *hilited_item = (MenuItem *)0;
350: register int drawn;
351: XButtonReleasedEvent ev;
352: register int changed;
353: int y, n, hilited_y, hilited_n, in_window;
354: static MenuItem *Mouse_InItem(), *Y_InItem();
355: static Unmap_Menu();
356:
357: /*
358: * Check that things are reasonable.
359: */
360: if(!menu || !event || !menu->menuItems || event->type != ButtonPressed)
361: return(-1);
362: /*
363: * Set the changed flag and clear the menu changed flags.
364: */
365: changed = menu->menuFlags & (menuChanged | menuItemChanged);
366: /*
367: * If the entire menu has changed, throw away any saved pixmap and
368: * then call RecalcMenu().
369: */
370: if(changed & menuChanged) {
371: if(menu->menuSaved)
372: XFreePixmap(menu->menuSaved);
373: menu->menuSaved = (Pixmap)0;
374: if(!Recalc_Menu(menu))
375: return(-1);
376: changed &= ~menuItemChanged;
377: }
378: /*
379: * Now if the window was never created, go ahead and make it. Otherwise
380: * if the menu has changed, resize the window.
381: */
382: if(!menu->menuWindow) {
383: if((menu->menuWindow = XCreateWindow(RootWindow, 0, 0,
384: menu->menuWidth, menu->menuHeight, menu->menuBorderWidth,
385: Gray_Tile, menu->menuBgTile)) == (Window)0)
386: return(-1);
387: XDefineCursor(menu->menuWindow, menu->menuCursor);
388: XSelectInput(menu->menuWindow, ExposeWindow | EnterWindow |
389: LeaveWindow | MouseMoved | ButtonReleased);
390: } else if(changed & menuChanged)
391: XChangeWindow(menu->menuWindow, menu->menuWidth,
392: menu->menuHeight);
393: /*
394: * Figure out where the menu is supposed to go, from the initial button
395: * press, and move the window there. Then map the menu.
396: */
397: if(!Move_Menu(menu, event) || !Map_Menu(menu))
398: return(-1);
399: /*
400: * Try to grab the mouse, over a period of 10 seconds.
401: */
402: for(i = 10 ; ; ) {
403: if(XGrabMouse(menu->menuWindow, menu->menuCursor,
404: ButtonReleased | EnterWindow | LeaveWindow | MouseMoved))
405: break;
406: if(--i <= 0) {
407: Unmap_Menu(menu);
408: return(-1);
409: }
410: sleep(1);
411: }
412: /*
413: * Save away the button that was pressed and use it to match a
414: * corresponding ButtonReleased event.
415: */
416: button = event->detail & 03;
417: /*
418: * Now process events for the menu window.
419: */
420: drawn = 0;
421: for( ; ; ) {
422: XNextEvent(&ev);
423: if(ev.type != ButtonReleased && ev.window != menu->menuWindow) {
424: if(menu->menuEventHandler)
425: (*menu->menuEventHandler)(&ev);
426: continue;
427: }
428: switch(ev.type) {
429: case ExposeWindow:
430: /*
431: * If we have a saved pixmap, display it. Otherwise
432: * redraw the menu and save it away.
433: */
434: if(menu->menuSaved) {
435: XPixmapPut(menu->menuWindow, 0, 0, 0, 0,
436: menu->menuWidth, menu->menuHeight,
437: menu->menuSaved, GXcopy, AllPlanes);
438: /*
439: * If the menuItemChanged flag is still set,
440: * then we need to redraw certain menu items.
441: * ("i" is the vertical position of the top
442: * of the current item.)
443: */
444: if(changed & menuItemChanged) {
445: i = menu->menuItemTop;
446: for(item = menu->menuItems ; item ;
447: item = item->nextItem) {
448: if(item->itemFlags &
449: itemChanged)
450: Modify_Item(menu, item,
451: i);
452: i += item->itemHeight;
453: }
454: }
455: } else
456: Draw_Menu(menu);
457: /*
458: * If the menu has changed in any way and we want to
459: * save the menu, throw away any existing save menu
460: * image and make a new one.
461: */
462: XFlush();
463: if(changed && (menu->menuFlags & menuSaveMenu)) {
464: if(menu->menuSaved)
465: XFreePixmap(menu->menuSaved);
466: menu->menuSaved = XPixmapSave(menu->menuWindow,
467: 0, 0, menu->menuWidth, menu->menuHeight);
468: }
469: /*
470: * See which item the cursor may currently be in. If
471: * it is in a non-disabled item, hilite it.
472: */
473: if(hilited_item = Mouse_InItem(menu, &hilited_y,
474: &hilited_n, &in_window))
475: XPixFill(menu->menuWindow, 0, hilited_y,
476: menu->menuWidth, hilited_item->itemHeight,
477: BlackPixmap, (Bitmap)0, GXinvert, InvertPlane);
478: drawn++;
479: break;
480: case EnterWindow:
481: in_window = TRUE;
482: /* drop through */
483: case MouseMoved:
484: if(!drawn || !in_window)
485: break;
486: /*
487: * See which item the cursor may currently be in. If
488: * the item has changed, unhilite the old one and
489: * then hilited the new one.
490: */
491: y = ((XEnterWindowEvent *)&ev)->y;
492: if((item = Y_InItem(menu, &y, &n)) != hilited_item) {
493: if(hilited_item)
494: XPixFill(menu->menuWindow, 0,
495: hilited_y, menu->menuWidth,
496: hilited_item->itemHeight, BlackPixmap,
497: (Bitmap)0, GXinvert, InvertPlane);
498: if(hilited_item = item) {
499: XPixFill(menu->menuWindow, 0,
500: hilited_y = y, menu->menuWidth,
501: item->itemHeight, BlackPixmap,
502: (Bitmap)0, GXinvert, InvertPlane);
503: hilited_n = n;
504: }
505: }
506: break;
507: case LeaveWindow:
508: if(!drawn)
509: break;
510: /*
511: * Unhilite any window that is currently hilited.
512: */
513: if(hilited_item) {
514: XPixFill(menu->menuWindow, 0, hilited_y,
515: menu->menuWidth, hilited_item->itemHeight,
516: BlackPixmap, (Bitmap)0, GXinvert, InvertPlane);
517: hilited_item = (MenuItem *)0;
518: }
519: in_window = FALSE;
520: break;
521: case ButtonReleased:
522: /*
523: * If the correct button was released, ungrab the mouse
524: * and return the index number of any selected menu
525: * item.
526: */
527: if((ev.detail & 0x3) == button) {
528: if(in_window) {
529: y = ((XButtonReleasedEvent *)&ev)->y;
530: if((item = Y_InItem(menu, &y, &n)) !=
531: hilited_item) {
532: if(hilited_item)
533: XPixFill(menu->menuWindow, 0,
534: hilited_y, menu->menuWidth,
535: hilited_item->itemHeight,
536: BlackPixmap, (Bitmap)0,
537: GXinvert, InvertPlane);
538: if(hilited_item = item) {
539: XPixFill(menu->menuWindow, 0,
540: hilited_y = y, menu->menuWidth,
541: hilited_item->itemHeight,
542: BlackPixmap, (Bitmap)0,
543: GXinvert, InvertPlane);
544: hilited_n = n;
545: }
546: }
547: }
548: XUngrabMouse();
549: menu->menuFlags &= ~(menuChanged |
550: menuItemChanged);
551: Unmap_Menu(menu);
552: XFlush();
553: if(hilited_item)
554: return(menu->menuInitialItem =
555: hilited_n);
556: return(-1);
557: }
558: break;
559: }
560: }
561: }
562:
563: /*
564: * Recalculate all of the various menu and item variables.
565: */
566: static Recalc_Menu(menu)
567: register Menu *menu;
568: {
569: register MenuItem *item;
570: register int max, i, height, fontheight;
571:
572: /*
573: * We must have already gotten the menu font.
574: */
575: if(!menu->menuFontInfo)
576: return(0);
577: /*
578: * Initialize the various max width variables.
579: */
580: fontheight = menu->menuFontInfo->height;
581: height = menu->menuItemTop;
582: menu->menuMaxTextWidth = menu->menuTitleWidth;
583: /*
584: * The item height is the maximum of the font height and the
585: * checkbox height.
586: */
587: max = fontheight;
588: if(checkMarkHeight > max)
589: max = checkMarkHeight;
590: /*
591: * Go through the menu item list.
592: */
593: for(item = menu->menuItems ; item ; item = item->nextItem) {
594: /*
595: * If the item text is a single dash, we assume this is
596: * a line separator and treat it special.
597: */
598: if(strcmp(item->itemText, "-") == 0)
599: height += (item->itemHeight = lineSeparatorHeight);
600: else {
601: height += (item->itemHeight = max);
602: /*
603: * Check the text width with the max value stored in
604: * menu.
605: */
606: if((item->itemTextWidth = XStringWidth(item->itemText,
607: menu->menuFontInfo, 0, 0)) > menu->menuMaxTextWidth)
608: menu->menuMaxTextWidth = item->itemTextWidth;
609: }
610: /*
611: * If the itemChanged flag is set, set the state bits.
612: */
613: if(item->itemFlags & itemChanged) {
614: item->itemFlags = (item->itemFlags & ~itemStateMask) |
615: ((item->itemFlags & itemSetMask) >> itemSetMaskShift);
616: item->itemFlags &= ~itemChanged;
617: }
618: }
619: /*
620: * Set the menu height and then set the menu width.
621: */
622: menu->menuHeight = height;
623: menu->menuWidth = 3 * menu->menuItemPad + menu->menuMaxTextWidth +
624: checkMarkWidth;
625: return(1);
626: }
627:
628: /*
629: * Figure out where to popup the menu, relative to the where the button was
630: * pressed.
631: */
632: static Move_Menu(menu, ev)
633: register Menu *menu;
634: XButtonPressedEvent *ev;
635: {
636: register MenuItem *item;
637: register int n, x, y;
638: int ev_x, ev_y;
639: int total_width;
640: Window subw;
641: extern int dropmenu; /* XXX */
642:
643: /*
644: * Get the coordinates of the mouse when the button was pressed.
645: */
646: XInterpretLocator(RootWindow, &ev_x, &ev_y, &subw, ev->location);
647: /*
648: * Try to popup the menu so that the cursor is centered within the
649: * width of the menu, but compensate if that would run it outside
650: * the display area.
651: */
652: total_width = menu->menuWidth + 2 * menu->menuBorderWidth;
653: if((x = ev_x - total_width / 2) < 0)
654: x = 0;
655: else if(x + total_width > DisplayWidth())
656: x = DisplayWidth() - total_width;
657: if (dropmenu)
658: y = 0;
659: else if(menu->menuInitialItem >= 0) {
660: /*
661: * If we have an inital item, try to popup the menu centered
662: * vertically within this item.
663: *
664: * Look through the item list. "y" is the vertical position
665: * of the top of the current item and "n" is the item number.
666: */
667: y = menu->menuItemTop + menu->menuBorderWidth;
668: for(n = 0, item = menu->menuItems ; ; n++) {
669: /*
670: * On finding the intial item, center within this item.
671: */
672: if(n == menu->menuInitialItem) {
673: y += item->itemHeight / 2;
674: break;
675: }
676: y += item->itemHeight;
677: /*
678: * If we run out of items, turn off the initial item
679: * and treat this as if no initial item.
680: */
681: if(!(item = item->nextItem)) {
682: menu->menuInitialItem = -1;
683: goto noInitial;
684: }
685: }
686: /*
687: * If no initial item, try to popup the menu centered in the item
688: * nearest the center of the menu.
689: */
690: } else {
691: noInitial:
692: /*
693: * Look through the item list. "y" is the vertical position
694: * of the top of the current item and "n" is the vertical
695: * position of the center of the menu.
696: */
697: y = menu->menuItemTop + menu->menuBorderWidth;
698: for(n = menu->menuHeight / 2, item = menu->menuItems ; item ;
699: item = item->nextItem)
700: /*
701: * If the center of the menu is in this item, we
702: * center within this item.
703: */
704: if((y += item->itemHeight) > n) {
705: y -= item->itemHeight / 2;
706: break;
707: }
708: }
709: /*
710: * If the menu extends above outside of the display, warp
711: * the mouse vertically so the menu will all show up.
712: */
713: if((y = ev_y - y) < 0) {
714: XWarpMouse(RootWindow, ev_x, ev_y - y);
715: y = 0;
716: } else if((n = y + menu->menuHeight + 2 * menu->menuBorderWidth
717: - DisplayHeight()) > 0) {
718: XWarpMouse(RootWindow, ev_x, ev_y - n);
719: y -= n;
720: }
721: XMoveWindow(menu->menuWindow, x, y);
722: /*
723: * If we are in freeze mode, save what will be the coordinates of
724: * the save image.
725: */
726: if(menu->menuFlags & menuFreeze) {
727: menu->menuSavedImageX = x;
728: menu->menuSavedImageY = y;
729: }
730: return(1);
731: }
732:
733: /*
734: * Map the menu window.
735: */
736: static Map_Menu(menu)
737: register Menu *menu;
738: {
739: register int i;
740:
741: /*
742: * If we are in freeze mode, save the pixmap underneath where the menu
743: * will be (including the border).
744: */
745: if(menu->menuFlags & menuFreeze) {
746: XGrabServer();
747: i = 2 * menu->menuBorderWidth;
748: if((menu->menuSavedImage = XPixmapSave(RootWindow,
749: menu->menuSavedImageX, menu->menuSavedImageY, menu->menuWidth
750: + i, menu->menuHeight + i)) == (Pixmap)0)
751: return(0);
752: }
753: /*
754: * Actually map the window.
755: */
756: XMapWindow(menu->menuWindow);
757: menu->menuFlags |= menuMapped;
758: return(1);
759: }
760:
761: /*
762: * Draw the entire menu in the blank window.
763: */
764: static Draw_Menu(menu)
765: register Menu *menu;
766: {
767: register MenuItem *item;
768: register int top = menu->menuItemTop;
769: register int x = menu->menuItemPad;
770: register int y, dim;
771:
772: /*
773: * If we have a menu title, draw it first, centered and hilited.
774: */
775: if(menu->menuTitleLength) {
776: XPixSet(menu->menuWindow, 0, 0, menu->menuWidth,
777: top - 1, menu->menuFgColor);
778: XText(menu->menuWindow, (menu->menuWidth -
779: menu->menuTitleWidth) / 2, menu->menuItemPad, menu->menuTitle,
780: menu->menuTitleLength, menu->menuFontInfo->id,
781: menu->menuBgColor, menu->menuFgColor);
782: }
783: /*
784: * For each item in the list, first draw any check mark and then
785: * draw the rest of it.
786: */
787: for(item = menu->menuItems ; item ; item = item->nextItem) {
788: SetStateFlags(item);
789: dim = (item->itemFlags & itemDisabled);
790: /*
791: * Draw the check mark, possibly dimmed, wherever is necessary.
792: */
793: if(item->itemFlags & itemChecked) {
794: XBitmapBitsPut(menu->menuWindow, x, y = top +
795: (item->itemHeight - checkMarkHeight) / 2,
796: checkMarkWidth, checkMarkHeight, dim ? Check_GrayBits :
797: Check_MarkBits, menu->menuFgColor, menu->menuBgColor,
798: (Bitmap)0, GXcopy, AllPlanes);
799: }
800: /*
801: * Draw the item, possibly dimmed.
802: */
803: Draw_Item(menu, item, top, dim);
804: top += item->itemHeight;
805: }
806: }
807:
808: /*
809: * Modify the item at vertical position y. This routine is table driven and
810: * the state and set bits are each 2 bits long, contiguous, the least
811: * significant bits in the flag word and with the state bits in bits 0 & 1.
812: */
813:
814: #define drawCheck 0x10
815: #define removeCheck 0x08
816: #define dimCheck 0x04
817: #define drawItem 0x02
818: #define dimItem 0x01
819:
820: static char Modify_Table[] = {
821: 0x00, 0x02, 0x08, 0x0a, 0x01, 0x00, 0x09, 0x08,
822: 0x10, 0x12, 0x00, 0x12, 0x15, 0x14, 0x05, 0x00
823: };
824:
825: static Modify_Item(menu, item, top)
826: register Menu *menu;
827: register MenuItem *item;
828: int top;
829: {
830: register int x = menu->menuItemPad;
831: register int y;
832: register int center = top + item->itemHeight / 2;
833: register int func = Modify_Table[item->itemFlags &
834: (itemStateMask | itemSetMask)];
835:
836: /*
837: * If we really won't be making a change, return.
838: */
839: if(func == 0)
840: return;
841: /*
842: * Draw the check mark if needed, possibly dimmed.
843: */
844: y = center - (checkMarkHeight / 2);
845: if(func & (drawCheck | dimCheck))
846: XBitmapBitsPut(menu->menuWindow, x, y, checkMarkWidth,
847: checkMarkHeight, (func & dimCheck) ? Check_GrayBits :
848: Check_MarkBits, menu->menuFgColor, menu->menuBgColor,
849: (Bitmap)0, GXcopy, AllPlanes);
850: /*
851: * Remove the check mark if needed.
852: */
853: if(func & removeCheck)
854: XTileSet(menu->menuWindow, x, y, checkMarkWidth,
855: checkMarkHeight, menu->menuBgTile);
856: /*
857: * Call Draw_Item if we need to draw or dim the item.
858: */
859: if((x = func & dimItem) || (func & drawItem))
860: Draw_Item(menu, item, top, x);
861: /*
862: * Update state flags.
863: */
864: SetStateFlags(item);
865: }
866:
867: /*
868: * Draw the item (less check mark) at vertical position y.
869: * Dim the item if "dim" is set.
870: */
871: static Draw_Item(menu, item, y, dim)
872: register Menu *menu;
873: register MenuItem *item;
874: register int y;
875: int dim;
876: {
877: register int x = 2 * menu->menuItemPad + checkMarkWidth;
878: register int center = y + item->itemHeight / 2;
879:
880: /*
881: * If the item text is a single dash, draw a separating line.
882: */
883: if(strcmp(item->itemText, "-") == 0) {
884: XLine(menu->menuWindow, 0, center, menu->menuWidth, center,
885: 1, 1, menu->menuFgColor, GXcopy, AllPlanes);
886: return;
887: }
888: /*
889: * Draw and/or dim the text, centered vertically.
890: */
891: y = center - (menu->menuFontInfo->height / 2);
892: if(dim) {
893: XTileSet(menu->menuWindow, x, y, item->itemTextWidth,
894: menu->menuFontInfo->height, Gray_Tile);
895: XTextPad(menu->menuWindow, x, y, item->itemText,
896: item->itemTextLength, menu->menuFontInfo->id, 0, 0,
897: menu->menuFgColor, menu->menuBgColor, menu->menuFgColor ?
898: GXand : GXor, AllPlanes);
899: } else
900: XText(menu->menuWindow, x, y, item->itemText,
901: item->itemTextLength, menu->menuFontInfo->id,
902: menu->menuFgColor, menu->menuBgColor);
903: }
904:
905: /*
906: * Determine which enabled menu item the mouse is currently in. Return the
907: * top position of this item and its item number. Set inwindow to whether
908: * we are or not.
909: */
910: static MenuItem *Mouse_InItem(menu, top, n, inwindow)
911: register Menu *menu;
912: int *top, *n, *inwindow;
913: {
914: int x, y;
915: Window subw;
916: static MenuItem *Y_InItem();
917:
918: /*
919: * Find out where the mouse is. If its not in the menu window,
920: * return NULL.
921: */
922: XQueryMouse(RootWindow, &x, &y, &subw);
923: if(subw != menu->menuWindow) {
924: *inwindow = FALSE;
925: return((MenuItem *)0);
926: }
927: *inwindow = TRUE;
928: /*
929: * Now get the coordinates relative to the menu window.
930: */
931: XInterpretLocator(menu->menuWindow, &x, &y, &subw, (x << 16) | y);
932: /*
933: * Call Y_InItem().
934: */
935: *top = y;
936: return(Y_InItem(menu, top, n));
937: }
938:
939: /*
940: * Return which enabled item the locator is in. Also return the
941: * top position of this item and its item number. Initial y passed
942: * in top.
943: */
944: static MenuItem *Y_InItem(menu, top, n)
945: register Menu *menu;
946: int *top, *n;
947: {
948: register MenuItem *item;
949: register int t, i;
950: register int y = *top;
951: Window subw;
952:
953: /*
954: * Go through the item list. "t" is the vertical position of the
955: * current item and "i" is its item number.
956: */
957: t = menu->menuItemTop;
958: /*
959: * If the mouse is before the first item, return.
960: */
961: if(y < t)
962: return((MenuItem *)0);
963: for(i = 0, item = menu->menuItems ; item ; i++, item = item->nextItem) {
964: /*
965: * If the y coordinate is within this menu item, then return.
966: * But don't return disable items.
967: */
968: if(t + item->itemHeight > y) {
969: if(item->itemFlags & itemDisabled)
970: return((MenuItem *)0);
971: *top = t;
972: *n = i;
973: return(item);
974: }
975: t += item->itemHeight;
976: }
977: /*
978: * Should never get here.
979: */
980: return((MenuItem *)0);
981: }
982:
983: /*
984: * Unmap_Menu() unmaps a menu, if it is currently mapped.
985: */
986: static Unmap_Menu(menu)
987: register Menu *menu;
988: {
989: register int i;
990:
991: if(!menu || !(menu->menuFlags & menuMapped))
992: return;
993: if(menu->menuFlags & menuFreeze) {
994: XUnmapTransparent(menu->menuWindow);
995: i = 2 * menu->menuBorderWidth;
996: XPixmapPut(RootWindow, 0, 0, menu->menuSavedImageX,
997: menu->menuSavedImageY, menu->menuWidth + i,
998: menu->menuHeight + i, menu->menuSavedImage,
999: GXcopy, AllPlanes);
1000: XFreePixmap(menu->menuSavedImage);
1001: XUngrabServer();
1002: } else
1003: XUnmapWindow(menu->menuWindow);
1004: menu->menuFlags &= ~menuMapped;
1005: }
1006: #endif MODEMENU
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.