1 /*
2  * Motif
3  *
4  * Copyright (c) 1987-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22 */
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 
27 
28 #ifdef REV_INFO
29 #ifndef lint
30 static char rcsid[] = "$TOG: Traversal.c /main/20 1999/08/17 11:52:26 vipin $"
31 #endif
32 #endif
33 
34 #include <Xm/GadgetP.h>
35 #include <Xm/ManagerP.h>
36 #include <Xm/MenuShellP.h>
37 #include <Xm/PrimitiveP.h>
38 #include <Xm/ScrolledWP.h>
39 #include <Xm/TraitP.h>
40 #include <Xm/TravConT.h>
41 #include <Xm/VendorSEP.h>
42 #include <Xm/VirtKeysP.h>
43 #include <Xm/DisplayP.h>
44 #include "BaseClassI.h"
45 #include "CallbackI.h"
46 #include "RepTypeI.h"
47 #include "TravActI.h"
48 #include "TraversalI.h"
49 
50 
51 /********    Static Function Declarations    ********/
52 
53 static Widget FindFirstManaged(
54                         Widget wid) ;
55 static Boolean CallTraverseObsured(
56                         Widget new_focus,
57                         XmTraversalDirection dir) ;
58 static Boolean IsTraversable(
59                         Widget wid,
60                         Boolean require_in_view) ;
61 static Widget FindFirstFocus(
62                         Widget wid) ;
63 static Boolean CallFocusMoved(Widget		   old,
64 			      Widget		   new_wid,
65 			      XEvent		   *event,
66 			      XmTraversalDirection direction);
67 static Widget RedirectTraversal(Widget		     old_focus,
68 				Widget		     new_focus,
69 				unsigned int	     focus_policy,
70 				XmTraversalDirection direction,
71 				unsigned int	     pass);
72 
73 /********    End Static Function Declarations    ********/
74 
75 
76 XmFocusData
_XmCreateFocusData(void)77 _XmCreateFocusData( void )
78 {
79   return (XmFocusData) XtCalloc(1, sizeof( XmFocusDataRec)) ;
80 }
81 
82 void
_XmDestroyFocusData(XmFocusData focusData)83 _XmDestroyFocusData(
84         XmFocusData focusData )
85 {
86   _XmFreeTravGraph( &(focusData->trav_graph)) ;
87   XtFree((char *) focusData->trav_graph.excl_tab_list) ;
88   XtFree((char *) focusData) ;
89 }
90 
91 void
_XmSetActiveTabGroup(XmFocusData focusData,Widget tabGroup)92 _XmSetActiveTabGroup(
93         XmFocusData focusData,
94         Widget tabGroup )
95 {
96     focusData->active_tab_group = tabGroup;
97 }
98 
99 Widget
_XmGetActiveItem(Widget w)100 _XmGetActiveItem(
101         Widget w )
102 {
103   return XmGetFocusWidget( w) ;
104 }
105 
106 /*ARGSUSED*/
107 void
_XmNavigInitialize(Widget request,Widget new_wid,ArgList args,Cardinal * num_args)108 _XmNavigInitialize(
109         Widget request,		/* unused */
110         Widget new_wid,
111         ArgList args,		/* unused */
112         Cardinal *num_args )	/* unused */
113 {
114   XmFocusData focusData ;
115 
116   if(    (focusData = _XmGetFocusData( new_wid)) != NULL    )
117     {
118       XmNavigationType navType = _XmGetNavigationType( new_wid) ;
119 
120       if(    navType == XmEXCLUSIVE_TAB_GROUP    )
121 	{
122 	  ++(focusData->trav_graph.exclusive) ;
123 	  _XmTabListAdd( &(focusData->trav_graph), new_wid) ;
124 	}
125       else
126 	{
127 	  if(    navType == XmSTICKY_TAB_GROUP    )
128 	    {
129 	      _XmTabListAdd( &(focusData->trav_graph), new_wid) ;
130 	    }
131 	}
132       if(    focusData->trav_graph.num_entries
133 	 &&  _XmGetNavigability( new_wid)    )
134 	{
135 	  /* If the graph exists, add the new navigable widget.
136 	   */
137 	  _XmTravGraphAdd( &(focusData->trav_graph), new_wid) ;
138 	}
139     }
140   /* If the traversal graph doesn't exist, do nothing, since the
141    * new widget will be picked-up when the graph is needed and created.
142    */
143   return ;
144 }
145 
146 /*ARGSUSED*/
147 Boolean
_XmNavigSetValues(Widget current,Widget request,Widget new_wid,ArgList args,Cardinal * num_args)148 _XmNavigSetValues(
149         Widget current,
150         Widget request,		/* unused */
151         Widget new_wid,
152         ArgList args,		/* unused */
153         Cardinal *num_args )	/* unused */
154 {
155   /* This routine is called from the SetValues method of Manager,
156    * Primitive, and Gadget to keep the traversal data structures
157    * up-to-date in regards to changes in the traversability of widgets.
158    *
159    * There are three purposes for this routine:
160    *
161    *   1:  Update the traversal graph in response to changes in
162    *       a widget's resources such that the widget is newly
163    *       eligible to receive the traversal focus.
164    *
165    *   2:  Update the focus data according to changes in
166    *       the Motif 1.0 "exclusive tab group" behavior.
167    *
168    *   3:  If the new widget of the SetValues call is the focus
169    *       widget and it becomes ineligible to have the focus,
170    *       then find an alternative to receive the focus (or
171    *       reset the focus for the hierarchy to the bootstrap
172    *       condition).
173    */
174 
175   XmFocusData focusData ;
176 
177   if(    (focusData = _XmGetFocusData( new_wid)) != NULL    )
178     {
179       XmTravGraph graph = &(focusData->trav_graph) ;
180       XmNavigationType newNavType = _XmGetNavigationType( new_wid) ;
181       XmNavigationType curNavType = _XmGetNavigationType( current) ;
182       Boolean ChangeInExclusive = FALSE ;
183 
184       if(    curNavType != newNavType    )
185 	{
186 	  if(    (curNavType == XmEXCLUSIVE_TAB_GROUP)
187 	     ||  (newNavType == XmEXCLUSIVE_TAB_GROUP)    )
188 	    {
189 	      /* This widget was "exclusive", now it is not (or vice-versa).
190 	       * Update the value of the focus data "exclusive" field.
191 	       */
192 	      ChangeInExclusive = TRUE ;
193 
194 	      if(    newNavType == XmEXCLUSIVE_TAB_GROUP    )
195 		{
196 		  ++(graph->exclusive) ;
197 		}
198 	      else
199 		{
200 		  --(graph->exclusive) ;
201 		}
202 	    }
203 	  if(    (newNavType == XmEXCLUSIVE_TAB_GROUP)
204 	     ||  (newNavType == XmSTICKY_TAB_GROUP)    )
205 	    {
206 	      if(    (curNavType != XmEXCLUSIVE_TAB_GROUP)
207 		 &&  (curNavType != XmSTICKY_TAB_GROUP)    )
208 		{
209 		  _XmTabListAdd( graph, new_wid) ;
210 		}
211 	    }
212 	  else
213 	    {
214 	      if(    (curNavType == XmEXCLUSIVE_TAB_GROUP)
215 		 ||  (curNavType == XmSTICKY_TAB_GROUP)    )
216 		{
217 		  _XmTabListDelete( graph, new_wid) ;
218 		}
219 	    }
220 	}
221       if(    XtIsRealized( new_wid)
222 	 &&  (focusData->focus_policy == XmEXPLICIT)    )
223 	{
224 	  if(    graph->num_entries    )
225 	    {
226 	      if(    ChangeInExclusive    )
227 		{
228 		  /* Since widget has changed to/from exlusive tab group
229 		   * behavior, need to re-make the traversal graph (as needed).
230 		   */
231 		  _XmFreeTravGraph( graph) ;
232 		}
233 	      else
234 		{
235 		  XmNavigability cur_nav = _XmGetNavigability( current) ;
236 		  XmNavigability new_nav = _XmGetNavigability( new_wid) ;
237 
238 		  if(    !cur_nav  &&  new_nav    )
239 		    {
240 		      /* Newly navigable widget; add it to the
241 		       * traversal graph.
242 		       */
243 		      _XmTravGraphAdd( graph, new_wid) ;
244 		    }
245 		  else
246 		    {
247 		      if(    cur_nav != new_nav    )
248 			{
249 			  /* Navigability changed; need to re-create the
250 			   * graph the next time it is needed.
251 			   */
252 			  _XmFreeTravGraph( graph) ;
253 			}
254 		    }
255 		}
256 	    }
257 	  if(    !(focusData->focus_item)    )
258 	    {
259 	      Widget shell ;
260 
261 	      /* CR 5417: Remove (focusData->focalPoint != XmMySelf) test. */
262 	      if(    XmIsTraversable( new_wid)
263 		 &&  (shell = _XmFindTopMostShell( new_wid))
264 		 &&  XmeFocusIsInShell( shell))
265 		{
266 		  /* Hierarchy currently has no focus, and this widget is
267 		   * now traversable, so bootstrap the focus for the hierarchy.
268 		   */
269 		  _XmMgrTraversal( shell, XmTRAVERSE_CURRENT) ;
270 		}
271 	    }
272 	  else
273 	    {
274 	      if(    (focusData->focus_item == new_wid)
275 		 &&  !IsTraversable( new_wid, TRUE)    )
276 		{
277 		  /* The new_wid now has the focus and is no longer
278 		   * traversable, so traverse away from it to the
279 		   * next traversable item.
280 		   */
281 		  Widget new_focus = _XmTraverseAway( graph, new_wid,
282                                     (focusData->active_tab_group != new_wid)) ;
283 		  if(    !new_focus    )
284 		    {
285 		      /* Could not find another widget eligible to take
286 		       * the focus, so use any widget to re-initialize/clear
287 		       * the focus in the widget hierarchy.
288 		       */
289 		      new_focus = new_wid ;
290 		    }
291 		  _XmMgrTraversal( new_focus, XmTRAVERSE_CURRENT) ;
292 
293 		  if(    !XtIsSensitive( new_wid)    )
294 		    {
295 		      /* Since widget has become insensitive, it did not
296 		       * receive the focus-out event.  Call the focus
297 		       * change method directly.
298 		       */
299 		      _XmWidgetFocusChange( new_wid, XmFOCUS_OUT) ;
300 		    }
301 		  return TRUE ;
302 		}
303 	    }
304 	}
305     }
306   return FALSE ;
307 }
308 
309 void
XmeNavigChangeManaged(Widget wid)310 XmeNavigChangeManaged(
311         Widget wid )
312 {
313   /* This routine must be called from the ChangeManaged method of
314    * all composite widgets that may have traversable children.
315    * This routine checks to see if the focus widget is traversable;
316    * if it is not, then an alternative traversable widget is found
317    * or the focus for the hierarchy is reset to the bootstrap condition.
318    *
319    * This routine also detects the condition for which there is no
320    * focus widget in the hierarchy and a newly managed widget is
321    * now eligible to have the focus; the focus is then initialized.
322    */
323   XmFocusData focus_data ;
324   _XmWidgetToAppContext(wid);
325 
326   _XmAppLock(app);
327   if(    XtIsRealized( wid)
328      &&  (focus_data = _XmGetFocusData( wid))
329      &&  (focus_data->focus_policy == XmEXPLICIT)    )
330     {
331       if(    focus_data->focus_item == NULL    )
332 	{
333 	  Widget firstManaged ;
334 
335 	  if(    XtIsShell( wid)    )
336 	    {
337 	      if(    focus_data->first_focus == NULL    )
338 		{
339 		  focus_data->first_focus = FindFirstFocus( wid) ;
340 		}
341 	      if(    (firstManaged = FindFirstManaged( wid)) != NULL    )
342 		{
343 		  /* Set bootstrap trigger for hierarchy that
344 		   * has no current focus.
345 		   */
346 		  XtSetKeyboardFocus( wid, firstManaged) ;
347 		}
348 	    }
349 	}
350       else
351 	{
352 	  /* If the focus widget is being destroyed, do nothing for now.
353 	   * We need to wait until _XmNavigDestroy is called to initiate
354 	   * the focus change; if we don't defer selection of the focus
355 	   * widget, the Intrinsics-generated focus-out event for the
356 	   * focus widget will go to the newly-selected focus widget
357 	   * (instead of the widget being destroyed, as intended).
358 	   */
359 	  if(    !(focus_data->focus_item->core.being_destroyed)
360 	     &&  !IsTraversable( focus_data->focus_item, TRUE)    )
361 	    {
362 	      Widget new_focus = _XmTraverseAway( &(focus_data->trav_graph),
363                                      focus_data->focus_item,
364 		                          (focus_data->active_tab_group
365 		                                  != focus_data->focus_item)) ;
366 	      if(    !new_focus    )
367 		{
368 		  new_focus = focus_data->focus_item ;
369 		}
370         if(new_focus)
371 	      _XmMgrTraversal( new_focus, XmTRAVERSE_CURRENT) ;
372 	    }
373 	}
374     }
375   _XmAppUnlock(app);
376   return ;
377 }
378 
379 static Widget
FindFirstManaged(Widget wid)380 FindFirstManaged(
381 	Widget wid)
382 {
383   if(    XtIsShell( wid)    )
384     {
385       unsigned i = 0 ;
386 
387       while(    i < ((CompositeWidget) wid)->composite.num_children    )
388 	{
389 	  if(    XtIsManaged( ((CompositeWidget) wid)
390 				                  ->composite.children[i])    )
391 	    {
392 	      return ((CompositeWidget) wid)->composite.children[i] ;
393 	    }
394 	  ++i ;
395 	}
396     }
397   return NULL ;
398 }
399 
400 void
_XmNavigResize(Widget wid)401 _XmNavigResize(
402 	Widget wid)
403 {
404   /* This routine must be called by all composites with (potentially)
405    * traversable children.  This is generally handled for all managers
406    * in the resize wrapper routines.
407    *
408    * This routine makes sure that the focus widget is always in view,
409    * either by invoking the XmNtraverseObscurredCallback mechansism
410    * of Scrolled Window or by finding an alternative focus widget.
411    */
412   XmFocusData focus_data ;
413 
414   if(    XtIsRealized( wid)  &&  !XtIsShell( wid)
415      &&  (focus_data = _XmGetFocusData( wid))    )
416     {
417       /* If the focus item is being destroyed, do nothing, since this
418        * will be handled more appropriately by _XmNavigDestroy().
419        */
420       if(    (focus_data->focus_policy == XmEXPLICIT)
421 	 &&  (    !(focus_data->focus_item)
422 	      ||  !((focus_data->focus_item)->core.being_destroyed))    )
423 	{
424 	  if(    !(focus_data->focus_item)    )
425 	    {
426 	      /* Hierarchy has no focus widget; re-initialize/clear the
427 	       * focus, but only if the parent is a managed shell (to
428 	       * avoid premature initialization during XtRealizeWidget).
429 	       */
430 	      Widget parent = XtParent( wid) ;
431 	      Widget firstManaged ;
432 
433 	      if(    parent && XtIsShell( parent)
434 		 &&  (firstManaged = FindFirstManaged( parent))    )
435 		{
436 		  /* Set bootstrap trigger for hierarchy that
437 		   * has no current focus.
438 		   */
439 		  XtSetKeyboardFocus( wid, firstManaged) ;
440 		}
441 	    }
442 	  else
443 	    {
444 	      if(    !IsTraversable( focus_data->focus_item, TRUE)    )
445 		{
446 		  /* Widget is not traversable, either because it is not
447 		   * viewable or some other reason.  Test again, this
448 		   * time allowing for obscured traversal.
449 		   *
450 		   * If it is not traversable regardless of the
451 		   * XmNtraverseObscuredCallback, or traversal to the
452 		   * obscured widget fails for some other reason, traverse
453 		   * away from the non-traversable widget.
454 		   */
455 		  if(    !IsTraversable( focus_data->focus_item, FALSE)
456 		     ||  !_XmMgrTraversal( focus_data->focus_item,
457 			                          XmTRAVERSE_CURRENT)    )
458 		    {
459 		      Widget new_focus = _XmTraverseAway(
460 			     &(focus_data->trav_graph), focus_data->focus_item,
461                                 (focus_data->active_tab_group
462 				                  != focus_data->focus_item)) ;
463 		      if(    !new_focus    )
464 			{
465 			  new_focus = focus_data->focus_item ;
466 			}
467             if(new_focus)
468 		      _XmMgrTraversal( new_focus, XmTRAVERSE_CURRENT) ;
469 		    }
470 		}
471 	    }
472 	}
473     }
474 }
475 
476 void
_XmValidateFocus(Widget wid)477 _XmValidateFocus(
478         Widget wid )
479 {
480   XmFocusData focus_data = _XmGetFocusData( wid) ;
481 
482   if(    focus_data
483      &&  (focus_data->focus_policy == XmEXPLICIT)
484      &&  (focus_data->focus_item != NULL)
485      &&  !IsTraversable( focus_data->focus_item, TRUE)    )
486     {
487       Widget new_focus = _XmTraverseAway( &(focus_data->trav_graph),
488 		                 focus_data->focus_item,
489                     (focus_data->active_tab_group != focus_data->focus_item)) ;
490       if(    !new_focus    )
491 	{
492 	  new_focus = wid ;
493 	}
494       _XmMgrTraversal( new_focus, XmTRAVERSE_CURRENT) ;
495     }
496 }
497 
498 void
_XmNavigDestroy(Widget wid)499 _XmNavigDestroy(
500         Widget wid )
501 {
502   /* This routine is used to keep the traversal data up-to-date with
503    * regards to widgets which are being destroyed.  It must be called
504    * by all composites that might have traversable children.  The
505    * DeleteChild method for Manager calls this routine, so its
506    * subclasses can explicitly chain to its superclasses DeleteChild
507    * method or call this routine directly.
508    *
509    * In addition to finding a new focus widget if it is being
510    * destroyed, this routine must make sure that there are no
511    * stale pointers to the widget being destroyed in any of its
512    * data structures.
513    */
514   XmFocusData focusData = _XmGetFocusData( wid) ;
515 
516   if(    focusData    )
517     {
518       XmTravGraph trav_list = &(focusData->trav_graph) ;
519       XmNavigationType navType = _XmGetNavigationType( wid) ;
520 
521       if(    wid == focusData->first_focus    )
522 	{
523 	  focusData->first_focus = NULL ;
524 	}
525       if(    navType == XmEXCLUSIVE_TAB_GROUP    )
526 	{
527 	  --(trav_list->exclusive) ;
528 	  _XmTabListDelete( trav_list, wid) ;
529 	}
530       else
531 	{
532 	  if(    navType == XmSTICKY_TAB_GROUP    )
533 	    {
534 	      _XmTabListDelete( trav_list, wid) ;
535 	    }
536 	}
537       if(    focusData->focus_item == wid    )
538 	{
539 	  /* The focus widget for this hierarhcy is being destroyed.
540 	   * Traverse away if in explicit mode, or just clear the
541 	   * focus item field.
542 	   */
543 	  Widget new_focus ;
544 
545 	  if(    (focusData->focus_policy != XmEXPLICIT)
546 	     ||  (    !(new_focus = _XmTraverseAway( trav_list,
547 				       focusData->focus_item,
548                                          (focusData->active_tab_group != wid)))
549 		  &&  !(new_focus = _XmFindTopMostShell( wid)))
550 	     ||  !_XmMgrTraversal( new_focus, XmTRAVERSE_CURRENT)    )
551 	    {
552 	      focusData->focus_item = NULL ;
553 	    }
554 	}
555       if(    focusData->trav_graph.num_entries    )
556 	{
557 	  _XmTravGraphRemove( trav_list, wid) ;
558 	}
559       if(    focusData->active_tab_group == wid    )
560 	{
561 	  focusData->active_tab_group = NULL ;
562 	}
563       if(    focusData->old_focus_item == wid    )
564 	{
565 	  focusData->old_focus_item = NULL ;
566 	}
567       if(    focusData->pointer_item == wid    )
568 	{
569 	  focusData->pointer_item = NULL ;
570 	}
571     }
572   return ;
573 }
574 
575 static Boolean
CallFocusMoved(Widget old,Widget new_wid,XEvent * event,XmTraversalDirection direction)576 CallFocusMoved(Widget		    old,
577 	       Widget		    new_wid,
578 	       XEvent		    *event,
579 	       XmTraversalDirection direction)
580 {
581   Widget w ;
582   Widget topShell ;
583   XtCallbackList callbacks ;
584   Boolean contin = TRUE ;
585 
586   if (old)
587     w = old;
588   else /* if (new_wid) -- if there's no w assignment we're in big trouble! */
589     w = new_wid;
590 
591   topShell 	= (Widget) _XmFindTopMostShell(w);
592 
593   /*
594    * make sure it's a shell that has a vendorExt object
595    */
596   if (XmIsVendorShell(topShell))
597     {
598       XmWidgetExtData		extData;
599       XmVendorShellExtObject	vendorExt;
600 
601       extData	= _XmGetWidgetExtData(topShell, XmSHELL_EXTENSION);
602       if(extData==NULL) return (contin);
603 
604       if ((vendorExt = (XmVendorShellExtObject) extData->widget) != NULL)
605 	{
606 	  if ((callbacks = vendorExt->vendor.focus_moved_callback) != NULL)
607 	    {
608 	      XmFocusMovedCallbackStruct	callData;
609 
610 	      callData.reason		= XmCR_FOCUS_MOVED;
611 	      callData.event		= event;
612 	      callData.cont		= True;
613 	      callData.old_focus	= old;
614 	      callData.new_focus	= new_wid;
615 	      callData.focus_policy	= vendorExt->vendor.focus_policy;
616 	      callData.direction	= direction;
617 
618 	      _XmCallCallbackList((Widget) vendorExt, callbacks,
619 				  (XtPointer) &callData);
620 	      contin = callData.cont ;
621 	    }
622 	}
623     }
624   return( contin) ;
625 }
626 
627 Boolean
_XmCallFocusMoved(Widget old,Widget new_wid,XEvent * event)628 _XmCallFocusMoved(
629         Widget old,
630         Widget new_wid,
631         XEvent *event )
632 {
633   return CallFocusMoved(old, new_wid, event, XmTRAVERSE_CURRENT);
634 }
635 
636 Boolean
_XmMgrTraversal(Widget wid,XmTraversalDirection direction)637 _XmMgrTraversal(
638         Widget wid,
639         XmTraversalDirection direction)
640 {
641   /* This routine is the workhorse for all traversal activities. */
642   Widget top_shell ;
643   Widget old_focus ;
644   Widget new_focus ;
645   Widget new_active_tab ;
646   XmFocusData focus_data;
647   XmTravGraph trav_list ;
648   Boolean rtnVal = FALSE ;
649   XmTraversalDirection local_dir;
650   XmDisplay dd = (XmDisplay)XmGetXmDisplay(XtDisplay(wid));
651 
652 #define traversal_in_progress \
653    ((XmDisplayInfo *)(dd->display.displayInfo))->traversal_in_progress
654 
655   if(    traversal_in_progress
656      ||  !(top_shell = _XmFindTopMostShell( wid))
657      ||  top_shell->core.being_destroyed
658      ||  !(focus_data = _XmGetFocusData( wid))
659      ||  (focus_data->focus_policy != XmEXPLICIT)    )
660     {
661       return FALSE ;
662     }
663   traversal_in_progress = TRUE ;
664 
665   /* Recursive traversal calls can sometimes be generated during
666    * the handling of focus events and associated callbacks.
667    * In this version of Motif, recursive calls always fail.
668    *
669    * Future enhancements could include the addition of a queue
670    * for recursive calls; these calls would then be serviced on
671    * a FIFO basis following the completion of the initial traversal
672    * processing.  Sequential FIFO processing is essential for
673    * providing a consistent and predicable environment for
674    * focus change callbacks and event processing.
675    */
676   trav_list = &(focus_data->trav_graph) ;
677   old_focus = focus_data->focus_item ;
678 
679   if(    (old_focus == NULL)
680      &&  (wid == top_shell)
681      &&  focus_data->first_focus
682      &&  IsTraversable( focus_data->first_focus, TRUE)    )
683     {
684       new_focus = focus_data->first_focus ;
685 
686       if (direction == XmTRAVERSE_GLOBALLY_FORWARD)
687 	local_dir = XmTRAVERSE_NEXT_TAB_GROUP;
688       else if (direction == XmTRAVERSE_GLOBALLY_BACKWARD)
689 	local_dir = XmTRAVERSE_PREV_TAB_GROUP;
690       else
691 	local_dir = direction;
692     }
693   else
694     {
695       new_focus = _XmTraverse(trav_list, direction, &local_dir, wid) ;
696     }
697 
698   new_focus = RedirectTraversal(old_focus, new_focus,
699 				focus_data->focus_policy, local_dir, 0);
700 
701   if(    new_focus
702      &&  (new_focus == old_focus)
703      &&  focus_data->old_focus_item    )
704     {
705       /* When traversal does not cause the focus to change
706        * to a different widget, focus-change events should
707        * not be generated.  The old_focus_item will be NULL
708        * when the focus is moving into this shell hierarchy
709        * from a different shell; in this case, focus-in
710        * events should be generated below.
711        */
712       rtnVal = TRUE ;
713     }
714   else if(    new_focus
715 	  &&  (new_active_tab = XmGetTabGroup( new_focus))
716 	  &&  CallFocusMoved(old_focus, new_focus, NULL, local_dir)
717 	  &&  CallTraverseObsured(new_focus, local_dir))
718     {
719       /* Set the keyboard focus in two steps; first to None, then
720        * to the new focus widget.  This will cause appropriate
721        * focus-in and focus-out events to be generated, even if
722        * the focus change is between two gadgets.
723        *
724        * Note that XtSetKeyboardFocus() generates focus change
725        * events "in-line", so focus data and manager active_child
726        * fields are not updated until after the focus-out events have
727        * been generated and dispatched to the current focus item.
728        *
729        * The FocusResetFlag is used to tell event actions procs to
730        * ignore any focus-in event that might be generated by the
731        * window manager (which won't like the fact that there the
732        * focus is now going to point to nobody).
733        */
734       _XmSetFocusFlag( top_shell, XmFOCUS_RESET, TRUE) ;
735       XtSetKeyboardFocus( top_shell, None) ;
736       _XmSetFocusFlag( top_shell, XmFOCUS_RESET, FALSE) ;
737 
738       _XmClearFocusPath( old_focus) ;
739 
740       focus_data->active_tab_group = new_active_tab ;
741 
742       if(    (new_active_tab != new_focus)
743 	 &&  XmIsManager( new_active_tab)    )
744 	{
745 	  XmManagerWidget manager = (XmManagerWidget) new_active_tab;
746 	  manager->manager.active_child = new_focus;
747 	}
748       if(    (new_active_tab != XtParent( new_focus))  /* Set above. */
749 	 &&  XmIsManager( XtParent( new_focus))    )
750 	{
751 	  XmManagerWidget manager = (XmManagerWidget) XtParent(new_focus);
752 	  manager->manager.active_child = new_focus ;
753 	}
754       focus_data->focus_item = new_focus ;
755       focus_data->old_focus_item = old_focus ? old_focus : new_focus ;
756 
757       /* Setting the focus data and manager active_child fields enables
758        * focus-in events to be propagated to the new focus widget.
759        */
760       XtSetKeyboardFocus( top_shell, new_focus) ;
761 
762       rtnVal = TRUE ;
763     }
764   else
765     {
766       /* Have failed to traverse to a new widget focus widget.
767        * If the current focus widget is no longer traversable,
768        * then reset focus data to its bootstrap state.
769        */
770       if(    !old_focus
771 	 ||  !IsTraversable( old_focus, TRUE)    )
772 	{
773 	  Widget firstManaged = FindFirstManaged( top_shell) ;
774 
775 	  _XmSetFocusFlag( top_shell, XmFOCUS_RESET, TRUE) ;
776 	  XtSetKeyboardFocus( top_shell, firstManaged) ;
777 	  _XmSetFocusFlag( top_shell, XmFOCUS_RESET, FALSE) ;
778 
779 	  _XmClearFocusPath( old_focus) ;
780 	  _XmFreeTravGraph( trav_list) ;
781 	}
782     }
783 
784   if(    trav_list->num_entries
785      &&  (focus_data->focalPoint == XmUnrelated)
786      &&  (    XmIsVendorShell( top_shell)
787 	 ||  !XmeFocusIsInShell( top_shell))    )
788     {
789       /* Free the graversal graph whenever the focus is out of this
790        * shell hierarchy, so memory use is limited to one traversal
791        * graph per display.  Since VendorShell has a handler which
792        * tracks the input focus, all we need to do is look at the
793        * focusData field.  For MenuShell and others, we need to go
794        * through the X server to find out where the focus is.
795        *
796        * Note the logic of the above conditional; VendorShell is the
797        * only shell class that maintains the focalPoint field of the
798        * focus data.  So, if its a VendorShell and focalPoint says
799        * "unrelated", we have the answer; any other shell and we need
800        * to call the generic focus test routine.
801        */
802       _XmFreeTravGraph( trav_list) ;
803     }
804   traversal_in_progress = FALSE ;
805   return rtnVal ;
806 }
807 
808 static Boolean
CallTraverseObsured(Widget new_focus,XmTraversalDirection dir)809 CallTraverseObsured(
810         Widget new_focus,
811         XmTraversalDirection dir)
812 {
813   Widget prev;
814   Widget ancestor = new_focus;
815   XRectangle focus_rect;	/* Area we're trying to make visible. */
816   XRectangle clip_rect;		/* Area a given ancestor obscures. */
817   XRectangle view_rect;		/* Temporary intersection of the two. */
818   XmTraverseObscuredCallbackStruct call_data;
819 
820   call_data.reason = XmCR_OBSCURED_TRAVERSAL;
821   call_data.event = NULL;
822   call_data.traversal_destination = new_focus;
823   call_data.direction = dir;
824 
825   _XmSetRect(&focus_rect, new_focus);
826 
827   /* Look for ancestors that clip this window. */
828   for (prev = ancestor;
829        ((ancestor = XtParent(ancestor)) != NULL) && !XtIsShell(ancestor);
830        prev = ancestor)
831     {
832       /* CR 9705: Special case overlapping work windows. */
833       if (!_XmIsScrollableClipWidget(ancestor, False, &clip_rect))
834 	_XmSetRect(&clip_rect, ancestor);
835 
836       if (!_XmIntersectionOf(&focus_rect, &clip_rect, &view_rect) ||
837 	  (view_rect.width != focus_rect.width) ||
838 	  (view_rect.height != focus_rect.height))
839 	{
840 	  /* This ancestor clips somebody. */
841 	  Widget sw = _XmIsScrollableClipWidget(prev, True, &focus_rect);
842 	  if (sw != NULL)
843 	    {
844 	      XtCallbackList callbacks = ((XmScrolledWindowWidget) sw)
845 		->swindow.traverseObscuredCallback;
846 	      XtCallCallbackList(sw, callbacks, (XtPointer) &call_data);
847 
848 	      ancestor = sw;
849 	    }
850 	  else
851 	    {
852 	      _XmIntersectRect(&focus_rect, ancestor, &focus_rect);
853 	    }
854 	}
855     }
856 
857   return IsTraversable( new_focus, TRUE);
858 }
859 
860 void
_XmClearFocusPath(Widget wid)861 _XmClearFocusPath(
862         Widget wid )
863 {
864   /* This routine should be called whenever the focus of a shell
865    * hierarchy needs to be reset to the bootstrap condition.
866    *
867    * This routine clears the active_child field of all manager
868    * widget ancestors of the widget argument, and clears other
869    * focus widget fields of the focus data record.  The clearing
870    * of the old_focus_item field indicates to the traversal code
871    * that the focus is not in this shell hierarchy.
872    */
873   XmFocusData focus_data ;
874 
875   while(    wid  &&  !XtIsShell( wid)    )
876     {
877       if(    XmIsManager( wid)    )
878 	{
879 	  ((XmManagerWidget) wid)->manager.active_child = NULL ;
880 	}
881       wid = XtParent( wid) ;
882     }
883   if(    (focus_data = _XmGetFocusData( wid)) != NULL    )
884     {
885       focus_data->focus_item = NULL ;
886       focus_data->old_focus_item = NULL ;
887       focus_data->active_tab_group = NULL ;
888     }
889 }
890 
891 Boolean
_XmFocusIsHere(Widget w)892 _XmFocusIsHere(
893         Widget w )
894 {
895     XmFocusData focus_data;
896     Widget	item;
897 
898     if ((focus_data = _XmGetFocusData( w)) &&
899 	(item = focus_data->focus_item))
900       {
901 	  for (; !XtIsShell(item); item = XtParent(item))
902 	    if (item == w)
903 	      return True;
904       }
905     return(False);
906 }
907 
908 unsigned char
_XmGetFocusPolicy(Widget w)909 _XmGetFocusPolicy(
910         Widget w )
911 {
912   Widget topmost_shell ;
913 
914   /* Find the topmost shell widget
915    */
916   topmost_shell = _XmFindTopMostShell( w) ;
917 
918   if(    XtIsVendorShell( topmost_shell)    )
919     {
920       XmWidgetExtData xwed = _XmGetWidgetExtData(topmost_shell, XmSHELL_EXTENSION);
921 
922       if(xwed == NULL) return(XmPOINTER);
923 
924       return (((XmVendorShellExtObject)
925 	       (xwed)->widget)
926 	      ->vendor.focus_policy) ;
927     }
928   else
929     {
930       if(    XmIsMenuShell( topmost_shell)    )
931 	{
932 	  return( ((XmMenuShellWidget) topmost_shell)->menu_shell.focus_policy);
933 	}
934     }
935   return( XmPOINTER) ;
936 }
937 
938 Widget
_XmFindTopMostShell(Widget w)939 _XmFindTopMostShell(
940         Widget w )
941 {
942   while(    w && !XtIsShell( w)    )
943     {
944       w = XtParent( w) ;
945     }
946   return( w) ;
947 }
948 
949 /*ARGSUSED*/
950 void
_XmFocusModelChanged(Widget wid,XtPointer client_data,XtPointer call_data)951 _XmFocusModelChanged(
952         Widget wid,
953         XtPointer client_data,	/* unused */
954         XtPointer call_data )
955 {
956   /* Invoked by the VendorShell widget, when the focus_policy changes.
957    * Registered as a callback by both the Manager and Primitive classes,
958    * when the parent is a VendorShell widget.
959    */
960   unsigned char new_focus_policy = (unsigned char)(unsigned long) call_data ;
961   Widget shell = _XmFindTopMostShell( wid) ;
962   XmFocusData focus_data = _XmGetFocusData( shell) ;
963 
964   if(    focus_data    )
965     {
966       if(    new_focus_policy == XmEXPLICIT    )
967 	{
968 	  Widget new_item = focus_data->pointer_item ;
969 
970 	  if(    new_item != NULL    )
971 	    {
972 	      if(    XmIsManager( new_item)
973 		 &&  (((XmManagerWidget) new_item)
974 		                     ->manager.highlighted_widget != NULL)    )
975 		{
976 		  new_item = ((XmManagerWidget) new_item)
977 		                                 ->manager.highlighted_widget ;
978 		}
979 	      _XmWidgetFocusChange( new_item, XmLEAVE) ;
980 	    }
981 	  if(    (new_item == NULL)
982 	     ||  !_XmMgrTraversal( new_item, XmTRAVERSE_CURRENT)    )
983 	    {
984 	      _XmMgrTraversal( shell, XmTRAVERSE_CURRENT) ;
985 	    }
986 	}
987       else /* new_focus_policy == XmPOINTER */
988 	{
989 	  if(    focus_data->focus_item    )
990 	    {
991 	      Widget firstManaged = FindFirstManaged( shell) ;
992 
993 	      _XmWidgetFocusChange( focus_data->focus_item, XmFOCUS_OUT) ;
994 
995 	      _XmClearFocusPath( focus_data->focus_item) ;
996 	      _XmSetFocusFlag( shell, XmFOCUS_RESET, TRUE) ;
997 	      XtSetKeyboardFocus( shell, firstManaged) ;
998 	      _XmSetFocusFlag( shell, XmFOCUS_RESET, FALSE) ;
999 	    }
1000 	  _XmFreeTravGraph( &(focus_data->trav_graph)) ;
1001 	}
1002     }
1003 }
1004 
1005 
1006 XmFocusData
_XmGetFocusData(Widget wid)1007 _XmGetFocusData (Widget wid)
1008 {
1009    /* This function returns a pointer to the focus data associated with the
1010     * topmost shell.  This allows us to treat the location opaquely.
1011     */
1012    while (wid && !XtIsShell (wid))
1013    {
1014       wid = XtParent (wid);
1015    }
1016    if (wid && !(wid->core.being_destroyed))
1017    {
1018       if (XmIsVendorShell (wid))
1019       {
1020          XmWidgetExtData xwed = _XmGetWidgetExtData (wid, XmSHELL_EXTENSION);
1021          XmVendorShellExtObject vse;
1022 
1023          if (xwed == NULL)
1024             return NULL;
1025          vse = (XmVendorShellExtObject) xwed->widget;
1026          if (vse && vse->vendor.focus_data)
1027          {
1028             vse->vendor.focus_data->focus_policy = vse->vendor.focus_policy;
1029             return vse->vendor.focus_data;
1030          }
1031       }
1032       else
1033       {
1034          if (XmIsMenuShell (wid)
1035              && ((XmMenuShellWidget) wid)->menu_shell.focus_data)
1036          {
1037             ((XmMenuShellWidget) wid)->menu_shell.focus_data->
1038                focus_policy =
1039                ((XmMenuShellWidget) wid)->menu_shell.focus_policy;
1040             return ((XmMenuShellWidget) wid)->menu_shell.focus_data;
1041          }
1042       }
1043    }
1044    return NULL;
1045 }
1046 
1047 
1048 Boolean
_XmComputeVisibilityRect(Widget w,XRectangle * rectPtr,Boolean include_initial_border,Boolean allow_scrolling)1049 _XmComputeVisibilityRect(Widget      w,
1050 			 XRectangle *rectPtr,
1051 			 Boolean     include_initial_border,
1052 			 Boolean     allow_scrolling)
1053 {
1054   /* This function will generate a rectangle describing the portion of
1055    * the specified widget which is not clipped by any of its ancestors.
1056    * It also verifies that the ancestors are both managed and
1057    * mapped_when_managed.
1058    *
1059    * It will return TRUE if the rectangle returned in rectPtr has a
1060    * non-zero area; it will return FALSE if the widget is not visible.
1061    *
1062    * If allow_scrolling is set and w is the work area child of an
1063    * automatic scrolled window with a non-null XmNtraverseObscuredCallback,
1064    * then the clip window is used as the initial rectangle for w.
1065    */
1066   Widget sw;
1067 
1068   if (!_XmIsViewable(w))
1069     {
1070       _XmClearRect(rectPtr);
1071       return False;
1072     }
1073 
1074   if (allow_scrolling && w && XtParent(w) &&
1075       ((sw = _XmIsScrollableClipWidget(w, True, rectPtr)) != NULL))
1076     {
1077       w = sw;
1078 
1079       if (!_XmIsViewable(w))
1080         {
1081 	  _XmClearRect(rectPtr);
1082 	  return False;
1083 	}
1084     }
1085   else
1086     {
1087       _XmSetRect(rectPtr, w);
1088     }
1089 
1090   if (include_initial_border)
1091     {
1092       int border = XtBorderWidth(w);
1093 
1094       rectPtr->x -= border;
1095       rectPtr->y -= border;
1096       rectPtr->width += 2 * border;
1097       rectPtr->height += 2 * border;
1098     }
1099 
1100   /* Process all widgets, excluding the shell widget. */
1101   while (((w = XtParent(w)) != NULL) && !XtIsShell(w))
1102     {
1103       if (!_XmIsViewable(w) || !_XmIntersectRect(rectPtr, w, rectPtr))
1104         {
1105 	  _XmClearRect(rectPtr);
1106 	  return False;
1107 	}
1108     }
1109 
1110   return True;
1111 }
1112 
1113 Boolean
_XmGetPointVisibility(Widget w,int root_x,int root_y)1114 _XmGetPointVisibility(Widget w,
1115 		      int    root_x,
1116 		      int    root_y)
1117 {
1118   /* Compute whether a point is really visible inside a widget. */
1119   XRectangle rect;
1120 
1121   if (_XmComputeVisibilityRect(w, &rect, TRUE, FALSE))
1122     {
1123       return ((root_x >= rect.x) &&
1124 	      (root_x <  rect.x + (int)rect.width) &&
1125 	      (root_y >= rect.y) &&
1126 	      (root_y <  rect.y + (int)rect.height));
1127     }
1128 
1129   return False;
1130 }
1131 
1132 void
_XmSetRect(register XRectangle * rect,Widget w)1133 _XmSetRect(
1134         register XRectangle *rect,
1135         Widget w )
1136 {
1137   /* Initialize the rectangle structure to the specified values.
1138    * The widget must be realized.
1139    */
1140    Position x, y;
1141 
1142    XtTranslateCoords(XtParent(w), w->core.x, w->core.y, &x, &y);
1143    rect->x = x + w->core.border_width;
1144    rect->y = y + w->core.border_width;
1145    rect->width = w->core.width;
1146    rect->height = w->core.height;
1147 }
1148 
1149 int
_XmIntersectRect(register XRectangle * srcRectA,register Widget widget,register XRectangle * dstRect)1150 _XmIntersectRect(
1151         register XRectangle *srcRectA,
1152         register Widget widget,
1153         register XRectangle *dstRect )
1154 {
1155   /* Intersects the specified rectangle with the rectangle describing the
1156    * passed-in widget.  Returns True if they intersect, or False if they
1157    * do not.
1158    */
1159   XRectangle srcRectB;
1160 
1161   _XmSetRect(&srcRectB, widget);
1162 
1163   return( (int) _XmIntersectionOf( srcRectA, &srcRectB, dstRect)) ;
1164 }
1165 
1166 int
_XmEmptyRect(register XRectangle * r)1167 _XmEmptyRect(
1168         register XRectangle *r )
1169 {
1170    if (r->width <= 0 || r->height <= 0)
1171       return (TRUE);
1172 
1173    return (FALSE);
1174 }
1175 
1176 void
_XmClearRect(register XRectangle * r)1177 _XmClearRect(
1178         register XRectangle *r )
1179 {
1180    r->x = 0;
1181    r->y = 0;
1182    r->width = 0;
1183    r->height = 0;
1184 }
1185 
1186 Boolean
_XmIsNavigable(Widget wid)1187 _XmIsNavigable(
1188 	Widget wid)
1189 {
1190   XmNavigability nav = _XmGetNavigability( wid) ;
1191   if(    (nav != XmTAB_NAVIGABLE)
1192      &&  (nav != XmCONTROL_NAVIGABLE)    )
1193     {
1194       return FALSE ;
1195     }
1196   while(    (wid = XtParent( wid)) && !XtIsShell( wid)    )
1197     {
1198       if(    !_XmGetNavigability( wid)    )
1199 	{
1200 	  return FALSE ;
1201 	}
1202     }
1203   return TRUE ;
1204 }
1205 
1206 void
_XmWidgetFocusChange(Widget wid,XmFocusChange change)1207 _XmWidgetFocusChange(
1208         Widget wid,
1209         XmFocusChange change)
1210 {
1211   XmBaseClassExt *er ;
1212 
1213   if(    XtIsRectObj( wid)
1214      && !wid->core.being_destroyed    )
1215     {
1216       if(    (er = _XmGetBaseClassExtPtr( XtClass( wid), XmQmotif))
1217 	 && (*er)
1218 	 && ((*er)->version >= XmBaseClassExtVersion)
1219 	 && (*er)->focusChange    )
1220         {
1221 	  (*((*er)->focusChange))( wid, change) ;
1222 	}
1223       else
1224         {   /* From here on is compatibility code.
1225 	     */
1226 	  WidgetClass wc ;
1227 
1228 	  if(    XmIsPrimitive( wid)    )
1229             {
1230 	      wc = (WidgetClass) &xmPrimitiveClassRec ;
1231 	    }
1232 	  else if(    XmIsGadget( wid)     )
1233 	    {
1234 	      wc = (WidgetClass) &xmGadgetClassRec ;
1235 	    }
1236 	  else if(    XmIsManager( wid)    )
1237 	    {
1238 	      wc = (WidgetClass) &xmManagerClassRec ;
1239 	    }
1240 	  else
1241 	    {
1242 	      wc = NULL ;
1243 	    }
1244 
1245 	  if(    wc
1246 	     && (er = _XmGetBaseClassExtPtr( wc, XmQmotif))
1247 	     && (*er)
1248 	     && ((*er)->version >= XmBaseClassExtVersion)
1249 	     && (*er)->focusChange    )
1250             {
1251 	      (*((*er)->focusChange))( wid, change) ;
1252 	    }
1253 	}
1254     }
1255   return ;
1256 }
1257 
1258 Widget
_XmNavigate(Widget wid,XmTraversalDirection direction)1259 _XmNavigate(
1260         Widget wid,
1261         XmTraversalDirection direction )
1262 {
1263   XmTraversalDirection local_dir;
1264   XmFocusData focus_data;
1265   Widget nav_wid = NULL ;
1266   Widget shell = _XmFindTopMostShell( wid) ;
1267 
1268   if(    (focus_data = _XmGetFocusData( shell))
1269      &&  (focus_data->focus_policy == XmEXPLICIT)    )
1270     {
1271       XmTravGraph trav_list = &(focus_data->trav_graph) ;
1272 
1273       nav_wid = _XmTraverse( trav_list, direction, &local_dir, wid) ;
1274 
1275       nav_wid = RedirectTraversal(focus_data->focus_item, nav_wid,
1276 				  focus_data->focus_policy, local_dir, 0);
1277 
1278       if(    trav_list->num_entries
1279 	 &&  (focus_data->focalPoint == XmUnrelated)
1280 	 &&  (    XmIsVendorShell( shell)
1281 	      ||  !XmeFocusIsInShell( shell))    )
1282 	{
1283 	  _XmFreeTravGraph( trav_list) ;
1284 	}
1285     }
1286   return nav_wid ;
1287 }
1288 
1289 void
_XmSetInitialOfTabGroup(Widget tab_group,Widget init_focus)1290 _XmSetInitialOfTabGroup(
1291 	Widget tab_group,
1292 	Widget init_focus)
1293 {
1294   XmFocusData focus_data ;
1295 
1296   if(    XmIsManager( tab_group)    )
1297     {
1298       ((XmManagerWidget) tab_group)->manager.initial_focus = init_focus ;
1299     }
1300   if(    (focus_data = _XmGetFocusData( tab_group))
1301      &&  focus_data->trav_graph.num_entries    )
1302     {
1303       _XmSetInitialOfTabGraph( &(focus_data->trav_graph),
1304 			      tab_group, init_focus) ;
1305     }
1306 }
1307 
1308 static Boolean
IsTraversable(Widget wid,Boolean require_in_view)1309 IsTraversable(
1310         Widget wid,
1311 	Boolean require_in_view)
1312 {
1313   if(    wid
1314      &&  _XmIsNavigable( wid)    )
1315     {
1316       if(    require_in_view    )
1317 	{
1318 	  return (XmGetVisibility( wid) != XmVISIBILITY_FULLY_OBSCURED) ;
1319 	}
1320       else
1321 	{
1322 	  /* _XmGetEffectiveView() returns the view port in
1323 	   * which the widget could be viewed through the use
1324 	   * of the XmNtraverseObscuredCallback of ScrolledWindow.
1325 	   */
1326 	  XRectangle visRect ;
1327 
1328 	  return _XmGetEffectiveView( wid, &visRect) ;
1329 	}
1330     }
1331   return FALSE ;
1332 }
1333 
1334 void
_XmResetTravGraph(Widget wid)1335 _XmResetTravGraph(
1336 	Widget wid)
1337 {
1338   XmFocusData focus_data = _XmGetFocusData( wid) ;
1339 
1340   if(    focus_data  &&  focus_data->trav_graph.num_entries    )
1341     {
1342       _XmFreeTravGraph( &(focus_data->trav_graph)) ;
1343     }
1344 }
1345 
1346 Boolean
XmeFocusIsInShell(Widget wid)1347 XmeFocusIsInShell(
1348         Widget wid)
1349 {
1350   Window focus ;
1351   Widget focus_wid ;
1352   Widget shell_of_wid = _XmFindTopMostShell( wid) ;
1353   XmFocusData focus_data ;
1354   int revert ;
1355   _XmWidgetToAppContext(wid);
1356 
1357   _XmAppLock(app);
1358   if(    XmIsVendorShell( shell_of_wid)
1359      &&  (focus_data = _XmGetFocusData( shell_of_wid))    )
1360     {
1361       if(    focus_data->focalPoint != XmUnrelated    )
1362 	{
1363 	  _XmAppUnlock(app);
1364 	  return TRUE ;
1365 	}
1366     }
1367   else
1368     {
1369       XGetInputFocus( XtDisplay( shell_of_wid), &focus, &revert) ;
1370 
1371       if(    (focus != PointerRoot)
1372 	 &&  (focus != None)
1373 	 &&  (focus_wid = XtWindowToWidget( XtDisplay( shell_of_wid), focus))
1374 	 &&  (shell_of_wid == _XmFindTopMostShell( focus_wid))    )
1375 	{
1376 	  _XmAppUnlock(app);
1377 	  return TRUE ;
1378 	}
1379     }
1380   _XmAppUnlock(app);
1381   return FALSE ;
1382 }
1383 
1384 Boolean
_XmShellIsExclusive(Widget wid)1385 _XmShellIsExclusive(
1386 	Widget wid)
1387 {
1388   XmFocusData focusData = _XmGetFocusData( wid) ;
1389 
1390   if(    focusData
1391      &&  focusData->trav_graph.exclusive    )
1392     {
1393       return TRUE ;
1394     }
1395   return FALSE ;
1396 }
1397 
1398 static Widget
FindFirstFocus(Widget wid)1399 FindFirstFocus(
1400 	Widget wid)
1401 {
1402   Widget shell = _XmFindTopMostShell( wid) ;
1403 
1404   return _XmNavigate( shell, XmTRAVERSE_CURRENT) ;
1405 }
1406 
1407 Widget
_XmGetFirstFocus(Widget wid)1408 _XmGetFirstFocus(
1409 	Widget wid)
1410 {
1411   XmFocusData focus_data = _XmGetFocusData( wid) ;
1412 
1413   if(    focus_data    )
1414     {
1415       if(    focus_data->focus_item    )
1416 	{
1417 	  return focus_data->focus_item ;
1418 	}
1419       else
1420 	{
1421 	  if(    focus_data->first_focus == NULL    )
1422 	    {
1423 	      focus_data->first_focus = FindFirstFocus( wid) ;
1424 	    }
1425           return focus_data->first_focus ;
1426 	}
1427     }
1428   return NULL ;
1429 }
1430 
1431 
1432 /*******************
1433  * Public procedures
1434  *******************/
1435 
1436 Boolean
XmIsTraversable(Widget wid)1437 XmIsTraversable(
1438         Widget wid)
1439 {
1440   Boolean traversable;
1441   _XmWidgetToAppContext(wid);
1442 
1443   _XmAppLock(app);
1444   traversable = IsTraversable( wid, FALSE) ;
1445   _XmAppUnlock(app);
1446   return traversable;
1447 }
1448 
1449 XmVisibility
XmGetVisibility(Widget wid)1450 XmGetVisibility(
1451         Widget wid)
1452 {
1453   XRectangle rect ;
1454   Window rootwindow, parent_window, p_window, *children;
1455   unsigned int numchildren;
1456   int i;
1457   Window *windowptr;
1458   _XmWidgetToAppContext(wid);
1459 
1460   _XmAppLock(app);
1461   if(    !wid
1462      || !_XmComputeVisibilityRect(wid, &rect, FALSE, TRUE)    )
1463     {
1464       _XmAppUnlock(app);
1465       return( XmVISIBILITY_FULLY_OBSCURED) ;
1466     }
1467   if(    (rect.width != XtWidth( wid))
1468      || (rect.height != XtHeight( wid))    )
1469     {
1470       _XmAppUnlock(app);
1471       return( XmVISIBILITY_PARTIALLY_OBSCURED) ;
1472     }
1473 
1474   /* Obscurity by siblings */
1475 
1476   children = NULL;
1477   if (!(parent_window = XtWindow(XtParent(wid))) ||
1478 	XQueryTree( XtDisplay(wid), parent_window, &rootwindow,
1479 		&p_window, &children, &numchildren ) == 0)
1480   {
1481         if (children) XFree(children);
1482         _XmAppUnlock(app);
1483         return( XmVISIBILITY_UNOBSCURED) ;
1484   }
1485 
1486   windowptr = children;
1487 
1488   /* walk through those which are under the window of interest */
1489   for ( i = 0; i < numchildren; i++ )
1490   {
1491         if ( *windowptr == XtWindow(wid))
1492         {
1493 	  break;
1494         }
1495         windowptr++;
1496   }
1497   i++;
1498   windowptr++;
1499 
1500   /* process windows above the window of interest */
1501   if(i < numchildren) {
1502       XRectangle parent_rect, srcRectB, intersect_rect;
1503       XWindowAttributes window_attributes_return;
1504       Region region = XCreateRegion();
1505       Region tmp_region = XCreateRegion();
1506       Region left_region = XCreateRegion();
1507       XmVisibility value;
1508 
1509       XUnionRectWithRegion(&rect, region, region);
1510       while(i < numchildren) {
1511         XGetWindowAttributes(XtDisplay(wid), *windowptr,
1512 				&window_attributes_return);
1513 
1514         if(window_attributes_return.map_state == IsViewable) {
1515           _XmSetRect(&parent_rect, XtParent(wid));
1516           srcRectB.x = parent_rect.x + window_attributes_return.x
1517 					+ window_attributes_return.border_width;
1518           srcRectB.y = parent_rect.y + window_attributes_return.y
1519 					+ window_attributes_return.border_width;
1520           srcRectB.width = window_attributes_return.width;
1521           srcRectB.height = window_attributes_return.height;
1522 
1523           /* accumulate all the region covered by siblings */
1524           if(_XmIntersectionOf( &rect, &srcRectB, &intersect_rect)) {
1525             XUnionRectWithRegion(&intersect_rect, tmp_region, tmp_region);
1526           }
1527         }
1528         i++;
1529         windowptr++;
1530      }
1531 
1532      XSubtractRegion(region, tmp_region, left_region);
1533      if(XEqualRegion(region, left_region)) {
1534 	value = XmVISIBILITY_UNOBSCURED;
1535      } else if(XEmptyRegion(left_region)) {
1536 	value = XmVISIBILITY_FULLY_OBSCURED;
1537      } else {
1538 	value = XmVISIBILITY_PARTIALLY_OBSCURED;
1539      }
1540      XDestroyRegion(region);
1541      XDestroyRegion(tmp_region);
1542      XDestroyRegion(left_region);
1543     if (children) XFree(children);
1544      _XmAppUnlock(app);
1545      return (value);
1546   }
1547   XFree((Window*)children);
1548 
1549   _XmAppUnlock(app);
1550   return( XmVISIBILITY_UNOBSCURED) ;
1551 }
1552 
1553 Widget
XmGetTabGroup(Widget wid)1554 XmGetTabGroup(
1555         Widget wid)
1556 {
1557   XmFocusData focus_data ;
1558   Boolean exclusive ;
1559   _XmWidgetToAppContext(wid);
1560 
1561   _XmAppLock(app);
1562   if(    !wid
1563      || (_XmGetFocusPolicy( wid) != XmEXPLICIT)
1564      || !(focus_data = _XmGetFocusData( wid))    )
1565     {
1566       _XmAppUnlock(app);
1567       return( NULL) ;
1568     }
1569   exclusive = !!(focus_data->trav_graph.exclusive) ;
1570 
1571   do
1572     {
1573       XmNavigationType nav_type = _XmGetNavigationType( wid) ;
1574 
1575       if(    (nav_type == XmSTICKY_TAB_GROUP)
1576 	 ||  (nav_type == XmEXCLUSIVE_TAB_GROUP)
1577 	 ||  (    (nav_type == XmTAB_GROUP)
1578 	      &&  !exclusive)    )
1579 	{
1580 	  _XmAppUnlock(app);
1581 	  return( wid) ;
1582 	}
1583     } while(    (wid = XtParent( wid)) && !XtIsShell( wid)    ) ;
1584 
1585   _XmAppUnlock(app);
1586   return wid ;
1587 }
1588 
1589 Widget
XmGetFocusWidget(Widget wid)1590 XmGetFocusWidget(
1591         Widget wid)
1592 {
1593   Widget focus_wid = NULL ;
1594   XmFocusData focus_data = _XmGetFocusData( wid) ;
1595   _XmWidgetToAppContext(wid);
1596 
1597   _XmAppLock(app);
1598   if(    focus_data != NULL    )
1599     {
1600       if(    focus_data->focus_policy == XmEXPLICIT    )
1601         {
1602 	  focus_wid = focus_data->focus_item ;
1603 	}
1604       else
1605         {
1606 	  focus_wid = focus_data->pointer_item ;
1607 
1608 	  if(    (focus_wid != NULL)
1609 	     &&  XmIsManager( focus_wid)
1610 	     &&  (((XmManagerWidget) focus_wid)
1611 		                     ->manager.highlighted_widget != NULL)    )
1612 	    {
1613 	      focus_wid = ((XmManagerWidget) focus_wid)
1614 		                                 ->manager.highlighted_widget ;
1615 	    }
1616 	}
1617     }
1618   _XmAppUnlock(app);
1619   return focus_wid ;
1620 }
1621 
1622 Boolean
XmProcessTraversal(Widget w,XmTraversalDirection dir)1623 XmProcessTraversal(
1624         Widget w,
1625         XmTraversalDirection dir)
1626 {
1627   XmFocusData focus_data ;
1628   Boolean ret_val = FALSE;
1629 
1630   if ( w )
1631   {
1632     _XmWidgetToAppContext(w);
1633 
1634     _XmAppLock(app);
1635     if(    (focus_data = _XmGetFocusData( w))
1636        &&  (focus_data->focus_policy == XmEXPLICIT)    )
1637       {
1638         if(    dir != XmTRAVERSE_CURRENT    )
1639           {
1640 	    if(    focus_data->focus_item    )
1641 	      {
1642 	        w = focus_data->focus_item ;
1643               }
1644 	    else
1645 	      {
1646 	        w = _XmFindTopMostShell( w) ;
1647 	      }
1648           }
1649         ret_val = _XmMgrTraversal( w, dir) ;
1650       }
1651     _XmAppUnlock(app);
1652   }
1653   return ret_val;
1654 }
1655 
1656 void
XmAddTabGroup(Widget tabGroup)1657 XmAddTabGroup(
1658         Widget tabGroup )
1659 {
1660     Arg		arg;
1661 
1662     XtSetArg(arg, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP);
1663     XtSetValues(tabGroup, &arg, 1);
1664 }
1665 
1666 void
XmRemoveTabGroup(Widget w)1667 XmRemoveTabGroup(
1668         Widget w )
1669 {
1670   Arg		arg;
1671 
1672   XtSetArg(arg, XmNnavigationType, XmNONE);
1673   XtSetValues(w, &arg, 1);
1674 }
1675 
1676 /*
1677  * Invoke the traversal redirection trait for all ancestors of both
1678  * old_focus and new_focus.  Repeat until concensus is achieved.
1679  */
1680 static Widget
RedirectTraversal(Widget old_focus,Widget new_focus,unsigned int focus_policy,XmTraversalDirection direction,unsigned int pass)1681 RedirectTraversal(Widget	       old_focus,
1682 		  Widget	       new_focus,
1683 		  unsigned int	       focus_policy,
1684 		  XmTraversalDirection direction,
1685 		  unsigned int	       pass)
1686 {
1687   XmTraversalControlTrait trav_trait;
1688   Widget proposal = new_focus;
1689   Widget parent;
1690 
1691   /* Try not to get into an infinite loop. */
1692   if (pass >= 255)
1693     {
1694       assert(FALSE);
1695       return NULL;
1696     }
1697 
1698   /* Check ancestors of the old focus. */
1699   for (parent = old_focus; parent != NULL; parent = XtParent(parent))
1700     {
1701       trav_trait = (XmTraversalControlTrait)
1702 	XmeTraitGet((XtPointer) XtClass(parent), XmQTtraversalControl);
1703 
1704       if (trav_trait && trav_trait->redirect)
1705 	{
1706 	  proposal = trav_trait->redirect(old_focus, new_focus,
1707 					  focus_policy, direction, pass);
1708 	  if (proposal != new_focus)
1709 	    return RedirectTraversal(old_focus, proposal,
1710 				     focus_policy, direction, pass + 1);
1711 	}
1712     }
1713 
1714   /* Check ancestors of the new focus. */
1715   for (parent = new_focus; parent != NULL; parent = XtParent(parent))
1716     {
1717       trav_trait = (XmTraversalControlTrait)
1718 	XmeTraitGet((XtPointer) XtClass(parent), XmQTtraversalControl);
1719 
1720       if (trav_trait && trav_trait->redirect)
1721 	{
1722 	  proposal = trav_trait->redirect(old_focus, new_focus,
1723 					  focus_policy, direction, pass);
1724 	  if (proposal != new_focus)
1725 	    return RedirectTraversal(old_focus, proposal,
1726 				     focus_policy, direction, pass + 1);
1727 	}
1728     }
1729 
1730   /* Nobody changed our mind. */
1731   return new_focus;
1732 }
1733