from trunk
[emacs.git] / oldXMenu / Activate.c
blob2e1996e7287216adfcc8869ce58b4690481fba47
1 /* Copyright Massachusetts Institute of Technology 1985 */
3 #include "copyright.h"
5 /*
6 Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
7 Free Software Foundation, Inc.
9 This program is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>. */
23 * XMenu: MIT Project Athena, X Window system menu package
25 * XMenuActivate - Maps a given menu to the display and activates
26 * the menu for user selection. The user is allowed to
27 * specify which pane and selection will be current,
28 * the X and Y location of the menu (relative to the
29 * parent window) and the mouse button event mask that
30 * will be used to identify a selection request.
32 * A menu selection is shown to be current by placing
33 * a highlight box around the selection as the mouse
34 * cursor enters its active region. Inactive selections
35 * will not be highlighted. As the mouse cursor moved
36 * from one menu pane to another menu pane the pane being
37 * entered is raised and made current and the pane being
38 * left is lowered.
40 * Anytime XMenuActivate returns, the p_num and
41 * s_num are left at their last known values (i.e.,
42 * the last known current pane and selection indices).
43 * The following are the defined return states:
45 * 1) If at any time an error occurs the data
46 * pointer is left untouched and XM_FAILURE
47 * is returned.
49 * 2) When a selection request is received (i.e.,
50 * when the specified mouse event occurs) the
51 * data pointer will be set to the data
52 * associated with the particular selection
53 * current at the time of the selection request
54 * and XM_SUCCESS is returned.
56 * 3) If no selection was current at the time a
57 * selection request is made the data pointer
58 * will be left untouched and XM_NO_SELECT will
59 * be returned.
61 * 4) If the selection that was current at the time
62 * a selection request is made is not an active
63 * selection the data pointer will be left
64 * untouched and XM_IA_SELECT will be returned.
66 * Since X processes events in an asynchronous manner
67 * it is likely that XMenuActivate will encounter
68 * a "foreign event" while it is executing. Foreign
69 * events are handled in one of three ways:
71 * 1) The event is discarded. This is the default
72 * mode and requires no action on the part of the
73 * application.
75 * 2) The application has identified an asynchronous
76 * event handler that will be called and the
77 * foreign event handed off to it. Note:
78 * AEQ mode disables this mode temporarily.
80 * 3) The application has enabled asynchronous event
81 * queuing mode. In this mode all foreign events
82 * will be queued up untill XMenuActivate
83 * terminates; at which time they will be
84 * returned to the X event queue. As long as
85 * AEQ mode is enabled any asynchronous event
86 * handler as temporarily disabled.
88 * Any events encountered while taking down the menu
89 * (i.e., exposure events from occluded windows) will
90 * automatically be returned to the X event queue after
91 * XMenuActivate has cleaned the queue of any of its own
92 * events that are no longer needed.
94 * Author: Tony Della Fera, DEC
95 * March 12, 1986
99 #include <config.h>
100 #include "XMenuInt.h"
101 #include <X11/keysym.h>
103 /* For debug, set this to 0 to not grab the keyboard on menu popup */
104 int x_menu_grab_keyboard = 1;
106 typedef void (*Wait_func)();
108 static Wait_func wait_func;
109 static void* wait_data;
111 void
112 XMenuActivateSetWaitFunction (func, data)
113 Wait_func func;
114 void *data;
116 wait_func = func;
117 wait_data = data;
121 XMenuActivate(display, menu, p_num, s_num, x_pos, y_pos, event_mask, data,
122 help_callback)
123 register Display *display; /* Display to put menu on. */
124 register XMenu *menu; /* Menu to activate. */
125 int *p_num; /* Pane number selected. */
126 int *s_num; /* Selection number selected. */
127 int x_pos; /* X coordinate of menu position. */
128 int y_pos; /* Y coordinate of menu position. */
129 unsigned int event_mask; /* Mouse button event mask. */
130 char **data; /* Pointer to return data value. */
131 void (* help_callback) (); /* Help callback. */
133 int status; /* X routine call status. */
134 int orig_x; /* Upper left menu origin X coord. */
135 int orig_y; /* Upper left menu origin Y coord. */
136 int ret_val; /* Return value. */
138 register XMPane *p_ptr; /* Current XMPane. */
139 register XMPane *event_xmp; /* Event XMPane pointer. */
140 register XMPane *cur_p; /* Current pane. */
141 register XMSelect *cur_s; /* Current selection. */
142 XMWindow *event_xmw; /* Event XMWindow pointer. */
143 XEvent event; /* X input event. */
144 XEvent peek_event; /* X input peek ahead event. */
146 Bool selection = False; /* Selection has been made. */
147 Bool forward = True; /* Moving forward in the pane list. */
149 Window root, child;
150 int root_x, root_y, win_x, win_y;
151 unsigned int mask;
152 KeySym keysym;
155 * Define and allocate a foreign event queue to hold events
156 * that don't belong to XMenu. These events are later restored
157 * to the X event queue.
159 typedef struct _xmeventque {
160 XEvent event;
161 struct _xmeventque *next;
162 } XMEventQue;
164 XMEventQue *feq = NULL; /* Foreign event queue. */
165 XMEventQue *feq_tmp; /* Foreign event queue temporary. */
168 * If there are no panes in the menu then return failure
169 * because the menu is not initialized.
171 if (menu->p_count == 0) {
172 _XMErrorCode = XME_NOT_INIT;
173 return(XM_FAILURE);
177 * Find the desired current pane.
179 cur_p = _XMGetPanePtr(menu, *p_num);
180 if (cur_p == NULL) {
181 return(XM_FAILURE);
183 cur_p->activated = cur_p->active;
186 * Find the desired current selection.
187 * If the current selection index is out of range a null current selection
188 * will be assumed and the cursor will be placed in the current pane
189 * header.
191 cur_s = _XMGetSelectionPtr(cur_p, *s_num);
194 * Compute origin of menu so that cursor is in
195 * Correct pane and selection.
197 _XMTransToOrigin(display,
198 menu,
199 cur_p, cur_s,
200 x_pos, y_pos,
201 &orig_x, &orig_y);
202 menu->x_pos = orig_x; /* Store X and Y coords of menu. */
203 menu->y_pos = orig_y;
205 if (XMenuRecompute(display, menu) == XM_FAILURE) {
206 return(XM_FAILURE);
210 * Flush the window creation queue.
211 * This batches all window creates since lazy evaluation
212 * is more efficient than individual evaluation.
213 * This routine also does an XFlush().
215 if (_XMWinQueFlush(display, menu, cur_p, cur_s) == _FAILURE) {
216 return(XM_FAILURE);
220 * Make sure windows are in correct order (in case we were passed
221 * an already created menu in incorrect order.)
223 for(p_ptr = menu->p_list->next; p_ptr != cur_p; p_ptr = p_ptr->next)
224 XRaiseWindow(display, p_ptr->window);
225 for(p_ptr = menu->p_list->prev; p_ptr != cur_p->prev; p_ptr = p_ptr->prev)
226 XRaiseWindow(display, p_ptr->window);
229 * Make sure all selection windows are mapped.
231 for (
232 p_ptr = menu->p_list->next;
233 p_ptr != menu->p_list;
234 p_ptr = p_ptr->next
236 XMapSubwindows(display, p_ptr->window);
240 * Synchronize the X buffers and the event queue.
241 * From here on, all events in the queue that don't belong to
242 * XMenu are sent back to the application via an application
243 * provided event handler or discarded if the application has
244 * not provided an event handler.
246 XSync(display, 0);
249 * Grab the mouse for menu input.
252 status = XGrabPointer(
253 display,
254 menu->parent,
255 True,
256 event_mask,
257 GrabModeAsync,
258 GrabModeAsync,
259 None,
260 menu->mouse_cursor,
261 CurrentTime
263 if (status == Success && x_menu_grab_keyboard)
265 status = XGrabKeyboard (display,
266 menu->parent,
267 False,
268 GrabModeAsync,
269 GrabModeAsync,
270 CurrentTime);
271 if (status != Success)
272 XUngrabPointer(display, CurrentTime);
275 if (status == _X_FAILURE) {
276 _XMErrorCode = XME_GRAB_MOUSE;
277 return(XM_FAILURE);
281 * Map the menu panes.
283 XMapWindow(display, cur_p->window);
284 for (p_ptr = menu->p_list->next;
285 p_ptr != cur_p;
286 p_ptr = p_ptr->next)
287 XMapWindow(display, p_ptr->window);
288 for (p_ptr = cur_p->next;
289 p_ptr != menu->p_list;
290 p_ptr = p_ptr->next)
291 XMapWindow(display, p_ptr->window);
293 XRaiseWindow(display, cur_p->window); /* Make sure current */
294 /* pane is on top. */
296 cur_s = NULL; /* Clear current selection. */
299 * Begin event processing loop.
301 while (1) {
302 if (wait_func) (*wait_func) (wait_data);
303 XNextEvent(display, &event); /* Get next event. */
304 switch (event.type) { /* Dispatch on the event type. */
305 case Expose:
306 event_xmp = (XMPane *)XLookUpAssoc(display,
307 menu->assoc_tab,
308 event.xexpose.window);
309 if (event_xmp == NULL) {
311 * If AEQ mode is enabled then queue the event.
313 if (menu->aeq) {
314 feq_tmp = (XMEventQue *)malloc(sizeof(XMEventQue));
315 if (feq_tmp == NULL) {
316 _XMErrorCode = XME_CALLOC;
317 return(XM_FAILURE);
319 feq_tmp->event = event;
320 feq_tmp->next = feq;
321 feq = feq_tmp;
323 else if (_XMEventHandler) (*_XMEventHandler)(&event);
324 break;
326 if (event_xmp->activated) {
327 XSetWindowBackground(display,
328 event_xmp->window,
329 menu->bkgnd_color);
331 else {
332 XSetWindowBackgroundPixmap(display,
333 event_xmp->window,
334 menu->inact_pixmap);
336 _XMRefreshPane(display, menu, event_xmp);
337 break;
338 case EnterNotify:
340 * First wait a small period of time, and see
341 * if another EnterNotify event follows hard on the
342 * heels of this one. i.e., the user is simply
343 * "passing through". If so, ignore this one.
346 event_xmw = (XMWindow *)XLookUpAssoc(display,
347 menu->assoc_tab,
348 event.xcrossing.window);
349 if (event_xmw == NULL) break;
350 if (event_xmw->type == SELECTION) {
352 * We have entered a selection.
354 /* if (XPending(display) == 0) usleep(150000); */
355 if (XPending(display) != 0) {
356 XPeekEvent(display, &peek_event);
357 if(peek_event.type == LeaveNotify) {
358 break;
361 cur_s = (XMSelect *)event_xmw;
362 help_callback (cur_s->help_string,
363 cur_p->serial, cur_s->serial);
366 * If the pane we are in is active and the
367 * selection entered is active then activate
368 * the selection.
370 if (cur_p->active && cur_s->active > 0) {
371 cur_s->activated = 1;
372 _XMRefreshSelection(display, menu, cur_s);
375 else {
377 * We have entered a pane.
379 /* if (XPending(display) == 0) usleep(150000); */
380 if (XPending(display) != 0) {
381 XPeekEvent(display, &peek_event);
382 if (peek_event.type == EnterNotify) break;
384 XQueryPointer(display,
385 menu->parent,
386 &root, &child,
387 &root_x, &root_y,
388 &win_x, &win_y,
389 &mask);
390 event_xmp = (XMPane *)XLookUpAssoc(display,
391 menu->assoc_tab,
392 child);
393 if (event_xmp == NULL) break;
394 if (event_xmp == cur_p) break;
395 if (event_xmp->serial > cur_p->serial) forward = True;
396 else forward = False;
397 p_ptr = cur_p;
398 while (p_ptr != event_xmp) {
399 if (forward) p_ptr = p_ptr->next;
400 else p_ptr = p_ptr->prev;
401 XRaiseWindow(display, p_ptr->window);
403 if (cur_p->activated) {
404 cur_p->activated = False;
405 XSetWindowBackgroundPixmap(display,
406 cur_p->window,
407 menu->inact_pixmap);
408 _XMRefreshPane(display, menu, cur_p);
410 if (event_xmp->active) event_xmp->activated = True;
411 #if 1
413 * i suspect the we don't get an EXPOSE event when backing
414 * store is enabled; the menu windows content is probably
415 * not drawn in when it should be in that case.
416 * in that case, this is probably an ugly fix!
417 * i hope someone more familiar with this code would
418 * take it from here. -- caveh@eng.sun.com.
420 XSetWindowBackground(display,
421 event_xmp->window,
422 menu->bkgnd_color);
423 _XMRefreshPane(display, menu, event_xmp);
424 #endif
425 cur_p = event_xmp;
427 break;
428 case LeaveNotify:
429 event_xmw = (XMWindow *)XLookUpAssoc(
430 display,
431 menu->assoc_tab,
432 event.xcrossing.window
434 if (event_xmw == NULL) break;
435 if(cur_s == NULL) break;
438 * If the current selection was activated then
439 * deactivate it.
441 if (cur_s->activated) {
442 cur_s->activated = False;
443 _XMRefreshSelection(display, menu, cur_s);
445 cur_s = NULL;
446 break;
448 case ButtonPress:
449 case ButtonRelease:
450 *p_num = cur_p->serial;
452 * Check to see if there is a current selection.
454 if (cur_s != NULL) {
456 * Set the selection number to the current selection.
458 *s_num = cur_s->serial;
460 * If the current selection was activated then
461 * we have a valid selection otherwise we have
462 * an inactive selection.
464 if (cur_s->activated) {
465 *data = cur_s->data;
466 ret_val = XM_SUCCESS;
468 else {
469 ret_val = XM_IA_SELECT;
472 else {
474 * No selection was current.
476 ret_val = XM_NO_SELECT;
478 selection = True;
479 break;
480 case KeyPress:
481 case KeyRelease:
482 keysym = XLookupKeysym (&event.xkey, 0);
484 /* Pop down on C-g and Escape. */
485 if ((keysym == XK_g && (event.xkey.state & ControlMask) != 0)
486 || keysym == XK_Escape) /* Any escape, ignore modifiers. */
488 ret_val = XM_NO_SELECT;
489 selection = True;
491 break;
492 default:
494 * If AEQ mode is enabled then queue the event.
496 if (menu->aeq) {
497 feq_tmp = (XMEventQue *)malloc(sizeof(XMEventQue));
498 if (feq_tmp == NULL) {
499 _XMErrorCode = XME_CALLOC;
500 return(XM_FAILURE);
502 feq_tmp->event = event;
503 feq_tmp->next = feq;
504 feq = feq_tmp;
506 else if (_XMEventHandler) (*_XMEventHandler)(&event);
509 * If a selection has been made, break out of the event loop.
511 if (selection == True) break;
515 * Unmap the menu.
517 for ( p_ptr = menu->p_list->next;
518 p_ptr != menu->p_list;
519 p_ptr = p_ptr->next)
521 XUnmapWindow(display, p_ptr->window);
525 * Ungrab the mouse.
527 XUngrabPointer(display, CurrentTime);
528 XUngrabKeyboard(display, CurrentTime);
531 * Restore bits under where the menu was if we managed
532 * to save them and free the pixmap.
536 * If there is a current selection deactivate it.
538 if (cur_s != NULL) cur_s->activated = 0;
541 * Deactivate the current pane.
543 cur_p->activated = 0;
544 XSetWindowBackgroundPixmap(display, cur_p->window, menu->inact_pixmap);
547 * Synchronize the X buffers and the X event queue.
549 XSync(display, 0);
552 * Dispatch any events remaining on the queue.
554 while (QLength(display)) {
556 * Fetch the next event.
558 XNextEvent(display, &event);
561 * Discard any events left on the queue that belong to XMenu.
562 * All others are held and then returned to the event queue.
564 switch (event.type) {
565 case Expose:
566 case EnterNotify:
567 case LeaveNotify:
568 case ButtonPress:
569 case ButtonRelease:
571 * Does this event belong to one of XMenu's windows?
572 * If so, discard it and process the next event.
573 * If not fall through and treat it as a foreign event.
575 event_xmp = (XMPane *)XLookUpAssoc(
576 display,
577 menu->assoc_tab,
578 event.xbutton.window
580 if (event_xmp != NULL) continue;
581 default:
583 * This is a foreign event.
584 * Queue it for later return to the X event queue.
586 feq_tmp = (XMEventQue *)malloc(sizeof(XMEventQue));
587 if (feq_tmp == NULL) {
588 _XMErrorCode = XME_CALLOC;
589 return(XM_FAILURE);
591 feq_tmp->event = event;
592 feq_tmp->next = feq;
593 feq = feq_tmp;
597 * Return any foreign events that were queued to the X event queue.
599 while (feq != NULL) {
600 feq_tmp = feq;
601 XPutBackEvent(display, &feq_tmp->event);
602 feq = feq_tmp->next;
603 free((char *)feq_tmp);
606 wait_func = 0;
609 * Return successfully.
611 _XMErrorCode = XME_NO_ERROR;
612 return(ret_val);
616 /* arch-tag: 6b90b578-ecea-4328-b460-a0c96963f872
617 (do not change this comment) */