/*
* 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: Traversal.c /main/20 1999/08/17 11:52:26 vipin $"
#endif
#endif
/* (c) Copyright 1987, 1988, 1989, 1990, 1991, 1992 HEWLETT-PACKARD COMPANY */
#include <Xm/GadgetP.h>
#include <Xm/ManagerP.h>
#include <Xm/MenuShellP.h>
#include <Xm/PrimitiveP.h>
#include <Xm/ScrolledWP.h>
#include <Xm/TraitP.h>
#include <Xm/TravConT.h>
#include <Xm/VendorSEP.h>
#include <Xm/VirtKeysP.h>
#include <Xm/DisplayP.h>
#include "BaseClassI.h"
#include "CallbackI.h"
#include "RepTypeI.h"
#include "TravActI.h"
#include "TraversalI.h"
/******** Static Function Declarations ********/
static Widget FindFirstManaged(
Widget wid) ;
static Boolean CallTraverseObsured(
Widget new_focus,
XmTraversalDirection dir) ;
static Boolean IsTraversable(
Widget wid,
Boolean require_in_view) ;
static Widget FindFirstFocus(
Widget wid) ;
static Boolean CallFocusMoved(Widget old,
Widget new_wid,
XEvent *event,
XmTraversalDirection direction);
static Widget RedirectTraversal(Widget old_focus,
Widget new_focus,
unsigned int focus_policy,
XmTraversalDirection direction,
unsigned int pass);
/******** End Static Function Declarations ********/
XmFocusData
_XmCreateFocusData( void )
{
return (XmFocusData) XtCalloc(1, sizeof( XmFocusDataRec)) ;
}
void
_XmDestroyFocusData(
XmFocusData focusData )
{
_XmFreeTravGraph( &(focusData->trav_graph)) ;
XtFree((char *) focusData->trav_graph.excl_tab_list) ;
XtFree((char *) focusData) ;
}
void
_XmSetActiveTabGroup(
XmFocusData focusData,
Widget tabGroup )
{
focusData->active_tab_group = tabGroup;
}
Widget
_XmGetActiveItem(
Widget w )
{
return XmGetFocusWidget( w) ;
}
/*ARGSUSED*/
void
_XmNavigInitialize(
Widget request, /* unused */
Widget new_wid,
ArgList args, /* unused */
Cardinal *num_args ) /* unused */
{
XmFocusData focusData ;
if( (focusData = _XmGetFocusData( new_wid)) != NULL )
{
XmNavigationType navType = _XmGetNavigationType( new_wid) ;
if( navType == XmEXCLUSIVE_TAB_GROUP )
{
++(focusData->trav_graph.exclusive) ;
_XmTabListAdd( &(focusData->trav_graph), new_wid) ;
}
else
{
if( navType == XmSTICKY_TAB_GROUP )
{
_XmTabListAdd( &(focusData->trav_graph), new_wid) ;
}
}
if( focusData->trav_graph.num_entries
&& _XmGetNavigability( new_wid) )
{
/* If the graph exists, add the new navigable widget.
*/
_XmTravGraphAdd( &(focusData->trav_graph), new_wid) ;
}
}
/* If the traversal graph doesn't exist, do nothing, since the
* new widget will be picked-up when the graph is needed and created.
*/
return ;
}
/*ARGSUSED*/
Boolean
_XmNavigSetValues(
Widget current,
Widget request, /* unused */
Widget new_wid,
ArgList args, /* unused */
Cardinal *num_args ) /* unused */
{
/* This routine is called from the SetValues method of Manager,
* Primitive, and Gadget to keep the traversal data structures
* up-to-date in regards to changes in the traversability of widgets.
*
* There are three purposes for this routine:
*
* 1: Update the traversal graph in response to changes in
* a widget's resources such that the widget is newly
* eligible to receive the traversal focus.
*
* 2: Update the focus data according to changes in
* the Motif 1.0 "exclusive tab group" behavior.
*
* 3: If the new widget of the SetValues call is the focus
* widget and it becomes ineligible to have the focus,
* then find an alternative to receive the focus (or
* reset the focus for the hierarchy to the bootstrap
* condition).
*/
XmFocusData focusData ;
if( (focusData = _XmGetFocusData( new_wid)) != NULL )
{
XmTravGraph graph = &(focusData->trav_graph) ;
XmNavigationType newNavType = _XmGetNavigationType( new_wid) ;
XmNavigationType curNavType = _XmGetNavigationType( current) ;
Boolean ChangeInExclusive = FALSE ;
if( curNavType != newNavType )
{
if( (curNavType == XmEXCLUSIVE_TAB_GROUP)
|| (newNavType == XmEXCLUSIVE_TAB_GROUP) )
{
/* This widget was "exclusive", now it is not (or vice-versa).
* Update the value of the focus data "exclusive" field.
*/
ChangeInExclusive = TRUE ;
if( newNavType == XmEXCLUSIVE_TAB_GROUP )
{
++(graph->exclusive) ;
}
else
{
--(graph->exclusive) ;
}
}
if( (newNavType == XmEXCLUSIVE_TAB_GROUP)
|| (newNavType == XmSTICKY_TAB_GROUP) )
{
if( (curNavType != XmEXCLUSIVE_TAB_GROUP)
&& (curNavType != XmSTICKY_TAB_GROUP) )
{
_XmTabListAdd( graph, new_wid) ;
}
}
else
{
if( (curNavType == XmEXCLUSIVE_TAB_GROUP)
|| (curNavType == XmSTICKY_TAB_GROUP) )
{
_XmTabListDelete( graph, new_wid) ;
}
}
}
if( XtIsRealized( new_wid)
&& (focusData->focus_policy == XmEXPLICIT) )
{
if( graph->num_entries )
{
if( ChangeInExclusive )
{
/* Since widget has changed to/from exlusive tab group
* behavior, need to re-make the traversal graph (as needed).
*/
_XmFreeTravGraph( graph) ;
}
else
{
XmNavigability cur_nav = _XmGetNavigability( current) ;
XmNavigability new_nav = _XmGetNavigability( new_wid) ;
if( !cur_nav && new_nav )
{
/* Newly navigable widget; add it to the
* traversal graph.
*/
_XmTravGraphAdd( graph, new_wid) ;
}
else
{
if( cur_nav != new_nav )
{
/* Navigability changed; need to re-create the
* graph the next time it is needed.
*/
_XmFreeTravGraph( graph) ;
}
}
}
}
if( !(focusData->focus_item) )
{
Widget shell ;
/* CR 5417: Remove (focusData->focalPoint != XmMySelf) test. */
if( XmIsTraversable( new_wid)
&& (shell = _XmFindTopMostShell( new_wid))
&& XmeFocusIsInShell( shell))
{
/* Hierarchy currently has no focus, and this widget is
* now traversable, so bootstrap the focus for the hierarchy.
*/
_XmMgrTraversal( shell, XmTRAVERSE_CURRENT) ;
}
}
else
{
if( (focusData->focus_item == new_wid)
&& !IsTraversable( new_wid, TRUE) )
{
/* The new_wid now has the focus and is no longer
* traversable, so traverse away from it to the
* next traversable item.
*/
Widget new_focus = _XmTraverseAway( graph, new_wid,
(focusData->active_tab_group != new_wid)) ;
if( !new_focus )
{
/* Could not find another widget eligible to take
* the focus, so use any widget to re-initialize/clear
* the focus in the widget hierarchy.
*/
new_focus = new_wid ;
}
_XmMgrTraversal( new_focus, XmTRAVERSE_CURRENT) ;
if( !XtIsSensitive( new_wid) )
{
/* Since widget has become insensitive, it did not
* receive the focus-out event. Call the focus
* change method directly.
*/
_XmWidgetFocusChange( new_wid, XmFOCUS_OUT) ;
}
return TRUE ;
}
}
}
}
return FALSE ;
}
void
XmeNavigChangeManaged(
Widget wid )
{
/* This routine must be called from the ChangeManaged method of
* all composite widgets that may have traversable children.
* This routine checks to see if the focus widget is traversable;
* if it is not, then an alternative traversable widget is found
* or the focus for the hierarchy is reset to the bootstrap condition.
*
* This routine also detects the condition for which there is no
* focus widget in the hierarchy and a newly managed widget is
* now eligible to have the focus; the focus is then initialized.
*/
XmFocusData focus_data ;
_XmWidgetToAppContext(wid);
_XmAppLock(app);
if( XtIsRealized( wid)
&& (focus_data = _XmGetFocusData( wid))
&& (focus_data->focus_policy == XmEXPLICIT) )
{
if( focus_data->focus_item == NULL )
{
Widget firstManaged ;
if( XtIsShell( wid) )
{
if( focus_data->first_focus == NULL )
{
focus_data->first_focus = FindFirstFocus( wid) ;
}
if( (firstManaged = FindFirstManaged( wid)) != NULL )
{
/* Set bootstrap trigger for hierarchy that
* has no current focus.
*/
XtSetKeyboardFocus( wid, firstManaged) ;
}
}
}
else
{
/* If the focus widget is being destroyed, do nothing for now.
* We need to wait until _XmNavigDestroy is called to initiate
* the focus change; if we don't defer selection of the focus
* widget, the Intrinsics-generated focus-out event for the
* focus widget will go to the newly-selected focus widget
* (instead of the widget being destroyed, as intended).
*/
if( !(focus_data->focus_item->core.being_destroyed)
&& !IsTraversable( focus_data->focus_item, TRUE) )
{
Widget new_focus = _XmTraverseAway( &(focus_data->trav_graph),
focus_data->focus_item,
(focus_data->active_tab_group
!= focus_data->focus_item)) ;
if( !new_focus )
{
new_focus = focus_data->focus_item ;
}
if(new_focus)
_XmMgrTraversal( new_focus, XmTRAVERSE_CURRENT) ;
}
}
}
_XmAppUnlock(app);
return ;
}
static Widget
FindFirstManaged(
Widget wid)
{
if( XtIsShell( wid) )
{
unsigned i = 0 ;
while( i < ((CompositeWidget) wid)->composite.num_children )
{
if( XtIsManaged( ((CompositeWidget) wid)
->composite.children[i]) )
{
return ((CompositeWidget) wid)->composite.children[i] ;
}
++i ;
}
}
return NULL ;
}
void
_XmNavigResize(
Widget wid)
{
/* This routine must be called by all composites with (potentially)
* traversable children. This is generally handled for all managers
* in the resize wrapper routines.
*
* This routine makes sure that the focus widget is always in view,
* either by invoking the XmNtraverseObscurredCallback mechansism
* of Scrolled Window or by finding an alternative focus widget.
*/
XmFocusData focus_data ;
if( XtIsRealized( wid) && !XtIsShell( wid)
&& (focus_data = _XmGetFocusData( wid)) )
{
/* If the focus item is being destroyed, do nothing, since this
* will be handled more appropriately by _XmNavigDestroy().
*/
if( (focus_data->focus_policy == XmEXPLICIT)
&& ( !(focus_data->focus_item)
|| !((focus_data->focus_item)->core.being_destroyed)) )
{
if( !(focus_data->focus_item) )
{
/* Hierarchy has no focus widget; re-initialize/clear the
* focus, but only if the parent is a managed shell (to
* avoid premature initialization during XtRealizeWidget).
*/
Widget parent = XtParent( wid) ;
Widget firstManaged ;
if( parent && XtIsShell( parent)
&& (firstManaged = FindFirstManaged( parent)) )
{
/* Set bootstrap trigger for hierarchy that
* has no current focus.
*/
XtSetKeyboardFocus( wid, firstManaged) ;
}
}
else
{
if( !IsTraversable( focus_data->focus_item, TRUE) )
{
/* Widget is not traversable, either because it is not
* viewable or some other reason. Test again, this
* time allowing for obscured traversal.
*
* If it is not traversable regardless of the
* XmNtraverseObscuredCallback, or traversal to the
* obscured widget fails for some other reason, traverse
* away from the non-traversable widget.
*/
if( !IsTraversable( focus_data->focus_item, FALSE)
|| !_XmMgrTraversal( focus_data->focus_item,
XmTRAVERSE_CURRENT) )
{
Widget new_focus = _XmTraverseAway(
&(focus_data->trav_graph), focus_data->focus_item,
(focus_data->active_tab_group
!= focus_data->focus_item)) ;
if( !new_focus )
{
new_focus = focus_data->focus_item ;
}
if(new_focus)
_XmMgrTraversal( new_focus, XmTRAVERSE_CURRENT) ;
}
}
}
}
}
}
void
_XmValidateFocus(
Widget wid )
{
XmFocusData focus_data = _XmGetFocusData( wid) ;
if( focus_data
&& (focus_data->focus_policy == XmEXPLICIT)
&& (focus_data->focus_item != NULL)
&& !IsTraversable( focus_data->focus_item, TRUE) )
{
Widget new_focus = _XmTraverseAway( &(focus_data->trav_graph),
focus_data->focus_item,
(focus_data->active_tab_group != focus_data->focus_item)) ;
if( !new_focus )
{
new_focus = wid ;
}
_XmMgrTraversal( new_focus, XmTRAVERSE_CURRENT) ;
}
}
void
_XmNavigDestroy(
Widget wid )
{
/* This routine is used to keep the traversal data up-to-date with
* regards to widgets which are being destroyed. It must be called
* by all composites that might have traversable children. The
* DeleteChild method for Manager calls this routine, so its
* subclasses can explicitly chain to its superclasses DeleteChild
* method or call this routine directly.
*
* In addition to finding a new focus widget if it is being
* destroyed, this routine must make sure that there are no
* stale pointers to the widget being destroyed in any of its
* data structures.
*/
XmFocusData focusData = _XmGetFocusData( wid) ;
if( focusData )
{
XmTravGraph trav_list = &(focusData->trav_graph) ;
XmNavigationType navType = _XmGetNavigationType( wid) ;
if( wid == focusData->first_focus )
{
focusData->first_focus = NULL ;
}
if( navType == XmEXCLUSIVE_TAB_GROUP )
{
--(trav_list->exclusive) ;
_XmTabListDelete( trav_list, wid) ;
}
else
{
if( navType == XmSTICKY_TAB_GROUP )
{
_XmTabListDelete( trav_list, wid) ;
}
}
if( focusData->focus_item == wid )
{
/* The focus widget for this hierarhcy is being destroyed.
* Traverse away if in explicit mode, or just clear the
* focus item field.
*/
Widget new_focus ;
if( (focusData->focus_policy != XmEXPLICIT)
|| ( !(new_focus = _XmTraverseAway( trav_list,
focusData->focus_item,
(focusData->active_tab_group != wid)))
&& !(new_focus = _XmFindTopMostShell( wid)))
|| !_XmMgrTraversal( new_focus, XmTRAVERSE_CURRENT) )
{
focusData->focus_item = NULL ;
}
}
if( focusData->trav_graph.num_entries )
{
_XmTravGraphRemove( trav_list, wid) ;
}
if( focusData->active_tab_group == wid )
{
focusData->active_tab_group = NULL ;
}
if( focusData->old_focus_item == wid )
{
focusData->old_focus_item = NULL ;
}
if( focusData->pointer_item == wid )
{
focusData->pointer_item = NULL ;
}
}
return ;
}
static Boolean
CallFocusMoved(Widget old,
Widget new_wid,
XEvent *event,
XmTraversalDirection direction)
{
Widget w ;
Widget topShell ;
XtCallbackList callbacks ;
Boolean contin = TRUE ;
if (old)
w = old;
else /* if (new_wid) -- if there's no w assignment we're in big trouble! */
w = new_wid;
topShell = (Widget) _XmFindTopMostShell(w);
/*
* make sure it's a shell that has a vendorExt object
*/
if (XmIsVendorShell(topShell))
{
XmWidgetExtData extData;
XmVendorShellExtObject vendorExt;
extData = _XmGetWidgetExtData(topShell, XmSHELL_EXTENSION);
if(extData==NULL) return (contin);
if ((vendorExt = (XmVendorShellExtObject) extData->widget) != NULL)
{
if ((callbacks = vendorExt->vendor.focus_moved_callback) != NULL)
{
XmFocusMovedCallbackStruct callData;
callData.reason = XmCR_FOCUS_MOVED;
callData.event = event;
callData.cont = True;
callData.old_focus = old;
callData.new_focus = new_wid;
callData.focus_policy = vendorExt->vendor.focus_policy;
callData.direction = direction;
_XmCallCallbackList((Widget) vendorExt, callbacks,
(XtPointer) &callData);
contin = callData.cont ;
}
}
}
return( contin) ;
}
Boolean
_XmCallFocusMoved(
Widget old,
Widget new_wid,
XEvent *event )
{
return CallFocusMoved(old, new_wid, event, XmTRAVERSE_CURRENT);
}
Boolean
_XmMgrTraversal(
Widget wid,
XmTraversalDirection direction)
{
/* This routine is the workhorse for all traversal activities. */
Widget top_shell ;
Widget old_focus ;
Widget new_focus ;
Widget new_active_tab ;
XmFocusData focus_data;
XmTravGraph trav_list ;
Boolean rtnVal = FALSE ;
XmTraversalDirection local_dir;
XmDisplay dd = (XmDisplay)XmGetXmDisplay(XtDisplay(wid));
#define traversal_in_progress \
((XmDisplayInfo *)(dd->display.displayInfo))->traversal_in_progress
if( traversal_in_progress
|| !(top_shell = _XmFindTopMostShell( wid))
|| top_shell->core.being_destroyed
|| !(focus_data = _XmGetFocusData( wid))
|| (focus_data->focus_policy != XmEXPLICIT) )
{
return FALSE ;
}
traversal_in_progress = TRUE ;
/* Recursive traversal calls can sometimes be generated during
* the handling of focus events and associated callbacks.
* In this version of Motif, recursive calls always fail.
*
* Future enhancements could include the addition of a queue
* for recursive calls; these calls would then be serviced on
* a FIFO basis following the completion of the initial traversal
* processing. Sequential FIFO processing is essential for
* providing a consistent and predicable environment for
* focus change callbacks and event processing.
*/
trav_list = &(focus_data->trav_graph) ;
old_focus = focus_data->focus_item ;
if( (old_focus == NULL)
&& (wid == top_shell)
&& focus_data->first_focus
&& IsTraversable( focus_data->first_focus, TRUE) )
{
new_focus = focus_data->first_focus ;
if (direction == XmTRAVERSE_GLOBALLY_FORWARD)
local_dir = XmTRAVERSE_NEXT_TAB_GROUP;
else if (direction == XmTRAVERSE_GLOBALLY_BACKWARD)
local_dir = XmTRAVERSE_PREV_TAB_GROUP;
else
local_dir = direction;
}
else
{
new_focus = _XmTraverse(trav_list, direction, &local_dir, wid) ;
}
new_focus = RedirectTraversal(old_focus, new_focus,
focus_data->focus_policy, local_dir, 0);
if( new_focus
&& (new_focus == old_focus)
&& focus_data->old_focus_item )
{
/* When traversal does not cause the focus to change
* to a different widget, focus-change events should
* not be generated. The old_focus_item will be NULL
* when the focus is moving into this shell hierarchy
* from a different shell; in this case, focus-in
* events should be generated below.
*/
rtnVal = TRUE ;
}
else if( new_focus
&& (new_active_tab = XmGetTabGroup( new_focus))
&& CallFocusMoved(old_focus, new_focus, NULL, local_dir)
&& CallTraverseObsured(new_focus, local_dir))
{
/* Set the keyboard focus in two steps; first to None, then
* to the new focus widget. This will cause appropriate
* focus-in and focus-out events to be generated, even if
* the focus change is between two gadgets.
*
* Note that XtSetKeyboardFocus() generates focus change
* events "in-line", so focus data and manager active_child
* fields are not updated until after the focus-out events have
* been generated and dispatched to the current focus item.
*
* The FocusResetFlag is used to tell event actions procs to
* ignore any focus-in event that might be generated by the
* window manager (which won't like the fact that there the
* focus is now going to point to nobody).
*/
_XmSetFocusFlag( top_shell, XmFOCUS_RESET, TRUE) ;
XtSetKeyboardFocus( top_shell, None) ;
_XmSetFocusFlag( top_shell, XmFOCUS_RESET, FALSE) ;
_XmClearFocusPath( old_focus) ;
focus_data->active_tab_group = new_active_tab ;
if( (new_active_tab != new_focus)
&& XmIsManager( new_active_tab) )
{
XmManagerWidget manager = (XmManagerWidget) new_active_tab;
manager->manager.active_child = new_focus;
}
if( (new_active_tab != XtParent( new_focus)) /* Set above. */
&& XmIsManager( XtParent( new_focus)) )
{
XmManagerWidget manager = (XmManagerWidget) XtParent(new_focus);
manager->manager.active_child = new_focus ;
}
focus_data->focus_item = new_focus ;
focus_data->old_focus_item = old_focus ? old_focus : new_focus ;
/* Setting the focus data and manager active_child fields enables
* focus-in events to be propagated to the new focus widget.
*/
XtSetKeyboardFocus( top_shell, new_focus) ;
rtnVal = TRUE ;
}
else
{
/* Have failed to traverse to a new widget focus widget.
* If the current focus widget is no longer traversable,
* then reset focus data to its bootstrap state.
*/
if( !old_focus
|| !IsTraversable( old_focus, TRUE) )
{
Widget firstManaged = FindFirstManaged( top_shell) ;
_XmSetFocusFlag( top_shell, XmFOCUS_RESET, TRUE) ;
XtSetKeyboardFocus( top_shell, firstManaged) ;
_XmSetFocusFlag( top_shell, XmFOCUS_RESET, FALSE) ;
_XmClearFocusPath( old_focus) ;
_XmFreeTravGraph( trav_list) ;
}
}
if( trav_list->num_entries
&& (focus_data->focalPoint == XmUnrelated)
&& ( XmIsVendorShell( top_shell)
|| !XmeFocusIsInShell( top_shell)) )
{
/* Free the graversal graph whenever the focus is out of this
* shell hierarchy, so memory use is limited to one traversal
* graph per display. Since VendorShell has a handler which
* tracks the input focus, all we need to do is look at the
* focusData field. For MenuShell and others, we need to go
* through the X server to find out where the focus is.
*
* Note the logic of the above conditional; VendorShell is the
* only shell class that maintains the focalPoint field of the
* focus data. So, if its a VendorShell and focalPoint says
* "unrelated", we have the answer; any other shell and we need
* to call the generic focus test routine.
*/
_XmFreeTravGraph( trav_list) ;
}
traversal_in_progress = FALSE ;
return rtnVal ;
}
static Boolean
CallTraverseObsured(
Widget new_focus,
XmTraversalDirection dir)
{
Widget prev;
Widget ancestor = new_focus;
XRectangle focus_rect; /* Area we're trying to make visible. */
XRectangle clip_rect; /* Area a given ancestor obscures. */
XRectangle view_rect; /* Temporary intersection of the two. */
XmTraverseObscuredCallbackStruct call_data;
call_data.reason = XmCR_OBSCURED_TRAVERSAL;
call_data.event = NULL;
call_data.traversal_destination = new_focus;
call_data.direction = dir;
_XmSetRect(&focus_rect, new_focus);
/* Look for ancestors that clip this window. */
for (prev = ancestor;
((ancestor = XtParent(ancestor)) != NULL) && !XtIsShell(ancestor);
prev = ancestor)
{
/* CR 9705: Special case overlapping work windows. */
if (!_XmIsScrollableClipWidget(ancestor, False, &clip_rect))
_XmSetRect(&clip_rect, ancestor);
if (!_XmIntersectionOf(&focus_rect, &clip_rect, &view_rect) ||
(view_rect.width != focus_rect.width) ||
(view_rect.height != focus_rect.height))
{
/* This ancestor clips somebody. */
Widget sw = _XmIsScrollableClipWidget(prev, True, &focus_rect);
if (sw != NULL)
{
XtCallbackList callbacks = ((XmScrolledWindowWidget) sw)
->swindow.traverseObscuredCallback;
XtCallCallbackList(sw, callbacks, (XtPointer) &call_data);
ancestor = sw;
}
else
{
_XmIntersectRect(&focus_rect, ancestor, &focus_rect);
}
}
}
return IsTraversable( new_focus, TRUE);
}
void
_XmClearFocusPath(
Widget wid )
{
/* This routine should be called whenever the focus of a shell
* hierarchy needs to be reset to the bootstrap condition.
*
* This routine clears the active_child field of all manager
* widget ancestors of the widget argument, and clears other
* focus widget fields of the focus data record. The clearing
* of the old_focus_item field indicates to the traversal code
* that the focus is not in this shell hierarchy.
*/
XmFocusData focus_data ;
while( wid && !XtIsShell( wid) )
{
if( XmIsManager( wid) )
{
((XmManagerWidget) wid)->manager.active_child = NULL ;
}
wid = XtParent( wid) ;
}
if( (focus_data = _XmGetFocusData( wid)) != NULL )
{
focus_data->focus_item = NULL ;
focus_data->old_focus_item = NULL ;
focus_data->active_tab_group = NULL ;
}
}
Boolean
_XmFocusIsHere(
Widget w )
{
XmFocusData focus_data;
Widget item;
if ((focus_data = _XmGetFocusData( w)) &&
(item = focus_data->focus_item))
{
for (; !XtIsShell(item); item = XtParent(item))
if (item == w)
return True;
}
return(False);
}
unsigned char
_XmGetFocusPolicy(
Widget w )
{
Widget topmost_shell ;
/* Find the topmost shell widget
*/
topmost_shell = _XmFindTopMostShell( w) ;
if( XtIsVendorShell( topmost_shell) )
{
XmWidgetExtData xwed = _XmGetWidgetExtData(topmost_shell, XmSHELL_EXTENSION);
if(xwed == NULL) return(XmPOINTER);
return (((XmVendorShellExtObject)
(xwed)->widget)
->vendor.focus_policy) ;
}
else
{
if( XmIsMenuShell( topmost_shell) )
{
return( ((XmMenuShellWidget) topmost_shell)->menu_shell.focus_policy);
}
}
return( XmPOINTER) ;
}
Widget
_XmFindTopMostShell(
Widget w )
{
while( w && !XtIsShell( w) )
{
w = XtParent( w) ;
}
return( w) ;
}
/*ARGSUSED*/
void
_XmFocusModelChanged(
Widget wid,
XtPointer client_data, /* unused */
XtPointer call_data )
{
/* Invoked by the VendorShell widget, when the focus_policy changes.
* Registered as a callback by both the Manager and Primitive classes,
* when the parent is a VendorShell widget.
*/
unsigned char new_focus_policy = (unsigned char)(unsigned long) call_data ;
Widget shell = _XmFindTopMostShell( wid) ;
XmFocusData focus_data = _XmGetFocusData( shell) ;
if( focus_data )
{
if( new_focus_policy == XmEXPLICIT )
{
Widget new_item = focus_data->pointer_item ;
if( new_item != NULL )
{
if( XmIsManager( new_item)
&& (((XmManagerWidget) new_item)
->manager.highlighted_widget != NULL) )
{
new_item = ((XmManagerWidget) new_item)
->manager.highlighted_widget ;
}
_XmWidgetFocusChange( new_item, XmLEAVE) ;
}
if( (new_item == NULL)
|| !_XmMgrTraversal( new_item, XmTRAVERSE_CURRENT) )
{
_XmMgrTraversal( shell, XmTRAVERSE_CURRENT) ;
}
}
else /* new_focus_policy == XmPOINTER */
{
if( focus_data->focus_item )
{
Widget firstManaged = FindFirstManaged( shell) ;
_XmWidgetFocusChange( focus_data->focus_item, XmFOCUS_OUT) ;
_XmClearFocusPath( focus_data->focus_item) ;
_XmSetFocusFlag( shell, XmFOCUS_RESET, TRUE) ;
XtSetKeyboardFocus( shell, firstManaged) ;
_XmSetFocusFlag( shell, XmFOCUS_RESET, FALSE) ;
}
_XmFreeTravGraph( &(focus_data->trav_graph)) ;
}
}
}
XmFocusData
_XmGetFocusData (Widget wid)
{
/* This function returns a pointer to the focus data associated with the
* topmost shell. This allows us to treat the location opaquely.
*/
while (wid && !XtIsShell (wid))
{
wid = XtParent (wid);
}
if (wid && !(wid->core.being_destroyed))
{
if (XmIsVendorShell (wid))
{
XmWidgetExtData xwed = _XmGetWidgetExtData (wid, XmSHELL_EXTENSION);
XmVendorShellExtObject vse;
if (xwed == NULL)
return NULL;
vse = (XmVendorShellExtObject) xwed->widget;
if (vse && vse->vendor.focus_data)
{
vse->vendor.focus_data->focus_policy = vse->vendor.focus_policy;
return vse->vendor.focus_data;
}
}
else
{
if (XmIsMenuShell (wid)
&& ((XmMenuShellWidget) wid)->menu_shell.focus_data)
{
((XmMenuShellWidget) wid)->menu_shell.focus_data->
focus_policy =
((XmMenuShellWidget) wid)->menu_shell.focus_policy;
return ((XmMenuShellWidget) wid)->menu_shell.focus_data;
}
}
}
return NULL;
}
Boolean
_XmComputeVisibilityRect(Widget w,
XRectangle *rectPtr,
Boolean include_initial_border,
Boolean allow_scrolling)
{
/* This function will generate a rectangle describing the portion of
* the specified widget which is not clipped by any of its ancestors.
* It also verifies that the ancestors are both managed and
* mapped_when_managed.
*
* It will return TRUE if the rectangle returned in rectPtr has a
* non-zero area; it will return FALSE if the widget is not visible.
*
* If allow_scrolling is set and w is the work area child of an
* automatic scrolled window with a non-null XmNtraverseObscuredCallback,
* then the clip window is used as the initial rectangle for w.
*/
Widget sw;
if (!_XmIsViewable(w))
{
_XmClearRect(rectPtr);
return False;
}
if (allow_scrolling && w && XtParent(w) &&
((sw = _XmIsScrollableClipWidget(w, True, rectPtr)) != NULL))
{
w = sw;
if (!_XmIsViewable(w))
{
_XmClearRect(rectPtr);
return False;
}
}
else
{
_XmSetRect(rectPtr, w);
}
if (include_initial_border)
{
int border = XtBorderWidth(w);
rectPtr->x -= border;
rectPtr->y -= border;
rectPtr->width += 2 * border;
rectPtr->height += 2 * border;
}
/* Process all widgets, excluding the shell widget. */
while (((w = XtParent(w)) != NULL) && !XtIsShell(w))
{
if (!_XmIsViewable(w) || !_XmIntersectRect(rectPtr, w, rectPtr))
{
_XmClearRect(rectPtr);
return False;
}
}
return True;
}
Boolean
_XmGetPointVisibility(Widget w,
int root_x,
int root_y)
{
/* Compute whether a point is really visible inside a widget. */
XRectangle rect;
if (_XmComputeVisibilityRect(w, &rect, TRUE, FALSE))
{
return ((root_x >= rect.x) &&
(root_x < rect.x + (int)rect.width) &&
(root_y >= rect.y) &&
(root_y < rect.y + (int)rect.height));
}
return False;
}
void
_XmSetRect(
register XRectangle *rect,
Widget w )
{
/* Initialize the rectangle structure to the specified values.
* The widget must be realized.
*/
Position x, y;
XtTranslateCoords(XtParent(w), w->core.x, w->core.y, &x, &y);
rect->x = x + w->core.border_width;
rect->y = y + w->core.border_width;
rect->width = w->core.width;
rect->height = w->core.height;
}
int
_XmIntersectRect(
register XRectangle *srcRectA,
register Widget widget,
register XRectangle *dstRect )
{
/* Intersects the specified rectangle with the rectangle describing the
* passed-in widget. Returns True if they intersect, or False if they
* do not.
*/
XRectangle srcRectB;
_XmSetRect(&srcRectB, widget);
return( (int) _XmIntersectionOf( srcRectA, &srcRectB, dstRect)) ;
}
int
_XmEmptyRect(
register XRectangle *r )
{
if (r->width <= 0 || r->height <= 0)
return (TRUE);
return (FALSE);
}
void
_XmClearRect(
register XRectangle *r )
{
r->x = 0;
r->y = 0;
r->width = 0;
r->height = 0;
}
Boolean
_XmIsNavigable(
Widget wid)
{
XmNavigability nav = _XmGetNavigability( wid) ;
if( (nav != XmTAB_NAVIGABLE)
&& (nav != XmCONTROL_NAVIGABLE) )
{
return FALSE ;
}
while( (wid = XtParent( wid)) && !XtIsShell( wid) )
{
if( !_XmGetNavigability( wid) )
{
return FALSE ;
}
}
return TRUE ;
}
void
_XmWidgetFocusChange(
Widget wid,
XmFocusChange change)
{
XmBaseClassExt *er ;
if( XtIsRectObj( wid)
&& !wid->core.being_destroyed )
{
if( (er = _XmGetBaseClassExtPtr( XtClass( wid), XmQmotif))
&& (*er)
&& ((*er)->version >= XmBaseClassExtVersion)
&& (*er)->focusChange )
{
(*((*er)->focusChange))( wid, change) ;
}
else
{ /* From here on is compatibility code.
*/
WidgetClass wc ;
if( XmIsPrimitive( wid) )
{
wc = (WidgetClass) &xmPrimitiveClassRec ;
}
else if( XmIsGadget( wid) )
{
wc = (WidgetClass) &xmGadgetClassRec ;
}
else if( XmIsManager( wid) )
{
wc = (WidgetClass) &xmManagerClassRec ;
}
else
{
wc = NULL ;
}
if( wc
&& (er = _XmGetBaseClassExtPtr( wc, XmQmotif))
&& (*er)
&& ((*er)->version >= XmBaseClassExtVersion)
&& (*er)->focusChange )
{
(*((*er)->focusChange))( wid, change) ;
}
}
}
return ;
}
Widget
_XmNavigate(
Widget wid,
XmTraversalDirection direction )
{
XmTraversalDirection local_dir;
XmFocusData focus_data;
Widget nav_wid = NULL ;
Widget shell = _XmFindTopMostShell( wid) ;
if( (focus_data = _XmGetFocusData( shell))
&& (focus_data->focus_policy == XmEXPLICIT) )
{
XmTravGraph trav_list = &(focus_data->trav_graph) ;
nav_wid = _XmTraverse( trav_list, direction, &local_dir, wid) ;
nav_wid = RedirectTraversal(focus_data->focus_item, nav_wid,
focus_data->focus_policy, local_dir, 0);
if( trav_list->num_entries
&& (focus_data->focalPoint == XmUnrelated)
&& ( XmIsVendorShell( shell)
|| !XmeFocusIsInShell( shell)) )
{
_XmFreeTravGraph( trav_list) ;
}
}
return nav_wid ;
}
void
_XmSetInitialOfTabGroup(
Widget tab_group,
Widget init_focus)
{
XmFocusData focus_data ;
if( XmIsManager( tab_group) )
{
((XmManagerWidget) tab_group)->manager.initial_focus = init_focus ;
}
if( (focus_data = _XmGetFocusData( tab_group))
&& focus_data->trav_graph.num_entries )
{
_XmSetInitialOfTabGraph( &(focus_data->trav_graph),
tab_group, init_focus) ;
}
}
static Boolean
IsTraversable(
Widget wid,
Boolean require_in_view)
{
if( wid
&& _XmIsNavigable( wid) )
{
if( require_in_view )
{
return (XmGetVisibility( wid) != XmVISIBILITY_FULLY_OBSCURED) ;
}
else
{
/* _XmGetEffectiveView() returns the view port in
* which the widget could be viewed through the use
* of the XmNtraverseObscuredCallback of ScrolledWindow.
*/
XRectangle visRect ;
return _XmGetEffectiveView( wid, &visRect) ;
}
}
return FALSE ;
}
void
_XmResetTravGraph(
Widget wid)
{
XmFocusData focus_data = _XmGetFocusData( wid) ;
if( focus_data && focus_data->trav_graph.num_entries )
{
_XmFreeTravGraph( &(focus_data->trav_graph)) ;
}
}
Boolean
XmeFocusIsInShell(
Widget wid)
{
Window focus ;
Widget focus_wid ;
Widget shell_of_wid = _XmFindTopMostShell( wid) ;
XmFocusData focus_data ;
int revert ;
_XmWidgetToAppContext(wid);
_XmAppLock(app);
if( XmIsVendorShell( shell_of_wid)
&& (focus_data = _XmGetFocusData( shell_of_wid)) )
{
if( focus_data->focalPoint != XmUnrelated )
{
_XmAppUnlock(app);
return TRUE ;
}
}
else
{
XGetInputFocus( XtDisplay( shell_of_wid), &focus, &revert) ;
if( (focus != PointerRoot)
&& (focus != None)
&& (focus_wid = XtWindowToWidget( XtDisplay( shell_of_wid), focus))
&& (shell_of_wid == _XmFindTopMostShell( focus_wid)) )
{
_XmAppUnlock(app);
return TRUE ;
}
}
_XmAppUnlock(app);
return FALSE ;
}
Boolean
_XmShellIsExclusive(
Widget wid)
{
XmFocusData focusData = _XmGetFocusData( wid) ;
if( focusData
&& focusData->trav_graph.exclusive )
{
return TRUE ;
}
return FALSE ;
}
static Widget
FindFirstFocus(
Widget wid)
{
Widget shell = _XmFindTopMostShell( wid) ;
return _XmNavigate( shell, XmTRAVERSE_CURRENT) ;
}
Widget
_XmGetFirstFocus(
Widget wid)
{
XmFocusData focus_data = _XmGetFocusData( wid) ;
if( focus_data )
{
if( focus_data->focus_item )
{
return focus_data->focus_item ;
}
else
{
if( focus_data->first_focus == NULL )
{
focus_data->first_focus = FindFirstFocus( wid) ;
}
return focus_data->first_focus ;
}
}
return NULL ;
}
/*******************
* Public procedures
*******************/
Boolean
XmIsTraversable(
Widget wid)
{
Boolean traversable;
_XmWidgetToAppContext(wid);
_XmAppLock(app);
traversable = IsTraversable( wid, FALSE) ;
_XmAppUnlock(app);
return traversable;
}
XmVisibility
XmGetVisibility(
Widget wid)
{
XRectangle rect ;
Window rootwindow, parent_window, p_window, *children;
unsigned int numchildren;
int i;
Window *windowptr;
_XmWidgetToAppContext(wid);
_XmAppLock(app);
if( !wid
|| !_XmComputeVisibilityRect(wid, &rect, FALSE, TRUE) )
{
_XmAppUnlock(app);
return( XmVISIBILITY_FULLY_OBSCURED) ;
}
if( (rect.width != XtWidth( wid))
|| (rect.height != XtHeight( wid)) )
{
_XmAppUnlock(app);
return( XmVISIBILITY_PARTIALLY_OBSCURED) ;
}
/* Obscurity by siblings */
children = NULL;
if (!(parent_window = XtWindow(XtParent(wid))) ||
XQueryTree( XtDisplay(wid), parent_window, &rootwindow,
&p_window, &children, &numchildren ) == 0)
{
if (children) XFree(children);
_XmAppUnlock(app);
return( XmVISIBILITY_UNOBSCURED) ;
}
windowptr = children;
/* walk through those which are under the window of interest */
for ( i = 0; i < numchildren; i++ )
{
if ( *windowptr == XtWindow(wid))
{
break;
}
windowptr++;
}
i++;
windowptr++;
/* process windows above the window of interest */
if(i < numchildren) {
XRectangle parent_rect, srcRectB, intersect_rect;
XWindowAttributes window_attributes_return;
Region region = XCreateRegion();
Region tmp_region = XCreateRegion();
Region left_region = XCreateRegion();
XmVisibility value;
XUnionRectWithRegion(&rect, region, region);
while(i < numchildren) {
XGetWindowAttributes(XtDisplay(wid), *windowptr,
&window_attributes_return);
if(window_attributes_return.map_state == IsViewable) {
_XmSetRect(&parent_rect, XtParent(wid));
srcRectB.x = parent_rect.x + window_attributes_return.x
+ window_attributes_return.border_width;
srcRectB.y = parent_rect.y + window_attributes_return.y
+ window_attributes_return.border_width;
srcRectB.width = window_attributes_return.width;
srcRectB.height = window_attributes_return.height;
/* accumulate all the region covered by siblings */
if(_XmIntersectionOf( &rect, &srcRectB, &intersect_rect)) {
XUnionRectWithRegion(&intersect_rect, tmp_region, tmp_region);
}
}
i++;
windowptr++;
}
XSubtractRegion(region, tmp_region, left_region);
if(XEqualRegion(region, left_region)) {
value = XmVISIBILITY_UNOBSCURED;
} else if(XEmptyRegion(left_region)) {
value = XmVISIBILITY_FULLY_OBSCURED;
} else {
value = XmVISIBILITY_PARTIALLY_OBSCURED;
}
XDestroyRegion(region);
XDestroyRegion(tmp_region);
XDestroyRegion(left_region);
if (children) XFree(children);
_XmAppUnlock(app);
return (value);
}
XFree((Window*)children);
_XmAppUnlock(app);
return( XmVISIBILITY_UNOBSCURED) ;
}
Widget
XmGetTabGroup(
Widget wid)
{
XmFocusData focus_data ;
Boolean exclusive ;
_XmWidgetToAppContext(wid);
_XmAppLock(app);
if( !wid
|| (_XmGetFocusPolicy( wid) != XmEXPLICIT)
|| !(focus_data = _XmGetFocusData( wid)) )
{
_XmAppUnlock(app);
return( NULL) ;
}
exclusive = !!(focus_data->trav_graph.exclusive) ;
do
{
XmNavigationType nav_type = _XmGetNavigationType( wid) ;
if( (nav_type == XmSTICKY_TAB_GROUP)
|| (nav_type == XmEXCLUSIVE_TAB_GROUP)
|| ( (nav_type == XmTAB_GROUP)
&& !exclusive) )
{
_XmAppUnlock(app);
return( wid) ;
}
} while( (wid = XtParent( wid)) && !XtIsShell( wid) ) ;
_XmAppUnlock(app);
return wid ;
}
Widget
XmGetFocusWidget(
Widget wid)
{
Widget focus_wid = NULL ;
XmFocusData focus_data = _XmGetFocusData( wid) ;
_XmWidgetToAppContext(wid);
_XmAppLock(app);
if( focus_data != NULL )
{
if( focus_data->focus_policy == XmEXPLICIT )
{
focus_wid = focus_data->focus_item ;
}
else
{
focus_wid = focus_data->pointer_item ;
if( (focus_wid != NULL)
&& XmIsManager( focus_wid)
&& (((XmManagerWidget) focus_wid)
->manager.highlighted_widget != NULL) )
{
focus_wid = ((XmManagerWidget) focus_wid)
->manager.highlighted_widget ;
}
}
}
_XmAppUnlock(app);
return focus_wid ;
}
Boolean
XmProcessTraversal(
Widget w,
XmTraversalDirection dir)
{
XmFocusData focus_data ;
Boolean ret_val = FALSE;
if ( w )
{
_XmWidgetToAppContext(w);
_XmAppLock(app);
if( (focus_data = _XmGetFocusData( w))
&& (focus_data->focus_policy == XmEXPLICIT) )
{
if( dir != XmTRAVERSE_CURRENT )
{
if( focus_data->focus_item )
{
w = focus_data->focus_item ;
}
else
{
w = _XmFindTopMostShell( w) ;
}
}
ret_val = _XmMgrTraversal( w, dir) ;
}
_XmAppUnlock(app);
}
return ret_val;
}
void
XmAddTabGroup(
Widget tabGroup )
{
Arg arg;
XtSetArg(arg, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP);
XtSetValues(tabGroup, &arg, 1);
}
void
XmRemoveTabGroup(
Widget w )
{
Arg arg;
XtSetArg(arg, XmNnavigationType, XmNONE);
XtSetValues(w, &arg, 1);
}
/*
* Invoke the traversal redirection trait for all ancestors of both
* old_focus and new_focus. Repeat until concensus is achieved.
*/
static Widget
RedirectTraversal(Widget old_focus,
Widget new_focus,
unsigned int focus_policy,
XmTraversalDirection direction,
unsigned int pass)
{
XmTraversalControlTrait trav_trait;
Widget proposal = new_focus;
Widget parent;
/* Try not to get into an infinite loop. */
if (pass >= 255)
{
assert(FALSE);
return NULL;
}
/* Check ancestors of the old focus. */
for (parent = old_focus; parent != NULL; parent = XtParent(parent))
{
trav_trait = (XmTraversalControlTrait)
XmeTraitGet((XtPointer) XtClass(parent), XmQTtraversalControl);
if (trav_trait && trav_trait->redirect)
{
proposal = trav_trait->redirect(old_focus, new_focus,
focus_policy, direction, pass);
if (proposal != new_focus)
return RedirectTraversal(old_focus, proposal,
focus_policy, direction, pass + 1);
}
}
/* Check ancestors of the new focus. */
for (parent = new_focus; parent != NULL; parent = XtParent(parent))
{
trav_trait = (XmTraversalControlTrait)
XmeTraitGet((XtPointer) XtClass(parent), XmQTtraversalControl);
if (trav_trait && trav_trait->redirect)
{
proposal = trav_trait->redirect(old_focus, new_focus,
focus_policy, direction, pass);
if (proposal != new_focus)
return RedirectTraversal(old_focus, proposal,
focus_policy, direction, pass + 1);
}
}
/* Nobody changed our mind. */
return new_focus;
}