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: TearOff.c /main/15 1997/08/21 14:19:26 csn $"
26 #endif
27 #endif
28 
29 #ifdef HAVE_CONFIG_H
30 #include <config.h>
31 #endif
32 
33 
34 #include <X11/cursorfont.h>
35 
36 #include <Xm/AtomMgr.h>
37 #include <Xm/BaseClassP.h>
38 #include <Xm/DisplayP.h>
39 #include <Xm/GadgetP.h>
40 #include <Xm/LabelP.h>
41 #include <Xm/MenuShellP.h>
42 #include <Xm/MenuT.h>
43 #include <Xm/MwmUtil.h>
44 #include <Xm/Protocols.h>
45 #include <Xm/SeparatorP.h>
46 #include <Xm/TraitP.h>
47 #include <Xm/VirtKeysP.h>
48 #include <Xm/XmosP.h>		/* for bzero */
49 #include "MenuStateI.h"
50 #include "MenuUtilI.h"
51 #include "RCMenuI.h"
52 #include "RowColumnI.h"
53 #include "ScreenI.h"
54 #include "TearOffI.h"
55 #include "TraversalI.h"
56 #include "XmI.h"
57 
58 #define IsPopup(m)     \
59     (((XmRowColumnWidget) (m))->row_column.type == XmMENU_POPUP)
60 #define IsPulldown(m)  \
61     (((XmRowColumnWidget) (m))->row_column.type == XmMENU_PULLDOWN)
62 #define IsOption(m)    \
63     (((XmRowColumnWidget) (m))->row_column.type == XmMENU_OPTION)
64 #define IsBar(m)       \
65     (((XmRowColumnWidget) (m))->row_column.type == XmMENU_BAR)
66 
67 /* Bury these here for now - not spec'd for 1.2, maybe for 1.3? */
68 #define CREATE_TEAR_OFF 0
69 #define RESTORE_TEAR_OFF_TO_TOPLEVEL_SHELL 1
70 #define RESTORE_TEAR_OFF_TO_MENUSHELL 2
71 #define DESTROY_TEAR_OFF 3
72 
73 #define OUTLINE_WIDTH	2
74 #define SEGS_PER_DRAW	(4*OUTLINE_WIDTH)
75 
76 
77 /********    Static Function Declarations    ********/
78 
79 static GC InitXmTearOffXorGC(
80                         Widget wid) ;
81 static void SetupOutline(
82                         Widget wid,
83 			GC gc,
84                         XSegment pOutline[],
85                         XEvent *event,
86 			Dimension delta_x,
87 			Dimension delta_y ) ;
88 static void EraseOutline(
89                         Widget wid,
90 			GC gc,
91                         XSegment *pOutline) ;
92 static void MoveOutline(
93                         Widget wid,
94 			GC gc,
95                         XSegment *pOutline,
96                         XEvent *event,
97 			Dimension delta_x,
98 			Dimension delta_y ) ;
99 static void PullExposureEvents(
100                         Widget wid ) ;
101 static void MoveOpaque(
102                         Widget wid,
103                         XEvent *event,
104 			Dimension delta_x,
105 			Dimension delta_y ) ;
106 static void GetConfigEvent(
107                         Display *display,
108                         Window window,
109                         unsigned long mask,
110                         XEvent *event) ;
111 static Cursor GetTearOffCursor(
112                         Widget wid) ;
113 static Boolean DoPlacement(
114                         Widget wid,
115                         XEvent *event) ;
116 static void CallTearOffMenuActivateCallback(
117                         Widget wid,
118                         XEvent *event,
119 #if NeedWidePrototypes
120 			int origin) ;
121 #else
122 			unsigned short origin) ;
123 #endif	/*NeedWidePrototypes */
124 static void CallTearOffMenuDeactivateCallback(
125                         Widget wid,
126                         XEvent *event,
127 #if NeedWidePrototypes
128 			int origin) ;
129 #else
130 			unsigned short origin) ;
131 #endif	/*NeedWidePrototypes */
132 static void RemoveTearOffEventHandlers(
133 			Widget wid ) ;
134 static void DismissOnPostedFromDestroy(
135 			Widget w,
136 			XtPointer clientData,
137 			XtPointer callData ) ;
138 static void DisplayDestroyCallback (
139 			Widget w,
140 			XtPointer client_data,
141 			XtPointer call_data );
142 /********    End Static Function Declarations    ********/
143 
144 
145 static GC
InitXmTearOffXorGC(Widget wid)146 InitXmTearOffXorGC(
147 	Widget wid )
148 {
149     XGCValues gcv;
150     XtGCMask  mask;
151 
152     mask = GCFunction | GCLineWidth | GCSubwindowMode | GCCapStyle;
153     gcv.function = GXinvert;
154     gcv.line_width = 0;
155     gcv.cap_style = CapNotLast;
156     gcv.subwindow_mode = IncludeInferiors;
157 
158     return (XCreateGC (XtDisplay(wid), wid->core.screen->root,
159 		       mask, &gcv));
160 }
161 
162 static void
SetupOutline(Widget wid,GC gc,XSegment pOutline[],XEvent * event,Dimension delta_x,Dimension delta_y)163 SetupOutline(
164 	Widget wid,
165 	GC gc,
166 	XSegment pOutline[],
167 	XEvent *event,
168 	Dimension delta_x,
169 	Dimension delta_y)
170 {
171    Position x, y;
172    Dimension w, h;
173    int n = 0;
174    int i;
175 
176    x = event->xbutton.x_root - delta_x;
177    y = event->xbutton.y_root - delta_y;
178    w = wid->core.width;
179    h = wid->core.height;
180 
181    for(i=0; i<OUTLINE_WIDTH; i++)
182    {
183       pOutline[n].x1 = x;
184       pOutline[n].y1 = y;
185       pOutline[n].x2 = x + w - 1;
186       pOutline[n++].y2 = y;
187 
188       pOutline[n].x1 = x + w - 1;
189       pOutline[n].y1 = y;
190       pOutline[n].x2 = x + w - 1;
191       pOutline[n++].y2 = y + h - 1;
192 
193       pOutline[n].x1 = x + w - 1;
194       pOutline[n].y1 = y + h  - 1;
195       pOutline[n].x2 = x;
196       pOutline[n++].y2 = y + h - 1;
197 
198       pOutline[n].x1 = x;
199       pOutline[n].y1 = y + h  - 1;
200       pOutline[n].x2 = x;
201       pOutline[n++].y2 = y;
202 
203       x += 1;
204       y += 1;
205       w -= 2;
206       h -= 2;
207    }
208 
209    XDrawSegments(XtDisplay(wid), wid->core.screen->root, gc,
210 		 pOutline, SEGS_PER_DRAW);
211 }
212 
213 static void
EraseOutline(Widget wid,GC gc,XSegment * pOutline)214 EraseOutline(
215 	Widget wid,
216 	GC gc,
217 	XSegment *pOutline )
218 {
219    XDrawSegments(XtDisplay(wid), wid->core.screen->root, gc,
220 		 pOutline, SEGS_PER_DRAW);
221 }
222 
223 static void
MoveOutline(Widget wid,GC gc,XSegment * pOutline,XEvent * event,Dimension delta_x,Dimension delta_y)224 MoveOutline(
225 	Widget wid,
226 	GC gc,
227 	XSegment *pOutline,
228 	XEvent *event,
229 	Dimension delta_x,
230 	Dimension delta_y )
231 {
232    EraseOutline(wid, gc, pOutline);
233    SetupOutline(wid, gc, pOutline, event, delta_x, delta_y);
234 }
235 
236 static void
PullExposureEvents(Widget wid)237 PullExposureEvents(
238 	Widget wid )
239 {
240    XEvent      event;
241    /*
242     * Force the exposure events into the queue
243     */
244    XSync (XtDisplay(wid), False);
245 
246    /*
247     * Selectively extract the exposure events
248     */
249    while (XCheckMaskEvent (XtDisplay(wid), ExposureMask, &event))
250    {
251       /*
252        * Dispatch widget related event:
253        */
254       XtDispatchEvent (&event);
255    }
256 }
257 
258 static void
MoveOpaque(Widget wid,XEvent * event,Dimension delta_x,Dimension delta_y)259 MoveOpaque(
260 	Widget wid,
261 	XEvent *event,
262 	Dimension delta_x,
263 	Dimension delta_y )
264 {
265    /* Move the MenuShell */
266    XMoveWindow(XtDisplay(wid), XtWindow(XtParent(wid)),
267       event->xbutton.x_root - delta_x, event->xbutton.y_root - delta_y);
268 
269    /* cleanup exposed frame parts */
270    PullExposureEvents (wid);
271 }
272 
273 
274 #define CONFIG_GRAB_MASK (ButtonPressMask|ButtonReleaseMask|\
275 		     PointerMotionMask|PointerMotionHintMask|\
276 		     KeyPress|KeyRelease)
277 
278 #define POINTER_GRAB_MASK (ButtonPressMask|ButtonReleaseMask|\
279                    PointerMotionMask|PointerMotionHintMask)
280 
281 
282 static void
GetConfigEvent(Display * display,Window window,unsigned long mask,XEvent * event)283 GetConfigEvent(
284 	Display *display,
285 	Window window,
286 	unsigned long mask,
287 	XEvent *event )
288 {
289    Window root_ret, child_ret;
290    int root_x, root_y, win_x, win_y;
291    unsigned int mask_ret;
292 
293    /* Block until a motion, button, or key event comes in */
294    XWindowEvent(display, window, mask, event);
295 
296    if (event->type == MotionNotify &&
297         event->xmotion.is_hint == NotifyHint)
298    {
299        /*
300         * "Ack" the motion notify hint
301         */
302        if ((XQueryPointer (display, window, &root_ret,
303                &child_ret, &root_x, &root_y, &win_x,
304                &win_y, &mask_ret)))
305        {
306            /*
307             * The query pointer values say that the pointer
308             * moved to a new location.
309             */
310            event->xmotion.window = root_ret;
311            event->xmotion.subwindow = child_ret;
312            event->xmotion.x = root_x;
313            event->xmotion.y = root_y;
314            event->xmotion.x_root = root_x;
315            event->xmotion.y_root = root_y;
316        }
317    }
318 }
319 
320 /*ARGSUSED*/
321 static void
DisplayDestroyCallback(Widget w,XtPointer client_data,XtPointer call_data)322 DisplayDestroyCallback
323 	( Widget w,
324         XtPointer client_data,
325         XtPointer call_data )	/* unused */
326 {
327 	XFreeCursor(XtDisplay(w), (Cursor)client_data);
328 }
329 
330 static Cursor
GetTearOffCursor(Widget wid)331 GetTearOffCursor(
332        Widget wid )
333 {
334 	XmDisplay   dd = (XmDisplay) XmGetXmDisplay(XtDisplay(wid));
335 	Cursor TearOffCursor =
336 		((XmDisplayInfo *)(dd->display.displayInfo))->TearOffCursor;
337 
338 	if (0L == TearOffCursor)
339 		{
340 		/* create some data shared among all instances on this
341 		** display; the first one along can create it, and
342 		** any one can remove it; note no reference count
343 		*/
344         	TearOffCursor =
345 			XCreateFontCursor(XtDisplay(wid), XC_fleur);
346 		if (0L == TearOffCursor)
347 			TearOffCursor = XmGetMenuCursor(XtDisplay(wid));
348 		else
349 			XtAddCallback((Widget)dd, XtNdestroyCallback,
350 			  DisplayDestroyCallback,(XtPointer)TearOffCursor);
351 		((XmDisplayInfo *)(dd->display.displayInfo))->TearOffCursor =
352 			TearOffCursor;
353 		}
354 
355    return TearOffCursor;
356 }
357 
358 static Boolean
DoPlacement(Widget wid,XEvent * event)359 DoPlacement(
360        Widget wid,
361        XEvent *event )
362 {
363    XmRowColumnWidget rc = (XmRowColumnWidget) wid;
364    XSegment outline[SEGS_PER_DRAW];
365    Boolean placementDone;
366    KeyCode *KCancel;
367    KeySym keysym = osfXK_Cancel;
368    int num_keys, index;
369    XmKeyBinding keys;
370    Boolean moveOpaque = False;
371    Dimension delta_x, delta_y;
372    Dimension old_x_root = 0;
373    Dimension old_y_root = 0;
374    GC tearoffGC;
375 
376    /* Determine which keycodes are bound to osfXK_Cancel. */
377    num_keys = XmeVirtualToActualKeysyms(XtDisplay(rc), keysym, &keys);
378    KCancel = (KeyCode*) XtMalloc(num_keys * sizeof(KeyCode));
379    for (index = 0; index < num_keys; index++)
380      KCancel[index] = XKeysymToKeycode(XtDisplay(rc), keys[index].keysym);
381    XtFree((char*) keys);
382 
383    /* Regrab the pointer and keyboard to the root window.  Grab in Async
384     * mode to free up the input queues.
385     */
386    XGrabPointer(XtDisplay(rc), rc->core.screen->root, FALSE,
387         (unsigned int)POINTER_GRAB_MASK,
388         GrabModeAsync, GrabModeAsync, rc->core.screen->root,
389         GetTearOffCursor(wid), CurrentTime);
390 
391    XGrabKeyboard(XtDisplay(rc), rc->core.screen->root, FALSE,
392       GrabModeAsync, GrabModeAsync, CurrentTime);
393 
394    tearoffGC = InitXmTearOffXorGC((Widget)rc);
395 
396    delta_x = event->xbutton.x_root - XtX(XtParent(rc));
397    delta_y = event->xbutton.y_root - XtY(XtParent(rc));
398 
399    moveOpaque = _XmGetMoveOpaqueByScreen(XtScreen(rc));
400 
401    /* Set up a dummy event of the menu's current position in case the
402     * move-opaque is cancelled.
403     */
404    if (moveOpaque)
405    {
406      old_x_root = XtX(XtParent(rc));
407      old_y_root = XtY(XtParent(rc));
408      MoveOpaque((Widget)rc, event, delta_x, delta_y);
409    }
410    else
411       SetupOutline((Widget)rc, tearoffGC, outline, event, delta_x, delta_y);
412 
413    placementDone = FALSE;
414 
415    while (!placementDone)
416    {
417       GetConfigEvent (XtDisplay(rc), rc->core.screen->root, CONFIG_GRAB_MASK,
418          event);        /* ok to overwrite event? */
419 
420       switch (event->type)
421       {
422          case ButtonRelease:
423             if (event->xbutton.button == /*BDrag */ 2)
424             {
425 	       if (!moveOpaque)
426                   EraseOutline((Widget)rc, tearoffGC, outline);
427 	       else
428 		  /* Signal next menushell post to reposition */
429 		  XtX(XtParent(rc)) = XtY(XtParent(rc)) = 0;
430 
431                placementDone = TRUE;
432 	       event->xbutton.x_root -= delta_x;
433 	       event->xbutton.y_root -= delta_y;
434             }
435             break;
436 
437          case MotionNotify:
438 	    if (moveOpaque)
439 	       MoveOpaque((Widget)rc, event, delta_x, delta_y);
440 	    else
441                MoveOutline((Widget)rc, tearoffGC, outline, event,
442 			   delta_x, delta_y);
443             break;
444 
445          case KeyPress:
446 	    /* Shouldn't we be checking modifiers too??? */
447 	    for (index = 0; index < num_keys; index++)
448 	      if (event->xkey.keycode == KCancel[index])
449 		{
450 		  if (!moveOpaque)
451 		    EraseOutline((Widget)rc, tearoffGC, outline);
452 		  else
453 		    {
454 		      event->xbutton.x_root = old_x_root;
455 		      event->xbutton.y_root = old_y_root;
456 		      MoveOpaque((Widget)rc, event, 0, 0);
457 		    }
458 
459 		  XtFree((char*) KCancel);
460 		  return(FALSE);
461 		}
462             break;
463       }
464    }
465    XFreeGC(XtDisplay(rc), tearoffGC);
466 
467    XUngrabKeyboard(XtDisplay(rc), CurrentTime);
468    XUngrabPointer(XtDisplay(rc), CurrentTime);
469 
470    XtFree((char*) KCancel);
471 
472    return (TRUE);
473 }
474 
475 static void
CallTearOffMenuActivateCallback(Widget wid,XEvent * event,int origin)476 CallTearOffMenuActivateCallback(
477 	Widget wid,
478 	XEvent *event,
479 #if NeedWidePrototypes
480 	int origin )
481 #else
482 	unsigned short origin )
483 #endif	/*NeedWidePrototypes */
484 {
485    XmRowColumnWidget rc = (XmRowColumnWidget) wid;
486    XmRowColumnCallbackStruct callback;
487 
488    if (!rc->row_column.tear_off_activated_callback)
489       return;
490 
491    callback.reason = XmCR_TEAR_OFF_ACTIVATE;
492    callback.event  = event;
493    callback.widget = NULL;	/* these next two fields are spec'd NULL */
494    callback.data   = (char *)(unsigned long) origin;
495    callback.callbackstruct = NULL;
496    XtCallCallbackList ((Widget)rc, rc->row_column.tear_off_activated_callback,
497       &callback);
498 }
499 
500 static void
CallTearOffMenuDeactivateCallback(Widget wid,XEvent * event,int origin)501 CallTearOffMenuDeactivateCallback(
502 	Widget wid,
503 	XEvent *event,
504 #if NeedWidePrototypes
505 	int origin )
506 #else
507 	unsigned short origin )
508 #endif	/*NeedWidePrototypes */
509 {
510    XmRowColumnWidget rc = (XmRowColumnWidget) wid;
511    XmRowColumnCallbackStruct callback;
512 
513    if (!rc->row_column.tear_off_deactivated_callback)
514       return;
515 
516    callback.reason = XmCR_TEAR_OFF_DEACTIVATE;
517    callback.event  = event;
518    callback.widget = NULL;	/* these next two fields are spec'd NULL */
519    callback.data   = (char *)(unsigned long) origin;
520    callback.callbackstruct = NULL;
521    XtCallCallbackList ((Widget) rc,
522       rc->row_column.tear_off_deactivated_callback, &callback);
523 }
524 
525 /*
526  * This event handler is added to label widgets and separator widgets inside
527  * a tearoff menu pane.  This enables the RowColumn to watch for the button
528  * presses inside these widgets and to 'do the right thing'.
529  */
530 /*ARGSUSED*/
531 void
_XmTearOffBtnDownEventHandler(Widget reportingWidget,XtPointer data,XEvent * event,Boolean * cont)532 _XmTearOffBtnDownEventHandler(
533         Widget reportingWidget,
534         XtPointer data,		/* unused */
535         XEvent *event,
536         Boolean *cont )
537 {
538     Widget parent;
539 
540     if (reportingWidget)
541     {
542        /* make sure only called for widgets inside a menu rowcolumn */
543 	  if ((XmIsRowColumn(parent = XtParent(reportingWidget))) &&
544 	      (RC_Type(parent) != XmWORK_AREA))
545 	  {
546 	      _XmMenuBtnDown (parent, event, NULL, 0);
547 	  }
548     }
549     *cont = True;
550 }
551 
552 /*ARGSUSED*/
553 void
_XmTearOffBtnUpEventHandler(Widget reportingWidget,XtPointer data,XEvent * event,Boolean * cont)554 _XmTearOffBtnUpEventHandler(
555         Widget reportingWidget,
556         XtPointer data,		/* unused */
557         XEvent *event,
558         Boolean *cont )
559 {
560     Widget parent;
561 
562     if (reportingWidget)
563     {
564        /* make sure only called for widgets inside a menu rowcolumn */
565 	  if ((XmIsRowColumn(parent = XtParent(reportingWidget))) &&
566 	      (RC_Type(parent) != XmWORK_AREA))
567 	  {
568 	      _XmMenuBtnUp (parent, event, NULL, 0);
569 	  }
570     }
571     *cont = True;
572 }
573 
574 void
_XmAddTearOffEventHandlers(Widget wid)575 _XmAddTearOffEventHandlers(
576 	Widget wid )
577 {
578     XmRowColumnWidget rc = (XmRowColumnWidget) wid;
579     Widget child;
580     int i;
581     Cursor cursor = XmGetMenuCursor(XtDisplay(wid));
582     XmMenuSavvyTrait	mtrait;
583 
584     for (i=0; i < rc->composite.num_children; i++)
585     {
586 	child = rc->composite.children[i];
587 
588 	mtrait = (XmMenuSavvyTrait) XmeTraitGet(XtClass(child), XmQTmenuSavvy);
589 
590 	/*
591 	 * The label and separator widgets do not care about
592 	 * button presses.  Add an event handler for these widgets
593 	 * so that the tearoff RowColumn is alerted about presses in
594 	 * these widgets.
595 	 *
596 	 * This now determines the right widgets to install the
597 	 * handlers on by using the MenuSavvy trait.  If the
598 	 * widget can't supply an activateCB name,  then it is
599 	 * assumed that the widget isn't activatable.
600 	 *
601 	 */
602 	if (! XmIsGadget(child) &&
603 	    (mtrait == (XmMenuSavvyTrait) NULL ||
604 	     mtrait -> getActivateCBName ==
605 	     (XmMenuSavvyGetActivateCBNameProc) NULL))
606 	  {
607     	    XtAddEventHandler(child, ButtonPressMask, False,
608 			      _XmTearOffBtnDownEventHandler,  NULL);
609 	    XtAddEventHandler(child, ButtonReleaseMask, False,
610 			      _XmTearOffBtnUpEventHandler,  NULL);
611 	  }
612 
613 	if (XtIsWidget(child))
614 	   XtGrabButton (child, (int)AnyButton, AnyModifier, TRUE,
615 	      (unsigned int)ButtonPressMask, GrabModeAsync, GrabModeAsync,
616 	      None, cursor);
617     }
618 }
619 
620 
621 static void
RemoveTearOffEventHandlers(Widget wid)622 RemoveTearOffEventHandlers(
623 	Widget wid )
624 {
625     XmRowColumnWidget rc = (XmRowColumnWidget) wid;
626     Widget child;
627     int i;
628 
629     for (i=0; i < rc->composite.num_children; i++)
630     {
631 	child = rc->composite.children[i];
632 
633 	/*
634 	 * Remove the event handlers on the label and separator widgets.
635 	 */
636 	if ((XtClass(child) == xmLabelWidgetClass) ||
637 	    _XmIsFastSubclass(XtClass(child), XmSEPARATOR_BIT))
638 	{
639 
640 	    XtRemoveEventHandler(child, ButtonPressMask, False,
641 				 _XmTearOffBtnDownEventHandler,  NULL);
642 	    XtRemoveEventHandler(child, ButtonReleaseMask, False,
643 				 _XmTearOffBtnUpEventHandler,  NULL);
644 	}
645 
646 	if (XtIsWidget(child) && !child->core.being_destroyed)
647 	   XtUngrabButton(child, (unsigned int)AnyButton, AnyModifier);
648     }
649 }
650 
651 void
_XmDestroyTearOffShell(Widget wid)652 _XmDestroyTearOffShell(
653 	Widget wid )
654 {
655    TopLevelShellWidget to_shell = (TopLevelShellWidget)wid;
656 
657    to_shell->composite.num_children = 0;
658 
659    if (to_shell->core.being_destroyed)
660      return;
661 
662    XtPopdown((Widget)to_shell);
663 
664    if (to_shell->core.background_pixmap != XtUnspecifiedPixmap)
665    {
666      XFreePixmap(XtDisplay(to_shell), to_shell->core.background_pixmap);
667      to_shell->core.background_pixmap = XtUnspecifiedPixmap;
668    }
669 
670    /* Before destroying the shell, force XtSetKeyboardFocus to remove any
671    ** XmNdestroyCallbacks on other widgets which name this shell as client_data
672    */
673    XtSetKeyboardFocus((Widget)to_shell, NULL);
674 
675    XtDestroyWidget((Widget)to_shell);
676 }
677 
678 /*ARGSUSED*/
679 void
_XmDismissTearOff(Widget shell,XtPointer closure,XtPointer call_data)680 _XmDismissTearOff(
681 	Widget shell,
682         XtPointer closure,
683         XtPointer call_data)	/* unused */
684 {
685    XmRowColumnWidget submenu = NULL;
686 
687    /* The first time a pane is torn, there's no tear off shell to destroy.
688     */
689    if (!shell ||
690        !(((ShellWidget)shell)->composite.num_children) ||
691        !(submenu =
692 	 (XmRowColumnWidget)(((ShellWidget)shell)->composite.children[0])) ||
693        !RC_TornOff(submenu))
694       return;
695 
696    RC_SetTornOff(submenu, FALSE);
697    RC_SetTearOffActive(submenu, FALSE);
698 
699    /* Unhighlight the active child and clear the focus for the next post */
700    if (submenu->manager.active_child)
701    {
702       /* update visible focus/highlighting */
703       if (XmIsPrimitive(submenu->manager.active_child))
704       {
705 	  (*(((XmPrimitiveClassRec *)XtClass(submenu->manager.
706 	  active_child))->primitive_class.border_unhighlight))
707 	  (submenu->manager.active_child);
708       }
709       else if (XmIsGadget(submenu->manager.active_child))
710       {
711 	  (*(((XmGadgetClassRec *)XtClass(submenu->manager.
712 	  active_child))->gadget_class.border_unhighlight))
713 	  (submenu->manager.active_child);
714       }
715       /* update internal focus state */
716       _XmClearFocusPath((Widget) submenu);
717 
718       /* Clear the Intrinsic focus from the tear off widget hierarchy.
719        * Necessary to remove the FocusChangeCallback from the active item.
720        */
721       XtSetKeyboardFocus(shell, NULL);
722    }
723 
724    if (XmIsMenuShell(shell))
725    {
726       /* Shared menupanes require extra manipulation.  We gotta be able
727        * to optimize this when there's more time.
728        */
729       if ((((ShellWidget)shell)->composite.num_children) > 1)
730          XUnmapWindow(XtDisplay(submenu), XtWindow(submenu));
731 
732       _XmDestroyTearOffShell(RC_ParentShell(submenu));
733 
734       /* remove orphan destroy callback from postFromWidget */
735       XtRemoveCallback(submenu->row_column.tear_off_lastSelectToplevel,
736 	 XtNdestroyCallback, (XtCallbackProc)DismissOnPostedFromDestroy,
737 	 (XtPointer) RC_ParentShell(submenu));
738    }
739    else	/* toplevel shell! */
740    {
741       /* Shared menupanes require extra manipulation.  We gotta be able
742        * to optimize this when there's more time.
743        */
744       if ((((ShellWidget)RC_ParentShell(submenu))->composite.num_children) > 1)
745          XUnmapWindow(XtDisplay(submenu), XtWindow(submenu));
746 
747 
748       _XmDestroyTearOffShell(shell);
749       if (submenu)
750       {
751 	 XtParent(submenu) = RC_ParentShell(submenu);
752          XReparentWindow(XtDisplay(submenu), XtWindow(submenu),
753 	    XtWindow(XtParent(submenu)), XtX(submenu), XtY(submenu));
754          submenu->core.mapped_when_managed = False;
755          submenu->core.managed = False;
756          if (RC_TearOffControl(submenu))
757           XtManageChild(RC_TearOffControl(submenu));
758       }
759 
760       /* Only Call the callbacks if we're not popped up (parent is not a
761        * menushell).  Popping up the shell caused the unmap & deactivate
762        * callbacks to be called already.
763        */
764       _XmCallRowColumnUnmapCallback((Widget)submenu, NULL);
765       CallTearOffMenuDeactivateCallback((Widget)submenu, (XEvent *)closure,
766 	 DESTROY_TEAR_OFF);
767       RemoveTearOffEventHandlers ((Widget) submenu);
768 
769       /* remove orphan destroy callback from postFromWidget */
770       XtRemoveCallback(submenu->row_column.tear_off_lastSelectToplevel,
771 	 XtNdestroyCallback, (XtCallbackProc)DismissOnPostedFromDestroy,
772 	 (XtPointer) shell);
773    }
774 }
775 
776 /*ARGSUSED*/
777 static void
DismissOnPostedFromDestroy(Widget w,XtPointer clientData,XtPointer callData)778 DismissOnPostedFromDestroy(
779 	Widget w,		/* unused */
780 	XtPointer clientData,
781 	XtPointer callData )	/* unused */
782 {
783    _XmDismissTearOff((Widget)clientData, NULL, NULL);
784 }
785 
786 #define DEFAULT_TEAR_OFF_TITLE ""
787 #define TEAR_OFF_TITLE_SUFFIX " Tear-off"
788 #define TEAR_OFF_CHARSET "ISO8859-1"
789 
790 void
_XmTearOffInitiate(Widget wid,XEvent * event)791 _XmTearOffInitiate(
792         Widget wid,
793         XEvent *event )
794 {
795    enum { XmAWM_DELETE_WINDOW, XmA_MOTIF_WM_HINTS, NUM_ATOMS };
796    static char *atom_names[] = { XmIWM_DELETE_WINDOW, _XA_MOTIF_WM_HINTS };
797 
798    XmRowColumnWidget submenu = (XmRowColumnWidget)wid;
799    Widget cb;
800    XmRowColumnWidget rc;
801    XmString label_xms;
802    unsigned char label_type;
803    Arg args[20];
804    ShellWidget to_shell;
805    Widget toplevel;
806    PropMwmHints *rprop = NULL;     /* receive pointer */
807    PropMwmHints sprop;          /* send structure */
808    Atom atoms[XtNumber(atom_names)];
809    Atom actual_type;
810    int actual_format;
811    unsigned long num_items, bytes_after;
812    XEvent newEvent;
813    XmMenuState mst = _XmGetMenuState((Widget)wid);
814    XtWidgetProc proc;
815 
816    if (IsPulldown(submenu))
817       cb = RC_CascadeBtn(submenu);
818    else
819       cb = NULL;
820 
821    if (RC_TearOffModel(submenu) == XmTEAR_OFF_DISABLED)
822       return;
823 
824    /* The submenu must be posted before we allow the tear off action */
825    if (!(XmIsMenuShell(XtParent(submenu)) &&
826          ((XmMenuShellWidget)XtParent(submenu))->shell.popped_up))
827       return;
828 
829    if (XmIsRowColumn(wid))
830       rc = (XmRowColumnWidget)wid;
831    else
832       rc = (XmRowColumnWidget) XtParent (wid);
833    _XmGetActiveTopLevelMenu((Widget)rc, (Widget *)&rc);
834 
835    /* Set up the important event fields for the new position */
836    memcpy(&newEvent, event, sizeof(XButtonEvent));
837 
838    /* if it's from a BDrag, find the eventual destination */
839    if ((event->xbutton.type == ButtonPress) &&
840        (event->xbutton.button == 2/*BDrag*/))
841    {
842       if (!DoPlacement((Widget) submenu, &newEvent))     /* Cancelled! */
843       {
844          /* restore grabs back to menu and reset menu cursor? */
845 
846          /* If the toplevel menu is an option menu, the grabs are attached to
847           * the top submenu else leave it as the menubar or popup.
848           */
849          if (IsOption(rc))
850             rc = (XmRowColumnWidget)RC_OptionSubMenu(rc);
851 
852          _XmGrabPointer((Widget) rc, True,
853             (unsigned int)(ButtonPressMask | ButtonReleaseMask |
854             EnterWindowMask | LeaveWindowMask),
855             GrabModeSync, GrabModeAsync, None,
856             XmGetMenuCursor(XtDisplay(rc)), CurrentTime);
857 
858          _XmGrabKeyboard((Widget)rc, True, GrabModeSync, GrabModeSync,
859             CurrentTime);
860          XAllowEvents(XtDisplay(rc), AsyncKeyboard, CurrentTime);
861 
862          XAllowEvents(XtDisplay(rc), SyncPointer, CurrentTime);
863 
864          /* and reset the input focus to the leaf submenu's menushell */
865          _XmMenuFocus(XtParent(submenu), XmMENU_MIDDLE, CurrentTime);
866          return;
867       }
868    }
869    else
870    {
871       newEvent.xbutton.x_root = XtX(XtParent(submenu));
872       newEvent.xbutton.y_root = XtY(XtParent(submenu));
873    }
874 
875 
876    /* If the submenu already exists, take it down for a retear */
877    _XmDismissTearOff(XtParent(submenu), (XtPointer)event, NULL);
878 
879    /* Shared menupanes require extra manipulation.  We gotta be able
880     * to optimize this when there's more time.
881     */
882    if ((((ShellWidget)XtParent(submenu))->composite.num_children) > 1)
883       XMapWindow(XtDisplay(submenu), XtWindow(submenu));
884 
885    /*
886     * Popdown the menu hierarchy!
887     */
888    /* First save the GetPostedFromWidget */
889    if (mst->RC_LastSelectToplevel)
890    {
891       submenu->row_column.tear_off_lastSelectToplevel =
892 	  mst->RC_LastSelectToplevel;
893    }
894    else
895       if (RC_TornOff(rc) && RC_TearOffActive(rc))
896          submenu->row_column.tear_off_lastSelectToplevel =
897             rc->row_column.tear_off_lastSelectToplevel;
898       else
899       {
900 	/* Fix CR 7983 - Assigning NULL RC_CascadeBtn causes core dump */
901 	 if (IsPopup(submenu) && RC_CascadeBtn(submenu))
902 	    submenu->row_column.tear_off_lastSelectToplevel =
903 	       RC_CascadeBtn(submenu);
904 	 else
905 	    submenu->row_column.tear_off_lastSelectToplevel = (Widget)rc;
906       }
907 
908    if (!XmIsMenuShell(XtParent(rc)))    /* MenuBar or TearOff */
909       (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)->
910          menu_shell_class.popdownEveryone))(RC_PopupPosted(rc), event, NULL,
911             NULL);
912    else
913       (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)->
914          menu_shell_class.popdownEveryone))(XtParent(rc), event, NULL, NULL);
915 
916    _XmSetInDragMode((Widget) rc, False);
917 
918    /* popdownEveryone() calls popdownDone() which will call MenuDisarm() for
919     * each pane.  We need to take care of non-popup toplevel menus (bar/option).
920     */
921    (*(((XmRowColumnClassRec *)XtClass(rc))->row_column_class.
922       menuProcedures)) (XmMENU_DISARM, (Widget) rc);
923 
924    _XmMenuFocus( (Widget) rc, XmMENU_END, CurrentTime);
925    XtUngrabPointer( (Widget) rc, CurrentTime);
926 
927    XtUnmanageChild(RC_TearOffControl(submenu));
928 
929    /* Use the toplevel application shell for the parent of the new transient
930     * shell.  This way, the submenu won't inadvertently be destroyed on
931     * associated-widget's shell destruction.  We'll make the connection to
932     * associate-widget with XmNtransientFor.
933     */
934    for (toplevel = wid; XtParent(toplevel); )
935       toplevel = XtParent(toplevel);
936 
937    XtSetArg(args[0], XmNdeleteResponse, XmDO_NOTHING);
938    /* system & title */
939    XtSetArg(args[1], XmNmwmDecorations,
940       MWM_DECOR_BORDER | MWM_DECOR_TITLE | MWM_DECOR_MENU);
941    XtSetArg(args[2], XmNmwmFunctions, MWM_FUNC_MOVE | MWM_FUNC_CLOSE);
942    /* need shell resize for pulldown to resize correctly when reparenting
943     * back without tear off control managed.
944     */
945    XtSetArg(args[3], XmNallowShellResize, True);
946    if (XmIsRowColumn(submenu->row_column.tear_off_lastSelectToplevel) &&
947        IsPopup(submenu->row_column.tear_off_lastSelectToplevel))
948    {
949       XtSetArg(args[4], XmNtransientFor,
950 	 _XmFindTopMostShell(RC_CascadeBtn(
951 	    submenu->row_column.tear_off_lastSelectToplevel)));
952    }
953    else
954       XtSetArg(args[4], XmNtransientFor,
955 	 _XmFindTopMostShell(submenu->row_column.tear_off_lastSelectToplevel));
956    /* Sorry, still a menu - explicit mode only so focus doesn't screw up */
957    XtSetArg(args[5], XmNkeyboardFocusPolicy, XmEXPLICIT);
958    /* Start Fix CR 5459
959     * It is important to set the shell's visual information (visual, colormap,
960     * depth) to match the menu whose parent it is becoming.  If you fail to do
961     * so, then when the user brings up a second copy of a menu that is already
962     * posted, a BADMATCH error occurs.
963     */
964    XtSetArg(args[6], XmNvisual,
965 	    ((XmMenuShellWidget)(XtParent(wid)))->shell.visual);
966    XtSetArg(args[7], XmNcolormap,
967 	    ((XmMenuShellWidget)(XtParent(wid)))->core.colormap);
968    XtSetArg(args[8], XmNdepth,
969 	    ((XmMenuShellWidget)(XtParent(wid)))->core.depth);
970    /* End Fix CR 5459 */
971    to_shell = (ShellWidget)XtCreatePopupShell(DEFAULT_TEAR_OFF_TITLE,
972       transientShellWidgetClass,
973       toplevel, args, 9);
974 
975    if (RC_TearOffTitle(submenu) != NULL)
976      XmeSetWMShellTitle(RC_TearOffTitle(submenu), (Widget) to_shell);
977    else if (cb) {
978      Widget lwid, mwid;
979 
980      /* If the top menu of the active menu hierarchy is an option menu,
981       * use the option menu's label for the name of the shell.
982       */
983      mwid = XmGetPostedFromWidget(XtParent(cb));
984      if (mwid && IsOption(mwid))
985        lwid = XmOptionLabelGadget(mwid);
986      else
987        lwid = cb;
988 
989      XtSetArg(args[0], XmNlabelType, &label_type);
990      XtGetValues((Widget)lwid, args, 1);
991 
992      /* better be a compound string! */
993      if (label_type == XmSTRING || label_type == XmPIXMAP_AND_STRING)
994        {
995 	 XmString title_xms, suffix_xms;
996 
997 	 XtSetArg(args[0], XmNlabelString, &label_xms);
998 	 XtGetValues((Widget)lwid, args, 1);
999 
1000  	 suffix_xms = XmStringCreate(TEAR_OFF_TITLE_SUFFIX, TEAR_OFF_CHARSET);
1001  	 title_xms = XmStringConcatAndFree(label_xms, suffix_xms);
1002 
1003  	 XmeSetWMShellTitle(title_xms, (Widget)to_shell);
1004 
1005  	 XmStringFree(title_xms);
1006        }
1007    }
1008 
1009    assert(XtNumber(atom_names) == NUM_ATOMS);
1010    XInternAtoms(XtDisplay(to_shell), atom_names, XtNumber(atom_names), FALSE,
1011 		atoms);
1012 
1013    XmAddWMProtocolCallback((Widget)to_shell, atoms[XmAWM_DELETE_WINDOW],
1014       _XmDismissTearOff, NULL);
1015 
1016    /* Add internal destroy callback to postFromWidget to eliminate orphan tear
1017     * off menus.
1018     */
1019    XtAddCallback(submenu->row_column.tear_off_lastSelectToplevel,
1020       XtNdestroyCallback, (XtCallbackProc)DismissOnPostedFromDestroy,
1021       (XtPointer) to_shell);
1022 
1023    RC_ParentShell(submenu) = XtParent(submenu);
1024    XtParent(submenu) = (Widget)to_shell;
1025 
1026    /* Needs to be set before the user gets a callback */
1027    RC_SetTornOff(submenu, TRUE);
1028    RC_SetTearOffActive(submenu, TRUE);
1029 
1030    _XmAddTearOffEventHandlers ((Widget) submenu);
1031    CallTearOffMenuActivateCallback((Widget)submenu, event,
1032       CREATE_TEAR_OFF);
1033    _XmCallRowColumnMapCallback((Widget)submenu, event);
1034 
1035    /* To get Traversal: _XmGetManagedInfo() to work correctly */
1036    submenu->core.mapped_when_managed = True;
1037    XtManageChild((Widget)submenu);
1038 
1039    /* Insert submenu into the new toplevel shell */
1040    _XmProcessLock();
1041    proc = ((TransientShellWidgetClass)transientShellWidgetClass)->
1042 		composite_class.insert_child;
1043    _XmProcessUnlock();
1044    (*proc)((Widget)submenu);
1045 
1046    /* Quick!  Configure the size (and location) of the shell before managing
1047     * so submenu doesn't get resized.
1048     */
1049    XmeConfigureObject((Widget) to_shell,
1050       newEvent.xbutton.x_root, newEvent.xbutton.y_root,
1051       XtWidth(submenu), XtHeight(submenu), XtBorderWidth(to_shell));
1052 
1053    /* Call change_managed routine to set up focus info */
1054    _XmProcessLock();
1055    proc = ((TransientShellWidgetClass)transientShellWidgetClass)->
1056 		composite_class.change_managed;
1057    _XmProcessUnlock();
1058    (*proc)((Widget)to_shell);
1059 
1060    XtRealizeWidget((Widget)to_shell);
1061 
1062    /* Wait until after to_shell realize to set the focus */
1063    XmProcessTraversal((Widget)submenu, XmTRAVERSE_CURRENT);
1064 
1065    XGetWindowProperty(XtDisplay(to_shell), XtWindow(to_shell),
1066 		      atoms[XmA_MOTIF_WM_HINTS], 0,
1067 		      PROP_MWM_HINTS_ELEMENTS, False,
1068 		      atoms[XmA_MOTIF_WM_HINTS],
1069 		      &actual_type, &actual_format, &num_items, &bytes_after,
1070 		      (unsigned char **)&rprop);
1071 
1072    if ((actual_type != atoms[XmA_MOTIF_WM_HINTS]) ||
1073        (actual_format != 32) ||
1074        (num_items < PROP_MOTIF_WM_INFO_ELEMENTS))
1075    {
1076        if (rprop != NULL) XFree((char *)rprop);
1077    }
1078    else
1079    {
1080       bzero((void *)&sprop, sizeof(sprop));
1081       /* Fix for 9346,  use sizeof(long) to calculate total
1082 	 size of block from get property */
1083       memcpy(&sprop, rprop, (size_t)sizeof(long) * num_items);
1084       if (rprop != NULL) XFree((char *)rprop);
1085 
1086       sprop.flags |= MWM_HINTS_STATUS;
1087       sprop.status |= MWM_TEAROFF_WINDOW;
1088       XChangeProperty(XtDisplay(to_shell), XtWindow(to_shell),
1089 		      atoms[XmA_MOTIF_WM_HINTS], atoms[XmA_MOTIF_WM_HINTS], 32,
1090 		      PropModeReplace,
1091 		      (unsigned char *) &sprop, PROP_MWM_HINTS_ELEMENTS);
1092    }
1093 
1094    /* Notify the server of the change */
1095    XReparentWindow(XtDisplay(to_shell), XtWindow(submenu), XtWindow(to_shell),
1096      0, 0);
1097 
1098    XtPopup((Widget)to_shell, XtGrabNone);
1099 
1100    RC_SetArmed (submenu, FALSE);
1101 
1102    RC_SetTearOffDirty(submenu, FALSE);
1103 }
1104 
1105 Boolean
_XmIsTearOffShellDescendant(Widget wid)1106 _XmIsTearOffShellDescendant(
1107 	Widget wid )
1108 {
1109    XmRowColumnWidget rc = (XmRowColumnWidget)wid;
1110    Widget cb;
1111 
1112    while (rc && (IsPulldown(rc) || IsPopup(rc)) && XtIsShell(XtParent(rc)))
1113    {
1114       if (RC_TearOffActive(rc))
1115          return(True);
1116 
1117       /* Popup is the top! "cascadeBtn" is postFromWidget! */
1118       if (IsPopup(rc))
1119 	 break;
1120 
1121       if (!(cb = RC_CascadeBtn(rc)))
1122          break;
1123       rc = (XmRowColumnWidget)XtParent(cb);
1124    }
1125 
1126    return (False);
1127 }
1128 
1129 void
_XmLowerTearOffObscuringPoppingDownPanes(Widget ancestor,Widget tearOff)1130 _XmLowerTearOffObscuringPoppingDownPanes(
1131         Widget ancestor,
1132 	Widget tearOff )
1133 {
1134    XRectangle tearOff_rect, intersect_rect;
1135    ShellWidget shell;
1136 
1137    _XmSetRect(&tearOff_rect, tearOff);
1138    if (IsBar(ancestor) || IsOption(ancestor))
1139    {
1140       if ((shell = (ShellWidget)RC_PopupPosted(ancestor)) != NULL)
1141 	 ancestor = shell->composite.children[0];
1142    }
1143 
1144    while (ancestor && (IsPulldown(ancestor) || IsPopup(ancestor)))
1145    {
1146       if (_XmIntersectRect( &tearOff_rect, ancestor, &intersect_rect ))
1147       {
1148  	 XtUnmapWidget(XtParent(ancestor));
1149 	 RC_SetTearOffDirty(tearOff, True);
1150       }
1151       if ((shell = (ShellWidget)RC_PopupPosted(ancestor)) != NULL)
1152 	 ancestor = shell->composite.children[0];
1153       else
1154 	 break;
1155    }
1156    if (RC_TearOffDirty(tearOff))
1157       XFlush(XtDisplay(ancestor));
1158 }
1159 
1160 /*ARGSUSED*/
1161 void
_XmRestoreExcludedTearOffToToplevelShell(Widget wid,XEvent * event)1162 _XmRestoreExcludedTearOffToToplevelShell(
1163         Widget wid,
1164 	XEvent *event )
1165 {
1166    int i;
1167    Widget pane;
1168    XmDisplay dd = (XmDisplay)XmGetXmDisplay(XtDisplay(wid));
1169    XmExcludedParentPaneRec *excPP =
1170 	&(((XmDisplayInfo *)(dd->display.displayInfo))->excParentPane);
1171 
1172    for(i=0; i < excPP->num_panes; i++)
1173    {
1174       if ((pane = (excPP->pane)[i]) != NULL)
1175       {
1176 	 /* Reset to NULL first so that _XmRestoreTearOffToToplevelShell()
1177 	  * doesn't prematurely abort.
1178 	  */
1179 	 (excPP->pane)[i] = NULL;
1180 	 _XmRestoreTearOffToToplevelShell(pane, event);
1181       }
1182       else
1183 	 break;
1184    }
1185    excPP->num_panes = 0;
1186 }
1187 
1188 /*
1189  * The menupane was just posted, it's current parent is the original menushell.
1190  * Reparent the pane back to the tear off toplevel shell.
1191  */
1192 void
_XmRestoreTearOffToToplevelShell(Widget wid,XEvent * event)1193 _XmRestoreTearOffToToplevelShell(
1194         Widget wid,
1195 	XEvent *event )
1196 {
1197    XmRowColumnWidget rowcol = (XmRowColumnWidget)wid;
1198    XtGeometryResult answer;
1199    Dimension almostWidth, almostHeight;
1200    int i;
1201    XmDisplay dd = (XmDisplay)XmGetXmDisplay(XtDisplay(wid));
1202    XmExcludedParentPaneRec *excPP =
1203 	&(((XmDisplayInfo *)(dd->display.displayInfo))->excParentPane);
1204 
1205    for(i=0; i < excPP->num_panes; i++)
1206       if ((Widget)rowcol == (excPP->pane)[i])
1207 	 return;
1208 
1209    if (RC_TornOff(rowcol) && !RC_TearOffActive(rowcol))
1210    {
1211       ShellWidget shell;
1212 
1213       /* Unmanage the toc before reparenting to preserve the focus item. */
1214       XtUnmanageChild(RC_TearOffControl(rowcol));
1215 
1216       /* In case we're dealing with a shared menushell, the rowcol is kept
1217        * managed.  We need to reforce the pane to be managed so that the
1218        * pane's geometry is recalculated.  This allows the tear off to be
1219        * forced larger by mwm so that the title is not clipped.  It's
1220        * done here to minimize the lag time/flashing.
1221        */
1222       XtUnmanageChild((Widget)rowcol);
1223 
1224       /* swap parents to the toplevel shell */
1225       shell = (ShellWidget)XtParent(rowcol);
1226       XtParent(rowcol) = RC_ParentShell(rowcol);
1227       RC_ParentShell(rowcol) = (Widget)shell;
1228       RC_SetTearOffActive(rowcol, TRUE);
1229 
1230       /* Sync up the server */
1231       XReparentWindow(XtDisplay(shell), XtWindow(rowcol),
1232          XtWindow(XtParent(rowcol)), 0, 0);
1233 
1234       XFlush(XtDisplay(shell));
1235 
1236       if (XtParent(rowcol)->core.background_pixmap != XtUnspecifiedPixmap)
1237       {
1238          XFreePixmap(XtDisplay(XtParent(rowcol)),
1239             XtParent(rowcol)->core.background_pixmap);
1240          XtParent(rowcol)->core.background_pixmap = XtUnspecifiedPixmap;
1241       }
1242 
1243       /* The menupost that reparented the pane back to the menushell has
1244        * wiped out the active_child.  We need to restore it.
1245        * Check this out if FocusPolicy == XmPOINTER!
1246        */
1247       rowcol->manager.active_child = _XmGetActiveItem((Widget)rowcol);
1248 
1249       _XmAddTearOffEventHandlers ((Widget) rowcol);
1250 
1251       /* Restore lastSelectToplevel as if the (torn) menu is posted */
1252       if (IsPulldown(rowcol))
1253 	 rowcol->row_column.lastSelectToplevel =
1254 	    rowcol->row_column.tear_off_lastSelectToplevel;
1255       else /* IsPopup */
1256 	 RC_CascadeBtn(rowcol) =
1257 	    rowcol->row_column.tear_off_lastSelectToplevel;
1258 
1259       CallTearOffMenuActivateCallback((Widget)rowcol, event,
1260 	 RESTORE_TEAR_OFF_TO_TOPLEVEL_SHELL);
1261       _XmCallRowColumnMapCallback((Widget)rowcol, event);
1262 
1263       /*
1264        * In case the rowcolumn's geometry has changed, make a resize
1265        * request to the top level shell so that it can changed.  All
1266        * geometry requests were handled through the menushell and the
1267        * top level shell was left unchanged.
1268        */
1269       answer = XtMakeResizeRequest (XtParent(rowcol), XtWidth(rowcol),
1270 				    XtHeight(rowcol), &almostWidth,
1271 				    &almostHeight);
1272 
1273       if (answer == XtGeometryAlmost)
1274 	  answer = XtMakeResizeRequest (XtParent(rowcol), almostWidth,
1275 					almostHeight, NULL, NULL);
1276 
1277 
1278 
1279       /* As in _XmTearOffInitiate(), To get Traversal: _XmGetManagedInfo()
1280        * to work correctly.
1281        */
1282       rowcol->core.mapped_when_managed = True;
1283       XtManageChild((Widget)rowcol);
1284 
1285       /* rehighlight the previous focus item */
1286       XmProcessTraversal(rowcol->row_column.tear_off_focus_item,
1287 	 XmTRAVERSE_CURRENT);
1288    }
1289 }
1290 
1291 void
_XmRestoreTearOffToMenuShell(Widget wid,XEvent * event)1292 _XmRestoreTearOffToMenuShell(
1293         Widget wid,
1294 	XEvent *event )
1295 {
1296    XmRowColumnWidget submenu = (XmRowColumnWidget) wid;
1297    XmMenuState mst = _XmGetMenuState((Widget)wid);
1298    XtExposeProc expose;
1299    Boolean wasDirty = False;
1300 
1301    if (RC_TornOff(submenu) && RC_TearOffActive(submenu))
1302    {
1303       ShellWidget shell;
1304       GC gc;
1305       XGCValues values;
1306       int i;
1307       Widget child;
1308 
1309       /* If the pane was previously obscured, it may require redrawing
1310        * before taking a pixmap snapshot.
1311        * Note: event could be NULL on right arrow browse through menubar back
1312        *   to this submenu.
1313        */
1314       if (RC_TearOffDirty(submenu) ||
1315 	  (event && (event->type == ButtonPress) &&
1316 	   (event->xbutton.time == mst->RC_ReplayInfo.time) &&
1317 	   (mst->RC_ReplayInfo.toplevel_menu == (Widget)submenu)) ||
1318 	  XmeFocusIsInShell((Widget)submenu))
1319       {
1320 	 RC_SetTearOffDirty(submenu, False);
1321          wasDirty = True;
1322 
1323 	 /* First make sure that the previous active child is unhighlighted.
1324 	  * In the tear off's inactive state, no children should be
1325 	  * highlighted.
1326 	  */
1327 	 if ((child = submenu->manager.active_child) != NULL)
1328 	 {
1329 	    if (XtIsWidget(child))
1330 	       (*(((XmPrimitiveClassRec *)XtClass(child))->
1331 		  primitive_class.border_unhighlight))(child);
1332 	    else
1333 	       (*(((XmGadgetClassRec *)XtClass(child))->
1334 		  gadget_class.border_unhighlight))(child);
1335 	 }
1336 
1337 	 /* Redraw the submenu and its gadgets */
1338 	 _XmProcessLock();
1339 	 expose = XtClass(submenu)->core_class.expose;
1340 	 _XmProcessUnlock();
1341 	 if (expose)
1342 	    (*expose)((Widget)submenu, NULL, NULL);
1343 
1344 	 /* Redraw the submenu's widgets */
1345 	 for (i=0; i<submenu->composite.num_children; i++)
1346 	 {
1347 	    child = submenu->composite.children[i];
1348 	    if (XtIsWidget(child))
1349 	    {
1350 	       _XmProcessLock();
1351 	       expose = XtClass(child)->core_class.expose;
1352 	       _XmProcessUnlock();
1353 	       if (expose)
1354 		  (*expose)(child, event, NULL);
1355 	    }
1356 	 }
1357 	 XFlush(XtDisplay(submenu));
1358       }
1359 
1360       shell =  (ShellWidget)XtParent(submenu);      /* this is a toplevel */
1361 
1362       /* Save away current focus item.  Then clear the focus path so that
1363        * the XmProcessTraversal() in _XmRestoreTOToTopLevelShell() re-
1364        * highlights the focus_item.
1365        */
1366       submenu->row_column.tear_off_focus_item =
1367 	 XmGetFocusWidget((Widget)submenu);
1368       _XmClearFocusPath((Widget) submenu);
1369 
1370       /* Get a pixmap holder first! */
1371 
1372       values.graphics_exposures = False;
1373       values.subwindow_mode = IncludeInferiors;
1374       gc = XtGetGC((Widget) shell, GCGraphicsExposures | GCSubwindowMode,
1375 		   &values);
1376 
1377       /* Fix for CR #4855, use of default depth, DRand 6/4/92 */
1378       shell->core.background_pixmap = XCreatePixmap(XtDisplay(shell),
1379 	 RootWindowOfScreen(XtScreen(shell)),
1380 	 shell->core.width, shell->core.height,
1381          shell->core.depth);
1382       /* End of Fix #4855 */
1383 
1384       XCopyArea(XtDisplay(shell), XtWindow(submenu),
1385 	 shell->core.background_pixmap, gc, 0, 0,
1386 	 shell->core.width, shell->core.height,
1387 	 0, 0);
1388 
1389       XtReleaseGC((Widget) shell, gc);
1390 
1391       XtParent(submenu) = RC_ParentShell(submenu);
1392       RC_ParentShell(submenu) = (Widget)shell;
1393       RC_SetTearOffActive(submenu, False);
1394       if (wasDirty)
1395          XtMapWidget(XtParent(submenu));
1396 
1397       submenu->core.mapped_when_managed = False;
1398       submenu->core.managed = False;
1399 
1400       /* Sync up the server */
1401       XSetWindowBackgroundPixmap(XtDisplay(shell), XtWindow(shell),
1402 	 shell->core.background_pixmap);
1403 
1404       XReparentWindow(XtDisplay(shell), XtWindow(submenu),
1405 	 XtWindow(XtParent(submenu)), XtX(submenu), XtY(submenu));
1406 
1407       XtManageChild(RC_TearOffControl(submenu));
1408 
1409       /* The traversal graph needs to be zeroed/freed when reparenting back
1410        * to a MenuShell.  This handles the case of shared/torn panes where
1411        * the pane moves from shell(context1) -> torn-shell -> shell(context2).
1412        * Shell(context2) receives the TravGraph from shell(context1).
1413        */
1414        /* even if only one pane, sensitivity of menu items may have changed, so
1415        ** wind up forcing a reevaluation of the traversing graph
1416        */
1417        if (submenu->row_column.postFromCount >= 1)
1418          _XmResetTravGraph(submenu->core.parent);
1419 
1420       _XmCallRowColumnUnmapCallback((Widget)submenu, event);
1421       CallTearOffMenuDeactivateCallback((Widget)submenu, event,
1422 	 RESTORE_TEAR_OFF_TO_MENUSHELL);
1423       RemoveTearOffEventHandlers ((Widget) submenu);
1424    }
1425 }
1426