%
% Geometry.tex -- 
%
% (c) 1996 The Hungry Programmers
% (c) 1996 Harald Albrecht (albrecht@igpm.rwth-aachen.de)
% See the file PUBLIC for details.
%
\chapter{Messy Geometry Management}
\by{Danny Backx\\Mitch Miers}

\section{Introduction}

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

The Xt geometry model is based on geometry negotiations: every change
that a widget wants to apply to its own geometry must first be
approved by the widget's parent.  The resources of the widget that are
part of this geometry negotiation mechanism are:

\begin{itemlist}
\item the position coordinates \code{x} and \code{y},
\item the \code{width} and \code{height},
\item and finally \code{border\_width}.
\end{itemlist}

\codeindex{XtMakeGeometryRequest}%
\codeindex{XtMakeResizeRequest}%
\codeindex{\_XmMakeGeometryRequest}%
A widget can request a geometry change by using
\code{XtMakeGeometryRequest()} or \code{XtMake\-ResizeRequest()}.
Whereas \code{XtMakeGeometryRequest()} can be used to change all five
geometry resources mentioned above, \code{XtMakeResizeRequest()} only
allows a widget to request a different size (width and/or height) and
is in the end just a convenience function for such cases where the
position of your widget doesn't matter.

In addition, \LessTif{} has a convenience function called
\code{\_XmMakeGeometryRequest()} which calls
\code{XtMakeGeometryRequest()}. But more on it below.


\section{Making Geometry Requests}

\subsection{The Xt Intrinsics Way}

The function prototype of \code{XtMakeGeometryRequest()} is as follows:

\begin{verbatim}
XtGeometryResult
    XtMakeGeometryRequest(Widget w,
                          XtWidgetGeometry *desired,
                          XtWidgetGeometry *allowed);
\end{verbatim}

This function should be called from the widget which is passed as the
first parameter.  The second parameter describes 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.

\codeindex{XtWidgetGeometry}%
The \code{XtWidgetGeometry} structure contains the five fields
indicated above (position, extend, and border width), together with 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 the width and height values, and to just read
those fields without looking at the flags.

\codeindex{XtCWQueryOnly}\codeindex{CWX}\codeindex{CWY}%
\codeindex{CWWidth}\codeindex{CWHeight}\codeindex{CWBorderWidth}%
The bitset field is called \code{request\_mode}.  It can be set using
an OR of zero or more of the macros \code{CWX}, \code{CWY},
\code{CWWidth}, \code{CWHeight} and \code{CWBorderWidth}, each of which
has exactly one bit set.  A final bit \code{XtCWQueryOnly} is
currently not used within \LessTif{}.  When set, the call to
\code{XtMakeGeometryRequest()} will return a result without changing
anything to the widget.

\codeindex{XtMakeGeometryRequest}%
\codeindex{XtGeometryYes}\codeindex{XtGeometryDone}%
\codeindex{XtGeometryNo}\codeindex{XtGeometryAlmost}%
\index{policy!geometry}%
\index{geometry policy}%
The result of \code{XtMakeGeometryRequest()} can have four values:

\begin{itemlist}
\item \code{XtGeometryYes}: Means that the request has been granted.
  
