sdo@soliado.East.Sun.COM (Scott Oaks - Sun Consulting NYC) (04/29/91)
Submitted-by: sdo@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@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@uunet.uu.net argv@ora.com argv@zipcode.com