1: #ifndef lint
2: static char * = "$Header: Menu.c,v 10.3 86/02/01 16:23:11 tony Rel $";
3: #endif lint
4:
5: /************************************************************************
6: * *
7: * Copyright (c) 1986 by *
8: * Digital Equipment Corporation, Maynard, MA *
9: * All Rights Reserved. *
10: * *
11: * Permission to use, copy, modify, and distribute this software *
12: * and its documentation is hereby granted only to licensees of *
13: * The Regents of the University of California pursuant to their *
14: * license agreement for the Berkeley Software Distribution *
15: * provided that the following appears on all copies. *
16: * *
17: * "LICENSED FROM DIGITAL EQUIPMENT CORPORATION *
18: * COPYRIGHT (C) 1986 *
19: * DIGITAL EQUIPMENT CORPORATION *
20: * MAYNARD, MA *
21: * ALL RIGHTS RESERVED. *
22: * *
23: * THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT *
24: * NOTICE AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL *
25: * EQUIPMENT CORPORATION. DIGITAL MAKES NO REPRESENTATIONS *
26: * ABOUT SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. IT IS *
27: * SUPPLIED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. *
28: * *
29: * IF THE UNIVERSITY OF CALIFORNIA OR ITS LICENSEES MODIFY *
30: * THE SOFTWARE IN A MANNER CREATING DERIVATIVE COPYRIGHT *
31: * RIGHTS APPROPRIATE COPYRIGHT LEGENDS MAY BE PLACED ON THE *
32: * DERIVATIVE WORK IN ADDITION TO THAT SET FORTH ABOVE." *
33: * *
34: ************************************************************************/
35:
36:
37: /*
38: * MODIFICATION HISTORY
39: *
40: * 000 -- M. Gancarz, DEC Ultrix Engineering Group
41: */
42:
43: #ifndef lint
44: static char *sccsid = "@(#)Menu.c 3.8 1/24/86";
45: #endif
46:
47: #include "uwm.h"
48:
49: #define DisplayLine(w, pane, width, height, str, fg, bg) \
50: XPixSet(w, 0, pane, width, height, bg); \
51: XTextMask(w, HMenuPad, pane + VMenuPad, str, strlen(str), MFont, fg);
52:
53: #define NVERTS 5 /* Number of vertices for hi-liter. */
54:
55: static Vertex vlist[NVERTS]; /* Vertex list for hi-liter. */
56:
57: Menu(window, mask, button, x, y, menu)
58: Window window; /* Event window. */
59: int mask; /* Button/key mask. */
60: short button; /* Button event detail. */
61: int x, y; /* Event mouse position. */
62: MenuInfo *menu;
63: {
64: XButtonEvent button_event; /* Button event packet. */
65: Bool func_stat; /* Function status return. */
66: int cur_x, cur_y; /* Current mouse position. */
67: Window sub_window; /* Current subwindow. */
68: int cur_item = 0; /* Current menu item. */
69: int hi_lite = 0; /* Current highlighted item. */
70: int i; /* Iteration counter. */
71: short hlfg, hlbg; /* Hi-liter pixels. */
72: MenuLine *ml; /* Menu lines pointer. */
73: char *hlname; /* Pointer to hi-liter name. */
74: char *strbuf; /* String buffer for IsTextNL. */
75: char *malloc();
76:
77: /*
78: * Change the cursor.
79: */
80: status = XGrabButton(RootWindow, MenuCursor, mask, EVENTMASK);
81: if (status == FAILURE)
82: Error("Menu -> Unable to grab button and change cursor.");
83:
84: /*
85: * Map the menu.
86: */
87: MapMenu(menu, x, y);
88:
89: /*
90: * Main loop.
91: */
92: while (TRUE) {
93:
94: /*
95: * If no button event, check the current mouse position.
96: */
97: status = XUpdateMouse(menu->w, &cur_x, &cur_y, &sub_window);
98: if (status == FAILURE) continue;
99:
100: /*
101: * If the mouse has moved out of the menu sideways, abort
102: * the menu operation. Reset the cursor and unmap the menu.
103: */
104: if (cur_x < 0 || cur_x > menu->width) {
105: UnmapMenu(menu, mask);
106: return(FALSE);
107: }
108:
109: /*
110: * If the mouse has moved below or above the menu, but is still
111: * within the same vertical plane, then simply adjust the values
112: * so the user doesn't fall off the edge.
113: */
114: if (cur_y >= menu->height) cur_y = menu->height - 1;
115: else if (cur_y < 0) cur_y = 0;
116:
117: /*
118: * If the mouse has moved to another item in the menu,
119: * highlight the new item.
120: */
121: cur_item = cur_y / menu->iheight;
122: if (cur_item != hi_lite) {
123:
124: /*
125: * Remove highlighting on old item.
126: */
127: if (hi_lite) {
128: DisplayLine(menu->w, hi_lite * menu->iheight,
129: menu->width, menu->iheight, hlname,
130: hlfg, hlbg);
131: }
132:
133: /*
134: * Highlight new item.
135: */
136: if (cur_item) {
137: for(i = 1, ml = menu->line; ml; i++, ml = ml->next) {
138: if (i == cur_item) break;
139: }
140: DisplayLine(menu->w, cur_item * menu->iheight,
141: menu->width, menu->iheight, ml->name,
142: menu->hlfg.pixel, menu->hlbg.pixel);
143: vlist[0].y = cur_item * menu->iheight + 1;
144: XDraw(menu->w, vlist, NVERTS, 1, 1,
145: menu->hlfg.pixel, GXcopy, AllPlanes);
146: }
147: hi_lite = cur_item;
148: hlfg = ml->fg.pixel;
149: hlbg = ml->bg.pixel;
150: hlname = ml->name;
151: }
152:
153: /*
154: * Check to see if we have a change in the mouse buttons.
155: * This means the user has selected an item or aborted the
156: * operation.
157: */
158: if (XPending() && GetButton(&button_event)) {
159:
160: /*
161: * Was button released?
162: */
163: if ((button_event.type == ButtonReleased) &&
164: ((button_event.detail & ValueMask) == button)) {
165: break;
166: } else {
167:
168: /*
169: * Some other button event occurred, so abort the menu
170: * operation.
171: */
172: UnmapMenu(menu, mask);
173: return(TRUE);
174: }
175: }
176: }
177:
178: /*
179: * If no item was selected, simply close the menu and return.
180: */
181: if (!cur_item) {
182: UnmapMenu(menu, mask);
183: return(TRUE);
184: }
185:
186: /*
187: * Get a pointer to the menu line selected.
188: */
189: --cur_item;
190: for(i = 0, ml = menu->line; ml; i++, ml = ml->next) {
191: if (i == cur_item) break;
192: }
193:
194: /*
195: * Perform the selected menu line action.
196: */
197: switch (ml->type) {
198:
199: case IsShellCommand:
200: UnmapMenu(menu, mask);
201: system(ml->text);
202: break;
203:
204: case :
205: UnmapMenu(menu, mask);
206: XStoreBytes(ml->text, strlen(ml->text));
207: break;
208:
209: case :
210: UnmapMenu(menu, mask);
211: strbuf = (char *)malloc(strlen(ml->text) + 2);
212: strcpy(strbuf, ml->text);
213: strcat(strbuf, "\n");
214: XStoreBytes(strbuf, strlen(strbuf));
215: free(strbuf);
216: break;
217:
218: case :
219: GetContext(&sub_window, &cur_x, &cur_y);
220: UnmapMenu(menu, mask);
221: if (sub_window != menu->w)
222: (*ml->func) (sub_window, mask, button, cur_x, cur_y);
223: break;
224:
225: case :
226: UnmapMenu(menu, mask);
227: (*ml->func) (sub_window, mask, button, cur_x, cur_y);
228: break;
229:
230: case :
231: while (TRUE) {
232: if (!GetButton(&button_event)) continue;
233: if (button_event.type != ButtonPressed) continue;
234: if ((KeyMask(button_event.detail) != KeyMask(mask)) ||
235: ((button_event.detail & ButtonMods) != button)) {
236: UnmapMenu(menu, mask);
237: return(TRUE);
238: }
239: break;
240: }
241: UnmapMenu(menu, mask);
242: func_stat = Menu(menu->w, mask, button, x, y, ml->menu);
243: return(func_stat);
244: break;
245:
246: default:
247: Error("Menu -> Internal type error.");
248: }
249: return(TRUE);
250: }
251:
252: /*
253: * Create the menu windows for later use.
254: */
255: ()
256: {
257: MenuLink *ptr;
258:
259: /*
260: * If MaxColors isn't set, then jam it to an impossibly high
261: * number.
262: */
263: if (MaxColors == 0)
264: MaxColors = 25000;
265:
266: for(ptr = Menus; ptr; ptr = ptr->next)
267: InitMenu(ptr->menu);
268: }
269:
270: /*
271: * Initialize a menu.
272: */
273: (menu)
274: MenuInfo *menu;
275: {
276: MenuLine *ml; /* Menu lines pointer. */
277: int width; /* Width of an item name. */
278: int maxwidth; /* Maximum width of item names. */
279: int len; /* Length of an item name. */
280: int count = 1; /* Number of items + 1 for name. */
281:
282: /*
283: * Determine the name of the longest menu item.
284: */
285: maxwidth = XQueryWidth(menu->name, MFont);
286: if (maxwidth == 0)
287: Error("InitMenu -> Couldn't get length of menu name");
288:
289: for(ml = menu->line; ml; ml = ml->next) {
290: if ((len = strlen(ml->name)) == 0)
291: break;
292: width = XQueryWidth(ml->name, MFont);
293: if (width == 0) Error("InitMenu -> Couldn't get length of menu item name");
294: if (width > maxwidth) maxwidth = width;
295: count++;
296: }
297:
298: /*
299: * Get the color cells for the menu items.
300: */
301: GetMenuColors(menu);
302:
303: /*
304: * Stash the menu parameters in the menu info structure.
305: */
306: menu->iheight = MFontInfo.height + (VMenuPad << 1);
307: menu->height = menu->iheight * count;
308: menu->width = maxwidth + (HMenuPad << 1);
309: menu->image = NULL;
310:
311: /*
312: * Create the menu window.
313: */
314: menu->w = XCreateWindow(RootWindow,
315: 0, 0,
316: menu->width,
317: menu->height,
318: MBorderWidth,
319: MBorder, MBackground);
320: if (menu->w == NULL) Error("InitMenu -> Couldn't create menu window");
321:
322: /*
323: * Store the window name.
324: */
325: XStoreName(menu->w, menu->name);
326:
327: /*
328: * Define a cursor for the window.
329: */
330: XDefineCursor(menu->w, MenuCursor);
331: }
332:
333: /*
334: * Map a menu.
335: */
336: (menu, x, y)
337: MenuInfo *menu;
338: int x, y;
339: {
340: int item;
341: Window w;
342: MenuLine *ml;
343:
344: w = menu->w;
345:
346: /*
347: * Move the menu into place, normalizing the coordinates, if necessary;
348: * then map it.
349: */
350: x -= (menu->width >> 1);
351: if (x < 0) x = 0;
352: else if (x + menu->width >= ScreenWidth)
353: x = ScreenWidth - menu->width - (MBorderWidth << 1);
354: if (y < 0) y = 0;
355: else if (y + menu->height >= ScreenHeight)
356: y = ScreenHeight - menu->height - (MBorderWidth << 1);
357: XMoveWindow(w, x, y);
358:
359: /*
360: * Map the window and draw the text items.
361: */
362: XMapWindow(w);
363: DisplayLine(w, 0, menu->width, menu->iheight, menu->name,
364: menu->bg.pixel, menu->fg.pixel);
365:
366: SetUpVlist(menu);
367: vlist[0].x = 1;
368: vlist[0].y = 1;
369: XDraw(menu->w, vlist, NVERTS, 1, 1, menu->bg.pixel, GXcopy, AllPlanes);
370: item = menu->iheight;
371: for(ml = menu->line; ml; ml = ml->next) {
372: DisplayLine(w, item, menu->width, menu->iheight, ml->name,
373: ml->fg.pixel, ml->bg.pixel);
374: item += menu->iheight;
375: }
376:
377: /*
378: * Position the mouse cursor in the menu header (or in the first item
379: * if "autoselect" is set).
380: */
381: if (Autoselect)
382: XWarpMouse(w, (menu->width >> 2) * 3, (menu->iheight >> 1) * 3);
383: else XWarpMouse(w, (menu->width >> 2) * 3, menu->iheight >> 1);
384:
385: XFlush();
386: }
387:
388: /*
389: * Unmap a menu, restoring the contents of the screen underneath
390: * if necessary. (Restore portion is a future.)
391: */
392: (menu, mask)
393: MenuInfo *menu;
394: int mask;
395: {
396: /*
397: * Restore the main cursor.
398: */
399: Grab((short)mask);
400:
401: /*
402: * Unmap and flush.
403: */
404: XUnmapWindow(menu->w);
405: XFlush();
406: }
407:
408: /*
409: * Get the context for invoking a window manager function.
410: */
411: GetContext(w, x, y)
412: Window *w;
413: int *x, *y;
414: {
415: XButtonEvent button_event; /* Button input event. */
416:
417: while (TRUE) {
418:
419: /*
420: * Get the next mouse button event. Spin our wheels until
421: * a button event is returned (ie. GetButton == TRUE).
422: * Note that mouse events within an icon window are handled
423: * in the "GetButton" function or by the icon's owner if
424: * it is not uwm.
425: */
426: if (!GetButton(&button_event)) continue;
427:
428: /*
429: * If the button event received is not a ButtonPressed event
430: * then continue until we find one.
431: */
432: if (button_event.type != ButtonPressed) continue;
433:
434: /*
435: * Okay, determine the event window and mouse coordinates.
436: */
437: status = XInterpretLocator(RootWindow,
438: x, y,
439: w,
440: button_event.location);
441:
442: if (status == FAILURE) continue;
443:
444: if (*w == 0)
445: *w = RootWindow;
446:
447: return;
448: }
449: }
450:
451: /*
452: * Get the color cells for a menu. This function is slightly brain-damaged
453: * in that once MaxColors <= 1, then it refuses to even try to allocate any
454: * more colors, even though the colors may have already been allocated. It
455: * probably ought to be done right someday.
456: */
457: (menu)
458: MenuInfo *menu;
459: {
460: register MenuLine *ml; /* Menu lines pointer. */
461:
462: /*
463: * If we have more than 2 colors available, then attempt to get
464: * the color map entries requested by the user.
465: * Otherwise, default to standard black and white.
466: */
467: if (DisplayCells() > 2) {
468:
469: /*
470: * Get the menu header colors first.
471: */
472: if (!(menu->foreground && menu->background && MaxColors > 1 &&
473: XParseColor(menu->foreground, &menu->fg) &&
474: XGetHardwareColor(&menu->fg) &&
475: XParseColor(menu->background, &menu->bg) &&
476: XGetHardwareColor(&menu->bg))) {
477: menu->fg.pixel = MTextForground;
478: menu->bg.pixel = MTextBackground;
479: } else {
480: AdjustMaxColors(menu->fg.pixel);
481: AdjustMaxColors(menu->bg.pixel);
482: }
483:
484: /*
485: * Get the menu highlight colors.
486: */
487: if (!(menu->fghighlight && menu->bghighlight && MaxColors > 1 &&
488: XParseColor(menu->fghighlight, &menu->hlfg) &&
489: XGetHardwareColor(&menu->hlfg) &&
490: XParseColor(menu->bghighlight, &menu->hlbg) &&
491: XGetHardwareColor(&menu->hlbg))) {
492: menu->hlfg.pixel = MTextBackground;
493: menu->hlbg.pixel = MTextForground;
494: } else {
495: AdjustMaxColors(menu->hlfg.pixel);
496: AdjustMaxColors(menu->hlbg.pixel);
497: }
498:
499: /*
500: * Get the menu item colors.
501: */
502: for(ml = menu->line; ml; ml = ml->next) {
503: if (!(ml->foreground && ml->background && MaxColors > 1 &&
504: XParseColor(ml->foreground, &ml->fg) &&
505: XGetHardwareColor(&ml->fg) &&
506: XParseColor(ml->background, &ml->bg) &&
507: XGetHardwareColor(&ml->bg))) {
508: ml->fg.pixel = MTextForground;
509: ml->bg.pixel = MTextBackground;
510: } else {
511: AdjustMaxColors(ml->fg.pixel);
512: AdjustMaxColors(ml->bg.pixel);
513: }
514: }
515:
516: } else {
517:
518: /*
519: * Only 2 colors available, so default to standard black and white.
520: */
521: menu->fg.pixel = MTextForground;
522: menu->bg.pixel = MTextBackground;
523: menu->hlfg.pixel = MTextBackground;
524: menu->hlbg.pixel = MTextForground;
525: for(ml = menu->line; ml; ml = ml->next) {
526: ml->fg.pixel = MTextForground;
527: ml->bg.pixel = MTextBackground;
528: }
529: }
530: }
531:
532: /*
533: * Decrement "MaxColors" if this pixel value has never been used in a
534: * menu before.
535: */
536: AdjustMaxColors(pixel)
537: int pixel;
538: {
539: register MenuLink *mptr;
540: register MenuLine *lptr;
541: int count = 0;
542:
543: for(mptr = Menus; mptr; mptr = mptr->next) {
544: if (mptr->menu->fg.pixel == pixel) ++count;
545: if (mptr->menu->bg.pixel == pixel) ++count;
546: if (mptr->menu->hlfg.pixel == pixel) ++count;
547: if (mptr->menu->hlbg.pixel == pixel) ++count;
548: for(lptr = mptr->menu->line; lptr; lptr = lptr->next) {
549: if (lptr->fg.pixel == pixel) ++count;
550: if (lptr->bg.pixel == pixel) ++count;
551: }
552: if (count > 1) return;
553: }
554: --MaxColors;
555: }
556:
557: /*
558: * Set up the vertex list for the hi-liter.
559: */
560: SetUpVlist(menu)
561: MenuInfo *menu;
562: {
563: vlist[1].x = menu->width - 3;
564: vlist[1].y = 0;
565: vlist[2].x = 0;
566: vlist[2].y = menu->iheight - 3;
567: vlist[3].x = (short)(0 - menu->width + 3);
568: vlist[3].y = 0;
569: vlist[4].x = 0;
570: vlist[4].y = (short)(0 - menu->iheight + 3);
571: vlist[1].flags = vlist[2].flags = vlist[3].flags =
572: vlist[4].flags = VertexRelative;
573: }