|
|
1.1 root 1: /* $Header: XMenuActivate.c,v 10.19 86/07/11 16:50:09 tony Rel $ */
2: /* Copyright Massachusetts Institute of Technology 1985 */
3:
4: /*
5: * XMenu: MIT Project Athena, X Window system menu package
6: *
7: * XMenuActivate - Maps a given menu to the display and activates
8: * the menu for user selection. The user is allowed to
9: * specify which pane and selection will be current,
10: * the X and Y location of the menu (relative to the
11: * parent window) and the mouse button event mask that
12: * will be used to identify a selection request.
13: *
14: * A menu selection is shown to be current by placing
15: * a highlight box around the selection as the mouse
16: * cursor enters its active region. Inactive selections
17: * will not be highlited. As the mouse cursor moved
18: * from one menu pane to another menu pane the pane being
19: * entered is raised and made current and the pane being
20: * left is lowered.
21: *
22: * Anytime XMenuActivate returns, the p_num and
23: * s_num are left at their last known values (i.e.,
24: * the last known current pane and selection indices).
25: * The following are the defined return states:
26: *
27: * 1) If at any time an error occurs the data
28: * pointer is left untouched and XM_FAILURE
29: * is returned.
30: *
31: * 2) When a selection request is recieved (i.e.,
32: * when the specified mouse event occurs) the
33: * data pointer will be set to the data
34: * associated with the particular selection
35: * current at the time of the selection request
36: * and XM_SUCCESS is returned.
37: *
38: * 3) If no selection was current at the time a
39: * selection request is made the data pointer
40: * will be left untouched and XM_NO_SELECT will
41: * be returned.
42: *
43: * 4) If the selection that was current at the time
44: * a selection request is made is not an active
45: * selection the data pointer will be left
46: * untouched and XM_IA_SELECT will be returned.
47: *
48: * Since X processes events in an asynchronous manner
49: * it is likely that XMenuActivate will encounter
50: * a "foreign event" while it is executing. Foreign
51: * events are handled in one of three ways:
52: *
53: * 1) The event is discarded. This is the default
54: * mode and requires no action on the part of the
55: * application.
56: *
57: * 2) The application has identified an asynchronous
58: * event handler that will be called and the
59: * foreign event handed off to it. Note:
60: * AEQ mode disables this mode temporarily.
61: *
62: * 3) The application has enabled asynchronous event
63: * queueing mode. In this mode all foreign events
64: * will be queued up untill XMenuActivate
65: * terminates; at which time they will be
66: * returned to the X event queue. As long as
67: * AEQ mode is enabled any asynchronous event
68: * handler as temporarily disabled.
69: *
70: * Any events encountered while taking down the menu
71: * (i.e., exposure events from occluded windows) will
72: * automatically be returned to the X event queue after
73: * XMenuActivate has cleaned the queue of any of its own
74: * events that are no longer needed.
75: *
76: * Author: Tony Della Fera, DEC
77: * March 12, 1986
78: *
79: */
80:
81: #include "XMenuInternal.h"
82:
83: int
84: XMenuActivate(menu, p_num, s_num, x_pos, y_pos, event_mask, data)
85: register XMenu *menu; /* Menu to activate. */
86: int *p_num; /* Pane number selected. */
87: int *s_num; /* Selection number selected. */
88: int x_pos; /* X coordinate of menu position. */
89: int y_pos; /* Y coordinate of menu position. */
90: int event_mask; /* Mouse button event mask. */
91: char **data; /* Pointer to return data value. */
92: {
93: register int i; /* Loop counter. */
94: int status; /* X routine call status. */
95: int orig_x; /* Upper left menu origin X coord. */
96: int orig_y; /* Upper left menu origin Y coord. */
97: int save_x; /* Upper left X of save region. */
98: int save_y; /* Upper left Y of save region. */
99: int save_w; /* Width of pixmap save region. */
100: int save_h; /* Height of pixmap save region. */
101: int save_w_offscr; /* Pixmap save width off screen. */
102: int save_h_offscr; /* Pixmap save height off screen. */
103: int x, y; /* Dummy X and Y arguments. */
104: int ret_val; /* Return value. */
105:
106: register XMPane *p_ptr; /* Current XMPane. */
107: register XMPane *event_xmp; /* Event XMPane pointer. */
108: register XMPane *cur_p; /* Current pane. */
109: register XMSelect *cur_s; /* Current selection. */
110: XMWindow *event_xmw; /* Event XMWindow pointer. */
111: XEvent event; /* X input event. */
112: XCrossingEvent *xc_event; /* X window crossing event. */
113: Window xc_window; /* X window crossing event window. */
114:
115: Pixmap save_pixmap; /* Pixmap to save bits under menu. */
116:
117: Bool saved = TRUE; /* Pixmap save succeeded. */
118: Bool selection = FALSE; /* Selection has been made. */
119: Bool forward = TRUE; /* Moving forward in the pane list. */
120: Bool p_lock = TRUE; /* Pane entrance lock. */
121: Bool s_lock = TRUE; /* Selection entrance lock. */
122:
123: /*
124: * Define and allocate a foreign event queue to hold events
125: * that don't belong to XMenu. These events are later restored
126: * to the X event queue.
127: */
128: typedef struct _xmeventque {
129: XEvent event;
130: struct _xmeventque *next;
131: } XMEventQue;
132:
133: XMEventQue *feq = NULL; /* Foreign event queue. */
134: XMEventQue *feq_tmp; /* Foreign event queue temporary. */
135:
136: /*
137: * Are the position arguments are positive?
138: */
139: if ((x_pos <= 0) || (y_pos <= 0)) {
140: _XMErrorCode = XME_ARG_BOUNDS;
141: return(XM_FAILURE);
142: }
143:
144: /*
145: * If there are no panes in the menu then return failure
146: * beacuse the menu is not initialized.
147: */
148: if (menu->p_count == 0) {
149: _XMErrorCode = XME_NOT_INIT;
150: return(XM_FAILURE);
151: }
152:
153: /*
154: * Find the desired current pane.
155: */
156: cur_p = _XMGetPanePtr(menu, *p_num);
157: if (cur_p == NULL) return(XM_FAILURE);
158:
159: /*
160: * Find the desired current selection.
161: * If the current selection index is out of range a null current selection
162: * will be assumed and the cursor will be placed in the current pane
163: * header.
164: */
165: cur_s = _XMGetSelectionPtr(cur_p, *s_num);
166:
167: /*
168: * Check to see that the menu's dependencies have been
169: * recomputed and are up to date. If not, do it now.
170: */
171: if (menu->recompute) XMenuRecompute(menu);
172:
173: /*
174: * If the current pane is active then activate it.
175: */
176: if (cur_p->active) {
177: cur_p->activated = 1;
178: XChangeBackground(cur_p->window, menu->bkgnd_pixmap);
179: }
180:
181: /*
182: * Compute the new menu origin such that the cursor hot point lies
183: * in the center of the desired current pane and selection.
184: */
185: _XMTransToOrigin(menu, cur_p, cur_s, x_pos, y_pos, &orig_x, &orig_y);
186:
187: /*
188: * Then move all the panes into position relative to the newly
189: * computed origin.
190: */
191: for (
192: p_ptr = menu->p_list->next;
193: p_ptr != menu->p_list;
194: p_ptr = p_ptr->next
195: ){
196: XMoveWindow(
197: p_ptr->window,
198: orig_x + p_ptr->window_x,
199: orig_y + p_ptr->window_y
200: );
201: }
202:
203: /*<
204: * If server freeze mode is selected...
205: */
206: if (menu->freeze) {
207: /*
208: * Compute pixmap save region.
209: */
210: save_x = max(orig_x, 0);
211: save_y = max(orig_y, 0);
212: save_w_offscr = (orig_x + menu->width) - DisplayWidth();
213: save_h_offscr = (orig_y + menu->height) - DisplayHeight();
214: if (save_w_offscr < 0) save_w = menu->width;
215: else save_w = menu->width - save_w_offscr;
216: if (save_h_offscr < 0) save_h = menu->height;
217: else save_h = menu->height - save_h_offscr;
218:
219: /*
220: * Grab the X server.
221: */
222: XGrabServer();
223:
224: /*
225: * Save the bits under where the menu will be.
226: */
227: save_pixmap = XPixmapSave(
228: menu->parent,
229: save_x, save_y,
230: save_w, save_h
231: );
232: if (save_pixmap == _X_FAILURE) saved = FALSE;
233: }
234: else {
235: saved = FALSE;
236: }
237:
238: /*
239: * Synchronize the X buffers and the event queue.
240: * From here on, all events in the queue that don't belong to
241: * XMenu are send back to the application via an application
242: * provided event handler or discarded if the application has
243: * not provided an event handler.
244: */
245: XSync(0);
246:
247: /*
248: * Grab the mouse for menu input.
249: */
250: status = XGrabMouse(menu->parent, menu->mouse_cursor, event_mask);
251: if (status == _X_FAILURE) {
252: _XMErrorCode = XME_GRAB_MOUSE;
253: return(XM_FAILURE);
254: }
255:
256: /*
257: * Map the menu panes.
258: */
259: for (
260: p_ptr = menu->p_list->prev;
261: p_ptr != menu->p_list;
262: p_ptr = p_ptr->prev
263: ){
264: if (p_ptr == cur_p) break;
265: XMapWindow(p_ptr->window);
266: }
267: for (
268: p_ptr = menu->p_list->next;
269: p_ptr != menu->p_list;
270: p_ptr = p_ptr->next
271: ){
272: if (p_ptr == cur_p) break;
273: XMapWindow(p_ptr->window);
274: }
275: XMapWindow(cur_p->window);
276:
277: /*
278: * Clear the current selection.
279: */
280: cur_s = NULL;
281:
282: /*
283: * Begin event processing loop.
284: */
285: while (1) {
286: /*
287: * Fetch the next event.
288: */
289: XNextEvent(&event);
290: /*
291: * Dispatch on the event type.
292: */
293: switch (event.type) {
294: case ExposeWindow:
295: event_xmp = (XMPane *)XLookUpAssoc(
296: menu->assoc_tab, event.window
297: );
298: if (event_xmp == NULL) {
299: /*
300: * If AEQ mode is enabled then queue the event.
301: */
302: if (menu->aeq) {
303: feq_tmp = (XMEventQue *)malloc(sizeof(XMEventQue));
304: if (feq_tmp == NULL) {
305: _XMErrorCode = XME_CALLOC;
306: return(XM_FAILURE);
307: }
308: feq_tmp->event = event;
309: feq_tmp->next = feq;
310: feq = feq_tmp;
311: }
312: else if (_XMEventHandler) (*_XMEventHandler)(&event);
313: break;
314: }
315: if (event_xmp == cur_p) {
316: _XMRefreshPane(menu, cur_p);
317: }
318: else _XMRefreshPaneText(menu, event_xmp);
319: break;
320: case EnterWindow:
321: event_xmw = (XMWindow *)XLookUpAssoc(
322: menu->assoc_tab,
323: event.window
324: );
325: if (event_xmw == NULL) break;
326: if (event_xmw->type == SELECTION) {
327: /*
328: * We have entered a selection.
329: */
330: cur_s = (XMSelect *)event_xmw;
331: /*
332: * If the pane we are in is active and the
333: * selection entered is active then activate
334: * the selection.
335: */
336: if (cur_p->active && cur_s->active) {
337: cur_s->activated = 1;
338: _XMRefreshSelection(menu, cur_s);
339: }
340: }
341: else {
342: /*
343: * We have entered a pane.
344: */
345: xc_event = (XCrossingEvent *)&event;
346: status = XInterpretLocator(
347: menu->parent,
348: &x, &y,
349: &xc_window,
350: xc_event->location
351: );
352: if (status == _X_FAILURE) {
353: _XMErrorCode = XME_INTERP_LOC;
354: return(XM_FAILURE);
355: }
356: event_xmp = (XMPane *)XLookUpAssoc(
357: menu->assoc_tab,
358: xc_window
359: );
360: if (event_xmp->window == cur_p->window) break;
361: if (event_xmp->serial > cur_p->serial) forward = TRUE;
362: else forward = FALSE;
363: p_ptr = cur_p;
364: while(1) {
365: if (forward) p_ptr = p_ptr->next;
366: else p_ptr = p_ptr->prev;
367: /*
368: * If the new pane is an active pane then
369: * activate it.
370: */
371: if (p_ptr->active) {
372: p_ptr->activated = 1;
373: XChangeBackground(
374: p_ptr->window,
375: menu->bkgnd_pixmap
376: );
377: XClear(p_ptr->window);
378: }
379: /*
380: * Raise the new pane.
381: */
382: XRaiseWindow(p_ptr->window);
383: /*
384: * If the previous current pane was activated
385: * deactivate it.
386: */
387: if (cur_p->activated) {
388: cur_p->activated = 0;
389: XChangeBackground(
390: cur_p->window,
391: menu->inact_pixmap
392: );
393: _XMRefreshPaneText(menu, cur_p);
394: }
395: /*
396: * Make the new pane the current pane.
397: */
398: cur_p = p_ptr;
399: /*
400: * If we have cycled through to the event
401: * pane we are done.
402: */
403: if (p_ptr->window == event_xmp->window) break;
404: }
405: }
406: break;
407: case LeaveWindow:
408: event_xmw = (XMWindow *)XLookUpAssoc(
409: menu->assoc_tab,
410: event.window
411: );
412: if (event_xmw == NULL) break;
413: /*
414: * If the current selection was activated then
415: * deactivate it.
416: */
417: if (cur_s->activated) {
418: cur_s->activated = 0;
419: _XMRefreshSelection(menu, cur_s);
420: }
421: cur_s = NULL;
422: break;
423: case ButtonPressed:
424: case ButtonReleased:
425: *p_num = cur_p->serial;
426: /*
427: * Check to see if there is a current selecion.
428: */
429: if (cur_s != NULL) {
430: /*
431: * Set the selection number to the current selection.
432: */
433: *s_num = cur_s->serial;
434: /*
435: * If the current selection was activated then
436: * we have a valid selection otherwise we have
437: * an inactive selection.
438: */
439: if (cur_s->activated) {
440: *data = cur_s->data;
441: ret_val = XM_SUCCESS;
442: }
443: else {
444: ret_val = XM_IA_SELECT;
445: }
446: }
447: else {
448: /*
449: * No selection was current.
450: */
451: ret_val = XM_NO_SELECT;
452: }
453: selection = TRUE;
454: break;
455: default:
456: /*
457: * If AEQ mode is enabled then queue the event.
458: */
459: if (menu->aeq) {
460: feq_tmp = (XMEventQue *)malloc(sizeof(XMEventQue));
461: if (feq_tmp == NULL) {
462: _XMErrorCode = XME_CALLOC;
463: return(XM_FAILURE);
464: }
465: feq_tmp->event = event;
466: feq_tmp->next = feq;
467: feq = feq_tmp;
468: }
469: else if (_XMEventHandler) (*_XMEventHandler)(&event);
470: }
471: /*
472: * If a selection has been made, break out of the event loop.
473: */
474: if (selection == TRUE) break;
475: }
476:
477: /*
478: * Unmap the menu.
479: */
480: if (saved) {
481: for (
482: p_ptr = menu->p_list->next;
483: p_ptr != menu->p_list;
484: p_ptr = p_ptr->next
485: ) {
486: XUnmapTransparent(p_ptr->window);
487: }
488: }
489: else {
490: for (
491: p_ptr = menu->p_list->next;
492: p_ptr != menu->p_list;
493: p_ptr = p_ptr->next
494: ) {
495: XUnmapWindow(p_ptr->window);
496: }
497: }
498:
499: /*
500: * Ungrab the mouse.
501: */
502: XUngrabMouse();
503:
504: /*
505: * Restore bits under where the menu was if we managed
506: * to save them and free the pixmap.
507: */
508: if (saved) {
509: XPixmapPut(
510: menu->parent,
511: 0, 0,
512: save_x, save_y,
513: save_w, save_h,
514: save_pixmap,
515: GXcopy, AllPlanes
516: );
517: XFreePixmap(save_pixmap);
518: }
519:
520: /*
521: * Ungrab the X server.
522: */
523: if (menu->freeze) XUngrabServer();
524:
525: /*
526: * If there is a current selection deactivate it.
527: */
528: if (cur_s != NULL) cur_s->activated = 0;
529:
530: /*
531: * Deactivate the current pane.
532: */
533: cur_p->activated = 0;
534: XChangeBackground(cur_p->window, menu->inact_pixmap);
535:
536: /*
537: * Synchronize the X buffers and the X event queue.
538: */
539: XSync(0);
540:
541: /*
542: * Dispatch any events remaining on the queue.
543: */
544: while (QLength()) {
545: /*
546: * Fetch the next event.
547: */
548: XNextEvent(&event);
549:
550: /*
551: * Discard any events left on the queue that belong to XMenu.
552: * All others are held and then returned to the event queue.
553: */
554: switch (event.type) {
555: case ExposeWindow:
556: case EnterWindow:
557: case LeaveWindow:
558: case ButtonPressed:
559: case ButtonReleased:
560: /*
561: * Does this event belong to one of XMenu's windows?
562: * If so, discard it and process the next event.
563: * If not fall through and treat it as a foreign event.
564: */
565: event_xmp = (XMPane *)XLookUpAssoc(
566: menu->assoc_tab,
567: event.window
568: );
569: if (event_xmp != NULL) continue;
570: default:
571: /*
572: * This is a foreign event.
573: * Queue it for later return to the X event queue.
574: */
575: feq_tmp = (XMEventQue *)malloc(sizeof(XMEventQue));
576: if (feq_tmp == NULL) {
577: _XMErrorCode = XME_CALLOC;
578: return(XM_FAILURE);
579: }
580: feq_tmp->event = event;
581: feq_tmp->next = feq;
582: feq = feq_tmp;
583: }
584: }
585:
586: /*
587: * Return any foreign events that were queued to the X event queue.
588: */
589: while (feq != NULL) {
590: feq_tmp = feq;
591: XPutBackEvent(&feq_tmp->event);
592: feq = feq_tmp->next;
593: free((char *)feq_tmp);
594: }
595:
596: /*
597: * Return successfully.
598: */
599: _XMErrorCode = XME_NO_ERROR;
600: return(ret_val);
601: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.