*** empty log message ***
[bpt/emacs.git] / oldXMenu / Activate.c
CommitLineData
e745ede7
DL
1/* Copyright Massachusetts Institute of Technology 1985 */
2
3#include "copyright.h"
4
54861d5e
GM
5/*
6Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007
7 Free Software Foundation, Inc.
8
9This program is free software; you can redistribute it and/or modify
10it under the terms of the GNU General Public License as published by
862b6004 11the Free Software Foundation; either version 3, or (at your option)
54861d5e
GM
12any later version.
13
14This program is distributed in the hope that it will be useful,
15but WITHOUT ANY WARRANTY; without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17GNU General Public License for more details.
18
19You should have received a copy of the GNU General Public License
20along with this program; see the file COPYING. If not, write to the
21Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22Boston, MA 02110-1301, USA. */
23
24
e745ede7
DL
25/*
26 * XMenu: MIT Project Athena, X Window system menu package
27 *
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.
34 *
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.
42 *
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:
47 *
48 * 1) If at any time an error occurs the data
49 * pointer is left untouched and XM_FAILURE
177c0ea7 50 * is returned.
e745ede7
DL
51 *
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.
58 *
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.
63 *
177c0ea7 64 * 4) If the selection that was current at the time
e745ede7
DL
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.
68 *
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:
73 *
74 * 1) The event is discarded. This is the default
75 * mode and requires no action on the part of the
76 * application.
77 *
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.
82 *
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.
90 *
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.
96 *
97 * Author: Tony Della Fera, DEC
98 * March 12, 1986
99 *
100 */
101
102#include <config.h>
103#include "XMenuInt.h"
e89f4e4b 104#include <X11/keysym.h>
e745ede7 105
38ee86a8
JD
106/* For debug, set this to 0 to not grab the keyboard on menu popup */
107int x_menu_grab_keyboard = 1;
108
141dbd2b
JD
109typedef void (*Wait_func)();
110
111static Wait_func wait_func;
112static void* wait_data;
113
114void
115XMenuActivateSetWaitFunction (func, data)
116 Wait_func func;
117 void *data;
118{
119 wait_func = func;
120 wait_data = data;
121}
122
e745ede7 123int
f9a70a31
GM
124XMenuActivate(display, menu, p_num, s_num, x_pos, y_pos, event_mask, data,
125 help_callback)
e745ede7
DL
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. */
f9a70a31 134 void (* help_callback) (); /* Help callback. */
e745ede7
DL
135{
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. */
140
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. */
148
149 Bool selection = False; /* Selection has been made. */
150 Bool forward = True; /* Moving forward in the pane list. */
151
152 Window root, child;
153 int root_x, root_y, win_x, win_y;
154 unsigned int mask;
e89f4e4b 155 KeySym keysym;
e745ede7
DL
156
157 /*
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.
161 */
162 typedef struct _xmeventque {
163 XEvent event;
164 struct _xmeventque *next;
165 } XMEventQue;
166
167 XMEventQue *feq = NULL; /* Foreign event queue. */
168 XMEventQue *feq_tmp; /* Foreign event queue temporary. */
177c0ea7 169
e745ede7
DL
170 /*
171 * If there are no panes in the menu then return failure
172 * because the menu is not initialized.
173 */
174 if (menu->p_count == 0) {
175 _XMErrorCode = XME_NOT_INIT;
176 return(XM_FAILURE);
177 }
178
179 /*
180 * Find the desired current pane.
181 */
182 cur_p = _XMGetPanePtr(menu, *p_num);
183 if (cur_p == NULL) {
184 return(XM_FAILURE);
185 }
186 cur_p->activated = cur_p->active;
187
188 /*
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.
193 */
194 cur_s = _XMGetSelectionPtr(cur_p, *s_num);
195
196 /*
197 * Compute origin of menu so that cursor is in
198 * Correct pane and selection.
199 */
177c0ea7
JB
200 _XMTransToOrigin(display,
201 menu,
202 cur_p, cur_s,
203 x_pos, y_pos,
e745ede7
DL
204 &orig_x, &orig_y);
205 menu->x_pos = orig_x; /* Store X and Y coords of menu. */
206 menu->y_pos = orig_y;
177c0ea7 207
e745ede7
DL
208 if (XMenuRecompute(display, menu) == XM_FAILURE) {
209 return(XM_FAILURE);
210 }
211
212 /*
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().
217 */
218 if (_XMWinQueFlush(display, menu, cur_p, cur_s) == _FAILURE) {
219 return(XM_FAILURE);
220 }
221
222 /*
223 * Make sure windows are in correct order (in case we were passed
224 * an already created menu in incorrect order.)
225 */
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);
230
231 /*
232 * Make sure all selection windows are mapped.
233 */
234 for (
235 p_ptr = menu->p_list->next;
236 p_ptr != menu->p_list;
237 p_ptr = p_ptr->next
238 ){
239 XMapSubwindows(display, p_ptr->window);
240 }
241
242 /*
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.
248 */
249 XSync(display, 0);
177c0ea7 250
e745ede7
DL
251 /*
252 * Grab the mouse for menu input.
253 */
177c0ea7 254
e745ede7
DL
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
265 );
38ee86a8
JD
266 if (status == Success && x_menu_grab_keyboard)
267 {
268 status = XGrabKeyboard (display,
269 menu->parent,
270 False,
271 GrabModeAsync,
272 GrabModeAsync,
273 CurrentTime);
274 if (status != Success)
275 XUngrabPointer(display, CurrentTime);
276 }
177c0ea7 277
e745ede7
DL
278 if (status == _X_FAILURE) {
279 _XMErrorCode = XME_GRAB_MOUSE;
280 return(XM_FAILURE);
281 }
282
283 /*
284 * Map the menu panes.
285 */
286 XMapWindow(display, cur_p->window);
287 for (p_ptr = menu->p_list->next;
177c0ea7 288 p_ptr != cur_p;
e745ede7
DL
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);
295
296 XRaiseWindow(display, cur_p->window); /* Make sure current */
297 /* pane is on top. */
177c0ea7 298
e745ede7
DL
299 cur_s = NULL; /* Clear current selection. */
300
301 /*
302 * Begin event processing loop.
303 */
304 while (1) {
141dbd2b 305 if (wait_func) (*wait_func) (wait_data);
e745ede7
DL
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,
177c0ea7 310 menu->assoc_tab,
e745ede7
DL
311 event.xexpose.window);
312 if (event_xmp == NULL) {
313 /*
314 * If AEQ mode is enabled then queue the event.
315 */
316 if (menu->aeq) {
317 feq_tmp = (XMEventQue *)malloc(sizeof(XMEventQue));
318 if (feq_tmp == NULL) {
319 _XMErrorCode = XME_CALLOC;
320 return(XM_FAILURE);
321 }
322 feq_tmp->event = event;
323 feq_tmp->next = feq;
324 feq = feq_tmp;
325 }
326 else if (_XMEventHandler) (*_XMEventHandler)(&event);
327 break;
328 }
329 if (event_xmp->activated) {
330 XSetWindowBackground(display,
177c0ea7 331 event_xmp->window,
e745ede7
DL
332 menu->bkgnd_color);
333 }
334 else {
335 XSetWindowBackgroundPixmap(display,
336 event_xmp->window,
337 menu->inact_pixmap);
338 }
339 _XMRefreshPane(display, menu, event_xmp);
340 break;
341 case EnterNotify:
177c0ea7 342 /*
e745ede7
DL
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.
347 */
177c0ea7 348
e745ede7
DL
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) {
354 /*
355 * We have entered a selection.
356 */
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;
362 }
177c0ea7 363 }
e745ede7 364 cur_s = (XMSelect *)event_xmw;
aabb1e71
GM
365 help_callback (cur_s->help_string,
366 cur_p->serial, cur_s->serial);
177c0ea7 367
e745ede7
DL
368 /*
369 * If the pane we are in is active and the
370 * selection entered is active then activate
371 * the selection.
372 */
373 if (cur_p->active && cur_s->active > 0) {
374 cur_s->activated = 1;
375 _XMRefreshSelection(display, menu, cur_s);
376 }
377 }
378 else {
379 /*
380 * We have entered a pane.
381 */
382 /* if (XPending(display) == 0) usleep(150000); */
383 if (XPending(display) != 0) {
384 XPeekEvent(display, &peek_event);
385 if (peek_event.type == EnterNotify) break;
386 }
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);
405 }
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);
412 }
413 if (event_xmp->active) event_xmp->activated = True;
414#if 1
415 /*
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.
422 */
423 XSetWindowBackground(display,
177c0ea7 424 event_xmp->window,
e745ede7
DL
425 menu->bkgnd_color);
426 _XMRefreshPane(display, menu, event_xmp);
427#endif
428 cur_p = event_xmp;
429 }
430 break;
431 case LeaveNotify:
432 event_xmw = (XMWindow *)XLookUpAssoc(
433 display,
434 menu->assoc_tab,
435 event.xcrossing.window
436 );
437 if (event_xmw == NULL) break;
438 if(cur_s == NULL) break;
177c0ea7 439
e745ede7
DL
440 /*
441 * If the current selection was activated then
442 * deactivate it.
443 */
444 if (cur_s->activated) {
445 cur_s->activated = False;
446 _XMRefreshSelection(display, menu, cur_s);
447 }
448 cur_s = NULL;
449 break;
177c0ea7 450
e745ede7
DL
451 case ButtonPress:
452 case ButtonRelease:
453 *p_num = cur_p->serial;
454 /*
455 * Check to see if there is a current selection.
456 */
457 if (cur_s != NULL) {
458 /*
459 * Set the selection number to the current selection.
460 */
461 *s_num = cur_s->serial;
462 /*
463 * If the current selection was activated then
464 * we have a valid selection otherwise we have
465 * an inactive selection.
466 */
467 if (cur_s->activated) {
468 *data = cur_s->data;
469 ret_val = XM_SUCCESS;
470 }
471 else {
472 ret_val = XM_IA_SELECT;
473 }
474 }
475 else {
476 /*
477 * No selection was current.
478 */
479 ret_val = XM_NO_SELECT;
480 }
481 selection = True;
482 break;
e89f4e4b
JD
483 case KeyPress:
484 case KeyRelease:
485 keysym = XLookupKeysym (&event.xkey, 0);
486
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. */
490 {
491 ret_val = XM_NO_SELECT;
492 selection = True;
493 }
494 break;
e745ede7
DL
495 default:
496 /*
497 * If AEQ mode is enabled then queue the event.
498 */
499 if (menu->aeq) {
500 feq_tmp = (XMEventQue *)malloc(sizeof(XMEventQue));
501 if (feq_tmp == NULL) {
502 _XMErrorCode = XME_CALLOC;
503 return(XM_FAILURE);
504 }
505 feq_tmp->event = event;
506 feq_tmp->next = feq;
507 feq = feq_tmp;
508 }
509 else if (_XMEventHandler) (*_XMEventHandler)(&event);
510 }
511 /*
512 * If a selection has been made, break out of the event loop.
513 */
514 if (selection == True) break;
515 }
516
517 /*
518 * Unmap the menu.
519 */
520 for ( p_ptr = menu->p_list->next;
521 p_ptr != menu->p_list;
177c0ea7 522 p_ptr = p_ptr->next)
e745ede7
DL
523 {
524 XUnmapWindow(display, p_ptr->window);
525 }
526
527 /*
528 * Ungrab the mouse.
529 */
530 XUngrabPointer(display, CurrentTime);
38ee86a8 531 XUngrabKeyboard(display, CurrentTime);
e745ede7 532
177c0ea7 533 /*
e745ede7
DL
534 * Restore bits under where the menu was if we managed
535 * to save them and free the pixmap.
536 */
537
538 /*
539 * If there is a current selection deactivate it.
540 */
541 if (cur_s != NULL) cur_s->activated = 0;
542
543 /*
544 * Deactivate the current pane.
545 */
546 cur_p->activated = 0;
547 XSetWindowBackgroundPixmap(display, cur_p->window, menu->inact_pixmap);
548
549 /*
550 * Synchronize the X buffers and the X event queue.
551 */
552 XSync(display, 0);
177c0ea7 553
e745ede7
DL
554 /*
555 * Dispatch any events remaining on the queue.
556 */
557 while (QLength(display)) {
558 /*
559 * Fetch the next event.
560 */
561 XNextEvent(display, &event);
562
563 /*
564 * Discard any events left on the queue that belong to XMenu.
565 * All others are held and then returned to the event queue.
566 */
567 switch (event.type) {
568 case Expose:
569 case EnterNotify:
570 case LeaveNotify:
571 case ButtonPress:
572 case ButtonRelease:
573 /*
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.
577 */
578 event_xmp = (XMPane *)XLookUpAssoc(
579 display,
580 menu->assoc_tab,
581 event.xbutton.window
582 );
583 if (event_xmp != NULL) continue;
584 default:
585 /*
586 * This is a foreign event.
587 * Queue it for later return to the X event queue.
588 */
589 feq_tmp = (XMEventQue *)malloc(sizeof(XMEventQue));
590 if (feq_tmp == NULL) {
591 _XMErrorCode = XME_CALLOC;
592 return(XM_FAILURE);
593 }
594 feq_tmp->event = event;
595 feq_tmp->next = feq;
596 feq = feq_tmp;
597 }
598 }
599 /*
600 * Return any foreign events that were queued to the X event queue.
601 */
602 while (feq != NULL) {
603 feq_tmp = feq;
604 XPutBackEvent(display, &feq_tmp->event);
605 feq = feq_tmp->next;
606 free((char *)feq_tmp);
607 }
177c0ea7 608
141dbd2b
JD
609 wait_func = 0;
610
e745ede7
DL
611 /*
612 * Return successfully.
613 */
614 _XMErrorCode = XME_NO_ERROR;
615 return(ret_val);
616
617}
ab5796a9
MB
618
619/* arch-tag: 6b90b578-ecea-4328-b460-a0c96963f872
620 (do not change this comment) */