** davby@ida.liu.se, 6 July: Bug in pos-visible-in-window-p
[emacs.git] / oldXMenu / Activate.c
blob0fb404675b999d431c9faf2937bdae46d4c0e0ed
1 /* Copyright Massachusetts Institute of Technology 1985 */
3 #include "copyright.h"
5 /*
6 Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007
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, or (at your option)
12 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; see the file COPYING. If not, write to the
21 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 Boston, MA 02110-1301, USA. */
26 * XMenu: MIT Project Athena, X Window system menu package
28 * XMenuActivate - Maps a given menu to the display and activates
29 * the menu for user selection. The user is allowed to
30 * specify which pane and selection will be current,
31 * the X and Y location of the menu (relative to the
32 * parent window) and the mouse button event mask that
33 * will be used to identify a selection request.
35 * A menu selection is shown to be current by placing
36 * a highlight box around the selection as the mouse
37 * cursor enters its active region. Inactive selections
38 * will not be highlighted. As the mouse cursor moved
39 * from one menu pane to another menu pane the pane being
40 * entered is raised and made current and the pane being
41 * left is lowered.
43 * Anytime XMenuActivate returns, the p_num and
44 * s_num are left at their last known values (i.e.,
45 * the last known current pane and selection indices).
46 * The following are the defined return states:
48 * 1) If at any time an error occurs the data
49 * pointer is left untouched and XM_FAILURE
50 * is returned.
52 * 2) When a selection request is received (i.e.,
53 * when the specified mouse event occurs) the
54 * data pointer will be set to the data
55 * associated with the particular selection
56 * current at the time of the selection request
57 * and XM_SUCCESS is returned.
59 * 3) If no selection was current at the time a
60 * selection request is made the data pointer
61 * will be left untouched and XM_NO_SELECT will
62 * be returned.
64 * 4) If the selection that was current at the time
65 * a selection request is made is not an active
66 * selection the data pointer will be left
67 * untouched and XM_IA_SELECT will be returned.
69 * Since X processes events in an asynchronous manner
70 * it is likely that XMenuActivate will encounter
71 * a "foreign event" while it is executing. Foreign
72 * events are handled in one of three ways:
74 * 1) The event is discarded. This is the default
75 * mode and requires no action on the part of the
76 * application.
78 * 2) The application has identified an asynchronous
79 * event handler that will be called and the
80 * foreign event handed off to it. Note:
81 * AEQ mode disables this mode temporarily.
83 * 3) The application has enabled asynchronous event
84 * queuing mode. In this mode all foreign events
85 * will be queued up untill XMenuActivate
86 * terminates; at which time they will be
87 * returned to the X event queue. As long as
88 * AEQ mode is enabled any asynchronous event
89 * handler as temporarily disabled.
91 * Any events encountered while taking down the menu
92 * (i.e., exposure events from occluded windows) will
93 * automatically be returned to the X event queue after
94 * XMenuActivate has cleaned the queue of any of its own
95 * events that are no longer needed.
97 * Author: Tony Della Fera, DEC
98 * March 12, 1986
102 #include <config.h>
103 #include "XMenuInt.h"
104 #include <X11/keysym.h>
106 /* For debug, set this to 0 to not grab the keyboard on menu popup */
107 int x_menu_grab_keyboard = 1;
109 typedef void (*Wait_func)();
111 static Wait_func wait_func;
112 static void* wait_data;
114 void
115 XMenuActivateSetWaitFunction (func, data)
116 Wait_func func;
117 void *data;
119 wait_func = func;
120 wait_data = data;
124 XMenuActivate(display, menu, p_num, s_num, x_pos, y_pos, event_mask, data,
125 help_callback)
126 register Display *display; /* Display to put menu on. */
127 register XMenu *menu; /* Menu to activate. */
128 int *p_num; /* Pane number selected. */
129 int *s_num; /* Selection number selected. */
130 int x_pos; /* X coordinate of menu position. */
131 int y_pos; /* Y coordinate of menu position. */
132 unsigned int event_mask; /* Mouse button event mask. */
133 char **data; /* Pointer to return data value. */
134 void (* help_callback) (); /* Help callback. */
136 int status; /* X routine call status. */
137 int orig_x; /* Upper left menu origin X coord. */
138 int orig_y; /* Upper left menu origin Y coord. */
139 int ret_val; /* Return value. */
141 register XMPane *p_ptr; /* Current XMPane. */
142 register XMPane *event_xmp; /* Event XMPane pointer. */
143 register XMPane *cur_p; /* Current pane. */
144 register XMSelect *cur_s; /* Current selection. */
145 XMWindow *event_xmw; /* Event XMWindow pointer. */
146 XEvent event; /* X input event. */
147 XEvent peek_event; /* X input peek ahead event. */
149 Bool selection = False; /* Selection has been made. */
150 Bool forward = True; /* Moving forward in the pane list. */
152 Window root, child;
153 int root_x, root_y, win_x, win_y;
154 unsigned int mask;
155 KeySym keysym;
158 * Define and allocate a foreign event queue to hold events
159 * that don't belong to XMenu. These events are later restored
160 * to the X event queue.
162 typedef struct _xmeventque {
163 XEvent event;
164 struct _xmeventque *next;
165 } XMEventQue;
167 XMEventQue *feq = NULL; /* Foreign event queue. */
168 XMEventQue *feq_tmp; /* Foreign event queue temporary. */
171 * If there are no panes in the menu then return failure
172 * because the menu is not initialized.
174 if (menu->p_count == 0) {
175 _XMErrorCode = XME_NOT_INIT;
176 return(XM_FAILURE);
180 * Find the desired current pane.
182 cur_p = _XMGetPanePtr(menu, *p_num);
183 if (cur_p == NULL) {
184 return(XM_FAILURE);
186 cur_p->activated = cur_p->active;
189 * Find the desired current selection.
190 * If the current selection index is out of range a null current selection
191 * will be assumed and the cursor will be placed in the current pane
192 * header.
194 cur_s = _XMGetSelectionPtr(cur_p, *s_num);
197 * Compute origin of menu so that cursor is in
198 * Correct pane and selection.
200 _XMTransToOrigin(display,
201 menu,
202 cur_p, cur_s,
203 x_pos, y_pos,
204 &orig_x, &orig_y);
205 menu->x_pos = orig_x; /* Store X and Y coords of menu. */
206 menu->y_pos = orig_y;
208 if (XMenuRecompute(display, menu) == XM_FAILURE) {
209 return(XM_FAILURE);
213 * Flush the window creation queue.
214 * This batches all window creates since lazy evaluation
215 * is more efficient than individual evaluation.
216 * This routine also does an XFlush().
218 if (_XMWinQueFlush(display, menu, cur_p, cur_s) == _FAILURE) {
219 return(XM_FAILURE);
223 * Make sure windows are in correct order (in case we were passed
224 * an already created menu in incorrect order.)
226 for(p_ptr = menu->p_list->next; p_ptr != cur_p; p_ptr = p_ptr->next)
227 XRaiseWindow(display, p_ptr->window);
228 for(p_ptr = menu->p_list->prev; p_ptr != cur_p->prev; p_ptr = p_ptr->prev)
229 XRaiseWindow(display, p_ptr->window);
232 * Make sure all selection windows are mapped.
234 for (
235 p_ptr = menu->p_list->next;
236 p_ptr != menu->p_list;
237 p_ptr = p_ptr->next
239 XMapSubwindows(display, p_ptr->window);
243 * Synchronize the X buffers and the event queue.
244 * From here on, all events in the queue that don't belong to
245 * XMenu are sent back to the application via an application
246 * provided event handler or discarded if the application has
247 * not provided an event handler.
249 XSync(display, 0);
252 * Grab the mouse for menu input.
255 status = XGrabPointer(
256 display,
257 menu->parent,
258 True,
259 event_mask,
260 GrabModeAsync,
261 GrabModeAsync,
262 None,
263 menu->mouse_cursor,
264 CurrentTime
266 if (status == Success && x_menu_grab_keyboard)
268 status = XGrabKeyboard (display,
269 menu->parent,
270 False,
271 GrabModeAsync,
272 GrabModeAsync,
273 CurrentTime);
274 if (status != Success)
275 XUngrabPointer(display, CurrentTime);
278 if (status == _X_FAILURE) {
279 _XMErrorCode = XME_GRAB_MOUSE;
280 return(XM_FAILURE);
284 * Map the menu panes.
286 XMapWindow(display, cur_p->window);
287 for (p_ptr = menu->p_list->next;
288 p_ptr != cur_p;
289 p_ptr = p_ptr->next)
290 XMapWindow(display, p_ptr->window);
291 for (p_ptr = cur_p->next;
292 p_ptr != menu->p_list;
293 p_ptr = p_ptr->next)
294 XMapWindow(display, p_ptr->window);
296 XRaiseWindow(display, cur_p->window); /* Make sure current */
297 /* pane is on top. */
299 cur_s = NULL; /* Clear current selection. */
302 * Begin event processing loop.
304 while (1) {
305 if (wait_func) (*wait_func) (wait_data);
306 XNextEvent(display, &event); /* Get next event. */
307 switch (event.type) { /* Dispatch on the event type. */
308 case Expose:
309 event_xmp = (XMPane *)XLookUpAssoc(display,
310 menu->assoc_tab,
311 event.xexpose.window);
312 if (event_xmp == NULL) {
314 * If AEQ mode is enabled then queue the event.
316 if (menu->aeq) {
317 feq_tmp = (XMEventQue *)malloc(sizeof(XMEventQue));
318 if (feq_tmp == NULL) {
319 _XMErrorCode = XME_CALLOC;
320 return(XM_FAILURE);
322 feq_tmp->event = event;
323 feq_tmp->next = feq;
324 feq = feq_tmp;
326 else if (_XMEventHandler) (*_XMEventHandler)(&event);
327 break;
329 if (event_xmp->activated) {
330 XSetWindowBackground(display,
331 event_xmp->window,
332 menu->bkgnd_color);
334 else {
335 XSetWindowBackgroundPixmap(display,
336 event_xmp->window,
337 menu->inact_pixmap);
339 _XMRefreshPane(display, menu, event_xmp);
340 break;
341 case EnterNotify:
343 * First wait a small period of time, and see
344 * if another EnterNotify event follows hard on the
345 * heels of this one. i.e., the user is simply
346 * "passing through". If so, ignore this one.
349 event_xmw = (XMWindow *)XLookUpAssoc(display,
350 menu->assoc_tab,
351 event.xcrossing.window);
352 if (event_xmw == NULL) break;
353 if (event_xmw->type == SELECTION) {
355 * We have entered a selection.
357 /* if (XPending(display) == 0) usleep(150000); */
358 if (XPending(display) != 0) {
359 XPeekEvent(display, &peek_event);
360 if(peek_event.type == LeaveNotify) {
361 break;
364 cur_s = (XMSelect *)event_xmw;
365 help_callback (cur_s->help_string,
366 cur_p->serial, cur_s->serial);
369 * If the pane we are in is active and the
370 * selection entered is active then activate
371 * the selection.
373 if (cur_p->active && cur_s->active > 0) {
374 cur_s->activated = 1;
375 _XMRefreshSelection(display, menu, cur_s);
378 else {
380 * We have entered a pane.
382 /* if (XPending(display) == 0) usleep(150000); */
383 if (XPending(display) != 0) {
384 XPeekEvent(display, &peek_event);
385 if (peek_event.type == EnterNotify) break;
387 XQueryPointer(display,
388 menu->parent,
389 &root, &child,
390 &root_x, &root_y,
391 &win_x, &win_y,
392 &mask);
393 event_xmp = (XMPane *)XLookUpAssoc(display,
394 menu->assoc_tab,
395 child);
396 if (event_xmp == NULL) break;
397 if (event_xmp == cur_p) break;
398 if (event_xmp->serial > cur_p->serial) forward = True;
399 else forward = False;
400 p_ptr = cur_p;
401 while (p_ptr != event_xmp) {
402 if (forward) p_ptr = p_ptr->next;
403 else p_ptr = p_ptr->prev;
404 XRaiseWindow(display, p_ptr->window);
406 if (cur_p->activated) {
407 cur_p->activated = False;
408 XSetWindowBackgroundPixmap(display,
409 cur_p->window,
410 menu->inact_pixmap);
411 _XMRefreshPane(display, menu, cur_p);
413 if (event_xmp->active) event_xmp->activated = True;
414 #if 1
416 * i suspect the we don't get an EXPOSE event when backing
417 * store is enabled; the menu windows content is probably
418 * not drawn in when it should be in that case.
419 * in that case, this is probably an ugly fix!
420 * i hope someone more familiar with this code would
421 * take it from here. -- caveh@eng.sun.com.
423 XSetWindowBackground(display,
424 event_xmp->window,
425 menu->bkgnd_color);
426 _XMRefreshPane(display, menu, event_xmp);
427 #endif
428 cur_p = event_xmp;
430 break;
431 case LeaveNotify:
432 event_xmw = (XMWindow *)XLookUpAssoc(
433 display,
434 menu->assoc_tab,
435 event.xcrossing.window
437 if (event_xmw == NULL) break;
438 if(cur_s == NULL) break;
441 * If the current selection was activated then
442 * deactivate it.
444 if (cur_s->activated) {
445 cur_s->activated = False;
446 _XMRefreshSelection(display, menu, cur_s);
448 cur_s = NULL;
449 break;
451 case ButtonPress:
452 case ButtonRelease:
453 *p_num = cur_p->serial;
455 * Check to see if there is a current selection.
457 if (cur_s != NULL) {
459 * Set the selection number to the current selection.
461 *s_num = cur_s->serial;
463 * If the current selection was activated then
464 * we have a valid selection otherwise we have
465 * an inactive selection.
467 if (cur_s->activated) {
468 *data = cur_s->data;
469 ret_val = XM_SUCCESS;
471 else {
472 ret_val = XM_IA_SELECT;
475 else {
477 * No selection was current.
479 ret_val = XM_NO_SELECT;
481 selection = True;
482 break;
483 case KeyPress:
484 case KeyRelease:
485 keysym = XLookupKeysym (&event.xkey, 0);
487 /* Pop down on C-g and Escape. */
488 if ((keysym == XK_g && (event.xkey.state & ControlMask) != 0)
489 || keysym == XK_Escape) /* Any escape, ignore modifiers. */
491 ret_val = XM_NO_SELECT;
492 selection = True;
494 break;
495 default:
497 * If AEQ mode is enabled then queue the event.
499 if (menu->aeq) {
500 feq_tmp = (XMEventQue *)malloc(sizeof(XMEventQue));
501 if (feq_tmp == NULL) {
502 _XMErrorCode = XME_CALLOC;
503 return(XM_FAILURE);
505 feq_tmp->event = event;
506 feq_tmp->next = feq;
507 feq = feq_tmp;
509 else if (_XMEventHandler) (*_XMEventHandler)(&event);
512 * If a selection has been made, break out of the event loop.
514 if (selection == True) break;
518 * Unmap the menu.
520 for ( p_ptr = menu->p_list->next;
521 p_ptr != menu->p_list;
522 p_ptr = p_ptr->next)
524 XUnmapWindow(display, p_ptr->window);
528 * Ungrab the mouse.
530 XUngrabPointer(display, CurrentTime);
531 XUngrabKeyboard(display, CurrentTime);
534 * Restore bits under where the menu was if we managed
535 * to save them and free the pixmap.
539 * If there is a current selection deactivate it.
541 if (cur_s != NULL) cur_s->activated = 0;
544 * Deactivate the current pane.
546 cur_p->activated = 0;
547 XSetWindowBackgroundPixmap(display, cur_p->window, menu->inact_pixmap);
550 * Synchronize the X buffers and the X event queue.
552 XSync(display, 0);
555 * Dispatch any events remaining on the queue.
557 while (QLength(display)) {
559 * Fetch the next event.
561 XNextEvent(display, &event);
564 * Discard any events left on the queue that belong to XMenu.
565 * All others are held and then returned to the event queue.
567 switch (event.type) {
568 case Expose:
569 case EnterNotify:
570 case LeaveNotify:
571 case ButtonPress:
572 case ButtonRelease:
574 * Does this event belong to one of XMenu's windows?
575 * If so, discard it and process the next event.
576 * If not fall through and treat it as a foreign event.
578 event_xmp = (XMPane *)XLookUpAssoc(
579 display,
580 menu->assoc_tab,
581 event.xbutton.window
583 if (event_xmp != NULL) continue;
584 default:
586 * This is a foreign event.
587 * Queue it for later return to the X event queue.
589 feq_tmp = (XMEventQue *)malloc(sizeof(XMEventQue));
590 if (feq_tmp == NULL) {
591 _XMErrorCode = XME_CALLOC;
592 return(XM_FAILURE);
594 feq_tmp->event = event;
595 feq_tmp->next = feq;
596 feq = feq_tmp;
600 * Return any foreign events that were queued to the X event queue.
602 while (feq != NULL) {
603 feq_tmp = feq;
604 XPutBackEvent(display, &feq_tmp->event);
605 feq = feq_tmp->next;
606 free((char *)feq_tmp);
609 wait_func = 0;
612 * Return successfully.
614 _XMErrorCode = XME_NO_ERROR;
615 return(ret_val);
619 /* arch-tag: 6b90b578-ecea-4328-b460-a0c96963f872
620 (do not change this comment) */