|
|
1.1 root 1: #ifndef lint
2: static char *rcsid_Menu_c = "$Header: Menu.c,v 1.11 87/08/03 13:08:09 swick Exp $";
3: #endif lint
4:
5: #include <X11/copyright.h>
6:
7: /*
8: * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
9: *
10: * All Rights Reserved
11: *
12: * Permission to use, copy, modify, and distribute this software and its
13: * documentation for any purpose and without fee is hereby granted,
14: * provided that the above copyright notice appear in all copies and that
15: * both that copyright notice and this permission notice appear in
16: * supporting documentation, and that the name of Digital Equipment
17: * Corporation not be used in advertising or publicity pertaining to
18: * distribution of the software without specific, written prior permission.
19: *
20: *
21: * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
22: * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
23: * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
24: * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
25: * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
26: * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
27: * SOFTWARE.
28: */
29:
30:
31:
32: /*
33: * MODIFICATION HISTORY
34: *
35: * 000 -- M. Gancarz, DEC Ultrix Engineering Group
36: * 001 -- L. Guarino Reid, DEC Ultrix Engineering Group, Western Software Lab
37: * February 16, 1987
38: * Change menu implementation so that it uses EnterWindow, LeaveWindow,
39: * and MouseMotion events to track the mouse, instead of polling.
40: * 002 -- L. Guarino Reid, DEC Ultrix Engineering Group, Western Software Lab
41: * April 30, 1987. Convert to X11.
42: * 003 -- L. Guarino Reid, DEC Ultrix Engineering Group, Western Software Lab
43: * June 18, 1987. Change call to system to handle signals move smoothly.
44: */
45:
46: #ifndef lint
47: static char *sccsid = "@(#)Menu.c 3.8 1/24/86";
48: #endif
49:
50: #include <signal.h>
51: #include "uwm.h"
52:
53: Bool alternateGC; /* true if only 2 colors are used */
54:
55: #define DisplayLine(w, pane, width, height, str, fg, bg, inv) \
56: if (alternateGC) { \
57: if (inv) \
58: XFillRectangle(dpy, w, MenuInvGC, 0, pane, width, height); \
59: else \
60: XDrawString(dpy, w, MenuGC, HMenuPad, pane + VMenuPad + MFontInfo->ascent, str, strlen(str)); \
61: } else { \
62: XSetForeground(dpy, MenuGC, bg); \
63: XFillRectangle(dpy, w, MenuGC, 0, pane, width, height); \
64: XSetForeground(dpy, MenuGC, fg); \
65: XDrawString(dpy, w, MenuGC, HMenuPad, pane + VMenuPad + MFontInfo->ascent, str, strlen(str)); \
66: }
67:
68: /* the following procedure is a copy of the implementation of system,
69: * modified to reset the handling of SIGINT, SIGQUIT, and SIGHUP before
70: * exec-ing
71: */
72: execute(s)
73: char *s;
74: {
75: int status, pid, w;
76: register int (*istat)(), (*qstat)();
77:
78: if ((pid = vfork()) == 0) {
79: signal(SIGINT, SIG_DFL);
80: signal(SIGQUIT, SIG_DFL);
81: signal(SIGHUP, SIG_DFL);
82: execl("/bin/sh", "sh", "-c", s, 0);
83: _exit(127);
84: }
85: istat = signal(SIGINT, SIG_IGN);
86: qstat = signal(SIGQUIT, SIG_IGN);
87: while ((w = wait(&status)) != pid && w != -1)
88: ;
89: if (w == -1)
90: status = -1;
91: signal(SIGINT, istat);
92: signal(SIGQUIT, qstat);
93: return(status);
94: }
95:
96: Bool Menu(window, mask, button, x, y, menu)
97: Window window; /* Event window. */
98: int mask; /* Button/key mask. */
99: int button; /* Button event detail. */
100: int x, y; /* Event mouse position. */
101: MenuInfo *menu;
102: {
103: XEvent button_event; /* Button event packet. */
104: int event_x, event_y; /* location of button event */
105: Bool func_stat; /* Function status return. */
106: Window sub_window; /* Current subwindow. */
107: int cur_item = 0; /* Current menu item. */
108: int hi_lite = 0; /* Current highlighted item. */
109: int i; /* Iteration counter. */
110: int hlfg, hlbg; /* Hi-liter pixels. */
111: MenuLine *ml; /* Menu lines pointer. */
112: char *hlname; /* Pointer to hi-liter name. */
113: char *strbuf; /* String buffer for IsTextNL. */
114: char *malloc();
115:
116: /*
117: * Change the cursor.
118: */
119: XChangeActivePointerGrab(dpy, EVENTMASK, MenuCursor, CurrentTime);
120:
121: /*
122: * Map the menu.
123: */
124: MapMenu(menu, x, y);
125: if (Autoselect) {
126: event_x = (menu->width >> 2) * 3;
127: event_y = (menu->iheight >> 1);
128: XWarpPointer(dpy, None, menu->w, 0, 0, 0, 0, event_x, event_y);
129: goto hilite;
130: }
131: else {
132: XWarpPointer(dpy, None, menu->w, 0, 0, 0, 0,
133: (menu->width >> 2) * 3, menu->iheight >> 1);
134: XFlush(dpy);
135: }
136:
137: /*
138: * Main loop.
139: */
140: while (TRUE) {
141:
142: /*
143: * Get next event for menu.
144: */
145: if (!GetButton(&button_event)) continue;
146: switch (button_event.type) {
147:
148: case LeaveNotify:
149: /*
150: * If the mouse has moved out of the menu sideways, abort
151: * the menu operation. Reset the cursor and unmap the menu.
152: */
153: event_x = ((XLeaveWindowEvent * )&button_event)->x;
154: event_y = ((XLeaveWindowEvent * )&button_event)->y;
155: if (event_x < 0 || event_x > menu->width) {
156: ResetCursor(button);
157: UnmapMenu(menu);
158: return(FALSE);
159: }
160: goto hilite;
161:
162: case EnterNotify:
163: event_x = ((XEnterWindowEvent * )&button_event)->x;
164: event_y = ((XEnterWindowEvent * )&button_event)->y;
165: goto hilite;
166: case MotionNotify:
167: {
168: event_x = ((XPointerMovedEvent * )&button_event)->x;
169: event_y = ((XPointerMovedEvent * )&button_event)->y;
170: hilite:
171: /*
172: * If the mouse has moved below or above the menu, but is still
173: * within the same vertical plane, then simply adjust the values
174: * so the user doesn't fall off the edge.
175: */
176: if (event_y >= menu->height)
177: event_y = menu->height - 1;
178: else if (event_y < 0)
179: event_y = 0;
180:
181: /*
182: * If the mouse has moved to another item in the menu,
183: * highlight the new item.
184: */
185: cur_item = event_y / menu->iheight;
186: if (cur_item != hi_lite) {
187:
188: /*
189: * Remove highlighting on old item.
190: */
191: if (hi_lite) {
192: DisplayLine(menu->w, hi_lite * menu->iheight,
193: menu->width, menu->iheight, hlname,
194: hlfg, hlbg, 1);
195: XFlush(dpy);
196: }
197:
198: /*
199: * Highlight new item.
200: */
201: hi_lite = cur_item;
202: if (cur_item) {
203: for(i = 1, ml = menu->line; ml; i++, ml = ml->next) {
204: if (i == cur_item) break;
205: }
206: DisplayLine(menu->w, cur_item * menu->iheight,
207: menu->width, menu->iheight, ml->name,
208: menu->hlfg.pixel, menu->hlbg.pixel, 1);
209: /* XSetForeground(dpy, MenuGC, menu->hlfg.pixel );
210: XDrawRectangle(dpy, menu->w, MenuGC, 1,
211: cur_item * menu->iheight + 1,
212: menu->width - 3, menu->iheight - 3);
213: */
214: XFlush(dpy);
215: hlfg = ml->fg.pixel;
216: hlbg = ml->bg.pixel;
217: hlname = ml->name;
218: }
219: }
220: break;
221:
222: case ButtonRelease:
223: /* have we released the invoking button? */
224: if (((XButtonReleasedEvent *)&button_event)->button == button) {
225: /*
226: * If no item was selected, simply close the menu and return.
227: */
228: if (!cur_item) {
229: ResetCursor(button);
230: UnmapMenu(menu);
231: return(TRUE);
232: }
233:
234: /*
235: * Get a pointer to the menu line selected.
236: */
237: --cur_item;
238: for(i = 0, ml = menu->line; ml; i++, ml = ml->next) {
239: if (i == cur_item) break;
240: }
241:
242: /*
243: * Perform the selected menu line action.
244: */
245: switch (ml->type) {
246:
247: case IsShellCommand:
248: UnmapMenu(menu);
249: execute(ml->text);
250: break;
251:
252: case IsText:
253: UnmapMenu(menu);
254: XStoreBytes(dpy, ml->text, strlen(ml->text));
255: break;
256:
257: case IsTextNL:
258: UnmapMenu(menu);
259: strbuf = (char *)malloc(strlen(ml->text) + 2);
260: strcpy(strbuf, ml->text);
261: strcat(strbuf, "\n");
262: XStoreBytes(dpy, strbuf, strlen(strbuf));
263: free(strbuf);
264: break;
265:
266: case IsUwmFunction:
267: /* change cursor and grab next button event
268: * to select the target window */
269: if (XGrabPointer( dpy, RootWindow(dpy, scr),
270: TRUE, EVENTMASK, GrabModeAsync,
271: GrabModeAsync, None,
272: TargetCursor, CurrentTime )
273: != GrabSuccess )
274: Error( "Could not grab pointer" );
275: GetContext(
276: &sub_window, &event_x, &event_y);
277: UnmapMenu(menu);
278: if (sub_window != menu->w)
279: func_stat =
280: (*ml->func) (
281: sub_window, mask, button, event_x,
282: event_y);
283: else func_stat = FALSE;
284: if (!func_stat) {
285: /* eat the next ButtonRelease */
286: while (TRUE) {
287: if (GetButton(&button_event) &&
288: button_event.type == ButtonRelease)
289: break;
290: }
291: }
292: XUngrabPointer( dpy, CurrentTime );
293: break;
294:
295: case IsImmFunction:
296: UnmapMenu(menu);
297: (*ml->func) (
298: sub_window, mask, button, event_x,
299: event_y);
300: break;
301:
302: case IsMenuFunction:
303: while (TRUE) {
304: if (!GetButton(&button_event)) continue;
305: if (button_event.type != ButtonPress) continue;
306: if ((((XButtonPressedEvent *)&button_event)->state != mask)
307: || (((XButtonPressedEvent *)&button_event)->button != button))
308: {
309: UnmapMenu(menu);
310: return(TRUE);
311: }
312: break;
313: }
314: UnmapMenu(menu);
315: func_stat =
316: Menu(menu->w, mask, button, x, y, ml->menu);
317: return(func_stat);
318: break;
319:
320: default:
321: Error("Menu -> Internal type error.");
322: }
323: return(TRUE);
324:
325: }
326: /* else a different button was released. Fall through: */
327: default:
328: /*
329: * Some other button event occurred, so abort the menu
330: * operation.
331: */
332: ResetCursor(button);
333: UnmapMenu(menu);
334: return(TRUE);
335:
336: }
337: }
338: }
339: }
340:
341:
342: /*
343: * Create the menu windows for later use.
344: */
345: CreateMenus()
346: {
347: MenuLink *ptr;
348:
349: /*
350: * If MaxColors isn't set, then jam it to an impossibly high
351: * number.
352: */
353: if (MaxColors == 0)
354: MaxColors = 25000;
355:
356: for(ptr = Menus; ptr; ptr = ptr->next)
357: InitMenu(ptr->menu);
358: }
359:
360: /*
361: * Initialize a menu.
362: */
363: InitMenu(menu)
364: MenuInfo *menu;
365: {
366: MenuLine *ml; /* Menu lines pointer. */
367: int width; /* Width of an item name. */
368: int maxwidth; /* Maximum width of item names. */
369: int len; /* Length of an item name. */
370: int count = 1; /* Number of items + 1 for name. */
371:
372: /*
373: * Determine the name of the longest menu item.
374: */
375: maxwidth = XTextWidth(MFontInfo, menu->name, strlen(menu->name));
376: if (maxwidth == 0)
377: Error("InitMenu -> Couldn't get length of menu name");
378:
379: for(ml = menu->line; ml; ml = ml->next) {
380: if ((len = strlen(ml->name)) == 0)
381: break;
382: width = XTextWidth(MFontInfo, ml->name, strlen(ml->name));
383: if (width == 0)
384: Error("InitMenu -> Couldn't get length of menu item name");
385: if (width > maxwidth) maxwidth = width;
386: count++;
387: }
388:
389: /*
390: * Get the color cells for the menu items.
391: */
392: GetMenuColors(menu);
393:
394: /*
395: * Stash the menu parameters in the menu info structure.
396: */
397: menu->iheight = MFontInfo->ascent + MFontInfo->descent + (VMenuPad << 1);
398: menu->height = menu->iheight * count;
399: menu->width = maxwidth + (HMenuPad << 1);
400: menu->image = NULL;
401:
402: /*
403: * Create the menu window.
404: */
405: menu->w = XCreateSimpleWindow(dpy, RootWindow(dpy, scr),
406: 0, 0,
407: menu->width,
408: menu->height,
409: MBorderWidth,
410: MBorder, MBackground);
411: if (menu->w == NULL) Error("InitMenu -> Couldn't create menu window");
412:
413: /*
414: * Store the window name.
415: */
416: XStoreName(dpy, menu->w, menu->name);
417:
418: /*
419: * Define a cursor for the window.
420: */
421: XDefineCursor(dpy, menu->w, MenuCursor);
422:
423: /*
424: * We want enter, leave, and mouse motion events for menus.
425: */
426: XSelectInput(dpy, menu->w,
427: (EnterWindowMask | LeaveWindowMask | PointerMotionMask));
428: }
429:
430: /*
431: * Map a menu.
432: */
433: MapMenu(menu, x, y)
434: MenuInfo *menu;
435: int x, y;
436: {
437: int item;
438: Window w;
439: MenuLine *ml;
440: XWindowChanges values;
441:
442: w = menu->w;
443:
444: /*
445: * Move the menu into place, normalizing the coordinates, if necessary;
446: * then map it.
447: */
448: x -= (menu->width >> 1);
449: if (x < 0) x = 0;
450: else if (x + menu->width >= ScreenWidth)
451: x = ScreenWidth - menu->width - (MBorderWidth << 1);
452: if (y < 0) y = 0;
453: else if (y + menu->height >= ScreenHeight)
454: y = ScreenHeight - menu->height - (MBorderWidth << 1);
455: values.x = x;
456: values.y = y;
457: values.stack_mode = Above;
458: XConfigureWindow(dpy, w, CWX|CWY|CWStackMode, &values);
459:
460: /*
461: * Map the window and draw the text items.
462: */
463: XMapWindow(dpy, w);
464: DisplayLine(w, 0, menu->width, menu->iheight, menu->name,
465: menu->bg.pixel, menu->fg.pixel, 0);
466:
467: if (alternateGC) {
468: XFillRectangle(dpy, menu->w, MenuInvGC, 0, 0,
469: menu->width, menu->iheight);
470: XDrawRectangle(dpy, menu->w, MenuInvGC, 1, 1,
471: menu->width - 3, menu->iheight - 3);
472: } else {
473: XSetForeground(dpy, MenuGC, menu->bg.pixel );
474: XDrawRectangle(dpy, menu->w, MenuGC, 1, 1, menu->width - 3,
475: menu->iheight - 3);
476: }
477:
478: item = menu->iheight;
479: for(ml = menu->line; ml; ml = ml->next) {
480: DisplayLine(w, item, menu->width, menu->iheight, ml->name,
481: ml->fg.pixel, ml->bg.pixel, 0);
482: item += menu->iheight;
483: }
484:
485: /*
486: * Position the mouse cursor in the menu header (or in the first item
487: * if "autoselect" is set).
488: */
489:
490: XFlush(dpy);
491: }
492:
493: /*
494: * Unmap a menu, restoring the contents of the screen underneath
495: * if necessary. (Restore portion is a future.)
496: */
497: UnmapMenu(menu)
498: MenuInfo *menu;
499: {
500: /*
501: * Unmap and flush.
502: */
503: XUnmapWindow(dpy, menu->w);
504: XFlush(dpy);
505: }
506:
507: /*
508: * Get the context for invoking a window manager function.
509: */
510: GetContext(w, x, y)
511: Window *w;
512: int *x, *y;
513: {
514: XEvent button_event; /* Button input event. */
515:
516: while (TRUE) {
517:
518: /*
519: * Get the next mouse button event. Spin our wheels until
520: * a button event is returned (ie. GetButton == TRUE).
521: * Note that mouse events within an icon window are handled
522: * in the "GetButton" function or by the icon's owner if
523: * it is not uwm.
524: */
525: if (!GetButton(&button_event)) continue;
526:
527: /*
528: * If the button event received is not a ButtonPress event
529: * then continue until we find one.
530: */
531: if (button_event.type != ButtonPress) continue;
532:
533: /*
534: * Okay, determine the event window and mouse coordinates.
535: */
536: status = XTranslateCoordinates(dpy,
537: RootWindow(dpy, scr),
538: RootWindow(dpy, scr),
539: ((XButtonPressedEvent *)&button_event)->x,
540: ((XButtonPressedEvent *)&button_event)->y,
541: x, y,
542: w);
543:
544: if (status == FAILURE) continue;
545:
546: if (*w == 0)
547: *w = RootWindow(dpy, scr);
548:
549: return;
550: }
551: }
552:
553: /*
554: * Get the color cells for a menu. This function is slightly brain-damaged
555: * in that once MaxColors <= 1, then it refuses to even try to allocate any
556: * more colors, even though the colors may have already been allocated. It
557: * probably ought to be done right someday.
558: */
559: GetMenuColors(menu)
560: MenuInfo *menu;
561: {
562: register MenuLine *ml; /* Menu lines pointer. */
563:
564: /*
565: * If we have more than 2 colors available, then attempt to get
566: * the color map entries requested by the user.
567: * Otherwise, default to standard black and white.
568: */
569: alternateGC = TRUE; /* assume the best */
570: if (DisplayCells(dpy, scr) > 2) {
571: /*
572: * Get the menu header colors first.
573: */
574: if (!(menu->foreground && menu->background && MaxColors > 1 &&
575: XParseColor(dpy, DefaultColormap(dpy, scr), menu->foreground, &menu->fg) &&
576: XAllocColor(dpy, DefaultColormap(dpy, scr), &menu->fg) &&
577: XParseColor(dpy, DefaultColormap(dpy, scr), menu->background, &menu->bg) &&
578: XAllocColor(dpy, DefaultColormap(dpy, scr), &menu->bg))) {
579: menu->fg.pixel = MTextForground;
580: menu->bg.pixel = MTextBackground;
581: } else {
582: AdjustMaxColors(menu->fg.pixel);
583: AdjustMaxColors(menu->bg.pixel);
584: alternateGC = FALSE;
585: }
586:
587: /*
588: * Get the menu highlight colors.
589: */
590: if (!(menu->fghighlight && menu->bghighlight && MaxColors > 1 &&
591: XParseColor(
592: dpy, DefaultColormap(dpy, scr), menu->fghighlight, &menu->hlfg) &&
593: XAllocColor(dpy, DefaultColormap(dpy, scr), &menu->hlfg) &&
594: XParseColor(
595: dpy, DefaultColormap(dpy, scr), menu->bghighlight, &menu->hlbg) &&
596: XAllocColor(dpy, DefaultColormap(dpy, scr), &menu->hlbg))) {
597: menu->hlfg.pixel = MTextBackground;
598: menu->hlbg.pixel = MTextForground;
599: } else {
600: AdjustMaxColors(menu->hlfg.pixel);
601: AdjustMaxColors(menu->hlbg.pixel);
602: alternateGC = FALSE;
603: }
604:
605: /*
606: * Get the menu item colors.
607: */
608: for(ml = menu->line; ml; ml = ml->next) {
609: if (!(ml->foreground && ml->background && MaxColors > 1 &&
610: XParseColor(dpy, DefaultColormap(dpy, scr), ml->foreground, &ml->fg) &&
611: XAllocColor(dpy, DefaultColormap(dpy, scr), &ml->fg) &&
612: XParseColor(dpy, DefaultColormap(dpy, scr), ml->background, &ml->bg) &&
613: XAllocColor(dpy, DefaultColormap(dpy, scr), &ml->bg))) {
614: ml->fg.pixel = MTextForground;
615: ml->bg.pixel = MTextBackground;
616: } else {
617: AdjustMaxColors(ml->fg.pixel);
618: AdjustMaxColors(ml->bg.pixel);
619: alternateGC = FALSE;
620: }
621: }
622:
623: } else {
624:
625: /*
626: * Only 2 colors available, so default to standard black and white.
627: */
628: menu->fg.pixel = MTextForground;
629: menu->bg.pixel = MTextBackground;
630: menu->hlfg.pixel = MTextBackground;
631: menu->hlbg.pixel = MTextForground;
632: for(ml = menu->line; ml; ml = ml->next) {
633: ml->fg.pixel = MTextForground;
634: ml->bg.pixel = MTextBackground;
635: }
636: }
637: }
638:
639: /*
640: * Decrement "MaxColors" if this pixel value has never been used in a
641: * menu before.
642: */
643: AdjustMaxColors(pixel)
644: int pixel;
645: {
646: register MenuLink *mptr;
647: register MenuLine *lptr;
648: int count = 0;
649:
650: for(mptr = Menus; mptr; mptr = mptr->next) {
651: if (mptr->menu->fg.pixel == pixel) ++count;
652: if (mptr->menu->bg.pixel == pixel) ++count;
653: if (mptr->menu->hlfg.pixel == pixel) ++count;
654: if (mptr->menu->hlbg.pixel == pixel) ++count;
655: for(lptr = mptr->menu->line; lptr; lptr = lptr->next) {
656: if (lptr->fg.pixel == pixel) ++count;
657: if (lptr->bg.pixel == pixel) ++count;
658: }
659: if (count > 1) return;
660: }
661: --MaxColors;
662: }
663:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.