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