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: MenuUtil.c /main/16 1999/05/13 15:57:21 mgreess $"
31 #endif
32 #endif
33 
34 #include <stdio.h>
35 #include <ctype.h>
36 #include <X11/IntrinsicP.h>
37 #include <X11/ShellP.h>
38 #include <Xm/CascadeBGP.h>
39 #include <Xm/CascadeBP.h>
40 #include <Xm/MenuShellP.h>
41 #include <Xm/RowColumnP.h>
42 #include <Xm/ScreenP.h>
43 #include <Xm/XmosP.h>
44 #include "GadgetUtiI.h"
45 #include "MenuStateI.h"
46 #include "MenuUtilI.h"
47 #include "MessagesI.h"
48 #include "RCMenuI.h"
49 #include "TravActI.h"
50 #include "TraversalI.h"
51 #include "UniqueEvnI.h"
52 #include "XmI.h"
53 
54 #define GRABPTRERROR    _XmMMsgCascadeB_0003
55 #define GRABKBDERROR    _XmMMsgRowColText_0024
56 
57 #define EVENTS              ((unsigned int) (ButtonPressMask | \
58                               ButtonReleaseMask | EnterWindowMask | \
59                               LeaveWindowMask))
60 
61 /********    Static Function Declarations    ********/
62 
63 static void MenuTraverse(
64                         Widget w,
65                         XEvent *event,
66                         XmTraversalDirection direction) ;
67 static void GadgetCleanup(
68                         XmRowColumnWidget rc,
69                         XmGadget oldActiveChild) ;
70 static Boolean WrapRight(
71                         XmRowColumnWidget rc) ;
72 static Boolean WrapLeft(
73                         XmRowColumnWidget rc) ;
74 static void LocateChild(
75                         XmRowColumnWidget rc,
76                         Widget wid,
77                         XmTraversalDirection direction) ;
78 static void MoveDownInMenuBar(
79                         XmRowColumnWidget rc,
80                         Widget pw) ;
81 static void MoveLeftInMenuBar(
82                         XmRowColumnWidget rc,
83                         Widget pw) ;
84 static void MoveRightInMenuBar(
85                         XmRowColumnWidget rc,
86                         Widget pw) ;
87 static void FindNextMenuBarItem(
88                         XmRowColumnWidget menubar) ;
89 static void FindPrevMenuBarItem(
90                         XmRowColumnWidget menubar) ;
91 static Boolean ValidateMenuBarItem(
92                         Widget oldActiveChild,
93                         Widget newActiveChild) ;
94 static Boolean FindNextMenuBarCascade(
95                         XmRowColumnWidget menubar) ;
96 static Boolean FindPrevMenuBarCascade(
97                         XmRowColumnWidget menubar) ;
98 static Boolean ValidateMenuBarCascade(
99                         Widget oldActiveChild,
100                         Widget newMenuChild) ;
101 
102 /********    End Static Function Declarations    ********/
103 
104 
105 Boolean
_XmIsActiveTearOff(Widget widget)106 _XmIsActiveTearOff (
107        Widget widget)
108 {
109     XmRowColumnWidget menu = (XmRowColumnWidget) widget;
110 
111     if (RC_TearOffActive(menu))
112         return (True);
113     else
114         return (False);
115 }
116 
117 
118 /*
119  * Call XtGrabPointer with retry
120  */
121 int
_XmGrabPointer(Widget widget,Bool owner_events,unsigned int event_mask,int pointer_mode,int keyboard_mode,Window confine_to,Cursor cursor,Time time)122 _XmGrabPointer(
123 	Widget widget,
124 	Bool owner_events,
125 	unsigned int event_mask,
126 	int pointer_mode,
127 	int keyboard_mode,
128 	Window confine_to,
129 	Cursor cursor,
130 	Time time )
131 {
132    register int status = 0, retry;
133 
134    for (retry=0; retry < 5; retry++)
135    {
136       if ((status = XtGrabPointer(widget, owner_events, event_mask,
137          			  pointer_mode, keyboard_mode, confine_to,
138 				  cursor, time)) == GrabSuccess)
139 	 break;
140 
141       XmeMicroSleep(1000);
142    }
143    if (status != GrabSuccess)
144       XmeWarning((Widget) widget, GRABPTRERROR);
145 
146    return(status);
147 }
148 
149 /*
150  * Call XtGrabKeyboard with retry
151  */
152 int
_XmGrabKeyboard(Widget widget,Bool owner_events,int pointer_mode,int keyboard_mode,Time time)153 _XmGrabKeyboard(
154 	Widget widget,
155 	Bool owner_events,
156 	int pointer_mode,
157 	int keyboard_mode,
158 	Time time )
159 {
160    register int status = 0, retry;
161 
162    for (retry=0; retry < 5; retry++)
163    {
164       if ((status = XtGrabKeyboard(widget, owner_events,
165          pointer_mode, keyboard_mode, time)) == GrabSuccess)
166 	 break;
167       XmeMicroSleep(1000);
168    }
169    if (status != GrabSuccess)
170       XmeWarning(widget, GRABKBDERROR);
171 
172    return(status);
173 }
174 
175 
176 void
_XmMenuSetInPMMode(Widget wid,int flag)177 _XmMenuSetInPMMode (
178 	Widget wid,
179 #if NeedWidePrototypes
180 	int flag )
181 #else
182 	Boolean flag )
183 #endif /* NeedWidePrototypes */
184 {
185    _XmGetMenuState((Widget)wid)->MU_InPMMode = flag;
186 }
187 
188 /*
189  * This menuprocs procedure allows an external object to turn on and off menu
190  * traversal.
191  */
192 void
_XmSetMenuTraversal(Widget wid,int traversalOn)193 _XmSetMenuTraversal(
194         Widget wid,
195 #if NeedWidePrototypes
196         int traversalOn )
197 #else
198         Boolean traversalOn )
199 #endif /* NeedWidePrototypes */
200 {
201    if (traversalOn)
202    {
203       _XmSetInDragMode(wid, False);
204       if (!XmProcessTraversal(wid , XmTRAVERSE_CURRENT))
205          XtSetKeyboardFocus(XtParent(wid), wid);
206    }
207    else
208    {
209      _XmSetInDragMode(wid, True);
210      if(    XmIsMenuShell( XtParent( wid))    )
211        {
212 	 /* Must be careful not to trash the traversal environment
213 	  * for RowColumns which are not using menu-specific traversal.
214 	  */
215 	 _XmLeafPaneFocusOut(wid);
216        }
217    }
218 }
219 
220 
221 void
_XmLeafPaneFocusOut(Widget wid)222 _XmLeafPaneFocusOut(
223 	Widget wid )
224 {
225    XEvent fo_event;
226    Widget widget;
227    XmRowColumnWidget rc = (XmRowColumnWidget)wid;
228 
229    /* find the leaf pane */
230    while (RC_PopupPosted(rc))
231      rc = (XmRowColumnWidget)
232        ((XmMenuShellWidget)RC_PopupPosted(rc))->composite.children[0];
233 
234    fo_event.type = FocusOut;
235    fo_event.xfocus.send_event = True;
236    if ((widget = rc->manager.active_child) && XmIsCascadeButtonGadget(widget))
237    {
238       /* clear the internal focus path; also active_child = NULL which happens
239        * to make cascadebutton focus out work correctly.
240        */
241       _XmClearFocusPath((Widget)rc);
242       _XmDispatchGadgetInput(widget, NULL, XmFOCUS_OUT_EVENT);
243       ((XmGadget)widget)->gadget.have_traversal = False;
244    }
245    else
246    {
247       if (widget && XmIsPrimitive(widget) &&
248           (((XmPrimitiveWidgetClass)(widget->core.widget_class))->
249             primitive_class.border_unhighlight != (XtWidgetProc)NULL))
250          (*(((XmPrimitiveWidgetClass)(widget->core.widget_class))->
251             primitive_class.border_unhighlight))((Widget) widget);
252       else
253 	 _XmManagerFocusOut( (Widget) rc, &fo_event, NULL, NULL);
254 
255       /* clears the focus_item so that next TraverseToChild() will work */
256       _XmClearFocusPath((Widget)rc);
257    }
258 }
259 
260 /*ARGSUSED*/
261 void
_XmMenuHelp(Widget wid,XEvent * event,String * params,Cardinal * num_params)262 _XmMenuHelp(
263         Widget wid,
264         XEvent *event,
265         String *params,		/* unused */
266         Cardinal *num_params )	/* unused */
267 {
268    XmRowColumnWidget rc = (XmRowColumnWidget) wid;
269    XmGadget gadget;
270 
271    if (!_XmIsEventUnique(event) ||
272        (!RC_IsArmed(rc) && !((RC_Type(rc) == XmMENU_OPTION) ||
273 			     (RC_Type(rc) == XmMENU_PULLDOWN))))
274      return;
275 
276    if (!_XmGetInDragMode ((Widget)rc))
277    {
278      if ((gadget = (XmGadget) rc->manager.active_child) != NULL)
279 	_XmDispatchGadgetInput( (Widget) gadget, event, XmHELP_EVENT);
280      else
281      {
282 	_XmSocorro( (Widget) rc, event, NULL, NULL);
283 	_XmMenuPopDown((Widget)rc, event, NULL);
284      }
285    }
286    else
287    {
288      if ((gadget = (XmGadget)
289 	  XmObjectAtPoint((Widget) rc, event->xkey.x, event->xkey.y)) != NULL)
290         _XmDispatchGadgetInput( (Widget) gadget, event, XmHELP_EVENT);
291      else
292      {
293 	_XmSocorro( (Widget) rc, event, NULL, NULL);
294 	_XmMenuPopDown((Widget)rc, event, NULL);
295      }
296    }
297    _XmRecordEvent(event);
298 }
299 
300 static void
MenuTraverse(Widget w,XEvent * event,XmTraversalDirection direction)301 MenuTraverse(
302         Widget w,
303         XEvent *event,
304         XmTraversalDirection direction )
305 {
306    Widget parent;
307 
308    /*
309     * The case may occur where the reporting widget is in fact the
310     * RowColumn widget, and not a child.  This will occur if the
311     * RowColumn has not traversable children.
312     */
313    if (XmIsRowColumn(w))
314       parent = w;
315    else if (XmIsRowColumn(XtParent(w)))
316       parent = XtParent(w);
317    else
318       return;
319 
320    if ((RC_Type(parent) == XmMENU_POPUP) ||
321        (RC_Type(parent) == XmMENU_PULLDOWN) ||
322        (RC_Type(parent) == XmMENU_BAR))
323    {
324       _XmRecordEvent(event);
325       (*(((XmRowColumnWidgetClass)XtClass(parent))->row_column_class.
326           traversalHandler))( (Widget) parent, (Widget) w, direction);
327    }
328 }
329 
330 /* ARGSUSED */
331 void
_XmMenuTraverseLeft(Widget wid,XEvent * event,String * param,Cardinal * num_param)332 _XmMenuTraverseLeft(
333         Widget wid,
334         XEvent *event,
335         String *param,
336         Cardinal *num_param )
337 {
338     if (_XmIsEventUnique(event))
339    {
340 	 MenuTraverse(wid, event, XmTRAVERSE_LEFT);
341    }
342 }
343 
344 /* ARGSUSED */
345 void
_XmMenuTraverseRight(Widget wid,XEvent * event,String * param,Cardinal * num_param)346 _XmMenuTraverseRight(
347         Widget wid,
348         XEvent *event,
349         String *param,
350         Cardinal *num_param )
351 {
352    if (_XmIsEventUnique(event))
353    {
354 	 MenuTraverse(wid, event, XmTRAVERSE_RIGHT);
355    }
356 }
357 
358 /* ARGSUSED */
359 void
_XmMenuTraverseUp(Widget wid,XEvent * event,String * param,Cardinal * num_param)360 _XmMenuTraverseUp(
361         Widget wid,
362         XEvent *event,
363         String *param,
364         Cardinal *num_param )
365 {
366    if (_XmIsEventUnique(event))
367    {
368 	 MenuTraverse(wid, event, XmTRAVERSE_UP);
369    }
370 }
371 
372 /* ARGSUSED */
373 void
_XmMenuTraverseDown(Widget wid,XEvent * event,String * param,Cardinal * num_param)374 _XmMenuTraverseDown(
375         Widget wid,
376         XEvent *event,
377         String *param,
378         Cardinal *num_param )
379 {
380    if (_XmIsEventUnique(event))
381    {
382 	 MenuTraverse(wid, event, XmTRAVERSE_DOWN);
383    }
384 }
385 
386 /* ARGSUSED */
387 void
_XmMenuEscape(Widget w,XEvent * event,String * params,Cardinal * num_params)388 _XmMenuEscape(
389         Widget w,
390         XEvent *event,
391         String *params,
392         Cardinal *num_params )
393 {
394    Widget parent = XtParent(w);
395 
396    /* Process the event only if not already processed */
397    if (!_XmIsEventUnique(event))
398       return;
399 
400    /*
401     * Catch case where its a menubar w/ no submenus up - can't call
402     *   menushell's popdown, call rowcolumn's instead.
403     */
404    if ((XmIsCascadeButton(w) || XmIsCascadeButtonGadget(w)) &&
405 	XmIsRowColumn(parent) && (RC_Type(parent) == XmMENU_BAR) &&
406 	!RC_PopupPosted(parent))
407    {
408       (*(((XmRowColumnClassRec *)XtClass(parent))->row_column_class.
409 	 menuProcedures)) (XmMENU_POPDOWN, parent, NULL, event, NULL);
410    }
411    else
412        /* Let the menushell widget clean things up */
413        (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)->
414 	  menu_shell_class.popdownOne))(w, event, NULL, NULL);
415 }
416 
417 void
_XmRC_GadgetTraverseDown(Widget wid,XEvent * event,String * param,Cardinal * num_param)418 _XmRC_GadgetTraverseDown(
419         Widget wid,
420         XEvent *event,
421         String *param,
422         Cardinal *num_param )
423 {
424         XmRowColumnWidget rc = (XmRowColumnWidget) wid ;
425    XmGadget gadget = (XmGadget)rc->manager.active_child;
426 
427    if (gadget && XmIsGadget(gadget))
428       _XmMenuTraverseDown((Widget) gadget, event, param, num_param);
429 }
430 
431 void
_XmRC_GadgetTraverseUp(Widget wid,XEvent * event,String * param,Cardinal * num_param)432 _XmRC_GadgetTraverseUp(
433         Widget wid,
434         XEvent *event,
435         String *param,
436         Cardinal *num_param )
437 {
438         XmRowColumnWidget rc = (XmRowColumnWidget) wid ;
439    XmGadget gadget = (XmGadget)rc->manager.active_child;
440 
441    if (gadget && XmIsGadget(gadget))
442       _XmMenuTraverseUp((Widget) gadget, event, param, num_param);
443 }
444 
445 void
_XmRC_GadgetTraverseLeft(Widget wid,XEvent * event,String * param,Cardinal * num_param)446 _XmRC_GadgetTraverseLeft(
447         Widget wid,
448         XEvent *event,
449         String *param,
450         Cardinal *num_param )
451 {
452         XmRowColumnWidget rc = (XmRowColumnWidget) wid ;
453    XmGadget gadget = (XmGadget)rc->manager.active_child;
454 
455    /*
456     * If there is not active child, then this RowColumn has
457     * no traversable children, so it's fielding traversal
458     * requests itself.
459     */
460    if (gadget)
461       _XmMenuTraverseLeft((Widget) gadget, event, param, num_param);
462    else
463       _XmMenuTraverseLeft((Widget) rc, event, param, num_param);
464 }
465 
466 void
_XmRC_GadgetTraverseRight(Widget wid,XEvent * event,String * param,Cardinal * num_param)467 _XmRC_GadgetTraverseRight(
468         Widget wid,
469         XEvent *event,
470         String *param,
471         Cardinal *num_param )
472 {
473         XmRowColumnWidget rc = (XmRowColumnWidget) wid ;
474    XmGadget gadget = (XmGadget)rc->manager.active_child;
475 
476    /*
477     * If there is not active child, then this RowColumn has
478     * no traversable children, so it's fielding traversal
479     * requests itself.
480     */
481    if (gadget)
482       _XmMenuTraverseRight((Widget) gadget, event, param, num_param);
483    else
484       _XmMenuTraverseRight((Widget) rc, event, param, num_param);
485 }
486 
487 
488 /*
489  * In case we've moved into our out of a gadget, we need to take care
490  * of the highlighting ourselves, since the gadget will not get a focus
491  * event.
492  */
493 static void
GadgetCleanup(XmRowColumnWidget rc,XmGadget oldActiveChild)494 GadgetCleanup(
495         XmRowColumnWidget rc,
496         XmGadget oldActiveChild )
497 {
498     XmGadget newActiveChild = (XmGadget)rc->manager.active_child;
499 
500     if (oldActiveChild != newActiveChild)
501     {
502         if (oldActiveChild && XmIsGadget(oldActiveChild))
503         {
504             _XmDispatchGadgetInput( (Widget) oldActiveChild, NULL,
505                                                             XmFOCUS_OUT_EVENT);
506             oldActiveChild->gadget.have_traversal = False;
507         }
508     }
509 }
510 
511 
512 /*
513  * At the edge of the menu, decide what to do in this case
514  */
515 static Boolean
WrapRight(XmRowColumnWidget rc)516 WrapRight (
517         XmRowColumnWidget rc )
518 {
519    Widget topLevel;
520    Widget oldActiveChild = rc->manager.active_child;
521    Boolean done = False;
522 
523    _XmGetActiveTopLevelMenu ((Widget) rc, (Widget *) &topLevel);
524 
525    /* if in a menubar system, try to move to next menubar item cascade */
526    if (XmIsMenuShell(XtParent(rc)) && (RC_Type(topLevel) == XmMENU_BAR) &&
527        (FindNextMenuBarCascade((XmRowColumnWidget) topLevel)))
528    {
529       GadgetCleanup(rc, (XmGadget) oldActiveChild);
530       done = True;
531    }
532 
533    return (done);
534 }
535 
536 /*
537  * At the edge of the menu, decide what to do in this case
538  */
539 static Boolean
WrapLeft(XmRowColumnWidget rc)540 WrapLeft (
541         XmRowColumnWidget rc )
542 {
543    Widget oldActiveChild = rc->manager.active_child;
544    Boolean done = False;
545 
546    /*
547     * If we're the topmost pulldown menupane from a menubar, then unpost
548     * and move to the next available item in the menubar, and post its
549     * submenu.
550     */
551    if (XmIsMenuShell(XtParent(rc)) &&
552        (RC_Type (rc) != XmMENU_POPUP) && RC_CascadeBtn(rc) &&
553        (RC_Type (XtParent(RC_CascadeBtn(rc))) == XmMENU_BAR) &&
554        (FindPrevMenuBarCascade((XmRowColumnWidget)
555                                       XtParent(RC_CascadeBtn(rc)))))
556    {
557       GadgetCleanup(rc, (XmGadget) oldActiveChild);
558       done = True;
559    }
560 
561    /*
562     * if we are in a pulldown from another posted menupane, unpost this one
563     */
564    else if ((RC_Type(rc) == XmMENU_PULLDOWN) && RC_CascadeBtn(rc) &&
565             (RC_Type(XtParent(RC_CascadeBtn(rc))) != XmMENU_OPTION) &&
566             XmIsMenuShell(XtParent(rc)))
567    {
568       (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)->
569                   menu_shell_class.popdownOne)) (XtParent(rc), NULL, NULL,
570                                                  NULL);
571       done = True;
572    }
573 
574    return (done);
575 }
576 
577 /*
578  * Search for the next menu item according to the direction
579  */
580 static void
LocateChild(XmRowColumnWidget rc,Widget wid,XmTraversalDirection direction)581 LocateChild (
582         XmRowColumnWidget rc,
583         Widget wid,
584         XmTraversalDirection direction )
585 {
586    Boolean done = False;
587    Widget nextWidget;
588 
589    /* special case a popped up submenu with no traversable items */
590    if (XmIsRowColumn(wid) &&
591        ((XmManagerWidget) wid)->manager.active_child == 0)
592    {
593      if (direction == XmTRAVERSE_LEFT)
594        WrapLeft (rc);
595      else if (direction == XmTRAVERSE_RIGHT)
596        WrapRight (rc);
597    }
598    else
599    {
600      nextWidget = _XmNavigate(wid, direction);
601 
602      if (direction == XmTRAVERSE_LEFT)
603      {
604        /* watch for left wrap */
605        if ((wid->core.x <= nextWidget->core.x) ||
606 	   (nextWidget->core.y + nextWidget->core.height <= wid->core.y) ||
607 	   (nextWidget->core.y >= wid->core.y + wid->core.height))
608 	 done = WrapLeft(rc);
609      }
610      else if (direction == XmTRAVERSE_RIGHT)
611      {
612        /* watch for right wrap */
613        if ((wid->core.x >= nextWidget->core.x) ||
614 	   (wid->core.y + wid->core.height <= nextWidget->core.y) ||
615 	   (wid->core.y >= nextWidget->core.y + nextWidget->core.height))
616 	 done = WrapRight(rc);
617      }
618 
619      if (!done)
620        _XmMgrTraversal (nextWidget, XmTRAVERSE_CURRENT);
621    }
622 
623 }
624 
625  void
_XmMenuTraversalHandler(Widget w,Widget pw,XmTraversalDirection direction)626 _XmMenuTraversalHandler(
627         Widget w,
628         Widget pw,
629         XmTraversalDirection direction )
630 {
631    XmRowColumnWidget rc = (XmRowColumnWidget) w;
632 
633    if (_XmGetInDragMode((Widget) rc))
634       return;
635 
636    if ( LayoutIsRtoLM(rc) ) {
637      if (direction == XmTRAVERSE_RIGHT)
638        direction = XmTRAVERSE_LEFT;
639      else if (direction == XmTRAVERSE_LEFT)
640        direction = XmTRAVERSE_RIGHT;
641    }
642    if (RC_Type(rc) != XmMENU_BAR)
643    {
644       /* check for cascading into a submenu */
645       if (direction == XmTRAVERSE_RIGHT &&
646           XmIsCascadeButtonGadget(pw) && CBG_Submenu(pw))
647       {
648          (*(((XmGadgetClassRec *)XtClass(pw))->gadget_class.
649             arm_and_activate))( (Widget) pw, NULL, NULL, NULL);
650       }
651       else if (direction == XmTRAVERSE_RIGHT &&
652                XmIsCascadeButton(pw) && CB_Submenu(pw))
653       {
654          (*(((XmPrimitiveClassRec *)XtClass(pw))->primitive_class.
655              arm_and_activate)) ((Widget) pw, NULL, NULL, NULL);
656       }
657 
658       else
659          LocateChild (rc, pw, direction);
660    }
661 
662    else
663    {
664        switch (direction)
665        {
666           case XmTRAVERSE_DOWN:
667           {
668              MoveDownInMenuBar (rc, pw);
669              break;
670           }
671 
672           case XmTRAVERSE_LEFT:
673           {
674 	    MoveLeftInMenuBar(rc, pw);
675 	    break;
676           }
677 
678           case XmTRAVERSE_RIGHT:
679           {
680 	    MoveRightInMenuBar(rc, pw);
681 	    break;
682           }
683 
684           case XmTRAVERSE_UP:
685 	  default:
686 	     break;
687        }
688     }
689 }
690 
691 
692 /*
693  * When the PM menubar mode is active, down arrow will
694  * cause us to post the menupane associated with the active cascade button
695  * in the menubar.
696  */
697 static void
MoveDownInMenuBar(XmRowColumnWidget rc,Widget pw)698 MoveDownInMenuBar(
699         XmRowColumnWidget rc,
700         Widget pw )
701 {
702     if (rc->manager.active_child == NULL)
703         return;
704 
705     if (XmIsPrimitive(pw))
706     {
707         XmPrimitiveClassRec * prim;
708 
709 	CB_SetTraverse (pw, TRUE);
710         prim = (XmPrimitiveClassRec *)XtClass(pw);
711         (*(prim->primitive_class.arm_and_activate)) ((Widget) pw, NULL,
712 						     NULL, NULL);
713 	CB_SetTraverse (pw, FALSE);
714     }
715 
716     else if (XmIsGadget(pw))
717     {
718         XmGadgetClassRec * gad;
719 
720 	CBG_SetTraverse (pw, TRUE);
721         gad = (XmGadgetClassRec *)XtClass(pw);
722         (*(gad->gadget_class.arm_and_activate)) ((Widget) pw, NULL,
723 						 NULL, NULL);
724 	CBG_SetTraverse (pw, FALSE);
725     }
726 }
727 
728 /* ARGSUSED */
729 static void
MoveLeftInMenuBar(XmRowColumnWidget rc,Widget pw)730 MoveLeftInMenuBar(
731         XmRowColumnWidget rc,
732         Widget pw )
733 {
734    XmMenuState mst = _XmGetMenuState((Widget)rc);
735 
736    if ((mst->MU_CurrentMenuChild != NULL) &&
737        (RC_PopupPosted(rc) != NULL) &&
738        ((XmIsCascadeButtonGadget(pw) && !CBG_Submenu(pw)) ||
739        (XmIsCascadeButton(pw) && !CB_Submenu(pw))))
740    {
741       /* Move to the previous item in the menubar */
742       FindPrevMenuBarItem(rc);
743    }
744    else
745    {
746       /* Move to the previous item in the menubar */
747       mst->MU_CurrentMenuChild = NULL;
748       FindPrevMenuBarItem(rc);
749    }
750 }
751 
752 static void
MoveRightInMenuBar(XmRowColumnWidget rc,Widget pw)753 MoveRightInMenuBar(
754         XmRowColumnWidget rc,
755         Widget pw )
756 {
757    XmMenuState mst = _XmGetMenuState((Widget)rc);
758 
759    if ((rc->manager.active_child == NULL) &&
760         ((XmIsCascadeButtonGadget(pw) && !CBG_Submenu(pw)) ||
761         (XmIsCascadeButton(pw) && !CB_Submenu(pw))))
762    {
763       FindNextMenuBarCascade(rc);
764    }
765    else
766    {
767       /* Move to the next item in the menubar */
768       mst->MU_CurrentMenuChild = NULL;
769       FindNextMenuBarItem(rc);
770    }
771 }
772 
773 
774 /*
775  * Find the next cascade button in the menubar which can be traversed to.
776  */
777 static void
FindNextMenuBarItem(XmRowColumnWidget menubar)778 FindNextMenuBarItem(
779         XmRowColumnWidget menubar )
780 {
781    register int i, j;
782    int upper_limit;
783    Widget active_child;
784 
785    /*
786     * We're not in the PM menubar mode if we don't have an active child.
787     */
788    if (menubar->manager.active_child == NULL)
789        return;
790 
791    upper_limit = menubar->composite.num_children;
792    active_child = menubar->manager.active_child;
793 
794    /* Find the index of the currently active item */
795    for (i = 0; i < upper_limit; i++)
796    {
797       if (menubar->composite.children[i] == active_child)
798 	 break;
799    }
800 
801    /* Start looking at the next child */
802    for (j = 0, i++; j < upper_limit - 1; j++, i++)
803    {
804        /* Wrap, if necessary */
805        if (i >= upper_limit)
806 	  i = 0;
807 
808 	if (ValidateMenuBarItem(active_child, menubar->composite.children[i]))
809 	  return;
810    }
811 }
812 
813 
814 /*
815  * Find the previous cascade button in the menubar which can be traversed to.
816  */
817 static void
FindPrevMenuBarItem(XmRowColumnWidget menubar)818 FindPrevMenuBarItem(
819         XmRowColumnWidget menubar )
820 {
821    register int i, j;
822    int upper_limit;
823    Widget active_child;
824 
825    /* We're not in the PM menubar mode if we don't have an active child */
826    if (menubar->manager.active_child == NULL)
827        return;
828 
829    upper_limit = menubar->composite.num_children;
830    active_child = menubar->manager.active_child;
831 
832    /* Find the index of the currently active item */
833    for (i = 0; i < upper_limit; i++)
834    {
835        if (menubar->composite.children[i] == active_child)
836 	   break;
837    }
838 
839    /* Start looking at the previous child */
840    for (j = 0, --i; j < upper_limit - 1; j++, --i)
841    {
842        /* Wrap, if necessary */
843        if (i < 0)
844 	  i = upper_limit - 1;
845 
846        if (ValidateMenuBarItem(active_child, menubar->composite.children[i]))
847 	  return;
848    }
849 }
850 
851 static Boolean
ValidateMenuBarItem(Widget oldActiveChild,Widget newActiveChild)852 ValidateMenuBarItem (
853 	Widget oldActiveChild,
854         Widget newActiveChild)
855 {
856    XmMenuState mst = _XmGetMenuState((Widget)oldActiveChild);
857 
858    if (XmIsTraversable(newActiveChild))
859    {
860       (void) XmProcessTraversal (newActiveChild, XmTRAVERSE_CURRENT);
861 
862       if (XmIsPrimitive(newActiveChild))
863       {
864          XmPrimitiveClassRec * prim;
865 
866          prim = (XmPrimitiveClassRec *)XtClass(newActiveChild);
867 
868          if (!mst->MU_InPMMode && CB_Submenu(newActiveChild))
869             (*(prim->primitive_class.arm_and_activate)) (newActiveChild, NULL,
870                                                                    NULL, NULL);
871      }
872       else if (XmIsGadget(newActiveChild))
873       {
874          XmGadgetClassRec * gadget;
875 
876          gadget = (XmGadgetClassRec *)XtClass(newActiveChild);
877 
878          if (!mst->MU_InPMMode && CBG_Submenu(newActiveChild))
879             (*(gadget->gadget_class.arm_and_activate)) (newActiveChild, NULL,
880                                                                    NULL, NULL);
881       }
882       return True;
883    }
884    else
885       return False;
886 }
887 
888 /*
889  * Find the next hierarchy in the menubar which can be traversed to.
890  */
891 static Boolean
FindNextMenuBarCascade(XmRowColumnWidget menubar)892 FindNextMenuBarCascade(
893         XmRowColumnWidget menubar )
894 {
895    Widget active_child = NULL;
896    register int i, j;
897    int upper_limit;
898    ShellWidget shell;
899    XmMenuState mst = _XmGetMenuState((Widget)menubar);
900 
901    upper_limit = menubar->composite.num_children;
902 
903    /*
904     * Determine which child is popped up.
905     */
906    shell = (ShellWidget) RC_PopupPosted(menubar);
907    if (shell != NULL)
908       active_child = mst->MU_CurrentMenuChild =
909          RC_CascadeBtn(shell->composite.children[0]);
910 
911    /* Find the index of the currently active item */
912    for (i = 0; i < upper_limit; i++)
913    {
914       if (menubar->composite.children[i] == mst->MU_CurrentMenuChild)
915           break;
916    }
917 
918    /* Start looking at the next child */
919    for (j = 0, i++; j < upper_limit - 1; j++, i++)
920    {
921       /* Wrap, if necessary */
922       if (i >= upper_limit)
923           i = 0;
924 
925       mst->MU_CurrentMenuChild = menubar->composite.children[i];
926       if (ValidateMenuBarCascade(active_child, mst->MU_CurrentMenuChild))
927          return True;
928    }
929    return False;
930 }
931 
932 
933 /*
934  * Find the previous hierarchy in the menubar which can be traversed to.
935  */
936 static Boolean
FindPrevMenuBarCascade(XmRowColumnWidget menubar)937 FindPrevMenuBarCascade(
938         XmRowColumnWidget menubar )
939 {
940     Widget active_child = NULL;
941     register int i, j;
942     int upper_limit;
943     ShellWidget shell;
944     XmMenuState mst = _XmGetMenuState((Widget)menubar);
945 
946     upper_limit = menubar->composite.num_children;
947 
948     /* Determine which child is popped up */
949     shell = (ShellWidget) RC_PopupPosted(menubar);
950     if (shell != NULL)
951        active_child = mst->MU_CurrentMenuChild =
952           RC_CascadeBtn(shell->composite.children[0]);
953 
954     /* Find the index of the currently active item */
955     for (i = 0; i < upper_limit; i++)
956     {
957         if (menubar->composite.children[i] == mst->MU_CurrentMenuChild)
958            break;
959     }
960 
961     /* Start looking at the previous child */
962     for (j = 0, --i; j < upper_limit - 1; j++, --i)
963     {
964         /* Wrap, if necessary */
965         if (i < 0)
966            i = upper_limit - 1;
967 
968         mst->MU_CurrentMenuChild = menubar->composite.children[i];
969         if (ValidateMenuBarCascade(active_child, mst->MU_CurrentMenuChild))
970            return True;
971     }
972     return False;
973 }
974 
975 /*ARGSUSED*/
976 static Boolean
ValidateMenuBarCascade(Widget oldActiveChild,Widget newMenuChild)977 ValidateMenuBarCascade (Widget oldActiveChild, /* unused */
978 			Widget newMenuChild)
979 {
980    XmRowColumnWidget menubar = (XmRowColumnWidget)XtParent(newMenuChild);
981    Time _time = XtLastTimestampProcessed(XtDisplay(menubar));
982 
983    if (XmIsTraversable(newMenuChild))
984    {
985       if (XmIsCascadeButtonGadget(newMenuChild))
986       {
987          XmGadgetClassRec * gadget;
988 
989          gadget = (XmGadgetClassRec *)XtClass(newMenuChild);
990 
991          if (RC_PopupPosted(menubar) && !CBG_Submenu(newMenuChild))
992          {
993 	     (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)->
994 		menu_shell_class.popdownEveryone))
995 		 (RC_PopupPosted(menubar),NULL, NULL, NULL);
996 
997             /* Return the X focus to the Menubar hierarchy from the menushell.
998              * Set the Xt focus to the cascade
999              */
1000             _XmMenuFocus((Widget) menubar, XmMENU_MIDDLE, _time);
1001             (void)XmProcessTraversal(newMenuChild, XmTRAVERSE_CURRENT);
1002          }
1003          else
1004          {
1005             (*(gadget->gadget_class.arm_and_activate)) (newMenuChild, NULL,
1006                                                                    NULL, NULL);
1007          }
1008          return True;
1009       }
1010       else if (XmIsCascadeButton (newMenuChild))
1011       {
1012          XmPrimitiveClassRec * prim;
1013 
1014          prim = (XmPrimitiveClassRec *)XtClass(newMenuChild);
1015 
1016          /* No submenu means PM mode */
1017          if (RC_PopupPosted(menubar) && !CB_Submenu(newMenuChild))
1018          {
1019 	     (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)->
1020 		menu_shell_class.popdownEveryone))
1021 		 (RC_PopupPosted(menubar),NULL, NULL, NULL);
1022 
1023             /* Update X and Xt focus */
1024             _XmMenuFocus((Widget) menubar, XmMENU_MIDDLE, _time);
1025             (void)XmProcessTraversal(newMenuChild, XmTRAVERSE_CURRENT);
1026          }
1027          else
1028          {
1029             (*(prim->primitive_class.arm_and_activate)) (newMenuChild, NULL,
1030                                                                    NULL, NULL);
1031          }
1032          return True;
1033       }
1034    }
1035    return False;
1036 }
1037 
1038 
1039 /* (New) Grab strategy for menus requires grab before posting. If not possible
1040   * don't post the menu! This will ensure grabs active during a posted menu.
1041   * This will help consistency by preventing simultaneously posted menus.
1042   */
1043 int
_XmMenuGrabKeyboardAndPointer(Widget widget,Time time)1044 _XmMenuGrabKeyboardAndPointer(
1045       Widget widget,
1046       Time time )
1047 {
1048 
1049    register int status =
1050            (_XmGrabKeyboard(widget,
1051                             True,
1052                             GrabModeSync,
1053                             GrabModeAsync,
1054                             time) != GrabSuccess);
1055    if (status)
1056       return(status);
1057 
1058    status = _XmGrabPointer(widget, True, EVENTS, GrabModeSync,
1059        GrabModeAsync, None, XmGetMenuCursor(XtDisplay(widget)), time) !=
1060          GrabSuccess;
1061 
1062    if (status)
1063       XtUngrabKeyboard(widget, CurrentTime);
1064 
1065    return(status);
1066 }
1067