Introduction

This document describes the way in which LessTif widgets handle their geometry negotiations. As geometry management is a subject that has much to do with Xt, part of this document describes (what I understand of) the Xt geometry model.

All feedback is, as always, welcomed.

Xt Geometry Management

The Xt geometry model is based on geometry negotiations. This really means that every change that a widget wants to apply to its geometry, must be approved by the widget's parent. The resources in the widget that are part of this geometry are

A widget can request a geometry change by using XtMakeGeometryRequest or XtMakeResizeRequest. In LessTif, we've wrappered XtMakeGeometryRequest in _XmMakeGeometryRequest(). This is the preferred method of geometry negotiation, as the result is (or at least should be) binary -- Yes or No. XtMakeResizeRequest only allows a widget to request a different size (width/height), whereas XtMakeGeometryRequest can be used to alter all five resources mentioned above.

LessTif has a convenience function called _XmMakeGeometryRequest which calls XtMakeGeometryRequest.

XtMakeGeometryRequest

The signature of XtMakeGeometryRequest is

XtGeometryResult
XtMakeGeometryRequest(  Widget w,
			XtWidgetGeometry *desired,
			XtWidgetGeometry *allowed)

It should be called from the widget which is passed as parameter 1. The second parameter described the geometry that the widget would like to have. The last parameter returns the geometry that the widget got, in some circumstances. Finally, the result of the function is a value indicating whether the request has been granted.

XtWidgetGeometry is a structure which holds the five fields indicated above, and a bitset in which you can indicate which fields have been initialized. In the return parameter, the bitset will also indicate how much information is valid. A common mistake is to assume that the parent widget will always set width and height values, and to just read those fields without looking at the flags.

The bitset field is called request_mode. It can be set using an OR of zero or more of the macros CWX, CWY, CWWidth, CWHeight, CWBorderWidth, each of which has exactly one bit set. A final bit XtCWQueryOnly is not currently used in LessTif. When set, the call to XtMakeGeometryRequest will return a result without changing anything to the widget.

The result of XtMakeGeometryRequest can have four values :

_XmMakeGeometryRequest

XtGeometryResult
_XmMakeGeometryRequest(Widget w, XtWidgetGeometry *g)
This function calls XtMakeGeometryRequest. If the return value is XtGeometryAlmost, it'll call it a second time, with the value just obtained. Then it returns.

Another programming mistake (introduced by Asente & Swick) : given the Xt rule about XtGeometryAlmost, you could program a loop in which you keep calling XtMakeGeometryRequest until the value is different from XtGeometryAlmost. The trouble is that this kind of a loop is only guaranteed to be finite if the parent widget(s) are bug-free. Need I say more?

_XmMakeGeometryRequest detects this problem and is verbose about it (if you set DEBUGSOURCES to print information about GeoUtils.c). It is known to happen with XmForm (i.e. XmForm currently doesn't always grant a geometry that it just suggested).

If anybody wants a good exercise in understanding this document, he or she is invited to find this bug. Really. I'll only start tracking it myself when I have no serious bugs to attend to. A couple of beers can be had in Leuven, Belgium, by the first person to fix this.

XtQueryGeometry

You can ask the preferred size of a widget.

Geometry and Widget Methods

The basic cycle involved is PreferredSize/XtMakeGeometryRequest/Layout.

For both primitives and composites:

For composites only:

NB: If a composite, and you either a) have no children, or b) have no managed children (BTW, _XmGeoCount_kids() will return zero for case b), then you probably shouldn't bother in either method to compute the preferred size or the layout; mostly because you don't have anything to operate on. Instead you should probably return the current geometry in query_geometry().

For constraints only

The Geometry Management Helper Interfaces

There are two sets of two basic functions, that roughly have the signatures For primitives:

    PreferredSize( Widget w /* input */ );
    Layout( Widget w /* input with side effects */ );

and for composites:

     PreferredSize( Widget w, /* input */
                    Widget instig, /* input */
                    XtWidgetGeometry *instig_request, /* input */
                    XtWidgetGeometry *preferred_geom /* output */ );
     Layout( Widget w, /* input */
             Widget instig, /* input */
             XtWidgetGeometry *instig_request, /* input/output */
             XtWidgetGeometry *preferred_geom /* input */ );

For primitives, an example of PreferredSize is XmCalcLabelDimensions(); the equivalent Layout() would be the resize() method. Regardless, you're Layout function or PreferredSize function can be modified for different behavior as appropriate to your class.

LessTif Geometry

We're discussing geometry of many of LessTif's manager widgets here. One widget (and many of its subclasses) is not treated here, though : XmBulletinBoard has functionality based on XmGeoMatrix which allows you to build dialogs with it in a simple way. This is all described in the document $LESSTIF/doc/GeoUtils.txt, `Fun and Pain with the GeoUtils'.

Generic layout functions in complicated widgets (deprecated)

Many of the LessTif manager widgets have one layout function with a fairly large number of parameters. This one layout function is built such that it can be called from all the geometry-related widget methods described above.

Here's an example of the signature of such a function :

static void
_XmMainWindowLayout(Widget w,
		    Boolean ParentResize,
                    Widget child,
                    Boolean TestMode,
                    XtWidgetGeometry *cg,
                    XtWidgetGeometry *mwg)
The parameters are used as follows :
w
this is the manager widget
ParentResize
is a boolean which indicates whether the widget is allowed to try to resize itself
child
is the instigator: it is the widget that requests geometry changes
TestMode
is another boolean; if it is True, no changes are actually done to any widget resources
cg
is the geometry request for the child widget
mwg
optionally returns information about the main widget's size

The structure of these functions is such that they can be called in a number of circumstances. A high level description of what they do is :

  1. declare lots of local variables to store geometries (for the widget itself, and all its children). This is necessary to implement test mode.
  2. initialise all local variables to reflect the current geometry of the relevant widget
  3. initialise the specific variables that reflect the instigator with its geometry
  4. see how big the manager widget must be
  5. if we're allowed to (ParentResize), ask whether we can resize ourselves to the geometry we need
  6. resize ourselves (if applicable)
  7. layout all children widgets
  8. return information to the caller