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