mirror of
https://github.com/NishiOwO/fvwm1.git
synced 2025-04-21 16:54:44 +00:00
784 lines
22 KiB
C
784 lines
22 KiB
C
/****************************************************************************
|
|
* This module is based on Twm, but has been siginificantly modified
|
|
* by Rob Nation
|
|
****************************************************************************/
|
|
/*****************************************************************************/
|
|
/** 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. **/
|
|
/*****************************************************************************/
|
|
|
|
|
|
/***********************************************************************
|
|
*
|
|
* fvwm 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 "fvwm.h"
|
|
#include "menus.h"
|
|
#include "misc.h"
|
|
#include "parse.h"
|
|
#include "screen.h"
|
|
|
|
int menu_on=0;
|
|
|
|
MenuRoot *ActiveMenu = NULL; /* the active menu */
|
|
MenuItem *ActiveItem = NULL; /* the active menu item */
|
|
|
|
int menuFromFrameOrWindowOrTitlebar = FALSE;
|
|
|
|
extern int Context,Button;
|
|
extern FvwmWindow *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;
|
|
|
|
/* 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)
|
|
if(Scr.flags & MWMMenus)
|
|
x = Stashed_X+ (ActiveMenu->width >> 1) + (menu->width >> 1) - 3;
|
|
if(ActiveItem)
|
|
y = ActiveItem->y_offset + MenuY + (Scr.EntryHeight >>1);
|
|
}
|
|
else
|
|
{
|
|
if(!GrabEm(MENU))
|
|
{
|
|
XBell(dpy,Scr.screen);
|
|
return MENU_DONE;
|
|
}
|
|
if(Scr.flags & MWMMenus)
|
|
x += (menu->width >> 1) - 3;
|
|
}
|
|
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;
|
|
|
|
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;
|
|
|
|
|
|
if(Scr.flags & MWMMenus)
|
|
{
|
|
if((!mi->prev)||(!mi->prev->state))
|
|
XClearArea(dpy, mr->w, 0,y_offset-1,mr->width,y_height+2,0);
|
|
else
|
|
XClearArea(dpy, mr->w, 0,y_offset+1,mr->width,y_height-1,0);
|
|
if ((mi->state)&&(mi->func != F_TITLE)&&(mi->func != F_NOP)&&*mi->item)
|
|
{
|
|
RelieveRectangle(mr->w, 3, y_offset, mr->width-5, mi->y_height,
|
|
ReliefGC,ShadowGC);
|
|
RelieveRectangle(mr->w, 2, y_offset-1, mr->width-3, mi->y_height+2,
|
|
ReliefGC,ShadowGC);
|
|
}
|
|
RelieveHalfRectangle(mr->w, 0, y_offset-1, mr->width,
|
|
y_height+2, ReliefGC, ShadowGC);
|
|
}
|
|
else
|
|
{
|
|
XClearArea(dpy, mr->w, 0,y_offset,mr->width,y_height,0);
|
|
if ((mi->state)&&(mi->func != F_TITLE)&&(mi->func != F_NOP)&&*mi->item)
|
|
RelieveRectangle(mr->w, 2, y_offset, mr->width-4, mi->y_height,
|
|
ReliefGC,ShadowGC);
|
|
RelieveHalfRectangle(mr->w, 0, y_offset, mr->width,
|
|
y_height, ReliefGC, ShadowGC);
|
|
}
|
|
if(mi->func == F_TITLE)
|
|
{
|
|
if(Scr.flags & MWMMenus)
|
|
{
|
|
text_y += HEIGHT_EXTRA>>1;
|
|
XDrawLine(dpy, mr->w, ShadowGC, 2, y_offset+y_height-2,
|
|
mr->width-3, y_offset+y_height-2);
|
|
XDrawLine(dpy, mr->w, ShadowGC, 2, y_offset+y_height-4,
|
|
mr->width-3, y_offset+y_height-4);
|
|
}
|
|
else
|
|
{
|
|
if(mi->next != NULL)
|
|
{
|
|
DrawSeparator(mr->w,ShadowGC,ReliefGC,5, y_offset+y_height-3,
|
|
mr->width-6, y_offset+y_height-3,1);
|
|
}
|
|
if(mi != mr->first)
|
|
{
|
|
text_y += HEIGHT_EXTRA_TITLE>>1;
|
|
DrawSeparator(mr->w,ShadowGC,ReliefGC,5, y_offset+1,
|
|
mr->width-6, y_offset+1,1);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
text_y += HEIGHT_EXTRA>>1;
|
|
if(mi->func == F_NOP && *mi->item==0)
|
|
{
|
|
if(Scr.flags & MWMMenus)
|
|
DrawSeparator(mr->w,ShadowGC,ReliefGC,2,y_offset-1+HEIGHT_SEPARATOR/2,
|
|
mr->width-3,y_offset-1+HEIGHT_SEPARATOR/2,0);
|
|
else
|
|
DrawSeparator(mr->w,ShadowGC,ReliefGC,5,y_offset-1+HEIGHT_SEPARATOR/2,
|
|
mr->width-6,y_offset-1+HEIGHT_SEPARATOR/2,1);
|
|
}
|
|
if(mi->next == NULL)
|
|
DrawSeparator(mr->w,ShadowGC,ShadowGC,1,mr->height-2,
|
|
mr->width-2, mr->height-2,1);
|
|
if(mi == mr->first)
|
|
DrawSeparator(mr->w,ReliefGC,ReliefGC,0,0, mr->width-1,0,-1);
|
|
|
|
if(check_allowed_function(mi))
|
|
currentGC = Scr.MenuGC;
|
|
else
|
|
/* should be a shaded out word, no just re-colored. */
|
|
currentGC = Scr.MenuStippleGC;
|
|
|
|
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);
|
|
|
|
/* pete@tecc.co.uk: If the item has a hot key, underline it */
|
|
if (mi->hotkey > 0)
|
|
DrawUnderline(mr->w, currentGC,mi->x,text_y,mi->item,mi->hotkey - 1);
|
|
if (mi->hotkey < 0)
|
|
DrawUnderline(mr->w, currentGC,mi->x2,text_y,mi->item2, -1 - mi->hotkey);
|
|
|
|
d=(Scr.EntryHeight-7)/2;
|
|
if(mi->func == F_POPUP)
|
|
if(mi->state)
|
|
DrawTrianglePattern(mr->w, ShadowGC, ReliefGC, ShadowGC,mr->width-d-8,
|
|
y_offset+d-1, mr->width-d-1, y_offset+d+7);
|
|
else
|
|
DrawTrianglePattern(mr->w, ReliefGC, ShadowGC, ReliefGC,mr->width-d-8,
|
|
y_offset+d-1, mr->width-d-1, y_offset+d+7);
|
|
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;
|
|
PaintEntry(ActiveMenu, mi);
|
|
}
|
|
ActiveItem = mi;
|
|
|
|
if(ActiveItem)
|
|
{
|
|
/* create a new sub-menu */
|
|
if((ActiveItem->func == F_POPUP)&&
|
|
((Scr.flags & MWMMenus)||(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 (((keysym >= XK_a) && (keysym <= XK_z)) || /* Only consider alphabetic */
|
|
((keysym >= XK_0) && (keysym <= XK_9))) /* ...or numeric keys */
|
|
{
|
|
/* 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 > 0) ? /* Extract hot character */
|
|
mi->item[mi->hotkey - 1] : mi->item2[-1 - mi->hotkey];
|
|
|
|
/* Convert to lower case to match the keysym */
|
|
if (isupper(key)) key = tolower(key);
|
|
|
|
if (keysym == key)
|
|
{ /* Are they equal? */
|
|
ActiveItem = mi; /* Yes: Make this the active item */
|
|
ev->type = ButtonRelease; /* Force a menu exit */
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
switch(keysym) /* Other special keyboard handling */
|
|
{
|
|
case XK_Escape: /* Escape key pressed. Abort */
|
|
ActiveItem = NULL; /* No selection */
|
|
ev->type = ButtonRelease; /* Make the menu exit */
|
|
break;
|
|
|
|
/* Nothing special --- Allow other shortcuts (cursor movement) */
|
|
default:
|
|
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|VisibilityChangeMask|ButtonMotionMask, &Event);
|
|
StashEventTime(&Event);
|
|
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 ButtonRelease:
|
|
/* The following lines holds the menu when the button is released */
|
|
if ((Scr.flags & MWMMenus) && !ActiveItem && (menu_on > 1))
|
|
{
|
|
int x,y;
|
|
XQueryPointer( dpy, Scr.Root, &JunkRoot, &JunkChild,
|
|
&JunkX, &JunkY, &x, &y, &JunkMask);
|
|
if((XFindContext(dpy, JunkChild,MenuContext,
|
|
(caddr_t *)&actual_mr)!=XCNOENT)&&
|
|
(actual_mr != ActiveMenu))
|
|
{
|
|
done = 1;
|
|
break;
|
|
}
|
|
}
|
|
PopDownMenu();
|
|
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:
|
|
case ButtonPress:
|
|
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((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)
|
|
{
|
|
y = Scr.MyDisplayHeight - menu->height-2;
|
|
/* Warp pointer to middle of top line */
|
|
/* Not with MWMMenus! */
|
|
if((!(Scr.flags & MWMMenus))||(menu_on < 2))
|
|
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;
|
|
|
|
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);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|