Geometry Management in LessTif

# 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 at u27113@kb.be.

# 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

- x
- y
- width
- height
- border_width

A widget can request a geometry change by using XtMakeGeometryRequest or XtMakeResizeRequest. 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 :

- XtGeometryYes :
Means that the request has been granted.
- XtGeometryDone :
The request has been granted, also it has been applied to the widget. According to "X Window System Toolkit" by Asente & Swick, a widget set should choose a policy : either use YES in all widgets, or use DONE. In LessTif, we've chosen the YES approach.
- XtGeometryNo :
You can guess this by now : the request has not been granted. Many manager widgets (subclasses of XmManager) in LessTif will return this when they get a request to change the X or Y (i.e. the position) of a child widget.
- XtGeometryAlmost :
This is a very useful but difficult return value. It means that the request has not been completely granted, and the allowed parameter returns a suggested geometry. If the widget takes the suggestion and calls XtMakeGeometryRequest with that same set of values, the parent widget must allow this request. That's the hard part.

## _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.

## geometry_manager method

The geometry_manager method is inherited from Composite. Hence, all LessTif manager widgets (subclasses of XmManager) have it. It is called in those widgets whenever a child widget asks the manager whether it (the child) is allowed to resize.

The signature of geometry_manager is

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

The widget w in geometry_manager is a child of the manager widget whose layout needs to be examined. Another hard rule in Xt is that the child widget calling geometry_manager must not be resized in geometry_manager. Ignoring this rule will probably get you into (almost) infinite loops. You'll get programs crashing because they exceed their stack size. What happens is that geometry_manager (incorrectly) resizes the calling widget, which will see that in its resize method, which will probably ask its parent to resize, and we're back in (another invocation of) geometry_manager.

Note that the calling widget, as we called it just now, is often referred to as the instigator.

As most manager widgets manage the layout of other widgets (their children), a layout algorithm is usually (read: always) called from geometry_manager.

One of the results of a geometry request by a child can be that the parent widget needs to adapt its own size. According to what we know now, our parent widget needs its own parents' permission to do that. If it does not get that permission (there are many reasons why this can happen), then the consequence is almost certainly that geometry_manager in the parent widget will end up returning XtGeometryNo.

As we've seen above in the description of XtMakeGeometryRequest, one of the more important things to do in geometry_manager is to return a sensible suggested geometry in an XtGeometryAlmost case.

## XtQueryGeometry

You can ask the preferred size of a widget.

## query_geometry method

Called by XtQueryGeometry.

## resize method

Called when a widget just got a new size.

## change_managed method

Called when a Composite widget has a child that changes its managed state. Yes this sounds complicated because it is.

## realize method

Called when the widget is first realized.

## insert_child and delete_child methods

These methods are called when a child is added to a manager widget, or when a child is destroyed. Their use is particularly important in those manager widget which keep information about their children in private data structures.

Note that these are unchained methods, which means they are not automatically called for all the superclasses of a manager widget. XmRowColumn's insert_child needs to call XmManager's insert_child, which in turn calls the one in its superclass.

# 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

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