/* 
 * 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;
}
