/* $XConsortium: GrabShell.c /main/9 1996/08/15 17:12:04 pascale $ */
/*
* Motif
*
* Copyright (c) 1987-2012, The Open Group. All rights reserved.
*
* These libraries and programs are free software; you can
* redistribute them and/or modify them under the terms of the GNU
* Lesser General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* These libraries and programs are distributed in the hope that
* they will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with these librararies and programs; if not, write
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301 USA
*/
/*
* HISTORY
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "XmI.h"
#include <X11/ShellP.h>
#include <X11/VendorP.h>
#include <X11/cursorfont.h>
#include <Xm/DrawP.h>
#include <Xm/GrabShellP.h>
#include <Xm/MenuUtilP.h>
#include <Xm/ScreenP.h>
#include <Xm/TransltnsP.h>
#include <Xm/VendorSEP.h>
#include <Xm/VendorSP.h>
#include "ColorI.h"
#include "MenuShellI.h"
#include "PixConvI.h"
#include "UniqueEvnI.h"
/* Warning messages */
#define default_translations _XmGrabShell_translations
#define Events (EnterWindowMask | LeaveWindowMask | \
ButtonPressMask | ButtonReleaseMask)
/******** Static Function Declarations ********/
#ifdef FIX_1445
static void MouseWheel (Widget grabshell,
XEvent *event,
String *params,
Cardinal *num_params);
#endif
static void BtnUp (Widget grabshell,
XEvent *event,
String *params,
Cardinal *num_params);
static void BtnDown (Widget grabshell,
XEvent *event,
String *params,
Cardinal *num_params);
static void Popdown (Widget grabshell,
XEvent *event,
String *params,
Cardinal *num_params);
static void ClassPartInitialize (WidgetClass wc);
static void Initialize (Widget req,
Widget new_w,
ArgList args,
Cardinal *num_args);
static Boolean SetValues (Widget cw,
Widget rw,
Widget nw,
ArgList args,
Cardinal *num_args);
static void Resize (Widget wid);
static void ChangeManaged (Widget w);
static XtGeometryResult GeometryManager(
Widget wid,
XtWidgetGeometry *request,
XtWidgetGeometry *reply) ;
static void Destroy (Widget wid);
static void MapNotifyHandler(Widget shell, XtPointer client_data,
XEvent *, Boolean *);
static void _XmFastExpose (Widget widget);
static void DrawBorder (Widget widget);
static void DoLayout (Widget gs);
static void GSAllowEvents(Widget gs, int, Time);
static int IgnoreXErrors(Display *, XErrorEvent *);
/******** End Static Function Declarations ********/
static XtActionsRec actionsList[] =
{
{ "GrabShellBtnDown", BtnDown },
{ "GrabShellBtnUp", BtnUp },
{ "GrabShellPopdown", Popdown }
#ifdef FIX_1445
,
{ "GrabShellMouseWheel", MouseWheel }
#endif
};
#define Offset(field) (XtOffsetOf(XmGrabShellRec, field))
static XtResource resources[] =
{
{
XmNallowShellResize, XmCAllowShellResize, XmRBoolean,
sizeof(Boolean), Offset(shell.allow_shell_resize),
XtRImmediate, (XtPointer)TRUE
},
{
XmNbackground, XmCBackground, XmRPixel,
sizeof (Pixel), Offset(core.background_pixel),
XmRCallProc, (XtPointer) _XmBackgroundColorDefault
},
{
XmNoverrideRedirect, XmCOverrideRedirect, XmRBoolean,
sizeof(Boolean), Offset(shell.override_redirect),
XtRImmediate, (XtPointer)TRUE
},
{
XmNsaveUnder, XmCSaveUnder, XmRBoolean,
sizeof(Boolean), Offset(shell.save_under),
XtRImmediate, (XtPointer)FALSE
},
{
XmNshadowThickness, XmCShadowThickness, XmRHorizontalDimension,
sizeof(Dimension), Offset(grab_shell.shadow_thickness),
XmRImmediate, (XtPointer)2
},
{
XmNtransient, XmCTransient, XmRBoolean,
sizeof(Boolean), Offset(wm_shell.transient),
XtRImmediate, (XtPointer)TRUE
},
{
XmNwaitForWm, XmCWaitForWm, XmRBoolean,
sizeof(Boolean), Offset(wm_shell.wait_for_wm),
XtRImmediate, (XtPointer)FALSE
},
{
XmNtopShadowColor, XmCTopShadowColor, XmRPixel,
sizeof(Pixel), Offset(grab_shell.top_shadow_color),
XmRCallProc, (XtPointer) _XmTopShadowColorDefault
},
{
XmNtopShadowPixmap, XmCTopShadowPixmap, XmRNoScalingDynamicPixmap,
sizeof(Pixmap), Offset(grab_shell.top_shadow_pixmap),
XmRCallProc, (XtPointer) _XmTopShadowPixmapDefault
},
{
XmNbottomShadowColor, XmCBottomShadowColor, XmRPixel,
sizeof(Pixel), Offset(grab_shell.bottom_shadow_color),
XmRCallProc, (XtPointer) _XmBottomShadowColorDefault
},
{
XmNbottomShadowPixmap, XmCBottomShadowPixmap, XmRNoScalingDynamicPixmap,
sizeof(Pixmap), Offset(grab_shell.bottom_shadow_pixmap),
XmRImmediate, (XtPointer) XmUNSPECIFIED_PIXMAP
},
{
XmNgrabStyle, XmCGrabStyle, XmRInt,
sizeof(int), Offset(grab_shell.grab_style),
XmRImmediate, (XtPointer) GrabModeAsync
},
{
XmNownerEvents, XmCOwnerEvents, XmRBoolean,
sizeof(Boolean), Offset(grab_shell.owner_events),
XmRImmediate, (XtPointer) FALSE
}
};
#undef Offset
externaldef(xmgrabshellclassrec) XmGrabShellClassRec xmGrabShellClassRec =
{
{ /* core class fields */
(WidgetClass) &vendorShellClassRec, /* superclass */
"XmGrabShell", /* class_name */
sizeof (XmGrabShellWidgetRec), /* widget_size */
NULL, /* class_initialize */
ClassPartInitialize, /* class_part_initialize */
FALSE, /* class_inited */
Initialize, /* initialize */
(XtArgsProc)NULL, /* initialize_hook */
XtInheritRealize, /* realize */
actionsList, /* actions */
XtNumber(actionsList), /* num_actions */
resources, /* resource list */
XtNumber(resources), /* resource_count */
NULLQUARK, /* xrm_class */
True, /* compress_motion */
XtExposeCompressMaximal, /* compress_exposure */
TRUE, /* compress_enterleave */
FALSE, /* visible_interest */
Destroy, /* destroy */
Resize, /* resize */
NULL, /* expose */
SetValues, /* set_values */
(XtArgsFunc)NULL, /* set_values_hook */
XtInheritSetValuesAlmost, /* set_values_almost */
(XtArgsProc)NULL, /* get_values_hook */
(XtAcceptFocusProc)NULL, /* accept_focus */
XtVersion, /* version */
NULL, /* callback_private */
default_translations, /* tm_table */
(XtGeometryHandler)NULL, /* query_geometry */
(XtStringProc)NULL, /* display_accelerator */
NULL, /* extension */
},
{ /* composite class fields */
GeometryManager, /* geometry_manager */
ChangeManaged, /* change_managed */
XtInheritInsertChild, /* insert_child */
XtInheritDeleteChild, /* delete_child */
NULL, /* extension */
},
{ /* shell class fields */
NULL, /* extension */
},
{ /* wmshell class fields */
NULL, /* extension */
},
{ /* vendor shell class fields */
NULL, /* extension */
},
{ /* grabshell class fields */
NULL, /* extension */
},
};
externaldef(xmgrabshellwidgetclass) WidgetClass xmGrabShellWidgetClass =
(WidgetClass) &xmGrabShellClassRec;
/* ------------- WIDGET CLASS METHODS ---------- */
/*
* Initialize()
*/
/*ARGSUSED*/
static void
Initialize(Widget req, /* unused */
Widget new_w,
ArgList args, /* unused */
Cardinal *num_args) /* unused */
{
XmGrabShellWidget grabsh = (XmGrabShellWidget)new_w;
XtAddEventHandler(new_w, StructureNotifyMask, False, MapNotifyHandler, NULL);
grabsh->grab_shell.unpost_time = (Time) -1;
grabsh->grab_shell.cursor = None;
grabsh->grab_shell.top_shadow_GC =
_XmGetPixmapBasedGC (new_w,
grabsh->grab_shell.top_shadow_color,
grabsh->core.background_pixel,
grabsh->grab_shell.top_shadow_pixmap);
grabsh->grab_shell.bottom_shadow_GC =
_XmGetPixmapBasedGC (new_w,
grabsh->grab_shell.bottom_shadow_color,
grabsh->core.background_pixel,
grabsh->grab_shell.bottom_shadow_pixmap);
/* CR 6723: The BtnUp event may arrive before MapNotify. */
grabsh->grab_shell.post_time = XtLastTimestampProcessed(XtDisplay(new_w));
/* CR 9920: Popdown may be requested before MapNotify. */
grabsh->grab_shell.mapped = False;
}
/*
* ClassPartInitialize()
* Set up the fast subclassing.
*/
static void
ClassPartInitialize(WidgetClass wc)
{
_XmFastSubclassInit (wc, XmGRAB_SHELL_BIT);
}
/*
* SetValues()
*/
/*ARGSUSED*/
static Boolean
SetValues(Widget cw,
Widget rw, /* unused */
Widget nw,
ArgList args, /* unused */
Cardinal *num_args) /* unused */
{
XmGrabShellWidget new_w = (XmGrabShellWidget) nw;
XmGrabShellWidget old_w = (XmGrabShellWidget) cw;
Boolean redisplay = FALSE;
if (old_w->grab_shell.shadow_thickness != new_w->grab_shell.shadow_thickness)
{
if (XtIsRealized(nw)) {
DoLayout(nw);
redisplay = TRUE;
}
}
if ((old_w->grab_shell.top_shadow_color !=
new_w->grab_shell.top_shadow_color) ||
(old_w->grab_shell.top_shadow_pixmap !=
new_w->grab_shell.top_shadow_pixmap))
{
XtReleaseGC (nw, new_w->grab_shell.top_shadow_GC);
new_w->grab_shell.top_shadow_GC =
_XmGetPixmapBasedGC (nw,
new_w->grab_shell.top_shadow_color,
new_w->core.background_pixel,
new_w->grab_shell.top_shadow_pixmap);
redisplay = TRUE;
}
if ((old_w->grab_shell.bottom_shadow_color !=
new_w->grab_shell.bottom_shadow_color) ||
(old_w->grab_shell.bottom_shadow_pixmap !=
new_w->grab_shell.bottom_shadow_pixmap))
{
XtReleaseGC (nw, new_w->grab_shell.bottom_shadow_GC);
new_w->grab_shell.bottom_shadow_GC =
_XmGetPixmapBasedGC (nw,
new_w->grab_shell.bottom_shadow_color,
new_w->core.background_pixel,
new_w->grab_shell.bottom_shadow_pixmap);
redisplay = TRUE;
}
return redisplay;
}
/*
* PopupCB()
* Grabs.
*/
/*ARGSUSED*/
static void
MapNotifyHandler(Widget shell, XtPointer client_data,
XEvent *event, Boolean *cont)
{
XmGrabShellWidget grabshell = (XmGrabShellWidget)shell;
Time time;
XErrorHandler old_handler;
/* Only handles map events */
if (event -> type != MapNotify) return;
/* CR 9920: Popdown may be called before MapNotify. */
grabshell->grab_shell.mapped = True;
if (!(time = XtLastTimestampProcessed(XtDisplay(shell))))
time = CurrentTime;
if (grabshell->grab_shell.cursor == None)
grabshell->grab_shell.cursor =
XCreateFontCursor (XtDisplay(grabshell), XC_arrow);
_XmFastExpose(shell);
(void) XtGrabKeyboard(shell, grabshell -> grab_shell.owner_events,
grabshell -> grab_shell.grab_style,
GrabModeAsync, time);
(void) XtGrabPointer(shell, grabshell -> grab_shell.owner_events,
Events,
grabshell -> grab_shell.grab_style,
GrabModeAsync, None,
grabshell->grab_shell.cursor, time);
GSAllowEvents(shell, SyncPointer, time);
/* Fix focus to shell */
XGetInputFocus(XtDisplay(shell), &grabshell->grab_shell.old_focus,
&grabshell->grab_shell.old_revert_to);
old_handler = XSetErrorHandler(IgnoreXErrors);
XSetInputFocus(XtDisplay(shell), XtWindow(shell), RevertToParent, time);
XSync(XtDisplay(shell), False);
XSetErrorHandler(old_handler);
}
#ifdef FIX_1445
static void MouseWheel (Widget w, XEvent *event, String *params, Cardinal *num_params)
{
XmGrabShellWidget grabshell = (XmGrabShellWidget) w;
GSAllowEvents(w, SyncPointer, event -> xbutton.time);
}
#endif
/*
* For BtnUp and BtnDown events we need to decide whether to
* popdown the grabshell. We "see" these if the user presses
* outside the shell.
*
* To decide, we call the XmNhasInterestCB to see if our poster
* wants to handle the event. If our poster does, we call
* XAllowEvents with REPLAY to get the event to the poster, otherwise
* we Popdown()
*
*/
static void
BtnUp (Widget w,
XEvent *event,
String *params,
Cardinal *num_params)
{
XmGrabShellWidget grabshell = (XmGrabShellWidget) w;
int delta;
/* Handle click to post
we then ignore the event if it occured within the
click to post time */
delta = event -> xbutton.time - grabshell -> grab_shell.post_time;
if (delta <= XtGetMultiClickTime(XtDisplay(w))) {
GSAllowEvents(w, SyncPointer, event -> xbutton.time);
return;
}
Popdown(w, event, params, num_params);
}
static void
BtnDown (Widget grabshell,
XEvent *event,
String *params,
Cardinal *num_params)
{
int x, y;
Window win;
/* Ignore modal cascade replay of event */
if (! _XmIsEventUnique(event)) return;
/* Move to grabshell's coordinate system */
XTranslateCoordinates(XtDisplay(grabshell), event -> xbutton.window,
XtWindow(grabshell),
event -> xbutton.x, event -> xbutton.y,
&x, &y, &win);
/* Popdown if outside the shell */
if (x >= 0 && y >= 0 &&
x <= XtWidth(grabshell) && y <= XtHeight(grabshell)) {
GSAllowEvents(grabshell, SyncPointer, event -> xbutton.time);
} else {
Popdown(grabshell, event, params, num_params);
}
}
/*
* Popdown()
* Popdown a GrabShell widget, also flag it's child as unmanaged.
*/
/*ARGSUSED*/
static void
Popdown(Widget shell,
XEvent *event, /* unused */
String *params,
Cardinal *num_params)
{
XmScreen screen = (XmScreen) XmGetXmScreen(XtScreen(shell));
XmGrabShellWidget grabshell = (XmGrabShellWidget)shell;
Time time;
/* Record for replay detection */
if (event && (event->type == ButtonPress || event->type == ButtonRelease)) {
grabshell->grab_shell.unpost_time = event->xbutton.time;
}
if (!(time = XtLastTimestampProcessed(XtDisplay(shell))))
time = CurrentTime;
/* CR 9920: Popdown may be called before MapNotify. */
if (grabshell->shell.popped_up && grabshell->grab_shell.mapped)
{
XErrorHandler old_handler;
if (screen -> screen.unpostBehavior == XmUNPOST_AND_REPLAY)
GSAllowEvents(shell, ReplayPointer, event ? event->xbutton.time : time);
XtUngrabPointer(shell, time);
XtUngrabKeyboard(shell, time);
_XmPopdown(shell);
/* Reset focus to old holder */
old_handler = XSetErrorHandler(IgnoreXErrors);
if (time != CurrentTime) time = time - 1; /* Avoid race in wm */
XSetInputFocus(XtDisplay(shell), grabshell->grab_shell.old_focus,
grabshell->grab_shell.old_revert_to, time);
XSync(XtDisplay(shell), False);
XSetErrorHandler(old_handler);
}
grabshell->grab_shell.mapped = False;
}
/*
* This only calls allow events if we have a sync grab.
*/
static void
GSAllowEvents(Widget gs, int mode, Time time)
{
XmGrabShellWidget grabshell = (XmGrabShellWidget) gs;
if (grabshell -> grab_shell.grab_style == GrabModeSync) {
XAllowEvents(XtDisplay(gs), mode, time);
}
}
/*
* Destroy()
*/
static void
Destroy(Widget widg)
{
XmGrabShellWidget grabshell = (XmGrabShellWidget) widg;
if (grabshell->grab_shell.cursor != None)
XFreeCursor(XtDisplay(widg), grabshell->grab_shell.cursor);
}
/*
* DoLayout()
*/
static void
DoLayout(Widget wid)
{
XmGrabShellWidget gs = (XmGrabShellWidget)wid;
if (XtIsManaged(gs->composite.children[0]))
{
Widget childwid = gs->composite.children[0];
Position offset = (gs->grab_shell.shadow_thickness +
childwid->core.border_width);
int cw = ((int) gs->core.width) - 2 * offset;
int ch = ((int) gs->core.height) - 2 * offset;
Dimension childW = MAX(1, cw);
Dimension childH = MAX(1, ch);
XmeConfigureObject (childwid, offset, offset,
childW, childH, childwid->core.border_width);
}
}
/************************************************************************
*
* GeometryManager
*
************************************************************************/
/*ARGSUSED*/
static XtGeometryResult
GeometryManager(
Widget wid,
XtWidgetGeometry *request,
XtWidgetGeometry *reply ) /* unused */
{
XmGrabShellWidget gs = (XmGrabShellWidget) XtParent(wid);
XtWidgetGeometry modified;
int bw;
XtGeometryResult ret_val;
/* Copy the existing request */
modified = *request;
bw = XtBorderWidth(wid);
/* Add shell's shadow thickness and child's borderwidth */
modified.width += 2*bw + 2*gs->grab_shell.shadow_thickness;
modified.height += 2*bw + 2*gs->grab_shell.shadow_thickness;
_XmProcessLock();
/* Send to vendor shell for final */
ret_val = ((VendorShellClassRec *) vendorShellWidgetClass) ->
composite_class.geometry_manager(wid,&modified,reply);
_XmProcessUnlock();
return ret_val;
}
/*
* ChangeManaged()
*/
static void
ChangeManaged(Widget wid)
{
XmGrabShellWidget gs = (XmGrabShellWidget)wid;
ShellWidget shell = (ShellWidget)wid;
Dimension bw = 0;
XtWidgetGeometry pref, mygeom, replygeom;
XtGeometryResult result;
Widget child;
mygeom.request_mode = 0;
if (gs->composite.num_children)
{
child = gs->composite.children[0];
if (XtIsManaged(child))
{
/* Get child's preferred size */
result = XtQueryGeometry(child, NULL, &pref);
/* Take whatever they want */
if (pref.request_mode & CWWidth)
{
mygeom.width = pref.width;
mygeom.request_mode |= CWWidth;
}
if (pref.request_mode & CWHeight)
{
mygeom.height = pref.height;
mygeom.request_mode |= CWHeight;
}
if (pref.request_mode & CWBorderWidth)
bw = pref.border_width;
else
bw = child->core.border_width;
}
}
mygeom.width += 2*bw + 2*gs->grab_shell.shadow_thickness;
mygeom.height += 2*bw + 2*gs->grab_shell.shadow_thickness;
result = XtMakeGeometryRequest((Widget)shell, &mygeom, &replygeom);
switch (result)
{
case XtGeometryAlmost:
XtMakeGeometryRequest((Widget)shell, &replygeom, NULL);
/* fall through. */
case XtGeometryYes:
DoLayout(wid);
break;
case XtGeometryNo:
case XtGeometryDone:
break;
}
}
/*
* Resize()
*/
static void
Resize(Widget w)
{
DoLayout(w);
}
/*
* When using an override redirect window, it is safe to draw to the
* window as soon as you have mapped it; you need not wait for exposure
* events to arrive. So ... to force shells to post quickly, we will
* redraw all of the items now, and ignore the exposure events we receive
* later.
*/
static void
_XmFastExpose(Widget widg)
{
register int i;
register Widget child;
XmGrabShellWidget gs = (XmGrabShellWidget)widg;
_XmProcessLock();
(*(XtClass(widg)->core_class.expose)) (widg, NULL, NULL);
_XmProcessUnlock();
/* Process each windowed child */
for (i = 0; i < gs->composite.num_children; i++)
{
child = gs->composite.children[i];
if (XtIsWidget(child) && XtIsManaged(child)) {
_XmProcessLock();
(*(XtClass(child)->core_class.expose)) (child, NULL, NULL);
_XmProcessUnlock();
}
}
XFlush(XtDisplay(widg));
DrawBorder(widg);
}
/*
* DrawBorder()
*/
static void
DrawBorder(Widget widg)
{
XmGrabShellWidget gs = (XmGrabShellWidget)widg;
int offset = 0;
XmeDrawShadows(XtDisplay(widg), XtWindow(widg),
gs->grab_shell.top_shadow_GC,
gs->grab_shell.bottom_shadow_GC,
offset, offset,
XtWidth(widg) - 2 * offset,
XtHeight(widg) - 2 * offset,
gs->grab_shell.shadow_thickness,
XmSHADOW_OUT);
}
/*
* IgnoreXErrors()
* An XErrorHandler that smothers errors.
*/
/*ARGSUSED*/
static int
IgnoreXErrors(Display *dpy, /* unused */
XErrorEvent *event) /* unused */
{
return 0;
}
/*******************
* Public Routines *
*******************/
Widget
XmCreateGrabShell(Widget parent,
char *name,
ArgList al,
Cardinal ac)
{
return XtCreatePopupShell(name, xmGrabShellWidgetClass, parent, al, ac);
}