/*
* 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 REV_INFO
#ifndef lint
static char rcsid[] = "$XConsortium: GMUtils.c /main/11 1995/09/19 23:03:32 cde-sun $"
#endif
#endif
/* (c) Copyright 1989, DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS. */
/* (c) Copyright 1987, 1988, 1989, 1990, 1991, 1992 HEWLETT-PACKARD COMPANY */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
/*******************************************************************
* This file contains the common geometry management set of routines
* for both pure BulletinBoard (not subclasses) and DrawingArea.
****************/
#include "XmI.h"
#include "GMUtilsI.h"
#include <Xm/ManagerP.h>
/******** Static Function Declarations ********/
/******** End Static Function Declarations ********/
/*******************************************************************
* Figure out how much size we need. Shrink wrap around the children.
****************/
void
_XmGMCalcSize(XmManagerWidget manager,
#if NeedWidePrototypes
int margin_width,
int margin_height,
#else
Dimension margin_width,
Dimension margin_height,
#endif /* NeedWidePrototypes */
Dimension *replyWidth,
Dimension *replyHeight )
{
register int i ;
register Widget child ;
int right, bottom ;
*replyWidth = *replyHeight = 0 ;
for (i = 0; i < manager->composite.num_children; i++) {
child = manager->composite.children[i];
if (XtIsManaged (child)) {
right = XtX(child) + 2*XtBorderWidth(child) + XtWidth(child) ;
bottom = XtY(child) + 2*XtBorderWidth(child) + XtHeight(child) ;
if (right > (int) *replyWidth) *replyWidth = right ;
if (bottom > (int) *replyHeight) *replyHeight = bottom ;
}
}
*replyWidth += margin_width + MGR_ShadowThickness(manager);
*replyHeight += margin_height + MGR_ShadowThickness(manager);
if (!(*replyWidth)) *replyWidth = 10;
if (!(*replyHeight)) *replyHeight = 10;
}
/****************************************************************
* Calculate the size needed for the Manager
* Request a change in size from parent if needed.
****************/
Boolean
_XmGMDoLayout(XmManagerWidget manager,
#if NeedWidePrototypes
int margin_width,
int margin_height,
#else
Dimension margin_width,
Dimension margin_height,
#endif /* NeedWidePrototypes */
int resize_policy,
#if NeedWidePrototypes
int queryonly
#else
Boolean queryonly
#endif /* NeedWidePrototypes */
)
{
/* return:
True means that the layout is accepted, either with no request
to the parent, or with a successfull serie of requests.
No means that the layout is denied, without request (NONE
policy for instance) or with request (parent does not accept the
change in size or proposes something unacceptable).
Note that when the parent returns almost, since this function returns
either true or false (see below), we don't handle almost reply
to the instigator.
The queryonly flag just controls the "reality" of the parent request
and the call to resize.
*/
XtWidgetGeometry request ;
XtWidgetGeometry reply ;
XtWidgetProc resize;
request.request_mode = CWWidth|CWHeight ;
if (queryonly) request.request_mode |= XtCWQueryOnly ;
_XmGMCalcSize(manager, margin_width, margin_height,
&request.width, &request.height);
/* no change, just accept it */
if ((XtWidth(manager) == request.width) &&
(XtHeight(manager) == request.height))
return (True);
/* the current manager sizes are bigger than the new ones and
we don't want to strink, just accept the layout as is, since
it already fits */
if ((resize_policy == XmRESIZE_GROW ||
resize_policy == XmRESIZE_NONE) &&
((XtWidth(manager) >= request.width) &&
(XtHeight(manager) >= request.height)) )
return (True);
/* the previous test passes over, so one of the needed sizes must be
bigger than one of the current, and we can't do that with NONE */
if (resize_policy == XmRESIZE_NONE )
return (False);
/* we can't shrink on one side while growing on the other, just
overwrite the shrinking side */
if(resize_policy == XmRESIZE_GROW ) {
if(request.width < XtWidth(manager))
request.width = XtWidth(manager) ;
if(request.height < XtHeight(manager))
request.height = XtHeight(manager) ;
}
_XmProcessLock();
resize = XtCoreProc(manager,resize);
_XmProcessUnlock();
/* now the request */
switch(XtMakeGeometryRequest((Widget)manager,
&request,
&reply)) {
case XtGeometryYes:
if (!queryonly)
/* call the resize proc, since the widget set has now
a Yes policy */
(*resize)((Widget)manager) ;
return(True) ;
case XtGeometryAlmost:
/* The following behavior is based on the shrink wrapper
behavior of this manager. What we have asked is the minimum.
If the almost returned size is smaller than the
requested one, refuse, since it will always clip something */
if ((reply.width < request.width) ||
(reply.height < request.height)) {
return( False) ;
} else {
/* if almost returned a bigger size than requested,
accept it since it cannot hurt the shrink wrap behavior,
neither the Grow policy (requested always > current).
Apply it if not queryonly. If queryonly, we don't
have to re-request something, since no change is needed
and we already know it's OK */
if (!queryonly) {
(void) XtMakeResizeRequest((Widget)manager,
reply.width,
reply.height,
NULL, NULL) ;
(*resize)((Widget)manager) ;
}
return(True) ;
}
case XtGeometryNo:
default:
break ;
}
return( False) ;
}
/* Enforce margins for children if margins are non-zero */
void
_XmGMEnforceMargin(XmManagerWidget manager,
#if NeedWidePrototypes
int margin_width,
int margin_height,
int setvalue
#else
Dimension margin_width,
Dimension margin_height,
Boolean setvalue
#endif /* NeedWidePrototypes */
)
{
int i ;
register Widget child ;
register Boolean do_move ;
Position newx, newy ;
for(i = 0 ; i < manager->composite.num_children ; i++) {
do_move = False ;
child = (Widget) manager->composite.children[i] ;
if(XtIsManaged (child)) {
if ((margin_width != 0) &&
(XtX(child) < (int) margin_width )) {
do_move = True ;
newx = margin_width ;
} else
newx = XtX(child) ;
if ((margin_height != 0) &&
(XtY(child) < (int) margin_height )) {
do_move = True ;
newy = margin_height ;
} else
newy = XtY(child) ;
if(do_move) {
if (setvalue) {
Arg args[2] ;
XtSetArg(args[0], XmNx, newx);
XtSetArg(args[1], XmNy, newy);
XtSetValues(child, args, 2);
} else
XmeConfigureObject(child, newx, newy,
child->core.width, child->core.height,
child->core.border_width);
}
}
}
}
/****************************************************************
* Handle query geometry requests for both BB and DA.
****************/
XtGeometryResult
_XmGMHandleQueryGeometry(Widget widget,
XtWidgetGeometry * intended,
XtWidgetGeometry * desired,
#if NeedWidePrototypes
int margin_width,
int margin_height,
#else
Dimension margin_width,
Dimension margin_height,
#endif /* NeedWidePrototypes */
int resize_policy)
{
Dimension width, height ;
/* first determine what is the desired size, using the resize_policy. */
if (resize_policy == XmRESIZE_NONE) {
desired->width = XtWidth(widget) ;
desired->height = XtHeight(widget) ;
} else {
if (GMode( intended) & CWWidth) width = intended->width;
if (GMode( intended) & CWHeight) height = intended->height;
_XmGMCalcSize ((XmManagerWidget)widget,
margin_width, margin_height, &width, &height);
if ((resize_policy == XmRESIZE_GROW) &&
((width < XtWidth(widget)) ||
(height < XtHeight(widget)))) {
desired->width = XtWidth(widget) ;
desired->height = XtHeight(widget) ;
} else {
desired->width = width ;
desired->height = height ;
}
}
/* deal with user initial size setting */
if (!XtIsRealized(widget)) {
if (XtWidth(widget) != 0) desired->width = XtWidth(widget) ;
if (XtHeight(widget) != 0) desired->height = XtHeight(widget) ;
}
return XmeReplyToQueryGeometry(widget, intended, desired) ;
}
/****************************************************************
*
* XmeReplyToQueryGeometry.
*
* This is the generic handling of Almost, No and Yes replies
* based on the intended values and the given desirs.
*
* This can be used by any widget that really only care about is
* width and height dimension. It just has to compute its desired size
* using its own private layout routine and resources before calling
* this one that will deal with the Xt reply value cuisine.
*
****************/
XtGeometryResult
XmeReplyToQueryGeometry(Widget widget,
XtWidgetGeometry * intended,
XtWidgetGeometry * desired)
{
_XmWidgetToAppContext(widget);
/* the caller should have set desired width and height*/
desired->request_mode = (CWWidth | CWHeight) ;
/* Accept any x, y, border and stacking. If the proposed
geometry matches the desired one, and the parent intends
to use these values (flags are set in intended),
return Yes. Otherwise, the parent intends to use values for
width and height that differ from the desired size, return No
if the desired is the current and Almost if the desired size
is different from the current size */
if ((IsWidth(intended)) &&
(intended->width == desired->width) &&
(IsHeight(intended)) &&
(intended->height == desired->height)) {
return XtGeometryYes ;
}
_XmAppLock(app);
if ((desired->width == XtWidth(widget)) &&
(desired->height == XtHeight(widget))) {
_XmAppUnlock(app);
return XtGeometryNo ;
}
_XmAppUnlock(app);
return XtGeometryAlmost ;
}
/****************
* Return True if w intersects with any other siblins.
****************/
Boolean
_XmGMOverlap(XmManagerWidget manager,
Widget w)
{
register int i ;
Position left1 = XtX(w) ;
Position top1 = XtY(w) ;
Dimension right1 = XtX(w) + 2*XtBorderWidth(w) + XtWidth(w) ;
Dimension bottom1 = XtY(w) + 2*XtBorderWidth(w) + XtHeight(w);
/****************/
for(i=0 ; i<manager->composite.num_children ; i++) {
Widget kid = manager->composite.children[i] ;
Position left2 = XtX(kid) ;
Position top2 = XtY(kid) ;
Dimension right2 = XtX(kid) + 2*XtBorderWidth(kid) +
XtWidth(kid);
Dimension bottom2 = XtY(kid) + 2*XtBorderWidth(kid) +
XtHeight(kid) ;
if(w != kid &&
(((left1 >= left2) && ((Dimension)left1 <= right2)) ||
((left2 >= left1) && ((Dimension)left2 <= right1))) &&
(((top1 >= top2) && ((Dimension)top1 <= bottom2)) ||
((top2 >= top1) && ((Dimension)top2 <= bottom1))) )
{ return( True) ;
}
}
return( False) ;
}
/****************
* Handle geometry requests from children.
****************/
XtGeometryResult
_XmGMHandleGeometryManager(Widget parent, Widget w,
XtWidgetGeometry * request,
XtWidgetGeometry * reply,
#if NeedWidePrototypes
int margin_width,
int margin_height,
#else
Dimension margin_width,
Dimension margin_height,
#endif /* NeedWidePrototypes */
int resize_policy,
int allow_overlap)
{
/* Policy: Yes
if margin is non null requests inside the margin or negative
are a priori almosted (or denied if the resizepolicy does not
authorize it).
That's the only case where almost is returned, no management
of a limit position or size is done
*/
XtWidgetGeometry localReply ;
Dimension width, height, borderWidth ;
Position x, y ;
XtGeometryResult returnCode = XtGeometryNo ;
Boolean geoFlag = False, queryonly = False ;
XmManagerWidget manager = (XmManagerWidget) parent ;
localReply = *request ;
localReply.request_mode = CWX|CWY|CWWidth|CWHeight|CWBorderWidth ;
if(!IsX(request)) localReply.x = XtX(w) ;
if(!IsY(request)) localReply.y = XtY(w) ;
if(!IsWidth(request)) localReply.width = XtWidth(w) ;
if(!IsHeight(request)) localReply.height = XtHeight(w) ;
if(!IsBorder(request)) localReply.border_width = XtBorderWidth(w) ;
/* check for x less than margin width
*/
if(IsX(request) && (margin_width != 0)
&& (request->x < (int)margin_width)) {
localReply.x = (Position) margin_width ;
returnCode = XtGeometryAlmost ;
}
/* check for y less than margin height */
if(IsY(request) && (margin_height != 0)
&& (request->y < (int)margin_height)) {
localReply.y = (Position) margin_height ;
returnCode = XtGeometryAlmost ;
}
/* Save current size and set to new size
*/
x = XtX(w) ;
y = XtY(w) ;
width = XtWidth(w) ;
height = XtHeight(w) ;
borderWidth = XtBorderWidth(w) ;
XtX(w) = localReply.x ;
XtY(w) = localReply.y ;
XtWidth(w) = localReply.width ;
XtHeight(w) = localReply.height ;
XtBorderWidth(w) = localReply.border_width ;
if(!allow_overlap && _XmGMOverlap (manager, w)) {
returnCode = XtGeometryNo ;
} else {
/* if we already know that we are not gonna change anything */
if ((returnCode == XtGeometryAlmost) || IsQueryOnly(request))
queryonly = True ;
/* see if the new layout is OK */
geoFlag = _XmGMDoLayout(manager,
margin_width, margin_height,
resize_policy, queryonly) ;
/* if we cannot adapt the new size but the child is still inside
go for it */
if(!geoFlag && ((localReply.x + localReply.width +
(localReply.border_width << 1))
<= (XtWidth(manager) - margin_width))
&& ((localReply.y + localReply.height +
(localReply.border_width << 1))
<= (XtHeight(manager) - margin_height)))
geoFlag = True ;
if (geoFlag) {
if (returnCode != XtGeometryAlmost)
returnCode = XtGeometryYes ;
} else
returnCode = XtGeometryNo ;
if (returnCode == XtGeometryAlmost){
if (reply) *reply = localReply ;
else returnCode = XtGeometryNo ;
}
}
if ((returnCode != XtGeometryYes) || queryonly) {
/* Can't oblige, so restore previous values. */
XtX(w) = x ;
XtY(w) = y ;
XtWidth(w) = width ;
XtHeight(w) = height ;
XtBorderWidth(w) = borderWidth ;
}
return returnCode ;
}