v12i070: olvwm - Open Look Virtual Window Manager, Part14/16

Scott Oaks - Sun Consulting NYC sdo at soliado.East.Sun.COM
Mon Apr 29 03:31:21 AEST 1991


Submitted-by: sdo at soliado.East.Sun.COM (Scott Oaks - Sun Consulting NYC)
Posting-number: Volume 12, Issue 70
Archive-name: olvwm/part14

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 14 (of 16)."
# Contents:  menu.c
# Wrapped by sdo at piccolo on Fri Apr 26 17:31:10 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'menu.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'menu.c'\"
else
echo shar: Extracting \"'menu.c'\" \(31690 characters\)
sed "s/^X//" >'menu.c' <<'END_OF_FILE'
X/*
X *      (c) Copyright 1989, 1990 Sun Microsystems, Inc. Sun design patents
X *      pending in the U.S. and foreign countries. See LEGAL_NOTICE
X *      file for terms of the license.
X *
X *	Written for Sun Microsystems by Crucible, Santa Cruz, CA.
X */
X
static char     sccsid[] = "@(#)menu.c	1.2 olvwm version 3/30/91";
X
X/*
X * Based on
static	char	sccsid[] = "@(#) menu.c 25.8 90/05/22 Crucible";
X *
X */
X
X/*
X * This file contains all of the functions for creating and displaying menus.
X *
X * Global Functions:
X * InitMenus	-- initialize menu stuff
X * MenuCreate	-- create a new menu
X * MenuShow	-- display a menu
X * SetButton	-- draw a button with a particular setting
X */
X
X
X#include <assert.h>
X#include <errno.h>
X#include <stdio.h>
X#include <memory.h>
X#include <X11/Xos.h>
X#include <X11/Xlib.h>
X#include <X11/Xutil.h>
X#include <X11/Xatom.h>
X
X#include <olgx/olgx.h>
X
X#include "events.h"
X#include "olwm.h"
X#include "win.h"
X#include "menu.h"
X#include "globals.h"
X
X/* Externals */
extern Graphics_info *olgx_gisnormal;
extern Graphics_info *olgx_gisbutton;
extern GC DrawBackgroundGC;
extern Bool ColorFocusLocked;
extern WinGeneric *ColorFocusWindow;
X
X/* Locals */
static XEvent		lastPress;
static int		lastX, lastY, minX;
static WinGeneric	*prevColorFocusWindow;
X
X
X/*
X * Table of currently active menus.
X * REMIND: perhaps this should be dynamically allocated.
X */
X#define MAX_ACTIVE_MENUS	20	/* We hope, more than enough. */
static MenuInfo	menuTable[MAX_ACTIVE_MENUS];
static int topMenu = 0;			/* Next free menuTable slot. */
X
X/* Menu callback support. */
static int	(*callback)() = NULL;	/* Button action. */
X
X/*
X * These menu modes correspond to the two possible styles of menu user 
X * interface.  MODE_DRAG corresponds to the "Press-Drag-Release" style, and
X * MODE_CLICK corresponds to the "Click-Move-Click" style.
X */
enum MenuTrackMode { MODE_DRAG, MODE_CLICK } menuTrackMode;
X
typedef enum {
X    L_ONBUTTON,		/* on a button */
X    L_ONPIN,		/* on the pin */
X    L_ONMENU,		/* elsewhere on the menu (or on an inactive button) */
X    L_OFFMENU,		/* off the menu entirely */
X} location_t;
X
X#define NOBUTTON	-1	/* no button is active */
X
X/* Calculate fontheight from font info structure. */
X#define FONT_HEIGHT(f)		((f)->ascent + (f)->descent)
X#define BUTT_FONTHEIGHT		FONT_HEIGHT(GRV.ButtonFontInfo)
X#define BUTT_FONTASCENT		(GRV.ButtonFontInfo->ascent)
X#define TITLE_FONTASCENT	(GRV.TitleFontInfo->ascent)
X
X/* Label positioning. */
X#define TEXT_HEIGHT		FONT_HEIGHT(GRV.ButtonFontInfo)
X
X/* Height and curve radius of button. */
X#define BUTT_HEIGHT		Button_Height(olgx_gisbutton)
X#define BUTT_RADIUS		ButtonEndcap_Width(olgx_gisbutton)
X
X/* Space between buttons (these should be adjusted for resolution). */
X#define BUTT_VSPACE	0  /* There used to be space between buttons. */
X#define BUTT_HSPACE	5  /* Space betw button/menumark & pushpin/title/box */
X
X/* Space above and below the titlebar text, and the space below the
X * last button in the menu.
X */
X#define HEAD_VSPACE	4
X
X/* The size of the menu mark is dependant on the font height. */
X#define MENUMARK_SIZE 6
X
X/* Width of menu border. */
X#define MENUBW		0
X
X/* Distance to the left and down of drop shadow (altitude of menu). */
X#define MENUSHADOW_OFF	(10)
X
X/* Events in the menu window that are interesting. */
X#define MENU_EVENT_MASK	(PropertyChangeMask | SubstructureNotifyMask)
X
X#define MENU_ATTR_EVENT_MASK	(ButtonPressMask | ExposureMask)
X
X#define MENU_HORIZ_OFFSET 3
X
X/* Static Functions */
X
static void		(*syncFunc)();
static void		*syncInfo;
static MenuInfo *	allocMenuInfo();
static void		showMenu();
static Bool		menuTrack();
static void		menuHandlePress();
static void		menuHandleMotion();
static Bool		menuHandleRelease();
static MenuInfo *	menuSearch();
static location_t	checkMenuEvent();
static int		menuHide();
static void		unmapChildren();
static void		activateButton();
static void		setMenuPin();
static void		activateSubMenu();
static void		drawButton();
static void		drawRevButton();
static Bool		isClick();
X
X/*
X * ===========================================================================
X */
X
X/*
X * Global routines
X */
X
X/*	
X * InitMenus	-- get the font and related menu initialization
X *	dpy	- display
X */
X/*ARGSUSED*/	/* dpy arg will be used when multiple Displays supported */
void
InitMenus(dpy)
Display		*dpy;
X{
X	/* Most of the stuff that should be in here 
X	 * is actually in InitGraphics.c
X	 */
X}
X
X/*
X * MenuCreate	-- fill out a new menu from an array of buttons
X *	dpy	- display to create menu on
X *	menu	- pointer to menu structure to be filled out
X */
void
MenuCreate(dpy, menu)
Display		*dpy;
Menu		*menu;
X{
X	Button	*buttons;		/* Copy of menu->buttons. */
X	Button	*bp;			/* Temp button pointer. */
X	int	count;			/* Copy of menu->buttonCount. */
X	int	bindex;			/* Current button. */
X	int	maxLabWidth;		/* Width of longest menu label. */
X	int	menWidth, menHeight;	/* Width and height of menu. */
X	int	nextY;			/* Button pos in menu. */
X	int	hasStacked = False;	/* True if there are submenus. */ 
X	XSetWindowAttributes attributes;
X
X	buttons = menu->buttons;
X	count = menu->buttonCount;
X
X	/* Find longest menu entry. */
X	for (maxLabWidth = 0, bindex = 0; bindex < count; bindex++)
X	{
X		maxLabWidth = MAX(maxLabWidth,
X				  XTextWidth(GRV.ButtonFontInfo,
X					     buttons[bindex].label,
X					     strlen(buttons[bindex].label)));
X		if (buttons[bindex].stacked)
X			hasStacked = True;
X	}
X
X	maxLabWidth += 2 * BUTT_RADIUS;
X
X	/* If any of the buttons have submenus, 
X	 * make space for the menu mark. 
X	 */
X	if (hasStacked)
X		maxLabWidth += BUTT_HSPACE + MENUMARK_SIZE;
X
X	/* Calculate title parameters. */
X	if (menu->title != NULL)
X	{
X		menu->titleWidth = XTextWidth(GRV.TitleFontInfo,
X					      menu->title,
X					      strlen(menu->title));
X		menu->titleHeight = HEAD_VSPACE + 
X				    MAX(FONT_HEIGHT(GRV.TitleFontInfo),
X					PushPinOut_Height(olgx_gisnormal)) +
X				    HEAD_VSPACE;
X
X		if (menu->hasPushPin)
X		{
X			menu->titleX = BUTT_HSPACE + 
X					PushPinOut_Width(olgx_gisnormal) +
X					BUTT_HSPACE;
X			menu->titleY = HEAD_VSPACE + TITLE_FONTASCENT;
X
X			menWidth = MAX(BUTT_HSPACE + maxLabWidth + BUTT_HSPACE,
X				       (BUTT_HSPACE + 
X					PushPinOut_Width(olgx_gisnormal) +
X					BUTT_HSPACE + menu->titleWidth +
X					BUTT_HSPACE));
X		}
X		else
X		{
X			menWidth = MAX(BUTT_HSPACE + maxLabWidth + BUTT_HSPACE,
X				       BUTT_HSPACE + menu->titleWidth +
X				       BUTT_HSPACE);
X
X			menu->titleX = (menWidth / 2) - (menu->titleWidth / 2);
X			menu->titleY = HEAD_VSPACE + TITLE_FONTASCENT;
X		}
X	}
X	else
X	{
X		menWidth = BUTT_HSPACE + maxLabWidth + BUTT_HSPACE;
X
X		menu->titleX = 0;
X		menu->titleY = 0;
X		menu->titleWidth = 0;
X		menu->titleHeight = 0;
X	}
X	
X	/* Menu height is the sum of the buttons, the title height if any,
X	 * the space above the first button, and the space below the last 
X	 * button.
X	 */
X	menHeight = menu->titleHeight + HEAD_VSPACE +
X		    ((BUTT_HEIGHT + BUTT_VSPACE) * count) +
X		    HEAD_VSPACE;
X
X	menu->width = menWidth;
X	menu->height = menHeight;
X	
X	/* Place the pushpin.
X	 * Pushpin is centered vertically in case the font height
X	 * is smaller than the pushpin height.
X	 */
X	menu->pushPinX = BUTT_HSPACE;
X	menu->pushPinY = (menu->titleHeight -
X			  PushPinOut_Height(olgx_gisnormal)) / 2;
X
X	/* Menu window. */
X	attributes.event_mask = MENU_ATTR_EVENT_MASK;
X	attributes.save_under = DoesSaveUnders(DefaultScreenOfDisplay(dpy));
X
X	menu->window = XCreateWindow(dpy, DefaultRootWindow(dpy),
X					0, 0,
X					menWidth,
X					menHeight,
X					MENUBW,
X					DefaultDepth(dpy, DefaultScreen(dpy)),
X					InputOutput,
X					DefaultVisual(dpy, DefaultScreen(dpy)),
X					CWEventMask | CWSaveUnder /*| CWBackPixel */,
X					&attributes);
X
X#ifdef SHADOW
X	attributes.background_pixmap = pixmapGray;
X	attributes.save_under = DoesSaveUnders(DefaultScreenOfDisplay(dpy));
X	menu->shadow = XCreateWindow(dpy, DefaultRootWindow(dpy),
X					0, 0,
X					menWidth,
X					menHeight,
X					0,
X					DefaultDepth(dpy, DefaultScreen(dpy)),
X					InputOutput,
X					DefaultVisual(dpy, DefaultScreen(dpy)),
X					CWBackPixmap |  CWSaveUnder,
X					&attributes);
X#endif /* SHADOW */
X
X	XDefineCursor( dpy, menu->window, GRV.MenuPointer );
X
X	/* Precalculate the button postions for faster 
X	 * display/drawing and button-press checking.
X	 * Because pinned menus don't have title windows,
X	 * ( we just draw in the title ),
X	 * these positions are calculated without the titleHeight.
X	 */
X	for(nextY = BUTT_VSPACE, bindex = 0; bindex < count; bindex++)
X	{
X		bp = &buttons[bindex];
X		/* These describe the area of the button that will
X		 * be hightlighted when the button is activated. Or
X		 * one could say that these describe the area that
X		 * will activate the button, since the button press code
X		 * uses these to determine if a button press happens.
X		 */
X		bp->activeX = BUTT_HSPACE;
X		bp->activeY = nextY;
X		bp->activeW = menWidth - (2 * BUTT_HSPACE);
X		bp->activeH = BUTT_HEIGHT;
X
X		/* Move down to next button postion. */
X		nextY += BUTT_HEIGHT + BUTT_VSPACE;
X	}
X}
X
X/*
X * ExecButtonAction
X *
X * Given a menu and a button, find the button's action (by searching down the 
X * menu tree following defaults, if necessary) and execute it.
X */
void
XExecButtonAction( dpy, winInfo, menu, btn, pinned )
Display		*dpy;
WinGeneric	*winInfo;
Menu		*menu;
int		 btn;
Bool		 pinned;
X{
X    /* search down the menu tree for defaults */
X    while ( btn >= 0 && menu->buttons[btn].stacked ) 
X    {
X	menu = menu->buttons[btn].action.submenu;
X	btn = menu->buttonDefault;
X    }
X    if ( btn >= 0 )
X	(*menu->buttons[btn].action.callback)(dpy, winInfo, menu, btn, pinned);
X}
X
X/*
X * ExecDefaultAction(dpy, winInfo, menu, fPinned)
X *
X * Given a menu, execute the associated default action (if any)
X */  
void
XExecDefaultAction(dpy, winInfo, menu, fPinned)
Display		*dpy;
WinGeneric	*winInfo;
Menu		*menu;
Bool		 fPinned;
X{
X	FuncPtr defaultCallback;
X
X	if (menu->buttonDefault < 0)
X		return;
X
X	defaultCallback = menu->buttons[menu->buttonDefault].action.callback;
X	if (defaultCallback != NULL)
X	{
X		(*defaultCallback)(dpy, winInfo, menu, 
X				   menu->buttonDefault, fPinned);
X	}
X}
X
X/*
X * Draw menu contents into menu->window.
X */
void
DrawMenu(dpy, menu)
Display	*dpy;
Menu	*menu;
X{
X	int bindex;
X	int byOff = 0;
X	Window	win = menu->window;
X
X	/* Draw the basic menu background if this menu isn't pinned */
X	if ((menu->originalMenu != NULL) || (!GRV.F3dUsed))
X	{
X		XFillRectangle(dpy, win, DrawBackgroundGC, 0, 0, 
X			menu->width, menu->height);
X	}
X	if (menu->originalMenu == NULL)
X	{
X		olgx_draw_box(olgx_gisnormal, win, 0, 0, 
X			menu->width, menu->height,  OLGX_NORMAL, True);
X	}
X
X	/* Draw the menu title. */
X	if (menu->title != NULL)
X	{
X		if (menu->hasPushPin)
X		{
X			/* If the menu is already displayed, draw the
X			 * pushpin grayed out to indicate that it can't
X			 * be pinned again.
X			 */
X			if (menu->currentlyDisplayed)
X			{
X				/* REMIND we have to manually erase the
X				 * pushpin because OLGX is broken when
X				 * it comes to erasing pushpins.
X				 */
X				XFillRectangle(dpy, win, DrawBackgroundGC,
X					menu->pushPinX, menu->pushPinY,
X					PushPinOut_Width(olgx_gisnormal),
X					PushPinOut_Height(olgx_gisnormal));
X				olgx_draw_pushpin(olgx_gisnormal, win, 
X						  menu->pushPinX,
X						  menu->pushPinY,
X						  OLGX_PUSHPIN_OUT |
X						  OLGX_INACTIVE);
X			}
X			else
X			{
X				XFillRectangle(dpy, win, DrawBackgroundGC,
X					menu->pushPinX, menu->pushPinY,
X					PushPinOut_Width(olgx_gisnormal),
X					PushPinOut_Height(olgx_gisnormal));
X				olgx_draw_pushpin(olgx_gisnormal, win, 
X						  menu->pushPinX,
X						  menu->pushPinY,
X						  OLGX_PUSHPIN_OUT);
X			}
X		}
X
X		olgx_draw_text(olgx_gisnormal, win, menu->title, menu->titleX,
X			menu->titleY, 0, False, OLGX_NORMAL);
X
X	        olgx_draw_text_ledge(olgx_gisnormal, win, 
X		        BUTT_HSPACE, menu->titleHeight-2, 
X		        menu->width-(BUTT_HSPACE*2));
X
X		/* byOff is the gap between the top of the menu and the
X		 * top of the first button.
X		 */
X		byOff = menu->titleHeight;
X	}
X	else  /* No title on this menu. */
X	{
X		byOff = HEAD_VSPACE;
X	}
X
X	/* Draw the buttons. */
X	for (bindex=0; bindex < menu->buttonCount; bindex++)
X		drawButton(dpy, win, &menu->buttons[bindex], byOff, 
X			   (bindex==menu->buttonDefault), 
X			   (menu->originalMenu != NULL));
X}
X
X
void
SetButton( dpy, menu, idx, highlight )
Display	*dpy;
Menu	*menu;
int	idx;
Bool	highlight;
X{
X    int		yoff;
X
X    if ( menu->title == NULL )
X	yoff = HEAD_VSPACE;
X    else
X	yoff = menu->titleHeight;
X
X    if ( highlight )
X	drawRevButton(dpy, menu->window, &menu->buttons[idx], yoff);
X    else
X	drawButton(dpy, menu->window, &menu->buttons[idx], yoff,
X		   (idx == menu->buttonDefault),
X		   (menu->originalMenu != NULL));
X}
X
X
X
X/*
X * MenuShow
X * MenuShowSync
X *
X * These functions are the main entry points into the menu tracking system.  
X * MenuShow() grabs everything, sets up the event interposer, and returns.
X * 
X * REMIND
X * MenuShowSync() sets up an additional callback that is called after the menu 
X * action is completed.  This is necessary for the present implementation of 
X * pinned menus, which need to have work done after the menu goes down, in 
X * addition to the menu button action.  This interface should probably go away 
X * once pinned menus are rearchitected.
X */
void
MenuShowSync(dpy, winInfo, menu, pevent, sfunc, sinfo)
Display		*dpy;
WinGeneric	*winInfo;
Menu		*menu;
XXEvent		*pevent;
void		(*sfunc)();
void		*sinfo;
X{
X	MenuInfo *mInfo;
X
X	/* Initialize the menu info alloc stuff. */
X	/* memset takes int 2nd arg (uses as char) */
X	/* lint will complain about this cast */
X	memset((char *)menuTable, 0, sizeof(MenuInfo) * MAX_ACTIVE_MENUS);
X	topMenu = 0;
X
X	/* Grab the server to prevent anybody from
X	 * sullying the underlying windows when the 
X	 * menu window is mapped.
X	 */
X	XGrabServer(dpy);
X	
X	XGrabPointer(dpy, DefaultRootWindow(dpy),
X		     False,
X		     ButtonReleaseMask | ButtonMotionMask |
X		     ButtonPressMask,
X		     GrabModeAsync, GrabModeAsync,
X		     None,
X		     GRV.MenuPointer,
X		     CurrentTime);
X        
X	if (ColorFocusLocked)
X	    prevColorFocusWindow = ColorFocusWindow;
X	InstallColormap(dpy, WIGetInfo(DefaultRootWindow(dpy)));
X
X	InstallInterposer( menuTrack, (void *)winInfo );
X
X	syncFunc = sfunc;
X	syncInfo = sinfo;
X
X	/* Install the first menu */
X	menuTrackMode = MODE_DRAG;
X	lastPress = *pevent;
X	mInfo = allocMenuInfo( menu );
X	showMenu(dpy, mInfo,
X		 pevent->xbutton.x_root - MENU_HORIZ_OFFSET,
X		 pevent->xbutton.y_root - (BUTT_HEIGHT + BUTT_VSPACE)/2);
X}
X
X
void
MenuShow(dpy, winInfo, menu, pevent)
Display		*dpy;
WinGeneric	*winInfo;
Menu		*menu;
XXEvent		*pevent;
X{
X    MenuShowSync(dpy, winInfo, menu, pevent, NULL, NULL);
X}
X
X
X/*
X * PointInRect	-- check if a point is inside a rectangle
X */
int
PointInRect(x, y, rx, ry, rw, rh)
int x, y, rx, ry, rw, rh;
X{
X	if (((x)>(rx)) && ((x)<(rx)+(rw)) && ((y)>(ry)) && ((y)<(ry)+(rh)))
X		return 1;
X	else
X		return 0;
X}
X
X
X/*
X * ===========================================================================
X */
X
X/*
X * Local routines
X */
X
static MenuInfo *
allocMenuInfo(menu)
Menu	*menu;
X{
X	MenuInfo	*new;
X
X	if ( topMenu > MAX_ACTIVE_MENUS ) 
X	{
X	    fputs( "olvwm: no more active menus!\n", stderr );
X	    exit( 1 );
X	}
X
X	new = &menuTable[topMenu];
X	++topMenu;
X
X	new->menu = menu;
X	new->childActive = False;
X	new->childMenu = (Menu *)NULL;
X	new->pinIn = False;
X	new->litButton = NOBUTTON;
X
X	return new;
X}
X
X
X/* not to be confused with MenuShow() */
static void
showMenu(dpy, mInfo, x, y)
Display		*dpy;
MenuInfo	*mInfo;
int		x, y;
X{
X	int		dpyWidth, dpyHeight;
X	Menu		*menu = mInfo->menu;
X#ifdef SHADOW
X	XWindowChanges	values;
X#endif /* SHADOW */
X
X	/* if menu has a default, line default button with current y;
X	 * otherwise line first button of menu up with current y. 
X	 */
X	if (menu->buttonDefault > 0)
X		y -= menu->buttonDefault * (BUTT_HEIGHT + BUTT_VSPACE);
X	if (menu->title != NULL)
X		y -= menu->titleHeight;
X
X	/* Make sure the menu is going to fit on the screen. */
X	dpyWidth = DisplayWidth(dpy, DefaultScreen(dpy));
X	dpyHeight = DisplayHeight(dpy, DefaultScreen(dpy));
X	if ((x + menu->width) > dpyWidth)
X		x = dpyWidth - menu->width;
X
X	if ((y + menu->height) > dpyHeight)
X		y = dpyHeight - menu->height;
X
X	if (y < 0)
X		y = 0;
X
X	/* Move the menu window to position. */
X	XMoveWindow(dpy, menu->window, x, y);
X#ifdef SHADOW
X	XMoveWindow(dpy, menu->shadow, x + MENUSHADOW_OFF, y + MENUSHADOW_OFF);
X#endif /* SHADOW */
X	menu->x = x;
X	menu->y = y;
X
X	/* Map the menu window and its shadow.
X	 * The OLWM designers want to see the menu appear first and
X	 * then the shadow, NOT the shadow and then the menu.
X	 * So, we have to mess around a bit to do this.
X	 * To make the menu appear first, and then the shadow
X	 * under it, we have to map the menu first and then
X	 * change the stacking order before mapping the shadow.
X	 */
X	XMapRaised(dpy, menu->window);
X
X#ifdef SHADOW
X	values.sibling = menu->window;
X	values.stack_mode = Below;
X	XConfigureWindow(dpy, menu->shadow, CWStackMode|CWSibling, &values);
X	XMapWindow(dpy, menu->shadow);
X#endif /* SHADOW */
X
X	mInfo->ignoreNextExpose = True;
X	DrawMenu(dpy, menu);
X}
X
X
X/*
X * eventX, eventY, eventTime
X *
X * Extract the xroot, yroot, or timestamp fields from an event, assuming it's
X * a MotionNotify, ButtonPress, or ButtonRelease.
X */
X
X#define eventX(e)	((e)->type == MotionNotify ? (e)->xmotion.x_root \
X						   : (e)->xbutton.x_root )
X
X#define eventY(e)	((e)->type == MotionNotify ? (e)->xmotion.y_root \
X						   : (e)->xbutton.y_root )
X
X#define eventTime(e)	((e)->type == MotionNotify ? (e)->xmotion.time \
X						   : (e)->xbutton.time )
X
X
X/*
X * menuTracker
X * Event interposer for menu tracking.
X */
static int
menuTrack( dpy, pevent, win, closure )
Display		*dpy;
XXEvent		*pevent;
WinGeneric	*win;
void		*closure;
X{
X    MenuInfo *mInfo;
X
X    switch ( pevent->type ) 
X    {
X    case ButtonPress:
X	lastPress = *pevent;
X	menuHandlePress( dpy, pevent );
X	break;
X    case MotionNotify:
X	if (!pevent->xmotion.same_screen)
X		break;
X	switch ( menuTrackMode ) 
X	{
X	case MODE_DRAG:
X	    if (!isClick(&lastPress,pevent))
X		menuHandleMotion( dpy, pevent );
X	    break;
X	case MODE_CLICK:
X	    if (!isClick(&lastPress,pevent))
X		menuTrackMode = MODE_DRAG;
X	    break;
X	}
X	break;
X    case ButtonRelease:
X	switch ( menuTrackMode ) 
X	{
X	case MODE_DRAG:
X	    if (isClick(&lastPress,pevent)) 
X	    {
X		menuTrackMode = MODE_CLICK;
X	    } 
X	    else 
X	    {
X		menuHide( dpy, (WinGeneric *)closure );
X	    }
X	    break;
X	case MODE_CLICK:
X	    if (menuHandleRelease(dpy,pevent))
X		menuHide( dpy, (WinGeneric *)closure );
X	    break;
X	}
X	break;
X    case KeyPress:
X	break;
X    case KeyRelease:
X	break;
X    case Expose:
X	mInfo = menuSearch( pevent );
X	if ( mInfo == NULL )
X	    return DISPOSE_DISPATCH;
X	if ( mInfo->ignoreNextExpose )
X	    mInfo->ignoreNextExpose = False;
X	else 
X	{
X	    DrawMenu( dpy, mInfo->menu );
X	    if ( mInfo->litButton != NOBUTTON )
X		SetButton( dpy, mInfo->menu, mInfo->litButton, True );
X	    if ( mInfo->pinIn ) 
X	    {
X		/*
X		 * REMIND
X		 * This is a trifle odd.  We have to set pinIn to False 
X		 * because setMenuPin does nothing if pinIn already equals the 
X		 * value we're setting it to.  The alternative is to code a 
X		 * call to olgx_draw_pushpin here, which is worse.
X		 */
X		mInfo->pinIn = False;
X		setMenuPin( dpy, mInfo, True );
X	    }
X	}
X	break;
X    default:
X	return DISPOSE_DISPATCH;
X    }
X
X    /* for pointer events, save the event location */
X    switch ( pevent->type ) 
X    {
X    case ButtonPress:
X    case ButtonRelease:
X    case MotionNotify:
X	if (pevent->xmotion.same_screen)
X	{
X	    lastX = eventX(pevent);
X	    lastY = eventY(pevent);
X	}
X	break;
X    default:
X	break;
X    }
X    return DISPOSE_USED;
X}
X
X
static void
menuHandlePress( dpy, pevent )
Display	*dpy;
XXEvent	*pevent;
X{
X    int bindex;
X    int status;
X    MenuInfo *mInfo;
X    
X    mInfo = menuSearch( pevent );
X    status = checkMenuEvent(mInfo->menu, pevent, &bindex);
X
X    switch ( status ) 
X    {
X    case L_ONBUTTON:
X	/* need to unmap children of menu choice which was
X	 * previously invoked using click menus
X	 * REMIND doesn't seem to be working
X	 */
X	unmapChildren(dpy, mInfo);
X	activateButton( dpy, mInfo, bindex );
X	minX = eventX(pevent);
X	break;
X    case L_ONPIN:
X	/* need to unmap children of menu choice which was
X	 * previously invoked using click menus
X	 * REMIND doesn't seem to be working
X	 */
X	unmapChildren(dpy, mInfo);
X	setMenuPin( dpy, mInfo, True );
X	break;
X    default:
X	break;
X    }
X}    
X
X
static void
menuHandleMotion( dpy, pevent )
Display *dpy;
XXEvent	*pevent;
X{
X    int status;
X    int bindex;
X    MenuInfo *mInfo;
X    int curX;
X    int deltaX;
X    Bool samebutton;
X    int i;
X
X    mInfo = menuSearch( pevent );
X    status = checkMenuEvent( mInfo->menu, pevent, &bindex );
X
X    /*
X     * If the push pin was in before and this event is not a L_ONPIN event,
X     * put the pin back out because we are no longer in the pin area.
X     */
X    if ((mInfo->pinIn) && (status != L_ONPIN))
X	setMenuPin( dpy, mInfo, False );
X
X    switch ( status ) 
X    {
X    case L_ONBUTTON:
X	samebutton = ( bindex == mInfo->litButton );
X	if (mInfo->childActive && !samebutton)
X	    unmapChildren(dpy, mInfo);
X	curX = pevent->xmotion.x_root;
X	activateButton( dpy, mInfo, bindex );
X
X	if (mInfo->menu->buttons[bindex].stacked) 
X	{
X	    if ( samebutton ) 
X	    {
X		deltaX = curX - minX;
X		minX = MIN(curX,minX);
X	    }
X	    else 
X	    {
X		deltaX = curX - MAX(lastX,mInfo->menu->x);
X		minX = MIN(curX,lastX);
X	    }
X
X	    if ((deltaX > GRV.DragRightDistance) ||
X		(curX > (mInfo->menu->x +
X			 mInfo->menu->buttons[bindex].activeX + 
X			 mInfo->menu->buttons[bindex].activeW -
X			 ButtonEndcap_Width(olgx_gisbutton) -
X			 MenuMark_Width(olgx_gisnormal)))) 
X	    {
X		activateSubMenu(dpy, mInfo, bindex, pevent->xmotion.x_root);
X		minX = curX;
X	    }
X	}
X	break;
X
X    case L_ONPIN:
X	    setMenuPin( dpy, mInfo, True );
X	    if (mInfo->childActive)
X		    unmapChildren(dpy, mInfo);
X	    activateButton( dpy, mInfo, NOBUTTON );
X	    break;
X
X    case L_OFFMENU:
X	    activateButton( dpy, mInfo, NOBUTTON );
X	    break;
X	    
X    case L_ONMENU:
X	    if (!mInfo->childActive)
X		activateButton( dpy, mInfo, NOBUTTON );
X	    break;
X
X    }  /* End switch */
X
X    /*
X     * Pull down all menus to the right of the current mouse position, except 
X     * for the initial menu.
X     */
X    i = topMenu-1;
X    while (i > 0 &&
X	   menuTable[i].menu->x > pevent->xmotion.x_root)
X	--i;
X    if ( i < topMenu-1 ) 
X    {
X	unmapChildren( dpy, &menuTable[i] );
X	topMenu = i+1;
X	if ( status != L_ONBUTTON )
X	    activateButton( dpy, &menuTable[i], NOBUTTON );
X    }
X}    
X
X
static Bool
menuHandleRelease( dpy, pevent )
Display *dpy;
XXEvent	*pevent;
X{
X    int bindex;
X    int status;
X    MenuInfo *mInfo;
X    
X    mInfo = menuSearch( pevent );
X    status = checkMenuEvent(mInfo->menu, pevent, &bindex);
X
X    if (status == L_ONBUTTON &&
X	mInfo->menu->buttons[bindex].stacked &&
X	menuTrackMode == MODE_CLICK &&
X	MouseButton(dpy,pevent) == MB_MENU)
X    {
X	unmapChildren(dpy, mInfo);
X	activateButton( dpy, mInfo, bindex );
X	activateSubMenu( dpy, mInfo, bindex, pevent->xbutton.x_root );
X	return False;
X    }
X    return True;
X}    
X
X
X/*
X * menuSearch
X *
X * Given an event, search the stack of active menus for the menu on which this
X * event occurred.  The event must be a ButtonPress, ButtonRelease,
X * MotionNotify, or Expose event.  If the event didn't occur on any of the
X * menus, for the pointer events, the topmost menu in the stack is returned.  
X * Otherwise, zero is returned.
X */
static MenuInfo *
menuSearch( event )
XXEvent *event;
X{
X    Window w;
X    int i;
X
X    switch ( event->type ) 
X    {
X    case ButtonPress:
X    case ButtonRelease:
X	w = event->xbutton.subwindow;
X	break;
X    case MotionNotify:
X	if (event->xmotion.same_screen)
X	{
X	    w = event->xmotion.subwindow;
X	}
X	break;
X    case Expose:
X	w = event->xexpose.window;
X	break;
X    default:
X	fputs( "olvwm: wrong event type passed to menuSearch\n", stderr );
X	return (MenuInfo *) 0;
X    }
X
X    for (i=topMenu-1; i>=0; --i) 
X    {
X	if ( w == menuTable[i].menu->window )
X	    return &menuTable[i];
X    }
X    return (event->type == Expose) ? (MenuInfo *) 0
X				   : &menuTable[topMenu-1];
X}
X
X
X/*
X * checkMenuEvent
X *
X * Check a button or motion event against a menu.  Sets the index of the 
X * active button (or to NOBUTTON) and returns the pointer location:
X *	L_ONBUTTON, L_ONPIN, L_ONMENU, or L_OFFMENU.
X */
static location_t
checkMenuEvent( menu, pevent, bindex )
Menu	*menu;
XXEvent	*pevent;
int	*bindex;
X{
X	int	i;
X	int	yoff = 0;
X	Window	subwindow;
X	int	ex, ey;
X
X	/* menu->title == NULL for pinned menus, as well as title-less ones */
X	if (menu->title != NULL)
X		yoff = menu->titleHeight;
X	else
X		yoff = HEAD_VSPACE;
X
X	switch ( pevent->type ) 
X	{
X	case MotionNotify:
X	    if (pevent->xmotion.same_screen)
X	    {
X	        subwindow = pevent->xmotion.subwindow;
X	        ex = pevent->xmotion.x_root;
X	        ey = pevent->xmotion.y_root;
X	    }
X	    break;
X	case ButtonPress:
X	case ButtonRelease:
X	    subwindow = pevent->xbutton.subwindow;
X	    ex = pevent->xbutton.x_root;
X	    ey = pevent->xbutton.y_root;
X	    break;
X	}
X
X	/* If the event window is not the menu window. */
X	if (subwindow != menu->window) 
X	{
X		*bindex = NOBUTTON;
X		return L_OFFMENU;
X	}
X	
X	/*
X	 * Check the event coordinates against each of the buttons.
X	 * Since the button event is reported relative to root window
X	 * it must be adjusted for the check.
X	 */
X	for (i=0; i < menu->buttonCount; i++)
X	{
X		if (PointInRect(ex - menu->x, 
X				ey - menu->y,
X				menu->buttons[i].activeX,
X				menu->buttons[i].activeY + yoff,
X				menu->buttons[i].activeW,
X				menu->buttons[i].activeH))
X		{
X			/* Event is in a button.
X			 * Is it a button stack and if so,
X			 * is it in the right half of the button?
X			 */
X			*bindex = i;
X			if (menu->buttons[i].state == Disabled)
X			    return L_ONMENU;
X			else
X			    return L_ONBUTTON;
X#ifdef notdef
X			if ((menu->buttons[i].stacked) &&
X			    ((ex - menu->x) > (menu->width/2)))
X				return S_ACTIVATE;
X			else
X				return S_ONBUTTON;
X#endif /* notdef */
X		}
X	}
X
X	/* Check the pushpin area. */
X	*bindex = -1;
X	if (menu->hasPushPin &&
X	    PointInRect(ex - menu->x, 
X			ey - menu->y,
X			menu->pushPinX, menu->pushPinY,
X			PushPinOut_Width(olgx_gisnormal), 
X			PushPinOut_Height(olgx_gisnormal)) && 
X	    !menu->currentlyDisplayed)		/* true if menu is pinned */
X		return L_ONPIN;
X	else
X		return L_ONMENU;
X}
X
X
X/*
X * menuHide
X *
X * Remove any active menus from the screen, and call the menu callback
X * function as necessary.
X */
static int
menuHide( dpy, winInfo )
Display		*dpy;
WinGeneric	*winInfo;
X{
X    int i;
X    MenuInfo *mInfo = &menuTable[topMenu-1];
X    int btn;
X    Menu *menu;
X    int (*callback)() = (int (*)()) 0;
X
X    if (ColorFocusLocked)
X	InstallColormap(dpy, prevColorFocusWindow);
X
X    XUngrabServer(dpy);
X    XUngrabPointer(dpy, CurrentTime);
X    XFlush(dpy);
X    UninstallInterposer();
X
X    /* Unmap any active menus. */
X    for ( i=topMenu-1; i>=0; --i )
X    {
X#ifdef SHADOW
X	XUnmapWindow(dpy, menuTable[i].menu->shadow);
X#endif /* SHADOW */
X	XUnmapWindow(dpy, menuTable[i].menu->window);
X    }
X
X    if ( mInfo->pinIn ) 
X    {
X	(*mInfo->menu->pinAction)(dpy, winInfo, mInfo->menu, NOBUTTON, False);
X    }
X    else 
X    {
X	if ( mInfo->litButton != NOBUTTON )
X	    ExecButtonAction(dpy, winInfo, mInfo->menu,
X			     mInfo->litButton, False );
X    }
X	
X    if ( syncFunc != NULL )
X	(*syncFunc)( syncInfo );
X}
X
X
static void
unmapChildren(dpy, mInfo)
Display		*dpy;
MenuInfo	*mInfo;
X{
X	int		i;
X
X	i = topMenu-1;
X	while ( i >= 0 && menuTable[i].menu != mInfo->menu ) 
X	{
X#ifdef SHADOW
X	    XUnmapWindow(dpy, menuTable[i].menu->shadow);
X#endif /* SHADOW */
X	    XUnmapWindow(dpy, menuTable[i].menu->window);
X	    --i;
X	}
X	topMenu = i+1;
X#ifdef DEBUG
X	if ( i < 0 )
X	    fputs( "olvwm: warning, internal error in unmapChildren!\n",
X		  stderr );
X#endif /* DEBUG */
X
X	mInfo->childActive = False;
X}
X
X
static void
activateButton( dpy, mInfo, idx )
Display		*dpy;
MenuInfo	*mInfo;
int		idx;
X{
X    if ( mInfo->litButton == idx )
X	return;
X
X    /* Unhighlight any highlit button. */
X    if ( mInfo->litButton != NOBUTTON ) 
X	SetButton( dpy, mInfo->menu, mInfo->litButton, False );
X
X    /* Highlight the new button */
X    if ( idx != NOBUTTON )
X	SetButton( dpy, mInfo->menu, idx, True );
X    
X    mInfo->litButton = idx;
X}
X
X
static void
setMenuPin( dpy, mInfo, state )
Display		*dpy;
MenuInfo	*mInfo;
Bool		state;
X{
X    if ( mInfo->pinIn != state ) 
X    {
X	mInfo->pinIn = state;
X	XFillRectangle(dpy, mInfo->menu->window, DrawBackgroundGC,
X		mInfo->menu->pushPinX, mInfo->menu->pushPinY,
X		PushPinOut_Width(olgx_gisnormal),
X		PushPinOut_Height(olgx_gisnormal));
X	olgx_draw_pushpin(olgx_gisnormal, mInfo->menu->window,
X	    mInfo->menu->pushPinX, mInfo->menu->pushPinY,
X	    state ? OLGX_PUSHPIN_IN : OLGX_PUSHPIN_OUT);
X    }
X}
X
X
X/*
X * activateSubMenu
X *
X * Given a MenuInfo struct and a button, activate that button's submenu.
X * It's assumed that the button actually has a submenu.  Note that only the
X * x-location is passed in, while the y-location is calculated.  The reason is 
X * that the x-location is determined by the mouse event, while the y-location 
X * is always based the location of the parent menu.  If a submenu is already 
X * active, do nothing.  This is primarily to prevent the same submenu from 
X * being activated again.  This occurs if a submenu is much narrower than its 
X * parent, and you pull off the right of the submenu back into the parent.
X */
static void
activateSubMenu( dpy, mInfo, bindex, x )
Display		*dpy;
MenuInfo	*mInfo;
int		bindex;
int		x;
X{
X    MenuInfo *newmenu;
X
X    if ( !mInfo->childActive ) 
X    {
X	mInfo->childActive = True;
X	mInfo->childMenu = 
X	    (Menu *)(mInfo->menu->buttons[bindex].
X		 action.submenu);
X	newmenu = allocMenuInfo(mInfo->childMenu);
X	showMenu(dpy, newmenu, x-MENU_HORIZ_OFFSET,
X		 mInfo->menu->y +
X		 mInfo->menu->buttons[bindex].activeY +
X		 ((mInfo->menu->title != NULL) ? 
X			mInfo->menu->titleHeight : 0));
X    }
X}
X
X
X/* Draw a normal button. 
X * if fDefault is true, a default ring will be drawn.
X * fIsPinned indicates whether this button is on a pinned menu.
X */
X/*ARGSUSED*/	/* dpy arg is not yet used */
static void
drawButton(dpy, win, button, yOffset, fDefault, fIsPinned)
Display	*dpy;
Window	win;
Button	*button;
int	yOffset;
Bool 	fDefault;
Bool 	fIsPinned;
X{
X	int state;
X
X	state = OLGX_NORMAL | 
X		OLGX_ERASE |
X		((button->state == Enabled)? 0 : OLGX_INACTIVE) |
X		((button->stacked) ? OLGX_HORIZ_MENU_MARK : 0) |
X		(fDefault? OLGX_DEFAULT : 0);
X	if (!(fIsPinned && GRV.FShowPinnedMenuButtons || GRV.FShowMenuButtons))
X	{
X		/* setting this flag turns OFF painting of the menu buttons */
X		state |= OLGX_MENU_ITEM;
X	}
X
X	olgx_draw_button(olgx_gisbutton, win, button->activeX, 
X			button->activeY+yOffset, button->activeW, 0,
X			button->label, state);
X			
X}
X
X
X/* Draw a reverse video button. */
X/*ARGSUSED*/	/* dpy arg is not yet used */
static void
drawRevButton(dpy, win, button, yOffset)
Display	*dpy;
Window	win;
Button	*button;
int	yOffset;
X{
X	int state;
X
X	/* if the button is disabled, do nothing */
X	if (button->state == Disabled)
X		return;
X
X	state = OLGX_INVOKED | OLGX_ERASE | 
X		((button->stacked) ? OLGX_HORIZ_MENU_MARK : 0);
X	olgx_draw_button(olgx_gisbutton, win, 
X			 button->activeX, button->activeY + yOffset,
X			 button->activeW, 0, 
X			 button->label, state | OLGX_MENU_ITEM);
X}
X
X
X/*
X * isClick
X * 
X * Takes two button events and returns a boolean indicating whether they are 
X * close enough (spacially and temporally) to be considered a click.
X */
X
X#define THRESH_DIST   5
X
static Bool
isClick( e1, e2 )
XXEvent *e1;
XXEvent *e2;
X{
X    return (
X	ABS(eventX(e1)-eventX(e2)) <= GRV.ClickMoveThreshold &&
X	ABS(eventY(e1)-eventY(e2)) <= GRV.ClickMoveThreshold &&
X	eventTime(e2)-eventTime(e1) <= GRV.DoubleClickTime
X    );
X}
END_OF_FILE
if test 31690 -ne `wc -c <'menu.c'`; then
    echo shar: \"'menu.c'\" unpacked with wrong size!
fi
# end of 'menu.c'
fi
echo shar: End of archive 14 \(of 16\).
cp /dev/null ark14isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 16 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0

--
Dan Heller
O'Reilly && Associates       Z-Code Software    Comp-sources-x:
Senior Writer                President          comp-sources.x at uunet.uu.net
argv at ora.com                 argv at zipcode.com



More information about the Comp.sources.x mailing list