root/branches/2.1/oldXMenu/Activate.c

Revision 3207, 15.1 kB (checked in by miyoshi, 5 years ago)

Sync up with Emacs-21.2.

  • Property svn:eol-style set to native
Line 
1 /* $Header: /cvsroot/emacs/emacs/oldXMenu/Activate.c,v 1.3 2000/07/21 14:36:24 gerd Exp $ */
2 /* Copyright    Massachusetts Institute of Technology    1985   */
3
4 #include "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 highlighted.  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 received (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  *                              queuing 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 <config.h>
84 #include "XMenuInt.h"
85
86 int
87 XMenuActivate(display, menu, p_num, s_num, x_pos, y_pos, event_mask, data,
88               help_callback)
89     register Display *display;          /* Display to put menu on. */
90     register XMenu *menu;               /* Menu to activate. */
91     int *p_num;                         /* Pane number selected. */
92     int *s_num;                         /* Selection number selected. */
93     int x_pos;                          /* X coordinate of menu position. */
94     int y_pos;                          /* Y coordinate of menu position. */
95     unsigned int event_mask;            /* Mouse button event mask. */
96     char **data;                        /* Pointer to return data value. */
97     void (* help_callback) ();          /* Help callback.  */
98 {
99     int status;                         /* X routine call status. */
100     int orig_x;                         /* Upper left menu origin X coord. */
101     int orig_y;                         /* Upper left menu origin Y coord. */
102     int ret_val;                        /* Return value. */
103
104     register XMPane *p_ptr;             /* Current XMPane. */
105     register XMPane *event_xmp;         /* Event XMPane pointer. */
106     register XMPane *cur_p;             /* Current pane. */
107     register XMSelect *cur_s;           /* Current selection. */
108     XMWindow *event_xmw;                /* Event XMWindow pointer. */
109     XEvent event;                       /* X input event. */
110     XEvent peek_event;                  /* X input peek ahead event. */
111
112     Bool selection = False;             /* Selection has been made. */
113     Bool forward = True;                /* Moving forward in the pane list. */
114
115     Window root, child;
116     int root_x, root_y, win_x, win_y;
117     unsigned int mask;
118
119     /*
120      * Define and allocate a foreign event queue to hold events
121      * that don't belong to XMenu.  These events are later restored
122      * to the X event queue.
123      */
124     typedef struct _xmeventque {
125         XEvent event;
126         struct _xmeventque *next;
127     } XMEventQue;
128
129     XMEventQue *feq = NULL;             /* Foreign event queue. */
130     XMEventQue *feq_tmp;                /* Foreign event queue temporary. */
131    
132     /*
133      * If there are no panes in the menu then return failure
134      * because the menu is not initialized.
135      */
136     if (menu->p_count == 0) {
137         _XMErrorCode = XME_NOT_INIT;
138         return(XM_FAILURE);
139     }
140
141     /*
142      * Find the desired current pane.
143      */
144     cur_p = _XMGetPanePtr(menu, *p_num);
145     if (cur_p == NULL) {
146         return(XM_FAILURE);
147     }
148     cur_p->activated = cur_p->active;
149
150     /*
151      * Find the desired current selection.
152      * If the current selection index is out of range a null current selection
153      * will be assumed and the cursor will be placed in the current pane
154      * header.
155      */
156     cur_s = _XMGetSelectionPtr(cur_p, *s_num);
157
158     /*
159      * Compute origin of menu so that cursor is in
160      * Correct pane and selection.
161      */
162     _XMTransToOrigin(display,
163                      menu,
164                      cur_p, cur_s,
165                      x_pos, y_pos,
166                      &orig_x, &orig_y);
167     menu->x_pos = orig_x;       /* Store X and Y coords of menu. */
168     menu->y_pos = orig_y;
169    
170     if (XMenuRecompute(display, menu) == XM_FAILURE) {
171         return(XM_FAILURE);
172     }
173
174     /*
175      * Flush the window creation queue.
176      * This batches all window creates since lazy evaluation
177      * is more efficient than individual evaluation.
178      * This routine also does an XFlush().
179      */
180     if (_XMWinQueFlush(display, menu, cur_p, cur_s) == _FAILURE) {
181         return(XM_FAILURE);
182     }
183
184     /*
185      * Make sure windows are in correct order (in case we were passed
186      * an already created menu in incorrect order.)
187      */
188     for(p_ptr = menu->p_list->next; p_ptr != cur_p; p_ptr = p_ptr->next)
189         XRaiseWindow(display, p_ptr->window);
190     for(p_ptr = menu->p_list->prev; p_ptr != cur_p->prev; p_ptr = p_ptr->prev)
191         XRaiseWindow(display, p_ptr->window);
192
193     /*
194      * Make sure all selection windows are mapped.
195      */
196     for (
197         p_ptr = menu->p_list->next;
198         p_ptr != menu->p_list;
199         p_ptr = p_ptr->next
200     ){
201         XMapSubwindows(display, p_ptr->window);
202     }
203
204     /*
205      * Synchronize the X buffers and the event queue.
206      * From here on, all events in the queue that don't belong to
207      * XMenu are sent back to the application via an application
208      * provided event handler or discarded if the application has
209      * not provided an event handler.
210      */
211     XSync(display, 0);
212    
213     /*
214      * Grab the mouse for menu input.
215      */
216    
217     status = XGrabPointer(
218                           display,
219                           menu->parent,
220                           True,
221                           event_mask,
222                           GrabModeAsync,
223                           GrabModeAsync,
224                           None,
225                           menu->mouse_cursor,
226                           CurrentTime
227                           );
228     if (status == _X_FAILURE) {
229         _XMErrorCode = XME_GRAB_MOUSE;
230         return(XM_FAILURE);
231     }
232
233     /*
234      * Map the menu panes.
235      */
236     XMapWindow(display, cur_p->window);
237     for (p_ptr = menu->p_list->next;
238          p_ptr != cur_p;
239          p_ptr = p_ptr->next)
240       XMapWindow(display, p_ptr->window);
241     for (p_ptr = cur_p->next;
242          p_ptr != menu->p_list;
243          p_ptr = p_ptr->next)
244       XMapWindow(display, p_ptr->window);
245
246     XRaiseWindow(display, cur_p->window);       /* Make sure current */
247                                                 /* pane is on top. */
248    
249     cur_s = NULL;                       /* Clear current selection. */
250
251     /*
252      * Begin event processing loop.
253      */
254     while (1) {
255         XNextEvent(display, &event);    /* Get next event. */
256         switch (event.type) {           /* Dispatch on the event type. */
257     case Expose:
258             event_xmp = (XMPane *)XLookUpAssoc(display,
259                                                menu->assoc_tab,
260                                                event.xexpose.window);
261             if (event_xmp == NULL) {
262                 /*
263                  * If AEQ mode is enabled then queue the event.
264                  */
265                 if (menu->aeq) {
266                     feq_tmp = (XMEventQue *)malloc(sizeof(XMEventQue));
267                     if (feq_tmp == NULL) {
268                         _XMErrorCode = XME_CALLOC;
269                         return(XM_FAILURE);
270                     }
271                     feq_tmp->event = event;
272                     feq_tmp->next = feq;
273                     feq = feq_tmp;
274                 }
275                 else if (_XMEventHandler) (*_XMEventHandler)(&event);
276                 break;
277             }
278             if (event_xmp->activated) {
279                 XSetWindowBackground(display,
280                                      event_xmp->window,
281                                      menu->bkgnd_color);
282             }
283             else {
284                 XSetWindowBackgroundPixmap(display,
285                                            event_xmp->window,
286                                            menu->inact_pixmap);
287             }
288             _XMRefreshPane(display, menu, event_xmp);
289             break;
290     case EnterNotify:
291             /*
292              * First wait a small period of time, and see
293              * if another EnterNotify event follows hard on the
294              * heels of this one. i.e., the user is simply
295              * "passing through". If so, ignore this one.
296              */
297        
298             event_xmw = (XMWindow *)XLookUpAssoc(display,
299                                                  menu->assoc_tab,
300                                                  event.xcrossing.window);
301             if (event_xmw == NULL) break;
302             if (event_xmw->type == SELECTION) {
303                 /*
304                  * We have entered a selection.
305                  */
306                 /* if (XPending(display) == 0) usleep(150000); */
307                 if (XPending(display) != 0) {
308                     XPeekEvent(display, &peek_event);
309                     if(peek_event.type == LeaveNotify) {
310                         break;
311                     }
312                 }                         
313                 cur_s = (XMSelect *)event_xmw;
314                 help_callback (cur_s->help_string,
315                                cur_p->serial, cur_s->serial);
316                
317                 /*
318                  * If the pane we are in is active and the
319                  * selection entered is active then activate
320                  * the selection.
321                  */
322                 if (cur_p->active && cur_s->active > 0) {
323                     cur_s->activated = 1;
324                     _XMRefreshSelection(display, menu, cur_s);
325                 }
326             }
327             else {
328                 /*
329                  * We have entered a pane.
330                  */
331                 /* if (XPending(display) == 0) usleep(150000); */
332                 if (XPending(display) != 0) {
333                     XPeekEvent(display, &peek_event);
334                     if (peek_event.type == EnterNotify) break;
335                 }
336                 XQueryPointer(display,
337                               menu->parent,
338                               &root, &child,
339                               &root_x, &root_y,
340                               &win_x, &win_y,
341                               &mask);
342                 event_xmp = (XMPane *)XLookUpAssoc(display,
343                                                    menu->assoc_tab,
344                                                    child);
345                 if (event_xmp == NULL) break;
346                 if (event_xmp == cur_p) break;
347                 if (event_xmp->serial > cur_p->serial) forward = True;
348                 else forward = False;
349                 p_ptr = cur_p;
350                 while (p_ptr != event_xmp) {
351                     if (forward) p_ptr = p_ptr->next;
352                     else p_ptr = p_ptr->prev;
353                     XRaiseWindow(display, p_ptr->window);
354                 }
355                 if (cur_p->activated) {
356                     cur_p->activated = False;
357                     XSetWindowBackgroundPixmap(display,
358                                                cur_p->window,
359                                                menu->inact_pixmap);
360                     _XMRefreshPane(display, menu, cur_p);
361                 }
362                 if (event_xmp->active) event_xmp->activated = True;
363 #if 1
364                 /*
365                  * i suspect the we don't get an EXPOSE event when backing
366                  * store is enabled; the menu windows content is probably
367                  * not drawn in when it should be in that case.
368                  * in that case, this is probably an ugly fix!
369                  * i hope someone more familiar with this code would
370                  * take it from here.  -- caveh@eng.sun.com.
371                  */
372                 XSetWindowBackground(display,
373                                      event_xmp->window,
374                                      menu->bkgnd_color);
375                 _XMRefreshPane(display, menu, event_xmp);
376 #endif
377                 cur_p = event_xmp;
378             }
379             break;
380     case LeaveNotify:
381             event_xmw = (XMWindow *)XLookUpAssoc(
382                                                  display,
383                                                  menu->assoc_tab,
384                                                  event.xcrossing.window
385                                                  );
386             if (event_xmw == NULL) break;
387             if(cur_s == NULL) break;
388            
389             /*
390              * If the current selection was activated then
391              * deactivate it.
392              */
393             if (cur_s->activated) {
394                 cur_s->activated = False;
395                 _XMRefreshSelection(display, menu, cur_s);
396             }
397             cur_s = NULL;
398             break;
399        
400     case ButtonPress:
401     case ButtonRelease:
402                 *p_num = cur_p->serial;
403                 /*
404                  * Check to see if there is a current selection.
405                  */
406                 if (cur_s != NULL) {
407                     /*
408                      * Set the selection number to the current selection.
409                      */
410                     *s_num = cur_s->serial;
411                     /*
412                      * If the current selection was activated then
413                      * we have a valid selection otherwise we have
414                      * an inactive selection.
415                      */
416                     if (cur_s->activated) {
417                         *data = cur_s->data;
418                         ret_val = XM_SUCCESS;
419                     }
420                     else {
421                         ret_val = XM_IA_SELECT;
422                     }
423                 }
424                 else {
425                     /*
426                      * No selection was current.
427                      */
428                     ret_val = XM_NO_SELECT;
429                 }
430                 selection = True;
431                 break;
432             default:
433                 /*
434                  * If AEQ mode is enabled then queue the event.
435                  */
436                 if (menu->aeq) {
437                     feq_tmp = (XMEventQue *)malloc(sizeof(XMEventQue));
438                     if (feq_tmp == NULL) {
439                         _XMErrorCode = XME_CALLOC;
440                         return(XM_FAILURE);
441                     }
442                     feq_tmp->event = event;
443                     feq_tmp->next = feq;
444                     feq = feq_tmp;
445                 }
446                 else if (_XMEventHandler) (*_XMEventHandler)(&event);
447         }
448         /*
449          * If a selection has been made, break out of the event loop.
450          */
451         if (selection == True) break;
452     }
453
454     /*
455      * Unmap the menu.
456      */
457     for ( p_ptr = menu->p_list->next;
458          p_ptr != menu->p_list;
459          p_ptr = p_ptr->next)
460       {
461           XUnmapWindow(display, p_ptr->window);
462       }
463
464     /*
465      * Ungrab the mouse.
466      */
467     XUngrabPointer(display, CurrentTime);
468
469     /*
470      * Restore bits under where the menu was if we managed
471      * to save them and free the pixmap.
472      */
473
474     /*
475      * If there is a current selection deactivate it.
476      */
477     if (cur_s != NULL) cur_s->activated = 0;
478
479     /*
480      * Deactivate the current pane.
481      */
482     cur_p->activated = 0;
483     XSetWindowBackgroundPixmap(display, cur_p->window, menu->inact_pixmap);
484
485     /*
486      * Synchronize the X buffers and the X event queue.
487      */
488     XSync(display, 0);
489    
490     /*
491      * Dispatch any events remaining on the queue.
492      */
493     while (QLength(display)) {
494         /*
495          * Fetch the next event.
496          */
497         XNextEvent(display, &event);
498
499         /*
500          * Discard any events left on the queue that belong to XMenu.
501          * All others are held and then returned to the event queue.
502          */
503         switch (event.type) {
504             case Expose:
505             case EnterNotify:
506             case LeaveNotify:
507             case ButtonPress:
508             case ButtonRelease:
509                 /*
510                  * Does this event belong to one of XMenu's windows?
511                  * If so, discard it and process the next event.
512                  * If not fall through and treat it as a foreign event.
513                  */
514                 event_xmp = (XMPane *)XLookUpAssoc(
515                                                    display,
516                                                    menu->assoc_tab,
517                                                    event.xbutton.window
518                                                    );
519                 if (event_xmp != NULL) continue;
520             default:
521                 /*
522                  * This is a foreign event.
523                  * Queue it for later return to the X event queue.
524                  */
525                 feq_tmp = (XMEventQue *)malloc(sizeof(XMEventQue));
526                 if (feq_tmp == NULL) {