jkh@ardent.UUCP (Jordan K. Hubbard) (06/16/88)
#! /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 17)."
# Contents: awm/menus/track_menu.c
# Wrapped by jkh@ardent on Thu Jun 16 00:44:24 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'awm/menus/track_menu.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'awm/menus/track_menu.c'\"
else
echo shar: Extracting \"'awm/menus/track_menu.c'\" \(26723 characters\)
sed "s/^X//" >'awm/menus/track_menu.c' <<'END_OF_FILE'
X#ident "%W% %G%"
X
X#ifndef lint
X static char sccs_id[] = "@(#)track_menu.c 2.1 12/16/87 Siemens Corporate Research and Support, Inc.";
X#endif
X
X
X#include "X11/copyright.h"
X
X/*
X RTL Menu Package Version 1.0
X by Joe Camaratta and Mike Berman, Siemens RTL, Princeton NJ, 1987
X
X track_menu.c: bring up menus and track the mouse
X */
X
X/*
X *
X * Copyright 1987, 1988 by Ardent Computer Corporation, Sunnyvale, Ca.
X *
X * Copyright 1987 by Jordan Hubbard.
X *
X *
X * All Rights Reserved
X *
X * Permission to use, copy, modify, and distribute this software and its
X * documentation for any purpose and without fee is hereby granted,
X * provided that the above copyright notice appear in all copies and that
X * both that copyright notice and this permission notice appear in
X * supporting documentation, and that the name of Ardent Computer
X * Corporation or Jordan Hubbard not be used in advertising or publicity
X * pertaining to distribution of the software without specific, written
X * prior permission.
X *
X */
X
X
X/*
X
X Copyright 1987 by
X Siemens Corporate Research and Support, Inc., Princeton, New Jersey
X
X Permission to use, copy, modify, and distribute this software
X and its documentation for any purpose and without fee is
X hereby granted, provided that the above copyright notice
X appear in all copies and that both that copyright notice and
X this permission notice appear in supporting documentation, and
X that the name of Siemens not be used in advertising or
X publicity pertaining to distribution of the software without
X specific, written prior permission. Siemens makes no
X representations about the suitability of this software for any
X purpose. It is provided "as is" without express or implied
X warranty.
X
X */
X
X/*
X * The menu package will break if you don't define this, but
X * it's there in case you want to see just how and where the
X * "eventstack" stuff is used and, if necessary, replace it.
X */
X#define SAVE_EVENTS
X
X
X#include <stdio.h>
X#include "X11/Xlib.h"
X#include "X11/cursorfont.h"
X#include "arrow_icon.h"
X#include "null_icon.h"
X#include "menu.h"
X#include "menu.def.h"
X#include "menu.ext.h"
X#include "dbug.h"
X#ifdef SAVE_EVENTS
X#include "eventstack.h"
X#endif
X
X#define MIN(x,y) (((x) <= (y))? x:y)
X#define MAX(x,y) (((x) >= (y))? x:y)
X
X#define CLICK_TIME 290 /* in milliseconds */
X
X#define CursorLockMask (ButtonReleaseMask | ExposureMask)
X
X/* Event macros */
X
X#define EventGetXCoord(rep) ((rep).xmotion.x)
X#define EventGetYCoord(rep) ((rep).xmotion.y)
X#define EventType(rep) ((rep).type)
X#define EventXWindow(rep) ((rep).xcrossing.window)
X#define EventXTime(rep) ((rep).xcrossing.time)
X#define EventXMode(rep) ((rep).xcrossing.mode)
X#define EventXRootX(rep) ((rep).xcrossing.x_root)
X#define EventXRootY(rep) ((rep).xcrossing.y_root)
X#define EventXDetail(rep) ((rep).xcrossing.detail)
X#define EventMWindow(rep) ((rep).xmotion.window)
X#define EventMTime(rep) ((rep).xmotion.time)
X#define EventButton(rep) ((rep).xbutton.button)
X#define EventBWindow(rep) ((rep).xbutton.window)
X#define EventBTime(rep) ((rep).xbutton.time)
X#define EventEX(rep) ((rep).xexpose.x)
X#define EventEY(rep) ((rep).xexpose.y)
X#define EventEWidth(rep) ((rep).xexpose.width)
X#define EventEHeight(rep) ((rep).xexpose.height)
X#define PointerEvent(rep) \
X ((EventType(rep) == ButtonPress) || \
X (EventType(rep) == ButtonRelease) || \
X (EventType(rep) == MotionNotify) || \
X (EventType(rep) == EnterNotify) || \
X (EventType(rep) == LeaveNotify) || \
X (EventType(rep) == FocusIn) || \
X (EventType(rep) == FocusOut))
X#define KeyEvent(rep) \
X ((EventType(rep) == KeyPress) || (EventType(rep) == KeyRelease))
X/* Possible states for the state machine */
typedef enum
X{
X Initial, /* Inside a submenu, but not any item */
X CheckTrigger, /* Inside an item that has submenu, checking for pullright */
X Leaf, /* Inside an item with no submenu */
X Exit, /* Preparing to exit */
X LevelControl /* Not in any submenu, waiting to enter something */
X } State;
X
State InitialState(), CheckTriggerState(), LeafState(), LevelControlState(),
X GetItemState();
bool EventNotSignificant(), PushSubmenu();
X
void OutputEvent(), GetNextSignificantEvent(), PopSubmenu(),
X Highlight(), Unhighlight(), DisplayInitialMenus(), LockCursor(),
X TossExtraMoves(), UnlockCursor();
X
void ProcessExposeEvents();
X
void SaveTest();
X
MenuItem *MenuGetItem();
Menu *MenuGetMenu();
X
X/* global state variables */
X
static MenuItem *current_item;
static Menu *current_menu;
static Window root_window;
extern Display *dpy;
extern int scr;
static int level; /* submenu level */
static Time button_time; /* time button press invoked */
static Cursor wait_cursor = None; /* empty cursor for lock state */
static bool click_allowed;
static bool lock_event_mask, unlock_event_mask;
X
extern int Autoselect;
extern int MDelta;
X
X#ifdef SAVE_EVENTS
static struct Ev_q *ev_save = 0;
X#endif
X
X
MenuItem *TrackMenu(root_menu, root_x, root_y,
X init_button, root, buttime)
Menu *root_menu; /* Pointer to root menu requested to pop up */
int root_x, root_y; /* Position to start menu */
int init_button; /* The # of button used to pop up menu */
Window root; /* Window label for parent of menu */
Time buttime; /* timestamp for button (or 0, if CLICK == 0) */
X{
X State CurrentState = LevelControl;
X XEvent Event_Reply;
X int open_x;
X bool selected = FALSE;
X MenuItem *selected_item;
X
X Entry("TrackMenu")
X
X /* Initialize globals */
X
X button_time = buttime;
X root_window = root;
X level = 0;
X current_menu = root_menu;
X click_allowed = (TestOptionFlag(current_menu, clickokay))? TRUE : FALSE;
X unlock_event_mask = (TestOptionFlag(current_menu, savebits))?
X MenuEventMask : (MenuEventMask | ExposureMask);
X lock_event_mask = (TestOptionFlag(current_menu, savebits))?
X CursorLockMask : (CursorLockMask | ExposureMask);
X
X /* If not already done, set up the null cursor for lock state */
X if (wait_cursor == None)
X {
X Pixmap wc_pixmap;
X XColor fg, bg;
X
X wc_pixmap = XCreateBitmapFromData (dpy, root_window,
X null_icon_bits,
X null_icon_width, null_icon_height);
X wait_cursor = XCreatePixmapCursor (dpy, wc_pixmap, wc_pixmap,
X &fg, &bg, 1, 1);
X }
X
X
X /* Block all other action by grabbing the server */
X /* XGrabServer (dpy); */
X /* Don't think we need to grab the server... so for now, we won't */
X
X#ifdef SAVE_EVENTS
X /* Get the present state, so it can be restored later */
X /* Any events on the queue when we start get saved now, restored later */
X SaveEvents (dpy, &ev_save, ~(unsigned long) ButtonReleaseMask);
X#endif
X
X LockCursor(root_window);
X if (!(current_item =
X Display_Menu(current_menu, NULLMENU, root_x, root_y)))
X {
X CurrentState = Exit;
X }
X /*
X * First item is a label and autoselect is on, so we want
X * to push on to the first "real" item.
X */
X if (ItemIsDeaf(current_item) && Autoselect)
X current_item = current_item->nextItem;
X LockCursor(ItemWindow(current_item));
X open_x = root_x;
X
X /* Push to appropriate previous item, if any */
X while (MenuHasInitialItem(current_menu) && (CurrentState != Exit))
X {
X current_item = GetInitialItem(current_menu);
X ClearInitialItem(current_menu);
X
X /* if the initial item can't be selected, take first in list */
X if (ItemIsNull(current_item) || ItemIsDisabled(current_item))
X {
X current_item = MenuItems(current_menu);
X break;
X }
X else if (ItemIsLeaf(current_item)) /* then we're done */
X break;
X else
X {
X open_x += ItemGetArrowPosition(current_item);
X if (!ItemIsDeaf(current_item))
X Highlight(current_item);
X TossExtraMoves(ItemWindow(current_item));
X (void)PushSubmenu(open_x);
X }
X }
X ProcessExposeEvents();
X if (CurrentState != Exit)
X CurrentState = (ItemIsLeaf(current_item)) ? Leaf : CheckTrigger;
X if (!ItemIsDeaf(current_item))
X Highlight(current_item);
X XSync (dpy, 0); /* get release click, if it's in queue */
X#ifdef SAVE_EVENTS
X DisposeEvents(dpy, (PointerMotionMask | EnterWindowMask |
X LeaveWindowMask | ExposureMask));
X#endif
X LockCursor(ItemWindow(current_item));
X PlacePointer(current_menu,current_item);
X UnlockCursor();
X
X /* State Machine */
X
X while (CurrentState != Exit)
X {
X GetNextSignificantEvent(&Event_Reply, init_button);
X switch (CurrentState)
X {
X case LevelControl:
X CurrentState = LevelControlState(Event_Reply);
X break;
X case Initial:
X CurrentState = InitialState(Event_Reply);
X break;
X case CheckTrigger:
X CurrentState = CheckTriggerState(Event_Reply);
X break;
X case Leaf:
X CurrentState = LeafState(Event_Reply, &selected);
X break;
X default:
X Retch("(RTLmenu) YOW! Unknown State! (%d)\n",
X CurrentState);
X CurrentState = Exit;
X break;
X }
X }
X /* Clean up and exit */
X
X selected_item = (selected)? current_item : NULLITEM;
X while (level)
X {
X if (selected)
X SetInitialItem(current_menu, current_item);
X PopSubmenu();
X }
X if (selected)
X {
X SetInitialItem(current_menu, current_item);
X }
X
X Undisplay_Menu(current_menu);
X UnlockCursor();
X XUngrabPointer(dpy, CurrentTime);
X
X /* Throw out any left over events from menu world */
X /* if (TestOptionFlag(current_menu, savebits)) {
X XSync(dpy,1);
X XUngrabServer(dpy, CurrentTime); add this if grab added!
X }
X else
X XSync(dpy,0);*/
X
X /* Push back any events that were lying around when menus started */
X
X XFlush(dpy);
X#ifdef SAVE_EVENTS
X DisposeEvents(dpy, (PointerMotionMask | EnterWindowMask |
X LeaveWindowMask | ExposureMask));
X RestoreEvents(dpy, &ev_save);
X#endif
X Leave(selected_item)
X}
X
X/* Used for debugging */
X
void OutputEvent(Event_Reply)
XXEvent Event_Reply;
X{
X Entry("OutputEvent")
X
X switch (EventType(Event_Reply))
X {
X case ButtonPress:
X case ButtonRelease:
X DBUG_5("RTLmenu","Button Press/Release, button %d on window %d at time %d\n",
X EventButton(Event_Reply), EventBWindow(Event_Reply),
X EventBTime(Event_Reply));
X break;
X case MotionNotify:
X DBUG_5("RTLmenu","Motion Notify on window %d at time %d, x=%d\n",
X EventMWindow(Event_Reply), EventMTime(Event_Reply),
X EventGetXCoord(Event_Reply));
X break;
X case EnterNotify:
X DBUG_4("RTLmenu","Enter Notify on window %d at time %d\n",
X EventXWindow(Event_Reply), EventXTime(Event_Reply));
X break;
X case LeaveNotify:
X DBUG_4("RTLmenu","Leave Notify on window %d at time %d\n",
X EventXWindow(Event_Reply), EventXTime(Event_Reply));
X break;
X default:
X DBUG_3("RTLmenu","Unexpected event type %d\n", EventType(Event_Reply));
X break;
X }
X Leave_void
X}
X
static bool locked = FALSE;
X
X/* Lock the cursor: make it disappear, and ignore events it generates. */
X/* Optionally, confine it to a single window. */
X/* (Using "None" for confine_window doesn't confine it. ) */
void LockCursor(confine_window)
Window confine_window;
X{
X int result;
X
X Entry("LockCursor")
X
X locked = TRUE;
X result = XGrabPointer(dpy,
X RootWindow(dpy, MenuScreen(current_menu)),
X True, lock_event_mask, GrabModeSync,
X GrabModeAsync, confine_window,
X wait_cursor, CurrentTime);
X DBUG_3("RTLmenu","Lock Cursor grab = %d\n",result);
X Leave_void
X}
X
X/* Unlock (and unconfine) the cursor. If cursor lock is not set, */
X/* this does nothing. */
X
void UnlockCursor()
X{
X int result;
X
X Entry("UnlockCursor")
X
X if (locked)
X {
X locked = FALSE;
X result = XGrabPointer(dpy,
X RootWindow(dpy, MenuScreen(current_menu)),
X True, unlock_event_mask,
X GrabModeAsync, GrabModeAsync, None,
X MenuCursor(current_menu), CurrentTime);
X DBUG_3("RTLmenu","Unlock Cursor grab = %d\n",result);
X }
X Leave_void
X}
X
X/* Keep getting the X events, until finding one that may be interesting */
X/* to the operation of the state machine. */
X
void GetNextSignificantEvent(Event_Reply,init_button)
XXEvent *Event_Reply;
int init_button; /* the button that initiated the menu */
X{
X XEvent Next_Event_Reply;
X bool InsignificantEvent = True;
X
X Entry("GetNextSignificantEvent")
X
X /* Loop as long as any of a number of "insignificant" events */
X /* are found; when the event no longer matches one of the tests, */
X /* it is assumed to be "significant" and returned.*/
X do
X {
X XNextEvent(dpy, Event_Reply);
X DBUG_EXECUTE("RTLmenu", OutputEvent(*Event_Reply));
X
X /* If this event is an "enter", check whether there is a */
X /* "leave" for the same window already in the queue, */
X /* immediately following it; if so, throw them both out */
X /* and get the next event */
X /* NOTE: might try to look further ahead, but this is */
X /* tricky because other events might intervene. */
X
X if ((EventType(*Event_Reply) == EnterNotify) &&
X (EventXMode(*Event_Reply) == NotifyNormal) &&
X (QLength(dpy) > 0) &&
X (MenuGetMenu(current_menu, EventXWindow(*Event_Reply))
X != current_menu))
X {
X XPeekEvent(dpy, &Next_Event_Reply);
X if ((EventType(Next_Event_Reply) == LeaveNotify) &&
X (EventXMode(Next_Event_Reply) == NotifyNormal) &&
X (EventXWindow(Next_Event_Reply) == EventXWindow(*Event_Reply)))
X {
X DBUG_2("RTLmenu","TOSS: Enter/leave pair.\n");
X XNextEvent(dpy, Event_Reply);
X XNextEvent(dpy, Event_Reply);
X }
X }
X#ifdef SAVE_EVENTS
X if (EventNotSignificant(*Event_Reply, init_button))
X {
X if (!(PointerEvent(*Event_Reply) || KeyEvent(*Event_Reply)
X || EventType(*Event_Reply) == Expose))
X {
X /* might be significant elsewhere -- save it for later */
X AddEventToStore(&ev_save, *Event_Reply);
X }
X }
X else
X#else
X if (!EventNotSignificant(*Event_Reply, init_button))
X#endif
X InsignificantEvent = FALSE;
X }
X while (InsignificantEvent);
X
X DBUG_2("RTLmenu","--->");
X Leave_void
X}
X
X/* Check whether the event matches one of the events considered */
X/* "not significant".*/
bool EventNotSignificant(Event_Reply, init_button)
XXEvent Event_Reply;
int init_button;
X{
X Entry("EventNotSignificant")
X
X /* Insignificant if not in following list */
X Leave(!((EventType(Event_Reply) == ButtonRelease) ||
X (EventType(Event_Reply) == ButtonPress) ||
X (EventType(Event_Reply) == MotionNotify) ||
X (EventType(Event_Reply) == EnterNotify) ||
X (EventType(Event_Reply) == Expose) ||
X (EventType(Event_Reply) == LeaveNotify))
X ||
X /* Insignificant if leave or enter is not "Normal" */
X (((EventType(Event_Reply) == LeaveNotify) ||
X (EventType(Event_Reply) == EnterNotify)) &&
X (EventXMode(Event_Reply) != NotifyNormal))
X ||
X /* Insignificant if hit button other than initial one */
X ((EventType(Event_Reply) == ButtonRelease) &&
X (EventButton(Event_Reply) != init_button))
X ||
X /* Insignificant if tail end of a click -- and clicks allowed */
X (click_allowed &&
X (EventType(Event_Reply) == ButtonRelease) &&
X (EventBTime(Event_Reply) - button_time < CLICK_TIME))
X )
X}
X
State LevelControlState(rep)
XXEvent rep;
X{
X State next_state;
X Menu *entered_menu;
X MenuItem *entered_item;
X
X Entry("LevelControlState")
X switch (EventType(rep))
X {
X case MotionNotify:
X case LeaveNotify:
X next_state = LevelControl; /* loop back to this state */
X break;
X case EnterNotify:
X /* Decide whether we've entered a menu window or item window */
X entered_menu = MenuGetMenu(current_menu, EventXWindow(rep));
X entered_item = MenuGetItem(current_menu,EventXWindow(rep));
X
X if ((MenuIsNull(entered_menu)) && (ItemIsNull(entered_item)))
X /* Must be some other window; carry on */
X next_state = LevelControl;
X else if (!ItemIsNull(entered_item) &&
X MenuIsDisplayed(ItemMenu(entered_item)))
X {
X /* we entered an item, but not a window. This should only happen */
X /* when we stayed in the parent of the current submenu. So, */
X /* Pop that submenu and get to the item. */
X if (level)
X {
X LockCursor(ItemWindow(entered_item));
X PopSubmenu();
X ProcessExposeEvents();
X UnlockCursor();
X current_item = entered_item;
X Highlight(current_item);
X next_state = GetItemState(rep);
X }
X else /* I must be very confused... */
X {
X Retch("(RTLmenu) Tried to pop the root menu...\n");
X next_state = Exit;
X }
X }
X
X else if (!MenuIsNull(entered_menu)&&
X MenuIsDisplayed(entered_menu))
X {
X /* entered a menu that is displayed */
X while ((current_menu != entered_menu) && level)
X /* drop down the menu that was entered */
X PopSubmenu();
X ProcessExposeEvents();
X UnlockCursor();
X if (current_menu == entered_menu)
X next_state = Initial;
X else
X {
X next_state = Exit;
X Retch("(RTLmenu) Couldn't find the menu I entered!!\n");
X }
X }
X else
X next_state = LevelControl;
X break;
X case ButtonRelease:
X next_state = Exit;
X break;
X default:
X Retch("RTLmenu","YOW! Unexpected event! (%d)\n", rep.type);
X next_state = Exit;
X break;
X }
X Leave(next_state)
X}
X
X/* Figure out the status of the item we've just entered */
State GetItemState(rep)
XXEvent rep;
X{
X int open_x;
X State next_state;
X
X Entry("GetItemState")
X if (ItemIsNull(current_item))
X {
X Retch("(RTLmenu) null current item!");
X next_state = Exit;
X }
X else if (MenuIsNull(current_menu))
X {
X Retch("(RTLmenu) null current menu!");
X next_state = Exit;
X }
X else if (ItemIsLeaf(current_item))
X {
X if (MenuHasInitialItem(current_menu))
X ClearInitialItem(current_menu);
X next_state = Leaf;
X }
X else if (EventGetXCoord(rep) >= (int)(ItemGetArrowPosition(current_item) - 4))
X {
X /* entered item in "auto pop-up zone", i.e., over pull-right arrow. */
X LockCursor(ItemWindow(current_item));
X TossExtraMoves(ItemWindow(current_item));
X if (PushSubmenu(EventXRootX(rep)))
X {
X LockCursor(ItemWindow(current_item));
X PlacePointer(current_menu, current_item);
X next_state = Initial;
X ProcessExposeEvents();
X }
X else
X next_state = CheckTrigger;
X UnlockCursor();
X }
X else if (MenuHasInitialItem(current_menu))
X {
X /* Entered menu has initial item -- move to it */
X DBUG_2("RTLmenu","Pushing for initial item.");
X current_item = GetInitialItem(current_menu);
X open_x = ItemGetArrowPosition(current_item) +
X EventXRootX(rep);
X ClearInitialItem(current_menu);
X LockCursor(ItemWindow(current_item));
X if (PushSubmenu(open_x))
X {
X ProcessExposeEvents();
X LockCursor(ItemWindow(current_item));
X PlacePointer(current_menu, current_item);
X next_state = Initial;
X }
X UnlockCursor();
X }
X else /* parent pull */
X next_state = CheckTrigger;
X Leave(next_state)
X}
X
State InitialState( rep)
XXEvent rep;
X{
X State next_state;
X
X Entry("Initial")
X switch (EventType(rep))
X {
X case EnterNotify:
X if (MenuIsNull(current_menu))
X {
X Retch("(RTLmenu) null current menu!?!?");
X next_state = Exit;
X }
X else if (EventXDetail(rep) == NotifyInferior)
X next_state = Initial;
X else
X {
X current_item = MenuGetItem(current_menu, EventXWindow(rep));
X if (ItemIsNull(current_item))
X {
X /* Retch("(RTLmenu) Window entered not an item!\n"); */
X next_state = Exit;
X }
X else
X {
X Highlight(current_item);
X next_state = GetItemState(rep);
X }
X }
X break;
X case LeaveNotify:
X /* Decide whether we're actually leaving */
X /* this menu for another submenu or the root, */
X /* or going into an item. */
X next_state = (EventXDetail(rep) == NotifyInferior)?
X Initial : LevelControl;
X break;
X case ButtonRelease:
X next_state = Exit;
X break;
X case MotionNotify:
X next_state = Initial;
X break;
X default:
X Retch("(RTLmenu) YOW! Unexpected event! (%d)\n", rep.type);
X next_state = Exit;
X break;
X }
X Leave(next_state)
X}
X
X#define NotSet -1
X/* Look to see if pull-right is requested */
State CheckTriggerState(rep)
XXEvent rep;
X{
X State next_state = CheckTrigger;
X static int Trigger = NotSet;
X static int OldX, NewX, childX, button;
X
X Entry("CheckTrigger")
X if (MenuIsNull(current_menu) || ItemIsNull(current_item))
X {
X Retch("(RTLmenu) Null menu or item...");
X next_state = Exit;
X goto exit;
X }
X if (Trigger == NotSet) /* set it */
X {
X Trigger = MIN(EventGetXCoord(rep) + MenuDelta(current_menu),
X ItemGetArrowPosition(current_item));
X NewX = NotSet;
X }
X switch (EventType(rep))
X {
X case LeaveNotify:
X next_state = Initial;
X Unhighlight(MenuGetItem(current_menu, EventXWindow(rep)));
X Trigger = NotSet;
X break;
X case ButtonRelease:
X next_state = Exit;
X Trigger = NotSet;
X break;
X
X case ButtonPress:
X button = rep.xbutton.button;
X while (TRUE) {
X XNextEvent(dpy, &rep);
X if (rep.type == ButtonRelease &&
X rep.xbutton.button == button)
X break;
X }
X next_state = CheckTrigger;
X childX = TestOptionFlag(current_menu, fixedchild) ?
X (MenuX(current_menu) + ItemGetArrowPosition(current_item)) :
X EventXRootX(rep);
X Trigger = NotSet;
X if (PushSubmenu(childX))
X {
X next_state = LevelControl;
X ProcessExposeEvents();
X LockCursor(ItemWindow(current_item));
X PlacePointer(current_menu, current_item);
X }
X UnlockCursor();
X break;
X
X case MotionNotify:
X next_state = CheckTrigger;
X OldX = NewX;
X NewX = EventGetXCoord(rep);
X if (NewX >= Trigger)
X {
X LockCursor(ItemWindow(current_item));
X childX = TestOptionFlag(current_menu, fixedchild)?
X (MenuX(current_menu) + ItemGetArrowPosition(current_item)):
X EventXRootX(rep);
X Trigger = NotSet;
X if (PushSubmenu(childX))
X {
X next_state = LevelControl;
X ProcessExposeEvents();
X LockCursor(ItemWindow(current_item));
X PlacePointer(current_menu, current_item);
X }
X UnlockCursor();
X }
X else if (NewX < OldX) /* reverse motion */
X Trigger = MIN(Trigger, NewX + MenuDelta(current_menu));
X break;
X
X default:
X Retch("(RTLmenu) YOW! Unexpected event!\n");
X next_state = Exit;
X break;
X }
X exit:
X Leave(next_state)
X}
X
State LeafState(rep,selected)
XXEvent rep;
bool *selected;
X{
X State next_state;
X
X Entry("LeafState")
X switch(EventType(rep))
X {
X case LeaveNotify:
X Unhighlight(MenuGetItem(current_menu, EventXWindow(rep)));
X next_state = Initial;
X break;
X
X case ButtonRelease:
X *selected = TRUE;
X next_state = Exit;
X break;
X
X case ButtonPress:
X case EnterNotify:
X case MotionNotify: /* if events set right, this never happens */
X next_state = Leaf;
X break;
X
X default:
X Retch("(RTLMenu) YOW! Unexpected event! (%d)\n",
X rep.type);
X next_state = Exit;
X break;
X }
X Leave(next_state)
X}
X
bool PushSubmenu(x)
int x;
X{
X int y;
X bool pushed;
X MenuItem *new_current_item;
X
X Entry("PushSubmenu")
X
X if (ItemIsNull(current_item))
X {
X Retch("(RTLMenu) Can't push from null item.\n");
X pushed = FALSE;
X }
X else if (MenuIsNull(ItemSubmenu(current_item)))
X {
X Retch("(RTLmenu) Null submenu.\n");
X pushed = FALSE;
X }
X else if (ItemIsNull(MenuItems(ItemSubmenu(current_item))))
X /* submenu has no items -- don't push, but not an error */
X pushed = FALSE;
X else
X {
X y = ItemGetMiddleY(current_item);
X ++level;
X
X if (new_current_item =
X Display_Menu(ItemSubmenu(current_item), current_menu, x, y))
X {
X XFlush(dpy);
X current_menu = ItemSubmenu(current_item);
X current_item = new_current_item;
X if (ItemIsDeaf(current_item) && Autoselect)
X current_item = current_item->nextItem;
X pushed = TRUE;
X }
X else
X {
X Retch("(RTLmenu) Display_Menu failed!\n");
X pushed = FALSE;
X }
X }
X Leave(pushed)
X}
X
void PopSubmenu()
X{
X Menu *parent;
X MenuItem *item;
X
X Entry("PopSubmenu")
X --level;
X parent = current_menu->parentMenu;
X Undisplay_Menu(current_menu);
X current_menu = parent;
X if (!MenuIsNull(current_menu))
X {
X item = MenuItemHighlighted(current_menu);
X if (!ItemIsNull(item))
X {
X current_item = item;
X }
X }
X
X Leave_void
X}
X
void Highlight(item)
MenuItem *item;
X{
X MenuItem *old_highlight;
X
X Entry("Highlight")
X
X old_highlight = MenuItemHighlighted(current_menu);
X if ((item != old_highlight) && /* else, already highlighted */
X (!ItemIsNull(item)))
X {
X if (!ItemIsNull(old_highlight) && !ItemIsDeaf(item))
X Unhighlight(old_highlight);
X MenuInvert(ItemMenu(item), item);
X SetHighlightItem(ItemMenu(item), item);
X }
X Leave_void
X}
X
void Unhighlight(item)
MenuItem *item;
X{
X Entry("Unhighlight")
X if (!ItemIsNull(item))
X {
X if (MenuItemHighlighted(current_menu) == item)
X {
X MenuInvert(ItemMenu(item), item);
X ResetHighlightItem(ItemMenu(item));
X }
X }
X Leave_void
X}
X
void TossExtraMoves(window)
Window window;
X{
X XEvent ev;
X
X Entry("TossExtraMoves")
X /* XSync(dpy,0); /* make sure they're all on the queue */
X while (XCheckTypedWindowEvent(dpy, window, MotionNotify, &ev))
X DBUG_2("RTLmenu","Tossing extra motion.\n");
X Leave_void
X}
X
X
void ProcessExposeEvents()
X{
X MenuItem *item;
X XEvent ev;
X
X Entry("ProcessExposeEvents")
X
X XSync(dpy,0);
X while (XCheckTypedEvent(dpy, Expose, &ev))
X {
X item = MenuGetItem(current_menu, EventXWindow(ev));
X if (!ItemIsNull(item))
X Draw_Item(ItemMenu(item), item, EventEX(ev), EventEY(ev),
X EventEWidth(ev), EventEHeight(ev));
X }
X Leave_void
X}
END_OF_FILE
if test 26723 -ne `wc -c <'awm/menus/track_menu.c'`; then
echo shar: \"'awm/menus/track_menu.c'\" unpacked with wrong size!
fi
# end of 'awm/menus/track_menu.c'
fi
echo shar: End of archive 14 \(of 17\).
cp /dev/null ark14isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 17 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