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 REV_INFO
24 #ifndef lint
25 static char rcsid[] = "$TOG: CascadeB.c /main/27 1999/08/11 14:26:35 mgreess $"
26 #endif
27 #endif
28
29 #ifdef HAVE_CONFIG_H
30 #include <config.h>
31 #endif
32
33
34 #include "XmI.h" /* ShellP.h doesn't define externalref. */
35 #include <X11/ShellP.h>
36 #include <X11/keysymdef.h>
37 #include <Xm/BaseClassP.h>
38 #include <Xm/DisplayP.h>
39 #include <Xm/DrawP.h>
40 #include <Xm/LabelGP.h>
41 #include <Xm/LabelP.h>
42 #include <Xm/MenuShellP.h>
43 #include <Xm/MenuStateP.h>
44 #include <Xm/MenuT.h>
45 #include <Xm/RowColumnP.h>
46 #include <Xm/TearOffP.h>
47 #include <Xm/TraitP.h>
48 #include <Xm/TransltnsP.h>
49 #include <Xm/VaSimpleP.h>
50 #include "CascadeBI.h"
51 #include "CascadeBGI.h"
52 #include "LabelI.h"
53 #include "GadgetUtiI.h"
54 #include "MenuStateI.h"
55 #include "MenuProcI.h"
56 #include "MenuUtilI.h"
57 #include "MessagesI.h"
58 #include "PrimitiveI.h"
59 #include "RCMenuI.h"
60 #include "TearOffI.h"
61 #include "TraversalI.h"
62 #include "UniqueEvnI.h"
63
64 #define FIX_1509
65
66 #define CASCADE_PIX_SPACE 4 /* pixels between label and bit map */
67 #define MAP_DELAY_DEFAULT 180
68 #define EVENTS ((unsigned int) (ButtonPressMask | \
69 ButtonReleaseMask | EnterWindowMask | \
70 LeaveWindowMask))
71
72
73 #define WRONGPARENT _XmMMsgCascadeB_0000
74 #define WRONGSUBMENU _XmMMsgCascadeB_0001
75 #define WRONGMAPDELAY _XmMMsgCascadeB_0002
76
77
78 /******** Static Function Declarations ********/
79
80 static void ClassInitialize( void ) ;
81 static void ClassPartInitialize(
82 WidgetClass wc) ;
83 static void BorderHighlight(
84 Widget wid) ;
85 static void BorderUnhighlight(
86 Widget wid) ;
87 static void DrawShadow(
88 register XmCascadeButtonWidget cb) ;
89 static void DrawCascade(
90 register XmCascadeButtonWidget cb) ;
91 static void Redisplay(
92 register Widget cb,
93 XEvent *event,
94 Region region) ;
95 static void Arm(
96 register XmCascadeButtonWidget cb) ;
97 static void ArmAndPost(
98 XmCascadeButtonWidget cb,
99 XEvent *event) ;
100 static void ArmAndActivate(
101 Widget wid,
102 XEvent *event,
103 String *params,
104 Cardinal *num_params) ;
105 static void Disarm(
106 register XmCascadeButtonWidget cb,
107 #if NeedWidePrototypes
108 int unpost) ;
109 #else
110 Boolean unpost) ;
111 #endif /* NeedWidePrototypes */
112 static void PostTimeout(
113 XtPointer closure,
114 XtIntervalId *id) ;
115 static void DelayedArm(
116 Widget wid,
117 XEvent *event,
118 String *param,
119 Cardinal *num_param) ;
120 static void CheckDisarm(
121 Widget wid,
122 XEvent *event,
123 String *param,
124 Cardinal *num_param) ;
125 static void StartDrag(
126 Widget wid,
127 XEvent *event,
128 String *param,
129 Cardinal *num_param) ;
130 static void Select(
131 register XmCascadeButtonWidget cb,
132 XEvent *event,
133 #if NeedWidePrototypes
134 int doCascade) ;
135 #else
136 Boolean doCascade) ;
137 #endif /* NeedWidePrototypes */
138 static void DoSelect(
139 Widget wid,
140 XEvent *event,
141 String *param,
142 Cardinal *num_param) ;
143 static void KeySelect(
144 Widget wid,
145 XEvent *event,
146 String *param,
147 Cardinal *num_param) ;
148 static void MenuBarSelect(
149 Widget wid,
150 XEvent *event,
151 String *param,
152 Cardinal *num_param) ;
153 static void MenuBarEnter(
154 Widget wid,
155 XEvent *event,
156 String *param,
157 Cardinal *num_param) ;
158 static void MenuBarLeave(
159 Widget wid,
160 XEvent *event,
161 String *param,
162 Cardinal *num_param) ;
163 static void CleanupMenuBar(
164 Widget wid,
165 XEvent *event,
166 String *param,
167 Cardinal *num_param) ;
168 static void PopdownGrandChildren(
169 XmRowColumnWidget rowcol) ;
170 static void Cascading(
171 Widget w,
172 XEvent *event) ;
173 static void Popup(
174 Widget cb,
175 XEvent *event) ;
176 static void size_cascade(
177 XmCascadeButtonWidget cascadebtn) ;
178 static void position_cascade(
179 XmCascadeButtonWidget cascadebtn) ;
180 static void setup_cascade(
181 XmCascadeButtonWidget cascadebtn,
182 #if NeedWidePrototypes
183 int adjustWidth,
184 int adjustHeight) ;
185 #else
186 Boolean adjustWidth,
187 Boolean adjustHeight) ;
188 #endif /* NeedWidePrototypes */
189 static void Destroy(
190 Widget wid) ;
191 static void Resize(
192 Widget cb) ;
193 static Boolean SetValuesPrehook(
194 Widget cw,
195 Widget rw,
196 Widget nw,
197 ArgList args,
198 Cardinal *num_args) ;
199 static Boolean SetValues(
200 Widget cw,
201 Widget rw,
202 Widget nw,
203 ArgList args,
204 Cardinal *num_args) ;
205 static void InitializePrehook(
206 Widget req,
207 Widget new_w,
208 ArgList args,
209 Cardinal *num_args) ;
210 static void InitializePosthook(
211 Widget req,
212 Widget new_w,
213 ArgList args,
214 Cardinal *num_args) ;
215 static void GetArmGC(
216 XmCascadeButtonWidget cb) ;
217 static void GetBackgroundGC(
218 XmCascadeButtonWidget cb) ;
219 static void Initialize(
220 Widget w_req,
221 Widget w_new,
222 ArgList args,
223 Cardinal *num_args) ;
224
225 /******** End Static Function Declarations ********/
226
227
228 /*
229 * event translation tables for cascadebutton. There are different
230 * ones for the different menus a cascadebutton widget can appear in
231 */
232
233 static XtTranslations menubar_events_parsed;
234
235 #define menubar_events _XmCascadeB_menubar_events
236
237 static XtTranslations p_events_parsed;
238
239 #define p_events _XmCascadeB_p_events
240
241 static XtActionsRec action_table [] =
242 {
243 {"DelayedArm", DelayedArm},
244 {"CheckDisarm", CheckDisarm},
245 {"StartDrag", StartDrag},
246 {"DoSelect", DoSelect},
247 {"KeySelect", KeySelect},
248 {"MenuBarSelect", MenuBarSelect},
249 {"MenuBarEnter", MenuBarEnter},
250 {"MenuBarLeave", MenuBarLeave},
251 {"MenuButtonTakeFocus", _XmMenuButtonTakeFocus },
252 {"MenuButtonTakeFocusUp", _XmMenuButtonTakeFocusUp },
253 {"CleanupMenuBar", CleanupMenuBar},
254 {"Help", _XmCBHelp},
255 };
256
257
258 static XtResource resources[] =
259 {
260 { XmNactivateCallback,
261 XmCCallback,
262 XmRCallback,
263 sizeof (XtCallbackList),
264 XtOffsetOf( struct _XmCascadeButtonRec, cascade_button.activate_callback),
265 XmRCallback,
266 NULL
267 },
268 { XmNcascadingCallback,
269 XmCCallback,
270 XmRCallback,
271 sizeof (XtCallbackList),
272 XtOffsetOf( struct _XmCascadeButtonRec, cascade_button.cascade_callback),
273 XmRCallback,
274 NULL
275 },
276 { XmNsubMenuId,
277 XmCMenuWidget, /* submenu */
278 XmRMenuWidget,
279 sizeof (Widget),
280 XtOffsetOf( struct _XmCascadeButtonRec, cascade_button.submenu),
281 XmRMenuWidget,
282 (XtPointer) NULL
283 },
284 { XmNcascadePixmap,
285 XmCPixmap,
286 XmRDynamicPixmap,
287 sizeof(Pixmap),
288 XtOffsetOf( struct _XmCascadeButtonRec, cascade_button.cascade_pixmap),
289 XmRImmediate,
290 (XtPointer) XmUNSPECIFIED_PIXMAP
291 },
292 { XmNmappingDelay,
293 XmCMappingDelay,
294 XmRInt,
295 sizeof (int),
296 XtOffsetOf( struct _XmCascadeButtonRec, cascade_button.map_delay),
297 XmRImmediate,
298 (XtPointer) MAP_DELAY_DEFAULT
299 },
300 {
301 XmNshadowThickness,
302 XmCShadowThickness,
303 XmRHorizontalDimension,
304 sizeof (Dimension),
305 XtOffsetOf( struct _XmCascadeButtonRec, primitive.shadow_thickness),
306 XmRCallProc,
307 (XtPointer) _XmSetThickness
308 },
309 {
310 XmNtraversalOn,
311 XmCTraversalOn,
312 XmRBoolean,
313 sizeof(Boolean),
314 XtOffsetOf( struct _XmPrimitiveRec, primitive.traversal_on),
315 XmRImmediate,
316 (XtPointer) True
317 },
318 {
319 XmNhighlightThickness,
320 XmCHighlightThickness,
321 XmRHorizontalDimension,
322 sizeof (Dimension),
323 XtOffsetOf( struct _XmPrimitiveRec, primitive.highlight_thickness),
324 XmRCallProc,
325 (XtPointer) _XmSetThickness
326 },
327 {
328 XmNmarginWidth,
329 XmCMarginWidth,
330 XmRHorizontalDimension,
331 sizeof (Dimension),
332 XtOffsetOf( struct _XmLabelRec, label.margin_width),
333 XmRImmediate,
334 (XtPointer) XmINVALID_DIMENSION
335 },
336 };
337
338 /*
339 * static initialization of the cascade button widget class record,
340 * must do each field
341 */
342 static XmBaseClassExtRec cascadeBBaseClassExtRec = {
343 NULL, /* Next extension */
344 NULLQUARK, /* record type XmQmotif */
345 XmBaseClassExtVersion, /* version */
346 sizeof(XmBaseClassExtRec), /* size */
347 InitializePrehook, /* initialize prehook */
348 SetValuesPrehook, /* set_values prehook */
349 InitializePosthook, /* initialize posthook */
350 XmInheritSetValuesPosthook, /* set_values posthook */
351 XmInheritClass, /* secondary class */
352 XmInheritSecObjectCreate, /* creation proc */
353 XmInheritGetSecResData, /* getSecResData */
354 {0}, /* fast subclass */
355 XmInheritGetValuesPrehook, /* get_values prehook */
356 XmInheritGetValuesPosthook, /* get_values posthook */
357 NULL, /* classPartInitPrehook */
358 NULL, /* classPartInitPosthook*/
359 NULL, /* ext_resources */
360 NULL, /* compiled_ext_resources*/
361 0, /* num_ext_resources */
362 FALSE, /* use_sub_resources */
363 XmInheritWidgetNavigable, /* widgetNavigable */
364 XmInheritFocusChange, /* focusChange */
365 };
366
367
368 externaldef(xmcascadebuttonclassrec) XmCascadeButtonClassRec xmCascadeButtonClassRec =
369 {
370 { /* core class record */
371 (WidgetClass) &xmLabelClassRec, /* superclass ptr */
372 "XmCascadeButton", /* class_name */
373 sizeof (XmCascadeButtonWidgetRec), /* size of Pulldown widget */
374 ClassInitialize, /* class init proc */
375 ClassPartInitialize, /* chained class init */
376 FALSE, /* class is not init'ed */
377 Initialize, /* widget init proc */
378 NULL, /* init_hook proc */
379 XtInheritRealize, /* widget realize proc */
380 action_table, /* class action table */
381 XtNumber (action_table),
382 resources, /* this class's resource list*/
383 XtNumber (resources), /* " " resource_count */
384 NULLQUARK, /* xrm_class */
385 TRUE, /* compress motion */
386 XtExposeCompressMaximal, /* compress exposure */
387 TRUE, /* compress enter-leave */
388 FALSE, /* no VisibilityNotify */
389 Destroy, /* class destroy proc */
390 Resize, /* class resize proc */
391 Redisplay, /* expose proc */
392 SetValues, /* set_value proc */
393 NULL, /* set_value_hook proc */
394 XtInheritSetValuesAlmost, /* set_value_almost proc */
395 NULL, /* get_values_hook */
396 NULL, /* class accept focus proc */
397 XtVersion, /* current version */
398 NULL, /* callback offset list */
399 NULL, /* default translation table */
400 /* this is manually set */
401 XtInheritQueryGeometry, /* query geo proc */
402 NULL, /* display accelerator*/
403 (XtPointer)&cascadeBBaseClassExtRec, /* extension */
404 },
405 {
406 /* Primitive Class record */
407 BorderHighlight, /* border_highlight */
408 BorderUnhighlight, /* border_uhighlight */
409 XtInheritTranslations, /* translations */
410 ArmAndActivate, /* arm & activate */
411 NULL, /* get resources */
412 0, /* num get_resources */
413 NULL, /* extension */
414 },
415 { /* Label Class record */
416 XmInheritWidgetProc, /* set override callback */
417 XmInheritMenuProc, /* menu procedures */
418 XtInheritTranslations, /* menu traversal xlation */
419 NULL, /* extension */
420 },
421 { /* cascade_button class record */
422 NULL, /* extension */
423 }
424 };
425
426 /*
427 * now make a public symbol that points to this class record
428 */
429
430 externaldef(xmcascadebuttonwidgetclass) WidgetClass xmCascadeButtonWidgetClass = (WidgetClass) &xmCascadeButtonClassRec;
431
432 /* Menu Savvy trait record */
433 static XmMenuSavvyTraitRec MenuSavvyRecord = {
434 /* version: */
435 -1,
436 NULL,
437 NULL,
438 NULL,
439 _XmCBNameActivate,
440 };
441
442
443 /*
444 * parse the translation tables for the different menutypes
445 */
446 static void
ClassInitialize(void)447 ClassInitialize( void )
448 {
449 menubar_events_parsed = XtParseTranslationTable (menubar_events);
450 p_events_parsed = XtParseTranslationTable (p_events);
451
452 /* set up base class extension quark */
453 cascadeBBaseClassExtRec.record_type = XmQmotif;
454 }
455
456 /*
457 * set up fast subclassing
458 */
459
460 static void
ClassPartInitialize(WidgetClass wc)461 ClassPartInitialize(
462 WidgetClass wc )
463 {
464 _XmFastSubclassInit (wc, XmCASCADE_BUTTON_BIT);
465
466 /* Install the menu savvy trait record, copying fields from XmLabel */
467 _XmLabelCloneMenuSavvy (wc, &MenuSavvyRecord);
468 }
469
470
471 /*
472 * The button is armed (does not pop up submenus).
473 */
474 static void
BorderHighlight(Widget wid)475 BorderHighlight(
476 Widget wid )
477 {
478 Arm ((XmCascadeButtonWidget) wid);
479 }
480
481
482 /*
483 * The button is disarmed (does not pop down submenus).
484 */
485 static void
BorderUnhighlight(Widget wid)486 BorderUnhighlight(
487 Widget wid )
488 {
489 XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid;
490 Boolean popdown;
491
492 if (Lab_IsMenupane(cb) &&
493 (((XmManagerWidget)XtParent(cb))->manager.active_child == wid) &&
494 CB_Submenu(cb))
495 {
496 XmMenuShellWidget mshell =
497 (XmMenuShellWidget) XtParent(CB_Submenu(cb));
498
499 if ((mshell->composite.children[0] == CB_Submenu(cb)) &&
500 (XmIsMenuShell(mshell)) &&
501 (mshell->shell.popped_up))
502 {
503 popdown = True;
504 }
505 else
506 popdown = False;
507 }
508 else
509 popdown = False;
510
511 Disarm ((XmCascadeButtonWidget) wid, popdown);
512 }
513
514
515 /*
516 * Draw the 3D shadow around the widget if its is armed.
517 */
518 static void
DrawShadow(register XmCascadeButtonWidget cb)519 DrawShadow(
520 register XmCascadeButtonWidget cb )
521 {
522 XmDisplay dpy = (XmDisplay) XmGetXmDisplay(XtDisplay((Widget) cb));
523 Boolean etched_in = dpy -> display.enable_etched_in_menu;
524
525 if (CB_IsArmed(cb))
526 {
527 if (XtIsRealized((Widget)cb))
528 {
529 XmeDrawShadows (XtDisplay (cb), XtWindow (cb),
530 cb->primitive.top_shadow_GC,
531 cb->primitive.bottom_shadow_GC,
532 cb->primitive.highlight_thickness,
533 cb->primitive.highlight_thickness,
534 cb->core.width - 2 *
535 cb->primitive.highlight_thickness,
536 cb->core.height - 2 *
537 cb->primitive.highlight_thickness,
538 cb->primitive.shadow_thickness,
539 etched_in ? XmSHADOW_IN : XmSHADOW_OUT);
540 }
541 }
542 }
543
544
545 static void
DrawCascade(register XmCascadeButtonWidget cb)546 DrawCascade(
547 register XmCascadeButtonWidget cb )
548 {
549 if ((CB_HasCascade(cb)) && (CB_Cascade_width(cb) != 0))
550 {
551 Pixmap pixmap ;
552 int depth ;
553
554 pixmap = CB_IsArmed(cb) &&
555 (CB_ArmedPixmap(cb) != XmUNSPECIFIED_PIXMAP) ?
556 CB_ArmedPixmap(cb) : CB_CascadePixmap(cb) ;
557
558 XmeGetPixmapData(XtScreen(cb),
559 pixmap,
560 NULL,
561 &depth,
562 NULL, NULL,
563 NULL, NULL,
564 NULL, NULL);
565
566 if (depth == cb->core.depth)
567 XCopyArea (XtDisplay(cb),
568 pixmap,
569 XtWindow(cb),
570 cb->label.normal_GC, 0, 0,
571 CB_Cascade_width(cb), CB_Cascade_height(cb),
572 CB_Cascade_x(cb), CB_Cascade_y(cb));
573 else
574 if (depth == 1)
575 XCopyPlane (XtDisplay(cb),
576 pixmap,
577 XtWindow(cb),
578 cb->label.normal_GC, 0, 0,
579 CB_Cascade_width(cb), CB_Cascade_height(cb),
580 CB_Cascade_x(cb), CB_Cascade_y(cb), 1);
581
582 }
583 }
584
585 /*
586 * redisplay the widget
587 */
588 static void
Redisplay(register Widget cb,XEvent * event,Region region)589 Redisplay(
590 register Widget cb,
591 XEvent *event,
592 Region region )
593 {
594 #ifdef FIX_1395
595 Pixel tmpc;
596 #endif
597 if (XtIsRealized (cb))
598 {
599 XmDisplay dpy = (XmDisplay) XmGetXmDisplay(XtDisplay(cb));
600 Boolean etched_in = dpy->display.enable_etched_in_menu;
601 GC tmpGC = NULL;
602 XtExposeProc expose;
603
604 if (etched_in) {
605 #ifdef FIX_1509
606 if (CB_IsArmed(cb))
607 XFillRectangle(XtDisplay(cb), XtWindow(cb), CB_ArmGC(cb),
608 0, 0, cb->core.width, cb->core.height);
609 else
610 XClearArea(XtDisplay(cb), XtWindow(cb),
611 0, 0, cb->core.width, cb->core.height, False);
612 #else
613 XFillRectangle(XtDisplay(cb), XtWindow(cb),
614 CB_IsArmed(cb) ? CB_ArmGC(cb) : CB_BackgroundGC(cb),
615 0, 0, cb->core.width, cb->core.height);
616 #endif
617 #ifdef USE_XFT
618 } else if (Lab_MenuType(cb) != XmWORK_AREA) { /* adeed with XFT support */
619 XClearArea(XtDisplay(cb), XtWindow(cb),
620 0, 0, cb->core.width, cb->core.height, False);
621 #endif
622 }
623 if (etched_in && CB_IsArmed(cb)) {
624 Pixel junk, select_pix;
625 Boolean replaceGC = False;
626
627 XmGetColors(XtScreen(cb), cb->core.colormap,
628 cb->core.background_pixel,
629 &junk, &junk, &junk, &select_pix);
630
631 if (select_pix ==
632 ((XmCascadeButtonWidget)cb)->primitive.foreground) {
633 replaceGC = True;
634 tmpGC = ((XmCascadeButtonWidget)cb)->label.normal_GC;
635 ((XmCascadeButtonWidget)cb)->label.normal_GC =
636 CB_BackgroundGC(cb);
637 }
638 #ifdef FIX_1395
639 /* 1395:
640 By default (if not etched and not armed) window (widget) have
641 background color that used to draw widget bg. When widget is
642 etched, background selected correctly (as selected color),
643 but label exposr method will use default background color
644 to fill area bellow lable text. Result is ugly menus.
645 We should replace colors before expose from label
646 and change it back after repainting.
647 */
648 tmpc = cb->core.background_pixel;
649 XSetWindowBackground(XtDisplay(cb), XtWindow(cb), select_pix);
650 #endif
651
652 _XmProcessLock();
653 expose = xmLabelClassRec.core_class.expose;
654 _XmProcessUnlock();
655 (*expose)((Widget) cb, event, region);
656 #ifdef FIX_1395
657 /*
658 Set correct window background (label is repainted, role back)
659 */
660 XSetWindowBackground(XtDisplay(cb), XtWindow(cb), tmpc);
661 if (cb->core.background_pixmap != XmUNSPECIFIED_PIXMAP)
662 XSetWindowBackgroundPixmap(XtDisplay(cb), XtWindow(cb),
663 cb->core.background_pixmap);
664 #endif
665
666 if (replaceGC)
667 ((XmCascadeButtonWidget)cb)->label.normal_GC = tmpGC;
668 }
669 else {
670
671 /* Label class does most of the work */
672
673 _XmProcessLock();
674 expose = xmLabelClassRec.core_class.expose;
675 _XmProcessUnlock();
676 (*expose)(cb, event, region);
677 }
678
679 DrawCascade((XmCascadeButtonWidget) cb);
680 DrawShadow ((XmCascadeButtonWidget) cb);
681 }
682 }
683
684
685 /*
686 * Arming the cascadebutton consists of setting the armed bit
687 * and drawing the 3D shadow.
688 */
689 static void
Arm(register XmCascadeButtonWidget cb)690 Arm(
691 register XmCascadeButtonWidget cb )
692 {
693 if (!CB_IsArmed(cb))
694 {
695 XmDisplay dpy = (XmDisplay) XmGetXmDisplay(XtDisplay(cb));
696 Boolean etched_in = dpy->display.enable_etched_in_menu;
697
698 CB_SetArmed(cb, TRUE);
699
700 if (etched_in)
701 Redisplay((Widget) cb, NULL, NULL);
702 else {
703 DrawCascade(cb);
704 DrawShadow (cb);
705 }
706 }
707
708 XmProcessTraversal((Widget) cb, XmTRAVERSE_CURRENT);
709 }
710
711
712
713 /*
714 * Post the submenu and then arm the button. The arming is done
715 * second so that the post can be quickly as possible.
716 */
717 static void
ArmAndPost(XmCascadeButtonWidget cb,XEvent * event)718 ArmAndPost(
719 XmCascadeButtonWidget cb,
720 XEvent *event )
721 {
722 if (!CB_IsArmed(cb))
723 {
724 _XmCascadingPopup ((Widget) cb, event, TRUE);
725 Arm (cb);
726 }
727 }
728
729
730
731
732 /*
733 * class function to cause the cascade button to be armed and selected
734 */
735 /*ARGSUSED*/
736 static void
ArmAndActivate(Widget wid,XEvent * event,String * params,Cardinal * num_params)737 ArmAndActivate(
738 Widget wid,
739 XEvent *event,
740 String *params, /* unused */
741 Cardinal *num_params ) /* unused */
742 {
743 XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid ;
744 XmRowColumnWidget parent = (XmRowColumnWidget) XtParent(cb);
745 XmMenuSystemTrait menuSTrait;
746 Time _time;
747
748 /* check if event has been processed - if it's NULL, override processing */
749 if (event && !_XmIsEventUnique(event))
750 return;
751
752 _time = _XmGetDefaultTime(wid, event);
753
754 menuSTrait = (XmMenuSystemTrait)
755 XmeTraitGet((XtPointer) XtClass(XtParent(wid)), XmQTmenuSystem);
756
757 if (menuSTrait == NULL) {
758 /* We're in trouble. This isn't a valid menu that we're
759 in and Arm and Activate has been called. Abort ! */
760 return;
761 }
762
763 switch (Lab_MenuType (cb))
764 {
765 case XmMENU_BAR:
766 {
767 ShellWidget myShell = NULL;
768
769 /* Shared menupanes require some additional checks */
770 if (CB_Submenu(cb))
771 myShell = (ShellWidget)XtParent(CB_Submenu(cb));
772
773 if (myShell &&
774 XmIsMenuShell(myShell) && /* not torn ?! */
775 (myShell->shell.popped_up) &&
776 (myShell->composite.children[0] == CB_Submenu(cb)) &&
777 (cb == (XmCascadeButtonWidget)RC_CascadeBtn(CB_Submenu(cb))))
778 {
779 menuSTrait -> popdown((Widget) parent, event);
780 Disarm (cb, FALSE);
781 }
782 else
783 {
784 /* call the cascading callbacks first thing */
785 Cascading ((Widget) cb, event);
786
787 /*
788 * check if the traversing flag is set true. This indicates
789 * that we are in a traverse and don't want to activate if
790 * there is no submenu attached. Set during KDown in menubar.
791 */
792 if (CB_Traversing(cb) && !CB_Submenu(cb))
793 return;
794
795 if (! RC_IsArmed (parent))
796 {
797 _XmMenuFocus((Widget) parent, XmMENU_BEGIN, _time);
798
799 menuSTrait -> arm((Widget) cb);
800 }
801 else
802 menuSTrait -> menuBarCleanup((Widget) parent);
803
804 /* do the select without calling the cascading callbacks again */
805 Select (cb, event, FALSE);
806
807 /* To support menu replay, keep the pointer in sync mode */
808 XAllowEvents(XtDisplay(cb), SyncPointer, CurrentTime);
809
810 if (CB_Submenu(cb))
811 {
812 /*
813 * if XmProcessTraversal() fails, it's possible that the pane
814 * has no traversable children, so reset the focus to the pane.
815 */
816 if (!XmProcessTraversal(CB_Submenu(cb), XmTRAVERSE_CURRENT))
817 {
818 /* Must clear focus path first for shared menushells.
819 * Otherwise, moving the focus back will have old stale
820 * (old) focus_item.
821 */
822 _XmClearFocusPath(CB_Submenu(cb));
823 XtSetKeyboardFocus(XtParent(CB_Submenu(cb)), CB_Submenu(cb));
824 }
825 }
826 else
827 {
828 menuSTrait -> disarm((Widget) parent);
829 _XmMenuFocus(XtParent(cb), XmMENU_END, _time);
830 XtUngrabPointer( (Widget) cb, _time);
831 }
832 }
833
834 break;
835 }
836
837 case XmMENU_PULLDOWN:
838 case XmMENU_POPUP:
839 {
840 /* In case the tear off is active but not armed or grabbed */
841 menuSTrait -> tearOffArm((Widget) parent);
842 Select (cb, event, TRUE);
843 if (CB_Submenu(cb))
844 {
845 /*
846 * if XmProcessTraversal() fails, it's possible that the pane
847 * has no traversable children, so reset the focus to the pane.
848 */
849 if (!XmProcessTraversal(CB_Submenu(cb), XmTRAVERSE_CURRENT))
850 {
851 /* Must clear focus path first for shared menushells.
852 * Otherwise, moving the focus back will have old stale
853 * (old) focus_item.
854 */
855 _XmClearFocusPath(CB_Submenu(cb));
856 XtSetKeyboardFocus(XtParent(CB_Submenu(cb)), CB_Submenu(cb));
857 }
858 }
859 break;
860 }
861 }
862 /* Record so spring loaded DispatchEvent() doesn't recall this routine. */
863 if (event)
864 _XmRecordEvent(event);
865 }
866
867
868 /*
869 * disarm the menu. This may include popping down any submenu that is up or
870 * removing the timeout to post a submenu
871 */
872 static void
Disarm(register XmCascadeButtonWidget cb,int unpost)873 Disarm(
874 register XmCascadeButtonWidget cb,
875 #if NeedWidePrototypes
876 int unpost )
877 #else
878 Boolean unpost )
879 #endif /* NeedWidePrototypes */
880 {
881 Widget rowcol = XtParent (cb);
882
883 if (CB_IsArmed(cb))
884 {
885 CB_SetArmed(cb, FALSE);
886
887 /* popdown any posted submenus */
888 if (unpost && RC_PopupPosted(rowcol))
889 {
890 (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)->
891 menu_shell_class.popdownEveryone))(RC_PopupPosted(rowcol),NULL,
892 NULL, NULL);
893 }
894
895 /* if a delayed arm is pending, remove it */
896 if (cb->cascade_button.timer)
897 {
898 XtRemoveTimeOut (cb->cascade_button.timer);
899 cb->cascade_button.timer = 0;
900 }
901
902 /* if the shadow is drawn and the menupane is not going down, erase it */
903 if ((! RC_PoppingDown(rowcol)) || RC_TornOff(rowcol))
904 {
905 if (XtIsRealized ((Widget)cb))
906 {
907 /* etched in menu button */
908 XmDisplay dpy = (XmDisplay) XmGetXmDisplay(XtDisplay(cb));
909 Boolean etched_in = dpy->display.enable_etched_in_menu;
910
911 if (etched_in)
912 Redisplay((Widget) cb, NULL, NULL);
913 else
914 XmeClearBorder (XtDisplay (cb), XtWindow (cb),
915 cb->primitive.highlight_thickness,
916 cb->primitive.highlight_thickness,
917 cb->core.width - 2 *
918 cb->primitive.highlight_thickness,
919 cb->core.height - 2 *
920 cb->primitive.highlight_thickness,
921 cb->primitive.shadow_thickness);
922 }
923 }
924 DrawCascade(cb);
925 }
926 }
927
928
929 /*
930 * called when the post delay timeout occurs.
931 */
932 /*ARGSUSED*/
933 static void
PostTimeout(XtPointer closure,XtIntervalId * id)934 PostTimeout(
935 XtPointer closure,
936 XtIntervalId *id) /* unused */
937 {
938 XmCascadeButtonWidget cb = (XmCascadeButtonWidget) closure ;
939
940 if (cb->cascade_button.timer)
941 {
942 cb->cascade_button.timer = 0;
943
944 _XmCascadingPopup ((Widget) cb, NULL, TRUE);
945 }
946 }
947
948
949 /*
950 * set the timer to post the submenu if a leave event does
951 * not occur first.
952 */
953 /*ARGSUSED*/
954 static void
DelayedArm(Widget wid,XEvent * event,String * param,Cardinal * num_param)955 DelayedArm(
956 Widget wid,
957 XEvent *event,
958 String *param,
959 Cardinal *num_param )
960 {
961 XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid ;
962 if ((! CB_IsArmed(cb)) &&
963 (((XmMenuShellWidget) XtParent(XtParent(cb)))->shell.popped_up) &&
964 _XmGetInDragMode((Widget) cb))
965 {
966 if (cb->cascade_button.map_delay <= 0)
967 ArmAndPost (cb, event);
968
969 else
970 {
971 /* To fix CR 8172, the following two lines were reversed.
972 Because calling Arm seems to cause a focus change (temporary)
973 out of the widget, the timer was incorrectly removed and
974 the menu wouldn't post. */
975 Arm(cb);
976 cb->cascade_button.timer =
977 XtAppAddTimeOut(XtWidgetToApplicationContext( (Widget) cb),
978 (unsigned long) cb->cascade_button.map_delay,
979 PostTimeout, (XtPointer) cb) ;
980 }
981 }
982 }
983
984
985 /*
986 * if traversal is not on and the mouse
987 * has not entered its cascading submenu, disarm the
988 * cascadebutton.
989 */
990 /*ARGSUSED*/
991 static void
CheckDisarm(Widget wid,XEvent * event,String * param,Cardinal * num_param)992 CheckDisarm(
993 Widget wid,
994 XEvent *event,
995 String *param,
996 Cardinal *num_param )
997 {
998 XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid ;
999 register XmMenuShellWidget submenushell;
1000 XEnterWindowEvent * entEvent = (XEnterWindowEvent *) event;
1001
1002 if (_XmGetInDragMode((Widget) cb) &&
1003 (/* !ActiveTearOff || */ event->xcrossing.mode == NotifyNormal))
1004 {
1005 if ((CB_IsArmed(cb)) &&
1006 (CB_Submenu(cb)))
1007 {
1008 submenushell = (XmMenuShellWidget) XtParent (CB_Submenu(cb));
1009
1010 if (submenushell->shell.popped_up)
1011 {
1012 if ((entEvent->x_root >= submenushell->core.x) &&
1013 (entEvent->x_root < submenushell->core.x +
1014 submenushell->core.width +
1015 (submenushell->core.border_width << 1)) &&
1016 (entEvent->y_root >= submenushell->core.y) &&
1017 (entEvent->y_root < submenushell->core.y +
1018 submenushell->core.height +
1019 (submenushell->core.border_width << 1)))
1020
1021 /* then we are in the cascading submenu, don't disarm */
1022 return;
1023
1024 /*
1025 * When kick-starting a cascading menu from a tear off, we grab
1026 * the pointer to the parent rc when the cascade has the focus
1027 * (In StartDrag(). A leave window event is generated (with
1028 * mode = NotifyGrab) which we don't wish to recognize.
1029 */
1030 if ((entEvent->mode == NotifyGrab) &&
1031 !XmIsMenuShell(XtParent(XtParent(cb))))
1032 return;
1033 }
1034 }
1035 Disarm (cb, TRUE);
1036 }
1037 }
1038
1039
1040 /*
1041 * post submenu and disable menu's traversal. The order of these
1042 * function calls is critical.
1043 */
1044 /*ARGSUSED*/
1045 static void
StartDrag(Widget wid,XEvent * event,String * param,Cardinal * num_param)1046 StartDrag(
1047 Widget wid,
1048 XEvent *event,
1049 String *param,
1050 Cardinal *num_param )
1051 {
1052 XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid ;
1053 Boolean validButton;
1054 XmRowColumnWidget parent = (XmRowColumnWidget)XtParent(cb);
1055 XmMenuSystemTrait menuSTrait;
1056
1057 menuSTrait = (XmMenuSystemTrait)
1058 XmeTraitGet((XtPointer) XtClass((Widget) parent), XmQTmenuSystem);
1059
1060 /* If no menu system trait then parent isn't a menu as it
1061 should be */
1062 if (menuSTrait == NULL) return;
1063
1064 /* Start with posted submenu bit reset */
1065 CB_SetWasPosted(cb, FALSE);
1066
1067 if (CB_Submenu(cb) &&
1068 RC_IsArmed ((XmRowColumnWidget) CB_Submenu(cb))) {
1069 CB_SetWasPosted(cb, TRUE);
1070 }
1071
1072 /*
1073 * make sure the shell is popped up, this takes care of a corner case
1074 * that can occur with rapid pressing of the mouse button
1075 */
1076 if (Lab_IsMenupane(cb) &&
1077 (!((XmMenuShellWidget) XtParent(parent))->shell.popped_up))
1078 {
1079 /* To support menu replay, keep the pointer in sync mode */
1080 XAllowEvents(XtDisplay(cb), SyncPointer, CurrentTime);
1081
1082 return;
1083 }
1084
1085 validButton = menuSTrait -> verifyButton((Widget) parent, event);
1086
1087 if (validButton)
1088 {
1089 /* In case the tear off is active but not armed or grabbed */
1090 menuSTrait -> tearOffArm((Widget) parent);
1091
1092 _XmSetInDragMode((Widget) cb, True);
1093
1094 _XmCascadingPopup ((Widget) cb, event, TRUE);
1095 Arm (cb);
1096
1097 /* record event so MenuShell does not process it */
1098 _XmRecordEvent (event);
1099 }
1100
1101 /* To support menu replay, keep the pointer in sync mode */
1102 XAllowEvents(XtDisplay(cb), SyncPointer, CurrentTime);
1103 }
1104
1105
1106 /*
1107 * do the popup (either w/ or w/o the cascade callbacks).
1108 * If there is not a submenu, bring down the menu system.
1109 */
1110 static void
Select(register XmCascadeButtonWidget cb,XEvent * event,int doCascade)1111 Select(
1112 register XmCascadeButtonWidget cb,
1113 XEvent *event,
1114 #if NeedWidePrototypes
1115 int doCascade )
1116 #else
1117 Boolean doCascade )
1118 #endif /* NeedWidePrototypes */
1119 {
1120 XmAnyCallbackStruct cback;
1121 XmMenuSystemTrait menuSTrait;
1122
1123 menuSTrait = (XmMenuSystemTrait)
1124 XmeTraitGet((XtPointer) XtClass(XtParent(cb)), XmQTmenuSystem);
1125 if (menuSTrait == NULL) return;
1126
1127 if (CB_WasPosted(cb)) {
1128 Disarm(cb, TRUE);
1129 if ((CB_Submenu(cb) != NULL) && (Lab_MenuType(cb) == XmMENU_BAR))
1130 _XmMenuPopDown(XtParent((Widget) cb), event, NULL);
1131 return;
1132 }
1133
1134 _XmCascadingPopup ((Widget) cb, event, doCascade);
1135
1136 /*
1137 * check if there is a submenu here in case this changed during
1138 * the cascading callbacks
1139 */
1140 if (CB_Submenu(cb) == NULL)
1141 {
1142 menuSTrait -> popdown(XtParent(cb), event);
1143
1144 Disarm (cb, FALSE);
1145
1146 menuSTrait -> disarm(XtParent(cb));
1147
1148 cback.event = event;
1149 cback.reason = XmCR_ACTIVATE;
1150
1151 if (menuSTrait != NULL)
1152 {
1153 menuSTrait -> entryCallback(XtParent(cb), (Widget) cb, &cback);
1154 }
1155
1156 if ((! cb->label.skipCallback) &&
1157 (cb->cascade_button.activate_callback))
1158 {
1159
1160 XtCallCallbackList ((Widget) cb, cb->cascade_button.activate_callback, &cback);
1161 }
1162 }
1163 else
1164 {
1165 Arm(cb);
1166 }
1167 }
1168
1169 /*
1170 * if there is a submenu, enable traversal.
1171 * call select to do the work
1172 */
1173 /*ARGSUSED*/
1174 static void
DoSelect(Widget wid,XEvent * event,String * param,Cardinal * num_param)1175 DoSelect(
1176 Widget wid,
1177 XEvent *event,
1178 String *param,
1179 Cardinal *num_param )
1180 {
1181 register XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid ;
1182 Boolean validButton;
1183 XmMenuSystemTrait menuSTrait;
1184
1185 menuSTrait = (XmMenuSystemTrait)
1186 XmeTraitGet((XtPointer) XtClass(XtParent(wid)), XmQTmenuSystem);
1187 if (menuSTrait == NULL) return;
1188
1189 /* To support menu replay, keep the pointer in sync mode */
1190 XAllowEvents(XtDisplay(cb), SyncPointer, CurrentTime);
1191
1192 if (event && event -> type == ButtonRelease &&
1193 event -> xbutton.button == 2)
1194 return;
1195
1196 if (!CB_IsArmed(cb))
1197 return;
1198
1199 if ((Lab_MenuType(cb) == XmMENU_BAR) && !RC_IsArmed (XtParent(cb)))
1200 return;
1201
1202 /*
1203 * make sure the shell is popped up, this takes care of a corner case
1204 * that can occur with rapid pressing of the mouse button
1205 */
1206 if (Lab_IsMenupane(cb) &&
1207 (!((XmMenuShellWidget) XtParent(XtParent(cb)))->shell.popped_up))
1208 {
1209 return;
1210 }
1211
1212 validButton = menuSTrait -> verifyButton(XtParent(cb), event);
1213
1214 if (validButton)
1215 {
1216 Select (cb, event, (Boolean)(CB_Submenu(cb) != NULL));
1217
1218 /* don't let the menu shell widget process this event */
1219 _XmRecordEvent (event);
1220
1221 _XmSetInDragMode((Widget) cb, False);
1222
1223 if (CB_Submenu(cb))
1224 {
1225 /*
1226 * if XmProcessTraversal() fails, it's possible that the pane
1227 * has no traversable children, so reset the focus to the pane.
1228 */
1229 if (!XmProcessTraversal(CB_Submenu(cb), XmTRAVERSE_CURRENT))
1230 {
1231 /* Must clear focus path first for shared menushells.
1232 * Otherwise, moving the focus back will have old stale
1233 * (old) focus_item.
1234 */
1235 _XmClearFocusPath(CB_Submenu(cb));
1236 XtSetKeyboardFocus(XtParent(CB_Submenu(cb)), CB_Submenu(cb));
1237 }
1238 }
1239 else
1240 {
1241 /* Move this call into Select().
1242 *
1243 * (* xmLabelClassRec.label_class.menuProcs) (XmMENU_DISARM,
1244 * XtParent(cb));
1245 */
1246
1247 if (Lab_MenuType(cb) == XmMENU_BAR)
1248 {
1249 _XmMenuFocus(XtParent(cb), XmMENU_END, CurrentTime);
1250 XtUngrabPointer( (Widget) cb, CurrentTime);
1251 }
1252 }
1253 }
1254 }
1255
1256 /*
1257 * if the menu system traversal is enabled, do a select
1258 */
1259 /*ARGSUSED*/
1260 static void
KeySelect(Widget wid,XEvent * event,String * param,Cardinal * num_param)1261 KeySelect(
1262 Widget wid,
1263 XEvent *event,
1264 String *param,
1265 Cardinal *num_param )
1266 {
1267 XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid ;
1268
1269 if (!_XmGetInDragMode((Widget) cb) &&
1270 (RC_IsArmed(XtParent(cb)) ||
1271 (RC_Type(XtParent(cb)) != XmMENU_BAR &&
1272 !XmIsMenuShell(XtParent(XtParent(cb))))))
1273 (* (((XmCascadeButtonClassRec *)(cb->core.widget_class))->
1274 primitive_class.arm_and_activate))
1275 ((Widget) cb, event, NULL, NULL);
1276 }
1277
1278
1279 /*
1280 * If the menu system is not active, arm it and arm this cascadebutton
1281 * else start the drag mode
1282 */
1283 static void
MenuBarSelect(Widget wid,XEvent * event,String * param,Cardinal * num_param)1284 MenuBarSelect(
1285 Widget wid,
1286 XEvent *event,
1287 String *param,
1288 Cardinal *num_param )
1289 {
1290 XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid ;
1291 Boolean validButton;
1292 Time _time = _XmGetDefaultTime(wid, event);
1293 XmMenuSystemTrait menuSTrait;
1294
1295 menuSTrait = (XmMenuSystemTrait)
1296 XmeTraitGet((XtPointer) XtClass(XtParent(wid)), XmQTmenuSystem);
1297 if (menuSTrait == NULL) return;
1298
1299 CB_SetWasPosted(cb, FALSE);
1300
1301 if (RC_IsArmed ((XmRowColumnWidget) XtParent(cb)))
1302 {
1303 /* Cleanup the PM menubar mode, if enabled */
1304 menuSTrait -> menuBarCleanup(XtParent(cb));
1305
1306 if (!CB_Submenu(cb))
1307 {
1308 _XmMenuFocus(XtParent(cb), XmMENU_MIDDLE, _time);
1309 }
1310
1311 StartDrag ((Widget) cb, event, param, num_param);
1312 }
1313
1314 else
1315 {
1316 /* XAllowEvents() is called here because StartDrag also calls it */
1317 /* To support menu replay, keep the pointer in sync mode */
1318 XAllowEvents(XtDisplay(cb), SyncPointer, CurrentTime);
1319
1320 validButton = menuSTrait -> verifyButton(XtParent(cb), event);
1321
1322 if (validButton)
1323 {
1324 /*
1325 * Don't post the menu if the menu cannot control grabs!
1326 */
1327
1328 if (_XmMenuGrabKeyboardAndPointer(XtParent(cb), _time) != GrabSuccess)
1329 {
1330 _XmRecordEvent (event);
1331 return;
1332 }
1333
1334 _XmMenuFocus(XtParent(cb), XmMENU_BEGIN, _time);
1335
1336 menuSTrait -> arm((Widget) cb);
1337
1338 _XmSetInDragMode((Widget) cb, True);
1339
1340 _XmCascadingPopup ((Widget) cb, event, TRUE);
1341
1342 if (!CB_Submenu(cb))
1343 {
1344 /*
1345 * since no submenu is posted, check if the grab has occured
1346 * and if not, do the pointer grab now.
1347 */
1348 if (RC_BeingArmed(XtParent(cb)))
1349 {
1350
1351 _XmGrabPointer(XtParent(cb), True, EVENTS,
1352 GrabModeAsync, GrabModeAsync, None,
1353 XmGetMenuCursor(XtDisplay(cb)), _time);
1354
1355 RC_SetBeingArmed(XtParent(cb), False);
1356 }
1357 }
1358
1359 /* To support menu replay, keep the pointer in sync mode */
1360 XAllowEvents(XtDisplay(cb), SyncPointer, CurrentTime);
1361
1362 /* record so that menuShell doesn't process this event */
1363 _XmRecordEvent (event);
1364 }
1365 }
1366 }
1367
1368
1369 /*
1370 * If the menu is active, post submenu and arm.
1371 */
1372 /*ARGSUSED*/
1373 static void
MenuBarEnter(Widget wid,XEvent * event,String * param,Cardinal * num_param)1374 MenuBarEnter(
1375 Widget wid,
1376 XEvent *event,
1377 String *param,
1378 Cardinal *num_param )
1379 {
1380 register XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid ;
1381 XmRowColumnWidget rc = (XmRowColumnWidget)XtParent(cb);
1382
1383 if (RC_IsArmed(rc) && !CB_IsArmed(cb) && _XmGetInDragMode((Widget) cb))
1384 {
1385 if (!CB_Submenu(cb))
1386 {
1387 _XmMenuFocus((Widget) rc, XmMENU_MIDDLE,
1388 _XmGetDefaultTime(wid, event));
1389 }
1390
1391 _XmCascadingPopup ((Widget) cb, event, TRUE);
1392 Arm(cb);
1393 }
1394 }
1395
1396
1397 /*
1398 * unless our submenu is posted or traversal is on, disarm
1399 */
1400 /*ARGSUSED*/
1401 static void
MenuBarLeave(Widget wid,XEvent * event,String * param,Cardinal * num_param)1402 MenuBarLeave(
1403 Widget wid,
1404 XEvent *event,
1405 String *param,
1406 Cardinal *num_param )
1407 {
1408 register XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid ;
1409 XmMenuShellWidget submenuShell;
1410
1411 if (RC_IsArmed (XtParent (cb)))
1412 {
1413 /* Reset this bit so that we don't unpost if the user
1414 reenters the cascade button */
1415 CB_SetWasPosted(cb, FALSE);
1416
1417 if (CB_Submenu(cb))
1418 {
1419 submenuShell = (XmMenuShellWidget) XtParent(CB_Submenu(cb));
1420
1421 if (submenuShell->shell.popped_up)
1422 return;
1423 }
1424
1425 if (_XmGetInDragMode((Widget) cb))
1426 Disarm (cb, TRUE);
1427 }
1428 }
1429
1430 /*
1431 * Cleanup the menubar, if its in the PM traversal mode
1432 */
1433 /*ARGSUSED*/
1434 static void
CleanupMenuBar(Widget wid,XEvent * event,String * param,Cardinal * num_param)1435 CleanupMenuBar(
1436 Widget wid,
1437 XEvent *event,
1438 String *param,
1439 Cardinal *num_param )
1440 {
1441 XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid ;
1442 XmRowColumnWidget parent = (XmRowColumnWidget)XtParent(cb);
1443
1444 if (RC_IsArmed(parent))
1445 {
1446 (* ((XmRowColumnWidgetClass) XtClass(parent))->
1447 row_column_class.armAndActivate) ( (Widget) parent,
1448 (XEvent *) NULL, (String *) NULL, (Cardinal *) NULL);
1449 _XmRecordEvent(event);
1450 }
1451 }
1452
1453
1454 /*
1455 * CascadeButton Widget and Gadget help routine - first bring down the
1456 * menu and then do the help callback.
1457 */
1458 void
_XmCBHelp(Widget w,XEvent * event,String * params,Cardinal * num_params)1459 _XmCBHelp(
1460 Widget w,
1461 XEvent *event,
1462 String *params,
1463 Cardinal *num_params )
1464 {
1465 XmRowColumnWidget parent = (XmRowColumnWidget) XtParent(w);
1466
1467 if (RC_Type(parent) == XmMENU_BAR)
1468 {
1469 /* Cannot call CleanupMenubar() 'cause it calls _XmRecordEvent */
1470 if (RC_IsArmed(parent))
1471 {
1472 (* ((XmRowColumnWidgetClass) XtClass(parent))->
1473 row_column_class.armAndActivate) ( (Widget) parent,
1474 (XEvent *) NULL, (String *) NULL, (Cardinal *) NULL);
1475 }
1476 }
1477
1478 else if ((RC_Type(parent) == XmMENU_PULLDOWN) ||
1479 (RC_Type(parent) == XmMENU_POPUP))
1480 {
1481 (*(((XmMenuShellClassRec *) xmMenuShellWidgetClass)->
1482 menu_shell_class.popdownDone)) (XtParent(parent), event,
1483 params, num_params);
1484 }
1485
1486 if (XmIsGadget(w))
1487 _XmSocorro(w, event, params, num_params);
1488 else
1489 _XmPrimitiveHelp( w, event, params, num_params) ;
1490 }
1491
1492
1493 /*
1494 * When moving between a shared menupane, we only want to unpost the
1495 * descendant panes, not the shared one.
1496 * We only need to check the first popup child, since the menushell
1497 * has always forced the popped up shell to be the first child.
1498 */
1499 static void
PopdownGrandChildren(XmRowColumnWidget rowcol)1500 PopdownGrandChildren(
1501 XmRowColumnWidget rowcol )
1502 {
1503 CompositeWidget menuShell;
1504
1505 if ((menuShell = (CompositeWidget) RC_PopupPosted(rowcol)) == NULL)
1506 return;
1507
1508 if ((menuShell = (CompositeWidget)
1509 RC_PopupPosted (menuShell->composite.children[0])) != NULL)
1510 {
1511 (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)->
1512 menu_shell_class.popdownEveryone))( (Widget) menuShell, NULL,
1513 NULL, NULL);
1514 }
1515 }
1516
1517
1518 /*
1519 * call the cascading callbacks. The cb parameter can be either a
1520 * cascadebutton widget or gadget.
1521 */
1522 static void
Cascading(Widget w,XEvent * event)1523 Cascading(
1524 Widget w,
1525 XEvent *event )
1526 {
1527 XmAnyCallbackStruct cback;
1528
1529 cback.reason = XmCR_CASCADING;
1530 cback.event = event;
1531
1532 if (XmIsCascadeButton(w))
1533 {
1534 XmCascadeButtonWidget cb = (XmCascadeButtonWidget)w;
1535 XmRowColumnWidget submenu = (XmRowColumnWidget) CB_Submenu(cb);
1536
1537 /* if the submenu is already up, just return */
1538 /* In case of shared menupanes, check the cascade button attachment */
1539
1540 if (submenu)
1541 {
1542 XmMenuShellWidget ms = (XmMenuShellWidget) XtParent(submenu);
1543 if (XmIsMenuShell(ms) &&
1544 ms->shell.popped_up &&
1545 ms->composite.children[0] == (Widget) submenu &&
1546 submenu->row_column.cascadeBtn == (Widget) cb)
1547 {
1548 return;
1549 }
1550 }
1551 XtCallCallbackList ((Widget) cb, cb->cascade_button.cascade_callback, &cback);
1552 }
1553 else
1554 {
1555 XmCascadeButtonGadget cb = (XmCascadeButtonGadget)w;
1556 XmRowColumnWidget submenu = (XmRowColumnWidget) CBG_Submenu(cb);
1557
1558 /* if the submenu is already up, just return */
1559 if (submenu)
1560 {
1561 XmMenuShellWidget ms = (XmMenuShellWidget) XtParent(submenu);
1562 if (XmIsMenuShell(ms) &&
1563 ms->shell.popped_up &&
1564 ms->composite.children[0] == (Widget) submenu &&
1565 submenu->row_column.cascadeBtn == (Widget) cb)
1566 {
1567 return;
1568 }
1569 }
1570
1571 XtCallCallbackList ((Widget) cb, cb->cascade_button.cascade_callback, &cback);
1572 }
1573 }
1574
1575
1576 /*
1577 * call the cascading callbacks and the popup any submenu. This is called
1578 * by both the cascadebutton widget and gadget.
1579 */
1580 void
_XmCascadingPopup(Widget cb,XEvent * event,int doCascade)1581 _XmCascadingPopup(
1582 Widget cb,
1583 XEvent *event,
1584 #if NeedWidePrototypes
1585 int doCascade )
1586 #else
1587 Boolean doCascade )
1588 #endif /* NeedWidePrototypes */
1589 {
1590 /* We must make sure the tear off to menushell restoration/callbacks are
1591 * called before the cascading callback. Exclude the pane in case Popup()
1592 * tries to restore it back to a transient if shared and already posted.
1593 */
1594 XmDisplay dd = (XmDisplay)XmGetXmDisplay(XtDisplay(cb));
1595 XmExcludedParentPaneRec *excPP =
1596 &(((XmDisplayInfo *)(dd->display.displayInfo))->excParentPane);
1597
1598 if (!excPP->pane)
1599 {
1600 excPP->pane_list_size = 4;
1601 excPP->pane = (Widget *)XtMalloc(sizeof(Widget) * excPP->pane_list_size);
1602 }
1603
1604 if (XmIsCascadeButtonGadget(cb))
1605 *(excPP->pane) = CBG_Submenu(cb);
1606 else
1607 *(excPP->pane) = CB_Submenu(cb);
1608
1609 if (*(excPP->pane))
1610 {
1611 excPP->num_panes = 1;
1612
1613 if (RC_TornOff(*(excPP->pane)) &&
1614 !XmIsMenuShell(XtParent(*(excPP->pane))))
1615 {
1616 /* If a subpane is already posted and it is not the pane that
1617 * will be posted from cb. Then it must be lowered so that
1618 * its tear off can be repainted.
1619 */
1620 if (RC_PopupPosted(XtParent(cb)))
1621 {
1622 XmRowColumnWidget postedPane = (XmRowColumnWidget)
1623 ((CompositeWidget)RC_PopupPosted(XtParent(cb)))->
1624 composite.children[0];
1625
1626 if ((Widget)postedPane != *(excPP->pane))
1627 {
1628 _XmLowerTearOffObscuringPoppingDownPanes( (Widget)postedPane,
1629 *(excPP->pane));
1630 }
1631 }
1632 _XmRestoreTearOffToMenuShell(*(excPP->pane), event);
1633 }
1634 }
1635
1636 if (doCascade)
1637 Cascading (cb, event);
1638 Popup (cb, event);
1639 }
1640
1641 /*
1642 * pop up the pulldown menu associated with this cascadebutton
1643 */
1644 static void
Popup(Widget cb,XEvent * event)1645 Popup(
1646 Widget cb,
1647 XEvent *event )
1648 {
1649 Widget oldActiveChild;
1650 Boolean popped_up = False;
1651 register XmRowColumnWidget submenu;
1652 XmMenuShellWidget shell = NULL;
1653 register XmRowColumnWidget parent = (XmRowColumnWidget) XtParent (cb);
1654 XmMenuSystemTrait menuSTrait;
1655 XmDisplay dd = (XmDisplay)XmGetXmDisplay(XtDisplay(cb));
1656 XmExcludedParentPaneRec *excPP =
1657 &(((XmDisplayInfo *)(dd->display.displayInfo))->excParentPane);
1658
1659 menuSTrait = (XmMenuSystemTrait)
1660 XmeTraitGet((XtPointer) XtClass((Widget) parent), XmQTmenuSystem);
1661 if (menuSTrait == NULL) return;
1662
1663 if (XmIsCascadeButtonGadget(cb))
1664 submenu = (XmRowColumnWidget) CBG_Submenu(cb);
1665 else
1666 submenu = (XmRowColumnWidget) CB_Submenu(cb);
1667
1668 /* if its already up, popdown submenus and then return */
1669 if (submenu &&
1670 (shell = (XmMenuShellWidget)XtParent(submenu)) &&
1671 XmIsMenuShell(shell) &&
1672 (popped_up = shell->shell.popped_up))
1673 {
1674
1675 /* Just in case the menu shell is being shared.
1676 * Shell's 0th child is currently posted submenu. In case of shared
1677 * menupanes we must check to make sure that it is not posted from
1678 * the same cascade button before popping down.
1679 * Also this is as good a time as any to clear have_traversal field
1680 * of submenu's active child. Updating this internal state allows
1681 * this gadget to highlight next time the submenu is posted.
1682 */
1683 if ((XmRowColumnWidget)shell->composite.children[0] == submenu)
1684 {
1685 if (cb == RC_CascadeBtn(submenu))
1686 {
1687 if (RC_PopupPosted(submenu))
1688 (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)->
1689 menu_shell_class.popdownEveryone))
1690 (RC_PopupPosted(submenu),NULL,NULL, NULL);
1691
1692 if (submenu->manager.active_child)
1693 {
1694 /* update visible focus/highlighting */
1695 if (XmIsPrimitive(submenu->manager.active_child))
1696 {
1697 (*(((XmPrimitiveClassRec *)XtClass(submenu->manager.
1698 active_child))->primitive_class.border_unhighlight))
1699 (submenu->manager.active_child);
1700 }
1701 else if (XmIsGadget(submenu->manager.active_child))
1702 {
1703 (*(((XmGadgetClassRec *)XtClass(submenu->manager.
1704 active_child))->gadget_class.border_unhighlight))
1705 (submenu->manager.active_child);
1706 }
1707 /* update internal focus state */
1708 _XmClearFocusPath((Widget) submenu);
1709 }
1710 *(excPP->pane) = NULL;
1711 excPP->num_panes = 0;
1712 return;
1713 }
1714 else
1715 {
1716 oldActiveChild = submenu->manager.active_child;
1717 if (oldActiveChild && XmIsGadget(oldActiveChild))
1718 ((XmGadget)oldActiveChild)->gadget.have_traversal = False;
1719 }
1720 }
1721 }
1722
1723 if (XtIsManaged ((Widget)parent))
1724 {
1725 if ((RC_Type(parent) == XmMENU_BAR) && !RC_IsArmed (parent))
1726 return;
1727
1728 /*
1729 * If the old active child for the menupane was a cascadeB gadget,
1730 * and it did not have its submenu posted, then
1731 * we need to manually send it FocusOut notification, since
1732 * when we managed our submenu, the active_child field for
1733 * our parent was set to us, and the parent now no longer knows
1734 * who previously had the focus.
1735 */
1736 oldActiveChild = parent->manager.active_child;
1737 if (oldActiveChild &&
1738 (oldActiveChild != (Widget)cb) &&
1739 XmIsCascadeButtonGadget(oldActiveChild) &&
1740 CBG_Submenu(oldActiveChild) &&
1741 (((XmMenuShellWidget)XtParent(CBG_Submenu(oldActiveChild)))->
1742 shell.popped_up == False))
1743 {
1744 parent->manager.active_child = NULL;
1745 _XmDispatchGadgetInput((Widget) oldActiveChild, NULL,
1746 XmFOCUS_OUT_EVENT);
1747 ((XmGadget)oldActiveChild)->gadget.have_traversal = False;
1748 }
1749 else
1750 /*
1751 * Fix for CR 5683 - If the RC_CascadeBtn == cb, then the menupane
1752 * should not be popped down (it probably already
1753 * is popped down), so do not pop down the
1754 * menupane (it messes up the traversal)
1755 */
1756 if (!submenu ||
1757 !popped_up ||
1758 (RC_PopupPosted(parent) != (Widget)shell) ||
1759 (submenu && RC_CascadeBtn(submenu) &&
1760 (RC_CascadeBtn(submenu) != cb) &&
1761 ((Widget)parent == XtParent(RC_CascadeBtn(submenu)))) )
1762 {
1763 /* popdown all visible subpanes of this parent when:
1764 * - moving to a button without a submenu
1765 * - between non-shared menushells
1766 * = then menushell will not be popped_up
1767 * = the old shell is different than the new shell
1768 * (old shell nonshared, new shell shared)
1769 * - special case when same pane attached to > 1 cb in same parent
1770 */
1771 if (RC_PopupPosted(parent))
1772 {
1773 (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)->
1774 menu_shell_class.popdownEveryone))
1775 (RC_PopupPosted(parent),NULL,NULL, NULL);
1776 }
1777
1778 /* Focus is not being handled perfectly for tear offs whose parent
1779 * is a top level shell. So force cascade unhighlighting here.
1780 */
1781 if (NULL != oldActiveChild &&
1782 oldActiveChild != cb &&
1783 ((parent->row_column.type == XmMENU_PULLDOWN) ||
1784 (parent->row_column.type == XmMENU_POPUP)) &&
1785 !XmIsMenuShell(XtParent(parent)))
1786 XmCascadeButtonHighlight(oldActiveChild, FALSE);
1787 }
1788 else
1789 {
1790 /*
1791 * Handle shared menupanes */
1792 PopdownGrandChildren (parent);
1793 }
1794
1795 /* We don't allow the possibility of the submenu to be restored
1796 * from the menushell back to the transient shell during the
1797 * previous popdown code. This occurs when the tear off is shared
1798 * and previously posted.
1799 */
1800 *(excPP->pane) = NULL;
1801 excPP->num_panes = 0;
1802
1803 if (submenu)
1804 {
1805 if (((ShellWidget)XtParent(submenu))->composite.num_children == 1)
1806 {
1807 menuSTrait -> cascade((Widget) submenu, cb, event);
1808
1809 /* Map the window first to sync up the server in case the
1810 * menushell was previously shared
1811 */
1812 XMapWindow(XtDisplay(submenu), XtWindow(submenu));
1813 XtManageChild( (Widget) submenu);
1814 }
1815 else
1816 {
1817 /* We will call menuprocs XmMENU_CASCADING from
1818 * popupSharedMenuShell routine so that it occurs between
1819 * shared menupane window configurations.
1820 */
1821 (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)->
1822 menu_shell_class.popupSharedMenupane))(cb,
1823 (Widget) submenu, event);
1824 }
1825 /* So help is delivered correctly when in drag mode */
1826 if (_XmGetInDragMode((Widget)cb))
1827 XtSetKeyboardFocus((Widget)submenu, None);
1828 }
1829 }
1830 }
1831
1832
1833 /*
1834 * get the cascade size set up
1835 */
1836 static void
size_cascade(XmCascadeButtonWidget cascadebtn)1837 size_cascade(
1838 XmCascadeButtonWidget cascadebtn )
1839 {
1840 Window rootwin;
1841 int x,y; /* must be int */
1842 unsigned int width, height, border, depth; /* must be int */
1843
1844 if (CB_CascadePixmap(cascadebtn) != XmUNSPECIFIED_PIXMAP)
1845 {
1846 XGetGeometry(XtDisplay(cascadebtn), CB_CascadePixmap(cascadebtn),
1847 &rootwin, &x, &y, &width, &height,
1848 &border, &depth);
1849
1850 CB_Cascade_width(cascadebtn) = (Dimension) width;
1851 CB_Cascade_height(cascadebtn) = (Dimension) height;
1852 }
1853 else
1854 {
1855 CB_Cascade_width(cascadebtn) = 0;
1856 CB_Cascade_height(cascadebtn) = 0;
1857 }
1858 }
1859
1860
1861 /*
1862 * set up the cascade position.
1863 */
1864 static void
position_cascade(XmCascadeButtonWidget cascadebtn)1865 position_cascade(
1866 XmCascadeButtonWidget cascadebtn )
1867 {
1868 Dimension buffer;
1869
1870 if (CB_HasCascade(cascadebtn))
1871 {
1872 if (LayoutIsRtoLP(cascadebtn))
1873 CB_Cascade_x(cascadebtn) = cascadebtn->primitive.highlight_thickness +
1874 cascadebtn->primitive.shadow_thickness +
1875 Lab_MarginWidth(cascadebtn);
1876 else
1877 CB_Cascade_x(cascadebtn) = XtWidth (cascadebtn) -
1878 cascadebtn->primitive.highlight_thickness -
1879 cascadebtn->primitive.shadow_thickness -
1880 Lab_MarginWidth(cascadebtn) -
1881 CB_Cascade_width(cascadebtn);
1882
1883 buffer = cascadebtn->primitive.highlight_thickness +
1884 cascadebtn->primitive.shadow_thickness +
1885 Lab_MarginHeight(cascadebtn);
1886
1887 CB_Cascade_y(cascadebtn) = buffer +
1888 ((XtHeight(cascadebtn) - 2*buffer) -
1889 CB_Cascade_height(cascadebtn)) / 2;
1890 }
1891 else
1892 {
1893 CB_Cascade_y(cascadebtn) = 0;
1894 CB_Cascade_x(cascadebtn) = 0;
1895 }
1896 }
1897
1898
1899 /*
1900 * set up the cascade size and location
1901 */
1902 static void
setup_cascade(XmCascadeButtonWidget cascadebtn,int adjustWidth,int adjustHeight)1903 setup_cascade(
1904 XmCascadeButtonWidget cascadebtn,
1905 #if NeedWidePrototypes
1906 int adjustWidth,
1907 int adjustHeight )
1908 #else
1909 Boolean adjustWidth,
1910 Boolean adjustHeight )
1911 #endif /* NeedWidePrototypes */
1912 {
1913 Dimension delta;
1914
1915 if (CB_HasCascade(cascadebtn))
1916 {
1917 /*
1918 * modify the size of the cascadebutton to acommadate the cascade, if
1919 * needed. The cascade should fit inside MarginRight.
1920 */
1921 if (LayoutIsRtoLP(cascadebtn))
1922 {
1923 if ((CB_Cascade_width(cascadebtn) + CASCADE_PIX_SPACE) >
1924 Lab_MarginLeft(cascadebtn))
1925 {
1926 delta = CB_Cascade_width(cascadebtn) + CASCADE_PIX_SPACE -
1927 Lab_MarginLeft(cascadebtn);
1928 Lab_MarginLeft(cascadebtn) += delta;
1929
1930 if (adjustWidth)
1931 XtWidth(cascadebtn) += delta;
1932
1933 else
1934 {
1935 if (cascadebtn->label.alignment == XmALIGNMENT_BEGINNING)
1936 Lab_TextRect_x(cascadebtn) += delta;
1937 else if (cascadebtn->label.alignment == XmALIGNMENT_CENTER)
1938 Lab_TextRect_x(cascadebtn) += delta/2;
1939 }
1940 }
1941
1942 }
1943 else
1944 {
1945 if ((CB_Cascade_width(cascadebtn) + CASCADE_PIX_SPACE) >
1946 Lab_MarginRight(cascadebtn))
1947 {
1948 delta = CB_Cascade_width(cascadebtn) + CASCADE_PIX_SPACE -
1949 Lab_MarginRight(cascadebtn);
1950 Lab_MarginRight(cascadebtn) += delta;
1951
1952 if (adjustWidth)
1953 XtWidth(cascadebtn) += delta;
1954
1955 else
1956 {
1957 if (cascadebtn->label.alignment == XmALIGNMENT_END)
1958 Lab_TextRect_x(cascadebtn) -= delta;
1959 else if (cascadebtn->label.alignment == XmALIGNMENT_CENTER)
1960 Lab_TextRect_x(cascadebtn) -= delta/2;
1961 }
1962 }
1963 }
1964
1965 /*
1966 * the cascade height should fit inside of
1967 * TextRect + marginTop + marginBottom
1968 */
1969 delta = CB_Cascade_height(cascadebtn) +
1970 2 * (Lab_MarginHeight(cascadebtn) +
1971 cascadebtn->primitive.shadow_thickness +
1972 cascadebtn->primitive.highlight_thickness);
1973
1974 if (delta > XtHeight(cascadebtn))
1975 {
1976 delta -= XtHeight(cascadebtn);
1977 Lab_MarginTop(cascadebtn) += delta/2;
1978 Lab_TextRect_y(cascadebtn) += delta/2;
1979 Lab_MarginBottom(cascadebtn) += delta - (delta/2);
1980
1981 if (adjustHeight)
1982 XtHeight(cascadebtn) += delta;
1983 }
1984 }
1985
1986 position_cascade(cascadebtn);
1987 }
1988
1989
1990 /*
1991 * Destroy the widget
1992 */
1993 static void
Destroy(Widget wid)1994 Destroy(
1995 Widget wid )
1996 {
1997 XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid ;
1998 XmRowColumnWidget submenu = (XmRowColumnWidget) CB_Submenu(cb);
1999 XmMenuSystemTrait menuSTrait;
2000
2001 menuSTrait = (XmMenuSystemTrait)
2002 XmeTraitGet((XtPointer) XtClass(XtParent(wid)), XmQTmenuSystem);
2003
2004
2005 /*
2006 * If the armed pixmap exists, both pixmaps must be cached arrows
2007 */
2008 if (CB_ArmedPixmap(cb))
2009 {
2010 _XmProcessLock();
2011 _XmArrowPixmapCacheDelete((XtPointer) CB_ArmedPixmap(cb));
2012 _XmArrowPixmapCacheDelete((XtPointer) CB_CascadePixmap(cb));
2013 _XmProcessUnlock();
2014 }
2015
2016 /*
2017 * break the submenu link
2018 */
2019 if (submenu != NULL && menuSTrait != NULL)
2020 menuSTrait -> recordPostFromWidget((Widget) submenu, (Widget) cb, FALSE);
2021
2022 if (cb->cascade_button.timer)
2023 XtRemoveTimeOut (cb->cascade_button.timer);
2024
2025 /* Release the GCs */
2026
2027 XtReleaseGC (wid, CB_ArmGC(wid));
2028 XtReleaseGC (wid, CB_BackgroundGC(wid));
2029
2030 }
2031
2032
2033 /*
2034 * routine to resize a cascade button, called by the parent
2035 * geometery manager
2036 */
2037 static void
Resize(Widget cb)2038 Resize(
2039 Widget cb )
2040 {
2041 /*
2042 */
2043 if (cb)
2044 {
2045 XtWidgetProc resize;
2046
2047 /* Label class does it's work */
2048
2049 _XmProcessLock();
2050 resize = xmLabelClassRec.core_class.resize;
2051 _XmProcessUnlock();
2052 (* resize) (cb);
2053
2054 /* move the cascade too */
2055 position_cascade ((XmCascadeButtonWidget) cb);
2056 }
2057 }
2058
2059 /************************************************************************
2060 *
2061 * SetValuesPrehook
2062 *
2063 ************************************************************************/
2064
2065 /*ARGSUSED*/
2066 static Boolean
SetValuesPrehook(Widget cw,Widget rw,Widget nw,ArgList args,Cardinal * num_args)2067 SetValuesPrehook(
2068 Widget cw, /* unused */
2069 Widget rw, /* unused */
2070 Widget nw,
2071 ArgList args, /* unused */
2072 Cardinal *num_args ) /* unused */
2073 {
2074 XmCascadeButtonWidget new_w = (XmCascadeButtonWidget) nw ;
2075
2076 /* CR 2990: Use XmNbuttonFontList as the default font. */
2077 if (new_w->label.font == NULL)
2078 new_w->label.font = XmeGetDefaultRenderTable (nw, XmBUTTON_FONTLIST);
2079
2080 return False;
2081 }
2082
2083 /*
2084 * Set Values
2085 */
2086 /*ARGSUSED*/
2087 static Boolean
SetValues(Widget cw,Widget rw,Widget nw,ArgList args,Cardinal * num_args)2088 SetValues(
2089 Widget cw,
2090 Widget rw,
2091 Widget nw,
2092 ArgList args, /* unused */
2093 Cardinal *num_args ) /* unused */
2094 {
2095 XmCascadeButtonWidget old = (XmCascadeButtonWidget) cw ;
2096 XmCascadeButtonWidget requested = (XmCascadeButtonWidget) rw ;
2097 XmCascadeButtonWidget new_w = (XmCascadeButtonWidget) nw ;
2098 Boolean flag = FALSE;
2099 Boolean adjustWidth = FALSE;
2100 Boolean adjustHeight = FALSE;
2101 XmMenuSystemTrait menuSTrait;
2102
2103 menuSTrait = (XmMenuSystemTrait)
2104 XmeTraitGet((XtPointer) XtClass(XtParent(cw)), XmQTmenuSystem);
2105
2106 if (old->primitive.foreground != new_w->primitive.foreground
2107 || old->core.background_pixel != new_w->core.background_pixel) {
2108 GetBackgroundGC(new_w);
2109 }
2110
2111 if ((CB_Submenu(new_w)) &&
2112 ((! XmIsRowColumn(CB_Submenu(new_w))) ||
2113 (RC_Type(CB_Submenu(new_w)) != XmMENU_PULLDOWN)))
2114 {
2115 CB_Submenu(new_w) = NULL;
2116 XmeWarning( (Widget)new_w, WRONGSUBMENU);
2117 }
2118
2119 if (new_w->cascade_button.map_delay < 0)
2120 {
2121 new_w->cascade_button.map_delay = old->cascade_button.map_delay;
2122 XmeWarning( (Widget)new_w, WRONGMAPDELAY);
2123 }
2124
2125 /* if there is a change to submenu, notify menu system */
2126 if (CB_Submenu(old) != CB_Submenu(new_w))
2127 {
2128 /* We must pass nw as the parameter to recordPostFromWidget
2129 * because old is a copy! The call to recordPostFromWidget() does
2130 * a widget ID comparison and we must pass the real widget (nw).
2131 */
2132
2133 if (CB_Submenu(old) && menuSTrait)
2134 menuSTrait -> recordPostFromWidget(CB_Submenu(old), nw, FALSE);
2135
2136 if (CB_Submenu(new_w) && menuSTrait)
2137 menuSTrait -> recordPostFromWidget(CB_Submenu(new_w), nw, TRUE);
2138 }
2139
2140 /* don't let traversal be changed */
2141 if (Lab_MenuType(new_w) == XmMENU_BAR)
2142 new_w->primitive.traversal_on = TRUE;
2143
2144 /* handle the cascade pixmap indicator */
2145 else if (Lab_IsMenupane(new_w))
2146 {
2147 /* don't let traversal be changed */
2148 new_w->primitive.traversal_on = TRUE;
2149
2150 if ((new_w->label.recompute_size) || (requested->core.width <= 0))
2151 adjustWidth = TRUE;
2152
2153 if ((new_w->label.recompute_size) || (requested->core.height <= 0))
2154 adjustHeight = TRUE;
2155
2156 /* get new pixmap size */
2157 if (CB_CascadePixmap(old) != CB_CascadePixmap (new_w))
2158 {
2159 if (CB_ArmedPixmap(old) != XmUNSPECIFIED_PIXMAP)
2160 {
2161 _XmProcessLock();
2162 _XmArrowPixmapCacheDelete((XtPointer) CB_ArmedPixmap(old));
2163 _XmArrowPixmapCacheDelete((XtPointer) CB_CascadePixmap(old));
2164 _XmProcessUnlock();
2165 }
2166 CB_ArmedPixmap(new_w) = XmUNSPECIFIED_PIXMAP;
2167 size_cascade (new_w);
2168 } else
2169 if ( ((CB_CascadePixmap(new_w) == XmUNSPECIFIED_PIXMAP) &&
2170 (!CB_Submenu(old) && CB_Submenu(new_w))) ||
2171 ((CB_ArmedPixmap(old) != XmUNSPECIFIED_PIXMAP) &&
2172 ((Lab_TextRect_height(old) != Lab_TextRect_height(new_w)) ||
2173 (old->primitive.foreground != new_w->primitive.foreground) ||
2174 (old->core.background_pixel !=
2175 new_w->core.background_pixel))))
2176 {
2177 _XmProcessLock();
2178 _XmArrowPixmapCacheDelete((XtPointer) CB_ArmedPixmap(old));
2179 _XmArrowPixmapCacheDelete((XtPointer) CB_CascadePixmap(old));
2180 CB_ArmedPixmap(new_w) = XmUNSPECIFIED_PIXMAP;
2181 CB_CascadePixmap(new_w) = XmUNSPECIFIED_PIXMAP;
2182 _XmCreateArrowPixmaps((Widget) new_w);
2183 _XmProcessUnlock();
2184 size_cascade (new_w);
2185 }
2186
2187 /*
2188 * resize widget if cascade appeared or disappeared, or if the
2189 * cascade pixmap changed size.
2190 */
2191 if ((CB_CascadePixmap (old) != CB_CascadePixmap (new_w)) ||
2192 (old->label.label_type != new_w->label.label_type) ||
2193 (old->cascade_button.submenu != new_w->cascade_button.submenu))
2194 {
2195 setup_cascade (new_w, adjustWidth, adjustHeight);
2196
2197 /* if there wasn't a cascade, and still isn't, don't redraw */
2198 if (old->cascade_button.submenu || new_w->cascade_button.submenu)
2199 flag = TRUE;
2200 }
2201
2202 /* make sure that other changes did not scrunch our pixmap */
2203 else if (new_w->cascade_button.submenu)
2204 {
2205 if ((new_w->primitive.highlight_thickness !=
2206 old->primitive.highlight_thickness) ||
2207 (new_w->primitive.shadow_thickness !=
2208 old->primitive.shadow_thickness) ||
2209 (Lab_MarginRight (new_w) != Lab_MarginRight (old)) ||
2210 (Lab_MarginHeight (new_w) != Lab_MarginHeight (old)) ||
2211 (Lab_MarginTop (new_w) != Lab_MarginTop (old)) ||
2212 (Lab_MarginBottom (new_w) != Lab_MarginBottom (old)))
2213 {
2214 setup_cascade (new_w,adjustWidth, adjustHeight);
2215 flag = TRUE;
2216 }
2217
2218 else if ((Lab_MarginWidth(new_w) != Lab_MarginWidth(old)) ||
2219 (new_w->core.width != old->core.width) ||
2220 (new_w->core.height != old->core.height))
2221
2222 {
2223 position_cascade (new_w);
2224 flag = TRUE;
2225 }
2226 }
2227 }
2228
2229 return (flag);
2230 }
2231
2232 /************************************************************
2233 *
2234 * InitializePrehook
2235 *
2236 * Put the proper translations in core_class tm_table so that
2237 * the data is massaged correctly
2238 *
2239 ************************************************************/
2240 /*ARGSUSED*/
2241 static void
InitializePrehook(Widget req,Widget new_w,ArgList args,Cardinal * num_args)2242 InitializePrehook(
2243 Widget req, /* unused */
2244 Widget new_w,
2245 ArgList args, /* unused */
2246 Cardinal *num_args ) /* unused */
2247 {
2248 unsigned char type;
2249 XmMenuSystemTrait menuSTrait;
2250 XmCascadeButtonWidget bw = (XmCascadeButtonWidget) new_w ;
2251
2252 _XmSaveCoreClassTranslations (new_w);
2253
2254 menuSTrait = (XmMenuSystemTrait)
2255 XmeTraitGet((XtPointer) XtClass((Widget) XtParent(new_w)), XmQTmenuSystem);
2256
2257 if (menuSTrait != NULL)
2258 type = menuSTrait->type(XtParent(new_w));
2259 else
2260 type = XmWORK_AREA;
2261
2262 _XmProcessLock();
2263 if (type == XmMENU_PULLDOWN ||
2264 type == XmMENU_POPUP)
2265 new_w->core.widget_class->core_class.tm_table = (String) p_events_parsed;
2266 else
2267 new_w->core.widget_class->core_class.tm_table =(String)menubar_events_parsed;
2268 _XmProcessUnlock();
2269
2270 /* CR 2990: Use XmNbuttonFontList as the default font. */
2271 if (bw->label.font == NULL)
2272 bw->label.font = XmeGetDefaultRenderTable (new_w, XmBUTTON_FONTLIST);
2273 }
2274
2275 /************************************************************
2276 *
2277 * InitializePosthook
2278 *
2279 * restore core class translations
2280 *
2281 ************************************************************/
2282 /*ARGSUSED*/
2283 static void
InitializePosthook(Widget req,Widget new_w,ArgList args,Cardinal * num_args)2284 InitializePosthook(
2285 Widget req, /* unused */
2286 Widget new_w,
2287 ArgList args, /* unused */
2288 Cardinal *num_args ) /* unused */
2289 {
2290 _XmRestoreCoreClassTranslations (new_w);
2291 }
2292
2293 /************************************************************************
2294 *
2295 * GetArmGC
2296 * Get the graphics context used for filling in background of the
2297 * cascade button when armed.
2298 *
2299 ************************************************************************/
2300
2301 static void
GetArmGC(XmCascadeButtonWidget cb)2302 GetArmGC(
2303 XmCascadeButtonWidget cb )
2304 {
2305 XGCValues values;
2306 XtGCMask valueMask;
2307 Pixel junk, select_pixel;
2308
2309 XmGetColors(XtScreen(cb), cb->core.colormap, cb->core.background_pixel,
2310 &junk, &junk, &junk, &select_pixel);
2311
2312 valueMask = GCForeground | GCBackground | GCGraphicsExposures;
2313
2314 values.foreground = select_pixel;
2315 values.background = cb->primitive.foreground;
2316 values.graphics_exposures = False;
2317
2318 CB_ArmGC(cb) = XtGetGC ((Widget) cb, valueMask, &values);
2319 }
2320
2321 /************************************************************************
2322 *
2323 * GetBackgroundGC
2324 * Get the graphics context used for filling in background of
2325 * the cascade button when not armed.
2326 *
2327 ************************************************************************/
2328 static void
GetBackgroundGC(XmCascadeButtonWidget cb)2329 GetBackgroundGC(
2330 XmCascadeButtonWidget cb )
2331 {
2332 XGCValues values;
2333 XtGCMask valueMask;
2334 XFontStruct *fs;
2335
2336 valueMask = GCForeground | GCBackground | GCFont | GCGraphicsExposures;
2337
2338 values.foreground = cb->core.background_pixel;
2339 values.background = cb->primitive.foreground;
2340 values.graphics_exposures = False;
2341
2342 if (XmeRenderTableGetDefaultFont(cb->label.font, &fs))
2343 values.font = fs->fid;
2344 else
2345 valueMask &= ~GCFont;
2346
2347 CB_BackgroundGC(cb) = XtGetGC ((Widget) cb, valueMask, &values);
2348 }
2349
2350 /*
2351 * Initialize
2352 */
2353 /*ARGSUSED*/
2354 static void
Initialize(Widget w_req,Widget w_new,ArgList args,Cardinal * num_args)2355 Initialize(
2356 Widget w_req,
2357 Widget w_new,
2358 ArgList args, /* unused */
2359 Cardinal *num_args ) /* unused */
2360 {
2361 XmCascadeButtonWidget req = (XmCascadeButtonWidget) w_req ;
2362 XmCascadeButtonWidget new_w = (XmCascadeButtonWidget) w_new ;
2363 Boolean adjustWidth = FALSE;
2364 Boolean adjustHeight = FALSE;
2365 XmMenuSystemTrait menuSTrait;
2366
2367 XmRowColumnWidget submenu = (XmRowColumnWidget) CB_Submenu(new_w);
2368 XmRowColumnWidget parent = (XmRowColumnWidget) XtParent(new_w);
2369
2370 menuSTrait = (XmMenuSystemTrait)
2371 XmeTraitGet((XtPointer) XtClass((Widget) parent), XmQTmenuSystem);
2372
2373 if ((! XmIsRowColumn (parent)) &&
2374 ((Lab_MenuType(new_w) == XmMENU_PULLDOWN) ||
2375 (Lab_MenuType(new_w) == XmMENU_POPUP) ||
2376 (Lab_MenuType(new_w) == XmMENU_BAR)))
2377 {
2378 XmeWarning( (Widget)new_w, WRONGPARENT);
2379 }
2380
2381 /* if menuProcs is not set up yet, try again */
2382 _XmProcessLock();
2383 if (xmLabelClassRec.label_class.menuProcs == NULL)
2384 xmLabelClassRec.label_class.menuProcs =
2385 (XmMenuProc) _XmGetMenuProcContext();
2386 _XmProcessUnlock();
2387
2388 /* CR 7651: Clear before setting. */
2389 new_w->cascade_button.armed = 0;
2390 CB_SetArmed(new_w, FALSE);
2391 new_w->cascade_button.timer = 0;
2392 CB_SetTraverse (new_w, FALSE);
2393 CB_SetWasPosted (new_w, FALSE);
2394 CB_ArmedPixmap(new_w) = XmUNSPECIFIED_PIXMAP;
2395
2396 /*
2397 * if the user did not specify a margin width, set the default.
2398 * The menubar cbs have a larger margin.
2399 */
2400 if (Lab_MarginWidth(req) == XmINVALID_DIMENSION)
2401 {
2402 if (Lab_MenuType(new_w) == XmMENU_BAR)
2403 Lab_MarginWidth(new_w) = 6;
2404 else
2405 Lab_MarginWidth(new_w) = 2;
2406 }
2407
2408
2409
2410 if (submenu &&
2411 (! XmIsRowColumn(submenu) ||
2412 (RC_Type(submenu) != XmMENU_PULLDOWN)))
2413 {
2414 submenu = NULL;
2415 XmeWarning( (Widget)new_w, WRONGSUBMENU);
2416 }
2417
2418 if (new_w->cascade_button.map_delay < 0)
2419 {
2420 new_w->cascade_button.map_delay = MAP_DELAY_DEFAULT;
2421 XmeWarning( (Widget)new_w, WRONGMAPDELAY);
2422 }
2423
2424 /* call submenu's class function to set the link */
2425 if (submenu != NULL && menuSTrait != NULL)
2426 menuSTrait -> recordPostFromWidget((Widget) submenu, (Widget) new_w, TRUE);
2427
2428 if (submenu && (CB_CascadePixmap(new_w) == XmUNSPECIFIED_PIXMAP)) {
2429 _XmProcessLock();
2430 _XmCreateArrowPixmaps((Widget) new_w);
2431 _XmProcessUnlock();
2432 }
2433
2434 if (Lab_IsMenupane(new_w))
2435 {
2436 if (req->core.width <= 0)
2437 adjustWidth = TRUE;
2438
2439 if (req->core.height <= 0)
2440 adjustHeight = TRUE;
2441
2442 /* get pixmap size and set up widget to allow room for it */
2443 size_cascade (new_w);
2444 setup_cascade (new_w, adjustWidth, adjustHeight);
2445 }
2446
2447 new_w->primitive.traversal_on = TRUE;
2448
2449 /* Initialize GCs for armed button select and background only */
2450 GetArmGC (new_w);
2451 GetBackgroundGC (new_w);
2452
2453 }
2454
2455
2456 /*
2457 *************************************************************************
2458 *
2459 * Public Routines
2460 *
2461 *************************************************************************
2462 */
2463 Widget
XmCreateCascadeButton(Widget parent,char * name,ArgList al,Cardinal ac)2464 XmCreateCascadeButton(
2465 Widget parent,
2466 char *name,
2467 ArgList al,
2468 Cardinal ac )
2469 {
2470 Widget cb;
2471
2472 cb = XtCreateWidget(name, xmCascadeButtonWidgetClass, parent, al, ac);
2473
2474 return (cb);
2475 }
2476
2477 Widget
XmVaCreateCascadeButton(Widget parent,char * name,...)2478 XmVaCreateCascadeButton(
2479 Widget parent,
2480 char *name,
2481 ...)
2482 {
2483 register Widget w;
2484 va_list var;
2485 int count;
2486
2487 Va_start(var,name);
2488 count = XmeCountVaListSimple(var);
2489 va_end(var);
2490
2491
2492 Va_start(var, name);
2493 w = XmeVLCreateWidget(name,
2494 xmCascadeButtonWidgetClass,
2495 parent, False,
2496 var, count);
2497 va_end(var);
2498 return w;
2499 }
2500
2501 Widget
XmVaCreateManagedCascadeButton(Widget parent,char * name,...)2502 XmVaCreateManagedCascadeButton(
2503 Widget parent,
2504 char *name,
2505 ...)
2506 {
2507 Widget w = NULL;
2508 va_list var;
2509 int count;
2510
2511 Va_start(var, name);
2512 count = XmeCountVaListSimple(var);
2513 va_end(var);
2514
2515 Va_start(var, name);
2516 w = XmeVLCreateWidget(name,
2517 xmCascadeButtonWidgetClass,
2518 parent, True,
2519 var, count);
2520 va_end(var);
2521 return w;
2522 }
2523
2524 /*
2525 * This routine is called for both cascadebutton gadgets and widgets.
2526 * The button is armed or disarmed but it does not pop up or down submenus.
2527 */
2528 void
XmCascadeButtonHighlight(Widget cb,int highlight)2529 XmCascadeButtonHighlight(
2530 Widget cb,
2531 #if NeedWidePrototypes
2532 int highlight )
2533 #else
2534 Boolean highlight )
2535 #endif /* NeedWidePrototypes */
2536 {
2537 XtAppContext app;
2538
2539 if (NULL == cb) return;
2540 app = XtWidgetToApplicationContext(cb);
2541 _XmAppLock(app);
2542 if ((cb) && XmIsCascadeButton(cb))
2543 {
2544 if (highlight)
2545 Arm ((XmCascadeButtonWidget) cb);
2546
2547 else
2548 Disarm ((XmCascadeButtonWidget) cb, FALSE);
2549 }
2550
2551 else if ((cb) && XmIsCascadeButtonGadget(cb))
2552 XmCascadeButtonGadgetHighlight ((Widget) cb, highlight);
2553 _XmAppUnlock(app);
2554 }
2555