/****************************************************************************
* This module is based on Twm, but has been siginificantly modified
* by Rob Nation
*
* later modified for BowMan
* by Bo Yang
*
* modified again for AfterStep
* by Frank Fejes
*
* yet more modifications
* by Alfredo Kojima
****************************************************************************/
/*****************************************************************************/
/** Copyright 1988 by Evans & Sutherland Computer Corporation, **/
/** Salt Lake City, Utah **/
/** Portions Copyright 1989 by the Massachusetts Institute of Technology **/
/** Cambridge, Massachusetts **/
/** **/
/** All Rights Reserved **/
/** **/
/** Permission to use, copy, modify, and distribute this software and **/
/** its documentation for any purpose and without fee is hereby **/
/** granted, provided that the above copyright notice appear in all **/
/** copies and that both that copyright notice and this permis- **/
/** sion notice appear in supporting documentation, and that the **/
/** names of Evans & Sutherland and M.I.T. not be used in advertising **/
/** in publicity pertaining to distribution of the software without **/
/** specific, written prior permission. **/
/** **/
/** EVANS & SUTHERLAND AND M.I.T. DISCLAIM ALL WARRANTIES WITH REGARD **/
/** TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- **/
/** ABILITY AND FITNESS, IN NO EVENT SHALL EVANS & SUTHERLAND OR **/
/** M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAM- **/
/** AGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA **/
/** OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER **/
/** TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE **/
/** OR PERFORMANCE OF THIS SOFTWARE. **/
/*****************************************************************************/
/***********************************************************************
*
* afterstep menu code
*
***********************************************************************/
#include "../configure.h"
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <ctype.h>
#include <X11/keysym.h>
#include <sys/types.h>
#include <sys/time.h>
#include "afterstep.h"
#include "menus.h"
#include "misc.h"
#include "parse.h"
#include "screen.h"
#ifdef ENABLE_TEXTURE
#include "stepgfx.h"
#endif
short menu_on=0;
Bool IgnoreRelease = 1;
int BottomOfPage=0;
enum {MWAIT, MCLICK, MHOLD} MenuMode;
Time MenuModeCommitTime;
MenuRoot *ActiveMenu = NULL; /* the active menu */
MenuItem *ActiveItem = NULL; /* the active menu item */
int menuFromFrameOrWindowOrTitlebar = FALSE;
extern int Context,Button;
extern ASWindow *ButtonWindow, *Tmp_win;
extern XEvent Event;
int Stashed_X, Stashed_Y,MenuY=0;
void DrawTrianglePattern(Window,GC,GC,GC,int,int,int,int);
void DrawSeparator(Window, GC,GC,int, int,int,int,int);
void DrawUnderline(Window w, GC gc, int x, int y, char *txt, int off);
int UpdateMenu(void);
extern XContext MenuContext;
/****************************************************************************
*
* Initiates a menu pop-up
*
***************************************************************************/
int do_menu (MenuRoot *menu)
{
int prevStashedX=0,prevStashedY=0;
MenuRoot *PrevActiveMenu=0;
MenuItem *PrevActiveItem=0;
int retval=MENU_NOP;
int x,y,offset;
/* this condition could get ugly */
if(menu->in_use)
return MENU_ERROR;
/* In case we wind up with a move from a menu which is
* from a window border, we'll return to here to start
* the move */
XQueryPointer( dpy, Scr.Root, &JunkRoot, &JunkChild,
&x, &y, &JunkX, &JunkY, &JunkMask);
if(menu_on)
{
prevStashedX = Stashed_X;
prevStashedY = Stashed_Y;
PrevActiveMenu = ActiveMenu;
PrevActiveItem = ActiveItem;
if(ActiveMenu) {
/* Offset the new menu depending on the width (odd/even)
of its parent menu */
offset = ((ActiveMenu->width % 2) ? 1 : 0);
x = (Stashed_X < (ActiveMenu->width >> 1)? ActiveMenu->width-1 :
Stashed_X + (ActiveMenu->width >> 1)) + (menu->width >> 1)
+ offset;
}
if(ActiveItem) {
if (!(Scr.flags & MenusHigh))
y = MenuY + ((ActiveItem->item_num)*(ActiveItem->y_height))-(Scr.EntryHeight & 1) - (Scr.EntryHeight >>1);
else
y = MenuY + (Scr.EntryHeight >>1);
}
}
else
{
if(!GrabEm(MENU))
{
XBell(dpy,Scr.screen);
return MENU_DONE;
}
MenuMode = MWAIT;
MenuModeCommitTime = lastTimestamp + Scr.ClickTime;
}
#ifdef ENABLE_TEXTURE
if (Scr.d_depth >= 8) {
if ((menu->titlebg==None) && (Textures.Mtype>0)) {
menu->titlebg = XCreatePixmap(dpy, menu->w, menu->width-2,
menu->first->y_height-2,
Scr.d_depth);
switch (Textures.Mtype) {
case 1:/* NW to SE gradient */
if (!DrawDegradeRelief(dpy, menu->titlebg, 0, 0, menu->width-2,
menu->first->y_height-2,
Textures.Mfrom, Textures.Mto, 1,
Textures.Mmaxcols)) {
XFreePixmap(dpy, menu->titlebg);
menu->titlebg = None;
}
break;
case 2: /* horizontal gradient */
case 3: /* horiz. cylindrical */
if (!DrawHGradient(dpy, menu->titlebg, 0, 0, menu->width-2,
menu->first->y_height-2,
Textures.Mfrom, Textures.Mto, 1,
Textures.Mmaxcols,Textures.Mtype-2)) {
XFreePixmap(dpy, menu->titlebg);
menu->titlebg = None;
}
break;
case 4: /* vertical gradient */
case 5: /* vert. cylindrical */
if (!DrawVGradient(dpy, menu->titlebg, 0, 0, menu->width-2,
menu->first->y_height-2,
Textures.Mfrom, Textures.Mto, 1,
Textures.Mmaxcols,Textures.Mtype-4)) {
XFreePixmap(dpy, menu->titlebg);
menu->titlebg = None;
}
break;
default:
XFreePixmap(dpy, menu->titlebg);
menu->titlebg = None;
afterstep_err("Invalid TextureType %i specified for menu title\n",
(char *)Textures.Mtype, NULL,NULL);
Textures.Mtype=0;
}
}
if ((menu->itembg==None) && (Textures.Itype>0) && (menu->last!=NULL)) {
menu->itembg = XCreatePixmap(dpy, menu->w, menu->width-2,
menu->last->y_height-3,
Scr.d_depth);
switch (Textures.Itype) {
case 1:/* NW to SE gradient */
if (!DrawDegradeRelief(dpy, menu->itembg, 0, 0,
menu->width-2,menu->last->y_height-3,
Textures.Ifrom, Textures.Ito, 0,
Textures.Imaxcols)) {
XFreePixmap(dpy, menu->itembg);
menu->itembg = None;
}
break;
case 2: /* horizontal gradient */
case 3: /* horiz. cylindrical */
if (!DrawHGradient(dpy, menu->itembg, 0, 0, menu->width-2,
menu->last->y_height-3,
Textures.Ifrom, Textures.Ito, 0,
Textures.Imaxcols,Textures.Itype-2)) {
XFreePixmap(dpy, menu->itembg);
menu->itembg = None;
}
break;
case 4: /* vertical gradient */
case 5: /* vert. cylindrical */
if (!DrawVGradient(dpy, menu->itembg, 0, 0, menu->width-2,
menu->last->y_height-3,
Textures.Ifrom, Textures.Ito, 0,
Textures.Imaxcols,Textures.Itype-4)) {
XFreePixmap(dpy, menu->itembg);
menu->itembg = None;
}
break;
default:
XFreePixmap(dpy, menu->itembg);
menu->itembg = None;
afterstep_err("Invalid TextureType %i specified for menu item\n",
(char *)Textures.Itype,NULL,NULL);
Textures.Itype = 0;
}
}
}
#endif /* ENABLE_TEXTURE */
if (PopUpMenu (menu, x, y))
{
retval = UpdateMenu();
}
else
XBell (dpy, Scr.screen);
ActiveMenu = PrevActiveMenu;
ActiveItem = PrevActiveItem;
if((ActiveItem)&&(menu_on))
ActiveItem->state = 1;
Stashed_X = prevStashedX;
Stashed_Y = prevStashedY;
if(!menu_on)
{
UngrabEm();
WaitForButtonsUp();
}
return retval;
}
/***********************************************************************
*
* Procedure:
* RelieveRectangle - add relief lines to a rectangular window
*
***********************************************************************/
void RelieveRectangle(Window win,int x,int y,int w, int h,GC Hilite,GC Shadow)
{
XDrawLine(dpy, win, Hilite, x, y, w+x-1, y);
XDrawLine(dpy, win, Hilite, x, y, x, h+y-1);
XDrawLine(dpy, win, Shadow, x, h+y-1, w+x-1, h+y-1);
XDrawLine(dpy, win, Shadow, w+x-1, y, w+x-1, h+y-1);
}
/***********************************************************************
*
* Procedure:
* RelieveHalfRectangle - add relief lines to the sides only of a
* rectangular window
*
***********************************************************************/
void RelieveHalfRectangle(Window win,int x,int y,int w,int h,
GC Hilite,GC Shadow)
{
XDrawLine(dpy, win, Hilite, x, y-1, x, h+y);
XDrawLine(dpy, win, Hilite, x+1, y, x+1, h+y-1);
XDrawLine(dpy, win, Shadow, w+x-1, y-1, w+x-1, h+y);
XDrawLine(dpy, win, Shadow, w+x-2, y, w+x-2, h+y-1);
}
/***********************************************************************
*
* Procedure:
* PaintEntry - draws a single entry in a poped up menu
*
***********************************************************************/
void PaintEntry(MenuRoot *mr, MenuItem *mi)
{
int y_offset,text_y,d, y_height;
GC ShadowGC, ReliefGC, currentGC;
char hk[2];
hk[1] = 0;
y_offset = mi->y_offset;
y_height = mi->y_height;
text_y = y_offset + Scr.StdFont.y;
ShadowGC = Scr.MenuShadowGC;
if(Scr.d_depth<2)
ReliefGC = Scr.MenuShadowGC;
else
ReliefGC = Scr.MenuReliefGC;
/* stuff */
if((mi->state)&&(mi->func != F_TITLE)&&(mi->func != F_NOP)&&*mi->item) {
Globalgcv.foreground = Scr.HiColors.fore;
XChangeGC(dpy,Scr.ScratchGC1,Globalgcm,&Globalgcv);
XFillRectangle(dpy, mr->w, Scr.ScratchGC1, 2, y_offset,
mr->width-2, y_height-2);
} else if( mi->func == F_TITLE) {
#ifdef ENABLE_TEXTURE
if (mr->titlebg != None) {
Globalgcv.foreground = Scr.HiColors.back;
XChangeGC(dpy,Scr.ScratchGC1,GCForeground,&Globalgcv);
XCopyArea(dpy, mr->titlebg, mr->w, DefaultGC(dpy,Scr.screen),
0, y_offset, mr->width-1, mr->first->y_height-2, 1,
y_offset+1);
XDrawRectangle(dpy, mr->w, Scr.ScratchGC1, 0, y_offset,
mr->width-1, y_height-2);
text_y += 4;
}
else
#endif
{
Globalgcv.foreground = Scr.HiColors.back;
XChangeGC(dpy,Scr.ScratchGC1,Globalgcm,&Globalgcv);
XFillRectangle(dpy, mr->w, Scr.ScratchGC1, 0, y_offset, mr->width-1,
y_height-2);
text_y += 4;
}
}
else {
#ifdef ENABLE_TEXTURE
if (mr->itembg!=None) {
XCopyArea(dpy, mr->itembg, mr->w, DefaultGC(dpy,Scr.screen), 0, 0,
mr->width-2, mr->last->y_height-3, 1, y_offset+1);
} else
#endif
XClearArea(dpy, mr->w, 0,y_offset,mr->width,y_height,0);
}
#ifdef ENABLE_TEXTURE
if (mr->titlebg == None || mi->func != F_TITLE) {
RelieveHalfRectangle(mr->w, 0, y_offset, mr->width,
y_height, ReliefGC, ShadowGC);
RelieveRectangle(mr->w, 0, y_offset, mr->width, y_height-1,
ReliefGC, ShadowGC);
/* hack
XDrawLine( dpy, mr->w, Scr.LineGC, 0, 22, 0, 1280);
*/
/* end hack */
if(mi->func != F_TITLE)
text_y += HEIGHT_EXTRA>>1;
XDrawLine( dpy, mr->w, Scr.LineGC, 0, y_height+y_offset-1,
mr->width, y_height+y_offset-1);
}
#else
RelieveHalfRectangle(mr->w, 0, y_offset, mr->width,
y_height, ReliefGC, ShadowGC);
RelieveRectangle(mr->w, 0, y_offset, mr->width, y_height-1,
ReliefGC, ShadowGC);
/* kludge
XDrawLine( dpy, mr->w, Scr.LineGC, 0, 0, 0, 1280);
*/
if(mi->func != F_TITLE)
text_y += HEIGHT_EXTRA>>1;
XDrawLine( dpy, mr->w, Scr.LineGC, 0, y_height+y_offset-1,
mr->width, y_height+y_offset-1);
/*
XDrawLine( dpy, mr->w, Scr.LineGC, 0, y_offset,
0, y_offset+y_height-1);
*/
#endif /* ENABLE_TEXTURE */
if( mi->func == F_TITLE) {
Globalgcv.foreground = Scr.HiColors.fore;
XChangeGC(dpy,Scr.ScratchGC1,Globalgcm,&Globalgcv);
currentGC = Scr.ScratchGC1;
}
else if(check_allowed_function(mi))
currentGC = Scr.MenuGC;
else
/* should be a shaded out word, no just re-colored. */
currentGC = Scr.MenuStippleGC;
if (Scr.d_depth<2 && mi->state) {
Globalgcv.foreground = WhitePixel(dpy,Scr.screen); /* ??? */
XChangeGC(dpy,Scr.ScratchGC1,Globalgcm,&Globalgcv);
currentGC = Scr.ScratchGC1;
}
if (mi->item_num == 0) { /* when paintig first entry(menu title) */
text_y = Scr.WindowFont.y + 4;
#undef FONTSET
#define FONTSET Scr.WindowFont.fontset
if(*mi->item)
XDrawString(dpy, mr->w, currentGC,mi->x,text_y, mi->item, mi->strlen);
if(mi->strlen2>0)
XDrawString(dpy, mr->w, currentGC,mi->x2,text_y, mi->item2,mi->strlen2);
d=(Scr.EntryHeight-7)/2;
if(mi->func != F_POPUP && mi->hotkey != 0) {
hk[0]= mi->hotkey;
XDrawString(dpy, mr->w, currentGC,
mr->width-d-4- XTextWidth(Scr.StdFont.font, hk, 1)/2,
text_y, hk, 1);
}
} else {
#undef FONTSET
#define FONTSET Scr.StdFont.fontset
if(*mi->item)
XDrawString(dpy, mr->w, currentGC,mi->x,text_y, mi->item, mi->strlen);
if(mi->strlen2>0)
XDrawString(dpy, mr->w, currentGC,mi->x2,text_y, mi->item2,mi->strlen2);
d=(Scr.EntryHeight-7)/2;
if(mi->func != F_POPUP && mi->hotkey != 0) {
hk[0]= mi->hotkey;
XDrawString(dpy, mr->w, currentGC,
mr->width-d-4- XTextWidth(Scr.StdFont.font, hk, 1)/2,
text_y, hk, 1);
}
}
d=(Scr.EntryHeight-7)/2;
if(mi->func == F_POPUP)
DrawTrianglePattern(mr->w, ShadowGC, ReliefGC, ShadowGC,mr->width-d-8,
y_offset+d-1, mr->width-d-1, y_offset+d+7);
/* major hackage going on here */
if (y_offset > 30)
XDrawLine( dpy, mr->w, Scr.LineGC, 0, y_offset-1, 1600, y_offset-1);
else
XDrawLine( dpy, mr->w, Scr.MenuStippleGC, 0, y_offset-1, 1600, y_offset-1);
return;
}
/****************************************************************************
* Procedure:
* DrawUnderline() - Underline a character in a string (pete@tecc.co.uk)
*
* Calculate the pixel offsets to the start of the character position we
* want to underline and to the next character in the string. Shrink by
* one pixel from each end and the draw a line that long two pixels below
* the character...
*
****************************************************************************/
void DrawUnderline(Window w, GC gc, int x, int y, char *txt, int posn)
{
int off1 = XTextWidth(Scr.StdFont.font, txt, posn);
int off2 = XTextWidth(Scr.StdFont.font, txt, posn + 1) - 1;
XDrawLine(dpy, w, gc, x + off1, y + 2, x + off2, y + 2);
}
/****************************************************************************
*
* Draws two horizontal lines to form a separator
*
****************************************************************************/
void DrawSeparator(Window w, GC TopGC, GC BottomGC,int x1,int y1,int x2,int y2,
int extra_off)
{
XDrawLine(dpy, w, TopGC , x1, y1, x2, y2);
XDrawLine(dpy, w, BottomGC, x1-extra_off, y1+1,x2+extra_off,y2+1);
}
/****************************************************************************
*
* Draws a little Triangle pattern within a window
*
****************************************************************************/
void DrawTrianglePattern(Window w,GC GC1,GC GC2,GC GC3,int l,int u,int r,int b)
{
int m;
m = (u + b)/2;
XDrawLine(dpy,w,GC1,l,u,l,b);
XDrawLine(dpy,w,GC2,l,b,r,m);
XDrawLine(dpy,w,GC3,r,m,l,u);
}
/***********************************************************************
*
* Procedure:
* PaintMenu - draws the entire menu
*
***********************************************************************/
void PaintMenu(MenuRoot *mr, XEvent *e)
{
MenuItem *mi;
for (mi = mr->first; mi != NULL; mi = mi->next)
{
/* be smart about handling the expose, redraw only the entries
* that we need to
*/
if (e->xexpose.y < (mi->y_offset + mi->y_height) &&
(e->xexpose.y + e->xexpose.height) > mi->y_offset)
{
PaintEntry(mr, mi);
}
}
XSync(dpy, 0);
return;
}
MenuRoot *PrevMenu = NULL;
MenuItem *PrevItem = NULL;
int PrevY=0;
/***********************************************************************
*
* Procedure:
* Updates menu display to reflect the highlighted item
*
***********************************************************************/
int FindEntry(void)
{
MenuItem *mi;
MenuRoot *actual_mr;
int retval = MENU_NOP;
MenuRoot *PrevPrevMenu;
MenuItem *PrevPrevItem;
int PrevPrevY;
int x, y, ChildY;
Window Child;
XQueryPointer( dpy, Scr.Root, &JunkRoot, &Child,
&JunkX,&ChildY, &x, &y, &JunkMask);
XQueryPointer( dpy, ActiveMenu->w, &JunkRoot, &JunkChild,
&JunkX, &ChildY, &x, &y, &JunkMask);
/* look for the entry that the mouse is in */
for(mi=ActiveMenu->first; mi; mi=mi->next)
if(y>=mi->y_offset && y<mi->y_offset+mi->y_height)
break;
if(x<0 || x>ActiveMenu->width)
mi = NULL;
/* if we weren't on the active entry, let's turn the old active one off */
if ((ActiveItem)&&(mi!=ActiveItem))
{
ActiveItem->state = 0;
PaintEntry(ActiveMenu, ActiveItem);
}
/* if we weren't on the active item, change the active item and turn it on */
if ((mi!=ActiveItem)&&(mi != NULL))
{
mi->state = 1;
/* blah */
if (mi->func != F_TITLE)
PaintEntry(ActiveMenu, mi);
}
ActiveItem = mi;
if(ActiveItem)
{
/* create a new sub-menu */
if((ActiveItem->func == F_POPUP)&& (x>(3*ActiveMenu->width>>2)))
{
PrevPrevMenu = PrevMenu;
PrevPrevItem = PrevItem;
PrevPrevY = PrevY;
PrevY = MenuY;
PrevMenu = ActiveMenu;
PrevItem = ActiveItem;
retval = do_menu(ActiveItem->menu);
/* Unfortunately, this is needed (why?) for multi-screen operation */
flush_expose(ActiveMenu->w);
for (mi = ActiveMenu->first; mi != NULL; mi = mi->next)
{
PaintEntry(ActiveMenu, mi);
}
XSync(dpy, 0);
MenuY = PrevY;
PrevMenu = PrevPrevMenu;
PrevItem = PrevPrevItem;
PrevY = PrevPrevY;
}
}
/* end a sub-menu */
if (XFindContext (dpy, Child,MenuContext,(caddr_t *)&actual_mr)==XCNOENT)
{
return retval;
}
if(actual_mr != ActiveMenu)
{
if(actual_mr == PrevMenu)
{
if((PrevItem->y_offset + PrevY > ChildY)||
((PrevItem->y_offset+PrevItem->y_height + PrevY) < ChildY))
{
return SUBMENU_DONE;
}
}
else
{
return SUBMENU_DONE;
}
}
return retval;
}
/***********************************************************************
* Procedure
* menuShortcuts() - Menu keyboard processing (pete@tecc.co.uk)
*
* Function called from UpdateMenu instead of Keyboard_Shortcuts()
* when a KeyPress event is received. If the key is alphanumeric,
* then the menu is scanned for a matching hot key. Otherwise if
* it was the escape key then the menu processing is aborted.
* If none of these conditions are true, then the default processing
* routine is called.
***********************************************************************/
void menuShortcuts(XEvent *ev)
{
MenuItem *mi;
KeySym keysym = XLookupKeysym(&ev->xkey,0);
/* Try to match hot keys */
if (menu_on) {
if (((keysym >= XK_a) && (keysym <= XK_z)) || /* Only consider alphabetic */
((keysym >= XK_A) && (keysym <= XK_Z)) || /* Only consider alphabetic */
((keysym >= XK_0) && (keysym <= XK_9))) /* ...or numeric keys */
{
keysym=toupper(keysym);
/* Search menu for matching hotkey */
for (mi = ActiveMenu->first; mi; mi = mi->next)
{
char key;
if (mi->hotkey == 0) continue; /* Item has no hotkey */
key = mi->hotkey;
if (keysym == key)
{ /* Are they equal? */
ActiveItem = mi; /* Yes: Make this the active item */
if (MenuMode == MWAIT) MenuMode = MCLICK;
IgnoreRelease = 0;
ev->type = ButtonRelease;
return;
}
}
}
}
switch(keysym) /* Other special keyboard handling */
{
case XK_Escape: /* Escape key pressed. Abort */
ActiveItem = NULL; /* No selection */
ev->type = ButtonRelease;
if (MenuMode == MWAIT) MenuMode = MCLICK;
IgnoreRelease = 0;
break;
/* Nothing special --- Allow other shortcuts (cursor movement) */
default:
if (MenuMode != MHOLD) {
MenuMode = MCLICK;
IgnoreRelease = 0;
}
Keyboard_shortcuts(ev,ButtonRelease);
break;
}
}
/***********************************************************************
*
* Procedure:
* Updates menu display to reflect the highlighted item
*
* Returns:
* 0 on error condition
* 1 on return from submenu to parent menu
* 2 on button release return
*
***********************************************************************/
int UpdateMenu(void)
{
int done,func;
int retval;
MenuRoot *actual_mr;
FindEntry();
while (TRUE)
{
/* block until there is an event */
XMaskEvent(dpy, ButtonPressMask|ButtonReleaseMask|ExposureMask |
KeyPressMask | ButtonMotionMask, &Event);
StashEventTime(&Event);
if ((MenuMode == MWAIT) && (lastTimestamp > MenuModeCommitTime))
MenuMode = MHOLD;
done = 0;
if (Event.type == MotionNotify)
{
/* discard any extra motion events before a release */
while((XCheckMaskEvent(dpy,ButtonMotionMask|ButtonReleaseMask,
&Event))&&(Event.type != ButtonRelease));
}
/* Handle a limited number of key press events to allow mouseless
* operation */
if(Event.type == KeyPress)
menuShortcuts(&Event);
switch(Event.type)
{
case ButtonPress:
IgnoreRelease = 0;
done=1;
break;
case ButtonRelease:
if (MenuMode == MWAIT) {
MenuMode = MCLICK;
IgnoreRelease = 0;
break;
}
if ((MenuMode == MCLICK) && IgnoreRelease) {
IgnoreRelease = 0;
break;
}
IgnoreRelease = 1;
PopDownMenu();
BottomOfPage = 0;
if(ActiveItem)
{
func = ActiveItem->func;
done = 1;
if(ButtonWindow)
{
ExecuteFunction(func, ActiveItem->action,
ButtonWindow->frame,
ButtonWindow, &Event, Context,
ActiveItem->val1,ActiveItem->val2,
ActiveItem->val1_unit,ActiveItem->val2_unit,
ActiveItem->menu,-1);
}
else
{
ExecuteFunction(func,ActiveItem->action,
None,None, &Event,
Context,ActiveItem->val1,
ActiveItem->val2,
ActiveItem->val1_unit,ActiveItem->val2_unit,
ActiveItem->menu,-1);
}
}
ActiveItem = NULL;
ActiveMenu = NULL;
menuFromFrameOrWindowOrTitlebar = FALSE;
return MENU_DONE;
case KeyPress:
case VisibilityNotify:
done=1;
break;
case MotionNotify:
done = 1;
retval = FindEntry();
if((retval == MENU_DONE)||(retval == SUBMENU_DONE))
{
PopDownMenu();
ActiveItem = NULL;
ActiveMenu = NULL;
menuFromFrameOrWindowOrTitlebar = FALSE;
}
if(retval == MENU_DONE)
return MENU_DONE;
else if (retval == SUBMENU_DONE)
return MENU_NOP;
break;
case Expose:
/* grab our expose events, let the rest go through */
if((XFindContext(dpy, Event.xany.window,MenuContext,
(caddr_t *)&actual_mr)!=XCNOENT))
{
PaintMenu(actual_mr,&Event);
done = 1;
}
break;
default:
break;
}
if(!done)DispatchEvent();
XFlush(dpy);
}
}
/***********************************************************************
*
* Procedure:
* PopUpMenu - pop up a pull down menu
*
* Inputs:
* menu - the root pointer of the menu to pop up
* x, y - location of upper left of menu
* center - whether or not to center horizontally over position
*
***********************************************************************/
Bool PopUpMenu (MenuRoot *menu, int x, int y)
{
if ((!menu)||(menu->w == None)||(menu->items == 0)||(menu->in_use))
return False;
menu_on++;
InstallRootColormap();
Stashed_X = x;
Stashed_Y = y;
/* pop up the menu */
ActiveMenu = menu;
ActiveItem = NULL;
x -= (menu->width >> 1);
y -= (Scr.EntryHeight >> 1);
if ( BottomOfPage != 1 )
{
if((Tmp_win)&&(menu_on == 1)&&(Context&C_LALL))
{
y = Tmp_win->frame_y+Tmp_win->boundary_width+Tmp_win->title_height+1;
x = Tmp_win->frame_x + Tmp_win->boundary_width +
Button*Tmp_win->title_height+1;
}
if((Tmp_win)&&(menu_on==1)&&(Context&C_RALL))
{
y = Tmp_win->frame_y+Tmp_win->boundary_width+Tmp_win->title_height+1;
x = Tmp_win->frame_x +Tmp_win->frame_width - Tmp_win->boundary_width-
Button*Tmp_win->title_height - menu->width+1;
}
if((Tmp_win)&&(menu_on==1)&&(Context&C_TITLE))
{
y = Tmp_win->frame_y+Tmp_win->boundary_width+Tmp_win->title_height+1;
if(x < Tmp_win->frame_x + Tmp_win->title_x)
x = Tmp_win->frame_x + Tmp_win->title_x;
if((x + menu->width) >
(Tmp_win->frame_x + Tmp_win->title_x +Tmp_win->title_width))
x = Tmp_win->frame_x + Tmp_win->title_x +Tmp_win->title_width-
menu->width +1;
}
/* clip to screen */
if (x + menu->width > Scr.MyDisplayWidth-2)
x = Scr.MyDisplayWidth - menu->width-2;
if (x < 0) x = 0;
if (y + menu->height > Scr.MyDisplayHeight-2)
{
if (menu_on == 1)
BottomOfPage = 1;
y = Scr.MyDisplayHeight - menu->height-2;
/* Warp pointer to middle of top line */
/* XWarpPointer(dpy, Scr.Root, Scr.Root, 0, 0, Scr.MyDisplayWidth,
Scr.MyDisplayHeight,
x + (menu->width>>1), (y + (Scr.EntryHeight >> 1)));
*/
}
if (y < 0) y = 0;
}
else
{
y = (Scr.MyDisplayHeight - (menu->height+2));
if (y < 0) y = 0;
if (x + menu->width > Scr.MyDisplayWidth-2)
x = Scr.MyDisplayWidth - menu->width-2;
if (x < 0) x = 0;
}
MenuY = y;
XMoveWindow(dpy, menu->w, x, y);
XMapRaised(dpy, menu->w);
menu->in_use = True;
return True;
}
/***********************************************************************
*
* Procedure:
* PopDownMenu - unhighlight the current menu selection and
* take down the menus
*
***********************************************************************/
void PopDownMenu()
{
if (ActiveMenu == NULL)
return;
menu_on--;
if (ActiveItem)
ActiveItem->state = 0;
XUnmapWindow(dpy, ActiveMenu->w);
UninstallRootColormap();
XFlush(dpy);
if (Context & (C_WINDOW | C_FRAME | C_TITLE | C_SIDEBAR))
menuFromFrameOrWindowOrTitlebar = TRUE;
else
menuFromFrameOrWindowOrTitlebar = FALSE;
ActiveMenu->in_use = FALSE;
}
/***************************************************************************
*
* Wait for all mouse buttons to be released
* This can ease some confusion on the part of the user sometimes
*
* Discard superflous button events during this wait period.
*
***************************************************************************/
void WaitForButtonsUp()
{
Bool AllUp = False;
XEvent JunkEvent;
unsigned int mask;
while(!AllUp)
{
XAllowEvents(dpy,ReplayPointer,CurrentTime);
XQueryPointer( dpy, Scr.Root, &JunkRoot, &JunkChild,
&JunkX, &JunkY, &JunkX, &JunkY, &mask);
if((mask&
(Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask))==0)
AllUp = True;
}
XSync(dpy,0);
while(XCheckMaskEvent(dpy,
ButtonPressMask|ButtonReleaseMask|ButtonMotionMask,
&JunkEvent))
{
StashEventTime (&JunkEvent);
XAllowEvents(dpy,ReplayPointer,CurrentTime);
}
}
syntax highlighted by Code2HTML, v. 0.9.1