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