\item \code{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
  \code{XtGeometryYes} in all widgets, or use \code{XtGeometryDone}.
  In \LessTif{}, we've chosen the \code{XtGeometryYes} approach.

\item \code{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
  \code{x} or \code{y} field (that is, the position) of a child
  widget.
  
\item \code{XtGeometryAlmost}: This is a very useful but difficult
  return value.  It means that the request has not been
  \emph{completely} granted, and the \code{allowed} parameter returns
  a suggested geometry.  If the widget takes the suggestion and calls
  \code{XtMakeGeometryRequest()} with that same set of values, the
  parent widget \emph{must} allow this request.  That's the hard part.
\end{itemlist}


\subsection{The LessTif Way}

\codeindex{\_XmMakeGeometryRequest}%
The function prototype of this geometry negotiation convenience
function is:

\begin{verbatim}
XtGeometryResult
    _XmMakeGeometryRequest(Widget w, XtWidgetGeometry *desired);
\end{verbatim}

This function is a wrapper around \code{XtMakeGeometry\-Request()}.
First, it asks the parent of the widget specified as the first
parameter \code{w} for the new geometry (given in \code{desired}). If
the parent's answer is \code{XtGeometryAlmost}, then
\code{\_XmMakeGeometryRequest()} takes a second round, asking the
parent of \code{w} again, but this time using the geometry proposed by
the parent. Then it returns.

Another programming mistake (introduced by Asente \& Swick): given the
Xt rule about \code{XtGeo\-metryAlmost}, you could happily program a
loop in which you keep calling \code{XtMakeGeo\-metryRequest()} until
the value is different from \code{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?

\code{\_XmMakeGeometryRequest()} detects this problem and is verbose
about it: you'll see a warning, saying that a ``Parent refused resize
request'' together with the name and class of the offending parent and
the widget geometries in question. With \LessTif{}, you might
occasionally see this warning, because XmForm currently doesn't always
grant a geometry that it just suggested...

If anybody wants a good exercise in understanding this document, she
or he 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
(real or virtual? Ed.)  can be had in Leuven, Belgium, by the first
person to fix this.


\section{Geometry Management and the Widget Methods}

The basic cycle involved is:
\begin{itemlist}
\item[\circled{1}] Calculate your \emph{preferred size}.
\item[\circled{2}] Ask for permission of your new preferred size using
\emph{\code{XtMakeGeometryRequest()}}.
\item[\circled{3}] Run your \emph{layout} procedure.
\end{itemlist}

For both primitives and composites, all the following rules for
geometry management apply, except where otherwise stated. The rules
mentioned in the subsections for the \code{geometry\_manager()},
\code{change\_managed()}, \code{insert\_child()} and
\code{delete\_child()} methods apply only to composites. For
Constraint widgets (and descendants), you should watch the rules
mentioned for the \code{constraint\_initia\-lize()} and
\code{constraint\_set\_values()} methods.

Just another note: if you have a composite widget, and this widget has
either no children, or it doesn't has managed children (by the way,
\code{\_XmGeoCount\_kids()} will return zero in the second case, but
the GeoUtils are described in more detail in the next chapter), 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 \code{query\_geometry()}.


\subsection{The initialize() Method}

\codecomposeindex{initialize}{method}%
Don't do anything if you're a Composite (-derived) widget. You don't
know enough yet (and you probably don't have any kids yet, unless the
user created you with the \code{XtNchildren} resource: if the user
does something like this, they're on their own). If you are a
primitive widget (no pun intended...), you should know enough to do a
basic layout.


\subsection{The set\_values() Method}

\codecomposeindex{set\_values}{method}%
If a value changed that should cause a layout change, go ahead and
recompute the preferred size. Then, just set your width/height to the
computed values: the Xt Intrinisics will automatically see a change
made and call \code{XtMakeGeometryRequest()} on your behalf, so
(ordinarily) don't do the layout step here.

If the request is granted, the Xt Intrinsics will automatically call your
\code{resize()} method; that should be where the layout is done.

Rebuttal: There may be times where this isn't true. Some resources may
require a Composite widget to re-layout. When doing so, there are a
few warnings that should be noted.

\begin{itemize}
\item If the user set a resource that triggered a size adjustment, the
  resize request may not be honored by the parent. If it is not, and
  you re-layout, then be aware that you may have layed out to a
  geometry that isn't honored. This can cause (grossly understated)
  unexpected results. This case can (and does) happen in Label (and
  LabelGadget); that's why there is a call to the \code{resize()}
  method in \code{expose()} -- to make sure the widget is displayed
  correctly.  The same is true of CascadeButton (and
  CascadeButtonGadget).
  
\item A superclass may have polluted the widget dimensions, or a
  resource that changed in a superclass may have altered the widget
  dimensions to cause a request that may not be honored. Laying out to
  this geometry may not be valid either.
\end{itemize}

Unfortunately, the intrinsics do not notify a widget if the resize
request wasn't honored, so there's no way to do a proper job of it
unless the \code{expose()} method calls the procedure that is
responsible for the layout. Needless to say, this is not good for
performance. One optimization that can be made is due to the nature of
\code{XtMakeGeometryRequest()} (which will be discussed later): if the
widget is not managed, or the parent isn't realized, then we can be
sure that the resize request \emph{will} be honored. In this case, we
can blithely call our layout procedure and be sure that the request
will be honored.


\subsection{The resize() Method}

\codecomposeindex{resize}{method}%
You can't do any geometry negotiation here. You must take
the size you currently have, and lay yourself out to this geometry.
This method, in combination with \code{set\_values()}, show the requirements
of the two basic algorithms: one to compute the preferred size, and one
to layout to a given size.


\subsection{The realize() Method}

\codecomposeindex{realize}{method}%
For primitive subclasses, you probably want to realize your window,
and then layout to it's geometry. For managers, it doesn't hurt to go
through the ``preferred size\circled{1}'' --
``\code{XtMakeGeometry\-Request()}\circled{2}'' -- ``layout\circled{3}''
cycle again, as things may have changed.


\subsection{The query\_geometry() Method}

\codecomposeindex{query\_geometry}{method}%
Call the routine that calculates the preferred size, and return the
result if the \code{request\_mode} is 0.  Otherwise, return the usual
rules of the request versus what you've computed.


\subsection{The geometry\_manager() Method}

\codecomposeindex{geometry\_manager}{method}%
This is the trickiest one. The preferred-size-routine should take two
parameters, \code{instigator} and \code{instig\_request}, and use the
values specified in \code{instig\_request} when treating the
\code{instigator}.

There are two additional rules that you should keep in mind:
\code{geometry\_manager()} doesn't get called if the parent isn't
realized; it also doesn't get called if the child isn't managed (in
both cases, the request is automatically granted). The method should
then call \code{\_XmMakeGeometry\-Request()}. Doing this guarantees that
either \code{XtGeometryYes} or \code{XtGeometryNo} is returned --
you'll never see \code{XtGeometryAlmost} unless a composite has a bug.

Finally the layout function should be called. The layout function
should take two additional parameters, the \code{instigator} and the
\code{instig\_request}. What to do next depends on the value that's
going to be returned by the \code{geometry\_manager()} method:

\begin{itemlist}
\item If the result of the layout procedure on the instigator is
  \code{XtGeometryAlmost}, then no change should be made to the
  instigator (\emph{or for that matter, to any other children in the
    composite}; this is an important point, and often alters the
  behavior of the layout method -- doing a configure on a child when
  the geometry didn't change just wastes cycles). Instead, the reply
  geometry should reflect the layout computed for the instigator.
  
\item[]
  \textbf{IMPORTANT}: if the instigator calls back with the geometry
  that you computed, the \code{geo\-metry\_manager()} method \emph{must}
  return \code{XtGeometryYes}.
  
\item[]
\item If the result of the layout procedure on the instigator is
  \code{XtGeometryYes}, the \emph{child's rectangle should be modified
    to reflect the geometry (this is important)}. You must do this so
  that \code{XtMakeGeometryRequest()} will reconfigure the requesting
  child's window. This is different from \code{XtGeometryDone} in that
  \code{XtGeometryDone} implies that the window change was made by the
  parent, and we don't do that in \LessTif{}. The GeoUtils and RowColumn
  do this now.
\end{itemlist}

In the layout function, if the child being manipulated is \emph{not}
the instigator, then the child should be configured (normally using
\code{\_XmConfigureObject()}), if the return is \code{XtGeometryYes}.


\subsection{The change\_managed() Method}

\codecomposeindex{change\_managed}{method}%
You've got to go through the complete cycle of ``preferred
size\circled{1}'' -- ``\code{XtMakeGeometryRe\-quest()}\circled{2}''
-- ``layout\circled{3}''.  The Xt Intrinsics don't help with any of
this, so it's got to be explicit. Remember that you've no instigator
here, so all managed children should be configured if a size change
took place.


\subsection{The insert\_child() and delete\_child() Methods}

\codecomposeindex{insert\_child}{method}%
\codecomposeindex{delete\_child}{method}%
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 widgets 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 \code{insert\_child()} needs to call XmManager's
\code{insert\_child()}, which in turn calls the one in its superclass.


\subsection{The constraint\_initialize() Method}

\codecomposeindex{constraint\_initialize}{method}%
Often, this method (if present) will not cause any geometry changes,
but does offer an excellent time to capture information that will
affect geometry management in the future. This includes things like
the \code{XmNpositionIndex} resource (RowColumn), or
\code{XmNpaneMinimum} and \code{XmNpaneMaximum} (PanedW).

\subsection{The constraint\_set\_values() Method}

\codecomposeindex{constraint\_set\_values}{method}%
Tricky. In this method, there can be so many interactions that the
mind boggles. Quite often, resources that are set here may have
\emph{serious} implications for geometry management (like
\code{XmNposi\-tionIndex}), but it is difficult to know when a change
should trigger a re-layout. In general, \emph{all of the warnings for
\code{set\_values()} apply}.

\subsection{The Geometry Management Helper Interfaces}

There are two sets of two basic functions, that roughly have the
following signatures. First, the ones for primitives:

\begin{verbatim}
PreferredSize( Widget w °/* input */° );
Layout( Widget w °/* input with side effects */° );
\end{verbatim}

and for composites: 

\begin{verbatim}
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 */° );
\end{verbatim}

For primitives, an example of the function to calculate the preferred
size is \code{XmCalcLabel\-Dimensions()}. The equivalent layout
procedure would be the \code{resize()} method. Regardless, your layout
function or preferred-size function can be modified for different
behavior as appropriate to your class.


% End of Geometry.tex
