/*
* Motif
*
* Copyright (c) 1987-2012, The Open Group. All rights reserved.
*
* These libraries and programs are free software; you can
* redistribute them and/or modify them under the terms of the GNU
* Lesser General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* These libraries and programs are distributed in the hope that
* they will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with these librararies and programs; if not, write
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301 USA
*/
/*
* HISTORY
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef REV_INFO
#ifndef lint
static char rcsid[] = "$TOG: TravAct.c /main/14 1999/05/27 13:58:09 mgreess $"
#endif
#endif
/* (c) Copyright 1987, 1988, 1989, 1990, 1991, 1992 HEWLETT-PACKARD COMPANY */
#include "TraversalI.h"
#include "TravActI.h"
#include <Xm/GadgetP.h>
#include <Xm/PrimitiveP.h>
#include <Xm/ManagerP.h>
#include <Xm/VendorSEP.h>
#include <Xm/MenuShellP.h>
#include "RepTypeI.h"
#include <Xm/VirtKeysP.h>
#include <Xm/DisplayP.h>
#include <Xm/ScrolledWP.h>
#include "ToolTipI.h"
#define EVENTS_EQ(ev1, ev2) \
((((ev1)->type == (ev2)->type) &&\
((ev1)->serial == (ev2)->serial) &&\
((ev1)->time == (ev2)->time) &&\
((ev1)->x == (ev2)->x) &&\
((ev1)->y == (ev2)->y)) ? TRUE : FALSE)
/******** Static Function Declarations ********/
static Boolean UpdatePointerData(Widget w, XEvent *event);
static void FlushPointerData(Widget w, XEvent *event);
static void DispatchGadgetInput(XmGadget g, XEvent *event, Mask mask);
/******** End Static Function Declarations ********/
/*
* The following functions are used by the widgets to query or modify one
* of the display dependent global variabled used by traversal mechanism.
*/
unsigned short
_XmGetFocusFlag(Widget w,
unsigned int mask)
{
XmDisplay dd = (XmDisplay)XmGetXmDisplay(XtDisplay(w));
return ((unsigned short)((XmDisplayInfo *)
(dd->display.displayInfo))->resetFocusFlag & mask);
}
void
_XmSetFocusFlag(Widget w,
unsigned int mask,
#if NeedWidePrototypes
int value)
#else
Boolean value)
#endif /* NeedWidePrototypes */
{
XmDisplay dd = (XmDisplay)XmGetXmDisplay(XtDisplay(w));
if (value)
((XmDisplayInfo *)
(dd->display.displayInfo))->resetFocusFlag |= mask;
else
((XmDisplayInfo *)
(dd->display.displayInfo))->resetFocusFlag &= ~mask;
}
static Boolean
UpdatePointerData(Widget w,
XEvent *event)
{
XmFocusData focusData;
if ((focusData = _XmGetFocusData(w)) != NULL)
{
XCrossingEvent *lastEvent = &(focusData->lastCrossingEvent);
focusData->needToFlush = TRUE;
if (!EVENTS_EQ(lastEvent, (XCrossingEvent *)event))
{
focusData->old_pointer_item = focusData->pointer_item;
focusData->pointer_item = w;
focusData->lastCrossingEvent = *(XCrossingEvent *) event;
return TRUE;
}
}
return FALSE;
}
static void
FlushPointerData(Widget w,
XEvent *event)
{
XmFocusData focusData = _XmGetFocusData(w);
if (focusData && focusData->needToFlush)
{
XCrossingEvent lastEvent;
lastEvent = focusData->lastCrossingEvent;
focusData->needToFlush = FALSE;
/*
* We are munging data into the event to fake out the focus
* code when Mwm is trying to catch up with the pointer.
* This event that we are munging might already have been
* munged by XmDispatchGadgetInput from a motion event to a
* crossing event !!!!!
*/
lastEvent.serial = event->xany.serial;
if ( (LeaveNotify == event->type) || (EnterNotify == event->type) )
lastEvent.time = event->xcrossing.time;
else
/* Approximation; the code appears to need even Focus events,
** so make up a time and try to continue, rather than limit
** lastEvent to XCrossingEvents. (It is a flaw in the X
** Protocol that Focus events do not have timestamps.)
*/
lastEvent.time = XtLastTimestampProcessed(XtDisplay(w));
lastEvent.focus = True;
XtDispatchEvent((XEvent *) &lastEvent);
}
}
/************************************************************************
*
* _XmTrackShellFocus
*
* This handler is added by ShellExt initialize to the front of the
* queue
*
************************************************************************/
void
_XmTrackShellFocus(Widget widget,
XtPointer client_data,
XEvent *event,
Boolean *dontSwallow)
{
XmVendorShellExtObject ve = (XmVendorShellExtObject) client_data;
XmFocusData focusData;
XmGeneology oldFocalPoint;
XmGeneology newFocalPoint;
if (widget->core.being_destroyed)
{
*dontSwallow = False;
return;
}
if ((focusData = ve->vendor.focus_data) == NULL)
return;
oldFocalPoint = newFocalPoint = focusData->focalPoint;
switch(event->type)
{
case EnterNotify:
case LeaveNotify:
/*
* If operating in a focus driven model, then enter and
* leave events do not affect the keyboard focus.
*/
if ((event->xcrossing.detail != NotifyInferior) &&
(event->xcrossing.focus))
{
switch (oldFocalPoint)
{
case XmUnrelated:
if (event->type == EnterNotify)
newFocalPoint = XmMyAncestor;
break;
case XmMyAncestor:
if (event->type == LeaveNotify)
newFocalPoint = XmUnrelated;
break;
case XmMyDescendant:
case XmMyCousin:
case XmMySelf:
default:
break;
}
}
break;
case FocusIn:
switch (event->xfocus.detail)
{
case NotifyNonlinear:
case NotifyAncestor:
case NotifyInferior:
newFocalPoint = XmMySelf;
break;
case NotifyNonlinearVirtual:
case NotifyVirtual:
newFocalPoint = XmMyDescendant;
break;
case NotifyPointer:
newFocalPoint = XmMyAncestor;
break;
}
break;
case FocusOut:
switch (event->xfocus.detail)
{
case NotifyPointer:
case NotifyNonlinear:
case NotifyAncestor:
case NotifyNonlinearVirtual:
case NotifyVirtual:
newFocalPoint = XmUnrelated;
break;
case NotifyInferior:
return;
}
break;
}
if (newFocalPoint == XmUnrelated)
{
focusData->old_focus_item = NULL;
if (focusData->trav_graph.num_alloc)
{
/* Free traversal graph, since focus is leaving hierarchy. */
_XmFreeTravGraph(&(focusData->trav_graph));
}
}
if ((focusData->focus_policy == XmEXPLICIT) &&
(oldFocalPoint != newFocalPoint) &&
focusData->focus_item)
{
if (oldFocalPoint == XmUnrelated)
_XmCallFocusMoved(NULL, focusData->focus_item, event);
else if (newFocalPoint == XmUnrelated)
_XmCallFocusMoved(focusData->focus_item, NULL, event);
}
focusData->focalPoint = newFocalPoint;
}
/************************************************************************
*
* Enter & Leave
* Enter and leave event processing routines.
*
************************************************************************/
/*ARGSUSED*/
void
_XmPrimitiveEnter(Widget wid,
XEvent *event,
String *params, /* unused */
Cardinal *num_params) /* unused */
{
_XmToolTipEnter(wid, event, params, num_params);
if (_XmGetFocusPolicy(wid) == XmPOINTER)
{
if (event->xcrossing.focus)
{
_XmCallFocusMoved(XtParent(wid), wid, event);
_XmWidgetFocusChange(wid, XmENTER);
}
UpdatePointerData(wid, event);
}
}
/*ARGSUSED*/
void
_XmPrimitiveLeave(Widget wid,
XEvent *event,
String *params, /* unused */
Cardinal *num_params) /* unused */
{
_XmToolTipLeave(wid, event, params, num_params);
if (_XmGetFocusPolicy(wid) == XmPOINTER)
{
if (event->xcrossing.focus)
{
_XmCallFocusMoved(wid, XtParent(wid), event);
_XmWidgetFocusChange(wid, XmLEAVE);
}
}
}
/************************************************************************
*
* Focus In & Out
*
************************************************************************/
/*ARGSUSED*/
void
_XmPrimitiveFocusInInternal(Widget wid,
XEvent *event,
String *params, /* unused */
Cardinal *num_params) /* unused */
{
if (!(event->xfocus.send_event) ||
_XmGetFocusFlag(wid, XmFOCUS_IGNORE))
return;
if (_XmGetFocusPolicy(wid) == XmPOINTER)
{
/* Maybe Mwm trying to catch up with us. */
if (XtIsShell(XtParent(wid)))
FlushPointerData(wid, event);
}
else
{
/* We should only be recieving the focus from a traversal request. */
if (!_XmGetActiveTabGroup(wid))
_XmMgrTraversal(_XmFindTopMostShell(wid), XmTRAVERSE_NEXT_TAB_GROUP);
else
_XmWidgetFocusChange(wid, XmFOCUS_IN);
}
}
/*ARGSUSED*/
void
_XmPrimitiveFocusOut(Widget wid,
XEvent *event,
String *params, /* unused */
Cardinal *num_params) /* unused */
{
if (event->xfocus.send_event &&
!(wid->core.being_destroyed) &&
(_XmGetFocusPolicy(wid) == XmEXPLICIT))
{
_XmWidgetFocusChange(wid, XmFOCUS_OUT);
}
}
void
_XmPrimitiveFocusIn(Widget pw,
XEvent *event,
String *params,
Cardinal *num_params)
{
_XmPrimitiveFocusInInternal(pw, event, params, num_params);
}
/************************************************************************
*
* _XmEnterGadget
* This function processes enter window conditions occuring in a gadget
*
************************************************************************/
/*ARGSUSED*/
void
_XmEnterGadget(Widget wid,
XEvent *event,
String *params, /* unused */
Cardinal *num_params) /* unused */
{
if (XmIsGadget(wid) && ((XmGadget)wid)->gadget.traversal_on)
{
_XmToolTipEnter(wid, event, params, num_params);
}
if (_XmGetFocusPolicy(wid) == XmPOINTER)
{
XmFocusData focusData = _XmGetFocusData(wid);
/* We may be getting called as a result of Mwm catching up
* with the pointer and setting input focus to the shell
* which then gets forwarded to us.
*/
if (focusData && (focusData->focalPoint != XmUnrelated))
{
_XmCallFocusMoved(XtParent(wid), wid, event);
_XmWidgetFocusChange(wid, XmENTER);
}
}
}
/************************************************************************
*
* DispatchGadgetInput
* This routine is used instead of _XmDispatchGadgetInput due to
* the fact that it needs to dispatch to unmanaged gadgets
*
************************************************************************/
static void
DispatchGadgetInput(XmGadget g,
XEvent *event,
Mask mask)
{
if ((g->gadget.event_mask & mask) && XtIsSensitive((Widget)g))
{
(*(((XmGadgetClass) (g->object.widget_class))->
gadget_class.input_dispatch)) ((Widget) g, event, mask);
}
}
/************************************************************************
*
* _XmLeaveGadget
* This function processes leave window conditions occuring in a gadget
*
************************************************************************/
/*ARGSUSED*/
void
_XmLeaveGadget(Widget wid,
XEvent *event,
String *params, /* unused */
Cardinal *num_params) /* unused */
{
if (XmIsGadget(wid) && ((XmGadget)wid)->gadget.traversal_on)
{
_XmToolTipLeave(wid, event, params, num_params);
}
if (_XmGetFocusPolicy(wid) == XmPOINTER)
{
_XmCallFocusMoved(wid, XtParent(wid), event);
_XmWidgetFocusChange(wid, XmLEAVE);
}
}
/************************************************************************
*
* _XmFocusInGadget
* This function processes focusIn conditions occuring in a gadget
*
************************************************************************/
/*ARGSUSED*/
void
_XmFocusInGadget(Widget wid,
XEvent *event, /* unused */
String *params, /* unused */
Cardinal *num_params) /* unused */
{
if (_XmGetFocusPolicy(wid) == XmEXPLICIT)
_XmWidgetFocusChange(wid, XmFOCUS_IN);
}
/************************************************************************
*
* _XmFocusOutGadget
* This function processes FocusOut conditions occuring in a gadget
*
************************************************************************/
/*ARGSUSED*/
void
_XmFocusOutGadget(Widget wid,
XEvent *event, /* unused */
String *params, /* unused */
Cardinal *num_params) /* unused */
{
if (_XmGetFocusPolicy(wid) == XmEXPLICIT)
_XmWidgetFocusChange(wid, XmFOCUS_OUT);
}
/************************************************************************
*
* Enter, FocusIn and Leave Window procs
*
* These two procedures handle traversal activation and deactivation
* for manager widgets. They are invoked directly throught the
* the action table of a widget.
*
************************************************************************/
/************************************************************************
*
* _XmManagerEnter
* This function handles both focusIn and Enter. Don't ask me why
* :-(
*
************************************************************************/
/*ARGSUSED*/
void
_XmManagerEnter(Widget wid,
XEvent *event_in,
String *params, /* unused */
Cardinal *num_params) /* unused */
{
XmManagerWidget mw = (XmManagerWidget) wid;
XCrossingEvent *event = (XCrossingEvent *) event_in;
if (_XmGetFocusPolicy((Widget) mw) == XmPOINTER)
{
if (UpdatePointerData((Widget) mw, event_in) && event->focus)
{
Widget old;
if (event->detail == NotifyInferior)
old = XtWindowToWidget(event->display, event->subwindow);
else
old = XtParent(mw);
_XmCallFocusMoved(old, (Widget) mw, (XEvent *) event);
_XmWidgetFocusChange((Widget) mw, XmENTER);
}
}
}
/*ARGSUSED*/
void
_XmManagerLeave(Widget wid,
XEvent *event,
String *params, /* unused */
Cardinal *num_params) /* unused */
{
/*
* This code is inefficient since it is called twice for each
* internal move in the hierarchy |||
*/
if (event->type == LeaveNotify)
{
if (_XmGetFocusPolicy(wid) == XmPOINTER)
{
Widget new_wid;
if (event->xcrossing.detail == NotifyInferior)
new_wid = XtWindowToWidget(event->xcrossing.display,
event->xcrossing.subwindow);
else
new_wid = XtParent(wid);
if (UpdatePointerData(wid, event) && event->xcrossing.focus)
{
_XmCallFocusMoved(wid, new_wid, event);
_XmWidgetFocusChange(wid, XmLEAVE);
}
}
}
}
/*ARGSUSED*/
void
_XmManagerFocusInInternal(Widget wid,
XEvent *event,
String *params, /* unused */
Cardinal *num_params) /* unused */
{
Widget child;
/*
* Managers ignore all focus events which have been generated by the
* window system; only those sent to us by a window manager or the
* Xtk focus code is accepted.
* Bail out if the focus policy is not set to explicit
*/
if (!(event->xfocus.send_event) ||
_XmGetFocusFlag(wid, XmFOCUS_RESET | XmFOCUS_IGNORE))
return;
if (_XmGetFocusPolicy(wid) == XmPOINTER)
{
FlushPointerData(wid, event);
}
else if (!_XmGetActiveTabGroup(wid))
{
/* If the heirarchy doesn't have an active tab group give it one. */
_XmMgrTraversal(_XmFindTopMostShell(wid), XmTRAVERSE_NEXT_TAB_GROUP);
}
else if ((child = ((XmManagerWidget) wid)->manager.active_child) &&
XmIsGadget(child))
{
/* If focus went to a gadget, then force it to highlight */
DispatchGadgetInput((XmGadget) child, event, XmFOCUS_IN_EVENT);
}
else
{
_XmWidgetFocusChange(wid, XmFOCUS_IN);
}
}
/*
* Non-menu widgets use this entry point, so that they will ignore focus
* events during menu activities.
*/
void
_XmManagerFocusIn(Widget mw,
XEvent *event,
String *params,
Cardinal *num_params)
{
_XmManagerFocusInInternal(mw, event, params, num_params);
}
/*
* If the manager widget received a FocusOut while it is processing its
* FocusIn event, then it knows that the focus has been successfully moved
* to one of its children. However, if no FocusOut is received, then the
* manager widget must manually force the child to take the focus.
*/
/*ARGSUSED*/
void
_XmManagerFocusOut(Widget wid,
XEvent *event,
String *params, /* unused */
Cardinal *num_params) /* unused */
{
Widget child;
if (!event->xfocus.send_event)
return;
if (_XmGetFocusPolicy(wid) == XmEXPLICIT)
{
/* If focus is in a gadget, then force it to unhighlight. */
if ((child = ((XmManagerWidget) wid)->manager.active_child) &&
XmIsGadget(child))
{
DispatchGadgetInput((XmGadget) child, event, XmFOCUS_OUT_EVENT);
}
else
{
_XmWidgetFocusChange(wid, XmFOCUS_OUT);
}
}
}
/*ARGSUSED*/
void
_XmManagerUnmap(Widget mw,
XEvent *event, /* unused */
String *params, /* unused */
Cardinal *num_params) /* unused */
{
/* This functionality is bogus, since a good implementation
* requires more code (hooks for mapping of widgets) than it's
* worth. To move focus away from a widget when it is unmapped
* implies the ability to recover from the case when the last
* traversable widget in a hierarchy is unmapped and then re-mapped.
* Since we don't have the hooks in place for the mapping of these
* widgets, and since the old code only worked some of the time,
* and since it is arguable that the focus should never be
* changed in response to a widget being unmapped, we should choose
* to do NO traversal in response to the unmapping of a widget.
* However, historical precedent again defeats good design.
*/
_XmValidateFocus(mw);
}
/*ARGSUSED*/
void
_XmPrimitiveUnmap(Widget pw,
XEvent *event, /* unused */
String *params, /* unused */
Cardinal *num_params) /* unused */
{
_XmValidateFocus(pw);
}