|
|
1.1 root 1: /* $Header: Activate.c,v 1.14 87/09/10 20:30:01 chris Exp $ */
2: /* Copyright Massachusetts Institute of Technology 1985 */
3:
4: #include <X11/copyright.h>
5:
6: /*
7: * XMenu: MIT Project Athena, X Window system menu package
8: *
9: * XMenuActivate - Maps a given menu to the display and activates
10: * the menu for user selection. The user is allowed to
11: * specify which pane and selection will be current,
12: * the X and Y location of the menu (relative to the
13: * parent window) and the mouse button event mask that
14: * will be used to identify a selection request.
15: *
16: * A menu selection is shown to be current by placing
17: * a highlight box around the selection as the mouse
18: * cursor enters its active region. Inactive selections
19: * will not be highlited. As the mouse cursor moved
20: * from one menu pane to another menu pane the pane being
21: * entered is raised and made current and the pane being
22: * left is lowered.
23: *
24: * Anytime XMenuActivate returns, the p_num and
25: * s_num are left at their last known values (i.e.,
26: * the last known current pane and selection indices).
27: * The following are the defined return states:
28: *
29: * 1) If at any time an error occurs the data
30: * pointer is left untouched and XM_FAILURE
31: * is returned.
32: *
33: * 2) When a selection request is recieved (i.e.,
34: * when the specified mouse event occurs) the
35: * data pointer will be set to the data
36: * associated with the particular selection
37: * current at the time of the selection request
38: * and XM_SUCCESS is returned.
39: *
40: * 3) If no selection was current at the time a
41: * selection request is made the data pointer
42: * will be left untouched and XM_NO_SELECT will
43: * be returned.
44: *
45: * 4) If the selection that was current at the time
46: * a selection request is made is not an active
47: * selection the data pointer will be left
48: * untouched and XM_IA_SELECT will be returned.
49: *
50: * Since X processes events in an asynchronous manner
51: * it is likely that XMenuActivate will encounter
52: * a "foreign event" while it is executing. Foreign
53: * events are handled in one of three ways:
54: *
55: * 1) The event is discarded. This is the default
56: * mode and requires no action on the part of the
57: * application.
58: *
59: * 2) The application has identified an asynchronous
60: * event handler that will be called and the
61: * foreign event handed off to it. Note:
62: * AEQ mode disables this mode temporarily.
63: *
64: * 3) The application has enabled asynchronous event
65: * queueing mode. In this mode all foreign events
66: * will be queued up untill XMenuActivate
67: * terminates; at which time they will be
68: * returned to the X event queue. As long as
69: * AEQ mode is enabled any asynchronous event
70: * handler as temporarily disabled.
71: *
72: * Any events encountered while taking down the menu
73: * (i.e., exposure events from occluded windows) will
74: * automatically be returned to the X event queue after
75: * XMenuActivate has cleaned the queue of any of its own
76: * events that are no longer needed.
77: *
78: * Author: Tony Della Fera, DEC
79: * March 12, 1986
80: *
81: */
82:
83: #include "XMenuInternal.h"
84:
85: int
86: XMenuActivate(display, menu, p_num, s_num, x_pos, y_pos, event_mask, data)
87: register Display *display; /* Display to put menu on. */
88: register XMenu *menu; /* Menu to activate. */
89: int *p_num; /* Pane number selected. */
90: int *s_num; /* Selection number selected. */
91: int x_pos; /* X coordinate of menu position. */
92: int y_pos; /* Y coordinate of menu position. */
93: unsigned int event_mask; /* Mouse button event mask. */
94: char **data; /* Pointer to return data value. */
95: {
96: int status; /* X routine call status. */
97: int orig_x; /* Upper left menu origin X coord. */
98: int orig_y; /* Upper left menu origin Y coord. */
99: int ret_val; /* Return value. */
100:
101: register XMPane *p_ptr; /* Current XMPane. */
102: register XMPane *event_xmp; /* Event XMPane pointer. */
103: register XMPane *cur_p; /* Current pane. */
104: register XMSelect *cur_s; /* Current selection. */
105: XMWindow *event_xmw; /* Event XMWindow pointer. */
106: XEvent event; /* X input event. */
107: XEvent peek_event; /* X input peek ahead event. */
108:
109: Bool selection = False; /* Selection has been made. */
110: Bool forward = True; /* Moving forward in the pane list. */
111:
112: Window root, child;
113: int root_x, root_y, win_x, win_y;
114: unsigned int mask;
115:
116: /*
117: * Define and allocate a foreign event queue to hold events
118: * that don't belong to XMenu. These events are later restored
119: * to the X event queue.
120: */
121: typedef struct _xmeventque {
122: XEvent event;
123: struct _xmeventque *next;
124: } XMEventQue;
125:
126: XMEventQue *feq = NULL; /* Foreign event queue. */
127: XMEventQue *feq_tmp; /* Foreign event queue temporary. */
128:
129: /*
130: * If there are no panes in the menu then return failure
131: * because the menu is not initialized.
132: */
133: if (menu->p_count == 0) {
134: _XMErrorCode = XME_NOT_INIT;
135: return(XM_FAILURE);
136: }
137:
138: /*
139: * Find the desired current pane.
140: */
141: cur_p = _XMGetPanePtr(menu, *p_num);
142: if (cur_p == NULL) {
143: return(XM_FAILURE);
144: }
145: cur_p->activated = cur_p->active;
146:
147: /*
148: * Find the desired current selection.
149: * If the current selection index is out of range a null current selection
150: * will be assumed and the cursor will be placed in the current pane
151: * header.
152: */
153: cur_s = _XMGetSelectionPtr(cur_p, *s_num);
154:
155: /*
156: * Compute origin of menu so that cursor is in
157: * Correct pane and selection.
158: */
159: _XMTransToOrigin(display,
160: menu,
161: cur_p, cur_s,
162: x_pos, y_pos,
163: &orig_x, &orig_y);
164: menu->x_pos = orig_x; /* Store X and Y coords of menu. */
165: menu->y_pos = orig_y;
166:
167: if (XMenuRecompute(display, menu) == XM_FAILURE) {
168: return(XM_FAILURE);
169: }
170:
171: /*
172: * Flush the window creation queue.
173: * This batches all window creates since lazy evaluation
174: * is more efficient than individual evaluation.
175: * This routine also does an XFlush().
176: */
177: if (_XMWinQueFlush(display, menu, cur_p, cur_s) == _FAILURE) {
178: return(XM_FAILURE);
179: }
180:
181: /*
182: * Make sure windows are in correct order (in case we were passed
183: * an already created menu in incorrect order.)
184: */
185: for(p_ptr = menu->p_list->next; p_ptr != cur_p; p_ptr = p_ptr->next)
186: XRaiseWindow(display, p_ptr->window);
187: for(p_ptr = menu->p_list->prev; p_ptr != cur_p->prev; p_ptr = p_ptr->prev)
188: XRaiseWindow(display, p_ptr->window);
189:
190: /*
191: * Make sure all selection windows are mapped.
192: */
193: for (
194: p_ptr = menu->p_list->next;
195: p_ptr != menu->p_list;
196: p_ptr = p_ptr->next
197: ){
198: XMapSubwindows(display, p_ptr->window);
199: }
200:
201: /*
202: * Synchronize the X buffers and the event queue.
203: * From here on, all events in the queue that don't belong to
204: * XMenu are sent back to the application via an application
205: * provided event handler or discarded if the application has
206: * not provided an event handler.
207: */
208: XSync(display, 0);
209:
210: /*
211: * Grab the mouse for menu input.
212: */
213:
214: status = XGrabPointer(
215: display,
216: menu->parent,
217: True,
218: event_mask,
219: GrabModeAsync,
220: GrabModeAsync,
221: None,
222: menu->mouse_cursor,
223: CurrentTime
224: );
225: if (status == _X_FAILURE) {
226: _XMErrorCode = XME_GRAB_MOUSE;
227: return(XM_FAILURE);
228: }
229:
230: /*
231: * Map the menu panes.
232: */
233: XMapWindow(display, cur_p->window);
234: for (p_ptr = menu->p_list->next;
235: p_ptr != cur_p;
236: p_ptr = p_ptr->next)
237: XMapWindow(display, p_ptr->window);
238: for (p_ptr = cur_p->next;
239: p_ptr != menu->p_list;
240: p_ptr = p_ptr->next)
241: XMapWindow(display, p_ptr->window);
242:
243: XRaiseWindow(display, cur_p->window); /* Make sure current */
244: /* pane is on top. */
245:
246: cur_s = NULL; /* Clear current selection. */
247:
248: /*
249: * Begin event processing loop.
250: */
251: while (1) {
252: XNextEvent(display, &event); /* Get next event. */
253: switch (event.type) { /* Dispatch on the event type. */
254: case Expose:
255: event_xmp = (XMPane *)XLookUpAssoc(display,
256: menu->assoc_tab,
257: event.xexpose.window);
258: if (event_xmp == NULL) {
259: /*
260: * If AEQ mode is enabled then queue the event.
261: */
262: if (menu->aeq) {
263: feq_tmp = (XMEventQue *)malloc(sizeof(XMEventQue));
264: if (feq_tmp == NULL) {
265: _XMErrorCode = XME_CALLOC;
266: return(XM_FAILURE);
267: }
268: feq_tmp->event = event;
269: feq_tmp->next = feq;
270: feq = feq_tmp;
271: }
272: else if (_XMEventHandler) (*_XMEventHandler)(&event);
273: break;
274: }
275: if (event_xmp->activated) {
276: XSetWindowBackground(display,
277: event_xmp->window,
278: menu->bkgnd_color);
279: }
280: else {
281: XSetWindowBackgroundPixmap(display,
282: event_xmp->window,
283: menu->inact_pixmap);
284: }
285: _XMRefreshPane(display, menu, event_xmp);
286: break;
287: case EnterNotify:
288: /*
289: * First wait a small period of time, and see
290: * if another EnterNotify event follows hard on the
291: * heels of this one. i.e., the user is simply
292: * "passing through". If so, ignore this one.
293: */
294:
295: event_xmw = (XMWindow *)XLookUpAssoc(display,
296: menu->assoc_tab,
297: event.xcrossing.window);
298: if (event_xmw == NULL) break;
299: if (event_xmw->type == SELECTION) {
300: /*
301: * We have entered a selection.
302: */
303: if (XPending(display) == 0) usleep(150000);
304: if (XPending(display) != 0) {
305: XPeekEvent(display, &peek_event);
306: if(peek_event.type == LeaveNotify) {
307: break;
308: }
309: }
310: cur_s = (XMSelect *)event_xmw;
311: /*
312: * If the pane we are in is active and the
313: * selection entered is active then activate
314: * the selection.
315: */
316: if (cur_p->active && cur_s->active) {
317: cur_s->activated = 1;
318: _XMRefreshSelection(display, menu, cur_s);
319: }
320: }
321: else {
322: /*
323: * We have entered a pane.
324: */
325: if (XPending(display) == 0) usleep(150000);
326: if (XPending(display) != 0) {
327: XPeekEvent(display, &peek_event);
328: if (peek_event.type == EnterNotify) break;
329: }
330: XQueryPointer(display,
331: menu->parent,
332: &root, &child,
333: &root_x, &root_y,
334: &win_x, &win_y,
335: &mask);
336: event_xmp = (XMPane *)XLookUpAssoc(display,
337: menu->assoc_tab,
338: child);
339: if (event_xmp == NULL) break;
340: if (event_xmp == cur_p) break;
341: if (event_xmp->serial > cur_p->serial) forward = True;
342: else forward = False;
343: p_ptr = cur_p;
344: while (p_ptr != event_xmp) {
345: if (forward) p_ptr = p_ptr->next;
346: else p_ptr = p_ptr->prev;
347: XRaiseWindow(display, p_ptr->window);
348: }
349: if (cur_p->activated) {
350: cur_p->activated = False;
351: XSetWindowBackgroundPixmap(display,
352: cur_p->window,
353: menu->inact_pixmap);
354: _XMRefreshPane(display, menu, cur_p);
355: }
356: if (event_xmp->active) event_xmp->activated = True;
357: cur_p = event_xmp;
358: }
359: break;
360: case LeaveNotify:
361: event_xmw = (XMWindow *)XLookUpAssoc(
362: display,
363: menu->assoc_tab,
364: event.xcrossing.window
365: );
366: if (event_xmw == NULL) break;
367: if(cur_s == NULL) break;
368:
369: /*
370: * If the current selection was activated then
371: * deactivate it.
372: */
373: if (cur_s->activated) {
374: cur_s->activated = False;
375: _XMRefreshSelection(display, menu, cur_s);
376: }
377: cur_s = NULL;
378: break;
379:
380: case ButtonPress:
381: case ButtonRelease:
382: *p_num = cur_p->serial;
383: /*
384: * Check to see if there is a current selecion.
385: */
386: if (cur_s != NULL) {
387: /*
388: * Set the selection number to the current selection.
389: */
390: *s_num = cur_s->serial;
391: /*
392: * If the current selection was activated then
393: * we have a valid selection otherwise we have
394: * an inactive selection.
395: */
396: if (cur_s->activated) {
397: *data = cur_s->data;
398: ret_val = XM_SUCCESS;
399: }
400: else {
401: ret_val = XM_IA_SELECT;
402: }
403: }
404: else {
405: /*
406: * No selection was current.
407: */
408: ret_val = XM_NO_SELECT;
409: }
410: selection = True;
411: break;
412: default:
413: /*
414: * If AEQ mode is enabled then queue the event.
415: */
416: if (menu->aeq) {
417: feq_tmp = (XMEventQue *)malloc(sizeof(XMEventQue));
418: if (feq_tmp == NULL) {
419: _XMErrorCode = XME_CALLOC;
420: return(XM_FAILURE);
421: }
422: feq_tmp->event = event;
423: feq_tmp->next = feq;
424: feq = feq_tmp;
425: }
426: else if (_XMEventHandler) (*_XMEventHandler)(&event);
427: }
428: /*
429: * If a selection has been made, break out of the event loop.
430: */
431: if (selection == True) break;
432: }
433:
434: /*
435: * Unmap the menu.
436: */
437: for ( p_ptr = menu->p_list->next;
438: p_ptr != menu->p_list;
439: p_ptr = p_ptr->next)
440: {
441: XUnmapWindow(display, p_ptr->window);
442: }
443:
444: /*
445: * Ungrab the mouse.
446: */
447: XUngrabPointer(display, CurrentTime);
448:
449: /*
450: * Restore bits under where the menu was if we managed
451: * to save them and free the pixmap.
452: */
453:
454: /*
455: * If there is a current selection deactivate it.
456: */
457: if (cur_s != NULL) cur_s->activated = 0;
458:
459: /*
460: * Deactivate the current pane.
461: */
462: cur_p->activated = 0;
463: XSetWindowBackgroundPixmap(display, cur_p->window, menu->inact_pixmap);
464:
465: /*
466: * Synchronize the X buffers and the X event queue.
467: */
468: XSync(display, 0);
469:
470: /*
471: * Dispatch any events remaining on the queue.
472: */
473: while (QLength(display)) {
474: /*
475: * Fetch the next event.
476: */
477: XNextEvent(display, &event);
478:
479: /*
480: * Discard any events left on the queue that belong to XMenu.
481: * All others are held and then returned to the event queue.
482: */
483: switch (event.type) {
484: case Expose:
485: case EnterNotify:
486: case LeaveNotify:
487: case ButtonPress:
488: case ButtonRelease:
489: /*
490: * Does this event belong to one of XMenu's windows?
491: * If so, discard it and process the next event.
492: * If not fall through and treat it as a foreign event.
493: */
494: event_xmp = (XMPane *)XLookUpAssoc(
495: display,
496: menu->assoc_tab,
497: event.xbutton.window
498: );
499: if (event_xmp != NULL) continue;
500: default:
501: /*
502: * This is a foreign event.
503: * Queue it for later return to the X event queue.
504: */
505: feq_tmp = (XMEventQue *)malloc(sizeof(XMEventQue));
506: if (feq_tmp == NULL) {
507: _XMErrorCode = XME_CALLOC;
508: return(XM_FAILURE);
509: }
510: feq_tmp->event = event;
511: feq_tmp->next = feq;
512: feq = feq_tmp;
513: }
514: }
515: /*
516: * Return any foreign events that were queued to the X event queue.
517: */
518: while (feq != NULL) {
519: feq_tmp = feq;
520: XPutBackEvent(display, &feq_tmp->event);
521: feq = feq_tmp->next;
522: free((char *)feq_tmp);
523: }
524:
525: /*
526: * Return successfully.
527: */
528: _XMErrorCode = XME_NO_ERROR;
529: return(ret_val);
530:
531: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.