Let's start with the two most similar cases: change_managed() and realize(). First take a look at BulletinBoard's change_managed() method (in BulletinBoard.c in the directory (LESSTIF_ROOT)/libXm). Here, in the book, I will present only lines of concern as I talk about them (otherwise this book would get even larger).
Note that the first thing we do after we enter this method is look to see if the class record for the widget has a geo_matrix_create() member. If there is one, we call handle_change_managed() and return (more about this on page ). If there isn't one, we proceed with generic BulletinBoard rules.
static void change_managed(Widget w) { Widget p; XmBulletinBoardClassRec *bb = (XmBulletinBoardClassRec *)XtClass(w); if (bb->bulletin_board_class.geo_matrix_create) { handle_change_managed(w, bb->bulletin_board_class.geo_matrix_create); return; }
_XmGMEnforceMarginNext, we call a _XmGMEnforceMargin(). This function ensures that the default BulletinBoard behavior of forcing children to be within the BulletinBoard margins is applied.
_XmGMEnforceMargin(w, BB_MarginWidth(w), BB_MarginHeight(w), False);
_XmClearShadowTypeThen we clear the old shadow, as what we may do could alter the way the shadow looks.
_XmClearShadowType(w, BB_OldWidth(w), BB_OldHeight(w), BB_OldShadowThickness(w), 0); BB_OldShadowThickness(w) = 0;
_XmGMDoLayoutIf we are realized, or our width or height is zero (usually indicating that this is the first child to be added), we call _XmGMDoLayout(). This function implements the generic BulletinBoard layout method.
if (XtIsRealized(w) || XtWidth(w) == 0 || XtHeight(w) == 0) { _XmGMDoLayout(w, BB_MarginWidth(w), BB_MarginHeight(w), BB_ResizePolicy(w), False); }
If we shrank, redraw the shadow (the expose method does this too, but...)
_XmDrawShadows
if ((XtWidth(w) < BB_OldWidth(w) || XtHeight(w) < BB_OldHeight(w)) && XtIsRealized(w)) { _XmDrawShadows(XtDisplay(w), XtWindow(w), MGR_TopShadowGC(w), MGR_BottomShadowGC(w), 0, 0, XtWidth(w), XtHeight(w), MGR_ShadowThickness(w), BB_ShadowType(w)); }
Then, we record our width/height/shadow thickness.
BB_OldWidth(w) = XtWidth(w); BB_OldHeight(w) = XtHeight(w); BB_OldShadowThickness(w) = MGR_ShadowThickness(w);
_XmNavigChangedAnd finally, the required call to _XmNavigChangedManaged() that all Manager subclasses must do.
_XmNavigChangeManaged(w); }
If you read the code for realize() in BulletinBoard.c, you'll see almost identical code. The call to _XmNavigChangeManaged() isn't necessary in the realize() method, as is the test XtIsRealized() either. Instead the realize() method must chain up to its superclass' realize() method.
handle_change_managedmethodNow, let's take a look at the handle_change_managed() method that is called from BulletinBoard's change_managed() method. We start this function by checking if we are realized, or if our resize policy allows us to resize (i.e., not XmNONE). If either case is true, we set our desired width/height to zero; this is a cue to the GeoUtils to compute the desired size of this manager. If either case is false, we set the desired width/height to our current width/height; this cues the GeoUtils to lay out the manager to the current geometry (if possible).
static void handle_change_managed(Widget w, XmGeoCreateProc mat_make) { Dimension wd, ht, retw, reth; XmGeoMatrix geo; XtGeometryResult result; if (!!XtIsRealized(w)) wd = ht = 0; else if (BB_ResizePolicy(w) !!= XmNONE) wd = ht = 0; else { wd = XtWidth(w); ht = XtHeight(w); }
We then call the matrix create function. This function is crucial, as the data structures created tell the GeoUtils how to layout this particular widget.
geo = mat_make(w, NULL, NULL);
_XmGeoMatrixGetNext, we call _XmGeoMatrixGet(). This function essentially iterates through all the children we want to manage, querying each child (except the instigator) for the geometry the child desires. But note: this is not the same as all the managed children of this manager. If you forget to represent a child in the data structures when you create the matrix, that child won't be considered when you lay out the manager. Instead, at least in the case of SelectionBox and friends, the results are as specified in those class's documentation - usually ``undefined'' (in practice, they'll probably get piled up in the top lefthand corner of the manager).
_XmGeoMatrixGet(geo, XmGET_PREFERRED_SIZE);
_XmGeoArrangeBoxesNow the real work-horse routine in the GeoUtils is invoked - _XmGeoArrangeBoxes(). This function ``parses'' the data structure (the GeoMatrix) and lays out the children according to the rules defined by the matrix. Caveat: this function does not alter the children's geometry, but instead records the new geometry information in the XmKidGeometry structure contained by the GeoMatrix.
_XmGeoArrangeBoxes(geo, 0, 0, &wd, &ht);
At this point, _XmGeoArrangeBoxes() has computed the size of the manager as it would ideally like to be. The next code fragment checks the BB_ResizePolicy for a value of XmRESIZE_GROW. If the ideal size is less than the current size, we reject the change (because that would involve shrinking, and we should only grow). We then must re-layout the manager, by calling _XmGeoArrangeBoxes() with our current width and height.
if (BB_ResizePolicy(w) == XmRESIZE_GROW) { /* check the return against the original. If the procedure would * like the BB to shrink, call again */ if (wd < XtWidth(w) || ht < XtHeight(w)) { wd = XtWidth(w); ht = XtHeight(w); _XmGeoArrangeBoxes(geo, 0, 0, &wd, &ht); } }
Now we look to see if any of the above calculations has indicated that the manager wants to resize (by comparing the computed width and height with the manager XtWidth and XtHeight). If no change is forthcoming, we just free the matrix and return. Otherwise we continue on.
_XmGeoMatrixFree
if (wd == XtWidth(w) && ht == XtHeight(w)) { _XmGeoMatrixFree(geo); _XmNavigChangeManaged(w); return; }
Okay, the manager wants to change size. We call XtMakeResizeRequest(), and ask our parent if we can change size. Eventually, the parent will respond with the size we can be (hopefully the size the manager wants, but we can compromise here).
retw = wd; reth = ht; do { result = XtMakeResizeRequest((Widget)w, retw, reth, &retw, &reth); } while (result == XtGeometryAlmost);
The next fragment of code checks if a compromise was necessary, by evaluating whether the size our parent said we can be is the same as what we want to be. If the two don't match, we end up calling _XmGeoArrangeBoxes() yet again, to arrange our children to suit our parent.
if (retw !!= wd || reth !!= ht) _XmGeoArrangeBoxes(geo, 0, 0, &retw, &reth);
_XmGeoMatrixSetNow that all the geometry calculation has been done, our parent is happy, and the manager is happy, we can go ahead and do _XmConfigureObject() calls on all our children. That particular job goes to the function _XmGeoMatrixSet(), which basically processes each XmKidGeometry box and configures the widget that the box represents.
_XmGeoMatrixSet(geo);
If we've gotten this far, then we are pretty sure the manager's size has changed. If the manager has a shadow, now is the time to draw it (after erasing the old one).
_XmClearShadowType_XmDrawShadows
if (XtIsRealized(w)) { _XmClearShadowType(w, BB_OldWidth(w), BB_OldHeight(w), BB_OldShadowThickness(w), 0); _XmDrawShadows(XtDisplay(w), XtWindow(w), MGR_TopShadowGC(w), MGR_BottomShadowGC(w), 0, 0, XtWidth(w), XtHeight(w), MGR_ShadowThickness(w), BB_ShadowType(w)); }
We're done with the GeoUtils for now, so we can deallocate the matrix. Then we record our new width/height/shadow thickness. And finally, the required call to _XmNavigChangedManaged that all Manager subclasses must do.
_XmGeoMatrixFree(geo); BB_OldWidth(w) = XtWidth(w); BB_OldHeight(w) = XtHeight(w); BB_OldShadowThickness(w) = MGR_ShadowThickness(w); _XmNavigChangeManaged(w); }
The realize() case is identical to this one.