822 lines
21 KiB
C
822 lines
21 KiB
C
#ifndef lint
|
||
static char sccs_id[] = "@(#)track_menu.c 5.2 6/2/89";
|
||
#endif
|
||
|
||
/*
|
||
* Copyright 1988 by Siemens Research and Technology Laboratories, Princeton, NJ
|
||
*
|
||
* All Rights Reserved
|
||
*
|
||
* Permission to use, copy, modify, and distribute this software and its
|
||
* documentation for any purpose and without fee is hereby granted,
|
||
* provided that the above copyright notice appear in all copies and that
|
||
* both that copyright notice and this permission notice appear in
|
||
* supporting documentation, and that the name of Siemens Research and Technology
|
||
* Laboratories not be used in advertising or publicity pertaining to
|
||
* distribution of the software without specific, written prior permission.
|
||
*
|
||
*
|
||
* SIEMENS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
|
||
* ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
|
||
* SIEMENS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
|
||
* ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
||
* WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||
* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
||
* SOFTWARE.
|
||
*/
|
||
#include "copyright.h"
|
||
|
||
/*
|
||
RTL Menu Package Version 1.2
|
||
by Joe Camaratta and Mike Berman, Siemens RTL, Princeton NJ, 1988
|
||
|
||
track_menu.c: bring up menus and track the mouse
|
||
*/
|
||
|
||
#include "arrow_icon.h"
|
||
#include "null_icon.h"
|
||
#include "rtlmenuP.h"
|
||
#include "evsaveX.h"
|
||
|
||
#define CLICK_TIME 360 /* in milliseconds */
|
||
#define CursorLockMask (ButtonReleaseMask)
|
||
|
||
/* Event macros */
|
||
|
||
#define EventGetXCoord(rep) ((rep).xmotion.x)
|
||
#define EventGetYCoord(rep) ((rep).xmotion.y)
|
||
#define EventType(rep) ((rep).type)
|
||
#define EventXWindow(rep) ((rep).xcrossing.window)
|
||
#define EventXTime(rep) ((rep).xcrossing.time)
|
||
#define EventXMode(rep) ((rep).xcrossing.mode)
|
||
#define EventXRootX(rep) ((rep).xcrossing.x_root)
|
||
#define EventXRootY(rep) ((rep).xcrossing.y_root)
|
||
#define EventXDetail(rep) ((rep).xcrossing.detail)
|
||
#define EventMWindow(rep) ((rep).xmotion.window)
|
||
#define EventMTime(rep) ((rep).xmotion.time)
|
||
#define EventButton(rep) ((rep).xbutton.button)
|
||
#define EventBWindow(rep) ((rep).xbutton.window)
|
||
#define EventBTime(rep) ((rep).xbutton.time)
|
||
#define EventEX(rep) ((rep).xexpose.x)
|
||
#define EventEY(rep) ((rep).xexpose.y)
|
||
#define EventEWidth(rep) ((rep).xexpose.width)
|
||
#define EventEHeight(rep) ((rep).xexpose.height)
|
||
#define PointerEvent(rep) \
|
||
((EventType(rep) == ButtonPress) || \
|
||
(EventType(rep) == ButtonRelease) || \
|
||
(EventType(rep) == MotionNotify) || \
|
||
(EventType(rep) == EnterNotify) || \
|
||
(EventType(rep) == LeaveNotify) || \
|
||
(EventType(rep) == FocusIn) || \
|
||
(EventType(rep) == FocusOut))
|
||
#define KeyEvent(rep) \
|
||
((EventType(rep) == KeyPress) || (EventType(rep) == KeyRelease))
|
||
#define SelectChildX(menu, item, rep) \
|
||
(TestOptionFlag(menu, fixedchild)? \
|
||
(MenuX(menu) + ItemGetArrowPosition(item)): \
|
||
MIN(MenuX(menu) + ItemGetArrowPosition(item),EventXRootX(rep)))
|
||
|
||
/* Possible states for the state machine */
|
||
typedef enum
|
||
{
|
||
Initial, /* Inside a submenu, but not any item */
|
||
CheckTrigger, /* Inside an item that has submenu, checking for pullright */
|
||
Leaf, /* Inside an item with no submenu */
|
||
Exit, /* Preparing to exit */
|
||
LevelControl /* Not in any submenu, waiting to enter something */
|
||
} State;
|
||
|
||
/* functions in the menu.c module */
|
||
|
||
extern RTLMenu NewMenu();
|
||
extern void DisposeMenu ();
|
||
extern void Draw_Menu();
|
||
extern void ClearInitialItem();
|
||
extern void PlacePointer();
|
||
extern void AdjustPointer();
|
||
extern void SetInitialItem();
|
||
extern void Undisplay_Menu();
|
||
extern void MenuInvert();
|
||
extern void Draw_Item();
|
||
|
||
extern RTLMenuItem MenuItemByName ();
|
||
extern RTLMenuItem Display_Menu();
|
||
extern RTLMenuItem MenuGetItem();
|
||
extern RTLMenuItem GetInitialItem();
|
||
extern RTLMenu MenuGetMenu();
|
||
|
||
void LockCursor();
|
||
void UnlockCursor();
|
||
void GetNextSignificantEvent();
|
||
void PopSubmenu();
|
||
void Highlight();
|
||
void Unhighlight();
|
||
void TossExtraMoves();
|
||
void ProcessExposeEvents();
|
||
|
||
bool GrabPointer();
|
||
|
||
State LevelControlState();
|
||
State GetItemState();
|
||
State InitialState();
|
||
State CheckTriggerState();
|
||
State LeafState();
|
||
|
||
static RTLMenu current_item;
|
||
static RTLMenu current_menu;
|
||
static Window root_window;
|
||
static Display *display;
|
||
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 EventStore ev_save;
|
||
static bool locked = FALSE;
|
||
|
||
RTLMenuItem TrackMenu(root_menu, root_x, root_y, init_button, root, buttime)
|
||
RTLMenu 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) */
|
||
{
|
||
State CurrentState = LevelControl;
|
||
XEvent Event_Reply;
|
||
int open_x;
|
||
bool selected = FALSE;
|
||
RTLMenuItem selected_item;
|
||
|
||
/* Initialize globals */
|
||
|
||
button_time = buttime;
|
||
root_window = root;
|
||
level = 0;
|
||
current_menu = root_menu;
|
||
display = MenuDisplay(current_menu);
|
||
click_allowed = (TestOptionFlag(current_menu, clickokay))? TRUE : FALSE;
|
||
|
||
/* If not already done, set up the null cursor for lock state */
|
||
if (wait_cursor == None)
|
||
{
|
||
Pixmap wc_pixmap;
|
||
XColor fg, bg;
|
||
|
||
wc_pixmap = XCreateBitmapFromData (display, root_window,
|
||
null_icon_bits,
|
||
null_icon_width, null_icon_height);
|
||
wait_cursor = XCreatePixmapCursor (display, wc_pixmap, wc_pixmap,
|
||
&fg, &bg, 1, 1);
|
||
}
|
||
|
||
|
||
/* *** Put up initial menus, and get into correct state *** */
|
||
|
||
AdjustPointer(current_menu, root_x, root_y);
|
||
|
||
/* Get the present state, so it can be restored later */
|
||
/* Any events on the queue when we start get saved now, restored later */
|
||
SaveEvents (display, &ev_save, ~(unsigned long) ButtonReleaseMask);
|
||
|
||
if (!GrabPointer() ||
|
||
!(current_item = Display_Menu(current_menu, NULLMENU, root_x, root_y)))
|
||
{
|
||
CurrentState = Exit;
|
||
}
|
||
else
|
||
{
|
||
LockCursor(ItemWindow(current_item));
|
||
open_x = root_x;
|
||
}
|
||
|
||
/* Push to appropriate previous item, if any */
|
||
while (MenuHasInitialItem(current_menu) && (CurrentState != Exit))
|
||
{
|
||
ProcessExposeEvents();
|
||
current_item = GetInitialItem(current_menu);
|
||
ClearInitialItem(current_menu);
|
||
|
||
/* if the initial item can't be selected, take first in list */
|
||
if (ItemIsNull(current_item) || ItemIsDisabled(current_item))
|
||
{
|
||
current_item = MenuItems(current_menu);
|
||
break;
|
||
}
|
||
else if (ItemIsLeaf(current_item)) /* then we're done */
|
||
break;
|
||
else
|
||
{
|
||
open_x += ItemGetArrowPosition(current_item);
|
||
Highlight(current_item);
|
||
TossExtraMoves(ItemWindow(current_item));
|
||
(void)PushSubmenu(open_x);
|
||
}
|
||
}
|
||
ProcessExposeEvents();
|
||
if (CurrentState != Exit)
|
||
{
|
||
CurrentState = (ItemIsLeaf(current_item))? Leaf: CheckTrigger;
|
||
if (!TestItemFlag(current_item, itemDisabled))
|
||
Highlight(current_item);
|
||
}
|
||
LockCursor(ItemWindow(current_item));
|
||
PlacePointer(current_menu,current_item);
|
||
DisposeEvents(display, (Mask)(PointerMotionMask | EnterWindowMask |
|
||
LeaveWindowMask | ExposureMask));
|
||
UnlockCursor();
|
||
|
||
/* State Machine */
|
||
|
||
while (CurrentState != Exit)
|
||
{
|
||
GetNextSignificantEvent(display, &Event_Reply, init_button);
|
||
|
||
switch (CurrentState)
|
||
{
|
||
case LevelControl:
|
||
CurrentState = LevelControlState(Event_Reply);
|
||
break;
|
||
case Initial:
|
||
CurrentState = InitialState(Event_Reply);
|
||
break;
|
||
case CheckTrigger:
|
||
CurrentState = CheckTriggerState(Event_Reply);
|
||
break;
|
||
case Leaf:
|
||
CurrentState = LeafState(Event_Reply, &selected);
|
||
break;
|
||
default:
|
||
CurrentState = Exit;
|
||
break;
|
||
}
|
||
}
|
||
|
||
/* Clean up and exit */
|
||
selected_item = (selected)? current_item : NULLITEM;
|
||
while (level)
|
||
{
|
||
if (selected && !TestOptionFlag(current_menu, forgetlast))
|
||
SetInitialItem(current_menu, current_item);
|
||
PopSubmenu();
|
||
}
|
||
if (selected && !TestOptionFlag(current_menu, forgetlast))
|
||
{
|
||
SetInitialItem(current_menu, current_item);
|
||
}
|
||
Undisplay_Menu(current_menu);
|
||
UnlockCursor();
|
||
XUngrabPointer(display, CurrentTime);
|
||
|
||
/* Push back any events that were lying around when menus started */
|
||
XFlush(display);
|
||
DisposeEvents(display,
|
||
(Mask) (PointerMotionMask | EnterWindowMask |
|
||
LeaveWindowMask));
|
||
RestoreEvents(display, &ev_save);
|
||
return(selected_item);
|
||
}
|
||
|
||
bool GrabPointer()
|
||
{
|
||
int tries = 10;
|
||
|
||
while((XGrabPointer(display,
|
||
RootWindow(display, MenuScreen(current_menu)),
|
||
True, CursorLockMask, GrabModeAsync,
|
||
GrabModeAsync, None,
|
||
wait_cursor, CurrentTime) != GrabSuccess)
|
||
&& tries--)
|
||
sleep(1);
|
||
if (tries)
|
||
return(TRUE);
|
||
else
|
||
return(FALSE);
|
||
}
|
||
|
||
/* Lock the cursor: make it disappear, and ignore events it generates. */
|
||
/* Optionally, confine it to a single window. */
|
||
/* (Using "None" for confine_window doesn't confine it. ) */
|
||
void LockCursor(confine_window)
|
||
Window confine_window;
|
||
{
|
||
int result;
|
||
|
||
locked = TRUE;
|
||
result = XGrabPointer(display,
|
||
RootWindow(display, MenuScreen(current_menu)),
|
||
True, CursorLockMask, GrabModeAsync,
|
||
GrabModeAsync, confine_window,
|
||
wait_cursor, CurrentTime);
|
||
return;
|
||
}
|
||
|
||
/* Unlock (and unconfine) the cursor. */
|
||
void UnlockCursor()
|
||
{
|
||
int result;
|
||
|
||
if (locked)
|
||
{
|
||
locked = FALSE;
|
||
result = XGrabPointer(display,
|
||
RootWindow(display, MenuScreen(current_menu)),
|
||
True, MenuEventMask,
|
||
GrabModeAsync, GrabModeAsync, None,
|
||
MenuCursor(current_menu), CurrentTime);
|
||
}
|
||
return;
|
||
|
||
}
|
||
|
||
/* Keep getting the X events, until finding one that may be interesting */
|
||
/* to the operation of the state machine. */
|
||
void GetNextSignificantEvent(displ,Event_Reply,init_button)
|
||
Display *displ;
|
||
XEvent *Event_Reply;
|
||
int init_button; /* the button that initiated the menu */
|
||
{
|
||
XEvent Next_Event_Reply;
|
||
bool InsignificantEvent = True;
|
||
|
||
/* Loop as long as any of a number of "insignificant" events */
|
||
/* are found; when the event no longer matches one of the tests, */
|
||
/* it is assumed to be "significant" and returned.*/
|
||
do
|
||
{
|
||
XNextEvent(displ, Event_Reply);
|
||
|
||
/* If this event is an "enter", check whether there is a */
|
||
/* "leave" for the same window already in the queue, */
|
||
/* immediately following it; if so, throw them both out */
|
||
/* and get the next event */
|
||
/* NOTE: might try to look further ahead, but this is */
|
||
/* tricky because other events might intervene. */
|
||
if ((EventType(*Event_Reply) == EnterNotify) &&
|
||
(EventXMode(*Event_Reply) == NotifyNormal) &&
|
||
(QLength(displ) > 0) &&
|
||
(MenuGetMenu(current_menu, EventXWindow(*Event_Reply))
|
||
!= current_menu))
|
||
{
|
||
XPeekEvent(displ,&Next_Event_Reply);
|
||
if ((EventType(Next_Event_Reply) == LeaveNotify) &&
|
||
(EventXMode(Next_Event_Reply) == NotifyNormal) &&
|
||
(EventXWindow(Next_Event_Reply) == EventXWindow(*Event_Reply)))
|
||
{
|
||
XNextEvent(displ, Event_Reply);
|
||
XNextEvent(displ, Event_Reply);
|
||
}
|
||
}
|
||
if (EventNotSignificant(*Event_Reply, init_button))
|
||
{
|
||
if (!(PointerEvent(*Event_Reply) || KeyEvent(*Event_Reply)
|
||
|| EventType(*Event_Reply) == Expose))
|
||
{
|
||
/* might be significant elsewhere -- save it for later */
|
||
AddEventToStore(&ev_save, *Event_Reply);
|
||
}
|
||
}
|
||
else
|
||
InsignificantEvent = FALSE;
|
||
}
|
||
while (InsignificantEvent);
|
||
return;
|
||
|
||
}
|
||
|
||
/* Check whether the event matches one of the events considered */
|
||
/* "not significant".*/
|
||
bool EventNotSignificant(Event_Reply, init_button)
|
||
XEvent Event_Reply;
|
||
int init_button;
|
||
{
|
||
/* Insignificant if not in following list */
|
||
return (!((EventType(Event_Reply) == ButtonRelease) ||
|
||
(EventType(Event_Reply) == MotionNotify) ||
|
||
(EventType(Event_Reply) == EnterNotify) ||
|
||
(EventType(Event_Reply) == Expose) ||
|
||
(EventType(Event_Reply) == LeaveNotify))
|
||
||
|
||
/* Insignificant if leave or enter is not "Normal" */
|
||
(((EventType(Event_Reply) == LeaveNotify) ||
|
||
(EventType(Event_Reply) == EnterNotify)) &&
|
||
(EventXMode(Event_Reply) != NotifyNormal))
|
||
||
|
||
/* Insignificant if hit button other than initial one */
|
||
((EventType(Event_Reply) == ButtonRelease) &&
|
||
(EventButton(Event_Reply) != init_button))
|
||
||
|
||
/* Insignificant if it's an expose and we're in savebits mode */
|
||
((EventType(Event_Reply) == Expose) &&
|
||
TestOptionFlag(current_menu, savebits))
|
||
||
|
||
/* Insignificant if tail end of a click -- and clicks allowed */
|
||
(click_allowed &&
|
||
(EventType(Event_Reply) == ButtonRelease) &&
|
||
(EventBTime(Event_Reply) - button_time < CLICK_TIME))
|
||
);
|
||
}
|
||
|
||
State LevelControlState(rep)
|
||
XEvent rep;
|
||
{
|
||
State next_state;
|
||
RTLMenu entered_menu;
|
||
RTLMenuItem entered_item;
|
||
|
||
switch (EventType(rep))
|
||
{
|
||
case MotionNotify:
|
||
case LeaveNotify:
|
||
next_state = LevelControl; /* loop back to this state */
|
||
break;
|
||
case EnterNotify:
|
||
/* Decide whether we've entered a menu window or item window */
|
||
entered_menu = MenuGetMenu(current_menu, EventXWindow(rep));
|
||
entered_item = MenuGetItem(current_menu,EventXWindow(rep));
|
||
|
||
if ((MenuIsNull(entered_menu)) && (ItemIsNull(entered_item)))
|
||
/* Must be some other window; carry on */
|
||
next_state = LevelControl;
|
||
else if (!ItemIsNull(entered_item) &&
|
||
MenuIsDisplayed(ItemMenu(entered_item)))
|
||
{
|
||
/* we entered an item, but not a window. This should only happen */
|
||
/* when we stayed in the parent of the current submenu. So, */
|
||
/* Pop that submenu and get to the item. */
|
||
if (level)
|
||
{
|
||
LockCursor(ItemWindow(entered_item));
|
||
PopSubmenu();
|
||
ProcessExposeEvents();
|
||
UnlockCursor();
|
||
current_item = entered_item;
|
||
Highlight(current_item);
|
||
next_state = GetItemState(rep);
|
||
}
|
||
else /* I must be very confused... */
|
||
{
|
||
next_state = Exit;
|
||
}
|
||
}
|
||
|
||
else if (!MenuIsNull(entered_menu)&&
|
||
MenuIsDisplayed(entered_menu))
|
||
{
|
||
/* entered a menu that is displayed */
|
||
while ((current_menu != entered_menu) && level)
|
||
/* drop down the menu that was entered */
|
||
PopSubmenu();
|
||
ProcessExposeEvents();
|
||
UnlockCursor();
|
||
if (current_menu == entered_menu)
|
||
next_state = Initial;
|
||
else
|
||
{
|
||
next_state = Exit;
|
||
}
|
||
}
|
||
else
|
||
next_state = LevelControl;
|
||
break;
|
||
case ButtonRelease:
|
||
next_state = Exit;
|
||
break;
|
||
default:
|
||
next_state = Exit;
|
||
break;
|
||
}
|
||
return next_state;
|
||
}
|
||
|
||
/* Figure out the status of the item we've just entered */
|
||
State GetItemState(rep)
|
||
XEvent rep;
|
||
{
|
||
int open_x;
|
||
State next_state;
|
||
|
||
if (ItemIsNull(current_item))
|
||
{
|
||
next_state = Exit;
|
||
}
|
||
else if (MenuIsNull(current_menu))
|
||
{
|
||
next_state = Exit;
|
||
}
|
||
else if (ItemIsLeaf(current_item) ||
|
||
(TestItemFlag(current_item, itemDisabled)))
|
||
{
|
||
if (MenuHasInitialItem(current_menu))
|
||
ClearInitialItem(current_menu);
|
||
next_state = Leaf;
|
||
}
|
||
else if (EventGetXCoord(rep) >= (int) ItemGetArrowPosition(current_item))
|
||
{
|
||
/* entered item in "auto pop-up zone", i.e., over pull-right arrow. */
|
||
LockCursor(ItemWindow(current_item));
|
||
TossExtraMoves(ItemWindow(current_item));
|
||
if (PushSubmenu(SelectChildX(current_menu, current_item, rep)))
|
||
{
|
||
LockCursor(ItemWindow(current_item));
|
||
PlacePointer(current_menu, current_item);
|
||
next_state = LevelControl;
|
||
ProcessExposeEvents();
|
||
}
|
||
else
|
||
next_state = CheckTrigger;
|
||
UnlockCursor();
|
||
}
|
||
|
||
else if (MenuHasInitialItem(current_menu))
|
||
{
|
||
/* Entered menu has initial item -- move to it */
|
||
current_item = GetInitialItem(current_menu);
|
||
open_x = ItemGetArrowPosition(current_item) +
|
||
EventXRootX(rep);
|
||
ClearInitialItem(current_menu);
|
||
LockCursor(ItemWindow(current_item));
|
||
if (PushSubmenu(open_x))
|
||
{
|
||
ProcessExposeEvents();
|
||
LockCursor(ItemWindow(current_item));
|
||
PlacePointer(current_menu, current_item);
|
||
next_state = Initial;
|
||
}
|
||
UnlockCursor();
|
||
}
|
||
else /* parent pull */
|
||
next_state = CheckTrigger;
|
||
return next_state;
|
||
|
||
}
|
||
|
||
State InitialState( rep)
|
||
XEvent rep;
|
||
{
|
||
State next_state;
|
||
|
||
switch (EventType(rep))
|
||
{
|
||
case EnterNotify:
|
||
if (MenuIsNull(current_menu))
|
||
{
|
||
next_state = Exit;
|
||
}
|
||
else if (EventXDetail(rep) == NotifyInferior)
|
||
next_state = Initial;
|
||
else
|
||
{
|
||
current_item = MenuGetItem(current_menu, EventXWindow(rep));
|
||
if (ItemIsNull(current_item))
|
||
{
|
||
next_state = Exit;
|
||
}
|
||
else
|
||
{
|
||
Highlight(current_item);
|
||
next_state = GetItemState(rep);
|
||
}
|
||
}
|
||
break;
|
||
case LeaveNotify:
|
||
/* Decide whether we're actually leaving */
|
||
/* this menu for another submenu or the root, */
|
||
/* or going into an item. */
|
||
next_state = (EventXDetail(rep) == NotifyInferior)?
|
||
Initial : LevelControl;
|
||
break;
|
||
case ButtonRelease:
|
||
next_state = Exit;
|
||
break;
|
||
case MotionNotify:
|
||
next_state = Initial;
|
||
break;
|
||
default:
|
||
next_state = Exit;
|
||
break;
|
||
}
|
||
return next_state;
|
||
|
||
}
|
||
|
||
#define NotSet -1
|
||
/* Look to see if pull-right is requested */
|
||
State CheckTriggerState(rep)
|
||
XEvent rep;
|
||
{
|
||
State next_state = CheckTrigger;
|
||
static int Trigger = NotSet;
|
||
static int OldX, NewX, childX;
|
||
|
||
if (MenuIsNull(current_menu) || ItemIsNull(current_item))
|
||
{
|
||
next_state = Exit;
|
||
goto exit;
|
||
}
|
||
if (Trigger == NotSet) /* set it */
|
||
{
|
||
Trigger = MIN(EventGetXCoord(rep) + MenuDelta(current_menu),
|
||
ItemGetArrowPosition(current_item));
|
||
NewX = NotSet;
|
||
}
|
||
switch (EventType(rep))
|
||
{
|
||
case LeaveNotify:
|
||
next_state = Initial;
|
||
Unhighlight(MenuGetItem(current_menu, EventXWindow(rep)));
|
||
Trigger = NotSet;
|
||
break;
|
||
case EnterNotify: /* Shouldn't really happen, but ... */
|
||
next_state = CheckTrigger;
|
||
break;
|
||
case ButtonRelease:
|
||
next_state = Exit;
|
||
Trigger = NotSet;
|
||
break;
|
||
|
||
case MotionNotify:
|
||
next_state = CheckTrigger;
|
||
OldX = NewX;
|
||
NewX = EventGetXCoord(rep);
|
||
if (NewX >= Trigger)
|
||
{
|
||
LockCursor(ItemWindow(current_item));
|
||
childX = SelectChildX(current_menu, current_item, rep);
|
||
Trigger = NotSet;
|
||
if (PushSubmenu(childX))
|
||
{
|
||
next_state = LevelControl;
|
||
ProcessExposeEvents();
|
||
LockCursor(ItemWindow(current_item));
|
||
PlacePointer(current_menu, current_item);
|
||
}
|
||
UnlockCursor();
|
||
}
|
||
else if (NewX < OldX) /* reverse motion */
|
||
Trigger = MIN(Trigger, NewX + MenuDelta(current_menu));
|
||
break;
|
||
default:
|
||
next_state = Exit;
|
||
break;
|
||
}
|
||
exit:
|
||
return next_state;
|
||
}
|
||
|
||
State LeafState(rep,selected)
|
||
XEvent rep;
|
||
bool *selected;
|
||
{
|
||
State next_state;
|
||
|
||
switch(EventType(rep))
|
||
{
|
||
case LeaveNotify:
|
||
Unhighlight(MenuGetItem(current_menu, EventXWindow(rep)));
|
||
next_state = Initial;
|
||
break;
|
||
case ButtonRelease:
|
||
if (!TestItemFlag(current_item, itemDisabled))
|
||
*selected = TRUE;
|
||
next_state = Exit;
|
||
break;
|
||
case EnterNotify:
|
||
case MotionNotify: /* if events set right, this never happens */
|
||
next_state = Leaf;
|
||
break;
|
||
default:
|
||
next_state = Exit;
|
||
break;
|
||
}
|
||
return next_state;
|
||
}
|
||
|
||
bool PushSubmenu(x)
|
||
int x;
|
||
{
|
||
int y;
|
||
bool pushed;
|
||
RTLMenuItem new_current_item;
|
||
|
||
if (ItemIsNull(current_item))
|
||
{
|
||
pushed = FALSE;
|
||
}
|
||
else if (MenuIsNull(ItemSubmenu(current_item)))
|
||
{
|
||
pushed = FALSE;
|
||
}
|
||
else if (ItemIsNull(MenuItems(ItemSubmenu(current_item))))
|
||
/* submenu has no items -- don't push, but not an error */
|
||
pushed = FALSE;
|
||
else
|
||
{
|
||
y = ItemGetMiddleY(current_item);
|
||
++level;
|
||
|
||
if (new_current_item =
|
||
Display_Menu(ItemSubmenu(current_item), current_menu, x, y))
|
||
{
|
||
XFlush(display);
|
||
current_menu = ItemSubmenu(current_item);
|
||
current_item = new_current_item;
|
||
pushed = TRUE;
|
||
}
|
||
else
|
||
{
|
||
pushed = FALSE;
|
||
}
|
||
}
|
||
return pushed;
|
||
}
|
||
|
||
void PopSubmenu()
|
||
{
|
||
RTLMenu parent;
|
||
RTLMenuItem item;
|
||
|
||
--level;
|
||
parent = MenuParent(current_menu);
|
||
Undisplay_Menu(current_menu);
|
||
current_menu = parent;
|
||
if (!MenuIsNull(current_menu))
|
||
{
|
||
item = MenuItemHighlighted(current_menu);
|
||
if (!ItemIsNull(item))
|
||
{
|
||
current_item = item;
|
||
}
|
||
}
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
void Highlight(item)
|
||
RTLMenuItem item;
|
||
{
|
||
RTLMenuItem old_highlight;
|
||
|
||
|
||
old_highlight = MenuItemHighlighted(current_menu);
|
||
if ((item != old_highlight) && /* else, already highlighted */
|
||
(!ItemIsNull(item)))
|
||
{
|
||
if (!ItemIsNull(old_highlight))
|
||
Unhighlight(old_highlight);
|
||
if (!TestItemFlag(current_item, itemDisabled))
|
||
{
|
||
MenuInvert(ItemMenu(item), ItemWindow(item));
|
||
SetHighlightItem(ItemMenu(item), item);
|
||
}
|
||
}
|
||
return;
|
||
|
||
}
|
||
|
||
void Unhighlight(item)
|
||
RTLMenuItem item;
|
||
{
|
||
if (!ItemIsNull(item))
|
||
{
|
||
if (MenuItemHighlighted(current_menu) == item)
|
||
{
|
||
MenuInvert(ItemMenu(item), ItemWindow(item));
|
||
ResetHighlightItem(ItemMenu(item));
|
||
}
|
||
}
|
||
return;
|
||
|
||
}
|
||
|
||
void TossExtraMoves(window)
|
||
Window window;
|
||
{
|
||
XEvent ev;
|
||
|
||
|
||
while (XCheckTypedWindowEvent(display, window, MotionNotify, &ev));
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
void ProcessExposeEvents()
|
||
{
|
||
RTLMenuItem item;
|
||
XEvent ev;
|
||
|
||
if (!TestOptionFlag(current_menu, savebits))
|
||
{
|
||
XSync(display,0);
|
||
|
||
while (XCheckTypedEvent(display, Expose, &ev))
|
||
{
|
||
item = MenuGetItem(current_menu, EventXWindow(ev));
|
||
if (!ItemIsNull(item))
|
||
Draw_Item(ItemMenu(item), item, EventEX(ev), EventEY(ev),
|
||
EventEWidth(ev), EventEHeight(ev));
|
||
}
|
||
}
|
||
return;
|
||
}
|