749N/A#ifndef lint
749N/Astatic char Xrcsid[] = "$XConsortium: Box.c,v 1.43 89/12/07 20:14:26 kit Exp $";
749N/A#endif /* lint */
749N/A
749N/A
749N/A/***********************************************************
749N/ACopyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts,
749N/Aand the Massachusetts Institute of Technology, Cambridge, Massachusetts.
749N/A
749N/A All Rights Reserved
749N/A
749N/APermission to use, copy, modify, and distribute this software and its
749N/Adocumentation for any purpose and without fee is hereby granted,
749N/Aprovided that the above copyright notice appear in all copies and that
749N/Aboth that copyright notice and this permission notice appear in
749N/Asupporting documentation, and that the names of Digital or MIT not be
749N/Aused in advertising or publicity pertaining to distribution of the
749N/Asoftware without specific, written prior permission.
749N/A
749N/ADIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
749N/AALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
749N/ADIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
749N/AANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
749N/AWHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
749N/AARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
749N/ASOFTWARE.
749N/A
749N/A******************************************************************/
749N/A
749N/A
749N/A/*
749N/A * Box.c - Box composite widget
749N/A *
749N/A */
749N/A
749N/A#include <X11/IntrinsicP.h>
749N/A#include <X11/StringDefs.h>
749N/A#include <X11/Xmu/Misc.h>
749N/A#include <./Xaw3_1XawInit.h>
749N/A#include <./Xaw3_1BoxP.h>
749N/A
749N/A/****************************************************************
749N/A *
749N/A * Box Resources
749N/A *
749N/A ****************************************************************/
749N/A
749N/Astatic XtResource resources[] = {
749N/A { XtNhSpace, XtCHSpace, XtRDimension, sizeof(Dimension),
749N/A XtOffsetOf(BoxRec, box.h_space),
749N/A XtRImmediate, (XtPointer)4 },
749N/A { XtNvSpace, XtCVSpace, XtRDimension, sizeof(Dimension),
749N/A XtOffsetOf(BoxRec, box.v_space),
749N/A XtRImmediate, (XtPointer)4 },
749N/A { XtNorientation, XtCOrientation, XtROrientation, sizeof(XtOrientation),
749N/A XtOffsetOf(BoxRec, box.orientation),
749N/A XtRImmediate, (XtPointer)XtorientVertical },
749N/A};
749N/A
749N/A/****************************************************************
749N/A *
749N/A * Full class record constant
749N/A *
749N/A ****************************************************************/
749N/A
749N/Astatic void ClassInitialize();
749N/Astatic void Initialize();
749N/Astatic void Realize();
749N/Astatic void Resize();
749N/Astatic Boolean SetValues();
749N/Astatic XtGeometryResult GeometryManager();
749N/Astatic void ChangeManaged();
749N/Astatic XtGeometryResult PreferredSize();
749N/A
749N/ABoxClassRec boxClassRec = {
749N/A {
749N/A/* core_class fields */
749N/A /* superclass */ (WidgetClass) &compositeClassRec,
749N/A /* class_name */ "Box",
749N/A /* widget_size */ sizeof(BoxRec),
749N/A /* class_initialize */ ClassInitialize,
749N/A /* class_part_init */ NULL,
749N/A /* class_inited */ FALSE,
749N/A /* initialize */ Initialize,
749N/A /* initialize_hook */ NULL,
749N/A /* realize */ Realize,
749N/A /* actions */ NULL,
749N/A /* num_actions */ 0,
749N/A /* resources */ resources,
749N/A /* num_resources */ XtNumber(resources),
749N/A /* xrm_class */ NULLQUARK,
749N/A /* compress_motion */ TRUE,
749N/A /* compress_exposure */ TRUE,
749N/A /* compress_enterleave*/ TRUE,
749N/A /* visible_interest */ FALSE,
749N/A /* destroy */ NULL,
749N/A /* resize */ Resize,
749N/A /* expose */ NULL,
749N/A /* set_values */ SetValues,
749N/A /* set_values_hook */ NULL,
749N/A /* set_values_almost */ XtInheritSetValuesAlmost,
749N/A /* get_values_hook */ NULL,
749N/A /* accept_focus */ NULL,
749N/A /* version */ XtVersion,
749N/A /* callback_private */ NULL,
749N/A /* tm_table */ NULL,
749N/A /* query_geometry */ PreferredSize,
749N/A /* display_accelerator*/ XtInheritDisplayAccelerator,
749N/A /* extension */ NULL
749N/A },{
749N/A/* composite_class fields */
749N/A /* geometry_manager */ GeometryManager,
749N/A /* change_managed */ ChangeManaged,
749N/A /* insert_child */ XtInheritInsertChild,
749N/A /* delete_child */ XtInheritDeleteChild,
749N/A /* extension */ NULL
749N/A },{
749N/A/* Box class fields */
749N/A /* empty */ 0,
749N/A }
749N/A};
749N/A
749N/AWidgetClass boxWidgetClass = (WidgetClass)&boxClassRec;
749N/A
749N/A
749N/A/****************************************************************
749N/A *
749N/A * Private Routines
749N/A *
749N/A ****************************************************************/
749N/A
749N/A/*
749N/A *
749N/A * Do a layout, either actually assigning positions, or just calculating size.
749N/A * Returns minimum width and height that will preserve the same layout.
749N/A *
749N/A */
749N/A
1295N/Astatic void DoLayout(bbw, width, height, reply_width, reply_height, position)
749N/A BoxWidget bbw;
749N/A Dimension width, height;
749N/A Dimension *reply_width, *reply_height; /* bounding box */
749N/A Boolean position; /* actually reposition the windows? */
749N/A{
749N/A Boolean vbox = (bbw->box.orientation == XtorientVertical);
749N/A Cardinal i;
749N/A Dimension w, h; /* Width and height needed for box */
749N/A Dimension lw, lh; /* Width and height needed for current line */
749N/A Dimension bw, bh; /* Width and height needed for current widget */
749N/A Dimension h_space; /* Local copy of bbw->box.h_space */
749N/A register Widget widget; /* Current widget */
749N/A int num_mapped_children = 0;
749N/A
749N/A /* Box width and height */
749N/A h_space = bbw->box.h_space;
749N/A w = h_space;
749N/A h = bbw->box.v_space;
749N/A
749N/A /* Line width and height */
749N/A lh = 0;
749N/A lw = h_space;
749N/A
749N/A for (i = 0; i < bbw->composite.num_children; i++) {
749N/A widget = bbw->composite.children[i];
749N/A if (widget->core.managed) {
749N/A if (widget->core.mapped_when_managed) num_mapped_children++;
749N/A /* Compute widget width */
749N/A bw = widget->core.width + 2*widget->core.border_width + h_space;
749N/A if (lw + bw > width) {
749N/A if (lw > h_space) {
749N/A /* At least one widget on this line, and
749N/A * can't fit any more. Start new line if vbox.
749N/A */
749N/A AssignMax(w, lw);
749N/A if (vbox) {
749N/A h += lh + bbw->box.v_space;
749N/A lh = 0;
749N/A lw = h_space;
749N/A }
749N/A }
749N/A else if (!position) {
749N/A /* too narrow for this widget; we'll assume we can grow */
749N/A DoLayout(bbw, lw + bw, height, reply_width,
749N/A reply_height, position);
749N/A return;
749N/A }
749N/A }
749N/A if (position && (lw != widget->core.x || h != widget->core.y)) {
749N/A /* It would be nice to use window gravity, but there isn't
749N/A * sufficient fine-grain control to nicely handle all
749N/A * situations (e.g. when only the height changes --
749N/A * a common case). Explicit unmapping is a cheap hack
749N/A * to speed things up & avoid the visual jitter as
749N/A * things slide around.
749N/A *
749N/A * %%% perhaps there should be a client resource to
749N/A * control this. If so, we'll have to optimize to
749N/A * perform the moves from the correct end so we don't
749N/A * force extra exposures as children occlude each other.
749N/A */
749N/A if (XtIsRealized(widget))
749N/A XUnmapWindow( XtDisplay(widget), XtWindow(widget) );
749N/A XtMoveWidget(bbw->composite.children[i], (int)lw, (int)h);
749N/A }
749N/A lw += bw;
749N/A bh = widget->core.height + 2*widget->core.border_width;
749N/A AssignMax(lh, bh);
749N/A } /* if managed */
749N/A } /* for */
749N/A
749N/A if (!vbox && width && lw > width && lh < height) {
749N/A /* reduce width if too wide and height not filled */
749N/A Dimension sw = lw, sh = lh;
749N/A Dimension width_needed;
749N/A XtOrientation orientation = bbw->box.orientation;
749N/A bbw->box.orientation = XtorientVertical;
749N/A while (sh < height && sw > width) {
749N/A width_needed = sw;
749N/A DoLayout(bbw, sw-1, height, &sw, &sh, False);
749N/A }
749N/A if (sh < height) width_needed = sw;
749N/A if (width_needed != lw) {
749N/A DoLayout(bbw,width_needed,height,reply_width,reply_height,position);
749N/A bbw->box.orientation = orientation;
749N/A return;
749N/A }
749N/A bbw->box.orientation = orientation;
749N/A }
749N/A
749N/A if (position && XtIsRealized((Widget)bbw)) {
749N/A if (bbw->composite.num_children == num_mapped_children)
749N/A XMapSubwindows( XtDisplay((Widget)bbw), XtWindow((Widget)bbw) );
749N/A else {
749N/A int i = num_mapped_children;
749N/A register Widget *childP = bbw->composite.children;
749N/A for (; i > 0; childP++) {
749N/A if (XtIsManaged(*childP) &&
749N/A (*childP)->core.mapped_when_managed) {
749N/A XtMapWidget(*childP);
749N/A i--;
749N/A }
749N/A }
749N/A }
749N/A }
749N/A
749N/A /* Finish off last line */
749N/A if (lw > h_space) {
749N/A AssignMax(w, lw);
749N/A h += lh + bbw->box.v_space;
749N/A }
749N/A
749N/A *reply_width = Max(w, 1);
749N/A *reply_height = Max(h, 1);
749N/A}
749N/A
749N/A/*
749N/A *
749N/A * Calculate preferred size, given constraining box, caching it in the widget.
749N/A *
749N/A */
749N/A
749N/Astatic XtGeometryResult PreferredSize(widget, constraint, preferred)
749N/A Widget widget;
749N/A XtWidgetGeometry *constraint, *preferred;
749N/A{
749N/A BoxWidget w = (BoxWidget)widget;
749N/A Dimension width /*, height */;
749N/A Dimension preferred_width = w->box.preferred_width;
749N/A Dimension preferred_height = w->box.preferred_height;
749N/A
749N/A constraint->request_mode &= CWWidth | CWHeight;
749N/A
749N/A if (constraint->request_mode == 0)
749N/A /* parent isn't going to change w or h, so nothing to re-compute */
749N/A return XtGeometryYes;
749N/A
749N/A if (constraint->request_mode == w->box.last_query_mode &&
749N/A (!(constraint->request_mode & CWWidth) ||
749N/A constraint->width == w->box.last_query_width) &&
749N/A (!(constraint->request_mode & CWHeight) ||
749N/A constraint->height == w->box.last_query_height)) {
749N/A /* same query; current preferences are still valid */
749N/A preferred->request_mode = CWWidth | CWHeight;
749N/A preferred->width = preferred_width;
749N/A preferred->height = preferred_height;
749N/A if (constraint->request_mode == (CWWidth | CWHeight) &&
749N/A constraint->width == preferred_width &&
749N/A constraint->height == preferred_height)
749N/A return XtGeometryYes;
749N/A else
749N/A return XtGeometryAlmost;
749N/A }
749N/A
749N/A /* else gotta do it the long way...
749N/A I have a preference for tall and narrow, so if my width is
749N/A constrained, I'll accept it; otherwise, I'll compute the minimum
749N/A width that will fit me within the height constraint */
749N/A
749N/A w->box.last_query_mode = constraint->request_mode;
749N/A w->box.last_query_width = constraint->width;
749N/A w->box.last_query_height= constraint->height;
749N/A
749N/A if (constraint->request_mode & CWWidth)
749N/A width = constraint->width;
749N/A else /* if (constraint->request_mode & CWHeight) */ {
749N/A /* let's see if I can become any narrower */
749N/A width = 0;
749N/A constraint->width = 65535;
749N/A }
749N/A
749N/A /* height is currently ignored by DoLayout.
749N/A height = (constraint->request_mode & CWHeight) ? constraint->height
749N/A : *preferred_height;
749N/A */
749N/A DoLayout(w, width, (Dimension)0,
749N/A &preferred_width, &preferred_height, FALSE);
749N/A
749N/A if (constraint->request_mode & CWHeight &&
749N/A preferred_height > constraint->height) {
749N/A /* find minimum width for this height */
749N/A if (preferred_width > constraint->width) {
749N/A /* punt; over-constrained */
749N/A }
749N/A else {
749N/A width = preferred_width;
749N/A do { /* find some width big enough to stay within this height */
749N/A width *= 2;
749N/A if (width > constraint->width) width = constraint->width;
749N/A DoLayout(w, width, 0, &preferred_width, &preferred_height, FALSE);
749N/A } while (preferred_height > constraint->height &&
749N/A width < constraint->width);
749N/A if (width != constraint->width) {
749N/A do { /* find minimum width */
749N/A width = preferred_width;
749N/A DoLayout(w, preferred_width-1, 0,
749N/A &preferred_width, &preferred_height, FALSE);
749N/A } while (preferred_height < constraint->height);
749N/A /* one last time */
749N/A DoLayout(w, width, 0, &preferred_width, &preferred_height, FALSE);
749N/A }
749N/A }
749N/A }
749N/A
749N/A preferred->request_mode = CWWidth | CWHeight;
749N/A preferred->width = w->box.preferred_width = preferred_width;
749N/A preferred->height = w->box.preferred_height = preferred_height;
749N/A
749N/A if (constraint->request_mode == (CWWidth|CWHeight)
749N/A && constraint->width == preferred_width
749N/A && constraint->height == preferred_height)
749N/A return XtGeometryYes;
749N/A else
749N/A return XtGeometryAlmost;
749N/A
749N/A}
749N/A
749N/A/*
749N/A *
749N/A * Actually layout the box
749N/A *
749N/A */
749N/A
749N/Astatic void Resize(w)
749N/A Widget w;
749N/A{
749N/A Dimension junk;
749N/A
749N/A DoLayout((BoxWidget)w, w->core.width, w->core.height, &junk, &junk, TRUE);
749N/A
749N/A} /* Resize */
749N/A
749N/A/*
749N/A *
749N/A * Try to do a new layout within the current width and height;
749N/A * if that fails try to resize and do it within the box returne
749N/A * by PreferredSize.
749N/A *
749N/A * TryNewLayout just says if it's possible, and doesn't actually move the kids
749N/A */
749N/A
749N/Astatic Boolean TryNewLayout(bbw)
749N/A BoxWidget bbw;
749N/A{
749N/A Dimension preferred_width, preferred_height;
749N/A Dimension proposed_width, proposed_height;
749N/A int iterations;
749N/A
749N/A DoLayout( bbw, bbw->core.width, bbw->core.height,
749N/A &preferred_width, &preferred_height, FALSE );
749N/A
749N/A /* at this point, preferred_width is guaranteed to not be greater
749N/A than bbw->core.width unless some child is larger, so there's no
749N/A point in re-computing another layout */
749N/A
749N/A if ((bbw->core.width == preferred_width) &&
749N/A (bbw->core.height == preferred_height)) {
749N/A /* Same size */
749N/A return (TRUE);
749N/A }
749N/A
749N/A /* let's see if our parent will go for a new size. */
749N/A iterations = 0;
749N/A proposed_width = preferred_width;
749N/A proposed_height = preferred_height;
749N/A do {
749N/A switch (XtMakeResizeRequest((Widget)bbw,proposed_width,proposed_height,
749N/A &proposed_width, &proposed_height))
749N/A {
749N/A case XtGeometryYes:
749N/A return (TRUE);
749N/A
749N/A case XtGeometryNo:
749N/A if (iterations > 0)
749N/A /* protect from malicious parents who change their minds */
749N/A DoLayout( bbw, bbw->core.width, bbw->core.height,
749N/A &preferred_width, &preferred_height, FALSE );
749N/A if ((preferred_width <= bbw->core.width) &&
749N/A (preferred_height <= bbw->core.height))
749N/A return (TRUE);
749N/A else
749N/A return (FALSE);
749N/A
749N/A case XtGeometryAlmost:
749N/A if (proposed_height >= preferred_height &&
749N/A proposed_width >= preferred_width) {
749N/A
749N/A /*
749N/A * Take it, and assume the parent knows what it is doing.
749N/A *
749N/A * The parent must accept this since it was returned in
749N/A * almost.
749N/A *
749N/A */
749N/A (void) XtMakeResizeRequest( (Widget)bbw,
749N/A proposed_width, proposed_height,
749N/A &proposed_width, &proposed_height);
749N/A return(TRUE);
749N/A }
749N/A else if (proposed_width != preferred_width) {
749N/A /* recalc bounding box; height might change */
749N/A DoLayout(bbw, proposed_width, 0,
749N/A &preferred_width, &preferred_height, FALSE);
749N/A proposed_height = preferred_height;
749N/A }
749N/A else { /* proposed_height != preferred_height */
749N/A XtWidgetGeometry constraints, reply;
749N/A constraints.request_mode = CWHeight;
749N/A constraints.height = proposed_height;
749N/A (void)PreferredSize((Widget)bbw, &constraints, &reply);
749N/A proposed_width = preferred_width;
749N/A }
749N/A }
749N/A iterations++;
749N/A } while (iterations < 10);
749N/A return (FALSE);
749N/A}
749N/A
749N/A/*
749N/A *
749N/A * Geometry Manager
749N/A *
749N/A * 'reply' is unused; we say only yeay or nay, never almost.
749N/A *
749N/A */
749N/A
749N/A/*ARGSUSED*/
749N/Astatic XtGeometryResult GeometryManager(w, request, reply)
749N/A Widget w;
749N/A XtWidgetGeometry *request;
749N/A XtWidgetGeometry *reply; /* RETURN */
749N/A
749N/A{
749N/A Dimension width, height, borderWidth;
749N/A BoxWidget bbw;
749N/A
749N/A /* Position request always denied */
749N/A if ((request->request_mode & CWX && request->x != w->core.x) ||
749N/A (request->request_mode & CWY && request->y != w->core.y))
749N/A return (XtGeometryNo);
749N/A
749N/A /* Size changes must see if the new size can be accomodated */
749N/A if (request->request_mode & (CWWidth | CWHeight | CWBorderWidth)) {
749N/A
749N/A /* Make all three fields in the request valid */
749N/A if ((request->request_mode & CWWidth) == 0)
749N/A request->width = w->core.width;
749N/A if ((request->request_mode & CWHeight) == 0)
749N/A request->height = w->core.height;
749N/A if ((request->request_mode & CWBorderWidth) == 0)
749N/A request->border_width = w->core.border_width;
749N/A
749N/A /* Save current size and set to new size */
749N/A width = w->core.width;
749N/A height = w->core.height;
749N/A borderWidth = w->core.border_width;
749N/A w->core.width = request->width;
749N/A w->core.height = request->height;
749N/A w->core.border_width = request->border_width;
749N/A
749N/A /* Decide if new layout works: (1) new widget is smaller,
749N/A (2) new widget fits in existing Box, (3) Box can be
749N/A expanded to allow new widget to fit */
749N/A
749N/A bbw = (BoxWidget) w->core.parent;
749N/A
749N/A/* whenever a child changes his geometry, we attempt to
749N/A * change ours to be the minimum enclosing size...
749N/A if (((request->width + request->border_width <= width + borderWidth) &&
749N/A (request->height + request->border_width <= height + borderWidth))
749N/A || bbw->box.preferred_width < bbw->core.width
749N/A || bbw->box.preferred_height < bbw->core.height
749N/A || TryNewLayout(bbw)) {
749N/A */
749N/A if (TryNewLayout(bbw)) {
749N/A /* Fits in existing or new space, relayout */
749N/A (*XtClass((Widget)bbw)->core_class.resize)((Widget)bbw);
749N/A return (XtGeometryYes);
749N/A } else {
749N/A /* Cannot satisfy request, change back to original geometry */
749N/A w->core.width = width;
749N/A w->core.height = height;
749N/A w->core.border_width = borderWidth;
749N/A return (XtGeometryNo);
749N/A }
749N/A }; /* if any size changes requested */
749N/A
749N/A /* Any stacking changes don't make a difference, so allow if that's all */
749N/A return (XtGeometryYes);
749N/A}
749N/A
749N/Astatic void ChangeManaged(w)
749N/A Widget w;
749N/A{
749N/A /* Reconfigure the box */
749N/A (void) TryNewLayout((BoxWidget)w);
749N/A Resize(w);
749N/A}
749N/A
749N/Astatic void ClassInitialize()
749N/A{
749N/A XawInitializeWidgetSet();
749N/A XtAddConverter( XtRString, XtROrientation, XmuCvtStringToOrientation,
749N/A NULL, (Cardinal)0 );
749N/A}
749N/A
749N/A/* ARGSUSED */
749N/Astatic void Initialize(request, new)
749N/A Widget request, new;
749N/A{
749N/A BoxWidget newbbw = (BoxWidget)new;
749N/A
749N/A newbbw->box.last_query_mode = CWWidth | CWHeight;
749N/A newbbw->box.last_query_width = newbbw->box.last_query_height = 0;
749N/A newbbw->box.preferred_width = Max(newbbw->box.h_space, 1);
749N/A newbbw->box.preferred_height = Max(newbbw->box.v_space, 1);
749N/A
749N/A if (newbbw->core.width == 0)
749N/A newbbw->core.width = newbbw->box.preferred_width;
749N/A
749N/A if (newbbw->core.height == 0)
749N/A newbbw->core.height = newbbw->box.preferred_height;
749N/A
749N/A} /* Initialize */
749N/A
749N/Astatic void Realize(w, valueMask, attributes)
749N/A register Widget w;
749N/A Mask *valueMask;
749N/A XSetWindowAttributes *attributes;
749N/A{
749N/A attributes->bit_gravity = NorthWestGravity;
749N/A *valueMask |= CWBitGravity;
749N/A
749N/A XtCreateWindow( w, (unsigned)InputOutput, (Visual *)CopyFromParent,
749N/A *valueMask, attributes);
749N/A} /* Realize */
749N/A
749N/A/* ARGSUSED */
749N/Astatic Boolean SetValues(current, request, new)
749N/A Widget current, request, new;
749N/A{
749N/A /* need to relayout if h_space or v_space change */
749N/A
749N/A return False;
749N/A}